Elm — 웹 앱 전문 함수형 프로그래밍 언어

김대현
HappyProgrammer
Published in
13 min readSep 26, 2018

지난 달 Elm(엘름)이라는 함수형 프로그래밍 언어를 접하고, 개인 홈을 개편하며 글을 한 편 올렸습니다. 이어서 N사에서 Elm을 소개하는 발표를 했고, 그 발표 자료를 정리하며 다시 미디엄에 올립니다.

(슬라이드에 이어 아래에 설명을 붙이겠습니다.)

저는 백엔드 중심 개발자입니다. 십여 년 전, AJAX라는 버즈워드가 유행하던 시절, 지금은 카카오가 돼버린 다음커뮤니케이션이란 회사에서 캘린더 서비스를 만들면서 에 프런트 개발도 약간 했었습니다만 요새의 프런트엔드 기준에서 보자면 아마도 구석기시대에 프런트 개발을 해본 사람일 겁니다. 그 이후 용케 프런트 개발은 할 필요 없이 지냈습니다.

그걸 갖고 프런트 개발 해봤다고 할 수 있나?

그런데 개인 프로젝트로 웹 앱 개발을 하자니 해야 할 일이 다양합니다. 백엔드야 원래 전문 분야니 직접 하면 되고, 서버 운용 관리나 DB 운용 관리는 클라우드 덕에 대폭 쉬워져서 제가 필요한 수준은 역시 직접 해도 됩니다. 그런데 프런트엔드 개발이 문제입니다. 이 바닥은 도무지 따라갈 수 없게끔 수시로 변덕스럽게 바뀌고 있는 데다, 사실 공들여 공부하고 싶은 분야가 아닌지라 이제와 직접 하자니 막막합니다. 그러 상황에 유용해 보이는 Elm을 만나서, 이렇게 다른 분들께 소개까지 하게 되었습니다. Elm은 사전을 찾아보니 ‘느릅나무’란 뜻이고, 발음은 ‘ㄹ’이 거의 들리지 않는 ‘엚’ 쯤 되는 것 같습니다만, 그냥 엘름이라고 부르겠습니다.

오늘 발표 내용과 순서입니다.

1. 엘름 소개

엘름은, 자바스크립트로 컴파일되는 함수형 프로그래밍 언어입니다. 자바스크립트를 써서 컴파일한다는 의미가 아니라, 컴파일된 결과코드가 자바스크립트라는 의미입니다. 타입스크립트나 클로저스크립트, 그리고 퓨어스크립트 같은 선상에 있습니다. 그리고, React가 프로그래밍 언어는 아니지만, React도 웹사이트나 웹앱을 만드는 용도의 관점에서는 경쟁관계에 있다 말합니다.

자바스크립트로 컴파일된다는 의미는, 웹 브라우저에서 실행한다, 그리고 Node.js 환경에서 실행한다는 의미가 있다는 점을 짚고 넘어갑니다.

우선 코드의 모습을 살짝 보여드릴게요. 단순 카운터 웹 앱 예제입니다. 자세한 문법 소개는 잠시 후에 드리기로 하고, 모양새가 이렇다고만 봐주시면 좋겠습니다.

엘름의 장점을 말씀드릴게요. 컴파일 타임에 거를 수 있는 에러는 깐깐히 다 거르기에 런타임 에러가 아예 없습니다. null이나 undefined를 아예 허용하지 않기에 관련한 런타임 에러가 발생할 일이 없습니다. 그리고, 컴파일러가 친절하게 미리 에러 메시지를 출력하기에 새로운 기능을 더 신속하게 추가할 수 있습니다. 딱 정해진 아키텍처에 별다른 여지가 없어서 앱의 규모가 커지더라도 계속 잘 정돈된 아키텍처를 유지할 수 있습니다. 그리고 요새의 언어답게 모든 Elm 라이브러리 패키지에 유의미 버전을 강제하고 있어서, 의존성 관리가 편리합니다.

또 무엇보다, 배우고 활용하기 쉽다는 점이 최대 장점이겠습니다.

2. 개발 환경

현재 최신 버전은 0.19이고, 공식 사이트에서 맥이나 윈도용 설치 패키지를 받을 수 있습니다. 맥의 경우 homebrew로도 설치할 수 있습니다.

