SST 도입, Feat:Serverless

SST
Serverless 아키텍처는 물리적 서버나 인프라를 직접 관리할 필요 없이, 클라우드 제공자가 자동으로 확장, 관리, 모니터링하는 환경을 말합니다. 이를 통해 개발자는 코드와 비즈니스 로직에 집중할 수 있으며, 사용한 만큼 비용을 지불하는 pay-as-you-go 모델을 적용할 수 있습니다. AWS에서는 Lambda, API Gateway, DynamoDB, CloudFront 등 다양한 Serverless 서비스를 제공하고 있습니다.
CloudFront 글로벌 콘텐츠 배포 네트워크 (CDN)
- 특징 및 역할
- 전 세계 엣지 로케이션: CloudFront는 전 세계에 분포된 엣지 서버를 통해 사용자에게 정적, 동적 콘텐츠를 빠르게 전달합니다.
- 캐싱: 자주 요청되는 콘텐츠를 엣지에 캐싱하여 서버 부하를 줄이고, 응답 시간을 최소화합니다.
- 보안 기능: AWS WAF, SSL/TLS 암호화 등 다양한 보안 기능을 통해 콘텐츠를 안전하게 전송합니다.
- 활용 예시
- 웹사이트 정적 콘텐츠 배포: 이미지, CSS, JavaScript 파일 등의 정적 리소스를 전 세계 사용자에게 빠르게 제공할 수 있습니다.
- 동적 콘텐츠 최적화: Lambda@Edge와 결합해 사용자 요청에 따른 동적 콘텐츠를 처리할 수 있습니다.
- CloudFront는 전 세계에 분산된 인프라를 통해 빠른 콘텐츠 전송을 보장함으로써 사용자 경험을 대폭 개선시켜 줍니다.
Lambda 이벤트 기반 서버리스 컴퓨팅
- 특징 및 역할
- 이벤트 기반 실행: Lambda 함수는 이벤트(예: HTTP 요청, 데이터베이스 업데이트, 파일 업로드 등)에 반응하여 자동으로 실행됩니다.
- 무제한 확장성: 트래픽이 급증해도 자동으로 스케일 아웃되어 요청을 처리할 수 있습니다.
- 비용 효율적: 코드 실행 시간에 대해서만 비용을 지불하므로, 사용하지 않을 때는 비용이 발생하지 않습니다.
- 다양한 언어 지원: Node.js, Python, Java, C# 등 다양한 프로그래밍 언어를 지원합니다.
- 활용 예시
- API 백엔드: API Gateway와 연동해 RESTful API 백엔드를 구성할 때, Lambda 함수가 비즈니스 로직을 처리합니다.
- 데이터 처리 및 변환: DynamoDB 스트림, S3 이벤트 등 다양한 데이터 소스로부터 이벤트를 받아 처리할 수 있습니다.
- Lambda는 짧은 시간에 실행되는 함수형 컴퓨팅 환경으로, 유연하고 확장 가능한 애플리케이션 아키텍처를 구성하는 데 핵심적인 역할을 합니다.
Lambda는 짧은 시간에 실행되는 함수형 컴퓨팅 환경으로, 유연하고 확장 가능한 애플리케이션 아키텍처를 구성하는 데 핵심적인 역할을 합니다.
DynamoDB: 완전 관리형 NoSQL 데이터베이스
- 특징 및 역할
- 빠른 성능: 밀리초 단위의 응답 시간과 높은 처리량을 제공하여, 대규모 트래픽을 처리할 수 있습니다.
- 완전 관리형: 데이터베이스 서버 관리, 패치, 백업, 복구 등 인프라 운영 부담 없이 사용 가능합니다.
- 자동 스케일링: 데이터 및 요청량에 따라 자동으로 스케일 아웃되어 트래픽을 원활하게 처리합니다.
- 유연한 데이터 모델링: Key-Value와 Document 모델을 지원하여 다양한 애플리케이션 요구사항에 맞게 데이터 구조를 설계할 수 있습니다.
- 활용 예시
- 실시간 애플리케이션: 채팅 앱, 게임, IoT 데이터 수집 등 빠른 읽기/쓰기 성능이 필요한 애플리케이션에 적합합니다.
- 서버리스 애플리케이션 백엔드: Lambda 함수와 결합해 데이터 저장 및 조회를 수행하며, 복잡한 인프라 관리 없이 애플리케이션을 구축할 수 있습니다. DynamoDB는 높은 성능과 확장성을 갖춘 NoSQL 데이터베이스로, 서버리스 애플리케이션의 데이터 계층에서 매우 중요한 역할을 합니다.
최근 서버리스 아키텍처를 도입하는 기업과 개발자들이 늘어나면서, 보다 쉽고 효율적으로 AWS 리소스를 관리하고 배포할 수 있는 도구들이 각광받고 있습니다.
또한 프론트엔드 개발자인 저에게 EC2와 같은 서버를 직접 관리하는 것은 익숙치않고, 복잡하기 때문에 AWS Serverless 사용해보기로 했습니다. 하지만 Serveless라고 하더라도, Lambda, API Gateway, DynamoDB 등 여러 서비스를 직접 설정하는 것은 번거롭고 복잡하기 때문에 프레임워크에게 역할을 위임하는 것이 좋다고 생각했습니다.
그 중 SST (Serverless Stack) v2는 Next.js와 같은 프론트엔드 프레임워크뿐 아니라 Lambda, API Gateway, DynamoDB 등 AWS 리소스를 손쉽게 다룰 수 있도록 도와줍니다. 이번 포스팅에서는 SST v2를 활용하여 Next.js 서비스를 배포하고, Lambda, API Gateway, DynamoDB를 사용하는 방법과 그 장점, 그리고 Vercel 및 Serverless Framework와의 비교에 대해 자세히 알아보겠습니다.
Next.js 기반 프로젝트의 배포 서비스는 많이 있습니다. 대표적으로 Vercel, Netlify, Amplify, SST, Serverless 등이 있습니다.
대표적으로 SST와, Serverless Framework의 차이점을 설명 드리자면,
SST (Serverless Stack) 는 AWS에서 Serverless 애플리케이션을 쉽게 배포하고 관리할 수 있도록 도와주는 오픈소스 프레임워크입니다. 기존 Serverless Framework의 불편한 점을 개선하고, AWS CDK 기반으로 확장성이 뛰어난 인프라를 구축할 수 있도록 지원합니다. 해당 포스트에서는 Serverless와 SST의 차이점을 설명하고, SST의 주요 특징을 설명합니다.
Serverless Framework 는 여러 클라우드 플랫폼(AWS, Azure, Google Cloud 등)의 서버리스 서비스 배포를 지원하는 프레임워크 입니다. 개발자가 필요한 인프라를 하나씩 설정하는 것은 번거롭고 복잡하기 때문에 그 역할을 프레임워크에 위임한다고 보면 됩니다. Serverless Framework 는 YAML 파일로 설정을 정의하여 몇 줄의 코드로 AWS Lambda 함수, API Gateway를 연결하는 등의 환경을 손쉽게 구현할 수 있습니다. 하지만 Next.js은 13이상 버전을 더 이상 지원하지 않고 있습니다.
SST는 TypeScript 기반의 코드형 프레임워크이고, 프론트엔드 개발자인 저에게 매력적으로 다가온 SST를 사용해보기로 했습니다.
Note: (현재 v3가 나온 상황이지만, v2 버전 기반으로 작성되었습니다.)
1. SST 란?
- AWS CDK 기반: AWS CDK를 활용해 인프라를 코드로 정의할 수 있습니다.
- 로컬 개발 환경 지원: Lambda 함수를 로컬에서 실행할 수 있어 개발 속도가 빠릅니다.
- Live Lambda Debugging: 코드 변경 사항을 실시간으로 반영할 수 있습니다.
- 다양한 AWS 서비스와 원활한 통합: Lambda, API Gateway, DynamoDB, S3, Cognito 등 AWS의 여러 서비스와 쉽게 연동됩니다.
- Next.js 지원: Next.js 애플리케이션을 AWS에 배포할 때 최적화된 구성을 제공합니다
2. Next.js 렌더링 방식이 Serverless에서 어떻게 적용될까?
정적 사이트 생성 (SSG):
- 정적 페이지는 CloudFront CDN을 통해 제공됩니다.
서버 사이드 렌더링 (SSR):
- 서버 사이드 렌더링은 CloudFront 엣지 위치에서 Lambda@Edge를 사용하여 수행됩니다.
API 라우트:
- API 요청은 CloudFront 엣지 위치에서 Lambda@Edge를 사용하여 처리됩니다.
점진적 정적 재생성 (ISR):
- 재생성은 Lambda 함수를 사용하여 수행되며, 생성된 페이지는 CloudFront CDN을 통해 제공됩니다.
이미지 최적화:
- 이미지 크기 조정 및 최적화는 CloudFront 엣지 위치에서 Lambda@Edge를 사용하여 수행됩니다.
3. SST 설정 둘러보기
import dotenv from 'dotenv'
import { SSTConfig } from "sst";
import { NextjsSite, Table, Function, Api } from "sst/constructs";
export default {
config(_input) {
return {
name: "web",
region: "ap-northeast-2",
};
},
stacks(app) {
app.stack(function Site({ stack }) {
const { stage } = stack;
const path = envPathMap.get(stage);
// DynamoDB 테이블 생성
const table = new Table(stack, "ViewCount", {
fields: {
id: "string",
},
primaryIndex: { partitionKey: "id" },
});
// Lambda 함수 생성
const updateViewCountFunction = new Function(stack, "UpdateViewCountFunction", {
handler: "lambda/handler.incrementViewCount",
environment: {
TABLE_NAME: table.tableName,
},
permissions: [
"dynamodb:PutItem",
"dynamodb:UpdateItem"
]
});
const getViewCountFunction = new Function(stack, "GetViewCountFunction", {
handler: "lambda/handler.getViewCount",
environment: {
TABLE_NAME: table.tableName,
},
permissions: ["dynamodb:GetItem"]
});
const getAllViewCountFunction = new Function(stack, "GetAllViewCountFunction", {
handler: "lambda/handler.getAllViewCount",
environment: {
TABLE_NAME: table.tableName,
},
permissions: ["dynamodb:GetItem"]
});
// Lambda 함수 권한 부여
updateViewCountFunction.attachPermissions([table]);
getViewCountFunction.attachPermissions([table]);
getAllViewCountFunction.attachPermissions([table]);
// API Gateway 생성
const api = new Api(stack, "Api", {
cors: {
allowMethods: ["GET", "POST"],
allowOrigins: ["https://kwangmin-nam.com", "https://www.kwangmin-nam.com"]
},
// API Gateway 라우트 설정
routes: {
"POST /view-count/{slug}": updateViewCountFunction,
"GET /view-count/{slug}": getViewCountFunction,
"GET /view-count": getAllViewCountFunction,
},
});
// 환경변수 설정
const { parsed: environment } = dotenv.config({ path });
const site = new NextjsSite(stack, "site", {
// ApiGateway 바인딩
bind: [api],
// Rotue 53 도메인 설정
customDomain: {
domainName: "kwangmin-nam.com",
domainAlias: "www.kwangmin-nam.com",
hostedZone: "kwangmin-nam.com",
}
});
stack.addOutputs({
SiteUrl: site.url,
ApiEndpoint: api.url,
});
});
},
} satisfies SSTConfig;
const envPathMap = new Map();
envPathMap.set('production', '.env.production');
우선 제가 사용한 AWS 서비스는 아래와 같습니다.
- API Gateway
- DynamoDB
- Lambda
- Route 53
AWS 서비스를 하나하나 등록하는 번거러운 일을 SST 한테 위임하는 것이므로 AWS IAM 자격 증명을 설정해줘야 합니다. 어떤 서비스까지 permission을 줄지 설정해줘야 합니다. Nextjs의 기본 permission은 아래와 문서를 참고해주세요.
IAM 자격증명 설정 https://v2.sst.dev/advanced/iam-credentials
간단하게 설명하자면 저의 블로그의 조회수를 카운트하는 서비스를 만들었습니다.
DynamoDB 조회수 테이블을 생성하고, Lambda 함수를 생성하여 API Gateway를 통해 조회수를 카운트하는 서비스를 만들었습니다.
또한 Route 53를 통해 도메인을 설정하고, config 파일에 작성하여 배포까지 해보았습니다.
각각의 Lambda 함수에 권한을 부여하고, API Gateway를 통해 조회수를 카운트하는 서비스를 만들었습니다.
3. 조회수 카운트로 드는 예시
1. Lambda 함수 추가
SST에서는 Lambda 함수를 손쉽게 추가할 수 있습니다. 예를 들어, API 요청을 처리하는 Lambda 함수를 다음과 같이 정의할 수 있습니다.
import { App, Stack, Function } from "sst/constructs";
export default function MyStack({ stack }: { stack: Stack }) {
const myFunction = new Function(stack, "MyFunction", {
handler: "lambda-handler.main", // lambda-handler.ts 파일 내 main 함수
});
// API Gateway와 연동하여 Lambda를 호출할 수 있도록 설정
stack.addOutputs({
FunctionArn: myFunction.functionArn,
});
}2. API Gateway 연동
API Gateway 연동 SST에서는 Lambda와 API Gateway를 쉽게 연동할 수 있습니다. Next.js API 라우트도 내부적으로 Lambda와 API Gateway를 사용하여 배포되며, 별도로 API Gateway를 구성할 때는 Api construct를 사용할 수 있습니다.
import { Api } from "sst/constructs";
export default function MyApiStack({ stack }: { stack: Stack }) {
const api = new Api(stack, "Api", {
routes: {
"GET /hello": "lambda-handler.main",
},
});
stack.addOutputs({
ApiEndpoint: api.url,
});
}3. DynamoDB
DynamoDB와의 연동 역시 SST에서는 간단하게 구성할 수 있습니다. 아래 예제는 DynamoDB 테이블을 생성하고 Lambda 함수와 연동하는 예제입니다.
import { Table, Function, Stack } from "sst/constructs";
export default function DatabaseStack({ stack }: { stack: Stack }) {
// DynamoDB 테이블 생성
const table = new Table(stack, "ItemsTable", {
fields: {
id: "string",
name: "string",
},
primaryIndex: { partitionKey: "id" },
});
// Lambda 함수 생성 및 환경변수로 테이블 이름 전달
const handler = new Function(stack, "ItemFunction", {
handler: "lambda-handler.itemHandler",
environment: {
TABLE_NAME: table.tableName,
},
});
// Lambda 함수에 테이블 접근 권한 부여
table.grantReadWriteData(handler);
stack.addOutputs({
TableName: table.tableName,
});
}4. SSR Streaming 적용방법
Vercel 에서 next.js app을 배포하면 별도의 설정이 필요없이 SSR streaming이 기본으로 제공되지만, SST에서는 별도의 설정이 필요합니다.

