Conversation
|
""" Walkthrough이 변경사항은 호스트 채널 정보의 조회 및 수정 기능을 정적 데이터에서 동적 API 기반으로 전환합니다. 새로운 API 함수와 타입 정의, React Query 기반 커스텀 훅들이 추가되어, 호스트 채널 정보와 상세 정보, 멤버 목록, 이벤트 리스트 등을 서버에서 비동기적으로 받아오고 수정할 수 있게 되었습니다. 이에 따라 여러 UI 컴포넌트들도 정적 데이터 참조 대신 API 호출 결과를 사용하도록 리팩터링되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant UI as HostEditPage/HostInfoPage 등
participant Hook as useHostChannelInfo
participant API as hostChannelInfo API
participant Server as Backend
UI->>Hook: hostChannelId 전달
Hook->>API: hostChannelInfo({ hostChannelId })
API->>Server: GET /host-channels/{hostChannelId}
Server-->>API: HostChannelInfoResponse 반환
API-->>Hook: data 반환
Hook-->>UI: data.result 전달
Note over UI,Hook: UI는 data.result를 사용해 화면 렌더링
sequenceDiagram
participant UI as HostEditPage
participant Hook as useUpdateHostChannelInfo
participant API as updateHostInfo API
participant Server as Backend
UI->>Hook: handleSave 호출 (수정 데이터 전달)
Hook->>API: updateHostInfo(hostChannelId, dto)
API->>Server: PUT /host-channels/{hostChannelId} (dto)
Server-->>API: 응답 반환
API-->>Hook: data 반환
Hook-->>UI: onSuccess/onError 처리 (알림, 캐시 무효화 등)
Possibly related issues
Possibly related PRs
Suggested reviewers
Poem
📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (4)
🚧 Files skipped from review as they are similar to previous changes (4)
✨ Finishing Touches
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed 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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (4)
src/entities/host/api/hostChannelInfo.ts (1)
1-7: API 함수 구현이 잘 되어 있습니다.호스트 채널 정보를 가져오는 API 함수가 적절하게 구현되어 있습니다. 인터페이스를 올바르게 활용하고 있으며, 엔드포인트 구성도 명확합니다.
다만, 에러 처리 로직을 추가하는 것을 고려해 보세요. 현재는 오류가 발생할 경우 호출자에게 그대로 전파됩니다.
const hostChannelInfo = async (dto: HostChannelInfoRequest) => { - const response = await axiosClient.get<HostChannelInfoResponse>(`/host-channels/${dto.hostChannelId}/info`); - return response.data; + try { + const response = await axiosClient.get<HostChannelInfoResponse>(`/host-channels/${dto.hostChannelId}/info`); + return response.data; + } catch (error) { + console.error('호스트 채널 정보를 가져오는 중 오류 발생:', error); + throw error; + } };src/features/host/api/host.ts (1)
1-8: API 모듈이 정상적으로 구현되었습니다호스트 정보를 업데이트하는 API 함수가 잘 구현되었습니다. 엔드포인트와 HTTP 메소드(PUT)가 적절하게 사용되었으며, 필요한 타입도 잘 가져오고 있습니다.
다만 몇 가지 개선할 점이 있습니다:
- 에러 처리 로직이 없습니다. 요청 실패 시 어떻게 처리할지 고려해보세요.
- 함수의 반환 타입을 명시하면 타입 안전성이 향상됩니다.
- const updateHostInfo = async (hostChannelId: number, dto: UpdateHostChannelInfoRequest) => { + const updateHostInfo = async (hostChannelId: number, dto: UpdateHostChannelInfoRequest): Promise<any> => { const response = await axiosClient.put(`/host-channels/${hostChannelId}`, dto); return response.data; };src/entities/host/hook/useHostChannelInfoHook.tsx (1)
1-12: React Query 훅 구현이 깔끔합니다호스트 채널 정보를 가져오는 커스텀 훅이 React Query를 활용하여 잘 구현되었습니다. 특히
enabled옵션을 통해hostChannelId가 유효할 때만 쿼리를 실행하도록 한 부분이 좋습니다.그러나 로딩 상태와 에러 상태를 처리하지 않고 있어, 이를 개선하면 훅을 사용하는 컴포넌트에서 더 유연하게 대응할 수 있을 것입니다.
const useHostChannelInfo = (hostChannelId: number) => { - const { data } = useQuery({ + const { data, isLoading, error } = useQuery({ queryKey: ['hostInfo', hostChannelId], queryFn: () => hostChannelInfo({ hostChannelId }), enabled: !!hostChannelId, }); - return { data }; + return { data, isLoading, error }; };src/pages/menu/ui/myHost/HostEditPage.tsx (1)
66-70:useEffect의존성에channelDescription을 제외하면 불필요한 재호출을 방지할 수 있습니다
channelDescription을 의존성에 두면 사용자가 입력할 때마다 effect가 재실행됩니다. 초기화 목적이라면hostInfo?.result.channelDescription만 의존성에 포함하는 것이 적절합니다.-useEffect(() => { - if (hostInfo?.result.channelDescription && channelDescription === '') { - setChannelDescription(hostInfo.result.channelDescription); - } -}, [hostInfo, channelDescription]); +useEffect(() => { + if (hostInfo?.result.channelDescription) { + setChannelDescription(hostInfo.result.channelDescription); + } +}, [hostInfo?.result?.channelDescription]);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (11)
src/entities/host/api/hostChannelInfo.ts(1 hunks)src/entities/host/hook/useHostChannelInfoHook.tsx(1 hunks)src/entities/host/hook/useHostDetailHook.ts(1 hunks)src/entities/host/model/hostChannelInfo.ts(1 hunks)src/features/host/api/host.ts(1 hunks)src/features/host/hook/useHostHook.ts(1 hunks)src/features/host/model/host.ts(1 hunks)src/pages/menu/ui/myHost/HostDetailPage.tsx(2 hunks)src/pages/menu/ui/myHost/HostEditPage.tsx(4 hunks)src/pages/menu/ui/myHost/HostInfoPage.tsx(1 hunks)src/shared/ui/backgrounds/HostDetailLayout.tsx(3 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (4)
src/entities/host/api/hostChannelInfo.ts (2)
src/entities/host/model/hostChannelInfo.ts (2)
HostChannelInfoRequest(1-3)HostChannelInfoResponse(5-14)src/shared/types/api/http-client.ts (1)
axiosClient(6-13)
src/features/host/hook/useHostHook.ts (1)
src/features/host/model/host.ts (1)
UpdateHostChannelInfoRequest(1-6)
src/features/host/api/host.ts (2)
src/features/host/model/host.ts (1)
UpdateHostChannelInfoRequest(1-6)src/shared/types/api/http-client.ts (1)
axiosClient(6-13)
src/pages/menu/ui/myHost/HostEditPage.tsx (2)
src/shared/types/hostInfoType.ts (1)
hostInfo(15-76)src/features/host/hook/useHostHook.ts (1)
useUpdateHostChannelInfo(6-14)
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: storybook
🔇 Additional comments (10)
src/entities/host/model/hostChannelInfo.ts (2)
1-3: 인터페이스가 잘 정의되어 있습니다.호스트 채널 ID를 요청하기 위한 인터페이스가 명확하게 정의되어 있습니다. 타입을
number로 지정한 것은 적절합니다.
5-14: 응답 인터페이스 구조가 깔끔합니다.호스트 채널 정보에 대한 응답 인터페이스가 체계적으로 구성되어 있습니다. 중첩된 객체와 배열의 타입이 명확하게 정의되어 있어 타입 안전성을 확보할 수 있습니다.
src/entities/host/hook/useHostDetailHook.ts (1)
8-8: 쿼리 최적화가 잘 적용되었습니다.
enabled: !!hostChannelId옵션 추가는 매우 좋은 최적화입니다. 유효한 hostChannelId가 있을 때만 쿼리가 실행되도록 하여 불필요한 API 호출을 방지합니다.src/features/host/model/host.ts (1)
1-6: 인터페이스 정의가 명확합니다호스트 채널 정보 업데이트를 위한 인터페이스가 잘 정의되어 있습니다. 필요한 필드들이 모두 포함되어 있고 타입이 명확합니다.
좀 더 가독성을 높이기 위해 각 필드에 JSDoc 주석을 추가하는 것을 고려해 볼 수 있습니다.
src/shared/ui/backgrounds/HostDetailLayout.tsx (3)
5-5: 적절한 훅 가져오기새로 만든 훅을 올바르게 가져오고 있습니다.
15-16: ID 변환 및 데이터 가져오기 로직 구현 적절URL 파라미터에서 가져온 문자열 ID를 숫자로 변환하고, 해당 ID를 사용하여 호스트 채널 정보를 가져오는 훅을 호출하는 부분이 잘 구현되었습니다.
33-33: 레이아웃 정렬 변경프로필 섹션의 정렬이 중앙(
justify-center)에서 시작(justify-start)으로 변경되었습니다. 디자인 변경에 따른 적절한 수정입니다.src/pages/menu/ui/myHost/HostInfoPage.tsx (1)
28-31:memberName.slice(1)로 첫 글자를 임의로 자르는 이유를 다시 확인해주세요
이 로직은 한글 이름에서 초성을 제거하려는 의도로 보이지만, 다국어·단문 이름에서는 의도치 않게 이름이 잘릴 수 있습니다. API에서 이미 별도 닉네임이 내려오는지, 또는 UI 요구사항인지 명확히 확인해 주세요.src/pages/menu/ui/myHost/HostEditPage.tsx (2)
38-44:updatedData필드 중복 여부를 확인해주세요
REST API 경로 파라미터로 이미hostChannelId가 전달된다면 DTO에 같은 값을 다시 넣을 필요가 없습니다. 백엔드 스펙을 확인 후 중복을 제거해 주세요.
48-50: Query Key 불일치 가능성
Invalidate 시 사용하는['hostInfo', hostChannelId]키가useHostChannelInfo내부에서 사용하는 키와 다르면 캐시가 무효화되지 않습니다. 훅 정의를 재확인해 동일한 키를 사용하도록 맞춰주세요.
| export const useUpdateHostChannelInfo = () => { | ||
| const { id } = useParams(); | ||
| const hostChannelId = Number(id); | ||
|
|
||
| const mutation = useMutation({ | ||
| mutationFn: (dto: UpdateHostChannelInfoRequest) => updateHostInfo(hostChannelId, dto), | ||
| }); | ||
| return mutation; | ||
| }; |
There was a problem hiding this comment.
🛠️ Refactor suggestion
뮤테이션 훅 구현이 잘 되어 있습니다.
호스트 채널 정보 업데이트를 위한 커스텀 훅이 적절하게 구현되어 있습니다. URL 파라미터에서 ID를 추출하고 뮤테이션을 설정하는 방식이 깔끔합니다.
다만, 몇 가지 개선 사항이 있습니다:
- ID 변환에 대한 유효성 검사가 필요합니다. (NaN 체크)
- 캐시 무효화 로직이 누락되어 있습니다.
- 성공/실패 처리가 설정되어 있지 않습니다.
export const useUpdateHostChannelInfo = () => {
const { id } = useParams();
- const hostChannelId = Number(id);
+ const hostChannelId = id ? Number(id) : undefined;
+
+ if (!hostChannelId || isNaN(hostChannelId)) {
+ throw new Error('유효하지 않은 호스트 채널 ID입니다.');
+ }
const mutation = useMutation({
mutationFn: (dto: UpdateHostChannelInfoRequest) => updateHostInfo(hostChannelId, dto),
+ onSuccess: () => {
+ // React Query 클라이언트 import 필요
+ // queryClient.invalidateQueries(['hostChannelInfo', hostChannelId]);
+ },
});
return mutation;
};Committable suggestion skipped: line range outside the PR's diff.
| <p className="text-lg md:text-xl font-bold">{data?.result.hostChannelName}</p> | ||
| <p className="flex-wrap text-sm md:text-base">{data?.result.channelDescription}</p> |
There was a problem hiding this comment.
🛠️ Refactor suggestion
동적 데이터 렌더링 구현
정적 데이터 대신 API에서 가져온 동적 데이터를 사용하여 호스트 이름과 설명을 렌더링하도록 변경되었습니다. 이는 좋은 개선입니다.
그러나 데이터 로딩 중이거나 오류 발생 시 대체 UI가 없어 사용자 경험에 영향을 줄 수 있습니다. 로딩 상태와 오류 상태에 대한 처리를 추가하는 것을 권장합니다.
const HostDetailLayout = ({ rightContent, children }: HostDetailLayoutProps) => {
const { id } = useParams<{ id: string }>();
const navigate = useNavigate();
const hostChannelId = Number(id);
- const { data } = useHostChannelInfo(hostChannelId);
+ const { data, isLoading, error } = useHostChannelInfo(hostChannelId);
const handleBackClick = () => {
navigate(-1);
};
+ // 로딩 중 또는 에러 발생 시 대체 UI 렌더링
+ const renderHostInfo = () => {
+ if (isLoading) {
+ return (
+ <div className="flex flex-col gap-1 md:gap-3 ml-5 text-white">
+ <p className="text-lg md:text-xl font-bold">로딩 중...</p>
+ <p className="flex-wrap text-sm md:text-base">호스트 정보를 불러오고 있습니다.</p>
+ </div>
+ );
+ }
+
+ if (error) {
+ return (
+ <div className="flex flex-col gap-1 md:gap-3 ml-5 text-white">
+ <p className="text-lg md:text-xl font-bold">정보 불러오기 실패</p>
+ <p className="flex-wrap text-sm md:text-base">호스트 정보를 불러오는데 실패했습니다.</p>
+ </div>
+ );
+ }
+
+ return (
+ <div className="flex flex-col gap-1 md:gap-3 ml-5 text-white">
+ <p className="text-lg md:text-xl font-bold">{data?.result.hostChannelName || '이름 없음'}</p>
+ <p className="flex-wrap text-sm md:text-base">{data?.result.channelDescription || '설명 없음'}</p>
+ </div>
+ );
+ };
return (
<div className="bg-white relative">
<div className="top-0 h-48 md:h-56 bg-gradient-to-br from-[#FF5593] to-[rgb(255,117,119)]">
{/* 헤더 영역 */}
<Header
leftButtonLabel="<"
leftButtonClassName="text-xl z-30"
leftButtonClick={handleBackClick}
rightContent={rightContent}
color="white"
/>
<div className="flex justify-start items-center px-6 md:px-10">
<ProfileCircle profile="hostProfile" className="md:w-28 md:h-28 w-24 h-24" />
- <div className="flex flex-col gap-1 md:gap-3 ml-5 text-white">
- <p className="text-lg md:text-xl font-bold">{data?.result.hostChannelName}</p>
- <p className="flex-wrap text-sm md:text-base">{data?.result.channelDescription}</p>
- </div>
+ {renderHostInfo()}
</div>
</div>
{/* 레이아웃 내용 */}
<div className="absolute top-[calc(100%-2vh)] w-full bg-white rounded-t-[20px]">{children}</div>
</div>
);
};📝 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.
| <p className="text-lg md:text-xl font-bold">{data?.result.hostChannelName}</p> | |
| <p className="flex-wrap text-sm md:text-base">{data?.result.channelDescription}</p> | |
| const HostDetailLayout = ({ rightContent, children }: HostDetailLayoutProps) => { | |
| const { id } = useParams<{ id: string }>(); | |
| const navigate = useNavigate(); | |
| const hostChannelId = Number(id); | |
| const { data, isLoading, error } = useHostChannelInfo(hostChannelId); | |
| const handleBackClick = () => { | |
| navigate(-1); | |
| }; | |
| // 로딩 중 또는 에러 발생 시 대체 UI 렌더링 | |
| const renderHostInfo = () => { | |
| if (isLoading) { | |
| return ( | |
| <div className="flex flex-col gap-1 md:gap-3 ml-5 text-white"> | |
| <p className="text-lg md:text-xl font-bold">로딩 중...</p> | |
| <p className="flex-wrap text-sm md:text-base">호스트 정보를 불러오고 있습니다.</p> | |
| </div> | |
| ); | |
| } | |
| if (error) { | |
| return ( | |
| <div className="flex flex-col gap-1 md:gap-3 ml-5 text-white"> | |
| <p className="text-lg md:text-xl font-bold">정보 불러오기 실패</p> | |
| <p className="flex-wrap text-sm md:text-base">호스트 정보를 불러오는데 실패했습니다.</p> | |
| </div> | |
| ); | |
| } | |
| return ( | |
| <div className="flex flex-col gap-1 md:gap-3 ml-5 text-white"> | |
| <p className="text-lg md:text-xl font-bold">{data?.result.hostChannelName || '이름 없음'}</p> | |
| <p className="flex-wrap text-sm md:text-base">{data?.result.channelDescription || '설명 없음'}</p> | |
| </div> | |
| ); | |
| }; | |
| return ( | |
| <div className="bg-white relative"> | |
| <div className="top-0 h-48 md:h-56 bg-gradient-to-br from-[#FF5593] to-[rgb(255,117,119)]"> | |
| {/* 헤더 영역 */} | |
| <Header | |
| leftButtonLabel="<" | |
| leftButtonClassName="text-xl z-30" | |
| leftButtonClick={handleBackClick} | |
| rightContent={rightContent} | |
| color="white" | |
| /> | |
| <div className="flex justify-start items-center px-6 md:px-10"> | |
| <ProfileCircle profile="hostProfile" className="md:w-28 md:h-28 w-24 h-24" /> | |
| {renderHostInfo()} | |
| </div> | |
| </div> | |
| {/* 레이아웃 내용 */} | |
| <div className="absolute top-[calc(100%-2vh)] w-full bg-white rounded-t-[20px]"> | |
| {children} | |
| </div> | |
| </div> | |
| ); | |
| }; |
| const hostChannelId = Number(id); | ||
| const { data } = useHostChannelInfo(hostChannelId); | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
URL 파라미터 검증 및 로딩/에러 상태를 명시적으로 처리해주세요
id가 undefined이거나 숫자로 변환이 실패(NaN)할 경우, 훅이 즉시 호출되어 불필요한 API 요청이 발생하거나 컴포넌트가 예외 상태에 빠질 수 있습니다. enabled 옵션 또는 early-return 패턴으로 방어 코드를 추가하는 편이 안전합니다. 또한 로딩·에러 UI를 명시해 UX를 개선해 보세요.
-const hostChannelId = Number(id);
-const { data } = useHostChannelInfo(hostChannelId);
+const hostChannelId = Number(id);
+
+// 잘못된 URL 파라미터 방어
+if (!id || Number.isNaN(hostChannelId)) {
+ return <div>잘못된 URL 입니다.</div>;
+}
+
+// React-Query useHostChannelInfo 훅이 `enabled` 옵션을 지원한다고 가정
+const { data, isLoading, isError } = useHostChannelInfo(hostChannelId, {
+ enabled: !!hostChannelId,
+});
+
+if (isLoading) return <div>로딩 중...</div>;
+if (isError) return <div>데이터를 불러오지 못했습니다.</div>;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const hostChannelId = Number(id); | |
| const { data } = useHostChannelInfo(hostChannelId); | |
| const hostChannelId = Number(id); | |
| // 잘못된 URL 파라미터 방어 | |
| if (!id || Number.isNaN(hostChannelId)) { | |
| return <div>잘못된 URL 입니다.</div>; | |
| } | |
| // React-Query useHostChannelInfo 훅이 `enabled` 옵션을 지원한다고 가정 | |
| const { data, isLoading, isError } = useHostChannelInfo(hostChannelId, { | |
| enabled: !!hostChannelId, | |
| }); | |
| if (isLoading) return <div>로딩 중...</div>; | |
| if (isError) return <div>데이터를 불러오지 못했습니다.</div>; |
| const hostChannelId = Number(id); | ||
| const { data } = useHostDetail(hostChannelId); | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
URL 파라미터 검증 로직 부재
Number(id) 결과가 NaN이면 useHostDetail 훅이 잘못된 파라미터로 호출됩니다. HostInfoPage와 동일한 방식으로 방어 코드를 추가해 주세요.
호스트 정보 수정 페이지
Summary by CodeRabbit
신규 기능
버그 수정
리팩터
문서화