<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>나의 댕청한 개발일지</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Mon, 09 Dec 2024 12:38:25 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>나의 댕청한 개발일지</title>
            <url>https://velog.velcdn.com/images/stulta_amiko/profile/c6567feb-f58b-4252-ac76-4456ca3338e9/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. 나의 댕청한 개발일지. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/stulta_amiko" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[졸업작품 서비스 종료]]></title>
            <link>https://velog.io/@stulta_amiko/%EC%A1%B8%EC%97%85%EC%9E%91%ED%92%88-%EC%84%9C%EB%B9%84%EC%8A%A4-%EC%A2%85%EB%A3%8C</link>
            <guid>https://velog.io/@stulta_amiko/%EC%A1%B8%EC%97%85%EC%9E%91%ED%92%88-%EC%84%9C%EB%B9%84%EC%8A%A4-%EC%A2%85%EB%A3%8C</guid>
            <pubDate>Mon, 09 Dec 2024 12:38:25 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/stulta_amiko/post/fc145e14-45fc-4a2a-acd9-1df3d2220586/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/stulta_amiko/post/c979ecea-0d40-421f-98ed-bbed4f5af7c7/image.png" alt=""></p>
<p>실제로 도메인을 구매하고 EC2 인스턴스를 이용해서 내부에서 nginx랑 npm을 이용해서 배포했던 서비스
젠킨스를 도입하고 많은 시도를 해봐서 좋은 경험이었다고 생각한다.
구동기간 2024.08-2024.12</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[시내 고속버스 경로찾기 api 근황]]></title>
            <link>https://velog.io/@stulta_amiko/%EC%8B%9C%EB%82%B4-%EA%B3%A0%EC%86%8D%EB%B2%84%EC%8A%A4-%EA%B2%BD%EB%A1%9C%EC%B0%BE%EA%B8%B0-api-%EA%B7%BC%ED%99%A9</link>
            <guid>https://velog.io/@stulta_amiko/%EC%8B%9C%EB%82%B4-%EA%B3%A0%EC%86%8D%EB%B2%84%EC%8A%A4-%EA%B2%BD%EB%A1%9C%EC%B0%BE%EA%B8%B0-api-%EA%B7%BC%ED%99%A9</guid>
            <pubDate>Wed, 17 Jul 2024 05:33:09 GMT</pubDate>
            <description><![CDATA[<p>그야말로 시간끌기의 제왕이다.</p>
<p>api는 js 파일로 네이티브하게 돌아가도록 바꾼상태이고
<img src="https://velog.velcdn.com/images/stulta_amiko/post/eb672eae-46cb-45f4-acda-84bd9beb5ea6/image.png" alt="">
프론트 엔드는 여기까지 만들었다.. 쉽지않다</p>
<p>여기까지 오는데도 많은 시행착오가 있었는데 완성하면 적던가 해야지..</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[시외/고속버스 환승 api 근황]]></title>
            <link>https://velog.io/@stulta_amiko/%EC%8B%9C%EC%99%B8%EA%B3%A0%EC%86%8D%EB%B2%84%EC%8A%A4-%ED%99%98%EC%8A%B9-api-%EA%B7%BC%ED%99%A9</link>
            <guid>https://velog.io/@stulta_amiko/%EC%8B%9C%EC%99%B8%EA%B3%A0%EC%86%8D%EB%B2%84%EC%8A%A4-%ED%99%98%EC%8A%B9-api-%EA%B7%BC%ED%99%A9</guid>
            <pubDate>Wed, 21 Feb 2024 09:08:54 GMT</pubDate>
            <description><![CDATA[<p>열심히 만들고있다.
일단 파이썬으로 코드를 짠다음에 자바스크립트로 이식할 계획이다.
먼저 데이터가 필요했기 때문에 데이터를 수집했다.
공공데이터 포털에있는 시외버스, 고속버스 운행데이터를 수집해서
networkx를 이용해서 그래프를 만든 다음에 A* 알고리즘을 이용해서 루트를 찾는 방식을 이용해서 구현했다.
<img src="https://velog.velcdn.com/images/stulta_amiko/post/1963e20b-f30b-454f-b6e1-0f7da0a776d8/image.png" alt="">
일단 시험적으로 구현했을때는 정상적으로 작동한다.
하지만 여기 여러문제가 발생하는데 데이터의 문제이다.
일단 모든 데이터가 공공데이터포털에 존재하지 않았다.
직접 추가해야하는 데이터가 존재할 수 밖에없는것 같다.
내가 찾아본 터미널중 수원터미널과 춘천터미널 이 둘은 현재 데이터 제공이 안된다.
무슨일인가. 하고 공공데이터포털에 문의를 넣어보니
티머니와 연계를 해서 데이터를 제공하고 있다보니 모든 데이터를 제공하지 못한다는 문제가 발생한다는 것이었다. 
따라서 조회가 안되는 터미널을 따로 조사해서 직접 데이터를 추가하는 수 밖에 없을것같다.</p>
<p>이런거에 시간 많이끌면 안될거같은데..
슬슬 다른것도 해야할것같고.. 레디스랑 카프카는 언제보고..
쉽지않다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[24년 목표?]]></title>
            <link>https://velog.io/@stulta_amiko/24%EB%85%84-%EB%AA%A9%ED%91%9C</link>
            <guid>https://velog.io/@stulta_amiko/24%EB%85%84-%EB%AA%A9%ED%91%9C</guid>
            <pubDate>Tue, 30 Jan 2024 04:30:39 GMT</pubDate>
            <description><![CDATA[<p>네이버지도를 들어가면 시외버스를 조회할때 환승을 하는경우에 어떤식으로 환승을 해야하는지 알려주는 기능있다.
하지만 이런기능은 있는데 아쉽게도 시간표까지 보여주진않는다.</p>
<p>그래서 일단 저런기능을 구현하고 보니깐 A* 알고리즘이나 CCH알고리즘을 사용하는듯 하다.. 아무래도 일반적인 지도탐색의 경우 에이스타가 느리다곤 하나 버스의 경우는 경로가 일반 지도경로탐색에 비해서는 많이 한정적이니 A* 알고리즘만으로 충분하지 않을까 싶다.
그리고 일단 그러려면 데이터를 가져와야 하는데 이는 공공데이터포털 데이터를 파싱하면 되지않을까 싶다..
그러고 네트워크 x로 그래프를 만들어서.. 구현하고.. 시간표데이터는 또 공공데이터포털에 있으니깐.. 어떻게든 되지않을까</p>
<p>까먹을까봐 적어둔다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[아치리눅스 mariadb 설치]]></title>
            <link>https://velog.io/@stulta_amiko/%EC%95%84%EC%B9%98%EB%A6%AC%EB%88%85%EC%8A%A4-mariadb-%EC%84%A4%EC%B9%98</link>
            <guid>https://velog.io/@stulta_amiko/%EC%95%84%EC%B9%98%EB%A6%AC%EB%88%85%EC%8A%A4-mariadb-%EC%84%A4%EC%B9%98</guid>
            <pubDate>Fri, 15 Sep 2023 03:50:26 GMT</pubDate>
            <description><![CDATA[<p>공식문서만 보고 그냥 설치하면 안되는듯 싶다.
그냥 생각없이</p>
<pre><code>sudo pacman -S mariadb</code></pre><p>라고 해버리면 이제 실행도 안되고 소켓파일이 없다고하는 오류를 직면할 수도 있다.
최소한 나는 그랬다.</p>
<p>일단 먼저 업데이트 부터 해주고</p>
<pre><code>sudo pacman -Syu</code></pre><pre><code>reboot</code></pre><p>안전하게 리부팅 까지한다.</p>
<p>그러고서</p>
<pre><code>sudo pacman -Sy mariadb</code></pre><p>를 해서 마리아디비 설치를 해준다.</p>
<pre><code>mariadb-install-db --user=mysql --basedir=/usr --datadir=/var/lib/mysql</code></pre><p>위 명령어를 실행해서 초기화한다.
디렉토리는 자신이 원하는 위치로 바꿔도 괜찮을듯 싶다.</p>
<p>그러고서</p>
<pre><code>systemctl enable --now mariadb</code></pre><p>명령어를 실행하면 마리아디비가 실행된다.</p>
<pre><code>systemctl status mariadb</code></pre><p>로 확인해보면 정상작동하는 모습을 볼 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Webhacking.kr 5번 문제 풀이]]></title>
            <link>https://velog.io/@stulta_amiko/Webhacking.kr-5%EB%B2%88-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4</link>
            <guid>https://velog.io/@stulta_amiko/Webhacking.kr-5%EB%B2%88-%EB%AC%B8%EC%A0%9C-%ED%92%80%EC%9D%B4</guid>
            <pubDate>Fri, 25 Aug 2023 05:36:58 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/stulta_amiko/post/fcbd42ff-84c7-4e2e-8034-a08db63868c5/image.png" alt="">
웹 해킹 문제를 가끔씩 풀어보고 있다.
단순히 웹사이트만 만드는것 뿐만 아니라 가끔은 백준이라던지 이런문제를 풀어보는것도 두뇌회전에 도움이 되는것 같다.</p>
<p>먼저 이렇게 생긴 페이지가 나온다.</p>
<p>소스를 보면</p>
<p><img src="https://velog.velcdn.com/images/stulta_amiko/post/5a842c5d-1c0d-40ee-8348-afd8f54eddb0/image.png" alt=""></p>
<p>먼저 encode 부분을 보면 id와 pw를 받아서 20번 base64로 encode 한다음에 1~8까지의 숫자를 특수문자로 바꾸고 쿠키로 아이디와 비밀번호를 적용하는게 encode 과정인것 같다.</p>
<p><img src="https://velog.velcdn.com/images/stulta_amiko/post/13a5bd76-f9a2-4433-96ca-9639741b1bb6/image.png" alt=""></p>
<p>그리고 끝의 코드를 보면 decode하는 과정도 있는데 encode의 역순으로 실행되는것을 알 수 있다.</p>
<p>그리고 decode된 id의 값이 admin이고 pw의 값이 nimda일때 문제는 해결이된다.</p>
<p>문제 자체는 내가 풀 수 있을정도면 초보자들도 쉽게 풀 수있을만큼 쉬운것 같다.</p>
<p>하지만 단순하게 풀 수 있는 문제는 아니고 코드를 짜서 하는게 정신건강에 이로울것같다.
물론 웹상에 존재하는 base 64 decode encode 페이지에 가서 20번씩 복사 붙여넣기를 하면서 encode 과정을 거친다음에 거기서 또 특수문자로 바꿀수 있는 자신이 있으면 그렇게 해도된다.</p>
<p>하지만 나는 그정도로 머리가 좋지않기때문에.. 몇번 하면 까먹을 것 같았고 그냥 프로그램을 짜기로한다.</p>
<pre><code>var id = &#39;admin&#39;
let password = &#39;nimda&#39;
console.log(&#39;id : &#39;, id)
console.log(&#39;password : &#39;, password)

let i

let base64EncodedId = Buffer.from(id, &#39;utf8&#39;).toString(&#39;base64&#39;)
let base64EncodedPwd = Buffer.from(password, &#39;utf8&#39;).toString(&#39;base64&#39;)

for (i = 0; i &lt; 19; i++) {
    base64EncodedId = Buffer.from(base64EncodedId, &#39;utf8&#39;).toString(&#39;base64&#39;)
    base64EncodedPwd = Buffer.from(base64EncodedPwd, &#39;utf8&#39;).toString(&#39;base64&#39;)
}
base64EncodedId = base64EncodedId
    .replace(/1/gi, &#39;!&#39;)
    .replace(/2/gi, &#39;@&#39;)
    .replace(/3/gi, &#39;$&#39;)
    .replace(/4/gi, &#39;^&#39;)
    .replace(/5/gi, &#39;&amp;&#39;)
    .replace(/6/gi, &#39;*&#39;)
    .replace(/7/gi, &#39;(&#39;)
    .replace(/8/gi, &#39;)&#39;)

base64EncodedPwd = base64EncodedPwd
    .replace(/1/gi, &#39;!&#39;)
    .replace(/2/gi, &#39;@&#39;)
    .replace(/3/gi, &#39;$&#39;)
    .replace(/4/gi, &#39;^&#39;)
    .replace(/5/gi, &#39;&amp;&#39;)
    .replace(/6/gi, &#39;*&#39;)
    .replace(/7/gi, &#39;(&#39;)
    .replace(/8/gi, &#39;)&#39;)

console.log(&#39;Base64 Encoded Id : &#39;, base64EncodedId)
console.log(&#39;Base64 Encoded Pwd : &#39;, base64EncodedPwd)

// Base64 Decoding
let base64DecodedId = base64EncodedId
let base64DecodedPwd = base64EncodedPwd

base64DecodedId = base64DecodedId
    .replace(/!/gi, &#39;1&#39;)
    .replace(/@/gi, &#39;2&#39;)
    .replace(/\$/gi, &#39;3&#39;)
    .replace(/\^/gi, &#39;4&#39;)
    .replace(/&amp;/gi, &#39;5&#39;)
    .replace(/\*/gi, &#39;6&#39;)
    .replace(/\(/gi, &#39;7&#39;)
    .replace(/\)/gi, &#39;8&#39;)
base64DecodedPwd = base64DecodedPwd
    .replace(/!/gi, &#39;1&#39;)
    .replace(/@/gi, &#39;2&#39;)
    .replace(/\$/gi, &#39;3&#39;)
    .replace(/\^/gi, &#39;4&#39;)
    .replace(/&amp;/gi, &#39;5&#39;)
    .replace(/\*/gi, &#39;6&#39;)
    .replace(/\(/gi, &#39;7&#39;)
    .replace(/\)/gi, &#39;8&#39;)

for (i = 0; i &lt; 20; i++) {
    base64DecodedId = Buffer.from(base64DecodedId, &#39;base64&#39;).toString(&#39;utf8&#39;)
    base64DecodedPwd = Buffer.from(base64DecodedPwd, &#39;base64&#39;).toString(&#39;utf8&#39;)
}

console.log(&#39;Base64 Decoded Id : &#39;, base64DecodedId)
console.log(&#39;Base64 Decoded pwd : &#39;, base64DecodedPwd)</code></pre><p>자바스크립트로 짠 코드이다.
코드는 정말 단순한 구조로 되어있다.</p>
<p>그냥 단순하게 base 64 encode를 20번 진행하고 1~8을 각 대응하는 특수문자로 바꿔준다 그게 답이다.</p>
<p>하지만 의심병이 많은 나는 decode도 진행해봤다. 혹시나 내가 틀렸을 수도 있으니..</p>
<p>결과는 잘 나온다.</p>
<blockquote>
<p><strong>실행결과</strong></p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/stulta_amiko/post/716dc0bf-ce03-49ef-a674-661c378f8395/image.png" alt=""></p>
<p>이런식으로 encode된 코드가 나오면 edit this cookie를 이용해서 
해당하는 쿠키에 값을 넣어주면 문제가 해결된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Chocolatey git설치 오류 (Wincupl 관련)]]></title>
            <link>https://velog.io/@stulta_amiko/crash1</link>
            <guid>https://velog.io/@stulta_amiko/crash1</guid>
            <pubDate>Sun, 14 May 2023 13:21:54 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/stulta_amiko/post/18f8ce97-4870-4ad6-b7dd-e823286014af/image.jpg" alt=""></p>
<p>프로젝트를 진행하던 도중 git 설치를 위해 chocolatey를 이용하는데 문제가 생겼다는 질문이 있어서 해결해주려고 하는데
난생 처음보는 오류가 발생해 있던것이다.</p>
<p>문자열의 길이는 0일 수 없습니다.
매개변수 이름 variable이라는 오류가 나온다.</p>
<p>한국어로 위와같은 오류를 검색하면 관련된 결과가 하나도 나오지 않게된다.</p>
<p>하지만 위 오류를 영어로 검색하게 되면 스택오버플로우 페이지가 하나 나오게된다.
<a href="https://stackoverflow.com/questions/59265652/chocolatey-any-package-install-error-string-cannot-be-of-zero-length">https://stackoverflow.com/questions/59265652/chocolatey-any-package-install-error-string-cannot-be-of-zero-length</a></p>
<p>답변을 참고해보면 Wincupl때문에 그렇다는 내용이 있다.
윈커플을 삭제하게되면 문제가 해결된다는 것이다.</p>
<p>답변에 있는 깃허브 링크로 들어가게 되면 작성자는 다음과 같이 말한다.</p>
<blockquote>
<p>환경변수를 엉망으로 만드는 WinCupl과 충돌하게 된다.</p>
</blockquote>
<p>따라서 Wincupl을 지우면 해결되는 문제이다.</p>
<p>팀원과 같은 과목을 수강하면서 wincupl의 존재를 알아서 다행이었다. 아니었으면 얼마나 헤맸을지..
wincupl은 하드웨어 코딩용으로 사용하는 프로그램중 하나이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[타입스크립트 기초 - 24]]></title>
            <link>https://velog.io/@stulta_amiko/tsb24</link>
            <guid>https://velog.io/@stulta_amiko/tsb24</guid>
            <pubDate>Mon, 22 Aug 2022 14:43:04 GMT</pubDate>
            <description><![CDATA[<h3 id="maybe-테스트">Maybe 테스트</h3>
<p>현실적인 상황에서 Maybe 모나드를 사용하는 예를 들면
웹서버는 HTML을 서버에서 만들어보내는 경우와
데이터를 JSON 포맷으로 보내고 브라우저쪽에서 데이터를 바탕으로 동적으로 생성하게 하는 API 서버로 구분할 수 있다.</p>
<p>웹브라우저에서 API 서버 데이터를 가져올때는 fetch 라는 함수를 사용한다.
fetch 함수는 웹 브라우저에서 기본으로 제공하지만 node.js 에서는 그렇지 않다.</p>
<p>fetch 함수는 다음처럼 문자열로 된 URL을 입력 매개변수로 호출하면 Promise 객체를 반환한다.</p>
<blockquote>
<p>fetch(url: string): Promise</p>
</blockquote>
<p>fetch가 반환한 Promise 객체는 then 메서드를 호출해 얻은 응답 객체의 text,blob,json과 같은 메서드를 호출해 실제 데이터를 얻을 수 있다.</p>
<p>다음 코드는 JSON으로 포맷된 글을 fetch 함수로 가져오는 코드이다.</p>
<p>코드를 쓰려고했는데...
책에있는 node-fetch의 버전이 낮은건지 현재 설치된 버전이랑 호환이 안되는지 node-fetch의 많은사람들이 빠지는 문제에 빠지게되었다.</p>
<p>그래서 Maybe 모나드의 테스트 코드는 넘어가기로 했다.
무엇보다 모나드의 이해를 위해 fetch를 이용하는것인데
fetch를 뜯어보고있으면 의도되지않은 방향으로 나가는것 이므로 다음으로 넘어가기로 결정했다.</p>
<p>그리고 무엇보다 하루동안이나 스택오버플로우랑 깃허브를 뒤지고 진짜 별에별 블로그를 다 뒤져봤는데도 결국 똑같은 문제가 발생한다.</p>
<h2 id="validation-모나드-이해와-구현">Validation 모나드 이해와 구현</h2>
<h3 id="validation-모나드란">Validation 모나드란?</h3>
<p>데이터 유무의 따라 코드가 적절하게 동작하는게 Maybe 모나드였다.
데이터가 있는데 그 데이터가 유효한지 판단하는 용도로 설계된 모나드가 Validation이다.
Validation 모나드는 판타지랜드의 어플라이 규격에 의존에 동작한다.</p>
<p>Validation 모나드는 Maybe와 비슷하게 Success 와 Failure 두 가지 모나드로 구성된다.
Success 와 Failure 모나드는 기본적으로 Identitiy 모나드의 ap 메서드 방식으로 동작한다.
ap 메서드를 사용할 때에는 Identity 모나드의 value가 함수여야 한다.</p>
<pre><code>import { Identity } from &quot;../classes/Identity&quot;;

const add = (a: number) =&gt; (b: number) =&gt; a+b
console.log(
    add(1)(2),
    Identity.of(add).ap(1).ap(2).value()
)</code></pre><p>먼저 앞에서본 Identity 모나드를 이용해서 2차 고차함수를 value로 삼은뒤 ap 메서드를 두번 호출해서 3을 만든다.</p>
<pre><code>import { Identity } from &quot;../classes/Identity&quot;;
type ISuccess = {isSuccess: boolean,isFailure: boolean}

const chechSuccess = (a: ISuccess) =&gt; (b: ISuccess) :boolean =&gt;
    [a,b].filter(({isFailure}) =&gt; isFailure == true).length == 0

const isSuccess = Identity.of(chechSuccess)
                          .ap({isSuccess: true,isFailure: false})
                          .ap({isSuccess: false,isFailure: true})
                          .value()

console.log(isSuccess)</code></pre><p>checkSuccess 함수는 두개의 고차 매개변수들을 배열로 만든다음 
isFailure 값이 true인것들만 추려내서 그 개수가 0개일 때만 성공이라고 판단한다.</p>
<p>하지만 코드는 isFaliure가 한번이라도 true인 적이 있으므로 false가 출력된다.
Validation 모나드가 제공하는 Success와 Failure 모나드는 이런방식으로 동작한다.</p>
<h3 id="validation-모나드-구조">Validation 모나드 구조</h3>
<pre><code>import { Success } from &quot;./Success&quot;;
import { Failure } from &quot;./Failure&quot;;

export class Validation{
    static Success = Success
    static Failure = Failure
    static of&lt;T&gt;(fn:T): Success&lt;T&gt; {return Success.of&lt;T&gt;(fn)}
}

export {Success,Failure}</code></pre><p>Validation 클래스는 Maybe와 비슷하게 Success와 Failure 두가지 모나드로 구성된다.</p>
<h3 id="success-모나드-구현">Success 모나드 구현</h3>
<p>Success 모나드는 IChain 형태로는 동작하지 않으므로 IFunctor와 IApply,IApplicative만 구현한다.
다른 메서드들과 달리 ap메서드는 매개변수가 Failure인지에 따라 조금 다르게 동작한다.</p>
<pre><code>import { IFunctor,IApply } from &quot;../../../ch11/src/interface&quot;;
import { IValidation } from &quot;./IValidation&quot;;

export class Success&lt;T&gt; implements IValidation&lt;T&gt;,IFunctor&lt;T&gt;,IApply&lt;T&gt;{
    constructor(public value: T,public isSuccess = true,public isFailure = false) {}
    //IApplicative
    static of&lt;U&gt;(value: U): Success&lt;U&gt; {return new Success&lt;U&gt;(value)}

    //IFunctor
    map&lt;U&gt;(fn: (x:T) =&gt; U){
        return new Success&lt;U&gt;(fn(this.value))
    }

    //IApply
    ap(b){
        return b.isFailure ? b : b.map(this.value)
    }
}</code></pre><p>Success 클래스의 value는 현재 함수이다.
따라서 map 함수의 콜백함수로 사용될 수 있다.</p>
<p>다음 테스트코드는 checkSuccess 함수가 최종적으로 boolean 타입의 값을 반환하므로 value 값은 true이다.</p>
<pre><code>import { Success } from &quot;../classes/Success&quot;;

const checkSuccess = &lt;T&gt;(a: Success&lt;T&gt;) =&gt; (b: Success&lt;T&gt;):boolean =&gt;
    [a,b].filter(({isFailure}) =&gt; isFailure == true).length == 0

console.log(
    Success.of(checkSuccess)
        .ap(Success.of(1))
        .ap(Success.of(2))
)</code></pre><blockquote>
<p><strong>실행결과</strong>
Success { value: true, isSuccess: true, isFailure: false }</p>
</blockquote>
<h3 id="failure-모나드-구현">Failure 모나드 구현</h3>
<p>Failure 모나드는 최종적으로 실패한 원인을 문자열 배열로 저장하는 것으로 구현할 것이다.</p>
<p>다음 Failure 구현코드는 ap메서드에서 과거 ap 호출때 발생한 에러 문자열들이 담긴 현재의 에러 문자열들을 전개연산자로 병합한다.</p>
<pre><code>import { IFunctor,IApply } from &quot;../../../ch11/src/interface&quot;;
import { IValidation } from &quot;./IValidation&quot;;

export class Failure&lt;T&gt; implements IValidation&lt;T&gt;,IFunctor&lt;T&gt;,IApply&lt;T&gt;{
    constructor(public value: T[],public isSuccess = false,public isFailure = true) {}

    //IApplicative
    static of&lt;U&gt;(value: U[]):Failure&lt;U&gt; {return new Failure&lt;U&gt;(value)}

    //IFunctor
    map(fn) {return new Failure&lt;T&gt;(fn(this.value))}

    //IApply
    ap(b){
        return b.isFailure ? new Failure&lt;T&gt;([...this.value,...b.value]) : this
    }
}</code></pre><h3 id="비밀번호-검증기능-구현">비밀번호 검증기능 구현</h3>
<p>비밀번호를 검증하려면 객체에 password 속성이 있어야하고 이속성에 string 타입이 들어있어야 한다.
다음 checkNull 함수는 이런내용을 검증한다.</p>
<pre><code>import { Success,Failure } from &quot;../classes/Validation&quot;;

export const checkNull = &lt;S,F&gt;(o: {password?: string}) =&gt; {
    const {password} = o
    return (password == undefined || typeof password != &#39;string&#39;)?
        new Failure([&#39;PAssword cant be null&#39;]) : new Success(o)
}</code></pre><p>그리고 길이를 만족해야지 넘어갈수있는지를 판별하는 함수인 checkLength함수는 다음과 같이 구현한다.</p>
<pre><code>import { Success,Failure } from &quot;../classes/Validation&quot;;

export const checkLength = (o: {password?: string}, minLength: number = 6) =&gt;{
        const {password} = o
        return (!password || password.length &lt; minLength) ?
            new Failure([&#39;Password must have more than 6 characters&#39;]) : new Success(o)
}</code></pre><p>위 두 함수를 이용해서 비밀번호의 조건이 만족하는지에 대해 판별하는 함수인 checkPassword 함수는 다음과 같이 구현한다.</p>
<pre><code>import { Validation } from &quot;../classes/Validation&quot;;
import { checkNull } from &quot;./checkNUll&quot;;
import { checkLength } from &quot;./checkLength&quot;;

export const checkPassword = (o) : [object,string[]] =&gt;{
    const result = Validation.of(a =&gt; b =&gt; o)
        .ap(checkNull(o))
        .ap(checkLength(o))

    return result.isSuccess ? [result.value, undefined] : [undefined, result.value]
}</code></pre><p>그리고 이를 테스트하는 코드를 짜보자</p>
<pre><code>import { checkPassword } from &quot;../utils/checkPassword&quot;;

[
    {password: &#39;123456&#39;},
    {password: &#39;1234&#39;},
    {},
    {pa: &#39;123456&#39;},
]

    .forEach((target,index) =&gt; {
        const [value, failureReason] = checkPassword(target)
        if(failureReason)
            console.log(index, &#39;validation fail.&#39;, JSON.stringify(failureReason))
        else 
            console.log(index, &#39;validation ok&#39;, JSON.stringify(value))
    })</code></pre><blockquote>
<p><strong>실행결과</strong>
0 validation ok {&quot;password&quot;:&quot;123456&quot;}
1 validation fail. [&quot;1Password must have more than 6 characters&quot;]
2 validation fail. [&quot;12Password cant be null&quot;,&quot;1Password must have more than 6 characters&quot;]
3 validation fail. [&quot;12Password cant be null&quot;,&quot;1Password must have more than 6 characters&quot;]</p>
</blockquote>
<p>잘 작동되는 모습을 볼 수 있다.</p>
<h3 id="이메일주소-검증기능-구현">이메일주소 검증기능 구현</h3>
<p>이메일 주소처럼 어떤 패턴이 있는 경우 정규식을 사용해 유효성을 판별할 수 있다.
다음 코드는 정규식을 사용해 이메일 주소를 검증한다.</p>
<pre><code>import { Success,Failure } from &quot;../classes/Validation&quot;;

export const checkEmailAddress = (o: {email?: string}) =&gt;{
    const {email} = o
    const re = new RegExp(/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/)
    return re.test(email) ? new Success(email)
        :new Failure([&#39;invalid email address&#39;])
}</code></pre><p>정규식은 인터넷에 검색하면 나온다.</p>
<pre><code>import { Validation } from &quot;./classes/Validation&quot;;
import { checkNull } from &quot;./utils/checkNUll&quot;;
import  {checkEmailAddress} from &quot;./utils/checkEmailAddress&quot;

export const checkEmail = (o) : [object, string[]] =&gt; {
    const result = Validation.of(a =&gt; o)
        .ap(checkEmailAddress(o))

    return result.isSuccess ? [result.value, undefined] : [undefined, result.value]
}</code></pre><p>위는 이메일을 검증하는 함수이다.</p>
<p>다음은 이메일 체크 메서드르 시험하는 코드이다.</p>
<pre><code>import { checkEmail } from &quot;../checkEmail&quot;;

[
    {email: &#39;abc@cgf.com&#39;},
    {email: &#39;aaaa&#39;}
].forEach((target,index) =&gt;{
    const [value,failureReason] = checkEmail(target)
    if(failureReason)
        console.log(index,&#39;validation Fail&#39;,JSON.stringify(failureReason))
    else
        console.log(index,&#39;validation ok&#39;,JSON.stringify(value))
})</code></pre><blockquote>
<p><strong>실행결과</strong>
0 validation ok {&quot;email&quot;:&quot;<a href="mailto:abc@cgf.com">abc@cgf.com</a>&quot;}
1 validation Fail [&quot;invalid email address&quot;]</p>
</blockquote>
<p>정상적으로 작동하는 모습을 볼 수 있다.</p>
<h2 id="io-모나드-이해와-구현">IO 모나드 이해와 구현</h2>
<h3 id="io-모나드란">IO 모나드란?</h3>
<p>Promise 객체는 생성할때 넘겨주는 콜백함수가 then 메서드를 호출해야 비로소 동작하는데 IO모나드도 이런방식으로 동작한다.
IO모나드는 사용하는 쪽 코드를 알아야 그 동작을 이해할 수 있으므로 그 동작을 이해할 수 있다.</p>
<p>다음코드에서 work 라는 함수가 있고 IO.of(work)로 IO 객체를 만든다.
여기서는 work 함수가 실행되지 않고
이후에 runIO 메서드가 호출되면 그때 동작하게된다.</p>
<h3 id="runio-메서드-이해하기">runIO 메서드 이해하기</h3>
<pre><code>export interface IRunIO {
    runIO&lt;R&gt; (...args: any[]):R
}</code></pre><p>runIO 메서드는 다음 코드처럼 여러개의 매개변수를 사용해 동작할 수 있다.</p>
<h3 id="io-모나드-구현">IO 모나드 구현</h3>
<p>IO 모나드가 동작하는 모습은 IApply의 ap 메서드를 연상시키지만 runIO에 의해 동작한다.
따라서 다음 IO 모나드 구현 코드에서는 IApply 메서드를 구현하지 않는다.
IO모나드의 map 메서드는 runIO가 호출되기 전까지는 동작하지 말아야 한다.
이에 따라 다른 모나드와 다르게 입력받은 콜백함수를 pipe를 사용해 조합하는 방식으로 구현해야한다.</p>
<pre><code>import { IRunIO } from &quot;./IrunIO&quot;;
import { IFunctor } from &quot;../interface&quot;;

const pipe = (...funcs) =&gt; (args) =&gt; funcs.reduce((value,fn) =&gt; fn(value),args)

export class IO implements IRunIO,IFunctor&lt;Function&gt;{
    constructor(public fn : Function) {}
    static of(fn: Function) {return new IO(fn)}

    //IRunIO
    runIO&lt;T&gt; (...args: any[]):T {
        return this.fn(...args) as T
    }
    //IFunctor
    map(fn: Function): IO{
        const f: Function = pipe(this.fn,fn)
        return IO.of(f)
    }    
    //IChain
    chain(fn){
        const that = this
        return IO.of((value) =&gt;{
            const io = fn(that.fn(value))
            return io.fn()
        })
    }
}
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[타입스크립트 기초 - 23]]></title>
            <link>https://velog.io/@stulta_amiko/tsb23</link>
            <guid>https://velog.io/@stulta_amiko/tsb23</guid>
            <pubDate>Thu, 18 Aug 2022 14:54:55 GMT</pubDate>
            <description><![CDATA[<h2 id="maybe-모나드-구현">Maybe 모나드 구현</h2>
<p>Maybe는 오류일때와 정상일때 모두를 고려하면서도 사용하는 쪽 코드를 간결하게 작성할 수 있게 해준다.
데이터의 유무에 따라 코드가 적절하게 동작하도록 설계했다.
Maybe는 하스켈 Prelude 표준 라이브러리에서 제공하는 모나드이다.</p>
<p>Maybe 모나드는 Option의 Some,None과 비슷한 의미를 가진 Just와 Nothing 이라는 두가지 타입을 제공한다.</p>
<p>Maybe 자체가 모나드가 아니고 <code>Just&lt;T&gt;</code>와 Nothing 타입이 모나드라고 한다.</p>
<pre><code>export class Maybe&lt;T&gt; {
    static Just&lt;U&gt;(value: U) {return new Just&lt;U&gt;(value)}
    static Nothing = new Nothing
}</code></pre><p>Maybe의 설계 목적은 코드의 안정성을 함수형 방식으로 보장받기 위해서이다.
예를들어 타입스크립트는 수를 0으로 나누면 0으로 나눌 수 없다는 예외를 발생시키는 것이 아니라 Infinity 값이 발생한다.</p>
<p>Infinity값은 number 타입의 값이므로 비정상 종료되는것이 아니라 로직에 혼동을 줄 수 있는 값이므로 피하는 것이 좋다.
따라서 undefined,null,Infinity등의 값을 유발할때 Maybe를 사용하면 효율적인 방식으로 코드를 작성할 수 있다.</p>
<pre><code>import {Maybe,IMaybe} from &#39;../classes/Maybe&#39;
const devide (a: number) =&gt; (b: number): IMaybe&lt;number&gt; =&gt;
b ? Maybe.just(a/b) : Maybe.Nothing</code></pre><p>위 코드는 b의 값이 undefined null 0 이 아닐때는 Maybe.just(a/b)가 반환되지만 반대일때는 Maybe.Nothing이 반환된다.</p>
<p>따라서 divide 함수는 간결하면서도 안정성을 해지지 않도록 작성된것이다.</p>
<h3 id="maybe-클래스-구조">Maybe 클래스 구조</h3>
<pre><code>import { Just } from &quot;../interface/Just&quot;;
import { Nothing } from &quot;../interface/Nothing&quot;;
import { IMonad } from &quot;../../../ch11/src/interface&quot;;
import { _IMaybe } from &quot;./_IMaybe&quot;;

export class Maybe&lt;T&gt;{
    static Just&lt;U&gt;(value: U) {return new Just&lt;U&gt;(value)}
    static Nothing =  new Nothing
}

export type IMaybe&lt;T&gt; = _IMaybe&lt;T&gt; &amp; IMonad&lt;T&gt;</code></pre><h3 id="just-모나드-구현">Just 모나드 구현</h3>
<p>Just 모나드는 앞에서 구현한 Identity 모나드에 _IMaybe 인터페이스를 구현한 다음과 같은 내용으로 구현한다.
Identity와 다르게 ISetoid는 구현하지 않는데
이는 Just 가 Nothing 일때를 고려해 value()가 아닌 getOrElse(0) 와 같은 형태로 동작하는것을 염두한 것이다.</p>
<pre><code>import { _IMaybe } from &#39;../classes/_IMaybe&#39;;
import {IMonad} from &#39;../../../ch11/src/interface&#39;

export class Just&lt;T&gt; implements _IMaybe&lt;T&gt;,IMonad&lt;T&gt;{
    constructor(private _value: T) {}
    value(): T {return this._value}

    //IApplicative
    static of&lt;T&gt;(value: T): Just&lt;T&gt; {return new Just&lt;T&gt;(value)}

    //IMaybe
    isJust(): boolean {return true}
    isNothing(): boolean {return false}
    getOrElse&lt;U&gt;(defaultValue: U) {return this.value()}

    //IFunctor
    map&lt;U,V&gt;(fn: (x:T) =&gt; U): Just&lt;U&gt; {return new Just&lt;U&gt;(fn(this.value()))}

    //IApply
    ap&lt;U&gt;(b: U){
        const f = this.value()
        if(f instanceof Function)
            return Just.of&lt;U&gt;((f as Function)(b))
    }
    //IChain
    chain&lt;U&gt;(fn: (T) =&gt; U):U {return fn(this.value())}
}</code></pre><h3 id="nothing-모나드-구현">Nothing 모나드 구현</h3>
<p>Nohing 모나드는 Just 모나드와 다르게 코드를 완벽하게 실행시키지 않는것이 설계 목적이다.
다음 코드는 <code>divide(1)(0).map(R.add(1)).getOrElse(0)</code> 형태의 코드가 비정상적으로 동작하지 않고 마지막에 호출된 메서드의 기본값을 반환하는것을 목적으로 작성했다.</p>
<pre><code>import { IMonad } from &quot;../../../ch11/src/interface&quot;;
import { _IMaybe } from &quot;../classes/_IMaybe&quot;;

export class Nothing implements _IMaybe&lt;null&gt;,IMonad&lt;null&gt;{
    //IApplicative
    static of&lt;T&gt;(value: T = null):Nothing {return new Nothing}

    // IMaybe
    isJust(): boolean {return false}
    isNothing(): boolean {return true}
    getOrElse&lt;U&gt;(defaultValue: U) {return defaultValue}

    //IFunctor
    map&lt;U,V&gt;(fn: (x)=&gt; U):Nothing {return new Nothing}

    //IApply
    ap&lt;U&gt;(b: U){return new Nothing}

    //IChain
    chain&lt;U&gt;(fn: (T) =&gt; U):Nothing{return new Nothing}
}</code></pre><h3 id="just와-nothing-모나드-단위-테스트">Just와 Nothing 모나드 단위 테스트</h3>
<p>Just는 정상적일때 동작하는 모나드 이므로 항상 자신의 실제값을 반환해야한다.
다음 코드는 Just가 정상적으로 작동하는것 처럼 보이면서 _IMaybe 인터페이스 기능을 추가로 제공하는 것을 보여준다.</p>
<pre><code>import * as R from &#39;ramda&#39;
import { Just } from &#39;../interface/Just&#39;

console.log(
    Just.of(100).isJust(), //true
    Just.of(100).isNothing(), //false
    Just.of(100).getOrElse(1), //100
    Just.of(100).map(R.identity).getOrElse(1), //100
    Just.of(R.identity).ap(100).getOrElse(1), //100
    Just.of(100).chain(Just.of).getOrElse(1) //100
)
</code></pre><p>다음 Nothing 모나드 테스트에서는 Just와 달리 자신의 모나드 관련 코드를 동작시키면 안된다.
또한 undefined나 null Nan Infinity와 같은 값을 반환해서도 안된다.</p>
<pre><code>import {Nothing} from &#39;../classes/Nothing&#39;
import {Just} from &#39;../classes/Just&#39;

console.log(
    Nothing.of().isJust(), //false
    Nothing.of().isNothing(), //true
    Nothing.of().getOrElse(1), //1
    Nothing.of().map(x =&gt; x+1).getOrElse(1), //1
    Nothing.of().ap(1).getOrElse(1), //1
    Nothing.of().chain(Just.of).getOrElse(1) //1
)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[타입스크립트 기초 - 22]]></title>
            <link>https://velog.io/@stulta_amiko/tsb22</link>
            <guid>https://velog.io/@stulta_amiko/tsb22</guid>
            <pubDate>Tue, 16 Aug 2022 13:34:57 GMT</pubDate>
            <description><![CDATA[<h2 id="모나드">모나드</h2>
<p>모나드는 수학의 카테고리 이론이라는 분야에서 사용되는 용어이다.
프로그래밍에서의 모나드는 일종의 코드 설계 패턴으로서 몇 개의 인터페이스를 구현한 클래스이다.
모나드 클래스는 몇가지 공통적인 특징이 있다.</p>
<h3 id="타입-클래스">타입 클래스</h3>
<p>모나드를 이해하기 위해서는 타입클래스가 왜 필요한지 알아야한다.
다음 2차 고차함수 callMap은 두번째 고차 매개변수 b가 map 이라는 메서드를 가졌다는 가정으로 구현되어 있다.</p>
<pre><code>const callaMap = fn =&gt; b =&gt; b.map(fn)</code></pre><p>다음코드를 사용하면 작성자의 의도대로 작동한다.</p>
<pre><code>callMap(a =&gt; a+1)([1])</code></pre><p>하지만 다음과 같이 작성하게 된다면 문제가 발생하게 된다.</p>
<pre><code>callMap(a =&gt; a+1)(1)</code></pre><p>비정상 종료가 된다.
이를 방지하기 위해서 매개변수 b는 반드시 map 메서드가 있는 타입이라고 타입을 제한해야한다.</p>
<pre><code>const callMap = &lt;T,U&gt;(fn: (T) =&gt; U) =&gt; &lt;T extends {map(fn)}&gt;(b:T) =&gt; b.map(fn)</code></pre><p>이런식으로 하면 비정상적인 종료를 방지하기위해 작성시점에서 오류를 띄우게 된다.</p>
<p>하스켈에서는 발상의 전환을 해서 모나드식 설계를 이루어 냈다.
보통의 객체지향언어라면 Number클래스를 만들고 map이라는 메서드를 구현하겠지만 그런방식이 아닌 map과 of 라는 이름의 메서드가 있는 <code>Monad&lt;T&gt;</code> 클래스를 만든다.</p>
<pre><code>class Monad&lt;T&gt;{
    constructor(public value: T) {}
    static of&lt;U&gt;(value: U): Monad&lt;U&gt; {return new Monad&lt;U&gt;(value)}
    map&lt;U&gt;(fn: (x: T) =&gt; U):Monad&lt;U&gt; {return new Monad&lt;U&gt;(fn(this.value))
}</code></pre><p>위와같은 모나드 클래스를 두고 타입클래스 라고한다.
타입 클래스는 다음처럼 함수를 만들 때 특별한 타입으로 제약하지 않아도된다.</p>
<pre><code>const callMonad = (fn) =&gt; (b) =&gt; Monad.of(b).map(fn).value</code></pre><p><code>Monad&lt;T&gt;</code>와 같은 타입클래스덕분에 타입에 대한 안정성을 보장하면서도 코드의 재사용성이 뛰어난 범용함수를 쉽게 만들 수 있다.
callMonad와 같은 함수는 한 번 만들어두면 다음처럼 매개변수의 타입에 무관한 간결한 코드를 쉽게 작성할 수 있다.</p>
<pre><code>callMonad((a: number) =&gt; a+1)(1)
callMonad((a: number[]) =&gt; a.map(value =&gt; value+1))([1,2,3,4])</code></pre><h3 id="고차타입">고차타입</h3>
<p>앞에서 본 <code>Monad&lt;T&gt;</code>는 타입 T를 <code>Monad&lt;T&gt;</code> 타입으로 변환했다가 다시 T 타입으로 변환해 준다. <code>Monad&lt;T&gt;</code> 처럼 타입 T를 한단계 더 높은 타입으로 변환하는 용도의 타입을 고차타입이라고 한다.</p>
<h3 id="판타지랜드-규격">판타지랜드 규격</h3>
<p><img src="blob:https://velog.io/19c4006b-b7fa-44bc-bbed-72f49c8cd8ca" alt="업로드중.."></p>
<p>위와 같은 구조를 모나드 계층 구조라고 한다.
판타지랜드 규격은 하스켈 표준 라이브러리 구조를 자바스크립트 방식으로 재구성 한 것을 뜻한다.</p>
<p>어떤 클래스가 다음 조건을 만족하면 그 클래스는 모나드이다.
펑터,어플라이,애플리커티브,체인 을 가지고있으면 모나드이다.</p>
<p>펑터는 map이라는 인스턴스 메서드를 가지는 것을 의미하고
어플라이는 펑터이면서 ap 메서드를 가지는 것을 의미하고
애플리커티브는 어플라이이면서 of라는 클래스 메서드를 가지는것을 의미하고
체인은 어플라이이면서 chain 메서드를 가지는 것을 의미한다.</p>
<p>위 조건을 모두 만족하면 모나드라는 것이다.</p>
<h2 id="identity-모나드-이해와-구현">identity 모나드 이해와 구현</h2>
<h3 id="값-컨테이너-구현용-ivalueablet-인터페이스-구현">값 컨테이너 구현용 <code>IValueable&lt;T&gt;</code> 인터페이스 구현</h3>
<p>어떤 타입 T가 있을 때 배열 T[]는 같은 타입의 아이템을 여러개 가지고있는 컨테이너이다.
앞에서 본 <code>Monad&lt;T&gt;</code> 처럼 배열이 아닌 단지 한개의 값만 가지는 컨테이너 클래스를 생각해 볼 수 있다.
이 컨테이너 클래스는 number 와 같은 구체적인 타입의 값을 가지는 것이 아니라 모든 타입 T를 가질 수 있는 제네릭 컨테이너 클래스를 생각할 수 있다.
이처럼 타입 T를 가지는 값의 컨테이너를 값 컨테이너라고 한다.</p>
<pre><code>export interface IValuable&lt;T&gt;{
    value(): T
}</code></pre><h3 id="identity-인-이유">identity 인 이유</h3>
<p>함수형 프로그래밍에서 identity는 항상 다음처럼 구현하는 특별한 의미의 함수이다.</p>
<pre><code>const identity = &lt;T&gt;(value: T): T =&gt; value</code></pre><p>앞에서 살펴본 샘플코드가 있는데
R.ifElse의 false 조건 부분에 사용된 R.identity가 앞의 identity를 구현하고 있다.</p>
<pre><code>const applyDiscount = (minimum: number,discount: number) =&gt; R.ifElse(
    R.flip(R.gte)(minimum),
    R.flip(R.subtract)(discount),
    R.identity
    )</code></pre><p>Identity는 앞에서 본 map,ap,of,chain과 같은 기본 메서드만 구현한 모나드이다.
카테고리 이론에서 자신의 타입에서 다른타입으로 갔다가 돌아올 때 값이 변경되지 않는 카테고리를 Identity라고 부른다.</p>
<p>다음 코드에서 <code>Identity&lt;number&gt;</code> 타입은 chain 메서드를 통해 다시 자기 자신의 타입으로 돌아올 수 있는데 이런 의미에서 Identity라는 이름으로 짓게 되는것이다.</p>
<h3 id="값-컨테이너로서의-identityt-구현하기">값 컨테이너로서의 <code>Identity&lt;T&gt;</code> 구현하기</h3>
<p>앞에서 본 <code>Monad&lt;T&gt;</code>는 코드 분량을 줄이고자 value 속성을 public 하게 사용했는데 Identity는 _value 속성을 private 하게 구현하는 대신 value를 public 하게 구현한다.</p>
<pre><code>import { IValuable } from &quot;../interface/IValuable&quot;;

export class Identity&lt;T&gt; implements IValuable&lt;T&gt;{
    constructor(private _value: T){}
    value() {return this._value}
}</code></pre><h3 id="isetoidt-인터페이스와-구현"><code>Isetoid&lt;T&gt;</code> 인터페이스와 구현</h3>
<p>판타지랜드 규격에서 setoid는 equals 라는 이름의 메서드를 제공하는 인터페이슬 의미한다.</p>
<pre><code>import { IValuable } from &quot;./IValuable&quot;;

export interface ISetoid&lt;T&gt; extends IValuable&lt;T&gt;{
    equals&lt;U&gt;(value: U): boolean
}</code></pre><p>ISetoid 인터페이스는 위와같이 구현한다.</p>
<pre><code>import { ISetoid } from &quot;../interface/ISetoid&quot;;

export class Identity&lt;T&gt; implements ISetoid&lt;T&gt;{
    constructor(private _value:T){}
    value() {return this._value}
    equals&lt;U&gt;(that: U): boolean {
        if(that instanceof Identity)
            return this.value() == that.value()
        return false
    }
}</code></pre><p>그리고 Identity에 ISetoid는 위와같이 구현한다.</p>
<p>그러면 ISetoid를 구현했으니 테스트를 할수있는 테스트 코드를 짜보자</p>
<pre><code>import { Identity } from &quot;../classes/Identity&quot;;

const one = new Identity(1), anotherOne = new Identity(1)
const two = new Identity(2)

console.log(
    one.equals(anotherOne),
    one.equals(two),
    one.equals(1),
    one.equals(null),
    one.equals([1])
)</code></pre><blockquote>
<p><strong>실행결과</strong>
true false false false false</p>
</blockquote>
<h3 id="ifunctort-인터페이스-구현"><code>IFunctor&lt;T&gt;</code> 인터페이스 구현</h3>
<p>판타지랜드 규격에서 펑터는 map이라는 메서드를 제공하는 인터페이스이다.
카테고리 이론에서 펑터는 엔도펑터라는 성질을 만족시켜야한다.</p>
<pre><code>export interface IFunctor&lt;T&gt;{
    map&lt;U&gt;(fn: (x:T) =&gt; U)
}</code></pre><p><strong>엔도펑터란</strong></p>
<p>엔도는 일종의 접두사이다 특정 카테고리에서 출발해도 도착 카테고리는 다시 출발 카테고리가 되게 하는 펑터를 의미한다.
다음 <code>Identity&lt;T&gt;</code>의 map 메서드는 엔도펑터로 동작하게 만든 코드이다.
타입이 중간에 바뀔 수는 있지만 카테고리는 여전히 Identity에 머문다.</p>
<pre><code>import { IFunctor,ISetoid } from &quot;../interface&quot;;

export class Identity&lt;T&gt; implements ISetoid&lt;T&gt;,IFunctor&lt;T&gt;{
    constructor(private _value:T){}
    //IValuable
    value() {return this._value}
    //ISetoid
    equals&lt;U&gt;(that: U): boolean {
        if(that instanceof Identity)
            return this.value() == that.value()
        return false
    }
    //IFunctor
    map&lt;U&gt;(fn: (x:T) =&gt; U){
        return new Identity&lt;U&gt;(fn(this.value()))
    }
}</code></pre><h3 id="iapplyt-인터페이스-구현"><code>IApply&lt;T&gt;</code> 인터페이스 구현</h3>
<p>판타지랜드 규격에서 어플라이는 자신은 펑터이면서 동시에 ap 메서드를 제공하는 인터페이스 이다.</p>
<pre><code>import { IFunctor } from &quot;./IFunctor&quot;;

export interface IApply&lt;T&gt; extends IFunctor&lt;T&gt;{
    ap&lt;U&gt;(b: U)
}</code></pre><p>IApply를 구현하는 컨테이너는 값 컨테이너로서 뿐만 아니라 고차함수의 컨테이너로도 작동한다.</p>
<pre><code>import { Identity } from &quot;../classes/Identity&quot;;

const add = x =&gt; y =&gt; x+y
const id = new Identity(add)

console.log(
    id.ap(1).ap(2).value()
)</code></pre><p>위는 IApply를 시험하는 코드이다.</p>
<p>Identity에는 다음과같이 작성한다.</p>
<pre><code>ap&lt;U&gt;(b: U){
        const f = this.value()
        if(f instanceof Function)
            return Identity.of&lt;U&gt;((f as Function)(b))
    }</code></pre><p>아직 of메서드를 작성하지 않아서 오류가 나는데 이는 다음 인터페이스를 구현하면 오류가 해결된다.</p>
<h3 id="iapplicativet-인터페이스-구현"><code>IApplicative&lt;T&gt;</code> 인터페이스 구현</h3>
<p>IApplicative 는 자신이 어플라이면서 of메서드를 추가 제공하는 인터페이스이다.</p>
<pre><code>import { IApply } from &quot;./IApply&quot;;

export interface IApplicative&lt;T&gt; extends IApply&lt;T&gt;{

}</code></pre><pre><code>    //IApplicative
    static of&lt;T&gt;(value: T): Identity&lt;T&gt; {return new Identity&lt;T&gt;(value)}</code></pre><p>Identity에는 위와같이 작성한다.</p>
<h3 id="ichaint-인터페이스-구현"><code>IChain&lt;T&gt;</code> 인터페이스 구현</h3>
<p>체인은 자신이 어플라이이면서 chain 메서드를 구현하는 인터페이스를 뜻한다.</p>
<pre><code>import { IApply } from &quot;./IApply&quot;;

export interface IChain&lt;T&gt; extends IApply&lt;T&gt;{
    chain&lt;U&gt;(fn: (T)=&gt; U)
}</code></pre><p>chain 메서드는 functor의 map과 다르게 엔도펑터로 구현할 필요가 없다.</p>
<pre><code>import { Identity } from &quot;../classes/Identity&quot;;
import { IChain } from &quot;../interface&quot;;

console.log(
    Identity.of(1).map(value =&gt; `the count is ${value}`).value(),
    Identity.of(1).chain(value =&gt; Identity.of(`the count is ${value}`).value())
)</code></pre><p>위 코드를 실행하면 같은결과가 나오지만
서로 다르게 사용하는 모습을 볼 수 있다.</p>
<p>엔도펑터인 map은 항상 같은 카테고리에 머무르므로 위와 같이 작성할 수 있지만</p>
<p>chain은 자신이 머무르고 싶은 카테고리를 스스로 정해야 하므로 map과 다르게 작성되는 모습을 볼 수 있다.</p>
<h3 id="imonadt-인터페이스-구현"><code>IMonad&lt;T&gt;</code> 인터페이스 구현</h3>
<p>판타지랜드 규격에서 모나드는 체인과 애플리커티브를 구현한 것이다.</p>
<pre><code>import { IChain } from &quot;./IChain&quot;;
import { IApplicative } from &quot;./IApplicative&quot;;

export interface IMonad&lt;T&gt; extends IChain&lt;T&gt;,IApplicative&lt;T&gt; {}</code></pre><p>이제 identity 모나드를 완성시킬 수 있다.</p>
<pre><code>import { IMonad,ISetoid } from &quot;../interface&quot;;

export class Identity&lt;T&gt; implements ISetoid&lt;T&gt;,IMonad&lt;T&gt;{
    constructor(private _value:T){}
    //IValuable
    value() {return this._value}
    //ISetoid
    equals&lt;U&gt;(that: U): boolean {
        if(that instanceof Identity)
            return this.value() == that.value()
        return false
    }
    //IFunctor
    map&lt;U&gt;(fn: (x:T) =&gt; U){
        return new Identity&lt;U&gt;(fn(this.value()))
    }
    //IApply
    ap&lt;U&gt;(b: U){
        const f = this.value()
        if(f instanceof Function)
            return Identity.of&lt;U&gt;((f as Function)(b))
    }
    //IApplicative
    static of&lt;T&gt;(value: T): Identity&lt;T&gt; {return new Identity&lt;T&gt;(value)}
    //IChain
    chain&lt;U&gt;(fn: (T) =&gt; U):U {return fn(this.value())}
}</code></pre><p>완성된 Identity 모나드의 모습이다.</p>
<p>다음코드는 모나드의 왼쪽법칙을 충족하는지 보여주는 코드이다.</p>
<pre><code>import { Identity } from &quot;../classes/Identity&quot;;

const a = 1
const f = a =&gt; a*2
console.log(
    Identity.of(a).chain(f) == f(a)  //true
)</code></pre><p>다음 코드는 모나드의 오른쪽 법칙을 충족하는지 보여주는 코드이다.</p>
<pre><code>import { Identity } from &quot;../classes/Identity&quot;;

const m = Identity.of(1)

console.log(
    m.chain(Identity.of).equals(m)
)</code></pre><p>따라서 Identity 모나드는 오른쪽법칙과 왼쪽법칙을 모두 충족하므로 정상적인 모나드라고 할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[타입스크립트 기초 - 21]]></title>
            <link>https://velog.io/@stulta_amiko/tsb21</link>
            <guid>https://velog.io/@stulta_amiko/tsb21</guid>
            <pubDate>Fri, 12 Aug 2022 06:26:36 GMT</pubDate>
            <description><![CDATA[<h2 id="nullable-타입과-프로그램-안정성">nullable 타입과 프로그램 안정성</h2>
<p>자바스크립트와 타입스크립트는 변수가 초기화되지 않으면 undefined 라는 값을 기본으로 지정한다.
하지만 비슷한의미에 null 자료형도 존재한다.
타입스크립트에서는 null자료형과 undefined 자료형이 있다.
예전에 프로젝트를 할때 null과 undefined 의 차이점에대해서 들어본적이 있는것 같다.
먼저 null은 잘못으로 인한 발생값이 아닌 무언가에 의한 고의적인 값으로 알고있다.
undefined의 경우는 값을 지정하지 않거나 무언가 잘못되었을때 발생한다.
잘모르겠다 확실한건 둘이 다르고 이런 뉘앙스를 띄는거같다.</p>
<p>이 nullable 타입은 프로그램이 동작할 떄 프로그램을 비정상적으로 종료시키는 주요 원인이 된다.
프로그램의 안전성을 해치게 된다는 뜻이다 따라서 함수형 언어들은 이를 방지하기 위해 연산자나 클래스를 제공하기도 한다.</p>
<h3 id="옵션-체이닝-연산자">옵션 체이닝 연산자</h3>
<p>변수가 선언만 되고 초기화 하지않으면 실행했을때 오류가 발생하면서 비정상적인 종료가 발생하게된다.</p>
<p>이런 오류는 프로그램의 안전성을 해치므로 프로그래밍 언어 설계자들은 옵션 체이닝 연산자나 널 병합 연산자를 제공한다.</p>
<pre><code>interface IPerson{
    name: string,
    age?: number
}

let person: IPerson
console.log(person.name)</code></pre><p>이런식으로 코드를 작성한다면 오류가 발생하지만</p>
<pre><code>interface IPerson{
    name: string,
    age?: number
}

let person: IPerson
console.log(person?.name)</code></pre><p>위와같이 작성하게된다면 
오류가 발생하지 않는것을 알 수 있다.</p>
<p>옵션 체이닝 연산자는 세이프 네비게이션 연산자라도 하는데</p>
<pre><code>type ICoordinates = {longitude: number}
type ILocation = {country: string, coords?: ICoordinates}
type IPerson = {name: string, location?: ILocation}

let person: IPerson = {name: &#39;Jack&#39;}
let longitude = person?.location?.coords?.longitude
console.log(longitude)</code></pre><p>위와 같이 간결하게 구현할 수 있기 때문이다.</p>
<pre><code>type ICoordinates = {longitude: number}
type ILocation = {country: string, coords?: ICoordinates}
type IPerson = {name: string, location?: ILocation}

let person: IPerson = {name: &#39;Jack&#39;}
if(person &amp;&amp; person.location &amp;&amp; person.person.location.coords){
    longitude = person.location.coords.longitude
    }
console.log(longitude)</code></pre><p>와 같이 구현해야 하기 때문이다.</p>
<h3 id="널-병합-연산자">널 병합 연산자</h3>
<p>자바스크립트는 옵션 체이닝 연산자를 표준으로 채택하면서 이와 동시에 물음표 기호 두개를 연달아 이어 붙인 ?? 널 병합 연산자도 표준으로 채택했다.</p>
<pre><code>type ICoordinates = {longitude: number}
type ILocation = {country: string, coords?: ICoordinates}
type IPerson = {name: string, location?: ILocation}

let person: IPerson = {name: &#39;Jack&#39;}
let longitude = person?.location?.coords?.longitude ?? 0
console.log(longitude)</code></pre><p>위 코드에서 널 병합 연산자를 사용해서 만약 nullable 한 경우 0으로 초기화하는 코드를 만들었다.
출력결과는 0이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[타입스크립트 기초 - 20]]></title>
            <link>https://velog.io/@stulta_amiko/tsb20</link>
            <guid>https://velog.io/@stulta_amiko/tsb20</guid>
            <pubDate>Tue, 09 Aug 2022 10:54:31 GMT</pubDate>
            <description><![CDATA[<h2 id="타입-가드">타입 가드</h2>
<p>다음과 같은 두개의 타입이 있다.</p>
<pre><code>export class Bird {fly() {console.log(`Flying`)}}
export class Fish {swim(){console.log(`Swimming`)}}</code></pre><blockquote>
<p>BirdAndFish.ts</p>
</blockquote>
<pre><code>import { Bird,Fish } from &quot;./BirdAndFish&quot;;

const flyOrSwim = (o:Bird|Fish): void =&gt;{

}</code></pre><blockquote>
<p>BirdAndFish-test.ts</p>
</blockquote>
<p>위 코드에서 flyOrSwim 함수를 보면 파라미터가 구체적으로 fish인지 bird인지 알 수가 없다.</p>
<h3 id="instanceof-연산자">instanceof 연산자</h3>
<p>그럴때 자바스크립트에서 제공하는 instanceof 연산자를 이용할 수 있다</p>
<pre><code>import { Bird,Fish } from &quot;./BirdAndFish&quot;;

const flyOrSwim = (o:Bird|Fish): void =&gt;{
    if(o instanceof Bird){
        (o as Bird).fly()
    }else if(o instanceof Fish){
        (&lt;Fish&gt;o).swim()
    }
}

const a:Bird = new Bird()
flyOrSwim(a)</code></pre><p>위와같이 instanceof 연산자를 이용하면 정상적인 결과가 나온다.
bird와 fish의 방식의 차이가 있는데 둘 다 사용가능하다는 것을 암시한다.</p>
<h3 id="타입가드">타입가드</h3>
<p>하지만 타입스크립트에서 타입가드 라는것이 존재한다.
타입가드는 타입을 변환하지 않은 상태에서 변환하지 않은 코드 때문에 비정상적으로 종료되는 상황을 보호해주는 것을 의미한다.</p>
<p>코드에서 마치 instanceof처럼 동작하는 함수를 구현할 수 있다.
타입가드 기능을 하는 함수를 구현할 수 있다는 말이다.
타입 가드 기능을 하는 함수는 다음처럼 함수의 반환 타입부분에 is 라는 이름의 연산자를 사용해야한다.</p>
<pre><code>import { Bird,Fish } from &quot;./BirdAndFish&quot;;

export const isFlyable = (o: Bird|Fish): o is Bird =&gt; {
    return o instanceof Bird
}
export const isSwimmable = (o: Bird|Fish): o is Fish =&gt; {
    return o instanceof Fish
}</code></pre><blockquote>
<p>isable.ts</p>
</blockquote>
<p>위와같은 형식으로 타입 가드 함수를 만들어준다.</p>
<pre><code>import { Bird,Fish } from &quot;./BirdAndFish&quot;;
import { isSwimmable,isFlyable } from &quot;./isable&quot;;

const flyOrSwim = (o:Bird|Fish): void =&gt;{
    if(isFlyable(o)){
        o.fly() 
    }else if(isSwimmable(o)){
        o.swim()
    }
}

const a:Bird = new Bird()
flyOrSwim(a)</code></pre><p>사용은 위와같이 한다. 기존 코드에서 if문 내부 조건문이 변경된것 말고는 없다.</p>
<p>위코드를 실행시키면 결과가 동일하게 나온다는 것을 알 수 있다.</p>
<h2 id="f-바운드-다형성">F-바운드 다형성</h2>
<p>타입스크립트에서 this 키워드는 타입으로도 사용된다. this가 타입으로 사용되면 객체지향 언어에서 의미하는 다형성 효과가 나타나게 된다.
일반적인 다형성과 구분하기 위해 this타입으로 인한 다형성을
<strong>&#39;F-바운드 다형성&#39;</strong> 이라고한다.</p>
<h3 id="f-바운드-타입">F-바운드 타입</h3>
<p>F-바운드 타입이란 자신을 구현하거나 상속하는 서브타입을 포함하는 타입을 말한다.
다음 <code>IValueProvider&lt;T&gt;</code>는 특별히 자신을 상속하는 타입이 포함되어 있지않은 일반타입이다.</p>
<pre><code>export interface IValueProvider&lt;T&gt;{
    value(): T
}</code></pre><p>다음 인터페이스는 add 메서드가 내가 아닌 나를 상속하는 타입을 반환하는 F-바운드 타입이다.</p>
<pre><code>export interface IAddable&lt;T&gt;{
    add(value: T): this
}</code></pre><p>다음 인터페이스는 메서드의 반환타입이 this 이므로 F-바운드 타입이다.</p>
<pre><code>export interface IMultiplyable&lt;T&gt;{
    multifly(value: T):this
}</code></pre><p>이제 위 세 개의 인터페이스를 이용해서 클래스를 구현 해야한다.</p>
<h3 id="ivalueprovidert-인터페이스-구현"><code>IValueProvider&lt;T&gt;</code> 인터페이스 구현</h3>
<pre><code>import { IValueProvider } from &quot;./IValueProvider&quot;;

export class Calculator implements IValueProvider&lt;number&gt;{
    constructor(private _value: number = 0) {}
    value(): number {return this._value}
}</code></pre><p>위 코드는 <code>IValueProvider&lt;T&gt;</code> 인터페이스를 구현하고 있다. 이 클래스는 _value 속성을 private로 만들어 Calculator 를 사용하는 코드에서 _value 속성이 아닌 value() 메서드로 접근할 수 있게 설계되었다.</p>
<p>같은방식으로 StringComposer 클래스를 구현해 보자</p>
<pre><code>import { IValueProvider } from &quot;./IValueProvider&quot;;

export class StringComposer implements IValueProvider&lt;string&gt; {
    constructor(private _value: string = &#39; &#39;) { }
    value(): string {
        return this._value
    }
}</code></pre><h3 id="iaddablet와-imultiplyablet-인터페이스-구현"><code>IAddable&lt;T&gt;</code>와 <code>IMultiplyable&lt;T&gt;</code> 인터페이스 구현</h3>
<pre><code>import { IValueProvider } from &quot;./IValueProvider&quot;;
import { IAddable } from &quot;./IAddable&quot;;
import { IMultiplyable } from &quot;./IMultiplyable&quot;;

export class Calculator implements IValueProvider&lt;number&gt;{
    constructor(private _value: number = 0) {}
    value(): number {return this._value}
    add(value: number): this{
        this._value = this._value+value
        return this
    }
    multiply(value: number): this {this._value = this._value * value; return this}
}</code></pre><p>Calculator 클래스는 메서드 체인을 이용하기 위해서 this값을 반환하는 것을 볼 수 있다.</p>
<pre><code>import { Calculator } from &quot;./Calculator&quot;;

const value = (new Calculator(1)).add(1).add(2).add(3).multiply(2).value()
console.log(value)</code></pre><p>위 Calculator 클래스를 테스트 해보면 정상적으로 값이 나오는것을 알 수 있다.
this를 반환하기 때문에 메서드 체이닝을 사용할 수 있다.</p>
<pre><code>import { IValueProvider } from &quot;./IValueProvider&quot;;
import { IAddable } from &quot;./IAddable&quot;;
import { IMultiplyable } from &quot;./IMultiplyable&quot;;

export class StringComposer implements IValueProvider&lt;string&gt; {
    constructor(private _value: string = &#39; &#39;) { }
    value(): string {
        return this._value
    }
    add(value: string):this {this._value = this._value.concat(value); return this}
    multiply(repeat: number): this {const value = this.value()
    for(let index = 0;index&lt;repeat;index++){
        this.add(value)
    }
    return this
    }
}</code></pre><p>stringcomposer는 위와 같이 구현하고서</p>
<pre><code>import { StringComposer } from &quot;./StringComposer&quot;;

const value = new StringComposer(&#39;hello&#39;).add(&#39; &#39;).add(&#39;world&#39;).multiply(3).value()
console.log(value)</code></pre><p>위와같이 실행하게 되면</p>
<blockquote>
<p><strong>실행결과</strong>
hello worldhello worldhello world</p>
</blockquote>
<p>와 같이 결과가 정상적으로 나오게된다.
클래스에 따라 반환 결과가 달라지는 것을 볼 수 있다.
this에 따라서 바뀌는데 calculator가 될 수도 있고 stringcomposer가 될 수도 있다.</p>
<p>이런식으로 동작하는 것을 F-바운드 다형성이라고 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[타입스크립트 기초 - 19]]></title>
            <link>https://velog.io/@stulta_amiko/tsb19</link>
            <guid>https://velog.io/@stulta_amiko/tsb19</guid>
            <pubDate>Mon, 08 Aug 2022 11:34:18 GMT</pubDate>
            <description><![CDATA[<h2 id="대수-데이터-타입">대수 데이터 타입</h2>
<p>대수 데이터 타입이라는것이 있다.
Algebraic Data Type 줄여서 ADT라고도 부르는것이다.</p>
<p>여기서 대수라는것은 대수학의 대수와 같은 의미이다.
구글에 대수 데이터 타입에 대해 검색하면 여러 결과가 나오지만 이해하기 힘든 내용이 많다.</p>
<p>hope 라는 언어에서 처음 사용된 ADT는 함수형 프로그래밍의 시초이다.</p>
<h3 id="합집합-타입">합집합 타입</h3>
<p>합집합 타입은 또는의 의미인 &#39;|&#39; 기호로 다양한 타입을 연결해서 만든 타입을 말한다.</p>
<pre><code>type NumberOrString = number|string
let ns: NumberOrString = 1
ns = &#39;hello&#39;</code></pre><p>위 코드는 number와 string 모두 담을 수 있는 합집합 타입을 이용한 코드이다.</p>
<h3 id="교집합-타입">교집합 타입</h3>
<p>교집합 타입은 and의 의미인 &amp; 기호로 다양한 타입을 연결해서 만드는 타입을 말한다.
교집합 타입의 대표적인 예는 두 개의 객체를 통합해서 새로운 객체를 만드는 것이다.</p>
<pre><code>export const mergeObjects =  &lt;T,U&gt;(a: T,b: U): T&amp;U =&gt;({...a,...b})</code></pre><blockquote>
<p>mergeObjects.ts</p>
</blockquote>
<pre><code>import { mergeObjects } from &quot;./mergeObjects&quot;;

type Inameable = {name: string}
type IAgeable = {age: number}

const nameAndAge: Inameable&amp;IAgeable = mergeObjects({name: &#39;jack&#39;},{age: 20})
console.log(nameAndAge)</code></pre><blockquote>
<p>mergeObjects-test.ts</p>
</blockquote>
<p>위 코드를 실행하면 name:&#39;jack&#39; age:20 이 나오게 된다.</p>
<h3 id="합집합-타입-구분">합집합 타입 구분</h3>
<p>다음과 같은 인터페이스가 세개 있다.</p>
<pre><code>interface ISquare {size: number}
interface IRectangle {width: number,height: number}
interface ICircle{radius: number}</code></pre><p>위 인터페이스로 만든 객체는 다음과 같다.</p>
<pre><code>const square: ISquare = {size: 10}
const rectangle: IRectangle = {width: 4, height: 5}
const circle: ICircle = {radius: 10}</code></pre><p>이 객체를 받아서 계산을 수행하는 calcArea 라는 함수를 만든다고 치면</p>
<pre><code>console.log(calcArea(square),calcArea(rectangle),calcArea(circle))</code></pre><p>calcArea 의 매개변수는 여러 타입을 받을 수 있어야한다.</p>
<pre><code>type IShape = ISquare | IRectangle | ICircle
export const calcArea = (shape; IShape): nubmer =&gt;{
return 0
}
</code></pre><p>위와같이 코드를 짜게되면 합집합 타입이여서 모든 타입을 받을 수 있게 되지만
세 타입의 경우 서로 계산방법이 다르기 때문에 문제가 발생한다.
따라서 이를 해결하기 위해 식별 합집합을 이용한다.</p>
<h3 id="식별-합집합">식별 합집합</h3>
<p>식별 합집합 구문을 사용하려면 합집합 타입을 구성하는 인터페이스들이 모두 똑같은 이름의 속성을 가지고 있어야한다.</p>
<pre><code>export interface ISquare {tag: &#39;square&#39;, size: number}
export interface IRectangle {tag: &#39;rectangle&#39;, width: number,height: number}
export interface ICircle{tag: &#39;circle&#39;,radius: number}

export type IShape = ISquare|IRectangle|ICircle</code></pre><blockquote>
<p>IShape.ts</p>
</blockquote>
<pre><code>import { IShape } from &quot;./IShape&quot;;

export const calcArea = (shape: IShape): number =&gt;{
    switch(shape.tag){
        case &#39;square&#39; : return shape.size * shape.size
        case &#39;circle&#39; : return shape.radius * shape.radius * Math.PI
        case &#39;rectangle&#39;: return shape.width * shape.height
    }
    return 0
}</code></pre><blockquote>
<p>calcArea.ts</p>
</blockquote>
<pre><code>import { calcArea } from &quot;./calcArea&quot;;
import { IRectangle,ICircle,ISquare } from &quot;./IShape&quot;; 

const square: ISquare = {tag:&#39;square&#39;,size: 10}
const rectangle: IRectangle =  {tag: &#39;rectangle&#39;, width: 5, height: 4}
const circle: ICircle = {tag: &#39;circle&#39;, radius: 6}

console.log(
    calcArea(square),calcArea(rectangle),calcArea(circle)
)</code></pre><blockquote>
<p>calcArea-test.ts</p>
</blockquote>
<p>이렇게 실행하게 되면 파라미터의 타입이 다르더라도 인식해서 정상적인 결과를 출력하게 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[타입스크립트 기초 - 18]]></title>
            <link>https://velog.io/@stulta_amiko/tsb18</link>
            <guid>https://velog.io/@stulta_amiko/tsb18</guid>
            <pubDate>Fri, 29 Jul 2022 08:15:03 GMT</pubDate>
            <description><![CDATA[<h2 id="제네릭-타입-이해하기">제네릭 타입 이해하기</h2>
<p>제네릭 타입은 인터페이스나 함수 타입 별칭등에 사용할 수 있는 기능이다.
해당 심벌의 타입을 미리 지정하지 않고 다양한 타입에 대응하려고 하는 기능이다.</p>
<h3 id="제네릭-사용하기">제네릭 사용하기</h3>
<p><strong>IValuable.ts</strong></p>
<pre><code>export interface IValuable&lt;T&gt;{
    value: T
}</code></pre><p><strong>index.ts</strong></p>
<pre><code>import { IValuable } from &quot;./IValuable&quot;;

export class Valuable&lt;T&gt; implements IValuable&lt;T&gt;{
    constructor(public value: T) { }
}

export {IValuable}</code></pre><p><strong>printValue.ts</strong></p>
<pre><code>import { Valuable,IValuable } from &quot;./index&quot;;

export const printValue = &lt;T&gt;(o: IValuable&lt;T&gt;): void =&gt; console.log(o.value)
export {Valuable,IValuable}</code></pre><p><strong>value-test.ts</strong></p>
<pre><code>import { printValue,Valuable } from &quot;./printValue&quot;;

printValue(new Valuable&lt;number&gt;(1))
printValue(new Valuable&lt;boolean&gt;(true))
printValue(new Valuable&lt;string&gt;(&#39;hello&#39;))
printValue(new Valuable&lt;number[]&gt;([2,3,4]))</code></pre><blockquote>
<p><strong>실행결과</strong>
1
true
hello
[ 2, 3, 4 ]</p>
</blockquote>
<p>위와 같은 형식으로 제네릭을 사용할 수 있다.</p>
<p>코드가 길어 보여서 뭔가 복잡해보이는데 별거아니다</p>
<p>그리고 타입스크립트는 저런식으로 제네릭안에 타입을 넣지않더라도
타입추론을 통해서 알아서 작동하긴 한다.</p>
<pre><code>import { printValue,Valuable } from &quot;./printValue&quot;;

printValue(new Valuable(1))
printValue(new Valuable(true))
printValue(new Valuable(&#39;hello&#39;))
printValue(new Valuable([2,3,4]))</code></pre><blockquote>
<p><strong>실행결과</strong>
1
true
hello
[ 2, 3, 4 ]</p>
</blockquote>
<hr>
<h3 id="제네릭-타입-예약">제네릭 타입 예약</h3>
<p>제네릭 예약은 타입 변수에 적용할 수 있는 타입의 범위를 한정하는 기능을 한다.
타입스크립트에서 제네릭 함수의 타입을 제한하고 싶을때는 다음과 같은 구문을 사용한다.</p>
<blockquote>
<p>&lt;<U>최종타입1</U> extends <U>타입1</U>, <U>최종타입2</U> extends <U>타입2</U>&gt;(a: <U>최종타입1</U>,b: <U>최종타입</U> 2,...){}</p>
</blockquote>
<p><strong>printValueT.ts</strong></p>
<pre><code>import { IValuable } from &quot;./IValuable&quot;;

export const printValueT = &lt;Q,T extends IValuable&lt;Q&gt;&gt;(o: T) =&gt; console.log(o.value)

export {IValuable}</code></pre><p><strong>printValueT-test.ts</strong></p>
<pre><code>import { printValueT,IValuable } from &quot;./printValueT&quot;;
import { Valuable } from &quot;./printValue&quot;;

printValueT(new Valuable(1))
printValueT({value: true}</code></pre><blockquote>
<p><strong>실행결과</strong>
1
true</p>
</blockquote>
<p>매개변수 타입을 어느 방식으로 제약하느냐만 다르고 사용방법을 동일하다.</p>
<hr>
<h3 id="new-타입제약">new 타입제약</h3>
<pre><code>const create = &lt;T&gt;(type: T): T =&gt; new type()</code></pre><p>위코드는 오류가 발생한다.
타입스크립트에서 타입의 타입을 허용하지 않기 때문이다.</p>
<p>그래서 타입의 타입을 발생시키는것 보다는 다음과 같이 사용할 수 있다.</p>
<pre><code>const create = &lt;T extends {new(): T}&gt;(type: T) =&gt; new type()</code></pre><p>혹은 중괄호를 생략해서 더 간결하게 표현 할 수도 있다.</p>
<pre><code>const create = &lt;T&gt;(type: new() =&gt; T):T =&gt; new type()</code></pre><p>결론적으로 {new(): T}와 new() =&gt; T 는 같다는 의미가 된다.
new 연산자를 type에 적용하면서 type의 생성자 쪽으로 매개변수를 전달해야 할 때 다음처럼 new(...args) 구문을 사용한다.</p>
<pre><code>const create = &lt;T&gt;(type: {new(...args):T},...args): T =&gt; new type(...args)</code></pre><p><strong>create.ts</strong></p>
<pre><code>export const create = &lt;T&gt;(type: {new(...args): T}, ...args):T =&gt; new type(...args)</code></pre><p><strong>create-test.ts</strong></p>
<pre><code>import { create } from &quot;./create&quot;;

class Point {constructor(public x: number, public y: number) { }}
[
    create(Date),
    create(Point,0,0)
].forEach(s=&gt;console.log(s))</code></pre><blockquote>
<p><strong>실행결과</strong>
2022-07-29T07:59:39.557Z
Point { x: 0, y: 0 }</p>
</blockquote>
<p>이런식으로 잘 작동하는 모습을 볼 수 있다.</p>
<hr>
<h3 id="인덱스-타입제약">인덱스 타입제약</h3>
<p>객체의 일정속성만 추려서 더 단순한 객체를 만들려고 할때
다음과 같이 일부속성만 추출해서 간단한 형태로 만들 수 있다.</p>
<pre><code>const obj = {name: &#39;Jane&#39;, age: 22, city: &#39;Seoul&#39;, country: &#39;Korea&#39;}
pick(obj,[&#39;name&#39;,&#39;obj&#39;])</code></pre><p>위 pick 함수는 다음과 같이 구현할 수 있다.</p>
<p>__ pick.ts__</p>
<pre><code>export const pick = (obj,keys) =&gt; keys.map(key =&gt; ({[key]: obj[key]}))
    .reduce((result,value) =&gt; ({...result,...value}), {})</code></pre><p><strong>pick-test.ts</strong></p>
<pre><code>import { pick } from &quot;./pick&quot;;

const obj = {name: &#39;Jane&#39;, age: 22, city: &#39;Seoul&#39;, country: &#39;Korea&#39;}
console.log(
    pick(obj,[&#39;name&#39;,&#39;age&#39;]),
    pick(obj,[&#39;nam&#39;,&#39;agge&#39;])
)</code></pre><blockquote>
<p><strong>실행결과</strong>
{ name: &#39;Jane&#39;, age: 22 } { nam: undefined, agge: undefined }</p>
</blockquote>
<p>이런식으로 key를 잘못입력하는 경우 원하지 않는 값이 나오게 된다.
이런일을 방지하기 위해서 인덱스 타입제약을 사용할 수 있다.</p>
<p>위에서 구현한 pick 함수와 다르게 T와 K라는 타입변수를 적용해서 코드를 짜면
<img src="https://velog.velcdn.com/images/stulta_amiko/post/07b54c2b-9b2e-4493-8787-e35decd03862/image.png" alt=""></p>
<p>다음과 같은 오류가 발생하게 된다.</p>
<p>이 오류메시지를 해결하기 위해서는 K가 T의 key라는것을 알려줘야 한다.
이때 타입스크립트의 인덱스 타입 제약을 사용한다.</p>
<p><img src="https://velog.velcdn.com/images/stulta_amiko/post/cf4f6590-cc49-478c-9cf3-e06e560605aa/image.png" alt=""></p>
<p>오류가 사라진 모습을 볼 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/stulta_amiko/post/a871e58e-23d1-4f68-b4ce-d8ffe9a78b46/image.png" alt="">
그러면 이제 위와 같이 못된 입력을 미리 방지할 수 있게 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[타입스크립트 기초 - 17]]></title>
            <link>https://velog.io/@stulta_amiko/tsb17</link>
            <guid>https://velog.io/@stulta_amiko/tsb17</guid>
            <pubDate>Thu, 28 Jul 2022 13:26:07 GMT</pubDate>
            <description><![CDATA[<h2 id="배열-다루기">배열 다루기</h2>
<h3 id="prepend--append">prepend / append</h3>
<pre><code>import * as R from &#39;ramda&#39;

const array: number[] = [3,4]
const new_arr_p = R.prepend(1)(array)
const new_arr_a = R.append(1)(array)

console.log(new_arr_p)
console.log(new_arr_a)</code></pre><blockquote>
<p><strong>실행결과</strong>
[ 1, 3, 4 ]
[ 3, 4, 1 ]</p>
</blockquote>
<p>prepend는 배열의 앞쪽에 값을 추가하는것이고
append는 배별의 뒤쪽에 값을 추가하는 함수이다.</p>
<hr>
<h3 id="flatten">flatten</h3>
<p>flatten 함수는 복잡한 배열을 평탄화 시켜주는 역할을 한다.</p>
<pre><code>import * as R from &#39;ramda&#39;

const arr = R.range(1,3).map((x: number) =&gt; {
    return R.range(1,3).map((y: number) =&gt; {
        return [x,y]
    })
})

console.log(arr)

const flat_arr = R.flatten(arr)
console.log(flat_arr)</code></pre><blockquote>
<p><strong>실행결과</strong>
[ [ [ 1, 1 ], [ 1, 2 ] ], [ [ 2, 1 ], [ 2, 2 ] ] ] ,
 [
  1, 1, 1, 2,
  2, 1, 2, 2
]</p>
</blockquote>
<p>보이는 대로 복잡한 배열을 저런식으로 평탄화 시킬 수 있다.</p>
<hr>
<h3 id="unnest">unnest</h3>
<p>unnest 함수는 완벽하게 평탄화 시키지는 않고 좀 더 정교한 방식으로 배열을 평탄화 시킨다.</p>
<pre><code>import * as R from &#39;ramda&#39;

const arr = R.range(1,3).map((x: number) =&gt; {
    return R.range(1,3).map((y: number) =&gt; {
        return [x,y]
    })
})

console.log(arr)

const unnest_arr = R.unnest(arr)
console.log(unnest_arr)

//@ts-ignore
const unnest_arr_R = R.pipe(R.unnest,R.unnest)(arr)
console.log(unnest_arr_R)</code></pre><blockquote>
<p><strong>실행결과</strong>
[ [ [ 1, 1 ], [ 1, 2 ] ], [ [ 2, 1 ], [ 2, 2 ] ] ]
[ [ 1, 1 ], [ 1, 2 ], [ 2, 1 ], [ 2, 2 ] ]
[
  1, 1, 1, 2,
  2, 1, 2, 2
]</p>
</blockquote>
<p>위와 같이 좀 더 정교하게 분해하는 모습을 볼 수 있다.</p>
<p>처음에는 한번만 돌리고 두번째는 두번 돌렸을때 결과이다.</p>
<hr>
<h3 id="sort">sort</h3>
<p>sort는 이름에서 알 수 있듯이 배열을 오름차순이나 내림차 순으로 정렬해 주는 함수이다.</p>
<pre><code>import * as R from &#39;ramda&#39;

type voidToNumberFunc = () =&gt; number
const makeRandomNumber = (max: number): voidToNumberFunc =&gt;
    (): number =&gt; Math.floor(Math.random() * max)

const arr = R.range(1,6).map(makeRandomNumber(100))
const sortedArr = R.sort((a: number,b: number): number =&gt; a-b)(arr)

console.log(arr,sortedArr)</code></pre><p>콜백에 마이너스 값이면 오름차순 0이나 플러스값이면 내림차 순이라고 하는데
무슨말인지 잘모르겠다. 이대로 실행하면 오름차순으로 정렬되어 나오긴한다.</p>
<p>좀더 보니깐 b-a로 하면 내림차순 구현이 된다.</p>
<hr>
<h3 id="sortby">sortBy</h3>
<p>배열에 담긴 아이템이 객체라면 특정 속성값에 따라 정렬해야하는데 이때 
sortBy 함수를 사용한다.</p>
<pre><code>import * as R from &#39;ramda&#39;
import { IPerson,makeRandomIPerson } from &#39;./model/person&#39;
import { displayPerson } from &#39;./displayPerson&#39;

const persons: IPerson[] = R.range(1,5).map(makeRandomIPerson)
const nameSortedPersons = R.sortBy(R.prop(&#39;name&#39;))(persons)
const ageSortedPersons = R.sortBy(R.prop(&#39;age&#39;))(persons)

displayPerson(&#39;sorted by name&#39;)(nameSortedPersons)
displayPerson(&#39;sorted by age&#39;)(ageSortedPersons)</code></pre><p><strong>sortby-test.ts</strong></p>
<pre><code>import * as R from &#39;ramda&#39;
import { IPerson } from &#39;./model/person&#39;

export const displayPerson = (prefix: string) =&gt; R.pipe(
    R.map((person: IPerson) =&gt; ({name: person.name,age: person.age})),
    R.tap(o =&gt; console.log(prefix,o))
)as any</code></pre><p><strong>displayPerson.ts</strong></p>
<blockquote>
<p><strong>실행결과</strong>
sorted by name [
  { name: &#39;Alejandro Beck&#39;, age: 41 },
  { name: &#39;Eleanor Colon&#39;, age: 18 },
  { name: &#39;Genevieve Bishop&#39;, age: 64 },
  { name: &#39;Troy Ward&#39;, age: 42 }
]
sorted by age [
  { name: &#39;Eleanor Colon&#39;, age: 18 },
  { name: &#39;Alejandro Beck&#39;, age: 41 },
  { name: &#39;Troy Ward&#39;, age: 42 },
  { name: &#39;Genevieve Bishop&#39;, age: 64 }
]</p>
</blockquote>
<hr>
<h3 id="sortwith">sortWith</h3>
<p>sortBy는 항상 오름차순으로만 정렬한다.
sortWith 함수는 ascend,descend를 이용해서 오름차순과 내림차순 정렬을 할 수있다.</p>
<pre><code>import * as R from &#39;ramda&#39;
import { IPerson,makeRandomIPerson } from &#39;./model/person&#39;
import { displayPerson } from &#39;./displayPerson&#39;

const persons: IPerson[] = R.range(1,5).map(makeRandomIPerson)
//@ts-ignore
const nameSortedPerson = R.sortWith([R.descend(R.prop((&#39;name&#39;)))])(persons)

displayPerson(&#39;sorted by name&#39;)(nameSortedPerson)</code></pre><p>람다 라이브러리 자체가 js기반이라 그런지는 모르겠는데
알 수 없는 오류가 자꾸 발생한다.
너무 화난다.
이런식으로 하면 내림차순으로 구현이된다.
애초에 뭐 공식사이트에 있는 예제를 박아도 똑같은 오류가 난다</p>
<h2 id="조합-논리-이해하기">조합 논리 이해하기</h2>
<h3 id="chain-조합자">chain 조합자</h3>
<p>chain 조합자는 함수를 매개변수로 받아 동작하는 함수로
매개변수가 한개일때와 두개일때의 동작이 다르다.</p>
<pre><code>import * as R from &#39;ramda&#39;

const array = [1,2,3]

R.pipe(
    R.chain(n =&gt; [n,n]),
    R.tap(n =&gt; console.log(n))
)(array)

R.pipe(
    R.chain(R.append,R.head),
    R.tap(n =&gt; console.log(n))
)(array)</code></pre><blockquote>
<p><strong>실행결과</strong>
[ 1, 1, 2, 2, 3, 3 ]
[ 1, 2, 3, 1 ]</p>
</blockquote>
<p>둘이 보면 실행결과가 다른데</p>
<p>매개변수가 한개일때는 아래 코드와 같이 동작한다.</p>
<pre><code>import * as R from &#39;ramda&#39;

const array = [1,2,3]

const flatMap = (f) =&gt; R.pipe(
    R.map(f),
    R.flatten
)

R.pipe(
    flatMap(n =&gt; [n,n]),
    R.tap(n =&gt; console.log(n))
)(array)
</code></pre><blockquote>
<p><strong>실행결과</strong>
[ 1, 1, 2, 2, 3, 3 ]</p>
</blockquote>
<p>그리고 매개변수가 두개일때는 아래와 같이 동작한다.</p>
<pre><code>import * as R from &#39;ramda&#39;

const array = [1,2,3]

const chainTwoFunc = (firstFn,SecondFn) =&gt; (x) =&gt; firstFn(SecondFn(x),x)

R.pipe(
    chainTwoFunc(R.append,R.head),
    R.tap(n =&gt; console.log(n))
)(array)</code></pre><blockquote>
<p><strong>실행결과</strong>
[ 1, 2, 3, 1 ]</p>
</blockquote>
<hr>
<h3 id="flip-조합자">flip 조합자</h3>
<p>flip 함수는 2차 고차 함수의 매개변수 순서를 서로 바꿔주는 역할을 한다.</p>
<blockquote>
<p>const flip = cb =&gt; a =&gt; b =&gt; cb(b)(a)</p>
</blockquote>
<pre><code>import * as R from &#39;ramda&#39;

const flip = cb =&gt; a =&gt; b =&gt; cb(b)(a)
const reverseSubtract = flip(R.subtract)

const new_arr = R.pipe(
    R.map(reverseSubtract(10)),
    R.tap(n =&gt; console.log(n))
)(R.range(1,10))</code></pre><p>flip을 풀어서 구현했다.</p>
<hr>
<h3 id="identity-조합자">identity 조합자</h3>
<p>identity는 엄청 단순하게 생겼다.</p>
<blockquote>
<p>const identity = x =&gt; x</p>
</blockquote>
<p>구조상 함수가 꼭 필요한 자리에 넣어줄 수 있는 조합자이다.</p>
<pre><code>import * as R from &#39;ramda&#39;
import { flatMap } from &#39;./flatmap&#39;


const unnest = flatMap(R.identity)

const arr = [[1],[2],[3]]
R.pipe(
    unnest,
    R.tap(n =&gt; console.log(n))
)(arr)</code></pre><p>flatMap 함수가 요구하는 콜백함수 자리에 identity를 넣은 모습을 볼 수 있다.</p>
<hr>
<h3 id="always-조합자">always 조합자</h3>
<p>always 조합자는 두개의 고차함수 파라미터중 첫번째 파라미터를 반환한다.</p>
<blockquote>
<p>const always = x =&gt; y =&gt; x</p>
</blockquote>
<pre><code>import * as R from &#39;ramda&#39;

const always = a =&gt; b =&gt; a
const flip = cb =&gt; a =&gt; b =&gt; cb(b)(a)

const first = &lt;T&gt;(a: T) =&gt; (b: T): T =&gt; always(a)(b)
const second = &lt;T&gt;(a: T) =&gt; (b: T): T =&gt; flip(always)(a)(b)

console.log(
    first(1)(2),
    second(1)(2)
)</code></pre><blockquote>
<p><strong>실행결과</strong>
1 2</p>
</blockquote>
<p>flip은 파라미터의 순서를 바꿔준다
따라서 위와같은 실행결과가 나오게된다.</p>
<hr>
<h3 id="applyto-조합자">applyTo 조합자</h3>
<p>applyTo 조합자는 특별하게 값을 첫 번째 매개변수로 이값을 입력으로 하는 콜백 함수를 두 번째 매개변수로 받아 다음 코드처럼 동작한다.</p>
<p>뭐라는거지</p>
<blockquote>
<p>const applyTo = value =&gt; cb =&gt; cb(value)</p>
</blockquote>
<p>위와같은 방식으로 작용한다고 한다.</p>
<pre><code>import * as R from &#39;ramda&#39;

const T = value =&gt; R.pipe(
    R.applyTo(value),
    R.tap(value =&gt; console.log(value))
)

const value100 = T(100)
const sameValue = value100(R.identity)
const add1Value = value100(R.add(1))</code></pre><blockquote>
<p><strong>실행결과</strong>
100 101</p>
</blockquote>
<p>위와같은 방식으로 동작한다.</p>
<hr>
<h3 id="ap-조합자">ap 조합자</h3>
<p>ap 조합자는 콜백 함수들의 배열을 첫 번째 매개변수로 배열을 두번째 매개변수로 입력받는 2차 고차 함수이다.</p>
<blockquote>
<p>const ap = ([callback]) =&gt; arr =&gt; [callback]&amp;nbsp(arr)</p>
</blockquote>
<p>ap 에 콜백함수가 하나일때는 map 처럼 동작한다.</p>
<pre><code>import * as R from &#39;ramda&#39;

const callAndAppend = R.pipe(
    R.ap([R.multiply(2)]),
    R.tap(n =&gt; console.log(n))
)

const input = [1,2,3]
const result = callAndAppend(input)</code></pre><blockquote>
<p><strong>실행결과</strong>
[2,4,6]</p>
</blockquote>
<p>하지만 콜백이 두개일때는
R.chain(n =&gt;[n,n]) 처럼 동작한다.
두 개일때 두 콜백 함수를 적용한 각각의 배열을 만든 다음에 연산이 끝나면 배열을 모두 통합해서 하나로 만들어준다.</p>
<pre><code>import * as R from &#39;ramda&#39;

const callAndAppend = R.pipe(
    R.ap([R.multiply(2),R.add(10)]),
    R.tap(a =&gt; console.log(a))
)

const input = [1,2,3]
const result =  callAndAppend(input)</code></pre><blockquote>
<p><strong>실행결과</strong>
[2,4,6,11,12,13]</p>
</blockquote>
<p>위 코드가 그 예시이다.</p>
<p>처음에 2를 곱해준값을 가지고 그다음에 원본 배열에 10을 더한 값을 가지는것을 볼 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[타입스크립트 기초 - 16]]></title>
            <link>https://velog.io/@stulta_amiko/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EA%B8%B0%EC%B4%88-16</link>
            <guid>https://velog.io/@stulta_amiko/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EA%B8%B0%EC%B4%88-16</guid>
            <pubDate>Tue, 26 Jul 2022 10:50:48 GMT</pubDate>
            <description><![CDATA[<h2 id="렌즈를-활용하여-객체의-속성-다루기">렌즈를 활용하여 객체의 속성 다루기</h2>
<p>렌즈는 하스켈에서 Control.Lens 라이브러리 내용중 자바스크립트에서 동작할 수 있는 게터와 세터 기능만을 람다함수로 구현한 것이다.</p>
<h3 id="propassoc">prop/assoc</h3>
<p>렌즈 기능을 이해하기 위해서는 prop 함수와 assoc 함수를 알아야한다.
prop 은 property 의 줄임말이고 객체의 특정 속성값을 가져오는 함수로서 게터의 역할과 비슷하다.
assoc 함수는 세터와 비슷한 기능을 한다.</p>
<pre><code>import * as R from &#39;ramda&#39;
import { IPerson,makeRandomIPerson } from &#39;./model/person&#39;

const person:IPerson = makeRandomIPerson()

const name = R.pipe(
    R.prop(&#39;name&#39;),
    R.tap(name =&gt; console.log(name))
)(person)</code></pre><p>prop 함수를 사용하는 예제를 먼저 보면 prop은 게터와 같은 역할을 한다고 했기 때문에 위와같이 IPerson 타입에서 &#39;name&#39;에 해당하는 부분만 가져오는 모습을 볼 수 있다.</p>
<pre><code>import * as R from &#39;ramda&#39;
import { IPerson,makeRandomIPerson } from &#39;./model/person&#39;

const getName = R.pipe(R.prop(&#39;name&#39;),R.tap(name =&gt; console.log(name)))

const person: IPerson = makeRandomIPerson()
const originalName = getName(person)

const modifiedPerson = R.assoc(&#39;name&#39;, &#39;Albert Einstein&#39;)(person)
const modifiedName = getName(modifiedPerson)</code></pre><p>위와같은 방법으로 assoc함수를 사용할 수 있다. 첫번째 파라미터로 대응되는 key를 넣어주고 두번째 값으로 value를 넣어주는것 세터와 거의 다른게 없다 </p>
<hr>
<h3 id="lens">lens</h3>
<p>렌즈 기능을 사용하려면 렌즈를 만들어야하는데 렌즈는 다음처럼
R.lens,R.prop,R.assoc 의 조합으로 만들 수 있다</p>
<hr>
<h3 id="viewsetover">view,set,over</h3>
<pre><code>import * as R from &#39;ramda&#39;

export const makeLens = (propName: string) =&gt; 
    R.lens(R.prop(propName),R.assoc(propName))

export const getter = (lens) =&gt; R.view(lens)
export const setter = (lens) =&gt; &lt;T&gt;(newValue: T) =&gt; R.set(lens, newValue)
export const setterUsingFunc = (lens) =&gt; &lt;T,R&gt;(func: (T) =&gt; R) =&gt; R.over(lens,func)</code></pre><p>위와 같은 형식으로 먼저 렌즈를 만들고 게터 셋터 그리고 세터를 이용하는 함수를 만들어서 먼저 모듈화 시켜놓는다.</p>
<pre><code>import * as R from &#39;ramda&#39;
import { makeLens,getter,setter,setterUsingFunc } from &#39;./lens&#39;
import { IPerson,makeRandomIPerson } from &#39;./model/person&#39;

const nameLens = makeLens(&#39;name&#39;)
const getName = getter(nameLens)
const setName = setter(nameLens)
const setNameUsingFunc = setterUsingFunc(nameLens)

const person: IPerson = makeRandomIPerson()

const name = getName(person)
const newPerson = setName(&#39;Albert Einstein&#39;)(person)
const anotherPerson = setNameUsingFunc(name =&gt; `&#39;Mr. ${name}&#39;`)(person)
const capitalPerson = setNameUsingFunc(R.toUpper)(person)

console.log(
    name,getName(newPerson),getName(anotherPerson),getName(capitalPerson),
)</code></pre><p>그리고 위 함수를 이용하기 위해서 무작위로 이름을 생성한다.</p>
<blockquote>
<p><strong>실행결과</strong>
Jordan Fox Albert Einstein &#39;Mr. Jordan Fox&#39; JORDAN FOX</p>
</blockquote>
<p>이름은 랜덤 생성된것이므로 다를 수 있다.</p>
<p>일단 코드를 먼저보면 nameLens에서 렌즈를 만드는 모습을 볼 수 있다.
그러고서 게터 세터와 세터를 이용하는 함수를 만든다.</p>
<p>사용할때는 일단 기본적으로 처음사용한게 게터
그다음이 세터로 이름을 바꾸는 작업을 하는 모습을 볼 수 있다.
후에는 세터를 이용하는 함수를 이용해서 이름에 Mr을 붙이는것을 볼 수 있고
그다음에는 전부 대문자로 만드는 모습을 볼 수 있다.
코드만 봐도 대충 뭘 하는지 쉽게 알 수 있다.</p>
<hr>
<h3 id="lenspath">lensPath</h3>
<p>IPerson 객체의 longitde 값을 알려면</p>
<p>person.location.coordinates.longitude 와 같이 긴 코드를 작성해야하는 불편한 상황이 발생하게 된다.
이런 긴 경로의 속성을 렌즈로 만들려면 lensPath를 사용한다.</p>
<pre><code>import * as R from &#39;ramda&#39;
import { makeLens,getter,setter,setterUsingFunc } from &#39;./lens&#39;
import { IPerson,makeRandomIPerson } from &#39;./model/person&#39;

const longitudeLens =R.lensPath([&#39;location&#39;,&#39;coordinates&#39;,&#39;longitude&#39;])
const getLongitude = getter(longitudeLens)
const setLongitude = setter(longitudeLens)
const setLongitudeUsingFunc = setterUsingFunc(longitudeLens)

const person: IPerson = makeRandomIPerson()

const longitude = getLongitude(person)
const newPerson = setLongitude(0.123456)(person)
const anothrPerson = setLongitudeUsingFunc(R.add(0.1234567))(person)

console.log(
    longitude,getLongitude(newPerson),getLongitude(anothrPerson)
)</code></pre><p>위와같은 방식으로 longitude만 값을 바꾸거나 수정하는 코드를 만들었다.
lensPath를 이용해서 만드는 모습을 볼 수 있다.</p>
<h2 id="객체-다루기">객체 다루기</h2>
<h3 id="topairs--frompairs">toPairs / fromPairs</h3>
<pre><code>import * as R from &#39;ramda&#39;
import { IPerson,makeRandomIPerson } from &#39;./model/person&#39;

const person: IPerson = makeRandomIPerson()
const pairs: [string,any][] = R.toPairs(person)
console.log(&#39;pairs&#39;,pairs)</code></pre><p>먼저 toPairs함수는 객체의 속성을 분해해 배열로 만들어준다.</p>
<p>위 코드의 실행결과를 보면</p>
<blockquote>
<p>pairs [
  [ &#39;name&#39;, &#39;Sarah Robinson&#39; ],
  [ &#39;age&#39;, 29 ],
  [ &#39;title&#39;, &#39;Education Adminator&#39; ],
  [
    &#39;location&#39;,
    {
      country: &#39;HU&#39;,
      city: &#39;Tuobdov&#39;,
      address: &#39;1224 Warku Ridge&#39;,
      coordinates: [Object]
    }
  ]
]</p>
</blockquote>
<p>위와같은 결과를 내게 되는데 객체에있는 키와 밸류가
[key,value] 형태의 배열로 만들어 진것을 확인할 수 있다.</p>
<p>다음은 fromPairs함수이다.
fromPairs 함수는 toPairs와 반대로 [key:value] 형태의 배열을 다시 객체로 만들어준다.</p>
<pre><code>import * as R from &#39;ramda&#39;
import { IPerson,makeRandomIPerson } from &#39;./model/person&#39;

const pairs: [string,any][] = R.toPairs(makeRandomIPerson())
const person: IPerson = R.fromPairs(pairs) as IPerson
console.log(person)</code></pre><blockquote>
<p>{
  name: &#39;Tommy Fletcher&#39;,
  age: 40,
  title: &#39;Organizational Development Manager&#39;,
  location: {
    country: &#39;AW&#39;,
    city: &#39;Noegoco&#39;,
    address: &#39;248 Vuso Highway&#39;,
    coordinates: { latitude: -6.43962, longitude: -100.80624 }
  }
}</p>
</blockquote>
<p>pairs에서 객체의 값을 가지고 있던 배열을 다시 객체로 만드는 모습을 볼 수 있다.</p>
<hr>
<h3 id="keys--values">keys / values</h3>
<p>먼저 keys 함수부터 살펴보면
이름만 들어도 직감할 수 있듯이 key값만 가져오는 함수임을 알 수 있다.</p>
<pre><code>import * as R from &#39;ramda&#39;
import { makeRandomIPerson } from &#39;./model/person&#39;

const keys: string[] = R.keys(makeRandomIPerson())
console.log(&#39;keys&#39;,keys)</code></pre><blockquote>
<p><strong>실행결과</strong>
keys [ &#39;name&#39;, &#39;age&#39;, &#39;title&#39;, &#39;location&#39; ]</p>
</blockquote>
<p>IPerson의 key를 전부 가져오는것을 알 수 있다.
key값은 전부 string이기 때문에 타입도 string 배열인 것을 알 수 있다.</p>
<p>다음은 values 함수이다.</p>
<pre><code>import * as R from &#39;ramda&#39;
import { makeRandomIPerson } from &#39;./model/person&#39;

const values: any[] = R.values(makeRandomIPerson())
console.log(&#39;values&#39;,values)</code></pre><blockquote>
<p><strong>실행결과</strong>
values [
  &#39;Ray Robertson&#39;,
  45,
  &#39;Professional Athlete&#39;,
  {
    country: &#39;IN&#39;,
    city: &#39;Ocalium&#39;,
    address: &#39;111 Ovouc Mill&#39;,
    coordinates: { latitude: -26.31672, longitude: -154.33124 }
  }
]</p>
</blockquote>
<p>key가 전부 string인것에 반하여 value는 여러 타입을 가질 수 있기때문에 any 타입 배열로 만들었다.</p>
<p>보면 key값은 제외하고 value값만 긁어오는 모습을 볼 수 있다.</p>
<hr>
<h3 id="zipobj">zipObj</h3>
<p>zipObj 함수는 키배열과 값배열을 결합해서 객체로 만들어준다.</p>
<pre><code>import * as R from &#39;ramda&#39;
import { makeRandomIPerson,IPerson } from &#39;./model/person&#39;

const person: IPerson = makeRandomIPerson()

const key: string[] = R.keys(person)
const values: any[] = R.values(person)

const zipped: IPerson = R.zipObj(key,values) as IPerson
console.log(zipped)</code></pre><blockquote>
<p><strong>실행결과</strong>
{
  name: &#39;Nathan Hogan&#39;,
  age: 56,
  title: &#39;Electro Optical Engineer&#39;,
  location: {
    country: &#39;AS&#39;,
    city: &#39;Reszipvih&#39;,
    address: &#39;197 Hawe Highway&#39;,
    coordinates: { latitude: -41.35164, longitude: -126.92718 }
  }
}</p>
</blockquote>
<p>위와같이 그냥 첫번째 파라미터로 key를 넣고 두번째 파라미터로 value를 넣는것을 볼 수 있다.</p>
<hr>
<h3 id="mergeleft--mergeright">mergeLeft / mergeRight</h3>
<p>mergeLeft는 왼쪽객체의 우선순위가 높은 함수이고
Right는 반대로 작용한다
두개의 객체를 입력받아 속성을 결합한다.</p>
<pre><code>import * as R from &#39;ramda&#39;

const left = {name: &#39;jack&#39;}, right = {name: &#39;jane&#39;,age: 33}
const person_L = R.mergeLeft(left,right)
const person_R = R.mergeRight(left,right)

console.log(person_L)
console.log(person_R)</code></pre><blockquote>
<p><strong>실행결과</strong>
{ name: &#39;jack&#39;, age: 33 }
{ name: &#39;jane&#39;, age: 33 }</p>
</blockquote>
<p>위 코드의 실행결괄를 보면 알 수 있듯이 mergeLeft 일때는 왼쪽에 있는 값이 우선순위가 높아서 이름이 jack이 되는것을 볼 수 있고
반대로 mergeRight일때는 우측에 있는 객체가 우선순위가 높아서 jane으로 바뀌는 것을 볼 수 있다.</p>
<hr>
<h3 id="mergedeepleft--mergedeepright">mergeDeepLeft / mergeDeepRight</h3>
<p>바로 위에서 본 mergeLeft나 mergeRight의 경우 객체의 속성에 담긴 객체를 바꾸지는 못한다.
IPerson의 속성값들만 바꿔줄 뿐 location이나 location.coordinates의 속성값을 바꾸지는 못한다.</p>
<p>여기서 mergeDeepLeft 혹은 mergeDeepRight를 이용하게 되면 경로의 속성값도 바꿔줄 수 있다.</p>
<pre><code>import * as R from &#39;ramda&#39;
import { makeRandomIPerson,IPerson } from &#39;./model/person&#39;
import { makeRandomICoordinates,ICoordinates } from &#39;./model/coordinates&#39;
import { makeRandomILocation,ILocation } from &#39;./model/location&#39;

const person: IPerson = makeRandomIPerson()
const location: ILocation = makeRandomILocation()
const coordinates: ICoordinates = makeRandomICoordinates()

const newLocation = R.mergeDeepRight(location,{coordinates})
const newPerson = R.mergeDeepRight(person,{location: newLocation})

console.log(&#39;person&#39;,person)
console.log(&#39;new Person&#39;,newPerson)</code></pre><blockquote>
<p><strong>실행결과</strong>
person {
  name: &#39;Fannie Burke&#39;,
  age: 49,
  title: &#39;Commercial Artist&#39;,
  location: {
    country: &#39;UG&#39;,
    city: &#39;Harfifvuc&#39;,
    address: &#39;1224 Veno Plaza&#39;,
    coordinates: { latitude: -85.21719, longitude: 32.52957 }
  }
}
new Person {
  name: &#39;Fannie Burke&#39;,
  age: 49,
  title: &#39;Commercial Artist&#39;,
  location: {
    country: &#39;DM&#39;,
    city: &#39;Kimassez&#39;,
    address: &#39;1509 Ogeri Terrace&#39;,
    coordinates: { latitude: -60.08834, longitude: 44.6996 }
  }
}</p>
</blockquote>
<p>deep을 사용하지 않으면 접근할 수 없는 객체 내부에 객체 내부에 객체까지 접근해서 바꾸는 못브을 볼 수 있다.</p>
<p>위는 원래 객체고 아래가 변경된 객체이다 location과 coordinates까지
바뀐 모습을 볼 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[타입스크립트 기초 - 15]]></title>
            <link>https://velog.io/@stulta_amiko/tsb15</link>
            <guid>https://velog.io/@stulta_amiko/tsb15</guid>
            <pubDate>Mon, 25 Jul 2022 07:58:55 GMT</pubDate>
            <description><![CDATA[<h3 id="서술자와-조건-연산">서술자와 조건 연산</h3>
<p>함수형 프로그래밍에서 boolean 타입 값을 반환해 어떤 조건을 만족하는지를 판단하는 함수를 서술자라고 한다.</p>
<p>람다 라이브러리 내부에는 수를 비교해 boolean 타입으로 반환하는 서술자들을 제공한다.</p>
<pre><code>import * as R from &#39;ramda&#39;

R.lt(a)(b): boolean // a &lt; b 이면 true
R.lte(a)(b): boolean // a &lt;= b 이면 true
R.gt(a)(b): boolean // a &gt; b 이면 true
R.gte(a)(b): boolean // a &gt;= b 이면 true</code></pre><p>수의 크기를 판단하는 서술자는 위와 같은 형식으로 제공한다.
filter 함수와 결합해서 사용하는 예제코드를 보면 다음과 같다.</p>
<pre><code>import * as R from &#39;ramda&#39;

R.pipe(
    R.filter(R.lte(3)),
    R.tap(n =&gt; console.log(n))
)(R.range(1,11))</code></pre><p>위 코드를 읽어보면 알겠지만
결과는 3부터 10까지 출력하게 된다.
필터에서 true인 결과만 저장하게 되니깐 3보다 작을땐 전부 false를 출력하게 될것이고 이에따라 3이상의 수만 저장되게 된다.</p>
<pre><code>import * as R from &#39;ramda&#39;

R.pipe(
    R.filter(R.lte(3)),
    R.filter(R.gt(7)),
    R.tap(n =&gt; console.log(n))
)(R.range(1,11))</code></pre><p>위 코드는 3 &lt;= x &lt; 7을 구현한 코드이다.
위 코드를 출력하게되면 3보다 크고 7보다 작은 수가 출력되게 될것이다.</p>
<hr>
<p><strong>allPass/anyPass</strong></p>
<blockquote>
<p>allPass(서술자) // 배열의 조건을 모두 만족하면 true
anyPass(서술자) // 배열의 조건을 하나라도 만족하면 true</p>
</blockquote>
<pre><code>import * as R from &#39;ramda&#39;

type NumberToBooleanFunc = (n: number) =&gt; boolean
const selectRange = (min: number,max: number): NumberToBooleanFunc =&gt;
    R.allPass(
        [R.lte(min),
        R.gt(max)]
    )

R.pipe(
    R.filter(selectRange(3,9)),
    R.tap(n =&gt; console.log(n))
)(R.range(1,11))</code></pre><p>위 코드는 allPass를 응용해서 3 &lt;= x &lt; 9를 구현한 모습이다.</p>
<hr>
<p><strong>not / ifElse</strong></p>
<p>not 함수는 true이면 false를 반환하고 false이면 true를 반환하는 함수이다.
!연산자와 유사한 느낌인거같다.
위에서 구현한 selectRange 함수와 반대되는 함수를 쉽게 구현할 수 있다.</p>
<pre><code>import * as R from &#39;ramda&#39;

type NumberToBooleanFunc = (n: number) =&gt; boolean
const selectRange = (min: number,max: number): NumberToBooleanFunc =&gt;
    R.allPass(
        [R.lte(min),
        R.gt(max)]
    )

const notRange = (min: number,max: number) =&gt; R.pipe(selectRange(min,max),R.not)
R.pipe(
    R.filter(notRange(3,9)),
    R.tap(n =&gt; console.log(n))
)(R.range(1,11))</code></pre><p>기존의 selectRange를 이용하면서 이런식으로 구현이 가능하다.
위 함수는 1부터 10까지 기존의 selectRange(3,9)에 해당되었던 값의 정반대인 값을 출력한다.
기존이 다음과 같은 식을 구현했다면 3 &lt;= x &lt; 9
위에서 구현한 코드는 x &lt; 3 and x &gt;= 9 
range 배열 내에서 저 조건문에 맞는 수를 남기게 된다.</p>
<p>ifElse는 세가지 매개변수를 포함한다.</p>
<blockquote>
<p>R.ifElse(
    <em>조건서술자</em>,
    <em>true일때 실행하는 함수</em>,
    <em>false일때 실행하는 함수</em>
    )</p>
</blockquote>
<p>위와 같은 형식으로 구성된다.</p>
<pre><code>import * as R from &#39;ramda&#39;

const input: number[] = R.range(1,11), halfValue = input[input.length/2]

const suborAdd = R.pipe(
    R.map(R.ifElse(
        R.lte(halfValue),
        R.inc,
        R.dec
    )),
    R.tap(n =&gt; console.log(n))
)

const result = suborAdd(input)</code></pre><p>ifElse를 응용하는 코드로 하프밸류보다 작으면 1씩 빼고
하프밸류보다 크면 1씩 추가하는 코드를 가져왔다.
첫번째 파라미터의 값에 부합하는 애들은 1씩 빼주고 값에 부합하지 않는애들은 1씩 더해주는 코드이다.
이처럼 간결하게 작업을 수행할 수 있는 함수들이 람다라이브러리에 존재한다.</p>
<h3 id="문자열-다루기">문자열 다루기</h3>
<p><strong>trim</strong>
trim은 문자열 앞뒤의 공백을 제거해준다.</p>
<pre><code>import * as R from &#39;ramda&#39;

console.log(
    R.trim(&#39;\t hello \t&#39;)
)</code></pre><hr>
<p><strong>toLower / toUpper</strong></p>
<p>이름만봐도 알 수 있듯이 대문자로 혹은 소문자로 바꿔주는 함수이다.</p>
<pre><code>import * as R from &#39;ramda&#39;

console.log(
    R.toUpper(&#39;hello&#39;),
    R.toLower(&#39;HELLO&#39;)
)</code></pre><hr>
<h4 id="구분자를-사용해-문자열을-배열로-변환">구분자를 사용해 문자열을 배열로 변환</h4>
<p>R.split 함수는 구분자를 사용해서 문자열을 배열로 바꿔준다.
R.join함수는 문자열 배열을 문자열로 바꿔준다.</p>
<pre><code>import * as R from &#39;ramda&#39;

const words: string[] = R.split(&#39; &#39;)(`Hello World!, I&#39;m Peter`)
console.log(words)</code></pre><p>위 코드를 출력하면 문자열을 배열로 만들어준다. 구분자는 &#39; &#39; 이다.</p>
<blockquote>
<p><strong>실행결과</strong>
[ &#39;Hello&#39;, &#39;World!,&#39;, &quot;I&#39;m&quot;, &#39;Peter&#39; ]</p>
</blockquote>
<hr>
<h4 id="tocamelcase-함수-만들기">toCamelCase 함수 만들기</h4>
<pre><code>import * as R from &#39;ramda&#39;

type StringToStringFunc = (string) =&gt; string

const toCamelCase = (delim: string): StringToStringFunc =&gt;{
    const makeFirstToCapital = (word: string) =&gt;{
        const characters = word.split(&#39;&#39;)
        return characters.map((c,index) =&gt; index == 0 ? c.toUpperCase() : c).join(&#39;&#39;)
    }

    const indexedMap = R.addIndex(R.map)
    return R.pipe(
        R.trim,
        R.split(delim),
        R.map(R.toLower),
        indexedMap((value: string,index: number) =&gt; index &gt; 0 ? makeFirstToCapital(value):value
        ),
        //@ts-ignore
        R.join(&#39;&#39;)
    ) as StringToStringFunc
}

console.log(
    toCamelCase(&#39; &#39;)(&#39;Hello World&#39;),
    toCamelCase(&#39;_&#39;)(&#39;Hello_Albert&#39;)
)</code></pre><p>위와같은 방식으로 만든다. CamelCase가 뭔가하면 이제 이름을 지을때 규칙같은것이다.
첫문자는 소문자로 그리고 띄워쓰기를 할 공간에 대문자 문자를 넣는것이다.
위 코드는 다음에 한번 더 해석해보는걸로 해야겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[타입스크립트 복습 - 3]]></title>
            <link>https://velog.io/@stulta_amiko/tsr3</link>
            <guid>https://velog.io/@stulta_amiko/tsr3</guid>
            <pubDate>Thu, 21 Jul 2022 10:58:06 GMT</pubDate>
            <description><![CDATA[<h3 id="promise--async--await">Promise / async / await</h3>
<p>먼저 Promise에 대해서 보면
MDN에서는 프로미스를 다음과 같이 설명하고있다.</p>
<blockquote>
<p>Promise 객체는 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타냅니다.</p>
</blockquote>
<p>프로미스는 resolve와 reject를 반환한다.</p>
<p>먼저 프로미스를 사용하는 이유에 대해 알려면 비동기식 코드와 동기식 코드에 대해서 알아야하는데
먼저 동기식으로 작동하게 되면 단일스레드로 작동하는 방식이라 어느 한작업이 끝날때까지 다른작업을 실행하지 않게된다.
하지만 이런방식으로 실행되게 된다면 작업을 지연시키는 주요원인이 될것이다.</p>
<p>따라서 현재 웹의 대부분은 비동기식 코드를 이용하고 있다.
비동기식 코드는 다중스레드처럼 동작하게 만들어준다. 따라서 효율적인 작업수행이 가능해진다.</p>
<pre><code>readFile(&#39;./package.json&#39;,(err: Error,buffer: Buffer)=&gt;{
    console.log(&#39;read package.json using asynchronous api&#39;)
    console.log(buffer.toString())
})</code></pre><p>위는 비동기식 코드를 구현한것이다. package.json내용을 받아오는 코드이고 콜백을 받는데 첫번째로 error를 두번째로 버퍼를 받는다. 그리고 버퍼를 string화 시켜서 출력시킨다.
이런방식이 효율적인 작업수행을 하는데 도움이 되긴하지만 이런식으로 콜백을 계속 엮게된다면 콜백지옥이라는것이 발생하게 된다.
비동기코드를 구현하려고 콜백안에 콜백을 또 그안에 콜백을 계속반복하게 되는 코드를 말하는 것이다. 이런 단점을 보완하기 위해서 존재하는게 바로 Promise이다.</p>
<pre><code>const readFilePromise = (filename: string): Promise&lt;string&gt; =&gt; 
    new Promise&lt;string&gt;((resolve,reject)=&gt;{
        readFile(filename,(err: Error,buffer: Buffer) =&gt; {
            if(err)
                reject(err)
            else 
                resolve(buffer.toString())
        })
    });</code></pre><p>위와 동일하게 파일을 읽는 코드를 작성하되 프로미스를 이용해서 작성을 해봤다.</p>
<p>프로미스는 일종의 클래스 이기때문에 new 키워드를 이용한다. 그리고 resolve와 reject를 콜백으로 받는다.</p>
<p>readfile을 했을때 오류가 나면 reject를 반환하고 정상적으로 실행이 되었을 때에는 resolve를 반환하게 된다.</p>
<pre><code>readFilePromise(&#39;./package.json&#39;)
    .then((content: string)=&gt;{
        console.log(content)
        return readFilePromise(&#39;./tsconfig.json&#39;)
    })
    .then((content: string)=&gt;{
        console.log(content)
        return readFilePromise(&#39;.&#39;)
    })
    .catch((err: Error)=&gt;console.log(&#39;err:&#39;,err.message))
    .finally(()=&gt;&#39;end&#39;)</code></pre><p>위 프로미스 코드를 이용하면 먼저 package.json을 읽어드린후에 버퍼를 tostring 한것을 출력한다 그다음에 tsconfig.json을 읽은후에 이를 출력하고 .을 읽으려고 하면 오류가 발생하니 catch로 오류메시지를 출력한 후에 finally 메서드로 프로그램을 종료하는 코드이다.</p>
<hr>
<p><strong>async</strong>,<strong>await</strong></p>
<blockquote>
<p>async function 선언은 AsyncFunction객체를 반환하는 하나의 비동기 함수를 정의합니다. 비동기 함수는 이벤트 루프를 통해 비동기적으로 작동하는 함수로, 암시적으로 Promise를 사용하여 결과를 반환합니다. 그러나 비동기 함수를 사용하는 코드의 구문과 구조는, 표준 동기 함수를 사용하는것과 많이 비슷합니다.</p>
</blockquote>
<p>MDN의 설명은 위와같다.</p>
<pre><code>const test1 = async() =&gt;{
    let value = await 1
    console.log(value)
    value = await Promise.resolve(1)
    console.log(value)
}

const test2 = async() =&gt;{
    let value = await &#39;hello&#39;
    console.log(value)
    value = await Promise.resolve(&#39;hello&#39;)
    console.log(value)
}

test1()
test2()</code></pre><p>위 코드를 실행하게 되면 1 1 hello hello가 아닌 1 hello 1 hello가 나오게 된다.</p>
<p>이를 의도한 대로 1 1 hello hello가 나오게 하려면 코드를 살짝 수정해주면 된다.</p>
<pre><code>test1().then(()=&gt;test2())</code></pre><p>마지막줄을 위와같은 형식으로 수정해주면되는데 test1함수가 끝난 후에 test2 함수를 실행하기 때문에 의도한 대로 1 1 hello hello가 나오게된다.</p>
<p>그리고 await은 async 안에서만 사용할 수 있다.
await은 프로미스를 기다리기 위해서 사용하는 연산자이다.</p>
<p>async는 봐도봐도 잘 모르겠다 하지만 이는 다음코드와 같다고 한다.</p>
<pre><code>async function foo() {
    return 1
}</code></pre><p>async로 작성된 foo 함수가 있다.</p>
<pre><code>function foo() {
    return Promise.resolve(1)
}</code></pre><p>그리고 프로미스로 작성된 foo 함수가 있는데
이 둘은 같은 함수이다.</p>
<p>위 함수에서 await이 사용되지 않은 모습을 볼 수있는데
이는 동기적으로 작동한다는 것을 의미한다.</p>
<p>await 문이 없는 async 함수는 동기적으로 실행된다
하지만 await 문이 있다면 async 함수는 항상 비동기적으로 완료된다.
라고 MDN에 나와있다.</p>
<h3 id="고차함수">고차함수</h3>
<p>MDN에서는 고차함수를 다음과 같이 설명한다.</p>
<blockquote>
<p>함수를 반환하는 함수를 고차 함수라고 부릅니다</p>
</blockquote>
<p>일반적으로 볼때 한번 값을 반환하는 함수가 있다 이게 보통 우리가 아는 일반적인 함수이다.
그리고 이걸 1차 고차함수라고 부르는것 같다.
2차 고차함수는 함수가 함수를 리턴하는것이다.</p>
<pre><code>type FirstOrderFunc&lt;T,R&gt; = (T) =&gt; R

const inc: FirstOrderFunc&lt;number,number&gt; = (x: number) =&gt; x+1

console.log(inc(1))</code></pre><p>1차 고차함수의 예시를 보면 위 코드와 같다. 먼저 타입 별칭으로 함수를 만들어 주는데 이게 1차 함수라고 볼수 있을것이다.
그리고 구현을 할때 inc로 구현을 하는데 제네릭에 number를 두개 넣은것을 볼 수있다. 따라서 위 함수는 파라미터로 number를 받고 반환값도 number를 준다는 뜻이다.</p>
<pre><code>type FirstOrderFunc&lt;T,R&gt; = (T) =&gt; R
type SecondOrderFunc&lt;T,R&gt; = (T) =&gt; FirstOrderFunc&lt;T,R&gt;

const add: SecondOrderFunc&lt;number,number&gt; = 
    (x: number): FirstOrderFunc&lt;number, number&gt; =&gt;
    (y: number): number =&gt; x+y

console.log(add(1)(2))</code></pre><p>위 코드를 처음 봤을때는 이해가 잘 되지않아서 그냥 넘어갔는데 복습을 하니깐 알 것같다. 확실히 복습이 중요하긴 한거같다.</p>
<p>먼저 첫번째 타입별칭으로 나오는 FirstOrderFunc는 위에서 다루듯이 평범한 1차 고차 함수이다. 
그리고 SecondOrderFunc가 있는데 이는 함수를 리턴한다.
따라서 이는 2차 고차 함수임을 알 수 있다.</p>
<p>위 함수는 파라미터를 적을때 ()()이런식으로 적는다.
그래서 결과로 3이 나오는것을 알 수있다.</p>
<pre><code>type FirstOrderFunc&lt;T,R&gt; = (T) =&gt; R
type SecondOrderFunc&lt;T,R&gt; = (T) =&gt; FirstOrderFunc&lt;T,R&gt;
type ThirdOrderFunc&lt;T,R&gt; = (T) =&gt; SecondOrderFunc&lt;T,R&gt;

const add: ThirdOrderFunc&lt;number,number&gt; = 
    (x: number): SecondOrderFunc&lt;number,number&gt; =&gt;
    (y: number): FirstOrderFunc&lt;number,number&gt; =&gt;
    (z: number): number =&gt; x+y+z


console.log(add(1)(2)(3))</code></pre><p>다음은 3차 고차함수이다. 2차고차함수를 무리없이 이해했다면 사실 더이상 어려운점이 보이지 않는다.</p>
<pre><code>type FirstOrderFunc&lt;T,R&gt; = (T) =&gt; R
type SecondOrderFunc&lt;T,R&gt; = (T) =&gt; FirstOrderFunc&lt;T,R&gt;
type ThirdOrderFunc&lt;T,R&gt; = (T) =&gt; SecondOrderFunc&lt;T,R&gt;

const add3: ThirdOrderFunc&lt;number,number&gt; = 
    (x: number): SecondOrderFunc&lt;number,number&gt; =&gt;
    (y: number): FirstOrderFunc&lt;number,number&gt; =&gt;
    (z: number): number =&gt; x+y+z

const add2: SecondOrderFunc&lt;number,number&gt; =
    (x: number): FirstOrderFunc&lt;number,number&gt; =&gt;
    (y: number): number =&gt; x+y

const add1: FirstOrderFunc&lt;number,number&gt; = (x: number) =&gt; x

const add_1: FirstOrderFunc&lt;number,number&gt; = add2(1)
const add_2: SecondOrderFunc&lt;number,number&gt; = add3(1)

console.log(add_2(2)(3),add_1(2))</code></pre><p>살짝 응용해서 보자면 이런 코드가 나온다. 원래대로라면 add3는 3차 고차함수 이기때문에 파라미터가 세번들어가야한다. 하지만 미리 정해놨기때문에 두개만 넣어도 오류가 나지 않는다 add_1의 경우도 마찬가지로 add2에 미리 한개의 파라미터를 정해놨기 때문에 문제가 되지 않는다. 위 코드를 실행하면 1+2+3 = 6 이 나오고 뒷 코드는 1+2 = 3 이 나오게된다.</p>
<hr>
<p><strong>클로저</strong></p>
<p>클로저 라는게 있다. 클로저는 어느 한 언어에 국한된 개념이 아니라
함수형 프로그래밍에서 유효한 개념이다.
먼저 타입스크립트의 가장 중요한 핵심은 자바스크립트에서 시작하기 때문에 MDN의 설명을 참조하자면</p>
<blockquote>
<p>클로저는 함수와 함수가 선언된 어휘적 환경의 조합이다. 클로저를 이해하려면 자바스크립트가 어떻게 변수의 유효범위를 지정하는지(Lexical scoping)를 먼저 이해해야 한다.</p>
</blockquote>
<p>MDN에서는 위와 같이 설명하고 있다
클로저는 이제 유효범위에 대해서 다루는 지정자? 같은 개념이다.</p>
<pre><code>const makeNames = (): () =&gt; string =&gt;{
    const names = [&#39;jack&#39;,&#39;jane&#39;,&#39;smith&#39;]
    let index = 0
    return (): string =&gt; {
        if(index == names.length)
            index = 0
        return names[index++]
    }
}

const makeName: () =&gt; string = makeNames()
console.log(
    [1,2,3,4,5,6].map(n=&gt;makeName())
)</code></pre><p>먼저 앞에서 한번다뤘던 예제이다. 
makeNames 함수는 string을 반환하는 익명함수를 가진 2차 고차함수이다.</p>
<p>잠시 위 예제를 다루기 전에 다루지않았던 내용을 봐야겠다.</p>
<pre><code>function add(x: number): (number) =&gt; number{ // 바깥쪽 유효범위
    return function(y: number): number{ // 안쪽 유효범위
        return x+y // 클로저
    } //안쪽 유효범위 끝
} //바깥쪽 유효범위 끝

const add1 = add(1) // 1
const result = add1(2) // 2</code></pre><p>안쪽의 유효범위만 봤을때 x는 이해할수 없는 변수이다.
이를 자유변수라고 부른다.
하지만 이는 컴파일 될때 문제가 생기는것은 아니다.
클로저는 다른말로 지속되는 유효 범위라고 부르는데
주석에 있는 1의 add1을 보게된다면 다음과 같이 add함수를 호출 하더라도 변수 x가 메모리에서 해제되지 않기 때문이다
출력시켜도 비정상적으로 출력된다. 2차고차함수인데 파라미터가 한번 들어갔기 때문에
주석 2를 보면 저런식으로 되야 비로소 메모리가 해제가 된다.
값을 발생시켜야 메모리가 해제되는 유효범위를 클로저 라고 한다.
개념이 더럽게 난해하다.</p>
<p>이제 위에서 본 예제를 다시보자
index와 names는 return 하는 함수안에서는 자유변수이다.
함수내부에서 index는 다시 0이 되는데 이 메모리가 해제가 되지않는것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[타입스크립트 복습 - 2]]></title>
            <link>https://velog.io/@stulta_amiko/tsr2</link>
            <guid>https://velog.io/@stulta_amiko/tsr2</guid>
            <pubDate>Mon, 18 Jul 2022 13:46:12 GMT</pubDate>
            <description><![CDATA[<h3 id="배열의-디스트럭처링--제네릭">배열의 디스트럭처링 / 제네릭</h3>
<p>디스트럭처링 자체는 저번에 복습 1때 다뤘었던 내용이다 
객체와 같은 자료형을 디스트럭처링 하는것을 다뤘었다면 이번에는 배열을 디스트럭처링 하는 방법이다.</p>
<pre><code>let array: number[] = [1,2,3,4,5]
let [first,second,third,...other] = array
console.log(first,second,third,other)</code></pre><p>이렇게 생긴 코드가 있다. array는 숫자 배열이다
두번째줄이 디스트럭처링을 하는 코드인데 보면 순서대로 
first second ... 하다가 마지막에 ... other 하는모습을 볼 수 있다.
그러면 1,2,3까지는 각각 first second third에 할당되는데 ...other은 레스트 연산자이기 때문에 4와 5가 한번에 other에 할당되는 모습을 볼 수 있다.
그리고 세번째 줄에서 출력하게 되면
1,2,3,[4,5] 이런식으로 출력되게 될것이다.</p>
<hr>
<p>제네릭 자체는 다른언어에서도 쓰긴한다. 대표적으로 자바같은 언어에서 사용하고 타입스크립트에서도 제네릭을 많이 사용하는 편이다.</p>
<pre><code>const arr_length = &lt;T&gt;(array: T[]): number =&gt; array.length</code></pre><p>위와같은 코드가 있다고 할때 예를들어 지금같은경우는 제네릭 T를 이용해서 배열을 받고있지만 만약에 제네릭을 사용하지 않고 number를 사용한다고 하자
하지만 입력되는 배열이 number 타입이 아닐수도 있고 boolean이나 string일 수도있는 것이다. 그에 맞게 알아서 바꿔주는게 제네릭인것 같다.</p>
<h3 id="배열의-메서드-filter--map--reduce">배열의 메서드 filter / map / reduce</h3>
<p><strong>filter</strong></p>
<pre><code>let num : number[] = range(1,11)

let odds: number[] = num.filter((value)=&gt;value%2!=0)
let evens: number[] = num.filter((value)=&gt;value%2==0)

console.log(odds,evens)</code></pre><p>먼저 필터함수부터 살펴보면 num 이라는 array가 있고</p>
<p>odds 와 evens 배열이 있다. 이는 num배열을 가공해서 만들어졌다.
먼저 odds 부터 보면 num 배열에 filter 메서드를 걸어서 가공하는 모습이다.
value를 2로 나눴을때 0이 아닌 수들을 필터링 해서 넣는것이다. 보면 이제 filter의 파라미터에는 함수가 들어가고 value 의 값대로 순환을 하면서 이제 참인것들만 배열에 남기는 것임을 알 수 있다.
evens는 odds 와 반대로 작동하므로 짝수만 남긴다
따라서 위 코드를 출력하게 되면 odds에는 홀수만 남게되고 evens에는 짝수만 남게 되어 출력될것이다.</p>
<hr>
<p><strong>map</strong></p>
<p>map 메서드는 배열을 순회하며 함수를 실행시키고 저장한 배열을 반환한다.</p>
<pre><code>let num : number[] = range(1,11)

let arr: number[] = num.map((val: number)=&gt; val*val)

console.log(arr)</code></pre><p>쉽게 이해하기 위해서 작성된 예제를 보면서 분해하면서 이해를 해보자</p>
<p>filter와 마찬가지로 num 이라는 1부터 10까지의 배열을 생성해준다.</p>
<p>arr에서는 map 메서드를 사용하는데 사용방법은 위에서 했던것과 비슷하지만 결과는 다르다.
filter는 반환값이 true인것만 남기는 반면에 map메서드는 함수의 실행결과를 각 인덱스에 저장하는 형태로 작동을 한다.</p>
<p>위 코드를 실행하게 되면 1부터 10까지 제곱한 수가 arr에 저장된 코드가 출력될것이다.</p>
<hr>
<p><strong>reduce</strong></p>
<p>메서드는 배열의 각 요소에 대해 주어진 reduce 함수를 실행하고, 하나의 결과값을 반환합니다.</p>
<pre><code>let num : number[] = range(1,101)
let add : number = num.reduce((result: number,value: number)=&gt; result+value,0)

console.log(add)</code></pre><p>위와 같은 코드를 보면서 이해를 해보자 이번엔 num 배열이 1부터 100까지 담겨있는 배열이다. result와 value가 존재하는데 여기서 result는 기존의 값이고 value는 그다음 인덱스의 값을 의미한다. 예를들어 위 코드는 1부터 100까지 돌면서 더한값을 result로 반환하고 최종적으로 result가 출력된다.
인덱스가 0부터 시작하니깐 인덱스가 0일때의 값은 1이다. result의 초기값은 0이고 첫 순회때는 1이 result에 저장될것이다. 1+0 = 1 이니 저장이되고 그다음에는 1+2 = 3이 result에 저장될것이다 이런식으로 차례로 계산하게 되면 최종적으로 5050을 반환하게 될것이다.
이런식으로 reduce 메서드는 배열을 하나로 만들어주는 결과를 생성한다.</p>
<h3 id="iterator--generator">iterator / generator</h3>
<p>iterator 혹은 반복기라고 불리는것은 자주 사용되는 연산이다.
반복기를 구현한 코드를 보자</p>
<pre><code>const createRangeIterable = (from: number,to: number) =&gt;{
    let currentValue = from
    return{
        next(){
            const value = currentValue &lt; to ? currentValue++ : undefined
            const done = value == undefined
            return {value,done}
        }
    }
}
</code></pre><p>rangeIterable이라는 클래스를 만들어서 반복기를 사용할 수 있다.
반복기를 제공하는 역할을 하는 함수를 반복기 제공자라고 한다.
next 메서드를 리턴하기 때문에 위 코드는 반복기를 제공하는 역할을 한다.</p>
<pre><code>const iterator = createRangeIterable(1,4)
while(true){
    const {value,done} = iterator.next()
    if(done) break
    console.log(value)
}</code></pre><p>createRangeIterable을 실행하기 위해 iterator 라는 변수를 만들었다.
range와 유사하게 createRangeIterable에 파라미터로 범위가 들어간다. iterator의 next 메서드를 호출하게되면 value와 done을 반환하게 된다.
그리고 done이 true가 되면 반복문이 멈추게 된다.
creatRanceIterable에서 currentvalue는 to가 될때까지 값을 늘리게 되고 목표한 값까지 반복이 끝나면 value가 undefined가 되게되고 따라서 done도 true가 되게 된다.</p>
<p>iterable한 createRangeIterable로 구현한 iterator를 for of에 넣게 되면 오류가 발생한다.</p>
<p><img src="https://velog.velcdn.com/images/stulta_amiko/post/88ac42b1-c82d-461e-b5e9-25b2a87191f9/image.png" alt=""></p>
<p>오류를 보면 [Symbol.iterator]를 가지고 있어야 한다고 나온다.
createRangeIterable을 함수에서 클래스로 바꿔준다음에  [Symbol.iterator]을 넣어준다.</p>
<pre><code>class createRangeIterable{
    constructor(public from: number,public to: number) {}
    [Symbol.iterator](){
        const that = this
        let currentValue = that.from
        return{
            next(){
                const value = currentValue &lt; that.to ? currentValue++ : undefined
                const done = value == undefined
                return{value,done}
            }
        }
    }

}

const iterator = new createRangeIterable(1,4)

for(let value of iterator){
    console.log(value)
}</code></pre><p>위와같은 방식으로 짜게된다.
이제 함수가 아니기 때문에 constructor 즉 생성자를 따로 넣어줘야한다.
그리고 for of는 iterable 한 객체만 들어갈 수 있기 때문에 클래스 내부에 iterator을 넣어준 모습을 볼 수 있다.</p>
<hr>
<p>제네릭이 들어가는 iterable도 있다</p>
<pre><code>class StringIterable implements Iterable&lt;string&gt;{
    constructor(private strings: string[] = [] ,private currentIndex: number = 0) { }
    [Symbol.iterator]() : Iterator&lt;string&gt; {
        const that = this
        let currentIndex = that.currentIndex, length = that.strings.length

        const iterator: Iterator&lt;string&gt; = {
            next(): {value: string,done: boolean} {
                const value = currentIndex &lt; length ? that.strings[currentIndex++] : undefined
                const done = value == undefined

                return {value,done}
            }
        }
        return iterator
    }
}

for(let value of new StringIterable([&#39;hello&#39;,&#39;world&#39;,&#39;!&#39;]))
    console.log(value)</code></pre><p>Iterable을 implements 하는것이 보인다. 자신을 구현하는 클래스가 Symbol.iterator를 제공하는것을 명시하는 것이다.
생성자에서 파라미터로 배열을 받는것이 보인다 후에 인덱스를 받는데 디폴트값으로 0이 설정되어있다. 후에는 iterator가 나오는데 보면 Iterator&lt;&gt;로 선언된게 있다 이는 타입을 명확히하는것을 뜻한다.
내부는 위에서 만들었던 숫자 배열 iterator랑 유사하다
나머지 부분은 따로 해석하지 않겠다.</p>
<hr>
<p><strong>generator</strong></p>
<p>function *키워드로 yield를 호출할 수 있고
yield는 return 과 유사하다</p>
<pre><code>function* gen(){
    console.log(&quot;generator start&quot;)
    let value = 1
    while(value &lt; 4)
        yield value++
    console.log(&#39;generator end&#39;)
}

for(let value of gen()){
    console.log(value)
}</code></pre><p>예전에 작성한 게시글에서 발생기의 예제 코드이다 보게 되면 특이하게 function 에 * 이붙는 것을 알 수 있다. 그리고 값을 yield로 반환하는데
gen에 있는 값을 for of로 추출하는 모습을 볼 수 있다. 여기서 좀 특이한 점이 이제 
for..of의 경우iterable한 경우에만 사용할 수 있는데 generator의 MDN의 설명을 보면 다음과 같다.</p>
<blockquote>
<p>Generator 객체는 generator function 으로부터 반환된 값이며 이터러블 프로토콜과 이터레이터 프로토콜을 준수합니다.</p>
</blockquote>
<p>라고 써있으므로 function *로 선언된 gen() generator 객체는 iterable 하기때문에 for of에도 적용할 수 있는 것이다.</p>
<p>그래서 위 코드를 실행하게 되면
start 1 2 3 end 이런식으로 끝나게 되는데 단일스레드로 작동하는게 아닌 다중스레드로 동작하는것 처럼 보이게 해준다.</p>
<p>위에서 구현한 stringIterator의 경우 generator를 사용하면 더 간결하게 작성할 수 있다.</p>
<pre><code>class StringIterable&lt;T&gt; implements Iterable&lt;T&gt;{
    constructor(private values: T[] = [] ,private currentIndex: number = 0) { }
    [Symbol.iterator] = function *(){
        while(this.currentIndex&lt;this.values.length){
            yield this.values[this.currentIndex++]
        }
    }
}

for(let value of new StringIterable([1,2,3,4,5]))
    console.log(value)

for(let value of new StringIterable([&#39;hello&#39;,&#39;world&#39;,&#39;!&#39;]))
    console.log(value)</code></pre><p>정확히는 string만 iterator 하는게 아니라 모든 배열을 iterator하게 해주는 클래스이다. 위에서 복잡하게 value와 done을 관리하던것과는 다르게 단순하게 생성기를 이용해서 값을 반환하는것을 볼 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[타입스크립트 복습 - 1]]></title>
            <link>https://velog.io/@stulta_amiko/tsr1</link>
            <guid>https://velog.io/@stulta_amiko/tsr1</guid>
            <pubDate>Mon, 18 Jul 2022 13:42:02 GMT</pubDate>
            <description><![CDATA[<p>현재까지 타입스크립트 기초를 공부하면서 어려웠던점과 이해가 되지 않았던 점에 대해서 다시 한번 복습을 하려고 한다.</p>
<h3 id="인터페이스-추상-상속">인터페이스 추상 상속</h3>
<pre><code>interface IPerson4{
    name: string,
    age: number,
}

class Person4 implements IPerson4{
    name: string
    age: number
}</code></pre><p><a href="https://velog.io/@stulta_amiko/tsb4#%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%EA%B5%AC%ED%98%84">https://velog.io/@stulta_amiko/tsb4#%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%EA%B5%AC%ED%98%84</a>
에서 다뤘던 내용이다.</p>
<p>인터페이스는 일반적인 객체 혹은 클래스와 같은 방식으로 다루며
클래스에서 사용을 하려고 할때에는 implements 키워드를 이용해서 사용한다.</p>
<p>추상은 다른 객체지향 언어에서도 볼 수있는 개념으로 abstract 키워드를 
이용해서 작성한다.</p>
<pre><code>abstract class class_name{
    abstract key : type
    abstract method_name( ) { }
}</code></pre><p>상속은 extends를 사용하며 부모 클래스의 생성자는 super 키워드로 불러올 수 있다.</p>
<pre><code>abstract class IPerson5{
    abstract name:string
    constructor(public age?: number) { }
}

class Person5 extends IPerson5{
    constructor(public name: string, age?: number){
        super(age)
    }
}

let jack5 : Person5 = new Person5(&#39;jack&#39;,43)
console.log(jack5)</code></pre><p>위 코드는 추상클래스를 상속하는 코드이다.
위와같은 방식으로 이용할 수 있으며 부모클래스의 생성자 또한 super로 불러오는 모습까지 확인할 수 있다.</p>
<h3 id="디스트럭처링">디스트럭처링</h3>
<p>ECMAScript에서 부터 적용되기 시작한 기법? 방법? 중에 하나로 스트럭처링 즉 구조화의 반대되는 개념으로 알고있다.
아직도 이해가 안되서 다시 보도록 하면</p>
<p>구조화라는 개념은 이제 보통 여러개의 변수나 자료형을 하나로 묶는 행위를 뜻한다.
C언어에서 struct 키워드의 사용방법이라던지 그런것을 생각해보면된다.
혹은 자바스크립트의 객체라던지 다른 객체지향언어의 객체라던지 보면 전부 무언가를 하나로 묶는 행위를 하는데 이를 구조화라고 부르는것 같다.</p>
<p>그러면 디스트럭처링은 무엇인가? 하면 이제 구조화의 반대개념이라고 생각하면 쉬울것 같다.</p>
<p>구조화가 한개로 묶는 개념이라면 구조화되어있던 자료형 혹은 변수와같은 것들을 풀어서 각각의 변수로 할당해주는것을 뜻한다.</p>
<pre><code>import { IPlane } from &quot;./test&quot;;

let B747: IPlane = {company:&#39;Boeing&#39;,name:747},
    B777: IPlane = {company:&#39;Boeing&#39;,name:777,type:300}

let {company,name} = B747
console.log(company,name)</code></pre><p><a href="https://velog.io/@stulta_amiko/tsb5#destructuring">https://velog.io/@stulta_amiko/tsb5#destructuring</a>
에서 봤던 예제이다.</p>
<p>예제를 보면 어떤느낌인지 바로온다 원래 객체인 B747과 B777 객체가있을때 company와 name 이라는 개별 변수로 B747의 값들을 다시 할당해 주는것을 디스트럭처링이라고 한다.</p>
<h3 id="타입변환">타입변환</h3>
<pre><code>let person:object = {name:&#39;jack&#39;,age:33};
person.name</code></pre><p>위와같은 코드가 있다. 객체의 값을 받아오려는 일반적인 코드로 보이지만
person은 object 타입으로 선언이 되있어서
만약 person.name을 불러오려고 한다면 오류가 발생할 것이다.</p>
<p>따라서 위와같은 코드처럼 어느 객체의 값을 가져오려고 한다면 그 값에 맞는 타입으로 변환을 시켜야한다.</p>
<pre><code>let person:object = {name:&#39;jack&#39;,age:33};
console.log((&lt;{name: string}&gt;person).name)</code></pre><p>하지만 타입스크립트에서는 이를 타입변환이라고 부르지 않고</p>
<p>&quot;타입단언&quot;</p>
<p>이라고 부른다.
자바스크립트의 타입변환이랑 헷갈린다는 이유라나 뭐라나</p>
<p>하여튼 이 타입단언의 종류는 위에서 설명했던 &lt;&gt;를 이용하는 방법과 as를 이용하는 방법이 있다.</p>
<pre><code>(&lt;type&gt; objcet)
(object as type)</code></pre><p> 위와같은 방법으로 타입단언을 한다.</p>
<pre><code> interface Iname{
    name:string
};

let obj:object = {name:&#39;jack&#39;}

let name1 = (&lt;Iname&gt;obj).name
let name2 = (obj as Iname).name

console.log(name1,name2)</code></pre><p>이런식으로 타입단언을 이용할 수 있다.</p>
<h3 id="함수-시그니처">함수 시그니처</h3>
<p>함수 시그니처를 앞에서 다뤘는데 너무 빈약하게 다뤘던거 같아서 다시한번 복습할겸해서 적는다.</p>
<pre><code>let func_ex: (string,number) =&gt; void = function(name: string,age: number): void{}</code></pre><p>위와같은 형식으로 함수를 만드는데 프로그래밍을 어느정도 했다면 대충 뭔지 보일것이다.</p>
<p>함수의 이름을 적고 콜론뒤에 파라미터의 타입이 오며 arrow 뒤에 함수가 리턴할 타입을 보여주고 = 뒤에 함수라는것을 명시하고 파라미터에 이름을 적어주고 다시 콜론뒤에 반환 타입이 뭔지 알려주는 방식으로 선언을 하는 모습이다.</p>
<h3 id="type-키워드">type 키워드</h3>
<p>타입스크립트는 type이라는 키워드를 제공하고 이는 기존에 존재하는 타입을 단순히 이름을 바꿔서 사용할 수 있게하는 키워드이고 이를 타입별칭 이라고 부른다.</p>
<p>위에서 만들었던 (string,number) =&gt; void 와 같은 함수 시그니처를
stringNumberFunc 와같은 방식으로 타입별칭을 만들 수 있다.</p>
<pre><code>type strnumFunc = (arg1:string,arg2:number) =&gt; void
let f:strnumFunc = function(a:string,b:number):void {}
let g:strnumFunc = function(a:string,b:number):void {}</code></pre><p>이런식으로 타입별칭을 만들어주고 사용할 수 있다.
타입명을 적을때 타입별칭으로 설정한것을 적어주면 된다.</p>
<h3 id="중첩함수">중첩함수</h3>
<pre><code>const calc = (value: number, cd:(number)=&gt;void): void =&gt;{
    let add = (a,b) =&gt; a+b;
    function multiply(a,b) {return a*b}

    let result = multiply(add(1,2),value)
    cd(result)
}

calc(30,(result:number) =&gt; console.log(`result is ${result}`))</code></pre><p>실행결과는 90이다.
함수안에 함수가 있는것을 보고 중첩함수라고 한다.
위 코드를 처음부터 분해해보자</p>
<p>먼저 calc 함수는 value와 함수를 파라미터로 받는 함수이다.
그리고 add 함수는 파라미터를 서로 더해주는 역할을 하고
multyply함수는 파라미터를 서로 곱해준다.
result는 1과 2를 더한값을 calc의 인수로 들어온 value값을 곱해주는 역할을 결과적으로 하게된다.</p>
<h3 id="고차함수">고차함수</h3>
<p>고차함수개념은 중간에 타입스크립트 기초 - 7에서 한번 배우고 넘어간적이 있고 그이후로 계속 개념이 나왔다.
처음에는 이해가 안됐지만 지금도 잘안된다.</p>
<p>다시한번 보도록하면 고차함수는 이제 함수형 프로그래밍에서 중요한부분을 차지하고있다고 하며</p>
<p>쉽게 보자면 우리가 아는 단하나의 함수가 아닌 함수와 함수가 연결되어 있는것을 고차함수라고 한다.</p>
<pre><code>const add2 = (a:number): (number) =&gt; number =&gt; (b: number): number =&gt;a+b</code></pre><p>위와같은 코드를 고차함수라고 한다.
사용은</p>
<pre><code>add(1)(2)</code></pre><p>와 같은 방식으로 하는것이고 차수가 두번있었으므로 2차고차함수라고 불린다.</p>
<pre><code>type NumberToNumberFunc = (number) =&gt; number
const add = (a: number): NumberToNumberFunc =&gt;{
    const _add: NumberToNumberFunc = (b: number) =&gt;{
        return a+b
    }
    return _add
}</code></pre><p>위 코드를 한번 분해해 보자</p>
<p>먼저 타입별칭으로 NumberToNumberFunc 타입을 만들어 준다.
숫자를 그대로 반환하는 타입이다.
add함수는 1차로 a라는 파라미터를 받는다. 그리고 내부에 _add함수가 2차로 존재하게된다.</p>
<p>그리고 위에서 말했던 2차고차함수를 쉽게 표현한게 위코드이다.
이런식으로 구현을 했기 때문에 중간과정을 생략하고도 가능하다.</p>
<p>저걸 한번에 구현해서 만든게</p>
<pre><code>const add2 = (a:number): (number) =&gt; number =&gt; (b: number): number =&gt;a+b</code></pre><p>이런식으로 되는것이다.</p>
]]></description>
        </item>
    </channel>
</rss>