Ramda.js, 자바스크립트 함수형 프로그래밍 라이브러리 도입

Ramda.js 적용기

What is Ramda.js

라이브러립니다. 함수형 코드를 짤 수 있도록 도와주는? 강제하는?

Ramda.js

잠시 홍보

HoseJS ❤️ Ramda.js

jq 대신 터미널에서 JSON을 핸들링하기 위해 만들었던 HoseJS에도 ramda.js 를 밀어넣었다. 로그등에서 단순 테스트나 데이터 뽑아낼 때도 ramda의 함수를 통해서 데이터를 뽑아낼 수 있다. 스크립트도 지원하므로 데이터 파이프라인 함수를 만들어 두고 재사용이 가능하다.


Why Ramda.js

underscore, lodash 를 쓰다가 중간에 안쓰게된 이유는 역설적이게도 지금 ramda.js 를 도입하려는 이유와 같습니다. 가독성 입니다.

lodash 계열의 유틸리티를 reduce 함수의 집합으로 봤습니다. 굳이 없어도 된다는는 생각, 특별히 복잡할 것도 없는 코드들 그냥 짜버리면 된다는 생각이 있었습니다. 마침 자바스립트 자체에도 map, reduce 도 들어왔구요.
제가 몇 줄 덜기 위해 어떤 함수를 lodash 문서에 찾고 적용하면 같이 일하는 팀원들도 여기까지 와줘야합니다. 안그러면 코드를 읽을수가 없으니까요.

만약 리뷰가 없는 고대의 시스템이라고 한다면 그 코드는 읽혀지지 않으므로 같은일을 하는 함수가 여러개 만들어질 수 도 있겠습니다.
반대로 리뷰가 있다면 그 함수를 이해하기 위해 다 같이 공부를 해야합니다. 공부는 강제할 수 없는 것이므로 이런류의 라이브러리는 도입에 공감대가 있어야겠군요.

그럼 소 제목을 달아서 왜 ramda.js 를 도입했는지 적어보겠습니다.

코드 리뷰에도 생산성이 있다.

버그를 줄이기 위해 코드 리뷰를 합니다만 이게 생각보다 시간을 엄청나게 잡아먹게됩니다. 가독성을 높이기 위해 의미있는 서술형 이름을 써보곤 하죠. 근데 그러다가 코드가 너무 길어져버리면 이게 오히려 가독성을 저해하기도 합니다. 여러모로 리뷰에 대한 생산성을 높일 필요가 있다고 생각했습니다. 함수형 코드가 가지는 선언적(declarative) 프로그래밍의 장점을 가져오기로 했습니다.

Ramda.js 의 디자인 철학

먼저 이달에 읽은 책에 ramda.js 에 대한 언급이 있었습니다. 아래는 제가 책을 읽고 남긴 서평입니다.

다른 함수형 라이브러리도(lodash/fp 등) 있을 텐데 왜 ramda.js 였는가.

아래는 홈페이지에 적혀 있는 내용입니다.

  • Ramda emphasizes a purer functional style. Immutability and side-effect free functions are at the heart of its design philosophy. This can help you get the job done with simple, elegant code.
  • Ramda functions are automatically curried. This allows you to easily build up new functions from old ones simply by not supplying the final parameters.
  • The parameters to Ramda functions are arranged to make it convenient for currying. The data to be operated on is generally supplied last.

요약하자면 아래 3줄이 핵심입니다.

  • 순수 함수(no-side effect), 불변성, 그리고 모든 함수가 커링이 지원됩니다
  • 특수 심볼(R.__)을 이용해서 파라미터의 순서를 바꿀 수 있습니다.
  • 데이터는 마지막에 투입합니다.

이런 점들이 강력하게 일관성을 가지고있기 때문에 생각보다 쉽게 익숙해집니다.

도입기

일단 해보자라는 심정으로 코드를 짯습니다(Learn by Doing 이니까 🏃). 어떤 것을 습득할때는 가끔 질문을 던지지 않고 그냥 달려야 할 때가 있는 것 같습니다. 저에겐 함수형이 그런 케이스였습니다.

R.map 같은 함수는 Array.prototype.map 에 상응하는데 굳이 있는걸 왜 쓰냐고 묻는다면 ramda.js 같은 경우는 데이터는 나중에 주입한다라는 철학이라도 있어서 이를 대답하겠지만 lodash의 _.map 같은 경우는 체인해서 다른 메소드 쓰려고 정도밖에 대답못할 것 같으니 스스로도 설득이 잘 안되니까요.

전에 어떤 포스트에서도 언급을 했었는데 20배정도의 시간이 든 것 같습니다. 자신없고 계속 변경해야할테니까 테스트 코드는 필수입니다.

20배의 시간을 들였다.
이해를 돕기 위해 코드의 하는 일을 설명하자면 Medium 포스트의 HTML을 가져와서 파싱하고 태그에 이름을 붙여주는 그런 내용입니다. TTS로 읽기 위해서 태그를 의미있는 글자로 바꾸는 그런 코드입니다.

