시리즈 물이 될지 모르겠지만, 이번에 3주간 프로젝트를 진행하며 자주 사용하게 된 타입스크립트 정의하는 방법에 대해서 작성해보고자 헙니다.
(당연하지만)CURD를 구현할 때 데이터에 대한 타입을 정의하는데 주로 사용되었고, 복습하는 겸 정리해보고자 합니다.
삼국지 국가 중 '오나라'의 데이터를 다룬다고 가정해보겠습니다.
아래와 같은 json파일이 프로젝트에 사용될 예정입니다.
{
"country": "오(吳)",
"ruler": "손권(孫權)",
"capital": "건업(建業)",
"foundation_year": 222,
"dissolution_year": 280,
"notable_people": [
{
"name": "손권(孫權)",
"title": "대제(大帝)",
"role": "초대 황제",
"description": "손견의 아들로, 손책의 동생. 뛰어난 통치력으로 오를 건국하고 안정시켰다."
},
{
"name": "손책(孫策)",
"title": "오왕(吳王)",
"role": "창업자",
"description": "손견의 장남. 강남 지역을 평정하며 오 나라의 기반을 다졌다."
},
{
"name": "주유(周瑜)",
"title": "대도독(大都督)",
"role": "군사 전략가",
"description": "적벽대전의 주역 중 한 명. 지략과 외교술로 유명하다."
},
{
"name": "여몽(呂蒙)",
"title": "대도독(大都督)",
"role": "군사 전략가",
"description": "지식과 군략으로 손권의 신뢰를 받았으며 관우를 사로잡은 업적이 있다."
},
{
"name": "육손(陸遜)",
"title": "대도독(大都督)",
"role": "군사 전략가",
"description": "이릉대전에서 유비를 격파하며 오의 명성을 높였다."
}
],
"key_events": [
{
"year": 208,
"event": "적벽대전",
"description": "조조의 대군을 주유와 제갈량의 협력으로 물리침."
},
{
"year": 222,
"event": "손권, 황제 즉위",
"description": "오 나라가 정식으로 건국됨."
},
{
"year": 229,
"event": "수도 이전",
"description": "수도를 무창에서 건업으로 옮김."
},
{
"year": 280,
"event": "오 멸망",
"description": "진나라의 공격으로 멸망."
}
],
"culture": {
"economy": "강남 지역의 풍부한 자원과 농업을 기반으로 번영.",
"military": "수군을 중심으로 강력한 해군력을 보유.",
"art": "문인과 학자들이 활약하며 강남 문화 번영."
}
}
데이터 타입을 지정하는 방법은 type과 interface가 있습니다. 두 방식 중에 편한 방식을 사용하면 됩니다.
type과interface차이interface
- 객체의 구조나 클래스와의 호환성에 주로 사용됨
- 확장(extends), 구현 (implements) 가능
- 선언 병합이 가능하여, 같은 이름으로 여러 번 선언할 수 있음
type
- 유니언 타입, 교차 타입, 리터럴 타입 등 다양한 타입 정의 가능
- 선언 병합 불가
https://f-lab.kr/insight/typescript-type-vs-interface-20240801?gad_source=1&gclid=CjwKCAiA65m7BhAwEiwAAgu4JGcKxK487ovF5ygQ8DuUO_TpRmD9Fh-VBLXv4ATFSGYrc-LfcZSh3BoCuDgQAvD_BwE)
array로 이루어진 경우 []를 끝에 삽입해주면 됩니다.
interface CountryData {
country: string;
ruler: string;
capital: string;
foundation_year: number;
dissolution_year: number;
notable_people: {
name: string;
title: string;
role: string;
description: string;
}[];
key_events: {
year: number;
event: string;
description: string;
}[];
culture: Culture;
}
interface를 key 별로 분리해서 재정의 해준다면,
1. 가독성 향상
2. 데이터 타입 추가/삭제 시 용이
에 편리하다는 장점이 있는데, 아래 데이터만 보더라도 어떤 식으로 용이한지 바로 파악이 가능할 거라 예상 됩니다.
특히 모든 데이터(CountryData 원문)를 필요로하지 않는 컴포넌트에서 일부 데이터(Culture, KeyEvent 등)만 필요로 하는 경우가 많은데, 해당 경우에도 분리되어 선언된 데이터 타입을 가져오면 편리합니다.
interface NotablePerson {
name: string;
title: string;
role: string;
description: string;
}
interface KeyEvent {
year: number;
event: string;
description: string;
}
interface Culture {
economy: string;
military: string;
art: string;
}
interface CountryData {
country: string;
ruler: string;
capital: string;
foundation_year: number;
dissolution_year: number;
notable_people: NotablePerson[];
key_events: KeyEvent[];
culture: Culture;
}
만약, Culture데이터 타입에 새로운 데이터를 추가 시킨 interface를 생성하고 싶다면 굳이 새로운 interface를 선언하지 않고, extends를 사용하면 됩니다.
interface NewCulture extends Culture {
politics: string;
}
기존의 economy, military, art의 데이터 타입을 유지하면서 politics이라는 타입이 추가 됩니다.
지금까지 Interface로 선언 했을 경우에만 알아봤는데, Type이 활용되는 경우에 대해서 알아보겠습니다. Generic을 활용할 때 용이하게 사용될 수 있는데, API Response를 받아올 때 특히 활용도가 높습니다.
사실상 Interface와 신텍스 이외에 큰 차이가 없습니다.
type NotablePerson = {
name: string;
title: string;
role: string;
description: string;
};
type KeyEvent = {
year: number;
event: string;
description: string;
};
type Culture = {
economy: string;
military: string;
art: string;
};
type CountryData = {
country: string;
ruler: string;
capital: string;
foundation_year: number;
dissolution_year: number;
notable_people: NotablePerson[];
key_events: KeyEvent[];
culture: Culture;
};
type에서도 교차 타입을 활용해 유사 확장(extends)이 가능하기도 합니다.
type NewCulture = Culture & {
politics: string;
};
아래와 같은 APIResponse 타입을 지정한 후에, Generic을 활용할 수 있습니다.
type ApiResponse<T> = {
data: T[];
totalPage: number;
page: number;
};
T는 Type의 약어로, Generic 타입 매개변수로 사용됩니다. 위 코드에서는 data 속성에 담길 데이터의 타입을 정의하고, 타입을 전달하여 data 배열이 어떤 타입의 요소를 포함할지 결정해줍니다.
type OnaraApiResponse = ApiResponse<CountryData>
위 처럼 Generic에 Type을 전달하여 사용 가능합니다. T가 CountryData로 지정되므로, data 속성은 CountryData[] 형태의 배열로 처리됩니다.
이전까지 타입스크립트의 type과 interface의 큰 차이를 몰랐었는데, 프로젝트를 진행하며 차이점을 정리하는 과정을 겪었습니다. interface는 주로 객체 타입 정의에 유용하고, type은 다양한 타입의 조합(본 글에는 없지만 Omit 등 기능이 더 많음)에 있어서 유리하다고 생각됩니다. 프로젝트를 진행하며 주로 컴포넌트의 타입은 interface, 데이터 타입은 type으로 활용하게 되었는데 다음 프로젝트에서 사용 방법에 변화가 있다면 다른 글로 가져와보도록 하겠습니다.