diff --git a/src/api/boardApi.js b/src/api/boardApi.js
index ea4ad90..6839e17 100644
--- a/src/api/boardApi.js
+++ b/src/api/boardApi.js
@@ -1,38 +1,51 @@
-// src/api/boardApi.js
-import axios from 'axios';
-import { attachAuthInterceptors } from './attachInterceptors';
+import axios from "axios";
+import { attachAuthInterceptors } from "./attachInterceptors";
const boardApi = axios.create({
baseURL: process.env.REACT_APP_API_BOARD, // ex) http://localhost:8801/api
headers: {
- 'Content-Type': 'application/json',
+ "Content-Type": "application/json",
},
});
-// JWT 인증 토큰 인터셉터 부착
+// ✅ JWT 토큰 자동 포함
attachAuthInterceptors(boardApi);
-// ✅ 게시판 목록 조회
-export const fetchBoards = () => boardApi.get('/api/boards/');
+//
+// ✅ 게시판 관련
+//
-// ✅ 특정 게시판의 게시글 목록 조회
+// 게시판 목록 조회
+export const fetchBoards = () => boardApi.get("/api/boards/");
+
+//
+// ✅ 게시글 관련 (게시판 내 nested 구조)
+//
+
+// 특정 게시판의 게시글 목록 조회
export const fetchPosts = (boardSlug, params = {}) =>
boardApi.get(`/api/boards/${boardSlug}/posts/`, { params });
-// ✅ 특정 게시글 상세 조회
export const fetchPostDetail = (boardSlug, postId) =>
boardApi.get(`/api/boards/${boardSlug}/posts/${postId}/`);
-// ✅ 게시글 생성
+// 게시글 생성
export const createPost = (boardSlug, data) =>
boardApi.post(`/api/boards/${boardSlug}/posts/`, data);
-// ✅ 게시글 수정
+// 게시글 수정
export const updatePost = (boardSlug, postId, data) =>
boardApi.put(`/api/boards/${boardSlug}/posts/${postId}/`, data);
-// ✅ 게시글 삭제
+// 게시글 삭제
export const deletePost = (boardSlug, postId) =>
boardApi.delete(`/api/boards/${boardSlug}/posts/${postId}/`);
+//
+// ✅ 게시글 단일 조회 (boardSlug 없이 ID만으로 조회)
+//
+
+// 단일 게시글 상세 조회 (id 기반)
+export const getPost = (postId) => boardApi.get(`/api/posts/${postId}/`);
+
export default boardApi;
diff --git a/src/components/Board/PostDetail.js b/src/components/Board/PostDetail.js
new file mode 100644
index 0000000..8eb46e5
--- /dev/null
+++ b/src/components/Board/PostDetail.js
@@ -0,0 +1,71 @@
+// src/components/Board/PostDetail.js
+import React, { useEffect, useState } from "react";
+import { fetchPostDetail, deletePost } from "../../api/boardApi";
+import { useAuth } from "../../context/AuthContext";
+
+const PostDetail = ({ postId, boardSlug, onClose, onDeleted }) => {
+ const [post, setPost] = useState(null);
+ const { user } = useAuth();
+
+ useEffect(() => {
+ const fetch = async () => {
+ try {
+ const res = await fetchPostDetail(boardSlug, postId); // ✅ boardSlug 포함 요청
+ setPost(res.data);
+ } catch (err) {
+ console.error("❌ 게시물 조회 실패", err);
+ alert("게시글을 불러오지 못했습니다.");
+ onClose();
+ }
+ };
+ fetch();
+ }, [postId, boardSlug, onClose]); // ✅ boardSlug 의존성 포함
+
+ const handleDelete = async () => {
+ if (!window.confirm("정말 삭제하시겠습니까?")) return;
+ try {
+ await deletePost(boardSlug, postId); // ✅ boardSlug 포함
+ alert("✅ 삭제되었습니다.");
+ onDeleted();
+ onClose();
+ } catch (err) {
+ console.error("❌ 삭제 실패", err);
+ alert("삭제에 실패했습니다.");
+ }
+ };
+
+ if (!post) return null;
+
+ const isAuthor = user?.email === post.author_name;
+
+ return (
+
+
{post.title}
+
작성자: {post.author_name}
+
+ 작성일: {new Date(post.created_at).toLocaleString()}
+
+
+
{post.content}
+
+
+
+ 닫기
+
+ {isAuthor && (
+
+ 삭제
+
+ )}
+
+
+ );
+};
+
+export default PostDetail;
diff --git a/src/components/Board/PostForm.js b/src/components/Board/PostForm.js
index 52eab7e..866bff5 100644
--- a/src/components/Board/PostForm.js
+++ b/src/components/Board/PostForm.js
@@ -1,4 +1,3 @@
-// src/components/Board/PostForm.js
import React, { useState } from "react";
import { createPost } from "../../api/boardApi";
@@ -34,11 +33,11 @@ const PostForm = ({ boardSlug, onClose, onCreated }) => {
console.log("✅ 전송할 payload:", payload);
try {
- await createPost(boardSlug, payload);
+ await createPost(boardSlug, payload); // ✅ 백엔드 호환: slug 사용
alert("✅ 게시글이 등록되었습니다.");
- setForm({ title: "", content: "", tags: "" });
- onCreated();
- onClose();
+ setForm({ title: "", content: "", tags: "" }); // 폼 초기화
+ onCreated(); // 목록 새로고침 트리거
+ onClose(); // 폼 닫기
} catch (err) {
console.error("❌ 게시글 등록 실패", err);
console.log("🚨 서버 응답 내용:", err.response?.data);
diff --git a/src/components/Board/PostList.js b/src/components/Board/PostList.js
index e6a9f26..c22fa10 100644
--- a/src/components/Board/PostList.js
+++ b/src/components/Board/PostList.js
@@ -1,43 +1,113 @@
-// src/components/Board/PostList.js
-import React, { useEffect, useState } from "react";
+import React, { useEffect, useState, useCallback } from "react";
import { fetchPosts } from "../../api/boardApi";
+import PostDetail from "./PostDetail";
const PostList = ({ boardSlug, search, tag }) => {
const [posts, setPosts] = useState([]);
+ const [selectedPostId, setSelectedPostId] = useState(null);
+ const [drawerOpen, setDrawerOpen] = useState(false);
+
+ const loadPosts = useCallback(async () => {
+ try {
+ const params = {};
+ if (tag) params.tag = tag;
+ if (search) params.search = search;
+
+ const res = await fetchPosts(boardSlug, params);
+ setPosts(res.data);
+ } catch (err) {
+ console.error("❌ 게시글 목록 불러오기 실패", err);
+ }
+ }, [boardSlug, tag, search]);
useEffect(() => {
- if (!boardSlug) return;
+ if (boardSlug) {
+ loadPosts();
+ }
+ }, [boardSlug, loadPosts]);
- const loadPosts = async () => {
- try {
- const params = {};
- if (tag) params.tag = tag;
- if (search) params.search = search;
-
- const res = await fetchPosts(boardSlug, params);
- setPosts(res.data);
- } catch (err) {
- console.error("❌ 게시글 목록 불러오기 실패", err);
+ // ✅ ESC 키로 닫기
+ useEffect(() => {
+ const handleKeyDown = (e) => {
+ if (e.key === "Escape") {
+ setDrawerOpen(false);
+ setSelectedPostId(null);
}
};
- loadPosts();
- }, [boardSlug, search, tag]);
+ if (drawerOpen) {
+ window.addEventListener("keydown", handleKeyDown);
+ } else {
+ window.removeEventListener("keydown", handleKeyDown);
+ }
+
+ return () => {
+ window.removeEventListener("keydown", handleKeyDown);
+ };
+ }, [drawerOpen]);
+
+ const handleSelectPost = (postId) => {
+ setSelectedPostId(postId);
+ setDrawerOpen(true);
+ };
+
+ const handleCloseDrawer = () => {
+ setDrawerOpen(false);
+ setSelectedPostId(null);
+ };
if (!boardSlug) return 📂 게시판을 선택해주세요.
;
return (
-
+
📝 게시글 목록
{posts.map((post) => (
-
+ handleSelectPost(post.id)}
+ >
{post.title}
작성자: {post.author_name}
{post.content}
))}
+
+ {/* ✅ 슬라이딩 Drawer */}
+
+
+
+ ✖ 닫기
+
+
+
+ {selectedPostId && (
+
+ )}
+
+
+
+ {/* ✅ 배경 오버레이 */}
+ {drawerOpen && (
+
+ )}
);
};
diff --git a/version b/version
index bc719c4..0e365fb 100644
--- a/version
+++ b/version
@@ -1 +1 @@
-0.0.17-rc1
\ No newline at end of file
+0.0.17-rc2
\ No newline at end of file