์–ด๋–ค ๊ธฐ์ˆ ์„ ์„ ์ •ํ•ด๋ณผ๊นŒ?

์ €ํฌํŒ€ (NDD)๋Š” ๋ฉด์ ‘์—ฐ์Šต ์„œ๋น„์Šค๋ฅผ ๊ฐœ๋ฐœํ•˜๊ธฐ์ „, ๊ธฐ์ˆ  ์„ ์ •์„ ํ•˜๋Š” ์‹œ๊ฐ„์„ ๊ฐ€์ง€๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

Profile Picture
adultlee
2023-11-08

์›๋ณธ

์„œ๋ฌธ

์ €ํฌํŒ€ (NDD)๋Š” ๋ฉด์ ‘์—ฐ์Šต ์„œ๋น„์Šค๋ฅผ ๊ฐœ๋ฐœํ•˜๊ธฐ์ „, ๊ธฐ์ˆ  ์„ ์ •์„ ํ•˜๋Š” ์‹œ๊ฐ„์„ ๊ฐ€์ง€๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ์ˆ  ์„ ์ •์— ์žˆ์–ด์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์งˆ๋ฌธ์ด ์ฃผ์–ด์กŒ์Šต๋‹ˆ๋‹ค.

  1. next๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•˜๋Š”๊ฐ€?
  2. ์–ด๋–ค styled in JS ๋ฅผ ์‚ฌ์šฉํ• ๊ฒƒ์ธ์ง€
  3. ์ƒํƒœ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ์–ด๋–ค๊ฒƒ?

์ €ํฌ๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ๋…ผ์˜๋ฅผ ์ง„ํ–‰ํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

Next ๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•˜๋Š”๊ฐ€?

Next๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋‹ค์–‘ํ•œ ํŽธ์˜์ ์ธ ๊ธฐ๋Šฅ๋“ค์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์•„์ฃผ ๊ฐ•๋ ฅํ•œ ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค.

์ด๋ฒˆ ํŒ€ ๋ธ”๋กœ๊ทธ๋„ SSG๋ฅผ ์ด์šฉํ•ด์„œ Next๊ธฐ๋ฐ˜์œผ๋กœ ๋งŒ๋“ค์—ˆ๊ธฐ๋„ ํ–ˆ์Šต๋‹ˆ๋‹ค.

Next์„ ์‚ฌ์šฉํ•˜๋ฉฐ ํ˜ธ๊ฐ์ด ๋“œ๋Š” ํ”„๋ ˆ์ž„์›Œํฌ ์ธ๋ฐ, ์—ฌ๋Ÿฌ๊ฐ€์ง€ ํŽธ๋ฆฌํ•œ ๊ธฐ๋Šฅ๋“ค์ด ์กด์žฌํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

1. ์ง๊ด€์ ์ธ ํŒŒ์ผ ์‹œ์Šคํ…œ ๊ธฐ๋ฐ˜ ๋ผ์šฐํŒ… ์ฒ˜๋ฆฌ

โ†’ next13 ๋ฒ„์ „์˜ app Dir ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด, page router ์ฒ˜๋ฆฌ์—์„œ ์†Œ์†Œํ•˜๊ฒŒ ๊ฐ๋™๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค. ํŠน๋ณ„ํ•œ ๊ทœ์น™์„ ์ ์šฉํ•˜์ง€ ์•Š๋”๋ผ๋„(react-router ๋“ฑ๋“ฑ) ์ •๋ง ์ง๊ด€์ ์ธ page ์ฒ˜๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋ฉฐ, ๋ฐ”๋กœ ์ฝ”๋“œ ์Šคํ”Œ๋ฆฟํŒ…์ด ์ด๋ฃจ์–ด์ ธ ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œํ‚ค๊ณ  ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ๊ฐœ์„ ํ•˜๋Š”๋ฐ ํฐ ๋„์›€์ด ๋œ๋‹ค๊ณ  ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. (ํŽ˜์ด์ง€ ๋ณ„๋กœ ํ•„์š”ํ•œ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ฝ”๋“œ๋งŒ ๋กœ๋“œํ•˜๋Š”๊ฒƒ์„ ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ ์ง€์›)

2. Next-Image๋ฅผ ํ†ตํ•œ ์ด๋ฏธ์ง€ ์ตœ์ ํ™”

โ†’ ์ผ๋ฐ˜์ ์œผ๋กœ ์›นํŽ˜์ด์ง€์˜ ์„ฑ๋Šฅ์„ ๋Œ์–ด์˜ฌ๋ฆฌ๋Š” ์—ฌ๋Ÿฌ ๋ฐฉ๋ฒ•์ค‘์— ์ด๋ฏธ์ง€ ์ตœ์ ํ™”๋Š” ์•„์ฃผ ํ•„์ˆ˜์ ์ธ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. Next๋ฅผ ๊ฐœ๋ฐœํ•œ vercel๋„ ์ด ๋˜ํ•œ ์œ ๋…ํ•ด์„œ ๊ธฐ๋Šฅ์„ ๊ฐœ๋ฐœํ•œ๊ฒƒ์ด ๋ˆˆ์— ๋„๋Š” ๋ฐ์š”! ๋Œ€ํ‘œ์ ์œผ๋กœ Lazy-Loading๊ณผ ์ด๋ฏธ์ง€ ์‚ฌ์ด์ฆˆ ์ตœ์ ํ™” ๋“ฑ๋“ฑ์„ ํ†ตํ•ด์„œ ์ด๋ฏธ์ง€๋ฅผ ํ†ตํ•ด์„œ ๋ฐœ์ƒํ•˜๋Š” ์—ฌ๋Ÿฌ ๋ฌธ์ œ๋ฅผ ์•„์ฃผ ์‰ฝ๊ฒŒ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

