Skip to content

Conversation

@hummingbbird
Copy link
Contributor

✏️ Summary


📑 Tasks

1. fsd 구조의 폴더 구조 세팅

📌 comfit은 왜 FSD 구조를 도입했나요?

fsd 구조를 도입하지 않는, 기존 방식은 아래와 같아요.

📁 src
 ├─ 📁 apis
 ├─ 📁 assets
 ├─ 📁 components
		├─ 📁 common
    ├─ 📁 Home
    └─ 📁 Search
 ├─ 📁 constants
 ├─ 📁 hooks
 ├─ 📁 layouts
 ├─ 📁 mocks
 ├─ 📁 pages
 ├─ 📁 routes
 ├─ 📁 types
 ├─ 📁 utils
 └─ App.tsx

하지만 이런 방식은 몇 가지 문제점을 가지고 있었어요.

❌ 하나의 기능을 위해 여러 개의 디렉토리를 돌아다녀야 해요
❌ 컴포넌트를 어디에 두어야 할지에 대한 팀원들의 생각이 달라
❌ 프로젝트의 규모가 작을 땐 문제가 되지 않지만, 그렇지 않은 경우 컴포넌트의 위치를 찾는 게 어려워
❌ components 폴더 외의 폴더들은 페이지 별로 파일이 구분되어 있지 않아 가독성이 떨어져요

FSD 구조를 사용하면 표준화된 구조로 이해 관계 파악이 용이하고, 기능을 중점으로 디렉토리가 나뉘어 코드의 위치가 명확해지기 때문에 위와 같은 문제를 해결할 수 있다고 판단했고, comfit 웹파트는 FSD 구조를 도입하기로 결정했어요. 또한 FSD 구조는 공식 문서를 통해 가이드라인을 제공하기 때문에 이해 및 적용 면에서 훨씬 수월하다는 장점이 있어요 👍

📌 최종 폴더 구조

프로젝트의 규모를 고려했을 때 layer의 여섯 계층을 모두 넣는 것은 과도한 세분화일 수 있어요. 실제로 공식 문서에서는 처음부터 모든 레이어를 나누어 개발하기보다는 점진적 도입을 제안하고 있어요.

❓ 점진적 도입
FSD 공식 문서에서는 기존 프로젝트에 FSD를 도입한다면 app, shared layer를 먼저 정리해서 기반을 다지고, 거기에 조금씩 widgets와 pages, entities 등을 점진적으로 도입하는 방법을 제안해요.
또한 도입 단계에서는 새로운 대규모 Entity나 복잡한 기능을 추가하지 않는 것을 추천하며, 구조를 안정적으로 정리하는 것에 초점을 맞추기를 제안하고 있어요.

서비스의 규모, 주어진 시간 등 다양한 요소를 고려하였을 때 appshared, pageswidgets까지 추가하여 4가지의 layer만 사용하는 것으로 결정했어요. features와 entities까지 사용하게 되면 과하게 복잡해지고 그렇게 되면 fsd 구조를 도입한 것이 오히려 협업에 방해가 될 수도 있다고 생각하고 내린 결론이에요.

