<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"
    xmlns:dc="http://purl.org/dc/elements/1.1/">
    <channel>
        <title>Harfang's Perch</title>
        <link>https://harfangk.dev</link>
        <description><![CDATA[On software in general, mostly functional]]></description>
        <atom:link href="https://harfangk.dev/ko/rss.xml" rel="self"
                   type="application/rss+xml" />
        <lastBuildDate>Mon, 11 Oct 2021 00:00:00 UT</lastBuildDate>
        <item>
    <title>프로덕트 매니저로서 얻은 것과 앞으로의 방향</title>
    <link>https://harfangk.dev/ko/posts/2021-10-11-where-i-am-as-a-pm-and-what-comes-next.html</link>
    <description><![CDATA[<p>흔해빠진 말이지만 저도 제 일을 하는 게 꿈입니다. 이미 한 번 꽤 무모하게 시도해봤고 실패했죠. 배운 건 많았지만 매우 고통스러운 경험이었습니다. 그래서 이제는 조금 더 계획적으로 접근하고 있습니다. 프로덕트 매니저로 일하기로 결정한 것도 그 일환이구요. 이번 롤에서 바란 것은 성공하는 제품을 그려내고, 계획하고, 실제로 만들어내는 방법을 배우는 것이었습니다. 이제 1년 반 가까이 PM으로 일해왔는데, 무엇을 얻었고 이어서 무엇을 할지 회고해보기 좋은 시점이라고 생각해서 이 글을 씁니다.</p>
<!--more-->
<p>PM이 갖춰야할 역량에 대한 글은 굉장히 많은데, 제가 가장 명쾌하다고 보는 것은 Ravi Mehta가 쓴 글입니다. 필요한 역량을 4개 영역의 12개 역량으로 분류하는데, 각각 무엇인지, 그리고 PM으로 성장하면서 어떤 커리어 단계에 어떤 역량이 중요한지에 대한 설명을 <a href="https://www.ravi-mehta.com/product-manager-skills/">여기</a>서 읽어보길 추천합니다.</p>
<p><img src="https://www.ravi-mehta.com/content/images/2020/04/product_competencies-1.png" /></p>
<p>PM 일을 처음 시작할 때, 예상했던 것들이 있습니다. 위 방식대로 정리하자면 제품 실행과 고객 이해 영역에 속한 역량은 빠르게 습득할 수 있을 것이라고 생각했습니다. 제품 실행 영역은 소프트웨어 개발자로서 일했던 경험을, 고객 이해는 심리학과 HCI를 전공하면서 배웠던 것을 활용할 수 있을테니까요. 사람 관리 영역에는 앞의 두 영역만큼 자신감이 있지는 않았지만 통번역가로 일하면서 습득한 의사소통 능력이 도움이 될 것이라 기대했습니다. 제품 전략은 제가 가장 약한 부분이었기 때문에 이를 배우고 싶었습니다.</p>
<p>일 년 반이 지나보니 제품 실행과 고객 이해는 기본적인 수준은 할 수 있게 되었습니다. 사람 관리는 평가하기 조금 어렵지만 정확하고 신뢰할 수 있게 의사소통하기 위해 계속 노력하고 있고 어느 정도 습득했다고 봅니다. 단, 의사소통을 더 자주 하는 것과 팀원을 고무시키는 것은 여전히 부족합니다. 제품 전략은 애매합니다. 처음에 제품 전략이라고 생각했던 것이 알고보니 사업 전략과 제품 전략을 포괄하는 개념이었어요.</p>
<p>마티 케이건이 자신의 <a href="https://svpg.com/business-strategy-vs-product-strategy/">블로그 글</a>에서 이에 대해서 적은 바 있습니다:</p>
<blockquote>
<p>“사업 전략은 사업 목표를 설정하고, 그 목표를 달성하기 위해서 어디에 투자해야 최선인지 판단하고 결정하는 것이다. 예를 들어 직접 판매 모델(사내 영업 인력이 고객에게 직접 판매)에서 온라인 판매 모델(고객이 웹사이트에서 구매)로 바꾸는 것은 사업 전략이다. 서비스 사용료 청구 방법을 구독료 방식과 거래 건 별 요금 방식 중에서 결정하는 것이나, 광고비 기반 매출 모델을 적용할지 결정하는 것은 사업 전략이다. 인접 시장으로 진입할지 결정하는 것도 사업 전략이다.</p>
<p>그리고 각 사업 전략은 당연히 제품에 큰 영향를 미친다. 하지만 이는 별개의 영역이다. 온라인 판매 방법도, 가치를 수익화하는 방법도, 연관 상품을 개발하거나 다른 방법으로 확보해서 고객에게 제시하는 방법도 정말 많다. 사업 전략을 어떻게 실현할지를 다루는 것이 제품 전략이다.</p>
<p>또한 사업부에서 엄청난 사업 기회가 있다고 생각하더라도 회사가 그 기회를 성공적으로 가져갈 수 있을지는 알 수 없다. 개발하기 너무 비쌀 수도 있고, 고객이 돈을 주고 구매할 가치를 못 느낄 수도 있다. 제품 전략, 특히 제품 발견이 중요해지는 지점이다.”</p>
</blockquote>
<p>제품 전략 영역에서로 실력이 늘기는 했습니다. 사업 전략을 실행하기 위해서 사업 효과에 따라서 기능 우선순위를 정하고, 사업 목표를 달성하기 위해 일관적인 월간 계획을 짜고, 결과를 내는 등 말이죠. 최소한 무엇을 모르는지는 아니까 초보자는 벗어났다고 봅니다. 하지만 그건 제가 얻고 싶은 역량 중 절반에 불과하다는 문제가 있습니다.</p>
<p>사업 전략은 제품 전략보다 더 상위 영역을 다룹니다. 마티 케이건의 표현을 빌리자면, 사업 목표를 설정하기 위해서는 시장과 사업 흐름을 더 잘 이해해야 하고, 어디에 투자할지 판단하고 결정하려면 사업 운영과 자산 관리를 더 잘 해야 합니다. 제가 배우고자 했던 나머지 절반의 역량이죠. 그리고 사업을 성공적으로 하려면 꼭 필요한 소양이니 그 역량을 키우는데 집중하고 싶습니다.</p>
<p>문제는 지금 회사에서 PM으로서 꾸준히 성잘할 수는 있겠지만 사업 능력이라는 측면에서 보았을 때 점진적인 발전에 불과하다는 점입니다. 폭발적으로 성장하려면 앞서 말한 역량을 키워야합니다. 이를 키울 방법도 여러가지가 있지만 아무래도 사업을 가장 빠르게 배우려면 역시 사업을 해야겠죠.</p>
<p>하지만 그렇다고 당장 퇴사하고 아무 계획 없이 사업을 할 생각은 없습니다. 앞서 적었듯이 이미 해봤고, 결과가 좋지 않았거든요. 일단 지금 회사를 다니면서 <a href="https://review.firstround.com/the-minimum-viable-testing-process-for-evaluating-startup-ideas">최소 실행성 테스트</a>를 여러 차례 해볼 계획입니다. 최소한 제가 얻고자 하는 역량을 얻을 수 있을 것이고, 부업이나 제 사업을 찾을 수도 있겠죠.</p>
<p>어떤 시장에서 실험을 해볼지 정하는 것도 중요한데, 일단 NFT, 블록체인 전반, 그리고 VR 영역을 눈여겨보고 있습니다. 아직 신규 시장이면서도 충분히 자금이 유입되었기 때문입니다. 또한 세상을 바꾸겠다는 포부를 가진 사람들이 많이 일하고 있기도 한데, 그런 사람들하고 일하고 싶기도 하거든요. 목표른 해가 가기 전에 실험을 하나라도 해보는 것입니다. 아마 그 경과도 여기에 공유할테니 어떻게 되었는지도 나중에 읽어볼 수 있을 거예요.</p>]]></description>
    <pubDate>Mon, 11 Oct 2021 00:00:00 UT</pubDate>
    <guid>https://harfangk.dev/ko/posts/2021-10-11-where-i-am-as-a-pm-and-what-comes-next.html</guid>
    <dc:creator>harfangk</dc:creator>
</item>
<item>
    <title>프로덕트 매니저가 되었습니다</title>
    <link>https://harfangk.dev/ko/posts/2021-09-11-im-a-product-manager-now.html</link>
    <description><![CDATA[<p>마지막으로 글을 쓴 지 2년이 지났는데, 감사하게도 그 동안 다음 글은 언제 나오냐고 생각보다 많은 분들이 물어봐주셨습니다. 이제야 드디어 다음 글을 쓰네요. 네, 제목에 적힌 대로 이직을 하면서 프로덕트 매니저로 직종을 옮겼습니다.</p>
<!--more-->
<p>가장 큰 이유는 가치 있는 제품을 만들 때 소프트웨어 개발자로서 할 수 있는 일 이상의 일을 하고 싶어서입니다. 전 직장에서 일할 때 제가 개발한 제품이 방향을 잃고 헤메다가 망했거든요. 첫 버전을 만들 때도 여러가지 문제가 많았고, 그 때 고통 받은 첫 PM은 출시 직후에 번아웃이 와서 퇴사했습니다. 그 다음에 온 PM도 금방 퇴사했고요. 그 이후에는 수개월 간 난세가 도래했습니다. PM도 없이 제품은 표류했고, 그 와중에도 모든 팀원이 제품을 살리기 위해 정말 노력했습니다. 디자이너는 사용성을 개선했고, 개발자는 성능을 향상시키고, 마케터도 마케팅 캠페인을 실행하고 했지만 다 소용이 없었어요. 고객은 계속 빠져나갔고, 팀의 사기는 밑바닥까지 떨어졌고, 팀원도 곧 퇴사하기 시작했습니다.</p>
<p>결국 목적지가 분명하지 않으면 아무리 열심히 달려도 의미가 없다는 것을 다시금 깨달았죠. 그 상황을 더 이상 도저히 견딜 수 없어서 경영진에게 이야기하고 PM 롤을 제가 맡았지만, 그 사이에 회사의 재정 상황이 너무 악화되어 맡은 프로젝트를 끝내지 못하고 4개월만에 퇴사를 하게 되었습니다. 그 이후에는 지금 회사로 이직해서 PM으로서 일 년 반 가까이 일하고 있습니다. 그 사이에 꽤나 흥미로운 일이 많았어요.</p>
<p>PM으로 일한 지 1년 정도 지나면서 제품과 PM에 관한 글을 쓰기 시작하려 했는데, 소프트웨어 관련 글을 쓸 때보다 쓰기가 어렵더라구요. 기술 관련 글을 쓸 때는 나름 정립한 양식을 따랐습니다. 문제를 소개하고, 해결책을 제시하고, 그를 뒷받침하는 사실 또는 주장을 쓰면 명쾌한 글 하나가 나왔는데, 제품 관련 글은 아직 어떻게 써야 할지 잘 모르겠습니다. 독자는 어떤 사람인지, 구성은 어떻게 해야 할지, 길이는 얼마나 길어야 할지 모르겠다보니 글을 쓰다 말다만 하다가 몇 달이 지나버렸는데, 에이미 호이가 말하듯이 뭐가 되었든 그냥 공개해버리기로 생각을 바꿨습니다. 이전에 비하면 글의 완성도가 좀 떨어질텐데 대신 더 많이 써서 올리려고 합니다. 일단 목표는 매 주 올리는 것이니 지켜봐주세요.</p>]]></description>
    <pubDate>Sat, 11 Sep 2021 00:00:00 UT</pubDate>
    <guid>https://harfangk.dev/ko/posts/2021-09-11-im-a-product-manager-now.html</guid>
    <dc:creator>harfangk</dc:creator>
