๐Ÿ•บ์ชผ์ชผ์ชผ์ชผ๐Ÿ•บ(์ชผ๊ฐœ๊ณ  ์ชผ๊ฐœ๊ณ  ๋˜ ์ชผ๊ฐœ๊ณ  ์ชผ๊ฐœ)

๋ฐ๋ธŒํ˜„ยท2024๋…„ 5์›” 11์ผ
14

๐Ÿงถ ๊ฐœ์š”

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

๊ด€๋ จ ์•„ํ‹ฐํด
(๋ฒˆ์—ญ) ๊ธฐ๋Šฅ ๋ถ„ํ•  ์„ค๊ณ„ - ์ตœ๊ณ ์˜ ํ”„๋ŸฐํŠธ์—”๋“œ ์•„ํ‚คํ…์ฒ˜

์ตœ๊ทผ์— ํŒ€ ๋‚ด์—์„œ FSD ์•„ํ‚คํ…์ฒ˜์— ๊ด€ํ•œ ์ด์•ผ๊ธฐ๊ฐ€ ์˜ค๊ฐ”๊ณ , ๋‹ค๋ฅธ ํŒ€์—์„œ ์ ์šฉํ•ด๋ณธ ๊ณณ๋„ ์žˆ์–ด์„œ ์ƒˆ๋กœ ์‹œ์ž‘ํ•˜๋Š” ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•ด๋ณด๊ณ ์ž ํ•˜์˜€๋‹ค.
๊ทธ๋ž˜์„œ ์‹ค์ œ๋กœ ์ ์šฉํ•ด๋ณด๋ฉด์„œ ์•„ํ‚คํ…์ฒ˜ ์„ค๋ช…๊ณผ ๋‚ด๊ฐ€ ๋Š๋ผ๋Š” ์žฅ๋‹จ์ ์„ ์ ์–ด๋ณด๊ณ ์ž ํ•œ๋‹ค.

๐Ÿ“ถ FSD ์•„ํ‚คํ…์ฒ˜๋ž€?


์ถœ์ฒ˜: https://emewjin.github.io/feature-sliced-design/

FSD๋Š” Feature-Sliced Design์˜ ์ถ•์•ฝ์œผ๋กœ ํ•ด์„ํ•˜๋ฉด ๊ธฐ๋Šฅ ๋ถ„ํ•  ์„ค๊ณ„ ์•„ํ‚คํ…์ฒ˜์ด๋‹ค.
ํฌ๊ฒŒ 3๊ฐ€์ง€์˜ ๊ฐœ๋…์œผ๋กœ ๊ตฌ๋ถ„๋˜์–ด ์žˆ์œผ๋ฉฐ, ๊ฐ๊ฐ Layer, Slice, Segment๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ๋‹ค.

์ž์„ธํ•œ ๋‚ด์šฉ์€ ์œ„ ์•„ํ‹ฐํด์„ ๋ณด๋Š”๊ฒŒ ๋”์ข‹์•„์„œ ๊ฐ„๋‹จํ•˜๊ฒŒ ์ž‘์„ฑํ•ด๋ณด๋ ค๊ณ  ํ•œ๋‹ค.

1๏ธโƒฃ Layer

๋ ˆ์ด์–ด์•ˆ์—๋Š” 7๊ฐ€์ง€์˜ ํด๋”๋กœ ๊ตฌ์„ฑํ•˜๋ฉด ๋˜๊ณ , ๊ฐ ํด๋”๋งˆ๋‹ค ์„œ๋กœ์˜ ์—ญํ• ์ด ์žˆ์–ด์„œ ํด๋”๋ณ„๋กœ ๊ตฌ๋ถ„ํ•ด์„œ ๊ด€๋ฆฌํ•ด์•ผ ํ•œ๋‹ค.

