[REFACTOR] 프로젝트 구조 FSD로 개편#26
Conversation
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthrough이 PR은 코드베이스 전체를 FSD(Feature Sliced Design) 아키텍처로 개편합니다. 공유 자산( Changes공유 자산 레이어 이관
기능별 feature 모듈 통합
위젯 및 페이지 레이어 연결
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (3)
src/shared/ui/AccordionMenu.tsx (1)
15-17: ⚡ Quick win조건부 Tailwind 클래스 결합은
cn()으로 통일해 주세요.Line 15, Line 17에서 템플릿 문자열로 조건부 클래스를 직접 이어붙이고 있어 스타일 조합 규칙이 분산됩니다. 이 파일도
cn()패턴으로 맞추면 가독성과 일관성이 좋아집니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/shared/ui/AccordionMenu.tsx` around lines 15 - 17, Replace the inline template-string conditional classNames in AccordionMenu.tsx with the shared cn() helper: update the <p> element that uses `{selected ? "text-main-main" : "text-gray-90"}` and the ArrowDownIcon className that uses `{selected ? "text-main-main rotate-180" : "text-gray-70"}` to call cn("text-body1-sb", selected ? "text-main-main" : "text-gray-90") and cn("size-6 transition-transform duration-200", selected ? "text-main-main rotate-180" : "text-gray-70") respectively so all conditional Tailwind class merging uses cn().Source: Coding guidelines
src/shared/ui/FileDragAndDrop.tsx (1)
45-49: ⚡ Quick win드래그 상태 클래스 조합도
cn()으로 맞춰주세요.Line 45-49는 조건부 클래스 문자열을 직접 이어붙이고 있습니다. 이미
cn을 사용 중이므로 이 구간도cn()으로 통일하면 유지보수가 쉬워집니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/shared/ui/FileDragAndDrop.tsx` around lines 45 - 49, Replace the inline template-string className construction with the existing cn() utility: locate the JSX element that sets className using the isDragging conditional (the block using isDragging ? "border-main-main bg-purple-10" : "bg-gray-10 ...") and wrap the static classes and the conditional classes in cn(...) (e.g., pass static class string and a conditional object or array keyed by isDragging). Update the className assignment so it uses cn(...) with the same static classes ("group rounded-12 w-full border border-dashed px-12 py-8 transition-colors") plus the conditional classes based on isDragging to keep behavior identical but consistent with other usages of cn.Source: Coding guidelines
src/shared/ui/dropdown/DateDropdownBox.tsx (1)
27-31: ⚡ Quick win조건부 텍스트/아이콘 클래스는
cn()사용을 권장합니다.Line 27, Line 31에서 템플릿 문자열로 조건부 클래스를 직접 합치고 있습니다. 공통 규칙대로
cn()으로 바꾸면 스타일 조건 관리가 더 명확해집니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/shared/ui/dropdown/DateDropdownBox.tsx` around lines 27 - 31, Replace the template-string conditional classNames in the DateDropdownBox component with the cn() helper: for the paragraph using selectedValue and placeholder (currently `text-body1-sb ${selectedValue ? "text-main-main" : "text-gray-70"}`) and for the ArrowDownIcon using isOpen (currently `text-gray-70 size-6 transition-transform duration-200 ${isOpen ? "rotate-180" : "rotate-0"}`), import/ensure cn is available and build className with cn() so the shared classes are passed as strings and the conditional classes use object/conditional arguments (preserving text-main-main/text-gray-70 and rotate-180/rotate-0 behavior).Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/features/instructor/home/lib/getDDay.ts`:
- Around line 4-7: The getDDay function currently constructs target = new
Date(deadline) and computes diff without checking for invalid dates; add a
validation after creating target (use Number.isNaN(target.getTime())) and if
invalid return a safe fallback (e.g., "-" ) or throw an error, otherwise
continue computing diff and returning `D-{n}`/`D+{n}` using the existing target,
today, and diff logic; update any callers/tests if they expect the previous
string.
In `@src/shared/ui/dropdown/BankDropdown.tsx`:
- Around line 5-12: BankDropdown currently imports BANK_OPTIONS, BankCode, and
BankOption from the features layer which breaks FSD one-way dependency; either
move the constants/types (BANK_OPTIONS, BankCode, BankOption) into the shared
layer and update BankDropdown to import them from shared, or relocate the
BankDropdown component into the features/signup layer so it depends inward;
modify imports in BankDropdown.tsx (and update any other files using those
symbols) to reflect the new module locations and ensure type exports for
BankCode/BankOption are available from the chosen target.
---
Nitpick comments:
In `@src/shared/ui/AccordionMenu.tsx`:
- Around line 15-17: Replace the inline template-string conditional classNames
in AccordionMenu.tsx with the shared cn() helper: update the <p> element that
uses `{selected ? "text-main-main" : "text-gray-90"}` and the ArrowDownIcon
className that uses `{selected ? "text-main-main rotate-180" : "text-gray-70"}`
to call cn("text-body1-sb", selected ? "text-main-main" : "text-gray-90") and
cn("size-6 transition-transform duration-200", selected ? "text-main-main
rotate-180" : "text-gray-70") respectively so all conditional Tailwind class
merging uses cn().
In `@src/shared/ui/dropdown/DateDropdownBox.tsx`:
- Around line 27-31: Replace the template-string conditional classNames in the
DateDropdownBox component with the cn() helper: for the paragraph using
selectedValue and placeholder (currently `text-body1-sb ${selectedValue ?
"text-main-main" : "text-gray-70"}`) and for the ArrowDownIcon using isOpen
(currently `text-gray-70 size-6 transition-transform duration-200 ${isOpen ?
"rotate-180" : "rotate-0"}`), import/ensure cn is available and build className
with cn() so the shared classes are passed as strings and the conditional
classes use object/conditional arguments (preserving text-main-main/text-gray-70
and rotate-180/rotate-0 behavior).
In `@src/shared/ui/FileDragAndDrop.tsx`:
- Around line 45-49: Replace the inline template-string className construction
with the existing cn() utility: locate the JSX element that sets className using
the isDragging conditional (the block using isDragging ? "border-main-main
bg-purple-10" : "bg-gray-10 ...") and wrap the static classes and the
conditional classes in cn(...) (e.g., pass static class string and a conditional
object or array keyed by isDragging). Update the className assignment so it uses
cn(...) with the same static classes ("group rounded-12 w-full border
border-dashed px-12 py-8 transition-colors") plus the conditional classes based
on isDragging to keep behavior identical but consistent with other usages of cn.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 409f4f1c-aa73-4052-a7e6-1449a6ca01d2
⛔ Files ignored due to path filters (44)
src/shared/assets/icons/icon_arrow_down.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_arrow_left.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_arrow_left_circle_fill.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_arrow_right.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_arrow_right_circle_fill.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_arrow_up.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_check_circle_fill.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_checkbox_border_gray.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_checkbox_border_white.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_checkbox_fill.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_close.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_close_circle.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_close_circle_fill.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_enter.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_exclamation_mark_circle.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_exit.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_exit_bold.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_eye_closed.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_eye_open.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_file.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_file_bold.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_file_image.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_file_image_bold.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_folder_add.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_loading.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_matching_off.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_matching_on.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_number_one_circle_fill.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_number_three_circle_fill.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_number_two_circle_fill.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_profile_circle.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_profile_circle_bold.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_search.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_search_bold.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_share.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_step_one_designer.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_step_one_instructor.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_step_three_designer.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_step_two_designer.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_step_two_instructor.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_user_type_designer.svgis excluded by!**/*.svgsrc/shared/assets/icons/icon_user_type_instructor.svgis excluded by!**/*.svgsrc/shared/assets/logos/ditda_logo_black.svgis excluded by!**/*.svgsrc/shared/assets/logos/ditda_logo_purple.svgis excluded by!**/*.svg
📒 Files selected for processing (95)
src/app/designer/layout.tsxsrc/app/instructor/layout.tsxsrc/app/instructor/page.tsxsrc/app/instructor/write/layout.tsxsrc/app/instructor/write/page.tsxsrc/app/login/layout.tsxsrc/app/login/page.tsxsrc/app/signup/designer/step1/page.tsxsrc/app/signup/designer/step2/page.tsxsrc/app/signup/designer/step3/page.tsxsrc/app/signup/instructor/step1/page.tsxsrc/app/signup/instructor/step2/page.tsxsrc/app/signup/layout.tsxsrc/app/signup/page.tsxsrc/assets/icons/index.tssrc/assets/logos/index.tssrc/features/instructor/home/index.tssrc/features/instructor/home/lib/getDDay.tssrc/features/instructor/home/model/home.tssrc/features/instructor/home/ui/CommissionsHeader.tsxsrc/features/instructor/home/ui/DraftSubmissionStatusRow.tsxsrc/features/instructor/home/ui/MatchingCommissionsRow.tsxsrc/features/instructor/home/ui/ModifyingCommissionsRow.tsxsrc/features/instructor/write/config/write.tssrc/features/instructor/write/index.tssrc/features/instructor/write/lib/color.tssrc/features/instructor/write/lib/date.tssrc/features/instructor/write/model/write.tssrc/features/instructor/write/model/writeFormStore.tssrc/features/instructor/write/ui/ColorChooseCard.tsxsrc/features/instructor/write/ui/ColorPicker.tsxsrc/features/instructor/write/ui/ConceptKeywordCard.tsxsrc/features/instructor/write/ui/PaperSizeCard.tsxsrc/features/instructor/write/ui/PaymentModal/PaymentModal.tsxsrc/features/instructor/write/ui/PaymentModal/Step1.tsxsrc/features/instructor/write/ui/PaymentModal/Step2.tsxsrc/features/instructor/write/ui/PlanChooseCard.tsxsrc/features/instructor/write/ui/ProgressBar.tsxsrc/features/instructor/write/ui/SizeRecommendedCard.tsxsrc/features/instructor/write/ui/StepHeader.tsxsrc/features/signup/config/signup.tssrc/features/signup/index.tssrc/features/signup/model/useSignupStep2Form.tssrc/features/signup/ui/UserTypeBtn.tsxsrc/shared/assets/icons/index.tssrc/shared/assets/logos/index.tssrc/shared/config/dropdown.tssrc/shared/config/sidebarMenu.tssrc/shared/lib/hooks/useUploadedFiles.tssrc/shared/lib/hooks/useWheelColumn.tssrc/shared/lib/utils/cn.tssrc/shared/lib/utils/dropdown.tssrc/shared/lib/utils/file.tssrc/shared/types/file.tssrc/shared/ui/AccordionMenu.tsxsrc/shared/ui/Badge.tsxsrc/shared/ui/Button.tsxsrc/shared/ui/Chip.tsxsrc/shared/ui/FileDragAndDrop.tsxsrc/shared/ui/FileUpload.tsxsrc/shared/ui/Header.tsxsrc/shared/ui/Modal.tsxsrc/shared/ui/PageIndicator.tsxsrc/shared/ui/Radio.tsxsrc/shared/ui/Sidebar.tsxsrc/shared/ui/SidebarMenu.tsxsrc/shared/ui/Tag.tsxsrc/shared/ui/Toggle.tsxsrc/shared/ui/dropdown/BankDropdown.tsxsrc/shared/ui/dropdown/DateDropdownBox.tsxsrc/shared/ui/dropdown/DateDropdownMenu.tsxsrc/shared/ui/dropdown/WheelColumn.tsxsrc/shared/ui/input/InputField.tsxsrc/shared/ui/input/SmallInput.tsxsrc/shared/ui/input/TextField.tsxsrc/widgets/instructor/home/config/home.tssrc/widgets/instructor/home/index.tssrc/widgets/instructor/home/lib/usePagination.tssrc/widgets/instructor/home/ui/DraftSubmissionStatusSection.tsxsrc/widgets/instructor/home/ui/MatchingCommissionsSection.tsxsrc/widgets/instructor/home/ui/ModifyingCommissionsSection.tsxsrc/widgets/instructor/write/index.tssrc/widgets/instructor/write/ui/AttachFileSection.tsxsrc/widgets/instructor/write/ui/BasicInfoTypingSection.tsxsrc/widgets/instructor/write/ui/CategorySection.tsxsrc/widgets/instructor/write/ui/ColorChooseSection.tsxsrc/widgets/instructor/write/ui/DeadlineChooseSection.tsxsrc/widgets/instructor/write/ui/DesignConceptSection.tsxsrc/widgets/instructor/write/ui/NecessaryPageChooseSection.tsxsrc/widgets/instructor/write/ui/PlanChooseSection.tsxsrc/widgets/instructor/write/ui/ReferenceSection.tsxsrc/widgets/instructor/write/ui/SizeSection.tsxsrc/widgets/instructor/write/ui/Step1Content.tsxsrc/widgets/instructor/write/ui/Step2Content.tsxsrc/widgets/instructor/write/ui/Step3Content.tsx
💤 Files with no reviewable changes (3)
- src/assets/logos/index.ts
- src/features/instructor/write/lib/date.ts
- src/assets/icons/index.ts
| const target = new Date(deadline); | ||
| target.setHours(0, 0, 0, 0); | ||
| const diff = Math.ceil((target.getTime() - today.getTime()) / (1000 * 60 * 60 * 24)); | ||
| return diff >= 0 ? `D-${diff}` : `D+${Math.abs(diff)}`; |
There was a problem hiding this comment.
유효하지 않은 날짜 입력 시 D+NaN이 반환됩니다.
new Date(deadline)가 Invalid Date인 경우 현재 분기에서 그대로 문자열이 만들어져 잘못된 D-Day가 노출됩니다. Number.isNaN(target.getTime()) 검증 후 안전한 fallback(예: "-" 반환 또는 예외 처리)을 넣어 주세요.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/features/instructor/home/lib/getDDay.ts` around lines 4 - 7, The getDDay
function currently constructs target = new Date(deadline) and computes diff
without checking for invalid dates; add a validation after creating target (use
Number.isNaN(target.getTime())) and if invalid return a safe fallback (e.g., "-"
) or throw an error, otherwise continue computing diff and returning
`D-{n}`/`D+{n}` using the existing target, today, and diff logic; update any
callers/tests if they expect the previous string.
| import { | ||
| BANK_DROPDOWN_MAX_HEIGHT, | ||
| BANK_OPTIONS, | ||
| type BankCode, | ||
| type BankOption, | ||
| } from "@/constants/signup"; | ||
| import { cn } from "@/lib/utils/cn"; | ||
| } from "@/features/signup"; | ||
| import { ArrowDownIcon, ArrowUpIcon } from "@/shared/assets/icons"; | ||
| import { cn } from "@/shared/lib/utils/cn"; |
There was a problem hiding this comment.
Shared 레이어에서 Features 레이어를 직접 참조하고 있습니다
Line 10에서 src/shared/ui/dropdown/BankDropdown.tsx가 @/features/signup을 import하여, FSD의 단방향 의존성(하위→상위 금지)을 깨고 있습니다. 이 상태는 shared 재사용성과 레이어 캡슐화를 약화시킵니다. BANK_OPTIONS/BankCode/BankOption을 shared로 내리거나, 해당 드롭다운을 features/signup로 올려 의존 방향을 맞춰주세요.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/shared/ui/dropdown/BankDropdown.tsx` around lines 5 - 12, BankDropdown
currently imports BANK_OPTIONS, BankCode, and BankOption from the features layer
which breaks FSD one-way dependency; either move the constants/types
(BANK_OPTIONS, BankCode, BankOption) into the shared layer and update
BankDropdown to import them from shared, or relocate the BankDropdown component
into the features/signup layer so it depends inward; modify imports in
BankDropdown.tsx (and update any other files using those symbols) to reflect the
new module locations and ensure type exports for BankCode/BankOption are
available from the chosen target.
📢 PR 유형
어떤 변경 사항이 있었나요?
📌 관련 이슈번호
✅ Key Changes
components,assets,constants,lib등 레이어 구분 없이 흩어져 있던 공통 자원을shared레이어(ui, lib, assets, config, types)로 통합 이동features/instructor/home,features/instructor/write,features/signup등)로 재배치entities/instructor/home을features/instructor/home으로 이동하고entities레이어 제거ui/,model/,lib/,config/)를 직접 import하던 부분을 배럴 파일(index.ts) 경로로 통일📸 스크린샷 or 실행영상
🎸 기타 사항 or 추가 코멘트
폴더 구조 개편 관련해서 노션 컨벤션 > 폴더 구조 가이드 (FSD 기반) 에 문서화 해두었습니다.
Summary by CodeRabbit
릴리스 노트
@/shared네임스페이스로 통합했습니다.@/features)을 생성하여 코드 구성을 명확하게 정리했습니다.