본문 바로가기
AI에서 살아남기(to. Android Developer)/1인 프로젝트

(모두의 웹툰) Part7 : 인프라 구축기 [ver 2]

by 개발자 인민군 2025. 12. 19.

바로 이전의 글 인 Part7 : 인프라 구축기 를 개발할 때에는 순조롭게 구축하였다고 생각하였습니다.

하지만 곧 운영 환경으로 사용하기에는 치명적인 문제들이 있다는 것을 깨달았습니다.

문제는 다음과 같습니다.

 

1. HTTP 프로토콜의 보안 문제

문제: 모든 데이터가 평문으로 전송됨
위험: 사용자 비밀번호, 개인정보 등이 중간에서 가로채기 가능

사용자 인증 정보를 다루는 API인데, HTTP로 통신한다는 것은 심각한 보안 위험이었습니다. 현대 웹 표준에서도 Chrome 등의 브라우저는 HTTP 사이트에 "안전하지 않음" 경고를 표시합니다.

2. IP 주소 직접 노출

현재: http://54.180.125.227:8080
문제: 서버 IP가 그대로 노출되어 공격 대상이 되기 쉬움

도메인 없이 IP를 직접 사용하면 DDoS 공격 등의 위험에 더욱 취약합니다.

3. 확장성 부재

현재 구조:
Client → 단일 EC2 인스턴스 → RDS

문제점:
- EC2 장애 시 전체 서비스 중단
- 트래픽 증가 시 수동으로 서버 추가 필요
- 로드밸런서 없음

단일 장애점(Single Point of Failure)이 존재하고, 사용자가 늘어나면 수동으로 인프라를 확장해야 하는 상황이었습니다.

 

추가적인 문제점인 배포가 불편하여 CI/CD를 구축해야한다...? 정도 됩니다.

 해결책 찾기: Elastic Beanstalk 발견

이런 문제들을 해결하기 위해 여러 방안을 고민하던 중, **AWS Elastic Beanstalk**이라는 PaaS를 알게 되었습니다.

Elastic Beanstalk이란?

개발자가 하는 일

  • 코드 작성
  • JAR 파일 업로드
  • 끝!

Elastic Beanstalk이 자동으로 하는 일 :

  • EC2 인스턴스 프로비저닝
  • 로드밸런서 (ALB) 설정
  • Auto Scaling 그룹 구성
  • 보안 그룹 설정
  • 헬스 체크 및 모니터링
  • 로그 수집
  • 플랫폼 업데이트

비교표

항목 EC2 직접 배포 (ver1) Elastic Beanstalk (ver2)
프로토콜 HTTP HTTPS
접근 방식 IP:8080 도메인 (HTTPS)
로드밸런서 없음 ALB (자동 생성)
스케일링 수동 Auto Scaling
배포 SCP + SSH 콘솔/CLI 업로드
인증서 없음 ACM (무료, 자동 갱신)
고가용성 없음 다중 AZ
월 비용 $0 ~$16 (ALB)

 

사실 여기서 Elastic Beanstalk 만 한다고 해서 비용이 증가하는건 아니고 ALB(Application Load Balence) 에서 비용이 발생합니다.

다른 써드파티를 사용하면 가격을 낮출 수 있지만 지금은 프로덕트를 개발하는 것이 목표이기 때문에 일단 눈물을 머금고 비용을 지출해보록 합시다.

ver2 구축기: Elastic Beanstalk으로의 전환

1단계: 환경 유형 선택의 딜레마

처음에는 비용을 절감하기 위해 "단일 인스턴스" 환경으로 생성했습니다.

하지만 곧 문제를 발견했습니다.

❌ ACM 인증서를 사용할 수 없음!

원인:

  • AWS Certificate Manager (ACM) 인증서는 EC2에 직접 부착 불가능
  • ACM 인증서를 사용하려면 ALB, CloudFront, API Gateway가 필요

구조 비교:

단일 인스턴스 환경:
인터넷 → EC2 (ACM 인증서 부착 불가능)

로드 밸런싱 환경:
인터넷 → ALB (ACM 인증서 부착) → EC2

결정:
HTTPS가 필수이므로 **로드 밸런싱 환경**으로 변경했습니다.

 

