๐Ÿš€ ๊ทธ๋ž˜ํ”„ํ์—˜ ์„œ๋ฒ„ ์บ์‹ฑ ์ „๋žต: ๋ฐ์ดํ„ฐ๋กœ๋” ํ™œ์šฉ ๐Ÿš€

์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿš€ ๊ทธ๋ž˜ํ”„ํ์—˜ ์„œ๋ฒ„ ์บ์‹ฑ ์ „๋žต: ๋ฐ์ดํ„ฐ๋กœ๋” ํ™œ์šฉ ๐Ÿš€

 

 

์•ˆ๋…•ํ•˜์„ธ์š”, ์—ฌ๋Ÿฌ๋ถ„! ์˜ค๋Š˜์€ ์ •๋ง ํฅ๋ฏธ์ง„์ง„ํ•œ ์ฃผ์ œ๋กœ ์—ฌ๋Ÿฌ๋ถ„๊ณผ ํ•จ๊ป˜ ์‹œ๊ฐ„์„ ๋ณด๋‚ด๋ ค๊ณ  ํ•ด์š”. ๋ฐ”๋กœ ๊ทธ๋ž˜ํ”„ํ์—˜(GraphQL) ์„œ๋ฒ„์˜ ์บ์‹ฑ ์ „๋žต์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•ด๋ณผ ๊ฑด๋ฐ์š”, ํŠนํžˆ ๋ฐ์ดํ„ฐ๋กœ๋”(DataLoader)๋ฅผ ํ™œ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ๊นŠ์ด ์žˆ๊ฒŒ ์•Œ์•„๋ณผ ๊ฑฐ์˜ˆ์š”. ๐Ÿ˜ƒ

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

์ด ๊ธ€์„ ํ†ตํ•ด ์—ฌ๋Ÿฌ๋ถ„์€ ๊ทธ๋ž˜ํ”„ํ์—˜ ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์–ด๋–ป๊ฒŒ ํšจ์œจ์ ์œผ๋กœ ์บ์‹ฑํ•˜๊ณ , ๋ฐ์ดํ„ฐ๋กœ๋”๋ฅผ ํ™œ์šฉํ•ด ์„ฑ๋Šฅ์„ ๊ทน๋Œ€ํ™”ํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ๋ฐฐ์šฐ๊ฒŒ ๋  ๊ฑฐ์˜ˆ์š”. ๋งˆ์น˜ ์žฌ๋Šฅ๋„ท์—์„œ ๋‹ค์–‘ํ•œ ์žฌ๋Šฅ์„ ํšจ์œจ์ ์œผ๋กœ ์—ฐ๊ฒฐํ•˜๋“ฏ์ด, ์šฐ๋ฆฌ๋„ ๋ฐ์ดํ„ฐ๋ฅผ ์Šค๋งˆํŠธํ•˜๊ฒŒ ์—ฐ๊ฒฐํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ตํ˜€๋ณผ ๊ฑฐ์˜ˆ์š”! ๐ŸŒŸ

์ž, ๊ทธ๋Ÿผ ์ด์ œ ๋ณธ๊ฒฉ์ ์œผ๋กœ ๊ทธ๋ž˜ํ”„ํ์—˜์˜ ์„ธ๊ณ„๋กœ ๋น ์ ธ๋ณผ๊นŒ์š”? ์ค€๋น„๋˜์…จ๋‚˜์š”? Let's dive in! ๐ŸŠโ€โ™‚๏ธ

๐Ÿ“š ๊ทธ๋ž˜ํ”„ํ์—˜(GraphQL)์ด๋ž€ ๋ฌด์—‡์ธ๊ฐ€?

๋จผ์ €, ๊ทธ๋ž˜ํ”„ํ์—˜์— ๋Œ€ํ•ด ๊ฐ„๋‹จํžˆ ์•Œ์•„๋ณผ๊นŒ์š”? ๊ทธ๋ž˜ํ”„ํ์—˜์€ ํŽ˜์ด์Šค๋ถ์—์„œ ๊ฐœ๋ฐœํ•œ ์ฟผ๋ฆฌ ์–ธ์–ด์ด์ž ๋Ÿฐํƒ€์ž„์ž…๋‹ˆ๋‹ค. REST API์˜ ํ•œ๊ณ„๋ฅผ ๊ทน๋ณตํ•˜๊ณ ์ž ๋งŒ๋“ค์–ด์กŒ์ฃ . ๐Ÿค“

๊ทธ๋ž˜ํ”„ํ์—˜์˜ ์ฃผ์š” ํŠน์ง•:

  • ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ ์š”์ฒญํ•  ์ˆ˜ ์žˆ์–ด์š”. (Over-fetching ๋ฌธ์ œ ํ•ด๊ฒฐ)
  • ํ•œ ๋ฒˆ์˜ ์š”์ฒญ์œผ๋กœ ์—ฌ๋Ÿฌ ๋ฆฌ์†Œ์Šค์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ์–ด์š”. (Under-fetching ๋ฌธ์ œ ํ•ด๊ฒฐ)
  • ๊ฐ•๋ ฅํ•œ ํƒ€์ž… ์‹œ์Šคํ…œ์„ ์ œ๊ณตํ•ด์š”.
  • ์‹ค์‹œ๊ฐ„ ์—…๋ฐ์ดํŠธ๋ฅผ ์œ„ํ•œ Subscription์„ ์ง€์›ํ•ด์š”.

๊ทธ๋ž˜ํ”„ํ์—˜์„ ์‚ฌ์šฉํ•˜๋ฉด, ๋งˆ์น˜ ์žฌ๋Šฅ๋„ท์—์„œ ์›ํ•˜๋Š” ์žฌ๋Šฅ์„ ์ •ํ™•ํžˆ ์ฐพ์•„ ์—ฐ๊ฒฐํ•˜๋“ฏ์ด, ํด๋ผ์ด์–ธํŠธ๊ฐ€ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์ •ํ™•ํžˆ ์š”์ฒญํ•˜๊ณ  ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ต๋‹ˆ๋‹ค. ์ด๋Š” ๋„คํŠธ์›Œํฌ ํšจ์œจ์„ฑ์„ ํฌ๊ฒŒ ๋†’์ด๊ณ , ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์„ฑ๋Šฅ์„ ๊ฐœ์„ ํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋ผ์š”. ๐Ÿ‘

GraphQL vs REST API ๋น„๊ต GraphQL REST API ๋ฐ์ดํ„ฐ ์š”์ฒญ ํšจ์œจ์„ฑ

์œ„ ๊ทธ๋ฆผ์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋“ฏ์ด, ๊ทธ๋ž˜ํ”„ํ์—˜์€ REST API์— ๋น„ํ•ด ๋ฐ์ดํ„ฐ ์š”์ฒญ์˜ ํšจ์œจ์„ฑ์ด ํ›จ์”ฌ ๋†’์Šต๋‹ˆ๋‹ค. ์ด๋Š” ๊ณง ์„œ๋ฒ„์˜ ๋ถ€ํ•˜๋ฅผ ์ค„์ด๊ณ , ํด๋ผ์ด์–ธํŠธ์˜ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œํ‚ค๋Š” ๊ฒฐ๊ณผ๋กœ ์ด์–ด์ง€์ฃ . ๐Ÿ˜Ž

ํ•˜์ง€๋งŒ ์ด๋ ‡๊ฒŒ ์ข‹์€ ๊ทธ๋ž˜ํ”„ํ์—˜๋„ ํ•œ ๊ฐ€์ง€ ๊ณ ๋ฏผ๊ฑฐ๋ฆฌ๊ฐ€ ์žˆ์–ด์š”. ๋ฐ”๋กœ ์บ์‹ฑ์ž…๋‹ˆ๋‹ค. REST API๋Š” URL ๊ธฐ๋ฐ˜์œผ๋กœ ์‰ฝ๊ฒŒ ์บ์‹ฑ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ๊ทธ๋ž˜ํ”„ํ์—˜์€ ๊ทธ๋ ‡์ง€ ์•Š๊ฑฐ๋“ ์š”. ๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ๋Š” ํŠน๋ณ„ํ•œ ์ „๋žต์ด ํ•„์š”ํ•œ ๊ฑฐ์˜ˆ์š”!

์ž, ์ด์ œ ๊ทธ๋ž˜ํ”„ํ์—˜์— ๋Œ€ํ•ด ๊ธฐ๋ณธ์ ์ธ ์ดํ•ด๋ฅผ ํ–ˆ์œผ๋‹ˆ, ๋ณธ๊ฒฉ์ ์œผ๋กœ ์บ์‹ฑ ์ „๋žต์— ๋Œ€ํ•ด ์•Œ์•„๋ณผ๊นŒ์š”? ๋‹ค์Œ ์„น์…˜์—์„œ ๊ณ„์†๋ฉ๋‹ˆ๋‹ค! ๐Ÿš€

๐Ÿง  ๊ทธ๋ž˜ํ”„ํ์—˜ ์„œ๋ฒ„์˜ ์บ์‹ฑ ํ•„์š”์„ฑ

์—ฌ๋Ÿฌ๋ถ„, ์บ์‹ฑ์ด ์™œ ์ค‘์š”ํ•œ์ง€ ์•„์‹œ๋‚˜์š”? ์บ์‹ฑ์€ ๋งˆ์น˜ ์šฐ๋ฆฌ๊ฐ€ ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ๋ฌผ๊ฑด์„ ์†์ด ๋‹ฟ๊ธฐ ์‰ฌ์šด ๊ณณ์— ๋‘๋Š” ๊ฒƒ๊ณผ ๊ฐ™์•„์š”. ๋ฐ์ดํ„ฐ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋ž๋‹ˆ๋‹ค. ์ž์ฃผ ์š”์ฒญ๋˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋น ๋ฅด๊ฒŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๊ณณ์— ์ €์žฅํ•ด๋‘๋ฉด, ์ „์ฒด์ ์ธ ์‹œ์Šคํ…œ์˜ ์„ฑ๋Šฅ์ด ํฌ๊ฒŒ ํ–ฅ์ƒ๋˜์ฃ . ๐Ÿš€

์บ์‹ฑ์˜ ์ฃผ์š” ์ด์ :

  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ฟผ๋ฆฌ ํšŸ์ˆ˜ ๊ฐ์†Œ
  • ์‘๋‹ต ์‹œ๊ฐ„ ๋‹จ์ถ•
  • ์„œ๋ฒ„ ๋ถ€ํ•˜ ๊ฐ์†Œ
  • ์‚ฌ์šฉ์ž ๊ฒฝํ—˜ ๊ฐœ์„ 

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

