Hello Ocean! ๐ŸŒผ

[TIL] ํ”„๋ฆฌ์˜จ๋ณด๋”ฉ ๋งˆ์ง€๋ง‰, ์ผ๊ณฑ ๋ฒˆ์งธ ๊ณผ์ œ_Cardoc ๋ณธ๋ฌธ

TIL

[TIL] ํ”„๋ฆฌ์˜จ๋ณด๋”ฉ ๋งˆ์ง€๋ง‰, ์ผ๊ณฑ ๋ฒˆ์งธ ๊ณผ์ œ_Cardoc

bba_dda 2021. 12. 2. 17:00
๋ฐ˜์‘ํ˜•

์ผ๊ณฑ ๋ฒˆ์งธ ๊ณผ์ œ ๐Ÿ”ฅ 

์ด๋ฒˆ ๊ณผ์ œ๋Š” ์ฒ˜์Œ์œผ๋กœ ํ˜ผ์ž ์ง„ํ–‰ํ•˜๋Š” ๊ณผ์ œ์˜€๋‹ค. ํ•œ ๋‹ฌ๋™์•ˆ ์žฆ์€ ๋ฐค์ƒ˜์— ์ง€์ณ๊ฐ€๋˜ ์ค‘์ด์—ˆ๋Š”๋ฐ, ๊ฐœ์ธ ๊ณผ์ œ์— ๊ธฐํ•œ๋„ ์ผ์ฃผ์ผ์ด๋‚˜ ์ฃผ์…”์„œ ํ–‰๋ณตํ–ˆ๋‹ค. 

์šฐ๋ฆฌ ํŒ€์€ ๊ฐœ์ธ๊ณผ์ œ์ž„์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ํ™œ๋ฐœํ•˜๊ฒŒ ์†Œํ†ตํ•˜๋ฉฐ ๊ณผ์ œ๋ฅผ ์ง„ํ–‰ํ–ˆ๋‹ค.

์ฃผ๋œ ๋ชฉ์ ์€ ์–ด๋ชฝ์–ด์Šค์™€ ์บ์น˜๋งˆ์ธ๋“œ ์˜€์ง€๋งŒ..

์‹œ๊ฐ„์— ์ซ“๊ธฐ์ง€ ์•Š์•„๋„ ๋˜๋‹ˆ, Nestjs ๋ฐ ๋„์ปค๋ฅผ ๊ณต๋ถ€ํ•˜๋ฉด์„œ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ์–ด์„œ ์ข‹์•˜๋‹ค.

 

github ๋งํฌ : https://github.com/sally0226/assignment-7-cardoc

 

GitHub - sally0226/assignment-7-cardoc

Contribute to sally0226/assignment-7-cardoc development by creating an account on GitHub.

github.com

 

1) DB ๋ชจ๋ธ๋ง ๐Ÿค” 

์ฒ˜์Œ์—๋Š” ํ…Œ์ด๋ธ”์„ ๋‘๊ฐœ๋งŒ ๋‘์–ด User์™€, User๊ฐ€ ๊ฐ€์ง„ ์ž๋™์ฐจ์™€ ๊ทธ์— ๋”ฐ๋ฅธ ํƒ€์ด์–ด ์ •๋ณด๋ฅผ ํ•œ ํ…Œ์ด๋ธ”์— ์ €์žฅํ•˜๋ ค๊ณ  ํ–ˆ์œผ๋‚˜, trimId๊ฐ€ ๋™์ผํ•œ ์ž๋™์ฐจ๋Š” ํƒ€์ด์–ด ์ •๋ณด๊ฐ€ ๊ฐ™์œผ๋ฏ€๋กœ, Cars ํ…Œ์ด๋ธ”๋กœ ๋งŒ๋“ค์–ด ๋™์ผํ•œ ์ปฌ๋Ÿผ๋“ค์˜ ์ค‘๋ณต ์ €์žฅ์„ ์ค„์ด๊ณ ์ž ํ–ˆ๋‹ค.

  • OwnedLists
    • ํ•œ user๊ฐ€ ๋™์ผํ•œ trimId์˜ ์ฐจ๋Ÿ‰์„ ์—ฌ๋Ÿฌ๊ฐœ ๋“ฑ๋กํ•  ์ˆ˜ ์—†๋„๋ก, id + trimId ์ปฌ๋Ÿผ์„ ๋ฌถ์–ด Unique์†์„ฑ์„ ๋ถ€์—ฌ
@Unique(["user", "car"])
  • Cars
    • ์ฒ˜์Œ์—๋Š” ํญ , ํŽธํ‰๋น„, ํœ  ์‚ฌ์ด์ฆˆ ์ปฌ๋Ÿผ์„ ๋‘์–ด frontTire์ •๋ณด๊ฐ€ ์žˆ์œผ๋ฉด ์ €์žฅ, ์—†์œผ๋ฉด rearTire์ •๋ณด๋กœ ์ €์žฅํ•˜๋ ค๊ณ  ํ–ˆ๋‹ค.
    • ํ•˜์ง€๋งŒ, frontTire์™€ rearTire์˜ ๊ทœ๊ฒฉ์ด ๋‹ค๋ฅธ ๊ฒฝ์šฐ๊ฐ€ ์žˆ์„ ๊ฒƒ์œผ๋กœ ํŒ๋‹จ๋˜์–ด frontTire์™€ rearTire์˜ ์ •๋ณด๋ฅผ ๋”ฐ๋กœ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋„๋ก ์ปฌ๋Ÿผ์˜ ์ˆ˜๋ฅผ ๋Š˜๋ฆผ
    • ํ•˜์ง€๋งŒ ์•„๋ž˜์™€ ๊ฐ™์ด rearTire์˜ ์ •๋ณด๊ฐ€ ์—†๋Š” case๋ฅผ ๋ฐœ๊ฒฌํ•˜๊ฒŒ ๋˜์–ด, rearTire๊ด€๋ จ ์ปฌ๋Ÿผ์— nullable ์†์„ฑ์„ ์ฃผ์—ˆ๋‹ค.

2) ์ธ์ฆ ๐Ÿค 

์ง€๋‚œ ๋Œ€๋ถ€๋ถ„์˜ ๊ณผ์ œ๋“ค์—๋„ ์ธ์ฆ ๊ธฐ๋Šฅ์ด ํ•„์š”ํ•˜์—ฌ ๊ตฌํ˜„ํ–ˆ์ง€๋งŒ, ์‹œ๊ฐ„์ด ๋„‰๋„‰ํ•˜์ง€ ์•Š์•„ ๋‹ค๋ฅธ ํŒ€์›๋ถ„์ด ์ด์ „์— nestjs๋กœ ์ž‘์„ฑํ•ด๋†“์œผ์‹  ํด๋”๊ตฌ์กฐ์™€ ์ฝ”๋“œ๋ฅผ ๋ณด๊ณ  ๋ฒ ๋ผ๋‹ค์‹œํ”ผ ๊ตฌํ˜„ํ–ˆ์—ˆ๋‹ค. 

๊ทธ๋ž˜์„œ ์ด๋ฒˆ ๊ณผ์ œ๋ฅผ ํ•  ๋•Œ๋Š”, Nestjs ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ๊ผผ๊ผผํžˆ ๋ณด๋ฉด์„œ ์ดํ•ดํ•˜๋ฉด์„œ ๊ตฌํ˜„ํ–ˆ๋‹ค.

๊ณต์‹๋ฌธ์„œ๋ฅผ ๋ณด๋ฉฐ ๊ตฌํ˜„ํ•˜๋‹ˆ, JwtStrategy์™€ localStrategy๊ฐ€ guard๋ฅผ ์œ„ํ•ด ํ•„์š”ํ•˜๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ฒŒ ๋˜์—ˆ๋‹ค.

 

guard๋ฅผ ๋งŒ๋“ค๊ณ , ํ•ด๋‹น guard๊ฐ€ ํ•„์š”ํ•œ ๊ณณ์—์„œ @UseGuard() ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ , ํšŒ์›๊ฐ€์ž… ์„ฑ๊ณต์‹œ ๋ฐ”๋กœ ๋กœ๊ทธ์ธ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ฃผ์–ด JWT๋ฅผ ๋ฐœ๊ธ‰ํ•ด์ฃผ๋Š” ๋ฐฉ์‹์„ ์ ์šฉํ–ˆ๋‹ค.

 

3) ์‚ฌ์šฉ์ž๊ฐ€ ์†Œ์œ ํ•œ ํƒ€์ด์–ด ์ •๋ณด๋ฅผ ์ €์žฅํ•˜๋Š” API ๐Ÿš“ 

์ด API๋Š” ์—ฌ๋Ÿฌ ์‚ฌ์šฉ์ž์˜ ์ •๋ณด๋ฅผ ํ•œ ๋ฒˆ์— ์š”์ฒญํ•  ์ˆ˜ ์žˆ๋Š” API์ด์ง€๋งŒ, ๊ณผ์ œ ์•ˆ๋‚ด ํŽ˜์ด์ง€์—

์ดํ›„์˜ API๋Š” ์ธ์ฆ๋œ ์‚ฌ์šฉ์ž๋งŒ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค.๋ผ๋Š” ๋ฌธ๊ตฌ๊ฐ€ ์žˆ์—ˆ๊ธฐ์—, ๋กœ๊ทธ์ธ์ด ๋˜์–ด์žˆ๋Š”์ง€ ๊ฒ€์‚ฌํ–ˆ๋‹ค.

๋‹ค๋งŒ, ๋ˆ„๊ฐ€ ๋กœ๊ทธ์ธํ–ˆ๋Š”์ง€๋Š” ์‹ ๊ฒฝ์“ฐ์ง€ ์•Š์•˜๋‹ค. 

 

์ด API์˜ res๋ฅผ ์ •ํ•˜๋Š” ๊ณผ์ •์—์„œ ๋งŽ์€ ๊ณ ๋ฏผ์„ ํ–ˆ๋‹ค. 

Request body๋กœ ์‚ฌ์šฉ์ž ์ •๋ณด + trimId ์Œ์„ ์ตœ๋Œ€ 5๊ฐœ ๊นŒ์ง€ ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š”๋ฐ, 

