('popular');
+
+ return (
+
+ {/* 지도 부분 onclick시 바텀시트 열기 */}
+
PIN 목록 바텀시트 미리보기
+
+
+
+
setOpen(false)}>
+
+
+
+
+
+
+ {place.name}
+
+
+ {place.creatorName} 님이
+ 생성한 PIN · {place.distance}m
+
+
+
+
+
+
+
+
+
+
+
+
+ {pins.map((pin) => (
+ -
+ onPinClick?.(pin)} />
+
+ ))}
+
+
+
+
+ );
+}
diff --git a/src/features/pin/components/SongBottom.tsx b/src/features/pin/components/SongBottom.tsx
new file mode 100644
index 00000000..bcfd8353
--- /dev/null
+++ b/src/features/pin/components/SongBottom.tsx
@@ -0,0 +1,80 @@
+import { useMemo, useState } from 'react';
+
+import { BottomSheet } from '@/components/ui/BottomSheet';
+import { SearchInput } from '@/components/ui/SearchInput';
+import { SongCard } from '@/features/pin/components/SongCard';
+import type { Song } from '@/types/pin';
+
+type SongCardProps = {
+ songs?: Song[];
+ onSelect?: (song: Song) => void;
+};
+
+const MOCK_SONGS: Song[] = [
+ { id: '1', title: 'Hype Boy', artist: 'NewJeans' },
+ { id: '2', title: 'Hype Boy (250 Remix)', artist: 'NewJeans' },
+ { id: '3', title: 'Hype Boy (Instrumental)', artist: 'NewJeans' },
+ { id: '4', title: 'Attention', artist: 'NewJeans' },
+];
+
+export default function SongPage({ songs = MOCK_SONGS, onSelect }: SongCardProps) {
+ const [open, setOpen] = useState(false);
+ const [query, setQuery] = useState('');
+
+ const filteredSongs = useMemo(() => {
+ const keyword = query.trim().toLowerCase();
+ if (!keyword) return songs;
+
+ return songs.filter(
+ (song) =>
+ song.title.toLowerCase().includes(keyword) || song.artist.toLowerCase().includes(keyword),
+ );
+ }, [query, songs]);
+
+ return (
+
+ {/* 지도 부분 onclick시 바텀시트 열기 */}
+
노래 선택 바텀시트 미리보기
+
+
+
+
setOpen(false)}>
+
+
+ 노래 선택하기
+
+
+ setQuery(event.target.value)}
+ placeholder="노래를 검색하세요"
+ />
+
+
+
+
+ {filteredSongs.map((song) => (
+ -
+ {
+ onSelect?.(song);
+ setOpen(false);
+ setQuery('');
+ }}
+ />
+
+ ))}
+
+
+
+
+ );
+}
diff --git a/src/features/pin/components/SongCard.tsx b/src/features/pin/components/SongCard.tsx
new file mode 100644
index 00000000..83b3ca7c
--- /dev/null
+++ b/src/features/pin/components/SongCard.tsx
@@ -0,0 +1,22 @@
+import type { Song } from '@/types/pin';
+
+type SongCardProps = {
+ song: Song;
+ onClick?: () => void;
+};
+
+export function SongCard({ song, onClick }: SongCardProps) {
+ return (
+
+ );
+}
diff --git a/src/features/pin/components/SortTabs.tsx b/src/features/pin/components/SortTabs.tsx
new file mode 100644
index 00000000..d176bd3b
--- /dev/null
+++ b/src/features/pin/components/SortTabs.tsx
@@ -0,0 +1,37 @@
+import { cn } from '@/lib/utils';
+import type { PinSort } from '@/types/pin';
+
+type SortTabsProps = {
+ value: PinSort;
+ onChange: (value: PinSort) => void;
+};
+
+const OPTIONS: { value: PinSort; label: string }[] = [
+ { value: 'popular', label: '인기순' },
+ { value: 'latest', label: '최신순' },
+];
+
+export function SortTabs({ value, onChange }: SortTabsProps) {
+ return (
+
+ {OPTIONS.map((option) => {
+ const selected = value === option.value;
+
+ return (
+
+ );
+ })}
+
+ );
+}
diff --git a/src/types/pin.ts b/src/types/pin.ts
new file mode 100644
index 00000000..662850af
--- /dev/null
+++ b/src/types/pin.ts
@@ -0,0 +1,23 @@
+export type PinSort = 'popular' | 'latest';
+
+export type PlaceInfo = {
+ id: string;
+ name: string;
+ creatorName: string;
+ distance: number;
+};
+
+export type Pin = {
+ id: string;
+ title: string;
+ artist: string;
+ pinCount: number;
+ likeCount?: number;
+ liked?: boolean;
+};
+
+export type Song = {
+ id: string;
+ title: string;
+ artist: string;
+};