1. 프론트엔드 개발에 Node.js가 필요한 이유

1.1 최신 스펙으로 개발할 수 있다.

  • 자바스크립트 스펙의 빠른 발전에 비해 브라우저 지원 속도는 항상 뒤쳐진다.
  • 아무리 편리한 스팩이 나오더라도 이것을 구현해주는 징검다리 역할, 예를 들면 바벨같은 도구의 도움 없이는 부족하다.
  • 더불어 웹팩, NPM과 같은 노드 기술로 만들어진 환경에서 사용할 때 비로소 자동화된 프론트엔드 개발환경을 갖출 수 있다.
  • Typescript, SASS 같은 고수준 프로그래밍 언어를 사용하려면 전용 트랜스파일러가 필요한다, 이것 역시 Node.js 환경이 뒷받침 되어야 우리가 말하는 프론트엔드 개발 환경을 만들 수 있다.

 

1.2 빌드 자동화

  • 과거처럼 코딩 결과물을 브라우저에 바로 올리는 경우는 흔치 않다.
  • 파일을 압축하고, 코드를 난독화하고, 폴리필을 추가하는 등 개발 이외의 작업을 거친 후 배포한다.
  • Node.js는 이러한 일련의 빌드 과정을 이해하는 데 적지 않은 역할을 한다. 뿐만 아니라 라이브러리 의존성을 해결하고, 각종 테스트를 자동화하는데도 사용된다.

 

1.3 개발 환경 커스터마이징

  • 각 프레임워크에서 자공하는 도구(React의 CRA, Vue의 vue-cli)를 사용하면 손쉽게 개발환경을 갖출 수 있다.
  • 그러나 개발 프로젝트는 각자의 형편이라는 것이 있어서 툴을 그대로 사용할 수 없는 경우도 빈번하다.
  • 커스터마이징 하려면 Node.js에 대한 지식이 필요하다.
  • 자동화된 도구를 사용할 수 없는 환경이라면 직접 환경을 구축해야 할 상황에 놓일 수도 있다.

이러한 배경하에 Node.js는 프론트엔드 개발에서 필수 기술로 자리매김하고있다.

 

1.4 Node.js 기반 프로젝트 만들기

mkdir sample
cd sample
npm init // 프로젝트 생성하는 명령어
  • sample 폴더를 만들고, 그 폴더 안에서 npm init을 실행시킨다.
  • 그러면 프로젝트의 메타데이터를 입력할 수 있는 화면이 제공됩니다.

  • 위와 같이 프로젝트 메타데이터를 입력하면, 해당 데이터를 기반으로 package.json 파일이 생성된다.
{
  "name": "npmtest",
  "version": "1.0.0",
  "description": "npm 테스트 프로젝트입니다.",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "npm"
  ],
  "author": "hyorish03",
  "license": "ISC"
}

 

1.5 패키지 설치

1.5.1 CDN을 이용한 방법

  • 외부 라이브러리를 가져다 쓰는 것은 무척 자연스러운 일이다.
  • 간단한 방법은 CDN(콘텐츠 전송 네트워크)으로 제공하는 라이브러리를 직접 가져오는 방식입니다.
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
  • 그러나 만일 CDN 서버 장애로인해 외부 라이브러리를 사용할 수 없다면, 우리 어플리케이션 서버가 정상이더라도 필수 라이브러리를 가져오지 못한다면 웹 어플리케이션은 정상적으로 동작하지 않을 것입니다.

1.5.2 직접 다운로드하는 방법

  • 라이브러리 코드를 우리 프로젝트 폴더에 다운받아 놓을 수 있다.
  • CDN을 사용하지 않기 때문에 장애와 독립적으로 웹 어플리케이션을 제공할 수 있다.
  • 그러나 라이브러리는 계속해서 업데이트 될 것이고 우리 프로젝트에서도 최신 버전으로 교체해야합니다.
  • 매번 직접 다운로드하는 것은 매우 귀찮은 일이 될 것이고, 버전에 따라 하위 호환성 여부까지 확인하려면 실수할 여지가 많습니다.

💡 그렇다면 라이브러리를 어느 한 곳에서 업데이트하고 하위 호환이 되는 안전한 버전만 다운받아 사용할 수 있다면 어떨까 ?

 

1.5.3 NPM을 이용한 방법

  • NPM은 이러한 방식으로 패키지를 관리한다.
  • npm install 명령어를 활용해 외부 패키지를 우리 프로젝트 폴더에 다운로드 할 수 있다.
npm install react
  • 위 코드는 최신 버전의 react를 NPM 저장소에서 찾아 우리 프로젝트로 다운로드 하는 명령어다.
  • 위 코드를 실행하면 최신 버전의 react가 다운로드 될 것이고, package.json의 dependencies에는 설치된 react 패키지의 정보를 기록한다.

'NPM' 카테고리의 다른 글