https://fe-developers.kakaoent.com/2022/220714-next-image/

์นด์นด์˜ค ์—”ํ„ฐํ…Œ์ด๋จผํŠธ์—์„œ๋„ Next/Image๋ฅผ ์‚ฌ์šฉํ•ด ์•„์ฃผ ๊ฐ„๋‹จํ•˜๊ฒŒ ์ด๋ฏธ์ง€ ์ตœ์ ํ™”๋ฅผ ์ ์šฉํ•œ๊ฒƒ์ด ๋ˆˆ์— ๋•๋‹ˆ๋‹ค.

3. SSR ์„ ํ”„๋ ˆ์ž„์›Œํฌ ๋‹จ์œ„์—์„œ ์ง€์›

โ†’ Next๋Š” ๋™์  ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์„œ๋ฒ„์ธก์—์„œ HTML ์„ ์ƒ์„ฑํ•˜๊ณ  ํด๋ผ์ด์–ธํŠธ์— ์ „์†กํ•˜๋Š” ๋ฐฉ์‹์ธ SSR์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์•„์ฃผ ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ์ด๋ฉฐ, SEO์— ๊ทนํžˆ ์œ ๋ฆฌํ•ฉ๋‹ˆ๋‹ค. (SEO๋Š” "Search Engine Optimization"์˜ ์•ฝ์ž๋กœ, ์›น์‚ฌ์ดํŠธ๋‚˜ ์›นํŽ˜์ด์ง€๋ฅผ ๊ฒ€์ƒ‰ ์—”์ง„์—์„œ ๋” ์ž˜ ์ฐพ์„ ์ˆ˜ ์žˆ๊ฒŒ ์ตœ์ ํ™”ํ•˜๋Š” ๊ณผ์ •์ž…๋‹ˆ๋‹ค. ์ด๋Š” ์›น์‚ฌ์ดํŠธ์˜ ๊ฐ€์‹œ์„ฑ์„ ๋†’์ด๊ณ , ๊ฒ€์ƒ‰ ์—”์ง„ ๊ฒฐ๊ณผ ํŽ˜์ด์ง€(SERP)์—์„œ์˜ ๋žญํ‚น์„ ํ–ฅ์ƒ์‹œ์ผœ, ๋” ๋งŽ์€ ๋ฐฉ๋ฌธ์ž์™€ ํŠธ๋ž˜ํ”ฝ์„ ์œ ๋„ํ•˜๋Š” ๋ชฉ์ ์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค.)

์œ„์— ์ž‘์„ฑํ•œ ๊ธฐ๋Šฅ๋“ค ๋ฟ์•„๋‹ˆ๋ผ ์—ฌ๋Ÿฌ ์žฅ์ ๋“ค์ด ๋‹ค์ˆ˜ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. (/public ๊ฒฝ๋กœ๋ฅผ ์šฐ์„ ํ•ด ์ด๋ฏธ์ง€ src ๊ด€๋ฆฌ๊ฐ€ ํŽธ๋ฆฌ ๋“ฑ๋“ฑ)

ํ•˜์ง€๋งŒ ์šฐ๋ฆฌ๋Š” Next๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ๋Š”๋ฐ, ์•„๋ž˜์™€ ๊ฐ™์€ ์ด์œ ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๊ฐ€ Next๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ธฐ๋กœ ํ•œ ์ด์œ 

1. ์šฐ๋ฆฌ์—๊ฒŒ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ(Next) ํ•™์Šตํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ• ๊นŒ?

โ†’ ์‚ฌ์‹ค ๊ฐ€์žฅ ํฐ ์ด์œ ๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ๋‹ค. ํŽธ๋ฆฌํ•œ ๊ธฐ๋Šฅ์ด ๋ชจ์—ฌ์ง„ ํ•˜๋‚˜์˜ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด, ๋” ์ข‹์€ ์„ฑ๋Šฅ์˜ ์„œ๋น„์Šค๋ฅผ ๋น ๋ฅธ ์‹œ์ผ ๋™์•ˆ ๋งŒ๋“ค์–ด ๋‚ผ ์ˆ˜ ์žˆ์„์ง€๋„ ๋ชจ๋ฅธ๋‹ค. ํ•˜์ง€๋งŒ ๊ทธ ๊ณผ์ •์—์„œ ์šฐ๋ฆฌ์—๊ฒŒ ๋‚จ๋Š”๊ฒƒ์ด ์„œ๋น„์Šค๋งŒ ์žˆ๊ธฐ๋ฅผ ๋ฐ”๋ผ์ง€๋Š” ์•Š๋Š”๋‹ค. ์šฐ๋ฆฐ ์ด๋ฒˆ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•˜๋ฉฐ ํ•™์Šต์„ ํ†ตํ•ด ์ง€์‹์ ์œผ๋กœ ์„ฑ์ทจ๋ฅผ ์ด๋ค„์•ผ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค. ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ ์ง€์›ํ•˜๋Š” ์ด๋ฏธ์ง€ ์ตœ์ ํ™”๋ฅผ ์ง์ ‘ ๋งŒ๋“ค์–ด๋ณด๊ณ , ์ฝ”๋“œ ์Šคํ”Œ๋ฆฟํŒ…์„ ํ•ด๋ณด๋ฉฐ ์ข€ ๋” ๊นŠ์ด ์žˆ๊ฒŒ ์›๋ฆฌ๋ฅผ ์ดํ•ดํ•ด ๋ณด๊ณ  ์‹ถ๋‹ค. ์‚ฌ์‹ค ์ •๋„์˜ ๋ฌธ์ œ์ด๊ธด ํ•ฉ๋‹ˆ๋‹ค. react์กฐ์ฐจ๋„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„์•ผ๋งŒ ํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•  ์ˆ˜ ๋„ ์žˆ์œผ๋‹ˆ ๋ง์ž…๋‹ˆ๋‹ค. ๊ทธ๋ž˜๋„ ์šฐ๋ฆฐ react๋ฅผ ์‚ฌ์šฉํ•˜๋ฉฐ ๋” ๊นŠ๊ฒŒ FE์˜ ์—ฌ๋Ÿฌ ๋ถ„์•ผ์— ๋Œ€ํ•ด์„œ ํ•™์Šต์„ ์ง„ํ–‰ํ•ด ๋ณด๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

