
Next.js 15버전을 기준으로 작성
아래의 포스팅에서 이어짐
웹 개발의 트렌드와 기술 스택 선택 / Next.js
https://velog.io/@juwon98/web-development-trends-and-nextjs
Next.js는 Vercel에서 개발한 React 기반 프레임워크이며, 아래와 같은 필요에 따라 Next.js가 출시되었다.
-> 이에 따라 Vercel이 React 생태계 위에 SSR 중심의 웹 프레임워크로 Next.js를 출시.
Next.js를 사용하는 이유가 SEO와 SSR, SSG 사용을 통한 퍼포먼스 향상이기 때문에 SEO, SSG 그리고 SSR과 CSR의 차이에 대해 이해할 필요가 있다.
SEO란 "검색 엔진 최적화", 즉, 서비스를 구글, 네이버 등의 포털 사이트의 검색 엔진에 노출하고, 사이트(또는 앱)의 구조에 따라 정보를 알맞게 제공하는 것을 말한다.
아래의 이미지처럼 검색했을 때 앱의 구조를 보여줄 수 있고,
카카오톡 같은 SNS 공유 시에도 아래의 이미지처럼 페이지에 따라 보여지는 이미지와 설명 등의 정보를 알기 쉽게 제공할 수 있다.
(무신사의 "세일" 페이지 주소를 보낸 결과)
React는 기본적으로 SPA(Single Page Application)를 만드는 데에 최적화된 라이브러리이다.
그런데, SPA는 모든 페이지가 비어있는 하나의 HTML에서 JavaScript를 실행해 컨텐츠를 생성 및 변경하는 방식이기 때문에 문제가 생긴다.

