1.개인 프로젝트를 위한 초간단 CI/CD 파이프라인 (GitHub Actions + EC2)

이 방법의 핵심 흐름은 매우 간단합니다.
git push → GitHub Actions가 푸시 감지 → EC2에 SSH 원격 접속 → git pull 실행 및 서버 재시작
GitHub Actions가 직접 EC2 서버에 접속해서 최신 코드를 받아오게 하는 방식입니다. 이 방식의 장점과 단점은 명확합니다.
👍 장점
- 🚀 빠른 배포 속도: 전체 프로젝트를 압축해서 전달하는 방식이 아닙니다. git pull을 활용해 변경된 코드만 업데이트하므로 배포 속도가 매우 빠릅니다.
- ⚙️ 간단한 인프라 구조: CI/CD를 위해 필요한 도구는 GitHub Actions 하나뿐입니다. 별도의 Jenkins 서버 등을 구축할 필요가 없어 구조가 복잡하지 않고 간단합니다.
👎 단점 및 주의사항
- ⚠️ 운영 서버 리소스 사용: 빌드 작업을 EC2 인스턴스에서 직접 진행합니다. 프로젝트 규모가 커서 빌드에 많은 시간과 리소스가 소모된다면, 빌드가 진행되는 동안 실제 운영 서버의 성능에 영향을 미칠 수 있습니다.
- 🔒 보안상 주의 필요: 이 방식을 사용하려면 GitHub Actions가 EC2에, EC2가 GitHub에 접근할 수 있는 키(Key)나 계정 정보를 저장해야 합니다. 밑에서 언급 할 .git-credentials 문제처럼 보안에 유의해야 합니다.
결론적으로 이 방법은 개인 프로젝트나 신뢰할 수 있는 팀원들과 진행하는 토이 프로젝트에서 속도와 편의성을 챙길 수 있는 훌륭한 선택지입니다. 하지만 실제 고객에게 서비스하는 상용 프로젝트라면, 빌드와 배포 환경을 분리하는 등 더 정교하고 안전한 방법을 고려하는 것이 좋습니다.
2. 내 개인 프로젝트를 위한 가장 간단한 CI/CD 구축법 (feat. GitHub Actions)
프로젝트에 기능을 추가하고 git push를 마쳤을 때, 배포를 위해 또다시 EC2 서버에 접속해서 명령어를 입력하고 계신가요? 이 반복적이고 귀찮은 과정을 자동화하는 CI/CD, 개인 프로젝트 수준에서는 어떻게 구축하는 것이 효율적일까요?
수동 배포의 문제점과 함께, 개인 프로젝트에서 많이 사용하는 간단한 GitHub Actions CI/CD 구축 방법의 명확한 장단점을 알아보겠습니다.
CI/CD 도입 전: 불안하고 번거로운 수동 배포 과정
CI/CD를 적용하기 전, 우리는 EC2 인스턴스에 직접 접속해 다음과 같은 과정을 거쳐 배포합니다.
1. 최초 배포 과정
- EC2 인스턴스에 접속하여 프로젝트를 git clone 합니다.
- ./gradlew clean build 명령어로 프로젝트를 빌드합니다.
- nohup java -jar <jar 파일> & 명령어를 통해 백그라운드에서 서버를 실행시킵니다.
2. 업데이트 시 반복 작업 기능을 수정하고 git push를 할 때마다 다시 EC2에 접속해서 아래 작업을 반복해야 합니다.
- git pull 명령어로 최신 코드를 받아옵니다. (이때 GitHub 아이디와 Access Token을 입력해야 합니다.)
- 다시 프로젝트를 빌드합니다. (./gradlew clean build)
- 기존에 실행되던 서버를 종료하고 다시 실행합니다.
매번 아이디와 토큰을 입력하는 것이 번거로워 git config --global credential.helper store 명령어로 로그인 정보를 저장하곤 합니다. 하지만 이것은 매우 위험한 방법입니다.
EC2 인스턴스의 최상위 경로(cd ~)에서 숨겨진 파일을 포함해 확인(ls -a)해보면, .git-credentials 라는 파일이 생성된 것을 볼 수 있습니다.

