[개발일지] 22년 33주차 - TypeScript

FeRo 페로·2022년 8월 22일
0
post-thumbnail

리액트 배우러 갔다가 TS 맛보기

React Master코스를 노마드에서 수강하는데 TS를 알려줬다. TS를 배울 생각이 있지는 않았는데 요즘 추세도 그렇고 오랜만에 강타입 언어를 배워보고 싶었기 때문에 재밌게 배웠다. 하지만 거기서는 자세하게 가르쳐 주지는 않았다. TS로 리액트를 쓸 건데 이런 부분이 필요하다 정도를 간단히 배웠다. 하지만 그걸로는 부족했다. 그래서 강의를 듣는 중간에도 '왜 이렇게 될까?'하는 생각이 많이 들었다. 찾아보기도 했지만 파편적으로 찾아보다 보니 이해가 더 안되는 느낌이 강했다.

그러던 와중에 희정님이랑 TS 이야기를 하게 되었다. '님도.. TS 공부함? 그럼 스터디??'하고 순식간에 스터디를 하기로 했다. 한 주 정도 했는데 배운 게 제네릭과 다형성, type과 interface의 차이가 있다. 오늘은 굵직한 내용을 중심으로 해서 한 주간 공부를 되돌아 볼까 한다.

다형성, 처음 뵙겠습니다.

이번에 스터디를 하면서 다형성에 대해서 배웠다. 다형성이라는 개념을 알게 된 것은 이번이 처음이었다. 영어로 하면 Polymorphism인데, 즉 다양한 형태를 말한다. 프로그래밍에서 다형성은 하나의 코드를 다양한 형태로 사용하는 것을 말한다. 아래 예시 코드에서 확인해보자.

// 제네릭으로 타입
type Add = <T>(a: T[]) => T;

const add: Add = (arr) => {
  arr.forEach((el) => console.log(el));
  return arr[0];
};

// 제네릭은 사용자가 type을 입력할 수 있도록 허용한다. 그래서 아래와 같이 입력을 하면 number | string | boolean으로 타입된다.
add([1, "string"]); 

아래는 typescript playground에서 위 코드를 작성한 후 add의 call signature를 확인한 결과이다.

Add를 제네릭으로 타입했기 때문에 typescript가 타입을 추론할 수 있고, 이를 통해서 다형성을 구현했다. 그리고 다형성을 구현한 코드이기 때문에 각각의 상황에서 다양하게 사용할 수 있다.

이쯤되면 제네릭에 대해 궁금증이 생길 것이다.

Generic

type Add = (a:Array<string|number>) => string|number|boolean;
const add:Add = (arr)=>{
    arr.forEach((el)=>console.log(el));
    return arr[0];
}

add([1,"string"]);

위 코드는 제네릭을 써 타입한 Add함수를 제네릭을 쓰지 않고 타입한 코드이다. 만약 여기서 add함수의 세 번째 배열 인자로 true라는 boolean을 넣으면 타입 에러가 발생한다.

왜냐하면 파라미터 타입 중 boolean이 없기 때문이다. 세 번째 인자로 true를 사용하기 위해서는 타입을 추가해주어야 한다. 하지만 제네릭은 이런 번거로운 부분이 없다. 위에서 말했듯이 typescript가 타입을 추론하기 때문이다.

아니 그러면 any랑 뭐가 다른가요?

Any는 typescript의 보호를 받을 수 없다는 뜻이다. 어떤(any) 타입도 들어올 수 있다는 시멘틱도 있지만 어떤 보호도 받을 수 없다는 표현이 훨씬 정확하다고 생각한다.

any로 타입을 하면 call signature부터가 다르게 나온다. 즉 제네릭으로 타입했을 때는 타입을 추론하기 때문에 추론된 타입을 바탕으로 보호받을 수 있지만 any로 타입을 하면 어떠한 보호도 받을 수 없다는 차이가 있다.

type, interface

두 가지는 비슷하면서도 다른 점이 확실하다. 아래 내용에서 두 가지를 비교해보도록 하자.

타입 범위

먼저, type은 다재다능한 친구이다. Alias처럼 쓸 수도 있고 객체를 타입할 수도 있고 개별적인 value를 타입할 수도 있다. 이 세 가지 경우를 아래 예시 코드로 확인해보자.

type Player = string; // string을 Player로 alias
type Team = { // 객체를 타입
    member: string[]
}
type Support = "Grazie" // 특정 value로 타입

하지만 interface는 객체를 타입하는 용도로 사용할 수 있다.

interface Team {
    player: string,
    league: string[]
}

중복선언

또 다른 점으로는 중복선언이 되는지 여부이다. type은 중복선언이 안된다. 하지만 interface는 중복선언을 하면 이전에 선언해 둔 내용에서 추가가 된다. 내용이 축적되는 것이다.

상속

상속할 때의 시멘틱도 다르다. type은 &연산자를 통해서 상속을 나타내지만 interface는 extends를 사용한다.

type이 더 포괄적인 개념이 아닌가요?

그렇게 생각할 수 있다. 그리고 어느 정도는 맞는 말이라고 본다. 하지만 시멘틱적으로 상속 시에 extends 키워드를 사용하기 때문에 interface가 더 객체지향에 적합하다고 생각한다. 그래서 class를 타입하기 위해서 추상 클래스를 만들 수 있지만 interface를 통해서 하면 시멘틱적으로 이질감이 없으면서도 js로 컴파일 시 깔끔함을 유지할 수 있다.

마무리

타입스크립트는 자바스크립트의 슈퍼셋 프로그래밍 언어답게 생산성과 안정성을 높여준다는 느낌을 받았다. 특히나 타입된 결과를 바탕으로한 안정적인 auto-complete는 강한 인상을 주었다. 아직은 걸음마 단계이지만, 지금까지 배운 걸 바탕으로라도 react에도 적용시키면 재밌겠다는 생각을 했다.

profile
주먹펴고 일어서서 코딩해

0개의 댓글