logo개발이 재밌는 날
npm

npm Deep Dive 스터디, 1장

posted by mia · 2025-07-06

1장. npm과 유의적 버전

1.1 npm의 정의와 역사

1.1.1 npm의 역사와 배경

오해와 진실

  • npm을 흔히 ‘Node Package Manager’의 약자로 알고 있지만 이는 사실과 다르다.
  • npm의 전신은 다양한 bash 유틸리티인 pm이며, 이 pm은 ‘pkgmakeinst’의 약자다.
  • npm은 이 pm을 이어받아 진화시킨 하나의 프로젝트다.
  • 굳이 약자로 소개한다면 ’Node pm’ 또는 ‘Node pkgmakeinst’가 옳다고 한다.

💡 많은 블로그, 아티클에서 node package manager로 소개되고 있고 의심해본 적 없던 사실인데 사실 아니라는 이야기에 놀랐고, 흥미로웠음

흥미로웠던 이야기

  • Node.js는 2009년에 등장했다
  • npm은 처름 만들어졌을 때부터 2013년까지 임시로 만들어진 인프라 위에 구축되어 최초 구조에서는 이러한 인기를 미쳐 예상하지 못했던 탓인지 많은 트래픽을 감당하지 못해 장시간 다운되는 일이 발생하곤 했다
  • 2020년 마이크로소프트가 인수한 깃허브가 npm을 인수했다

같이 알아보면 좋을 개념

  • CDN (Content Delivery Network)
  • CDN은 웹사이트나 애플리케이션의 콘텐츠(이미지, CSS, JavaScript 파일 등)를 사용자에게 더 빠르고 효율적으로 전달하기 위한 분산 서버 네트워크이다. 콘텐츠를 전 세계 곳곳에 분산된 서버(엣지 서버)에 저장하여, 사용자와 가장 가까운 서버에서 콘텐츠를 전송함으로써 웹사이트 로딩 속도를 획기적으로 단축한다
  • 프론트엔드나 웹에서 사용하던 리소스는 과거에는 CDN에 업로드하는 형태였습니다. npm과 같은 패키지 매니저가 보편화되기 전에는, jQuery나 Bootstrap 같은 라이브러리를 CDN에서 직접 불러와 사용하는 방식이 흔했다고 함.

1.1.2 npm의 주요 기능

핵심 요약

  • 패키지 설치 및 관리: 가장 기본 기능. package.json의 dependencies와 devDependencies에 선언된 패키지를 node_modules에 설치해서 Node.js 기반 프로젝트에서 사용할 수 있게 해준다. 이외에도 오래된 패키지 업데이트, 보안 취약점이 있는 패키지 확인, 의존성 임의 수정등이 가능하다.
  • 패키지 배포 및 공유: npm 레지스트리는 자바스크립트 개발자가 만든 자바스크립트 패키지를 업로드 할 수 있는 오픈소스 생태계 최대 규모의 레지스크리 서비스다. 유료 서비스를 이용하면 특정 사용자들만 다운로드 할 수 있는 사설(private) 패키기도 업로드 할 수 있다.
  • 스크립트 실행: package.json의 scripts 필드를 사용해서 원하는 스크립트를 실행할 수 있게 해준다. 해당 프로젝트에 자주 사용되는 복잡한 명령어를 간단한 CLI로 실행할 수 있다.
  • npmjs.com: npm에 업로드돼 있는 패키지에 대한 정보를 손쉽게 확인할 수 있는 사이트

같이 알아두면 좋을 개념

  • 레지스트리(Registry): '목록을 모아둔 곳' 또는 '정보를 정리해둔 큰 창고' 이다. (Ex. 앱스토어)
  • npm 레지스트리는 전 세계 개발자들이 만든 모든 자바스크립트 패키지(코드 묶음) 들을 모아서 잘 정리해둔 온라인 창고이다.