[NPM] Package.json vs Package-lock.json 그리고 Caret, Tilde 등등..  (0) 2024.06.25

0. 공부 계기

프로젝트를 진행하며 Package.json 파일은 자주 보고, 사용하지만 Package-lock.json에 대해서 따로 공부해본 경험이 없습니다.

그저 패키지매니저를 재설치 할 때 package-lock.json와 node_modules를 지우고 재설치해야된다는 정도...? 그래서 정확히 공부해야할 필요성을 느껴 공부하기 시작했습니다.

 

1. Package.json이란

Node.js 프로젝트를 시작할 때, 터미널에 'npm install', 'yarn' 등의 패키지매니저를 설치하면 아래와 같이 여러 파일들이 기본적으로 설치됩니다.

 

현재 진행중인 프로젝트의 Package.json

{
    "name": "moyeota-webview",
    "private": true,
    "version": "0.0.0",
    "type": "module",
    "scripts": {
        "dev": "vite --host",
        "build": "tsc && vite build",
    },
    "dependencies": {
        "react": "^18.2.0",
        "react-bootstrap": "^2.10.1",
        /* 중략... */
    },
    "devDependencies": {
        "@types/navermaps": "^3.6.7",
        "@types/react": "^18.2.15",
        /* 중략... */
    }
}

 

2. Package-lock.json이란

  • package-lock.json 파일은 npm을 활용해 node_modules 트리나 package.json 파일을 수정하게 되면 자동으로 생성되는 파일입니다.
  • 이 파일은 파일이 생성되는 시점의 의존성 트리에 대한 정확한 정보를 가지고 있습니다.

다음은 Package-lock.json의 일부분입니다.

{
  "name": "moyeota-webview",
  "version": "0.0.0",
  "lockfileVersion": 2,
  "requires": true,
  "packages": {
    "": {
      "name": "moyeota-webview",
      "version": "0.0.0",
       /* 중략 */
      "devDependencies": {
        /* 중략 */
        "vite-plugin-svgr": "^3.2.0",
        "zustand": "^4.4.1"
      }
    },
  "dependencies": {
    /* 중략 */
    "@types/react": {
      "version": "18.2.28",
      "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.28.tgz",
      "integrity": "sha512-ad4aa/RaaJS3hyGz0BGegdnSRXQBkd1CCYDCdNjBPg90UUpLgo+WlJqb9fMYUxtehmzF3PJaTWqRZjko6BRzBg==",
      "requires": {
        "@types/prop-types": "*",
        "@types/scheduler": "*",
        "csstype": "^3.0.2"
      }
    },
    "zustand": {
      "version": "4.4.3",
      "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.4.3.tgz",
      "integrity": "sha512-oRy+X3ZazZvLfmv6viIaQmtLOMeij1noakIsK/Y47PWYhT8glfXzQ4j0YcP5i0P0qI1A4rIB//SGROGyZhx91A==",
      "dev": true,
      "requires": {
        "use-sync-external-store": "1.2.0"
      }
    }
  }
}

 

2.1 Package-lock.json이 필요한 이유

  • package.json 파일 의존성 선언에는 version range가 사용됩니다.
  • version range란 특정 버전이 아니라 버전의 범위를 의미합니다.
  • 예를 들어, npm install dotenv를 실행하면 "dotenv": "^16.3.1" 와 같은 형식으로 package.json에 설치됩니다.
    • 여기서 ^16.3.1^은 캐럿 표기법('16.3.1버전 이상, 17.0.0이하'라는 range를 알려줌)으로 ~(틸드)와 구분됩니다. (아래 정리돼있음)
  • package-lock.json에는 해당 라이브러리 설치 당시의 버전에 대한 기록을 가지고 있습니다.
  • 따라서 이후 다른 팀원이 git clone을 받고, npm install을 한다 하더라도 package-lock.json에 있는 파일 버전을 기준으로 동일하게 다운로드 할 수 있습니다.
   "node_modules/dotenv": {
      "version": "16.3.1",
      "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
      "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==",
      "engines": {
        "node": ">=12"
      },
      "funding": {
        "url": "https://github.com/motdotla/dotenv?sponsor=1"
      }
    },

 

2.2 package-lock.json 사용 예시

package.json and package-lock.json @Atatus

1) 6월 1일, Developer 1이 dotenv 16.3.1을 다운 받습니다. 이는 Package.json으로 관리합니다.

2) Package-lock.json 파일은 Package.json 파일에 기록된 당시의 의존성 트리를 관리합니다.

3) 6월 17일, dotenv 라이브러리가 16.4.0으로 업데이트 됐다고 가정해봅시다

