자바스크립트 개발자를 위한 ReasonML

시작하며

ReasonML은 OCaml과 자바스크립트의 특징을 두루 가지고 있는 현대적 프로그래밍 언어입니다. 특히 리액트나 그래프큐엘과 같이 널리 쓰이는 기술과도 잘 맞아서 개발자의 생산성을 많이 올려줍니다.

그린랩스에서는 ReasonML로 풀스택 웹 개발을 하고 있습니다. 수개월간 제품 개발에 사용해본 결과, 지금은 ReasonML 덕분에 전체적인 개발 생산성이 많이 올라갔습니다. 하지만 한국에서 ReasonML은 아직 생소한 언어인 것 같습니다.

이 글은 웹 개발을 할 때 ReasonML의 특징과 그런 특징을 가지게 된 역사적 이유에 대해 이야기합니다. 이 글이 ReasonML에 관심있는 분에게 조금이라도 도움이 되길 바랍니다.

ReasonML 특징

1. 자바스크립트와 좋은 관계


1/* 상품명 앞에 "Name: "을 붙여주는 함수 */
2let formatName = product => "Name: " ++ product.name;

만약 자바스크립트 개발 경험이 있다면, 위의 코드가 쉽게 읽힐 것입니다. 그러나 자바스크립트처럼 생긴 위 코드는, 실은 ReasonML 코드입니다. ReasonML은 2016년에 만들어진 매우 현대적인 언어임에도 자바스크립트를 포함한 C 스타일의 언어에 익숙한 사람에게 친숙해지는 것을 목표로 설계되었습니다. 따라서 문법(Syntax)의 많은 모양새가 자바스크립트를 닮았습니다.

또한 ReasonML 코드는 자바스크립트 코드로 컴파일되어 브라우저와 노드 환경 양쪽에서 실행 가능합니다. 따라서 우리가 기존에 웹 개발을 할 때 사용했던 리액트, 익스프레스 등 자바스크립트 생태계를 그대로 사용할 수 있습니다.

2. 강력하고 안전한 타입 시스템

ReasonML은 강력한 타입 시스템을 가진 정적 언어입니다. 강력한 타입 시스템은 프로그래밍 할 때, 버그를 줄여주고 유지 보수를 용이하게 해줍니다. 개발자가 ReasonML의 강력한 타입 시스템을 통해 얻는 이점은 다음의 두 가지로 설명할 수 있습니다.

첫 번째로, 런타임에 null/undefined 타입 에러가 발생하지 않음을 보장합니다. ReasonML은 안전한(Sound) 타입 시스템을 가졌기 때문에 컴파일러의 타입 검사를 통과한 코드는 위와 같은 에러가 발생하지 않습니다.

두 번째로, 타입 추론 능력이 뛰어나기 때문에, 개발자가 불필요한 타입 어노테이션을 적을 필요가 없습니다. 그래서 타입 힌트로 인해 코드가 장황해지는 부작용이 없습니다.

1/* 상품 타입을 선언 */
2type product = {
3 name: string,
4 id: int,
5};
6
7/* 별다른 타입 힌트를 주지 않아도, 상품 타입으로 옳게 추론함. */
8let updateName = (product, name) => { ...product, name };

실례로 페이스북은 2017년, 페이스북 메신저의 웹 버전을 ReasonML로 포팅한 결과를 포스팅했습니다. 해당 포스팅에서 페이스북은 1년 동안 단 10개의 버그를 찾았다고 발표했고, 가장 큰 이유로 ReasonML의 강력한 타입 시스템을 언급했습니다.

ReasonML에서 이런 마법 같은 일이 가능한 이유는 OCaml 언어의 안전한(Sound) 타입 시스템을 이용하기 때문입니다. 사실 ReasonML은 OCaml에 중괄호 문법 등을 추가해 자바스크립트를 사용했던 사람도 익숙하게 사용할 수 있도록 문법적으로 확장한 것입니다.

[OCaml]

1let _ =
2 let msg = "Hello" in
3 print_string msg;
4 let msg2 = "Goodbye" in
5 print_string msg2

[중괄호를 이용한 ReasonML]

