ํ•จ์ˆ˜ํ˜• ํ”„๋ก ํŠธ์—”๋“œ์—์„œ ์˜์กด์„ฑ ์ œ์–ดํ•˜๊ธฐ

taehee
  • #ํ”„๋ก ํŠธ์—”๋“œ
  • #์˜์กด์„ฑ
  • #ํ•จ์ˆ˜ํ˜•
  • #React

์•ˆ๋…•ํ•˜์„ธ์š”. ํ”„๋กฌ ํ”„๋ก ํŠธ์—”๋“œ ํŒ€์—์„œ ํ…Œ์ŠคํŠธ์™€ ์ด๊ฒƒ์ €๊ฒƒ ์ „๋„ํ•˜๋Š” ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž ๊น€ํƒœํฌ์ž…๋‹ˆ๋‹ค.

์ œ๊ฐ€ 4์›”์— ๋“ค์–ด์™”๋Š”๋ฐ. ์ด์ œ 2023๋…„์ด ๋๋‚˜๊ณ  2024๋…„์ด ๋‹ค๊ฐ€์˜ค๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ œ๊ฐ€ ๊ณผ๊ฑฐ์— ์งฐ๋˜ ์ฝ”๋“œ๋Š” ๋ ˆ๊ฑฐ์‹œ๊ฐ€ ๋˜์–ด๊ฐ€๊ณ ์š”. ํ•˜๋‚˜ ๋‘˜ ์ถ”๊ฐ€ํ–ˆ๋˜ ๊ธฐ๋Šฅ์€ ๋ณต์žกํ•˜๊ณ  ๊ฑฐ๋Œ€ํ•œ ๋กœ์ง์ด ๋˜๊ธฐ๋„ ํ–ˆ์Šต๋‹ˆ๋‹ค. ์„œ๋ฒ„/๋ธŒ๋ผ์šฐ์ €/webview ๊ฐ™์€ ๋‹ค์–‘ํ•œ ํ™˜๊ฒฝ๊ณผ, react์™€ qwik ๊ฐ™์ด ์—ฌ๋Ÿฌ ํ”„๋ ˆ์ž„์›Œํฌ ๋“ฑ์„ ์ง€์›ํ•˜๋ ค ํ•˜๋ฉด์„œ, ์˜์กด์„ฑ์— ๋Œ€ํ•ด ์—ฌ๋Ÿฌ๋ชจ๋กœ ๊ณ ๋ฏผ์„ ํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

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

๊ทธ๋Ÿฌ๋ฉด ์–ด๋–ป๊ฒŒ ํ•˜๋ฉด ์˜์กด์„ฑ์„ ๋” ์ž˜ ์ œ์–ดํ•ด์„œ ๋” ์ดํ•ดํ•˜๊ธฐ ์‰ฝ๊ณ , ๋ณ€๊ฒฝํ•˜๊ธฐ ์‰ฌ์šฐ๋ฉฐ, ์•ˆ์ „ํ•œ ์ฝ”๋“œ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์„์ง€ ๊ฐ™์ด ๊ณ ๋ฏผํ•ด๋ณด์‹œ์ฃ .

์˜์กด์„ฑ์— ๋Œ€ํ•˜์—ฌ

์ฝ”์นญ์„ ํ•˜๊ณ  ํŒ€์› ๋ถ„๋“ค๊ณผ ์ด์•ผ๊ธฐ๋ฅผ ํ•˜๋‹ค๋ณด๋ฉด ์˜์กด์„ฑ์— ๋Œ€ํ•ด ๋‹ค๋“ค ๋‹ค๋ฅธ ์ƒ๊ฐ์„ ๊ฐ€์ง€๊ณ  ๊ณ„์‹ญ๋‹ˆ๋‹ค. ๋ช…ํ™•ํ•˜์ง€ ์•Š๊ณ  ์ด๊ฒƒ์ €๊ฒƒ ์ƒ๊ฐ์ด ํ˜ผ์žฌ๋œ ๊ฒฝ์šฐ๋„ ๋งŽ๊ณ ์š”. ์ €๋Š” ์ด๋ฏธ ๊ฐœ์ธ ๋ธ”๋กœ๊ทธ์— ํ”„๋ŸฐํŠธ์—”๋“œ์—์„œ ์˜์กด์„ฑ์„ ์ œ์–ดํ•˜๋Š” ๋ฒ•์ด๋ผ๋Š” ๊ธ€์„ ์“ด ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค๋งŒ.

์ž ์‹œ ๊ทธ๋•Œ ํ–ˆ๋˜ ์ด์•ผ๊ธฐ๋ฅผ ์ •๋ฆฌํ•ด๋ณด๊ณ , ์˜์กด์„ฑ์˜ ๊ฐœ๋…์„ ์žก๊ณ  ์‹œ์ž‘ํ•ด๋ณด๋ ค ํ•ฉ๋‹ˆ๋‹ค.

์ผ๋‹จ ์˜์กด์„ฑ์ด๋ผ ํ•˜๋ฉด js๊ฐœ๋ฐœ์ž์—๊ฒŒ๋Š” package.json์—์„œ dependencies๋ผ ๋ถ€๋ฅด๋Š” ์™ธ๋ถ€ ํŒจํ‚ค์ง€๋“ค์ด ๋– ์˜ค๋ฅด์‹ค ๊ฒ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์ž๋ฉด react๋‚˜ next๋ถ€ํ„ฐ jotai๋‚˜ react-query ๊ฐ™์€ ์ƒํƒœ๊ด€๋ฆฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋‚˜, styled-component๋‚˜ tailwind ๊ฐ™์€ ์Šคํƒ€์ผ๋ง ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋“ฑ์ด ์žˆ๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฐ ์™ธ๋ถ€ ์˜์กด์„ฑ์€ ์ง์ ‘ ๋ฐ”ํ€ด๋ฅผ ์žฌ๋ฐœ๋ช…ํ•  ํ•„์š” ์—†์ด, ๋น ๋ฅด๊ฒŒ ์ƒˆ ๊ธฐ๋Šฅ์„ ๊ฐœ๋ฐœํ•˜๊ฒŒ ๋„์™€์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ๋งŽ์ด๋“ค ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

์™ธ๋ถ€ ์˜์กด์„ฑ์ด ์ „๋ถ€๋Š” ์•„๋‹ˆ์ง€๋งŒ, ์ผ๋‹จ ์™ธ๋ถ€ ์˜์กด์„ฑ์— ์ง‘์ค‘ํ•ด๋ณด์ฃ . ๋จผ์ € ๊ณต๊ฐœ ์ธํ„ฐํŽ˜์ด์Šค์™€ ๋‚ด๋ถ€ ๊ตฌํ˜„์ด ๋ฌด์—‡์ธ์ง€ ์‚ดํŽด๋ด…์‹œ๋‹ค.

์ธํ„ฐํŽ˜์ด์Šค vs ๋‚ด๋ถ€ ๊ตฌํ˜„, ์ œ๊ณต์ž vs ์‚ฌ์šฉ์ž

์ด๋Ÿฐ ์˜์กด์„ฑ ์ œ๊ณต์ž๋“ค์€ ์‚ฌ์šฉ์ž๋“ค์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” api๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. api๋ผ ํ•˜๋ฉด http ์„œ๋ฒ„์™€ ํ†ต์‹ ํ•˜๋Š” ๊ฒƒ๋งŒ ์ƒ๊ฐํ•˜์‹œ๋Š” ๊ฒฝ์šฐ๋„ ์žˆ๋Š”๋ฐ์š”. ๋” ๋„“๊ฒŒ ์ƒ๊ฐํ•˜๋ฉด ์ด๋Ÿฐ ๊ฒƒ๋„ ๋‹ค ๊ณต๊ฐœ ์ธํ„ฐํŽ˜์ด์Šค์ž…๋‹ˆ๋‹ค. java ๊ฐ™์€ ์–ธ์–ด์—์„œ๋Š” public ์ ‘๊ทผ ์ œ์–ด์ž๋กœ, golang ๊ฐ™์€ ์–ธ์–ด์—์„œ๋Š” ์ฒซ ๊ธ€์ž๋ฅผ ๋Œ€๋ฌธ์ž๋กœ ์„ค์ •ํ•˜๊ธฐ๋„ ํ•˜๋Š”๋ฐ์š”. javascript ๋ชจ๋“ˆ์—์„œ๋Š” export ํ•œ ๊ฒƒ๋“ค์ด ๊ณต๊ฐœ ์ธํ„ฐํŽ˜์ด์Šค์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ์˜์กด์„ฑ ์‚ฌ์šฉ์ž๋“ค์€ importํ•ด์„œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด ๋‹ค์Œ ์ฝ”๋“œ์—์„œ๋Š” jotai๊ฐ€ ์ œ๊ณตํ•˜๋Š” atom์œผ๋กœ cartAtom์„ ๋งŒ๋“ค๊ณ ์š”. useAtomValue๋กœ atom์— ํ•ด๋‹นํ•˜๋Š” ์ƒํƒœ๋ฅผ store์—์„œ ๊บผ๋‚ด์„œ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

// atom/cart.ts
import { atom } from 'jotai';

import { CartVo } from 'vo/shared/Cart.vo';

const cartAtom = atom<CartVo | undefined>(undefined);

export { cartAtom }

// pages/index.tsx
import { useAtomValue } from 'jotai';

import { cartAtom } from 'atom/cart';

function Page(){
    const cart = useAtomValue(cartAtom)
    // ...
}

์ด๋ ‡๊ฒŒ ์ƒ๊ฐํ•˜๋ฉด ์ €ํฌ๊ฐ€ ์ง์ ‘ ๋งŒ๋“  ์˜์กด์„ฑ๋„ ๋น„์Šทํ•˜๊ฒŒ ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด atom/cart.ts ๋ชจ๋“ˆ์€ ์™ธ๋ถ€์— cartAtom์„ ๊ณต๊ฐœํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. pages/index.tsx ๋ชจ๋“ˆ์—์„œ๋Š” ์ด๋Ÿฐ cartAtom์„ ๊ฐ€์ ธ๋‹ค๊ฐ€, jotai์˜ useAtomValue์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์ฃ .

๋ฆฌํŒฉํ„ฐ๋ง vs ํŒŒ๊ดด์  ๋ณ€๊ฒฝ

์ธํ„ฐํŽ˜์ด์Šค์™€ ๋™์ž‘์„ ์œ ์ง€ํ•˜๋ฉด์„œ ๋‚ด๋ถ€ ๊ตฌํ˜„๋งŒ ๋ฐ”๊พธ๋ฉด ๋ฆฌํŒฉํ„ฐ๋ง์ด๋ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ตฌํ˜„์„ ๋ฐ”๊พธ๋ฉด์„œ ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ๋ฐ”๋€Œ๊ฑฐ๋‚˜, ๊ฒ‰๋ณด๊ธฐ ๋™์ž‘์ด ๊นจ์ง€๋ฉด ํŒŒ๊ดด์  ๋ณ€๊ฒฝ์ด๋ผ ํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด ๋ฆฌ์•กํŠธ ์ฟผ๋ฆฌ๋ฅผ ์ด์šฉํ•œ ๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ๋ด์ฃผ์‹œ์ฃ . ๋ธŒ๋žœ๋“œ์˜ ๊ฒฝ๋กœ๋ฅผ ๋ฐ›์•„์„œ, ํ•ด๋‹นํ•˜๋Š” ๋ธŒ๋žœ๋“œ์˜ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋กœ์ง์ธ๋ฐ์š”. react query v3์—์„œ๋Š” cache key์™€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋น„๋™๊ธฐ ํ•จ์ˆ˜, options๋ฅผ 3๊ฐœ์˜ ์ธ์ž๋กœ ๋ฐ›๊ฒŒ ํ–ˆ์Šต๋‹ˆ๋‹ค.

