Continuous Integration - 100-hours-a-week/3-team-ssammu-wiki GitHub Wiki

1. CI ๋„์ž… ๋ฐฐ๊ฒฝ

๐Ÿšง ๊ธฐ์กด ๋ฌธ์ œ์ 

  • ์ˆ˜๋™ ๋ฐฐํฌ๋กœ ์ธํ•œ ์‹ค์ˆ˜ ๋ฐœ์ƒ

    ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ SSH๋กœ VM์— ์ ‘์†ํ•˜๊ณ  scp๋กœ ํŒŒ์ผ์„ ์ „์†กํ•˜๋Š” ๋ฐฉ์‹์€ ๋ฐ˜๋ณต์ ์ด๊ณ  ์˜ค๋ฅ˜ ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์Œ

  • ์ฝ”๋“œ ํ’ˆ์งˆ ๊ด€๋ฆฌ ๋ถ€์žฌ

    ์ผ๊ด€๋œ ํ…Œ์ŠคํŠธ๋‚˜ ๋ฆฐํŠธ ๊ณผ์ •์„ ๊ฑฐ์น˜์ง€ ์•Š๊ณ  ์ฝ”๋“œ๊ฐ€ ๋ณ‘ํ•ฉ๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋ฐœ์ƒ

  • ํ˜‘์—… ๋ฐ ๊ฐ€์‹œ์„ฑ ๋ถ€์กฑ

    ๋ˆ„๊ฐ€ ์–ด๋–ค ์ฝ”๋“œ๋ฅผ ์–ธ์ œ ์–ด๋–ค ํ™˜๊ฒฝ์— ๋ฐ˜์˜ํ–ˆ๋Š”์ง€ ์ถ”์ ์ด ์–ด๋ ค์›€


2. CI์˜ ์ •์˜ ๋ฐ ๋ชฉํ‘œ

โœ… CI(์ง€์†์  ํ†ตํ•ฉ, Continuous Integration)๋ž€?

์—ฌ๋Ÿฌ ๊ฐœ๋ฐœ์ž๊ฐ€ ๋ณ€๊ฒฝํ•œ ์ฝ”๋“œ๋ฅผ ์ฃผ๊ธฐ์ ์œผ๋กœ ํ†ตํ•ฉํ•˜๊ณ , ๋นŒ๋“œ ๋ฐ ํ…Œ์ŠคํŠธ ์ž๋™ํ™”๋ฅผ ํ†ตํ•ด ํ’ˆ์งˆ์„ ์œ ์ง€ํ•˜๋Š” ๊ฐœ๋ฐœ ๋ฐฉ๋ฒ•๋ก ์ž…๋‹ˆ๋‹ค.

๐ŸŽฏ ๋„์ž… ๋ชฉํ‘œ

  • ์ž๋™ํ™”๋œ ๋นŒ๋“œยทํ…Œ์ŠคํŠธ ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌ์ถ•

    ํ‘ธ์‹œ(Push) ๋˜๋Š” PR ์‹œ์ ์— ์ž๋™์œผ๋กœ ๋™์ž‘ํ•˜์—ฌ ์ฝ”๋“œ ํ’ˆ์งˆ ์œ ์ง€

  • ์ผ๊ด€๋œ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ์œ ์ง€

    ๋ชจ๋“  ๋ธŒ๋žœ์น˜/์ปค๋ฐ‹์—์„œ ๋™์ผํ•œ ํ™˜๊ฒฝ์—์„œ ํ…Œ์ŠคํŠธ ๋ฐ ๋ถ„์„ ์ˆ˜ํ–‰

  • ๋ฌธ์ œ ์กฐ๊ธฐ ๋ฐœ๊ฒฌ ๋ฐ ๋น ๋ฅธ ํ”ผ๋“œ๋ฐฑ

    ํ…Œ์ŠคํŠธ ์‹คํŒจ, ๋ฆฐํŠธ ์˜ค๋ฅ˜ ๋“ฑ์„ ๋น ๋ฅด๊ฒŒ ๊ฐ์ง€ํ•˜๊ณ  ์ˆ˜์ • ์œ ๋„

  • ๋ฐฐํฌ ์•ˆ์ •์„ฑ ํ™•๋ณด

    ๋นŒ๋“œ ๊ฒฐ๊ณผ๋ฅผ Cloud Storage ๋“ฑ์— ์•ˆ์ „ํ•˜๊ฒŒ ์ €์žฅํ•˜์—ฌ ๋ฐฐํฌ ์•ˆ์ •์„ฑ ๊ฐ•ํ™”


3. ์†Œ์Šค ๊ด€๋ฆฌ ๋ฐ CI ๋„๊ตฌ ์„ ์ •๊ณผ ์ด์œ 

