
TypeScript란?
JavaScript와의 차이
JavaScript는 동적 타입 언어이다. 변수에 어떤 값이 들어올지 예측하기 어렵고, 실수로 잘못된 값을 넣어도 에러 없이 실행되는 경우가 많다. 이런 문제를 해결해주는 것이 바로 TypeScript이다.
TypeScript는 JavaScript에 '정적 타입'을 추가한 언어로, 코드를 작성하는 단계에서 실수를 미리 잡을 수 있게 도와준다.
function add(a: number, b: number): number {
return a + b;
}
왜 써야 하는가?
- 실수 방지
변수 타입을 명확히 지정하므로 잘못된 값이 들어오면 에러를 미리 잡아준다. - 코드 예측 가능
개발자가 의도한 데이터 구조나 동작을 명확히 드러낸다.
TypeScript는 어떻게 실행될까?
TypeScript는 브라우저에서 직접 실행되지 않는다.
우리가 작성한 .ts 파일을 브라우저가 이해할 수 있는 .js 파일로 컴파일 해야한다.
# ts 파일을 js로 컴파일하는 명령어
tsc index.ts
TypeScript -> (컴파일) -> JavaScript -> 브라우저 실행
🤔 그럼 JavaScript는 처음에 왜 타입을 없게 만들었을까?
A. 웹 디자이너도 쉽게 쓸 수 있는 스크립트 언어 용도였기 때문에.
초기 목적은 간단한 웹페이지 동작만 처리하는 용도였기 때문에 빠르게 만들고 바로 실행하는 게 중요했다.
그래서 타입을 미리 지정하는 정적 타입 시스템은 오히려 부담이었다.
타입 시스템 소개
기본 타입
string, number, boolean, null / undefined
배열
string[], number[]
튜플
[타입1, 타입2, ... ]
정해진 타입과 길이를 가진 배열이다
let userInfo: [string, number] = ["유진", 26];
유니언 타입 ( | )
여러 타입 중 하나를 허용
let status: string | number = "loading";
status = 404; // OK
리터럴 타입
특정 값만 허용
let direction: "left" | "right" | "up" | "down";
direction = "left"; // ✅
direction = "center"; // ❌
주로 버튼 타입, 상태, 값, 방향 같은 고정된 옵션을 지정할 때 많이 씀
타입 단언 (as)
개발자가 "내가 타입을 알고 있으니 믿어줘"라고 명시하는 방법
let value: any = "hello";
let length: number = (value as string).length;
타입 추론
타입을 명시하지 않아도, TypeScript가 자동으로 타입을 유츄해줌
let score = 100; // TypeScript가 number로 자동 추론함
하지만 복잡한 객체나 매개변수에선 명시적으로 타입 지정하는 게 좋음
옵셔널 속성 (?)
해당 속성이 있을 수도 있고 없을 수도 있음
type User = {
name: string;
age?: number;
};
const user1: User = { name: "유진" }; // age 없음 OK
const user2: User = { name: "유진", age: 25 }; // 있음도 OK
함수와 타입
TypeScript는 함수의 매개변수와 반환값 모두에 타입을 지정해 실수를 방지할 수 있다.
function greet(name: string): string {
return `Hello, ${name}`;
}
greet("유진"); // ✅ OK
greet(123); // ❌ Error: number는 string이 아님
- name: string -> 매개변수 타입
- : string -> 반환값 타입
반환값 타입은 대부분 생략해도 TypeScript가 자동 추론하지만, 명시하면 더 명확하다.
선택적 매개변수 ( ? )
function greet(name?: string) {}
greet(); // "Hello, stranger"
greet("유진"); // "Hello, 유진"
기본값 설정
매개변수에 기본값을 설정하면 값을 전달하지 않아도 자동으로 사용됨
function greet(name: string = "Guest") {
console.log(`Hi, ${name}`);
}
greet(); // "Hi, Guest"
greet("유진"); // "Hi, 유진"
Rest 파라미터와 타입
function sum(...numbers: number[]): number {
return numbers.reduce((a, b) => a + b, 0);
}
sum(1, 2, 3); // 6
sum(); // 0
- ...numbers: number[]
여러 개의 인자를 배열로 받아서 처리할 수 있음
Rest 파라미터는 배열 타입으로 지정해야 한다.
JS에서는 에러가 안 나는 이유
function func1(a, b, c) {
console.log(a, b, c);
}
func1(1); // a=1, b=undefined, c=undefined
func1(1, 2, 3, 4, 5); // a=1, b=2, c=3 → 추가 인자는 무시됨
JavaScript는
- 매개변수가 부족해도 에러 안 나고 undefined
- 매개변수가 많아도 앞에서부터 필요한 만큼만 받음
-> 매우 유연하지만, 실수 잡기가 힘듦!
TS에서는 어떻게 막을까?
function func1(a: number, b: number, c: number) {
console.log(a, b, c);
}
func1(1); // ❌ Error: 인자 1개 전달, 3개 필요
func1(1, 2, 3, 4); // ❌ Error: 인자 너무 많음
TypeScript는 컴파일 시점에:
- 필요한 인자의 개수
- 각 인자의 타입을 모두 검사해서 잘못된 호출을 방지함!
객체의 타입 정의 - type vs interface
JavaScript 객체는 자유롭게 속성을 추가하거나 구조가 바뀌기 쉽기 때문에,
TypeScript에서는 객체 구조를 명확하게 정의해줄 필요가 있어.
이를 위해 type과 interface를 사용한다.
type 키워드 - 타입 별칭
type User = {
name: string;
age: number;
};
- type은 타입에 별명을 붙이는 것
- 객체뿐만 아니라 유니언 타입, 튜플, 함수 타입 등 복합적인 타입 정의에 유리
interface 키워드 - 객체 전용 설계도
interface User {
name: string;
age: number;
}
- interface는 객체 구조를 설계할 때 자주 사용
- 주로 클래스와 함께 쓰거나, API 응답 객체 등 명확한 구조가 필요한 경우
확장 방법: & vs extends
type의 확장 → & (Intersection)
type Animal = { name: string };
type Dog = Animal & { breed: string };
const dog: Dog = {
name: "Max",
breed: "Poodle"
};
interface의 확장 → extends
interface Animal {
name: string;
}
interface Dog extends Animal {
breed: string;
}
const dog: Dog = {
name: "Max",
breed: "Poodle"
};
구조는 거의 같지만, 사용하는 키워드가 다르다는 점 주의!
속성 제어: readonly, ?
readonly
값을 초기화한 뒤 변경되지 않도록 제한
type User = {
readonly id: number;
name: string;
};
const user: User = { id: 1, name: "유진" };
user.id = 2; // ❌ Error: readonly라서 변경 불가
? (옵셔널 속성)
해당 속성이 있어도 되고 없어도 됨
interface Profile {
name: string;
bio?: string;
}
const p1: Profile = { name: "유진" }; // OK
const p2: Profile = { name: "유진", bio: "백엔드" }; // OK
Generic (제네릭) - 타입을 나중에 넣는 템플릿
제네릭이 뭘까?
제네릭은 '타입을 함수나 클래스 외부에서 주입할 수 있는 문법'이다.
어떤 타입이든 받아서 그 타입을 그대로 써주는 함수/클래스를 만들고 싶을 때 제네릭이 필요하다.
function identity<T>(arg: T): T {
return arg;
}
identity<string>("hello"); // "hello"
identity<number>(123); // 123
- T는 임의의 타입 변수
- 호출 시 <string>, <number>처럼 타입을 넣어줄 수 있다.
- 타입을 생략해도 TS가 추론 가능할 경우 알아서 추론한다.
const result = identity("유진"); // 타입 자동 추론됨 (string)
제네릭을 안 쓰면?
function identityWrong(arg: any): any {
return arg;
}
any는 타입 체크가 안 되기 때문에 반환값도 타입 정보가 사라져서 오히려 위험해졌다.
'프로디지털아카데미' 카테고리의 다른 글
| HTTP Request & Response (2) | 2025.08.17 |
|---|---|
| 내 주력 언어의 특징 - JAVA (Feat. KPT 회고) (10) | 2025.08.10 |
| [Open API를 활용한 금융서비스 프로젝트] SumEarly 발표 후 회고 (8) | 2025.08.01 |
| 웹 인증 시스템 이해하기 - Cookie, Session, JWT (2) | 2025.06.30 |
| [PDA] 프론트엔드 입문 첫걸음: JavaScript 개념 정리 모음 (3) | 2025.06.08 |