AWS Lambda로 API 서버 만들기

작업 중 다중 이미지 업로드를 구현할 일이 생겼다. 최악의 경우 한 번에 200MB가 들어올 수도 있는데 싱글스레드인 Node가 버텨줄지 자신이 없었고, 그렇다고 서버를 하나 더 세우기는 귀찮아서 결국 람다로 도망쳤다.

목차

배경

한 번에 열 장 정도 업로드될 걸로 예상하지만, 휴대폰으로 찍은 고화질 사진이기에 심하면 한 번에 200MB 정도 업로드되는 최악의 경우를 대비해야겠다고 생각했다.

조건
예상 동시 업로드 10장 내외
최악의 경우 총 용량 약 200 MB
백엔드 런타임 Node.js (싱글스레드)

백엔드에서 리사이징을 하더라도 일단 업로드부터 받아서 처리해야 하는데, 싱글스레드인 Node가 버텨줄지 의문이었다. 클러스터를 믿고 의존하기엔 애매했고, 프론트에서 리사이징시켜서 올려버릴까 생각도 했지만 일단 좀 더 찾아보기로 했다.

Serverless 개념 이미지 서버가 있는데 왜 써..? 굳이..? 라고 생각했었다

사실 serverless는 니꼴라스 같은 유튜버가 혁신이네 뭐네 하면서 강의하는 것만 봤지 실제로 써볼 생각은 안 했다. 서버가 있는데 왜 써..? 굳이..?

그런데 막상 따져보니 서버 인스턴스 하나 더 구축하는 게 더 귀찮을 것 같았다. 그래서 람다로 관리 포인트를 줄이는 쪽을 택했다.

최종 목적은 S3 이미지 업로드지만, 처음 구현해보는 거라 간단한 예제부터 해보려고 예전에 만들어두었던 SMS 발송 코드를 가져왔다.

기본 세팅

처음 구현은 은근히 까다롭다.

IAM을 통해 자격증명을 받아 AccessKey와 AccessSecretKey를 발급받는 과정은 공식문서에서 확인할 수 있다. 본 글에서는 위 링크대로 기본 세팅이 되어 있다는 전제하에 작성했다.

brew install awscli

우선 AWS를 로컬에서 편하게 다루게 awscli를 설치해준다. 다음 aws configure 명령어로 기본 환경설정 파일을 열고, 발급받았던 액세스키와 시크릿키를 보안자격증명 페이지에서 가져와 넣어준다.

aws configure 실행 화면 한국을 리전으로 잡았다면 대괄호 안에 있는 대로 리전과 포맷을 적어주면 된다

여기 입력한 값은 ~/.aws/credentials 에 저장되니 참고하자. 사실 aws configure 다시 쳐도 수정 가능하다.

권한 등록

AccessKey로 사용자를 등록해주었으니, 이제 이 사용자가 S3와 람다에 접근할 수 있게 권한을 가져올 차례다. 키가 있다고 다 되는 게 아니라, “무엇을 할 수 있는지”는 정책(Policy)으로 따로 붙여줘야 한다.

보안자격증명 → 액세스 관리 → 사용자에 들어가 지정된 AccessKey를 선택한 뒤, 우측 권한 추가에서 정책 추가를 눌러 아래 두 정책을 검색해서 추가해준다.

IAM 사용자 권한 추가 화면 사용자에 정책 두 개를 붙인다

본 글에서는 간단한 SMS 발송 코드만 구현하기에 S3 권한은 사실 필요 없지만, 목적은 S3 업로드이기에 함께 권한을 요청했다.

역할에 정책을 부여하는 과정 이후 역할(Role)에서도 권한을 똑같이 부여해주자

역할 생성 단계 1

역할 생성 단계 2

역할 생성 단계 3

생성 완료된 역할 이 역할의 ARN을 뒤에서 쓴다

코드 작성

이제 로컬에서 코드를 작성하자.

const coolsms = require("coolsms-node-sdk").default;
const messageService = new coolsms(
  "api키 자리",
  "api secret키 자리"
);

