[친구하자] Android 앱을 만들며 배운 CI/CD와 AWS EC2·ECS 이야기
친구하자 프로젝트 출시를 준비하며 CI/CD를 도입하고, Docker, EC2, ECS, ECR 등 AWS 인프라를 활용한 배포 흐름을 정리했다.
- 시작하기 전에
- 1. CI/CD란 무엇인가요?
- 2. Docker가 왜 필요한가요?
- 3. AWS EC2란 무엇인가요?
- 4. AWS ECS란 무엇인가요?
- 5. ECR이란 무엇인가요?
- 6. 지금 내 프로젝트의 전체 흐름
- 7. 정리
시작하기 전에
현재 Android 앱 “친구하자”의 백엔드를 Spring Boot로 개발하고 있다. 소규모로 시작해서 테스트 유저 10명 이하로 운영 중이며, 곧 Play Store 출시를 앞두고 있다.
출시를 준비하면서 지금까지 당연하게 해왔던 배포 방식을 돌아보게 됐다.
ssh -i "my-key.pem" ec2-user@xxx.xxx.xxx.xxx
# 접속 후 직접 명령어 실행...
매번 터미널을 열고, SSH로 접속하고, 직접 명령어를 치는 방식이었다. 테스트 유저가 적을 때는 괜찮았지만 출시가 다가오니 이런 생각이 들었다.
“코드 push하고 나서 배포까지 내가 직접 해야 하나? 이거 자동화할 수 없나?”
그래서 CI/CD를 도입하게 됐고, 그 과정에서 배운 내용을 정리해 본다.
이 글에서 다루는 것
CI/CD만 이야기하면 “push하면 자동으로 테스트하고 배포한다”로 끝나는데, 실제로 그 배포가 돌아가려면 몇 가지가 필요하다. (1) 뭘 배포할지 정하는 단위 → Docker 이미지, (2) 그 이미지를 어디에 보관할지 → ECR, (3) 그 이미지를 어디서 실행할지 → EC2, (4) 나중에 트래픽이 늘면 어떻게 관리·확장할지 → ECS. 아래에서는 이 순서대로 “왜 이걸 쓰는지” 맥락을 붙여서 정리한다.
1. CI/CD란 무엇인가요?
CI (Continuous Integration) — 지속적 통합
“코드를 push하면 테스트까지 자동으로”
코드를 GitHub에 push할 때마다 자동으로 빌드 + 테스트를 돌려서, 작성한 코드가 기존 코드를 망가뜨리지 않았는지 바로 확인한다.
개발자가 push
↓
GitHub Actions가 자동으로 실행
↓
빌드 → 테스트
↓
통과 ✅ or 실패 ❌ (즉시 알림)
이 프로젝트에서는 Redis를 활용한 매칭 큐, 이벤트 리스너 등 여러 컴포넌트를 테스트해야 했는데, 매번 로컬에서 직접 테스트를 돌리다 보니 빠뜨리는 경우가 생겼다. CI를 붙이고 나서는 push할 때마다 자동으로 전체 테스트가 돌아가니 훨씬 안심이 됐다.
핵심 가치: 문제를 빠르게 발견한다. 나중에 발견할수록 고치기 어렵다.
CD (Continuous Deployment) — 지속적 배포
“테스트까지 통과하면 서버 배포도 자동으로”
CI가 성공하면 자동으로 서버에 새 버전을 배포한다. 매번 SSH로 직접 접속해서 배포하던 방식이 완전히 사라진다.
CI 성공 ✅
↓
Docker 이미지 빌드
↓
ECR(이미지 저장소)에 업로드
↓
EC2 서버에 새 버전 자동 배포
↓
개발자는 push만 했을 뿐인데 배포 완료 🎉
도입 전 vs 도입 후
| 도입 전 | 도입 후 | |
|---|---|---|
| 배포 방법 | SSH 접속 → 직접 명령어 실행 | git push 한 번 |
| 테스트 | 까먹으면 안 함 | 항상 자동 실행 |
| 실수 가능성 | 있음 (손으로 하니까) | 없음 |
| 배포 시간 | 10~15분 | 기다리면 자동 완료 |
2. Docker가 왜 필요한가요?
CD는 “테스트 통과한 코드를 서버에 그대로 올리는 것”이다. 이때 “그대로”를 보장하려면 배포 단위가 정해져 있어야 한다. 로컬에서 돌리던 걸 그냥 서버에 복사하면 환경이 달라서 실패하는 경우가 많다. 그래서 CI/CD 흐름에서는 Docker 이미지를 그 단위로 쓰고, 그 이미지를 서버에서 실행하는 방식이 일반적이다. 즉 Docker는 “CD가 뭘 배포할지 정의하는 방식”이라서 빠질 수 없다.
개발 환경과 서버 환경이 다르면 이런 일이 생긴다.
“내 맥에서는 되는데 서버에서는 왜 안 되지?”
Docker는 앱 실행에 필요한 모든 것(코드, 라이브러리, 설정)을 하나의 박스에 담아 버린다.
Dockerfile (조리법)
↓ 빌드
Docker 이미지 (완성된 통조림)
↓ 실행
컨테이너 (실제로 접시에 담아 먹는 상태)