이게 함수형이 맞나? 더 안보이는데… 이런 생각이 들어도 일단 전진해야합니다. 먼저 익숙해지고 판단은 나중에 한다는 전략입니다.

다른 프로젝트를 진행하면서 계속적으로 써보기 시작했습니다. 출 퇴근길에는 레퍼런스 문서를 보면서 어떤 함수가 있는지 공부도하고 무 인수 코딩을 위해 노력을 하고 있었습니다.

그리고 오늘 간만에 글도 쓸겸해서 그 코드들을 조금 더 함수형(?)에 맞게 리팩토링 했습니다. 무인수를 지향했습니다. 좋아졌는지 더 잘보이는지는 모르겠습니다. 일단 그런건 프로젝트가 끝나고 나서 다음 프로젝트에 들어갈때 생각할 예정입니다.

Ramda.js 익숙도 상승에 따라온 무인수 코드
RR 은 단지 타입형식이 맞지 않아 회피하기 위한 용도로 선언되었습니다.

대충 이런 느낌입니다. 좀 더 무인수 코딩느낌이 강합니다. 라인은 좀 더 길어졌네요. 데피니션이 약해서 as any 가 많이 사용되었지만 걷어내고 보시면 살짝 클로져(clojure) 느낌도 있는거 같고 그렇습니다.

변수선언 방식의 함수 선언을 하다보니 호이스팅이 안되는 이유로 export 함수들이 아래쪽에 가게 되는 것은 조금 아쉽지만 이런건 파일 레벨에서 잘 관리하면 될 것으로 보고 있습니다.

그리고 이게 익숙해질 수록 뭔가 파이프를 연결하는 그럼 느낌이 강해서 재미도 꽤나 있습니다.

파이프 연결해서 물을 흐르게하는 게임, 출처 http://mailgame.tistory.com/683

요즘 프로젝트를 진행하면서 사소한 것들은 타입 데피니션에 컨트리뷰션을 하고는 있는데 데피니션이 성치가 않습니다. 함수 인자에 대한 설명이 반대로 되어 있다거나...(어이없..)

회사원이었으면 업무 끝나고 의미있는 컨트리뷰션을 할 수 있었을텐데 하는 아쉬움이 있있네요. 지금은 여유가 없으므로…

타입과 관련해서는 레포지터리가 두개가 있습니다.흔치는 않은데… https://github.com/types/npm-ramda 이쪽이 더 리드하는 느낌인데 전 따로 설정을 해줘야하는 번거로움이 있어서 익숙한 https://github.com/DefinitelyTyped/DefinitelyTyped 껄 쓰고 있습니다.

실제로 프로젝트에 쓰려는거면 선택의 폭이 있다는 걸 알고 있으면 좋을 것 같습니다.


글에 사용된 코드의 저장소입니다.

도입의 장단점

장점

  • 정해진 함수를 통해 코딩하므로 타인의 코드와 내 코드 사이에 갭이 준다
  • 메소드를 서로 잘 알기만 하면 타인의 코드건 뭐건 바로 읽힐껏만 같다
  • Side-effect 프리, 불변성으로 인한 버그감소

단점

  • 높은 러닝 커브, 익숙해질 때까지의 시간
  • 많은 수의 메소드, 출 퇴근시에 그냥 책 처럼 모든 메소드를 읽으면서 다녀야..
  • 타입스크립트와 맞지 않음, 푸어 타입 데피니션
  • 과도하게 R. 로 시작하고 싶어서 어렵게 짜는 로직도 발생

Next?

강제 ramda.js 적응, 적용을 했으니, 이제 진수인 에러 처리를 하러 가면 될 것 같다.

한마디 더

메소드 공부가 필요하지만 함께 일하는 동료에게 불변성에 장점을 설득시키고 사이드 이펙트는 무서운 것이란 걸 설득하는 것 보단 덜 힘들수가 있습니다.

자바스크립트는 const 와 관계없이 동적 수정이 가능하고 엔지니어의 레벨도 가지 각색이기 때문에 고통 요소가 많은데 ramda.js 가 가진 순수성, 불변성은 이를 해결하는데 도움이 됩니다.

처음 도입의 목적이었던 리뷰에서도 속도 개선이 있었습니다. 제 코드와 상대방의 코드도 비슷해지고 컨벤션에 대한 논쟁도 필요 없어졌구요.

테스트하기도 용이해져서 프로젝트 규모가 커지고 재사용 함수들이 많아지면 점점더 파워풀 해질 것 같은 기대감도 있네요.

만약 도입하려고했는데 코딩 시작 자체가 안된다고 한다면 하던대로 익숙하게 코드를 작성하고 이를 ramda.js로 바꿔가는 방법을 쓰면 됩니다.

R.tryCatchPromise 처리를 하기 위한 삽질등에 대한 글도 남겼어야했는데 적고 보니 없군요. 껄껄..

다 좋은데 코딩 속도가 떨어졌습니다. 이건 익숙해지면 다시 올라오겠죠? 🙇


글이 더 필요하다면