Typescript의 런타임 타입 불일치 문제

rabbit jack·2025년 6월 26일

Typescript

목록 보기
4/5

Javascript와 달리 Typescripttype을 지정할 수 있다.

export interface ReviewDTO {
  id: number;
  rating: number;    
  content: string;
  movie_id: number;
  nickname: string;
  is_liked: boolean;
  likes_count: number;
  comments_count: number;
}

하지만 위에서 설정한 type만을 믿고 안전하다 생각하며 사용해선 안된다.

실행시점에는 지정된 것과 다른 type의 데이터가 들어올 수 있기 때문이다.

Typescript가 런타임 타입을 보장하지 못하는 이유

TypeScript로 작성된 코드는 Javascript로 변환된 후 실행되기 때문이다.

즉, 컴파일 시점 까지의 type 안전성만 보존하며, 실행시점에서의 type을 명확히 보장하진 않는다.

가령 서버로 부터 전송되는 데이터의 타입은 런타임중에 결정되므로, 이때의 타입까지 보장해주진 못한다.

특히, 백엔드가 DB에서 가져온 데이터를 그대로 DTO에 담아 프론트로 전송할때, type이 보장되지 않는 사고가 발생하기 쉽다.

가령TypeOMRCount(*) 또는 COALESCE(...)로 만들어진 값들은 직관적으로 정수라고 생각하기 쉬운데, 실은 SQL 문자열(string)으로 처리되는 경우가 많다.

'COALESCE(like_summary.like_count, 0) AS likes_count',
'COALESCE(comment_summary.comment_count, 0) AS comments_count',

만일 해당 명령로 얻은 값을 파싱하지 않고 그대로 넘겨준다면, DTO에서 정의한것과 다른 타입의 데이터가 들어가게 되는 셈이다.

해결법

  1. 쿼리에서 타입 강제하기
CAST(COALESCE(like_summary.like_count, 0) AS UNSIGNED) AS likes_count

하지만 위 방법은 DBMS의 종류에 따라 결과값을 보장하지 못한다는 단점이 있다.

  1. TS에서 직접 파싱하기
class ReviewDto {
  likes_count: number;
  comments_count: number;

  static fromRaw(raw: any): ReviewDto {
    return {
      ...raw,
      likes_count: Number(raw.likes_count),
      comments_count: Number(raw.comments_count),
    };
  }
}

다소 번거롭더라도 위와 같은 방법을 쓰는것이 안전하다.

결론

  1. Typescript에서 Type을 보장하는것은 컴파일 단계 까지이며, 런타임에는 Javascript와 마찬가지로 Type을 보장하지 않는다.

  2. DB나 외부 저장소 및 서비스 등에서 읽어온 데이터는 수신측에서도 type이 일치하는지 재차 검토해야한다

  1. 백엔드->프론트로 보내는 데이터 뿐만 아니라, 프론트->백엔드로 보내는 데이터 또한 런타임중의 type을 보장할 수 없으므로, validator pipe를 쓰는등의 대책을 적용해야한다.

0개의 댓글