Skip to content

ObO314/ObO-back

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

OBO - ์ผ์ •๊ด€๋ฆฌ ์›น ์„œ๋น„์Šค

ObO profile 34

TypeScript Nest.js FxTs jest PostgreSQL MikroORM AWS(EC2, RDS)

๊ธฐ๊ฐ„

  • 2023.03~2023.07

ํ”„๋กœ์ ํŠธ ์ธ์› ( ์ด 2๋ช… )

  • ํ”„๋ก ํŠธ์—”๋“œ : 1๋ช…
  • ๋ฐฑ์—”๋“œ : 1๋ช… (์œ ์˜์„)

  • ํ•ด๋‹น ๋ฌธ์„œ๋Š” ๋ฐฑ์—”๋“œ์— ๋Œ€ํ•ด์„œ๋งŒ ์„œ์ˆ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. *

  • ํ•ด๋‹น ๋ฌธ์„œ์— ์ฒจ๋ถ€๋œ ๋ชจ๋“  ์ด๋ฏธ์ง€๋Š” ์ง์ ‘ ์ œ์ž‘ํ•œ ์ž๋ฃŒ์ž…๋‹ˆ๋‹ค. *

  • Notion ์œผ๋กœ ๋” ํŽธํ•˜๊ฒŒ ๋ณด๊ธฐ => https://uiseok.notion.site/OBO-d6313b5578404684892def01e41dbf45?pvs=4 *


1. ํ”„๋กœ์ ํŠธ ๊ฐœ์š”

1-1. ์†Œ ๊ฐœ

OBO ํ”„๋กœ์ ํŠธ : One by One ์˜ ์•ฝ์ž๋กœ, ํ•˜๋ฃจํ•˜๋ฃจ๋ฅผ ๊ณ„ํš์ ์œผ๋กœ ๋ณด๋‚ด๊ธฐ ์œ„ํ•œ ํ”Œ๋ž˜๋„ˆ ์›น ์„œ๋น„์Šค์ด๋‹ค.

1-2. ๋ชฉ ํ‘œ

  • ๊ธฐ๋Šฅ์  ๋ชฉํ‘œ
    • ๋งŽ์€ ํด๋ฆญ๊ณผ ์ž…๋ ฅ์ด ํ•„์š”ํ–ˆ๋˜ ๊ธฐ์กด์˜ ํ”Œ๋ž˜๋„ˆ์™€ ๋‹ฌ๋ฆฌ, ๋‹จ์ˆœํ•œ ๋™์ž‘์œผ๋กœ ํ•˜๋ฃจ ๊ณ„ํš์„ ์‰ฝ๊ฒŒ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ํฐ ํ‹€ (์›”๊ฐ„, ์ฃผ๊ฐ„) ์—์„œ ์ž‘์„ฑํ•œ ์ผ์ •์„ ๋ชจ์•„, ์ž‘์€ ํ‹€ (์ผ์ผ๊ณ„ํš)์„ ์ƒ์„ธํžˆ ์ž‘์„ฑํ•˜๋Š” ํ˜•ํƒœ๋กœ ๊ตฌํ˜„ํ•œ๋‹ค.
  • ๊ธฐ์ˆ ์  ๋ชฉํ‘œ
    • ํ”„๋กœ์ ํŠธ์— ์•„ํ‚คํ…์ณ๋ฅผ ์ ์šฉํ•ด ๋ณธ๋‹ค.
    • ํ”„๋กœ์ ํŠธ์— ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ํŒจ๋Ÿฌ๋‹ค์ž„์„ ์ ์šฉํ•ด ๋ณธ๋‹ค.
    • ์„œ๋น„์Šค์— ์ ํ•ฉํ•˜๊ณ  ํšจ์œจ์ ์ธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์„ค๊ณ„ํ•œ๋‹ค.
    • ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๊ธฐ๋Šฅ์„ ๊ฒ€์ฆํ•œ๋‹ค.

1-3. ํ”„๋กœ์ ํŠธ ์š”๊ตฌ์‚ฌํ•ญ (์š”์•ฝ)

  • User
    • ์ผ๋ฐ˜ ํšŒ์›๊ฐ€์ž…๊ณผ ์†Œ์…œ ํšŒ์›๊ฐ€์ž…์ด ๊ฐ€๋Šฅํ•ด์•ผ ํ•œ๋‹ค.
  • Readme
    • ๊ฐœ์ธ์„ ์†Œ๊ฐœํ•˜๋Š” ๊ธ€์„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ๊ฐœ์ธ๊ณผ ์—ฐ๊ด€๋œ ํ•ด์‹œํƒœ๊ทธ๋ฅผ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ๋‹ค.
  • Todo
    • ๊ฐœ์ธ์˜ ํ•  ์ผ์„ ๋‚ ์งœ๋ณ„๋กœ ์ž‘์„ฑ ๋ฐ ์กฐํšŒ๊ฐ€ ๊ฐ€๋Šฅํ•ด์•ผ ํ•œ๋‹ค.
    • ์™„๋ฃŒ์—ฌ๋ถ€๋ฅผ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ๋‹ค.
  • Routine
    • ๋ฐ˜๋ณต์ ์ธ ์ผ๊ณผ๋ฅผ ๋“ฑ๋กํ•˜์—ฌ ์ž๋™์œผ๋กœ ์ผ์ผ๊ณ„ํšํ‘œ์— ํ‘œ์‹œ๋˜์–ด์•ผ ํ•œ๋‹ค.
    • ๋‚ ์งœ ๋ณ„๋กœ ์™„๋ฃŒ์—ฌ๋ถ€๋ฅผ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ๋ฃจํ‹ด์ด ์‚ญ์ œ๋˜์–ด๋„, ์ด์ „๋‚ ์งœ์˜ ๊ธฐ๋ก์€ ์ง€์›Œ์ง€์ง€ ์•Š์•„์•ผ ํ•œ๋‹ค.
  • Circle
    • ๊ณตํ†ต์ ์ธ ์—…๋ฌด๋ฅผ ํ•˜๋Š” ์œ ์ €๋“ค๋ผ๋ฆฌ ๋ชจ์—ฌ ๊ทธ๋ฃน์„ ์ƒ์„ฑํ•œ๋‹ค.
    • ๋ฉค๋ฒ„์— ๋“ฑ๊ธ‰์„ ๋ถ€์—ฌํ•˜์—ฌ, ์—…๋ฌด์˜ ์ž‘์„ฑ๊ณผ ์‚ญ์ œ ๊ถŒํ•œ์„ ํ• ๋‹นํ•  ์ˆ˜ ์žˆ๋‹ค.
  • Work
    • ์„œํด์„ ๊ตฌ์„ฑํ•œ ์ธ์›๋“ค๋ผ๋ฆฌ ์—…๋ฌด์˜ ์ผ์ •๊ณผ ๋‚ด์šฉ์„ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ๋ฉค๋ฒ„ ๋ณ„๋กœ ์™„๋ฃŒ ์—ฌ๋ถ€๋ฅผ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ์™„๋ฃŒํ•œ ๋ฉค๋ฒ„๋ฅผ ์นด์šดํŠธํ•˜์—ฌ ์ง„์ฒ™๋„๋ฅผ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ๋‹ค.

2. ํŒจ๋Ÿฌ๋‹ค์ž„๊ณผ ์•„ํ‚คํ…์ณ

2-1. ์ด์ „ ํ”„๋กœ์ ํŠธ๋“ค์˜ ๋ฌธ์ œ์ 

  • ์•„ํ‚คํ…์ณ์˜ ๋ถ€์žฌ
    • ์•„ํ‚คํ…์ณ์˜ ๋ถ€์žฌ๋กœ ์ธํ•˜์—ฌ, ์ˆ˜์ • ์‹œ ์ฝ”๋“œ๊ฐ€ ์–ด๋А๋ฒ”์œ„๊นŒ์ง€ ์˜ํ–ฅ์„ ๋ฏธ์น˜๋Š”์ง€ ์ง์ ‘ ๋„“์€ ๋ฒ”์œ„๋ฅผ ํ™•์ธํ•ด์•ผ ํ•จ
    • ์ฝ”๋“œ์˜ ๋ช…ํ™•ํ•œ ์—ญํ• ๋ถ„๋ฆฌ์™€ ์œ ์ง€๋ณด์ˆ˜์„ฑ์˜ ํ–ฅ์ƒ์„ ์œ„ํ•˜์—ฌ ์•„ํ‚คํ…์ณ์˜ ์ ์šฉ์ด ํ•„์š”
  • ํŒจ๋Ÿฌ๋‹ค์ž„์˜ ๋ถ€์žฌ
    • ์ฝ”๋“œ๊ฐ€ ์„ ์–ธํ˜•์œผ๋กœ ์ž‘์„ฑ๋  ๊ฒฝ์šฐ, ๋‹ค์–‘ํ•œ ๋ณ€์ˆ˜๊ฐ€ ์–ด๋””์—์„œ ์‚ฌ์šฉ๋˜๋Š”์ง€ ํ™•์ธํ•ด์•ผ ํ•˜๋ฏ€๋กœ ๊ฐ€๋…์„ฑ์ด ๋–จ์–ด์ง
    • ๋งค์„œ๋“œ๊ฐ€ ์–ด๋–ป๊ฒŒ ๊ธฐ๋Šฅ์„ ํ•˜๋Š” ์ง€ ํ•œ ๋ˆˆ์— ์•Œ์•„๋ณด๊ธฐ ์–ด๋ ค์›€
    • ๋‹ค์–‘ํ•œ ๋ฉ”์„œ๋“œ๊ฐ€ ๋ถ€์ˆ˜ํšจ๊ณผ๋ฅผ ์ผ์œผํ‚ค๋ฏ€๋กœ, ์ด๋ฅผ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋Š” ํŒจ๋Ÿฌ๋‹ค์ž„์ด ํ•„์š”
    • ์ž์—ฐ์Šค๋Ÿฌ์šด ํ๋ฆ„์œผ๋กœ ์ดํ•ด๊ฐ€ ๊ฐ€๋Šฅํ•œ ์ฝ”๋“œํ˜•ํƒœ๋กœ ๊ฐœ์„ ์ด ํ•„์š”

2-2. ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ

  • ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ
    • ์ˆœ์ˆ˜ํ•จ์ˆ˜๋“ค์„ ์กฐํ•ฉํ•˜์—ฌ, ๋ถ€์ˆ˜ํšจ๊ณผ๋ฅผ ์ œ์–ดํ•˜๋Š” ํ”„๋กœ๊ทธ๋ž˜๋ฐ ํ˜•ํƒœ
    • ๋ฉ”์„œ๋“œ์— ๋Œ€ํ•ด ์˜ํ–ฅ์„ ๋ผ์น˜๋Š” ๋ฒ”์œ„๋ฅผ ํ•จ์ˆ˜๋‚ด๋ถ€๋กœ ์ œํ•œํ•˜๊ณ , ์ด๋Ÿฐ ํ˜•ํƒœ์˜ ํ•จ์ˆ˜๋“ค์„ ์—ฎ์–ด ์—ญํ• ๊ณผ ๋‹จ๊ณ„๊ฐ€ ๋ช…ํ™•ํ•ด ์ง€๋„๋ก ์ ์šฉํ•จ.
  • Fxts
    • ์ดํ„ฐ๋ ˆ์ดํ„ฐ๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์ง€์—ฐํ‰๊ฐ€๋ฅผ ๊ตฌํ˜„ํ•œ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ํŒจ๋Ÿฌ๋‹ค์ž„์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
    • ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ฃผ์š” ๋งค์„œ๋“œ์˜ ๊ตฌํ˜„์„ ์ž์„ธํžˆ ์—ฐ๊ตฌํ•˜์—ฌ ํ™œ์šฉ

