본문 바로가기
TypeScript

[타입스크립트 프로그래밍] 7장 에러처리

by 복숭아 우유씨 2023. 6. 7.

* 타입스크립트 프로그래밍(보리스 체르니, 프로그래밍 인사이트)를 읽고 요약 정리한 글입니다.

 

타입스크립트 에러 표현/처리의 일반적인 패턴 4가지

1) null 반환

2) 예외 던지기

3) 예외 반환

4) Option 타입

 

7.1 null 반환

- 타입 안정성을 유지하면서 에러를 처리하는 가장 간단한 방법이다.

- 입력의 유효성을 검사하여 유효하지 않은 경우 null을 반환하고, 타입 시스템에서 모든 상황이 처리되는지를 확인한다.

function ask() {
  return prompt("When is your birthday?");
}

function parse(birthday: string): Date | null {
  let date = new Date(birthday);
  if (!isValid(date)) {
    return null; // 입력이 유효하지 않으면 null을 반환한다.
  }
  return new Date(birthday);
}

// 입력한 날짜가 유효한지 검사하는 함수
function isValid(date: Date) {
  return (
    Object.prototype.toString.call(date) === "[object Date]" &&
    !Number.isNaN(date.getTime())
  );
}

let date = parse(ask());
// 결과가 null인지 확인 후 결과를 사용한다.
if (date) {
  console.info("Date is", date.toISOString());
} else {
  console.error("Error parsing date for some reason");
}

 

문제점

  • 문제가 생긴 원인 알 수 없어서 로그 확인하면서 디버깅해야 함
  • 모호한 에러메시지
  • 조합이 어려워짐 (모든 연산에서 null을 확인해야 하므로 연산 중첩, 연결시 코드가 지저분해짐)

 

7.2 예외 던지기

문제 발생시 예외를 던지는 방법

 

장점

  • 어떤 문제인지에 따라 대체 가능할 수 있음
  • 디버깅에 도움되는 메타데이터 얻을 수 있음
  • 커스템 에러 이용하여 문제를 구체적으로 알리고, 문제 원인도 설명할 수 있음 (여러 동작을 하나의 try/catch문으로 감싸서 연쇄적이고 중첩된 동작을 효율적으로 만들 수 있음)
 

유의할 점 및 응용

- 전체 응용프로그램이 크래시 되지 않도록 주의 필요(try-catch문 사용)

- 다른 에러 발생시 무시하지 않고 다시 던지는 것이 좋다.

// ...

function parse(birthday: string): Date | null {
  let date = new Date(birthday);
  if (!isValid(date)) {
    throw new RangeError("Enter a date in the form YYYY/MM/DD"); // 문제 발생시 에러를 던짐
  }
  return new Date(birthday);
}

// ...

// 에러를 던질때 전체 응용프로그램이 크래시 되지 않도록 try-catch 사용
try {
  let date = parse(ask());
  console.info("Date is", date.toISOString());
} catch (e) {
  if (e instanceof RangeError) {
    console.error(e.message);
  } else {
    throw e; // 다른 에러 발생시 무시하지 않고 다시 던지도록 함
  }
}

- 추후 서브클래싱을 통해 추가한 에러 구분 가능
   : 커스텀 에러 이용 -> 문제 구체화 가능, 문제 생긴 이유 설명 가능

 
// 커스텀 에러 타입
class InvaliddateFormatError extends RangeError {}
class DateIsInTheFutureError extends RangeError {}

function parse(birthday: string): Date | null {
  let date = new Date(birthday);
  if (!isValid(date)) {
    throw new InvaliddateFormatError("Enter a date in the form YYYY/MM/DD");
  }
  if (date.getTime() > Date.now()) {
    throw new DateIsInTheFutureError("Are you a timelord?");
  }
  return new Date(birthday);
}

try {
  let date = parse(ask());
  console.info("Date is", date.toISOString());
} catch (e) {
  if (e instanceof InvaliddateFormatError) {
    console.error(e.message);
  } else if (e instanceof DateIsInTheFutureError) {
    console.info(e.message);
  } else {
    throw e;
  }
}

 

7.3 예외 반환

- 타입스크립트는 예외를 함수의 시그니처로 취급하지 않으므로, 특정 타입의 에러를 처리하기 위한 별도의 방법이 필요하다.

- 타입스크립트는 throws문을 지원하지 않지만 유니온 타입을 이용해서 흉내낼 수 있다.

   - 함수 시그니처에 발생할 수 있는 예외 나열, 메서드 사용자에게 발생 가능한 에러 전달, 메서드 사용자가 각각의 에러를 모두 처리하거나 다시 던지도록 강제하

// ...

function parse(
  birthday: string
): Date | InvaliddateFormatError | DateIsInTheFutureError {
  let date = new Date(birthday);
  if (!isValid(date)) {
    throw new InvaliddateFormatError("Enter a date in the form YYYY/MM/DD");
  }
  if (date.getTime() > Date.now()) {
    throw new DateIsInTheFutureError("Are you a timelord?");
  }
  return new Date(birthday);
}

// ...

let result = parse(ask());
if (result instanceof InvaliddateFormatError) {
  console.error(result.message);
} else if (result instanceof DateIsInTheFutureError) {
  console.info(result.message);
} else {
  console.info("Date is", result.toISOString());
}

//좀더 간단한 방법
let result = parse(ask());
if (result instanceof Error) {
  console.error(result.message);
} else {
  console.info("Date is", result.toISOString());
}

장점

- 단순, 자료구조 사용하지 않음

단점

- 에러를 던지는 연산을 연쇄적으로 호출할 경우, 코드가 지저분해짐

 

7.4 Option 타입

 

특수 목적 데이터 타입을 사용해 예외를 표현하는 방법

: Try, Option, Either 타입 등

- JS에서 기본 제공되지 않으므로 설치나 구현 필요

  장점

   - 에러 발생할 수 있는 계산에 여러 연산을 연쇄적으로 수행할 수 있음

  단점

   - 이런 데이터 타입을 사용하지 않는 다른 코드와 호환되지 않음

 

Option  타입

핵심 개념

어떤 특정 값을 반환하는 대신 컨테이너를 반환한다.

  - 컨테이너

    - 값을 포함할수도 있고, 포함하지 않을 수도 있다.

    - 컨테이너는 자체 제공 메서드가 있으며, 이를 이용해 연쇄 연산을 수행할 수 있다.

    - 값을 포함할 수 있다면, 어떤 자료구조로도 컨테이너를 구현할 수 있다.

 

정의

interface Option<T> {}

class Some<T> implements Option<T> {
  constructor(private value: T) {}
}

class None implements Option<never> {}

1) Some<T>와  None이 구현하게 될 인터페이스 이다.

  - Some<T>, None: Option의 한 형태인 클래스

    - Some<T> : T라는 값을 포함하는 Option, 연산에 성공하여 값이 만들어진 상황을 나타내며 결괏값을 포함함.

    - None: 값이 없는 Option, 연산에 실패한 상황을 나타냄.

2) 타입이기도 하고 함수이기도 하다.

  - 타입 관점: Some과 None의 슈퍼타입

  - 함수 관점: Option 타입의 새 값을 만드는 기능

 

장점

- 성공하거나 실패할 수 있는 동작을 연쇄적으로 수행할 때 유리함. 

- 타입 안정성 제공

- 타입 시스템을 통해 연산이 실패할 수 있음을 알려줄 수 있음

- 오버로드로 기능 추가시 더 다른 언어로 표현할 수 없는 일도 할 수 있음.

- 호출 시그니처(Some, None으로만 제한)를 활용하여 코드 안전하게 만들 수 있음

 

단점

- 에러 발생 이유를 알려주지는 않음

- Option을 사용하지 않는 다른 코드와는 호환되지 않음

 

 

 

 


Reference

1) 타입스크립트 프로그래밍,보리스 체르니, 프로그래밍 인사이트

댓글