나는 EC2환경에서 nextjs기반의 프론트 프로젝트와 spring boot기반의 백엔드 프로젝트를 동시에 배포하고있다.
비용적인 이유가 가장 크다.
EC2에서의 배포는 Git을 통해 소스코드를 pull받은 뒤에 서버에서 빌드를 돌리려고 했다.
프론트 프로젝트는 가능했지만, 백엔드 서버의 경우 서버의 성능이 따라주지 못해 로컬 빌드 후 .jar파일을 EC2로 SCP를 통해 옮긴 후 nohup java -jar /home/ec2-user/app/golrajo-0.0.1-SNAPSHOT.jar > app.log 2>&1 & 명령어를 통해 배포하고있었다.
하지만 이는 너무 비효율적인 프로세스이므로 개선해보려고한다.
CI/CD란?
CI/CD는 Continuous Integration(지속적 통합)과 Continuous Delivery(지속적 전달) 또는 Continuous Deployment(지속적 배포)의 약자이다.
Continuous Delivery (지속적 전달)
- 운영 환경에 배포할 수 있는 상태로 자동 준비되지만,
- 실제 배포는 사람이 수동으로 승인함
Continuous Deployment (지속적 배포)
- 테스트를 통과한 코드를 자동으로 운영 환경까지 배포함
- 개발자의 개입 없이도 빠르게 릴리스 가능
대표적은 CI/CD로 도구로 젠킨스가 있다. 하지만 나는 바르게 CI/CD 과정을 경험해보는것이 목표이므로 GitHub Actions를 사용할 예정이다.
GitHub Actions로 대략적인 플로우를 익힌 후 젠킨스로 넘어가도록 해보자.
GitHub Actions 개요
GitHub Actions는 GitHub에서 제공하는 워크플로 자동화 도구로
코드 푸시, PR, 태그 생성 등 이벤트를 기준으로 빌드, 테스트, 배포를 자동으로 수행할 수 있다.
구현 과정
yaml 파일 생성
name: Spring Boot CI/CD
on:
push:
branches: [ "main" ] # main 브랜치에 push될 때 실행
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: 코드 체크아웃
uses: actions/checkout@v3
- name: JDK 17 설정
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: Gradle 권한 설정
run: chmod +x ./gradlew
- name: 빌드 & 테스트
run: ./gradlew build
- name: 서버에 배포
uses: appleboy/scp-action@v0.1.4
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SERVER_SSH_KEY }}
source: "build/libs/*.jar"
target: "/home/ubuntu/app"
- name: 서버에서 실행
uses: appleboy/ssh-action@v0.1.6
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SERVER_SSH_KEY }}
script: |
export PATH=$PATH:/home/ubuntu/.nvm/versions/node/v23.11.0/bin
pm2 delete spring-app || true
pm2 start java --name spring-app -- -jar /home/ubuntu/app/build/libs/andyou-
script : |
YAML에서 |는 멀티라인 문자열을 작성할 때 사용하는 문법이다.
GitHub Actions는 .github/workflows 폴더에 .yml 파일로 정의한다.
예를 들어 springboot-deploy.yml 파일을 만들어 볼 수 있다.
├── build.gradle / pom.xml
├── src/
├── build/libs/your-app.jar <-- 빌드 결과물
└── .github/workflows/springboot-deploy.yml
springboot-deploy.yml파일을 위와같은 위치에 배치시킨다.
GitHub Secrets 설정
| 이름 | 설명 |
|---|---|
SERVER_HOST |
서버 ip (예: 13.124.56.78) |
SERVER_USER |
서버 사용자 (예: ec2-user) |
SERVER_SSH_KEY |
ssh 개인 키 (PEM 또는 OpenSSH 포맷) |
다음으로 GitHub에서 EC2로 연결될 수 있도록 키값을 넣어줘야한다.GitHub Repository > Settings > Secrets and variables > Actions에서 다음 정보들을 등록해준다.
로컬에서 SSH 키 생성
이 때 ssh 통신을 위해 개인키를 발급해야한다.
ssh-keygen -t rsa -b 4096 -C "github-actions"
~/.ssh/id_rsa (개인키)~/.ssh/id_rsa.pub (공개키)
명령어 실행을 통해 개인키/공개키 쌍을 생성했다.
EC2 서버에 공개키 등록
ssh -i your-aws-key.pem ec2-user@<EC2_PUBLIC_IP>
ssh 명령어를 통해 EC2에 접속 후 공개키를 복사 후 ~/.ssh/authorized_keys에 저장한다.
GitHub에 개인키 등록
Settings > Secrets and variables > Actions > New repository secret 메뉴에서 개인키를 등록할 수 있다.
key : SERVER_SSH_KEY
value : id_rsa 전체 내용을 복사해서 붙여넣기 (-----BEGIN RSA PRIVATE KEY----- ~ END까지 전부)
CI/CD Flow
main브랜치에 푸시- GitHub Actions가 실행됨
- Gradle로 빌드 및 테스트
.jar파일을 EC2 서버로 전송- EC2 서버에서 실행 중인 앱 종료 후 재시작

이를 통해 배포 프로세스를 조금 더 개선할 수 있었다.
하지만 위의 빌드는 실패로 끝났는데 그 이유는
GithubActions에서 `nohup java -jar /home/ubuntu/app/golrajo-0.0.1-SNAPSHOT.jar.jar > app.log 2>&1 &`(원래 명령어) 명령어 수행 후 전체 SSH 세션을 종료하면서 nohup으로 실행한 프로세스까지 강제로 종료되어 실패했다.
이후에 pm2를 사용하는것으로 대체하여 해결할 수 있었다.
ubuntu@ip-172-31-43-125:~/app/build/libs$ which pm2
/home/ubuntu/.nvm/versions/node/v23.11.0/bin/pm2
expert한 부분은 GitHub Actions에서 pm2를 못찾길래 넣어줬다.

'Web' 카테고리의 다른 글
| Nextjs + GitHub Actions 으로 CI/CD 적용하기 (0) | 2025.04.07 |
|---|---|
| Route 53 적용하기 (0) | 2025.04.06 |
| IoC, DI, 컨테이너 (0) | 2022.03.29 |
| AppConfig (설정 정보) (0) | 2022.03.29 |
| 싱글톤 패턴 (0) | 2022.03.29 |