๐Ÿ—‚๏ธ src
  โ”ฃ ๐Ÿ—‚๏ธ app
  โ”ฃ ๐Ÿ—‚๏ธ pages
  โ”ฃ ๐Ÿ—‚๏ธ widgets
  โ”ฃ ๐Ÿ—‚๏ธ features
  โ”ฃ ๐Ÿ—‚๏ธ entities
  โ”— ๐Ÿ—‚๏ธ shared
  • app: Provider(๋ฆฌ์•กํŠธ ์ฟผ๋ฆฌ ํ”„๋กœ๋ฐ”์ด๋”, ์ „์—ญ ์ปจํ…์ŠคํŠธ ๋“ฑ), ์ „์—ญ ์Šคํƒ€์ผ, ์ „์—ญ ์ปจํ…์ŠคํŠธ ๋“ฑ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ตœ์ƒ๋‹จ์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๊ฒƒ๋“ค์ด ์—ฌ๊ธฐ์—์„œ ์ •์˜๋œ๋‹ค.
  • pages: ํด๋”์˜ ๋ง ๊ทธ๋Œ€๋กœ ์„œ๋น„์Šค์˜ ํŽ˜์ด์ง€๋“ค์ด ๋“ค์–ด๊ฐ€๊ฒŒ ๋œ๋‹ค.
  • widgets: ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๋…๋ฆฝ์ ์ธ UI๋ฅผ ํ•ฉ์ณ์„œ ํ•˜๋‚˜์˜ ์ปดํฌ๋„ŒํŠธ๋กœ ๋งŒ๋“œ๋Š” ๊ณณ์ด ๋œ๋‹ค.
  • features: ์‚ฌ์šฉ์ž ์ด๋ฒคํŠธ๋ฅผ ๋‹ค๋ฃจ๋Š” ๊ณณ(๋น„์ง€๋‹ˆ์Šค ๋กœ์ง)์ด๋ผ๊ณ  ๋ณด๋ฉด ๋œ๋‹ค. ์ง๊ด€์ ์œผ๋กœ ๋งํ•˜์ž๋ฉด ๋™์‚ฌ๋กœ ํ‘œํ˜„๋  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด ๋“ค์–ด๊ฐ€๋ฉด ๋œ๋‹ค. (ex) ๊ฒŒ์‹œ๋ฌผ์„ ๋ถ๋งˆํฌํ•œ๋‹ค, ๋ฉ”์‹œ์ง€๋ฅผ ์ „์†กํ•œ๋‹ค ๋“ฑ)
  • entities: ๋น„์ง€๋‹ˆ์Šค์˜ ์—”ํ‹ฐํ‹ฐ๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค. ์‰ฝ๊ฒŒ ๋งํ•˜๋ฉด ๋ฐ์ดํ„ฐ์˜ ๋ชจ๋ธ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค. (ex) ํ”„๋กœํ•„ ๋ฐ์ดํ„ฐ ๋ชจ๋ธ, ๊ฒŒ์‹œํŒ ๋ฐ์ดํ„ฐ ๋ชจ๋ธ ๋“ฑ)
  • shared: ์žฌ์‚ฌ์šฉ์ด ํ•„์š”ํ•˜๊ฑฐ๋‚˜ ์œ ํ‹ธ๋ฆฌํ‹ฐ๋กœ ์“ฐ์ด๋Š” ํ•จ์ˆ˜๋“ค ๊ฐ™์ด '๊ณต์œ '๊ฐ€ ํ•„์š”ํ•œ ๊ฒƒ๋“ค์ด ํฌํ•จ๋œ๋‹ค. (ex) ํ—ฌํผ ํ•จ์ˆ˜, ๊ฐ์ข… ์œ ํ‹ธ ํ•จ์ˆ˜๋“ค, ๊ณตํ†ต์œผ๋กœ ์“ฐ์ด๋Š” ๋ชจ๋ธ(ํƒ€์ž…))

ํด๋”์˜ ์—ญํ• ์— ๋งž์ถฐ์„œ ๊ตฌ๋ถ„ํ•˜๋Š” ๊ฒƒ๋„ ์ค‘์š”ํ•˜์ง€๋งŒ, ๊ฐ ๊ณ„์ธต(ํด๋”) ๋ณ„๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด ์ œํ•œ์ ์ธ ๊ฒƒ๋„ ํŠน์ง• ์ค‘์— ํ•˜๋‚˜์ด๋‹ค. ๊ทธ๋ฆผ์œผ๋กœ ๋ณด๋Š”๊ฒŒ ๋” ์ดํ•ดํ•˜๊ธฐ ํŽธํ• ๊ฒƒ์ด๊ธฐ์— ํ‘œ๋กœ ๋‚˜ํƒ€๋‚ด๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

Layer์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ณ„์ธต
app๋ชจ๋‘ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
pageswidgets, entities, shared
widgetsentities, shared
entitiesshared
sharedx