1{
2 let msg = "Hello";
3 print_string msg;
4 let msg2 = "Goodbye";
5 print_string msg2
6};

3. 빠른 컴파일 속도

ReasonML은 빠른 컴파일 속도가 장점입니다. 페이스북의 통계를 다시 인용하자면 페이스북 메신저 프로젝트에서 수백 개 파일을 빌드 하는데 걸린 시간이 2초 이내였고, 그 이후에 수행되는 점진적인 빌드 시간 또한 100ms를 넘지 않았습니다. 저도 회사에서 ReasonML 코드를 작성할 때, 자바스크립트 코드로 컴파일되는 시간은 대부분 50ms 이내 끝났습니다. 마치 파일 수정과 동시에 빌드가 완료되는 것 같았습니다.

ReasonML은 빌드 속도를 위해서 간결하고 빠르다고 알려진 닌자를 빌드 도구로 선택했습니다. 그리고 ReasonML 빌드에 사용되는 OCaml 프론트엔드 컴파일러는 프랑스 국립 연구기관인 INRIA에 의해 수십 년 동안 연구 및 최적화되어서 굉장히 빠릅니다.

공식 사이트에서 진행한 컴파일 속도 테스트의 내용은 다음과 같습니다.

  • 조건: 맥북 프로 2015년형 (3.1GHz Intel Core i7) 기준. 10,000개의 파일을 생성하고, 절반은 의존성을 추가해 줌.
  • 결과: 클린 빌드 시, 3분 이내. 점진적 빌드 시, 의존성이 없는 경우, 평균 1초 안에 완료.

또한 한 개발자가 타입스크립트와 ReasonML의 컴파일 속도를 비교한 포스팅이 있는데, TodoMVC 애플리케이션 컴파일 시, 타입스크립트와 리액트 조합은 총 6.18초나 걸린 반면, ReasonML + 리액트 애플리케이션은 0.99초 밖에 걸리지 않았습니다.

정리하자면 ReasonML은 OCaml이지만 자바스크립트로 컴파일됩니다. 그래서 OCaml의 강력한 타입 시스템과 빠른 컴파일 속도라는 장점을 취하면서도, 동시에 가장 대중적이고 활발한 자바스크립트 생태계 전체를 활용할 수 있는 현대적 언어입니다.

ReasonML의 기원

1. 리액트의 탄생

갑자기 리액트가 등장해서 의아하시겠지만, ReasonML은 리액트를 만든 Jordan Walke가 만들었습니다. 그래서 그가 ReasonML을 왜 만들었는지 알기 위해서는 리액트의 탄생부터 알아보아야 합니다.

Jordan Walke는 페이스북에서 일하면서 Standard ML이라는 언어의 매력에 푹 빠져있었습니다. 그래서 리액트의 프로토타입 또한 SML로 만들었습니다. (이러한 이유로 리액트의 근본이 되는 철학은 객체 지향 프로그래밍보다는 함수형 프로그래밍에 더 가깝습니다.) 하지만 그는 대중성을 위해 리액트 프로젝트를 자바스크립트로 포팅하기를 결심합니다. 그 결과 리액트는 매우 큰 성공을 거두었고, 현재 세계에서 가장 유명한 프론트엔드 기술이 되었습니다.

2. 함수형 혹은 강력한 타입 시스템의 여러 프로젝트들

리액트가 나온 이후 몇몇 사람들은 리액트라는 구현체보다 그 바탕에 있는 패러다임을 주목했습니다. 이후 리덕스, 엘름과 같은 프로젝트들이 탄생했습니다. 어떤 프로젝트는 리액트와 함께 사용하는 라이브러리로, 어떤 프로젝트는 리액트와 경쟁하는 라이브러리가 됐습니다. 하지만 양쪽의 경우 모두 개발자들로 하여금 함수형 패러다임과 강력한 타입 시스템에 대한 관심을 증폭시키는 계기가 되었습니다. 이러한 프로젝트의 성공을 보며 페이스북과 Jordan Walke도 웹 개발을 위한 새로운 언어가 필요하다고 생각하게 됩니다.

3. 버클스크립트 컴파일러1의 탄생

