Section 4. App Router 시작하기(2)

OlMinJe·2025년 10월 13일

Next.js

목록 보기
12/20
post-thumbnail

인프런 "한 입 크기로 잘라먹는 Next.js" 수강

1. 리액트 서버 컴포넌트 이해하기

1️⃣ 리액트 서버 컴포넌트란?

React Server Component(RSC)
서버에서만 실행되는 컴포넌트로, 브라우저에서는 전혀 실행되지 않는다.

서버 컴포넌트 이해하기

  • 즉, 서버 컴포넌트는 서버에서 HTML을 사전 렌더링할 때 딱 한 번만 실행되고, 브라우저에서는 이미 만들어진 HTML만 전달받는다.
  • 반면, 클라이언트 컴포넌트는 사전 렌더링 때 한 번, 하이드레이션 때 한 번, 총 두 번 실행된다.

2️⃣ 서버 vs 클라이언트

Next.js의 App Router에서는 기본적으로 모든 컴포넌트가 서버 컴포넌트(Server Component)이다.

즉, 기본값은 서버 컴포넌트!

클라이언트 컴포넌트를 사용하고 싶다면, 컴포넌트 파일의 최상단에 'use client'를 선언해주면 된다.

'use client';
import { useEffect } from 'react';
import styles from './page.module.css';

export default function Home() {
  useEffect(() => {}, []);
  return <div className={styles.page}>인덱스 페이지</div>;
}

3️⃣ 클라이언트 컴포넌트는 언제 써야 할까?

브라우저에서만 가능한 동작이 있다면 클라이언트 컴포넌트로 만든다.

  • React의 Hook(useState, useEffect) 같은 것들은 브라우저 환경에서만 동작하는 것
  • 또는 사용자의 입력, 클릭, 드래그 등과 같은 “상호작용(interaction)”이 필요한 부분

권장사항

  • 페이지의 대부분을 서버 컴포넌트로 구성할 것
  • 클라이언트 컴포넌트는 꼭 필요한 경우에만 사용할 것

구체적으로 구분하자면, “상호작용이 있어야 하면” 클라이언트 컴포넌트로 만들고 그렇지 않다면 서버 컴포넌트로 만들면 된다.


4️⃣ 검색창 만들기

검색창은 사용자가 검색어를 입력하면 state가 변하기 떄문에 대표적인 클라이언트 컴포넌트이다.

'use client';

import React, { useState } from 'react';

export default function Searchbar() {
  const [search, setSearch] = useState('');

  const onChangeSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
    setSearch(e.target.value);
  };

  return (
    <div>
      <input value={search} onChange={onChangeSearch} />
      <button>검색</button>
    </div>
  );
}

이렇게 'use client'를 선언하면 Next.js가 해당 파일을 브라우저용으로 번들링해서 클라이언트에서 동작하도록 만들어준다.


5️⃣ Co-Location

컴포넌트를 어디에 둬야 할까?

App Router에서는 page.tsx, layout.tsx처럼 특별한 예약 파일명을 제외하면 그 외의 파일은 일반적인 JS/TS 파일로 간주한다.
그래서 app 폴더 안에서도 자유롭게 컴포넌트를 만들어둘 수 있다.

이러한 패턴을 Co-Location이라고 부른다.

즉, 페이지와 관련된 컴포넌트를 같은 위치에 함께 배치할 수 있다는 뜻이다! (ex. app/(with-searchbar)/search/components/Searchbar.tsx 등)
이러한 구조 덕분에 관련된 코드끼리 관리할 수 있어서 코드 가독성유지보수성에 도움이 된다.


2. 리액트 서버 컴포넌트 주의 사항

1️⃣ 서버 컴포넌트에는 브라우저에서 실행될 코드가 포함되면 안된다.

서버 컴포넌트는 이름 그대로 서버에서만 실행된다.
그래서 아래와 같은 브라우저 전용 코드들은 포함될 수 없음!

  • 브라우저에서만 동작하는 React Hook(useState, useEffect 등)
  • 클릭, 입력 같은 이벤트 핸들러
  • 브라우저 전용 라이브러리 (예: DOM 조작 라이브러리)

이런 코드는 반드시 'use client'를 선언한 클라이언트 컴포넌트 안에서만 사용해야 한다.

서버 컴포넌트는 **"렌더링 결과만 반환하는 순수한 UI 정의 영역"


2️⃣ 클라이언트 컴포넌트는 클라이언트에서만 실행되지 않는다.

클라이언트 컴포넌트는 서버에서 한 번, 클라이언트에서 한 번, 총 두 번 실행된다.

실행 위치실행 이유
서버사전 렌더링(Pre-rendering)을 위해
클라이언트하이드레이션(Hydration)을 위해

즉, 서버와 브라우저 양쪽에서도 실행된다!


3️⃣ 클라이언트 컴포넌트에서 서버 컴포넌를 import 하면 안 된다.

클라이언트 컴포넌트는 앞서 말했듯 서버와 브라우저 모두에서 실행된다.
그런데 이 컴포넌트 안에서 서버 컴포넌트를 import 하게 되면 어떻게 될까?

❌ 브라우저에서도 서버 컴포넌트를 실행하려 하기 때문에 "런타입 에러"가 발생한다.

Next.js는 이런 경우를 해결하기 위해, 서버 컴포넌트를 자동으로 클라이언트 컴포넌트로 변환해버린다. 하지만 이건 일시적인 해결책으로 좋은 해결 방식은 아니다.

💡 권장 방식
클라이언트 컴포넌트에서 서버 컴포넌트를 직접 import 하지 말고,
propsReactNode 타입의 children을 받아 렌더링하는 방식을 사용한다.

이 방식을 사용하면 서버 컴포넌트가 클라이언트 컴포넌트 안에서도 안전하게 렌더링된다.


4️⃣ 서버 컴포넌트에서 클라이언트 컴포넌트에게 "직렬화 불가능한 Props"를 전달하면 안된다.

서버 컴포넌트는 렌더링 결과를 직렬화(serialize)해서 클라이언트로 전달한다.
즉, 데이터는 문자열(JSON 형태)로 변환되어야 클라이언트 컴포넌트로 전달된다.

그런데 만약 직렬화 불가능한 데이터를 props로 전달한다면?

  • 함수
  • 클래스 인스턴스
  • Symbol
  • 복잡한 클로저나 참조 타입

Error: A function cannot be passed to a Client Component from a Server Component.
즉, 서버에서 "브라우저가 이해할 수 없는 형태의 데이터"를 넘기려 하면 에러가 발생한다.

import ClientComponent from './ClientComponent';

export default function ServerComponent() {
  const handleClick = () => alert('Click!'); // ❌ 함수는 직렬화 불가
  return <ClientComponent onClick={handleClick} />;
}

함수는 브라우저로 보낼 수 없기 때문에 에러가 발생한다.

✅ 해결 방법
브라우저에서 실행되어야 하는 로직이라면, 클라이언트 컴포넌트 내부에서 직접 정의애햐 한다.

'use client';
export default function ClientComponent() {
  const handleClick = () => alert('클라이언트에서 실행!');
  return <button onClick={handleClick}>클릭</button>;
}

💡RSC Payload란?

React Server Component의 순수한 결과물로, "서버 컴포넌트를 직렬화한 데이터 덩어리"로 볼 수 있다.

RSC Payload에는 서버 컴포넌트의 모든 데이터가 포함된다.

  1. 서버 컴포넌트의 렌더링 결과
    • 서버에서 사전 렌더링된 HTML 정보
  2. 연결된 클라이언트 컴포넌트의 위치
    • 어디에 어떤 클라이언트 컴포넌트가 들어갈지에 대한 매핑 정보
  3. 클라이언트 컴포넌트로 전달할 Props
    • 상호작용을 위해 필요한 props 데이터

즉, 서버는 RSC Payload를 만들어 브라우저에게 보내고, 브라우저는 그골 벋어 하이드레이션을 진행하며, "서버 + 클라이언트가 연결된 완전한 페이지"를 구성하는 것이다.

profile
큐트걸

0개의 댓글