์˜ˆ๋ฅผ ๋“ค์–ด, ์žฌ๋Šฅ๋„ท๊ณผ ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ ์‚ฌ์šฉ์ž ํ”„๋กœํ•„ ์ •๋ณด๋ฅผ ์กฐํšŒํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•ด๋ณผ๊นŒ์š”? ์‚ฌ์šฉ์ž A์˜ ๊ธฐ๋ณธ ์ •๋ณด, ๋ณด์œ  ๊ธฐ์ˆ , ํ‰์  ๋“ฑ์„ ๊ฐ๊ฐ ๋‹ค๋ฅธ ์ฟผ๋ฆฌ๋กœ ์š”์ฒญํ•  ์ˆ˜ ์žˆ๊ฒ ์ฃ . ์ด๋•Œ ์บ์‹ฑ ์ „๋žต์ด ์—†๋‹ค๋ฉด, ๋งค๋ฒˆ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ ‘๊ทผํ•ด์•ผ ํ•  ๊ฑฐ์˜ˆ์š”. ํ•˜์ง€๋งŒ ์ ์ ˆํ•œ ์บ์‹ฑ ์ „๋žต์ด ์žˆ๋‹ค๋ฉด? ํ•œ ๋ฒˆ ์กฐํšŒํ•œ ์ •๋ณด๋ฅผ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด ํ›จ์”ฌ ํšจ์œจ์ ์ด๊ฒ ์ฃ ! ๐Ÿ‘

์บ์‹ฑ์˜ ํšจ๊ณผ ์บ์‹ฑ ์ ์šฉ ์บ์‹ฑ ๋ฏธ์ ์šฉ ์‘๋‹ต ์‹œ๊ฐ„

์œ„ ๊ทธ๋ฆผ์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋“ฏ์ด, ์บ์‹ฑ์„ ์ ์šฉํ–ˆ์„ ๋•Œ์™€ ๊ทธ๋ ‡์ง€ ์•Š์„ ๋•Œ์˜ ์‘๋‹ต ์‹œ๊ฐ„ ์ฐจ์ด๋Š” ์ƒ๋‹นํžˆ ํฝ๋‹ˆ๋‹ค. ์บ์‹ฑ์„ ์ ์šฉํ•˜๋ฉด ๋ฐ์ดํ„ฐ๋ฅผ ๋น ๋ฅด๊ฒŒ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์–ด, ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์ด ํฌ๊ฒŒ ๊ฐœ์„ ๋˜์ฃ . ๐Ÿ˜Š

ํ•˜์ง€๋งŒ ์—ฌ๊ธฐ์„œ ํ•œ ๊ฐ€์ง€ ๊ณ ๋ฏผ์ด ์ƒ๊น๋‹ˆ๋‹ค. ๊ทธ๋ž˜ํ”„ํ์—˜์—์„œ๋Š” ์–ด๋–ป๊ฒŒ ํšจ๊ณผ์ ์œผ๋กœ ์บ์‹ฑ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์„๊นŒ์š”? REST API์™€๋Š” ๋‹ฌ๋ฆฌ, ๊ทธ๋ž˜ํ”„ํ์—˜์€ ์š”์ฒญ๋งˆ๋‹ค ๋‹ค๋ฅธ ํ˜•ํƒœ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ์–ด์„œ ๋‹จ์ˆœํžˆ URL ๊ธฐ๋ฐ˜์˜ ์บ์‹ฑ์œผ๋กœ๋Š” ๋ถ€์กฑํ•˜๊ฑฐ๋“ ์š”. ๐Ÿค”

๋ฐ”๋กœ ์ด ์ง€์ ์—์„œ ๋ฐ์ดํ„ฐ๋กœ๋”(DataLoader)๊ฐ€ ๋“ฑ์žฅํ•ฉ๋‹ˆ๋‹ค! ๋ฐ์ดํ„ฐ๋กœ๋”๋Š” ๊ทธ๋ž˜ํ”„ํ์—˜ ํ™˜๊ฒฝ์—์„œ ํšจ๊ณผ์ ์ธ ์บ์‹ฑ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ์˜ˆ์š”. ๋‹ค์Œ ์„น์…˜์—์„œ ๋ฐ์ดํ„ฐ๋กœ๋”์— ๋Œ€ํ•ด ์ž์„ธํžˆ ์•Œ์•„๋ณด๋„๋ก ํ• ๊นŒ์š”? ๊ณ„์†ํ•ด์„œ ํ•จ๊ป˜ ํ•ด์ฃผ์„ธ์š”! ๐Ÿš€

๐Ÿ” ๋ฐ์ดํ„ฐ๋กœ๋”(DataLoader)๋ž€?

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

๋ฐ์ดํ„ฐ๋กœ๋”์˜ ์ฃผ์š” ๊ธฐ๋Šฅ:

  • ๋ฐฐ์น˜ ๋กœ๋”ฉ (Batching)
  • ์บ์‹ฑ (Caching)
  • ์ค‘๋ณต ์š”์ฒญ ์ œ๊ฑฐ (Deduplication)

๋ฐ์ดํ„ฐ๋กœ๋”๋Š” ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ• ๊นŒ์š”? ๊ฐ„๋‹จํ•œ ์˜ˆ์‹œ๋ฅผ ํ†ตํ•ด ์‚ดํŽด๋ณผ๊ฒŒ์š”. ๐Ÿง


const DataLoader = require('dataloader');

const userLoader = new DataLoader(keys => fetchUsersFromDB(keys));

// ์‚ฌ์šฉ ์˜ˆ์‹œ
const user1 = userLoader.load(1);
const user2 = userLoader.load(2);
const user3 = userLoader.load(1);  // ์บ์‹œ์—์„œ ๊ฐ€์ ธ์˜ด
  

์œ„ ์ฝ”๋“œ์—์„œ userLoader๋Š” ์‚ฌ์šฉ์ž ID๋ฅผ ๋ฐ›์•„ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๋กœ๋“œํ•˜๋Š” ๋ฐ์ดํ„ฐ๋กœ๋”์˜ˆ์š”. load ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ๋งˆ๋‹ค ๋ฐ์ดํ„ฐ๋กœ๋”๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค:

  1. ์š”์ฒญ๋œ ํ‚ค(์—ฌ๊ธฐ์„œ๋Š” ์‚ฌ์šฉ์ž ID)๋ฅผ ๋ชจ์•„๋‘ก๋‹ˆ๋‹ค.
  2. ์ด๋ฒคํŠธ ๋ฃจํ”„์˜ ํ˜„์žฌ ํ„ด์ด ๋๋‚˜๋ฉด, ๋ชจ์•„๋‘” ํ‚ค๋“ค์„ ํ•œ ๋ฒˆ์— fetchUsersFromDB ํ•จ์ˆ˜์— ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.
  3. ๊ฒฐ๊ณผ๋ฅผ ์บ์‹œ์— ์ €์žฅํ•˜๊ณ , ๊ฐ load ํ˜ธ์ถœ์— ๋Œ€ํ•œ ํ”„๋กœ๋ฏธ์Šค๋ฅผ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์—ฌ๋Ÿฌ ๋ฒˆ์˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ฟผ๋ฆฌ๋ฅผ ํ•˜๋‚˜๋กœ ํ•ฉ์น  ์ˆ˜ ์žˆ๊ณ (๋ฐฐ์น˜ ๋กœ๋”ฉ), ์ด๋ฏธ ๋กœ๋“œํ•œ ๋ฐ์ดํ„ฐ๋Š” ์บ์‹œ์—์„œ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์–ด์š”(์บ์‹ฑ). ๋˜ํ•œ ๊ฐ™์€ ํ‚ค์— ๋Œ€ํ•œ ์ค‘๋ณต ์š”์ฒญ๋„ ์ž๋™์œผ๋กœ ์ œ๊ฑฐ๋ฉ๋‹ˆ๋‹ค(์ค‘๋ณต ์š”์ฒญ ์ œ๊ฑฐ). ๐Ÿ‘

๋ฐ์ดํ„ฐ๋กœ๋”์˜ ์ž‘๋™ ๋ฐฉ์‹ ํด๋ผ์ด์–ธํŠธ ์š”์ฒญ ๋ฐ์ดํ„ฐ๋กœ๋” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ฒฐ๊ณผ ๋ฐ˜ํ™˜

์œ„ ๊ทธ๋ฆผ์€ ๋ฐ์ดํ„ฐ๋กœ๋”์˜ ์ž‘๋™ ๋ฐฉ์‹์„ ๊ฐ„๋‹จํžˆ ๋„์‹ํ™”ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ์˜ ์š”์ฒญ์ด ๋ฐ์ดํ„ฐ๋กœ๋”๋ฅผ ๊ฑฐ์ณ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋กœ ์ „๋‹ฌ๋˜๊ณ , ๊ทธ ๊ฒฐ๊ณผ๊ฐ€ ๋‹ค์‹œ ํด๋ผ์ด์–ธํŠธ๋กœ ๋ฐ˜ํ™˜๋˜๋Š” ๊ณผ์ •์„ ๋ณด์—ฌ์ฃผ๊ณ  ์žˆ์–ด์š”. ๋ฐ์ดํ„ฐ๋กœ๋”๋Š” ์ด ๊ณผ์ •์—์„œ ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ์™€ ์บ์‹ฑ์„ ์ˆ˜ํ–‰ํ•˜์—ฌ ํšจ์œจ์„ฑ์„ ๋†’์ž…๋‹ˆ๋‹ค. ๐Ÿš€

๋ฐ์ดํ„ฐ๋กœ๋”๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ด์ ์„ ์–ป์„ ์ˆ˜ ์žˆ์–ด์š”:

  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ฟผ๋ฆฌ ํšŸ์ˆ˜ ๊ฐ์†Œ๋กœ ์ธํ•œ ์„ฑ๋Šฅ ํ–ฅ์ƒ
  • ์ค‘๋ณต ๋ฐ์ดํ„ฐ ๋กœ๋”ฉ ๋ฐฉ์ง€๋กœ ์ธํ•œ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ ์ตœ์ ํ™”
  • ์ผ๊ด€๋œ ๋ฐ์ดํ„ฐ ์•ก์„ธ์Šค ํŒจํ„ด ์ œ๊ณต์œผ๋กœ ์ฝ”๋“œ ํ’ˆ์งˆ ํ–ฅ์ƒ

