Server Action과 API Route, 언제 무엇을 써야 할까?

January 4, 2026

3 min read

프로젝트 아키텍처를 리팩토링하다 보니 어떤 곳에서는 route.ts를, 어떤 곳에서는 Server Action을 사용하고 있다는 점이 눈에 들어왔다.

리팩토링을 하면서 Server Component, Server Action, route.ts를 ‘언제, 왜 쓰는지’ 기준을 다시 정리해보기로 했다.


Next.js App Router의 서버 로직 3가지

App Router 기준으로 서버 로직을 작성하는 방법은 크게 세 가지다.

  • Server Component
  • Server Action ("use server")
  • Route Handler (app/api/**/route.ts)

Route Handler (route.ts)

app/api/**/route.tsURL을 가진 HTTP 엔드포인트를 만든다.

GET  /api/auth/confirm
POST /api/webhook/payment

이 URL은 브라우저, 외부 서비스, 혹은 다른 클라이언트에서 직접 호출될 수 있다.

그렇다면 언제 route.ts를 써야 할까?

1️. 외부 시스템이 우리 서버를 호출해야 할 때

  • OAuth / SSO callback

  • 결제, 알림 Webhook

이 경우에는 URL 자체가 외부와의 계약이기 때문에 route.ts가 필수다.

2️. Next.js 앱 외부의 클라이언트가 호출해야 할 때

  • 모바일 앱

  • 별도의 어드민 프론트엔드

  • 다른 레포의 웹 애플리케이션

즉, HTTP API로서 명확한 인터페이스가 필요할 때다.

3️. URL 단위의 제어가 필요할 때

  • HTTP status code 제어 (401 / 403 / 429)

  • header 제어

  • redirect 처리

  • 명시적인 캐싱 규칙

이런 경우는 Server Action이나 Server Component로는 표현하기 어렵다.


Server Component

Server Component는 페이지를 렌더링하기 위해 필요한 데이터를 서버에서 조회하고, 그 결과를 바로 JSX로 그리는 역할을 한다.

Server Component가 담당하는 것들

  • 목록 조회

  • 상세 조회

  • 초기 화면 데이터

  • 화면 단위 데이터 조합 (BFF 역할)

이런 로직은 Server Action이 아니라 Server Component의 책임이다.


Server Action

Server Action은 UI 이벤트를 서버에서 처리하기 위한 장치다.

Server Action이 잘 어울리는 경우

  • 버튼 클릭

  • form submit

  • 생성 / 수정 / 삭제

  • 쿠키 / 세션 기반 인증

  • DB 직접 접근이 필요한 변경 작업

Server Action은 API라기 보다는 UI에서 발생한 행동을 서버로 전달하는 통로에 가깝다.

외부에서 이 로직을 URL로 직접 호출할 이유가 없다면 굳이 route.ts를 만들 필요는 없다.


내가 정리해본 기준

여러 번 헷갈리다 보니,

기준을 한 번에 정리하려 하기보다는 단계를 나눠서 생각하는 편이 훨씬 편했다.

그래서 이렇게 두 가지 질문으로 정리해봤다.

1️. 이 로직은 URL로 직접 호출될 필요가 있을까?

  • 있다route.ts
  • 없다 → Server Component 또는 Server Action

2. URL로 노출되지 않는다면, 이 로직은 무엇을 하고 있을까?

  • 페이지를 그리기 위한 조회라면Server Component
  • UI 이벤트로 상태를 변경한다면Server Action

우리 프로젝트에 적용해보면

우리 프로젝트는 별도의 백엔드 서버 없이 Next.js + Supabase로 구성되어 있다.

이 구조에서는 대부분의 서버 로직을 굳이 URL로 노출할 필요가 없다.

예를 들어, 예전에 만들었던 아래 같은 API들은

  • POST /api/diary/create
  • POST /api/diary/update
  • POST /api/diary/delete

다시 생각해보면 공통점이 꽤 분명하다.

  • 사용자 본인 데이터만 다룬다
  • 내부 UI 흐름에서만 호출된다
  • 버튼 클릭이나 폼 제출로 발생한다
  • DB에 직접 접근하는 변경 작업이다

이런 특징을 놓고 보면 이 로직들은 API라기보다는 UI 이벤트 처리에 가깝다.

그래서 route.ts로 분리하기보다는 Server Action으로 처리하는 쪽이 훨씬 자연스럽다.

features/diary/actions/createDiary.ts    // Server Action (WRITE)
features/diary/actions/updateDiary.ts    // Server Action (WRITE)

features/diary/queries/getDiaryList.ts   // Server Component 전용 (READ)
features/tag/queries/getTags.ts           // Server Component 전용 (READ)

또한 이 조회 로직들이 특정 페이지에서만 사용된다면,

굳이 분리하지 않고 Server Component 안에서 바로 조회해도 충분하다.


정리

Next.js App Router를 사용하면서

프론트엔드와 서버를 명확히 나누던 예전 방식이 더 이상 잘 맞지 않는다는 걸 느꼈다.

API를 먼저 설계하기보다, 이 로직이 어디에 속하는지, 어떤 역할을 맡아야 하는지부터 정하는 것 이 App Router를 제대로 활용하는 방법이라고 느꼈다.