icon
16장 : 고급 주제

서버리스 함수 활용

Next.js는 React 프레임워크를 넘어, 서버 사이드 렌더링(SSR)과 정적 사이트 생성(SSG) 기능을 제공하며, 동시에 서버리스 함수(Serverless Functions) 를 통해 백엔드 로직까지 통합할 수 있는 풀스택 프레임워크로 발전했습니다. 서버리스 함수는 Next.js 애플리케이션 내에서 API 엔드포인트나 백엔드 로직을 별도의 서버 없이 구현할 수 있게 해주며, Vercel과 같은 플랫폼에서 자동으로 배포되고 관리됩니다.

이 절에서는 서버리스 함수의 개념과 이점, Next.js에서 서버리스 함수를 구현하는 방식(API Routes, Server Actions, Edge Functions), 그리고 실제 애플리케이션에서 서버리스 함수를 효율적으로 활용하는 전략에 대해 상세히 알아보겠습니다.


서버리스 함수란 무엇이며 왜 중요한가요?

서버리스 함수 (Serverless Functions) 는 클라우드 공급자(AWS Lambda, Google Cloud Functions, Azure Functions 등)가 서버 인프라 관리를 전적으로 담당하고, 개발자는 코드만 작성하여 배포하는 컴퓨팅 모델입니다. 코드는 이벤트(예: HTTP 요청, 데이터베이스 변경)에 의해 트리거될 때만 실행되며, 사용량에 따라 비용이 청구됩니다.

서버리스 함수의 주요 이점

  • 인프라 관리 부담 감소: 서버를 프로비저닝, 패치, 스케일링할 필요가 없습니다. 클라우드 공급자가 모든 인프라를 관리합니다.
  • 자동 스케일링: 트래픽 증가에 따라 자동으로 함수 인스턴스가 확장(스케일 아웃)되어 대규모 트래픽도 안정적으로 처리할 수 있습니다.
  • 비용 효율성: 코드가 실행될 때만 비용이 발생합니다(Pay-per-execution 모델). 유휴 시간에는 비용이 발생하지 않아 효율적입니다.
  • 빠른 배포: 코드 변경 사항을 빠르게 배포하고 적용할 수 있습니다.
  • 개발 생산성 향상: 백엔드 인프라 걱정 없이 비즈니스 로직 구현에 집중할 수 있습니다.

Next.js는 이러한 서버리스 함수의 이점을 애플리케이션에 쉽게 통합할 수 있도록 지원합니다.


Next.js에서 서버리스 함수 구현하기

Next.js에서 서버리스 함수를 구현하는 주요 방법은 API Routes, Server Actions, 그리고 Edge Functions입니다.

API Routes

API Routes는 Next.js에서 가장 기본적인 서버리스 함수 구현 방식입니다. pages/api (Pages Router) 또는 app/api (App Router) 디렉토리에 파일을 생성하면 해당 파일이 API 엔드포인트로 자동 매핑됩니다.

기본적인 API Routes 예시 (App Router: app/api/hello/route.ts)

app/api/hello/route.ts
// app/api/hello/route.ts
// GET 요청을 처리하는 서버리스 함수 (API 라우트)

import { NextResponse } from 'next/server';

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url);
  const name = searchParams.get('name') || 'World';

  // JSON 응답 반환
  return NextResponse.json({ message: `Hello, ${name}!` });
}

// POST, PUT, DELETE 등 다른 HTTP 메서드도 동일한 방식으로 export 가능
export async function POST(request: Request) {
  const data = await request.json();
  return NextResponse.json({ received: data, status: 'success' }, { status: 200 });
}

특징

  • HTTP 요청(GET, POST 등)에 응답하는 RESTful API 엔드포인트를 쉽게 구축할 수 있습니다.
  • 클라이언트 측에서 fetch API 등을 사용하여 이 API 라우트를 호출할 수 있습니다.
  • Vercel에 배포 시 자동으로 서버리스 함수로 변환됩니다.
  • 데이터베이스 접근, 외부 API 호출, 인증 처리 등 다양한 백엔드 로직을 구현할 수 있습니다.

Server Actions (App Router Only)

Server Actions는 Next.js 13의 App Router에서 도입된 강력한 기능으로, 클라이언트 컴포넌트에서 직접 서버 코드를 호출할 수 있게 해줍니다. 이는 API 라우트를 명시적으로 정의할 필요 없이 데이터 변경(mutations)과 폼 처리 등을 간소화합니다.

Server Actions 예시 (app/add-todo/page.tsx 또는 app/add-todo/actions.ts)

app/add-todo/page.tsx
// app/add-todo/page.tsx (서버 컴포넌트)
import { addTodo } from './actions'; // Server Action 임포트

export default function AddTodoPage() {
  return (
    <form action={addTodo} style={{ margin: '50px', padding: '20px', border: '1px solid #ddd', borderRadius: '8px' }}>
      <input type="text" name="todo" placeholder="새 할 일" style={{ padding: '10px', marginRight: '10px' }} />
      <button type="submit" style={{ padding: '10px 15px', backgroundColor: '#007bff', color: 'white', border: 'none', borderRadius: '4px' }}>
        할 일 추가
      </button>
    </form>
  );
}
app/add-todo/actions.ts
// app/add-todo/actions.ts (Server Action 정의)
'use server'; // 이 파일의 모든 함수가 서버에서 실행됨을 명시

import { revalidatePath } from 'next/cache'; // 데이터 갱신을 위해 Next.js 캐시 유틸리티 임포트

export async function addTodo(formData: FormData) {
  const todo = formData.get('todo') as string;

  // 실제 데이터베이스에 할 일을 추가하는 로직 (예시)
  console.log(`서버에서 할 일 추가됨: ${todo}`);
  // await db.todos.create({ text: todo });

  // 특정 경로의 캐시를 무효화하여 최신 데이터를 가져오도록 강제
  revalidatePath('/todos'); // '/todos' 경로의 데이터 캐시 갱신
}

특징

  • 클라이언트와 서버 간의 데이터 직렬화 및 통신을 Next.js가 자동으로 처리하여 개발 복잡성을 줄입니다.
  • 폼 제출, 버튼 클릭 등 UI 상호작용에 직접적으로 반응하는 서버 로직을 작성하기에 이상적입니다.
  • Next.js 캐시 재검증 (revalidatePath, revalidateTag) 과 통합되어 데이터 변경 후 UI를 자동으로 업데이트할 수 있습니다.
  • Vercel에 배포 시 서버리스 함수로 변환됩니다.

Edge Functions

Edge Functions (Middleware 및 특정 API/Server Actions) 는 Vercel의 엣지 네트워크(CDN 노드)에서 실행되는 서버리스 함수입니다. 사용자의 요청에 가장 가까운 지점에서 실행되므로, 지연 시간을 최소화하고 매우 빠른 응답 속도를 제공합니다. 미들웨어(Middleware)와 같이 요청이 실제 서버에 도달하기 전에 로직을 실행하거나, 특정 API 라우트/Server Actions에서 사용될 수 있습니다.

Edge Middleware 예시 (middleware.ts in App Router root)

middleware.ts
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

// 이 미들웨어는 모든 요청에 적용됩니다.
export function middleware(request: NextRequest) {
  const authCookie = request.cookies.get('auth_token');

  // 인증 토큰이 없으면 로그인 페이지로 리다이렉트 (예시)
  if (!authCookie && request.nextUrl.pathname.startsWith('/dashboard')) {
    return NextResponse.redirect(new URL('/login', request.url));
  }

  // 요청 헤더에 사용자 정보 추가 (예시)
  const response = NextResponse.next();
  response.headers.set('X-User-Auth', authCookie ? 'true' : 'false');
  return response;
}

// 미들웨어가 실행될 경로를 설정 (선택 사항)
export const config = {
  matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'], // 특정 경로를 제외
};

API Route/Server Action을 Edge Functions로 실행 (runtime: 'edge')

특정 API 라우트나 Server Action을 엣지 함수로 실행하려면, 파일 상단에 export const runtime = 'edge';를 추가합니다.

app/api/edge-hello/route.ts
// app/api/edge-hello/route.ts (Edge API 라우트)
export const runtime = 'edge'; // 이 함수를 엣지 런타임에서 실행

import { NextResponse } from 'next/server';

export async function GET(request: Request) {
  return NextResponse.json({ message: 'Hello from the Edge!' });
}

특징

  • 글로벌 분산: 전 세계 엣지 노드에서 실행되어 사용자에게 최저 지연 시간 제공.
  • 제한된 Node.js API: V8 JavaScript 런타임 기반이므로 Node.js의 모든 내장 모듈(예: fs, path)을 사용할 수 없습니다. 가벼운 로직에 적합합니다.
  • 빠른 시작 시간: 콜드 스타트(cold start) 지연이 거의 없어 즉각적인 응답이 가능합니다.
  • 주요 사용 사례: 인증/인가 미들웨어, A/B 테스팅, URL 리다이렉트/리라이트, 지리 기반 콘텐츠 제공 등.

서버리스 함수 활용 전략 및 고려사항

  • 적절한 런타임 선택
    • API Routes/Server Actions (Node.js 런타임): 대부분의 백엔드 로직 (데이터베이스 접근, 복잡한 계산, 서드파티 API 연동)에 적합합니다.
    • Edge Functions: 지연 시간이 중요한 미들웨어, 인증/인가, A/B 테스팅, 지리 기반 라우팅 등 매우 빠르고 가벼운 로직에 사용합니다. fs, crypto 등 Node.js 내장 모듈 사용이 필요한 경우 Edge Functions는 적합하지 않습니다.
  • 환경 변수 관리: 민감한 정보(API 키, DB 연결 문자열)는 환경 변수로 관리하고, Vercel 대시보드나 .env 파일을 통해 안전하게 주입합니다.
  • 콜드 스타트(Cold Start) 이해: 서버리스 함수는 일정 시간 사용되지 않으면 "콜드" 상태가 됩니다. 첫 요청 시 컨테이너가 시작되어 약간의 지연(콜드 스타트 지연)이 발생할 수 있습니다. Edge Functions는 이 지연이 거의 없지만, Node.js 런타임 함수는 발생할 수 있습니다. 중요한 API에는 Warm-up 전략을 고려할 수 있습니다.
  • 상태 비저장(Stateless) 설계: 서버리스 함수는 상태를 유지하지 않는(stateless) 방식으로 설계해야 합니다. 사용자 세션 정보나 데이터는 데이터베이스, 캐시(Redis), 또는 외부 스토리지에 저장해야 합니다.
  • 로그 및 모니터링: Vercel 대시보드에서 서버리스 함수의 실행 로그와 성능 지표를 모니터링할 수 있습니다. 문제 발생 시 디버깅에 활용합니다.
  • 오류 처리: 서버리스 함수 내에서 발생하는 오류를 적절히 처리하고 로깅하여 안정성을 확보합니다.
  • 보안: API 라우트 및 Server Actions를 통해 노출되는 엔드포인트에 대해 인증, 인가, 입력 유효성 검사 등 적절한 보안 조치를 적용해야 합니다.

Next.js의 서버리스 함수 기능은 프론트엔드 개발자가 풀스택 애플리케이션을 더 쉽게 구축하고 배포할 수 있도록 지원합니다. API 라우트, Server Actions, Edge Functions의 특징을 이해하고 적절히 활용함으로써, 고성능의 확장 가능한 웹 애플리케이션을 효율적으로 개발할 수 있습니다.