타입스크립트를 접한 지 이제 한 5-6개월이 다 되어간다. 하지만 이론적으로 제대로 공부한 게 많이 없어서 그런지 여전히 모르는 게 너무 많다. 흑흑
그저 개발 기간에 맞춰 기능 구현에만 급급해서 제대로 짚고 넘어가지 못한 개념들이 많다. 그 중 하나가 type과 interface이다. 매번 개발을 할 때마다 나는 그냥 확장 가능성이 있어보이면 interface를 쓰고, 그렇지 않으면 거의 다 type을 쓰곤 했다.
오늘은 type과 interface에 톺아보고 내가 지금까지 잘 사용을 했던 건지 아니면 잘못 사용을 하고 있는 건 아닌지 검토해보려고 한다.
시작하기에 앞서, 빠지면 섭섭한 공식문서를 읽고 가보자! ㅎ.ㅎ
위 내용을 간단히 살펴보자.
type A = string;
fieldsToUpdate: Partial<Todo> …
const todo: Readonly<Todo> = { … }
const cats: Record<CatName, CatInfo> = { … }
type TodoPreview = Pick<Todo, ‘title’ | ‘completed’>
모두 작성하기엔 너무 많다. 읽어보니 몰랐던 방식들이 많다.
일단 차근차근 가보장!
📌 Type과 interface 선언
일단 타입을 선언하는 방법은 다음과 같다.
type SimpleType = {
name: string;
age: number;
}
인터페이스 선언 방법도 거의 똑같다.
interface SimpleType {
name: string;
age: number;
}
그럼 차이점은 뭘까?
⇒ interface는 당연한 얘기지만 객체에서만 사용이 가능하고, index signature 사용 유무에 따라 차이가 있다.
이제 확장 가능성을 따져보자.
interface Person {
name: string;
age: number;
}
interface SimpleMan extends Person {
simple: string;
}
const chaemin: SimpleMan = {
name: 'chaemin';
age: 22;
simple: 'hello-world';
}
interface는 다음과 같이 확장할 수 있다.
type Person = {
name: string;
age: number;
}
type SimpleMan = Person & {
simple: string;
}
const chaemin: SimpleMan = {
name: 'chaemin';
age: 22;
simple: 'hello-world';
}
type은 다음과 같이 & 앤드 기호를 통해 확장이 가능하다. 상속이라고도 할 수 있겠다.
흠..여기서 잠깐
❓가장 큰 차이점이 뭘까?
⇒ 바로 “확장 가능성”
이다.
type은 유니온 타입 및 다른 타입을 활용해 사용이 가능하지만, 이미 정의되어 있는 타입에 있어선 추가적인 확장이 어렵다.
하지만! interface는 상속을 통해 다른 인터페이스를 확장할 수 있기 때문에 더 유연하게 타입 구성이 가능하다.
역할을 정리해보자.
흠..
각각의 개념은 그래도 대충 다 알겠는데, 그래서 뭐 어떻게 잘 써먹어야 할지는 아직 감이 안온다.
예시 코드를 가져와보자. 😈
아래 코드는 DND 프로젝트에서 사용했던 타입들 중 하나이다.
export type Profile = {
nickname: string;
career: string;
profileImageUrl: string;
birthday: string;
email: string;
field: string;
level: number;
feedbackCount: number;
projectCount: number;
likeCount: number;
restFeedbackCount: number;
};
export type UserInfoProps = {
nickname: string;
birthday: string;
email: string;
interest: string;
profileImgUrl: string;
career: string;
};
export type UserFeedBackInfoProps = {
level: number;
restFeedbackCount: number;
feedbackCount: number;
projectCount: number;
likeCount: number;
};
보니까 중복되는 property가 많다. UI와 함께 봐보자.
설명하기에 앞서 너무 귀엽당 !!! ㅋㅋㅋ
아무튼 이 전체 컴포넌트에 데이터를 받아오기 위해서는 Profile
타입이 필요하다. 즉, Profile이 가장 큰 타입이라고 할 수 있겠다.
여기서 우린 아래 이미지처럼 버튼을 기준으로 크게 두 area로 나누어 구현하였다.
즉 데이터가 두 컴포넌트로 각각 전달되어야 한다.
위에 있던 type 코드들은 각각의 컴포넌트에 맞게 따로따로 작성되었다면, 이제 확장성을 높여 상속으로 구현을 다시 해보자.
export interface Profile extends UserInfoProps, UserFeedBackInfoProps {
field: string;
};
export interface UserInfoProps {
nickname: string;
birthday: string;
email: string;
interest: string;
profileImgUrl: string;
career: string;
};
export interface UserFeedBackInfoProps {
level: number;
restFeedbackCount: number;
feedbackCount: number;
projectCount: number;
likeCount: number;
};
이렇게 되면 Profile
인터페이스가 UserInfoProps
인터페이스와 UserFeedBackInfoProps
인터페이스의 모든 속성을 포함하게 된다.
추후 개발 수정 사항이 생겨서 UserInfoProps에 속성을 추가해야할 경우엔 UserInfoProps만 수정하면 된다.
이럴 때 interface를 쓰는 게 아닐까!?
예시 코드를 보면 알겠듯이, 개발을 진행할 때에는 확장성을 깊게 생각하지 않고 코드를 짠게 다반사였다. 지금은 Profile 관련 타입에 대해서만 언급을 했지만, 다른 파일에서도 이렇게 상속을 활용하지 않고 타입을 때려박은 곳들이 몇 개 있는 것 같다. ㅎㅎ…
얼른 고쳐야겠다~
계속 쉬운 내용의 TIL을 작성하고 있는 것 같은데, 그래도 꾸준함이 중요한 게 아닐까? ㅎㅎ
추후에 난이도 있고 깊은 내용을 잘 이해하고 다룰 수 있도록 지금 이순간에 최선을 다하자 😈
그리고 오늘 ‘함께 자라기’책을 빌렸다. 나의 롤모델 멘토님이 추천하신 책!!
얼른 읽어봐야겠당~