💡 참고: 단일 인스턴스에서도 Let's Encrypt를 사용하면 HTTPS를 구성할 수 있지만, 90일마다 수동 갱신이 필요하고 EB 재배포 시마다 재설정해야 하는 번거로움이 있습니다. 하지만 공짜임!!

 

2단계: 기존 RDS 연결

새로 RDS를 만들지 않고 기존 데이터베이스를 그대로 사용하기로 했습니다.

RDS 보안 그룹 수정:

인바운드 규칙 추가:
유형: PostgreSQL
포트: 5432
소스: Elastic Beanstalk 보안 그룹 (sg-xxxxx)

환경 변수 설정

DB_URL=jdbc:postgresql://modu-webtoon-db.c3cw2kggcfv4.ap-northeast-2.rds.amazonaws.com:5432/moduwebtoon
DB_USERNAME=postgres
DB_PASSWORD=[비밀번호]
JWT_SECRET=[시크릿]
SPRING_PROFILES_ACTIVE=prod
SERVER_PORT=5000
⚠️ 주의: `DB_URL`에 `jdbc:postgresql://` 프리픽스를 반드시 포함해야 합니다. 이것을 빼먹어서 502 에러가 발생했던 적이 있습니다.

3단계: 도메인 및 SSL 설정

AWS에서도 Amazon Route 53 이란 도메인 서비스가 있고 쉽게 붙일 수 있지만 가격이 년 12만원일 정도로 굉장히 비싸므로 외부 도메인 대행사에서 구매하였습니다.

도메인 구매:

  • 등록 대행사: Gabia (가비아)
  • 도메인: `moduwebtoon.co.kr`
  • 서브도메인: `api.moduwebtoon.co.kr`
  • 가격: 16,000원/년

DNS 설정 (Gabia):

호스트: api
타입: CNAME
값: modu-webtoon-env.eba-s7cymfrt.ap-northeast-2.elasticbeanstalk.com. (BeanStalk를 만들 시 하나가 생성됩니다.)
TTL: 3600

 

⚠️ 중요: CNAME 값 끝에 점(`.`)을 반드시 붙여야 합니다!

 

ACM SSL 인증서 발급:

  1. AWS Certificate Manager에서 인증서 요청
  2. 도메인: `api.moduwebtoon.co.kr`
  3. 검증 방법: DNS 검증
  4. ACM이 제공하는 CNAME 레코드를 Gabia DNS에 추가
  5. 약 5-10분 후 인증서 발급 완료 ✅

4단계: HTTPS 설정의 난관

첫 번째 시도: Elastic Beanstalk 콘솔에서 HTTPS 리스너 추가

  • 구성 > 로드 밸런서 > 리스너 추가
  • 포트 443, HTTPS, ACM 인증서 선택
  • 결과: "연결할 수 없음" 에러

두 번째 시도: ALB 보안 그룹 확인

  • 문제 발견: 443 포트 인바운드 규칙이 없음!
  • 해결: HTTPS(443) 인바운드 규칙 추가

세 번째 시도: EC2 콘솔에서 직접 HTTPS 리스너 추가

EC2 > 로드 밸런서 > 리스너 추가
프로토콜: HTTPS
포트: 443
SSL 인증서: api.moduwebtoon.co.kr (ACM)


✅ 성공!

5단계: Health Check 문제 해결

배포 후 환경 상태가 Red로 변경되는 문제 발생.
로그 확인:

172.31.9.202 - - "GET / HTTP/1.1" 500 83 "-" "ELB-HealthChecker/2.0"


원인:

  • ALB가 `/` 경로로 Health Check를 시도
  • Spring Boot 앱에는 루트 엔드포인트가 없어서 500 에러

해결:

Health Check Path 변경: / → /api/v1/sections


환경 상태가 Green으로 변경 ✅

6단계: 최종 배포 및 테스트

JAR 빌드:

./gradlew clean build -x test


Elastic Beanstalk 콘솔에서 배포:
- 환경 > 업로드 및 배포
- JAR 파일 선택 및 업로드

🎉 배포 완료! 🎉

 테스트:

curl https://api.moduwebtoon.co.kr/api/v1/sections


결과:

{
  "success": true,
  "data": [...],
  "message": "섹션 목록 조회 성공"
}

