[우아한 타입스크립트(with 리액트)] - 2장. 타입 (2.1 ~ 2.2)

Rachel·2024년 3월 21일
0
post-thumbnail

우아한형제들 웹프론트엔드개발그룹, 『우아한 타입스크립트 with 리액트』, 한빛미디어(2023) p.34 ~ 65

타입이란

✔️ 자료형으로서의 타입

변수: 값을 저장할 수 있는 공간이자 값을 가리키는 상징적인 이름

🌟 자바스크립트 7자기 데이터 타입(자료형)

  • undefined
  • null
  • Boolean
  • String
  • Symbol
  • Numeric(Number와 BigInt)
  • Object

데이터 타입: 여러 종류의 데이터를 식별하는 분류 체계로 컴파일러에 값의 형태를 알려준다.

메모리에 저장된 값을 데이터 타입으로 설명할 수 있으며 모든 데이터를 해석할 때 데이터 타입 체계가 사용된다.

집합으로서의 타입

프로그래밍에서의 타입은 수학의 집합과 유사.
타입 = 값이 가질 수 있는 유효한 범위의 집합

타입 시스템 -> 코드에서 사용되는 유효한 값의 범위를 제한해서 런타임에서 발생할 수 있는 유효하지 않은 값에 대한 에러를 방지해준다.

정적 타입과 동적 타입

정적 타입: 모든 변수의 타입이 컴파일타임에 결정됨. 코드 수준에서 개발자가 타입을 명시해줘야 하는 C, 자바, 타입스크립트 등이 정적 타입 언어에 속한다.

동적 타입: 변수 타입이 런타임에서 결정됨. 파이썬, 자바스크립트가 대표적인 동적 타입 언어로 개발자가 직접 타입을 정의해줄 필요가 없다.

✏️ 컴파일타임과 런타임

  • 컴파일타임: 기계(컴퓨터, 엔진)가 소스코드를 이해할 수 있도록 기계어로 변환되는 시점.
  • 런타임: 변환된 파일이 메모리에 적재되어 실행되는 시점.

강타입과 약타입

타입이 결정되는 시점은 다르지만 모든 프로그래밍 언어에는 값의 타입이 존재.

암묵적 타입 변환(Implicit coercion conversion): 개발자가 의도적으로 타입을 명시하거나 바꾸지 않았는데도 컴파일러 또는 엔진 등에 의해서 런타임에 타입이 자동으로 변경되는 것

암묵적 타입 변환 여부에 따라 타입 시스템을 강타입(strongly type)과 약타입(weakly type)으로 분류할 수 있다.

  • 강타입 특징을 가진 언어에서는 서로 다른 타입을 갖는 값끼리 연산을 시도하면 컴파일러 또는 인터프리터에서 에러가 발생. ex_파이썬, 루비, 타입스크립트
print('2' - 1)

TypeError: unsupported operand type(s) for -: 'str' and 'int'

  • 약타입 특징을 갖는 언어에서 서로 다른 타입을 갖는 값끼리 연산할 때는 컴파일러 또는 인터프리터가 내부적으로 판단해서 특정 값의 타입을 변환하여 연산을 수행한 후 값을 도출한다. ex_자바스크립트, C++, 자바
class Main {
    public static void main(String[] args) {
        System.out.printIn('2', -1);
    }
}

Main 49

문자열 '2'를 내부적으로 숫자 타입인 아스키 값 50으로 변환해 빼기 연산 진행

👉 아스키 코드
개발 공부하면서 자주 나오는 아스키 코드(ASCII_American Standard Code for Information Interchange, 영문 키보드로 입력할 수 있는 모든 기호들이 할당되어 있는 가장 기본적인 부호 체계)
A는 65, a는 97정도는 알고 있자!

📌 타입의 필요성

자바스크립트는 약타입 언어이기 때문에 런타임에서 발생할 수 있는 에러를 예측하고 방지하는 코드를 작성하는 것이 필요하다.