src
 ┣ app
 ┃ ┣ layout // 전체 레이아웃
 ┃ ┣ providers // provider
 ┃ ┣ routes // 라우팅
 ┃ ┣ styles // reset, global 스타일
 ┃ ┣ App.tsx
 ┃ ┗ main.tsx
 ┃
 ┣ pages
 ┃ ┣ company-analyze
 ┃ ┣ experience
 ┃ ┣ home
 ┃ ┃ ┗ ui
 ┃ ┃ ┣ home-page.tsx
 ┃ ┃ ┗ home-page.css.ts
 ┃ ┣ login
 ┃ ┣ my-page
 ┃ ┣ onboarding
 ┃ ┗ register
 ┃
 ┣ widgets
 ┃ ┗ header
 ┃  ┗ ui
 ┃
 ┗ shared
   ┣ api
   ┣ assets
   ┃ ┣ icons
   ┃ ┗ images
   ┣ config
   ┣ lib
   ┣ model
   ┣ styles
	 ┃ ┗ ui
   ┣ types
   ┗ ui
  1. app 계층: 프로젝트 초기화, 라우팅 설정 등
  • /routes → 라우팅
  • /styles → global, reset, theme 등 글로벌 스타일
  • /providers → 프로바이더, React Context Provider
  • /layout → 공통적으로 감싸지는 레이아웃
  1. pages 계층: 도메인 기준 폴더 분리(라우팅 기준)

    • 와이어프레임을 기준으로 폴더를 구분했어요
    • 각 도메인(slice) 폴더 내부에는 ui 폴더에 page.tsx/page.css.ts 만 존재하는 것을 기본 원칙으로 합니다.(pages/home 폴더 참고)
  2. widgets 계층: 의미 있는 ui 블록 단위

    • comfit은 공통 컴포넌트는 아니지만 여러 곳에서 사용되는 컴포넌트가 많아요. comfit 서비스의 특성을 고려하여 도메인 단위로 구분하지 않고, 사용되는 곳 혹은 컴포넌트 단위로 slice를 구분하는 것으로 결정했어요. (ex) header, company/info-section, company-button 등)
    • 각 slice 내부에서 사용되는 segment는 ui, api, config, lib 등이 있어요. 필요에 따라 자유롭게 추가하면 됩니다. (단, 적절한 근거와 이유는 필수! 또한 이 외의 segment가 필요하다고 판단되면 명확한 네이밍과 근거를 제시해주세요!)
    • 좀 더 자세한 설명은 아티클에 써두었으니 꼭 확인해주세요!🙂‍↕️
  3. shared 계층: 모든 레이어에서 import 가능

    • api/ → api 인프라 레벨로 axiosInstance, base response 타입 등 지정
    • assets/ → 이미지, 아이콘, 폰트 등
    • config/ → 라우트 상수, 공통 상수 등 변경 빈도가 낮은 상수값(기존 constants와 같은 역할)
    • lib/ → 유틸 함수(기존 utils와 같은 역할)
    • model/ → useDebonce, useKeyboard같은 UI와 직접 연결되지 않는 훅(기존 hooks와 같은 역할)
    • styles/ → 스타일 토큰 및 theme 정의
    • types/ → 여러 곳에서 사용되는 types
    • ui/ → 공통 컴포넌트(주의: Button, Input, Modal과 같은 정말 basic한 공컴만 이곳에 둘 것)


2. 절대 경로 세팅

구조에 맞게 절대 경로를 세팅했습니다. 이 때 shared 내부에 있는 segment들 또한 절대 경로로 지정하여 사용할 수 있도록 하여 편의성을 높였습니다.

// tsconfig.app.json 일부
"paths": {
   "@/*": ["./src/*"],
   "@app/*": ["./src/app/*"],
   "@pages/*": ["./src/pages/*"],
   "@widgets/*": ["./src/widgets/*"],
   "@shared/*": ["./src/shared/*"],

   "@images/*": ["./src/shared/assets/images/*"],
   "@icons/*": ["./src/shared/assets/icons/*"],
   "@api/*": ["./src/shared/api/*"],
   "@config/*": ["./src/shared/config/*"],
   "@lib/*": ["./src/shared/lib/*"],
   "@model/*": ["./src/shared/model/*"],
   "@styles/*": ["./src/shared/styles/*"],
   "@types/*": ["./src/shared/types/*"],
   "@ui/*": ["./src/shared/ui/*"]
 }