1.1.3 npm과 관련된 유용한 사이트

  • 번들포비아: npm에 업로드돼 있는 패키지의 크기와 정보를 얻을 수 있는 사이트(나도 가끔 사용), 웹 서비스를 배포하는 프런트엔드 개발자에게 패키지 크기는 매우 중요한 문제이므로 반드시 배포하기 전에 패키지를 한 번씩 확인해보는 것이 좋다.
  • npm 트렌드: 각 패키지의 현재 상태와 다운로드 횟수 비교 가능한 그래프를 보여주고 비슷한 역할을 하는 패키지를 찾아보기 유용한 사이트 (나도 자주 사용함)
  • unpkg: 실제 npm에 업로드돼 있는 파일을 직접 확인할 수 있는 서비스(사용 안 해봄), 버전별 파일 상태도 확인 가능.
  • snyk: 전 세계의 다양한 오픈소스 취약점을 소개해주는 사이트. 오픈소스 보안 취약점 중 npm을 선택하면 최근 날짜 기준으로 등록 된 취약점이 발생한 패키지, 영향 받는 버전 등을 확인할 수 있다. (사용 안 해봄)
  • 서비스에 사용할 패키지가 있다면 반드시 해당 패키지에 보안 취약점리 있는지 먼저 확인하는 습관을 두는 것이 좋다.

1.1.4 정리

인상 깊었던 말

p.20 개발자로서 도구의 원리를 깊이 이해하는 것은 더 나은 문제 해결 능력을 갖추는 데 필수적이다.

1.2 유의적 버전이란?

1.2.1 유의적 버전의 등장 배경과 정의

  • 계속 변화하는 의존성으로 인한 혼란을 제어하기 위해 만들어진 것이 바로 유의적 버전이다.
  • 유의적 버전은 소프트웨어의 버전을 어떻게 정의하고, 어떻게 올리고 관리하는지에 대한 규약을 나타낸다.
  • 버전은 주.부.수(Major.Minor.Patch)로 이뤄진다.
  • 기존 버전과 호환되지 않는 API 변경 사항이 있다면 주 버전이 올라간다.
  • 기존 버전과 호환되면서 새로운 기능이 추가되면 부 버전을 올린다.
  • 기존 버전과 호환되면서 버그를 수정한 것이라면 수 버전을 올린다.
  • 버전을 어떻게 올리는지에 대한 규칙을 정해놓으면 사용자는 구체적으로 어떤 변화가 있는지는 몰라도 버전에 따라 어떠한 변화가 있는지 예상할 수 있다.

1.2.2 유의적 버전의 구체적인 명세

  • 유의적 버전 명세 참고
  • 실수로 잘못된 패키지를 npm에 업로드한 경우: 버그 수정을 의미하는 수 버전을 하나 올려 올바른 패키지를 업로드 한다 -> 해당 패키지는 npm deprecate 명령어를 이용해 설치하는 사용자로 하여금 잘못된 패키지임을 알리는 것을 권장한다
  • npm 레지스트리에서 패키지를 삭제하고 싶은 경우: npm unpublish한다(단, 업로드한지 72시간이 되지 않은 경우에 한 함. 업로드한지 72시간이 지난 경우 다른 패키지의 의존성이 아니고, 총 다운로드 횟수가 300회 미만이고, 패키지 소유자가 한 명인 경우에는 가능하다)
  • 삭제 조건은 npm패키지 삭제로 인해 발생한 다양한 이슈들로 점차 강화됐다.
  • 0.x.x 버전으로 공개된 소프트웨어는 초기 개발을 위한 실험적인 소프트웨어임을 의미한다.
  • 주 버전이 0인 경우에 한해 부 버전의 변경만으로도 주 버전과 같은 의미를 가질 수 있다.
  • 따라서 특별한 이유가 있는 것이 아니고 안정적인 패키지나 소프트웨어를 목표로 한다면 주 버전이 0인 패키지의 사용은 가급적 자제하는 것이 좋다.
  • 초기 릴리스 버전은 0.1.0이라고 정해져있다(유의적 버전 명세 문서 참고)
  • 그러나 프로젝트의 필요나 상황 따라 원하는 버전에 맞춰서 사용해도 된다.

