-
[SpringBoot] 10주차 AWS 클라우드 배포, CI/CD 자동화 및 HTTPS 무중단 보안 적용Study/SpringBoot 2026. 6. 10. 12:05

로컬(내 컴퓨터)을 벗어나 전 세계에서 접속할 수 있는 '클라우드 환경'에 프로젝트를 띄웠다. 단순히 서버에 코드를 올리는 것을 넘어, 코드를 수정하고 GitHub에 푸시(Push)만 하면 알아서 서버에 반영되는 CI/CD 자동화와 보안을 위한 HTTPS 적용까지 백엔드 배포의 전체 라이프사이클을 완성해 보았다.
이번 주차 공부 내용
- Infrastructure: AWS EC2(서버) 및 RDS(데이터베이스) 생성
- CI/CD: GitHub Actions를 활용한 빌드 및 자동 배포 파이프라인 구축
- Security: 도메인 연결 및 HTTPS(SSL) 인증서 적용
- Documentation: 프로젝트 아키텍처 및 README 정리
기능 구현 Flow (코드 및 인프라 로직)
Step 1. AWS 클라우드 인프라 구축: 서버와 DB 독립시키기 (EC2 & RDS)
지금까지는 내 컴퓨터(로컬)가 서버이자 DB 역할을 했지만 이제는 24시간 꺼지지 않는 외부 클라우드 환경으로 프로젝트를 이주시켜야 한다. AWS를 활용해 가상 서버와 관리형 데이터베이스를 구축했다.
- EC2 생성: 스프링 부트 애플리케이션을 띄울 가상의 리눅스(Ubuntu) 컴퓨터를 임대하고 탄력적 IP(고정 IP)를 할당했다.
- RDS 생성: MySQL이 설치된 클라우드 전용 DB를 생성했다. 내 컴퓨터의 DB가 아니므로 스프링 부트가 이 외부 DB를 찾아갈 수 있도록 application.yml의 DB 접속 정보를 변경했다.
# application.yml 중 일부 (클라우드 환경 설정) spring: datasource: # localhost가 아닌 AWS RDS가 부여한 고유 엔드포인트 주소로 변경 url: jdbc:mysql://my-rds-database.xxxxxx.ap-northeast-2.rds.amazonaws.com:3306/mydb username: admin password: mypasswordAWS EC2와 RDS란?
- EC2 (Elastic Compute Cloud): 클라우드에서 빌려 쓰는 빈 깡통 컴퓨터다. 여기에 자바를 깔고 내 .jar 파일을 실행하면 전 세계 어디서든 접속할 수 있는 서버가 된다.
- RDS (Relational Database Service): 단순히 DB만 띡 빌려주는 것이 아니라 주기적인 백업, 버전 업데이트, 장애 복구 등을 AWS가 알아서(Managed) 관리해 주는 아주 편리한 데이터베이스 전용 서비스다.
Step 2. GitHub Actions로 CI/CD 무인 자동화 구축
매번 코드를 수정할 때마다 로컬에서 빌드 -> 파일 전송 -> 기존 서버 종료 -> 새 서버 실행을 수동으로 하는 것은 끔찍한 일이다. 이를 해결하기 위해 GitHub Actions를 도입하여 파이프라인을 구축했다.
- CI (지속적 통합): main 브랜치에 코드가 Push 되면 GitHub의 가상 컴퓨터가 코드를 내려받아 테스트를 돌리고 .jar 파일로 빌드한다.
- CD (지속적 배포): 빌드된 파일을 EC2 서버로 전송하고 실행 중이던 기존 프로세스를 죽인 뒤 새 파일로 서버를 다시 띄운다.
# .github/workflows/deploy.yml 중 일부 name: Spring Boot CI/CD on: push: branches: [ "main" ] # main 브랜치에 코드가 푸시될 때만 작동 jobs: build: runs-on: ubuntu-latest # GitHub이 제공하는 가상 리눅스 환경에서 실행 steps: - uses: actions/checkout@v3 - name: Set up JDK 21 uses: actions/setup-java@v3 with: java-version: '21' distribution: 'temurin' - name: Build with Gradle run: ./gradlew build -x test # 테스트 통과 후 빌드 수행CI/CD 자동화란? 개발자가 코드를 짜고 "업로드(Push)" 버튼 하나만 누르면 테스트부터 서버 배포까지의 모든 노동을 GitHub Actions이 대신해 주는 현대 백엔드 개발의 필수 인프라 구성이다.
Step 3. Nginx와 HTTPS를 활용한 보안 적용 및 리버스 프록시
현대 웹 브라우저는 보안되지 않은 HTTP 서버와의 통신을 경고하거나 차단한다. 프론트엔드와 원활하게 통신하고 보안을 강화하기 위해 도메인을 연결하고 HTTPS(SSL)를 부착했다. 이를 위해 스프링 부트 앞단에 Nginx(엔진엑스)라는 웹 서버를 배치했다.
💡 왜 스프링 부트 앞에 굳이 Nginx를 두었을까?
스프링 부트(8080 포트)가 실제 비즈니스 로직을 처리하는 '사무실 직원'이라면, Nginx(80/443 포트)는 건물 1층에 있는 '안내데스크 보안요원'과 같다. 외부 클라이언트는 내부로 바로 직행할 수 없고 반드시 Nginx를 거쳐야 한다.
- 리버스 프록시 (보안 방패): Nginx가 요청을 대신 받아서 뒷단의 스프링 부트로 토스해 준다. 덕분에 스프링 부트의 진짜 위치(포트)와 내부 구조를 외부 해커로부터 완벽하게 숨길 수 있다.
- SSL 인증 해독 (부하 분산): 스프링 부트가 직접 HTTPS 암호화를 풀고 묶으려면 무겁고 귀찮다. 1층 안내데스크(Nginx)에서 암호화 해독(SSL Termination)을 다 끝내버리고, 안전한 데이터만 뒷단으로 넘겨주어 스프링 부트는 본업(비즈니스 로직)에만 집중하게 만들었다.
- 웹 서버 앞세우기: EC2에 Nginx를 설치하여 리버스 프록시로 세팅했다.
- HTTPS 적용: Let's Encrypt를 통해 무료 SSL 인증서를 발급받아 Nginx에 적용했다
# /etc/nginx/sites-available/default 중 일부 server { listen 443 ssl; # HTTPS 기본 포트 server_name my-domain.com; # 구매한 내 도메인 주소 # SSL 인증서 경로 (Let's Encrypt가 자동 생성해줌) ssl_certificate /etc/letsencrypt/live/my-domain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/my-domain.com/privkey.pem; location / { # 443(HTTPS)으로 들어온 트래픽을 내부에 숨어있는 8080(스프링)으로 토스! proxy_pass http://localhost:8080; } }이제 클라이언트는 8080 포트를 알 필요 없이, 안전하게 암호화된 https://my-domain.com 으로만 접근하면 내부적으로 알아서 백엔드 서버와 연결된다.
코드에 사용된 문법(어노테이션) 및 객체 정리
이번 주차는 자바 코드가 아닌 인프라 설정(YAML, NGINX) 및 스크립트 문법이 주를 이루었다.
Step 1. AWS 클라우드 인프라 구축: 무중단 백그라운드 실행
핵심 코드: EC2 내부에서 스프링 부트를 실행하는 쉘 명령어
# 단순 실행(java -jar)의 단점을 극복한 백그라운드 실행 명령어 nohup java -jar build/libs/my-app.jar > app.log 2>&1 &🔍 주요 명령어 분석
- nohup (No Hang Up): 터미널 접속을 끊거나 SSH 창을 닫아도 실행한 프로그램이 종료되지 않고 계속 돌아가도록 잡아주는 명령어다. 서버 배포의 핵심이다.
- > app.log 2>&1: 화면에 출력되어야 할 스프링 부트의 실행 로그들을 텍스트 파일(app.log)에 차곡차곡 저장하라는 뜻이다. 에러(2)와 일반출력(1)을 모두 한 곳에 모은다.
- & (앰퍼샌드): 이 프로세스를 겉 화면이 아닌 백그라운드(Background)에서 조용히 실행하라는 뜻이다.
Step 2. GitHub Actions: 파이프라인 트리거와 시크릿 변수
핵심 코드: GitHub 저장소에 올라가면 안 되는 민감 정보 처리
steps: - name: application.yml 생성 run: | mkdir -p src/main/resources echo "${{ secrets.APPLICATION_YML }}" > src/main/resources/application.yml🔍 주요 문법 및 객체 분석
- on: push: 이 파이프라인(Workflow)이 언제 실행될지 결정하는 방아쇠(Trigger)다. 특정 브랜치에 코드가 합쳐질 때만 작동하도록 제어할 수 있다.
- ${{ secrets.XXX }}: GitHub Actions의 핵심 객체다. DB 비밀번호나 외부 API 키 같은 민감한 정보가 코드에 노출되지 않도록 GitHub Repository Settings에 암호화하여 저장해 둔 변수(Secret)를 꺼내오는 문법이다. 위 코드는 빌드 직전에 깃허브가 변수를 꺼내와 application.yml 파일을 실시간으로 만들어내는 마법 같은 로직이다.
Step 3. Nginx: 리버스 프록시(Reverse Proxy) 라우팅
핵심 코드: Nginx의 서버 블록 설정 문법
location / { proxy_pass http://localhost:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }🔍 주요 문법 및 객체 분석
- proxy_pass: Nginx(웹 서버)가 받은 요청을 내부의 다른 서버(WAS - 스프링 부트)로 전달하는 리버스 프록시의 핵심 지시어다.
- proxy_set_header: 요청을 8080으로 토스할 때 원래 요청을 보낸 클라이언트의 진짜 정보(진짜 IP 등)를 스프링 부트가 알 수 있도록 헤더에 꼬리표를 달아 넘겨주는 필수 설정이다. 이 설정이 없으면 스프링 부트는 모든 요청이 Nginx(localhost)에서 온 것으로 착각하게 된다.
학습 회고
이번 10주 차를 마지막으로 "요구사항 분석 -> DB 설계 -> API 개발 -> 테스트 -> 클라우드 배포"로 이어지는 백엔드 개발의 한 사이클을 완주했다.
특히 마지막 배포 과정에서는 단순히 코드를 짜는 것을 넘어 코드가 돌아갈 '환경(인프라)'을 설계하고 제어하는 법을 배웠다. 무인 자동화 배포(CI/CD) 파이프라인을 구상하고 Nginx로 프록시 서버를 둬보는 등, 아키텍처 관점에서 시스템을 바라보는 시야를 한 단계 넓힐 수 있었던 값진 시간이었다. 앞으로 어떤 프로젝트를 하더라도 스스로 인프라를 구축하고 서비스할 수 있다는 자신감이 생겼다!
'Study > SpringBoot' 카테고리의 다른 글
[SpringBoot] 9주차 테스트 코드(JUnit/Mockito) 자동화 및 Docker 가상화 인프라 배포 (0) 2026.06.06 [SpringBoot] 8주차 외부 API 연동, Redis 연결, 그리고 TTL(Time-To-Live) 설정 (0) 2026.05.28 [SpringBoot] 7주차 게시글 + 댓글 동시 저장 서비스 작성, 예외 케이스 적용, 인덱스 생성 (0) 2026.05.20 [SpringBoot] 6주차 페이지네이션(Pagination), N+1 문제 해결 (0) 2026.05.07 [SpringBoot] 5주차 JWT 인증/인가(회원가입 및 로그인) API 구현 (0) 2026.04.30