본문 바로가기
프로젝트 기록

[Next.js+MongoDB] 게시판 북마크 기능 구현하기 (feat. App Router)

by dygreen 2024. 8. 24.

평소 메모장을 사용할 때 편리했던 기능 중에 하나인 '북마크 기능'을

현재 리팩토링을 진행하고 있는 게시판 프로젝트에 적용해보고 싶었다

 

북마크 버튼을 통해 게시글을 북마크하고,

북마크된 게시글을 최상단에서 볼 수 있는 기능을 구현하고자 한다.


구현 화면

 

목차

  • 북마크 기능 구현 단계
  • MongoDB를 활용한 API 세팅
  • (Next.js App Router 를 사용한) UI 세팅

 

 

📌 북마크 기능 구현 단계

북마크 기능을 구현하기 위해서는 아래 단계를 거치면 된다.

 

  1. 데이터베이스 내 게시글 데이터에 북마크 관련 필드를 추가한다
  2. 북마크 여부를 저장하는 API 를 세팅한다
  3. UI 를 세팅한다 (→ 북마크 버튼, 북마크된 게시글이 최상단에 위치할 수 있도록 재정렬)

 

 

📌 MongoDB 를 활용한 API 세팅

게시글 북마크 관련 필드값은 isBookmarked 로 지정했다.

 

api/board/bookmark-article/route.ts
import { NextRequest, NextResponse } from 'next/server'
import { connectDB } from '@util/database'
import { ObjectId } from 'mongodb'

export async function POST(req: NextRequest) {
    try {
        const db = (await connectDB).db('board')
        const body = await req.json()

        await db
            .collection('article')
            .updateOne(
                { _id: new ObjectId(body.id as string) },
                { $set: { isBookmarked: body.isBookmarked } },
            )

        return NextResponse.json({ data: true })
    } catch (e) {
        return NextResponse.json(
            { message: '북마크 설정 중 오류가 발생했습니다.' },
            { status: 500 },
        )
    }
}
  • App Router ver. API 로직으로, POST 함수를 생성한다
  • body 로 게시글 id 값북마크 여부를 나타내는 isBookmarked 값을 받는다
  • updateOne : 해당하는 id 값을 가진 게시글을 찾아, 북마크 여부를 바꾼다
    • $set 연산자 : 필드 값을 지정된 값으로 바꾼다
  • 응답값은 boolean 형태로 주어, 요청이 성공했는지 실패했는지 판단할 수 있도록 하였다

 

 

📌 UI 세팅 (feat. App Router)

BookmarkBtn.tsx
'use client'

import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faThumbTack } from '@fortawesome/free-solid-svg-icons'

export default function BookmarkBtn({
    selected,
    bookmarked,
}: {
    selected: string
    bookmarked: boolean
}) {
    const handleBookmarkArticle = async () => {
        try {
            const response = await fetch('/api/board/bookmark-article', {
                method: 'POST',
                body: JSON.stringify({
                    id: selected,
                    isBookmarked: !bookmarked,
                }),
            })
            const data = await response.json()

            if (response.status === 200) {
                window.location.reload()
            } else if (response.status === 500) {
                alert(data.message)
            }
        } catch (e) {
            console.error(e)
        }
    }

    return (
        <div className="bookmark-wrapper" onClick={handleBookmarkArticle}>
            <FontAwesomeIcon
                icon={faThumbTack}
                className={`article-icon ${bookmarked && 'bookmarked'}`}
            />
        </div>
    )
}
  • 'use client' : click event 가 일어나야 하기 때문에 client component 로 생성한다
  • 해당하는 게시글의 id 값과 bookmark 여부를 알 수 있는 값을 props 로 전달한다
  • 북마크 버튼 클릭 시, id 값과 기존 bookmark 여부의 반대되는 값body 에 실어 보낸다
    • API 요청이 성공했으면 (= 북마크 여부 저장을 성공했으면) 페이지를 새로고침한다
    • 실패했으면 에러 메시지를 띄운다
  • bookmark 여부에 따른 style 은 className 을 통해 주었다.

 

API 요청 성공 - 페이지 새로고침 시

북마크된 게시글이 최상단에 위치하도록 하려면 아래와 같이 데이터를 불러오면 된다

app/page.tsx
import ArticleItem from '@components/board/ArticleItem'
import { connectDB } from '@util/database'
import { ArticleItemFlag } from '@util/interface'

export default async function Home() {
    const db = (await connectDB).db('board')
    const articles = await db
        .collection<ArticleItemFlag>('article')
        .find()
        .sort({ isBookmarked: -1, regDate: -1 })
        .toArray()

    return <ArticleItem articles={articles} />
}
  • sort() 사용
    • isBookmarked: -1 : 북마크된 게시글(isBookmarked가 true)이 가장 먼저 나오도록 내림차순으로 정렬
    • regDate: -1 : 같은 북마크 상태 내에서, 게시물 작성일(regDate)을 기준으로 최신순으로 정렬

 

 

게시판 프로젝트 바로가기👇

 

BOARD

편집됨 (2024.8.24 23:04:27)

board-base.vercel.app

 

728x90

댓글