1.2.4 Node.js와 npm에서 사용하는 유의적 버전

  • 정규 표현식을 사용해 문자열이 유의적 버전 문법에 맞는지 검증할 수 있는데 자바스크립트 패키지인 semver를 사용하는 것이 일반적이다.
  • 이 패키지는 npm에서 제공하는 공식 패키지로 실제 npm 내부에서 버전을 비교하는 경우에도 이 패키지를 사용하기 때문에 믿고 사용해 볼 만하다.

1.3 유의적 버전과 npm 생태계의 명과 암

1.3.1 left-pad: 수천만 패키지에서 의존하는 유틸 패키지가 사라지면 어떻게 될까?

  • 현재는 지원 중단됐지만 과거 여러 패키지에서 널리 쓰이던 left-pad라고 하는 자바스크립트 패키지가 있었음
  • left-pad는 line-numbers라는 패키지에서 사용되었고, line-numbers는 babel-code-frame에서 사용되었음
  • babel-code-frame의 문제는 해당 패키지를 시용하는 babel의 설치를 불가능하게 만든다는 결과를 초래함.
  • 어떠한 이유로(책 참고) left-pad가 삭제되었을 때, babel에 의존하는 대부분의 자바스크립트 프로젝트도 영향을 받아 사용할 수 없게 되었고 npm에 따르면 약 2시간 30분 동안 혼란이 빚어졌다고 함.
  • 혼란의 결과로 npm팀은 npm unpublish에 앞서 1.2.2절의 ‘유의적 버정의 구체적인 명세’에서 언급한 까다로운 정책을 추가하고 패키지 삭제를 어렵게 만들고 방어 패키지(security-holder: 갑작스럽게 유명한 패키지가 삭제되는 경우, 다른 사람이 이를 점유해서 악성 코드를 올릴 수 있으므로 다른 사용자가 해당 이름을 점유하지 못하도롯 만드는 일종의 홀더 패키지)를 추가하게 됨
  • 잘 수습된 것처럼 보이지만 이 정책은 이후에 또 다른 문제를 낳게 된다고 함

1.3.2 everything: 의존성으로 있으면 패키지 삭제가 안 된다고? 그렇다면 모든 자바스크립트 패키지를 의존성으로 가져본다면 어떨까?

  • 개발자들의 작은 호기심에서 시작된 npm API와 크롤링을 활용해 현존하는 모든 패키지를 의존성으로 갖는 everything 패키지가 개발되어 npm에 업로드 되었다
  • 이 장난 같은 작업은 npm에 업로드 된 대부분의 패키지가 삭제되지 않는 사태를 발생시켰다
  • 뒤늦게 사태의 심각성을 파악한 깃허브 팀에서 everything 깃허브 저장소를 삭제하고, npm팀에서 @everything-registry의 패키지를 삭제하며 혼란이 마무리 되었다고 한다.

1.3.3 is-promise: 잘못된 부 버전 업데이트가 만들어낸 사태

  • 개발자의 문법 실수로 배포된 패키지를 다른 패키지들이 사용하고 의존성 중첩을 타고 거치면서 create-react-app에 까지 영향을 미치게 되었다는 이야기
  • ^을 사용하면 주가 아닌 부, 수의 최신 버전을 사용할 수 있어 부 버전 업데이트가 다른 패키지들에 적용이 되었다
  • 작은 패키지에서 일어난 사고가 얼마나 큰 영향을 미칠 수 있는지에 대해 생각해보자

1.3.4 colors.js와 faker.js: 섣부른 부, 수 버전 업데이트는 독이 될 수도 있다.

  • 자신이 만든 많은 프로젝트를 여러 대형 IT 기업이 사용하고 있지만 이에 대한 정당한 보상을 지급받지 못하고 있다는 불만 표출로 버그를 발생시키는 버전을 릴리스해버린 개발자에 대한 이야기.
  • 이러한 개발자의 주장이 옳다는 입장과 아무리 옳은 주장이라도 많은 프로젝트에 영향을 미쳐서는 안 된다는 두 주장이 갑론을박하게 됐으며, 사태는 결국 npm이 버그 발생되기 이전 버전 이후에 업로드 된 패키지를 모두 삭제하면서 마무리 됐다고 한다.

