TypeScript
📋 문서 버전
이 문서는 2개의 버전이 있습니다. 현재 최신 버전을 보고 있습니다.
TypeScript
1. 개요
TypeScript는 마이크로소프트(Microsoft)에서 개발한 오픈 소스 프로그래밍 언어로, 자바스크립트(JavaScript)에 정적 타이핑(Static Typing) 기능을 추가한 정적 타입(Statically Typed)의 상위 집합(Superset) 언어입니다.
TypeScript의 핵심 철학은 자바스크립트의 유연함을 유지하면서도, 대규모 애플리케이션 개발 시 발생할 수 있는 런타임 오류를 컴파일 단계에서 사전에 방지하여 코드의 안정성과 유지보수성을 높이는 데 있습니다. 모든 TypeScript 코드는 최종적으로 자바스크립트로 변환되어 실행되므로, 기존 자바스크립트 생태계의 모든 라이브러리와 프레임워크를 그대로 사용할 수 있습니다.
2. 주요 특징 및 작동 원리
2.1 정적 타이핑 (Static Typing)
자바스크립트는 변수의 타입을 실행 시점에 결정하는 동적 타입 언어입니다. 반면, TypeScript는 코드를 작성하는 단계에서 변수, 함수 매개변수, 반환 값의 타입을 명시하는 정적 타이핑을 지원합니다. 이를 통해 개발자는 IDE(통합 개발 환경)의 자동 완성 기능을 최대한 활용할 수 있으며, 타입 불일치로 인한 버그를 즉각적으로 발견할 수 있습니다.
2.2 트랜스파일 (Transpile)
브라우저나 Node.js 런타임은 TypeScript 코드를 직접 해석할 수 없습니다. 따라서 TypeScript 컴파일러(tsc)를 통해 TypeScript 코드를 표준 자바스크립트 코드로 변환하는 과정이 필요한데, 이를 트랜스파일(Transpile)이라고 합니다.
2.3 JavaScript vs TypeScript 비교
| 구분 | JavaScript | TypeScript |
|---|---|---|
| 타입 결정 시점 | 런타임 (Dynamic) | 컴파일 타임 (Static) |
| 오류 발견 | 실행 중 오류 발생 시 인지 | 코드 작성 및 컴파일 단계에서 인지 |
| 학습 곡선 | 낮음 (빠른 시작 가능) | 중간 (타입 시스템 학습 필요) |
| 도구 지원 | 기본적 자동 완성 | 강력한 타입 기반 자동 완성 및 리팩토링 |
| 실행 환경 | 브라우저, Node.js 직접 실행 | JS로 변환 후 실행 가능 |
3. 설치 및 시작하기
3.1 설치
TypeScript 컴파일러를 사용하기 위해서는 Node.js가 설치되어 있어야 하며, npm(Node Package Manager)을 통해 전역 또는 프로젝트 단위로 설치합니다.
# 전역 설치
npm install -g typescript
# 프로젝트 단위 설치 (권장)
npm install --save-dev typescript
3.2 첫 번째 파일 실행
.ts확장자로 파일을 생성합니다 (예:index.ts).- 컴파일러를 통해
.js파일로 변환합니다.tsc index.ts - 생성된
.js파일을 실행합니다.node index.js
4. 타입 시스템
4.1 기본 타입
TypeScript는 자바스크립트의 기본 타입 외에 추가적인 타입 시스템을 제공합니다.
string: 문자열number: 숫자 (정수, 실수 구분 없음)boolean: 불리언 (true, false)array: 배열 (예:number[]또는Array<number>)tuple: 고정된 길이와 타입을 가진 배열 (예:[string, number])any: 모든 타입 허용 (타입 검사를 무효화하므로 사용을 지양해야 함)unknown:any의 안전한 대안. 타입을 확정(타입 가드 등)하기 전까지는 메서드 호출이나 할당이 제한되어 런타임 에러를 방지합니다.never: 절대 발생할 수 없는 값의 타입 (예: 항상 예외를 던지는 함수, 무한 루프 함수)void: 반환 값이 없는 함수의 반환 타입
4.2 타입 추론 (Type Inference)
TypeScript는 개발자가 모든 곳에 타입을 명시하지 않아도, 할당된 값이나 함수의 반환 값을 통해 타입을 자동으로 추론하는 기능을 제공합니다.
let x = 3; // TypeScript가 x를 number 타입으로 자동 추론
// x = "hello"; // 오류 발생: 'string' 형식은 'number' 형식에 할당할 수 없음
4.3 인터페이스(Interface)와 타입 별칭(Type Alias)
객체의 구조를 정의할 때 사용합니다.
- Interface: 주로 객체의 형태를 정의하며, 선언 병합(Declaration Merging)이 가능하여 확장성이 좋습니다.
- Type Alias: 모든 타입에 이름을 붙일 수 있으며, Union 타입 등 복잡한 타입 정의에 유리합니다.
// 인터페이스 예시
interface User {
id: number;
name: string;
email?: string; // 선택적 프로퍼티 (Optional)
}
// 타입 별칭 예시
type Point = {
x: number;
y: number;
};
const user: User = { id: 1, name: "홍길동" };
const coord: Point = { x: 10, y: 20 };
4.4 제네릭 (Generics)
제네릭은 타입을 마치 함수의 매개변수처럼 사용하여, 다양한 타입에 대해 재사용 가능한 컴포넌트를 만들 때 사용합니다.
function wrapInArray<T>(value: T): T[] {
return [value];
}
const stringArr = wrapInArray<string>("Hello"); // string[] 반환
const numberArr = wrapInArray<number>(123); // number[] 반환
4.5 고급 타입 기능
Union 및 Intersection 타입
- Union (
|): "A 또는 B" 타입임을 의미합니다. 여러 타입 중 하나일 수 있음을 명시합니다. - Intersection (
&): "A이면서 동시에 B"인 타입을 의미하며, 여러 타입을 하나로 결합하여 모든 속성을 갖게 합니다.
// Union 타입 예시
type Status = "success" | "error" | "loading";
function printStatus(status: Status) {
console.log(`현재 상태: ${status}`);
}
// Intersection 타입 예시
interface Nameable { name: string; }
interface Ageable { age: number; }
type Person = Nameable & Ageable;
const person: Person = { name: "김철수", age: 25 };
타입 가드 (Type Guard)
Union 타입으로 정의된 변수가 실제 런타임에 어떤 타입인지 확인하여 타입을 좁히는(Narrowing) 기법입니다.
function printId(id: number | string) {
if (typeof id === "string") {
// 이 블록 안에서 id는 string 타입으로 간주됨
console.log(id.toUpperCase());
} else {
// 이 블록 안에서 id는 number 타입으로 간주됨
console.log(id.toFixed(2));
}
}
매핑된 타입 (Mapped Types)
기존 타입의 모든 속성을 순회하며 새로운 타입을 생성하는 기능입니다.
type Options = {
color: string;
size: number;
};
type ReadonlyOptions = {
readonly [P in keyof Options]: Options[P];
};
5. 설정 및 환경 구축
5.1 <a href="/doc/%EA%B8%B0%EC%88%A0/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D/%EC%84%A4%EC%A0%95%20%ED%8C%8C%EC%9D%BC/tsconfig.json" class="wiki-link wiki-link-missing">tsconfig.json</a>
TypeScript 프로젝트의 루트 디렉토리에 위치하며, 컴파일러의 동작 방식을 설정하는 파일입니다. tsc --init 명령어로 생성할 수 있습니다.
주요 옵션:
- target: 컴파일 결과물이 될 자바스크립트 버전 (예: ES5, ES6, ESNext)
- module: 모듈 시스템 설정 (예: CommonJS, ESNext)
- strict: 엄격한 타입 검사 활성화 (true 권장)
- outDir: 컴파일된 .js 파일이 저장될 경로
- rootDir: 소스 파일(.ts)이 위치한 경로
설정 예시:
{
"compilerOptions": {
"target": "ES6",
"module": "CommonJS",
"strict": true,
"outDir": "./dist",
"rootDir": "./src",
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
5.2 개발 환경 (IDE)
Visual Studio Code(VS Code)는 TypeScript와 동일한 마이크로소프트 제품으로, 가장 강력한 지원을 제공합니다. 실시간 오류 표시, 타입 힌트, 리팩토링 도구 등을 기본적으로 내장하고 있습니다.
6. 생태계 및 활용
6.1 프레임워크 통합
- React:
.tsx확장자를 사용하여 JSX 문법과 타입을 함께 사용합니다. Props와 State에 인터페이스를 적용하여 컴포넌트 간 데이터 흐름을 명확히 합니다. - Vue: Vue 3 버전부터 TypeScript를 공식적으로 지원하며, Composition API와 결합하여 강력한 타입 시스템을 구축할 수 있습니다.
- Node.js:
@types/node패키지를 설치하여 Node.js 내장 API에 대한 타입 정의를 사용할 수 있습니다.
6.2 타입 정의 파일 (.d.ts)
자바스크립트로 작성된 외부 라이브러리를 TypeScript에서 사용할 때, 해당 라이브러리의 타입 정보가 필요합니다. 이를 위해 .d.ts (Declaration file) 파일이 사용됩니다. 많은 오픈 소스 라이브러리는 @types 네임스페이스(DefinitelyTyped)를 통해 커뮤니티 기반의 타입 정의 파일을 제공합니다.
- 예: npm install --save-dev @types/lodash
7. 실무 활용 팁 및 베스트 프랙티스
any사용 지양:any를 남용하면 TypeScript를 사용하는 의미가 사라집니다. 타입을 알 수 없는 경우unknown타입을 사용하고, 타입 가드를 통해 타입을 확정한 후 사용하십시오.- 인터페이스 vs 타입 별칭: 객체의 구조를 정의하고 확장(extends)이 필요한 경우에는
interface를, Union/Intersection 등 복잡한 타입 조합이 필요한 경우에는type을 사용하십시오. - 엄격한 모드(
strict: true) 활성화: 초기 설정 시strict모드를 켜서null및undefined체크를 강제함으로써 런타임의NullPointerException과 같은 오류를 방지하십시오. - 타입 추론 활용: 뻔한 타입(예:
const a: number = 1;)은 명시하지 말고 TypeScript의 추론 기능에 맡겨 코드의 가독성을 높이십시오. - 유틸리티 타입 활용: 내장 유틸리티 타입을 활용하여 중복되는 타입 정의를 줄이십시오.
interface User { id: number; name: string; email: string; } // Partial<T>: 모든 속성을 선택적으로 변경 (업데이트 시 유용) const updateUser: Partial<User> = { name: "새 이름" }; // Pick<T, K>: 특정 속성만 선택하여 새로운 타입 생성 type UserSummary = Pick<User, "id" | "name">; // Omit<T, K>: 특정 속성만 제외하고 새로운 타입 생성 type UserWithoutEmail = Omit<User, "email">;
불변성과 상수 집합 관리
기본 타입 시스템을 보강하여 데이터의 불변성을 유지하고 정해진 값의 집합을 효율적으로 관리하는 방법입니다.
enum(열거형): 서로 관련 있는 상수들의 집합을 정의할 때 사용합니다. 코드의 가독성을 높이고 의미 없는 숫자나 문자열 대신 명명된 상수를 사용할 수 있게 합니다.enum Direction { Up = "UP", Down = "DOWN", Left = "LEFT", Right = "RIGHT", } const move: Direction = Direction.Up;readonly수식어: 클래스의 속성이나 인터페이스의 프로퍼티에 사용하여, 초기화 이후 값이 변경되는 것을 방지합니다. 이는 함수형 프로그래밍 패턴이나 상태 관리 시 데이터 무결성을 보장하는 데 유용합니다.interface Config { readonly apiKey: string; readonly endpoint: string; } const config: Config = { apiKey: "abc-123", endpoint: "https://api.com" }; // config.apiKey = "new-key"; // 오류 발생: 읽기 전용 속성이므로 할당할 수 없음
조건부 타입과 타입 추론
입력 타입에 따라 출력 타입을 동적으로 결정하는 고도의 타입 프로그래밍 기법입니다.
- 조건부 타입 (Conditional Types):
T extends U ? X : Y문법을 사용하여, 타입T가U에 할당 가능하다면X타입을, 그렇지 않으면Y타입을 반환합니다. infer키워드: 조건부 타입 내에서 특정 타입의 구성 요소를 추출하여 새로운 타입 변수로 사용할 수 있게 합니다.
// 예제: 배열의 요소 타입을 추출하는 유틸리티 타입
type UnpackArray<T> = T extends (infer U)[] ? U : T;
type StringArray = string[];
type Result1 = UnpackArray<StringArray>; // string
type NotArray = number;
type Result2 = UnpackArray<NotArray>; // number
최신 동향 및 발전 방향
TypeScript는 단순한 타입 추가를 넘어, 더 정교한 타입 표현력과 런타임 효율성을 추구하고 있습니다.
- Template Literal Types: 문자열 타입을 템플릿 리터럴 형태로 정의하여, 특정 패턴의 문자열을 엄격하게 제한할 수 있습니다. (예:
type Color = `rgb(${number}, ${number}, ${number})`) satisfies연산자: 변수가 특정 타입을 만족하는지 검사하면서도, 추론된 가장 구체적인 타입을 유지하고 싶을 때 사용합니다.as(타입 단언)와 달리 타입 안전성을 해치지 않습니다.type Colors = "red" | "green" | "blue"; type Palette = Record<string, Colors | number>; // satisfies를 사용하면 Palette 타입을 만족하는지 확인하면서, // 각 키의 값이 구체적으로 "red"인지 number인지 기억합니다. const palette = { primary: "red", secondary: 100, } satisfies Palette; console.log(palette.primary.toUpperCase()); // 가능 (string으로 추론됨)- 차세대 런타임 지원: Deno와 Bun과 같은 최신 런타임은 TypeScript를 기본적으로 지원하거나 내장 컴파일러를 통해 별도의
tsc과정 없이 실행할 수 있는 환경을 제공하여 개발 생산성을 높이고 있습니다.
런타임 타입 검증과 보완책
TypeScript의 타입 시스템은 컴파일 타임에만 존재하며, 런타임에 사라지는 '타입 소거(Type Erasure)' 특성이 있습니다. 따라서 외부 API 응답이나 사용자 입력값과 같은 런타임 데이터의 유효성을 검증하기 위해 별도의 라이브러리가 필요합니다.
| 비교 항목 | Zod | Valibot |
|---|---|---|
| 특징 | 가장 널리 쓰이는 표준 라이브러리 | 모듈형 구조로 트리 쉐이킹 최적화 |
| 번들 크기 | 상대적으로 큼 | 매우 작음 (필요한 함수만 임포트) |
| 사용성 | 체이닝 방식의 직관적인 API | 함수형 합성 방식의 API |
| 성능 | 일반적인 수준 | 런타임 오버헤드 최소화에 집중 |
- 실무 활용 예시 (Zod):
import { z } from "zod"; // 1. 런타임 스키마 정의 const UserSchema = z.object({ id: z.number(), username: z.string().min(2), email: z.string().email(), }); // 2. 스키마로부터 TypeScript 타입 추출 (infer) type User = z.infer<typeof UserSchema>; // 3. 런타임 데이터 검증 const response = await fetch("/api/user"); const data = await response.json(); const result = UserSchema.safeParse(data); if (!result.success) { console.error("데이터 형식이 올바르지 않습니다:", result.error); } else { console.log("검증된 데이터:", result.data); // result.data는 User 타입으로 추론됨 }
서버 컴포넌트와 프레임워크 통합 보강
Next.js의 App Router 도입 이후, 서버와 클라이언트 간의 경계가 명확해짐에 따라 타입 정의 방식이 진화하고 있습니다.
- 서버 컴포넌트(Server Components): 서버에서만 실행되는 컴포넌트의 Props에
Promise타입을 활용하여 비동기 데이터 페칭의 타입 안전성을 확보합니다. - API 라우트 타입 정의:
NextRequest와NextResponse타입을 통해 요청 헤더, 쿼리 파라미터, 응답 바디의 타입을 명확히 정의하여 런타임 에러를 방지합니다. - Server Actions: 서버 함수에 타입을 지정함으로써, 클라이언트에서 서버 함수를 호출할 때 전달하는 인자와 반환 값에 대해 완전한 타입 체크가 가능해졌습니다.
이 문서는 AI 모델(gemma-4-31b)에 의해 생성된 콘텐츠입니다.
주의사항: AI가 생성한 내용은 부정확하거나 편향된 정보를 포함할 수 있습니다. 중요한 결정을 내리기 전에 반드시 신뢰할 수 있는 출처를 통해 정보를 확인하시기 바랍니다.