description: "Entities model 폴더 - zod 스키마 및 타입 정의 패턴" paths:
model/은 프론트에서 실제로 사용하는 도메인 타입을 정의한다.
mapper가 API 응답을 변환한 결과가 이 model의 Schema/Type이 된다.
API 응답 → dto (백엔드 원본 타입) → mapper → model (Schema/Type) → 컴포넌트/훅에서 사용
dto는 백엔드 응답을 그대로 반영한 타입(snake_case)이고, model은 프론트에서 쓸 도메인 타입(camelCase)이다.
(엔티티명)Schema(엔티티명)Type// model/chat-title-list.ts
import { z } from 'zod';
export const ChatTitleListSchema = z.object({
sessionId: z.string(),
title: z.string(),
createdAt: z.string(),
updatedAt: z.string().optional(),
});
export type ChatTitleListType = z.infer<typeof ChatTitleListSchema>;
mapper는 dto 타입을 받아 이 Schema로 변환한다.
// mapper/map-chat-title-list.ts
import { ChatTitleListSchema, ChatTitleListType } from '../model/chat-title-list';
import { ChatTitleListDto } from '../dto/chat-title-list-dto';
export const mapChatTitleList = (item: ChatTitleListDto): ChatTitleListType | undefined => {
const result = ChatTitleListSchema.safeParse({
sessionId: item.session_id,
title: item.title || 'Untitled',
createdAt: item.created_at,
updatedAt: item.updated_at,
});
if (!result.success) {
console.error('[mapChatTitleList] 파싱 실패:', result.error);
return undefined;
}
return result.data;
};
단순한 타입이면 interface/type으로 정의하되 네이밍은 동일하게 (엔티티명)Type을 따른다.
export interface ChatTitleListType {
sessionId: string;
title: string;
createdAt: string;
updatedAt?: string;
}
용도가 다른 모델은 파일을 분리한다.
model/
├── chat-title-list.ts # 목록
├── chat-detail.ts # 상세
└── chat-update.ts # 수정 요청
const로, Type은 type으로 export아직 피드백이 없어요. 첫 번째로 의견을 남겨보세요!