Skip to content

refactor: /presentations DTO 구현 및 API 연동 (#111)#113

Merged
kimyw1018 merged 27 commits intodevelopfrom
refactor/setting-type-111
Feb 4, 2026
Merged

refactor: /presentations DTO 구현 및 API 연동 (#111)#113
kimyw1018 merged 27 commits intodevelopfrom
refactor/setting-type-111

Conversation

@AndyH0ng
Copy link
Member

@AndyH0ng AndyH0ng commented Feb 2, 2026

📌 관련 이슈

✨ 변경 내용

  1. 타입 시스템 (src/types/api.ts)
  • ApiResponse를 Discriminated Union으로 변경
type ApiResponse<T> =
  | { resultType: 'SUCCESS'; error: null; success: T }
  | { resultType: 'FAILURE'; error: ApiError; success: null }
  • ApiError 구조 변경: code/messageerrorCode/reason - ConversionStatus, ConversionStatusResponse 타입 추가
  1. 슬라이드 타입 (src/types/slide.ts)
  • SlideListItem: API 목록 조회 응답 (slideId, imageUrl, slideNum, createdAt, updatedAt)
  • SlideDetail: API 상세 조회 응답 (prevSlideId, nextSlideId 추가)
  • SlideUpdateResponse: API 수정 응답 타입
  • Slide: 프론트엔드 스토어용 (임시)
  1. 도메인 모델 및 DTO 정립 (src/api/dto/, src/types/)
    DTO 계층 분리: projects, slides, scripts, opinions, reactions 등 도메인별 요청/응답 DTO를 전용 파일로 분리하여 관리함.

슬라이드 모델 세분화: 목록용(SlideListItem), 상세용(SlideDetail), 수정 응답용(SlideUpdateResponse) 등으로 용도에 맞게 타입을 쪼개서 안정성 확보함.

  1. API 엔드포인트
  • 모든 엔드포인트에 ApiResponse<T> 래퍼 + 타입 가드 적용
if (response.data.resultType === 'SUCCESS') {
  return response.data.success;
}

throw new Error(response.data.error.reason);
  • presentations.ts - getConversionStatus() 신규 함수 추가
  • client.ts - 에러 인터셉터에서 reason 필드 사용
  1. 필드명 마이그레이션
  • id → slideId 전체 변경
  • thumb → imageUrl 전체 변경
  • 영향 받은 파일:
    • 컴포넌트: SlideList, SlideThumbnail, SlideWorkspace, SlideViewer, SlideInfoPanel
    • 스토어: slideStore, useSlideSelectors
    • 훅: useComments, useReactions
    • 페이지: SlidePage, FeedbackSlidePage, InsightPage
  1. 에러 핸들링
  • queryClient.ts - ApiFailureResponse 타입으로 에러 처리
  • useUpload.ts - error.reason 필드 사용
  • client.ts - response.data.error.reason 구조 지원

💡 참고 사항

스크린샷, 참고 링크, 추가 설명 등 (없으면 생략 가능)

@AndyH0ng AndyH0ng self-assigned this Feb 2, 2026
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @AndyH0ng, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 PR은 API 응답 처리 방식과 데이터 전송 객체(DTO) 구조를 전반적으로 개선하고, 프론트엔드 코드 전반의 필드명 일관성을 강화하는 리팩토링을 포함합니다. 주요 변경 사항으로는 ApiResponse 타입을 Discriminated Union으로 재정의하여 API 응답의 안정성을 높이고, 요청 DTO를 별도 디렉토리로 분리하여 코드의 모듈성과 유지보수성을 향상시켰습니다. 또한, id와 thumb 필드명을 각각 slideId와 imageUrl로 통일하여 데이터 모델의 명확성을 확보하고, 에러 처리 로직을 개선하여 사용자 경험을 향상시켰습니다.

Highlights

  • API 응답 타입 시스템 변경: ApiResponse를 Discriminated Union 타입으로 변경하여 성공/실패 응답을 명확히 구분하고, ApiError 구조를 errorCode/reason으로 재정의했습니다.
  • DTO (Data Transfer Object) 분리: src/api/dto 디렉토리를 신설하여 CreateProjectDto, UpdateProjectDto, CreateSlideDto, UpdateSlideDto 등 요청 DTO들을 엔드포인트 파일에서 분리하여 관리합니다.
  • API 엔드포인트에 ApiResponse 래퍼 및 타입 가드 적용: 모든 API 호출에 ApiResponse 래퍼를 적용하고, resultType을 확인하는 타입 가드를 통해 성공/실패 응답을 안전하게 처리합니다.
  • 필드명 마이그레이션: id를 slideId로, thumb를 imageUrl로 전면 변경하여 일관성을 확보했습니다. 이 변경은 여러 컴포넌트, 스토어, 훅, 페이지에 영향을 미쳤습니다.
  • 에러 핸들링 개선: queryClient.ts에서 ApiFailureResponse 타입을 사용하여 에러를 처리하고, useUpload.ts 및 client.ts에서 error.reason 필드를 활용하도록 개선했습니다.
  • 새로운 API 엔드포인트 추가: presentations.ts에 getConversionStatus() 함수를 추가하여 프로젝트 파일 변환 상태를 조회할 수 있도록 했습니다.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

이 PR은 API 명세 변경에 맞춰 대규모 리팩토링을 진행한 점이 인상적입니다. 특히 DTO를 도입하고 ApiResponse 타입을 Discriminated Union으로 개선하여 타입 안정성과 코드 유지보수성을 크게 향상시킨 점이 좋습니다.

리뷰 결과, 몇 가지 개선점을 발견했습니다.

  1. 여러 API 엔드포인트 함수에서 응답을 처리하는 로직이 중복되고 있습니다. 이를 헬퍼 함수로 추출하여 코드 중복을 줄이고 일관성을 높일 수 있습니다.
  2. MSW(Mock Service Worker) 핸들러 중 일부가 새로운 API 응답 형식(ApiResponse)을 따르지 않아, 모킹 환경에서 런타임 에러를 유발할 수 있습니다. 특히 GETDELETE 요청에 대한 핸들러 수정이 필요합니다.

자세한 내용은 각 파일에 남긴 개별 코멘트를 참고해주세요. 전반적으로 코드 품질을 크게 향상시키는 훌륭한 작업입니다.

@github-actions github-actions bot added component 컴포넌트 관련 변경 page 페이지 관련 변경 types 타입 정의 변경 store 상태 관리 변경 hooks 커스텀 훅 변경 labels Feb 2, 2026
updatedAt: new Date().toISOString(),
};
slideComments.set(slideId, comments);
found = true;
updatedAt: new Date().toISOString(),
};
commentReplies.set(parentId, replies);
found = true;
}

