FrontEnd/Next.js

[ Next.js ] Book 기능사항 구현하기

ChatjihoiPT 2025. 4. 22. 16:44
 

 

  1. utils -> http-commons.js 주소 변경하기
  2. store -> member -> useHook, app -> layout -> bookmark
  3. src -> member -> book -> isbn -> bookmark -> page
  • 구현 사항 - 기능: 등록/ 삭제/ 모두 삭제
    • 등록: 도서 상세 페이지에서
    • 삭제, 모두 삭제: 북마크 리스트 페이지에서 </aside>


1. store > book-mark.tsx

todo1.

제공할 상태와 상태를 변경할 함수에 대한 타입 설정하기

todo2.

createContext() 함수로 제공할 context 생성하기

interface BookContextType {  
//todo1. 사용할 데이터 타입 정의
	books: Book[];//여러 배열 가져오니까 [] 사용
	registBookMark:(book: Book) => void; // 리턴 타입 없음
	removeBookMark:(isbn: string) => void;
	clearBookMark: () => void;
	
//todo2. createContext() 함수로 제공할 context 생성하기
const BookMarkContext = createContext<BookContextType | undefined>(undefined);

}

 

todo3.

타입에 해당하는 구현부를 Provider로 작성하여 리턴하기

todo4.

커스텀 훅: useMemberContext

export const BookMarkProvider = ({ children }: { children: ReactNode }) => {
  const [books, setBook] = useState<Book[]>([]);  // 상태 선언 
const registBookMark = (book: Book) => {
    setBook([...books, book]);  //spread 함수
  };

  const removeBookMark = (isbn: string) => {
    setBook(books.filter((book) => book.isbn !== isbn));  // 리렌더링하기 위해서
  };

  • export 사용해야 다른 파일*(layout)에서 import 할 수 있음

export하는 두가지 방식

  1. rsc 단축키 사용 후 const 화살표 함수, export 문장 사용하기
  2. export default function () {} 사용하기

📌 filter VS prev두 방식 비교

방식 예시 특징 ]

직접 참조 setBook(books.filter(...)) 상태값을 직접 사용함 (렌더링 시점에 따라 부작용 가능성 있음)
함수형 업데이트 (prev) setBook(prev => prev.filter(...)) 가장 최신 상태 기반으로 동작함. 안정성 ↑, 추천! ✅

 

비동기 상태 업데이트가 많거나, 상태 의존도가 높은 앱이라면 prev함수형 업데이트 사용하기

export const BookMarkProvider = ({ children }: { children: ReactNode }) => {
  const [books, setBook] = useState<Book[]>([]);
  const registBookMark = useCallback((book: Book) => {
    setBook((prev) => (prev.find((item) => item.isbn === book.isbn) ? prev : [...prev, book]));
  }, []);

  const removeBookMark = useCallback((isbn: string) => {
    setBook((prev) => prev.filter((item) => item.isbn !== isbn));
  }, []);

  const clearBookMark = useCallback(() => {
    setBook([]);
  }, []);

  const returnValue = useMemo(
    () => ({ books, registBookMark, removeBookMark, clearBookMark }),
    [books]
  );

  return <BookMarkContext.Provider value={returnValue}>{children}</BookMarkContext.Provider>;
};

 

1. useCallback

 

함수를 기억하고 싶을 때

const memoizedFunc = useCallback(() => {
  함수동작();
}, [의존값]);

- 함수 자체를 기억해서, 매번 새로 만들지 않음.

- 주로 자식 컴포넌트에 콜백 함수 props로 넘길 때 사용.

 

2. useMemo

렌더링할 때마다 바로 계산하지 않고,

이 진짜 바뀔 때만 계산하고, 기억해두는 것

const memoizedValue = useMemo(() => {
return 계산결과;
}, [의존성]);

- 계산 결과: 복잡한 연산 또는 함수 호출

- [의존성]: 이 값이 바뀔 때만 다시 계산됨


2. app > layout.tsx

export default function RootLayout({
                   // return (...) 형태로 레이아웃 구조를 반환
  children,
}: Readonly<{
  children: React.ReactNode;
  // children은 React.ReactNode 타입이며 Readonly이므로 props는 읽기 전용임
  // 따라서 컴포넌트 내부에서는 children을 수정 불가
 
}>) {
  return (
    <html lang="en">
      <body>
        <QueryProvider>
          <MemberProvider>
            <BookMarkProvider>
              <Navbar /> {/* 전역 변수 사용하기 때문에 감싼 형태로 BookMarkProvider 사용 */}
              {children}
            </BookMarkProvider>
          </MemberProvider>
        </QueryProvider>
      </body>
    </html>
  );
}
  • ReactNode

문자열, 숫자, JSX element, 배열 등 대부분의 렌더링 가능한 요소를 포괄함

 

  • Readonly

읽는 전용의 props


3. app > {isbn} > page.tsx

// 중간에 useBookMarkContext 사용
const { registBookMark } = useBookMarkContext();

  const handleRegistBookMark = useCallback(() => {
    if (book) {
      registBookMark(book);
      alert("선택할 책을 즐겨찾기에 담아놨습니다.");
    }
  }, [book]);
  
  // 맨 하단에 button onClick 추가
  <button onClick={handleRegistBookMark}>북마크</button>
        <button onClick={handleRemove}>삭제</button>

4. component > book_mark > BookMarkItem.tsx

const BookMarkItem = ({ book }: BookProps) => {
  const { removeBookMark } = useBookMarkContext();
  const handleRemove = useCallback(() => {
    removeBookMark(book.isbn);
  }, [book]);

5. app > book_mark > page.tsx

const BookMarkList = () => {
  const { clearBookMark, books } = useBookMarkContext();
  const handlerClear = useCallback(() => {
    clearBookMark();
  }, []);
{books.length > 0 ? (
            books.map((book) => <BookMarkItem key={book.isbn} book={book}></BookMarkItem>)
          // react가 알아볼 수 있게 key 값 필수이며 프라이머리 키인 book도 넣어줌
          ) : (
            <tr>
              <td colSpan={6}>북마크 내용이 비었습니다.</td>
            </tr>
          )}

 

'FrontEnd > Next.js' 카테고리의 다른 글

[ Next.js ] Router  (1) 2025.04.21
[ Next.js ] Axios  (1) 2025.04.17