</item>
<item>
    <title>지킬에서 하킬까지 장대한 야크 털 깎기 여정</title>
    <link>https://harfangk.dev/ko/posts/2019-03-15-from-jekyll-to-hakyll.html</link>
    <description><![CDATA[<p>이 모든 것은 블로그 글에 삽입한 엘름 코드에 코드 하이라이트가 안 된다는 것을 알아차렸을 때 시작되었습니다. 문제의 근원은 간단히 확인할 수 있었습니다. 기존 블로그는 지킬 및 보조 라이브러리를 모아둔 <code>Github Pages</code> 젬을 사용해서 만들었는데, 이 중 코드 하이라이트를 담당하는 <code>rouge</code> 라이브러리가 엘름 문법을 지원하기 이전인 2.2.1 버전으로 고정되어 있었습니다. <code>rouge</code> 버전을 강제로 올려서 간단히 해결할 수도 있었겠지만, 이 참에 아예 <code>Github Pages</code> 젬을 빼버리고 모든 의존성을 최신 버전으로 올려버릴까? 라는 생각이 들었습니다. 그렇게 야크 털 깎기가 시작되었습니다.</p>
<!--more-->
<p>그런데 이왕 그럴거면 어설프게 짠 CSS도 고치는게 어떨까? 그런데 이왕 그럴거면 다른 정적 사이트 생성기를 써보는 건 어떨까? 그런데 이왕 그럴거면 최근에 새롭게 배운 언어를 써보면 어떨까? 다행히도 하스켈로 만들어진 정적 사이트 생성기인 하킬은 이미 안정화 단계에 들어섰고, 제가 필요로 하는 기능도 대부분 제공하고 있었습니다. 그리하여 블로그를 하킬로 개편하기로 결정했습니다. 이 시점에서 장대한 야크 털 깎기 여정이 시작될 것을 알았지만 에라 모르겠다하고 첫 발을 내딛었습니다.</p>
<h2 id="하스켈에서는-시행착오를-통해서-프로그램-동작방식을-배울-수-없다">하스켈에서는 시행착오를 통해서 프로그램 동작방식을 배울 수 없다</h2>
<p>하킬은 제가 처음으로 제대로 사용해본 하스켈 라이브러리인데, 익숙해지는데 생각보다 많이 어려웠습니다. 다른 언어에서 새로운 라이브러리를 살펴볼 때는 먼저 문서를 읽어서 전체적인 흐름을 파악하고, 코드를 조금씩 실행해보면서 제가 이해한 대로 라이브러리가 동작하는지 확인했습니다. 이건 무난하고 일반적인 방식이라고 생각합니다.</p>
<p>문제는 하스켈에서는 이 방법을 사용할 수 없었다는 것입니다. 하킬 문서를 읽은 후 코드를 실행해보려 했을 때, 컴파일러는 제가 작성한 코드 내의 타입이 일치하지 않는다는 에러 메시지만 주었습니다. 정작 제가 작성한 코드가 올바른지 아닌지에 대해서는 아무 것도 알려주지 않는 피드백이었기에 그다지 쓸모가 없었죠. 물론 컴파일러가 제가 무엇인가 잘못했다는 것을 알려주고 있다는 것 정도는 알 수 있었습니다. 하지만 에러 메시지는 제 실수를 하킬 타입을 사용해서 설명하고 있었고, 애초에 그 코드를 작성한 이유가 하킬의 코드와 타입이 어떻게 동작하는지 이해하기 위한 것이었다는 것을 생각해보면 딱히 도움이 되는 피드백은 아니었습니다. 에러 메시지에 나온 하킬 타입이 무엇인지 이해하기 위해서는 그저 문서와 소스 코드를 읽는 것 외에는 다른 방법이 없었습니다. 이건 마치 새로운 수학적 개념을 관련 정리와 증명을 읽어보기만 하면서 이해하려는 것과도 비슷한 느낌이었습니다.</p>
<p>또한 라이브러리를 조금이라도 사용해보려면 라이브러리의 상당 부분을 이해해야만 한다는 문제가 있었습니다. 하킬에서는 IO와 통신하는 인터페이스로 <code>hakyll :: Rules a -&gt; IO ()</code> 함수를 제공하는데, 타입에서 보면 알 수 있듯이 이를 사용하려면 <code>Rules a</code> 타입을 이해해야만 합니다. 문서에 따르면 이 타입은 “[하킬 자체의 문서 생성용] 컴파일러를 실행할 때 사용되는 규칙”에 상응합니다. 그리고 <code>Rules a</code> 타입의 데이터는 <code>match :: Pattern -&gt; Rules () -&gt; Rules ()</code>, <code>route :: Routes -&gt; Rules ()</code>, 및 <code>compile :: (Binary a, Typeable a, Writable a) =&gt; Compiler (Item a) -&gt; Rules ()</code> 같은 함수를 통해서 생성할 수 있는데, 이걸 사용하려면 또다시 <code>Pattern</code>, <code>Routes</code>, <code>Comipler a</code>, <code>Item a</code> 타입을 이해해야만 하는 등 알아둬야할 내용이 고구마 줄기처럼 연이어서 나옵니다. 물론 이렇게 연결된 개념을 계속 익혀야하는 것은 소프트웨어 분야에서 일반적인 상황이지만, 하스켈의 경우 시행착오를 통해서 조금씩 이해할 수도 없이 한 번에 이 모든 개념을 관념적으로 이해해야 했습니다.</p>
<p>다행히 하킬의 저자는 다양한 예제 및 예시를 제공했고, 이는 라이브러리를 이해하는데 정말 유용했습니다. 만약 그런 자료가 없었다면 라이브러리를 사용할 수 있을 때까지 도대체 얼마나 더 걸렸을 지 알 수 없을 정도로요. 만약 하스켈 라이브러리를 작성할 계획이라면 예시 코드를 최대한 많이 제공하도록 부탁드립니다. 하스켈 라이브러리를 시험해보는 것은 하스켈 특유의 엄격한 컴파일러때문에 어려운데, 이는 특히 초심자에게 더욱 부담으로 다가오므로 그런 어려움을 해소해줄 수 있도록 최대한 많은 도움이 필요합니다.</p>
<h2 id="국제화는-언제나-어렵다">국제화는 언제나 어렵다</h2>
<p>기존 블로그와 마찬가지로 이번에도 구현하는데 가장 어려운 것은 국제화였습니다. 하킬에서 이를 기본 제공하지 않기 때문에 직접 구현해야만 했는데, 구글해서 찾은 기존 구현체는 그다지 마음에 들지 않았습니다. 해당 구현체에서는 원본 마크다운 파일 안에 모든 언어의 텍스트가 들어있었고, 각 텍스트 앞에는 그 언어명이 표기되어 있었습니다. 그리고 사용하지 않는 언어의 텍스트는 언어명을 이용해 확인한 후 유닉스 <code>sed</code> 명령어를 통해서 제거하고 있었습니다. 대충 아래와 같은 구성이었죠.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode md"><code class="sourceCode markdown"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="an">Fr:</span><span class="co"> ## Bienvenue</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="an">En:</span><span class="co"> ## Welcome</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="an">Fr:</span><span class="co"> Bienvenue sur mon site.</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="an">En:</span><span class="co"> Welcome on my website.</span></span></code></pre></div>
<p>일단 여러 언어의 텍스트를 섞어놓는 것이 마음에 들지 않았습니다. 난잡해 보일 뿐 아니라 제가 글을 쓰는 방식하고도 맞지 않았습니다. 그래서 새로운 구조를 생각해냈는데, 각 글의 제목에 따른 폴더를 만들고 그 아래에 언어별로 파일을 만드는 방식이었습니다. 예를 들어 이 글의 마크다운 파일은 <code>posts/2019-03-15-from-jekyll-to-hakyll/en.md</code>와 <code>posts/2019-03-15-from-jekyll-to-hakyll/ko.md</code>라는 두 파일로 구성되어 있습니다. 일단 해당 파일을 찾아서 적절한 HTML 파일로 변경하도록하는 것까지는 수월했습니다.</p>
<p>하지만 각 글마다 언어 변경을 위한 링크가 필요하다는 문제가 발생했죠. 링크를 만들기 위해서는 하킬 프로그램에서 변환 중인 마크다운 파일이 들어있는 폴더 내에 어떤 파일들이 존재하는지를 알아야 했는데, 이는 하스켈 상에서 IO 타입 내에 표현되어야하는 IO 작업이었습니다. 따라서 <code>main :: IO ()</code> 함수 내에서 모든 글의 폴더와 파일의 목록을 작성했고, 마크다운 파일을 변환할 때마다 해당 목록을 활용해서 언어 변경 링크를 생성해야 했습니다.</p>
<p>또한 템플릿 내에 실제로 링크를 만드는 부분에서도 문제가 발생했습니다. 하킬 템플릿에 언어별 링크 텍스트와 URL을 담은 튜플이나 레코드 타입 데이터의 목록을 전달할 수 있었으면 편했을텐데, 하킬 템플릿이 받는 데이터 타입은 <code>Item a</code> 뿐이었는데 이는 특정한 내용과 해당 내용을 가리키는 식별자를 나타내는 타입이었습니다. 따라서 “en” 혹은 “ko”를 식별자로 가지되 내용은 비어있는 <code>Item String</code> 타입의 목록을 만들고, 이를 템플릿에 전달하여 링크를 만들도록 했습니다. <code>Item a</code> 타입을 잘못 사용한 것은 아니지만 링크를 만드는 방법이 불필요하게 번잡하다는 느낌을 지울 수는 없었습니다.</p>
<h2 id="버티컬-리듬과-rem-기반-레이아웃">버티컬 리듬과 Rem 기반 레이아웃</h2>
<p>버티컬 리듬이란 개념은 기존 블로그를 만드는 과정에서 다른 사람의 <a href="https://www.sylvaindurand.org/improving-typography-on-jekyll">글</a>을 보고 알게 되었는데, 그 사람의 블로그 글의 자간 및 행간이 너무나 마음에 들어서 허락을 받고 관련 CSS를 복사해왔었습니다. 당시에는 CSS를 잘 몰라서 있는 그대로 통째로 가져왔죠. 하지만 지금은 CSS에도 훨씬 익숙해졌기 때문에 버티컬 리듬을 제대로 이해하고 새 블로그에 직접 적용해보기로 했습니다. 그 과정에서 큰 도움이 된 <a href="https://zellwk.com/blog/responsive-vertical-rhythm/">글</a>에 나온 조언에 따라서 기본적인 버티컬 리듬을 적용했는데, 원래 사용하던 CSS의 저자인 Sylvain이 작성한 버티컬 리듬보다는 훨씬 단순하지만 제가 이해하고 관리할 수 있는 정도로 만들었습니다. 또한 레이아웃도 마음에 들었던 다른 <a href="https://sonnym.github.io">블로그</a>에서 허락을 받고 따와서 Rem 기반 레이아웃을 만들었습니다.</p>
<h2 id="깃헙-페이지에서-넷라이피로">깃헙 페이지에서 넷라이피로</h2>
<p>호스팅도 깃헙 페이지에서 넷라이피(Netlify)로 옮겼는데, 가장 큰 이유는 넷라이피에서 리다이렉션 룰을 제공하기 때문입니다. 이를 통해 사용자의 HTTP 헤더를 확인하고 <code>/ko/index.html</code> 또는 <code>/en/index.html</code>로 적절히 보내도록 할 수 있게 되었습니다. 깃헙 페이지에서는 해당 기능이 없어서 영어는 <code>index.html</code>에서, 한국어는 <code>index_ko.html</code>에서 제공했는데 처음부터 굉장히 마음에 안 들었었습니다. 게다가 넷라이피는 리포지토리에 새로운 소스 코드를 푸시하면 자동적으로 빌드 및 배포하는 기능을 비롯해 깃헙 페이지의 강점은 모두 가지고 있는데 더해 추가적인 기능도 지원하기 때문에 깃헙 페이지보다 모든 면에서 나은 선택이라고 생각합니다.</p>
<h2 id="엘름-코드-하이라이트는-여전히-안된다는-반전">엘름 코드 하이라이트는 여전히 안된다는 반전</h2>
<p>블로그를 다시 만든 후에야 알아차렸는데 엘름 코드 하이라이트가 여전히 안 되고 있었습니다. 하킬은 마크다운을 HTML으로 변환할 때 Pandoc 이라는 하스켈 라이브러리를 사용하는데, Pandoc은 코드 하이라이트 기능을 위해 Skylighting이라는 라이브러리를 사용합니다. 그리고 이 Skylighting 라이브러리에는 엘름을 다루는 문법 분석기가 없었습니다. 기껏 열심히 하스켈 타입들을 공부하고 사용하고 새로운 CSS 기법도 적용해서 새 블로그를 만들어놨더니 다시 원점이라는 말이었죠. 솔직히 많이 허탈하긴 했는데, 이왕 여기까지 온거 엘름 문법 분석기까지 만들었고, 지금은 Skylighting 라이브러리와 KDE의 코드 하이라이트 라이브러리에서 리뷰를 받고 있는 중입니다.</p>
<h2 id="이렇게까지-해야만-했는가">이렇게까지 해야만 했는가?</h2>
<p>결국 블로그 개편은 생각보다 훨씬 오래걸렸는데, 역시나 초보자가 쓸만한 정적 사이트로 하킬을 추천하지는 않을 것 같습니다. 하스켈 특유의 진입장벽때문에 정적 사이트 생성기로 쓰기는 적절하지 않은 것 같고, 차라리 고나 자바스크립트로 만들었다면 더 빨리 만들었을 것 같습니다. 하지만 실제로 하스켈 프로그램을 작성해보기에는 정말 좋은 연습용 프로젝트라고 생각합니다. 그러니 정적 사이트 생성기를 사용하는게 이미 익숙한데 하스켈을 연습해보고 싶다면 하킬을 써보는 것을 추천합니다. 추상화도 직관적이고 예제나 예시도 많이 있으니까요.</p>]]></description>
    <pubDate>Fri, 15 Mar 2019 00:00:00 UT</pubDate>
    <guid>https://harfangk.dev/ko/posts/2019-03-15-from-jekyll-to-hakyll.html</guid>
    <dc:creator>harfangk</dc:creator>
</item>
<item>
    <title>Elm에서 동시에 여러개의 Msg를 호출하기</title>
    <link>https://harfangk.dev/ko/posts/2019-01-13-elm-multiple-msgs.html</link>
    <description><![CDATA[<p>Elm Architecture에서 <code>update</code> 함수는 <code>Model</code> 상태를 변경하는데 사용됩니다. <code>Model</code>, <code>Msg</code>, <code>update</code>의 구조에 따라서 <code>update</code> 함수를 호출한 뒤에 다른 <code>Msg</code>를 인자로 삼아 <code>update</code> 함수를 다시 호출해야할 수도 있습니다. <code>update</code>를 재귀적으로 호출하는 것은 간단히 할 수 있습니다.</p>
<!--more-->
<div class="sourceCode" id="cb1"><pre class="sourceCode elm"><code class="sourceCode elm"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">Msg</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>    <span class="op">=</span> <span class="dt">FirstMsg</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>    <span class="op">|</span> <span class="dt">SecondMsg</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="fu">update</span> : <span class="dt">Msg</span> <span class="op">-&gt;</span> <span class="dt">Model</span> <span class="op">-&gt;</span> ( <span class="dt">Model</span><span class="op">,</span> <span class="dt">Cmd</span> <span class="dt">Msg</span> )</span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="fu">update</span> <span class="fu">msg</span> <span class="fu">model</span> <span class="op">=</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a>    <span class="cf">case</span> <span class="fu">msg</span> <span class="cf">of</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a>        <span class="dt">FirstMsg</span> <span class="op">-&gt;</span> </span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a>            <span class="fu">update</span> <span class="dt">SecondMsg</span> <span class="fu">model</span></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a>        <span class="dt">SecondMsg</span> <span class="op">-&gt;</span> </span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a>            ( <span class="fu">model</span><span class="op">,</span> <span class="dt">Cmd</span><span class="op">.</span><span class="fu">none</span> )</span></code></pre></div>
<p>하지만 여러 개의 <code>Msg</code>를 호출하고 싶을 수도 있습니다. 그럴 때에는 <code>Task</code> 를 사용하면 됩니다.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode elm"><code class="sourceCode elm"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">Msg</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>    <span class="op">=</span> <span class="dt">FirstMsg</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>    <span class="op">|</span> <span class="dt">SecondMsg</span> ()</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>    <span class="op">|</span> <span class="dt">ThirdMsg</span> ()</span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a><span class="fu">update</span> : <span class="dt">Msg</span> <span class="op">-&gt;</span> <span class="dt">Model</span> <span class="op">-&gt;</span> ( <span class="dt">Model</span><span class="op">,</span> <span class="dt">Cmd</span> <span class="dt">Msg</span> )</span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a><span class="fu">update</span> <span class="fu">msg</span> <span class="fu">model</span> <span class="op">=</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a>    <span class="cf">case</span> <span class="fu">msg</span> <span class="cf">of</span></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a>        <span class="dt">FirstMsg</span> <span class="op">-&gt;</span> </span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a>            <span class="kw">let</span></span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a>                <span class="fu">cmd</span> <span class="op">=</span></span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a>                      <span class="dt">Cmd</span><span class="op">.</span><span class="fu">batch</span></span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a>                          [ <span class="dt">Task</span><span class="op">.</span><span class="fu">perform</span> <span class="dt">SecondMsg</span> (<span class="dt">Task</span><span class="op">.</span><span class="fu">succeed</span> ())</span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a>                          <span class="op">,</span> <span class="dt">Task</span><span class="op">.</span><span class="fu">perform</span> <span class="dt">ThirdMsg</span> (<span class="dt">Task</span><span class="op">.</span><span class="fu">succeed</span> ())</span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a>                          ]</span>
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a>            <span class="kw">in</span></span>
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a>                ( <span class="fu">model</span><span class="op">,</span> <span class="fu">cmd</span> )</span>
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true" tabindex="-1"></a>        <span class="dt">SecondMsg</span> <span class="op">-&gt;</span> </span>
<span id="cb2-20"><a href="#cb2-20" aria-hidden="true" tabindex="-1"></a>            ( <span class="fu">model</span><span class="op">,</span> <span class="dt">Cmd</span><span class="op">.</span><span class="fu">none</span> )</span>
<span id="cb2-21"><a href="#cb2-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-22"><a href="#cb2-22" aria-hidden="true" tabindex="-1"></a>        <span class="dt">ThirdMsg</span> <span class="op">-&gt;</span></span>
<span id="cb2-23"><a href="#cb2-23" aria-hidden="true" tabindex="-1"></a>            ( <span class="fu">model</span><span class="op">,</span> <span class="dt">Cmd</span><span class="op">.</span><span class="fu">none</span> )</span></code></pre></div>
<p>이 때 <code>SecondMsg</code>와 <code>ThirdMsg</code> 사이에 호출 순서가 보장이 안 된다는 문제, 그리고 이렇게 호출되는 <code>Msg</code>는 인자를 필요로 한다는 문제가 발생합니다. 무엇보다 중요한 것은 재귀적으로 <code>update</code> 함수를 호출하는 것이 좋은 패턴이 아니라고 생각합니다. 정말 다른 방법이 없을 때만 이 방식을 사용해야 합니다.</p>]]></description>
    <pubDate>Sun, 13 Jan 2019 00:00:00 UT</pubDate>
    <guid>https://harfangk.dev/ko/posts/2019-01-13-elm-multiple-msgs.html</guid>
    <dc:creator>harfangk</dc:creator>
