๐Ÿ”ฅ ์‚ฌ๋‚ด ์Šคํ„ฐ๋”” ํ”„๋กœ์ ํŠธ ๊ธฐ๋ก : ๊ตญ๋‚ด ๋ฐ ํ•ด์™ธ ๊ฐ€์ƒ์ž์‚ฐ ์ฝ”์ธ ๊ฑฐ๋ž˜์†Œ๋“ค์˜ ๊ฑฐ๋ž˜๋Ÿ‰์„ ํ•œ ๋ˆˆ์— ? ๐Ÿ‘€

leehyunjuยท2023๋…„ 1์›” 30์ผ
11
post-thumbnail
post-custom-banner

์—…๋กœ๋“œ์ค‘..

์ทจ์—… ์ดํ›„๋กœ๋Š” ์ฒ˜์Œ ์ž‘์„ฑํ•ด๋ณด๋Š” ์‚ฌ์ด๋“œ ํ”„๋กœ์ ํŠธ ํšŒ๊ณ ๋ก ์ž…๋‹ˆ๋‹ค.
์‚ฌ๋‚ด์—์„œ ๋˜์ ธ์ค€ ์ฃผ์ œ๋กœ ์ž‘์—…ํ•œ ์Šคํ„ฐ๋””์šฉ ํ”„๋กœ์ ํŠธ์ด๋ฉฐ ์ œ๊ฐ€ ์ž‘์—…ํ•œ ์ฝ”๋“œ์˜ ์ผ๋ถ€๋งŒ ๊ณต๊ฐœํ•˜์—ฌ ํŠธ๋ ˆ์ด๋“œ ์˜คํ”„(Trade-Off)์™€ ๊ธฐ์ˆ  ๊ณต์œ ์˜ ๋ชฉ์ ์œผ๋กœ ํฌ์ŠคํŒ… ํ•ด๋ณผ๊นŒ ํ•ฉ๋‹ˆ๋‹ค.


๐Ÿ”ฅ ํ”„๋กœ์ ํŠธ ๊ฐœ์š”

  • ๊ตญ๋‚ด ๋ฐ ํ•ด์™ธ ๊ฐ€์ƒ์ž์‚ฐ ์ฝ”์ธ ๊ฑฐ๋ž˜์†Œ๋“ค์˜ ๊ฑฐ๋ž˜๋Ÿ‰ ์ˆœ์œ„๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ œ๊ณตํ•ด์ฃผ๋Š” ํŽ˜์ด์ง€๋ฅผ ํด๋ก  ์ฝ”๋”ฉํ•˜๋Š” ์‚ฌ๋‚ด ์Šคํ„ฐ๋””์šฉ ํ”„๋กœ์ ํŠธ ์ž…๋‹ˆ๋‹ค.

๐Ÿ‘ฅ ์ฐธ์—ฌ ๋ฉค๋ฒ„ (์ด 3๋ช…)

  • ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž : 1๋ช… ๋‚˜ โœ‹
  • ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž : 2๋ช… (Batch ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘ / API ๊ฐœ๋ฐœ)

โฐ ๊ฐœ๋ฐœ ๊ธฐ๊ฐ„ (6์ฃผ)

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

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

๊ฐœ๋ฐœ์ด ๋๋‚œ ๋’ค, ์„ธ์…˜ ๋ฐœํ‘œ๋ฅผ ํ†ตํ•ด ๊ฐœ๋ฐœ๊ทธ๋ฃน๊ณผ ๊ธฐํš๊ทธ๋ฃน ์ผ์›๋“ค์—๊ฒŒ UX ์ ์œผ๋กœ ๊ฐœ์„ ํ•œ ์ ์„ ์–ดํ•„ํ•˜๊ณ  ์ฝ”๋“œ๋Š” ์™œ ์ด๋ ‡๊ฒŒ ์งฐ๋Š”์ง€ ๋ถ€๊ฐ€ ์„ค๋ช…๊ณผ Recoil์ด๋ผ๋Š” ๊ธฐ์ˆ  ์Šคํƒ์— ๋Œ€ํ•ด ๊ธฐ์ˆ  ์ „ํŒŒ(?)ํ•˜๋Š” ์‹œ๊ฐ„์„ ๊ฐ€์กŒ๋‹ค.


