☁️ goormTIL | Next.js #59

매루·2025년 12월 2일

goormTIL

목록 보기
57/67
post-thumbnail

📅 2025-12-02

➡️ Next.js 서버/클라이언트 컴포넌트, 페이지 전환 방식과 렌더링 전략(CSR/SSR/SSG/ISR)에 대해 새롭게 알게 된 것 또는 헷갈리는 부분 정리 + 🤔❓


🔎 학습 리마인드

📌 페이지 이동

  • Next.js는 기본 <a> 태그 대신 <Link> 컴포넌트 사용

특징

  • 클라이언트 사이드 라우팅 지원

    • 페이지를 이동할 때 브라우저 전체를 다시 로드하지 않음
    • 클라이언트가 필요한 데이터만 받아서 빠르게 화면만 교체하는 방식
    • Client-Side Navigation이라고 부르며 SPA처럼 부드러운 전환과 빠른 UX 제공
  • prefetching 지원

    • <Link>뷰포트에 보이는 순간 다음 페이지의 코드/데이터 미리 다운로드
    • 클릭하면 거의 즉시 이동 → 매우 빠른 UX
    • 뷰포트(viewport)란?
      • 현재 사용자가 보고 있는 화면 영역

요소설명
<a>전통적 페이지 이동
페이지 전체 새로고침 발생
느림
<Link>SPA처럼 동작하여 빠름
클릭 즉시 페이지 이동 (prefetch)
SEO에 유리 (내부적으로 a 태그 렌더링)

💡 useRouter

  • 항상 파일 최상단에 "use client" 선언 필요
  • 페이지 이동을 이벤트 핸들러 내부에서 제어할 때 사용
  • 클릭 후 로직의 순서에 따라 실행해서 즉시 이동하지 않음
"use client";

import { useRouter } from "next/navigation";

export default function Test () {
	const router = useRouter();
	
	const handleButtonClick = () => {
		...
		// 로직 실행 후 이동
		router.push("/new_location");
	}

	return <button onClick={handleButtonClick}>클릭!</button>
}

라우터 메서드

메서드설명사용 예
router.push("/page")새 URL을 히스토리 스택에 추가일반 페이지 이동
router.replace("/page")현재 페이지를 새 페이지로 덮어씀로그인 후 뒤로가기 방지
router.back()브라우저 뒤로 가기와 동일이전 화면으로 이동
router.reload()새로고침최신 데이터 재요청

📌 서버 컴포넌트와 클라이언트 컴포넌트

🔗 https://nextjs.org/docs/app/getting-started/server-and-client-components

구분서버 컴포넌트 (기본)클라이언트 컴포넌트
실행 위치서버(Node.js 환경)브라우저
선언 방식기본"use client" 필요
브라우저 API❌ 사용 불가⭕ 사용 가능
React Hooks (useState, useEffect 등)
Node API (fs, os 등)
상호작용(이벤트)
데이터 패칭, SEO, 정적 UI⭕ (하지만 서버가 더 효율적)
  • app/ 폴더 아래의 모든 컴포넌트는 기본적으로 서버 컴포넌트
  • "use client" 선언이 있는 파일만 클라이언트 컴포넌트

💡 서버 컴포넌트 (Server Component)

특징

  • 브라우저가 아니라 서버(Node.js 런타임 환경)에서 실행
  • 콘솔 출력 → 브라우저가 아닌 터미널에 표시
  • 브라우저 API 사용 불가 (예: alert, confirm, window, document)
    // src/app/page.tsx
    export default function Home() {
      alert("hello"); // 에러
    
      return (
        <div>
          <h1>Test Page </h1>
          <p>Test Next</p>
        </div>
      );
    }
  • React Hooks (useState, useEffect, useRef) 사용 불가
  • Node API 사용 가능 (fs, os, path 등)
    const page = () => {
      const os = require("os");
      console.log(os);
      return (
        <div>
          <h1>Test Page </h1>
          <p>Test Next</p>
        </div>
      );
    };
    
    export default page;

예시

  • console.log()가 브라우저 개발자 도구가 아닌 터미널 로그에 찍힘

💡 클라이언트 컴포넌트 (Client Component)

특징

  • 브라우저에서 실행
  • "use client" 선언 필요 (파일 맨 위에 위치)
  • 상태 관리 및 상호작용 가능
    • useState, useEffect, onClick, 모달, alert, 입력 폼 등 모든 UI 인터랙션 처리

예시

"use client";

export default function Component() {
  return <button onClick={() => alert("Hello!")}>Click</button>;
}

💡 언제 어떤 컴포넌트 사용?

  • 서버 컴포넌트 (기본)

    • 데이터 패칭, SEO 텍스트, 정적 콘텐츠
    • 목록 렌더링, DB/API 접근, 레이아웃 구성 등
  • 클라이언트 컴포넌트 (필요할 때만)

    • 버튼 클릭, 폼 입력, 애니메이션, 로컬 상태 관리
    • useState / useEffect / 브라우저 이벤트 / alert 등 필요할 때

💡 권장 패턴: 상호작용만 분리

  • 페이지 전체는 서버 컴포넌트로 유지
  • 브라우저 상호작용이 필요한 최소한의 컴포넌트만 클라이언트 컴포넌트로 분리
  • 이유: 성능 최적화 + 번들 크기 감소

