CI/CD ํ์ดํ๋ผ์ธ์ JavaScript ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง๋ฅผ ํตํฉํ์ฌ ์ํํธ์จ์ด ํ์ง์ ๋์ด๊ณ ๋ฒ๊ทธ๋ฅผ ์ค์ด๋ฉฐ ์์ ์ ์ธ ์ฑ ์ฑ๋ฅ์ ๋ณด์ฅํ๋ ๋ฐฉ๋ฒ์ ์์๋ณด์ธ์. ๊ธ๋ก๋ฒ ๋ชจ๋ฒ ์ฌ๋ก์ ์์ ๊ฐ ํฌํจ๋ฉ๋๋ค.
JavaScript ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง ํตํฉ: ๊ฒฌ๊ณ ํ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ํ ํ ์คํธ ํ์ดํ๋ผ์ธ ๊ฐํ
์ค๋๋ ๋น ๋ฅด๊ฒ ๋ณํํ๋ ์ํํธ์จ์ด ๊ฐ๋ฐ ํ๊ฒฝ์์ JavaScript ์ ํ๋ฆฌ์ผ์ด์ ์ ํ์ง๊ณผ ์์ ์ฑ์ ๋ณด์ฅํ๋ ๊ฒ์ ๋งค์ฐ ์ค์ํฉ๋๋ค. ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง๋ ํ ์คํธ ์ค์ ์คํ๋ ์ฝ๋๋ฒ ์ด์ค์ ๋น์จ์ ์ธก์ ํ๋ ์งํ๋ก, ํ ์คํธ๋์ง ์์ ์์ญ๊ณผ ์ ์ฌ์ ์ทจ์ฝ์ ์ ์๋ณํ๋ ๋ฐ ์ค์ํ ์ญํ ์ ํฉ๋๋ค. ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง๋ฅผ ์ง์์ ํตํฉ ๋ฐ ์ง์์ ์ ๋ฌ(CI/CD) ํ์ดํ๋ผ์ธ์ ํตํฉํ๋ฉด ํ๊ท๋ฅผ ๋ฐฉ์งํ๊ณ ๋ฒ๊ทธ๋ฅผ ์ค์ด๋ฉฐ ์ ์ธ๊ณ ์ฌ์ฉ์์๊ฒ ๊ณ ํ์ง ์ํํธ์จ์ด๋ฅผ ์ ๊ณตํ๋ ๊ฐ๋ ฅํ ๋ฉ์ปค๋์ฆ์ ์ ๊ณตํฉ๋๋ค.
์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง๋ ๋ฌด์์ด๋ฉฐ ์ ์ค์ํ๊ฐ?
์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง๋ ์์ค ์ฝ๋์ ์ด๋ ๋ถ๋ถ์ด ํ ์คํธ ์ค์ํธ์ ์ํด ์คํ๋์๋์ง ํ์ธํ๋ ๋ฐ ์ฌ์ฉ๋๋ ๊ธฐ์ ์ ๋๋ค. ์ด๋ ํ ์คํธ์ ํจ์จ์ฑ์ ๋ํ ํต์ฐฐ๋ ฅ์ ์ ๊ณตํ๊ณ ์ถ๊ฐ ํ ์คํธ๊ฐ ํ์ํ ์์ญ์ ์๋ณํ๋ ๋ฐ ๋์์ด ๋ฉ๋๋ค. ๊ฐ๊ฐ ๊ณ ์ ํ ๊ด์ ์ ์ ๊ณตํ๋ ์ฌ๋ฌ ๊ฐ์ง ์ปค๋ฒ๋ฆฌ์ง ์งํ๊ฐ ์์ต๋๋ค:
- ๊ตฌ๋ฌธ ์ปค๋ฒ๋ฆฌ์ง(Statement Coverage): ์ฝ๋์์ ์คํ๋ ๊ตฌ๋ฌธ์ ๋น์จ์ ์ธก์ ํฉ๋๋ค. ๊ตฌ๋ฌธ์ ์์ ์ ์ํํ๋ ๋จ์ผ ์ฝ๋ ๋ผ์ธ์ ๋๋ค.
- ๋ถ๊ธฐ ์ปค๋ฒ๋ฆฌ์ง(Branch Coverage): ์คํ๋ ๋ถ๊ธฐ(์: `if` ๋ฌธ, ๋ฃจํ)์ ๋น์จ์ ์ธก์ ํฉ๋๋ค. ์ด๋ ์กฐ๊ฑด๋ฌธ์ `true` ๋ฐ `false` ๋ถ๊ธฐ๊ฐ ๋ชจ๋ ํ ์คํธ๋์๋์ง ํ์ธํฉ๋๋ค.
- ํจ์ ์ปค๋ฒ๋ฆฌ์ง(Function Coverage): ์ฝ๋์์ ํธ์ถ๋ ํจ์์ ๋น์จ์ ์ธก์ ํฉ๋๋ค. ์ด๋ ํ ์คํธ ์ค์ ๋ชจ๋ ํจ์๊ฐ ํธ์ถ๋๋์ง ํ์ธํฉ๋๋ค.
- ๋ผ์ธ ์ปค๋ฒ๋ฆฌ์ง(Line Coverage): ์คํ๋ ์ฝ๋ ๋ผ์ธ์ ๋น์จ์ ์ธก์ ํฉ๋๋ค. ๊ตฌ๋ฌธ ์ปค๋ฒ๋ฆฌ์ง์ ์ ์ฌํ์ง๋ง ์ค ๋ฐ๊ฟ๊ณผ ๋จ์ผ ๋ผ์ธ์ ์ฌ๋ฌ ๊ตฌ๋ฌธ์ ๊ณ ๋ คํฉ๋๋ค.
์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง๋ ์ ์ค์ํ ๊น์? ๋ค์๊ณผ ๊ฐ์ ๋ช ๊ฐ์ง ์ค์ํ ์ด์ ์ ์ ๊ณตํฉ๋๋ค:
- ์ฝ๋ ํ์ง ํฅ์: ํ ์คํธ๋์ง ์์ ์์ญ์ ์๋ณํจ์ผ๋ก์จ ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง๋ ๋ ํฌ๊ด์ ์ธ ํ ์คํธ๋ฅผ ์์ฑํ๋ ๋ฐ ๋์์ ์ฃผ์ด ์ฝ๋ ํ์ง์ ๋์ ๋๋ค.
- ๋ฒ๊ทธ ๊ฐ์: ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง ๋ณด๊ณ ์๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ ์ฒ ์ ํ ํ ์คํธ๋ ํ๋ก๋์ ์ ๋๋ฌํ๊ธฐ ์ ์ ์ ์ฌ์ ์ธ ๋ฒ๊ทธ์ ์ทจ์ฝ์ ์ ๋ฐ๊ฒฌํ๋ ๋ฐ ๋์์ด ๋ฉ๋๋ค.
- ์์ ๊ฐ ์ฆ๊ฐ: ์ฝ๋๊ฐ ์ ํ ์คํธ๋์๋ค๋ ์ฌ์ค์ ์๋ฉด ์๋ก์ด ๊ธฐ๋ฅ๊ณผ ์ ๋ฐ์ดํธ๋ฅผ ๋ฆด๋ฆฌ์คํ๋ ๋ฐ ๋ ํฐ ์์ ๊ฐ์ ๊ฐ์ง ์ ์์ต๋๋ค.
- ๋ ๋น ๋ฅธ ๋๋ฒ๊น : ๋ฒ๊ทธ๊ฐ ๋ฐ์ํ์ ๋ ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง ๋ณด๊ณ ์๋ ๋ฌธ์ ์ ์์ธ์ ๋ ๋นจ๋ฆฌ ์ฐพ์๋ด๋ ๋ฐ ๋์์ด ๋ ์ ์์ต๋๋ค.
- ํ๊ท ๋ฐฉ์ง: CI/CD ํ์ดํ๋ผ์ธ์ ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง๋ฅผ ํตํฉํ๋ฉด ์ฝ๋ ๋ณ๊ฒฝ ํ์๋ ๊ธฐ์กด ํ ์คํธ๊ฐ ์ฌ์ ํ ํต๊ณผํ๋์ง ํ์ธํ์ฌ ํ๊ท๋ฅผ ๋ฐฉ์งํฉ๋๋ค.
- ์ฝ๋ ์ดํด๋ ํฅ์: ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง ๋ณด๊ณ ์๋ฅผ ๋ถ์ํ๋ฉด ์ฝ๋์ ๊ตฌ์กฐ์ ๋์์ ๋ ์ ์ดํดํ๋ ๋ฐ ๋์์ด ๋ ์ ์์ต๋๋ค.
CI/CD ํ์ดํ๋ผ์ธ์ ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง ํตํฉํ๊ธฐ
์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง์ ์ง์ ํ ํ์ CI/CD ํ์ดํ๋ผ์ธ์ ํตํฉ๋ ๋ ๋ฐํ๋ฉ๋๋ค. ์ด๋ฅผ ํตํด ์ปค๋ฒ๋ฆฌ์ง ์งํ๋ฅผ ์๋์ผ๋ก ์ถ์ ํ๊ณ , ํ๊ท๋ฅผ ์๋ณํ๋ฉฐ, ํ์ง ๊ฒ์ดํธ๋ฅผ ๊ฐ์ ํ ์ ์์ต๋๋ค. ์ผ๋ฐ์ ์ธ ์ํฌํ๋ก๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
- ์ฝ๋ ๋ณ๊ฒฝ: ๊ฐ๋ฐ์๊ฐ ์ฝ๋๋ฒ ์ด์ค๋ฅผ ๋ณ๊ฒฝํ๊ณ ๋ฒ์ ๊ด๋ฆฌ ์์คํ (์: Git)์ ์ปค๋ฐํฉ๋๋ค.
- CI/CD ํธ๋ฆฌ๊ฑฐ: ์ฝ๋ ์ปค๋ฐ์ด CI/CD ํ์ดํ๋ผ์ธ์ ํธ๋ฆฌ๊ฑฐํฉ๋๋ค.
- ์๋ํ๋ ํ ์คํธ: ํ์ดํ๋ผ์ธ์ด ์๋ํ๋ ํ ์คํธ ์ค์ํธ๋ฅผ ์คํํฉ๋๋ค.
- ์ปค๋ฒ๋ฆฌ์ง ๋ณด๊ณ ์ ์์ฑ: ํ ์คํธ ์คํ ์ค์ ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง ๋๊ตฌ๊ฐ ์ผ๋ฐ์ ์ผ๋ก LCOV๋ Cobertura์ ๊ฐ์ ํ์ค ํ์์ผ๋ก ๋ณด๊ณ ์๋ฅผ ์์ฑํฉ๋๋ค.
- ์ปค๋ฒ๋ฆฌ์ง ๋ถ์: ํ์ดํ๋ผ์ธ์ด ์ปค๋ฒ๋ฆฌ์ง ๋ณด๊ณ ์๋ฅผ ๋ถ์ํ๊ณ ๋ฏธ๋ฆฌ ์ ์๋ ์๊ณ๊ฐ ๋๋ ์ด์ ๋น๋์ ๋น๊ตํฉ๋๋ค.
- ํ์ง ๊ฒ์ดํธ: ํ์ดํ๋ผ์ธ์ด ์ปค๋ฒ๋ฆฌ์ง ์งํ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ์ง ๊ฒ์ดํธ๋ฅผ ๊ฐ์ ํฉ๋๋ค. ์๋ฅผ ๋ค์ด, ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง๊ฐ ํน์ ๋น์จ ์๋๋ก ๋จ์ด์ง๋ฉด ๋น๋๊ฐ ์คํจํ ์ ์์ต๋๋ค.
- ๋ณด๊ณ ๋ฐ ์๊ฐํ: ์ปค๋ฒ๋ฆฌ์ง ๊ฒฐ๊ณผ๊ฐ ๋ณด๊ณ ๋๊ณ ์๊ฐํ๋์ด ๊ฐ๋ฐ์๊ฐ ์ฐ๋ ค๋๋ ์์ญ์ ์ฝ๊ฒ ์๋ณํ ์ ์์ต๋๋ค.
- ๋ฐฐํฌ: ์ฝ๋๊ฐ ๋ชจ๋ ํ์ง ๊ฒ์ดํธ๋ฅผ ํต๊ณผํ๋ฉด ๋์ ํ๊ฒฝ์ ๋ฐฐํฌ๋ฉ๋๋ค.
์ฌ๋ฐ๋ฅธ ๋๊ตฌ ์ ํํ๊ธฐ
JavaScript ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง๋ฅผ ์์ฑํ๊ณ ๋ถ์ํ๋ ๋ฐ ์ฌ์ฉํ ์ ์๋ ํ๋ฅญํ ๋๊ตฌ๋ค์ด ๋ช ๊ฐ์ง ์์ต๋๋ค. ์ต์์ ์ ํ์ ํ ์คํธ ํ๋ ์์ํฌ์ CI/CD ํ๊ฒฝ์ ๋ฐ๋ผ ๋ค๋ฆ ๋๋ค.
ํ ์คํธ ํ๋ ์์ํฌ ๋ฐ ์ปค๋ฒ๋ฆฌ์ง ๋๊ตฌ
- Jest: Facebook(Meta)์์ ๊ฐ๋ฐํ ์ธ๊ธฐ ์๋ JavaScript ํ ์คํธ ํ๋ ์์ํฌ์ธ Jest๋ ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง๋ฅผ ๊ธฐ๋ณธ์ ์ผ๋ก ์ง์ํฉ๋๋ค. ๋ด๋ถ์ ์ผ๋ก Istanbul์ ์ฌ์ฉํ์ฌ ์ปค๋ฒ๋ฆฌ์ง ๋ณด๊ณ ์๋ฅผ ์์ฑํฉ๋๋ค. Jest์ ๋จ์์ฑ๊ณผ ์ฌ์ฉ ํธ์์ฑ์ ๋ง์ ํ๋ก์ ํธ์ ํ๋ฅญํ ์ ํ์ด ๋ฉ๋๋ค. `jest.config.js` ํ์ผ์์ ์ปค๋ฒ๋ฆฌ์ง ์๊ณ๊ฐ์ ์ค์ ํ ์ ์์ต๋๋ค:
- Mocha: Mocha๋ ๋ค์ํ ์ด์ค์
๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ฐ ์ปค๋ฒ๋ฆฌ์ง ๋๊ตฌ์ ํตํฉํ ์ ์๋ ์ ์ฐํ JavaScript ํ
์คํธ ํ๋ ์์ํฌ์
๋๋ค. Mocha์ ํจ๊ป Istanbul(nyc๋ผ๊ณ ๋ ํจ)์ด๋ blanket.js์ ๊ฐ์ ๋ค๋ฅธ ์ปค๋ฒ๋ฆฌ์ง ๋๊ตฌ๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
// Example using nyc with mocha npm install --save-dev nyc mocha // Run tests with coverage nyc mocha test/**/*.js - Cypress: Cypress๋ ์ค์ ๋ธ๋ผ์ฐ์ ํ๊ฒฝ์์ ์ ํ๋ฆฌ์ผ์ด์
์ ํ
์คํธํ ์ ์๋ ๊ฐ๋ ฅํ ์๋ํฌ์๋ ํ
์คํธ ํ๋ ์์ํฌ์
๋๋ค. Cypress๋ก ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง๋ฅผ ์์ฑํ๋ ค๋ฉด `cypress-istanbul` ํ๋ฌ๊ทธ์ธ์ ์ฌ์ฉํ ์ ์์ต๋๋ค. ์ด๋ฅผ ์ํด์๋ `babel-plugin-istanbul`๋ก ์ฝ๋๋ฅผ ๊ณ์ธกํด์ผ ํฉ๋๋ค.
// cypress/plugins/index.js module.exports = (on, config) => { require('@cypress/code-coverage/task')(on, config) return config } - Karma: Karma๋ ์ฌ๋ฌ ๋ธ๋ผ์ฐ์ ์์ ํ ์คํธ๋ฅผ ์คํํ ์ ์๋ ํ ์คํธ ๋ฌ๋์ ๋๋ค. Karma๋ฅผ Istanbul์ด๋ ๋ค๋ฅธ ์ปค๋ฒ๋ฆฌ์ง ๋๊ตฌ์ ํตํฉํ์ฌ ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง ๋ณด๊ณ ์๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.
// jest.config.js
module.exports = {
// ... other configurations
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80,
},
},
};
CI/CD ํ๋ซํผ
๋๋ถ๋ถ์ CI/CD ํ๋ซํผ์ ํ ์คํธ ์คํ ๋ฐ ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง ๋ณด๊ณ ์ ์์ฑ์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ง์ํฉ๋๋ค. ๋ค์์ ๋ช ๊ฐ์ง ์ธ๊ธฐ ์๋ ์ต์ ์ ๋๋ค:
- GitHub Actions: GitHub Actions๋ CI/CD ์ํฌํ๋ก๋ฅผ ์๋ํํ๋ ์ ์ฐํ๊ณ ๊ฐ๋ ฅํ ๋ฐฉ๋ฒ์ ์ ๊ณตํฉ๋๋ค. GitHub Actions๋ฅผ ์ฌ์ฉํ์ฌ ํ
์คํธ๋ฅผ ์คํํ๊ณ , ์ปค๋ฒ๋ฆฌ์ง ๋ณด๊ณ ์๋ฅผ ์์ฑํ๋ฉฐ, ํ์ง ๊ฒ์ดํธ๋ฅผ ๊ฐ์ ํ ์ ์์ต๋๋ค. ๋ง์ผํ๋ ์ด์ค์๋ ์๊ฐํ๋ฅผ ์ํด ์ปค๋ฒ๋ฆฌ์ง ๋ณด๊ณ ์๋ฅผ ์ง์ ์
๋ก๋ํ๊ณ ์ฒ๋ฆฌํ ์ ์๋ ๋ง์ ์ก์
์ด ์์ต๋๋ค.
# .github/workflows/ci.yml name: CI on: push: branches: [ main ] pull_request: branches: [ main ] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Use Node.js 16 uses: actions/setup-node@v3 with: node-version: '16.x' - run: npm install - run: npm test -- --coverage - name: Upload coverage to Codecov uses: codecov/codecov-action@v3 with: token: ${{ secrets.CODECOV_TOKEN }} flags: unittests name: codecov-umbrella - Jenkins: Jenkins๋ ์ํํธ์จ์ด๋ฅผ ๋น๋, ํ ์คํธ ๋ฐ ๋ฐฐํฌํ๋ ๋ฐ ์ฌ์ฉํ ์ ์๋ ๋๋ฆฌ ์ฌ์ฉ๋๋ ์คํ ์์ค ์๋ํ ์๋ฒ์ ๋๋ค. Jenkins๋ ๋ค์ํ ํ ์คํธ ํ๋ ์์ํฌ ๋ฐ ์ปค๋ฒ๋ฆฌ์ง ๋๊ตฌ์ ํตํฉํ๊ธฐ ์ํ ํ๋ฌ๊ทธ์ธ์ ์ ๊ณตํฉ๋๋ค.
- CircleCI: CircleCI๋ ์ํํธ์จ์ด ๊ฐ๋ฐ ์ํฌํ๋ก๋ฅผ ์๋ํํ๋ ๊ฐ๋จํ๊ณ ์ง๊ด์ ์ธ ๋ฐฉ๋ฒ์ ์ ๊ณตํ๋ ํด๋ผ์ฐ๋ ๊ธฐ๋ฐ CI/CD ํ๋ซํผ์ ๋๋ค.
- GitLab CI/CD: GitLab CI/CD๋ GitLab ํ๋ซํผ์ ์ง์ ํตํฉ๋์ด ์ ํ๋ฆฌ์ผ์ด์ ์ ๋น๋, ํ ์คํธ ๋ฐ ๋ฐฐํฌํ๋ ์ํํ ๊ฒฝํ์ ์ ๊ณตํฉ๋๋ค.
- Azure DevOps: Azure DevOps๋ CI/CD ํ์ดํ๋ผ์ธ์ ํฌํจํ ์ํํธ์จ์ด ๊ฐ๋ฐ์ ์ํ ํฌ๊ด์ ์ธ ๋๊ตฌ ๋ชจ์์ ์ ๊ณตํฉ๋๋ค.
์ปค๋ฒ๋ฆฌ์ง ๋ณด๊ณ ๋ฐ ์๊ฐํ ๋๊ตฌ
- Codecov: Codecov๋ ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง ์งํ๋ฅผ ์๊ฐํํ๊ณ ์ถ์ ํ๋ ์ธ๊ธฐ ์๋ ์๋น์ค์ ๋๋ค. ๋ง์ CI/CD ํ๋ซํผ ๋ฐ ํ ์คํธ ํ๋ ์์ํฌ์ ์ํํ๊ฒ ํตํฉ๋ฉ๋๋ค. Codecov๋ ๋ํ GitHub, GitLab, Bitbucket๊ณผ์ ํตํฉ์ ์ง์ํ์ฌ ํ ๋ฆฌํ์คํธ์ ์ฃผ์์ ์ ๊ณตํฉ๋๋ค.
- Coveralls: Codecov์ ์ ์ฌํ๊ฒ Coveralls๋ ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง ๋ณด๊ณ ๋ฐ ๋ถ์์ ์ ๊ณตํฉ๋๋ค.
- SonarQube: ์ฃผ๋ก ์ ์ ๋ถ์ ๋๊ตฌ์ด์ง๋ง SonarQube๋ ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง ๋ถ์๋ ์ง์ํ๋ฉฐ ์ฝ๋ ํ์ง์ ๋ํ ํฌ๊ด์ ์ธ ๋ณด๊ณ ์๋ฅผ ์ ๊ณตํฉ๋๋ค. SonarQube๋ ๋๊ท๋ชจ ์ฝ๋๋ฒ ์ด์ค๋ ๋ณต์กํ ํ๋ก์ ํธ๋ฅผ ๋ค๋ฃฐ ๋ ํนํ ์ ์ฉํฉ๋๋ค.
์ค์ฉ์ ์ธ ์์ ๋ฐ ๊ตฌํ
๋ค์ํ ๋๊ตฌ๋ฅผ ์ฌ์ฉํ์ฌ CI/CD ํ์ดํ๋ผ์ธ์ ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง๋ฅผ ํตํฉํ๋ ๋ช ๊ฐ์ง ์ค์ฉ์ ์ธ ์์ ๋ฅผ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
์์ 1: Jest์ GitHub Actions ์ฌ์ฉํ๊ธฐ
- Jest ์ค์น ๋ฐ ์ปค๋ฒ๋ฆฌ์ง ์ค์ :
`package.json` ๋๋ `jest.config.js`์์ Jest๋ฅผ ์ค์ ํ์ฌ ์ปค๋ฒ๋ฆฌ์ง๋ฅผ ํ์ฑํํฉ๋๋ค.
npm install --save-dev jest - GitHub Actions ์ํฌํ๋ก ์์ฑ: `.github/workflows/ci.yml` ํ์ผ์ ๋ง๋ค๊ณ ๋ค์ ๋ด์ฉ์ ์ถ๊ฐํฉ๋๋ค:
# .github/workflows/ci.yml name: CI on: push: branches: [ main ] pull_request: branches: [ main ] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Use Node.js 16 uses: actions/setup-node@v3 with: node-version: '16.x' - run: npm install - run: npm test -- --coverage - name: Upload coverage to Codecov uses: codecov/codecov-action@v3 with: token: ${{ secrets.CODECOV_TOKEN }} flags: unittests name: codecov-umbrella - Codecov ์ค์ : Codecov์ ๊ณ์ ์ ๋ง๋ค๊ณ ๋ฆฌํฌ์งํ ๋ฆฌ ํ ํฐ์ ์ป์ต๋๋ค. ์ด ํ ํฐ์ GitHub ๋ฆฌํฌ์งํ ๋ฆฌ์ ์ํฌ๋ฆฟ์ผ๋ก ์ถ๊ฐํฉ๋๋ค(Settings -> Secrets -> Actions).
- ์ปค๋ฐ ๋ฐ ํธ์: ๋ณ๊ฒฝ ์ฌํญ์ ์ปค๋ฐํ๊ณ GitHub ๋ฆฌํฌ์งํ ๋ฆฌ๋ก ํธ์ํฉ๋๋ค. GitHub Actions ์ํฌํ๋ก๊ฐ ์๋์ผ๋ก ํ ์คํธ๋ฅผ ์คํํ๊ณ ์ปค๋ฒ๋ฆฌ์ง ๋ณด๊ณ ์๋ฅผ Codecov์ ์ ๋ก๋ํฉ๋๋ค.
์์ 2: Mocha, Istanbul(nyc), Jenkins ์ฌ์ฉํ๊ธฐ
- Mocha ๋ฐ nyc ์ค์น:
npm install --save-dev mocha nyc - nyc ์ค์ : `package.json` ํ์ผ์์ `nyc`๋ฅผ ์ค์ ํฉ๋๋ค:
// package.json { // ... "scripts": { "test": "mocha test/**/*.js", "coverage": "nyc mocha test/**/*.js" }, "nyc": { "reporter": ["text", "html"] } } - Jenkins ์ค์ :
- ์๋ก์ด Jenkins ์์ ์ ์์ฑํฉ๋๋ค.
- ๋ฒ์ ๊ด๋ฆฌ ์์คํ ์์ ์ฝ๋๋ฅผ ์ฒดํฌ์์ํ๋๋ก ์์ ์ ์ค์ ํฉ๋๋ค.
- ๋ค์ ๋ช
๋ น์ ์คํํ๋ ๋น๋ ๋จ๊ณ๋ฅผ ์ถ๊ฐํฉ๋๋ค:
npm run coverage - Jenkins์ HTML Publisher ํ๋ฌ๊ทธ์ธ์ ์ค์นํฉ๋๋ค.
- nyc์์ ์์ฑ๋ HTML ์ปค๋ฒ๋ฆฌ์ง ๋ณด๊ณ ์(์ผ๋ฐ์ ์ผ๋ก `coverage` ๋๋ ํ ๋ฆฌ์ ์์น)๋ฅผ ๊ฒ์ํ๋ ๋น๋ ํ ์์ ์ ์ถ๊ฐํฉ๋๋ค.
- Jenkins ์์ ์คํ: Jenkins ์์ ์ ์คํํ์ฌ ํ ์คํธ๋ฅผ ์คํํ๊ณ ์ปค๋ฒ๋ฆฌ์ง ๋ณด๊ณ ์๋ฅผ ์์ฑํฉ๋๋ค.
์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง ๋ชจ๋ฒ ์ฌ๋ก
์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง๋ ๊ฐ์น ์๋ ์งํ์ด์ง๋ง, ํ๋ช ํ๊ฒ ์ฌ์ฉํ๊ณ ์ผ๋ฐ์ ์ธ ํจ์ ์ ํผํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค.
- ๋์ ์ปค๋ฒ๋ฆฌ์ง๋ฅผ ๋ชฉํ๋ก ํ๋, ์ง์ฐฉํ์ง ๋ง ๊ฒ: ๋์ ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง๋ฅผ ์ํด ๋ ธ๋ ฅํ๋, 100% ๋ฌ์ฑ์ ์ง์ฐฉํ์ง ๋ง์ญ์์ค. ์ค์ํ ๊ธฐ๋ฅ๊ณผ ์ฃ์ง ์ผ์ด์ค๋ฅผ ๋ค๋ฃจ๋ ์๋ฏธ ์๋ ํ ์คํธ๋ฅผ ๊ฐ๋ ๊ฒ์ด ๋ ์ค์ํฉ๋๋ค. ์ปค๋ฒ๋ฆฌ์ง ๋น์จ์๋ง ์ง์คํ๋ฉด ์ค์ ์ฝ๋ ํ์ง์ ํฅ์์ํค์ง ์๋ ํผ์์ ์ธ ํ ์คํธ๋ฅผ ์์ฑํ๊ฒ ๋ ์ ์์ต๋๋ค.
- ์ค์ํ ์ฝ๋์ ์ง์คํ ๊ฒ: ์ฝ๋๋ฒ ์ด์ค์ ๊ฐ์ฅ ์ค์ํ๊ณ ๋ณต์กํ ๋ถ๋ถ์ ํ ์คํธ๋ฅผ ์ฐ์ ์์๋ก ๋์ญ์์ค. ์ด๋ฌํ ์์ญ์ ๋ฒ๊ทธ์ ์ทจ์ฝ์ ์ ํฌํจํ ๊ฐ๋ฅ์ฑ์ด ๋ ๋์ต๋๋ค.
- ์๋ฏธ ์๋ ํ ์คํธ๋ฅผ ์์ฑํ ๊ฒ: ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง๋ ํ ์คํธ์ ํ์ง๋งํผ๋ง ์ข์ต๋๋ค. ์ฝ๋๋ฅผ ์ฒ ์ ํ๊ฒ ์คํํ๊ณ ๋ค์ํ ์๋๋ฆฌ์ค๋ฅผ ๋ค๋ฃจ๋ ํ ์คํธ๋ฅผ ์์ฑํ์ญ์์ค.
- ์ปค๋ฒ๋ฆฌ์ง๋ฅผ ๋ชฉํ๊ฐ ์๋ ๊ฐ์ด๋๋ก ์ฌ์ฉํ ๊ฒ: ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง ๋ณด๊ณ ์๋ฅผ ์ฌ์ฉํ์ฌ ๋ ๋ง์ ํ ์คํธ๊ฐ ํ์ํ ์์ญ์ ์๋ณํ๋, ์ด๊ฒ์ด ํ ์คํธ ์ ๋ต์ ์ข์ฐํ๊ฒ ๋์ง ๋ง์ญ์์ค.
- ๋ค๋ฅธ ์งํ์ ๊ฒฐํฉํ ๊ฒ: ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง๋ ์ ์ ๋ถ์ ๋ฐ ์ฝ๋ ๋ฆฌ๋ทฐ์ ๊ฐ์ ๋ค๋ฅธ ์ฝ๋ ํ์ง ์งํ์ ํจ๊ป ์ฌ์ฉํด์ผ ํฉ๋๋ค.
- ํ์ค์ ์ธ ์๊ณ๊ฐ์ ์ค์ ํ ๊ฒ: ๋๋ฌด ๋์ ์๊ณ๊ฐ์ ์ค์ ํ๋ ๊ฒ์ ๋น์์ฐ์ ์ผ ์ ์์ต๋๋ค. ๋ฌ์ฑ ๊ฐ๋ฅํ ๋ชฉํ๋ก ์์ํ์ฌ ํ ์คํธ๊ฐ ์ฑ์ํด์ง์ ๋ฐ๋ผ ์ ์ฐจ ๋์ฌ๊ฐ์ญ์์ค. ์ปค๋ฒ๋ฆฌ์ง ๋ชฉํ๋ฅผ ์ค์ ํ ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ค๋ฅธ ๋ถ๋ถ๊ณผ ๊ด๋ จ๋ ๋ณต์ก์ฑ๊ณผ ์ํ์ ๊ณ ๋ คํ์ญ์์ค.
- ์ปค๋ฒ๋ฆฌ์ง ๊ฒ์ฌ๋ฅผ ์๋ํํ ๊ฒ: CI/CD ํ์ดํ๋ผ์ธ์ ์ปค๋ฒ๋ฆฌ์ง ๊ฒ์ฌ๋ฅผ ํตํฉํ์ฌ ํ๊ท๋ฅผ ์๋์ผ๋ก ๊ฐ์งํ๊ณ ํ์ง ๊ฒ์ดํธ๋ฅผ ๊ฐ์ ํ์ญ์์ค.
- ์ ๊ธฐ์ ์ผ๋ก ์ปค๋ฒ๋ฆฌ์ง ๋ณด๊ณ ์๋ฅผ ๊ฒํ ํ ๊ฒ: ์ ๊ธฐ์ ์ผ๋ก ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง ๋ณด๊ณ ์๋ฅผ ๊ฒํ ํ๊ณ ๊ฐ์ ํ ์์ญ์ ์๋ณํ๋ ์ต๊ด์ ๋ค์ด์ญ์์ค.
๊ณ ๊ธ ๊ธฐ์ ๋ฐ ๊ณ ๋ ค ์ฌํญ
- ๋ฎคํ ์ด์ ํ ์คํธ(Mutation Testing): ๋ฎคํ ์ด์ ํ ์คํธ๋ ์ฝ๋์ ์์ ๋ณ๊ฒฝ(๋ฎคํ ์ด์ )์ ๋์ ํ๊ณ ํ ์คํธ๊ฐ ์ด๋ฌํ ๋ณ๊ฒฝ์ ๊ฐ์งํ ์ ์๋์ง ํ์ธํ๋ ๊ธฐ์ ์ ๋๋ค. ์ด๋ ํ ์คํธ ์ค์ํธ์ ํจ์จ์ฑ์ ํ๊ฐํ๊ณ ํ ์คํธ ์ ๋ต์ ์ฝ์ ์ ์๋ณํ๋ ๋ฐ ๋์์ด ๋ฉ๋๋ค. JavaScript ๋ฎคํ ์ด์ ํ ์คํธ์๋ Stryker์ ๊ฐ์ ๋๊ตฌ๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
- ์ฐจ๋ฑ ์ปค๋ฒ๋ฆฌ์ง(Differential Coverage): ์ฐจ๋ฑ ์ปค๋ฒ๋ฆฌ์ง๋ ํน์ ์ปค๋ฐ์ด๋ ํ ๋ฆฌํ์คํธ์์ ๋ณ๊ฒฝ๋ ์ฝ๋๋ง์ ์ปค๋ฒ๋ฆฌ์ง์ ์ค์ ์ ๋ก๋๋ค. ์ด๋ฅผ ํตํด ๋ณ๊ฒฝ ์ฌํญ์ด ์ฝ๋ ํ์ง์ ๋ฏธ์น๋ ์ํฅ์ ์ ์ํ๊ฒ ํ๊ฐํ๊ณ ์๋ก ํ ์คํธ๋์ง ์์ ์์ญ์ ์๋ณํ ์ ์์ต๋๋ค.
- ์ฑ๋ฅ ๊ณ ๋ ค ์ฌํญ: ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง ๋ณด๊ณ ์๋ฅผ ์์ฑํ๋ฉด ํ ์คํธ ์คํ์ ์ค๋ฒํค๋๊ฐ ์ถ๊ฐ๋ ์ ์์ต๋๋ค. ํ ์คํธ ํ๊ฒฝ์ ์ต์ ํํ๊ณ ๋ณ๋ ฌ ํ ์คํธ์ ๊ฐ์ ๊ธฐ์ ์ ์ฌ์ฉํ์ฌ ์ฑ๋ฅ์ ๋ฏธ์น๋ ์ํฅ์ ์ต์ํํ์ญ์์ค.
- ์ ์ ๋ถ์๊ณผ์ ํตํฉ: ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง ๋ถ์์ ESLint ๋ฐ SonarQube์ ๊ฐ์ ์ ์ ๋ถ์ ๋๊ตฌ์ ๊ฒฐํฉํ์ฌ ์ฝ๋ ํ์ง์ ๋ํ ๋ณด๋ค ํฌ๊ด์ ์ธ ์๊ฐ์ ์ป์ผ์ญ์์ค. ์ ์ ๋ถ์์ ํ ์คํธ๋ก ๋ฐ๊ฒฌ๋์ง ์์ ์ ์๋ ์ ์ฌ์ ์ธ ์ฝ๋ ๊ฒฐํจ ๋ฐ ์ทจ์ฝ์ ์ ์๋ณํ ์ ์์ต๋๋ค.
์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง์ ๋ํ ๊ธ๋ก๋ฒ ๊ด์
์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง์ ์ค์์ฑ์ ์ ์ธ๊ณ ๋ค์ํ ์ํํธ์จ์ด ๊ฐ๋ฐ ํ๊ณผ ์กฐ์ง์์ ์ธ์ ๋ฐ๊ณ ์์ต๋๋ค. ์ฌ์ฉ๋๋ ํน์ ๋๊ตฌ๋ ๊ธฐ์ ์ ์ง์ญ ๋ฐ ์ฐ์ ์ ๋ฐ๋ผ ๋ค๋ฅผ ์ ์์ง๋ง, ์ฝ๋ ํ์ง ๊ฐ์ , ๋ฒ๊ทธ ๊ฐ์, ์ ๋ขฐํ ์ ์๋ ์ํํธ์จ์ด ์ ๊ณต์ด๋ผ๋ ๊ธฐ๋ณธ ์์น์ ๋์ผํฉ๋๋ค.
- ์ ๋ฝ: ์ ๋ฝ์ ์ํํธ์จ์ด ๊ฐ๋ฐ ํ์ฌ๋ ๊ธ์ต ๋ฐ ์๋ฃ์ ๊ฐ์ ์ฐ์ ์ ์๊ฒฉํ ๊ท์ ์๊ฑด์ผ๋ก ์ธํด ์๊ฒฉํ ํ ์คํธ ๋ฐ ์ฝ๋ ํ์ง ํ์ค์ ๊ฐ์กฐํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ต๋๋ค. ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง๋ ์ด๋ฌํ ํ์ค ์ค์๋ฅผ ๋ณด์ฅํ๊ธฐ ์ํด ๋๋ฆฌ ์ฌ์ฉ๋ฉ๋๋ค.
- ๋ถ๋ฏธ: ๋ถ๋ฏธ ๊ธฐ์ , ํนํ ๊ธฐ์ ์ฐ์ ์์๋ ์ ์ํ ๊ฐ๋ฐ๊ณผ ์ง์์ ์ธ ์ ๊ณต์ ์ฐ์ ์ํฉ๋๋ค. ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง๋ ํ ์คํธ๋ฅผ ์๋ํํ๊ณ ํ๊ท๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด CI/CD ํ์ดํ๋ผ์ธ์ ํตํฉ๋ฉ๋๋ค.
- ์์์: ์์์ ์ํํธ์จ์ด ๊ฐ๋ฐ ํ์ ์ ์์ผ ๋ฐฉ๋ฒ๋ก ๊ณผ DevOps ๊ดํ์ ์ ์ ๋ ๋ง์ด ์ฑํํ๊ณ ์์ผ๋ฉฐ, ์ด๋ ํ์ง ๋ณด์ฆ ํ๋ก์ธ์ค์ ํต์ฌ ๊ตฌ์ฑ ์์๋ก ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง๋ฅผ ํฌํจํฉ๋๋ค.
- ํธ์ฃผ: ํ์ ๊ณผ ๊ธฐ์ ์ ์ค์ ์ ๋ ํธ์ฃผ ๊ธฐ์ ๋ค์ ๊ตญ๋ด์ธ ์์ฅ์ ์ํ ๊ณ ํ์ง ์ํํธ์จ์ด๋ฅผ ๊ตฌ์ถํ๊ธฐ ์ํด ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง๋ฅผ ์ ๊ทน์ ์ผ๋ก ํ์ฉํ๊ณ ์์ต๋๋ค.
๊ฒฐ๋ก
JavaScript ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง๋ฅผ CI/CD ํ์ดํ๋ผ์ธ์ ํตํฉํ๋ ๊ฒ์ ๊ฒฌ๊ณ ํ๊ณ ์ ๋ขฐํ ์ ์๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ๋ ๋ฐ ์ค์ํ ๋จ๊ณ์ ๋๋ค. ํ ์คํธ์ ํจ์จ์ฑ์ ๋ํ ํต์ฐฐ๋ ฅ์ ์ ๊ณตํ๊ณ ํ ์คํธ๋์ง ์์ ์์ญ์ ์๋ณํ๋ ๋ฐ ๋์์ ์ค์ผ๋ก์จ, ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง๋ ์ฝ๋ ํ์ง์ ๊ฐ์ ํ๊ณ ๋ฒ๊ทธ๋ฅผ ์ค์ด๋ฉฐ ๋ ๋์ ์ฌ์ฉ์ ๊ฒฝํ์ ์ ๊ณตํ ์ ์๊ฒ ํด์ค๋๋ค. ์ฌ๋ฐ๋ฅธ ๋๊ตฌ๋ฅผ ์ ํํ๊ณ , ๋ชจ๋ฒ ์ฌ๋ก๋ฅผ ๋ฐ๋ฅด๋ฉฐ, ํ ์คํธ ์ ๋ต์ ์ง์์ ์ผ๋ก ๊ฐ์ ํ๊ธฐ ์ํด ๋ ธ๋ ฅํ์ญ์์ค. ๊ฐ๋ฐ ์ํฌํ๋ก์ ํ์์ ์ธ ๋ถ๋ถ์ผ๋ก ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง๋ฅผ ๋ฐ์๋ค์ด๋ฉด ์ธ๊ณ์ ์์ค์ JavaScript ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ๋ ๊ธธ์ ๋ค์ด์๊ฒ ๋ ๊ฒ์ ๋๋ค.