Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
27 changes: 15 additions & 12 deletions src/api/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -387,19 +387,22 @@ export const useGetGuidebookQuery = ({
/** 개발용 목 데이터 (API 연동 후 제거) */
const getGuidebookMockData = (): GuidebookIdSchema =>
getGuidebookIdSchema.parse({
id: 0,
title: '가이드북 제목',
desc: '가이드북 설명',
shortDesc: '가이드북 짧은 설명',
thumbnail: null,
desktopThumbnail: null,
contentStructure: '자료 구성 내용',
contentComposition: '자료 구성 내용',
accessMethod: '가이드북 열람 방식',
recommendedFor: '가이드북 추천 대상',
priceInfo: {
priceId: 1,
price: 10000,
discount: 1000,
guidebookPriceType: 'CHARGE',
},
description: '가이드북 설명',
isVisible: true,
job: null,
contentUrl: null,
contentFileUrl: null,
price: 10000,
discount: 1000,
Comment on lines +390 to +404

Choose a reason for hiding this comment

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

medium

Mock 데이터에서 사용된 ID(0), 가격(10000) 및 할인 금액(1000)과 같은 숫자는 의미를 명확히 하고 유지보수성을 높이기 위해 명명된 상수로 대체하는 것이 좋습니다.

References
  1. 의미가 불분명한 매직 넘버를 명명된 상수로 대체하여 가독성을 높여야 합니다. (link)

programTypeInfo: [],
});

/** RSC/페이지용 가이드북 상세 조회 (fetch 사용) */
Expand Down Expand Up @@ -433,7 +436,7 @@ export const usePostGuidebookMutation = ({
} = {}) => {
return useMutation({
mutationFn: async (data: CreateGuidebookReq) => {
const res = await axios.post(`/admin/guidebook`, data);
const res = await axios.post(`/guidebook`, data);
return res.data as unknown;
},
onSuccess: successCallback,
Expand All @@ -451,15 +454,15 @@ export const usePatchGuidebookMutation = ({
return useMutation({
mutationFn: async (data: UpdateGuidebookReq & { guidebookId: number }) => {
const { guidebookId, ...rest } = data;
const res = await axios.patch(`/admin/guidebook/${guidebookId}`, rest);
const res = await axios.patch(`/guidebook/${guidebookId}`, rest);
return res.data as unknown;
},
onSuccess: successCallback,
onError: errorCallback,
});
};

/** DELETE /admin/guidebook/{id} 가이드북 삭제 */
/** DELETE /guidebook/{id} 가이드북 삭제 */
export const useDeleteGuidebookMutation = ({
errorCallback,
successCallback,
Expand All @@ -469,7 +472,7 @@ export const useDeleteGuidebookMutation = ({
} = {}) => {
return useMutation({
mutationFn: async (guidebookId: number) => {
const res = await axios.delete(`/admin/guidebook/${guidebookId}`);
const res = await axios.delete(`/guidebook/${guidebookId}`);
return res.data as unknown;
},
onSuccess: successCallback,
Expand Down
4 changes: 2 additions & 2 deletions src/app/(user)/program/guidebook/[id]/[title]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ export async function generateMetadata({

return {
title,
description: program.desc ?? undefined,
description: program.shortDesc,
openGraph: {
title,
description: program.desc ?? undefined,
description: program.shortDesc || undefined,
url,
images: [
{
Expand Down
7 changes: 3 additions & 4 deletions src/domain/program/guidebook/ui/GuidebookBasicInfoSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,8 @@ const GuidebookBasicInfoSection = ({
}: GuidebookBasicInfoSectionProps) => {
const thumbnail =
guidebook.thumbnail ?? guidebook.desktopThumbnail ?? null;
const priceInfo = guidebook.priceInfo;
const price = priceInfo?.price ?? 0;
const discount = priceInfo?.discount ?? 0;
const price = guidebook.price ?? 0;
const discount = guidebook.discount ?? 0;

return (
<div className="mx-auto w-full max-w-[1000px] px-5 pb-10 md:px-0 md:pb-20">
Expand All @@ -47,7 +46,7 @@ const GuidebookBasicInfoSection = ({
<BasicInfoRow
icon={<FolderIcon />}
title="자료 구성"
content={guidebook.contentStructure}
content={guidebook.contentComposition}
/>
<BasicInfoRow
icon={<FileIcon />}
Expand Down
16 changes: 10 additions & 6 deletions src/hooks/useDuplicateProgram.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,13 +122,17 @@ export const guidebookToCreateInput = (
guidebook: GuidebookIdSchema,
): CreateGuidebookReq => ({
title: (guidebook.title ?? '') + ' - 사본',
shortDesc: guidebook.shortDesc ?? '',
thumbnail: guidebook.thumbnail ?? '',
desktopThumbnail: guidebook.desktopThumbnail ?? '',
contentStructure: guidebook.contentStructure ?? '',
contentComposition: guidebook.contentComposition ?? '',
accessMethod: guidebook.accessMethod ?? '',
recommendedFor: guidebook.recommendedFor ?? '',
desc: guidebook.desc ?? '',
programTypeInfo: (guidebook.classificationInfo ?? []).map((value) => ({
description: guidebook.description ?? '',
job: guidebook.job ?? '',
contentUrl: guidebook.contentUrl ?? '',
contentFileUrl: guidebook.contentFileUrl ?? '',
programTypeInfo: (guidebook.programTypeInfo ?? []).map((value) => ({
classificationInfo: {
programClassification: value.programClassification ?? 'PASS',
},
Expand All @@ -141,10 +145,10 @@ export const guidebookToCreateInput = (
}))
: [],
priceInfo: {
guidebookPriceType: guidebook.priceInfo?.guidebookPriceType ?? 'CHARGE',
guidebookPriceType: guidebook.guidebookPriceType ?? 'CHARGE',
priceInfo: {
price: guidebook.priceInfo?.price ?? 0,
discount: guidebook.priceInfo?.discount ?? 0,
price: guidebook.price ?? 0,
discount: guidebook.discount ?? 0,
},
},
});
Expand Down
105 changes: 52 additions & 53 deletions src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -597,32 +597,30 @@ export type UpdateLiveReq = {
}[];
};

/** GET /api/v1/guidebook/{id} 가이드북 상세 조회 */
/** GET /api/v1/guidebook/{guidebookId} 가이드북 상세 조회 */
export const getGuidebookIdSchema = z.object({
title: z.string(),
desc: z.string().nullable().optional(),
id: z.number(),
title: z.string().nullable().optional(),

Choose a reason for hiding this comment

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

medium

가이드북 상세 조회 스키마에서 titlenullable().optional()로 변경되었습니다. CreateGuidebookReq에서 title이 필수 값인 점을 고려할 때, 상세 조회 결과에서도 항상 존재할 것으로 기대되므로 z.string()과 같이 필수 값으로 유지하여 타입 안정성을 강화하는 것을 권장합니다.

Suggested change
title: z.string().nullable().optional(),
title: z.string(),

shortDesc: z.string().nullable().optional(),
thumbnail: z.string().nullable().optional(),
desktopThumbnail: z.string().nullable().optional(),
contentStructure: z.string().nullable().optional(),
contentComposition: z.string().nullable().optional(),
accessMethod: z.string().nullable().optional(),
recommendedFor: z.string().nullable().optional(),
materialUrl: z.string().nullable().optional(),
priceInfo: z
.object({
priceId: z.number(),
price: z.number().optional().nullable(),
discount: z.number().optional().nullable(),
accountNumber: z.string().optional().nullable(),
accountType: accountType.optional().nullable(),
guidebookPriceType: guidebookPriceTypeSchema.optional().nullable(),
})
.optional(),
classificationInfo: z
description: z.string().nullable().optional(),
job: z.string().nullable().optional(),
contentUrl: z.string().nullable().optional(),
contentFileUrl: z.string().nullable().optional(),
price: z.number().nullable().optional(),
discount: z.number().nullable().optional(),
guidebookPriceType: guidebookPriceTypeSchema.nullable().optional(),
programTypeInfo: z
.array(
z.object({
programClassification: ProgramClassificationEnum.nullable().optional(),
}),
)
.nullable()
.optional(),
adminClassificationInfo: z
.array(
Expand All @@ -636,70 +634,71 @@ export const getGuidebookIdSchema = z.object({

export type GuidebookIdSchema = z.infer<typeof getGuidebookIdSchema>;

/**
* POST /api/v1/admin/guidebook 가이드북 생성
* 백엔드 확정 후 필수/이름 수정 예정
*/
/** POST /api/v1/guidebook 가이드북 생성 */
export type CreateGuidebookReq = {
programTypeInfo?: {
classificationInfo: { programClassification: ProgramClassification };
}[];
adminProgramTypeInfo?: {
classificationInfo: {
programAdminClassification: ProgramAdminClassification;
};
}[];
job?: string;
title: string;
shortDesc?: string;
contentStructure?: string;
accessMethod?: string;
recommendedFor?: string;
priceInfo?: {
shortDesc: string;
thumbnail: string;
desktopThumbnail?: string;
contentComposition: string;
accessMethod: string;
recommendedFor: string;
description: string;
job: string;
contentUrl?: string;
contentFileUrl?: string;
priceInfo: {
priceInfo: {
price: number;
discount: number;
accountNumber?: string;
accountType?: string;
deadline?: string;
accountType?: AccountType;
};
guidebookPriceType: GuidebookPriceType;
guidebookPriceType?: GuidebookPriceType;

Choose a reason for hiding this comment

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

medium

CreateGuidebookReq에서는 guidebookPriceType이 선택 사항(?)으로 정의되어 있으나, UpdateGuidebookReq(692행)에서는 필수 값으로 정의되어 있어 일관성이 떨어집니다. 가이드북 생성 시에도 가격 타입을 명확히 지정해야 하므로 필수 값으로 변경하는 것이 적절해 보입니다.

Suggested change
guidebookPriceType?: GuidebookPriceType;
guidebookPriceType: GuidebookPriceType;

};
materialUrl?: string;
thumbnail?: string;
desktopThumbnail?: string;
desc?: string;
};

/** PATCH /api/v1/admin/guidebook/{id} 가이드북 수정 */
export type UpdateGuidebookReq = {
programTypeInfo?: {
programTypeInfo: {
classificationInfo: { programClassification: ProgramClassification };
}[];
adminProgramTypeInfo?: {
classificationInfo: {
programAdminClassification: ProgramAdminClassification;
};
}[];
job?: string;
};

/** PATCH /api/v1/guidebook/{guidebookId} 가이드북 수정 */
export type UpdateGuidebookReq = {
title?: string;
shortDesc?: string;
contentStructure?: string;
thumbnail?: string;
desktopThumbnail?: string;
contentComposition?: string;
accessMethod?: string;
recommendedFor?: string;
description?: string;
isVisible?: boolean;
job?: string;
contentUrl?: string;
contentFileUrl?: string;
priceInfo?: {
priceInfo: {
price: number;
discount: number;
accountNumber?: string;
accountType?: string;
deadline?: string;
accountType?: AccountType;
};
guidebookPriceType: GuidebookPriceType;
};
materialUrl?: string;
thumbnail?: string;
desktopThumbnail?: string;
desc?: string;
isVisible?: boolean;
programTypeInfo?: {
classificationInfo: { programClassification: ProgramClassification };
}[];
adminProgramTypeInfo?: {
classificationInfo: {
programAdminClassification: ProgramAdminClassification;
};
}[];
};

// ADMIN LIVE 클래스 및 VOD 클래스 직무
Expand Down