<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by 아프니까병원이다 on Medium]]></title>
        <description><![CDATA[Stories by 아프니까병원이다 on Medium]]></description>
        <link>https://medium.com/@0e?source=rss-6357424e5eef------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>Stories by 아프니까병원이다 on Medium</title>
            <link>https://medium.com/@0e?source=rss-6357424e5eef------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Tue, 19 May 2026 03:52:43 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@0e/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[그래서, ROP가 뭔데 씹덕아 (Railway oriented programming)]]></title>
            <link>https://0e.medium.com/%E1%84%80%E1%85%B3%E1%84%85%E1%85%A2%E1%84%89%E1%85%A5-rop%E1%84%80%E1%85%A1-%E1%84%86%E1%85%AF%E1%86%AB%E1%84%83%E1%85%A6-%E1%84%8A%E1%85%B5%E1%86%B8%E1%84%83%E1%85%A5%E1%86%A8%E1%84%8B%E1%85%A1-railway-oriented-programming-4e8070c04bda?source=rss-6357424e5eef------2</link>
            <guid isPermaLink="false">https://medium.com/p/4e8070c04bda</guid>
            <category><![CDATA[typescript]]></category>
            <category><![CDATA[functional-programming]]></category>
            <dc:creator><![CDATA[아프니까병원이다]]></dc:creator>
            <pubDate>Sun, 15 Mar 2020 14:10:28 GMT</pubDate>
            <atom:updated>2021-11-26T12:56:46.544Z</atom:updated>
            <content:encoded><![CDATA[<h3>그래서, ROP가 뭔데? (Railway oriented programming)</h3><blockquote>이 글은 <a href="https://github.com/icepeng/">icepeng</a>이 제게 해준 이야기를 바탕으로 작성되었습니다. 함수형 프로그래밍을 알게해준, 그리고 가르쳐준 icepeng에게 감사를 전합니다.</blockquote><p>서버에서 “유저의 이름과 이메일을 업데이트 해주세요” 라는 요구 사항이 있다고 생각해보자 개발자는 다음의 요소들을 떠올리며 코딩을 시작하게 될것이다. (그리고 아무런 오류 없이 잘 동작할 것이라고 생각하고 행복회로를 가동한다)</p><ul><li>Request 받기</li><li>Request 검증 / 정규화</li><li>로그 남기기</li><li>DB에 Update</li><li>결과 리턴</li></ul><p>대충 코드로 간단하게 표현해보자면 이런 식이 될 것이다.</p><pre>interface Input {<br>    name: string;<br>    email: string;<br>}<br><br>function app(input: Input) {<br>    validateInput(input);<br>    canonicalizeEmail(input);<br>    console.log(input);<br>    db.updateUser(input);<br>    return &#39;Success&#39;;<br>}</pre><p>예외처리 없이 이렇게만 됐으면 얼마나 좋겠나 싶지만, 현실은 알다시피 시궁창이다. 갖가지 상황에서 오만가지의 오류가 일어난다. 개발자의 행복회로는 불타고 만다. 슬픈 상황이 벌어지고 만다는 것이다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*J1I9SIC7ClKXJ6cdKaescg.png" /></figure><blockquote>“A program is a spell cast over a computer, turning input into error messages”</blockquote><p>아까 적어 놨던 요소들을 다시 보자. 실제로 서버에선 각 요소마다 여러가지 예외적인 상황들이 발생할 것이다.</p><ul><li>Request 받기</li><li>Request 검증 / 정규화<br><em>❌ Name is blank<br>❌ Invalid email</em></li><li>로그 남기기</li><li>DB에 Update<br><em>🚨 User not found<br>❌ DB Error</em></li><li>결과 리턴</li></ul><p>Request를 검증 하는 도중, 이름이 없을 수도 있고, 이메일이 올바르지 못할 수도 있다. 막상 DB에 업데이트를 하려고 하니, 해당하는 유저가 없을 수도 있고, DB자체가 오류가 날 수 도 있다.</p><p>그래서 실제로는 코드가 이렇게 변한다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*C-KkNA_4EYkrsPvHI1EKFQ.png" /></figure><p>이랬던 코드가</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*k1RQoFXLCkDeiznm-ueMIg.png" /></figure><p>validateInput 에 대한 예외처리가 생기고</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*WWEIg0AGN33sYU7sdNNI3Q.png" /></figure><p>DB와 관련된 예외처리가 생기고</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*jSUAFPV9g0jpVxjQovcG0w.png" /></figure><p>DB에 대한 예외처리가 또 추가 된다</p><p>다들 한번쯤은 겪어 봤을 일이다.</p><p>너무 슬픈 일이다. 원래 5줄이었던 코드는 15줄의 코드로 늘어났고, 200% 가량 코드가 불어나 버렸다.</p><p>이러한 현상은 심심치 않게 매일같이 벌어지는 현상이다.</p><p>이제, 함수형 코드에서는 이러한 것들이 어떻게 될지, 이런 슬픈 일들이 반복 될지 알아보도록 하겠다.</p><p>처음에 작성했었던(에러 처리가 없는) 코드를 함수형으로 표현해보면 이렇게 될 것이다.</p><pre>const app = pipe(<br>    validateInput,<br>    canonicalizeEmail,<br>    logInput,<br>    updateDb,<br>    returnMessage,<br>)</pre><p>그리고 에러 처리가 들어간 함수형 코드는 이렇게 된다.</p><pre>const app = pipe(<br>    validateInput,<br>    canonicalizeEmail,<br>    logInput,<br>    updateDb,<br>    returnMessage,<br>)</pre><p>(대충 물음표 짤)</p><p>똑같다. 함수형 프로그래밍을 하게되면 코드의 아름다운 모습을 지키면서도 에러 처리도 가능하게 된다.</p><h3>함수형 프로그래밍</h3><p>함수란 무엇일까?</p><p>함수는,</p><ul><li>하나의 Input을 받아서</li><li>뭔가 한다음</li><li>하나의 Output을 리턴한다</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*bqhW7VGKCJPcjnmMpNT5Pg.png" /></figure><p>이것이 함수의 정의인데 좀더 정확히는 “순수함수”가 좀 더 함수형 프로그래밍에서 설명하는 함수에 적합하다.</p><p>순수함수란,</p><ul><li>Input이 같으면 Output도 같아야 한다</li><li>1을 넣으면 2가 나오는 함수는 어떤 상황에서든 1을 넣으면 2가 나와야 한다</li></ul><p>이렇게 순수함수를 사용하게되면 다음과 같은 이점을 얻을 수 있다.</p><ul><li>캐싱 가능<br>➡️ 미리 연산을 해놓고 캐싱해놓으면, 다음에도 같은 값이 들어올때 캐싱한 값을 리턴하면 된다</li><li>매우 쉬운 테스팅<br>➡️ 한 함수에 한가지 목적을 가진 로직만 있으므로, 좀더 엄격한 테스트를 짤 수 있다.</li></ul><p>하지만 일반적으로 순수 함수로만 코드를 구성하는건 생각보다 지키기 어렵다.</p><h4>숨겨진 Input</h4><p>우리가 일반적으로 짠 코드들에는 숨겨진 Input이 존재한다.</p><p>대표적인 것이 바로 this 다. Input이 같아도 this.data 가 변경되었다면 다른 Output이 나올 것이다. 그래서 this.data도 Input이나 다름 없게 된다.</p><p>그리고 전역 변수, 예로 환경변수를 들 수 있을 것 같다. 환경 변수에 따라 결과가 바뀐다면 환경 변수도 숨겨진 Input 이다.</p><p>마지막으로 I/O, 가장 일반적인 상황이 될텐데, 함수 안에서 DB에 쿼리를 한다면, 쿼리 결과도 숨겨진 Input 될 것이다.</p><h4>숨겨진 Output</h4><p>숨겨진 Input이 있다면, 숨겨진 Output도 있다.</p><p>대표적으로 throw new Error 가 있는데, 숨겨진 Output인 이유는 리턴 타입에 명시되지 않는다는 점 때문이다.</p><p>이게 무슨의미 인가 하면, VS Code 에서 이 함수의 정의를 보려고 함수에 마우스 올려 본다고 하자. 이때, 뜨는 정보에서는 throw new Error 에 대한 정보가 전혀 나오지 않아, 이 함수에서 에러가 날 가능성이 있는지 전혀 알 수 없다.</p><p>그래서 Railway oriented programming 은 에러도 타입에 포함시켜 처리한다. 그리고 이것은 Railway oriented programming의 핵심이다.</p><h4>함수형 프로그래밍으로 나아가기</h4><p>Javascript(or Typescript)에서 함수형 프로그래밍으로 나아가기 위해서는 다음과 같은 조건이 필요하다.</p><ul><li>let이 없는 코드</li><li>this가 없는 코드</li><li>throw가 없는 코드<br>✨ Railway oriented programming의 핵심</li></ul><p>이러한 원칙을 지키게 되면, 함수형으로 짜고 싶지 않아도, 자연스럽게 함수형 코드가 되어버린다.</p><p>let이 없는 코드가 어떻게 함수형으로 변하는지 알아보자</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*ZwczsV2qWEJ32_lmX8QSEg.png" /></figure><p>위의 코드가 let을 사용한 일반적인 절차지향적인 Javascript 코드, 밑의 코드가 let이 없는 Javascript 코드다.</p><p>위의 코드는 sum 의 값이 변경되지만, 밑의 코드에서는 sum의 값이 불변성을 얻으므로, 해당 값을 변경시키려고 하면 컴파일러는 에러를 띄우게 된다. 결과적으로 더 안전한 코드가 된다.</p><h4>슬픈 상황도 설계에 포함해보자</h4><p><strong>throw가 없는 코드</strong>를 중점적으로 들여다 보며, 어떻게 슬픈 상황을 함수형 디자인으로 매끄럽게 처리할 수 있는지 알아보자</p><h4>일반적 디자인</h4><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*uhdV8APCErvw1umbIeubUA.png" /></figure><p>슬픈 상황에서 일반적 디자인은 함수 중간에 탈출하여 에러를 내뱉을 것이다. 보통 함수마다 throw new Error 를 이용하여 try-catch로 처리 할 것이다.</p><h4>함수형 디자인</h4><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*h-uEw4gsh_at1inxY7dwIQ.png" /></figure><p>그렇담 throw new Error 가 없는 함수형 프로그래밍에서는 중간에 오류가 나면 어떻게 행동할까?</p><p>중간에 오류가 나면 어떻게 끊고, 다른 함수를 실행하지 않고, 마지막에 에러를 어떻게 뱉어 낼까?</p><blockquote>이제 본격적으로 Typescript를 사용한다. 해당 언어에 대한 기본적인 지식이 있어야 한다.</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*-i6vszbwaVr9X0lGjZPn0g.png" /></figure><p>방금 전 말했듯이, ROP에서는 에러도 타입으로 본다고 했다. 그러니 Result 타입을 만들고 여기에 성공과 각종 에러와 관련된 결과 타입을 만들어서 함수가 해당 타입으로 내뱉게 하면 된다.</p><p>그런데 에러타입이 너무 많아지면 복잡해지니, Success와 Failure 로 줄이자</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*2Ng8IZwxeBg_auv-FKnwlQ.png" /></figure><p>그리고 함수는 데이터가 리턴되어야 하는데 이렇게 두개의 타입만 있게되면 데이터를 포함할 수 없으니, 제너릭을 사용해서 다음과 같이 타입을 선언한다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*eeKGWRKDmrVKlC-0qACxgw.png" /></figure><p>이렇게 되면 Success에는 데이터를, Failure에서는 실패 사유를 가지고 있을 수 있게 된다.</p><p>그러면 이제 어떻게 함수에서 중간에 탈출을 할것인가?</p><p>각 단계는 함수 하나씩에 대응될 것이고,<br> 각 함수는 “Success” | “Failure” 두개의 Union type을 리턴할 것이고,<br> 동일한 원리로 각 단계도 더 작은 함수들을 연결해서 만들 수 있다.<br> 어느 단계에서건 발생한 에러는 하나의 “Failure” 경로에 합쳐지게 될 것이다.</p><p>그런데, 에러가 발생하면 다른 경로로 어떻게 보내버릴까?</p><h3>Railway oriented programming</h3><blockquote>A functional approach to error handling</blockquote><p>함수를 하나의 철도라고 생각해보자.</p><p>사과를 바나나로 만드는 철도 Function1 있다고 가정해보자.<br> 그리고 바나나를 체리로 만드는 철도 Function2 가 있다.</p><p>그리고 이 두개를 이어 붙인다고 생각해보자</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*K4xDazl-6SvNimMyOPByuQ.png" /></figure><p>이어 붙이는 이 행위를 함수 합성이라고 하는데 함수를 합성하게 되면 사과를 체리로 만드는 함수가 만들어 진다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*IE_760OO-s5ROsu8n2jjCA.png" /></figure><p>이제 이 함수합성을 응용해보자</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*Fv5qwT34hRvXh4OGoi94nw.png" /></figure><p>함수 Validate 가 에러를 리턴하게 하려면 위와 같은 코드가 나올 것이다.<br> 중요한건 값을 리턴할 때, success 혹은 failure를 감싸서 리턴해야 된다는 점이다.</p><p>좀더 자세하게 해당 interface를 확인해 보자</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*7x5uMzbKTmPan5mDr7TYvg.png" /></figure><p>success는 { success } 를 true 로 리턴 하고, failure는 { success } 를 false 로 리턴 하고 message를 보낸다.</p><p>success 혹은 failure를 감싸서 리턴을 하게 되면 이 일정한 규칙에 따라 함수들은 값을 리턴하게 될 것이다.</p><p>그래서 success 와 failure를 합한 <strong>합타입을 TwoTrack</strong>으로 정의 하였다.</p><p>이 TwoTrack을 리턴하는 함수는 결국 철도로 따지면 하나의 분기점이 되게 된다. 이 분기점은, Input을 받아서, <strong>성공 트랙</strong> 혹은 <strong>실패 트랙</strong>으로 나눠진다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*xpfyCaMaZTI1bp5JRo232w.png" /></figure><p>함수합성… 그리고 분기점… 이제 우리는 이 분기점들을 이어 붙여나가 볼 것이다.</p><p>Validate를 한 다음, UpdateDb를 한다고 한다면, 성공한 경우에만 UpdateDb 를 해야된다. 실패한 경우에는 실패 트랙으로 그냥 지나가야 된다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*kWG7hFLV98sCDvt3WbSdhg.png" /></figure><p>이 둘을 이어 붙이면 이런 느낌이 된다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*p0FFfWMHtFK9sUvFT0Mz8g.png" /></figure><p>그리고 이러한 함수들을 이어 붙이면 이런 느낌이 된다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*_6SXZVwQJHTjXBOYvn3xIw.png" /></figure><p>아무리 많은 함수가 이어진다 해도, 다 이어 붙일 수 있게 되고, 하나의 투-트랙 모델이 된다.</p><p>이 투-트랙 모델을 사용하게 되면 에러 처리에 대한 “Railway oriented Programming”의 접근 방식을 취할 수 있게 된다.</p><p>성공하면 계속 성공한 트랙으로, 실패하면 실패한 트랙으로, 성공하다 실패하면 실패한 트랙으로, 마치 철도가 두개의 차선으로 동작하는 것 처럼 보인다.</p><p>하지만 우린 아까 함수 합성에 대해서 배웠다.<br> 함수 합성은 앞의 함수의 Output이 그 다음 함수의 Input 타입과 일치해야만 가능 하다.</p><p>그렇기에 단일 트랙 함수들을 연결하는건 쉽다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*nzh4qvlWVI8cO0xEn0BvKA.png" /></figure><p>이와 같이 투-트랙 함수들을 연결하는 것도 마찬가지로 쉽다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*uOLzbR2hD0AHbsuY9ZVYmw.png" /></figure><p>하지만 현재 우리가 가지고 있는 함수들은 이렇게 생겼다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*1kW-Q3enDP4BNZxAtpv_IA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*4BwPX20VNltGPm_Gih70_Q.png" /></figure><p>단일 트랙을 받아서 투-트랙을 내뱉고 있어, 이어 붙일 수가 없다.<br> 단일 트랙을 투-트랙으로 만들어 주는 어댑터를 만들어야 한다.</p><h4>어댑터를 만들자</h4><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*haN8M2jdprPiaNE49faqNw.png" /></figure><p>위와 같이 우리는 단일 트랙을 투-트랙으로 만들어주는 어댑터를 만들어야 한다.</p><p>많은 함수형 프로그래밍에선 flatMap이라는 함수를 만들어서 제공하고 있다. 어댑터는 = flatMap 이다. 우리도 flatMap을 투-트랙에 맞게 만들어 보자</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*4sV0npvdXY0I61JM2hl2rQ.png" /></figure><p>우선, 리턴 타입은 투-트랙으로 나오게 한다.<br> 노란색 부분은 이전 함수에서 성공을 했을 경우, 지금 내 함수를 실행시킨다는 의미다.<br> 파란색 부분을 보면 단일 트랙을 Input으로 받는 함수를 실행시켜주는걸 알 수 있다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*RKqpJ5hh4RwV_Vgs_EaduQ.png" /></figure><p>그리고 이전 함수가 실패했을 경우에는 그대로 실패 트랙으로 가게끔 만들어 준다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*a-GBKyMDP9GuNXWITXkFCQ.png" /></figure><p>정리해보자면, flatMap은 단일 트랙을 받는 함수를 투-트랙을 받는 함수로 변환을 시켜주는 역할을 한것이다.</p><p>그러면 다시, 아까 봤었던 함수들을 나열 해보자. 여전히 철도 조각에 불과할 것이다. 아직 이어 붙일 수 없다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*RJRtmvE_HKnEmuve9gSw5Q.png" /></figure><p>이 함수들에 flatMap을 씌워주면 이어 붙일 수 있는 철도 조각이 만들어 진다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*G82PyIKiWjQ5QNp06XSZSw.png" /></figure><p>그리고 이어 붙이면 이렇게 된다. 이제 완전히 이어 붙게 된다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*vKsHdgnkCjl3GjZ_OFlOBg.png" /></figure><p>그런데… 코드가 좀 못생겼다. 함수형 프로그래밍에서 자주 쓰이는 유틸리티중 하나인 Pipe를 이용해보자</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*Kjpb0woh2UBr8Sr-N2O-UQ.png" /></figure><p>그러면 이렇게 예쁘게 이어 붙일 수 있게 된다. 이 유틸 함수는 위에서 부터 하나식 절차적으로 함수를 실행시키는 기능을 가지고 있다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*QMknjd4lsvz-_ih63BEmbQ.png" /></figure><p>그리고 한번더 validateRequest 에 대입을 하게 되면, 이 전체를 하나의 함수로 만들 수 있다. 세개의 부분 함수를 합성해서 validateRequest라는 새로운 함수를 만들게 된 것이다.</p><h3>마무리</h3><p>여기까지가 ROP의 기본적인 내용이었다. 이처럼, ROP를 도입하게 되면 좀더 에러처리를 깔끔하게 대응할 수 있게되어, 코드의 구조적 아름다움을 유지할 수 있게 해준다.</p><p>좀 더 심화한 부분을 다루기엔 내가 너무 지쳐서 다음 시간에 다뤄보도록 하려고 한다. 분명이 해뜰때 글쓰기 시작했는데 해 다 지고 글이 끝났다😢</p><p>끝.</p><h4>참고</h4><ul><li><a href="https://github.com/icepeng/">icepeng &lt;Railway oriented programming&gt;</a><br>🦾 내가 열심히 follow 중인 개발자중 한분이시다. 함수형 프로그래밍의 이해도나 숙련도를 보면 경험치가 어마어마 하다.</li><li><a href="https://www.slideshare.net/ScottWlaschin/railway-oriented-programming">Scott Wlaschin &lt;Railway Oriented Programming&gt;</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4e8070c04bda" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[도대체 Javascript는 어떻게 비동기 처리를 할까?]]></title>
            <link>https://0e.medium.com/%E1%84%83%E1%85%A9%E1%84%83%E1%85%A2%E1%84%8E%E1%85%A6-promise%E1%84%80%E1%85%A1-%E1%84%86%E1%85%AF%E1%86%AB%E1%84%83%E1%85%A6-b0940365610d?source=rss-6357424e5eef------2</link>
            <guid isPermaLink="false">https://medium.com/p/b0940365610d</guid>
            <category><![CDATA[async]]></category>
            <category><![CDATA[javascript]]></category>
            <dc:creator><![CDATA[아프니까병원이다]]></dc:creator>
            <pubDate>Sat, 14 Mar 2020 15:30:05 GMT</pubDate>
            <atom:updated>2020-03-15T06:32:15.672Z</atom:updated>
            <content:encoded><![CDATA[<p>이런 질문을 받았다.</p><pre>Q: Javascript는 싱글쓰레드 언어인데 Browser에서 어케 비동기 처리할까요?<br>나: (뇌정지)??Promise<br>Q: 그럼 Promise가 뭔가요<br>나: 일종의 모나드다 주저리주저리 콜백 지옥 해결해줌 주저리<br>Q: 그럼 async await는 왜 나온 건가요?<br>나: 코드 예쁘게 짜려고</pre><p>…미안하다아아아아</p><p>머릿속으로는 알고 있는데 말이 튀어나오지 않는다.</p><p>그래서 오늘은 초심으로 돌아가 Javascript가 비동기를 어떻게 처리하는지에 대해 알아보도록 하겠다.</p><h3>Javascript 는 싱글쓰레드 언어다</h3><p>스레드가 하나밖에 없으니, 한 번에 한 개의 작업만을 처리할 수 있다. 그런데 Browser에서 Javascript는 어떻게 화면 전환을 하면서 HTTP 요청이나 마우스 클릭 이벤트를 받을 수 있는 것일까?</p><h3>Javascript의 Engine</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*RLbK8nM3pfLWPu4qIUaWww.png" /><figcaption><a href="https://blog.sessionstack.com/how-does-javascript-actually-work-part-1-b0bacc073cf">https://blog.sessionstack.com/how-does-javascript-actually-work-part-1-b0bacc073cf</a> 에서 이미지 발췌</figcaption></figure><p>Memory Heap 과 Call Stack 은 Javascript의 엔진의 주요 구성 요소다. Memory Heap은 변수와 객체의 메모리 할당을 담당하고, Call Stack은 함수 실행 호출이 쌓이는 곳이다.</p><p>Call Stack은 이름처럼 쌓이는 순서와 반대로 실행이 된다(LIFO)</p><p>예를 들면, 아래와 같은 코드가 있다고 하자</p><pre>function foo() {<br>    bar()<br>    console.log(1)<br>}</pre><pre>function bar() {<br>    console.log(2)<br>}</pre><pre>foo()</pre><p>Call Stack은 다음의 그림과 같이, 순서대로 쌓인 다음 나중에 쌓인 함수가 먼저 실행되고 Call Stack에서 제거한다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*K6PFS4iXVT_qd2C1erN6Lw.png" /><figcaption>왼쪽에서 ➡️ 오른쪽이 Call Stack의 변화 순서다</figcaption></figure><p>foo()에서 bar()를 호출함으로, Call Stack은 위와 같이 쌓인후, bar()를 실행하고 Call Stack에서 제거한후, 다시 foo()를 실행하고 foo()를 Call Stack에서 제거한다.</p><p>Call Stack에 관해서는 스택 오버플로우 등 더 소개할게 남아 있지만, 비동기 처리에 대해 알아보려 하기 때문에 이만 줄이도록 하겠다.</p><p><strong>Call Stack 세줄요약</strong></p><ol><li>코드가 실행되면 Call Stack에 실행할 함수가 쌓인다.(push)</li><li>쌓인 반대 순서로 함수가 실행된다.</li><li>실행이 된 함수는 Call Stack에서 제거된다(pop)</li></ol><h3>Javascript의 Runtime</h3><p>Javascript의 실행 환경(Runtime)은 다음과 같다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*FA9NGxNB6-v1oI2qGEtlRQ.png" /><figcaption><a href="https://blog.sessionstack.com/how-does-javascript-actually-work-part-1-b0bacc073cf">https://blog.sessionstack.com/how-does-javascript-actually-work-part-1-b0bacc073cf</a> 에서 이미지 발췌</figcaption></figure><p>Browser에서 Javascript는 엔진만으로 동작하지 않는다. DOM 조작이나 AJAX 같은 비동기 이벤트를 위해 Web API를 제공하고, 이를 통제하기 위해 이벤트 루프(Event Loop)와 이벤트 큐(Event Queue, 이미지에선 Callback Queue)가 존재한다.</p><h4>Web API, 이벤트 큐, 그리고 이벤트 루프</h4><p>이제 본격적으로 Javascript의 비동기 처리에 대해 알아보려고 한다.<br>아래의 코드를 보고, 동작을 그림과 함께 비동기 이벤트가 어떻게 동작하는지 확인해 보도록 하겠다.</p><pre>// 제이쿼리가 임포트 되어있다는 가정하에 작성된 코드이다.<br>$.on(&#39;button&#39;, &#39;click&#39;, function onClick() {<br>    console.log(&#39;You clicked the button!&#39;);    <br>});<br><br>console.log(&quot;Hi!&quot;);<br><br>setTimeout(function timeout() {<br>    console.log(&quot;Click the button!&quot;);<br>}, 5000);<br><br>console.log(&quot;Welcome to loupe.&quot;);</pre><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*x7RAgY3Es7wTQsB20eYWaQ.gif" /></figure><p>코드가 Browser에서 실행되고, $.on(&#39;button&#39;, &#39;click&#39;, ...)이 Call Stack에 쌓인다.</p><p>$.on(&#39;button&#39;, &#39;click&#39;, ...)은 onClick()을 콜백 함수로 갖고 마우스 클릭을 기다리는 비동기 이벤트다.</p><p>Call Stack은 비동기 함수의 호출이 있을 경우 해당 함수를 Web API로 이동하여 호출되기를 기다리게 한다. 그리고 Call Stack에서는 해당 함수의 실행이 끝난 것으로 보고, Call Stack에서 제거한다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*RmQliVyFSHhtHm59hylVWQ.gif" /></figure><p>이후 console.log(&quot;Hi!&quot;);가 Call Stack 에 들어오고 실행되며 Call Stack에서 제거된다. 콘솔창을 확인해보면 Hi! 가 찍혀있을 것이다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/480/1*vEq9zPgG7D-kiB0GpF3mYg.gif" /><figcaption>표현을 엉성하게 했는데 양해 바란다.. 키노트로는 이게 최선이었다</figcaption></figure><p>그런 다음 setTimeout(function timeout() { console.log(“Click the button!”); }, 5000)이 Call Stack 에 들어오고 Web API로 이동한다. 그리고 console.log(“Welcome to loupe.”);이 Call Stack으로 들어오고 역시, 실행된 다음 Call Stack에서 제거된다.</p><p>Web API로 이동된 비동기 함수 setTimeout은 타임아웃을 5초로 설정했기 때문에 Web API에서는 5초 카운트를 하고 Callback Queue에다 콜백 함수인 timeout()을 전달한다. (*참고로 최소 5초다, 내부 사정에 따라 지연될 가능성도 있다)</p><p>Event Loop는 Callback Queue에 함수가 들어오면 함수를 Call Stack에 push 한다.</p><p>Call Stack에 push 된 timeout()은 console.log(&quot;Click the button!&quot;)을 다시 Call Stack에 쌓고 console.log(&quot;Click the button!&quot;)가 실행된 뒤 제거된다.</p><p>마우스 클릭이 들어온다면 역시 위의 그림처럼 Callback Queue에 콜백 함수 onClick()이 들어올 것이고, Event Loop가 큐에 있는 함수를 Call Stack에 push 한 다음, Call Stack은 해당 함수를 실행한 뒤 Call Stack에서 제거할 것이다.</p><h4>요약해보자면</h4><ol><li>Call Stack에서 비동기 함수가 호출되면 Web API (혹은 백그라운드라고도 한다)에 해당 함수가 등록되고 Call Stack에서 제거된다</li><li>비동기 이벤트가 동작하면 해당 콜백 함수가 Callback Queue 에 push 된다.</li><li>Event Loop 가 동작하여 Call Stack에 Callback Queue에 있는 함수가 push 된다</li><li>Call Stack에 들어온 해당 함수가 실행되고 Call Stack 에서 제거된다</li></ol><p>헉, Promise 설명하려 했는데 비동기 처리만 말하다가 끝나 버렸다. (움짤 만드느라 너무 힘들었다)</p><p>다음 글에서 Promise, 그리고 async/await에 대해 다뤄보도록 하겠다.</p><p>끝.</p><blockquote>글쓰다가 너무 피곤해서 띄엄띄엄 쉬면서 썼다. 혹시라도 문장에 이상이 있다거나 설명이 매끄럽지 못하다면 댓글로 팩폭 부탁드린다.</blockquote><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b0940365610d" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Prisma2 맛보기]]></title>
            <link>https://0e.medium.com/prisma2-%E1%84%86%E1%85%AE%E1%84%8B%E1%85%A5%E1%86%BA%E1%84%8B%E1%85%B5-%E1%84%83%E1%85%A1%E1%84%85%E1%85%B3%E1%86%AB%E1%84%80%E1%85%A1-d54aeba261ce?source=rss-6357424e5eef------2</link>
            <guid isPermaLink="false">https://medium.com/p/d54aeba261ce</guid>
            <category><![CDATA[how-to]]></category>
            <category><![CDATA[tutorial]]></category>
            <category><![CDATA[orm]]></category>
            <category><![CDATA[prisma]]></category>
            <dc:creator><![CDATA[아프니까병원이다]]></dc:creator>
            <pubDate>Sat, 15 Feb 2020 09:09:40 GMT</pubDate>
            <atom:updated>2020-02-21T00:54:34.468Z</atom:updated>
            <content:encoded><![CDATA[<blockquote>이 글은 Prisma2 preview-021 기반으로 작성되었습니다.</blockquote><p>Prisma에 대해 간략하게 요약하자면…</p><ul><li>Prisma는 데이터 모델을 GraphQL과 유사한 형식으로 작성이 가능한 ORM 이다</li><li>데이터 모델과 DB 마이그레이션을 자동으로 수행할 수 있다.</li><li>어드민 페이지를 별도로 제공하여 쉽게 데이터를 볼 수 있게 해준다. (현재 Prisma2 에서는 준비중이다)</li></ul><p>Prisma1은 이를 위해 DB Proxy Server를 두고 API Server에서 요청이 들어오면 Prisma Server가 처리하는 식이었는데,</p><p>Prisma2 에서는 Prisma Server의 기능들이 모듈로 쪼개져 라이브러리로 제공되어 이제 DB Proxy Server 구성을 위해 Prisma Server를 만들지 않아도 된다. (물론 만들 수는 있다고 한다.)</p><blockquote>Prisma1 에 대해 궁금하면 <a href="https://medium.com/labelstore/prisma%EB%A1%9C-graphql%EC%9D%84-%EC%89%BD%EA%B2%8C-%EB%8F%84%EC%9E%85%ED%95%98%EA%B8%B0-fa64dcf63382">레이블스토어의 Prisma 도입기에 대해 읽어보자</a>, 내가 본 글 중에 제일 한국어로 잘 정리되어 있었다</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*XyuykGlMvZ1baDmI3OgWaw.png" /><figcaption>현재 Photon과 Lift의 명칭이 각각 Prisma Client JS, Prisma Migrate로 명칭이 변경되었다.</figcaption></figure><p>그럼 이제 프로젝트를 만들고 Prisma2를 빠르게 흩어보자, (본인이 타입충 이기 때문에 Typescript로 진행한다)</p><pre>npm init -y<br>npm install typescript ts-node --save-dev<br>npm install @prisma/client<br>touch tsconfig.json</pre><p>tsconfig.json 에는 아래와 같이 내용을 추가 한다</p><pre>{<br>  &quot;compilerOptions&quot;: {<br>    &quot;outDir&quot;: &quot;dist&quot;,<br>    &quot;rootDir&quot;: &quot;src&quot;,<br>    &quot;lib&quot;: [&quot;esnext&quot;],<br>    &quot;strict&quot;: true<br>  },<br>  &quot;include&quot;: [&quot;src/**/*&quot;]<br>}</pre><p>Prisma2 를 추가해보자</p><pre>npm install prisma2 --save-dev<br>npx prisma2 init</pre><p>이렇게 하면 빈 Prisma 데이터 모델이 생성되어 있을 것이다. 이제 데이터 모델을 작성해야 한다.</p><p>우선 prisma/schema.prisma 파일에 DB 정보를 입력하자</p><blockquote>⚠️ prisma/schema.prisma는 확장자가 prisma다 ts가 아니다!!</blockquote><pre>datasource db {<br>  provider = &quot;postgresql&quot;<br>  url      = &quot;postgresql://user:p@ssw0rd@localhost:5432/testdb?schema=public&quot;<br>}</pre><p>DB 별 형식은 다음과 같다. 알아서 잘 셋업 해보자.</p><ul><li>MySQL: mysql://USER:PASSWORD@HOST:PORT/DATABASE</li><li>PostgreSQL: postgresql://USER:PASSWORD@HOST:PORT/DATABASE?schema=SCHEMA</li><li>SQLite: sqlite:./FILE.db</li></ul><blockquote>당연한 말이지만, 실제 프로덕트 환경에서는 <a href="https://github.com/prisma/prisma2/blob/master/docs/prisma-schema-file.md#using-environment-variables">환경변수</a>로 해줘야 된다 🤫</blockquote><p>DB 셋업이 끝나면 이제 데이터 모델을 작성해보자</p><h4>Prisma 데이터 모델 작성하기</h4><p>데이터 모델을 작성하는데에는 2가지의 방법이 있다.</p><ul><li>데이터 모델을 작성하고 CLI를 이용하여 DB에 Migration 한다.</li><li>DB에서 테이블 등을 작성하고 CLI의 introspect 기능을 이용하여 DB를 분석하여 데이터 모델을 생성한다.</li></ul><p>어썸하다. 본인이 편한 방식으로 하면 된다. 그럼 둘다 알아보자 아 참고로 Migrate 기능은 실험 기능이다. 사용시 주의하자</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*Zlv_QKoQ6PvqhPHHlTS37Q.png" /></figure><h4>데이터 모델 작성하고 DB Migration 하기</h4><p>prisma/schema.prisma 하단에 아래와 같이 추가해보자</p><pre>model posts {<br>  author_id  users?<br>  content    String?<br>  created_at DateTime?<br>  post_id    Int       @id<br>  title      String<br>}<br><br>model profiles {<br>  bio        String?<br>  profile_id Int     @id<br>  user_id    users<br>}<br><br>model users {<br>  email      String     @unique<br>  name       String?<br>  user_id    Int        @id<br>  postses    posts[]<br>  profileses profiles[]<br>}</pre><p>그리고 아래의 커맨드를 통해 데이터 모델의 마이그레이션을 작성하자</p><pre>npx prisma2 migrate save --experimental</pre><p>마이그레이션 이름 뭐할거냐고 나오는데 걍 무시하고 엔터누르면 알아서 시간에 맞춰서 타임스탬프를 이름으로 설정한다. 다 끝났으면 이제 마이그레이션을 실제 DB에 적용해보자</p><pre>npx prisma2 migrate up --experimental</pre><p>별 문제 없으면 잘 적용이 됐을거다. (아직 실험기능이라 가끔 너무 꼬여버리면 오류 나긴 한다.)</p><p>DB를 확인해보면 대충 어떤 형식이 어떻게 저장되는지 감이 올거다. 자세한건 <a href="https://github.com/prisma/prisma2/blob/master/docs/data-modeling.md">Prisma2 문서</a>를 참고해보자.</p><h4>DB 에서 작업하고 Prisma 데이터 모델에 적용하기</h4><p>그럼 이제 DB에서 작업하고 Prisma 데이터 모델에 적용해보자.</p><p>방금 DB를 작성했으니 DB 새로 교체해주던가 생성한 테이블을 다 날리고, prisma/scheme.prisma 에서 DB 셋업 정보만 빼고 데이터 모델은 다 지워주자</p><p>다 되돌렸으면, 이제 아래의 SQL schema를 이용해 DB에 테이블들을 생성해주자</p><pre>CREATE TABLE users (<br>  user_id SERIAL PRIMARY KEY NOT NULL,<br>  name VARCHAR(256),<br>  email VARCHAR(256) UNIQUE NOT NULL<br>);<br><br>CREATE TABLE posts (<br>  post_id SERIAL PRIMARY KEY NOT NULL,<br>  created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,<br>  title VARCHAR(256) NOT NULL,<br>  content TEXT,<br>  author_id INTEGER,<br>  FOREIGN KEY (author_id) REFERENCES users(user_id) <br>);<br><br>CREATE TABLE profiles (<br>  profile_id SERIAL PRIMARY KEY NOT NULL,<br>  bio TEXT,<br>  user_id INTEGER NOT NULL,<br>  FOREIGN KEY (user_id) REFERENCES users(user_id)<br>);</pre><p>생성이 끝났으면 Introspect 커맨드를 실행해보자</p><pre>npx prisma2 introspect</pre><p>실행이 완료되고 prisma/schema.prisma 를 확인해보면 아까와 같은 데이터 모델이 생성된 것을 확인해 볼 수 있다.</p><h4>Prisma Client 생성하기</h4><p>이제 프로젝트에서 Prisma2 를 사용할 수 있게 Prisma Client 를 생성해줘야 한다.</p><p>아래의 커맨드를 입력하면, 아까 만든 prisma/schema.prisma 를 바탕으로 Prisma Client가 node_modules/@prisma/client 에 생성된다. (node의 특성을 이용해 편법을 사용하는거 같다)</p><pre>npx prisma2 generate</pre><p>그럼 이제 index.ts 파일을 만들고, Prisma Client를 이용하여 DB에 데이터를 생성해보자. 아래와 같이 작성하면 된다.</p><pre>import { PrismaClient } from &#39;@prisma/client&#39;<br><br>const prisma = new PrismaClient()<br><br>async function main() {<br><br>  const users = await prisma.user.create({<br>      data: {<br>        email: &#39;john@prisma.io&#39;,<br>        name: &#39;John&#39;,<br>        profile: {<br>          create: { <br>            bio: &quot;Hello World&quot;<br>          }<br>        }<br>      },<br>    })<br>}<br><br>main()</pre><p>실행해보면 user 테이블에 데이터가 생성된다.</p><pre>npx ts-node script.ts</pre><p>이 정도로 Prisma2 맛보기 프로젝트를 마무리하려 한다.</p><p>코드를 보면 알겠지만, 이런식으로 다양하게 활용하면 된다. GraphQL 서버를 짜든 REST API 서버를 짜든, 알맞게 잘 적용하면 된다.</p><p>혹시라도 어렵게 느껴진다면 <a href="https://github.com/prisma">Prisma 깃헙</a>에 있는 example 들을 찾아보자, 다양하게 있을것이다. (사실 이 예제도 코드 짜기 귀찮아서 다 example 에서 짜깁기 한거다)</p><p>원래 본인은 Spring 환경에서 일해왔기 때문에 Mybatis로 직접 SQL Query를 작성하는 SQL 순혈(?) 주의자였다.</p><p>그러나 이번에 새로운 사이드 프로젝트를 진행하면서 Prisma를 적용해 봤는데 Prisma 덕에 DB CRUD나 데이터 모델 작성이 훨씬 간편해진 걸 느낄 수 있었다.</p><p>앞으로도 이 간편함 덕에 Prisma를 계속 사용할 거 같다.</p><p>끝.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d54aeba261ce" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[PNPM]]></title>
            <link>https://0e.medium.com/pnpm-9d8f8be3ac78?source=rss-6357424e5eef------2</link>
            <guid isPermaLink="false">https://medium.com/p/9d8f8be3ac78</guid>
            <dc:creator><![CDATA[아프니까병원이다]]></dc:creator>
            <pubDate>Fri, 31 Jan 2020 08:55:56 GMT</pubDate>
            <atom:updated>2020-01-31T08:55:56.290Z</atom:updated>
            <content:encoded><![CDATA[<h3>PNPM? 이게 무람</h3><p>깃헙 탐험하다가 발견한 레포 <a href="https://github.com/pnpm/pnpm/">PNPM</a></p><p>좀 보니까 쩌는거 같다. 우선 Background를 보자</p><blockquote>pnpm uses hard links and symlinks to save one version of a module only ever once on a disk. When using npm or Yarn for example, if you have 100 projects using the same version of lodash, you will have 100 copies of lodash on disk. With pnpm, lodash will be saved in a single place on the disk and a hard link will put it into the node_modules where it should be installed.</blockquote><blockquote>As a result, you save gigabytes of space on your disk and you have a lot faster installations! If you’d like more details about the unique node_modules structure that pnpm creates and why it works fine with the Node.js ecosystem, read this small article: Flat node_modules is not the only way.</blockquote><p>대충 요약해보면 핵심은 이거다</p><blockquote>하드링크와 심볼릭 링크를 이용하여 한 버전의 모듈을 디스크에 한 번만 저장한다.</blockquote><p>이게 무슨소리냐, 내 맥북에 한 100개 정도 레포가 있다고 치자, 그리고 이 100개의 레포가 lodash를 사용한다고 가정했을때 각 레포에 lodash가 설치되는게 아니라 pnpm의 모듈저장하는 곳에서 한번만 설치하고 하드 링크를 제공하여 연결해준다는 거다.</p><p>이거 개쩌는 거다. 이제 똑같은 모듈 설치한다고 시간낭비할 일 없어지는 거다.</p><p>벤치마크 이미지도 들고 왔음둥</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/1*q4_VzNG_fsEtgHliWheYXA.png" /></figure><p>어떤가 당장 가서 스타 박을 수 밖에</p><p>아 근데 일단 본인은 아직 쓸 생각 없다, 실 사용에는 어느 정도 무리가 있는 듯 하다.</p><p>나보다 조금더 자세하게 다룬 블로그가 있어서 <a href="https://imch.dev/posts/pnpm-a-manager-what-is-not-flat">링크</a> 남겨본다</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9d8f8be3ac78" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[✊ 개발자의 건강을 위한 개발환경 구축하기 (소프트웨어 편)]]></title>
            <link>https://0e.medium.com/%EA%B0%9C%EB%B0%9C%EC%9E%90%EC%9D%98-%EA%B1%B4%EA%B0%95%EC%9D%84-%EC%9C%84%ED%95%9C-%EA%B0%9C%EB%B0%9C%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95%ED%95%98%EA%B8%B0-%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4-%ED%8E%B8-9b81b525117e?source=rss-6357424e5eef------2</link>
            <guid isPermaLink="false">https://medium.com/p/9b81b525117e</guid>
            <category><![CDATA[vim]]></category>
            <category><![CDATA[건강]]></category>
            <dc:creator><![CDATA[아프니까병원이다]]></dc:creator>
            <pubDate>Sun, 26 Jan 2020 07:05:27 GMT</pubDate>
            <atom:updated>2020-01-26T07:10:16.621Z</atom:updated>
            <content:encoded><![CDATA[<p>개발자는 건강을 챙겨야 한다. 까딱 잘못하면 허리 나가고 손목 <a href="https://www.rsipain.com/">rsi</a>로 평생 고생하고, 체형이 망가진다.</p><p>이 시리즈는 하드웨어 편과 소프트웨어 편으로 나뉜다. 오늘은 소프트웨어를 다뤄보도록 하겠다.(macOS 기준)</p><h3>vim</h3><p>에디터는 vim 을 선정했다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*6hG3ousQXHXtPjsY.png" /><figcaption>이미지가 이렇게 클 줄은 몰랐다</figcaption></figure><p>Emac는 미친듯한 조합키로 손가락을 미치게 만들고, ui 가 있는 editor들은 마우스를 움직이게 만든다. 그리고 마우스는 쓰면 쓸수록 손목 근육들을 긴장시킨다.</p><p>그렇다면 vim은? 거의 손목 움직임이 없다. 방향키도 hjkl 이 네가지 키로 조작할 수 있고, 입력 모드, 노말 모드, 비주얼 모드 이 세가지 모드를 통해 키배열을 크게 안벗어나고도 여러 기능을 사용할 수 있게 키매핑이 되어있다.</p><p>앞으로 소개하는 소프트웨어들도 대부분 vim의 영향을 받은 프로그램들이다.</p><h3>vim-vixen</h3><p>파이어폭스 <a href="https://addons.mozilla.org/en-US/firefox/addon/vim-vixen/">확장프로그램</a>이다. 브라우징을 vim처럼 하게 해준다.</p><p>가장 큰 특징은 f를 눌렀을때 노란색 딱지들이 뜨는데 해당하는 키를 누르면 그 영역을 클릭해준다. 이것 덕에 키보드만 사용하는데도 마우스를 사용할때보다 브라우징을 빠르게 할 수 있다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/700/0*mv3dC6RAfvp3PMSt.png" /></figure><h3>VSCodeVim</h3><p><a href="https://marketplace.visualstudio.com/items?itemName=vscodevim.vim">VS Code 를 Vim 처럼</a> 사용할 수 있게 해준다.</p><h3>Shortcat</h3><p><a href="https://shortcatapp.com/">Killing mice, one at a time.</a> 라는 슬로건을 내걸고 있다.</p><p>cmd + shift + space 를 누르면 조그마한 입력창이 뜬다. 원하는 메뉴의 철자를 입력하면 해당 하는 곳을 클릭할 수 있게 해준다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/0*umSfD7IPzdYfLNoa.gif" /></figure><h3>Keytty</h3><p><a href="https://keytty.com/ko/how-to-use/">한국인 개발자님</a>이 만드신거 같다.<br>cmd 를 두번 누르면 마우스를 키보드로 이동할 수있게 해준다. 처음엔 어색했는데 점점 편해지고 있다. tab 이나 vim으로 제어 안되는 부분들을 제어 할 때 용이하다.</p><p>자세한 사용법은 <a href="https://www.youtube.com/embed/g2cNLmcsBC8">유튜브에 친절</a>하게 나와 있다.</p><h3>Alfred</h3><p>원래는 기본으로 탑재된 Spotlight을 사용했었는데 이게 더 많은 기능이 들어가 있다고 해서 바꿨다.<br>검색엔진에다 바로 검색할 수 있게 해주는게 가장 큰 장점인거 같다. 사이트는 <a href="https://www.alfredapp.com/">이 쪽</a>으로</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*H-_RNIBAXd5BUWYY.jpg" /></figure><p>현재는 이렇게 사용하고 있다. 다른 괜찮은 프로그램이 있다면 댓글로 소개 부탁드린다.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9b81b525117e" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[소프트웨어 장인]]></title>
            <link>https://0e.medium.com/%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4-%EC%9E%A5%EC%9D%B8-315dbebd63b9?source=rss-6357424e5eef------2</link>
            <guid isPermaLink="false">https://medium.com/p/315dbebd63b9</guid>
            <dc:creator><![CDATA[아프니까병원이다]]></dc:creator>
            <pubDate>Sun, 26 Jan 2020 06:35:29 GMT</pubDate>
            <atom:updated>2020-01-26T06:35:29.219Z</atom:updated>
            <content:encoded><![CDATA[<p>이 책은 <a href="https://ridibooks.com/v2/Detail?id=754016528">소프트웨어 장인으로서 가져야 할 프로페셔널리즘과 그 여정에 대해 다루고</a> 있다.</p><p>저자는 내가 지금 올바른 길을 가고 있는지에 대해 한번 더 짚어주고, 고민거리를 해결해준다.</p><p>내가 요약하고 싶은 대로 요약해봤다.</p><h3>🙅‍♀️ ‘아니오’라고 말하는 방법 배우기</h3><p>많은 개발자들이 소화 할 수 없는 일정을 관리자 혹은 상사의 압박에 못 이겨 ‘할 수 있다’고 말한다. 그리고 이 불가능한 일정을 수행하기 위해 야근과 휴일 반납을 밥먹듯이 한다.</p><p>이 반복되는 악순환을 그만 둬야된다. 할 수 없는 일정은 ‘아니오’라고 말하자, 그리고 대안을 제시하자.</p><p>진정한 프로는 고객의 모든 요구사항 그대로 받아들이지 않는다. 고객이 가장 필요한 것에 대해 최선을 다하고 돕는다. 변호사, 회계사, 의사와 같은 다른 프로페셔널들에게 고객은 하나하나 지시하지 않는다. 이는 소프트웨어에서도 동일하게 작용한다.<br>진정한 프로페셔널은 고객이 올바르지 않는 결정을 할때 그것을 거부한다.</p><h3>👯‍♂️ 조직에 좋은 개발자 끌어들이기</h3><p>좋은 개발자를 끌어들이려면 좋은 채용공고가 필요하다.<br>우선, 보통의 채용공고 부터 보자</p><pre>자격요건<br>- AA언어 개발 경험 0년 이상<br>- BBB 프레임워크 경험<br>- CCC 관련 서비스 개발 경험<br>- DD 관련 학사 이상</pre><pre>우대사항<br>- E2E 테스트 경험<br>- 동종업계 경력자</pre><p>이 공고에서는 관련 학위, 보유 기술 목록, 동종업계 경력을 요구하고 있다. 이 세 가지 요건만 맞다면 언제든지 지원할 수 있다는 소리다. 시스템을 엉망으로 만들고 간 개발자가 다른 곳에서 같은 일자리를 구할 수 있을 수 있다는 말도 된다.</p><p>채용 공고에 직무 요건 목록을 기술하는 방식은 피해야 할 관례다. <a href="https://velog.io/www.tlnt.com">TLNT</a>에 글을 투고중인 Lou Adler는 기술과 경험에 근거한 직무 요건에 대해 “반 재능(anti-talent)적이고 반 다양성(anti-diversity)적이다, 미래의 성공에 대한 끔찍한 변수가 되는 것 빼고는 말이다” 라고 말하고 있다.</p><p>해당 글에 적혀있는 6가지 이유중 첫 번째 이유만 의역했다.</p><blockquote><em>다 써보려고 했는데 너무 길어서 생략했다. 시간이 된다면 직접 들어가서 </em><a href="https://www.tlnt.com/get-rid-of-job-descriptions-and-youll-hire-better-people/"><em>읽어보자</em></a></blockquote><ol><li><strong>어느 정도의 기술 수준도 중요하지만, 채용공고에 쓰여 있는 경력을 의미하는 숫자는 제멋대로이고, 오해의 소지가 있으며, 변덕스럽다.</strong> 상식적으로 볼 때, 지원자의 이력서에 이미 그 사람이 해당 기술을 수행할 수 있는지는 알 수 있을 것이다. 기량은 경력으로 정해지지 않는다. 채용된 후에 현행 유지만 하는 개발자와 끊임없이 나아가는 개발자는 경력은 같지만 실력은 그렇지 않을 것이다. 그리고 사실, 채용하는 기업 입장에서 경력은 적고 배우는 건 빠른 개발자를 고용 하기를 원할 것이다. 제정신이라면 이러한 요건에 부합하는 사람들을 채용 대상에서 제외시키지 않을 것이다.</li></ol><p>그렇다면, 어떤 채용공고가 좋은 채용공고일까?</p><p>답은 간단하다. 가치관과 회사의 문화에 집중된 내용으로 채워넣어야 한다. 직무 요건이 필요하다면 해당 직무의 태도와 책임, 프로젝트 종류, 사용 기술(요구 조건 🙅‍♀️ 오로지 참고용으로) 정도만 작성하자</p><p>좋은 예로는 <a href="https://www.7digital.com/careers">7digital의 채용공고</a>가 있다.</p><pre>- You can take part in regular weekly sessions sharing ideas, doing Katas/Dojos and discussing practices and technologies.</pre><pre>- You will get up to two days “innovation time” a month to investigate new technologies or product ideas.</pre><pre>- You can attend conferences and community events, both as a participant or contributor - we’ve presented/run sessions at XP2016, QCon, SPA, Strata + Hadoop World, Agile Cambridge, Agile Manchester, APIdays, XPDay, Agile on the Beach and many others.</pre><pre>- You can help us host our own popular community event – Devs in the ‘ditch</pre><pre>- We hold frequent retrospectives on how we can improve the way we work.</pre><p>채용 공고중 일부를 발췌했다. 평소 회사에서 어떠한 것을 주요 가치로 두고 문화로 삼고 있는지 잘 알 수 있는 대목이다.</p><p>이처럼 어떤 일을 하고 무엇을 가치있게 여기는지, 개발자에게 기대하는 것은 무엇인지에 대한 기술은 열정적이고 재능 있는 개발자들을 끌어들인다.</p><h3>이런 면접좀 하지 말자 ❌❌❌</h3><h4>답 없는 질문은 하지 않는다.</h4><p>어떤 질문에 어떤 답변이 나와야 하는지 잘 모르겠다면 그런 질문은 하지 말자. 해당 직무에 그다지 유용한 질문은 아닐 것이다.</p><h4>이상한 질문은 하지 말자</h4><p>비행기에 골프 공이 몇 개나 들어갈까? 이거 알면 코드 잘 짜고 프로페셔널한 개발자일까? 절대 아니다. 구글도 이런 건 이제 안 한다.</p><h4>코딩 면접 때 인터넷 접속을 막는다</h4><p>코딩할 때 구글링 안 하나요?</p><h4>종이에 코드를 작성하게 한다, 알고리즘 문제를 낸다</h4><p>대부분의 개발 업무가 알고리즘에 대한 깊은 이해를 필요로 하지 않는걸 우리는 매우 잘 알고 있다.</p><p>대신, 실제 프로젝트와 유사한 과제를 내자. 이걸로도 충분히 “문제 해결 능력”은 파악할 수 있을 것이다.</p><h3>단순한 설계를 위한 네 가지 원칙</h3><ol><li>모든 테스트의 통과</li><li>중복의 최소화</li><li>명료성의 최대화</li><li>구성요소의 최소화</li></ol><p>더 적어보려고 했는데 어쩌다 보니 책 반납일이 됐다. (사실, 귀찮다)<br>궁금한 사항이 있다면, 직접 읽어보도록 하자</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=315dbebd63b9" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>