최신 트렌드: 서버 컴포넌트와 클라이언트 컴포넌트
Next.js의 핵심 특징인 서버 컴포넌트와 클라이언트 컴포넌트를 언제, 어떻게 나누어 써야 하는지 명확히 구분해 봅니다.
Next.js 최신 버전(App Router)으로 코딩을 시작하면 가장 먼저 부딪히게 되는 개념이 바로 서버 컴포넌트(Server Component)와 클라이언트 컴포넌트(Client Component)입니다. 이 두 가지를 명확히 구분하지 못하면 AI가 작성해준 코드에서 원인을 알 수 없는 에러가 빈번하게 발생합니다. 이번 시간에는 언제 어떤 컴포넌트를 사용해야 하는지 그 기준과 특징을 완벽하게 이해해 봅니다.
서버 컴포넌트: 기본이 되는 뼈대
Next.js 환경에서 여러분이 만드는 모든 컴포넌트는 기본적으로 서버 컴포넌트(Server Component) 로 간주됩니다. 말 그대로 사용자의 브라우저(클라이언트)가 아닌, 화면을 서빙하는 '서버' 쪽에서 렌더링되어 완성된 HTML 결과물만 브라우저로 전달되는 컴포넌트입니다.
서버 컴포넌트의 장점
서버 컴포넌트는 보안과 성능 측면에서 매우 강력한 장점을 가집니다. 브라우저로 다운로드되어야 하는 자바스크립트 코드의 양을 극적으로 줄여주기 때문에 화면 로딩 속도가 훨씬 빠릅니다.
무엇보다 데이터베이스나 서버의 중요한 비밀번호(환경 변수)에 직접 접근할 수 있습니다. 코드가 클라이언트의 브라우저로 넘어가지 않기 때문에, 악의적인 사용자가 코드를 뜯어보고 데이터베이스 구조나 비밀키를 훔쳐볼 위험이 원천적으로 차단됩니다.
언제 사용해야 하는가
화면에 보여주는 정보가 사용자의 조작에 따라 실시간으로 변하지 않는 '정적인 콘텐츠'라면 무조건 서버 컴포넌트를 사용하는 것이 유리합니다.
- 블로그의 게시글 본문 내용
- 사이트의 푸터(Footer) 정보
- 데이터베이스에서 직접 목록을 불러와서 나열만 하는 페이지
이러한 요소들은 한번 화면에 그려지고 나면 사용자가 클릭해서 내용이 바뀌거나 할 필요가 없으므로 서버 컴포넌트로 구성하는 것이 이상적입니다.
클라이언트 컴포넌트: 사용자와의 상호작용
서버 컴포넌트가 강력하지만 모든 것을 해결할 수는 없습니다. 서버에서 이미 완성된 뼈대를 보내주기 때문에, 그 이후에 사용자가 버튼을 클릭하거나 애니메이션을 작동시키는 등의 '상호작용(Interaction)'을 처리하려면 브라우저에서 실행될 코드가 필요합니다. 이때 등장하는 것이 바로 클라이언트 컴포넌트(Client Component) 입니다.
클라이언트 컴포넌트의 필요성
앞서 우리가 배운 `State(상태)`나 클릭 같은 이벤트 처리는 브라우저 환경에서만 동작합니다. 서버에서는 사용자가 마우스를 움직이거나 버튼을 누르는 것을 감지할 수 없기 때문입니다. 따라서 사용자와 대화하며 화면의 내용을 동적으로 바꿔야 하는 부분은 클라이언트 컴포넌트로 만들어야 합니다.
서버 컴포넌트와 클라이언트 컴포넌트의 역할 비교
| 비교 항목 | 서버 컴포넌트 (기본값) | 클라이언트 컴포넌트 |
|---|---|---|
| 실행 위치 | 서버 (결과물만 브라우저 전송) | 브라우저 (클라이언트) |
| 상호작용 | 클릭, 입력 등의 이벤트 처리 불가 | onClick, onChange 등 이벤트 처리 가능 |
| 상태 관리 | State 및 React 훅 사용 불가 | State, Effect 등 모든 훅 사용 가능 |
| 보안 | DB 접근 가능, 보안 키 숨김 가능 | 브라우저 노출, 보안 키 사용 주의 |
'use client' 지시어
Next.js에게 "이 컴포넌트는 서버가 아니라 사용자의 브라우저에서 실행해 주세요!"라고 알려주기 위해서는 파일의 가장 첫 줄에 특별한 주문을 적어야 합니다. 바로 `"use client"` 라는 지시어입니다.
이 지시어가 선언된 파일과 그 안에서 불러오는 모든 자식 컴포넌트들은 클라이언트 컴포넌트 묶음으로 취급됩니다. 만약 AI가 만들어준 코드에서 `useState`나 `onClick`을 썼는데 에러가 발생한다면, 가장 먼저 파일 맨 위에 `"use client"`가 빠져있지 않은지 확인해 보아야 합니다.
두 컴포넌트를 함께 조화롭게 사용하기
현대적인 Next.js 애플리케이션은 서버 컴포넌트의 성능 이점과 클라이언트 컴포넌트의 상호작용을 적절히 섞어서 만듭니다.
컴포넌트 트리와 경계
기본적인 전략은 최대한 많은 부분을 서버 컴포넌트로 유지하고, 상호작용이 필요한 아주 작은 요소들만 클라이언트 컴포넌트로 떼어내어 부착하는 것입니다. 이를 통해 성능과 상호작용이라는 두 마리 토끼를 모두 잡을 수 있습니다.
서버 안에 클라이언트 컴포넌트 껴넣기
예를 들어, 쇼핑몰의 상품 상세 페이지를 상상해 보겠습니다.
상품의 사진, 제목, 긴 설명글, 가격 등은 모두 서버 컴포넌트로 만들어 빠르게 화면에 그립니다. 그리고 오직 수량을 늘리고 줄이는 버튼 컴포넌트에만 `"use client"`를 붙여 별도의 파일로 분리한 뒤, 서버 컴포넌트 화면 안에 쏙 집어넣어 조립합니다.
이렇게 하면 전체 페이지가 가벼워지면서도 사용자가 조작해야 하는 버튼은 완벽하게 작동하게 됩니다.
주의할 점은, 한 번 클라이언트 컴포넌트의 영역이 시작되면 그 자식 컴포넌트들은 원칙적으로 모두 클라이언트 컴포넌트가 된다는 것입니다. 따라서 컴포넌트의 최상단에서 무턱대고 `"use client"`를 선언하면 서버 컴포넌트의 장점을 잃게 되므로 꼭 필요한 부분(잎사귀 부분)으로 내려가서 적용하는 것이 좋습니다.
개념 퀴즈
퀴즈 답을 맞춰야 학습 완료가 됩니다.사용자가 버튼을 클릭하면 숫자가 증가하는 '좋아요' 기능을 구현하려고 합니다. 이 컴포넌트의 최상단에 반드시 추가해야 하는 코드는 무엇인가요?
3줄 요약
- 1Next.js의 모든 컴포넌트는 기본적으로 서버 컴포넌트이며, 성능과 보안에 유리합니다.
- 2이벤트 처리나 상태 관리가 필요한 부분은 'use client'를 선언하여 클라이언트 컴포넌트로 만듭니다.
- 3페이지의 큰 뼈대는 서버 컴포넌트로 유지하고 상호작용 요소만 클라이언트 컴포넌트로 분리하는 것이 핵심입니다.