src>extensions>React>useclient.mdx

서버 컴포넌트 vs 클라이언트 컴포넌트: 'use client’

v1.0.0
@young_log|React|Published on 2025-12-31

""거기서부터는 네가 해" - 'use client'가 서버에게 건네는 바톤 넘기기?"

Details
Changelog
Dependencies

“use client”

“use client”는 Next.js 환경에서 브라우저가 담당하는 영역임을 알려주는 역할을 한다.

서버와 클라이언트 사이의 검문소랄까?

Next.js 환경이 어떤 환경??

리액트라는 엔진을 장착한, 풀옵션 스마트 자동차!

단순히 UI만 그리는 리액트와 달리, Next.js는 서버와 브라우저가 아주 긴밀하게 협동하며 작동한다.

  1. 서버와 브라우저가 같이 일하는 환경

    일반적인 리액트는 브라우저가 모든 일을 다 하지만(CSR), Next.js는 서버가 미리 요리를 해서 브라우저로 보냄.

    • 서버: 데이터를 미리 가져오고 HTML 뼈대를 미리 그려서 보내줌(그럼 빠르겠죠?)
    • 브라우저: 서버가 보내준 뼈대에 자바스크립트를 입혀서 ‘클릭’, ‘입력’ 가능하게 한다(이것이 하이드레이션!)
  2. App Router 라는 새로운 규칙

    지금 이 블로그에서도 사용하고 있는 방식인데요!

    • 폴더 구조가 곧 주소(URL) ex) app/blog/page.tsx -> y0ung.dev/blog
    • 기본적으로 모든 컴포넌트는 '서버 컴포넌트'로 설정되어 있어서, 아까 배운 'use client' 같은 지시어가 필요하게 된 환경인 것
  3. 성능 업된 기능들이 기본 장착

    Next.js 환경에서는 개발자가 일일이 신경 써야 했던 것들을 알아서 해준다.

    • 이미지 최적화: 큰 사진도 용량을 확 줄여서 빠르게 보여준다.
    • SEO(검색 엔진 최적화): 네이버나 구글 로봇이 나의 블로그 내용을 아주 잘 읽어갈 수 있는 환경을 만들어준다. (그래서 도메인을 연결했을 때 더 가치가 있죠!)
    • 빠른 배포: Vercel 같은 도구와 찰떡궁합이라, git push 한 번에 배포된다.

하이드레이션(Hydration)

React+Next.js 조합(이 블로그 현 조합입니당?)으로 개발을 하다 보면 하이드레이션 에러 정말 많이 본다!
(내가 사용을 잘못해서 그럴수도 있구여..ㅎㅎ)

Hydration Error = hydration mismatch

Next.js나 React SSR 환경에서, 클라이언트가 서버에서 렌더(render)된 HTML을 재사용(hydrate)할 때 서버와 클라이언트의 렌더링 결과가 다를 경우 발생하는 오류를 말한다.

위의 Next.js 환경에서 봤듯이 Next.js는 페이지의 HTML를 서버에서 미리 렌더링을 하기 때문에
SSR과 CSR 사이에서 값이 달라지는 에러가 났다는 것임.

근데 이 포스트에서는 이게 중요한게 아니기 때문에 너무 길어질 것 같아서 이건 따로 소개를 해보겠음.!(Comming soon :))

여하튼 이 하이드레이션은 리액트 상의 모든 컴포넌트에서 발생하는 건 아니고
”use client”라고 상단에 명시된 컴포넌트에만 발생한다.

그러니까 “use client” 명시된 컴포넌트는 모두 Client Component

그 외 모두 기본적으로 Server Component

이렇게 구분을 해놓으면 자바스크립트가 다운받아야 하는 코드가 줄어드니 페이지 로드 속도가 빨라지겠죠?

Client Component

: 즉 “use client” 가 명시된 컴포넌트! ⇒ 렌더링되고 하이드레이트 된다.

  • “use client”를 정의하면 하위 컴포넌트를 포함하여 해당 파일로 가져온 모든 모듈이 클라이언트 번들의 일부로 간주한다.
  • client compnent는 server component에 포함 가능

Server Component

: “use client” 사용 안한 컴포넌트 ⇒ 렌더링되고 하이드레이트되지 않는다.

  • server compnent는 client component에 포함 불가

이게 무슨 수수께끼 같은 소리인가 싶은데…. 집합관계라고 생각하기로 했다.

그러니까 서버 컴포넌트는 이미 완성품!

서버에서 이미 HTML 뼈대를 이미 보냈고, 브라우저에서 따로 조립(하이드레이트)하지 않아도 되죠!(브라우저는 왕 편함, 그리고 검색 엔진 최적화에도 도움이 된다.)