๐Ÿ‘ ๋ชฉํ‘œ

  • ๊ธฐ์ˆ  ์Šคํƒ ์ตœ์‹ ํ™” : ๊ธฐ์กด์—๋Š” PHP ํ™˜๊ฒฝ์œผ๋กœ ๊ฐœ๋ฐœ ๋๋˜ ํŽ˜์ด์ง€๋ฅผ ํด๋ก ์ฝ”๋”ฉ ํ•˜๋ฉด์„œ ๋ฐฑ์—”๋“œ๋Š” Java, ํ”„๋ก ํŠธ๋Š” React๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค.
  • BE : ๋ฐฑ์—”๋“œ์— ๋Œ€ํ•œ ์šฉ์–ด์˜ ๊ฐœ๋…๋“ค์„ ์–ด๊นจ๋„ˆ๋จธ ์•Œ๊ณ ์‹ถ์—ˆ์Œ
  • ์ƒˆ๋กœ์šด ๊ธฐ์ˆ  ์Šต๋“ : ์ƒํƒœ๊ด€๋ฆฌ ๊ธฐ๋Šฅ์˜ ๊ธฐ์ˆ  ์Šคํƒ์ธ Recoil์„ ๋„์ž…ํ•˜์—ฌ ์ด ๊ธฐํšŒ์— ๋‚ด๊ป„๋กœ ๋งŒ๋“ค๊ณ ์ž ํ–ˆ๋‹ค.
  • โœจ Clean Code โœจ : ์–ด๋–ป๊ฒŒ ํ•˜๋ฉด ๊น”๋”ํ•˜๊ณ  ๋ณด๊ธฐ ์ข‹์€ ์ฝ”๋“œ๋ฅผ ์งค ์ˆ˜ ์žˆ์„์ง€์— ๋Œ€ํ•ด ๊ณ ๋ฏผํ•˜๋ฉด์„œ ๋…ธ๋ ฅํ–ˆ๋‹ค. ํŠนํžˆ, ์ƒ์šฉํ™”ํ•˜๊ธฐ ์ข‹์€ ์ฝ”๋“œ๋ฅผ ๋ชจ๋“ˆํ™” ํ•˜์—ฌ ์ฝ”๋“œ๋ฅผ ์‹ฌํ”Œํ•˜๊ฒŒ ํ•˜๊ณ ์ž ํ–ˆ๋‹ค.
  • ํ™œ์šฉ๋„ ์ข‹์€ ์ปดํฌ๋„ŒํŠธ : FE ๊ฐœ๋ฐœ์ž๋ผ๋ฉด ๋‹น์—ฐํ•œ ์žฌํ™œ์šฉ ํ•˜๊ธฐ ์ข‹์€ ์ปดํฌ๋„ŒํŠธ ๋งŒ๋“ค๊ธฐ..๐Ÿงฉ
  • UI/UX ๊ฐœ์„  : ๋‚ด๊ฐ€ ํด๋ผ์ด์–ธํŠธ์˜ ์ž…์žฅ์ด ๋˜์–ด ๋ถˆํŽธํ•œ ๋ถ€๋ถ„๋“ค์„ ์ฐพ์•„ ์ „๋ถ€ ๊ฐœ์„  ํ•˜์ž
  • ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ณ€๋™๋˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์–ด๋–ป๊ฒŒ ๋ณด์—ฌ์ค„ ๊ฒƒ์ธ์ง€ ๊ณ ๋ฏผ

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

(์‚ฌ์‹ค ๊ทธ๋ฃน์žฅ๋‹˜๊ป˜์„œ ํ”„๋ก ํŠธ์— ๋ง‰ ๊ด€์‹ฌ ๊ฐ–๋˜ ์‹œ๊ธฐ์…จ์Œ...! ์€ ๋น„๋ฐ€ ๐Ÿคซ)

๐Ÿ˜ ๊ทธ ๋‹น์‹œ์— ์ค€๋น„ํ–ˆ๋˜ ์ž๋ฃŒ๋“ค, React ์ดˆ๊ธฐ, ํ”„๋กœ์ ํŠธ ํ™˜๊ฒฝ Setting
https://velog.io/@leemember/FE-React-ํ”„๋กœ์ ํŠธ-์ดˆ๊ธฐ-Setting


๐ŸŽณ ๊ธฐ์ˆ ์Šคํƒ

๋‚ด๊ฐ€ ์ •ํ•œ ๊ธฐ์ˆ ์Šคํƒ์€ ์ด๋Ÿฌํ•˜๋‹ค.
๊ทธ๋™์•ˆ ์ƒํƒœ๊ด€๋ฆฌ๋Š” Redux, Mobx ๋ฐ–์— ์“ธ ์ค„ ๋ชฐ๋ž์ง€๋งŒ ์ด ๊ธฐํšŒ์— Recoil์„ ์จ๋ณด๋ฉด์„œ ์•ž์œผ๋กœ Recoil๋งŒ ์“ฐ๊ณ ์‹ถ์„ ์ •๋„๋กœ ํŽธํ–ˆ๋‹ค ๐Ÿ‘๐Ÿ˜ฎ

  • React (๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ) ^18.2.0
  • TypeScript (์–ธ์–ด) ^4.7.4
  • Recoil (์ƒํƒœ๊ด€๋ฆฌ) ^0.7.4
  • yarn 1.22.19

Redux vs Mobx vs Recoil ๋น„๊ต๊ธ€ + Recoil ์‚ฌ์šฉ๋ฒ•์„ ์ •๋ฆฌํ•ด๋ณธ ๊ธ€์ž…๋‹ˆ๋‹ค ! ๐Ÿ“
https://velog.io/@leemember/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%83%81%ED%83%9C%EA%B4%80%EB%A6%AC-Recoil%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC


๐ŸŒˆ ๊ธฐ๋Šฅ ๋ถ„์„

๐Ÿ‘‡ ์ด ํŽ˜์ด์ง€๋ฅผ ํด๋ก ์ฝ”๋”ฉ ํ• ๊ฑฐ๋‹ค. ์–ด๋–ป๊ฒŒ?

๋จผ์ €, ๋ฆฌ์•กํŠธ ์Šค๋Ÿฝ๊ฒŒ ์ปดํฌ๋„ŒํŠธํ™” ์‹œํ‚ค๊ธฐ ์œ„ํ•ด ํ™”๋ฉด์„ ๋ณด๊ณ  ๋ถ„์„ํ•ด์„œ ์ชผ๊ฐœ๋ดค๋‹ค ๐Ÿง ๋ฐ‘์— ํ‘œ๋Š” ๊ธฐ์กด์—๋Š” <table> ํƒœ๊ทธ๋กœ ์ž‘์—… ๋ผ์žˆ๋˜ ๊ฒƒ์„ ๋ฐ˜์‘ํ˜•์œผ๋กœ๋„ ๊ตฌํ˜„ํ•ด๋ณด๊ณ  ์‹ถ์–ด์„œ <div> ํƒœ๊ทธ๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ ์ž‘์—…ํ–ˆ๋‹ค.


1. ์ปดํฌ๋„ŒํŠธํ™”

ํ™”๋ฉด์„ ๋ณด๊ณ  ์–ด๋–ป๊ฒŒ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋‚˜๋ˆŒ์ง€ ๊ณ ๋ฏผํ•œ ๋’ค, ์œ„ ํ™”๋ฉด์ฒ˜๋Ÿผ ๋ฐ•์Šค ํ‘œ์‹œ ํ•ด๋‘” ๊ฒƒ์„ ๊ธฐ์ค€์œผ๋กœ ์•„๋ž˜์™€ ๊ฐ™์€ ํŒŒ์ผ ๊ฒฝ๋กœ๋กœ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ชผ๊ฐฐ๋‹ค.

๐Ÿ“ฆ src
 โ”ฃ ๐Ÿ“‚ pages
 โ”ƒ โ”ฃ ๐Ÿ“œ domestic.tsx ๐Ÿ“(๊ตญ๋‚ด ๊ฐ€์ƒ์ž์‚ฐ ์ฝ”์ธ๊ฑฐ๋ž˜์†Œ)
 โ”ƒ โ”ฃ ๐Ÿ“œ global.tsx ๐Ÿ“(๊ตญ๋‚ด ๋ฐ ๊ตญ์ œ ๊ฐ€์ƒ์ž์‚ฐ ์ฝ”์ธ๊ฑฐ๋ž˜์†Œ)
 โ”ฃ ๐Ÿ“‚ lib
 โ”ƒ โ”ฃ ๐Ÿ“œ type.ts ๐Ÿ“ (๋ฐ์ดํ„ฐ ํƒ€์ž…)
 โ”ƒ โ”ฃ ๐Ÿ“œ api.ts ๐Ÿ“ (api ์š”์ฒญ ๋ชจ๋“ˆ)
 โ”ƒ โ”ฃ ๐Ÿ“œ day.ts ๐Ÿ“ (์˜ค๋Š˜ ๋‚ ์งœ)
 โ”ฃ ๐Ÿ“‚ component
 โ”ƒ โ”ฃ ๐Ÿ“‚ Common
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œ Search.tsx ๐Ÿ“(๋‚ ์งœ ์ž…๋ ฅ DatePicker)
 โ”ƒ โ”ฃ ๐Ÿ“‚ Global ๐Ÿ“(๊ตญ๋‚ด ๋ฐ ๊ตญ์ œ ๊ฐ€์ƒ์ž์‚ฐ ์ฝ”์ธ๊ฑฐ๋ž˜์†Œ ํŽ˜์ด์ง€์—๋งŒ ์“ฐ์ผ ์ปดํฌ๋„ŒํŠธ๋“ค)
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œ DomesticSummary.tsx ๐Ÿ“(๊ตญ๋‚ด ์ˆœ์œ„)
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œ GlobalSummary.tsx ๐Ÿ“(๊ธ€๋กœ๋ฒŒ ์ˆœ์œ„)
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œ Header.tsx ๐Ÿ“(ํ—ค๋”์— ์žˆ๋Š” ๋‚ด์šฉ๋“ค)
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œ List.tsx ๐Ÿ“(๊ฐ ๊ฑฐ๋ž˜์†Œ๋“ค์˜ ๊ฑฐ๋ž˜์•ก๊ณผ ๊ธ€๋กœ๋ฒŒ ์ ์œ ์œจ)
 โ”ƒ โ”ฃ ๐Ÿ“‚ Domestic 
 โ”ƒ โ”ƒ โ”ฃ ๐Ÿ“œ *.tsx (๊ธ€๋กœ๋ฒŒ๊ณผ ๋™์ผ)

2. ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ณ€๋™๋˜๋Š” ๋ฐ์ดํ„ฐ

์‹ค์‹œ๊ฐ„์œผ๋กœ ์šด์˜๋˜๋Š” ๊ฑฐ๋ž˜์†Œ ์žฅ์„ 1๋ถ„ ๊ฐ„๊ฒฉ์œผ๋กœ HTTP ์š”์ฒญ์„ ๋‚ ๋ ค ์ฃผ๊ธฐ์ ์œผ๋กœ ์—…๋ฐ์ดํŠธ ์‹œ์ผฐ๋‹ค. ์ด๊ฒƒ์„ Polling(ํด๋ง)์ด๋ผ๊ณ  ํ•œ๋‹ค.

