Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEAT] 검색 결과 저장 #64

Merged
merged 24 commits into from
Nov 23, 2024
Merged

[FEAT] 검색 결과 저장 #64

merged 24 commits into from
Nov 23, 2024

Conversation

keemsebin
Copy link
Member

@keemsebin keemsebin commented Nov 22, 2024

관련 이슈

close #53

📑 작업 내용

  • 검색 저장 API 연결 하였습니다.
  • context를 사용하면 모든 컴포넌트가 리렌더링되는 이슈가 있어 jotai로 변경하였습니다.
  • bookMarkList , bookMarkDetail 컴포넌트 구현했습니다.
  • SearchResultsList, ExtractedPlacesList를 가능한 하나로 합칠 수 있으면 합칠 예정입니다.

💬 리뷰 중점 사항/기타 참고 사항

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능
    • jotai 라이브러리 추가로 상태 관리 기능 향상.
    • 새로운 BookmarkDetail, BookmarkSelectionList, SearchResultsList 컴포넌트 추가로 북마크 및 검색 결과 관리 개선.
    • 사용자 인터페이스 개선을 위한 Body 컴포넌트 사용.
    • useBottomFunnel 훅을 통해 사용자 인터랙션을 위한 하단 플로우 관리 기능 추가.
    • extractVideoId 유틸리티 함수 추가로 비디오 ID 추출 기능 개선.
    • getStoredMapDataclearMapStorage 함수 추가로 세션 저장소 관리 기능 향상.
  • 버그 수정
    • LinkInputLanding 컴포넌트의 마진 조정으로 UI 일관성 향상.
  • 리팩토링
    • 여러 컴포넌트의 상태 관리 로직을 Jotai로 전환하여 성능 향상.

@keemsebin keemsebin added the feat 기능 label Nov 22, 2024
@keemsebin keemsebin self-assigned this Nov 22, 2024
@keemsebin keemsebin requested a review from houony as a code owner November 22, 2024 11:51
Copy link

coderabbitai bot commented Nov 22, 2024

Walkthrough

이 변경 사항은 jotai라는 새로운 의존성을 추가하고, 여러 컴포넌트 및 훅을 수정하여 상태 관리 및 UI 구성 방식을 개선합니다. 주요 변경 사항으로는 BookmarkDetail, BookmarkList, SearchResultsList와 같은 새로운 컴포넌트의 추가와 함께, 기존 컴포넌트에서 컨텍스트 제공자를 제거하여 구조를 단순화했습니다. 또한, Axios 요청 메서드를 비동기적으로 변경하고, 여러 타입과 헬퍼 함수가 추가되었습니다.

Changes

파일 경로 변경 요약
.pnp.cjs jotai 의존성 추가 및 메타데이터 확장
package.json "jotai": "^2.10.3" 의존성 추가
src/App.tsx MapDataProviderMarkerProvider 제거
src/components/features/BookmarkDetail/BookmarkDetail.tsx 새로운 BookmarkDetail 컴포넌트 추가
src/components/features/BookmarkDetail/index.tsx BookmarkDetail 컴포넌트 내보내기 추가
src/components/features/BookmarkList/BookmarkList.tsx onNext prop 추가 및 UI 업데이트
src/components/features/BookmarkList/index.tsx BookmarkList 컴포넌트 내보내기 추가
src/components/features/BottomSheetContent/BottomSheetContent.tsx useBottomFunnel 훅 사용으로 로직 수정
src/components/features/BottomSheetContent/ExtractedPlaces.tsx ExtractedPlaces 컴포넌트 삭제
src/components/features/BottomSheetContent/SearchResult.tsx SearchResult 컴포넌트 삭제
src/components/features/BottomSheetContent/index.tsx ExtractedPlaces, SearchResult 내보내기 삭제
src/components/features/ExtractedPlacesList/ExtractedPlacesList.tsx ExtractedList 이름 변경 및 로직 수정
src/components/features/ExtractedPlacesList/index.tsx ExtractedPlacesList 내보내기 추가
src/components/features/LinkForm/ExtractionStatus.tsx extractVideoId 유틸리티 함수 추가
src/components/features/LinkForm/Landing.tsx 마진 변경
src/components/features/LinkForm/LinkForm.stories.tsx useFunnel 훅의 타입 매개변수 업데이트
src/components/features/LinkForm/LinkInput.tsx 마진 변경
src/components/features/SearchList/SearchList.tsx SearchList 컴포넌트 삭제
src/components/features/SearchResultsList/BookmarkSelectionList.tsx 새로운 BookmarkSelectionList 컴포넌트 추가
src/components/features/SearchResultsList/SearchResultsList.tsx 새로운 SearchResultsList 컴포넌트 추가
src/components/features/SearchResultsList/index.tsx SearchResultsList 내보내기 추가
src/constants/funnelStep.ts 새로운 상수 및 타입 추가
src/contexts/MapAtom.tsx mapDataAtom, setMapDataAtom 추가
src/contexts/MarkerAtom.tsx markersAtom, addMarkerAtom, clearMarkersAtom 추가
src/hooks/api/bookmarks/useBookMarkList.ts useBookMarkList 훅 추가
src/hooks/api/marker/useMarkerList.ts useMarkerList 훅 추가
src/hooks/api/marker/useNewMarker.ts useNewMarker 훅 추가
src/hooks/common/useBottomFunnel.tsx 새로운 useBottomFunnel 훅 추가
src/hooks/common/useFunnel.tsx useFunnel 훅 제네릭 타입으로 수정
src/hooks/common/useInput.tsx 유효성 검사 기본값 변경
src/hooks/common/useMapData.tsx Jotai를 사용한 상태 관리로 변경
src/hooks/common/useMarkers.tsx Jotai를 사용한 상태 관리로 변경
src/hooks/common/useSessionDataLoader.tsx 새로운 useSessionDataLoader 훅 추가
src/lib/axios.ts HTTP 요청 메서드를 비동기적으로 변경
src/lib/external.ts Axios 인스턴스의 타임아웃 설정 변경
src/pages/Link.tsx useFunneluseYoutubePlace 훅 수정
src/pages/MapView.tsx 상태 관리 및 인증 통합 개선
src/types/naver.ts Place 타입의 속성 변경 (필수 → 선택)
src/utils/extractVideoId.ts 새로운 extractVideoId 함수 추가
src/utils/getStorageData.ts 새로운 스토리지 데이터 관리 함수 추가

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant App
    participant BookmarkDetail
    participant MarkerList
    User->>App: 요청
    App->>BookmarkDetail: 데이터 요청
    BookmarkDetail->>MarkerList: 마커 데이터 요청
    MarkerList->>BookmarkDetail: 마커 데이터 반환
    BookmarkDetail->>User: 데이터 표시
Loading

Assessment against linked issues

