npm 패키지매니저를 들어보셨죠?
node 환경에서 사용할 수 있는 패키지매니저 라는 뜻이에요. 이것 외에도 pnpm, yarn 등의 패키지 매니저가 존재해요.
외부 의존성(=외부코드, 모듈)을 잘 사용할수있게
버저닝(버전식별), fetch(다운로드), link(쓸수있게)
해주는 역할을 가져요.
버저닝은 아래에서 설명해드릴테니 정확하게 뭔지 몰라도, 뭘 다운받아야하는 지 정하는구나 정도로 이해해주세요.
뭘 다운받아야할 지가 정해졌으면 다운로드(fetch)받아야겠죠?
그 다운로드 받은 소스파일을 쓰기좋게 잘 저장해야겠고요
방금 말한 것이 Resoultion(버저닝), fetch, Link 에요.
아래에서도 이것 3 단계를 설명할 거에요.
semver가 뭘까요?
package.json에 ^1.2.3라고 적혀있으면 어떤 버전이 설치될까요?
i. 1.2.3
ii. 1.2.9
iii. 1.4.0
iv. 2.0.0미만 중 최신버전
네 답은 iv에요.
^은 minor버전까지는 최신으로 올려서 설치하게되어요
^1.2.3이라면, 2.0.0미만의 모든 버전과 매칭될 수 있는것이죠.
만약 현재 npm에 올라가있는 최신 버전이 1.5.7이라면?
^1.2.3은 1.5.7로 결정이 되겠죠?
이게 `semver에 따른 버전고정`이에요
ex) @toss/use-overlay가 react 의존성을 가지는 경우.
@toss/use-overlay의 의존성버전 고정후,
react 의존성 확인하고,
react의 의존성 버전을 고정한다.
고정된 버전 결과물을 yarn.lock
, package-lock.json
에 저장.
-> 결정된 버전의 파일을 다운로드함
거의대부분 네트워크(보통 npm 레지스트리(npmjs.org))에서 받아옴.
네. 버전이 결정되었으니 그 모듈들을 다운로드 받아야겠죠? 그렇게 다운로드 받는 과정이에요.
npm 모듈들은 모두 npmjs.org라는 사이트에 올라가 있어서, 대부분 여기서 받아오게되어요.
node_modules에 파일시스템형태로 저장.
-> 단점 : 파일 탐색에 시간이 오래걸림.
-> 단점 2: 100개의 프로젝트가 React 18.2.0을 사용한다면, React 18.2.0이 정말 100번 복붙해서 추가함.
(타파하기위해 호이스팅을 도입하지만, 완전하지않고 불안정함)
@toss/use-overlay
가 react를 사용하면
@toss/use-overlay
폴더내에 react가 또 생성됨
위와 동일하지만 Hard link 방식을 이용해 단점 2를 해결했어요.
Hard link만 추가하면 돼서 중복쓰기문제는 없어졌지만, 파일탐색에 시간이 걸리는 단점 1은 어쩔수가 없긴함.
Hard Link가 뭘까요?
윈도우의 바로가기와 같은 역할을 하는 거에요.
OS에는 소프트링크, 하드링크가 있는데요.
둘은 차이가 존재하지만, 지금은 우선 둘 다 비슷한 거구나 정도로 이해하고 계시면 되어요.
(스킵해도됨. 소프트 링크, 하드링크 부연설명)
소프트링크는 윈도우의 바로가기에요.
파일 A를 만들고, 이에대한 바로가기파일 A.lnk를 만들면,
A.lnk를 눌러도 파일 A로 가지요?
반면 하드링크는 파일 A와 같은 실제 파일이에요.
파일이란 뭘까요? 디스크로 찾아갈 수 있는 입구같은거에요.
파일시스템에 파일을 추가하면,
실제로는 1. 디스크쓰기, 2. 하드링크생성(우리가 보는 파일)
이 두작업이 발생해요.
실제로 비싼 작업은 1이고, 2는 소프트링크를 만드는 데 걸리는 것과 시간차이가 없어요
그럼 하드링크를 하나 더 만들면 어떻게될까요?
입구가 하나 더 생길 뿐, 소프트링크와 거의 동일하게 동작해요.
디스크 공간도 거의차지하지않고, 생성시간도 매우 짧아요.
파일시스템 대신 JS Map 객체를 이용해요.
파일 읽기/쓰기가 없고, 메모리(RAM)상에서 해결되니까
하드디스크처럼 오래걸리는 녀석을 참조할 필요가 없어서 속도가 빠르겠죠?
그리고 해싱이 이용되기때문에 찾아가는 속도도 더 빨라요.
그래서 위 단점 2개를 모두 해결 한 셈.
하지만
node-modules
디렉토리와 호환성이 낮음.Node.js를 초기구동해야하기때문에 그 시간이 걸리고,
node-modules 방식을 사용하지 않기때문에 호환성이 낮다고 할수있겠죠?
PnP와 Zero-intall을 헷갈리시는 경우가 많은데요.
둘은 많이 비스하지만 달라요
Zero-intall은 fetch된 의존성과 PnP의 JS Map객체까지 전부 Git에 넣어 관리하자는 방식이에요.
다른말로하면?
node-modules 폴더를 Git에 올려버리는 것으로 이해할 수 있겠죠?
우선은 이렇게 이해해주세요 !
정확하게는,
yarn의 zero-install에서는
pnp와 npm 모드(느림)가 있어요.
선택된 모드에따라 node-modules를 올릴수도있고, pnp JS Map객체를 올릴수도 있겠죠?
npm 방식
에서는 의존성 중복문제 (@toss/use-overlay에 react가 또 설치됨) 때문에 공간낭비가 심했지만,
PnP는 의존성 중복문제를 해결했기때문에 그 문제가 좀 덜해요.
장점은 install 과정이 없고, 초기설정이 없고, 일관성있는(버전) 환경을 사용할 수 있겠죠?
단점은 역시 Git에 모든 파일을 올려버리니 용량이 커진다는 단점이 있겠네요.
두번째 단점은 Git관리가 어려워진다.
에요
소스코드뿐만아니라 외부의존성까지 Git으로 관리한다면, 커밋이력이 좀 더 복잡해지겠죠?
하지만 역시 단점이 커서 정말 잘 고려해봐야할거같네요.
토스팀도 yarn install을 하고, node_modules 지웠다가 다시설치하고... 이게 싫어서
zero-install을 도입했었는데,
편리하긴했으나 위 두 단점때문에 다시 돌아갔다고 해요.
토스에서는 Yarn(PnP) 방식을 사용해요.
위 방식 중에선 에러발생없이 정확하고, 성능이 좋기 때문이에요.
Zero-install 이 더 빠르긴 하겠지만, Git이 무거워지는 문제때문에 여기까지만 도입을 한 것 같아요.
자세한 상세는 토스 라이트닝토크 - 패키지매니저를 참고해주세요
브라우저환경에서는 어떻게 import 할까요?
HTML에서 import하려면?
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin>
이렇게 많이하지요?
최근 좀 더 표준적인 방법.
즉 JS의 표준으로 Import Map 방식이 나왔어요.
<script type="importmap">
{
"imports":{
"react": "https://unpkg.com/react@18/umd/react.development.js"
}
}
</script>
와 같이 json 형태로 imports내에 작성해주면 되어요.
이는 브라우저 외에 Deno에서도 기본적인 방법으로 사용하고있어요.
Deno는 Node.js의 대체재에요. Node.js 개발자가 만들었어요.
이 포스팅은 아래의 글을 바탕으로
배경지식이 조금 더 적은 경우에도 읽을 수 있게
예시와 예제를 조금 더 추가하여 작성하였습니다.
추후 포스팅 : peerdependencies 관련 내용