-
Notifications
You must be signed in to change notification settings - Fork 1
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] 유투브 장소 추출 저장 #56
Changes from all commits
3684bcf
8b9507c
38f894d
7f46b91
6a29b58
b7dd27f
c2504e3
06f61f3
39c377c
150d835
6504afe
34892fb
430dca5
1edaea4
4bb3b70
1e43e16
699df83
a2fa192
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,19 @@ | ||
import { Outlet } from 'react-router-dom'; | ||
|
||
import { Layout } from './components/common/Layout'; | ||
import { MapDataProvider } from './contexts/MapContext'; | ||
import { MarkerProvider } from './contexts/MarkerContext'; | ||
import { useAuth } from './hooks/auth/useAuth'; | ||
|
||
export const App = () => { | ||
useAuth(); | ||
return ( | ||
<Layout> | ||
<MapDataProvider> | ||
<MarkerProvider> | ||
<Outlet /> | ||
<Layout> | ||
<Outlet /> | ||
</Layout> | ||
</MarkerProvider> | ||
</Layout> | ||
</MapDataProvider> | ||
); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,17 @@ | ||
import { cva } from 'class-variance-authority'; | ||
|
||
export const ChipVariants = cva(`bg-primary rounded-md text-white cursor-default px-3 py-1`, { | ||
variants: { | ||
variant: { | ||
small: 'h-6 min-w-6 text-caption', | ||
medium: 'h-7 min-w-7 text-body4', | ||
large: 'h-8 min-w-8 text-body3', | ||
export const ChipVariants = cva( | ||
`flex items-center bg-primary rounded-md text-white cursor-default px-3 py-1`, | ||
{ | ||
variants: { | ||
variant: { | ||
small: 'h-5 min-w-6 text-caption', | ||
medium: 'h-6 min-w-7 text-body4', | ||
large: 'h-7 min-w-8 text-body3', | ||
}, | ||
}, | ||
}, | ||
defaultVariants: { | ||
variant: 'medium', | ||
}, | ||
}); | ||
defaultVariants: { | ||
variant: 'medium', | ||
}, | ||
} | ||
); |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -8,7 +8,7 @@ import { Tooltip } from '../Tooltip'; | |||||
|
||||||
export const SideMenu = ({ variant, position = 'left', className = '', ...props }: Props) => { | ||||||
return ( | ||||||
<button type="button" className="relative" {...props}> | ||||||
<button type="button" className="relative focus:outline-none" {...props}> | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 키보드 접근성 개선이 필요합니다
다음과 같이 포커스 스타일을 추가하는 것을 권장드립니다: - <button type="button" className="relative focus:outline-none" {...props}>
+ <button type="button" className="relative focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500 focus-visible:ring-offset-2" {...props}> 이렇게 하면:
📝 Committable suggestion
Suggested change
|
||||||
<IconButton name={variant} className={cn('peer', className)} /> | ||||||
<Tooltip | ||||||
message={sideMenuTooltip[variant]} | ||||||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,10 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { ListCard } from '@/components/common/ListCard'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
export const BookmarkList = () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
나의 핀디 리스트 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<ListCard>리스트 조회</ListCard> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+3
to
+10
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 컴포넌트 타입 정의 및 구조 개선이 필요합니다. 컴포넌트의 기본 구조를 개선하기 위해 다음 사항들을 고려해주세요:
다음과 같이 수정하는 것을 제안드립니다: +import styled from '@emotion/styled';
import { ListCard } from '@/components/common/ListCard';
-export const BookmarkList = () => {
+interface BookmarkListProps {
+ className?: string;
+}
+
+export const BookmarkList = ({ className }: BookmarkListProps) => {
return (
- <div>
- 나의 핀디 리스트
+ <StyledSection className={className}>
+ <h2>나의 핀디 리스트</h2>
<ListCard>리스트 조회</ListCard>
- </div>
+ </StyledSection>
);
};
+
+const StyledSection = styled.section`
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+
+ h2 {
+ font-size: 18px;
+ font-weight: 600;
+ }
+`; 📝 Committable suggestion
Suggested change
💡 Codebase verification 인증 체크가 필요합니다 BottomSheetContent 컴포넌트에서 token을 확인하고는 있지만, BookmarkList 컴포넌트를 렌더링할 때 인증 상태를 확인하지 않고 있습니다. 다음 사항들을 수정해주세요:
🔗 Analysis chain인증 시스템과의 통합 검증이 필요합니다. 이 컴포넌트는 인증된 사용자만 접근 가능해야 하는 것으로 보입니다. 상위 컴포넌트에서 적절한 인증 체크가 이루어지고 있는지 확인이 필요합니다. Let me check the implementation of useAuth and how BookmarkList is used in the components that import it. 🏁 Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Description: 인증 관련 코드 확인
# BookmarkList 컴포넌트 사용 위치 확인
rg -l "BookmarkList"
# 인증 관련 로직 확인
ast-grep --pattern 'useAuth'
Length of output: 865 Script: #!/bin/bash
# Check useAuth implementation
cat src/hooks/auth/useAuth.ts
# Check how BookmarkList is used in other components
rg -A 5 "BookmarkList" src/components/features/BottomSheetContent/BottomSheetContent.tsx
rg -A 5 "BookmarkList" src/components/features/BottomSheetContent/ExtractedPlaces.tsx
rg -A 5 "BookmarkList" src/components/features/BottomSheetContent/SearchResult.tsx
Length of output: 2042 |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,32 @@ | ||||||||||||||||||||||||||||
import { YoutubeResponse } from '@/hooks/api/link/useYoutubePlace'; | ||||||||||||||||||||||||||||
import { useAuth } from '@/hooks/auth/useAuth'; | ||||||||||||||||||||||||||||
import { Place } from '@/types/naver'; | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
import { ExtractedPlaces } from './ExtractedPlaces'; | ||||||||||||||||||||||||||||
import { SearchResult } from './SearchResult'; | ||||||||||||||||||||||||||||
import { BottomSheetContentProps } from './types'; | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
import { BookmarkList } from '../BookmarkList/BookmarkList'; | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
export const BottomSheetContent = ({ type, data }: BottomSheetContentProps) => { | ||||||||||||||||||||||||||||
const { token } = useAuth(); | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
if (type === 'search') { | ||||||||||||||||||||||||||||
return <SearchResult places={data as Place[]} />; | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
Comment on lines
+14
to
+16
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 타입 검증 로직 추가 필요
if (type === 'search') {
+ const isPlaceArray = (data: unknown): data is Place[] =>
+ Array.isArray(data) && data.every(item =>
+ typeof item === 'object' && item !== null && 'name' in item
+ );
+ if (!isPlaceArray(data)) {
+ return <div>잘못된 데이터 형식입니다.</div>;
+ }
- return <SearchResult places={data as Place[]} />;
+ return <SearchResult places={data} />;
} 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
if (type === 'extract') { | ||||||||||||||||||||||||||||
return <ExtractedPlaces places={data as YoutubeResponse} />; | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
Comment on lines
+18
to
+20
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion YouTube 응답 데이터 검증 로직 추가 필요
if (type === 'extract') {
+ const isYoutubeResponse = (data: unknown): data is YoutubeResponse =>
+ typeof data === 'object' && data !== null && 'places' in data;
+ if (!isYoutubeResponse(data)) {
+ return <div>유효하지 않은 YouTube 데이터입니다.</div>;
+ }
- return <ExtractedPlaces places={data as YoutubeResponse} />;
+ return <ExtractedPlaces places={data} />;
} 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
if (token && type === 'list') { | ||||||||||||||||||||||||||||
return <BookmarkList />; | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
// TODO : 로그인하지 않은 경우 로그인 유도 | ||||||||||||||||||||||||||||
return ( | ||||||||||||||||||||||||||||
<div className=" flex items-center justify-center py-8"> | ||||||||||||||||||||||||||||
<p className="text-gray-500">로그인한 사용자만 즐겨찾기가 가능합니다.</p> | ||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||
}; |
Original file line number | Diff line number | Diff line change | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,27 @@ | ||||||||||||||||
import { extractedPlaceStepNames } from '@/constants/funnelStep'; | ||||||||||||||||
import { YoutubeResponse } from '@/hooks/api/link/useYoutubePlace'; | ||||||||||||||||
import { useFunnel } from '@/hooks/common/useFunnel'; | ||||||||||||||||
|
||||||||||||||||
import { BookmarkList } from '../BookmarkList/BookmarkList'; | ||||||||||||||||
import { ExtractedList } from '../ExtractedPlacesList/ExtractedList'; | ||||||||||||||||
|
||||||||||||||||
export type ExtractedPlacesProps = { | ||||||||||||||||
places: YoutubeResponse; | ||||||||||||||||
}; | ||||||||||||||||
export const ExtractedPlaces = ({ places }: ExtractedPlacesProps) => { | ||||||||||||||||
const { Funnel, Step, setStep } = useFunnel(extractedPlaceStepNames[0]); | ||||||||||||||||
|
||||||||||||||||
Comment on lines
+11
to
+13
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 에러 처리와 로딩 상태 관리가 필요합니다. places 데이터가 유효하지 않은 경우나 로딩 중인 상태에 대한 처리가 없습니다. 다음과 같이 개선해보세요: export const ExtractedPlaces = ({ places }: ExtractedPlacesProps) => {
const { Funnel, Step, setStep } = useFunnel(extractedPlaceStepNames[0]);
+ if (!places) {
+ return <div>데이터를 불러올 수 없습니다.</div>;
+ } 📝 Committable suggestion
Suggested change
|
||||||||||||||||
return ( | ||||||||||||||||
<> | ||||||||||||||||
<Funnel> | ||||||||||||||||
<Step name="추출장소"> | ||||||||||||||||
<ExtractedList places={places} onNext={() => setStep('리스트')} /> | ||||||||||||||||
</Step> | ||||||||||||||||
{/* TODO : 리스트 노출 */} | ||||||||||||||||
<Step name="리스트"> | ||||||||||||||||
<BookmarkList /> | ||||||||||||||||
</Step> | ||||||||||||||||
</Funnel> | ||||||||||||||||
</> | ||||||||||||||||
); | ||||||||||||||||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { searchPlaceStepNames } from '@/constants/funnelStep'; | ||
import { useFunnel } from '@/hooks/common/useFunnel'; | ||
import { Place } from '@/types/naver'; | ||
|
||
import { BookmarkList } from '../BookmarkList/BookmarkList'; | ||
import { SearchList } from '../SearchList/SearchList'; | ||
|
||
export type SearchResultProps = { | ||
places: Place[]; | ||
}; | ||
export const SearchResult = ({ places }: SearchResultProps) => { | ||
const { Funnel, Step, setStep } = useFunnel(searchPlaceStepNames[0]); | ||
return ( | ||
<> | ||
<Funnel> | ||
<Step name="추출장소"> | ||
<SearchList places={places} onNext={() => setStep('리스트선택')} /> | ||
</Step> | ||
{/* TODO : 리스트 선택, 리스트 노출 */} | ||
<Step name="리스트선택">2. 리스트 선택</Step> | ||
<Step name="리스트"> | ||
<BookmarkList /> | ||
</Step> | ||
</Funnel> | ||
</> | ||
); | ||
}; | ||
Comment on lines
+11
to
+27
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 컴포넌트 구현에 대한 여러 개선사항이 있습니다.
다음과 같은 개선을 제안드립니다: export const SearchResult = ({ places }: SearchResultProps) => {
const { Funnel, Step, setStep } = useFunnel(searchPlaceStepNames[0]);
+
+ if (places.length === 0) {
+ return <EmptyState message="검색 결과가 없습니다" />;
+ }
+
return (
<>
<Funnel>
<Step name="추출장소">
<SearchList places={places} onNext={() => setStep('리스트선택')} />
</Step>
- {/* TODO : 리스트 선택, 리스트 노출 */}
- <Step name="리스트선택">2. 리스트 선택</Step>
+ <Step name="리스트선택">
+ <ListSelector onSelect={(listId) => {
+ // 선택된 리스트 처리 로직
+ setStep('리스트');
+ }} />
+ </Step>
<Step name="리스트">
<BookmarkList />
</Step>
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export { ExtractedPlaces } from './ExtractedPlaces'; | ||
export { SearchResult } from './SearchResult'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { YoutubeResponse } from '@/hooks/api/link/useYoutubePlace'; | ||
import { Place } from '@/types/naver'; | ||
|
||
export type BottomSheetContentProps = { | ||
type: 'search' | 'extract' | 'list' | null; | ||
data?: Place[] | YoutubeResponse | null; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
useAuth 훅의 에러 처리 추가 필요
useAuth 훅의 호출 결과에 대한 에러 처리가 없습니다. 인증 실패나 네트워크 오류 등의 상황에 대한 처리가 필요합니다.
다음과 같은 에러 처리를 추가하는 것을 제안드립니다:
📝 Committable suggestion