비슷한 시기에 많은 회사들이 자바스크립트에 타입 시스템을 도입하는 방법에 대해서 연구했습니다. 그 시도는 자바스크립트에 점진적 타입 시스템(타입스크립트, 플로우 등)을 적용하는 접근도 있었고, 정적 타입 언어를 개발하여 자바스크립트로 컴파일(퓨어스크립트 등)하는 접근도 있었습니다. 그 시도 중 하나로 블룸버그 개발자였던 Hongbo Zhang이 OCaml 컴파일러의 백엔드를 자바스크립트로 포팅했습니다. 그는 OCaml 코드를 자바스크립트로 컴파일해서 브라우저에서 실행하는데 성공했고, 이 프로젝트를 버클스크립트라는 오픈소스로 공개했습니다.

4. ReasonML의 탄생

Jordan Walke가 리액트를 만들 때 사용했던 SML과 OCaml은 뿌리가 같습니다. 둘 다 ML 계열의 언어로, 굳이 비유하자면 SML은 미국 사투리, OCaml은 프랑스 사투리입니다. 따라서 Jordan Walke는 새로운 언어를 만들며 OCaml과 버클스크립트 컴파일러를 활용해야겠다고 생각했습니다. 또한 그는 리액트 프로젝트를 자바스크립트로 포팅해 성공한 경험으로 인해, 새로운 언어가 자바스크립트 문법과 친숙해야한다고 생각했습니다. 그렇게 그는 OCaml에 문법을 추가했고, 그 결과 ML 계열의 강력한 타입 시스템을 가지고 있으면서, 기존 자바스크립트 생태계와도 자연스럽게 융화될 수 있는 ReasonML이 탄생했습니다.

[자바스크립트 문법]

1const myFun = (x, y) => {
2 const doubleX = x + x;
3 const doubleY = y + y;
4 return doubleX + doubleY;
5};

[자바스크립트와 닮아있는 ReasonML 문법]

1let myFun = (x, y) => {
2 let doubleX = x + x
3 let doubleY = y + y
4 doubleX + doubleY
5}

최근 Jordan Walke는 ReasonML에 대해서 다음과 같은 의견을 가지고 있습니다.

ReasonML은 여러 가지 하위 프로젝트를 위한 생태계이자 스폰서다. 이 모든 프로젝트는 타입 안정성, 빠른 컴파일 및 빠른 실행 코드를 많은 개발자들에게 제공한다는 목표를 가지고 있다.

ReasonML이 새로운 문법을 추가하여 프로그래밍 언어적으로 발전하는 것도 물론 중요하지만, 보다 높은 수준에서의 진정한 목표는 타입 안정성, 빠른 컴파일 속도, 빠른 실행 속도 이 세 가지라는 것입니다. 이와 관련하여 보다 자세한 내용이 담겨있는 발표 자료가 있으니 참고하시면 좋을 것 같습니다.

[React to the Future - Jordan Walk]

타입스크립트와의 비교

ReasonML과 타입스크립트 모두 타입을 도입하여 자바스크립트를 보다 안전하게 사용하기 위한 목적으로 만들어졌습니다. 현재 타입스크립트는 가장 성공한 자바스크립트의 타입 시스템이 아닐까 싶습니다.

  1. 자바스크립트 문법과의 하위 호환
  2. 사용성
  3. 완벽한 타입 안정성

여기서 두 가지만 선택해야 한다면 어떤 것들을 선택하시겠나요? 타입스크립트는 1,2번을 선택하였고, ReasonML은 2,3번을 선택하였습니다.2 이것은 옳고 그름의 문제가 아닙니다. 언어별로 디자인의 철학이 다를 뿐입니다. ReasonML이 보다 완벽하게 타입 안정성을 제공하지만, 타입스크립트의 방향성 또한 옳습니다.

하지만 두 언어의 철학이 다른 것에서 오는 차이점도 분명히 존재합니다. 공식 웹사이트에 있는 타입스크립트와의 차이점의 내용을 바탕으로 정리해보겠습니다.

