[feature] 지원서 작성/편집 기능 구현 및 질문 컴포넌트 구조화#445
Conversation
질문 제목을 표시하고 편집할 수 있는 컴포넌트입니다. 모드에 따라 builder에서는 편집 가능, answer에서는 읽기 전용으로 동작합니다. 필수 항목일 경우 원형 마크로 표시됩니다. Co-Authored-By: Seongwon Seo <seongwon0903@gmail.com>
질문 설명을 표시하거나 수정할 수 있는 `QuestionDescription` 컴포넌트를 추가했습니다. `builder` 모드에서는 수정 가능하며, `answer` 모드에서는 읽기 전용으로 동작합니다. Co-Authored-By: Seongwon Seo <seongwon0903@gmail.com>
질문 제목, 설명, 사용자 입력값을 포함하는 단답형 질문 필드를 구현했습니다. builder 모드에서는 질문 편집이 가능하며, answer 모드에서는 사용자가 값을 입력할 수 있습니다. Co-Authored-By: Seongwon Seo <seongwon0903@gmail.com>
질문 제목과 설명을 수정할 수 있으며, 선택 항목은 최소 2개에서 최대 6개까지 추가/삭제 가능합니다. 사용자가 하나의 옵션을 선택할 수 있는 형태로, builder/answer 모드에 따라 동작 방식이 달라집니다. Co-Authored-By: Seongwon Seo <seongwon0903@gmail.com>
지원서 작성 폼에서 사용되는 질문 입력란의 placeholder 값을 관리하는 상수 파일을 생성했습니다. Co-Authored-By: Seongwon Seo <seongwon0903@gmail.com>
질문 타입 선택, 필수 여부 토글, 단일/다중 선택 전환 등을 포함한 빌더 컴포넌트를 구현했습니다. Co-Authored-By: Seongwon Seo <seongwon0903@gmail.com>
Co-Authored-By: Seongwon Seo <seongwon0903@gmail.com>
질문 데이터를 상태로 관리하며, QuestionBuilder 컴포넌트를 통해 질문 편집 UI를 구성했습니다. 질문 제목, 설명, 항목, 질문 타입 변경을 위한 핸들러를 각각 구현했습니다. Co-Authored-By: Seongwon Seo <seongwon0903@gmail.com>
사용자가 지원서를 작성할 수 있는 부분입니다. 질문과 선택지를 동적으로 렌더링할 수 있으며, 추후 answer 모드 기능이 추가될 예정입니다. Co-Authored-By: Seongwon Seo <seongwon0903@gmail.com>
이를 통해 지원서 조회 및 작성 페이지로 이동이 가능해졌습니다. Co-Authored-By: Seongwon Seo <seongwon0903@gmail.com>
✅ Deploy Preview for moadong ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
Warning
|
| 파일/경로 요약 | 변경 요약 |
|---|---|
| frontend/src/App.tsx | 'view-application'과 'create-application' 경로 신규 라우트 추가 |
| frontend/src/constants/APPLICATION_FORM.ts | 질문 입력 필드 placeholder를 담은 상수 객체 추가 |
| frontend/src/pages/AdminPage/application/ApplicationForm.tsx | 질문 데이터와 답변 상태 관리, 렌더링 구조 설계 및 컴포넌트 추가 |
| frontend/src/pages/AdminPage/application/CreateForm.tsx | 질문 생성/수정용 상태, 핸들러, UI 구조 설계 및 구현 |
| frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.tsx, .../QuestionBuilder.styles.ts | QuestionBuilder 컴포넌트 및 스타일 파일 추가, 질문 유형, 필수 여부, 선택 방식 등 빌더 UI 구현 |
| frontend/src/pages/AdminPage/application/components/QuestionDescription/QuestionDescription.tsx | QuestionDescription 컴포넌트 추가: 설명 입력, 모드별 읽기/쓰기 처리 |
| frontend/src/pages/AdminPage/application/components/QuestionTitle/QuestionTitle.tsx | QuestionTitle 컴포넌트 추가: 제목 입력, 필수 표시, ID 렌더링, 모드별 처리 |
| frontend/src/pages/AdminPage/application/fields/Choice.tsx | Choice 컴포넌트 추가: 객관식/다중선택형 질문, 옵션 관리, 아이템 추가/삭제 기능 |
| frontend/src/pages/AdminPage/application/fields/ShortText.tsx | ShortText 컴포넌트 추가: 단답형 질문, 제목/설명/답변 상태 관리 및 렌더링 |
Sequence Diagram(s)
sequenceDiagram
participant User
participant App
participant ApplicationForm
participant CreateForm
participant QuestionBuilder
participant ShortText/Choice
User->>App: "view-application" 또는 "create-application" 라우트 접근
App->>ApplicationForm: "view-application"일 때 렌더링
App->>CreateForm: "create-application"일 때 렌더링
CreateForm->>QuestionBuilder: 각 질문 데이터로 QuestionBuilder 렌더링
QuestionBuilder->>ShortText/Choice: 질문 유형에 따라 입력 컴포넌트 렌더링
ShortText/Choice->>QuestionTitle/QuestionDescription: 제목/설명 입력 처리
User->>ShortText/Choice: 답변 또는 옵션 입력/수정
ShortText/Choice-->>CreateForm: onChange 콜백으로 상태 업데이트
Assessment against linked issues
| Objective | Addressed | Explanation |
|---|---|---|
| 폼 데이터 구조(schema) 정의, 질문 유형별 입력 컴포넌트 제작, ShortTextField, MultipleChoiceField, FormFieldRenderer 분기 렌더링, ApplicationForm에서 응답 상태 통합 관리, 기본 스타일 적용, 조건부 질문 구조 고려 (FE-122) | ✅ |
Possibly related issues
- [feature] FE-122 질문 유형별 입력 컴포넌트 구현 #440: 질문 입력 컴포넌트, 폼 구조, 질문 빌더 및 질문 유형별 렌더링 구현이 본 PR의 변경사항과 직접적으로 관련됨
"""
📜 Recent review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.tsx
✨ 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 a reusable application form with editable question components for both the builder and answer modes. Key changes include new components for short text and choice questions, a QuestionBuilder component for dynamic question configuration, and routes for creating and viewing applications.
Reviewed Changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| frontend/src/pages/AdminPage/application/fields/ShortText.tsx | Adds ShortText component integrating title, description, and an input field. |
| frontend/src/pages/AdminPage/application/fields/Choice.tsx | Implements Choice component with options management and add/delete item features. |
| frontend/src/pages/AdminPage/application/components/QuestionTitle/QuestionTitle.tsx | Provides a styled title component with editable title support. |
| frontend/src/pages/AdminPage/application/components/QuestionDescription/QuestionDescription.tsx | Provides a styled description component with edit/read-only features. |
| frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.tsx | Creates a dynamic question builder with toggles for required status and selection type. |
| frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.styles.ts | Contains styled components for the QuestionBuilder UI. |
| frontend/src/pages/AdminPage/application/CreateForm.tsx | Implements a form creation page that manages and renders questions using the QuestionBuilder. |
| frontend/src/pages/AdminPage/application/ApplicationForm.tsx | Sets up a basic application form layout to eventually display answer mode components. |
| frontend/src/constants/APPLICATION_FORM.ts | Defines placeholders for different question types. |
| frontend/src/App.tsx | Adds new routes for application viewing and form creation. |
Codecov ReportAttention: Patch coverage is 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Actionable comments posted: 13
♻️ Duplicate comments (1)
frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.tsx (1)
7-14: [이전 리뷰 참조] QuestionType을 공통 타입 파일로 추출하세요.이전에 언급했듯이
QuestionType이 여러 파일에 중복 정의되어 있습니다.
🧹 Nitpick comments (11)
frontend/src/pages/AdminPage/application/components/QuestionDescription/QuestionDescription.tsx (1)
9-17: 스타일 컴포넌트 네이밍 개선을 고려해주세요.
QuestionDescriptionText라는 이름보다는QuestionDescriptionInput이 더 명확한 의미를 전달할 것 같습니다. input 요소임을 명시적으로 나타내는 것이 좋겠습니다.-const QuestionDescriptionText = styled.input` +const QuestionDescriptionInput = styled.input`그리고 컴포넌트에서도 동일하게 수정:
- <QuestionDescriptionText + <QuestionDescriptionInputfrontend/src/pages/AdminPage/application/fields/ShortText.tsx (1)
30-30: 외부 div에 key 속성 추가를 고려해주세요.만약 이 컴포넌트가 리스트에서 렌더링된다면, 외부 div에 key 속성을 추가하는 것이 좋겠습니다.
return ( - <div> + <div key={id}>또는 부모 컴포넌트에서 key를 관리하는 것도 좋은 방법입니다.
frontend/src/pages/AdminPage/application/components/QuestionTitle/QuestionTitle.tsx (2)
50-50: ID 표시 조건을 더 명확하게 해주세요.현재
id && <QuestionTitleId>{id}.</QuestionTitleId>로 되어 있는데, id가 0인 경우 의도치 않게 렌더링되지 않을 수 있습니다.다음과 같이 수정하는 것을 제안합니다:
- {id && <QuestionTitleId>{id}.</QuestionTitleId>} + {id !== undefined && <QuestionTitleId>{id}.</QuestionTitleId>}또는 더 명시적으로:
- {id && <QuestionTitleId>{id}.</QuestionTitleId>} + {typeof id === 'number' && <QuestionTitleId>{id}.</QuestionTitleId>}
9-14: 색상 값을 상수로 분리하는 것을 고려해주세요.
#ff5414색상이 여러 곳에서 반복 사용되고 있습니다. 디자인 시스템이나 색상 상수 파일로 분리하면 유지보수성이 향상됩니다.색상 상수 파일을 만들어 다음과 같이 사용하는 것을 제안합니다:
// colors.ts export const COLORS = { PRIMARY: '#ff5414', TEXT_DARK: '#111', // ... } as const;+import { COLORS } from '@/constants/colors'; const QuestionTitleId = styled.p` - color: #ff5414; + color: ${COLORS.PRIMARY}; // ... `;Also applies to: 25-31
frontend/src/pages/AdminPage/application/fields/Choice.tsx (2)
53-53: TODO 주석을 해결해주세요.다중 선택 기능이 아직 구현되지 않았습니다.
isMultiprop이 이미 정의되어 있으니 이를 활용하여 기능을 완성해주세요.다중 선택 기능 구현을 도와드릴까요? 다음과 같은 구조로 구현할 수 있습니다:
- 답변 상태를 문자열 배열로 관리
- 체크박스/라디오 버튼 선택
- 선택/해제 로직 구현
이 기능을 구현하는 새로운 이슈를 생성하시겠습니까?
66-74:filledItems계산 로직을 단순화해주세요.현재 로직이 복잡하고 이해하기 어렵습니다. 더 명확한 방식으로 개선할 수 있습니다.
다음과 같이 단순화하는 것을 제안합니다:
- const filledItems = - items.length >= MIN_ITEMS - ? items - : [ - ...items, - ...Array.from({ length: MIN_ITEMS - items.length }, () => ({ - value: '', - })), - ]; + const filledItems = useMemo(() => { + if (items.length >= MIN_ITEMS) { + return items; + } + + const emptyItems = Array(MIN_ITEMS - items.length).fill({ value: '' }); + return [...items, ...emptyItems]; + }, [items]);
useMemo를 사용하여 불필요한 재계산을 방지할 수 있습니다.frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.styles.ts (1)
25-49:RequiredToggleCircle스타일 로직을 단순화해주세요.현재 active 상태에 따른 스타일링이 복잡하고 중복이 있습니다. 배경색이 두 번 설정되는 문제도 있습니다.
다음과 같이 단순화하는 것을 제안합니다:
export const RequiredToggleCircle = styled.span<{ active?: boolean }>` position: relative; width: 16px; height: 16px; border-radius: 50%; border: 1px solid ${(props) => (props.active ? '#ff5000' : '#ccc')}; - background-color: ${(props) => (props.active ? '#ff5000' : 'white')}; + background-color: white; - ${({ active }) => - active && - ` - background-color: #fff; - &::after { - content: ''; - width: 10px; - height: 10px; - background-color: #ff5000; - border-radius: 50%; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - } - `} + &::after { + content: ''; + width: 10px; + height: 10px; + background-color: ${({ active }) => active ? '#ff5000' : 'transparent'}; + border-radius: 50%; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + } `;frontend/src/pages/AdminPage/application/CreateForm.tsx (3)
43-81: 핸들러 함수들의 중복 코드를 제거하세요.모든 핸들러가 동일한 패턴을 따르므로, 제네릭 핸들러를 만들어 코드 중복을 줄일 수 있습니다.
- const handleTitleChange = (id: number) => (value: string) => { - setQuestions((prev) => ({ - ...prev, - [id]: { - ...prev[id], - title: value, - }, - })); - }; - - const handleDescriptionChange = (id: number) => (value: string) => { - setQuestions((prev) => ({ - ...prev, - [id]: { - ...prev[id], - description: value, - }, - })); - }; - - const handleItemsChange = (id: number) => (newItems: { value: string }[]) => { - setQuestions((prev) => ({ - ...prev, - [id]: { - ...prev[id], - items: newItems, - }, - })); - }; - - const handleTypeChange = (id: number) => (newType: QuestionType) => { - setQuestions((prev) => ({ - ...prev, - [id]: { - ...prev[id], - type: newType, - }, - })); - }; + const handleQuestionUpdate = <K extends keyof Question>( + id: number, + field: K + ) => (value: Question[K]) => { + setQuestions((prev) => ({ + ...prev, + [id]: { + ...prev[id], + [field]: value, + }, + })); + }; + + const handleTitleChange = (id: number) => handleQuestionUpdate(id, 'title'); + const handleDescriptionChange = (id: number) => handleQuestionUpdate(id, 'description'); + const handleItemsChange = (id: number) => handleQuestionUpdate(id, 'items'); + const handleTypeChange = (id: number) => handleQuestionUpdate(id, 'type');
88-88: 불필요한 Number() 변환을 제거하세요.
id는 이미 number 타입이므로Number()변환이 필요 없습니다.- key={id} - id={Number(id)} + key={id} + id={+id}- onTitleChange={handleTitleChange(Number(id))} - onDescriptionChange={handleDescriptionChange(Number(id))} - onItemsChange={handleItemsChange(Number(id))} - onTypeChange={handleTypeChange(Number(id))} + onTitleChange={handleTitleChange(+id)} + onDescriptionChange={handleDescriptionChange(+id)} + onItemsChange={handleItemsChange(+id)} + onTypeChange={handleTypeChange(+id)}Also applies to: 93-96
3-3: TODO: 질문 삭제 및 추가 기능 구현이 필요합니다.질문 관리 기능을 완성하려면 추가/삭제 기능이 필요합니다.
질문 추가/삭제 기능을 구현하는 코드를 생성해드릴까요? 새로운 이슈를 생성하여 추적할 수도 있습니다.
frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.tsx (1)
72-72: TODO: LONG_TEXT 타입 구현이 필요합니다.
LONG_TEXT타입을 위한 컴포넌트가 필요합니다.
LongText컴포넌트를 생성하는 코드를 제공해드릴까요?
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
frontend/src/assets/images/icons/drop_button_icon.svgis excluded by!**/*.svg
📒 Files selected for processing (10)
frontend/src/App.tsx(2 hunks)frontend/src/constants/APPLICATION_FORM.ts(1 hunks)frontend/src/pages/AdminPage/application/ApplicationForm.tsx(1 hunks)frontend/src/pages/AdminPage/application/CreateForm.tsx(1 hunks)frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.styles.ts(1 hunks)frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.tsx(1 hunks)frontend/src/pages/AdminPage/application/components/QuestionDescription/QuestionDescription.tsx(1 hunks)frontend/src/pages/AdminPage/application/components/QuestionTitle/QuestionTitle.tsx(1 hunks)frontend/src/pages/AdminPage/application/fields/Choice.tsx(1 hunks)frontend/src/pages/AdminPage/application/fields/ShortText.tsx(1 hunks)
🔇 Additional comments (3)
frontend/src/pages/AdminPage/application/fields/ShortText.tsx (2)
29-50: 컴포넌트 구조가 잘 설계되었습니다.ShortText 컴포넌트가 재사용 가능한 하위 컴포넌트들을 잘 조합하여 구성되어 있고, 모드에 따른 동작 분기도 적절합니다.
43-48:Details
✅ Verification successful
InputField의 disabled 로직을 검증해주세요.
현재
mode === 'builder'일 때 InputField가 비활성화되는데, 이 로직이 의도된 동작인지 확인이 필요합니다. 일반적으로 빌더 모드에서는 미리보기 목적으로 비활성화하는 것이 맞을 수 있습니다.다음 스크립트로 다른 필드 컴포넌트들의 disabled 로직을 확인해보겠습니다:
🏁 Script executed:
#!/bin/bash # Description: 다른 필드 컴포넌트들의 disabled 로직 패턴 확인 # Choice 컴포넌트나 다른 필드들의 disabled 로직 확인 rg -A 3 -B 3 "disabled.*builder|mode.*builder" frontend/src/pages/AdminPage/application/fields/ # 빌더 모드에서의 의도된 동작 패턴 확인 rg -A 5 "mode.*builder" frontend/src/pages/AdminPage/application/Length of output: 8824
🏁 Script executed:
#!/bin/bash # 출력 Choice.tsx 컴포넌트 내용 확인 sed -n '1,200p' frontend/src/pages/AdminPage/application/fields/Choice.tsxLength of output: 3330
ShortText InputField 비활성화 로직 확인 완료
mode === 'builder'일 때 입력 필드를 비활성화하는 것은 빌더 모드에서 편집이 아닌 미리보기만 허용하기 위한 의도된 동작입니다.- Choice 컴포넌트 등 다른 필드들도 모드에 따라 활성화/비활성화 로직이 다르게 적용되고 있으므로, ShortText의 로직은 일관성에 부합합니다.
frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.tsx (1)
1-156: 컴포넌트 구조가 잘 설계되었습니다!질문 타입별 렌더링 로직 분리, 스타일드 컴포넌트 활용, 조건부 렌더링 등이 깔끔하게 구현되었습니다.
frontend/src/pages/AdminPage/application/components/QuestionDescription/QuestionDescription.tsx
Outdated
Show resolved
Hide resolved
frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.tsx
Outdated
Show resolved
Hide resolved
frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.tsx
Show resolved
Hide resolved
설명을 돕기 위해 placeholder와 접근성 강화를 위한 aria-label을 추가했습니다.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
frontend/src/pages/AdminPage/application/components/QuestionDescription/QuestionDescription.tsx (1)
25-33: 불필요한 React.Fragment를 제거해주세요.이제 단일 요소만 반환하므로 React.Fragment가 불필요합니다. 이전 피드백에서 제안된 placeholder와 aria-label이 잘 적용되었습니다.
return ( - <> <QuestionDescriptionText value={description} placeholder='질문에 대한 설명을 입력하세요' aria-label='질문 설명' readOnly={mode === 'answer'} onChange={(e) => onChange?.(e.target.value)} /> - </> );
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
frontend/src/pages/AdminPage/application/components/QuestionDescription/QuestionDescription.tsx(1 hunks)
🔇 Additional comments (1)
frontend/src/pages/AdminPage/application/components/QuestionDescription/QuestionDescription.tsx (1)
19-35: 이전 피드백이 잘 반영되었습니다.이전 리뷰에서 제안된 다음 사항들이 적절히 구현되었습니다:
- placeholder 텍스트 추가
- aria-label을 통한 접근성 개선
- 불필요한 div 래퍼 제거 (React.Fragment 사용)
- 옵셔널 체이닝을 통한 안전한 onChange 호출
컴포넌트의 로직과 TypeScript 타이핑도 올바르게 구현되었습니다.
frontend/src/pages/AdminPage/application/components/QuestionDescription/QuestionDescription.tsx
Show resolved
Hide resolved
질문 항목에 'required' 옵션을 추가하여 필수 여부를 설정할 수 있도록 했습니다. 기존에 고정값으로 처리되던 필수 여부를 데이터 기반으로 동적으로 처리하게 변경했습니다.
객관식 항목을 나타내는 변수명은 items입니다
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (5)
frontend/src/App.tsx (1)
74-76: 관리자 라우트 경로 일관성 문제이전 리뷰에서 언급된 대로, 관리자 기능인
ApplicationForm과CreateForm의 라우트가 최상위 레벨에 있습니다. TODO 주석에서도 인지하고 있듯이/admin경로 하위로 이동이 필요합니다.frontend/src/pages/AdminPage/application/CreateForm.tsx (1)
7-14: QuestionType 타입 정의 중복이전 리뷰에서 언급된 대로,
QuestionType이 여러 파일에 중복 정의되어 있습니다. 공통 타입 파일로 추출하세요.frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.tsx (3)
16-27: 부모 컴포넌트와의 상태 동기화를 위한 콜백 추가가 필요합니다.
isRequired상태 변경을 부모 컴포넌트에 알리기 위한onRequiredChange콜백이 props 인터페이스에 누락되어 있습니다.Props 인터페이스에 추가하세요:
interface QuestionBuilderProps { id: number; title: string; description: string; items?: { value: string }[]; type: QuestionType; required: boolean; onTitleChange: (value: string) => void; onDescriptionChange: (value: string) => void; onItemsChange?: (newItems: { value: string }[]) => void; onTypeChange?: (type: QuestionType) => void; + onRequiredChange?: (required: boolean) => void; }
127-130: 필수 여부 상태 변경을 부모 컴포넌트에 전달하세요.
isRequired상태가 변경되어도 부모 컴포넌트에 알리지 않아 상태 불일치가 발생할 수 있습니다.토글 버튼 onClick 수정:
- <Styled.RequiredToggleButton onClick={() => setIsRequired(!isRequired)}> + <Styled.RequiredToggleButton onClick={() => { + const newRequired = !isRequired; + setIsRequired(newRequired); + onRequiredChange?.(newRequired); + }}>
144-146: 드롭다운에 더 많은 질문 타입을 추가하세요.현재 7개의
QuestionType중 2개만 드롭다운에서 선택 가능합니다. 사용자 편의성을 위해 다른 타입들도 추가하세요.<Styled.Dropdown value={questionType} onChange={(e) => { const selectedType = e.target.value as QuestionType; setQuestionType(selectedType); onTypeChange?.(selectedType); }} > <option value='CHOICE'>객관식</option> <option value='SHORT_TEXT'>단답형</option> + <option value='LONG_TEXT'>장문형</option> + <option value='EMAIL'>이메일</option> + <option value='PHONE_NUMBER'>전화번호</option> + <option value='NAME'>이름</option> </Styled.Dropdown>
🧹 Nitpick comments (1)
frontend/src/pages/AdminPage/application/components/QuestionTitle/QuestionTitle.tsx (1)
34-34: 불필요한 id 존재 확인 제거
idprop이 필수로 정의되어 있는데 존재 여부를 확인하고 있습니다.다음 중 하나를 선택하세요:
옵션 1: id 확인 제거 (권장)
interface QuestionTitleProps { id: number; // ... } // 컴포넌트 내부 - {id && <QuestionTitleId>{id}.</QuestionTitleId>} + <QuestionTitleId>{id}.</QuestionTitleId>옵션 2: id를 선택적으로 변경
interface QuestionTitleProps { - id: number; + id?: number; // ... }Also applies to: 50-50
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
frontend/src/App.tsx(2 hunks)frontend/src/constants/APPLICATION_FORM.ts(1 hunks)frontend/src/pages/AdminPage/application/CreateForm.tsx(1 hunks)frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.tsx(1 hunks)frontend/src/pages/AdminPage/application/components/QuestionTitle/QuestionTitle.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/src/constants/APPLICATION_FORM.ts
🔇 Additional comments (2)
frontend/src/pages/AdminPage/application/CreateForm.tsx (1)
16-24: Question 타입 구조가 잘 설계되었습니다
required필드를options객체 내에 배치한 것은 향후 확장성을 위한 좋은 설계입니다.frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.tsx (1)
1-5: 임포트 구조가 깔끔하게 구성되어 있습니다.React hooks, 관련 컴포넌트들, 그리고 스타일이 논리적으로 그룹핑되어 임포트되어 있어 가독성이 좋습니다.
frontend/src/pages/AdminPage/application/components/QuestionTitle/QuestionTitle.tsx
Show resolved
Hide resolved
frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.tsx
Show resolved
Hide resolved
seongwon030
left a comment
There was a problem hiding this comment.
페어프로그래밍 오랜만에 재밌었습니다 ㅎㅎ
추가적인 작업도 열심히 해봅시당
기존 내부 상태로 처리되던 required 상태를 props를 기반으로 관리하도록 변경했습니다.
- filledItems 제거, map 기반 항목 수정 로직 간소화 - 타입 변경 시 items 초기화 조건 분리로 가독성 향상 - 단답형 → 객관식 전환 시 렌더링 오류 해결
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (3)
frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.tsx (2)
147-149: 드롭다운에 더 많은 질문 타입 추가가 필요합니다.현재 7개의
QuestionType중 2개만 드롭다운에서 선택 가능합니다. 나머지 타입들도 추가해야 합니다.<option value='CHOICE'>객관식</option> <option value='SHORT_TEXT'>단답형</option> + <option value='LONG_TEXT'>장문형</option> + <option value='EMAIL'>이메일</option> + <option value='PHONE_NUMBER'>전화번호</option> + <option value='NAME'>이름</option>
73-92: LONG_TEXT 케이스 처리가 여전히 누락되었습니다.TODO 주석이 있지만
LONG_TEXT타입에 대한 실제 처리가 구현되지 않았습니다.case 'SHORT_TEXT': case 'NAME': case 'EMAIL': case 'PHONE_NUMBER': + case 'LONG_TEXT': return ( <ShortText id={id} title={title} required={required} description={description} mode='builder' onTitleChange={onTitleChange} onDescriptionChange={onDescriptionChange} /> );frontend/src/pages/AdminPage/application/CreateForm.tsx (1)
36-48: 질문 설명과 타입이 일치하지 않습니다.
MULTI_CHOICE타입이지만 설명이 "중복 선택 가능합니다"로 되어 있어 혼란을 줄 수 있습니다. 이전 리뷰에서 지적된 문제가 여전히 존재합니다.title: '지원 분야를 선택해주세요', - description: '중복 선택 가능합니다', + description: '여러 개를 선택할 수 있습니다',
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
frontend/src/pages/AdminPage/application/CreateForm.tsx(1 hunks)frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.tsx(1 hunks)frontend/src/pages/AdminPage/application/fields/Choice.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/src/pages/AdminPage/application/fields/Choice.tsx
🧰 Additional context used
🪛 Biome (1.9.4)
frontend/src/pages/AdminPage/application/CreateForm.tsx
[error] 107-108: Avoid the delete operator which can impact performance.
Unsafe fix: Use an undefined assignment instead.
(lint/performance/noDelete)
🪛 GitHub Check: codecov/patch
frontend/src/pages/AdminPage/application/CreateForm.tsx
[warning] 4-5: frontend/src/pages/AdminPage/application/CreateForm.tsx#L4-L5
Added lines #L4 - L5 were not covered by tests
[warning] 26-27: frontend/src/pages/AdminPage/application/CreateForm.tsx#L26-L27
Added lines #L26 - L27 were not covered by tests
[warning] 51-52: frontend/src/pages/AdminPage/application/CreateForm.tsx#L51-L52
Added lines #L51 - L52 were not covered by tests
[warning] 61-62: frontend/src/pages/AdminPage/application/CreateForm.tsx#L61-L62
Added lines #L61 - L62 were not covered by tests
[warning] 71-72: frontend/src/pages/AdminPage/application/CreateForm.tsx#L71-L72
Added lines #L71 - L72 were not covered by tests
[warning] 81-83: frontend/src/pages/AdminPage/application/CreateForm.tsx#L81-L83
Added lines #L81 - L83 were not covered by tests
[warning] 86-86: frontend/src/pages/AdminPage/application/CreateForm.tsx#L86
Added line #L86 was not covered by tests
[warning] 92-92: frontend/src/pages/AdminPage/application/CreateForm.tsx#L92
Added line #L92 was not covered by tests
[warning] 97-97: frontend/src/pages/AdminPage/application/CreateForm.tsx#L97
Added line #L97 was not covered by tests
[warning] 100-100: frontend/src/pages/AdminPage/application/CreateForm.tsx#L100
Added line #L100 was not covered by tests
[warning] 107-108: frontend/src/pages/AdminPage/application/CreateForm.tsx#L107-L108
Added lines #L107 - L108 were not covered by tests
[warning] 120-120: frontend/src/pages/AdminPage/application/CreateForm.tsx#L120
Added line #L120 was not covered by tests
[warning] 122-122: frontend/src/pages/AdminPage/application/CreateForm.tsx#L122
Added line #L122 was not covered by tests
[warning] 142-142: frontend/src/pages/AdminPage/application/CreateForm.tsx#L142
Added line #L142 was not covered by tests
🔇 Additional comments (2)
frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.tsx (1)
128-133: required 상태 변경이 올바르게 구현되었습니다.이전 리뷰에서 지적된 문제가 해결되었습니다.
onRequiredChange콜백을 통해 부모 컴포넌트에 상태 변경을 올바르게 전달하고 있습니다.frontend/src/pages/AdminPage/application/CreateForm.tsx (1)
81-105: 타입 변경 로직이 잘 구현되었습니다.선택형과 비선택형 질문 타입 간 전환 시
items배열을 적절히 관리하는 로직이 잘 구현되어 있습니다. 특히 최소 2개의 옵션을 보장하는 부분이 좋습니다.🧰 Tools
🪛 GitHub Check: codecov/patch
[warning] 81-83: frontend/src/pages/AdminPage/application/CreateForm.tsx#L81-L83
Added lines #L81 - L83 were not covered by tests
[warning] 86-86: frontend/src/pages/AdminPage/application/CreateForm.tsx#L86
Added line #L86 was not covered by tests
[warning] 92-92: frontend/src/pages/AdminPage/application/CreateForm.tsx#L92
Added line #L92 was not covered by tests
[warning] 97-97: frontend/src/pages/AdminPage/application/CreateForm.tsx#L97
Added line #L97 was not covered by tests
[warning] 100-100: frontend/src/pages/AdminPage/application/CreateForm.tsx#L100
Added line #L100 was not covered by tests
frontend/src/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder.tsx
Show resolved
Hide resolved
| // 지원서 제작하기 : 지원서 제작 컴포넌트 | ||
| // 지원서 수정과 제작을 맡을 컴포넌트 | ||
| // Todo: 질문 삭제 및 질문 추가 기능 구현 | ||
| import { useState } from 'react'; | ||
| import QuestionBuilder from '@/pages/AdminPage/application/components/QuestionBuilder/QuestionBuilder'; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
테스트 커버리지가 부족합니다.
정적 분석에서 지적한 대로 대부분의 라인이 테스트로 커버되지 않았습니다. 컴포넌트의 핵심 기능에 대한 단위 테스트 추가를 고려하세요.
단위 테스트 작성을 도와드릴까요? 다음과 같은 테스트 케이스들을 생성할 수 있습니다:
- 초기 상태 렌더링 테스트
- 질문 타입 변경 핸들러 테스트
- 필수 여부 토글 테스트
🧰 Tools
🪛 GitHub Check: codecov/patch
[warning] 4-5: frontend/src/pages/AdminPage/application/CreateForm.tsx#L4-L5
Added lines #L4 - L5 were not covered by tests
🤖 Prompt for AI Agents
In frontend/src/pages/AdminPage/application/CreateForm.tsx lines 1 to 5, the
component lacks sufficient test coverage. Add unit tests covering core
functionalities such as initial state rendering, handling question type changes,
and toggling required fields. Write tests that render the component, simulate
user interactions for changing question types and toggling required flags, and
assert expected state and UI updates.
questionType 상태를 제거하고 type prop를 일관되게 활용하도록 수정했습니다.

#️⃣연관된 이슈
📝작업 내용
성원님과 함께 3일간 짝 프로그래밍하며 진행한 PR입니다. 수고 많으셨습니다! 😄
이번 작업은 지원서 컴포넌트를 재사용 가능하도록 설계하는 데 중점을 두었습니다.
개발 순서에 따라 작업 내역을 정리하면 다음과 같습니다:
1.QuestionTitle 및 QuestionDescription 컴포넌트 추가
mode에 따라 편집 가능 여부가 달라집니다.
builder 모드: 제목/설명 수정 가능
answer 모드: 읽기 전용
2. 폼 질문 유형별 컴포넌트를 구현하였습니다.
1️⃣ 선택형 질문: Choice
2️⃣ 단답형 질문: ShortText
3. 질문 빌더: QuestionBuilder 컴포넌트 구현
4. 지원서 생성: CreateForm 컴포넌트 구현
관리자가 새로운 질문을 추가하거나, 기존 질문을 편집할 수 있는 페이지
각 질문을 유연하게 구성할 수 있도록 상태 관리 및 동적 렌더링 구현
해당 페이지에 추후 api 데이터에 맞게 제작, 수정 폼을 띄워야합니다.
질문 삭제, 추가 버튼도 필요합니다.
5. 지원서 뷰 및 작성 페이지로 이동할 수 있는 라우팅 추가
중점적으로 리뷰받고 싶은 부분(선택)
주말 일정으로 인해 PR이 다소 늦어진 점 양해 부탁드립니다.. 기다려주셔서 감사합니다 🙇♂️
혼자 진행한 작업은 아래와 같습니다:
추가하고 싶은 내용이 있으시다면 자유롭게 커밋 부탁드립니다!
🫡 참고사항
Summary by CodeRabbit
Summary by CodeRabbit
신규 기능
CreateForm과ApplicationForm컴포넌트가 도입되어, 질문 항목을 동적으로 관리할 수 있습니다.스타일
🔧 리뷰로 인한 수정사항들 🔧
질문 제목, 설명 컴포넌트에 placeholder와 접근성 속성 추가
→ 6b14dc9
LONG_TEXT placeholder 상수 값이 최대 20자로 되어 있어 500자로 수정
→ a6fae14
App.tsx CreateForm 경로 이동 필요 TODO 주석 추가
→ b64e5e1
CreateForm에서 QuestionBuilder에 required가 항상 true로 하드코딩 되어 있던 문제 해결
→ 30fb4a2
mock 데이터 안내 문구 수정
→ ecd73a3
QuestionBuilder의 잘못된 props명 options를 items로 변경
→ 4906413