검색엔진의 웹 크롤러(Web Crawler)가 웹사이트의 정보를 수집할 때, 서버에 HTML 파일을 요청해서 이 비어있는 HTML을 수집하게 되므로, 아무런 컨텐츠를 노출하지 않고, metadata 또한 한 페이지에 대해서만 제공할 수 밖에 없다.
이렇게 클라이언트(브라우저)에서 컨텐츠를 생성하는 방식을 CSR(Client Side Rendering)이라고 한다.
브라우저(클라이언트)에서는 HTML과 CSS, JavaScript 파일을 모두 요청하고, 그 JavaScript 파일을 실행해 비어있는 HTML에 그려주는 "브라우저 렌더링(rendering + painting)" 과정을 거치기 때문에 모든 컨텐츠를 볼 수 있다.
아래에서 설명할 SSR과는 조금 다른 개념
SSG: 사용자가 웹사이트를 방문하기 전에(빌드 시점에) 모든 페이지를 미리 HTML 파일로 생성해 서버나 CDN에 배포하는 방식
쉽게 말하면, React를 사용한 SPA에서 각 페이지에 접속할 때의 (JavaScript 파일을 실행해서 HTML에 DOM 요소를 만드는) 작업을 빌드 과정에 미리 실행해서, 각 페이지에 해당하는 HTML 파일을 서버(또는 CDN)에 배포해두는 것이다.
React 앱에서 사전작업이라고 할 수 있는 이 SSG를 거치게 되면, 브라우저는 이미 완성된 HTML 파일을 받아서 "최초 렌더링(inital rendering)"을 생략한다.
그 다음에 JavaScript 파일을 실행해서 페이지에 동적인 움직임과 사용자 상호작용을 처리할 EventLister 등을 추가하는 "Hydration"과정만을 수행한다.
서버 요청에 대한 HTML 파일 응답을 받는 즉시 화면을 그릴 수 있기 때문에 초기 로딩속도가 빠름.
웹 크롤러가 사이트 정보를 수집할 때, 미리 만들어둔 HTML을 제공하므로 SEO가 가능함.
정적 라우팅(/product/:id처럼 유동적인 경로가 없는 경우)만 필요한 경우, SSR보다 좋은 선택지가 될 수 있음. (서버 부하가 없거나 서버 없이도 정적 파일 서빙만 가능)
동적 라우팅이 필요한 상황에는 한계가 있음.
빌드 시점에 모든 페이지를 미리 만들어두기 때문에, 빌드 시간이 길어짐.
React같은 SAP 라이브러리나 프레임워크를 사용하지 않고 HTML, JavaScript(+ jQuery 등)로 페이지를 만드는 경우도 SSG 방식이라고 볼 수 있을 것 같다.
SSR: 사용자의 요청이 들어올 때마다, 서버에서 React 앱을 렌더링하여 HTML을 실시간으로 생성해서 반환하는 방식
사용자 요청 → 서버에서 HTML 페이지 생성 후 반환 → 브라우저가 hydration 진행
Next.js는 이러한 SSR의 단점을 보완하기 위해 페이지의 일부분만 천천히 보내고(layout 등) 그 때 비어있는 부분을 Loading 컴포넌트로 채우는 등의 장치가 마련되어 있음.
Next.js는 SEO, 퍼포먼스 향상을 위해 이 세 가지 방식을 혼합하여 만든 React 기반의 프레임워크이다.
거기다 추가로 API Routes를 통한 백엔드 기능도 어느정도 구현이 가능하고, 초기 세팅 간소화, 파일 기반 라우팅, 이미지 최적화 등의 기능까지 제공하고 있다.
SSG 방식을 사용하고, 여러 부가적인 일들을 처리하는 만큼 빌드 시간이 엄청나게 길어짐.
SSR을 위한 동적 서버가 필요함. (Vercel의 Edge 환경을 통해 어느정도 해결 가능)
프레임워크이기 때문에 개발 환경에 대한 커스터마이징이 제한됨.
SSR, SSG 등의 개념도 익혀야 하고 Next.js에서 제공하는 기능들이 많기 때문에 러닝커브가 심함.
+ 개인적인 생각
제공하는 기능에 대해 정확이 알고 선택하는 것이 필요함.
(실제로 Next.js에서 이미지 최적화를 위해 제공하는<Image>태그를 사용할 때 메모리 누수가 있다는 등의 글이 있음)
하지만, 간편하고 좋은 기능들을 많이 제공하기 때문에 상황에 따라 잘 판단하고 선택하면 좋은 프레임워크다!
특히, 백엔드 기능을 제공하는 만큼 혼자서 풀스택 개발을 할 때에도 유용하다고 생각한다.
Next.js공부 및 테스트해볼 때 만든 연습용 저장소 (14, 15ver)
https://github.com/joowon-jang/learn-nextjs14/commits/main/노마드코더의 강의를 들으면서 작성했고, 15버전 사용에 따라 일부 변경하였음.
각 commit마다 프로젝트 init부터 주요 기능들 사용에 대한 설명을 적어두었음.
Next.js는 파일 기반 라우팅을 제공한다.
app(또는 pages)폴더 안에 페이지 경로에 해당하는 폴더를 만들고 page.tsx 파일을 만들면, 그 page.tsx가 해당 경로로 접속할 때 렌더링되는 방식이다.
예를들어, tododong.com이라는 도메인을 사용하는 프로젝트에서 /app/login/page.tsx 경로로 페이지를 만들면, tododong.com/login 경로로 접속할 때 렌더링된다.
Project structure and organization - Next.js
https://nextjs.org/docs/app/getting-started/project-structure
React Router를 사용해 본 사람이라면 공감할텐데, 여러 페이지에서 공통으로 사용되는 Layout이 있다면, <Layout> 컴포넌트를 만들고 어떤 경로에서 보여질지 라우팅을 직접 설정해야 한다.
하지만, Next.js는 layout.tsx라는 파일을 만들면, 그 layout.tsx가 위치한 경로의 하위 경로에는 모두 나타나게 되어 귀찮은 설정이 필요없다.
(하위 경로에서 Layout이 보여지지 않도록 하려면 예외처리 필요)
How to create layouts and pages - Next.js
https://nextjs.org/docs/app/getting-started/layouts-and-pages
데이터 fetching하는 중에 나머지 부분을 먼저 화면에 보여주고, 서버 요청의 응답이 도착하면 비어있는 부분을 채우게 된다.
그 비어있는 부분에 데이터를 불러오는 중임을 알리기 위한 Loading 컴포넌트를 loading.tsx라는 이름으로 만들어두기만 하면 자동으로 적용이 된다.
// 예시
// /app/games/loading.tsx
// 이런 식으로 적용하고 싶은 경로에 만듬
export default function Loading() {
return <p>경기 정보를 불러오는 중입니다...</p>;
}
URL 경로에 해당하는 페이지를 찾지 못한 경우에 표시해줄 404페이지도 not-found.tsx라는 파일로 만들어두기만 하면 자동으로 표시해준다!
// app/not-found.tsx
export default function NotFound() {
return <h2>페이지를 찾을 수 없습니다 😢</h2>;
}
Loading UI and Streaming - Next.js
https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming
not-found.js - Next.js
https://nextjs.org/docs/app/api-reference/file-conventions/not-found
혼자서 백엔드 api까지 만들 수 있는 정말 좋은 기능!
아래처럼 api 요청을 핸들링할 route.ts파일을 만들어두고,
// app/api/products/route.ts
import { NextRequest, NextResponse } from 'next/server';
export async function GET() {
const products = await fetchProducts();
return NextResponse.json(products);
}
export async function POST(req: NextRequest) {
const body = await req.json();
const created = await createProduct(body);
return NextResponse.json(created, { status: 201 });
}
아래처럼 해당 경로로 fetch 요청을 하면 응답을 받을 수 있다.
// app/products/page.tsx
'use client';
import { useEffect, useState } from 'react';
export default function ProductListPage() {
const [products, setProducts] = useState([]);
useEffect(() => {
fetch('/api/products')
.then(res => res.json())
.then(data => setProducts(data));
}, []);
return (
<div>
<h1>상품 목록</h1>
<ul>
{products.map((p: any, idx: number) => (
<li key={idx}>{p.name} - {p.price}원</li>
))}
</ul>
</div>
);
}
API Routes - Next.js
https://nextjs.org/docs/app/guides/migrating/app-router-migration#api-routes
너무 많은 기능이 있기 때문에 필요할 때마다 Next.js 공식문서를 보면서 적용하면 좋을 것 같다.