👀 To Reviewer

  • 절대 경로 확인을 위해 임의의 이미지를 추가하였습니다. 여러분도 확인 한 번씩 해보고 케로로녀석이 잘 보이는지 알려주세요! ^^
  • FSD 구조에 대해 정리한 2주차 아티클 꼭 읽어주세요! FSD 구조를 100% 따르는 것이 아니기 때문에 FSD구조를 어디까지 따르고 컴포넌트를 어디에 둘지 등에 대해 각자 의견이 다를 수 있다고 생각해요. 우리가 편한, 우리에게 적합한 구조인 게 가장 중요합니다! 충분한 논의 후에 최종 구조를 결정하도록 해요😙 자유로운 의견 제시 환영하고 이와 관련해서는 디스코드로 얘기 나눠봅시다~

🔔 ETC

PR 템플릿에서 '✏️ Summary' 부분 설명이 주석이 아니라 인용으로 되어있네요! 너무 작은 수정이라 해당 pr에서 같이 수정해도 될지 여쭤봅니다~!!

- shared/styles 폴더를 추가합니다. tokens 폴더도 추가하여 스타일 세팅 시에 사용되는 토큰을 저장하도록 합니다.
- 절대 경로를 지정합니다. (개발용/빌드용)
@hummingbbird hummingbbird self-assigned this Jan 2, 2026
@hummingbbird hummingbbird linked an issue Jan 2, 2026 that may be closed by this pull request
2 tasks
@github-actions github-actions bot added 🎉INIT 초기 세팅 📑DOCS 문서, readme 작성 🔗API api 연동 🧁STYLE 디자인 및 UI 관련 채영🥦 labels Jan 2, 2026
@github-actions
Copy link

github-actions bot commented Jan 2, 2026

🚀 빌드 결과

린트 검사 완료
빌드 성공

로그 확인하기
Actions 탭에서 자세히 보기

@hummingbbird hummingbbird removed 🔗API api 연동 📑DOCS 문서, readme 작성 🧁STYLE 디자인 및 UI 관련 labels Jan 2, 2026
@u-zzn
Copy link
Collaborator

u-zzn commented Jan 3, 2026

꺅 😽 Comfit 서비스의 상황에 맞게 FSD 구조를 적절히 경량화하여 초기 폴더 구조 세팅이 잘 된 것 같아요!! 수고하셨습니다🖤
PR 설명이랑 아티클 모두 읽어보았는데 근거 기반으로 상세하게 작성해주신 덕분에 세팅되어 있는 구조를 이해하기에 너무 수월했습니다 :)

지금 시점에서 featuresentities까지 모두 도입하면 오히려 규칙과 논의 비용이 커질 수 있다는 판단에 충분히 동의합니다. 레이어를 app / shared / pages / widgets로만 구성해서 FSD 공식 문서에서 강조하는 ‘점진적 도입’ 철학이 잘 적용된 근거있는 선택이라는 생각이 드네요 ☺️ shared 계층의 세그먼트 역할(api, config, lib, model, types, ui, styles, assets 등)을 기존 프로젝트 구조와 잘 매핑해 정의해주셔서, 앞으로 작업할 때 코드를 어디에 두면 좋을지에 대한 고민도 훨씬 덜 하게 될 것 같습니다!! :)
레이어 단위뿐만 아니라 shared 내부 세그먼트까지 절대 경로 세팅도 수고 정말 많으셨습니다!! 👍

다만 FSD 구조를 100% 따르지 않는 부분, 특히 widgetsslice 기준에 대해서는 이후에 한 번 더 클라 팀 차원의 논의가 있으면 좋을 것 같다는 생각이 들었습니다. 지금처럼 도메인 기준이 아니라 사용처나 컴포넌트 단위(header, company-card 등)로 나누는 방식은 Comfit 서비스 특성상 충분히 이해되고 합리적인 선택이라고 생각하지만, 추후 앱잼이 끝나고 스프린트를 이어갈 때 혹시 컴포넌트가 점점 늘어나게 되면 이 컴포넌트가 widgets인지, 특정 page 전용인지, 아니면 shared 공통 컴포넌트인지 다시 애매해질 수 있을 것 같다고 생각했습니다. 위젯으로 승격되는 기준(재사용 횟수, 독립성, 상태 포함 여부 등) 정도는 팀 컨벤션으로 한 번만 합의해두면 이후 개발 속도가 훨씬 빨라질 것 같아서 말씀해주신 것처럼 이 부분은 디스코드 회의에서 가볍게 논의해보면 좋을 것 같아요 😊🖤