2. Next13์˜ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๊ธฐ๋Šฅ์— ๋Œ€ํ•ด์„œ ์•„์ง ํ•™์Šตํ•  ์ค€๋น„๊ฐ€ ๋˜์ง€ ์•Š์•˜๋‹ค.

โ†’ Next13์ด ์—…๋ฐ์ดํŠธ ๋˜๋ฉฐ ์ •๋ง ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์ด ์—…๋ฐ์ดํŠธ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด์ „๊นŒ์ง€๋Š” client-component๊ธฐ๋ฐ˜์ด๋˜ Next๊ฐ€ default๋ฅผ server-component๋กœ ๋ณ€๊ฒฝํ–ˆ๋‹ค๋Š” ์ ์ด ๊ฐ€์žฅ ํฐ ์ „ํ™˜์ ์ด๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์—ฌ๊ธฐ์„œ ๋‹ค์†Œ ์น˜๋ช…์ ์ธ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋Š”๋ฐ, emotion์„ ๋น„๋กฏํ•œ ์—ฌ๋Ÿฌ style in JS ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋“ค์ด ํ˜ธํ™˜๋˜์ง€ ์•Š๋Š” ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค.(๋ฌผ๋ก  ๊ทธ๋Ÿฐ ํ๋ฆ„์— ๋”ฐ๋ผ Next ๋˜ํ•œ atomic css ๊ธฐ๋ฐ˜์ธ tailwind๋ฅผ ์ ๊ทน์ ์œผ๋กœ ์ง€์›ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ) ์ด๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„  ๋‹ค์†Œ ๋‚œํ•ดํ•œ ์…‹ํŒ…์„ ์ง„ํ–‰ํ•œ ํ›„ ํŒŒ์ผ์˜ ์ตœ ์ƒ๋‹จ์— โ€œuse clientโ€๋ฅผ ๊ธฐ์ž…ํ•˜๋ฉฐ ๋‹ค์†Œ ์–ด๊ธ€๋ฆฌํ•œ(์ง€๊ทนํžˆ ์ฃผ๊ด€์ ์ž…๋‹ˆ๋‹ค) ์ฝ”๋“œ๊ฐ€ ์ž‘์„ฑ๋˜์–ด์•ผ๋งŒ ํ•ฉ๋‹ˆ๋‹ค. ๋ฌผ๋ก  app router๋ฅผ ์‚ฌ์šฉํ•ด์„œ layout๋ฅผ ์žฌํ™œ์šฉํ•˜๋Š” ๋ถ€๋ถ„๊นŒ์ง€ ํฌํ•จ๋˜์–ด ์žˆ๋‹ค๋ณด๋‹ˆ, ์ด์ฏค ๋˜๋ฉด FE ๊ณต๋ถ€๋ผ๊ธฐ๋ณด๋‹จ Next13์˜ ํ•™์Šต์— ์น˜์ค‘๋˜๋Š” ๋“ฏํ•˜์—ฌ ๋ฐฉํ–ฅ์„ฑ์ด ์ž˜๋ชป๋˜์—ˆ๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. (๋ฌผ๋ก  app router๋ฅผ ์„ค์ •ํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ์ด์ „๊นŒ์ง€์˜ next๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์ด๋Š” ์˜คํžˆ๋ ค ์‚ฌ์žฅ๋˜์–ด ๊ฐ€๋Š” ํ”„๋ ˆ์ž„์›Œํฌ์˜ ๋์— ๋ฉ”๋‹ฌ๋ ค ์žˆ๋Š” ๋“ฏํ•œ ๊ธฐ๋ถ„์ด ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. Next 13์˜ app router๊ฐ€ stable ๊นŒ์ง€ ๋˜์—ˆ์œผ๋‹ˆ, ๋”๋”์šฑ ์ด์ „ ๋ฒ„์ „์— ๋Œ€ํ•œ ํ•™์Šต์€ ์•„์‰ฝ๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. )

https://nextjs.org/blog/next-13-4

3. SSR์€ (์•„์ง) ํ•„์š”ํ•˜์ง€ ์•Š๋‹ค.