โœ… 1) GitHub์„ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ 

ํ˜„์žฌ ์šฐ๋ฆฌ ํŒ€์€ ์†Œ์Šค์ฝ”๋“œ ๊ด€๋ฆฌ ๋ฐ ํ˜‘์—… ๋„๊ตฌ๋กœ GitHub์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ์ด์œ ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

ํ•ญ๋ชฉ ์„ค๋ช…
์—…๊ณ„ ํ‘œ์ค€ ๋„๊ตฌ ๋Œ€๋ถ€๋ถ„์˜ ์˜คํ”ˆ์†Œ์Šค ํ”„๋กœ์ ํŠธ์™€ ์Šคํƒ€ํŠธ์—…, ๊ธฐ์—… ๊ฐœ๋ฐœํŒ€์ด ์‚ฌ์šฉ ์ค‘์ด๋ฉฐ, ํ˜‘์—… ๊ด€ํ–‰์ด ์ž˜ ์ •๋ฆฝ๋ผ ์žˆ์Œ
PR ๊ธฐ๋ฐ˜ ํ˜‘์—… Pull Request๋ฅผ ํ†ตํ•ด ์ฝ”๋“œ ๋ฆฌ๋ทฐ, ์Šน์ธ, ๋จธ์ง€ ๊ณผ์ •์ด ํˆฌ๋ช…ํ•˜๊ณ  ์ฒด๊ณ„์ ์œผ๋กœ ๊ด€๋ฆฌ๋จ
๊ฐ€์‹œ์„ฑ ๋ฐ ์ถ”์ ์„ฑ ์ปค๋ฐ‹/PR/๋ฆฌ๋ทฐ/๋ฆด๋ฆฌ์ฆˆ ๊ธฐ๋ก์ด ๋ช…ํ™•ํ•˜๊ฒŒ ๋‚จ์•„ ํŒ€ ๋‚ด ํžˆ์Šคํ† ๋ฆฌ ๊ด€๋ฆฌ๊ฐ€ ์šฉ์ด
ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ์ƒํƒœ๊ณ„ Actions, Dependabot, ๋ณด์•ˆ ๊ฒ€์‚ฌ ๋“ฑ ๋‹ค์–‘ํ•œ ์ž๋™ํ™” ๊ธฐ๋Šฅ์„ ์„ค์ • ์—†์ด ๋ฐ”๋กœ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
๋ณด์•ˆ ๋ฐ ๊ถŒํ•œ ๊ด€๋ฆฌ ์กฐ์ง๋ณ„ ๊ถŒํ•œ ์„ค์ •, ๋ธŒ๋žœ์น˜ ๋ณดํ˜ธ, ๋น„๋ฐ€ ๊ฐ’ ๊ด€๋ฆฌ ๋“ฑ ํ˜‘์—… ์‹œ ํ•„์š”ํ•œ ๋ณด์•ˆ ๊ธฐ๋Šฅ์ด ์™„๋น„
๋น„์šฉ ํšจ์œจ์„ฑ Private ์ €์žฅ์†Œ๋„ ๋ฌด๋ฃŒ ์ œ๊ณต๋˜๋ฉฐ, ํŒ€ ๊ทœ๋ชจ๊ฐ€ ์ž‘์•„๋„ ๋ถ€๋‹ด ์—†์ด ์‚ฌ์šฉ ๊ฐ€๋Šฅ

GitHub์€ ๋‹จ์ˆœํ•œ ์ฝ”๋“œ ์ €์žฅ์†Œ๋ฅผ ๋„˜์–ด์„œ ํšจ์œจ์ ์ธ ํ˜‘์—… ํ”Œ๋žซํผ์œผ๋กœ ๊ธฐ๋Šฅํ•˜๊ณ  ์žˆ์œผ๋ฉฐ, CI/CD์™€๋„ ๋ฐ€์ ‘ํ•˜๊ฒŒ ํ†ตํ•ฉ๋˜์–ด ์žˆ์–ด ์œ ์ง€๊ด€๋ฆฌ ๋ถ€๋‹ด์ด ์ ์Šต๋‹ˆ๋‹ค.


โœ… 2) CI ๋„๊ตฌ ๋น„๊ต ๋ฐ ์„ ํƒ: GitHub Actions

GitHub๊ณผ ์™„์ „ํžˆ ํ†ตํ•ฉ๋˜๋Š” CI ๋„๊ตฌ๋กœ๋Š” GitHub Actions๋ฅผ ์„ ํƒํ–ˆ์Šต๋‹ˆ๋‹ค. ์•„๋ž˜๋Š” ์ฃผ์š” CI ๋„๊ตฌ์™€์˜ ๋น„๊ต์ž…๋‹ˆ๋‹ค:

