Skip to content

[feature & refactor] 객관식 항목 1개 허용 · 필수 입력 UX 개선 · Application 구조 리팩토링#558

Merged
oesnuj merged 18 commits intodevelop-fefrom
feature/#557-choice-ui-and-single-option
Jul 20, 2025
Merged

[feature & refactor] 객관식 항목 1개 허용 · 필수 입력 UX 개선 · Application 구조 리팩토링#558
oesnuj merged 18 commits intodevelop-fefrom
feature/#557-choice-ui-and-single-option

Conversation

@oesnuj
Copy link
Member

@oesnuj oesnuj commented Jul 20, 2025

#️⃣ 연관된 이슈

📝 변경 사항

1. 객관식 항목 1개 허용

‘확인했습니다’처럼 단일 답변만 필요한 질문에서도 선택지를 1개만 보여줍니다.
기존에는 최소 2개 이상을 강제했어서 2개 항목 이하로는 삭제가 불가능했음. 최소 1개로 수정


2. ChoiceItem 도입 및 스타일 충돌 해결

기존에는 객관식 항목을 InputFieldreadOnly 모드로 처리해 글자색이 회색으로 표시되는 등 디자인 충돌이 있었습니다.
이를 해결하기 위해 전용 ChoiceItem 컴포넌트를 생성했습니다.

[Choice.tsx]

  • mode === 'answer' 일 때는 ChoiceItem으로 렌더링
  • mode === 'builder' 일 때는 기존 InputField 사용
Before After
Before ChoiceItem After ChoiceItem

3. InputField placeholder 자동 숨김

입력 중에도 placeholder가 남아 혼란을 주던 UX를 개선했습니다.
이제 사용자가 입력을 시작하면 placeholder가 자동으로 사라져 가독성이 높아집니다.

InputField placeholder 숨김

4. QuestionContainer로 즉각적 오류 안내

기존 : 필수 질문 미입력 시 그냥 에러 메세지 alert으로 발생 -> 어디서 오류가 났는지 알 수 없음

  • 해당 질문 경고 UI 적용 및 자동 스크롤/포커스 이동을 적용

  • 입력 후에는 경고 UI 해제

데스크탑 모바일
QuestionContainer 오류 안내 (데스크탑) QuestionContainer 오류 안내 (모바일)

5. Application 컴포넌트 구조 분리

관리자용·사용자용 컴포넌트가 한 디렉토리에 뒤섞여 있어 네이밍 충돌과 구조 파악이 어려웠습니다.

  • CreateApplicationFormAdminPage/ApplicationEditTab (관리자용)
  • AnswerApplicationFormPages/ApplicationFormPage (사용자용)
  • 공통 컴포넌트(QuestionTitle, QuestionDescription, QuestionTypes) → src/components/application

Summary by CodeRabbit

  • 신규 기능

    • 지원서 작성 페이지에 새로운 응답 폼과 질문 컨테이너 컴포넌트가 추가되어, 필수 입력 문항 오류 시 시각적 안내가 제공됩니다.
    • 선택형 문항의 선택 항목 UI가 개선되었습니다.
  • 버그 수정

    • 입력 필드에서 포커스 시 플레이스홀더가 자연스럽게 사라지도록 개선되었습니다.
  • 리팩터링/스타일

    • 입력 필드의 색상 커스터마이징 옵션이 제거되어 인터페이스가 단순화되었습니다.
    • 반응형 스타일이 추가되어 모바일 환경에서 문항 간 간격이 조정됩니다.
  • 기타

    • 기존 지원서 관련 컴포넌트 및 경로가 정리되고, 새로운 구조로 대체되었습니다.

oesnuj added 18 commits July 20, 2025 19:02
@oesnuj oesnuj self-assigned this Jul 20, 2025
@oesnuj oesnuj added ✨ Feature 기능 개발 🎨 Design 마크업 & 스타일링 🔨 Refactor 코드 리팩토링 💻 FE Frontend 🛠Fix 기능이 의도한 대로 동작하지 않는 버그를 수정 labels Jul 20, 2025
@netlify
Copy link

netlify bot commented Jul 20, 2025

Deploy Preview for moadong ready!

