본문 바로가기
웹/TypeScript

[TypeScript] 타입스크립트 공부하기-1

by 보먀 2024. 11. 12.
728x90
반응형

1. 타입스크립트란?

  • C# 의 창시자인 덴마크 출신의 개발자 앤더스 하일스버그(Anders Hejlsberg)가 만들었다. (그래서 C# 과 비슷하다고 함)
  • 오픈소스로 공개되어 있어 많은 프로그래머들이 언어의 개발과 유지보수에 참여하고 있기 때문에 발전 가능성이 창창한 언어
  • 자바스크립트는 유연하지만 그로인해 복잡한 대규모 프로그램을 만들 때 한계점이 존재했음 -> 그래서 타입스크립트가 탄생
  • 타입스크립트는 자바스크립트에 타입 관련 기능들을 추가한 확장판 -> 타입이 까다롭지 않은 자바스크립트와 다르게 타입을 까다롭게 받기 때문에 더 안전하게 사용가능!

 

1.1. 타입 시스템?

 

- 정적 타입 시스템

  • 코드 실행 이전 모든 변수의 타입을 고정적으로 결정 (변수 타입을 미리 결정)
  • 엄격하고 고정적
  • C, Java
String a = "hello";
int a = 123;

int c = a * b; // 에디터에서 실행전에 오류 알려줌

 

- 동적 타입 시스템

  • 코드를 실행하고 나서 유동적으로 변수의 타입을 결정 (변수 타입을 미리 결정X)
  • 자유롭고 유연
  • Python, JavaScript
let a = "hello";
a = 123;

 

 

이렇게만 보면 동적 타입 시스템이 편해보이고 좋은데 뭐가 문제일까?

 

오류가 발생할 코드를 미리 알 수 없고, 실행 후에야 알 수 있기 때문에 서비스를 배포하고 시간이 수 일 후에 오류가 발생하는 일이 상황이 발생할 수 있음 -> 오류 발생으로 프로그램 강제 종료, 서비스가 마비되는 상황 도래 (치명적)

 

 

- 점진적 타입 시스템

 

그렇다면 타입스크립트는 정적 타입 시스템인가? 

 

아니다. 자바스크립트는 동적 + 정적이 합쳐진 것 같은 "점진적 타입 시스템(Gradual Type System)" 이라는 독특한 타입 시스템을 사용한다. 정적 타입 시스템처럼 타입이 정의되어 있는 변수들에 대해서는 타입을 미리 결정해서 오류가 있다면 알려주고, 타입이 결정되지 않은 변수가 있다면 타입을 자동으로 추론해준다. 

 

 

1.2. 타입스크립트의 동작 원리

 

대다수의 프로그래밍 언어들은 아래와 같이 동작한다. 

한 입 크기로 잘라먹는 타입스크립트

 

반면 타입스크립트는 컴파일하면 바이트 코드가 만들어지는 것이 아니라 자바스크립트 코드가 만들어진다. 

 

타입 오류 없이 정상적으로 타입 검사를 통과한 코드는 타입 문제가 없는 안전한 자바스크립트 코드로 변환되고, 

변환된 자바스크립트 코드가 다시 AST 를 거져 바이트 코드로 변환된다. 

 

결국 타입스크립트는 자바스크립트를 보다 안전하게 사용하기 위해 미리 한 번 코드를 검사하는 용도로 사용되는 것!

한 입 크기로 잘라먹는 타입스크립트

 

 

1.3. 타입스크립트 컴파일하기

 

타입스크립트 코드를 작성하고 컴파일하려면 tsc 명령어를 사용하면 된다.

tsc 파일명.ts

 

컴파일이 완료되면 파일명.js 파일이 생성된다. 컴파일 결과로 생성된 자바스크립트 코드는 node 를 이용해서 실행하면 된다.

node 파일명.js

 

 

근데 tsc -> node 로 두 번의 명령어를 거쳐 파일을 실행시키기 귀찮다면, tsx(TypeScript Execute) 를 사용하면 된다.

 

사용을 위한 설치

sudo npm i -g tsx // mac

npm i -g tsx // window

 

tsx 를 사용하면 자바스크립트 파일을 생성하지 않고 한번에 타입스크립트 파일을 실행시켜준다. 

