๐ŸฆŠcodecamp 14~15์ผ ์ •๋ฆฌ๋ณธ

JBoBยท2023๋…„ 2์›” 27์ผ
0

๐ŸงNest.js 14์ผ์ฐจ

๐ŸคNest.js ๋ž€?

Nest.js๋Š” ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ง€์›ํ•˜๋Š” ํšจ์œจ์ ์ด๊ณ  ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ๋…ธ๋“œ์˜ ์„œ๋ฒ„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ”„๋ ˆ์ž„์›Œํฌ์ด๋ฉฐ,

OOP,FP,FRP ์š”์†Œ๋ฅผ ๊ฒฐํ•ฉํ•˜๋Š” ํŠน์ง•์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.

[์ถœ์ฒ˜](https://velog.io/@funnysunny08/Nest.js-%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80)

  • ๊ธฐ๋ณธ ์ปจ์…‰์€ Provider,Controller๋ฅผ module๋กœ ํ•ฉ์น˜๊ณ ,๊ทธ ๋ชจ๋“ˆ๋“ค์„ ์ตœ์ข…์ ์œผ๋กœ app.module์— ํ•ฉ์ณ์„œ ์‚ฌ์šฉํ•œ๋‹ค
  • Nestjs๋Š” ์‹ฑ๊ธ€ํ†คํŒจํ„ด์„ ์ง€ํ–ฅํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ธ์Šคํ„ด์Šค๋ฅผ ์ง์ ‘ ์ƒ์„ฑํ•˜์ง€ ์•Š๊ณ  ๋ชจ๋“ˆ์„ ํ†ตํ•ด injectionํ•˜๋Š” ํŒจํ„ด์„ ๊ถŒ์žฅํ•˜๊ณ  ์žˆ๋‹ค.
  • ์‹ฑ๊ธ€ํ†คํŒจํ„ด : ์ „์—ญ ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ๊ฐ์ฒด๋ฅผ ํ•˜๋‚˜๋งŒ ์ƒ์„ฑ ํ•˜๋„๋กํ•˜๋ฉฐ, ์ƒ์„ฑ๋œ ๊ฐ์ฒด๋ฅผ ์–ด๋””์—์„œ๋“ ์ง€ ์ฐธ์กฐ ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ํŒจํ„ด

Controller

: ์ฒ˜์Œ ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋Š” ์ž…๊ตฌ ์—ญํ• ์„ ๋‹ด๋‹นํ•˜๋ฉฐ , ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ๋”ฐ๋กœ ๋ถ„๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ์ง„์ž…์ ์„ ๋”ฐ๋กœ ๋ถ„๋ฆฌํ•ด๋‘” ๊ฒƒ์ด๋‹ค.

์ฆ‰, ์‰ฝ๊ฒŒ ์„ค๋ช…ํ•˜๋ฉด ์ขŒ์„์„ ์•ˆ๋‚ดํ•ด์ฃผ๋Š” ์ง์›์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค.

HTTP Request์— ๋”ฐ๋ผ ์–ด๋– ํ•œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์ ์šฉ ์‹œํ‚ฌ์ง€์— ๋Œ€ํ•œ ์„ค์ •์„ ํ•˜๋ฉด ๋œ๋‹ค,

์žฅ์ 

ํšจ์œจ์„ฑ

NestJS๋Š” ๊ฐœ๋ฐœ์ž์™€ ํŒ€์ด ๊ณ ๋„๋กœ ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅํ•˜๊ณ , ํ™•์žฅ ๊ฐ€๋Šฅํ•˜๋ฉฐ, ๋Š์Šจํ•˜๊ฒŒ ๊ฒฐํ•ฉ๋˜๊ณ  ์œ ์ง€๊ด€๋ฆฌ๊ฐ€ ์‰ฌ์šด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋Š”ย ์ฆ‰์‹œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์•„ํ‚คํ…์ฒ˜๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

๊ทธ ์™ธ์—๋„ย TypeScript๊ธฐ๋ฐ˜์˜ Framework์ด๋ฉฐย Dependency Injection(์˜์กด์„ฑ์ฃผ์ž…),ย Inversion of Control(์ œ์–ด์˜ ์—ญ์ „),ย Module์„ ํ†ตํ•œ ๊ตฌ์กฐํ™” ๋“ฑ ์ƒ์‚ฐ์„ฑ์— ์šฉ์ดํ•ฉ๋‹ˆ๋‹ค.

์•ˆ์ •์„ฑ

NestJS๋Š”ย TypeScript๋ฅผ ์ ๊ทน์ ์œผ๋กœ ๋„์ž…ํ•˜๋ฉด์„œ ์„œ๋ฒ„ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐœ๋ฐœ ์‹œ ๋ฐœ์ƒ ๊ฐ€๋Šฅํ•œ ์˜ค๋ฅ˜๋“ค์„ ์‚ฌ์ „์— ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ์„ธ๋ถ€์ ์ธ Module๋กœ ๋‚˜๋ˆ„์–ด์ ธ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋…๋ฆฝ์ ์ธ Unit Test๋ฅผ ์‰ฝ๊ฒŒ ์ž‘์„ฑ ๊ฐ€๋Šฅํ•˜๋„๋ก ๊ตฌํ˜„๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

ํ™•์žฅ์„ฑ

NestJS๋Š” Module Class๋ฅผ ์ง€์›ํ•˜๋ฉฐ, ๊ฐ Module์€ ๋น„์Šทํ•œ ๊ธฐ๋Šฅ๊ณผ ๊ฐœ๋…๋“ค์„ Class ํ•œ ๊ณณ์— ๋‹ด์•„ ์บก์Šํ™”ํ•˜๊ณ  ์„œ๋กœ Import๊ฐ€ ๊ฐ€๋Šฅํ•˜๋„๋ก ๊ตฌํ˜„๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ Module ๊ตฌ์กฐ๋Š” Architecture๋ฅผย ์กฐ์ง์ (Organize)์œผ๋กœ ๊ฐ€์ ธ๊ฐ€๊ฒŒ ํ•˜๊ณ ย ๋Š์Šจํ•œ ๊ฒฐํ•ฉ(Loose Coupling)์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋งŒ๋“ค์–ดย ํ™•์žฅ์„ฑ(Extensible)๊ณผย ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ์„ฑ(Testable)์„ ๋†’์ž…๋‹ˆ๋‹ค.

**IoC(Inversion of Control, ์ œ์–ด์˜ ์—ญ์ „)**

: ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„œ๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ new๋กœ ์ƒˆ๋กœ์šด ๊ฐ์ฒด๋ฅผ ์ง€์ •ํ•˜์—ฌ ๊ณ„์† ์„ค์ •ํ•ด์ฃผ๊ณ  ๊ฐœ๋ฐœ์ž๊ฐ€ ์ œ์–ด๋ฅผ ํ–ˆ์ง€๋งŒ

Nest ์—์„œ๋Š” Nest ์•ˆ์—์„œ ์ „์— ํ•ด์ฃผ๋˜ ๊ฑฐ๋ฅผ ํ•ด์ฃผ๊ณ  ์žˆ์œผ๋ฏ€๋กœ ์ œ์–ด๊ฐ€ Nest๋กœ ๋„˜์–ด๊ฐ“๋‹ค. ์ฆ‰, ๋ชจ๋“  ์ œ์–ด๊ถŒ์„ ํ”„๋ ˆ์ž„์›Œํฌ์˜ ์ปจํ…Œ์ด๋„ˆ์—๊ฒŒ ๋„˜๊ธฐ๋Š” ๊ฐœ๋…??

Nest.js ์„ค์น˜ํ•˜๊ธฐ

sudo npm i -g @nestjs/cli

yarn add -g @nestjs/cli

cli(command line interface)?? ์‹ ํ˜ธ ์ƒํ˜ธ์ž‘์šฉ
-> ํ‰์†Œ ์šฐ๋ฆฌ ์ปดํ“จํ„ฐ์—์„œ ํด๋” ์•„์ด์ฝ˜์„ ํด๋ฆญํ•ด ๋“ค์–ด๊ฐ€ ์•ˆ์— ์žˆ๋Š” ํŒŒ์ผ๋“ค์„ ์•„์ด์ฝ˜์œผ๋กœ ๋ณด๊ณ , ๋˜ ๊ทธ๊ฒƒ๋“ค์„ ํด๋ฆญํ•ด์„œ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
CLI๋Š” ํ„ฐ๋ฏธ๋„์—์„œ ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•ด ํด๋”๋ฅผ ์—ด๊ณ , ํŒŒ์ผ์„ ์‹คํ–‰ํ•˜๋Š” ๋“ฑ์˜ ์ƒํ˜ธ์ž‘์šฉํ•˜๋Š” ๋ฐฉ์‹ 

nest -version // ์„ค์น˜๊ฐ€ ์™„๋ฃŒ๋ฌ๋Š”์ง€ ๋ฒ„์ „ํ™•์ธ

nest new (์ƒˆํด๋”๋ช…)

ํด๋”์•ˆ ์ˆจ๊ฒจ์ง„ ํŒŒ์ผ ์ œ๊ฑฐํ•˜๊ธฐ
ls -al //(์ˆจ๊ฒจ์ง„ ํŒŒ์ผ๊นŒ์ง€ ์กฐํšŒ)
rm -rf .git // gitํŒŒ์ผ ์ œ๊ฑฐ

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ƒ์„ฑ๋œ aaa ํด๋”์— Nest์— ๋งŽ์€ ํด๋”์™€ ํŒŒ์ผ๋“ค์ด ์ž๋™์œผ๋กœ ์ƒ์„ฑ๋œ๋‹ค. ์ด๋ ‡๊ฒŒ ๊ธฐ๋ณธ์ ์œผ๋กœ ์„ค์น˜ํ•ด์ฃผ๋Š” ๊ฒƒ์„ ๋ณด์ผ๋Ÿฌ ํ”Œ๋ ˆ์ดํŠธ ๋ผ๊ณ  ํ•œ๋‹ค.

  • ๋ณด์ผ๋Ÿฌ ํ”Œ๋ ˆ์ดํŠธ( ์ดˆ๊ธฐ ํด๋” ๊ตฌ์กฐ ) : ์ƒˆ๋กœ์šด ํ”„๋กœ์ ํŠธ๋ฅผ ์‹คํ–‰ํ• ๋•Œ ์ฒ˜์Œ ๋งŒ๋“ค์–ด์ง„ Nest ํด๋” ๊ตฌ์กฐ

๋ชจ๋“  ํšŒ์‚ฌ๊ฐ€ ๊ฐ™์€ ๋ณด์ผ๋Ÿฌ ํ”Œ๋ ˆ์ดํŠธ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค. ํšŒ์‚ฌ์— ๋”ฐ๋ผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ํ•„์š”ํ•œ ๊ธฐ๋Šฅ๋“ค์ด ๋‚ด์žฅ๋˜์–ด ์žˆ๋Š” ๋ณด์ผ๋Ÿฌ ํ”Œ๋ ˆ์ดํŠธ๊ฐ€ ์กด์žฌํ•˜๊ฒŒ ๋˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

eslint vs prettier ์†Œ๊ฐœ

eslint ๋ฅผ ๊ฒ€์ƒ‰ํ•ด์„œ ESLint ๋„ ์„ค์น˜

.prettierrc

// .prettierrc

{
      "singleQuote": true,
      "trailingComma": "all"
}
  • singleQuote : โ€œ โ€œ ๋ฅผ โ€˜ โ€˜ ๋กœ ํ†ต์ผ์‹œ์ผœ์ฃผ๋Š” ๊ฒƒ
  • trailingComma : ๋ฌธ์žฅ ์ œ์ผ ๋์— ์ฝค๋งˆ( , ) ๋ฅผ ์ž๋™์œผ๋กœ ๋ถ™์—ฌ์ฃผ๋Š” ๊ฒƒ

VS Code ์˜ ์„ค์ •(Settings) ์œผ๋กœ ๋“ค์–ด๊ฐ€์„œ format on save ๋ฅผ ๊ฒ€์ƒ‰

์ฒดํฌ๋ฅผ ํ•˜๊ฒŒ ๋˜๋ฉด ์ฝ”๋“œ ์ˆ˜์ • ํ›„ ํŒŒ์ผ์„ ์ €์žฅ ํ•˜๊ฒŒ๋˜์—ˆ์„ ๋•Œ prettier๊ฐ€ ์ ์šฉ๋˜๊ฒŒ ๋˜๋Š”๋ฐ

์šฐ๋ฆฌ๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด ์ฒดํฌ ํ•œ ์‚ฌ๋žŒ๋งŒ ์ ์šฉ๋˜๊ธฐ ๋•Œ๋ฌธ์— ํ˜‘์—…์„ ํ•  ๋•Œ ๋ถˆํŽธํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ, ๋ชจ๋‘๊ฐ€ ์ผ๊ด„ ์ ์šฉ๋  ์ˆ˜ ์žˆ๊ฒŒ Settings ๋ถ€๋ถ„์„ ์ฝ”๋“œ๋กœ ๋งŒ๋“ค์–ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

prettier ์ €์žฅ์‹œ ์ž๋™ํฌ๋ฉงํŒ… ์‹ค์Šต

ํŒŒ์ผ์˜ ์ตœ์ƒ๋‹จ ์œ„์น˜๋กœ ์ด๋™ํ•ด ์ฃผ์„ธ์š”.

์ตœ์ƒ๋‹จ ์œ„์น˜์—์„œ .vscode ํด๋”๋ฅผ ๋งŒ๋“ค์–ด ์ฃผ์„ธ์š”

.vscode ํด๋” ์•ˆ์— settings.json ํŒŒ์ผ์„ ๋งŒ๋“ค์–ด ์ฃผ์„ธ์š”.

settings.json ํŒŒ์ผ ์•ˆ์— VS Code settings ์„ ์ž‘์„ฑํ•ด์ค๋‹ˆ๋‹ค.

// settings.json

{
    "editor.formatOnSave": true,
    "editor.defaultFormatter": "esbenp.prettier-vscode"
}
  • "editor.formatOnSave": true : ์ €์žฅํ•˜๋ฉด ํ”„๋ฆฌํ‹ฐ์–ด๊ฐ€ ๋ฐ”๋กœ ์ ์šฉ๋  ์ˆ˜ ์žˆ๊ฒŒ ์„ค์ •
  • "editor.defaultFormatter": "esbenp.prettier-vscodeโ€ : prettier ํฌ๋ฉง ์ ์šฉ ์„ค์ •

๐Ÿšจย  import ์—์„œ eslintrc ์—๋Ÿฌ๊ฐ€ ๋‚˜ํƒ€๋‚œ๋‹ค๋ฉด eslintrc๊ฐ€ tsconfig.json ํŒŒ์ผ ์œ„์น˜๋ฅผ ์ฐพ์ง€ ๋ชปํ•ด์„œ ๋‚˜ํƒ€๋‚˜๋Š” ์—๋Ÿฌ์ด๋ฏ€๋กœ .eslintrc.js ํŒŒ์ผ์„ ์•„๋ž˜์™€ ๊ฐ™์ด ์ˆ˜์ •ํ•ด์ฃผ์„ธ์š”!

๐Ÿšจย ์—๋Ÿฌ๊ฐ€ ๊ณ„์† ๋ฐœ์ƒ๋œ๋‹ค๋ฉด ํŒŒ์ผ์„ ๊ป๋‹ค๊ฐ€ ๋‹ค์‹œ ์ผœ์ฃผ์„ธ์š”!

// .eslintrc.js

module.exports = {
  parser: '@typescript-eslint/parser',
  parserOptions: {
    project: './tsconfig.json',
		tsconfigRootDir: __dirname,
    sourceType: 'module',
  },
  plugins: ['@typescript-eslint/eslint-plugin'],
  extends: [
    'plugin:@typescript-eslint/recommended',
    'plugin:prettier/recommended',
  ],
  root: true,
  env: {
    node: true,
    jest: true,
  },
  ignorePatterns: ['.eslintrc.js'],
  include: ['.eslintrc.js'],
  rules: {
    '@typescript-eslint/interface-name-prefix': 'off',
    '@typescript-eslint/explicit-function-return-type': 'off',
    '@typescript-eslint/explicit-module-boundary-types': 'off',
    '@typescript-eslint/no-explicit-any': 'off',
  },
};
  • tsconfigRootDir: __dirname ๐Ÿ‘‰๐Ÿปย tsconfig root ํด๋” ์ฃผ์†Œ๋ฅผ ํ˜„์žฌ ์‹คํ–‰์ค‘์ธ ํด๋” ์œ„์น˜์ž„์„ ์•Œ๋ ค์ฃผ๋Š” ๊ฒƒ

๐Ÿšจย  import ์—์„œ eslintrc ์—๋Ÿฌ๊ฐ€ ๋‚˜ํƒ€๋‚œ๋‹ค๋ฉด eslintrc๊ฐ€ tsconfig.json ํŒŒ์ผ ์œ„์น˜๋ฅผ ์ฐพ์ง€ ๋ชปํ•ด์„œ ๋‚˜ํƒ€๋‚˜๋Š” ์—๋Ÿฌ์ด๋ฏ€๋กœ .eslintrc.js ํŒŒ์ผ์„ ์•„๋ž˜์™€ ๊ฐ™์ด ์ˆ˜์ •ํ•ด์ฃผ์„ธ์š”!

๐Ÿšจย ์—๋Ÿฌ๊ฐ€ ๊ณ„์† ๋ฐœ์ƒ๋œ๋‹ค๋ฉด ํŒŒ์ผ์„ ๊ป๋‹ค๊ฐ€ ๋‹ค์‹œ ์ผœ์ฃผ์„ธ์š”!

// .eslintrc.js

module.exports = {
  parser: '@typescript-eslint/parser',
  parserOptions: {
    project: './tsconfig.json',
		tsconfigRootDir: __dirname,
    sourceType: 'module',
  },
  plugins: ['@typescript-eslint/eslint-plugin'],
  extends: [
    'plugin:@typescript-eslint/recommended',
    'plugin:prettier/recommended',
  ],
  root: true,
  env: {
    node: true,
    jest: true,
  },
  ignorePatterns: ['.eslintrc.js'],
  include: ['.eslintrc.js'],
  rules: {
    '@typescript-eslint/interface-name-prefix': 'off',
    '@typescript-eslint/explicit-function-return-type': 'off',
    '@typescript-eslint/explicit-module-boundary-types': 'off',
    '@typescript-eslint/no-explicit-any': 'off',
  },
};
  • tsconfigRootDir: __dirname ๐Ÿ‘‰๐Ÿปย tsconfig root ํด๋” ์ฃผ์†Œ๋ฅผ ํ˜„์žฌ ์‹คํ–‰์ค‘์ธ ํด๋” ์œ„์น˜์ž„์„ ์•Œ๋ ค์ฃผ๋Š” ๊ฒƒ

Schema First(์Šคํ‚ค๋งˆ ์šฐ์„ )

  • Schema first ๋Š” graphql์˜ schema๋ฅผ ๋จผ์ € ์ •์˜ํ•˜๊ณ , ๊ทธ ์ •์˜์— ๋งž๊ฒŒ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋งํ•œ๋‹ค.
  • Schema ์„ ์ž‘์„ฑํ•˜ใ…ฃใ„ฑ ์œ„ํ•ด์„œ๋Š” graphql์˜ data model์„ ๋‚˜ํƒ€๋‚ด๊ธฐ ์œ„ํ•ด ๋งŒ๋“ค์–ด์ง„ SDL(Schema Definiton Language)์„ ์‚ฌ์šฉํ•œ๋‹ค.
GraphQLModule.forRoot({
  typePaths: ['./**/*.graphql'],
}),

//์Šคํ‚ค๋งˆ ์šฐ์„  ์ ‘๊ทผ ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๋จผ์ € ์˜ต์…˜ ๊ฐœ์ฒด์— typePaths ์†์„ฑ์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. 
//typePaths ์†์„ฑ์€ GraphQLModule์ด ์ž‘์„ฑํ•  GraphQL SDL ์Šคํ‚ค๋งˆ ์ •์˜ ํŒŒ์ผ์„ 
//์ฐพ์•„์•ผํ•˜๋Š” ์œ„์น˜๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

Code First(์ฝ”๋“œ ์šฐ์„ )

  • ์ฝ”๋“œ ์šฐ์„  ์ ‘๊ทผ ๋ฐฉ์‹์—์„œ๋Š” ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ์™€ ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋จผ์ € ์ž‘์„ฑํ•œ ๋ฆฌ์กธ๋ฒ„์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•ด๋‹น GraphQL ์Šคํ‚ค๋งˆ๋ฅผ ์ž๋™ ์ƒ์„ฑํ•œ๋‹ค.
  • ์ด ๋ฐฉ๋ฒ•์€ Typescript๋กœ๋งŒ ์ž‘์—…ํ•˜๊ณ  ์–ธ์–ด ๊ตฌ๋ฌธ๊ฐ„์˜ ์ปฝ๋„ฅ์ŠคํŠธ ์ „ํ™˜์„ ํ”ผํ•˜๋ ค๋Š” ๊ฒฝ์šฐ ์œ ์šฉํ•˜๋‹ค.
  • Schema์™€ Resolver๊ฐ„์˜ type safety์„ ๋ณด์žฅํ•˜๋ฉฐ ์ฝ”๋“œ ์ค‘๋ณต์ด ์ตœ์†Œํ™”ํ•˜๋Š” ์žฅ์ ์ด ์กด์žฌํ•œ๋‹ค.
GraphQLModule.forRoot<ApolloDriverConfig>({
      driver: ApolloDriver,
			autoSchemaFile: 'src/common/graphql/schema.gql',
})
//autoSchemaFile ์†์„ฑ ๊ฐ’์€ ์ž๋™์œผ๋กœ ์ƒ์„ฑ๋œ ์Šคํ‚ค๋งˆ๊ฐ€ ์ƒ์„ฑ๋  ๊ฒฝ๋กœ

GraphQL ํด๋” ๊ตฌ์กฐ ์…‹ํŒ…(Code First ์‹ค์Šต)

$ yarn add @nestjs/graphql @nestjs/apollo graphql apollo-server-express

์„ค์น˜ํ•ด์ค€ ๋‹ค์Œ app.module.ts ์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ˆ˜์ •ํ•˜์ž

@Module({
  imports: [
				//์ˆ˜์ •๋ณธ
    GraphQLModule.forRoot<ApolloDriverConfig>({
      driver: ApolloDriver,
				//
    }),
  ],
  // controllers: [AppController],
  // providers: [AppService],
})
export class AppModule {}
//app.module.ts 

@Module({
  imports: [
    BoardModule,
    GraphQLModule.forRoot<ApolloDriverConfig>({
      driver: ApolloDriver,
      autoSchemaFile: 'src/common/graphql/schema.gql',
    }),
  ],
  // controllers: [AppController],
  // providers: [AppService],
})
export class AppModule {}

// boards.module.ts

import { Module } from '@nestjs/common';
import { BoardsResolver } from './boards.resolver';
import { BoardsService } from './boards.service';

@Module({
  // imports: [],
  // controllers: [],
  providers: [BoardsResolver, BoardsService],
})
export class BoardModule {}

// boards.resolver.ts

import { Query, Resolver } from '@nestjs/graphql';
import { BoardsService } from './boards.service';

@Resolver()
export class BoardsResolver {
  constructor(private readonly boardsService: BoardsService) {}

  @Query(() => String)
  fetchBoards(): string {
    return this.boardsService.qqq();
  }
}

// boards.service.ts

import { Injectable } from '@nestjs/common';

@Injectable() // ์˜์กด์„ฑ ๊ด€๋ จ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ
export class BoardsService {
  qqq(): string {
    return 'Hello World!';
  }
}

๐Ÿง15์ผ์ฐจ Nest.js

Typescript Type

์ œ๋„ค๋ฆญ์€ ์–ธ์–ด์—์„œ ์žฌ์‚ฌ์šฉ์„ฑ์ด ๋†’์€ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค ๋•Œ ๊ฐ€์žฅ ๋งŽ์ด ํ™œ์šฉ๋˜๋Š” ํŠน์ง•์ด๋‹ค.

// 1.๋ฌธ์ž/์ˆซ์ž/๋ถˆ๋ฆฐ ๊ธฐ๋ณธํƒ€์ž…
const getPrimitive = (arg1: string, arg2: number, arg3: boolean): [boolean, number, string] => {
  return [arg3, arg2, arg1];
};

const result1 = getPrimitive("์ฒ ์ˆ˜", 123, true);

//
//
// 2. any ํƒ€์ž…(์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์™€ ๋™์ผ)
const getAny = (arg1: any, arg2: any, arg3: any): [any, any, any] => {
  console.log(arg1 + 100); // any ๋Š” ์•„๋ฌด๊ฑฐ๋‚˜ ๋‹ค ๋จ!
  return [arg3, arg2, arg1];
};

const result2 = getAny("์ฒ ์ˆ˜", 123, true);

//
//
// 3. unknown ํƒ€์ž…
const getUnknown = (arg1: unknown, arg2: unknown, arg3: unknown): [unknown, unknown, unknown] => {
  if (typeof arg1 === "number") console.log(arg1 + 100); // any ๋ณด๋‹ค๋Š” ์•ˆ์ „
  return [arg3, arg2, arg1];
};

const result3 = getUnknown("์ฒ ์ˆ˜", 123, true);

//
//
// 4-1. generic ํƒ€์ž…(๋“ค์–ด์˜ค๋Š” ํƒ€์ž…์— ๋”ฐ๋ผ์„œ MyType์ด ์„ค์ •๋œ๋‹ค.)
function getGeneric<MyType1, MyType2, MyType3>(arg1: MyType1, arg2: MyType2, arg3: MyType3): [MyType3, MyType2, MyType1] {
  return [arg3, arg2, arg1];
}

const result4 = getGeneric("์ฒ ์ˆ˜", 123, true);

//
//
// 4-2. generic ํƒ€์ž…(๋“ค์–ด์˜ค๋Š” ํƒ€์ž…์„ ๊ณ ์ •์‹œํ‚ค๊ธฐ ์œ„ํ•ด์„œ๋Š” ์•„๊ทœ๋จผํŠธ์— <> ์ถ”๊ฐ€ ํ•ด์ค˜์•ผํ•œ๋‹ค.)
function getGeneric2<MyType1, MyType2, MyType3>(arg1: MyType1, arg2: MyType2, arg3: MyType3): [MyType3, MyType2, MyType1] {
  return [arg3, arg2, arg1];
}

const result5 = getGeneric2<string, number, boolean>("์ฒ ์ˆ˜", 123, true);

//
//
// 4-3. generic ํƒ€์ž…
function forRoot<T1, T2, T3>(arg1: T1, arg2: T2, arg3: T3): [T3, T2, T1] {
  return [arg3, arg2, arg1];
}

const result6 = forRoot("์ฒ ์ˆ˜", 123, true);

//
//
// 4-4. generic ํƒ€์ž…
function getGeneric4<T, U, V>(arg1: T, arg2: U, arg3: V): [V, U, T] {
  return [arg3, arg2, arg1];
}
const result7 = getGeneric4<string, number, boolean>("์ฒ ์ˆ˜", 123, true);

//
//
// 4-5. generic ํƒ€์ž… - 4 (ํ™”์‚ดํ‘œ ํ•จ์ˆ˜ ์‚ฌ์šฉ)
const getGeneric5 = <T, U, V>(arg1: T, arg2: U, arg3: V): [V, U, T] => {
  return [arg3, arg2, arg1];
};
const result8 = getGeneric5<string, number, boolean>("์ฒ ์ˆ˜", 123, true);

Utility Types

**interface IProfile {
  name: string;
  age: number;
  school: string;
  hobby?: string;
}

//1. Partial ํƒ€์ž… (๋ถ€๋ถ„:์žˆ์–ด๋„ ๋˜๊ณ  ์—†์–ด๋„ ๋˜๋Š”)
type aaa = Partial<IProfile>;

//2. Required ํƒ€์ž… (ํ•„์ˆ˜์‚ฌํ•ญ)
type bbb = Required<IProfile>;

//3. Pick ํƒ€์ž… (์›ํ•˜๋Š” ์†์„ฑ๋งŒ ๋ฝ‘์•„์„œ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์„ ๋•Œ)
type ccc = Pick<IProfile, "name" | "age">;

//4. Omit ํƒ€์ž… (์›ํ•˜๋Š” ์†์„ฑ ์ œ๊ฑฐํ•˜์—ฌ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์„ใ„ท ๋•Œ )
type ddd = Omit<IProfile, "school">;

//5. Record ํƒ€์ž…
type eee = "์ฒ ์ˆ˜" | "์˜ํฌ" | "ํ›ˆ์ด"; // Union ํƒ€์ž…
let child1: eee = "์ฒ ์ˆ˜"; // ์ฒ ์ˆ˜,์˜ํฌ,ํ›ˆ์ด๋งŒ ๋จ
let child2: string = "์‚ฌ๊ณผ"; // ์ฒ ์ˆ˜ ์˜ํฌ ํ›ˆ์ด ์‚ฌ๊ณผ ๋ฐ”๋‚˜๋‚˜ ๋‹ค๋จ

type fff = Record<eee, IProfile>; // Record ํƒ€์ž…

//6.๊ฐ์ฒด์˜ ํ‚ค๋“ค๋กœ Union ํƒ€์ž… ๋งŒ๋“ค๊ธฐ
type ggg = keyof IProfile; // "name" | "age" | "school" | "hobby"์˜ Unionํƒ€์ž…์„ ๋งŒ๋“ค์–ด์คŒ
let myprofile: ggg = "hobby";

//7. type vs interface ์ฐจ์ด  => interface๋Š” ์„ ์–ธ๋ณ‘ํ•ฉ ๊ฐ€๋Šฅ
interface IProfile {
  candy: number; // ์„ ์–ธ๋ณ‘ํ•ฉ์œผ๋กœ ์ถ”๊ฐ€๋จ
}

// 8.๋ฐฐ์šด๊ฒƒ ์‘์šฉ
let profile: Partial<IProfile> = {
  candy: 10,
};**

Mysql

MySQL ์„ค์น˜

brew update
brew install mysql
brew servcies start mtsql
// ๋น„๋ฐ€๋ฒˆํ˜ธ ์„ค์ •
 mysql_secure_installation

๋ณต์žกํ•œ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์‚ฌ์šฉํ• ๊ฒƒ์ด๋ƒ๋Š” ์งˆ๋ฌธ์— N ์„ ์ž…๋ ฅํ•˜๊ณ  ์—”ํ„ฐ๋ฅผ ์นฉ๋‹ˆ๋‹ค.

New password:

Re-enter new password:

New password: ๋ผ๊ณ  ๋‚˜์˜ค๋ฉด, ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜๊ณ  ์—”ํ„ฐ๋ฅผ ์ณ์„œ ์„ค์ •ํ•ด์ค๋‹ˆ๋‹ค.

Re-enter new password: ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋‹ค์‹œ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค.

By default, a MySQL installation has an anonymous user,
allowing anyone to log into MySQL without having to have
a user account created for them. This is intended only for
testing, and to make the installation go a bit smoother.
You should remove them before moving into a production
environment.

Remove anonymous users? (Press y|Y for Yes, any other key for No) : y

y๋ฅผ ์ž…๋ ฅํ•ด, ์ต๋ช…์˜ ์œ ์ €๋ฅผ ์‚ญ์ œํ•˜๋Š”๋ฐ ๋™์˜ํ•ฉ๋‹ˆ๋‹ค

Normally, root should only be allowed to connect from
'localhost'. This ensures that someone cannot guess at
the root password from the network.

Disallow root login remotely? (Press y|Y for Yes, any other key for No) : n

n์„ ์ž…๋ ฅํ•ด root์˜ ์›๊ฒฉ ์ ‘์†์„ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค.

By default, MySQL comes with a database named 'test' that
anyone can access. This is also intended only for testing,
and should be removed before moving into a production
environment.

Remove test database and access to it? (Press y|Y for Yes, any other key for No) : n

 ... skipping.
Reloading the privilege tables will ensure that all changes
made so far will take effect immediately.

n์„ ์ž…๋ ฅํ•ด test ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.

Reload privilege tables now? (Press y|Y for Yes, any other key for No) : y
Success.

y๋ฅผ ์ž…๋ ฅํ•ด ๋ณ€๊ฒฝ๋œ ๊ถŒํ•œ์„ ํ…Œ์ด๋ธ”์— ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.

MySQL ์ ‘์† ํ™•์ธ

ํ„ฐ๋ฏธ๋„์— mysql -u root -p ๋ผ๊ณ  ์ž…๋ ฅํ•œ ๋’ค, ๋ฐฉ๊ธˆ ์„ค์ •ํ•œ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ฉ๋‹ˆ๋‹ค.

mysql> ์ฒ˜๋Ÿผ ํ„ฐ๋ฏธ๋„์ด ๋ฐ”๋€Œ๋ฉด ์„ฑ๊ณต์ž…๋‹ˆ๋‹ค! ๐Ÿ‘

MySQL ์—ฐ๊ฒฐ

brew services // ์„œ๋น„์Šค ๋ชฉ๋ก ํ™•์ธ

brew services start mysql // mysql ์‹คํ–‰ํ•˜๊ธฐ
brew services stop mysql // mysql ์ •์ง€

Typeorm์„ ํ™œ์šฉํ•œ NestJs์™€ MySQL ์—ฐ๊ฒฐ ์‹ค์Šต - TypeORM ์„ค์น˜

yarn add @nestjs/typeorm typeorm mysql2

- @nestjs/typeorm : NestJS ์šฉ TypeORM.
- `typeorm` : typeorm ์ตœ์‹  ๋ฒ„์ „์œผ๋กœ ์„ค์น˜.
- `mysql2` : TypeORM ์„ MySQL ๋กœ ์—ฐ๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ํ”„๋กœ๊ทธ๋žจ ์„ค์น˜.
@Module({
  imports: [
    BoardModule,
    GraphQLModule.forRoot<ApolloDriverConfig>({ // ์ฝ”๋“œํƒ€์ž… ์šฐ์„  ๋ฐฉ์‹
      driver: ApolloDriver,
      autoSchemaFile: 'src/commons/graphql/schema.gql',
    }),
    TypeOrmModule.forRoot({ // ์ถ”๊ฐ€
      type: 'mysql',            // ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค ํƒ€์ž…
      host: 'localhost',        // local ํ™˜๊ฒฝ์œผ๋กœ ์ง„ํ–‰
      port: 3306,               // mysql์€ ๊ธฐ๋ณธ port๋Š” 3306
      username: 'root',         // mysql์€ ๊ธฐ๋ณธ user๋Š” root๋กœ ์ง€์ •
      password: 'root',         // ๋ณธ์ธ์˜ mysql password 
      database: 'myproject',    // ์—ฐ๊ฒฐํ•  ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค๋ช…
      entities: [Board],        // ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค์™€ ์—ฐ๊ฒฐํ•  entity
      synchronize: true,        // entity ํ…Œ์ด๋ธ”์„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ๋™๊ธฐํ™”ํ•  ๊ฒƒ์ธ์ง€
      logging: true,            // ์ฝ˜์†” ์ฐฝ์— log๋ฅผ ํ‘œ์‹œํ•  ๊ฒƒ์ธ์ง€
    }),
  ],
})
export class AppModule {}

Board API ๋งŒ๋“ค๊ธฐ

fetchBoards API ์™€ createBoard API ์ƒ์„ฑ

**// boards.resolver.ts 

import { Query, Resolver, Mutation } from '@nestjs/graphql';
import { BoardsService } from './boards.service';

@Resolver()
export class BoardsResolver {
  constructor(private readonly boardsService: BoardsService) {}

  @Query(() => String, { nullable: true })
  fetchBoards(): string {
    return this.boardsService.findAll();
  }

  @Mutation(() => String)
  createBoard(): string {
    return this.boardsService.create();
  }
}**

ํ•จ์ˆ˜๋ฅผ findAll ๊ณผ create๋ฅผ ๋งŒ๋“ค์–ด์คฌ๋“ฏ์ด boardservice ์—์„œ๋„ ๊ทธ ํ•จ์ˆ˜์— ๋Œ€ํ•œ ๋กœ์ง์„ ๋งŒ๋“ค์–ด์ค€๋‹ค,

// boards.service.ts

import { Injectable } from '@nestjs/common';

@Injectable()
export class BoardsService {
  findAll(): string {
    // 1. ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•˜๋Š” ๋กœ์ง => DB์— ์ ‘์†ํ•ด์„œ ๋ฐ์ดํ„ฐ ๊บผ๋‚ด์˜ค๊ธฐ

		// 2. DB์—์„œ ๊บผ๋‚ด์˜จ ๊ฒฐ๊ณผ๋ฅผ ๋ธŒ๋ผ์šฐ์ €์— ์‘๋‹ต(response) ์ฃผ๊ธฐ
    return '์กฐํšŒ์— ์„ฑ๊ณตํ•˜์˜€์Šต๋‹ˆ๋‹ค.';
  }

	create(): string {
    // 1. ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋ณด๋‚ด์ค€ ๋ฐ์ดํ„ฐ ํ™•์ธํ•˜๊ธฐ
		//

		// 2. ๋ฐ์ดํ„ฐ๋ฅผ ๋“ฑ๋กํ•˜๋Š” ๋กœ์ง => DB์— ์ ‘์†ํ•ด์„œ ๋ฐ์ดํ„ฐ ์ €์žฅํ•˜๊ธฐ
    //

		// 3. DB์— ์ €์žฅ์ด ์ž˜ ๋์œผ๋ฉด, ๊ฒฐ๊ณผ๋ฅผ ๋ธŒ๋ผ์šฐ์ €์— ์‘๋‹ต(response) ์ฃผ๊ธฐ
    return '๊ฒŒ์‹œ๋ฌผ ๋“ฑ๋ก์— ์„ฑ๊ณตํ•˜์˜€์Šต๋‹ˆ๋‹ค!!';
}

์™œ ๋”ฐ๋กœ ์ด๋ ‡๊ฒŒ ๋ถ„๋ฆฌํ•˜๋Š๋ƒ??? service๋ฅผ ๋งŒ๋“œ๋Š” ์ด์œ ๋Š” ์ค‘๋ณต๋˜๋Š” ๋ถ€๋ถ„๋“ค์„ ๋‹ค๋ฅธ API์—์„œ๋„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•จ์œผ๋กœ ์‹ค์ œ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๊ณณ์€ resolver ์—์„œ ์ตœ์ข…์ ์œผ๋กœ ์‚ฌ์šฉ๋˜๊ฒŒ ๋œ๋‹ค.

import { Field, Int, ObjectType } from '@nestjs/graphql';
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
// ๋งจ์œ„ ๊ณจ๋ฑ…์ด๋Š” mysQL ์•„๋ž˜๊ณจ๋ฑ…์ด๋Š” graphQL
@Entity()
@ObjectType()
export class Board {
  @PrimaryGeneratedColumn('increment')
  @Field(() => Int)
  number: number;

  @Column()
  @Field(() => String)
  writer: string;

  @Column()
  @Field(() => String)
  title: string;

  @Column()
  @Field(() => String)
  contents: string;
}

boards.resolver.ts ์ˆ˜์ •

// boards.resolver.ts

import { Resolver, Query, Mutation } from '@nestjs/graphql';
import { BoardsService } from './boards.service';
import { Board } from './entities/board.entity';

@Resolver()
export class BoardsResolver {
  constructor(private readonly boardsService: BoardsService) {}

  @Query(() => [Board], { nullable: true }) 
  fetchBoards(): Board[] {
    return this.boardsService.findAll();
  }

  @Mutation(() => String)
  createBoard(
    @Args('writer') writer: string, //@Args ๋Š” ์ธ์ž๋ฅผ ๊ฐœ๋ณ„์ ์œผ๋กœ ์ถ”๊ฐ€ํ•ด์ค„๋•Œ ํ•ด์ค€๋‹ค.
    @Args('title') title: string,   //@Args์€ gqlํƒ€์ž…์ด๊ณ  ๊ทธ ๋’ค๋Š” ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ ํƒ€์ž…์„ ์˜๋ฏธ!
    @Args('contents') contents: string,
  ): string {
    return this.boardsService.create({ writer, title, contents });
  }
}

createBoard์—์„œ InputType DTO๋กœ ๋ฌถ์–ด์„œ ๋ฐ›๊ธฐ

์œ„์—์„œ ๋ฆฌ์กธ๋ฒ„์—์„œ ๊ฐ๊ฐ์˜ ์ธ์ž๋ฅผ ๋ฝ‘์•„์„œ ์ถ”๊ฐ€ํ•˜์˜€๋‹ค. ์ง€๊ธˆ์€ ์„ธ๊ฐœ๋ฐ–์— ์ž…๋ ฅ์„ ์•ˆํ–ˆ์ง€๋งŒ ๋” ๋ณต์žกํ•˜๊ณ  ๋งŽ์€ ์ž…๋ ฅ์„ ๋ฐ›์•„์•ผ ํ•œ๋‹ค๋ฉด ์ด๋ฐฉ์‹์€ ํšจ์œจ์ ์ด์ง€ ์•Š๋‹ค. ๋” ๋งŽ์€ ์ž…๋ ฅ์„ ๋ฐ›์•„์•ผ ํ•œ๋‹ค๋ฉด ๊ฐ์ฒด๋กœ ๋ฌถ์–ด์„œ ์ „๋‹ฌ๋ฐ›๋Š” ๋ฐฉ์‹์ด ๋” ํšจ์œจ์ ์ด๋‹ค.

dto ํด๋”๋ฅผ ์ƒ์„ฑํ•˜์—ฌ create-board.input.ts ํŒŒ์ผ์„ ์ƒ์„ฑ

dto

: ๋ฐ์ดํ„ฐ ์ „์†ก ๊ฐ์ฒด, ์ฆ‰ ๋„คํŠธ์›Œํฌ ๊ฐ„์— ๋ฐ์ดํ„ฐ๋ฅผ ์–ด๋–ค ์‹์œผ๋กœ ๋ณด๋‚ผ์ง€๋ฅผ ์ •์˜ํ•œ ๊ฐ์ฒด์ด๋‹ค.

// create-board.input.ts

import { InputType, Field } from '@nestjs/graphql';

@InputType()
export class CreateBoardInput {
  @Field(() => String)
  writer: string;

  @Field(() => String)
  title: string;

  @Field(() => String)
  contents: string;
}

boards.resolver.ts ์— ์ ์šฉ ์‹œ์ผœ์ฃผ๊ธฐ.

// boards.resolver.ts

import { Query, Resolver, Mutation, Args } from '@nestjs/graphql';
import { BoardsService } from './boards.service';
import { CreateBoardInput } from './dto/createBoard.input';
import { Board } from './entities/board.entity';

@Resolver()
export class BoardsResolver {
  constructor(private readonly boardsService: BoardsService) {}

  @Query(() => [Board], { nullable: true })
  fetchBoards(): Board[] {
    return this.boardsService.findAll();
  }

  @Mutation(() => String)
  createBoard(
		// @Args('writer') writer: string,
    // @Args('title') title: string,
    // @Args('contents') contents: string,
===> ์ถ”๊ฐ€
    @Args('createBoardInput') createBoardInput: CreateBoardInput,
  ): string {
    return this.boardsService.create({createBoardInput});
  }
}

boards.service.ts ์ˆ˜์ •


  create({ createBoardInput }: IBoardsServiceCreate): string {
    // 1. ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋ณด๋‚ด์ค€ ๋ฐ์ดํ„ฐ ํ™•์ธํ•˜๊ธฐ
		console.log(createBoardInput.writer);
    console.log(createBoardInput.title);
    console.log(createBoardInput.contents);

		// 2. ๋ฐ์ดํ„ฐ๋ฅผ ๋“ฑ๋กํ•˜๋Š” ๋กœ์ง => DB์— ์ ‘์†ํ•ด์„œ ๋ฐ์ดํ„ฐ ์ €์žฅํ•˜๊ธฐ
    //

		// 3. DB์— ์ €์žฅ์ด ์ž˜ ๋์œผ๋ฉด, ๊ฒฐ๊ณผ๋ฅผ ๋ธŒ๋ผ์šฐ์ €์— ์‘๋‹ต(response) ์ฃผ๊ธฐ
    return '๊ฒŒ์‹œ๋ฌผ ๋“ฑ๋ก์— ์„ฑ๊ณตํ•˜์˜€์Šต๋‹ˆ๋‹ค!!';
  }
}

// board-services.interface.ts

import { CreateBoardInput } from '../dto/create-board.input';

export interface IBoardsServiceCreate {
  createBoardInput: CreateBoardInput;
}

์•„๊นŒ TypeOrm ์„ค์น˜๋ถ€๋ถ„์—์„œ ๊ฐœ์ธ์ •๋ณด๋“ค์ด ์กด์žฌํ•œ๋‹ค ์ด๋ฅผ ์œ„ํ•ด์„  env ์ฒ˜๋ฆฌํ•ด์ค˜์•ผํ•œ๋‹ค.

.env ์™€ .env.docker ์— envํ•  ๊ฒƒ๋“ค์„ ์ •๋ฆฌํ•˜๊ณ  module.ts ํŒŒ์ผ๊ณผ ์—ฐ๊ฒฐํ•ด์ค€๋‹ค.

์—ฐ๊ฒฐ ํ•˜๊ธฐ์ „ nest ์—์„œ ์ง€์›ํ•ด์ฃผ๊ณ  ์žˆ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ•œ๊ฐœ๋ฅผ ์„ค์น˜ํ•ด์•ผ ํ•œ๋‹ค.

yarn add @nestjs/config
// app.module.ts

@Module({
  imports: [
    BoardsModule,
    ConfigModule.forRoot(),  ==> env ์ถ”๊ฐ€
    GraphQLModule.forRoot<ApolloDriverConfig>({
      driver: ApolloDriver,
      autoSchemaFile: 'src/commons/graphql/schema.gql',
    }),
    TypeOrmModule.forRoot({ ==> process.env ์ฒ˜๋ฆฌํ•ด์„œ ๊ฐœ์ธ์ •๋ณด ๋ณด์•ˆ 
      type: process.env.DATABASE_TYPE as 'mysql',
      host: process.env.DATABASE_HOST,
      port: Number(process.env.DATABASE_PORT),
      username: process.env.DATABASE_USERNAME,
      password: process.env.DATABASE_PASSWORD,
      database: process.env.DATABASE_DATABASE,
      entities: [Board],
      synchronize: true,
      logging: true,
    }),
  ],
})
export class AppModule {}
profile
๊ฐ„์ ˆํ•˜๊ณ  ์น˜์—ดํ•˜๊ฒŒ ์‚ด์ž

0๊ฐœ์˜ ๋Œ“๊ธ€