Skip to content

feat: 피드 메인페이지 - 내 띱 목록 API 연동#104

Merged
heeeeyong merged 6 commits into
developfrom
feat/api-auth
Aug 13, 2025
Merged

feat: 피드 메인페이지 - 내 띱 목록 API 연동#104
heeeeyong merged 6 commits into
developfrom
feat/api-auth

Conversation

@heeeeyong
Copy link
Copy Markdown
Collaborator

@heeeeyong heeeeyong commented Aug 13, 2025

#️⃣연관된 이슈

#71 [API] feeds API 연동

📝작업 내용

  1. 피드 메인페이지 - 내 띱 목록 구현
image image
2. 사용자 검색 시, 엔터/돋보기 버튼을 눌렀을 때 isFinalized = true 쿼리파라미터 전송 (최근검색어 저장을 위한 변수) image image

💬리뷰 요구사항

특별히 없습니다!

Summary by CodeRabbit

  • New Features
    • 최근 검색 및 최근 작성자 조회 API가 추가되어 팔로우 리스트가 실제 최근 작성자 데이터를 불러옵니다.
    • 피드와 닉네임 화면에서 소셜 로그인 토큰 발급 흐름이 자동으로 실행됩니다.
  • Improvements
    • 사용자 검색이 “검색 확정(isFinalized)” 상태를 반영하도록 동작이 정교화되었습니다.
  • Bug Fixes
    • 프로필 아이템에서 팔로워 수가 없을 때 0으로 안정적으로 표시됩니다.
  • Style
    • BookInfoCard의 너비가 조정되어 카드 레이아웃과 말줄임 처리 일관성이 향상되었습니다.

@vercel
Copy link
Copy Markdown

vercel Bot commented Aug 13, 2025

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

Project Deployment Preview Comments Updated (UTC)
thip Ready Preview Comment Aug 13, 2025 4:00pm

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Aug 13, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

최근 검색·최근 팔로잉 API 모듈을 추가하고, 피드 상단 팔로우 리스트를 API 연동으로 교체했습니다. 사용자 검색에 isFinalized 옵션을 전파하고, OAuth 토큰 발급 훅 및 일부 회원가입·앱 초기화 관련 인증 로직을 단순화했습니다.

Changes

Cohort / File(s) Summary
Recent Search API
src/api/recentsearch/getRecentSearch.ts
SearchType, RecentSearchData, GetRecentSearchResponse 타입과 getRecentSearch(type) 함수 추가. /recent-search?type=<type> GET 후 response.data 반환.
Recent Following API
src/api/users/getRecentFollowing.ts
RecentWriterData, GetRecentFollowingResponse 타입과 getRecentFollowing() 함수 추가. /users/my-followings/recent-feeds 호출.
FollowList UI
src/components/feed/FollowList.tsx
하드코딩 데이터를 제거하고 getRecentFollowing로 recentWriters를 로드하는 비동기 로직 추가(로딩/빈 상태 처리, 클릭 내비게이션, 경로 /feed/search 변경). 타입 RecentWriterData 사용.
User Search (API + Hook + Page)
src/api/users/getUsers.ts, src/hooks/useUserSearch.ts, src/pages/feed/UserSearch.tsx
getUsers 파라미터에 isFinalized?: boolean 추가. useUserSearch가 isFinalized 옵션 수용 및 전달. UserSearch에서 isFinalized: isSearched로 훅 호출 업데이트.
Layout / Profile UI
src/components/feed/BookInfoCard.tsx, src/components/feed/UserProfileItem.tsx
BookInfoCard에 고정/제약된 폭(min/max/width) 추가. UserProfileItem에서 followerCount를 followerCount ?? 0으로 기본값 처리.
API client simplification
src/api/index.ts
TokenManager 및 토큰 주입/인터셉터 제거, default export 제거, apiClient를 named export로 간소화(사전 토큰/에러 처리 삭제, withCredentials: true 추가).
OAuth token hook & usage
src/hooks/useOAuthToken.ts, src/pages/feed/Feed.tsx, src/pages/signup/SignupNickname.tsx
로그인 토큰 발급 흐름을 처리하는 useOAuthToken 훅 추가(쿼리 loginTokenKey 확인 → POST /oauth-success → URL 정리 또는 루트 이동). Feed 및 SignupNickname에서 훅 호출 추가.
Signup genre flow
src/pages/signup/SignupGenre.tsx
클라이언트 사이드 쿠키/Authorization 직접 처리 제거; 닉네임 유무 검사(on load) 추가; postSignup 호출에서 헤더 조작 제거 및 로깅 정리.
App container change
src/App.tsx
CookiesProvider 래퍼 제거(프래그먼트로 변경).