โ†’ SSR์˜ ์ตœ๋Œ€ ์žฅ์ ์ด๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ๋Š” ์ดˆ๊ธฐ ๋กœ๋”ฉ ์†๋„์™€ Seo ์— ์œ ๋ฆฌํ•˜๋‹ค๋Š” ์  ๋ชจ๋‘ ์•„์ง ์šฐ๋ฆฌ ์„œ๋น„์Šค์— ๋Œ€ํ•ด์„œ ๋ถˆํ•„์š”ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค. ์ €ํฌ์˜ ์„œ๋น„์Šค๋Š” Desktop ํ™˜๊ฒฝ์ด ์—์„œ ์ œ๊ณต๋˜๊ธฐ ๋•Œ๋ฌธ์— ์•„์ง ์ดˆ๊ธฐ ๋กœ๋”ฉ ์†๋„์— ๋Œ€ํ•ด์„œ ๋ชจ๋ฐ”์ผ์— ๋น„ํ•ด ์—ฌ์œ ๋กญ๊ฒŒ ๋Œ€์ฒ˜๊ฐ€ ๊ฐ€๋Šฅํ• ๊ฒƒ์ด๋ผ ์˜ˆ์ƒํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. seo ๋˜ํ•œ ssr์ด ์•„๋‹Œ csr ์ƒํƒœ์—์„œ ์ตœ์„ ์„ ๋‹คํ•ด์„œ seo๋ฅผ ์ค€๋น„ํ•˜๋Š” ๊ฒฝํ—˜์„ ๊ฐ€์ง€๊ณ  ์‹ถ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์ด ํŽธ์ด ์šฐ๋ฆฌ FE ํŒ€์ด ์„ฑ์žฅํ•˜๋Š”๋ฐ ํฐ ๋„์›€์ด ๋˜๋ฆฌ๋ผ ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค. ๋”๋ถˆ์–ด SSR์ด๊ธฐ ๋•Œ๋ฌธ์— ๋™์ ์œผ๋กœ ์„œ๋ฒ„๋ฅผ ์šด์˜ํ•ด์•ผ ํ•˜๋Š” ๋ถ€๋‹ด์„ ๋™์‹œ์— ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฒฐ๊ตญ ์ด์™€ ๊ฐ™์€ ์ด์œ ๋กœ ์šฐ๋ฆฌ๋Š” Next๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค!

์–ด๋–ค styled in JS ๋ฅผ ์‚ฌ์šฉํ• ๊ฒƒ์ธ์ง€

์‚ฌ์‹ค ๋งŽ์€ ์„ ํƒ์ง€๊ฐ€ ์žˆ์ง€๋Š” ์•Š์•˜์Šต๋‹ˆ๋‹ค. ์˜คํžˆ๋ ค ๋‹ค๋“ค ๋ช…ํ™•ํ–ˆ์Šต๋‹ˆ๋‹ค.

์šฐ๋ฆฌ ํŒ€์›๋“ค ๋ชจ๋‘ tailwind์™€ ๊ฐ™์€ atomic css ์— ์ต์ˆ™ํ•˜์ง€๋Š” ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์— tailwind css๋Š” ๊ฐ€์žฅ๋จผ์ € ์šฐ์„ ์ˆœ์œ„์—์„œ ์ œ๊ฑฐ๋˜์—ˆ์Šต๋‹ˆ๋‹ค (์‚ฌ์šฉํ• ๊ฑฐ๋ฉด ์ฐจ๋ผ๋ฆฌ Next13์˜ app router๋ž‘ ํ•จ๊ป˜ ์‚ฌ์šฉํ–ˆ์ง€โ€ฆ!)

ํŒ€์›๋“ค ๋ชจ๋‘ styled-components๋ฅผ ์‚ฌ์šฉํ•ด๋ณธ ๊ฒฝํ—˜์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

๋ชจ๋‘๋“ค styled-components์— ์ต์ˆ™ํ–ˆ์œผ๋ฉฐ, emotion์„ ์‚ฌ์šฉํ•˜๊ธฐ์— ํฐ ์–ด๋ ค์›€์ด ์—†์„๊ฒƒ ์ด๋ผ๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

๋ฌด์—‡๋ณด๋‹ค ์ œ๊ฐ€ emotion์„ ์‚ฌ์šฉํ•ด๋ณด๊ณ  ์‹ถ์—ˆ์Šต๋‹ˆ๋‹ค.

์ œ๊ฐ€ ์‚ฌ์šฉํ•ด๋ดค๋˜ css-props์— ๋Œ€ํ•œ ๊ฒฝํ—˜์ด ์ข‹์•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์–ด๋Š ์ •๋„๋Š” inline css ์Šค๋Ÿฌ์šด ์ž‘์„ฑ์ด์—ˆ์œผ๋‚˜, ๋ณด๋‹ค ์ง๊ด€์ ์ด์—ˆ์œผ๋ฉฐ ๋” ์ด์ƒ styled-component์˜ ์ด๋ฆ„์„ ์ •ํ•˜๋Š” ๊ฒƒ์— ๊ณ ํ†ต๋ฐ›์ง€ ์•Š์•„๋„ ๋œ๋‹ค๋Š” ์ ์ด ๋งค๋ ฅ์ ์ด์—ˆ์Šต๋‹ˆ๋‹ค.

๊ฐˆ๋“ฑ ํ•˜๋‚˜ ์—†์ด ์šฐ๋ฆฌ๋Š” Emotion์„ ์„ ํƒํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

์ƒํƒœ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ์–ด๋–ค๊ฒƒ?

์‚ฌ์‹ค ๊ฒฐ์ •ํ•˜๊ธฐ ๊ฐ€์žฅ ์–ด๋ ค์šด ๋ถ€๋ถ„์ด์—ˆ์Šต๋‹ˆ๋‹ค.

