• 로그인
  • 장바구니에 상품이 없습니다.

home2 게시판 Next.js 게시판 이미지 변경 문제.. 해결되지 않는부분 질문입니다!

이미지 변경 문제.. 해결되지 않는부분 질문입니다!

  • 이 주제에는 3개 답변, 2명 참여가 있으며 codingapple2 년 전에 전에 마지막으로 업데이트했습니다.
4 글 보임 - 1 에서 4 까지 (총 4 중에서)
  • 글쓴이
  • #87635

    강민택
    참가자
    안녕하세요 선생님.
    선생님의 nextJs 강의를 듣고 난 뒤에 개인 프로젝트에 적용을 하던 도중 해결되지 않는 부분이 있어서 질문 남깁니다!
    프로필 사진 업로드 후에 변경 기능을 추가하려고 합니다.
    사진은 S3에 업로드가 되고, S3에서 url을 반환받아서 mongoDB에 유저정보에 저장하는 식으로 되어 있는데요,
    mongoDB updateOne 함수로 DB profileImage 데이터가 바뀌는 것 까지는 잘됩니다! 
    하지만 로그아웃을 했다가 다시 재 로그인을 해야지만 변경된 프로필 이미지가 뜨는데, 로그인 상태에서 바로 변경될 수는 없는건가요?
    ( 로그인 방식은 next-auth 의 jwt 방식을 사용했습니다 ) 
    
    제가 생각한 문제점은
    1. session에 원래 있던 프로필 이미지가 저장이 되어있기때문에 클라이언트 측에서는 변경된 이미지를 확인불가능하다.
    2. 세션 업데이트를 하지않아서 이미지가 변경되지 않는것이다.
    이렇게 생각했었는데요,
    그래서 시도한 방법은
    1. useSession 훅의 update함수를 사용하는것
    2. 클라이언트 측에서 이미지 업로드를 할때에 session 업데이트를 하는 api를 만들어서 적용,
    
    이렇게 시도했었는데 해결이 안되네요.. 이제는 뭐가 문제인지 잘 모르겠습니다.
    
    아래는 코드 입니다.
    
    
    // Profile.js ( Profile 페이지 컴포넌트 )
    
    "use client";
    import * as S from "./profileStyle";
    import Link from "next/link";
    import Image from "next/image";
    import back from "/public/assets/img/left.png";
    import edit from "/public/assets/img/editprofile.png";
    import LogoutOnclick from "./logoutOnclick";
    import useImageUpload from "../../hooks/useImageUpload";
    import { useSession } from "next-auth/react";
    export default function Profile() {
      const right = ">";
      const { data: session, status, update } = useSession();
      let user = session?.user;
      const { src, handleImageUpload } = useImageUpload();
      const handleFileChange = (e) => {
        const file = e.target.files[0];
        handleImageUpload(file);
      };
      const userProfileImage = user?.profileImage;
      const profileImageUrl = `https://baemin-taek.s3.amazonaws.com/${userProfileImage}`;
      return (
        <S.Container>
          <S.Header>
            <Link href="/mypage" as="/mypage">
              <Image src={back} width={27} alt="back-btn" />
            </Link>
            <span className="header-title">내 정보 수정</span>
          </S.Header>
          {userProfileImage ? (
            <Image
              src={profileImageUrl}
              alt="profile-image"
              width={100}
              height={100}
            />
          ) : (
            <Image src={edit} alt="edit" width={100} height={100} />
          )}
          <input type="file" accept="image/*" onChange={handleFileChange} />
          <S.InfoBox>
            <S.Item>
              <span className="title">닉네임</span>
              <div>
                <span className="sub">{user?.name}</span>
                <S.RightBtn>{right}</S.RightBtn>
              </div>
            </S.Item>
            <S.Item>
              <span className="title">이메일</span>
              <span className="sub">{user?.email}</span>
            </S.Item>
            <S.Item style={{ borderBottom: "none" }}>
              <span className="title">휴대폰 번호 변경</span>
              <S.RightBtn>{right}</S.RightBtn>
            </S.Item>
          </S.InfoBox>
          <S.Box>
            <div style={{ display: "flex", flexDirection: "column" }}>
              <span className="title">로그인 기기 관리</span>
              <span className="disc">
                내 아이디로 로그인 된 기기를 관리할 수 있어요
              </span>
            </div>
            <S.RightBtn style={{ marginRight: "15px" }}>{right}</S.RightBtn>
          </S.Box>
          <S.Box>
            <div style={{ display: "flex", flexDirection: "column" }}>
              <span className="title">연결된 서비스 관리</span>
              <span className="disc">
                배민 아이디와 연결된 서비를 관리할 수 있어요
              </span>
            </div>
            <S.RightBtn style={{ marginRight: "15px" }}>{right}</S.RightBtn>
          </S.Box>
          <S.Box>
            <span className="title">연동된 소셜 계정</span>
          </S.Box>
          <S.AccountBox>
            <LogoutOnclick />
            <span className="bar">|</span>
            <span className="out">회원탈퇴</span>
          </S.AccountBox>
        </S.Container>
      );
    }
    
    
    /api/image.js
    import aws from 'aws-sdk';
    import { MongoClient } from 'mongodb';
    import { getSession } from "next-auth/react";
    export default async function handler(req, res) {
      const session = await getSession({ req });
      aws.config.update({
        accessKeyId: process.env.ACCESS_KEY,
        secretAccessKey: process.env.SECRET_KEY,
        region: 'ap-northeast-2',
        signatureVersion: 'v4',
      });
      const s3 = new aws.S3();
      const url = await s3.createPresignedPost({
        Bucket: process.env.BUCKET_NAME,
        Fields: { key: req.query.file },
        Expires: 60, // seconds
        Conditions: [
          ['content-length-range', 0, 1048576], // 파일용량 1MB 까지 제한
        ],
      });
      // MongoDB 저장
      const userId = session?.user?.name;
      const value = url.fields.key;
      
      try {
        const mongoUrl = process.env.MONGO_URL;
        const client = await MongoClient.connect(mongoUrl);
        const db = client.db('baemin');
        const collection = db.collection('accounts');
        
        await collection.updateOne(
          { name: userId },
          { $set: { profileImage: value} }
        );
        client.close();
      } catch (error) {
        console.error('MongoDB 저장 실패:', error);
        res.status(500).json({ error: ' MongoDB 저장 실패' });
        return;
      }
      res.status(200).json(url);
    }
     
    /hooks/useImageUpload ( 이미지 업로드 커스텀 훅 )
    
    import { useState } from "react";
    export default function useImageUpload() {
      const [src, setSrc] = useState("");
      const handleImageUpload = async (file) => {
        try {
          const filename = encodeURIComponent(file.name);
          let res = await fetch(`/api/imageUpload/image?file=${filename}`);
          res = await res.json();
          // S3 업로드
          const formData = new FormData();
          Object.entries({ ...res.fields, file }).forEach(([key, value]) => {
            formData.append(key, value);
          });
          let result = await fetch(res.url, {
            method: "POST",
            body: formData,
          });
          if (result.ok) {
            setSrc(`${result.url}/${filename}`);
          } else {
            window.alert('이미지 크기는는 1MB 이하여야 합니다.');
            
          }
        } catch (error) {
          console.error(error);
        }
      };
      return { src, handleImageUpload };
    }
     
    여기까지 제 코드 입니다.. 어떤 부분이 문제인지 아니면 접근 방식이 잘못되었는지 알려주신다면 감사하겠습니다!
    앗 추가로 useSession() 쓰면 update 함수에 nextauth 설정파일에있는 jwt 함수가 실행된다고 알고 있는데 유저 jwt내용을 어떻게 수정할수있을까요?
    
    / [...nextauth].js
    callbacks: {
        //4. jwt 만들 때 실행되는 코드
        //user변수는 DB의 유저정보담겨있고 token.user 저장 시 jwt에 들어감.
        jwt: async ({ token, user }) => {
          if (user) {
            token.user = {};
            token.user.name = user.name;
            token.user.email = user.email;
            token.user.profileImage = user.profileImage;
          }
          return token;
        },
        //5. 유저 세션이 조회될 때 마다 실행되는 코드
        session: async ({ session, token }) => {
          session.user = token.user;
          return session;
        },
      },
    
    next auth 설정 부분입니다! 이틀째 해결을 못하고있는데 답답하네요 ㅠㅠ
     
    #87669

    codingapple
    키 마스터
    https://stackoverflow.com/a/75924500 이거 따라합시다 
    아무 페이지에서 update() 실행하면 jwt: async ({ token, user }) => { 안에있는게 실행되는데 
    거기서 jwt안에 원하는 정보 추가하는 식으로 하면 되는듯요
    #87730

    강민택
    참가자
    덕분에 해결했습니다!
    근데 의도에 맞게 동작은 하는데 이렇게 하는게 맞는지 확신이 들진 않습니다. 혹시 죄송하지만 코드가 괜찮은지 한번 봐주실수있나요?
    수정된 부분은 다음과 같습니다. 
    
    // jwt 
    jwt: async ({ token, user, session, trigger }) => {
          if (user) {
            token.user = {};
            token.user.name = user.name;
            token.user.email = user.email;
            token.user.profileImage = user.profileImage;
          }
          if( trigger === 'update'){
            token.user.profileImage = session.info
          }
          return token;
        },
    
    // image upload 부분
    
    const handleFileChange = (e) => {
        const file = e.target.files[0];
        handleImageUpload(file);
        sessionUpdate({
          info: file.name
        })
      };
    
    항상 감사합니다. 좋은 하루 보내세요!
    #87769

    codingapple
    키 마스터
    잘될거같군요
4 글 보임 - 1 에서 4 까지 (총 4 중에서)
  • 답변은 로그인 후 가능합니다.

About

현재 월 700명 신규수강중입니다.

  (09:00~20:00) 빠른 상담은 카톡 플러스친구 코딩애플 (링크)
  admin@codingapple.com
  이용약관
ⓒ Codingapple, 강의 예제, 영상 복제 금지
top

© Codingapple, All rights reserved. 슈퍼로켓 에듀케이션 / 서울특별시 강동구 고덕로 19길 30 / 사업자등록번호 : 212-26-14752 온라인 교육학원업 / 통신판매업신고번호 : 제 2017-서울강동-0002 호 / 개인정보관리자 : 박종흠