๊ทธ๋ž˜์„œ ์ผ๋ถ€๋Š” ์„ฑ๊ณตํ•˜๊ณ , ์ผ๋ถ€๋Š” ์‹คํŒจํ•œ ๊ฒฝ์šฐ์— ์–ด๋–ป๊ฒŒ Response๋ฅผ ๋ณด๋‚ด์ค„์ง€์— ๋Œ€ํ•ด ๊ณ ๋ฏผ์ด ๋˜์—ˆ๋‹ค.

 

1. ํ•œ ๊ฐœ๋ผ๋„ ์‹คํŒจํ•˜๋ฉด Error Res

2. ํ•œ ๊ฐœ๋ผ๋„ ์„ฑ๊ณตํ•˜๋ฉด 200 Res, ๋Œ€์‹ ์— ์•ˆ์— ๋ฐฐ์—ด๋กœ ๋ฉ”์„ธ์ง€ ์ ์–ด ๋ณด๋‚ด๊ธฐ 

์ค‘์— ๊ณ ๋ฏผ์„ ํ–ˆ๋Š”๋ฐ, 1๋ฒˆ ๋ฐฉ๋ฒ•์€ ๋น„ํšจ์œจ์ ์ด๋ผ๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ๋‹ค.

๊ฐ€๋Šฅํ•œ ํ•ญ๋ชฉ์— ๋Œ€ํ•ด์„œ๋Š” ์ตœ๋Œ€ํ•œ ์ฒ˜๋ฆฌํ•ด์ฃผ๋Š”๊ฒŒ ์ข‹์„ ๊ฒƒ ๊ฐ™์•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

๋”ฐ๋ผ์„œ 1๊ฐœ๋ผ๋„ ์„ฑ๊ณตํ•˜๋ฉด 200์œผ๋กœ Response๋ฅผ ๋ณด๋‚ด๋˜ ๋ฉ”์„ธ์ง€๋กœ ์‹คํŒจํ•œ ๊ฒฝ์šฐ๋ฅผ ๋‚˜ํƒ€๋‚ด์ฃผ์—ˆ๋‹ค. 

[์˜ˆ์‹œ]

  • ํ•œ ๊ฐœ๋ผ๋„ ์„ฑ๊ณตํ•œ ๊ฒฝ์šฐ
    • ์„ฑ๊ณตํ•œ ๊ฒฝ์šฐ๋Š” ์ƒ์„ฑ๋œ id์™€ trimId์Œ์ด,
      [
          {
              "id": "bada1",
              "trimId": 5
          },
          "์ค‘๋ณต๋œ ์ •๋ณด์ž…๋‹ˆ๋‹ค.",
          "์กด์žฌํ•˜์ง€ ์•Š๋Š” user์ž…๋‹ˆ๋‹ค."
      ]
    • ์‹คํŒจํ•œ ๊ฒฝ์šฐ์—๋Š” ํ•ด๋‹น case์— ๋Œ€ํ•œ error message๊ฐ€ ๋“ค์–ด์žˆ๋‹ค.
    • 200๊ณผ ํ•จ๊ป˜ ๋ฐฐ์—ด์ด Response๋œ๋‹ค.
  • ์ „๋ถ€ ์‹คํŒจํ•œ ๊ฒฝ์šฐ
    {
        "statusCode": 400,
        "message": [
            "์ค‘๋ณต๋œ ์ •๋ณด์ž…๋‹ˆ๋‹ค.",
            "์กด์žฌํ•˜์ง€ ์•Š๋Š” user์ž…๋‹ˆ๋‹ค."
        ],
        "path": "/owned-lists"
    }
    • 400๊ณผ ํ•จ๊ป˜ ๋ฉ”์„ธ์ง€๊ฐ€ ๋ฐฐ์—ดํ˜•ํƒœ๋กœ Response๋œ๋‹ค.
    • ๊ฐ ํ•ญ๋ชฉ๋งˆ๋‹ค ์‹คํŒจํ•œ ์ด์œ ๊ฐ€ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ฉ”์„ธ์ง€๋ฅผ ๋ฐฐ์—ดํ˜•ํƒœ๋กœ ์ „์†กํ–ˆ๋‹ค. 

๋‹ค๋งŒ, DTO๊ฐ€ ํ•œ ๊ฐœ๋ผ๋„ ์–ด๊ธ‹๋‚ฌ์„ ๋•Œ์—๋Š” ๋ฐ”๋กœ 400์ด Response๋œ๋‹ค.

Nestjs๊ฐ€ DTO๊ฐ€ ์–ด๊ธ‹๋‚ฌ์„ ๋•Œ, ์–ด๋–ป๊ฒŒ Error Handle์„ ํ•˜๋Š”์ง€ ๋” ๊ณต๋ถ€ํ•  ์ˆ˜ ์žˆ๋Š” ์‹œ๊ฐ„์ด ์žˆ์—ˆ์œผ๋ฉด ์ข‹์•˜์„ ๊ฒƒ ๊ฐ™๋‹ค.

