Spring Boot κΈ°λ°μ νμ μ 보 κ΄λ¦¬ APIμ λλ€. AWS ν΄λΌμ°λ νκ²½μμ VPC λ€νΈμν¬ κ΅¬μ±, RDS DB μ°λ, S3 νμΌ μ μ₯, ALB + ASG ꡬμ±, GitHub Actionsλ₯Ό ν΅ν CI/CD μλνκΉμ§ λ¨κ³λ³λ‘ ꡬμΆνμ΅λλ€.
| λΆλ₯ | κΈ°μ |
|---|---|
| Language | Java 17 |
| Framework | Spring Boot 4.0.3 |
| ORM | Spring Data JPA / Hibernate |
| DB | MySQL (AWS RDS), H2 (local) |
| Infra | AWS VPC, EC2, RDS, S3, ALB, ASG, CloudFront |
| CI/CD | GitHub Actions, Docker, Docker Hub |
| Config | AWS Parameter Store |
| Monitoring | Spring Actuator |
| Build | Gradle |
src/
βββ main/
β βββ java/com/cloud/
β β βββ CloudApplication.java
β β βββ member/
β β βββ controller/ MemberController.java
β β βββ service/ MemberService.java, S3Service.java
β β βββ repository/ MemberRepository.java
β β βββ entity/ Member.java
β β βββ dto/ MemberRequest.java, MemberResponse.java
β β βββ exception/ GlobalExceptionHandler.java
β βββ resources/
β βββ application.properties # κ³΅ν΅ μ€μ
β βββ application-local.properties # λ‘컬 (H2)
β βββ application-prod.properties # μ΄μ (MySQL + Parameter Store)
βββ Dockerfile
βββ .github/workflows/deploy.yml
μκΈ νν λ°©μ§λ₯Ό μν λΉμ© μλ¦Ό μ€μ
- AWS Budgetsμμ μ μμ° $100 μ€μ
- μμ°μ 80% λλ¬ μ μ΄λ©μΌ μλ¦Ό μμ μ€μ
VPC μ€κ³λΆν° EC2 λ°°ν¬, ν¬μ€μ²΄ν¬κΉμ§
μΈνλΌ
- VPC μμ± λ° Public / Private Subnet λΆλ¦¬
- Public Subnetμ EC2 μΈμ€ν΄μ€ μμ±
API κΈ°λ₯
| Method | URL | μ€λͺ |
|---|---|---|
| POST | /api/members |
νμ μ 보(μ΄λ¦, λμ΄, MBTI) μ μ₯ |
| GET | /api/members/{id} |
νμ μ 보 μ‘°ν |
μ΄μ μ€μ
- Spring Profile λΆλ¦¬:
local(H2) /prod(MySQL) - INFO λ 벨 API μμ² λ‘κ·Έ:
[API - LOG] ... - ERROR λ 벨 μμΈ μ²λ¦¬ + μ€ννΈλ μ΄μ€ λ‘κΉ
- Spring Actuator ν¬μ€μ²΄ν¬ λ ΈμΆ
GET /actuator/health
{"status": "UP"}π₯οΈ EC2 νΌλΈλ¦ IP: 13.125.129.84
RDS λΆλ¦¬ + 보μ κ·Έλ£Ή 체μ΄λ + Parameter Store
μΈνλΌ
- Public Subnetμ MySQL RDS μμ± (λ‘컬 μ μ ν μ€νΈ κ°λ₯)
- 보μ κ·Έλ£Ή 체μ΄λ: RDS Inboundμ IP μ§μ νμ© λμ EC2 보μ κ·Έλ£Ή IDλ§ νμ©
μ€μ
- AWS Parameter Storeμ DB μ μ μ 보 μ μ₯ (
url,username,password,team-name) - μ± μ€ν μ Parameter Store κ° μλ μ£Όμ
Actuator Info νμ₯
GET /actuator/info
{"app": {"team-name": "Team2"}}π Actuator Info URL: https://pposong.kr/actuator/info
πΈ RDS 보μ κ·Έλ£Ή μΈλ°μ΄λ κ·μΉ νλ©΄

S3 + IAM Role + Presigned URL
μΈνλΌ
- S3 λ²ν· μμ± (νΌλΈλ¦ μ‘μΈμ€ μ°¨λ¨ ON)
- IAM Role μμ± β EC2μ μ°κ²° (Access Key μ½λ λ―Έν¬ν¨)
API κΈ°λ₯
| Method | URL | μ€λͺ |
|---|---|---|
| POST | /api/members/{id}/profile-image |
νλ‘ν μ¬μ§ S3 μ λ‘λ |
| GET | /api/members/{id}/profile-image |
Presigned URL λ°ν (μ ν¨κΈ°κ° 7μΌ) |
π Presigned URL:
https://camp-health-pposong-files.s3.ap-northeast-2.amazonaws.com/profiles/6aaf57ff-fa0d-412b-b99f-cfee8c0f966b_2.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ASIASSDNAZWDS5S4ADTL%2F20260313%2Fap-northeast-2%2Fs3%2Faws4_request&X-Amz-Date=20260313T014914Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Security-Token=IQoJb3JpZ2luX2VjELr%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEaDmFwLW5vcnRoZWFzdC0yIkYwRAIgGwyBGbQCpyRNZmG%2F3zN5E%2F6ysrLwoWCao5hkTFZOsN0CIACV7g%2B2oYt83sJudRzNTkz2rUafx0haVxQKKvU3g7JRKtQFCIP%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FwEQABoMMTc2MzIyMzAxMzE5Igzm17YtM4H69uBs0RIqqAWif5pNGmtroPNqqtTZyZ27YGSV9JFM8B%2BVPq2LzCv1BelPaCfBbyD18jTvjsPHB4ca3qRCnPJA5zo8mQKZ1Pb4rQf68HAZq%2BtmgvpvIoorWvB1XL5wXYHFq%2BOUehXb55Pdp8JFnHfLDz7YgsUJ5wS2LoamEDyGcsNFkEMRX62R%2FjBZVlnVT7eoRRal4qA5UD1f66DvvysZCs%2FCLO1CDBm3%2F24gRcW9gASDbY3CfwGOYr529vDGOoqrfZXV1VYl2XXhMCiT%2Fz2QoJpypKZQW9O7zbh9oDUA81aBPAPegoQrJNFZZ6N8M2ZErQAuanJpZnUoRrbHUrZ4RzixD5rHZeE5y1ApRJBgPDY9lFxT064T1UWYFvUUuF6mzqeuUoC%2BySyro9N0NNtAtKUlGR8DyqArGWbaRj2qxNneS%2BEcw95LXASUDRzZR%2F3WnXpArxGiSuoy119WP2jeW9PZL%2FZPNPmP0Cx1uLJm8nSs0zAZMuOUuaX7Cf4PV07sSTz4l7K%2FMFyFERok2OuyIFxNmTlpzSVgMEnvDyepTFs5zqhAS05%2BDhX9qc2%2FUpPm7McoZQJjFAn8qV5RcCEwG63xVRxGavwe10hecRu0EhpZBjzlAXC4KoHhEUmRGKA%2BGhvLIUjz1T1Khx8w9WlL7eBJivMEZqNo20Rd%2FEiolpeythQ8Kc6urJhbFNy6ZtBmjeKvJPXIQjmM4H52RahOWSAjP1lKoFSlhC2jQsTAJ%2FJ7hJ%2BeX%2FGFuD1Or45KnTexIG7WOnKnBqKGoHSaY8POsT9wCr4KF0ZXirp8acMi7f6VyK40o8lMpiFwQ8%2Bojn0Mt3ZIQzZRlo3%2B2mNDsTvHyOTyo44MC0sPUNd%2BQBgsAQlAtIhS6B3w5V1k3jz%2BUhGA5L2v22aKiCQHu6y1QzQ8SzD70c3NBjqyAVAfqvT6ZNdhSx3w85fGz%2BHw%2F%2BsdZn5xNf79Xclhq3ng0uOZYO2r7gAkTHJSXohoxQS%2FO8EnA%2F8i6%2FMe7EkK5ihFRDazPDEQLPi%2FL7jvZrvxCDXAmpy1mT6y%2BrpNXV91xY3l9MWA2iGy7RX5Fb3q7bzfrx9%2Bl4Z5h8sRCbES68vjo9MX2z%2FWnKIO42nA38VSuhs8ZPHrLQXt1U26N%2B1gM9JXdpR6%2BL5%2BaZDOaZNsPRtr9Dk%3D&X-Amz-Signature=9507ffffb33d68403a0826c4f35b870edfdf3d6be671879a4f18fd3e2ea85f62
β° λ§λ£ μκ°: 2026λ
3μ 20μΌ

