개발/Git

프로젝트에 GitHub Actions로 CI 구축하기

bokueyo 2025. 4. 2. 13:22

 

최근에 사이드 프로젝트를 만들면서

GitHub Actions를 활용해 자동화된 CI 환경을 구축한 경험을 정리해보려고 합니다.

 


✅ 왜 CI가 필요했을까?

 

프로젝트가 커지고 브랜치가 많아지다 보니 이런 문제가 생기더라고요.

코드 수정할 때마다 직접 build, test를 돌려봐야 하는 번거로움

과거의 내가 만든 코드가 기존 기능을 깨뜨릴 수도 있음

자잘한 수정만 했을 때도 전체 테스트를 돌리는 건 시간 낭비

 

그래서 자연스럽게 이런 생각이 들었습니다.

 

“PR 올리면 자동으로 테스트 돌고, 이상 있으면 merge 못 하게 만들 수 없을까?”

 

➡️ 이걸 해결해줄 수 있는 게 바로 CI (Continuous Integration) 입니다.

 


🤖 왜 GitHub Actions를 선택했을까?

 

CI 도구는 정말 많습니다. Jenkins, Travis CI, CircleCI…

그런데 전 GitHub에서 코드 관리하고 있으니까, GitHub Actions가 가장 자연스럽더라고요.

별도 설치 필요 없이 .yml 파일 하나면 끝!

GitHub PR과 바로 연동됨

무료 사용량도 넉넉

 

즉, 가볍게 시작하고도 꽤 강력하다!

 


🎯 목표: 이런 자동화를 만들고 싶었어요

PR이 열릴 때

변경된 파일 중 테스트가 필요한 경로가 있다면

pnpm install, pnpm build, pnpm test 자동 실행

테스트 실패 시 merge 불가 메시지 출력

 


📂 테스트가 필요한 경로 기준은?

src/components/
src/utils/
src/hooks/
src/context/
src/pages/
src/App.tsx
src/index.tsx

위 경로 중 하나라도 수정되었다면 → 테스트 실행!

아니면 → 테스트 생략!

 


⚙️ GitHub Actions 설정

.github/workflows/ci.yml 경로에 아래와 같이 작성했습니다.

<pre><code class="language-yaml">name: Project CI

on:
  pull_request:
    branches: [ main ]

jobs:
  build-and-test:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v4
      with:
        fetch-depth: 0

    - name: Check changed files
      id: changed_files
      run: |
        git fetch origin main
        CHANGED_FILES=$(git diff --name-only origin/main HEAD)
        echo "Changed files:"
        echo "$CHANGED_FILES"

        # 테스트가 필요한 파일들이 변경되었는지 확인
        NEEDS_TESTING=false
        while IFS= read -r file; do
          case "$file" in
            src/components/*|src/utils/*|src/hooks/*|src/context/*|src/pages/*|src/App.tsx|src/index.tsx)
              NEEDS_TESTING=true
              break
              ;;
          esac
        done <<< "$CHANGED_FILES"

        if [ "$NEEDS_TESTING" = "true" ]; then
          echo "needs_testing=true" >> $GITHUB_OUTPUT
        else
          echo "needs_testing=false" >> $GITHUB_OUTPUT
        fi

    - name: Setup Node.js
      if: steps.changed_files.outputs.needs_testing == 'true'
      uses: actions/setup-node@v4
      with:
        node-version: '20'

    - name: Install pnpm
      if: steps.changed_files.outputs.needs_testing == 'true'
      uses: pnpm/action-setup@v2
      with:
        version: 8

    - name: Install dependencies
      if: steps.changed_files.outputs.needs_testing == 'true'
      run: pnpm install

    - name: Build
      if: steps.changed_files.outputs.needs_testing == 'true'
      run: pnpm build

    - name: Run tests
      if: steps.changed_files.outputs.needs_testing == 'true'
      run: pnpm test -- --passWithNoTests

    - name: Check test status
      if: steps.changed_files.outputs.needs_testing == 'true' && failure()
      run: |
        echo "Tests failed. This PR cannot be merged until all tests pass."
        exit 1
</code></pre>

 

 


🔍 구성 요소 설명

⚙️ GitHub Actions 설정 전체 코드 + 한 줄씩 설명

name: Project CI

이 워크플로우의 이름입니다. GitHub Actions 탭에서 표시되는 이름이에요.

"프로젝트의 CI 자동화"라는 의미로 이름을 붙였어요.

 


 

on:
  pull_request:
    branches: [ main ]

이 워크플로우는 **PR(Pull Request)**이 main 브랜치로 열릴 때 자동으로 실행됩니다.

즉, PR이 생성되거나 업데이트될 때마다 CI가 동작하게 됩니다.

 


jobs:
  build-and-test:
    runs-on: ubuntu-latest

build-and-test라는 **작업(Job)**을 정의합니다.

GitHub이 제공하는 최신 우분투 환경에서 이 작업을 실행합니다.

 


    steps:
    - uses: actions/checkout@v4
      with:
        fetch-depth: 0

GitHub 저장소의 코드를 체크아웃(다운로드)합니다.

fetch-depth: 0은 전체 git 기록을 가져온다는 의미입니다.

→ 나중에 git diff로 변경된 파일을 확인하기 위해 꼭 필요합니다.

 


    - name: Check changed files
      id: changed_files
      run: |

변경된 파일 목록을 확인하는 스크립트를 실행합니다.

id: changed_files → 이 step의 결과를 나중에 if: 조건에서 사용하기 위해 ID를 지정해둔 것입니다.

 


        git fetch origin main
        CHANGED_FILES=$(git diff --name-only origin/main HEAD)

main 브랜치와 현재 PR의 차이점(변경된 파일 목록)을 가져옵니다.

 


        echo "Changed files:"
        echo "$CHANGED_FILES"

디버깅 용도로 어떤 파일이 변경되었는지 로그에 출력합니다.

 


        NEEDS_TESTING=false
        while IFS= read -r file; do

테스트가 필요한 변경이 있는지 확인하기 위한 변수입니다.

 


          case "$file" in
            src/components/*|src/utils/*|src/hooks/*|src/context/*|src/pages/*|src/App.tsx|src/index.tsx)
              NEEDS_TESTING=true
              break
              ;;
          esac
        done <<< "$CHANGED_FILES"

위에 나열된 경로에 속한 파일이 변경되었는지 case 문으로 하나하나 검사합니다.

해당되면 NEEDS_TESTING=true로 설정하고 루프를 종료합니다.

 


        if [ "$NEEDS_TESTING" = "true" ]; then
          echo "needs_testing=true" >> $GITHUB_OUTPUT
        else
          echo "needs_testing=false" >> $GITHUB_OUTPUT
        fi

결과를 GitHub Actions의 output 변수로 전달합니다.

이 값을 이용해서 나머지 단계들이 실행될지 말지 판단합니다.

 


    - name: Setup Node.js
      if: steps.changed_files.outputs.needs_testing == 'true'
      uses: actions/setup-node@v4
      with:
        node-version: '20'

테스트가 필요한 경우에만 Node.js 20을 설치합니다.

pnpm도 Node.js 위에서 작동하므로 반드시 설치 필요!

 


    - name: Install pnpm
      if: steps.changed_files.outputs.needs_testing == 'true'
      uses: pnpm/action-setup@v2
      with:
        version: 8

GitHub Actions에서 공식적으로 제공하는 pnpm 설치용 액션을 사용합니다.

여기서 중요한 포인트 👉 pnpm/action-setup은 GitHub Actions에서 공식적으로 지원됩니다!

 


    - name: Install dependencies
      if: steps.changed_files.outputs.needs_testing == 'true'
      run: pnpm install

package.json을 기반으로 의존성(라이브러리)을 설치합니다.

 


    - name: Build
      if: steps.changed_files.outputs.needs_testing == 'true'
      run: pnpm build

빌드 명령어를 실행합니다.

프로젝트가 Vite, Webpack, Next 등 어떤 도구를 사용하든 여기서 빌드됩니다.

 


    - name: Run tests
      if: steps.changed_files.outputs.needs_testing == 'true'
      run: pnpm test -- --passWithNoTests

테스트 명령어를 실행합니다.

--passWithNoTests는 테스트 파일이 없더라도 실패하지 않게 해주는 옵션입니다.

 


    - name: Check test status
      if: steps.changed_files.outputs.needs_testing == 'true' && failure()
      run: |
        echo "Tests failed. This PR cannot be merged until all tests pass."
        exit 1

테스트가 실패한 경우, 명시적인 오류 메시지를 출력하고 PR 병합을 막습니다.

CI에서 실패하면 GitHub에서 빨간불 표시가 뜨고 merge 불가!

 


🔄 pnpm과 GitHub Actions의 호환성은?

항목내용

✅ 공식 지원 pnpm/action-setup이라는 GitHub Actions 공식 플러그인 존재
✅ Node와 호환 actions/setup-node와 잘 연동됨
✅ lockfile 사용 pnpm-lock.yaml을 통해 버전 고정 및 캐시 최적화 가능
✅ workspace 호환 모노레포 구성에서도 pnpm workspace 완벽하게 동작함
✅ 속도 GitHub Actions 캐시 + pnpm 조합 → 빠른 빌드와 테스트 가능

 


 

✅ GitHub Actions 구축 효과

항목효과

PR 자동 체크 빌드 & 테스트 자동 수행
PR 병합 전 안정성 확보 깨지는 코드 PR은 머지 차단
선택적 테스트 불필요한 테스트 낭비 방지
개발 생산성 향상 사람 손이 덜 감, 실수도 줄어듬

 

 


🔚 마무리

 

이번에 CI를 직접 구성하면서 느낀 건…

 

“자동화는 개발자의 집중력을 지켜주는 방패다.”

 

앞으로는 더 고도화된 작업도 할 계획입니다.

pnpm workspace + 모노레포 연동

Lint, Prettier 자동 실행

PR 제목 체크 등 자동화 확장