TypeScript
TypeScript 유니온 타입을 안전하게 변경하는 방법
기존 코드를 깨지 않으면서 타입을 확장하려면, 단계적 마이그레이션이 필수다.
TypeScript 타입을 변경할 때, 한 번에 여러 곳을 고치면 빌드가 망가진다. 유니온 타입이나 선택적 필드를 안전하게 변경하는 패턴을 정리했다.
변경 전 현재 타입 정의 문서화
뭘 바꾸려는지 명확히 정의해야 한다.
// Before
type Status = 'pending' | 'active' | 'inactive';
interface User {
id: string;
name: string;
status: Status;
}
이 타입을 확장해야 한다면 (예: 'archived' 상태 추가), 어디서 쓰이는지 먼저 찾아야 한다.
모든 사용처 찾기
grep -r "Status" src/
npm run build # 빌드 에러 메시지로 영향받는 파일 찾기
또는 IDE의 "Go to References" 기능을 쓴다. 타입이 얼마나 많은 곳에서 쓰이는지 파악해야 변경 계획을 세울 수 있다.
단계 1: optional/null 추가 (호환성 유지)
새 값을 추가하되, 처음엔 선택적으로 만든다.
type Status = 'pending' | 'active' | 'inactive' | 'archived';
interface User {
id: string;
name: string;
status: Status;
archivedAt?: Date; // 새 필드는 선택적
}
이 상태에서 빌드하면, 기존 코드는 문제없다. 새 필드를 사용하는 부분만 점진적으로 추가한다.
단계 2: 빌드하고 에러 확인
npx tsc --noEmit
npm run build
TypeScript가 타입 에러를 자동으로 찾아낸다. 에러가 있는 파일들을 하나씩 수정한다.
단계 3: 가드 로직 추가
타입이 바뀌면, 그에 맞게 처리 로직도 수정해야 한다.
function handleStatus(user: User) {
switch (user.status) {
case 'pending':
// ...
case 'active':
// ...
case 'inactive':
// ...
case 'archived':
// 새로 추가된 케이스
console.log('User archived at', user.archivedAt);
break;
}
}
모든 switch 문이 새로운 케이스를 처리하는지 확인한다.
단계 4: API 응답 처리
Backend API가 새 필드를 반환하기 시작했는가? 확인해보자.
curl -s 'https://api.example.com/users/1' | jq '.status'
백엔드가 아직 구 버전이라면, 프론트엔드에서는 새 상태를 받지 못한다. 이 경우 호환성 레이어를 만든다.
function mapBackendStatus(status: string): Status {
if (status === 'archive') return 'archived'; // 백엔드 구 버전 호환
return status as Status;
}
단계 5: 테스트로 모든 케이스 검증
각 상태에서 앱이 정상 동작하는지 테스트한다.
describe('User Status', () => {
test('archived users should show archive date', () => {
const user: User = {
id: '1',
name: 'John',
status: 'archived',
archivedAt: new Date('2024-06-29'),
};
expect(user.archivedAt).toBeDefined();
});
});
단계 6: 배포하고 모니터링
변경사항이 배포된 후, 실제 데이터가 올바르게 처리되는지 모니터링한다.
- 에러 로그에 "unknown status" 같은 메시지가 없는가?
- 새 상태의 사용자들이 정상으로 표시되는가?