์ด๋Ÿฌํ•œ ์ด์ ๋“ค์€ ํŠนํžˆ ๋Œ€๊ทœ๋ชจ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋”์šฑ ๋น›์„ ๋ฐœํ•˜์ฃ . ์žฌ๋Šฅ๋„ท๊ณผ ๊ฐ™์ด ๋‹ค์–‘ํ•œ ์‚ฌ์šฉ์ž์™€ ์žฌ๋Šฅ ์ •๋ณด๋ฅผ ๋‹ค๋ฃจ๋Š” ํ”Œ๋žซํผ์—์„œ๋Š” ๋ฐ์ดํ„ฐ๋กœ๋”์˜ ํ™œ์šฉ์ด ์‹œ์Šคํ…œ ์ „๋ฐ˜์˜ ์„ฑ๋Šฅ์„ ํฌ๊ฒŒ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ๋‹ต๋‹ˆ๋‹ค. ๐Ÿ˜Š

์ž, ์ด์ œ ๋ฐ์ดํ„ฐ๋กœ๋”์— ๋Œ€ํ•ด ๊ธฐ๋ณธ์ ์ธ ์ดํ•ด๋ฅผ ํ•˜์…จ๋‚˜์š”? ๋‹ค์Œ ์„น์…˜์—์„œ๋Š” ์‹ค์ œ๋กœ ๊ทธ๋ž˜ํ”„ํ์—˜ ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋กœ๋”๋ฅผ ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ•˜๊ณ  ํ™œ์šฉํ•˜๋Š”์ง€ ๋” ์ž์„ธํžˆ ์•Œ์•„๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ณ„์†ํ•ด์„œ ํ•จ๊ป˜ ํ•ด์ฃผ์„ธ์š”! ๐Ÿš€

๐Ÿ› ๏ธ ๊ทธ๋ž˜ํ”„ํ์—˜ ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋กœ๋” ๊ตฌํ˜„ํ•˜๊ธฐ

์ž, ์ด์ œ ์‹ค์ œ๋กœ ๊ทธ๋ž˜ํ”„ํ์—˜ ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋กœ๋”๋ฅผ ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ•˜๊ณ  ํ™œ์šฉํ•˜๋Š”์ง€ ์•Œ์•„๋ณผ ์ฐจ๋ก€์˜ˆ์š”! ๋งˆ์น˜ ์žฌ๋Šฅ๋„ท์—์„œ ๋‹ค์–‘ํ•œ ์žฌ๋Šฅ์„ ํšจ์œจ์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๋“ฏ์ด, ์šฐ๋ฆฌ๋„ ๋ฐ์ดํ„ฐ๋ฅผ ์Šค๋งˆํŠธํ•˜๊ฒŒ ๊ด€๋ฆฌํ•ด๋ณผ ๊ฑฐ์˜ˆ์š”. ์ค€๋น„๋˜์…จ๋‚˜์š”? Let's dive in! ๐ŸŠโ€โ™‚๏ธ

๋จผ์ €, ๊ฐ„๋‹จํ•œ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๋‹ค๋ฃจ๋Š” ๊ทธ๋ž˜ํ”„ํ์—˜ ์„œ๋ฒ„๋ฅผ ์˜ˆ๋กœ ๋“ค์–ด๋ณผ๊ฒŒ์š”. ์ด ์„œ๋ฒ„๋Š” ์‚ฌ์šฉ์ž ID๋กœ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ์กฐํšŒํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•ด๋ณผ๊นŒ์š”?


const { ApolloServer, gql } = require('apollo-server');
const DataLoader = require('dataloader');

// ๊ฐ€์ƒ์˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค
const users = [
  { id: '1', name: '๊น€์žฌ๋Šฅ', skill: '์›น๊ฐœ๋ฐœ' },
  { id: '2', name: '์ดtalented', skill: '๋””์ž์ธ' },
  { id: '3', name: '๋ฐ•๋Šฅ๋ ฅ', skill: '๋งˆ์ผ€ํŒ…' },
];

// ๋ฐ์ดํ„ฐ๋กœ๋” ์ƒ์„ฑ
const userLoader = new DataLoader(async (ids) => {
  console.log('๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์‚ฌ์šฉ์ž ๋กœ๋”ฉ:', ids);
  return ids.map(id => users.find(user => user.id === id));
});

// ์Šคํ‚ค๋งˆ ์ •์˜
const typeDefs = gql`
  type User {
    id: ID!
    name: String!
    skill: String!
  }

  type Query {
    user(id: ID!): User
    users(ids: [ID!]!): [User]!
  }
`;

// ๋ฆฌ์กธ๋ฒ„ ์ •์˜
const resolvers = {
  Query: {
    user: (_, { id }) => userLoader.load(id),
    users: (_, { ids }) => userLoader.loadMany(ids),
  },
};

// ์„œ๋ฒ„ ์ƒ์„ฑ ๋ฐ ์‹คํ–‰
const server = new ApolloServer({ typeDefs, resolvers });

server.listen().then(({ url }) => {
  console.log(`๐Ÿš€ Server ready at ${url}`);
});
  

์œ„ ์ฝ”๋“œ์—์„œ ์ฃผ๋ชฉํ•ด์•ผ ํ•  ๋ถ€๋ถ„๋“ค์„ ํ•˜๋‚˜์”ฉ ์‚ดํŽด๋ณผ๊นŒ์š”? ๐Ÿ˜Š

  1. ๋ฐ์ดํ„ฐ๋กœ๋” ์ƒ์„ฑ: userLoader๋ฅผ ์ƒ์„ฑํ•  ๋•Œ, ๋ฐฐ์น˜ ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋Š” ID ๋ฐฐ์—ด์„ ๋ฐ›์•„ ํ•ด๋‹นํ•˜๋Š” ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ๋ฐ˜ํ™˜ํ•ด์š”.
  2. ๋ฆฌ์กธ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋กœ๋” ์‚ฌ์šฉ: user์™€ users ์ฟผ๋ฆฌ ๋ฆฌ์กธ๋ฒ„์—์„œ ๊ฐ๊ฐ load์™€ loadMany ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ๊ตฌํ˜„ํ•˜๋ฉด ์–ด๋–ค ์žฅ์ ์ด ์žˆ์„๊นŒ์š”? ๐Ÿค”

๋ฐ์ดํ„ฐ๋กœ๋” ์‚ฌ์šฉ์˜ ์žฅ์ :

  • ์—ฌ๋Ÿฌ ์š”์ฒญ์„ ํ•˜๋‚˜์˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ฟผ๋ฆฌ๋กœ ํ•ฉ์น  ์ˆ˜ ์žˆ์–ด์š”. (๋ฐฐ์น˜ ๋กœ๋”ฉ)
  • ๊ฐ™์€ ID์— ๋Œ€ํ•œ ์š”์ฒญ์€ ์บ์‹œ์—์„œ ์ฒ˜๋ฆฌ๋ผ์š”. (์บ์‹ฑ)
  • ๊ฐ™์€ ์š”์ฒญ ๋‚ด์—์„œ ์ค‘๋ณต๋œ ID ์š”์ฒญ์„ ์ž๋™์œผ๋กœ ์ œ๊ฑฐํ•ด์ค˜์š”. (์ค‘๋ณต ์ œ๊ฑฐ)

์ด์ œ ์ด ์„œ๋ฒ„์— ๋ช‡ ๊ฐ€์ง€ ์ฟผ๋ฆฌ๋ฅผ ๋‚ ๋ ค๋ณผ๊นŒ์š”? ๐Ÿš€


query {
  user1: user(id: "1") {
    name
    skill
  }
  user2: user(id: "2") {
    name
    skill
  }
  user3: user(id: "1") {  # ์ค‘๋ณต ์š”์ฒญ
    name
    skill
  }
}
  

์ด ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด, ์ฝ˜์†”์—๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋กœ๊ทธ๊ฐ€ ์ฐํž ๊ฑฐ์˜ˆ์š”:


๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์‚ฌ์šฉ์ž ๋กœ๋”ฉ: [ '1', '2' ]
  

๋ณด์ด์‹œ๋‚˜์š”? ์„ธ ๋ฒˆ์˜ ์‚ฌ์šฉ์ž ์š”์ฒญ์ด ์žˆ์—ˆ์ง€๋งŒ, ์‹ค์ œ๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์š”์ฒญํ•œ ๊ฒƒ์€ ๋‹จ ํ•œ ๋ฒˆ์ด์—์š”! ์ด๊ฒŒ ๋ฐ”๋กœ ๋ฐ์ดํ„ฐ๋กœ๋”์˜ ๋งˆ๋ฒ•์ด์ฃ . ๐Ÿ˜Ž

๋ฐ์ดํ„ฐ๋กœ๋”์˜ ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ ๋ฐ ์บ์‹ฑ ํด๋ผ์ด์–ธํŠธ ์š”์ฒญ ๋ฐ์ดํ„ฐ๋กœ๋” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์บ์‹œ ๊ฒฐ๊ณผ ๋ฐ˜ํ™˜

์œ„ ๊ทธ๋ฆผ์€ ๋ฐ์ดํ„ฐ๋กœ๋”๊ฐ€ ์–ด๋–ป๊ฒŒ ์š”์ฒญ์„ ๋ฐฐ์น˜ ์ฒ˜๋ฆฌํ•˜๊ณ  ์บ์‹ฑํ•˜๋Š”์ง€๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ์˜ ์—ฌ๋Ÿฌ ์š”์ฒญ์ด ๋ฐ์ดํ„ฐ๋กœ๋”์— ์˜ํ•ด ํ•˜๋‚˜์˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ฟผ๋ฆฌ๋กœ ํ•ฉ์ณ์ง€๊ณ , ๊ฒฐ๊ณผ๋Š” ์บ์‹œ์— ์ €์žฅ๋˜์–ด ํ›„์† ์š”์ฒญ์— ์žฌ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์‹œ์Šคํ…œ์˜ ํšจ์œจ์„ฑ์„ ํฌ๊ฒŒ ํ–ฅ์ƒ์‹œํ‚ค์ฃ . ๐Ÿš€