๋‹ค์Œ์—” ์ด ๋ถ€๋ถ„์„ ๊ณต๋ถ€ํ•ด๋ณผ ๊ฒƒ์ด๋‹ค.

{
    "statusCode": 400,
    "message": [
        "list.1.id should not be empty",
        "list.1.id must be a string"
    ],
    "path": "/owned-lists"
}

 

4) ์ž๋™์ฐจ ์ •๋ณด ์กฐํšŒ API์˜ ์‚ฌ์šฉ ๐ŸŽ๏ธ

[API ํ˜ธ์ถœ]

์ž๋™์ž ์ •๋ณด ์กฐํšŒ API๋ฅผ ํ˜ธ์ถœํ•˜๊ธฐ ์œ„ํ•ด axios์˜ HttpService๋ฅผ ์ด์šฉํ–ˆ๋‹ค.

๋น„๋™๊ธฐ๋กœ ์ฒ˜๋ฆฌ๋˜๋Š” APIํ˜ธ์ถœ์ด ์™„๋ฃŒ๋  ๋•Œ ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆด ํ•„์š”๊ฐ€ ์žˆ์—ˆ๋Š”๋ฐ, return type์ด Promise๊ฐ€ ์•„๋‹ˆ๋ผ Observable์ด์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ”๋กœ await๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์—ˆ๋‹ค.

์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด rjax์˜ lastValueFrom ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋Š” Observable๋ฅผ Promise๋กœ ๋ฐ”๊ฟ”์ฃผ๋ฉฐ ํ•จ์ˆ˜ ์ด๋ฆ„์ด ๋œปํ•˜๋Š” ๋ฐ”์™€ ๊ฐ™๊ฒŒ ๋งˆ์ง€๋ง‰ value๋ฅผ returnํ•ด์ค€๋‹ค.

 

์ง€๋‚œ ํœด๋จผ์Šค์ผ€์ดํ”„์˜ ๊ณผ์ œ์—์„œ ์™ธ๋ถ€ API ํ˜ธ์ถœ์ด ํ•„์š”ํ–ˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์—, ์–ด๋ ต์ง€ ์•Š๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค. 

axios๋ง๊ณ , @nestjs/common์—์„œ ์ œ๊ณตํ•˜๋Š” HttpService๋„ ์žˆ์–ด์„œ ์ง€๋‚œ ๊ณผ์ œ์—์„œ ์ด๊ฒƒ์„ ์ด์šฉํ•˜๋ ค๊ณ  ํ–ˆ๋Š”๋ฐ, 

"HttpModule" (from the "@nestjs/common" package) is deprecated and will be removed in the next major release. Please, use the "@nestjs/axios" package instead.
(๋Œ€์ถฉ ์ด์ œ ์ด๊ฑฐ ์ง€์›์•ˆํ•˜๋‹ˆ๊นŒ axios ์“ฐ๋ผ๋Š” ์–˜๊ธฐ)

์ด๋Ÿฐ ๋ฉ”์„ธ์ง€๊ฐ€ ๋–ด๊ธฐ ๋•Œ๋ฌธ์— axios๋ฅผ ์„ค์น˜ํ•ด ์ด์šฉํ–ˆ๋‹ค. 

[์ •๊ทœ์‹ ์ด์šฉ]

๋˜ํ•œ, frontTire/rearTire.value๊ฐ€ 205/75R18์™€ ๊ฐ™์ด ํ•˜๋‚˜์˜ string์•ˆ์— ํญ, ํŽธํ‰๋น„, ํœ  ์‚ฌ์ด์ฆˆ๊ฐ€ ๋ชจ๋‘ ํฌํ•จ๋˜์–ด ์žˆ๋Š” ํ˜•ํƒœ์˜€๋‹ค. 

๋”ฐ๋ผ์„œ ์ฃผ์–ด์ง„ string์„ /์™€ R๋กœ splitํ•ด์ฃผ์–ด์•ผ ํ–ˆ๋Š”๋ฐ, ์ •๊ทœ์‹์„ ์ž‘์„ฑํ•˜์—ฌ ํ•œ ๋ฒˆ์˜ splitํ•จ์ˆ˜๋กœ [ํญ, ํŽธํ‰๋น„, ํœ  ์‚ฌ์ด์ฆˆ]๋ฅผ ๊ตฌํ•  ์ˆ˜ ์žˆ๋„๋ก ํ–ˆ๋‹ค.

 

์ด๋ฒˆ ๊ณผ์ œ๋ฅผ ํ†ตํ•ด ์ •๊ทœ์‹์—๋Š” "(๋”ฐ์˜ดํ‘œ)๋ฅผ ๋ถ™์ด์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ฒŒ๋˜์—ˆ๋‹ค. 

์ด๊ฑธ ๋ชจ๋ฅด๊ณ  ๋”ฐ์˜ดํ‘œ๋ฅผ ๋ถ™์ธ ์ฑ„๋กœ ์™œ ์•ˆ๋˜์ง€? ํ•˜๋‹ค๊ฐ€ ๋‚˜์ค‘์— ์•Œ๊ฒŒ๋˜์—ˆ๋‹ค..

