개발 slecs

daily-bible 모듈에 TypeScript strict 모드 타입

목차

daily-bible 모듈의 서버 응답 처리 부분에서 TypeScript strict 모드 빌드를 통과하기 위해 res.json() 반환값의 타입을 명시적으로 캐스팅했다.

strict 모드가 요구하는 타입 엄격함

팀에서 TypeScript strict 모드를 적용한 지 한참 되었는데, 이건 단순한 린팅 규칙이 아니라 런타임 에러를 사전에 차단하는 보험이다. 특히 서버와의 통신 부분은 그 중요성이 더 크다.

res.json()은 서버에서 돌아오는 응답 본문을 파싱하는 메서드인데, TypeScript 입장에선 그 결과의 구체적인 타입을 알 수 없다. 따라서 unknown으로 반환된다. strict 모드 이전에는 any로 취급하거나 암묵적으로 무시했을 법한 부분인데, strict 모드에서는 "이 unknown 값을 너가 명시적으로 안전하게 다루고 있는가?"를 강제한다.

unknown이 vs any의 차이

구분 any unknown
타입 검사 무시 필수
프로퍼티 접근 자유 타입 좁혀야 함
함수 호출 자유 타입 좁혀야 함
보안성 낮음 높음

any를 써버리면 "나중에 누군가 이 코드를 잘못 건드릴 때" 타입 체커가 아무 도움이 못 된다. unknown은 개발자에게 "니가 지금 뭘 받는 거고, 그걸 어떻게 쓸 건데?"를 명시적으로 구현하라고 강요한다. 이게 처음엔 번거로워 보이지만, 6개월 뒤 코드를 다시 읽을 때는 이 명시성이 얼마나 큰 자산인지 느껴진다.

API 응답 처리의 실무 패턴

일반적으로 서버 응답을 다룰 땐 이런 식으로 타입을 좁혀간다:

// ❌ 피하기
const data = await res.json(); // unknown
data.userId; // Error: unknown에는 userId가 없을 수도 있음

// ✅ 타입 가드 활용
const data = await res.json();
if (typeof data === 'object' && data !== null && 'userId' in data) {
  const userId = (data as { userId: string }).userId; // 이제 안전
}

// ✅ 스키마 검증 (더 강력)
const schema = z.object({ userId: z.string() });
const data = await res.json().then(d => schema.parse(d)); // 런타임 검증

실무에선 보통 zod, valibot 같은 스키마 라이브러리를 통해 서버 응답을 런타임에 검증하면서 동시에 타입을 확정 짓는다. 이 방식이 가장 안전한데, 애초에 서버가 보낸 형식과 클라이언트가 기대하는 형식의 괴리를 캐치할 수 있기 때문이다.

팀 차원의 타입 안전성과 배운 점

strict 모드 빌드를 필수로 두면서 느낀 가장 큰 효과는 PR 리뷰 때 타입 관련 이슈가 줄어들었다는 것이다. 예전엔 "이 값이 null일 수도 있지 않나?" 같은 코멘트가 왕왕 있었는데, 이제는 "빌드가 안 되니까" 애초에 그런 상황이 원천 차단된다.

또한 이런 변경이 누적되면서 코드베이스 전체의 신뢰도가 올라간다. 신입이 들어와서 기존 코드를 읽을 때도 "이 값이 undefined일 수 있나?" 같은 의문 없이 타입 정의를 믿고 진행할 수 있다.

다만 팀에서 strict 모드를 강제할 때는 점진적 마이그레이션이 중요했다. 처음부터 전체 코드를 strict로 돌리려고 했다면 저항이 심했을 텐데, 모듈 단위로 천천히 적용하면서 "아, 이게 왜 필요한데?"를 각자 깨닫게 했다. 이 fix도 그 과정의 일부고, 앞으로도 비슷한 작은 개선들이 계속 쌓여갈 거 같다.


🛒 이 글과 어울리는 추천 상품

*위 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.

댓글 0

첫 댓글 달아줘.