현대 대부분 에디터에 Elm용 플러그인이 있고, elm-format 플러그인을 추가하거나 기본 elm 플러그인의 옵션을 켜면 자동 포맷팅 기능을 쓸 수 있습니다. (단축키 이용 or 저장할 때 자동)

터미널에서는 레플이나 개발용 웹 서버, 빌드 툴을 돌릴 수 있는데 elm에 이어 커맨드를 추가로 붙여서 실행합니다.

다음, 언어 기본 문법 설명드릴게요. 먼저 기본 값으로는 다른 언어들과 크게 다르지 않은 모습으로 문자열숫자, 그리고 불린을 쓸 수 있고요.

함수형 언어답게, 함수도 기본 값입니다. isNegative라는 이름으로 함수를 정의한 것인데, n이라는 하나의 인수를 받아서, 그 n이 0보다 작은 지를 비교한 결과 불린 값을 반환하는 일을 합니다. 레플(repl) 첫째 줄처럼 함수를 정의하면 곧바로 선언한 내용의 타입이 화면에 보이는데, 정의한 것이 함수(function)이고, 숫자(number)를 하나 받아서 불린(Bool) 값을 되돌려주는 것이라는 표현입니다. 둘째 줄에서는 이어서, 그 함수를 호출하는 모습입니다. isNegative라는 함수에 4라는 인자를 넘겼고, 그러면 그 결괏값은, False라는 값이 나옵니다. 마지막 줄에서는 인수를 넘길 때 괄호로 감싸는 걸 강조해 보이고 있습니다. 다른 언어와는 괄호의 위치가 좀 다르게 보일 수도 있습니다. 함수 호출의 문법이 쉼표(,) 없이 이어서 붙이기 때문입니다.

이름 없이 만들어 쓰는 익명 함수는 람다 기호와 비슷한 역슬래시 기호를 써서 인수부를 표현하고 화살표 기호(->)로 함수 본문을 잇습니다. (제 터미널 창 글꼴이 ligature를 쓰는 D2Coding 글꼴이라 한 문자로 보이지만 -와 >를 이은 두 문자입니다). 역시 괄호 위치가 조금 생소할 수 있는데요, 함수 본문이 어디서 끝날 지 문법적으로 애매하기에 괄호로 익명 함수를 묶어 쓰고, 그 뒤에 4라는 인수를 넘긴 모습입니다.

조건문은 여느 언어와 크게 다르지 않습니다. 다만, 조건문 분기의 기준인 조건식에는 순수히 불린 결과만 허용합니다. 이점은 자바 같은 언어와 마찬가지이지만, 여느 다른 언어들이 조금 관대하게 참 거짓 값을 판명하는 점과는 다릅니다.

기본 값으로 리스트를 만들어 쓸 수 있는데, 다른 값들을 대괄호로 감싸면 됩니다. 중요한 점은 리스트의 각 요소들은 동일한 타입이어야 한다는 점입니다. 리스트를 다루는 함수들은 List패키지에 있습니다.

튜플은 정해진 개수의 다양한 타입 값이 들어갑니다. 위 예제는 문자열 하나, 숫자 하나를 짝지은 두 개짜리 튜플입니다. 이름 문자열과 생년 숫자를 묶어 한 사람의 이름과 생년을 표현했습니다.

두서넛 값을 묶어서 튜플로 표현해도 되지만, 두서너 값이 넘어가면 위치로만 값을 접근하기는 불편합니다. 또 명시적 이름이 있어야 좋을 때도 많으니, 필드 이름을 지정하는 레코드를 씁니다. 미리 정한 구성의 키 값 집합인데요. 위 첫 번째 예제에서는 x, y 좌표값을 담아서 2차원 평면의 점을 표현했습니다. 그다음은 사람의 이름과 나이를 nameage라는 필드명으로 eugene이라는 값을 표현했습니다. eugene이라는 레코드 값의 name 필드에 접근하면 “유진”이란 문자열 값이 나타납니다.

특이하게, 필드에 접근하는 함수가 자동으로 만들어진다는 점이 있습니다. .name이라는 함수가 있어서, 해당 레코드의 필드에 접근하는 일을 해줍니다. List 패키지에 있는 map함수에 .name이라는 함수를 넘겨서, eugene값이 세 개 들어있는 리스트에 사상(map)을 했고요, 그러면 “유진”이라는 값이 세 개 들어있는 리스트가 나옵니다.