const frontInfo = frontTire ? frontTire.split(/R|\//) : null;

[nullable]

Cars ์—”ํ‹ฐํ‹ฐ ๋ชจ๋ธ๋ง์—์„œ๋„ ์„ค๋ช…ํ•œ ๋‚ด์šฉ์ด์ง€๋งŒ,

frontTire์™€ rearTire์˜ ์ •๋ณด๋ฅผ ๊ฐ๊ฐ ์ €์žฅํ•˜๋Š”๋ฐ, rearTire๊ฐ€ ์ •๋ณด๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ๊ทธ๋Ÿฐ ๊ฒฝ์šฐ์—๋Š” null์ด ๋“ค์–ด๊ฐˆ ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ฃผ์—ˆ๋‹ค.

frontTire, rearTire ๋ชจ๋‘ null → error

๋‘˜ ์ค‘์— ํ•˜๋‚˜๋งŒ null → ํ†ต๊ณผ ํ›„ ํ•ด๋‹น ์ปฌ๋Ÿผ๊ฐ’์ด null๋กœ ์ €์žฅ๋˜๋„๋ก ์ฒ˜๋ฆฌ

 

carService๋‚ด์˜ ์ž๋™์ฐจ ์ •๋ณด ์กฐํšŒ API๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ํ•จ์ˆ˜ ๋งํฌ

async getCarInfo(trimId: number) {
    const url = `https://dev.mycar.cardoc.co.kr/v1/trim/${trimId}`;
    try {
        const responseData = await lastValueFrom(
            this.httpService
            .get(url)
            .pipe(map((response) => response.data?.spec?.driving))
        );
        const frontTire = responseData.frontTire?.value;
        const rearTire  = responseData.rearTire?.value;
        if (!frontTire && !rearTire) return null;
        const frontInfo = frontTire ? frontTire.split(/R|\//) : null;
        const rearInfo  = rearTire  ? rearTire.split(/R|\//)  : null;
        const result = await this.carsRepository.createOne(
            trimId, 
            frontInfo, 
            rearInfo
        );
        return result;
    } catch (error) {
        return null;
    }
}

 

5) HTTP Status Code โ›ณ

๊ฐ API์˜ status code๋Š” ๊ณผ์ œ ์•ˆ๋‚ด ํŽ˜์ด์ง€์— ๋”ฐ๋ผ 200, 400, 401, 500์œผ๋กœ ๊ตฌ์„ฑํ–ˆ๋‹ค.

 

400์˜ ๊ฒฝ์šฐ, error์˜ ์ด์œ ๊ฐ€ ๋‹ค์–‘ํ•˜๋ฏ€๋กœ error message๋ฅผ ๋‹ค๋ฅด๊ฒŒ ๋„ฃ์–ด์ฃผ์—ˆ๋‹ค.

ex) ํšŒ์›๊ฐ€์ž… ์‹œ ์ค‘๋ณต ๊ฐ€์ž…์˜ ๊ฒฝ์šฐ, DTO๊ฐ€ ์•ˆ ๋งž๋Š” ๊ฒฝ์šฐ ๋“ฑ..

// ์ค‘๋ณต ๊ฐ€์ž…์‹œ
{
    "statusCode": 400,
    "message": "์ค‘๋ณต๋œ id์ž…๋‹ˆ๋‹ค.",
    "path": "/users/signup"
}
// DTO์™€ ๋งž์ง€ ์•Š์„ ๋•Œ
{
    "statusCode": 400,
    "message": [
        "id should not be empty",
        "id must be a string"
    ],
    "path": "/users/signup"
}

 

6) Exception ์ฒ˜๋ฆฌ ๐Ÿšซ

์ง€๋‚œ ๊ณผ์ œ๋“ค์—์„œ๋„ Exception ์ฒ˜๋ฆฌ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ–ˆ์—ˆ๋‹ค. ํ•˜์ง€๋งŒ Nestjs๋ฅผ ์ž˜ ๋ชจ๋ฅด๋Š” ์ƒํƒœ์—ฌ์„œ ๋‹ค๋ฅธ ํŒ€์›์ด ํ•ด ๋†“์€ ๋ฐฉ์‹์„ ๊ทธ๋Œ€๋กœ ์ด์šฉํ–ˆ๋‹ค. ๋‹ค๋งŒ ๊ทธ ๊ตฌ์กฐ๊ฐ€ ํŒŒ์ผ์ด ๋งŽ์ด ํ•„์š”ํ•˜๊ณ  ๋ณต์žกํ•ด์„œ, ๋” ๊ฐ„๋‹จํ•œ ๋ฐฉ๋ฒ•์ด ์กด์žฌํ•˜์ง€ ์•Š์„๊นŒ ์ƒ๊ฐํ–ˆ๋‹ค. 

๋‹ค๋ฅธ ํŒ€์›๋“ค๋„ ๋‚˜์™€ ๊ฐ™์€ ์ƒ๊ฐ์„ ํ•ด์„œ, ๋‚˜์ค‘์— ๊ณต๋ถ€ํ•ด๋ณด์ž๊ณ  ์ด์•ผ๊ธฐ๋ฅผ ๋‚˜๋ˆด์—ˆ๋Š”๋ฐ ์ด๋ฒˆ ๊ณผ์ œ๋ฅผ ํ†ตํ•ด ๋” ๋‚˜์€ ๋ฐฉ๋ฒ•์„ ์ฐพ์„ ์ˆ˜ ์žˆ์—ˆ๋‹ค.

 