// api ์ƒํƒœ๊ด€๋ฆฌ
/* โญ๏ธ useRecoilValue๋Š” Recoil์—์„œ ์ œ๊ณต๋˜๋Š” ๋ฉ”์„œ๋“œ๋กœ ์ฝ๊ธฐ ์ „์šฉ์˜ ๊ธฐ๋Šฅ์„ ํ•ฉ๋‹ˆ๋‹ค. 
๊ทธ๋ž˜์„œ useRecoilValue๋ฅผ ํ†ตํ•ด ์ดˆ๊ธฐ api ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š”๋ฐ, 
์—ฌ๊ธฐ์„œ parameter ๊ฐ’์— todayCall์€ ์˜ค๋Š˜์˜ ๋‚ ์งœ๋ฅผ ๊ฐ€๊ณต์‹œํ‚จ ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค.
*/
    const domesticRankings: Rank[] = useRecoilValue(getGlobalDomeRankListApi(todayCall));
    const globalRankingList: Rank[] = useRecoilValue(getGlobalRankListApi(todayCall));
    const globalTotal: TotalTypes = useRecoilValue(getGlobalTotalApi(todayCall));
    const globalCoinList: [CoinList][] = useRecoilValue(getGlobalCoinListApi(todayCall));
    // โญ๏ธ ๋ฐ์ดํ„ฐ ํ”ผ์ปค์— ๋‚ ์งœ ์„ ํƒ ์‹œ, ๊ทธ ๋‚ ์งœ์˜ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋„๋ก ์ƒํƒœ๋ณ€๊ฒฝ
    const clickDay = useRecoilValue(daySearch);
    
    // โญ๏ธ ๋žญํ‚น ๋ฆฌ์ŠคํŠธ
    // โญ๏ธ ํŽ˜์ด์ง€ ์ง„์ž…ํ•˜๊ณ  ๋ Œ๋”๋ง ํ•˜์ž๋งˆ์ž ๋ฐ์ดํ„ฐ๋“ค ๋ณด์—ฌ์ง€๋„๋ก
    const [domeRanking, setDomeRanking] = useState<Rank[]>(domesticRankings);
    const [globalRanking, setGlobalRanking] = useState<Rank[]>(globalRankingList);
    const [total, setTotal] = useState<TotalTypes>(globalTotal);
    const [coinList, setCoinList] = useState<[CoinList][]>(globalCoinList);
    // โญ๏ธ ๋ฐ์ดํ„ฐํ”ผ์ปค์— ์žˆ๋Š” ๋‚ ์งœ๋ฅผ ํด๋ฆญํ•  ๋•Œ
    const changeDay = clickDay.replaceAll('-', '');
    
// ๐Ÿ“api๋ฅผ ํ˜ธ์ถœํ•ด์ฃผ๋Š” ํ•จ์ˆ˜๋‹ค. dayParams์—๋Š” ๋‚ ์งœ๋ฅผ ๋ฐ›์•„์˜ค๋„๋ก ํ–ˆ๋‹ค.
const globalState = async (dayParams: {}) => {
        const domesticRank = await fetcher(METHOD.GET, `/global/domesticRank?toDate=${dayParams}`);
        const globalRank = await fetcher(METHOD.GET, `/global/globalRank?toDate=${dayParams}`);
        const globalTotal = await fetcher(METHOD.GET, `/global/total?toDate=${dayParams}`);
        const coinList = await fetcher(METHOD.GET, `/global/coinList?toDate=${dayParams}`);

        setDomeRanking(domesticRank.data);
        setGlobalRanking(globalRank.data);
        setTotal(globalTotal.data);
        setCoinList(coinList.data);
    };
    
 useEffect(() => {
 		// ๐Ÿ“setInterval์„ ํ†ตํ•ด 1๋ถ„ ๊ฐ„๊ฒฉ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์—…๋ฐ์ดํŠธ (=Polling)
        let polling = setInterval(() => {
            globalState(todayCall);
        }, 60000);

		// ๐Ÿ“ ๋งŒ์ผ์— ๋ฐ์ดํ„ฐํ”ผ์ปค์— ์„ ํƒํ•œ ๋‚ ์งœ๊ฐ€ ์˜ค๋Š˜ ๋‚ ์งœ๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด polling์ด ๋Š๊ธฐ๋„๋กํ•˜๊ณ , ์„ ํƒํ•œ ๋‚ ์งœ์˜ ๋ฐ์ดํ„ฐ๋“ค์ด ๋ณด์—ฌ์ง€๋„๋ก ์กฐ๊ฑด๋ฌธ์„ ๊ฑธ์—ˆ๋‹ค.
        if (clickDay.replaceAll('-', '') !== todayCall) {
            clearInterval(polling);
            globalState(changeDay);
        } else {
            globalState(changeDay);
        }

        // ๐Ÿ“ ํŽ˜์ด์ง€์— ๋ฒ—์–ด๋‚  ๊ฒฝ์šฐ์—๋„ polling X
        return () => {
            clearInterval(polling);
        };
    }, [clickDay]);

์ด์ฒ˜๋Ÿผ setInterval์„ ์‚ฌ์šฉํ•˜์—ฌ 1๋ถ„(=60000) ๊ฐ„๊ฒฉ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ƒˆ๋กญ๊ฒŒ ๋ถˆ๋Ÿฌ์™”๋‹ค. ๋ฌธ์ œ๋Š” setInterval๋งŒ์„ ์‚ฌ์šฉํ•ด์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๊ฒŒ ๋œ๋‹ค๋ฉด ์ฒ˜์Œ ํŽ˜์ด์ง€์— ์ดˆ๊ธฐ ์ง„์ž… ํ–ˆ์„ ๋•Œ ์•„๋ฌด๋Ÿฐ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†๋Š” ์ƒํƒœ์—์„œ 1๋ถ„ ๋’ค์— ๋ฐ์ดํ„ฐ๊ฐ€ ๋‚˜์˜ค๊ฒŒ ๋  ๊ฒƒ์ด๋‹ค. ๊ทธ๋ž˜์„œ ํ•ด๊ฒฐ์ฑ…์œผ๋กœ ์ฒ˜์Œ ํŽ˜์ด์ง€ ์ง„์ž… ํ–ˆ์„ ๋•Œ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ๋“ค์ด ๋ Œ๋”๋ง ๋˜๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด Recoil๋กœ ์ดˆ๊ธฐ๊ฐ’์„ ๋ฐ์ดํ„ฐ๋ฅผ ๋„ฃ์–ด์คฌ๋‹ค.