다음은 레코드를 다룰 때 편리한 패턴 매칭 기능인데요, under40라는 함수를 정의하면서 인수로 받을 위치에 중괄호를 감싼 age를 받은 다음 본문을 정의했습니다. 이 경우, 숫자형 age 필드가 있는 레코드를 받아서 그 값을 활용하는 함수인 건데요, eugene이라는 레코드를 넘겨 보면 나이가 40 이상이기에 거짓(False) 값이 나오는 것을 볼 수 있습니다. 즉석에서 “고애신” 레코드를 만들어 넘겨보면, 29살로 40 미만이기에 참(True) 값이 나옵니다.

레코드를 업데이트할 때에는 중괄호 안에 원래 레코드를 넣고, 기호 ‘|’를 넣고 업데이트할 필드와 값을 이어 넣습니다. 첫 번째 예제는, eugene 레코드 값에서 name 필드만 “유진초이”로 바꾼 레코드를 만드는 식입니다. 이어서 eugene레코드의 age필드를 20으로 바꾸어 보았는데요, 이 경우 원래 name필드 값 “유진”이 남아있는 걸 확인할 수 있습니다. 레코드 필드 값을 바꾸더라도 원래 레코드 값은 바뀌지 않고, 그 일부가 바뀐 새로운 레코드를 만들게 됩니다. 함수형 언어들이 꼭 쓰게 되는 불변 존속 자료형이지요.

레코드는 사뭇 자바스크립트 오브젝트와 비슷한데요, 차이점은 이렇습니다. 자바스크립트의 오브젝트와는 달리, 없는 필드에는 접근할 수 없고 (컴파일 에러), 필드 값이 undefined나 null일 수 없습니다. 그리고, this나 self 키워드로 재귀 구조를 만들 수 없다는 내용이 있습니다. (이 부분은 저는 잘 모르겠습니다. 자바스크립트에서는 필드 값에 다시 레코드 변수를 지정해서 재귀 구조를 만들 수 있나 본데요, 어떨 때 쓰는 건지 잘 모르겠으니, 아시는 분은 댓글로 알려주시면 감사하겠습니다. 아무튼, 엘름에서는 허용되지 않는다 합니다.)

4. 엘름 아키텍처

다음으로 엘름 웹앱의 아키텍처를 알아보겠습니다.

엘름은 웹 앱을 만들기 위한 기본 골격이 다 갖춰져 있습니다. 그에 맞춰 개발하면 복잡한 웹 앱을 만들면서도 새로운 기능을 쉽게 추가하고 기존 코드를 리팩터링 할 수 있습니다. 이미 잘 고민해서 구현해 놓은 아키텍처가 있기에 내가 뭔가 새롭고 어려운 아키텍처를 구성하지 않아도 됩니다. ReduxElm의 영향을 받았다고 하니, 관련해서 익숙하신 분들은 거부감 없이 받아들일 수 있습니다.

아키텍처 기본은 모델, 업데이트, 뷰로 나뉩니다. 모델은 흔히 레코드를 쓰는데, 전체 애플리케이션의 상태를 담습니다. 업데이트는 원래 모델과 어떤 행위를 받아서 새로운 상태로 전이하는 함수입니다. 뷰는, 지금 모델에 담겨 있는 상태를 HTML로 그려내는 함수입니다.

이쯤에서, 아까 단순 카운터 예제를 다시 보겠습니다. Counter라는 이름으로 모듈을 나누어 정의했는데, 이 부분은 설명하지 않았으니 넘어가겠습니다. 다른 언어에서의 패키지, 네임스페이스, 모듈과 비슷합니다. 그다음 다른 모듈의 기능을 불러다 쓰기 위한 import구문이 있는데 역시 모양만 보고 넘어가고요, 다음 main함수가 이 예제의 진입점입니다. 브라우저와 연동되어 초기화하고 구성하는 함수를 부르고 있고요, 엘름 아키텍처의 모델, 업데이트, 뷰가 이하 어떤 함수로 연결되는지를 레코드로 묶어 전달하고 있습니다.

그다음, Msg라는 타입을 정의했는데, 이 커스텀 타입에는 Increment 또는 Decrement라는 값이 들어 갈 수 있습니다. 다른 언어의 Enum과 비슷하다고 볼 수 있습니다.

