๐ŸŽฏGitHub Actions๋กœ ๋ชจ๋…ธ๋ ˆํฌ Lerna ํ”„๋กœ์ ํŠธ ๋ฐฐํฌ ์ž๋™ํ™”

junameeยท2025๋…„ 4์›” 17์ผ
0

ํ”„๋ก ํŠธ์—”๋“œ

๋ชฉ๋ก ๋ณด๊ธฐ
18/18

Yarn workspace + Lerna + GitHub Actions + S3 + CloudFront

โœ… ๋“ค์–ด๊ฐ€๋ฉฐ

ํ˜„์žฌ ์ฐธ์—ฌ ์ค‘์ธ ํ”„๋กœ์ ํŠธ๋Š” Yarn workspace + Lerna ๊ธฐ๋ฐ˜ ๋ชจ๋…ธ๋ ˆํฌ ๊ตฌ์กฐ๋กœ ๊ด€๋ฆฌ๋˜๊ณ  ์žˆ์œผ๋ฉฐ,
GitHub Actions๋ฅผ ์ด์šฉํ•œ CI/CD ์ž๋™ํ™” ํŒŒ์ดํ”„๋ผ์ธ์ด ๊ตฌ์„ฑ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

์ง์ ‘ ์ฒ˜์Œ๋ถ€ํ„ฐ ์ž‘์„ฑํ•œ ์Šคํฌ๋ฆฝํŠธ๋Š” ์•„๋‹ˆ์ง€๋งŒ,
์ „์ฒด์ ์ธ ๊ตฌ์กฐ๋ฅผ ์ดํ•ดํ•˜๊ณ , ์‹ค๋ฌด์—์„œ ๋ฐœ์ƒํ•œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ฑฐ๋‚˜
ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์ž๋™ํ™”, ์•Œ๋ฆผ ์กฐ๊ฑด ์ฒ˜๋ฆฌ ๋“ฑ ์ผ๋ถ€ ๋กœ์ง์„ ํ†บ์•„๋ณด๊ณ  ๊ฐœ์„ ยทํ™•์žฅํ–ˆ์Šต๋‹ˆ๋‹ค.

์ด ๊ธ€์—์„œ๋Š” ์ด CI/CD ํŒŒ์ดํ”„๋ผ์ธ์˜ ๊ตฌ์กฐ์™€ ์ž‘๋™ ๋ฐฉ์‹, ๊ทธ๋ฆฌ๊ณ  ์ œ๊ฐ€ ์ดํ•ดํ•œ ํฌ์ธํŠธ๋ฅผ ๊ณต์œ ํ•˜๋ ค ํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“ฆ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ

root
โ”œโ”€โ”€ packages
โ”‚   โ”œโ”€โ”€ admin
โ”‚   โ”œโ”€โ”€ common
โ”‚   โ”œโ”€โ”€ design-system
โ”‚   โ””โ”€โ”€ ...
โ”œโ”€โ”€ .github
โ”‚   โ””โ”€โ”€ workflows
โ”‚       โ””โ”€โ”€ deploy.yml

โš™๏ธ ๋ชจ๋…ธ๋ ˆํฌ ํŒจํ‚ค์ง€๋ณ„ ๋นŒ๋“œ ์ „๋žต

Lerna์˜ --scope ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ, ๋ณ‘ํ•ฉ๋˜๋Š” release-* ๋ธŒ๋žœ์น˜์— ๋”ฐ๋ผ ํ•ด๋‹น ํŒจํ‚ค์ง€๋งŒ ๋นŒ๋“œ๋˜๋„๋ก ๊ตฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.

run: yarn lerna run build:prod --scope=@fe/${{ steps.extract.outputs.PACKAGE_NAME }}

PACKAGE_NAME์€ ๋ธŒ๋žœ์น˜๋ช…์—์„œ ์ž๋™ ์ถ”์ถœ
release-{package_name} โ†’ PACKAGE_NAME

๐ŸŒ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์ž๋™ ์ƒ์„ฑ

GitHub Actions์—์„œ ๋ธŒ๋žœ์น˜๋ช…์„ ๊ธฐ์ค€์œผ๋กœ production / stage ํ™˜๊ฒฝ์„ ํŒ๋‹จํ•˜๊ณ , ๊ฐ ํ™˜๊ฒฝ์— ๋งž๋Š” secret ๊ฐ’์„ .env ํŒŒ์ผ๋กœ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

 echo "PACKAGE_NAME=$(echo ${{ github.ref_name }} | cut -d '-' -f2)" >> $GITHUB_OUTPUT
 