open-next 문서에도 나와있고, SST 자체에서 디스코드 채널을 운영해서 저는 채널에 직접 문의를 해서 해결하였습니다. SSR Lambda 함수를 open-next.config.ts 파일에 설정해주면 됩니다.
Note: (SST init할땐 sst.config.ts가 생기지만, open-next.config.ts가 생기지 않아서 직접 생성해야합니다.)
// open-next.config.ts
import type { OpenNextConfig } from "@opennextjs/aws/types/open-next.js";
const config = {
default: {
override: {
wrapper: "aws-lambda-streaming", // This is necessary to enable lambda streaming
},
},
} satisfies OpenNextConfig;
export default config;5. 결론
사용하면서 단점으로 뽑자면 Next.js 버전이 업그레이드 될때마다 혹은 새로운 feature가 나올때마다 Vercel에서 배포하는경우에는 별도의 설정이나 안되는경우 없이 바로 적용이 되는것 같지만 SST 아무래도 시간이 조금 걸리거나 별도의 설정을 해줘야되는 것이 단점인것같습니다.
개인적인 경험으로 Streaming SSR을 사용하려면 open-next.config에서 별도의 설정이 필요하고, PPR(Partial Pre Rendering)이 현재 Next.js 에서 experimental 버전에서만 사용 할 수 있다고했지만 Vercel 배포했을땐 적용이 잘되었지만 SST는 적용이 안되었습니다.
하지만 SST를 사용하면 AWS 기반 서버리스 애플리케이션의 개발, 배포, 그리고 관리를 단순화할 수 있어, 복잡한 인프라 구성에 대한 부담 없이 빠르게 서비스를 구축할 수 있습니다. 특히, TypeScript와 AWS CDK를 활용한 코드 기반의 인프라 관리, Live Lambda 개발을 통한 빠른 피드백, 그리고 Next.js 등 프론트엔드와의 원활한 통합으로 개발자 경험을 크게 향상시킵니다. 즉, SST는 개발 속도를 높이고 효율적인 운영을 가능하게 하여, 클라우드 애플리케이션 구축의 모든 측면에서 큰 장점을 제공합니다.
댓글 로딩 중...