์ €๋Š” ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์ƒํƒœ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด๋ณธ์ ์ด ์—†์—ˆ์œผ๋ฉฐ, ์œ ์ผํ•˜๊ฒŒ ์‚ฌ์šฉํ•ด๋ณธ ์ƒํƒœ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” Mobx์˜€์Šต๋‹ˆ๋‹ค.

Mobx์— ๋Œ€ํ•œ ๊ฒฝํ—˜์€ ์ •๋ง ์ข‹์•˜์Šต๋‹ˆ๋‹ค. Mobx์˜ ์ค‘์‹ฌ์ด ๋˜๋Š” ์ฒ ํ•™์€

"Anything that can be derived from the application state, should be derived. Automatically."

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

ํ•˜์ง€๋งŒ ๋‹ค๋ฅธ ์ƒํƒœ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋Œ€ํ•œ ๊ฒฝํ—˜์€ ์ „ํ˜€ ์—†์—ˆ๊ธฐ ๋•Œ๋ฌธ์—, ํŒ€์›๋ถ„๋“ค์„ ๋ฏฟ๊ณ  ํŒ€์›๋ถ„๋“ค์˜ recoil์„ ์‚ฌ์šฉํ•ด๋ณด์ž๋Š” ์˜๊ฒฌ์„ ๋”ฐ๋ฅด๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

recoil์„ ์ƒˆ๋กญ๊ฒŒ ๋„์ž…ํ•˜๊ธฐ ์ „์— ์ œ๊ฐ€ mobx๋ฅผ ์‚ฌ์šฉํ•˜๋˜ ์ฝ”๋“œ๋ฅผ ํ™•์ธํ•ด ๋ณด๋ฉฐ, ๊ทธ ๊ฐœ๋…์„ ๋‹ค์‹œ๊ธˆ ์ตํ˜€๋ณด๋ คํ–ˆ์Šต๋‹ˆ๋‹ค.

์•„๋ž˜๋Š” ์ œ๊ฐ€ ์ง€๊ธˆ๊นŒ์ง€ ์‚ฌ์šฉํ–ˆ๋˜ mobx ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ์˜ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

์ „์ฒด์ ์ธ Mobx ๋Š๋‚Œ์„ ๊ธฐ์–ตํ•ด๋ณด๋ ค ๋‹ค์‹œ ๊บผ๋‚ด๋ดค์Šต๋‹ˆ๋‹ค!

// MobX์˜ `observable`๊ณผ `toJS` ํ•จ์ˆ˜๋ฅผ ์ž„ํฌํŠธํ•ฉ๋‹ˆ๋‹ค.
import { observable, toJS } from "mobx";
// ToastProps์™€ ToastState ํƒ€์ž…์„ ์ž„ํฌํŠธํ•ฉ๋‹ˆ๋‹ค.
import type { ToastProps, ToastState } from "./type";

// `toastStore` ์ƒํƒœ ์ €์žฅ์†Œ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์ด ์ƒํƒœ๋Š” ๋ฐ˜์‘ํ˜•์œผ๋กœ ๊ด€์ฐฐ ๊ฐ€๋Šฅํ•œ ๊ฐ์ฒด์ž…๋‹ˆ๋‹ค.
export const toastStore = observable({
  // `state` ๊ฐ์ฒด๋ฅผ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—๋Š” `toasts` ๋ฐฐ์—ด๊ณผ `deviceType` ๋ฌธ์ž์—ด์ด ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.
  state: {
    toasts: [],
    deviceType: "desktop",
  } as ToastState,

  // ์ƒˆ๋กœ์šด ํ† ์ŠคํŠธ ๋ฉ”์‹œ์ง€๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๋ฉ”์„œ๋“œ์ž…๋‹ˆ๋‹ค.
  addToast(content: string, type: "success" | "warning" | "info" | "error") {
    // ๋””๋ฐ”์ด์Šค ํƒ€์ž…์— ๋”ฐ๋ผ ์ตœ๋Œ€ ํ† ์ŠคํŠธ ๋ฉ”์‹œ์ง€ ๊ธธ์ด๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
    const maxLength = this.state.deviceType === "desktop" ? 5 : 0;
    // ์ตœ๋Œ€ ๊ธธ์ด๋ฅผ ์ดˆ๊ณผํ•˜๋ฉด ๊ฐ€์žฅ ์˜ค๋ž˜๋œ ํ† ์ŠคํŠธ ๋ฉ”์‹œ์ง€๋ฅผ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.
    if (this.state.toasts.length > maxLength) this.state.toasts.shift();

    // ๋žœ๋คํ•œ ID๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
    const randIndex = Math.floor(Math.random() * 1000);

    // ์ƒˆ๋กœ์šด ํ† ์ŠคํŠธ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐฐ์—ด์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
    this.state.toasts.push({
      content: content,
      id: randIndex,
      visible: false,
      type: type,
    });

    // ํ† ์ŠคํŠธ ๋ฉ”์‹œ์ง€๊ฐ€ ๋ณด์ด๋„๋ก ์„ค์ •ํ•˜๋Š” ํƒ€์ด๋จธ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
    setTimeout(() => {
      toastStore.visibleToast(randIndex);
    }, 1);

    // ํ† ์ŠคํŠธ ๋ฉ”์‹œ์ง€๋ฅผ ์ˆจ๊ธฐ๋Š” ํƒ€์ด๋จธ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
    setTimeout(() => {
      toastStore.unVisibleToast(randIndex);
    }, 4000);

    // ํ† ์ŠคํŠธ ๋ฉ”์‹œ์ง€๋ฅผ ์‚ญ์ œํ•˜๋Š” ํƒ€์ด๋จธ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
    setTimeout(() => {
      toastStore.deleteToast(randIndex);
    }, 5000);
  },

  // ๋””๋ฐ”์ด์Šค ํƒ€์ž…์„ ์„ค์ •ํ•˜๋Š” ๋ฉ”์„œ๋“œ์ž…๋‹ˆ๋‹ค.
  setDiviceType(deviceType: "desktop" | "webview" | "mobile") {
    this.state = {
      ...this.state,
      deviceType,
    };
  },

  // ID์— ํ•ด๋‹นํ•˜๋Š” ํ† ์ŠคํŠธ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด์ด๋„๋ก ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๋ฉ”์„œ๋“œ์ž…๋‹ˆ๋‹ค.
  visibleToast(id: number) {
    const toasts = toJS(this.state.toasts);

    const toastIndex = toasts.findIndex((toast) => {
      if (toast?.id === id) {
        return true;
      }
    });
    this.state.toasts[toastIndex].visible = true;

    this.state = {
      ...this.state,
      toasts: [...this.state.toasts],
    };
  },

  // ID์— ํ•ด๋‹นํ•˜๋Š” ํ† ์ŠคํŠธ ๋ฉ”์‹œ์ง€๋ฅผ ์ˆจ๊ธฐ๋„๋ก ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๋ฉ”์„œ๋“œ์ž…๋‹ˆ๋‹ค.
  unVisibleToast(id: number) {
    const toasts = toJS(this.state.toasts);

    const toastIndex = toasts.findIndex((toast) => {
      if (toast?.id === id) {
        return true;
      }
    });
    if (toastIndex !== -1) this.state.toasts[toastIndex].visible = false;
  },

  // ID์— ํ•ด๋‹นํ•˜๋Š” ํ† ์ŠคํŠธ ๋ฉ”์‹œ์ง€๋ฅผ ์‚ญ์ œํ•˜๋Š” ๋ฉ”์„œ๋“œ์ž…๋‹ˆ๋‹ค.
  deleteToast(id: number) {
    const toastIndex = this.state.toasts.findIndex((toast) => {
      if (toast?.id === id) {
        return true;
      }
    });

    if (toastIndex !== -1) this.state.toasts.splice(toastIndex, 1);
  },

  // ๊ณ„์‚ฐ๋œ ๊ฐ’์„ ์ œ๊ณตํ•˜๋Š” getter์ž…๋‹ˆ๋‹ค. ํ˜„์žฌ ํ† ์ŠคํŠธ ๋ฐฐ์—ด์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
  //* computed value
  get toasts(): ToastProps[] {
    return toJS(this.state.toasts);
  },
});

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

