January 29, 2024
์๋ฌธ : React Libraries for 2024
๋ฆฌ์กํธ๋ ๊ฝค ์ค๋ซ๋์ ์ฌ์ฉ๋์ด ์์ต๋๋ค. ๊ทธ๋์ ์ปดํฌ๋ํธ ๊ธฐ๋ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ค์ฌ์ผ๋ก ๋ค์ํ๊ณ ๋ฐฉ๋ํ ์ํ๊ณ๋ฅผ ๋ฐ์ ์์ผ ์์ต๋๋ค. ๋ค๋ฅธ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ, ํ๋ ์์ํฌ๋ฅผ ์ฌ์ฉํ๋ ๊ฐ๋ฐ์๋ค์ ์ข ์ข ๋ฆฌ์กํธ๋ฅผ ์ฌ์ฉํ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ง๋ค๊ธฐ ์ํ ๋ชจ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํ์ ํ๋ ๋ฐ ์ด๋ ค์์ ๊ฒช์ ์ ์์ต๋๋ค.
ํต์ฌ์ ์ผ๋ก, ๋ฆฌ์กํธ๋ ํจ์ ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ์ฌ ์ปดํฌ๋ํธ ์ค์ฌ์ ์ฌ์ฉ์ ์ธํฐํ์ด์ค๋ฅผ ์์ฑํ ์ ์๊ฒ ํด ์ค๋๋ค. ๋ฆฌ์กํธ๋ ๋ก์ปฌ ์ํ, ์ฌ์ด๋ ์ดํํธ, ๊ทธ๋ฆฌ๊ณ ์ฑ๋ฅ ์ต์ ํ๋ฅผ ์ํ ๋ฆฌ์กํธ ํ ๊ณผ ๊ฐ์ ๋ด์ฅ ์๋ฃจ์ ์ ์ ๊ณตํฉ๋๋ค. ๊ฒฐ๊ตญ, ํจ์(์ปดํฌ๋ํธ ๋ฐ ํ )๋ง์ ์ฌ์ฉํด์ UI๋ฅผ ๋ง๋ค๊ณ ์๋ ์ ์ ๋๋ค.
๊ณ์ ์ฝ๊ธฐ : ๋ฆฌ์กํธ ํธ๋ ๋
์ด์ ๋ฆฌ์กํธ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฌ์ฉํ ์ ์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋ํด ์์ธํ ์์๋ด ์๋ค.
๋ฆฌ์กํธ ์ด๋ณด์์๊ฒ ๊ฐ์ฅ ๋จผ์ ๋ ์ค๋ฅด๋ ์ง๋ฌธ์ ๋ฆฌ์กํธ ํ๋ก์ ํธ๋ฅผ ์ค์ ํ๋ ๋ฐฉ๋ฒ์ ๋๋ค. ์ ํํ ์ ์๋ ๋๊ตฌ๋ ๋ง์ต๋๋ค. ๋ฆฌ์กํธ ์ปค๋ฎค๋ํฐ์์ ๊ฐ์ฅ ์ธ๊ธฐ ์๋ ๋๊ตฌ๋ ๋ค์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ(์: ๋ฆฌ์กํธ)+์ตํธ์ธ ํ์ ์คํฌ๋ฆฝํธ ์กฐํฉ์ผ๋ก ํ๋ก์ ํธ๋ฅผ ์์ฑํ ์ ์๋ Vite์ ๋๋ค. Vite๋ ๋๋ผ์ด ์ฑ๋ฅ์ ๋ณด์ฌ์ค๋๋ค.
๊ณ์ ์ฝ๊ธฐ : ์น ์ฌ์ดํธ ๋ฐ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ํด ์์ธํ ์์๋ณด๊ธฐ
์ด๋ฏธ ๋ฆฌ์กํธ์ ์ต์ํ๋ค๋ฉด ๊ฐ์ฅ ์ธ๊ธฐ ์๋ (๋ฉํ) ํ๋ ์์ํฌ ์ค ํ๋์ธ Next.js๋ฅผ Vite ๋์ ์ ํํ ์ ์์ต๋๋ค. ์ด ํ๋ ์์ํฌ๋ ๋ฆฌ์กํธ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๊ตฌ์ถ๋์์ผ๋ฏ๋ก ๋ฆฌ์กํธ์ ๊ธฐ๋ณธ ์ฌํญ์ ๋ํด ์๊ณ ์์ด์ผ ํฉ๋๋ค. ์ด ๋ถ์ผ์์ ์ธ๊ธฐ ์๋ ๋์์ Remix์ ๋๋ค.
๊ณ์ ์ฝ๊ธฐ : ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋๋ ํ๋ ์์ํฌ๋ก์ ๋ฆฌ์กํธ๋ฅผ ๋ฐฐ์ฐ๋ ๋ฐฉ๋ฒ
Next.js๋ ์ฒ์์๋ ์๋ฒ ์ฌ์ด๋ ๋ ๋๋ง(์น ์ ํ๋ฆฌ์ผ์ด์ )์ ์ฌ์ฉ๋์์ง๋ง, ๋ค๋ฅธ ๋ ๋๋ง ํจํด์ ์ด์ด ์ ์ ์ฌ์ดํธ ์์ฑ(SSG)์๋ ์ฌ์ฉํ ์ ์์ต๋๋ค. Next.js์ ๊ฐ์ฅ ์ต๊ทผ์ ์ถ๊ฐ๋ ๊ฒ์ 2023๋ ๋ถํฐ ๋ฆฌ์กํธ ์ปดํฌ๋ํธ๋ฅผ ํด๋ผ์ด์ธํธ์์ ์๋ฒ๋ก ์ด๋ํ์ฌ ํจ๋ฌ๋ค์ ์ ํ์ ๊ธฐ์ฌํ๋ ๋ฆฌ์กํธ ์๋ฒ ์ปดํฌ๋ํธ์ ๋๋ค.
์ ์ ์ฝํ ์ธ ๋ฅผ ์ํ ์ต๊ณ ์ ์ฑ๋ฅ์ ์ผ๋์ ๋ ํ๋ ์์ํฌ๋ฅผ ์ฐพ๊ณ ์๋ค๋ฉด, ํ๋ ์์ํฌ์ ๊ตฌ์ ๋ฐ์ง ์๊ณ ๋ฆฌ์กํธ์ ํจ๊ป ์ฌ์ฉํ ์ ์๋ Astro๋ฅผ ํ์ธํด ๋ณด์ธ์. ์ด ํ๋ ์์ํฌ๋ ์ปดํฌ๋ํธ ์์ฑ์ ๋ฆฌ์กํธ์ ๊ฐ์ ํ๋ ์์ํฌ๊ฐ ์ฌ์ฉ๋๋๋ผ๋ ๋ธ๋ผ์ฐ์ ์ HTML๊ณผ CSS๋ง ์ ์กํฉ๋๋ค. ์ด๋ฌํ ์ปดํฌ๋ํธ๊ฐ ์ธํฐ๋ ํฐ๋ธํด์ง ๊ฒฝ์ฐ์๋ง ํด๋ผ์ด์ธํธ์์ ํ์ํ ์๋ฐ์คํฌ๋ฆฝํธ๋ฅผ ์์ฒญํฉ๋๋ค.
๊ณ์ ์ฝ๊ธฐ : ๋ฆฌ์กํธ ํ๋ก์ ํธ๋ฅผ ์์ํ๋ ๋ฐฉ๋ฒ
Vite์ ๊ฐ์ ๋๊ตฌ๊ฐ ์ด๋ป๊ฒ ์๋ํ๋์ง ์ดํดํ๊ณ ์ถ๋ค๋ฉด ์ง์ ๋ฆฌ์กํธ ํ๋ก์ ํธ๋ฅผ ์ธํ ํด ๋ณด์ธ์. ์๋ฐ์คํฌ๋ฆฝํธ๊ฐ ํฌํจ๋ HTML ํ๋ก์ ํธ๋ก ์์ํ์ฌ ์ง์ ๋๊ตฌ(์: Webpack, Babel)์ ํจ๊ป ๋ฆฌ์กํธ๋ฅผ ์ง์ ์ถ๊ฐํ ์ ์์ต๋๋ค. ํนํ Vite๊ฐ Webpack์ ๊ณ์น์๊ฐ ๋์๊ธฐ ๋๋ฌธ์ ์ผ์์ ์ธ ์์ ์์ ๊ผญ ๋ค๋ค์ผ ํ๋ ๊ฒ์ ์๋์ง๋ง, ๊ธฐ๋ณธ ๋๊ตฌ์ ๋ํด ์์๊ฐ๋ ๊ฒ์ ํ๋ฅญํ ํ์ต ๊ฒฝํ์ด ๋ ๊ฒ์ ๋๋ค.
๋ฆฌ์กํธ์ ๋ฒ ํ ๋์ด๊ณ ์๋ก์ด ๊ฒ์ ์๋ํด๋ณด๊ณ ์ถ๋ค๋ฉด Nitro ๋๋ Waku๋ฅผ ํ์ธํด ๋ณด์ธ์. ํ์๋ Zustand์ ๊ฐ๋ฐ์๊ฐ ๋ง๋ ๊ฒ์ผ๋ก, ๋ฆฌ์กํธ ์๋ฒ ์ปดํฌ๋ํธ๋ฅผ ์ง์ํฉ๋๋ค.
์๋ฐ์คํฌ๋ฆฝํธ ์ํ๊ณ(์ฆ, ๋ฆฌ์กํธ)์์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ(์์กด์ฑ, ๋ ธ๋ ํจํค์ง)๋ฅผ ์ค์นํ๋ ๋ฐ ๊ฐ์ฅ ๋๋ฆฌ ์ฌ์ฉ๋๋ ํจํค์ง ๊ด๋ฆฌ์๋ npm์ผ๋ก, ๋ชจ๋ Node.js ์ค์น์ ํจ๊ป ์ ๊ณต๋๊ธฐ ๋๋ฌธ์ ๋๋ค. ํ์ง๋ง yarn๊ณผ pnpm๋ ํ๋ฅญํ ๋์์ด๋ฉฐ, ํนํ ํ์๋ ๋ ํฐ ์ฑ๋ฅ ํฅ์์ ์ ๊ณตํฉ๋๋ค.
๊ณ์ ์ฝ๊ธฐ : ์น ๊ฐ๋ฐ์ ์ํ Mac ์ค์
์๋ก ์์กดํ๊ฑฐ๋ ๊ณตํต๋ ์ฌ์ฉ์ ์ ์ UI ์ปดํฌ๋ํธ ์ธํธ๋ฅผ ๊ณต์ ํ๋ ์ฌ๋ฌ ๊ฐ์ ๋ฆฌ์กํธ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ง๋๋ ๊ฒฝ์ฐ ๋ชจ๋ ธ๋ ํฌ๋ผ๋ ๊ฐ๋ ์ ํ์ธํด ๋ณผ ์ ์์ต๋๋ค. ์์ ์ธ๊ธํ ๋ชจ๋ ํจํค์ง ๊ด๋ฆฌ์๋ ์์ฒด ์์ ๊ณต๊ฐ ๊ธฐ๋ฅ์ ์ฌ์ฉํ์ฌ ๋ชจ๋ ธ๋ ํฌ๋ฅผ ์์ฑํ ์ ์์ง๋ง, ๊ฐ์ธ์ ์ผ๋ก๋ yarn ๋๋ pnpm์ ์ฌ์ฉํ๋ ๊ฒ์ด ๊ฐ์ฅ ์ข์ ๊ฐ๋ฐ์ ๊ฒฝํ์ด์์ต๋๋ค. Turborepo์ ๊ฐ์ ๋ชจ๋ ธ๋ ํฌ ํ์ดํ๋ผ์ธ ๋๊ตฌ์ ํจ๊ป ์ฌ์ฉํ๋ฉด ์๋ฒฝํ ๋ชจ๋ ธ๋ ํฌ ๊ฒฝํ์ด ๋ ๊ฒ์ ๋๋ค.
๊ณ์ ์ฝ๊ธฐ : ๋ชจ๋ ธ๋ ํฌ ์ค์
ํ๋์ ํจํค์ง ๊ด๋ฆฌ์๋ฅผ ์ ํํ๊ณ ๊ทธ๊ฒ๋ง ์ฌ์ฉํ์ธ์.
๋ฆฌ์กํธ์๋ ๋ก์ปฌ ์ํ๋ฅผ ๊ด๋ฆฌํ๊ธฐ ์ํ ๋ ๊ฐ์ง ๋ด์ฅ ํ , ์ฆ useState์ useReducer๊ฐ ์์ต๋๋ค. ์ํ๋ฅผ ์ ์ญ์ ์ผ๋ก ๊ด๋ฆฌํด์ผ ํ๋ ๊ฒฝ์ฐ, ๋ฆฌ์กํธ์ ๋ด์ฅ๋ useContext ํ ์ ์ฌ์ฉํ์ฌ ํ๋กญ์ ์ฌ์ฉํ์ง ์๊ณ ๋ ํ๋กญ์ ์ต์์ ์ปดํฌ๋ํธ์์ ํ์ ์ปดํฌ๋ํธ๋ก ํฐ๋๋ง ํ ์ ์์ต๋๋ค. ์ด๋ก์จ ํ๋กญ ๋๋ฆด๋ง ๋ฌธ์ ๋ฅผ ํผํ ์ ์์ต๋๋ค.
๊ณ์ ์ฝ๊ธฐ : ์ธ์ useState ์ useReducer๋ฅผ ์ฌ์ฉํด์ผ ํ๋์ง ์์๋ณด๊ธฐ
๋ฆฌ์กํธ์ useState/useReducer ํ ์ ์ฌ์ฉํ์ฌ ์ปดํฌ๋ํธ์ ํจ๊ป ๋ฐฐ์นํ๊ฑฐ๋, ๋ฆฌ์กํธ์ useContext ํ ๊ณผ ๊ฒฐํฉํ์ฌ ์ ์ญ์ ์ผ๋ก ๊ด๋ฆฌํ ์๋ ์์ต๋๋ค. ์ธ ๊ฐ์ง ๋ฆฌ์กํธ ํ ์ ํตํด ๊ฐ๋ฐ์๋ ๊ฐ๋ ฅํ ์ํ ๊ด๋ฆฌ๋ฅผ ๋ฆฌ์กํธ์์ ๊ตฌํํ ์ ์์ต๋๋ค.
๊ณ์ ์ฝ๊ธฐ : useState/useReducer๋ฅผ useContext์ ๊ฒฐํฉํ๋ ๋ฐฉ๋ฒ ์์๋ณด๊ธฐ
๊ณต์ ๋๊ฑฐ๋, ์ ์ญ ์ํ์ ๋ฆฌ์กํธ์ ์ปจํ ์คํธ๋ฅผ ๋๋ฌด ์์ฃผ ์ฌ์ฉํ๋ค๋ฉด, Zustand๋ฅผ ๊ผญ ํ์ธํด ๋ณด์ธ์. ์ด๋ฅผ ์ฌ์ฉํ๋ฉด ์คํ ์ด์ ์ฐ๊ฒฐ๋ ๋ชจ๋ ๋ฆฌ์กํธ ์ปดํฌ๋ํธ์์ ์ฝ๊ณ ์์ ํ ์ ์๋ ์ ์ญ ์ ํ๋ฆฌ์ผ์ด์ ์ํ๋ฅผ ๊ด๋ฆฌํ ์ ์์ต๋๋ค.
Zustand๊ฐ ์ปค๋ฎค๋ํฐ์์ ์ฌ์ค์ ํ์ค์ด ๋์๋ค๊ณ ๋งํ์ง๋ง, ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ํ์ํ๋ค๋ฉด ์ฌ์ ํ ๊ฐ์ฅ ์ธ๊ธฐ ์๋ ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ธ Redux๋ฅผ ๋นผ๋์ ์ ์์ ๊ฒ์ ๋๋ค. ์ ๋ Zustand์ ๋จ์์ฑ์ ์ข์ํ๊ธฐ ๋๋ฌธ์ Redux๋ฅผ ์ง๋ ๋ช ๋ ๊ฐ์ ๊ฐ์ธ์ ์ธ ํ๋ฆฌ๋์ ํ๋ก์ ํธ์์ ์ฌ์ฉํ์ง๋ ์์์ง๋ง, Redux์ ํจ๊ป ์ ๊ณต๋๋ ์ค๋๋ ๋ฆฌ์กํธ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ง์ด ์ฐพ์ ์ ์์ต๋๋ค.
๊ณ์ ์ฝ๊ธฐ : Redux ํํ ๋ฆฌ์ผ(Redux Toolkit ์ ์ธ)
Redux๋ฅผ ์ฌ์ฉํ์ ๋ค๋ฉด Redux Toolkit๋ ๊ผญ ํ์ธํด ๋ณด์ธ์. ๊ทธ๋ฆฌ๊ณ ์ํ ๋จธ์ ์ ๊ด์ฌ์ด ์๋ค๋ฉด XState ๋๋ Zag๋ฅผ ํ์ธํด ๋ณด์ธ์. ์ ์ญ ์คํ ์ด๊ฐ ํ์ํ์ง๋ง Zustand๋ Redux๊ฐ ๋ง์์ ๋ค์ง ์๋๋ค๋ฉด Jotai, Recoil ๋๋ Nano Stores์ ๊ฐ์ ๋ค๋ฅธ ์ธ๊ธฐ ์๋ ๋ก์ปฌ ์ํ ๊ด๋ฆฌ ์๋ฃจ์ ์ ํ์ธํด ๋ณด์ธ์.
UI์ ์ํ๋ ๋ฆฌ์กํธ ๋ด์ฅ ํ ์ผ๋ก๋ ์ถฉ๋ถํ์ง๋ง, ์๊ฒฉ ๋ฐ์ดํฐ(์ฆ, ๋ฐ์ดํฐ ํ์นญ)์ ๋ํ ์ํ ๊ด๋ฆฌ(์บ์ฑ)์ ๊ดํด์๋ TanStack Query(์ด์ ์ React Query)์ ๊ฐ์ ์ ์ฉ ๋ฐ์ดํฐ ํ์นญ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๊ถ์ฅํฉ๋๋ค.
TanStack Query ์์ฒด๋ ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก ๊ฐ์ฃผ๋์ง ์์ง๋ง, ์ฃผ๋ก API์์ ์๊ฒฉ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ๋ฐ ์ฌ์ฉ๋๊ธฐ ๋๋ฌธ์ ์๊ฒฉ ๋ฐ์ดํฐ์ ๋ชจ๋ ์ํ ๊ด๋ฆฌ(์: ์บ์ฑ, ๋๊ด์ ์ ๋ฐ์ดํธ)๋ฅผ ๋์ ์ฒ๋ฆฌํด ์ค๋๋ค.
๊ณ์ ์ฝ๊ธฐ : TanStack Query๊ฐ ๋ด๋ถ์์ ์ด๋ป๊ฒ ์๋ํ๋์ง ์์๋ณด๊ธฐ
TanStack Query๋ REST API๋ฅผ ์ฌ์ฉํ๋๋ก ์ค๊ณ๋์์ต๋๋ค. ํ์ง๋ง ์์ฆ์ GraphQL๋ ์ง์ํฉ๋๋ค. ๊ทธ๋ฌ๋ ๋ฆฌ์กํธ ํ๋ฐํธ์๋๋ฅผ ์ํ ๋ณด๋ค ์ ์ฉ์ ์ธ GraphQL ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฐพ๊ณ ์๋ค๋ฉด ์ธ๊ธฐ ์๋ Apollo Client๋ ๊ฐ๋ฒผ์ด urql ๋๋ Facebook์์ ์ ๊ณตํ๋ Relay ์ค ํ๋๋ฅผ ํ์ธํด ๋ณด์ธ์.
๊ณ์ ์ฝ๊ธฐ : ๋ก์ปฌ ๋ฐ ์๊ฒฉ ๋ฐ์ดํฐ์ ๋ํ ๋ฆฌ์กํธ ์ํ์ ๊ดํ ๋ชจ๋ ๊ฒ
์ด๋ฏธ Redux๋ฅผ ์ฌ์ฉ ์ค์ด๊ณ Redux์์ ํตํฉ ์ํ ๊ด๋ฆฌ์ ํจ๊ป ๋ฐ์ดํฐ ํ์นญ์ ์ถ๊ฐํ๋ ค๋ ๊ฒฝ์ฐ, TanStack Query๋ฅผ ์ถ๊ฐํ๋ ๋์ ๋ฐ์ดํฐ ํ์นญ์ Redux์ ๊น๋ํ๊ฒ ํตํฉํ๋ RTK Query๋ฅผ ํ์ธํด ๋ณผ ์ ์์ต๋๋ค.
๋ง์ง๋ง์ผ๋ก, ํ๋ฐํธ์๋์ ๋ฐฑ์๋(๋ ๋ค ํ์ ์คํฌ๋ฆฝํธ)๋ฅผ ์ ์ดํ๋ ๊ฒฝ์ฐ, ์๋ํฌ์๋ ํ์ ์์ ํ API๋ฅผ ์ํด tRPC๋ฅผ ํ์ธํด ๋ณด์ธ์. ์ ๋ ์ง๋ 1๋ ๋์ ์ด ๋๊ตฌ๋ฅผ ์ฌ์ฉํด ์๋๋ฐ ์์ฐ์ฑ ํฅ์๊ณผ DX์ ์์ฒญ๋ ๋์์ด ๋์์ต๋๋ค. TanStack Query์ ๊ฒฐํฉํ์ฌ ๋ฐ์ดํฐ ํ์นญ๊ณผ ๊ด๋ จ๋ ๋ชจ๋ ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์์ผ๋ฉฐ, ๋์์ ํ์ ์ด ์ง์ ๋ ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ํ๋ฐํธ์๋์์ ๋ฐฑ์๋๋ฅผ ํธ์ถํ ์ ์์ต๋๋ค.
๊ณ์ ์ฝ๊ธฐ : E2E ํ์ ์์ ์ฑ์ ๊ฐ์ถ ์ฒซ ๋ฒ์งธ tRPC ์ ํ๋ฆฌ์ผ์ด์ ๋ง๋ค๊ธฐ
TanStack Query(REST API ๋๋ GraphQL API)
Next.js์ ๊ฐ์ ๋ฆฌ์กํธ ํ๋ ์์ํฌ๋ ์ด๋ฏธ ๋ผ์ฐํ ์ด ์ฒ๋ฆฌ๋์ด ์์ต๋๋ค. ๊ทธ๋ฌ๋ ํ๋ ์์ํฌ ์์ด ํด๋ผ์ด์ธํธ ์ธก ๋ ๋๋ง์๋ง ๋ฆฌ์กํธ๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ(์: SSR์ด ์๋ Vite), ๊ฐ์ฅ ๊ฐ๋ ฅํ๊ณ ์ธ๊ธฐ ์๋ ๋ผ์ฐํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ React Router์ ๋๋ค. ์์ ํ ํ์ ์คํฌ๋ฆฝํธ ์ง์์ ์ผ๋์ ๋ ์๋ก์ด ๋์์ผ๋ก TanStack Router๊ฐ ์์ต๋๋ค.
๊ณ์ ์ฝ๊ธฐ : React Router ์ฌ์ฉ๋ฒ ์์๋ณด๊ธฐ
๋ฆฌ์กํธ ๋ผ์ฐํฐ์ ํจ๊ป ๋ฆฌ์กํธ์์ ํด๋ผ์ด์ธํธ ์ธก ๋ผ์ฐํ
์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ, ๋ผ์ฐํธ ์์ค์์ ์ฝ๋ ๋ถํ ์ ๋์
ํ๋ ๊ฒ์ ๊ทธ๋ฆฌ ์ด๋ ค์ด ์ผ์ด ์๋๋๋ค. ์ด๋ฐ ์ข
๋ฅ์ ์ต์ ํ๋ฅผ ๋์
ํ๋ ๊ฒฝ์ฐ, React.lazy()
๋ฅผ @loadable/component
๋ก ๋์ฒดํ ์ ์์ต๋๋ค.
๊ณ์ ์ฝ๊ธฐ : React Router๋ก ์ง์ฐ ๋ก๋ฉ ๋ฐฐ์ฐ๊ธฐ
๋ฆฌ์กํธ ํ๋ก์ ํธ์ ๋ผ์ฐํฐ๋ฅผ ๋์ ํ๊ธฐ ์ ์, ์ด์ ๋ง ๋ฆฌ์กํธ๋ฅผ ๋ฐฐ์ฐ๊ธฐ ์์ํ์ ๋, ๋ฆฌ์กํธ์ ์กฐ๊ฑด๋ถ ๋ ๋๋ง์ ๋จผ์ ์ฌ์ฉํด ๋ณผ ์ ์์ต๋๋ค. ๋ผ์ฐํ ์ ๋์ฒดํ๋ ๊ฒ์ ์๋์ง๋ง, ํ์ด์ง ์์ค์์ ์ปดํฌ๋ํธ๋ฅผ ๊ต์ฒดํ๋ ๊ฒ์ด ์ด๋ป๊ฒ ์๋ํ๋์ง ์ฟ๋ณผ ์ ์์ต๋๋ค.
์์ฆ ๋ ์ค๋ฅด๋ ๊ฒ์ TanStack Router
๋ฆฌ์กํธ์ ์คํ์ผ๋ง/CSS์ ๋ํ ๋ง์ ์ต์ ๊ณผ ํจ์ฌ ๋ ๋ง์ ์๊ฒฌ์ด ์๊ธฐ ๋๋ฌธ์ ์ฌ๊ธฐ ํ ์น์ ์ ๋ชจ๋ ๊ฒ์ ๋ด๋ ๊ฒ๋ง์ผ๋ก๋ ์ถฉ๋ถํ์ง ์์ต๋๋ค. ์ด ์ฃผ์ ์ ๋ํด ๋ ์์ธํ ์์๋ณด๊ณ ๋ชจ๋ ์ต์ ์ ๋ํด ์๊ณ ์ถ๋ค๋ฉด ๋ค์ ๊ฐ์ด๋๋ฅผ ํ์ธํ์ธ์.
๊ณ์ ์ฝ๊ธฐ : ๋ฆฌ์กํธ CSS ์คํ์ผ๋ง(์ข ํฉ ํํ ๋ฆฌ์ผ)
๋ฆฌ์กํธ ์ด๋ณด์๋ JSX์์ ์คํ์ผ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ์ฌ ์ธ๋ผ์ธ ์คํ์ผ๊ณผ ๊ธฐ๋ณธ CSS๋ก ์์ํด๋ ๊ด์ฐฎ์ต๋๋ค. ํ์ง๋ง ์ค์ ์ ํ๋ฆฌ์ผ์ด์ ์๋ ๊ฑฐ์ ์ฌ์ฉํ์ง ์๋๊ฒ์ด ์ข์ต๋๋ค.
const Headline = ({ title }) => <h1 style={{ color: 'blue' }}>{title}</h1>;
์ธ๋ผ์ธ ์คํ์ผ์ ๋ฆฌ์กํธ์ JSX์์ ์๋ฐ์คํฌ๋ฆฝํธ๋ก ์คํ์ผ์ ๋์ ์ผ๋ก ์ถ๊ฐํ๋ ๋ฐ ์ฌ์ฉํ ์ ์์ง๋ง, ์ธ๋ถ CSS ํ์ผ์ ๋ฆฌ์กํธ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋๋จธ์ง ์คํ์ผ์ ๋ชจ๋ ๋ด์ ์ ์์ต๋๋ค.
import './Headline.css';
const Headline = ({ title }) => (
<h1 className="headline" style={{ color: 'blue' }}>
{title}
</h1>
);
์ ํ๋ฆฌ์ผ์ด์ ์ ํฌ๊ธฐ๊ฐ ์ปค์ง๋ฉด ๋ค๋ฅธ ์คํ์ผ๋ง ๋ฐฉ๋ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
๋จผ์ , ์ฌ๋ฌ CSS-in-CSS ์๋ฃจ์ ์ค ํ๋์ธ CSS Module์ ์ดํด๋ณผ ๊ฒ์ ๊ถ์ฅํฉ๋๋ค. CSS Module์ ์ฌ์ฉํ๋ฉด CSS๋ฅผ ์ปดํฌ๋ํธ์ ํจ๊ป ๋ฐฐ์น๋ ๋ชจ๋๋ก ์บก์ํํ ์ ์์ต๋๋ค. ์ด๋ ์คํ์ผ์ด ๋ค๋ฅธ ์ปดํฌ๋ํธ๋ก ์ค์๋ก ์ ์ถ๋๋ ๊ฒ์ ๋ฐฉ์งํฉ๋๋ค.
import styles from './style.module.css';
const Headline = ({ title }) => <h1 className={styles.headline}>{title}</h1>;
๋ ๋ฒ์งธ๋ก, ๋ฆฌ์กํธ๋ฅผ ์ํ ๋ง์ CSS-in-JS ์๋ฃจ์ ์ค ํ๋์ธ Styled Components๋ฅผ ์๊ฐํ๊ณ ์ ํฉ๋๋ค(๋ ์ด์ ๊ถ์ฅํ์ง ์์ต๋๋ค). ์ด ์ ๊ทผ ๋ฐฉ์์ Styled-Components(๋๋ emotion๊ณผ ๊ฐ์ ๋์)๋ผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํตํด ์ ๊ณต๋๋๋ฐ, ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์๋ฐ์คํฌ๋ฆฝํธ๋ก ์์ฑ๋ ์คํ์ผ๋ง์ ๊ฐ์ ํ์ผ์ ๋ฆฌ์กํธ ์ปดํฌ๋ํธ ์ ๋๋ ๊ฐ์ ์์น์ ์๋ ํ์ผ์ ๋ฐฐ์นํฉ๋๋ค.
import styled from 'styled-components';
const BlueHeadline = styled.h1`
color: blue;
`;
const Headline = ({ title }) => <BlueHeadline>{title}</BlueHeadline>;
๊ณ์ ์ฝ๊ธฐ : Styled Components ๋ชจ๋ฒ ์ฌ๋ก
์ ์งธ, ๊ฐ์ฅ ์ธ๊ธฐ ์๋ Utility-First-CSS ์๋ฃจ์ ์ผ๋ก Tailwind CSS๋ฅผ ์ถ์ฒํ๊ณ ์ถ์ต๋๋ค. ์ด ์๋ฃจ์ ์ ์ฌ์ ์ ์๋ CSS ํด๋์ค์ ํจ๊ป ์ ๊ณต๋ฉ๋๋ค. ์ด๋ฅผ ํตํด ๊ฐ๋ฐ์์ ํจ์จ์ฑ์ ๋์ด๊ณ ๋ฆฌ์กํธ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋์์ธ ์์คํ ์ ๊ฐ์ํํ ์ ์์ง๋ง, ๋ชจ๋ ํด๋์ค๋ฅผ ์์์ผ ํ๊ณ ๋ง์ CSS ํด๋์ค์ ์ฅํฉํ ์ธ๋ผ์ด๋์ด ํ์ํ๋ค๋ ๋จ์ ์ด ์์ต๋๋ค.
const Headline = ({ title }) => <h1 className="text-blue-700">{title}</h1>;
CSS-in-CSS๋ฅผ ์ ํํ ์ง, Utility-First-CSS๋ฅผ ์ ํํ ์ง๋ ์ฌ์ฉ์์๊ฒ ๋ฌ๋ ค ์์ต๋๋ค. ์ต๊ทผ์ ์ถ์ธ๋ Tailwind CSS๋ฅผ ์ฌ์ฉํ Utility-First-CSS์ ๋๋ค. CSS-in-JS ์๋ฃจ์ ์ ์๋ฒ ์ธก ํ๊ฒฝ๊ณผ ๊ด๋ จ๋ ๋ฌธ์ ๋ก ์ธํด ๋ ์ด์ ์ธ๊ธฐ๊ฐ ์์ต๋๋ค. ๋ง์ง๋ง์ผ๋ก ํ ๊ฐ์ง ํํธ๋ฅผ ๋๋ฆฌ์๋ฉด, ๋ฆฌ์กํธ์์ className์ ์กฐ๊ฑด๋ถ๋ก ์ ์ฉํ๋ ค๋ฉด clsx์ ๊ฐ์ ์ ํธ๋ฆฌํฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ธ์.
Utility-First-CSS (๊ฐ์ฅ ์ธ๊ธฐ ์์)
CSS-in-CSS
CSS-in-JS(๊ฐ์ธ์ ์ผ๋ก๋ ์๋ฒ ์ฌ์ด๋ ํ๊ฒฝ์์์ ์ฑ๋ฅ ๋ฐ ๊ธฐํ ๋ฌธ์ ๋ก ์ธํด ๋ฐํ์ CSS-in-JS๋ฅผ ๋ ์ด์ ๊ถ์ฅํ์ง ์์ต๋๋ค.)
์ด๋ณด์๋ผ๋ฉด ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ์ปดํฌ๋ํธ๋ฅผ ์ฒ์๋ถํฐ ๋ง๋ค์ด ๋ณด๋ ๊ฒ์ ์ข์ ํ์ต ๊ฒฝํ์ด ๋ ๊ฒ์ ๋๋ค. ๋๋กญ๋ค์ด, ์ ๋ ํธ, ๋ผ๋์ค ๋ฒํผ, ์ฒดํฌ๋ฐ์ค๋ฑ ๋ฌด์์ด๋ ์ธ์ ๊ฐ ์ด๋ฌํ UI ์ปดํฌ๋ํธ๋ฅผ ์ง์ ๋ง๋๋ ๋ฐฉ๋ฒ์ ์๊ฒ ๋ ๊ฒ์ ๋๋ค.
ํ์ง๋ง ๋ชจ๋ ์ปดํฌ๋ํธ๋ฅผ ์ง์ ๋ง๋ค ๋ฆฌ์์ค๊ฐ ์๋ค๋ฉด ๋์ผํ ๋์์ธ ์์คํ , ๊ธฐ๋ฅ ๋ฐ ์ ๊ทผ์ฑ ๊ท์น์ ๊ณต์ ํ๋ ์ฌ์ ์ ๊ตฌ์ถ๋ ๋ง์ ์ปดํฌ๋ํธ์ ์ ๊ทผํ ์ ์๋ UI ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋๊ฒ์ด ์ข์ต๋๋ค.
ํ์ง๋ง ์ต๊ทผ์ ํธ๋ ๋๋ ํค๋๋ฆฌ์ค UI ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ ํธํ๋ ์ถ์ธ์ ๋๋ค. ํค๋๋ฆฌ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์คํ์ผ๋ง ์์ด ์ ๊ณต๋์ง๋ง, ์ต์ ์ปดํฌ๋ํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ํ์ํ ๋ชจ๋ ๊ธฐ๋ฅ๊ณผ ์ ๊ทผ์ฑ์ ๊ฐ์ถ๊ณ ์์ต๋๋ค. ๋๋ถ๋ถ Tailwind์ ๊ฐ์ ์ ํธ๋ฆฌํฐ ์ฐ์ CSS ์๋ฃจ์ ๊ณผ ํจ๊ป ์ฌ์ฉ๋ฉ๋๋ค.
๋ค๋ฅธ UI ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ณผ ๋น๊ตํ์ ๋, ์ธ๊ธฐ๊ฐ ๋ ํ ๊ฒ์ผ๋ก ๋ณด์ ๋๋ค.
์ด๋ฌํ ๋ชจ๋ UI ๋ผ์ด๋ธ๋ฌ๋ฆฌ์๋ ์์ฒด ์ปดํฌ๋ํธ๊ฐ ํฌํจ๋์ด ์์ง๋ง, ๊ฐ ์ปดํฌ๋ํธ๋ฅผ ํ๋์ UI ์ปดํฌ๋ํธ์ ์ค์ ์ ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋งํผ ๊ฐ๋ ฅํ๊ฒ ๋ง๋ค ์๋ ์์ต๋๋ค. ์๋ฅผ ๋ค์ด, react-table-library๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฆฌ์กํธ์์ ๊ฐ๋ ฅํ ํ ์ด๋ธ ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค ์ ์๋ ๋์์, ์ด๋ฅผ UI ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๊ฒฐํฉํ ์ ์๋ ํ ๋ง(์: Material UI)๋ ์ ๊ณตํ ์ ์์ต๋๋ค.
์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ชจ๋ ์ ๋๋ฉ์ด์ ์ CSS๋ก ์์ํฉ๋๋ค. ๊ฒฐ๊ตญ CSS ์ ๋๋ฉ์ด์ ๋ง์ผ๋ก๋ ์๊ตฌ ์ฌํญ์ ์ถฉ์กฑ์ํค๊ธฐ ์ด๋ ต๋ค๋ ๊ฒ์ ์๊ฒ ๋ ๊ฒ์ ๋๋ค. ๋ณดํต ๊ฐ๋ฐ์๋ค์ ๊ทธ๋ด ๋ ๋ฆฌ์กํธ ์ปดํฌ๋ํธ๋ก ์ ๋๋ฉ์ด์ ์ ๊ตฌํํ ์ ์๋ React Transition Group์ ์ดํด๋ด ๋๋ค. ๋ฆฌ์กํธ๋ฅผ ์ํ ๋ค๋ฅธ ์ ์๋ ค์ง ์ ๋๋ฉ์ด์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
์ฐจํธ๋ฅผ ์ฒ์๋ถํฐ ์ง์ ๋ง๋ค๊ณ ์ถ๋ค๋ฉด D3๋ฅผ ์ฌ์ฉํ๋ ๊ฒ ์ธ์๋ ๋ฐฉ๋ฒ์ด ์์ต๋๋ค. ๋ฉ์ง ์ฐจํธ๋ฅผ ๋ง๋๋ ๋ฐ ํ์ํ ๋ชจ๋ ๊ฒ์ ์ ๊ณตํ๋ ์ ์์ค ์๊ฐํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๊ธฐ ๋๋ฌธ์ ๋๋ค. ๊ทธ๋ฌ๋ D3๋ฅผ ๋ฐฐ์ฐ๋ ๊ฒ์ ์์ ํ ๋ค๋ฅธ ๋ชจํ์ด๊ธฐ ๋๋ฌธ์, ๋ง์ ๊ฐ๋ฐ์๊ฐ ์ ์ฐ์ฑ ๋์ ๋ชจ๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ ๋ฆฌ์กํธ ์ฐจํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ ํํฉ๋๋ค. ์ธ๊ธฐ ์๋ ์๋ฃจ์ ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
Recharts (๊ฐ์ธ์ ์ธ ์ถ์ฒ)
์ฆ์ ์ฌ์ฉ ๊ฐ๋ฅํ ์ฐจํธ๊ฐ ๋ ๋ง์ง๋ง, ์ปค์คํฐ๋ง์ด์ง์ด ๋ ์ด๋ ค์
๋ฆฌ์กํธ์์ ๊ฐ์ฅ ๋ง์ด ์ฌ์ฉ๋๋ ํผ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ React Hook Form์ ๋๋ค. ์ ํจ์ฑ ๊ฒ์ฌ(๊ฐ์ฅ ๋ง์ด ์ฌ์ฉ๋๋ ํตํฉ์ zod), ์์ ์ ์ถ, ์์ ์ํ ๊ด๋ฆฌ ๋ฑ ํ์ํ ๋ชจ๋ ๊ฒ์ด ํฌํจ๋์ด ์์ต๋๋ค. ๋์์ผ๋ก Formik๊ณผ React Final Form์ด ์์ต๋๋ค. ๋ฆฌ์กํธ UI ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ ์ด๋ฌํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค ํ๋์ ์ด๋ป๊ฒ ํตํฉ๋๋์ง ํ์ธํด ๋ณด์ธ์.
๊ณ์ ์ฝ๊ธฐ : ๋ฆฌ์กํธ์์ ํผ์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ
React Hook Form
๋ฆฌ์กํธ๋ PropTypes๋ผ๋ ๋ด๋ถ ํ๋กญ ์ ํจ์ฑ ๊ฒ์ฌ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค. PropTypes๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฆฌ์กํธ ์ปดํฌ๋ํธ์ ๋ํ ํ๋กญ๋ฅผ ์ ์ํ ์ ์์ต๋๋ค. ์๋ชป๋ ํ์ ์ ํ๋กญ์ด ์ปดํฌ๋ํธ์ ์ ๋ฌ๋ ๋๋ง๋ค ์ค๋ฅ ๋ฉ์์ง๊ฐ ํ์๋ฉ๋๋ค.
import PropTypes from 'prop-types';
const List = ({ list }) => (
<div>
{list.map(item => (
<div key={item.id}>{item.title}</div>
))}
</div>
);
List.propTypes = {
list: PropTypes.array.isRequired,
};
๊ทธ๋ฌ๋ PropTypes๋ ๋ ์ด์ ๋ฆฌ์กํธ์ ํฌํจ๋์ง ์์ต๋๋ค. ์ ๋ ์ฌ์ฉ์ ์ถ์ฒํ์ง ์์ง๋ง, ์ญ์ฌ์ ์ธ ์ด์ ๋ก ์ฌ์ ํ ์ฌ๊ธฐ์ ๋จ๊ฒจ๋๊ณ ์์ต๋๋ค.
์ ๊ณ ํ์ค์ ๋ฆฌ์กํธ ์ ํ๋ฆฌ์ผ์ด์ ์์ ํ์ ์คํฌ๋ฆฝํธ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๋๋ค. ์์ฆ ํ์ ์คํฌ๋ฆฝํธ ์์ด ์๋ก ์์ํ๋ ๋ฆฌ์กํธ ํ๋ก์ ํธ๋ ๊ฑฐ์ ์์ผ๋ฏ๋ก ์ฌ๋ฌ๋ถ๋ ์ด์ ์ต์ํด์ ธ์ผ ํฉ๋๋ค.
type Item = {
id: string;
title: string;
};
type ListProps = {
list: Item[];
};
const List = ({ list }: ListProps) => (
<div>
{list.map(item => (
<div key={item.id}>{item.title}</div>
))}
</div>
);
์์ฆ์ ํ์ ์คํฌ๋ฆฝํธ๊ฐ ๋์ธ์ ๋๋ค. ํ์ ์ด ์ง์ ๋ ์์ ์ ํจ์ฑ ๊ฒ์ฌ, API ์ ํจ์ฑ ๊ฒ์ฌ(์: tRPC ์ฌ์ฉ ์)๋ฑ์ ์ํด ํ์ ์คํฌ๋ฆฝํธ๋ฅผ ๋์ด์๊ณ ์ถ๋ค๋ฉด Zod๋ฅผ ํ์ธํด ๋ณด์ธ์.
ํต์ผ๋๊ณ ์ผ๋ฐ์ ์ธ ์ฝ๋ ์คํ์ผ์ ์ ์งํ๊ณ ์ถ๋ค๋ฉด ๋ฆฌ์กํธ ํ๋ก์ ํธ์์ ESLint๋ฅผ ์ฌ์ฉํ์ธ์. ESLint์ ๊ฐ์ ๋ฆฐํฐ๋ ํน์ ์ฝ๋ ์คํ์ผ์ ๊ฐ์ ํฉ๋๋ค. ์๋ฅผ ๋ค์ด, ์ธ๊ธฐ ์๋ ์คํ์ผ ๊ฐ์ด๋(์: ์์ด๋น์ค๋น ์คํ์ผ ๊ฐ์ด๋)๋ฅผ ๋ฐ๋ฅด๋๋ก ESLint์์ ์๊ตฌ์ฌํญ์ผ๋ก ์ค์ ํ ์ ์์ต๋๋ค. ๋ํ IDE/ํธ์ง๊ธฐ์ ESLint๋ฅผ ํตํฉํ๋ฉด ๋ชจ๋ ์ค์๋ฅผ ํ์ํด ์ค๋๋ค.
๊ณ์ ์ฝ๊ธฐ : ๋ฆฌ์กํธ ํ์ผ/ํด๋ ๊ตฌ์กฐ(์ข ํฉ ํํ ๋ฆฌ์ผ)
ํตํฉ๋ ์ฝ๋ ํ์์ ์ฌ์ฉํ๋ ค๋ฉด ๋ฆฌ์กํธ ํ๋ก์ ํธ์์ Prettier๋ฅผ ์ฌ์ฉํ์ธ์. ๋ช ๊ฐ์ง ์ต์ ๋ง ์ค์ ํ ์ ์๋ ๋ ๋จ์ ์ธ ์ฝ๋ ํฌ๋งทํฐ์ ๋๋ค. ํ์ผ์ ์ ์ฅํ ๋๋ง๋ค ์ฝ๋ ํฌ๋งท์ ์ง์ ํ๋๋ก ํธ์ง๊ธฐ๋ IDE์ ํตํฉํ ์ ์์ต๋๋ค. Prettier๋ ESLint๋ฅผ ๋์ฒดํ์ง๋ ์์ง๋ง, ESLint์ ์ ํตํฉ๋ฉ๋๋ค.
2024๋ ์ ๋ ์ค๋ฅด๋ ์คํ๋ ์๋ง๋ Biome(์ด์ ์ Rome)์ด ๋ ๊ฒ์ ๋๋ค. ESLint์ Prettier๋ ์ค์ ์ด๋ ํนํ ์ํธ ์์ฉ ์ธก๋ฉด์์ ๊ฐ์ฅ ์ ํธ๋๋ ๋๊ตฌ๋ ์๋๋๋ค. ํ์ง๋ง ๋ชจ๋ ์น ๊ฐ๋ฐ์์ ์ผ์ ์ ๋ฌด์ ๋ฐ๋์ ํ์ํฉ๋๋ค. Biome์ ๋น ๋ฅธ(Rust ๊ธฐ๋ฐ) ์ฌ์ธ์ ํด์ฒด์ธ์ ์ ๊ณตํจ์ผ๋ก์จ Prettier์ ESLint์ ๋์์ด ๋๊ณ ์ ํฉ๋๋ค.
๋ฆฌ์กํธ ์ ํ๋ฆฌ์ผ์ด์ ์์ ๊ฐ์ , ๋ก๊ทธ์ธ, ๋ก๊ทธ์์๊ณผ ๊ฐ์ ๊ธฐ๋ฅ์ ๊ฐ์ถ ์ธ์ฆ์ ๋์ ํ๊ณ ์ถ์ ์ ์์ต๋๋ค. ๋น๋ฐ๋ฒํธ ์ฌ์ค์ ๋ฐ ๋น๋ฐ๋ฒํธ ๋ณ๊ฒฝ ๊ธฐ๋ฅ๊ณผ ๊ฐ์ ๊ธฐ๋ฅ๋ ์ข ์ข ํ์ํฉ๋๋ค. ์ด๋ฌํ ๊ธฐ๋ฅ๋ค์ ๋ฆฌ์กํธ๋ฅผ ๋์ด ๋ฐฑ์๋ ์ ํ๋ฆฌ์ผ์ด์ ์ด ์ด๋ฌํ ๊ธฐ๋ฅ์ ๊ด๋ฆฌํ๊ธฐ ๋๋ฌธ์ ์ค์ํฉ๋๋ค.
๊ณ์ ์ฝ๊ธฐ : React Router๋ก ์ธ์ฆ์ ์ค๋นํ๋ ๋ฐฉ๋ฒ
๊ฐ์ฅ ์ข์ ํ์ต ๊ฒฝํ์ ์ธ์ฆ ๊ธฐ๋ฅ์ด ์๋ ๋ฐฑ์๋ ์ ํ๋ฆฌ์ผ์ด์ (์: GraphQL ๋ฐฑ์๋)์ ์ง์ ๊ตฌํํ๋ ๊ฒ์ ๋๋ค. ๊ทธ๋ฌ๋ ์ธ์ฆ์๋ ๋ง์ ๋ณด์ ์ํ๊ณผ ๋ง์ ์ฌ๋์ด ์์ง ๋ชปํ๋ ์ธ๋ถ ์ฌํญ์ด ์๋ฐ๋๋ฏ๋ก, ์์ค์ ๋์ ์๋ ๋ง์ ์ธ์ฆ/๋ฐฑ์๋ ์๋น์ค ์๋ฃจ์ ์ค ํ๋๋ฅผ ์ฌ์ฉํ๋๊ฒ์ด ์ข์ต๋๋ค.
๋ฆฌ์กํธ๋ฅผ ์๋ฒ๋ก ์ด๋์ํค๋ ๊ฐ๋ ฅํ ์ถ์ธ๊ฐ ์๊ธฐ ๋๋ฌธ์ ๋ฆฌ์กํธ ํ๋ก์ ํธ์ ๊ฐ์ฅ ์์ฐ์ค๋ฌ์ด ํ๊ฒฝ์ Next.js, Astro ๋๋ Remix์ ๊ฐ์ ๋ฉํ ํ๋ ์์ํฌ์ผ ๊ฒ์ ๋๋ค.
๋ง์ฝ ๋ค์ํ ์ด์ ๋ก JS/TS๋ ์ฌ์ฉํ ์ ์์ง๋ง ํ์คํ ํ๋ ์์ํฌ๋ฅผ ์ฌ์ฉํ ์ ์๋ค๋ฉด, tRPC ๋๋ Hono๋ฅผ ํ์ธํด ๋ณด์ ์ผ ํฉ๋๋ค. ๊ตฌ์์ด์ง๋ง ์ฌ์ ํ ์ธ๊ธฐ ์๋ Express๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ๋ Node ๋ฐฑ์๋๋ ์ธ๊ธํ ๋งํฉ๋๋ค. ๋ค๋ฅธ ๋์์ผ๋ก๋ Fastify๋ Nest๊ฐ ์์ต๋๋ค.
๋ฆฌ์กํธ์ ์ง์ ์ ์ผ๋ก ๊ด๋ จ์ด ์๋ ๊ฒ์ ์๋์ง๋ง, ์์ฆ ํ์คํ ๋ฆฌ์กํธ ์ ํ๋ฆฌ์ผ์ด์ ์ด ์ธ๊ธฐ๋ฅผ ์ป๊ณ ์๊ธฐ ๋๋ฌธ์ ๋ฆฌ์กํธ๋ ๊ทธ ์ด๋ ๋๋ณด๋ค ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ณ์ธต๊ณผ ๊ฐ๊น์์ก์ต๋๋ค. Next.js ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฐ๋ฐํ๋ ๋์, ๋๋ถ๋ถ ๋ฐ์ดํฐ๋ฒ ์ด์ค ORM์ ๋ค๋ฃจ๊ฒ ๋ ๊ฐ๋ฅ์ฑ์ด ๋์ต๋๋ค. ์์ฆ ๊ฐ์ฅ ์ธ๊ธฐ ์๋ ORM์ Prisma์ ๋๋ค. ์ต๊ทผ ์ ํํ๋ ๋์์ Drizzle ORM์ ๋๋ค. ๋ ๋ง์ ๋์์ผ๋ก๋ Kysely์ database-js(PlanetScale์๋ง ํด๋น)๊ฐ ์์ต๋๋ค.
๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ํ์ ์์ด์๋ Supabase(๋๋ Firebase)๊ฐ ์ ํจํ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ๊ณต์ ์ฒด์ ๋๋ค. PostgreSQL์ ์ฌ์ฉํ๋ Supabase๋ ์์ฒด ํธ์คํ ํ๊ฑฐ๋ ์ ๋ฃ ์๋น์ค๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค. ์๋ฒ๋ฆฌ์ค ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ ๊ณตํ๋ ์ธ๊ธฐ ์๋ ๋์์ผ๋ก๋ PlanetScale(๊ฐ์ธ์ ์ถ์ฒ), Neon, Xata๊ฐ ์์ต๋๋ค.
๊ณ์ ์ฝ๊ธฐ : ์น ๊ฐ๋ฐ ํธ๋ ๋
๋ฆฌ์กํธ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ค๋ฅธ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ฒ๋ผ ๋ฐฐํฌํ๊ณ ํธ์คํ ํ ์ ์์ต๋๋ค. ์์ ํ ์ ์ด๋ฅผ ์ํ๋ค๋ฉด Digital Ocean๊ณผ ๊ฐ์ ๊ฒ์ ์ ํํ ์ ์์ต๋๋ค. ๋๊ตฐ๊ฐ ๋์ ๋ชจ๋ ๊ฒ์ ์ฒ๋ฆฌํด์ฃผ๊ธธ ์ํ๋ค๋ฉด Netlify๋ Vercel (ํนํ Next.js์ ๊ฒฝ์ฐ)์ด ์ธ๊ธฐ ์๋ ์๋ฃจ์ ์ ๋๋ค.
์ด๋ฏธ Firebase/Supabase์ ๊ฐ์ ์๋ํํฐ ๋ฐฑ์๋ ์๋น์ค๋ฅผ ์ฌ์ฉํ๊ณ ์๋ค๋ฉด, ํด๋น ์๋น์ค๊ฐ ํธ์คํ ๋ ์ ๊ณตํ๋์ง ํ์ธํด ๋ณผ ์ ์์ต๋๋ค. ๋ค๋ฅธ ์ธ๊ธฐ ์๋ ํธ์คํ ์ ๊ณต์๋ก๋ Render, Fly.io, Railway, ๋๋ ๋ ์ง์ ์ ์ธ AWS/Azure/Google Cloud/Hetzner ๋ฑ์ด ์์ต๋๋ค.
๋ง์ฝ ๋ฆฌ์กํธ์์์ ํ ์คํธ์ ๋ํด ๊น๊ฒ ์๊ณ ์ถ๋ค๋ฉด โ๋ฆฌ์กํธ์์ ์ปดํฌ๋ํธ๋ฅผ ํ ์คํธํ๋ ๋ฐฉ๋ฒโ์ ์ฝ์ด๋ณด์ธ์. ํต์ฌ์ ๋ฆฌ์กํธ ์ ํ๋ฆฌ์ผ์ด์ ์ ํ ์คํธํ๋ ๊ทผ๊ฐ์ Jest์ ๊ฐ์ ํ ์คํธ ํ๋ ์์ํฌ์ ๋๋ค. Jest๋ ํ ์คํธ ๋ฌ๋, ์ด์ค์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ, ์คํ์ด, ๋ชจํน, ์คํ ๊ธฐ๋ฅ ๋ฑ์ ์ ๊ณตํฉ๋๋ค. ํฌ๊ด์ ์ธ ํ ์คํธ ํ๋ ์์ํฌ์์ ํ์ํ ๋ชจ๋ ๊ฒ์ ์ ๊ณตํฉ๋๋ค. ๋ง์ฝ Vite์ ํฌ์ด๋ผ๋ฉด Jest ๋์์ผ๋ก Vitest๋ฅผ ํ์ธํด ๋ณด์ธ์.
์ต์ํ์ผ๋ก, react-test-renderer๋ฅผ ์ฌ์ฉํ์ฌ ํ ์คํธ ํ๋ ์์ํฌ์์ ๋ฆฌ์กํธ ์ปดํฌ๋ํธ๋ฅผ ๋ ๋๋ง ํ ์ ์์ต๋๋ค. ์ด๊ฒ๋ง์ผ๋ก๋ Vitest ๋๋ Jest์ ํจ๊ป ์ค๋ ์ท ํ ์คํธ๋ฅผ ์ํํ๊ธฐ์ ์ถฉ๋ถํฉ๋๋ค. ์ค๋ ์ท ํ ์คํธ๋ ๋ค์๊ณผ ๊ฐ์ ๋ฐฉ์์ผ๋ก ๋์ํฉ๋๋ค. ํ ์คํธ๋ฅผ ์คํํ๋ฉด ๋ฆฌ์กํธ ์ปดํฌ๋ํธ์ ๋ ๋๋ง๋ DOM ์์์ ๋ํ ์ค๋ ์ท์ด ์์ฑ๋ฉ๋๋ค. ํน์ ์์ ์์ ๋ค์ ํ ์คํธ๋ฅผ ์คํํ๋ฉด ์ด์ ์ค๋ ์ท๊ณผ์ ์ฐจ์ด์ ์ผ๋ก ์ฌ์ฉ๋๋ ๋ ๋ค๋ฅธ ์ค๋ ์ท์ด ์์ฑ๋ฉ๋๋ค. ์ฐจ์ด๊ฐ ์์ผ๋ฉด ํ ์คํธ ํ๋ ์์ํฌ์์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉฐ ์ค๋ ์ท์ ์์ฉํ๊ฑฐ๋ ์ปดํฌ๋ํธ๋ฅผ ์กฐ์ ํด์ผ ํฉ๋๋ค.
๊ฒฐ๊ตญ์๋ React Testing Library (RTL)๋ฅผ ์ฌ์ฉํ๊ฒ ๋ ๊ฒ์ ๋๋ค. ์ด๋ ํ ์คํธ ํ๋ ์์ํฌ ํ๊ฒฝ ๋ด์์ ๋๋ฆฌ ์ฌ์ฉ๋๋ฉฐ, ๋ฆฌ์กํธ๋ฅผ ์ํ ํฌ๊ด์ ์ธ ํ ์คํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค. RTL์ ์ฌ์ฉํ๋ฉด ์ปดํฌ๋ํธ๋ฅผ ๋ ๋๋งํ๊ณ HTML ์์์์ ์ด๋ฒคํธ๋ฅผ ์๋ฎฌ๋ ์ด์ ํ ์ ์์ต๋๋ค. ๊ทธ ํ์๋ ์ด์ค์ ์ ์ํด ํ ์คํธ ํ๋ ์์ํฌ๊ฐ ์ฌ์ฉ๋ฉ๋๋ค.
๋ฆฌ์กํธ E2E ํ ์คํธ๋ฅผ ์ํ ๋๊ตฌ๋ฅผ ์ฐพ๊ณ ์๋ค๋ฉด, Playwright์ Cypress๊ฐ ๊ฐ์ฅ ์ธ๊ธฐ ์๋ ์ ํ์ง์ ๋๋ค.
๋ฐ๋๋ผ ์๋ฐ์คํฌ๋ฆฝํธ๋ ๋ฐ์ดํฐ ๊ตฌ์กฐ๋ฅผ ๋ถ๋ณ์ธ ๊ฒ์ฒ๋ผ ์ฒ๋ฆฌํ ์ ์๋ ๋ค์ํ ๋ด์ฅ ๋๊ตฌ๋ฅผ ์ ๊ณตํฉ๋๋ค. ํ์ง๋ง ๋ถ๋ณ์ ๋ฐ์ดํฐ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ ํด์ผ ํ ํ์์ฑ์ ๋๋๋ค๋ฉด, ๊ฐ์ฅ ๋ง์ด ์ฌ์ฉ๋๋ ๋๊ตฌ๋ Immer์ ๋๋ค.
๋ฆฌ์กํธ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตญ์ ํ(i18n)์ ๋ํด ์๊ฐํ ๋, ๋ฒ์ญ๋ฟ๋ง ์๋๋ผ ๋ณต์ํ, ๋ ์ง ๋ฐ ํตํ ํ์, ๊ทธ๋ฆฌ๊ณ ๊ธฐํ ์ฌ๋ฌ ์ฌํญ๋ค๋ ๊ณ ๋ คํด์ผ ํฉ๋๋ค. ์ด๋ฅผ ๋ค๋ฃจ๊ธฐ ์ํ ๊ฐ์ฅ ์ธ๊ธฐ ์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
๋ฆฌ์กํธ์ ๋ฆฌ์น ํ ์คํธ ํธ์ง๊ธฐ์ ๊ดํด์๋ ์๋ ํ๋ณด๋ง ๋ ์ฌ๋ฆด ์ ์์ต๋๋ค.
๊ฐ์ฅ ์ผ๋ฐ์ ์ธ ๊ฒฐ์ ๊ณต๊ธ์๋ Stripe์ PayPal์ ๋๋ค. ๋ ๋ค ๋ฆฌ์กํธ์ ์์ฝ๊ฒ ํตํฉ๋ ์ ์์ต๋๋ค. ๋ค์์ ์๋ํ๋ Stripe Checkout๊ณผ ๋ฆฌ์กํธ ํตํฉ ์์ ์ ๋๋ค.
๋ฆฌ์กํธ ์ ํ๋ฆฌ์ผ์ด์ ์ด ๋ ์ง, ์๊ฐ ๋ฐ ์๊ฐ๋๋ฅผ ๋ง์ด ๋ค๋ฃจ๋ ๊ฒฝ์ฐ, ์ด๋ฌํ ์์๋ค์ ๊ด๋ฆฌํด ์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋์ ํ ์ ์์ต๋๋ค. ์ฌ๊ธฐ ๋ช ๊ฐ์ง ์ต์ ๋ค์ ๋๋ค.
Electron๊ณผ Tauri๋ ํฌ๋ก์ค ํ๋ซํผ ๋ฐ์คํฌํฑ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฐ๋ฐํ๊ธฐ ์ํ ์ฃผ์ ํ๋ ์์ํฌ์ ๋๋ค.
๊ฐ์ธ์ ์ผ๋ก react-beautiful-dnd์ ํ์ ๋ฒ์ ์ ์ฌ์ฉํด ๋ดค๋๋ฐ, ์ ๋ง ์ข์์ต๋๋ค. ํจ์ฌ ๋ง์ ์ ์ฐ์ฑ๊ณผ ์ต์ ์ ์ ๊ณตํ์ง๋ง ํ์ต ๊ณก์ ์ด ๋ค์ ๊ฐํ๋ฅธ ๋น์ฉ์ด ์๋ ์ธ๊ธฐ ์๋ ๋์์ผ๋ก๋ dnd kit์ด ์์ต๋๋ค. ์ด ๋ถ์ผ์ ๋ ๋ค๋ฅธ ๋์์ผ๋ก๋ react-dnd๊ฐ ์์ต๋๋ค.
๋ฆฌ์กํธ๋ฅผ ์น์์ ๋ชจ๋ฐ์ผ๋ก ๊ฐ์ ธ์ค๋ ๊ฐ์ฅ ์ผ๋ฐ์ ์ธ ํด๊ฒฐ์ฑ ์ ์ฌ์ ํ React Native์ ๋๋ค. React Native ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ง๋ค๊ธฐ ์ํ ๊ฐ์ฅ ์ธ๊ธฐ ์๋ ํ๋ ์์ํฌ๋ Expo์ ๋๋ค. ์น๊ณผ ๋ชจ๋ฐ์ผ์์ ํต์ผ๋ ์ปดํฌ๋ํธ๊ฐ ํ์ํ๋ค๋ฉด Tamagui๋ฅผ ํ์ธํด๋ณด๋ ๊ฒ์ด ์ข์ต๋๋ค.
๊ณ์ ์ฝ๊ธฐ : React Native์์ ํ์ ๋ฐฐ์ฐ๊ธฐ
๋ฆฌ์กํธ๋ฅผ ์ฌ์ฉํ์ฌ ๊ฐ์ ํ์ค(VR)์ด๋ ์ฆ๊ฐ ํ์ค(AR)๋ก ๋ฐ์ด๋ค ์ ์์ต๋๋ค. ์์งํ ๋งํด์, ์ด๋ฌํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค ์ด๋ ๊ฒ๋ ์ฌ์ฉํ ์ ์ ์๊ณ ๋๋ถ๋ถ์ด ์ด๊ธฐ ๋จ๊ณ(์คํ์ )์ ์์ง๋ง AR/VR์์ ๋ฆฌ์กํธ๋ฅผ ๋ค๋ฃฐ ๋ ์ ๊ฐ ์๊ณ ์๋ ๋ช ๊ฐ์ง ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
UI/UX ๋ฐฐ๊ฒฝ์ ๊ฐ์ง๊ณ ์๋ค๋ฉด, ์๋ก์ด ๋ฆฌ์กํธ ์ปดํฌ๋ํธ, ๋ ์ด์์ ๋๋ UI/UX ๊ฐ๋ ์ ๋น ๋ฅด๊ฒ ํ๋กํ ํ์ดํํ ์ ์๋ ๋๊ตฌ๋ฅผ ์ฌ์ฉํ๊ณ ์ถ์ ๊ฒ์ ๋๋ค. ๊ฐ์ธ์ ์ผ๋ก๋ Figma๋ฅผ ์ฌ์ฉํฉ๋๋ค. ๋๋ต์ ์ด๊ณ ๊ฐ๋ฒผ์ด ์ค์ผ์น์๋ Excalidraw๋ฅผ ์ ํธํ๋ฉฐ, ์ด๋ค ์ฌ๋๋ค์ tldraw๋ฅผ ์ ํธํ๊ธฐ๋ ํฉ๋๋ค.
์ปดํฌ๋ํธ์ ๋ํ ๋ฌธ์ ์์ฑ์ ๋ด๋นํ๋ ๊ฒฝ์ฐ, ๋ค์ํ๊ณ ๊น๋ํ ๋ฆฌ์กํธ ๋ฌธ์ํ ๋๊ตฌ๊ฐ ์์ต๋๋ค. ์ ๋ ๋ง์ ํ๋ก์ ํธ์์ Storybook์ ์ฌ์ฉํด ์๊ณ , ์ด์ ๋ํด ์ค๋ฆฝ์ ์ธ ์๊ฒฌ์ ๊ฐ์ง๊ณ ์์ต๋๋ค. ๋ค๋ฅธ ์๋ฃจ์ ์ ๋ํด์๋ ์ข์ ์๊ธฐ๋ฅผ ๋ค์์ต๋๋ค.
๊ฒฐ๊ตญ ๋ฆฌ์กํธ ์ํ๊ณ๋ ๋ฆฌ์กํธ๋ฅผ ์ํ ํ๋ ์์ํฌ๋ก ๋ณผ ์ ์์ง๋ง, ๊ทธ ํต์ฌ์ ๋ฆฌ์กํธ๋ฅผ ํตํด ์ ์ฐ์ฑ์ ์ ์งํฉ๋๋ค. ์ด๋ ์ด๋ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ ํํ ๊ฒ์ธ์ง์ ๋ํด ์ถฉ๋ถํ ์ ๋ณด๋ฅผ ๋ฐํ์ผ๋ก ์ค์ค๋ก ๊ฒฐ์ ํ ์ ์๋ ์ ์ฐํ ํ๋ ์์ํฌ๋ผ๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค. ์๊ท๋ชจ๋ก ์์ํ์ฌ ํน์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ง ์ถ๊ฐํ ์ ์์ต๋๋ค. ๋ฐ๋๋ก ๋ฆฌ์กํธ๋ง์ผ๋ก ์ถฉ๋ถํ ๊ฒฝ์ฐ, ๋ค๋ฅธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์ด ์ฌ์ฉํ์ฌ ๊ฐ๋ณ๊ฒ ์ ์งํ ์ ์์ต๋๋ค.
๐ ํ๊ตญ์ด๋ก ๋ ํ๋ฐํธ์๋ ์ํฐํด์ ๋น ๋ฅด๊ฒ ๋ฐ์๋ณด๊ณ ์ถ๋ค๋ฉด Korean FE Article(https://kofearticle.substack.com/)์ ๊ตฌ๋ ํด ์ฃผ์ธ์!