import { getBrand } from 'api/brand';
import useOnApiError from 'hooks/useOnApiError';
import { useSuspendedQuery } from 'hooks/useQuery';

const useGetBrand = (brandPath: string) => {
    const onApiError = useOnApiError();

    return useQuery(
        ['getBrand', brandPath], // key
        (signal) => getBrand(brandPath, signal), // data๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋น„๋™๊ธฐ ํ•จ์ˆ˜
        { onError: onApiError } // options
    );
};

export default useGetBrand;

semantic version์„ ์ค€์ˆ˜ํ•˜๋Š” ์˜์กด์„ฑ ์ œ๊ณต์ž๋“ค์€ minor ๋ฒ„์ „ ์—…๋ฐ์ดํŠธ์—์„œ๋Š” ๋ณดํ†ต ์ธํ„ฐํŽ˜์ด์Šค ํ•˜์œ„ ํ˜ธํ™˜์„ ์ง€ํ‚ค๋ ค ๋…ธ๋ ฅํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด 3.1์—์„œ 3.2๋กœ ์˜ฌ๋ผ๊ฐ€๋ฉด์„œ ์„ฑ๋Šฅ ์ตœ์ ํ™”๋ฅผ ์œ„ํ•ด ์—ฌ๋Ÿฌ ๋‚ด๋ถ€ ๊ตฌํ˜„์„ ๊ฐœ์„ ํ–ˆ๋‹ค๊ณ  ํ•ด๋ด…์‹œ๋‹ค. ๊ทธ๋ž˜์„œ ์ธํ„ฐํŽ˜์ด์Šค๋Š” ๋ณ€ํ•œ ๊ฒŒ ์—†๊ธฐ ๋•Œ๋ฌธ์—, ์˜์กด์„ฑ์„ ์‚ฌ์šฉํ•˜๋Š” ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ๋ฒ„์ „์„ ์˜ฌ๋ ค๋„ ์•„๋ฌด ๊ฒƒ๋„ ๊นจ์ง€์ง€ ์•Š์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. (๋ฌผ๋ก  ์„ฑ๋Šฅ์€ ์ข‹์•„์ง€๊ฒ ์ฃ .)

์ด๋ ‡๊ฒŒ ์™ธ๋ถ€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์œ ์ง€ํ•˜๋ฉด์„œ, ๋‚ด๋ถ€ ๊ตฌํ˜„์„ ๊ฐœ์„ ํ•˜๋Š” ๊ฒƒ๋งŒ ๋ฆฌํŒฉํ„ฐ๋ง์ด๋ผ ํ•ฉ๋‹ˆ๋‹ค. ์ฝ”๋“œ ๊ตฌ์กฐ๋ฅผ ๊ฐˆ์•„ ์—Ž์œผ๋ฉด ๋‹ค ๋ฆฌํŒฉํ„ฐ๋ง์ด๋ผ๊ณ  ์ž˜๋ชป ์“ฐ์ด๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์ง€๋งŒ์š”.

์˜์กด์„ฑ ์ œ๊ณต์ž๋“ค์€ ๋ฉ”์ด์ € ๋ฒ„์ „์„ ์˜ฌ๋ฆด ๋•Œ ํŒŒ๊ดด์  ๋ณ€๊ฒฝ์„ ๋„์ž…ํ•˜๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด react-query๋ฅผ ์“ฐ๋‹ค๋ณด๋ฉด useQuery ๋ง๊ณ ๋„ prefetch๋‚˜ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์— key, ๋น„๋™๊ธฐ ํ•จ์ˆ˜, options๋ฅผ ๋ณต๋ถ™ํ•˜๊ฒŒ ๋  ๋•Œ๊ฐ€ ๋งŽ์€๋ฐ์š”. ๊ทธ๋ž˜์„œ react-query v5์—์„œ๋Š” ์ € ์„ธ ๊ฐ€์ง€ ์ธ์ž๋ฅผ ํ•˜๋‚˜์˜ ๊ฐ์ฒด๋กœ ๋ฐ›๋„๋ก ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋ฌผ๋ก  ์•ฝ๊ฐ„ ๋” ์žฅํ™ฉํ•ด์ง€๊ธด ํ–ˆ์ง€๋งŒ, query ๊ฐ์ฒด๋ฅผ ํ•˜๋‚˜ ๋งŒ๋“ค์–ด๋‘๋ฉด ์ด๊ณณ์ €๊ณณ ๊ณต์œ ํ•ด์„œ ์“ธ ์ˆ˜ ์žˆ์–ด์„œ ํŽธ๋ฆฌํ•ด์กŒ์ฃ .

ํ•˜์ง€๋งŒ ์ด๋Š” ํŒŒ๊ดด์  ๋ณ€๊ฒฝ์ž…๋‹ˆ๋‹ค! version 5๋กœ ์˜ฌ๋ผ๊ฐ€๋ ค๋ฉด ์ƒˆ๋กœ์šด ์ธํ„ฐํŽ˜์ด์Šค์— ๋งž์ถฐ์„œ, ๊ธฐ์กด ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ชจ๋“  ๋ ˆ๊ฑฐ์‹œ ์ฝ”๋“œ๋ฅผ ๋œฏ์–ด๊ณ ์ณ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฐ ๊ณณ์ด 28๊ณณ์ด๋ผ๋ฉด? 100๊ณณ์ด๋ผ๋ฉด? 1000๊ณณ์ด๋ผ๋ฉด?

๊ทธ๋ฆฌ๊ณ  ๋ณ€์ˆ˜๋ช…์ด๋‚˜ ๋งค๊ฐœ๋ณ€์ˆ˜ ๋ชจ์–‘์ด ๋ฐ”๋€Œ๋Š” ์ •๋„๊ฐ€ ์•„๋‹ˆ๋ผ ํŒจ๋Ÿฌ๋‹ค์ž„์ด ๋ฐ”๋€Œ๋Š” ์ˆ˜์ค€์˜ ํฐ ํŒŒ๊ดด์  ๋ณ€๊ฒฝ๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

  • react๊ฐ€ class component์™€ life cycle์—์„œ hook์œผ๋กœ ๊ฐˆ์•„ํƒ€๋ฉด์„œ ๋ชจ๋“  ์ƒํƒœ๊ณ„๊ฐ€ ๋’ค์ง‘ํ˜”์Šต๋‹ˆ๋‹ค
  • react์— server component๊ฐ€ ๋„์ž…๋˜๋ฉด์„œ styled๋‚˜ emotion ๋“ฑ์˜ ๊ธฐ์กด css in js ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ๋™์ž‘ํ•˜์ง€ ์•Š๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค
  • react-query๋Š” v5๋ถ€ํ„ฐ useQuery์— onSuccess๋‚˜ onError ๋“ฑ์˜ handler๋ฅผ ์ œ๊ณตํ•˜์ง€ ์•Š๊ธฐ๋กœ ํ–ˆ์Šต๋‹ˆ๋‹ค

์‹คํ–‰ ํ™˜๊ฒฝ๊ณผ ํ‘œ์ค€, ์–ด๋Œ‘ํ„ฐ, ํด๋ฆฌํ•„

์ €ํฌ๋Š” ๋‹ค์–‘ํ•œ ์‹คํ–‰ ํ™˜๊ฒฝ(runtime)์—์„œ ์ฝ”๋“œ๋ฅผ ๋Œ๋ฆฝ๋‹ˆ๋‹ค. ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋Š” ์›๋ž˜ ๊ณผ๊ฑฐ Netscape ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋Œ์•„๊ฐ€๊ธฐ ์œ„ํ•ด ๋งŒ๋“ค์–ด์ง„ ์–ธ์–ด์˜€์Šต๋‹ˆ๋‹ค. ์ธํ„ฐ๋„ท ์ต์Šคํ”Œ๋กœ๋Ÿฌ๊ฐ€ ์„ธ์ƒ์„ ์ง€๋ฐฐํ•˜๊ณ  ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋ฒ ๊ปด์„œ JScript๋ฅผ ๋งŒ๋“ค๊ธฐ๋„ ํ•˜๊ณ ์š”. ๊ทธ ํ›„์— ๋‚˜์˜จ ํฌ๋กฌ์ด๋‚˜ ํŒŒ์ด์–ดํญ์Šค ๋“ฑ ๋‹ค์–‘ํ•œ ๋ธŒ๋ผ์šฐ์ €๋“ค๋„ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ง€์›ํ–ˆ์Šต๋‹ˆ๋‹ค๋งŒ. ํ‘œ์ค€์ด ์žˆ์–ด๋„ ๋งˆ์ดํฌ๋กœ์†Œํ”„ํŠธ๊ฐ€ ๋ฌด์‹œํ–ˆ๊ณ  ์ด๋Ÿฐ์ €๋Ÿฐ ๋ณต์žกํ•œ ์—ญ์‚ฌ๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

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

์ด๋Ÿฐ ์‹คํ–‰ ํ™˜๊ฒฝ๋“ค์€ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ฝ”๋“œ๋ฅผ ํ•œ ์ค„๋„ ๊ณ ์น˜์ง€ ์•Š์•„๋„ ์•Œ์•„์„œ ๋ณ€ํ™”ํ•ฉ๋‹ˆ๋‹ค. ์‚ฌํŒŒ๋ฆฌ 16์—์„œ๋Š” ์ž˜ ๋Œ๋˜ ์ฝ”๋“œ๊ฐ€ ์‚ฌํŒŒ๋ฆฌ 17์—์„œ๋Š” ๋ณด์•ˆ์ด ๊ฐ•ํ™”๋˜๋ฉด์„œ ๊นจ์ง€๊ธฐ๋„ ํ•˜๊ณ ์š”. ์‚ฌํŒŒ๋ฆฌ 17์— ๋‚˜์˜จ ๊ธฐ๋Šฅ์„ ์จ๋ณด๋ ค ํ•ด๋„ ์‚ฌํŒŒ๋ฆฌ 14๋ฅผ ์“ฐ๋Š” ์‚ฌ์šฉ์ž๋“ค์„ ์ง€์›ํ•  ์ˆ˜ ์—†์–ด์„œ ํฌ๊ธฐํ•˜๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค. ์•ˆ๋“œ๋กœ์ด๋“œ๋‚˜ ios๋„ ์ƒˆ๋กœ ๋ฒ„์ „์—…์„ ํ•˜๊ณ , js๋„ ๊ณ„์†ํ•ด์„œ ES6, ES2023, ES2024๊ฐ€ ๋‚˜์˜ต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ์–ด๋–ค ์˜์กด์„ฑ์˜ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋‹ค๋ฅธ ์˜์กด์„ฑ์˜ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๋Š” ์–ด๋Œ‘ํ„ฐ๋“ค์ด ๋‚˜์˜ต๋‹ˆ๋‹ค. Java๋Š” ๋˜‘๊ฐ™์€ ์ฝ”๋“œ๊ฐ€ window, linux, mac์—์„œ ๋ชจ๋‘ ๋Œ์•„๊ฐˆ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์คฌ์ฃ . JQuery๋Š” ๋น„์ผ๊ด€์ ์ธ ๋ธŒ๋ผ์šฐ์ € js ์‚ฌ์ด์—์„œ ์ผ๊ด€๋œ ๋ฐฉ์‹์œผ๋กœ dom์„ ์กฐ์ž‘ํ•  ์ˆ˜ ์žˆ๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณตํ–ˆ์Šต๋‹ˆ๋‹ค.