Sequence Diagram(s)

sequenceDiagram
  participant UI as FollowList
  participant API as getRecentFollowing
  participant HTTP as apiClient
  participant BE as Backend

  UI->>API: getRecentFollowing()
  API->>HTTP: GET /users/my-followings/recent-feeds
  HTTP->>BE: Request
  BE-->>HTTP: Response { recentWriters[] }
  HTTP-->>API: data
  API-->>UI: recentWriters[]
  UI->>UI: setState(loading=false, recentWriters)
  alt Empty
    UI->>User: Show "독자를 찾아보세요"
    User->>UI: Click "찾기"
    UI->>Router: navigate('/feed/search')
  else Item click
    User->>UI: Click writer
    UI->>Router: navigate(`/profile/${userId}`)
  end
Loading
sequenceDiagram
  participant Page as UserSearch
  participant Hook as useUserSearch
  participant API as getUsers
  participant HTTP as apiClient
  participant BE as Backend

  Page->>Hook: useUserSearch({ keyword, size, isFinalized })
  Hook->>API: getUsers({ keyword, size, isFinalized })
  API->>HTTP: GET /users?...&isFinalized=<bool>
  HTTP->>BE: Request
  BE-->>HTTP: Response { users... }
  HTTP-->>API: data
  API-->>Hook: data
  Hook->>Hook: setResults(data)
  Hook-->>Page: results
  Page->>Page: render(results)
Loading
sequenceDiagram
  participant Page as Feed/SignupNickname
  participant Hook as useOAuthToken
  participant HTTP as apiClient
  participant BE as Backend
  participant Router as Browser

  Page->>Hook: mount -> useOAuthToken()
  Hook->>Hook: read loginTokenKey from URL
  alt loginTokenKey present & not requested
    Hook->>HTTP: POST /oauth-success { loginTokenKey } (withCredentials)
    HTTP->>BE: Request
    BE-->>HTTP: Response (token set via cookie)
    HTTP-->>Hook: success
    Hook->>Router: replace URL without loginTokenKey
  else failure
    Hook->>Router: navigate('/')
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

  • [API] feeds API 연동 #71: FollowList를 실 API 데이터로 교체해 피드 상단 팔로우 리스트 통합 목표와 직접적으로 맞물립니다.

Possibly related PRs

Suggested reviewers

  • ho0010
  • ljh130334

Poem

토끼가 폴짝, 코드 밭을 누비네 🥕
새 검색 추가하고 팔로우도 맞이하네
토큰은 살짝 다듬고, 훅은 살며시 달려가
카드 폭은 가지런히, 이름은 빈틈없이
데이터 따라 춤추며, 배포길로 뛰어간다


📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 654d9c4 and 52cc5dc.

📒 Files selected for processing (7)
  • src/App.tsx (1 hunks)
  • src/api/index.ts (1 hunks)
  • src/hooks/useOAuthToken.ts (1 hunks)
  • src/hooks/useUserSearch.ts (3 hunks)
  • src/pages/feed/Feed.tsx (2 hunks)
  • src/pages/signup/SignupGenre.tsx (2 hunks)
  • src/pages/signup/SignupNickname.tsx (2 hunks)
