[feature & refactor] 객관식 항목 1개 허용 · 필수 입력 UX 개선 · Application 구조 리팩토링#558
[feature & refactor] 객관식 항목 1개 허용 · 필수 입력 UX 개선 · Application 구조 리팩토링#558oesnuj merged 18 commits intodevelop-fefrom
Conversation
…cationFormPage 전용으로 이동
…ditTab 및 ApplicationFormPage로 변경
✅ Deploy Preview for moadong ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
Warning
|
| 파일/경로 그룹 | 변경 요약 |
|---|---|
| frontend/src/App.tsx | 기존 지원서 작성/편집 컴포넌트 import 및 라우트 제거, 신규 컴포넌트(ApplicationEditTab, ApplicationFormPage)로 교체 |
| frontend/src/pages/AdminPage/application/answer/AnswerApplicationForm.tsx | 기존 AnswerApplicationForm 컴포넌트 파일 삭제 |
| frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx | CreateApplicationForm → ApplicationEditTab으로 컴포넌트 및 관련 import/로직/내보내기 변경, 선택형 문항 최소 아이템 수 2→1로 변경 |
| frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx | 신규 AnswerApplicationForm 컴포넌트 도입(지원서 작성/제출/유효성 검사/에러 처리 등 전체 구현) |
| frontend/src/pages/ApplicationFormPage/components/ChoiceItem/ChoiceItem.tsx frontend/src/pages/ApplicationFormPage/components/ChoiceItem/ChoiceItem.styles.ts |
ChoiceItem 컴포넌트 및 스타일 신규 추가(선택지 항목 UI) |
| frontend/src/pages/ApplicationFormPage/components/QuestionAnswerer/QuestionAnswerer.tsx frontend/src/pages/ApplicationFormPage/components/QuestionContainer/QuestionContainer.tsx |
신규 QuestionContainer 컴포넌트 추가(문항별 에러 표시), QuestionAnswerer에서 컴포넌트 import 경로 변경 |
| frontend/src/components/application/questionTypes/Choice.tsx | QuestionTitle/Description import 경로 변경, ChoiceItem 적용, 선택지 최소 개수 2→1, answer 모드에서 ChoiceItem 렌더링 도입 등 렌더링 로직 리팩토링 |
| frontend/src/components/application/questionTypes/ShortText.tsx frontend/src/components/application/questionTypes/LongText.tsx |
QuestionTitle/Description import 경로 변경, ShortText의 builder 모드에서 readOnly 속성 추가 |
| frontend/src/pages/AdminPage/components/QuestionBuilder/QuestionBuilder.tsx | 문항 타입 컴포넌트 import 경로 변경 |
| frontend/src/components/common/InputField/InputField.tsx | bgColor, textColor, borderColor props 및 관련 스타일 제거, 컴포넌트 인터페이스 단순화 |
| frontend/src/components/common/InputField/InputField.styles.ts | Input 스타일: 배경 투명, placeholder 컬러 트랜지션 추가, focus 시 placeholder 숨김 등 개선 |
| frontend/src/pages/ApplicationFormPage/ApplicationFormPage.styles.ts | QuestionsWrapper: 500px 이하에서 gap 50px→30px로 반응형 조정 |
Sequence Diagram(s)
sequenceDiagram
participant User
participant App
participant ApplicationFormPage (AnswerApplicationForm)
participant API
User->>App: /application/:clubId 라우트 접근
App->>ApplicationFormPage: 컴포넌트 렌더링
ApplicationFormPage->>API: 클럽 정보/질문 목록 fetch
API-->>ApplicationFormPage: 데이터 반환
ApplicationFormPage->>User: 지원서 폼/질문 렌더링
User->>ApplicationFormPage: 답변 입력/수정
User->>ApplicationFormPage: 제출 버튼 클릭
ApplicationFormPage->>ApplicationFormPage: 필수 문항 유효성 검사
alt 유효성 실패
ApplicationFormPage->>User: 에러 메시지/스크롤 이동
else 유효성 통과
ApplicationFormPage->>API: 답변 제출
API-->>ApplicationFormPage: 성공/실패 응답
ApplicationFormPage->>User: 결과 알림/이동
end
Possibly related PRs
- #536: 기존 지원서 작성/편집 컴포넌트 및 라우트 도입 PR로, 이번 PR의 교체 대상과 직접 연결됨.
- #490: AnswerApplicationForm 컴포넌트 및 답변 처리 로직을 도입한 PR로, 이번 PR에서 해당 컴포넌트가 제거되고 신규 컴포넌트로 대체됨.
- #499: 기존 지원서 작성/편집 컴포넌트에서 API 연동 및 리팩토링을 수행한 PR로, 이번 PR에서 해당 컴포넌트가 제거되고 새로운 구조로 전환됨.
Suggested reviewers
- seongwon030
✨ Finishing Touches
- 📝 Generate Docstrings
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.
🪧 Tips
Chat
There are 3 ways to chat with CodeRabbit:
- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
I pushed a fix in commit <commit_id>, please review it.Explain this complex logic.Open a follow-up GitHub issue for this discussion.
- Files and specific lines of code (under the "Files changed" tab): Tag
@coderabbitaiin a new review comment at the desired location with your query. Examples:@coderabbitai explain this code block.@coderabbitai modularize this function.
- PR comments: Tag
@coderabbitaiin a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.@coderabbitai read src/utils.ts and explain its main purpose.@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.@coderabbitai help me debug CodeRabbit configuration file.
Support
Need help? Create a ticket on our support page for assistance with any issues or questions.
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.
CodeRabbit Commands (Invoked using PR comments)
@coderabbitai pauseto pause the reviews on a PR.@coderabbitai resumeto resume the paused reviews.@coderabbitai reviewto trigger an incremental review. This is useful when automatic reviews are disabled for the repository.@coderabbitai full reviewto do a full review from scratch and review all the files again.@coderabbitai summaryto regenerate the summary of the PR.@coderabbitai generate docstringsto generate docstrings for this PR.@coderabbitai generate sequence diagramto generate a sequence diagram of the changes in this PR.@coderabbitai resolveresolve all the CodeRabbit review comments.@coderabbitai configurationto show the current CodeRabbit configuration for the repository.@coderabbitai helpto get help.
Other keywords and placeholders
- Add
@coderabbitai ignoreanywhere in the PR description to prevent this PR from being reviewed. - Add
@coderabbitai summaryto generate the high-level summary at a specific location in the PR description. - Add
@coderabbitaianywhere in the PR title to generate the title automatically.
Documentation and Community
- Visit our Documentation for detailed information on how to use CodeRabbit.
- Join our Discord Community to get help, request features, and share feedback.
- Follow us on X/Twitter for updates and announcements.
There was a problem hiding this comment.
Pull Request Overview
This PR implements support for single-choice multiple choice questions, improves required input UX, and refactors the Application component structure. The changes focus on enhancing user experience for form validation and creating better separation between admin and user-facing components.
- Allow minimum 1 choice item instead of requiring 2+ options for multiple choice questions
- Introduce dedicated ChoiceItem component to resolve styling conflicts with InputField
- Add immediate error feedback with QuestionContainer highlighting and auto-scroll to invalid questions
Reviewed Changes
Copilot reviewed 15 out of 21 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| ApplicationFormPage.tsx | New user-facing application form with enhanced validation and error handling |
| QuestionContainer.tsx | New component for wrapping questions with error state display |
| ChoiceItem.tsx | New dedicated component for choice selection in answer mode |
| Choice.tsx | Updated to use ChoiceItem for answer mode and support minimum 1 item |
| ApplicationEditTab.tsx | Renamed and updated admin form component with single choice support |
| InputField.tsx | Enhanced placeholder hiding on focus and removed unused styling props |
Comments suppressed due to low confidence (1)
frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx:16
- The component name 'AnswerApplicationForm' doesn't match the file name 'ApplicationFormPage'. Consider renaming the component to 'ApplicationFormPage' for consistency.
const AnswerApplicationForm = () => {
frontend/src/pages/ApplicationFormPage/components/QuestionContainer/QuestionContainer.tsx
Show resolved
Hide resolved
| `; | ||
|
|
||
| const ErrorText = styled.div` | ||
| color: #FF5414; |
There was a problem hiding this comment.
The color value '#FF5414' is duplicated. Consider defining this as a theme constant for consistency.
|
|
||
| try { | ||
| await applyToClub(clubId, answers); | ||
| alert('답변이 성공적으로 제출되었습니다.'); |
There was a problem hiding this comment.
Using alert() for success messages provides poor UX. Consider using a toast notification or modal component for better user experience.
There was a problem hiding this comment.
Actionable comments posted: 4
🔭 Outside diff range comments (1)
frontend/src/pages/ApplicationFormPage/ApplicationFormPage.styles.ts (1)
12-21: 반응형 디자인 개선은 좋지만 매직 넘버를 명명된 상수로 교체하세요.작은 화면에서 더 나은 사용자 경험을 위한 간격 조정은 좋은 개선사항입니다. 하지만 코딩 가이드라인에 따라 매직 넘버를 명명된 상수로 교체해야 합니다.
다음과 같이 상수를 정의하여 가독성을 향상시키세요:
+const SPACING = { + QUESTIONS_GAP_DESKTOP: '50px', + QUESTIONS_GAP_MOBILE: '30px', +} as const; + +const BREAKPOINTS = { + MOBILE_MAX: '500px', +} as const; + export const QuestionsWrapper = styled.div` display: flex; flex-direction: column; - gap: 50px; + gap: ${SPACING.QUESTIONS_GAP_DESKTOP}; - @media (max-width: 500px) { - gap: 30px; + @media (max-width: ${BREAKPOINTS.MOBILE_MAX}) { + gap: ${SPACING.QUESTIONS_GAP_MOBILE}; } `;
🧹 Nitpick comments (6)
frontend/src/components/application/questionTypes/ShortText.tsx (1)
36-37: 중복된 readOnly 속성을 제거하세요.
disabled={mode === 'builder'}와readOnly={mode === 'builder'}가 동일한 조건으로 설정되어 있습니다.disabled속성만으로도 충분히 입력을 막을 수 있으므로readOnly속성은 불필요합니다.disabled={mode === 'builder'} - readOnly={mode === 'builder'}frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx (2)
33-34: 매직 넘버를 상수로 대체하여 가독성 개선가이드라인에 따라 매직 넘버를 명명된 상수로 대체해야 합니다.
파일 상단에 상수를 정의하고 사용하세요:
+const INITIAL_QUESTION_ID = 1; + const ApplicationEditTab = () => { const { clubId } = useAdminClubContext(); if (!clubId) return null; const { data, isLoading, isError } = useGetApplication(clubId); const [formData, setFormData] = useState<ApplicationFormData>(INITIAL_FORM_DATA); useEffect(() => { if (data) { setFormData(data); } }, [data]); const [nextId, setNextId] = useState(() => { const questions = data?.questions ?? INITIAL_FORM_DATA.questions; - if (questions.length === 0) return 1; + if (questions.length === 0) return INITIAL_QUESTION_ID; const maxId = Math.max(...questions.map((q: Question) => q.id)); - return maxId + 1; + return maxId + INITIAL_QUESTION_ID; });
119-122: 매직 넘버 개선 제안ID 재정렬 로직에서 매직 넘버를 상수로 대체하여 의도를 명확히 하세요.
+const QUESTION_ID_START = 1; + const handleSubmit = async () => { if (!clubId) return; const reorderedQuestions = formData.questions.map((q, idx) => ({ ...q, - id: idx + 1, + id: idx + QUESTION_ID_START, }));frontend/src/pages/ApplicationFormPage/components/QuestionContainer/QuestionContainer.tsx (1)
4-18: 스타일 상수를 명명된 변수로 분리하여 유지보수성 향상가이드라인에 따라 매직 넘버와 하드코딩된 값들을 상수로 분리해야 합니다.
+const THEME = { + colors: { + error: '#FF5414', + transparent: 'transparent', + }, + spacing: { + borderRadius: '16px', + padding: '24px', + scrollMarginTop: '120px', + errorTextMargin: '8px', + }, + typography: { + errorFontSize: '0.9rem', + errorFontWeight: 600, + }, + animation: { + borderTransition: 'border 0.2s ease', + }, +} as const; + +const ERROR_MESSAGE = '‼️ 필수 입력 문항입니다'; + const Container = styled.div<{ hasError?: boolean }>` - border: ${({ hasError }) => (hasError ? '1px solid #FF5414' : 'transparent')}; - border-radius: 16px; - padding: 24px; + border: ${({ hasError }) => (hasError ? `1px solid ${THEME.colors.error}` : THEME.colors.transparent)}; + border-radius: ${THEME.spacing.borderRadius}; + padding: ${THEME.spacing.padding}; position: relative; - scroll-margin-top: 120px; - transition: border 0.2s ease; + scroll-margin-top: ${THEME.spacing.scrollMarginTop}; + transition: ${THEME.animation.borderTransition}; `; const ErrorText = styled.div` - color: #FF5414; - font-size: 0.9rem; - font-weight: 600; - margin-bottom: 8px; + color: ${THEME.colors.error}; + font-size: ${THEME.typography.errorFontSize}; + font-weight: ${THEME.typography.errorFontWeight}; + margin-bottom: ${THEME.spacing.errorTextMargin}; `;그리고 JSX에서도 상수 사용:
- {hasError && <ErrorText>‼️ 필수 입력 문항입니다</ErrorText>} + {hasError && <ErrorText>{ERROR_MESSAGE}</ErrorText>}frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx (2)
97-97: 매직 넘버를 상수로 대체가이드라인에 따라 매직 넘버를 명명된 상수로 대체해야 합니다.
+const HEADER_OFFSET = 172; + return ( <> <Header /> - <PageContainer style={{ paddingTop: '172px' }}> + <PageContainer style={{ paddingTop: `${HEADER_OFFSET}px` }}>
16-34: 상태 관리를 더 작은 훅으로 분리 고려가이드라인에 따라 광범위한 상태 관리를 더 작은, 집중된 훅으로 분리하는 것을 고려해보세요. 현재 컴포넌트가 많은 책임을 가지고 있습니다.
다음과 같이 커스텀 훅으로 분리할 수 있습니다:
// useApplicationFormValidation.ts const useApplicationFormValidation = (formData, getAnswersById) => { const [invalidQuestionIds, setInvalidQuestionIds] = useState<number[]>([]); const questionRefs = useRef<Array<HTMLDivElement | null>>([]); const validateRequiredQuestions = () => { /* ... */ }; const scrollToFirstInvalidQuestion = (invalidIds: number[]) => { /* ... */ }; return { invalidQuestionIds, setInvalidQuestionIds, questionRefs, validateRequiredQuestions, scrollToFirstInvalidQuestion, }; };
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (15)
frontend/src/App.tsx(3 hunks)frontend/src/components/application/questionTypes/Choice.tsx(3 hunks)frontend/src/components/application/questionTypes/LongText.tsx(1 hunks)frontend/src/components/application/questionTypes/ShortText.tsx(2 hunks)frontend/src/components/common/InputField/InputField.styles.ts(3 hunks)frontend/src/components/common/InputField/InputField.tsx(0 hunks)frontend/src/pages/AdminPage/application/answer/AnswerApplicationForm.tsx(0 hunks)frontend/src/pages/AdminPage/components/QuestionBuilder/QuestionBuilder.tsx(1 hunks)frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx(3 hunks)frontend/src/pages/ApplicationFormPage/ApplicationFormPage.styles.ts(1 hunks)frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx(1 hunks)frontend/src/pages/ApplicationFormPage/components/ChoiceItem/ChoiceItem.styles.ts(1 hunks)frontend/src/pages/ApplicationFormPage/components/ChoiceItem/ChoiceItem.tsx(1 hunks)frontend/src/pages/ApplicationFormPage/components/QuestionAnswerer/QuestionAnswerer.tsx(1 hunks)frontend/src/pages/ApplicationFormPage/components/QuestionContainer/QuestionContainer.tsx(1 hunks)
💤 Files with no reviewable changes (2)
- frontend/src/pages/AdminPage/application/answer/AnswerApplicationForm.tsx
- frontend/src/components/common/InputField/InputField.tsx
🧰 Additional context used
📓 Path-based instructions (2)
frontend/**/*.{ts,tsx}
Instructions used from:
Sources:
📄 CodeRabbit Inference Engine
- frontend/.cursorrules
frontend/**/*.tsx
Instructions used from:
Sources:
📄 CodeRabbit Inference Engine
- frontend/.cursorrules
🧠 Learnings (14)
📓 Common learnings
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Choose field-level or form-level cohesion based on form requirements.
frontend/src/pages/ApplicationFormPage/ApplicationFormPage.styles.ts (3)
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Choose field-level or form-level cohesion based on form requirements.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.{ts,tsx} : Replace magic numbers with named constants for clarity.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Separate significantly different conditional UI/logic into distinct components.
frontend/src/components/common/InputField/InputField.styles.ts (2)
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.{ts,tsx} : Replace magic numbers with named constants for clarity.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Choose field-level or form-level cohesion based on form requirements.
frontend/src/App.tsx (12)
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Separate significantly different conditional UI/logic into distinct components.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Abstract complex logic/interactions into dedicated components or higher-order components (HOCs).
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Choose field-level or form-level cohesion based on form requirements.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.{ts,tsx} : Replace magic numbers with named constants for clarity.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Use component composition instead of props drilling.
Learnt from: lepitaaar
PR: Moadong/moadong#406
File: backend/src/main/java/moadong/club/service/ClubApplyService.java:34-38
Timestamp: 2025-05-19T05:45:52.957Z
Learning: The code duplication between createClubApplication and editClubApplication methods in ClubApplyService.java is acknowledged but will be addressed in a future refactoring, as per the developer's plan.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Colocate simple, localized logic or use inline definitions to reduce context switching.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Break down broad state management into smaller, focused hooks or contexts.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.{ts,tsx} : Use consistent return types for similar functions and hooks.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.{ts,tsx} : Define constants near related logic or ensure names link them clearly.
Learnt from: seongwon030
PR: Moadong/moadong#195
File: frontend/src/pages/AdminPage/AdminPage.tsx:7-7
Timestamp: 2025-03-19T05:18:07.818Z
Learning: AdminPage.tsx에서 현재 하드코딩된 클럽 ID('67d2e3b9b15c136c6acbf20b')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.
Learnt from: seongwon030
PR: Moadong/moadong#548
File: frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx:17-57
Timestamp: 2025-07-19T05:05:10.171Z
Learning: ClubDetailPage.tsx에서 notJoinedClubNames 배열의 하드코딩은 의도적인 설계 결정입니다. 개발자가 명시적으로 하드코딩을 선택했으므로 이에 대한 리팩토링 제안을 하지 않아야 합니다.
frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx (10)
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.{ts,tsx} : Replace magic numbers with named constants for clarity.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Separate significantly different conditional UI/logic into distinct components.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Choose field-level or form-level cohesion based on form requirements.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Abstract complex logic/interactions into dedicated components or higher-order components (HOCs).
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Use component composition instead of props drilling.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.{ts,tsx} : Define constants near related logic or ensure names link them clearly.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.{ts,tsx} : Use consistent return types for similar functions and hooks.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Break down broad state management into smaller, focused hooks or contexts.
Learnt from: lepitaaar
PR: Moadong/moadong#406
File: backend/src/main/java/moadong/club/service/ClubApplyService.java:34-38
Timestamp: 2025-05-19T05:45:52.957Z
Learning: The code duplication between createClubApplication and editClubApplication methods in ClubApplyService.java is acknowledged but will be addressed in a future refactoring, as per the developer's plan.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Colocate simple, localized logic or use inline definitions to reduce context switching.
frontend/src/pages/ApplicationFormPage/components/QuestionContainer/QuestionContainer.tsx (4)
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Separate significantly different conditional UI/logic into distinct components.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Use component composition instead of props drilling.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Choose field-level or form-level cohesion based on form requirements.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Abstract complex logic/interactions into dedicated components or higher-order components (HOCs).
frontend/src/pages/ApplicationFormPage/components/QuestionAnswerer/QuestionAnswerer.tsx (9)
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Choose field-level or form-level cohesion based on form requirements.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.{ts,tsx} : Replace magic numbers with named constants for clarity.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Separate significantly different conditional UI/logic into distinct components.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Colocate simple, localized logic or use inline definitions to reduce context switching.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.{ts,tsx} : Define constants near related logic or ensure names link them clearly.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Use component composition instead of props drilling.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.{ts,tsx} : Use consistent return types for similar functions and hooks.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Abstract complex logic/interactions into dedicated components or higher-order components (HOCs).
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Break down broad state management into smaller, focused hooks or contexts.
frontend/src/pages/AdminPage/components/QuestionBuilder/QuestionBuilder.tsx (10)
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.{ts,tsx} : Replace magic numbers with named constants for clarity.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Choose field-level or form-level cohesion based on form requirements.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.{ts,tsx} : Define constants near related logic or ensure names link them clearly.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.{ts,tsx} : Use consistent return types for similar functions and hooks.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Separate significantly different conditional UI/logic into distinct components.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Use component composition instead of props drilling.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Colocate simple, localized logic or use inline definitions to reduce context switching.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Break down broad state management into smaller, focused hooks or contexts.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Abstract complex logic/interactions into dedicated components or higher-order components (HOCs).
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.{ts,tsx} : Use unique, descriptive names for custom wrappers and functions to avoid ambiguity.
frontend/src/components/application/questionTypes/ShortText.tsx (10)
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Separate significantly different conditional UI/logic into distinct components.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.{ts,tsx} : Define constants near related logic or ensure names link them clearly.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Choose field-level or form-level cohesion based on form requirements.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Colocate simple, localized logic or use inline definitions to reduce context switching.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.{ts,tsx} : Replace magic numbers with named constants for clarity.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Break down broad state management into smaller, focused hooks or contexts.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Use component composition instead of props drilling.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Abstract complex logic/interactions into dedicated components or higher-order components (HOCs).
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.{ts,tsx} : Use consistent return types for similar functions and hooks.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.{ts,tsx} : Use unique, descriptive names for custom wrappers and functions to avoid ambiguity.
frontend/src/pages/ApplicationFormPage/components/ChoiceItem/ChoiceItem.styles.ts (6)
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Choose field-level or form-level cohesion based on form requirements.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Separate significantly different conditional UI/logic into distinct components.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.{ts,tsx} : Replace magic numbers with named constants for clarity.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.{ts,tsx} : Use unique, descriptive names for custom wrappers and functions to avoid ambiguity.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.{ts,tsx} : Define constants near related logic or ensure names link them clearly.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Use component composition instead of props drilling.
frontend/src/components/application/questionTypes/Choice.tsx (10)
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.{ts,tsx} : Replace magic numbers with named constants for clarity.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Choose field-level or form-level cohesion based on form requirements.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Separate significantly different conditional UI/logic into distinct components.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.{ts,tsx} : Use consistent return types for similar functions and hooks.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Colocate simple, localized logic or use inline definitions to reduce context switching.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.{ts,tsx} : Replace complex or nested ternary operators with if/else statements or IIFEs for readability.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Use component composition instead of props drilling.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Abstract complex logic/interactions into dedicated components or higher-order components (HOCs).
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.{ts,tsx} : Define constants near related logic or ensure names link them clearly.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Break down broad state management into smaller, focused hooks or contexts.
frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx (6)
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Abstract complex logic/interactions into dedicated components or higher-order components (HOCs).
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Choose field-level or form-level cohesion based on form requirements.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Separate significantly different conditional UI/logic into distinct components.
Learnt from: seongwon030
PR: Moadong/moadong#195
File: frontend/src/pages/AdminPage/AdminPage.tsx:7-7
Timestamp: 2025-03-19T05:18:07.818Z
Learning: AdminPage.tsx에서 현재 하드코딩된 클럽 ID('67d2e3b9b15c136c6acbf20b')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Use component composition instead of props drilling.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Break down broad state management into smaller, focused hooks or contexts.
frontend/src/pages/ApplicationFormPage/components/ChoiceItem/ChoiceItem.tsx (6)
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Choose field-level or form-level cohesion based on form requirements.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Separate significantly different conditional UI/logic into distinct components.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.{ts,tsx} : Replace magic numbers with named constants for clarity.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Use component composition instead of props drilling.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.{ts,tsx} : Use consistent return types for similar functions and hooks.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Abstract complex logic/interactions into dedicated components or higher-order components (HOCs).
frontend/src/components/application/questionTypes/LongText.tsx (10)
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Separate significantly different conditional UI/logic into distinct components.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.{ts,tsx} : Define constants near related logic or ensure names link them clearly.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.{ts,tsx} : Replace magic numbers with named constants for clarity.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Colocate simple, localized logic or use inline definitions to reduce context switching.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Abstract complex logic/interactions into dedicated components or higher-order components (HOCs).
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Use component composition instead of props drilling.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.{ts,tsx} : Use consistent return types for similar functions and hooks.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Break down broad state management into smaller, focused hooks or contexts.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.tsx : Choose field-level or form-level cohesion based on form requirements.
Learnt from: CR
PR: Moadong/moadong#0
File: frontend/.cursorrules:0-0
Timestamp: 2025-07-19T05:09:10.682Z
Learning: Applies to frontend/**/*.{ts,tsx} : Use unique, descriptive names for custom wrappers and functions to avoid ambiguity.
🧬 Code Graph Analysis (1)
frontend/src/components/application/questionTypes/Choice.tsx (1)
frontend/src/constants/APPLICATION_FORM.ts (1)
APPLICATION_FORM(1-22)
🔇 Additional comments (14)
frontend/src/pages/AdminPage/components/QuestionBuilder/QuestionBuilder.tsx (1)
2-4: 임포트 경로 리팩토링이 잘 되었습니다.질문 타입 컴포넌트를 중앙 집중화하는 리팩토링이 올바르게 적용되었습니다. 이는 관리자와 사용자 폼 간의 컴포넌트 재사용성을 개선합니다.
frontend/src/components/application/questionTypes/LongText.tsx (1)
1-2: 컴포넌트 중앙화 리팩토링이 적절히 적용되었습니다.QuestionTitle과 QuestionDescription 컴포넌트의 임포트 경로가 더 직관적인 구조로 업데이트되었습니다. 이는 관리자와 사용자 인터페이스 간의 컴포넌트 공유를 개선합니다.
frontend/src/pages/ApplicationFormPage/components/QuestionAnswerer/QuestionAnswerer.tsx (1)
2-4: 일관된 임포트 경로 리팩토링이 잘 적용되었습니다.질문 타입 컴포넌트들의 임포트 경로가 공유 컴포넌트 디렉토리로 일관성 있게 업데이트되었습니다. 이는 전체 리팩토링 전략과 일치하며 컴포넌트 재사용성을 향상시킵니다.
frontend/src/components/common/InputField/InputField.styles.ts (1)
32-32: 투명 배경색 설정이 적절합니다.InputField의 배경을 투명하게 설정하여 일관된 스타일링을 제공합니다.
frontend/src/components/application/questionTypes/ShortText.tsx (1)
1-2: import 경로 변경이 적절합니다.컴포넌트 구조 리팩토링에 따른 import 경로 업데이트가 올바르게 적용되었습니다.
frontend/src/App.tsx (2)
15-15: 컴포넌트 구조 리팩토링이 올바르게 적용되었습니다.새로운 컴포넌트 import가 적절하게 추가되었습니다.
Also applies to: 19-19
77-77: 라우팅 업데이트가 적절합니다.관리자용과 사용자용 컴포넌트 분리에 따른 라우팅 변경이 올바르게 적용되었습니다.
Also applies to: 89-89
frontend/src/pages/ApplicationFormPage/components/ChoiceItem/ChoiceItem.tsx (1)
1-17: 새로운 ChoiceItem 컴포넌트가 잘 설계되었습니다.PR 목표에서 언급된 스타일 충돌 문제를 해결하는 깔끔한 컴포넌트입니다. 빈 라벨에 대한 fallback 텍스트 처리도 사용자 경험을 고려한 좋은 구현입니다.
frontend/src/pages/ApplicationFormPage/components/ChoiceItem/ChoiceItem.styles.ts (1)
18-24: Label 컴포넌트의 텍스트 오버플로우 처리가 적절합니다.긴 텍스트에 대한 ellipsis 처리가 잘 구현되어 있습니다.
frontend/src/components/application/questionTypes/Choice.tsx (3)
2-3: import 경로 변경 및 새 컴포넌트 추가가 적절합니다.컴포넌트 구조 리팩토링에 따른 import 변경과 새로운 ChoiceItem 컴포넌트 import가 올바르게 적용되었습니다.
Also applies to: 7-7
9-9: 최소 항목 수 변경이 PR 목표와 일치합니다.PR 목표에서 언급된 "객관식 항목 1개 허용" 요구사항이 올바르게 구현되었습니다.
86-100: 조건부 렌더링이 잘 구현되었습니다.answer 모드와 builder 모드에 따른 다른 UI 렌더링이 적절하게 분리되어 있습니다. 이는 PR 목표에서 언급된 스타일 충돌 문제를 해결하는 좋은 접근입니다. retrieved learning에서 언급된 "significantly different conditional UI/logic into distinct components" 원칙도 잘 따르고 있습니다.
frontend/src/pages/AdminPage/tabs/ApplicationEditTab/ApplicationEditTab.tsx (1)
99-101: 선택지 최소 개수 변경이 올바르게 구현됨단일 선택지 허용 요구사항에 맞게 최소 개수가 1개로 올바르게 변경되었습니다.
frontend/src/pages/ApplicationFormPage/components/QuestionContainer/QuestionContainer.tsx (1)
25-34: forwardRef 구현이 올바르게 되어 있음ref 전달과 조건부 에러 표시 로직이 올바르게 구현되었습니다.
| const isEmpty = | ||
| (Array.isArray(value) && value.every(v => v.trim() === '')) || | ||
| (!Array.isArray(value) && value.trim() === ''); | ||
|
|
||
| if (!isEmpty && invalidQuestionIds.includes(id)) { | ||
| setInvalidQuestionIds(prev => prev.filter(qid => qid !== id)); | ||
| } | ||
| }; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
복잡한 조건부 로직을 명명된 변수로 분리
가이드라인에 따라 복잡한 boolean 조건을 명명된 변수로 할당해야 합니다.
const onAnswerChange = (id: number, value: string | string[]) => {
rawOnAnswerChange(id, value);
// 입력이 생겼다면 해당 질문의 오류 제거
- const isEmpty =
- (Array.isArray(value) && value.every(v => v.trim() === '')) ||
- (!Array.isArray(value) && value.trim() === '');
+ const isArrayEmpty = Array.isArray(value) && value.every(v => v.trim() === '');
+ const isStringEmpty = !Array.isArray(value) && value.trim() === '';
+ const isEmpty = isArrayEmpty || isStringEmpty;
- if (!isEmpty && invalidQuestionIds.includes(id)) {
+ const hasValidInput = !isEmpty;
+ const isCurrentlyInvalid = invalidQuestionIds.includes(id);
+
+ if (hasValidInput && isCurrentlyInvalid) {
setInvalidQuestionIds(prev => prev.filter(qid => qid !== id));
}
};🤖 Prompt for AI Agents
In frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx around lines
42 to 49, the complex boolean condition checking if the value is empty should be
assigned to a clearly named variable for better readability. Extract the
condition that determines if the value is empty into a separate, descriptively
named constant before using it in the if statement.
| const handleSubmit = async () => { | ||
| const invalidIds: number[] = formData.questions | ||
| .filter((q: Question) => q.options.required) | ||
| .filter((q: Question) => { | ||
| const a = getAnswersById(q.id); | ||
| return a.length === 0 || a.every((s) => s.trim() === ''); | ||
| }) | ||
| .map((q: Question) => q.id); | ||
|
|
||
| if (invalidIds.length > 0) { | ||
| setInvalidQuestionIds(invalidIds); | ||
|
|
||
| const firstInvalidIndex = formData.questions.findIndex((q: Question) => | ||
| invalidIds.includes(q.id), | ||
| ); | ||
| const targetEl = questionRefs.current[firstInvalidIndex]; | ||
| targetEl?.scrollIntoView({ behavior: 'smooth', block: 'start' }); | ||
| return; | ||
| } | ||
|
|
||
| try { | ||
| await applyToClub(clubId, answers); | ||
| alert('답변이 성공적으로 제출되었습니다.'); | ||
| } catch (e) { | ||
| alert('답변 제출에 실패했습니다. 잠시 후 다시 시도해 주세요.'); | ||
| } | ||
| }; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
긴 함수를 작은 함수들로 분리하여 가독성 향상
가이드라인에 따라 복잡한 로직을 전용 컴포넌트나 함수로 분리해야 합니다.
+ const validateRequiredQuestions = () => {
+ return formData.questions
+ .filter((q: Question) => q.options.required)
+ .filter((q: Question) => {
+ const answers = getAnswersById(q.id);
+ return answers.length === 0 || answers.every((s) => s.trim() === '');
+ })
+ .map((q: Question) => q.id);
+ };
+
+ const scrollToFirstInvalidQuestion = (invalidIds: number[]) => {
+ const firstInvalidIndex = formData.questions.findIndex((q: Question) =>
+ invalidIds.includes(q.id),
+ );
+ const targetElement = questionRefs.current[firstInvalidIndex];
+ targetElement?.scrollIntoView({ behavior: 'smooth', block: 'start' });
+ };
+
+ const submitAnswers = async () => {
+ try {
+ await applyToClub(clubId, answers);
+ alert('답변이 성공적으로 제출되었습니다.');
+ } catch (error) {
+ alert('답변 제출에 실패했습니다. 잠시 후 다시 시도해 주세요.');
+ }
+ };
+
const handleSubmit = async () => {
- const invalidIds: number[] = formData.questions
- .filter((q: Question) => q.options.required)
- .filter((q: Question) => {
- const a = getAnswersById(q.id);
- return a.length === 0 || a.every((s) => s.trim() === '');
- })
- .map((q: Question) => q.id);
+ const invalidIds = validateRequiredQuestions();
if (invalidIds.length > 0) {
setInvalidQuestionIds(invalidIds);
-
- const firstInvalidIndex = formData.questions.findIndex((q: Question) =>
- invalidIds.includes(q.id),
- );
- const targetEl = questionRefs.current[firstInvalidIndex];
- targetEl?.scrollIntoView({ behavior: 'smooth', block: 'start' });
+ scrollToFirstInvalidQuestion(invalidIds);
return;
}
- try {
- await applyToClub(clubId, answers);
- alert('답변이 성공적으로 제출되었습니다.');
- } catch (e) {
- alert('답변 제출에 실패했습니다. 잠시 후 다시 시도해 주세요.');
- }
+ await submitAnswers();
};📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const handleSubmit = async () => { | |
| const invalidIds: number[] = formData.questions | |
| .filter((q: Question) => q.options.required) | |
| .filter((q: Question) => { | |
| const a = getAnswersById(q.id); | |
| return a.length === 0 || a.every((s) => s.trim() === ''); | |
| }) | |
| .map((q: Question) => q.id); | |
| if (invalidIds.length > 0) { | |
| setInvalidQuestionIds(invalidIds); | |
| const firstInvalidIndex = formData.questions.findIndex((q: Question) => | |
| invalidIds.includes(q.id), | |
| ); | |
| const targetEl = questionRefs.current[firstInvalidIndex]; | |
| targetEl?.scrollIntoView({ behavior: 'smooth', block: 'start' }); | |
| return; | |
| } | |
| try { | |
| await applyToClub(clubId, answers); | |
| alert('답변이 성공적으로 제출되었습니다.'); | |
| } catch (e) { | |
| alert('답변 제출에 실패했습니다. 잠시 후 다시 시도해 주세요.'); | |
| } | |
| }; | |
| const validateRequiredQuestions = () => { | |
| return formData.questions | |
| .filter((q: Question) => q.options.required) | |
| .filter((q: Question) => { | |
| const answers = getAnswersById(q.id); | |
| return answers.length === 0 || answers.every((s) => s.trim() === ''); | |
| }) | |
| .map((q: Question) => q.id); | |
| }; | |
| const scrollToFirstInvalidQuestion = (invalidIds: number[]) => { | |
| const firstInvalidIndex = formData.questions.findIndex((q: Question) => | |
| invalidIds.includes(q.id), | |
| ); | |
| const targetElement = questionRefs.current[firstInvalidIndex]; | |
| targetElement?.scrollIntoView({ behavior: 'smooth', block: 'start' }); | |
| }; | |
| const submitAnswers = async () => { | |
| try { | |
| await applyToClub(clubId, answers); | |
| alert('답변이 성공적으로 제출되었습니다.'); | |
| } catch (error) { | |
| alert('답변 제출에 실패했습니다. 잠시 후 다시 시도해 주세요.'); | |
| } | |
| }; | |
| const handleSubmit = async () => { | |
| const invalidIds = validateRequiredQuestions(); | |
| if (invalidIds.length > 0) { | |
| setInvalidQuestionIds(invalidIds); | |
| scrollToFirstInvalidQuestion(invalidIds); | |
| return; | |
| } | |
| await submitAnswers(); | |
| }; |
🤖 Prompt for AI Agents
In frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx between lines
66 and 92, the handleSubmit function is too long and contains multiple logical
steps. Refactor by extracting the validation logic that identifies invalid
question IDs into a separate helper function, and also consider moving the
scrolling logic into its own function. This will improve readability and
maintainability by breaking down the complex function into smaller, focused
functions.
| export const ItemWrapper = styled.div<{ 'data-selected'?: string }>` | ||
| flex: 1; | ||
| height: 45px; | ||
| padding: 12px 18px; | ||
| border: none; | ||
| border-radius: 6px; | ||
| font-size: 1.125rem; | ||
| background-color: ${({ 'data-selected': selected }) => | ||
| selected === 'true' ? '#FFECE5' : '#F5F5F5'}; | ||
| color: ${({ 'data-selected': selected }) => | ||
| selected === 'true' ? '#111111' : '#818181'}; | ||
| cursor: pointer; | ||
| transition: background-color 0.2s, border-color 0.2s; | ||
| `; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
매직 넘버를 명명된 상수로 교체하세요.
스타일링 로직은 적절하지만, 코딩 가이드라인에 따라 매직 넘버들을 명명된 상수로 교체해야 합니다.
+const CHOICE_ITEM_HEIGHT = 45;
+const CHOICE_ITEM_PADDING = '12px 18px';
+const CHOICE_ITEM_BORDER_RADIUS = 6;
+const CHOICE_ITEM_FONT_SIZE = '1.125rem';
+const CHOICE_ITEM_TRANSITION_DURATION = '0.2s';
+
export const ItemWrapper = styled.div<{ 'data-selected'?: string }>`
flex: 1;
- height: 45px;
- padding: 12px 18px;
+ height: ${CHOICE_ITEM_HEIGHT}px;
+ padding: ${CHOICE_ITEM_PADDING};
border: none;
- border-radius: 6px;
- font-size: 1.125rem;
+ border-radius: ${CHOICE_ITEM_BORDER_RADIUS}px;
+ font-size: ${CHOICE_ITEM_FONT_SIZE};
background-color: ${({ 'data-selected': selected }) =>
selected === 'true' ? '#FFECE5' : '#F5F5F5'};
color: ${({ 'data-selected': selected }) =>
selected === 'true' ? '#111111' : '#818181'};
cursor: pointer;
- transition: background-color 0.2s, border-color 0.2s;
+ transition: background-color ${CHOICE_ITEM_TRANSITION_DURATION}, border-color ${CHOICE_ITEM_TRANSITION_DURATION};
`;🤖 Prompt for AI Agents
In
frontend/src/pages/ApplicationFormPage/components/ChoiceItem/ChoiceItem.styles.ts
lines 3 to 16, replace all hardcoded color values and numeric literals used for
height, padding, border-radius, font-size, and transition durations with named
constants defined at the top of the file or in a separate constants file. This
improves code readability and maintainability by avoiding magic numbers in the
styled component.
seongwon030
left a comment
There was a problem hiding this comment.
관리자와 사용자 질문 폼이 헷갈렸는데 분리한 것 정말 좋네요~
| if (isError || clubError) { | ||
| alert(applicationError?.message || '문제가 발생했어요.'); | ||
| navigate(`/club/${clubId}`); |
There was a problem hiding this comment.
해당 부분은 예전에 은호님이 작성하신 로직이긴 한데, 말씀 주신 내용처럼 navigate 호출 이후 컴포넌트가 곧 unmount되기 때문에 JSX를 return하는 건 실제로 의미가 없을 수 있을 것 같네요
| const firstInvalidIndex = formData.questions.findIndex((q: Question) => | ||
| invalidIds.includes(q.id), | ||
| ); | ||
| const targetEl = questionRefs.current[firstInvalidIndex]; | ||
| targetEl?.scrollIntoView({ behavior: 'smooth', block: 'start' }); | ||
| return; | ||
| } |
There was a problem hiding this comment.
handleSumit 안에 질문 유효성 검사, 스크롤, api 연결로직이 다 들어가 있어서 분리를 해도 좋을 것 같아요

#️⃣ 연관된 이슈
📝 변경 사항
1. 객관식 항목 1개 허용
‘확인했습니다’처럼 단일 답변만 필요한 질문에서도 선택지를 1개만 보여줍니다.
기존에는 최소 2개 이상을 강제했어서 2개 항목 이하로는 삭제가 불가능했음. 최소 1개로 수정
2. ChoiceItem 도입 및 스타일 충돌 해결
기존에는 객관식 항목을
InputField의readOnly모드로 처리해 글자색이 회색으로 표시되는 등 디자인 충돌이 있었습니다.이를 해결하기 위해 전용
ChoiceItem컴포넌트를 생성했습니다.[Choice.tsx]
mode === 'answer'일 때는ChoiceItem으로 렌더링mode === 'builder'일 때는 기존InputField사용3. InputField placeholder 자동 숨김
입력 중에도 placeholder가 남아 혼란을 주던 UX를 개선했습니다.
이제 사용자가 입력을 시작하면 placeholder가 자동으로 사라져 가독성이 높아집니다.
4. QuestionContainer로 즉각적 오류 안내
기존 : 필수 질문 미입력 시 그냥 에러 메세지 alert으로 발생 -> 어디서 오류가 났는지 알 수 없음
해당 질문 경고 UI 적용 및 자동 스크롤/포커스 이동을 적용
입력 후에는 경고 UI 해제
5. Application 컴포넌트 구조 분리
관리자용·사용자용 컴포넌트가 한 디렉토리에 뒤섞여 있어 네이밍 충돌과 구조 파악이 어려웠습니다.
CreateApplicationForm→AdminPage/ApplicationEditTab(관리자용)AnswerApplicationForm→Pages/ApplicationFormPage(사용자용)QuestionTitle,QuestionDescription,QuestionTypes) →src/components/applicationSummary by CodeRabbit
신규 기능
버그 수정
리팩터링/스타일
기타