[retfactor] 지원서 페이지에 지원서 설명 추가 및 리팩토링#564
Conversation
margin-top 및 gap 값 수정
|
Warning
|
| 파일/경로 요약 | 변경 내용 요약 |
|---|---|
| frontend/src/components/common/CustomTextArea/CustomTextArea.styles.ts, frontend/src/components/common/InputField/InputField.styles.ts |
입력 컨테이너 및 텍스트에어리어의 최소 너비를 385px에서 300px로 축소 |
| frontend/src/pages/ApplicationFormPage/ApplicationFormPage.styles.ts | FormTitle 스타일 조정, FormDescription 컴포넌트 신설, QuestionsWrapper 간격 및 반응형 스타일 개선 |
| frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx | ClubProfile 임시 비활성화, 설명 파싱 및 렌더링 추가, 검증/스크롤/에러 처리 리팩토링 |
| frontend/src/pages/ApplicationFormPage/components/QuestionContainer/QuestionContainer.tsx | 컨테이너 패딩을 24px에서 상하 26px, 좌우 15px로 변경 |
| frontend/src/hooks/useValidateAnswers.ts | 필수 질문 미응답 검증 훅(validateAnswers) 신설 |
| frontend/src/utils/parseDescriptionWithLinks.tsx | 텍스트 내 URL을 자동 링크로 변환하는 유틸리티(parseDescriptionWithLinks) 신설 |
Sequence Diagram(s)
sequenceDiagram
participant User
participant ApplicationFormPage
participant parseDescriptionWithLinks
participant validateAnswers
User->>ApplicationFormPage: 페이지 진입
ApplicationFormPage->>parseDescriptionWithLinks: 설명 텍스트 전달
parseDescriptionWithLinks-->>ApplicationFormPage: 링크 포함된 React 노드 반환
User->>ApplicationFormPage: 지원서 제출 클릭
ApplicationFormPage->>validateAnswers: 질문 및 답변 전달
validateAnswers-->>ApplicationFormPage: 미응답 필수 질문 ID 배열 반환
ApplicationFormPage-->>User: 미응답 질문 위치로 스크롤 또는 제출 진행
Estimated code review effort
- frontend/src/components/common/CustomTextArea/CustomTextArea.styles.ts, frontend/src/components/common/InputField/InputField.styles.ts: 1 (20 minutes)
- frontend/src/pages/ApplicationFormPage/ApplicationFormPage.styles.ts: 2 (45 minutes)
- frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx: 4 (240 minutes)
- frontend/src/pages/ApplicationFormPage/components/QuestionContainer/QuestionContainer.tsx: 1 (15 minutes)
- frontend/src/hooks/useValidateAnswers.ts: 2 (40 minutes)
- frontend/src/utils/parseDescriptionWithLinks.tsx: 1 (20 minutes)
Possibly related PRs
- [feature & refactor] 객관식 항목 1개 허용 · 필수 입력 UX 개선 · Application 구조 리팩토링 #558: 지원서 폼 컴포넌트 구조 개선 및 관련 리팩토링이 진행된 PR로, 본 PR의 구조적 변경과 직접적으로 연관됨.
- [feature] 지원서 질문 추가/삭제 및 장문형 질문 타입 구현 #454: CustomTextArea 스타일 관련 변경이 포함되어 있어, 본 PR의 스타일 조정과 코드 레벨에서 연관됨.
Suggested labels
🔨 Refactor
Suggested reviewers
- seongwon030
"""
📜 Recent review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
frontend/src/utils/parseDescriptionWithLinks.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/src/utils/parseDescriptionWithLinks.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.
✅ Deploy Preview for moadong ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
There was a problem hiding this comment.
Pull Request Overview
This PR enhances the club application form page by adding description display functionality and refactoring the component for better maintainability. The changes focus on improving user experience through better content presentation and code organization.
- Added application form description rendering with HTML link parsing support
- Extracted validation logic into a reusable custom hook for better separation of concerns
- Updated styling for improved mobile responsiveness and visual balance
Reviewed Changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| frontend/src/utils/parseDescriptionWithLinks.tsx | New utility function to parse URLs in text and convert them to React link components |
| frontend/src/hooks/useValidateAnswers.ts | Extracted answer validation logic into a reusable custom hook |
| frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx | Main component refactoring with description display and validation hook integration |
| frontend/src/pages/ApplicationFormPage/ApplicationFormPage.styles.ts | Updated styling for form title, description, and responsive design improvements |
| frontend/src/pages/ApplicationFormPage/components/QuestionContainer/QuestionContainer.tsx | Minor padding adjustment for question containers |
| frontend/src/components/common/InputField/InputField.styles.ts | Reduced minimum width for better mobile compatibility |
| frontend/src/components/common/CustomTextArea/CustomTextArea.styles.ts | Reduced minimum width for better mobile compatibility |
Comments suppressed due to low confidence (1)
frontend/src/utils/parseDescriptionWithLinks.tsx:3
- The function name 'parseDescriptionWithLinks' doesn't match the actual functionality. The function parses URLs in any text, not specifically HTML with links. Consider renaming to 'parseTextWithUrls' or 'convertUrlsToLinks' for clarity.
export const parseDescriptionWithLinks = (text: string): React.ReactNode => {
| import { Fragment } from 'react'; | ||
|
|
||
| export const parseDescriptionWithLinks = (text: string): React.ReactNode => { | ||
| const urlRegex = /(https?:\/\/[^\s]+)/g; |
There was a problem hiding this comment.
The URL regex pattern is too simplistic and may not handle edge cases properly. It doesn't account for URLs ending with punctuation or parentheses that shouldn't be part of the link. Consider using a more robust URL detection pattern or a dedicated library.
frontend/src/pages/ApplicationFormPage/components/QuestionContainer/QuestionContainer.tsx
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 7
🔭 Outside diff range comments (1)
frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx (1)
39-39: 중복된 early return 로직을 제거하세요.
clubId체크와 로딩 상태 체크가 중복되어 있습니다 (라인 39, 81-82). 코드 중복을 제거하고 일관성을 개선하세요.const { onAnswerChange: rawOnAnswerChange, getAnswersById, answers } = useAnswers(); const { data: clubDetail, error: clubError, } = useGetClubDetail(clubId ?? ''); const { data: formData, isLoading, isError, error: applicationError, } = useGetApplication(clubId ?? ''); - if (!clubId) return null; - if (isLoading) return <Spinner />; + // 모든 early return 조건을 함께 처리 + if (!clubId) return null; + if (isLoading) return <Spinner />; + if (isError || clubError) { + alert(applicationError?.message || '문제가 발생했어요.'); + navigate(`/club/${clubId}`); + return <div>문제가 발생했어요. 잠시 후 다시 시도해 주세요.</div>; + } + if (!formData || !clubDetail) { + return <div>지원서 정보를 불러오지 못했어요. 새로고침하거나 잠시 후 다시 시도해 주세요.</div>; + }Also applies to: 81-90
🧹 Nitpick comments (8)
frontend/src/utils/parseDescriptionWithLinks.tsx (1)
4-4: 정규식 패턴을 상수로 추출하여 성능을 개선하세요.매번 함수 호출 시마다 새로운 정규식 인스턴스를 생성하는 대신, 상수로 정의하여 재사용하는 것이 좋습니다.
+const URL_REGEX = /(https?:\/\/[^\s]+)/g; + export const parseDescriptionWithLinks = (text: string): React.ReactNode => { - const urlRegex = /(https?:\/\/[^\s]+)/g; + return text.split(URL_REGEX).map((part, index) => {frontend/src/components/common/CustomTextArea/CustomTextArea.styles.ts (1)
3-3: TODO 주석에 대한 지원을 제공할 수 있습니다.InputField 컴포넌트와의 중복 제거를 위한 리팩토링이 계획되어 있습니다. 공통 스타일을 추출하여 재사용 가능한 구조로 개선할 수 있습니다.
공통 스타일을 추출하는 리팩토링 솔루션을 생성하거나 이를 추적하기 위한 새로운 이슈를 열어드릴까요?
frontend/src/hooks/useValidateAnswers.ts (2)
3-14: 함수명이 훅 컨벤션과 일치하지 않습니다.파일명은
useValidateAnswers.ts이지만 실제 함수는validateAnswers입니다. React 훅 컨벤션에 따라use로 시작하거나, 순수 함수라면 파일명을 변경하는 것이 좋습니다.옵션 1: 훅으로 만들기
-export const validateAnswers = ( - questions: Question[], - getAnswersById: (id: number) => string[] -): number[] => { +export const useValidateAnswers = () => { + return (questions: Question[], getAnswersById: (id: number) => string[]): number[] => {옵션 2: 파일명 변경
파일명을validateAnswers.ts또는validation.ts로 변경하여 순수 함수임을 명확히 표현
3-6: 입력 매개변수 검증을 추가하세요.함수의 견고성을 위해 입력 매개변수에 대한 기본적인 검증을 추가하는 것이 좋습니다.
export const validateAnswers = ( questions: Question[], getAnswersById: (id: number) => string[] ): number[] => { + if (!questions || !Array.isArray(questions)) { + return []; + } + if (typeof getAnswersById !== 'function') { + throw new Error('getAnswersById must be a function'); + } + return questionsfrontend/src/pages/ApplicationFormPage/ApplicationFormPage.styles.ts (3)
5-11: 매직 넘버를 명명된 상수로 교체하세요.코딩 가이드라인에 따라 매직 넘버들을 명명된 상수로 교체하는 것을 고려해보세요:
2.2rem(폰트 크기)30px(마진)15px(패딩)+const FORM_TITLE_FONT_SIZE = '2.2rem'; +const FORM_TITLE_MARGIN = '30px'; +const FORM_HORIZONTAL_PADDING = '15px'; export const FormTitle = styled.h1` - font-size: 2.2rem; + font-size: ${FORM_TITLE_FONT_SIZE}; font-weight: 700; border: none; outline: none; - margin-top: 30px; - margin-bottom: 30px; - padding: 0 15px; + margin-top: ${FORM_TITLE_MARGIN}; + margin-bottom: ${FORM_TITLE_MARGIN}; + padding: 0 ${FORM_HORIZONTAL_PADDING}; `;
14-27: FormDescription 컴포넌트의 구조와 반응형 디자인이 우수합니다.새로운 FormDescription 컴포넌트가 잘 구현되었습니다:
white-space: pre-line으로 줄바꿈 처리- 적절한 타이포그래피 설정
- 모바일 반응형 대응
다만, 일관성을 위해 몇 가지 매직 넘버를 상수로 추출하는 것을 고려해보세요:
#444색상 값-20px,48px등의 간격 값
33-37: 반응형 디자인 개선과 미디어 쿼리 헬퍼 사용이 적절합니다.
media.mobile헬퍼를 사용한 반응형 디자인 접근 방식이 좋습니다. gap 값 조정도 적절해 보입니다 (20px → 10px).매직 넘버 개선을 위해 간격 값들을 상수로 추출하는 것을 고려해보세요:
+const QUESTIONS_GAP = '20px'; +const QUESTIONS_GAP_MOBILE = '10px'; export const QuestionsWrapper = styled.div` display: flex; flex-direction: column; - gap: 20px; + gap: ${QUESTIONS_GAP}; ${media.mobile} { - gap: 10px; + gap: ${QUESTIONS_GAP_MOBILE}; } `;frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx (1)
74-74: Non-null assertion 사용에 주의하세요.
clubId!에서 non-null assertion을 사용하고 있지만, 이미 위에서clubId체크를 했으므로 안전합니다. 하지만 더 명시적인 검증을 고려해보세요.- await applyToClub(clubId!, answers); + if (!clubId) return; // 추가 안전 장치 + await applyToClub(clubId, answers);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
frontend/src/components/common/CustomTextArea/CustomTextArea.styles.ts(2 hunks)frontend/src/components/common/InputField/InputField.styles.ts(1 hunks)frontend/src/hooks/useValidateAnswers.ts(1 hunks)frontend/src/pages/ApplicationFormPage/ApplicationFormPage.styles.ts(1 hunks)frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx(2 hunks)frontend/src/pages/ApplicationFormPage/components/QuestionContainer/QuestionContainer.tsx(1 hunks)frontend/src/utils/parseDescriptionWithLinks.tsx(1 hunks)
🧰 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 (6)
📓 Common learnings
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')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.
frontend/src/components/common/InputField/InputField.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/**/*.{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.
frontend/src/pages/ApplicationFormPage/components/QuestionContainer/QuestionContainer.tsx (1)
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.
frontend/src/components/common/CustomTextArea/CustomTextArea.styles.ts (4)
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} : 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 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/**/*.tsx : Separate significantly different conditional UI/logic into distinct components.
frontend/src/pages/ApplicationFormPage/ApplicationFormPage.styles.ts (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 : 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/**/*.{ts,tsx} : Define constants near related logic or ensure names link them clearly.
frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx (5)
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 : 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 : Abstract complex logic/interactions into dedicated components or higher-order components (HOCs).
🧬 Code Graph Analysis (2)
frontend/src/pages/ApplicationFormPage/ApplicationFormPage.styles.ts (1)
frontend/src/styles/mediaQuery.ts (1)
media(6-9)
frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx (3)
frontend/src/hooks/useValidateAnswers.ts (1)
validateAnswers(3-14)frontend/src/apis/application/applyToClub.ts (1)
applyToClub(4-34)frontend/src/utils/parseDescriptionWithLinks.tsx (1)
parseDescriptionWithLinks(3-22)
🔇 Additional comments (5)
frontend/src/hooks/useValidateAnswers.ts (1)
7-13: 검증 로직이 올바르게 구현되었습니다.필수 질문 필터링과 빈 답변 검증 로직이 정확하고 효율적으로 구현되어 있습니다. 체이닝된 필터와 맵 연산이 적절히 사용되었습니다.
frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx (4)
55-59: 헬퍼 함수 추출이 코드 가독성을 개선했습니다.
handleScrollToInvalid함수로 스크롤 로직을 분리한 것이 좋습니다. 단일 책임 원칙을 잘 따르고 있습니다.
65-65: validateAnswers 훅 사용으로 검증 로직이 잘 분리되었습니다.검증 로직을 별도 훅으로 추출한 것이 코드 재사용성과 테스트 가능성을 개선했습니다. 관심사 분리 원칙을 잘 따르고 있습니다.
104-109: 설명 렌더링 기능이 잘 구현되었습니다.조건부 렌더링과
parseDescriptionWithLinks유틸리티 함수 사용이 적절합니다. HTML 링크를 안전하게 파싱하여 렌더링하는 접근 방식이 좋습니다.
6-6: ClubProfile 컴포넌트 사용 확인 요청ApplicationFormPage.tsx에서 ClubProfile import 및 사용 부분이 주석 처리되어 있습니다.
다른 파일(ClubDetailPage/components/ClubProfile/ClubProfile.tsx)과 ClubDetailHeader.tsx에서는 정상적으로 컴포넌트가 존재하고 사용 중인 것으로 확인되었습니다.– 확인 대상
• frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx
– import 부분: 라인 6
– JSX 사용 부분: 라인 96–102해당 주석 처리된 부분이 의도된 임시 조치인지 알려주세요.
frontend/src/pages/ApplicationFormPage/components/QuestionContainer/QuestionContainer.tsx
Show resolved
Hide resolved
seongwon030
left a comment
There was a problem hiding this comment.
고생하셨습니다~ descriptionLink는 소개글 안의 url을 의미하나요?
어.. descriptionLink라는 변수나 컴포넌트는 없는데, 혹시 parseDescriptionWithLinks에 대해 말씀하신 걸까요? |
옙 |
아하 const desc = "자세한 내용은 https://example.com 참고해주세요!";
parseDescriptionWithLinks(desc);
[
<Fragment key={0}>자세한 내용은 </Fragment>,
<a key={1} href="https://example.com" ...>https://example.com</a>,
<Fragment key={2}> 참고해주세요!</Fragment>
]결과적으로 위와 같은 JSX 배열이 리턴됩니다! |
@seongwon030 |

#️⃣연관된 이슈
📝작업 내용
🆕 새로운 기능
🔧 유틸리티 함수 추가
🎣 커스텀 훅 분리
🎨 스타일 개선
Summary by CodeRabbit
신규 기능
UI/스타일 개선
버그 수정
기타