Objective Addressed Explanation
검색 장소 저장 (#[53])

Possibly related PRs

Suggested reviewers

  • houony

Warning

Rate limit exceeded

@keemsebin has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 15 minutes and 6 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 5580580 and 1e48dda.


📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 5580580 and 1e48dda.

📒 Files selected for processing (1)
  • src/components/features/SearchResultsList/BookmarkSelectionList.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/features/SearchResultsList/BookmarkSelectionList.tsx

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 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.
    • Generate unit testing code for this file.
    • 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 generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • 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 generate unit testing code.
    • @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.

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

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

github-actions bot commented Nov 22, 2024

Update: 2024년 11월 23일 23시 24분 46초
Storybook 미리보기: https://6709628f8a6eec04b29d4f39-wjqjqocbpb.chromatic.com/

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: 30

🧹 Outside diff range and nitpick comments (40)
src/utils/extractVideoId.ts (2)

1-6: 기본 구현이 올바르지만 유지보수성 개선이 필요합니다.

정규식이 복잡하여 유지보수가 어려울 수 있습니다. 여러 패턴으로 분리하여 가독성을 높이는 것을 고려해보세요.

다음과 같이 개선해보세요:

-export const extractVideoId = (url: string): string | null => {
-  const match = url.match(
-    /(?:https?:\/\/)?(?:www\.)?(?:youtube\.com\/(?:[^/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?/\s]{11})/
-  );
-  return match ? match[1] : null;
-};
+const YOUTUBE_URL_PATTERNS = {
+  standard: /(?:https?:\/\/)?(?:www\.)?youtube\.com\/watch\?v=([^"&?/\s]{11})/,
+  shortened: /(?:https?:\/\/)?(?:www\.)?youtu\.be\/([^"&?/\s]{11})/,
+  embed: /(?:https?:\/\/)?(?:www\.)?youtube\.com\/embed\/([^"&?/\s]{11})/
+};
+
+export const extractVideoId = (url: string): string | null => {
+  for (const pattern of Object.values(YOUTUBE_URL_PATTERNS)) {
+    const match = url.match(pattern);
+    if (match) return match[1];
+  }
+  return null;
+};

1-1: 문서화와 테스트 케이스 추가가 필요합니다.

함수의 목적, 입력값, 반환값에 대한 JSDoc 문서화와 다양한 URL 형식에 대한 테스트 케이스가 필요합니다.

다음과 같이 문서화를 추가해보세요:

+/**
+ * YouTube URL에서 동영상 ID를 추출합니다.
+ * 지원하는 URL 형식:
+ * - https://www.youtube.com/watch?v=VIDEO_ID
+ * - https://youtu.be/VIDEO_ID
+ * - https://www.youtube.com/embed/VIDEO_ID
+ *
+ * @param url - YouTube 동영상 URL
+ * @returns 동영상 ID 또는 매칭되지 않을 경우 null
+ *
+ * @example
+ * extractVideoId('https://www.youtube.com/watch?v=dQw4w9WgXcQ')
+ * // Returns: 'dQw4w9WgXcQ'
+ */
 export const extractVideoId = (url: string): string | null => {

테스트 케이스 작성을 도와드릴까요?

src/hooks/common/useMarkers.tsx (1)

6-8: 원자적 상태 관리 구현이 효율적으로 분리되었습니다.

각각의 마커 관련 동작(조회, 추가, 초기화)이 개별 atom으로 분리되어 있어 관심사 분리가 잘 되어있습니다. 하지만 타입 안정성을 위해 다음과 같은 개선을 제안드립니다.

-  const [markers] = useAtom(markersAtom);
-  const [, addMarker] = useAtom(addMarkerAtom);
-  const [, clearMarkers] = useAtom(clearMarkersAtom);
+  const [markers] = useAtom<Place[]>(markersAtom);
+  const [, addMarker] = useAtom<(place: Place) => void>(addMarkerAtom);
+  const [, clearMarkers] = useAtom<() => void>(clearMarkersAtom);
src/contexts/MarkerAtom.tsx (1)

5-5: 더 구체적인 이름을 사용하는 것이 좋을 것 같습니다.

markersAtom이라는 이름보다 searchResultMarkersAtom과 같이 더 구체적인 이름을 사용하면 atom의 용도를 더 명확하게 표현할 수 있습니다.

-export const markersAtom = atom<Place[]>([]);
+export const searchResultMarkersAtom = atom<Place[]>([]);
src/types/naver.ts (2)

Line range hint 8-21: 타입 정의에 대한 문서화 제안

Place 타입의 각 필드가 어떤 상황에서 누락될 수 있는지 설명하는 JSDoc 문서를 추가하면 좋을 것 같습니다.

다음과 같이 문서화를 추가해보세요:

+/**
+ * 장소 정보를 나타내는 타입
+ * @property {number} [id] - 저장된 장소의 고유 식별자
+ * @property {number} [timeStamp] - 장소가 저장된 시간
+ * @property {string} title - 장소명
+ * @property {string} [link] - 장소 상세 정보 링크 (네이버 검색 결과에서만 제공)
+ * @property {Category | string} category - 장소 카테고리
+ * @property {string} [description] - 장소 설명 (네이버 검색 결과에서만 제공)
+ * @property {string} [telephone] - 전화번호 (제공되지 않을 수 있음)
+ * @property {string} address - 지번 주소
+ * @property {string} [roadAddress] - 도로명 주소 (제공되지 않을 수 있음)
+ * @property {string} mapx - X 좌표
+ * @property {string} mapy - Y 좌표
+ */
 export type Place = {
   // ... existing type definition
 };

Line range hint 8-21: 타입 안전성 개선 제안

좌표값(mapx, mapy)의 타입이 string으로 되어있는데, 실제로는 숫자 데이터를 다루므로 number 타입으로 변경하는 것이 더 적절할 것 같습니다.

다음과 같이 변경을 제안드립니다:

 export type Place = {
   // ... other properties
   address: string;
   roadAddress?: string;
-  mapx: string;
-  mapy: string;
+  mapx: number;
+  mapy: number;
 };
src/hooks/common/useMapData.tsx (2)

6-15: token 매개변수의 목적을 문서화해 주세요.

setState 함수에서 사용되는 token 매개변수의 목적과 사용 사례를 명확히 하면 좋겠습니다.

다음과 같이 JSDoc을 추가하는 것을 제안합니다:

+/**
+ * 맵 데이터 상태를 관리하는 훅
+ * @template T - 맵 데이터의 타입
+ * @returns {Object} 맵 데이터 상태와 상태 설정 함수
+ */
 export const useMapData = <T,>() => {
   const [mapData] = useAtom<MapDataState<T>>(mapDataAtom);
   const [, setMapData] = useAtom(setMapDataAtom);

+  /**
+   * 맵 데이터 상태를 업데이트하는 함수
+   * @param {MapDataState<T>} newState - 새로운 맵 데이터 상태
+   * @param {string | null} token - [설명 필요: token의 목적]
+   */
   const setState = useCallback(

17-17: 반환 타입을 명시적으로 정의하면 좋겠습니다.

반환 구조는 깔끔하지만, 타입 안전성을 높이기 위해 명시적인 반환 타입을 추가하면 좋겠습니다.

다음과 같은 변경을 제안합니다:

-export const useMapData = <T,>() => {
+export const useMapData = <T,>(): {
+  state: MapDataState<T>;
+  setState: (newState: MapDataState<T>, token: string | null) => void;
+} => {
src/utils/getStorageData.ts (1)

13-16: 세션 스토리지 접근 안전성 개선 필요

브라우저의 세션 스토리지 사용 가능 여부를 확인하고 오류를 처리하는 것이 좋습니다.

다음과 같이 개선해보세요:

 export const clearMapStorage = (): void => {
+  try {
+    if (typeof sessionStorage === 'undefined') {
+      throw new Error('Session storage is not available');
+    }
     sessionStorage.removeItem('mapData');
     sessionStorage.removeItem('bottomSheetType');
+  } catch (error) {
+    console.error('Failed to clear map storage:', error);
+  }
 };
src/hooks/api/marker/useNewMarker.ts (1)

1-3: import 구문 순서를 수정해 주세요.

외부 라이브러리와 내부 모듈의 import 순서를 다음과 같이 정리하면 좋겠습니다:

-import { useMutation } from '@tanstack/react-query';
-
-import { post } from '@/lib/axios';
+import { useMutation } from '@tanstack/react-query';
+import { post } from '@/lib/axios';
🧰 Tools
🪛 eslint (1.23.1)

[error] 1-1: There should be no empty line within import group

(import/order)


[error] 3-3: @/lib/axios import should occur before import of @tanstack/react-query

(import/order)

src/hooks/common/useFunnel.tsx (2)

12-13: 제네릭 타입 구현이 잘 되었습니다!

타입 안전성이 향상되었으며, 컴파일 타임에 더 정확한 타입 체크가 가능해졌습니다. 다만, 제네릭 타입 사용에 대한 문서화가 있으면 좋을 것 같습니다.

다음과 같은 JSDoc 문서를 추가하는 것을 제안합니다:

+/**
+ * Funnel 패턴을 구현하는 커스텀 훅
+ * @template T - 스텝의 이름을 나타내는 문자열 리터럴 타입
+ * @param initStep - 초기 스텝 값
+ * @returns Funnel 컴포넌트와 관련 유틸리티
+ */
export const useFunnel = <T extends string>(initStep: T) => {

20-23: 성능 최적화가 적절하게 구현되었습니다!

useMemo를 사용한 최적화로 불필요한 계산을 방지하고 있습니다. 의존성 배열도 정확하게 설정되어 있습니다.

추가로 고려해볼 만한 사항:

  • 자식 컴포넌트가 많을 경우를 대비해 Map을 사용한 캐싱 전략을 고려해볼 수 있습니다.
  • 퍼포먼스 모니터링을 위한 디버그 모드 추가를 고려해보세요.
src/hooks/api/bookmarks/useBookMarkList.ts (2)

1-3: import 구문 정리가 필요합니다.

import 구문의 순서를 다음과 같이 정리하면 코드의 가독성이 향상될 것 같습니다.

-import { useQuery } from '@tanstack/react-query';
-
-import { get } from '@/lib/axios';
+import { get } from '@/lib/axios';
+import { useQuery } from '@tanstack/react-query';
🧰 Tools
🪛 eslint (1.23.1)

[error] 1-1: There should be no empty line within import group

(import/order)


[error] 3-3: @/lib/axios import should occur before import of @tanstack/react-query

(import/order)


13-20: 타입에 대한 문서화가 필요합니다.

각 필드의 의미와 용도를 명확히 하기 위해 JSDoc 주석을 추가하면 좋을 것 같습니다.

+/**
+ * 북마크 상세 정보를 나타내는 타입
+ */
 export type BookmarkDetail = {
+  /** 북마크의 고유 식별자 */
   bookmarkId: number;
+  /** 유튜버의 프로필 이미지 URL */
   youtuberProfile: string;
+  /** 유튜버 이름 */
   name: string;
+  /** 저장된 마커의 수 */
   markersCount: number;
+  /** 북마크 유형 */
   bookmarkType: string;
+  /** 유튜브 동영상 링크 */
   youtubeLink: string;
 };
src/contexts/MapAtom.tsx (1)

4-9: 타입 정의에 문서화가 필요합니다

타입 정의가 명확하지만, 각 타입의 용도와 필드에 대한 설명이 있으면 더 좋을 것 같습니다.

다음과 같이 JSDoc 주석을 추가하는 것을 제안합니다:

+/**
+ * 바텀 시트의 표시 상태를 나타내는 타입
+ * - extract: 장소 추출 모드
+ * - search: 검색 모드
+ * - list: 목록 보기 모드
+ */
 export type BottomSheetType = 'extract' | 'search' | 'list';

+/**
+ * 지도 데이터의 상태를 관리하는 제네릭 타입
+ * @template T - 저장할 데이터의 타입
+ */
 export type MapDataState<T> = {
+  /** 저장된 데이터 (없을 경우 null) */
   data: T | null;
+  /** 현재 바텀 시트의 표시 상태 (없을 경우 null) */
   type: BottomSheetType | null;
 };
src/hooks/common/useInput.tsx (1)

Line range hint 12-15: 입력값 검증 시점 최적화 제안

onBlur 이벤트에서만 유효성을 검사하면 사용자가 포커스를 잃기 전까지 피드백을 받을 수 없습니다.

실시간 검증을 위해 onChange 이벤트에서도 유효성 검사를 수행하는 것을 고려해보세요:

 const onChange: ChangeEventHandler<HTMLInputElement> = useCallback((e) => {
   const value = e.target.value;
   setState(value);
+  if (value.length > 0) {
+    setIsValid(validation.some((criteria) => value.includes(criteria)));
+  }
 }, []);
src/constants/funnelStep.ts (1)

28-28: 타입에 대한 문서화를 추가하면 좋을 것 같습니다.

타입의 용도와 각 플로우의 의미를 설명하는 JSDoc 주석을 추가하는 것을 제안드립니다.

+/** 페이지 플로우의 종류를 정의합니다.
+ * - search: 검색 결과에서 시작하는 플로우
+ * - extract: 추출된 장소에서 시작하는 플로우
+ * - list: 리스트에서 시작하는 플로우
+ */
 export type FlowType = keyof typeof FLOW_CONFIGS;
src/hooks/common/useSessionDataLoader.tsx (2)

8-10: token 매개변수의 타입 안전성을 개선하면 좋겠습니다.

token 매개변수의 타입을 더 구체적으로 정의하면 좋을 것 같습니다.

다음과 같이 변경을 제안드립니다:

-export const useSessionDataLoader = (token: string | null) => {
+type AuthToken = string;
+export const useSessionDataLoader = (token: AuthToken | null) => {

25-28: useEffect에 정리(cleanup) 함수 추가를 고려해보세요.

컴포넌트 언마운트 시 필요한 정리 작업이 있을 수 있으며, 로딩 상태 관리도 추가하면 좋을 것 같습니다.

다음과 같은 개선을 제안드립니다:

+const [isLoading, setIsLoading] = useState(false);
+
 useEffect(() => {
-  loadSessionStorageData();
+  let mounted = true;
+
+  const loadData = async () => {
+    if (!mounted) return;
+    setIsLoading(true);
+    await loadSessionStorageData();
+    if (mounted) {
+      setIsLoading(false);
+    }
+  };
+
+  loadData();
+
+  return () => {
+    mounted = false;
+  };
 }, [loadSessionStorageData]);
src/hooks/api/marker/useMarkerList.ts (3)

1-4: import 문 순서를 수정해 주세요

import 문의 순서가 올바르지 않습니다. 다음과 같이 수정하는 것이 좋습니다:

-import { useQuery } from '@tanstack/react-query';
-
-import { get } from '@/lib/axios';
-
+import { get } from '@/lib/axios';
+import { useQuery } from '@tanstack/react-query';
🧰 Tools
🪛 eslint (1.23.1)

[error] 1-1: There should be no empty line within import group

(import/order)


[error] 3-3: @/lib/axios import should occur before import of @tanstack/react-query

(import/order)


5-28: 타입 정의에 대한 문서화 및 개선 제안

타입 정의가 잘 되어 있지만, 다음 사항들을 고려해 보시기 바랍니다:

  1. 각 타입에 대한 JSDoc 문서화 추가
  2. mapxmapy를 문자열 대신 숫자로 처리하는 것이 좋을 것 같습니다

예시:

+/**
+ * 북마크와 관련된 마커 정보를 담는 타입
+ */
 export type Marker = {
   bookmarkName: string;
   markers: MarkersList;
 };

+/**
+ * 마커 목록과 페이지네이션 정보를 담는 타입
+ */
 export type MarkersList = {
   data: MarkerDetail[];
   currentCursor: number;
   pageSize: number;
   hasNext: boolean;
   nextCursor: number;
 };

+/**
+ * 개별 마커의 상세 정보를 담는 타입
+ */
 export type MarkerDetail = {
   markerId: number;
   title: string;
   address: string;
-  mapx: string;
-  mapy: string;
+  mapx: number;
+  mapy: number;
   category: {
     majorCategory: string;
     middleCategory: string;
   };
 };

34-34: API 엔드포인트 구조 개선 제안

현재 API 엔드포인트가 상대 경로로 되어 있습니다. 다음과 같은 개선을 고려해 보세요:

  1. API 엔드포인트를 상수로 분리
  2. 기본 URL을 환경 변수로 관리

예시:

// constants/api.ts
export const API_ENDPOINTS = {
  MARKERS: (bookmarkId: number) => `/api/markers/${bookmarkId}`,
} as const;

// hooks/api/marker/useMarkerList.ts
import { API_ENDPOINTS } from '@/constants/api';

// ... 사용시
get<Marker>(API_ENDPOINTS.MARKERS(bookmarkId), ...)
src/lib/axios.ts (2)

11-11: 제네릭 타입 제약 조건 추가 권장

현재 제네릭 타입 T에 대한 제약 조건이 없어 잠재적인 타입 관련 문제가 발생할 수 있습니다.

다음과 같이 제네릭 타입에 제약 조건을 추가하는 것을 권장합니다:

-export const get = async <T>(...args: Parameters<typeof instance.get>) => {
+export const get = async <T extends Record<string, unknown>>(...args: Parameters<typeof instance.get>) => {

다른 메서드들에도 동일하게 적용해주세요.

Also applies to: 16-16, 21-21, 26-26, 31-31


12-13: 응답 처리 유연성 개선 제안

현재 구현은 response.data만 반환하고 있어, 상태 코드나 헤더와 같은 중요한 메타데이터에 접근할 수 없습니다.

다음과 같이 전체 응답을 선택적으로 받을 수 있도록 개선하는 것을 제안합니다:

 export const get = async <T extends Record<string, unknown>>(
+  fullResponse = false,
   ...args: Parameters<typeof instance.get>
 ) => {
   const response = await instance.get<T>(...args);
-  return response.data;
+  return fullResponse ? response : response.data;
 };

이렇게 하면 필요한 경우 전체 응답 객체에 접근할 수 있습니다.

Also applies to: 17-18, 22-23, 27-28, 32-33

src/components/features/LinkForm/LinkForm.stories.tsx (1)

Line range hint 21-45: 스토리에 엣지 케이스 추가를 고려해보세요

현재 기본적인 플로우만 테스트하고 있습니다. 다음과 같은 엣지 케이스들을 추가하면 좋을 것 같습니다:

  • 잘못된 URL 입력 시나리오
  • 로딩 상태에서의 UI
  • 에러 상태에서의 UI

예시 코드:

export const WithInvalidUrl: Story = {
  render: () => {
    const { Funnel, Step, setStep } = useFunnel<'입력전' | '링크입력' | '추출상태'>('링크입력');
    return (
      <Layout>
        <Funnel>
          <Step name="링크입력">
            <LinkInput
              onNext={() => setStep('추출상태')}
              onHomeClick={() => {}}
              context={{ state: 'invalid-url', setState: () => {} }}
            />
          </Step>
        </Funnel>
      </Layout>
    );
  },
};
src/components/features/BookmarkList/BookmarkList.tsx (2)

23-27: 접근성 개선이 필요합니다

클릭 가능한 요소들에 대한 키보드 접근성과 ARIA 속성이 누락되었습니다.

다음과 같이 수정을 제안합니다:

 <div
   key={item.bookmarkId}
   onClick={() => handleBookmarkClick(item.bookmarkId)}
+  onKeyDown={(e) => e.key === 'Enter' && handleBookmarkClick(item.bookmarkId)}
+  role="button"
+  tabIndex={0}
+  aria-label={`${item.name}의 북마크 상세보기`}
   className="flex flex-row justify-between items-center cursor-pointer"
 >

그리고 위치 아이콘에도 적절한 aria-label을 추가해주세요:

- <Icon name="location" size={15} />
+ <Icon name="location" size={15} aria-label="저장된 위치 수" />

Also applies to: 40-43


29-37: 이미지 최적화가 필요합니다

유튜버 프로필 이미지에 대한 로딩 및 오류 처리가 누락되었습니다.

다음과 같이 수정을 제안합니다:

 {item.youtuberProfile ? (
   <img
     src={item.youtuberProfile}
     className="w-12 h-12 rounded-full"
     alt={`${item.name}의 프로필 이미지`}
+    loading="lazy"
+    onError={(e) => {
+      e.currentTarget.onerror = null;
+      e.currentTarget.src = '/default-profile.png';
+    }}
   />
 ) : (
   <Icon name="findy1" className="w-11 h-11" />
 )}
src/components/features/BookmarkDetail/BookmarkDetail.tsx (2)

12-12: Props 타입에 대한 문서화 개선이 필요합니다.

Props 인터페이스가 간단하지만, 각 속성의 목적과 사용법에 대한 설명이 부족합니다.

다음과 같이 개선해보세요:

-type Props = { bookmarkId: number; onPrev: () => void };
+/** BookmarkDetail 컴포넌트 Props */
+type BookmarkDetailProps = {
+  /** 북마크의 고유 식별자 */
+  bookmarkId: number;
+  /** 이전 화면으로 돌아가기 위한 콜백 함수 */
+  onPrev: () => void;
+};

13-64: 컴포넌트 아키텍처 개선이 필요합니다.

현재 컴포넌트가 데이터 페칭과 표현 로직을 모두 처리하고 있어 단일 책임 원칙을 위반하고 있습니다.

다음과 같은 구조 개선을 제안드립니다:

  1. 컨테이너 컴포넌트와 프레젠테이션 컴포넌트로 분리:
// BookmarkDetailContainer.tsx
export const BookmarkDetailContainer = ({ bookmarkId, onPrev }: Props) => {
  const { token } = useAuth();
  const { data, isError, isLoading } = useMarkerList(bookmarkId, token);
  const [, setMarkers] = useAtom(markersAtom);

  // ... 데이터 처리 로직

  return <BookmarkDetailPresentation 
    bookmarkName={data?.bookmarkName}
    markers={data?.markers?.data}
    onBack={() => {
      setMarkers([]);
      onPrev();
    }}
  />;
};

// BookmarkDetailPresentation.tsx
export const BookmarkDetailPresentation = ({ 
  bookmarkName,
  markers,
  onBack
}: PresentationProps) => {
  // ... UI 렌더링 로직
};

이렇게 분리하면 다음과 같은 이점이 있습니다:

  • 테스트 용이성 향상
  • 컴포넌트 재사용성 증가
  • 관심사의 명확한 분리
src/components/features/SearchResultsList/SearchResultsList.tsx (3)

14-18: Props 타입 정의를 개선하세요.

Props 인터페이스에 다음 사항들을 추가하면 좋을 것 같습니다:

  • JSDoc 문서화
  • 로딩 상태 관리를 위한 isLoading prop
  • 에러 상태 관리를 위한 error prop
+/**
+ * 검색 결과 목록 컴포넌트의 Props
+ * @param places - 장소 목록
+ * @param onNext - 다음 단계로 이동하는 콜백
+ * @param onSelect - 장소 선택 시 호출되는 콜백
+ */
 type Props = {
   places: Place[];
+  isLoading?: boolean;
+  error?: Error;
   onNext: () => void;
   onSelect: (place: NewMarker) => void;
 };

19-23: 상태 관리 로직을 개선하세요.

선택 로직을 별도의 커스텀 훅으로 분리하면 코드의 재사용성과 테스트 용이성이 향상될 것 같습니다.

// useSelection.ts
export const useSelection = () => {
  const [selectedIndex, setSelectedIndex] = useState<number | null>(null);

  const handleSelect = (index: number) => {
    const newIndex = selectedIndex === index ? null : index;
    setSelectedIndex(newIndex);
  };

  return { selectedIndex, handleSelect };
};

1-71: 아키텍처 개선 사항을 고려하세요.

컴포넌트의 안정성과 테스트 용이성을 위해 다음 사항들을 고려해보세요:

  • ErrorBoundary 추가
  • 단위 테스트 작성
  • E2E 테스트 시나리오 작성

특히 다음과 같은 테스트 케이스들이 필요합니다:

  • 장소 선택/해제
  • 로그인 모달 표시
  • 에러 상태 처리
  • 키보드 네비게이션
src/components/features/LinkForm/ExtractionStatus.tsx (1)

24-25: 썸네일 URL 생성 로직 개선이 필요합니다.

현재 구현은 이전보다 개선되었지만, 다음과 같은 추가 개선사항을 고려해보시기 바랍니다:

  1. 썸네일 URL을 상수나 유틸리티 함수로 분리
  2. maxresdefault.jpg가 존재하지 않을 경우를 대비한 폴백(fallback) 옵션 추가

다음과 같이 개선해보시는 건 어떨까요?

-  const thumbnailUrl = videoId ? `https://img.youtube.com/vi/${videoId}/maxresdefault.jpg` : '';
+  const thumbnailUrl = videoId ? getThumbnailUrl(videoId) : '';

// utils/youtube.ts에 추가
+export const getThumbnailUrl = (videoId: string) => {
+  const qualities = ['maxresdefault', 'hqdefault', 'mqdefault', 'default'];
+  return `https://img.youtube.com/vi/${videoId}/${qualities[0]}.jpg`;
+};
src/components/features/SearchResultsList/BookmarkSelectionList.tsx (1)

12-15: Props 타입 정의를 더 구체적으로 개선하세요.

onNext 콜백의 용도가 북마크 저장 완료 후 다음 단계로 진행하는 것이므로, 이를 타입에 더 명확하게 표현하면 좋겠습니다.

 type Props = {
-  onNext: () => void;
+  onNext: () => Promise<void> | void;
   selectedPlace: NewMarker;
 };
src/components/features/ExtractedPlacesList/ExtractedPlacesList.tsx (3)

38-41: map 함수에서 unused variable 경고를 제거하는 더 나은 방법이 있습니다.

eslint 주석을 사용하는 대신 구조 분해 할당을 활용하여 코드를 개선할 수 있습니다.

-        // eslint-disable-next-line @typescript-eslint/no-unused-vars
-        .map(({ id, ...placeData }) => placeData),
+        .map(({ id: _, ...placeData }) => placeData),

Line range hint 71-96: key prop 누락 및 최적화 이슈가 있습니다.

  1. Fragment(<>)에 key prop이 없고, div에 잘못 배치되어 있습니다.
  2. 반복적인 스타일 로직은 별도의 스타일 컴포넌트로 추출하면 좋습니다.
- <>
+ <React.Fragment key={`${item.title}-${item.address}`}>
    <div
-     key={`${item.title}-${item.address}`}
      className={`flex flex-row justify-between items-center ${
        index !== places.places.length - 1 && 'pb-2'
      }`}
    >

Line range hint 56-57: TODO 코멘트 처리가 필요합니다.

이미지 대체와 컴포넌트화에 대한 TODO 항목이 있습니다. 이를 해결하기 위한 구체적인 계획이 필요해 보입니다.

다음 작업들을 도와드릴 수 있습니다:

  1. 이미지 폴백(fallback) 컴포넌트 구현
  2. 반복되는 리스트 아이템을 별도 컴포넌트로 추출

필요하시다면 GitHub 이슈를 생성해드리겠습니다.

src/pages/MapView.tsx (1)

105-111: BottomSheet 컴포넌트의 상태 관리 개선이 필요합니다.

현재 구현은 다소 복잡하며, 상태 업데이트 로직이 인라인으로 작성되어 있습니다. 이를 별도의 훅이나 함수로 분리하면 유지보수성이 향상될 것 같습니다.

다음과 같은 리팩토링을 제안드립니다:

const useBottomSheetState = (token: string) => {
  const handleClose = useCallback(() => {
    setState({ ...state, type: null }, token);
  }, [state, token]);

  return {
    isOpen: !!state.type,
    handleClose
  };
};

그리고 컴포넌트에서는 다음과 같이 사용:

- <BottomSheet
-   isOpen={!!state.type}
-   setIsOpen={(open) => {
-     if (!open) setState({ ...state, type: null }, token);
-   }}
- >
+ const { isOpen, handleClose } = useBottomSheetState(token);
+ <BottomSheet
+   isOpen={isOpen}
+   setIsOpen={(open) => !open && handleClose()}
+ >
src/hooks/common/useBottomFunnel.tsx (2)

36-42: 비로그인 상태에 대한 유형별 처리 고려 필요

현재 !token && type === 'list' 조건으로 렌더링을 제어하고 있습니다. 만약 다른 type에서도 비로그인 상태에 대한 처리가 필요하다면 조건문을 수정하여 모든 경우를 포괄할 수 있도록 고려해주세요.


29-34: useCallback의 의존성 배열 확인 필요

handleStepChange 함수의 의존성 배열에 setStep만 포함되어 있습니다. 하지만 handleStepChange 내부에서 사용하는 nextStep은 함수의 매개변수이므로 의존성 배열에 포함할 필요는 없습니다. 현재 상태로도 문제가 없으나, 의존성 배열을 재확인해주세요.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between f18cd5a and d233c08.

⛔ Files ignored due to path filters (3)
  • .yarn/cache/jotai-npm-2.10.3-20aff9aa6c-64f6536aaa.zip is excluded by !**/.yarn/**, !**/*.zip
  • .yarn/install-state.gz is excluded by !**/.yarn/**, !**/*.gz
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (44)
  • .pnp.cjs (3 hunks)
  • package.json (1 hunks)
  • src/App.tsx (1 hunks)
  • src/components/features/BookmarkDetail/BookmarkDetail.tsx (1 hunks)
  • src/components/features/BookmarkDetail/index.tsx (1 hunks)
  • src/components/features/BookmarkList/BookmarkList.tsx (1 hunks)
  • src/components/features/BookmarkList/index.tsx (1 hunks)
  • src/components/features/BottomSheetContent/BottomSheetContent.tsx (1 hunks)
  • src/components/features/BottomSheetContent/ExtractedPlaces.tsx (0 hunks)
  • src/components/features/BottomSheetContent/SearchResult.tsx (0 hunks)
  • src/components/features/BottomSheetContent/index.tsx (0 hunks)
  • src/components/features/ExtractedPlacesList/ExtractedPlacesList.tsx (3 hunks)
  • src/components/features/ExtractedPlacesList/index.tsx (1 hunks)
  • src/components/features/LinkForm/ExtractionStatus.tsx (2 hunks)
  • src/components/features/LinkForm/Landing.tsx (1 hunks)
  • src/components/features/LinkForm/LinkForm.stories.tsx (1 hunks)
  • src/components/features/LinkForm/LinkInput.tsx (1 hunks)
  • src/components/features/SearchList/SearchList.tsx (0 hunks)
  • src/components/features/SearchResultsList/BookmarkSelectionList.tsx (1 hunks)
  • src/components/features/SearchResultsList/SearchResultsList.tsx (1 hunks)
  • src/components/features/SearchResultsList/index.tsx (1 hunks)
  • src/constants/funnelStep.ts (1 hunks)
  • src/contexts/MapAtom.tsx (1 hunks)
  • src/contexts/MapContext.tsx (0 hunks)
  • src/contexts/MarkerAtom.tsx (1 hunks)
  • src/contexts/MarkerContext.tsx (0 hunks)
  • src/hooks/api/bookmarks/useBookMarkList.ts (1 hunks)
  • src/hooks/api/link/useYoutubePlace.ts (1 hunks)
  • src/hooks/api/marker/useMarkerList.ts (1 hunks)
  • src/hooks/api/marker/useNewMarker.ts (1 hunks)
  • src/hooks/common/useBottomFunnel.tsx (1 hunks)
  • src/hooks/common/useFunnel.tsx (2 hunks)
  • src/hooks/common/useInput.tsx (1 hunks)
  • src/hooks/common/useMapData.tsx (1 hunks)
  • src/hooks/common/useMapStorage.tsx (0 hunks)
  • src/hooks/common/useMarkers.tsx (1 hunks)
  • src/hooks/common/useSessionDataLoader.tsx (1 hunks)
  • src/lib/axios.ts (1 hunks)
  • src/lib/external.ts (1 hunks)
  • src/pages/Link.tsx (1 hunks)
  • src/pages/MapView.tsx (5 hunks)
  • src/types/naver.ts (1 hunks)
  • src/utils/extractVideoId.ts (1 hunks)
  • src/utils/getStorageData.ts (1 hunks)
💤 Files with no reviewable changes (7)
  • src/components/features/BottomSheetContent/ExtractedPlaces.tsx
  • src/components/features/BottomSheetContent/SearchResult.tsx
  • src/components/features/BottomSheetContent/index.tsx
  • src/components/features/SearchList/SearchList.tsx
  • src/contexts/MapContext.tsx
  • src/contexts/MarkerContext.tsx
  • src/hooks/common/useMapStorage.tsx
✅ Files skipped from review due to trivial changes (6)
  • src/components/features/BookmarkDetail/index.tsx
  • src/components/features/BookmarkList/index.tsx
  • src/components/features/ExtractedPlacesList/index.tsx
  • src/components/features/LinkForm/Landing.tsx
  • src/components/features/LinkForm/LinkInput.tsx
  • src/components/features/SearchResultsList/index.tsx
🧰 Additional context used
🪛 eslint (1.23.1)
src/hooks/api/bookmarks/useBookMarkList.ts

[error] 1-1: There should be no empty line within import group

(import/order)


[error] 3-3: @/lib/axios import should occur before import of @tanstack/react-query

(import/order)

src/hooks/api/marker/useMarkerList.ts

[error] 1-1: There should be no empty line within import group

(import/order)


[error] 3-3: @/lib/axios import should occur before import of @tanstack/react-query

(import/order)

src/hooks/api/marker/useNewMarker.ts

[error] 1-1: There should be no empty line within import group

(import/order)


[error] 3-3: @/lib/axios import should occur before import of @tanstack/react-query

(import/order)

🔇 Additional comments (33)
.pnp.cjs (2)

74-74: 의존성 추가가 올바르게 구성되었습니다.

Jotai 라이브러리가 Yarn PnP 구성에 정상적으로 추가되었습니다.

Also applies to: 8165-8165


10108-10129: Peer 의존성 버전 호환성을 확인하세요.

Jotai v2.10.3이 현재 프로젝트에서 사용 중인 React 버전(18.3.1)과 @types/react 버전(18.3.10)과 호환되는지 확인이 필요합니다.

✅ Verification successful

Jotai v2.10.3의 peer 의존성이 현재 프로젝트의 React 버전과 호환됩니다.

Jotai v2.10.3은 React >=17.0.0 및 @types/react >=17.0.0을 요구하며, 현재 프로젝트에서 사용 중인 React ^18.3.1과 @types/react ^18.3.10은 이 요구사항을 충족합니다.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify jotai's peer dependencies compatibility with current React versions

# Check jotai's peer dependencies requirements
curl -s https://registry.npmjs.org/jotai/2.10.3 | jq '.peerDependencies'

# Check current React version compatibility range
curl -s https://registry.npmjs.org/jotai/2.10.3 | jq '.peerDependencies."react"'

Length of output: 222


Script:

#!/bin/bash
# Check package.json for React and @types/react versions
fd package.json --type f --exec cat {} \; | jq '.dependencies["react"], .dependencies["@types/react"], .devDependencies["react"], .devDependencies["@types/react"]'

Length of output: 199

src/App.tsx (2)

9-11: 컴포넌트 구조가 개선되었습니다!

컨텍스트 프로바이더를 제거하고 구조를 단순화한 것이 좋은 변경사항입니다. 이는 불필요한 리렌더링을 방지하는데 도움이 될 것입니다.


9-11: 컨텍스트에서 Jotai로의 마이그레이션 검증이 필요합니다.

이전에 MapDataProviderMarkerProvider를 사용하던 컴포넌트들이 Jotai로 올바르게 마이그레이션되었는지 확인이 필요합니다.

다음 스크립트를 실행하여 컨텍스트 사용 여부를 확인해주세요:

✅ Verification successful

Let me check the content of the Jotai-related files to ensure proper migration.


Jotai로의 마이그레이션이 성공적으로 완료되었습니다.

코드베이스 검사 결과, 기존 Context API (MapDataProvider, MarkerProvider)의 사용이 완전히 제거되었으며, Jotai 기반의 상태 관리로 성공적으로 전환되었음을 확인했습니다:

  • MapAtom.tsxMarkerAtom.tsx에서 적절한 atom 정의
  • useMapDatauseMarkers 훅에서 Jotai의 useAtom 사용
  • 컴포넌트(BookmarkDetail)에서 Jotai atom 올바르게 사용
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify if any components are still using the old context

# Check for MapDataContext usage
echo "Checking for MapDataContext usage..."
rg -l "useContext.*MapDataContext|MapDataProvider" 

# Check for MarkerContext usage
echo "Checking for MarkerContext usage..."
rg -l "useContext.*MarkerContext|MarkerProvider"

# Check for new Jotai atom usage
echo "Checking for Jotai atom usage..."
rg -l "useAtom|atom"

Length of output: 550


Script:

#!/bin/bash
# Check the implementation of Jotai atoms and their usage
echo "=== MapAtom.tsx ==="
cat src/contexts/MapAtom.tsx

echo -e "\n=== MarkerAtom.tsx ==="
cat src/contexts/MarkerAtom.tsx

echo -e "\n=== useMapData.tsx ==="
cat src/hooks/common/useMapData.tsx

echo -e "\n=== useMarkers.tsx ==="
cat src/hooks/common/useMarkers.tsx

echo -e "\n=== BookmarkDetail.tsx ==="
cat src/components/features/BookmarkDetail/BookmarkDetail.tsx

Length of output: 4713

src/hooks/common/useMarkers.tsx (2)

1-1: 상태 관리 라이브러리 마이그레이션이 잘 이루어졌습니다.

Context API에서 Jotai로의 전환이 깔끔하게 이루어졌습니다. 이는 PR 목표에서 언급된 불필요한 리렌더링 문제를 해결하는데 도움이 될 것입니다.

Also applies to: 3-3


10-14: 훅의 반환값이 명확하게 구조화되었습니다.

객체 형태로 반환하여 사용하는 쪽에서 필요한 기능만 구조 분해 할당으로 가져갈 수 있어 좋습니다.

이 훅의 사용처를 확인하기 위해 다음 스크립트를 실행합니다:

✅ Verification successful

useMarkers 훅이 올바르게 사용되고 있음을 확인했습니다.

검색 결과를 통해 다음과 같은 사용 패턴을 확인했습니다:

  • MapView에서 addMarker, clearMarkers 구조 분해 할당 사용
  • useSessionDataLoader에서 clearMarkers, addMarker 구조 분해 할당 사용
  • ExtractedPlacesList에서 clearMarkers 구조 분해 할당 사용
  • BookmarkSelectionList에서 clearMarkers 구조 분해 할당 사용
  • NaverMap에서 markers 구조 분해 할당 사용

모든 사용처에서 필요한 기능만 구조 분해 할당으로 가져가서 사용하고 있어 훅의 반환값이 효율적으로 활용되고 있습니다.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: 훅의 사용처를 검색하여 올바르게 사용되고 있는지 확인합니다.

echo "useMarkers 훅의 사용처 확인:"
rg "useMarkers\(\)" -A 5

Length of output: 2750

src/contexts/MarkerAtom.tsx (3)

1-4: LGTM! 임포트가 깔끔하게 정리되어 있습니다.

필요한 의존성만 정확하게 임포트되어 있습니다.


12-14: 마커 초기화 전에 사용자 확인이 필요할 수 있습니다.

모든 마커를 한 번에 삭제하는 것은 위험할 수 있으므로, 컴포넌트 레벨에서 사용자 확인을 추가하는 것이 좋습니다.


1-14: 검색 결과 저장 API와의 연동 확인이 필요합니다.

마커 상태 관리 구현이 잘 되어있지만, PR 목적인 검색 결과 저장 기능과의 연동이 필요합니다. API 호출 시점과 마커 상태 업데이트 순서를 확인해주세요.

✅ Verification successful

검색 결과 저장 기능이 올바르게 구현되어 있습니다.

코드베이스 분석 결과, 마커 상태 관리와 검색 결과 저장이 다음과 같이 적절하게 연동되어 있습니다:

  • useBottomFunnel 훅에서 검색 결과(Place[])를 받아 단계별 저장 플로우를 관리합니다
  • useNewMarker 훅을 통해 선택된 장소를 북마크에 저장하는 API 호출이 구현되어 있습니다
  • 마커 상태(MarkerAtom)는 UI 표시 용도로만 사용되며, 실제 데이터 저장은 API를 통해 이루어집니다
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: 검색 결과 저장 API 사용 확인

# API 호출 관련 코드 검색
rg -t typescript -t javascript "saveSearchResult|searchSave" -A 5

Length of output: 102


Script:

#!/bin/bash
# Search for API related code with common HTTP method patterns
rg -g '*.{ts,tsx,js,jsx}' '(post|put|patch|get)\s*\([^)]*search' -A 5

# Search for any search-related API calls
rg -g '*.{ts,tsx,js,jsx}' 'api.*search' -A 5

# Look for components or hooks that might handle search result saving
rg -g '*.{ts,tsx,js,jsx}' '(save|store).*search' -A 5

# Check for any Place type usage which might indicate search result handling
rg -g '*.{ts,tsx,js,jsx}' 'Place.*\[\]' -A 5

Length of output: 5576


Script:

#!/bin/bash
# Check for any API calls or mutations in hooks directory
rg -g '*.{ts,tsx}' 'useMutation|useQuery' ./src/hooks -A 5

# Look for any save-related hooks
rg -g '*.{ts,tsx}' 'use.*Save' ./src/hooks -A 5

# Check bottom funnel implementation for save functionality
ast-grep --pattern 'const useBottomFunnel = ({ type, data }: bottomFunnelProps) => {
  $$$
}'

Length of output: 9979

src/types/naver.ts (1)

13-13: 선택적 필드 처리 검증이 필요합니다

해당 필드들을 선택적으로 변경하면서 관련 컴포넌트에서 적절한 처리가 필요할 것 같습니다. 특히 BookmarkDetailSearchResultsList 컴포넌트에서 이 필드들의 부재를 고려한 처리가 되어있는지 확인이 필요합니다.

Also applies to: 15-15, 16-16, 18-18

✅ Verification successful

선택적 필드 처리가 안전하게 구현되어 있습니다

컴포넌트 코드를 검토한 결과, link, description, telephone, roadAddress 필드들이 직접적으로 사용되지 않고 있어 선택적 필드로 변경하는 것이 안전합니다. BookmarkDetailSearchResultsList 컴포넌트는 필수 필드인 title, address, category만 사용하고 있습니다.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: 선택적 필드를 사용하는 컴포넌트들의 처리 방식 확인

# Place 타입의 선택적 필드를 사용하는 패턴 검색
rg -t tsx -t ts "(?:link|description|telephone|roadAddress)" src/

Length of output: 95


Script:

#!/bin/bash
# Description: 선택적 필드를 사용하는 컴포넌트들의 처리 방식 확인

# Place 타입의 선택적 필드 사용 패턴 검색 (확장자 수정)
rg "(?:link|description|telephone|roadAddress)" --type ts --type typescript src/

# BookmarkDetail과 SearchResultsList 컴포넌트 검색
fd "BookmarkDetail|SearchResultsList" --type f --extension ts --extension tsx src/

Length of output: 324


Script:

#!/bin/bash
# BookmarkDetail과 SearchResultsList 컴포넌트의 내용 확인
cat src/components/features/BookmarkDetail/BookmarkDetail.tsx
cat src/components/features/SearchResultsList/SearchResultsList.tsx

# Place 타입 사용 패턴 재검색
rg "(?:link|description|telephone|roadAddress)" src/components/features/BookmarkDetail/BookmarkDetail.tsx src/components/features/SearchResultsList/SearchResultsList.tsx

Length of output: 4920

src/hooks/common/useMapData.tsx (2)

1-4: 의존성 가져오기가 깔끔하게 구성되었습니다.

React Context에서 Jotai로의 전환이 잘 반영되어 있으며, 필요한 의존성들이 명확하게 정의되어 있습니다.


1-17: Context에서 Jotai로의 전환이 적절히 이루어졌는지 확인이 필요합니다.

이 변경으로 인한 성능 개선을 확인하기 위해 불필요한 리렌더링이 실제로 감소했는지 검증이 필요합니다.

✅ Verification successful

Context에서 Jotai로의 전환이 성공적으로 완료되었습니다.

코드베이스 분석 결과:

  • Context 관련 코드가 완전히 제거되었으며, 새로운 Jotai 기반 구현이 적절히 이루어졌습니다.
  • MapAtom.tsx에서 atom이 올바르게 정의되어 있으며, sessionStorage 연동도 잘 구현되어 있습니다.
  • useMapData 훅이 MapViewuseSessionDataLoader에서 정상적으로 사용되고 있습니다.
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: 이전 Context 구현과 관련된 파일들이 모두 제거되었는지 확인

# Context 관련 파일 검색
echo "Searching for old context files..."
rg -l "MapContext|MapProvider" 

# Context 사용 여부 확인
echo "Checking for remaining useContext usage..."
rg "useContext.*Map" 

Length of output: 229


Script:

#!/bin/bash
# Let's verify the Jotai implementation and its usage

# Check the MapAtom implementation
echo "Checking MapAtom implementation..."
rg -A 10 "mapDataAtom" src/

# Check all files using the MapAtom
echo "Checking MapAtom usage..."
rg "useMapData|mapDataAtom" 

# Check if there are any remaining Context-like implementations
echo "Checking for any remaining Context implementations..."
fd -e tsx -e ts -e jsx -e js --exec grep -l "createContext.*Map"

Length of output: 3029

src/hooks/api/link/useYoutubePlace.ts (2)

17-17: URL 인코딩 추가로 보안성이 향상되었습니다.

encodeURIComponent를 사용하여 URL 파라미터를 적절히 인코딩하는 것은 매우 중요한 보안 개선사항입니다. 특수 문자나 한글이 포함된 YouTube 링크를 안전하게 처리할 수 있게 되었습니다.


19-19: 재시도 로직 추가로 안정성이 개선되었습니다.

일시적인 네트워크 오류에 대한 복원력을 제공하기 위해 단일 재시도를 추가한 것은 적절한 선택입니다. 사용자 경험과 서버 부하 사이의 균형이 잘 맞춰져 있습니다.

src/utils/getStorageData.ts (1)

1-1: 파일 구조의 일관성 검토 필요

jotai로 마이그레이션했다는 PR 목표에 따르면, MapAtomcontexts 폴더가 아닌 다른 위치(예: atoms 또는 store)에 있어야 할 것 같습니다.

다음 스크립트로 파일 위치를 확인해보겠습니다:

src/hooks/api/bookmarks/useBookMarkList.ts (1)

5-11: 타입 정의가 명확합니다.

페이지네이션 관련 필드들이 잘 정의되어 있으며, 커서 기반 페이지네이션 구현에 적합합니다.

src/constants/funnelStep.ts (3)

3-9: 단계 상수가 명확하게 정의되어 있습니다!

상수명과 값이 직관적이며, as const 사용으로 타입 안정성이 보장됩니다.


11-11: 타입 정의가 적절합니다!

STEPS 객체의 값들을 유니온 타입으로 잘 추출하였습니다.


13-26: 플로우 설정의 단계 순서 검증이 필요합니다.

각 플로우의 단계 배열이 올바른 순서로 정의되어 있는지 확인이 필요합니다.

✅ Verification successful

플로우 설정의 단계 순서가 적절히 구현되어 있습니다.

코드베이스 분석 결과:

  • FLOW_CONFIGS의 단계 순서가 논리적으로 올바르게 정의되어 있습니다
  • useBottomFunnel 훅에서 handleStepChange 함수를 통해 단계 전환이 적절히 관리되고 있습니다
  • 각 플로우 타입별로 필요한 단계들이 순차적으로 구성되어 있습니다
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the usage of flow steps in components and hooks

# Search for components using these steps
echo "컴포넌트에서 단계 사용 확인:"
rg -l "STEPS\.(SEARCH_PLACES|EXTRACTED_PLACES|LIST_SELECT|LIST|BOOKMARK_DETAIL)" --type ts --type tsx

# Search for step order validation logic
echo "단계 순서 검증 로직 확인:"
rg "steps\[.*\]|steps\.indexOf" --type ts --type tsx

Length of output: 298


Script:

#!/bin/bash
# Search for components using these steps without file type restrictions
echo "컴포넌트에서 단계 사용 확인:"
rg -l "STEPS\.(SEARCH_PLACES|EXTRACTED_PLACES|LIST_SELECT|LIST|BOOKMARK_DETAIL)"

# Search for step order validation logic
echo "단계 순서 검증 로직 확인:"
rg "steps\[.*\]|steps\.indexOf"

# Search for flow navigation logic
echo "플로우 네비게이션 로직 확인:"
rg "FLOW_CONFIGS" -A 5

Length of output: 1709


Script:

#!/bin/bash
# Check the implementation of useFunnel hook
echo "useFunnel 훅 구현 확인:"
rg -A 10 "export .* useFunnel" src/hooks/common/useFunnel.ts

# Check step transitions in useBottomFunnel
echo "useBottomFunnel 단계 전환 로직 확인:"
rg -A 10 "setStep" src/hooks/common/useBottomFunnel.tsx

# Check step type definitions
echo "단계 타입 정의 확인:"
cat src/constants/funnelStep.ts

Length of output: 1716

src/hooks/common/useSessionDataLoader.tsx (1)

1-6: 의존성 import가 깔끔하게 구성되어 있습니다!

필요한 React 훅들과 유틸리티 함수들이 잘 정리되어 있습니다.

src/lib/external.ts (2)

Line range hint 10-34: HTTP 메서드 구현이 잘 되어있습니다.

타입 안전성이 보장되며, 응답 데이터 처리가 일관되게 구현되어 있습니다.


7-7: 타임아웃 시간 증가에 대한 검토가 필요합니다.

타임아웃을 10초로 증가시킨 것은 API 응답 시간에 문제가 있을 수 있음을 시사합니다. 다음 사항들을 고려해주세요:

  1. API 응답이 느린 근본적인 원인을 파악하고 개선이 필요한지 검토
  2. 사용자 경험 향상을 위해 로딩 상태 표시 추가
  3. 긴 요청 시 사용자가 취소할 수 있는 기능 구현 검토

다음 스크립트로 API 응답 시간이 긴 엔드포인트를 확인해보겠습니다:

✅ Verification successful

타임아웃 설정이 적절하게 구현되어 있습니다.

코드베이스 분석 결과, 다음과 같은 사항들이 확인되었습니다:

  1. 유튜브 장소 정보 추출 기능에 적절한 로딩 상태(isLoading)가 이미 구현되어 있습니다.
  2. 사용자에게 "장소 정보를 추출 중입니다" 메시지와 함께 로딩 상태를 시각적으로 표시합니다.
  3. 다른 API 인스턴스들(naver.ts, axios.ts)도 5초의 타임아웃을 사용하는 것으로 보아, 유튜브 데이터 추출에는 더 긴 처리 시간이 필요한 것으로 판단됩니다.

타임아웃 증가는 유튜브 데이터 처리의 특성을 고려한 적절한 변경으로 보이며, 이미 사용자 경험을 위한 로딩 상태 처리가 잘 구현되어 있습니다.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# API 엔드포인트 사용 패턴 분석
rg -A 2 "externalInstance\.(get|post|put|patch|delete)" --type ts

Length of output: 1344


Script:

#!/bin/bash
# Search for API endpoint usage patterns in components and pages
rg -A 2 "import.*external'.*" --type ts

Length of output: 236


Script:

#!/bin/bash
# Look for any loading state implementations
rg -A 2 "loading|isLoading" --type ts

Length of output: 2275


Script:

#!/bin/bash
# Search for timeout or error handling patterns
rg -A 2 "(catch|error.*handle|timeout)" --type ts

Length of output: 358


Script:

#!/bin/bash
# Find files that might be related to bookmarks feature
fd -e ts -e tsx bookmark

Length of output: 295

src/components/features/LinkForm/LinkForm.stories.tsx (1)

21-21: 타입 안정성 개선이 잘 이루어졌습니다!

유니온 타입을 명시적으로 정의하여 타입 안정성을 향상시킨 것이 좋은 개선사항입니다. 이를 통해 컴파일 타임에 잘못된 스텝 이름 사용을 방지할 수 있습니다.

src/pages/Link.tsx (2)

13-13: 타입 안전성이 향상된 좋은 변경사항입니다!

명시적인 타입 파라미터를 사용하여 코드의 타입 안전성이 개선되었습니다.


19-19: URL 인코딩 처리 확인이 필요합니다.

encodeURIComponent가 제거되었는데, 특수 문자가 포함된 URL이 제대로 처리되는지 확인이 필요합니다.

다음 스크립트로 useYoutubePlace 훅에서 URL 인코딩이 처리되는지 확인해보겠습니다:

✅ Verification successful

URL 인코딩이 정상적으로 처리되고 있습니다.

useYoutubePlace 훅 내부에서 encodeURIComponent를 사용하여 URL을 적절히 인코딩하고 있는 것을 확인했습니다. video/place/${encodeURIComponent(youtubeLink)} 형태로 API를 호출하고 있어 특수 문자가 포함된 URL도 안전하게 처리됩니다.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: URL 인코딩이 useYoutubePlace 훅 내부에서 처리되는지 확인

# useYoutubePlace 훅 구현 확인
ast-grep --pattern 'useYoutubePlace = ($params) => {
  $$$
  encodeURIComponent($$$)
  $$$
}'

# API 호출부분 확인
rg -A 5 "useYoutubePlace.*youtubeLink"

Length of output: 929

src/components/features/LinkForm/ExtractionStatus.tsx (1)

10-10: 코드 모듈화가 잘 되었습니다!

비디오 ID 추출 로직을 별도의 유틸리티 함수로 분리한 것은 좋은 접근입니다. 이는 코드의 재사용성을 높이고 유지보수를 용이하게 만듭니다.

package.json (1)

33-33: jotai 의존성 추가가 적절해 보입니다.

컨텍스트에서 jotai로의 전환은 불필요한 리렌더링을 방지하는데 도움이 될 것으로 보입니다. 다만, 몇 가지 확인이 필요합니다.

번들 크기와 호환성을 확인하기 위해 다음 스크립트를 실행하겠습니다:

✅ Verification successful

Let me verify the React version in the project to ensure compatibility.


jotai 의존성 추가가 안전하게 확인되었습니다

번들 크기가 gzip 기준 3.5KB로 매우 작고, React 18.3.1 버전과 완벽하게 호환됩니다. 불필요한 리렌더링을 방지하기 위한 좋은 선택입니다.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: jotai 의존성의 번들 크기와 호환성 검사

# 번들 크기 확인
curl -s https://bundlephobia.com/api/size?[email protected] | jq '{gzip: .gzip, size: .size}'

# React 버전 호환성 확인
curl -s https://registry.npmjs.org/jotai/2.10.3 | jq '.peerDependencies'

Length of output: 264


Script:

#!/bin/bash
# React 버전 확인
cat package.json | jq '.dependencies.react, .devDependencies.react'

Length of output: 84

src/components/features/ExtractedPlacesList/ExtractedPlacesList.tsx (1)

16-16: 컴포넌트 이름이 더 명확해졌습니다!

ExtractedList에서 ExtractedPlacesList로의 이름 변경은 컴포넌트의 목적을 더 잘 표현합니다.

src/pages/MapView.tsx (2)

11-11: 인증 관련 훅 통합이 잘 되었습니다!

새로운 인증 관련 훅들의 통합이 깔끔하게 이루어졌습니다. useAuthuseSessionDataLoader의 도입으로 인증 상태 관리가 체계적으로 구현되었습니다.

Also applies to: 16-16, 20-20


44-48: 상태 업데이트 시 경쟁 상태 확인이 필요합니다.

extractedData를 처리하는 useEffect 내에서 setStatehandleMarkers를 순차적으로 호출하고 있습니다. 이는 잠재적인 경쟁 상태를 유발할 수 있습니다.

다음 스크립트로 다른 비동기 작업과의 상호작용을 확인해보세요:

src/components/features/BottomSheetContent/BottomSheetContent.tsx (2)

1-2: 필요한 모듈이 올바르게 임포트되었습니다.

FlowTypeuseBottomFunnel이 정확하게 임포트되었습니다.


6-7: 필요한 타입이 정확하게 임포트되었습니다.

YoutubeResponsePlace 타입이 올바르게 임포트되었습니다.

src/hooks/common/useBottomFunnel.tsx (1)

22-22: useAuth 훅의 반환값 구조 확인 필요

const { token } = useAuth();에서 useAuth 훅이 반환하는 객체에 token이 포함되어 있는지 확인해주세요. 만약 user 객체 내에 token이 있다면, 구조 분해 할당을 수정하여 정확한 값을 가져와야 합니다.

src/contexts/MarkerAtom.tsx Show resolved Hide resolved
src/utils/getStorageData.ts Show resolved Hide resolved
src/hooks/api/marker/useNewMarker.ts Show resolved Hide resolved
Comment on lines +17 to +26
export const useNewMarker = (bookmarkId: number, token: string) => {
return useMutation({
mutationFn: (data: NewMarker) =>
post(`api/markers/${bookmarkId}`, data, {
headers: {
Authorization: `Bearer ${token}`,
},
}),
});
};
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

⚠️ Potential issue

에러 처리 및 토큰 검증 개선이 필요합니다.

코드베이스 분석 결과, 다음과 같은 문제점들이 확인되었습니다:

  1. HTTP 클라이언트(axios.ts)에 전역 에러 처리 로직이 없습니다.
  2. 다른 뮤테이션 훅들도 동일하게 에러 처리가 누락되어 있습니다.
  3. API 엔드포인트 구조(api/markers/${bookmarkId})는 일관성 있게 사용되고 있습니다.

다음과 같이 개선하는 것을 제안드립니다:

export const useNewMarker = (bookmarkId: number, token: string) => {
  if (!token) {
    throw new Error('인증 토큰이 필요합니다.');
  }

  return useMutation({
    mutationFn: async (data: NewMarker) => {
      try {
        return await post(`api/markers/${bookmarkId}`, data, {
          headers: {
            Authorization: `Bearer ${token}`,
          },
        });
      } catch (error) {
        if (axios.isAxiosError(error)) {
          if (error.response?.status === 401) {
            throw new Error('인증이 만료되었습니다.');
          }
          throw new Error(`마커 생성 실패: ${error.response?.data?.message || '알 수 없는 오류'}`);
        }
        throw error;
      }
    },
  });
};

추가로, src/lib/axios.ts에 전역 에러 처리 인터셉터 추가를 권장합니다.

🔗 Analysis chain

에러 처리 및 보안 개선이 필요합니다.

  1. 에러 처리가 누락되어 있습니다.
  2. API 엔드포인트 구조 검증이 필요합니다.
  3. 토큰 유효성 검사가 필요합니다.

다음과 같이 개선하는 것을 제안드립니다:

 export const useNewMarker = (bookmarkId: number, token: string) => {
+  if (!token) {
+    throw new Error('인증 토큰이 필요합니다.');
+  }
+
   return useMutation({
     mutationFn: (data: NewMarker) =>
       post(`api/markers/${bookmarkId}`, data, {
         headers: {
           Authorization: `Bearer ${token}`,
         },
       }),
+    onError: (error) => {
+      console.error('마커 생성 중 오류 발생:', error);
+      throw new Error('마커를 생성하는 중 문제가 발생했습니다.');
+    }
   });
 };

API 엔드포인트 구조를 확인하기 위해 다음 스크립트를 실행해 주세요:


Let me gather more information about error handling patterns and token validation in the codebase.


Let me check how errors are handled in the HTTP client layer since there's no explicit error handling in the hooks.


Let me check the axios instance configuration and error handling setup.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: API 엔드포인트 구조 확인

# API 엔드포인트 패턴 검색
rg -g '*.ts' -g '*.js' "api/markers/[^'\"]+" 

# 다른 마커 관련 API 엔드포인트 검색
rg -g '*.ts' -g '*.js' "markers.*post|post.*markers"

Length of output: 377


Script:

#!/bin/bash
# Check error handling patterns in hooks
rg -g '*.ts' 'onError.*throw|throw.*Error' -A 2 -B 2

# Check token validation patterns
rg -g '*.ts' 'if.*!token|token.*validation|validateToken'

# Check other mutation hooks for consistent patterns
ast-grep --pattern 'useMutation({
  $$$
})'

Length of output: 1283


Script:

#!/bin/bash
# Check HTTP client implementation
rg -g '*.ts' 'post.*=.*|export.*post' -A 5 -B 5

# Check if there's global error handling
rg -g '*.ts' 'axios.*interceptor|error.*handler|catch.*error' -A 3 -B 3

Length of output: 1955


Script:

#!/bin/bash
# Check axios instance configuration
rg -g '*.ts' 'const.*instance.*=.*axios' -A 10 -B 2

# Check if there's any global error handling setup
rg -g '*.ts' 'instance\.interceptors|errorHandler|handleError'

Length of output: 639

src/hooks/api/bookmarks/useBookMarkList.ts Show resolved Hide resolved
src/hooks/common/useBottomFunnel.tsx Show resolved Hide resolved
src/hooks/common/useBottomFunnel.tsx Show resolved Hide resolved
src/hooks/common/useBottomFunnel.tsx Show resolved Hide resolved
src/hooks/common/useBottomFunnel.tsx Show resolved Hide resolved
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: 0

🧹 Outside diff range and nitpick comments (1)
src/components/common/Chip/Chip.stories.tsx (1)

30-34: 스토리의 너비 제한 설정에 대한 개선 제안

고정된 너비(w-20)로 제한하는 것은 컴포넌트의 실제 사용 사례를 충분히 보여주지 못할 수 있습니다. 다양한 너비 시나리오를 보여주는 것이 좋겠습니다.

다음과 같이 여러 너비 변형을 추가하는 것을 고려해보세요:

 export const Basic: Story = {
   args: {
     variant: 'medium',
     children: '카페',
+    width: 'default'
   },
   argTypes: {
     variant: {
       control: 'inline-radio',
       options: ['small', 'medium', 'large'],
     },
+    width: {
+      control: 'inline-radio',
+      options: ['narrow', 'default', 'wide'],
+    },
     children: {
       control: 'text',
     },
   },
   render: (args) => {
     return (
-      <div className="w-20">
+      <div className={
+        args.width === 'narrow' ? 'w-20' :
+        args.width === 'wide' ? 'w-40' :
+        'w-28'
+      }>
         <Chip variant={args.variant}>{args.children}</Chip>
       </div>
     );
   },
 };
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between d233c08 and 69c0b07.

📒 Files selected for processing (1)
  • src/components/common/Chip/Chip.stories.tsx (1 hunks)

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: 4

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 69c0b07 and 5580580.

📒 Files selected for processing (2)
  • src/components/features/SearchResultsList/BookmarkSelectionList.tsx (1 hunks)
  • src/utils/getStorageData.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/utils/getStorageData.ts

Comment on lines +18 to +20
const { token } = useAuth();
const { data } = useBookMarkList(token);
const { clearMarkers } = useMarkers();
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

토큰 유효성 검증이 필요합니다

API 호출 전에 토큰의 존재 여부를 확인하는 것이 좋습니다.

 const { token } = useAuth();
+if (!token) {
+  return <div>로그인이 필요합니다</div>;
+}
 const { data } = useBookMarkList(token);
📝 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
const { token } = useAuth();
const { data } = useBookMarkList(token);
const { clearMarkers } = useMarkers();
const { token } = useAuth();
if (!token) {
return <div>로그인이 필요합니다</div>;
}
const { data } = useBookMarkList(token);
const { clearMarkers } = useMarkers();

if (bookmarkId !== 0 && selectedPlace) {
mutation.mutate(selectedPlace, {
onSuccess: () => {
sessionStorage.clear();
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

sessionStorage 클리어 범위를 최소화하세요

sessionStorage.clear()는 모든 세션 스토리지를 비우므로 의도치 않은 부작용이 발생할 수 있습니다. 필요한 항목만 제거하도록 수정하세요.

-sessionStorage.clear();
+sessionStorage.removeItem('bottomSheetType');
+sessionStorage.removeItem('mapData');

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines 78 to 80
<Button variant="primary" size="large" onClick={handleSave}>
저장하기
</Button>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

저장 버튼의 비활성화 상태 처리가 필요합니다

북마크가 선택되지 않은 경우 저장 버튼을 비활성화해야 합니다.

-<Button variant="primary" size="large" onClick={handleSave}>
+<Button 
+  variant="primary" 
+  size="large" 
+  onClick={handleSave}
+  disabled={bookmarkId === 0}
+>
   저장하기
 </Button>

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +43 to +77
<ListCard>
{data?.data.map((item, index) => (
<>
<div key={item.bookmarkId} className={`flex flex-row justify-between items-center `}>
<div className="flex flex-row gap-4 py-2.5 items-center justify-center">
{item.youtuberProfile ? (
<img
src={item.youtuberProfile}
className="w-12 h-12 rounded-full"
alt={`${item.name}의 프로필 이미지`}
/>
) : (
<Icon name="findy1" className="w-11 h-11" />
)}
<div className="flex flex-col py-1">
<Body2 weight="medium">{item.name}</Body2>
<div className="flex flex-row items-center gap-1">
<Icon name="location" size={15} />
<Body3 className=" text-gray-500">{item.markersCount}</Body3>
</div>
</div>
</div>
{item.bookmarkType !== 'YOUTUBE' && (
<Icon
name="check"
className="cursor-pointer h-7"
color={bookmarkId === item.bookmarkId ? 'primary' : 'gray'}
onClick={() => handleToggleSelect(item.bookmarkId)}
/>
)}
</div>
{index < data.data.length - 1 && <hr className="border-dashed pt-2" />}
</>
))}
</ListCard>
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 처리가 누락되었습니다.

 <ListCard>
+  {!data?.data.length && (
+    <div className="flex justify-center items-center p-4">
+      <Body2>저장된 북마크가 없습니다</Body2>
+    </div>
+  )}
+  {!data && (
+    <div className="flex justify-center items-center p-4">
+      <LoadingSpinner />
+    </div>
+  )}
   {data?.data.map((item, index) => (

Committable suggestion skipped: line range outside the PR's diff.

@keemsebin keemsebin merged commit e81c4a2 into main Nov 23, 2024
5 checks passed
@keemsebin keemsebin deleted the feat/#53-searchresultapi branch November 23, 2024 17:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feat 기능
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[FEAT] 검색 장소 저장
2 participants