1. ReasonML은 엄선된 기능만을 제공하기 때문에 사용하기 쉽습니다.

  • 타입스크립트는 다소 복잡하고 익혀야할 것이 많습니다. 왜냐하면 기존 자바스크립트 문법 전체를 포함하면서, 그 이상의 기능을 제공하여 개발자의 생산성을 높이는 것이 목표[^3]이기 때문입니다.
  • ReasonML은 자바스크립트 문법 중, 안전한 지점만 제한적으로 사용하도록 하여 프로그램 전체의 타입 안정성을 획득하는 것이 목표입니다. 엄선된 기능(curated set)만을 제공하기 때문에 상대적으로 문법이 쉽고 간단합니다.

2. ReasonML은 프로젝트의 타입 커버리지를 올바르게 측정할 수 있습니다.

  • 기존 자바스크립트 프로젝트를 마이그레이션할 때, 타입스크립트를 이용해 타입을 추가하다보면 여러 파일에 파편적으로 타입 힌트를 추가하게 됩니다. 이런 방식으로는 과거와 비교해 얼마만큼 더 안전해졌는지 알기 어렵고, 또한 프로그램 전체의 타입 커버리지를 측정하기도 어렵습니다.
  • ReasonML은 안전한 코드(= 순수한 ReasonML 코드)와 그렇지 않은 코드(= 자바스크립트와 함께 동작하는 코드)가 명확히 구분됩니다. 따라서 파일 단위로 마이그레이션을 하면 점진적으로 타입 안정성을 획득하고 커버리지 또한 측정할 수 있습니다.

3. ReasonML은 타입이 있는 함수형 프로그래밍 스타일을 권장합니다.

  • if나 가상 디스패치보다 패턴 매칭으로, 클래스보다 순수 데이터와 함수 조합으로 프로그래밍하도록 합니다. 또한 무분별하게 문자열을 사용하기보다는 타입을 통한 적절한 데이터 모델링을 하도록 강제합니다.

마치며

ReasonML 컴파일러는 제 코드의 타입이 틀린 곳을 자세하고 집요하게 지적해 줍니다. 때문에 처음에는 컴파일러의 검사를 통과시키기가 힘들었습니다. 하지만 그 깐깐한 검사를 통과하면 굉장히 뿌듯하고 마음까지 편안해집니다. 적어도 실행 시점에 예상치 못한 타입 에러는 없을 것이기 때문입니다.

저는 ReasonML의 이런 개발 경험을 **"똑똑한 컴파일러와 페어 프로그래밍을 하는 것 같다"**라고 표현합니다. 덕분에 런타임에 브라우저 에러 로그를 보고 실수를 바로잡는 일을 해본 지 정말 오래된 것 같습니다.

현재 그린랩스에서는 크고 작은 여러 프로젝트에서 ReasonML을 사용하고 있습니다. ReasonML을 통해서 타입 시스템이라는 깊고 심오한 분야가 있다는 것을 알게 되었고, 올바른 타입 시스템을 사용해 프로그램을 작성하는 방법에 대해 꾸준히 공부하고 있습니다.

제가 느꼈던 좋은 개발 경험에 대해 나누고 싶었습니다. 제 글이 ReasonML을 시작하는 분이나 자바스크립트에 타입 시스템을 도입하고 싶으신 분들께 작게나마 도움이 된다면 좋겠습니다. 이 글에서 미처 담지 못한 내용과 리액트, 그래프큐엘과 함께 사용할 때의 좋은 점 등은 나중에 주제별로 자세히 다루겠습니다.



Gravatar for kimchhickey@gmail.com
양성민백엔드 엔지니어
2020. 12. 01.


참고자료


1 버클스크립트가 2020년 8월 리스크립트로 리브랜딩 하여 활발히 개발되고 있습니다.
(참고)
2 이 질문은 Jordan Walke가 해커 뉴스에서 작성한 댓글에서 발췌하였습니다.
(링크)
3 또한 타입스크립트는 안전(
sound)하지 않은 타입 시스템 언어입니다. 대신 안전하지 않을 수 있는 동작에 대해서 심도있게 고려하여, 그러한 상황이 발생할 수 있는 시나리오를 문서로 정리해서 제공하고 있습니다. (참고)


추천 콘텐츠