tsx 파일명.ts

 

 

1.4. 컴파일러 옵션

 

컴파일 옵션을 통해 컴파일 과정에서의 세부적인 사항들을 정할 수 있다. 

 

 

- 컴파일러 옵션 자동 생성

 

이렇게 사용하면 기본 옵션이 설정된 컴파일러 옵션 파일을 자동으로 생성할 수 있다.

tsc --init

 

 

- include 옵션

 

tsc 에게 컴파일 할 타입스크립트 파일의 범위와 위치를 알려주는 옵션

 

이렇게 설정해두면 tsc 명령어만 입력해도 src 폴더 아래의 모든 타입스크립트 파일을 동시에 컴파일 시킬 수 있다. 

{
  "include": ["src"]
}

 

만약 설정해두지 않았다면 이렇게 일일히 쳐야함.

// 100개의 파일이 존재할 경우
tsc file1.ts
tsc file2.ts
...
tsc file100.ts

 

 

- target 옵션

 

컴파일 결과 생성되는 자바스크립트 코드의 버전을 설정할 수 있다.

{
  "compilerOptions": {
    "target": "ES5"
  },
  "include": ["src"]
}

 

 

- module 옵션

 

변환되는 자바스크립트 코드의 모듈 시스템을 설정할 수 있다. 

{
  "compilerOptions": {
    "target": "ESNext",
		"module": "CommonJS"
  },
  "include": ["src"]
}

 

 

- outDir 옵션

 

컴파일 결과 생성할 자바스크립트 코드의 위치를 결정할 수 있다. 아래와 같이 설정하면 컴파일 결과가 dist 폴더에 생성된다.

{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "outDir": "dist"
  },
  "include": ["src"]
}

 

 

- strict 옵션

 

타입스크립 컴파일러의 타입 검사 엄격함 수준을 정할 수 있다.

{
  "compilerOptions": {
    ...
    "strict": true
  },
  "include": ["src"]
}

 

 

- strictNullChecks 옵션

 

null 값을 임시값으로 활용하고 싶은데, 타입스크립트는 타입을 엄격하게 검사하기 때문에 null 타입 변수가 아닌 이상 null 값을 임시값으로 사용하지 못한다. 이때 strictNullChecks 옵션을 false 로 설정하면 타른 타입의 변수에서도 null 값을 임시값으로 활용할 수 있다.

{
  "compilerOptions": {
    ...
    "strictNullChecks": false,
		...
  },
  "ts-node": {
    "esm": true
  },
  "include": ["src"]
}

 

 

- ModuleDetection 옵션

 

타입스크립트의 모든 파일은 기본적으로 전역 파일(모듈)로 취급되기 때문에 다른 파일에 동일한 이름의 변수를 선언하면 오류가 발생한다.

이때 각 파일에 모듈 시스템 키워드를 최소 하나 이상 사용해 해당 파일을 전역 모듈이 아닌 로컬 모듈로 취급되도록 만들어야하는데, 이를 자동화하는 옵션이 ModuleDetection 이다. 

 

moduleDectection 을 force 로 설정하면 모든 타입스크립트 파일이 로컬 모듈(독립 모듈)로 취급된다.

{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "outDir": "dist",
		"moduleDetection": "force"
  },
  "include": ["src"]
}

 

 

- ts-node 옵션

 

moduleDectection 옵션을 활성화하고 타입스크립트 파일에서 모듈 시스템을 사용하게 되면 ts-node 로 실행시 오류가 발생하는데, ts-node 옵션을 설정하면 ts-node 로 타입스크립트 모듈을 실행할 수 있다.

{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "outDir": "dist",
		"moduleDetection": "force"
  },
	"ts-node": {
		"esm": true
	},
  "include": ["src"]
}

 

 

 

2. 타입스크립트의 타입 살펴보기

 

2.1. 기본 타입 (Basic Type)

 

타입스크립트가 자체적으로 제공하는 타입들 (기본 타입 == 내장 타입)

한 입 크기로 잘라먹는 타입스크립트

 

2.1. 원시 타입

  • number
  • string
  • boolean
  • null -> null 만 포함되는 타입
  • undefined -> undefined 만 포함되는 타입
  • 리터럴 타입

 