ํ•ญ๋ชฉ GitHub Actions GitLab CI/CD Jenkins
GitHub ํ†ตํ•ฉ์„ฑ โœ… ์™„์ „ ํ†ตํ•ฉ, ๋ณ„๋„ ์—ฐ๋™ ๋ถˆํ•„์š” ๐Ÿ”ถ ์™ธ๋ถ€ ์—ฐ๋™ ํ•„์š” โŒ ์ˆ˜๋™ ์—ฐ๋™ ๋ฐ ์„ค์ • ํ•„์š”
์„ค์ • ๋ฐฉ์‹ .yml ๊ธฐ๋ฐ˜์œผ๋กœ ์ง๊ด€์  .gitlab-ci.yml Groovy ์Šคํฌ๋ฆฝํŠธ ๋˜๋Š” UI ์„ค์ •
ํ•™์Šต ๊ณก์„  โœ… ๋‚ฎ์Œ ์ค‘๊ฐ„ โŒ ๋†’์Œ
ํ™•์žฅ์„ฑ GitHub Marketplace์—์„œ ์ˆ˜์ฒœ ๊ฐœ ์•ก์…˜ ์ œ๊ณต ์ผ๋ถ€ ์˜คํ”ˆ์†Œ์Šค ์ง€์› ํ”Œ๋Ÿฌ๊ทธ์ธ ๋งŽ์ง€๋งŒ ๋ณต์žก
๋ณด์•ˆ ์—ฐ๋™ GitHub ๊ณ„์ • ๋ฐ ์กฐ์ง ๊ถŒํ•œ๊ณผ ํ†ตํ•ฉ ๋ณ„๋„ ์ธ์ฆ ํ•„์š” ๋ณ„๋„ ์„ค์ • ํ•„์š”
๋น„์šฉ โœ… Private ์ €์žฅ์†Œ๋„ ๋ฌด๋ฃŒ tier ์ง€์› ๋ฌด๋ฃŒ tier ์ œํ•œ ์กด์žฌ ์„œ๋ฒ„ ์ง์ ‘ ์šด์˜ ํ•„์š”
GCP ์—ฐ๋™ ๊ณต์‹ ์•ก์…˜ ๋ฐ gcloud CLI ์ง€์›์œผ๋กœ ๊ฐ„ํŽธ ๋งค๋‰ด์–ผ ์„ค์ • ํ•„์š” ํ”Œ๋Ÿฌ๊ทธ์ธ ๋˜๋Š” Shell ์Šคํฌ๋ฆฝํŠธ ์ž‘์„ฑ ํ•„์š”

GitHub Actions๋Š” GitHub ๊ธฐ๋ฐ˜์˜ ์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ ๊ฐ€์žฅ ์ž์—ฐ์Šค๋Ÿฝ๊ณ  ์‰ฝ๊ฒŒ ์ž๋™ํ™”ํ•  ์ˆ˜ ์žˆ๋Š” CI ๋„๊ตฌ์ž…๋‹ˆ๋‹ค.

๋ณ„๋„ CI ์„œ๋ฒ„ ์—†์ด๋„ ๋น ๋ฅด๊ฒŒ ๊ตฌ์ถ• ๊ฐ€๋Šฅํ•˜๋ฉฐ, GCP ๋ฐ ํƒ€ ์„œ๋น„์Šค์™€์˜ ์—ฐ๋™๋„ ๊ฐ„๋‹จํ•˜๋‹ค๋Š” ์ ์—์„œ ์‹ค๋ฌด ์ ์šฉ์„ฑ๊ณผ ํšจ์œจ์„ฑ์ด ๋งค์šฐ ๋›ฐ์–ด๋‚ฉ๋‹ˆ๋‹ค.


4. ๋ธŒ๋žœ์น˜ ์ „๋žต

