[nextJS] 서버 컴포넌트냐? 클라이언트 컴포넌트냐?

2024. 10. 14. 21:52카테고리 없음

next.js 13버전은 페이지 단위로 렌더링 방식을 규정하지 않고 컴포넌트 단위인 서버 컴포넌트, 클라이언트 컴포넌트로 렌더링을 한다.

 

 

📌 server 컴포넌트

app 폴더 안에 있는 컴포넌트들이 대부분 서버 컴포넌트이다!

server 컴포넌트 코드는 서버에서 실행되고 클라이언트에는 코드의 결과만을 제공한다.

따라서 console log 자체도 브라우저가 아닌 서버 터미널에서 실행된다. 클라이언트로 js 코드를 보내지 않기에 클라이언트 측에서 부하도 없고 웹사이트의 로딩 시간을 줄일 수 있는 장점이 있다.

 

When?  언제 사용하는가?

 

  • 데이터 패칭이 필요한 컴포넌트: 서버에서 데이터를 가져오고, 클라이언트에 그 데이터를 렌더링할 때.
  • 상태나 이벤트 처리가 필요하지 않은 UI: 예를 들어, 정적인 콘텐츠나 SEO가 중요한 페이지.

 

📌 클라이언트 컴포넌트

Next.js의 장점 중 하나는 서버 측 렌더링(SSR)과 정적 생성(SSG) 기능을 활용하여 성능과 SEO를 최적화하는 것이다.

app폴더에서 최상위 파일인 page.tsx가장 처음으로 보는 페이지 인데, 만약 이 파일을 클라이언트 컴포넌트로 사용할 경우 next.js를 사용할 의미가 없어지는 것이다. 서버 측 기능을 활용하지 않고 브라우저에서만 렌더링이 이루어지기에 next.js의 렌더링 및 핵심 장점을 못 살리는 것이기 때문!

 

SSR
서버 사이드 렌더링은 요청이 있을 때마다 페이지의 HTML 마크업을 미리 생성하여 클라이언트에 전달하는 방식.

SSG
빌드 시점에  리액트 앱을 HTML로 미리 렌더링한다. 리액트 Component를 HTML 파일로 변환하고 HTML 파일을 클라이언트에 전송하여 많은 처리나 대역폭 사용 없이 사용자에게 빠르게 표시할 수 있다.
ex> 제품 목록, 도움말/문서 등과 같이 유저가 요청을 보내기 전에  페이지를 미리 만들어도 되는 경우.

 

When?  언제 사용하는가?

  • 사용자 이벤트(onClick,onChange)를 사용할때 -> 이벤트 핸들러도 웹에서 호출하고 사용하니까! 
  • useState,useCallback,useMemo 등등 react hook을 사용할 때
  • window, document와 같은 웹 브라우저 전용 api를 사용할때
  • React 클래스 컴포넌트를 사용할때

 

"use client";

'use client'를 선언하고 사용!

 

🚨 주의

서버 컴포넌트를 클라이언트 컴포넌트에서 불러올 수는 없다.

'use client'

// You cannot import a Server Component into a Client Component.
import ServerComponent from './Server-Component'
 
export default function ClientComponent({
  children,
}: {
  children: React.ReactNode
}) {
  const [count, setCount] = useState(0)
 
  return (
    <>
      <button onClick={() => setCount(count + 1)}>{count}</button>
 
      <ServerComponent />
    </>
  )
}

 

서버에서만 실행되는 서버 컴포넌트는 브라우저에 순수한 HTML만 전송되고

브라우저에서만 실행되는 클라이언트 컴포넌트는 브라우저에서 필요한 js가 포함되기에 서버 컴포넌트를 포함할 수가 없는 것이다!

 

HOW?  그럼 어떻게 두 컴포넌트를 같이 사용하는가?

클라이언트 컴포넌트에서 서버 컴포넌트를 불러올 수는 없지만 

서버 컴포넌트에서는 클라이언트를 포함할 수 있다!

 

1. 서버 요소를 props를 통해 클라이언트에 전달

const ServerComponent= async () =>{
  const data = await fetchData();
  return <ClientComponent data={data} />;
};

 

서버 컴포넌트로부터 data를 props로 전달하여 

'use client'

const ClientComponent = ({data}) =>{
  return <div>{data}</div>;
};

클라이언트 컴포넌트가 받는다.

 

2. children을 이용

// ServerComponent.tsx (Server Component)
const ServerComponent = ({ children }: { children: React.ReactNode }) => {
  // 서버에서 데이터를 가져오는 로직
  const data = fetchData(); 
  return (
    <div>
      <h1>서버 컴포넌트에서 가져온 데이터: {data}</h1>
      {children} {/* 클라이언트 컴포넌트가 children으로 들어옴 */}
    </div>
  );
};

export default ServerComponent;

 

// ClientComponent.tsx (Client Component)
'use client'; // 클라이언트 컴포넌트로 선언
const ClientComponent = () => {
  const handleClick = () => {
    alert('클라이언트에서만 동작!');
  };

  return (
    <div>
      <button onClick={handleClick}>클릭</button>
    </div>
  );
};

export default ClientComponent;

이렇게 두개의 컴포넌트 파일을 만든 후

export default function Page(){
  return(
    <ServerComponent>
      <ClientComponent />
    </ServerComponent>
  );
 }

서버가 클라이언트를 포함하는 방식으로 사용 할 수 있다.