Skip to content

feat: feeds api 구현#77

Merged
heeeeyong merged 21 commits intodevelopfrom
feat/api-feeds
Aug 6, 2025
Merged

feat: feeds api 구현#77
heeeeyong merged 21 commits intodevelopfrom
feat/api-feeds

Conversation

@heeeeyong
Copy link
Collaborator

@heeeeyong heeeeyong commented Aug 5, 2025

#️⃣연관된 이슈

#71 [API] feeds API 연동

📝작업 내용

  1. 전체 피드 탭 관련 API 연동 및 무한 스크롤 구현
  2. 내 피드 탭 관련 API 연동 및 무한 스크롤 구현
  3. 피드 저장
  4. 피드 글 상세 조회 페이지 API 연동
  5. 특정 사용자 피드 페이지 API 연동
  6. 내 띱 목록 및 특정 사용자 띱 목록 페이지 API 연동

위 작업 모두 마쳤고, 피드 삭제 / 좋아요, 전체 피드 상단에 있는 내 띱 목록 컴포넌트와 관련된 API 들은 백엔드에서 개발 완료되는대로 연동할 예정입니다.

스크린샷

image image image image image image image

💬리뷰 요구사항 & 해결해야할 사항

  1. TotalBar 반응형 문제
  2. 다른 사람의 띱 목록에서 내 프로필을 눌렀을 때, 어떻게 해결할 것인지? 내가 내 피드 상세 페이지로 이동한 경우에 팔로우 버튼을 없애야할 것 같은데 이 상황을 어떻게 해결해야할지 생각중입니다. 애초에 어떤방법으로 내가 나를 선택했는지 판단을 해야할지가 원초적인 고민입니다.
  3. 타입의 경우에 화면이 너무 많아서 대부분의 API들이 반복되는 타입을 공통적으로 사용하지 못하고 있는데 이 부분은 추후 리팩토링때 고려해야할 것 같습니다.
  4. 무한스크롤을 요구하는 화면이 몇개 있는데 custom hook처리를 할지 고민됩니다.

Summary by CodeRabbit

  • 신규 기능

    • 피드 상세 조회, 다른 유저 피드/프로필, 팔로워/팔로잉 목록, 팔로우/언팔로우, 피드 저장/해제, 내 프로필 조회 등 다양한 API 연동 기능이 추가되었습니다.
    • 댓글 작성 API가 도입되어 댓글 및 답글 작성 기능이 향상되었습니다.
    • 팔로워/팔로잉 목록 페이지가 무한 스크롤과 동적 데이터 로딩을 지원하도록 개선되었습니다.
    • 라우터에 팔로워 리스트 접근 경로가 추가되었습니다.
  • 기능 개선

    • 피드, 프로필, 팔로워/팔로잉 컴포넌트가 동적 데이터와 API 기반으로 리팩터링되었습니다.
    • 팔로우/언팔로우, 피드 저장/해제 등 주요 액션이 서버와 연동되어 실시간 반영됩니다.
    • 피드, 댓글, 답글 등에서 aliasColor로 색상 prop 명칭이 일관화되었습니다.
    • 피드 목록에서 마지막 포스트의 하단 경계선이 사라집니다.
    • 피드 페이지에서 중복 포스트가 나타나지 않도록 개선되었습니다.
    • 무한 스크롤 시 중복 요청 및 무한 반복 방지 로직이 추가되었습니다.
    • 피드 상세 페이지가 URL 파라미터 기반 데이터 로딩으로 전환되어 동적 렌더링을 지원합니다.
    • 프로필 및 팔로워 컴포넌트가 API 응답에 따른 동적 상태로 변경되었습니다.
    • 사용자 프로필 및 팔로잉 관련 컴포넌트의 prop 명칭과 상태 관리가 개선되었습니다.
  • 스타일

    • 피드, 프로필, 팔로워/팔로잉 등 주요 컴포넌트의 레이아웃 및 스타일이 개선되었습니다.
    • 피드 페이지 컨테이너에 고정 높이와 배경색이 적용되었습니다.
    • 빈 상태 및 컨테이너의 최소 높이 및 패딩이 조정되었습니다.
  • 문서 및 메타데이터

    • 파비콘, 페이지 타이틀, 오픈그래프 메타태그가 브랜드에 맞게 변경되었습니다.
  • 기타

    • React StrictMode가 제거되어 앱이 일반 모드로 실행됩니다.
    • 타입 및 데이터 구조가 일관성 있게 정비되었습니다.

@vercel
Copy link

vercel bot commented Aug 5, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
thip ✅ Ready (Inspect) Visit Preview 💬 Add feedback Aug 6, 2025 4:43am

@coderabbitai
Copy link

coderabbitai bot commented Aug 5, 2025

Walkthrough

이 변경사항은 피드, 프로필, 팔로우/팔로워 등 주요 소셜 기능의 API 연동을 전면적으로 구현하고, 관련 컴포넌트와 타입을 대대적으로 리팩토링합니다. 정적 목업 데이터를 제거하고, 실제 API 호출을 통한 동적 데이터 로딩, 상태 관리, UI 반영이 이루어집니다. 또한, prop 및 타입 명명 규칙이 일관성 있게 정비되었습니다.

Changes

Cohort / File(s) Change Summary
피드 API 연동 및 상세 데이터
src/api/feeds/getFeedDetail.ts, src/api/feeds/getMyFeed.ts, src/api/feeds/getMyProfile.ts, src/api/feeds/getOtherFeed.ts, src/api/feeds/getTotalFeed.ts, src/api/feeds/postSave.ts
피드 상세, 내 피드, 내 프로필, 타인 피드, 전체 피드, 피드 저장/해제 등 다양한 피드 관련 API 모듈 신설 및 반환 타입 정비. 기존 일부 함수의 반환 타입 명시 제거.
팔로우/팔로워 API 연동
src/api/users/getFollowerList.ts, src/api/users/getFollowingList.ts, src/api/users/getOtherProfile.ts, src/api/users/postFollow.ts
팔로워/팔로잉 리스트, 타인 프로필, 팔로우/언팔로우 API 유틸 함수 및 타입 추가.
피드/프로필 타입 및 데이터 구조 정비
src/types/follow.ts, src/types/post.ts, src/types/profile.ts, src/types/user.ts, src/data/postData.ts, src/data/userData.ts
타입 및 목업 데이터에서 titleColor → aliasColor, userName → nickName 등 명명 일관화. isFollowed → isFollowing 등 필드명 통일. 새로운 인터페이스 추가.
피드 및 프로필 컴포넌트 리팩토링
src/components/feed/Profile.tsx, src/components/feed/MyFollower.tsx, src/components/feed/MyFeed.tsx, src/components/feed/OtherFeed.tsx, src/components/feed/FeedPost.tsx, src/components/feed/TotalFeed.tsx, src/components/feed/UserProfileItem.tsx
정적 데이터에서 API 기반 동적 데이터로 전환. prop 명 및 타입 정비, follow/언팔로우·피드 저장 등 액션에 API 연동 적용. isLast, isTotalFeed 등 prop 추가 및 스타일 조정.
피드 상세/댓글/헤더 컴포넌트 prop 정비
src/components/feed/FeedDetailPost.tsx, src/components/common/Post/PostFooter.tsx, src/components/common/Post/PostHeader.tsx, src/components/common/Post/Reply.tsx, src/components/common/Post/SubReply.tsx
aliasColor 등 prop 명 통일. FeedDetailData 기반으로 상세 피드 컴포넌트 구조 변경. 피드 저장 액션에 API 연동.
피드/팔로우 페이지 API 연동 및 무한스크롤
src/pages/feed/Feed.tsx, src/pages/feed/FeedDetailPage.tsx, src/pages/feed/FollowerListPage.tsx, src/pages/feed/OtherFeedPage.tsx
기존 목업 데이터 제거, API 기반 데이터 로딩, 무한스크롤 및 중복 제거, 에러/로딩 상태 처리, prop 전달 방식 변경 등 대규모 리팩토링.
라우팅 및 기타
src/pages/index.tsx, index.html, src/main.tsx
팔로워/팔로잉 상세 페이지 라우트 추가, HTML 메타데이터(파비콘, OG 태그) 및 타이틀 변경, StrictMode 제거 등 부수적 변경.