ํ•œํŽธ ์‹  ๋ฒ„์ „์˜ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌ ๋ฒ„์ „์˜ ๊ธฐ๋Šฅ๋งŒ์œผ๋กœ ๋น„์Šทํ•˜๊ฒŒ ๊ตฌํ˜„ํ•ด์ฃผ๋Š” ํด๋ฆฌํ•„๋„ ๋งŽ์ด๋“ค ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด core-js์™€ babel/preset-env๋Š” browserlist์˜ ์ •๋ณด๋ฅผ ์ด์šฉํ•ด์„œ ์•Œ์•„์„œ ๊ตฌ๋ฒ„์ „์˜ js๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๊ฑฐ๋‚˜, ์ง€์›ํ•˜์ง€ ์•Š๋Š” ๋ธŒ๋ผ์šฐ์ € api๋Š” ๋น„์Šทํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์„œ ๋„ฃ์–ด์ค๋‹ˆ๋‹ค. ๋ชจ๋˜ css๋ฅผ ์˜› ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋Œ์•„๊ฐ€๊ฒŒ ํ•ด์ฃผ๋Š” postcss-preset-env ๊ฐ™์€ ๊ฒƒ๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

์ง€๊ธˆ๊นŒ์ง€ ์ •๋ฆฌํ•œ ๊ฐœ๋…๋“ค์„ ํ•œ ๋ฒˆ ๋˜์งš์–ด ๋ณด์‹œ์ฃ . ๋‚ด๊ฐ€ ์ฝ”๋”ฉ์„ ํ•˜๋ฉด์„œ ๊ฒช์—ˆ๋˜ ์‚ฌ๋ก€๋„ ๋– ์˜ฌ๋ ค๋ณด์‹  ๋’ค์— ๊ณ„์† ์ฝ์–ด๋‚˜๊ฐ€์‹œ๋ฉด ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค.

์˜์กด์„ฑ์˜ ํŒŒ๊ดด์  ๋ณ€๊ฒฝ์— ๋Œ€์ฒ˜ํ•˜๊ธฐ

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

ํ•˜์ง€๋งŒ ์ƒํƒœ๊ณ„์™€ ๋Ÿฐํƒ€์ž„์ด ๋ฐœ์ „ํ•˜๋Š” ๊ฑด ๋ง‰์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ œ๊ฐ€ ์•„๋Š” ์–ด๋–ค ํ”„๋กœ์ ํŠธ๋Š” ํŒŒ์ด์ฌ ๋ฒ„์ „์„ ์˜ฌ๋ฆฌ์ง€ ์•Š๊ณ  ๋ฒ„ํ‹ฐ๋‹ค๊ฐ€, ํฌ๋งคํ„ฐ๋‚˜ ๋ฆฐํŠธ ๊ฐ™์€ ๋ชจ๋“  ํˆด๊นŒ์ง€ ์ง€์›์ด ๋Š๊ธฐ๋ฉด์„œ ๋ฒ„์ „์„ ์˜ฌ๋ฆด ์ˆ˜ ๋ฐ–์— ์—†๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. react class component๋กœ ๋œ ๋ ˆ๊ฑฐ์‹œ๋“ค๋„ ๋น„์Šทํ•œ ์‹ ์„ธ์— ์ฒ˜ํ•ด ์žˆ์ฃ .

๊ทธ๋Ÿฌ๋ฉด ์–ด๋–ป๊ฒŒ ํ•˜๋ฉด ์ข‹์„๊นŒ์š”?

์™ธ๋ถ€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ฐ์‹ธ๊ธฐ

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

์ด๋Š” ํŠนํžˆ ์˜์กด์„ฑ์ด ๋ณ€๊ฒฝ๋  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’๊ฑฐ๋‚˜, ๋งŽ์€ ๊ณณ์—์„œ ์‚ฌ์šฉํ•  ์˜ˆ์ •์ด๋ผ๋ฉด, ํ˜น์€ ๋‹ค์–‘ํ•œ ๊ตฌํ˜„์ฒด๋กœ ๋นŒ๋“œํ•  ํ•„์š”๊ฐ€ ์žˆ์„ ๋•Œ์—๋Š” ๊ผญ ์ง€ํ‚ค์‹œ๋ฉด ์ข‹์Šต๋‹ˆ๋‹ค.

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

ํ•˜์ง€๋งŒ react๋‚˜ ํ”„๋ก ํŠธ์—”๋“œ์—์„œ๋Š” class๋‚˜ interface๋ฅผ ์“ฐ์ง€ ์•Š๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค. ํ•จ์ˆ˜์—์„œ๋Š” ์–ด๋–ป๊ฒŒ ํ•˜๋ฉด ์ข‹์„๊นŒ์š”?

๋ฆฌ์•กํŠธ์—์„œ๋Š” ๋ณดํ†ต ์ปค์Šคํ…€ ํ›…์ด๋‚˜ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ด์šฉํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์œ„์—์„œ ๋ณธ cartAtom์€ ๋‹ค์Œ์ฒ˜๋Ÿผ useCart๋กœ ๊ฐ์Œ€ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// atom/cart.ts
import { atom, useAtom } from 'jotai';

import { CartVo } from 'vo/shared/Cart.vo';

const cartAtom = atom<CartVo | undefined>(undefined);

function useCart(){
    return useAtom(cartAtom)
}

export { cartAtom, useCart }

// pages/index.tsx
import { useCart } from 'atom/cart';