ํ‘œ๋ฅผ ๋ณด๋ฉด ๊ฐ ๊ณ„์ธต์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด ์ œํ•œ์ ์ด๊ธฐ์— ์ด๋Ÿฐ ์ ์„ ๋” ์ƒ๊ฐํ•ด์„œ ์„ค๊ณ„๋ฅผ ํ•ด์•ผ ํ•œ๋‹ค.

2๏ธโƒฃ Slice

์Šฌ๋ผ์ด์Šค๋Š” Layer์˜ ๊ฐ ๊ณ„์ธต์— ๋ชจ๋‘ ์กด์žฌ(shared๋Š” ์ œ์™ธ)ํ•  ์ˆ˜ ์žˆ๋‹ค. ์Šฌ๋ผ์ด์Šค์—์„œ๋Š” ํ”„๋กœ๋•ํŠธ์˜ ์„ฑ๊ฒฉ๋งˆ๋‹ค ๋น„์ง€๋‹ˆ์Šค๊ฐ€ ๋ชจ๋‘ ๋‹ค๋ฅด๊ธฐ์— ๊ฐ ํ”„๋กœ์ ํŠธ์— ๋งž์ถฐ์„œ ์ง€์ •ํ•˜๋ฉด ๋œ๋‹ค.

๐Ÿ—‚๏ธ src
  โ”ฃ ๐Ÿ—‚๏ธ app
  โ”ฃ ๐Ÿ—‚๏ธ pages
  โ”ƒ    โ”ฃ detail/
  โ”ƒ    โ”— profile/
  โ”ฃ ๐Ÿ—‚๏ธ widgets
  โ”ƒ    โ”ฃ header/
  โ”ƒ    โ”— footer/
  โ”ฃ ๐Ÿ—‚๏ธ features
  โ”ƒ    โ”— ...
  โ”ฃ ๐Ÿ—‚๏ธ entities
  โ”— ๐Ÿ—‚๏ธ shared

์ค‘์š”ํ•œ ์ ์€ ์Šฌ๋ผ์ด์Šค์— ์žˆ๋Š” ์ฝ”๋“œ๋ฅผ ์ง์ ‘์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋ฉด ์•ˆ๋œ๋‹ค.
์ฆ‰, import๋ฅผ ์ง์ ‘์ ์œผ๋กœ ์ฝ”๋“œ์—์„œ ๊ฐ€์ ธ์™€์„œ ํ•˜๋ฉด ์•ˆ ๋œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

3๏ธโƒฃ Segment

์„ธ๊ทธ๋จผํŠธ๋Š” ์Šฌ๋ผ์ด์Šค ๋‚ด์— ์กด์žฌํ•˜๋ฉฐ, ์„ธ๊ทธ๋จผํŠธ๋‚ด์— ๋“œ๋””์–ด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด ๋“ค์–ด๊ฐ€๊ฒŒ ๋˜๋Š” ํด๋”์ด๋‹ค. ํŠน์ดํ•˜๊ฒŒ shared๋Š” ๋ฐ”๋กœ segment๊ฐ€ ๋ฐ”๋กœ ์˜ค๊ฒŒ ๋œ๋‹ค.

๐Ÿ—‚๏ธ src
  โ”ฃ ๐Ÿ—‚๏ธ app
  โ”ฃ ๐Ÿ—‚๏ธ pages
  โ”ƒ    โ”ฃ detail/
  โ”ƒ    โ”ƒ  โ”ฃ ui/
  โ”ƒ    โ”ƒ  โ”— constants/
  โ”ƒ    โ”— profile/
  โ”ฃ ๐Ÿ—‚๏ธ widgets
  โ”ƒ    โ”ฃ header/
  โ”ƒ    โ”ƒ  โ”ฃ ui/
  โ”ƒ    โ”ƒ  โ”ฃ model/
  โ”ƒ    โ”ƒ  โ”— constants/
  โ”ƒ    โ”— footer/
  โ”ฃ ๐Ÿ—‚๏ธ features
  โ”ƒ    โ”— ...
  โ”ฃ ๐Ÿ—‚๏ธ entities
  โ”— ๐Ÿ—‚๏ธ shared
       โ”ฃ ui/
       โ”ฃ model/
       โ”— constants/
  • ui: ์Šฌ๋ผ์ด์Šค์˜ UI ์ปดํฌ๋„ŒํŠธ
  • model: ๋น„์ง€๋‹ˆ์Šค ๋กœ์ง์ด ๋“ค์–ด๊ฐ€๋Š” ๊ณณ, ๋ฐ์ดํ„ฐ ๋ชจ๋ธ ๋˜๋Š” actions์ด ๋“ค์–ด๊ฐ
  • constants: ์ƒ์ˆ˜๋กœ์จ ์“ฐ์ด๋Š” ๊ฐ’๋“ค
  • hooks: ์ปค์Šคํ…€ ํ›…
  • api: ์„œ๋ฒ„ ์š”์ฒญ์— ์“ฐ์ด๋Š” ๊ฒƒ๋“ค(api, useQuery ๋“ฑ)
  • utils: ๊ฐ์ข… ์œ ํ‹ธ ํ•จ์ˆ˜