2-3. ํ—ฅ์‚ฌ๊ณ ๋‚  ์•„ํ‚คํ…์ณ

ObO ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ์•„ํ‚คํ…์ณ๋กœ โ€˜ํฌํŠธ ์•ค ์–ด๋Œ‘ํ„ฐโ€™ ๋ผ๊ณ  ๋ถˆ๋ฆฌ๋Š” โ€˜ํ—ฅ์‚ฌ๊ณ ๋‚  ์•„ํ‚คํ…์ณโ€™๋ฅผ ์ ์šฉํ–ˆ๋‹ค. ์ด ์•„ํ‚คํ…์ณ๋ฅผ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๋ ˆ์ด์–ด๋“œ ์•„ํ‚คํ…์ณ์™€ ๋น„๊ตํ•˜๊ณ , NestJS์—์„œ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ™œ์šฉํ–ˆ๋‹ค.

  • ๋ ˆ์ด์–ด๋“œ ์•„ํ‚คํ…์ณ์™€ ํ—ฅ์‚ฌ๊ณ ๋‚  ์•„ํ‚คํ…์ณ์˜ ๋น„๊ต แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2023-07-08 แ„‹แ…ฉแ„’แ…ฎ 9 00 39

    2-3-1) 3-layer ์•„ํ‚คํ…์ณ

    • ๋ ˆ์ด์–ด๋“œ ์•„ํ‚คํ…์ณ๋Š” ๊ฐ ๊ณ„์ธต์ด ์—ญํ•  ๋ณ„๋กœ ๋ถ„๋ฆฌ๋˜์–ด ์žˆ์ง€๋งŒ, ์ƒ์œ„ ๊ณ„์ธต์ด ํ•˜์œ„๊ณ„์ธต์— ๋งค์šฐ ์˜์กด์ ์ด๋‹ค.
    • nestJS์—์„œ๋Š” ์˜์กด์„ฑ ์ฃผ์ž…์„ ํ†ตํ•ด ๋ ˆ์ด์–ด๋“œ ์•„ํ‚คํ…์ณ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š”๋ฐ, ์ด๋Š” ํด๋ž˜์Šค๋“ค์˜ ๊ฒฐ์†์„ ๊ฐ•ํ•˜๊ฒŒ ๋งŒ๋“ ๋‹ค.
    แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2023-07-08 แ„‹แ…ฉแ„’แ…ฎ 9 00 54

    2-3-2) ํ—ฅ์‚ฌ๊ณ ๋‚  ์•„ํ‚คํ…์ณ

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

    แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2023-07-08 แ„‹แ…ฉแ„’แ…ฎ 9 01 03

    2-3-3) ์ธํ„ฐํŽ˜์ด์Šค๋กœ ๊ตฌํ˜„ํ•œ ํ—ฅ์‚ฌ๊ณ ๋‚  ์•„ํ‚คํ…์ณ

    • ํ—ฅ์‚ฌ๊ณ ๋‚  ์•„ํ‚คํ…์ณ๋ฅผ nestJS๋ฅผ ํ†ตํ•ด ๊ตฌํ˜„ํ•˜๋ ค๋ฉด ์œ„์™€ ๊ฐ™์€ ํ˜•ํƒœ๋กœ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๊ฒ ๋‹ค.
    • ๊ฐ ๊ณ„์ธต์€ ํฌํŠธ์— ํ•ด๋‹นํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜์—ฌ ์ƒ์œ„๊ณ„์ธต์— ์ฃผ์ž…๋œ๋‹ค.
    • ๋”ฐ๋ผ์„œ ๊ฐ ๊ณ„์ธต์€ ํƒ€ ๊ณ„์ธต์„ ์‹ ๊ฒฝ์“ธ ํ•„์š” ์—†์ด, ์ž์‹ ์—๊ฒŒ ์—ฐ๊ฒฐ๋œ ์ธํ„ฐํŽ˜์ด์Šค๋งŒ ์‹ ๊ฒฝ์“ฐ๋ฉด ๋œ๋‹ค.
    • ์ถ”ํ›„ ๋น„์ฆˆ๋‹ˆ์Šค๋กœ์ง์— ์—ฐ๊ฒฐํ•ด์•ผํ•  DB๊ฐ€ ๋ณ€๊ฒฝ๋œ๋‹ค๋ฉด, ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์€ ๊ทธ๋Œ€๋กœ ๋‘๊ณ  ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ถฉ์กฑ์‹œํ‚ค๋Š” DB๋ฅผ ์—ฐ๊ฒฐํ•˜๋ฉด ๋˜๋Š” ๊ฒƒ์ด๋‹ค. ๋”ฐ๋ผ์„œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์— ๋”์šฑ ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ๋‹ค.



3. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค๊ณ„

3-1. ERD

  • ERD ๋‹ค์ด์–ด๊ทธ๋žจ

    erd

    3-1-1) ERD ๋‹ค์ด์–ด๊ทธ๋žจ. ์•ฝ 20์—ฌ๊ฐœ์˜ ํ…Œ์ด๋ธ”์ด ์—ฐ๊ฒฐ๋˜์–ด ์žˆ๋‹ค. (ํด๋ฆญ ์‹œ ํฌ๊ฒŒ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.)

3-2. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค๊ณ„ ์‹œ ์ค‘์ ์‚ฌํ•ญ

  • ์ •๊ทœํ™”
    • ๊ฐ๊ฐ์˜ ํ…Œ์ด๋ธ”์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ƒ์„ฑํ•˜์˜€๋‹ค.
      1. ๊ธฐ๋Šฅ๋ณ„๋กœ ๊ธฐ์ค€ํ…Œ์ด๋ธ”์„ ์ƒ์„ฑ. ( Users, Todos, Routines, Circles, Worksโ€ฆ)

      2. ๊ธฐ์ค€ํ…Œ์ด๋ธ”์—์„œ ํ•„์š”ํ•œ ๋ชจ๋“  ์ปฌ๋Ÿผ์„ ๋‚˜์—ด

        ์˜ˆ) Routine :

          id, user, name, updated_at, description, startTime, endTime, is_active, date..
        
      3. ํ‚ค๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๊ฐ ํŠœํ”Œ์ด ๊ณ ์œ ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ปฌ๋Ÿผ๋“ค์„ ๋ถ„๋ฆฌ (์ •๊ทœํ™”)

        ์˜ˆ) Routine :

        • routine (๋ฃจํ‹ด ์ž์ฒด์˜ ์ •๋ณด) : id, user, name, description
        • routine_histories (์œ ์ €๊ฐ€ ๋ฃจํ‹ด์„ ์ˆ˜์ •ํ•œ ๊ธฐ๋ก) : routine, updated_at, start_Time, end_Time, is_active
        • routine_records (์œ ์ €๊ฐ€ ๋ฃจํ‹ด์„ ์ดํ–‰ํ–ˆ๋Š”์ง€ ๊ธฐ๋ก) : routine, date
      4. ์ธ๋ฑ์‹ฑ์„ ๊ณ ๋ คํ•˜์—ฌ ํ•„์š”ํ•œ ์ปฌ๋Ÿผ์„ ํ…Œ์ด๋ธ”์— ์ถ”๊ฐ€

        ์˜ˆ) routine_histories๋Š” routine๊ณผ joinํ•˜๋ฉด user๋ฅผ ์•Œ ์ˆ˜ ์žˆ์ง€๋งŒ, user ๋ณ„๋กœ ์ •๋ ฌ๋˜์–ด ์žˆ์ง€ ์•Š์œผ๋ฉด ์œ ์ €๋กœ ์—ฐ๊ด€๋œ ๋ฐ์ดํ„ฐ๋“ค์„ ์ฐพ๋Š”๋ฐ ์˜ค๋ž˜๊ฑธ๋ฆฐ๋‹ค. ๋”ฐ๋ผ์„œ routine_histories์— user๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ์ธ๋ฑ์‹ฑ์„ ํ•˜๊ณ , ์ฟผ๋ฆฌ ์†Œ์š”์‹œ๊ฐ„์„ ์ค„์ด๊ณ ์ž ํ–ˆ๋‹ค.

3-3. PostgreSQL ๊ณผ MikroORM

  • PostgreSQL

    แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2023-07-08 แ„‹แ…ฉแ„’แ…ฎ 8 58 51
    • DBMS ์˜ ๊ฒฝํ—˜์ด ํ’๋ถ€ํ•˜์ง€ ์•Š์€ ์ƒํƒœ์—์„œ ์ ํ•ฉํ•œ RDMS ๋ฅผ ๊ณ ๋ฅด๋Š” ๊ฒƒ์€ ๋งค์šฐ ์–ด๋ ค์šด ์ผ์ด๋‹ค. ๋”ฐ๋ผ์„œ, ๊ธฐ์กด์— ์‚ฌ์šฉํ•ด ๋ณธ MongoDB์™€ ๊ฐ™์ด JSON ๋ฐ์ดํ„ฐ๋ฅผ ์™„์ „ํžˆ ์ง€์›ํ•˜๊ณ , ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ˜•ํƒœ๋ฅผ ์ง€๋‹Œ ๋ฌด๋ฃŒ์˜ PostgreSQL์„ ์„ ์ •ํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค. ( ๊ทธ๋Ÿฌ๋‚˜ ์‹ค์ œ๋กœ JSON ๋ฐ์ดํ„ฐ๋ฅผ ์ง์ ‘ DBMS์— ๋„ฃ์–ด ์‚ฌ์šฉํ•˜๋Š” ์ผ์€ ์—†์—ˆ์œผ๋ฉฐ, ORM ์„ ํ†ตํ•ด ๊ด€๋ฆฌํ•˜์˜€๋‹ค. ) ๋˜ํ•œ PostgreSQL์€ ์˜คํ”ˆ์†Œ์Šค DBMS์ด๋ฏ€๋กœ, ์ปค๋ฎค๋‹ˆํ‹ฐ์˜ ๋„์›€๊ณผ ์ด์Šˆ์˜ ๋น ๋ฅธํ•ด๊ฒฐ์„ ๊ธฐ๋Œ€ํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๋ถ€๋ถ„์„ ์ฐธ๊ณ ํ–ˆ๋‹ค.
  • MikroORM

    แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2023-07-08 แ„‹แ…ฉแ„’แ…ฎ 8 59 01
    • NestJS ์„œ๋ฒ„ ๋‚ด์—์„œ ์ž˜ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด, ๊ณต์‹๋ฌธ์„œ ์ƒ์— ์žˆ๋Š” ORM ์ค‘ ํ•˜๋‚˜๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ๋กœ ํ–ˆ๋‹ค.
    • TypeORM์€ ์ธ๊ธฐ์žˆ๋Š” ORM์œผ๋กœ ๊ด€๋ จ์ž๋ฃŒ๋„ ๋งŽ์ง€๋งŒ, ๋ฉ”์ด์ € ๋ฒ„์ „์ด ๋‚˜์˜ค์ง€ ์•Š์€ ์ƒํƒœ๋กœ ๋ฉ”์ธํ…Œ์ด๋„ˆ๊ฐ€ ๋” ์ด์ƒ ์‹œ๊ฐ„์„ ํˆฌ์žํ•˜์ง€ ๋ชปํ•˜๋Š” ์ด์Šˆ๊ฐ€ ์˜ฌ๋ผ ์˜จ ์ ์ด ์žˆ์—ˆ๋‹ค. Prisma๋Š” ์ผ๋ถ€ ์ œํ•œ๋œ ๊ธฐ๋Šฅ์— ๋Œ€ํ•ด ๋ฌด๋ฃŒ๋กœ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ–ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๊ณต์‹๋ฌธ์„œ๊ฐ€ ๋ณด๊ธฐ ์‰ฝ๊ณ , ๋ฌด๋ฃŒ๋ฒ„์ „์ธ MikroORM์„ ์ฑ„ํƒํ–ˆ๋‹ค.
    • ๊ฐœ๋ฐœ ๊ณผ์ •์—์„œ MikroORM์—์„œ๋Š” Entity Manager๋ผ๋Š” ๊ธฐ๋Šฅ์„ ์ง€์›ํ•˜๋Š”๋ฐ, ์ด๋Š” ์—”ํ‹ฐํ‹ฐ๋ณ„ Repository๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ์ธ์Šคํ„ด์Šคํ™” ํ•˜์ง€ ์•Š์•„๋„, Entity Manager ์ธ์Šคํ„ด์Šค ํ•˜๋‚˜๋งŒ์œผ๋กœ ๋ชจ๋“  ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ „์ฒด์ ์œผ๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค. ๋˜ํ•œ NestJS์—์„œ์˜ ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์„ ์ง€์›ํ•œ๋‹ค. (์„œ๋กœ ๋‹ค๋ฅธ ํด๋ž˜์Šค์— ์ฃผ์ž…๋œ em ์ธ์Šคํ„ด์Šค์˜ ์ฃผ์†Œ๊ฐ€ ์ผ์น˜ํ•จ์„ ํ™•์ธ)
  • Entity Generator

    • PostgreSQL ์—์„œ ์ง€์›ํ•˜๋Š” ๊ด€๋ฆฌํ”„๋กœ๊ทธ๋žจ์ธ PgAdmin4 ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ…Œ์ด๋ธ”(์—”ํ‹ฐํ‹ฐ)๋ฅผ ์ •์˜ํ–ˆ๋‹ค.

    • ์ด์–ด์„œ MikroORM์—์„œ ์ง€์›ํ•˜๋Š” generate ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•˜์—ฌ ์„œ๋ฒ„ ๋‚ด ์—”ํ‹ฐํ‹ฐ๋ฅผ ์ƒ์„ฑํ–ˆ๋‹ค.

      npx mikro-orm generate-entities --save --path=./src/database/entities



4. ์ธ์ฆ๊ณผ ์ธ๊ฐ€

4-1. Dynamic Guard

  • Guard์˜ ๊ตฌํ˜„

    • NestJS ๊ณต์‹๋ฌธ์„œ์—์„œ๋Š” ๊ฐ Strategy ๋ณ„ ๊ฐ€๋“œ๋ฅผ ์‰ฝ๊ฒŒ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋„๋ก ๋•๋Š”๋‹ค.

      export class JwtAuthGuard extends AuthGuard('jwt')
      
      export class JwtAuthGuard extends AuthGuard(['strategy_1','...']) { ... }
    • ๊ทธ๋Ÿฌ๋‚˜ ์œ„์™€ ๊ฐ™์€ ๋ฐฉ๋ฒ•์œผ๋กœ๋งŒ ์‚ฌ์šฉํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฌธ์ œ์ ์ด ์žˆ๋‹ค.

      1. ๋‹จ์ผ ์ „๋žต์„ ์‚ฌ์šฉํ•˜๋Š” Guard๋ฅผ ์ƒ์„ฑํ•  ๊ฒฝ์šฐ, ๊ฐ ๊ฐ€๋“œ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๊ฐ€ ๋ถ™๋Š” api๋ฅผ ๊ฐ๊ฐ ๋งŒ๋“ค์–ด ์ฃผ์–ด์•ผํ•œ๋‹ค.
      2. 1๋ฒˆ์˜ ๋‹จ์ ์„ ๊ทน๋ณตํ•˜๊ธฐ ์œ„ํ•ด ์ „๋žต๋ฐฐ์—ด์„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ๊ฐ ์†Œ์…œ๋กœ๊ทธ์ธ๋งˆ๋‹ค ์ง€์›ํ•˜๋Š” ๊ธฐ๋Šฅ์ด ์–ด๋–ป๊ฒŒ ๋‹ค๋ฅธ ์ง€ ์•Œ ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ์ถ”๊ฐ€์ ์ธ ์ˆ˜์ •์†Œ์š”๊ฐ€ ์˜ˆ์ƒ๋œ๋‹ค.
    • ๋”ฐ๋ผ์„œ ๋‹จ์ผ ๊ฐ€๋“œ๋กœ ๊ตฌํ˜„ํ•˜๋˜, ๊ฐ€๋“œ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” CanActivate๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋”ฉ ํ•˜์—ฌ ๋™์ ์ธ โ€˜๋‹ค์ด๋‚˜๋ฏน ๊ฐ€๋“œโ€™ ๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค.

  • ๋‹ค์ด๋‚˜๋ฏน ๊ฐ€๋“œ์˜ ํ˜•ํƒœ

    แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2023-07-08 แ„‹แ…ฉแ„’แ…ฎ 11 15 06
  • ๋‹ค์ด๋‚˜๋ฏน ๊ฐ€๋“œ์˜ ๊ตฌํ˜„

    โ†—๏ธ auth.dynamic.guard.ts

    @Injectable()
    export class DynamicAuthGuard implements CanActivate {
      constructor(
        private localAuthGuard: AuthLocalGuard,
        private googleAuthGuard: AuthGoogleGuard,
      ) {}
    
      canActivate(
        context: ExecutionContext,
      ): boolean | Promise<boolean> | Observable<boolean> {
        const guard = this.getGuard(context);
        return guard ? guard.canActivate(context) : false;
      }
    
      private getGuard(context: ExecutionContext): CanActivate {
        const request = context.switchToHttp().getRequest();
    
        switch (request.path.split('/').pop()) {
          case 'local':
            return this.localAuthGuard;
          case 'google':
            return this.googleAuthGuard;
          default:
            return null;
        }
      }
    }

4-2. ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ

  • ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ

    • ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ์€ ๋กœ๊ทธ์ธ์‹œ ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ์„ ์ƒ์„ฑํ•˜์—ฌ DB์— ์ €์žฅํ•˜๊ณ , ๋กœ๊ทธ์•„์›ƒ ์‹œ ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ์„ DB์—์„œ ์‚ญ์ œํ•˜๋„๋ก ๊ตฌํ˜„ํ–ˆ๋‹ค.
    • ํ”„๋ก ํŠธ์„œ๋ฒ„์—์„œ ์•ก์„ธ์Šค ํ† ํฐ์ด ๋งŒ๋ฃŒ๋˜์–ด ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ์„ ๋ฐฑ์—”๋“œ ์„œ๋ฒ„์— ์ œ์‹œํ–ˆ์„ ๊ฒฝ์šฐ ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ์„ ๋ฐ›๊ณ  ์›๋ž˜ ํ•˜๋ ค๋˜ api ์š”์ฒญ์„ ๋ฐ˜๋ณตํ•˜์ง€ ์•Š๋„๋ก, ํ•œ ๋ฒˆ์˜ ์š”์ฒญ์œผ๋กœ ๋ฆฌํ”„๋ ˆ์‹œ ํ† ํฐ ๋ฐœ๊ธ‰๊ณผ api ์‘๋‹ต์„ ์ „์†กํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌํ˜„ํ–ˆ๋‹ค.
    แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2023-07-08 แ„‹แ…ฉแ„’แ…ฎ 11 45 25

    โ†—๏ธ auth.jwt.guard.ts

    // AuthJwtGuard.ts
    
    export class AuthJwtGuard extends AuthGuard('jwt') implements CanActivate {
      constructor() {
        super();
      }
      async canActivate(context: ExecutionContext): Promise<boolean> {
        const isValid = (await super.canActivate(context)) as boolean;
    
        const request = context.switchToHttp().getRequest();
        const response = context.switchToHttp().getResponse();
        const newAccessToken = request.user.newAccessToken;
        const userId: string = request.user.userId;
    
        if (newAccessToken) {
          response.setHeader('Authorization', `Bearer ${newAccessToken}`);
        }
        request.user = userId;
    
        return isValid;
      }
    }

    โ†—๏ธ auth.jwt.strategy.ts

    // AuthJwtStrategy.ts
    
    async validate(
        payload: AuthJwtValidateInboundPortInputDto,
      ): Promise<AuthJwtValidateInboundPortOutputDto> {
        const { userId, tokenType, iat, exp } = payload;
        switch (tokenType) {
          case 'ACCESS':
            return { undefined, userId };
          case 'REFRESH':
            const foundUserToken =
              await this.authFindRefreshTokenOutboundPort.execute({
                userId: userId,
              });
            if (!foundUserToken) {
              throw new HttpException(
                '๋‹ค์‹œ ๋กœ๊ทธ์ธํ•˜์—ฌ ์ฃผ์‹ญ์‹œ์˜ค.',
                HttpStatus.BAD_REQUEST,
              );
            }
            const savedUser = foundUserToken.user.id;
            const savedToken = foundUserToken.token;
    
            const validateUser = userId == savedUser;
            const decodedToken = this.jwtService.verify(savedToken);
    
            const validateToken =
              iat == decodedToken.iat && exp == decodedToken.exp;
    
            if (validateUser && validateToken) {
              const newAccessToken = this.jwtService.sign(
                { userId: userId, tokenType: 'ACCESS' },
                { expiresIn: '30m' },
              );
              return { newAccessToken, userId };
            } else {
              throw new HttpException(
                '๋น„์ •์ƒ์ ์ธ ์ ‘๊ทผ์ž…๋‹ˆ๋‹ค.',
                HttpStatus.BAD_REQUEST,
              );
           }
        }
      }



5. ์„œ๋น„์Šค๋กœ์ง ๊ตฌํ˜„

๐Ÿ’ก ์„œ๋น„์Šค๋Š” ํฌ๊ฒŒ `User`, `Readme`, `Todo`,`Routine`, `Circle`, `Work` ๋“ฑ ์˜ ๋ถ„์•ผ๋กœ ๊ตฌ๋ถ„๋˜์–ด ์žˆ๋‹ค.

๊ทธ ์ค‘ ์œ ์ €์ •๋ณด, User, Readme, Todo๋Š” ๋‹จ์ˆœ CRUD ์˜ ๊ธฐ๋Šฅ์œผ๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ,

์—ฌ๊ธฐ์„œ๋Š” ์–ด๋А์ •๋„ ์„œ๋น„์Šค ๋กœ์ง์ด ๊ตฌํ˜„๋œ Routine๊ณผ Circle์— ๋Œ€ํ•ด ์„ค๋ช…ํ–ˆ๋‹ค.

5-1. ๋ฃจํ‹ด ๊ธฐ๋Šฅ

  • ๋ฃจํ‹ด์˜ ๊ธฐ๋Šฅ

    • ์œ ์ €๊ฐ€ ๋งค์ผ ํŠน์ •์‹œ๊ฐ„์— ๋ฐ˜๋ณตํ•˜๋Š” ํ•  ์ผ์„ ์ƒ์„ฑํ•œ๋‹ค.
    • ์ƒ์„ฑ๋œ ๋ฃจํ‹ดํ•ญ๋ชฉ์€ ์ผ์ผํŽ˜์ด์ง€์— ์‹œ๊ฐ„๋Œ€์— ๋งž์ถฐ ์ž๋™์œผ๋กœ ํ‘œ๊ธฐ๋œ๋‹ค.
    • ๋‚ ๋งˆ๋‹ค ๋ฃจํ‹ด์„ ์‹ค์‹œ ํ–ˆ๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ํ‘œ๊ธฐํ•  ์ˆ˜ ์žˆ๋‹ค.
    • ๋ฃจํ‹ด์„ ์ดํ–‰ํ•ด ์˜ค๋‹ค๊ฐ€ ์–ด๋А ๋‚  ์‹œ๊ฐ„๋Œ€๋ฅผ ์ˆ˜์ •ํ•ด๋„, ์ด์ „ ๋‚ ์˜ ์ˆ˜ํ–‰๊ธฐ๋ก ๋ฐ ์‹œ๊ฐ„์€ ๋ฐ”๋€Œ์ง€ ์•Š๋Š”๋‹ค.
    • ๋ฃจํ‹ด์„ ์ดํ–‰ํ•ด ์˜ค๋‹ค๊ฐ€ ์–ด๋А ๋‚  ์‚ญ์ œํ•ด๋„, ์ด์ „ ๋‚ ์˜ ์ˆ˜ํ–‰๊ธฐ๋ก์€ ์‚ญ์ œ๋˜์ง€ ์•Š๋Š”๋‹ค.
  • ๊ณ ๋ ค์‚ฌํ•ญ

    • ๋ฃจํ‹ด์€ ๋งค์ผ ์ƒ์„ฑํ•˜๋Š” ์ผ๊ณผ ์ด์ง€๋งŒ, ์ž๋™์ ์œผ๋กœ ์ผ์ผ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ์‹œํ–‰์—ฌ๋ถ€๋ฅผ boolean์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋ฉด DB๊ฐ€ ๋งค์šฐ ๋‚ญ๋น„๋  ๊ฒƒ์ด๋‹ค. ( ๋ฐฉ์น˜ํ•œ ๋ฃจํ‹ด๋„ ๋งค์ผ ์˜๋ฏธ์—†๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์‚ฐํ•˜๊ฒŒ ๋˜๋ฏ€๋กœ )
    • ๋ฃจํ‹ด์„ ์ˆ˜์ •ํ•ด๋„ ์ด์ „ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฐ”๋€Œ๋ฉด ์•ˆ ๋˜๋Š” ๊ฒƒ์€, ๊ทธ ๋™์•ˆ์˜ ์ˆ˜์ • ๋‚ด์—ญ์„ ์ €์žฅํ•ด์•ผ ํ•จ์„ ์˜๋ฏธํ•œ๋‹ค.
    • ํŠน์ • ๋‚ ์งœ์˜ ๋ฃจํ‹ด ์ดํ–‰์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•˜๋ ค๋ฉด, ๊ฐ๊ฐ ์ˆ˜์ •์‹œ์ ์ด ๋‹ค๋ฅธ ๋ฃจํ‹ด์˜ ์ •๋ณด๋ฅผ ๋ฐ›์•„์™€์•ผ ํ•œ๋‹ค. ( A๋ฃจํ‹ด์€ 3์›” 1์ผ, B ๋ฃจํ‹ด์€ 5์›” 14์ผ์— ์ˆ˜์ •ํ•œ ์ƒํƒœ์—์„œ 4์›” 30์ผ์˜ ์ดํ–‰์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•˜๋ ค๋ฉด, A๋Š” ์ˆ˜์ •๋œ ํ›„์˜ ์ •๋ณด, B๋Š” ์ˆ˜์ •๋˜๊ธฐ ์ „์˜ ์ •๋ณด๋ฅผ ๋ถˆ๋Ÿฌ์™€์•ผ ํ•จ. )
  • ์—”ํ‹ฐํ‹ฐ ์„ค๊ณ„

    แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2023-07-09 แ„‹แ…ฉแ„’แ…ฎ 3 27 08

    5-1-1) ๋ฃจํ‹ด์˜ ํ…Œ์ด๋ธ”

    • Routines : ๋ฃจํ‹ด์˜ ์ด๋ฆ„๊ณผ ๋‚ด์šฉ์ด ๊ธฐ๋ก. ๋ฃจํ‹ด ์ž์ฒด์ด๋ฏ€๋กœ id๋กœ ๊ตฌ๋ณ„ ๊ฐ€๋Šฅ.
    • RoutineHistory : ์ด์ „์˜ ์ˆ˜์ •๋‚ด์—ญ์„ ์ €์žฅ. ์–ธ์ œ ์ˆ˜์ •์ด ์ด๋ฃจ์–ด ์กŒ๋Š”์ง€๋กœ ๊ตฌ๋ถ„.
    • RoutineRecords : ์ผ์ผ ์ดํ–‰์—ฌ๋ถ€๋ฅผ ๊ธฐ๋ก. ์‹œํ–‰์—ฌ๋ถ€๋Š” ๋ฃจํ‹ด-๋‚ ์งœ ์Œ์ด ์กด์žฌํ•˜๋Š” ์ง€ ์—ฌ๋ถ€๋กœ ํ™•์ธ.
  • ์ธ๋ฑ์‹ฑ

    • RoutineHistory : user ์ปฌ๋Ÿผ์— ๋Œ€ํ•ด b-tree๋กœ ์ƒ์„ฑ
      • ์œ ์ €๊ฐ€ ์ž์‹ ์˜ ๋ฃจํ‹ด์„ ๊ฒ€์ƒ‰ํ•  ๋•Œ, ๋ฃจํ‹ด์„ ๋‚ ์งœ ๊ธฐ์ค€์œผ๋กœ ์ตœ์‹  ์ •๋ณด๋ฅผ ์ฐพ์•„์™€์•ผ ํ•œ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๋ฃจํ‹ด์ด id๋กœ ์ •๋ ฌ๋˜์–ด ์žˆ๋‹ค๋ฉด, ์ž์‹ ์˜ ๋ฃจํ‹ด์„ ์ฐพ๊ธฐ์œ„ํ•ด์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ๋ชจ๋“  ํŠœํ”Œ์„ ์ฐพ์•„๋ด์•ผ ํ•  ๊ฒƒ์ด๋‹ค. ๋น„ํšจ์œจ์ ์ธ ๊ฒ€์ƒ‰์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด, user ๋ณ„๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ์ธ๋ฑ์Šค๋ฅผ ์ง€์ •ํ•˜์˜€๋‹ค.
    • Routine : ์ธ๋ฑ์Šค ์ถ”๊ฐ€ X
      • ๋ฃจํ‹ด์˜ ๊ฒ€์ƒ‰์€ ๋‚ ์งœ๋ณ„๋กœ ๊ทธ ๊ฒฐ๊ณผ๊ฐ€ ๋‹ฌ๋ผ์ง„๋‹ค. ๋”ฐ๋ผ์„œ RoutineHistory ๋กœ ๊ฒ€์ƒ‰์ด ๋จผ์ € ๋œ ํ›„, RoutineHistory.routine ์™ธ๋ž˜ํ‚ค๋ฅผ ํ†ตํ•ด [Routine.id](http://Routine.id) ๋กœ ํƒ์ƒ‰ํ•˜๊ฒŒ ๋˜๋ฏ€๋กœ, โ€˜userโ€™ ๋“ฑ ๋‹ค๋ฅธ ์ปฌ๋Ÿผ์˜ ์ธ๋ฑ์Šค๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š์•˜๋‹ค.
  • ๋ชจ๋“ˆ ๊ตฌ์กฐ ( ํ—ฅ์‚ฌ๊ณ ๋‚  ์•„ํ‚คํ…์ณ )

    แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2023-07-09 แ„‹แ…ฉแ„Œแ…ฅแ†ซ 10 49 24

    5-1-2) ํ—ฅ์‚ฌ๊ณ ๋‚  ์•„ํ‚คํ…์ณ๋กœ ๊ณ„์ธต ๊ฐ„ ์˜์กด์„ฑ์„ ๋ถ„๋ฆฌํ•œ Routine Module ์˜ ํ˜•ํƒœ

    แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2023-07-09 แ„‹แ…ฉแ„’แ…ฎ 3 53 29

    5-1-3) ๊ฐ ๊ณ„์ธต์ด ์ƒ์œ„๊ณ„์ธต์— ์ฃผ์ž…๋œ, ์˜์กด์„ฑ์ด ๋งค์šฐ ๋†’์€ ํ˜•ํƒœ์˜ ๊ตฌ์กฐ๊ฐ€ ์•„๋‹ˆ๋‹ค.

    • ํ—ฅ์‚ฌ๊ณ ๋‚  ์•„ํ‚คํ…์ณ๋ฅผ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ™œ์šฉํ•˜์—ฌ Routine ๋ชจ๋“ˆ์„ ๊ทธ๋ฆผ 5-1-2 ๊ณผ ๊ฐ™์ด ๊ตฌํ˜„ํ–ˆ๋‹ค.
    • ์„œ๋น„์Šค ๋กœ์ง์—์„œ ๋ณด๋ฉด, Repository์— ๋Œ€ํ•œ ๋‚ด์šฉ์„ ์ง์ ‘์ ์œผ๋กœ ์•Œ ์ˆ˜ ์—†๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์•Œ ํ•„์š”๋„ ์—†๋‹ค. ํŒŒ๋ž€์ƒ‰์œผ๋กœ ํ‘œ์‹œํ•œ ์ธํ„ฐํŽ˜์ด์Šค์ธ outbound-port ๋งŒ ์•Œ๊ณ  ์žˆ์œผ๋ฉฐ, ์ด๋Ÿฐ ํ˜•ํƒœ์˜ ์ธ์Šคํ„ด์Šค๊ฐ€ โ€˜์žˆ๋‹ค ์น˜๊ณ โ€™ ๋™์ž‘ํ•˜๋ฏ€๋กœ, ์„œ๋น„์Šค ๋กœ์ง์„ ํ•˜์œ„๊ณ„์ธต์—์„œ ๋ถ„๋ฆฌํ•˜์—ฌ ์ถ”์ƒ์ ์œผ๋กœ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๊ตฌํ˜„ ์ฝ”๋“œ โ†—๏ธ routine.read-by-date.service.ts

    export class RoutineReadByDateService implements RoutineReadByDateInboundPort {
      constructor(
    		// ์„œ๋น„์Šค๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ฃผ์ž…๋ฐ›์•„ ์ถ”์ƒ์ ์œผ๋กœ ํ™œ์šฉํ•œ๋‹ค.
    		// ์‹ค์ œ ๊ตฌํ˜„์ฒด์˜ ์ฃผ์ž…์€ ์„œ๋ฒ„๊ฐ€ ์‹คํ–‰ ๋  ๋•Œ, ๋ชจ๋“ˆ์— ์˜ํ•˜์—ฌ ํ† ํฐ์— ํ•ด๋‹นํ•˜๋Š” ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๊ฐ€ ์ฃผ์ž…๋œ๋‹ค.
        @Inject(ROUTINE_READ_BY_DATE_OUTBOUND_PORT)
        private readonly routineReadByDateOutboundPort: RoutineReadByDateOutboundPort,
        @Inject(ROUTINE_READ_RECORD_BY_DATE_OUTBOUND_PORT)
        private readonly routineReadRecordByDateOutboundPort: RoutineReadRecordByDateOutboundPort,
      ) {}
      async execute(
        params: RoutineReadByDateInboundPortInputDto,
      ): Promise<RoutineReadByDateInboundPortOutputDto> {
        // ํ•จ์ˆ˜ํ˜• ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ Fxts์˜ pipe, map, filter, flat, toArray ์‚ฌ์šฉ
        return await pipe(
          [params],
          toAsync,
          map((param) => {
            return this.routineReadByDateOutboundPort.readByDate(param);
          }),
          flat,
          filter((routine) => routine.is_active == true),
          map(async (userRoutine) => {
            return {
              routine: userRoutine.routine,
              name: userRoutine.name,
              startTime: userRoutine.start_time,
              endTime: userRoutine.end_time,
              description: userRoutine.description,
              done: await this.routineReadRecordByDateOutboundPort.execute({
                routineId: userRoutine.routine,
                date: params.date,
              }),
            };
          }),
          toArray,
        );
      }
    }

    ์œ„ ์ฝ”๋“œ๋Š” ํŠน์ •๋‚ ์งœ์˜ ๋ฃจํ‹ด๊ณผ ์ดํ–‰์—ฌ๋ถ€๋ฅผ ์ฐพ์•„ ๋ฐ˜ํ™˜ํ•˜๋Š” ์„œ๋น„์Šค๋กœ์ง RoutineReadByDateService ๋‹ค.

    • ํ—ฅ์‚ฌ๊ณ ๋‚  ์•„ํ‚คํ…์ณ์˜ ๊ตฌํ˜„
      • ์„œ๋น„์Šค๋กœ์ง์— ๋“ค์–ด์˜จ input์ธ params ์— ๋Œ€ํ•ด, ์ฃผ์–ด์ง„ ๋‚ ์งœ๋ฅผ ๊ธฐ์ค€์— ๋งž๋Š” ๋ฃจํ‹ด์˜ ์ •๋ณด๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๊ณ , ํ™œ์„ฑํ™” ๋˜์–ด ์žˆ๋Š” (์‚ญ์ œ๋˜์ง€ ์•Š์€) ๋ฃจํ‹ด์— ๋Œ€ํ•ด์„œ๋งŒ ๊ฐ๊ฐ์˜ ์ •๋ณด์™€ ์ดํ–‰์—ฌ๋ถ€๋ฅผ ์ฐพ์•„ ๋ฐฐ์—ด์— ๋‹ด์•„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
      • ๊ทธ๋ฆผ 5-1-2 ์„ ๋ณด๋ฉด, RoutineReadByDateService ์—๋Š” read-by-date ์™€ read-record-by-date Repository๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์ง€๋งŒ, ์ฝ”๋“œ์—์„œ๋Š” repository ๊ด€๋ จ ์–ด๋–ค ์ฝ”๋“œ๋„ ์ฐพ์•„๋ณผ ์ˆ˜ ์—†๋‹ค. ๋ชจ๋‘ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ์ถ”์ƒํ™” ๋˜์–ด ์‚ฌ์šฉํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
    • ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ Fxts ์‚ฌ์šฉ
      • **pipe** : ์ฒซ ์ธ์ž์™€ ํ•จ์ˆ˜๋“ค์„ ๋ฐ›์•„, ์ธ์ž๊ฐ€ ํ•จ์ˆ˜๋ฅผ ์ฐจ๋ก€๋Œ€๋กœ ํ†ต๊ณผํ•˜๋„๋ก ๋งŒ๋“ฌ. ๋งŒ์•ฝ map, filter์™€ ๊ฐ™์ด ์ดํ„ฐ๋ ˆ์ดํ„ฐ๋ฅผ ํ™œ์šฉํ•˜๋Š” ๋งค์„œ๋“œ๊ฐ€ ํ•จ์ˆ˜๋กœ ๋“ค์–ด์˜ฌ ๊ฒฝ์šฐ, ์ฒซ ์ธ์ž๋Š” ์ดํ„ฐ๋Ÿฌ๋ธ”ํ•œ ๊ฐ์ฒด๊ฐ€ ํ•„์š”ํ•˜๋ฉฐ, ์ดํ„ฐ๋ ˆ์ดํ„ฐ์˜ ๊ฐ’์„ ํ•˜๋‚˜์”ฉ ๋„˜๊ธฐ๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์ดํ„ฐ๋ ˆ์ดํ„ฐ ์ž์ฒด๋ฅผ ํ•จ์ˆ˜๋กœ ๋„˜๊ธด๋‹ค.
      • [params] : fxts๋Š” ์ง€์—ฐํ‰๊ฐ€ ๋ฉ”์„œ๋“œ์ธ map, filter ๋“ฑ์˜ ๊ตฌํ˜„์„ ์ดํ„ฐ๋ ˆ์ดํ„ฐ๋ฅผ ํ†ตํ•ด ๊ตฌํ˜„ํ•œ๋‹ค. ๋”ฐ๋ผ์„œ ๋“ค์–ด์˜จ input ๊ธธ์ด๊ฐ€ 1 ์ž„์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ , map, filter ๋“ฑ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ฐฐ์—ด๋กœ ๋‹ค๋“ฌ์—ˆ๋‹ค.
      • toAsync : map๊ณผ filter ๋‚ด๋ถ€์— ๋น„๋™๊ธฐ ๋™์ž‘์ด ๊ตฌํ˜„ ๋  ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋น„๋™๊ธฐ ๋™์ž‘์œผ๋กœ ๋ณ€ํ™˜ํ–ˆ๋‹ค. (ํƒ๊ตฌ ๊ณผ์ • : https://velog.io/@uiseok0514/Fxts-AsyncIterable)
      • flat : ์ค‘์ฒฉ๋œ ๋ฐฐ์—ด์„ ํ•œ ์ฐจ์› ํ’€์–ด ํ‰ํƒ„ํ•˜๊ฒŒ ๋งŒ๋“ ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” DB๋กœ ๋ถ€ํ„ฐ ์ฐพ์•„์˜จ ๋ฃจํ‹ด์ด ํ•˜๋‚˜์˜ ๋ฐฐ์—ด์— ๋‹ด๊ฒจ์žˆ์–ด, ์ด๋ฅผ ์ดํ„ฐ๋ ˆ์ดํ„ฐ๋กœ ์ˆœํ™˜ํ•˜๊ธฐ ์œ„ํ•ด ํ‰ํƒ„ํ™” ํ–ˆ๋‹ค.
      • filter : ํŠน์ • ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๋Š” ์š”์†Œ๋งŒ ์ดํ„ฐ๋ ˆ์ดํ„ฐ์˜ next()๋กœ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ํ™œ์„ฑํ™” ๋œ ๋ฃจํ‹ด๋งŒ ํ•„ํ„ฐ๋งํ•˜๋„๋ก ์„ค์ •ํ–ˆ๋‹ค. ( * ๋ฃจํ‹ด์ด ์ˆ˜์ •/์‚ญ์ œ๋˜์–ด๋„ ์ด์ „ ์ •๋ณด๋Š” ์œ ์ง€๋˜์–ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์œ ์ €๊ฐ€ ๋ฃจํ‹ด์„ ์‚ญ์ œํ•ด๋„ ์‹ค์ œ๋กœ๋Š” ์‚ญ์ œ๋˜์ง€ ์•Š๊ณ  ๋น„ํ™œ์„ฑํ™”๋˜์–ด ๋ณด์ด์ง€ ์•Š๋Š”๋‹ค.)
      • map : ์ดํ„ฐ๋ ˆ์ดํ„ฐ์˜ ๊ฐ ์š”์†Œ๋งˆ๋‹ค ์ •ํ•ด์ง„ ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ๊ฒฐ๊ณผ๋ฅผ ๋ฐฐ์—ด๋กœ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ๋ฃจํ‹ด์˜ ์‹œํ–‰์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•˜๊ณ , service ์˜ outputDTO ์— ๋งž์ถ”๋Š” ์ž‘์—…์„ ํ•œ๋‹ค.
      • toArray : pipe๋ฅผ ํ†ต๊ณผํ•œ ์ดํ„ฐ๋ ˆ์ดํ„ฐ๋กœ ๋ถ€ํ„ฐ ์ตœ์ข… ๊ฒฐ๊ณผ๊ฐ’์„ ๋ฐ›์•„๋‚ด๋Š” ์ž‘์—…์ด ํ•„์š”ํ•˜๋‹ค. toArray๋Š” pipe๋ฅผ ํ†ต๊ณผํ•œ ๊ฒฐ๊ณผ๊ฐ’์„ ๋ชจ์•„ ๋ฐฐ์—ด์— ๋‹ด์•„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

5-2. ์„œํด & ์›Œํฌ ๊ธฐ๋Šฅ

  • ์„œํด & ์›Œํฌ์˜ ๊ธฐ๋Šฅ

    • ์„œํด(Circle) ์˜ ๊ธฐ๋Šฅ
      • ๋™์ผํ•œ ๊ด€์‹ฌ์‚ฌ ๋˜๋Š” ๊ฐ™์€ ์ผ์ •์„ ๊ณต์œ ํ•ด์•ผํ•˜๋Š” ์œ ์ €๋“ค ๋ผ๋ฆฌ ๊ทธ๋ฃน์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.
      • ๊ฐ ์„œํด์—๋Š” ์„œํด์žฅ(owner)๊ณผ ์Šคํƒœํ”„(staff) ๋ฉค๋ฒ„(member)๊ฐ€ ์กด์žฌํ•œ๋‹ค.
      • ์„œํด์˜ ๊ฐ€์ž…์€ owner ๋˜๋Š” staff๊ฐ€ ์Šน์ธํ•ด์•ผ ์ด๋ฃจ์–ด์ง„๋‹ค.
    • ์›Œํฌ(Work) ์˜ ๊ธฐ๋Šฅ
      • ์„œํด์˜ Todo ๊ธฐ๋Šฅ์ธ ์›Œํฌ(work)๋Š” owner ๋˜๋Š” staff ๋“ฑ๊ธ‰์˜ ์œ ์ €๊ฐ€ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.
      • ์›Œํฌ๋Š” ์„œํด์— ํฌํ•จ๋œ ๋ชจ๋“  ์œ ์ €๊ฐ€ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.
      • ์›Œํฌ์˜ ์ˆ˜์ •์€ ๋ณธ์ธ๋งŒ ๊ฐ€๋Šฅํ•˜๋ฉฐ, ์‚ญ์ œ๋Š” ๋ณธ์ธ ๋˜๋Š” owner๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.
      • ์„œํด์˜ ์œ ์ €๋Š” ์–ด๋–ค work ์— ๋Œ€ํ•ด ์ž์‹ ์˜ ์ผ์ผ ํ”Œ๋žœ ํ™”๋ฉด์—์„œ ์™„๋ฃŒ์—ฌ๋ถ€๋ฅผ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ๋‹ค.
      • ์›Œํฌ ์กฐํšŒ ์‹œ, ํ•ด๋‹น ์›Œํฌ๋ฅผ ์™„๋ฃŒํ•œ ์œ ์ €๋น„์œจ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๊ณ ๋ ค์‚ฌํ•ญ

    • ์„ธ๋ถ€์ ์ธ ๊ธฐ๋Šฅ์ด ๋งŽ์ด ํ•„์š”ํ•˜๋ฏ€๋กœ, ์–ด๋–ค ๋งฅ๋ฝ์œผ๋กœ ๊ธฐ๋Šฅ๋“ค์„ ๊ตฌ๋ถ„ํ•  ์ง€ ๊ณ ๋ คํ•ด์•ผํ•œ๋‹ค.

      โ‡’ ์„œํด ๊ด€๋ฆฌ / ๋ฉค๋ฒ„ ๊ด€๋ฆฌ / ์›Œํฌ ์ด 3๊ฐ€์ง€ ๋ชจ๋“ˆ๋กœ ๋ถ„๋ฆฌ

    • ์„œํด๋ณ„ / ๋“ฑ๊ธ‰๋ณ„ ๋กœ ๊ฐ€๋Šฅํ•œ ๋™์ž‘์ด ๊ตฌ๋ถ„๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ, ๊ถŒํ•œ์„ ์ค€์ˆ˜ํ•  ์ˆ˜ ์žˆ๋Š” ์žฅ์น˜๊ฐ€ ํ•„์š”

      ( ์„œํด์— ๊ฐ€์ž…ํ•˜์ง€ ์•Š์•˜๋Š”๋ฐ, ์›Œํฌ๋ฅผ ์ƒ์„ฑํ•˜๊ฑฐ๋‚˜, member ๋“ฑ๊ธ‰์ธ๋ฐ owner๋ฅผ ๊ฐ•ํ‡ด์‹œ๊ธฐ๊ธฐ ๋“ฑ )

  • ํ…Œ์ด๋ธ” ์„ค๊ณ„

    แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2023-07-09 แ„‹แ…ฉแ„’แ…ฎ 3 28 21

    5-2-1) ์„œํด๊ณผ ์›Œํฌ์˜ ํ…Œ์ด๋ธ”

    • circle : ์„œํด์— ๋Œ€ํ•œ ์ •๋ณด. โ€˜is_Openโ€™ ์œผ๋กœ ์„œํด๊ฐ€์ž…์„ ์—ด๊ณ  ๋‹ซ์„ ์ˆ˜ ์žˆ์œผ๋ฉฐ, โ€˜membersโ€™ ๋Š” ๋ฉค๋ฒ„ ์ด ์ธ์› ์ˆ˜๋ฅผ ์ €์žฅ
    • circle_application : ์„œํด ๊ฐ€์ž…์‹ ์ฒญ ๋ฐ์ดํ„ฐ. ์œ ์ €-์„œํด ์Œ์„ ์ƒ์„ฑํ•˜์—ฌ ์‹ ์ฒญ์—ฌ๋ถ€๋ฅผ ํ™•์ธ ํ•จ.
    • circle_grades : ์„œํด ๋ฉค๋ฒ„์— ๋Œ€ํ•œ ๋“ฑ๊ธ‰. 1-owner ~ 3-member ๊นŒ์ง€๋กœ ์„ค์ •
    • user_circles : ์œ ์ €๊ฐ€ ๊ฐ€์ž…ํ•œ ์„œํด ๋ฐ์ดํ„ฐ. ์œ ์ €-์„œํด ์Œ์„ ์ƒ์„ฑํ•˜์—ฌ ๊ฐ€์ž…์—ฌ๋ถ€๋ฅผ ํ™•์ธ ํ•จ.
    • work : ์„œํด์˜ todo ์ธ ์›Œํฌ์— ๋Œ€ํ•œ ์ •๋ณด.
    • work_records : ์–ด๋–ค ์›Œํฌ์— ๋Œ€ํ•ด ๋ˆ„๊ฐ€ ์™„๋ฃŒํ–ˆ๋Š”์ง€๋ฅผ ๊ธฐ๋กํ•˜๋Š” ๋ฐ์ดํ„ฐ. ์ปฌ๋Ÿผ์— circle์„ ์ถ”๊ฐ€ํ•œ ์ด์œ ๋Š” ์ธ๋ฑ์‹ฑ์„ ์œ„ํ•ด์„œ ์ถ”๊ฐ€ํ•จ.
    • work_priority : ์›Œํฌ์— ๋Œ€ํ•œ ์ค‘์š”๋„ ๋ฐ์ดํ„ฐ. 1~5 ๋“ฑ๊ธ‰์œผ๋กœ ๊ตฌ๋ถ„
  • ์ธ๋ฑ์‹ฑ

    • circle : name ์œผ๋กœ ์ธ๋ฑ์Šค ์ถ”๊ฐ€
      • ์„œํด ๊ฒ€์ƒ‰ ์‹œ, ์‹œ๊ฐ„์„ ์ตœ์†Œํ™” ํ•˜๊ธฐ ์œ„ํ•œ ์กฐ์น˜
    • user_circles : circle๋กœ ์ธ๋ฑ์Šค ์ถ”๊ฐ€
      • ๊ธฐ๋ณธ์ ์œผ๋กœ user์— ๋Œ€ํ•ด ์ธ๋ฑ์Šค๊ฐ€ ์ƒ์„ฑ๋˜์–ด ์œ ์ €๊ฐ€ ์ž์‹ ์ด ๊ฐ€์ž…ํ•œ ์„œํด์„ ๊ฒ€์ƒ‰ํ•  ๋•Œ ๋น ๋ฅธ ์†๋„๋กœ ์ฐพ์„ ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์„œํด์— ๊ฐ€์ž…ํ•œ ๋ฉค๋ฒ„๋“ค์„ ์กฐํšŒํ•  ๋•Œ ํ…Œ์ด๋ธ”์„ ๋ชจ๋‘ ํƒ์ƒ‰ํ•ด์•ผ ํ•˜๋ฏ€๋กœ, ์„œํด ๋ณ„๋กœ ์ธ๋ฑ์‹ฑ์„ ์ถ”๊ฐ€ํ•  ํ•„์š”๊ฐ€ ์žˆ๋‹ค.
    • work : circle๋กœ ์ธ๋ฑ์Šค ์ถ”๊ฐ€
      • ์›Œํฌ๋Š” ์„œํด๋ณ„๋กœ ์กฐํšŒํ•˜๋ฏ€๋กœ, id ๊ธฐ๋ณธ ์ธ๋ฑ์Šค ์ด์™ธ์— circle๋กœ ์ถ”๊ฐ€ ์ธ๋ฑ์Šค๊ฐ€ ํ•„์š”ํ–ˆ๋‹ค.
    • work_records : ์ธ๋ฑ์Šค ์ถ”๊ฐ€ X
      • ์ผ์ผํ”Œ๋ž˜๋„ˆ์—์„œ ์›Œํฌ๋Š” ์œ ์ €-๊ฐ€์ž…๋œ์„œํด-์›Œํฌ ์ˆœ์œผ๋กœ ๊ฒ€์ƒ‰๋˜๋ฏ€๋กœ, work id ๋กœ ์ถฉ๋ถ„ํžˆ ๊ฒ€์ƒ‰์ด ๊ฐ€๋Šฅํ•˜์—ฌ ๋ณ„๋„ ์ธ๋ฑ์Šค๋ฅผ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š์•˜๋‹ค.
  • ๋ชจ๋“ˆ ๊ตฌ์กฐ ( ํ—ฅ์‚ฌ๊ณ ๋‚  ์•„ํ‚คํ…์ณ )

    • ํ˜•ํƒœ๋Š” 5-1-2 ๋ฃจํ‹ด๊ธฐ๋Šฅ์˜ ์•„ํ‚คํ…์ณ ๊ตฌ์กฐ์™€ ๋™์ผํ•˜๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ์‹ค์ œ ์ฝ”๋“œ ํŒŒ์ผ์ด ์–ด๋–ป๊ฒŒ ๊ตฌ์„ฑ๋˜์–ด ์žˆ๊ณ , ๋ชจ๋“ˆ์—์„œ ๊ฐ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์–ด๋–ป๊ฒŒ ์ œ์–ดํ•˜๊ณ  ์žˆ๋Š” ์ง€ ์„ค๋ช…ํ–ˆ๋‹ค.

    แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2023-07-09 แ„‹แ…ฉแ„’แ…ฎ 4 05 40

    5-2-2) circle ์˜ management ๋ชจ๋“ˆ.

    แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2023-07-09 แ„‹แ…ฉแ„’แ…ฎ 4 06 27

    5-2-3) circle.management.module ์˜ ์„ค์ •.

    • ๊ฐ ๊ณ„์ธต์˜ ๊ตฌ์กฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ˜•ํƒœ๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ๋‹ค

      module - controller - inboundPort - service - outboundPort - repository

    • ๊ฐ ๊ณ„์ธต์—์„œ๋Š” ์™ธ๋ถ€๋กœ๋ถ€ํ„ฐ ์ฃผ์ž…๋˜๋Š” ์ธ์Šคํ„ด์Šค๋ฅผ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•ด ์ถ”์ƒ์ ์œผ๋กœ ๋‹ค๋ฃจ๊ธฐ ๋•Œ๋ฌธ์—, ๋Ÿฐํƒ€์ž„ ์‹œ ์‹ค์ œ๋กœ ์–ด๋–ค ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•  ์ง€ ์ง€์ •ํ•ด ์ฃผ์–ด์•ผ ํ•œ๋‹ค.

    • ๊ฐ ๊ณ„์ธต์—๋Š” ์ธํ„ฐํŽ˜์ด์Šค์— ํ† ํฐ์ด ์ฃผ์ž…๋˜์–ด ์žˆ์œผ๋ฉฐ, ํ† ํฐ์ด ๋ฌด์—‡์„ ์ง€์นญํ•˜๋Š”์ง€๋Š” module์—์„œ 5-2-3 ์‚ฌ์ง„์ฒ˜๋Ÿผ ์„ค์ •ํ•œ๋‹ค.

      {
      	provide: CIRCLE_MANAGEMENT_CREATE_INBOUND_PORT, // ์ด ํ† ํฐ์ด ์ฃผ์ž…๋œ ๊ฒƒ์€
      	useClass: CircleManagementCreateService, // ์‹ค์ œ๋กœ๋Š” ์ด ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
      }
      แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2023-07-09 แ„‹แ…ฉแ„’แ…ฎ 4 24 23

      5-2-4) ๊ฐ ๊ณ„์ธต์—๋Š” ํ† ํฐ์ด ์ฃผ์ž…๋˜๊ณ , ์ฃผ์ž…๋˜๋Š” ์ธ์Šคํ„ด์Šค๋ฅผ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•ด ์ถ”์ƒ์ ์œผ๋กœ ๋‹ค๋ฃฌ๋‹ค.

    • ์ด๋Š” ๊ฐ ๊ณ„์ธต์ด ์ธํ„ฐํŽ˜์ด์Šค๋กœ ํƒ€ ๊ณ„์ธต์„ ์ถ”์ƒ์ ์œผ๋กœ ๋‹ค๋ฃจ๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ€๋Šฅํ•˜๋‹ค. ๋˜ํ•œ ๊ณ„์ธต์ด ๋ณ€๋™ ๋˜๋”๋ผ๋„, ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋งŒ์กฑํ•˜๋Š” ๋‹ค๋ฅธ ๊ณ„์ธต์„ ๋ชจ๋“ˆ์—์„œ ํ† ํฐ์œผ๋กœ ์—ฐ๊ฒฐํ•˜๋ฉด ๋˜๋ฏ€๋กœ ๊ด€๋ฆฌ๊ฐ€ ์šฉ์ดํ•˜๋‹ค.

    ๐Ÿ“Œ ์ง€๊ธˆ๊นŒ์ง€ ๋ฃจํ‹ด๊ณผ ์„œํด์„ ์˜ˆ์‹œ๋กœ, ์–ด๋–ป๊ฒŒ ์„œ๋ฒ„๊ฐ€ ๊ตฌ์„ฑ ๋˜์—ˆ๋Š”์ง€ ๋‚˜ํƒ€๋‚ด์—ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด ์„ค๋ช…์ด ์„œ๋ฒ„์— ๋Œ€ํ•œ ๋ชจ๋“  ๊ฒƒ์„ ๋‚ดํฌํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค. ์•„ํ‚คํ…์ณ๋Š” ๋™์ผํ•˜๊ฒŒ ๊ฐ€์ ธ๊ฐ€๋˜, ์„œ๋น„์Šค๋กœ์ง์€ ๊ฐ ๋ชจ๋“ˆ์˜ ๊ธฐ๋Šฅ์— ์ดˆ์ ์„ ๋งž์ถ”์–ด ๊ตฌํ˜„๋˜์—ˆ๋‹ค. ์ž์„ธํ•œ ์ฝ”๋“œ๋Š” : [https://github.com/ObO314/ObO-back](https://github.com/ObO314/ObO-back)



6. ํ…Œ์ŠคํŠธ์ผ€์ด์Šค

6-1. ์˜์กด์„ฑ ๋ถ„๋ฆฌ

  • ์•„ํ‚คํ…์ณ๊ฐ€ ์ฃผ๋Š” ํ…Œ์ŠคํŠธ์ผ€์ด์Šค์˜ ์ด์ 
    • ํ—ฅ์‚ฌ๊ณ ๋‚  ์•„ํ‚คํ…์ณ์˜ ์ ์šฉ์€, ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•ด ๊ฐ ๊ณ„์ธต์˜ ์˜์กด์„ฑ์„ ๋ถ„๋ฆฌ ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.
    • ํ…Œ์ŠคํŠธ ๊ด€์ ์—์„œ๋„ ๊ฐ ๊ณ„์ธต์˜ ์˜์กด์„ฑ ๋ถ„๋ฆฌ๋Š” ํ•ด๋‹น๊ณ„์ธต์˜ ํ…Œ์ŠคํŠธ์— ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ๋Š” ํ™˜๊ฒฝ์„ ์ œ๊ณตํ•œ๋‹ค.
    • ๋ชจํ‚นํ•˜๋Š” ์ธ์Šคํ„ด์Šค๊ฐ€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋ฉฐ, ์‹ค์ œ์˜ ํด๋ž˜์Šค ํ˜•ํƒœ๋ฅผ ์ค€์ˆ˜ํ•œ๋‹ค.

6-2. ๊ณ„์ธต๋ณ„ ํ…Œ์ŠคํŠธ

  • ๋ฆฌํฌ์ง€ํ† ๋ฆฌ ํ…Œ์ŠคํŠธ

    แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2023-07-09 แ„‹แ…ฉแ„’แ…ฎ 9 11 38
    • ๋ ˆํฌ์ง€ํ† ๋ฆฌ ํ…Œ์ŠคํŠธ์˜ ์ค‘์  : ์‹ค์ œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ํ†ต์‹ ํ•˜๊ณ  ์ ์ ˆํ•œ ๋ฐ์ดํ„ฐ์˜ ์ž…์ถœ๋ ฅ์ด ์ด๋ฃจ์–ด์ง€๋Š”๊ฐ€?

    • ๋น„์–ด์žˆ๋Š” ํ…Œ์ŠคํŠธ์šฉ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—, ์—”ํ‹ฐํ‹ฐ ๋งค๋‹ˆ์ €๊ฐ€ create, upsert ๋“ฑ์„ ์ž˜ ๋ฐ˜์˜ํ•˜๋Š”์ง€ ์ฒดํฌํ•œ๋‹ค.

    • ํ…Œ์ŠคํŠธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ™˜๊ฒฝ ๊ตฌ์ถ• ( ํ…Œ์ด๋ธ”์„ ์˜ค๋ฆฌ์ง€๋„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ๋™์ผํ•˜๊ฒŒ ๊ตฌ์ถ•ํ•˜๊ธฐ )

      const orm = await MikroORM.init(testConfig); // ํ…Œ์ŠคํŠธ์šฉ DB์ฃผ์†Œ๊ฐ€ ์ž…๋ ฅ๋œ ์„ค์ •์‚ฌ์šฉ
        const generator = orm.getSchemaGenerator(); // ์Šคํ‚ค๋งˆ ์ƒ์„ฑ๊ธฐ๋ฅผ ๊ฐ€์ ธ์™€ ์‹คํ–‰
        await generator.updateSchema();
      
    • ์•„๋ž˜๋Š” ์œ ์ €๋ฅผ ์ƒ์„ฑํ•˜๋Š” UserCreateRepository ํด๋ž˜์Šค์— ๋Œ€ํ•œ ์ฝ”๋“œ์™€ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค์ด๋‹ค.

      โ†—๏ธ user.create.repository.ts

      export class UserCreateRepository implements UserCreateOutboundPort {
        constructor(private readonly em: EntityManager) {}
      
        async execute(
          params: UserCreateOutboundPortInputDto,
        ): Promise<UserCreateOutboundPortOutputDto> {
          const newUser = this.em.create(Users, params);
          await this.em.persistAndFlush(newUser);
          return newUser;
        }
      }

      โ†—๏ธ user.create.repository.spec.ts

      describe('UserCreateRepository Spec', () => {
        let userCreateRepository: UserCreateRepository;
        let em: EntityManager;
        let orm: MikroORM;
      
        beforeAll(async () => {
          orm = await MikroORM.init({ ...testConfig, driver: PostgreSqlDriver });
          em = orm.em;
          userCreateRepository = new UserCreateRepository(em);
        });
      
      ...
      
      	test('ํšŒ์›๊ฐ€์ž…(๋กœ์ปฌ) : ๋กœ์ปฌ์œ ์ €๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.', async () => {
      	    const params = {
      	      id: '123', // ์ž„์˜์ง€์ • (์›๋ž˜๋Š” ์‹œํ€€์Šค ์‚ฌ์šฉ)
      	      email: 'createLocalTester@obo.com',
      	      nickname: 'oboNewbie',
      	      password: '1q2w3e4r',
      	      authMethod: 'LOCAL',
      	    };
      	
      	    const result: UserCreateOutboundPortOutputDto =
      	      await userCreateRepository.execute(params);
      	
      	    expect(result).toEqual({
      	      userId: '123',
      	      email: 'createLocalTester@obo.com',
      	      nickname: 'oboNewbie',
      	    });
      	  });
      	...
      }

      แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2023-07-09 แ„‹แ…ฉแ„’แ…ฎ 9 27 05

  • ์„œ๋น„์Šค ํ…Œ์ŠคํŠธ

    แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2023-07-09 แ„‹แ…ฉแ„’แ…ฎ 9 07 02
    • ์„œ๋น„์Šค ํ…Œ์ŠคํŠธ๋Š” ์‹ค์ œ ๋ ˆํฌ์ง€ํ† ๋ฆฌ๊ฐ€ ์•„๋‹Œ Mock ๊ฐ์ฒด๊ฐ€ ์ฃผ์ž…๋œ๋‹ค.
    • mock์€ ์„œ๋น„์Šค๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” outboundPort ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ค€์ˆ˜ํ•œ๋‹ค.
    • ๋”ฐ๋ผ์„œ ์„œ๋น„์Šค๋Š” ์ •ํ•ด์ง„ mock์„ ๋ฐ›๊ธฐ ์œ„ํ•œ ์˜ฌ๋ฐ”๋ฅธ ์ž…๋ ฅ๊ณผ, ์ ์ ˆํ•œ ์ตœ์ข…๊ฐ’์ด ์ถœ๋ ฅ ๋˜๋Š”์ง€ ํ…Œ์ŠคํŠธ ํ•œ๋‹ค.

    โ†—๏ธ user.sign-up.service.ts

    export class UserSignUpService implements UserSignUpInboundPort {
      constructor(
        @Inject(USER_READ_OUTBOUND_PORT)
        private readonly userReadOutboundPort: UserReadOutboundPort,
        @Inject(USER_CREATE_OUTBOUND_PORT)
        private readonly userCreateOutboundPort: UserCreateOutboundPort,
      ) {}
    
      async execute(
        params: UserSignUpInboundPortInputDto,
      ): Promise<UserSignUpInboundPortOutputDto> {
    
        // ์ด๋ฉ”์ผ์ด ์กด์žฌํ•˜๋Š”๊ฐ€
        // ์œ ํšจํ•œ ๋น„๋ฐ€๋ฒˆํ˜ธ์ธ๊ฐ€
        // ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ํ•ด์‰ฌํ™”
        // ์œ ์ € ์ƒ์„ฑํ•˜๊ธฐ
        // ์œ ์ €๋ฅผ ์ฐพ์•„ DB์—์„œ ๋ฐ˜ํ™˜ ํ•˜๊ธฐ
    
        return await pipe(
          [params],
          toAsync,
          filter(async (params) => {
            if (!(await this.userReadOutboundPort.execute({email: params.email}))) {
              return true;
            } else {
              throw new HttpException(
                '์ด๋ฏธ ๊ฐ€์ž…๋œ ์ด๋ฉ”์ผ์ž…๋‹ˆ๋‹ค.',
                HttpStatus.BAD_REQUEST,
              );
            }
          }),
          filter((params) => {
            if (params.password) { return true } 
    				else {
              throw new HttpException(
                '๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”.',
                HttpStatus.BAD_REQUEST,
              );
            }
          }),
          map(async (params) => {
            return {
              ...params,
              password: await bcrypt.hash(params.password, 10),
            };
          }),
          map((params) => this.userCreateOutboundPort.execute(params)),
          map((user) => {
            return {
              userId: user.id,
              email: user.email,
              nickname: user.nickname,
            };
          }),
          head,
        );
      }
    }

    โ†—๏ธ user.sign-up.service.spec.ts

    describe('UserSignUpService Spec', () => {
      let userSignUpService: UserSignUpService;
    
    	//-- Mocking --
    
      class MockUserReadOutboundPort implements UserReadOutboundPort {
        constructor(private readonly params: UserReadOutboundPortOutputDto) {}
        async execute(
          _: UserReadOutboundPortInputDto,
        ): Promise<UserReadOutboundPortOutputDto> {
          return this.params;
        }
      }
    
      class MockUserCreateOutboundPort implements UserCreateOutboundPort {
        constructor(private readonly params: UserCreateOutboundPortOutputDto) {}
        async execute(
          _: UserCreateOutboundPortInputDto,
        ): Promise<UserCreateOutboundPortOutputDto> {
          return this.params;
        }
      }
    
    	//-- testing --
    
      test('ํšŒ์›๊ฐ€์ž…(๋กœ์ปฌ) : ์ •์ƒ์ ์ธ ํšŒ์›๊ฐ€์ž…', async () => {
        userSignUpService = new UserSignUpService(
          new MockUserReadOutboundPort(null),
          new MockUserCreateOutboundPort({
            id: '1',
            email: 'createLocalTester@obo.com',
            nickname: 'oboNewbie',
            authMethod: 'LOCAL',
          }),
        );
    
        const params = {
          email: 'createLocalTester@obo.com',
          password: '1q2w3e4r',
          nickname: 'oboNewbie',
          authMethod: 'LOCAL',
        };
    
        const result: UserSignUpInboundPortOutputDto =
          await userSignUpService.execute(params);
    
        expect(result).toEqual({
          userId: '1',
          email: 'createLocalTester@obo.com',
          nickname: 'oboNewbie',
        });
      });
    
      //--------------------------------------------------
    
      test('ํšŒ์›๊ฐ€์ž…(๋กœ์ปฌ) : ์ค‘๋ณต๋œ ์ด๋ฉ”์ผ๋กœ ํšŒ์›๊ฐ€์ž… ์‹œ๋„', async () => {
        userSignUpService = new UserSignUpService(
          new MockUserReadOutboundPort({
            id: '1',
            email: 'existentUsers@obo.com',
            nickname: 'oboUser',
            authMethod: 'LOCAL',
          }),
          new MockUserCreateOutboundPort(null),
        );
    
        const params = {
          email: 'existentUsers@obo.com', // ์ค‘๋ณต๋œ ์ด๋ฉ”์ผ
          password: '1q2w3e4r',
          nickname: 'oboNewbie',
          authMethod: 'LOCAL',
        };
    
        expect(async () => await userSignUpService.execute(params)).rejects.toThrow(
          new HttpException('์ด๋ฏธ ๊ฐ€์ž…๋œ ์ด๋ฉ”์ผ์ž…๋‹ˆ๋‹ค.', HttpStatus.BAD_REQUEST),
        );
      });
    
      //--------------------------------------------------
    
      test('ํšŒ์›๊ฐ€์ž…(๋กœ์ปฌ) : ๋น„๋ฐ€๋ฒˆํ˜ธ ์—†์ด ํšŒ์›๊ฐ€์ž… ์‹œ๋„', async () => {
        userSignUpService = new UserSignUpService(
          new MockUserReadOutboundPort(null),
          new MockUserCreateOutboundPort({
            id: '1',
            email: 'createLocalTester@obo.com',
            nickname: 'oboNewbie',
            authMethod: 'LOCAL',
          }),
        );
    
        const params = {
          email: 'createLocalTester@obo.com',
          password: null, // ๋กœ์ปฌ ๊ฐ€์ž…์ธ๋ฐ ๋น„๋ฐ€๋ฒˆํ˜ธ ๋ฏธ ์ž…๋ ฅ
          nickname: 'oboNewbie',
          authMethod: 'LOCAL',
        };
    
        expect(async () => await userSignUpService.execute(params)).rejects.toThrow(
          new HttpException('๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”.', HttpStatus.BAD_REQUEST),
        );
      });
    });



7. ๋งˆ์น˜๋ฉฐ

7-1. ํšŒ๊ณ 

  • ObO ํ”„๋กœ์ ํŠธ์˜ ์˜๋ฏธ

    • OBO ํ”„๋กœ์ ํŠธ์˜ ์ผ์ •๊ด€๋ฆฌ ์„œ๋น„์Šค๋Š” ๋‚ด๊ฐ€ ์‹ค์ œ๋กœ ํ•„์š”ํ•œ ๊ธฐ๋Šฅ์„ ๋‹ด์•„ ๋งŒ๋“  ํ”„๋กœ์ ํŠธ์ด๋‹ค.
    • ๊ทธ๋ž˜์„œ ํด๋ผ์ด์–ธํŠธ ๊ด€์ ์—์„œ ๋ฐ”๋ผ๋ณด๋ฉฐ, ์ด๊ฒƒ์ด ๋ฐฑ์—”๋“œ์—์„œ๋Š” ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„๋˜๋Š”์ง€ ์ƒ๊ฐํ•ด ๋ณผ ์ˆ˜ ์žˆ์—ˆ๋‹ค.
    • ๊ทธ ์ „๊นŒ์ง€์˜ ํ”„๋กœ์ ํŠธ๋“ค์€ ๋‹จ์ˆœ ๊ตฌํ˜„์— ๋จธ๋ฌผ๋ €์ง€๋งŒ, ObO๋Š” ์•„ํ‚คํ…์ณ์™€ ํŒจ๋Ÿฌ๋‹ค์ž„์˜ ์ ์šฉ๊ณผ ์ƒˆ๋กœ์šด ๊ธฐ์ˆ ์˜ ์‚ฌ์šฉ์ด ์ด๋ฃจ์–ด์กŒ๊ธฐ์— ๋”์šฑ ์„ฑ์žฅํ•  ์ˆ˜ ์žˆ๋Š” ํ”„๋กœ์ ํŠธ์˜€๋‹ค.
    • ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉฐ ๋งˆ์ฃผํ•œ ์ž์ž˜ํ•œ ๋ฌธ์ œ๋“ค์€ ๊ธฐ๋กํ•˜์ง€ ์•Š์•˜๋‹ค. ๋Œ€๋ถ€๋ถ„์ด ๋‹จ์ˆœ ์ง€์‹๋ถ€์กฑ์ด๊ธฐ์— ๊ธฐ๋กํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค๊ณ  ๋А๊ผˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๋ฐ˜๋ฉด์— ์ƒˆ๋กœ ์•Œ๊ฒŒ๋˜๊ณ  ์ดํ•ด๊ฐ€ ์ •๋ฆฌ๋œ ๋‚ด์šฉ์€ ๊ธฐ๋กํ–ˆ๋‹ค.
  • ๋А๋ฆฌ์ง€๋งŒ ํ™•์‹คํ•˜๊ฒŒ

    • ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉด์„œ ์ดํ•ด๊ฐ€ ๋ถ€์กฑํ•˜๋‹ค๊ณ  ๋А๋‚€๋ถ€๋ถ„์€ ํ”„๋กœ์ ํŠธ๋ฅผ ์ค‘๋‹จํ•˜๊ณ  ๊ฐœ๋…์„ ์ดํ•ดํ•˜๊ณ  ์˜ค๊ธฐ๋„ ํ–ˆ๋‹ค. ํŠนํžˆ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ด ๊ทธ๋žฌ๋‹ค. (https://velog.io/@uiseok0514/FP-deepdive)
    • ๊ณต๋ถ€์™€ ๊ฐœ๋ฐœ์„ ๋ฐ˜๋ณตํ•˜๋‹ค๋ณด๋‹ˆ ์ž‘์„ฑํ•œ ์ง€ ์กฐ๊ธˆ ์ง€๋‚œ ์ฝ”๋“œ๋“ค์€ ์ด์ „์— ๋ณด์ด์ง€ ์•Š๋˜ ์ž˜๋ชป๋œ ์ ๊ณผ ๋‹จ์ ๋“ค์ด ๋ณด์ด๊ธฐ ์‹œ์ž‘ํ–ˆ๋‹ค. ํŠนํžˆ ์ดˆ๋ฐ˜์— ๊ฐœ๋ฐœํ•œ user์™€ todo ํŒŒํŠธ๋Š” ๊ฑฐ์˜ 3๋ฒˆ์ •๋„ ๊ฐˆ์•„ ์—Ž์—ˆ๋‹ค.
    • ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋„ ์ ˆ๋ฐ˜ ์ด์ƒ ๋ชจ๋“ˆ์„ ์ž‘์„ฑํ–ˆ๋‹ค๊ฐ€, ์ž˜๋ชป๋œ ๋ถ€๋ถ„์„ ๊นจ๋‹ซ๊ณ  ๋ชจ๋‘ ์‚ญ์ œํ–ˆ๋‹ค. ํ˜„์žฌ ๋‹ค์‹œ ํ…Œ์ŠคํŠธ์ผ€์ด์Šค๋ฅผ ์ž‘์„ฑ ์ค‘์ด๋‹ค. ( ์ด ํ”„๋กœ์ ํŠธ๋Š” TDD๋Š” ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— ํ…Œ์ŠคํŠธ์ผ€์ด์Šค๋ฅผ ๋จผ์ € ์ž‘์„ฑํ•˜์ง„ ์•Š์•˜๋‹ค. )
  • ํ•˜์ง€๋งŒ ์•„์ง๋„ ๋ถ€์กฑํ•˜๋‹ค

    • ํ”„๋กœ์ ํŠธ๋ฅผ ํ†ตํ•ด ๋‹ค์–‘ํ•œ ๊ธฐ์ˆ ๊ณผ ๊ฐœ๋…์„ ์ƒˆ๋กœ ์ ‘ํ–ˆ์ง€๋งŒ, ์•„์ง ๋ฐฐ์šฐ๊ณ  ์ตํ˜€์•ผ ํ•  ๊ฒƒ๋“ค์ด ๋„˜์ณ๋‚œ๋‹ค.
    • ์˜ˆ๋ฅผ ๋“ค๋ฉด, ๋‚˜๋Š” DB์ฟผ๋ฆฌ๋ฅผ mikroORM์˜ entityManager๋ฅผ ํ†ตํ•ด ์ž‘์„ฑํ–ˆ์ง€๋งŒ, SQL ์ฟผ๋ฆฌ๋ฌธ์œผ๋กœ ์ž‘์„ฑํ•˜์ง„ ์•Š์•˜๋‹ค. ORM์ด ๊ฐ€์ง„ ํ•œ๊ณ„์ ์€ ๋ถ„๋ช…ํ•˜๊ธฐ์—, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ตœ์ ํ™” ๋˜๊ณ  ๋ณต์žกํ•œ ์ฟผ๋ฆฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” SQL๋ฌธ ์ž‘์„ฑ ๋Šฅ๋ ฅ์ด ํ•„์š”ํ•˜๋‹ค.
    • ์ง€๊ธˆ์˜ ์„œ๋ฒ„๋Š” ๋‹จ์ผ์„œ๋ฒ„, ๋‹จ์ผDB ์ด๋‹ค. ๋งŒ์ผ ์„œ๋ฒ„์˜ ์œ ์ €๊ฐ€ ๋งค์šฐ ๋งŽ๊ณ  ์š”์ฒญ๋˜๋Š” ์ฟผ๋ฆฌ์™€ ์ •๋ณด๊ฐ€ ํ•œ ๋Œ€์˜ ์„œ๋ฒ„ ๋งŒ์œผ๋กœ ์ปค๋ฒ„๋  ์ˆ˜ ์—†์„ ๊ฒƒ์ด๊ณ , ์ˆ˜์ง ๋˜๋Š” ์ˆ˜ํ‰์  ํ™•์žฅ์ด ์ด๋ฃจ์–ด์ ธ์•ผ ํ•œ๋‹ค. ์—ฌ๊ธฐ์— ๋Œ€ํ•ด์„œ๋Š” ์ถ”๊ฐ€์ ์œผ๋กœ ๊ณต๋ถ€๊ฐ€ ํ•„์š”ํ•˜๋‹ค.
    • ์ข‹์€ ํ”„๋กœ๊ทธ๋žจ์ด๋ž€ ๋ฌด์—‡์ผ๊นŒ? ์ข‹์€ ์ฝ”๋“œ๋ž€ ๋ฌด์—‡์ผ๊นŒ? ๋ผ๋Š” ์งˆ๋ฌธ์— ๋Œ€ํ•ด ๊นจ๋‹ฌ์Œ์„ ์–ป๊ธฐ์œ„ํ•ด ์ฝ์œผ๋ ค๊ณ  ์Œ“์•„๋‘” ์ฑ…์ด ์•„์ง ๋งŽ๋‹ค. ๊ฒฝํ—˜์ด ๋ถ€์กฑํ•ด์„œ ์ฑ…์˜ ๋‚ด์šฉ์„ ๋‹ค ์ดํ•ด ๋ชปํ•  ๋•Œ๋„ ๋งŽ๊ณ , ์‹œ๊ฐ„์ด ๋ถ€์กฑํ•ด ์ฝ์ง€ ๋ชปํ•œ ์ฑ…๋„ ์žˆ๋‹ค. ๊พธ์ค€ํžˆ ์ฝ๊ณ  ์ƒ๊ฐํ•˜๋ฉฐ ๊ณ ์ฐฐํ•ด์•ผ ํ•  ๊ฒƒ์ด๋‹ค.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published