그런데 클라이언트 컴포넌트는 밀키트 느낌!

“use client” 다? 브라우저에서 JS엔진 돌아가야 함.
브라우저는 이 코드를 받고 “아 오키, 이거 누르면 이렇게 동작하게 할께!”

왜 "서버 안에 클라이언트"는 되는데, 그 반대는 안 될까?

서버 컴포넌트에서 'use client’ 이걸 붙이는 순간 “키트화”되어지는 것.

근데 반대로 클라이언트 컴포넌트는 이미 브라우저 내에서 돌아가고 있는데.. 갑자기 서버로 돌아가게 한다? → 이건 말이 안됨. 이미 서버와 연결이 끊긴 상태이니까!!

그래도 기어이 클라이언트 안에 서버를 넣고 싶다면..?

children 이용하기!

이미 “use client”를 바른 상태의 컴포넌트가 있을때,

이 안이 children 는 서버 컴포넌트일수도 있죠?

클라이언트 컴포넌트가 서버 컴포넌트를 직접 불러온 것이 아닌 서버가 미리 서버 컴포넌트를 만들어서 클라이언트 컴포넌트의 children 에 넣어준 것이기 때문이랍니다

// ClientComponent.tsx
'use client';

import ServerComponent from './ServerComponent'; // ❌ 이렇게 직접 불러오면 안 돼요!

export default function ClientComponent() {
  return (
    <div>
      <button onClick={() => alert('클라이언트!')}>클릭</button>
      <ServerComponent /> {/* 서버 전용 기능이 여기서 작동 안하겠죠? */}
    </div>
  );
}
// 1. 클라이언트 컴포넌트 (껍데기 역할)
'use client';

export default function ClientWrapper({ children }: { children: React.ReactNode }) {
  console.log("나는 브라우저에서 실행돼요!");
  return (
    <div className="border-2 border-blue-500 p-4">
      <button onClick={() => alert('조립 완료!')}>클릭</button>
      {children} 
    </div>
  );
}

// 2. 서버 컴포넌트 (실제 내용물)
// 'use client'가 없으니 기본적으로 서버 컴포넌트입니다.
export default async function ServerContent() {
  console.log("나는 서버에서만 실행돼요! DB 접속 가능!");
  return <div>서버에서 가져온 아주 무거운 데이터 내용</div>;
}

// 3. 페이지 (합치기) - app/page.tsx
export default function Page() {
  return (
    <ClientWrapper>
      <ServerContent /> 
    </ClientWrapper>
  );
}

블로그에서 사이드바(RightBar)는 클릭 이벤트가 많으니 'use client'겠죠?

하지만 그 사이드바 안에 서버에서 실시간으로 가져온 공지사항 같은 걸 넣고 싶다면, 이 children 패턴을 쓰면 된다.

“use client” 남발해도 될까?

서버 클라이언트는 정적인 거만 가능하니 제약이 많을텐데,

그렇다면 모든 컴포넌트를 “use client” 붙이면 되는 것 아닌가? 라고 생각했다..

그렇게 하면 돌아는 간다! (그러면 Next.js는 왜 쓰냐 이거야)

  1. 브라우저가 무거워진다.

    클라이언트 컴포넌트가 많아지면 페이지 로딩 속도가 느려진다. (가능한 최소화하면 좋겠죠?)

  2. 구글 로봇이 글을 잘 못 읽는다. → SEO 문제

    Next.js의 가장 큰 장점은 서버에서 미리 다 그려서 보내주는 것이라 검색엔진에 유리하다는 점이다.

    그런데 전부 “use client”로 도배한다? 브라우저가 자바스크립트 다 실행하고 나서 보여주기 때문에 검색 로복은 “어, 여기 아무것도 없네?” 하고 그냥 돌아가버린다..

  3. 보안 취약

    서버 컴포넌트는 브라우저에 노출이 안된다. 그런데 클라이언트 컴포넌트는 개발자 도구에 다 노출됨!!

그러니까 큰 틀은 서버 컴포넌트로 개발하고, 그 안에 동적인 클라이언트 컴포넌트를 구성하는 것이 효율적이다!

결론

사실 초반엔 디폴트로 서버 컴포넌트 사용하다가 동적인게 들어가면 그때 “use client”를 붙여왔었는데,

이번에 공부하면서 내 코드들을 다시 검토해볼 수 있었다.

TERMINAL
DEBUG CONSOLE
OUTPUT
~/stay-young-loggit(main)npm run comment:write
nickname:
content:
-- TOTAL COMMENTS: 0 --
[LOADING...] fetching data from supabase...