케로로 이미지도 잘 보이는거 했고 🫡 PR 템플릿 중 ‘✏️ Summary’가 주석이 아니라 인용 형태로 되어 있는건 이번 PR에서 함께 수정해주시면 좋을 것 같습니다!! 진짜 수고 많으셨습니다 최고최고ㅠ!! 🥹👍

Comment on lines 8 to 24
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
"@app": path.resolve(__dirname, "./src/app"),
"@pages": path.resolve(__dirname, "./src/pages"),
"@widgets": path.resolve(__dirname, "./src/widgets"),
"@shared": path.resolve(__dirname, "./src/shared"),
"@images": path.resolve(__dirname, "./src/shared/assets/images"),
"@icons": path.resolve(__dirname, "./src/shared/assets/icons"),
"@api": path.resolve(__dirname, "./src/shared/api"),
"@config": path.resolve(__dirname, "./src/shared/config"),
"@model": path.resolve(__dirname, "./src/shared/model"),
"@styles": path.resolve(__dirname, "./src/shared/styles"),
"@types": path.resolve(__dirname, "./src/shared/types"),
"@ui": path.resolve(__dirname, "./src/shared/ui"),
},
},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tsconfig에는 @lib/*가 있는데 vite.config.ts alias에는 @lib가 빠져있어서 빌드 시에 @lib/... import가 깨질 수도 있을 것 같은데, alias 세팅에 @lib관련 세팅(@lib: path.resolve(__dirname, "./src/shared/lib"))도 추가하는게 어떨까요? ☺️

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 실수로 빼먹은 거 같아여!! 예리한 지적 감사합니다~

Comment on lines 29 to 47
"paths": {
"@/*": ["src/*"],
"@app/*": ["src/app/*"],
"@pages/*": ["src/pages/*"],
"@widgets/*": ["src/widgets/*"],
"@shared/*": ["src/shared/*"],
"@images/*": ["src/shared/assets/images/*"],
"@icons/*": ["src/shared/assets/icons/*"],
"@api/*": ["src/shared/api/*"],
"@config/*": ["src/shared/config/*"],
"@lib/*": ["src/shared/lib/*"],
"@model/*": ["src/shared/model/*"],
"@styles/*": ["src/shared/styles/*"],
"@types/*": ["src/shared/types/*"],
"@ui/*": ["src/shared/ui/*"]
}
},
"include": ["src"]
}
Copy link
Collaborator

@u-zzn u-zzn Jan 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

레이어 단위(@app/@pages/@widgets/@shared)랑 shared segment 단위(@api/@config/...)까지 꼼꼼하게 paths 열어두신 점 굳굳입니당!! 👍

src/app/main.tsx Outdated
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App.tsx";
import App from "./App";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기에서도 alias 적용해서 @app/App처럼 경로 가져와도 좋을 것 같아요 😊

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

바아로 반영했습니둥~

Copy link
Collaborator

@odukong odukong left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아티클과 PR내용 모두 채영님께서 많은 고민을 했다는게 느껴지는 글들이라 하나하나 소중히 너무 잘 읽었습니다... !
그래서 그런지 어떤 흐름으로 프로젝트를 구조화하고, 어떤 파일은 어떤 레이어로 위치하게 만들어야하는지 이전보다 좀 더 명확해진 것 같습니다!!
기존 FSD처럼 처음부터 너무 세분화해 구조화하기보다 지금처럼 경량화 된 FSD로 구조화하여 점진적으로 구체화시켜나가는 방향성도 너무 좋구요 🥰

현재 FSD 구조 기준으로 몇 가지 제 의견 덧붙여보자면,

채영님이 말씀해주신대로 컴핏 내부에서는 공통 컴포넌트는 아니지만 여러 페이지에서 사용되는 컴포넌트들이 많습니다. 그래서 widgets 계층이라는 레이어를 만들고, widgets 계층에서 의미 있는 블록 단위로 나누는 것이 적합하다고 말씀해주신 것! 저도 동의하는 바예요.
하지만 문득 widgets 계층만으로는, 프로젝트에서 사용되는 컴포넌트(공통 컴포넌트 제외)를 모두 widgets 계층에 정의하기에는 무리일 수도 있겠다는 생각이 들었어요.
그렇다고 해당 pages에서만 사용되는, 특히나 복잡한, 행동이 있는 컴포넌트를 pages 계층에서 정의하기에는 이미 구현되어있는 컴포넌트를 조립하는 기능을 중점으로 두는 pages 계층의 책임의 의미가 조금 달라지지 않을까 합니다.
그래서 features라는 레이어 계층을 추가로 두어, widgets는 의미있는 UI블록에 집중한다면, features는 비지니스로직을 포함하는 세부적인 컴포넌트(무언가 행위가 일어나는가)를 features에 위치하게 하는 방식도 한 번 제안드려요.

app → processes → pages → widgets → features → entities → shared

물론 FSD구조가 단방향 의존성을 추구하기 때문에, widgetsfeatures레이어를 참고할 수 있고, pagesfeatureswidgets을 모두 참조할 수 있는 방향인거죠!
간단하게 예시를 들자면, widgets에는 북마크 된 회사정보 레이아웃에 대한 company-card 컴포넌트가 위치하고 북마크를 취소하는 버튼을 bookmark-deletefeatures레이어에 위치시켜 company-card가 행동을 하는 bookmark-delete버튼을 포함하게 되는 흐름이 될겁니다.

추가적으로 pages계층은 라우팅을 위한 페이지가 바로 위치하는 것이 통상적으로 사용되는 경우가 많은 것 같아 ui slice도 별도로 정의하지 않고 아래처럼 pages 폴더 아래 바로 위치하게 하는 방식도 좋을 것 같다는 의견이에요.

├─ 📁 onboarding
	   ├─ 📋 onboarding-page.tsx
	   └─ 📋 onboarding-page.css.ts 

지금 말씀드린 것들은 순전히 개인 의견이기 때문에 회의 때 다시 한번 의견 나눠보고 기준을 픽스하면 좋을 것 같아요!!

노션에 남겨주신 마이페이지에 대한 개인적인 의견 남겨보자면, pages 레이어는 라우팅단위입니다. 마이페이지 (/mypage)나 북마크 페이지(/mypage/bookmark)역시 라우팅이 나누어지기 때문에 pages 아래 my-pagebook-mark처럼 별도의 slice로 구분하는 것이 적절하다는 의견입니다. 이와 비슷한 경우의 FSD구조를 나타내는 프로젝트 구조 예시인데 한번 참고해 주세요!!

이 부분도 마찬가지로 회의 때 의견 나눠보면 좋을 것 같아요!!

그리고 이미 코드리뷰는 유진님이 잘 남겨주셔서 제가 남겨드릴 부분은 딱히 없는 것 같네요 허허 수고 많으셨어요!! (≧∀≦)ゞ

Copy link
Collaborator

@qowjdals23 qowjdals23 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR이랑 작성해주신 FSD 관련 아티클 잘 읽었습니다 !!!
왜 FSD 구조를 도입했는지, 기준을 어디까지 가져갈지에 대한 고민 과정이 잘 정리돼 있어서 이번 구조 세팅의 방향성을 이해하기가 정말 수월했던 것 같아요 !

프로젝트 규모를 고려해서 app, shared, pages, widgets까지만 점진적으로 도입한 판단도 너무 좋았고,
회의 때 한 번 더 의견 나눠보고, 수빈님이 말씀해주신 것처럼 features 레이어를 도입하는 방향도 충분히 좋은 선택지일 것 같아요

그리고 PR 템플릿 인용으로 되어 있는 부분도 이번 PR에서 주석으로 한 번만 정리해주시면 감사하겠습니다 !ㅎㅎ 폴더 구조에 절대 경로까지 작업하시느라 정말 수고 많으셨습니다 ❤️‍🔥

유진님, 수빈님 꼼꼼한 코드리뷰도 읽어보면서 많이 배워갑니다 ... 👍

- pr 템플릿 주석 수정
- 빠진 절대 경로 추가
- app import 경로 수정
@hummingbbird
Copy link
Contributor Author

hummingbbird commented Jan 3, 2026

오늘 논의한 내용을 토대로 폴더 구조 변경 완료했습니다! 변경된 내용은 아래와 같아요.

1. features 레이어 추가
1차적으로 정한 방식대로 구조화한다면, widgets 폴더에 대부분에 컴포넌트를 두게 돼요. 도메인 단위로 구분하지 않으면 컴포넌트의 개수가 점점 많아질수록 오히려 구조화가 덜 되고, 구분이 어려워질 수 있다는 의견을 반영하여 features 레이어를 추가하기로 결정했어요.

  1. widgets 레이어 목적 및 구조 변경
    features 레이어는 도메인을 기준으로 slice를 구분하여 각 페이지에 사용되는 컴포넌트들을 정의해요. widgets 레이어에는 헤더, 푸터, 회사 카드 등 의미적으로 구분되며 여러 곳에서 사용되는 컴포넌트들을 정의해요. (결국, features 는 도메인 기준, widgets는 컴포넌트 기준으로 slice를 구분합니다) 두 레이어 모두 slice 내부 segment는 필요에 따라 추가하는 방식으로 진행해요.

변경 내용이 잘 반영되었는지 확인 한 번씩 부탁드려요! 추가적인 의견 역시 환영 🤗🤗

@u-zzn
Copy link
Collaborator

u-zzn commented Jan 3, 2026

제가 확인할 때는 아까 회의에서 이야기했던 방향대로 폴더 구조가 잘 잡힌 것 같습니다!! features 레이어를 추가하면서 도메인 기준과 컴포넌트 기준이 명확히 분리된 점이 좋은 것 같고, widgets 역할도 훨씬 또렷해진 것 같습니다 ☺️

다른 리뷰 반영된 부분들도 잘 확인했습니다. 구조 정리랑 리뷰 사항 빠르게 반영하느라 수고 많으셨습니다ㅠ!! 👍🖤

@odukong
Copy link
Collaborator

odukong commented Jan 3, 2026

변경된 폴더구조에 대한 반영사항들 잘 확인했습니다❕
변경 사항을 적용해 보니 컴포넌트를 어느 레이어에 위치시켜야 할지 분리하기가 훨씬 수월해질 것 같아요!
지금 정해진 featureswidgets 각 레이어의 기준도 정답이다!라고는 말할 수 없겠지만, 개발하면서 더 적합한 방향성이 존재한다면 조금씩 개선해나가면서 FSD구조에 대한 점진적 도입 진행하면 좋을 것 같습니다 !!
수고 많으셨어요🥰

@qowjdals23
Copy link
Collaborator

변경 내용 반영된 폴더 구조 잘 확인했습니다 !
회의에서 이야기했던 방향대로 features 레이어가 추가되면서 구조가 더 탄탄해진 느낌이네욥
빠르게 반영해주셔서 감사합니다 ❤️‍🔥

@hummingbbird hummingbbird merged commit 680d4d3 into dev Jan 3, 2026
2 checks passed
@hummingbbird hummingbbird deleted the init/#7/implement-fsd-folder-setting branch January 3, 2026 17:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Init] fsd 구조에 따른 폴더 구조 세팅

5 participants