🎯 TypeScript의 κΈ°λ³Έ κ°œλ…μ„ μ •λ¦¬ν•©λ‹ˆλ‹€.


πŸ“— Today I Learned

TypeScript

Microsoftμ—μ„œ κ°œλ°œν•œ JavaScript의 μƒμœ„ μ–Έμ–΄λ‘œ, 정적 νƒ€μž… κΈ°λŠ₯κ³Ό 객체지ν–₯적 μš”μ†Œλ₯Ό κ°–μΆ”κ³  μžˆμŠ΅λ‹ˆλ‹€.

νŠΉμ§•

TypeScript둜 μž‘μ„±λœ μ½”λ“œλŠ” 직접 싀행될 수 μ—†μœΌλ©°, λ¨Όμ € JavaScript둜 컴파일된 ν›„ λΈŒλΌμš°μ €λ‚˜ Node.js ν™˜κ²½μ—μ„œ μ‹€ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

πŸ€” μ™œ νƒ€μž…μŠ€ν¬λ¦½νŠΈκ°€ ν•„μš”ν• κΉŒ?

JavaScriptλŠ” ν”„λ‘œμ νŠΈ 규λͺ¨κ°€ μ»€μ§€λ©΄μ„œ 각 λ³€μˆ˜λ‚˜ ν•¨μˆ˜κ°€ μ–΄λ–€ νƒ€μž…μ„ κΈ°λŒ€ν•˜λŠ”μ§€ κΈ°μ–΅ν•˜κΈ° μ–΄λ ΅μ§€λ§Œ TypeScriptλŠ” νƒ€μž…μ„ μ§€μ •ν•  수 있기 λ•Œλ¬Έμ— μœ μ§€λ³΄μˆ˜μ„±μ΄ μ’‹μŠ΅λ‹ˆλ‹€.

JavaScriptλŠ” λŸ°νƒ€μž„μ—μ„œ νƒ€μž… 였λ₯˜λ₯Ό λ°œκ²¬ν•˜μ§€λ§Œ, TypeScriptλŠ” 컴파일 λ‹¨κ³„μ—μ„œ 였λ₯˜λ₯Ό λ°œκ²¬ν•˜κΈ° λ•Œλ¬Έμ— νƒ€μž…μ΄ 잘λͺ»λ˜λ©΄ 버그λ₯Ό 미리 λ°©μ§€ν•  수 μžˆμŠ΅λ‹ˆλ‹€.




μ„€μΉ˜ 및 μ‚¬μš© 방법

TypeScriptλ₯Ό μ‚¬μš©ν•˜λ €λ©΄ λ¨Όμ € Node.jsκ°€ μ„€μΉ˜λ˜μ–΄ μžˆμ–΄μ•Ό ν•©λ‹ˆλ‹€.

1. TypeScriptλ₯Ό μ „μ—­μœΌλ‘œ μ„€μΉ˜ν•©λ‹ˆλ‹€.

npm i -g typescript

2. tsconfig.json μ„€μ • νŒŒμΌμ„ μƒμ„±ν•©λ‹ˆλ‹€.

tsc --init

3. TypeScript 컴파일 ν•©λ‹ˆλ‹€.

tsc 파일λͺ….ts

맀번 μ»΄νŒŒμΌμ„ ν•  ν•„μš” 없이 μžλ™ 컴파일 λ˜λ„λ‘ --watch λͺ¨λ“œλ₯Ό μ„€μ •ν•΄μ€λ‹ˆλ‹€.

tsc -w 파일λͺ….ts

4. JavaScript둜 λ³€ν™˜λœ νŒŒμΌμ„ Node.js둜 μ‹€ν–‰ν•©λ‹ˆλ‹€.

node 파일λͺ….js



TypeScript νƒ€μž… μ‹œμŠ€ν…œ

TypeScriptλŠ” 데이터 νƒ€μž…μ„ λͺ…ν™•ν•˜κ²Œ μ •μ˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€. λ˜ν•œ, νƒ€μž…μ„ λͺ…μ‹œν•˜μ§€ μ•Šλ”λΌλ„ νƒ€μž… μΆ”λ‘  κΈ°λŠ₯을 톡해 μžλ™μœΌλ‘œ νƒ€μž…μ„ κ²°μ •ν•©λ‹ˆλ‹€.


κΈ°λ³Έ νƒ€μž… (Primitive Types)

  • number : 숫자λ₯Ό ν‘œν˜„ν•˜λŠ” νƒ€μž…μž…λ‹ˆλ‹€. μ •μˆ˜μ™€ μ‹€μˆ˜λ₯Ό λͺ¨λ‘ ν¬ν•¨ν•©λ‹ˆλ‹€.
