TypeScript 프로젝트에서 외부 라이브러리를 쓰다 보면, 타입 정의가 함께 제공되지 않는 패키지를 만나게 됩니다. 이때 가장 먼저 찾게 되는 것이 @types 패키지입니다.
npm에 @types/패키지명 형태로 올라와 있고, 설치만 하면 바로 타입이 붙습니다. 겉으로 보면 꽤 매끄럽습니다. 하지만 몇 번 이상 실제 운영 코드에 얹어 보면, 이 구조가 어떻게 유지되는지 궁금해지기 시작합니다.
DefinitelyTyped와 @types를 단순히 “설치해서 쓰는 법”이 아니라, 실무에서 겪는 문제와 관리 포인트를 중심으로 정리해 보았습니다.
@types 패키지는 어디에서 오는가
@types 패키지의 대부분은 DefinitelyTyped라는 저장소에서 관리됩니다. 라이브러리 작성자가 직접 제공하지 않는 타입 정의를, 커뮤니티가 별도로 관리하는 구조입니다.
예를 들어 lodash를 쓰는 경우, 다음과 같이 설치합니다.
npm install lodash
npm install -D @types/lodash
이후에는 import만으로 타입 추론이 자연스럽게 붙습니다. 이 단계에서는 큰 고민이 필요 없습니다.
버전이 어긋나기 시작하는 순간
문제는 라이브러리 버전과 @types 버전이 어긋날 때입니다.
- 라이브러리는 최신 버전인데 타입은 구버전
- 타입 정의는 추가됐는데 실제 구현은 없음
- 구현은 바뀌었는데 타입은 그대로
이 경우 타입은 맞는데 런타임이 깨지거나, 런타임은 정상인데 타입 에러가 나는 상황이 생깁니다. 문제는 이 원인이 @types에 있다는 걸 바로 떠올리기 어렵다는 점입니다.
타입 정의를 그대로 믿었을 때
커뮤니티가 관리하는 타입 정의는 기본적으로 신뢰할 만하지만, 항상 정확한 것은 아닙니다.
import legacy from 'legacy-lib';
legacy.doSomething('value');
타입상으로는 doSomething이 string을 받는다고 되어 있지만, 실제 구현에서는 객체를 기대하는 경우도 있습니다. 이런 상황에서는 타입 정의가 오히려 오해를 낳습니다.
타입을 로컬에서 덮어써야 하는 경우
@types 정의가 실제 구현과 맞지 않을 때는, 프로젝트 내부에서 타입을 보완해야 합니다.
// types/legacy-lib.d.ts
declare module 'legacy-lib' {
export function doSomething(input: {
value: string;
}): number;
}
이 방식은 임시 해결책으로는 충분합니다. 다만 외부 타입을 덮어쓰는 만큼, 팀 내부에서 그 이유를 공유하지 않으면 나중에 혼란이 생깁니다.
타입 정의를 직접 기여하는 경우
프로젝트 규모가 커지면, DefinitelyTyped에 직접 기여하는 경우도 생깁니다.
- 자주 쓰는 라이브러리인데 타입이 부정확한 경우
- 회사 내부 패키지를 외부에 공개한 경우
- 라이브러리 작성자가 타입을 제공하지 않는 경우
이 단계에서는 타입 정의가 단순 보조 수단이 아니라, 라이브러리의 일부로 다뤄집니다.
esModuleInterop와의 관계
@types를 쓰다 보면 import 구문과 관련된 문제도 자주 겪습니다.
import * as moment from 'moment';
혹은
import moment from 'moment';
이 차이는 tsconfig의 esModuleInterop 설정과 타입 정의 방식에 따라 달라집니다. 런타임과 타입 시스템의 해석이 어긋나는 대표적인 사례입니다.
운영에서 체감되는 차이
@types를 잘 관리한 프로젝트는 외부 라이브러리 사용이 비교적 안정적입니다. IDE 자동완성과 타입 추론이 자연스럽게 이어집니다.
반대로 버전 관리가 느슨하면, 다음과 같은 문제가 반복됩니다.
- 의미 없는 타입 에러가 갑자기 발생
- 라이브러리 업그레이드 후 예상치 못한 컴파일 오류
- 타입은 맞는데 런타임 오류 발생
이 경우 문제는 코드가 아니라, 타입 정의와 구현의 어긋남인 경우가 많습니다.
DefinitelyTyped와 @types는 TypeScript 생태계를 지탱하는 중요한 기반입니다. 직접 타입을 작성하지 않아도 대부분의 라이브러리를 안전하게 쓸 수 있게 해줍니다.
다만 이 타입 정의는 구현과 분리되어 관리됩니다. 버전, 설정, 선언 방식이 조금만 어긋나도 타입 시스템과 런타임이 서로 다른 이야기를 하게 됩니다.
@types는 설치로 끝나는 도구가 아니라, 프로젝트의 의존성 중 하나로 관리해야 하는 대상에 가깝습니다. 이 점을 인지하고 사용하는 편이 장기적으로 훨씬 안정적입니다.
'개발 > Typescript' 카테고리의 다른 글
| [TYPESCRIPT] 모노레포 환경에서 TypeScript를 관리하다 보면 보이는 경계들 (0) | 2026.02.22 |
|---|---|
| [TYPESCRIPT] 모듈 보강을 쓰게 되는 순간과 타입 경계가 흔들리는 지점들 (0) | 2026.02.21 |
| [TYPESCRIPT] Ambient Declaration을 직접 작성해야 하는 순간과 d.ts 파일이 남기는 영향 (0) | 2026.02.19 |
| [TYPESCRIPT] 선언 병합을 쓰게 되는 순간과 그 이후에 남는 것들 (0) | 2026.02.18 |
| [TYPESCRIPT] Extract와 ReturnType을 쓰다 보면 생기는 활용과 한계 (0) | 2026.02.17 |
