Skip to content

ABI 안정성

소개

ABI(Application Binary Interface)는 프로그램이 컴파일된 다른 프로그램에서 함수를 호출하고 데이터 구조를 사용하는 방법입니다. 이는 API(Application Programming Interface)의 컴파일된 버전입니다. 즉, 애플리케이션이 원하는 작업을 수행할 수 있도록 하는 클래스, 함수, 데이터 구조, 열거형 및 상수를 설명하는 헤더 파일은 컴파일을 통해 ABI 제공자가 컴파일된 주소 집합과 예상 매개변수 값, 메모리 구조 크기 및 레이아웃에 해당합니다.

ABI를 사용하는 애플리케이션은 사용 가능한 주소, 예상 매개변수 값, 메모리 구조 크기 및 레이아웃이 ABI 제공자가 컴파일된 것과 일치하도록 컴파일되어야 합니다. 이는 일반적으로 ABI 제공자가 제공하는 헤더에 대해 컴파일하여 수행됩니다.

ABI 제공자와 ABI 사용자는 서로 다른 시기에 다른 버전의 컴파일러를 사용하여 컴파일될 수 있으므로 ABI 호환성을 보장하는 책임의 일부는 컴파일러에 있습니다. 서로 다른 공급업체에서 제공할 수 있는 컴파일러의 다른 버전은 모두 특정 콘텐츠가 있는 헤더 파일에서 동일한 ABI를 생성해야 하며, 주어진 헤더에 설명된 API에 액세스하는 애플리케이션 코드를 헤더의 설명에서 발생하는 ABI의 규칙에 따라 생성해야 합니다. 최신 컴파일러는 컴파일하는 애플리케이션의 ABI 호환성을 깨지 않는 데 상당히 좋은 기록을 가지고 있습니다.

ABI 호환성을 보장하는 나머지 책임은 컴파일 시 안정적으로 유지될 ABI를 생성하는 API를 제공하는 헤더 파일을 유지 관리하는 팀에 있습니다. 헤더 파일을 변경할 수 있지만 변경 사항의 특성을 면밀히 추적하여 컴파일 시 ABI가 기존 ABI 사용자를 새 버전과 호환되지 않게 변경되지 않도록 해야 합니다.

Node.js의 ABI 안정성

Node.js는 여러 독립적인 팀에서 관리하는 헤더 파일을 제공합니다. 예를 들어 node.hnode_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 헤더 파일은 없을 것입니다. 이러한 단계는 JavaScript 엔진에 구애받지 않는 API가 Node.js에서 먼저 제공되고 네이티브 애드온에서 채택되지 않으면 일반적으로 Node.js 생태계와 특히 네이티브 애드온 생태계를 파괴할 수 있습니다.

이러한 목적을 위해 Node.js는 버전 8.6.0에서 N-API를 도입했으며 Node.js 8.12.0부터 프로젝트의 안정적인 구성 요소로 표시했습니다. API는 헤더 node_api.hnode_api_types.h에 정의되어 있으며 Node.js 주 버전 경계를 넘나드는 순방향 호환성을 보장합니다. 보증은 다음과 같이 설명할 수 있습니다.

주어진 버전 n의 N-API는 게시된 Node.js 주 버전과 이후의 모든 Node.js 버전(이후의 주 버전 포함)에서 사용할 수 있습니다.

네이티브 애드온 작성자는 애드온이 node_api.h에 정의된 API와 node_api_types.h에 정의된 데이터 구조 및 상수만 사용하도록 하여 N-API 순방향 호환성 보증을 활용할 수 있습니다. 그렇게 함으로써 작성자는 프로젝트에 네이티브 애드온을 추가하여 애플리케이션의 유지 관리 부담이 순수하게 JavaScript로 작성된 패키지를 추가하는 것보다 더 이상 증가하지 않음을 프로덕션 사용자에게 알림으로써 애드온의 채택을 촉진합니다.

새로운 API가 수시로 추가되기 때문에 N-API는 버전 관리됩니다. 시맨틱 버전 관리와 달리 N-API 버전 관리는 누적됩니다. 즉, N-API의 각 버전은 semver 시스템의 부 버전과 동일한 의미를 전달하며 N-API에 대한 모든 변경 사항은 이전 버전과 호환됩니다. 또한 커뮤니티에 프로덕션 환경에서 API를 검증할 기회를 제공하기 위해 새로운 N-API가 실험적 플래그로 추가됩니다. 실험적 상태는 새로운 API가 향후 ABI와 호환되지 않는 방식으로 수정될 필요가 없도록 주의를 기울였지만 프로덕션에서 설계된 대로 올바르고 유용하다는 것이 충분히 입증되지 않았으며 결과적으로 향후 버전의 N-API에 최종적으로 통합되기 전에 ABI와 호환되지 않는 변경 사항이 발생할 수 있음을 의미합니다. 즉, 실험적 N-API는 아직 순방향 호환성 보증이 적용되지 않습니다.