์ด์ œ ๋ฐ์ดํ„ฐ๋กœ๋”๋ฅผ ํ™œ์šฉํ•œ ๊ทธ๋ž˜ํ”„ํ์—˜ ์„œ๋ฒ„ ๊ตฌํ˜„์— ๋Œ€ํ•ด ๊ธฐ๋ณธ์ ์ธ ์ดํ•ด๋ฅผ ํ•˜์…จ์„ ๊ฑฐ์˜ˆ์š”. ํ•˜์ง€๋งŒ ์—ฌ๊ธฐ์„œ ๋์ด ์•„๋‹™๋‹ˆ๋‹ค! ์‹ค์ œ ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ๋Š” ๋” ๋ณต์žกํ•œ ์ƒํ™ฉ๋“ค์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์–ด์š”. ๊ทธ๋Ÿผ ๋ช‡ ๊ฐ€์ง€ ๊ณ ๊ธ‰ ๊ธฐ๋ฒ•๋“ค์„ ์‚ดํŽด๋ณผ๊นŒ์š”? ๐Ÿ˜Ž

1. ๋ฐ์ดํ„ฐ๋กœ๋” ํŒฉํ† ๋ฆฌ ์‚ฌ์šฉํ•˜๊ธฐ

์‹ค์ œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ๋Š” ์š”์ฒญ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๋กœ๋” ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์š”์ฒญ ๊ฐ„ ์บ์‹œ๊ฐ€ ๊ณต์œ ๋˜์ง€ ์•Š์•„ ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์–ด์š”.


const createLoaders = () => ({
  userLoader: new DataLoader(batchUsers),
  postLoader: new DataLoader(batchPosts),
});

const server = new ApolloServer({
  typeDefs,
  resolvers,
  context: () => ({
    loaders: createLoaders(),
  }),
});
  

2. ๋ฐ์ดํ„ฐ๋กœ๋” ์บ์‹œ ์ œ์–ดํ•˜๊ธฐ

๋•Œ๋กœ๋Š” ์บ์‹œ๋ฅผ ์ˆ˜๋™์œผ๋กœ ์ œ์–ดํ•ด์•ผ ํ•  ํ•„์š”๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ๋กœ๋”๋Š” ์ด๋ฅผ ์œ„ํ•œ ๋ฉ”์„œ๋“œ๋ฅผ ์ œ๊ณตํ•ด์š”.


// ์บ์‹œ ํด๋ฆฌ์–ด
userLoader.clearAll();

// ํŠน์ • ํ‚ค์˜ ์บ์‹œ๋งŒ ํด๋ฆฌ์–ด
userLoader.clear(userId);

// ์บ์‹œ์— ์ง์ ‘ ๊ฐ’ ์„ค์ •
userLoader.prime(userId, userData);
  

3. ์—๋Ÿฌ ์ฒ˜๋ฆฌ

๋ฐ์ดํ„ฐ๋กœ๋”๋Š” ๋ฐฐ์น˜ ํ•จ์ˆ˜์—์„œ ๋ฐœ์ƒํ•œ ์—๋Ÿฌ๋ฅผ ๊ฐœ๋ณ„ ํ”„๋กœ๋ฏธ์Šค๋กœ ์ „ํŒŒํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ™œ์šฉํ•ด ์„ธ๋ฐ€ํ•œ ์—๋Ÿฌ ์ฒ˜๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•ด์š”.


const userLoader = new DataLoader(async (ids) => {
  try {
    const users = await fetchUsersFromDB(ids);
    return ids.map(id => {
      const user = users.find(u => u.id === id);
      if (!user) throw new Error(`User not found: ${id}`);
      return user;
    });
  } catch (error) {
    console.error('Failed to load users:', error);
    throw error;
  }
});
  

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

๐Ÿ’ก Pro Tip: ๋ฐ์ดํ„ฐ๋กœ๋”๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ํ•ญ์ƒ ์„ฑ๋Šฅ๊ณผ ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ ์‚ฌ์ด์˜ ๊ท ํ˜•์„ ๊ณ ๋ คํ•ด์•ผ ํ•ด์š”. ์บ์‹ฑ์€ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œํ‚ค์ง€๋งŒ, ๋„ˆ๋ฌด ์˜ค๋ž˜๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ œ๊ณตํ•  risk๋„ ์žˆ์ฃ . ์ ์ ˆํ•œ ์บ์‹œ ๋ฌดํšจํ™” ์ „๋žต์„ ์„ธ์šฐ๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค!

์ž, ์—ฌ๊ธฐ๊นŒ์ง€ ๊ทธ๋ž˜ํ”„ํ์—˜ ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋กœ๋”๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  ํ™œ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์•Œ์•„๋ดค์–ด์š”. ์ด์ œ ์—ฌ๋Ÿฌ๋ถ„๋„ ๋ฐ์ดํ„ฐ๋กœ๋”๋ฅผ ํ™œ์šฉํ•ด ํšจ์œจ์ ์ธ ๊ทธ๋ž˜ํ”„ํ์—˜ ์„œ๋ฒ„๋ฅผ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ์„ ๊ฑฐ์˜ˆ์š”! ๐Ÿš€

๋‹ค์Œ ์„น์…˜์—์„œ๋Š” ์‹ค์ œ ํ”„๋กœ์ ํŠธ์— ๋ฐ์ดํ„ฐ๋กœ๋”๋ฅผ ์ ์šฉํ•  ๋•Œ ๊ณ ๋ คํ•ด์•ผ ํ•  ์‚ฌํ•ญ๋“ค๊ณผ best practices์— ๋Œ€ํ•ด ๋” ์ž์„ธํžˆ ์•Œ์•„๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ณ„์†ํ•ด์„œ ํ•จ๊ป˜ ํ•ด์ฃผ์„ธ์š”! ๐Ÿ˜ƒ

๐Ÿ’ผ ์‹ค์ œ ํ”„๋กœ์ ํŠธ์— ๋ฐ์ดํ„ฐ๋กœ๋” ์ ์šฉํ•˜๊ธฐ: Best Practices

์ž, ์ด์ œ ์šฐ๋ฆฌ๋Š” ๋ฐ์ดํ„ฐ๋กœ๋”์˜ ๊ธฐ๋ณธ ๊ฐœ๋…๊ณผ ๊ตฌํ˜„ ๋ฐฉ๋ฒ•์„ ์•Œ๊ฒŒ ๋˜์—ˆ์–ด์š”. ํ•˜์ง€๋งŒ ์‹ค์ œ ํ”„๋กœ์ ํŠธ, ํŠนํžˆ ์žฌ๋Šฅ๋„ท๊ณผ ๊ฐ™์€ ๋ณต์žกํ•œ ํ”Œ๋žซํผ์— ๋ฐ์ดํ„ฐ๋กœ๋”๋ฅผ ์ ์šฉํ•  ๋•Œ๋Š” ์ข€ ๋” ์„ธ์‹ฌํ•œ ์ ‘๊ทผ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿผ ๋ช‡ ๊ฐ€์ง€ best practices๋ฅผ ์‚ดํŽด๋ณผ๊นŒ์š”? ๐Ÿง

1. ์ ์ ˆํ•œ ๋ฐฐ์น˜ ํฌ๊ธฐ ์„ ํƒ

๋ฐฐ์น˜ ํฌ๊ธฐ๋Š” ์„ฑ๋Šฅ์— ํฐ ์˜ํ–ฅ์„ ๋ฏธ์นฉ๋‹ˆ๋‹ค. ๋„ˆ๋ฌด ์ž‘์œผ๋ฉด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ฟผ๋ฆฌ๊ฐ€ ์ž์ฃผ ๋ฐœ์ƒํ•˜๊ณ , ๋„ˆ๋ฌด ํฌ๋ฉด ์ฒซ ๋ฒˆ์งธ ์‘๋‹ต์ด ์ง€์—ฐ๋  ์ˆ˜ ์žˆ์–ด์š”.


const userLoader = new DataLoader(async (ids) => {
  if (ids.length > 100) {
    console.warn(`Large batch detected: ${ids.length} users`);
  }
  return fetchUsersFromDB(ids);
}, { maxBatchSize: 100 });
  

2. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ธ๋ฑ์‹ฑ ์ตœ์ ํ™”

๋ฐ์ดํ„ฐ๋กœ๋”๋Š” ์ฃผ๋กœ ID ๊ธฐ๋ฐ˜์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๊ด€๋ จ ์ปฌ๋Ÿผ์— ์ ์ ˆํ•œ ์ธ๋ฑ์Šค๋ฅผ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ด์š”.


-- SQL ์˜ˆ์‹œ
CREATE INDEX idx_users_id ON users(id);
  

3. ์บ์‹œ ์ „๋žต ์ˆ˜๋ฆฝ

๋ฐ์ดํ„ฐ์˜ ํŠน์„ฑ์— ๋”ฐ๋ผ ์ ์ ˆํ•œ ์บ์‹œ ์ „๋žต์„ ์„ธ์›Œ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ž์ฃผ ๋ณ€๊ฒฝ๋˜๋Š” ๋ฐ์ดํ„ฐ๋Š” ์บ์‹œ ์œ ํšจ ๊ธฐ๊ฐ„์„ ์งง๊ฒŒ ๊ฐ€์ ธ๊ฐ€๋Š” ๊ฒƒ์ด ์ข‹์•„์š”.


const userLoader = new DataLoader(fetchUsersFromDB, {
  cacheMap: new LRU({ max: 1000, maxAge: 1000 * 60 * 5 }) // 5๋ถ„ ์บ์‹œ
});
  

4. ๋ฐ์ดํ„ฐ ์ผ๊ด€์„ฑ ์œ ์ง€

๋ฐ์ดํ„ฐ๊ฐ€ ์—…๋ฐ์ดํŠธ๋˜๋ฉด ๊ด€๋ จ๋œ ์บ์‹œ๋ฅผ ๋ฌดํšจํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด pub/sub ์‹œ์Šคํ…œ์„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์–ด์š”.


// ๋ฐ์ดํ„ฐ ์—…๋ฐ์ดํŠธ ์‹œ
await updateUserInDB(userData);
pubsub.publish('USER_UPDATED', { userId: userData.id });

// ๊ตฌ๋…
pubsub.subscribe('USER_UPDATED', ({ userId }) => {
  userLoader.clear(userId);
});
  

5. ์—๋Ÿฌ ์ฒ˜๋ฆฌ ๋ฐ ๋กœ๊น…