Dockerfile μμ± + GitHub Actions μλ λ°°ν¬
Docker
Dockerfileμμ± (Amazon Corretto 17 κΈ°λ°)app.jarλΉλ ν μ΄λ―Έμ§ν
GitHub Actions (deploy.yml)
- main λΈλμΉ Push μ μλ νΈλ¦¬κ±°
- Gradle λΉλ (
bootJar) - Docker μ΄λ―Έμ§ λΉλ β Docker Hub Push
- AWS μ격μ¦λͺ μ€μ
- ASG Instance Refreshλ‘ EC2 μλ μ¬λ°°ν¬
πΈ GitHub Actions μ±κ³΅ νλ©΄

πΈ EC2 sudo docker ps μ€ν νλ©΄

NAT Gateway + ALB + ASG + ACM
μΈνλΌ λ³κ²½
- NAT Gateway μμ± (Public Subnet) β Private Subnet λΌμ°ν μ€μ
- RDS & EC2 β Private SubnetμΌλ‘ μ΄μ (λ‘컬 μ§μ μ μ λΆκ°)
λλ©μΈ & 보μ
- Cloudflareμμ λλ©μΈ μ°κ²°
- ACMμμ SSL μΈμ¦μ λ°κΈ
λ‘λ λ°Έλ°μ & μ€ν μ€μΌμΌλ§
- ALB μμ±: HTTPS(443) 리μ€λ + μΈμ¦μ μ μ©, HTTP β HTTPS 리λ€μ΄λ νΈ
- Launch Template μμ± β Auto Scaling Group μμ±
- Cloudflare CNAME β ALB DNS μ°κ²°
π HTTPS λλ©μΈ: https://pposong.kr
πΈ Target Group Healthy μν νλ©΄

S3 + CloudFrontλ‘ κΈλ‘λ² μ΄λ―Έμ§ λ°°ν¬
ꡬμ±
- S3 λ²ν·μ OriginμΌλ‘ CloudFront λ°°ν¬ μμ±
- Presigned URL β CloudFront λλ©μΈ URL λ°©μμΌλ‘ λ³κ²½
π CloudFront μ΄λ―Έμ§ URL:
https://d1v1uch0vgh0vj.cloudfront.net/profiles/6aaf57ff-fa0d-412b-b99f-cfee8c0f966b_2.jpg
| Method | URL | μ€λͺ | μνμ½λ |
|---|---|---|---|
| POST | /api/members |
νμ λ±λ‘ | 201 |
| GET | /api/members/{id} |
νμ μ‘°ν | 200 |
| POST | /api/members/{id}/profile-image |
νλ‘ν μ¬μ§ μ λ‘λ | 200 |
| GET | /api/members/{id}/profile-image |
νλ‘ν μ΄λ―Έμ§ URL μ‘°ν | 200 |
| GET | /actuator/health |
ν¬μ€μ²΄ν¬ | 200 |
| GET | /actuator/info |
ν μ 보 μ‘°ν | 200 |
| Profile | DB | μ©λ |
|---|---|---|
local |
H2 (In-Memory) | λ‘컬 κ°λ° |
prod |
MySQL (RDS) | μ΄μ λ°°ν¬ |
/camp-health-app/prod/
βββ spring.datasource.url
βββ spring.datasource.username
βββ spring.datasource.password
βββ TEAM-NAME