exports.handler = function (event, context, callback) {
  try {
    messageService.sendOne({
      to: "받는사람",
      from: "보내는번호",
      text: `[테스트] 인증번호는 [${parseInt(
        Math.random() * 9000
      )}] 입니다. \n알맞게 입력해주세요.`,
    });
  } catch (e) {
    console.log(e);
  }
  const response = {
    statusCode: 200,
    headers: {},
    body: JSON.stringify("success send sms"),
  };
  callback(null, response);
};

대충 이렇게 넣어줬다. yarn add coolsms-node-sdk 로 패키지를 설치하고 작성했다.

상세한 의미는 다른 블로그를 참고하면 되고, 우선 exports.handler는 꼭 지켜주자. 추후 람다에게 진입점으로 알려줘야 하는 이름이다.

이제 코드를 작성하는 경로에서 아래 명령어로 node_modules 폴더와 함께 싸그리 압축시켜주자.

zip -r 원하는이름.zip 압축할경로

# 예시
zip -r sms.zip .

업로드

터미널에 아래와 같이 입력한다.

aws lambda create-function \
  --function-name [람다에 등록될 함수명] \
  --zip-file  [파일] \
  --handler   [기본함수] \
  --runtime   [런타임환경] \
  --role      [아까 발급받았던 역할의 arn]

# 나는 아래와 같이 작성했다
aws lambda create-function \
  --function-name sms \
  --zip-file fileb://sms.zip \
  --handler index.handler \
  --runtime nodejs18.x \
  --role arn:aws:iam::<계정ID>:role/image-resizing

role 자리에 들어갈 ARN은 아래에서 확인할 수 있다.

역할 상세 화면의 ARN ARN 문자열을 그대로 복사해 쓰면 된다

명령어를 치고 나면 아래처럼 정보가 뜨고, q를 눌러 빠져나오면 된다.

create-function 실행 결과 생성된 함수의 메타데이터가 그대로 출력된다

테스트

아래와 같이 잘 등록되어 나오는 걸 확인했다. 그럼 들어가서 테스트해보자.

람다 콘솔의 함수 목록

테스트 이벤트 설정

테스트 실행 결과 코드에 작성한 대로 값들이 잘 반환되었다

수신된 문자 메시지 문자도 잘 수신됐다

API Gateway 연동

함수는 잘 올렸으니 이제 이걸 호출할 루트를 만들 차례다. 여기서는 AWS에서 제공하는 API Gateway 서비스를 이용했다.

API Gateway 트리거 추가 1

API Gateway 트리거 추가 2

API Gateway 트리거 추가 3

API Gateway 트리거 추가 4 순서대로 따라 하면 된다

그러면 함수 개요에 API Gateway가 람다와 연동된 게 다이어그램으로 표기되고, 아래에 API endpoint 주소가 나온다. 우리가 함수를 호출할 최종 URL이다.

연동 완료된 함수 개요와 엔드포인트 이 엔드포인트를 때리면 문자가 날아간다

마치며

“서버가 있는데 왜 써”라고 생각했던 게 무색하게, 결국 관리 포인트를 줄이려고 람다를 쓰게 됐다. 인스턴스를 하나 더 띄우면 그 인스턴스의 OS 업데이트와 모니터링, 비용까지 전부 내 몫이 되는데 람다는 그게 없으니까.

대신 처음 세팅은 은근히 까다로웠다. 정작 코드는 20줄 남짓인데, IAM 사용자와 역할과 정책이 서로 어떻게 엮이는지 감을 잡는 데 시간을 제일 많이 썼다.

아무튼 이제 원래 목적이었던 S3 이미지 리사이징으로 넘어갈 차례다.

다음엔 이런 걸 해보면 좋겠다.

  • 이미지 리사이징에 실제로 적용해보기 — 이 글의 원래 목적이다.
  • 콘솔 대신 IaC로 — 지금은 CLI와 콘솔을 오가며 만들었다. SAM이나 Terraform으로 관리하면 재현이 쉬워질 것 같다.
  • 콜드스타트 측정 — 체감으로만 “좀 느린데” 하고 넘어갔다. 숫자로 봐야 대응할지 말지 정할 수 있다.

맨 위로

© 2026 consolekakao. 글과 이미지는 직접 작성·촬영한 것이다.