@@ -128,7 +126,7 @@ function MobileBirthday() {
🎁
- {year}년 {decodedMemberName} 생일카페 정보가 준비 중입니다
+ {year}년 {member?.name} 생일카페 정보가 준비 중입니다
생일카페 정보가 등록되면 이곳에 표시됩니다
diff --git a/frontend/src/pages/mobile/schedule/Schedule.jsx b/frontend/src/pages/mobile/schedule/Schedule.jsx
index 69c0432..a602287 100644
--- a/frontend/src/pages/mobile/schedule/Schedule.jsx
+++ b/frontend/src/pages/mobile/schedule/Schedule.jsx
@@ -777,11 +777,7 @@ function MobileSchedule() {
key={schedule.id}
schedule={schedule}
delay={index * 0.05}
- onClick={() => {
- const scheduleYear = new Date(schedule.date).getFullYear();
- const memberName = schedule.member_names;
- navigate(`/birthday/${encodeURIComponent(memberName)}/${scheduleYear}`);
- }}
+ onClick={() => navigate(`/schedule/${schedule.id}`)}
/>
);
}
diff --git a/frontend/src/pages/mobile/schedule/ScheduleDetail.jsx b/frontend/src/pages/mobile/schedule/ScheduleDetail.jsx
index cef689c..c19cb00 100644
--- a/frontend/src/pages/mobile/schedule/ScheduleDetail.jsx
+++ b/frontend/src/pages/mobile/schedule/ScheduleDetail.jsx
@@ -6,6 +6,34 @@ import { Calendar, Clock, ChevronLeft, Link2, X, ChevronRight } from 'lucide-rea
import Linkify from 'react-linkify';
import { getSchedule } from '@/api';
import { decodeHtmlEntities, formatFullDate, formatTime, formatXDateTimeWithTime } from '@/utils';
+import Birthday from './Birthday';
+
+/**
+ * 특수 일정 ID 파싱
+ * @param {string} id - 일정 ID
+ * @returns {object|null} { type, year, nameEn } 또는 null
+ */
+function parseSpecialId(id) {
+ // birthday-{year}-{nameEn} 형식
+ const birthdayMatch = id.match(/^birthday-(\d{4})-(.+)$/);
+ if (birthdayMatch) {
+ return { type: 'birthday', year: birthdayMatch[1], nameEn: birthdayMatch[2] };
+ }
+
+ // debut-{year} 형식
+ const debutMatch = id.match(/^debut-(\d{4})$/);
+ if (debutMatch) {
+ return { type: 'debut', year: debutMatch[1] };
+ }
+
+ // anniversary-{year} 형식
+ const anniversaryMatch = id.match(/^anniversary-(\d{4})$/);
+ if (anniversaryMatch) {
+ return { type: 'anniversary', year: anniversaryMatch[1] };
+ }
+
+ return null;
+}
/**
* 전체화면 시 자동 가로 회전 훅 (숏츠가 아닐 때만)
@@ -393,6 +421,12 @@ function MobileDefaultSection({ schedule }) {
function MobileScheduleDetail() {
const { id } = useParams();
+ // 특수 일정 ID 체크
+ const specialId = parseSpecialId(id);
+ if (specialId?.type === 'birthday') {
+ return
;
+ }
+
// 모바일 레이아웃 활성화
useEffect(() => {
document.documentElement.classList.add('mobile-layout');
diff --git a/frontend/src/pages/pc/public/schedule/Birthday.jsx b/frontend/src/pages/pc/public/schedule/Birthday.jsx
index 620c7c3..c44a6ff 100644
--- a/frontend/src/pages/pc/public/schedule/Birthday.jsx
+++ b/frontend/src/pages/pc/public/schedule/Birthday.jsx
@@ -1,4 +1,4 @@
-import { useParams, Link } from 'react-router-dom';
+import { Link } from 'react-router-dom';
import { useQuery } from '@tanstack/react-query';
import { motion } from 'framer-motion';
import { ChevronRight } from 'lucide-react';
@@ -6,25 +6,23 @@ import { fetchApi } from '@/api';
/**
* PC 생일 페이지
+ * @param {object} props
+ * @param {string} props.year - 연도
+ * @param {string} props.nameEn - 멤버 영문 이름 (소문자)
*/
-function PCBirthday() {
- const { memberName, year } = useParams();
-
- // URL 디코딩
- const decodedMemberName = decodeURIComponent(memberName || '');
-
- // 멤버 정보 조회
+function PCBirthday({ year, nameEn }) {
+ // 멤버 정보 조회 (영문 이름으로)
const {
data: member,
isLoading: memberLoading,
error,
} = useQuery({
- queryKey: ['member', decodedMemberName],
- queryFn: () => fetchApi(`/members/${encodeURIComponent(decodedMemberName)}`),
- enabled: !!decodedMemberName,
+ queryKey: ['member', nameEn],
+ queryFn: () => fetchApi(`/members/${encodeURIComponent(nameEn)}`),
+ enabled: !!nameEn,
});
- if (!decodedMemberName || error) {
+ if (!nameEn || error) {
return (
@@ -125,7 +123,7 @@ function PCBirthday() {
🎁
- {year}년 {decodedMemberName} 생일카페 정보가 준비 중입니다
+ {year}년 {member?.name} 생일카페 정보가 준비 중입니다
생일카페 정보가 등록되면 이곳에 표시됩니다
diff --git a/frontend/src/pages/pc/public/schedule/Schedule.jsx b/frontend/src/pages/pc/public/schedule/Schedule.jsx
index ab0276e..ae9a58d 100644
--- a/frontend/src/pages/pc/public/schedule/Schedule.jsx
+++ b/frontend/src/pages/pc/public/schedule/Schedule.jsx
@@ -262,15 +262,17 @@ function PCSchedule() {
// 일정 클릭 핸들러
const handleScheduleClick = (schedule) => {
- if (schedule.is_birthday || String(schedule.id).startsWith('birthday-')) {
- const scheduleYear = new Date(schedule.date).getFullYear();
- navigate(`/birthday/${encodeURIComponent(schedule.member_names)}/${scheduleYear}`);
+ // 생일, 데뷔, 주년 등 특수 일정
+ if (schedule.is_birthday || schedule.is_debut || schedule.is_anniversary) {
+ navigate(`/schedule/${schedule.id}`);
return;
}
+ // 유튜브(2), X(3), 콘서트(6) 카테고리
if ([2, 3, 6].includes(schedule.category_id)) {
navigate(`/schedule/${schedule.id}`);
return;
}
+ // 소스 URL이 있으면 외부 링크로
if (!schedule.description && schedule.source?.url) {
window.open(schedule.source.url, '_blank');
} else {
diff --git a/frontend/src/pages/pc/public/schedule/ScheduleDetail.jsx b/frontend/src/pages/pc/public/schedule/ScheduleDetail.jsx
index 44848a4..c664ba3 100644
--- a/frontend/src/pages/pc/public/schedule/ScheduleDetail.jsx
+++ b/frontend/src/pages/pc/public/schedule/ScheduleDetail.jsx
@@ -6,6 +6,34 @@ import { getSchedule } from '@/api';
// 섹션 컴포넌트들
import { YoutubeSection, XSection, DefaultSection, decodeHtmlEntities } from './sections';
+import Birthday from './Birthday';
+
+/**
+ * 특수 일정 ID 파싱
+ * @param {string} id - 일정 ID
+ * @returns {object|null} { type, year, nameEn } 또는 null
+ */
+function parseSpecialId(id) {
+ // birthday-{year}-{nameEn} 형식
+ const birthdayMatch = id.match(/^birthday-(\d{4})-(.+)$/);
+ if (birthdayMatch) {
+ return { type: 'birthday', year: birthdayMatch[1], nameEn: birthdayMatch[2] };
+ }
+
+ // debut-{year} 형식
+ const debutMatch = id.match(/^debut-(\d{4})$/);
+ if (debutMatch) {
+ return { type: 'debut', year: debutMatch[1] };
+ }
+
+ // anniversary-{year} 형식
+ const anniversaryMatch = id.match(/^anniversary-(\d{4})$/);
+ if (anniversaryMatch) {
+ return { type: 'anniversary', year: anniversaryMatch[1] };
+ }
+
+ return null;
+}
/**
* PC 일정 상세 페이지
@@ -13,6 +41,12 @@ import { YoutubeSection, XSection, DefaultSection, decodeHtmlEntities } from './
function PCScheduleDetail() {
const { id } = useParams();
+ // 특수 일정 ID 체크
+ const specialId = parseSpecialId(id);
+ if (specialId?.type === 'birthday') {
+ return
;
+ }
+
const {
data: schedule,
isLoading,
diff --git a/frontend/src/routes/mobile/index.jsx b/frontend/src/routes/mobile/index.jsx
index a342752..29d7111 100644
--- a/frontend/src/routes/mobile/index.jsx
+++ b/frontend/src/routes/mobile/index.jsx
@@ -9,7 +9,6 @@ import Members from '@/pages/mobile/members/Members';
import MembersPreview from '@/pages/mobile/members/MembersPreview';
import Schedule from '@/pages/mobile/schedule/Schedule';
import ScheduleDetail from '@/pages/mobile/schedule/ScheduleDetail';
-import Birthday from '@/pages/mobile/schedule/Birthday';
import Album from '@/pages/mobile/album/Album';
import AlbumDetail from '@/pages/mobile/album/AlbumDetail';
import TrackDetail from '@/pages/mobile/album/TrackDetail';
@@ -55,7 +54,6 @@ export default function MobileRoutes() {
}
/>
} />
-
} />
} />
} />
} />
-
} />
} />
} />
} />