멋사사이트를 만드는 중 , 다음과 같은 3가지 아카이빙 페이지(프로젝트 아카이빙, 추억 아카이빙, 세션 아카이빙)을 제작해야 했다.
https://github.com/cau-likelion-org/cau-likelion-next/pull/26
Archiving 데이터 타입 변경 by d0422 · Pull Request #26 · cau-likelion-org/cau-likelion-next
🛠️ 한 줄 요약 아카이빙 데이터 타입 변경 상세 🔥 바꾼거 Archiving 상위 인터페이스로 하여 Project, Session, Gallery에서 상속 받아 디테일 프로퍼티 적용 컴포넌트 제네릭 사용하여 타입별 랜더링
github.com
해당 페이지들은 모두 동일하게 아래와 같은 Card형식을 사용하고 있다.
다만, 사진 아래의 내용이 세션카드냐, 프로젝트 카드냐, 추억 카드냐에 따라 1차세션, 아이디어톤, 날짜로 각 내용이 달라야 한다.
그래서 처음에는 이렇게 타입을 만들어주었다. category에 따라 그 내용이 달라지게 만든 것이다.
export interface IArchivingData {
id: number;
title: string;
category: string;
thumbnail: string;
dev_stack?: number[];
description?: string;
}
그리고 Archiving이라는 카드를 찍어주는 컴포넌트를 만들기 위해 api를 통해 받아온 데이터 타입을 이렇게 설계했다.
export type ArchivingArrayType = Record<string, IArchivingData[]>;
{
9:[
{
id:1,
title:"프로젝트이름",
dev_stack:[1,2,3,9,4],
description:"설명",
category:"해커톤"
thumbnail:"https://어쩌구~"
},
{
},
...
],
10:[{}],
11:[{},{}],
}
이런식의 데이터를
이렇게 찍어주는 것이다.
그런데 아래의 타입에 따르면 category라는 프로퍼티 키에 세션카드인 경우 몇차세션인지가, 추억카드인 경우는 날짜가 들어가게된다.
export interface IArchivingData {
id: number;
title: string;
category: string;
thumbnail: string;
dev_stack?: number[];
description?: string;
}
계속해서 떠오르는 ISP때문에 이걸 개선하기로 했다.
공통 부분을 IArchivingData로 냅두고, extends를 통해 바뀌는 부분을 해결한다.
export interface IArchivingData {
id: number;
title: string;
thumbnail: string;
description?: string;
}
export interface IProjectData extends IArchivingData {
category: string;
dev_stack: number[];
}
export interface ISessionData extends IArchivingData {
degree: number;
}
export interface IGalleryData extends IArchivingData {
date: string;
}
여기까진 순조롭다.
문제는 아래의 녀석과 실제 데이터를 찍어주는 컴포넌트들이다.
export type ArchivingArrayType = Record<string, IArchivingData[]>;
이녀석을 제네릭을 써서 고쳐줬다.
export type ArchivingArrayType<T> = Record<string, T[]>;
컴포넌트 역시 제네릭을 써서, 각 타입에 맞는 타입을 자동적으로 수용할 수 있게 만들었다.
const Archiving = <Type extends ISessionData | IProjectData | IGalleryData>({
archivingType,archivingIndex,archivingData,
}: {
archivingType: ArchivingType;
archivingIndex: string;
archivingData: Type[];
}){
...
}
세부 객체 데이터들은 다음과 같이 분류해서 화면에 렌더링 해주었다.
const getIndexMessageAndURL = (archivingType: ArchivingType, archivingIndex: number) => {
if (archivingType == 'session') {
return ['/session', TRACK[archivingIndex]];
}
if (archivingType == 'gallery') {
return ['/gallery', `${archivingIndex}년`];
}
return ['/project', `${archivingIndex}기`];
};
const getType = <Type extends ISessionData | IProjectData | IGalleryData>(data: Type) => {
if ('category' in data) return data.category;
if ('degree' in data) return data.degree;
if ('date' in data) return data.date;
};
랜더링 컴포넌트
...
<CardWrapper>
{archivingData.map((data, index) => (
<Card
key={index}
id={data.id}
link={link}
thumbnail={data.thumbnail}
title={data.title}
description={data.description}
dev_stack={'dev_stack' in data ? data.dev_stack : undefined}
category={getType(data)}
/>
))}
</CardWrapper>
...