이 글에서는 작은 서비스를 Github Action, GHCR을 사용한 간단한 CI/CD 를 구성하는 내용을 담고 있습니다.
요즘 계속 인프라에 대한 간단한 작업들을 하는 것에 관심이 가고 있다. 그래서 기존에 만들어두고 거의 사용하지 않고 있던 Oracle 서버를 통해 무엇을 할 수 있을까? 고민을 하게 되었다.
그래서 먼저 내가 지금 가지고 있는 불편함이 무엇인지 먼저 찾아보기로 했다.
요즘 가장 큰 관심사는 당연 포트폴리오를 만드는 것이다. 프로젝트 관련 내용을 정리하면서 Github 나 Figma 등 링크들을 첨부하려고 했다.
포트폴리오를 만들면서 가장 많이 고민이 되었던 부분은 “가독성” 이었다. 사용성과 사용자 경험에 대해 항상 고민을 했기 때문에 “어떻게하면 내용을 잘 전달할 수 있을까? 가 큰 고민이었다.
첨부하는 링크들 중 많은 구현 화면을 보여주기 위해 Figma 링크를 첨부하려고 했는데, 다음과 같이 첨부 링크가 너무 길게 들어간다는 문제가 있었다.

그래서 이런 고민을 시작했다.
“링크를 어떻게 하면 짧게 줄일 수 있을까? ”
bit.ly 와 같은 변환해주는 사이트(부분 무료)를 이용하는 방법도 있었지만, Domain에 “나의 정체성” 을 담는 것도 면접관님께 어필 할 수 있는 하나의 방법이라는 생각이 들었다.
그래서 내가 가지고 있는 Oracle 서버를 통해 “포트폴리오 링크들을 Redirect 하는 간단한 서비스”를 만들어보기로 했다.
링크를 Redirect 하는 방법을 어떻게 구현하지 정해야했다. 이 기능을 구현하는 생각하는 순간부터 정말 많은 구현 방법이 떠올랐다. React 나 Next.js, 간단한 Node.js, Spring Boot 등 사용할 수 있는 도구는 아주 아주 많이 있었다. 그래서 어떤 방식을 사용할지 딱 정할 기준이 필요했다.
Oracle 서버에서 가지고 있는 인스턴스는 평생 무료 라는 이야기에 넘어가 만들어놓은 상태였다.

A1 코어 24GB 메모리 서버를 사용…했으면 아주 행복했겠지만… 😢 아쉽게도 1GB 메모리 서버를 이용하고 있었다.
가능하다면 해당 서버로 나의 블로그 같은 서비스 함께 올리는 것으로 확장하여 사용하고 싶었지만, 물리적인 한계로 인해 그러지는 못할 것 같았다.
그러기 때문에 “링크 변환” 이라는 작은 기능을 돌리기에는 무거운 프레임워크까지 사용하는 것은 과하다고 판단했다.
가장 비용이 적게 드는 방법으로 “단순 Redirect” 를 선택했다. 서버에서 “딸깍” 처리만 해주면 되기 때문에 아주 쉬운 방법이었다.
사용할 서버로는 가장 많이 다뤄보아서 익숙한 Nginx 를 사용하기로 했다.

conf 파일 만들기conf 파일을 사용해 Nginx 실행하기.*구현 내용을 모두 작성하기에는 블로그의 공간이 부족해 적지 못했다… - 박 페르마(?) -
아주 간단하기에 구현 내용은 아래 Github 링크에 들어가서 확인바랍니다. 😉
https://link.scent-jo.me/portfolio-redirects/github
모든 구현을 마쳤으니 이제 CI/CD 를 구성해보자. 이것의 목표를 간단하게 한문장으로 정의하면 다음과 같다.
“링크 데이터 (YAML 파일)을 수정할 때마다 서버에 자동으로 반영되도록 한다.”
CI 는 Continuous Integration 의 약자로 직역하면 “지속적인 통합” 이라고 할 수 있다.
여기서 말하는 “지속적인 통합” 은 서비스에 새로운 변경 사항이 생겼을 때, 정기적으로 빌드 및 테스트 되어 Repository에 통합하는 것을 의미한다.
즉, 테스트와 빌드를 통해 서비스가 잘 동작하는지를 확인 후 병합하는 과정의 자동화라고 생각하면 된다.
이 과정은 혼자 개발할 때보다 다수의 개발자가 함께 개발할 때 큰 의미를 가지게 된다.
CD 는 Continuous Delivery 혹은 Continuous Deployment 용어의 축약어 입니다. 해석하면 “지속적인 서비스 제공, “지속적인 배포” 라고 한다.
CI를 통해 서비스가 Repository 에 잘 통합 되도록 하였다면, CD 는 Repository를 넘어서 Production 환경까지 Release 되는 것을 의미한다.
간단하게 말해, “실제로 운영되는 환경에 자동으로 반영되게 한다!” 라고 생각하면 좋다.

이 글에서는 CI/CD에 대해 심플하게만 설명하고 넘어간다.
예전 SSAFY 에서 경험해본 방식은 Jenkins를 통해 CI/CD 파이프라인을 작성하는 것이었다.
하지만, Jenkins를 구동할 서버가 필요하다는 점에서 과하다는 생각이 들어, 더 쉬운 선택지인 Github Action을 사용하기로 했다.
사용한 기술
- Github Action
- Docker
- Github Container Registry(GHCR)
————CI—————
————CD————

