라떼군 이야기


TypeScript 5.6에서 'Buffer is not assignable to Uint8Array' 에러 원인과 해결 방법

Problem

Node.js 환경의 프로젝트에서 TypeScript 버전을 5.6 이상으로 업그레이드할 때, node_modules/@types/node 내부에서 갑작스러운 타입 에러가 발생하는 경우가 있습니다. 대표적으로 Interface 'Buffer' incorrectly extends interface 'Uint8Array' 또는 Type 'Buffer' does not satisfy the constraint 'ArrayBufferView'와 같은 에러 메시지가 출력됩니다. 에러의 세부 내용을 보면 entries(), reverse() 등의 메서드 반환 타입이 호환되지 않으며, map, filter 등의 속성이 누락되었다고 지적합니다. 프로젝트 코드를 전혀 수정하지 않았음에도 불구하고 컴파일이 실패하여 많은 개발자들이 당황하게 되는 흔한 시나리오입니다.

Background

이 문제가 발생하는 근본적인 원인은 최근 JavaScript(ECMAScript) 표준에 추가된 Iterator Helper 기능과 TypeScript 5.6의 타입 시스템 업데이트가 맞물렸기 때문입니다. 과거의 IterableIteratormap, filter와 같은 배열 메서드를 지원하지 않았으나, 최근 JS 표준에서는 Iterator.prototype에 이러한 유용한 메서드들을 내장하게 되었습니다. 이를 반영하기 위해 TypeScript 5.6은 내장 이터레이터 타입을 IteratorObjectArrayIterator라는 새로운 타입으로 업데이트했습니다.

하지만 기존의 @types/node 패키지에서 Buffer 인터페이스는 Uint8Array를 상속받으면서 entries() 메서드의 반환 타입을 구형인 IterableIterator로 하드코딩해두고 있었습니다. 결과적으로 TS 5.6 환경에서는 부모인 Uint8Array가 새로운 ArrayIterator를 반환하는데, 자식인 Buffer는 구형 타입을 반환하게 되어 타입 시그니처가 충돌(Diverge)하게 된 것입니다.

Solution

이 문제는 프로젝트의 코드를 수정할 필요 없이, Node.js의 타입 정의 파일인 @types/node를 최신 버전으로 업데이트하는 것만으로 간단히 해결할 수 있습니다. DefinitelyTyped의 메인테이너들이 이미 이 충돌 문제를 해결한 패치를 배포했기 때문입니다.

방법 1: @types/node 패키지 최신 버전으로 업데이트하기

가장 권장되는 방법은 현재 사용 중인 패키지 매니저를 통해 @types/node를 업데이트하는 것입니다.

# npm을 사용하는 경우
npm update @types/node --save-dev

# yarn을 사용하는 경우
yarn upgrade @types/node

# pnpm을 사용하는 경우
pnpm update @types/node

방법 2: 특정 Node.js 버전에 맞는 타입 설치하기

프로젝트에서 특정 버전의 Node.js를 타겟팅하고 있다면, 해당 메이저 버전에 맞는 최신 @types/node를 명시적으로 설치하는 것이 좋습니다.

# Node.js 18 버전을 사용하는 경우
npm install -D @types/node@18

# Node.js 20 버전을 사용하는 경우
npm install -D @types/node@20

# Node.js 22 버전을 사용하는 경우
npm install -D @types/node@22

package.json 확인

업데이트 후 package.json 파일에 반영된 버전을 확인하여, 캐시 문제로 인해 구버전이 설치되지 않았는지 체크합니다.

{
  "devDependencies": {
    // 버전 번호 앞에 ^ 기호가 있는지 확인하여 마이너/패치 업데이트가 허용되도록 합니다.
    "@types/node": "^20.14.0", 
    "typescript": "^5.6.2"
  }
}

Deep Dive

TypeScript 5.6에서 도입된 Iterator Helper 기능은 실제 프로덕션 환경에서 매우 유용하게 활용될 수 있습니다. 기존에는 제너레이터나 entries()가 반환하는 이터레이터를 다루기 위해 Array.from()을 사용해 배열로 변환한 뒤 map이나 filter를 적용해야 했습니다. 이는 큰 데이터셋을 다룰 때 불필요한 메모리 할당을 발생시켰습니다. 하지만 이제는 Iterator.from(...).filter(fn).map(fn) 형태로 지연 평가(Lazy Evaluation)를 수행할 수 있어 성능과 메모리 효율이 크게 향상됩니다.

또한, JavaScript 런타임에 새로 추가된 전역 객체인 Iterator와 TypeScript가 기존에 타입 체크용으로 가지고 있던 Iterator 타입 간의 이름 충돌(Name clash)을 피하기 위해, TypeScript 팀이 내부적으로 IteratorObject라는 새로운 타입을 도입했다는 점도 흥미로운 설계적 결정입니다.

Conclusion

TypeScript 5.6 업그레이드 시 발생하는 BufferUint8Array 간의 타입 에러는 JavaScript의 새로운 Iterator Helper 표준이 타입 시스템에 반영되면서 발생한 과도기적 문제입니다. 최신 버전의 @types/node로 업데이트하는 것만으로 완벽하게 해결할 수 있습니다. TypeScript 컴파일러 버전을 올릴 때는 항상 관련된 내장 객체의 타입 정의 패키지(@types/*)들도 함께 최신화하는 것을 베스트 프랙티스로 삼는 것이 좋습니다.

References

협업 및 후원 연락하기 →