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

home2 게시판 Next.js 게시판 이미지 수정 기능 질문있습니다.

이미지 수정 기능 질문있습니다.

8 글 보임 - 1 에서 8 까지 (총 8 중에서)
  • 글쓴이
  • #99016

    공플
    참가자
    안녕하십니까? 선생님 강의 잘 보고 있습니다. 제 코딩 실력 향상에 선생님 강의가 큰 도움이 되었습니다.
    그런데 강의를 듣고 응용 기능을 만드는 것에서 난관이 닥쳐 질문을 좀 드리고자 합니다.
    
     수정 페이지에서 이미지를 수정하는 기능을 넣고 싶은데 기존의 방식은 props를 활용해서 기존 게시물의 id를 받아오는 방식입니다.
    수정페이지를 ' use client'를 사용해서 클라이언트 컴포넌트로 바꾸면 mongoDB와 props를 사용할 수 없게 되어 게시물의 id를 받아
    오는 것이 불가능하여 수정페이지에 기존 게시글을 보여주는 것도 불가하고 서버로 해당 게시글의 POST요청도 보낼 수가 없습니다.
    
     그렇다고 기존의 SSR방식을 유지하여 form태그를 활용해서 이미지를 서버로 POST요청을 보내면 이미지의 이름만 전송이 되고 또
    기존 mongoDB에 등록된 이미지 URL과 이미지 키를 받아와서 업데이트 할 수가 없는 상황입니다. // 이 부분은 제가 실력이 부족하여
    그렇다고 생각합니다. 쓰기 삭제는 쉬운데 수정 기능이 까다롭습니다.
    
     이를 해결하는 좋은 방법이 있다면 알려주시면 매우 감사하겠습니다.
    
    
    #99022

    codingapple
    키 마스터
    수정버튼누르면 이미지 url과 글 _id를 서버로 보내고
    서버는 그걸로 mongodb 에 있던 기존 이미지 url 덮어쓰기 하면 이미지 수정 끝 아닐까요
    #99237

    공플
    참가자
    대략 어떤 식인지는 이해가 갔는데 클라이언트 페이지에서 서버 컴포넌트로 파일원본을
    전송하는 법을 모르겠습니다. 혹시 코드를 보시고 조언을 해주실 수 있으십니까?
    
    
    // edit 클라이언트 페이지
    import { connectDB } from "@/database/MongoDB";
    import { ObjectId } from 'mongodb'
    export default async function edit (props) {
        
     console.log(props)
        const db = (await connectDB).db("Forum")
        let 자료 = await db.collection('post').findOne({_id : new ObjectId(props.params.id)});
       
        return (
            
        <div>
            <h4>글 수정 페이지</h4>
            <form action="/api/edit" method="POST">
                <input style={{display:'none'}} name="_id" defaultValue={자료._id.toString()}/>
                <input className="제목쓰는곳" name="title" defaultValue={자료.title}/>
                <br/>
                <div className="새로운이미지선택">
                    <input type="file" accept="image/*" name='newImage'/>
                </div>
                <textarea className='글쓰는곳' name="content" defaultValue={자료.content}/>
                <br/>   
                <button type="submit">수정</button>
            </form>
        </div>
        )
    }
    
    
    //edit페이지 엔드포인트(서버)
    import { connectDB } from "@/database/MongoDB";
    import { ObjectId } from "mongodb";
    import { getServerSession } from "next-auth";
    import { authOptions } from "./auth/[...nextauth]";
    import aws from 'aws-sdk'
    export default async function handler (요청, 응답) {
       
        console.log(요청.body)
        
        if (요청.method == 'POST') {
                 
            let 유저정보 = await getServerSession(요청, 응답, authOptions)
            const db = (await connectDB).db('Forum')
            let 찾기 = await db.collection('post').findOne({_id : new ObjectId(요청.body._id)})
            if (!유저정보 || !유저정보 .user || !유저정보 .user.email) {
            return 응답.status(401).json('로그인필요');
            }
            
            const 제목 = 요청.body.title.trim();
            const 본문 = 요청.body.content.trim();
            if (!제목 || 제목 === 'null'){
                return 응답.redirect(302,'/error/nullError') 
                }
            if (!본문 || 본문 === 'null'){
                return 응답.redirect(302,'/error/nullError')
            }  
            if (찾기.author!=유저정보.user.email){
            return 응답.status(401).json('니꺼만 수정하세요');
            }
            else {
                const randomNumber = Math.floor(Math.random()*(99999 - 10000 + 1)) + 10000;
                const 저장될파일명 = 찾기.title + randomNumber;
                //S3 수정(기존에 저장된 이미지 삭제 후 새로 업로드)
                if (???) {
                    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 whatIupload = {
                            Bucket: process.env.BUCKET_NAME,
                            Key : 저장될파일명,
                            Body: ???,
                        };
                    
                    try {
                        const 삭제결과 = await s3.deleteObject({
                        Bucket: process.env.BUCKET_NAME,
                        Key: 찾기.imageName,
                        }).promise();
                        await s3.upload(whatIupload).promise();
                    } catch {
                        return 응답.status(500).json({ error: uploadErr.message });
                    }
                }
            
                //몽고DB업데이트
                let 바꿀거 = {
                    title : 요청.body.title,
                    content : 요청.body.content,
                    imageName : 저장될파일명,
                    imageUrl : 'https://s3.ap-northeast-2.amazonaws.com/' + process.env.BUCKET_NAME + 파일명
                }    
                const db = (await connectDB).db('Forum')
                let 결과 = await db.collection('post').updateOne(
                {_id : new ObjectId(요청.body._id.toString())},
                {$set : 바꿀거 });
                return 응답.redirect(302,'/')
            }
        }
    }
     
    #99262

    codingapple
    키 마스터
    form태그에 enctype="multipart/form-data" 속성 넣어봅시다
    #99298

    공플
    참가자
    감사합니다. 선생님 실례가 안된다면 한가지 더 질문을 드리겠습니다.
    
     form태그에 enctype="multipart/form-data" 속성을 넣고 서버에 요청을 보내고
    요청.body를 로그로 찍으니 각각의 text 데이터는 WebKitFormBoundary이렇게 뜨고
    이미지 파일은 외계어로 찍힙니다.
    
     그리고 요청.body._id 등등 구체적으로 로그를 찍으면 undefiend라 로그에 뜨는데
    서버에서 파싱을 따로 해줘야 하는지 클라이언트 페이지에서 요청을 보낼때 따로 
    인코딩을 해야되는지 궁금합니다.
    
    
    
    
    #99303

    codingapple
    키 마스터
    multer나 formidable 라이브러리 설치해야 깔끔하게 잘될듯요 
    아니면 app/api 폴더에선 바로 된다는 소문도 있습니다
    https://stackoverflow.com/questions/72663673/how-do-i-get-uploaded-image-in-next-js-and-save-it
    #99418

    공플
    참가자
     선생님 정말 감사드립니다. formidable 활용해서 S3이미지 수정기능 구현 완료 했습니다. 아래는 제가 작성한 코드입니다.
    저랑 같은 고민을 가진 사람들이 참고가 되었으면 하여 올립니다. //차후 버그가 발견되면 수정본 따로 올리겠습니다.
    // //edit페이지 엔드포인트(서버)
    import { connectDB } from "@/database/MongoDB";
    import { ObjectId } from "mongodb";
    import { getServerSession } from "next-auth";
    import { authOptions } from "./auth/[...nextauth]";
    import aws from 'aws-sdk';
    //포미더블 사용을위해 {IncomingForm} 설치법 npm install formidable
    import {IncomingForm} from 'formidable';
    //파일 시스템 접근을 위한 fs임포트
    import fs from 'fs';
    //formidalbe 사용을 위한 요청.body 파싱끄기 !!중요!!
    export const config = {
        api: {
          bodyParser: false,
        },
      };
    export default async function handler (요청, 응답) {
        if (요청.method === 'POST') {
            // 서버에 들어오는 데이터 처리 { allowEmptyFiles: true, minFileSize: 0 } 안의 구문은 빈파일 허용을 위함
            const form = new IncomingForm({ allowEmptyFiles: true, minFileSize: 0 });
            // formidable 요청 처리 메서드
            //요청.body 가 요청.fields, 요청.file로 대체됨, array로 저장됨 따라서 파일 선택을 위해[0] 사용
            form.parse(요청, async (err, fields, files) => {
                if (err) {
                    return 응답.status(400).json({ error: err.message });
                }
                let 유저정보 = await getServerSession(요청, 응답, authOptions);
                const db = (await connectDB).db('Forum');
                let 찾기 = await db.collection('post').findOne({_id: new ObjectId(fields._id[0])});
         
                // 로그인 안된  유저에게 전송
                if (!유저정보 || !유저정보.user || !유저정보.user.email) {
                    return 응답.status(401).json('로그인필요');
                }
                //요청.body가 공란이면 해당 페이지로 리다이렉트
                const 제목 = fields.title[0].trim();
                const 본문 = fields.content[0].trim();
                if (!제목 || 제목 === 'null'){
                    return 응답.redirect(302,'/error/nullError');
                }
                if (!본문 || 본문 === 'null'){
                    return 응답.redirect(302,'/error/nullError');
                }
                if (찾기.author !== 유저정보.user.email) {
                    return 응답.status(401).json('니꺼만 수정하세요');
                }
                //파일 이미지가 존재할 때 처리할 if문
                if (files.newImage && files.newImage[0] && files.newImage[0].size > 0) {
                        // 중복 이미지 방지를 위해 저장될 파일명을 랜덤숫자로 지정
                        let randomNumber = Math.floor(Math.random() * (99999 - 10000 + 1)) + 10000;
                        let 저장될파일명 = randomNumber.toString();
                        aws.config.update({
                            accessKeyId: process.env.ACCESS_KEY,
                            secretAccessKey: process.env.SECRET_KEY,
                            region: 'ap-northeast-2',
                            signatureVersion: 'v4',
                        });
        
                        const s3 = new aws.S3();
                        //fs.createReadStream() 매서드로 파일 경롤를 읽어옴
                        const 파일경로 = fs.createReadStream(files.newImage[0].filepath);
                        let 업로드할거 = {
                            Bucket: process.env.BUCKET_NAME,
                            Key: 저장될파일명,
                            Body: 파일경로,
                        };
                        const 삭제결과 = await s3.deleteObject({
                            Bucket: process.env.BUCKET_NAME,
                            Key: 찾기.imageName,
                        }).promise();
                        
                        await s3.upload(업로드할거).promise();
                        let 이미지바꿀거 = {
                            imageName: 저장될파일명,
                            imageUrl: 'https://s3.ap-northeast-2.amazonaws.com/' + process.env.BUCKET_NAME + '/' + 저장될파일명,
                            }
                        
                        let 결과2 = await db.collection('post').updateOne(
                            {_id: new ObjectId(fields._id[0])},
                            {$set: 이미지바꿀거}
                        );
                }
                // 파일 이미지가 존재하지 않거나 존재하더라도 요청.body가 수정되었을 때 처리할 코드
                let 바꿀거 = {
                    title: fields.title[0],
                    content: fields.content[0],}
                let 결과 = await db.collection('post').updateOne(
                    {_id: new ObjectId(fields._id[0])},
                    {$set: 바꿀거});
                return 응답.redirect(302,'/');
            });
        }
    }
    
    혹시 수정하거나 다듬을 부분이 있다면 충고해주시면 감사하겠습니다. 다시 한번 감사드립니다.
     
    #99437

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

About

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

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

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