StudyDad Loop 제품을 만들며 배운 운영과 설계를 기록합니다.

FamBlend를 중심으로 실제 구현, 운영 메모, GitHub 포트폴리오를 연결해 쌓아가는 StudyDad의 작업 기록입니다.

성장과 기술/시스템 설계

Lambda Function URL 대신 API Gateway를 선택한 이유

박세식 2026. 7. 10. 12:00

운영 콘솔 아키텍처 시리즈 5/9

Lambda를 HTTP로 호출하려면 가장 단순한 방법은 Lambda Function URL이다. 별도 게이트웨이 없이 함수에 HTTPS URL을 붙일 수 있다. 작은 API라면 이 방식이 충분할 때도 많다.

그런데 운영 콘솔의 API 앞에는 API Gateway를 뒀다. 단순히 "원래 그렇게 하니까"가 아니라, 브라우저 기반 콘솔에서 필요한 운영 기능을 한곳에서 처리하기 위해서였다.

문제 상황

운영 콘솔 API는 브라우저에서 호출된다. 정적 SPA는 console.example.com에서 서빙되고, API는 api.example.com 또는 클라우드 API 도메인에서 제공된다. 이 순간 CORS가 발생한다.

또한 API는 하나의 엔드포인트가 아니었다. 동기화, 조회, 리포트 생성, 작업 상태 확인처럼 여러 path와 method가 필요했다. 어떤 요청은 공개적으로 읽을 수 있고, 어떤 요청은 내부 인증이 필요했다.

함수 하나에 URL 하나만 붙이는 방식으로도 처리할 수는 있다. 하지만 라우팅, CORS, 로깅, 스로틀링, 배포 단위를 모두 함수 코드 안으로 밀어 넣게 된다.

선택지

첫 번째 선택지는 Lambda Function URL이었다. 구성이 단순하고 비용도 낮다. path 분기는 함수 코드에서 처리하면 된다.

두 번째 선택지는 API Gateway HTTP API였다. REST API보다 가볍고 저렴하다. 기본적인 라우팅과 CORS를 제공한다.

세 번째 선택지는 API Gateway REST API였다. 상대적으로 비용과 지연은 크지만, MOCK Integration, Usage Plan, API Key, request validation, 상세한 stage 설정 등 운영 기능이 더 많다.

운영 콘솔에서는 세 번째 방식을 선택했다.

최종 결정

Lambda 앞에 API Gateway를 둔다.

API Gateway는 단순 프록시가 아니라 API 경계 역할을 한다. 브라우저가 마주하는 HTTP API의 정책을 Lambda 코드 바깥에서 다룬다.

구현 구조

가장 큰 이유 중 하나는 CORS preflight였다. 브라우저는 JSON 요청이나 인증 헤더가 포함된 요청을 보내기 전에 OPTIONS 요청을 보낼 수 있다.

이때 매번 Lambda를 깨워서 다음과 같은 응답을 만드는 것은 불필요하다.

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://console.example.com
Access-Control-Allow-Methods: GET,POST,OPTIONS
Access-Control-Allow-Headers: Content-Type,Authorization
Access-Control-Max-Age: 86400

API Gateway의 MOCK Integration을 사용하면 OPTIONS 요청은 Lambda를 호출하지 않고 게이트웨이에서 바로 응답할 수 있다. 비용과 지연이 줄고, Lambda 코드는 실제 비즈니스 요청에 집중할 수 있다.

path와 method도 Gateway에서 명시적으로 관리한다.

GET    /api/items
POST   /api/sync
POST   /api/reports
GET    /api/jobs/{jobId}
OPTIONS /*

함수 내부에서 최종 라우팅을 하더라도, 외부에서 어떤 API가 열려 있는지 Gateway 리소스 트리로 확인할 수 있다.

얻은 것

CORS 정책을 표준화할 수 있었다. 브라우저 콘솔의 CORS 문제는 대부분 실제 API 로직이 아니라 preflight와 응답 헤더 누락에서 나온다. Gateway에서 OPTIONS를 통일하면 장애 원인을 줄일 수 있다.

관측성도 좋아졌다. API Gateway access log와 metric을 보면 Lambda 내부 로그를 보기 전에 어느 path에서 몇 번 실패했는지, 4xx와 5xx가 어떻게 나뉘는지 확인할 수 있다.

스로틀링도 중요한 장점이다. Lambda는 자동으로 확장되지만, 뒤에 RDB나 외부 API가 있다면 무제한 확장이 항상 좋은 것은 아니다. Gateway에서 요청량을 제한하면 하위 시스템을 보호할 수 있다.

마지막으로 백엔드 교체 여지가 생긴다. 지금은 Lambda로 보내는 path라도 나중에 일부를 컨테이너 API나 다른 HTTP 백엔드로 옮길 수 있다. 클라이언트 URL을 유지한 채 내부 구현을 바꾸는 여지가 생긴다.

포기한 것

구성은 복잡해진다. Lambda Function URL 하나보다 설정할 것이 많다. CORS, method response, integration response, stage 배포 같은 개념도 이해해야 한다.

비용과 지연도 조금 늘어난다. 트래픽이 매우 크고 단순한 API라면 HTTP API나 Function URL이 더 맞을 수 있다.

하지만 운영 콘솔의 트래픽 규모에서는 이 차이가 핵심 비용이 아니었다. 오히려 장애 분석, CORS 안정성, 정책 분리의 가치가 더 컸다.

다시 설계한다면

처음부터 Gateway 설정을 코드로 관리하겠다. 콘솔에서 path별 OPTIONS를 수동으로 추가하면 누락이 생기기 쉽다. IaC나 스크립트로 CORS와 라우팅을 재현 가능하게 만드는 편이 안전하다.

또한 path가 많아질수록 하나의 Lambda 안에서 모든 라우팅을 처리하는 방식은 한계가 있다. 일정 규모 이상에서는 기능별 Lambda나 별도 API 서버로 나누는 기준을 문서화해야 한다.

정리

Lambda Function URL은 단순한 HTTP 진입점으로는 좋다. 하지만 브라우저 기반 운영 콘솔에서는 CORS, 라우팅, 로깅, 스로틀링, 배포 stage 같은 운영 요구가 빠르게 생긴다.

API Gateway를 Lambda 앞에 둔 이유는 Lambda를 호출하기 위해서가 아니라, API를 운영 가능한 경계로 만들기 위해서였다.

함께 볼 GitHub 저장소

Next Step 이 글은 StudyDad 작업 루프의 한 조각입니다.

글에서 정리한 생각은 GitHub의 코드와 포트폴리오로 이어지고, 일부는 FamBlend 같은 제품 실험으로 확장됩니다.