Project

[javascript] 네이버 클론 코딩 + 블로그 5: 게시글 수정

끊임없이 개발하는 새럼 2025. 3. 13. 17:25

🚀 현재 프로젝트 환경 정리

 네이버 스타일 로그인 & 블로그 시스템 구현 중
 사용된 기술 및 개발 도구 정리

 코드가 수정될 때마다 게시글 계속 업데이트 중


📌 프로젝트 개요

  • 프로젝트명: 네이버 스타일 로그인 & 블로그 클론 코딩
  • DB: 로컬 스토리지 (LocalStorage) 사용
  • 프레임워크: React (JSX 기반 UI 구성)
  • 백엔드: Node.js
  • 개발 언어: Javascript
  • 개발툴: Visual Studio Code

 

챗 gpt한테만 항상 의존해 코딩을 짜고 있다가,

혼자 시도해보려니 도저히 머리가 안 굴러가서

GPT에 의존하던 코딩 방식에서 벗어나, 참고만 하며 직접 고민하며 개발하려고 한다.

 

그렇기 때문에 개발 속도는 아주 느릴 예정이다.

(이해가 안 가는 부분이 나올 때마다 하나씩 물어가며 이해하고 진행하려고 함)

이 게시글은 지속적으로 수정될 수 있어, 현재 코드가 완성본이 아닐 수 있다.

 

 

EditPost.js

import React, { useState, useEffect } from "react";
import { useParams, useNavigate } from "react-router-dom";
import { EditorContent, useEditor } from "@tiptap/react";
import StarterKit from "@tiptap/starter-kit";
import Bold from "@tiptap/extension-bold";
import Italic from "@tiptap/extension-italic";
import Underline from "@tiptap/extension-underline";
import Heading from "@tiptap/extension-heading";
import BulletList from "@tiptap/extension-bullet-list";
import OrderedList from "@tiptap/extension-ordered-list";
import ListItem from "@tiptap/extension-list-item";

import PostDetail from "./PostDetail";

function EditPost() {

    const { id } = useParams();
    const navigate = useNavigate();
    const [post, setPost] = useState(null);
    const [title, setTitle] = useState("");
    const [content, setContent] = useState("");

    const editor = useEditor({
        extensions: [
            StarterKit,
            Bold,
            Italic,
            Underline,
            Heading,
            BulletList,
            OrderedList,
            ListItem,
        ],
        content: "",
    });

    useEffect(() => {


        // 기존 게시글 데이터 가져오기
        const savedPosts = JSON.parse(localStorage.getItem("posts")) || [];
        const foundPost = savedPosts.find((p) => p.id === Number(id));

        console.log("foundPost: ", foundPost.content);
        if (foundPost) {
            setPost(foundPost);
            setTitle(foundPost.title);
            setContent(foundPost.content);
        } else {
            alert("게시글을 찾을 수 없습니다.");
            navigate("/blog");
        }
    }, [id, navigate]);

    useEffect(() => {
        if (editor && content) {
            editor.commands.setContent(content); // 기존 내용을 에디터에 삽입
        }
    }, [editor, content]);


    const handleSave = () => {
        if (!title.trim() || !content.trim()) {
            alert("제목과 내용을 입력하세요.");
            return;
        }

        // 기존 게시글 수정
        const savePosts = JSON.parse(localStorage.getItem("posts")) || [];
        const updatePosts = savePosts.map((p) =>
            p.id === Number(id) ? { ...p, title, content } : p
        );

        localStorage.setItem("posts", JSON.stringify(updatePosts));
        alert("게시글이 수정되었습니다.");

        navigate(`/post/${id}`); // ✅ 수정 완료 후 해당 게시글로 이동
    };

    return (
        <div className="edit-post-container">
            <h2>게시글 수정</h2>
            <input
                type="text"
                value={title}
                onChange={(e) => setTitle(e.target.value)}
                placeholder="제목을 입력하세요"
            />
            {editor && (
                <div className="toolbar">
                    <button onClick={() => editor.chain().focus().toggleBold().run()} className={editor.isActive("bold") ? "active" : "ㅎㅇㅎㅇ"}>
                        <b>B</b>
                    </button>
                    <button onClick={() => editor.chain().focus().toggleItalic().run()} className={editor.isActive("italic") ? "active" : ""}>
                        <i>I</i>
                    </button>
                    <button onClick={() => editor.chain().focus().toggleUnderline().run()} className={editor.isActive("underline") ? "active" : ""}>
                        <u>U</u>
                    </button>
                    <button onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()} className={editor.isActive("heading", { level: 1 }) ? "active" : ""}>
                        H1
                    </button>
                    <button onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()} className={editor.isActive("heading", { level: 2 }) ? "active" : ""}>
                        H2
                    </button>
                    <button onClick={() => editor.chain().focus().toggleBulletList().run()} className={editor.isActive("bulletList") ? "active" : ""}>
                        • List
                    </button>
                    <button onClick={() => editor.chain().focus().toggleOrderedList().run()} className={editor.isActive("orderedList") ? "active" : ""}>
                        1. List
                    </button>
                    <button onClick={() => editor.chain().focus().undo().run()}>↶ Undo</button>
                    <button onClick={() => editor.chain().focus().redo().run()}>↷ Redo</button>
                </div>
            )}
            <EditorContent editor={editor} className="editor" value={content} onChange={(e) => setContent(e.target.value)} placeholder="내용을 입력하세요."/>

            <button onClick={handleSave}>수정 완료</button>
        </div>
    );
}

export default EditPost;