๋ฐ์ดํ„ฐ๋กœ๋”์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์—๋Ÿฌ๋ฅผ ์ ์ ˆํžˆ ์ฒ˜๋ฆฌํ•˜๊ณ  ๋กœ๊น…ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋ฌธ์ œ ํ•ด๊ฒฐ๊ณผ ์„ฑ๋Šฅ ์ตœ์ ํ™”์— ํฐ ๋„์›€์ด ๋ผ์š”.


const userLoader = new DataLoader(async (ids) => {
  try {
    const users = await fetchUsersFromDB(ids);
    return users.map(user => user || new Error(`User not found`));
  } catch (error) {
    console.error('Failed to load users:', error);
    throw error;
  }
});
  

6. ์„ฑ๋Šฅ ๋ชจ๋‹ˆํ„ฐ๋ง

๋ฐ์ดํ„ฐ๋กœ๋”์˜ ์„ฑ๋Šฅ์„ ์ง€์†์ ์œผ๋กœ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜๊ณ  ์ตœ์ ํ™”ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ์บ์‹œ ํžˆํŠธ์œจ, ์ฟผ๋ฆฌ ์‹คํ–‰ ์‹œ๊ฐ„ ๋“ฑ์„ ์ถ”์ ํ•ด๋ณด์„ธ์š”.


const userLoader = new DataLoader(fetchUsersFromDB, {
  onBatch: (ids) => {
    console.log(`Batch size: ${ids.length}`);
    performanceMetrics.recordBatchSize(ids.length);
  }
});
  

์ด๋Ÿฌํ•œ best practices๋ฅผ ์ ์šฉํ•˜๋ฉด, ์žฌ๋Šฅ๋„ท๊ณผ ๊ฐ™์€ ๋ณต์žกํ•œ ํ”Œ๋žซํผ์—์„œ๋„ ๋ฐ์ดํ„ฐ๋กœ๋”๋ฅผ ํšจ๊ณผ์ ์œผ๋กœ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์–ด์š”. ์‚ฌ์šฉ์ž๋“ค์€ ๋น ๋ฅธ ์‘๋‹ต ์‹œ๊ฐ„๊ณผ ์•ˆ์ •์ ์ธ ์„œ๋น„์Šค๋ฅผ ๊ฒฝํ—˜ํ•  ์ˆ˜ ์žˆ๊ฒ ์ฃ ? ๐Ÿ˜Š

๐Ÿ’ก Pro Tip: ๋ฐ์ดํ„ฐ๋กœ๋”๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ํ•ญ์ƒ "์š”์ฒญ์˜ ํŠน์„ฑ"๊ณผ "๋ฐ์ดํ„ฐ์˜ ํŠน์„ฑ"์„ ๊ณ ๋ คํ•ด์•ผ ํ•ด์š”. ์˜ˆ๋ฅผ ๋“ค์–ด, ์‹ค์‹œ๊ฐ„์„ฑ์ด ์ค‘์š”ํ•œ ๋ฐ์ดํ„ฐ(์˜ˆ: ์‹ค์‹œ๊ฐ„ ์ฑ„ํŒ…)์™€ ์ƒ๋Œ€์ ์œผ๋กœ ์ •์ ์ธ ๋ฐ์ดํ„ฐ(์˜ˆ: ์‚ฌ์šฉ์ž ํ”„๋กœํ•„)๋Š” ๋‹ค๋ฅธ ์บ์‹ฑ ์ „๋žต์„ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ž, ์—ฌ๊ธฐ๊นŒ์ง€ ๋ฐ์ดํ„ฐ๋กœ๋”๋ฅผ ์‹ค์ œ ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•  ๋•Œ ๊ณ ๋ คํ•ด์•ผ ํ•  ์‚ฌํ•ญ๋“ค๊ณผ best practices์— ๋Œ€ํ•ด ์•Œ์•„๋ดค์–ด์š”. ์ด์ œ ์—ฌ๋Ÿฌ๋ถ„์€ ๋ฐ์ดํ„ฐ๋กœ๋”๋ฅผ ํ™œ์šฉํ•ด ํšจ์œจ์ ์ด๊ณ  ์•ˆ์ •์ ์ธ ๊ทธ๋ž˜ํ”„ํ์—˜ ์„œ๋ฒ„๋ฅผ ๊ตฌ์ถ•ํ•  ์ค€๋น„๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค! ๐Ÿš€

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

๐Ÿ” ๋ฐ์ดํ„ฐ๋กœ๋” ์‚ฌ์šฉ ์‹œ ํ”ํ•œ ๋ฌธ์ œ์™€ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

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

1. N+1 ๋ฌธ์ œ

๋ฌธ์ œ: ๋ฐ์ดํ„ฐ๋กœ๋”๋ฅผ ์‚ฌ์šฉํ•ด๋„ ์—ฌ์ „ํžˆ N+1 ์ฟผ๋ฆฌ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์–ด์š”.

ํ•ด๊ฒฐ๋ฐฉ๋ฒ•: ์ค‘์ฒฉ๋œ ๋ฆฌ์กธ๋ฒ„์—์„œ๋„ ๋ฐ์ดํ„ฐ๋กœ๋”๋ฅผ ์‚ฌ์šฉํ•˜๊ณ , ๊ฐ€๋Šฅํ•˜๋‹ค๋ฉด Join ์ฟผ๋ฆฌ๋ฅผ ํ™œ์šฉํ•˜์„ธ์š”.


const resolvers = {
  User: {
    posts: (user, _, { loaders }) => loaders.postLoader.loadMany(user.postIds)
  }
};
  

2. ์บ์‹œ ์ผ๊ด€์„ฑ ๋ฌธ์ œ

๋ฌธ์ œ: ๋ฐ์ดํ„ฐ๊ฐ€ ์—…๋ฐ์ดํŠธ๋˜์—ˆ๋Š”๋ฐ ์บ์‹œ๋œ ์˜ค๋ž˜๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฐ˜ํ™˜๋  ์ˆ˜ ์žˆ์–ด์š”.

ํ•ด๊ฒฐ๋ฐฉ๋ฒ•: ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ ์‹œ ๊ด€๋ จ ์บ์‹œ๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ๋ฌดํšจํ™”ํ•˜์„ธ์š”.


async function updateUser(userData) {
  await updateUserInDB(userData);
  userLoader.clear(userData.id);
}
  

3. ๋Œ€๋Ÿ‰์˜ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ

๋ฌธ์ œ: ๋„ˆ๋ฌด ๋งŽ์€ ID๋ฅผ ํ•œ ๋ฒˆ์— ๋กœ๋“œํ•˜๋ ค๊ณ  ํ•˜๋ฉด ์„ฑ๋Šฅ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์–ด์š”.

ํ•ด๊ฒฐ๋ฐฉ๋ฒ•: ๋ฐฐ์น˜ ํฌ๊ธฐ๋ฅผ ์ œํ•œํ•˜๊ณ , ํ•„์š”ํ•˜๋‹ค๋ฉด ํŽ˜์ด์ง€๋„ค์ด์…˜์„ ๊ตฌํ˜„ํ•˜์„ธ์š”.


const userLoader = new DataLoader(fetchUsersFromDB, { maxBatchSize: 100 });

async function loadUsers(ids) {
  const chunks = chunk(ids, 100);
  const results = await Promise.all(chunks.map(chunk => userLoader.loadMany(chunk)));
  return results.flat();
}
  

4. ๋ณต์žกํ•œ ๋ฐ์ดํ„ฐ ๊ด€๊ณ„ ์ฒ˜๋ฆฌ

๋ฌธ์ œ: ๋ฐ์ดํ„ฐ ๊ฐ„์˜ ๊ด€๊ณ„๊ฐ€ ๋ณต์žกํ•  ๋•Œ ๋ฐ์ดํ„ฐ๋กœ๋” ์‚ฌ์šฉ์ด ์–ด๋ ค์šธ ์ˆ˜ ์žˆ์–ด์š”.

ํ•ด๊ฒฐ๋ฐฉ๋ฒ•: ๋ณตํ•ฉ ํ‚ค๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, ์—ฌ๋Ÿฌ ๋ฐ์ดํ„ฐ๋กœ๋”๋ฅผ ์กฐํ•ฉํ•ด ์‚ฌ์šฉํ•˜์„ธ์š”.


const userPostLoader = new DataLoader(async (keys) => {
  const [userIds, postIds] = unzip(keys);
  const results = await fetchUserPostsFromDB(userIds, postIds);
  return keys.map(([userId, postId]) => 
    results.find(r => r.userId === userId && r.postId === postId)
  );
});

// ์‚ฌ์šฉ
const userPost = await userPostLoader.load([userId, postId]);
  

5. ์—๋Ÿฌ ์ฒ˜๋ฆฌ์˜ ๋ณต์žก์„ฑ

๋ฌธ์ œ: ๋ฐฐ์น˜ ๋กœ๋”ฉ ์ค‘ ์ผ๋ถ€ ํ•ญ๋ชฉ๋งŒ ์‹คํŒจํ•  ๊ฒฝ์šฐ ์—๋Ÿฌ ์ฒ˜๋ฆฌ๊ฐ€ ๋ณต์žกํ•ด์งˆ ์ˆ˜ ์žˆ์–ด์š”.

ํ•ด๊ฒฐ๋ฐฉ๋ฒ•: ๊ฐœ๋ณ„ ๊ฒฐ๊ณผ์— ๋Œ€ํ•œ ์—๋Ÿฌ ์ฒ˜๋ฆฌ ๋กœ์ง์„ ๊ตฌํ˜„ํ•˜์„ธ์š”.


const userLoader = new DataLoader(async (ids) => {
  const users = await fetchUsersFromDB(ids);
  return ids.map(id => {
    const user = users.find(u => u.id === id);
    return user || new Error(`User not found: ${id}`);
  });
});

// ์‚ฌ์šฉ
const user = await userLoader.load(id).catch(error => {
  console.error('Failed to load user:', error);
  return null;
});
  

์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋“ค์„ ์ž˜ ํ•ด๊ฒฐํ•˜๋ฉด, ์žฌ๋Šฅ๋„ท๊ณผ ๊ฐ™์€ ๋ณต์žกํ•œ ํ”Œ๋žซํผ์—์„œ๋„ ๋ฐ์ดํ„ฐ๋กœ๋”๋ฅผ ํšจ๊ณผ์ ์œผ๋กœ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์–ด์š”. ์‚ฌ์šฉ์ž๋“ค์€ ๋” ๋น ๋ฅด๊ณ  ์•ˆ์ •์ ์ธ ์„œ๋น„์Šค๋ฅผ ๊ฒฝํ—˜ํ•  ์ˆ˜ ์žˆ๊ฒ ์ฃ ? ๐Ÿ˜Š

