
CI/CD 파이프라인을 게임으로 만들다
배포는 개발자에게 긴장과 불안의 순간입니다. "Make Deploy Delightful"은 이 긴장의 순간을 즐거운 경험으로 바꾸자는 철학에서 출발했습니다. 터미널의 딱딱한 로그 대신 Softbank의 마스코트가 달리고, 실패해도 AI가 친절하게 해결책을 알려주는 그런 배포 경험을 만들고 싶었습니다.

배포 이벤트 발생 시:
개발자 Git Push → GitHub → CodePipeline 트리거 → Source Stage (코드 가져오기)
→ Build Stage (npm install, test, Docker 빌드, ECR 푸시)
→ Deploy Stage (Elastic Beanstalk 배포)
→ EventBridge (파이프라인 상태 변경 이벤트 캡처)
→ Lambda writer (이벤트 파싱 + Bedrock AI 분석)
→ DynamoDB (상태 저장)
→ Webhook 알림 (Slack, Discord)
프론트엔드 상태 조회 시:
React 앱 (2초 폴링) → API Gateway → Lambda reader → DynamoDB 조회
→ JSON 응답 → Zustand 상태 업데이트 → Kaplay 게임 애니메이션 반영
| 분류 | 기술 | 선정 이유 및 용도 |
|---|---|---|
| Language | Python 3.13, TypeScript | Lambda 런타임 최신 버전 활용, 프론트엔드 타입 안정성 확보 |
| Frontend | React 18 + Vite 6 | 빠른 HMR과 빌드 속도, 최신 React 기능 활용 |
| Game Engine | Kaplay | 경량 2D 게임 라이브러리, 빠른 프로토타이핑에 적합 |
| State | Zustand + TanStack Query | 전역 상태와 서버 상태 분리, 폴링 로직 간소화 |
| IaC | Terraform | AWS 리소스 코드화, 모듈별 분리로 재사용성 확보 |
| Compute | AWS Lambda | 서버리스로 비용 최적화, 이벤트 기반 처리에 적합 |
| Database | DynamoDB | 서버리스 NoSQL, 파이프라인 상태 저장에 적합한 Key-Value 구조 |
| API | API Gateway (HTTP API) | REST API보다 저렴하고 빠른 HTTP API 선택 |
| AI | Amazon Bedrock (Claude 3.5 Sonnet) | 빌드 실패 시 에러 로그 분석 및 솔루션 제안 |
| Container | Docker + ECR | 일관된 배포 환경, Elastic Beanstalk Docker 플랫폼 활용 |
| Hosting | AWS Amplify | React 앱 자동 빌드/배포, GitHub 연동 간편 |
대안 비교:
| 구분 | EC2 + ALB | ECS Fargate | Lambda + API Gateway |
|---|---|---|---|
| 초기 비용 | 높음 (상시 가동) | 중간 | 낮음 (사용량 기반) |
| 운영 복잡도 | 높음 | 중간 | 낮음 |
| 확장성 | 수동 설정 필요 | 자동 | 자동 |
| 해커톤 적합성 | 낮음 | 중간 | 높음 |
선택 이유:
1. 해커톤 특성상 빠른 개발과 배포가 필요했고, 서버리스는 인프라 관리 부담을 최소화
2. 파이프라인 이벤트는 간헐적으로 발생하므로 상시 가동 서버보다 이벤트 기반 Lambda가 비용 효율적
3. API Gateway HTTP API는 REST API 대비 70% 저렴하고 지연 시간도 짧음
Deploy-Land-Infra/
├── api_gateway/ # HTTP API, 라우트, CORS 설정
│ ├── apigateway_api.tf
│ ├── apigateway_route.tf
│ └── outputs.tf
├── lambda/ # writer, reader, HealthCheck 함수
│ ├── lambda_function.tf
│ └── lambda_permission.tf
├── dynamodb/ # deploy-land-status 테이블
│ └── dynamodb_table.tf
├── codepipeline/ # CI/CD 파이프라인 정의
│ └── codepipeline.tf
├── codebuild/ # 빌드 프로젝트 설정
│ └── codebuild_project.tf
├── elastic_beanstalk/ # Docker 플랫폼 환경
│ └── elastic_beanstalk_environment.tf
└── iam/ # 역할, 정책, 권한 관리
├── iam_role.tf
├── iam_policy.tf
└── iam_role_policy_attach.tf
모듈화 의도:
| 함수명 | 메모리 | 타임아웃 | 역할 |
|---|---|---|---|
writer | 512MB | 30초 | EventBridge 이벤트 수신 → DynamoDB 기록 + Bedrock AI 분석 |
reader | 128MB | 3초 | API Gateway 요청 → DynamoDB 조회 → JSON 응답 |
App-HealthCheck | 128MB | 123초 | Elastic Beanstalk 환경 상태 체크 |
writer 함수 핵심 로직:
# EventBridge에서 CodePipeline 상태 변경 이벤트 수신
# 1. 이벤트 파싱 (pipelineId, stage, status 추출)
# 2. 실패 시 Bedrock Claude 3.5에 에러 로그 전달하여 분석 요청
# 3. DynamoDB에 상태 + AI 분석 결과 저장
# 4. Slack/Discord Webhook으로 알림 전송
Slack/Discord Webhook 연동:
GET /api/status/LATEST_EXECUTION → 최신 파이프라인 실행 ID 반환
GET /api/status/{pipelineId} → 특정 파이프라인 상세 상태 반환
Problem:
파이프라인 상태를 프론트엔드에 실시간으로 반영해야 했습니다. 초기에는 단순 폴링으로 구현했으나, 불필요한 API 호출이 많아지고 상태 변경 감지가 지연되는 문제가 있었습니다.
Solution:
TanStack Query의 refetchInterval을 활용한 스마트 폴링 구현:
// usePipelineStatus.ts
const { data: pipelineStatus } = useQuery<PipelineStatus>({
queryKey: ["pipelineStatus", currentPipelineId],
queryFn: () => getPipelineStatus(currentPipelineId),
enabled: !!currentPipelineId,
staleTime: 0,
refetchInterval: currentPipelineId ? 2000 : false, // 조건부 폴링
});
enabled 옵션으로 pipelineId가 있을 때만 폴링 시작staleTime: 0으로 항상 최신 데이터 요청Problem:
API 응답(성공/실패)에 따라 게임 애니메이션을 실행해야 하는데, 비동기 처리와 애니메이션 타이밍이 맞지 않아 모달이 너무 빨리 뜨거나 애니메이션이 끊기는 문제가 발생했습니다.
Solution:
Promise 기반 애니메이션 시스템과 useRef를 활용한 중복 처리 방지:
// GameCore.ts
async triggerSuccessWithGoalMove(onComplete?: () => void): Promise<void> {
// 1. 장애물 제거
this.obstacleManager.destroyAll();
// 2. 플레이어 이동 완료까지 await
await this.player.moveToPosition(targetX);
// 3. 이동 완료 확인 (while 루프로 상태 체크)
while (this.player.moving && waitCount < 20) {
await new Promise<void>((resolve) => {
this.k.wait(0.1, () => resolve());
});
}
// 4. 성공 애니메이션 실행
this.player.triggerSuccess();
// 5. 애니메이션 완료 후 콜백 (모달 표시)
await new Promise<void>((resolve) => {
this.k.wait(1.5, () => resolve());
});
onComplete?.();
}
Problem:
로컬 개발 환경에서 API Gateway 직접 호출 시 CORS 에러 발생. 프로덕션과 개발 환경의 API 호출 방식이 달라야 했습니다.
Solution:
Vite 프록시 + 환경 변수 분기 처리:
// cicd.ts
const url = import.meta.env.DEV
? path // 개발: Vite 프록시 사용 (상대 경로)
: `${API_BASE_URL}${path}`; // 프로덕션: 직접 호출
// vite.config.ts (개발 환경)
proxy: {
'/api': {
target: 'https://xxx.execute-api.ap-northeast-2.amazonaws.com',
changeOrigin: true,
}
}
API Gateway CORS 설정에 Amplify 도메인 추가로 프로덕션 환경 해결.
Problem:
writer Lambda에 EventBridge 이벤트 처리, DynamoDB 쓰기, Bedrock AI 호출, Slack/Discord 알림까지 모든 기능이 집중되어 있었습니다. 이로 인해:
해결 방안 (향후 개선):
Lambda를 기능별로 분리하고 Step Functions로 오케스트레이션:
EventBridge → Step Functions
├── Lambda: event-parser (이벤트 파싱)
├── Lambda: db-writer (DynamoDB 저장)
├── Lambda: ai-analyzer (Bedrock 분석) [조건부]
└── Lambda: notifier (Slack/Discord) [조건부]
비용 분석:
Lambda 단일 함수 집중: writer 함수에 너무 많은 책임이 몰려 있어 유지보수와 디버깅이 어려웠음
폴링 방식의 한계: 2초 폴링은 실시간성이 떨어지고 불필요한 API 호출 발생. WebSocket이나 Server-Sent Events를 처음부터 고려했어야 함
이벤트 로그 단순화: CodePipeline 이벤트만 캡처했는데, CodeBuild 로그, 테스트 결과, 배포 상세 로그 등 더 다양한 이벤트를 수집했다면 풍부한 분석 가능했음
단일 스테이지/액션: Source → Build → Deploy 3단계만 구현. 실제 프로덕션에서는 Lint, Test, Security Scan, Staging, Production 등 다양한 스테이지가 필요
Slack Webhook 단방향: 알림 전송만 구현하고 Slack에서 명령어로 배포 트리거하는 양방향 연동은 못함
Bedrock 활용 부족: 에러 분석에만 사용했는데, 코드 리뷰 제안, 테스트 케이스 생성, 문서 자동화 등 더 다양한 활용 가능했음
프롬프트 엔지니어링 미흡: Bedrock에 전달하는 프롬프트가 단순했음. Few-shot 예시, 구조화된 출력 형식 등을 활용했다면 더 정확한 분석 가능
비용 모니터링 부재: AWS Cost Explorer나 Budgets 연동 없이 진행. 서비스가 얼마나 비용을 소모하는지 실시간 파악 불가
롤백 기능 미구현: 배포 실패 시 이전 버전으로 자동 롤백하는 기능이 없었음
Lambda 분리 + Step Functions: 기능별 Lambda 분리 후 Step Functions로 워크플로우 오케스트레이션. 실패 격리와 재시도 로직 개선
WebSocket 실시간 통신: API Gateway WebSocket API 도입으로 진정한 실시간 상태 업데이트 구현
API Gateway WebSocket → Lambda → DynamoDB Streams → 클라이언트 푸시
다양한 이벤트 소스 연동:
멀티 스테이지 파이프라인:
Source → Lint → Unit Test → Build → Security Scan → Staging Deploy → E2E Test → Production Deploy
Slack 양방향 연동: Slack Slash Command로 /deploy staging, /rollback production 등 명령어 지원
Bedrock 확장 활용:
프롬프트 최적화:
당신은 AWS 전문가입니다. 다음 빌드 에러 로그를 분석하고:
1. 에러 원인 (1줄 요약)
2. 해결 방법 (단계별)
3. 예방 조치
형식으로 답변해주세요.
[에러 로그]
{error_log}
비용 모니터링 대시보드: AWS Budgets + SNS + Lambda로 비용 임계치 알림, Cost Explorer API로 일별 비용 추적
자동 롤백 구현: CodePipeline 실패 시 이전 성공 버전으로 자동 롤백 + 알림
Deploy Land 프로젝트는 "배포가 게임이 된다"는 단순하지만 강력한 컨셉으로 시작했습니다. 1주일이라는 짧은 해커톤 기간 동안 AWS 서버리스 아키텍처, Terraform IaC, 실시간 이벤트 처리, AI 연동까지 풀스택 경험을 압축적으로 쌓을 수 있었습니다.
가장 큰 배움은 "완벽한 설계보다 빠른 실행과 반복"의 중요성이었습니다. Lambda 단일 함수에 기능을 몰아넣은 것이 아쉽지만, 그 덕분에 빠르게 동작하는 프로토타입을 만들 수 있었고, 이것이 예선 입선과 본선 진출의 원동력이 되었습니다.
앞으로는 이 프로젝트에서 느낀 아쉬운 점들을 개선하며, 단순히 "동작하는 코드"를 넘어 "확장 가능하고 유지보수하기 좋은 아키텍처"를 설계하는 개발자로 성장하고 싶습니다. 특히 서버리스 환경에서의 마이크로서비스 분리, 이벤트 기반 아키텍처, 그리고 AI를 활용한 DevOps 자동화에 더 깊이 파고들 계획입니다.