타입 안정성: 타입을 사용해서 프로그램이 유효하지 않은 작업을 수행하지 않도록 방지하는 것
타입 시스템: 타입 검사기가 프로그램에 타입을 할당하는 데 사용하는 규칙 집합
-> 어떤 타입을 사용하는지를 컴파일러에 명시적으로 알려줘야 하는 타입 시스템이 있고, 자동으로 타입을 추론하는 타입 시스템도 있다.

컴파일 방식

컴파일: 사람이 이해할 수 있는 방식으로 작성한 코드를 컴퓨터가 이해할 수 있는 기계어로 바꿔주는 과정
즉 개발자가 자바, C# 등의 고수준 언어로 소스코드를 작성하면, 컴파일러는 컴퓨터가 해석할 수 있는 바이너리 코드(0, 1)로 변환한다. 기본적으로 서로 다른 수준(고수준-저수준) 간의 코드 변환을 의미.

타입스크립트의 컴파일 결과물 -> 자바스크립트

타입스크립트의 타입 시스템

타입 애너테이션 방식(type annotation)

변수나 상수 혹은 함수의 인자와 반환 값에 타입을 명시적으로 선언해서 어떤 타입 값이 저장될 것인지를 컴파일러에 직접 알려주는 문법.

타입스크립트에서는 변수 이름 뒤에 :type 구문을 붙여 데이터 타입을 명시해준다.

let color: string = "yellow";

구조적 타이핑(Structural type system)

이름으로 타입을 구분하는 명목적인 타입 언어의 특징과 달리 타입스크립트는 구조로 타입을 구분한다.

구조적 서브타이핑(Structural Subtyping)

타입 = 값의 집합(set of value), 타입은 단지 집합에 포함되는 값이고 특정 값은 많은 집합에 포함될 수 있다.

타입이 값의 집합이라고 생각하니 제일 잘 이해가 간다!
예를 들어 string or number or null 여러 타입을 동시에 가질 수 있다.

구조적 서브타이핑
🌟 객체가 가지고 있는 속성(프로퍼티)을 바탕으로 타입을 구분하는 것.
이름이 다른 객체라도 가진 속성이 동일하다면 타입스크립트는 서로 호환이 가능한 동일한 타입이라고 여긴다.

interface Pet {
  name: string;
}

interface Cat {
  name: string;
  age: number;
}

let pet: Pet;
let cat: Cat = { name: "miyang", age: 2 };

pet = cat; // ✅
// cat = pet ❌ 'age' 속성이 'Pet' 형식에는 없지만 'Cat' 형식에서 필수입니다.

console.log(pet); // { name: 'miyang', age: 2 }
console.log(cat); // { name: 'miyang', age: 2 }

Cat과 Pet은 다른 타입으로 선언되었지만 Pet이 가지고 있는 name이라는 속성을 가지고 있으므로 Cat 타입으로 선언한 cat을 Pet 타입으로 선언한 pet에 할당 가능!

  • 구조적 서브타이핑은 함수의 매개변수에도 적용된다.

  • 타입스크립트의 서브타이핑, 즉 타입의 상속 역시 구조적 타이핑을 기반으로 하고 있다.

-> 서로 다른 두 타입 간의 호환성은 오로지 타입 내부의 구조에 의해 결정된다. 타입 A가 타입 B의 서브 타입이라면 A 타입의 인스턴스는 B 타입이 필요한 곳에 언제든지 위치할 수 있다. 즉, 타입이 계층 구조로부터 자유롭다.

자바스크립트를 닮은 타입스크립트

명목적 타이핑(nominal typing)과 대조적(C++, 자바) -> 각 클래스 안 속성이 동일해도 각 클래스로 생성한 인스턴스는 서로 호환 x. 구조가 같더라도 이름이 다르다면 다른 타입으로 취급.

자바스크립트는 본질적으로 덕 타이핑을 기반으로 함.

