이설아
Study

10. 리액트 17과 18의 변경사항 살펴보기

10.1 리액트 17 버전 살펴보기

리액트의 점진적인 업그레이드

  • 전체 애플리케이션 트리는 리액트 17이지만, 일부 트리와 컴포넌트에 대해서만 18을 선택하는 점진적인 버전 업 가능

    ⇒ 이상적인 방법은 아니나, 버전을 올리기에 부담이 되는 큰 애플리케이션에서는 고려해볼만 함

  • 리액트 팀에서는 한꺼번에 업그레이드가 불가능한 상태에서만 차선책으로 선택하는 것이 좋다고 함

이벤트 위임 방식의 변경

  • 이벤트 위임이란 이벤트 단계의 원리를 활용하여 이벤트를 상위 컴포넌트에만 붙이는 것
  • 16 버전까지는 모두 document에서 수행되고 있었으나, 17부터는 컴포넌트 최상단 트리인 루트 요소로 바뀜

import React from ’react'가 더 이상 필요 없다: 새로운 JSX transform

  • 17부터 바벨과 협력하여 import 구문 없이도 JSX로 변환 가능

    ⇒ 불필요한 구문을 삭제하여 번들링 크기 감소 가능

    ⇒ 컴포넌트 작성을 간결하게 도와줌

그 밖의 주요 변경 사항

이벤트 풀링 제거

  • 이벤트 풀링: SyntheticEvent 풀을 만들어서 이벤트가 발생할 때마다 가져오는 것을 의미
  • 제거 이유
    1. 비동기 코드로 이벤트 핸들러에 접근하기 위해 별도 메모리 공간에 합성 이벤트 객체를 할당해야 함
    2. 해당 방식이 모던 브라우저에서 성능 향상에 크게 도움이 되지 않음

useEffect 클린업 함수의 비동기 실행

  • 16 버전까지 useEffect에 있는 클린업 함수는 비동기적으로 처리됨

  • 17 버전부터는 화면이 완전히 업데이트 된 이후 클린업 함수가 비동기적으로 실행됨

    ⇒ 클린업 함수는 컴포넌트의 커밋 단계가 완료될 때까지 지연됨

컴포넌트의 undefined 반환에 대한 일관적인 처리

  • 16과 17버전에서 컴포넌트 내부에서 undefined를 반환하면 오류를 발생하도록 함

    ⇒ 의도치 않게 잘못된 반환으로 인한 실수 방지

  • but, 16 버전에서 forwardRefmemo에서 undefined를 반환할 경우 에러가 발생하지 않음

  • 17 버전에서 해당 오류 해결

  • but, 18 버전부터는 undefined를 반환해도 에러가 발생하지 않음

10.2 리액트 18 버전 살펴보기

새로 추가된 훅 살펴보기

useId

  • 컴포넌트 별로 유니크한 값을 생성

  • Math.random()은 서버에서 렌더링 했을 때와, 이벤트를 입히기 위해 하이드레이션을 했을 때의 값이 다름

    ⇒ useId를 사용하여 클라이언트와 서버에서 불일치를 피하면서 컴포넌트 내부의 고유한 값을 생성 가능

useTransition

  • UI 변경을 가로막지 않고 상태를 업데이트 가능

  • 해당 훅을 활용하면 상태 업데이트를 긴급하지 않은 것으로 간주해 무거운 렌더링을 미룰 수 있음

    ⇒ 사용자에게 더 나은 UX 제공 가능

주의할 점

  • startTransition 내부는 반드시 setState와 같은 상태를 업데이트하는 합수와 관련된 작업만 넘길 수 있다. 만약 props나 사용자 정의 흑에서 반환하는 값 등을 사용용하고 싶다면 뒤이어 설명할 useDefferedValue를 시용하면 된다.
  • startTransition으로 넘겨주는 상태 업데이트는 다른 모든 동기 상태 업데이트로 인해 실행이 지연될 수 있다. 예를 들어, 타이핑으로 인해 setState가 일어나는 경우 타이핑이 끝날 때까지 useTransition으로 지연시킨 상태 업데이트는 일어나지 않는다.
  • startTransition으로 넘겨주는 함수는 반드시 동기 함수여야 한다. 만약 이 안에 setTimeout과 같은 비동기 함수를 넣으면 제대로 작동하지 않게 된다. 이는 startTransition이 작업을 지연시키는 작업과 비동기로 함수가 실행되는 작업 사이에 불일치가 일어나기 때문이다

