최근 시작한 프로젝트에서 Next Auth v5로 네이버 로그인을 구현하면서 마주한 오류이다.
[auth][cause]: OperationProcessingError: "response" body "expires_in" property must be a positive number
구글링을 해보니 NextAuth.js에 공식적으로 지원되는 인증 공급자 중 OAuth 2.0 프로토콜을 기반으로 하는 Azure AD B2C (Azure Active Directory Business to Consumer)에서 발생하는 에러였다.
위 네이버 개발자 페이지의 문의 내용처럼 expires_in 필드의 타입이 OAuth 2.0 표준인 number 타입이 아닌, 네이버에서 string으로 반환하는 문제가 존재한다.
네이버 로그인에서도 공식적으로 해당 문제를 인식하고 있으나 당장은 변경이 불가능하다는 깃허브 discussion을 보게 되어 기존 패키지의 정의된 종속성을 다른 버전이나 포크로 변경하는 오버라이딩을 진행하기로 했다.
// package.json
"overrides": {
"oauth4webapi": "npm:@jacobkim/oauth4webapi@^2.10.4"
}
그러나 위의 방식처럼 oauth4webapi
의 종속성을 Jacob Kim님의 포크 버전으로 대체하려했지만 이번에는 내 프로젝트의 npm 내부 버그로 인해 온전하지 못하다는 것을 알게 되었다.
해당 페이지에 나온 버그 내용들을 요약하자면,
overrides
속성은 패키지를 처음 설치할 때만 적용되고, 이후 설치 시에는 무시되는 경향이 있다.package-lock.json
의 영향:npm install
실행 시 package-lock.json
이 없는 경우 overrides
가 정상적으로 작동한다. 하지만 이 파일이 생성된 후 다시 npm install
을 실행하면 overrides
가 무시되는 현상이 발생한다.npm update
를 실행할 때는 overrides
가 적용되기도 하지만, 이것이 일관성 있게 적용되지 않아 사용자에게 혼란을 준다.overrides
가 정상적으로 작동하지 않으면, 패키지 중복이나 의존성 충돌, 보안 관련 추가적인 문제를 일으킬 수 있다.overrides
가 잘 작동할 수 있으나, CI 파이프라인과 같은 다른 환경에서는 overrides
설정이 제대로 적용되지 않아 보안 취약점이 발생하거나 파이프라인 실패를 초래할 수 있다.개발 특성상 일관성과 신뢰성이 중요한데 npm의 이러한 불안정함![]
은 너무나도 큰 단점으로 다가왔다.
npm은 JavaScript의 표준 패키지 관리자로, 모든 Node.js 환경에서 널리 사용된다. 또한 npm은 초기 설정이나 사용법이 간단하며, Node.js를 설치할 때 자동으로 포함되므로 별도의 설치 과정이 필요 없다.
필자 또한 낮은 진입장벽 덕분에 개발을 시작한 2년 동안 npm만 사용했다.
또다른 npm의 특징으로는 v3 버전 이후로 종속성을 평평하게(flat) 관리하는 방식을 채택하고 있다는 점이다. 이는 node_modules
디렉터리 안에서 가능한 한 많은 패키지를 최상위 레벨에 배치하여 중복을 최소화하는 것을 목표로 한다. 그러나 이 방식은 몇 가지 문제점을 동반한다 :
디스크 사용량과 중복:
node_modules
가 독립적으로 존재하므로, 여러 프로젝트에서 동일한 패키지를 사용할 경우 각 프로젝트의 node_modules
에 중복된 패키지가 저장된다. 이는 여러 프로젝트를 관리할 때 디스크 공간의 비효율적인 사용을 초래한다.예를 들어, 클라이언트 코드로 이루어진 프로젝트A와 서버 코드로 이루어진 프로젝트B가 있다고 가정해보자.
프로젝트 A: 웹 애플리케이션 (React)
react
- 사용자 인터페이스 구성axios
- 외부 API와의 HTTP 통신redux
- 상태 관리프로젝트 B: API 서버 (Node.js/Express)
express
- 서버 프레임워크axios
- 다른 서비스와의 HTTP 통신mongoose
- MongoDB와의 데이터 상호작용공통 패키지: axios
두 프로젝트 모두 axios
를 사용하여 외부 서버나 서비스와 HTTP 통신을 한다. 이때 npm을 사용할 경우, axios
패키지가 각 프로젝트의 node_modules
디렉터리에 별도로 저장되어 디스크 공간이 중복 사용된다.
비효율적인 종속성 해결:
node_modules
의 구조가 복잡해지고 관리가 어려워질 수 있다.예를 들어 최근에 과거 코드를 살짝 손보면서 마주했던 사례를 가져와봤다.
프로젝트 구성:
react@16.8.0
react-dom@16.8.0
library-X
가 필요하며, 이는 react@15.6.2
를 필요로 함프로젝트 A는 react
의 최신 버전인 16.8.0
을 사용하고 싶지만, 동시에 library-X
를 사용하고 싶다. 그런데 library-X
는 구버전인 react@15.6.2
에 의존한다.
이때 npm은 어떻게 작동될까?
npm
은 우선적으로 프로젝트의 package.json
에서 선언된 react@16.8.0
과 react-dom@16.8.0
을 설치한다.library-X
의 종속성을 해결하려 할 때, npm
은 이미 설치된 react
의 버전과 library-X
가 요구하는 react
버전 사이의 충돌을 발견한다.node_modules
생성: npm
은 호환 가능한 버전을 찾지 못하므로, library-X
내부의 node_modules
디렉터리에 react@15.6.2
를 설치하여 버전 충돌을 해결하려고 한다.react
를 가지고 있으며, 이는 node_modules/library-X/node_modules/react@15.6.2
와 같은 구조로 저장된다.react
가 동시에 존재하게 되면 예상치 못한 동작이나 성능 저하를 초래한다.이러한 이유들로 2년 간 동거동락했던 npm을 손절하기로 했다!
pnpm의 공식 문서 motivation에 의하면 pnpm의 가장 큰 특징으로는 디스크 공간 절약, 설치 속도 향상, 비평평한 node_modules가 있다.
pnpm
은 내용 주소 가능 저장소(content-addressable store)를 사용하여 디스크 공간을 절약한다. 앞선 예시처럼, 두개의 프로젝트 모두가 axios
패키지를 가지고 있어서 npm
을 사용할 경우 이 종속성의 복사본이 그대로 디스크에 중복 저장된다. (100개의 프로젝트에서 하나의 종속성을 사용해도 100개의 복사본이 디스크에 저장된다.)
그러나 pnpm
을 사용할 경우 종속성의 다른 버전에 대해, 달라진 파일만 저장소에 추가된다. 예를 들어, 종속성이 100개의 파일을 가지고 있고 새 버전에서 단 하나의 파일만 변경되었다면, pnpm update
는 하나의 새 파일만 저장소에 추가한다. 즉, 전체 종속성을 복제하지 않는다는 뜻이다.
또한, 모든 파일은 디스크의 한 장소에 저장된다. 패키지가 설치될 때, 그 파일들은 이 장소에서 하드 링크 (파일의 물리적 위치에 대한 직접적 포인터) 되어 추가적인 디스크 공간을 사용하지 않는다. 이를 통해 같은 버전의 종속성을 프로젝트 간에 공유할 수 있다. 즉, 파일을 네트워크에서 다운로드하고 저장하는 대신, 이미 다운로드된 파일을 재사용할 수 있다는 뜻이다.
pnpm
은 세 단계의 설치 과정을 수행한다:
node_modules
디렉터리 구조가 종속성을 기반으로 계산된다.node_modules
로 하드 링크된다.npm
이나 Yarn Classic
을 사용할 때, 모든 패키지는 모듈 디렉터리의 루트로 호이스팅된다. 그 결과, 소스 코드는 프로젝트에 종속성으로 추가되지 않은 종속성에 접근할 수 있다.
기본적으로 pnpm
은 심볼릭 링크(파일 또는 디렉토리의 경로를 가리키는 파일)를 사용하여 프로젝트의 직접 종속성만 모듈 디렉터리의 루트에 추가한다.
이는 다음과 같이 작동한다.
express
버전 4.17.1lodash
버전 4.17.20pnpm
은 express
와 lodash
의 적절한 버전을 식별한다.node_modules
의 루트에는 express
와 lodash
에 대한 심볼릭 링크가 생성된다./project
/node_modules
/express -> .pnpm/express@4.17.1/node_modules/express
/lodash -> .pnpm/lodash@4.17.20/node_modules/lodash
이 구조에서 express
와 lodash
는 심볼릭 링크를 통해 접근된다. 이렇게 함으로써 pnpm
은 중복 저장을 방지하고, 각 패키지가 필요로 하는 종속성을 정확히 제공하면서 프로젝트의 node_modules
디렉터리를 깔끔하게 유지할 수 있다.
npm
에서 pnpm
으로 마이그레이션하는 과정은 상당히 간단하다.
먼저, pnpm
을 설치해야 한다.
npm install -g pnpm
기존의 npm
프로젝트에서 pnpm
으로 전환하기 전에, 기존의 node_modules
폴더와 package-lock.json
파일을 삭제하는 것이 좋다. 이는 pnpm
이 자체적인 방식으로 종속성을 관리하고 새로운 종속성 트리를 생성하기 때문이다.
rm -rf node_modules
rm package-lock.json
이후 프로젝트 디렉토리에서 다음 명령을 실행하여 pnpm
을 사용해 종속성을 설치한다.
pnpm install
해당 명령은 npm install
처럼 이 명령은 package.json
에 명시된 모든 종속성을 설치한다. 또한 pnpm
의 콘텐츠 주소 가능 저장소를 사용하여 종속성을 관리하게 해준다.
pnpm
으로 전환한 후, 프로젝트가 정상적으로 작동하는지 확인한다.
pnpm run build
pnpm start
pnpm run dev
다행히 네이버와 무사히 연결되었다!
pnpm 공식 문서
npm 공식 문서
네이버 개발자 포럼
https://developers.naver.com/forum/posts/35298
Jacob.kim
https://jacob.kim/blog/next-auth-expires-in/ko
github discussions