- 리터럴 타입

 

리터럴 타입은 string, number 처럼 많은 값을 포함하는 타입이 아니라 딱 하나의 값만 포함하는, 값 자체로 만들어진 타입이다. 

let strA: "hello" = "hello";
let boolA: true = true;
let boolB: false = false;

 

 

2.2. 배열과 튜플

 

- 배열

 

배열 타입은 타입 뒤에 중괄호를 붙여서 정의하거나

let numArr: number[] = [1, 2, 3]

 

Array<배열요소타입> 형태(제네릭)로 정의할 수 있다. 

let boolArr: Array<boolean> = [true, false, true];

 

만약 한 배열 안에 다양한 타입의 요소들을 함께 넣고 싶다면 유니온 연산자( | )를 사용해서 타입을 정의해주면 된다.

let multiArr: (number | string)[] = [1, "hello"];

 

다차원 배열은 차원 수 만큼 중괄호를 붙여주면 된다.

let doubleArr : number[][] = [
  [1, 2, 3], 
  [4, 5],
];

 

 

- 튜플 

 

튜플은 자바스크립트에는 없고 타입스크립트에만 있는 특수한 타입으로 길이와 타입이 고정된 배열이라고 생각하면 된다.

타입과 길이가 고정되어 있기 때문에 순서나 타입을 잘못 배치하는 실수를 막을 수 있다. 

let tup1: [number, number] = [1, 2];
let tup2: [number, string, boolean] = [1, "hello", true];

 

하지만, 튜플은 결국 길이와 타입이 고정된 배열이므로 컴파일해보면 자바스크립트 배열로 변환되는 것을 확인할 수 있다.

그래서 배열 메서드인 push, pop 을 이용해 고정된 길이를 무시하고 요소를 추가/삭제할 수 있다. (하지만 주의할 것)

 

 

2.3. 객체

 

객체 타입을 정의하는 방법 2가지

 

- object 타입으로 정의

let user: object = {
  id: 1,
  name: "이정환",
};

user.id; // 값을 읽을 수 없음

 

object 타입은 단순 값이 객체임을 표현하는 것 외에 아무런 정보도 제공하지 않는다. object 타입은 객체의 프로퍼티에 대한 정보를 전혀 가지고 있지 않기 때문에 이렇게 프로퍼티에 접근하려고 하면 오류가 발생한다. 

 

 

- 리터럴 타입으로 정의

let user: {
  id: number;
  name: string;
} = {
  id: 1,
  name: "이정환",
};

user.id;

 

객체의 구조를 정의하듯이 객체의 타입을 정의해준다. 이런 타입을 구조적 타입 시스템이라고 부르며, 이 방법을 사용하면 오류 없이 객체의 프로퍼티에 접근할 수 있다. 

 

 

객체를 타입을 정의할 때 특정 프로퍼티는 있어도 되고 없어도 되는 상황이 존재하는데, 이럴 때는 옵셔널 연산자( ? )를 사용하면 된다.

let user: {
  id?: number; // 옵셔널 프로퍼티
  name: string;
} = {
  id: 1,
  name: "이정환",
};

user = {
  name: "홍길동",
};

 

 

또 수정이 불가한 프로퍼티를 만들고 싶다면 프로퍼티 이름 앞에 readonly 키워드를 써서 수정을 막을 수 있다.

let user: {
  id?: number;
  readonly name: string; // Readonly 프로퍼티가 됨
} = {
  id: 1,
  name: "이정환",
};

user.name = "dskfd"; // 오류 발생 -> 수정 불가

 

 

2.4. 타입 별칭 (Type Alias)

 

자주 사용하는 타입의 경우 타입 별칭으로 만들어서 사용하면 편리하다.

type User = {
  id: number;
  name: string;
  nickname: string;
  birth: string;
  bio: string;
  location: string;
};

let user: User = {
  id: 1,
  name: "이정환",
  nickname: "winterlood",
  birth: "1997.01.07",
  bio: "안녕하세요",
  location: "부천시",
};

let user2: User = {
  id: 2,
  name: "홍길동",
  nickname: "winterlood",
  birth: "1997.01.07",
  bio: "안녕하세요",
  location: "부천시",
};

 

 

2.5. 인덱스 시그니처 (Index Signature)

 

객체 타입을 유연하게 정의할 수 있게 하는 특수 문법이다. 

type CountryCodes = {
  [key: string]: string;
};

let countryCodes: CountryCodes = {
  Korea: "ko",
  UnitedState: "us",
  UnitedKingdom: "uk",
  // (... 약 100개의 국가)
  Brazil : 'bz'
};

 

 

주의할 점은 인덱스 시그니쳐를 사용하면서 동시에 추가적인 프로퍼티를 정의할 때에는 인덱스 시그니쳐의 value 타입과 직접 추가한 프로퍼티의 value 타입이 호환되거나 일치해야 한다는 것이다. 

 

아래의 코드는 Key 값의 타입은 문제 없지만, value 의 타입이 달라 오류가 난다.

type CountryNumberCodes = {
  [key: string]: number;
  Korea: string; // 오류!
};

 

 

2.6. 열거형 (Enum)

 

열거형은 자바스크립트에는 없고 타입스크립트에서만 사용할 수 있는 특별한 타입이다. 열거형은 값을 나열하는 용도로 사용된다.

enum Role { // 숫자 열거형
  ADMIN,  // 0
  USER,   // 1
  GUEST,  // 2
}

enum Language { // 문자 열거형
  korean = "ko",
  english = "en",
}

 

숫자 열거형은 값을 따로 부여하지 않아도 0 번부터 들어가는데, 만약 아래처럼 값을 부여하면 그 밑의 요소들은 자동으로 부여한 값에서 +1씩 더해져서 들어간다. 

enum Role {
  ADMIN = 10, // 10 할당 
  USER,       // 11 할당(자동)
  GUEST,      // 12 할당(자동)
}

 

 

추가로 열거형은 컴파일될 때 다른 타입들처럼 사라지지 않고 자바스크립트 객체로 변환된다. 

 

 

2.7. any 와 unknown

 

- any

 

any 타입은 타입스크립트에서만 제공되는 특별한 타입으로 타입 검사를 받지 않는 특수 치트키 타입이다.

any 는 타입 검사를 받지 않기 때문에 어떤 타입의 값도 담아서 사용할 수 있는데, 문법에서 자유로운만큼 위험하기 때문에 최대한 사용을 지양해야 한다. 

let anyVar: any = 10;
anyVar = "hello";

 

 

- unknown

 

any 와 비슷하지만 any 보다 안전한 타입이다. unknown 타입 역시 어떤 타입의 값이든 다 저장할 수 있다.

그러나 unknown 타입으로 지정된 변수에는 어떠한 타입의 값도 넣을 수 없고, 연산 참여 불가, 메서드 사용 불가이다. 

let num: number = 10;

let unknownVar: unknown;
unknownVar = "";
unknownVar = 1;
unknownVar = () => {};

num = unknownVar; // 오류 !
let unknownVar: unknown;

unknownVar * 2 // 오류!

 

 

- void 

 

아무 값도 없음을 의미하는 타입이다. 보통 아무런 값도 반환하지 않는 함수의 반환값 타입을 정의할 때 사용한다.

function func2(): void {
  console.log("hello");
}

 

놀랍게도 void 타입으로 정의된 변수에는 undefined 타입은 담을 수 있는데, void 타입이 undefined 타입을 포함하는 타입이기 때문이다.

let a: void;
a = undefined;

 

 

- never

 

never 타입은 불가능을 의미하는 타입이다. 어떠한 값도 반환할 수 없는 상황일 때 해당 함수의 반환값 타입을 정의할 때 사용한다.

아래의 함수는 무한 루프를 돌기 때문에 아무 값도 반환할 수 없는데, 이렇게 불가능한 타입을 정의할 때 never 를 사용한다. 

function func3(): never {
  while (true) {}
}

 

또 의도적으로 오류를 발생시키는 함수에도 never 타입을 사용할 수 있다.

function func4(): never {
  throw new Error();
}

 

만약 함수가 아닌 변수에 never 타입을 사용한다면, any 를 포함해서 그 어떠한 타입의 값도 이 변수에 담을 수 없다.

 

728x90
반응형