Sequence Diagram(s)

피드 상세 조회 및 저장/해제

sequenceDiagram
    participant User
    participant FeedDetailPage
    participant getFeedDetail API
    participant postSaveFeed API

    User->>FeedDetailPage: 피드 상세 진입 (feedId)
    FeedDetailPage->>getFeedDetail API: GET /feeds/{feedId}
    getFeedDetail API-->>FeedDetailPage: FeedDetailData 반환
    FeedDetailPage-->>User: 피드 상세 렌더링

    User->>FeedDetailPage: 저장/해제 버튼 클릭
    FeedDetailPage->>postSaveFeed API: POST /feeds/{feedId}/saved (isSaved)
    postSaveFeed API-->>FeedDetailPage: 저장 상태 응답
    FeedDetailPage-->>User: 저장 상태 UI 반영
Loading

팔로우/언팔로우 액션

sequenceDiagram
    participant User
    participant Profile
    participant postFollow API

    User->>Profile: 팔로우/언팔로우 버튼 클릭
    Profile->>postFollow API: POST /users/following/{userId} (type)
    postFollow API-->>Profile: isFollowing 상태 응답
    Profile-->>User: 팔로우 상태 UI 갱신
Loading

팔로워/팔로잉 리스트 무한스크롤

sequenceDiagram
    participant User
    participant FollowerListPage
    participant getFollowerList API

    User->>FollowerListPage: 팔로워/팔로잉 리스트 진입
    loop 스크롤 하단 도달
        FollowerListPage->>getFollowerList API: GET /users/{userId}/followers?cursor=...
        getFollowerList API-->>FollowerListPage: 팔로워 데이터 + nextCursor
        FollowerListPage-->>User: 리스트 추가 렌더링
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40분

Possibly related PRs

Suggested labels

✨ Feature

Poem

🐇
새로운 피드, 팔로우, 프로필,
API와 함께 춤을 추네.
목업은 안녕, 실데이터 반영!
닉네임, 별명, 색상까지
토끼는 오늘도 코드를 달려
"띱!"하며 기능을 완성해!
🥕✨

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/api-feeds

🪧 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.
  • 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.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

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 generate unit tests to generate unit tests for 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.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • 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

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

@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: 16

♻️ Duplicate comments (1)
src/api/feeds/getTotalFeed.ts (1)

24-24: 명시적 반환 타입 주석 제거에 대한 고려사항

getMyFeed.ts와 동일한 패턴으로 명시적 반환 타입이 제거되었습니다. API 함수의 타입 안전성과 가독성을 위해 명시적 반환 타입을 유지하는 것을 권장합니다.