์ด๋ ‡๊ฒŒ ํ•จ์œผ๋กœ์จ ํŽ˜์ด์ง€ ์ดˆ๊ธฐ ์ง„์ž…์‹œ ๋ Œ๋”๋ง ๋  ๋•Œ ๋ฐ”๋กœ ๋ฐ์ดํ„ฐ๋“ค์ด ๋ณด์—ฌ์ง€๋Š” ์ƒํƒœ์—์„œ, setInterval๋กœ ํ•˜์—ฌ๊ธˆ 1๋ถ„ ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๋“ค์ด ๋ณด์—ฌ์งˆ ์ˆ˜ ์žˆ๊ฒŒ ๊ตฌํ˜„ํ–ˆ๋‹ค.

ํ•˜์ง€๋งŒ, ๋ฐ์ดํ„ฐํ”ผ์ปค๋กœ ๋‹ค๋ฅธ ๋‚ ์งœ๋ฅผ ์„ ํƒํ–ˆ์„ ๋•Œ๋Š” polling ๋Š์–ด์ง€๋„๋ก ์กฐ๊ฑด๋ฌธ์„ ๋‹ฌ์•˜์Œ. (์™œ๋ƒํ•˜๋ฉด ๊ณผ๊ฑฐ ๋‚ ์งœ๋Š” ์ด๋ฏธ ์žฅ์ด ๋๋‚ฌ๊ธฐ ๋•Œ๋ฌธ)


3. ๋ชจ๋“ˆํ™” (์ฝ”๋“œ ๋ฆฌํŒฉํ† ๋ง)

์ƒ์šฉํ™” ํ•  ์ˆ˜ ์žˆ๋Š” ์ฝ”๋“œ๋ฅผ ๋ชจ๋“ˆํ™” ์‹œ์ผœ ์ฝ”๋“œ๋ฅผ ๊ฐ„๊ฒฐํ•˜๊ฒŒ ํ–ˆ๋‹ค.

(1) API fetcher ๋ชจ๋“ˆ -> API ์š”์ฒญ ๊ธฐ๋Šฅ

import React from 'react';
import axios from 'axios';
import { METHOD } from './type';

const apis = axios.create({
    baseURL: `${process.env.REACT_APP_INDEX_SERVER_URL}`
});

const fetcher = async (method: METHOD, url: string) => {
    try {
        const res = await apis[method](url);
        return res.data;
    } catch (error: any) {
        //๐Ÿ“ ๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Œ์„ alert๋กœ ๋„์šฐ๊ณ  ํŽ˜์ด์ง€๋ฅผ ๋ฆฌ๋กœ๋“œ
        alert('๋ฐ์ดํ„ฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.')
        location.reload();
        return error;
    }
};

export default fetcher;

์‚ฌ์šฉ๋ฒ•์€ ์ด๋Ÿฐ์‹์œผ๋กœ ์“ธ ์ˆ˜ ์žˆ๋‹ค.

const domesticRank = await fetcher(METHOD.GET, `/global/domesticRank?toDate=${dayParams}`);

๊ทธ๋ฆฌ๊ณ  baseURL์— ๋‹ด๊ธด env ๋กœ์ปฌ ์ฃผ์†Œ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ์œ„ํ•ด์„œ๋Š”, yarn add env-cmd๋ฅผ ์„ค์น˜ํ•˜๊ณ  ํ”„๋กœ์ ํŠธ ๊ฐ€์žฅ ์ƒ์œ„ ํด๋”์— .env-* ํด๋”๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  baseURL์— ๊ฐ€์ ธ์˜ค๊ณ ์ž ํ•˜๋Š” ๊ฐ’์„ ๋„ฃ์–ด์ค€๋‹ค.

๊ทธ๋ฆฌ๊ณ  package.json์„ ์ˆ˜์ •ํ•ด์ค˜์•ผ ํ•จ !

"scripts": {
        "start": "env-cmd -f .env-local react-scripts start",
        "build": "react-scripts build",
        "build:dev": "env-cmd -f .env-dev react-scripts build",
        "test": "react-scripts test",
        "eject": "react-scripts eject"
    },

์ด๋ ‡๊ฒŒ scripts ์•ˆ์— ์šฐ์„ ์ˆœ์œ„๋กœ ๋ถˆ๋Ÿฌ์˜ค๊ณ ์ž ํ•˜๋Š” env ํ™˜๊ฒฝ ํŒŒ์ผ ์ˆœ์„œ๋Œ€๋กœ ์„ ์–ธํ•ด์ฃผ๋ฉด ๋œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  package.json ํŒŒ์ผ ์ฝ”๋“œ ๊ฐ€์žฅ ํ•˜๋‹จ์— "proxy": "api url ์ฃผ์†Œ" ๋ฅผ ๋„คํŠธ์›Œํฌ ํ†ต์‹ ํ•  api ์ฃผ์†Œ๋ฅผ ๊ธฐ์ž…ํ•˜๊ธฐ


(2) ๊ฐ ๊ฑฐ๋ž˜์†Œ์˜ ๊ฑฐ๋ž˜์•ก ํ•ฉ๊ณ„๋‚ด๊ธฐ

const coinSum = (arrName: any) => {
        const arrNum = _.map(arrName, 'volume');
        return <>{_.sum(arrNum).toLocaleString()}</>;
    };
  • ๊ฑฐ๋ž˜์•ก ํ•ฉ๊ณ„๋ฅผ ์‰ฝ๊ฒŒ ๋‚ด๊ธฐ ์œ„ํ•ด์„œ lodash๋ผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ชจ๋“  ๋ฐฐ์—ด์˜ ์ˆ˜๋ฅผ ํ•ฉ๊ณ„๋‚ด๋Š” ์ž‘์—…์„ ํ–ˆ๋‹ค.