✨ 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-auth

🪧 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.
    • 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.
  • 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 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/Issue comments)

Type @coderabbitai help to get the list of available commands.

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

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • 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.

@heeeeyong heeeeyong self-assigned this Aug 13, 2025
Copy link
Copy Markdown

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

🧹 Nitpick comments (5)
src/components/feed/UserProfileItem.tsx (1)

58-61: null/undefined 대비 기본값 처리 좋습니다. 숫자 포맷·접근성 소소 개선 제안

기본값 0 처리로 UI 누락을 방지한 점은 👍. 가독성과 접근성을 위해 아래 두 가지를 함께 적용하면 더 좋겠습니다.

  • 큰 숫자 가독성: 천 단위 구분(1,234 등)
  • 아이콘 대체 텍스트 추가

적용 예시는 아래 diff 참고하세요.

-            <div>{followerCount ?? 0}명이 띱하는 중</div>
+            <div>{(followerCount ?? 0).toLocaleString('ko-KR')}명이 띱하는 중</div>
-            <img src={rightArrow} />
+            <img src={rightArrow} alt="팔로워 목록 이동" />
src/components/feed/BookInfoCard.tsx (1)

20-27: 고정 폭 대신 유연한 레이아웃으로 텍스트 잘림/오버플로우 리스크 완화

.left를 width: 220px, .right .name을 width: 100px로 고정하면 번역 길이/사용자 환경에 따라 우측 영역이 과도하게 잘리거나 레이아웃이 깨질 수 있습니다. flex 기반으로 좌측은 가변, 우측은 축소되지 않도록 하는 편이 안전합니다.

   .left {
     overflow: hidden;
-    width: 220px;
+    flex: 1;
+    min-width: 0; /* ellipsis 동작을 위한 최소값 */
     white-space: nowrap;
     color: var(--color-white);
     text-overflow: ellipsis;
     font-size: var(--font-size-base);
     font-weight: var(--font-weight-semibold);
     line-height: 24px;
   }
@@
     .name {
-      width: 100px;
+      max-width: 120px;  /* 환경에 맞게 적절히 조정 */
+      flex-shrink: 0;    /* 우측 이름이 과도하게 줄어들지 않도록 */
       overflow: hidden;
       text-overflow: ellipsis;
       white-space: nowrap;
     }

Also applies to: 42-47

src/api/recentsearch/getRecentSearch.ts (1)

23-26: 쿼리 파라미터 인코딩 및 반환 타입 명시로 견고성 향상

type은 제한적이지만, 습관적으로 인코딩을 적용하면 안전합니다. 또한 반환 타입을 명시해 호출부에서 타입 추론을 더 확실히 할 수 있습니다.