if [[ ${{ github.ref_name }} == master ]]; then
  echo "env=production" >> $GITHUB_OUTPUT
else
  echo "env=stage" >> $GITHUB_OUTPUT
fi

๐Ÿš€ ๋ฐฐํฌ: S3 + CloudFront

 run: aws s3 sync ${{ steps.extract.outputs.PACKAGE_NAME }}/dist s3://{PACKAGE_NAME}-prod
- run: aws cloudfront create-invalidation --paths "/*"

release-* ๋ธŒ๋žœ์น˜๋Š” stage๋กœ master ๋ณ‘ํ•ฉ ์‹œ, production์œผ๋กœ ๋ฐฐํฌ
๋ฐฐํฌ ํ›„ CloudFront ์บ์‹œ ๋ฌดํšจํ™”๊นŒ์ง€ ์ž๋™ ์ฒ˜๋ฆฌํ•˜์—ฌ ์ฆ‰๊ฐ ๋ณ€๊ฒฝ์ด ๋ฐ˜์˜๋˜๋„๋ก ์ฒ˜๋ฆฌ

๐Ÿ”” Slack ์•Œ๋ฆผ ์—ฐ๋™

- if: failure()
  uses: ./.github/actions/slack-notify
  with:
    slack_incoming_url: ${{ secrets.SLACK_INCOMING_URL }}

- if: success()
  uses: ./.github/actions/slack-notify
  with:
    status: success

์„ฑ๊ณต/์‹คํŒจ์— ๋”ฐ๋ผ ์Šฌ๋ž™์œผ๋กœ ์•Œ๋ฆผ์„ ๋ณด๋ƒ…๋‹ˆ๋‹ค.
์‹คํŒจ ์‹œ์—๋Š” ๋นŒ๋“œ ๋กœ๊ทธ๋ฅผ ํ•จ๊ป˜ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค์ •ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

๐Ÿงน Release ๋ธŒ๋žœ์น˜ ์ž๋™ ์‚ญ์ œ

if [[ "$BRANCH_NAME" == release-* ]]; then
  git push origin --delete $BRANCH_NAME

release-* ๋ธŒ๋žœ์น˜๊ฐ€ master์— ๋ณ‘ํ•ฉ๋˜๋ฉด ์ž๋™์œผ๋กœ ์‚ญ์ œ๋˜์–ด
๋ธŒ๋žœ์น˜ ์ •๋ฆฌ๋ฅผ ์ˆ˜๋™์œผ๋กœ ํ•˜์ง€ ์•Š์•„๋„ ๋ฉ๋‹ˆ๋‹ค.

๐Ÿค” ์•„์‰ฌ์šด ์ 

๋ธŒ๋žœ์น˜๋ช…์— ์˜์กดํ•˜๋Š” ํ™˜๊ฒฝ ๋ฐ ํŒจํ‚ค์ง€๋ช… ํŒŒ์•…

echo "PACKAGE_NAME=$(echo ${{ github.ref_name }} | cut -d '-' -f2)" >> $GITHUB_OUTPUT

ํ•ญ์ƒ release-*-* ํ˜•ํƒœ์˜ ๋ธŒ๋žœ์น˜๋ช…์„ ๊ณ ์ˆ˜ํ•ด์•ผ CI/CD๊ฐ€ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.
๋ธŒ๋žœ์น˜๋ช…์ด release๋กœ ์‹œ์ž‘ํ•˜๋ฉด stage ํ™˜๊ฒฝ,master์ด๋ฉด prod ํ™˜๊ฒฝ.
*-*์˜ ์กฐํ•ฉ์€ ํŒจํ‚ค์ง€๋ช…