useDeferredValue

  • 컴포넌트 트리에서 리렌더링이 급하지 않은 부분을 지연할 수 있게 도와주는 훅

  • 디바운스와 비슷하지만 useDeferredValue 만 가진 장점이 존재

    • 고정된 지연시간 없이 렌더링이 완료된 이후에 useDeferredValue 로 지연된 렌더링 수행

      ⇒ 지연된 렌더링은 중단 가능하며, 사용자의 인터랙션을 차단하지도 않음

  • useDeferredValue와 useTransition 차이점

    • useTransition은 state 값을 업데이트 하는 함수를 감싸서 사용
    • useDeferredValue은 state 값 자체만을 감싸서 사용

useSyncExternalStore

  • 테어링 현상 해결 가능 ⇒ 테이링 현상: 외부 데이터 소스에 리액트에서 추구하는 동시성 처리가 추가돼 있지 않을 때 발생하는 것

uselnsertionEffect

  • CSS-in-js 라이브러리를 위한 훅

  • 17 버전에서 styled-components에서 클라이언트 렌더링 시 다시 계산이 되지 않도록 서버 사이드에서 스타일 코드 삽입

    ⇒ 해당 작업을 할 수 있도록 도와주는 훅이 uselnsertionEffect

react-dom/clinet

  • 클라이언트에서 리액트 크리를 만들 때 사용되는 API가 변경됨
  • 만약 18 이하 버전에서 만든 프로젝트를 유지 보수 중이라면 18로 업그레이드 시 반드시 index.{t:j}jsx 에 있는 내용을 변경해야 함

createRoot

  • react-dom에 있던 render 메서드를 대체할 새로운 메서드
  • 18의 기능을 사용하고 싶다면 createRoot와 reder를 함께 사용해야 함
// before
import ReactDOM from 'react-dom';
import App from 'App';
 
const container = document.getElementByld('root');
 
ReactDOM.render(<App />, container);
// after
import ReactDOM from 'react-dom';
import App from 'App';
 
const container = document.getElementByld('root');
 
const root = ReactDOM.createRoot(container);
root.render(<App />);

hydrateRoot

  • 서버 사이드 렌더링 애플리케이션에서 하이드레이션을 하기 위한 새로운 메서드
// before
import ReactDOM from 'react-dom';
import App from 'App';
 
const container = document.getElementByld('root');
 
ReactDOM.hydrate(<App />, container);
// after
import ReactDOM from 'react-dom';
import App from 'App';
 
const container = document.getElementByld('root');
 
const root = ReactDOM.hydrateRoot(container, <App />);

react-dom/server

  • 서버에서 컴포넌트를 생성하는 API에 변경

renderToPipeableStream

  • 리액트 컴포넌트를 HTML로 렌더링하는 메서드
  • 스트림을 지원하는 메서드
  • HTML을 점진적으로 렌더링
  • 클라이언트에서는 중간에 script를 삽입하는 등의 작업 수행

자동 배칭(Automatic Batching)

  • 리액트가 여러 상태 업데이트를 하나의 리렌더링으로 묶어서 성능을 향상시키는 방법
  • flushSync는 react가 아닌 react-dom에서 제공
  • 자세한 설명 (opens in a new tab)

더욱 엄격해진 엄격 모드

리액트의 엄격 모드

  • 리액트에서 제공되는 컴포넌트 중 하나
  • 리액트 애플리케이션에서 발생할 수 있는 잠재적인 버그를 찾는데 도움
    1. 더 이상 안전하지 않은 특정 생명주기를 사용하는 컴포넌트에 대해 경고
    2. 문자열 ref 사용 금지
    3. findDOMNode에 대한 경고 출력
    4. 구 Context API 사용 시 발생하는 경고
    5. 예상치 못한 부작용(side-effects) 검사
  • 향후 컴포넌트가 마운트 해제된 상태에서도 컴포넌트 내부의 상태값을 유지할 수 있는 기능 제공 예정

Suspense 기능 강화

  • 컴포넌트를 동적으로 가져올 수 있게 도와주는 기능
  • 18 버전에서 지원되는 Suspense
    • 아직 마운트되기 직전임에도 effect가 빠르게 실행되는 문제가 수정됐다. 이제 컴포넌트가 실제로 화면에 노출될 때 effect가 실행
    • Suspense로 인해 컴포넌트가 보이거나 사라질 때도 effect가 정상적으로 실행된다. 이전에는 컴포넌트 스스로가 Suspense에 의해 현재 보여지고 있는지, 숨겨져 있는지 알 방법이 없었다. 그러나 이제 Suspense에 의해 노출된다면 useLayoutEffecteffect가 가려진다면, useLayoutEffectcleanup이 정상적으로 실행된다.
    • Suspense를 이제 서버에서도 실행할 수 있게 된다. 서버에서는 일단 fallback 상태의 트리를 클라이언트에 제공하고, 불러올 준비가 된다면 자연스럽게 렌더링된다.
    • Suspense 내에 스로틀링이 추가됐다. 화면이 너무 자주 업데이트 되어 시각적으로 방해받는 것을 방지하기 위해 리액트는 다음 렌더링을 보여주기 전에 잠시 대기한다. 즉, 중첩된 Suspense의 fallback이 있다면 자동으로 스로틀되어 최대한 자연스럽게 보여주기 위해 노력한다.

인터넷 익스플로러 지원 중단에 따른 추가 폴리필 필요

  • Promise: 비동기 연산이 종료된 이후에 실패 또는 결과값을 확인할 수 있는 객체
  • Symbol: 자바스크립트의 새로운 데이터 형식으로 익명의 객체 속성을 만들 수 있는 특성을 가진 객체
  • Object.assign: 객체의 열거 가능한 모든 속성을 다른 객체로 붙여 넣는 메서드

그 밖에 알아두면 좋은 변경사항

  • 컴포넌트에서 undefined를 반환해도 에러가 발생하지 않음

    ⇒ undefined 반환은 null 반환과 동일하게 처리

  • <Suspense fallback = {undefined}>null과 동일하게 처리

  • renderToNodeStream 이 지원 중단됨

    ⇒ 그 대신 renderToPipeableStream을 사용하는 것이 권장

11. Next.js 13과 리액트 18

11.1 app 디렉터리의 등장

라우팅

  • /pages로 정의하던 라우팅 방식이 /app 디렉터리로 이동
  • 파일명으로 라우팅하는 것이 불가능해짐

라우팅을 정의하는 법

  • /app/a/b/a/b로 변환되며, 파일명은 무시. 폴더명까지만 주소로 변환
  • Next.js 13의 app 디렉터리 내부의 파일명은 라우팅 명칭에 아무런 영향을 미치지 못함

layout.js

  • 페이지의 기본적인 레이아웃을 구성하는 요소
  • 해당 폴더에 layout이 있다면 그 하위 폴더 및 주소에 모두 영향을 미침
// app/layout.tsx
import { ReactNode } from 'react'
 
export default function AppLayout({ children }: { children: ReactNode }) {
	return (
		<html lang="ko">
			<head >
				<title>안녕하셰요 !</title>
			</head >
			<body>
				<h1>웹페이지에 오신 것을 환영합니다 .</h1>
				<main >{ c h i l d r e n } </main >
			</body>
		</html>
	)
}
 
// app/blog/layout.tsx
import { ReactNode } from 'react'
 
export default function BlogLayout({ children }: { children: ReactNode }) {
	return <section>{children}</section>
}
  • 루트에 단 하나의 layout을 만들어 둘 수 있음

    ⇒ 모든 페이지에 영향을 미치는 공통 레이아웃

  • 웹 페이지를 만드는 데 필요한 공통적인 내용(html, head 등)을 다루는 곳

  • _document.jsx에서만 처리할수 있었던 부자연스러움이 사라짐

주의할 점

  • layout은 app 디렉터리 내부에서는 예약어다. 무조건 layout.{js|jsx|ts|tsx}로 사용해야 하며, 레이아웃 이외의 다른 목적으로는 사용 불가
  • layout은 children을 props로 받아서 렌더링해야 한다. 레이아웃이므로 당연히 그려야 할 검포넌트를 외부에서 주입받고 그려야 한다.
  • layout 내부에는 반드시 export default로 내보내는 컴포넌트가 있어야 한다.
  • layout 내부에서도 API 요청과 같은 비동기 작업을 수행할 수 있다. 이에 대한 자세한 내용은 뒤이어서 다룬다.

page.js

  • 예약어로, Next.js에서 일반적으로 다뤘던 페이지를 의미

규칙

  • page도 역시 app 디렉터리 내부의 예약어다. 무조건 page.{js|jsx|ts|tsx}로 사용해야 하며, 레이아웃 이외의 다른 목적으로는 사용할 수 없다.
  • page도 역시 내부에서 반드시 export default로 내보내는 컴포넌트가 있어야 한다.

error.js

  • 해당 라우팅 영역에서 사용되는 공통 에러 컴포넌트
  • 특정 라우팅별로 서로 다른 에러 UI를 렌더링 하는 것이 가능
  • error 페이지는 에러 정보를 담고 있는 error: Error 객체와 에러 바운더리를 초기화 할 reset: () => voidprops로 받음
  • 에러 바운더리는 클라이언트에서만 작동하므로 error 컴포넌트도 클라이언트 컴포넌트여야 함

not-found.js

  • 특정 라우팅 하위 주소를 찾을 수 없는 404 페이지를 렌더링 할 때 사용
export default function NotFound(){
	return (
		<>
			<h2>Not Fount</h2>
			<p>404<p>
		</>
	)
}

loading.js

  • Suspense를 기반으로 해당 컴포넌트가 불러오는 중임을 나타낼 때 사용
export default function Loading() {
  return 'Loading...';
}

route.js

  • 디렉터리가 라우팅 주소를 담당하며 파일명은 route.js로 통일
  • route의 함수들이 받을 수 있는 파라미터
    • request: NextRequest 객체이며, fetchRequest를 확장한 Next.js만의 Request라고 보면 된다. 이 객체에는 API 요청과 관련된 cookie, headers 등뿐만 아니라 nextUrl 같은 주소 객처|도 확인할 수 있다.
    • context: params만을 가지고 있는 객체이며, 이 객체는 앞서 파일 기반 라우팅에서 언급한 것과 동일한 동적 라우팅 파라미터 객체가 포함돼 있다. 이 객체는 Next.js에서 별도 인터페이스를 제공하지 않으므로 주소의 필요에 따라 원하는 형식으로 선언하면 된다.

11.2 리액트 서버 컴포넌트

기존 리액트 컴포넌트와 서버 사이드 렌더링의 한계

  1. JS 번들 크기가 0인 컴포넌트를 만들 수 없다
  2. 백엔드 리소스에 대한 직접적인 접근이 불가능하다
  3. 자동 코드 분할(code split)이 불가능하다
  4. 연쇄적으로 발생하는 클라이언트와 서버의 요청을 대응하기 어렵다
  5. 추상화에 드는 비용이 증가한다

서버 컴포넌트란?

  • 하나의 언어, 하나의 프레임워크, 그리고 하나의 API와 개념을 사용하면서 서버와 클라이언트 모두에서 컴포넌트를 렌더링 할 수 있는 기법
  • 일부 컴포넌트는 클라이언트에서, 일부 컴포넌트는 서버에서 렌더링 되는 것

서버 컴포넌트

  • 요청이 오면 그 순간 서버에서 딱 한 번 실행될 뿐이므로 상태를 가질 수 없음 ⇒ 리액트에서 상태를 가질 수 있는 useState, useReducer 등의 훅을 사용 불가

  • 렌더링 생명주기도 사용 불가

    ⇒ 한 번 렌더링되면 그걸로 끝이기 때문

  • 앞으로 두 가지 제약사항으로 인해 effect나 state에 의존하는 사용자 정의 훅 또한 사용 불가

    ⇒ 그러나 effect나 state에 의존하지 않고 서버에서 제공할 수 있는 기능만 사용하는 훅이라면 충분히 사용 가능

  • 브라우저에서 실행되지 않고 서버에서만 실행되기 때문에 DOM API를 쓰거나 window, document 등에 접근 불가

  • 데이터베이스, 내부 서비스, 파일 시스템 등 서버에만 있는 데이터를 async/await으로 접근 가능

    ⇒ 컴포넌트 자체가 async한 것이 가능

  • 다른 서버 컴포넌트를 렌더링 하거나 div, spin, p 같은 요소를 렌더링하거나, 혹은 클라이언트 컴포넌트를 렌더링할 수 있음

클라이언트 컴포넌트

  • 브라우저 환경에서만 실행되므로 서버 컴포넌트를 불러오거나 서버 전용 혹이나 유틸리티를 불러올 수 없음

  • 그러나 앞의 코드에서 본 것처럼 서버 컴포넌트가 클라이언트 컴포넌트를 렌더링하는데, 그 클라이언트 컴포넌트가 자식으로 서버 컴포넌트를 갖는 구조는 가능

    ⇒ 그 이유는 클라이언트 입장에서 봤을 때 서버 컴포넌트는 이미 서버에서 만들어진 트리를 가지고 있을 것이고 클라이언트 컴포넌트는 이미 서버에서 만들어진 그 트리를 삽입해서 보여주기만 하기 때문

    ⇒ 따라서 서버 컴포넌트와 클라이언트 컴포넌트를 중접해서 갖는 위와 같은 구조로 설계하는 것이 가능

  • 이 두 가지 예외 사항을 제외하면 일반적으로 우리가 알고 있는 리액트 컴포넌트와 같음

    ⇒ state와 effect를 사용할 수 있으며, 브라우저 API도 사용 가능

공용 컴포넌트

  • 이 컴포넌트는 서버와 클라이언트 모두에서 사용 가능

    ⇒ 공통으로 사용할 수 있는 만큼, 당연히 서버컴포넌트와 클라이언트 컴포넌트의 모든 제약을 받는 컴포넌트가 됨

서버 사이드 렌더링과 서버 컴포넌트의 차이

서버 사이드 렌더링

  1. 답받은 페이지 전체를 HTML로 렌더링하는 과정을 서버에 수행한 수 해당 결과를 클라이언트에 내림
  2. 클라이언트에서 하이드레이션 과정을 거쳐 서버의 결과물 확인 후 이벤트를 붙이는 등의 작업 수행
  • 목적은 초기에 인터랙션은 불가능하지만 정적인 HTML을 빠르게 내려주는데 초점을 맞춤

    ⇒ 초기 HTML이 로딩된 이후에는 클라이언트에서 JS 코드 다운로드하고, 파싱하고, 실행하는데 비용이 듦

결론

  • 둘은 대체제가 아닌 상호보완하는 개념

서버 컴포넌트는 어떻게 작동하는가?

  1. 서버가 렌더링 요청
  2. 서버는 받은 요청에 따라 컴포넌트를 JSON으로 직렬화(Serialize)
  3. 브라우저가 리액트 컴포넌트 트리를 구성

특별한 점

  • 먼저 서버에서 클라이언트로 정보를 보낼 때 스트리밍 형태로 보냄으로써 클라이언트가 줄 단위로, JSON을 읽고 컴포넌트를 렌더링 할 수 있어 브라우저에서는 되도록 빨리 사용자에게 결과물을 보여줄 수 있다는 장점
  • 컴포넌트들이 하나의 번들러 작업에 포함돼 있지 않고 각 컴포넌트 별로 번들링이 별개로 돼 있어 필요에 따라 컴포넌트를 지연해서 받거나 따로 받는 등의 작업이 가능
  • HTML 대신 단순한 리액트 컴포넌트 구조를 JSON으로 받아서 리액트 컴포넌트 트리의 구성을 최대한 빠르게 할 수 있도록 도와 줌

11.3 Next.js에서의 리액트 서버 컴포넌트

새로운 fetch 도입과 getServerSideProps, getStaticProps, getlnitial Props의 삭제

  • getServerSideProps, getStaticProps, getlnitial Props가 /app 디렉터리 내부에서 삭제

    ⇒ 모든 데이터 요청은 웹에서 제공하는 표준인 API인 fetch를 기반으로 이루어짐

  • getServerSideProps는 SSR을 위한 것이었으므로, 서버에서 데이터를 직접 불러올 수 있게 됨

정적 렌더링과 동적 렌더링

  • 정적인 라우팅에 대해서는 기본적으로 빌드 타임에 렌더링을 미리 해두고 캐싱해 재사용 가능하게 함
  • 동적인 라우팅에서는 서버에 매번 요청이 올 때마다 컴포넌트를 렌더링하도록 변경
  • 캐싱하지 않겠다는 선언을 fetch에 해두면 Next.js는 해당 요청을 미리 빌드하여 대기 시키지 않고 요청이 올 때마다 fetch 요청 이후에 렌더링 수행
  • 동적인 주소이지만 특정 주소에 대해서 캐싱하고 싶은 경우, generateStaticParams 사용

캐시와 mutating, 그리고 revalidating

  • fetch의 기본 작동을 재정의 하여 해당 데이터의 유효한 시간을 정해두고 해당 시간이 지나면 다시 데이터를 불러와 페이지를 렌더링
    1. 최초로 해당 라우트로 요청이 올 때는 미리 정적으로 캐시해 둔 데이터를 보여줌
    2. 이 캐시된 초기 요청은 revalidate에 선언된 값만큼 유지
    3. 만약 해당 시간이 지나도 일단 캐시된 데이터를 보여줌
    4. Next.js는 캐시된 데이터를 보여주는 한 편, 시간이 경과했으므로 백드라운드에서 다시 데이터를 불러옴
    5. 4번의 작업이 성공적으로 끝나면 캐시된 데이터를 갱신하고, 그렇지 않으면 과거 데이터를 보여줌
  • 이러한 캐시를 전체적으로 무효화 하고 싶다면 router에 추가된 refresh 메서드로 router.refresh() 사용

스트리밍을 활용한 점진적인 페이지 불러오기

  • HTML을 작은 단위로 쪼개서 완성되는 대로 클라이언트로 점진적으로 보내는 스트리밍

    ⇒ 모든 데이터가 로드될 때까지 기다리지 않더라도 먼저 데이터가 로드되는 컴포넌트를 빠르게 보여주는 방법 가능

  • 사용자가 일부라도 페이지와 인터랙션을 할 수 있음

    ⇒ 핵심 웹 지표인 최초 바이트까지의 시간 (TTFB: Time To First Byte)과 최초 콘텐츠풀 페인팅(FCP: First Contentful Paint)을 개선하는데 큰 도움

11.4 웹팩의 대항마, 터보팩의 등장(beta)

  • 웹팩 대비 최대 700배, Vite 대비 최대 10배 빠름
  • 러스트 기반으로 작성
  • next dev —trubo로 개발 환경에서 터보팩 활성화

11.5 서버 액션(alpha)

  • API를 굳이 생성하지 않더라도 함수 수준에서 서버에 직접 접근하여 데이터 요청 등을 수행할 수 있는 기능

  • 서버 컴포넌트와 달리, 특정 함수 실행 그 자체만을 서버에서 수행할 수 있다는 장점

    ⇒ 해당 실행 결과에 따라 다양한 작업을 수행 가능

  • 서버 액션을 활성화 하려면 next.config.js에서 실험 기능을 활성화 해야 함

/** @type {import(’next’).NextConfig} */
const nextConfig = {
  experimental: {
    serverActions: true,
  },
};
 
module.exports = nextConfig;
  • 함수 내부 또는 파일 상단에 선언과 비슷하게 "use server" 지시자 선언
async function serverAction() {
	"use server‘,
	// 서버에 바로 접근하는 코드
}
 
// 이 파일 내부의 모든 내용이 서버 액션으로 간주된다.
'use server'
 
export async function myAction() {
	//...
	// 서버에 바로 접근하는 코드
}
 

form의 action

  • HTML에서 양식을 보낼 때 사용하는 태그
  • action props를 추가하여 해당 양식 데이터를 처리할 URL을 넘길 수 있음
  • 폼과 실제 노출하는 데이터가 연동되어 있을 때 효과적으로 사용 가능
  • Next.js에서 관리하는 캐시를 효과적으로 초기화 할 수 있으므로 사용자에게 더욱 자연스러운 UX 제공 가능

input의 submit과 image의 formAction

  • input type="submit" 또는 input type="image"에 formAction prop으로도 서버 액션을 추가 가능

startTransition과의 연동

  • isPending을 활용하여 startTransition으로 서버 액션이 실행됐을 때 해당 버튼을 숨기고 로딩 버튼을 노출함

    ⇒ 페이지 단위 로딩이 아닌 좀 더 컴포넌트 단위의 로딩 처리도 가능

server mutation이 없는 작업

  • 별도의 server mutation을 실행하지 않는다면 바로 이벤트 핸들러에 넣기 가능
export default function Page() {
  async function handleClick() {
    'use server';
 
    // server mutationOI 필요 없는 작업
  }
  return <button onClick={handleClick}>form 요청 보내보기 </button>;
}

서버 액션 사용 시 주의할 점

  • 서버 액션은 클라이언트 컴포넌트 내에서 정의 될 수 없음

  • 서버 액션을 import하는 것 뿐만 아니라, props 형태로 서버 액션을 클라이언트 컴포넌트에 넘기는 것 또한 기능

    ⇒ 서버에서만 실행될 수 있는 자원은 반드시 파일 단위로 분리

11.6 그 밖의 변화

  • 프로젝트 전체 라우트에서 사용할 수 있는 미들웨어 강화
  • SEO를 쉽게 작성할 수 있는 기능 추가
  • 정적으로 내부 링크를 분석할 수 있는 기능 추가