4. 게시글 수정

윤지영·2024년 3월 21일

🌞 MongoDB 데이터 수정하기

await db.collection('콜렉션이름').updateOne(
	{게시물정보}, 
    { 업데이트연산자 : {바꿀데이터}}
);
  • $set 연산자: 지정한 필드의 값을 변경하거나, 해당 필드가 없는 경우 새로운 필드를 추가(덮어쓰기)
  • $unset 연산자: 특정 필드를 제거합니다. 지정된 필드가 문서에 존재한다면 그 필드가 삭제되며, 필드가 없다면 아무런 작용을 하지 않음.
    • deleteOne() 메서드와의 차이 : 필드 레벨에서의 변경이 필요한 경우 $unset을 사용하고, 전체 문서를 삭제하고자 하는 경우 .deleteOne()을 사용
  • $inc 연산자: 기존 값이 숫자일 경우 숫자 증감 시 사용.

수정페이지 생성.

- EditPage

  • 수정 아이콘 누를 시 해당 id 경로로 동적 라우팅
  • .findOne() 메서드 사용하여 해당 게시물id를 가진 글을 DB에서 가져오기
  • EditForm 컴포넌트에 editData를 prop으로 전달
  • EditForm 클라이언트 컴포넌트로 분리
//  app/edit/[id]/page.js
import EditForm from "@/components/EditForm";
import { connectToDatabase } from "@/utils/database";
import { ObjectId } from "mongodb";

export default async function EditPage({ params }) {
  const { postCollection } = await connectToDatabase();
  let data = await postCollection..findOne()({ _id: new ObjectId(params.id) });  

  const editData = { ...data, _id: data._id.toString() };
  return <EditForm editData={editData} />;
}

- EditForm 컴포넌트

<input type="hidden" name="_id"value={editData._id}/>
  • 수정할 게시물을 식별하기 위해 _id 값을 폼 데이터에 포함시키고, 폼을 제출할 때 _id도 함께 서버로 전송해줘야 한다!
    클라이언트에서 게시물을 수정할 때, 수정하려는 특정 게시물의 _id를 서버에 전달해야 서버 측에서 해당 게시물을 데이터베이스에서 찾아 업데이트할 수 있다.
  • input type="hidden을 통해 UI에 노출시키지 않을 수 있다.
  • router.refresh() : 이전과 바뀐점을 분석해서 바뀐부분만 새로고침 (soft refresh)
// src/components/EditForm.js
"use client";
import styles from "./edit.module.css";
import { useRouter } from "next/navigation";
import { useEffect, useState } from "react";

export default function EditForm({ editData }) {
  const [title, setTitle] = useState("");
  const [content, setContent] = useState("");
  const [message, setMessage] = useState("");
  const router = useRouter();

  useEffect(() => {
    setTitle(editData.title);
    setContent(editData.content);
  }, []);

  const handleChange = (e) => {
    const { name, value } = e.target;
    if (name === "title") {
      setTitle(value);
    } else if (name === "content") {
      setContent(value);
    }
  };

  const handleSubmit = async (e) => {
    e.preventDefault();
    const _id = e.target._id.value;
    const title = e.target.title.value;
    const content = e.target.content.value;
    try {
      const response = await fetch("/api/edit", {
        method: "PATCH",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ _id, title, content }), 
      });

      if (!response.ok) {
        throw new Error("네트워크 응답이 올바르지 않습니다.");
      }
      setMessage("글이 수정되었습니다😊");
      router.push("/list");
      router.refresh();
    } catch (error) {
      console.error(error);
      setMessage(error.message);
    }
  };

  const handleCancel = () => {
    router.push("/list");
  };

  return (
    <div className={styles["flex-col"]}>
      <h2>게시글 수정</h2>

      {message && <p>{message}</p>}
      <form
        className={`${styles.form} ${styles["flex-col"]}`}
        onSubmit={handleSubmit}
      >
        <input
          type="hidden"
          name="_id"
          defaultValue={editData._id.toString()}
        />

        <input
          className={styles.input}
          name="title"
          placeholder="글제목"
          value={title}
          onChange={handleChange}
        />
        <textarea
          name="content"
          cols="30"
          rows="10"
          value={content}
          onChange={handleChange}
        />
        <button type="button" onClick={handleCancel}>
          취소
        </button>
        <button type="submit">수정</button>
      </form>
    </div>
  );
}