(3) ์˜ค๋Š˜ ๋‚ ์งœ ํ•จ์ˆ˜

const today = new Date();
const year = today.getFullYear();
const month = ('0' + (today.getMonth() + 1)).slice(-2);
const day = ('0' + today.getDate()).slice(-2);

๐Ÿ“ export const todayCall = year + month + day;

Today ํ•จ์ˆ˜๋„ api ์ฝœ ํ• ๋•Œ ์š”๊ธฐ์ฃ ๊ธฐ ์ž์ฃผ ์“ฐ์—ฌ์„œ ๋ชจ๋“ˆํ™” ํ–ˆ๋‹ค.

4. DatePicker ์ปดํฌ๋„ŒํŠธ ์ปค์Šคํ…€

๊ธฐ์กด์— ์ž‘์—…๋œ ํŽ˜์ด์ง€๋ฅผ ๋ณด๋ฉด ๋‚ ์งœ๋ฅผ ์„ ํƒํ•˜๋Š” ๊ธฐ๋Šฅ์€ ์—†๋‹ค. ๊ธฐ์กด์—๋Š” ๋‚ ์งœ๋ฅผ ์ง์ ‘ ์ž…๋ ฅํ•ด์•ผ ํ•˜๊ณ  ๋„ˆ๋ฌด๋‚˜๋„ ๋ถˆํŽธํ–ˆ๋˜ ๊ฒŒ ์ˆซ์ž์™€ (-) ํ•˜์ดํ”ˆ๋„ ์ง์ ‘ ๋„ฃ์–ด์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•ด์•ผ ๋˜๋Š” ์‹œ์Šคํ…œ์ด๋‹ค. (ex) 2023-01-01 ์ด๋Ÿฐ์‹์œผ๋กœ ์ˆซ์ž์™€ ํ•˜์ดํ”ˆ ์„ž์–ด๊ฐ€๋ฉฐ ๋‚ ์งœ๋ฅผ ์ž…๋ ฅํ•œ ๋’ค, ๋˜ ํ•œ๋ฒˆ '์กฐํšŒ' ๋ผ๋Š” ๋ฒ„ํŠผ์„ ํด๋ฆญํ•ด์•ผ ํ•ด๋‹น ๋‚ ์งœ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณด์—ฌ์ง„๋‹ค.

๊ทธ๋ž˜์„œ ๋‚˜๋Š” ์ด๋Ÿฌํ•œ ๋ถˆํŽธํ•จ์„ ์—†์• ๊ณ  ์‹ฌ๋ฏธ์„ฑ๊ณผ ํŽธ๋ฆฌ์„ฑ ๋‘˜ ๋‹ค ์žก๊ธฐ ์œ„ํ•ด UI/UX์ ์œผ๋กœ ๊ฐœ์„ ์‹œํ‚ค๊ณ ์ž antd ๋ผ๋Š” ์ปดํฌ๋„ŒํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ DataPicker ๋ฅผ ๊ฐ€์ ธ์˜จ ๋’ค ์ปค์Šคํ…€ํ•˜์˜€๋‹ค.

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

๐Ÿ“ Search.tsx (DatePicker ์ปค์Šคํ…€ ์ปดํฌ๋„ŒํŠธ)

import React from 'react';
import moment from 'moment';
import { useRecoilValue, useSetRecoilState } from 'recoil';
import { daySearch } from 'recoil/atom';
import { dayValue } from 'recoil/selector';
import type { DatePickerProps } from 'antd';
import { DatePicker } from 'antd';
import { todayCall } from 'lib/day';
import './styles.scss';

const Search = () => {
	// ๐Ÿ“ useSetRecoilState๋Š” ๋ฆฌ์ฝ”์ผ์—์„œ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•ด์ฃผ๋Š” setter ์—ญํ• ์ด๋‹ค.
    const setCurrent: any = useSetRecoilState(daySearch);
    const value = useRecoilValue(dayValue);

	// ๐Ÿ“ setCurrent์— ๋‹ด๊ธด daySearch ๊ฐ’์„ ํด๋ฆญํ•œ dateString์˜ ๊ฐ’์œผ๋กœ ๋ณ€๊ฒฝํ•œ๋‹ค.
    const onChangeDate: DatePickerProps['onChange'] = (date, dateString) => {
        setCurrent(`${dateString}`);
    };

    // ์‹ค์‹œ๊ฐ„
    const reloadButton = () => {
        setCurrent(todayCall);
    };

    return (
        <section className="search">
            <DatePicker
                onChange={onChangeDate}
                className="dataInput"
                // ๐Ÿ“ ๋‚ ์งœ ํ˜•์‹
                format="YYYY-MM-DD"
                placeholder="๋‚ ์งœ๋ฅผ ์„ ํƒํ•ด์ฃผ์„ธ์š”."
                allowClear={false}
                value={value}
                // ๐Ÿ“ ์˜ค๋Š˜ ๋‚ ์งœ ์ดํ›„๋กœ ๋ฏธ๋ž˜ ๋‚ ์งœ๋ฅผ ํด๋ฆญํ•˜์ง€ ๋ชปํ•˜๊ฒŒ ๋น„ํ™œ์„ฑํ™” ์‹œํ‚ค๋Š” ๋ฐฉ๋ฒ•
                disabledDate={(current: any) => {
                    // ์ด ๋‚ ๋ถ€ํ„ฐ ~ ์ด ๋‚  ๊นŒ์ง€๋งŒ ์„ ํƒ ๊ฐ€๋Šฅ
                    return current.year() < 2020 || current >= moment().subtract(1, 'days').toDate();
                }}
            />
            <button className="btn-now" id="searchNowBtn" type="button" onClick={reloadButton}>
                ์‹ค์‹œ๊ฐ„
            </button>
        </section>
    );
};

