React 18 Suspense와 Server Component 실험

배경

3월에 React 18이 정식 릴리즈됐다. Concurrent Rendering이 핵심이지만, 당장 눈에 띄는 건 Suspense for Data Fetching과 Server Components였다. 사내 어드민 프로젝트에 적용 가능성을 검토하기 위해 간단한 POC를 진행했다.

Suspense for Data Fetching

기존에는 컴포넌트 내부에서 loading 상태를 관리했다.

function UserList() {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetchUsers().then(data => {
      setUsers(data);
      setLoading(false);
    });
  }, []);

  if (loading) return <Spinner />;
  return <ul>{users.map(u => <li>{u.name}</li>)}</ul>;
}

Suspense를 사용하면 로딩 상태를 상위로 위임할 수 있다.

function UserList() {
  const users = use(fetchUsers()); // Suspense 통합
  return <ul>{users.map(u => <li>{u.name}</li>)}</ul>;
}

// 사용
<Suspense fallback={<Spinner />}>
  <UserList />
</Suspense>

여러 컴포넌트의 로딩 상태를 조율하기 편해졌다. 특히 중첩된 데이터 페칭에서 waterfall 문제를 해결하는 데 유용했다.

Server Components

Next.js 12에서 알파로 제공 중이다. 서버에서만 실행되는 컴포넌트로, 번들 사이즈를 줄이고 직접 DB 접근이 가능하다.

// UserList.server.jsx
import db from 'db';

export default function UserList() {
  const users = db.query('SELECT * FROM users');
  return <ul>{users.map(u => <li>{u.name}</li>)}</ul>;
}

아직 안정화되지 않아 프로덕션 적용은 보류했지만, GraphQL 없이도 백엔드 로직을 컴포넌트에 통합할 수 있다는 점이 흥미로웠다.

결론

React 팀이 제시하는 방향은 명확하다. 데이터 페칭 로직을 선언적으로 작성하고, 서버와 클라이언트의 경계를 프레임워크가 관리하게 하는 것. 당장은 실험적이지만, 1~2년 내 표준이 될 것 같다.