Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
ab29621
refactor: 타입 맞추기 (#111)
AndyH0ng Feb 2, 2026
e0566ff
fix: API 경로 수정 (#111)
AndyH0ng Feb 2, 2026
e7fd61a
fix: Presentation API 연결 (#111)
AndyH0ng Feb 2, 2026
b5ec59b
fix: /presentations API 연결 (#111)
AndyH0ng Feb 2, 2026
4a8a219
Merge branch 'develop' into refactor/setting-type-111
AndyH0ng Feb 2, 2026
ff9abe7
feat: /comments API 연결 (#111)
AndyH0ng Feb 2, 2026
d8bbbe1
Merge branch 'refactor/setting-type-111' of https://github.com/TTORAN…
AndyH0ng Feb 2, 2026
295b1ae
feat: 구글 로그인 연동 (#111)
AndyH0ng Feb 2, 2026
6523ec7
feat: 네이버 카카오 로그인 연동 (#111)
AndyH0ng Feb 2, 2026
a026d8f
feat: 로그인 콜백 및 라우팅 (#111)
AndyH0ng Feb 2, 2026
778b328
design: 제목 수정 팝오버 사용성 개선 (#111)
AndyH0ng Feb 2, 2026
59b5a03
fix: 빌드 오류 해결 (#111)
AndyH0ng Feb 2, 2026
06f29fb
refactor: api 연동 구조 변경 (#111)
kimyw1018 Feb 3, 2026
44f486e
refactor: 빌드에러 수정(#111)
kimyw1018 Feb 4, 2026
5e75db5
fix: 무한루프 해결(#111)
kimyw1018 Feb 4, 2026
eed8303
fix: SocialLoginSuccessResponseDto로 업데이트 (#111)
AndyH0ng Feb 4, 2026
05bda5c
refactor: Script 타입을 DTO로 통합 (#111)
AndyH0ng Feb 4, 2026
3ecaf2e
refactor: Slide 타입을 DTO로 통합 (#111)
AndyH0ng Feb 4, 2026
0d632a5
refactor: Comment 타입을 DTO로 통합 (#111)
AndyH0ng Feb 4, 2026
435a0d5
fix: DTO export 누락 및 import 오류 수정 (#111)
AndyH0ng Feb 4, 2026
7e65f9c
Merge branch 'refactor/setting-type-111' of https://github.com/TTORAN…
kimyw1018 Feb 4, 2026
e27ed4a
refactor: Video DTO 구조 통일 (#111)
AndyH0ng Feb 4, 2026
d1a545b
Merge branch 'refactor/setting-type-111' of https://github.com/TTORAN…
AndyH0ng Feb 4, 2026
e633cfb
Merge branch 'develop' into refactor/setting-type-111
AndyH0ng Feb 4, 2026
73dd156
fix: 빌드 오류 2/3 해결 (#111)
AndyH0ng Feb 4, 2026
411b6ee
fix: Recording props 수정(#111)
kimyw1018 Feb 4, 2026
02119e3
fix: 클로드 리뷰 반영 (#111)
kimyw1018 Feb 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 18 additions & 8 deletions src/api/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,21 @@ import { useAuthStore } from '@/stores/authStore';
export const API_TIMEOUT_MS = 10000;

/**
* API 에러 응답 타입
* API 에러 응답 타입 (FAILURE 응답의 error 필드)
*/
export interface ApiError {
message: string;
code?: string;
status?: number;
export interface ApiErrorResponse {
errorCode: string;
reason: string;
data?: unknown;
}

/**
* API FAILURE 응답 구조
*/
export interface ApiFailureResponse {
resultType: 'FAILURE';
error: ApiErrorResponse;
success: null;
}

/**
Expand Down Expand Up @@ -69,14 +78,15 @@ apiClient.interceptors.request.use(
*/
apiClient.interceptors.response.use(
(response) => response,
(error: AxiosError<ApiError>) => {
(error: AxiosError<ApiFailureResponse>) => {
const status = error.response?.status;
const message = error.response?.data?.message || '알 수 없는 오류가 발생했습니다';
const errorData = error.response?.data?.error;
const reason = errorData?.reason || '알 수 없는 오류가 발생했습니다';

// [하이브리드 전략]
// 시스템 에러 (401 인증, 500 서버 장애)는 Axios가 즉시 처리
if (status === 401 || (status && status >= 500)) {
handleApiError(status, message);
handleApiError(status, reason);
// 다운스트림(React Query 등)에서 중복 처리하지 않도록 플래그 설정
error.isHandled = true;
}
Expand Down
6 changes: 6 additions & 0 deletions src/api/dto/analytics.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* 대본 복원 요청 DTO
*/
export interface RestoreScriptRequestDto {
version: number;
}
30 changes: 30 additions & 0 deletions src/api/dto/auth.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* 인증 관련 DTO
*/

/**
* 소셜 로그인 성공 응답의 사용자 정보
*/
export interface SocialLoginUserDto {
id: string;
email: string;
name: string;
sessionId: string;
}

/**
* 소셜 로그인 성공 응답의 토큰 정보
*/
export interface SocialLoginTokensDto {
accessToken: string;
refreshToken: string;
}

/**
* 소셜 로그인 성공 응답
*/
export interface SocialLoginSuccessDto {
message: string;
user: SocialLoginUserDto;
tokens: SocialLoginTokensDto;
}
Empty file added src/api/dto/comment.dto.ts
Empty file.
Empty file added src/api/dto/file.dto.ts
Empty file.
11 changes: 11 additions & 0 deletions src/api/dto/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* @file index.ts
* @description DTO 배럴 export
*/

export type { SocialLoginSuccessDto, SocialLoginTokensDto, SocialLoginUserDto } from './auth.dto';
export type { CreateProjectDto, UpdateProjectDto } from './projects.dto';

Check failure on line 7 in src/api/dto/index.ts

View workflow job for this annotation

GitHub Actions / Build

Cannot find module './projects.dto' or its corresponding type declarations.
export type { CreateSlideDto, UpdateSlideDto } from './slides.dto';

Check failure on line 8 in src/api/dto/index.ts

View workflow job for this annotation

GitHub Actions / Build

Module '"./slides.dto"' has no exported member 'UpdateSlideDto'.

Check failure on line 8 in src/api/dto/index.ts

View workflow job for this annotation

GitHub Actions / Build

Module '"./slides.dto"' has no exported member 'CreateSlideDto'.
export type { UpdateScriptDto, RestoreScriptDto } from './scripts.dto';

Check failure on line 9 in src/api/dto/index.ts

View workflow job for this annotation

GitHub Actions / Build

Module '"./scripts.dto"' has no exported member 'RestoreScriptDto'.

Check failure on line 9 in src/api/dto/index.ts

View workflow job for this annotation

GitHub Actions / Build

Module '"./scripts.dto"' has no exported member 'UpdateScriptDto'.
export type { CreateOpinionDto } from './opinions.dto';

Check failure on line 10 in src/api/dto/index.ts

View workflow job for this annotation

GitHub Actions / Build

Cannot find module './opinions.dto' or its corresponding type declarations.
export type { ToggleSlideReactionDto } from './reactions.dto';
Empty file added src/api/dto/presentation.dto.ts
Empty file.
8 changes: 8 additions & 0 deletions src/api/dto/reactions.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { ReactionType } from '@/types/script';

/**
* 슬라이드 리액션 토글 요청 DTO
*/
export interface ToggleSlideReactionDto {
type: ReactionType;
}
39 changes: 39 additions & 0 deletions src/api/dto/scripts.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* 대본 저장 및 수정 DTO
*/
export interface UpdateScriptRequestDto {
script: string;
}
/**
* 대본 조회 DTO
*/
export interface GetScriptResponseDto {
message: string;
slideId: string;
charCount: number;
scriptText: string;
estimatedDurationSeconds: number;
createdAt: string;
updatedAt: string;
}
/**
* 대본 버전 히스토리 목록 조회 DTO
*/
export interface GetScriptVersionHistoryResponseDto {
versionNumber: number;
scriptText: string;
charCount: number;
createdAt: string;
}
/**
* 특정 버전으로 대본 복원 DTO
*/
export interface RestoreScriptResponseDto {
message: string;
slideId: string;
charCount: number;
scriptText: string;
estimatedDurationSeconds: number;
createdAt: string;
updatedAt: string;
}
Empty file added src/api/dto/session.dto.ts
Empty file.
Empty file added src/api/dto/sharelink.dto.ts
Empty file.
33 changes: 33 additions & 0 deletions src/api/dto/slides.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* 슬라이드 목록 조회 DTO
*/
export interface CreateSlideResponseDto {
slideId: string;
projectId: string;
title: string;
slideNum: number;
imageUrl: string;
createdAt: string;
updatedAt: string;
}

/**
* 슬라이드 제목 수정 요청 DTO
*/
export interface UpdateSlideTitleRequestDto {
title?: string;
}

/**
* 슬라이드 상세 조회 DTO
*/
export interface GetSlideResponseDto {
slideId: string;
projectId: string;
title: string;
slideNum: number;
imageUrl: string;
prevSlideId: string | null;
nextSlideId: string | null;
updatedAt: string;
}
65 changes: 65 additions & 0 deletions src/api/dto/video.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/**
* 영상 타임스탬프 댓글 생성
*/
export interface CreateOpinionDto {
content: string;
/** 답글인 경우 부모 의견 내용 고쳐라이D */
parentId?: string;
}

/**
* 영상 녹화 관련 DTO 정의
*/
export interface StartVideoRequest {
projectId: number;
title: string;
}

export interface StartVideoResponse {
resultType: 'SUCCESS' | 'FAILURE';
error: null | {
errorCode: string;
reason: string;
data?: unknown;
};
success: {
videoId: number;
};
}

export interface FinishVideoRequest {
slideLogs: Array<{
slideId: number;
timestampMs: number;
}>;
}

export interface FinishVideoResponse {
resultType: 'SUCCESS' | 'FAILURE';
error: null | {
errorCode: string;
reason: string;
data?: unknown;
};
success: {
videoId: string;
status: string;
slideCount: number;
slideDurations: Array<{
slideId: string;
totalDurationMs: number;
}>;
};
}

export interface ChunkUploadResponse {
resultType: 'SUCCESS' | 'FAILURE';
error: null | {
errorCode: string;
reason: string;
data?: unknown;
};
success: {
ok: boolean;
};
}
13 changes: 1 addition & 12 deletions src/api/endpoints/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,7 @@ export async function getSummaryAnalytics(projectId: string): Promise<SummaryAna
);

if (response.data.resultType === 'FAILURE') {
// 1. reason이 객체인지 문자열인지 확인해서 처리
const reason = response.data.reason;
let errorMessage = '분석 요약 조회에 실패했습니다.';

if (typeof reason === 'string') {
errorMessage = reason; // 문자열이면 그대로 사용
} else if (reason && typeof reason === 'object') {
errorMessage = reason.message || errorMessage; // 객체면 .message 추출
}

// 2. 이제 errorMessage는 무조건 string이므로 에러가 사라집니다.
throw new Error(errorMessage);
throw new Error(response.data.error.reason);
}

// success가 없으면 에러 처리 (옵셔널 체이닝 문제 방지)
Expand Down
68 changes: 68 additions & 0 deletions src/api/endpoints/auth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* @file auth.ts
* @description 인증 관련 API 엔드포인트
*/
import { apiClient } from '@/api';
import type { SocialLoginSuccessDto } from '@/api/dto';
import type { ApiResponse } from '@/types/api';

/**
* Google OAuth 콜백 처리
*
* @param code - OAuth 인증 코드
* @returns 로그인 성공 정보 (user, tokens)
*
* @example
* const result = await getGoogleCallback('authorization-code');
*/
export async function getGoogleCallback(code: string): Promise<SocialLoginSuccessDto> {
const response = await apiClient.get<ApiResponse<SocialLoginSuccessDto>>(
`/auth/google/callback`,
{ params: { code } },
);

if (response.data.resultType === 'SUCCESS') {
return response.data.success;
}
throw new Error(response.data.error.reason);
}

/**
* Kakao OAuth 콜백 처리
*
* @param code - OAuth 인증 코드
* @returns 로그인 성공 정보 (user, tokens)
*
* @example
* const result = await getKakaoCallback('authorization-code');
*/
export async function getKakaoCallback(code: string): Promise<SocialLoginSuccessDto> {
const response = await apiClient.get<ApiResponse<SocialLoginSuccessDto>>(`/auth/kakao/callback`, {
params: { code },
});

if (response.data.resultType === 'SUCCESS') {
return response.data.success;
}
throw new Error(response.data.error.reason);
}

/**
* Naver OAuth 콜백 처리
*
* @param code - OAuth 인증 코드
* @returns 로그인 성공 정보 (user, tokens)
*
* @example
* const result = await getNaverCallback('authorization-code');
*/
export async function getNaverCallback(code: string): Promise<SocialLoginSuccessDto> {
const response = await apiClient.get<ApiResponse<SocialLoginSuccessDto>>(`/auth/naver/callback`, {
params: { code },
});

if (response.data.resultType === 'SUCCESS') {
return response.data.success;
}
throw new Error(response.data.error.reason);
}
Loading
Loading