<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>zero_mountain.log</title>
        <link>https://velog.io/</link>
        <description>매몰되지 않는 개발자가 되자</description>
        <lastBuildDate>Wed, 11 Aug 2021 01:08:44 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>zero_mountain.log</title>
            <url>https://images.velog.io/images/zero_mountain/profile/cc0e6aac-e7da-4d64-b6d3-1b3f09397835/KakaoTalk_Photo_2020-06-30-23-01-48.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. zero_mountain.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/zero_mountain" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Node] middleware arrangement]]></title>
            <link>https://velog.io/@zero_mountain/Node-middleware-arrangement</link>
            <guid>https://velog.io/@zero_mountain/Node-middleware-arrangement</guid>
            <pubDate>Wed, 11 Aug 2021 01:08:44 GMT</pubDate>
            <description><![CDATA[<p>클론 코딩을 하면서, 마이페이지에서 해당 유저의 팔로잉과 팔로워 목록을 리덕스의 dispatch를 통해서 가져왔었는데, 아무래도 dispatch를 사용하게 되면 코드의 양이나 유지 보수에 부담이 되기 때문에 <code>swr</code> 모듈을 적용했다.</p>
<blockquote>
<p>swr은 데이터를 가져오기 위한 React Hooks로, 자세한 내용은 <a href="https://swr.vercel.app/ko">공식문서</a>를 참조.</p>
</blockquote>
<ul>
<li><p>dispatch를 사용한 데이터 호출</p>
<pre><code class="language-jsx">useEffect(() =&gt; {
  dispatch({
    type: LOAD_FOLLOWERS_PENDING
  });
  dispatch({
    type: LOAD_FOLLOWINGS_PENDING
  });
}, []);</code></pre>
</li>
<li><p>swr을 사용한 데이터 호출</p>
<pre><code class="language-jsx">import useSWR from &quot;swr&quot;;
</code></pre>
</li>
</ul>
<p>const fetcher = url =&gt;
  axios.get(url, { withCredentials: true }).then(result =&gt; result.data);</p>
<p>const { data: followersData, error: followerError } = useSWR(
    <code>http://localhost:3065/user/followers</code>,
    fetcher
  );
  const { data: followingsData, error: followingError } = useSWR(
    <code>http://localhost:3065/user/followings</code>,
    fetcher
  );</p>
<p>```</p>
<p>해당 내용만 비교해보면 그렇게 효율성이 높아 보이지 않지만, 리듀서 파트와 사가 파트의 코드도 swr 코드로 대체할 수 있기 때문에, 리덕스의 액션 지옥에서 구원받을 수 있다.</p>
<p>일단, 코드를 대체하고 서버를 실행해 잘 동작하는지 확인했다.</p>
<p>언제나 그렇듯 오류가 발생했다. 어떤 오류인지 확인해보니 콘솔창에 404 에러가 발생했다.</p>
<p>네트워크 탭을 열어서 확인해보니 정말 404 에러가 확인되었다.</p>
<p><img src="https://images.velog.io/images/zero_mountain/post/5bcab162-cc7b-496c-8284-65487686b558/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-08-11%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%208.26.46.png" alt=""></p>
<p>이상하다 싶어 서버 코드를 확인해 보았다.</p>
<p><img src="https://images.velog.io/images/zero_mountain/post/3194aafe-575b-49a9-99b7-2580df8950a1/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-08-11%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%208.29.08.png" alt=""></p>
<p>분명히 팔로잉 목록과 팔로워 목록을 가져오는 라우터가 존재하는데 404 에러가 발생하니 의아했다.</p>
<p><img src="https://images.velog.io/images/zero_mountain/post/dd64287b-d368-408f-bf5e-f0a12f051c72/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-08-11%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%209.25.05.png" alt=""></p>
<p>서버쪽 로깅도 확인해 봤으나 404 로그말고는 별다른 오류가 없었다.</p>
<p>구글에 다양한 키워드로 검색해 봤지만, 내가 원하던 내용은 찾을 수 없었다.</p>
<p>마지막 보루로, node 장인 <a href="https://www.zerocho.com/category/NodeJS/post/578b5a36d8316615006bee0f">제로초</a>님의 블로그를 확인해보니, 원하던 정보를 얻을 수 있었다.</p>
<p>블로그 내용을 발췌해 보자면, 라우팅을 처리할 때, 특히 엔드포인트가 와일드카드가 사용된다면 순서에 유의해야 한다고 작성되어 있었다.</p>
<p>위의 코드를 살펴봐도, 팔로잉 목록과 팔로워 목록을 가져오는 라우터가 와일드카드가 사용된 라우터들 보다 아래에 있었다.</p>
<p>합리적 의심이 확신이 되었고, 두개의 라우터를 와일드카드가 포함된 라우터보다 상단으로 옮겨주니 정상적으로 목록을 가져올 수 있었다. </p>
<blockquote>
<p><strong>미들웨어는 위에서 아래로, 왼쪽에서 오른쪽으로 실행</strong></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Jekyll permission 오류 해결]]></title>
            <link>https://velog.io/@zero_mountain/Jekyll-permission-%EC%98%A4%EB%A5%98-%ED%95%B4%EA%B2%B0</link>
            <guid>https://velog.io/@zero_mountain/Jekyll-permission-%EC%98%A4%EB%A5%98-%ED%95%B4%EA%B2%B0</guid>
            <pubDate>Mon, 09 Aug 2021 23:20:13 GMT</pubDate>
            <description><![CDATA[<p><a href="https://pages.github.com/">깃헙 블로그 공식문서</a></p>
<p>해당 공식문서를 참고해서 자신의 블로그의 레포지토리를 생성합니다.</p>
<p>공식문서의 5단계를 모두 완성했다면, <code>username.github.io</code>로 접속해 보면 index.html에 입력된 내용을 확인할 수 있을 것이다.</p>
<p>다음으로, 본격적으로 깃헙 블로그의 UI를 위해 jekyll을 세팅하는 작업을 해보자.</p>
<h2 id="맥에-jekyll-설치하기">맥에 Jekyll 설치하기</h2>
<p>Jekyll 공식문서 : <a href="https://jekyllrb-ko.github.io/docs/installation/macos/">맥에 Jeykill 설치하기</a></p>
<p>다음 Jekyll 공식문서를 참고하여 Jeykill 설치를 진행합니다.</p>
<p>공식문서를 확인해 보면, homebrew를 통해서 ruby를 설치하고 다음 명령어를 통해서 쉘의 환경 변수 설정을 요구하고 있습니다.</p>
<p><code>echo &#39;export PATH=&quot;/usr/local/opt/ruby/bin:$PATH&quot;&#39; &gt;&gt; ~/.bash_profile</code></p>
<ul>
<li>zsh 설정<ul>
<li><code>vim ~/.zshrc</code> 명령어를 통해서 vim 에디터를 통해서 환경 변수를 설정합니다.</li>
<li><code>~/.bash_profile</code> 대신에 <code>~/.zsh</code>로 수정해서 저장해 줍니다.</li>
</ul>
</li>
</ul>
<p>환경 변수 설정을 마치고 공식문서를 참고하여 rbenv 설치를 진행합니다.</p>
<p>rbenv 설치까지 정상적으로 완료했다면, Bundler와 Jekyll을 설치해 줍니다.</p>
<p>jekyll 번들러 설치시 다음과 같이 permission 오류가 발생한다면 </p>
<p><img src="https://images.velog.io/images/zero_mountain/post/09a230e5-9292-4af2-9292-d03791383498/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-08-10%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%207.37.55.png" alt=""></p>
<p><code>sudo gem install jekyll bundler</code> 명령어를 입력해 설치를 진행합니다.</p>
<p>이 모든 설치가 완료되었다면, 깃헙 블로그의 로컬 레포지토리에 저장된 index.html을 삭제합니다.</p>
<p>로컬 레포지토리에서 <code>jekyll new ./</code> 명령어를 입력하면, 블로그에 사용될 기본 소스코드가 생성됩니다.</p>
<p><code>bundle exec jekyll serve</code> 명령어를 입력하면 로컬 서버에서 블로그의 기본 틀을 확인할 수 있습니다.</p>
<p><img src="https://images.velog.io/images/zero_mountain/post/81a85e87-47d4-4808-9ef4-f23b840dde38/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-08-10%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%208.18.45.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[IPC for 리눅스]]></title>
            <link>https://velog.io/@zero_mountain/IPC-for-%EB%A6%AC%EB%88%85%EC%8A%A4</link>
            <guid>https://velog.io/@zero_mountain/IPC-for-%EB%A6%AC%EB%88%85%EC%8A%A4</guid>
            <pubDate>Sat, 07 Aug 2021 14:34:42 GMT</pubDate>
            <description><![CDATA[<p>IPC는 프로세스간에 커뮤니케이션을 할 수 있도록 도와주는 기술이다.</p>
<p>그렇기 때문에, 리눅스에서 IPC를 이해하는 것은 매우 중요하다.</p>
<ul>
<li><p><a href="https://velog.io/@zero_mountain/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EA%B5%AC%EC%A1%B0-4">file</a></p>
</li>
<li><p><a href="https://velog.io/@zero_mountain/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EA%B5%AC%EC%A1%B0-5">message queue</a></p>
<center>
<img src="https://images.velog.io/images/zero_mountain/post/45af06ff-e385-4ae9-ae7e-29e375093030/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-13%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%206.35.42.png" width="600px" />
</cneter>
</li>
<li><p><a href="https://velog.io/@zero_mountain/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EA%B5%AC%EC%A1%B0-5">shared memory</a></p>
</li>
<li><p><a href="https://velog.io/@zero_mountain/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EA%B5%AC%EC%A1%B0-5">pipe</a></p>
<ul>
<li>fork 시스템콜로 자식 프로세스 생성했을 때, 부모와 자식 프로세스간의 통신</li>
<li>fd: 파일 스크립터</li>
<li>nbytes: 실제 파일에 사용될 버퍼의 길이 </li>
</ul>
</li>
</ul>
<center>
<img src="https://images.velog.io/images/zero_mountain/post/987818b8-97e6-474e-8d25-faeba017f6e6/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-13%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%205.57.03.png" width="600px"/>
</center>

<center>
<img src="https://images.velog.io/images/zero_mountain/post/addb0c06-9dec-4b46-b278-684a37684718/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-13%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%205.56.46.png" width="600px" />
</center>

<ul>
<li><a href="https://velog.io/@zero_mountain/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EA%B5%AC%EC%A1%B0-6">signal</a></li>
<li>semaphore</li>
<li><a href="https://velog.io/@zero_mountain/%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EA%B5%AC%EC%A1%B0-6">socket</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Error Log] UNESCAPED_CHARACTERS]]></title>
            <link>https://velog.io/@zero_mountain/Error-Log-UNESCAPEDCHARACTERS</link>
            <guid>https://velog.io/@zero_mountain/Error-Log-UNESCAPEDCHARACTERS</guid>
            <pubDate>Fri, 06 Aug 2021 01:16:29 GMT</pubDate>
            <description><![CDATA[<p>해쉬태그 목록을 가져오는 기능을 구현하면서, 다음과 같은 에러가 발생했다.</p>
<p><img src="https://images.velog.io/images/zero_mountain/post/d7d8041d-3023-48e6-a509-30a6de0f98c1/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-08-06%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%201.26.17.png" alt=""></p>
<p>해당 오류를 확인해 보면, Request의 경로가 캐릭터가 이스케이프되지 않았다는 것을 확인할 수 있다.</p>
<p>이스케이프가 뭔고 하고 찾아보니, MDN 공식문서에 다음과 같이 기재되어 있었다.</p>
<blockquote>
<p><strong>The escape() function computes a new string in which certain characters have been replaced by a hexadecimal escape sequence.</strong></p>
</blockquote>
<p>MDN의 정의를 보면, <code>escape</code> 라는것은 컴퓨터가 못알아먹는 언어를 컴퓨터가 알아먹도록 바꿔주는 함수라는 것을 알 수 있었다.</p>
<ul>
<li><p>에러 코드 (프론트)
<img src="https://images.velog.io/images/zero_mountain/post/f4623293-e97d-4d66-a86f-c9f3eab0cf9e/hashtagAPI.png" alt=""></p>
</li>
<li><p>에러 코드 (백엔드)
<img src="https://images.velog.io/images/zero_mountain/post/575ca160-57b4-4394-b082-ba37181759eb/hashtagServer.png" alt=""></p>
</li>
</ul>
<p>에러 코드를 살펴보면, 프론트에서 get 요청을 보내는 엔드포인트에 data를 encode 해주지 않았고, 백엔드에서는 req.params.tag를 decode 해주지 않았다.</p>
<p>다만, URI에서는 일반적인 encode함수를 사용하지 않고, <strong>encodeURIComponent</strong>라는 함수를 사용한다. decode 역시, <strong>decodeURIComponent</strong>를 사용하면 컴퓨터가 알 수 있는 언어로 처리해준다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Error Log] getServerSideProps]]></title>
            <link>https://velog.io/@zero_mountain/Error-Log-getServerSideProps</link>
            <guid>https://velog.io/@zero_mountain/Error-Log-getServerSideProps</guid>
            <pubDate>Thu, 05 Aug 2021 08:37:53 GMT</pubDate>
            <description><![CDATA[<p>클론 코딩을 하면서 서버 사이드 렌더링을 적용하기 위해서 Next에서 제공되는 getServerSideProps를 사용하는데 오류가 발생했다.</p>
<p>에러 부분은 캡처를 따지 못했다. 서버에 쉬지 않고 무한 요청을 보내서, 셀프 디도스급 요청에 컴퓨터가 잠깐 먹통이 되어버렸기 때문이다...(이제 바꿀때가 되긴했나보다...)</p>
<p>오류의 원인은 getServerSideProps의 매개변수로 들어간느 context에 리덕스 리듀서에서 createStore로 생성한 store가 들어 있어야 하는데, 이 store가 존재하지 않아서 오류가 생겼던걸로 추측이 되었다.</p>
<hr>
<p><img src="https://images.velog.io/images/zero_mountain/post/3ec76bff-6b3e-4f2c-b897-482c14bc1f64/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-08-05%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%201.23.41.png" alt=""></p>
<p><img src="https://images.velog.io/images/zero_mountain/post/3d5758df-8cc2-41a6-ac82-55f07716ead1/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-08-05%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%202.23.32.png" alt=""></p>
<hr>
<ul>
<li>오류에서 사용된 코드</li>
</ul>
<p><img src="https://images.velog.io/images/zero_mountain/post/f47abf89-3275-4088-b1ce-bcdda447da3c/getServerSideProps7.png" alt=""> </p>
<p>store가 undefined로 콘솔에 찍히니, 리듀서 부분에서 스토어를 잘못 생성했나 싶었는데, 그것도 아니었다.</p>
<p>여기 저기 찾아보다, next-redux-wrapper 모듈의 버전에 관한 글을 보게 되었다.</p>
<p>혹시나 해서, 프로젝트의 package.json을 확인해 보니, 해당 모듈이 7버전인 것을 확인했다. </p>
<p>next-redux-wrapper 공식문서에 들어가보니 7버전에서는 getServerSideProps를 다음과 같이 사용하라고 설명했다.</p>
<p><img src="https://images.velog.io/images/zero_mountain/post/cf2fb096-c7e6-46ea-b007-add1a41a639c/getServerSidePropsinf.png" alt=""></p>
<p>공식 문서를 참고해서 코드를 수정하니, getServerSideProps가 올바르게 작동했다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TS] 기본 타입 - (1)]]></title>
            <link>https://velog.io/@zero_mountain/TS-%EA%B8%B0%EB%B3%B8-%ED%83%80%EC%9E%85-1</link>
            <guid>https://velog.io/@zero_mountain/TS-%EA%B8%B0%EB%B3%B8-%ED%83%80%EC%9E%85-1</guid>
            <pubDate>Wed, 04 Aug 2021 14:38:57 GMT</pubDate>
            <description><![CDATA[<p>타입스크립트의 기본 타입 12가지를 알아보자.</p>
<p>우선, 타입스크립트에서 변수에 타입을 선언하는 가장 기본적인 방법에 대해 살펴보자면,</p>
<p><code>var 변수명:타입=값</code></p>
<p>위와 같이, 변수명에 값을 할당해주기 전에, 타입을 정해주면 된다.</p>
<h3 id="number">Number</h3>
<ul>
<li>변수의 타입이 숫자 형태이면 <code>number</code>를 사용해 타입을 지정해준다.</li>
</ul>
<pre><code class="language-ts">const score: number = 90;</code></pre>
<h3 id="string">String</h3>
<ul>
<li>변수의 타입이 문자 형태이면 <code>string</code>을 사용해 타입을 지정해준다.</li>
</ul>
<pre><code class="language-ts">const sentence: string = &#39;Hello, Typescript!&#39;;</code></pre>
<h3 id="boolean">Boolean</h3>
<ul>
<li>변수의 타입이 진위 값인 경우에 <code>boolean</code>을 사용해 타입을 지정해준다.</li>
</ul>
<pre><code class="language-ts">const isHandsome: boolean = false;</code></pre>
<h3 id="null--undefined">Null &amp; Undefined</h3>
<ul>
<li>타입스크립트에서 null과 undefined는 타입스크립트의 환경 설정에 따라 할당이 가능하기 때문에 주류로 사용되는 타입은 아니다.</li>
</ul>
<pre><code class="language-ts">let u: undefined = undefined;
let n: null = null;</code></pre>
<h3 id="array">Array</h3>
<ul>
<li>자바스크립트의 배열에서는 배열의 요소에 어떤 타입이와도 자유로웠지만, 타입스크립트에서는 배열의 모든 요소가 같은 타입인 것을 지향한다.</li>
<li>기본 표현법</li>
</ul>
<pre><code class="language-ts">// 배열의 모든 요소가 number 타입
const arr: number[] = [1, 2, 3];</code></pre>
<ul>
<li>제네릭 표현법</li>
</ul>
<pre><code class="language-ts">const arr: Array&lt;number&gt; = [1, 2, 3];</code></pre>
<h3 id="object">Object</h3>
<ul>
<li><p>원시 타입이 아닌, 나머지 타입들, 즉 참조 타입을 갖는 모든 변수를 object라 함</p>
</li>
<li><p>빈 객체</p>
<pre><code class="language-ts">const obj: object = {};
const arr: object = [];
const func: object = function() {};</code></pre>
</li>
<li><p>속성과 값이 있는 객체</p>
<pre><code class="language-ts">const SON: {name: stirng, job: string, joined_club:string} = {
name: &#39;손흥민&#39;,
job: &#39;축구선수&#39;,
joined_club: &#39;토트넘 핫스퍼&#39;
}</code></pre>
</li>
<li><p>타입의 재사용을 위한 인터페이스 타입</p>
</li>
</ul>
<pre><code class="language-ts">interface Player {
  name: string,
  joined_club: string,
}

const SON: Player = {
  name: &quot;손흥민&quot;,
  joined_club: &quot;토트넘 핫스퍼&quot;
}

const KDB: Player = {
  name: &quot;케빈 더 브라위너&quot;
  joined_club: &quot;맨체스터시티&quot;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TS] JS vs TS 타입]]></title>
            <link>https://velog.io/@zero_mountain/TS-JS-vs-TS-%ED%83%80%EC%9E%85</link>
            <guid>https://velog.io/@zero_mountain/TS-JS-vs-TS-%ED%83%80%EC%9E%85</guid>
            <pubDate>Tue, 03 Aug 2021 11:20:25 GMT</pubDate>
            <description><![CDATA[<p>자바스크립트의 타입은 동적타입, 타입스크립트의 타입은 정적타입이라고 불린다.</p>
<p>동적, 정적이라는 단어에서 유추할 수 있겠지만, 자바스크립트는 동적타입 언어라 변수를 선언할 때, 어떤 타입이 와도 이해해준다. 예를 들면, 다음과 같이 a 라는 변수에 <code>number</code> 타입의 1을 할당해줘도,  <code>string</code> 타입의 &#39;문자&#39;를 할당해줘도, <code>boolean</code> 타입의 true가 할당해줘도 자바스크립트는 이를 너그럽게 이해해 준다.</p>
<pre><code class="language-js">var a = 1
a = &#39;문자&#39;
a = true</code></pre>
<p>반면에, 이런 부분에 굉장히 민감하다. 타입스크립트는 정적타입의 언어이기 때문에, 변수의 타입을 처음부터 정해주고 시작한다. 다음과 같이 a라는 변수의 타입을 <code>number</code> 타입으로 정해주면 빼도 박도 못하고 숫자만 할당할 수 있다. 만약, <code>number</code>타입으로 타입을 정해주고 <code>string</code> 타입의 value를 할당하려고 하면 타입 오류가 발생할 것이다.</p>
<pre><code class="language-ts">var a: number = 1
a = &#39;오류가 발생&#39;</code></pre>
<p>동적타입은 런타임 환경에서 타입을 알 수 있기 때문에, 코드를 실행해 보기 전까지 타입 오류를 확인하기 어렵다. 반면, 정적타입은 개발을 하면서 타입을 분석하기 때문에, 타입 오류를 바로 바로 확인할 수 있다. </p>
<ul>
<li>자바스크립트</li>
</ul>
<pre><code class="language-js">function sum (num1, num2) {
  // 1.javascript가 런타임에 자동으로 실행
  if(typeof num1 !== &#39;number&#39; || typeof num2 !== &#39;number&#39;) {
    throw new Error();
  }
  return num1 + num2;
}

sum(10, 4678)</code></pre>
<ul>
<li>타입스크립트</li>
</ul>
<pre><code class="language-ts">function sum(num1: number, num2: number): number {
  return num1 + num2;
}
sum(10, 4678)</code></pre>
<p>다음 코드에서 볼 수 있듯이, 자바스크립트는 sum 이라는 함수의 코드를 실행하면 1번의 코드를 통해서 인자의 타입을 확인하고, <code>number</code> 타입이 아니면 그제서야 오류를 반환한다. </p>
<p>하지만, 타입스크립트는 sum 이라는 함수의 코드를 작성하는 중에, 인자의 타입을 설정을 안해주었거나, 잘못된 타입이 함수 인자로 들어올 경우 강력하게 어필해 자아성찰할 수 있도록 도와준다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Error Log] axios data 에러]]></title>
            <link>https://velog.io/@zero_mountain/Error-Log-axios-data-%EC%97%90%EB%9F%AC</link>
            <guid>https://velog.io/@zero_mountain/Error-Log-axios-data-%EC%97%90%EB%9F%AC</guid>
            <pubDate>Mon, 02 Aug 2021 07:43:01 GMT</pubDate>
            <description><![CDATA[<p>게시물 좋아요와 좋아요 취소를 구현하는 중에 다음 오류를 직면했다.</p>
<hr>
<p><img src="https://images.velog.io/images/zero_mountain/post/8a63e993-ce62-4066-bd4a-780ba090c1e5/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-08-01%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%202.10.06.png" alt=""></p>
<p><img src="https://images.velog.io/images/zero_mountain/post/a1daeb5e-3bda-475b-82f5-303f27cc3935/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-08-01%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2010.59.08.png" alt=""></p>
<hr>
<p>오류 메시지를 통해서 likePost라는 함수에서 data가 undefined로 들어오는 것을 알 수 있었다.</p>
<p>likePost 함수는 saga의 이펙트 함수를 통해서 서버와 통신을 담당하는데, 아무리 찾아봐도 어느 부분에서 잘못된건지 확인이 되질 않았고, 콘솔을 찍어도 오류때문인지 콘솔도 찍히지 않았다.</p>
<p>이렇게 갈피를 잡지 못하고, 리듀서 코드, 서버쪽 코드를 오랜 시간 들여서 확인했다.</p>
<p>어디서도 오타나 잘못된 코드가 입력됐는지 확인이 되지 않아, 클론코딩 강사님께 질문을 했고 다음과 같은 답변을 받게 되었다.</p>
<hr>
<p><img src="https://images.velog.io/images/zero_mountain/post/b0051521-c2f9-44dc-beae-eea5844e9460/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-08-02%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.30.13.png" alt=""></p>
<hr>
<p>처음에 답변을 봤을때, 이게 무슨 소리인가 싶었다.</p>
<p>x.data에서 x가 갑자기 어디서 튀어나온 것인지 감이 안잡혔다.</p>
<p>답변 내용을 면밀히 곱씹으면서 보고, 코드를 확인해보니 오류가 발생했던 부분에 result.data라고 작성된 부분을 확인하고, result가 x에 해당됨을 인지했다. </p>
<p>에러 메시지에 data가 undefined라고 나와서 data를 콘솔에 찍었었는데, 콘솔에 찍히지 않았던 이유가 data가 result 객체 안에 있던 변수였기 때문이다.</p>
<p>result로 콘솔을 찍어보니, result의 값이 undefined인 것을 확인할 수 있었다.</p>
<p>result는 API 통신으로 서버에서 응답받은 객체를 할당 받는 변수이다.</p>
<pre><code class="language-js">const result = yield call(likePostAPI, action.data);</code></pre>
<p>이 result값이 undefined라는 것은 API함수에서 반환된 값이 undefined라는 것인데, 
API 함수를 확인해보니 axios 요청을 return 해주지 않았기 때문에 오류가 발생했음을 알 수 있었다.</p>
<hr>
<p><img src="https://images.velog.io/images/zero_mountain/post/bfd55739-00e7-487f-aa98-89ac059e08fa/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-08-02%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%201.26.38.png" alt=""></p>
<hr>
<p>해당 API 함수를 다음과 같이 수정해줬다.</p>
<pre><code class="language-js">function likePostApi(data) {
  return axios.patch(`/post/${data}/like`);
}</code></pre>
<blockquote>
<p>이번 오류를 겪으면서, 객관적으로 살펴보지 않았던 점, 이 부분에서는 실수하지 않았을 거라는 안일함과 내 자신에 자만했던 내 모습을 반성하는 시간이 되었다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] useCallback 캐싱]]></title>
            <link>https://velog.io/@zero_mountain/React-useCallback-%EC%BA%90%EC%8B%B1</link>
            <guid>https://velog.io/@zero_mountain/React-useCallback-%EC%BA%90%EC%8B%B1</guid>
            <pubDate>Sun, 01 Aug 2021 12:07:24 GMT</pubDate>
            <description><![CDATA[<p><code>[profileModal, setProfileModal]</code>의 형태로 Modal창의 visible 유무를 관리하기 위해서 <code>useState</code> 훅함수를 이용했다.</p>
<p>다음으로, onClick 이벤트 함수로 사용할 함수를 만들었다.</p>
<p>이 이벤트 함수에서는 <code>setProfileModal</code>을 실행해서 <code>profileModal</code>의 상태값의 반대의 상태값으로 바뀌도록 작성했다.</p>
<ul>
<li><code>setProfileModal(!profileModal)</code></li>
</ul>
<p>브라우저에서 모달창이 잘 작동하는지 확인을 해보는데, 모달창이 나타나고 다시 프로필 수정 버튼을 클릭하면, 모달창이 사라져야 하는데 그대로 화면에 남아 있었다.</p>
<p>이벤트 함수가 잘못 작동하는건가 싶어서, 이벤트 함수 내부에서 <code>console.log(profileModal)</code>을 추가해서 해당 상태가 제대로 바뀌는지 확인해봤다.</p>
<p>다시 브라우저에서 콘솔창을 열고 확인해보니, 처음 버튼을 클릭하면, 모달창이 띄워지지만 <code>profileModal</code>의 값은 계속 false라는 것을 확인했다.</p>
<p><img src="https://images.velog.io/images/zero_mountain/post/007b4f89-de6b-469e-906a-1ba0fa1a5a8b/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-08-01%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%208.48.34.png" alt=""></p>
<p>문제를 찾기 위해서 코드를 다시 살펴봤는데, 문제점을 찾을 수 있었다.</p>
<p>이벤트 함수의 최적화를 위해서 <code>useCallback</code> 훅함수를 사용했는데, dependency 값으로 빈 배열로 설정해두었다.</p>
<p><img src="https://images.velog.io/images/zero_mountain/post/72c544fa-6b8a-48e8-a31e-87ef46ae3cca/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-08-01%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%208.48.20.png" alt=""></p>
<p>dependency 값이 비어있어 profileModal의 값을 캐싱해오지 못했기 때문에, 계속 <code>false</code>를 반환해 제대로 동작하지 않았던 것이다.</p>
<p>해당 dependency 값을 <code>[profileModal]</code>로 수정하니 제대로 작동했다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Error Log] passport 로그인 유지]]></title>
            <link>https://velog.io/@zero_mountain/Error-Log-passport-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EC%9C%A0%EC%A7%80</link>
            <guid>https://velog.io/@zero_mountain/Error-Log-passport-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EC%9C%A0%EC%A7%80</guid>
            <pubDate>Sat, 31 Jul 2021 11:03:45 GMT</pubDate>
            <description><![CDATA[<p>passport를 사용해 로그인 상태를 유지하는 작업을 하려고 코드를 짜고, 브라우저를 통해서 로그인이 유지 되는지 실행해 봤을때, 다음과 같은 오류가 발생했다.</p>
<hr>
<p><img src="https://images.velog.io/images/zero_mountain/post/9ba197e2-2a8d-489f-a08c-7c7df5af6c70/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-31%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%207.52.03.png" alt=""></p>
<hr>
<p><code>_context11.t0.reponse is undefined</code>라는 처음 보는 오류가 로깅되서 당황스러웠다.</p>
<p>어쨋든, 로그인 상태를 유지하기 위해 서버로 GET 요청을 보내는 과정에서 오류가 발생했음을 확인할 수 있어서, 서버 로그를 확인해 보았다.</p>
<p>서버 로그를 확인해 보니, 다음과 같은 에러 메시지를 확인할 수 있었다.</p>
<hr>
<p><img src="https://images.velog.io/images/zero_mountain/post/24009048-bcfe-40e0-b4e6-12f74574b90d/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-31%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%207.51.23.png" alt=""></p>
<hr>
<p>유저 관련 라우터에서 user 데이터가 undefined로 들어가고 있음을 확인했고, 바로 유저 라우터를 확인해봤다.</p>
<hr>
<p><img src="https://images.velog.io/images/zero_mountain/post/331d8c7f-cc0d-4bb8-a584-daf98a76ec74/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-31%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%207.51.37.png" alt=""></p>
<hr>
<p>유저 라우터를 확인해보니, 유저 테이블을 조회하는 조건 부분에서 <code>user.id</code>에 접근할 수 없음을 확인했다.</p>
<p>passport를 통해서 로그인을 유지하게되면, 각 요청마다 <code>deserializeUser</code> 함수가 실행되면서 <code>request</code>에 유저 정보를 생성하기 때문에, <code>req.user</code>로 접근해야 유저의 정보를 확인할 수 있었다. </p>
<p>해당 조건 부분을 <code>id: req.user.id</code>로 수정하니, 유저 정보를 지속적으로 가지고 와서 성공적으로 로그인을 유지할 수 있었다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Error Log] 회원가입]]></title>
            <link>https://velog.io/@zero_mountain/Error-Log-%ED%9A%8C%EC%9B%90%EA%B0%80%EC%9E%85</link>
            <guid>https://velog.io/@zero_mountain/Error-Log-%ED%9A%8C%EC%9B%90%EA%B0%80%EC%9E%85</guid>
            <pubDate>Fri, 30 Jul 2021 07:19:59 GMT</pubDate>
            <description><![CDATA[<p>express 서버에서 회원가입 라우터를 만들고, 브라우저에서 회원가입을 시도했을 때, 다음과 같은 오류가 발생했다.</p>
<hr>
<p><img src="https://images.velog.io/images/zero_mountain/post/bcc5ca41-4c89-41ce-b580-db769d6c0108/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-29%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2010.00.28.png" alt=""></p>
<hr>
<p>에러 메시지를 살펴보니, 테이블에서 조회하는 로직에서 문제가 발생했음을 알았고 findOne 메서드의 아규먼트가 객체 형태이어야 하는데, 객체 형태가 아니라는 것을 알 수 있었다.</p>
<hr>
<ul>
<li>에러를 발생시킨 코드
<img src="https://images.velog.io/images/zero_mountain/post/51ddbf6c-0b64-4e3b-b8ed-5bcaa4663f5b/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-29%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2010.01.00.png" alt=""></li>
</ul>
<hr>
<ul>
<li>정상적으로 동작하는 코드
<img src="https://images.velog.io/images/zero_mountain/post/fdfdd7fd-1001-4a28-8639-a092ec576d02/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-30%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.07.41.png" alt=""></li>
</ul>
<hr>
<p><code>req.body</code>에서 username을 받아서, 이를 통해 유저 테이블을 조회하도록 로직을 설계했었는데</p>
<p>findOne 메서드에서의 option 객체 사용법을 망각하고, username이라는 value값을 그대로 </p>
<p>findOne의 아규먼트로 넣어주어서 에러가 발생했던 것이다.<code>where</code> 옵션을 추가해 객체 형태로 넣어주니 에러가 해결되었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SWIFT] function 이해하기]]></title>
            <link>https://velog.io/@zero_mountain/SWIFT-function-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@zero_mountain/SWIFT-function-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 29 Jul 2021 05:00:36 GMT</pubDate>
            <description><![CDATA[<p>swift에서 함수는 <code>func &lt;함수명&gt;(arg1:type, arg2:type, ...){statement}</code>의 형태를 갖는다. </p>
<p>중요한건 함수의 반환값이 있을 경우 형태가 좀 다르다. 이는 다음과 같다.</p>
<p><code>func &lt;함수명&gt;(arg1:type, arg2:type,...) -&gt; &lt;return type&gt;{statment}</code></p>
<p>함수의 반환값이 있는 경우, 반환값의 타입도 설정해주고 이 함수는 반환값이 있다는 표시로 화살표를 사용한다.</p>
<p>그럼, swift에서 함수의 사용 예를 살펴보겠다.</p>
<ul>
<li>이름을 출력하는 함수<pre><code class="language-swift">func printName(firstName: String, lastName: String) {
print(&quot;나의 이름은 \(firstName) \(lastName)&quot;)
}
</code></pre>
</li>
</ul>
<p>printName(firstName: &quot;David&quot;, lastName: &quot;Son&quot;)
printName(firstName: &quot;chris&quot;, lastName: &quot;evans&quot;)</p>
<pre><code>
- 이름을 출력하는 함수 (external name없이 사용)
```swift
func printName(_ firstName: String, _ lastName: String) {
  print(&quot;나의 이름은 \(firstName) \(lastName)&quot;)
}

printName(&quot;David&quot;, &quot;Son&quot;)
printName(&quot;chris&quot;, &quot;evans&quot;)</code></pre><ul>
<li>이름을 반환하는 함수<pre><code class="language-swift">func printName(_ firstName: String, _ lastName: String) -&gt; String {
let fullName = &quot;\(firstName) \(lastName)&quot;
return fullName
}
</code></pre>
</li>
</ul>
<p>let son = printName(&quot;David&quot;, &quot;Son&quot;)
let capt = printName(&quot;chris&quot;, &quot;evans&quot;)</p>
<p>print(son)
print(capt)</p>
<pre><code>
- 파라미터에 기본값을 설정하는 함수
  - `price`에 기본값 1500을 설정, `price`를 인자로 넣어 주지 않으면 기본값으로 함수 실행
```swift
func printTotalPriceWithDefaultValue(price: Int = 1500, count: Int) {
    print(&quot;Total Price: \(price * count)&quot;)
}

printTotalPriceWithDefaultValue(count: 20)</code></pre><ul>
<li>overloading: 같은 함수의 이름을 갖지만 인풋과 아웃풋이 다른 경우에 사용<ul>
<li><code>nil</code>: 값이 없다는 의미<pre><code class="language-swift">func printName(firstName: String, lastName: String) {
print(&quot;나의 이름은 \(firstName) \(lastName)&quot;)
}
</code></pre>
</li>
</ul>
</li>
</ul>
<p>func printName(firstName: nil, lastName: String) {
  print(&quot;나의 이름은 (lastName)&quot;)
}</p>
<p>printName(firstName: nil, lastName: &quot;Son&quot;)
printName(firstName: &quot;chris&quot;, lastName: &quot;evans&quot;)</p>
<pre><code>
- `in-out` 파라미터
  - 파라미터로 들어오는 변수를 함수 내부에서 직접 변경하고 싶은 경우 사용
```swift
var value = 3
func incrementAndPrint(_ value: inout Int) { // parameter는 상수취급되기 때문에, In-out 키워드 사용
    value += 1
    print(value)
}

incrementAndPrint(&amp;value)</code></pre><ul>
<li>함수를 파라미터로 받는 경우<pre><code class="language-swift">func add(_ a: Int, _ b: Int) -&gt; Int {
  return a + b
}
func subtract(_ a: Int, _ b: Int) -&gt; Int {
  return a - b
}
func multiple(_ a: Int, _ b: Int) -&gt; Int {
  return a * b
}
func divide(_ a: Int, _ b: Int) -&gt; Int {
  return a / b
}
</code></pre>
</li>
</ul>
<p>func printResult(_ function:(Int, Int) -&gt; Int, _ a: Int, _ b: Int) {
   let result = function(a,b)
   print(result)
}</p>
<p>printResult(add, 4, 10)
printResult(subtract, 50, 10)
printResult(multiple, 3, 5)
printResult(devide, 100, 25)
```</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SWIFT] Flow Control의 이해]]></title>
            <link>https://velog.io/@zero_mountain/SWIFT-Flow-Control%EC%9D%98-%EC%9D%B4%ED%95%B4</link>
            <guid>https://velog.io/@zero_mountain/SWIFT-Flow-Control%EC%9D%98-%EC%9D%B4%ED%95%B4</guid>
            <pubDate>Wed, 28 Jul 2021 04:58:54 GMT</pubDate>
            <description><![CDATA[<p>Flow Control이란 - 어떤 작업을 반복해서 수행하거나, 특정 조건에만 수행하도록 동작하도록 하는 기법(?)을 말한다. 즉, <strong>반복문과 조건문</strong>이다.</p>
<h1 id="swift-조건문">SWIFT: 조건문</h1>
<p>먼저, Swift에서 조건문이 어떻게 사용되는지 코드 예제를 통해서 살펴보겠다. </p>
<ul>
<li><code>if-else-</code></li>
</ul>
<pre><code class="language-swift">var num = 10
if num &lt; 10 {
  print(&quot;10보다 작은 수입니다.&quot;)
} else {
  print(&quot;10보다 크거나 같은 수 입니다.&quot;)
}</code></pre>
<ul>
<li><p><code>switch</code></p>
<pre><code class="language-swift">var num = 10
switch num {
case _ where num % 2 == 0:
  print(&quot;짝수입니다.&quot;)
default:
  print(&quot;홀수입니다.&quot;)
}</code></pre>
</li>
<li><p><code>3항연산자</code></p>
<pre><code class="language-swift">var num = 10
var checkEvenNum: String = num % 2 == 0 ? &quot;짝수입니다.&quot; : &quot;짝수가 아닙니다.&quot;
var checkOddNum: String = num % 2 != 0 ? &quot;홀수입니다.&quot; : &quot;홀수가 아닙니다.&quot;</code></pre>
</li>
</ul>
<p>Swift에서 주로 사용되는 조건문 3가지 예제 코드를 살펴보면, 다른 프로그래밍 언어의 조건문과 크게 다르지 않다고 느껴진다. </p>
<p>다만, 생소한 것이라면 <code>where</code>라는 키워드를 사용해서 조건문 안에서 다른 조건을 추가해 줄 수 있다는 것이다.</p>
<p><code>where</code> 조건절은 반복문에서도 사용이 가능해, 반복문에서도 심심찮게 등장하므로 기억해야할 항목이다.</p>
<h1 id="swift-반복문">SWIFT: 반복문</h1>
<p>다음으로, swift에서 반복문이 어떻게 사용되는지 코드 예제를 통해서 살펴보겠다.</p>
<ul>
<li><code>for</code></li>
</ul>
<pre><code class="language-swift">var sum = 0 
for i in 0...10 {
  sum += i
}
print(sum) // 55</code></pre>
<pre><code class="language-swift">var sum = 0
for i in 0..&lt;10 {
  sum += i
}
print(sum) // 45</code></pre>
<p>swift에서는 0부터 10까지 범위를 <code>...</code>으로 표시하고 <code>..&lt;</code>은 0부터 10보다 작은 범위를 나타낸다.</p>
<ul>
<li><code>while</code></li>
</ul>
<pre><code class="language-swift">var num = 0
while num &lt; 10 {
  print(num) 
  num += 1
}
print(&quot;num = \(num)&quot;) // 10</code></pre>
<pre><code class="language-swift">var num = 0
repeat {
  print(num)
  num += 1
} while num &lt; 10
print(&quot;num = \(num)&quot;)</code></pre>
<p><code>repeat-while-</code> 문법은 자바스크립트의 <code>do-while-</code> 문법과 동일하게 동작한다. </p>
<p>작업 수행을 먼저하고, 조건을 검사하기 때문에, 조건 검사를 먼저하는 while문과 다르게 동작할 수 있을 여지가 있기 때문에, 이 점을 숙지하고 사용하도록 하자.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Error Log] redux reducer function]]></title>
            <link>https://velog.io/@zero_mountain/Error-Log-redux-reducer-function</link>
            <guid>https://velog.io/@zero_mountain/Error-Log-redux-reducer-function</guid>
            <pubDate>Tue, 27 Jul 2021 08:56:16 GMT</pubDate>
            <description><![CDATA[<p>클라이언트와 게시물 CRUD API를 연결하기 전,</p>
<p>더미데이터로 게시물 추가가 잘 되는지 확인하던 도중에 다음 오류가 발생했다. </p>
<p><img src="https://images.velog.io/images/zero_mountain/post/83d6f27b-cd81-478f-bbc6-d6e4bf84b455/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-27%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%205.41.37.png" alt=""></p>
<p>오류 메시지만 봐도 왜 오류가 발생했는지 알 수 있었다.</p>
<p><code>immer</code> 라이브러리를 사용해 게시물 reducer 함수를 작성한 부분에서</p>
<p>그 중에서 게시물을 추가하는 로직의 코드에서 오타가 발견됐다.</p>
<p>unshift가 unshif로 작성되어 있었다.</p>
<p>제대로 썻다가 실수로 t만 지워진건지 모르겠지만...  <strong>역시 자나 깨나 오타 조심이다.</strong></p>
<ul>
<li><p>오타 수정 전
<img src="https://images.velog.io/images/zero_mountain/post/319dbd65-5f3b-43ed-a7ee-d84edbe74f32/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-27%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%205.52.39.png" alt=""></p>
</li>
<li><p>오타 수정 후
<img src="https://images.velog.io/images/zero_mountain/post/7d6ebdd2-69ae-409a-a013-a43a6d50dd2a/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-27%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%205.52.51.png" alt=""></p>
</li>
</ul>
<p>코드 수정 후에는 게시물 추가도 잘 되고, 오류도 사라졌다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Error Log] async await]]></title>
            <link>https://velog.io/@zero_mountain/Error-Log-async-await</link>
            <guid>https://velog.io/@zero_mountain/Error-Log-async-await</guid>
            <pubDate>Mon, 26 Jul 2021 02:08:33 GMT</pubDate>
            <description><![CDATA[<p>프론트에서 백엔드 로그인 API와 연결하는 중에 다음 오류가 발생했다. </p>
<p><img src="https://images.velog.io/images/zero_mountain/post/c68c5581-e842-498f-821e-a0270a4d9736/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-25%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2010.16.21.png" alt=""></p>
<p>오류를 보면, 서버로부터 데이터를 못가져오는 것을 확인할 수 있었다.</p>
<p>서버 코드를 살펴보니, 로그인 API를 작성한 부분에서 비동기로 처리되는 부분에 await을 사용해주지 않아서 해당 코드가 실행이 되지 않았다.</p>
<p><img src="https://images.velog.io/images/zero_mountain/post/5c8b0345-4d23-4430-a945-2a7bcee76225/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-25%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2010.38.00.png" alt=""></p>
<p>비밀번호를 제외한 유저 정보를 가져오기 위해서, <code>User</code>테이블에서 findOne 메서드를 사용해서 데이터를 가지고 오도록 코드를 작성했는데, 이 부분이 비동기로 처리됐던 부분인데, await을 사용해주지 않아서 <code>response.data</code> 에 데이터가 빈 객체로 들어간채로 프론트로 넘어가고 있었다.</p>
<p><img src="https://images.velog.io/images/zero_mountain/post/1036a232-c23f-4d2d-b976-065fb7829043/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-26%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%2010.56.49.png" alt=""></p>
<p>다음과 같이 <code>await</code>을 추가해주니, 오류가 깔끔하게 해결됐다.</p>
<blockquote>
<h1 id="async-----await----">async --- await ---</h1>
<p>기본적으로, <code>async/await</code> 문법은 비동기처리를 동기적으로 처리하기 위해서 사용한다. 
기존 <code>Promise</code> + <code>then</code> 으로 비동기 로직을 동기적으로 처리하던 방법에서 조금 더 직관적이고 체이닝으로 인한 코드의 길이가 길어지는 단점을 해결할 수 있다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SQL] GROUP BY]]></title>
            <link>https://velog.io/@zero_mountain/SQL-GROUP-BY</link>
            <guid>https://velog.io/@zero_mountain/SQL-GROUP-BY</guid>
            <pubDate>Sun, 25 Jul 2021 12:02:27 GMT</pubDate>
            <description><![CDATA[<h2 id="group-by">GROUP BY</h2>
<ul>
<li>공통된 row 값으로 그룹을 지을 때 사용</li>
<li>COUNT, MAX, MIN, SUM, AVG와 같은 집계 함수와 자주 사용</li>
<li><a href="https://www.w3schools.com/mysql/mysql_groupby.asp">W3S</a><pre><code>SELECT column_name(s)
FROM table_name
WHERE condition
GROUP BY column_name(s)
ORDER BY column_name(s);</code></pre><h3 id="프로그래머스">프로그래머스</h3>
<pre><code>SELECT ANIMAL_TYPE, COUNT(ANIMAL_ID) as &#39;COUNT&#39; 
FROM ANIMAL_INS
WHERE ANIMAL_TYPE = &#39;Cat&#39; or ANIMAL_TYPE = &#39;Dog&#39;
GROUP BY ANIMAL_TYPE
ORDER BY ANIMAL_TYPE ASC;
</code></pre></li>
</ul>
<pre><code>- 고양이와 개를 그룹화해서 각 동물의 수를 카운트
  - 고양이가 개보다 먼저 조회되도록
</code></pre><p>SELECT NAME, COUNT(ANIMAL_ID) as &#39;count&#39; 
FROM ANIMAL_INS
GROUP BY NAME 
HAVING COUNT(NAME) &gt; 1
ORDER BY NAME ASC;</p>
<pre><code>- 이름이 두 번 이상 사용된 이름과 회수 조회
  - 이름 순으로 정렬
  - HAVING: 집계 함수와 함께 WHERE 구문을 사용할 수 없기 때문에 HAVING 구문을 통해서 조건을 설정
</code></pre><p>SELECT HOUR(DATETIME) as &#39;HOUR&#39;, COUNT(HOUR(DATETIME)) as &#39;COUNT&#39; 
FROM ANIMAL_OUTS
WHERE HOUR(DATETIME) between 9 and 20
GROUP BY HOUR
ORDER BY HOUR;</p>
<pre><code>- 시간대 별로 입양 시간대와 입양 회수 조회
  - 9시 부터 19시 59분 까지
  - HOUR: HOUR 함수를 사용하면 DATETIME에서 시간대만 추출할 수 있음
</code></pre><p>SET @hour := -1;</p>
<p>SELECT (@hour := @hour + 1) as HOUR,
(SELECT COUNT(*) FROM ANIMAL_OUTS WHERE HOUR(DATETIME) = @hour) as COUNT
FROM ANIMAL_OUTS
WHERE @hour &lt; 23;</p>
<pre><code>- 시간대 별로 입양 시간대와 입양 회수 조회
  - 0시 부터 23시 59분 까지
  - SET: SQL에서 변수 할당 예약어(@변수명)
  - 1행: 0부터 23까지 시간대를 조회하겠음(재할당: @hour + 1)
  - 2행: 테이블에서 시간대별로 모두 카운트한걸 COUNT라 칭하고 조회하겠음
  - 3행: ANIMAL_OUTS 테이블에서 조회하겠음
  - 4행: @hour는 23보다 작음</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[Error Log] redux-saga]]></title>
            <link>https://velog.io/@zero_mountain/Error-Log-redux-saga</link>
            <guid>https://velog.io/@zero_mountain/Error-Log-redux-saga</guid>
            <pubDate>Sat, 24 Jul 2021 11:02:03 GMT</pubDate>
            <description><![CDATA[<p>프론트서버와 백엔드서버를 연결해 회원가입을 구현하는 중에 다음과 같은 오류가 발생했다.</p>
<p><img src="https://images.velog.io/images/zero_mountain/post/40a5ad83-e4db-46ac-9f92-1ace04e72107/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-24%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%207.51.16.png" alt=""></p>
<p>오류 코드를 살펴보면, <code>user.js</code>라는 파일에서 문제가 발생한다는 것을 확인할 수 있었다.</p>
<p><code>user.js</code>는 유저 데이터를 서버로 넘기기 위해서 리덕스 사가를 사용해 코드를 작성했다.</p>
<p>해당 파일을 확인해보니, 다음과 같이 코드가 작성된 것을 발견했다.</p>
<p><img src="https://images.velog.io/images/zero_mountain/post/65f24087-dd81-4ab8-bde4-cabf7e8db98f/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-24%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%207.54.26.png" alt=""></p>
<p>사가의 call의 아규먼트로 API를 호출하는 함수를 <code>action.data</code>를 아규먼트로 전해주면서 함수를 바로 실행하도록 코드를 짰기 때문에 오류가 발생한 것으로 추측됐다. (이 부분은 조금 더 학습이 필요해보임_)</p>
<p>구글링 해보니, call 이펙트 함수는 실행 함수와 실행 함수의 아규먼트를 call의 아규먼트로 각각 넣어 줘야 했다.</p>
<p><img src="https://images.velog.io/images/zero_mountain/post/900fcaf1-4513-46c2-9658-ff917c41e2f5/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-24%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%208.00.40.png" alt=""></p>
<p>코드를 다음과 같이 수정해주니, 타입 에러 오류가 깔끔하게 해결되었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Error Log] Facebook passport 인증 오류]]></title>
            <link>https://velog.io/@zero_mountain/Error-Log-Facebook-passport-%EC%9D%B8%EC%A6%9D-%EC%98%A4%EB%A5%98</link>
            <guid>https://velog.io/@zero_mountain/Error-Log-Facebook-passport-%EC%9D%B8%EC%A6%9D-%EC%98%A4%EB%A5%98</guid>
            <pubDate>Fri, 23 Jul 2021 12:24:35 GMT</pubDate>
            <description><![CDATA[<p>페이스북 로그인, 회원가입 구현 중 다음 오류를 만나게 되었다.</p>
<p><code>TypeError: OAuth2Strategy requires a verify callback</code></p>
<ul>
<li>오류가 발생한 코드<pre><code class="language-js">passport.use(
new FacebookStrategy({
  // appId와 secretId 발급
  clientID: process.env.FACEBOOK_APPID,
  clientSecret: process.env.FACEBOOK_SECRETCODE,
  callbackURL: `${process.env.SITE_DOMAIN}/auth/facebook/callback`,
  profileFields: [&quot;id&quot;, &quot;displayName&quot;, &quot;photos&quot;, &quot;email&quot;],
}),
async (accessToken, refreshToken, profile, done) =&gt; {
  console.log(accessToken, refreshToken);
  console.log(profile);
}
);
</code></pre>
</li>
</ul>
<p>router.get(&quot;/facebook&quot;, passport.authenticate(&quot;facebook&quot;, { scope: &quot;email&quot; }));</p>
<p>router.get(
  &quot;/facebook/callback&quot;,
  passport.authenticate(&quot;facebook&quot;, {
    successRedirect: &quot;/auth/facebook/success&quot;,
    failureRedirect: &quot;/auth/facebook/fail&quot;,
  })
);</p>
<p>router.get(&quot;/facebook/success&quot;, (req, res) =&gt; {
  res.send(req.user);
});</p>
<p>router.get(&quot;/facebook/fail&quot;, (req, res) =&gt; {
  res.send(&quot;facebook login fail&quot;);
});</p>
<p>```</p>
<p>페이스북 로그인 전략이 성공할 경우에 request 유저 정보를 json으로 출력되도록했고</p>
<p>실패하면 <code>facebook login fail</code> 이라는 메시지를 반환하도록 했다.</p>
<p>오류 메시지 상으로는 콜백 URL 문제가 있는거 같아서 여러 가지 시도를 해봤지만</p>
<p>해결이 안되서 해당 오류를 구글링했다. </p>
<p><img src="https://images.velog.io/images/zero_mountain/post/7aa26993-dfd5-443f-bad1-00cd02ebe6af/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-23%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%209.16.58.png" alt=""></p>
<p>검색을 통해서 다음 스택오버플로우 질문을 확인할 수 있었다.</p>
<p>해당 페이지에 접속해 답변 내용을 확인해 보았다.</p>
<p><img src="https://images.velog.io/images/zero_mountain/post/8d4057e5-2278-46d9-aea7-09b27cd3ba20/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-07-23%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%209.19.02.png" alt=""></p>
<p>다음과 같이 해당 질문자가 <code>FacebookStrategy</code>의 아규먼트로 객체와 콜백함수를 줘야 하는데,</p>
<p>처음과 같이 객체만 아규먼트로 주고, 콜백함수를 <code>passport.use</code>의 아규먼트로 주었다고  답변을 해줬다.</p>
<p>확인해보니 내가 작성한 코드에서도 같은 실수를 발견할 수 있었다.</p>
<p>긴 삽질의 시간을 갖게될 뻔했는데, 역시 스택오버플로우는 the love이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[node] express + OOP]]></title>
            <link>https://velog.io/@zero_mountain/node-express-OOP</link>
            <guid>https://velog.io/@zero_mountain/node-express-OOP</guid>
            <pubDate>Thu, 22 Jul 2021 06:19:45 GMT</pubDate>
            <description><![CDATA[<p>express 서버에 OOP를 적용해 보자.</p>
<ul>
<li>먼저 app과 server로 파일을 나누어서<ul>
<li>app.js 파일은 온전히 application 객체를 만드는데 집중하고</li>
<li>server.js 파일은 서버의 실행만 담당하도록 코드를 작성한다.</li>
</ul>
</li>
</ul>
<pre><code class="language-js">// app.js
const express = require(&quot;express&quot;);
const logger = require(&quot;morgan&quot;);
const cookieParser = require(&quot;cookie-parser&quot;);

const db = require(&quot;./models&quot;);

class App {
  constructor() {
    this.app = express();
    this.dbConnection();
    this.setMiddleWare();
    this.getRouting();
  }

  dbConnection() {
    db.sequelize
      .authenticate()
      .then(() =&gt; {
        console.log(&quot;서버와 연결 성공&quot;);
        return db.sequelize.sync();
      })
      .then(() =&gt; {
        console.log(&quot;Sync 완료&quot;);
      })
      .catch((err) =&gt; {
        console.error(&quot;DB와 연결할 수 없음:&quot;, err);
      });
  }

  setMiddleWare() {
    this.app.use(logger(&quot;dev&quot;));
    this.app.use(express.json()); 
    this.app.use(express.urlencoded({ extended : true})); 
    this.app.use(cookieParser());
  }

  getRouting() {
    this.app.use(require(&quot;./controllers&quot;));
  }
}

module.exports = new App().app;
</code></pre>
<p><code>App.js</code>에서 사용할 모듈들을 <code>require</code>하고</p>
<p>class를 사용해 application 객체를 생성할 준비를 한다.</p>
<p><code>construct</code> 생성자 함수에 서버 실행 시 사용할 메서드와 application을 초기화한다.</p>
<ul>
<li><strong>dbConnection</strong>: 서버와 데이터베이스의 연결을 담당하는 메서드</li>
<li><strong>setMiddleware</strong>: application에서 사용할 미들웨어들을 정의하는 메서드<ul>
<li>앞으로 사용할 미들웨어 모듈은 이 메서드에서 등록해줘야 함</li>
</ul>
</li>
<li><strong>getRouting</strong>: 분기 처리된 라우팅 미들웨어를 가져오는 메서드</li>
</ul>
<p>마지막으로 클래스로 정의한 객체를 모듈화 해준다.</p>
<blockquote>
<p>함수는 호이스팅이 발생하기 때문에 함수를 호출하는 코드가 함수 선언 코드보다 먼저 나와도 괜찮지만, 클래스로 객체를 생성할 때는 호이스팅이 발생하지 않기 때문에 항상 클래스 선언이 먼저 되어야 한다. </p>
</blockquote>
<pre><code class="language-js">// server.js
const app = require(&quot;./app.js&quot;);
const port = 3000;

app.listen(port, function () {
  console.log(&quot;Express listening on port&quot;, port);
});
</code></pre>
<p><code>server.js</code> 파일은 위 코드와 같이 application 서버를 실행하는데만 집중한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SQL] MIN/MAX/COUNT/DISTINCT]]></title>
            <link>https://velog.io/@zero_mountain/SQL-MINMAXCOUNTDISTINCT</link>
            <guid>https://velog.io/@zero_mountain/SQL-MINMAXCOUNTDISTINCT</guid>
            <pubDate>Wed, 21 Jul 2021 07:59:07 GMT</pubDate>
            <description><![CDATA[<h1 id="min">MIN</h1>
<ul>
<li><p>테이블의 해당 컬럼의 최소값을 구할 때 사용</p>
</li>
<li><p>W3Schools</p>
<pre><code>SELECT MIN(column_name)
FROM table_name
WHERE condition;</code></pre></li>
<li><p>프로그래머스</p>
<ul>
<li>출력값: 보호동물 테이블에서 보호소에 가장 최근에 들어온 시간을 조회<pre><code>SELECT MAX(DATETIME) FROM ANIMAL_INS</code></pre></li>
</ul>
</li>
</ul>
<h1 id="max">MAX</h1>
<ul>
<li><p>테이블의 해당 컬럼의 최대값을 구할 때 사용</p>
</li>
<li><p>W3Schools</p>
<pre><code>SELECT MAX(column_name)
FROM table_name
WHERE condition;</code></pre></li>
<li><p>프로그래머스</p>
<ul>
<li>출력값: 보호 동물 테이블에서 보호소에 가장 먼저 들어온 시간을 조회<pre><code>SELECT MIN(DATETIME) FROM ANIMAL_INS</code></pre></li>
</ul>
</li>
</ul>
<h1 id="count">COUNT</h1>
<ul>
<li><p>테이블의 해당 칼럼의 갯수를 구할 때 사용</p>
</li>
<li><p>W3Schools</p>
<pre><code>SELECT COUNT(column_name)
FROM table_name
WHERE condition;</code></pre></li>
<li><p>프로그래머스</p>
<ul>
<li>보호 동물 테이블에서 보호 중인 동물의 수를 조회<pre><code>SELECT COUNT(ANIMAL_TYPE) FROM ANIMAL_INS</code></pre></li>
</ul>
</li>
</ul>
<h1 id="distinct">DISTINCT</h1>
<ul>
<li>테이블의 중복되는 칼럼을 제거할 때 사용</li>
</ul>
<pre><code>SELECT DISTINCT(column_name)
FROM table_name</code></pre><ul>
<li>프로그래머스<ul>
<li>보호 동물 테이블에서 이름이 없거나 중복되는 이름을 1개로 통합한 동물 이름의 개수 조회<pre><code>SELECT COUNT(DISTINCT(NAME)) FROM ANIMAL_INS
WHERE NAME IS NOT NULL</code></pre></li>
</ul>
</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>