ํ๋ก๊ทธ๋ผํผ 6๊ธฐ nodejs ์คํฐ๋ ๋ ํฌ์
๋๋ค. ์ด ์คํฐ๋๋ Typescript์ express๋ฅผ ํ์ฉํ ์๋ฒ ์ ํ๋ฆฌ์ผ์ด์
์ ์์ ๋ชฉํ๋ก ํ๋ ์๋ฒ ๊ธฐ์ด ์คํฐ๋์
๋๋ค. node ๋ฒ์ ผ์ latest LTS์ธ v12(์์ฑ์ผ ๊ธฐ์ค v12.16.1)์ ์ฌ์ฉํฉ๋๋ค. ์ปดํจํฐ์ ์๋ค๋ฉด ๋ฏธ๋ฆฌ ์ค๋นํด์ค๊ธฐ ๋ฐ๋๋๋ค. ์ค์น ๋ฐฉ๋ฒ์ ๊ฐ OS๋ณ๋ก ๋ค๋ฅด๋ nodejs ๊ณต์ ํํ์ด์ง์์ ํ์ธํ์ ์ค์นํ๋ฉด ๋ฉ๋๋ค. ์๋ํฐ๋ ๊ตญ๋ฃฐ vscode๋ฅผ ๊ธฐ์ค์ผ๋ก ์งํํฉ๋๋ค.
NodeJS ๊ฐ๋ฐ ํ๊ฒฝ์ ์ฌ์ค nodejs๋ง ์ค์นํ๋ฉด ๋์
๋๋ค.๊ผญ ๋ธ๋ก๊ทธ ๋ฐ๋ผํ๋ฉด ์คํ ์๋จ ๋ง๋ฅ ๋ฐ๋ผ ์ค์นํ๋ฉด ๋๋๋ผ. ๋ณด๋ค๋ ์ ์ค์นํ๋์ง ์๊ณ ์ค์น๋ฅผ ํ๋ฉด ์ข์ต๋๋ค. ์ด ์คํฐ๋์์๋ ๊ธฐ๋ณธ node์์ ๋์๊ฐ ํ์
์ ์ ์ฉํฉ๋๋ค. ๋ค์์ ์คํฌ๋ฆฝํธ ์์๋๋ก Typescript, eslint, jest ๊ฐ๊ฐ ์ค์นํฉ๋๋ค.
ํ์ ์คํฌ๋ฆฝํธ(Typescript)๋ nodejs์์ ํ์ ์ ์ฌ์ฉํ ์ ์๊ฒ ๋์์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค. javascript ์ฝ๋ฉ ์ค์๋ ํ์ ์ ๋ํด์ ์ ๊ฒฝ์ธ ํ์๊ฐ ์๊ธฐ ๋๋ฌธ์ ์์ ๋ฅผ ๋๋ผ๋ฉฐ ๋ง๊ตฌ๋ง๊ตฌ ์์ฑํ ์ ์์ง๋ง, ์์ฑํ๋ค๋ณด๋ฉด ๋ฐํ์์์ ๋ฐ์ํ ์๋ฌ๋ฅผ ์ก๋ ๋ฐ์ ์์ฒญ๋ ์๊ฐ์ ์๋ ๋์ฐธ์ฌ๋ฅผ ๊ฒฝํ ํ ์ ์์ต๋๋ค.(ํนํ ์คํ๋ก ๋ฐ์ํ๋ ์ค๋ฅ) ํ์ ์ ์ถ๊ฐํ๋ฉด, ์ฝ๋ ์์ฑ ๋จ๊ณ์์ ๋ฐํ์์ ๋ฐ์ํ ์ ์๋ ์ค๋ฅ๋ฅผ ๋ฏธ๋ฆฌ ๊ฐ์งํ ์ ์์ต๋๋ค. ํ์ ์ด ์ด๊ฑธ ํด๋ ๋๋ค. ์ฐธ๊ณ ๋ก ํ์ ์คํฌ๋ฆฝํธ ๊ณต์๋ฌธ์์์ ๋ณด๋ฉด static type checker๋ผ๊ณ ํํํ๊ณ ์์ต๋๋ค.
ํ์
์คํฌ๋ฆฝํธ๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด, ์ปดํ์ผ๋ฌ ์ญํ ์ ํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ฐ์์ผ ํฉ๋๋ค. Typescript๋ฅผ ๋ฐ์์ฃผ์ธ์. ์ค์น ํ์๋ ์ปดํ์ผ ์ค์ ํ์ผ์ธ tsconfig.json์ ์์ฑํ๋ฉด ์ค๋น๊ฐ ๋๋ฉ๋๋ค.
# typescript ์ค์น
npm i --save-dev typescript
# tsconfig.json ์ด๊ธฐ ํ์ผ ์์ฑ
npx tsc --init
# ์ปดํ์ผ
npx tsc์ฝ๋ ์์
์ ์๋ฐ์คํฌ๋ฆฝํธ ํ์ฅ์์ธ .js ๋์ ์ .ts๋ฅผ ์ฌ์ฉํฉ๋๋ค. .ts ํ์ผ์ ์ฝ๋๋ฅผ ์์ฑํ ํ์ npx tsc ๋ช
๋ น์ด๋ฅผ ์ด์ฉํ๋ฉด .jsํ์ผ๋ก ์ปดํ์ผ ํ ์ ์์ต๋๋ค. ์ฅ? ์ด์ฌํ ํ์
์คํฌ๋ฆฝํธ๋ก ์ผ๋๋ฐ ์ ๋ค์ ์๋ฐ์คํฌ๋ฆฝํธ๋ก ๋์๊ฐ์ฃ ? ํ๊ณ ์๋ฌธ์ ์ ๋๋ ์๋ ์๋๋ฐ, ํ์
์คํฌ๋ฆฝํธ๋ ์ฝ๋ ์์ฑ์ ๋์์ ์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ณ ๊ฒฐ๊ตญ ๋ฐํ์์ nodeJS ์
๋๋ค. ๊ทธ๋์ ์๋ฐ์คํฌ๋ฆฝํธ๋ก ๋์๊ฐ์ผํฉ๋๋ค.์ฝ๋๋ฅผ ๊น๋ํ๊ฒ ์ฌ์ฉํ๊ธฐ ์ํด ํ์
์คํฌ๋ฆฝํธ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์
๋๋ค.
ํ์
์คํฌ๋ฆฝํธ๋ฅผ ์ฐ๋ค๋ณด๋ฉด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ชป์ฐพ๋ ์ค๋ฅ๊ฐ ๋ฐ์ํฉ๋๋ค. ์์ฃผ ์์๋ ์๋ฒ ํ๋ ์์ํฌ์ธ express๋ฅผ ์ธ ๋๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ชป์ฐพ์ ์ ์์ต๋๋ค. ์ด๋ด ๋๋ @types/express ๋ฅผ ์ค์นํ๋ฉด ์ฌ์ฉํ ์ ์์ต๋๋ค. @types/ ๋ค์ ์ฌ์ฉํ๋ ค๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ด๋ฆ์ ์จ์ ๋ฐ์ผ๋ฉด ํด๋น ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ํ์ดํ ํ์ผ์ ๋ฐ์ ์ ์์ต๋๋ค. ํ์ดํ ํ์ผ์ ์๋ฐ์คํฌ๋ฆฝํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํ์
์คํฌ๋ฆฝํธ ์ฒ๋ผ ์ฌ์ฉํ ์ ์๊ฒ ๋์์ค๋๋ค. @types/ ๋ก ์์ํ๋ ๊ฒ์ ํ์
์คํฌ๋ฆฝํธ์์ ๊ณต์์ง์ํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก ๋ณด์๋ฉด๋ฉ๋๋ค. ์ต๊ทผ์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค ๋ง๋ค ํ์ดํ์ ์ ๊ณตํด์ ๋ฐ์ง ์์๋ ์ฌ์ฉ๊ฐ๋ฅํ๊ธฐ๋ ํฉ๋๋ค. ๊ฐํน๊ฐ๋ค ์์ ํ์ดํ์ด ์ ๊ณต๋์ง ์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ด ์๋๋ฐ, ํ์
์คํฌ๋ฆฝํธ ์ฌ์ฉ์์๋ ํด๋น ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์์ฐ๊ธฐ๋ฅผ ์ ๊ทน ๊ถ์ฅํฉ๋๋ค. ๊ฐ์ ๊ธฐ๋ฅ์ ํ๋ ๋ค๋ฅธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค. ์๋๋ฉด, ์ง์ ํ์ดํ ํ์ผ์ ๋ง๋ค์ด์ ์ฌ์ฉํ ์ ์์ต๋๋ค. **.d.ts๋ผ๋ ๋ค์ด๋ฐ์ผ๋ก ์ฌ์ฉํฉ๋๋ค. ํ์ดํ์ ์ง์ ๋ง๋๋ ๋ฐฉ๋ฒ์ ํ์
์คํฌ๋ฆฝํธ ๊ณต์ ํํ์ด์ง์์ ์ฐพ์๋ณด์ธ์.
js์ ts์ ์ฝ๋ ์คํ์ผ์ ๋น๊ตํด๋ด ๋๋ค. ๋ค์์ ๊ณ์ฐ๊ธฐ ๊ธฐ๋ฅ์ ๊ฐ์ง๊ณ ์๋ ๊ธฐ๋ณธ์ ์ธ Calculator ํด๋์ค๋ฅผ ์ฌ์ฉํด์ ํ๋ฉด์ ๊ฒฐ๊ณผ๊ฐ์ ์ถ๋ ฅํ๋ ์์ ์ ๋๋ค.
class Calculator {
add(a, b) {
return a + b;
}
privateSubtract(a, b) {
return a - b;
}
}
const calculator = new Calculator();
console.log(calculator.add(1, 2)); // 1. ์ ์ ์คํ
console.log(calculator.privateSubtract(2, 1)); // 2. ์ ์ ์คํ
console.log(calculator.multiply(3, 4)); // 3. ๋ฐํ์์ ์ค๋ฅ ๋ฐ์ - ์๋ ๋ฉ์๋
console.log(calculator.add('a', 'b')); // 4. ๋ฐํ์์ ์ค๋ฅ ๋ฐ์ - 'ab' ๋ฆฌํด์๋ฐ์คํฌ๋ฆฝํธ๋ ์คํํ๊ธฐ ์ ๊น์ง ์ค๋ฅ๊ฐ ๋ ์ง ํ์ธ์ด ํ๋ญ๋๋ค. eslint๋ฅผ ์ฌ์ฉํ์ ๋ ํํธ๋ฅผ ์ป์ ์๋ ์์ต๋๋ค๋ง, ๋ฐํ์์์์์ ์ค๋ฅ๋ฅผ ๋ชจ๋ ์ก๊ธฐ์ ํฑ์์ด ๋ถ์กฑํฉ๋๋ค.
class Calculator {
public add(a: number, b: number) {
return a + b;
}
private subtract(a: number, b: number) {
return a - b;
}
}
const calculator = new Calculator();
console.log(calculator.add(1, 2)); // 1. ์ ์ ์คํ
console.log(calculator.subtract(2, 1)); // 2. ์ฝ๋์ ์ค๋ฅ๋ก ๊ฑธ๋ฆผ - private ๋ฉ์๋ ์ ๊ทผ ๋ถ๊ฐ
console.log(calculator.multiply(3, 4)) // 3. ์ฝ๋์ ์ค๋ฅ๋ก ๊ฑธ๋ฆผ - ์๋ ๋ฉ์๋
console.log(calculator.add('a', 'b')); // 4. ์ฝ๋์ ์ค๋ฅ๋ก ๊ฑธ๋ฆผ - ํ๋ผ๋ฏธํฐ ํ์
๋ถ์ผ์นํ์ ์คํฌ๋ฆฝํธ๋ก ์์ ์ ํ๋ฉด ๊ฐ ๋ฉ์๋์ ๋ฐ์ ๋ณ์์ ํ์ ์ ์ง์ ํ ์ ์์ต๋๋ค. ๋ํ ์ ๊ทผ์ง์ ์(public, private, protected)๋ฅผ ์ง์ ํ ์ ์์ด์ ์บก์ํ๋ฅผ ๊ตฌํํ ์ ์์ต๋๋ค. ์ฝ๋ ์์์ ๋ฐ์ํ ์ ์๋ ์๋ฌ๋ฅผ ๋ฏธ๋ฆฌ ์๋ ค์ฃผ๊ธฐ ๋๋ฌธ์ ์ฝ๋์ ์๋ฌ์จ์ด ํ์ ํ ์ค์ด๋๋ ๊ฒ์ ๊ฒฝํํ ์ ์์ต๋๋ค. ์ฒ์ ์ฝ๋ฉ์ ์ ํ๋ ์ฌ๋๋ค์๊ฒ ํ์ ์ ๋ํด์ ์ดํดํ๋ ๊ฒ๋ถํฐ ํ๋ ์ ๋ค์ด ์๊ฒ ์ง๋ง, ์ด๋์ ๋ ์ต์ํด์ง๊ณ ๋์๋ ์ฝ๋ฉ์ด ๊ต์ฅํ ํธํด์ง๋๋ค.
๋ฏธ๋ฆฌ ์๋ฌ๊ฐ ๋๋ ๊ฒ์ ๋ฐฉ์งํด์ฃผ๋ typescript๋ ์์ง๋ง, ์ฝ๋ ์คํ์ผ์ ์์๊ฒ ๋ง๋ค์ด์ฃผ๋ ํด๋ ์์ต๋๋ค. lint๋ ์ฝ๋ ์คํ์ผ ๊ท์น์ ์ ํด์, ๊ท์น์ ๋ง์ง ์์ผ๋ฉด ๋ง์ง ์์ ๊ฒ์ ์๋ ค์ฃผ๊ฑฐ๋ npx eslint --fix ๋ช
๋ น์ด๋ฅผ ํตํด ํ๋ฒ์ ์ฝ๋์ ์ผ๊ด์ฑ์ ์ ์งํ ์ ์๋ ๊ฐ๋ ฅํ ๋๊ตฌ ์
๋๋ค. ์ ฏ๋ธ๋ ์ธ ์๋ํฐ(์๋๋ก์ด๋ ์คํ๋์ค, ์ธํ
๋ฆฌ์ ์ด ๋ฑ)์ ์ฌ์ฉํ์๋ ๋ถ๋ค์ด๋ผ๋ฉด Ctrl + Alt + L์ ์ฌ์ฉํ๋ ๊ฒ์ด๋ผ๊ณ ์๊ฐํ๋ฉด ๋ฉ๋๋ค.(์ ฏ๋ธ๋ ์ธ ์ฝ๋์คํ์ผ์ด ์์) ์ด ์คํฐ๋์์๋ VSCODE๋ฅผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์, lint๋ฅผ ๋์์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค์น๊ฐ ํ์ํฉ๋๋ค. eslint์ prettier๋ฅผ ๋ฐ์์ฃผ์ธ์.
# eslint, prettier ์ค์น
npm install --save-dev eslint prettier
# eslint ์ด๊ธฐํ
npx eslint --init
# ์๋ ํฝ์ค
npx eslint --fix
๋งค๋ฒ cli ๋ช ๋ น์ด๋ก ์์ ํ๊ธฐ ๊ท์ฐฎ๋ค๋ฉด, vscode ์ต์คํ ์ ์ ์ค์นํ์ฌ ํ์ผ ์ ์ฅ์์ ์๋์ผ๋ก ๋ง์ถ ์๋ ์์ต๋๋ค.
์ด ์คํฐ๋์์๋ ์ด๋ฏธ ๊ฐ๋ฐ๋ ๊ธฐ๋ฅ์ ๋ํ ํ ์คํธ๋ฅผ ์งํํฉ๋๋ค. ํ ์คํธ๋ฅผ ์๊ณ ์ฌ์ฉํ์ ๋์ ์ฅ์ ์ ์ฒซ๋ฒ์งธ ๋๋ฒ๊น ์ด ํจ์ฌ ์ฉ์ดํด์ง๋๋ค. ํฌ์คํธ๋งจ์ผ๋ก ๋ ๋ ค๋ณด๊ณ ๋ฐ์ดํฐ ํ์ธํ๊ณ , ์๋ฒ ๋ก๊ทธ ์ฐ์ด์ ์ด๋์ ๋ฐ์ดํฐ๊ฐ ์ด์ํด์ง๋์ง ํ์ธํ๊ณ ์ด๋ฐ ๊ณผ์ ์ ํ ํ์๊ฐ ์์ต๋๋ค.(console.log ์๋ ~) ๋์งธ๋ก ๊ธฐ๋ฅ์ ์ถ๊ฐํ๊ฑฐ๋ ๋ฒ๊ทธ๋ฅผ ์์ ํ์ ๋, ์์ ์ด ๋ณ๋ ๋ฒ๊ทธ๋ฅผ ๋ง์ ์ ์์ต๋๋ค. ๋ฒ๊ทธ๋ฅผ ์์ ํ๋ค๋ณด๋ฉด ์๋๋ ์ฝ๋์์ ๋ฒ๊ทธ๊ฐ ๋ฐ์ํ๋ ํ์์ ๊ฒฝํํ๊ธฐ๋ ํ๋๋ฐ, ๋ฐฐํฌ ํ๊ธฐ ์ ์ ๋ฒ๊ทธ๊ฐ ์๋์ง ํ์ธํ ์๊ฐ ์์ต๋๋ค. ์ธ๋ฒ์งธ๋ก TDD(ํ ์คํธ ์ฃผ๋ ๊ฐ๋ฐ)์ ๋ ๊ฐ๊น์์ง ์ ์์ต๋๋ค.
์ข ๋ ์ฒ ์ ํ e2e(์๋ ํฌ ์๋, ์๋ฒ์ ์์ฒญ์ ๋ํ ์๋ต์ ํ ์คํธ) ํ ์คํธ๋ฅผ ๋ฏธ๋ฆฌ ์์ฑํด๋๊ณ ๊ฐ๋ฐ์ ํ๋ฉด ํ๋ก ํธ์๋(์ฑ, ์น)์์๋ ์ด๋ป๊ฒ ์์ฒญ์ ๋ณด๋ด๊ณ ์๋ต์ด ์ด๋ป๊ฒ ๋์ค๋์ง๋ฅผ ๋ฏธ๋ฆฌ ์ ์ ์๊ธฐ ๋๋ฌธ์ ํ๋ก ํธ์์ ์๋ฒ๊ฐ ์์ฑ๋๊ธฐ ๊ธฐ๋ค๋ฆฌ๋ ๊ฐ๋ฐ ๊ณต๋ฐฑ์ ์์จ ์ ์์ต๋๋ค.
์ด ์คํฐ๋์์๋ ํ
์คํธ ํ๋ ์์ํฌ๋ก ํ์ด์ค๋ถ์์ ๊ฐ๋ฐํ jest๋ฅผ ์ฌ์ฉํฉ๋๋ค.
# jest, ts-jest ์ค์น
npm install --save-dev jest ts-jest
# jest.config.js ํ์ผ ์์ฑ
npx jest --init
jest.config.js ํ์ผ์ ts-jest ์ค์ ์ ํด์ค์ผ ํ๋๋ฐ, ์ด ํ์ผ์ ๋ณด๊ณ ์์ ํ๋ฉด ๋ ๊ฒ ๊ฐ์ต๋๋ค.
tests ํด๋๋ฅผ ์์ฑํ๊ณ ํ
์คํธ ํ์ผ์ ์์ฑํด์ค๋๋ค. tests/Calculator.spec.ts์ ์์ฑํฉ๋๋ค.
import { Calculator } from '../src/Calculator';
describe('test start', () => {
const calculator = new Calculator();
test('calculator.add', () => {
expect(calculator.add(1, 2)).toBe(3);
});
});์ด์ ํ ์คํธ๋ฅผ ์คํํด ๋ณด์ธ์
# test ์คํ
npx jest๋ช
๋ น์ด ๋ฟ๋ง ์๋๋ผ ํ
์คํธ๋ฅผ vscode์์ ๋ฒํผ์ผ๋ก ์คํ์ํฌ ์ ์์ต๋๋ค. vscode์์ ํ
์คํธ ์คํ ํ๊ฒฝ์ ์ ์ํด์ผํฉ๋๋ค. .vscode/launch.json ๋ฅผ ๋ง๋ค๊ณ ์๋์ ์ฝ๋๋ฅผ ๋ฃ์ด์ฃผ์ธ์.
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Jest All test files",
"program": "${workspaceFolder}/node_modules/.bin/jest",
"args": ["--runInBand"],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"disableOptimisticBPs": true,
"windows": {
"program": "${workspaceFolder}/node_modules/jest/bin/jest",
},
}, {
"type": "node",
"request": "launch",
"name": "Jest Current File",
"program": "${workspaceFolder}/node_modules/.bin/jest",
"args": [
"${fileDirname}/${fileBasenameNoExtension}",
"--config",
"jest.config.js"
],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"disableOptimisticBPs": true,
"windows": {
"program": "${workspaceFolder}/node_modules/jest/bin/jest",
},
}
]
}ํ ์คํธ ๋ฉ์๋๋ ์ด๊ฒ ๋ง๊ณ ๋ ๊ต์ฅํ ๋ค์ํ๊ฒ ์๋๋ฐ, ๋ ๊ถ๊ธํ๋ค๋ฉด jest ํํ์ด์ง๋ฅผ ์ฐธ๊ณ ํด์ฃผ์ธ์
์ด์ ์๋ฒ๋ฅผ ๋ง๋ญ๋๋ค. ํ์ ์ ์ผ์ ๋ ๋์์ด ๋ง์ด ๋๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ด์ฉํด๋ณด๊ฒ ์ต๋๋ค. ์๋์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ ๋ฐ์์ฃผ์ธ์
npm i --save express reflect-metadata routing-controllers class-validator class-transformer
npm i --save-dev @types/express
routing-controllers๋ฌธ์๋ฅผ ๋ณด๋ฉด ๊ฐ๋จํ๊ฒ ๋ฐ๋ผํ ์ ์์ต๋๋ค.
src/app.ts
import express from 'express';
import { useExpressServer } from 'routing-controllers';
const app = express();
useExpressServer(app, {
controllers: [`${__dirname}/controllers/**`]
})
export {
app
}src/index.ts
import { app } from './app';
app.listen(3000, () => {
console.log(`server is running on ${3000}`);
})src/controllers/TodoController.ts
import { BaseController } from './BaseController';
import { JsonController, Get, Param } from 'routing-controllers';
@JsonController('/todos')
export class TodoController extends BaseController {
@Get()
public index() {
return [
{
id: 1,
title: 'first task',
description: 'create express app'
}
]
}
@Get('/:todoId')
public retrieve(@Param('todoId') todoId: number) {
return {
id: todoId,
title: 'new todo title',
description: 'todo description',
}
}
}npx tsc && node dist๋ฅผ ํตํด์ ์๋ฒ ์คํํ๋ ๊ฒ์ ์ ์ ์์ต๋๋ค.
์ด์ ๋ง๋ค์ด์ง ์๋ฒ๋ฅผ ํ
์คํธ ํ๋ ๋ฐฉ๋ฒ์ ์์๋ด
๋๋ค. jest ํ๊ฒฝ์ ๋๋ถ์ด์ supertest ๋ฅผ ์ค์นํ์ฌ ์๋ฒ ํ
์คํธ๋ฅผ ํด๋ณด๊ฒ ์ต๋๋ค.
npm i --save supertest
npm i --save-dev @types/supertesttests/features/Todo.spec.ts
import supertest from 'supertest';
import { app } from '../../src/app';
describe('test Todo', () => {
const client = supertest(app);
test('test index todos', async () => {
const response = await client.get('/todos');
// ๊ธฐ๋ณธ ์ํ์ฝ๋๋ก ํ
์คํธ
expect(response.status).toBe(200);
// ์๋ต ๋ด์ฉ์ด ๋ฐฐ์ด์ธ์ง ๊ตฌ๋ถํ๋ ํ
์คํธ
expect(Array.isArray(response.body)).toBe(true);
})
})์์ ํ ์คํธ ์ฝ๋๋ฅผ ์คํ์์ผ๋ณด๋ฉด ์๋ฒ๊ฐ ์ ์๋ํ๋ ์ง ์ ์ ์์ต๋๋ค. ์ข๋ deepํ๊ฒ ํ ์คํธ๋ฅผ ํ๋ค๋ฉด, ์๋ต์ ๋ด์ฉ์ ์ํ๋ ํค๊ฐ์ด ์ ๋ค์ด ์๋์ง ํ๋จํด๋ณผ ์ ์์ต๋๋ค.
tests/features/Todo.spec.ts
import supertest from 'supertest';
import { app } from '../../src/app';
// ํค๊ฐ์ด ๋ชจ๋ ์๋์ง ๊ฒ์ฌํ๋ ํจ์๋ฅผ ์์ฑ
const assertItem = (item) => {
const expectedKeys = ['id', 'title', 'description'];
Object.keys(item).forEach((key) => {
const idx = expectedKeys.indexOf(key);
if (idx > -1) {
expectedKeys.splice(idx, 1);
}
})
expect(expectedKeys).toBe([]);
}
describe('test Todo', () => {
const client = supertest(app);
test('test index todos', async () => {
const response = await client.get('/todos');
expect(response.status).toBe(200);
expect(Array.isArray(response.body)).toBe(true);
// ๋ฐฐ์ด ๋ด์ ๋ชจ๋ ์์ดํ
์ ๋ํ์ฌ ๊ฒ์ฌ
for (const item of response.body) {
assertItem(item);
}
})
})์ฌ๊ธฐ๊น์ง ์๋ฒ๋ฅผ ํ ์คํธ ํ๋ ๋ฐฉ๋ฒ์ ์์๋ดค์ต๋๋ค.
์๋ฒ๋ฅผ ๋ง๋ค๋ฉด ํด๋ผ์ด์ธํธ(ํ๋ก ํธ)์ ์ฌ๋ฌ ๋ฐ์ดํฐ ํต์ ์ด ์ค๊ฐ์ผํฉ๋๋ค. ์ด ๊ณผ์ ์์ ์ ์ฅํด์ผ๋๋ ๋ฐ์ดํฐ๊ฐ ์๊ธฐ๋๋ฐ ์ ์ฅ์ ์ํด์ Mysql, MariaDB, Postgres, MongoDB ๋ฑ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ฌ์ฉํด์ผ ์ ์ฅ๊ณผ ๋ฐ์ดํฐ ๊ฒ์์ด ์ฉ์ดํด ์ง๋๋ค. ๋ชฉ์ ์ ๋ฐ๋ผ์ RDB ๋๋ NoSQL์ ์ ํํ ์ ์๊ณ , ์ฟผ๋ฆฌ๋ฅผ ํธํ๊ฒ ์ฐ๊ธฐ ์ํด์ ORM์ ์ฌ์ฉํ๊ธฐ๋ ํฉ๋๋ค. ์ด ์คํฐ๋์์๋ ORM์ ์๋์ง๋ง, ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฌ์ฉ์ด ์ฉ์ดํ prisma๋ฅผ ๋ค๋ค๋ด
๋๋ค.
๋ค์์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ค์นํด๋ด ๋๋ค.
prisma๋ ํน์ดํ๊ฒ ์๋์ฒ๋ผ ๋๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆด ๋ฐ๋ก ๋ฐ์์ ์ฌ์ฉํฉ๋๋ค.
npm install --save-dev @prisma/cli
npm install --save @prisma/client์ด์ ๋ชจ๋ธํ์ผ์ ๋ง๋ค์ด๋ด
๋๋ค. url์ ๋ค์ด๊ฐ๋ ๋ด์ฉ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ๊ทผ uri schema๋ก ๋ง๋ญ๋๋ค. mysql://{ID}:{PASSORD}@{ENDPOINT}/{DATABASE} ์ ํ์์ผ๋ก ์์ฑํ ์ ์์ต๋๋ค. ์๋๋ ์์ url์ ํ๊ฒฝ๋ณ์์์ ๊ฐ์ ธ์จ๋ค๋ ์ฝ๋์
๋๋ค.
datasource mysql {
url = env("DB_URL")
provider = "mysql"
}
generator client {
provider = "prisma-client-js"
}
model Todo {
id Int @default(autoincrement()) @id
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @map("updated_at") @updatedAt
title String
description String?
@@map("todos")
}prisma๋ .prisma ํ์์ ํ์ผ์ ๋ง๋ค์ด์ ๋ชจ๋ธ์ ๊ด๋ฆฌํ๊ณ npx prisma migrate save๋ก ๋ง์ด๊ทธ๋ ์ด์
์ ๊ด๋ฆฌํ ์ ์๊ณ , npx prisma migrate up ๊ณผ npx prisma migrate down ์ผ๋ก ์ค์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ฐ์ํ ์ ์์ต๋๋ค.
๋ชจ๋ธ ํ์ผ(.prisma)์ด ์ ์ ๋์ด ์๋ ์ํ์์ npx prisma generate ๋ฅผ ์ฌ์ฉํ์ฌ, ๋ชจ๋ธ ํ์ดํ์ ์์ฑํ ์ ์์ต๋๋ค. ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ ๊ทผํ๋ ํ์ดํ์ด ์์ฑ๋์ด์ ์ฝ๋ฉ์ด ํธํด์ง๋๋ค.
์๋ ์ ๊ฐ์ ๋ฐฉ๋ฒ์ผ๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ๊ทผํ ์ ์์ต๋๋ค.
const client = new PrismaClient();
await client.todo.create({
data: {
title: 'New Todo Item',
description: 'do something!'
},
});import { BaseController } from './BaseController';
import {
JsonController,
Get,
Param,
Post,
BodyParam,
Put,
Delete,
} from 'routing-controllers';
import { PrismaClient } from '@prisma/client';
@JsonController('/todos')
export class TodoController extends BaseController {
private client: PrismaClient;
constructor() {
super();
this.client = new PrismaClient();
}
@Get()
public index() {
return this.client.todo.findMany();
}
@Get('/:todoId')
public retrieve(@Param('todoId') todoId: number) {
return this.client.todo.findOne({ where: { id: Number(todoId) } });
}
@Post()
public async create(
@BodyParam('title') title: string,
@BodyParam('description') description: string
) {
return this.client.todo.create({
data: {
title,
description,
},
});
}
@Put('/:todoId')
public async update(
@Param('todoId') todoId: number,
@BodyParam('title') title: string,
@BodyParam('description') description: string
) {
return this.client.todo.update({
where: { id: Number(todoId) },
data: {
title,
description,
},
});
}
@Delete('/:todoId')
public async delete(@Param('todoId') todoId: number) {
return this.client.todo.delete({ where: { id: Number(todoId) } });
}
}์ค์ ๋ก ๋ฐ์ดํฐ๊ฐ ์ ์ฅ์ด ์ ๋์๋์ง ํ ์คํธ๋ฅผ ์์ฑํด๋ณด๊ฒ ์ต๋๋ค. ์๋ํฌ์ธํธ๋ก ์์ฒญ์ ๋ฃ์์ ๋, ์ค์ ๋ก ๋๋น์ ์ ์ฅ์ด ๋์๋์ง๋ฅผ ํ์ธํ ์ ์์ต๋๋ค.
import supertest from 'supertest';
import { app } from '../../src/app';
import { PrismaClient } from '@prisma/client';
describe('test Todo', () => {
const client = supertest(app);
test('test index todos', async () => {
const response = await client.get('/todos');
expect(response.status).toBe(200);
const actual = await new PrismaClient().todo.findOne({
where: { id: Number(response.body.id) },
});
expect(actual.description).toBe(response.body.description);
expect(actual.title).toBe(response.body.title);
});
});์์์ง์์ผ๋ก ์ฐ๋ ๊ฒ๋ณด๋จ ์ ์ค๋ช ๋์ด ์๋ ๋ธ๋ก๊ทธ๋ฅผ ํตํด ํ์ต์ ํ๋ฉด ์ข๋ ํธํ ๊ฒ ๊ฐ์ต๋๋ค.
- ์ด๋ณด๋ฅผ ์ํ ๋์ปค ์๋ด์ - ๋์ปค๋ ๋ฌด์์ธ๊ฐ?
- ์ด๋ณด๋ฅผ ์ํ ๋์ปค ์๋ด์ - ์ค์นํ๊ณ ์ปจํ ์ด๋ ์คํํ๊ธฐ
- ์ด๋ณด๋ฅผ ์ํ ๋์ปค ์๋ด์ - ์ด๋ฏธ์ง ๋ง๋ค๊ณ ๋ฐฐํฌํ๊ธฐ
๋ค๋ฅธ ๊ฐ์ํ๊ฒฝ์ด๋ ์ดํดํ๋ ๋ฐฉ๋ฒ์ ๊ฐ์๋ฐ, ์ฐ๋ฆฌ๋ ์๋ 3๊ฐ์ง๋ง ํ๋ฉด ๋ฉ๋๋ค.
- Dockerfile ์์ฑ
- ๋์ปค ์ด๋ฏธ์ง ์์ฑ
- ๋์ปค ์ปจํ ์ด๋ ์์ฑ
์ฌ๋ฌ๊ฐ์ง ๋์ปคํ์ผ ๋ช ๋ น์ด๋ค์ด ์๋ค. ๋ด์ฉ์ ๋์ปค ๋ํ๋จผํธ๋ด์์ ํ์ธ์ด ๊ฐ๋ฅํ๋ค. Dockerfile best practice์ ์ฐ์ฝ ์ฝ์ด๋ณด๋ฉด ์ข์ ๊ฒ ๊ฐ๋ค.
์ด ์คํฐ๋์์ ์์ฑํ ๋์ปคํ์ผ์ ๋ณด์
# ์ ์ฃผ์์ ์ธ ์ ์๋ค.
# ๋ค๋ฅธ ์ด๋ฏธ์ง๋ฅผ ์์๋ฐ์ ๊ตฌํํ๋ค. node ๋ฒ์ ผ 12๋ฅผ ๊ฐ์ง๊ณ ์๋ ์ด๋ฏธ์ง๋ฅผ ๋ฐ๋๋ค.
FROM node:12
# ๋์ปคํ์ผ ๋ด์์ ์ฌ์ฉํ๋ ๋ณ์ ์ ์ธ
ARG PROJECT_PATH=/web/service/api
# ํ์ฌ ๋๋ ํ ๋ฆฌ์์ ์ด๋ฏธ์ง๋ก ํ์ผ ๋ณต์ฌ
COPY package.json ${PROJECT_PATH}/package.json
COPY package-lock.json ${PROJECT_PATH}/package-lock.json
# ๋ช
๋ น์ด๋ฅผ ๊ธฐ๋ณธ ์คํํ๋ ์ปจํ
์ด๋ ๋ด๋ถ ๊ฒฝ๋ก ์ค์ (์๊น ARG๋ก ์ค์ ํ ๋ด์ฉ์ ์ด๋ค)
WORKDIR ${PROJECT_PATH}
# ๋ช
๋ น์ด ์คํ
RUN npm install
# ํ๋ก์ ํธ ํ์ผ ๋ณต์ฌ(ADD์ COPY ๋๊ฐ์ง ๋ช
๋ น์ด๊ฐ ๋ณต์ฌ๋ผ๋ ์ญํ ์ ํ๋๋ฐ ๋์ ์ฐจ์ด๋ ๋ฌธ์์์ ํ์ธํด๋ณด์)
COPY . ${PROJECT_PATH}
# ํ๋ก์ ํธ ๋น๋
RUN npm run build
# ๋์ปค ์ธ๋ถ์ ๋ณด์ฌ์ค ์ด ์ปดํจํฐ๊ฐ ์ ๊ณตํ ํฌํธ ์ ์
EXPOSE 3000
# ์ปจํ
์ด๋๊ฐ ์คํ ๋ ๋ ์ฒ์์ ์คํํ ๋ช
๋ น์ด ์ ์ ๊ฐ์ ์ญํ ์ ํ๋ ENTRYPOINT๊ฐ ์๋ค. ๋ฌธ์์์ ๋์ ์ฐจ์ด๊ฐ ๋ฌด์์ธ์ง ์ฐพ์๋ณด์
CMD ["npm", "start"]
์์์ ์์ฑํ ๋ด์ฉ์ ๋น๋ํฉ๋๋ค. ๋น๋๋ docker build๋ผ๋ ๋ช
๋ น์ด๋ก ํ ์ ์์ต๋๋ค. ์๋์ ๊ฐ์ ๋ฐฉ๋ฒ์ผ๋ก ํ ์ ์์ต๋๋ค.
docker build -t {ํ๊ทธ} {Dockerfile ๊ฒฝ๋ก}ํ๊ทธ๋ ์ด๋ฏธ์ง์ ๋ถํ ์ด๋ฆ์ด๋ผ๊ณ ์๊ฐํ๋ฉด ๋ฉ๋๋ค. ์ด๋ฏธ์ง ์ด๋ฆ + ๋ฒ์ ผ์ผ๋ก๋ ๋ช
์ํ ์ ์์ต๋๋ค. simple-server:latest, simple-server:2 ๋ฑ๋ฑ ์ ๋ฐฉ๋ฒ์ผ๋ก ์ด๋ฏธ์ง ์ด๋ฆ๊ณผ ๋ฒ์ ผ์ผ๋ก ๋ฒ์ ผ๊ด๋ฆฌ๋ฅผ ํ ์ ์์ต๋๋ค. ์ด๋ฏธ์ง ์ด๋ฆ์ ์์ฐ๋ ๊ฒฝ์ฐ ๋ฌด์์ ์ด๋ฆ์ ์ง์ด์ค๋๋ค.
ex)
docker build -t simple-server .์ปจํ ์ด๋ ์์ฑ์ run์ผ๋ก ํ ์ ์์ต๋๋ค. ๋ช๊ฐ์ง ์ต์ ์ ์์๋๋ฉด ๋์์ด ๋๊ฒ ๋ค.
--rm: ์ปจํ ์ด๋๊ฐ ๊บผ์ง๋ฉด ๋ฐ๋ก ์ญ์ ํ๋ ์ต์ -v, --volume: ์ปจํ ์ด๋์ ๋๋ ํ ๋ฆฌ์ ์ปดํจํฐ์ ๋๋ ํ ๋ฆฌ๋ฅผ ์ฐ๊ฒฐํ๋ ์ต์ , ํ๊ฒฝ์ ์ปจํ ์ด๋, ํ์ผ์ ์ปดํจํฐ๋ฅผ ์ฌ์ฉํ ์ ์๋ค.-v {์ปดํจํฐ ๊ฒฝ๋ก}:{์ปจํ ์ด๋ ๊ฒฝ๋ก}-p, --publish: ์ปจํ ์ด๋์ ํฌํธ์ ์ปดํจํฐ์ ํฌํธ๋ฅผ ์ฐ๊ฒฐํ๋ ์ต์ ,-p {์ปดํจํฐ ํฌํธ}:{์ปจํ ์ด๋ ํฌํธ}--entrypoint: ์ปจํ ์ด๋ ์คํ์ ๊ธฐ์กด์ Dockerfile์ ์ ์๋CMD๋ENTRYPOINT๋ฅผ Overriding ํ๋ ์ต์ , ์ค์ ์๋ฒ ์คํ์ ์ํ๊ณ ๋ค๋ฅธ ๊ธฐ๋ฅ์ ์ํํ ์ ์๋ค.--name: ์ปจํ ์ด๋์ ์ด๋ฆ์ ๋ถ์ด๋ ์ต์ -it: ์ปจํ ์ด๋๋ฅผ ์คํํ ๋ ํฐ๋ฏธ๋๋ก ์ธํฐ๋์ (๋ก๊ทธ ํ์ธ ๋ฑ) ์ ํ ์ ์๋ค.-d:-it์ ๋ฐ๋, detached ์ต์ , ๋ฐฑ๊ทธ๋ผ์ด๋์์๋ง ๋์๊ฐ๋๋ก ์ค์ ํ๋ค.--link: ์ด๋ฏธ ์๋์ค์ธ ๋ค๋ฅธ ์ปจํ ์ด๋์ ์ฐ๊ฒฐํ๋ ์ต์ , ์ด ์ต์ ์ด ์์ผ๋ฉด ๊ธฐ๋ณธ์ ์ผ๋ก ์ปจํ ์ด๋๋ค ๋ผ๋ฆฌ ์ฐ๊ฒฐ์ด ์๋๋ค.
docker run {image-name}ex)
docker run -it \
-p 3000:3000 \
-v (PWD):/web/service/api \
--name simple-container \
simple-server์ดํ์ ์ปจํ ์ด๋๋ฅผ ์ผ๊ณ ๋๋ ค๋ฉด
# ํ์ฌ ์คํ์ค์ธ ์ปจํ
์ด๋ ๋ชฉ๋ก
docker ps
# ๋ชจ๋ ์ปจํ
์ด๋ ๋ชฉ๋ก
docker ps -a
# ์ปจํ
์ด๋ ์ข
๋ฃ(๊ฐ์ ์ปดํจํฐ ์ข
๋ฃ)
docker stop {์ปจํ
์ด๋ ID ๋๋ ์ด๋ฆ}
# ์ปจํ
์ด๋ ์์(๊ฐ์ ์ปดํจํฐ ์คํ)
docker start {์ปจํ
์ด๋ ID ๋๋ ์ด๋ฆ}
# ์ปจํ
์ด๋ ์ฌ์์
docker restart {์ปจํ
์ด๋ ID ๋๋ ์ด๋ฆ}
# ์ปจํ
์ด๋ ์ญ์
docker rm {์ปจํ
์ด๋ ID ๋๋ ์ด๋ฆ}๋์ปค ์ฌ๋ฌ๊ฐ๋ฅผ ํ๋ฒ์ ์ผ๊ณ ๋ ์ ์๋ค. docker-compose.yml์ ์ฌ์ฉํ๋ฉด ๊ฐ๋ฅํ๋ค. ์คํ์ด๋ ํ์ ๋ง๋ค์ด๋ ๊ฐ๋ฅํ๋ฐ ์ด๋ณด์์ ์ ์ฅ์์ ์ด๊ฒ๋ง์ผ๋ก๋ ๊ฐ๋ฅํ๋ค.
๊ธฐ๋ณธ์ ์ผ๋ก --link ์ต์
์ ์ฌ์ฉํด์ผ ์ปจํ
์ด๋๋ค ๋ผ๋ฆฌ ์ฐ๊ฒฐ์ด ๊ฐ๋ฅํ๋ฐ, ์ปจํ
์ด๋์ ์ข
์์ฑ์ด ๊ผฌ์ด๋ ๊ฒฝ์ฐ๊ฐ ์๊ธธ ์ ์๋ค. ์ด๋ ๋์ปค ๋คํธ์ํฌ๋ฅผ ๋ง๋ค๊ณ ํ๋์ ๋คํธ์ํฌ์ ์ปจํ
์ด๋๋ค์ ์ฐ๊ฒฐํด์ฃผ๋ฉด ๋ค๋ฅธ ์ค์ ์ ํด์ฃผ์ง ์์๋ ์ปจํ
์ด๋๋ผ๋ฆฌ ์ฐ๊ฒฐ์ด ๊ฐ๋ฅํ๋ค.
docker-comnpose.yml ๋ฅผ ์๋์ฒ๋ผ ์์ ํด์ ์จ๋ณด์ธ์
# docker-compose ๋ฒ์ ผ
version: "3.7"
# ์ปจํ
์ด๋ ๋ชฉ๋ก
services:
# ์ปจํ
์ด๋ ์ด๋ฆ ๋ณ์์ฒ๋ผ ์ง์ ๊ณ ๋ฅด๋ฉด ๋๋ค.
db:
# ์ปจํ
์ด๋์ ์ฌ์ฉํ ์ด๋ฏธ์ง
image: mysql:latest
# ํฌํธ ์ฐ๊ฒฐ ์ค์
ports:
- 3306:3306
# ์ปจํ
์ด๋ ์คํ์์ ํ๊ฒฝ๋ณ์ ์ฌ์ฉ
environment:
MYSQL_ROOT_PASSWORD: 1016
MYSQL_DATABASE: sample
# ์ฌ์์ํ๋ ์กฐ๊ฑด
restart: on-failure
# ์ปจํ
์ด๋์ ์ฐ๊ฒฐํ ๋ณผ๋ฅจ ์ฐ๊ฒฐ
volumes:
- ./db/conf.d:/etc/mysql/conf.d
- ./db/data:/var/lib/mysql
# ๋คํธ์ํฌ ์ค์
networks:
default:
ipv4_address: 172.16.2.2
server:
# ์ปจํ
์ด๋์ ์ฌ์ฉํ Dockerfile์ด ์๋ ๊ฒฝ๋ก, image ๋์ ์ ์ฐ๋ฉด, ์๋ ๊ฒฝ์ฐ ์ด๋ฏธ์ง๋ฅผ ์๋์ผ๋ก ๋น๋ํ๊ณ ์ปจํ
์ด๋๋ฅผ ์์ฑํ๋ค.
build: .
ports:
- 3000:3000
environment:
HOST: 0.0.0.0
PORT: 3000
DB_URL: mysql://root:[email protected]/sample
# ๊ธฐ๋ณธ์ ์ผ๋ก ์จ์๋ CMD๋ ENTRYPOINT์ ๋ฎ์ด์ธ ๋ด์ฉ
entrypoint: "npm run dev"
networks:
default:
ipv4_address: 172.16.2.3
networks:
default:
driver: bridge
ipam:
config:
- subnet: 172.16.2.0/24
์์ ํ์ผ์ ๋ฐ์ง๊ณ ๋ณด๋ฉด docker run ๋ช
๋ น์ด๋ฅผ ํ
์คํธ๋ก ์์ฑํด๋๋ค๊ณ ์๊ฐํ๋ฉด ๋๋ค. ์ด์ ์ด ํ์ผ์ด ์์ผ๋ฉด ๋๊ตฌ๋ ์์ฌ์ด ๋ช
๋ น์ด๋ก ๋์ปค ์ปจํ
์ด๋๋ฅผ ์ผ๊ณ ๋ ์ ์๋ค.
# ์ด๋ฏธ์ง๊ฐ ์๋ค๋ฉด ๋น๋ํ ๋จผํ
์ด๋ ์คํ(docker-compose.yml ํ์ผ์ ๋ณํ๊ฐ ์๋ ๊ฒฝ์ฐ ์ปจํ
์ด๋ ์ฌ ์์ฑ)
docker-compose up -d(detached ์ต์
, ์ด๊ฒ ์์ผ๋ฉด ์๋ฒ์ ๋๋น ๋ชจ๋ ์ฝ์์ด ์ผ์ง ์ํ๊ฐ ๋๋ค.)
# ์ปจํ
์ด๋ ์ข
๋ฃ ํ ์ปจํ
์ด๋ ์ญ์ , ์ด๋ฏธ์ง ์ญ์ ๋ ์๋!(build ์ต์
์ ๊ฒฝ์ฐ์๋ง)
docker-compose down
# ์ปนํ
์ด๋๋ง ์ผ๊ณ ๋๊ณ ์ฌ์์
docker-compose start
docker-compose stop
docker-compose restart
# ์ด๋ฏธ์ง ๋น๋๋ง ์คํ
docker-compose build์ด๋ ๊ฒ ํ ์ ์๋ค.
ex)
docker-compose up -d์๋ง์กด์์ ์ ๊ณตํ๋ ElasticBeanstalk๋ฅผ ์ด์ฉํด์ ๋ฐฐํฌ๋ฅผ ํ๋ค. ์ ์ด๊ฑธ ์ฐ๋๋ฉด, ec2 ์ธ์คํด์ค ๋ฐ๊ณ ๊ฐ๋ฐํ๊ฒฝ ์ค์ ํ๊ณ ํฐ๋ฏธ๋๋ก ์ ์ํด์ ์๋ฒ ์คํ ํ๊ณ ๋ฅผ ํ๋๊ฒ ์๋๋ผ ๋ด ์ปดํจํฐ์ ๋ก์ปฌ ํ๊ฒฝ์์ ํฐ๋ฏธ๋ ์ปค๋งจ๋ ํ๋๋ง๋ก ๋ฐฐํฌ๋ฅผ ํ ์ ์๊ธฐ ๋๋ฌธ์ ๋๋ฌด ํธํด์ ์ด๋ค.
eb๋ ์๋ง์กด์ ์ฝ์์์๋ ์์ฑํด์ ์ธ ์ ์๋ค. ํ์ง๋ง cli๋ก ํ๊ธฐ ์ํด์๋ IAM ๊ถํ์ด ํ์ํ๋ค. ๊ถํ์ ์ํด ์ฌ์ฉ์ ์ค์ ์ ํด๋ณด์
-
์ฌ์ฉ์ ์ถ๊ฐ ์๋ฃ -
aws-access-key์aws-secret-key๋ฅผ ๊ธฐ์ตํด๋์(๋ณต์ฌ๋ฅผ ํด๋ ๋๊ณ , csv ํ์ผ์ ๋ฐ์์ ์ ์ฅํ ์ ์๋ค)
eb cli๋ฅผ ๋ก์ปฌํ๊ฒฝ์์ ์ค์นํ๋ ค๋ฉด eb cli ํ๋ก๊ทธ๋จ ์ค์น๊ฐ ํ์ํ๋ค. ์ค์นํ ๋๋ pip๋ฅผ ์ด์ฉํ๋๊ฒ ์ ์ ๊ฑด๊ฐ์ ์ด๋กญ๋ค. ๋๋ ์ฃฝ์ด๋ ํ์ด์ฌ์ ์ค์นํ๊ธฐ ์ซ๋ค๋ฉด, ํ์ด์ฌ์ฉ ๋์ปค ์ปจํ ์ด๋๋ฅผ ๋ง๋ค์ด์ ์ค์ ์ ํ ์๋ ์๋ค. (์ฐธ๊ณ )
-
IAM์์ ํ์ธํ
aws-access-key์aws-secret-key๋ฅผ ์ ๋๋ค.
-
๋ฐฐํฌํ ๋ ํ์ํ ํ์ผ ์ค๋ช
- .ebextensions/**: ๋ฐฐํฌ์ ํ์ํ ํ๊ฒฝ ์ค์ ์ ๊ด๋ฆฌํ๋ ํ์ผ. ํ๊ฒฝ๋ณ์(git์ ํฌํจ๋๋ฉด ์๋๋ค)์ timezone, ๋ชจ๋ํฐ๋ง, ๋ก๋๋ฐธ๋ฐ์ ๋ฑ๋ฑ
- Dockerrun.aws.json: ๋์ปค ์คํ์ ํ์ํ ๋ด์ฉ ์ ์(docker-compose.yml ์ฒ๋ผ ์คํ์ ์ฌ์ฉํ port, volume, ๋ก๊น , ์์ ๋ช ๋ น์ด ๋ฑ์ ์ ์) - ์ฐธ๊ณ : aws๋ฌธ์ - ๋จ์ผ ์ปจํ ์ด๋ Docker ๊ตฌ์ฑ
- .ebignore: ๋ฐฐํฌ์ ์ ์ธ ์ํฌ ํ์ผ์ ์ ์, ์จ๊นํ์ผ(.์ผ๋ก ์์ํ๋ ํ์ผ ๋๋ ํด๋) ๋ด์ฉ์ ์
๋ก๋๊ฐ ์๋๋๋ฐ,
.ebextensions๋ด์ฉ์ ์ฌ์ฉํ๊ธฐ ์ํด.ebignore์ ๋ฑ๋ก๋ ๋ด์ฉ์ ์ ์ธํ๊ณ ๋ชจ๋ ํฌํจ์ํค๊ธฐ ์ํด ์ฌ์ฉํ๋ค.
-
๋ค์ ๋ฐฐํฌํ ๋๋
eb deploy๋ฅผ ์ฐ๋ฉด ๋๋ค. -
์ญ์ ์์๋
eb teminate๋ฅผ ์ฐ๋ฉด ๋๋ค.