덕 타이핑(duck typing): 어떤 함수의 매개변숫값이 올바르게 주어진다면 그 값이 어떻게 만들어졌는지 신경 쓰지 않고 사용하는 개념.
어떤 타입에 부합하는 함수와 메서드를 가질 경우 해당 타입에 속하는 것으로 간주하는 방식.
'만약 어떤 새가 오리처럼 걷고, 헤엄치며 꽥꽥거리는 소리를 낸다면 나는 그 새를 오리라고 부를거야!'

-> 타입스크립트는 이런 동작을 그대로 모델링. 객체나 함수가 가진 구조적 특징을 기반으로 타이핑하는 방식 택함.

📌 타입스크립트는 객체 간 속성이 동일하다면 서로 호환되는 구조적 타입 시스템을 제공하여 더욱 편리성을 높였다!
쉬운 사용성 + 안전성 두 가지 목표 사이의 균형 중시.

✏️ 덕 타이핑과 구조적 타이핑의 차이

  • 덕 타이핑은 런타임에 타입 검사, 구조적 타이핑은 컴파일타임에 타입체커가 타입을 검사.

구조적 타이핑의 결과

구조적 타이핑의 예기치 못한 결과가 발생할 수는 있는 한계를 극복하고자 명목적 타이핑 언어의 특징을 가미한 식별할 수 있는 유니온(Discriminated Unions)같은 방법이 생겨남.

타입스크립트의 점진적 타입 확인

타입스크립트는 점진적으로 타입을 확인하는 언어(gradually typed)

✏️ 점진적 타입 검사
컴파일 타임에 타입을 검사하면서 필요에 따라 타입 선언 생략을 허용하는 방식.
타입을 지정한 변수와 표현식은 정적으로 타입을 검사하지만 타입 선언이 생략되면 동적으로 검사를 수행한다.

👉 프로젝트 진행 시에 의문이었던 부분.
모든 타입을 지정해주는게 좋은건가?

const [name, setName] = useState<string>("");
const [name, setName] = useState("");

예를 들어 이렇게 두 가지로 작성할 수 있는데 내가 찾아본 바로는 이런 누가 봐도 알 수 있는 명확한 타입은 굳이 항상 명시해주지 않아도 되고, 명확하게 타입을 작성하면 좋은 부분에는 명시해주는게 좋다고 한다.

any 타입
타입스크립트 내 모든 타입의 종류를 포함하는 가장 상위 타입으로 모든 타입 값이든 할당할 수 있다.
단, 타입스크립트 컴파일 옵션인 noImplicitAny는 타입 애너테이션이 없을 때 변수가 any 타입으로 추론되는 것을 허락하지 x
-> 정확한 타이핑을 위해 tsconfig의 noImplicitAny 옵션 true로 설정하는게 좋다.

자바스크립트 슈퍼셋으로서의 타입 스크립트

타입스크립트 ⊃ 자바스크립트

값 vs 타입

값(value): 프로그램이 처리하기 위해 메모리에 저장하는 모든 데이터. 프로그램에서 조작하고 다룰 수 있는 어떤 표현이며 다양한 형태의 데이터를 포함.

값 공간과 타입 공간의 이름은 서로 충돌하지 않기 때문에 타입과 변수를 같은 이름으로 정의할 수 있는데 타입스크립트가 자바스크립트의 슈퍼셋인 것과 관련이 있다. 타입스크립트 문법인 type으로 선언한 내용은 자바스크립트의 런타임에서 제거되기 때문에 값 공간과 타입 공간은 서로 충돌하지 않는다.

  • 타입은 주로 타입 선언(:), 단언 문(as)로 작성하고 값은 할당 연산자인 = 으로 작성

  • 함수의 매개변수처럼 여러 개의 심볼이 함께 쓰인다면 타입과 값을 명확하게 구분해야 한다.

function email({person, subject, body}: {person: Person; subject: string; body: string;}) {...}

🚨 값과 타입 공간에 동시에 존재하는 심볼도 있다 -> class와 enum

ES6에서 등장한 클래스객체 인스턴스를 더욱 쉽게 생성하기 위한 문법 기능(syntactic sugar)으로 실제 기능은 함수와 같다.

class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}

const rect1 = new Rectangle(5, 4);
  • 동시에 클래스는 타입으로도 사용된다. 즉 타입스크립트 코드에서 클래스는 값과 타입 공간 모두에 포함될 수 있다.
class Developer {
  name: string;
  domain: string;

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

const me: Developer = new Developer("zig", "frontend");

변수명 me 뒤에는 타입, new 키워드 뒤에는 클래스의 생성자 함수인 값으로 동작.

클래스와 마찬가지로 타입스크립트 문법인 enum 역시 런타임에 객체로 변환되는 값.

  • enum은 실제로 객체에 존재하며, 함수로 표현될 수 있다.

  • 클래스처럼 타입 공간에서 타입을 제한하는 역할을 하지만 자바스크립트 런타임에서 실제 값으로도 사용될 수 있다.

enum이 타입으로 사용된 예시

enum WeekDays {
  MON = "Mon",
  TUES = "Tues",
  WEDNES = "Wednes",
  THURS = "Thurs",
  FRI = "Fri",
}

type WeekDaysKey = keyof typeof WeekDays;
function printDay(key: WeekDaysKey, message: string) {
  const day = WeekDays[key];
  if (day <= WeekDays.WEDNES) {
    console.log(`It's still ${day}day, ${message}`);
  }
}
printDay("TUES", "wanna go home");

위 예시는 출력 문장이 항상 동일해 아래처럼 바꿔보았다.

enum WeekDays {
  MON = "Mon",
  TUES = "Tues",
  WEDNES = "Wednes",
  THURS = "Thurs",
  FRI = "Fri",
}

type WeekDaysKey = keyof typeof WeekDays;
function printDay(key: WeekDaysKey, message: string) {
  const day = WeekDays[key];
  if (day != WeekDays.FRI) {
    console.log(`It's still ${day}day, ${message}`);
  } else {
    console.log(`It's finally Friday 🥳`);
  }
}
printDay("THURS", "wanna go home"); // It's still Thursday, wanna go home
printDay("FRI", "wanna go home"); // It's finally Friday 🥳

enum이 값 공간에서 사용된 예시

enum MyColors {
  BLUE = "#0000FF",
  YELLOW = "#FFFF00",
  MINT = "#2AC1BC",
}

function whatMintColor(palette: { MINT: string }) {
  return palette.MINT;
}

whatMintColor(MyColors);

일반적인 객체처럼 동작.

🌟 타입스크립트에서 자바스크립트의 키워드가 해석되는 방식

키워드타입
classYY
const, let, varYN
enumYY
functionYN
interfaceNY
typeNY
namespaceYN

✔️ 트리쉐이킹(tree-shaking)
자바스크립트, 타입스크립트에서 사용하지 않는 코드를 삭제하는 방식.
나무를 흔들면 죽은 나뭇잎이 떨어지는 모습을 보고 이름을 따옴.
ES6 이후의 최신 애플리케이션 개발 환경에서는 웹팩, 롤업같은 모듈 번들러를 사용한다.
이러한 도구로 번들링 작업을 수행할 때 사용하지 않는 코드는 자동으로 삭제됨.

타입을 확인하는 방법

typeof, instanceof, 타입 단언(as)을 사용해 타입 확인.

typeof는 연산하기 전에 피연산자의 데이터 타입을 나타내는 문자열을 반환
typeof 연산자가 반환하는 값: Boolean, null, undefined, Number, BigInt, String, Symbol, Function, 호스트 객체, object 객체

자바스크립트에서 instanceof 연산자를 사용하면 프로토타입 체이닝 어딘가에 생성자의 프로토타입 속성이 존재하는지 판단할 수 있다.


읽어주셔서 감사합니다. 잘못된 내용이나 추가로 알면 좋은 내용 있으면 댓글로 남겨주세요 🙂

profile
기존 블로그: https://hi-rachel.tistory.com

0개의 댓글