<script>
import DetailPage from "@containers/main/board/detail/DetailPage"
import { BoardItem } from "@models/board"
import { getboardById } from "@services/board"
import { GetServerSidePropsContext } from "next"
import React from "react"
const Index = ({
data,
board_name,
board_id
}: {
data: BoardItem | undefined
board_name: string
board_id: string
}) => {
return <DetailPage data={data} board_name={board_name} board_id={board_id}></DetailPage>
}
export default Index
export async function getServerSideProps(context: GetServerSidePropsContext) {
// "/commnity/boardName/id"
const splitedPath = context.resolvedUrl.split("/")
const splitedPathLength = splitedPath.length
const board_name = splitedPath[2]
const board_id = splitedPath[splitedPathLength - 1]
if (!board_id || !board_name) throw new Error("board_id, board_name is missing")
const data = await getboardById({ board_id, board_name })
if (!data) return
return {
props: { data: JSON.parse(JSON.stringify(data)), board_name, board_id }
}
}
</script>
먼저 위와같이 게시판을 보여줄땐 url로 요청이 들어오는 순간 최신화된 데이터를 사용자에게 제공해야한다.
next를 사용하면서 이런 로직에 대한 충분한 고민이 필요하다는것을 실전에서야 느꼈다.
위와같이 사용자에게 요청받은 url로 원하는 부분을 추출하고 , DetailPage로 data props를 넘겨줬다.
<script>
import React from "react"
import { BoardItem } from "@models/board"
// eslint-disable-next-line no-unused-vars
import { deleteBoard, getboardById, updateBoard } from "@services/board"
// deleteBoard
import { addComment, deleteComment, updateComment } from "@services/comment"
import { addReComment, deleteReComment } from "@services/recomment"
import Link from "next/link"
import { useRouter } from "next/router"
import { useRef, useState } from "react"
import { useUserState } from "src/10.context/AuthUserContext"
import BoardCommentItem from "src/11.board_comment_item/Board_comment_item"
import * as S from "../DetailPage.style"
const DetailPage = ({
data,
board_name,
board_id
}: {
data: BoardItem | undefined
board_name: string
board_id: string
}) => {
const { token, user } = useUserState()
const router = useRouter()
const [recommentInputVisible, setRecommentInputVisible] = useState<boolean>(false)
const [commentModify, setCommentModify] = useState<boolean>(false)
const [content, setContent] = useState("")
const boardCommentRef = useRef<{
board_name: string
board_id: string
comment_author_name: string
}>({
board_name,
board_id,
comment_author_name: ""
})
if (!data) throw new Error("no data")
const isAuthor = data.author == user?.uid
const boardRecommentRef = useRef<{
content: string
board_name: string
board_id: string
}>({
content: "",
board_name,
board_id
})
return (
<S.Wrapper>
<div className="board_title_author_name_container">
<h3 className="board_content_title">{data.title}</h3>
<span className="board_content_author_name">{data.board_author_name}</span>
<span className="board_created_at">작성일자:</span>
</div>
<div className="board_content_container">
<p className="board_content">{data.content}</p>
</div>
<div className="comment_container">
<div className="board_delete_update_box">
{isAuthor ? (
<span className="board_delete_btn" onClick={() => handleDeleteBoard()}>
글 삭제하기
</span>
) : null}
<Link href={`/community/${board_name}/modify/${data.board_id}`}>
<a>
{isAuthor ? (
<span className="board_update_btn" onClick={() => handleUpdateBoard()}>
글 수정하기
</span>
) : null}
</a>
</Link>
</div>
<div className="comment_box">
<h3>댓글</h3>
<input
onChange={(e) => setContent(e.target.value)} //content value = "안녕하세요"
className="comment_input_area"
type="text"
maxLength={400}
value={content}
/>
<button
className="comment_submit_btn"
onClick={() => {
handleCommentSubmit()
}}
>
등록
</button>
</div>
<div className="comment_board">
<ul>
{data.comments?.map((comment, i) => (
<>
<BoardCommentItem
board_name={board_name}
board_id={board_id}
handleModifyComment={handleModifyComment}
setCommentModify={setCommentModify}
commentModify={commentModify}
boardRecommentRef={boardRecommentRef}
setRecommentInputVisible={setRecommentInputVisible}
recommentInputVisible={recommentInputVisible}
handleDeleteComment={handleDeleteComment}
handleDeleteRecomment={handleDeleteRecomment}
handleAddRecomment={handleAddRecomment}
comment={comment}
i={i}
user={user}
></BoardCommentItem>
</>
))}
</ul>
</div>
</div>
</S.Wrapper>
)
///
function inputValueReset() {
setContent("")
}
function handleCommentSubmit() {
console.log("!")
const { board_name, board_id } = boardCommentRef.current
if (!token || !user) throw new Error("token or user is missing")
addComment({ token, user }, { content, board_name, board_id })
.then(() => router.push(router.asPath))
.then(() => inputValueReset())
}
function handleDeleteBoard() {
if (!token || !user) throw new Error("token or user is missing")
const deleteReCommentAgree = confirm("정말 삭제하시겠습니까?")
if (deleteReCommentAgree) {
deleteBoard(
{ token, user },
{
board_name,
board_id
}
).then(() => router.push(`/community/${board_name}`))
} else return
}
function handleUpdateBoard() {
if (!token || !user) throw new Error("token or user 2is missing")
if (!data) throw new Error("no data")
updateBoard(
{ token, user },
{
board_name,
title: data.title,
content: data.content,
board_id,
is_secret: data?.is_secret || null,
is_notice: data?.is_notice || null,
secret_number: data?.secret_number || ""
}
)
}
function handleDeleteComment(comment_author_id: string, comment_id: string) {
if (!token || !user) throw new Error("token or user is missing")
const deleteCommentAgree = confirm("정말 삭제하시겠습니까?")
if (deleteCommentAgree) {
deleteComment(
{ token, user },
{
board_name,
board_id,
comment_id,
comment_author_id
}
).then(() => router.push(router.asPath))
} else return
}
function handleModifyComment({
content,
board_name,
board_id,
comment_id,
comment_author_id
}: {
content: string
board_name: string
board_id: string
comment_id: string
comment_author_id: string
}) {
if (!token || !user) throw new Error("token or user is missing")
console.log(board_id)
updateComment(
{ token, user },
{
content,
board_name,
board_id,
comment_id,
comment_author_id
}
).then(() => router.push(router.asPath))
}
//대댓글 작성
function handleAddRecomment(comment_id: string) {
if (!token || !user) throw new Error("token or user is missing")
const { content, board_name, board_id } = boardRecommentRef.current
addReComment(
{ token, user },
{
content,
board_name,
board_id,
comment_id
}
).then(() => router.push(router.asPath))
setRecommentInputVisible(false)
}
//대댓글 삭제
function handleDeleteRecomment(comment_id: string, re_comment_id: string) {
if (!token || !user) throw new Error("token or user is missing")
const deleteReCommentAgree = confirm("정말 삭제하시겠습니까?")
if (deleteReCommentAgree) {
deleteReComment(
{ token, user },
{
board_name,
board_id,
comment_id,
re_comment_id
}
).then(() => router.push(router.asPath))
} else return
}
}
</script>
export default DetailPage
먼저 useUserState를 통해 사용자의 대한 데이터를 받아오고 대댓글이 보이는 조건 ,
댓글삭제조건 ,댓글 수정 조건 의 대한 모든 분기처리를 하는데 시간이 꽤 오래걸렸던것같다.
먼저 댓글삭제하기 버튼이 보이는 조건자체가 내가 쓴 댓글일때만 이기때문에
const isAuthor = data.author == user?.uid
를 통해서 변수로 담아두고 수정 삭제 , 대댓글삭제 에 재사용을 할 수 있게 사용했다.
또한 함수를 function으로 가장 아래로 둔 것은 호이스팅으로 인해 상태가 끌어올려져 동작은 똑같이 하기때문에 조금 더 가독성이 좋게 만들었다 .
const () => {} <<이런형식으로 하면 당연히 선언과 초기화단계가 일치하지 않기에 function 키워드만 가능하다 .
컴포넌트의 구조 설계
daetailPage getServerSideProps전달
|
daetail Component (getServerSideProps사용)
|
BoardCommentItem(getServerSideProps사용)
|
BoardRecommentItem
위와같이 Detail page를 구성하고 재사용성을 높였다 . 중복되고 반복되는 부분의 스타일링은
globalCss에서 다루고 styled Component를 사용했다.