diff --git a/client/src/App.tsx b/client/src/App.tsx
index 6adc72f8..2c27c745 100644
--- a/client/src/App.tsx
+++ b/client/src/App.tsx
@@ -15,6 +15,7 @@ import useRefreshToken from "./hooks/useRefreshToken";
import Modal from "react-modal";
import TestPage from "./test/TestPage";
import Home from "./pages/Home";
+import HTestPage from "./pages/HTestPage";
// import { worker } from "./mocks/browser";
// // 개발 모드로 실행되었을 때, mocking 라이브러리가 실행되도록 명시하는 코드
@@ -61,6 +62,7 @@ function AppContent() {
} />
} />
} />
+ } />
>
diff --git a/client/src/apis/CommentApi.ts b/client/src/apis/CommentApi.ts
new file mode 100644
index 00000000..598f99fe
--- /dev/null
+++ b/client/src/apis/CommentApi.ts
@@ -0,0 +1,65 @@
+import tokenRequestApi from "./TokenRequestApi";
+import { eduApi } from "./EduApi";
+
+// ====================== 댓글 등록 (post) ===========================
+
+export interface CommentDto {
+ content: string;
+ studygroupId: number;
+ commentId: number;
+ nickName: string;
+}
+
+export const postComment = async (data: string) => {
+ try {
+ const jsonData = JSON.stringify({ content: data }); // 데이터를 JSON 문자열로 직렬화
+ await tokenRequestApi.post("/studygroup/31/comment", jsonData);
+ } catch (error) {
+ console.log(error);
+ throw new Error("댓글 등록 실패");
+ } //31 -> 변수로 나중에 바꿔야 함
+};
+// ====================== 댓글 수정 (patch) ===========================
+export const patchComment = async (
+ studyGroupId: number,
+ patchId: number,
+ data: string
+) => {
+ try {
+ const jsonData = JSON.stringify({ content: data }); // 데이터를 JSON 문자열로 직렬화
+ await tokenRequestApi.patch(
+ `/studygroup/${studyGroupId}/comment/${patchId}`,
+ jsonData
+ );
+ } catch (error) {
+ console.log(error);
+ throw new Error("댓글 수정 실패");
+ }
+}; //31 -> 변수로 나중에 바꿔야 함
+
+// ====================== 댓글 전부 조회 (get) ===========================
+export const getComments = async (
+ studyGroupId: number
+): Promise => {
+ try {
+ const response = await eduApi.get(
+ `/studygroup/${studyGroupId}/comments`
+ ); //31 -> 변수로 나중에 바꿔야 함
+ return response.data;
+ } catch (error) {
+ console.log(error);
+ throw new Error("댓글 전부 조회 실패");
+ }
+};
+
+// ====================== 댓글 삭제 (DELETE) ===========================
+export const deleteComment = async (studyGroupId: number, patchId: number) => {
+ try {
+ const response = await tokenRequestApi.delete(
+ `/studygroup/${studyGroupId}/comment/${patchId}`
+ );
+ console.log("댓글이 삭제되었습니다.", response);
+ } catch (error) {
+ console.error("댓글을 삭제하는데 실패했습니다. 권한을 확인하세요", error);
+ }
+};
diff --git a/client/src/apis/StudyGroupApi.ts b/client/src/apis/StudyGroupApi.ts
index 1fbf0ee1..8f3eb3b7 100644
--- a/client/src/apis/StudyGroupApi.ts
+++ b/client/src/apis/StudyGroupApi.ts
@@ -40,7 +40,7 @@ export interface StudyInfoDto {
introduction: string;
isRecruited: boolean;
tags: {
- [key: string]: string;
+ [key: string]: string[];
};
leader: {
id: number;
@@ -238,9 +238,8 @@ export interface StudyGroupMemberListDto {
}
// TODO : StudyGroup에 가입된 멤버 리스트
-export async function getStudyGroupMemberList (id: number, isLoggedIn : boolean) {
- if (!isLoggedIn)
- throw new Error("Access token is not defined.");
+export async function getStudyGroupMemberList(id: number, isLoggedIn: boolean) {
+ if (!isLoggedIn) throw new Error("Access token is not defined.");
try {
const response = await axios.get(
`${import.meta.env.VITE_APP_API_URL}/studygroup/${id}/member?join=true`
diff --git a/client/src/apis/TokenRequestApi.ts b/client/src/apis/TokenRequestApi.ts
index bc97e890..de1f88ac 100644
--- a/client/src/apis/TokenRequestApi.ts
+++ b/client/src/apis/TokenRequestApi.ts
@@ -5,6 +5,9 @@ import { getRefreshToken } from "../pages/utils/Auth";
let accessToken: string | null = null;
const tokenRequestApi: AxiosInstance = axios.create({
baseURL: `${import.meta.env.VITE_APP_API_URL}`,
+ headers: {
+ "Content-Type": "application/json", // 요청 헤더(content type) 설정
+ },
});
tokenRequestApi.interceptors.request.use(
@@ -52,4 +55,4 @@ tokenRequestApi.setAccessToken = (token): void => {
}
};
-export default tokenRequestApi;
\ No newline at end of file
+export default tokenRequestApi;
diff --git a/client/src/components/StudyComment.tsx b/client/src/components/StudyComment.tsx
index c0735902..0a2afb9d 100644
--- a/client/src/components/StudyComment.tsx
+++ b/client/src/components/StudyComment.tsx
@@ -1,28 +1,39 @@
import styled from "styled-components";
+import { useRecoilValue } from "recoil";
+import { LogInState } from "../recoil/atoms/LogInState";
+//import { useParams } from "react-router-dom";
import { useState } from "react";
// import { Link } from "react-router-dom";
-import axios from "axios";
import { validateEmptyInput } from "../pages/utils/loginUtils";
+import { postComment } from "../apis/CommentApi";
+import { useNavigate } from "react-router-dom";
const StudyComment = () => {
+ //let { id } = useParams();
+ const isLoggedIn = useRecoilValue(LogInState);
+ const navigate = useNavigate();
+
const [comment, setComment] = useState("");
const handleComment = (e: React.ChangeEvent) => {
setComment(e.target.value);
+ //console.log(id);
};
- const handleCommentButton = () => {
- if (validateEmptyInput(comment)) {
+ const handleCommentButton = async () => {
+ if (!isLoggedIn) navigate("/login");
+ else if (validateEmptyInput(comment)) {
alert("댓글 내용을 입력해주세요");
} else {
- axios
- .post(`${import.meta.env.VITE_APP_API_URL}/comment`, {
- comment,
- })
- .catch((error) => {
- console.log(error);
- alert("댓글 내용을 입력해주세요");
- });
+ try {
+ await postComment(comment);
+ setComment("");
+
+ console.log("댓글이 성공적으로 등록되었습니다.");
+ } catch (error) {
+ console.log(error);
+ console.log("댓글 등록에 실패했습니다.");
+ }
}
};
@@ -30,6 +41,7 @@ const StudyComment = () => {
{
+ const isLoggedIn = useRecoilValue(LogInState);
+ const navigate = useNavigate();
+
+ const [comments, setComments] = useState([]);
+ const [comment, setComment] = useState("");
+ const [patchId, setPatchId] = useState(null);
+ const [isUpdateMode, setIsUpdateMode] = useState(false);
+
+ const handleUpdate = (id: number, content: string) => {
+ if (!isLoggedIn) navigate("/login");
+ setIsUpdateMode(!isUpdateMode);
+ setPatchId(id);
+ setComment(content);
+ };
+
+ const handleDelete = async (patchId: number) => {
+ if (!isLoggedIn) navigate("/login");
+ try {
+ const studyGroupId = 31;
+ await deleteComment(studyGroupId, patchId);
+ } catch (error) {
+ console.log("댓글 삭제 실패", error);
+ }
+ };
+
+ const handleComment = (e: React.ChangeEvent) => {
+ setComment(e.target.value);
+ //console.log(id);
+ };
+
+ const handleUpdateButton = async () => {
+ if (!isLoggedIn) navigate("/login");
+
+ if (validateEmptyInput(comment)) {
+ alert("댓글 내용을 입력해주세요.");
+ } else {
+ try {
+ const studyGroupId = 31;
+ if (patchId) {
+ await patchComment(studyGroupId, patchId, comment);
+ setIsUpdateMode(false);
+ setPatchId(null);
+ setComment("");
+ }
+ } catch (error) {
+ console.log("댓글 등록 실패", error);
+ }
+ }
+ };
+ useEffect(() => {
+ const fetchData = async () => {
+ try {
+ const studyGroupId = 31;
+ const newComment = await getComments(studyGroupId);
+ setComments(newComment);
+ } catch (error) {
+ console.log(error);
+ }
+ };
+ fetchData();
+ }, [!isUpdateMode]); // post시 바로 변경될 수 있도록 의존성 배열 추가 예정
+ return (
+ <>
+
+ >
+ );
+};
+
+const CommentItemDiv = styled.div`
+ width: 80%;
+ height: 70px;
+ padding: 10px 10px 10px 10px;
+ background-color: #ffffff;
+ display: flex;
+ justify-content: space-between;
+ border-bottom: solid #e9e9e9;
+`;
+const ContentItem = styled.div`
+ text-align: left;
+ button {
+ margin-left: 10px;
+ background-color: #858da8;
+ font-size: 13px;
+ padding: 3px;
+ color: #ffffff;
+ }
+ p {
+ font-size: 16px;
+ font-weight: bold;
+ color: #2759a2;
+ }
+ span {
+ font-size: 12px;
+ }
+`;
+
+const ButtonDiv = styled.div`
+ height: 100%;
+ display: flex;
+ align-items: flex-end;
+ button {
+ font-size: 12px;
+ padding: 3px;
+ color: #858da8;
+ }
+`;
+export default StudyCommentList;
diff --git a/client/src/components/TagInput.tsx b/client/src/components/TagInput.tsx
index 5bd078dd..1b67abae 100644
--- a/client/src/components/TagInput.tsx
+++ b/client/src/components/TagInput.tsx
@@ -8,14 +8,16 @@ const TagInput = ({
selectedCategory,
tags,
setTags,
+ viewTag,
+ setViewTag,
}: {
selectedCategory: string;
tags: string[];
setTags: React.Dispatch>;
+ viewTag: boolean;
+ setViewTag: React.Dispatch>;
}) => {
- const [view, setView] = useState(false);
-
- const [defaultTag, setDefaultTag] = useState<{ [key: string]: string }>({});
+ const [defaultTag, setDefaultTag] = useState<{ [key: string]: string[] }>({});
const [createdTag, setCreatedTag] = useState("");
const handleTag = (e: React.ChangeEvent) => {
@@ -40,7 +42,6 @@ const TagInput = ({
};
useEffect(() => {
- setView(false);
setTags([]);
setCreatedTag("");
const fetchData = async () => {
@@ -69,13 +70,13 @@ const TagInput = ({
{
- setView(!view);
+ setViewTag(true);
}}
>
- {selectedCategory} {view ? "⌃" : "⌄"}
- {view && defaultTag && (
+ {selectedCategory} {viewTag ? "⌃" : "⌄"}
+ {viewTag && defaultTag && (
diff --git a/client/src/pages/HTestPage.tsx b/client/src/pages/HTestPage.tsx
new file mode 100644
index 00000000..80fc8122
--- /dev/null
+++ b/client/src/pages/HTestPage.tsx
@@ -0,0 +1,13 @@
+import StudyComment from "../components/StudyComment";
+import StudyCommentList from "../components/StudyCommentList";
+const HTestPage = () => {
+ return (
+
+
zz
+
+
+
+ );
+};
+
+export default HTestPage;
diff --git a/client/src/pages/StudyPost.tsx b/client/src/pages/StudyPost.tsx
index a862245d..471e4448 100644
--- a/client/src/pages/StudyPost.tsx
+++ b/client/src/pages/StudyPost.tsx
@@ -19,12 +19,14 @@ const StudyPost = () => {
const [memberCountMax, setMemberCountMax] = useState(1);
const [platform, setPlatform] = useState("");
const [tags, setTags] = useState([]);
+ const [viewTag, setViewTag] = useState(false);
const [introduction, setIntroduction] = useState("");
const [selectedCategory, setSelectedCategory] =
useState("프론트엔드");
const handleCategory = (e: React.ChangeEvent) => {
setSelectedCategory(e.target.value);
+ setViewTag(false);
};
const handleTitle = (e: React.ChangeEvent) => {
@@ -180,6 +182,8 @@ const StudyPost = () => {
selectedCategory={selectedCategory}
tags={tags}
setTags={setTags}
+ viewTag={viewTag}
+ setViewTag={setViewTag}
/>