ABI 안정성
소개
애플리케이션 바이너리 인터페이스(ABI)는 프로그램이 다른 컴파일된 프로그램에서 함수를 호출하고 데이터 구조를 사용하는 방법입니다. 이것은 애플리케이션 프로그래밍 인터페이스(API)의 컴파일된 버전입니다. 다시 말해, 애플리케이션이 원하는 작업을 수행할 수 있도록 하는 클래스, 함수, 데이터 구조, 열거형 및 상수를 설명하는 헤더 파일은 컴파일을 통해 ABI 제공자가 컴파일된 주소 및 예상 매개변수 값과 메모리 구조 크기 및 레이아웃 집합에 해당합니다.
ABI를 사용하는 애플리케이션은 사용 가능한 주소, 예상 매개변수 값, 메모리 구조 크기 및 레이아웃이 ABI 제공자가 컴파일된 것과 일치하도록 컴파일해야 합니다. 이것은 일반적으로 ABI 제공자가 제공한 헤더에 대해 컴파일하여 수행됩니다.
ABI 제공자와 ABI 사용자는 컴파일러의 다른 버전으로 다른 시간에 컴파일될 수 있으므로 ABI 호환성을 보장하는 책임의 일부는 컴파일러에 있습니다. 다른 공급업체에서 제공할 수 있는 컴파일러의 서로 다른 버전은 특정 내용의 헤더 파일에서 동일한 ABI를 생성해야 하며, 헤더에 설명된 결과 ABI의 규칙에 따라 주어진 헤더에 설명된 API에 액세스하는 애플리케이션 코드를 생성해야 합니다. 현대 컴파일러는 컴파일하는 애플리케이션의 ABI 호환성을 손상시키지 않는 상당히 좋은 기록을 가지고 있습니다.
ABI 호환성을 보장하는 나머지 책임은 컴파일 시 안정적으로 유지되어야 하는 ABI를 생성하는 API를 제공하는 헤더 파일을 유지 관리하는 팀에 있습니다. 헤더 파일을 변경할 수 있지만, 컴파일 시 ABI가 기존 ABI 사용자를 새 버전과 호환되지 않게 변경하지 않도록 변경의 특성을 면밀히 추적해야 합니다.
Node.js의 ABI 안정성
Node.js는 여러 독립 팀에서 관리하는 헤더 파일을 제공합니다. 예를 들어, node.h
및 node_buffer.h
와 같은 헤더 파일은 Node.js 팀에서 관리합니다. v8.h
는 V8 팀에서 관리하며, Node.js 팀과 긴밀하게 협력하지만 독립적이며 자체 일정과 우선 순위를 가지고 있습니다. 따라서 Node.js 팀은 프로젝트에서 제공하는 헤더에 도입되는 변경 사항에 대해 부분적인 통제권만 가지고 있습니다. 결과적으로 Node.js 프로젝트는 시맨틱 버전 관리를 채택했습니다. 이를 통해 프로젝트에서 제공하는 API는 하나의 주요 버전 내에서 릴리스된 Node.js의 모든 마이너 및 패치 버전에 대해 안정적인 ABI를 보장합니다. 실제로 이는 Node.js 프로젝트가 주어진 주요 버전의 Node.js에 대해 컴파일된 Node.js 네이티브 애드온이 컴파일된 주요 버전 내의 모든 Node.js 마이너 또는 패치 버전에서 성공적으로 로드되도록 보장하는 데 전념했음을 의미합니다.
N-API
Node.js에 여러 Node.js 주요 버전에서 안정적으로 유지되는 ABI를 생성하는 API를 장착해야 한다는 요구가 생겨났습니다. 이러한 API를 생성하는 동기는 다음과 같습니다.
JavaScript 언어는 매우 초창기부터 자체적으로 호환성을 유지해 왔지만 JavaScript 코드를 실행하는 엔진의 ABI는 Node.js의 모든 주요 버전에서 변경됩니다. 즉, 순수하게 JavaScript로 작성된 Node.js 패키지로 구성된 응용 프로그램은 해당 응용 프로그램이 실행되는 프로덕션 환경에 새로운 주요 버전의 Node.js가 드롭될 때 재컴파일, 재설치 또는 재배포할 필요가 없습니다. 반대로 응용 프로그램이 네이티브 애드온을 포함하는 패키지에 의존하는 경우, 프로덕션 환경에 새로운 주요 버전의 Node.js가 도입될 때마다 응용 프로그램을 재컴파일, 재설치 및 재배포해야 합니다. 네이티브 애드온을 포함하는 Node.js 패키지와 순수하게 JavaScript로 작성된 패키지 간의 이러한 차이는 네이티브 애드온에 의존하는 프로덕션 시스템의 유지 보수 부담을 가중시켰습니다.
다른 프로젝트에서는 Node.js의 대체 구현에 해당하는 JavaScript 인터페이스를 생성하기 시작했습니다. 이러한 프로젝트는 일반적으로 V8과 다른 JavaScript 엔진을 기반으로 하기 때문에 네이티브 애드온은 필연적으로 다른 구조를 가지며 다른 API를 사용합니다. 그럼에도 불구하고 Node.js JavaScript API의 여러 구현에서 네이티브 애드온에 대해 단일 API를 사용하면 이러한 프로젝트에서 Node.js 주변에 축적된 JavaScript 패키지 생태계를 활용할 수 있습니다.
Node.js는 향후 다른 JavaScript 엔진을 포함할 수 있습니다. 즉, 외부적으로는 모든 Node.js 인터페이스가 동일하게 유지되지만 V8 헤더 파일이 없을 것입니다. 이러한 단계는 Node.js에서 JavaScript 엔진에 구애받지 않는 API가 먼저 제공되고 네이티브 애드온에서 채택되지 않는 경우 Node.js 생태계 전체, 특히 네이티브 애드온에 혼란을 야기할 것입니다.
이러한 목적으로 Node.js는 버전 8.6.0에서 N-API를 도입했으며 Node.js 8.12.0부터 프로젝트의 안정적인 구성 요소로 표시했습니다. API는 node_api.h
및 node_api_types.h
헤더에 정의되어 있으며 Node.js 주요 버전 경계를 넘는 순방향 호환성을 보장합니다. 보장은 다음과 같이 설명할 수 있습니다.
주어진 버전 n의 N-API는 게시된 Node.js 주요 버전과 이후의 모든 버전(이후 주요 버전 포함)에서 사용할 수 있습니다.
네이티브 애드온 작성자는 애드온이 node_api.h
에 정의된 API와 node_api_types.h
에 정의된 데이터 구조 및 상수만 사용하도록 보장함으로써 N-API 순방향 호환성 보장을 활용할 수 있습니다. 그렇게 함으로써 작성자는 자신의 응용 프로그램에 대한 유지 보수 부담이 순수하게 JavaScript로 작성된 패키지를 프로젝트에 추가했을 때보다 네이티브 애드온을 추가함으로써 더 이상 증가하지 않는다는 점을 프로덕션 사용자에게 알려서 애드온의 채택을 용이하게 합니다.
N-API는 새로운 API가 수시로 추가되기 때문에 버전이 지정됩니다. 시맨틱 버전 관리와 달리 N-API 버전 관리는 누적적입니다. 즉, 각 버전의 N-API는 semver 시스템의 마이너 버전과 동일한 의미를 전달하며, 이는 N-API에 대한 모든 변경 사항이 이전 버전과 호환된다는 것을 의미합니다. 또한 새로운 N-API는 커뮤니티가 프로덕션 환경에서 검증할 수 있는 기회를 제공하기 위해 실험적 플래그 아래에 추가됩니다. 실험적 상태는 새로운 API가 향후 ABI와 호환되지 않는 방식으로 수정될 필요가 없도록 주의를 기울였지만 설계된 대로 올바르고 유용한지 프로덕션에서 충분히 입증되지 않았으며, 따라서 최종적으로 예정된 N-API 버전으로 통합되기 전에 ABI와 호환되지 않는 변경 사항이 발생할 수 있음을 의미합니다. 즉, 실험적 N-API는 아직 순방향 호환성 보장의 적용을 받지 않습니다.