1. TypeScript
1.1. 정의
TypeScript는 JavaScript를 확장한 정적 타입 언어다. JavaScript의 모든 문법을 포함하면서, 변수·매개변수·반환값에 타입을 명시할 수 있는 기능을 추가한다. 작성된 TypeScript 코드는 컴파일러를 거쳐 JavaScript로 변환되며, 변환된 코드는 브라우저 또는 Node.js 환경에서 실행된다.
1.2. 왜 JavaScript에 타입이 필요한가
JavaScript는 변수에 어떤 타입의 값이든 자유롭게 담을 수 있다. 이는 내용물을 표시하지 않은 상자에 비유할 수 있다. 상자 안의 값이 무엇인지 확인하려면 코드를 직접 실행해보거나 문서를 일일이 확인해야 한다. 프로젝트 규모가 커질수록 이 비용은 기하급수적으로 증가한다.
TypeScript는 상자 외부에 “이 상자에는 숫자만 들어간다”, “이 상자에는 사용자 정보만 들어간다”와 같은 라벨을 붙이는 역할을 한다. 코드를 읽는 사람과 컴파일러 모두가 라벨을 통해 내용물을 미리 파악할 수 있다.
1.3. 설치
TypeScript 컴파일러는 npm 또는 yarn을 통해 전역으로 설치한다.
npm install -g typescript
# 또는
yarn global add typescript-g 옵션은 패키지를 시스템 전역에 설치하며, 설치 이후에는 어떤 디렉터리에서도 tsc 명령어를 사용할 수 있다.
1.4. 코드 비교
JavaScript와 TypeScript의 차이는 변수 선언부에서 가장 명확하게 드러난다.
// JavaScript — 타입을 명시하지 않는다
let name = '홍길동';
let age = 30;
// TypeScript — 타입을 명시한다
let userName: string = '홍길동';
let userAge: number = 30;
// 복합 타입 — 인터페이스로 객체 구조를 정의한다
interface User {
name: string;
age: number;
isStudent: boolean;
}
let student: User = {
name: '김철수',
age: 25,
isStudent: true
};TypeScript는 : 타입 형식의 타입 어노테이션(Type Annotation) 을 통해 변수가 가질 수 있는 값의 형태를 제한한다.
2. TypeScript와 JavaScript의 차이
| 항목 | JavaScript | TypeScript |
|---|---|---|
| 타입 시스템 | 동적 타입 | 정적 타입 |
| 타입 검사 시점 | 런타임 | 컴파일 타임 |
| 오류 발견 시점 | 코드 실행 중 | 코드 작성 중 |
| 적합한 프로젝트 규모 | 소규모 | 대·중규모 |
2.1. 동적 타입과 정적 타입
JavaScript는 런타임에 변수의 타입이 결정되며, 동일한 변수에 다른 타입의 값을 자유롭게 재할당할 수 있다. TypeScript는 컴파일 시점에 타입이 결정되며, 선언된 타입과 다른 값을 할당하면 컴파일 오류가 발생한다.
다음 두 코드는 동일한 동작을 시도하지만 결과가 다르다.
// JavaScript — 문제없이 실행된다
let x = 10;
x = "안녕"; // 정상
x = true; // 정상// TypeScript — 컴파일 단계에서 차단된다
let x: number = 10;
x = "안녕"; // 오류: 'string' 형식은 'number' 형식에 할당할 수 없다.JavaScript는 이런 코드도 실행은 되지만, 이후 x를 숫자로 가정하고 작성한 다른 코드에서 예기치 못한 버그가 발생한다. TypeScript는 이러한 버그가 만들어지는 순간 자체를 차단한다.
2.2. 장점과 단점
장점
- 코드 가독성과 유지보수성이 향상된다.
- 런타임 이전에 오류를 발견할 수 있어 디버깅 비용이 감소한다.
- IDE의 자동 완성, 타입 추론, 리팩토링 도구 지원이 강화된다.
- 대규모 협업 환경에서 인터페이스 정의를 통해 의사소통 비용을 줄일 수 있다.
단점
- 학습 곡선이 존재한다.
- 컴파일 단계가 추가되어 빌드 시간이 증가한다.
- 작은 규모의 프로젝트에서는 오버헤드가 될 수 있다.
소규모 프로토타입이나 일회성 스크립트는 JavaScript로 충분하다. 그러나 여러 명이 협업하거나 장기적으로 유지보수해야 하는 프로젝트라면 TypeScript의 비용은 충분히 감수할 가치가 있다.
3. 컴파일 과정
JavaScript는 별도의 변환 과정 없이 브라우저나 Node.js에서 곧바로 실행된다. 반면 TypeScript는 브라우저나 Node.js가 직접 실행할 수 없으며, JavaScript로 변환하는 과정이 필요하다. 이 변환 과정을 컴파일(Compile) 이라 한다.
[1] .ts 파일 작성
↓
[2] tsc(TypeScript 컴파일러)가 타입 검사 수행
↓
[3] 타입 정보를 제거한 .js 파일 생성
↓
[4] 브라우저 또는 Node.js에서 실행타입 정보는 컴파일 결과물에 포함되지 않는다. 즉, 타입은 개발 단계에서만 작동하는 안전장치이며, 실행 환경에는 영향을 주지 않는다. 이 때문에 TypeScript의 모든 타입 검사는 코드를 실행하기 전에 끝나야 의미가 있다.
4. 데이터 타입
4.1. 기본 타입
| 타입 | 설명 |
|---|---|
number |
정수 및 실수를 포함한 숫자 |
string |
문자열 |
boolean |
true 또는 false |
null |
의도적으로 비어 있음을 나타내는 값 |
undefined |
값이 할당되지 않은 상태 |
any |
모든 타입을 허용하는 타입 |
JavaScript에도 동일한 값들이 존재하지만, 변수에 타입을 강제할 방법이 없다. TypeScript는 위의 타입을 변수 선언 시점에 명시하여 잘못된 값의 할당을 사전에 차단한다.
4.2. any 타입에 관한 주의
any 타입은 모든 값을 허용하므로, 사실상 JavaScript 변수와 동일하게 동작한다.
let value: any = 10;
value = "문자열"; // 오류 없음
value = true; // 오류 없음any를 남용하면 TypeScript가 제공하는 정적 분석의 이점을 잃게 되므로, 외부 라이브러리나 동적 데이터를 다루는 등 불가피한 경우에만 제한적으로 사용한다.
4.3. 변수 선언
let message: string = 'Hello World!';
const pi: number = 3.14159;
var age: number = 25;| 키워드 | 재할당 | 스코프 | 권장 |
|---|---|---|---|
const |
불가 | 블록 | 우선 사용 |
let |
가능 | 블록 | 재할당이 필요한 경우 |
var |
가능 | 함수 | 사용을 지양한다 |
var는 호이스팅과 함수 스코프로 인해 예측하기 어려운 동작을 유발할 수 있으므로, 현대 TypeScript/JavaScript에서는 const와 let만 사용하는 것이 표준이다. 기본은 const로 선언하고, 값을 재할당해야 하는 경우에 한해 let으로 변경하는 방식이 권장된다.
5. 함수
5.1. 기본 함수
함수는 JavaScript에도 존재하지만, TypeScript에서는 매개변수와 반환값 모두에 타입을 명시한다. 두 코드를 비교하면 차이가 분명하게 드러난다.
// JavaScript — 어떤 값이든 받을 수 있다
function greet(name) {
return `Hello, ${name}!`;
}
greet(123); // 실행됨: "Hello, 123!"// TypeScript — 문자열만 허용한다
function greet(name: string): string {
return `Hello, ${name}!`;
}
greet(123); // 오류: 'number' 형식의 인수는 'string' 형식의 매개변수에 할당될 수 없다.
console.log(greet('Jane'));name: string은 매개변수의 타입이며, 함수 시그니처 끝의 : string은 반환값의 타입이다. 반환 타입을 생략하면 컴파일러가 추론하지만, 명시적으로 작성하는 편이 가독성에 유리하다.
5.2. 옵셔널 파라미터
매개변수 이름 뒤에 ?를 붙이면 해당 매개변수는 선택적이 된다. 호출 시 값을 전달하지 않아도 오류가 발생하지 않으며, 전달되지 않은 경우 값은 undefined가 된다.
function testOptionalParam(base: string, optionalParam?: string) {
console.log('base: ', base);
console.log('optionalParam: ', optionalParam);
}
testOptionalParam('필수값');
testOptionalParam('필수값', '추가값');옵셔널 파라미터는 반드시 필수 파라미터 뒤에 위치해야 한다. JavaScript에서는 매개변수의 필수 여부를 문법적으로 구분할 수 없고 함수 내부에서 직접 검사해야 했지만, TypeScript는 이를 시그니처 단계에서 명시한다.
6. 인터페이스
6.1. 정의
인터페이스(Interface)는 객체가 가져야 할 속성과 그 타입을 정의하는 일종의 명세다. 객체의 구조를 사전에 약속함으로써, 잘못된 형태의 객체가 사용되는 것을 컴파일 단계에서 차단한다. 건축에 비유하면 인터페이스는 설계도이며, 객체는 그 설계도를 따라 지어진 건물이다.
JavaScript에는 인터페이스 문법이 없다. 객체의 형태가 코드 어디에서도 보장되지 않으므로, 잘못된 속성에 접근하거나 오타를 내도 런타임이 되어서야 문제가 드러난다.
interface User {
name: string;
age: number;
}
const user: User = { name: 'John', age: 30 };User 인터페이스를 따르지 않는 객체를 할당하면 컴파일 오류가 발생한다.
const invalidUser: User = { name: 'John' };
// 오류: 'age' 속성이 '{ name: string; }' 형식에 없지만 'User' 형식에서 필수다.6.2. 옵셔널 속성
인터페이스의 속성에도 ?를 사용하여 선택적 속성을 정의할 수 있다.
interface User {
name: string;
age?: number;
}
const user1: User = { name: 'John' };
const user2: User = { name: 'Jane', age: 30 };user1은 age 속성이 없지만 User 타입을 만족하며, user2는 age 속성을 포함하므로 마찬가지로 유효하다. 옵셔널 속성은 값이 존재하지 않거나 undefined인 경우를 허용한다. 회원가입 시 이름은 필수이지만 나이는 선택 입력으로 받는 경우가 대표적인 사용 예다.
7. tsconfig.json
7.1. 역할
tsconfig.json은 TypeScript 프로젝트의 컴파일러 설정 파일이다. 이 파일이 위치한 디렉터리가 프로젝트의 루트로 인식되며, 컴파일 대상·출력 위치·언어 버전 등 컴파일 동작 전반이 이 파일을 통해 제어된다. JavaScript에는 컴파일 단계가 없으므로 이런 설정 파일이 필요하지 않다.
7.2. 프로젝트 초기화 절차
# 1. TypeScript 전역 설치
npm install -g typescript
# 2. tsconfig.json 생성
tsc --init
# 3. .ts 파일 작성
# 4. 컴파일 실행
tsctsc --init은 기본 설정이 포함된 tsconfig.json을 자동으로 생성한다.
7.3. 주요 옵션
| 옵션 | 설명 |
|---|---|
outDir |
컴파일된 JavaScript 파일이 저장될 출력 디렉터리 |
include |
컴파일에 포함할 파일 또는 디렉터리 패턴 |
exclude |
컴파일에서 제외할 파일 또는 디렉터리 패턴 |
allowJs |
JavaScript 파일(.js)의 컴파일 허용 여부 |
7.4. 설정 예시
{
"compilerOptions": {
"outDir": "./dist",
"target": "ES6",
"allowJs": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}위 설정은 src 디렉터리 하위의 모든 파일을 ES6 문법의 JavaScript로 컴파일하여 dist 디렉터리에 출력하며, node_modules는 컴파일 대상에서 제외한다.
TypeScript가 필요한 이유 — 코드 예시
예시 1. 한 달 뒤의 나, 또는 다른 동료가 함수를 사용할 때
JavaScript 함수는 시그니처만으로 사용법을 알 수 없다. 매개변수의 의미와 타입, 반환값의 형태를 모두 함수 본문이나 별도의 주석에 의존해야 한다.
// JavaScript — 한 달 뒤에 다시 보면?
function calculateDiscount(item, rate, options) {
// rate는 0~1 사이의 비율인가, 0~100 사이의 퍼센트인가?
// options에는 어떤 속성이 들어가야 하는가?
// 반환값은 할인된 가격인가, 할인 금액인가?
// ...
}
TypeScript로 작성된 같은 함수는 시그니처가 곧 사용 설명서가 된다.
// TypeScript — 시그니처만 봐도 사용법이 명확하다
interface Product {
id: string;
price: number;
}
interface DiscountOptions {
applyToShipping: boolean;
maxAmount?: number;
}
function calculateDiscount(
item: Product,
rate: number,
options: DiscountOptions
): number {
return item.price * (1 - rate);
}
함수를 호출하는 쪽에서는 IDE가 매개변수의 타입과 옵션의 구조를 자동으로 안내한다. 작성자에게 직접 물어볼 필요가 없으며, 작성자가 회사를 떠난 뒤에도 코드 자체가 문서 역할을 한다.
예시 2. 객체 형태가 보장되지 않을 때 발생하는 오류
서버에서 받아온 응답이나 외부 모듈에서 전달된 객체를 다룰 때, JavaScript는 객체의 구조를 보장하지 않는다.
// JavaScript — 오타를 내도 실행은 된다
function renderUser(user) {
console.log(user.nme); // 오타: name → nme
}
renderUser({ name: '홍길동', age: 30 });
// 출력: undefined
// 화면에는 빈 값이 그대로 표시된다.
TypeScript는 이런 실수를 컴파일 시점에 잡아낸다.
interface User {
name: string;
age: number;
}
function renderUser(user: User) {
console.log(user.nme);
// 오류: 'nme' 속성이 'User' 형식에 없습니다. 'name'을(를) 사용하시겠습니까?
}
코드 작성자가 5명, 10명으로 늘어나도 인터페이스만 공유하면 모두가 동일한 객체 구조를 전제로 작업할 수 있다.
예시 3. 변경 영향 범위가 자동으로 추적된다
기존 함수의 시그니처를 수정하는 상황을 가정해보자. 매개변수 하나를 추가하거나 반환 타입을 바꾸는 작업이다.
// JavaScript — 함수 시그니처 변경 후
function getUser(id) { // 기존
return { name: '홍길동' };
}
function getUser(id, includeEmail) { // 변경: 매개변수 추가
return { name: '홍길동', email: '...' };
}
// 어디서 이 함수를 쓰고 있었는지 일일이 검색해야 한다.
// 빠뜨린 호출 지점은 런타임에 가서야 오류로 드러난다.
// TypeScript — 모든 호출 지점이 즉시 오류로 표시된다
function getUser(id: string, includeEmail: boolean): User {
// ...
}
getUser('user-1');
// 오류: 2개의 인수가 필요한데 1개를 가져왔습니다.
getUser('user-2', true); // ✅ OK
컴파일러가 변경의 파급 범위를 즉시 알려주므로, 수정해야 할 위치가 한눈에 드러난다. 이 차이는 코드베이스가 클수록, 변경이 잦을수록 더 크게 벌어진다.
세 예시 모두 한 사람이 한 번 작성하고 끝나는 코드가 아니라, 여러 사람이 오랜 기간 함께 다듬는 코드일수록 효과가 크다. 결국 TypeScript는 협업과 유지보수를 위한 도구다.
정리
TypeScript는 JavaScript의 동적 타입 특성에서 비롯되는 런타임 오류를 컴파일 단계에서 차단하기 위해 설계된 언어다. 타입 어노테이션·인터페이스·컴파일러 옵션은 모두 코드의 안정성과 유지보수성을 높이기 위한 도구이며, 개별 문법보다 정적 타입 시스템이라는 큰 그림을 이해하는 것이 학습의 핵심이다. JavaScript와의 차이를 비교하면서 코드를 작성해보면, 타입이 단순히 문법적 제약이 아니라 협업과 유지보수를 위한 설계 도구임을 체감할 수 있다.
Tags: #TypeScript #JavaScript #NodeJS #정적타입 #개발입문
'Front_end' 카테고리의 다른 글
| 오류코드 정리 (0) | 2026.05.13 |
|---|---|
| 강의 자료 2 (0) | 2026.02.25 |
| 리엑트 server.js 예외처리방법 (0) | 2026.01.01 |
| React 개발자를 위한 가장 쉬운 Node.js 백엔드 연동 가이드 (0) | 2025.11.03 |
| ☁️구름톤 자바스크립트 기초 정리 (0) | 2025.05.03 |