어디서 실행해도 똑같이 동작하기 때문에 환경 차이로 인한 문제가 사라진다.
3. AWS EC2란 무엇인가요?
CD가 “Docker 이미지를 서버에 배포한다”고 했을 때, 그 서버가 어디인지가 정해져 있어야 한다. 친구하자 프로젝트에서는 그 역할을 EC2 한 대가 맡고 있다. EC2 위에서 빌드된 Docker 이미지를 받아서 컨테이너로 실행하면, 사용자 요청을 처리하는 백엔드 서버가 되는 식이다.
EC2 (Elastic Compute Cloud)는 AWS에서 빌려 쓰는 가상 컴퓨터이다.
Spring Boot 백엔드 서버를 EC2 위에서 Docker 컨테이너로 돌리고 있다. 처음에는 아래처럼 직접 SSH로 접속해서 배포했다.
# 매번 이걸 직접 했었음
ssh -i "my-key.pem" ec2-user@서버IP
docker stop my-app
docker rm my-app
docker pull 새이미지
docker run -d --name my-app -p 8080:8080 새이미지
한두 번은 괜찮지만, 기능 추가할 때마다 반복하다 보면 꽤 번거롭다. 게다가 docker stop 하고 docker run 하는 사이에 서비스가 잠깐 끊기는 순단이 발생한다. 테스트 유저 10명일 때는 몰랐는데, Play Store 출시 후 실 사용자가 생기면 문제가 될 수 있겠다 싶었다.
4. AWS ECS란 무엇인가요?
지금은 EC2 한 대에서 컨테이너를 직접 올리고 내리는 방식으로 CD가 동작한다. 이 방식은 단순하지만, 배포 시 잠깐 서비스가 끊기는 순단이 있고, 사용자가 몰렸을 때 서버를 늘리는 것도 수동이다. 트래픽이 늘어나면 “배포할 때도 끊기지 않게(무중단 배포)” “부하가 크면 컨테이너를 자동으로 늘리게(오토스케일링)”가 필요해진다. 그때 CD의 배포 대상을 EC2 직접 제어에서 ECS로 바꾸면, 컨테이너 실행·롤링 업데이트·스케일링을 AWS가 관리해 준다.
ECS (Elastic Container Service)는 Docker 컨테이너를 AWS가 대신 관리해 주는 서비스이다.
EC2가 “컴퓨터를 빌려 드릴게요”라면, ECS는 “컨테이너 실행·관리까지 다 해 드릴게요”이다.
핵심 개념 3가지
클러스터 (Cluster)
└── 컨테이너들이 실행되는 공간 전체 (운동장)
서비스 (Service)
└── "이 컨테이너를 항상 N개 유지해줘" (운동장 관리자)
태스크 정의 (Task Definition)
└── 컨테이너 실행 설정 명세서
(어떤 이미지, CPU/메모리 얼마, 포트는 몇 번 등)
ECS의 핵심 장점: 롤링 업데이트 (무중단 배포)
EC2 직접 배포는 이렇게 된다:
기존 컨테이너 중지 → 💀 서비스 잠깐 중단 → 새 컨테이너 시작
ECS 롤링 업데이트는 이렇게 된다:
새 컨테이너 먼저 시작 & 정상 확인
↓
기존 컨테이너 중지 ← 사용자는 아무것도 못 느낌 ✅
5. ECR이란 무엇인가요?
CD 흐름을 다시 보면, “CI 통과 → Docker 이미지 빌드 → 어딘가에 보관 → EC2(또는 ECS)가 그걸 가져가서 실행”이다. 이미지를 보관하는 곳이 없으면 EC2나 ECS가 “어떤 이미지로 컨테이너를 띄울지” 가져올 수 없다. 그 보관소 역할을 AWS에서는 ECR이 한다. GitHub Actions에서 이미지를 빌드한 뒤 ECR에 푸시하고, EC2(또는 ECS)는 ECR에서 이미지를 pull 해서 컨테이너를 띄우는 식으로 연결된다.
ECR (Elastic Container Registry)은 Docker 이미지를 저장하는 AWS 전용 창고이다.

6. 지금 내 프로젝트의 전체 흐름
현재 구조 (EC2 + 자동 배포)
main 브랜치에 push
↓
[CI]
Redis 컨테이너 띄우기
→ 빌드 + 전체 테스트 자동 실행
→ 실패 시 여기서 중단 ❌
↓ CI 성공
[CD]
Docker 이미지 빌드
→ ECR에 푸시
→ EC2에 SSH로 자동 접속
→ 새 이미지로 컨테이너 재시작
↓
사용자가 새 버전 사용 🎉
예전에는 이 과정을 손으로 직접 했는데, 이제는 push 한 번이면 끝이다.
Play Store 출시 이후엔 ECS로 전환 예정
지금은 소규모라 EC2 직접 배포로도 충분하다. 하지만 출시 후 트래픽이 늘어나면 아래 두 가지가 필요해질 것이다.
- 무중단 배포: 배포할 때 앱이 잠깐이라도 끊기면 사용자 경험이 나빠짐
- 오토스케일링: 갑자기 사용자가 몰려도 자동으로 서버를 늘려줌
그때 ECS로 전환할 예정이며, CD 파이프라인에서 ECS 배포 스텝만 교체하면 되기 때문에 마이그레이션 비용도 크지 않다.
7. 정리
| EC2 직접 배포 (현재) | ECS (출시 후) | |
|---|---|---|
| 배포 방식 | 컨테이너 재시작 | 롤링 업데이트 |
| 서비스 중단 | 순단 가능 | 없음 |
| 스케일링 | 수동 | 자동 |
| 비용 | 저렴 | 상대적으로 높음 |
| 적합한 시점 | 초기 소규모 | 트래픽 증가 후 |
한 줄 요약:
push 한 번으로 테스트부터 배포까지. 지금은 EC2로 가볍게, 트래픽 늘면 ECS로 전환.