Name Link
🔨 Latest commit 3adbae0
🔍 Latest deploy log https://app.netlify.com/projects/moadong/deploys/687cd07fbafacc0008551991
😎 Deploy Preview https://deploy-preview-558--moadong.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 39
Accessibility: 89
Best Practices: 92
SEO: 92
PWA: -
View the detailed breakdown and full score reports

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jul 20, 2025

Warning

.coderabbit.yaml has a parsing error

The CodeRabbit configuration file in this repository has a parsing error and default settings were used instead. Please fix the error(s) in the configuration file. You can initialize chat with CodeRabbit to get help with the configuration file.

💥 Parsing errors (1)
Validation error: Invalid regex pattern for base branch. Received: "**" at "reviews.auto_review.base_branches[0]"
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Walkthrough

이 변경사항은 기존 지원서 작성 및 편집 관련 컴포넌트(AnswerApplicationForm, CreateApplicationForm)를 새로운 컴포넌트(ApplicationEditTab, ApplicationFormPage)로 교체하고, 라우팅 및 관련 UI/로직을 전면적으로 리팩토링합니다. 또한, 선택형 문항 UI 개선, 입력 필드 스타일 단순화, 신규 공통 컴포넌트 도입 등이 포함됩니다.

Changes

파일/경로 그룹 변경 요약
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
Loading

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.

❤️ Share
🪧 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 @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in 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 pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere 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.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

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 = () => {

`;

const ErrorText = styled.div`
color: #FF5414;
Copy link

Copilot AI Jul 20, 2025

Choose a reason for hiding this comment

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

The color value '#FF5414' is duplicated. Consider defining this as a theme constant for consistency.

Copilot uses AI. Check for mistakes.

try {
await applyToClub(clubId, answers);
alert('답변이 성공적으로 제출되었습니다.');
Copy link

Copilot AI Jul 20, 2025

Choose a reason for hiding this comment

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

Using alert() for success messages provides poor UX. Consider using a toast notification or modal component for better user experience.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

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

📥 Commits

Reviewing files that changed from the base of the PR and between f130ebe and 3adbae0.

📒 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 전달과 조건부 에러 표시 로직이 올바르게 구현되었습니다.

Comment on lines +42 to +49
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));
}
};
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ 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.

Comment on lines +66 to +92
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('답변 제출에 실패했습니다. 잠시 후 다시 시도해 주세요.');
}
};
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ 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.

Suggested change
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.

Comment on lines +3 to +16
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;
`;
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ 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.

Copy link
Member

@seongwon030 seongwon030 left a comment

Choose a reason for hiding this comment

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

관리자와 사용자 질문 폼이 헷갈렸는데 분리한 것 정말 좋네요~

Comment on lines +52 to +54
if (isError || clubError) {
alert(applicationError?.message || '문제가 발생했어요.');
navigate(`/club/${clubId}`);
Copy link
Member

Choose a reason for hiding this comment

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

navigate가 렌더링 이후 실행되나요?

Copy link
Member Author

Choose a reason for hiding this comment

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

해당 부분은 예전에 은호님이 작성하신 로직이긴 한데, 말씀 주신 내용처럼 navigate 호출 이후 컴포넌트가 곧 unmount되기 때문에 JSX를 return하는 건 실제로 의미가 없을 수 있을 것 같네요

Copy link
Member Author

Choose a reason for hiding this comment

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

요기 페이지 추가로 리펙토링 해보겠습니다

Comment on lines +78 to +84
const firstInvalidIndex = formData.questions.findIndex((q: Question) =>
invalidIds.includes(q.id),
);
const targetEl = questionRefs.current[firstInvalidIndex];
targetEl?.scrollIntoView({ behavior: 'smooth', block: 'start' });
return;
}
Copy link
Member

Choose a reason for hiding this comment

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

handleSumit 안에 질문 유효성 검사, 스크롤, api 연결로직이 다 들어가 있어서 분리를 해도 좋을 것 같아요

Copy link
Member Author

Choose a reason for hiding this comment

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

확인했습니다~~ 같이 수정해볼게요

Copy link
Member

@Zepelown Zepelown left a comment

Choose a reason for hiding this comment

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

고생하셨습니다

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🎨 Design 마크업 & 스타일링 💻 FE Frontend ✨ Feature 기능 개발 🛠Fix 기능이 의도한 대로 동작하지 않는 버그를 수정 🔨 Refactor 코드 리팩토링

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants