CI - 100-hours-a-week/9-team-Devths-WIKI GitHub Wiki
๋ชฉ์ฐจ (ํผ์น๊ธฐ/์ ๊ธฐ)
๐ก CI (Continuous Integration)์ด๋? ์ฌ๋ฌ ๊ฐ๋ฐ์๊ฐ ๋ณ๊ฒฝํ ์ฝ๋๋ฅผ ์ฃผ๊ธฐ์ ์ผ๋ก ํตํฉํ๊ณ , ๋น๋ ๋ฐ ํ ์คํธ ์๋ํ๋ฅผ ํตํด ํ์ง์ ์ ์งํ๋ ๊ฐ๋ฐ ๋ฐฉ๋ฒ๋ก ์ ๋๋ค.
1. ์ฝ๋ ๋ณํฉ ๊ณผ์ ์์์ ํ๊ณ
- ๊ฐ ํ์ ๋ ๋ฆฝ ๋ ํฌ๋ฅผ ์ฌ์ฉํ๊ณ ์์ผ๋, ๊ณตํต๋ ๋ฆด๋ฆฌ์ฆ ์ผ์ ์ ๋ง์ถ๊ธฐ ์ํด ๊ฐ์์ develop ๋ธ๋์น์์ ๋น๋ฒํ ๋ณํฉ ์์ ์ ์ํํจ.
- PR ์์ ์๋ง ์๋ ๋ฆฌ๋ทฐ์ ํ ์คํธ๋ฅผ ์งํํ๋ค ๋ณด๋, ๋ฆฐํธ ์ค๋ฅ๋ ํ ์คํธ ์คํจ๊ฐ ๋ค๋ฆ๊ฒ ๋ฐ๊ฒฌ๋์ด ๋ฆฌ๋ทฐ ๋ณ๋ชฉ๊ณผ ์ผ์ ์ง์ฐ์ด ๋ฐ์ํ๋ ์ฌ๋ก ์กด์ฌ.
2. ๋ฐฐํฌ ๋น๋์ ์ฆ๊ฐ
- ์๋น์ค๋ฅผ ๋น ๋ฅด๊ฒ ๊ฐ์ ํ๊ณ ๊ธฐ๋ฅ์ ๊ฒ์ฆํ๊ธฐ ์ํด ๋ฐฐํฌ ํ์๊ฐ ์ฆ๊ฐํจ.
- ๋ฐ๋ณต์ ์ด๊ณ ์๋์ ์ธ ๋ฐฐํฌ ๊ณผ์ ์์๋ ์ค๋ฅ๊ฐ ๋์ ๋๊ณ , ๋ฐฐํฌ ์๋๊ฐ ๋๋ ค ์ฌ์ฉ์ ํผ๋๋ฐฑ ๋ฐ์์ด ๋ฆ์ด์ง.
- CI ํ์ดํ๋ผ์ธ์ ํตํด ์๋ ๋น๋์ ํ ์คํธ๋ฅผ ์ํํ๋ฉด, ๋น ๋ฅธ ๋ฐฐํฌ๊ฐ ๊ฐ๋ฅํ๊ณ ๊ฐ๋ฐ ์์ฐ์ฑ์ ๋์ผ ์ ์์.
3. ์ฝ๋ ํ์ง ๊ด๋ฆฌ ์ฒด๊ณ์ ๋ฏธํก
- ๊ฐ ๋ ํฌ๋ณ๋ก ํ ์คํธ ์ ์ฑ ์ด๋ ๋ฆฐํธ ๊ท์น์ด ์ ๋ฆฝ๋์ง ์์, ์ผ๋ถ ๋ธ๋์น์์๋ ํ ์คํธ๊ฐ ๋๋ฝ๋๊ฑฐ๋ ๋ฆฐํธ๊ฐ ๋ฌด์๋ ์ฑ ๋ณํฉ๋๋ ์ฌ๋ก ๋ฐ์.
- ํ์ฌ main ๋ธ๋์น ๋ณํฉ ์์ ์๋ ์ฝ๋ ์ ์ ๋ถ์, ํ ์คํธ ์๋ํ๊ฐ ์ถฉ๋ถํ ์ ์ฉ๋์ง ์์, ์ด์ ๋ฐ์ ์ง์ ์ ์ค๋ฅ๊ฐ ๋ฐ๊ฒฌ๋๋ ๊ฒฝ์ฐ๊ฐ ๋ฐ์ํจ.
- ํ์ ๊ฐ ํ์ ๊ฒฝํ์ด ๋ค๋ฅด๋ค ๋ณด๋ ํ ์คํธ ์ฝ๋ ์์ฑ ๋ฐ ์คํ ์ต๊ด์ด ๋ถ์กฑํ์ฌ ์ฝ๋ ํ๋ฆฌํฐ์ ๋ํ ์ ๋ขฐ์ฑ ํ๋ณด๊ฐ ์ด๋ ค์.
4. ์ด์ ํ๊ฒฝ๊ณผ ๊ฐ๋ฐ ํ๊ฒฝ์ ๋ถ๋ฆฌ ๋ฏธํก
- ๊ธฐ์กด์๋
GitHub Flow๋ก์, main๊ณผ feat ๋ง์ผ๋ก ์ด์ํ์ฌ ํจ์ฌ ๊ฐ๋จํ์ง๋ง ๋น ๋ฅด๊ฒ ์์ ๋ฐฐํฌํ ์ ์๋ ์ ๋ต์ ์ฌ์ฉํจ. - ํ์ง๋ง ์ด์ ์๋ฒ์ ๊ฐ๋ฐ ์๋ฒ๊ฐ ๋ช ํํ ๋ถ๋ฆฌ๋์ด ์์ง ์์, ํ ์คํธ ๊ณผ์ ์์ ๋ฐ์ํ ๋ฌธ์ ๋ ๋ฒ๊ทธ๊ฐ ์๋น์ค ์ ์ฒด ๊ฐ์ฉ์ฑ์ ์ง์ ์ ์ธ ์ํฅ์ ๋ฏธ์น ์ํ์ด ์๋ค๊ณ ํ๋จํจ.
- Git Flow ๊ธฐ๋ฐ์ผ๋ก feature โ develop โ Release(Staging) โ main ์์ผ๋ก PR๊ณผ ๋จธ์ง๋ฅผ ์งํ
- ๊ฐ ๋จ๊ณ์์ Lint โ Static Analysis โ Unit/Integration Test โ Build โ Artifact ์ ์ฅ โ Discord ์๋ฆผ์ผ๋ก CI๋ฅผ ์งํ
- ์ฌ์ฉํ๋ ํ๋ซํผ : Github Actions
- ํ๊ฒฝ : AWS EC2 ์ธ์คํด์ค (๊ธฐ์กด 2๊ฐ / ์ํฉ์ ๋ฐ๋ผ์ 3๊ฐ)
- ํ์ ๊ด๋ฆฌ : GItHub
- ๋ฐฐํฌ ๋์: ํ๋ก ํธ์๋, ๋ฐฑ์๋, AI ์๋ฒ, DB ์ ๋ถ ๋จ์ผ ์ธ์คํด์ค์์ ์ด์
- ๋ธ๋์น ์ ๋ต : ๊ฐ๋ฐ / ์คํ ์ด์ง / ์ด์ ์๋ฒ ๊ตฌ์ถ
๋๊ตฌ ์ ์ ์ ๊ธฐ์ค์
GitHub ํตํฉ์ฑ, ์๋, ๋น์ฉ, ๋ณด์ ๋ชจ๋ธ
CI/CD ๋๊ตฌ ์ ์ ์จ [Jetbrain ์ฐธ๊ณ ๋งํฌ] GitLab/Azure ํ๊ฒฝ์ ์ฌ์ฉํ์ง์๊ธฐ์ ์ ์ธํ์์ต๋๋ค.
| ํญ๋ชฉ | GitHub Actions (์ ํ) | Jenkins | Circle CI | Travis CI |
|---|---|---|---|---|
| GitHub ํตํฉ์ฑ | ์์ ํตํฉ, ๋ณ๋ ์ฐ๋ ๋ถํ์ | Webhook ์ฐ๋ ๋ฐ ์ค์ ํ์ | OAuth ์ฐ๋ ํ์ | OAuth ์ฐ๋ ํ์ |
| ์ค์ ๋ฐฉ์ | YAML ๊ธฐ๋ฐ | Groovy ์คํฌ๋ฆฝํธ ๋๋ UI ์ค์ | YAML ๊ธฐ๋ฐ | YAML ๊ธฐ๋ฐ |
| ์๋ ๋ฐ ์บ์ฑ | ์ฐ์ (GitHub ์ธํ๋ผ ํ์ฉ, ์บ์ฑ ์ก์ ์ง์) | ์ด์ ์ฌ์์ ๋ฐ๋ผ ๋ค๋ฆ (์์ฒด ์๋ฒ ์ฑ๋ฅ์ ์์กด) | ์ต์ (Docker ๋ ์ด์ด ์บ์ฑ ๋ฐ ๋ณ๋ ฌํ ํนํ) | ๋ณดํต (๋์ ์คํ ์ ํ ์กด์ฌ) |
| ๊ด๋ฆฌ ๋ถ๋ด | ์ต์ (์๋ฒ ๊ด๋ฆฌ ๋ถํ์) | ์ต๊ณ (๋ง์คํฐ/์์ด์ ํธ ๊ด๋ฆฌ) | ๋ฎ์ (SaaS UI ์ฌ์ฉ) | ๋ฎ์ (SaaS UI ์ฌ์ฉ) |
| ๋น์ฉ | Public ์ด๊ธฐ์ ๋ฌด๋ฃ | ์คํ์์ค๋ก์ ์๋ฒ ์ง์ ์ด์ | ์ต๋ 6,000 ๋ถ๊น์ง ๋ฌด๋ฃ ์ฌ์ฉ | ์ ๋น์ฉ $15 ๋ฐ์ |
โ GitHub Actions๋ GitHub ๊ธฐ๋ฐ ์ํฌํ๋ก์ฐ์ด๊ธฐ์ ๋จธ์ง ๊ท์น, ๊ถํ/์ํฌ๋ฆฟ ๊ด๋ฆฌ์ ๊ฐ์ ํตํฉ์ฑ์ด ์ ์ผ ์ข์ต๋๋ค. ๋ํ, ๋ฒ์ ๋ ๊ด๋ฆฌ์์ด ์ฝ๊ฒ ์๋ํํ ์ ์์ผ๋ฉฐ, ๋น์ฉ ๋ถ๋ด ์์ด ๋น ๋ฅด๊ฒ ๊ตฌ์ถ ๊ฐ๋ฅํ๊ธฐ์ ์ ์ ํ๊ฒ ๋์์ต๋๋ค.
๋ธ๋์น ํ๋ฆ๋ ์์ฝ
(feat/*) โ develop โ release โ main
โ
hotfix/*
์ด์ ์ ์ ์์ฌํญ
| ๋ธ๋์น ์ด๋ฆ | ์ฉ๋ | ๋ณํฉ ๋ฐฉ์ |
|---|---|---|
main |
์ด์ ๊ธฐ์ค ์์ ํ ์ฝ๋ | PR ๋ฆฌ๋ทฐ ํ ๋ณํฉ |
release |
๋ฐฐํฌ ์ ์ด์ ํ๊ฒฝ ๊ฒ์ฆ, QA ๊ฒ์ฆ | dev โ release โ main |
develop |
๊ฐ๋ฐ ํตํฉ ๋ธ๋์น | PR ๋ฆฌ๋ทฐ ํ ๋ณํฉ |
feat/* |
๊ธฐ๋ฅ ๊ฐ๋ฐ | Push |
hotfix/* |
๊ธด๊ธ ํจ์น ๋ฐ ์ด์ ๋ฒ๊ทธ ์์ | main์์ ๋ถ๊ธฐ โ main ๋ก ๋ณํฉ |
๋ณ๋์ ์์ Release ์๋ฒ๋ฅผ ์ด์ํ๋ ๊ฒ์ด ์๋๋ผ ์ ํ์ ์ผ๋ก Release ํ๊ฒฝ์ ๊ตฌ์ฑํ๋ ์ ๋ต์ ์ฑํํ์์ต๋๋ค.
1. ๋ฐฐํฌ ์์ ์ฑ ํ๋ณด
- Release ํ๊ฒฝ์ ํ์ ์์๋ง ๊ตฌ์ฑํ๋ฉด,
- ์ด์ ํธ๋ํฝ๊ณผ ๋ถ๋ฆฌ๋ ๊ณต๊ฐ์์ ๋ฐฐํฌ ์ ๊ฒ์ฆ(QA/๋ถํ)์ ์ํํ ์ ์๊ณ
- ๋ฌธ์ ๋ฐ์ ์์๋ Dev/Main์ ๋ฏธ์น๋ ์ํฅ์ ์ต์ํํ ์ ์์ต๋๋ค.
- ๋ํ ๋ฆด๋ฆฌ์ฆ ์ง์ ์ด์๊ณผ ๋์ผํ ์กฐ๊ฑด์์ ๊ฒ์ฆ ํ ๋ฐฐํฌํจ์ผ๋ก์จ, ๋ฐฐํฌ ์์ ์ฑ์ ๋์ผ์ ์์ต๋๋ค.
2. ๋ฆฌ์์ค ๋ฐ ๋น์ฉ
- ๋ถํํ ์คํธ ๋ฐ QA๋ฅผ ์งํํ๊ธฐ ์ํด์ ์ด์ ์๋ฒ์ ๋์ผํ๊ฒ ์งํํด์ผํ๊ธฐ์, ๋์ผํ ์คํ(High-spec)์ ํ๊ฒฝ์ด ํ์์ ๋๋ค.
- Release ํ๊ฒฝ ์์ด Dev ์๋ฒ๋ก๋ง ๊ฐ๋ํ ๊ฒฝ์ฐ, ์ค์ ํธ๋ํฝ ์ฌ์ฉ๋ฅ ์ด ๋ฎ์์๋ ๋ถ๊ตฌํ๊ณ ํ
์คํธ๋ฅผ ์ํด ๋์ ๋น์ฉ์ ์ ์งํด์ผ ํฉ๋๋ค.
- ์ด์๊ณผ ๋์ผํ
$75.92์ ๋น์ฉ์ด ์์๋๋ฉฐ, ์ด์/๊ฐ๋ฐ์ ์ฝ$152๋ฌ๋ฌ ์ฒญ๊ตฌ ์์
- ์ด์๊ณผ ๋์ผํ
- ์ ํ์ ์ผ๋ก Release ํ๊ฒฝ์ ๊ตฌ์ฑํ๊ฒ ๋๋ค๋ฉด, ๊ฐ๋ฐ ํ๊ฒฝ์์๋ ๋ฎ์ ์คํ์ผ๋ก ์งํํด๋ ๋๋ค๊ณ ํ๋จํ์ต๋๋ค.
- Idleํ ์ํ์์ ๊ธฐ๋ณธ ์ค์น ์, ํ์ํ ๋ฉ๋ชจ๋ฆฌ 1.2GB
- ๊ฐ๋ฐ ํ๊ฒฝ์์๋ ์ง์ฐ ์๊ฐ์ด ํ์ฉ๋๋ค๊ณ ํ๋จํ์ฌ, ์ค์ ๋ฉ๋ชจ๋ฆฌ ํตํ ์ถ๊ฐ ๋ฉ๋ชจ๋ฆฌ ํ๋ณด
- ํ๋ก์ธ์ค๊ฐ ๋ง์ ๋จ์ผ ์ธ์คํด์ค ํน์ฑ ์, Context Switching ์ด ์ ๋๋ก CPU๊ฐ ๋ง์ ์ธ์คํด์ค๊ฐ ํ์ํ๋ค๊ณ ํ๋จ
- t2์ ๋นํด์ CPU๊ฐ 1์ฝ์ด ๋ ๋ง๊ณ ์ ๋ ดํ t3๋ฅผ ์ ์
- ๊ฐ๋ฐํ๊ฒฝ์์๋
t3.small1๋ ์ค์ ํ์ฌ, ํ ๋ฌ(744์๊ฐ)์ ์ฝ$22์ฒญ๊ตฌ ์์
โ 35.5% ๋ ์ ๋ ดํ๊ฒ ์ด์๊ฐ๋ฅ
3. ์ด์์ ๋ณต์ก์ฑ ๊ฐ๋ฅ์ฑ ์กด์ฌ
- ํด๋ผ์ฐ๋ ํ๊ฒฝ์ ์ต๋ 3๊ฐ๊น์ง ๊ตฌ์ถํด์ผ๋๋ค๋ ๋จ์ ์ด ์กด์ฌํฉ๋๋ค.
- ํ์ง๋ง, Artifact์ ์ ์ง ๋ฐ CD์ผ๋ก์ ํ์ฅ์ฑ์ ๊ณ ๋ คํ๋ค๋ฉด ์๋ ๋ฐฐํฌ์ ํ๊ฒฝ๋ณด๋ค๋ ์ด์์ ๋์ด๋๊ฐ ๋ฎ์์ง๋ค๊ณ ํ๋จํ๊ณ , ๋น์ฉ์ ์ธก๋ฉด๊ณผ ์์ ์ฑ์ด ๋์ด๋๋ค๋ ์ฅ์ ์ ํํ๊ธฐ๋ก ํ๋จํ์ต๋๋ค.
| ๋ธ๋์น ์ด๋ฆ | ํ์ดํ๋ผ์ธ ์ค๋ช |
|---|---|
main |
lint โ ์ ์ ๋ถ์ โ ๋จ์/ํตํฉ ํ ์คํธ โ build โ artifact |
release |
lint โ ์ ์ ๋ถ์ โ ๋จ์/ํตํฉ ํ ์คํธ โ build โ artifact |
develop |
lint โ ์ ์ ๋ถ์ โ ๋จ์/ํตํฉ ํ ์คํธ โ build โ artifact |
hotfix/* |
lint โ ์ ์ ๋ถ์ โ ๋จ์/ํตํฉ ํ ์คํธ โ build โ artifact |
feat/* |
- |
- ๊ฐ ๋จ๊ณ๋ฅผ ์คํํ ๋, ํ๋์ ํ์ดํ๋ผ์ธ์ด๋ผ๋ ์คํจ ์, merge ๋ถ๊ฐ๋ฅ
3-4-1. Lint
์์ค ์ฝ๋์ ์คํ์ผ ๋ถ์ผ์น, ๊ตฌ๋ฌธ ์ค๋ฅ ๋ฐ ์ฝ๋ฉ ๊ท์น ์ค์ ์ฌ๋ถ๋ฅผ ๋ถ์
- ๋ชฉ์ : ์ฝ๋ ์คํ์ผ/ํฌ๋งท/๊ธฐ์ด ๊ท์น์ ๋น ๋ฅด๊ฒ ํต๊ณผ์ํค๊ณ PR ํ์ง์ ๊ท ์ผํ
- ๋์: ๊ณตํต
- ์ฌ์ฉ ๋๊ตฌ
- Front:
eslint,prettier - Python:
ruff,eslint,prettier - Java:
eslint,prettier
- Front:
- Lint ์คํจ ์, PR ๋จธ์ง ์คํจ
3-4-2. ์ ์ ๋ถ์
์์ค ์ฝ๋๋ฅผ ์คํํ์ง ์๊ณ ์ทจ์ฝ์ ์๋ณ, ๋ฒ๊ทธ, ์ฑ๋ฅ ๋ฌธ์ , ์ฝ๋ฉ ํ์ค ๋ฏธ์ค์ ๋ฑ์ ๋ํ ์ ์ฌ์ ์ธ ๋ฌธ์ ์ ๋ํด ๋ถ์
- ์ฐ๋ฆฌ ์๋น์ค, ์ ์ ๋ถ์ ๋๊ตฌ์ ์ ํ ๊ธฐ์ค์
๋น์ฉ๊ณผ ์ทจ์ฝ์ ์๋ณ ๊ณผ ๋ฒ๊ทธ ์๋ณ์ด๋ค.์ ์ ๋ถ์ ์ด์ ์ Lint์ ์์ ์ด ์๊ธฐ์ ์ฝ๋ฉ ํ์ค ๋ฏธ์ค์๋ ๊ฐ์ค์น๋ฅผ ๋ฎ๊ฒ ๋๊ณ ์งํํจ
| SonarQube | CodeQL(์ ํ) | Code Climate | Codacy | Semgrep | |
|---|---|---|---|---|---|
| ์ฝ๋ฉ ํ์ค ๋ฏธ์ค์ | ๊ฐ๋ฅ (Maintainability/Code Smell ๊ท์น + Quality Profile/Quality Gate๋ก ๊ฐ์ ) | ๊ฐ๋ฅ(์ฃผ๋ก โ์ฝ๋ฉ ์ค๋ฅ/์ทจ์ฝ์ โ ์ค์ฌ. ํ์ํ๋ฉด ์ปค์คํ ์ฟผ๋ฆฌ๋ก ๊ท์นํ ๊ฐ๋ฅ) | ๊ฐ๋ฅ(์์ง/๋ฃฐ ๊ธฐ๋ฐ์ผ๋ก ์คํ์ผยท์ค๋ณตยท๋ณต์ก๋ ๋ฑ โ์ ์ง๋ณด์์ฑโ ์๋ฐ์ ์ก๋ ์ฉ๋์ ๊ฐํจ) | ๊ฐ๋ฅ(IDE/PR์์ โcoding standardsโ๋ฅผ ๊ณต์ ยท๊ฐ์ + ๋ฃฐ ์ปค์คํฐ๋ง์ด์ฆ) | ๊ฐ๋ฅ(์ปค์คํ ๋ฃฐ๋ก ์ฝ๋ฉ ํ์ค/๊ฐ๋๋ ์ผ ๊ฐ์ ์ ์ต์ ) |
| ๋ฒ๊ทธ ์๋ณ | ๊ฐ๋ฅ | ๊ฐ๋ฅ | ์ผ๋ถ ๊ฐ๋ฅ | ๊ฐ๋ฅ | ๊ฐ๋ฅ |
| ์ทจ์ฝ์ ์๋ณ | ๊ฐ๋ฅ(Security = vulnerability & hotspot) | ๊ฐ๋ฅ(โvulnerabilitiesโ ์๋ณ์ ๋ช ์) | ๊ธฐ๋ณธ์ ์ฝ๋ ํ์ง ์ค์ฌ. ๋ณด์์ ๋ณ๋ ์์ง/๋๊ตฌ ์ฐ๋ ์ฑ๊ฒฉ์ด ๊ฐํจ | ๊ฐ๋ฅ(SAST ์ทจ์ฝ์ + ํ๋์ฝ๋ฉ ์ํฌ๋ฆฟ + ์ทจ์ฝ ์์กด์ฑ ๋ฑ) | ๊ฐ๋ฅ(Semgrep Code๋ SAST๋ก ์ทจ์ฝ์ ํ์ง) |
| ๋น์ฉ | - (Server) ์ง์ ์ด์ ์ ์ธํ๋ผ/DB ์ด์๋น ๋ฐ์- (Cloud) Free: private 50k LOC / Team $32/mo~ | - Public repo๋ ์ผ๋ถ ๋ณด์ ๊ธฐ๋ฅ์ ๋ฌด๋ฃ๋ก ์ ๊ณต- Private repo์์ Code Scanning ๋ฑ ์ฐ๋ ค๋ฉด GitHub Code Security/Advanced Security ๋ผ์ด์ ์ค ํ์(ํ์ฑ ์ปค๋ฏธํฐ ๊ธฐ์ค ๊ณผ๊ธ/๋ผ์ด์ ์ฑ) | - Open Source $0- Startup $0(์ต๋ 4 seats)- Team $16.67/seatยทmo(์ฐ๊ฐ) ๋๋ $20/seatยทmo(์๊ฐ) | ์ **$18** |
- Community Edition Free- Teams $40/์/์ปจํธ๋ฆฌ๋ทฐํฐ (์ต๋ 10๋ช ๊น์ง ๋ฌด๋ฃ ๊ตฌ๊ฐ ํ๊ธฐ) |
| ํน์ง | - ๊ท์น์ด Bug / Code Smell / Vulnerability / Security Hotspot๋ก ์ฒด๊ณํ (Sonar Documentation)- ๊ธฐ๋ณธ ๋ด์ฅ DB(H2)๋ ํ ์คํธ์ฉ์ด๊ณ , ์ด์์ ์ธ๋ถ DB(์: PostgreSQL ๋ฑ) ๊ถ์ฅ | - GitHub โcode scanning alertsโ๋ก ๊ฒฐ๊ณผ๊ฐ ๋ฐ๋ก PR/๋ฆฌํฌ์งํ ๋ฆฌ์ ๋ถ์ (GitHub Docs)- Public repo๋ ๋ฐ๋ก ์ฌ์ฉ ๊ฐ๋ฅ, ์กฐ์ง/Private๋ Code Security ํ์ฑํ ํ์ | - ์ข์(Seat) ๊ธฐ๋ฐ ๊ณผ๊ธ + GitHub PR ์ฐ๋ ์ ๊ณต (Code Climate)- ์ฌ๋ฌ ์คํ์์ค ๋ถ์ ์์ง์ โ์์ง/ํ๋ฌ๊ทธ์ธโ ํํ๋ก ๋ถ์ด๋ ์ํ๊ณ | - โUnlimited LOCโ๋ฅผ ๊ฐ์กฐํ๋ ์ข์ ๊ธฐ๋ฐ(ํ๋์ ๋ฐ๋ผ PR ์ค์บ/๋ฆฌํฌ ์ ๋ฑ ์ฐจ๋ฑ) (codacy.com)- SAST/์ํฌ๋ฆฟ/์์กด์ฑ + ํ์ง(์ค๋ณต/๋ณต์ก๋/์ฑ๋ฅ ์ด์)๊น์ง ํ ํ๋ฉด์์ ๊ด๋ฆฌ | - ์คํ์์ค CLI + ๋ฃฐ(ํจํด) ๊ธฐ๋ฐ์ด๋ผ ์ปค์คํ ๋ฃฐ ์์ฑ/์ ์ฉ์ด ๊ฐ์ (GitHub)- Teams๋ ์ ํ๋ณ(์ฝ๋/SCA/์ํฌ๋ฆฟ)๋ก ๋ถ๋ฆฌ ๊ณผ๊ธ ๊ตฌ์กฐ |
โ CodeQL์ GitHub ์ ์ฅ์/PR ํ๋ฆ์ โ๋ด์ฅํโ์ผ๋ก ๋ถ๊ณ , ์ทจ์ฝ์ ๋ฐ ์ค๋ฅ(๋ฒ๊ทธ์ฑ) ํ์ง๊ฐ ๊ณต์์ ์ผ๋ก ๋ชฉ์ ์ ํฌํจ ๋์ด ์์ด, ์ด๊ธฐ ์ด์ ๋น์ฉ๊ณผ ๋์ /์ด์ ์ค๋ฒํค๋๋ฅผ ์ต์ํํ๋ฉด์ ํ์ํ ์์ค์ ๋ณด์ยท๋ฒ๊ทธ ํ์ง๋ฅผ ํ๋ณดํ ์ ์๊ธฐ์ ์ ์ ํ์์ต๋๋ค.
3-4-3. ํ ์คํธ
- ๋ชฉ์ : โ์ฝ๋๊ฐ ์๋๋๋ก ๋์ํ๋์งโ๋ฅผ ์คํ์ผ๋ก ๊ฒ์ฆ
- ๋ฒ์
- ๋จ์ ํ ์คํธ(Unit Test): ํจ์/๋ชจ๋ ๋ ๋ฒจ ๋ก์ง ๊ฒ์ฆ (๊ฐ์ฅ ๋น ๋ฅด๊ณ ๋ง์ด)
-
ํตํฉ ํ
์คํธ(Integration Test): DB/์ธ๋ถ API ์ฐ๋์์ ํ
์คํธ ๊ฒ์ฆ
- ์ฐ๋ฆฌ์๋น์ค๋ ์ธ๋ถ API๊ฐ 3๊ฐ๋ก ๋ง๊ธฐ์, ์ค์ ํธ์ถ ๋์ Mock/Stubbing์ผ๋ก ํญ์ ๊ฐ์ ์๋ต์ ๋ฐ๊ฒ ๋ง๋ค์ด์ ํ ์คํธ ์ ๋ขฐ๋ ๋์ด๊ณ ์ ํฉ๋๋ค.
- ์ฐ์ถ๋ฌผ
- ํ ์คํธ ๋ฆฌํฌํธ ์ ๋ก๋
- ์ปค๋ฒ๋ฆฌ์ง ๋ฆฌํฌํธ : โ์ ์ง๋ณด์ ๊ฐ๋ฅํ ์์ค์ ์ต์ ๊ธฐ์คโ๋ง ์ค์
3-4-4. ๋น๋
-
๋ชฉ์ : ๋ฐฐํฌ ๊ฐ๋ฅํ ์ฐ์ถ๋ฌผ (์คํํ์ผ/์ ์ ํ์ผ/ํจํค์ง) ์ ์์ฑ
-
์ํ ์์
- ์ข
์์ฑ ์ค์น ๋ฐ ๋น๋
- Front:
npm run build(์ ์ ์ฐ์ถ๋ฌผ ์์ฑ) - Backend(Java):
gradle build(jar ์์ฑ) - Backend(Python): ํจํค์ง/์์กด์ฑ ๊ณ ์ ํ ์คํ ๋จ์ ๊ตฌ์ฑ
- Front:
- ๋น๋ ์บ์ ํ์ฉ : ์์กด์ฑ ์บ์๋ก CI ์๋ ์์ ํ
- ์ข
์์ฑ ์ค์น ๋ฐ ๋น๋
-
ํ์ง ๊ธฐ์ค
- ๋ถํ์ํ ๋น๋ ๋ญ๋น ๋ฐฉ์ง์ํด์, ๋น๋๋ โํ ์คํธ ํต๊ณผ ํโ ์๋ง ์ํ
- ๋น๋ ๊ฒฐ๊ณผ๋ฌผ์ ๋ฒ์ ์๋ณ์(commit hash / tag / build number)๋ฅผ ๋ฐ๋์ ํฌํจ
3-4-5. Artifact ์ ๋ก๋๋ฅผ ํด์ผํ๋ ์ด์
- artifact๋ ๋น๋๋ ์คํ ํ์ผ ๋๋ ์ ์ ํ์ผ์ ์๋ฏธํ๋ฉฐ, ์ปค๋ฐ ํด์๋ฅผ ๊ธฐ์ค์ผ๋ก ์ ์ฅ๋์ด ๋กค๋ฐฑ ์ ์ ํํ ์์ ๋ณต์์ด ๊ฐ๋ฅํฉ๋๋ค.
- ๋ค์ค ์ธ์คํด์ค์ ํ์ฅ์ฑ์ ๊ณ ๋ คํด์ ์ฐ์ถ๋ฌผ์ ๊ณตํต์ ์ ์ฅ์์ ์ ์ฅํ๋ ๊ณผ์ ์ด ํ์ํ๋ค.
4-1-1. ์๋๋ฅผ ๋น ๋ฅด๊ฒ ๋ง๋ค๊ธฐ ์ํ ์บ์ฑ
์ค์ ์ด๋ฏธ์ง ํ์ธ
- ๋งค๋ฒ ๋น๋๋ฅผ ์งํํ ๋๋ง๋ค ์๋๊ฐ ๋งค์ฐ ๋๋ฆฌ๊ธฐ์ ์ด๋ค ๊ฒ์ด ๋ฌธ์ ์ธ๊ฐ ํ์ธ์ ํด๋ณด์๋๋, Gradle๊ณผ ๊ฐ์ ์์กด์ฑ ์ค์น์์ 4๋ถ 40์ด๊ฐ ๊ฑธ๋ฆฌ๋ ๋ชจ์ต์ ํ์ธํ ์ ์์
- Gradle ์บ์ฑ์ผ๋ก
98.9%๊ฐ์
4-1-2. ํตํฉ ํ ์คํธ๋ฅผ ์ํ ํ๊ฒฝ ์ค์ ๊ตฌ์ฑ
ํตํฉ ํ ์คํธ๋ฅผ ์ํ ํ๊ฒฝ ์ค์ ๊ธฐ์ค
๋ฉฑ๋ฑ์ฑ, ๊ฒฉ๋ฆฌ์ฑ, ์ ์ง๋ณด์์ผ๋ก ์ก์์ต๋๋ค.
| ์ข ๋ฅ | ์ง์ ์ค์นํด์ ํ๋ก์ธ์ค๋ก ๋์ฐ๊ธฐ | ๊นํ๋ธ ์์ฒด ์ปจํ ์ด๋ ์ฌ์ฉ [๊ทผ๊ฑฐ] | self-hosted Runner |
|---|---|---|---|
| ์๋ | ๋งค๋ฒ ํจํค์ง ๋ค์ด๋ก๋ ๋ฐ ๋น๋/์ค์น ์๊ฐ ๋ฐ์ | ์ด๋ฏธ์ง Pull ๋ฐ ์ปจํ ์ด๋ ๊ธฐ๋ ์๊ฐ ํ์ | Runner์ ์์กด์ฑ/์บ์๊ฐ ์ด๋ฏธ ์์ผ๋ฉด ๋งค์ฐ ๋น ๋ฆ. |
| ํน์ง |
apt install์ ์คํฌ๋ฆฝํธ์ ์ํฌํ๋ก์ฐ์ ์ง์ ๋ช
์ |
jobs.<id>.services ์ค์ ์ ํตํด ์ ์ธ์ ์ผ๋ก ๊ตฌ์ฑ |
์ค์ EC2์ ํ ์คํธ์ฉ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ฌ์ ๊ตฌ์ฑ |
| ์ฅ์ | Docker ์ง์ ์์ด๋ ๋ฆฌ๋ ์ค ๋ช ๋ น์ด๋ง์ผ๋ก ์ฆ์ ํ๊ฒฝ ๊ตฌ์ฑ ๊ฐ๋ฅ. | ๋งค๋ฒ ๊นจ๋ํ ์ํ์์ ํ ์คํธ ์งํ ๊ฐ๋ฅ | VPC ๋ด๋ถ์ ๋์ด ๋ณด์ ํต๊ณผ ์์ด ๋ด๋ถ DB/API์ ๊ณ ์ ์ ๊ทผ ๊ฐ๋ฅ |
| ๋ฌธ์ ์ | Runner์์ ํจํค์ง ์ค์น/ํ๊ฒฝ ์ฐจ์ด๋ก ๋ถ์์ ํด์ง ์ ์์ | ๊นํ๋ธ ๊ณต์ฉ ์๋ฒ์ CPU/RAM ํ๊ณ๋ก ์ฑ๋ฅ ๋ณ๋ชฉ ๊ฐ๋ฅ์ฑ ์กด์ฌ | ๋ฐ์ดํฐ ํ๊ฒฝ ์ค์ผ ๋ฌธ์ ๋ฐ EC2์ ๋ฆฌ์์ค ๋ถ์กฑ ๋ฌธ์ ๊ฐ๋ฅ์ฑ |
โ GitHub Service Containers ๋ฐฉ์์ ์ฑํํ์์ต๋๋ค.
1. ๋ฉฑ๋ฑ์ฑ
- Self-hosted Runner๋ ์ด์ ํ ์คํธ๊ฐ ๋จ๊ธด ๋ฐ์ดํฐ๋ ํ๋ก์ธ์ค๊ฐ ๋ค์ ํ ์คํธ์ ์ํฅ์ ์ฃผ๋ 'ํ๊ฒฝ ์ค์ผ' ๋ฌธ์ ๊ฐ๋ฅ์ฑ์ด ์กด์ฌํฉ๋๋ค.
- Service Containers๋ Job ์์ ์ ์ปจํ ์ด๋๋ฅผ ์์ฑํ๊ณ ์ข ๋ฃ ์ ์ฆ์ ํ๊ธฐํฉ๋๋ค. ์ด๋ค ์์ ์ ์คํํ๋๋ผ๋ ํญ์ ์๋ฒฝํ๊ฒ ๊นจ๋ํ ์ํ(Fresh State)์์ ํ ์คํธ๊ฐ ์์๋๋ฏ๋ก, ์ฝ๋ ๋ณ๊ฒฝ ์ธ์ ์ธ๋ถ ์์ธ์ผ๋ก ํ ์คํธ ๊ฒฐ๊ณผ๊ฐ ๋ฌ๋ผ์ง์ง ์๋ ๋ฉฑ๋ฑ์ฑ์ ๋ณด์ฅํฉ๋๋ค.
2. ๊ฒฉ๋ฆฌ์ฑ : ์ถฉ๋ ์๋ ๋ณ๋ ฌ ํ ์คํธ
- Self-hosted Runner๋ ์ฌ๋ฌ ๋น๋๊ฐ ๋์์ ๋์๊ฐ ๊ฒฝ์ฐ ํฌํธ ์ถฉ๋์ด๋ ๋ฆฌ์์ค ๊ฐ์ญ์ด ๋ฐ์ํ ์ ์์ต๋๋ค.
- ๊ฐ ํ ์คํธ๋ ๋ ๋ฆฝ๋ Docker ์ปจํ ์ด๋ ๋คํธ์ํฌ ๋ด์์ ์คํ๋ฉ๋๋ค. ๋์ผํ ํฌํธ(์: MySQL 3306)๋ฅผ ์ฌ์ฉํ๋๋ผ๋ ์ปจํ ์ด๋ ๋จ์๋ก ์๋ฒฝํ ๊ฒฉ๋ฆฌ๋์ด ์์ด, ๋ณ๋ ฌ ๋น๋ ์์๋ ์์ ์ ์ธ ํ ์คํธ๊ฐ ๊ฐ๋ฅํฉ๋๋ค.
3. ์ ์ง๋ณด์์ฑ : ํ ์คํธ ํ๊ฒฝ์ ๊ด๋ฆฌ
- ์ง์ ์ค์น๋ OS ์ ๋ฐ์ดํธ์ ๋ฐ๋ฅธ ์คํฌ๋ฆฝํธ ์์ ์ด ํ์ํ๊ณ , Self-hosted Runner๋ EC2์ ๋ณด์ ํจ์น, ๋์คํฌ ์ฉ๋, ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๊ด๋ฆฌ๋ฅผ ๊ฐ๋ฐ์๊ฐ ์ง์ ์ํํด์ผ ํฉ๋๋ค.
- ์๋น์ค ์ปจํ
์ด๋๋
YAMLํ์ผ ๋ด์ ์ฌ์ฉํ ์ด๋ฏธ์ง์ ๋ฒ์ ๋ง ๋ช ์ํ๋ฉด ๋ฉ๋๋ค. GitHub๊ฐ ๊ด๋ฆฌํ๋ ๊ด๋ฆฌํ ์๋น์ค๋ฅผ ํ์ฉํจ์ผ๋ก์จ ์ธํ๋ผ ์ด์ ๋ถ๋ด์ ์ต์ํํ๊ณ ๋น์ฆ๋์ค ๋ก์ง ๊ฒ์ฆ์ ์ง์คํ ์ ์์ต๋๋ค.
4-1-3. GitHub Secrets ํ๊ฒฝ ์ค์ ๊ตฌ์ฑ
๊ณตํต
- `DISCORD_WEBHOOK_URL` : ์ ๋ฌํ ์ ์ฒด ๋์ค์ฝ๋ ์ฑํ
๋ฐฉ
- `AWS_REGION` : ์ง์ญ
- `AWS_ACCESS_KEY` : AWS AccessKey
- `AWS_SECRET_KEY` : AWS SecretKey
- `BUCKET_NAME` : S3๋ฒํท ์ด๋ฆ
BE
- `DISCORD_WEBHOOK_URL_BE` : ์ ๋ฌํ ์ง๊ตฐ ๋์ค์ฝ๋ ์ฑํ
๋ฐฉ
- `application-test.yml` : ํ
์คํธ์ฉ YAML
FE
- `DISCORD_WEBHOOK_URL_FE` : ์ ๋ฌํ ์ง๊ตฐ ๋์ค์ฝ๋ ์ฑํ
๋ฐฉ
- `env` : ENV
AI
- `DISCORD_WEBHOOK_URL_AI` : ์ ๋ฌํ ์ง๊ตฐ ๋์ค์ฝ๋ ์ฑํ
๋ฐฉ
- `env` : ENV
4-1-4. ๋ธ๋์น ๋ณดํธ
-
develop,release,main์์ โRequire status checks to passโ ํ์ฑํ
4-2-1. BE ๊ด๋ จ ํ์ดํ๋ผ์ธ ์ค๊ณ
-
BE ํ์ดํ๋ผ์ธ PR ์ค๊ณ (ํผ์น๊ธฐ)
name: Devths-BE PR ํ์ดํ๋ผ์ธ on: pull_request: branches: - develop - release - main env: JAVA_VERSION: "21" jobs: lint: name: lint runs-on: ubuntu-22.04 steps: - name: ์ฒดํฌ์์ - uses: actions/checkout@v4 - name: ์๋ฐ ์ค์น uses: actions/setup-java@v4 with: distribution: temurin java-version: ${{ env.JAVA_VERSION }} - name: Gradle ์บ์ฑ uses: actions/cache@v3 with: path: | ~/.gradle/caches ~/.gradle/wrapper key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} restore-keys: | gradle-${{ runner.os }} - name: Lint ์ํ run: ๋ฆฐํธ ์ํ static_analysis: name: static-analysis runs-on: ubuntu-22.04 needs: [lint] steps: - name: ์ฒดํฌ์์ - uses: actions/checkout@v4 - name: ์๋ฐ ์ค์น uses: actions/setup-java@v4 with: distribution: temurin java-version: ${{ env.JAVA_VERSION }} - name: ์ ์ ๋ถ์ ์งํ run : ์ ์ ๋ถ์ ๋๊ตฌ ์ ์ ํ ์งํ test: name: test runs-on: ubuntu-22.04 needs: [static_analysis] services: mysql: image: mysql:8.0 env: # ํ ์คํธ์ฉ ์๋น์ค์ด๊ธฐ์ ๋ ธ์ถ๋์ด๋ ๋ฌธ์ ์์ MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: app MYSQL_USER: app MYSQL_PASSWORD: app ports: - 3306:3306 options: >- --health-cmd="mysqladmin ping -h 127.0.0.1 -u root -proot" --health-interval=10s --health-timeout=5s --health-retries=10 steps: - name: ๋ ํฌ์งํ ๋ฆฌ ์ฒดํฌ์์ - uses: actions/checkout@v4 - name: ์๋ฐ ์ค์ uses: actions/setup-java@v4 with: distribution: temurin java-version: ${{ env.JAVA_VERSION }} - name: Application-test ์ฃผ์ run: | mkdir -p src/test/resources echo "${{secrets.APPLICATION_TEST_YML}}" > ./src/test/resources/application-test.yml - name: ํ ์คํธ ์คํ run: ./gradlew test - name: ํ ์คํธ ๊ฒฐ๊ณผ ํ์ธ if: always() uses: actions/upload-artifact@v4 with: name: be-test-reports-${{ github.sha }} path: | build/reports/tests build/test-results if-no-files-found: ignore - name: Jacoco ์ปค๋ฒ๋ฆฌ์ง ํ์ธํ๊ธฐ if: always() uses: actions/upload-artifact@v4 with: name: be-coverage-${{ github.sha }} path: | build/reports/jacoco if-no-files-found: ignore build: name: build runs-on: ubuntu-22.04 needs: [test] # ํ ์คํธ ํต๊ณผ ํ ๋น๋ steps: - name: ์ฒดํฌ์์ - uses: actions/checkout@v4 - name: ์๋ฐ ์ค์น uses: actions/setup-java@v4 with: distribution: temurin java-version: ${{ env.JAVA_VERSION }} - name: Bootjar ๊ธฐ๋ฐ ๋น๋(ํ ์คํธ ํต๊ณผ) run: ./gradlew bootJar -x test --no-daemon notify-discord: name: notify runs-on: ubuntu-22.04 needs: build if: always() steps: - name: Discord ๋น๋ ์๋ฆผ uses: sarisia/actions-status-discord@v1 with: webhook: ${{ github.event.pull_request.base.ref == 'develop' && secrets.DISCORD_WEBHOOK_URL_BE || secrets.DISCORD_WEBHOOK_URL }} title: "${{ needs.build.result == 'success' && 'โ ' || 'โ' }} ์๋ก์ด PR์ด ์ฌ๋ผ์์ต๋๋ค!" description: | **์ ๋ชฉ**: ${{ github.event.pull_request.title }} **์์ฑ์**: ${{ github.event.pull_request.user.login }} **๋ธ๋์น**: `${{ github.event.pull_request.base.ref }}`โ`${{ github.event.pull_request.head.ref }}` **๐ ๋น๋ ๊ฒฐ๊ณผ**: - ์ํ: ${{ needs.build.result == 'success' && '์ฑ๊ณต' || '์คํจ' }} **๐ ๋งํฌ**: - [PR ํ์ธํ๊ธฐ](${{ github.event.pull_request.html_url }}) color: "${{ needs.build.result == 'success' && 65280 || 16711680 }}" username: GitHub PR Bot avatar_url: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png
-
BE ํ์ดํ๋ผ์ธ Merge ์ค๊ณ (ํผ์น๊ธฐ)
name: Devths-BE on: push: branches: - develop - release - main env: JAVA_VERSION: "21" AWS_REGION: ${{ secrets.AWS_REGION }} S3_BUCKET: ${{ secrets.S3_BUCKET }} S3_PREFIX: ci/${{ github.repository }}/${{ github.ref_name }}/${{ timestamp }}_${{ github.sha }} #์ปค๋ฐ ๋ฐ ์๊ฐ ๊ธฐ์ค ํด์ jobs: lint: name: lint runs-on: ubuntu-22.04 steps: - name: ์ฒดํฌ์์ - uses: actions/checkout@v4 - name: ์๋ฐ ์ค์น uses: actions/setup-java@v4 with: distribution: temurin java-version: ${{ env.JAVA_VERSION }} - name: Gradle ์บ์ฑ uses: actions/cache@v3 with: path: | ~/.gradle/caches ~/.gradle/wrapper key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} restore-keys: | gradle-${{ runner.os }} - name: Lint ์ํ run: ๋ฆฐํธ ์ํ static_analysis: name: static-analysis runs-on: ubuntu-22.04 needs: [lint] steps: - name: ์ฒดํฌ์์ - uses: actions/checkout@v4 - name: ์๋ฐ ์ค์น uses: actions/setup-java@v4 with: distribution: temurin java-version: ${{ env.JAVA_VERSION }} - name: ์ ์ ๋ถ์ ์งํ run : ์ ์ ๋ถ์ ๋๊ตฌ ์ ์ ํ ์งํ test: name: test runs-on: ubuntu-22.04 needs: [static_analysis] # ํตํฉํ ์คํธ์ฉ ์๋น์ค ์ปจํ ์ด๋ ์ค์ services: mysql: image: mysql:8.0 env: # ํ ์คํธ์ฉ ์๋น์ค์ด๊ธฐ์ ๋ ธ์ถ๋์ด๋ ๋ฌธ์ ์์ MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: app MYSQL_USER: app MYSQL_PASSWORD: app ports: - 3306:3306 options: >- --health-cmd="mysqladmin ping -h 127.0.0.1 -u root -proot" --health-interval=10s --health-timeout=5s --health-retries=10 steps: - name: ๋ ํฌ์งํ ๋ฆฌ ์ฒดํฌ์์ - uses: actions/checkout@v4 - name: ์๋ฐ ์ค์ uses: actions/setup-java@v4 with: distribution: temurin java-version: ${{ env.JAVA_VERSION }} - name: Application-test ์ฃผ์ run: | mkdir -p src/test/resources echo "${{secrets.APPLICATION_TEST_YML}}" > ./src/test/resources/application-test.yml - name: ํ ์คํธ ์คํ run: ./gradlew test - name: ํ ์คํธ ๊ฒฐ๊ณผ ํ์ธ if: always() uses: actions/upload-artifact@v4 with: name: be-test-reports-${{ github.sha }} path: | build/reports/tests build/test-results if-no-files-found: ignore - name: Jacoco ์ปค๋ฒ๋ฆฌ์ง ํ์ธํ๊ธฐ if: always() uses: actions/upload-artifact@v4 with: name: be-coverage-${{ github.sha }} path: | build/reports/jacoco if-no-files-found: ignore build: name: build runs-on: ubuntu-22.04 needs: [test] # ํ ์คํธ ํต๊ณผ ํ ๋น๋ steps: - name: ์ฒดํฌ์์ - uses: actions/checkout@v4 - name: ์๋ฐ ์ค์น uses: actions/setup-java@v4 with: distribution: temurin java-version: ${{ env.JAVA_VERSION }} - name: Bootjar ๊ธฐ๋ฐ ๋น๋(ํ ์คํธ ํต๊ณผ) run: ./gradlew bootJar -x test --no-daemon - name: AWS ์๊ฒฉ ์ฆ๋ช ์ค์ uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{secrets.AWS_ACCESS_KEY}} aws-secret-access-key: ${{secrets.AWS_SECRET_KEY}} aws-region: ${{secrets.AWS_REGION}} - name: JAR์ S3 ์ ๋ก๋ run: | set -euo pipefail DEST="s3://${S3_BUCKET}/${S3_PREFIX}/artifacts/jar" mkdir -p /tmp/jars # build/libs/*.jar ์ ๋ก๋ (์ฌ๋ฌ ๊ฐ์ผ ์ ์์ด for ์ฒ๋ฆฌ) shopt -s nullglob files=(build/libs/*.jar) if [ ${#files[@]} -eq 0 ]; then echo "No jar found in build/libs" exit 1 fi for f in "${files[@]}"; do aws s3 cp "$f" "${DEST}/$(basename "$f")" --only-show-errors done notify-discord: name: notify runs-on: ubuntu-22.04 needs: build if: always() steps: - name: Discord ๋น๋ ์๋ฆผ uses: sarisia/actions-status-discord@v1 with: webhook: ${{ secrets.DISCORD_WEBHOOK_BACK_URL }} title: "${{ needs.build.result == 'success' && 'โ ' || 'โ' }} ์๋ก์ด PR์ด ์ฌ๋ผ์์ต๋๋ค!" description: | **์ ๋ชฉ**: ${{ github.event.pull_request.title }} **์์ฑ์**: ${{ github.event.pull_request.user.login }} **๋ธ๋์น**: `${{ github.event.pull_request.base.ref }}`โ`${{ github.event.pull_request.head.ref }}` **๐ ๋น๋ ๊ฒฐ๊ณผ**: - ์ํ: ${{ needs.build.result == 'success' && '์ฑ๊ณต' || '์คํจ' }} **๐ ๋งํฌ**: - [PR ํ์ธํ๊ธฐ](${{ github.event.pull_request.html_url }}) color: "${{ needs.build.result == 'success' && 65280 || 16711680 }}" username: GitHub PR Bot avatar_url: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png
4-2-2. FE ๊ด๋ จ ํ์ดํ๋ผ์ธ ์ค๊ณ
-
FE ํ์ดํ๋ผ์ธ PR ์ค๊ณ (ํผ์น๊ธฐ)
name: Devths-FE PR ํ์ดํ๋ผ์ธ on: pull_request: branches: - develop - release - main env: NODE_VERSION: "20.x" jobs: # 1. Lint ๋จ๊ณ lint: name: lint runs-on: ubuntu-22.04 steps: - name: ์ฒดํฌ์์ uses: actions/checkout@v3 - name: ๋ ธ๋ JS ์ค์น uses: actions/setup-node@v3 with: node-version: ${{ env.NODE_VERSION }} cache: 'npm' - name: ์์กด์ฑ ์ค์น run: npm ci - name: Lint ๋ถ์ run: npm run lint # 2. ์ ์ ๋ถ์ ๋จ๊ณ (Lint ์ฑ๊ณต ์ ์คํ) static-analysis: name: static-analysis (CodeQL) needs: [lint] runs-on: ubuntu-22.04 permissions: actions: read contents: read security-events: write # ๊ฒฐ๊ณผ๋ฅผ Security ํญ์ ๊ธฐ๋กํ๊ธฐ ์ํด ํ์ steps: - name: ์ฒดํฌ์์ uses: actions/checkout@v3 - name: CodeQL ์ด๊ธฐํ uses: github/codeql-action/init@v2 with: languages: javascript # TS๋ฅผ ํฌํจํ JS ํ๊ฒฝ ๋ถ์ - name: ๋ ธ๋ JS ์ค์น uses: actions/setup-node@v3 with: node-version: ${{ env.NODE_VERSION }} cache: 'npm' - name: ์์กด์ฑ ์ค์น run: npm ci - name: CodeQL ๋ถ์ ์ํ uses: github/codeql-action/analyze@v2 # 3. ํ ์คํธ ๋จ๊ณ (์ ์ ๋ถ์ ์ฑ๊ณต ์ ์คํ) test: name: test needs: [static-analysis] runs-on: ubuntu-22.04 steps: - name: ์ฒดํฌ์์ uses: actions/checkout@v3 - name: ๋ ธ๋ JS ์ค์น uses: actions/setup-node@v3 with: node-version: ${{ env.NODE_VERSION }} cache: 'npm' - name: ์์กด์ฑ ์ค์น run: npm ci - name: ํ ์คํธ ์คํ (JUnit ๋ฆฌํฌํธ ์์ฑ) run: npm test -- --reporters=default --reporters=jest-junit env: JEST_JUNIT_OUTPUT_DIR: "./test-results" JEST_JUNIT_OUTPUT_NAME: "test-results.xml" - name: Test ๊ฒฐ๊ณผ ์ ๋ก๋ uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: files: 'test-results/test-results.xml' # 4. ๋น๋ ๋จ๊ณ (ํ ์คํธ ์ฑ๊ณต ์ ์คํ) build: name: build needs: [test] runs-on: ubuntu-22.04 steps: - name: ์ฒดํฌ์์ uses: actions/checkout@v3 - name: ๋ ธ๋ JS ์ค์น uses: actions/setup-node@v3 with: node-version: ${{ env.NODE_VERSION }} cache: 'npm' - name: ์์กด์ฑ ์ค์น run: npm ci - name: ํ๋ก์ ํธ ๋น๋ run: npm run build # 5. ๋์ค์ฝ๋ ์๋ฆผ ๋จ๊ณ (ํญ์ ์คํ๋์ง๋ง ๋น๋ ๊ฒฐ๊ณผ๋ฅผ ๋ฆฌํฌํธํจ) notify-discord: name: notify runs-on: ubuntu-22.04 needs: [build] # ๋น๋ ๋จ๊ณ์ ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ๋ค๋ฆผ if: always() steps: - name: Discord ๋น๋ ์๋ฆผ uses: sarisia/actions-status-discord@v1 with: webhook: ${{ github.event.pull_request.base.ref == 'develop' && secrets.DISCORD_WEBHOOK_URL_FE || secrets.DISCORD_WEBHOOK_URL }} title: "${{ needs.build.result == 'success' && 'โ ' || 'โ' }} ํ๋ฐํธ ์๋ก์ด PR์ด ์ฌ๋ผ์์ต๋๋ค!" description: | **์ ๋ชฉ**: ${{ github.event.pull_request.title }} **์์ฑ์**: ${{ github.event.pull_request.user.login }} **๋ธ๋์น**: `${{ github.event.pull_request.base.ref }}` โ `${{ github.event.pull_request.head.ref }}` **๐ ์ต์ข ๋น๋ ๊ฒฐ๊ณผ**: - ์ํ: ${{ needs.build.result == 'success' && '์ฑ๊ณต' || '์คํจ' }} **๐ ๋งํฌ**: - [PR ํ์ธํ๊ธฐ](${{ github.event.pull_request.html_url }}) color: "${{ needs.build.result == 'success' && 65280 || 16711680 }}" username: GitHub PR Bot avatar_url: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png
-
FE ํ์ดํ๋ผ์ธ Merge ์ค๊ณ (ํผ์น๊ธฐ)
name: DevthsFE ํ์ดํ๋ผ์ธ on: push: branches: - develop - release - main env: NODE_VERSION: "20.x" AWS_REGION: ${{ secrets.AWS_REGION }} S3_BUCKET: ${{ secrets.S3_BUCKET }} S3_PREFIX: ci/${{ github.repository }}/${{ github.ref_name }}/${{ timestamp }}_${{ github.sha }} #์ปค๋ฐ ๋ฐ ์๊ฐ ๊ธฐ์ค ํด์ jobs: # 1. Lint ๋จ๊ณ lint: name: lint runs-on: ubuntu-22.04 steps: - name: ์ฒดํฌ์์ uses: actions/checkout@v3 - name: ๋ ธ๋ JS ์ค์น uses: actions/setup-node@v3 with: node-version: ${{ env.NODE_VERSION }} cache: 'npm' - name: ์์กด์ฑ ์ค์น run: npm ci - name: Lint ๋ถ์ run: npm run lint # 2. ์ ์ ๋ถ์ ๋จ๊ณ (Lint ์ฑ๊ณต ์ ์คํ) static-analysis: name: static-analysis (CodeQL) needs: [lint] runs-on: ubuntu-22.04 permissions: actions: read contents: read security-events: write # ๊ฒฐ๊ณผ๋ฅผ Security ํญ์ ๊ธฐ๋กํ๊ธฐ ์ํด ํ์ steps: - name: ์ฒดํฌ์์ uses: actions/checkout@v3 - name: CodeQL ์ด๊ธฐํ uses: github/codeql-action/init@v2 with: languages: javascript # TS๋ฅผ ํฌํจํ JS ํ๊ฒฝ ๋ถ์ - name: ๋ ธ๋ JS ์ค์น uses: actions/setup-node@v3 with: node-version: ${{ env.NODE_VERSION }} cache: 'npm' - name: ์์กด์ฑ ์ค์น run: npm ci - name: CodeQL ๋ถ์ ์ํ uses: github/codeql-action/analyze@v2 # 3. ํ ์คํธ ๋จ๊ณ (์ ์ ๋ถ์ ์ฑ๊ณต ์ ์คํ) test: name: test needs: [static-analysis] runs-on: ubuntu-22.04 steps: - name: ์ฒดํฌ์์ uses: actions/checkout@v3 - name: ๋ ธ๋ JS ์ค์น uses: actions/setup-node@v3 with: node-version: ${{ env.NODE_VERSION }} cache: 'npm' - name: ์์กด์ฑ ์ค์น run: npm ci - name: ํ ์คํธ ์คํ (JUnit ๋ฆฌํฌํธ ์์ฑ) run: npm test -- --reporters=default --reporters=jest-junit env: JEST_JUNIT_OUTPUT_DIR: "./test-results" JEST_JUNIT_OUTPUT_NAME: "test-results.xml" - name: Test ๊ฒฐ๊ณผ ์ ๋ก๋ uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: files: 'test-results/test-results.xml' # 4. ๋น๋ ๋จ๊ณ (ํ ์คํธ ์ฑ๊ณต ์ ์คํ) build: name: build needs: [test] runs-on: ubuntu-22.04 steps: - name: ์ฒดํฌ์์ uses: actions/checkout@v3 - name: ๋ ธ๋ JS ์ค์น uses: actions/setup-node@v3 with: node-version: ${{ env.NODE_VERSION }} cache: 'npm' - name: ์์กด์ฑ ์ค์น run: npm ci - name: ํ๋ก์ ํธ ๋น๋ run: npm run build - name: AWS ์๊ฒฉ ์ฆ๋ช ์ค์ uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }} aws-secret-access-key: ${{ secrets.AWS_SECRET_KEY }} aws-region: ${{ env.AWS_REGION }} - name: S3 ๊ณ ์ ๊ฒฝ๋ก๋ก ์ ๋ก๋ run: | # ์ฌ์ฉ์๊ฐ ์ ์ํ prefix ๊ท์น ์ ์ฉ S3_PATH="s3://${{ env.S3_BUCKET }}/ci/${{ github.repository }}/${{ github.ref_name }}/${{ env.TIMESTAMP }}_${{ github.sha }}" aws s3 sync ./dist $S3_PATH echo "DEPLOY_URL=$S3_PATH" >> $GITHUB_ENV # 5. ๋์ค์ฝ๋ ์๋ฆผ ๋จ๊ณ (ํญ์ ์คํ๋์ง๋ง ๋น๋ ๊ฒฐ๊ณผ๋ฅผ ๋ฆฌํฌํธํจ) notify-discord: name: notify runs-on: ubuntu-22.04 needs: [build] # ๋น๋ ๋จ๊ณ์ ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ๋ค๋ฆผ if: always() steps: - name: Discord ๋น๋ ์๋ฆผ uses: sarisia/actions-status-discord@v1 with: webhook: ${{ github.event.pull_request.base.ref == 'develop' && secrets.DISCORD_WEBHOOK_URL_FE || secrets.DISCORD_WEBHOOK_URL }} title: "${{ needs.build.result == 'success' && 'โ ' || 'โ' }} ํ๋ฐํธ ์๋ก์ด PR์ด ์ฌ๋ผ์์ต๋๋ค!" description: | **์ ๋ชฉ**: ${{ github.event.pull_request.title }} **์์ฑ์**: ${{ github.event.pull_request.user.login }} **๋ธ๋์น**: `${{ github.event.pull_request.base.ref }}` โ `${{ github.event.pull_request.head.ref }}` **๐ ์ต์ข ๋น๋ ๊ฒฐ๊ณผ**: - ์ํ: ${{ needs.build.result == 'success' && '์ฑ๊ณต' || '์คํจ' }} **๐ ๋งํฌ**: - [PR ํ์ธํ๊ธฐ](${{ github.event.pull_request.html_url }}) color: "${{ needs.build.result == 'success' && 65280 || 16711680 }}" username: GitHub PR Bot avatar_url: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png
4-2-3. AI ๊ด๋ จ ํ์ดํ๋ผ์ธ ์ค๊ณ
-
AI ํ์ดํ๋ผ์ธ PR ์ค๊ณ (ํผ์น๊ธฐ)
name: Devths-AI PR CI ํ์ดํ๋ผ์ธ on: pull_request: branches: - develop - release - main env: PYTHON_VERSION: "3.10.19" jobs: # 1. Lint ๋จ๊ณ (Ruff) lint: name: lint (Ruff) runs-on: ubuntu-22.04 steps: - name : ์ฒดํฌ์์ uses: actions/checkout@v4 - name: ํ์ด์ฌ ์ค์ uses: actions/setup-python@v4 with: python-version: ${{ env.PYTHON_VERSION }} cache: 'pip' - name: Ruff ์ค์น ๋ฐ ์คํ run: | pip install ruff ruff check . # 2. ์ ์ ๋ถ์ ๋จ๊ณ (CodeQL) static-analysis: name: static-analysis (CodeQL) needs: [lint] runs-on: ubuntu-22.04 permissions: security-events: write steps: - name : ์ฒดํฌ์์ uses: actions/checkout@v4 - name: CodeQL ์ด๊ธฐํ uses: github/codeql-action/init@v2 with: languages: python - name: CodeQL ๋ถ์ ์ํ uses: github/codeql-action/analyze@v2 # 3. ํ ์คํธ ๋จ๊ณ (Pytest) test: name: test (Pytest) needs: [static-analysis] runs-on: ubuntu-22.04 permissions: checks: write pull-requests: write steps: - name : ์ฒดํฌ์์ uses: actions/checkout@v4 - name: ํ์ด์ฌ ์ค์ uses: actions/setup-python@v4 with: python-version: ${{ env.PYTHON_VERSION }} cache: 'pip' - name: ์์กด์ฑ ์ค์น run: | python -m pip install --upgrade pip pip install -r requirements.txt pip install pytest pytest-xml-report - name: ํ ์คํธ ์คํ run: pytest --junitxml=test-results.xml - name: Test ๊ฒฐ๊ณผ ์ ๋ก๋ uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: files: 'test-results.xml' # 4. ๋น๋ ๋จ๊ณ build: name: build needs: [test] runs-on: ubuntu-22.04 steps: - name : ์ฒดํฌ์์ uses: actions/checkout@v4 - name: ํ์ด์ฌ ์ค์ uses: actions/setup-python@v4 with: python-version: ${{ env.PYTHON_VERSION }} cache: 'pip' - name: ์์กด์ฑ ์ค์น ๋ฐ ๋น๋ ๊ฒ์ฆ run: | python -m pip install --upgrade pip pip install -r requirements.txt # 5. ๋์ค์ฝ๋ ์๋ฆผ notify-discord: name: notify runs-on: ubuntu-22.04 needs: [build] if: always() steps: - name: Discord ๋น๋ ์๋ฆผ uses: sarisia/actions-status-discord@v1 with: webhook: ${{ github.event.pull_request.base.ref == 'develop' && secrets.DISCORD_WEBHOOK_URL_AI || secrets.DISCORD_WEBHOOK_URL }} title: "${{ needs.build.result == 'success' && 'โ ' || 'โ' }} AI PR ํ์ดํ๋ผ์ธ ๊ฒฐ๊ณผ" description: | **์ ๋ชฉ**: ${{ github.event.pull_request.title }} **์์ฑ์**: ${{ github.event.pull_request.user.login }} **์ํ**: ${{ needs.build.result == 'success' && '์ฑ๊ณต' || '์คํจ' }} **๐ ๋ถ์ ์์ฝ**: - Lint: Ruff ๊ฒ์ฌ ์๋ฃ - Security: CodeQL ์ ์ ๋ถ์ ์๋ฃ - Test: Pytest ๋จ์ ํ ์คํธ ์๋ฃ - Build: Pip ์์กด์ฑ ๊ฒ์ฆ ์๋ฃ **๐ ๋งํฌ**: - [PR ํ์ธํ๊ธฐ](${{ github.event.pull_request.html_url }}) color: "${{ needs.build.result == 'success' && 65280 || 16711680 }}" username: GitHub AI Bot avatar_url: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png
-
AI ํ์ดํ๋ผ์ธ Merge ์ค๊ณ (ํผ์น๊ธฐ)
name: Devths-AI ๋ฐฐํฌ ํ์ดํ๋ผ์ธ on: push: branches: - develop - release - main env: PYTHON_VERSION: "3.10.19" AWS_REGION: ${{ secrets.AWS_REGION }} S3_BUCKET: ${{ secrets.S3_BUCKET_AI }} jobs: # 1. Lint ๋จ๊ณ lint: name: lint (Ruff) runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - name: ํ์ด์ฌ ์ค์ uses: actions/setup-python@v4 with: python-version: ${{ env.PYTHON_VERSION }} cache: 'pip' - run: pip install ruff - run: ruff check . # 2. ์ ์ ๋ถ์ (CodeQL) static-analysis: name: static-analysis (CodeQL) needs: [lint] runs-on: ubuntu-22.04 permissions: security-events: write steps: - uses: actions/checkout@v4 - name: CodeQL ์ด๊ธฐํ uses: github/codeql-action/init@v2 with: languages: python - name: CodeQL ๋ถ์ ์ํ uses: github/codeql-action/analyze@v2 # 3. ํ ์คํธ ๋จ๊ณ test: name: test (Pytest) needs: [static-analysis] runs-on: ubuntu-22.04 permissions: checks: write pull-requests: write steps: - uses: actions/checkout@v4 - name: ํ์ด์ฌ ์ค์ uses: actions/setup-python@v4 with: python-version: ${{ env.PYTHON_VERSION }} cache: 'pip' - run: | pip install -r requirements.txt pip install pytest pytest-xml-report - name: ํ ์คํธ ์คํ run: pytest --junitxml=test-results.xml - name: Test ๊ฒฐ๊ณผ ์ ๋ก๋ uses: EnricoMi/publish-unit-test-result-action@v2 if: always() with: files: 'test-results.xml' # 4. ๋น๋ ๋ฐ S3 ์ ๋ก๋ build_and_deploy: name: build and S3 upload needs: [test] runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - name: ํ์ด์ฌ ์ค์ uses: actions/setup-python@v4 with: python-version: ${{ env.PYTHON_VERSION }} cache: 'pip' # ํ์์คํฌํ ์์ฑ - name: ํ์์คํฌํ ์์ฑ run: echo "TIMESTAMP=$(date +'%Y%m%d%H%M%S')" >> $GITHUB_ENV - name: AWS ์๊ฒฉ ์ฆ๋ช ์ค์ uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }} aws-secret-access-key: ${{ secrets.AWS_SECRET_KEY }} aws-region: ${{ env.AWS_REGION }} - name: S3๋ก ์์ค ํจํค์ง ์ ๋ก๋ run: | # 1. ์ ๋ก๋ํ ๊ฒฝ๋ก ์ค์ (prefix) S3_PATH="s3://${{ env.S3_BUCKET }}/ci/${{ github.repository }}/${{ github.ref_name }}/${{ env.TIMESTAMP }}_${{ github.sha }}" # 2. ๋ถํ์ํ ํ์ผ(.git, __pycache__ ๋ฑ) ์ ์ธํ๊ณ ๋๊ธฐํ aws s3 sync . $S3_PATH \ --exclude ".git/*" \ --exclude ".github/*" \ --exclude "__pycache__/*" \ --exclude "*.pyc" \ --exclude ".pytest_cache/*" echo "DEPLOY_PATH=$S3_PATH" >> $GITHUB_ENV # 5. ๋์ค์ฝ๋ ์๋ฆผ notify-discord: name: notify runs-on: ubuntu-22.04 needs: [build_and_deploy] if: always() steps: - name: Discord ๋ฐฐํฌ ์๋ฆผ uses: sarisia/actions-status-discord@v1 with: webhook: ${{ github.ref_name == 'develop' && secrets.DISCORD_WEBHOOK_URL_AI || secrets.DISCORD_WEBHOOK_URL }} title: "${{ needs.build_and_deploy.result == 'success' && 'โ ' || 'โ' }} AI ๋ฐฐํฌ ํ์ดํ๋ผ์ธ ๊ฒฐ๊ณผ" description: | **์ด๋ฒคํธ**: PUSH (`${{ github.ref_name }}`) **์์ฑ์**: ${{ github.actor }} **์ํ**: ${{ needs.build_and_deploy.result == 'success' && '์ฑ๊ณต' || '์คํจ' }} **๐ ๋ฐฐํฌ ์ ๋ณด**: - S3 ์ ์ฅ ๊ฒฝ๋ก: `ci/${{ github.repository }}/${{ github.ref_name }}/...` **๐ ๋งํฌ**: - [GitHub Actions ์คํ ํ์ธ](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) color: "${{ needs.build_and_deploy.result == 'success' && 65280 || 16711680 }}" username: GitHub AI Bot
- ์ฐ๋ฆฌ ์๋น์ค์ ์๋ฆผ ์์คํ
์ ํ ๊ธฐ์ค์
์ ๊ทผ์ฑ, ํ์ฅ์ฑ, ๋น์ฉ์ ๋๋ค. - ์๋ฆผ ์์ฒด๊ฐ ๊ฐ๋ฐ์๋ค์๊ฒ ๋ช ํํ๊ณ ์ฝ๊ฒ ์ ๋ฌ๋ ์ ์์ด์ผํฉ๋๋ค.
- CI์ ์๋ฆผ ๋ฟ ์๋๋ผ, ๋ชจ๋ํฐ๋ง์์๊น์ง ์๋ฆผ์ด ์ฌ์ฉ๋์ด์ผ ํ๊ธฐ์ ํ์ฅ์ฑ์ด ์ข์ ๊ฒ์ ์ ํํ๊ธฐ๋ก ํ๋จํ์ต๋๋ค.
์๋ฆผ ์๋น์ค ๋น๊ต
์์ฆIT(์๊ฐ PV 100๋ง, MAU 46๋ง, ๋ด์ค๋ ํฐ 8๋ง5์ฒ.) ๋งค๊ฑฐ์ง ๋ด ๋ฐ์ท (2025.09.18 ๊ธฐ์ค) ์์ธ์ ์ผ๋ก ํ์ฌ ์ฌ์ฉ์ค์ธ ๋์ค์ฝ๋๋ฅผ ์ถ๊ฐํ์ฌ, ํ์ ์ฉ ๋ฉ์ ์ ํด ๋น๊ต
| ์ข ๋ฅ | Discord (์ ํ) | Slack | Teams |
|---|---|---|---|
| ์ ๊ทผ์ฑ | - ๊ธฐ์กด์ ํ์ด ์ฌ์ฉ ์ค์ธ ํ๋ซํผ์ด๊ธฐ์ ์ฆ์ ๋์ ๊ฐ๋ฅ | - ํ ์ ์์ด ์ ๊ท ํด ์ ์ ํ์ | - ํ ์ ์์ด ์ ๊ท ํด ์ ์ ํ์ |
| GitHub ํ์ฅ์ฑ | - Webhook์ ํตํ ์ง์ ์ปค์คํฐ๋ง์ด์ง ๊ฐ๋ฅ | - Webhook์ ํตํ ์ง์ ์ปค์คํฐ๋ง์ด์ง ๊ฐ๋ฅ | - Webhook์ ํตํ ์ง์ ์ปค์คํฐ๋ง์ด์ง ๊ฐ๋ฅ |
| AWS ํ์ฅ์ฑ | - ์ง์ ๊ตฌ์ฑ ํ์ (SNS/EventBridge โ Lambda โ Discord Webhook) | - Slack ๊ธฐ๋ฐ AWS Support ์ฑ ์กด์ฌ โ ์์ฝ๊ฒ ์ค์ ๊ฐ๋ฅ |
- Webhook์ ํตํ ์ง์ ์ปค์คํฐ๋ง์ด์ง ๊ฐ๋ฅ |
| PLG ์คํ ํ์ฅ์ฑ | - Webhook์ ํตํ ์ง์ ์ปค์คํฐ๋ง์ด์ง ๊ฐ๋ฅ | - Slack API ๋๋ Webhook์ ํตํ ์ฐ๊ฒฐ ๊ฐ๋ฅ | - Webhook์ ํตํ ์ง์ ์ปค์คํฐ๋ง์ด์ง ๊ฐ๋ฅ |
| ELK ์คํ ํ์ฅ์ฑ | - Webhook์ ํตํ ์ง์ ์ปค์คํฐ๋ง์ด์ง ๊ฐ๋ฅ | - Webhook์ ํตํ ์ง์ ์ปค์คํฐ๋ง์ด์ง ๊ฐ๋ฅ | - Webhook์ ํตํ ์ง์ ์ปค์คํฐ๋ง์ด์ง ๊ฐ๋ฅ |
| ๋น์ฉ | - ๋ฌด๋ฃ๋ก ์์ ๊ฐ๋ฅ - AWS Lambda๋ฅผ ์ฐ๊ฒ ๋๋ค๋ฉด, 100๋ง ๊ฑด๋น $0.20 ๋ฐ์ |
- ๊ธฐ๋ณธ์ ๋ฌด๋ฃ๋ก ์์ ๊ฐ๋ฅ - ๋ฌด๋ฃ ๋ฒ์ ์ 90์ผ ์ดํ ํ์คํ ๋ฆฌ ์ ํ - PRO ๋ฒ์ ์ ๋ฃ, ์ธ๋น $4.4 ๋ฐ์ |
- (์กฐ์ง ๋ผ์ด์ ์ค ์ ์ฑ ์ ๋ฐ๋ผ ์์ด) |
โ Slack์ ์์ฒด API๊ฐ ์กด์ฌํ์ฌ ์๋ฆผ ์ฐ๋์ด ๋ ์ฝ๋ค๋ ์ฅ์ ์ด ์กด์ฌํ์ง๋ง, ๋ค๋ฅธ ์๋น์ค ๋ํ webhook์ ํตํด์ ์๋ฆผ ์ ๋ฌ์ด ๊ฐ๋ฅํฉ๋๋ค. ๊ทธ๋ ๊ธฐ์, ์ฐ๋ฆฌ ์๋น์ค์์๋ ์ ์์ฑ/๋น์ฉ์ ๋ ๊ฐ์ฐ์ ์ ๋์ด์, ๊ธฐ์กด์ ์ฐ๋ Discord๋ฅผ ์๋ฆผ ๋ฉ์ ์ ๋ก ์ ์ ํ์์ต๋๋ค.
| ํญ๋ชฉ | ์ค๋ช |
|---|---|
| ํด | Discord Webhook |
| ์๋ฆผ ์์ | ์ํฌํ๋ก์ฐ ์ข ๋ฃ ์ (always()) |
| ํฌํจ ๋ด์ฉ | ๋น๋ ์ํ, ์คํํ ๋ธ๋์น, ์ปค๋ฐ ๋ฉ์์ง, ์ํฌํ๋ก์ฐ ๋งํฌ |
| ์คํจ ์ ์กฐ์น | - |
5-2-1. ์๋ฆผ ๋ฉ์์ง ํฌ๋งท ๋ฐ ์์
Success: โ
[๋ธ๋์น๋ช
] ๋ธ๋์น ๋ฐฐํฌ ๊ฒฐ๊ณผ
์ปค๋ฐ ๋ฉ์์ง: [์ปค๋ฐ ์ ๋ชฉ]
์์ฑ์: [์์ฑ์]
๋ธ๋์น: [๋ธ๋์น๋ช
]
๐ ๋น๋/๋ฐฐํฌ ๊ฒฐ๊ณผ:
์ํ: [์ฑ๊ณต/์คํจ]
Event - [push/pr]
Triggered by [๊ฐ๋ฐ์๋ช
]
2025. 12. 18. ์คํ 7:475-2-2. CI ํ์ดํ๋ผ์ธ ์๋ฆผ ๊ตฌ์ฑ
- ๋น๋/ํ ์คํธ ๊ฒฐ๊ณผ์ ๋ฐ๋ผ Discord ์๋ฆผ์ ์๋ ์์ฑ
- ๊ฐ๋ฐ์๊ฐ ํ ์คํธ๊ฐ ์คํจํ๋์ง์ ๊ด๋ จ ์ปค๋ฐ์ผ๋ก ๋ฐ๋ก ์ด๋ํ์ฌ ํ์ธํ ์ ์๋๋ก ๊ตฌ์ฑ
- ๋๋ฌด ๋ง์ ์๋์ด ์กด์ฌํ๋ค๋ฉด, ์๋ ์ ๋ฌ์ ๋ํ ๋ฐฉํด
-
๋ธ๋์น ๋ฐ ์ํฉ์ ๋ฐ๋ผ์ ์ ๋ฌ๋๋ ์ฑํ ๋ฐฉ ์ง์
์ผ์ด์ค ์์ค ๋ธ๋์น ํ๊น ๋ธ๋์น ์ํฉ ์๋ฆผ ์ฑํ ๋ฐฉ 1 Feat/*DevPR ๊ฒฐ๊ณผ ํด๋น ์ง๊ตฐ ์ฑํ ๋ฐฉ(FE/BE/AI) 2 Feat/*DevMerge ๊ฒฐ๊ณผ ์ ์ฒด ์ฑํ ๋ฐฉ 3 DevReleasePR/Merge ์ ์ฒด ์ฑํ ๋ฐฉ 4 ReleaseMainPR/Merge ๊ฒฐ๊ณผ ์ ์ฒด ์ฑํ ๋ฐฉ 5 HotfixMainPR/Merge ๊ฒฐ๊ณผ ์ ์ฒด ์ฑํ ๋ฐฉ
-
# ๋น๋/ํ
์คํธ ์ํ
result = runBuildPipeline() # lint โ test โ build
# ๊ฒฐ๊ณผ์ ๋ฐ๋ผ ๋ฉ์์ง ๊ตฌ์ฑ
if result == SUCCESS:
status = "โ
์ฑ๊ณต"
color = GREEN
else:
status = "โ ์คํจ"
color = RED
# Discord ์๋ฆผ ์ ์ก
sendDiscordWebhook(
title = f"{status} - ๋ธ๋์น ๋ฐฐํฌ ๊ฒฐ๊ณผ",
description = f"์ปค๋ฐ: <์ปค๋ฐ ๋ฉ์์ง>\n์์ฑ์: <์์ฑ์>\n๋ธ๋์น: <๋ธ๋์น ์ด๋ฆ>\n์ํ: {status}",
color = color
)
| ํญ๋ชฉ | CI ๋์ ์ | CI ๋์ ํ | ๋น๊ณ |
|---|---|---|---|
| ํ ์คํธ ์๊ฐ | ์๋, ํ๊ท 10๋ถ | ์๋ํ, ํ๊ท #๋ถ | ์๊ฐ ์ ๊ฐ ๊ฐ๋ฅ |
| ํ ์คํธ ์ปค๋ฒ๋ฆฌ์ง | ๋น์ ๊ธฐ์ ์๋ ์ธก์ | JaCoCo๋ก ์๋ ์ธก์ | ์ปค๋ฒ๋ฆฌ์ง ๋ฆฌํฌํธ๋ฅผ ์๊ฐํํ์ฌ ๊ณต์ ๊ฐ๋ฅ |
| ํผ๋๋ฐฑ ์์ | QA ๋จ๊ณ | ์ปค๋ฐ ์งํ | ๊ฐ๋ฐ ๋จ๊ณ์์ ๋ฒ๊ทธ ์กฐ๊ธฐ ๋ฐ๊ฒฌ ๊ฐ๋ฅ |
| ์ฝ๋ ํ์ง ์ ๊ฒ | ์์ | Lint ๋๊ตฌ๋ก ์๋ ๊ฒ์ฌ | ์ผ๊ด๋ ์ฝ๋ ์คํ์ผ ์ ์ง ๊ฐ๋ฅ |
| ์ฝ๋ ๊ฒ์ฌ | ์์ฒด ์ฝ๋ ๋ฆฌ๋ทฐ๋ฅผ ํตํ ์์ฒด์ ์ธ ๋ถ์ | ์ ์ ๋ถ์ ๋๊ตฌ๋ก ์๋ ๊ฒ์ฌ | ์ค์๋ก ์ธํด ํ์ธํ์ง ๋ชปํ ๋ด์ฉ์ ๋ฐ๊ฒฌ ๊ฐ๋ฅ |
| ์๋ฆผ ๋ฐ ๋์ ์๋ | ๋น๋ ์คํจ ์ ์๋ ํ์ธ | Discord๋ก ์ฆ์ ์๋ฆผ | ์คํจ ์ฆ์ ํ์์๊ฒ ์๋ ํต๋ณด ๊ฐ๋ฅ |