-export const getRecentSearch = async (type: SearchType) => {
-  const response = await apiClient.get<GetRecentSearchResponse>(`/recent-search?type=${type}`);
+export const getRecentSearch = async (type: SearchType): Promise<GetRecentSearchResponse> => {
+  const response = await apiClient.get<GetRecentSearchResponse>(`/recent-search?type=${encodeURIComponent(type)}`);
   return response.data;
 };
src/api/users/getRecentFollowing.ts (1)

21-26: 에러 처리 전략을 개선해 보세요.

현재 함수는 에러를 호출자에게 전파하도록 되어 있는데, API 호출 실패 시 더 구체적인 에러 처리나 로깅을 추가하는 것을 고려해 보세요.

다음과 같이 에러 처리를 개선할 수 있습니다:

 export const getRecentFollowing = async () => {
-  const response = await apiClient.get<GetRecentFollowingResponse>(
-    '/users/my-followings/recent-feeds',
-  );
-  return response.data;
+  try {
+    const response = await apiClient.get<GetRecentFollowingResponse>(
+      '/users/my-followings/recent-feeds',
+    );
+    return response.data;
+  } catch (error) {
+    console.error('최근 팔로잉 조회 API 호출 실패:', error);
+    throw error;
+  }
 };
src/components/feed/FollowList.tsx (1)

61-63: 로딩 상태 UI 개선이 필요합니다.

현재 로딩 상태에서 아무것도 렌더링하지 않고 있는데, 사용자 경험을 위해 로딩 인디케이터를 표시하는 것이 좋겠습니다.

로딩 상태 UI를 개선해 보세요:

-      {loading ? (
-        <></>
-      ) : hasFollowers ? (
+      {loading ? (
+        <LoadingContainer>
+          <div>로딩 중...</div>
+        </LoadingContainer>
+      ) : hasFollowers ? (

그리고 스타일 컴포넌트도 추가해야 합니다:

const LoadingContainer = styled.div`
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 20px;
  color: var(--color-grey-100);
  font-size: var(--font-size-xs);
`;
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 007a16c and 654d9c4.

📒 Files selected for processing (8)
  • src/api/recentsearch/getRecentSearch.ts (1 hunks)
  • src/api/users/getRecentFollowing.ts (1 hunks)
  • src/api/users/getUsers.ts (2 hunks)
  • src/components/feed/BookInfoCard.tsx (3 hunks)
  • src/components/feed/FollowList.tsx (2 hunks)
  • src/components/feed/UserProfileItem.tsx (1 hunks)
  • src/hooks/useUserSearch.ts (3 hunks)
  • src/pages/feed/UserSearch.tsx (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
src/api/recentsearch/getRecentSearch.ts (1)
src/api/index.ts (1)
  • apiClient (27-33)
src/api/users/getRecentFollowing.ts (1)
src/api/index.ts (1)
  • apiClient (27-33)
src/components/feed/FollowList.tsx (1)
src/api/users/getRecentFollowing.ts (2)
  • RecentWriterData (4-8)
  • getRecentFollowing (21-26)
🔇 Additional comments (18)
src/components/feed/BookInfoCard.tsx (1)

9-11: min/max-width 추가는 모바일 가로폭 제약 내에서 안전

컨테이너 min-width: 280px, max-width: 500px 설정은 카드의 가로 영역을 안정화하는 데 도움이 됩니다. 위 유연 레이아웃 제안과 함께 사용하면 다양한 화면에서 텍스트 오버플로우를 보다 자연스럽게 처리할 수 있습니다.

src/api/recentsearch/getRecentSearch.ts (1)

3-20: 타입 정의와 응답 스키마 일관성 양호

SearchType, RecentSearchData, GetRecentSearchResponse의 구조가 명확하고, 기존 apiClient 패턴과도 일관됩니다.

src/api/users/getUsers.ts (2)

38-41: isFinalized 쿼리 전송 조건 확인 요청

현재 구현은 false인 경우에도 isFinalized=false를 항상 전송합니다. PR 목적은 “엔터/돋보기 검색 시 true 전송”인데, 입력 중(isSearched=false)에도 false가 붙는 동작이 백엔드 계약과 부합하는지 확인이 필요합니다. 서버가 파라미터 미지정과 false를 동일하게 처리한다면, 호출부에서 undefined일 때는 파라미터를 생략하도록 조정하는 편이 더 안전할 수 있습니다.

백엔드와 아래 두 점을 확인해 주세요:

  • isFinalized=false를 명시적으로 보내는 것과 파라미터를 생략하는 것이 동등한가?
  • isFinalized는 'true'/'false' 문자열을 기대하는가(현재 toString으로 해당 형식 전송)?

필요 시, 호출부(UserSearch.tsx)에서 isSearched가 false일 때 isFinalized를 undefined로 넘겨 파라미터 자체를 생략하도록 바꾸는 것을 제안드립니다.


24-25: 파라미터 추가 자체는 적절

GetUsersParams에 isFinalized 추가는 다른 변경들과도 일관되고, 타입 정의가 명확합니다.

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

23-24: isFinalized 파라미터 생략 처리 검토 요청

현재 getUsers 호출 시 isFinalized: isSearched를 전달하면, isSearched === false일 때도 ?isFinalized=false가 항상 전송됩니다. 불필요한 파라미터 전송을 줄이고, “최종 검색 시에만 true”를 명확히 하기 위해 아래와 같이 수정할 것을 제안드립니다.

수정 대상:

  • 파일: src/pages/feed/UserSearch.tsx
  • 라인: 23–24
-    isFinalized: isSearched,
+    isFinalized: isSearched ? true : undefined,

백엔드에서 false를 명시적으로 보내야 하는 경우엔 현 상태를 유지해도 무방합니다. 해당 변경이 API 계약에 부합하는지 확인해주세요.

src/hooks/useUserSearch.ts (4)

9-9: 인터페이스에 새로운 속성이 올바르게 추가되었습니다.

isFinalized 옵션 속성이 적절하게 정의되어 검색 완료 상태를 나타낼 수 있습니다.


12-17: 훅 시그니처 및 기본값 처리가 적절합니다.

isFinalized 파라미터의 기본값을 false로 설정한 것은 기존 호출 코드와의 호환성을 보장하면서도 새로운 기능을 제공하는 좋은 방법입니다.


40-40: API 호출에 새 파라미터가 올바르게 전달되었습니다.

getUsers 함수에 isFinalized 파라미터를 전달하는 것이 적절하게 구현되었습니다.


59-59: 의존성 배열이 올바르게 업데이트되었습니다.

useCallback의 의존성 배열에 isFinalized를 추가한 것은 올바른 접근법입니다. 이를 통해 isFinalized 값이 변경될 때 함수가 재생성됩니다.

src/api/users/getRecentFollowing.ts (2)

4-8: 타입 정의가 명확하고 적절합니다.

RecentWriterData 인터페이스가 필요한 필드들을 잘 정의하고 있으며, 타입 안전성을 보장합니다.


11-18: 표준적인 API 응답 타입 구조를 따르고 있습니다.

GetRecentFollowingResponse 타입이 프로젝트의 다른 API 응답 타입들과 일관성 있는 구조(isSuccess, code, message, data)를 따르고 있어 좋습니다.

src/components/feed/FollowList.tsx (7)

3-3: 필요한 React 훅들이 적절하게 추가되었습니다.

API 연동을 위해 useStateuseEffect를 추가한 것이 적절합니다.


8-8: API 모듈 임포트가 올바릅니다.

새로 생성된 API 함수와 타입을 적절하게 임포트하고 있습니다.


12-13: 상태 관리가 적절하게 구현되었습니다.

recentWritersloading 상태를 적절한 초기값으로 설정하고 올바른 타입을 지정했습니다.


16-33: API 호출 함수의 에러 처리가 잘 구현되었습니다.

성공/실패 상황을 모두 적절히 처리하고 있으며, 에러 로깅도 포함되어 있어 디버깅에 도움이 됩니다. 로딩 상태 관리도 올바르게 구현되어 있습니다.


36-38: 컴포넌트 마운트 시 데이터 페칭이 적절합니다.

useEffect를 사용하여 컴포넌트가 마운트될 때 한 번만 데이터를 조회하도록 구현된 것이 올바릅니다.


66-70: API 데이터 매핑이 올바르게 구현되었습니다.

구조 분해 할당을 통해 필요한 필드들(userId, profileImageUrl, nickname)을 추출하고, 각각을 적절한 용도로 사용하고 있습니다. alt 속성 추가도 접근성 측면에서 좋은 개선입니다.


44-44: 라우트 /feed/search 정의 수동 확인 필요
자동화된 검색 스크립트로 해당 경로를 찾지 못했습니다.

  • src/App.tsx 또는 라우터 설정 파일(src/routes.tsx 등)에서 /feed/search가 실제로 등록되어 있는지 확인해 주세요.

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

Labels

✨ Feature 기능 개발

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant