본문으로 건너뛰기
Henry
모든 노트

Next.js · React

Server Action과 일반 API 요청의 차이

Server Action도 브라우저에서 호출하면 네트워크 요청이 나간다. 일반 API 요청과 다른 점과 워터폴이 생기는 조건을 정리했다.

목차

Server Action을 쓰면 워터폴도 없어지는지 궁금했다. 일반 API 요청과 무엇이 다른지부터 정리했다.

이 노트는 Next.js App Router 기준이다. 공식 문서는 클라이언트에서 호출할 수 있는 서버 함수를 Server Function이라고 부르고 폼 제출이나 데이터 변경에 쓰는 경우를 Server Action이라고 구분한다. 아래에서는 저장 요청을 예로 들어 Server Action이라는 이름을 썼다.

Server Action 호출

클라이언트에서 아래처럼 호출한다고 해서 함수 본문이 브라우저에서 실행되는 건 아니다. saveNote는 파일 맨 위에 'use server'를 선언한 별도 모듈에서 가져온 함수라고 가정했다.

TypeScript
// 클라이언트에서 Server Action 호출
await saveNote({ title: '요청 방식 정리' })

인자를 서버로 보내고 서버에서 실행한 결과를 돌려받는다. 인자와 반환값은 React가 지원하는 형태로 직렬화되어야 한다. 이 과정은 React의 'use server' 문서에 나와 있다.

Next.js에서는 이 호출을 POST 요청으로 처리한다. URL과 요청 본문을 직접 조립하지 않아도 되는 것이지 네트워크 통신이 사라진 건 아니다. 응답을 기다리는 상태와 요청 실패도 처리해야 한다. Next.js Mutating Data

일반 API와 비교

같은 저장 작업을 HTTP API로 만들면 클라이언트 쪽 코드는 이런 모양이다. 아래는 요청 형태만 비교한 예제다. 두 방식 모두 실제로 붙일 때는 중복 제출과 실패 처리가 필요하다.

TypeScript
const response = await fetch('/api/notes', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ title: '요청 방식 정리' }),
})

if (!response.ok) throw new Error('노트 저장 실패')

Next.js 안에서도 app/api/notes/route.ts에 Route Handler를 만들어 이런 요청을 받을 수 있다. URL과 HTTP 메서드에 맞춰 응답 상태와 본문을 작성한다. Next.js Route Handlers

비교할 부분Server Action일반 HTTP API
브라우저에서 호출함수를 호출하는 코드로 작성URL·메서드·본문을 지정해 요청
전송 형식React·Next.js가 정한 직렬화와 호출 규약API에서 정한 형식. 예제에서는 JSON
HTTP 메서드POST조회·변경 등 용도에 맞게 설계
외부에서 함께 사용외부 앱에 제공할 API로는 따로 설계하는 편이 낫다웹·모바일 등 여러 클라이언트에 같은 규약 제공

폼 저장 후 화면을 갱신하는 작업에는 Server Action의 연동 기능이 편하다. 반대로 모바일 앱이나 외부 서비스에도 제공해야 한다면 호출 규약이 명시적인 HTTP API를 선택하겠다. 이건 용도에 따른 선택이고 어느 한쪽이 항상 빠르다는 뜻은 아니다.

워터폴과 병렬 요청

서버 코드에서도 요청을 하나씩 기다리면 다음 작업은 늦게 시작한다. 아래 readProducts와 readCategories는 서버 내부에서 데이터를 조회하는 함수라고 가정했다. 서로 다른 Server Action을 클라이언트에서 호출하는 예제가 아니다.

TypeScript
// 상품 조회가 끝나야 카테고리 조회를 시작한다.
const products = await readProducts()
const categories = await readCategories()

두 조회가 서로의 결과를 필요로 하지 않는다면 먼저 시작하고 함께 기다릴 수 있다.

TypeScript
const [products, categories] = await Promise.all([
  readProducts(),
  readCategories(),
])

이런 코드는 Server Component나 Route Handler 안에서도 쓸 수 있다. Server Action 하나의 내부에서도 가능하다. 다만 첫 요청에서 얻은 ID로 다음 요청을 해야 한다면 그대로 병렬 처리할 수 없다. Next.js 병렬 데이터 조회 예제

클라이언트에서 Server Action 여러 개를 Promise.all로 묶는 것은 따로 봐야 한다. 현재 Next.js 문서는 한 클라이언트에서 Server Function을 하나씩 보내고 기다린다고 설명한다. 변경될 수 있는 구현 세부사항이라는 단서도 있다. 함수 호출을 배열에 넣었다는 이유만으로 요청이 병렬로 나간다고 가정하면 안 된다. Next.js Server Function 호출 방식

조회와 저장을 나눠서 선택하기

React 문서는 Server Function을 주로 서버 상태를 변경하는 용도로 설명하고 데이터 조회용으로는 권장하지 않는다. 처음 화면에 필요한 데이터라면 Server Component에서 조회할 수 있는지 먼저 보겠다. 클라이언트에서 검색하거나 반복해서 갱신할 데이터라면 HTTP API와 클라이언트 조회 도구를 함께 쓰는 방법도 있다. React의 사용 시 주의점

어느 쪽이든 서버에서 입력값을 검증하고 보호가 필요한 데이터나 작업에는 인증·권한 검사를 해야 한다. Server Action도 UI를 거치지 않은 직접 요청을 받을 수 있다. 버튼을 숨겼거나 서버 파일에 넣었다는 이유로 권한 검사를 생략할 수는 없다. Next.js 데이터 보안

배포 뒤 Server Action 요청이 실패했던 과정은 Server Action 배포 오류 수정 글에 따로 적어두었다.