๐Ÿ’ก Pro Tip: ๋ฐ์ดํ„ฐ๋กœ๋”๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ํ•ญ์ƒ "์ „์ฒด ์‹œ์Šคํ…œ์˜ ๊ด€์ "์—์„œ ์ƒ๊ฐํ•ด์•ผ ํ•ด์š”. ๋‹จ์ˆœํžˆ ์ฟผ๋ฆฌ ํšŸ์ˆ˜๋ฅผ ์ค„์ด๋Š” ๊ฒƒ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, ์ „์ฒด ์‹œ์Šคํ…œ์˜ ๋ณต์žก์„ฑ, ์œ ์ง€๋ณด์ˆ˜์„ฑ, ํ™•์žฅ์„ฑ์„ ๊ณ ๋ คํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋•Œ๋กœ๋Š” ์•ฝ๊ฐ„์˜ ์„ฑ๋Šฅ ์ €ํ•˜๋ฅผ ๊ฐ์ˆ˜ํ•˜๊ณ  ๋” ๋ช…ํ™•ํ•˜๊ณ  ์œ ์ง€๋ณด์ˆ˜ํ•˜๊ธฐ ์‰ฌ์šด ์ฝ”๋“œ๋ฅผ ์„ ํƒํ•˜๋Š” ๊ฒƒ์ด ์žฅ๊ธฐ์ ์œผ๋กœ ๋” ๋‚˜์„ ์ˆ˜ ์žˆ์–ด์š”.

์ž, ์—ฌ๊ธฐ๊นŒ์ง€ ๋ฐ์ดํ„ฐ๋กœ๋”๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ํ”ํžˆ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ๋“ค๊ณผ ๊ทธ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด ์•Œ์•„๋ดค์–ด์š”. ์ด๋Ÿฌํ•œ ์ง€์‹์„ ๋ฐ”ํƒ•์œผ๋กœ ์—ฌ๋Ÿฌ๋ถ„์€ ๋”์šฑ ๊ฒฌ๊ณ ํ•˜๊ณ  ํšจ์œจ์ ์ธ ๊ทธ๋ž˜ํ”„ํ์—˜ ์„œ๋ฒ„๋ฅผ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ์„ ๊ฑฐ์˜ˆ์š”! ๐Ÿš€

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

๐Ÿ“Š ๋ฐ์ดํ„ฐ๋กœ๋” ์‚ฌ์šฉ ์‚ฌ๋ก€ ์—ฐ๊ตฌ: ์„ฑ๋Šฅ ๊ฐœ์„ ์˜ ์‹ค์ œ

์ž, ์ด์ œ ์šฐ๋ฆฌ๊ฐ€ ๋ฐฐ์šด ๋ชจ๋“  ๊ฒƒ์„ ์‹ค์ œ ์‚ฌ๋ก€์— ์ ์šฉํ•ด๋ณผ ์‹œ๊ฐ„์ด์—์š”! ์žฌ๋Šฅ๋„ท๊ณผ ์œ ์‚ฌํ•œ ๊ฐ€์ƒ์˜ ํ”Œ๋žซํผ "TalentHub"๋ฅผ ์˜ˆ๋กœ ๋“ค์–ด๋ณผ๊ฒŒ์š”. ์ด ํ”Œ๋žซํผ์—์„œ ๋ฐ์ดํ„ฐ๋กœ๋”๋ฅผ ๋„์ž…ํ•˜๊ธฐ ์ „๊ณผ ํ›„์˜ ์„ฑ๋Šฅ์„ ๋น„๊ตํ•ด๋ณผ ๊ฑฐ์˜ˆ์š”. ์ค€๋น„๋˜์…จ๋‚˜์š”? ๐Ÿš€

์‚ฌ๋ก€: TalentHub์˜ ์‚ฌ์šฉ์ž ํ”„๋กœํ•„ ํŽ˜์ด์ง€

์ƒํ™ฉ: TalentHub์˜ ์‚ฌ์šฉ์ž ํ”„๋กœํ•„ ํŽ˜์ด์ง€๋Š” ๋‹ค์Œ ์ •๋ณด๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค:

  • ์‚ฌ์šฉ์ž ๊ธฐ๋ณธ ์ •๋ณด
  • ์‚ฌ์šฉ์ž์˜ ์Šคํ‚ฌ ๋ชฉ๋ก (์ตœ๋Œ€ 10๊ฐœ)
  • ์‚ฌ์šฉ์ž์˜ ์ตœ๊ทผ ํ”„๋กœ์ ํŠธ (์ตœ๋Œ€ 5๊ฐœ)
  • ์‚ฌ์šฉ์ž์˜ ๋ฆฌ๋ทฐ (์ตœ๋Œ€ 3๊ฐœ)

๋ฐ์ดํ„ฐ๋กœ๋” ๋„์ž… ์ „:


const resolvers = {
  Query: {
    user: async (_, { id }) => {
      const user = await fetchUserFromDB(id);
      user.skills = await fetchUserSkillsFromDB(id);
      user.projects = await fetchUserProjectsFromDB(id);
      user.reviews = await fetchUserReviewsFromDB(id);
      return user;
    }
  }
};
  

์„ฑ๋Šฅ:

  • ํ‰๊ท  ์‘๋‹ต ์‹œ๊ฐ„: 500ms
  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ฟผ๋ฆฌ ์ˆ˜: 4 (์‚ฌ์šฉ์ž, ์Šคํ‚ฌ, ํ”„๋กœ์ ํŠธ, ๋ฆฌ๋ทฐ ๊ฐ๊ฐ)

๋ฐ์ดํ„ฐ๋กœ๋” ๋„์ž… ํ›„:


const createLoaders = () => ({
  userLoader: new DataLoader(ids => fetchUsersFromDB(ids)),
  skillLoader: new DataLoader(ids => fetchSkillsFromDB(ids)),
  projectLoader: new DataLoader(ids => fetchProjectsFromDB(ids)),
  reviewLoader: new DataLoader(ids => fetchReviewsFromDB(ids))
});

const resolvers = {
  Query: {
    user: (_, { id }, { loaders }) => loaders.userLoader.load(id)
  },
  User: {
    skills: (user, _, { loaders }) => loaders.skillLoader.loadMany(user.skillIds),
    projects: (user, _, { loaders }) => loaders.projectLoader.loadMany(user.projectIds),
    reviews: (user, _, { loaders }) => loaders.reviewLoader.loadMany(user.reviewIds)
  }
};
  

์„ฑ๋Šฅ:

  • ํ‰๊ท  ์‘๋‹ต ์‹œ๊ฐ„: 200ms (60% ๊ฐ์†Œ)
  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ฟผ๋ฆฌ ์ˆ˜: ์ตœ๋Œ€ 4, ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ 1-2 (์บ์‹ฑ ํšจ๊ณผ)

๐ŸŽ‰ ์ฃผ์š” ๊ฐœ์„  ์‚ฌํ•ญ:

  • ์‘๋‹ต ์‹œ๊ฐ„ 60% ๊ฐ์†Œ
  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ถ€ํ•˜ ๊ฐ์†Œ (ํŠนํžˆ ๋™์‹œ ์ ‘์†์ž๊ฐ€ ๋งŽ์„ ๋•Œ ํšจ๊ณผ์ )
  • ์ฝ”๋“œ์˜ ๋ชจ๋“ˆ์„ฑ๊ณผ ์žฌ์‚ฌ์šฉ์„ฑ ํ–ฅ์ƒ

์ถ”๊ฐ€ ์ตœ์ ํ™”:

๋ฐ์ดํ„ฐ๋กœ๋” ๋„์ž… ํ›„, TalentHub ํŒ€์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ถ”๊ฐ€ ์ตœ์ ํ™”๋ฅผ ์ˆ˜ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค:

  1. ์บ์‹œ ์ „๋žต ์ˆ˜๋ฆฝ: ์ž์ฃผ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๋Š” ๋ฐ์ดํ„ฐ(์˜ˆ: ์Šคํ‚ฌ)์— ๋Œ€ํ•ด longer-lived ์บ์‹œ ์ ์šฉ
  2. ๋ฐฐ์น˜ ์‚ฌ์ด์ฆˆ ์ตœ์ ํ™”: ํ‰๊ท ์ ์ธ ์š”์ฒญ ํŒจํ„ด์„ ๋ถ„์„ํ•˜์—ฌ ๊ฐ ๋กœ๋”์˜ ๋ฐฐ์น˜ ์‚ฌ์ด์ฆˆ ์กฐ์ •
  3. ํ”„๋ฆฌํŽ˜์นญ: ์ž์ฃผ ํ•จ๊ป˜ ์š”์ฒญ๋˜๋Š” ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•ด ํ”„๋ฆฌํŽ˜์นญ ๋กœ์ง ๊ตฌํ˜„

์ด๋Ÿฌํ•œ ์ตœ์ ํ™” ํ›„, ํ‰๊ท  ์‘๋‹ต ์‹œ๊ฐ„์€ 150ms๊นŒ์ง€ ๊ฐ์†Œํ–ˆ๊ณ , ์„œ๋ฒ„ ๋ฆฌ์†Œ์Šค ์‚ฌ์šฉ๋Ÿ‰๋„ ํฌ๊ฒŒ ์ค„์—ˆ์Šต๋‹ˆ๋‹ค. ๐Ÿ˜Š

TalentHub ์„ฑ๋Šฅ ๊ฐœ์„  ๊ทธ๋ž˜ํ”„ TalentHub ์„ฑ๋Šฅ ๊ฐœ์„  ๊ทธ๋ž˜ํ”„ ์ตœ์ ํ™” ๋‹จ๊ณ„ ์‘๋‹ต ์‹œ๊ฐ„ (ms) ๋„์ž… ์ „ ๋ฐ์ดํ„ฐ๋กœ๋” ๋„์ž… ์ถ”๊ฐ€ ์ตœ์ ํ™” 500ms 200ms 150ms