๐Ÿ‘“ Public๊ณผ Private ๐Ÿ•ถ๏ธ

"์œ„์—์„œ ์ง์ ‘์ ์œผ๋กœ import ํ•ด์„œ๋Š” ์•ˆ๋œ๋‹ค" ๋ผ๋Š” ๋ง์„ ํ–ˆ์—ˆ๋Š”๋ฐ, ์ด์™€ ๊ด€๋ จ๋œ ๋‚ด์šฉ์ด๋‹ค. FSD์—์„œ๋Š” ๊ฒฐํ•ฉ๋„๋Š” ๋‚ฎ์ถ”๊ณ  ์‘์ง‘๋„๋Š” ๋†’์ด๋Š” ๊ฒƒ์ด ๋ชฉํ‘œ ์ค‘ ํ•˜๋‚˜์ธ๋ฐ ์ด๋ฅผ publicํ•œ ๊ฒƒ๊ณผ privateํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ ๊ด€๋ฆฌํ•œ๋‹ค.

// features/submitLike/ui/SubmitLikeButton.tsx
/** ์ข‹์•„์š” ๋ˆ„๋ฅด๊ธฐ ๋ฒ„ํŠผ */
export const SubmitLikeButton = () => {
   // ...
}
----------------------------------------

// widgets/postFooter/ui/PostFooterSection.tsx
// DO NOT โŒ
import { SubmitLikeButton } from 'features/submitLike/ui/SubmitLikeButton.tsx
// GOOD โœ…
import { SubmitLikeButton } from 'features/submitLike

/** ๊ฒŒ์‹œ๋ฌผ ํ•˜๋‹จ ์˜์—ญ */
export const PostFooterSection = () => {
   // ...
   return (
    <>
     ...
     <SubmitLikeButton />
     ...
    </>
    )
}
----------------------------------------

// features/submitLike/index.ts
export { SubmitLikeButton } from 'features/submitLike/ui/SubmitLikeButton'

์œ„์ฒ˜๋Ÿผ ui ํด๋” ๋‚ด์˜ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ง์ ‘์ ์œผ๋กœ import ํ•ด์„œ๋Š” ์•ˆ๋œ๋‹ค.
submitLike ํ•˜์œ„์˜ index.ts ํŒŒ์ผ์— ์„ ์–ธ๋œ Public ํ•œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ž„ํฌํŠธํ•˜๋Š” ํ˜•ํƒœ๋กœ ๊ฐ€์ ธ์™€์„œ ์จ์•ผํ•œ๋‹ค.

privateํ•œ ๊ฒƒ๋“ค์€ ๋ฌด์—‡์ด ์žˆ์„๊นŒ?

๊ทธ๋Ÿผ ๋Œ€๋ถ€๋ถ„์ด publicํ•œ ์ปดํฌ๋„ŒํŠธ์ธ๋ฐ ๊ตณ์ด ๊ตณ์ด ์ด๋ ‡๊ฒŒ ํ•ด์•ผํ•˜๋‚˜? ๋ผ๊ณ  ์ƒ๊ฐ์ด ๋“ค์ˆ˜๋„ ์žˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด๋ ‡๊ฒŒ ์“ธ ๊ฒฝ์šฐ ๋ช…์‹œ์ ์œผ๋กœ publicํ•˜๊ฒŒ ๊ด€๋ฆฌ๋˜๋Š” ๊ฒƒ๊ณผ ์•„๋‹Œ ๊ฒƒ์„ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.

privateํ•œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์˜ˆ์‹œ๋กœ ๋“ค์–ด๋ณด๋ฉด ๊ฐ„๋‹จํ•˜๊ฒŒ ๋ฐ˜๋ณต ์ปดํฌ๋„ŒํŠธ์—์„œ ์“ฐ์ด๋Š” ๊ฒƒ์ด๋ผ๊ณ  ๋ณด๋ฉด ๋œ๋‹ค.