๋ธŒ๋žœ์น˜ ์ด๋ฆ„ ์šฉ๋„ ๋ณ‘ํ•ฉ ๋ฐฉ์‹ ๋ธŒ๋žœ์น˜ ์ƒ์„ฑ ๊ธฐ์ค€ ํŒŒ์ดํ”„๋ผ์ธ ์„ค๋ช…
main ์šด์˜ ๊ธฐ์ค€ ์•ˆ์ •ํ™”๋œ ์ฝ”๋“œ ์Šค๋ƒ…์ƒท - ์šด์˜ ๋ฆด๋ฆฌ์ฆˆ ํ›„ merge๋จ Push, PR + ๋ฆฌ๋ทฐ ํ›„ ๋ณ‘ํ•ฉ -
release/x.y.z ์šด์˜ ๋ฐฐํฌ ๋ธŒ๋žœ์น˜ Push, PR + ๋ฆฌ๋ทฐ ํ›„ ๋ณ‘ํ•ฉ dev build โ†’ test โ†’ lint โ†’ artifact
dev ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ๋ธŒ๋žœ์น˜ Push, PR + ๋ฆฌ๋ทฐ ํ›„ ๋ณ‘ํ•ฉ main build โ†’ test โ†’ lint โ†’ artifact
feature/* ๊ธฐ๋Šฅ ๊ฐœ๋ฐœ ๋ธŒ๋žœ์น˜ Push dev lint
hotfix/* ์šด์˜ ์ค‘ ๊ธด๊ธ‰ ์ˆ˜์ • Push release build โ†’ test โ†’ lint

๋ธŒ๋žœ์น˜ ํ๋ฆ„๋„ ์š”์•ฝ

(feature/ํšŒ์›๊ฐ€์ž…) โ†’ dev โ†’ release/1.0.0 โ†’ main
                          โ†‘         โ†“
                     hotfix/*     ์šด์˜ ๊ธฐ์ค€ ์Šค๋ƒ…์ƒท

์šด์˜ ์‹œ ์œ ์˜์‚ฌํ•ญ

release ๋ธŒ๋žœ์น˜๋Š” ์šด์˜ ๋ฐฐํฌ ์ „ QA ์ตœ์ข… ๊ณต๊ฐ„์ž…๋‹ˆ๋‹ค. ๊ฒ€์ˆ˜ ํ›„์—๋งŒ main์— ๋ณ‘ํ•ฉํ•ฉ๋‹ˆ๋‹ค.

main์€ ์šด์˜ ๋ฐฐํฌ ์ด๋ ฅ์ด ์ถ•์ ๋œ ์Šค๋ƒ…์ƒท ์ €์žฅ์†Œ ์—ญํ• ์„ ํ•˜๋ฉฐ, ์ง์ ‘ ๋ฐฐํฌ์—๋Š” ๊ด€์—ฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๊ธด๊ธ‰ ์ˆ˜์ • ์‹œ์—๋Š” ๋ฐ˜๋“œ์‹œ hotfix/*๋ฅผ ์‚ฌ์šฉํ•˜๋ฉฐ, release์™€ dev์— ๋ชจ๋‘ ๋ณ‘ํ•ฉํ•ฉ๋‹ˆ๋‹ค.


5. CI ํŒŒ์ดํ”„๋ผ์ธ ์ •์˜

๐Ÿชœ๋‹จ๊ณ„ ์ •์˜

์ดˆ๊ธฐ ์„ค๊ณ„๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ๋‚˜๋ˆ ๋ณด๋ฉด ์ข‹์•„์š”:

  1. Build: ์ข…์†์„ฑ ์„ค์น˜, ๋นŒ๋“œ (ex. npm install, npm run build)
  2. Test: ์œ ๋‹› ๋ฐ ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ์ˆ˜ํ–‰ (ex. npm run test)
  3. Lint : ์ฝ”๋“œ ์Šคํƒ€์ผ ์ ๊ฒ€ (ex. eslint)
  4. Artifact Upload: ๋นŒ๋“œ ๊ฒฐ๊ณผ๋ฌผ์„ GCS์— ์—…๋กœ๋“œํ•˜์—ฌ ๋ฒ„์ „ ๊ด€๋ฆฌ ๋ฐ ๋กค๋ฐฑ ๋Œ€๋น„

๐Ÿ“ฆ GCS ์ €์žฅ ๊ฒฝ๋กœ ํ˜•์‹ (์ปค๋ฐ‹ ํ•ด์‹œ ๊ธฐ์ค€ ๋ฒ„์ „ ๊ด€๋ฆฌ)

์„œ๋น„์Šค ์ €์žฅ ๊ฒฝ๋กœ ์˜ˆ์‹œ ์„ค๋ช…
Backend gs://careerbee-deploy-artifacts/backend/app-.jar ๊ฐ ์ปค๋ฐ‹๋งˆ๋‹ค JAR ํŒŒ์ผ ์ €์žฅ
Frontend gs://careerbee-deploy-artifacts/frontend// ์ •์  ๋ฆฌ์†Œ์Šค ๋””๋ ‰ํ† ๋ฆฌ ์ „์ฒด ์ €์žฅ
AI Server gs://careerbee-deploy-artifacts/ai/ai-.zip ์†Œ์Šค, ๊ฐ€์ƒํ™˜๊ฒฝ ํฌํ•จ ์••์ถ• ํŒŒ์ผ ์ €์žฅ

artifact๋Š” ๋นŒ๋“œ๋œ ์‹คํ–‰ ํŒŒ์ผ ๋˜๋Š” ์ •์  ํŒŒ์ผ์„ ์˜๋ฏธํ•˜๋ฉฐ, ์ปค๋ฐ‹ ํ•ด์‹œ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ €์žฅ๋˜์–ด ๋กค๋ฐฑ ์‹œ ์ •ํ™•ํ•œ ์‹œ์  ๋ณต์›์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.


๐Ÿ”น feature/* ๋ธŒ๋žœ์น˜ ํŒŒ์ดํ”„๋ผ์ธ

ํ•ญ๋ชฉ ๋‚ด์šฉ
Trigger push
์ฃผ์š” ๋‹จ๊ณ„ checkout โ†’ install โ†’ lint
๋ฐฐํฌ ์—ฌ๋ถ€ โŒ ์—†์Œ
๊ธฐ๋Šฅ ๊ฐœ๋ฐœ ์ค‘ ๋น ๋ฅธ ํ”ผ๋“œ๋ฐฑ์šฉ (๋‹จ๊ณ„ ์‹คํŒจ ์‹œ PR ๋ฐฉ์ง€)

๐Ÿ”ธ dev ๋ธŒ๋žœ์น˜ ํŒŒ์ดํ”„๋ผ์ธ

ํ•ญ๋ชฉ ๋‚ด์šฉ
Trigger push, pull_request (PR ๋Œ€์ƒ์ด dev์ผ ๊ฒฝ์šฐ)
์ฃผ์š” ๋‹จ๊ณ„ checkout โ†’ install โ†’ build โ†’ test โ†’ lint
๋ฐฐํฌ ์—ฌ๋ถ€ โŒ ์—†์Œ
๊ธฐ๋Šฅ ํ†ตํ•ฉ ํ’ˆ์งˆ ์ ๊ฒ€ ๋ฐ QA ํ™˜๊ฒฝ ๋Œ€์‘

๐ŸŸข release/x.y.z ๋ธŒ๋žœ์น˜ ํŒŒ์ดํ”„๋ผ์ธ

ํ•ญ๋ชฉ ๋‚ด์šฉ
Trigger push, pull_request (PR ๋Œ€์ƒ์ด release์ผ ๊ฒฝ์šฐ)
์ฃผ์š” ๋‹จ๊ณ„ checkout โ†’ install โ†’ build โ†’ test โ†’ lint
๋ฐฐํฌ ์—ฌ๋ถ€ โœ… ์šด์˜ ํ™˜๊ฒฝ ์ž๋™ ๋ฐ˜์˜
๊ธฐ๋Šฅ ์‹ค์„œ๋น„์Šค ๋ฐ˜์˜, ์•ˆ์ •์„ฑ ํ™•๋ณด ์ตœ์šฐ์„ 

๐Ÿ”ด hotfix/* ๋ธŒ๋žœ์น˜ ํŒŒ์ดํ”„๋ผ์ธ**

ํ•ญ๋ชฉ ๋‚ด์šฉ
Trigger push
์ฃผ์š” ๋‹จ๊ณ„ checkout โ†’ install โ†’ build โ†’ test โ†’ lint
๋ฐฐํฌ ์—ฌ๋ถ€ โŒ ์—†์Œ (main ๋ณ‘ํ•ฉ ์‹œ ์ž๋™ ๋ฐฐํฌ ์ง„ํ–‰)
๊ธฐ๋Šฅ ๊ธด๊ธ‰ ์ˆ˜์ • ์‚ฌํ•ญ์— ๋Œ€ํ•ด release ์ˆ˜์ค€์˜ ํ’ˆ์งˆ ์ฒดํฌ ๋ฐ ๋น ๋ฅธ ๋ฐฐํฌ ์ง€์›

6. ํŒŒ์ดํ”„๋ผ์ธ ๋‹ค์ด์–ด๊ทธ๋žจ

แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2025-04-23 11 37 14
  • ๋ชจ๋“  ๋ธŒ๋žœ์น˜๋Š” Push ๋˜๋Š” PR ์‹œ GitHub Actions CI๊ฐ€ ์ž๋™ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.
  • feature ๋ธŒ๋žœ์น˜๋Š” Lint๋งŒ ์ˆ˜ํ–‰ํ•˜๋ฉฐ, dev/release์—์„œ๋Š” Build, Test, Lint๊ฐ€ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.
  • main ๋ธŒ๋žœ์น˜์—์„œ๋Š” ๋นŒ๋“œ ์‚ฐ์ถœ๋ฌผ์ด GCS์— ์—…๋กœ๋“œ๋˜์–ด CD ํŒŒ์ดํ”„๋ผ์ธ๊ณผ ์—ฐ๊ณ„๋ฉ๋‹ˆ๋‹ค.

7. GitHub Actions ์„ค์ • ํŒŒ์ผ ์˜ˆ์‹œ

name: CI Pipeline

# ๐Ÿ“Œ 1. ์–ด๋–ค ์ด๋ฒคํŠธ์—์„œ ์‹คํ–‰ํ• ์ง€ ์„ค์ •
on:
  push:
    branches:
      - 'main'       # ์šด์˜ ๋ธŒ๋žœ์น˜
      - 'dev'        # ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ๋ธŒ๋žœ์น˜
      - 'feature/**' # ๊ธฐ๋Šฅ ๊ฐœ๋ฐœ ๋ธŒ๋žœ์น˜
      - 'hotfix/**'  # ๊ธด๊ธ‰ ์ˆ˜์ • ๋ธŒ๋žœ์น˜
  pull_request:
    branches:
      - 'main'
      - 'dev'

# ๐Ÿ“Œ 2. ๊ณตํ†ต ํ™˜๊ฒฝ ๋ณ€์ˆ˜
env:
  NODE_VERSION: '18'
  GCS_BUCKET: ${{ secrets.GCS_BUCKET_NAME }}     # GCS ๋ฒ„ํ‚ท ์ด๋ฆ„ (Secrets์— ์ €์žฅ ํ•„์š”)
  GCP_SA_KEY: ${{ secrets.GCP_SA_KEY }}          # GCP ์„œ๋น„์Šค ๊ณ„์ • ํ‚ค (JSON)
  COMMIT_HASH: ${{ github.sha }}                 # ํ˜„์žฌ ์ปค๋ฐ‹ ํ•ด์‹œ (๋ฐฐํฌ ์‹œ ๋ฒ„์ „ ์ถ”์ ์šฉ)

jobs:
  ci:
    runs-on: ubuntu-latest  # GitHub์—์„œ ์ œ๊ณตํ•˜๋Š” ๋ฆฌ๋ˆ…์Šค ํ™˜๊ฒฝ ์‚ฌ์šฉ

    steps:
      # ๐Ÿ“ฆ ์ฝ”๋“œ ์ฒดํฌ์•„์›ƒ
      - name: ๐Ÿ“ฆ Checkout code
        uses: actions/checkout@v3

      # โš™๏ธ Node.js ์„ค์น˜
      - name: โš™๏ธ Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: ${{ env.NODE_VERSION }}

      # ๐Ÿ“ฅ Node ํŒจํ‚ค์ง€ ์„ค์น˜
      - name: ๐Ÿ“ฅ Install dependencies
        run: npm ci

      # โœ… [feature ๋ธŒ๋žœ์น˜ ์ „์šฉ] Lint๋งŒ ์ˆ˜ํ–‰
      - name: ๐Ÿงน Lint (feature only)
        if: startsWith(github.ref, 'refs/heads/feature/')
        run: npm run lint

      # โฉ feature ๋ธŒ๋žœ์น˜์—์„œ๋Š” ๋นŒ๋“œ/ํ…Œ์ŠคํŠธ ์ƒ๋žต
      - name: โฉ Skip build/test (feature only)
        if: startsWith(github.ref, 'refs/heads/feature/')
        run: echo "Skip build/test for feature/*"

      # โœ… [main/dev/hotfix] ๊ณตํ†ต ๋นŒ๋“œ ๋ฐ ํ…Œ์ŠคํŠธ

      # ๐Ÿ›  ํ”„๋กœ์ ํŠธ ๋นŒ๋“œ
      - name: ๐Ÿ›  Build project
        if: "!startsWith(github.ref, 'refs/heads/feature/')"
        run: npm run build

      # ๐Ÿงช ๋ฐฑ์—”๋“œ ํ…Œ์ŠคํŠธ
      - name: ๐Ÿงช Run Backend tests
        if: "!startsWith(github.ref, 'refs/heads/feature/')"
        run: npm run test:backend

      # ๐Ÿงช ํ”„๋ก ํŠธ์—”๋“œ ํ…Œ์ŠคํŠธ
      - name: ๐Ÿงช Run Frontend tests
        if: "!startsWith(github.ref, 'refs/heads/feature/')"
        run: npm run test:frontend

      # ๐Ÿงช AI ์„œ๋ฒ„ ํ…Œ์ŠคํŠธ (Python)
      - name: ๐Ÿงช Run AI tests
        if: "!startsWith(github.ref, 'refs/heads/feature/')"
        run: |
          cd ai
          python -m venv venv
          source venv/bin/activate
          pip install -r requirements.txt
          pytest

      # ๐Ÿงน ๋ฆฐํŠธ ์‹คํ–‰ (๊ณตํ†ต)
      - name: ๐Ÿงน Lint
        if: "!startsWith(github.ref, 'refs/heads/feature/')"
        run: npm run lint

      # โ˜๏ธ ๋ฐฐํฌ ๋‹จ๊ณ„ (main ๋ธŒ๋žœ์น˜ ํ•œ์ •)

      - name: ๐Ÿ“ฆ Package backend
        if: github.ref == 'refs/heads/main'
        run: zip -r app-${COMMIT_HASH:7}.zip dist/

      - name: โ˜๏ธ Upload backend to GCS
        if: github.ref == 'refs/heads/main'
        uses: google-github-actions/upload-cloud-storage@v1
        with:
          path: app-${COMMIT_HASH:7}.zip
          destination: ${{ env.GCS_BUCKET }}/backend/
          credentials: ${{ env.GCP_SA_KEY }}

      - name: ๐Ÿ“ฆ Package frontend
        if: github.ref == 'refs/heads/main'
        run: |
          mkdir -p packaged/frontend-${COMMIT_HASH:7}
          cp -r dist/* packaged/frontend-${COMMIT_HASH:7}/

      - name: โ˜๏ธ Upload frontend to GCS
        if: github.ref == 'refs/heads/main'
        uses: google-github-actions/upload-cloud-storage@v1
        with:
          path: packaged/frontend-${COMMIT_HASH:7}
          destination: ${{ env.GCS_BUCKET }}/frontend/${{ env.COMMIT_HASH }}
          credentials: ${{ env.GCP_SA_KEY }}

      - name: ๐Ÿ“ฆ Package AI
        if: github.ref == 'refs/heads/main'
        run: zip -r ai-${COMMIT_HASH:7}.zip ai/ venv/ requirements.txt main.py

      - name: โ˜๏ธ Upload AI to GCS
        if: github.ref == 'refs/heads/main'
        uses: google-github-actions/upload-cloud-storage@v1
        with:
          path: ai-${COMMIT_HASH:7}.zip
          destination: ${{ env.GCS_BUCKET }}/ai/
          credentials: ${{ env.GCP_SA_KEY }}

๐Ÿ“ ํ”„๋กœ์ ํŠธ ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ ๋ฐ ๊ตฌ์„ฑ ํŒŒ์ผ ์ •๋ฆฌ

๐Ÿ“ฆ root/
โ”œโ”€โ”€ backend/                    # ๋ฐฑ์—”๋“œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์†Œ์Šค
โ”‚   โ”œโ”€โ”€ dist/                  # ๋นŒ๋“œ ์‚ฐ์ถœ๋ฌผ (ex. app-<commit>.zip)
โ”‚   โ””โ”€โ”€ tests/                 # ๋ฐฑ์—”๋“œ ์œ ๋‹›/ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ
โ”‚
โ”œโ”€โ”€ frontend/                  # ํ”„๋ก ํŠธ์—”๋“œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์†Œ์Šค
โ”‚   โ”œโ”€โ”€ dist/                  # ์ •์  ํŒŒ์ผ ๋นŒ๋“œ ์‚ฐ์ถœ๋ฌผ (GCS์— ์—…๋กœ๋“œ๋จ)
โ”‚   โ””โ”€โ”€ __tests__/             # ํ”„๋ก ํŠธ์—”๋“œ ์ปดํฌ๋„ŒํŠธ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ
โ”‚
โ”œโ”€โ”€ ai/                        # AI ์„œ๋ฒ„ ๊ด€๋ จ ์†Œ์Šค
โ”‚   โ”œโ”€โ”€ main.py               # AI ์„œ๋ฒ„ ์ง„์ž…์ 
โ”‚   โ”œโ”€โ”€ requirements.txt      # Python ์ข…์†์„ฑ ๋ช…์„ธ
โ”‚   โ”œโ”€โ”€ venv/                 # Python ๊ฐ€์ƒํ™˜๊ฒฝ (CI์—์„œ ์ƒ์„ฑ)
โ”‚   โ””โ”€โ”€ tests/                # pytest ๊ธฐ๋ฐ˜ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ
โ”‚
โ”œโ”€โ”€ package.json               # ๊ณตํ†ต ๋นŒ๋“œ ๋ฐ ํ…Œ์ŠคํŠธ ์Šคํฌ๋ฆฝํŠธ ์ •์˜
โ”œโ”€โ”€ .eslintrc.json             # ๋ฆฐํŠธ ๊ทœ์น™ ์„ค์ •
โ”‚
โ”œโ”€โ”€ .github/
โ”‚   โ””โ”€โ”€ workflows/
โ”‚       โ””โ”€โ”€ ci.yml            # GitHub Actions CI ํŒŒ์ดํ”„๋ผ์ธ ์„ค์ • ํŒŒ์ผ

๐Ÿงพ ์ฃผ์š” ํŒŒ์ผ ๋ฐ ํด๋” ์„ค๋ช…

๊ฒฝ๋กœ ํ•ญ๋ชฉ ์„ค๋ช…
/.github/workflows/ci.yml GitHub Actions ์„ค์ • ํŒŒ์ผ ๋ธŒ๋žœ์น˜๋ณ„ CI ์‹คํ–‰ ์ •์˜ (build/test/lint/deploy)
/package.json ๋นŒ๋“œ/ํ…Œ์ŠคํŠธ ์Šคํฌ๋ฆฝํŠธ test:backend, test:frontend, lint ๋“ฑ ๋ช…๋ น์–ด ์ •์˜
/backend/dist/ ๋นŒ๋“œ ์‚ฐ์ถœ๋ฌผ ์••์ถ• ํ›„ GCS์— ์—…๋กœ๋“œ๋จ
/frontend/dist/ ์ •์  ํŒŒ์ผ ๋นŒ๋“œ ๊ฒฐ๊ณผ GCS์— ์ „์ฒด ํด๋” ์—…๋กœ๋“œ
/ai/ AI ์„œ๋ฒ„ ์‹คํ–‰ ํŒŒ์ผ(main.py), ๊ฐ€์ƒํ™˜๊ฒฝ(venv), ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ํฌํ•จ
/ai/tests/ pytest ํ…Œ์ŠคํŠธ pytest๊ฐ€ ์ธ์‹ํ•˜๋Š” Python ํ…Œ์ŠคํŠธ ํŒŒ์ผ๋“ค
/.eslintrc.json ๋ฆฐํŠธ ์„ค์ • ESLint ๊ทœ์น™ ์ •์˜

8. GitHub Secrets ๋ชฉ๋ก

์ด๋ฆ„ ์„ค๋ช…
GCS_BUCKET_NAME ์‚ฐ์ถœ๋ฌผ ์ €์žฅ์šฉ GCS ๋ฒ„ํ‚ท ์ด๋ฆ„
GCP_SA_KEY ์„œ๋น„์Šค ๊ณ„์ • ํ‚ค (JSON ์ „์ฒด ๋‚ด์šฉ)

9. ์˜ˆ์ƒ๋˜๋Š” ๋ฌธ์ œ์  ๋ฐ ํ•œ๊ณ„

๐Ÿ• 1) CI ์‹คํ–‰ ์‹œ๊ฐ„ ์ฆ๊ฐ€

  • dev/main ๋ธŒ๋žœ์น˜์˜ ๊ฒฝ์šฐ ๋นŒ๋“œ, ํ…Œ์ŠคํŠธ, ๋ฆฐํŠธ๊นŒ์ง€ ํฌํ•จ๋˜๋ฏ€๋กœ PR๋งˆ๋‹ค ์ „์ฒด ํŒŒ์ดํ”„๋ผ์ธ ์‹คํ–‰์— ์ˆ˜๋ถ„ ์ด์ƒ ์†Œ์š”
  • ์ ์  ํ”„๋กœ์ ํŠธ๊ฐ€ ์ปค์ง€๋ฉด ๋” ๋А๋ ค์งˆ ์ˆ˜ ์žˆ์Œ

๋Œ€์‘ ๋ฐฉํ–ฅ: ์บ์‹ฑ ์ „๋žต (actions/cache), step ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ, ํ…Œ์ŠคํŠธ ๋ถ„๋ฆฌ ์‹คํ–‰ ๋„์ž… ์˜ˆ์ •


๐Ÿ’ธ 2) GitHub Actions ์‹คํ–‰ ์‹œ๊ฐ„ ์ œํ•œ ๋ฐ ์š”๊ธˆ

  • ๋ฌด๋ฃŒ tier์—๋Š” ์›”๋ณ„ ์‹คํ–‰ ์‹œ๊ฐ„ ์ œํ•œ์ด ์กด์žฌํ•จ (2,000๋ถ„/์›”)
  • ๋‹ค์ˆ˜์˜ ๋ณ‘๋ ฌ ๋ธŒ๋žœ์น˜๋‚˜ ๋นˆ๋ฒˆํ•œ Push ๋ฐœ์ƒ ์‹œ ๊ณผ๊ธˆ ๋ฐœ์ƒ ๊ฐ€๋Šฅ์„ฑ

๋Œ€์‘ ๋ฐฉํ–ฅ: Lint๋งŒ ์‹คํ–‰ํ•˜๋Š” ๋ธŒ๋žœ์น˜ ๊ตฌ๋ถ„, ํ•„์š”ํ•œ ์กฐ๊ฑด์—์„œ๋งŒ full CI ์‹คํ–‰


๐Ÿ“Œ ๋ณธ ํŽ˜์ด์ง€๋Š” 2025๋…„ 5์›” 12์ผ์— ๋งˆ์ง€๋ง‰์œผ๋กœ ์—…๋ฐ์ดํŠธ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

โš ๏ธ **GitHub.com Fallback** โš ๏ธ