๋‹ต์€ ์—ญ์‹œ ๊ณต์‹๋ฌธ์„œ์— ์žˆ์—ˆ๋‹ค. Nestjs ๊ณต์‹๋ฌธ์„œ๋ฅผ ํ†ตํ•ด ๊ตฌํ˜„ํ–ˆ์œผ๋ฉฐ, ํ•„์š”ํ•œ ๋ถ€๋ถ„์„ ์ˆ˜์ •ํ–ˆ๋‹ค.

 

ํŠนํžˆ message๋ณ€์ˆ˜๋ฅผ ์ˆ˜์ •ํ–ˆ๋‹ค.

๋ณดํ†ต์˜ ๊ฒฝ์šฐ์—๋Š” exception.message์— string ํ˜•ํƒœ์˜ message๊ฐ€ ๋“ค์–ด์žˆ๋Š”๋ฐ,

DTO๊ด€๋ จ error์™€ ๊ฐ™์ด error message๊ฐ€ 1๊ฐœ ์ด์ƒ์œผ๋กœ ๋ฐฐ์—ด ํ˜•ํƒœ๋กœ ์กด์žฌํ• ๋•Œ,

exception.message ์—๋Š” ํฌ๊ด„์ ์ธ ๋ฉ”์„ธ์ง€๋งŒ ๋“ค์–ด์žˆ๊ณ ,

์ž์„ธํ•œ ๋ฉ”์„ธ์ง€๋“ค์€ exception.getResponse()["message"]์— ์กด์žฌํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
	catch(exception: HttpException, host: ArgumentsHost) {
		const ctx = host.switchToHttp();
		const response = ctx.getResponse<Response>();
		const request = ctx.getRequest<Request>();
		const status = exception.getStatus();
		const message = exception.getResponse()["message"] || exception.message;
		response.status(status).json({
			statusCode: status,
			message,
			path: request.url
		});
	}
}

 

7) Docker๋ฅผ ์ด์šฉํ•œ ๋ฐฐํฌ ๐Ÿณ

์ธ๊ณต์ง€๋Šฅ ์Šคํƒ€ํŠธ์—…์—์„œ ์ธํ„ด์„ ํ•  ๋•Œ, Docker๋ฅผ ์ฒ˜์Œ ์ ‘ํ–ˆ๋‹ค. ์ง์ ‘ image๋ฅผ ๋นŒ๋“œํ•œ๊ฑด ์•„๋‹ˆ์—ˆ๊ณ , ๋นŒ๋“œ๋œ ์ด๋ฏธ์ง€ ์—ฌ๋Ÿฌ๊ฐœ๋ฅผ ์‹คํ–‰์‹œ์ผœ๋ณด๊ณ , ์‹คํ–‰์ค‘์ธ ์ปจํ…Œ์ด๋„ˆ์— ์ ‘์†ํ•ด ํŒŒ์ผ๋ชฉ๋ก์„ ๋ณด๋Š” ์ •๋„์˜ ์ž‘์—…๋งŒ ์ˆ˜ํ–‰ํ–ˆ๋‹ค. 

ํ•œ ๋Œ€์˜ ์ปดํ“จํ„ฐ์—์„œ ๋…๋ฆฝ์ ์ธ ํ™˜๊ฒฝ์„ ๋งŒ๋“ค์–ด ์ด์šฉํ•ด ์„œ๋กœ ์ถฉ๋Œ์ด ์ผ์–ด๋‚˜์ง€ ์•Š๊ฒŒ ํ•˜๋Š” ์ ์ด ๋งค๋ ฅ์ ์ด์—ˆ๊ณ , ์ฐจ์ด์ ์ด ์žˆ๊ธด ํ•˜์ง€๋งŒ, ์•„๋‚˜์ฝ˜๋‹ค์˜ ๊ฐ€์ƒํ™˜๊ฒฝ๊ณผ ๋น„์Šทํ•œ ๊ฐœ๋…์ด๋ผ๋Š” ์ƒ๊ฐ๋„ ๋“ค์—ˆ๋‹ค. 

๋‚ด ์„œ๋ฒ„์—์„œ ์‹คํ–‰๋˜๊ณ  ์žˆ๋Š” 2๊ฐœ์˜ ๋„์ปค๋“ค!&amp;amp;nbsp;๐Ÿณ

[MySQL]

docker hub์—์„œ latest๋ฒ„์ „์˜ image๋ฅผ ๋‹ค์šด๋ฐ›์•„ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์ƒ์„ฑ ๋ฐ ์‹คํ–‰ํ–ˆ๋‹ค.

