Next.js 14를 배워야 하는 이유 (feat. 노마드 코더)

Sheryl Yun·2024년 2월 13일
0

Next.js

목록 보기
6/6
post-thumbnail

🎥 영상 링크

개요

  • Next.js는 풀스택 웹 애플리케이션을 구축하기 위한 최고의 React 프레임워크
  • 14 버전에서 업데이트된 메인 기능 2가지
    • 서버 컴포넌트
    • 서버 액션
  • 서버 컴포넌트는 13 버전부터 있었지만 서버 액션은 14 버전부터 안정화

14 버전 업그레이드로 좋아진 점

  • 개발자 환경과 생산성이 엄청나게 향상됨
  • 13 버전에서 14 버전으로 마이그레이션 시 장점
    • 많은 코드, 라이브러리 삭제
    • 유저가 다운로드 하는 자바스크립트 코드 감소

서버 컴포넌트

  • 컴포넌트를 서버에서만 렌더링할지 여부 선택 가능

이전 버전의 Next.js

  • 컴포넌트를 총 2번 렌더링
    • 백엔드에서 한 번, 프론트엔드에서 한 번
  • 백엔드에서 미리 렌더링
    -> 유저가 페이지로 이동하면 비인터랙티브 버전의 앱이 표시됨
    -> 이후 비인터랙티브 UI 위에 React 앱을 초기화하여 인터랙티브 앱으로 만듦
    • 즉, 브라우저가 모든 컴포넌트의 코드를 다운로드한 뒤 브라우저에서 다시 렌더링
  • 브라우저가 모든 컴포넌트 코드를 다운로드하므로 누구나 소스 코드를 볼 수 있음
    • 즉, 컴포넌트에서 데이터베이스에 연결하거나 비밀 API 키를 노출할 수 없음
    • 대신 API를 통해 데이터베이스에서 안전하게 가져오거나 백엔드에서 API 키를 사용해야

서버 컴포넌트 사용 시

  • 컴포넌트의 두 번째 렌더링을 선택 해제 가능
    • 즉, 백엔드에서 컴포넌트가 한 번만 렌더링됨
    • 유저에게는 결과 UI만 제공되고 컴포넌트 자바스크립트 코드는 제공하지 않음
  • 유저가 브라우저에서 애플리케이션의 모든 컴포넌트 코드를 다운로드하고 다시 렌더링할 필요가 없음 => 엄청난 양의 코드 삭제

이전 버전과 14 버전 비교: DB에서 데이터 가져오기

이전 버전 코드

  • useState와 useEffect를 써서 API를 호출하여 데이터를 가져옴
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>;
}

14 버전 코드

  • API 호출 없이 데이터베이스에서 바로 가져옴 + 유저는 컴포넌트 코드를 모두 다운로드하지 않고 컴포넌트가 반환하는 UI만 다운로드
async function Posts() {
	const posts = await db.posts.all(); // *
    
    return <ul>{posts.map(post => <li>...</li>)}</ul>;
}

서버 컴포넌트: Suspense

  • 서버 컴포넌트로 로딩 상태를 처리하려면 Suspense 사용
function Page() {
	return (
    	<html>
        	<body>
            	<h1>All Posts</h1>
                <Suspense fallback={"Loading posts..."}>
                	<Posts />
                </Suspense>
            </body>
        </html>
    );
}
  • 유저가 페이지로 이동하면 자식 컴포넌트인 <Posts/> 자리에 'Loading posts...' 텍스트가 먼저 표시됨
  • 이후 <Posts/>의 글 가져오기가 완료되면
    • Streaming Next.js 사용 시 'Loading posts...' 텍스트가 <Posts/>가 가져온 결과 UI(<ul><li>...</li></ul>)로 바뀜
      => 즉, API와 fetching 라이브러리가 필요 없게 됨
function Page() {
	return (
    	<html>
        	<body>
            	<h1>All Posts</h1>
                <ul>
                	<li>...</li>
                    <li>...</li>
                </ul>
            </body>
        </html>
    );
}

14 버전의 추가 특징

  • Next.js 14에서 모든 컴포넌트는 기본적으로 서버 컴포넌트이므로 한 번만 렌더링
  • 클라이언트 컴포넌트를 사용해야 하는 경우
    • localStorage나 Geolocation 같은 브라우저 API 사용 시
    • 이벤트 핸들러(onClick 등) 사용 시
  • 이때 컴포넌트 상단에 'use client' 한 줄만 작성하면 Next.js가 알아서 이 컴포넌트가 브라우저에서 하이드레이션되기를 원한다는 것을 인식

'use client' 주의
'use client'는 클라이언트에서만 렌더링하라는 뜻? (x)
클라이언트와 서버 둘 다에서 렌더링됨 ('use hydrate'이 더 맞는 표현)

서버 컴포넌트의 data mutation

예시

유저가 웹 사이트에서 새로운 계정을 생성하는 경우

  • 서버 액션이 없는 경우
    • 컴포넌트에 /api/users/ API(유저 계정을 생성하는 API) 경로로
    • POST 요청을 전송하는 onSubmit 함수(백엔드 데이터베이스와의 통신) 필요
 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>
    );
 }
  • 서버 액션을 사용하면
    • API 호출과 fetch 코드를 완전히 제거할 수 있음
    • 데이터베이스와 직접 통신
      • 프론트에서 서버 코드를 작성해도 Next.js가 안전하게 처리
 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>
    );
 }

14 버전의 2가지 훅

1) useFormStatus

  • use~ 커스텀 훅과 비슷
  • form의 모든 자식에서 호출 가능
'use client';

import { useFormStatus } from 'react-dom';

export function SubmitButton() {
	const { pending } = useFormStatus();
    
    return ...
}

2) useFormState

  • useReducer와 비슷
  • 오류가 발생할 경우에 대비하여 더 많은 응답 구조를 가짐
'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 ...
}

최종 정리

  • 서버 액션
    • 데이터 재검증, 쿠키 확인, mutation 완료 후 유저 redirection하기 등 여러 기능이 있음
  • 의의
    • API 엔드포인트와 fetch 코드를 완전히 제거
    • 컴포넌트에서 바로 데이터베이스 로직 작성 가능
    • 나머지는 Next.js가 알아서 처리 (컴포넌트와 액션 간에 데이터를 안전하게 전달)
profile
데이터 분석가 준비 중입니다 (티스토리에 기록: https://cherylog.tistory.com/)

0개의 댓글