์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋ ๊ด๋ฆฌ ํ๋ ์์ํฌ์ ํ์ฅ ๊ฐ๋ฅํ๊ณ ์ ์ง๋ณด์ ์ฉ์ดํ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ์ํ ๊ฒฌ๊ณ ํ ํ์ง ๋ณด์ฆ ์ธํ๋ผ ๊ตฌ์ถ ๋ฐฉ๋ฒ์ ์์๋ณด์ธ์. ํ ์คํธ, ๋ฆฐํ , ์ง์์ ํตํฉ์ ์ํ ๋ชจ๋ฒ ์ฌ๋ก, ๋๊ตฌ, ์ ๋ต์ ํ์ตํฉ๋๋ค.
์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋ ๊ด๋ฆฌ ํ๋ ์์ํฌ: ๊ฒฌ๊ณ ํ ํ์ง ๋ณด์ฆ ์ธํ๋ผ ๊ตฌ์ถํ๊ธฐ
์ค๋๋ ๋น ๋ฅด๊ฒ ๋ฐ์ ํ๋ ์น ๊ฐ๋ฐ ํ๊ฒฝ์์ ์๋ฐ์คํฌ๋ฆฝํธ๋ ํ๋ก ํธ์๋์ ๋ฐฑ์๋ ๊ฐ๋ฐ ๋ชจ๋์์ ์ง๋ฐฐ์ ์ธ ์ธ์ด๊ฐ ๋์์ต๋๋ค. ํนํ ํฌ๊ณ ๋ณต์กํ ํ๋ก์ ํธ์์ ์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋๋ฅผ ํจ๊ณผ์ ์ผ๋ก ๊ด๋ฆฌํ๋ ๊ฒ์ ํ์ฅ์ฑ, ์ ์ง๋ณด์์ฑ, ์ ๋ฐ์ ์ธ ํ์ง์ ๋ณด์ฅํ๋ ๋ฐ ๋งค์ฐ ์ค์ํฉ๋๋ค. ์ด๋ฅผ ์ํด์๋ ๊ฒฌ๊ณ ํ ํ์ง ๋ณด์ฆ(QA) ์ธํ๋ผ๊ฐ ๋ท๋ฐ์นจ๋๋ ์ ์ ์๋ ์ฝ๋ ๊ด๋ฆฌ ํ๋ ์์ํฌ๊ฐ ํ์ํฉ๋๋ค.
์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋ ๊ด๋ฆฌ ํ๋ ์์ํฌ๋ ๋ฌด์์ธ๊ฐ?
์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋ ๊ด๋ฆฌ ํ๋ ์์ํฌ๋ ๊ฐ๋ฐ ํ๋ก์ธ์ค๋ฅผ ๊ฐ์ํํ๊ณ , ์ฝ๋ ํ์ง์ ํฅ์์ํค๋ฉฐ, ๊ฐ๋ฐ์ ๊ฐ์ ํ์ ์ ์ด์งํ๊ธฐ ์ํด ๊ณ ์๋ ์ผ๋ จ์ ๊ดํ, ๋๊ตฌ ๋ฐ ๊ฐ์ด๋๋ผ์ธ์ ํฌํจํฉ๋๋ค. ์ด๋ ๋จ์ํ ์ฝ๋๋ฅผ ์์ฑํ๋ ๊ฒ์ ๋์ด ์ฝ๋๊ฐ ์ด๋ป๊ฒ ๊ตฌ์ฑ๋๊ณ , ํ ์คํธ๋๊ณ , ๊ฒํ ๋๊ณ , ๋ฐฐํฌ๋๋์ง์ ์ด์ ์ ๋ง์ถฅ๋๋ค. ์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋ ๊ด๋ฆฌ ํ๋ ์์ํฌ์ ์ฃผ์ ์ธก๋ฉด์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
- ์ฝ๋ฉ ํ์ค ๋ฐ ๊ท์ฝ: ์ผ๊ด๋ ์ฝ๋ฉ ์คํ์ผ์ ๊ฐ๋ ์ฑ๊ณผ ์ ์ง๋ณด์์ฑ์ ํฅ์์ํต๋๋ค.
- ๋ฒ์ ๊ด๋ฆฌ: Git(๋๋ ์ ์ฌ ๋๊ตฌ)์ ์ฌ์ฉํ์ฌ ๋ณ๊ฒฝ ์ฌํญ์ ์ถ์ ํ๊ณ ํ์ ์ ์ด์งํฉ๋๋ค.
- ํ ์คํ : ์ฝ๋ ๊ธฐ๋ฅ์ฑ์ ๋ณด์ฅํ๊ธฐ ์ํด ๋ค์ํ ์ ํ์ ํ ์คํธ(๋จ์, ํตํฉ, ์ข ๋จ ๊ฐ)๋ฅผ ๊ตฌํํฉ๋๋ค.
- ๋ฆฐํ ๋ฐ ์ฝ๋ ๋ถ์: ์ ์ฌ์ ์ค๋ฅ๋ฅผ ์๋ณํ๊ณ ์ฝ๋ฉ ํ์ค์ ๊ฐ์ ํ๋ ์๋ํ๋ ๋๊ตฌ์ ๋๋ค.
- ์ฝ๋ ๋ฆฌ๋ทฐ: ์ค๋ฅ๋ฅผ ๋ฐ๊ฒฌํ๊ณ ์ฝ๋ ํ์ง์ ํฅ์์ํค๊ธฐ ์ํ ๋๋ฃ ๊ฒํ ์ ๋๋ค.
- ์ง์์ ํตํฉ/์ง์์ ๋ฐฐํฌ(CI/CD): ๋น๋, ํ ์คํธ, ๋ฐฐํฌ ํ๋ก์ธ์ค๋ฅผ ์๋ํํฉ๋๋ค.
- ์์กด์ฑ ๊ด๋ฆฌ: npm์ด๋ yarn๊ณผ ๊ฐ์ ๋๊ตฌ๋ฅผ ์ฌ์ฉํ์ฌ ํ๋ก์ ํธ ์์กด์ฑ์ ๊ด๋ฆฌํฉ๋๋ค.
- ๋ฌธ์ํ: ์ฝ๋์ API์ ๋ํ ๋ช ํํ๊ณ ๊ฐ๊ฒฐํ ๋ฌธ์๋ฅผ ์์ฑํฉ๋๋ค.
๊ฒฌ๊ณ ํ QA ์ธํ๋ผ๊ฐ ์ ํ์์ ์ธ๊ฐ?
A solid QA infrastructure is the backbone of any successful JavaScript project. It ensures that code is reliable, maintainable, and delivers the expected functionality. The benefits of a robust QA infrastructure are numerous:- ๋ฒ๊ทธ ๊ฐ์: ๋ฒ๊ทธ์ ์กฐ๊ธฐ ๋ฐ๊ฒฌ ๋ฐ ์๋ฐฉ.
- ์ฝ๋ ํ์ง ํฅ์: ์ฝ๋ฉ ํ์ค๊ณผ ๋ชจ๋ฒ ์ฌ๋ก๋ฅผ ๊ฐ์ ํฉ๋๋ค.
- ๋ ๋น ๋ฅธ ๊ฐ๋ฐ ์ฃผ๊ธฐ: ์๋ํ๋ก ์๋ ํ ์คํธ ๋ ธ๋ ฅ์ด ์ค์ด๋ญ๋๋ค.
- ์์ ๊ฐ ์ฆ๊ฐ: ๊ฐ๋ฐ์๋ค์ด ์์ ์ ์ฝ๋์ ๋ ์์ ๊ฐ์ ๊ฐ๊ฒ ๋ฉ๋๋ค.
- ์ ์ง๋ณด์ ๋น์ฉ ์ ๊ฐ: ์ฝ๋ ์ ์ง๋ณด์ ๋ฐ ๋๋ฒ๊น ์ด ๋ ์ฌ์์ง๋๋ค.
- ํ์ ๊ฐํ: ๋ช ํํ ๊ฐ์ด๋๋ผ์ธ๊ณผ ํ๋ก์ธ์ค๊ฐ ํ์ ์ ์ด์งํฉ๋๋ค.
- ์ฌ์ฉ์ ๊ฒฝํ ํฅ์: ๋์ ํ์ง์ ์ฝ๋๋ ๋ ๋์ ์ฌ์ฉ์ ๊ฒฝํ์ผ๋ก ์ด์ด์ง๋๋ค.
์๋ฐ์คํฌ๋ฆฝํธ QA ์ธํ๋ผ ๊ตฌ์ถํ๊ธฐ: ๋จ๊ณ๋ณ ๊ฐ์ด๋
ํฌ๊ด์ ์ธ ์๋ฐ์คํฌ๋ฆฝํธ QA ์ธํ๋ผ๋ฅผ ๊ตฌ์ถํ๋ ค๋ฉด ์ ์คํ ๊ณํ๊ณผ ๊ตฌํ์ด ํ์ํฉ๋๋ค. ๋ค์์ ๋จ๊ณ๋ณ ๊ฐ์ด๋์ ๋๋ค:
1. ์ฝ๋ฉ ํ์ค ๋ฐ ๊ท์ฝ ์๋ฆฝ
์ผ๊ด๋ ์ฝ๋ฉ ์คํ์ผ์ ๊ฐ๋ ์ฑ๊ณผ ์ ์ง๋ณด์์ฑ์ ํ์์ ์ ๋๋ค. ์คํ์ผ ๊ฐ์ด๋(์: Airbnb, Google, StandardJS)๋ฅผ ์ ํํ๊ฑฐ๋ ์์ ๋ง์ ์คํ์ผ ๊ฐ์ด๋๋ฅผ ๋ง๋์ธ์. ์ฝ๋ฉ ํ์ค์ ์ฃผ์ ์์๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
- ๋ค์ฌ์ฐ๊ธฐ: ์ผ๊ด๋ ๋ค์ฌ์ฐ๊ธฐ(๋ณดํต 2์นธ ๋๋ 4์นธ)
- ๋ค์ด๋ฐ ์ปจ๋ฒค์ : ๋ณ์, ํจ์, ํด๋์ค์ ๋ํ ๋ช ํํ๊ณ ์ค๋ช ์ ์ธ ์ด๋ฆ.
- ์ฃผ์: ๋ณต์กํ ๋ก์ง์ ์ค๋ช ํ๊ธฐ ์ํ ์ ์ ํ ์ฃผ์.
- ํ์ผ ๊ตฌ์ฑ: ์ผ๊ด๋ ํ์ผ ๊ตฌ์กฐ ๋ฐ ์ด๋ฆ ์ง์ .
์์:
// ์ข์ ์
const calculateArea = (width, height) => {
return width * height;
};
// ๋์ ์
var calcArea = function(w,h){
return w*h;
}
2. ๋ฆฐํ ๋ฐ ์ฝ๋ ๋ถ์ ๊ตฌํ
๋ฆฐํ ๋๊ตฌ๋ ์คํ์ผ ์๋ฐ, ์ ์ฌ์ ์ค๋ฅ ๋ฐ ์ฝ๋ฉ ํ์ค ์ค์ ์ฌ๋ถ๋ฅผ ์๋์ผ๋ก ํ์ธํฉ๋๋ค. ์ธ๊ธฐ ์๋ ์๋ฐ์คํฌ๋ฆฝํธ ๋ฆฐํฐ๋ก๋ ESLint์ JSHint๊ฐ ์์ต๋๋ค. SonarQube์ ๊ฐ์ ์ฝ๋ ๋ถ์ ๋๊ตฌ๋ ์ฝ๋ ํ์ง, ๋ณด์ ์ทจ์ฝ์ ๋ฐ ๊ธฐ์ ๋ถ์ฑ์ ๋ํ ๋ ๊น์ ํต์ฐฐ๋ ฅ์ ์ ๊ณตํฉ๋๋ค.
ESLint ์์ (์ค์ ):
ํ๋ก์ ํธ ๋ฃจํธ์ `.eslintrc.js` ํ์ผ์ ์์ฑํฉ๋๋ค:
module.exports = {
env: {
browser: true,
es2021: true,
node: true,
},
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:@typescript-eslint/recommended',
],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 12,
sourceType: 'module',
},
plugins: [
'react',
'@typescript-eslint',
],
rules: {
'indent': [
'error',
2,
],
'linebreak-style': [
'error',
'unix'
],
'quotes': [
'error',
'single'
],
'semi': [
'error',
'always'
]
},
};
์ด ์ค์ ์ ๊ถ์ฅ ESLint ๊ท์น์ ํ์ฅํ๊ณ , React ๋ฐ TypeScript ์ง์์ ์ถ๊ฐํ๋ฉฐ, ๋ค์ฌ์ฐ๊ธฐ, ์ค ๋ฐ๊ฟ, ๋ฐ์ดํ, ์ธ๋ฏธ์ฝ๋ก ์ ๋ํ ์ฌ์ฉ์ ์ ์ ๊ท์น์ ์ ์ํฉ๋๋ค.
3. ํ ์คํธ ํ๋ ์์ํฌ ์ ํ
์ฌ๋ฐ๋ฅธ ํ ์คํธ ํ๋ ์์ํฌ๋ฅผ ์ ํํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค. ์ธ๊ธฐ ์๋ ์ ํ์ง๋ก๋ Jest, Mocha, Jasmine, Cypress๊ฐ ์์ต๋๋ค. ํ๋ ์์ํฌ๋ฅผ ์ ํํ ๋ ๋ค์ ์์๋ฅผ ๊ณ ๋ คํ์ธ์:
- ์ฌ์ฉ ์ฉ์ด์ฑ: ํ ์คํธ๋ฅผ ์์ฑํ๊ณ ์คํํ๊ธฐ ์ผ๋ง๋ ์ฌ์ด๊ฐ?
- ๊ธฐ๋ฅ: ๋ชจ์(mocking), ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง ๋ฐ ๊ธฐํ ํ์ ๊ธฐ๋ฅ์ ์ง์ํ๋๊ฐ?
- ์ปค๋ฎค๋ํฐ ์ง์: ์ง์๊ณผ ๋ฆฌ์์ค๋ฅผ ์ ๊ณตํ๋ ํฌ๊ณ ํ๋ฐํ ์ปค๋ฎค๋ํฐ๊ฐ ์๋๊ฐ?
- ํตํฉ์ฑ: ๊ธฐ์กด ๋๊ตฌ ๋ฐ CI/CD ํ์ดํ๋ผ์ธ๊ณผ ์ ํตํฉ๋๋๊ฐ?
ํ ์คํธ ํผ๋ผ๋ฏธ๋: * ๋จ์ ํ ์คํธ: ๊ฐ๋ณ ๊ตฌ์ฑ ์์๋ ํจ์๋ฅผ ๋ ๋ฆฝ์ ์ผ๋ก ํ ์คํธํฉ๋๋ค. * ํตํฉ ํ ์คํธ: ์๋ก ๋ค๋ฅธ ๊ตฌ์ฑ ์์ ๊ฐ์ ์ํธ ์์ฉ์ ํ ์คํธํฉ๋๋ค. * ์ข ๋จ ๊ฐ ํ ์คํธ: ์ฌ์ฉ์ ์ํธ ์์ฉ๋ถํฐ ๋ฐ์ดํฐ ์์์ฑ๊น์ง ์ ์ฒด ์ ํ๋ฆฌ์ผ์ด์ ํ๋ฆ์ ํ ์คํธํฉ๋๋ค.
Jest ์์ (๋จ์ ํ ์คํธ):
// sum.js
const sum = (a, b) => {
return a + b;
};
module.exports = sum;
// sum.test.js
const sum = require('./sum');
test('1 + 2๊ฐ 3๊ณผ ๊ฐ์์ง ํ
์คํธ', () => {
expect(sum(1, 2)).toBe(3);
});
4. ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง ๊ตฌํ
์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง๋ ํ ์คํธ์ ์ํด ์คํ๋๋ ์ฝ๋์ ๋น์จ์ ์ธก์ ํฉ๋๋ค. ๋๋ถ๋ถ์ ์ฝ๋๊ฐ ํ ์คํธ๋๋๋ก ๋์ ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง(์: 80% ์ด์)๋ฅผ ๋ชฉํ๋ก ํ์ธ์. Jest ๋ฐ Istanbul๊ณผ ๊ฐ์ ๋๊ตฌ๋ ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง ๋ณด๊ณ ์๋ฅผ ์ ๊ณตํฉ๋๋ค.
์์ (Jest ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง):
Jest๊ฐ ์ปค๋ฒ๋ฆฌ์ง ์ ๋ณด๋ฅผ ์์งํ๋๋ก ์ค์ ํฉ๋๋ค:
// jest.config.js
module.exports = {
collectCoverage: true,
coverageReporters: ['html', 'text', 'text-summary'],
};
ํ ์คํธ๋ฅผ ์คํํ ํ, Jest๋ `coverage` ๋๋ ํ ๋ฆฌ์ ์ปค๋ฒ๋ฆฌ์ง ๋ณด๊ณ ์๋ฅผ ์์ฑํฉ๋๋ค.
5. ์ฝ๋ ๋ฆฌ๋ทฐ ์๋ํ
์ฝ๋ ๋ฆฌ๋ทฐ๋ QA ํ๋ก์ธ์ค์ ์ค์ํ ๋ถ๋ถ์ ๋๋ค. ๋ชจ๋ ์ฝ๋ ๋ณ๊ฒฝ์ ๋ํ ๋๋ฃ ๋ฆฌ๋ทฐ๋ฅผ ์ฅ๋ คํ์ธ์. GitHub, GitLab, Bitbucket๊ณผ ๊ฐ์ ๋๊ตฌ๋ ๋ด์ฅ๋ ์ฝ๋ ๋ฆฌ๋ทฐ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค. ๋ณ๊ฒฝ ์ฌํญ์ ๋ฉ์ธ ๋ธ๋์น์ ๋ณํฉํ๊ธฐ ์ ์ ์ฝ๋ ๋ฆฌ๋ทฐ๋ฅผ ํ์๋ก ํ์ฌ ํ๋ก์ธ์ค๋ฅผ ์๋ํํ์ธ์.
์ฝ๋ ๋ฆฌ๋ทฐ ๋ชจ๋ฒ ์ฌ๋ก:
- ์ฝ๋ ํ์ง์ ์ง์ค: ์ ์ฌ์ ์ค๋ฅ, ๋ฒ๊ทธ, ๋ณด์ ์ทจ์ฝ์ ์ ์ฐพ์ต๋๋ค.
- ์ฝ๋ฉ ํ์ค ๊ฐ์ : ์ฝ๋๊ฐ ์๋ฆฝ๋ ์ฝ๋ฉ ํ์ค์ ์ค์ํ๋์ง ํ์ธํฉ๋๋ค.
- ๊ฑด์ค์ ์ธ ํผ๋๋ฐฑ ์ ๊ณต: ๊ฐ์ ์ ์ํ ๊ตฌ์ฒด์ ์ธ ์ ์์ ์ ๊ณตํฉ๋๋ค.
- ๋๊ตฌ๋ก ์๋ํ: ๋ฆฐํฐ์ ์ ์ ๋ถ์ ๋๊ตฌ๋ฅผ ์ฌ์ฉํ์ฌ ๋ฆฌ๋ทฐ ํ๋ก์ธ์ค์ ์ผ๋ถ๋ฅผ ์๋ํํฉ๋๋ค.
- ๋ฆฌ๋ทฐ๋ ๊ฐ๊ฒฐํ๊ฒ ์ ์ง: ํ ๋ฒ์ ๋๋ฌด ๋ง์ ์ฝ๋๋ก ๋ฆฌ๋ทฐ์ด๋ฅผ ์๋ํ์ง ๋ง์ธ์. ์๊ณ ์ง์ค๋ ๋ฆฌ๋ทฐ๊ฐ ๋ ํจ๊ณผ์ ์ ๋๋ค.
6. ์ง์์ ํตํฉ/์ง์์ ๋ฐฐํฌ(CI/CD) ์ค์
CI/CD๋ ๋น๋, ํ ์คํธ, ๋ฐฐํฌ ํ๋ก์ธ์ค๋ฅผ ์๋ํํฉ๋๋ค. ์ธ๊ธฐ ์๋ CI/CD ๋๊ตฌ๋ก๋ Jenkins, CircleCI, Travis CI, GitHub Actions, GitLab CI/CD๊ฐ ์์ต๋๋ค. ๋ชจ๋ ์ฝ๋ ์ปค๋ฐ ์ ํ ์คํธ, ๋ฆฐํ , ์ฝ๋ ๋ถ์์ ์คํํ๋๋ก CI/CD ํ์ดํ๋ผ์ธ์ ๊ตฌ์ฑํ์ธ์. ์ฑ๊ณต์ ์ธ ํ ์คํธ ํ ์คํ ์ด์ง ๋๋ ํ๋ก๋์ ํ๊ฒฝ์ ์ฝ๋๋ฅผ ์๋์ผ๋ก ๋ฐฐํฌํ์ธ์.
์์ (GitHub Actions):
๋ฆฌํฌ์งํ ๋ฆฌ์ `.github/workflows/main.yml` ํ์ผ์ ์์ฑํฉ๋๋ค:
name: CI/CD Pipeline
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Node.js
uses: actions/setup-node@v2
with:
node-version: '16.x'
- name: Install dependencies
run: npm install
- name: Run linting
run: npm run lint
- name: Run tests
run: npm run test
- name: Build project
run: npm run build
- name: Deploy to Production
if: github.ref == 'refs/heads/main'
run: |
# ์ฌ๊ธฐ์ ๋ฐฐํฌ ๋จ๊ณ๋ฅผ ์ถ๊ฐํ์ธ์
echo "Deploying to Production..."
์ด ์ํฌํ๋ก๋ `main` ๋ธ๋์น์ ๋ํ ๋ชจ๋ ํธ์์ ๋ชจ๋ ํ ๋ฆฌํ์คํธ์์ ์คํ๋๋ CI/CD ํ์ดํ๋ผ์ธ์ ์ ์ํฉ๋๋ค. ์์กด์ฑ์ ์ค์นํ๊ณ , ๋ฆฐํ ์ ์คํํ๊ณ , ํ ์คํธ๋ฅผ ์คํํ๊ณ , ํ๋ก์ ํธ๋ฅผ ๋น๋ํ๋ฉฐ, ํ๋ก๋์ ์ ๋ฐฐํฌํฉ๋๋ค(๋ฐฐํฌ ๋จ๊ณ ์์).
7. ๋ชจ๋ํฐ๋ง ๋ฐ ๊ฐ์
QA๋ ์ง์์ ์ธ ํ๋ก์ธ์ค์ ๋๋ค. QA ์งํ(์: ๋ฒ๊ทธ ์, ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง, ํ ์คํธ ์คํ ์๊ฐ)๋ฅผ ์ง์์ ์ผ๋ก ๋ชจ๋ํฐ๋งํ๊ณ ๊ฐ์ ํ ์์ญ์ ์๋ณํ์ธ์. ์ฝ๋ฉ ํ์ค, ํ ์คํธ ์ ๋ต, CI/CD ํ์ดํ๋ผ์ธ์ ์ ๊ธฐ์ ์ผ๋ก ๊ฒํ ํ๊ณ ์ ๋ฐ์ดํธํ์ธ์.
์๋ฐ์คํฌ๋ฆฝํธ QA ์ธํ๋ผ๋ฅผ ์ํ ๋๊ตฌ
- ๋ฆฐํฐ: ESLint, JSHint, Stylelint
- ํ ์คํธ ํ๋ ์์ํฌ: Jest, Mocha, Jasmine, Cypress
- ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง ๋๊ตฌ: Istanbul, Jest (๋ด์ฅ)
- ์ฝ๋ ๋ถ์ ๋๊ตฌ: SonarQube, Code Climate
- CI/CD ๋๊ตฌ: Jenkins, CircleCI, Travis CI, GitHub Actions, GitLab CI/CD
- ์ฝ๋ ๋ฆฌ๋ทฐ ๋๊ตฌ: GitHub, GitLab, Bitbucket
- ์์กด์ฑ ๊ด๋ฆฌ: npm, yarn, pnpm
์ค์ ์ฌ๋ก: ๊ธ๋ก๋ฒ ๊ด์
์ง์ญ๊ณผ ํ์ฌ์ ๋ฐ๋ผ ์๋ฐ์คํฌ๋ฆฝํธ QA์ ๋ํ ์ ๊ทผ ๋ฐฉ์์ด ๋ค๋ฅผ ์ ์์ต๋๋ค. ๋ช ๊ฐ์ง ์๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
- ์ค๋ฆฌ์ฝ ๋ฐธ๋ฆฌ (๋ฏธ๊ตญ): ์๋ํ๋ ํ ์คํธ์ CI/CD ํ์ดํ๋ผ์ธ์ ๊ฐ์กฐํฉ๋๋ค. ์ข ๋จ ๊ฐ ํ ์คํธ๋ฅผ ์ํด Cypress์ ๊ฐ์ ๊ณ ๊ธ ๋๊ตฌ๋ฅผ ์์ฃผ ํ์ฉํฉ๋๋ค. ์ ์์ผ ๋ฐฉ๋ฒ๋ก ์ด ๋๋ฆฌ ํผ์ ธ ์์ต๋๋ค.
- ๋ฐฉ๊ฐ๋ก๋ฅด (์ธ๋): ํนํ ์์์์ฑ ํ์ฌ์์ ์๋ ํ ์คํธ์ ์ค์ ์ ๋ก๋๋ค. Selenium ๋ฐ Cypress์ ๊ฐ์ ์๋ํ๋ ํ ์คํธ ํ๋ ์์ํฌ์ ์ฑํ์ด ์ฆ๊ฐํ๊ณ ์์ต๋๋ค.
- ๋ฐ๋ (์๊ตญ): ์๋ํ๋ ํ ์คํธ์ ์๋ ํ ์คํธ๋ฅผ ํผํฉํ ๊ท ํ ์กํ ์ ๊ทผ ๋ฐฉ์์ ์ทจํฉ๋๋ค. Cucumber์ ๊ฐ์ ๋๊ตฌ๋ฅผ ์ฌ์ฉํ BDD(ํ๋ ์ฃผ๋ ๊ฐ๋ฐ)๋ฅผ ์ฑํํฉ๋๋ค. ์ ๊ทผ์ฑ ํ ์คํธ๋ฅผ ๊ฐ๋ ฅํ๊ฒ ๊ฐ์กฐํฉ๋๋ค.
- ๋ฒ ๋ฅผ๋ฆฐ (๋ ์ผ): ์ฝ๋ ํ์ง๊ณผ ์ ์ง๋ณด์์ฑ์ ์ค์ ์ ๋ก๋๋ค. SonarQube์ ๊ฐ์ ์ ์ ๋ถ์ ๋๊ตฌ์ ์ฒ ์ ํ ์ฝ๋ ๋ฆฌ๋ทฐ๋ฅผ ๊ฐ์กฐํฉ๋๋ค.
- ๋์ฟ (์ผ๋ณธ): ์ข ์ข ๋ ๊ตฌ์กฐํ๋๊ณ ๊ณต์์ ์ธ ์ํํธ์จ์ด ๊ฐ๋ฐ ์ ๊ทผ ๋ฐฉ์์ ์ทจํฉ๋๋ค. ์์ธํ ๋ฌธ์ํ์ ์๊ฒฉํ ํ ์คํธ ํ๋ก์ธ์ค๊ฐ ํน์ง์ ๋๋ค.
์ด๋ ์ผ๋ฐ์ ์ธ ๊ด์ฐฐ์ด๋ฉฐ ๊ฐ ์ง์ญ ๋ด์ ๋ชจ๋ ํ์ฌ์ ์ ์ฉ๋๋ ๊ฒ์ ์๋ ์ ์์ต๋๋ค. ๊ทธ๋ฌ๋ ์ด๋ ์ ์ธ๊ณ์ ์ผ๋ก ๋ค์ํ ์๋ฐ์คํฌ๋ฆฝํธ QA ์ ๊ทผ ๋ฐฉ์์ ๋ณด์ฌ์ค๋๋ค.
๊ณผ์ ๊ทน๋ณตํ๊ธฐ
๊ฒฌ๊ณ ํ QA ์ธํ๋ผ๋ฅผ ๊ตฌ์ถํ๋ ๋ฐ์๋ ์ด๋ ค์์ด ๋ฐ๋ฆ ๋๋ค:
- ์์ ๋ถ์กฑ: ํ ์คํธ์ QA์ ์ถฉ๋ถํ ์๊ฐ๊ณผ ์์์ ํ ๋นํ๋ ๊ฒ.
- ๋ณํ์ ๋ํ ์ ํญ: ๊ฐ๋ฐ์๋ค์ด ์๋ก์ด ๋๊ตฌ์ ํ๋ก์ธ์ค๋ฅผ ์ฑํํ๋ ๋ฐ ์ ํญํ ์ ์์ต๋๋ค.
- ๋ณต์ก์ฑ: CI/CD ํ์ดํ๋ผ์ธ์ ์ค์ ํ๊ณ ์ ์งํ๋ ๊ฒ์ด ๋ณต์กํ ์ ์์ต๋๋ค.
- ์งํํ๋ ๊ธฐ์ : ์ต์ ์๋ฐ์คํฌ๋ฆฝํธ ํ๋ ์์ํฌ์ ๋๊ตฌ๋ฅผ ๋ฐ๋ผ์ก๋ ๊ฒ.
- ํ ์คํธ ์ปค๋ฒ๋ฆฌ์ง ์ ์ง: ๊ธฐ๋ฅ์ด ๋ฐ์ ํจ์ ๋ฐ๋ผ ํ ์คํธ๊ฐ ์ ๋ฐ์ดํธ๋๋๋ก ๋ณด์ฅํ๋ ๊ฒ.
์ด๋ฌํ ๊ณผ์ ๋ฅผ ๊ทน๋ณตํ๊ธฐ ์ํด์๋ ๋ค์์ด ํ์์ ์ ๋๋ค:
- QA ์ฐ์ ์์ ์ง์ : QA๋ฅผ ์ฐ์ ์์๋ก ์ผ๊ณ ์ถฉ๋ถํ ์์์ ํ ๋นํฉ๋๋ค.
- ๊ต์ก ์ ๊ณต: ๊ฐ๋ฐ์๋ค์๊ฒ ์ต์ ๋๊ตฌ์ ํ๋ก์ธ์ค์ ๋ํ ๊ต์ก์ ์ ๊ณตํฉ๋๋ค.
- ์๊ฒ ์์ํ๊ธฐ: ๊ธฐ๋ณธ์ ์ธ QA ์ธํ๋ผ๋ก ์์ํ์ฌ ์ ์ฐจ ํ์ฅํฉ๋๋ค.
- ๋ชจ๋ ๊ฒ์ ์๋ํํ๊ธฐ: ์๋ ๋ ธ๋ ฅ์ ์ค์ด๊ธฐ ์ํด ๊ฐ๋ฅํ ํ ๋ง์ด ์๋ํํฉ๋๋ค.
- ํ์ง ๋ฌธํ ์กฐ์ฑ: ๊ฐ๋ฐ์๋ค์ด ์ฝ๋ ํ์ง์ ๋ํ ์ฃผ์ธ์์์ ๊ฐ๋๋ก ์ฅ๋ คํฉ๋๋ค.
์คํ ๊ฐ๋ฅํ ํต์ฐฐ๋ ฅ๊ณผ ๊ถ์ฅ ์ฌํญ
์ฑ๊ณต์ ์ธ ์๋ฐ์คํฌ๋ฆฝํธ QA ์ธํ๋ผ๋ฅผ ๊ตฌ์ถํ๊ธฐ ์ํ ๋ช ๊ฐ์ง ์คํ ๊ฐ๋ฅํ ํต์ฐฐ๋ ฅ๊ณผ ๊ถ์ฅ ์ฌํญ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
- ๊ธฐ๋ณธ๋ถํฐ ์์ํ๊ธฐ: ์ฝ๋ฉ ํ์ค, ๋ฆฐํ , ๋จ์ ํ ์คํธ ์ค์ ์ ์ง์คํฉ๋๋ค.
- ์กฐ๊ธฐ์ ์๋ํํ๊ธฐ: ๊ฐ๋ฅํ ํ ๋นจ๋ฆฌ CI/CD ํ์ดํ๋ผ์ธ์ ์ค์ ํฉ๋๋ค.
- ๊ต์ก์ ํฌ์ํ๊ธฐ: ๊ฐ๋ฐ์๋ค์ด QA ๋๊ตฌ๋ฅผ ํจ๊ณผ์ ์ผ๋ก ์ฌ์ฉํ ์ ์๋๋ก ํ์ํ ๊ต์ก์ ์ ๊ณตํฉ๋๋ค.
- ์งํ ์ํฉ ์ธก์ ํ๊ธฐ: QA ์งํ๋ฅผ ์ถ์ ํ๊ณ ๊ฐ์ ํ ์์ญ์ ์๋ณํฉ๋๋ค.
- ์ ์์ผ ์์น ์์ฉํ๊ธฐ: ์ ์์ผ ๊ฐ๋ฐ ํ๋ก์ธ์ค์ QA๋ฅผ ํตํฉํฉ๋๋ค.
- ๊ธ๋ก๋ฒ ๋งฅ๋ฝ ๊ณ ๋ คํ๊ธฐ: ๊ธ๋ก๋ฒ ํ๊ณผ ๋ชฉํ ๊ณ ๊ฐ์ ํน์ ์๊ตฌ์ ๊ณผ์ ์ ๋ง๊ฒ QA ์ ๋ต์ ์กฐ์ ํฉ๋๋ค.
๊ฒฐ๋ก
๊ฒฌ๊ณ ํ QA ์ธํ๋ผ๊ฐ ๋ท๋ฐ์นจ๋๋ ์ ์ ์๋ ์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋ ๊ด๋ฆฌ ํ๋ ์์ํฌ๋ ํ์ฅ ๊ฐ๋ฅํ๊ณ ์ ์ง๋ณด์ ๊ฐ๋ฅํ๋ฉฐ ๊ณ ํ์ง์ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ถํ๋ ๋ฐ ํ์์ ์ ๋๋ค. ์ด ๊ฐ์ด๋์ ์ค๋ช ๋ ๊ดํ, ๋๊ตฌ, ์ ๋ต์ ๊ตฌํํจ์ผ๋ก์จ ์ฝ๋ ํ์ง์ ํฅ์์ํค๊ณ ๋ฒ๊ทธ๋ฅผ ์ค์ด๋ฉฐ ๊ฐ๋ฐ ํ๋ก์ธ์ค๋ฅผ ๊ฐ์ํํ ์ ์์ต๋๋ค. QA๋ ์ง์์ ์ธ ํ๋ก์ธ์ค์ด๋ฉฐ ํ๋ก์ ํธ์ ํ์ ๋ณํํ๋ ์๊ตฌ์ ๋ง์ถฐ ์ง์์ ์ธ ๋ชจ๋ํฐ๋ง, ๊ฐ์ , ์ ์์ด ํ์ํ๋ค๋ ๊ฒ์ ๊ธฐ์ตํ์ธ์. ํ์ง์ ์ฐ์ ์ํ๊ณ ์๋ํ๋ฅผ ์์ฉํจ์ผ๋ก์จ ์ฅ๊ธฐ์ ์ผ๋ก ์๋ฐ์คํฌ๋ฆฝํธ ํ๋ก์ ํธ์ ์ฑ๊ณต์ ๋ณด์ฅํ ์ ์์ต๋๋ค.