// ์‚ฌ์šฉ๋ฐฉ๋ฒ•
// ํŠน์ • ๋น„์ง€๋‹ˆ์Šค ์ฝ”๋“œ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹
const { userStore, toastStore } = useStore();

try {
	// ๋กœ๊ทธ์ธ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋กœ์ง
} catch (error) {
	await logger.error(apiService.get.name, error);
	toastStore.addToast("์ด๋ฉ”์ผ ๋˜๋Š” ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ํ™•์ธํ•ด์ฃผ์„ธ์š”", "error"); // UI๋ฅผ ์—…๋ฐ์ดํŠธ ํ•ฉ๋‹ˆ๋‹ค
	router.push("/login");
}

// Toast.tsx

import React from "react";
import { observer } from "mobx-react";
import useStore from "@hooks/useStore";
import ToastPresenter from "./Toast.presenter";

import type { ToastComponentProps } from "./Toast.type";

const Toast = ({ isWebview }: ToastComponentProps) => {
	const { toastStore } = useStore();

	const deleteToast = (id: number) => {
		toastStore.unVisibleToast(id);

		setTimeout(() => {
			toastStore.deleteToast(id);
		}, 400);
	};

	return (
		<ToastPresenter
			toastList={toastStore.toasts} // toastStore์˜ ๋ฐฐ์—ด์„ ํ™•์ธํ•˜์—ฌ UI๋ฅผ ์—…๋ฐ์ดํŠธ ํ•ฉ๋‹ˆ๋‹ค.
			deleteToast={deleteToast}
			isWebview={isWebview}
		/>
	);
};

export default observer(Toast);

๋‹ค์Œ๊ณผ ๊ฐ™์ด Mobx๋ฅผ ํ†ตํ•ด์„œ Store๋ฅผ ์—…๋ฐ์ดํŠธ ํ•˜๊ณ , Toast ์ปดํฌ๋„ŒํŠธ๋Š” toastStore๊ฐ€ ์—…๋ฐ์ดํŠธ ๋จ์„ ํ™•์ธํ•˜๋ฉฐ UI๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.

์œ„์™€ ๊ฐ™์ด Mobx๋Š” Toast์˜ ์ƒํƒœ๋ฅผ ์ง€์†์ ์œผ๋กœ observable์ด๋ผ๋Š” ๊ด€์ฐฐ ๊ฐ€๋Šฅํ•œ ์ƒํƒœ๋ฅผ ํ†ตํ•ด์„œ ์ปดํฌ๋„ŒํŠธ ์—…๋ฐ์ดํŠธ๋ฅผ ํ†ต์ œํ•˜๋Š”๊ฒƒ์ด ์ €์—๊ฒ ์ต์ˆ™ํ•œ ๊ฐœ๋…์ด์—‡์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์ง๊ด€์ ์œผ๋กœ ์ƒํƒœ๋ณ€๊ฒฝ์„ ์ผ์œผํ‚ค๊ธฐ ์œ„ํ•ด action์„ ์‚ฌ์šฉํ•˜๋Š”๊ฒƒ ๋˜ํ•œ ๋งˆ์Œ์— ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