예시

  • page.tsx (Server Component)
    // src/app/page.tsx
    export default function Home() {
      return (
        <div className="p-8">
         안녕하세요 구름 프론트엔드 넥스트 강의 입니다.
          <section>
            <h1>제목</h1>
            <p>내용</p>
            <ul>
              <li>항목1</li>
              <li>항목2</li>
              <li>항목3</li>
            </ul>
          </section>
         <Button />
        </div>
      );
    }
  • Button.tsx (Client Component)
    // src/components/Button.tsx
    "use client";
    
    const Button = () => {
      return (
        <button
          onClick={() => {
            alert("안녕하세요!");
          }}
        >
          클릭
        </button>
      );
    };
    
    export default Button;

📌 MPA와 SPA

💡 MPA (Multi Page Application)

특징

  • URL마다 별도의 HTML 문서 존재
  • 페이지 이동 시 문서 전체를 서버에서 다시 요청
  • 페이지 이동 시 화면 깜빡임 발생, 로딩 속도 편차 큼

장점

  • 서버에서 완성된 HTML 제공 → SEO에 유리
  • 구조가 단순함

단점

  • 페이지 이동 시 전체 새로고침 → UX 저하
  • 페이지별 로딩 시간 편차 → 사용자 체감 느림

💡 SPA (Single Page Application)

특징

  • HTML 1개(index.html) + JS로 화면 교체
  • 브라우저에서 필요한 부분만 업데이트 → 페이지 전체 새로고침 없음

동작 흐름

  1. 서버에서 index.html + JS 번들 다운로드
  2. 브라우저에서 JS 번들 실행 → #root 같은 DOM에 전체 UI 렌더링
  3. 이후 라우팅 시 JS가 필요한 부분만 교체, URL은 바뀌지만 페이지 전체 요청 없음

장점

  • 한 번 로딩 후 페이지 전환 매우 빠름
  • 앱처럼 부드러운 UX 제공

단점

  • 초기 로딩이 무거움 → 첫 화면 느릴 수 있음
  • JS 실행 전 HTML이 비어 있음 → SEO 불리

📌 4가지 렌더링 전략

  • Next.js에서는 페이지를 렌더링하는 방식에 따라 CSR, SSR, SSG, ISR 4가지 전략을 사용할 수 있음

💡 CSR(Client Side Rendering)

  • 브라우저에서 렌더링하는 순수 SPA 스타일
  • 렌더링 주체: 브라우저(클라이언트)
  • 동작: 서버는 정적 파일(JS, CSS, HTML)만 제공 → 클라이언트에서 React가 화면 렌더링

장점

  • 초기 렌더링만 되면 이후 모든 화면 전환/상호작용이 매끄럽고 빠름
  • 서버 입장에서 부담이 적음

단점

  • TTV(Time To View)가 늦을 수 있음
  • JS로딩 / 실행되기 전까지 빈 화면
  • SEO 불리

💡 SSG(Static Site Generation)

  • 빌드할 때 HTML을 미리 만들어 두고, 요청 오면 바로 전달
  • 렌더링 주체: 빌드 시점
  • 동작: HTML을 미리 생성 → 요청 시 바로 제공

장점

  • TTV(Time To View) 매우 빠름
  • SEO에 유리
  • 정적 파일이라 CDN 배포 용이

단점

  • 데이터가 자주 바뀌면 재빌드 필요
  • 유저별 동적 화면 구현 어려움

💡 ISR(Incremental Static Regeneration)

  • 일정 주기마다 최신 데이터로 자동 갱신
  • 렌더링 주체: SSG 기반 + 백그라운드 갱신
  • 동작: 초기 빌드 시 HTML 생성 → 설정한 주기마다 백그라운드에서 재생성

장점

  • 정적 페이지처럼 빠름
  • 어느 정도 최신 데이터 유지 가능
  • CDN 캐싱 활용 가능

단점

  • 실시간 데이터에는 한계
  • 갱신 시점이 사용자에게 명확하지 않음

💡 SSR(Server Side Rendering)

  • 요청이 들어올 때마다 서버가 그 자리에서 렌더링
  • 렌더링 주체: 서버
  • 동작: 요청 시마다 서버가 React 렌더링 → HTML 생성 → 응답

장점

  • 항상 최신 데이터 기반으로 페이지 생성
  • SEO에 유리
  • 로그인, 마이페이지 등 동적 화면에 적합

단점

  • 요청이 많아질수록 서버 부하 증가
  • SSG / ISR에 비해 느릴 수 있음
  • CDN 캐싱 활용이 상대적으로 어려움

💡 정리

렌더링 전략빌드 시간SEO페이지 요청 응답 속도최신 정보
CSR짧음나쁨보통맞음
SSR짧음좋음느림맞음
SSG좋음빠름아님
ISR좋음빠름아닐 수 있음

🌊 Deep Dive

🤔 Next.js 15.1 비동기(Promise) 기반으로 변경된 API

  • Next.js 15.1부터 params를 포함한 여러 요청 관련 객체가 Promise 형태로 동작하도록 변경됨

  • Promise로 변경된 항목

    • cookies
    • headers
    • draftMode
    • params in layout.jspage.jsroute.jsdefault.jsgenerateMetadata, and generateViewport
    • searchParams in page.js

Next.js 15.1 이전

export default function Page({ params: { id } }: { params: { id: string } }) {
  // ...
}
  • 동기 객체로 바로 접근 가능

Next.js 15.1 이후

type PageParams = Promise<{ id: string }>;

export default async function Page({ params }: { params: PageParams }) {
  const { id } = await params;
}
  • paramsPromise 객체로 전달 → await 필요

🔗 https://nextjs.org/blog/next-15#async-request-apis-breaking-change

0개의 댓글