이전 버전의 Next.js
- 컴포넌트를 총 2번 렌더링
- 백엔드에서 한 번, 프론트엔드에서 한 번
- 백엔드에서 미리 렌더링
-> 유저가 페이지로 이동하면 비인터랙티브 버전의 앱이 표시됨
-> 이후 비인터랙티브 UI 위에 React 앱을 초기화하여 인터랙티브 앱으로 만듦
- 즉, 브라우저가 모든 컴포넌트의 코드를 다운로드한 뒤 브라우저에서 다시 렌더링
- 브라우저가 모든 컴포넌트 코드를 다운로드하므로 누구나 소스 코드를 볼 수 있음
- 즉, 컴포넌트에서 데이터베이스에 연결하거나 비밀 API 키를 노출할 수 없음
- 대신 API를 통해 데이터베이스에서 안전하게 가져오거나 백엔드에서 API 키를 사용해야
서버 컴포넌트 사용 시
- 컴포넌트의 두 번째 렌더링을 선택 해제 가능
- 즉, 백엔드에서 컴포넌트가 한 번만 렌더링됨
- 유저에게는 결과 UI만 제공되고 컴포넌트 자바스크립트 코드는 제공하지 않음
- 유저가 브라우저에서 애플리케이션의 모든 컴포넌트 코드를 다운로드하고 다시 렌더링할 필요가 없음 => 엄청난 양의 코드 삭제
function Posts() {
const [posts, setPosts] = useState([]);
const fetchPosts = async () => {
const data = await (await fetch("/api/posts")).json();
setPosts(data);
}
useEffect(() => {
fetchPosts();
});
return <ul>{posts.map(post => <li>...</li>)}</ul>;
}
async function Posts() {
const posts = await db.posts.all(); // *
return <ul>{posts.map(post => <li>...</li>)}</ul>;
}
function Page() {
return (
<html>
<body>
<h1>All Posts</h1>
<Suspense fallback={"Loading posts..."}>
<Posts />
</Suspense>
</body>
</html>
);
}
<Posts/>
자리에 'Loading posts...' 텍스트가 먼저 표시됨<Posts/>
의 글 가져오기가 완료되면<Posts/>
가 가져온 결과 UI(<ul><li>...</li></ul>
)로 바뀜function Page() {
return (
<html>
<body>
<h1>All Posts</h1>
<ul>
<li>...</li>
<li>...</li>
</ul>
</body>
</html>
);
}
'use client' 주의
'use client'는 클라이언트에서만 렌더링하라는 뜻? (x)
클라이언트와 서버 둘 다에서 렌더링됨 ('use hydrate'이 더 맞는 표현)
유저가 웹 사이트에서 새로운 계정을 생성하는 경우
/api/users/
API(유저 계정을 생성하는 API) 경로로 export default function SignUp() {
async function onSubmit(e) {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const response = await fetch('/api/users', {
method: 'POST',
body: formData,
});
//...
}
return (
<form onSubmit={onSubmit}>
<input type="text" name="name" />
<input type="text" name="username" />
<input type="text" name="password" />
<button type="submit">Submit</button>
</form>
);
}
export default function SignUp() {
async function createUser(formData) {
'use server'; // 이 한 줄로 서버 컴포넌트(함수)로 사용 가능
const user = await db.users.create(formData);
}
return (
<form action={createUser}> // 'onSubmit' 속성 아님!
<input type="text" name="name" />
<input type="text" name="username" />
<input type="text" name="password" />
<button type="submit">Submit</button>
</form>
);
}
'use client';
import { useFormStatus } from 'react-dom';
export function SubmitButton() {
const { pending } = useFormStatus();
return ...
}
'use client';
import { useFormState } from 'react-dom';
import { createUser } from '@/app/actions';
const initialState = {
message: '',
}
export function Signup() {
const [state, formAction] = useFormState(createUser, initialState);
return ...
}