ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [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: mypassword

     

    AWS 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를 거쳐야 한다.

    1. 리버스 프록시 (보안 방패): Nginx가 요청을 대신 받아서 뒷단의 스프링 부트로 토스해 준다. 덕분에 스프링 부트의 진짜 위치(포트)와 내부 구조를 외부 해커로부터 완벽하게 숨길 수 있다.
    2. 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로 프록시 서버를 둬보는 등, 아키텍처 관점에서 시스템을 바라보는 시야를 한 단계 넓힐 수 있었던 값진 시간이었다. 앞으로 어떤 프로젝트를 하더라도 스스로 인프라를 구축하고 서비스할 수 있다는 자신감이 생겼다!

제목 없는 코딩 블로그