[Nestjs ์„œ๋ฒ„]

  • v12์˜ node image๋ฅผ ์ด์šฉ
    • ๊ฐœ๋ฐœ์‹œ ์ด์šฉํ•œ ๋ฒ„์ „์€ v14.x.x ์˜€์ง€๋งŒ, docker image๋“ค ๊ฐ€์žฅ ์ตœ์‹  LTS์ธ v12๋ฅผ ์‚ฌ์šฉํ•ด๋ณด์•˜๋Š”๋ฐ, ๋ฒ„์ „ ์ฐจ์ด์— ๋”ฐ๋ฅธ ๋ณ„๋‹ค๋ฅธ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์•„ ๊ทธ๋ƒฅ v12 image๋ฅผ ์ด์šฉํ–ˆ๋‹ค. 
    • ์ถ”ํ›„์— docker์˜ node image๋“ค์— ๋Œ€ํ•ด์„œ๋„ ์•Œ์•„๋ณผ ์˜ˆ์ •์ด๋‹ค.
  • ์•ˆ์ •์ ์ธ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์‹คํ–‰์„ ์œ„ํ•ด pm2๋ฅผ ์ด์šฉ
    • ๊ทธ๋ƒฅ pm2๊ฐ€ ์•„๋‹Œ ๋„์ปค์— ์ ํ•ฉํ•œ pm2-runtime ๋ช…๋ น์–ด๋ฅผ ์ด์šฉ
  • --output option์„ ์ด์šฉํ•ด, ํŠน์ • ์œ„์น˜์— logํŒŒ์ผ์ด ์ƒ๊ธธ ์ˆ˜ ์žˆ๋„๋ก ์ฒ˜๋ฆฌ

์ตœ์ข…์ ์œผ๋กœ, ์•„๋ž˜์™€ ๊ฐ™์€ Dockerfile์„ ์ž‘์„ฑํ•˜์—ฌ ์ด์šฉ

From node:12

# ์•ฑ ๋””๋ ‰ํ† ๋ฆฌ ์ƒ์„ฑ
WORKDIR /usr/src/app

COPY package*.json /usr/src/app/
RUN npm install
RUN npm install -g pm2
COPY . /usr/src/app/

RUN npm run build

EXPOSE 3000

CMD ["pm2-runtime", "start", "dist/main.js", "-i", "max", "--output", "/home/contents/log/main_out.log"]

# ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์„ค์ •

# DATABASE
ENV MYSQL_HOST ...
ENV MYSQL_USERNAME ...
ENV MYSQL_PASSWORD ...
ENV MYSQL_DATABASE ...

# JWT
ENV JWT_SECRET_KEY ...

 

์ด๋ฒˆ ๊ณผ์ œ๋ฅผ ํ†ตํ•ด ์ฒ˜์Œ์œผ๋กœ ์ง์ ‘ Docker image๋ฅผ ๋นŒ๋“œํ•ด๋ณด์•˜๋‹ค.

image๋ฅผ ๋นŒ๋“œํ•˜๋Š” ๊ณผ์ •์—์„œ ๊ต‰์žฅํžˆ ํƒˆ์ด ๋งŽ์•˜์œผ๋‚˜,, ์—ฌ๋Ÿฌ ๋ฒˆ์˜ ์‹œํ–‰์ฐฉ์˜ค ๋์— ์„ฑ๊ณตํ•˜๊ฒŒ ๋˜์–ด ๊ต‰์žฅํžˆ ํ–‰๋ณตํ–ˆ๋‹ค.

  1. COPY . . ์•ˆ๋˜๋Š” ๋ฌธ์ œ
    1. WORKDIR ๋ช…๋ น์–ด๋กœ ํด๋”๋ฅผ ์ง€์ •ํ•ด์ค€ ํ›„, COPY . .๋ฅผ ํ•ด์ฃผ๋ฉด ํ•ด๋‹น ํด๋”์— ์นดํ”ผ๊ฐ€ ๋˜๋Š” ์ค„ ์•Œ์•˜๋Š”๋ฐ, ๋˜์ง€ ์•Š์•„์„œ . ๋Œ€์‹ ์— ์ ˆ๋Œ€๊ฒฝ๋กœ๋ฅผ ์ ์–ด์ฃผ์—ˆ๋‹ค.
  2. pm2 ์‹คํ–‰ ํ›„ log๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์—†๋˜ ๋ฌธ์ œ
    1. pm2 monit์„ ์ด์šฉํ•ด ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ƒ๊ธฐ๋Š” log๋ฅผ ํ™•์ธํ•  ์ˆ˜๋Š” ์žˆ์—ˆ์ง€๋งŒ, ์ด์ „์— ์ƒ์„ฑ๋œ log๋“ค์„ ํ™•์ธํ•  ์ˆ˜ ์—†์—ˆ๋‹ค.
    2. pm2-runtime์œผ๋กœ ์‹คํ–‰ํ•˜๋‹ˆ defalut๋กœ clustor๊ฐ€ 2๊ฐœ ์ƒ์„ฑ๋˜์–ด์„œ ๊ทธ๋Ÿฐ์ง€ pm2 logs [์ด๋ฆ„]์œผ๋กœ log๋ฅผ ํ™•์ธํ•  ์ˆ˜๊ฐ€ ์—†์—ˆ๋‹ค. 
      1. ๋”ฐ๋ผ์„œ, pm2-rumtime ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•  ๋•Œ --output๋ช…๋ น์–ด๋กœ log๊ฒฝ๋กœ๋ฅผ ์ง€์ •ํ•ด์ฃผ์—ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋‹ˆ ํ•ด๋‹น ๊ฒฝ๋กœ์— logํŒŒ์ผ์ด ์ƒ๊ฒจ์„œ ์ง€๋‚œ log๋“ค์„ ์ •์ƒ์ ์œผ๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

 

