node-sass가 노드 버전에 의존적이 이유

스타일시트 전처리 언어인 sass를 사용하는데 노드 환경에서는 node-sass를 이용해 css 코드로 변환한다. 전처리 언어 중 less를 먼저 사용했는데 점차 sass를 사용하는 분위기였다. c언어 구현체가 있는 sass의 빠른 빌드 속도 때문이듯 하다. 요즘엔 거의 sass만 사용한다.

sass 환경의 프로젝트에서 이따금 이런 오류를 접하게 된다.

Error: Node Sass does not yet support your current environment: OS X 64-bit with Unsupported runtime (83)
For more information on which environments are supported please see:

보통 노드 버전을 올린 뒤 나오는 메세지다.

node-sass 최신 버전을 설치해서 이 문제를 해결해 왔는데 딱히 원인을 짚어낼 수 없었다. '이 패키지가 노드 버전을 타는구나' 정도로 어렴풋이 이해하고 있었다.

오류 메세지를 단서로 원인을 좀 찾아 보았다.

오류 메세지의 원인

node-sass에서 메세지가 나오는 경로는 이러하다.

// lib/index.js
var binding = require("./binding")(sass)

프로젝트 진입점에서 바인딩(binding) 모듈을 가져와 실행한다.

// lib/binding.js
if (!ext.isSupportedEnvironment())

바인딩 모듈에서는 현재 프로그램이 실행되는 컴퓨터 환경을 점검한다.

// lib/extensions.js
function getHumanNodeVersion(abi)
  switch (parseInt(abi || process.versions.modules, 10)) {
    case 11: return 'Node 0.10.x';
    case 14: return 'Node 0.12.x';
    case 42: return 'io.js 1.x';
    case 43: return 'io.js 1.1.x';
    case 44: return 'io.js 2.x';
    // ...
    default: return false;

그 중 노드 버전을 검사하는 코드다. process.versions 객체로 노드 버전을 체크하는데 찾고자 하는 버전이 없으면 false를 반환한다.

// lib/binding.js
throw new Error(errors.unsupportedEnvironment())

바인딩 모듈은 노드 버전 검사에서 false를 받으면 정의된 오류 메세지를 예외로 던지고 프로그램을 종료한다.

// lib/erors.js
module.exports.unsupportedEnvironment = function() {
  return [
    'Node Sass does not yet support your current environment: '
};

unsupportedEnvironment()가 바로 우리가 찾던 오류메세지를 반환하는 녀석이다.

현상을 재현해보면 이렇다.

  • node-sass@4.13.0(구 버전)을 사용한다고 가정했을 경우
  • getHumanNodeVersion() 함수에서 체크하는 노드 버전은 13까지다
  • node@13(구 버전)을 사용하면 이 버전 체크에 통과한다
  • 하지만 node@14(신 버전)로 올리면 node-sass 구버전에서는 지원하지 않는 버전이므로 예외를 던진다

이러한 방식으로 node-sass는 지원하는 노드 버전을 체크한뒤 미지원 버전에서는 동작하지 않는 것이다.

node-sass는 왜 노드 버전을 체크하는 것일까?

그럼 node-sass는 무엇 때문에 노드 버전을 체크하는 것일까?

libsass

그전에 libsass를 먼저 봐야겠다. sass는 처음에 루비 환경에서 사용할 수 있는 도구였다. 다른 환경도 지원하기 위해 c/c++로 만든 것이 libsass 라이브러리다.

이것은 단지 라이브러리 일뿐이라서 sass 코드를 컴파일하기 위해서는 임플리멘터(implementer)가 필요하다. 노드, 파이썬, 자바 등 다양한 환경에서 사용하기 위한 임플리멘터가 있고 node-sass도 이들 중 하나이다. node-sass는 libsass를 노드 환경에서 사용하기 위한 도구인 것이다.

C++ Addon

그럼 c/c++로 만든 코드를 노드에서 실행시키는 방법은 뭘까? 노드는 자바스크립트와 c/c++ 라이브러리 간의 인터페이스를 제공해 주는데 바로 C++ 애드온이라고 부른다.

node-sass도 애드온을 이용해 libsass를 노드에서 사용한다. 하지만 애드온이 자바스크립트 코드를 해석하는 v8 버전에 의존적이라는 것이 문제다.

v8 엔진이 업그레이드 되면 애드온이 제공하는 인터페이스도 맞추어야 하기 때문이다. 보통 노드 메이저 업데이트가 되면 v8 버전도 올라가면서 애드온도 수정해야한다.

이러한 동작 원리 때문에 node-sass도 node 버전에 따라 업데이트 버전을 제공하고 있는 것이다.

애드온의 의존성 문제를 해결하기 위한 방법도 있다(참고: https://aerocode.net/341). NAN과 N-API가 있는데 NAN(Native Abstractions for Node.js)는 위에서 설명한 방식이고, 언급한 문제를 해결하기 위해 나온 것이 N-API 방식이다.

현재 node-sass는 NAN 방식으로 애드온을 사용하고 있고, N-API 방식으로 변경하는 작업을 진행 중인것 같다.

결론

노드 업데이트 전에 node-sass 지원 여부를 확인하자.

노드 메이저 업데이트 시 node-sass 최신 버전을 사용하자.

N-API 방식으로 바뀐다면 이러한 버전관리에 신경을 덜 쓸것으로 기대해 본다.