let age: number = 25;

πŸ€” 숫자 λ¦¬ν„°λŸ΄μ΄λž€ λ¬΄μ—‡μΌκΉŒ?

숫자 값을 직접 ν‘œν˜„ν•˜μ—¬ μ •μ˜ν•˜λŠ” 방식을 λ§ν•©λ‹ˆλ‹€.

let speed: 50 | 100 | 200;
speed = 100; // 유효
speed = 150; // μ—λŸ¬

βœ… λ¦¬ν„°λŸ΄(Literal)μ΄λž€?

λ¦¬ν„°λŸ΄μ€ μš°λ¦¬κ°€ μ½”λ“œμ—μ„œ 직접 적어 λ„£λŠ” κ°’ κ·Έ 자체λ₯Ό λ§ν•©λ‹ˆλ‹€.
예λ₯Ό λ“€μ–΄,

const num = 3;
const str = "hello";

  • string : λ¬Έμžμ—΄μ„ ν‘œν˜„ν•©λ‹ˆλ‹€. μž‘μ€λ”°μ˜΄ν‘œ('), ν°λ”°μ˜΄ν‘œ("), λ˜λŠ” λ°±ν‹±(`)을 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
let name: string = "Alice";

πŸ€” λ¬Έμžμ—΄ λ¦¬ν„°λŸ΄μ΄λž€ λ¬΄μ—‡μΌκΉŒ?

λ¬Έμžμ—΄ 값을 직접 ν‘œν˜„ν•˜μ—¬ μ •μ˜ν•˜λŠ” 방식을 λ§ν•©λ‹ˆλ‹€.

let fruit: "apple";
fruit = "apple";   // 유효
fruit = "banana"; // μ—λŸ¬

  • boolean : μ°Έ(true) λ˜λŠ” κ±°μ§“(false)을 λ‚˜νƒ€λƒ…λ‹ˆλ‹€.
let isDarkModeEnabled: boolean = false;

πŸ€” λΆˆλ¦¬μ–Έ λ¦¬ν„°λŸ΄μ΄λž€ λ¬΄μ—‡μΌκΉŒ?

μ°Έκ³Ό κ±°μ§“ 값을 직접 ν‘œν˜„ν•˜μ—¬ μ •μ˜ν•˜λŠ” 방식을 λ§ν•©λ‹ˆλ‹€.

let isTrue: true;
isTrue = true; // 유효
isTrue = false; // μ—λŸ¬ 

  • null : 값이 μ—†μŒμ„ λͺ…μ‹œμ μœΌλ‘œ λ‚˜νƒ€λƒ…λ‹ˆλ‹€.
let selectedProduct: string | null = null;

πŸ€” null λ¦¬ν„°λŸ΄μ΄λž€ λ¬΄μ—‡μΌκΉŒ?

null 값을 직접 ν‘œν˜„ν•˜μ—¬ μ •μ˜ν•˜λŠ” 방식을 λ§ν•©λ‹ˆλ‹€.

let currentUser: null;
currentUser = null; // 유효
currentUser = "guest"; // μ—λŸ¬

  • undefined : 값이 ν• λ‹Ήλ˜μ§€ μ•Šμ€ μƒνƒœλ₯Ό λ‚˜νƒ€λƒ…λ‹ˆλ‹€.
let userName: string | undefined;
console.log(userName); // undefined

πŸ€” undefined λ¦¬ν„°λŸ΄μ΄λž€ λ¬΄μ—‡μΌκΉŒ?

undefined 값을 직접 ν‘œν˜„ν•˜μ—¬ μ •μ˜ν•˜λŠ” 방식을 λ§ν•©λ‹ˆλ‹€.

let status: undefined;
status = undefined; // 유효
status = "active"; // μ—λŸ¬



객체 νƒ€μž…(Object Types)

  • object : 객체의 νƒ€μž…μ„ μ •μ˜ν•  λ•Œ μ‚¬μš©ν•©λ‹ˆλ‹€. κ°μ²΄λŠ” ν΄λž˜μŠ€λ‚˜ ν•¨μˆ˜λ‘œ 생성할 수 μžˆμŠ΅λ‹ˆλ‹€.
// 클래슀 μ‚¬μš© μ˜ˆμ‹œ
class Person {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
}

const student: object = new Person("Alice");

// ν•¨μˆ˜λ‘œ 객체 생성
function createAnimal(type: string, age: number) {
  return { type, age };
}

const animal: object = createAnimal("Dog", 3);

πŸ€” 객체 λ¦¬ν„°λŸ΄μ΄λž€ λ¬΄μ—‡μΌκΉŒ?

μ€‘κ΄„ν˜Έ {}λ₯Ό μ‚¬μš©ν•΄ 직접 객체λ₯Ό μ„ μ–Έν•˜κ³  값을 μ •μ˜ν•˜λŠ” 방식을 λ§ν•©λ‹ˆλ‹€. 즉, 객체λ₯Ό λ§Œλ“€ λ•Œ 클래슀λ₯Ό μ‚¬μš©ν•˜κ±°λ‚˜ ν•¨μˆ˜λ‘œ μƒμ„±ν•˜μ§€ μ•Šκ³ , ν•„μš”ν•œ 속성과 값을 직접 μ¨μ„œ μ¦‰μ„μ—μ„œ λ§Œλ“  κ°μ²΄μž…λ‹ˆλ‹€.

let person :  { name: 'John', age: 30 };
person = { name: 'John', age: 30 }; // 유효
person =  { name: 'Alice', age: 25 }; // μ—λŸ¬

  • array : 같은 νƒ€μž…μ˜ μš”μ†Œλ₯Ό κ°€μ§€λŠ” 배열을 ν‘œν˜„ν•  λ•Œ μ‚¬μš©ν•©λ‹ˆλ‹€.
// κΈ°λ³Έ λ°°μ—΄ μ„ μ–Έ
let numbers: number[] = [1, 2, 3];
let names: string[] = ["Alice", "Bob"];

// μœ λ‹ˆμ–Έ νƒ€μž… λ°°μ—΄
let mixedArray : (number | string)[] = [1, 'two', 3, 'four'];

// 읽기 μ „μš© λ°°μ—΄ (ReadonlyArray<T>)
let readOnlyArray : ReadonlyArray<number> = [1, 2, 3];

πŸ€” μœ λ‹ˆμ–Έ νƒ€μž…(Union Types)μ΄λž€?
μ—¬λŸ¬ νƒ€μž… 쀑 ν•˜λ‚˜λ₯Ό κ°€μ§ˆ 수 μžˆλ„λ‘ ν—ˆμš©ν•˜λŠ” νƒ€μž…μž…λ‹ˆλ‹€.

let value: string | number;
value = "hello";   
value = 42;        

  • tuple : κ³ μ •λœ 길이와 각각의 μΈλ±μŠ€μ— λ‹€λ₯Έ νƒ€μž…μ„ κ°€μ§ˆ 수 μžˆλŠ” λ°°μ—΄μž…λ‹ˆλ‹€. νƒ€μž…μ˜ μˆœμ„œμ™€ λ°°μ—΄μ˜ 길이가 κ³ μ •λœ λ°°μ—΄μž…λ‹ˆλ‹€.
let person: [string, number] = ["Alice", 30];



특수 νƒ€μž… (Special Types)

  • any : λͺ¨λ“  νƒ€μž…μ„ ν—ˆμš©ν•©λ‹ˆλ‹€. κ°€λŠ₯ν•œ ν•œ μ‚¬μš©μ„ μ§€μ–‘ν•΄μ•Ό ν•˜λ©°, νƒ€μž… κ²€μ‚¬μ˜ 이점을 μžƒκ²Œ λ©λ‹ˆλ‹€.
let value: any = "λ¬Έμžμ—΄"; 
value = 10;

  • unknown : μ™ΈλΆ€ 데이터와 같이 νƒ€μž…μ„ μ•Œ 수 μ—†λŠ” 값을 μ²˜λ¦¬ν•  λ•Œ μ‚¬μš©λ©λ‹ˆλ‹€. any보닀 μ•ˆμ „ν•˜λ©°, νƒ€μž… 확인 없이 μ‚¬μš©ν•˜λ©΄ 였λ₯˜κ°€ λ°œμƒν•©λ‹ˆλ‹€.
let input: unknown = "Hello";

if (typeof input === "string") {
  console.log(input.toUpperCase());
}



μ‚¬μš©μž μ •μ˜ νƒ€μž… (Custom Types)

  • interface : 객체의 속성 ꡬ쑰와 νƒ€μž…μ„ μ •μ˜ν•˜λŠ” 데 μ‚¬μš©λ©λ‹ˆλ‹€. ν•¨μˆ˜, 클래슀, 객체 λ“± λ‹€μ–‘ν•œ κ³³μ—μ„œ ꡬ쑰λ₯Ό λͺ…ν™•νžˆ μ§€μ •ν•  수 μžˆμ–΄ μ½”λ“œμ˜ 가독성과 μœ μ§€λ³΄μˆ˜μ„±μ„ λ†’μ—¬μ€λ‹ˆλ‹€.
interface Animal {
  name: string;
  age?: number; // 선택적 ν”„λ‘œνΌν‹°λŠ” 속성값 뒀에`?`λ₯Ό λΆ™μ—¬ ν‘œν˜„ν•΄μ€λ‹ˆλ‹€.
  sound(): void;
}

class Dog implements Animal { // implementsλŠ” 'κ΅¬ν˜„ν•˜λ‹€'λŠ” 의미둜, Dog ν΄λž˜μŠ€κ°€ Animal μΈν„°νŽ˜μ΄μŠ€μ˜ ꡬ쑰λ₯Ό λ°˜λ“œμ‹œ 따라야 ν•œλ‹€λŠ” λœ»μž…λ‹ˆλ‹€.
  name: string;
  age?: number;

  constructor(name: string, age?: number) {
    this.name = name;
    if (age !== undefined) {
      this.age = age;
    }
  }

  sound() { // sound() λ©”μ„œλ“œλŠ” ν˜•νƒœλ§Œ μ‘΄μž¬ν•˜λ―€λ‘œ, μ˜€λ²„λΌμ΄λ”©(μž¬μ •μ˜)λ₯Ό κΌ­ ν•΄μ€˜μ•Ό ν•©λ‹ˆλ‹€.
    console.log("멍멍!");
  }
}

const dog1 = new Dog("μ΄ˆμ½”", 3);
const dog2 = new Dog("바둑이"); 

dog1.sound(); // 멍멍!


  • enum : 의미 μžˆλŠ” μ΄λ¦„μœΌλ‘œ μ •ν•΄μ§„ κ°’μ˜ 집합을 μ •μ˜ν•˜λŠ”λ° μ‚¬μš©λ©λ‹ˆλ‹€. 숫자 λ˜λŠ” λ¬Έμžμ—΄ 값을 직접 μ •μ˜ν•˜κ³  관리할 수 있으며, μ½”λ“œμ˜ 가독성, μ•ˆμ •μ„±, μžλ™μ™„μ„± νŽΈμ˜μ„±μ„ λ†’μ—¬μ€λ‹ˆλ‹€.
// κΈ°λ³Έ `eunm`
enum Weekday {
  Monday, // 아무 값도 μ§€μ •ν•˜μ§€ μ•ŠμœΌλ©΄ 0λΆ€ν„° 순차적으둜 μˆ«μžκ°€ ν• λ‹Ήλ©λ‹ˆλ‹€.
  Tuesday,
  Wednesday,
  Thursday,
  Friday,
  Saturday,
  Sunday,
}

let today: Weekday = Weekday.Tuesday;
console.log(today); //  1

// -----------------------------------------

// λ¬Έμžμ—΄ `enum`
enum Direction {
  Up = "UP",
  Down = "DOWN",
  Left = "LEFT",
  Right = "RIGHT",
}

let move: Direction = Direction.Left;
console.log(move); // LEFT


  • type : νƒ€μž…μ— 이름을 λΆ™μ—¬ μž¬μ‚¬μš©ν•  수 있게 ν•΄μ€λ‹ˆλ‹€. λ‹€μ–‘ν•œ ν˜•νƒœμ˜ νƒ€μž…μ„ ν‘œν˜„ν•  수 있으며, λ³΅μž‘ν•œ νƒ€μž…μ„ κ°„κ²°ν•˜κ²Œ κ΄€λ¦¬ν•˜κ³  가독성을 λ†’μ—¬μ€λ‹ˆλ‹€.
type Status = "loading" | "success" | "error";

let current: Status = "success";



클래슀 기반 객체 (Class-based OOP)

클래슀 (Class)

속성(ν”„λ‘œνΌν‹°)κ³Ό λ™μž‘(λ©”μ„œλ“œ)을 μ •μ˜ν•˜κ³ , 이λ₯Ό λ°”νƒ•μœΌλ‘œ new ν‚€μ›Œλ“œλ₯Ό 톡해 μ‹€μ œ 객체λ₯Ό λ§Œλ“€ 수 μžˆμŠ΅λ‹ˆλ‹€.

class Person {
  name: string;
  age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }

  greet(): void {
    console.log(`μ•ˆλ…•ν•˜μ„Έμš”, μ €λŠ” ${this.name}이고 ${this.age}μ‚΄μž…λ‹ˆλ‹€.`);
  }
}

const user = new Person("Alice", 30);
user.greet(); // μ•ˆλ…•ν•˜μ„Έμš”, μ €λŠ” Alice이고 30μ‚΄μž…λ‹ˆλ‹€.

getter / setter

클래슀 μ•ˆμ—μ„œ ν”„λ‘œνΌν‹°λ₯Ό μ•ˆμ „ν•˜κ²Œ 닀루기 μœ„ν•œ λ°©λ²•μœΌλ‘œ 직접 속성에 μ ‘κ·Όν•˜λŠ” λŒ€μ‹ , κ°„μ ‘μ μœΌλ‘œ 값을 μ½κ±°λ‚˜ μˆ˜μ •ν•  수 있게 ν•©λ‹ˆλ‹€.

class User {
  private _name: string;

  constructor(name: string) {
    this._name = name;
  }

  // getter
  get name(): string {
    return this._name;
  }

  // setter
  set name(newName: string) {
    if (newName.length < 2) {
      throw new Error("이름은 μ΅œμ†Œ 2자 이상이어야 ν•©λ‹ˆλ‹€.");
    }
    this._name = newName;
  }
}

const user = new User("Alice");
console.log(user.name); // "Alice"
user.name = "Bob";  

πŸ€” μ™œ public을 μ•ˆ μ“°κ³  μΌλΆ€λŸ¬ λ³΅μž‘ν•˜κ²Œ private + get/set을 μ“ΈκΉŒ?

1️⃣ 데이터에 직접 접근을 막고 μ œμ–΄ν•  수 있기 λ•Œλ¬Έμž…λ‹ˆλ‹€.

set에 λ„£μ—ˆλ˜ 쑰건으둜 μ΄μƒν•œ 값을 λ„£λŠ” 것을 λ°©μ§€ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

set empName(val: string) {
  if (val.length < 2) {
    throw new Error("이름은 두 κΈ€μž 이상이어야 ν•©λ‹ˆλ‹€!");
  }
  this._empName = val;
}

2️⃣ λ‚΄λΆ€ κ΅¬ν˜„μ„ λ°”κΎΈκΈ° 쉽기 λ•Œλ¬Έμž…λ‹ˆλ‹€.

_empName 말고 λ‹€λ₯Έ ν˜•νƒœλ‘œ λ°”κΏ€ λ•Œ μš©μ΄ν•©λ‹ˆλ‹€.

get empName() {
  return this._empName.toUpperCase(); 
}

✨ μƒμ„±μžμ—μ„œ λ°”λ‘œ μ„ μ–Έν•˜κ³  μ΄ˆκΈ°ν™”ν•˜λŠ” 방법

before

class Employee {
  private _empName: string;
  private _age: number;
  private _empJob: string;

  constructor(empName: string, age: number, empJob: string) {
    this._empName = empName;
    this._age = age;
    this._empJob = empJob;
  }
}
  • λ§€κ°œλ³€μˆ˜ 이름과 ν•„λ“œ 이름을 λ‹€λ₯΄κ²Œ μ§€μ •ν•  수 μžˆμ–΄μ„œ μœ μ—°ν•©λ‹ˆλ‹€
  • 더 λ³΅μž‘ν•œ 둜직이 μžˆλŠ” 경우 ( this._age = validateAge(age) ) μœ λ¦¬ν•©λ‹ˆλ‹€.

after

class Employee {
  constructor(
    private _empName: string,
    private _age: number,
    private _empJob: string
  ) {}
}
  • 반볡이 μ—†μ–΄μ„œ 가독성 μ’‹κ³  λΉ λ₯΄κ²Œ μž‘μ„± κ°€λŠ₯ν•©λ‹ˆλ‹€.



κ³ κΈ‰ νƒ€μž… κΈ°λŠ₯ (Advanced Concepts)

νƒ€μž… κ°€λ“œ (Type Guard)

λŸ°νƒ€μž„μ—μ„œ 더 μ •ν™•ν•œ νƒ€μž…μ„ μΆ”λ‘ ν•  수 있게 λ„μ™€μ£ΌλŠ” κΈ°λŠ₯μž…λ‹ˆλ‹€.

function printValue(value: string | number) {
  if (typeof value === "string") {
    console.log(value.toUpperCase()); // string으둜 μ’ν˜€μ§
  } else {
    console.log(value.toFixed(2)); // number둜 μ’ν˜€μ§
  }
}



✏️ 회고

C 계열 μ–Έμ–΄μ—μ„œ κ²½ν—˜ν–ˆλ˜ interfaceλ‚˜ enum κ°œλ… 덕뢄에, νƒ€μž…μŠ€ν¬λ¦½νŠΈ 문법이 훨씬 덜 μ–΄λ ΅κ²Œ λŠκ»΄μ‘Œλ‹€.

profile
🌱개발 기둝μž₯

0개의 λŒ“κΈ€