</item>
<item>
    <title>엘름에서 포트 함수 관리하기</title>
    <link>https://harfangk.dev/ko/posts/2018-12-19-elm-managing-ports.html</link>
    <description><![CDATA[<p>엘름에서 포트 함수 관련 코드를 어떻게 하면 깔끔하게 관리할 수 있을지 고민하다가 두 가지 방식을 정리해보았습니다. 독자가 엘름 아키텍처와 포트 함수의 동작 방식 등은 이미 알고 있다고 가정하겠습니다. 깃헙 리포에 예시 앱을 올려두었으니 아래와 같이 받아서 실행해보실 수 있습니다.</p>
<!--more-->
<div class="sourceCode" id="cb1"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="fu">git</span> clone harfangk/elm-port-examples</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="bu">cd</span> elm-port-example</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="ex">npm</span> install <span class="at">--global</span> elm elm-live</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="ex">elm-live</span> src/Main.elm <span class="at">--open</span> <span class="at">--</span> <span class="at">--output</span><span class="op">=</span>elm.js</span></code></pre></div>
<h2 id="개별-포트">개별 포트</h2>
<p>이 방식에서는 엘름과 자바스크립트간 통신마다 하나의 포트 함수를 정의합니다. 포트는 항상 단방향 통신이기 때문에 데이터가 왕복할 경우 한 쌍이 필요합니다. 간단한 예시 코드인데, 전체 코드는 <code>individual-ports</code> 브랜치에서 확인해보실 수 있습니다.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode elm"><code class="sourceCode elm"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="op">#</span> <span class="dt">Main</span><span class="op">.</span><span class="fu">elm</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="fu">update</span> : <span class="dt">Msg</span> <span class="op">-&gt;</span> <span class="dt">Model</span> <span class="op">-&gt;</span> ( <span class="dt">Model</span><span class="op">,</span> <span class="dt">Cmd</span> <span class="dt">Msg</span> )</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="fu">update</span> <span class="fu">msg</span> <span class="fu">model</span> <span class="op">=</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>    <span class="cf">case</span> <span class="fu">msg</span> <span class="cf">of</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a>        <span class="dt">RemoveLocalStorageItem</span> <span class="op">-&gt;</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a>            ( <span class="fu">model</span><span class="op">,</span> <span class="fu">removeLocalStorageItem</span> <span class="fu">model</span><span class="op">.</span><span class="fu">keyForRemoveItem</span> )</span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a>        <span class="fu">_</span> <span class="op">-&gt;</span> ( <span class="fu">model</span><span class="op">,</span> <span class="dt">Cmd</span><span class="op">.</span><span class="fu">none</span> )</span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a><span class="fu">subscriptions</span> : <span class="dt">Model</span> <span class="op">-&gt;</span> <span class="dt">Sub</span> <span class="dt">Msg</span></span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a><span class="fu">subscriptions</span> <span class="fu">model</span> <span class="op">=</span></span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a>    <span class="dt">Sub</span><span class="op">.</span><span class="fu">batch</span></span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a>        [ <span class="fu">gotLocalStorageItem</span> <span class="dt">GotLocalStorageItem</span></span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a>        <span class="op">,</span> <span class="fu">gotCookies</span> <span class="dt">GotCookies</span></span>
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a>        ]</span>
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true" tabindex="-1"></a><span class="kw">port</span> <span class="fu">setLocalStorageItem</span> : { <span class="fu">key</span> : <span class="dt">String</span><span class="op">,</span> <span class="fu">value</span> : <span class="dt">String</span> } <span class="op">-&gt;</span> <span class="dt">Cmd</span> <span class="fu">msg</span></span>
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true" tabindex="-1"></a><span class="kw">port</span> <span class="fu">getLocalStorageItem</span> : <span class="dt">String</span> <span class="op">-&gt;</span> <span class="dt">Cmd</span> <span class="fu">msg</span></span>
<span id="cb2-20"><a href="#cb2-20" aria-hidden="true" tabindex="-1"></a><span class="kw">port</span> <span class="fu">gotLocalStorageItem</span> : ({ <span class="fu">key</span> : <span class="dt">String</span><span class="op">,</span> <span class="fu">value</span> : <span class="dt">Maybe</span> <span class="dt">String</span> } <span class="op">-&gt;</span> <span class="fu">msg</span>) <span class="op">-&gt;</span> <span class="dt">Sub</span> <span class="fu">msg</span></span>
<span id="cb2-21"><a href="#cb2-21" aria-hidden="true" tabindex="-1"></a><span class="kw">port</span> <span class="fu">removeLocalStorageItem</span> : <span class="dt">String</span> <span class="op">-&gt;</span> <span class="dt">Cmd</span> <span class="fu">msg</span></span>
<span id="cb2-22"><a href="#cb2-22" aria-hidden="true" tabindex="-1"></a><span class="kw">port</span> <span class="fu">clearLocalStorage</span> : () <span class="op">-&gt;</span> <span class="dt">Cmd</span> <span class="fu">msg</span></span>
<span id="cb2-23"><a href="#cb2-23" aria-hidden="true" tabindex="-1"></a><span class="kw">port</span> <span class="fu">getCookies</span> : () <span class="op">-&gt;</span> <span class="dt">Cmd</span> <span class="fu">msg</span></span>
<span id="cb2-24"><a href="#cb2-24" aria-hidden="true" tabindex="-1"></a><span class="kw">port</span> <span class="fu">gotCookies</span> : (<span class="dt">String</span> <span class="op">-&gt;</span> <span class="fu">msg</span>) <span class="op">-&gt;</span> <span class="dt">Sub</span> <span class="fu">msg</span></span>
<span id="cb2-25"><a href="#cb2-25" aria-hidden="true" tabindex="-1"></a><span class="kw">port</span> <span class="fu">setCookie</span> : { <span class="fu">key</span> : <span class="dt">String</span><span class="op">,</span> <span class="fu">value</span> : <span class="dt">String</span> } <span class="op">-&gt;</span> <span class="dt">Cmd</span> <span class="fu">msg</span></span></code></pre></div>
<p>개별 포트 방식에서는 단 하나의 기능만 수행하는 구체적인 함수를 정의합니다. 덕분에 입력값과 결과값의 타입을 세밀하게 표기할 수 있어서 컴파일 타임에 타입 안전성이 어느 정도 보장됩니다. 또한 모두 별개의 함수이기 때문에 유의미한 이름을 지어서 함수의 역할을 쉽게 이해할 수 있도록 할 수도 있습니다.</p>
<p>그리고 각 포트 함수가 하나의 작은 기능만 수행하기 때문에 모듈 경계선을 넘어서 영향을 미칠 가능성이 매우 낮습니다. <code>complex-model-individual-ports</code> 브랜치에는 조금 더 복잡한 모델 구조에 맞추어 개별 포트 방식으로 예시를 구현해두었는데, <code>Main</code> 모듈을 확인해보시면 <code>Page.Storage.initModel</code>, <code>Page.Storage.update</code>, <code>Page.Storage.view</code>, 그리고 <code>Page.Storage.subscriptions</code> 네 함수만으로 <code>Main</code> 모듈과 <code>Page.Storage</code>을 연동하는 것을 볼 수 있습니다. 이렇게 모듈화를 하기 쉽기 때문에 이후에 코드를 교체하거나 수정하는 것도 쉽습니다.</p>
<p>반면에 이 방식을 사용하면 필요한 포트 함수의 갯수가 순식간에 증가합니다. 예시에서는 로컬스토리지와 쿠키 관련 기초적인 기능만 구현했는데도 포트 함수가 10개나 필요했습니다. 더 큰 앱에서는 포트 함수 수가 100개를 넘어가는 것도 순식간이겠죠.</p>
<p>또한 이 방식을 사용하면 모듈화는 쉽지만 각 모듈 내에서 엘름 코드와 엘름-자바스크립트 상호작용 코드가 뒤섞이게 됩니다. <code>Page.Storage</code> 모듈을 열어봤을 때 각 <code>Msg</code>가 엘름 이벤트를 처리하는지 자바스크립트 이벤트를 처리하는지 한 눈에 알아보기 어렵습니다. 주석을 통해서 어느 정도 보완할 수는 있지만 계속 <code>Msg</code>를 추가할수록 결국 관리가 어려워지고요.</p>
<p>마지막으로 각 포트 함수가 이벤트 리스너를 사용하기 때문에 성능에 영향을 미칠 수도 있습니다. 하지만 저도 엘름 내에서 이를 어떤 식으로 관리하고 최적화하는지는 잘 모르기 때문에 불필요한 염려일지도 모르니 이를 감안해주시기 바랍니다.</p>
<h2 id="중앙-포트">중앙 포트</h2>
<p>이 방식에서는 엘름에서 자바스크립트로 가는 통신을 모두 담당할 포트 함수 하나, 그리고 자바스크립트에서 엘름으로 오는 통신을 모두 담당할 포트 함수 하나를 정의합니다. 전체 코드는 <code>centralized-ports</code> 브랜치에서 확인할 수 있습니다.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode elm"><code class="sourceCode elm"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="op">#</span> <span class="dt">Main</span><span class="op">.</span><span class="fu">elm</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="fu">update</span> : <span class="dt">Msg</span> <span class="op">-&gt;</span> <span class="dt">Model</span> <span class="op">-&gt;</span> ( <span class="dt">Model</span><span class="op">,</span> <span class="dt">Cmd</span> <span class="dt">Msg</span> )</span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="fu">update</span> <span class="fu">msg</span> <span class="fu">model</span> <span class="op">=</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>    <span class="cf">case</span> <span class="fu">msg</span> <span class="cf">of</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>        <span class="dt">RemoveLocalStorageItem</span> <span class="op">-&gt;</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>            <span class="kw">let</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>                <span class="fu">portMsg</span> <span class="op">=</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>                    <span class="dt">Port</span><span class="op">.</span><span class="dt">RemoveLocalStorageItem</span> { <span class="fu">key</span> <span class="op">=</span> <span class="fu">model</span><span class="op">.</span><span class="fu">keyForRemoveItem</span> }</span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a>                        <span class="op">|&gt;</span> <span class="dt">Port</span><span class="op">.</span><span class="fu">encode</span></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a>            <span class="kw">in</span></span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a>            ( <span class="fu">model</span><span class="op">,</span> <span class="dt">Port</span><span class="op">.</span><span class="fu">sendPortMsg</span> <span class="fu">portMsg</span> )</span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a>        <span class="fu">_</span> <span class="op">-&gt;</span> ( <span class="fu">model</span><span class="op">,</span> <span class="dt">Cmd</span><span class="op">.</span><span class="fu">none</span>)</span>
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-15"><a href="#cb3-15" aria-hidden="true" tabindex="-1"></a><span class="fu">subscriptions</span> : <span class="dt">Model</span> <span class="op">-&gt;</span> <span class="dt">Sub</span> <span class="dt">Msg</span></span>
<span id="cb3-16"><a href="#cb3-16" aria-hidden="true" tabindex="-1"></a><span class="fu">subscriptions</span> <span class="fu">model</span> <span class="op">=</span></span>
<span id="cb3-17"><a href="#cb3-17" aria-hidden="true" tabindex="-1"></a>    <span class="dt">Port</span><span class="op">.</span><span class="fu">gotPortMsg</span> (<span class="dt">JD</span><span class="op">.</span><span class="fu">decodeValue</span> <span class="dt">Port</span><span class="op">.</span><span class="fu">decoder</span> <span class="op">&gt;&gt;</span> <span class="dt">GotPortMsg</span>)</span>
<span id="cb3-18"><a href="#cb3-18" aria-hidden="true" tabindex="-1"></a>    </span>
<span id="cb3-19"><a href="#cb3-19" aria-hidden="true" tabindex="-1"></a><span class="op">#</span> <span class="dt">Port</span><span class="op">.</span><span class="fu">elm</span> </span>
<span id="cb3-20"><a href="#cb3-20" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-21"><a href="#cb3-21" aria-hidden="true" tabindex="-1"></a><span class="kw">port</span> <span class="fu">sendPortMsg</span> : <span class="dt">JE</span><span class="op">.</span><span class="dt">Value</span> <span class="op">-&gt;</span> <span class="dt">Cmd</span> <span class="fu">msg</span></span>
<span id="cb3-22"><a href="#cb3-22" aria-hidden="true" tabindex="-1"></a><span class="kw">port</span> <span class="fu">gotPortMsg</span> : (<span class="dt">JE</span><span class="op">.</span><span class="dt">Value</span> <span class="op">-&gt;</span> <span class="fu">msg</span>) <span class="op">-&gt;</span> <span class="dt">Sub</span> <span class="fu">msg</span></span>
<span id="cb3-23"><a href="#cb3-23" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-24"><a href="#cb3-24" aria-hidden="true" tabindex="-1"></a><span class="co">-- Outbound Ports</span></span>
<span id="cb3-25"><a href="#cb3-25" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-26"><a href="#cb3-26" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">OutboundMethod</span></span>
<span id="cb3-27"><a href="#cb3-27" aria-hidden="true" tabindex="-1"></a>    <span class="op">=</span> <span class="dt">SetLocalStorageItem</span> { <span class="fu">key</span> : <span class="dt">String</span><span class="op">,</span> <span class="fu">value</span> : <span class="dt">String</span> }</span>
<span id="cb3-28"><a href="#cb3-28" aria-hidden="true" tabindex="-1"></a>    <span class="op">|</span> <span class="dt">GetLocalStorageItem</span> { <span class="fu">key</span> : <span class="dt">String</span> }</span>
<span id="cb3-29"><a href="#cb3-29" aria-hidden="true" tabindex="-1"></a>    <span class="op">|</span> <span class="dt">ClearLocalStorage</span></span>
<span id="cb3-30"><a href="#cb3-30" aria-hidden="true" tabindex="-1"></a>    <span class="op">|</span> <span class="dt">RemoveLocalStorageItem</span> { <span class="fu">key</span> : <span class="dt">String</span> }</span>
<span id="cb3-31"><a href="#cb3-31" aria-hidden="true" tabindex="-1"></a>    <span class="op">|</span> <span class="dt">GetCookies</span></span>
<span id="cb3-32"><a href="#cb3-32" aria-hidden="true" tabindex="-1"></a>    <span class="op">|</span> <span class="dt">SetCookie</span> { <span class="fu">key</span> : <span class="dt">String</span><span class="op">,</span> <span class="fu">value</span> : <span class="dt">String</span> }</span>
<span id="cb3-33"><a href="#cb3-33" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-34"><a href="#cb3-34" aria-hidden="true" tabindex="-1"></a><span class="fu">encode</span> : <span class="dt">OutboundMethod</span> <span class="op">-&gt;</span> <span class="dt">JE</span><span class="op">.</span><span class="dt">Value</span></span>
<span id="cb3-35"><a href="#cb3-35" aria-hidden="true" tabindex="-1"></a><span class="fu">encode</span> <span class="fu">method</span> <span class="op">=</span></span>
<span id="cb3-36"><a href="#cb3-36" aria-hidden="true" tabindex="-1"></a>    <span class="cf">case</span> <span class="fu">method</span> <span class="cf">of</span></span>
<span id="cb3-37"><a href="#cb3-37" aria-hidden="true" tabindex="-1"></a>        <span class="dt">SetLocalStorageItem</span> { <span class="fu">key</span><span class="op">,</span> <span class="fu">value</span> } <span class="op">-&gt;</span></span>
<span id="cb3-38"><a href="#cb3-38" aria-hidden="true" tabindex="-1"></a>            <span class="kw">let</span></span>
<span id="cb3-39"><a href="#cb3-39" aria-hidden="true" tabindex="-1"></a>                <span class="fu">payload</span> <span class="op">=</span></span>
<span id="cb3-40"><a href="#cb3-40" aria-hidden="true" tabindex="-1"></a>                    <span class="dt">JE</span><span class="op">.</span><span class="fu">object</span></span>
<span id="cb3-41"><a href="#cb3-41" aria-hidden="true" tabindex="-1"></a>                        [ ( <span class="st">&quot;key&quot;</span><span class="op">,</span> <span class="dt">JE</span><span class="op">.</span><span class="fu">string</span> <span class="fu">key</span> )</span>
<span id="cb3-42"><a href="#cb3-42" aria-hidden="true" tabindex="-1"></a>                        <span class="op">,</span> ( <span class="st">&quot;value&quot;</span><span class="op">,</span> <span class="dt">JE</span><span class="op">.</span><span class="fu">string</span> <span class="fu">value</span> )</span>
<span id="cb3-43"><a href="#cb3-43" aria-hidden="true" tabindex="-1"></a>                        ]</span>
<span id="cb3-44"><a href="#cb3-44" aria-hidden="true" tabindex="-1"></a>            <span class="kw">in</span></span>
<span id="cb3-45"><a href="#cb3-45" aria-hidden="true" tabindex="-1"></a>            <span class="dt">JE</span><span class="op">.</span><span class="fu">object</span></span>
<span id="cb3-46"><a href="#cb3-46" aria-hidden="true" tabindex="-1"></a>                [ ( <span class="st">&quot;method&quot;</span><span class="op">,</span> <span class="dt">JE</span><span class="op">.</span><span class="fu">string</span> <span class="st">&quot;setLocalStorageItem&quot;</span> )</span>
<span id="cb3-47"><a href="#cb3-47" aria-hidden="true" tabindex="-1"></a>                <span class="op">,</span> ( <span class="st">&quot;payload&quot;</span><span class="op">,</span> <span class="fu">payload</span> )</span>
<span id="cb3-48"><a href="#cb3-48" aria-hidden="true" tabindex="-1"></a>                ]</span>
<span id="cb3-49"><a href="#cb3-49" aria-hidden="true" tabindex="-1"></a>        <span class="op">...</span></span>
<span id="cb3-50"><a href="#cb3-50" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-51"><a href="#cb3-51" aria-hidden="true" tabindex="-1"></a><span class="co">-- Inbound Ports</span></span>
<span id="cb3-52"><a href="#cb3-52" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-53"><a href="#cb3-53" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">InboundMethod</span></span>
<span id="cb3-54"><a href="#cb3-54" aria-hidden="true" tabindex="-1"></a>    <span class="op">=</span> <span class="dt">GotLocalStorageItem</span> { <span class="fu">key</span> : <span class="dt">String</span><span class="op">,</span> <span class="fu">value</span> : <span class="dt">String</span> }</span>
<span id="cb3-55"><a href="#cb3-55" aria-hidden="true" tabindex="-1"></a>    <span class="op">|</span> <span class="dt">GotCookies</span> <span class="dt">String</span></span>
<span id="cb3-56"><a href="#cb3-56" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-57"><a href="#cb3-57" aria-hidden="true" tabindex="-1"></a><span class="fu">decoder</span> : <span class="dt">Decoder</span> <span class="dt">InboundMethod</span></span>
<span id="cb3-58"><a href="#cb3-58" aria-hidden="true" tabindex="-1"></a><span class="fu">decoder</span> <span class="op">=</span></span>
<span id="cb3-59"><a href="#cb3-59" aria-hidden="true" tabindex="-1"></a>    <span class="dt">JD</span><span class="op">.</span><span class="fu">field</span> <span class="st">&quot;method&quot;</span> <span class="dt">JD</span><span class="op">.</span><span class="fu">string</span></span>
<span id="cb3-60"><a href="#cb3-60" aria-hidden="true" tabindex="-1"></a>        <span class="op">|&gt;</span> <span class="dt">JD</span><span class="op">.</span><span class="fu">andThen</span> <span class="fu">decoder_</span></span>
<span id="cb3-61"><a href="#cb3-61" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-62"><a href="#cb3-62" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-63"><a href="#cb3-63" aria-hidden="true" tabindex="-1"></a><span class="fu">decoder_</span> : <span class="dt">String</span> <span class="op">-&gt;</span> <span class="dt">JD</span><span class="op">.</span><span class="dt">Decoder</span> <span class="dt">InboundMethod</span></span>
<span id="cb3-64"><a href="#cb3-64" aria-hidden="true" tabindex="-1"></a><span class="fu">decoder_</span> <span class="fu">method</span> <span class="op">=</span></span>
<span id="cb3-65"><a href="#cb3-65" aria-hidden="true" tabindex="-1"></a>    <span class="cf">case</span> <span class="fu">method</span> <span class="cf">of</span></span>
<span id="cb3-66"><a href="#cb3-66" aria-hidden="true" tabindex="-1"></a>        <span class="st">&quot;gotLocalStorageItem&quot;</span> <span class="op">-&gt;</span></span>
<span id="cb3-67"><a href="#cb3-67" aria-hidden="true" tabindex="-1"></a>            <span class="dt">JD</span><span class="op">.</span><span class="fu">map</span> <span class="dt">GotLocalStorageItem</span> (<span class="dt">JD</span><span class="op">.</span><span class="fu">field</span> <span class="st">&quot;payload&quot;</span> <span class="fu">kvDecoder</span>)</span>
<span id="cb3-68"><a href="#cb3-68" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-69"><a href="#cb3-69" aria-hidden="true" tabindex="-1"></a>        <span class="st">&quot;gotCookies&quot;</span> <span class="op">-&gt;</span></span>
<span id="cb3-70"><a href="#cb3-70" aria-hidden="true" tabindex="-1"></a>            <span class="dt">JD</span><span class="op">.</span><span class="fu">map</span> <span class="dt">GotCookies</span> (<span class="dt">JD</span><span class="op">.</span><span class="fu">at</span> [ <span class="st">&quot;payload&quot;</span><span class="op">,</span> <span class="st">&quot;cookies&quot;</span> ] <span class="dt">JD</span><span class="op">.</span><span class="fu">string</span>)</span>
<span id="cb3-71"><a href="#cb3-71" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-72"><a href="#cb3-72" aria-hidden="true" tabindex="-1"></a>        <span class="fu">_</span> <span class="op">-&gt;</span></span>
<span id="cb3-73"><a href="#cb3-73" aria-hidden="true" tabindex="-1"></a>            <span class="dt">JD</span><span class="op">.</span><span class="fu">fail</span> <span class="st">&quot;Got unregistered inbound port method&quot;</span></span></code></pre></div>
<p>중앙 포트 방식은 엘름과 자바스크립트 사이에서 라우터 기능을 수행하는 두 개의 포트 함수를 사용합니다. 이 한 쌍의 함수에서 엘름-자바스크립트 통신을 모두 처리하기 때문에 관련 코드를 하나의 모듈에 집적하여 엘름 코드와 엘름-자바스크립트 통신 코드를 분리하기 쉽고, 따라서 전체 앱 내에서 해당 기능을 이해하고 변경하는 것도 쉬워집니다. 매번 포트 함수를 사용하기 위해 데이터를 인코딩 디코딩할 필요 없이 <code>Port</code> 모듈에 정의된 함수를 호출하기만 해도 자바스크립트와 통신할 수 있고, 관련 기능을 변경할 경우 <code>Port</code> 모듈에 있는 함수만 변경하면 대부분의 작업이 끝나게 됩니다.</p>
<p>하지만 이 방식을 사용하면 모듈화가 조금 어려집니다. 각 포트 섭스크립션을 처리할 <code>Msg</code>를 최상위 <code>Main</code> 모듈에서 모두 지정해줘야하기 때문에 최상위 모듈에서 하위 모듈의 <code>Msg</code> 컨스트럭터에 접근할 수 있어야만 합니다. <code>complex-model-centralized-ports</code> 브랜치의 <code>Main</code> 모듈에 정의된 <code>inboundMethodToMsg</code> 함수를 살펴봅시다.</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode elm"><code class="sourceCode elm"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="fu">inboundMethodToMsg</span> : <span class="dt">Port</span><span class="op">.</span><span class="dt">InboundMethod</span> <span class="op">-&gt;</span> <span class="dt">Msg</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="fu">inboundMethodToMsg</span> <span class="fu">inboundMethod</span> <span class="op">=</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>    <span class="cf">case</span> <span class="fu">inboundMethod</span> <span class="cf">of</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>        <span class="dt">Port</span><span class="op">.</span><span class="dt">GotLocalStorageItem</span> <span class="fu">kv</span> <span class="op">-&gt;</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>            <span class="fu">kv</span> <span class="op">|&gt;</span> <span class="dt">Page</span><span class="op">.</span><span class="dt">Storage</span><span class="op">.</span><span class="dt">GotLocalStorageItem</span> <span class="op">&gt;&gt;</span> <span class="dt">GotStorageMsg</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a>        <span class="dt">Port</span><span class="op">.</span><span class="dt">GotCookies</span> <span class="fu">cookies</span> <span class="op">-&gt;</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a>            <span class="fu">cookies</span> <span class="op">|&gt;</span> <span class="dt">Page</span><span class="op">.</span><span class="dt">Storage</span><span class="op">.</span><span class="dt">GotCookies</span> <span class="op">&gt;&gt;</span> <span class="dt">GotStorageMsg</span></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a>        <span class="dt">Port</span><span class="op">.</span><span class="dt">ConnectedToServer</span> <span class="op">-&gt;</span></span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a>            <span class="dt">ConnectedToServer</span></span></code></pre></div>
<p><code>Main</code> 모듈이 포트 섭스크립션을 제대로 처리하기 위해 하위 모듈의 <code>Msg</code> 타입 컨스트럭터에 직접 접근해야하는 것을 볼 수 있습니다. 이러면 모듈화에 제약이 걸립니다.</p>
<p>또한 중앙 포트 함수는 최대한 유연해야하기 때문에 가장 범용적인 타입인 <code>Json.Encode.Value</code>를 사용할 수밖에 없어서 컴파일 타임 타입 안전성이 매우 약해지게 됩니다. 엘름에서 JSON을 디코딩하는 방식 덕분에 런타임에 프로그램이 충돌하는 일이 발생하지는 않지만, 그래도 런타임에 논리 오류가 발생할 가능성은 증가할 수밖에 없습니다. 그리고 디코딩을 처리할 디코더를 만들고 관리보수할 필요도 생기는데, 이는 정말 귀찮고 불편한 작업입니다.</p>
<h2 id="어떤-방식을-사용해야-할까">어떤 방식을 사용해야 할까</h2>
<p>가장 중요한 것은 엘름-자바스크립트 통신이 얼마나 많은가입니다. 별로 없다면 개별 포트 방식이 구현하고 사용하기에 더 간단하고 직관적입니다.</p>
<p>하지만 상당한 양의 엘름-자바스크립트 통신이 필요하다면 다음으로 확인해보아야 할 것은 엘름 모듈 구조가 더 자주 변하는지, 아니면 엘름-자바스크립트 통신 코드가 더 자주변하는지입니다.</p>
<p>엘름 코드가 더 자주 변한다면 엘름 모듈 간 경계를 명확히 할 수 있는 개별 포트 방식이 더 나을 것입니다. 반대의 경우면 엘름과 자바스크립트 영역을 명확히 분리할 수 있는 중앙 포트 방식이 더 나을 것이고요. 코드가 자주 변하는 부분에 더 많은 시간을 쏟기 마련이니 해당 부분의 코드를 고치기 쉽게 코드베이스를 구성하는 것이 합당합니다.</p>
<p>제가 겪은 바로는 엘름 코드는 한 번 작성하면 정말 안정적이라 크게 바꿀 일이 별로 없습니다. 반면 자바스크립트 코드는 언어 및 생태계 특성 상 변화 속도가 매우 빨랐습니다. 그러니 프로젝트가 어떻게 흘러갈지 잘 모르겠으면 장기적으로는 중앙 포트 방식이 더 안전한 선택일 가능성이 높습니다.</p>
<p>두 방식의 장점만 취할 수 있는 방식이 있었으면 좋겠지만 아직 그런 방식을 찾아내지는 못했습니다. 혹시 더 나은 방식이 있다면 공유 부탁드립니다!</p>]]></description>
    <pubDate>Wed, 19 Dec 2018 00:00:00 UT</pubDate>
    <guid>https://harfangk.dev/ko/posts/2018-12-19-elm-managing-ports.html</guid>
    <dc:creator>harfangk</dc:creator>