4) 6월 18일, Developer 2가 Git 클론을 받고 npm install 합니다.

     4-1) Developer1이 package-lock.jsongit에 올리지 않았다면 dotenv 16.4.0이 다운로드 될 것입니다.
     4-2) Developer1이 package-lock.jsongit에 올렸다면 Developer2는 클론받은 프로젝트 내의 Package-lock.json 파일을 활용해 정확한 버전인 dotenv 16.3.1이 다운로드 될 것입니다.

 

 

3. 결론

Package-lock.json 파일이 없다면 협업시 팀원과 버전 충돌이 나타날 확률이 매~~우 높다.

우리는 이를 방지하기 위해 Package-lock.json 파일을 사용하는 것이다.

 

 

4. Caret & Tilde

4.1 Semeantic Versioning

React 12.3.5와 같은 버전은 MAJOR.MINOR.PATCH의 구조로 구성되고, 이는 Semantic Versioning, 간단히 줄여 semver이라 부릅니다.

  • MAJOR 버전은 하휘호환 되지 않는 API의 변화들
  • MINOR 버전은 하휘호환이 되는 범위 내에서 기능 추가들
  • PATCH 버전은 하휘호환이 되는 범위 내에서 버그 수정

 

SemVer 명세

1. SemVer을 사용하는 소프트웨어는 무조건(MUST) 공개 API에 선언되어야 한다. 이 API는 코드 그 자체로 선언되거나, 문서로 엄격히 명시해야 한다. 어떤 방식으로든, 정확하고 이해하기 쉬워야 한다.

2. 일반적인 버전 넘버는 무조건(MUST) X.Y.Z 형태여야 한다. 여기서 X, Y, Z는 양의 정수여야 하며, 절대(MUST NOT) 앞에 0을 붙여서는 안된다. X는 MAJOR, Y는 MINOR, Z는 PATCH 버전으로, 각각의 요소는 증가하는 수여야 한다. (1.9.0 -> 1.10.0 -> 1.11.0)

3. 특정 버전을 배포하고나면, 해당 버전의 내용은 절대 (MUST NOT) 변경되어서는 안된다. 어떠한 수정사항일지언정 새로운 버전을 릴리즈 해야한다.

 

여기까지가 제가 번역한 SemVer 명세 1, 2, 3번 입니다. 더 깊은 내용이 궁금하신 분들은 공식문서를 읽어보시면 좋을 것 같습니다.

 

4.2 캐럿(^)과  틸드 (~) 그리고 기타 등등 (<, >, x)

  • 캐럿(^)
    • ^X.Y.Z라 한다면, X 이하의 하위호환성이 보장되는 범위 내에서 버전 업데이트
    • ^1.1.0 : 1.1.0 <= 업데이트 버전 < 2.0  인 버전과 호환 가능
    • 주의) 캐럿도 pre-release 버전 (<1.0)일 경우 틸드처럼 동작
  • 틸드(~)
    • ~X.Y.Z라 한다면, Z 범위 내에서 버전 업데이트
    • ~1.1.0 : 1.1.0 <=, 1.2 > 인 버전과 호환 가능
    • 1.2.x라 한다면, 1.2.x로 표시된 버전만 자동 업데이트가 가능
    • 즉, 1.2.0 ~ 1.2.9까진 가능하지만 1.3.0부턴 자동 업데이트가 불가능
  • ||
    • 1.2.3 || >= 1.2.9 < 2.0.0
    • ||은 or 연산자로, 하나 이상의 범위를 명시할 수 있습니다.
    • 만일 A || B로 버전을 명시한 경우, A 혹은 B 둘 중 하나만 충족해도 버전업이 가능
    • 예를 들어 1.2.3 || >= 1.2.9은 1.2.3 또는 1.3.0 으로 업데이트 가능

 

4.3 그래서 나랑 무슨 상관..?

  • 예를 들어, 내가 zustand 4.4.1 버전을 사용하고 있다고 가정합시다.
  • 그러던 중 4.4.1버전에 버그가 생겨 zustand 개발자는 버그를 수정 후 4.4.2 버전을 다시 릴리즈 했습니다.
  • 그러나 현실적으로 개발자가 사용하는 유저에게 모두 사용중인 zustand의 버전을 업그레이드 하라고 연락을 할 순 없습니다.
  • 이때 만일 package.json의 zustand 버전이 ^4.4.1이라면, 4.5 미만의 버전과는 모두 호환이 가능하다는 것입니다.
  • 따라서 4.4.2 버전이 릴리즈 되고, 업데이트 된다면 나는 굳이 새로 zustand를 업데이트 하지 않아도 자동으로 업데이트 및 호환이 가능하다는 것입니다.

 

 

 

 


[참고자료]

https://semver.org/

https://hyunjun19.github.io/2018/03/23/package-lock-why-need/

https://han7096.medium.com/package-lock-json-%EC%97%90-%EA%B4%80%ED%95%98%EC%97%AC-5652f90b734c

https://dev-ellachoi.tistory.com/65 

+ Recent posts