김인서inseout
← 프로젝트

anby

통합 자원 관리 SaaS — 예약·웨이팅·주문을 하나의 “자원”으로 묶습니다.

팀 3인 2025.02 — 2025.08 (7개월)
GoAWS Lambda (ARM)DynamoDB SQSCognitoGrafana React · TypeScript · Next.js
−20%
Lambda 실행 비용
x86 → ARM · 동일 성능 기준
100단위
낙관적 락 버저닝
동시 요청 시나리오 검토 뒤 정한 폭
256MB
최적 람다 메모리
Power Tuning 실측으로 선택
FIFO
데이터 유실 방지
SQS 순차 처리 보장
anby 통합 대시보드 화면
통합 대시보드 — 자원과 티켓을 한 화면에서
anby 모바일 화면
이용자 화면
Context

왜 만들었나

교내 외주에서
출발한 프로젝트

교내 타 학과의 외주 프로젝트가 출발점이었습니다. 현장에서 예약과 웨이팅이 동시에 이루어져야 하는데 서로 다른 서비스를 써야 하는 불편함을 직접 확인했습니다.

주 타깃으로 삼은 교내 동아리·학생회는 공간 대여, 물품 대여, 축제 주점처럼 관리할 자원이 분명히 있는데도 대부분 전산화되어 있지 않았습니다. 같은 자원을 두고 여러 서비스를 중복으로 쓰는 구조를 하나로 합치면 되겠다고 판단했습니다.

핵심 추상화

유·무형의 대상을 “자원”으로 추상화해 관리 가능한 객체로 만들고, 이용자의 요청은 전부 “티켓” 단위로 다룹니다. 예약·웨이팅·주문은 서로 다른 기능이 아니라, 같은 자원에 대한 다른 종류의 티켓입니다.

Role

맡은 부분

팀 3인
프론트 1 · 관리자 BE 1
클라이언트 BE 1
  • 관리자 대시보드 RESTful API 엔드포인트 설계 및 구현
  • 대규모 트래픽 처리를 고려한 티켓 도메인 데이터 구조와 DynamoDB 스키마 설계
  • DynamoDB 조건부 업데이트와 낙관적 락으로 중복 예약 방지 및 동시성 제어
  • ARM 서버리스 전환과 비용 최적화, Grafana 기반 운영 모니터링 구축
Architecture

시스템 구성

anby 시스템 아키텍처 다이어그램
서버리스 아키텍처 — API Gateway · Lambda(ARM) · DynamoDB · SQS
  1. 인증·인가

    API Gateway의 Lambda Authorizer로 JWT를 발급·검증해 관리자 API 접근을 통제합니다. 로그인·회원가입은 Cognito가 처리하되, 서버 요청 보안 처리를 위해 사용자 정보는 users 테이블에서 별도로 관리합니다.

  2. ARM 기반 서버리스

    x86 대비 ARM 람다가 동일 성능에서 20% 저렴하다는 점을 확인하고, Go로 작성한 서버 코드를 ARM 람다에 올렸습니다. Go를 고른 이유도 람다 환경에서의 가벼운 런타임과 빠른 콜드 스타트였습니다.

  3. SQS로 유실 방지

    람다 타임아웃이나 DB 과부하 상황에서 데이터가 유실될 수 있는 문제를 SQS로 막았습니다. FIFO 큐로 설정해 메시지가 순차적으로 처리되도록 보장했습니다.

  4. 테이블 분리

    티켓은 서비스별로 상태 변화가 잦아 예약·주문·웨이팅 테이블을 분리했습니다. 대신 projects 테이블에 연동 관계를 두어, 독립된 티켓 테이블들을 복합 프로젝트로 묶어 관리할 수 있게 했습니다.

Problems

부딪힌 문제와 해결

중복 예약

문제
예약처럼 단일 자원에 여러 쓰기 요청이 몰리면 예약 중복 또는 덮어쓰기가 발생했습니다. DynamoDB에는 이를 막아 주는 기본 장치가 없습니다.
선택
낙관적 락과 비관적 락을 두고 고민했습니다. 몇 번의 인터뷰와 예상 시나리오 테스트로 통합 자원 관리 특성상 동일 자원에 동시에 100번 이상의 중복 요청은 들어오지 않는다는 점을 확인하고, 100 단위 버저닝을 쓰는 낙관적 락을 택했습니다. 경합이 드문 곳에 비관적 락의 대기 비용을 치를 이유가 없었습니다.

모니터링 비용

문제
CloudWatch로 모니터링은 쉽게 붙었지만, 알람을 여러 개 만들면 추가 비용이 발생했습니다.
선택
CloudWatch 메트릭을 받아 Grafana 대시보드로 람다 실행 시간과 DynamoDB 오류 발생 여부를 시각화했습니다. 임계값을 넘으면 webhook으로 개발팀 채널에 즉시 경고가 가도록 붙였습니다.

람다 메모리 구성

문제
람다의 컴퓨팅 파워는 메모리 용량으로 결정됩니다. 크게 잡으면 빠르지만 비싸고, 작게 잡으면 실행 시간이 길어져 결국 비효율이 생깁니다.
선택
추측하지 않고 측정했습니다. AWS Lambda Power Tuning으로 실제 사용할 더미 데이터셋을 만들어 메모리 구성별 테스트를 돌렸습니다. 256MB와 512MB의 실행 시간이 동일하게 가장 짧게 나왔고, 실행 비용은 256MB가 실행 시간당 0.0000005$ 저렴했습니다. 같은 속도라면 싼 쪽이라 256MB를 택했습니다.
Data Model

DynamoDB 스키마

테이블 7종 전체 보기

resources — 자원

속성Type설명
PKstringprojectID#resourceID
SKstring자원 ID
namestring자원 이름
typestringMENU | OPTION | CATEGORY | RESERVATIONTARGET
fieldstring자원에 필요한 정보를 직렬화
imageURIstringpresigned URI
statebool자원 사용 가능 여부
capacity / usedint자원 수량 / 사용된 자원량
createdAt / updatedAtdatetime생성 · 업데이트 시각

projects — 프로젝트와 연동 관계

속성Type설명
PKstringuserID#projectID · parentProjectID
SKstringINFO · childProjectID
namestring프로젝트 이름
typestringRESERVATION | ORDER | WAITING · META
fieldstring프로젝트에 사용되는 정보 직렬화
statebool프로젝트 활성 여부
GSI_metastring복합 프로젝트 검색용 GSI

waiting_tickets · order_tickets · reservation_tickets — 티켓

세 테이블은 같은 형태를 공유합니다. 상태 변화 빈도가 서로 달라 물리적으로 분리했습니다.

속성Type설명
PK / SKstring프로젝트 ID / 티켓 ID
requesterstring요청자 ID
datastring요청자 정보를 직렬화
requestedTimedatetime티켓 생성 시각
startTime / endTimedatetime티켓 유효 시작 · 종료 시각
expiredTimedatetime티켓 파기 시각
statestringPENDING | ACQUIRE | RELEASED
memostring요청 사항
OSIstring유효 주문 처리를 위한 인증 키 (order 전용)

users · teams — 사용자와 팀

테이블설명
usersemail (PK)userID · username · createdAt · updatedAt
teamsteamID (PK)SK로 INFO · MEMBER#userID · PROJECT#projectID를 구분. owner / admin / member 권한과 GSI_team · GSI_project 보조 인덱스