icon안동민 개발노트

서버리스 함수 활용


 Next.js App Router 환경에서 서버리스 함수를 활용하는 것은 확장성 있고 효율적인 백엔드 로직을 구현하는 강력한 방법입니다.

 이 절에서는 서버리스 함수의 개념, 장점, 그리고 Next.js 애플리케이션에서의 구현 방법에 대해 알아보겠습니다.

서버리스 아키텍처의 장점

  1. 자동 확장성
  2. 비용 효율성 (사용한 만큼만 지불)
  3. 인프라 관리 부담 감소
  4. 빠른 배포 및 업데이트

API 라우트를 서버리스 함수로 배포

 Next.js App Router에서 API 라우트는 app/api 디렉토리 내에 route.js 파일로 정의됩니다.

 이 파일들은 Vercel이나 다른 서버리스 플랫폼에 배포될 때 자동으로 서버리스 함수로 변환됩니다.

 예시

app/api/hello/route.js
import { NextResponse } from 'next/server';
 
export async function GET(request) {
  return NextResponse.json({ message: 'Hello, Serverless World!' });
}

 이 API 라우트는 배포 시 자동으로 서버리스 함수로 변환됩니다.

환경 변수 관리

 서버리스 환경에서 환경 변수를 관리하는 방법

  1. .env.local 파일 사용 (개발 환경)
  2. Vercel 대시보드에서 환경 변수 설정 (프로덕션 환경)
app/api/hello/route.js
import { NextResponse } from 'next/server';
import { connectToDatabase } from '@/lib/db';
 
export async function GET() {
  const db = await connectToDatabase(process.env.MONGODB_URI);
  // 데이터베이스 연산 수행
  return NextResponse.json({ success: true });
}

콜드 스타트 최소화 전략

  1. 함수 크기 최소화 : 필요한 의존성만 포함
  2. 연결 풀링 : 데이터베이스 연결 재사용
  3. 함수 미리 워밍업 : 정기적으로 함수 호출

 예시

lib/db.js
import { MongoClient } from 'mongodb';
 
let cachedClient = null;
 
export async function connectToDatabase() {
  if (cachedClient) {
    return cachedClient;
  }
 
  const client = await MongoClient.connect(process.env.MONGODB_URI);
  cachedClient = client;
  return client;
}

서버리스 함수와 데이터베이스 연동

 서버리스 함수에서 데이터베이스 연결 예시

app/api/users/route.js
import { NextResponse } from 'next/server';
import { connectToDatabase } from '@/lib/db';
 
export async function GET() {
  const client = await connectToDatabase();
  const db = client.db('my-database');
  const users = await db.collection('users').find().toArray();
  return NextResponse.json(users);
}
 
export async function POST(request) {
  const client = await connectToDatabase();
  const db = client.db('my-database');
  const { name, email } = await request.json();
  const result = await db.collection('users').insertOne({ name, email });
  return NextResponse.json(result);
}

Vercel과 AWS Lambda 비교

 1. Vercel

  • Next.js에 최적화
  • 간편한 배포 프로세스
  • 자동 HTTPS, CDN 설정

 2. AWS Lambda

  • 더 많은 커스터마이징 옵션
  • 다양한 AWS 서비스와의 통합
  • 복잡한 설정 필요

11장 API 라우트와의 연관성

 16장의 서버리스 함수 활용은 11장에서 다룬 API 라우트와 직접적으로 연관됩니다.

 App Router의 API 라우트 (app/api 디렉토리의 route.js 파일들)는 서버리스 환경에 배포될 때 자동으로 개별 서버리스 함수로 변환됩니다.

 이를 통해 11장에서 구현한 API 로직을 그대로 서버리스 아키텍처에서 활용할 수 있습니다.

실습 : 이메일 전송 서버리스 함수

 이메일 전송 기능을 수행하는 서버리스 함수를 구현하고 Next.js 애플리케이션과 통합해보겠습니다.

  1. 필요한 패키지 설치
npm install nodemailer
  1. 이메일 전송 API 라우트 구현
app/api/send-email/route.js
import { NextResponse } from 'next/server';
import nodemailer from 'nodemailer';
 
export async function POST(request) {
  const { to, subject, text } = await request.json();
 
  const transporter = nodemailer.createTransport({
    host: process.env.SMTP_HOST,
    port: process.env.SMTP_PORT,
    auth: {
      user: process.env.SMTP_USER,
      pass: process.env.SMTP_PASS,
    },
  });
 
  try {
    await transporter.sendMail({
      from: process.env.FROM_EMAIL,
      to,
      subject,
      text,
    });
 
    return NextResponse.json({ success: true });
  } catch (error) {
    console.error('Email send error:', error);
    return NextResponse.json({ error: 'Failed to send email' }, { status: 500 });
  }
}
  1. 클라이언트 측 컴포넌트에서 이메일 전송 함수 호출
app/components/EmailForm.js
'use client'
 
import { useState } from 'react';
 
export default function EmailForm() {
  const [email, setEmail] = useState('');
  const [subject, setSubject] = useState('');
  const [message, setMessage] = useState('');
 
  const handleSubmit = async (e) => {
    e.preventDefault();
    try {
      const response = await fetch('/api/send-email', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ to: email, subject, text: message }),
      });
      const data = await response.json();
      if (data.success) {
        alert('Email sent successfully!');
      } else {
        throw new Error(data.error);
      }
    } catch (error) {
      alert('Failed to send email: ' + error.message);
    }
  };
 
  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Recipient Email"
        required
      />
      <input
        type="text"
        value={subject}
        onChange={(e) => setSubject(e.target.value)}
        placeholder="Subject"
        required
      />
      <textarea
        value={message}
        onChange={(e) => setMessage(e.target.value)}
        placeholder="Message"
        required
      />
      <button type="submit">Send Email</button>
    </form>
  );
}
  1. 환경 변수 설정 (.env.local 및 Vercel 대시보드)
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USER=your-username
SMTP_PASS=your-password
[email protected]
  1. 이메일 폼 컴포넌트를 페이지에 추가
app/page.js
import EmailForm from './components/EmailForm';
 
export default function Home() {
  return (
    <main>
      <h1>Send Email</h1>
      <EmailForm />
    </main>
  );
}

 이 실습을 통해 서버리스 함수를 사용하여 이메일 전송 기능을 구현하고, Next.js App Router 애플리케이션과 통합하는 과정을 경험할 수 있습니다.

 실습의 경우처럼 Next.js에서는 서버리스 함수를 활용하는 것으로 확장성 있고 유지보수가 쉬운 백엔드 로직을 구현할 수 있습니다.

 이로써 풀스택 프레임워크인 Next.js에서는 API 라우트 기능과 결합하여 프론트엔드와 백엔드를 긴밀하게 통합해볼 수 있었습니다.