export const PostList = () => {
   return (
     <>
     {postList.map((postItem) => {
       return <PostItem key={postItem.id}/>
     }
     </>
}

PostItem ์ปดํฌ๋„ŒํŠธ๋Š” ์–ด๋””์—์„œ๋„ ์“ฐ์ด์ง€ ์•Š๊ณ  ํ•ด๋‹น ํŒŒ์ผ ๋‚ด์—์„œ๋งŒ ์“ฐ์ด๋Š” ์ปดํฌ๋„ŒํŠธ์ด๋‹ค. ๊ทธ๋Ÿฌ๋ฏ€๋กœ index.ts์— ์ •์˜ํ•˜์ง€ ์•Š์•„๋„ ๋˜์–ด privateํ•œ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋œ๋‹ค.

์ด ์™ธ์—๋„ ์ƒ์ˆ˜(constant) ๊ฐ’์ด๋‚˜ ์™ธ๋ถ€ ๋ ˆ์ด์–ด์—์„œ ์‚ฌ์šฉ๋˜์ง€ ์•Š์„ api๊ฐ™์€ ๊ฒƒ๋“ค์€ publicํ•˜๊ฒŒ ์ •์˜ํ•˜์ง€ ์•Š์•„๋„ ๋˜๋Š” ๊ฒƒ์ด๋‹ค.

๊ทธ๋ž˜์„œ FSD ์ข‹์•„? ๐Ÿคผโ€โ™‚๏ธ

FSD์— ๋Œ€ํ•œ ์ „๋ฐ˜์ ์ธ ์„ค๋ช…์ด ๋๋‚ฌ์œผ๋‹ˆ ๋‚ด๊ฐ€ ์“ฐ๋ฉด์„œ ๋Š๊ผˆ๋˜ ์žฅ์ ๊ณผ ๋‹จ์ ์„ ์ ์–ด๋ณด๋ ค๊ณ  ํ•œ๋‹ค.

Pros ๐Ÿฅฐ

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

Cons ๐Ÿคฅ

  • ์„ค๊ณ„๋‹จ์—์„œ ์‹œ๊ฐ„์„ ๋งŽ์ด ๊ฐ€์ ธ๊ฐ€์•ผ ํ•œ๋‹ค.(์ดˆ๊ธฐ ์„ค๊ณ„๋ฅผ ์ž˜๋ชปํ•˜๊ฒŒ ๋  ๊ฒฝ์šฐ ๊ณ ํ†ต ๋ฐ›์„ ์ˆ˜ ์žˆ์Œ) === ํŒ€๊ณผ์˜ ์†Œํ†ต์ด ์ค‘์š”ํ•˜๋‹ค.
  • ์•„ํ† ๋ฏน ๋””์ž์ธ ํŒจํ„ด๊ณผ ๋น„์Šทํ•œ ๋‹จ์ ์ธ '์ด๊ฑด ์–ด๋””์— ๋“ค์–ด๊ฐ€์•ผ ๋งž์„๊นŒ?'์™€ ๊ฐ™์€ ์ƒ๊ฐ์ด ๋“ค ๋•Œ๊ฐ€ ์žˆ๋‹ค.
  • ํด๋”๊ฐ€ ๋งค์šฐ ๋งŽ์ด ์ƒ๊ธฐ๊ฒŒ ๋œ๋‹ค. (๋Ž์Šค๊ฐ€ ๊นŠ์–ด์งˆ ์ˆ˜ ์žˆ์Œ)

๋Š๋‚€ ์  ๐Ÿค 

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

๊ฐ™์€ ๊ทœ์น™์„ ๋ณด๊ณ ๋„ ์„œ๋กœ ๋‹ค๋ฅธ ๊ด€์ ์œผ๋กœ ์ƒ๊ฐํ•˜๋ฉด ์˜๊ฒฌ์ด ๊ฐˆ๋ฆด ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค. ์ฆ‰, "ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์™œ features์— ๋“ค์–ด๊ฐ€์•ผํ•˜๊ณ  ์–ด๋–ค ๋น„์ง€๋‹ˆ์Šค ๋กœ์ง์„ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ•ด๋‹น ๊ณ„์ธต์ด ๋งž๋‹ค" ๋ผ๋Š” ๊ฒฐ๋ก ์— ๋„๋‹ฌํ•ด์•ผ ํ•œ๋‹ค.

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

๐Ÿง™ ๋ถ€๊ฐ€์ ์ธ ๋‚ด์šฉ

1. Next.js ์•ฑ๋ผ์šฐํ„ฐ ์“ฐ๋Š” ์‚ฌ๋žŒ์€์š”?

Next.js ์•ฑ๋ผ์šฐํ„ฐ๋ฅผ ์“ฐ๋Š” ์‚ฌ๋žŒ์€ ์˜๋ฌธ์‚ฌํ•ญ์ด ์žˆ์—ˆ์„ ๊ฒƒ์ด๋‹ค.
Next.js์—์„œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ app ์ด๋ผ๋Š” ํด๋”๋Š” ๋ผ์šฐํ„ฐ๋ฅผ ์˜๋ฏธํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์œ„์˜ ๊ทœ์น™์„ ์ง€ํ‚ค๊ธฐ๊ฐ€ ์• ๋งคํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด๋Ÿฐ ๋ถ€๋ถ„์„ FSD ์„ค๊ณ„ํ•œ ์‚ฌ๋žŒ๋„ ์ธ์ง€ํ•˜๊ณ  ์žˆ์–ด์„œ ๊ณต์‹ ๋ฌธ์„œ์—์„œ๋„ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์„ ์ œ์‹œํ•œ๋‹ค.

โ”ฃ ๐Ÿ—‚๏ธ app                # NextJS app folder
โ”ฃ ๐Ÿ—‚๏ธ src
โ”ƒ   โ”ฃ ๐Ÿ—‚๏ธ app            # FSD app folder
โ”ƒ   โ”ฃ ๐Ÿ—‚๏ธ entities
โ”ƒ   โ”ฃ ๐Ÿ—‚๏ธ features
โ”ƒ   โ”ฃ ๐Ÿ—‚๏ธ pages
โ”ƒ   โ”ฃ ๐Ÿ—‚๏ธ shared
โ”ƒ   โ”— ๐Ÿ—‚๏ธ widgets

์œ„์™€ ๊ฐ™์ด ์•ฑ๋ผ์šฐํ„ฐ๋ฅผ ์œ„ํ•œ ๋ณ„๋„ ํด๋”์™€ FSD๋ฅผ ์œ„ํ•œ app ํด๋”๋ฅผ ๋‚˜๋ˆ„์–ด์„œ ๊ด€๋ฆฌํ•˜๋ผ๊ณ  ์ œ์‹œํ•œ๋‹ค. ๋” ๊ถ๊ธˆํ•˜๋ฉด ์—ฌ๊ธฐ์„œ ๋ณด๋Š” ๊ฒƒ์„ ์ถ”์ฒœํ•œ๋‹ค.

๊ทธ ์™ธ์—๋„ FSD๋ฅผ ์ ์šฉํ•œ ๋‹ค์–‘ํ•œ Example ๋ ˆํฌ์ง€ํ† ๋ฆฌ๊ฐ€ ์žˆ์–ด์„œ ํ•จ๊ป˜ ์ฐธ๊ณ ํ•˜๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™๋‹ค.

2. ์ ์šฉํ•œ ๋’ค ์•„์‰ฌ์› ๋˜ ์ (๋ฆฌํŒฉํ† ๋ง ํ•ด์•ผ ํ•˜๋Š” ์ )

์œ„์—์„œ ์–ธ๊ธ‰ํ•œ pages ๊ณ„์ธต์ด ๋„ˆ๋ฌด ๋งŽ์€ ์—ญํ• ์„ ๋‹ด๋‹นํ•˜๊ณ  ์žˆ๋Š” ๊ฒŒ ์•„์‰ฌ์› ๋‹ค. ๊ฒฐ๊ตญ์— pages๋‚ด์—์„œ๋Š” ์œ„์ ฏ์ด ํ•ฉ์ณ์ ธ ๊ตฌ์„ฑ๋˜๊ฒŒ ํŽ˜์ด์ง€๋ฅผ ๊ตฌ์„ฑํ•˜๊ฒŒ ๋œ๋‹ค.
๊ฐ„๋‹จํ•˜๊ฒŒ ํ”„๋กœํ•„๋กœ ์˜ˆ์‹œ๋ฅผ ๋“ค์–ด๋ณด์ž๋ฉด...(์ž์„ธํžˆ ์ ์„ ์ˆ˜ ์—†์–ด ํ‹€๋งŒ ๋ด์ฃผ์„ธ์š”)

// pages/profile/ui/ProfilePage.tsx
/** ํ”„๋กœํ•„ ํŽ˜์ด์ง€ */
export const ProfilePage = () => {
  // .. ๊ฐ์ข… state๋“ค..
  return (
    <>
  	 <ProfileHeader ... />
     <ProfileBody ... />
     <ProfileFooter ... />
    </>
  )
}

// widgets/ProfileBody/ui/ProfileBodySection.tsx
/** ํ”„๋กœํ•„ ์ž‘์„ฑ ์˜์—ญ */
export const ProfileBodySection = (...) => {
  // ...
}

// features/saveProfileData/ui/SaveProfileDataButton.tsx
/** ํ”„๋กœํ•„ ์ €์žฅ ๋ฒ„ํŠผ */
export const SaveProfileDataButton = (...) => {
  // ...
}

์œ„์™€ ๊ฐ™์€ ๊ตฌ์กฐ๋ผ๋ฉด ๊ฒฐ๊ตญ ํ”„๋กœํ•„ ํŽ˜์ด์ง€์— ์„œ๋กœ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ์— ๊ฐ™์€ ๋ฐ์ดํ„ฐ(state)๋ฅผ ๋ฐ”๋ผ๋ณด๊ฒŒ ํ•ด์•ผ ํ•œ๋‹ค๋ฉด, Page๋‹จ์—์„œ ์ƒํƒœ๋ฅผ ์ •์˜ํ•ด์ค˜์•ผ ํ•œ๋‹ค. ์ฆ‰, props๋กœ ๋„˜๊ฒจ์ฃผ์–ด์•ผ ๊ฐ๊ฐ์˜ ๋ ˆ์ด์–ด์—์„œ ๋™์ผํ•˜๊ฒŒ ์—…๋ฐ์ดํŠธ๋ฅผ ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

์ด๋Š” ์ „์—ญ ์ƒํƒœ ๊ด€๋ฆฌ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์“ฐ๋ฉด ์–ด๋Š์ •๋„ ํ•ด์†Œ๋  ๊ฒƒ ๊ฐ™์ง€๋งŒ, ์ข€ ๋” ๊ทผ๋ณธ์ ์ธ ๊ตฌ์กฐ๋ฅผ ์ƒ๊ฐํ•ด๋ณด๊ณ  ์‹ถ๋‹ค.(๋ฌผ๋ก  ๋‚ด๊ฐ€ ์ž˜๋ชป ์„ค๊ณ„ํ–ˆ์„ ์ˆ˜ ์žˆ๋‹ค.)

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

ํ”„๋กœ์ ํŠธ๊ฐ€ ๋๋‚˜๊ณ  ๊ณ ๋„ํ™”๋ฅผ ํ•˜๋ฉด์„œ ์ง€๊ธˆ๋ณด๋‹ค ํƒ„ํƒ„ํ•œ ์•„ํ‚คํ…์ฒ˜๋กœ ๊ตฌ์„ฑํ•ด๋ณผ ๊ฒƒ์ด๋‹ค.

์—ฌ๊ธฐ๊นŒ์ง€ ์ฝ์–ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค ๐Ÿ™‡๐Ÿปโ€โ™‚๏ธ
์ž˜๋ชป๋œ ๋‚ด์šฉ์ด ์žˆ์œผ๋ฉด ์–ธ์ œ๋“ ์ง€ ์ง€์ ํ•ด์ฃผ์„ธ์š”.

๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค ๐Ÿซฐ

์ฐธ๊ณ ํ•˜๋ฉด ์ข‹์€ ์˜์ƒ/๋งํฌ

profile
ํ•˜๋‹ค๋ณด๋ฉด ์•ˆ๋˜๋Š” ๊ฒƒ์ด ์—†๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋Š” 3๋…„์ฐจ ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž์ž…๋‹ˆ๋‹ค.

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

comment-user-thumbnail
2024๋…„ 5์›” 11์ผ

๊ฒฝํ—˜๊ธฐ๋ฐ˜์˜ ์ข‹์€ ๊ธ€ ์ž˜ ์ฝ์—ˆ์Šต๋‹ˆ๋‹ค! ์ œ ๊ธ€์ด ๊ฑธ๋ ค์žˆ๋‹ค๋‹ˆ ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค :)

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