๊ทธ๋ ‡์ง€๋งŒ ์‹ค์ˆ˜(?)๋กœ ์˜คํƒ€๊ฐ€ ์ƒ๊ธธ ์ˆ˜๋„ ์žˆ๊ณ , ๋„ค์ด๋ฐ ์ปจ๋ฒค์…˜ ๋ณ€๊ฒฝ์„ ๊ณ ๋ คํ• ๋•Œ CI/CD๋„ ์ˆ˜์ •์ด ํ•„์š”ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ ์—…๋ฌด ์ค‘ ๊ธ‰ํ•œ ์ผ์ด ์žˆ์–ด ํ•ด๋‹น ์ปจ๋ฒค์…˜๋ช…์„ ๋”ฐ๋ฅด์ง€ ์•Š์€ ๋ธŒ๋žœ์น˜๋ฅผ ๋ฐฐํฌํ•œ ์ ์ด ์žˆ์—ˆ๊ณ  ๋ฐฐํฌ๊ฐ€ ์‹คํŒจํ•ด์„œ ๋‹นํ™ฉํ–ˆ๋˜ ์ ์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿผ ์–ด๋–ป๊ฒŒ ?!
1. github PR Label ํ™œ์šฉํ•˜๊ธฐ
ํ˜„์žฌ PROD-release, DEV-release ๋ผ๋Š” ํƒœ๊ทธ๋ฅผ ์“ฐ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
ํ•˜๋‚˜๋Š” master ๋ณ‘ํ•ฉ์šฉ release๋ธŒ๋žœ์น˜ PR ํƒœ๊ทธ
๋‹ค๋ฅธ ํ•˜๋‚˜๋Š” develop ๋ณ‘ํ•ฉ์šฉ relese ๋ธŒ๋žœ์น˜ PR ํƒœ๊ทธ ์ž…๋‹ˆ๋‹ค.
git action์—์„œ๋„ ์ด ๋ผ๋ฒจ ํƒœ๊ทธ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ•˜๋‹ˆ ์ด๊ฑธ ๊ธฐ์ค€์œผ๋กœ ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์„ค์ •์„ ํ•œ๋‹ค๋ฉด ์–ด๋А ๋ธŒ๋žœ์น˜ ์ปจ๋ฒค์…˜์—์„œ๋„ ๋ฌธ์ œ๊ฐ€ ์—†์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค!

2. lerna changed
ํŒจํ‚ค์ง€๋ช… ๊ฐ™์€ ๊ฒฝ์šฐ๋Š” lerna์—์„œ ๋ณ€๊ฒฝ์„ ๊ฐ์ง€ํ•  ์ˆ˜ ์žˆ์–ด์„œ (lerna changed)
๊ผญ ๋ธŒ๋žœ์น˜๋ช…์— ํŒจํ‚ค์ง€๋ช…์„ ๋ช…์‹œํ•˜์ง€ ์•Š์•„๋„ ์•Œ์•„์„œ ๋ณ€๊ฒฝ์ด ์žˆ๋Š” ํŒจํ‚ค์ง€๋ฅผ ๋นŒ๋“œํ•˜๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

โœ… ๋งˆ๋ฌด๋ฆฌํ•˜๋ฉฐ

ํ˜„์žฌ ๋ชจ๋…ธ๋ ˆํฌ ๊ตฌ์กฐ์—์„œ CI/CD๊ฐ€ ์–ด๋–ป๊ฒŒ ๊ตฌ์„ฑ๋˜๊ณ  ๊ด€๋ฆฌ๋˜๋Š”์ง€๋ฅผ ์กฐ๊ธˆ์ด๋‚˜๋งˆ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
์ดˆ๊ธฐ์—๋Š” ์–ด๋ ค์› ์ง€๋งŒ ๋นŒ๋“œ, ๋ฐฐํฌ, ์บ์‹œ ๋ฌดํšจํ™”, slack ์•Œ๋ฆผ, ๋ธŒ๋žœ์น˜ ์ •๋ฆฌ๊นŒ์ง€์˜ ๊ณผ์ •์„ ์˜จ์ „ํžˆ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์—ˆ๊ณ  ์•„์‰ฌ์šด ์ ๋„ ๋ฐœ๊ฒฌํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์•„์‰ฌ์šด ์ ์€ ์ง€๊ธˆ๊นŒ์ง€ ์œ ์ง€๋˜์–ด์˜จ ์ปจ๋ฒค์…˜์„ ๋ฐ”๊ฟ€ ํ•„์š”๊ฐ€ ์—†์–ด ํฐ ๋ฌธ์ œ์—†์œผ๋‚˜, ์ถ”ํ›„์— ์‹œ๊ฐ„์ด ๋œ๋‹ค๋ฉด ๊ผญ ๊ฐœ์„ ํ•ด๋ณด๊ณ  ์‹ถ์€ ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค :D

profile
์•„ํ‹ฐํด๋ฆฌ์ŠคํŠธ - bit.ly/3wjIlZJ

0๊ฐœ์˜ ๋Œ“๊ธ€