본문으로 건너뛰기
피드

JS 번들 뚱뚱해지는 진짜 이유 3가지 - npm 의존성 다이어트 해야 할 때

frontend 약 6분
vote
0
댓글
북마크

npm 패키지 트리가 왜 이렇게 무거워졌는지 분석한 글. 레거시 엔진 지원, 원자적 패키지 설계, 오래된 ponyfill 세 가지가 주범임. e18e 커뮤니티가 이 문제를 해결하려고 열심히 삽질 중ㅋㅋ

  • 1

    레거시 엔진 지원·크로스-렐름 처리 등 극소수 요구사항이 전체 생태계 비용으로 전가됨

  • 2

    한 줄짜리 원자적 패키지들이 단일 소비자·중복·공급망 취약점 문제 일으킴

  • 3

    수년 전 이미 네이티브 지원된 기능의 ponyfill이 수천만 다운로드 유지 중

  • 레거시 엔진 지원: IE6급 구형 환경, 글로벌 네임스페이스 보호, 크로스-렐름(cross-realm) 이슈 때문에 is-string 같은 패키지가 생겨남
  • 원자적 아키텍처: path-key, is-wsl, slash 같은 한 줄짜리 패키지들이 남발되면서 의존성 트리가 개복잡해짐
  • Ponyfill 좀비화: globalThis, Array.prototype.indexOf 같이 이미 10년 전에 다 지원된 기능의 ponyfill이 아직도 수천만 다운로드 기록 중 ㄷㄷ
  • 결국 극소수를 위한 설계가 모든 개발자한테 비용을 전가하는 구조가 문제
  • knip, e18e CLI, npmgraph, module-replacements 프로젝트로 지금 당장 의존성 다이어트 가능

1. 레거시 런타임 지원 (크로스-렐름 & 안전성)

is-string, hasown 같은 패키지가 왜 존재하냐면 세 가지 이유가 있음:

① 구형 엔진 지원 Array.prototype.forEach, Object.keys조차 없는 ES3(IE6/7) 환경을 지원해야 하는 사람들이 있음. 뭐... 업그레이드하면 되지 않냐고 하고 싶지만ㅋㅋ

② 글로벌 네임스페이스 보호 Node.js 내부에서 쓰는 "primordials" 개념 - 누군가 Map을 재정의해도 Node 자체가 안 망가지도록 원본 참조를 따로 보관하는 방식. math-intrinsics 같은 패키지가 이 철학으로 만들어짐.

③ 크로스-렐름 값 처리 <iframe>에서 생성된 RegExp는 부모 페이지의 RegExp와 다른 클래스라 instanceof 체크가 실패함. 그래서 Object.prototype.toString.call(val)로 체크하는 패키지들이 생겨남. chai도 이 문제 있다고 함.

진짜 문제: 이런 니즈가 있는 사람은 극소수인데, 그 비용을 우리 모두가 내고 있는 거임 ㄹㅇ.


2. 원자적(Atomic) 아키텍처

"패키지는 최소 단위로 쪼개야 재사용 가능하다"는 철학으로 만들어진 패키지들:

  • arrify - 값을 배열로 변환 (Array.isArray(val) ? val : [val]... 이게 패키지임)
  • slash - 경로의 백슬래시를 /로 교체
  • path-key - 현재 플랫폼의 PATH 환경변수 키 반환 (Unix: PATH, Windows: Path)
  • is-wsl - WSL 환경 체크
  • is-windows - process.platform === 'win32' 체크

왜 문제냐:

  • 단일 소비자 문제: shebang-regexshebang-command에서만, onetimerestore-cursor에서만 씀. 인라인 코드랑 다를 게 없는데 npm 요청, tar 압축 해제, 대역폭 비용은 다 냄
  • 중복 버전 난무: nuxt 의존성 트리만 봐도 is-docker, is-stream, path-key 등이 버전 2개씩 들어있음 ㅋㅋ
  • 공급망 공격 표면 확대: 패키지 많을수록 해킹 포인트도 많아짐. 실제로 이 패키지들 관리자 한 명이 작년에 해킹당해서 수백 개 패키지가 동시에 털렸음 ㄷㄷ

3. 폰어필(Ponyfill)의 좀비화

Ponyfill이란? 환경을 오염시키지 않는 폴리필. import해서 쓰는 방식이라 라이브러리에서 써도 안전함.