-export const getTotalFeeds = async (params?: GetTotalFeedParams) => {
+export const getTotalFeeds = async (params?: GetTotalFeedParams): Promise<TotalFeedResponse> => {
🧹 Nitpick comments (24)
src/main.tsx (1)

5-5: StrictMode 제거 이유를 명확히 설명하거나 되돌리는 것을 고려하세요
개발 환경에서 React.StrictMode는 부작용 감지·경고 및 향후 React 기능 호환성 테스트에 유용합니다. 불필요한 API 중복 호출을 피하기 위해 의도적으로 제거한 것이라면 PR 설명에 그 배경을 명시해 주세요. 그렇지 않다면 다시 래핑하여 개발 시점 품질 보증을 유지하는 편이 좋습니다.

index.html (1)

2-2: lang="en"lang="ko" 전환 검토
페이지 주 내용이 한글이므로 언어 코드를 잘못 지정하면 스크린리더·검색엔진이 텍스트를 잘못 처리할 수 있습니다.

-<html lang="en">
+<html lang="ko">
src/types/follow.ts (1)

1-9: 타입 주석·불변성 선언을 고려해보세요
FollowData 인터페이스가 잘 정의돼 있습니다만, 다른 타입 정의 파일들(profile.ts 등)과의 일관성을 위해 /** ... */ JSDoc 주석으로 필드 의미를 명시하고, 변경 불가능한 모델임을 나타내기 위해 readonly 수식어를 붙이는 방안을 권장드립니다. 런타임에 값이 변경되지 않는다는 의도가 분명해져 유지보수성이 좋아집니다.

src/types/profile.ts (1)

8-10: 프로퍼티 명명 일관성 확인
latestFollowerProfileImageUrls 는 복수형 Urls 로 끝나고, 상위 필드들은 단수형입니다. 의도된 것이지만 길이가 상당히 길어 가독성이 떨어집니다.
예: latestFollowerThumbnails 처럼 의미가 통하면서도 짧은 대안을 검토해 보세요.

src/components/common/Post/SubReply.tsx (1)

32-35: liked 상태 참조로 인한 잠재적 불일치
setLikedsetCurrentLikeCount 가 같은 핸들러에서 순차 호출될 때, 두 번째 콜백에서 외부의 liked 값을 사용하면 비동기 업데이트 타이밍에 따라 의도와 다른 증감이 발생할 수 있습니다. 직접 prev 값만 이용하면 안전합니다.

const handleLike = () => {
-  setLiked(prev => !prev);
-  setCurrentLikeCount(prev => (liked ? prev - 1 : prev + 1));
+  setLiked(prevLiked => {
+    setCurrentLikeCount(prevCount => (prevLiked ? prevCount - 1 : prevCount + 1));
+    return !prevLiked;
+  });
};
src/components/common/Post/PostHeader.tsx (1)

36-37: 인라인 스타일 대신 styled-components 사용 권장
.usertitle 클래스에 이미 기본 색상이 선언돼 있는데, 인라인 스타일로 다시 덮어쓰면 우선순위가 뒤엉켜 디버깅이 어려워집니다. aliasColor 를 props 로 받아 styled-components 의 css 템플릿 리터럴에서 처리하면 재사용·테마화가 쉬워집니다.

-<div className="usertitle" style={{ color: aliasColor }}>
+<div className="usertitle">
.const Container = styled.div<{ type?: 'post' | 'reply'; aliasColor: string }>`
 ...
 .usertitle {
-  color: var(--color-character-pink);
+  color: ${({ aliasColor }) => aliasColor ?? 'var(--color-character-pink)'};

컴포넌트 호출부에서는 <Container aliasColor={aliasColor}> 로 전달하면 됩니다.

src/components/common/Post/Reply.tsx (1)

27-30: SubReply 와 동일한 Like 로직 중복 제거
handleLike 구현이 SubReply 와 동일하게 복제돼 있습니다. 훅(useLikeToggle)으로 추출하거나 공통 유틸로 분리하면 버그 수정·기능 추가 시 두 파일을 동시에 고칠 필요가 없어집니다.
또한 liked 외부 값을 참조하는 문제는 위 SubReply 와 같은 방식으로 수정 가능합니다.

src/api/feeds/getMyFeed.ts (1)

25-25: 명시적 반환 타입 주석 제거에 대한 고려사항

API 함수에서 명시적인 반환 타입을 제거했지만, 다음과 같은 이유로 명시적 타입 주석을 유지하는 것을 권장합니다:

  1. API 계약의 명확성: API 함수의 반환 타입을 명시적으로 표현하여 다른 개발자들이 함수의 의도를 쉽게 파악할 수 있습니다.
  2. 타입 안전성: 실수로 잘못된 타입을 반환하는 경우를 컴파일 시점에 잡을 수 있습니다.
  3. 문서화 효과: 코드 자체가 문서 역할을 합니다.
-export const getMyFeeds = async (params?: GetMyFeedParams) => {
+export const getMyFeeds = async (params?: GetMyFeedParams): Promise<MyFeedResponse> => {
src/api/users/getOtherProfile.ts (1)

12-15: 입력 검증과 에러 핸들링 개선을 고려해보세요.

API 함수의 구조는 깔끔하지만, 다음 개선사항들을 고려해보시기 바랍니다:

  1. userId 유효성 검증 (양수인지 확인)
  2. 더 명확한 에러 메시지 제공
 export const getOtherProfile = async (userId: number) => {
+  if (!userId || userId <= 0) {
+    throw new Error('유효하지 않은 사용자 ID입니다.');
+  }
+  
+  try {
     const response = await apiClient.get<OtherProfileResponse>(`/feeds/users/${userId}/info`);
     return response.data;
+  } catch (error) {
+    console.error('프로필 조회 실패:', error);
+    throw error;
+  }
 };
src/pages/feed/OtherFeedPage.tsx (1)

34-38: userId 유효성 검증을 강화하세요.

문자열을 숫자로 변환할 때 유효성 검증이 필요합니다.

       if (!userId) {
         setError('사용자 ID가 없습니다.');
         setLoading(false);
         return;
       }
+      
+      const numericUserId = Number(userId);
+      if (isNaN(numericUserId) || numericUserId <= 0) {
+        setError('유효하지 않은 사용자 ID입니다.');
+        setLoading(false);
+        return;
+      }
src/api/feeds/getOtherFeed.ts (1)

31-34: 입력 검증 추가를 고려하세요.

getOtherProfile과 동일한 패턴으로 입력 검증을 추가하는 것이 좋겠습니다.

 export const getOtherFeed = async (userId: number) => {
+  if (!userId || userId <= 0) {
+    throw new Error('유효하지 않은 사용자 ID입니다.');
+  }
+  
   const response = await apiClient.get<OtherFeedResponse>(`/feeds/users/${userId}`);
   return response.data;
 };
src/api/users/getFollowingList.ts (1)

23-26: size 파라미터 검증 로직을 개선하세요.

현재 size가 1~10 범위를 벗어나면 조용히 무시되는데, 이보다는 기본값을 명시적으로 설정하거나 에러를 던지는 것이 더 명확합니다.

   const size = params?.size || 10;
-  if (size >= 1 && size <= 10) {
-    queryParams.append('size', size.toString());
-  }
+  const validSize = Math.min(Math.max(size, 1), 10);
+  queryParams.append('size', validSize.toString());

또는 더 엄격한 검증을 원한다면:

   const size = params?.size || 10;
-  if (size >= 1 && size <= 10) {
-    queryParams.append('size', size.toString());
-  }
+  if (size < 1 || size > 10) {
+    throw new Error('size는 1~10 사이의 값이어야 합니다.');
+  }
+  queryParams.append('size', size.toString());
src/api/feeds/getFeedDetail.ts (1)

38-43: 주석 예제가 유용합니다.

함수 사용법을 보여주는 예제 코드가 개발자들에게 도움이 될 것 같습니다. 다만 실제 사용 시에는 이 주석을 제거하는 것을 권장합니다.

src/api/users/getFollowerList.ts (1)

32-32: URL 구성 로직을 개선할 수 있습니다.

현재 방식도 작동하지만, 더 간결하게 작성할 수 있습니다.

다음과 같이 리팩토링을 고려해보세요:

-  const url = `/users/${userId}/followers${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;
+  const queryString = queryParams.toString();
+  const url = `/users/${userId}/followers${queryString ? `?${queryString}` : ''}`;

또는 더 간단하게:

-  const url = `/users/${userId}/followers${queryParams.toString() ? `?${queryParams.toString()}` : ''}`;
+  const url = `/users/${userId}/followers?${queryParams.toString()}`;
src/api/users/postFollow.ts (1)

16-20: 사용되지 않는 타입 정의

FollowErrorResponse 인터페이스가 정의되어 있지만 실제로 사용되지 않고 있습니다. 에러 처리에 활용하거나 불필요하다면 제거하는 것이 좋겠습니다.

src/api/feeds/postSave.ts (1)

32-50: 사용 예제를 JSDoc으로 이동 검토

사용 예제가 유용하지만, 주석 블록보다는 JSDoc 형식으로 함수 위에 배치하는 것이 더 표준적입니다.

-// 피드 저장/저장 취소 API 함수
+/**
+ * 피드 저장/저장 취소 API 함수
+ * @param feedId - 저장할 피드 ID
+ * @param shouldSave - true: 저장, false: 저장 취소
+ * @returns 저장 결과
+ * @example
+ * // 피드 저장
+ * const saveResult = await postSaveFeed(123, true);
+ * // 피드 저장 취소
+ * const unsaveResult = await postSaveFeed(123, false);
+ */
 export const postSaveFeed = async (feedId: number, shouldSave: boolean) => {
src/pages/feed/FollowerListPage.tsx (2)

80-81: 주석 처리된 코드 제거

사용하지 않는 주석 처리된 코드는 제거하는 것이 좋습니다. 필요하다면 TODO 주석으로 의도를 명확히 하세요.

-        // setTotalCount(prev => prev + userData.length);

82-86: 재시도 로직 개선 필요

재시도 횟수만 증가시키고 실제 재시도는 구현되지 않았습니다. 자동 재시도나 사용자 액션을 통한 재시도 기능을 추가하면 좋겠습니다.

재시도 로직을 구현하는 코드를 생성해드릴까요? 지수 백오프를 사용한 자동 재시도나 수동 재시도 버튼을 추가할 수 있습니다.

src/components/feed/MyFeed.tsx (2)

34-36: 로딩 중 UI 개선 필요

로딩 중에 빈 Fragment를 반환하면 레이아웃이 갑자기 변경될 수 있습니다. 스켈레톤 UI나 로딩 인디케이터를 표시하는 것이 사용자 경험에 더 좋습니다.

   if (loading || !profileData) {
-    return <></>;
+    return (
+      <Container>
+        <div>프로필 정보를 불러오는 중...</div>
+      </Container>
+    );
   }

52-53: 복합 키 생성 방식 재검토

feedId와 인덱스를 조합한 키는 같은 feedId가 여러 번 나타날 경우 문제가 될 수 있습니다. feedId가 고유하다면 인덱스를 추가할 필요가 없습니다.

-            key={`${post.feedId}-${index}`}
+            key={post.feedId}
src/components/feed/OtherFeed.tsx (1)

20-22: 로딩 상태 UI 개선 고려

MyFeed와 동일하게 빈 Fragment 대신 로딩 인디케이터나 스켈레톤 UI를 표시하는 것이 좋겠습니다.

   if (!profileData) {
-    return <></>;
+    return (
+      <Container>
+        <div>프로필 정보를 불러오는 중...</div>
+      </Container>
+    );
   }
src/components/feed/MyFollower.tsx (1)

84-91: 팔로워 이미지 렌더링 로직이 적절하지만 에러 처리 개선을 권장합니다.

조건부 렌더링과 이미지 개수 제한(최대 5개)이 잘 구현되어 있습니다. 다만 이미지 로딩 실패에 대한 처리를 추가하는 것을 고려해보세요.

-            <img className="profileImg" key={index} src={imageUrl} alt="나를 띱한 유저들" />
+            <img 
+              className="profileImg" 
+              key={index} 
+              src={imageUrl} 
+              alt="나를 띱한 유저들"
+              onError={(e) => {
+                e.currentTarget.src = '/default-profile.png'; // 기본 이미지로 대체
+              }}
+            />
src/components/feed/UserProfileItem.tsx (1)

27-38: 비동기 팔로우 토글 로직이 잘 구현되었지만 로딩 상태 추가를 권장합니다.

API 호출과 에러 처리가 적절하게 구현되어 있습니다. 다만 API 호출 중 중복 클릭을 방지하기 위한 로딩 상태 추가를 고려해보세요.

const UserProfileItem = ({ ... }: UserProfileItemProps) => {
  const navigate = useNavigate();
  const [followed, setFollowed] = useState(isFollowing);
+ const [isLoading, setIsLoading] = useState(false);

  const toggleFollow = async (e: React.MouseEvent) => {
    e.stopPropagation();
+   
+   if (isLoading) return;
+   setIsLoading(true);

    try {
      const response = await postFollow(userId, !followed);
      setFollowed(response.data.isFollowing);
      console.log(`${nickName} - ${response.data.isFollowing ? '띱 완료' : '띱 취소'}`);
    } catch (error) {
      console.error('팔로우/언팔로우 실패:', error);
+   } finally {
+     setIsLoading(false);
    }
  };
src/components/feed/Profile.tsx (1)

35-57: 비동기 팔로우 토글 로직이 잘 구현되었지만 개선사항이 있습니다.

API 호출 로직과 에러 처리가 적절하게 구현되어 있습니다. 다만 다음 개선사항을 고려해보세요:

  1. 프로덕션 환경에서는 console.log 제거
  2. 로딩 상태 추가로 중복 클릭 방지
const Profile = ({ ... }: ProfileProps) => {
  const [followed, setFollowed] = useState(isFollowing);
+ const [isLoading, setIsLoading] = useState(false);

  const toggleFollow = async () => {
    if (!userId) {
-     console.error('userId가 없습니다.');
      return;
    }

+   if (isLoading) return;
+   setIsLoading(true);

    try {
-     console.log('현재 팔로우 상태:', followed);
-     console.log('요청할 타입:', !followed);
      const response = await postFollow(userId, !followed);
-     console.log('API 응답:', response);
      setFollowed(response.data.isFollowing);
-     console.log(`${nickname} - ${response.data.isFollowing ? '띱 완료' : '띱 취소'}`);
    } catch (error) {
      console.error('팔로우/언팔로우 실패:', error);
+   } finally {
+     setIsLoading(false);
    }
  };
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e4f7cd9 and cbb2fb3.

⛔ Files ignored due to path filters (2)
  • public/assets/custom_favicon.svg is excluded by !**/*.svg
  • public/assets/thumbnail.png is excluded by !**/*.png
📒 Files selected for processing (35)
  • index.html (1 hunks)
  • src/api/feeds/getFeedDetail.ts (1 hunks)
  • src/api/feeds/getMyFeed.ts (1 hunks)
  • src/api/feeds/getMyProfile.ts (1 hunks)
  • src/api/feeds/getOtherFeed.ts (1 hunks)
  • src/api/feeds/getTotalFeed.ts (1 hunks)
  • src/api/feeds/postSave.ts (1 hunks)
  • src/api/users/getFollowerList.ts (1 hunks)
  • src/api/users/getFollowingList.ts (1 hunks)
  • src/api/users/getOtherProfile.ts (1 hunks)
  • src/api/users/postFollow.ts (1 hunks)
  • src/components/common/Post/PostFooter.tsx (2 hunks)
  • src/components/common/Post/PostHeader.tsx (3 hunks)
  • src/components/common/Post/Reply.tsx (2 hunks)
  • src/components/common/Post/SubReply.tsx (2 hunks)
  • src/components/feed/FeedDetailPost.tsx (2 hunks)
  • src/components/feed/FeedPost.tsx (1 hunks)
  • src/components/feed/MyFeed.tsx (2 hunks)
  • src/components/feed/MyFollower.tsx (1 hunks)
  • src/components/feed/OtherFeed.tsx (1 hunks)
  • src/components/feed/Profile.tsx (2 hunks)
  • src/components/feed/TotalFeed.tsx (2 hunks)
  • src/components/feed/UserProfileItem.tsx (2 hunks)
  • src/data/postData.ts (4 hunks)
  • src/data/userData.ts (1 hunks)
  • src/main.tsx (1 hunks)
  • src/pages/feed/Feed.tsx (5 hunks)
  • src/pages/feed/FeedDetailPage.tsx (4 hunks)
  • src/pages/feed/FollowerListPage.tsx (2 hunks)
  • src/pages/feed/OtherFeedPage.tsx (2 hunks)
  • src/pages/index.tsx (1 hunks)
  • src/types/follow.ts (1 hunks)
  • src/types/post.ts (3 hunks)
  • src/types/profile.ts (1 hunks)
  • src/types/user.ts (1 hunks)
🔇 Additional comments (44)
src/pages/index.tsx (1)

65-65: 새로운 라우트 추가 승인

사용자별 팔로워/팔로잉 목록을 지원하기 위한 라우트 추가가 적절합니다. FollowerListPage 컴포넌트가 userId 파라미터 유무에 따라 올바르게 동작하도록 구현되어 있는지 확인해주세요.

src/data/postData.ts (1)

93-93: 속성명 표준화 승인

titleColor에서 aliasColor로의 속성명 변경이 일관성 있게 적용되었습니다. 더 명확한 의미를 전달하는 좋은 리팩토링입니다.

Also applies to: 108-108, 119-119, 133-133

src/data/userData.ts (1)

6-11: 사용자 데이터 속성명 표준화 승인

사용자 프로필 관련 속성들의 명명 규칙이 일관성 있게 표준화되었습니다:

  • userNamenickName: 더 명확한 의미
  • userTitlealiasName: 별칭의 의미를 정확히 표현
  • titleColoraliasColor: 별칭 색상을 명확히 표현
  • isFollowedisFollowing: 팔로우 상태를 능동적으로 표현

마지막 사용자 엔트리에 누락되었던 isFollowing 속성도 적절히 추가되었습니다.

Also applies to: 15-20, 24-29, 33-38

src/components/feed/FeedPost.tsx (2)

25-25: LGTM! 새로운 prop 추가가 적절합니다.

isLast prop 추가로 마지막 게시물을 구분할 수 있게 되었습니다. 함수 시그니처가 올바르게 업데이트되었습니다.


33-33: 조건부 렌더링이 올바르게 구현되었습니다.

마지막 게시물에서 하단 경계선을 제거하는 로직이 정확합니다. 이는 일반적인 피드 UX 패턴을 따르고 있습니다.

src/components/common/Post/PostFooter.tsx (1)

10-10: API 함수 임포트가 올바르게 추가되었습니다.

postSaveFeed API 함수 임포트가 프로젝트의 임포트 패턴을 따르고 있습니다.

src/types/post.ts (3)

24-25: 새로운 선택적 props가 올바르게 추가되었습니다.

isTotalFeedisLast props 추가로 피드 렌더링 제어가 가능해졌습니다. 선택적(optional) 타입으로 설정되어 기존 코드와의 호환성도 유지됩니다.


31-32: FeedPostProps 확장이 적절합니다.

FeedListProps와 일관성 있게 동일한 props가 추가되었습니다. 타입 안전성이 보장됩니다.


48-48: 속성명 변경이 일관성을 향상시킵니다.

titleColor에서 aliasColor로의 변경이 코드베이스 전반의 명명 일관성을 개선합니다. 의미가 더 명확해졌습니다.

Also applies to: 61-61

src/api/feeds/getMyProfile.ts (2)

1-16: API 함수 구현이 깔끔하고 일관성 있습니다.

프로젝트의 API 패턴을 잘 따르고 있으며, TypeScript 타입 정의가 적절합니다. 인터페이스 정의와 함수 구현이 명확합니다.


18-23: 사용 예시가 도움이 됩니다.

주석으로 제공된 사용 예시가 다른 개발자들의 이해를 돕습니다. 실제 사용법을 명확하게 보여주고 있습니다.

src/components/feed/TotalFeed.tsx (4)

7-7: Props 시그니처 업데이트가 올바릅니다.

isTotalFeedisLast props 추가로 컴포넌트의 렌더링 제어가 가능해졌습니다. 기본값 설정도 적절합니다.


14-22: 게시물 렌더링 로직이 잘 구현되었습니다.

인덱스를 포함한 키 생성과 조건부 isLast prop 전달이 올바르게 구현되었습니다. 마지막 게시물에서만 isLast가 true로 설정되는 로직이 정확합니다.


33-33: 스타일링 개선이 적절합니다.

min-height 사용으로 반응형 레이아웃이 개선되었고, 하단 패딩 조정으로 더 나은 사용자 경험을 제공합니다.

Also applies to: 35-35


45-45: 빈 상태 스타일링이 향상되었습니다.

상단 마진 추가로 빈 상태 메시지의 레이아웃이 개선되었습니다.

src/types/user.ts (1)

5-9: 프로퍼티 네이밍 개선이 잘 되었습니다.

nickName, aliasName, aliasColor, isFollowing으로의 변경이 코드의 가독성과 일관성을 크게 향상시킵니다. 특히 isFollowing은 능동적 의미로 더 명확하고, aliasColor의 optional 처리도 적절합니다.

src/pages/feed/OtherFeedPage.tsx (1)

44-47: API 호출 병렬 처리가 효율적입니다.

Promise.all을 사용한 병렬 API 호출로 성능을 최적화한 점이 훌륭합니다.

src/api/users/getFollowingList.ts (1)

32-36: URL 구성과 API 호출 로직이 깔끔합니다.

URLSearchParams를 사용한 쿼리 파라미터 처리와 조건부 URL 구성이 잘 구현되어 있습니다.

src/api/feeds/getFeedDetail.ts (2)

4-22: 인터페이스 구조가 잘 정의되었습니다.

피드 상세 정보에 필요한 모든 필드들이 적절히 포함되어 있고, 타입 정의도 명확합니다. 특히 isSaved, isLiked boolean 필드들로 사용자 상호작용 상태를 잘 표현했습니다.


33-36: API 함수 구현이 간결하고 명확합니다.

타입 안전성을 보장하면서도 간단한 구조로 잘 작성되었습니다. feedId를 URL 경로에 포함하는 RESTful API 패턴을 올바르게 따르고 있습니다.

src/pages/feed/Feed.tsx (4)

47-51: 중복 제거 로직이 잘 구현되었습니다.

무한 스크롤에서 중복 피드가 나타나는 문제를 해결하는 좋은 접근법입니다. Set을 사용한 중복 검사가 효율적이고, 새로운 피드만 추가하는 로직이 명확합니다.


149-159: isLast prop 전달이 적절합니다.

하위 컴포넌트에서 마지막 페이지 상태를 알 수 있도록 isLast prop을 전달하는 것이 좋은 설계입니다. UI 렌더링에서 경계선 처리 등에 유용할 것 같습니다.


166-172: 컨테이너 스타일링 개선되었습니다.

고정 높이(100vh)와 배경색 설정으로 더 일관된 레이아웃을 제공합니다. 최소/최대 너비 설정도 반응형 디자인에 도움이 됩니다.


74-74: Feed.tsx — 전체 및 내 피드 모두 중복 제거 로직 미구현
코드 확인 결과 loadTotalFeedsloadMyFeeds 모두

setTotalFeedPosts(prev => [...prev, ...response.data.feedList]);
setMyFeedPosts(prev    => [...prev, ...response.data.feedList]);

방식으로 페이징된 데이터를 단순 병합만 하고 있어 중복 제거가 적용되어 있지 않습니다.
일관성을 위해 전체 피드와 내 피드 모두에서 중복 제거 로직을 추가하는 방안을 검토해주세요.

Likely an incorrect or invalid review comment.

src/components/feed/FeedDetailPost.tsx (3)

25-27: 타입 확장이 적절합니다.

FeedDetailData를 확장하여 추가 props를 정의한 방식이 좋습니다. 타입 안전성을 유지하면서 컴포넌트별로 필요한 props를 명확히 정의했습니다.


29-29: props 구조분해가 명확합니다.

tagListaliasColor를 명시적으로 추출하고 나머지를 spread로 처리한 방식이 깔끔합니다. 각 하위 컴포넌트에 적절한 props를 전달할 수 있게 됩니다.


33-34: prop 이름 변경이 일관성을 개선합니다.

aliasColor를 PostHeader에 전달하고, tagListtags로 변환하여 전달하는 것이 각 컴포넌트의 인터페이스와 잘 맞습니다. prop 이름의 일관성이 향상되었습니다.

src/pages/feed/FeedDetailPage.tsx (3)

27-49: 비동기 데이터 로딩이 잘 구현되었습니다.

에러 처리, 로딩 상태 관리, 그리고 feedId 유효성 검사가 모두 포함된 견고한 구현입니다. Number(feedId) 변환도 적절합니다.


79-102: 사용자 경험을 고려한 상태 처리입니다.

로딩과 에러 상태에 대한 명확한 메시지를 표시하여 사용자에게 현재 상황을 알려주는 좋은 UX 설계입니다. 일관된 레이아웃 구조도 유지하고 있습니다.


118-118: 타입 변환이 적절합니다.

feedId를 string에서 number로 변환하여 API 호출에 사용하는 것이 올바릅니다. 라우트 매개변수는 항상 string이므로 이런 변환이 필요합니다.

src/api/users/getFollowerList.ts (1)

23-26: 매개변수 유효성 검사가 좋습니다.

size 매개변수의 범위를 1~10으로 제한하는 유효성 검사가 적절합니다. API 오남용을 방지하고 성능을 보호하는 좋은 방법입니다.

src/components/feed/MyFollower.tsx (4)

56-60: 인터페이스 정의가 적절합니다.

타입 정의가 명확하고, 선택적 속성들이 컴포넌트의 유연성을 잘 지원합니다.


62-66: Props 구조분해와 기본값이 적절합니다.

기본값 설정으로 props가 누락되어도 컴포넌트가 안전하게 동작할 수 있습니다.


69-73: 조건부 네비게이션 처리가 적절합니다.

userId가 없을 때의 안전 처리가 잘 구현되어 있어 잘못된 라우팅을 방지합니다.


80-80: 동적 팔로워 수 표시가 적절합니다.

Props에서 받은 데이터를 올바르게 표시하고 있습니다.

src/components/feed/UserProfileItem.tsx (4)

7-7: API 임포트가 적절합니다.

새로운 postFollow API 임포트가 올바르게 추가되었습니다.


21-21: 팔로우 상태 초기화가 적절합니다.

Props에서 받은 isFollowing 값으로 로컬 상태를 올바르게 초기화했습니다.


46-48: JSX에서 변경된 props 사용이 적절합니다.

이름이 변경된 props들이 올바르게 사용되고 있으며, 스타일 속성도 적절하게 적용되었습니다.


9-19: type prop 전달 확인 완료: 모든 사용처에서 명시적으로 지정하고 있습니다.

  • src/pages/feed/FollowerListPage.tsx: <UserProfileItem … type={type as UserProfileType} …/>
  • src/pages/feed/UserSearchResult.tsx: <UserProfileItem … type="followerlist" …/>

위 두 곳에서 모두 type을 전달하므로, 기본값 제거로 인한 문제는 없습니다. 이대로 머지해도 좋습니다.

src/components/feed/Profile.tsx (5)

1-1: 새로운 임포트가 적절합니다.

useEffectpostFollow API 임포트가 컴포넌트의 새로운 기능에 필요하며 올바르게 추가되었습니다.

Also applies to: 4-4


6-16: 인터페이스 정의가 동적 프로필 데이터를 잘 지원합니다.

새로운 props들이 명확하게 정의되어 있고, 일관된 네이밍 규칙을 따르고 있습니다.


18-28: Props 구조분해가 적절합니다.

모든 props가 올바르게 구조분해되었고, 필요한 곳에 기본값이 제공되었습니다.


29-33: Props와 로컬 상태 동기화가 적절합니다.

useEffect를 사용한 props 변경 감지와 로컬 상태 동기화가 올바르게 구현되었습니다.


63-63: 동적 데이터 사용으로 JSX가 올바르게 업데이트되었습니다.

정적 데이터에서 동적 props로의 전환이 모든 JSX 요소에서 올바르게 적용되었으며, MyFollower 컴포넌트에 필요한 props가 적절히 전달되고 있습니다.

Also applies to: 65-67, 77-81

Comment on lines +23 to +28
export interface OtherFeedResponse {
success: boolean;
code: number;
message: string;
data: OtherFeedData;
}
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

API 응답 타입의 일관성을 확인하세요.

OtherFeedResponse에서 success 필드를 사용하는 반면, getOtherProfile.tsOtherProfileResponse에서는 isSuccess를 사용합니다. 전체 앱에서 일관된 네이밍을 사용하는 것이 좋겠습니다.

다음 스크립트로 API 응답 타입의 일관성을 확인해보겠습니다:


🏁 Script executed:

#!/bin/bash
# API 응답 타입들에서 success 필드 네이밍 확인
echo "=== success 필드 사용 현황 ==="
rg -A 2 -B 2 "success.*boolean"
echo ""
echo "=== isSuccess 필드 사용 현황 ==="
rg -A 2 -B 2 "isSuccess.*boolean"

Length of output: 2766


API 응답 타입 네이밍 통일 필요

현재 코드베이스 전반에 걸쳐 successisSuccess 가 혼용되어 있습니다. 대부분의 피드 조회 API는 success 를, 일부 피드·사용자 관련 API는 isSuccess 를 사용 중이므로, 한 가지 형태로 통일해주세요.

다음 인터페이스의 필드명을 일관된 네이밍으로 변경해야 합니다:

  • src/api/feeds/postSave.ts (SaveFeedResponse: isSuccess)
  • src/api/feeds/getMyProfile.ts (MyProfileResponse: isSuccess)
  • src/api/users/getFollowerList.ts (FollowerListResponse: isSuccess)
  • src/api/users/getFollowingList.ts (FollowingListResponse: isSuccess)
  • src/api/users/getOtherProfile.ts (OtherProfileResponse: isSuccess)
  • src/api/users/postFollow.ts (FollowResponse: isSuccess)

예:

// 변경 전
export interface SaveFeedResponse {
  isSuccess: boolean;
  code: number;
  message: string;
}
// 변경 후 (피드 API 기준)
export interface SaveFeedResponse {
  success: boolean;
  code: number;
  message: string;
}

원하는 네이밍으로 통일한 뒤, 관련 함수 호출부와 문서 주석도 함께 업데이트해주세요.

🤖 Prompt for AI Agents
In src/api/feeds/postSave.ts, src/api/feeds/getMyProfile.ts,
src/api/users/getFollowerList.ts, src/api/users/getFollowingList.ts,
src/api/users/getOtherProfile.ts, and src/api/users/postFollow.ts around the
interface definitions for SaveFeedResponse, MyProfileResponse,
FollowerListResponse, FollowingListResponse, OtherProfileResponse, and
FollowResponse respectively, rename the boolean field from isSuccess to success
to unify the API response naming convention. After renaming, update all related
function calls, variable usages, and documentation comments to reflect this
change consistently across these files.

Comment on lines +3 to +6
// 요청 바디 타입
export interface SaveFeedRequest {
type: boolean; // true: 저장, false: 저장 취소
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

type 필드명 일관성 필요

postFollow.ts와 마찬가지로 boolean 타입의 type 필드명이 모호합니다. API 전반에 걸쳐 일관된 네이밍 컨벤션을 사용하는 것이 좋겠습니다.

 export interface SaveFeedRequest {
-  type: boolean; // true: 저장, false: 저장 취소
+  shouldSave: boolean; // true: 저장, false: 저장 취소
 }
📝 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
// 요청 바디 타입
export interface SaveFeedRequest {
type: boolean; // true: 저장, false: 저장 취소
}
// 요청 바디 타입
export interface SaveFeedRequest {
shouldSave: boolean; // true: 저장, false: 저장 취소
}
🤖 Prompt for AI Agents
In src/api/feeds/postSave.ts between lines 3 and 6, the boolean field named
'type' in the SaveFeedRequest interface is ambiguous and inconsistent with
naming conventions used elsewhere like in postFollow.ts. Rename the 'type' field
to a more descriptive and consistent name that clearly indicates its purpose,
such as 'isSaved' or 'saveStatus', and update all references to this field
accordingly to maintain consistency across the API.

Comment on lines +23 to +26
export const postSaveFeed = async (feedId: number, isSaved: boolean) => {
const requestBody: SaveFeedRequest = {
type: isSaved, // 현재 저장 상태로 변경
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

파라미터명과 주석이 혼란스럽습니다

isSaved 파라미터명과 "현재 저장 상태로 변경" 주석이 모호합니다. 이 값이 목표 상태인지 현재 상태인지 명확하지 않습니다.

-export const postSaveFeed = async (feedId: number, isSaved: boolean) => {
+export const postSaveFeed = async (feedId: number, shouldSave: boolean) => {
   const requestBody: SaveFeedRequest = {
-    type: isSaved, // 현재 저장 상태로 변경
+    type: shouldSave, // 원하는 저장 상태로 변경
   };
📝 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
export const postSaveFeed = async (feedId: number, isSaved: boolean) => {
const requestBody: SaveFeedRequest = {
type: isSaved, // 현재 저장 상태로 변경
};
export const postSaveFeed = async (feedId: number, shouldSave: boolean) => {
const requestBody: SaveFeedRequest = {
type: shouldSave, // 원하는 저장 상태로 변경
};
🤖 Prompt for AI Agents
In src/api/feeds/postSave.ts around lines 23 to 26, the parameter name 'isSaved'
and the comment "현재 저장 상태로 변경" are unclear about whether the value represents
the current or target saved state. Rename the parameter to clearly indicate it
is the target save state, such as 'shouldSave' or 'targetSaveState', and update
the comment to explicitly state that this value represents the desired save
status to set.

<App />
</StrictMode>,
);
createRoot(document.getElementById('root')!).render(<App />);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

document.getElementById('root')! 널 단언은 런타임 오류를 숨깁니다
! 연산자로 널 검사를 건너뛰면 실제로 DOM 노드가 없을 때도 타입 검사가 통과해 앱이 즉시 크래시 됩니다. 다음과 같이 방어 코드를 추가하는 것을 권장합니다.

-createRoot(document.getElementById('root')!).render(<App />);
+const container = document.getElementById('root');
+if (!container) {
+  throw new Error("Root element with id 'root' not found");
+}
+createRoot(container).render(<App />);
📝 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
createRoot(document.getElementById('root')!).render(<App />);
const container = document.getElementById('root');
if (!container) {
throw new Error("Root element with id 'root' not found");
}
createRoot(container).render(<App />);
🤖 Prompt for AI Agents
In src/main.tsx at line 5, avoid using the non-null assertion operator (!) on
document.getElementById('root') as it can cause runtime crashes if the element
is missing. Instead, add defensive code to check if the element exists before
calling createRoot and rendering the app, and handle the case where the element
is null gracefully.

import moreIcon from '../../assets/common/more.svg';
import ReplyList from '@/components/common/Post/ReplyList';
import { mockFeedPost, mockCommentList } from '@/data/postData';
import { mockCommentList } from '@/data/postData';
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

목업 데이터 의존성을 제거해주세요.

아직 mockCommentList를 사용하고 있습니다. 피드 데이터는 동적으로 로드하지만 댓글은 여전히 목업을 사용하고 있어 일관성이 부족합니다.

댓글 데이터도 동적으로 로드하도록 개선을 고려해주세요:

-import { mockCommentList } from '@/data/postData';
+import { getCommentList } from '@/api/comments/getCommentList';
📝 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
import { mockCommentList } from '@/data/postData';
-import { mockCommentList } from '@/data/postData';
+import { getCommentList } from '@/api/comments/getCommentList';
🤖 Prompt for AI Agents
In src/pages/feed/FeedDetailPage.tsx at line 9, the code imports mockCommentList
which is a static mock data for comments. To fix this, remove the import of
mockCommentList and replace the usage of this mock data with dynamic loading of
comment data, such as fetching comments from an API or state management, to
ensure consistency with how feed data is loaded.

Comment on lines +54 to +58
if (type === 'followerlist') {
userData = (response.data as { followers: FollowData[] })?.followers || [];
} else {
userData = (response.data as { followings: FollowData[] })?.followings || [];
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

타입 단언(Type Assertion) 사용 시 주의 필요

API 응답 데이터를 타입 단언으로 처리하고 있는데, 이는 런타임 오류로 이어질 수 있습니다. 타입 가드나 더 안전한 방식을 사용하는 것이 좋겠습니다.

-        let userData: FollowData[] = [];
-        if (type === 'followerlist') {
-          userData = (response.data as { followers: FollowData[] })?.followers || [];
-        } else {
-          userData = (response.data as { followings: FollowData[] })?.followings || [];
-        }
+        let userData: FollowData[] = [];
+        if (type === 'followerlist' && 'followers' in response.data) {
+          userData = response.data.followers || [];
+        } else if ('followings' in response.data) {
+          userData = response.data.followings || [];
+        }
📝 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
if (type === 'followerlist') {
userData = (response.data as { followers: FollowData[] })?.followers || [];
} else {
userData = (response.data as { followings: FollowData[] })?.followings || [];
}
let userData: FollowData[] = [];
if (type === 'followerlist' && 'followers' in response.data) {
userData = response.data.followers || [];
} else if ('followings' in response.data) {
userData = response.data.followings || [];
}
🤖 Prompt for AI Agents
In src/pages/feed/FollowerListPage.tsx around lines 54 to 58, the code uses type
assertion to cast API response data, which can cause runtime errors if the data
shape is unexpected. Replace the type assertions with proper type guards or
validation checks to safely verify the presence and structure of 'followers' or
'followings' in the response before assigning to userData, ensuring safer
handling of API responses.

Comment on lines +60 to +61
console.log('API 응답:', response);
console.log('추출된 사용자 데이터:', userData);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

프로덕션 코드에서 console.log 제거 필요

디버깅용 console.log가 남아있습니다. 프로덕션 빌드에서는 제거되어야 합니다.

-        console.log('API 응답:', response);
-        console.log('추출된 사용자 데이터:', userData);
+        // 디버깅이 필요한 경우 개발 환경에서만 로그 출력
+        if (process.env.NODE_ENV === 'development') {
+          console.log('API 응답:', response);
+          console.log('추출된 사용자 데이터:', userData);
+        }

Also applies to: 99-99

🤖 Prompt for AI Agents
In src/pages/feed/FollowerListPage.tsx at lines 60-61 and also line 99, remove
the console.log statements used for debugging to ensure no debug logs remain in
the production build. Replace them with proper logging if necessary or remove
them entirely.

Comment on lines +63 to +69
if (loading) {
return <></>;
}

if (error) {
return <></>;
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

로딩 및 에러 상태의 사용자 경험을 개선하세요.

현재 로딩과 에러 상태에서 빈 화면을 보여주고 있어 사용자가 혼란스러울 수 있습니다. 적절한 UI 컴포넌트를 표시하는 것이 좋겠습니다.

   if (loading) {
-    return <></>;
+    return (
+      <Container>
+        <TitleHeader
+          leftIcon={<img src={leftArrow} alt="뒤로가기" />}
+          onLeftClick={handleBackClick}
+        />
+        <div style={{ textAlign: 'center', padding: '50px 0' }}>
+          로딩 중...
+        </div>
+      </Container>
+    );
   }

   if (error) {
-    return <></>;
+    return (
+      <Container>
+        <TitleHeader
+          leftIcon={<img src={leftArrow} alt="뒤로가기" />}
+          onLeftClick={handleBackClick}
+        />
+        <div style={{ textAlign: 'center', padding: '50px 0' }}>
+          {error}
+        </div>
+      </Container>
+    );
   }
📝 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
if (loading) {
return <></>;
}
if (error) {
return <></>;
}
if (loading) {
return (
<Container>
<TitleHeader
leftIcon={<img src={leftArrow} alt="뒤로가기" />}
onLeftClick={handleBackClick}
/>
<div style={{ textAlign: 'center', padding: '50px 0' }}>
로딩 ...
</div>
</Container>
);
}
if (error) {
return (
<Container>
<TitleHeader
leftIcon={<img src={leftArrow} alt="뒤로가기" />}
onLeftClick={handleBackClick}
/>
<div style={{ textAlign: 'center', padding: '50px 0' }}>
{error}
</div>
</Container>
);
}
🤖 Prompt for AI Agents
In src/pages/feed/OtherFeedPage.tsx around lines 63 to 69, the current code
returns an empty fragment during loading and error states, which leads to a
confusing user experience. Replace these empty returns with meaningful UI
components such as a loading spinner or message during loading, and an error
message or retry option during error states to clearly communicate the app
status to users.

Copy link

@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: 1

🧹 Nitpick comments (1)
src/hooks/useReplyActions.ts (1)

35-36: API 호출 개선이 좋습니다.

전용 API 함수 사용으로 코드가 더 깔끔해졌습니다. 다만 프로덕션 환경에서는 응답 로깅을 제거하는 것을 고려해보세요.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b7b7ce8 and a03ff13.

📒 Files selected for processing (3)
  • src/api/comments/postReply.ts (1 hunks)
  • src/hooks/useReplyActions.ts (2 hunks)
  • src/pages/feed/FeedDetailPage.tsx (4 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/api/comments/postReply.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/pages/feed/FeedDetailPage.tsx
🔇 Additional comments (2)
src/hooks/useReplyActions.ts (2)

2-2: 좋은 아키텍처 개선입니다!

직접적인 axios 사용을 전용 API 함수로 교체한 것은 타입 안전성과 유지보수성을 향상시키는 좋은 변경사항입니다.


6-6: 타입 일관성 개선이 좋습니다.

API 함수와 일치하는 대문자 리터럴 타입 사용으로 타입 일관성이 개선되었습니다.

isReplyRequest: isReplying, // 답글 여부
parentId: isReplying ? parentId : null, // 답글이라면 부모 댓글 ID 포함
postType,
postType: postType.toUpperCase() as 'FEED' | 'RECORD' | 'VOTE',
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

불필요한 타입 변환을 제거하세요.

SubmitCommentProps 인터페이스에서 이미 postType을 대문자 리터럴로 정의했으므로, toUpperCase() 호출과 타입 단언이 불필요합니다.

다음과 같이 단순화할 수 있습니다:

-      postType: postType.toUpperCase() as 'FEED' | 'RECORD' | 'VOTE',
+      postType,
📝 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
postType: postType.toUpperCase() as 'FEED' | 'RECORD' | 'VOTE',
postType,
🤖 Prompt for AI Agents
In src/hooks/useReplyActions.ts at line 30, remove the unnecessary toUpperCase()
call and type assertion on postType since SubmitCommentProps already defines
postType as an uppercase literal type. Simply assign postType directly without
transformation or casting to simplify the code.

@heeeeyong heeeeyong changed the title Feat/api feeds feat: feeds api 구현 Aug 6, 2025
@ho0010
Copy link
Collaborator

ho0010 commented Aug 6, 2025

LGTM!

다른 사람의 띱 목록에서 내 프로필을 눌렀을 때, 어떻게 해결할 것인지? 내가 내 피드 상세 페이지로 이동한 경우에 팔로우 버튼을 없애야할 것 같은데 이 상황을 어떻게 해결해야할지 생각중입니다. 애초에 어떤방법으로 내가 나를 선택했는지 판단을 해야할지가 원초적인 고민입니다.

  • 피드 상세 페이지에서 분기 처리를 하는게 맞는 것 같아요 나 OR 타인으로요. 기본적으로 프로필 조회할때 회원 ID를 기반으로 할 것이라 예상됩니다. 때문에 이 ID가 내 ID라면 내 프로필을 보여주는 것으로 분기처리를 하면 좋을 것 같아요.

타입의 경우에 화면이 너무 많아서 대부분의 API들이 반복되는 타입을 공통적으로 사용하지 못하고 있는데 이 부분은 추후 리팩토링때 고려해야할 것 같습니다.

  • 동의합니다.

무한스크롤을 요구하는 화면이 몇개 있는데 custom hook처리를 할지 고민됩니다.

  • 저도 비슷한 생각을 했는데 custom hook 하나 만들어놓고 쓰는게 좋을 것 같기는 합니다.

@heeeeyong heeeeyong merged commit c7242cc into develop Aug 6, 2025
2 of 3 checks passed
Copy link
Member

@ljh130334 ljh130334 left a comment

Choose a reason for hiding this comment

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

다른 사람의 띱 목록에서 내 프로필을 눌렀을 때, 어떻게 해결할 것인지? 내가 내 피드 상세 페이지로 이동한 경우에 팔로우 버튼을 없애야할 것 같은데 이 상황을 어떻게 해결해야할지 생각중입니다. 애초에 어떤방법으로 내가 나를 선택했는지 판단을 해야할지가 원초적인 고민입니다.

저도 가장 일반적이고 확실한 해결 방법은 현재 로그인한 사용자 ID와 프로필 페이지의 사용자 ID를 비교하는 것이라고 생각합니다. useAuthStore에서 currentUserId를 가져와서 currentUserId === profileUserId로 비교하는 방식으로 하면 어떤 경로로 접근하든 확실하게 내 프로필인지 판단할 수 있을 것 같아요.
그리고 예외 상황도 고려하면 좋을 것 같아요. 로그인하지 않은 상태거나, 존재하지 않는 사용자 ID로 접근하는 경우 등?!

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants