12.serverless

Episode 12: "클라우드 네이티브: 서버리스의 등장"

서버를 관리하지 않고 코드만 배포하는 꿈


프롤로그: 서버 관리의 악몽

2014년, 당신은 간단한 웹 애플리케이션을 하나 만들었습니다. 하루에 방문자 10명 정도 오는 작은 서비스였죠. 하지만 서버 관리는 간단하지 않았습니다.

bash
# 2014년, 간단한 앱을 배포하기 위해 필요한 것들

1. EC2 인스턴스 생성 및 설정
   - 인스턴스 타입 선택 (t2.micro? t2.small?)
   - 키페어 생성, 보안 그룹 설정
   - 월 비용: $10-20 (거의 안 쓰는데도)

2. 서버 설정
   $ ssh ec2-user@your-instance.com
   $ sudo apt-get update && sudo apt-get upgrade
   $ sudo apt-get install nginx nodejs npm
   $ sudo systemctl enable nginx

3. 애플리케이션 배포
   $ git clone your-repo
   $ npm install
   $ pm2 start app.js
   $ sudo nginx -s reload

4. 모니터링 설정
   - CloudWatch 알람 설정
   - 로그 수집 설정
   - 디스크 용량 체크

5. 보안 패치
   - 매달 OS 업데이트
   - 취약점 점검
   - SSL 인증서 갱신

6. 새벽 3시
   "⚠️ High CPU Usage Alert!"
   (방문자는 여전히 10명...)
전통적인 서버 관리의 문제들

고정 비용:

  • 트래픽이 0이어도 서버는 24/7 가동
  • 사용하지 않는 시간의 비용도 지불
  • 최소 단위가 1대의 서버

과잉 프로비저닝:

  • 트래픽 급증을 대비해 여유 용량 확보
  • 대부분의 시간에는 낭비
  • 비용 효율 10-20%

운영 부담:

  • OS 업데이트, 보안 패치
  • 스케일링 설정 및 관리
  • 장애 대응 24/7

복잡한 인프라:

  • 로드 밸런서, 오토스케일링 그룹
  • VPC, 서브넷, 보안 그룹
  • 학습 곡선이 가파름

개발자들은 생각했습니다:

"나는 그냥 코드만 작성하고 싶은데, 왜 서버 관리자가 되어야 하지?"

2014년 11월, Amazon은 충격적인 발표를 합니다. AWS Lambda.

"더 이상 서버를 관리할 필요가 없습니다. 코드를 업로드하기만 하면 됩니다. 사용한 만큼만 지불하세요."

Werner VogelsAWS CTO, re:Invent 2014

서버리스 시대가 시작되었습니다.

이 글에서 다룰 내용
  • AWS Lambda의 충격적 등장
  • "NoOps"의 유토피아와 현실
  • JAMstack: 정적 사이트의 화려한 부활
  • Edge Computing: CDN에서 컴퓨팅까지

Chapter 1: AWS Lambda의 충격적 등장

2014년, 패러다임의 전환

AWS Lambda는 완전히 새로운 컴퓨팅 모델을 제시했습니다:

javascript
// 이것이 전부입니다
exports.handler = async (event) => {
  return {
    statusCode: 200,
    body: JSON.stringify({ message: 'Hello from Lambda!' })
  };
};

// 배포하면 끝
// 서버? 없음
// 설정? 최소한
// 비용? 호출당 과금

Lambda의 혁명적인 특징

서버리스의 핵심 개념

서버 관리 제로:

  • EC2 인스턴스 없음
  • OS 업데이트 없음
  • 인프라 설정 없음
  • 그냥 코드만

완벽한 종량제:

  • 요청당 과금
  • 실행 시간만큼만
  • 트래픽 0 = 비용 0
  • 100ms 단위 과금

자동 스케일링:

  • 요청 1개든 1백만 개든
  • 자동으로 확장
  • 설정 필요 없음
  • 무한대로 확장 가능

이벤트 드리븐:

  • HTTP 요청
  • S3 파일 업로드
  • DynamoDB 변경
  • 일정 (cron)

Lambda 사용 예제: 이미지 썸네일 생성

javascript
// 기존 방식: EC2 서버에서 실행
// - 서버 구축 및 유지보수 필요
// - 24/7 비용 발생
// - 스케일링 복잡

// Lambda 방식: 이벤트에 반응
const AWS = require('aws-sdk');
const sharp = require('sharp');
const s3 = new AWS.S3();

exports.handler = async (event) => {
  // S3에 이미지 업로드되면 자동 실행
  const bucket = event.Records[0].s3.bucket.name;
  const key = event.Records[0].s3.object.key;
  
  // 이미지 다운로드
  const image = await s3.getObject({ Bucket: bucket, Key: key }).promise();
  
  // 썸네일 생성
  const thumbnail = await sharp(image.Body)
    .resize(200, 200)
    .toBuffer();
  
  // 썸네일 저장
  await s3.putObject({
    Bucket: bucket,
    Key: `thumbnails/${key}`,
    Body: thumbnail
  }).promise();
  
  return { statusCode: 200, body: 'Thumbnail created' };
};

// 배포 후:
// - 이미지 업로드할 때만 실행
// - 자동 스케일링 (동시에 1000개 업로드해도 OK)
// - 비용: 실행한 만큼만
비용 비교

EC2 기반 (기존):

  • t2.small 인스턴스: $17/월
  • 24/7 가동
  • 월 1000개 이미지 처리해도 $17
  • 사용률 1%라도 $17

Lambda 기반:

  • 월 1000개 처리: $0.20
  • 사용하지 않으면: $0
  • 트래픽 급증해도 자동 대응
  • 85배 저렴

서버리스의 핵심 개념

1

FaaS (Function as a Service)

함수 단위로 코드 실행. 서버 없음, 인프라 없음

2

이벤트 드리븐

이벤트가 발생할 때만 실행. 유휴 시간 없음

3

무상태 (Stateless)

각 실행은 독립적. 상태는 외부 저장소에

4

관리형 서비스

스케일링, 가용성, 보안 등 모두 플랫폼이 관리


Chapter 2: "NoOps"의 유토피아와 현실

NoOps의 꿈

서버리스는 "NoOps"(No Operations)의 꿈을 약속했습니다:

typescript
// 개발자가 원하는 이상향

// 1. 코드만 작성
async function processPayment(userId: string, amount: number) {
  // 비즈니스 로직에만 집중
  const user = await db.users.findById(userId);
  const charge = await stripe.charge(user.card, amount);
  await db.payments.create({ userId, amount, chargeId: charge.id });
  return { success: true };
}

// 2. 배포
// $ serverless deploy

// 3. 끝!
// - 인프라? 신경 쓸 필요 없음
// - 스케일링? 자동
// - 모니터링? 기본 제공
// - 보안? 관리형

NoOps가 가능하게 한 것들

개발자 생산성 혁명

개발 속도:

  • 인프라 고민 시간: 0
  • 배포까지: 분 단위
  • MVP 출시: 며칠

팀 구성:

  • DevOps 엔지니어 불필요 (초기)
  • 개발자 1-2명으로 시작 가능
  • 인건비 절감

집중:

  • 비즈니스 로직에 집중
  • 고객 가치 창출에 집중
  • 차별화에 집중

하지만 현실은...

서버리스를 도입한 지 6개월 후:

javascript
// Lambda 함수가 점점 늘어나고...
// ├─ user-create.js
// ├─ user-update.js
// ├─ user-delete.js
// ├─ payment-process.js
// ├─ payment-refund.js
// ├─ email-send.js
// ├─ notification-push.js
// ├─ image-upload.js
// ├─ image-process.js
// ├─ analytics-track.js
// ... (50개 더)

// 문제 발생:
// "어? payment-process에서 에러가 났는데..."
// "어떤 함수가 어떤 함수를 호출하지?"
// "로그가 50개 함수에 흩어져 있어..."
// "디버깅이 왜 이렇게 어려워?"
// "함수 간 의존성을 어떻게 관리하지?"
// "Cold Start로 첫 요청이 3초 걸려..."
서버리스의 현실적인 문제들

복잡성의 이동:

  • 서버 관리는 사라짐
  • 하지만 분산 시스템 복잡성은 증가
  • 함수 간 오케스트레이션 필요

Cold Start:

  • 첫 요청은 느림 (수 초)
  • Node.js: 100-500ms
  • Java: 1-3초
  • 사용자 경험 저하 가능

디버깅의 어려움:

  • 로컬 실행이 어려움
  • 로그가 분산됨
  • 추적(tracing) 복잡

벤더 종속:

  • AWS Lambda 코드는 GCP에서 안 돌아감
  • API가 플랫폼마다 다름
  • 이전 비용 높음

비용 예측 어려움:

  • 트래픽 급증 시 비용 폭탄 가능
  • 잘못된 코드로 무한 루프 = 파산
  • 모니터링 필수

서버리스 코드 패턴의 변화

전통적인 서버와 서버리스는 코드 작성 방식이 다릅니다:

javascript
// 전통적인 Express.js 서버
// - 상태 유지 가능 (메모리 캐시)
// - 연결 재사용 (DB 커넥션 풀)
// - 긴 실행 시간 OK

const express = require('express');
const app = express();

// 앱 시작 시 한 번만 초기화
const dbPool = createConnectionPool({
  max: 10,
  min: 2
});

// 메모리 캐시
const cache = new Map();

app.get('/users/:id', async (req, res) => {
  // 캐시 확인
  if (cache.has(req.params.id)) {
    return res.json(cache.get(req.params.id));
  }
  
  // DB 조회 (커넥션 재사용)
  const user = await dbPool.query('SELECT * FROM users WHERE id = ?', [req.params.id]);
  cache.set(req.params.id, user);
  
  res.json(user);
});

app.listen(3000);
// 서버는 계속 실행됨
javascript
// Lambda (서버리스)
// - 무상태 (stateless)
// - 커넥션 재사용 제한적
// - 실행 시간 제한 (15분)

const AWS = require('aws-sdk');
const dynamodb = new AWS.DynamoDB.DocumentClient();

// 전역 변수는 제한적으로만 재사용됨
// (Lambda 컨테이너가 재사용될 때만)

exports.handler = async (event) => {
  const userId = event.pathParameters.id;
  
  // 캐시 불가능 (무상태)
  // 매번 DB 조회
  const result = await dynamodb.get({
    TableName: 'Users',
    Key: { id: userId }
  }).promise();
  
  return {
    statusCode: 200,
    body: JSON.stringify(result.Item)
  };
};

// 각 요청마다 독립적으로 실행
// 실행 후 컨테이너는 잠들거나 종료됨

서버리스 코드 작성 패턴

새로운 사고방식

상태 관리:

  • 메모리 캐시 X → Redis/DynamoDB 사용
  • 세션 스토리지 X → JWT 또는 외부 저장소
  • 전역 변수 신뢰 X

커넥션 관리:

  • DB 커넥션 풀 제한적
  • HTTP 커넥션 재사용 어려움
  • → Connection Proxy 사용 (RDS Proxy)

실행 시간:

  • 긴 작업 X
  • 최대 15분 (Lambda)
  • → 작업 분할 또는 Step Functions

에러 처리:

  • Retry 로직 필수
  • 멱등성(idempotency) 보장 필요
  • Dead Letter Queue 설정

서버리스가 적합한 경우 vs 부적합한 경우

typescript
// ✅ 서버리스가 적합한 경우

// 1. 이벤트 기반 작업
// - 이미지 처리, 파일 변환
// - 웹훅 처리
// - 예약된 작업 (cron)

// 2. 간헐적 트래픽
// - 하루 몇 번만 호출되는 API
// - 주기적인 데이터 동기화
// - 백오피스 도구

// 3. 빠른 프로토타입
// - MVP 개발
// - PoC (Proof of Concept)
// - 해커톤

// 4. 마이크로서비스
// - 작고 독립적인 서비스
// - 느슨한 결합
// - 명확한 책임

// ❌ 서버리스가 부적합한 경우

// 1. 웹소켓 / 긴 연결
// - 실시간 채팅
// - 게임 서버
// - 스트리밍

// 2. 고정된 높은 트래픽
// - 24/7 높은 부하
// - 예측 가능한 트래픽
// - → EC2가 더 저렴할 수 있음

// 3. 상태가 중요한 애플리케이션
// - 인메모리 캐시가 필수
// - 복잡한 세션 관리
// - 커넥션 풀 의존

// 4. 레이턴시가 중요한 경우
// - Cold Start 허용 불가
// - 밀리초 단위 응답 필요
// - 게임, 금융 거래 등

"서버리스는 은탄환이 아닙니다. 특정 사용 사례에 매우 적합하지만, 모든 문제의 해결책은 아닙니다."

Martin Fowler

Chapter 3: JAMstack - 정적 사이트의 화려한 부활

2015년, Netlify의 새로운 비전