이 파일의 내용을 cat .git-credentials 명령어로 열어보면, 내 GitHub 계정 정보와 토큰 값이 그대로 노출됩니다. 😱 만약 서버가 해킹당한다면 GitHub 계정까지 탈취당할 수 있는 심각한 보안 문제입니다. 이러한 번거로움과 보안 문제 때문에 우리는 CI/CD를 도입해야 합니다.
CI/CD 도입후: 자동화의 핵심, GitHub Actions 워크플로우(.yml) 파헤치기
CI/CD 파이프라인을 구축하기로 마음먹었다면, 이제 GitHub Actions에게 일을 시킬 차례입니다.
이전 장에서 처럼 바로 .github/workflows 폴더 안에 위치한 deploy.yml 파일입니다.

# 워크플로우의 이름. GitHub Actions 탭에 이 이름이 표시됩니다.
name: Deploy To EC2
# 언제 이 워크플로우를 실행할 것인지 결정하는 '트리거' 설정
on:
push:
branches:
# main 브랜치에 push 이벤트가 발생했을 때 실행됩니다.
- main
# 워크플로우에서 실행될 실제 작업들의 모음
jobs:
# 'Deploy'라는 이름의 작업 정의
Deploy:
# 이 작업을 실행할 가상 머신(Runner)의 종류를 지정합니다.
# 'ubuntu-latest'는 GitHub이 제공하는 최신 우분투 환경을 의미합니다.
runs-on: ubuntu-latest
# 작업 안에서 실행될 단계(Step)들의 목록
steps:
# 첫 번째 스텝: EC2 서버에 SSH로 원격 접속하기
- name: SSH(원격 접속)로 EC2에 접속하기
# appleboy/ssh-action@v1.0.3 이라는 마켓플레이스의 액션을 사용합니다.(위에 사진참고)
# SSH 접속 및 원격 명령어 실행을 매우 쉽게 해주는 고마운 도구입니다.
uses: appleboy/ssh-action@v1.0.3
# 위에서 uses로 지정한 액션에 필요한 설정값들을 전달합니다.
with:
# [필수] EC2 인스턴스의 public IP 또는 도메인 주소
host: ${{ secrets.EC2_HOST }}
# [필수] EC2 인스턴스에 접속할 사용자 이름 (예: ubuntu, ec2-user)
username: ${{ secrets.EC2_USERNAME }}
# [필수] EC2 인스턴스 접속에 필요한 Private Key (키페어)
key: ${{ secrets.EC2_PRIVATE_KEY }}
# [옵션] 스크립트 실행 중 오류가 발생하면 즉시 중단합니다.
script_stop: true
# [필수] 원격 접속 성공 후 실행할 명령어들의 목록
# (run 기능인데 이 라이브러리는 script로 적으라는뜻)
script: |
cd /home/ubuntu/instargram-server
git pull origin main
./gradlew clean build
sudo fuser -k -n tcp 8080 || true
nohup java -jar build/libs/*-SNAPSHOT.jar > ./output.log 2>&1 &
🔐 복습!! 잠깐, ${{ secrets.XXX }} 이건 뭔가요?
워크플로우 파일에 IP 주소나 비밀번호, Private Key 같은 민감한 정보를 그대로 적는 것은 매우 위험합니다. 이 정보를 안전하게 관리하기 위해 GitHub Secrets 기능을 사용합니다.
GitHub 저장소의 Settings > Secrets and variables > Actions 메뉴에서 값을 등록하면, 워크플로우 파일 내에서 ${{ secrets.등록한_이름 }} 형태로 안전하게 불러와 사용할 수 있습니다.
키페어는 EC2 인스턴스 생성때 파일로 다운받는데, 인스턴스이름.pem 파일을 cat 명령어던 다른 방법으로 열고 맨밑
— END RSA PRIVATE KEY — 포함해서 맨위에
— BEGIN RSA PRIVATE KEY — 여기까지 선택후 복사하고, 이제 깃허브 설정 스크릿키 추가에 키페어 추가시 해당값에 이거를 복붙해주면 됩니다.
⚙️ script 명령어 상세 분석
script 부분은 자동 배포의 핵심 로직입니다. EC2 서버에서 한 줄 한 줄 실행되는 명령어들의 의미는 다음과 같습니다.
- cd /home/ubuntu/instargram-server
- EC2 서버 내에 미리 클론해둔 프로젝트 폴더로 이동합니다.
- git pull origin main
- GitHub 저장소의 main 브랜치로부터 최신 코드 변경사항을 받아옵니다.
- ./gradlew clean build
- pull 받은 최신 코드를 포함하여 프로젝트를 다시 빌드합니다. (기존 빌드 파일을 삭제하고 새로 만듭니다.)
- sudo fuser -k -n tcp 8080 || true
- 새로운 버전의 서버를 실행하기 전, 기존에 실행 중이던 서버를 종료하는 명령어입니다.
- fuser -k -n tcp 8080: 8080 포트를 사용하고 있는 프로세스를 찾아 종료시킵니다.
- || true: 이게 핵심입니다! 만약 실행 중인 서버가 없어서 8080 포트를 사용하는 프로세스가 없다면, fuser 명령어는 실패(오류)를 반환합니다. 이때 || true가 없으면 전체 워크플로우가 오류로 중단됩니다. 이 코드를 추가함으로써 "프로세스가 없어서 종료에 실패해도 괜찮으니 그냥 넘어가 줘" 라고 알려주는 역할을 합니다.
- nohup java -jar build/libs/*-SNAPSHOT.jar > ./output.log 2>&1 &
- 드디어 새로 빌드한 프로젝트를 실행하는 명령어입니다. 여러 옵션이 붙어있습니다.
- nohup ... &: No Hang Up. SSH 접속이 끊겨도(즉, GitHub Actions 작업이 끝나도) 서버가 계속해서 백그라운드에서 동작 하도록 만들어줍니다.
- > ./output.log: 서버가 실행되면서 발생하는 모든 로그(출력)를 output.log 파일에 기록하라는 의미입니다.
- 2>&1: 에러가 발생했을 때의 로그(2)도 일반 로그(1)가 기록되는 output.log에 함께 기록하라는 의미입니다. 이렇게 해야 에러 추적이 용이합니다.
이제 이 워크플로우 파일을 저장소에 push 해두면, 앞으로 main 브랜치에 코드를 push할 때마다 이 모든 과정이 자동으로 실행됩니다. 🚀
3.🛡️ 민감한 설정 정보, 안전하고 똑똑하게 배포하기 (.gitignore와 GitHub Secrets 활용)
데이터베이스 접속 정보, API 키 등 민감한 정보가 담긴 application.yml(또는 .properties) 파일을 GitHub에 그대로 올리는 것은 보안상 매우 위험합니다. 그래서 보통 이런 설정 파일은 .gitignore에 추가해 버전 관리에서 제외하죠.
하지만 여기서 문제가 발생합니다. .gitignore 처리된 파일은 git pull을 받아도 서버에 존재하지 않으니, 배포 자동화가 끊기게 됩니다. 결국 서버에 직접 접속해서 파일을 수동으로 만들거나 수정해야 할까요?
아닙니다! GitHub Secrets와 워크플로우 수정을 통해 이 문제를 아주 깔끔하게 해결할 수 있습니다.
워크플로우(.yml) 수정: Secret을 환경변수로 주입하기
핵심은 민감한 application.yml 파일의 내용 전체를 GitHub Secrets에 등록하고, CI/CD가 진행될 때 이 값을 서버에 전달해 동적으로 파일을 생성하는 것입니다.
먼저, GitHub 저장소의 Settings > Secrets and variables > Actions에서 APPLICATION_PROPERTIES 라는 이름으로 application.yml 파일의 내용을 그대로 복사-붙여넣기하여 Secret을 생성합니다.
(추후 새로운 기능 개발이나 수정후 yml에 새로운 설정 코드가 생기면 해당 시크릿키 연필 모양 아이콘 클릭후, 이전 적힌 설정값 포함해서 복붙한다)


그다음, 아래와 같이 .yml 워크플로우 파일을 수정합니다.
jobs:
Deploy:
runs-on: ubuntu-latest
steps:
- name: SSH로 EC2에 접속하기
uses: appleboy/ssh-action@v1.0.3
# 🔑 GitHub Secret을 GitHub Actions 환경변수로 등록
env:
APPLICATION_PROPERTIES: ${{ secrets.APPLICATION_PROPERTIES }}
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_USERNAME }}
key: ${{ secrets.EC2_PRIVATE_KEY }}
# 🔑 등록된 환경변수를 EC2 원격 스크립트에서 사용할 수 있도록 전달
envs: APPLICATION_PROPERTIES
script_stop: true
script: |
cd /home/ubuntu/instargram-server
# 혹시 모를 기존 설정 파일을 삭제하여 항상 새로운 상태를 유지
rm -rf src/main/resources/application.yml
git pull origin main
# 🤫 GitHub Secret으로 등록한 내용을 application.yml 파일로 생성
echo "$APPLICATION_PROPERTIES" > src/main/resources/application.yml
./gradlew clean build
sudo fuser -k -n tcp 8080 || true
nohup java -jar build/libs/*-SNAPSHOT.jar > ./output.log 2>&1 &
추가된 코드 상세 분석
1. env 와 envs 설정
- env: APPLICATION_PROPERTIES: ${{secrets.APPLICATION_PROPERTIES}}
- appleboy/ssh-action 라이브러리가 GitHub Secret을 EC2 인스턴스로 전달하기 위해 사용하는 설정입니다.
- 먼저 env 블록을 통해 GitHub Actions의 실행 환경 자체에 APPLICATION_PROPERTIES라는 환경 변수를 만듭니다. 그 값은 우리가 GitHub Secrets에 저장해 둔 내용({{ secrets.APPLICATION_PROPERTIES }})입니다.
- envs: APPLICATION_PROPERTIES
- with 블록 안의 envs는 env에서 선언한 환경 변수 중, 실제 EC2 원격 서버로 전달할 변수를 지정하는 역할을 합니다.
2. script 명령어 분석: 설정 파일 동적 생성
- rm -rf src/main/resources/application.yml
- 배포 안정성을 위해, git pull을 받기 전에 혹시라도 남아있을 수 있는 이전 버전의 application.yml 파일을 확실하게 삭제합니다.
- echo "$APPLICATION_PROPERTIES" > src/main/resources/application.yml
- 이번 업데이트의 핵심입니다!
- echo 명령어는 뒤따라오는 문자열을 출력합니다. $APPLICATION_PROPERTIES는 envs를 통해 전달받은 환경 변수, 즉 GitHub Secret에 저장했던 application.yml의 내용 전체를 의미합니다.
- > 기호는 echo로 출력된 내용을 파일로 저장하는 '리다이렉션(Redirection)' 역할을 합니다.
- 결론적으로, 이 한 줄은 GitHub Secret 값을 내용으로 하는 application.yml 파일을 EC2 서버의 지정된 경로에 실시간으로 생성하는 마법 같은 명령어입니다.
이제 우리는 민감한 설정 파일을 코드 저장소에 노출하지 않으면서도, git push 한 번으로 완벽하게 자동화된 배포 파이프라인을 유지할 수 있게 되었습니다. ✨
'CICD' 카테고리의 다른 글
| 3. 일반 프로젝트에 CI/CD 구축법(깃허브 액션에서 빌드/테스트) (0) | 2025.09.03 |
|---|---|
| 1. CI/CD의 개념,기본 문법 (0) | 2025.09.03 |