그동안 React로 CSR 방식의 프로젝트만 구현했었는데, SSR 방식을 알아보던 중 React 문법을 사용할 수 있는 Next.js를 직접 사용해 보면서 어떤 차이점이 있는지 알아보고 싶어 간단한 애플리케이션을 구현해보았다.
1. 애플리케이션 생성
https://nextjs.org/docs/getting-started/installation
기존에 React로 생성할 때 CRA(create-react-app) 혹은 Vite를 사용했었는데 Next.js를 사용할 것이므로 CNA(create-next-app)로 애플리케이션을 생성해 주었다.
React는 index.js안에서 호출된 App.js 컴포넌트가 index 페이지를 담당하고 Next의 경우 app 폴더 안의 page와 layout 컴포넌트가 담당한다.
2. layout
일반적으로 현재 주 컨텐츠 컴포넌트 외에 Header, Footer는 매번 import 하지 않고 layout 설정을 하게 되는데, React의 경우 파일을 새로 만들고 추가해 주어야 하지만 Next는 기본적으로 layout 파일이 생성된다.
// React layout
const OrderLayout = () => {
return (
<>
<div id='orderLayout'>
{/* Header, Footer 고정 */}
<OrderHeader />
<Outlet />
<OrderFooter />
</div>
</>
);
};
// Next layout
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body className={inter.className}>{children}</body>
</html>
);
}
3. Route
Next를 처음 사용했을 때 느꼈던 가장 큰 차이점인 Route는 기존 React 방식보다 더 간편했다.
path로 url 경로를 지정해주고 해당 컴포넌트를 import 하는 방식과 다르게 Next는 app 폴더 안에 Route 하고 싶은 폴더를 생성하면 폴더명이 그대로 경로가 되고 컴포넌트(page) 호출이 된다.
// React
<Routes>
<Route path='/' element={<Home />} />
<Route path='/info' element={<Info />} />
<Route path='/terms' element={<Terms />} />
<Route path='/privacy' element={<Privacy />} />
<Route path='/electronicList' element={<ElectronicList />} />
<Route path='/noticeList' element={<NoticeLayout />}>
<Route index element={<Noticelist/>}/>
<Route path=':id' element={<NoticeDetail />} />
</Route>
</Routes>
about, blog, decs, pricing이 각각 Route 되며 폴더 안에 있는 page.tsx, layout.tsx가 렌더링 된다.
이러한 직관적인 방식이 갖는 여러 장점이 있다.
1. 파일 구조와 경로가 일치해 쉽게 관리할 수 있다.
2. 별도의 Routing 설정이 필요하지 않다.
3. 필요한 컴포넌트만 로드되기 때문에 code splitting 되어 초기 로딩 시간을 단축할 수 있다.
4. 각 페이지가 폴더 구조에 맞게 URL을 가지므로 SEO 최적화가 가능하다.
4. "use client"
Next는 기본적으로 SSR 방식이기 때문에 client 측에서 처리해야 할 것이 있는 컴포넌트에 "use client" 를 추가해 주어야 한다.
https://react.dev/reference/react/use-client
이를 이해하기 위해선 server component와 client componet의 차이를 알 필요가 있었다.
server에서 렌더링되는 server component는 client에서 받는 HTML에 하드코딩되어있는 반면 client componet는 client에서 Javascript 파일이 렌더링 된다는 차이점이 있다.
위 사진처럼 컴포넌트를 생성할 때 해당 컴포넌트가 어떤 기능을 하는지에 따라 server componet와 client component로 나누어진다.
이번에 간단한 Todo 애플리케이션을 구현해보았기에 다양한 경우를 생각해 보진 않았지만, 서버에 보낼 필요 없이 컴포넌트에서 데이터를 관리하기 위해 useState가 필요해 사용해 보았다.