서버리스가 백엔드를 바꾸는 동안, JAMstack은 프론트엔드를 바꾸고 있었습니다.

JAMstack이란?

JavaScript + APIs + Markup

핵심 아이디어:

  • 정적 파일을 CDN에 배포
  • 동적 기능은 API로 (서버리스!)
  • 빌드 타임에 HTML 생성
bash
# 전통적인 워드프레스 블로그 (2010년)

User → Nginx → PHP → MySQL → HTML 생성 → Response
# 매 요청마다 서버에서 HTML 생성
# 느림, 비쌈, 스케일링 어려움

# JAMstack 블로그 (2020년)

Build Time:
  Markdown → Static Site Generator → HTML/CSS/JS
  
Runtime:
  User → CDN → HTML (즉시 전송!)
  Comments? → API (서버리스)
  Search? → API (서버리스)
  
# 빠름, 저렴, 스케일링 자동

JAMstack의 진화

1

Jekyll (2008)

GitHub Pages의 정적 사이트 생성기. Ruby 기반

2

Netlify 설립 (2014)

JAMstack 용어 만들고 플랫폼 제공

3

Gatsby (2015)

React 기반 정적 사이트 생성기. GraphQL 통합

4

Next.js SSG (2020)

Static Site Generation 지원. 하이브리드 렌더링

5

Vercel의 부상

Next.js를 만든 Vercel이 JAMstack 플랫폼의 강자로

Vercel: 개발자 경험의 정점

Vercel은 서버리스와 JAMstack을 완벽하게 결합했습니다:

typescript
// Next.js with Vercel
// pages/api/users.ts - API Route (서버리스!)

export default async function handler(req, res) {
  // 이 코드는 Vercel의 서버리스 함수로 실행됨
  const users = await db.users.findMany();
  res.json(users);
}

// pages/index.tsx - 정적 생성
export async function getStaticProps() {
  // 빌드 타임에 한 번 실행
  const posts = await fetchPosts();
  return { props: { posts }, revalidate: 60 };
}

export default function Home({ posts }) {
  return (
    <div>
      {posts.map(post => (
        <Article key={post.id} {...post} />
      ))}
    </div>
  );
}

// 배포
// $ git push
// → Vercel이 자동 배포
// → 정적 파일은 전 세계 CDN에
// → API는 서버리스 함수로
// → 끝!

Vercel의 혁신

개발자 경험 혁명

제로 설정:

  • git push만 하면 배포 완료
  • 프레임워크 자동 감지 (Next.js, Svelte, etc)
  • 환경 변수만 설정하면 됨

자동 최적화:

  • 이미지 최적화 자동
  • 코드 스플리팅 자동
  • CDN 캐싱 자동

프리뷰 배포:

  • PR마다 자동 프리뷰 환경
  • 고유 URL로 테스트 가능
  • 팀 리뷰 간편

Edge Functions:

  • CDN 엣지에서 코드 실행
  • 낮은 레이턴시
  • 개인화 가능

분석 내장:

  • Core Web Vitals 추적
  • 성능 모니터링
  • 사용자 경험 개선
bash
# Vercel 배포 예제

# 1. 프로젝트 생성
$ npx create-next-app my-app
$ cd my-app

# 2. Git 연결
$ git init
$ git add .
$ git commit -m "Initial commit"
$ git remote add origin https://github.com/username/my-app.git
$ git push -u origin main

# 3. Vercel 연결
$ vercel login
$ vercel

# 끝!
# - 자동 HTTPS
# - 전 세계 CDN
# - 서버리스 API
# - CI/CD 자동
# - 도메인 연결 간단

# 이후 배포
$ git push
# → 자동으로 프로덕션 배포

# PR 생성 시
# → 자동으로 프리뷰 배포
# → 고유 URL 생성

JAMstack의 장점과 한계

JAMstack의 강력한 장점

성능:

  • CDN에서 바로 전송
  • TTFB (Time To First Byte) < 100ms
  • No server processing

보안:

  • 공격 표면 최소화
  • 서버 해킹 불가 (서버 없음)
  • DDoS 내성 강함

비용:

  • 정적 호스팅 거의 무료
  • 트래픽 급증해도 문제없음
  • Netlify/Vercel 무료 티어 넉넉

개발자 경험:

  • Git 기반 워크플로우
  • 로컬 개발 쉬움
  • 롤백 간단

스케일링:

  • 무한대로 확장 가능
  • 설정 필요 없음
  • 자동으로 전 세계 배포
JAMstack의 한계

빌드 시간:

  • 페이지 많으면 빌드 느림
  • 수천 페이지면 수십 분 소요
  • Incremental Static Regeneration으로 완화

실시간성:

  • 빌드 타임에 생성
  • 실시간 데이터 어려움
  • → ISR 또는 클라이언트 사이드 페칭

복잡한 동적 기능:

  • 개인화 어려움
  • 사용자별 컨텐츠 제한적
  • → Edge Functions로 해결

검색/필터:

  • 클라이언트 사이드 또는 외부 서비스 필요
  • Algolia, ElasticSearch 등 의존

Chapter 4: Edge Computing - CDN에서 컴퓨팅까지

CDN의 진화

CDN(Content Delivery Network)은 원래 정적 파일 캐싱만 했습니다:

bash
# 전통적인 CDN (2000년대)

User (Seoul) → CDN (Seoul) → Origin (US West)
                 ↓
               Cache
                 ↓
User (Seoul) → CDN (Seoul) [Cache Hit!]

# 정적 파일만: HTML, CSS, JS, 이미지
# 동적 컨텐츠는 Origin까지 가야 함

하지만 서버리스 시대에 CDN은 컴퓨팅 플랫폼이 되었습니다:

bash
# Edge Computing (2020년대)

User (Seoul) → CDN Edge (Seoul)
                    ↓
                [Code 실행!]
                    ↓
                 Response

# Origin 서버까지 안 가도 됨
# 레이턴시 최소화
# 개인화 가능

Cloudflare Workers: 엣지에서의 서버리스

Cloudflare Workers는 전 세계 200+ 도시의 엣지에서 코드를 실행합니다:

javascript
// Cloudflare Worker 예제
// 전 세계 엣지에서 실행됨

addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request))
});

async function handleRequest(request) {
  const url = new URL(request.url);
  
  // A/B 테스트
  const variant = Math.random() < 0.5 ? 'A' : 'B';
  
  // 지역별 개인화
  const country = request.cf.country; // 'KR', 'US', etc
  const language = country === 'KR' ? 'ko' : 'en';
  
  // HTML 수정
  const response = await fetch(url);
  let html = await response.text();
  html = html.replace('{{language}}', language);
  html = html.replace('{{variant}}', variant);
  
  return new Response(html, {
    headers: { 'Content-Type': 'text/html' }
  });
}

// 전 세계 어디서든 < 50ms 응답

Edge Computing의 특징

레이턴시의 최소화

지리적 분산:

  • 전 세계 수백 개 위치
  • 사용자와 가까운 곳에서 실행
  • 레이턴시 최소화

빠른 Cold Start:

  • V8 Isolates 사용
  • Cold Start < 5ms
  • Lambda보다 100배 빠름

무한 확장:

  • 자동 스케일링
  • DDoS 내성 강함
  • 글로벌 트래픽 처리

사용 사례:

  • 인증/인가
  • 리다이렉션
  • A/B 테스트
  • 개인화
  • 봇 차단
  • API 라우팅

Vercel Edge Functions

Vercel도 Edge Functions를 제공합니다:

typescript
// app/api/edge/route.ts
import { NextRequest, NextResponse } from 'next/server';

export const runtime = 'edge'; // Edge에서 실행!

export async function GET(request: NextRequest) {
  const geo = request.geo;
  
  // 사용자 위치 기반 응답
  const message = geo?.country === 'KR' 
    ? '안녕하세요!' 
    : 'Hello!';
  
  return NextResponse.json({
    message,
    location: geo?.city,
    country: geo?.country,
    latency: 'sub-50ms'
  });
}

// 배포하면 자동으로 전 세계 엣지에 배포됨
1

Edge Middleware

요청 전처리, 인증, 리다이렉션

2

Edge API Routes

낮은 레이턴시의 API 엔드포인트

3

Edge Rendering

SSR을 엣지에서 (실험적)

Edge vs Lambda: 언제 무엇을 쓸까?

typescript
// ⚡ Edge Functions (Cloudflare, Vercel Edge)
// - 실행 시간: < 50ms
// - 메모리: 128MB
// - 언어: JavaScript/TypeScript, Rust, C++
// - Cold Start: < 5ms
// - 비용: 매우 저렴

// 적합한 경우:
// - 간단한 로직
// - 낮은 레이턴시 필수
// - 지역별 개인화
// - 인증/인가
// - 리다이렉션

async function edgeHandler(request: Request) {
  // 가볍고 빠른 작업만
  const auth = await verifyToken(request.headers.get('authorization'));
  if (!auth) {
    return new Response('Unauthorized', { status: 401 });
  }
  return fetch(request);
}

// 🚀 Lambda Functions (AWS, GCP, Azure)
// - 실행 시간: 최대 15분
// - 메모리: 최대 10GB
// - 언어: 거의 모든 언어
// - Cold Start: 100ms-3s
// - 비용: 상대적으로 비쌈

// 적합한 경우:
// - 복잡한 로직
// - 긴 실행 시간
// - 많은 메모리
// - DB 연결
// - 파일 처리

async function lambdaHandler(event) {
  // 복잡하고 무거운 작업
  const image = await downloadImage(event.imageUrl);
  const processed = await heavyImageProcessing(image);
  await uploadToS3(processed);
  await updateDatabase(event.id, processed.url);
  return { statusCode: 200 };
}
하이브리드 아키텍처

현대적인 애플리케이션은 둘 다 사용합니다:

User Request
    ↓
Edge Function (인증, 라우팅)
    ↓
필요시 → Lambda (복잡한 처리)
    ↓
Database

최적의 성능과 비용 효율을 위해 적재적소에 배치!

Edge에서 가능한 새로운 패턴들

typescript
// 1. 점진적 마이그레이션
// 레거시 시스템을 점진적으로 교체

export async function middleware(request: NextRequest) {
  const url = new URL(request.url);
  
  // 새로운 페이지는 Next.js로
  if (url.pathname.startsWith('/new')) {
    return NextResponse.next();
  }
  
  // 기존 페이지는 레거시 서버로
  return NextResponse.rewrite('https://legacy.example.com' + url.pathname);
}

// 2. Feature Flags
// 사용자별로 기능 토글

export async function middleware(request: NextRequest) {
  const user = await getCurrentUser(request);
  const flags = await getFeatureFlags(user.id);
  
  // 응답 헤더에 플래그 추가
  const response = NextResponse.next();
  response.headers.set('x-features', JSON.stringify(flags));
  return response;
}

// 3. 지능형 캐싱
// 사용자 상태에 따라 캐시 전략 변경

export async function edgeHandler(request: Request) {
  const isPremium = await checkPremiumStatus(request);
  
  if (isPremium) {
    // 프리미엄 유저는 항상 최신 데이터
    return fetch(request, { cache: 'no-cache' });
  } else {
    // 무료 유저는 캐시된 데이터
    return fetch(request, { cache: 'force-cache' });
  }
}

// 4. 봇 차단 및 보안
// 엣지에서 악의적인 요청 차단

export async function middleware(request: NextRequest) {
  const ip = request.ip;
  const userAgent = request.headers.get('user-agent');
  
  // 봇 감지
  if (isBot(userAgent) && !isGoodBot(userAgent)) {
    return new NextResponse('Forbidden', { status: 403 });
  }
  
  // Rate limiting
  const rateLimit = await checkRateLimit(ip);
  if (rateLimit.exceeded) {
    return new NextResponse('Too Many Requests', { 
      status: 429,
      headers: { 'Retry-After': '60' }
    });
  }
  
  return NextResponse.next();
}

에필로그: 서버리스가 바꾼 세상

2014년 → 2024년: 10년의 변화

AWS Lambda의 등장으로부터 10년, 서버리스는 주류가 되었습니다.

서버리스가 바꾼 것들

소프트웨어 개발의 민주화

스타트업:

  • Before: 최소 DevOps 1명 필요, 인프라 비용 월 $500+
  • After: 개발자 혼자 시작 가능, 초기 비용 거의 $0

개발 속도:

  • Before: MVP까지 2-3개월 (인프라 포함)
  • After: MVP까지 1-2주 (코드만 작성)

비용 구조:

  • Before: 고정 비용 (서버 24/7)
  • After: 변동 비용 (사용량에 비례)

스케일링:

  • Before: 수동, 예측 필요, 복잡
  • After: 자동, 무한대, 간단

팀 구성:

  • Before: 개발자 + DevOps + SRE
  • After: 개발자 (풀스택)

숫자로 보는 서버리스

서버리스 현황 (2024)