export default Search;

๐Ÿ‘‡ ๊ฒฐ๊ณผ๋ฌผ

โญ๏ธ ์ฐธ๊ณ ์ž๋ฃŒ โญ๏ธ
antd ๐Ÿ‘‰ https://ant.design/components/date-picker#header
์Šคํƒ์˜ค๋ฒ„ํ”Œ๋กœ์šฐ ๐Ÿ‘‰https://stackoverflow.com/questions/46358603/disable-date-and-time-for-antd-datepicker

5. ๊ฐ ๊ฑฐ๋ž˜์†Œ ๋งˆ๋‹ค์˜ ์ฝ”์ธ ๋ฆฌ์ŠคํŠธ

import React from 'react';
import { CoinList, Rank } from 'lib/type';
import CoinNameList from './CoinNameList';
import ExchangeList from './ExchangeList';
import './styles.scss';

interface Props {
    coinList: [CoinList][];
    rankingList: Rank[];
}
const List = ({ coinList, rankingList }: Props) => {
    // ๊ฑฐ๋ž˜์†Œ ์ฝ”์ธ ๋ฆฌ์ŠคํŠธ
    const arrDealList = () => {
        const list = [];
        for (let i = 0; i < coinList.length; i++) {
            list.push(<ExchangeList key={i} globalCoinList={coinList[i]} globalRank={rankingList[i]} />);
        }
        return list;
    };

    return (
        <div className="coinListBox">
            <CoinNameList coinName={coinList[0]} />
            {arrDealList()}
        </div>
    );
};

export default List;

๋ถ€๋ชจ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ฟŒ๋ ค์ฃผ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„ List ์ปดํฌ๋„ŒํŠธ์— ๋ฐ˜๋ณต๋ฌธ์„ ๋Œ๋ ค ์ฝ”์ธ๋ฆฌ์ŠคํŠธ์˜ length ๋งŒํผ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋„๋ก ํ–ˆ๋‹ค.

๐Ÿ‘€ ์ตœ์ข… ๊ฒฐ๊ณผ๋ฌผ

โ™ป๏ธ ๊ธฐ์กด ํŽ˜์ด์ง€ (๊ตญ๋‚ด ๊ฑฐ๋ž˜์†Œ)

โœ… ๊ฐœ์„ ํ•œ ํŽ˜์ด์ง€ (๊ตญ๋‚ด ๊ฑฐ๋ž˜์†Œ)

โ™ป๏ธ ๊ธฐ์กด ํŽ˜์ด์ง€ (๊ตญ๋‚ด ๋ฐ ๊ตญ์ œ ๊ฑฐ๋ž˜์†Œ)

โœ… ๊ฐœ์„ ํ•œ ํŽ˜์ด์ง€ (๊ตญ๋‚ด ๋ฐ ๊ตญ์ œ ๊ฑฐ๋ž˜์†Œ)

๐ŸŒ€ ๊ฐœ์„ ํ•œ์  ์ •๋ฆฌ

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

๋Ÿฌ๋‹ํฌ์ธํŠธ

1๏ธโƒฃ ์ œ๊ณต๋˜๋Š” ๋ฐ์ดํ„ฐ ์ˆ˜์น˜(๋ฐฐ์—ด, ๊ฐ์ฒด)๋“ค์„ ํ•ธ๋“ค๋ง ํ•˜๋Š”๋ฐ lodash๋ผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋น ๋ฅธ ์ž‘์—…๊ณผ ๊ฐ„๊ฒฐํ•œ ์ฝ”๋“œ๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด๊ฒƒ์€ ํผํฌ๋จผ์Šค ์ธก๋ฉด์—์„œ๋„ native๋ณด๋‹ค ๋” ๋‚˜์€ ์„ฑ๋Šฅ์„ ๊ฐ€์กŒ๋‹ค๊ณ  ํ•œ๋‹ค.
๐Ÿ‘‰ ์ฐธ๊ณ ์ž๋ฃŒ https://lodash.com/docs/4.17.15

