타입스크립트 시작하기
- Published on
- 타입스크립트는 자바스크립트의 강력한 타입 시스템을 제공하여 코드의 안정성을 높이고 구조적 타이핑과 제네릭을 통해 복잡한 타입의 조합과 정확한 타입 추론을 가능하게 합니다.
Table of Contents
새 프로그래머를 위한 타입스크립트
타입스크립트를 첫 언어로 선택한 당신, 이미 좋은 결정을 하고 있습니다!
타입스크립트가 자바스크립트의 "변형" 또는 "버전"이라는 말을 이미 들어보셨을 겁니다. 타입스크립트(TS)와 자바스크립트(JS) 사이의 관계는 현대 프로그래밍 언어 중에서도 독특하므로 이 관계에 대해 더 알아보는 것이 타입스크립트가 자바스크립트에 어떻게 추가되는지 이해하는 데 도움이 됩니다.
자바스크립트란? 간단한 역사
자바스크립트(또한 ECMAScript로 알려져 있음)는 처음에는 브라우저를 위한 간단한 스크립팅 언어로 시작했습니다. 발명 당시에는 웹 페이지에 포함된 짧은 코드 일부에 사용될 것으로 예상되었으며 몇 십 줄 이상의 코드를 작성하는 것은 다소 이례적인 일이었습니다. 이로 인해 초기 웹 브라우저는 이런 코드를 상당히 느리게 실행했습니다. 그러나 시간이 지나면서 JS는 점점 더 인기를 얻게 되었고 웹 개발자는 이를 사용하여 인터랙티브한 경험을 만들기 시작했습니다.
웹 브라우저 개발자는 JS 사용 증가에 대응하여 실행 엔진을 최적화하고(JS의 동적 컴파일) JS로 할 수 있는 일을 확장했습니다(API 추가), 이는 다시 웹 개발자가 JS를 더 많이 사용하도록 만들었습니다. 현대 웹사이트에서는 브라우저가 수십만 줄의 코드를 실행하는 애플리케이션을 자주 실행하고 있습니다. 이것은 "웹"의 길고 점진적인 성장을 나타내며 단순한 정적 페이지의 네트워크에서 시작하여 모든 종류의 풍부한 애플리케이션을 위한 플랫폼으로 진화했습니다.
이와 더불어 JS는 브라우저의 상황을 벗어나 사용되기에 충분히 인기를 얻었습니다. 예를 들어, Node.js를 사용하여 JS 서버를 구현하는 것처럼요. JS의 "어디서나 실행" 성격은 크로스 플랫폼 개발을 위한 매력적인 선택을 만듭니다. 요즘에는 자바스크립트만을 사용하여 전체 스택을 프로그래밍하는 개발자가 많습니다!
요약하자면, 단순한 용도로 설계된 언어가 백만 줄의 애플리케이션을 작성하는 전문 도구로 성장한 것을 가지고 있습니다. 모든 언어는 자신만의 특이점 — 이상함과 놀라움 — 을 가지고 있으며 자바스크립트의 겸손한 시작은 이로 인해 많은 이상함을 가지고 있습니다. 몇 가지 예를 들어보자면:
자바스크립트의 동등 연산자(==)는 피연산자를 강제 변환하여 예상치 못한 동작을 초래합니다.
if ("" == 0) {
// 그렇습니다! 하지만 왜죠??
}
if (1 < x < 3) {
// x의 *어떤* 값에 대해서도 참입니다!
}
자바스크립트는 존재하지 않는 속성에 접근을 허용합니다.
const obj = { width: 10, height: 15 };
// 왜 이게 NaN일까요? 철자가 어렵네요!
const area = obj.width * obj.heigth;
대부분의 프로그래밍 언어는 이런 종류의 오류가 발생하면 오류를 던질 것이며 일부는 코드가 실행되기 전인 컴파일 중에 오류를 던질 것입니다. 작은 프로그램을 작성할 때 이런 특이점은 성가시지만 관리할 수 있습니다. 수백 또는 수천 줄의 코드로 애플리케이션을 작성할 때 이런 지속적인 놀라움은 심각한 문제입니다.
타입
타입스크립트는 자바스크립트의 타입이 추가된 확장 버전입니다. 이는 다양한 값들을 어떻게 사용할 수 있는지에 대한 규칙을 추가함을 의미합니다. 앞서 언급된 obj.heigth
의 오류는 문법 오류가 아닌 잘못된 방식으로 특정 값(타입)을 사용한 오류입니다.
예를 들어, 브라우저에서 실행할 수 있는 자바스크립트 코드가 있고 이 코드는 값을 기록합니다.
console.log(4 / []);
이 문법적으로 유효한 프로그램은 무한대를 기록합니다. 하지만 타입스크립트는 숫자를 배열로 나누는 것을 말이 안 되는 연산으로 간주하고 오류를 발생시킵니다.
console.log(4 / []);
// 오류: 산술 연산의 오른쪽은 'any', 'number', 'bigint' 또는 열거형 타입이어야 합니다.
대부분의 경우 숫자를 배열로 나누려는 것은 프로그래밍 실수입니다. 타입스크립트의 타입 검사기는 올바른 프로그램을 허용하면서 가능한 한 많은 일반적인 오류를 잡도록 설계되었습니다. 코드에서 자바스크립트 파일을 타입스크립트 파일로 옮길 때 코드가 어떻게 작성되었는지에 따라 타입 오류를 볼 수 있습니다. 이는 코드에 실제 문제가 있거나 타입스크립트가 지나치게 보수적인 경우일 수 있습니다.
런타임 동작
타입스크립트는 자바스크립트의 런타임 동작을 보존하는 프로그래밍 언어입니다. 예를 들어, 자바스크립트에서 0으로 나누면 무한대가 생성되며 런타임 예외가 발생하지 않습니다. 타입스크립트는 자바스크립트 코드의 런타임 동작을 절대 변경하지 않습니다.
이는 자바스크립트에서 타입스크립트로 코드를 옮긴 경우 타입스크립트가 코드에 타입 오류가 있다고 판단하더라도 동일한 방식으로 실행된다는 것을 보장합니다.
지워진 타입
대략적으로 말해서 타입스크립트 컴파일러가 코드 검사를 마친 후 타입을 지워 "컴파일된" 코드를 생성합니다. 이는 코드가 컴파일된 후 결과적으로 생성된 순수 자바스크립트 코드에는 타입 정보가 없다는 것을 의미합니다.
자바스크립트와 타입스크립트 학습
"자바스크립트를 배워야 하나요, 아니면 타입스크립트를 배워야 하나요?"라는 질문을 자주 봅니다.
답은 자바스크립트를 배우지 않고는 타입스크립트를 배울 수 없다는 것입니다! 타입스크립트는 자바스크립트와 문법 및 런타임 동작을 공유하므로 자바스크립트에 대해 배우는 모든 것이 동시에 타입스크립트 학습에도 도움이 됩니다.
프로그래머가 자바스크립트를 배울 수 있는 자료는 매우 많습니다. 타입스크립트를 작성하는 경우 이 자료들을 무시해서는 안 됩니다. 예를 들어, StackOverflow에는 타입스크립트보다 자바스크립트로 태그된 질문이 약 20배 더 많지만, 모든 자바스크립트 질문이 타입스크립트에도 적용됩니다.
자바스크립트 프로그래머를 위한 타입스크립트
타입스크립트는 자바스크립트와 독특한 관계에 있습니다. 타입스크립트는 자바스크립트의 모든 기능을 제공할 뿐만 아니라 이 위에 추가적인 계층을 제공합니다. 타입스크립트의 타입 시스템입니다.
예를 들어, 자바스크립트는 문자열과 숫자 같은 언어 원시 타입을 제공하지만, 이들이 일관되게 할당되었는지를 검사하지는 않습니다. 타입스크립트는 이를 수행합니다.
이는 기존의 작동하는 자바스크립트 코드도 타입스크립트 코드라는 것을 의미합니다. 타입스크립트의 주요 이점은 코드에서 예상치 못한 동작을 강조하여 버그의 가능성을 낮추는 것입니다.
이 튜토리얼은 타입스크립트, 특히 그 타입 시스템에 대한 간략한 개요를 제공합니다.
타입 추론
타입스크립트는 자바스크립트 언어를 알고 있으며 많은 경우에 대해 타입을 자동으로 생성합니다. 예를 들어, 변수를 생성하고 특정 값에 할당하는 경우 타입스크립트는 그 값을 타입으로 사용합니다.
let helloWorld = "Hello World";
let helloWorld: string
자바스크립트의 작동 방식을 이해함으로써 타입스크립트는 자바스크립트 코드를 수용하지만 타입을 가진 타입 시스템을 구축할 수 있습니다. 이는 코드에 타입을 명시적으로 추가하는 추가 문자 없이도 타입 시스템을 제공합니다. 위 예제에서 타입스크립트가 helloWorld
가 문자열임을 알 수 있는 방식입니다.
Visual Studio Code에서 자바스크립트를 작성하며 편집기 자동 완성 기능을 사용해본 적이 있을 겁니다. Visual Studio Code는 자바스크립트 작업을 더 쉽게 하기 위해 내부적으로 타입스크립트를 사용합니다.
타입 정의
자바스크립트에서는 다양한 디자인 패턴을 사용할 수 있습니다. 그러나 일부 디자인 패턴(예를 들어, 동적 프로그래밍을 사용하는 패턴)은 타입이 자동으로 추론되기 어렵게 만듭니다. 이러한 경우를 다루기 위해 타입스크립트는 자바스크립트 언어의 확장을 지원하여 타입스크립트에 타입이 무엇이어야 하는지 알려줄 수 있는 위치를 제공합니다.
예를 들어, name: string
과 id: number
를 포함하는 타입이 추론된 객체를 생성하려면 다음과 같이 작성할 수 있습니다.
const user = {
name: "Hayes",
id: 0,
};
이 객체의 형태를 인터페이스 선언을 사용해 명시적으로 기술할 수 있습니다.
interface User {
name: string;
id: number;
}
변수 선언 뒤에 : TypeName
과 같은 문법을 사용하여 자바스크립트 객체가 새 인터페이스의 형태를 따른다고 선언할 수 있습니다.
const user: User = {
name: "Hayes",
id: 0,
};
제공된 객체가 제공한 인터페이스와 일치하지 않으면 타입스크립트가 경고합니다.
interface User {
name: string;
id: number;
}
const user: User = {
username: "Hayes",
id: 0,
};
{ username: string; id: number; }
타입은 User
타입에 할당될 수 없습니다. 객체 리터럴은 알려진 속성만을 명시할 수 있으며 username
은 User
타입에 존재하지 않습니다.
자바스크립트가 클래스와 객체 지향 프로그래밍을 지원하므로 타입스크립트도 마찬가지입니다. 클래스와 함께 인터페이스 선언을 사용할 수 있습니다.
interface User {
name: string;
id: number;
}
class UserAccount {
name: string;
id: number;
constructor(name: string, id: number) {
this.name = name;
this.id = id;
}
}
const user: User = new UserAccount("Murphy", 1);
함수의 매개변수와 반환 값에 인터페이스를 사용해 주석을 달 수 있습니다.
function deleteUser(user: User) {
// ...
}
function getAdminUser(): User {
//...
}
자바스크립트에서는 이미 소수의 원시 타입들이 사용 가능합니다. boolean
, bigint
, null
, number
, string
, symbol
, undefined
이들은 인터페이스에서 사용할 수 있습니다. 타입스크립트는 이 목록에 몇 가지를 더 추가합니다. 예를 들어, any
(아무것도 허용함), unknown
(이 타입을 사용하는 사람이 타입을 선언하도록 요구함), never
(이 타입이 발생할 가능성이 없음), 그리고 void
(함수가 undefined를 반환하거나 반환 값이 없음)가 있습니다.
타입을 구축하기 위한 두 가지 문법이 있음을 알 수 있습니다. 인터페이스와 타입. 인터페이스를 선호해야 합니다. 특정 기능이 필요할 때 타입을 사용하세요.
타입 구성
타입스크립트를 사용하여 간단한 타입들을 결합함으로써 복잡한 타입을 만들 수 있습니다. 이를 위한 두 가지 인기 있는 방법이 있습니다. 유니온과 제네릭입니다.
유니온
유니온을 사용하면 타입이 여러 가지 타입 중 하나가 될 수 있음을 선언할 수 있습니다. 예를 들어, boolean
타입이 true
또는 false
일 수 있음을 설명할 수 있습니다.
type MyBool = true | false;
참고: 위의
MyBool
은boolean
으로 분류된 것을 볼 수 있습니다. 이것은 구조적 타입 시스템의 속성입니다. 이에 대해서는 아래에서 더 자세히 설명합니다.
유니온 타입의 인기 있는 사용 사례는 값이 될 수 있는 문자열이나 숫자 리터럴 집합을 설명하는 것입니다.
type WindowStates = "open" | "closed" | "minimized";
type LockStates = "locked" | "unlocked";
type PositiveOddNumbersUnderTen = 1 | 3 | 5 | 7 | 9;
유니온은 다른 타입들을 다루는 방법도 제공합니다. 예를 들어, 배열이나 문자열을 받는 함수가 있을 수 있습니다.
function getLength(obj: string | string[]) {
return obj.length;
}
변수의 타입을 알아보려면 typeof
를 사용하세요.
Type Predicate
string typeof s === "string"
number typeof n === "number"
boolean typeof b === "boolean"
undefined typeof undefined === "undefined"
function typeof f === "function"
array Array.isArray(a)
예를 들어, 문자열이나 배열이 전달되는 경우에 따라 함수가 다른 값을 반환하도록 만들 수 있습니다.
function wrapInArray(obj: string | string[]) {
if (typeof obj === "string") {
return [obj];
}
return obj;
}
제네릭
제네릭은 타입에 변수를 제공합니다. 배열은 흔한 예입니다. 제네릭이 없는 배열은 어떤 것이든 포함할 수 있습니다. 제네릭이 있는 배열은 배열이 포함하는 값의 유형을 설명할 수 있습니다.
type StringArray = Array<string>;
type NumberArray = Array<number>;
type ObjectWithNameArray = Array<{ name: string }>;
제네릭을 사용하는 자신만의 타입을 선언할 수 있습니다.
interface Backpack<Type> {
add: (obj: Type) => void;
get: () => Type;
}
declare const backpack: Backpack<string>;
const object = backpack.get();
backpack.add(23);
number
형식의 인수는 string
형식의 매개변수에 할당될 수 없습니다.
구조적 타입 시스템
타입스크립트의 핵심 원칙 중 하나는 타입 검사가 가지고 있는 값의 형태에 중점을 둔다는 것입니다. 이는 때때로 "덕 타이핑" 또는 "구조적 타이핑"이라고 불립니다.
구조적 타입 시스템에서는 두 객체가 같은 형태를 가지고 있으면 같은 타입으로 간주됩니다.
interface Point {
x: number;
y: number;
}
function logPoint(p: Point) {
console.log(`${p.x}, ${p.y}`);
}
const point = { x: 12, y: 26 };
logPoint(point);
여기서는 두 객체가 같은 형태를 가지므로 같은 타입으로 간주됩니다.
point
변수는 Point
타입으로 선언된 적이 없습니다. 그러나 타입스크립트는 타입 검사에서 point
의 형태와 Point
의 형태를 비교합니다. 둘은 같은 형태를 가지므로 코드는 통과합니다.
형태 일치는 객체의 필드 일부만 일치하면 됩니다.
const point3 = { x: 12, y: 26, z: 89 };
logPoint(point3); // "12, 26"을 로그에 기록합니다.
const rect = { x: 33, y: 3, width: 30, height: 80 };
logPoint(rect); // "33, 3"을 로그에 기록합니다.
const color = { hex: "#187ABF" };
logPoint(color);
{ hex: string; }
유형의 인수는 'Point' 유형의 매개변수에 할당될 수 없습니다. { hex: string; }
유형은 'Point' 유형에서 x, y 속성이 누락되었습니다.
클래스와 객체가 형태에 부합하는 방식에 차이는 없습니다.
class VirtualPoint {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
}
const newVPoint = new VirtualPoint(13, 56);
logPoint(newVPoint); // "13, 56"을 로그에 기록합니다.
객체나 클래스가 필요한 모든 속성을 가지고 있다면 타입스크립트는 구현 세부 사항에 관계없이 일치한다고 판단합니다.