,
+ timeline: Array<{ slideId: string; title?: string | null; timestampMs: number }>,
): { slides: SlideDetail[]; slideChangeTimes: number[] } {
if (!timeline.length) {
const slides = sourceSlides.map((slide, index) => ({
@@ -101,7 +102,7 @@ function mapSlidesByTimeline(
return {
slideId: String(item.slideId),
projectId: fallbackProjectId,
- title: `슬라이드 ${index + 1}`,
+ title: getSlideTitle(item.title, index + 1),
slideNum: index + 1,
imageUrl: '',
createdAt: now,
@@ -215,6 +216,7 @@ export function useFeedbackVideo(
)
.map((slide) => ({
slideId: String(slide.slideId),
+ title: slide.title,
timestampMs: normalizeTimestampMs(slide.timestampMs),
}));
@@ -255,6 +257,7 @@ export function useFeedbackVideo(
) {
timelineSlides = timelineResult.value.data.success.slides.map((slide) => ({
slideId: String(slide.slideId),
+ title: slide.title,
timestampMs: slide.timestampMs,
}));
}
diff --git a/src/types/recording.ts b/src/types/recording.ts
index 7b96d9e5..8951c0cd 100644
--- a/src/types/recording.ts
+++ b/src/types/recording.ts
@@ -1,3 +1,5 @@
+import { getSlideTitle } from '@/utils/slideTitle';
+
import type { SlideDetail } from './slide';
/**
@@ -36,7 +38,7 @@ export function convertToRecordingSlides(
page: index + 1,
imageUrl: slide.imageUrl || `/thumbnails/${projectId}/${index}.webp`,
script: slide.script || '',
- title: slide.title,
+ title: getSlideTitle(slide.title, index + 1),
}));
}
diff --git a/src/types/share.ts b/src/types/share.ts
index 309b3550..8c8ab572 100644
--- a/src/types/share.ts
+++ b/src/types/share.ts
@@ -58,7 +58,7 @@ export type ShareableVideosResponse = ApiResponse;
export interface SharedPresentationSlide {
slideId: string;
slideNum: number;
- title: string;
+ title: string | null;
imageUrl: string;
scriptText: string;
timestampMs: number;
diff --git a/src/types/slide.ts b/src/types/slide.ts
index d8156640..75b9b00b 100644
--- a/src/types/slide.ts
+++ b/src/types/slide.ts
@@ -10,7 +10,7 @@ export interface SlideListItem {
script: string;
slideId: string;
projectId: string;
- title: string;
+ title: string | null;
slideNum: number;
imageUrl: string;
createdAt: string;
diff --git a/src/utils/sharedContent.ts b/src/utils/sharedContent.ts
index 79db2a71..04bcb58f 100644
--- a/src/utils/sharedContent.ts
+++ b/src/utils/sharedContent.ts
@@ -4,6 +4,7 @@
*/
import type { SharedPresentationSlide } from '@/types/share';
import type { SlideDetail } from '@/types/slide';
+import { getSlideTitle } from '@/utils/slideTitle';
export const SHARED_PROJECT_ID = 'shared';
@@ -25,7 +26,7 @@ export function normalizeSharedSlides(rawSlides: SharedPresentationSlide[]): Sli
return {
slideId: slide.slideId,
projectId: SHARED_PROJECT_ID,
- title: slide.title ?? `슬라이드 ${slideNum}`,
+ title: getSlideTitle(slide.title, slideNum),
slideNum,
imageUrl: slide.imageUrl,
createdAt: now,
diff --git a/src/utils/slideTitle.test.ts b/src/utils/slideTitle.test.ts
new file mode 100644
index 00000000..58e5c2ce
--- /dev/null
+++ b/src/utils/slideTitle.test.ts
@@ -0,0 +1,18 @@
+import { describe, expect, it } from 'vitest';
+
+import { getSlideTitle } from './slideTitle';
+
+describe('getSlideTitle', () => {
+ it('returns the original title when title exists', () => {
+ expect(getSlideTitle('도입', 1)).toBe('도입');
+ });
+
+ it('returns fallback when title is null', () => {
+ expect(getSlideTitle(null, 2)).toBe('슬라이드 2');
+ });
+
+ it('returns fallback when title is empty or blank', () => {
+ expect(getSlideTitle('', 3)).toBe('슬라이드 3');
+ expect(getSlideTitle(' ', 4)).toBe('슬라이드 4');
+ });
+});
diff --git a/src/utils/slideTitle.ts b/src/utils/slideTitle.ts
new file mode 100644
index 00000000..05fa6a43
--- /dev/null
+++ b/src/utils/slideTitle.ts
@@ -0,0 +1,19 @@
+/**
+ * @file slideTitle.ts
+ * @description 슬라이드 제목 렌더링 공통 유틸리티
+ */
+
+/**
+ * 슬라이드 제목 표시 문자열을 반환합니다.
+ *
+ * - title이 존재하면 title 그대로 반환
+ * - title이 없거나 공백이면 "슬라이드 N" 반환
+ */
+export function getSlideTitle(title: string | null | undefined, slideNum: number): string {
+ if (typeof title === 'string' && title.trim().length > 0) {
+ return title;
+ }
+
+ const safeSlideNum = Number.isFinite(slideNum) && slideNum > 0 ? Math.floor(slideNum) : 1;
+ return `슬라이드 ${safeSlideNum}`;
+}