TypeScript
TypeScript optional 런타임 데이터 불일치 디버깅
TypeScript 타입 정의와 실제 런타임 데이터가 다를 때 효과적으로 추적하는 방법.
TypeScript를 쓰는 가장 큰 장점은 빌드 타임에 타입 에러를 잡을 수 있다는 것이다. 하지만 가끔 타입 정의는 정확하지만, 실제 API 응답이 타입과 맞지 않을 때가 있다. 로컬에서는 괜찮았던 설정도 배포 환경에서는 다르게 보일 수 있다.
빌드 에러 확인
가장 먼저 해야 할 일은 빌드가 정말로 통과하는지 확인하는 것이다:
npm run build
npx tsc --noEmit
TypeScript 컴파일러가 에러를 보고하지 않는다면, 타입 정의 자체는 일관성이 있다는 뜻이다. 하지만 그렇다고 런타임 데이터가 타입과 일치한다는 보장은 없다.
타입 정의와 API 응답 비교
API 응답을 실제로 확인해봐야 한다. 브라우저 개발자 도구의 Network 탭에서 API 응답을 보면서 타입 정의와 비교하자.
interface User {
id: string;
name: string;
email?: string; // optional
profile?: {
bio: string;
avatar: string;
};
}
만약 실제 응답에서 profile이 항상 들어온다면 ?를 빼야 한다. 반대로 항상 없다면 profile 필드 자체를 제거해야 한다.
환경별 응답 확인
로컬과 운영 환경에서 API 응답이 다를 수 있다. 예를 들어:
- 로컬: 모든 필드가 있음
- 운영: 특정 필드는 권한이 없으면 빠짐
이런 경우를 대비하려면 각 환경에서 실제로 응답을 받아봐야 한다.
타입 가드 강화
optional 필드를 접근할 때는 항상 타입 가드를 해야 한다:
if (user.profile && user.profile.bio) {
console.log(user.profile.bio);
}
// 또는 옵셔널 체이닝 사용
console.log(user.profile?.bio);
타입 가드가 없으면 Cannot read property 'bio' of undefined 같은 런타임 에러가 난다.
실제 타입 검증하기
런타임에 실제 타입을 확인하는 방법도 있다:
function isUserValid(data: unknown): data is User {
if (typeof data !== 'object' || data === null) return false;
const obj = data as Record<string, unknown>;
return (
typeof obj.id === 'string' &&
typeof obj.name === 'string' &&
(obj.email === undefined || typeof obj.email === 'string')
);
}
const response = await fetchUser();
if (isUserValid(response)) {
// response의 타입이 안전하다
console.log(response.name);
} else {
console.error('Invalid user data:', response);
}
배포 후 검증
공개 URL에서 실제로 데이터가 제대로 렌더링되는지 여러 번 확인해야 한다:
- 로그인한 사용자로 접근했을 때
- 로그인하지 않은 사용자로 접근했을 때
- 특정 권한만 있는 사용자로 접근했을 때
각 경우에 화면이 제대로 보이고, 콘솔에 에러가 없는지 확인하자.
기록 남기기
나중에 비슷한 문제가 나올 때를 대비해서 현재 상태를 기록해두면 효율적이다. 어떤 환경에서 어떤 데이터가 다르게 들어왔는지, 그 결과 어떤 에러가 났는지를 간단하게라도 남겨두면 다음 추적이 훨씬 빠르다.