</item>
<item>
    <title>엘름 디버거에서 전체 텍스트 표시하기</title>
    <link>https://harfangk.dev/ko/posts/2018-07-29-display-entire-text-in-elm-debugger.html</link>
    <description><![CDATA[<p>엘름 디버거는 제가 써본 중 가장 우수한 디버거입니다. 엘름 언어 자체 특성 때문에 개발 과정에서 버그가 생길 일이 거의 없어서 사실 디버깅할 일이 없는게 함정이지만요. 엘름 디버거는 표시 가능한 문자열 길이에 제한을 두고, 지정된 길이를 넘어가는 문자열이나 자료 구조는 <code>...</code>으로 생략해버립니다. 합리적인 기본값이긴 한데 가끔 내가 정확히 어떤 메시지를 받았는지 알고 싶을 때는 짜증을 유발합니다. 리처드 펠드먼의 <a href="https://github.com/rtfeldman/elm-spa-example">RealWorld 샘플 앱</a>에서 디버거가 긴 문자열을 생략하는 예시를 봅시다.</p>
<!--more-->
<figure>
<img src="/images/debugger_vanilla.png" title="Default Debugger" alt="Default Debugger" />
<figcaption aria-hidden="true">Default Debugger</figcaption>
</figure>
<p>말줄임표를 보는 것도 지겨워져서 전체 문자열을 표시할 방법을 연구하기 시작했고 어느 정도 성과를 거두었습니다. 이제 전체 메시지를 볼 수 있습니다.</p>
<figure>
<img src="/images/debugger_full_text.png" title="Debugger Without Truncation" alt="Debugger Without Truncation" />
<figcaption aria-hidden="true">Debugger Without Truncation</figcaption>
</figure>
<p><code>elm-stuff/packages/elm-lang/virtual-dom/2.0.4/src/Native/Debugger.js</code> 파일에 있는 <code>messageToString</code> 함수를 아래 코드로 갈아치운 결과입니다.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">function</span> <span class="fu">messageToString</span>(value)  {</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>	<span class="kw">var</span> result <span class="op">=</span> <span class="st">&#39;&#39;</span><span class="op">;</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>	<span class="cf">for</span> (<span class="kw">var</span> key <span class="kw">in</span> value)</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>	{</span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>		result <span class="op">=</span> result <span class="op">+</span> <span class="st">&#39; &#39;</span> <span class="op">+</span> <span class="fu">messageToStringHelper</span>(value[key])<span class="op">;</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>	}</span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a>	<span class="cf">return</span> result<span class="op">;</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a>}</span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a><span class="kw">function</span> <span class="fu">toFlatArray</span>(value<span class="op">,</span> result)</span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a>{</span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a>	<span class="cf">if</span> (value[<span class="st">&#39;ctor&#39;</span>] <span class="op">===</span> <span class="st">&#39;[]&#39;</span>)</span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a>	{</span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a>		<span class="cf">return</span> result<span class="op">;</span></span>
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a>	}</span>
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a>	<span class="cf">else</span></span>
<span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a>	{</span>
<span id="cb1-18"><a href="#cb1-18" aria-hidden="true" tabindex="-1"></a>		<span class="kw">var</span> new_result <span class="op">=</span> [<span class="op">...</span>result<span class="op">,</span> value[<span class="st">&#39;_0&#39;</span>]]<span class="op">;</span></span>
<span id="cb1-19"><a href="#cb1-19" aria-hidden="true" tabindex="-1"></a>		<span class="cf">return</span> <span class="fu">toFlatArray</span>(value[<span class="st">&#39;_1&#39;</span>]<span class="op">,</span> new_result)<span class="op">;</span></span>
<span id="cb1-20"><a href="#cb1-20" aria-hidden="true" tabindex="-1"></a>	}</span>
<span id="cb1-21"><a href="#cb1-21" aria-hidden="true" tabindex="-1"></a>}</span>
<span id="cb1-22"><a href="#cb1-22" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-23"><a href="#cb1-23" aria-hidden="true" tabindex="-1"></a><span class="kw">function</span> <span class="fu">messageToStringHelper</span>(value)</span>
<span id="cb1-24"><a href="#cb1-24" aria-hidden="true" tabindex="-1"></a>{</span>
<span id="cb1-25"><a href="#cb1-25" aria-hidden="true" tabindex="-1"></a>	<span class="cf">switch</span> (<span class="kw">typeof</span> value)</span>
<span id="cb1-26"><a href="#cb1-26" aria-hidden="true" tabindex="-1"></a>	{</span>
<span id="cb1-27"><a href="#cb1-27" aria-hidden="true" tabindex="-1"></a>		<span class="cf">case</span> <span class="st">&#39;boolean&#39;</span><span class="op">:</span></span>
<span id="cb1-28"><a href="#cb1-28" aria-hidden="true" tabindex="-1"></a>			<span class="cf">return</span> value <span class="op">?</span> <span class="st">&#39;True&#39;</span> <span class="op">:</span> <span class="st">&#39;False&#39;</span><span class="op">;</span></span>
<span id="cb1-29"><a href="#cb1-29" aria-hidden="true" tabindex="-1"></a>		<span class="cf">case</span> <span class="st">&#39;number&#39;</span><span class="op">:</span></span>
<span id="cb1-30"><a href="#cb1-30" aria-hidden="true" tabindex="-1"></a>			<span class="cf">return</span> value <span class="op">+</span> <span class="st">&#39;&#39;</span><span class="op">;</span></span>
<span id="cb1-31"><a href="#cb1-31" aria-hidden="true" tabindex="-1"></a>		<span class="cf">case</span> <span class="st">&#39;string&#39;</span><span class="op">:</span></span>
<span id="cb1-32"><a href="#cb1-32" aria-hidden="true" tabindex="-1"></a>			<span class="cf">return</span> <span class="fu">addSlashes</span>(value<span class="op">,</span> <span class="kw">false</span>)<span class="op">;</span></span>
<span id="cb1-33"><a href="#cb1-33" aria-hidden="true" tabindex="-1"></a>	}</span>
<span id="cb1-34"><a href="#cb1-34" aria-hidden="true" tabindex="-1"></a>	<span class="cf">if</span> (value <span class="kw">instanceof</span> <span class="bu">String</span>)</span>
<span id="cb1-35"><a href="#cb1-35" aria-hidden="true" tabindex="-1"></a>	{</span>
<span id="cb1-36"><a href="#cb1-36" aria-hidden="true" tabindex="-1"></a>		<span class="cf">return</span> <span class="st">&#39;</span><span class="sc">\&#39;</span><span class="st">&#39;</span> <span class="op">+</span> <span class="fu">addSlashes</span>(value<span class="op">,</span> <span class="kw">true</span>) <span class="op">+</span> <span class="st">&#39;</span><span class="sc">\&#39;</span><span class="st">&#39;</span><span class="op">;</span></span>
<span id="cb1-37"><a href="#cb1-37" aria-hidden="true" tabindex="-1"></a>	}</span>
<span id="cb1-38"><a href="#cb1-38" aria-hidden="true" tabindex="-1"></a>	<span class="cf">if</span> (<span class="kw">typeof</span> value <span class="op">!==</span> <span class="st">&#39;object&#39;</span> <span class="op">||</span> value <span class="op">===</span> <span class="kw">null</span>)</span>
<span id="cb1-39"><a href="#cb1-39" aria-hidden="true" tabindex="-1"></a>	{</span>
<span id="cb1-40"><a href="#cb1-40" aria-hidden="true" tabindex="-1"></a>		<span class="cf">return</span> <span class="st">&#39;…&#39;</span><span class="op">;</span></span>
<span id="cb1-41"><a href="#cb1-41" aria-hidden="true" tabindex="-1"></a>	}</span>
<span id="cb1-42"><a href="#cb1-42" aria-hidden="true" tabindex="-1"></a>	<span class="cf">if</span> (<span class="kw">typeof</span> value <span class="op">===</span> <span class="st">&#39;object&#39;</span> <span class="op">&amp;&amp;</span> <span class="op">!</span>(<span class="st">&#39;ctor&#39;</span> <span class="kw">in</span> value))</span>
<span id="cb1-43"><a href="#cb1-43" aria-hidden="true" tabindex="-1"></a>	{</span>
<span id="cb1-44"><a href="#cb1-44" aria-hidden="true" tabindex="-1"></a>		<span class="kw">var</span> result <span class="op">=</span> []<span class="op">;</span></span>
<span id="cb1-45"><a href="#cb1-45" aria-hidden="true" tabindex="-1"></a>		<span class="cf">for</span> (<span class="kw">var</span> key <span class="kw">in</span> value)</span>
<span id="cb1-46"><a href="#cb1-46" aria-hidden="true" tabindex="-1"></a>		{</span>
<span id="cb1-47"><a href="#cb1-47" aria-hidden="true" tabindex="-1"></a>			result<span class="op">.</span><span class="fu">push</span>(key <span class="op">+</span> <span class="st">&#39;: &#39;</span> <span class="op">+</span> <span class="fu">messageToStringHelper</span>(value[key]))<span class="op">;</span></span>
<span id="cb1-48"><a href="#cb1-48" aria-hidden="true" tabindex="-1"></a>		}</span>
<span id="cb1-49"><a href="#cb1-49" aria-hidden="true" tabindex="-1"></a>		<span class="cf">return</span> <span class="st">&#39;{&#39;</span> <span class="op">+</span> result<span class="op">.</span><span class="fu">join</span>(<span class="st">&#39;, &#39;</span>) <span class="op">+</span> <span class="st">&#39;}&#39;</span><span class="op">;</span></span>
<span id="cb1-50"><a href="#cb1-50" aria-hidden="true" tabindex="-1"></a>	}</span>
<span id="cb1-51"><a href="#cb1-51" aria-hidden="true" tabindex="-1"></a>	<span class="cf">if</span> (value<span class="op">.</span><span class="at">ctor</span><span class="op">.</span><span class="fu">match</span>(<span class="ss">/</span><span class="sc">^\_</span><span class="ss">Task/</span>))</span>
<span id="cb1-52"><a href="#cb1-52" aria-hidden="true" tabindex="-1"></a>	{</span>
<span id="cb1-53"><a href="#cb1-53" aria-hidden="true" tabindex="-1"></a>		<span class="cf">return</span> <span class="st">&#39;…&#39;</span><span class="op">;</span></span>
<span id="cb1-54"><a href="#cb1-54" aria-hidden="true" tabindex="-1"></a>	}</span>
<span id="cb1-55"><a href="#cb1-55" aria-hidden="true" tabindex="-1"></a>	<span class="cf">if</span> (value<span class="op">.</span><span class="at">ctor</span><span class="op">.</span><span class="fu">match</span>(<span class="ss">/</span><span class="sc">^\_</span><span class="ss">Tuple/</span>))</span>
<span id="cb1-56"><a href="#cb1-56" aria-hidden="true" tabindex="-1"></a>	{</span>
<span id="cb1-57"><a href="#cb1-57" aria-hidden="true" tabindex="-1"></a>		<span class="kw">var</span> tupleResult <span class="op">=</span> []<span class="op">;</span></span>
<span id="cb1-58"><a href="#cb1-58" aria-hidden="true" tabindex="-1"></a>		<span class="cf">for</span> (<span class="kw">var</span> key <span class="kw">in</span> value)</span>
<span id="cb1-59"><a href="#cb1-59" aria-hidden="true" tabindex="-1"></a>		{</span>
<span id="cb1-60"><a href="#cb1-60" aria-hidden="true" tabindex="-1"></a>			<span class="cf">if</span> (key <span class="op">!==</span> <span class="st">&#39;ctor&#39;</span>)</span>
<span id="cb1-61"><a href="#cb1-61" aria-hidden="true" tabindex="-1"></a>			{</span>
<span id="cb1-62"><a href="#cb1-62" aria-hidden="true" tabindex="-1"></a>				tupleResult<span class="op">.</span><span class="fu">push</span>(value[key])<span class="op">;</span></span>
<span id="cb1-63"><a href="#cb1-63" aria-hidden="true" tabindex="-1"></a>			}</span>
<span id="cb1-64"><a href="#cb1-64" aria-hidden="true" tabindex="-1"></a>		}</span>
<span id="cb1-65"><a href="#cb1-65" aria-hidden="true" tabindex="-1"></a>		<span class="cf">return</span> <span class="st">&#39;(&#39;</span> <span class="op">+</span> tupleResult<span class="op">.</span><span class="fu">map</span>(messageToStringHelper)<span class="op">.</span><span class="fu">join</span>(<span class="st">&#39;, &#39;</span>) <span class="op">+</span> <span class="st">&#39;)&#39;</span><span class="op">;</span></span>
<span id="cb1-66"><a href="#cb1-66" aria-hidden="true" tabindex="-1"></a>	}</span>
<span id="cb1-67"><a href="#cb1-67" aria-hidden="true" tabindex="-1"></a>	<span class="cf">if</span> ([<span class="st">&#39;&lt;decoder&gt;&#39;</span><span class="op">,</span> <span class="st">&#39;_Process&#39;</span><span class="op">,</span> <span class="st">&#39;Set_elm_builtin&#39;</span><span class="op">,</span> <span class="st">&#39;RBNode_elm_builtin&#39;</span><span class="op">,</span> <span class="st">&#39;RBEmpty_elm_builtin&#39;</span>]<span class="op">.</span><span class="fu">indexOf</span>(value<span class="op">.</span><span class="at">ctor</span>) <span class="op">&gt;=</span> <span class="dv">0</span>)</span>
<span id="cb1-68"><a href="#cb1-68" aria-hidden="true" tabindex="-1"></a>	{</span>
<span id="cb1-69"><a href="#cb1-69" aria-hidden="true" tabindex="-1"></a>		<span class="cf">return</span> <span class="st">&#39;…&#39;</span><span class="op">;</span></span>
<span id="cb1-70"><a href="#cb1-70" aria-hidden="true" tabindex="-1"></a>	}</span>
<span id="cb1-71"><a href="#cb1-71" aria-hidden="true" tabindex="-1"></a>	<span class="cf">if</span> (value<span class="op">.</span><span class="at">ctor</span> <span class="op">===</span> <span class="st">&#39;::&#39;</span>)</span>
<span id="cb1-72"><a href="#cb1-72" aria-hidden="true" tabindex="-1"></a>	{</span>
<span id="cb1-73"><a href="#cb1-73" aria-hidden="true" tabindex="-1"></a>		<span class="kw">var</span> arrayResult <span class="op">=</span> <span class="fu">toFlatArray</span>(value<span class="op">,</span> [])<span class="op">.</span><span class="fu">map</span>(messageToStringHelper)<span class="op">;</span></span>
<span id="cb1-74"><a href="#cb1-74" aria-hidden="true" tabindex="-1"></a>		<span class="cf">return</span> <span class="st">&#39;[&#39;</span> <span class="op">+</span> arrayResult<span class="op">.</span><span class="fu">join</span>(<span class="st">&#39;, &#39;</span>) <span class="op">+</span> <span class="st">&#39;]&#39;</span><span class="op">;</span></span>
<span id="cb1-75"><a href="#cb1-75" aria-hidden="true" tabindex="-1"></a>	}</span>
<span id="cb1-76"><a href="#cb1-76" aria-hidden="true" tabindex="-1"></a>	<span class="cf">if</span> (<span class="kw">typeof</span> value <span class="op">===</span> <span class="st">&#39;object&#39;</span>)</span>
<span id="cb1-77"><a href="#cb1-77" aria-hidden="true" tabindex="-1"></a>	{</span>
<span id="cb1-78"><a href="#cb1-78" aria-hidden="true" tabindex="-1"></a>		<span class="kw">var</span> result <span class="op">=</span> <span class="st">&#39;&#39;</span><span class="op">;</span></span>
<span id="cb1-79"><a href="#cb1-79" aria-hidden="true" tabindex="-1"></a>		<span class="cf">for</span> (<span class="kw">var</span> key <span class="kw">in</span> value)</span>
<span id="cb1-80"><a href="#cb1-80" aria-hidden="true" tabindex="-1"></a>		{</span>
<span id="cb1-81"><a href="#cb1-81" aria-hidden="true" tabindex="-1"></a>			result <span class="op">=</span> result <span class="op">+</span> <span class="st">&#39; &#39;</span> <span class="op">+</span>  <span class="fu">messageToStringHelper</span>(value[key])<span class="op">;</span></span>
<span id="cb1-82"><a href="#cb1-82" aria-hidden="true" tabindex="-1"></a>		}</span>
<span id="cb1-83"><a href="#cb1-83" aria-hidden="true" tabindex="-1"></a>		<span class="cf">return</span> result<span class="op">;</span></span>
<span id="cb1-84"><a href="#cb1-84" aria-hidden="true" tabindex="-1"></a>	}</span>
<span id="cb1-85"><a href="#cb1-85" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
<p>엘름 디버거에서 <code>30ch</code>로 고정된 사이드바 너비도 꽤나 걸리적거립니다. <code>elm-stuff/packages/elm-lang/virtual-dom/2.0.4/src/VirtualDom/Debug.elm</code> 파일에 있는 코드를 수정하면 됩니다.</p>
<figure>
<img src="/images/debugger_modified_sidebar.png" title="Debugger With Wider Sidebar" alt="Debugger With Wider Sidebar" />
<figcaption aria-hidden="true">Debugger With Wider Sidebar</figcaption>
</figure>
<div class="sourceCode" id="cb2"><pre class="sourceCode css"><code class="sourceCode css"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#values</span> {</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">display</span><span class="ch">:</span> <span class="dv">block</span><span class="op">;</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">float</span><span class="ch">:</span> <span class="dv">left</span><span class="op">;</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>  <span class="kw">height</span><span class="ch">:</span> <span class="dv">100</span><span class="dt">%</span><span class="op">;</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>  <span class="co">/* width: calc(100% - 30ch); */</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a>  <span class="kw">width</span><span class="ch">:</span> <span class="dv">50</span><span class="dt">%</span><span class="op">;</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a>  <span class="kw">margin</span><span class="ch">:</span> <span class="dv">0</span><span class="op">;</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a>  <span class="kw">overflow</span><span class="ch">:</span> <span class="bu">auto</span><span class="op">;</span></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a>  <span class="kw">cursor</span><span class="ch">:</span> <span class="dv">default</span><span class="op">;</span></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a>}</span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a><span class="fu">.debugger-sidebar</span> {</span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a>  <span class="kw">display</span><span class="ch">:</span> <span class="dv">block</span><span class="op">;</span></span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a>  <span class="kw">float</span><span class="ch">:</span> <span class="dv">left</span><span class="op">;</span></span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a>  <span class="co">/* width: 30ch; */</span> </span>
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a>  <span class="kw">width</span><span class="ch">:</span> <span class="dv">50</span><span class="dt">%</span><span class="op">;</span></span>
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a>  <span class="kw">height</span><span class="ch">:</span> <span class="dv">100</span><span class="dt">%</span><span class="op">;</span></span>
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true" tabindex="-1"></a>  <span class="kw">color</span><span class="ch">:</span> <span class="cn">white</span><span class="op">;</span></span>
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true" tabindex="-1"></a>  <span class="kw">background-color</span><span class="ch">:</span> <span class="fu">rgb(</span><span class="dv">61</span><span class="op">,</span> <span class="dv">61</span><span class="op">,</span> <span class="dv">61</span><span class="fu">)</span><span class="op">;</span></span>
<span id="cb2-20"><a href="#cb2-20" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
<p>예쁜 코드는 아니지만 일단 제가 원하는 대로 동작은 하고 있습니다. 필요하시면 원하시는 대로 변경해서 사용하세요. 단, 어디까지나 개발 과정을 돕기 위한 몽키 패칭이니까 프로덕션용 빌드에서는 원래 함수로 되돌리는 것을 잊지 마세요.</p>]]></description>
    <pubDate>Sun, 29 Jul 2018 00:00:00 UT</pubDate>
    <guid>https://harfangk.dev/ko/posts/2018-07-29-display-entire-text-in-elm-debugger.html</guid>
    <dc:creator>harfangk</dc:creator>
</item>
<item>
    <title>Spacemacs에서 한국어 사용하기</title>
    <link>https://harfangk.dev/ko/posts/2018-07-22-using-korean-in-spacemacs.html</link>
    <description><![CDATA[<p>Vim에서 하스켈 개발 환경을 설정하다가 짜증이 나서 대안을 찾다가 Spacemacs를 설치해서 사용해보고 있는데, 별도의 설정 없이 설치하자마자 바로 쓸 수 있을 정도로 기본 설정이 훌륭해서 현재까지는 만족스럽게 쓰고 있습니다. 다만 한국어를 설정하는 방법이 옛날 글만 있거나 이맥스 초보가 이해하기 어렵게 작성되어 있어서 좀 헤멨습니다. 문서를 찾아보면서 어느 정도 원하는 대로 설정하는데 성공해서 설정을 공유합니다. 한국어 글꼴은 <code>D2Coding</code>을 사용하고 있는데 원하시는 글꼴을 설치해서 사용하시면 되겠습니다.</p>
<p>환경은 Ubuntu 16.04.4, Emacs 26.1입니다.</p>
<!--more-->
<pre class="elisp"><code>;; ~/.spacemacs

(defun dotspacemacs/user-config ()
  ...
  (set-language-environment &quot;Korean&quot;)
  (prefer-coding-system &#39;utf-8)
  (setq default-korean-keyboard &quot;3f&quot;)
  (global-set-key (kbd &quot;&lt;kana&gt;&quot;) &#39;toggle-input-method)
  (global-unset-key (kbd &quot;S-SPC&quot;))
  (set-fontset-font &quot;fontset-default&quot; &#39;hangul &#39;(&quot;D2Coding&quot; . &quot;unicode-bmp&quot;))
  ;; 정정: 아래와 같이 유니코드 범위를 명시할 필요 없이 위의 줄만으로도 한글 유니코드는 커버됩니다.
  ;; (set-fontset-font &quot;fontset-default&quot; &#39;(#x1100 . #x11ff) &#39;(&quot;D2Coding&quot; . &quot;iso10646&quot;))
  ;; (set-fontset-font &quot;fontset-default&quot; &#39;#x20a9 &#39;(&quot;D2Coding&quot; . &quot;iso10646&quot;))
  ;; (set-fontset-font &quot;fontset-default&quot; &#39;(#x302e . #x302f) &#39;(&quot;D2Coding&quot; . &quot;iso10646&quot;))
  ;; (set-fontset-font &quot;fontset-default&quot; &#39;(#x3130 . #x318f) &#39;(&quot;D2Coding&quot; . &quot;iso10646&quot;))
  ;; (set-fontset-font &quot;fontset-default&quot; &#39;(#x3200 . #x321e) &#39;(&quot;D2Coding&quot; . &quot;iso10646&quot;))
  ;; (set-fontset-font &quot;fontset-default&quot; &#39;(#x3260 . #x327f) &#39;(&quot;D2Coding&quot; . &quot;iso10646&quot;))
  ;; (set-fontset-font &quot;fontset-default&quot; &#39;(#xa960 . #xa97f) &#39;(&quot;D2Coding&quot; . &quot;iso10646&quot;))
  ;; (set-fontset-font &quot;fontset-default&quot; &#39;(#xac00 . #xd7a3) &#39;(&quot;D2Coding&quot; . &quot;iso10646&quot;))
  ;; (set-fontset-font &quot;fontset-default&quot; &#39;(#xd7b0 . #xd7ff) &#39;(&quot;D2Coding&quot; . &quot;iso10646&quot;))
  ;; (set-fontset-font &quot;fontset-default&quot; &#39;(#xffa1 . #xffdc) &#39;(&quot;D2Coding&quot; . &quot;iso10646&quot;))
  ;; (set-fontset-font &quot;fontset-default&quot; &#39;#xffe6 &#39;(&quot;D2Coding&quot; . &quot;iso10646&quot;))
  ...
  )</code></pre>
<p>각 줄의 의미는 아래와 같습니다.</p>
<ul>
<li><code>(set-language-environment "Korean")</code>: 한국어 언어 설정을 불러옵니다. <code>C-h L Korean &lt;RET&gt;</code>를 실행하시면 어떤 기능을 하는지 확인할 수 있습니다.</li>
<li><code>(prefer-coding-system 'utf-8)</code>: 한국어 언어 설정에서 지원하는 기본 인코딩 시스템은 <code>ISO-2022-KR</code>, <code>EUC-KR</code>과 <code>CP949</code>입니다. 대신 <code>utf-8</code>로 설정했습니다.</li>
<li><code>(setq default-korean-keyboard "3f")</code>: 기본은 두벌식인데 저는 세벌식 최종을 사용하므로 이렇게 설정했습니다. 두벌식을 사용할 경우 필요 없는 설정입니다. 사용 가능한 입력기 목록은 <code>C-h I hangul</code>에서 확인할 수 있습니다.</li>
<li><code>(global-set-key (kbd "&lt;kana&gt;") 'toggle-input-method)</code>: 입력기 전환 기능을 한/영 키에 설정합니다.</li>
<li><code>(global-unset-key (kbd "S-SPC"))</code>: 기본적으로 설정되어 있는 입력기 전환 키는 <code>Shift+Space</code>인데 자꾸 원하지 않을때 전환하는 일이 생겨서 키를 해제했습니다.</li>
<li><code>(set-fontset-font "fontset-default" '(#x1100 . #x11ff) '("D2Coding" . "iso10646"))</code>: <code>U+1100-U+11ff</code> 범위 내의 문자에는 <code>D2Coding</code> 글꼴을 사용하도록 설정했습니다. 해당 범위는 <code>iso-10646</code> 유니코드 표준 상에서 <a href="http://www.unicode.org/charts/PDF/U1100.pdf">Hangul Jamo</a>에 해당하는 영역입니다.</li>
<li><code>(set-fontset-font "fontset-default" '#x20a9 '("D2Coding" . "iso10646"))</code>: <a href="http://www.unicode.org/charts/PDF/U20A0.pdf">Currency Symbols</a>에서 반각 원화 기호의 글꼴을 지정합니다.</li>
<li><code>(set-fontset-font "fontset-default" '(#x302e . #x302f) '("D2Coding" . "iso10646"))</code>: <a href="http://unicode.org/charts/PDF/U3000.pdf">CJK Symbols and Punctuation</a>에서 중세 한국어 성조 표기 기호의 글꼴을 지정합니다.</li>
<li><code>(set-fontset-font "fontset-default" '(#x3130 . #x318f) '("D2Coding" . "iso10646"))</code>: <a href="http://www.unicode.org/charts/PDF/U3130.pdf">Hangul Compatibility Jamo</a>에 해당하는 범위의 글꼴을 지정합니다.</li>
<li><code>(set-fontset-font "fontset-default" '(#x3200 . #x321e) '("D2Coding" . "iso10646"))</code>: <a href="http://www.unicode.org/charts/PDF/U3200.pdf">Enclosed CJK Letters and Months</a>에서 괄호로 감싸인 기호의 글꼴을 지정합니다.</li>
<li><code>(set-fontset-font "fontset-default" '(#x3260 . #x327f) '("D2Coding" . "iso10646"))</code>: <a href="http://www.unicode.org/charts/PDF/U3200.pdf">Enclosed CJK Letters and Months</a>에서 원으로 감싸인 기호 및 KS기호의 글꼴을 지정합니다.</li>
<li><code>(set-fontset-font "fontset-default" '(#xa960 . #xa97f) '("D2Coding" . "iso10646"))</code>: <a href="http://www.unicode.org/charts/PDF/UA960.pdf">Hangul Jamo Extended-A</a>에 해당하는 문자 범위의 글꼴을 지정합니다.</li>
<li><code>(set-fontset-font "fontset-default" '(#xac00 . #xd7a3) '("D2Coding" . "iso10646"))</code>: <a href="http://unicode.org/charts/PDF/UAC00.pdf">Hangul Syllables</a>에 해당하는 문자 범위의 글꼴을 지정합니다.</li>
<li><code>(set-fontset-font "fontset-default" '(#xd7b0 . #xd7ff) '("D2Coding" . "iso10646"))</code>: <a href="http://www.unicode.org/charts/PDF/UD7B0.pdf">Hangul Jamo Extended-B</a>에 해당하는 문자 범위의 글꼴을 지정합니다.</li>
<li><code>(set-fontset-font "fontset-default" '(#xffa1 . #xffdc) '("D2Coding" . "iso10646"))</code>: <a href="http://www.unicode.org/charts/PDF/UFF00.pdf">Halfwidth and Fullwidth Forms</a>에서 한글 반각 문자의 글꼴을 지정합니다.</li>
<li><code>(set-fontset-font "fontset-default" '#xffe6 '("D2Coding" . "iso10646"))</code>: <a href="http://www.unicode.org/charts/PDF/UFF00.pdf">Halfwidth and Fullwidth Forms</a>에서 전각 원화 기호의 글꼴을 지정합니다.</li>
</ul>]]></description>
    <pubDate>Sun, 22 Jul 2018 00:00:00 UT</pubDate>
    <guid>https://harfangk.dev/ko/posts/2018-07-22-using-korean-in-spacemacs.html</guid>
    <dc:creator>harfangk</dc:creator>
</item>
<item>
    <title>CSS가 어려운 이유</title>
    <link>https://harfangk.dev/ko/posts/2018-07-14-why-css-hard.html</link>
    <description><![CDATA[<p>예전에 CSS보다는 하스켈이 차라리 더 쉽다는 농담을 들은 적이 있는데, 당시에는 그냥 웃어 넘겼었습니다. 하지만 지난 몇 달 동안 CSS를 작성해보았더니 왜 그 사람이 그런 말을 했는지 이제는 이해할 수 있습니다. CSS는 복잡합니다. 문법과 구조는 간단하지만 결과물이 런타임에 따라 천변만화하기 때문에 어떤 결과물이 나올지 예측하기 정말 어렵습니다.</p>
<h2 id="세-가지-문제">세 가지 문제</h2>
<p>제가 보기엔 문제의 원인은 크게 세 가지로 나눌 수 있습니다.</p>
<ol type="1">
<li>어떤 CSS 규칙이 최종적으로 적용될지 예측하기 어렵다</li>
<li>CSS 규칙의 상호작용을 모두 알기 어렵다</li>
<li>런타임 환경을 예상하기 어렵다</li>
</ol>
<!--more-->
<h3 id="어떤-css-규칙이-최종적으로-적용될지-예측하기-어렵다">어떤 CSS 규칙이 최종적으로 적용될지 예측하기 어렵다</h3>
<p>CSS에서는 같은 CSS규칙을 여러번 선언할 수 있습니다. 중복 선언된 규칙 중 어떤 것이 최종적으로 적용될지는 <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Cascade">cascade</a>라는 알고리즘에 따라 결정되는데, 간단한 알고리즘이기 때문에 어떤 규칙이 적용될지는 쉽게 예측할 수 있습니다. 문제는 선언된 모든 규칙을 빠짐없이 파악하는 부분입니다.</p>
<p>먼저 CSS 속성은 어디서든 선언될 수 있습니다. 이는 보통 글로벌 네임스페이스 문제라고 부르는데, 이 때문에 CSS의 최종 결과값을 알기 위해서는 해당 문서 파일과 함께 불러온 모든 CSS 파일 내의 모든 CSS 속성을 살펴봐야합니다. 수십 줄 정도를 살펴보는 것은 일도 아니지만 수십 개의 파일에 걸쳐 선언된 수천 줄의 CSS를 실수 없이 뒤져보는 것은 현실적으로 불가능한 일입니다.</p>
<p>또한 DOM 요소는 부모 요소로부터 CSS 속성을 상속할 수도 있습니다. 따라서 해당 요소의 특정 CSS 값을 알기 위해서는 모든 부모 요소의 CSS 값도 파악해야 합니다. 예를 들어 DOM 트리 말단에 위치한 요소의 폰트 값은 실제로는 해당 요소의 머나먼 조상 요소에 선언되었고, 해당 요소는 이를 상속한 것일 뿐일수도 있습니다.</p>
<p>그리고 특정 DOM 요소에 대한 CSS 속성을 선언하는 방법이 너무나도 다양합니다. 다음 HTML 코드를 예로 들어보겠습니다.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode html"><code class="sourceCode html"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;</span><span class="kw">div</span><span class="ot"> class</span><span class="op">=</span><span class="st">&quot;container&quot;</span><span class="dt">&gt;</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>  <span class="dt">&lt;</span><span class="kw">p</span><span class="ot"> id</span><span class="op">=</span><span class="st">&quot;content&quot;</span><span class="ot"> class</span><span class="op">=</span><span class="st">&quot;sample-text&quot;</span><span class="dt">&gt;</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>    <span class="dt">&lt;</span><span class="kw">span</span><span class="dt">&gt;</span>Sample Text<span class="dt">&lt;/</span><span class="kw">span</span><span class="dt">&gt;</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>  <span class="dt">&lt;/</span><span class="kw">p</span><span class="dt">&gt;</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="dt">&lt;/</span><span class="kw">div</span><span class="dt">&gt;</span></span></code></pre></div>
<p>여기서 <code>span</code> 태그를 선택할 수 있는 방법은 수십가지 넘게 있습니다. 단일 <code>span</code> 타입 선택자, 타입 선택자를 중첩한 <code>div p span</code>, 클래스 선택자, 아이디 선택자, 타입 선택자를 조합한 <code>.container #content span</code>, 클래스 선택자, 타입 선택자를 조합하되 일부만 명시한 <code>.sample-text span</code> 등 다양합니다. 그렇기 때문에 어떤 속성이 어떤 요소에 적용될지 확실하고 손쉽게 파악할 수 없습니다.</p>
<p>이론적으로는 특정 DOM 요소에 적용되는 CSS 규칙을 빠짐없이 찾으려면 모든 CSS 파일의 모든 CSS 규칙을 꼼꼼하게 살펴봐야 합니다. 물론 실제로는 경험을 바탕으로 적절히 추측할 수 있기 때문에 그 정도까지 가는 일은 없습니다. 하지만 최초의 추측이 틀렸을 때는 CSS의 고질적인 문제를 마주해야 하며, 이 때 CSS를 디버깅하는 과정은 논리적인 문제 해결 과정보다는 운에 맡기고 이것저것 시도해보는 것에 가깝게 느껴집니다.</p>
<h3 id="css-규칙의-상호작용을-모두-알기-어렵다">CSS 규칙의 상호작용을 모두 알기 어렵다</h3>
<p>CSS 속성은 대부분 다른 속성과 상호작용을 하여 결과값을 내는데, 덕분에 복잡성이 더더욱 증가합니다. <code>background-color</code>, <code>text-decoration</code>, <code>cursor</code> 등의 속성은 다른 CSS 속성과 무관하게 결과값을 내지만 이런 속성은 몇 없습니다.</p>
<p>어떤 속성끼리 상호작용하는지 예측이 가능한 경우도 가끔 있습니다. <code>font-size</code>, <code>font-weight</code>, <code>font-family</code>가 모두 타이포그래피에 관련된 속성이라는 것은 직관적으로 알 수 있습니다. 하지만 대부분의 경우 속성의 이름만 보고 상호작용을 예상하기는 어렵습니다.</p>
<p>예를 들어 <code>border</code> 속성은 <code>width</code>와 <code>height</code> 값에 영향을 줄까요? 보통은 그렇지 않지만 <code>box-sizing</code> 속성값에 따라서 영향을 줄 때도 있습니다. <code>font-size</code> 값을 증가시킨다면? 보통은 영향을 주지만 <code>overflow</code> 속성값에 따라서 영향을 주지 않을 수도 있습니다. 자식 요소의 크기는 부모 요소의 <code>width</code> 값이 영향을 줄까요? 보통은 영향을 미치지만 자식 요소의 <code>position</code> 속성값에 따라 영향을 미치지 않을 수도 있습니다. 대부분의 CSS 속성이 이런 식입니다.</p>
<h3 id="런타임-환경을-예상하기-어렵다">런타임 환경을 예상하기 어렵다</h3>
<p>웹브라우저의 버전과 개발사, 심지어 운영체제도 CSS 결과값에 영향을 줍니다.</p>
<p>각 웹브라우저는 CSS 스펙을 독자적인 방식으로 구현합니다. 최신 브라우저에서는 구현 방식별 차이점 문제가 두드러지지 않으나, 마이크로소프트 사의 브라우저는 여전히 타사의 브라우저와는 조금 다르게 동작하는 경우가 있습니다. 구버전 브라우저의 경우 새로 도입된 CSS 속성을 지원하지 않으므로 주의해야 합니다.</p>
<p>사용자의 브라우저 설정도 고려해야 합니다. 확대를 했거나 텍스트 크기를 바꿨을 수도 있고, 사용자가 직접 정의한 CSS가 있을 수도 있습니다. 이럴 경우 결과값에 큰 영향이 있을 수 있습니다.</p>
<p>또한 운영체제 설정도 영향을 미칠 수 있습니다. MacOS와 윈도우즈는 폰트 렌더링 방식이 다르기 때문에 <code>font-weight</code> 값이 같더라도 MacOS에서는 폰트가 조금 더 두껍게 그려집니다. 운영체제 자체에도 텍스트 크기 설정이 있기 때문에 타이포그래피가 생각한 것과 다르게 나올 수 있습니다.</p>
<p>마지막으로 사용자 기기도 문제가 될 수 있습니다. 화면 크기에 따라 레이아웃이 크게 바뀔 수 있고, 색상 조정값에 따라서 세밀하게 지정한 색상이 어그러질 수도 있습니다.</p>
<h2 id="대응방법">대응방법</h2>
<h3 id="어떤-css-규칙이-최종적으로-적용될지-예측하기-어렵다-1">어떤 CSS 규칙이 최종적으로 적용될지 예측하기 어렵다</h3>
<p>글로벌 네임스페이스, 상속, 선택자 조합을 모두 함께 언급하긴 했지만 가장 근본적인 문제는 글로벌 네임스페이스입니다. 조상 속성이 몇 개 되지 않으면 상속된 속성을 파악하는 것은 문제가 되지 않고, 선택자의 경우 일정한 방식으로 사용하기로 팀 내에 관습을 정하면 됩니다.</p>
<p>CSS 글로벌 네임스페이스 문제에 대해서는 수십년 동안 수많은 해결책이 제시되었는데 제가 선호하는 두 가지를 소개하겠습니다. 먼저 BEM을 사용하고, 프로젝트가 충분히 복잡해지면 CSS Modules를 도입하는 것을 추천합니다.</p>
<h4 id="bem">BEM</h4>
<p><a href="https://en.bem.info/">BEM</a>은 CSS 특유의 글로벌 네임스페이스에서도 이름이 중복되지 않도록 CSS 아이디와 클래스를 명명하는 방법론입니다. 단순하면서도 충분히 구체적이기 때문에 배우기도 쉽게 프로젝트 특성에 따라 변형시켜 사용하기도 좋습니다.</p>
<p>BEM은 단순한 명명법이기 때문에 자바스크립트나 SASS 같은 의존성을 추가할 필요가 없습니다. 따라서 프로젝트가 불필요하게 복잡해지지 않도록 하여 접근성을 유지하고 싶다면 매우 적절한 시작점입니다. BEM 자체도 단순하기 때문에 프로젝트가 발전하는 과정에 따라 다른 의존성을 손쉽게 추가할 수 있습니다.</p>
<p>하지만 수동적으로 적용하는 관습이기 때문에 그에 따른 단점은 분명히 있습니다. 먼저 모든 팀원이 BEM이 무엇인지 이해하고, 어떻게 사용할 지를 이해하고 있어야하며, 이를 따를 의사가 있어야 합니다. 하지만 BEM을 사용하는 방식에 있어 조금씩 이견이 생기는 것은 불가피한 일입니다.</p>
<h4 id="css-modules">CSS Modules</h4>
<p><a href="https://github.com/css-modules/postcss-modules">CSS Modules</a>는 각 파일에 선언된 CSS 선택자에 고유한 해시 문자열을 추가하여, 해당 선택자가 각 파일의 네임스페이스에서만 동작하도록 자동적으로 관리해줍니다.</p>
<p>CSS Modules를 처음 설정하는 것은 훨씬 어렵습니다. 가장 일반적인 방법은 자바스크립트를 사용해서 <a href="https://webpack.js.org/">웹팩</a> 안에 <a href="https://github.com/postcss/postcss-loader">PostCSS Loader</a>를 추가하고, 여기서 <a href="https://github.com/postcss/postcss">PostCSS</a>의 플러그인으로 불러와서 사용하는 것입니다. 기능 하나를 사용하기 위한 것치고는 의존성이 매우 많습니다. 심지어 설정하기 복잡하기로 악명높은 웹팩도 끼어있습니다.</p>
<p>그럼에도 불구하고 CSS Modules는 글로벌 네임스페이스 문제를 확실하고 자동적인 방식으로 해결해주기는 합니다. 프로젝트가 충분히 복잡해지면 이런 기능이 절실해질 수도 있습니다.</p>
<h3 id="css-규칙의-상호작용을-모두-알기-어렵다-1">CSS 규칙의 상호작용을 모두 알기 어렵다</h3>
<p>아쉽게도 CSS 속성끼리 어떻게 상호작용하는지는 경험을 통해서만 배울 수 있는 것 같고, 이름을 바탕으로 파악할 수 있는 요령은 없는 것 같습니다. 형식 체계의 근본이 되는 공리를 찾으려는 방식보다는, 외국어를 배울 때 새로운 단어를 배우는 마음으로 여유롭게 하나씩 배우는 것이 최선의 방법이라 생각합니다.</p>
<h3 id="런타임-환경을-예상하기-어렵다-1">런타임 환경을 예상하기 어렵다</h3>
<p>이 문제는 코드를 통해서 해결할 수 없는 문제입니다. 사용자의 런타임 환경을 바꾸는 것도, 모든 런타임 환경에서 완벽하게 동작하는 CSS를 작성하는 것도 불가능합니다. 어쩔 수 없는 일 때문에 스트레스를 받으며 시간을 낭비하기보다는 현실을 직시하는 편이 낫습니다. 그보다는 어떤 런타임 환경을 대상으로 할 지 확정하고 이를 확실히 지원하는 것이 보다 현명한 접근법이라 생각합니다.</p>
<h2 id="css를-있는-그대로-받아들이자">CSS를 있는 그대로 받아들이자</h2>
<p>처음에는 CSS가 너무 불확실하고 무질서하게 느껴져서 매우 싫었습니다. 하지만 무엇인가 새로운 것을 배울 때는 마음을 열고 받아들여야한다는 말을 되새기며 CSS를 기존 관점에서 평가하기보다는 CSS의 관점에서 살펴보려고 노력했습니다. CSS를 있는 그대로 받아들이기로 하고보니 충분히 유용한 도구라는 점도 인정하게 되었습니다. 가끔 기묘하게 동작해서 할 말을 잃게 만드는 일은 여전히 있지만 작성하면서 조금씩 재미도 느끼게 되었습니다. 여전히 마음에 쏙 드는 언어는 아니지만 그냥저냥 쓸 수는 있을 것 같습니다.</p>]]></description>
    <pubDate>Sat, 14 Jul 2018 00:00:00 UT</pubDate>
    <guid>https://harfangk.dev/ko/posts/2018-07-14-why-css-hard.html</guid>
    <dc:creator>harfangk</dc:creator>
</item>
<item>
    <title>Safer Smart Contracts Through Type-Driven Development - 감상문</title>
    <link>https://harfangk.dev/ko/posts/2018-02-18-thoughts-on-safer-smart-contracts-through-tdd.html</link>
    <description><![CDATA[<h2 id="서문">서문</h2>
<p>작년에 이드리스 언어에 관한 책을 한 권 읽고 <a href="./2017-10-23-tdd-with-idris-review.html">블로그 글을</a> 한 편 작성한 적이 있습니다. 그리고 그 글을 쓴 지 얼마 되지 않아 우연히 블록체인 기술을 다루는 회사에 입사해서 일을 하기 시작했습니다. 그러니 제가 최근 흥미를 가진 이 두 가지 주제를 함께 다룬 <a href="https://publications.lib.chalmers.se/records/fulltext/234939/234939.pdf">“Safer smart contracts through type-driven development: Using dependent and polymorphic types for safer development of smart contracts”</a>라는 논문이 있다고 들었을 때 꼭 읽어봐야겠다는 생각을 하게 되었습니다.</p>
<p>이 글은 해당 논문을 읽으면서 가지게 된 생각을 정리한 글이며, 논문 내용에서 한 발짝 더 나아가거나 한 내용은 없습니다. 또한 글을 쓸 때 함수형 프로그래밍 패러다임, 특히 순수 함수와 사이드 이펙트 사이의 차이점에 대한 기본적인 이해를 가정하고 작성했으니 감안하고 읽어주시기 바랍니다.</p>
<!--more-->
<h2 id="타입을-활용하여-컴파일-타임에-원래-런타임에서-했어야할-연산을-수행하기">타입을 활용하여 컴파일 타임에 원래 런타임에서 했어야할 연산을 수행하기</h2>
<p>스마트 컨트랙트가 올바르게 작성되었는지를 검증하는 것은 블록체인 업계에서 가장 개선이 절실한 분야 중 하나입니다. 업계의 규모가 커질수록 스마트 컨트랙트를 통해서 처리되는 금액 또한 증가할 것이고, 잘못된 스마트 컨트랙트 때문에 발생하는 피해액 또한 그에 비례해 증가할 것입니다. 스마트 컨트랙트의 현재 상황에 대해 알게 될수록 정적 타입 함수형 언어야말로 이 분야에 적합한 언어라는 생각을 하기 시작했습니다. 그 계통의 언어는 런타임 에러가 거의 발생하지 않는 것으로 유명하니까요. 금융 기관에서 유달리 그런 언어를 많이 쓰는 것에는 이유가 있습니다. 개인적으로도 엄격한 컴파일 타임 타입 체크가 이루어지면 널 포인터 에러 등 특정한 종류의 흔한 에러를 피할 수 있다는 것을 경험해 보았습니다.</p>
<p>저자인 Jack Pettersson와 Robert Edström 또한 같은 생각을 가지고 이 주제를 연구해보았습니다. “우리의 논지는 방대한 타입 시스템을 가진 함수형 언어가 스마트 컨트랙트를 보다 안전하게 개발하도록 해줄 수 있다는 것이다.”</p>
<p>논문을 읽고 나서 저는 정적 타입 프로그래밍 언어가 스마트 컨트랙트를 쓰는데 매우 적합하리라는 심증을 더욱 강하게 가지게 되었습니다. 인간의 정신은 매우 강력하고 유연하지만 실수 또한 잦으며, 이런 단점은 피곤하거나 주의력이 떨어졌을 때 더욱 두드러집니다. 신뢰성을 위해서는 컴퓨터를 잘 활용해야 합니다.</p>
<h2 id="타입을-사용하여-사전조건-및-사후조건-표현">타입을 사용하여 사전조건 및 사후조건 표현</h2>
<p>제가 논문에서 가장 흥미롭게 본 것은 아래 코드입니다.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>finalize <span class="op">:</span> { auto p<span class="op">:</span> <span class="dt">LTE</span> <span class="dv">20</span> b } <span class="ot">-&gt;</span> <span class="dt">Eff</span> <span class="dt">Int</span> </span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>           [ <span class="dt">STORE</span>, <span class="dt">ETH</span> <span class="dv">0</span> b <span class="dv">0</span> <span class="dv">0</span> ]</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>           (\winner <span class="ot">=&gt;</span> <span class="kw">if</span> winner <span class="op">==</span> <span class="dv">0</span> </span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>                          <span class="kw">then</span> [ <span class="dt">STORE</span>, <span class="dt">ETH</span> <span class="dv">0</span> b <span class="dv">0</span> <span class="dv">0</span> ] </span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>                          <span class="kw">else</span> [ <span class="dt">STORE</span>, <span class="dt">ETH</span> <span class="dv">0</span> b <span class="dv">20</span> <span class="dv">0</span> ])</span></code></pre></div>
<p><code>finalize</code>라는 함수의 타입 표기입니다. 이 함수는 두 사람이 각자 10 이더씩 걸고 참가하는 가위바위보 경기의 최종 결과를 판단하는 함수입니다. 승자는 20이더를 모두 챙겨가죠. 이 타입 표기에는 정말 많은 정보가 담겨 있기 때문에 이런 종류의 언어에 익숙하지 않다면 이해하기 어려울 수도 있습니다.</p>
<p>각 부분의 의미는 이러합니다.</p>
<p><code>{ auto p: LTE 20 b }</code>는 <code>20</code>이 <code>b</code>보다 작거나 같다는 증명을 이드리스 컴파일러에게 전달합니다. 여기서 <code>b</code>는 잔고(balance)를 의미하고 <code>p</code>는 증명(proof)을 의미합니다. 컴파일러는 이 증명에 의거하여 이 함수가 호출될 때 <code>b</code>는 반드시 <code>20</code>보다 크다고 가정합니다. 그러면 잔고 이상의 금액을 전송하지 않는다는 것을 컴파일 타임에 보장할 수 있습니다.</p>
<p><code>EFFECT</code>는 사이드 이펙트를 표현하기 위한 이드리스의 타입입니다. <code>Effects</code> 라이브러리에 관해서는 이드리스 공식 사이트에서 <a href="http://docs.idris-lang.org/en/latest/effects/">튜토리얼</a>을 제공합니다. 단, 사이트를 보시면 아시겠지만 현재는 <code>Effects</code>가 아니라 다른 모델을 사용할 것을 권장합니다.
&gt; Effects를 사용해야 할 특별한 이유가 있지 않은 이상 대신 <code>Control.ST</code>를 사용할 것을 권합니다.</p>
<p><code>Eff</code>는 <code>EFFECT</code> 타입의 타입 컨스트럭터로, <code>Int</code> 타입의 값, <code>Effect</code> 값의 리스트, 그리고 <code>Int -&gt; List Effect</code> 타입의 함수라는 세 개의 인자를 받습니다.</p>
<p>첫 번째 인자는 이 함수의 결과값을 나타냅니다. <code>finalize</code>의 경우 <code>Int</code> 타입이죠.</p>
<p>두 번째 인자는 <code>인풋 사이드 이펙트</code>라고 불리는데, 이 함수가 호출되었을 때 <code>STORE</code>와 <code>ETH 0 b 0 0</code> 이펙트를 사용할 수 있다는 것을 의미합니다.</p>
<p>세 번째 인자는 <code>아웃풋 사이드 이펙트</code>라고 불립니다. 이는 첫 번째 인자를 바탕으로 <code>finalize</code> 함수가 <code>[ STORE, ETH 0 b 0 0 ]</code>와 <code>[ STORE, ETH 0 b 20 0 ]</code> 중에서 어떤 사이드 이펙트를 가질 지를 결정하는 함수입니다.</p>
<p>결과값 뿐만 아니라, 그 결과값에 따라서 해당 함수가 어떤 사이드 이펙트를 가지게 될 지도 타입 표기를 통해 표현할 수 있어서 정말 깜짝 놀랐습니다. 이를 활용하면 사전 사이드 이펙트와 사후 사이드 이펙트를 타입을 사용해 명시하여, 함수에서 발생하는 네트워크 통신 같은 사이드 이펙트도 컴파일 타임에 명확히 표현하고 다룰 수 있게 됩니다.</p>
<h2 id="ast-변환의-원리">AST 변환의 원리</h2>
<p>저는 주로 웹 개발을 독학했기 때문에 하드웨어, 어셈블리, 컴파일러, 운영체계 등의 로우레벨 분야에 대해서는 많이 알지 못합니다. 그래서 제게는 해당 분야는 항상 신비로운 미지의 땅처럼 느껴졌습니다. 그래서 논문의 저자들이 이드리스의 AST를 이더리움 가상 머신에서 사용하는 서펜트라는 언어의 AST로 변환하는 과정을 설명했을 때 그 과정이 생각보다 복잡하지 않아서 놀랐습니다. 기본 원리만 보면 한 AST에서 다른 AST로 변환하는 규칙만 작성하면 되는 것이었으니까요. 물론 실제로 해보면 그렇게 간단하진 않겠지만 그 과정에 대해서 어느 정도 이해하게 된 것 같아서 좋았습니다.</p>
<h2 id="함수형-프로그래밍은-분산된-노드끼리-메시지를-주고-받는-것을-표현할-수-없다">함수형 프로그래밍은 분산된 노드끼리 메시지를 주고 받는 것을 표현할 수 없다</h2>
<p>마지막 논의 부분에서 저자들은 함수형 패러다임에 실망한 이유를 이야기합니다.</p>
<blockquote>
<p>이드리스 구현체로 스마트 컨트랙트를 작성할 때 대부분의 핵심 기능은 순수 함수가 아니라 사이드 이펙트를 가진 함수로 표현해야 했습니다. 이는 순수 함수에는 통신에 관한 개념이 없기 때문입니다. 하지만 이더리움의 실행 모델은 계정끼리 주고받는 메시지에 전적으로 기반을 두고 있고, 이는 우리가 알고 있는 모든 스마트 컨트랙트 플랫폼이 공통적으로 가지는 특성입니다. 또한 순수 함수는 프로그램의 상태를 직접 표현하지 않는데, 상태를 표현하는 것 또한 스마트 컨트랙트 플랫폼의 중요한 측면입니다.</p>
</blockquote>
<p>저도 함수형 패러다임이 이 분야에 매우 적합할 것이라고 생각했기 때문에 예상치 못한 결과에 저자들 만큼이나 놀랐습니다. 하지만 기본적으로 저자들의 관점이 옳다고 봅니다. 어떤 모델이 특정 분야의 핵심 요소를 표현해내지 못한다면 해당 분야에 적합한 모델이 아닐 수도 있습니다. 저자들은 <a href="https://www.wikiwand.com/en/Process_calculus">프로세스 캘큘러스(process calculus)</a>가 더 적합할 수도 있다고 제시했는데, 처음 들어본 개념이기 때문에 한 번 찾아보았습니다. 얼랭과 엘릭서를 다루면서 배우게 된 액터 모델과 유사해 보이는데, 복잡한 네트워크 통신을 정식으로 표현하기에는 더 적합한 모델로 보이긴 했습니다.</p>
<h2 id="디펜던트-타입-사이드-이펙트의-표현력">디펜던트 타입 사이드 이펙트의 표현력</h2>
<p>저자들은 이드리스로 스마트 컨트랙트를 작성할 때 디펜던트 타입과 명시적인 사이드 이펙트 관리라는 이드리스의 두 특징 모두 핵심적인 역할을 했다고 평가했습니다. 논문에서 이를 구현한 예시 코드 또한 제공하는데 매우 흥미롭게 읽었습니다.</p>
<p>저는 최근에는 주로 엘름으로 프로그램을 짜고 있는데, 저자들이 두 특징 모두 핵심적이었다고 할 때 무슨 의미로 말하는 것인지 알 것 같습니다. 엘름 또한 <code>Cmd</code>와 <code>Sub</code> 타입을 통해 명시적으로 사이드 이펙트를 관리하지만 제가 원하는 것보다 표현력이 부족할 때가 있습니다. 이는 엘름의 설계 철학에 따른 것이고, 덕분에 엘름 코드가 매우 읽고 이해하기 쉽다는 점은 정말 바람직한 일이라고 생각합니다. 하지만 엘름의 타입 시스템이 보다 강력해서 더 많은 것을 표현할 수 있으면 좋겠다는 생각이 가끔 드는 것은 어쩔 수 없습니다.</p>
<p>예를 들어 위의 코드의 일부를 엘름으로 표현해본다고 생각해봅시다. 목표는 보유하고 있는 이더리움 양 이상의 이더리움을 전송할 수 없게 하는 것입니다. 엘름은 <code>Cmd Msg</code>타입을 사용해서 사이드 이펙트를 표현하는데, 이 때 <code>Msg</code> 타입은 보통 유니언 타입으로 표현합니다. 여기서는 <code>type Msg = SendEth Int | DepositEth Int</code>라고 정의해봅시다. 이더리움 잔고가 50이더라면 <code>i</code>가 50보다 클 경우에는 <code>Cmd (SendEth i)</code>를 실행할 수 없어야만 합니다. 하지만 이 때 <code>i</code>의 값은 런타임에 결정되므로 이 사이드 이펙트가 올바른지는 런타임에만 확인할 수 있습니다.</p>
<p>반면 이드리스는 디펜던트 타입을 지원하기 때문에 컴파일 타임에 이를 확인할 수 있습니다. <code>ETH</code>는 디펜던트 타입이기 때문에 세 번째 인자가 서로 다른 <code>ETH 0 b 0 0</code>와<code>ETH 0 b 20 0</code>는 서로 다른 타입으로 계산됩니다. 덕분에 컴파일러가 타입 수준에서 여러 사이드 이펙트를 구분할 수 있기 때문에 이 프로그램에서는 올바른 사이드 이펙트만 실행한다는 것을 컴파일 타임에 검증할 수 있습니다. 엘름의 타입 시스템에서는 불가능한 일입니다.</p>
<h2 id="맺음말">맺음말</h2>
<p>조금 현실적인 사용법을 위해 적성된 이드리스 프로그램을 볼 수 있어서 즐거웠습니다. 특히 인풋과 아웃풋의 타입 뿐 아니라 인풋 사이드 이펙트와 아웃풋 사이드 이펙트의 타입도 명시할 수 있다는 점은 정말 놀라웠습니다. 이드리스를 볼 때마다 프로그래밍을 매번 또 새로운 관점에서 바라볼 수 있게 되는 것 같습니다.</p>]]></description>
    <pubDate>Sun, 18 Feb 2018 00:00:00 UT</pubDate>
    <guid>https://harfangk.dev/ko/posts/2018-02-18-thoughts-on-safer-smart-contracts-through-tdd.html</guid>
    <dc:creator>harfangk</dc:creator>
</item>
<item>
    <title>엘름 함수 연산자(|>, <|, >>, <<)에 대한 간단한 설명</title>
    <link>https://harfangk.dev/ko/posts/2018-01-28-elm-function-operators.html</link>
    <description><![CDATA[<h2 id="요약">요약</h2>
<p>엘름 언어에 대해서 기본적인 것을 배우고 나서 엘름 코드를 읽다보면 <code>&gt;&gt;</code>, <code>&lt;&lt;</code>, <code>|&gt;</code>, <code>&lt;|</code> 같은 기묘한 연산자를 보게 되는데, 이는 함수를 조합하거나 적용할 때 사용되는 함수입니다. 이 글에서는 제가 해당 연산자를 언제 어떻게 쓰는지에 대해서 간단히 이야기해보겠습니다. 하지만 먼저 간단히 요점만 정리하면 다음과 같습니다.</p>
<ol type="1">
<li><p><code>|&gt;</code>와 <code>&lt;|</code>는 데이터의 흐름을 시각적으로 명확히 표현하고 싶을 때 사용합니다. 괄호를 중첩해서 사용해도 똑같은 의미를 나타낼 수 있지만 <code>|&gt;</code>나 <code>&lt;|</code> 연산자를 쓰면 더 명확합니다.</p></li>
<li><p><code>&gt;&gt;</code>와 <code>&lt;&lt;</code>는 데이터 흐름과는 무관하게 함수의 조합만을 표현하고 싶을 때 사용합니다. 실제 코드에서는 함수 조합을 통해서 새 함수를 정의하고, 그 함수를 <code>|&gt;</code>나 <code>&lt;|</code>와 함께 사용합니다.</p></li>
<li><p><code>|&gt;</code>와 <code>&gt;&gt;</code>의 쌍이나 <code>&lt;|</code>와 <code>&lt;&lt;</code>의 쌍만 사용하고, 둘을 섞어서 사용하지 않습니다. 연산자의 모양이 방향에 대한 의미를 내포하고 있기 때문에 서로 다른 방향을 나타내는 연산자를 섞어 쓸 경우 뇌에서 인지적 자원을 더 소모하게 됩니다.</p></li>
</ol>
<!--more-->
<h2 id="이런-기묘한-연산자가-정말-필요한가요">이런 기묘한 연산자가 정말 필요한가요?</h2>
<p>반드시 필요한 것은 아닙니다. 이런 연산자가 없어도 똑같은 기능을 다른 방식으로 얼마든지 표현할 수 있기 때문에 엘름 언어는 여전히 잘 작동할 것입니다. 어차피 이 연산자도 편의성 함수일 뿐이니까요. 하지만 이 연산자를 사용하면 데이터의 흐름이나 코드의 구조를 시각적으로 코드 안에서도 표현할 수 있기 때문에 더 이해하기 쉬운 코드를 작성할 수 있습니다. 예시를 살펴볼까요?</p>
<h2 id="예시">예시</h2>
<h3 id="연산자"><code>|&gt;</code> 연산자</h3>
<p>임의의 정수의 리스트가 주어졌을 때, 각 정수의 첫 번째 자리수를 리스트로 돌려주는 함수를 정의해봅시다. 먼저 함수 연산자 없이 간단히 구현해봅시다.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode elm"><code class="sourceCode elm"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="fu">getFirstDigits</span> : <span class="dt">List</span> <span class="dt">Int</span> <span class="op">-&gt;</span> <span class="dt">List</span> <span class="dt">Int</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="fu">getFirstDigits</span> <span class="fu">list</span> <span class="op">=</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>        <span class="fu">stringList</span> <span class="op">=</span> <span class="dt">List</span><span class="op">.</span><span class="fu">map</span> <span class="fu">toString</span> <span class="fu">list</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>        <span class="fu">firstStringDigits</span> <span class="op">=</span> <span class="dt">List</span><span class="op">.</span><span class="fu">map</span> (<span class="dt">String</span><span class="op">.</span><span class="kw">left</span> <span class="dv">1</span>) <span class="fu">stringList</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>        <span class="fu">firstResultIntDigits</span> <span class="op">=</span> <span class="dt">List</span><span class="op">.</span><span class="fu">map</span> (<span class="dt">String</span><span class="op">.</span><span class="fu">toInt</span>) <span class="fu">firstStringDigits</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a>        <span class="fu">firstIntDigits</span> <span class="op">=</span> <span class="dt">List</span><span class="op">.</span><span class="fu">map</span> (<span class="dt">Result</span><span class="op">.</span><span class="fu">withDefault</span> <span class="dv">0</span>) <span class="fu">firstResultIntDigits</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a>    <span class="kw">in</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a>        <span class="fu">firstIntDigits</span></span></code></pre></div>
<p>각 중간 단계의 결과를 변수에 할당하고 다음 단계로 넘겨주었습니다. 이는 명령형 언어에서 흔히 볼 수 있는 일반적인 형태입니다. 하지만 파이프 연산자라고 불리는 <code>|&gt;</code>를 사용하면 해당 코드를 더 이해하기 쉽게 쓸 수 있습니다.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode elm"><code class="sourceCode elm"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- 여러 줄에 걸쳐 쓸 경우:</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="fu">getFirstDigits</span> : <span class="dt">List</span> <span class="dt">Int</span> <span class="op">-&gt;</span> <span class="dt">List</span> <span class="dt">Int</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="fu">getFirstDigits</span> <span class="fu">list</span> <span class="op">=</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>    <span class="fu">list</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>    <span class="op">|&gt;</span> <span class="dt">List</span><span class="op">.</span><span class="fu">map</span> <span class="fu">toString</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a>    <span class="op">|&gt;</span> <span class="dt">List</span><span class="op">.</span><span class="fu">map</span> (<span class="dt">String</span><span class="op">.</span><span class="kw">left</span> <span class="dv">1</span>)</span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a>    <span class="op">|&gt;</span> <span class="dt">List</span><span class="op">.</span><span class="fu">map</span> (<span class="dt">String</span><span class="op">.</span><span class="fu">toInt</span>)</span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a>    <span class="op">|&gt;</span> <span class="dt">List</span><span class="op">.</span><span class="fu">map</span> (<span class="dt">Result</span><span class="op">.</span><span class="fu">withDefault</span> <span class="dv">0</span>)</span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a><span class="co">-- 한 줄에 쓸 경우:</span></span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a><span class="fu">getFirstDigits</span> : <span class="dt">List</span> <span class="dt">Int</span> <span class="op">-&gt;</span> <span class="dt">List</span> <span class="dt">Int</span></span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a><span class="fu">getFirstDigits</span> <span class="fu">list</span> <span class="op">=</span></span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a>    <span class="fu">list</span> <span class="op">|&gt;</span> <span class="dt">List</span><span class="op">.</span><span class="fu">map</span> <span class="fu">toString</span> <span class="op">|&gt;</span> <span class="dt">List</span><span class="op">.</span><span class="fu">map</span> (<span class="dt">String</span><span class="op">.</span><span class="kw">left</span> <span class="dv">1</span>) <span class="op">|&gt;</span> <span class="dt">List</span><span class="op">.</span><span class="fu">map</span> (<span class="dt">String</span><span class="op">.</span><span class="fu">toInt</span>) <span class="op">|&gt;</span> <span class="dt">List</span><span class="op">.</span><span class="fu">map</span> (<span class="dt">Result</span><span class="op">.</span><span class="fu">withDefault</span> <span class="dv">0</span>)</span></code></pre></div>
<p><code>|&gt;</code>를 사용하면 중간 변수에 값을 할당할 필요 없이 결과값을 다음 단계로 전달할 수 있습니다. 또한 <code>|&gt;</code>는 데이터가 어디로 이동할지를 시각적으로 보여줍니다.</p>
<p><code>|&gt;</code>는 첫 번째 인자를 함수인 두 번째 인자로 전달해줍니다. <code>(|&gt;) : a -&gt; (a -&gt; b) -&gt; b</code>라고 정의되어 있죠. 이를 사용하면 이전 단계의 결과값을 다음 단계로 계속 전달해주면서 함수를 여러 개의 독립적인 단계가 아니라 서로 연관된 단계로 구성된 하나의 파이프라인으로 표현할 수 있습니다.</p>
<p>앞서 말했듯이 <code>|&gt;</code>는 편의성 함수일 뿐이기 때문에 이를 쓰지 않아도 다음과 같이 표현할 수 있습니다.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode elm"><code class="sourceCode elm"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="fu">getFirstDigits</span> : <span class="dt">List</span> <span class="dt">Int</span> <span class="op">-&gt;</span> <span class="dt">List</span> <span class="dt">Int</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="fu">getFirstDigits</span> <span class="fu">list</span> <span class="op">=</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>    (<span class="dt">List</span><span class="op">.</span><span class="fu">map</span> (<span class="dt">Result</span><span class="op">.</span><span class="fu">withDefault</span> <span class="dv">0</span>) (<span class="dt">List</span><span class="op">.</span><span class="fu">map</span> (<span class="dt">String</span><span class="op">.</span><span class="fu">toInt</span>) (<span class="dt">List</span><span class="op">.</span><span class="fu">map</span> (<span class="dt">String</span><span class="op">.</span><span class="kw">left</span> <span class="dv">1</span>) (<span class="dt">List</span><span class="op">.</span><span class="fu">map</span> <span class="fu">toString</span> <span class="fu">list</span>))))</span></code></pre></div>
<p>하지만 <code>|&gt;</code>를 사용하면 괄호를 중첩해서 쓰거나, 다음에 어떤 단계로 이어질 지 직접 기억하고 있을 필요가 없어집니다. 일반적으로 <code>|&gt;</code>는 서로 연관된 작업들을 표현하려할 때 유용합니다.</p>
<h3 id="연산자-1"><code>&lt;|</code> 연산자</h3>
<p><code>&lt;|</code>는 <code>|&gt;</code>를 뒤집은 함수인데, 두 번째 인자를 함수인 첫 번째 인자에 전달하며 <code>(&lt;|) : (a -&gt; b) -&gt; a -&gt; b</code>라고 정의되어 있습니다. <code>&lt;|</code>를 사용하면 앞서 적은 함수를 다음과 같이 표현할 수 있습니다.</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode elm"><code class="sourceCode elm"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="fu">getFirstDigits</span> : <span class="dt">List</span> <span class="dt">Int</span> <span class="op">-&gt;</span> <span class="dt">List</span> <span class="dt">Int</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="fu">getFirstDigits</span> <span class="fu">list</span> <span class="op">=</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>    <span class="dt">List</span><span class="op">.</span><span class="fu">map</span> (<span class="dt">Result</span><span class="op">.</span><span class="fu">withDefault</span> <span class="dv">0</span>) <span class="op">&lt;|</span> <span class="dt">List</span><span class="op">.</span><span class="fu">map</span> (<span class="dt">String</span><span class="op">.</span><span class="fu">toInt</span>) <span class="op">&lt;|</span> <span class="dt">List</span><span class="op">.</span><span class="fu">map</span> (<span class="dt">String</span><span class="op">.</span><span class="kw">left</span> <span class="dv">1</span>) <span class="op">&lt;|</span> <span class="dt">List</span><span class="op">.</span><span class="fu">map</span> <span class="fu">toString</span> <span class="op">&lt;|</span> <span class="fu">list</span></span></code></pre></div>
<p>예시에서 볼 수 있듯이 <code>&lt;|</code>를 사용하면 우측에서 좌측으로 가는 흐름을 시각적으로 표현할 수 있습니다. 비슷하게 우에서 좌로 향하는 수학적 표기법이나 언어에 익숙하다면 매우 편하게 느껴질 것입니다. 개인적으로는 좌상단에서 우하단으로 가는 흐름이 가장 익숙하기 때문에 우에서 좌로 가는 흐름은 불편하게 느껴서 이 연산자는 거의 사용하지 않습니다. 그래도 유용하게 사용할 때가 있습니다. 예를 들어 함수의 마지막 인자가 여러 줄에 걸친 함수일 경우에는 아래와 같이 사용합니다.</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode elm"><code class="sourceCode elm"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- &lt;| 사용</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="fu">test</span> : <span class="dt">Test</span><span class="op">.</span><span class="dt">Test</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="fu">test</span> <span class="st">&quot;subtraction should have identity property&quot;</span> <span class="op">&lt;|</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>    \<span class="fu">_</span> <span class="op">-&gt;</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>        <span class="kw">let</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a>            <span class="fu">a</span> <span class="op">=</span> <span class="fu">negate</span> (<span class="dv">2</span> <span class="op">^</span> <span class="dv">50</span> <span class="op">+</span> <span class="dv">32</span>)</span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a>            <span class="fu">b</span> <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a>        <span class="kw">in</span></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a>            <span class="dt">Expect</span><span class="op">.</span><span class="fu">equal</span> (<span class="fu">a</span> <span class="op">-</span> <span class="fu">b</span>) <span class="fu">a</span></span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a><span class="co">-- 괄호 사용</span></span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a><span class="fu">test</span> : <span class="dt">Test</span><span class="op">.</span><span class="dt">Test</span></span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a><span class="fu">test</span> <span class="st">&quot;subtraction should have identity property&quot;</span></span>
<span id="cb5-14"><a href="#cb5-14" aria-hidden="true" tabindex="-1"></a>    (\<span class="fu">_</span> <span class="op">-&gt;</span></span>
<span id="cb5-15"><a href="#cb5-15" aria-hidden="true" tabindex="-1"></a>        <span class="kw">let</span></span>
<span id="cb5-16"><a href="#cb5-16" aria-hidden="true" tabindex="-1"></a>            <span class="fu">a</span> <span class="op">=</span> <span class="fu">negate</span> (<span class="dv">2</span> <span class="op">^</span> <span class="dv">50</span> <span class="op">+</span> <span class="dv">32</span>)</span>
<span id="cb5-17"><a href="#cb5-17" aria-hidden="true" tabindex="-1"></a>            <span class="fu">b</span> <span class="op">=</span> <span class="dv">0</span></span>
<span id="cb5-18"><a href="#cb5-18" aria-hidden="true" tabindex="-1"></a>        <span class="kw">in</span></span>
<span id="cb5-19"><a href="#cb5-19" aria-hidden="true" tabindex="-1"></a>            <span class="dt">Expect</span><span class="op">.</span><span class="fu">equal</span> (<span class="fu">a</span> <span class="op">-</span> <span class="fu">b</span>) <span class="fu">a</span></span>
<span id="cb5-20"><a href="#cb5-20" aria-hidden="true" tabindex="-1"></a>    )</span></code></pre></div>
<p><code>&lt;|</code>를 사용하면 <code>&lt;|</code> 다음에 오는 코드를 모두 이해하기 전에는 <code>&lt;|</code> 전에 오는 코드를 신경쓸 필요 없다고 시각적으로 명확하게 표현할 수 있어서 유용합니다.</p>
<h3 id="연산자-2"><code>&gt;&gt;</code>, <code>&lt;&lt;</code> 연산자</h3>
<p>이 두 연산자를 사용하면 두 개의 함수를 하나의 함수로 조합할 수 있습니다. 예시를 볼까요?</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode elm"><code class="sourceCode elm"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="fu">toString</span> : <span class="fu">a</span> <span class="op">-&gt;</span> <span class="dt">String</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a><span class="dt">String</span><span class="op">.</span><span class="fu">length</span> : <span class="dt">String</span> <span class="op">-&gt;</span> <span class="dt">Int</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a><span class="fu">toString</span> <span class="op">&gt;&gt;</span> <span class="dt">String</span><span class="op">.</span><span class="fu">length</span> : <span class="fu">a</span> <span class="op">-&gt;</span> <span class="dt">Int</span></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a><span class="dt">String</span><span class="op">.</span><span class="fu">length</span> <span class="op">&lt;&lt;</span> <span class="fu">toString</span> : <span class="fu">a</span> <span class="op">-&gt;</span> <span class="dt">Int</span></span></code></pre></div>
<p>함수 시그니처에서 볼 수 있듯이 <code>&gt;&gt;</code>와 <code>&lt;&lt;</code>는 두 함수를 하나로 조합해줍니다. 두 연산자의 정의에서는 이를 보다 추상적으로 표현하고 있습니다.</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode elm"><code class="sourceCode elm"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a>(<span class="op">&lt;&lt;</span>) : (<span class="fu">b</span> <span class="op">-&gt;</span> <span class="fu">c</span>) <span class="op">-&gt;</span> (<span class="fu">a</span> <span class="op">-&gt;</span> <span class="fu">b</span>) <span class="op">-&gt;</span> (<span class="fu">a</span> <span class="op">-&gt;</span> <span class="fu">c</span>)</span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a>(<span class="op">&gt;&gt;</span>) : (<span class="fu">a</span> <span class="op">-&gt;</span> <span class="fu">b</span>) <span class="op">-&gt;</span> (<span class="fu">b</span> <span class="op">-&gt;</span> <span class="fu">c</span>) <span class="op">-&gt;</span> (<span class="fu">a</span> <span class="op">-&gt;</span> <span class="fu">c</span>)</span></code></pre></div>
<p>이 둘은 서로 반대 방향으로 작동한다는 것을 제외하면 기능적으로는 동일합니다. 어떤 방향으로 작동하는지는 연산자의 화살표 방향을 보면 알 수 있습니다. 하지만 거의 동일하다면 둘 중 어떤 것을 써야할까요? 이는 개인적 취향에 달려있다고 봅니다. 조합 연산자를 파이프 연산자와 함께 사용하면 예시 함수를 다음과 같이 표현할 수 있습니다.</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode elm"><code class="sourceCode elm"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="fu">getFirstDigits</span> : <span class="dt">List</span> <span class="dt">Int</span> <span class="op">-&gt;</span> <span class="dt">List</span> <span class="dt">Int</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="fu">getFirstDigits</span> <span class="fu">list</span> <span class="op">=</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a>    <span class="dt">List</span><span class="op">.</span><span class="fu">map</span> (<span class="dt">Result</span><span class="op">.</span><span class="fu">withDefault</span> <span class="dv">0</span>) <span class="op">&lt;&lt;</span> <span class="dt">List</span><span class="op">.</span><span class="fu">map</span> (<span class="dt">String</span><span class="op">.</span><span class="fu">toInt</span>) <span class="op">&lt;&lt;</span> <span class="dt">List</span><span class="op">.</span><span class="fu">map</span> (<span class="dt">String</span><span class="op">.</span><span class="kw">left</span> <span class="dv">1</span>) <span class="op">&lt;&lt;</span> <span class="dt">List</span><span class="op">.</span><span class="fu">map</span> <span class="fu">toString</span> <span class="op">&lt;|</span> <span class="fu">list</span></span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a><span class="fu">getFirstDigits</span> : <span class="dt">List</span> <span class="dt">Int</span> <span class="op">-&gt;</span> <span class="dt">List</span> <span class="dt">Int</span></span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a><span class="fu">getFirstDigits</span> <span class="fu">list</span> <span class="op">=</span></span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a>    <span class="fu">list</span> <span class="op">|&gt;</span> <span class="dt">List</span><span class="op">.</span><span class="fu">map</span> <span class="fu">toString</span> <span class="op">&gt;&gt;</span> <span class="dt">List</span><span class="op">.</span><span class="fu">map</span> (<span class="dt">String</span><span class="op">.</span><span class="kw">left</span> <span class="dv">1</span>) <span class="op">&gt;&gt;</span> <span class="dt">List</span><span class="op">.</span><span class="fu">map</span> (<span class="dt">String</span><span class="op">.</span><span class="fu">toInt</span>) <span class="op">&gt;&gt;</span> <span class="dt">List</span><span class="op">.</span><span class="fu">map</span> (<span class="dt">Result</span><span class="op">.</span><span class="fu">withDefault</span> <span class="dv">0</span>)</span></code></pre></div>
<p>우에서 좌 흐름을 선호한다면 <code>&lt;&lt;</code>와 <code>&lt;|</code>의 쌍을 사용하시고, 반대로 좌에서 우 흐름을 선호하시면 <code>&gt;&gt;</code>와 <code>|&gt;</code>의 쌍을 사용하시면 됩니다. 단, 방향이 다른 연산자를 섞어 쓸 경우 뇌에 부담을 주니 섞어 쓰지만 말아주세요.</p>
<p>함수 조합 연산자는 언제 쓸모가 있을까요? 추상적으로 말하자면 이를 사용해서 여러 함수를 조합해서 보다 큰 함수를 만들어낼 수 있습니다. 실질적으로는 함수를 작성할 때 매우 자유롭게 표현할 수 있게됩니다. 예시 함수를 다시 살펴봅시다.</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode elm"><code class="sourceCode elm"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="fu">getFirstDigits</span> : <span class="dt">List</span> <span class="dt">Int</span> <span class="op">-&gt;</span> <span class="dt">List</span> <span class="dt">Int</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a><span class="fu">getFirstDigits</span> <span class="fu">list</span> <span class="op">=</span></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a>    <span class="fu">list</span></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a>    <span class="op">|&gt;</span> <span class="dt">List</span><span class="op">.</span><span class="fu">map</span> <span class="fu">toString</span></span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a>    <span class="op">|&gt;</span> <span class="dt">String</span><span class="op">.</span><span class="kw">left</span> <span class="dv">1</span></span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a>    <span class="op">|&gt;</span> <span class="dt">List</span><span class="op">.</span><span class="fu">map</span> (<span class="dt">String</span><span class="op">.</span><span class="fu">toInt</span> <span class="op">&gt;&gt;</span> <span class="dt">Result</span><span class="op">.</span><span class="fu">withDefault</span> <span class="dv">0</span>)</span></code></pre></div>
<p>여기서는 <code>&gt;&gt;</code>을 사용해서 두 개의 작은 함수를 묶어내어 문자열에서 정수로 변환하는 함수를 만들었습니다. 그 두 함수는 독립적일 때보다는 하나로 조합되었을 때 보다 의미있다고 생각했기 때문입니다. 이렇게 논리적으로 긴밀히 연결된 함수들을 하나의 함수로 묶어낼 때 <code>&gt;&gt;</code>를 가장 유용하게 사용할 수 있습니다. 물론 새 함수로 분리해낼 수도 있지만 <code>&gt;&gt;</code>를 사용하면 인라인에서도 같은 일을 할 수도 있습니다.</p>
<h2 id="함수-연산자를-사용할-때-주의점">함수 연산자를 사용할 때 주의점</h2>
<p>이 글에서 예시로 든 함수를 표현할 수 있는 방법은 정말 많습니다. 예를 들어 아래와 같이 작성할 수도 있습니다.</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode elm"><code class="sourceCode elm"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- 별도 함수로 분리</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a><span class="fu">getFirstDigits</span> : <span class="dt">List</span> <span class="dt">Int</span> <span class="op">-&gt;</span> <span class="dt">List</span> <span class="dt">Int</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a><span class="fu">getFirstDigits</span> <span class="fu">list</span> <span class="op">=</span></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a>    <span class="fu">list</span></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a>    <span class="op">|&gt;</span> <span class="dt">List</span><span class="op">.</span><span class="fu">map</span> <span class="fu">toString</span></span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a>    <span class="op">|&gt;</span> <span class="dt">String</span><span class="op">.</span><span class="kw">left</span> <span class="dv">1</span></span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a>    <span class="op">|&gt;</span> <span class="dt">List</span><span class="op">.</span><span class="fu">map</span> <span class="fu">stringToInt</span></span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true" tabindex="-1"></a><span class="fu">stringToInt</span> : <span class="dt">String</span> <span class="op">-&gt;</span> <span class="dt">Int</span></span>
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true" tabindex="-1"></a><span class="fu">stringToInt</span> <span class="fu">s</span> <span class="op">=</span> <span class="dt">String</span><span class="op">.</span><span class="fu">toInt</span> <span class="op">&lt;&lt;</span> <span class="dt">Result</span><span class="op">.</span><span class="fu">withDefault</span> <span class="op">&lt;|</span> <span class="fu">s</span></span></code></pre></div>
<p>저 함수를 작성할 수 있는 방법을 이 글에서만 9가지를 보여주었습니다. 함수 연산자가 없었다면 괄호를 중첩해서 쓰거나, 중간 변수를 할당하거나, 새 함수를 정의하는 등의 방법만 쓸 수 있었을 것입니다. 하지만 함수 연산자를 쓰면 코드를 작성할 때 표현할 수 있는 방식이 훨씬 다채로워집니다.</p>
<p>하지만 이를 남용하면 오히려 더 코드가 혼란스러워질 것은 자명합니다. 함수를 정의할 때 다양한 방식을 섞어 쓰면 다른 사람들이 코드를 읽고 이해하기 더 어려워집니다. 물론 여기에는 미래의 나 자신도 포함되죠. 그러니 함수 연산자를 사용할 때는 통일된 방식으로 사용할 수 있도록 스스로도 절제하고, 명확한 관습을 만들어두는 것이 중요합니다.</p>
<h2 id="결론">결론</h2>
<p>엘름에 있는 네 개의 함수 연산자 <code>&gt;&gt;</code>, <code>&lt;&lt;</code>, <code>|&gt;</code>, <code>&lt;|</code>를 소개하고 예시 함수를 통해서 이를 어떻게 사용할 수 있는 지를 간단히 살펴보았습니다. 해당 연산자에 대한 학문적인 논의는 의도적으로 이 글에서 다루지 않았는데, 이에 대한 이미 좋은 글이 많아서이기도 하고, 연산자를 실제로 사용하는 방법에 대해서는 오히려 글이 너무 없어서이기도 합니다. 이 글이 그 공백을 조금이나마 메꿀 수 있었으면 좋겠습니다.</p>]]></description>
    <pubDate>Sun, 28 Jan 2018 00:00:00 UT</pubDate>
    <guid>https://harfangk.dev/ko/posts/2018-01-28-elm-function-operators.html</guid>
    <dc:creator>harfangk</dc:creator>
</item>

    </channel>
</rss>