다음으로, update함수를 정의했는데, 이 함수는 msgmodel, 두 개의 인수를 받습니다. msgIncrement인지 Decrement인지에 따라 원래 모델 값에서 1을 더하거나 빼서 새로운 모델 값을 돌려줍니다. main함수에서와 여기서 보셨겠지만, 이 웹 앱은 모델 값으로 단순히 하나의 숫자를 쓰고 있습니다. 이 단순한 웹 앱은 0에서 시작한 숫자를 모델로 증감 행위를 다루고 있습니다.

마지막으로 뷰 함수인데요, 그 단일 숫자를 담고 있는 모델을 받아서 HTML로 그리는 부분입니다. div라는 함수를 이용해서 HTML div 엘리먼트를 그리고, 그 안에 button, div, button 세 개의 요소를 담았으며, onClick 이벤트에서는 Decrement/Increment 메시지를 만들 수 있게 준비했습니다.

view함수의 onClick 부분이 실행되면 원하는 메시지가 발생해서, update함수에 전달되고, 그 결과에 따라 새로운 모델 값으로 상태가

바뀌며, 그 바뀐 상태에 따라 다시 달라진 HTML 뷰가 그려지는 흐름입니다.

이런 흐름을 내부적으로 Browser.sandbox라는 함수가 다뤄주고 있고요, 전체로 보자면, Elm 프로그램 영역에서 모델로 만들어낸 뷰 (Html) 값을 엘름 런타임에 전달하면, 이 런타임은 내부적으로 버추얼 돔(DOM)으로 관리하며 웹브라우저를 위한 DOM을 그려내고, 관련한 이벤트가 발생하면 이 이벤트를 Msg 형태로 바꾸어서 다시 엘름 프로그램으로 전달하는 구조입니다.

그렇게 엘름 프로그램 영역은 철저히 함수형으로 남아 있고, 상태 값이 바뀌며 얽히고설키는 부분은 런타임이 대신 처리해주고 있습니다.

5. 연습 프로젝트

연습 프로젝트로, 지난번 글에 소개한 개인 홈페이지 개편 작업에 활용해 봤습니다. 그 글 이후 이 발표 전에 엘름이 0.18 버전에서 0.19로 업데이트된 것을 알아차려서, 업데이트 작업도 조금 했고요.

사이드 프로젝트로 무언가 만들어 보고 있습니다. 기대만큼 마음에 들어서 당분간 프런트엔드 개발할 때는 계속 쓸 것 같습니다.

6. 그래서 뭐가 좋나?

엘름의 장점을 다시 정리하자면, 다 갖춰진 웹 앱 개발 구조에 맞추어 큰 고민 없이 쉽게 현대적인 웹 앱을 만들 수 있으며, 철저히 함수형이기에 함수형 프로그래밍의 강점을 누릴 수 있으며, 강력한 타입으로 체계적인 값을 다룰 수 있고, 웹 앱 전문 라이브러리가 잘 갖춰져 있어서 프런트엔드 웹 앱 개발을 하기에 강력하다고 말할 수 있겠습니다.

한편, 엘름의 단점으로는, 자바스크립트 환경과 철저히 나뉘어 운용되기에 필연적으로 발생하는 점이 있습니다. 기존 자바스크립트 라이브러리를 활용하려면 마치 브라우저 환경에서 멀티스레드 프로그래밍할 때 쓰는 웹 워커 스타일로 해야만 합니다. 메시지를 전달하고 콜백 함수를 걸어 놓는 방식으로 상호 작용하기에, 순수 자바스크립트 라이브러리를 적극 활용해야만 하는 경우에는, 안 되는 것은 아니지만, 번거로울 수 있습니다.

결국 엘름은 저처럼, 함수형 언어의 유용성을 기대하는 백엔드 개발자가 프런트까지 개발해야 할 경우에 아주 유용할 것 같습니다. 또는 정신없는 프런트엔드 프레임워크 선택에 골치 아팠던 프런트 개발자가 새로운 프로젝트를 시작할 때도 고려해 볼만한 선택지인 것 같고요.

이상 함수형 웹 앱 프로그래밍 언어 엘름에 대해 소개해 드렸습니다. 엘름에 대해 궁금하셨던 분들께 조금이나마 도움이 되었기를 기대합니다.

감사합니다.

--

--

김대현
HappyProgrammer

시니어 백엔드 개발자. 함수형 프로그래밍을 선망하며 클로저, 스칼라, 하스켈로 도전하며 만족 중. 마이너리티 언어만 쫓아다니면서도 다행히 잘 먹고 산다. 최근엔 러스트로 프로그래머 인생 확장.