// 답글에서 찾기
if (!found) {
}
}

if (!found) {
@AndyH0ng
Copy link
Member Author

AndyH0ng commented Feb 2, 2026

@claude 코드 리뷰해줘

@claude
Copy link

claude bot commented Feb 2, 2026

Claude finished @AndyH0ng's task in 4m 22s —— View job


코드 리뷰 완료 ✅

대규모 리팩토링 작업으로 DTO 계층 도입, API 응답 타입 개선, 필드명 표준화 등 전반적인 코드 품질 향상이 이루어졌습니다. 전체적으로 좋은 개선이지만, 몇 가지 중요한 이슈와 개선 제안이 있습니다.


✅ 긍정적인 변경사항

1. 타입 안정성 대폭 향상

  • ApiResponse<T>를 Discriminated Union으로 변경하여 컴파일 타임 타입 안전성 확보
  • resultType으로 성공/실패 응답을 명확히 구분할 수 있어 런타임 에러 방지

2. DTO 계층 분리

  • src/api/dto/ 디렉토리로 요청 DTO 분리하여 관심사 분리 및 유지보수성 향상
  • 각 도메인별로 명확하게 구조화됨

3. 필드명 일관성

  • idslideId, thumbimageUrl 통일로 코드 가독성 향상
  • 50개 파일에 걸쳐 일관되게 적용됨

4. 에러 처리 구조 개선

  • error.reason 필드로 에러 메시지 일관성 확보
  • ApiError 타입에 errorCode, reason, data 구조화

🔴 Critical Issues (반드시 수정 필요)

1. MSW 핸들러의 DELETE 응답이 API 인터페이스와 불일치

위치: src/mocks/handlers.ts:198, 370

DELETE 엔드포인트들이 204 No Contentnull body를 반환하지만, API 함수들은 ApiResponse<null> 타입의 JSON 응답을 기대합니다:

// handlers.ts (Line 198, 370)
return new HttpResponse(null, { status: 204 });

// presentations.ts (Line 96)
const response = await apiClient.delete<ApiResponse<null>>(`/presentations/${projectId}`);
if (response.data.resultType === 'FAILURE') { ... }

204 응답은 body가 없어서 response.dataundefined가 되고, response.data.resultType 접근 시 런타임 에러 발생합니다.

수정 방안:

// handlers.ts에서
return HttpResponse.json(wrapResponse(null));

이 문제는 다음 핸들러들에 영향을 미칩니다:

  • DELETE /presentations/:projectId (Line 198)
  • DELETE /presentations/slides/:slideId (Line 370)
  • DELETE /opinions/:opinionId (해당 핸들러 확인 필요)

Fix MSW DELETE handlers →


2. GET 슬라이드 상세 조회 핸들러가 ApiResponse 래퍼 없음

위치: src/mocks/handlers.ts:274

// Line 259-275: GET /presentations/slides/:slideId
return HttpResponse.json(slide);  // ❌ Should be: wrapResponse(slide)

API 엔드포인트 getSlide() (src/api/endpoints/slides.ts:40)는 ApiResponse<SlideDetail>을 기대하지만, 핸들러는 bare object를 반환합니다.

수정 방안:

// Line 274를 다음과 같이 변경
return HttpResponse.json(wrapResponse(slide));

단, 이 경우 slide 객체의 타입이 SlideDetail과 일치하는지 확인 필요. 현재 slides 배열은 Slide 타입(deprecated)을 사용 중입니다.

Fix GET slide handler →


3. 슬라이드 수정/생성 핸들러도 ApiResponse 래퍼 없음

위치: src/mocks/handlers.ts:319, 346

// Line 319: PATCH /presentations/slides/:slideId
return HttpResponse.json(slides[slideIndex]);  // ❌

// Line 346: POST /presentations/:projectId/slides
return HttpResponse.json(newSlide, { status: 201 });  // ❌

이들도 API 함수가 ApiResponse<SlideUpdateResponse>, ApiResponse<SlideDetail>을 기대하므로 래퍼 필요합니다.


⚠️ Major Issues (심각한 문제)

4. API 응답 처리 로직 중복 (Code Smell)

위치: 모든 API 엔드포인트 파일

Gemini 리뷰어가 지적한 것처럼, 모든 API 함수에서 다음 패턴이 반복됩니다:

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

이는 코드 중복이며, 향후 로직 변경 시 여러 곳을 수정해야 합니다.

권장 수정안: 헬퍼 함수 추출

// src/api/helpers.ts (신규 파일)
import { AxiosResponse } from 'axios';
import { ApiResponse } from '@/types/api';

export function unwrapApiResponse<T>(response: AxiosResponse<ApiResponse<T>>): T {
  if (response.data.resultType === 'SUCCESS') {
    return response.data.success;
  }
  throw new Error(response.data.error.reason);
}

사용 예시:

// presentations.ts
export async function getPresentations(): Promise<Presentation[]> {
  const response = await apiClient.get<ApiResponse<PresentationListResponse>>(`/presentations`);
  const data = unwrapApiResponse(response);
  return data.presentations;
}

이 변경으로 12개 엔드포인트 파일의 40+ 함수를 간소화할 수 있습니다.

Create unwrapApiResponse helper →


5. scripts.ts에 중복 타입 정의

위치: src/api/endpoints/scripts.ts:28, 74

// Line 28-30
export interface UpdateScriptRequest {
  script: string;
}

// Line 74-76
export interface RestoreScriptRequest {
  version: number;
}

이미 src/api/dto/scripts.dto.tsUpdateScriptDto, RestoreScriptDto가 정의되어 있으므로 중복입니다.

수정 방안: DTO import 사용

import type { UpdateScriptDto, RestoreScriptDto } from '@/api/dto';

export type UpdateScriptRequest = UpdateScriptDto;  // deprecated alias
export type RestoreScriptRequest = RestoreScriptDto;  // deprecated alias

6. CodeQL 정적 분석 경고 (handlers.ts)

GitHub Code Quality 봇이 다음 이슈를 감지했습니다:

  1. Useless assignment (Line 1028, 1052): found = true 설정 직후 return하므로 값이 사용되지 않음
  2. Useless conditional (Line 1042, 1066): if (!found) 조건이 항상 true

위치: src/mocks/handlers.ts:1010-1069 (PATCH /comments/:commentId 핸들러)

근본 원인: 로직 구조 문제. 루프 내에서 찾으면 즉시 return하므로 found 플래그가 불필요합니다.

수정 방안:

// 첫 번째 루프 후 찾았으면 이미 return했으므로
// if (!found) 조건 없이 두 번째 루프 실행
// 마지막에는 무조건 404 return

상세 수정은 CodeQL 제안을 따르되, 핵심은 found 변수 제거입니다.


📝 Minor Issues & Suggestions

7. Legacy 주석 오해의 소지

위치: src/mocks/handlers.ts:231-232

/**
 * 프로젝트의 슬라이드 목록 조회 (Legacy)
 * GET /presentations/:projectId/slides
 */

주석에 "(Legacy)"라고 표시되어 있지만, src/api/endpoints/slides.ts:24getSlides() 함수가 현재 이 엔드포인트를 사용 중입니다. 오히려 Line 209의 /projects/:projectId/slides가 legacy일 가능성이 높습니다.

수정 방안: 주석 수정 또는 제거


8. Deprecated 타입의 사용 지속

위치: src/mocks/handlers.ts:18, 333

let slides: Slide[] = [...MOCK_SLIDES];  // Line 18
const newSlide: Slide = { ... };         // Line 333

Slide 타입은 src/types/slide.ts:57에서 @deprecated로 표시되어 있습니다. MSW 핸들러에서 계속 사용 중이므로, 점진적으로 SlideListItem 또는 SlideDetail로 마이그레이션 필요합니다.

단, MSW는 테스트/개발 환경이므로 우선순위는 낮을 수 있습니다.


9. 타입 불일치 가능성

위치: src/api/endpoints/slides.ts:87

export async function createSlide(
  projectId: string,
  data: { title: string; script?: string },  // Inline type
): Promise<SlideDetail>

DTO로 CreateSlideDto가 정의되어 있는데 사용하지 않았습니다.

수정 방안:

import type { CreateSlideDto } from '@/api/dto';

export async function createSlide(
  projectId: string,
  data: CreateSlideDto,
): Promise<SlideDetail>

10. 에러 처리 일관성

위치: src/api/endpoints/presentations.ts:98-100, slides.ts:108-110

DELETE 함수들에서 SUCCESS 케이스를 확인하지 않고 FAILURE만 체크합니다:

if (response.data.resultType === 'FAILURE') {
  throw new Error(response.data.error.reason);
}
// No explicit SUCCESS check

다른 함수들은 if (SUCCESS) return 패턴을 사용하는데 일관성이 없습니다.

권장:

if (response.data.resultType === 'SUCCESS') {
  return;  // void 함수이므로 명시적 return
}
throw new Error(response.data.error.reason);

혹은 unwrapApiResponse 헬퍼를 도입하면 이 문제가 자동 해결됩니다.


🔍 Code Quality Observations

11. 필드명 마이그레이션 완료도

50개 파일에 걸쳐 idslideId, thumbimageUrl 변경이 일관되게 적용되었습니다. 잔존하는 이슈:

  • src/mocks/slides.ts의 mock 데이터도 업데이트되었는지 확인 필요
  • src/mocks/handlers.ts:265에서 slides.find((s) => s.id === slideId) - 여전히 id 사용 (deprecated Slide 타입이므로)

12. TypeScript strict mode 호환성

CLAUDE.md에 strict mode 사용이 명시되어 있는데, 다음 부분들이 잠재적 문제:

  • src/api/client.ts:84: errorData?.reason || '알 수 없는 오류가 발생했습니다' - optional chaining 사용 (OK)
  • MSW 핸들러에서 타입 단언 as 사용이 많음 (개발 환경이므로 허용 가능)

📊 Summary

구분 개수 상세
Critical 3 MSW DELETE 응답 불일치, GET 슬라이드 래퍼 누락, POST/PATCH 래퍼 누락
Major 4 API 응답 로직 중복, 타입 중복, CodeQL 경고
Minor 6 Legacy 주석, deprecated 타입 사용, 타입 일관성 등

🎯 우선순위 제안

High Priority (배포 전 필수):

  1. MSW DELETE 핸들러 수정 (chore: 프로젝트 세팅 #1)
  2. MSW GET/POST/PATCH 슬라이드 핸들러 수정 (chore/프로젝트 세팅 #2, Closed #3)
  3. unwrapApiResponse 헬퍼 도입 (design: 디자인 시스템 구축 #4)

Medium Priority (다음 릴리스):
4. CodeQL 경고 수정 (#6)
5. 중복 타입 정리 (#5, #9)
6. 에러 처리 일관성 (#10)

Low Priority (점진적 개선):
7. Legacy 주석 정리 (#7)
8. Deprecated 타입 마이그레이션 (#8)


💡 전체 평가

긍정적 평가:

  • 타입 시스템 개선으로 런타임 에러 방지 효과 ✅
  • DTO 계층 분리로 관심사 분리 명확 ✅
  • 50개 파일에 걸친 일관된 필드명 변경 ✅
  • 에러 구조 표준화 ✅

개선 필요 사항:

  • MSW 핸들러가 새 API 인터페이스를 따르지 않아 개발 환경 런타임 에러 가능성 ⚠️
  • API 응답 처리 로직 중복으로 유지보수성 저하 ⚠️
  • 정적 분석 도구 경고 존재 ⚠️

전체적으로 잘 설계된 리팩토링이지만, MSW 핸들러 수정과 헬퍼 함수 도입이 완료되면 훨씬 견고해질 것으로 보입니다.


Copy link
Collaborator

@kimyw1018 kimyw1018 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네이ㅣ밍 수정response, request

kimyw1018 and others added 4 commits February 4, 2026 11:57
- ScriptResponse → GetScriptResponseDto
- ScriptVersion → GetScriptVersionHistoryResponseDto
- UpdateScriptRequest → UpdateScriptRequestDto
- RestoreScriptRequest → RestoreScriptRequestDto
- types/api.ts에서 중복 타입 제거

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
AndyH0ng and others added 8 commits February 4, 2026 12:55
- SlideDetail → GetSlideResponseDto
- SlideUpdateResponse → UpdateSlideResponseDto
- UpdateSlideDto → UpdateSlideTitleRequestDto
- deprecated UpdateSlideRequest 타입 별칭 제거
- types/slide.ts에서 중복 타입 제거

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- CommentResponse → CommentResponseDto
- CommentListResponse → GetSlideCommentsResponseDto
- CommentWithUser → CommentWithUserDto
- ReplyListResponse → GetReplyListResponseDto
- getReplies 에러 핸들링 패턴 통일
- types/comment.ts에서 API 응답 타입 제거

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- useSlides.ts: UpdateSlideRequest → UpdateSlideTitleRequestDto
- dto/index.ts: CreateOpinionDto export 추가
- presentations.dto.ts: UpdateProjectDto 정의 및 export 추가

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Response DTO에서 ApiResponse 래퍼 중복 제거
- StartVideoResponse → StartVideoResponseDto (success 데이터만)
- FinishVideoResponse → FinishVideoResponseDto (success 데이터만)
- ChunkUploadResponse → ChunkUploadResponseDto (success 데이터만)
- videos.ts endpoint에서 ApiResponse<T> 래핑 적용
- useVideoUpload.ts 타입 수정

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@AndyH0ng
Copy link
Member Author

AndyH0ng commented Feb 4, 2026

  • 기존 types에서 관리되던 부분 Dto로 마이그레이션
  • 비디오 부분에서도 공용 ApiResponse<T> 래퍼 사용하도록 수정

@kimyw1018
Copy link
Collaborator

@claude 코드리뷰 하셈

@claude
Copy link

claude bot commented Feb 4, 2026

Claude finished @kimyw1018's task in 2m 58s —— View job


코드 리뷰 완료 ✅

대규모 리팩토링으로 API 타입 시스템과 DTO 계층을 크게 개선한 작업입니다. 전반적으로 코드 품질 향상에 기여하는 좋은 변경이지만, 몇 가지 개선이 필요한 부분이 있습니다.


작업 목록

  • 프로젝트 가이드라인 확인 (CLAUDE.md)
  • PR 변경사항 분석
  • 주요 파일 검토
  • 리뷰 피드백 작성

✅ 긍정적인 변경사항

1. 타입 안정성 대폭 향상

  • ApiResponse<T>를 Discriminated Union으로 변경하여 컴파일 타임 안전성 확보
  • resultType으로 성공/실패를 명확히 구분 ('SUCCESS' | 'FAILURE')
  • TypeScript의 타입 좁히기(type narrowing) 활용 가능

2. DTO 계층 분리

  • src/api/dto/ 디렉토리로 요청/응답 DTO 체계적으로 분리
  • 관심사 분리로 코드 유지보수성 향상
  • 11개 도메인별 DTO 파일 추가 (slides, scripts, comments, presentations 등)

3. 필드명 일관성 확보

  • idslideId (67개 파일에 걸쳐 일관되게 적용)
  • thumbimageUrl (의미 명확화)
  • 스토어, 컴포넌트, 페이지 전반에 일관된 네이밍

4. 에러 처리 구조 개선

  • ApiError 타입: errorCode, reason, data 구조화
  • Axios 인터셉터에서 error.reason 필드 사용
  • 일관된 에러 응답 처리

5. 비디오 API도 공통 래퍼 적용

  • videosApi의 모든 엔드포인트가 ApiResponse<T> 래퍼 사용
  • 타입 일관성 유지

🔴 Critical Issues (반드시 수정 필요)

1. MSW DELETE 핸들러가 ApiResponse 형식 미준수

위치: src/mocks/handlers.ts:198, 370

DELETE 엔드포인트가 204 No Content를 반환하지만, API 함수는 ApiResponse<null> JSON을 기대합니다:

// handlers.ts Line 198, 370
return new HttpResponse(null, { status: 204 });

// presentations.ts Line 96, slides.ts Line 104
const response = await apiClient.delete<ApiResponse<null>>(...);
if (response.data.resultType === 'FAILURE') { ... }

문제: 204 응답은 body가 없어서 response.dataundefined가 되고, response.data.resultType 접근 시 런타임 에러 발생

수정 방안:

// handlers.ts에서
return HttpResponse.json(wrapResponse(null));

영향받는 핸들러:

  • DELETE /presentations/:projectId (Line 198)
  • DELETE /presentations/slides/:slideId (Line 370)

Fix MSW DELETE handlers →


2. GET 슬라이드 상세 조회 핸들러가 ApiResponse 래퍼 누락

위치: src/mocks/handlers.ts:274

// Line 259-274: GET /presentations/slides/:slideId
const slide = slides.find((s) => s.id === slideId);
// ...
return HttpResponse.json(slide);  // ❌ Should be: wrapResponse(slide)

문제: getSlide() API는 ApiResponse<GetSlideResponseDto>를 기대하지만 bare object 반환

수정 방안:

// Line 274
return HttpResponse.json(wrapResponse(slide));

주의: slides 배열은 deprecated Slide 타입 사용 중이므로, GetSlideResponseDto와 필드 매핑 확인 필요

Fix GET slide handler →


3. 슬라이드 수정/생성 핸들러도 ApiResponse 래퍼 누락

위치: src/mocks/handlers.ts:319, 346

// Line 319: PATCH /presentations/slides/:slideId
return HttpResponse.json(slides[slideIndex]);  // ❌

// Line 346: POST /presentations/:projectId/slides
return HttpResponse.json(newSlide, { status: 201 });  // ❌

문제: API가 각각 ApiResponse<UpdateSlideResponseDto>, ApiResponse<GetSlideResponseDto> 기대

수정 방안: 두 핸들러 모두 wrapResponse() 적용

Fix PATCH/POST slide handlers →


⚠️ Major Issues (중요한 개선 사항)

4. API 응답 처리 로직 중복 (Code Smell)

위치: 모든 API 엔드포인트 파일

Gemini 리뷰어 지적대로, 다음 패턴이 40+ 함수에서 반복됩니다:

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

권장 수정안: 헬퍼 함수 추출

// src/api/helpers.ts (신규 파일)
import { AxiosResponse } from 'axios';
import { ApiResponse } from '@/types/api';

export function unwrapApiResponse<T>(response: AxiosResponse<ApiResponse<T>>): T {
  if (response.data.resultType === 'SUCCESS') {
    return response.data.success;
  }
  throw new Error(response.data.error.reason);
}

사용 예시:

// presentations.ts
export async function getPresentations(): Promise<Presentation[]> {
  const response = await apiClient.get<ApiResponse<PresentationListResponse>>('/presentations');
  const data = unwrapApiResponse(response);
  return data.presentations;
}

효과: 12개 파일, 40+ 함수 간소화 및 유지보수성 향상

Create unwrapApiResponse helper →


5. DTO 네이밍 일관성 부족

위치: 여러 DTO 파일

kimyw1018 리뷰어가 지적한 대로, DTO 네이밍 규칙이 일관되지 않습니다:

현재 상태:

  • scripts.dto.ts: UpdateScriptRequestDto, GetScriptResponseDto ✅ (Request/Response 명시)
  • slides.dto.ts: CreateSlideResponseDto, UpdateSlideTitleRequestDto
  • comments.dto.ts: CreateCommentRequestDto, CommentUserDto ⚠️ (후자는 Request/Response 구분 없음)
  • analytics.dto.ts: RestoreScriptRequestDto

혼란스러운 export:

  • src/api/dto/index.ts:24: RestoreScriptRequestDtoanalytics.dto.ts에서 export (스크립트 복원인데 왜 analytics?)

권장 사항:

  1. 모든 DTO에 RequestDto 또는 ResponseDto suffix 명시 (data 객체 제외)
  2. RestoreScriptRequestDtoscripts.dto.ts로 이동 (현재 analytics.dto.ts에 위치)
  3. CommentUserDto, CommentWithUserDto 같은 중간 모델은 별도 타입으로 관리 (예: types/comment.ts)

6. CodeQL 정적 분석 경고

위치: src/mocks/handlers.ts:1028, 1042, 1052, 1066

github-code-quality 봇이 감지한 이슈:

  1. Useless assignment (Line 1028, 1052): found = true 직후 return하므로 값 미사용
  2. Useless conditional (Line 1042, 1066): if (!found) 조건이 항상 true

근본 원인: PATCH /comments/:commentId 핸들러의 로직 구조 문제

수정 방안:

// found 변수 제거 가능
// 첫 번째 루프에서 찾으면 즉시 return
// 두 번째 루프에서 찾으면 즉시 return
// 어디서도 못 찾으면 404 return (무조건 실행)

상세 수정은 CodeQL 제안을 따르되, found 플래그 제거로 간소화 가능


📝 Minor Issues & Suggestions

7. Legacy 주석 오해의 소지

위치: src/mocks/handlers.ts:231-232

/**
 * 프로젝트의 슬라이드 목록 조회 (Legacy)
 * GET /presentations/:projectId/slides
 */

문제: 주석에 "(Legacy)"가 있지만, src/api/endpoints/slides.ts:28getSlides()가 현재 이 엔드포인트 사용 중

추측: 오히려 Line 209의 /projects/:projectId/slides가 legacy일 가능성

수정 방안: 주석 수정 또는 제거


8. Deprecated 타입의 지속 사용

위치: src/mocks/handlers.ts:18, 265, 333

let slides: Slide[] = [...MOCK_SLIDES];  // Line 18
const slide = slides.find((s) => s.id === slideId);  // Line 265
const newSlide: Slide = { ... };  // Line 333

문제: Slide 타입은 src/types/slide.ts:32에서 @deprecated 표시

권장: 점진적으로 SlideListItem 또는 GetSlideResponseDto로 마이그레이션

우선순위: 낮음 (MSW는 개발 환경이므로)


9. createSlide 함수의 인라인 타입

위치: src/api/endpoints/slides.ts:85

export async function createSlide(
  projectId: string,
  data: { title: string; script?: string },  // Inline type
): Promise<GetSlideResponseDto>

문제: DTO 파일에 CreateSlideRequestDto 정의가 없고 인라인 타입 사용

권장:

// slides.dto.ts에 추가
export interface CreateSlideRequestDto {
  title: string;
  script?: string;
}

// slides.ts에서 사용
import type { CreateSlideRequestDto } from '@/api/dto';

10. 에러 처리 일관성

위치: src/api/endpoints/presentations.ts:98-100, slides.ts:106-108

DELETE 함수들이 다른 패턴 사용:

if (response.data.resultType === 'FAILURE') {
  throw new Error(response.data.error.reason);
}
// No explicit SUCCESS check

권장: 다른 함수들과 일관되게 SUCCESS 명시적 확인

if (response.data.resultType === 'SUCCESS') {
  return;  // void 함수이므로 명시적 return
}
throw new Error(response.data.error.reason);

참고: Issue #4의 unwrapApiResponse 헬퍼 도입 시 자동 해결


11. src/api/dto/analytics.dto.ts 주석

위치: src/api/dto/analytics.dto.ts:8

//위 형식대로 response, request dto 작성 부탁드립니다.

문제: 커밋에 개발자 간 커뮤니케이션 주석 포함

권장: 주석 제거 또는 공식 가이드로 변경


🔍 추가 관찰 사항

12. MSW 핸들러의 deprecated 필드 사용

위치: src/mocks/handlers.ts:265

const slide = slides.find((s) => s.id === slideId);  // 여전히 'id' 사용

이유: MSW는 deprecated Slide 타입 사용 중이므로 s.id가 맞음

영향: 없음 (MSW 내부 일관성 유지)


📊 리뷰 요약

구분 개수 우선순위
Critical (배포 전 필수) 3 High
Major (다음 릴리스) 4 Medium
Minor (점진적 개선) 5 Low

🎯 권장 수정 순서

배포 전 필수 (High Priority):

  1. ✅ MSW DELETE 핸들러 수정 (chore: 프로젝트 세팅 #1)
  2. ✅ MSW GET/POST/PATCH 슬라이드 핸들러 수정 (chore/프로젝트 세팅 #2, Closed #3)

다음 릴리스 (Medium Priority):
3. unwrapApiResponse 헬퍼 도입 (#4)
4. DTO 네이밍 일관성 개선 (#5)
5. CodeQL 경고 수정 (#6)

점진적 개선 (Low Priority):
6. Legacy 주석 정리 (#7)
7. Deprecated 타입 마이그레이션 (#8)
8. CreateSlideRequestDto 추가 (#9)
9. DELETE 함수 일관성 (#10)
10. analytics.dto.ts 주석 제거 (#11)


💡 전체 평가

✅ 강점:

  • Discriminated Union으로 타입 안전성 대폭 향상
  • DTO 계층 분리로 관심사 분리 명확
  • 67개 파일에 걸친 일관된 필드명 마이그레이션
  • 에러 구조 표준화 (errorCode, reason)
  • 비디오 API도 공통 ApiResponse 래퍼 적용

⚠️ 개선 필요:

  • MSW 핸들러가 새 API 인터페이스 미준수 → 개발 환경 런타임 에러 가능성
  • API 응답 처리 로직 중복 → 유지보수성 저하
  • DTO 네이밍 규칙 불일치
  • 정적 분석 경고 존재

종합: 잘 설계된 리팩토링이지만, MSW 핸들러 수정이 최우선입니다. 이후 unwrapApiResponse 헬퍼 도입으로 코드 품질을 한층 더 높일 수 있습니다.


@kimyw1018 kimyw1018 merged commit e665a6d into develop Feb 4, 2026
8 checks passed
@AndyH0ng AndyH0ng deleted the refactor/setting-type-111 branch February 4, 2026 09:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component 컴포넌트 관련 변경 hooks 커스텀 훅 변경 page 페이지 관련 변경 store 상태 관리 변경 types 타입 정의 변경

Projects

None yet

Development

Successfully merging this pull request may close these issues.

refactor: 타입 맞추기

2 participants