1.3.5 event-stream 사건: 오픈소스는 얼마나 안전한가?

  • Node.js에서 스트림을 쉽게 다루기 위한 유틸리티 패키지인 event-stream 개발자는 해당 프로젝트의 유지보수를 이어가고 싶다는 신원 미상의 개발자에게 의심없이 event-stream의 소유권을 넘기게 된다.
  • 신원 미상의 개발자는 수상한 신생 패키지(flatmap-stream)를 event-stream에 추가하고 원칙에 맞지 않는 수 버전 업데이트등을 진행한다.
  • 이후 한 달 반 뒤, flatmap-stream 내부에 비트코인 탈취를 위한 악성 코드가 삽입돼 있다는 사실이 밝혀진다.
  • 이로 인해 flatmap-stream 패키지가 npm에 의해 제거됐고, 이에 의존하고 있던 event-stream의 버전도 삭제됐다.
  • 이 사건은 유의적 버전의 규약을 교묘하게 이용한 악의적인 행동으로 볼 수 있다.

1.3.6 유의적 버전과 npm을 사용할 때 주의할 점

  • 위험은 스스로가 무엇을 하고 있는지 모르기 때문에 발생한다. - 워렌 버핏
  • 유의적 버전은 규약일 뿐이다: 일부 패키지는 유의적 버전을 준수하지 않을 수도 있다는 점을 명심하자.
  • 무조건 설치하는 것이 능사는 아니다: 정말로 대체 불가능한 패키지인지, 사용할 수 있는 만큼 검증됐는지, 타당한 dependencies를 가지고 있는지 확인한다.
  • 락 파일 변경에 주의를 기울이기: 락 파일이 변경되었다면 어떤 내용이 바뀌었는지 확인해봐야 한다
  • 보안 취약점에 귀 기울이기: snyk, dependabot 등 오픈소스 보안 취약점을 알려주는 서비스를 활용하라

새로 알게 된 내용

  • npm install은 package-lock.json이 변경되어 기존과 다른 새로운 버전이 설치될 수 있는 반면, npm ci는 철저하게 package-lock.json을 기준으로 설치한다.
  • dependabot은 깃허브에서 제공하는 서비스로, 프로젝트 내에 보안 취약점이 있는지 확인하고, 보안 취약점이 있다면 이를 해결할 수 있는 풀 리퀘스트까지 생성해주는 서비스다(사용해봐야겠음)

1.3.7 정리

  • 앞서 배운 문제점, 이외의 여러 문제들을 미연에 방지하기 위해서는 개발자 스스로가 어떠한 위험성을 가지고 있는지 충분히 인식하고, 이를 방지하기 위한 방법을 수시로 모색해 봐야 한다.

1장을 덮으며.

개인적으로 참 혼란스러운 시기다. 빠르게 진화하는 AI와 줄어드는 TO 소식에, 심지어 회사에서는 개발자에게 코딩 대신 '프롬프트'만 작성하라는 당혹스러운 프로젝트를 추진하기도 했다(결국 무산되었지만). 고민이 참 많았다. 뭘 공부해야 될지, 어디에 가치를 둬야 될지 고민하던 중 우연히 알게 된 이 책은 아주 좋은 기분전환이 되어주고 있다.

1장에서 가장 인상 깊었던 말로는 "개발자로서 도구의 원리를 깊이 이해하는 것은 더 나은 문제 해결 능력을 갖추는 데 필수적이다.(p.20)"인데, 이게 내 방황에 약간의 실마리를 던져 주었다. 코드 생성 속도는 나보다 AI가 더 빠르다. 하지만 결정적인 순간에 발생하는 '할루시네이션' 문제를 해결하는 건 아직까진 인간의 몫이다. '할루시네이션'을 잘 찾아내려면 더 깊은 지식이 필요하다는 생각이 들었다.

그래서 평소 잘 모르면서 관성적으로 대충 쓰던 도구들에 대한 공부를 하기로 마음 먹었다. 좀 더 깊이 파보기로 결정했다. 이게 정답인지는 모르겠지만 내가 좋아하는 일을 계속 해나갈 수 있는 원동력이 되길 바라며 할 수 있는 것들을 해보려 한다.