본문 바로가기

4주차

[4주차] Typescript 왜 사용할까? 제대로 알고 사용하자

지금 당장 구직사이트에서 프론트엔드 개발자만 검색해봐도 많은 IT기업들은 Typescript 사용 경험자를 우대하거나 필수로 하고 있는 것을 볼 수 있다. 또한, Typescript의 중요성과 인기가 나날이 증가하고 있다. 그렇다면 왜? Javascript가 아닌 Typescript를 사용할까? 단순히 타입 체크를 위해서 일까? 지금 같이 알아보러 가자.


깃허브 인기 언어 순위 변화(이미지=깃허브)


=> Typescript의 그래프가 가장 가파른 상승세를 보여주고 있다.

Typescript가 Javascript를 대체하고 있는 이유를 알기 전에 Typescript와 Javascript의 차이점을 알아보자


📌동적 타입(Javascript) vs 정적 타입(Typescript)

 

동적 타입: 변수의 타입이 런타임 시점에 결정된다. 즉, 변수에 어떤 값이 할당될 때 그 값에 따라 타입이 결정된다.
ex) Python, JavaScript, Ruby
정적 타입: 변수의 타입이 컴파일 시점에 결정된다. 즉, 코드를 작성할 때 변수의 타입이 미리 선언되거나 추론된다.
ex) Java, C++, TypeScript

동적 타입: 타입 오류가 런타임 시점에 발생한다. 즉, 코드 실행 중에 잘못된 타입 사용으로 오류가 발생할 수 있다.
ex) JavaScript에서, 문자열과 숫자를 혼합한 연산이 의도치 않은 결과를 초래할 수 있음.
정적 타입: 타입 오류가 컴파일 시점에 발생한다. 즉, 컴파일러가 타입 불일치를 감지하여 코드 실행 전에 오류를 알려준다.
ex) TypeScript에서, 타입이 맞지 않는 경우 컴파일러가 오류를 보고합니다.

 

위를 통해 첫번째 Typescript 사용 이유를 알 수 있다. 바로, 정적 타입 체크이다. Typescript는 정적 타입 시스템을 제공하여, 코드 작성 중에 타입 오류를 미리 발견할 수 있다. 이는 런타임 오류를 줄이고 코드의 안정성을 높이는 데 도움이 된다. 이는 코드 품질 향상에도 도움이 된다(코드에 대한 명확한 타입 정의가 요구되므로). 그러나, Javascript는 동적 타입 언어로, 런타임에 타입 오류가 발생할 수 있고 의도치 않은 결과를 초래할 수 있다.

 


📌예시 코드

// math.js
function sum(a, b) {
  return a + b;
}

 

// math.ts
function sum(a: number, b: number) {
  return a + b;
}


두 코드 모두 두 숫자의 합을 구하는 함수 코드이다. 하나는 자바스크립트로 그리고 다른 하나는 타입스크립트로 작성하였다.

sum(10, 20); // 30


이렇게 sum 함수를 이용하여 숫자 10과 20을 더한다. 그러면 우리가 원하는 결과인 30을 얻을 수 있다.

그러나, 만약 아래와 같이 함수를 호출하면 어떻게 될까?

 

sum('10', '20'); // 1020


숫자 대신 문자열을 더하기 때문에 10 + 20 = 30이 아닌 1020이라는 결과가 나타나게 된다.

이처럼 의도하지 않은 코드의 동작을 예방할 수 있다. 자바스크립트의 버그 중 15%를 타입스크립트의 사용으로 미리 예방할 수 있다는 연구가 있다고 한다.

 



📌IDE 지원

IDE(예시로는 VisualStudioCode가 있다)에서 코드의 자동 완성, 디버깅, 문법 검사 등 다양한 기능을 활용할 수 있으며, 개발 생산성을 높일 수 있다.

📌객체 지향 프로그래밍

Typescript는 클래스, 인터페이스, 모듈 등 javascript에서 제공하지 않는 객체 지향 프로그래밍(OOP)의 기능을 지원한다. 이를 통해 코드의 재사용성과 유지보수성을 높일 수 있다.

📌제네릭(Generic)

제네릭(Generic)은 타입스크립트에서 함수, 클래스, 또는 인터페이스가 다양한 타입을 처리할 수 있게 하는 기능이다. 제네릭은 코드를 보다 유연하고 재사용 가능하게 만들어 주며, 동시에 타입 안정성을 유지하게 해준다.

 

✅제네릭의 기본 문법