최종 아키텍처

┌─────────────────────────────────────────────────────────────┐
│                        AWS Cloud                            │
│                                                             │
│  ┌─────────────────┐                                        │
│  │ Gabia DNS       │                                        │
│  │ api.moduwebtoon │                                        │
│  │ .co.kr          │                                        │
│  └────────┬────────┘                                        │
│           │ CNAME                                           │
│           ▼                                                 │
│  ┌─────────────────────────────────────────────────────┐    │
│  │         Elastic Beanstalk Environment               │    │
│  │                                                     │    │
│  │  ┌─────────────────────────────────────────────┐    │    │
│  │  │   Application Load Balancer (ALB)           │    │    │
│  │  │   - HTTPS:443 (ACM 인증서)                    │    │    │
│  │  │   - Health Check: /api/v1/sections          │    │    │
│  │  └─────────────────┬───────────────────────────┘    │    │
│  │                    │                                │    │
│  │                    ▼                                │    │
│  │  ┌─────────────────────────────────────────────┐    │    │
│  │  │        Auto Scaling Group (Min:1, Max:4)    │    │    │
│  │  │                                             │    │    │
│  │  │  ┌──────────────────────────────────┐       │    │    │
│  │  │  │   EC2 Instance (t2.micro)        │       │    │    │
│  │  │  │   - Java 17 (Corretto)           │       │    │    │
│  │  │  │   - Spring Boot (Port 5000)      │       │    │    │
│  │  │  └──────────────────────────────────┘       │    │    │
│  │  └─────────────────────────────────────────────┘    │    │
│  └─────────────────────┼───────────────────────────────┘    │
│                        │                                    │
│                        ▼                                    │
│  ┌─────────────────────────────────────────────────────┐    │
│  │           RDS PostgreSQL (db.t3.micro)              │    │
│  │   - 데이터베이스: moduwebtoon                          │    │
│  │   - 포트: 5432                                       │    │
│  └─────────────────────────────────────────────────────┘    │
│                                                             │
└─────────────────────────────────────────────────────────────┘

트러블슈팅 회고

구축 과정에서 마주했던 주요 문제들:

1. 502 Bad Gateway

  • 원인: 환경 변수 `DB_URL`에 `jdbc:postgresql://` 프리픽스 누락
  • 해결: 전체 JDBC URL 입력

2. Health Check 실패 (Red 상태)

  • 원인: ALB가 존재하지 않는 `/` 경로로 Health Check
  • 해결: Health Check 경로를 `/api/v1/sections`로 변경

3. HTTPS 리스너 "연결할 수 없음"

  • 원인: Elastic Beanstalk 콘솔의 버그 또는 설정 동기화 문제
  • 해결: EC2 콘솔에서 직접 HTTPS 리스너 추가

4. HTTPS 연결 타임아웃

  • 원인: ALB 보안 그룹에 443 포트 인바운드 규칙 미설정
  • 해결: 보안 그룹에 HTTPS(443) 인바운드 규칙 추가

5. Gabia CNAME 검증 오류

  • 원인: CNAME 레코드 값 끝에 점(`.`) 누락
  • 해결: CNAME 값 끝에 `.` 추가 (예: `example.com.`)

개선 효과

Before (ver1)

❌ HTTP 평문 통신 (보안 취약)
❌ IP 주소 직접 노출
❌ 단일 장애점 존재
❌ 수동 스케일링
❌ 불편한 배포 프로세스
✅ 비용: $0


After (ver2)

✅ HTTPS 보안 통신
✅ 도메인 사용 (api.moduwebtoon.co.kr)
✅ ALB를 통한 고가용성
✅ Auto Scaling 지원
✅ 간편한 배포 (JAR 업로드만)
✅ ACM 자동 인증서 관리
💰 비용: ~$16/월 (ALB)

마치며

처음에는 비용 절감을 위해 가장 단순한 EC2 직접 배포 방식을 선택했지만, 운영 환경의 요구사항(보안, 안정성, 확장성)을 충족하기 위해 Elastic Beanstalk으로 전환하게 되었습니다.

월 $16의 추가 비용이 발생하지만...

저는 인프라에 대해 모르니 하나하나 뜯어가며 개선해나가야 할거같습니다!