목록으로

코드리뷰피하는 스프린터

1개월 전

Next.js App Router에서 서버 컴포넌트와 클라이언트 컴포넌트를 같이 쓸 때 헷갈리는 부분이 있어요. 아래처럼 서버 컴포넌트에서 데이터를 fetch하고 클라이언트 컴포넌트에 props로 넘기고 있는데, 버튼 클릭 시 데이터를 다시 불러오려면 어떤 방식이 좋을까요?

router.refresh() vs 클라이언트에서 직접 fetch 두 가지 방법의 차이가 궁금합니다.


// components/PostList.jsx (클라이언트 컴포넌트)
'use client';  

import { useState } from 'react';

export default function PostList({ posts }) {
  const [items, setItems] = useState(posts);

  const handleRefresh = () => {
    // 여기서 데이터를 다시 불러오고 싶은데
    // fetch를 직접 호출하면 되는 건지,
    // router.refresh()를 써야 하는 건지 모르겠습니다
  };

  return (
    <div>
      <button onClick={handleRefresh}>새로고침</button>
      {items.map((post) => (
        <article key={post.id}>
          <h2>{post.title}</h2>
          <p>{post.content}</p>
        </article>
      ))}
    </div>
  );
}

[AI가 생성한 질문 예시입니다]

댓글 1

김병연
김병연1개월 전

두 방법 모두 가능하지만 용도가 다릅니다. 핵심 차이를 정리하면 이렇습니다.

router.refresh()는 현재 라우트의 서버 컴포넌트를 다시 실행해서 페이지 전체 데이터를 갱신합니다. 클라이언트 상태는 유지되면서 서버 데이터만 새로 받아오기 때문에, 서버 컴포넌트에서 이미 fetch 로직이 있다면 첫 번째 방식이 깔끔합니다.

이 경우 useState로 posts를 감쌀 필요가 없습니다. 서버 컴포넌트가 다시 실행되면서 새 posts props가 내려오기 때문입니다.

반면 클라이언트에서 직접 fetch하는 방식(두 번째 방식)은 부분 갱신이나 무한 스크롤처럼 클라이언트 상태를 직접 관리해야 할 때 적합합니다.


// 첫 번째 방식
'use client';

import { useRouter } from 'next/navigation';

export default function PostList({ posts }) {
  const router = useRouter();

  const handleRefresh = () => {
    router.refresh();
  };

  return (
    <div>
      <button onClick={handleRefresh}>새로고침</button>
      {posts.map((post) => (
        <article key={post.id}>
          <h2>{post.title}</h2>
          <p>{post.content}</p>
        </article>
      ))}
    </div>
  );
}

// 두 번째 방식
'use client';

import { useState } from 'react';

export default function PostList({ posts: initialPosts })
 {
  const [items, setItems] = useState(initialPosts);

  const handleLoadMore = async () => {
    const res = await fetch(`/api/posts?offset=${items.length}`);
    const newPosts = await res.json();
    setItems((prev) => [...prev, ...newPosts]);
  };

  return (
    <div>
      {items.map((post) => (
        <article key={post.id}>
          <h2>{post.title}</h2>
          <p>{post.content}</p>
        </article>
      ))}
      <button onClick={handleLoadMore}>더 보기</button>
    </div>
  );
}