키즈노트 FE개발파트는 몇 달 전부터 젠킨스로 구축되어 있던 CI/CD 환경을 Github Actions로 마이그레이션 해왔어요.
젠킨스는 좋은 도구이지만, 실무에서 사용해 보니 전체적인 관리가 조금 어렵다고 느껴졌어요. 젠킨스의 다양한 기능들을 사용하기 위해 그루비를 학습해야 하고, 데브옵스 담당자분들에게 많이 의지해야 했어요.
"도와주세요 @데브옵스
" 🥺
이러한 단점들을 보완하고자, Github Actions를 도입해서 사용 범위를 점진적으로 확대해가고 있습니다.
젠킨스보다 더 쉽게 CI/CD 환경을 만들 수 있다니! 이쯤 되니 Github Actions에 대해 궁금해지는데요, 하지만 저는 회사 레퍼지토리 설정에 대한 권한이 없기 때문에 개인 레퍼지토리에서 CI/CD 환경을 구축해 보도록 하겠습니다.
Github Actions는 빌드, 테스트, 배포 파이프라인을 자동화할 수 있는 CI/CD 플랫폼이에요. Github Actions를 이용해서 PR을 빌드 및 테스트하거나, 누군가 생성한 이슈에 라벨을 달아주는 등 다양한 액션을 자동화할 수 있어요.
레퍼지토리에서 특정 이벤트가 발생하면 Github Actions를 이용해서 워크플로우를 트리거 할 수 있어요. 이 워크플로우는 하나 이상의 job으로 구성되어 있는데요, 이 job은 Github에서 제공하는 가상 머신 러너에서 실행됩니다. 만약 특별한 설정이 필요한 서버에서 job을 실행하고 싶다면 자체 호스팅도 가능해요.
좀 더 자세한 내용은 공식 문서에서 확인할 수 있어요!
제가 만들고 싶은 CI/CD 환경은 아래와 같아요.
main
브랜치로 머지 하는 PR이 생성되면 빌드 및 테스트를 실행해요.main
브랜치만 설정해 줄게요.Merge pull request
버튼을 기본적으로 비활성화하고, 빌드 및 테스트가 모두 성공할 경우 버튼을 활성화해줄게요.main
브랜치로 머지가 완료되면 Netlify를 이용해서 자동으로 배포해요.PR이 생성될 때 테스트하고 싶은 것들은 아래와 같아요.
Create React App을 이용해서 간단한 리액트 어플리케이션을 만들어줄게요.
루트 디렉토리에서 .github/workflows
디렉토리를 만들고 하위에 pull-request.yml
파일을 생성해 줄게요. pull-request
워크플로우는 PR이 생성될 때 트리거 해줄 거예요.
name: Pull Request
on:
pull_request:
types: [opened, synchronize]
PR이 생성(opened
) 되거나 코드가 업데이트(synchronize
) 된 경우 트리거 되도록 이벤트를 지정해 주었어요.
PR 워크플로우에서 실행될 job들을 지정해 줄 거예요. 가장 먼저 패키지들을 정상적으로 설치할 수 있는지 테스트하기 위해 install-dependencies
job을 추가해 줄게요.
jobs:
install-dependencies:
name: Install Dependencies
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '14'
- name: Install Dependencies
run: npm install
npm install
명령어를 실행하도록 설정해 주었어요.
문법이나 컨벤션에 어긋나는 코드가 없는지 검사하기 위해 lint
job을 아래에 추가해 줄게요.
lint:
name: Lint
needs: [install-dependencies]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '14'
- name: Install Dependencies
run: npm install
- name: Run Lint
run: npm run lint
이번에는 needs
옵션을 추가해 주었어요. 패키지들이 정상적으로 설치되지 않으면 린트 검사도 실행할 수 없기 때문에, install-dependencies
job이 성공적으로 실행되면 lint
job을 실행시켜줄 거예요.
그런데, 이전에 실행되는 install-dependencies
job에서 패키지들을 설치해 주었는데 왜 lint
job에서 또 다시 패키지들을 설치해 주어야 할까요?
각각의 job들은 서로 다른 가상 머신 러너에서 병렬적으로 실행되기 때문에, ESLint를 실행하기 위해서는 다른 job과 관계없이 패키지를 설치해 주어야 합니다.
마지막에 npm run lint
명령어를 추가해서 린트 검사를 실행하도록 설정해 주었어요.
앗! 그런데 ESLint 설정을 하지 않아서 lint
job 테스트를 할 수 없군요! ESLint 설정을 추가해서 airbnb 규칙을 적용해 줄게요.
아래와 같이 package.json
의 devDependencies
에 ESLint 패키지들을 추가해 줍니다.
"devDependencies": {
"eslint": "^8.45.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-jsx-a11y": "^6.7.1",
"eslint-plugin-react": "^7.33.0"
}
그리고 루트 디렉토리에 .eslintrc.json
파일을 생성해 줄게요.
{
"env": {
"browser": true,
"es2021": true,
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react/recommended",
"airbnb" // airbnb 규칙 추가
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": [
"@typescript-eslint",
"react"
],
"rules": {},
"settings": {}
}
이제 PR 워크플로우에 추가한 job들이 정상적으로 실행되는지 확인해 볼게요. develop
브랜치를 생성해서 사용하지 않는 변수를 추가해 줍니다.
그리고 main
← develop
으로 머지 하는 PR을 생성해 줄게요.
의도한 대로 lint
job에서 실패했네요! Details
버튼을 눌러서 확인해 볼까요?
notUsed
변수에서 린트 에러가 발생한 것을 확인할 수 있어요.
그런데 PR을 살펴보면 lint
job에서 실패했는데도 머지 버튼이 활성화되어 있어요.
레퍼지토리에서 브랜치 보호 규칙을 설정하지 않았기 때문에 머지가 가능한 상태인데요, 브랜치 보호 규칙을 추가해서 모든 job이 성공해야 머지 버튼이 활성화되도록 설정해 줄게요.
Github 레퍼지토리에서 Settings > Branches
메뉴로 이동하면 현재 브랜치 보호 규칙이 없는 것을 확인할 수 있어요. 여기서 add for branch protect rule
버튼을 클릭해서 규칙을 추가해 줍니다.
실습에서는 main
, develop
브랜치만 사용할 예정이기 때문에 main
브랜치를 보호해 주도록 할게요.
아래 체크박스를 활성화해서 install-dependencies
, lint
job이 통과하면 머지가 가능하도록 지정해 주었어요.
하단에 Create
버튼을 클릭하고 다시 PR로 돌아오면 머지 버튼이 비활성화된 것을 확인할 수 있어요.
test
job을 아래와 같이 추가해 줄게요.
test:
name: Test
needs: [install-dependencies]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '14'
- name: Install Dependencies
run: npm install
- name: Run Test
run: npm run test
lint
job과 마찬가지로 need
옵션과 npm install
명령어를 넣어주고, 브랜치 보호 규칙에서 test
job도 통과해야 머지가 가능하도록 수정해 주었어요.
테스트는 CRA 기본 설정에 포함되어 있기 때문에 별도의 테스트 설정은 하지 않을게요.
테스트를 위해 App.test.tsx
파일을 수정해 줍니다.
develop
브랜치를 push 하면 코드가 업데이트되었기 때문에 PR 워크플로우가 트리거 된 것을 확인할 수 있어요.
어떤 테스트 파일에서 왜 실패했는지 자세히 알려줍니다.
마지막으로 빌드를 체크하는 job을 추가해 줄게요.
build:
name: Build
needs: [install-dependencies]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '14'
- name: Install Dependencies
run: npm install
- name: Run Build
run: npm run build
모든 job이 정상적으로 실행되는 것을 확인할 수 있어요.
Deploy 워크플로우를 만들기 전에!!! 어떻게 배포할 것인지 결정해서 배포를 해봐야겠죠? 이번 실습에서 만든 리액트 어플리케이션은 아주 작은 규모이기 때문에 Netlify로 배포해 줄 거예요.
Netlify에 가입할 때 Github 계정을 연동하면 레퍼지토리 목록을 확인할 수 있어요.
레퍼지토리를 선택하면 곧바로 배포가 시작됩니다.
빠르게 배포에 성공했네요!
이제 .github/workflows
디렉토리 하위에 deploy-production.yml
파일을 생성해 줄게요. deploy-production
워크플로우는 PR이 main
브랜치에 머지 되면서 main
브랜치에 새로운 커밋이 생겼을 때 트리거 해줄 거예요.
name: Deploy to Production
on:
push:
branches:
- 'main'
배포 자동화를 구현하기 위해 deploy
job을 추가해 줄게요.
jobs:
deploy:
name: Deploy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '14'
- name: Install Dependencies
run: npm install
- name: Run Build
run: npm run build
- name: Deploy to Netlify
uses: nwtgck/actions-netlify@v2.0
with:
publish-dir: './build'
production-deploy: true
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
timeout-minutes: 1
배포 전에 npm install
, npm run build
명령어를 실행해서 빌드 파일을 생성해 줄 거예요. 이렇게 만들어진 빌드 파일은 Netlify를 이용해서 배포해 줄 것이기 때문에, 마켓 플레이스에서 다운로드 수가 가장 많은 netlify-actions를 가져왔어요.
netlify-actions를 사용하기 위해서 아래 세 가지를 필수로 입력해 주어야 합니다.
publish-dir
: 빌드 결과물이 저장될 디렉토리 이름NETLIFY_AUTH_TOKEN
: Netlify에서 발급한 액세스 토큰NETLIFY_SITE_ID
: 배포할 사이트 아이디액세스 토큰은 User Settings > Applications > OAuth
메뉴에서 생성할 수 있어요.
액세스 토큰에 대한 간략한 설명을 적고 Generate token
버튼을 클릭하면 액세스 토큰을 생성할 수 있어요. 액세스 토큰은 보안 상 외부 노출에 유의해야 합니다!
사이트 아이디는 Site configuration > General > Site details
메뉴에서 확인할 수 있어요.
이렇게 가져온 액세스 토큰과 사이트 아이디를 Github에 저장해 줄게요. 레퍼지토리에서 Settings > Security > Secrets and variables > Actions
메뉴로 이동합니다. New repository secret
버튼을 클릭해서 액세스 토큰과 사이트 아이디를 저장합니다.
저장 후 아래와 같이 Repository secrets
에서 목록을 확인할 수 있어요. 사이트 아이디는 외부에 노출되어도 문제없지만 원활한 변수 관리를 위해 액세스 토큰과 같은 위치에 저장해 주었습니다.
이제 배포 테스트를 해볼까요? develop
브랜치에서 App.tsx
파일을 수정해 줄게요.
netlify-actions의 enable-pull-request-comment
옵션 기본값이 true
로 설정되어 있기 때문에 아래와 같이 코멘트를 확인할 수 있어요. 최신 커밋, 배포 로그, 프리뷰 정보를 제공해 주고 있군요!
모든 job들이 성공적으로 실행되었으니, 머지를 해보도록 하겠습니다!
머지 후 사이트로 이동하면 정상적으로 배포된 것을 확인할 수 있어요.
사실 개인적으로 젠킨스를 이용해서 CI/CD 환경 구축을 도전해 본 적이 있는데요, 생각보다 높은 진입 장벽에 막혀서 쩔쩔맸던 기억이 있어요. 그런데 Github Actions는 공식 문서도 친절하고 마켓 플레이스에서 제공하는 다양한 기능들 덕분에 쉽게 CI/CD 환경을 만들 수 있었습니다.
앞으로 개인 프로젝트를 진행할 때 Github Actions를 적극적으로 사용해야겠네요!
실습에 사용된 코드는 여기서 확인할 수 있습니다.