์ด ์‚ฌ๋ก€ ์—ฐ๊ตฌ๋ฅผ ํ†ตํ•ด ์šฐ๋ฆฌ๋Š” ๋ฐ์ดํ„ฐ๋กœ๋”๊ฐ€ ์‹ค์ œ๋กœ ์–ผ๋งˆ๋‚˜ ํฐ ์„ฑ๋Šฅ ํ–ฅ์ƒ์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋Š”์ง€ ํ™•์ธํ–ˆ์–ด์š”. ์žฌ๋Šฅ๋„ท๊ณผ ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ๋„ ์ด์™€ ์œ ์‚ฌํ•œ ์ ‘๊ทผ ๋ฐฉ์‹์„ ํ†ตํ•ด ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ํฌ๊ฒŒ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ์„ ๊ฑฐ์˜ˆ์š”. ๐ŸŒŸ

๐Ÿ’ก Pro Tip: ์„ฑ๋Šฅ ์ตœ์ ํ™”๋Š” ์ง€์†์ ์ธ ๊ณผ์ •์ด์—์š”. ๋ฐ์ดํ„ฐ๋กœ๋”๋ฅผ ๋„์ž…ํ•œ ํ›„์—๋„ ๊ณ„์†ํ•ด์„œ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜๊ณ , ์‚ฌ์šฉ ํŒจํ„ด์„ ๋ถ„์„ํ•˜๋ฉฐ, ํ•„์š”์— ๋”ฐ๋ผ ์กฐ์ •ํ•ด ๋‚˜๊ฐ€๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ, ์„ฑ๋Šฅ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ฝ”๋“œ์˜ ์œ ์ง€๋ณด์ˆ˜์„ฑ๊ณผ ํ™•์žฅ์„ฑ๋„ ํ•จ๊ป˜ ๊ณ ๋ คํ•ด์•ผ ํ•ด์š”.

์ž, ์—ฌ๊ธฐ๊นŒ์ง€ ๋ฐ์ดํ„ฐ๋กœ๋”๋ฅผ ์‚ฌ์šฉํ•œ ์‹ค์ œ ์‚ฌ๋ก€ ์—ฐ๊ตฌ๋ฅผ ์‚ดํŽด๋ดค์–ด์š”. ์ด๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋กœ๋”๊ฐ€ ์–ด๋–ป๊ฒŒ ์‹ค์ œ ํ”„๋กœ์ ํŠธ์˜ ์„ฑ๋Šฅ์„ ๊ฐœ์„ ํ•˜๋Š”์ง€ ์ง์ ‘ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ์ฃ ? ์ด์ œ ์—ฌ๋Ÿฌ๋ถ„๋„ ์ž์‹ ์˜ ํ”„๋กœ์ ํŠธ์— ๋ฐ์ดํ„ฐ๋กœ๋”๋ฅผ ์ ์šฉํ•ด๋ณผ ์ค€๋น„๊ฐ€ ๋˜์—ˆ์„ ๊ฑฐ์˜ˆ์š”! ๐Ÿš€

๋‹ค์Œ ์„น์…˜์—์„œ๋Š” ์ด ๋ชจ๋“  ๋‚ด์šฉ์„ ์ข…ํ•ฉํ•˜์—ฌ, ๊ทธ๋ž˜ํ”„ํ์—˜ ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋กœ๋”๋ฅผ ํ™œ์šฉํ•œ ์บ์‹ฑ ์ „๋žต์˜ ๋ฏธ๋ž˜์™€ ๋ฐœ์ „ ๋ฐฉํ–ฅ์— ๋Œ€ํ•ด ๋…ผ์˜ํ•ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๊ณ„์†ํ•ด์„œ ํ•จ๊ป˜ ํ•ด์ฃผ์„ธ์š”! ๐Ÿ˜ƒ p>

๐Ÿ”ฎ ๊ทธ๋ž˜ํ”„ํ์—˜ ์„œ๋ฒ„ ์บ์‹ฑ์˜ ๋ฏธ๋ž˜: ๋ฐ์ดํ„ฐ๋กœ๋”์™€ ๊ทธ ๋„ˆ๋จธ

์ž, ์ด์ œ ์šฐ๋ฆฌ๋Š” ๋ฐ์ดํ„ฐ๋กœ๋”์˜ ๊ธฐ๋ณธ ๊ฐœ๋…๋ถ€ํ„ฐ ์‹ค์ œ ์ ์šฉ ์‚ฌ๋ก€๊นŒ์ง€ ํญ๋„“๊ฒŒ ์‚ดํŽด๋ดค์–ด์š”. ๊ทธ๋ ‡๋‹ค๋ฉด ์ด์ œ ๋ฏธ๋ž˜๋ฅผ ํ–ฅํ•ด ํ•œ ๊ฑธ์Œ ๋” ๋‚˜์•„๊ฐ€๋ณผ๊นŒ์š”? ๊ทธ๋ž˜ํ”„ํ์—˜ ์„œ๋ฒ„ ์บ์‹ฑ์˜ ๋ฏธ๋ž˜, ํŠนํžˆ ์žฌ๋Šฅ๋„ท๊ณผ ๊ฐ™์€ ๋ณต์žกํ•œ ํ”Œ๋žซํผ์—์„œ์˜ ๋ฐœ์ „ ๋ฐฉํ–ฅ์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•ด๋ด์š”. ๐Ÿš€

1. ๋จธ์‹ ๋Ÿฌ๋‹์„ ํ™œ์šฉํ•œ ์Šค๋งˆํŠธ ์บ์‹ฑ

๋ฏธ๋ž˜์˜ ๋ฐ์ดํ„ฐ๋กœ๋”๋Š” ๋จธ์‹ ๋Ÿฌ๋‹ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ํ†ตํ•ด ์‚ฌ์šฉ ํŒจํ„ด์„ ํ•™์Šตํ•˜๊ณ , ์ž๋™์œผ๋กœ ์ตœ์ ์˜ ์บ์‹ฑ ์ „๋žต์„ ์ˆ˜๋ฆฝํ•  ์ˆ˜ ์žˆ์„ ๊ฑฐ์˜ˆ์š”.


const smartLoader = new SmartDataLoader(fetchFromDB, {
  mlModel: new CachePredictionModel(),
  adaptiveBatchSize: true
});
  

์ด๋Ÿฌํ•œ ์Šค๋งˆํŠธ ๋กœ๋”๋Š” ์‚ฌ์šฉ์ž์˜ ํ–‰๋™ ํŒจํ„ด์„ ๋ถ„์„ํ•˜์—ฌ ์–ด๋–ค ๋ฐ์ดํ„ฐ๋ฅผ ์–ผ๋งˆ๋‚˜ ์˜ค๋ž˜ ์บ์‹œํ• ์ง€, ์–ด๋–ค ๋ฐ์ดํ„ฐ๋ฅผ ํ”„๋ฆฌํŽ˜์น˜ํ• ์ง€ ๋“ฑ์„ ์ž๋™์œผ๋กœ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ์„ ๊ฑฐ์˜ˆ์š”.

2. ๋ถ„์‚ฐ ์บ์‹ฑ ์‹œ์Šคํ…œ๊ณผ์˜ ํ†ตํ•ฉ

๋Œ€๊ทœ๋ชจ ์‹œ์Šคํ…œ์—์„œ๋Š” ๋‹จ์ผ ์„œ๋ฒ„์˜ ๋ฉ”๋ชจ๋ฆฌ๋งŒ์œผ๋กœ๋Š” ์ถฉ๋ถ„ํ•œ ์บ์‹ฑ์ด ์–ด๋ ค์šธ ์ˆ˜ ์žˆ์–ด์š”. ๋ฏธ๋ž˜์˜ ๋ฐ์ดํ„ฐ๋กœ๋”๋Š” Redis๋‚˜ Memcached ๊ฐ™์€ ๋ถ„์‚ฐ ์บ์‹ฑ ์‹œ์Šคํ…œ๊ณผ ๋” ๊ธด๋ฐ€ํ•˜๊ฒŒ ํ†ตํ•ฉ๋  ์ˆ˜ ์žˆ์„ ๊ฑฐ์˜ˆ์š”.


const distributedLoader = new DataLoader(fetchFromDB, {
  cacheMap: new RedisLRUCache({
    redis: redisClient,
    maxAge: 1000 * 60 * 5 // 5๋ถ„
  })
});
  

3. ์‹ค์‹œ๊ฐ„ ๋ฐ์ดํ„ฐ ๋™๊ธฐํ™”

WebSocket์ด๋‚˜ GraphQL Subscriptions๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์บ์‹œ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜์ด ๋”์šฑ ๋ฐœ์ „ํ•  ๊ฑฐ์˜ˆ์š”.


const realtimeLoader = new RealtimeDataLoader(fetchFromDB, {
  subscriptionClient: graphqlSubscriptionClient,
  onUpdate: (updatedData) => {
    realtimeLoader.clear(updatedData.id);
    realtimeLoader.prime(updatedData.id, updatedData);
  }
});
  

4. ์ปจํ…์ŠคํŠธ ์ธ์‹ ์บ์‹ฑ

์‚ฌ์šฉ์ž์˜ ์—ญํ• , ์œ„์น˜, ๋””๋ฐ”์ด์Šค ์ข…๋ฅ˜ ๋“ฑ ๋‹ค์–‘ํ•œ ์ปจํ…์ŠคํŠธ๋ฅผ ๊ณ ๋ คํ•œ ๋” ์Šค๋งˆํŠธํ•œ ์บ์‹ฑ ์ „๋žต์ด ๋“ฑ์žฅํ•  ์ˆ˜ ์žˆ์–ด์š”.


const contextAwareLoader = new ContextAwareDataLoader(fetchFromDB, {
  getCacheKey: (id, context) => `${id}:${context.userRole}:${context.deviceType}`
});
  

5. ํผ์‹œ์Šคํ„ดํŠธ ์บ์‹ฑ๊ณผ ์˜คํ”„๋ผ์ธ ์ง€์›

PWA(Progressive Web App)์˜ ๋ฐœ์ „๊ณผ ํ•จ๊ป˜, ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด๋“œ์—์„œ์˜ ํผ์‹œ์Šคํ„ดํŠธ ์บ์‹ฑ๊ณผ ์˜คํ”„๋ผ์ธ ์ง€์›์ด ๋”์šฑ ์ค‘์š”ํ•ด์งˆ ๊ฑฐ์˜ˆ์š”.