์†Œ๊ฐ ์—ด ๋งˆ๋”” ๐ŸŒ™

์‹œ์ž‘๋ถ€๋ถ„์—๋„ ์ ์—ˆ์ง€๋งŒ, ํ”„๋ฆฌ์˜จ๋ณด๋”ฉ ์ฝ”์Šค์—์„œ ์ฒ˜์Œ์ด์ž ๋งˆ์ง€๋ง‰์ธ ๊ฐœ์ธ๊ณผ์ œ์˜€๋‹ค. 

ํ•œ ๋‹ฌ๋™์•ˆ 2๋ฐ•3์ผ๋„ ์•ˆ๋˜๋Š” ๊ธฐํ•œ์œผ๋กœ 6๊ฐœ์˜ ๊ณผ์ œ๋“ค์„ ๋ชฐ์•„์น˜๋“ฏ์ด ์ˆ˜ํ–‰ํ•˜๋ฉด์„œ, 

ํ˜‘์—… ๋ฐฉ์‹์ด๋‚˜, ๊ธฐ์ˆ ์ ์ธ ๋ฉด์—์„œ๋„ ๋งŽ์ด ์„ฑ์žฅํ–ˆ์ง€๋งŒ ๋‚ด๊ฐ€ ๋ฐฐ์šด๊ฒƒ๋“ค์„ ์†Œํ™”์‹œํ‚ค์ง€ ๋ชปํ•˜๊ณ  ๊พธ์—ญ๊พธ์—ญ ๋ฐ€์–ด ๋„ฃ๋Š” ๋Š๋‚Œ์ด ๋“ค ๋•Œ๊ฐ€ ์žˆ์—ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ๋งˆ์ง€๋ง‰ ๊ณผ์ œ๋ฅผ ๊ฐœ์ธ๊ณผ์ œ๋กœ ํ•˜๋ฉด์„œ, ๊ทธ ๋™์•ˆ ๋ฐฐ์› ๋˜ ๊ฒƒ๋“ค์„ ์ฒœ์ฒœํžˆ ์ •๋ฆฌํ•˜๊ณ , ๊ณต๋ถ€ํ•˜๋ฉด์„œ ๋Œ์•„๋ณผ ์ˆ˜ ์žˆ๋Š” ์‹œ๊ฐ„์ด ๋˜์–ด ์ฐธ ์ข‹์•˜๋‹ค. 

ํ”„๋ฆฌ์˜จ๋ณด๋”ฉ ์ฝ”์Šค ์ „๋ฐ˜์— ๋Œ€ํ•œ ํ›„๊ธฐ๋Š” ๋‚˜์ค‘์— ๋”ฐ๋กœ ํฌ์ŠคํŒ…์œผ๋กœ ๋‚จ๊ธธ ์˜ˆ์ •์ด๋‹ค.

 

์ด ๊ณผ์ œ์— ๋Œ€ํ•ด ๋” ์–˜๊ธฐํ•ด๋ณด์ž๋ฉด, 

Docker๋ฅผ ์ด์šฉํ•ด ๋ฐฐํฌํ•ด๋ณผ ์ˆ˜ ์žˆ์–ด ์ข‹์•˜๋‹ค. Docker ์จ๋ดค๋‹ค! nestjs๋ฅผ Docker๋กœ ๋ฐฐํฌํ•  ์ˆ˜ ์žˆ๋‹ค! ๋ผ๋Š” ์—ญ๋Ÿ‰ ํ•œ ์ค„์ด ์ƒ๊ฒผ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. 

๊ทธ๋ฆฌ๊ณ , ์ด์ „๋ถ€ํ„ฐ ๋Š๋ผ๋˜๊ฑฐ์ง€๋งŒ Nestjs๋ž‘ Spring์ด๋ž‘ ๋น„์Šทํ•œ ์ ์ด ๋งŽ์€ ๊ฒƒ ๊ฐ™๋‹ค. ์˜์กด์„ฑ ์ฃผ์ž… ๋ฐ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ๋“ฑ๋“ฑ..

๋‹ค๋งŒ Spring์„ ๊ฒ‰ํ•ฅ๊ธฐ์‹์œผ๋กœ๋งŒ ๋ฐฐ์›Œ์„œ, ์ •ํ™•ํžˆ ๋‘ ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋น„์Šทํ•˜๊ณ , ์–ด๋–ป๊ฒŒ ๋‹ค๋ฅธ์ง€ ๊ณต๋ถ€ํ•ด๋ด์•ผ๊ฒ ๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ๋‹ค. 

 

๋! 

 

๋ฐ˜์‘ํ˜•