문제는 이미 모든 엔진이 기능을 지원하는데도 아무도 제거를 안 했다는 것:

  • globalthis - 2019년부터 전 브라우저 지원, 지금도 주당 4,900만 다운로드
  • indexof - 2010년부터 지원, 주당 230만 다운로드
  • object.entries - 2017년부터 지원, 주당 3,500만 다운로드

그냥 아무도 안 지운 거임. 레전드급 관성ㅋㅋ


그래서 뭘 할 수 있냐

🔧 지금 당장 쓸 수 있는 도구들:

  • knip - 미사용 의존성, 데드코드 탐지
  • e18e CLI - analyze 모드로 대체 가능한 패키지 탐지 + migrate 명령어로 chalk → picocolors 같은 마이그레이션 자동화
  • npmgraph - 의존성 트리 시각화. ESLint의 find-up 브랜치처럼 고립된 비대 의존성 발견 가능
  • module-replacements - 네이티브 대체재나 더 나은 패키지 데이터셋. codemods도 제공

💡 할 수 있는 것:

  • 직접 의존성 보면서 "이게 왜 있지?" 질문하기
  • 불필요해 보이면 maintainer한테 issue 올리기
  • 문제 많은 패키지는 대안 찾기 (module-replacements 참고)

결국 JS 생태계는 '과거의 두려움'이 패키지로 결정화(crystallize)된 것ㅋㅋ 누군가 용감하게 PR 올려서 오래된 ponyfill 지워주는 게 진짜 오픈소스 기여인 시대가 온 듯.

댓글

댓글

댓글을 불러오는 중...

frontend

개인 웹사이트에 JSON-LD 넣는 법, 검색엔진과 크롤러가 내 사이트를 제대로 읽게 만들기

개인 웹사이트에 JSON-LD 구조화 데이터를 추가해 검색엔진과 크롤러가 사이트, 사람, 글, 프로젝트를 더 정확히 이해하게 만드는 실전 가이드야. WebSite, Person, ProfilePage, BlogPosting 같은 노드를 어떻게 연결하고 어느 페이지에 넣어야 하는지 예시 중심으로 설명해.

frontend

Deno, 웹 프로젝트를 데스크톱 앱으로 묶는 `deno desktop` 공개

Deno가 TypeScript 파일 하나부터 Next.js 앱까지 데스크톱 앱으로 패키징하는 `deno desktop`을 공개했다. 아직 안정 릴리스는 아니고 Deno v2.9.0 canary에서만 쓸 수 있지만, 운영체제 WebView 기반의 작은 바이너리, 프레임워크 자동 감지, 내장 자동 업데이트까지 한 번에 노린다.

frontend

파비콘 안에 웹사이트를 숨겨 넣은 개발자, 진짜 됨

한 개발자가 웹사이트의 파비콘 이미지를 작은 저장소처럼 사용해 HTML을 픽셀 RGB 값 안에 넣고, 브라우저에서 다시 읽어 렌더링하는 실험을 했다. 208바이트짜리 HTML payload에 4바이트 길이 헤더를 붙여 총 212바이트를 만들었고, 이를 9x9 픽셀 PNG 안에 87% 사용률로 저장했다.

frontend

스크린이 절대 못 보여주는 색은 어디에 있을까

이 글은 우리가 화면에서 보는 색이 인간이 볼 수 있는 색 전체가 아니라, sRGB와 Display-P3 같은 색역 안에 갇힌 일부라는 점을 파고든다. 특히 숲, 바닷속, 새와 나비의 구조색, 생물발광, 교통신호 LED 같은 실제 세계에는 모니터와 카메라가 제대로 담지 못하는 청록색과 녹색 계열이 꽤 많다는 얘기다. 디스플레이, 카메라, 조명, 렌더링을 다루는 개발자라면 “색상값 하나”가 생각보다 물리와 표준의 타협이라는 걸 체감하게 된다.

frontend

크롬, 매니페스트 버전 2 우회로까지 닫는다

구글 크롬이 매니페스트 버전 2 확장 지원을 사실상 최종 종료 단계로 밀어넣고 있다. 기존에는 플래그나 레지스트리 설정으로 유블록 오리진 같은 확장을 살리는 우회가 있었지만, 크로미움 150과 151을 거치며 그 우회 코드까지 제거되는 흐름이다.