const offlineSupport = {
  cacheStorage: new CacheStorage(),
  sync: async (loader) => {
    const offlineChanges = await offlineSupport.cacheStorage.getChanges();
    await Promise.all(offlineChanges.map(change => loader.clear(change.id)));
    await syncWithServer(offlineChanges);
  }
};
  

๐Ÿ’ก ๋ฏธ๋ž˜๋ฅผ ์œ„ํ•œ Tip: ๊ธฐ์ˆ ์€ ๋น ๋ฅด๊ฒŒ ๋ณ€ํ™”ํ•˜์ง€๋งŒ, ๊ธฐ๋ณธ ์›์น™์€ ๋ณ€ํ•˜์ง€ ์•Š์•„์š”. ๋ฐ์ดํ„ฐ์˜ ํŠน์„ฑ, ์‚ฌ์šฉ์ž์˜ ์š”๊ตฌ, ์‹œ์Šคํ…œ์˜ ์ œ์•ฝ์„ ํ•ญ์ƒ ๊ณ ๋ คํ•˜์„ธ์š”. ์ƒˆ๋กœ์šด ๊ธฐ์ˆ ์„ ๋„์ž…ํ•  ๋•Œ๋Š” ํ•ญ์ƒ ์‹ค์ œ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š”์ง€, ๊ทธ๋ฆฌ๊ณ  ๊ทธ ๋ณต์žก์„ฑ์ด ์ •๋‹นํ™”๋˜๋Š”์ง€ ์‹ ์ค‘ํžˆ ํ‰๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ๋ฏธ๋ž˜์˜ ๋ฐœ์ „ ๋ฐฉํ–ฅ์€ ์žฌ๋Šฅ๋„ท๊ณผ ๊ฐ™์€ ํ”Œ๋žซํผ์— ์–ด๋–ค ์˜ํ–ฅ์„ ๋ฏธ์น ๊นŒ์š”? ๐Ÿค”

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

๋ฌผ๋ก , ์ด๋Ÿฌํ•œ ๋ฐœ์ „์€ ์ƒˆ๋กœ์šด ๋„์ „๋„ ๊ฐ€์ ธ์˜ฌ ๊ฑฐ์˜ˆ์š”. ๋ฐ์ดํ„ฐ ํ”„๋ผ์ด๋ฒ„์‹œ, ์บ์‹œ ์ผ๊ด€์„ฑ ์œ ์ง€, ์‹œ์Šคํ…œ ๋ณต์žก๋„ ๊ด€๋ฆฌ ๋“ฑ์˜ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด์•ผ ํ•  ํ…Œ๋‹ˆ๊นŒ์š”. ํ•˜์ง€๋งŒ ์ด๋Ÿฌํ•œ ๋„์ „์„ ๊ทน๋ณตํ•˜๋ฉด์„œ ์šฐ๋ฆฌ๋Š” ๋” ๋‚˜์€ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์„ ๊ฑฐ์˜ˆ์š”. ๐Ÿ˜Š

์ž, ์—ฌ๊ธฐ๊นŒ์ง€ ๊ทธ๋ž˜ํ”„ํ์—˜ ์„œ๋ฒ„ ์บ์‹ฑ์˜ ๋ฏธ๋ž˜์— ๋Œ€ํ•ด ์‚ดํŽด๋ดค์–ด์š”. ์ด๋Ÿฌํ•œ ๋ฐœ์ „ ๋ฐฉํ–ฅ์„ ์—ผ๋‘์— ๋‘๊ณ  ํ˜„์žฌ์˜ ์‹œ์Šคํ…œ์„ ์„ค๊ณ„ํ•˜๊ณ  ๊ฐœ์„ ํ•ด ๋‚˜๊ฐ„๋‹ค๋ฉด, ๋ฏธ๋ž˜์˜ ๋ณ€ํ™”์—๋„ ์œ ์—ฐํ•˜๊ฒŒ ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ์„ ๊ฑฐ์˜ˆ์š”. ๊ธฐ์ˆ ์˜ ๋ฐœ์ „ ์†๋„๊ฐ€ ๋น ๋ฅธ ๋งŒํผ, ์šฐ๋ฆฌ๋„ ๊ณ„์†ํ•ด์„œ ํ•™์Šตํ•˜๊ณ  ์ ์‘ํ•ด ๋‚˜๊ฐ€์•ผ ํ•ด์š”. ํ•จ๊ป˜ ์„ฑ์žฅํ•ด ๋‚˜๊ฐ€์š”! ๐ŸŒฑ

๐ŸŽ“ ๊ฒฐ๋ก : ๋ฐ์ดํ„ฐ๋กœ๋”๋กœ ๋” ๋‚˜์€ ๊ทธ๋ž˜ํ”„ํ์—˜ ์„ธ์ƒ ๋งŒ๋“ค๊ธฐ

์ž, ์—ฌ๋Ÿฌ๋ถ„! ๊ธด ์—ฌ์ •์ด์—ˆ์ง€๋งŒ ๋“œ๋””์–ด ์šฐ๋ฆฌ์˜ ๋ฐ์ดํ„ฐ๋กœ๋” ํƒํ—˜์ด ๋งˆ๋ฌด๋ฆฌ๋˜์–ด ๊ฐ€๊ณ  ์žˆ์–ด์š”. ์ด ์—ฌ์ •์„ ํ†ตํ•ด ์šฐ๋ฆฌ๋Š” ๊ทธ๋ž˜ํ”„ํ์—˜ ์„œ๋ฒ„์˜ ์„ฑ๋Šฅ์„ ํš๊ธฐ์ ์œผ๋กœ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ์ธ ๋ฐ์ดํ„ฐ๋กœ๋”์— ๋Œ€ํ•ด ๊นŠ์ด ์žˆ๊ฒŒ ์•Œ์•„๋ดค์ฃ . ๐Ÿ˜Š

์šฐ๋ฆฌ๊ฐ€ ํ•จ๊ป˜ ๋ฐฐ์šด ๋‚ด์šฉ์„ ๊ฐ„๋‹จํžˆ ์ •๋ฆฌํ•ด๋ณผ๊นŒ์š”?

  1. ๋ฐ์ดํ„ฐ๋กœ๋”์˜ ๊ธฐ๋ณธ ๊ฐœ๋…๊ณผ ์ž‘๋™ ์›๋ฆฌ
  2. ๊ทธ๋ž˜ํ”„ํ์—˜ ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋กœ๋”๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•
  3. ๋ฐ์ดํ„ฐ๋กœ๋” ์‚ฌ์šฉ ์‹œ ์ฃผ์˜ํ•ด์•ผ ํ•  ์ ๋“ค๊ณผ best practices
  4. ์‹ค์ œ ์‚ฌ๋ก€๋ฅผ ํ†ตํ•œ ๋ฐ์ดํ„ฐ๋กœ๋”์˜ ์„ฑ๋Šฅ ๊ฐœ์„  ํšจ๊ณผ
  5. ๊ทธ๋ž˜ํ”„ํ์—˜ ์„œ๋ฒ„ ์บ์‹ฑ์˜ ๋ฏธ๋ž˜ ๋ฐœ์ „ ๋ฐฉํ–ฅ

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

๐ŸŒŸ ํ•ต์‹ฌ takeaway:

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

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

์ค‘์š”ํ•œ ๊ฒƒ์€ ์—ฌ๋Ÿฌ๋ถ„์ด ์ด์ œ ๋ฐ์ดํ„ฐ๋กœ๋”๋ผ๋Š” ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ๋ฅผ ์—ฌ๋Ÿฌ๋ถ„์˜ ๊ฐœ๋ฐœ์ž ๋„๊ตฌ ์ƒ์ž์— ์ถ”๊ฐ€ํ–ˆ๋‹ค๋Š” ๊ฑฐ์˜ˆ์š”. ์ด๋ฅผ ํ†ตํ•ด ์—ฌ๋Ÿฌ๋ถ„์€ ๋” ํšจ์œจ์ ์ด๊ณ  ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ๊ทธ๋ž˜ํ”„ํ์—˜ ์„œ๋ฒ„๋ฅผ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ์ฃ . ๐Ÿ‘จโ€๐Ÿ’ป๐Ÿ‘ฉโ€๐Ÿ’ป

์•ž์œผ๋กœ๋„ ๊ณ„์†ํ•ด์„œ ํ•™์Šตํ•˜๊ณ  ๊ฒฝํ—˜์„ ์Œ“์•„๊ฐ€์„ธ์š”. ๊ทธ๋ž˜ํ”„ํ์—˜๊ณผ ๋ฐ์ดํ„ฐ๋กœ๋” ๊ธฐ์ˆ ์€ ๊ณ„์† ๋ฐœ์ „ํ•˜๊ณ  ์žˆ์œผ๋‹ˆ๊นŒ์š”. ์—ฌ๋Ÿฌ๋ถ„์˜ ์ง€์‹๊ณผ ๊ฒฝํ—˜์ด ์Œ“์ผ์ˆ˜๋ก, ์—ฌ๋Ÿฌ๋ถ„์ด ๋งŒ๋“œ๋Š” ์„œ๋น„์Šค๋Š” ๋”์šฑ ๊ฐ•๋ ฅํ•ด์งˆ ๊ฑฐ์˜ˆ์š”. ๐ŸŒฑ

๋งˆ์ง€๋ง‰์œผ๋กœ, ์ด ๊ธ€์ด ์—ฌ๋Ÿฌ๋ถ„์˜ ๊ทธ๋ž˜ํ”„ํ์—˜ ์—ฌ์ •์— ๋„์›€์ด ๋˜์—ˆ๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ๋ถ„ ๋ชจ๋‘๊ฐ€ ๋ฐ์ดํ„ฐ๋กœ๋”๋ฅผ ํ™œ์šฉํ•ด ๋†€๋ผ์šด ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค์–ด๋‚ด๊ธธ ๊ธฐ๋Œ€ํ• ๊ฒŒ์š”. ํ•จ๊ป˜ ๋” ๋‚˜์€ ๊ทธ๋ž˜ํ”„ํ์—˜ ์„ธ์ƒ์„ ๋งŒ๋“ค์–ด ๋‚˜๊ฐ€์š”! ๐ŸŒ

ํ–‰์šด์„ ๋น•๋‹ˆ๋‹ค, ๊ทธ๋ฆฌ๊ณ  ์ฆ๊ฑฐ์šด ์ฝ”๋”ฉํ•˜์„ธ์š”! ๐Ÿ˜Š๐Ÿ‘