초기 CI/CD을 기본적인 내용을 빠르게 작성하고 싶어서 아주 간단하게 구성해보았다.
사실 여기에 빠진 테스트, Health Check, 로그 확인, 롤백 등 이 들어가야만 진정한 CI/CD 라고 할 수 있다.
해당 내용들은 추후 천천히 추가하여 업그레이드 해보겠다.

위에서 설계한 내용을 바탕으로 CI / CD 를 한번에 구성하는 Github Action 워크 플로우를 작성해보자.
내가 작성한 Repository에 .github/workflows/ci-cd.yml 을 작성해주면 된다.
main 브랜치에 Push 할 때 실행workflow_dispatch)
name: 🚀 간단한 배포
on:
push:
branches: main
workflow_dispatch:
permissions:
contents: read
packages: write
cache: "pnpm"으로 의존성 캐시를 저장해 빌드 속도 향상jobs:
deploy:
name: 빌드 및 배포
runs-on: ubuntu-latest
steps:
- name: 1. 체크아웃
uses: actions/checkout@v4
- name: 2. pnpm 설치
uses: pnpm/action-setup@v4
with:
version: latest
- name: 3. Node.js 설정
uses: actions/setup-node@v4
with:
node-version: "22"
cache: "pnpm"
nginx.conf를 스크립트로 생성--frozen-lockfile → lockfile 에 정의된 버전 그대로 의존성 설치
- name<: 4. 의존성 설치 및 nginx.conf 생성
run: |
pnpm install --frozen-lockfile
pnpm generate
if [ ! -f "nginx.conf" ]; then
echo "❌ nginx.conf 파일이 생성되지 않았습니다!"
exit 1
fi
echo "✅ nginx.conf 파일이 성공적으로 생성되었습니다."
${{ github.actor }}는 워크플로 실행자의 GitHub ID${{ secrets.GITHUB_TOKEN }}은 GitHub에서 자동 생성된 인증 토큰- name: 5. GHCR 로그인
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
→ 여기서 사용되는 actor, GITHUB_TOKEN 변수들은 따로 설정하지 않아도 Github에서 알아서 설정해준다.
buildx를 설정 (Docker 공식 Action 사용)Dockerfile 을 기반으로 현재 위치에서 이미지 빌드 (Docker 공식 Action 사용):sha):latest)cache-from / cache-to: GitHub Actions 캐시 사용으로 빌드 속도 개선
- name: 5.1 Docker Buildx 설정
uses: docker/setup-buildx-action@v3
- name: 6. 이미지 빌드 및 푸쉬
uses: docker/build-push-action@v6
id: build
with:
context: .
push: true
tags: |
ghcr.io/${{ github.repository }}:${{ github.sha }}
ghcr.io/${{ github.repository }}:latest
cache-from: type=gha
cache-to: type=gha,mode=max
appleboy/ssh-action@v1.2.2 Action을 사용하여 Oracle 서버에 SSH 로 접속하기


- name: 6.1 변수 설정
run: |
echo "IMAGE_NAME=ghcr.io/${{ github.repository }}:${{ github.sha }}" >> $GITHUB_ENV
echo "CONTAINER_NAME=nginx-redirect" >> $GITHUB_ENV
- name: 7. 서버 배포
uses: appleboy/ssh-action@v1.2.2
with:
host: ${{ secrets.ORACLE_SERVER_IP }}
username: ${{ secrets.ORACLE_SERVER_USERNAME }}
key: ${{ secrets.ORACLE_SERVER_KEY }}
script: |
set -e # 오류 발생 시 바로 종료
# GHCR 로그인
echo "${{ secrets.GITHUB_TOKEN }}" | sudo docker login ghcr.io -u ${{ github.actor }} --password-stdin
# 이전 컨테이너 정리
sudo docker stop ${{ env.CONTAINER_NAME }} || true
sudo docker rm ${{ env.CONTAINER_NAME }} || true
sudo docker image prune -f
# 새 이미지 배포
sudo docker pull ${{ env.IMAGE_NAME }}
sudo docker run -d --name ${{ env.CONTAINER_NAME }} --network nginx-proxy-manager_default -p 8080:80 ${{ env.IMAGE_NAME }}
# 배포 확인
sleep 3
sudo docker ps | grep ${{ env.CONTAINER_NAME }}
sudo docker logs --tail 30 ${{ env.CONTAINER_NAME }} || true
# GHCR 로그아웃
docker logout ghcr.io || true
위 내용 작성 후 Github 저장소 에 반영 하고 나면 다음과 같이 Action이 활성화가 되고, 동작하는 것을 확인할 수 있다.

Redirect 데이터에 이번 프로젝트에 대한 링크도 넣어놓았다.
아래 링크를 통해 잘 동작하는지 확인할 수 있다.
https://link.scent-jo.me/portfolio-redirects/github

이번 프로젝트는 아주 간단하지만, 서버를 다루다보니 재미가 쏠쏠한 시간이었다. 현재 나에게 필요한 것을 구현한 것이다보니 더 몰입해서 할 수 있었던 것 같다.
과정에서 정말 많은 “딸깍” 이 될 정도로 많은 부분에서 오픈소스화 되어있는 것이 Github Action의 크고 큰 장점이 아닌가 싶다.
나도 언젠가는 저런 자동화 서비스를 오픈소스로 만들어보고 싶다는 소망을 품어본다. 😁😁
대부분의 회사에서 CI/CD 를 많이 구축해 놓아 사용하고 있다고 알고 있다. 이 경험이 아주 멋진 자산이 되길 기대한다!!
끝!!
“ 아 CI/CD 전략 고도화는 잊지 않고 꼭 해볼거다! “