function Page(){
    const [cart] = useCart()
    // ...
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด atom/cart๋งŒ jotai์— ์˜์กดํ•˜๊ณ ์š”, pages/index.tsx ๋Š” jotai์˜ ์กด์žฌ๋ฅผ ๋ชจ๋ฅด๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ํ˜น์‹œ๋‚˜ jotai ๋ง๊ณ  react-query๋‚˜ ๋‹ค๋ฅธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ๊ฐˆ์•„ํƒ€๊ฒŒ ๋˜๋”๋ผ๋„ useCart์˜ ๊ตฌํ˜„๋งŒ ์ˆ˜์ •ํ•ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

ํ•œํŽธ ๊ธฐ์กด์— useQuery๋ฅผ useQueryRequest๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ ์ด๋ฏธ ๊ฐ์‹ธ์„œ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ์•ž์„œ ๋งํ•œ ๋ถˆํŽธํ•จ๋„ ์žˆ๊ณ , cacheKey์— ์–ธ์–ด๋‚˜ ํ†ตํ™” ๊ฐ™์€ ์ž์ฃผ ๊ฒน์น˜๋Š” ์š”์†Œ๋ฅผ ๋งค๋ฒˆ ๋„ฃ์–ด์ฃผ๊ธฐ๊ฐ€ ๋ฒˆ๊ฑฐ๋กœ์›Œ์„œ ๋งŒ๋“  ๊ฒƒ์ด์—ˆ๋Š”๋ฐ์š”. ์ด๋ฒˆ์— react-query v5๋กœ ์˜ฌ๋ผ๊ฐ€๋ฉด์„œ ํŒŒ๊ดด์  ๋ณ€๊ฒฝ์ด ์ƒ๋‹นํ–ˆ์ง€๋งŒ, useQueryRequest๋Š” ์ด๋ฏธ ๊ฐ์ฒด ํ•˜๋‚˜๋งŒ ๋ฐ›๋„๋ก ํ•ด๋‘์–ด์„œ ๋‚ด๋ถ€ ๊ตฌํ˜„๋งŒ ๊ณ ์ณ์ฃผ๋ฉด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

์™ธ๋ถ€ ์˜์กด์„ฑ์„ ์œ ์—ฐํ•˜๊ฒŒ migrationํ•˜๊ธฐ

์˜์กด์„ฑ์„ ์‚ฌ์šฉํ•˜๋‹ค๋ณด๋ฉด ์šฐ๋ฆฌ๊ฐ€ ์ง€์›ํ•ด์•ผํ•˜๋Š” ํ™˜๊ฒฝ์—์„œ๋Š” ๋™์ž‘ํ•˜์ง€ ์•Š์•„์„œ, ์ด์‚ฌ(migration)๋ฅผ ๊ฐ€์•ผํ•  ๋•Œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

์ €ํฌ๋Š” ์‹ค์ œ๋กœ jotai์˜ SSR ๋™์ž‘์ด ์ €ํฌ๊ฐ€ ๊ธฐ๋Œ€ํ•˜๋Š” ๊ฒƒ๊ณผ ๋‹ฌ๋ผ์„œ currencyAtom์„ cookie์™€ useSyncExtenralStore๋กœ migration ํ•œ ์ ์ด ์žˆ์—ˆ๋Š”๋ฐ์š”. ์Šฌํ”„๊ฒŒ๋„ useAtom(currencyAtom)์„ ๊ณณ๊ณณ์—์„œ ์“ฐ๊ณ  ์žˆ์–ด์„œ, ๋ชจ๋“  ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.

๋˜ ๋‹ค๋ฅธ ์˜ˆ์‹œ๋กœ tailwind๋‚˜ styled component ๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ๋””์ž์ธ ์‹œ์Šคํ…œ์ด๋‚˜ ๊ณต์šฉ ์ปดํฌ๋„ŒํŠธ๋กœ ํ•œ ๋ฒˆ ๊ฐ์‹ธ๋‘๋ฉด ์ข‹์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌ๋ฉด ์‚ฌ์šฉํ•˜๊ธฐ์—๋„ ํŽธ๋ฆฌํ•˜๊ณ ์š”. styled-component ๋“ฑ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” server-component ๋“ฑ์œผ๋กœ migration์„ ํ•  ๋•Œ ๊ณต์šฉ ์ปดํฌ๋„ŒํŠธ์˜ ์Šคํƒ€์ผ๋งŒ ๋˜‘๊ฐ™์ด ๋งŒ๋“ค์–ด์ฃผ๋ฉด ๋˜๊ฑฐ๋“ ์š”. ํ”„๋กœ์ ํŠธ ์ „๋ฐ˜์— ๋ณต์‚ฌ ๋ถ™์—ฌ๋„ฃ๊ธฐํ•œ ์Šคํƒ€์ผ์ด ํผ์ ธ์žˆ๋‹ค๋ฉด, ํ•œ ์Šคํƒ€์ผ์—์„œ ๋‹ค๋ฅธ ์Šคํƒ€์ผ๋กœ ๋ฐ”๊พธ๊ธฐ๊ฐ€ ๋งค์šฐ ์–ด๋ ค์šธ ๊ฒ๋‹ˆ๋‹ค.

๋น„์Šทํ•˜๊ฒŒ react-router-dom์„ ์‚ฌ์šฉํ•˜๋˜ ๋ถ„๋“ค์€ next/router๋กœ ๊ฐˆ์•„ํƒ€๋ฉด์„œ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๊ณ ํ†ต์„ ๊ฒช๊ฒŒ ๋˜์‹ค ๊ฒ๋‹ˆ๋‹ค. Link๋‚˜ useRouter ๊ฐ™์€ ๊ฑธ ํ•œ ๋ฒˆ ๊ฐ์‹ธ๋‘์—ˆ๋‹ค๋ฉดโ€ฆ <Link to="..." ๊ฐ€ <Link href="..." ๋กœ ๋ฐ”๋€Œ๋Š” ์‹์˜ ์‚ฌ์†Œํ•œ ์ฐจ์ด์— ๋” ์‰ฝ๊ฒŒ ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ์„ ๊ฒ๋‹ˆ๋‹ค. storybook์ด๋‚˜ test ํ™˜๊ฒฝ์ฒ˜๋Ÿผ Next router๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” ํ™˜๊ฒฝ์—์„œ๋„ next์˜ ๋‚ด๋ถ€ ๊ตฌํ˜„๊ณผ ์”จ๋ฆ„ํ•  ํ•„์š” ์—†์ด, ๊ตฌํ˜„์ฒด๋งŒ ๋ฐ”๊พธ๋ฉด ๋˜์ง€์š”.

์ด๋Ÿฐ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋งŒ๋“ค ๋•Œ์—๋Š” ํ‘œ์ค€์— ๋Œ€ํ•ด ๊นŠ์ด ์ƒ๊ฐํ•ด๋ณด์‹œ๋ฉด ์ข‹์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด router๋ฅผ ์ถ”์ƒํ™”ํ•˜๋ ค๋ฉด โ€˜react-router-domโ€™, โ€˜nextโ€™, โ€˜stackflowโ€™ ๋“ฑ, ์šฐ๋ฆฌ๊ฐ€ migrationํ•  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์€ ๊ตฌํ˜„์ฒด๋“ค์„ ์‚ดํŽด๋ณด์‹œ๊ณ  ๊ณตํ†ต์ ์€ ๋ฌด์—‡์ด๊ณ  ์ฐจ์ด์ ์€ ๋ฌด์—‡์ธ์ง€ ๊ณ ๋ฏผํ•ด๋ณด์‹œ๋ฉด ์ข‹์Šต๋‹ˆ๋‹ค.

์˜์กด์„ฑ์„ ๋ช…์‹œ์ ์œผ๋กœ ๋“œ๋Ÿฌ๋‚ด๊ณ  ์ฃผ์ž…ํ•˜์ž

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

์˜ˆ๋ฅผ ๋“ค์–ด ๋‹ค์Œ useFormatPrice ํ›…์€ ์‚ฌ์šฉ์ž๊ฐ€ ์„ค์ •ํ•œ currency์™€ lang์„ context์—์„œ ์•”์‹œ์ ์œผ๋กœ ๊ฐ€์ ธ์˜ค๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ํ˜„์žฌ ์‚ฌ์šฉ์ž๊ฐ€ ์„ค์ •ํ•œ ํ†ตํ™”์™€ ์–ธ์–ด๋กœ ๊ฐ€๊ฒฉ์„ ๋ Œ๋”ํ•ด์ฃผ๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

export const useFormatPrice = () => {
    const { lang } = useTranslation('common');
    const currency = useCurrencyValue();

    const formatPrice = useCallback(
        (price: number | null, lang: LanguageCodeT, currencyCode = currency.code) => {
            const formattedPrice = addCommaToNumber(price ?? 0);

            if (currencyCode === 'KRW') {
                return lang === 'ko' ? `${formattedPrice}์›` : `โ‚ฉ${formattedPrice}`;
            }
            // currentCode === 'USD'
            return `$${formattedPrice}`;
        },
        [currency.code, lang]
    );
    return { formatPrice };
};


const { formatPrice } = useFormatPrice()

// ์‚ฌ์šฉ์ž๊ฐ€ KRW, ko๋กœ ์„ค์ •ํ•œ ๊ฒฝ์šฐ
formatPrice(1000) // 1000์›

// ์‚ฌ์šฉ์ž๊ฐ€ KRW, en์œผ๋กœ ์„ค์ •ํ•œ ๊ฒฝ์šฐ
formatPrice(1000) // โ‚ฉ1000

// ์‚ฌ์šฉ์ž๊ฐ€ USD๋กœ ์„ค์ •ํ•œ ๊ฒฝ์šฐ
formatPrice(1000) // $1000

ํ”ํžˆ๋“ค ๋งŒ๋“œ๋Š” ํ›…์ด๊ณ  ๋‹น์žฅ์€ ํŽธ๋ฆฌํ•ด๋ณด์ด์ง€๋งŒ ์—ฌ๋Ÿฌ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

๋จผ์ € react hook์— ์˜์กดํ•˜๊ธฐ ๋•Œ๋ฌธ์— server component์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ด hook์„ ์ด์šฉํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๊ณ ์š”. ์ €ํฌ๋Š” ์–ธ์–ด๋‚˜ ํ†ตํ™”๋Š” ์„œ๋ฒ„์—์„œ url๊ณผ cookie๋กœ ์•Œ ์ˆ˜ ์žˆ๋Š”๋ฐ, ์‚ฌ์šฉ์„ ๋ชปํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ์œผ๋กœ ์‚ฌ์šฉ์ž๊ฐ€ ์„ค์ •ํ•œ ํ†ตํ™”์™€ ๋‹ค๋ฅด๊ฒŒ ๋ Œ๋”ํ•˜๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ์—๋Š” ์‚ฌ์šฉํ•  ์ˆ˜๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์–ด๋–ค ํ•œ๊ตญ์ธ ์‚ฌ์šฉ์ž๊ฐ€ 61000์›์— ์•จ๋ฒ”์„ ์ƒ€๋‹ค๊ณ  ํ•ด๋ด…์‹œ๋‹ค. ๋ฏธ๊ตญ์— ์‚ฌ๋Š” ์นœ๊ตฌ์—๊ฒŒ๋„ ์•จ๋ฒ”์„ ์‚ฌ์ฃผ๋ ค๊ณ  USD๋กœ ์„ค์ •ํ•œ ๋’ค์—, ์ฃผ๋ฌธ ๋‚ด์—ญ์— ๋“ค์–ด๊ฐ€๋ฉด ๋‹ค์Œ์ฒ˜๋Ÿผ ์ด์ƒํ•œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

61,000์›์ด ์•„๋‹ˆ๋ผ, ํ˜„์žฌ ์„ค์ •๋œ ๋‹ฌ๋Ÿฌ๋กœ ๊ฐ€๊ฒฉ์ด ๋‹ค๋ฅด๊ฒŒ ํ‘œ๊ธฐ๋˜๋Š” ๊ฒƒ์ด์ฃ ! ์™ธ๋ถ€์—์„œ formatPrice๋ฅผ ์ œ์–ดํ•  ๋ฐฉ๋ฒ•๋„ ์—†์Šต๋‹ˆ๋‹ค. formatPrice๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์„ค์ •ํ•œ ์–ธ์–ด์™€ ํ†ตํ™”๋Š” ๋ฌผ๋ก ์ด๊ณ  react hook๊ณผ ๊ฐ•๊ฒฐํ•ฉ๋˜์–ด ์žˆ์–ด์„œ ์œ ์—ฐํ•˜์ง€ ๋ชปํ•œ ์ฝ”๋“œ๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋ฉด ์–ด๋–ป๊ฒŒ ํ•˜๋ฉด ์ข‹์„๊นŒ์š”? formatPrice๋ฅผ ์ˆœ์ˆ˜ ํ•จ์ˆ˜๋กœ ๋ถ„๋ฆฌํ•˜๊ณ , ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ context๋ฅผ ๋„˜๊ธฐ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

function formatPrice(price: number | null, currencyCode: CurrencyCodeT, lang: LanguageCodeT){
    const formattedPrice = addCommaToNumber(price ?? 0);

    if (currencyCode === 'KRW') {
        return lang === 'ko' ? `${formattedPrice}์›` : `โ‚ฉ${formattedPrice}`;
    }
    // currentCode === 'USD'
    return `$${formattedPrice}`;
}

// ์‚ฌ์šฉ์ž๊ฐ€ KRW, ko๋กœ ์„ค์ •ํ•œ ๊ฒฝ์šฐ
formatPrice(1000, 'KRW', 'ko') // 1000์›

// ์‚ฌ์šฉ์ž๊ฐ€ KRW, en์œผ๋กœ ์„ค์ •ํ•œ ๊ฒฝ์šฐ
formatPrice(1000, 'KRW', 'en') // โ‚ฉ1000

// ์‚ฌ์šฉ์ž๊ฐ€ USD๋กœ ์„ค์ •ํ•œ ๊ฒฝ์šฐ
formatPrice(1000, 'USD', 'ko') // $1000

์ด๋Ÿฌํ•œ ์˜์กด์„ฑ์€ renderHook์ด๋‚˜ Provider ๋กœ ๊ฐ์‹ธ์ฃผ๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๋ฆฌ์•กํŠธ์— ํŠน์ˆ˜ํ•œ ๋กœ์ง ์—†์ด ์† ์‰ฝ๊ฒŒ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. qwik ๊ฐ™์€ ๋‹ค๋ฅธ ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ณ ์š”.

ํ•˜์ง€๋งŒ ์ด๋Ÿฌ๋ฉด ๋งค๋ฒˆ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ 3๊ฐœ์”ฉ ๋„˜๊ฒจ์•ผ ํ•˜๋‹ˆ ๋ฒˆ๊ฑฐ๋กญ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ context์˜ ์˜์กด์„ฑ์„ ์ฃผ์ž…ํ•ด์ค€ custom hook์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

export const useFormatPrice = () => {
    const { lang } = useTranslation('common');
    const currency = useCurrencyValue();

    return {
        formatPrice: (price: number | null, currencyCode = currency.code, lang = lang) => formatPrice(price, currencyCode, lang)
    };
};

const { formatPrice } = useFormatPrice();

formatPrice(1000) // 1000์›
formatPrice(1000, 'USD') // $1000

์ด๋ ‡๊ฒŒ ํŠน์ • ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์ฑ„์›Œ๋‘” ์ƒˆ๋กœ์šด ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“œ๋Š” ๊ธฐ๋ฒ•์„ ํ•จ์ˆ˜ํ˜•์—์„œ๋Š” ๋ถ€๋ถ„ ์ ์šฉ ํ˜น์€ ์ปค๋ง์ด๋ผ๊ณ ๋„ ๋ถ€๋ฆ…๋‹ˆ๋‹ค.

์˜์กด์„ฑ์ด ์—†๋Š” ์ˆœ์ˆ˜ ํ•จ์ˆ˜๋Š” ์„œ๋ฒ„์—์„œ๋„ ๋ธŒ๋ผ์šฐ์ €์—์„œ๋„ ์ž˜ ๋Œ์•„๊ฐ‘๋‹ˆ๋‹ค. ์ด๋Š” ์—ฌ๋Ÿฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค๋„ ๋น„์Šทํ•œ ์ „๋žต์„ ์“ฐ๊ณ  ์žˆ์œผ๋‹ˆ ์ฐธ๊ณ ํ•ด๋ณด์‹œ๋ฉด ์ข‹์Šต๋‹ˆ๋‹ค.

component์— prop์„ ๋ช…์‹œ์ ์œผ๋กœ ์ฃผ์ž…ํ•˜๊ธฐ

๋น„์Šทํ•˜๊ฒŒ ์ปดํฌ๋„ŒํŠธ๋„ ์ˆœ์ˆ˜ ํ•จ์ˆ˜๋กœ ๋งŒ๋“ค๊ณ  props๋ฅผ ์ด์šฉํ•ด ๋ช…์‹œ์ ์œผ๋กœ ์˜์กด์„ฑ์„ ๋„˜๊ธฐ๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค. props๋„ ํ•จ์ˆ˜์˜ ๋งค๊ฐœ๋ณ€์ˆ˜์ผ ๋ฟ์ด๋‹ˆ๊นŒ์š”. (class ๊ฐ์ฒด์ง€ํ–ฅ์—์„œ๋Š” ์ƒ์„ฑ์ž ์ฃผ์ž…์ด๋ผ๊ณ ๋„ ๋ถ€๋ฆ…๋‹ˆ๋‹ค.) ๊ณผ๊ฑฐ์—๋Š” props๋ฅผ ์ด์šฉํ•ด ์˜์กด์„ฑ์„ ์ฃผ์ž…ํ•˜๋Š” ๊ฒŒ ๋‹น์—ฐํ•œ ๋ฐฉ๋ฒ•์ด์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ๋‹น์‹œ์—๋Š” ๊ณ ์ฐจ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ด์šฉํ•ด์„œ ์ปดํฌ๋„ŒํŠธ์— ํŠน์ • props๋ฅผ ๋ถ€๋ถ„ ์ ์šฉํ–ˆ์ง€์š”. ์œ„์—์„œ ๋งํ•œ ๋ถ€๋ถ„ ์ ์šฉ๊ณผ ๋‹ค๋ฅด์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ง€๊ธˆ๋„ next-translate ๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” hook์„ ์ด์šฉํ•œ ๋ฐฉ๋ฒ•๊ณผ ๊ณ ์ฐจ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ด์šฉํ•œ ๋ฐฉ๋ฒ•์„ ๋‘˜ ๋‹ค ์ง€์›ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

import React from 'react'
import withTranslation from 'next-translate/withTranslation'

class ErrorBoundary extends React.Component {
  render() {
    const { t, lang } = this.props.i18n

    return <p>{t('common:error-title')}</p> // <p>์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค!</p>
  }
}

export default withTranslation(ErrorBoundary)

๊ทธ๋Ÿฐ๋ฐ hook์ด ๋‚˜์˜ค๊ณ  ๋‚˜์„œ๋Š” props์˜ ํƒ€์ž…์„ ์ •์˜ํ•˜๊ณ , ๊ณ ์ฐจ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒŒ ๋ฒˆ๊ฑฐ๋กญ๊ธฐ๋„ ํ•˜๊ณ ์š”. hook์€ import๋งŒ ํ•˜๋ฉด ์‰ฝ๊ฒŒ ๊ฐ€์ ธ๋‹ค ์“ฐ๊ณ  ํ•ฉ์„ฑํ•  ์ˆ˜ ์žˆ์œผ๋‹ˆ, ์ ์  props๋ฅผ ์ด์šฉํ•œ ์ฃผ์ž… ๋ฐฉ์‹์ด ์žŠํ˜€์ ธ ๊ฐ€๊ณ  ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค๋งŒโ€ฆ

server side rendering์ด ๋‚˜์˜ค๋ฉด์„œ ์ด์•ผ๊ธฐ๊ฐ€ ์ข€ ๋‹ฌ๋ผ์ง€๊ธฐ ์‹œ์ž‘ํ–ˆ์Šต๋‹ˆ๋‹ค. nextjs์˜ pages ๋ผ์šฐํ„ฐ๋ฅผ ์‚ฌ์šฉํ•ด๋ณด์‹  ๋ถ„๋“ค์€ ์•„์‹œ๊ฒ ์ง€๋งŒ, getServerSideProps๋ฅผ ์ด์šฉํ•ด์„œ Page์— ์„œ๋ฒ„์—์„œ ๊ฐ€์ ธ์˜จ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ์ž…ํ•ฉ๋‹ˆ๋‹ค. ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์—์„œ๋„ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์— props์„ ์ด์šฉํ•ด์„œ ๋ช…์‹œ์ ์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋„˜๊ฒจ์ฃผ๊ณ ์š”. ๋งŒ์•ฝ์— props๋ฅผ ๋ฐ›์ง€ ์•Š๊ณ , ํŠน์ • ์ƒํƒœ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ hook์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ๊ฑฐ๋ผ๊ณ  ๊ฐ€์ •ํ–ˆ๋‹ค๋ฉด ์ด๋Ÿฐ ๋ณ€ํ™”์— ์ ์‘ํ•˜๊ธฐ๊ฐ€ ์–ด๋ ค์šธ ๊ฒ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‹ˆ hook์„ ์“ฐ์ง€ ๋ง์ž๋Š” ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค๋งŒ. (์ €๋„ props๋ฅผ ์ด์šฉํ•œ ์ฃผ์ž…์€ ์ž์ œํ•˜๋Š” ํŽธ์ž…๋‹ˆ๋‹ค.) props๋ฅผ ์ด์šฉํ•œ ์ฃผ์ž… ๋ฐฉ์‹์ด ๋ฒˆ๊ฑฐ๋กœ์›Œ๋ณด์ผ์ง€๋ผ๋„, ์ข‹์€ ๋Œ€์•ˆ ์ค‘ ํ•˜๋‚˜๋ผ๋Š” ๊ฑธ ๊ธฐ์–ตํ•ด์ฃผ์‹œ๋ฉด ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค.

ํ•„์š”ํ•œ ๊ณณ์—๋งŒ ์˜์กดํ•˜๊ธฐ aka ์ธํ„ฐํŽ˜์ด์Šค ๊ฒฉ๋ฆฌ์˜ ์›์น™

๋งค๊ฐœ๋ณ€์ˆ˜๋Š” ํ•„์š”ํ•œ ๊ฒƒ๋งŒ ๋ฐ›๊ณ , ์„œ๋กœ ๋‹ค๋ฅธ ๋กœ์ง์˜ ์ธํ„ฐํŽ˜์ด์Šค๋Š” ๋ถ„๋ฆฌํ•˜๋Š” ๊ฒŒ ์ข‹์Šต๋‹ˆ๋‹ค.

ํ•จ์ˆ˜๋‚˜ ์ปค์Šคํ…€ ํ›…์„ ๋งŒ๋“ค๋‹ค๋ณด๋ฉด ๊ฑฐ๋Œ€ํ•œ ํƒ€์ž…์„ ๋งŒ๋“ค๊ณ , ํƒ€์ž… ์˜์กด์„ฑ์„ ์ด๊ณณ์ €๊ณณ์— ํผํŠธ๋ฆฌ๊ณ ๋Š” ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌ๋ฉด ๊ฒฐํ•ฉ๋„๋„ ๋†’์•„์ง€๊ณ , ํ…Œ์ŠคํŠธ๋„, ์‚ฌ์šฉ๋„ ์–ด๋ ค์›Œ์ง‘๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด ์ €ํฌ ํŒ€์—๋Š” ์ƒํ’ˆ์˜ ์ƒํƒœ๋ฅผ ํŒ์ •ํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ์žˆ๋Š”๋ฐ์š”. ์ƒํ’ˆ์˜ ๋”ฑ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ 4๊ฐœ์—๋งŒ ์˜์กด์„ ํ•˜๊ฒŒ ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

import type { GoodsStatusT } from 'Types';

export type ComputedGoodsStatusT = 'ready' | 'soldout' | 'quit' | 'comingsoon';

export function computeGoodsStatus(
    {
        startDate,
        endDate,
        status
    }: {
        startDate?: number; // ํŒ๋งค ์‹œ์ž‘ ์‹œ๊ฐ
        endDate?: number; // ํŒ๋งค ์ข…๋ฃŒ ์‹œ๊ฐ
        status?: GoodsStatusT; // ์„œ๋ฒ„์—์„œ ์ง€์ •ํ•œ ์ƒํ’ˆ ์ƒํƒœ
    },
    now: number
): ComputedGoodsStatusT {
    // ์ดํ•˜ ๋กœ์ง
}

๊ทธ๋Ÿฐ๋ฐ ์ฝ”๋“œ๋ฆฌ๋ทฐ๋ฅผ ํ•˜๋‹ค๋ณด๋ฉด, api์—์„œ ๊ฐ€์ ธ์˜จ ๋ฐ์ดํ„ฐ๋ฅผ Type ์ •์˜์— ๋‹ค ์ง‘์–ด ๋„ฃ๋Š” ๊ฒฝ์šฐ๋ฅผ ๋ณด๊ณ ๋Š” ํ•ฉ๋‹ˆ๋‹ค. ๋‹น์žฅ์€ ํŽธ๋ฆฌํ•˜๊ณ  ๊ฐ„๊ฒฐํ•ด๋ณด์ž…๋‹ˆ๋‹ค.

import type { GoodsStatusT } from 'Types';
import type { GetGoodsResponse } from '@knowmerce/store-api-goods';

export type ComputedGoodsStatusT = 'ready' | 'soldout' | 'quit' | 'comingsoon';

export function computeGoodsStatus(
    {
        startDate,
        endDate,
        status
    }: GetGoodsResponse, // <- api์—์„œ ๊ฐ€์ ธ์˜จ ํƒ€์ž…์„ ๊ทธ๋Œ€๋กœ ๋„ฃ์—ˆ์Šต๋‹ˆ๋‹ค.
    now: number
): ComputedGoodsStatusT {
    // ... ์ดํ•˜ ์ƒ๋žต
}

ํ•˜์ง€๋งŒ ์ด๋Ÿฌ๋ฉด computeGoodsStatus๋Š” GetGoodsReponse์—๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์ €ํฌ๋Š” ์ƒํ’ˆ์„ ์„ ํƒํ•˜๊ฒŒ ๋„์™€์ฃผ๋Š” Bundle์ด๋ผ๋Š” ๊ฒŒ ์žˆ๋Š”๋ฐ์š”. Bundle์—๋„ startDate, endDate, status๊ฐ€ ๋‹ค ์žˆ๊ณ  ๋กœ์ง๋„ ๋˜‘๊ฐ™์Šต๋‹ˆ๋‹ค. (๋˜‘๊ฐ™์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค) ๊ทธ๋Ÿฐ๋ฐ ์ด๋Ÿฌ๋ฉด bundle์„ ์œ„ํ•œ ๋กœ์ง์€ ์ƒˆ๋กœ ๋ณต์‚ฌ ๋ถ™์—ฌ๋„ฃ๊ธฐํ•ด์„œ ๋งŒ๋“ค์–ด์•ผ ํ•˜์ฃ .

ํ…Œ์ŠคํŠธ๋„ ์–ด๋ ค์›Œ์ง‘๋‹ˆ๋‹ค. ์›๋ž˜ ์ด ์ˆœ์ˆ˜ ํ•จ์ˆ˜๋Š” ๋งค๊ฐœ ๋ณ€์ˆ˜๊ฐ€ ๋ช‡ ๊ฐœ ๋˜์ง€ ์•Š์œผ๋‹ˆ, ๋‹ค์Œ์ฒ˜๋Ÿผ ์‰ฝ๊ฒŒ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

const ์‹œ์ž‘์ „ = unixDate(2019, 12, 31, 23, 59);
const startDate = unixDate(2020, 1, 1);

test('startDate ์ด์ „์ด๋ฉด, comingsoon ์ด๋‹ค', () => {
    expect(computeGoodsStatus({ status: 'ready', startDate }, ์‹œ์ž‘์ „)).toBe('comingsoon');
});

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

test('startDate ์ด์ „์ด๋ฉด, comingsoon ์ด๋‹ค', () => {
    expect(computeGoodsStatus({
        status: 'ready',
        startDate,
        id: 333,
        name: '๋“œ๋ฆผ์บ์ณ (Dreamcatcher) 8th Mini Album [Apocalypse : From us] [W ver.] (ํ•œ์ •๋ฐ˜)',
        description: '...์ƒํ’ˆ ์„ค๋ช…...',
        body: '๋งค์šฐ ๊ธด ์ƒํ’ˆ ์ƒ์„ธ ์„ค๋ช…',
        image: 'https://my-awesome-domain/images/333/f0ca6dc0-2494-406b-8250-8c4bd03d76f2_1683775448296',
        // ... ์ดํ•˜ ์ƒ๋žต
    }, ์‹œ์ž‘์ „)).toBe('comingsoon');
});

์™œ ์ด๋ ‡๊ฒŒ ๋˜์—ˆ์„๊นŒ์š”? ์ด ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์‚ฌ์šฉ์ž์—๊ฒŒ ๋„ˆ๋ฌด ๋งŽ์€ ๊ฑธ ์š”๊ตฌํ–ˆ์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ํ†ต์ œํ•  ์ˆ˜ ์—†๋Š” ์„œ๋ฒ„์˜ ํƒ€์ž…์— ๊ฐ•๊ฒฐํ•ฉ๋˜์—ˆ๊ณ ์š”. ์šฐ๋ฆฌ์˜ ๊ด€์‹ฌ์‚ฌ์™€ ๋ฌด๊ด€ํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋’ค์„ž์—ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

๋ฌธ์ œ์˜ ํ•ด๊ฒฐ์ฑ…์€ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค. ์ธํ„ฐํŽ˜์ด์Šค ์‚ฌ์šฉ์ž์—๊ฒŒ ์ตœ์†Œํ•œ๋งŒ ์š”๊ตฌํ•˜๊ณ , ์„œ๋กœ ๋‹ค๋ฅธ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋’ค์„ž์ง€ ์•Š์œผ๋ฉด ๋ฉ๋‹ˆ๋‹ค. ๊ท€์ฐฎ๋”๋ผ๋„ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋ถ„๋ฆฌํ•˜์‹œ๊ณ  ์ž‘์€ ํƒ€์ž…์„ ์—ฌ๋Ÿฌ ๊ฐœ ๋งŒ๋“œ์‹œ๋Š” ๊ฒŒ ์ข‹์Šต๋‹ˆ๋‹ค.

context๋ฅผ ์ด์šฉํ•œ ์˜์กด์„ฑ ์ฃผ์ž…

๋งˆ์ง€๋ง‰์œผ๋กœ context์™€ ๋ชจ๋“ˆ์— ๋Œ€ํ•œ ์ด์•ผ๊ธฐ๋ฅผ ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๋งŽ์€ ๋ถ„๋“ค์ด react์˜ context api๋ฅผ ๋ฆฌ๋•์Šค ๊ฐ™์€ ์ƒํƒœ ๊ด€๋ฆฌ ๋„๊ตฌ๋กœ๋งŒ ์˜คํ•ดํ•˜์‹œ๊ณ ๋Š” ํ•˜๋Š”๋ฐ์š”. ๊ทธ๋ ‡๋‹ค๊ณ  ํ•˜๋ฉด ์ข€ ์ด์ƒํ•œ ๊ฒŒ ๋งŽ์Šต๋‹ˆ๋‹ค. ๋‹ค๋“ค ์ด์•ผ๊ธฐํ•˜์ง€๋งŒ ๋ฆฌ๋ Œ๋”๋„ ์ตœ์ ํ™”๋˜์ง€ ์•Š๊ณ ์š”. redux๋‚˜ jotai ๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๊ฐ™์ด ์จ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

jotai ๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” Provider๊ฐ€ ํ•„์š” ์—†๋Š” Provider less ๋ชจ๋“œ๋ฅผ ์ง€์›ํ•˜๊ธฐ๋„ ํ•˜๊ณ ์š”. ์ „์—ญ singleton ๊ฐ์ฒด๋ฅผ ์ด์šฉํ•ด์„œ ๋น„์Šทํ•œ ๊ธฐ๋Šฅ์„ ์ง€์›ํ•˜๋Š” ๊ฒฝ์šฐ๋„ ๋งŽ์Šต๋‹ˆ๋‹ค.

context api๊ฐ€ ์ •๋ง ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๋‚˜ ์ƒ๊ฐํ•ด๋ณด๋ฉด ๊ผญ ๊ทธ๋Ÿด ํ•„์š”๋Š” ์—†์Šต๋‹ˆ๋‹ค. ๋Œ€๋ถ€๋ถ„์€ ๊ฑฐ์˜ ๋ณ€ํ•˜์ง€ ์•Š๋Š” ์ •์ ์ธ ๊ฒƒ๋“ค๋งŒ ๊ตฌ๋…ํ•ฉ๋‹ˆ๋‹ค.

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

๋‹ค๋ฅธ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์จ๋ณด์‹  ๋ถ„์€ ์•„์‹œ๊ฒ ์ง€๋งŒ, context api๋Š” ์˜์กด์„ฑ ์ฃผ์ž… ๋„๊ตฌ๋กœ ๋งŽ์ด ์“ฐ์ž…๋‹ˆ๋‹ค. context api์™€ ์œ ์‚ฌํ•œ ๊ธฐ๋Šฅ์„ vue์—์„œ๋Š” provide / inject๋ผ ๋ถ€๋ฆ…๋‹ˆ๋‹ค. svelte๋Š” getContext์™€ setContext๋ผ๊ณ  ํ•˜๊ณ ์š”. ์„œ๋ฒ„ ํ”„๋ ˆ์ž„์›Œํฌ๋“ค์—์„œ๋Š” context๊ฐ€ db connection์ด๋‚˜ ๋กœ๊ทธ์ธํ•œ ์ •๋ณด ๊ฐ™์€ ์™ธ๋ถ€ ์˜์กด์„ฑ์„ ๋“ค๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

vue์—์„œ ์“ฐ๋Š” ์šฉ์–ด๊ฐ€ ํŠนํžˆ ์ฃผ์˜๋ฅผ ๊ธฐ์šธ์ผ๋งŒ ํ•ฉ๋‹ˆ๋‹ค. inject? ํด๋ž˜์Šค ๊ฐ์ฒด์ง€ํ–ฅ ์–ธ์–ด์—์„œ ์˜์กด์„ฑ ์ฃผ์ž…(Dependency Injection)์„ ํ•ด๋ณด์‹  ๋ถ„์€ ์•„์‹œ๊ฒ ์ง€๋งŒ, ์˜์กด์„ฑ์„ โ€˜์ฃผ์ž…โ€™ํ•œ๋‹ค๋Š” ๋ง์ž…๋‹ˆ๋‹ค.

react์—์„œ๋„ context api๋Š” ๋ณดํ†ต ์˜์กด์„ฑ์„ ์ฃผ์ž…ํ•˜๋Š” ์šฉ๋„๋กœ ์”๋‹ˆ๋‹ค. redux๋‚˜ jotai ๊ฐ™์€ ์ƒํƒœ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” store๋ฅผ context api๋กœ ์ฃผ์ž…ํ•˜๊ณ  ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. react-router-dom์€ Provider๋ฅผ ์ด์šฉํ•ด ๊ตฌํ˜„์ฒด๋ฅผ ๊ฐˆ์•„ ๋ผ์šธ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. (MemoryRouter, HashRouter ๊ฐ™์€) tanstack-query๋Š” QueryClient๋ฅผ context๋กœ ์ฃผ์ž…ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ test๋ฅผ ์œ„ํ•ด ๊ฐ€์งœ ์˜์กด์„ฑ์„ ์ฃผ์ž…ํ•  ๋•Œ์—๋„ ์ด๋Ÿฐ Provider๋ฅผ ์‚ฌ์šฉํ•˜์ฃ .

context API๋Š” ์—ด๋“ฑํ•œ ๋ฆฌ๋•์Šค๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. context๋Š” ์ž์ฃผ ๋ณ€ํ•˜๋Š” ์ƒํƒœ๋ณด๋‹ค๋Š”, ์‚ฌ์šฉ์ž ์„ค์ •์ด๋‚˜ ํ…Œ๋งˆ, ์œ„์—์„œ ๋งํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ์ž์ฃผ ๋ณ€ํ•˜์ง€ ์•Š๋Š” ์˜์กด์„ฑ์ธ ๊ฒฝ์šฐ๊ฐ€ ๋” ๋งŽ์Šต๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด์„œ ์œ„์˜ ์˜ˆ์‹œ์—์„œ ์ €๋Š” currency๋‚˜ lang์„ component์˜ props๋กœ ๋ฐ›์ง€ ์•Š๊ณ , useCurrencyValue๋‚˜ useTranslation ๊ฐ™์€ ํ›…์„ ์ด์šฉํ•ด์„œ ๋ฐ›์•˜๋Š”๋ฐ์š”. ์ด ์—ญ์‹œ ์ผ์ข…์˜ context๋ฅผ ์ฃผ์ž… ๋ฐ›์•˜๋‹ค๊ณ  ํ•  ์ˆ˜ ์žˆ๊ฒ ์Šต๋‹ˆ๋‹ค.

// props๋กœ ์ฃผ์ž…
function Component({ currency, lang }: { currency: CurrencyCodeT, lang: LanguageCodeT })

// Context๋กœ ์ฃผ์ž…
function Component()
    const { lang } = useTranslation('common');
    const currency = useCurrencyValue();

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

module alias์™€ importmap์„ ์ด์šฉํ•œ ์˜์กด์„ฑ ์ฃผ์ž…

๊ทธ๋Ÿฐ๋ฐ ๋ฆฌ์•กํŠธ ํŒ€์˜ Sebastian์€ Context๋ฅผ ์ด์šฉํ•œ ๋Ÿฐํƒ€์ž„ ์˜์กด์„ฑ ์ฃผ์ž…๋ณด๋‹ค๋Š” ์ •์ ์ธ module alias๋ฅผ ๋” ์„ ํ˜ธํ•œ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ์‹ฌ์ง€์–ด Runtime dependency injection is the worst thing ever๋ผ๊ณ  ๊ฐ•๋„ ๋†’๊ฒŒ ๋น„ํŒํ•  ์ •๋„์ธ๋ฐ์š”. ํŠนํžˆ subtree๋งˆ๋‹ค ๋‹ค๋ฅธ ์˜์กด์„ฑ์„ ์ฃผ์ž…ํ•ด์ฃผ๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ, ์•ฑ ์ „์ฒด์˜ ์ „์—ญ ์˜์กด์„ฑ์„ ์ฃผ์ž…ํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” ํŠนํžˆ ๊ทธ๋ ‡๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

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

๊ทธ๋ ‡๋‹ค๋ฉด sebastian์ด ์ œ์‹œํ•˜๋Š” ๋Œ€์•ˆ์ธ ์ •์ ์ธ module alias๋Š” ๋ฌด์—‡์ผ๊นŒ์š”? webpack์ด๋‚˜ vite ๊ฐ™์€ ๋ฒˆ๋“ค๋Ÿฌ์˜ module alias ๊ธฐ๋Šฅ์„ ์ด์šฉํ•˜๋ฉด ํŠน์ • ๋ชจ๋“ˆ์„ importํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์‰ฝ๊ฒŒ ๋ฐ”๊ฟ”์น˜๊ธฐ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ณดํ†ต์€ js์—์„œ ์ ˆ๋Œ€ ๊ฒฝ๋กœ import๋ฅผ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด ์“ฐ์ด๊ณ ๋Š” ํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด next๋กœ ๊ฐœ๋ฐœํ•˜๋‹ค๋ณด๋ฉด, vitest๋‚˜ storybook ๊ฐ™์€ ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์—์„œ next/link๋‚˜ next/image ๋“ฑ์ด ์ž‘๋™ํ•˜์ง€ ์•Š๊ณ ๋Š” ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ๋ณดํ†ต ์ด๋Ÿฌํ•œ ํ™˜๊ฒฝ์„ mockingํ•˜๋ ค๊ณ  ๊ณ ์ƒํ•˜๊ฒŒ ๋˜๋Š”๋ฐ์š”. laddle์ด๋ผ๋Š” ์Šคํ† ๋ฆฌ๋ถ ๋Œ€์ฒด ํ”„๋ ˆ์ž„์›Œํฌ์˜ ๋ฌธ์„œ์—์„œ๋Š” ๋” ๋‹จ์ˆœํ•œ ๋ฐฉ๋ฒ•์„ ์ œ์•ˆํ•ฉ๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•œ ๊ฐ€์งœ ๊ตฌํ˜„์ฒด๋กœ next์—์„œ importํ•˜๋Š” ๋ชจ๋“ˆ์„ ๋ฐ”๊ฟ”์น˜๊ธฐํ•˜๋Š” ๊ฒƒ์ด์ฃ .

import path from "path";
import { defineConfig } from "vite";

export default defineConfig({
  resolve: {
    alias: {
      "next/image": path.resolve(__dirname, "./.ladle/UnoptimizedImage.tsx"),
      "next/link": path.resolve(__dirname, "./.ladle/UnoptimizedLink.tsx"),
    },
  },
});
const UnoptimizedLink = (props: any) => {
  return <a {...props} />;
};
export default UnoptimizedLink;
const UnoptimizedImage = (props: any) => {
  return <img {...props} />;
};
export default UnoptimizedImage;

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

์•ž์„œ ๋งํ•œ ๊ฒƒ ๊ฐ™์€ ๊ธฐ์ˆ ์Šคํƒ์„ migration ํ•˜๋Š” ์ƒํ™ฉ์—๋„ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ์กด์˜ ์˜์กด์„ฑ๊ณผ ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ํ˜ธํ™˜๋˜๋Š” ์–ด๋Œ‘ํ„ฐ๋ฅผ ๋งŒ๋“œ์‹  ๋‹ค์Œ, module alias๋กœ ์ฒ˜๋ฆฌํ•˜์‹œ๋ฉด ๊ธฐ์กด ์ฝ”๋“œ๋ฅผ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๊ณ ๋„ ์ƒˆ ๊ธฐ์ˆ ์Šคํƒ์œผ๋กœ ํ”„๋กœ์ ํŠธ ์ „์ฒด๋ฅผ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด react์˜ ๋” ๊ฐ€๋ฒผ์šด ๊ตฌํ˜„์ฒด์ธ preact๊ฐ€ ์žˆ๋Š”๋ฐ์š”. preact ๋ฌธ์„œ์—์„œ๋Š” preact/compat์ด๋ผ๋Š” ํ˜ธํ™˜ ๋ชจ๋“ˆ๋กœ aliasํ•ด์„œ ๊ธฐ์กด react ํ”„๋กœ์ ํŠธ๋ฅผ ํ†ต์งธ๋กœ migrationํ•˜๋Š” ๋ฒ•์„ ์•Œ๋ ค์ค๋‹ˆ๋‹ค. ๋‹ค์–‘ํ•œ ๋ฒˆ๋“ค๋Ÿฌ์—์„œ module alias๋ฅผ ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์นœ์ ˆํžˆ ์„ค๋ช…ํ•ด์ฃผ๊ณ  ์žˆ์œผ๋‹ˆ, webpack์ด๋‚˜ vite ๊ฐ€ ์•„๋‹Œ ๋ฒˆ๋“ค๋Ÿฌ๋ฅผ ์“ฐ์‹œ๋Š” ํŒ€์—์„œ๋Š” ์ฐธ๊ณ ํ•ด๋ณด์‹œ๋ฉด ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค.

const config = {
   //...snip
  "resolve": {
    "alias": {
      "react": "preact/compat",
      "react-dom/test-utils": "preact/test-utils",
      "react-dom": "preact/compat",     // Must be below test-utils
      "react/jsx-runtime": "preact/jsx-runtime"
    },
  }
}

๊ทธ๋Ÿฌ๋ฉด ์ด๋Ÿฐ module alias๋Š” ๋ฒˆ๋“ค๋Ÿฌ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ๋งŒ ์“ธ ์ˆ˜ ์žˆ์„๊นŒ์š”? ์ƒˆ๋กœ ๋‚˜์˜จ importmap์€ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ module์„ resolveํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ง€์ •ํ•˜๋Š” ํ‘œ์ค€์ž…๋‹ˆ๋‹ค. ๋ฒˆ๋“ค๋Ÿฌ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ํ”„๋กœ์ ํŠธ์—์„œ๋„ ์‰ฝ๊ฒŒ module alias๋ฅผ ์„ค์ •ํ•˜๊ณ , ์—ฌ๋Ÿฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์„ ์‰ฝ๊ฒŒ importํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. js ๋ฐฑ์—”๋“œ ์ƒํƒœ๊ณ„์—์„œ๋Š” deno ๊ฐ™์€ ๋Ÿฐํƒ€์ž„๋“ค๋„ ์ž˜ ์ง€์›ํ•ด์ฃผ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ module alias๋Š” ํ•œ๊ณ„๊ฐ€ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค. react๋กœ server side rendering์„ ํ•˜๋‹ค๋ณด๋ฉด, ์‚ฌ์šฉ์ž๋‚˜ request๋งˆ๋‹ค ์ƒํƒœ๋‚˜ ์˜์กด์„ฑ์„ ๊ณต์œ ํ•˜๋ฉด ์•ˆ ๋˜๋Š” ๊ฒฝ์šฐ๋„ ์ƒ๊ธฐ๊ณ ์š”. ์‚ฌ์šฉ์ž ์„ค์ •์ฒ˜๋Ÿผ ๋Ÿฐํƒ€์ž„์— ๋ฐ”๋€Œ๋Š” ์ƒํƒœ ์˜์กด์„ฑ๋„ ๊ฒฝ์šฐ๋„ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. (์ž์ฃผ ๋ฐ”๋€Œ์ง€๋Š” ์•Š๊ฒ ์Šต๋‹ˆ๋‹ค๋งŒ)

์˜ˆ๋ฅผ ๋“ค์–ด ํ•œ ํŽ˜์ด์ง€๋Š” KRW๋กœ ๋ Œ๋”๋˜๋”๋ผ๋„, ๋‹ค๋ฅธ ํŽ˜์ด์ง€๋Š” USD๋กœ ๋ Œ๋”ํ•˜๊ณ  ์‹ถ์„ ์ˆ˜ ์žˆ๋Š”๋ฐ์š”. react-query๋‚˜ jotai ๋“ฑ์˜ ๋ฌธ์„œ๋ฅผ ๋ณด์‹œ๋ฉด ๋ณดํ†ต ๋Ÿฐํƒ€์ž„์— QueryClient๋‚˜ store๋ฅผ request๋งˆ๋‹ค ๋™์ ์œผ๋กœ ๋งŒ๋“ค์–ด์„œ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค.

import { Hydrate, QueryClient, QueryClientProvider } from 'react-query'

// query client๋ฅผ ์ •์ ์œผ๋กœ ์ƒ์„ฑํ•˜๋ฉด ๋ชจ๋“  ์‚ฌ์šฉ์ž๊ฐ€ query๋ฅผ ๊ณต์œ ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค X
// const queryClient = new QueryClient()

export default function MyApp({ Component, pageProps }) {
  // ๋Ÿฐํƒ€์ž„์— ๋™์ ์œผ๋กœ ์ƒ์„ฑํ•˜๋ฉด ๋ Œ๋”๋งˆ๋‹ค ๊ฒฉ๋ฆฌ๋ฉ๋‹ˆ๋‹ค O
  const [queryClient] = React.useState(() => new QueryClient())

  return (
    <QueryClientProvider client={queryClient}>
      <Hydrate state={pageProps.dehydratedState}>
        <Component {...pageProps} />
      </Hydrate>
    </QueryClientProvider>
  )
}

์ €ํฌ๋Š” ์‹ค์ œ๋กœ ์ด ์ฒ˜๋ฆฌ๋ฅผ ๊นŒ๋จน์–ด์„œ, ํ”„๋กœ๋•์…˜์—์„œ ์ด์Šˆ๋กœ ์ด์–ด์ง„ ์ ๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์œ„์—์„œ๋„ ์–ธ๊ธ‰ํ–ˆ๋˜ $61000 ์ด์Šˆ์ธ๋ฐ์š”. KRW์—์„œ USD๋กœ ํ†ตํ™” ์„ค์ •์„ ๋ฐ”๊พธ์–ด๋„ ๊ณ„์† KRW๋กœ ๋ Œ๋”๋˜์–ด์„œ ์‚ฌ์šฉ์ž ๋ถ„๋“ค์ด ๋ถˆํŽธ์„ ๊ฒช์œผ์…จ์ฃ โ€ฆ

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

์˜ˆ๋ฅผ ๋“ค์–ด ์ €ํฌ์˜ vitest๋Š” isolate ์˜ต์…˜์„ ๋„๋ฉด 2๋ฐฐ ๋นจ๋ผ์กŒ๊ณ ์š”. isolation์„ ์ง€์›ํ•˜์ง€ ์•Š๋Š” bun test๋ฅผ ์“ฐ๋ฉด 157๊ฐœ์˜ typescript unit test๋ฅผ ์‹คํ–‰ํ•˜๋Š”๋ฐ 0.1์ดˆ ๋ฐ–์— ๊ฑธ๋ฆฌ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ •์ ์ธ ์˜์กด์„ฑ ์ฃผ์ž…์œผ๋กœ ์ „์—ญ ์ƒํƒœ๋ฅผ ๊ณต์œ ํ•˜๊ฒŒ ํ–ˆ๋‹ค๋ฉด ๋ถˆ๊ฐ€๋Šฅํ–ˆ๊ฒ ์ฃ .

๊ทธ๋ฆฌ๊ณ  ์ตœ๊ทผ JIT ์ปดํŒŒ์ผ์ด ์ž์ฃผ ์“ฐ๋Š” ์ฝ”๋“œ๋Š” inline ์ตœ์ ํ™” ๋“ฑ์„ ํ†ตํ•ด ๋™์ ์ธ ์˜์กด์„ฑ ์ฃผ์ž…์˜ ๋น„์šฉ์„ ์ค„์—ฌ์ฃผ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ €๋Š” js ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ python์ด๋‚˜ lua ๋“ฑ์˜ ์–ธ์–ด์—์„œ ๋‹ค์–‘ํ•˜๊ฒŒ ์‹คํ—˜์„ ํ•ด๋ณด๊ณ  ์žˆ๋Š”๋ฐ์š”. ํ‰๋ฒ”ํ•œ ์ธํ„ฐํ”„๋ฆฌํ„ฐ์™€ ๋‹ฌ๋ฆฌ JIT compile์„ ํ•˜๋Š” ๊ตฌํ˜„์ฒด์—์„œ๋Š” ์ดˆ๊ธฐํ™” ์ดํ›„์—๋Š” ํฐ ์ฐจ์ด๊ฐ€ ์—†์–ด์„œ ์‹ ๊ธฐํ•˜๊ฒŒ ์ƒ๊ฐํ–ˆ๋˜ ๊ธฐ์–ต์ด ์žˆ์Šต๋‹ˆ๋‹ค.

๋ชจ๋“  ์˜์กด์„ฑ ์ฃผ์ž… ๋ฐฉ๋ฒ•์€ ๊ฐ์ž ์žฅ์ ๊ณผ ํ•œ๊ณ„๊ฐ€ ์žˆ์œผ๋‹ˆ, ์–ด๋–ค ์˜์กด์„ฑ์„ ์–ด๋–ป๊ฒŒ ๊ด€๋ฆฌํ• ์ง€ ๊ณ ๋ฏผํ•ด๋ณด์‹œ๋ฉด ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค. ํ•ญ์ƒ ์ •์ ์ธ ์˜์กด์„ฑ ์ฃผ์ž…๋งŒ ์“ฐ๊ฑฐ๋‚˜, ํ•ญ์ƒ ๋™์ ์ธ ์˜์กด์„ฑ ์ฃผ์ž…๋งŒ ์“ธ ์ˆ˜๋Š” ์—†๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ํŠน์ • ์ด์Šˆ๋‚˜ ๋ฌธ์ œ๋กœ ํฌ๊ฒŒ ๊ณ ํ†ต ๋ฐ›์•˜๋‹ค๊ณ  ํ•ด์„œ, ๊ทธ ๋ฐฉ๋ฒ•๋ก  ์ž์ฒด๊ฐ€ the worst thing ever๋ผ๊ณ  ๋‹จ์ •์ง€์–ด์„œ๋Š” ์•ˆ ๋˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๋ฌผ๋ก  ํ˜„๋ช…ํ•œ ์‚ฌ๋žŒ๋„ ๊ทธ๋Ÿฐ ์‹ค์ˆ˜๋ฅผ ํ•  ๋•Œ๊ฐ€ ์žˆ์ง€๋งŒ์š”.

๊ฒฐ๋ก 

์•„์ง ํ•˜๊ณ  ์‹ถ์€ ์ด์•ผ๊ธฐ๊ฐ€ ๋งŽ์ง€๋งŒ, ์•„์‰ฝ๊ฒŒ๋„ ์ด๋ฏธ ์ด์•ผ๊ธฐ๊ฐ€ ๋งŽ์ด ๊ธธ์–ด์ ธ์„œ ์ด๋งŒ ์ค„์—ฌ์•ผํ•  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ง€๊ธˆ๊นŒ์ง€ ํ•œ ์ด์•ผ๊ธฐ๋ฅผ ์ถ”์ƒ์ ์œผ๋กœ ์š”์•ฝํ•ด๋ณผํ…Œ๋‹ˆ, ๊ตฌ์ฒด์  ์˜ˆ์‹œ๋“ค์„ ๋– ์˜ฌ๋ ค๋ณด์‹œ๋ฉด์„œ ์ฒœ์ฒœํžˆ ์ฝ์–ด๋ณด์‹œ์ฃ .

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

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

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

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

react์˜ context API๋Š” ์ฃผ๋กœ ์ƒํƒœ ๊ด€๋ฆฌ๋ณด๋‹ค๋Š” ์˜์กด์„ฑ ์ฃผ์ž… ๋„๊ตฌ๋กœ ๋” ๋„๋ฆฌ ์“ฐ์ž…๋‹ˆ๋‹ค. vue๋‚˜ svelte ๊ฐ™์€ ๋‹ค๋ฅธ ํ”„๋ ˆ์ž„์›Œํฌ๋„ ์œ ์‚ฌํ•œ ๋ฐฉ๋ฒ•์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ๋ฐ›๊ณ  ๋„˜๊ธฐ๊ณ ๋ฅผ ๋ฐ˜๋ณตํ•  ํ•„์š” ์—†์ด, ์ปดํฌ๋„ŒํŠธ tree๋ฅผ ๋›ฐ์–ด ๋„˜์–ด์„œ ์‰ฝ๊ฒŒ context๋ฅผ ์ฃผ์ž…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ๋Ÿฐํƒ€์ž„ ์˜์กด์„ฑ ์ฃผ์ž…์€ ๋ฒˆ๊ฑฐ๋กญ๊ณ , ์„ฑ๋Šฅ ์ด์Šˆ๊ฐ€ ์žˆ์„ ๋•Œ๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฒˆ๋“ค๋Ÿฌ๋Š” ์ •์ ์œผ๋กœ module alias๋ฅผ ํ†ตํ•ด ๋นŒ๋“œ์‹œ์— ์„ค์ •ํ•œ ์˜์กด์„ฑ๊ณผ ์—ฐ๊ฒฐํ•ด์ค๋‹ˆ๋‹ค. ์ด๋Š” ๊ฐ•๋ ฅํ•œ ๋ฐฉ๋ฒ•์ด์ง€๋งŒ, ๋Ÿฐํƒ€์ž„์— request๋‚˜ test๋งˆ๋‹ค ์ƒํƒœ๊ฐ€ ๊ฒฉ๋ฆฌ๋˜์–ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” ์‚ฌ์šฉํ•˜๊ธฐ ์–ด๋ ต์Šต๋‹ˆ๋‹ค. ์ƒํ™ฉ์— ๋”ฐ๋ผ ์–ด๋–ค ๋ฐฉ๋ฒ•์ด ์ข‹์€์ง€๋Š” ๊ฐœ์ธ์˜ ๊ฒฝํ—˜์ด๋‚˜ ์•ˆ ์ข‹์€ ๊ธฐ์–ต์ด ์•„๋‹ˆ๋ผ, ์‹คํ—˜๊ณผ ๋ฒค์น˜๋งˆํฌ, ํ”„๋กœ์ ํŠธ์˜ ๋งฅ๋ฝ์„ ์ž์„ธํžˆ ์‚ดํ”ผ๊ณ  ๊ฒฐ์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์„ธ์ƒ์— ์˜์กด์„ฑ์„ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์ด ์™ธ์—๋„ ๋งค์šฐ ๋งŽ๊ณ  ๋‹ค์–‘ํ•ฉ๋‹ˆ๋‹ค. ์˜์กด์„ฑ์˜ ์˜์กด์„ฑ์„ ์ดˆ๊ธฐํ™”ํ•˜๋Š” ์˜์กด์„ฑ ๊ทธ๋ž˜ํ”„๋ผ๊ฑฐ๋‚˜, ์˜์กด์„ฑ scope, ์ง์ ‘ ์˜์กด์„ฑ์„ ํŒ๋‹จํ•ด์„œ ์„ ํƒํ•˜๋Š” ์˜์กด์„ฑ ์„ ํƒ, event๋‚˜ message๋ฅผ ์ด์šฉํ•ด์„œ ๋Š์Šจํ•œ ๊ฒฐํ•ฉ์œผ๋กœ ์˜์กด์„ฑ์„ ๊ฒฉ๋ฆฌํ•˜๋Š” ๋ฒ•๋„ ์žˆ๊ณ ์š”. pandacss๋‚˜ tailwind๊ฐ€ CSS๋ผ๋Š” ํ‘œ์ค€์„ ์ •์  ์ปดํŒŒ์ผ๋Ÿฌ์™€ ๊ฒฐํ•ฉํ•ด์„œ ๋‹ค์–‘ํ•œ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์ง€์›ํ•˜๋Š” ๊ฒƒ๋„ ํฅ๋ฏธ๋กญ์Šต๋‹ˆ๋‹ค. ์ตœ๊ทผ์— SSR์ด ๋„์ž…๋˜๊ณ  ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ๋ผ๋Š” ๋‘ ๊ฐœ์˜ ํ™˜๊ฒฝ์„ ์ง€์›ํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค๊ฑฐ๋‚˜. ์ €ํฌ์ฒ˜๋Ÿผ ๋‹ค์–‘ํ•œ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์“ฐ๋Š” ํŒ€์—์„œ ๊ณตํ†ต ๋กœ์ง์„ ๊ด€๋ฆฌํ•˜๋ฉด์„œ ์ƒ๊ฐํ•ด๋ณผ ์ ๋„ ์ ์  ๋งŽ์•„์ง€๊ณ  ์žˆ์ง€ ์•Š๋‚˜ ์‹ถ์Šต๋‹ˆ๋‹ค. ์–ธ์  ๊ฐ€ ๋‚จ์€ ์ด์•ผ๊ธฐ๋„ ํ•  ์ˆ˜ ์žˆ๋Š” ์‹œ๊ฐ„์ด ์ƒ๊ธฐ๊ธธ ๋ฐ”๋ž๋‹ˆ๋‹ค.

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

โ† ๋ชฉ๋ก์œผ๋กœ ๋Œ์•„๊ฐ€๊ธฐ

Art Changes Life

๋…ธ๋จธ์Šค์™€ ํ•จ๊ป˜ ์—”ํ„ฐํ…Œํฌ ์‚ฐ์—…์„ ํ˜์‹ ํ•ด๋‚˜๊ฐˆ ๋ฉค๋ฒ„๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค.

์ฑ„์šฉ ์ค‘์ธ ๊ณต๊ณ  ๋ณด๊ธฐ