From bedcce19d8e74a40f0b87e188bc3eaf57eb1fd42 Mon Sep 17 00:00:00 2001 From: GulSam00 Date: Mon, 25 May 2026 21:26:29 +0900 Subject: [PATCH 1/7] =?UTF-8?q?feat=20:=20=ED=95=9C=EA=B5=AD=EC=96=B4=20?= =?UTF-8?q?=EC=A0=9C=EB=AA=A9/=EA=B0=80=EC=88=98=20=EB=B0=8F=20=EB=85=B8?= =?UTF-8?q?=EB=9E=98=EB=B0=A9=20=EB=B2=88=ED=98=B8=20=EA=B2=80=EC=83=89=20?= =?UTF-8?q?=EC=A7=80=EC=9B=90=20(#244)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/src/app/api/search/route.ts | 55 ++++++++++++++++++++++++-- apps/web/src/app/search/HomePage.tsx | 5 ++- apps/web/src/hooks/useSaveSongModal.ts | 3 +- apps/web/src/hooks/useSearchSong.ts | 4 +- apps/web/src/types/song.ts | 2 + 5 files changed, 59 insertions(+), 10 deletions(-) diff --git a/apps/web/src/app/api/search/route.ts b/apps/web/src/app/api/search/route.ts index e0dd9fac..32b5e2bf 100644 --- a/apps/web/src/app/api/search/route.ts +++ b/apps/web/src/app/api/search/route.ts @@ -24,7 +24,18 @@ interface DBSong extends Song { function applyExactFilter(baseQuery: any, type: string, searchText: string) { if (type === 'all') { - return baseQuery.or(`title.ilike.${searchText},artist.ilike.${searchText}`); + return baseQuery.or( + `title.ilike.${searchText},title_ko.ilike.${searchText},artist.ilike.${searchText},artist_ko.ilike.${searchText}`, + ); + } + if (type === 'title') { + return baseQuery.or(`title.ilike.${searchText},title_ko.ilike.${searchText}`); + } + if (type === 'artist') { + return baseQuery.or(`artist.ilike.${searchText},artist_ko.ilike.${searchText}`); + } + if (type === 'number') { + return baseQuery.or(`num_tj.eq.${searchText},num_ky.eq.${searchText}`); } return baseQuery.ilike(type, searchText); } @@ -32,9 +43,25 @@ function applyExactFilter(baseQuery: any, type: string, searchText: string) { function applyPartialFilter(baseQuery: any, type: string, searchText: string) { if (type === 'all') { return baseQuery - .or(`title.ilike.%${searchText}%,artist.ilike.%${searchText}%`) + .or( + `title.ilike.%${searchText}%,title_ko.ilike.%${searchText}%,artist.ilike.%${searchText}%,artist_ko.ilike.%${searchText}%`, + ) .not('title', 'ilike', searchText) - .not('artist', 'ilike', searchText); + .not('title_ko', 'ilike', searchText) + .not('artist', 'ilike', searchText) + .not('artist_ko', 'ilike', searchText); + } + if (type === 'title') { + return baseQuery + .or(`title.ilike.%${searchText}%,title_ko.ilike.%${searchText}%`) + .not('title', 'ilike', searchText) + .not('title_ko', 'ilike', searchText); + } + if (type === 'artist') { + return baseQuery + .or(`artist.ilike.%${searchText}%,artist_ko.ilike.%${searchText}%`) + .not('artist', 'ilike', searchText) + .not('artist_ko', 'ilike', searchText); } return baseQuery.ilike(type, `%${searchText}%`).not(type, 'ilike', searchText); } @@ -50,6 +77,26 @@ async function executeSearchQueries( ): Promise<{ data: DBSong[]; hasNext: boolean } | { error: string }> { const size = to - from + 1; + // 번호 검색은 정확 매칭만 지원 (부분 일치 단계 스킵) + if (type === 'number') { + const exactCountResult = await applyExactFilter( + supabase.from('songs').select(selectClause, { count: 'exact', head: true }), + type, + query, + ); + if (exactCountResult.error) return { error: exactCountResult.error.message }; + const exactTotal = exactCountResult.count ?? 0; + + const exactQuery = applyExactFilter(supabase.from('songs').select(selectClause), type, query); + const { data, error } = await exactQuery.order(order).range(from, to); + if (error) return { error: error.message }; + + return { + data: (data as DBSong[]) ?? [], + hasNext: exactTotal > to + 1, + }; + } + // 1. 정확 일치 / 부분 일치 각각의 총 개수를 병렬로 조회 const exactCountQuery = applyExactFilter( supabase.from('songs').select(selectClause, { count: 'exact', head: true }), @@ -134,7 +181,7 @@ export async function GET(request: Request): Promise - + {( [ ['all', '전체'], ['title', '제목'], ['artist', '가수'], + ['number', '번호'], ] as const ).map(([value, label]) => ( Date: Mon, 25 May 2026 21:26:34 +0900 Subject: [PATCH 2/7] =?UTF-8?q?fix=20:=20=EC=A6=90=EA=B2=A8=EC=B0=BE?= =?UTF-8?q?=EA=B8=B0=C2=B7=EC=A0=80=EC=9E=A5=20=EA=B3=A1=20API=20=ED=95=9C?= =?UTF-8?q?=EA=B5=AD=EC=96=B4=20=EC=A0=9C=EB=AA=A9/=EA=B0=80=EC=88=98=20?= =?UTF-8?q?=ED=95=84=EB=93=9C=20=EB=88=84=EB=9D=BD=20=EB=B3=B4=EC=99=84=20?= =?UTF-8?q?(#244)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/src/app/api/songs/like/route.ts | 2 ++ apps/web/src/app/api/songs/save/route.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/apps/web/src/app/api/songs/like/route.ts b/apps/web/src/app/api/songs/like/route.ts index 23d01bd2..84669571 100644 --- a/apps/web/src/app/api/songs/like/route.ts +++ b/apps/web/src/app/api/songs/like/route.ts @@ -36,6 +36,8 @@ export async function GET(): Promise>> created_at: item.created_at, title: item.songs.title, artist: item.songs.artist, + title_ko: item.songs.title_ko, + artist_ko: item.songs.artist_ko, num_tj: item.songs.num_tj, num_ky: item.songs.num_ky, })); diff --git a/apps/web/src/app/api/songs/save/route.ts b/apps/web/src/app/api/songs/save/route.ts index 7688ffdc..4bd9500d 100644 --- a/apps/web/src/app/api/songs/save/route.ts +++ b/apps/web/src/app/api/songs/save/route.ts @@ -38,6 +38,8 @@ export async function GET(): Promise>> { updated_at: item.updated_at, title: item.songs.title, artist: item.songs.artist, + title_ko: item.songs.title_ko, + artist_ko: item.songs.artist_ko, num_tj: item.songs.num_tj, num_ky: item.songs.num_ky, })); From 5d118ba5d61f56bd73f1a691a2b65da7c344e255 Mon Sep 17 00:00:00 2001 From: GulSam00 Date: Mon, 25 May 2026 21:26:53 +0900 Subject: [PATCH 3/7] =?UTF-8?q?feat=20:=20=EA=B3=A1=20=EB=AA=A9=EB=A1=9D?= =?UTF-8?q?=C2=B7=EC=B6=94=EC=B2=9C=C2=B7=EC=9D=B8=EA=B8=B0=EC=B0=A8?= =?UTF-8?q?=ED=8A=B8=C2=B7=EC=A0=80=EC=9E=A5=20=EB=AA=A8=EB=8B=AC=EC=97=90?= =?UTF-8?q?=20=ED=95=9C=EA=B5=AD=EC=96=B4=20=EC=A0=9C=EB=AA=A9/=EA=B0=80?= =?UTF-8?q?=EC=88=98=20=ED=91=9C=EC=8B=9C=20(#244)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/src/app/info/like/SongItem.tsx | 6 ++++++ apps/web/src/app/info/save/FolderCard.tsx | 6 ++++++ apps/web/src/app/search/AddFolderModal.tsx | 8 +++++++- apps/web/src/app/search/SearchResultCard.tsx | 2 ++ apps/web/src/app/tosing/ModalSongItem.tsx | 6 ++++++ apps/web/src/components/RankingItem.tsx | 14 +++++++++++++- apps/web/src/components/ThumbUpModal.tsx | 10 ++++++++++ 7 files changed, 50 insertions(+), 2 deletions(-) diff --git a/apps/web/src/app/info/like/SongItem.tsx b/apps/web/src/app/info/like/SongItem.tsx index 6c5b8906..6d24d491 100644 --- a/apps/web/src/app/info/like/SongItem.tsx +++ b/apps/web/src/app/info/like/SongItem.tsx @@ -22,7 +22,13 @@ export default function SongItem({ />
{song.title} + {song.title_ko && song.title_ko !== song.title && ( + {song.title_ko} + )} {song.artist} + {song.artist_ko && song.artist_ko !== song.artist && ( + {song.artist_ko} + )}
); diff --git a/apps/web/src/app/info/save/FolderCard.tsx b/apps/web/src/app/info/save/FolderCard.tsx index 5217c4b2..993bb113 100644 --- a/apps/web/src/app/info/save/FolderCard.tsx +++ b/apps/web/src/app/info/save/FolderCard.tsx @@ -111,7 +111,13 @@ export default function FolderCard({

{song.title}

+ {song.title_ko && song.title_ko !== song.title && ( +

{song.title_ko}

+ )}

{song.artist}

+ {song.artist_ko && song.artist_ko !== song.artist && ( +

{song.artist_ko}

+ )}
diff --git a/apps/web/src/app/search/AddFolderModal.tsx b/apps/web/src/app/search/AddFolderModal.tsx index 6f6ad0ad..38159b35 100644 --- a/apps/web/src/app/search/AddFolderModal.tsx +++ b/apps/web/src/app/search/AddFolderModal.tsx @@ -46,7 +46,7 @@ export default function AddFolderModal({ const [folderName, setFolderName] = useState(''); const [isExistingPlaylist, setIsExistingPlaylist] = useState(false); - const { id: songId, title, artist } = song; + const { id: songId, title, artist, title_ko, artist_ko } = song; const LOGIC_TEXT = modalType === 'POST' ? '저장' : '수정'; @@ -114,7 +114,13 @@ export default function AddFolderModal({ {/* 곡 정보 */}
{title} + {title_ko && title_ko !== title && ( + {title_ko} + )} {artist} + {artist_ko && artist_ko !== artist && ( + {artist_ko} + )}
diff --git a/apps/web/src/app/search/SearchResultCard.tsx b/apps/web/src/app/search/SearchResultCard.tsx index 217932c1..c82c5cd9 100644 --- a/apps/web/src/app/search/SearchResultCard.tsx +++ b/apps/web/src/app/search/SearchResultCard.tsx @@ -143,6 +143,8 @@ export default function SearchResultCard({ songId={id} title={title} artist={artist} + title_ko={title_ko} + artist_ko={artist_ko} thumb={thumb || 0} handleClose={() => setOpen(false)} /> diff --git a/apps/web/src/app/tosing/ModalSongItem.tsx b/apps/web/src/app/tosing/ModalSongItem.tsx index bdf0c534..e6d81997 100644 --- a/apps/web/src/app/tosing/ModalSongItem.tsx +++ b/apps/web/src/app/tosing/ModalSongItem.tsx @@ -28,7 +28,13 @@ export default function ModalSongItem({ />
{song.title} + {song.title_ko && song.title_ko !== song.title && ( + {song.title_ko} + )} {song.artist} + {song.artist_ko && song.artist_ko !== song.artist && ( + {song.artist_ko} + )}
); diff --git a/apps/web/src/components/RankingItem.tsx b/apps/web/src/components/RankingItem.tsx index 5ac27bb3..f1db5895 100644 --- a/apps/web/src/components/RankingItem.tsx +++ b/apps/web/src/components/RankingItem.tsx @@ -14,6 +14,8 @@ interface RankingItemProps { rank: number; title: string; artist: string; + title_ko?: string; + artist_ko?: string; num_tj: string; num_ky: string; value: number; @@ -25,6 +27,8 @@ export default function RankingItem({ rank, title, artist, + title_ko, + artist_ko, num_tj, num_ky, value, @@ -68,7 +72,13 @@ export default function RankingItem({
{title} + {title_ko && title_ko !== title && ( + {title_ko} + )} {artist} + {artist_ko && artist_ko !== artist && ( + {artist_ko} + )}
@@ -82,7 +92,7 @@ export default function RankingItem({
-
+
From 33a84c9afae69410dd6199d385db77ae02c1ed0a Mon Sep 17 00:00:00 2001 From: GulSam00 Date: Mon, 25 May 2026 21:26:57 +0900 Subject: [PATCH 4/7] =?UTF-8?q?fix=20:=20=ED=99=8D=EB=B3=B4=20=EB=AA=A8?= =?UTF-8?q?=EB=8B=AC=20=EB=AA=A8=EB=B0=94=EC=9D=BC=20=EB=86=92=EC=9D=B4=20?= =?UTF-8?q?=EC=9E=98=EB=A6=BC=20=EC=88=98=EC=A0=95=20(#244)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/src/app/search/SearchResultCard.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/src/app/search/SearchResultCard.tsx b/apps/web/src/app/search/SearchResultCard.tsx index c82c5cd9..3d45d33c 100644 --- a/apps/web/src/app/search/SearchResultCard.tsx +++ b/apps/web/src/app/search/SearchResultCard.tsx @@ -261,7 +261,7 @@ export default function SearchResultCard({ - + Date: Mon, 25 May 2026 21:27:02 +0900 Subject: [PATCH 5/7] =?UTF-8?q?refactor=20:=20=EC=A0=84=EA=B4=91=ED=8C=90?= =?UTF-8?q?=20=ED=95=9C=EA=B5=AD=EC=96=B4=20=EB=B2=88=EC=97=AD=20=EC=84=B8?= =?UTF-8?q?=EB=A1=9C=20=EB=B0=B0=EC=B9=98=20=EB=B0=8F=20=EB=86=92=EC=9D=B4?= =?UTF-8?q?=20=EC=A1=B0=EC=A0=95=20(#244)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/src/components/PromotionBanner.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/web/src/components/PromotionBanner.tsx b/apps/web/src/components/PromotionBanner.tsx index 857bd204..b2568095 100644 --- a/apps/web/src/components/PromotionBanner.tsx +++ b/apps/web/src/components/PromotionBanner.tsx @@ -86,7 +86,7 @@ export default function PromotionBanner() { transition={{ duration: 0.25, ease: 'easeInOut' }} className="overflow-hidden" > -
+
-
+
{current.title} {hasKoTitle && ( - + {current.title_ko} )}
-
+
{current.artist} {hasKoArtist && ( - + {current.artist_ko} )} From 256d98c78a5258fffdf555bce3053d92623d14ec Mon Sep 17 00:00:00 2001 From: GulSam00 Date: Mon, 25 May 2026 21:27:07 +0900 Subject: [PATCH 6/7] =?UTF-8?q?chore=20:=20=ED=99=8D=EB=B3=B4=20=EB=AA=A8?= =?UTF-8?q?=EB=8B=AC=20=EC=95=88=EB=82=B4=20=EB=AC=B8=EA=B5=AC=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EB=B0=8F=20sitemap=20=EC=9E=AC=EC=83=9D=EC=84=B1?= =?UTF-8?q?=20(#244)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/public/sitemap-0.xml | 6 +++--- apps/web/src/components/SongPromotionModal.tsx | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/apps/web/public/sitemap-0.xml b/apps/web/public/sitemap-0.xml index c6d2ee01..bed39794 100644 --- a/apps/web/public/sitemap-0.xml +++ b/apps/web/public/sitemap-0.xml @@ -1,6 +1,6 @@ -https://www.singcode.kr/manifest.webmanifest2026-05-19T13:17:55.218Zweekly0.7 -https://www.singcode.kr2026-05-19T13:17:55.220Zweekly0.7 -https://www.singcode.kr/patch-notes2026-05-19T13:17:55.220Zweekly0.7 +https://www.singcode.kr/manifest.webmanifest2026-05-25T11:26:49.726Zweekly0.7 +https://www.singcode.kr/patch-notes2026-05-25T11:26:49.727Zweekly0.7 +https://www.singcode.kr2026-05-25T11:26:49.727Zweekly0.7 \ No newline at end of file diff --git a/apps/web/src/components/SongPromotionModal.tsx b/apps/web/src/components/SongPromotionModal.tsx index 0fd33990..e4812184 100644 --- a/apps/web/src/components/SongPromotionModal.tsx +++ b/apps/web/src/components/SongPromotionModal.tsx @@ -139,9 +139,7 @@ export default function SongPromotionModal({
곡 홍보하기 - - 하루 50P를 소모해 검색 페이지 전광판에 곡을 홍보하세요. - + 하루 단위로 전광판에 곡을 홍보할 수 있어요.
From 200eeb2907f40e16dfca5eb347f1603df0bd6c2e Mon Sep 17 00:00:00 2001 From: GulSam00 Date: Mon, 25 May 2026 21:51:30 +0900 Subject: [PATCH 7/7] =?UTF-8?q?feat=20:=20=EA=B2=80=EC=83=89=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=20=EC=A7=91=EA=B3=84=EB=A5=BC=20=EC=B5=9C=EA=B7=BC=20?= =?UTF-8?q?15=EC=9D=BC=EB=A1=9C=20=EC=A0=9C=ED=95=9C=20(#244)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/web/src/app/api/search/log/route.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/web/src/app/api/search/log/route.ts b/apps/web/src/app/api/search/log/route.ts index 27526586..c48d7392 100644 --- a/apps/web/src/app/api/search/log/route.ts +++ b/apps/web/src/app/api/search/log/route.ts @@ -1,3 +1,4 @@ +import { subDays } from 'date-fns'; import { NextResponse } from 'next/server'; import createClient from '@/lib/supabase/server'; @@ -11,7 +12,13 @@ interface SearchLogCount { export async function GET(): Promise>> { try { const supabase = await createClient(); - const { data, error } = await supabase.from('search_logs').select('text'); + + // 최근 15일간의 검색 로그만 집계 + const fifteenDaysAgo = subDays(new Date(), 15).toISOString(); + const { data, error } = await supabase + .from('search_logs') + .select('text') + .gte('created_at', fifteenDaysAgo); if (error) throw error;