AWS Lambda:

  • 월 10조+ 요청 처리
  • 수백만 개발자
  • 200+ AWS 서비스와 통합

Vercel:

  • 월 100억+ 요청
  • 10만+ 팀
  • 평균 배포 시간 < 1분

Cloudflare Workers:

  • 일 30조+ 요청
  • 전 세계 200+ 도시
  • Edge Cold Start < 5ms

영향:

  • 개발자 생산성: 3-5배 향상
  • 인프라 비용: 40-60% 감소
  • 출시 속도: 5-10배 빠름

하지만 서버는 사라지지 않았다

서버리스는 **"서버가 없다"**는 뜻이 아닙니다. **"서버를 관리하지 않는다"**는 뜻입니다.

typescript
// 서버리스 애플리케이션도 서버 위에서 돌아감
// 단지 우리가 관리하지 않을 뿐

// Lambda 함수는 실제로는:
// - EC2 인스턴스에서 실행됨
// - Container로 격리됨
// - AWS가 모든 것을 관리함

// 우리는 이것만 신경 쓰면 됨:
export async function handler(event) {
  return { statusCode: 200, body: 'Hello' };
}

// 나머지는 AWS가:
// - 인스턴스 프로비저닝
// - 스케일링
// - 로드 밸런싱
// - 장애 복구
// - 모니터링
// 모두 자동

"서버리스는 서버가 없다는 뜻이 아닙니다. 개발자가 서버에 대해 생각할 필요가 없다는 뜻입니다."

Werner VogelsAWS CTO

서버리스의 미래

1

WebAssembly + Serverless

더 빠른 Cold Start, 더 많은 언어 지원

2

Serverless Containers

Fargate, Cloud Run 등 컨테이너 기반 서버리스

3

Edge-First 아키텍처

모든 것을 엣지에서

4

AI/ML과 통합

서버리스 GPU, 추론 엔드포인트

5

Database도 서버리스

Aurora Serverless, Neon, PlanetScale

완벽한 추상화는 없다

서버리스는 강력하지만 만능은 아닙니다:

typescript
// 서버리스가 숨기는 복잡성들

// 1. 분산 시스템의 복잡성
// - 네트워크 지연
// - 부분 실패
// - 일관성 문제

// 2. Cold Start
// - 첫 요청은 느림
// - 사용자 경험 영향

// 3. 벤더 종속
// - AWS Lambda → GCP로 이전 어려움
// - API 차이

// 4. 디버깅의 어려움
// - 로컬 재현 어려움
// - 분산 추적 필요

// 5. 비용 예측의 어려움
// - 트래픽 급증 시 비용 폭탄
// - 모니터링 필수
서버리스 채택 시 주의사항

다음과 같은 경우 신중하게:

  • 레이턴시가 매우 중요한 경우 (< 10ms)
  • 상태 관리가 복잡한 경우
  • 기존 레거시 시스템이 큰 경우
  • 팀이 분산 시스템에 익숙하지 않은 경우

시작 전 확인:

  • 비용 모델 이해
  • 제한사항 파악 (실행 시간, 메모리 등)
  • 모니터링 설정
  • Cold Start 전략

서버리스의 본질

결국 서버리스는 추상화의 진화입니다:

bash
# 추상화의 역사

1990s: Physical Servers
       "서버 랙에 설치하고 케이블 연결"
       
2000s: Virtual Machines (AWS EC2)
       "가상화로 빠른 프로비저닝"
       
2010s: Containers (Docker)
       "격리와 이식성"
       
2014~: Serverless (Lambda)
       "서버 추상화, 코드만 작성"
       
2020~: Edge Computing
       "지리적 분산, 레이턴시 최소화"
       
미래: ?
      "더 높은 수준의 추상화"

"Good abstractions allow people to build on top of them and create higher-level constructs."

Jeff Bezos

서버리스는 개발자가 비즈니스 로직에 집중할 수 있게 합니다. 인프라는 플랫폼이 관리합니다.

2014년, AWS Lambda는 **"서버를 관리하지 않고 코드만 배포한다"**는 꿈을 현실로 만들었습니다.

2024년, 그 꿈은 표준이 되었습니다.



다음 에피소드에서는 "마이크로서비스: 모놀리스의 해체"를 다룰 예정입니다. 거대한 시스템을 작은 조각으로 나누는 여정을 살펴보겠습니다.