- API

  • updateOne() 메서드 통해 게시물 업데이트
// /app/api/edit/route.js
import { connectToDatabase } from "@/utils/database";
import { ObjectId } from "mongodb";
import { NextResponse } from "next/server";

export async function PATCH(req) {
  try {
    // 요청 본문에서 데이터 추출
    const { title, content, _id } = await req.json(); 
    
    const { postCollection } = await connectToDatabase();
    const result = await postCollection.updateOne(
      { _id: new ObjectId(_id)}, // 업데이트할 게시글 식별
      { $set: { title, content } } // 업데이트할 내용
    );
    console.log(result);

    return NextResponse.json({
      message: "게시글이 성공적으로 수정되었습니다.",
    });
  } catch (error) {
    console.error(error);
    return NextResponse.json(
      { error: "서버 오류가 발생했습니다." },
      { status: 500 }
    );
  }
}

🔥 오늘의 트러블슈팅

👿 error

Warning: Only plain objects can be passed to Client Components from Server Components.

import EditForm from "@/components/EditForm";
import { connectToDatabase } from "@/utils/database";
import { ObjectId } from "mongodb";

export default async function EditPage({ params }) {
  const { postCollection } = await connectToDatabase();
  let editData = await postCollection.findOne({ _id: new ObjectId(params.id) });

  /* 
  Warning: Only plain objects can be passed to Client Components from Server Components. Objects with toJSON methods are not supported. Convert it manually to a simple value before passing it to props.
  {_id: {buffer: ...}, title: "hi", content: ...} */
  
  return <EditForm editData={editData} />;
}

클라이언트 컴포넌트에 plain object가 아닌 객체(예: ObjectId 객체)를 전달하려고 했기 때문에 발생

✨ 해결

MongoDB에서 가져온 데이터를 클라이언트 컴포넌트로 전달하기 전에 _id 값을 문자열로 변환!

 const editData = { ...data, _id: data._id.toString() };

_id 필드가 문자열이 되어 클라이언트 컴포넌트로 안전하게 전달될 수 있다.

import EditForm from "@/components/EditForm";
import { connectToDatabase } from "@/utils/database";
import { ObjectId } from "mongodb";

export default async function EditPage({ params }) {
  const { postCollection } = await connectToDatabase();
  let data = await postCollection.findOne({ _id: new ObjectId(params.id) });
  
 // MongoDB에서 가져온 데이터를 클라이언트 컴포넌트로 전달하기 전에 _id 값을 문자열로 변환
  const editData = { ...data, _id: data._id.toString() };

  return <EditForm editData={editData} />;
}

:: 진행상황 & TODO ::

  • 몽고 DB setting
  • MongoDB 입출력
  • 글 목록 조회 기능(/list)
    • 글 제목,~~
    • 날짜 데이터바인딩
  • 글 상세 페이지(/detail/[id]/page.js)
    • 제목, 내용,
    • 게시 날짜, 댓글 데이터바인딩
  • 글 작성 페이지
  • 글 수정 페이지
  • 글 삭제 페이지
  • S3 파일업로드
  • Next-auth 회원인증기능
  • 404페이지 만들기
  • 글 작성 페이지 마크다운 작성페이지로 변경
  • 캐싱, 에러처리 등 부가기능
  • 스타일링
  • AWS 클라우드 배포
profile
쑥쑥쑥쑥 레벨업🌱🌼🌳

0개의 댓글