[Next.js] #3 클라이언트 컴포넌트(CC)와 서버 컴포넌트(SC)
1. Next.js v12와 v13의 주요 차이점
Next.js에서 가장 중요한 개념이 바로 렌더링 방식이다.⭐(CSR, SSR, ISR, SSG)
- Next.js 버전 12까지는 렌더링 방식을 “페이지 단위”로 규정하였다.
- about 페이지는 SSG로 동작
- todolist 페이지는 SSR로 동작
- sample 페이지는 CSR로 동작
하지만 Next.js 13 버전에서는 React 18 버전에서 제시한
- 서버컴포넌트(서버상에서만 동작 → node.js 환경)
- 클라이언트컴포넌트(브라우저에서만 동작 → 브라우저 환경)
을 토대로 렌더링 방식이 기존 “페이지 단위”가 아닌 “컴포넌트 단위”로 변경되었다. 컴포넌트 하나 하나가 각각의 렌더링 방식을 가질 수 있게 되었다!!
정리
- Next.js 버전 12는
페이지 단위
가 렌더링 기준 - Next.js 버전 13은
컴포넌트 단위
가 렌더링 기준⭐
위 그림과 같이 서버컴포넌트와 클라이언트컴포넌트가 공존이 가능하다.
2. 클라이언트 컴포넌트 vs 서버 컴포넌트
2.1 node.js 환경과 브라우저 환경
기본적으로 app 폴더 하위의 모든 컴포넌트는 서버컴포넌트이다.⭐⭐
"use client"
표시가 없으면 무조건 서버컴포넌트!
- 크롬 브라우저가 아니라 서버를 돌리고 있는 localhost인 내 컴퓨터의 node 환경에서 console.log가 출력되고 있다.
- 만약 aws 같은 클라우드 컴퓨팅에서 돌아가고 있다면, 사용자의 브라우저가 아니라 해당 서버의 로그에 console.log가 출력될 것이다.
- 이처럼 컴포넌트의 실행 환경이 브라우저인지, 서버인지에 따라 서버컴포넌트인지 클라이언트 컴포넌트인지가 결정된다.
- 위와 같이
"use client"
를 적어주면 브라우저에 console.log가 출력되고 있는 것을 확인할 수 있다. - 서버 컴포넌트에서는 alert, confirm 처럼 유저와의 상호작용이 필요한 메서드, 기능은 사용할 수 없지만,
“use client”;
를 컴포넌트 최상단에 붙히면 사용할 수 있게 된다.
컴포넌트에 alert(“hello”)를 출력해보자
import React from "react";
alert("경고!!");
const Home = () => {
return <div>Home</div>;
};
export default Home;
alert는 서버 컴포넌트에서(Node.js 런타임 환경)는 사용할 수 없는 것을 확인할 수 있다.
2.2 SC CC의 사용
아래 예시를
"use client";
를 넣은 상태에서 1회 테스트 + 뺀 상태에서 1회 테스트를 해보자
"use client";
import { useEffect, useState } from "react";
// src>app>page.tsx
export default function Home() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log("렌더링이 완료되었습니다.");
}, []);
const handleClick = () => {
setCount(count + 1);
};
return (
<div className="p-8">
행복한 하루 보내세요!
<div>{count}</div>
<button onClick={handleClick}>클릭</button>
</div>
);
}
- “use client”가 없다면, 오류가 발생하는 것을 확인할 수 있다.
- 앞으로 아래와 같은 오류가 발생한다면 클라이언트 컴포넌트로 변경해야한다.
언제 클라이언트 컴포넌트(SC), 서버 컴포넌트(CC)를 사용할까?
Next.js 공식문서에선 User와의 상호작용이 있는 경우 CC를, 그 외의 경우는 SC를 쓰도록 권장하고 있다.
3. 컴포넌트 분리하기
3.1 예시1
만약 이런 코드가 있다고 가정해보자
// 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
onClick={() => {
alert("안녕하세요!");
}}
>
클릭
</button>
</div>
);
}
- 위 코드는 “use client”가 없기 때문에 서버에서만 동작한다.(서버 컴포넌트)
- useEffect, useState, onClick 등은 클라이언트 컴포넌트에서만 사용 가능한 기술이기 때문에 오류가 발생한다.
그렇다면
"use client";
을 사용하면 되는 것 아닌가??!
아니다!!!! onClick 하나 때문에 모든 컴포넌트의 모든 구성요소가 클라이언트 컴포넌트로 만들어버리는 것은 엄청난 낭비이다. (Next.js를 사용하는 이유 없어짐)
“default는 서버 컴포넌트이다. 꼭 필요한 곳에만 “use client”를 이용해서 클라이언트 컴포넌트로 만들어야 한다.” 는 것을 명심하자!⭐⭐⭐
yarn build
yarn start
아래와 같이 컴포넌트 분리를 할 수 있다.
- 서버 컴포넌트
// src > app > page.tsx
import Button from "@/components/Button";
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>
);
}
- 클라이언트 컴포넌트
// src > components > Button.tsx
"use client";
import React from "react";
const Button = () => {
return (
<button
onClick={() => {
alert("안녕하세요!");
}}
>
클릭
</button>
);
};
export default Button;
3.1 예시2
아래 예시도 컴포넌트 분리를 해보자
분리 전
// src > app > page.tsx
"use client";
import { useEffect, useState } from "react";
// src>app>page.tsx
export default function Home() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log("렌더링이 완료되었습니다.");
}, []);
const handleClick = () => {
setCount(count + 1);
};
return (
<div className="p-8">
안녕하세요! 내배캠 리액트.. 아니아니 넥스트입니다!
<div>{count}</div>
<button onClick={handleClick}>클릭</button>
</div>
);
}
분리 후
- 서버 컴포넌트
// src > app > page.tsx
import Counter from "@/components/Counter";
export default function Home() {
return (
<div className="p-8">
안녕하세요! 내배캠 리액트.. 아니아니 넥스트입니다!
<Counter />
</div>
);
}
- 클라이언트 컴포넌트
"use client";
import React from "react";
import { useState } from "react";
const Counter = () => {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
};
return (
<div>
<div>{count}</div>
<button onClick={handleClick}>클릭</button>
</div>
);
};
export default Counter;
댓글남기기