Skip to content

Commit 3518a22

Browse files
committed
게시글 수정 및 삭제 기능 구현
1 parent 7029572 commit 3518a22

File tree

7 files changed

+227
-36
lines changed

7 files changed

+227
-36
lines changed

app/(page)/articles.jsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,9 @@ export default function Articles() {
276276
<tr
277277
key={i}
278278
onClick={() => {
279-
router.push(`articles/${noteData.length - i}`);
279+
router.push(
280+
`articles/${noteData.length - i}?id=${data.uuid}`
281+
);
280282
}}
281283
>
282284
{isMobile ? null : <td>{noteData.length - i}</td>}

app/(page)/note.jsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,9 @@ export default function Note() {
114114
<tr
115115
key={i}
116116
onClick={() => {
117-
router.push(`note/${noteData.length - i}`);
117+
router.push(
118+
`note/${noteData.length - i}?id=${data.uuid}`
119+
);
118120
}}
119121
>
120122
{isMobile ? null : <td>{noteData.length - i}</td>}

app/(page)/write.jsx

Lines changed: 107 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ import {
1717
setDoc,
1818
getDocFromCache,
1919
query,
20+
updateDoc,
2021
} from "firebase/firestore";
22+
import { v4 } from "uuid";
2123

2224
export default function Write({ searchParams }) {
2325
const [title, setTitle] = useState("");
@@ -27,6 +29,8 @@ export default function Write({ searchParams }) {
2729
const [isLogin, setLogin] = useState(false);
2830
const router = useRouter();
2931

32+
const [modifyData, setModifyData] = useState([]);
33+
3034
const init = async () => {
3135
// 처음 마운트 될 때 실행되는 함수
3236
// ready 시킨 후 localStorage에 user 존재하면 자동로그인
@@ -43,10 +47,45 @@ export default function Write({ searchParams }) {
4347
});
4448
};
4549

50+
useEffect(() => {
51+
async function fetchIds() {
52+
const type = searchParams.type == "normal" ? "articles" : "announcements";
53+
const querySnapshot = await getDocs(collection(db, type));
54+
const docIds = querySnapshot.docs.map((doc) => doc.id);
55+
const promises = docIds.map(async (id) => {
56+
const snapshot = await getDoc(doc(db, type, id));
57+
return snapshot.data();
58+
});
59+
60+
const results = await Promise.all(promises);
61+
return results;
62+
}
63+
64+
async function fetchData() {
65+
const data = await fetchIds();
66+
setModifyData(data);
67+
console.log(data);
68+
}
69+
70+
fetchData();
71+
}, []);
72+
4673
useEffect(() => {
4774
init();
4875
}, []);
4976

77+
useEffect(() => {
78+
if (searchParams.mode == "modify") {
79+
modifyData.forEach((data, idx) => {
80+
if (data.uuid == searchParams.id) {
81+
setTitle(data.title);
82+
setMdValue(data.message);
83+
setUserName(data.userName);
84+
}
85+
});
86+
}
87+
}, [modifyData]);
88+
5089
useEffect(() => {
5190
if (searchParams.type == "announce" && isLogin) setUserName("잡케");
5291
}, [isLogin]);
@@ -64,47 +103,85 @@ export default function Write({ searchParams }) {
64103
<button
65104
onClick={async () => {
66105
if (mdValue && title && userName) {
67-
if (searchParams.type == "announce") {
68-
if (isLogin) {
69-
await addDoc(collection(db, "announcements"), {
106+
if (searchParams.mode == "modify") {
107+
await updateDoc(
108+
doc(
109+
db,
110+
searchParams.type == "normal"
111+
? "articles"
112+
: "announcements",
113+
searchParams.id
114+
),
115+
{
70116
title: title,
71117
message: mdValue,
72-
creationTime: new Date(),
73118
userName: userName,
74-
});
75-
setTitle("");
76-
setMdValue("");
77-
alert("글이 등록되었습니다.");
78-
router.push("/note");
79-
} else {
80-
alert("비정상적인 접근입니다.");
81-
}
82-
} else if (searchParams.type == "normal") {
83-
if (userName == "관리자" || userName == "잡케") {
119+
}
120+
);
121+
alert("글이 수정되었습니다.");
122+
router.push(
123+
"/" + (searchParams.type == "normal" ? "articles" : "note")
124+
);
125+
} else {
126+
if (searchParams.type == "announce") {
84127
if (isLogin) {
85-
await addDoc(collection(db, "articles"), {
128+
const uuid = v4();
129+
await setDoc(doc(db, "announcements", uuid), {
86130
title: title,
87131
message: mdValue,
88132
creationTime: new Date(),
89133
userName: userName,
90-
reply: [],
134+
uuid: uuid,
91135
});
92-
router.push("/articles");
136+
setTitle("");
137+
setMdValue("");
138+
alert("글이 등록되었습니다.");
139+
router.push("/note");
93140
} else {
94-
alert("사용 불가한 이름입니다.");
141+
alert("비정상적인 접근입니다.");
142+
}
143+
} else if (searchParams.type == "normal") {
144+
if (userName == "관리자" || userName == "잡케") {
145+
if (isLogin) {
146+
const uuid = v4();
147+
await setDoc(doc(db, "articles", uuid), {
148+
title: title,
149+
message: mdValue,
150+
creationTime: new Date(),
151+
userName: userName,
152+
reply: [],
153+
uuid: uuid, // v4() -> '1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed' uuid 결과 출력하는 함수
154+
});
155+
router.push("/articles");
156+
} else {
157+
alert("사용 불가한 이름입니다.");
158+
}
159+
} else {
160+
const uuid = v4();
161+
let userPw = prompt(
162+
"수정과 삭제 시 사용할 비밀번호를 등록해주세요."
163+
);
164+
if (userPw == null) {
165+
alert("취소되었습니다.");
166+
} else {
167+
while (!userPw) {
168+
userPw = prompt("비밀번호를 다시 입력해주세요.");
169+
}
170+
await setDoc(doc(db, "articles", uuid), {
171+
title: title,
172+
message: mdValue,
173+
creationTime: new Date(),
174+
userName: userName,
175+
reply: [],
176+
uuid: uuid,
177+
password: userPw,
178+
});
179+
setTitle("");
180+
setMdValue("");
181+
alert("글이 등록되었습니다.");
182+
router.push("/articles");
183+
}
95184
}
96-
} else {
97-
await addDoc(collection(db, "articles"), {
98-
title: title,
99-
message: mdValue,
100-
creationTime: new Date(),
101-
userName: userName,
102-
reply: [],
103-
});
104-
setTitle("");
105-
setMdValue("");
106-
alert("글이 등록되었습니다.");
107-
router.push("/articles");
108185
}
109186
}
110187
} else {

app/[page]/[id]/page.jsx

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {
1919
arrayUnion,
2020
updateDoc,
2121
} from "firebase/firestore";
22-
import { useSearchParams } from "next/navigation";
22+
import { useRouter, useSearchParams } from "next/navigation";
2323

2424
export default function Page({ params: { page, id } }) {
2525
const [noteData, setNoteData] = useState([]);
@@ -32,6 +32,8 @@ export default function Page({ params: { page, id } }) {
3232

3333
const searchParams = useSearchParams();
3434

35+
const router = useRouter();
36+
3537
function formatTime(t) {
3638
const wallTime = new Date(t);
3739
const formatWallTime = `${wallTime.getFullYear()}-${
@@ -128,6 +130,54 @@ export default function Page({ params: { page, id } }) {
128130
<span>
129131
작성일 : {formatTime(data.creationTime.seconds * 1000)}
130132
</span>
133+
<div>
134+
<div
135+
onClick={() => {
136+
router.push(
137+
`/write?type=normal&mode=modify&id=${data.uuid}`
138+
);
139+
}}
140+
>
141+
수정
142+
</div>
143+
<div
144+
onClick={() => {
145+
(async () => {
146+
if (isLogin) {
147+
const check = confirm("글을 삭제하시겠습니까?");
148+
if (check) {
149+
await deleteDoc(
150+
doc(db, "articles", data.uuid)
151+
);
152+
alert("관리자 권한으로 글이 삭제되었습니다.");
153+
router.push("/articles");
154+
} else {
155+
alert("취소되었습니다.");
156+
}
157+
} else {
158+
const check = confirm("글을 삭제하시겠습니까?");
159+
if (check) {
160+
const pw = prompt("비밀번호를 입력해주세요.");
161+
if (pw == data.password) {
162+
await deleteDoc(
163+
doc(db, "articles", data.uuid)
164+
);
165+
alert("글이 삭제되었습니다.");
166+
} else {
167+
alert(
168+
"비밀번호가 올바르지 않습니다. 다시 시도해주세요."
169+
);
170+
}
171+
} else {
172+
alert("취소되었습니다.");
173+
}
174+
}
175+
})();
176+
}}
177+
>
178+
삭제
179+
</div>
180+
</div>
131181
</div>
132182
<MDEditor.Markdown source={data.message} />
133183
<div className={styles.replyText}>
@@ -235,6 +285,39 @@ export default function Page({ params: { page, id } }) {
235285
<span>
236286
작성일 : {formatTime(data.creationTime.seconds * 1000)}
237287
</span>
288+
<div>
289+
<div
290+
onClick={() => {
291+
router.push(
292+
`/write?type=note&mode=modify&id=${data.uuid}`
293+
);
294+
}}
295+
>
296+
수정
297+
</div>
298+
<div
299+
onClick={() => {
300+
(async () => {
301+
if (isLogin) {
302+
const check = confirm("글을 삭제하시겠습니까?");
303+
if (check) {
304+
await deleteDoc(
305+
doc(db, "announcements", data.uuid)
306+
);
307+
alert("관리자 권한으로 글이 삭제되었습니다.");
308+
router.push("/note");
309+
} else {
310+
alert("취소되었습니다.");
311+
}
312+
} else {
313+
alert("글 삭제 권한이 없습니다.");
314+
}
315+
})();
316+
}}
317+
>
318+
삭제
319+
</div>
320+
</div>
238321
</div>
239322
<MDEditor.Markdown source={data.message} />
240323
</div>

package-lock.json

Lines changed: 15 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"react-dom": "^18.3.1",
2121
"react-responsive": "^10.0.0",
2222
"rehype-sanitize": "^6.0.0",
23-
"styled-components": "^6.1.9"
23+
"styled-components": "^6.1.9",
24+
"uuid": "^10.0.0"
2425
}
2526
}

styles/viewer.module.css

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,25 @@
2222
/* 공지 날짜 */
2323
font-size: 0.9rem;
2424
font-weight: 200;
25-
color: var(--gray);
2625
padding-bottom: 1.5rem;
2726
border-bottom: 1px solid #383838;
2827
display: flex;
2928
gap: 1rem;
3029
}
3130

31+
.note > div > div:nth-child(2) > div {
32+
/* 수정 및 삭제 컨테이너 */
33+
flex-grow: 1;
34+
display: flex;
35+
justify-content: flex-end;
36+
gap: 0.8rem;
37+
padding-right: 1rem;
38+
& > div {
39+
color: #797979;
40+
cursor: pointer;
41+
}
42+
}
43+
3244
.note > div > div:nth-child(3) {
3345
/* 공지 내용 */
3446
padding: 2rem;

0 commit comments

Comments
 (0)