Conversation
- suspense로 fallback으로 로딩 중 화면 비는 현상 방지
- categories -> divisions - tags -> categories로 변경
- 모집대상 수정 인풋태그 추가
✅ Deploy Preview for moadong ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
- 소개글과 프리뷰 컴포넌트 묶는 EditorPreviewContainer추가
- height 속성 삭제
- presidentName -> clubPresidentName - presidentPhoneNumber -> telephoneNumber
- Partial 타입으로 선택적으로 값 추가 가능
- presidentName -> clubPresidentName - presidentPhoneNumber -> telephoneNumber
- clubDetail -> update ClubDetail
- 동적 데이터 패칭을 위해 loading과 error추가
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Walkthrough이 Pull Request는 클럽 상세 정보 업데이트와 관련된 API 및 UI 컴포넌트들을 업데이트합니다. 새로운 비동기 API 함수와 custom hook들이 추가되어 클럽 설명 및 상세 정보를 수정할 수 있게 되었으며, AdminPage와 관련 컴포넌트들이 데이터 로딩, 에러 처리 및 동적 데이터 전달을 지원하도록 개선되었습니다. 또한, 버튼 컴포넌트와 스타일, 타입 정의 등이 업데이트되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant U as 사용자
participant A as AdminPage/ClubInfoEditTab/RecruitEditTab
participant H as useUpdateClubDetail/Description
participant API as API 서버
U->>A: 업데이트 버튼 클릭
A->>H: handleUpdateClub 호출
H->>API: PUT 요청 전송 (클럽 상세/설명 업데이트)
API-->>H: 응답(성공/오류)
H-->>A: 결과 반환 및 쿼리 무효화
A-->>U: 업데이트 결과 피드백
Assessment against linked issues
Poem
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
- 기존 애니메이션 버튼 컴포넌트 제거 - ButtonProps에 불리언 타입으로 animated 추가
@coderabbitai review |
🧩 Analysis chain@seongwon030 I'll review your pull request. Let me gather the necessary information about the changes first. 🏁 Script executed: Length of output: 2665 @seongwon030 I'm currently examining the key files in your PR to understand the admin page API integration. Looking at your API implementations, hooks, and UI components to provide a thorough review. 🏁 Script executed: Length of output: 19422 @seongwon030 PR 리뷰가 완료되었습니다. 관리자 페이지의 API 연동 작업에 대해 아래와 같은 피드백을 드립니다. 잘 구현된 부분 👍
개선이 필요한 부분 🔧
전반적으로 API 연동이 잘 구현되었으며, 사용자 경험도 좋게 설계되어 있습니다. 위에서 언급한 작은 개선점들이 반영된다면 더 완성도 높은 코드가 될 것 같습니다. 수고하셨습니다! 🌟 혹시 추가 질문이 있으시면 편하게 물어봐주세요. ✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (11)
frontend/src/hooks/queries/club/useUpdateClubDescription.ts (1)
1-22: React Query를 활용한 훅 구현이 잘 되었습니다.
useUpdateClubDescription훅은 React Query의useMutation을 효과적으로 활용하여 클럽 설명 업데이트 기능을 잘 구현했습니다. 성공 시 쿼리 캐시를 무효화하여 최신 데이터를 가져오는 로직도 적절합니다.다만, 에러 처리 부분은 개선할 여지가 있습니다. 현재는 콘솔에만 오류를 기록하고 있는데, 사용자에게 알림을 표시하거나 특정 오류 유형에 따라 다른 처리를 하는 방식으로 확장하면 좋을 것 같습니다.
에러 처리 개선 예시:
onError: (error) => { console.error('Error updating club detail:', error); + // 사용자에게 오류 알림 표시 (예: 토스트 메시지) + // showToast('클럽 설명 업데이트 중 오류가 발생했습니다.'); + + // 또는 오류 유형에 따라 다른 처리 + // if (error instanceof NetworkError) { + // // 네트워크 오류 처리 + // } else if (error instanceof ValidationError) { + // // 유효성 검사 오류 처리 + // } },frontend/src/hooks/queries/club/useUpdateClubDetail.ts (1)
1-22: Partial 타입을 활용한 효율적인 업데이트 훅 구현이 돋보입니다.
useUpdateClubDetail훅은Partial<ClubDetail>타입을 사용하여 클럽 정보의 일부만 업데이트할 수 있도록 유연하게 설계되었습니다. React Query의 mutation 기능을 활용한 구현과 성공 시 쿼리 캐시 무효화 로직이 적절합니다.다만, 이전 훅과 마찬가지로 에러 처리 부분은 개선할 여지가 있습니다. 단순히 콘솔 로깅만 하는 것보다 사용자에게 피드백을 제공하거나 특정 오류 상황에 대응하는 로직을 추가하면 더 좋을 것 같습니다.
에러 처리 개선 예시:
onError: (error) => { console.error('Error updating club detail:', error); + // 오류 유형에 따른 처리 추가 + // if (error.status === 400) { + // // 잘못된 요청 처리 + // } else if (error.status === 401) { + // // 인증 오류 처리 + // } + + // 사용자에게 알림 표시 + // notifyError('클럽 정보 업데이트에 실패했습니다.'); },frontend/src/pages/AdminPage/AdminPage.tsx (2)
16-19: 로딩 및 에러 상태 처리데이터 로딩 상태와 에러 상태에 대한 처리가 적절히 구현되었습니다. 사용자 경험을 더 향상시키기 위해 로딩 중인 상태에서는 스켈레톤 UI나 스피너 컴포넌트를 사용하는 것을 고려해 보세요.
- if (!clubDetail) { - return <div>Loading...</div>; - } + if (isLoading || !clubDetail) { + return <LoadingSpinner />; // 로딩 스피너 컴포넌트 사용 + }
26-26: 조건부 체이닝 연산자 간소화
clubDetail이 존재하지 않는 경우 이미 이전 조건문에서 처리되므로, 여기서 조건부 체이닝 연산자(?.)를 사용할 필요가 없습니다.- <SideBar clubName={clubDetail?.name || ''} /> + <SideBar clubName={clubDetail.name} />frontend/src/apis/updateClubDetail.ts (1)
15-21: 에러 처리 개선 가능JSON 파싱 에러를 try-catch로 잘 처리하고 있으나, 이전 리뷰에서 논의된 것처럼 코드를 더 간결하게 만들 수 있습니다.
result변수를let대신const로 선언하고, 파싱 실패 시 바로 에러를 던지는 방식으로 리팩토링할 수 있을 것입니다.- let result; - try { - result = await response.json(); - } catch (error) { - console.error('📌 JSON 파싱 실패:', error); - result = null; - } + try { + const result = await response.json(); + + if (!response.ok) { + const errorMessage = result?.message + ? `Failed to update club (HTTP ${response.status}): ${result.message}` + : `Failed to update club (HTTP ${response.status})`; + + throw new Error(errorMessage); + } + + if (!result?.data) { + console.error('📌 API 응답에 data 필드가 없음:', result); + throw new Error('Unexpected API response: Missing data field'); + } + } catch (error) { + if (error instanceof Error) { + throw error; + } + console.error('📌 JSON 파싱 실패:', error); + throw new Error('Failed to parse API response'); + }frontend/src/apis/updateClubDescription.ts (1)
15-34: 코드 중복 리팩토링 필요이 함수의 에러 처리 로직이
updateClubDetail.ts의 코드와 거의 동일합니다. 공통 유틸리티 함수로 추출하여 코드 중복을 줄일 수 있을 것입니다.+// frontend/src/utils/apiUtils.ts (새 파일 생성) +export const handleApiResponse = async (response: Response): Promise<any> => { + try { + const result = await response.json(); + + if (!response.ok) { + const errorMessage = result?.message + ? `API request failed (HTTP ${response.status}): ${result.message}` + : `API request failed (HTTP ${response.status})`; + + throw new Error(errorMessage); + } + + if (!result?.data) { + console.error('📌 API 응답에 data 필드가 없음:', result); + throw new Error('Unexpected API response: Missing data field'); + } + + return result.data; + } catch (error) { + if (error instanceof Error) { + throw error; + } + console.error('📌 JSON 파싱 실패:', error); + throw new Error('Failed to parse API response'); + } +};그리고 API 함수에서 이 유틸리티 사용:
- let result; - try { - result = await response.json(); - } catch (error) { - console.error('📌 JSON 파싱 실패:', error); - result = null; - } - - if (!response.ok) { - const errorMessage = result?.message - ? `Failed to update club (HTTP ${response.status}): ${result.message}` - : `Failed to update club (HTTP ${response.status})`; - - throw new Error(errorMessage); - } - - if (!result?.data) { - console.error('📌 API 응답에 data 필드가 없음:', result); - throw new Error('Unexpected API response: Missing data field'); - } + await handleApiResponse(response);frontend/src/components/common/Button/Button.tsx (1)
44-52: 컴포넌트 구현 개선Button 컴포넌트가
animated속성을 기본값 false로 포함하도록 업데이트된 것은 좋은 접근법입니다. 기존 코드와의 호환성을 유지하면서 새 기능을 추가할 수 있습니다.애니메이션 기능이 추가되었으므로, 이 기능을 사용하는 컴포넌트에 대해 설명하는 주석을 추가하는 것이 좋겠습니다:
/** + * Button 컴포넌트 + * + * @param width - 버튼의 너비 (기본값: auto) + * @param children - 버튼 내부 컨텐츠 + * @param onClick - 클릭 이벤트 핸들러 + * @param animated - 애니메이션 효과 활성화 여부 (기본값: false) + */ const Button = ({ width, children, onClick, animated = false, }: ButtonProps) => ( <StyledButton width={width} onClick={onClick} animated={animated}> {children} </StyledButton> );frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx (2)
24-25: 하드코딩된 배열 관리
divisions와categories가 하드코딩 상태인데, 만약 변경 가능성이 높다면 상수 파일로 분리하거나 추후 서버 API를 통해 동적으로 받는 방안을 고려하길 권장합니다.
44-85: 업데이트 로직과 알림 처리
handleUpdateClub함수 내에서 방어 코드(if (!clubDetail) return;)가 존재해 예외 처리가 잘 되어 있습니다. 또한,updateClub호출 시 성공/실패 시나리오를alert로 알려주는 방식은 간단하고 명확합니다.기능 확장을 고려한다면 사용자 경험 측면에서
alert대신 Toast 컴포넌트 같은 UI 요소를 활용하는 방법도 좋겠습니다.frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.styles.ts (1)
16-22: 미리보기 컨테이너 구성
에디터와 미리보기를 가로로 나란히 배치하기 위한flex-direction: row;사용이 적절합니다. 반응형 구현을 고려한다면, 모바일 화면 등에 대한 대응도 추가로 검토해보면 좋겠습니다.frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsx (1)
53-112: Promise.allSettled로 이중 업데이트 처리
클럽 정보와 소개글을 병렬로 처리하고, 실패 시 alert로 처리하는 로직이 명확하여 가독성이 좋습니다. 다만, 일부만 실패하는 상황에서 세부적인 재시도 또는 롤백 전략이 필요할 수 있으니 참고하십시오.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (15)
frontend/src/apis/updateClubDescription.ts(1 hunks)frontend/src/apis/updateClubDetail.ts(1 hunks)frontend/src/components/common/Button/Button.tsx(2 hunks)frontend/src/hooks/queries/club/useGetClubDetail.ts(1 hunks)frontend/src/hooks/queries/club/useUpdateClubDescription.ts(1 hunks)frontend/src/hooks/queries/club/useUpdateClubDetail.ts(1 hunks)frontend/src/pages/AdminPage/AdminPage.tsx(1 hunks)frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx(2 hunks)frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.styles.ts(1 hunks)frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx(3 hunks)frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.styles.ts(2 hunks)frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsx(1 hunks)frontend/src/pages/ClubDetailPage/components/InfoBox/InfoBox.tsx(1 hunks)frontend/src/pages/ClubDetailPage/components/IntroduceBox/IntroduceBox.styles.ts(1 hunks)frontend/src/types/club.ts(1 hunks)
🔇 Additional comments (27)
frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.styles.ts (1)
3-7: 잘 구현된 스타일 컴포넌트입니다.
TitleButtonContainer스타일드 컴포넌트가 제목과 버튼을 수평으로 배치하면서 공간을 효율적으로 활용할 수 있도록 적절히 구현되었습니다. flex 속성을 사용해 레이아웃을 명확하게 정의한 점이 좋습니다.frontend/src/pages/ClubDetailPage/components/IntroduceBox/IntroduceBox.styles.ts (1)
36-37: 스타일 일관성 개선이 잘 이루어졌습니다.순서 있는 리스트(
ol)와 순서 없는 리스트(ul) 모두에 동일한 패딩을 적용함으로써 일관된 시각적 경험을 제공하는 좋은 개선입니다. 사용자 인터페이스 일관성 측면에서 중요한 디테일을 잘 고려하셨습니다.frontend/src/pages/ClubDetailPage/components/InfoBox/InfoBox.tsx (1)
23-24: 프로퍼티 이름이 업데이트되었습니다.클럽 정보의 프로퍼티 이름이
presidentName에서clubPresidentName으로,presidentPhoneNumber에서telephoneNumber로 변경되었습니다. 이 변경은 타입 파일의ClubDetail인터페이스 변경과 일치합니다.frontend/src/hooks/queries/club/useGetClubDetail.ts (1)
3-3: 제네릭 타입 매개변수 추가로 타입 안전성 향상
useQuery훅에<ClubDetail>타입 매개변수를 추가하여 API 응답이ClubDetail인터페이스에 맞는지 컴파일 시점에 확인할 수 있게 되었습니다. 이는 타입 안전성을 향상시키는 좋은 개선사항입니다.Also applies to: 6-6
frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx (2)
6-8: SideBarProps 인터페이스 추가
SideBarProps인터페이스를 추가하여 컴포넌트의 프로퍼티 타입을 명확하게 정의했습니다. 이는 타입스크립트의 장점을 잘 활용한 접근 방식입니다.
16-16: 하드코딩된 클럽 이름을 동적 값으로 변경하드코딩된 클럽 이름("WAP")을 동적인
clubNameprop으로 변경했습니다. 이로써 컴포넌트의 재사용성이 향상되었고, 다양한 클럽 이름을 표시할 수 있게 되었습니다.Also applies to: 29-29
frontend/src/pages/AdminPage/AdminPage.tsx (1)
28-28: 컨텍스트를 통한 클럽 상세 정보 전달
Outlet컴포넌트의contextprop을 통해 클럽 상세 정보를 하위 컴포넌트에 전달하는 방식이 적절합니다. 이를 통해 하위 라우트에서 클럽 정보에 쉽게 접근할 수 있습니다.frontend/src/types/club.ts (2)
16-17: 필드 이름 변경에 대한 올바른 대응백엔드 API 필드 이름 변경에 맞게
presidentName에서clubPresidentName으로,presidentPhoneNumber에서telephoneNumber로 변경이 잘 이루어졌습니다. PR 설명에서 언급한 API 필드 불일치 문제를 해결하는 좋은 접근법입니다.
22-25: 새 인터페이스 추가 적절함
ClubDescription인터페이스 추가는 클럽 설명 업데이트 기능에 적합한 타입 정의입니다. 클럽 ID와 설명을 포함하여 API 요청에 필요한 데이터 구조를 명확하게 정의하고 있습니다.frontend/src/apis/updateClubDetail.ts (2)
4-6: API 함수 정의 적절
Partial<ClubDetail>타입을 매개변수로 사용하여 클럽 정보 일부만 업데이트할 수 있도록 하는 유연한 접근방식이 좋습니다. 이전 리뷰에서 논의된 것처럼 나중에 타입 리팩토링이 필요할 수 있지만, 현재 구현은 적절합니다.
23-34: 에러 메시지 처리 잘 구현됨응답 코드와 에러 메시지를 결합하여 유용한 정보를 제공하는 방식이 잘 구현되었습니다. 또한
data필드 유무를 확인하여 예상치 못한 API 응답을 처리하는 것은 좋은 방어적 프로그래밍 접근법입니다.frontend/src/apis/updateClubDescription.ts (1)
4-13: API 엔드포인트 및 매개변수 적절
ClubDescription타입을 매개변수로 사용하여 클럽 설명 업데이트 함수를 잘 구현했습니다. API 엔드포인트 경로가 명확하고 HTTP 메소드 및 헤더 설정이 적절합니다.frontend/src/components/common/Button/Button.tsx (3)
4-9: 인터페이스 개선 사항
ButtonProps인터페이스가 export로 변경되고animated속성이 추가된 것은 좋은 개선입니다. 애니메이션을 선택적으로 적용할 수 있는 유연성을 제공합니다.
11-15: 애니메이션 효과 추가
pulse키프레임 애니메이션을 추가한 것은 사용자 경험 향상에 좋은 기여입니다. 스케일 변경과 배경색 전환이 자연스러운 효과를 줍니다.
29-40: 조건부 애니메이션 적용 잘 구현됨
animated속성에 따라 애니메이션을 조건부로 적용하는 방식이 잘 구현되었습니다. hover 상태와 active 상태 모두에 애니메이션 효과를 일관되게 적용했습니다.frontend/src/pages/AdminPage/tabs/ClubInfoEditTab/ClubInfoEditTab.tsx (5)
2-7: 모듈 임포트 구성이 적절합니다
React Router와 리소스 훅을 활용하여 필요한 기능들을 잘 불러오고 있습니다.
13-14: Context 활용과 Mutation 훅 분리가 깔끔합니다
useOutletContext로clubDetail을 받아오고,useUpdateClubDetail훅을 별도로 선언하여 로직을 분리한 점이 재사용과 유지보수에 유리해 보입니다.
19-21: 초기 상태값 설정 주의
introduction,selectedDivision,selectedCategory를 빈 문자열로 초기화한 것은 합리적입니다. 다만, 서버에서 해당 필드가 누락되어 null이나 undefined로 내려올 수 있는 경우를 고려해야 합니다.서버 응답이 예상과 다를 때도 안정적으로 처리될 수 있는지 확인하고, 필요하다면 안전한 디폴트 값을 추가해주세요.
28-41: useEffect 통한 상태 동기화
clubDetail이 들어오면 해당 값을 상태로 업데이트하는 로직이 잘 구성되었습니다. 하지만 FIXME 코멘트에 언급된 필드명 수정 사항이 향후 반영되지 않으면, 다른 파일과의 호환성 문제나 충돌이 발생할 수 있습니다.다른 컴포넌트나 API 요청에서 동일한 필드를 사용하는지 확인하고, 전체적으로 통일된 이름을 적용할지 결정하면 좋겠습니다.
133-137: 소개 글 최대 글자 수 주의
maxLength를 20자로 줄인 점은 UI 차원에서 간단하지만, 이미 서버에 20자 초과로 저장된 소개 글이 있을 경우 편집 시 잘려 나갈 가능성을 유념해야 합니다.서버 측 혹은 기존 데이터가 20자를 초과할 때 어떤 방식으로 처리가 될지 확인해 보세요.
frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.styles.ts (4)
5-5: 세로 레이아웃 전환
flex-direction: column;을 적용해 내용이 세로로 배치되어 가독성이 한층 좋아졌습니다.
10-14: 버튼 컨테이너 스타일 정의
EditButtonContainer에서justify-content: space-between을 사용해 양쪽 버튼 배치가 가능해졌고, 인터페이스가 보다 직관적입니다.
28-28: 에디터 최소 높이 확보
min-height: 400px;으로 에디터 영역이 너무 작아지지 않도록 기본 크기를 보장하는 접근이 좋습니다.
62-62: 에디터 확장성 향상
flex-grow: 1;을 적용해 화면 크기에 따라 에디터가 유연하게 확장할 수 있게 구성한 점이 훌륭합니다.frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsx (3)
1-2: 불필요한 의존성 없이 필요한 모듈만 임포트
React, styled 컴포넌트, Mutation 훅 등을 적절히 조합해, 각 기능의 책임을 명확히 분리한 구성이 보기 좋습니다.Also applies to: 6-7, 9-12
15-22: 상태 정의 및 Mutation 훅 설정
useOutletContext로 받아온clubDetail을 바탕으로 여러 mutation 훅 (useUpdateClubDetail,useUpdateClubDescription)을 분리해 사용하는 것은 유지보수에 유리합니다.
41-51: 기본 데이터 세팅 주의
parseRecruitmentPeriod로 날짜를 변환해 상태로 세팅하고,recruitmentTarget과description을 초기에 채우는 로직이 합리적입니다. null이면 어떻게 될지 다시 한번 확인해 주세요.
#️⃣연관된 이슈
📝작업 내용
상세정보 소개글 ul 스타일 설정 a609192
SideBar에 동아리 이름 적용 5da3cbe
관리자 페이지 default값 가져오기
관리자 페이지 수정 및 수정하기 버튼 추가
소개글 수정 탭 api 연결 43bd993
중점적으로 리뷰받고 싶은 부분(선택)
논의하고 싶은 부분(선택)
현재 클럽상세조회 api와 클럽상세정보 수정 api에서의 동아리 회장이름과 넘버 필드명이 다릅니다.
백엔드에서 고치면 해결할 예정입니다.
🫡 참고사항
Summary by CodeRabbit
새로운 기능
스타일 개선