์œ„์—์„œ๋„ ์ ํ˜€์žˆ๋Š” store.ts์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋“ฏ, state๋ฅผ ์ถ”์ƒํ™”ํ•˜์—ฌ ๊ด€๋ฆฌํ•˜๋Š”๊ฒƒ ๋˜ํ•œ ๊ฐ์ฒด์ง€ํ–ฅ์ ์œผ๋กœ ์ถ”์ƒํ™”๊ฐ€ ์ž˜ ๋˜์–ด ์žˆ๋Š”๋“ฏ ํ–ˆ์Šต๋‹ˆ๋‹ค.

Recoil์— ๋Œ€ํ•œ ๊ฐ„๋‹จํ•œ ํ•™์Šต

Recoil์€ Facebook์—์„œ ๋งŒ๋“  React์šฉ ์ƒํƒœ ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. React์˜ Context API์™€ ์œ ์‚ฌํ•œ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜์ง€๋งŒ, React ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ๋” ํšจ์œจ์ ์ด๊ณ  ์œ ์—ฐํ•˜๊ฒŒ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ์„ค๊ณ„๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ช‡ ๊ฐ€์ง€ ์ฃผ์š” ํŠน์ง•์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  1. Atom: Atom์€ ์ƒํƒœ์˜ ์ผ๋ถ€๋ฅผ ๋‚˜ํƒ€๋‚ด๋ฉฐ, ์ด ์ƒํƒœ๋Š” ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์–ด๋Š ๋ถ€๋ถ„์—์„œ๋‚˜ ์ฝ๊ฑฐ๋‚˜ ์“ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ Atom์€ ๊ณ ์œ ํ•œ ํ‚ค๋ฅผ ๊ฐ€์ง€๋ฉฐ, React ์ปดํฌ๋„ŒํŠธ๋Š” ์ด Atom์„ ๊ตฌ๋…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Atom ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๋ฉด ํ•ด๋‹น Atom์„ ๊ตฌ๋…ํ•˜๋Š” ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง๋ฉ๋‹ˆ๋‹ค.
  2. Selector: Selector๋Š” ํŒŒ์ƒ๋œ ์ƒํƒœ(derived state)์˜ ์ผ๋ถ€๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ์ˆœ์ˆ˜ ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. Atom์ด๋‚˜ ๋‹ค๋ฅธ Selector์˜ ์ƒํƒœ๋ฅผ ์ž…๋ ฅ์œผ๋กœ ๋ฐ›์•„ ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๋ฅผ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค. Selector๋Š” ์บ์‹œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํšจ์œจ์ ์ธ ์ƒํƒœ ํŒŒ์ƒ์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.
  3. ๋น„๋™๊ธฐ ์ฟผ๋ฆฌ: Recoil์€ ๋น„๋™๊ธฐ ์ฟผ๋ฆฌ๋ฅผ ํฌํ•จํ•œ ์ƒํƒœ ๊ด€๋ฆฌ๋ฅผ ์‰ฝ๊ฒŒ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ ํŽ˜์นญ, ๋น„๋™๊ธฐ ๊ณ„์‚ฐ ๋“ฑ์„ ์†์‰ฝ๊ฒŒ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  4. ํ•จ์ˆ˜ํ˜• ์—…๋ฐ์ดํŠธ: React์˜ useState ํ›…๊ณผ ์œ ์‚ฌํ•˜๊ฒŒ, Recoil์€ ํ•จ์ˆ˜ํ˜• ์—…๋ฐ์ดํŠธ๋ฅผ ์ง€์›ํ•˜์—ฌ ์ด์ „ ์ƒํƒœ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ƒˆ ์ƒํƒœ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  5. DevTools: Recoil์€ ๊ฐœ๋ฐœ์ž ๋„๊ตฌ๋ฅผ ์ œ๊ณตํ•˜์—ฌ ์ƒํƒœ ๋ณ€ํ™”๋ฅผ ์‹œ๊ฐ์ ์œผ๋กœ ์ถ”์ ํ•˜๊ณ  ๋””๋ฒ„๊น…ํ•˜๋Š” ๊ฒƒ์„ ๋„์™€์ค๋‹ˆ๋‹ค.

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

์•ž์œผ๋กœ ์ง„ํ–‰ํ•˜๊ฒŒ ๋  recoil์— ๋Œ€ํ•ด์„œ๋„ ๊นŠ์ด ์žˆ๋Š” ํ•™์Šต์„ ์ง„ํ–‰ํ•ด mobx์™€ ๋น„๊ตํ•˜๋ฉฐ ์ „์—ญ์ƒํƒœ๊ด€๋ฆฌ ๋ฐฉ์‹์— ๋Œ€ํ•ด ํ•™์Šต์ด ํ•„์š”ํ•  ๋“ฏ ํ•ฉ๋‹ˆ๋‹ค.

์•„๋ž˜๋Š” ๊ฐ„๋‹จํ•˜๊ฒŒ ํ™•์ธํ•ด๋ณธ recoil ์ฝ”๋“œ์ด๋ฉฐ mobx์™€์˜ ๊ฐ„๋‹จํ•œ ๋น„๊ต๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

// ์ƒํƒœ๊ด€๋ฆฌ state ์ •์˜

// recoil -> ํ† ์ŠคํŠธ ์ƒํƒœ๋ฅผ ์ €์žฅํ•˜๋Š” atom์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.
export const toastState = atom({
  key: 'toastState', // ๊ณ ์œ ํ•œ key
  default: {
    toasts: [],
    deviceType: 'desktop',
  }, // ๊ธฐ๋ณธ ์ƒํƒœ
});

// mobx -> `state` ๊ฐ์ฒด๋ฅผ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—๋Š” `toasts` ๋ฐฐ์—ด๊ณผ `deviceType` ๋ฌธ์ž์—ด์ด ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.
  state: {
    toasts: [],
    deviceType: "desktop",
  } as ToastState,
// ์ƒํƒœ๊ด€๋ฆฌ ๋ฉ”์„œ๋“œ ๊ด€๋ฆฌ
// recoil -> selector๋Š” set ์†์„ฑ์„ ํ†ตํ•ด ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
set: ({ set, get }, action) => {
    const { toasts, deviceType } = get(toastState);

    if (action.type === 'addToast') {
      const maxLength = deviceType === 'desktop' ? 5 : 0;
      if (toasts.length > maxLength) toasts.shift();

      const randIndex = Math.floor(Math.random() * 1000);
      toasts.push({
        content: action.content,
        id: randIndex,
        visible: false,
        type: action.toastType,
      });

      // ๋น„๋™๊ธฐ ์•ก์…˜์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด setTimeout์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
      setTimeout(() => setVisibleToast(set, randIndex), 1);
      setTimeout(() => setUnVisibleToast(set, randIndex), 4000);
      setTimeout(() => deleteToast(set, randIndex), 5000);
    }

    set(toastState, { toasts, deviceType });
  },

// mobx -> ๋งค์„œ๋“œ ํ˜•์‹์œผ๋กœ addToast ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.
 addToast(content: string, type: "success" | "warning" | "info" | "error") {
    // ๋””๋ฐ”์ด์Šค ํƒ€์ž…์— ๋”ฐ๋ผ ์ตœ๋Œ€ ํ† ์ŠคํŠธ ๋ฉ”์‹œ์ง€ ๊ธธ์ด๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
    const maxLength = this.state.deviceType === "desktop" ? 5 : 0;
    // ์ตœ๋Œ€ ๊ธธ์ด๋ฅผ ์ดˆ๊ณผํ•˜๋ฉด ๊ฐ€์žฅ ์˜ค๋ž˜๋œ ํ† ์ŠคํŠธ ๋ฉ”์‹œ์ง€๋ฅผ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.
    if (this.state.toasts.length > maxLength) this.state.toasts.shift();

    // ๋žœ๋คํ•œ ID๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
    const randIndex = Math.floor(Math.random() * 1000);

    // ์ƒˆ๋กœ์šด ํ† ์ŠคํŠธ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐฐ์—ด์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
    this.state.toasts.push({
      content: content,
      id: randIndex,
      visible: false,
      type: type,
    });

    // ํ† ์ŠคํŠธ ๋ฉ”์‹œ์ง€๊ฐ€ ๋ณด์ด๋„๋ก ์„ค์ •ํ•˜๋Š” ํƒ€์ด๋จธ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
    setTimeout(() => {
      toastStore.visibleToast(randIndex);
    }, 1);

    // ํ† ์ŠคํŠธ ๋ฉ”์‹œ์ง€๋ฅผ ์ˆจ๊ธฐ๋Š” ํƒ€์ด๋จธ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
    setTimeout(() => {
      toastStore.unVisibleToast(randIndex);
    }, 4000);

    // ํ† ์ŠคํŠธ ๋ฉ”์‹œ์ง€๋ฅผ ์‚ญ์ œํ•˜๋Š” ํƒ€์ด๋จธ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
    setTimeout(() => {
      toastStore.deleteToast(randIndex);
    }, 5000);
  },

๋งบ์Œ๋ง

์‚ฌ์‹ค โ€œ์™œ recoil์„ ์‚ฌ์šฉํ•ด์•ผ๋งŒ ํ–ˆ์–ดโ€ ๋ผ๋Š” ์งˆ๋ฌธ์—๋Š” ์•„์ง ์™„๋ฒฝํ•œ ๋Œ€๋‹ต์„ ํ•˜์ง€ ๋ชปํ• ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์ €๋Š” ์ง€๊ธˆ๊นŒ์ง€ mobx์— ์ต์ˆ™ํ•œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์™”๊ณ , recoil ์ฝ”๋“œ๋ฅผ ์œ„์™€ ๊ฐ™์ด ์ž‘์„ฑํ•ด๋ณด์•˜์ง€๋งŒ, ์•„์ง mobx ์‚ฌ๊ณ ์—์„œ ๋ฒ—์–ด๋‚˜์ง€ ๋ชปํ•œ๋“ฏ ํ•ฉ๋‹ˆ๋‹ค.

์•ž์œผ๋กœ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด๊ฐ€๋ฉฐ recoil์„ ํ†ตํ•œ ๊ด€๋ฆฌ ๋ฐฉ์‹์˜ ์ฒ ํ•™์„ ์ž˜ ์ดํ•ดํ•˜๊ณ  ์ •๋ฆฌํ•˜๋ฉฐ ์ƒˆ๋กญ๊ฒŒ ํ•™์Šตํ•˜๋Š”๊ฒƒ ๋˜ํ•œ ์˜๋ฏธ ์žˆ๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

ยฉ 2024 Adultlee. All rights reserved.Made with โค by ์ด์„ฑ์ธ