제네릭은 일반적으로 <T>와 같은 형태로 사용된다. 여기서 T는 임의의 타입 이름을 나타내며, T 대신 다른 알파벳이나 이름을 사용할 수도 있습니다. 제네릭은 함수나 클래스의 사용 시점에 타입을 지정할 수 있게 해준다.

✅제네릭 함수

제네릭 함수를 사용하면 다양한 타입을 인자로 받아들일 수 있습니다. 예를 들어, 어떤 배열의 첫 번째 요소를 반환하는 함수를 제네릭으로 작성할 수 있다.

✅코드예시

function getFirstElement<T>(array: T[]): T { return array[0]; }
  • 이 함수는 배열의 타입을 T로 지정하고, 함수 호출 시 전달되는 배열의 타입에 따라 T가 결정됩니다.
  • getFirstElement([1, 2, 3])처럼 호출하면 T는 number로 추론되고, getFirstElement(["a", "b", "c"])처럼 호출하면 T는 string으로 추론된다.

✅제네릭 클래스

제네릭 클래스는 클래스 내부에서 사용할 타입을 인스턴스 생성 시 결정할 수 있게 합니다. 예를 들어, 데이터를 저장하는 클래스 DataStorage를 제네릭으로 정의할 수 있다.

 

class DataStorage<T> {
    private data: T[] = [];

    addItem(item: T): void {
        this.data.push(item);
    }

    removeItem(item: T): void {
        this.data = this.data.filter(i => i !== item);
    }

    getItems(): T[] {
        return [...this.data];
    }
}

const stringStorage = new DataStorage<string>();
stringStorage.addItem("hello");
stringStorage.addItem("world");

const numberStorage = new DataStorage<number>();
numberStorage.addItem(1);
numberStorage.addItem(2);
 
  • 여기서 DataStorage<string>는 T가 string으로, DataStorage<number>는 T가 number로 지정된다.
  • 클래스 인스턴스를 만들 때 T의 타입이 결정되며, 각 인스턴스는 서로 다른 타입의 데이터를 안전하게 처리할 수 있다.

✅제네릭 인터페이스

인터페이스도 제네릭을 사용할 수 있다. 예를 들어, 특정 속성을 가진 객체를 나타내는 인터페이스 KeyValuePair를 정의할 수 있다.

 
interface KeyValuePair<K, V> {
    key: K;
    value: V;
}

const kv1: KeyValuePair<string, number> = { key: "age", value: 30 };
const kv2: KeyValuePair<number, string> = { key: 1, value: "one" };
  • 이 인터페이스는 K와 V라는 제네릭 타입을 가지며, 사용할 때 구체적인 타입을 지정할 수 있다.
  • KeyValuePair<string, number>와 KeyValuePair<number, string>은 서로 다른 타입을 가지며, 서로 다른 상황에 맞게 사용할 수 있다.

✅제네릭 타입 제약 (Constraints)

제네릭 타입을 정의할 때, 특정 타입만 허용하도록 제약을 걸 수도 있다. 예를 들어, 특정 타입에 length 속성이 있는지 검사하려면 제약을 걸어 해당 속성이 있는 타입만 허용할 수 있다.

 
 
function logLength<T extends { length: number }>(item: T): void {
    console.log(item.length);
}

logLength("hello");       // 문자열은 length 속성이 있으므로 사용 가능
logLength([1, 2, 3]);     // 배열도 length 속성이 있으므로 사용 가능
// logLength(10);         // 에러: 숫자는 length 속성이 없음
  • 여기서 <T extends { length: number }>는 T가 length라는 속성을 가진 타입이어야 한다는 제약을 겁니다.
  • 이를 통해 제네릭의 유연성을 유지하면서도 안전한 타입 사용을 보장할 수 있다.

✅제네릭의 장점

  1. 타입 안전성: 여러 타입을 처리하더라도 코드가 타입 안전성을 잃지 않도록 보장합니다.
  2. 재사용성: 동일한 로직을 여러 타입에 대해 반복 작성할 필요가 없어서 코드가 간결해지고 재사용성이 높아집니다.
  3. 가독성: 코드 작성 시 제네릭 타입을 통해 의도를 명확하게 전달할 수 있어 가독성이 향상됩니다.
 
 
 
 

 

 
 
 
 

 

 

 

'4주차' 카테고리의 다른 글

React Router에서 Outlet과 Layout을 활용하는 방법  (0) 2024.11.12
Protected Route로 라우트 보호하기  (0) 2024.11.12
TypeScript의 .d.ts 파일 🧐  (0) 2024.11.12
타입스크립트의 타입  (0) 2024.11.12
Next.js 톺아보기 !  (0) 2024.11.08