2๏ธโƒฃ ์ด ์Šคํ„ฐ๋””๋ฅผ ํ•˜๋ฉด์„œ ๋‚ด ๋ชฉํ‘œ์˜€๋˜ ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž์™€ ์›ํ™œํ•œ ์†Œํ†ต์„ ์œ„ํ•œ ๋ฐฑ์—”๋“œ ๊ด€๋ จ ๊ฐœ๋… ์šฉ์–ด๋“ค์„ ์ˆ™์ง€ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค. Batch ๋ผ๋Š” ๊ฒƒ์„ ์ƒˆ๋กญ๊ฒŒ ์•Œ๊ฒŒ ๋๋Š”๋ฐ ์ด๊ฒƒ์€ ๊ณผ๊ฑฐ์˜ ๋ฐ์ดํ„ฐ๋“ค์„ ์ง‘๊ณ„ํ•˜๋Š” ๊ฐœ๋…์ด๋‹ค. ๊ทธ๋ž˜์„œ ์ด๊ฑธ๋กœ ํ•˜์—ฌ๊ธˆ ๊ฑฐ๋ž˜์†Œ์˜ ๊ณผ๊ฑฐ ๋ฐ์ดํ„ฐ๋“ค์„ ์ˆ˜์ง‘ํ•  ์ˆ˜ ์žˆ์—ˆ๋˜ ๊ฒƒ์ด์—ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๊ฐ ๋‚ ์งœ ๋ณ„๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์‘๋‹ต ๋ฐ›์„ ๋•Œ๋„ ํŒŒ๋ผ๋ฏธํ„ฐ ํ˜น์€ ์ฟผ๋ฆฌ์ŠคํŠธ๋ง์œผ๋กœ ๋‚ ์งœ ๊ตฌ๊ฐ„์„ ์ƒํƒœ๊ด€๋ฆฌ atom์œผ๋กœ ๋ฐ์ดํ„ฐํ”ผ์ปค๋กœ ์„ ํƒํ•œ ๋‚ ์งœ๋ฅผ ๋ฐ›์•„ ๋ฐ์ดํ„ฐ๋ฅผ ์‘๋‹ต๋ฐ›์„ ์ˆ˜ ์žˆ์—ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  Swagger์™€ PostMan ํ™œ์šฉํ•˜๋Š” ๋ฒ•๋„ ์ œ๋Œ€๋กœ ํ„ฐ๋“ํ•  ์ˆ˜ ์žˆ์—ˆ์Œ ๐Ÿคฉ

3๏ธโƒฃ ์ƒํƒœ๊ด€๋ฆฌ ๊ธฐ์ˆ  ์Šคํƒ์œผ๋กœ Recoil์ด๋ผ๋Š” ๊ฒƒ์„ ์‚ฌ์šฉํ•ด๋ณธ ์ .

4๏ธโƒฃ env-cmd ๋ฅผ ํ™œ์šฉํ•˜์—ฌ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ์„ค์ •ํ–ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ด๋ฅผ ํ†ตํ•ด ๊ฐœ๋ฐœ, QA ๋“ฑ๋“ฑ์˜ ํ™˜๊ฒฝ์—์„œ ์‚ฌ์šฉํ•  ํ™˜๊ฒฝ๋ณ€์ˆ˜ ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜์—ฌ ์ƒํ™ฉ์— ๋งž๊ฒŒ package.json์— ์Šคํฌ๋ฆฝํŠธ ๋ช…๋ น์–ด ๋ณ„๋กœ env ํŒŒ์ผ ์šฐ์„ ์ˆœ์œ„๋ฅผ ์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

5๏ธโƒฃ ๋ฆฌ์•กํŠธ๋กœ ๊ฐœ๋ฐœํ•  ๋•Œ ๊ฐœ๋ฐœ ์„œ๋ฒ„ ์ ‘์† ์ฃผ์†Œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ localhost:3000์ด๋‹ค. ๋งŒ์•ฝ api ์„œ๋ฒ„ ์ฃผ์†Œ๊ฐ€ localhostL5000 ์ด๋ผ๊ณ  ํ•˜๋ฉด, ๋”ฐ๋กœ CORS ์„ค์ •์„ ํ•ด๋†“์ง€ ์•Š์€ ์ด์ƒ api request๋ฅผ ๋‚ ๋ฆฌ๋ฉด CORS ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ๊ทธ๋ž˜์„œ packages.json์—์„œ Proxy ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋ฉด CORS ์ •์ฑ…์„ ์šฐํšŒํ•  ์ˆ˜ ์žˆ๋‹ค.

6๏ธโƒฃ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ธฐ์ ์œผ๋กœ ์š”์ฒญํ•˜๊ณ  ์‘๋‹ต๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” Polling์„ ์ด์šฉํ•˜์—ฌ ์ฝ”์ธ ์žฅ์˜ ๋ฐ์ดํ„ฐ๋“ค์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ฐ›์•„๋‚ผ ์ˆ˜ ์žˆ์—ˆ๋‹ค.

๋ ๐Ÿ˜ถ

profile
์•„๋Š‘ํ•œ ๋‡Œ๊ณต๊ฐ„ ๐Ÿง 
post-custom-banner

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

comment-user-thumbnail
2023๋…„ 2์›” 8์ผ

ํ˜„์žฌ ์ „ ์„ธ๊ณ„์—๋Š” 200์—ฌ ๊ฐœ ์ด์ƒ์˜ ์•”ํ˜ธํ™”ํ ๊ฑฐ๋ž˜์†Œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์ค‘์—์„œ ์šฐ๋ฆฌ์˜ ์—ฌ๊ฑด์— ์ ํ•ฉํ•œ ๊ฑฐ๋ž˜์†Œ๋ฅผ ์ฐพ๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋งŽ์€ ๋…ธ๋ ฅ์ด ํ•„์š”ํ•  ๊ฒ๋‹ˆ๋‹ค. ๋‹จ์ˆœํžˆ ์ง€์›๋˜๋Š” ํ† ํฐ์˜ ์ˆ˜๋Ÿ‰์ด๋‚˜, ์•ˆ์ „, ์ˆ˜์ˆ˜๋ฃŒ๋ฅผ ๊ณ ๋ คํ•˜๋Š” ๊ฒƒ ์ด์™ธ์—๋„, ๊ณ ๊ฐ ์„œ๋น„์Šค ํ’ˆ์งˆ, ์ง€๊ฐ‘ ๋ณด์•ˆ, ๊ทธ๋ฆฌ๊ณ  ์ตœ์†Œ ์˜ˆ์น˜๊ธˆ์— ๋Œ€ํ•ด์„œ๋„ ์•Œ์•„์•ผ ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. McKesson Ordering

1๊ฐœ์˜ ๋‹ต๊ธ€