<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>jhyeon.log</title>
        <link>https://velog.io/</link>
        <description>The only thing that interferes with my learning is my education.</description>
        <lastBuildDate>Sun, 17 Jan 2021 06:37:21 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>jhyeon.log</title>
            <url>https://images.velog.io/images/open_h/profile/48c17ca4-23d7-4477-bb0d-55bbabbc4e4a/J.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. jhyeon.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/open_h" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[글을 쓰는 이유에 관한 글]]></title>
            <link>https://velog.io/@open_h/why-i-do-writing</link>
            <guid>https://velog.io/@open_h/why-i-do-writing</guid>
            <pubDate>Sun, 17 Jan 2021 06:37:21 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>팀 프로젝트로 마켓컬리 클론을 하고 <a href="https://velog.io/@open_h/team-project1">후기</a>를 남겼었다. 마찬가지로 당근 마켓 모바일 앱 클론 팀 프로젝트 후기를 쓰다가 문득 왜 쓰고 있는가 의문이 들었다. 그래서 <strong>왜 후기를 쓰는지, 회고록을 남기는지 본질적인 이유</strong>에 생각해보았다. 앞으로 후기와 회고를 작성할 때 그것이 가지는 힘과 필요성을 이해한 채로 글을 쓰고 싶었다.</p>
</blockquote>
<p>후기나 회고란 (당연하게도) 누가 강요해서 쓰는 것이 아니다. 내 경우에는 글을 쓰는 것 자체를 엄청 즐기는 사람이라서 후기를 쓰는 것도 아니다. 그런데도 나는 왜 후기 혹은 어떤 글을 왜 쓰고 있다. 글을 쓰는 이유는 사람마다 조금씩 차이가 있을 수 있겠지만 어느정도 공통 이유가 있을 것이라 예상된다. </p>
<p>글을 많이 써보지도 않았고, 잘 쓰지도 못하는 내가 왜 회고록을 쓰고 있는걸까? 불필요한 행동을 굳이 시간을 들여 이유도 모른채 하고 싶지는 않았다. 그래서 이번 글을 작성하며 후기와 회고록 혹은 포괄적으로 &#39;글&#39;을 왜 써야하는가에 대해 스스로 정리해보았다.</p>
<p><em>아래 글은 온전히 저의 개인적인 견해입니다. 어떠한 근거를 기반하여 쓴 글이 아닙니다.</em></p>
<h1 id="기억의-한계-극복하기">기억의 한계 극복하기</h1>
<p>어떤 경험을 하고 나서 그것에 대해서 생각해보거나 기록으로 남기지 않으면 나중에는 그 경험이 그냥 누구에게 들은 이야기처럼 흐릿해진다. 여행 중 사진을 한장도 찍지 않았다면 그 여행은 머릿속에만 있는, 서서히 잊혀가는 기억일 것이다. 그러나 여행에 대해 글을 쓰거나 여행에서 찍은 사진이나 영상을 나중에 다시 보게 되었을 때 그 때의 기억과 감정이 떠오르게 된다. &#39;인간은 결국 똑같은 실수를 반복하기 마련&#39;이라는 말도 결국 자신이 겪은 중요한 경험에 대해 되돌아보거나 기록하는 과정을 거치지 않았기 때문일 것이다.</p>
<h3 id="망각-곡선">망각 곡선</h3>
<p>기억력과 관련해서 에빙하우스의 망각 곡선 가설이 잘 알려져 있다. 학습한 내용을 <strong>의식적으로 복습을 하지 않으면</strong> 시간이 지남에 따라 빠르게 잊게 된다는 것이다. 반면 의식적인 복습이 있을 때마다 배운 지식 중 많은 부분을 더 오래 기억한다는 것이다. 내가 대학생 때 들은 과목들 중 기초과목, 기반과목의 경우 학교에서 여러 수업을 듣다보면 어쩔 수 없이 복습하게 되어 지금까지도 잘 기억한다. 하지만 벼락치기를 했거나 그 당시 잠깐 공부했던 과목은 지금 거의 기억하지 못한다.</p>
<p><img src="https://www.researchgate.net/profile/Bo_Ae_Chun/publication/324816198/figure/fig1/AS:620205050982405@1524879815703/Ebbinghaus-forgetting-curve-and-review-cycle.png" alt="Ebbinghaus&#39; forgetting curve and review cycle. | Download Scientific Diagram"></p>
<p>기억이라는 것은 지식에 국한되지 않는다. 자기계발도서를 그냥 만화책 읽듯이 슥 읽고 넘어간다면 &#39;자기계발&#39;에 전혀 도움이 되지 않을 것이다. 마찬가지로 나에게 중요했던 프로젝트를 하고 나서 느낀 점이 무엇인지 기록하지 않거나 다시 상기시키며 생각으로라도 정리해보지 않는다면 내가 한 팀 프로젝트는 그냥 &#39;개발자로서 성장했던 즐거운 경험이었어!&#39; 정도로 남게된다.</p>
<p>그렇다고 모든 경험을 다 기록하겠다는 뜻은 아니다. 또한 같은 경험이라도 사람마다 다르게 느낄 수 있고, 개개인의 가치관은 다르기 때문에 오래 기억하고 싶은 것이 다를 수도 있다. 예를 들어 내게 두 번의 팀 프로젝트는 배운것도 많고 기억하고 싶은 것도 많기에 다양한 측면에서 나에게 굉장히 소중한 경험이었다. 그렇기에 경험에 대해 회고를 하며 후기를 남기는 것이다.</p>
<blockquote>
<p>내게 의미있는 경험, 기억 그리고 배움을 정리하거나 기록할 수 있다. 간단한 일기를 쓸 수도 있겠고, 회고록의 형태로 글을 쓸 수도 있다. 기록은 그자체로서 가치도 있지만 기록하는 과정에서 머릿속에 배움이 좀 더 정확하고 오래 남게 된다.</p>
</blockquote>
<h1 id="회고로부터-새로운-배움">회고로부터 새로운 배움</h1>
<p>과거에 얽매이는 것은 좋지 않지만, 배울 것이 있다면 과거에 대해 짚고 넘어가는 것이 좋다. 프로젝트를 경험하고 나서 그 당시에 느낀 것도 있지만 <strong>회고하고 다시 생각 정리를 하면서 새롭게 느끼고 배우는 것들도 많다.</strong> 그 당시에 집중하느라 큰 그림을 보고 있지 못하다가 끝나고 되돌아보니 새로 알게되는 것들이 있다는 뜻이다.</p>
<h3 id="회고하는-것도-결국-절대적인-시간을-쓰는-일이다">회고하는 것도 결국 절대적인 시간을 쓰는 일이다</h3>
<p>첫 팀프로젝트였던 마켓컬리 클론 프로젝트가 끝나고 회고록을 쓰고 있는 주말 오후였다. 프로젝트가 어땠는지, 무엇이 아쉬웠는지, 무엇을 배웠는지 등을 정리하고 있었다. 왜 팀프로젝트에서 소통이 중요한지 몸소 느꼈다는 것이 큰 경험 중 하나였다. 그러나 프로젝트 당시에는 <strong>&#39;소통이 중요하다&#39;</strong>라고 느끼기만 하고, 다음에는 어떻게 더 <strong>소통을 잘 할 수 있을지</strong>에 대한 고민은 깊게 하지 못했다. 짧은 프로젝트 기간에 개발적으로 굉장히 몰두했던 기간이라 그런 생각을 할 틈이 없었다. </p>
<blockquote>
<p>즉, 소통의 중요성과 소통으로 인해 발생한 문제를 인지하기만 하고 해결책을 생각해보지 않은 것이다.</p>
</blockquote>
<p>그 당시에 소통을 더 잘할 방법에 대해 생각해보지 않았다는 것이 잘못된 것이 절대 아니다. 내가 하고 싶은 말은 <strong>회고를 하면서 당시 발생한 문제와 해결하지 못했던 것들에 대한 해결책을 생각해볼 수 있다</strong>는 것이다. 프론트와 백엔드가 회의할 때는 어떤 이야기를 해야 하는지. 프론트팀 내부에서 회의할 때는 어떤 소통을 해야 하는지. 어떤 식으로 말해야 내가 하고자 하는 말이 전달되는지. 각자가 서로 한 일에 대해서 어느정도로 디테일하게 공유해야 하는지. </p>
<p>나의 경험과 배움에 대해 구체적으로 생각해보고 정리하며 문제가 있었다면 해결 방향에 대해서도 고민하는 행위는 <strong>절대적인 시간이 필요</strong>하다. 시간 투자와 결과물은 일반적으로 정직한 비례관계를 가지기 마련이다. 회고와 후기남기기라는 시간 투자에서 이루어질 수 있는 것들이 분명 존재한다. 새로운 해결 방안을 찾거나 당시에는 생각지 못했던 배움을 얻기도 한다.</p>
<p>실제로 바로 다음 프로젝트에서는 1차 프로젝트 때 배운 소통의 방법을 적용시켜 보았다. 팀 인원이 7인에서 4인으로 줄어 소통의 방식이 다른 느낌이긴 했다. 그렇지만 개발자 사이의 소통, 사람 사이의 소통에 대해 회고에서 생각한 것들을 자연스럽게 혹은 의식적으로 적용시켜볼 수 있었고 2차 프로젝트는 소통 면에서 꽤 만족했다.</p>
<h3 id="지식도-마찬가지다">지식도 마찬가지다</h3>
<p>지식에 대한 회고라고 하면 복습이라고 볼 수 있다. 새로운 지식을 처음 배울 때 신기해하며 이해했다고(착각하며) 즐거워하는 경우가 있다. 그러나 아는만큼 보인다고, 다시 한 번 그 지식을 복습하거나 다른 지식을 습득하고 다시 돌아보면 내가 이해한 것이 전부가 아니라는 것을 깨닫게 된다.</p>
<p>새롭게 배우면서 내 이전 지식이 깨지는 경우도 물론 많다. 그러나 같은 내용이라도 복습하거나 좀 더 깊이 생각하다보면 더 제대로 이해할 때가 있다. 한 번 더 생각해보고 한 번 더 되돌아볼 때 새롭게 보이는 것들이 있기 마련이다. 내가 모르는 것을 스스로 진단하고 어떤 방향으로 학습을 이어나가야 할지 진단하기에도 글쓰기는 유용한 방법이다.</p>
<h1 id="글과-기록이-가지는-힘">글과 기록이 가지는 힘</h1>
<p>이 항목은 회고가 가지는 힘이라기보다 글이라는 기록 방법 자체가 가진 힘이라고 볼 수 있다. 나는 글을 쓰는 것이 주는 장점에 대해서는 논리력을 길러준다던가 하는 이유 등 전문적으로 아는 바가 없다. 때문에 이 항목은 특히나 더 내 생각에 기반을 둔 글이다.</p>
<h3 id="자신의-생각과-지식을-정리정돈할-수-있다">자신의 생각과 지식을 정리정돈할 수 있다</h3>
<p>글을 쓴다는 것 자체로 내 자신에 대해 생각할 시간을 투자하는 것이다. 동기부여, 목표의식, 자아성찰 등의 단어는 생각으로 시작하여 글로써 정리정돈할 수 있다. 글이 아니라 대화를 통해 새로운 인사이트를 얻거나 회의를 통해 반짝이는 아이디어를 얻을 수 있다. 어느것이 더 좋다 비교하는 것은 당연히 아니다(게다가 나는 대화를 통해 새로운 인사이트를 얻는것을 매우 좋아한다). 상황에 맞는 것들이 있고 각자의 장단점이 있는 것이다.</p>
<p>글을 쓰는 많은 이유가 있겠지만, 글을 쓰다보면 정리정돈이 되는 느낌을 많이 받았다. 나는 개인적으로 내 생각을 말로 풀어내는 게 글로 쓰는 것보다 더 쉽다고 생각한다. 하지만 말로 풀어내는 것이 쉬운만큼 말하고 싶었던 포인트를 조금 빠트린다던가 제대로 설명하지 못해 아쉬운 경우가 분명 있다. 특히나 내가 애매하게 알거나 정확히 내 머릿속에서 정돈되지 않은 생각이나 지식이라면 더욱 그런 현상이 나타난다.</p>
<p>그러나 글로 풀어낼 경우 그렇지 않다. 글을 쓰다 보면 하나씩 차근차근 집고 넘어가게 된다. 내가 알고 있었다고 생각한 것이 사실 애매하게 알고 있던 것임을 글을 쓰다 보면 깨우치게 된다. 또 즉흥적으로 말하는 것보다 생각하고 정리할 시간이 주어지기에 말보다는 더 정돈된 형태로 내 생각이나 지식을 표현할 수 있게 된다. 그 과정에서 내가 모르는 것과 아는 것을 명확하게 구분할 수 있게 된다.</p>
<blockquote>
<p>게다가 글로 정리한 뒤에 말로 설명하면 정돈된 말로 표현할 수 있다!</p>
</blockquote>
<p>지식 습득을 위한 공부를 할 때 글로 정리해야 하는가에 대해선 의견이 많이 갈린다. 나는 글을 쓰는 것에 대한 신봉자는 아니기 때문에 내가 배우는 지식을 모두 정리해서 글로 써야 한다고 생각하지 않는다. 많은 상황에서 글로써 정리하는 것보다 학습에 더 효과적인 방법이 많다. 그 시간에 학습 자료를 한 번 더 복습한다거나, 문제를 푼다던가, 실제 상황에 적용시켜본다던가! 하지만 때에 따라 글로서 정리해보면 더 정리가 잘되는 경우가 있다. 안타깝지만 이건 정답이 있는 것이 아니라 개인차가 있을 수 있고 개개인에 맞게 스스로 그 노하우를 터득해야 하는 문제라고 생각한다.</p>
<h3 id="글은-정리된-기록이다">글은 정리된 기록이다.</h3>
<p>대화로서 생각을 공유하는 것은 그 고유의 독특함과 특징이 있다. 매신저로 대화하는 것과 달리 대면하여 대화할 때만 느껴지는 그 무언가가 있다. 마찬가지로 글로서 내 생각과 지식을 공유할 때도 특유의 강점이 있다. 중요한 업무는 글로써 기록을 남긴다. 구두계약보다는 글로 명시되어 있는 계약서를 당연하게 사용하고 있다.</p>
<p>다시 말해 어떤 주제에 대해서 내 생각과 내가 알고 있는 것을 최대한 빠짐없이 공유하는 데는 글이라는 매체가 효율적이라는 뜻이다. 또한 글로 남겨놓으면 나중에 내가 필요할 때 <strong>&#39;내 식으로 쓴 글&#39;</strong>이기에 내가 가장 잘 이해할 수 있다. 자바스크립트 호이스팅에 관한 블로그 글을 쓴 적이 있는데 내가 이해한 방식과 내가 선호하는 워딩으로 작성한 글이기에 다시 그 글을 찾아 보았을 때 매우 편하고 쉽게 글을 읽을 수 있었다.</p>
<h1 id="추억-팔이">추억 팔이</h1>
<p>친구들과 술자리에서 하는 추억 팔이는 언제나 즐겁다. 다음 술자리에서 같은 이야기로 우려먹어도 또 재미있다. 후기와 회고도 가끔 그런 재미가 있을 수 있다. 일상 영상을 비디오로 만들어서 유튜브에 올리는 것처럼, 그냥 즐거웠던 기억과 추억을 글로 쓴다는 측면에서 글 쓰는 것이 일상의 행복이 될수도 있다고 생각한다.</p>
<p>글을 쓰는 것 자체가 마냥 재미없고 따분하다면? 혹은 오래 기억하기 위해, 새로운 학습을 위해, 혹은 정리정돈을 위해서만 글을 쓴다면? 글 쓰는 것 자체가 불행하거나 너무 스트레스를 받는다면 쓰지 않는 것이 나을 수 있다. 글 쓰는 것을 업으로 사는 사람도 아닌데 그걸로 너무 스트레스 받으면서 붙잡고 있을 필요는 없다고 생각한다. </p>
<p>그런 의미에서 추억 팔이로서의 후기, 회고는 쓰는 것에 대한 즐거움 자체를 준다고 생각한다. 추억 팔이로서의 내용이 공유하기 어렵다면 혼자 일기처럼 적어두면 되리 것이다. 글의 일부 혹은 전체를 가볍고 재미있께 쓰는 것은 글을 쓰는 것 자체에 부담을 줄이고 재미를 높일 수 있다고 생각한다.</p>
<p>후기와 회고. 혹은 글쓰는 것 그자체. 이 일들이 재미있고 즐거운 일이 되면 정말 좋을 것이다. <strong>설령 그렇지 않다고 하더라도</strong> 내가 그렇게 즐거움을 느끼도록 <strong>내가 글의 주제 방향을 잡아서</strong> 글을 쓴다면 글 쓰는 것이 즐겁다는 그 자체로 글을 쓰는 이유가 될 수 있을 것이라 생각한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React Native 당근 마켓 클론 (3)]]></title>
            <link>https://velog.io/@open_h/React-Native-KiwiMarket-3</link>
            <guid>https://velog.io/@open_h/React-Native-KiwiMarket-3</guid>
            <pubDate>Thu, 14 Jan 2021 01:47:37 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>React Native로 2주동안 당근마켓 앱 클론 &#39;키위 마켓&#39;을 시작했다. 프론트엔드 2명과 백엔드 2명, 총 4명의 팀원이 함께 했다.</p>
</blockquote>
<h1 id="로딩-화면-구현하기">로딩 화면 구현하기</h1>
<p>내가 맡은 제품 상세 스크린의 경우 다른 API 주소에 총 3번의 요청을 보내야 했다. 처음에는 비동기로 순차적으로 그 함수들을 불렀다. 그러나 AWS 무료 플랜을 사용할 경우 속도가 매우 느리다는 문제가 있었다. 그리고 아래와 같은 로딩 화면을 구현하였다.</p>
<p><img src="https://images.velog.io/images/open_h/post/225aac5f-f96a-4692-854d-81dfdb055feb/loading%20screen.gif" alt=""></p>
<p>React Native에서 제공하는 <code>Animated</code> 와  <code>interpolate</code> 를 사용하여 키위 이미지가 돌아가게 하여 로딩 컴포넌트를 만들었다. 위에 보이는 로딩 화면은 제품 상세의 기본적인 데이터를 받아올 때까지(첫 번째 서버 요청에서 데이터를 받아올 때까지) 보여주는 화면이다. 성공할 경우 아래와 같이 화면내 돌아가는 키위가 나타나게 된다.</p>
<p><img src="https://images.velog.io/images/open_h/post/f28c9d31-de2b-4106-a395-9617ea779160/loading%20inside%20screen.gif" alt=""></p>
<pre><code class="language-jsx">const LoadingKiwi = () =&gt; {
  const [spinAnim, setSpinAnim] = useState(new Animated.Value(0));
  const spin = spinAnim.interpolate({
    inputRange: [0, 1],
    outputRange: [&quot;0deg&quot;, &quot;3600deg&quot;],
  });

  useEffect(() =&gt; {
    Animated.loop(
      Animated.timing(spinAnim, {
        toValue: 1,
        duration: 10000,
        easing: Easing.ease,
        useNativeDriver: true,
      })
    ).start();
  }, []);

  return (
    &lt;Animated.Image
      source={require(&quot;../assets/smallKiwi.png&quot;)}
      style={{ ...styles.loadingImage, transform: [{ rotate: spin }] }}
    /&gt;
  );
};</code></pre>
<h1 id="promiseall-사용-그리고-발생한-문제">Promise.all() 사용 그리고 발생한 문제</h1>
<p>처음에는 순차적으로 서버에 요청을 보내는 코드로 작성하였으나, <code>Promise.all()</code> 을 사용하여 로딩속도를 개선하고자 했다. 무료 AWS는 너무 느렸기에 실제로 로딩속도가 개선되는 것이 확연하게 느껴졌다!</p>
<p>그러나 비동기 코드를 작성하는 과정에서 문제가 발생했다. 상세페이지의 경우 나는 <code>SectionList</code> 를 사용했고, <code>SectionList</code> 에 들어가는 섹션 데이터는 아래와 같았다.</p>
<pre><code class="language-jsx">const [section, setSection] = useState([
    {
      title: [&quot;판매자님의 판매 상품&quot;, &quot;더보기&quot;],
      data: [&quot;loading&quot;],
    },
    {
      title: [`이건 어때요?`, &quot;&quot;],
      data: [&quot;loading&quot;],
    },
  ]);</code></pre>
<p>그리고 위 state를 변경하는 비동기 함수가 <code>Promise.all</code>로 불려지게 되는 과정에서 <code>setSection</code> 이 원하는 대로 적용되지 않았다는 점이다. 아래는 문제의 코드이다.</p>
<pre><code class="language-jsx">useEffect(() =&gt; {
    const itemDetailData = getItemDetailData();
    const sellerData = getSellerData();
    const recommendData = getRecommendItems();

    Promise.all([itemDetailData, sellerData, recommendData]);
}, []);

// code...

const getSellerData = async () =&gt; { 
 // code...
  setSection(
      [
      {
        ...section[0],
        data: 판매자의 다른 상품 데이터
      },
      section[1]
    ]
  )
 // code..
}

const getRecommendItems = async () =&gt; {
 // code ...
  setSection(
      [
      section[0],
      {
        ...section[1]
        data: 추천 판매 상품 데이터
        }
    ]
  )
 // code ...
}</code></pre>
<p>발생한 문제의 순서는 아래와 같다.</p>
<ol>
<li><code>getSellerData</code> 가 먼저 성공한다.</li>
<li>판매자의 상품이 보인다 여전히 추천 상품은 로딩 중이다.</li>
<li><code>getRecommendItems</code> 가 성공한다.</li>
<li>판매자의 상품이 사라지고 다시 키위 로딩으로 변경되며, 이젠 추천상품이 보인다.</li>
</ol>
<p>해결책부터 제시하자면, <code>setSection</code> 의 코드만 아래와 같이 수정하여 문제를 해결하였다.</p>
<pre><code class="language-jsx">// getSellerData 내부
setSection((prevSection) =&gt; [
  {
    ...prevSection[0],
    data: smallCardViewArrayGenerator(result.sellerItemsData),
  },
  prevSection[1],
]);

// getRecommendItems 내부
setSection((prevSection) =&gt; [
  prevSection[0],
  {
    ...prevSection[1],
    data: smallCardViewArrayGenerator(result.productList),
  },
]);</code></pre>
<p>코드만 보면 금방 해결되는 단순한 문제처럼 보인다. 하지만 개인적으로 스스로 이 문제를 해결하는데 꽤 힘이 들었고, 결국 질문을 해서 도움을 받아 해결하게 되었다. 그리고 내가 왜 이 문제를 쉽게 해결하지 못했는지를 스스로 진단해보았다. 나는 <strong>내가 훅과 자바스크립트 클로저에 대해서 좀 더 깊게 이해해야 한다</strong>라고 결론을 내렸다. 관련해서 참고 자료 링크를 남긴다.</p>
<ul>
<li><a href="https://ko.reactjs.org/docs/hooks-reference.html">리액트 훅 공식문서 링크</a></li>
<li><a href="https://rinae.dev/posts/a-complete-guide-to-useeffect-ko">useEffect 완벽 가이드 게시글 링크</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[React Native 당근 마켓 클론 (2)]]></title>
            <link>https://velog.io/@open_h/React-Native-KiwiMarket-2</link>
            <guid>https://velog.io/@open_h/React-Native-KiwiMarket-2</guid>
            <pubDate>Sun, 03 Jan 2021 14:46:43 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>React Native로 2주동안 당근마켓 앱 클론 &#39;키위 마켓&#39;을 시작했다. 프론트엔드 2명과 백엔드 2명, 총 4명의 팀원이 함께 했다.</p>
</blockquote>
<h1 id="로그인">로그인</h1>
<p>당근 마켓 앱에서 로그인은 휴대폰 인증으로 이루어진다. 이미 가입된 회원이라면 휴대폰 인증으로 바로 로그인이 가능하며, 신규 회원이라도 일단 휴대폰 인증 후 회원가입 페이지로 넘어가게 된다. 휴대폰 번호를 입력하고 인증 문자 받기 버튼을 누르면 인증 문자를 입력할 수 있는 창이 나타나게 된다.</p>
<table>
<thead>
<tr>
<th><img src="https://images.velog.io/images/open_h/post/7bc33801-4107-4ad9-b795-5050b9b7bcc7/IMG_1048.PNG" alt=""></th>
<th><img src="https://images.velog.io/images/open_h/post/9cdd8519-ad1f-43e1-995f-542065a396ca/IMG_1049.PNG" alt=""></th>
</tr>
</thead>
</table>
<p>로그인은 백엔드에서 휴대폰 문자인증을 구현하여 당근마켓과 동일한 방식으로 프론트에서도 구현할 수 있었다. 아래 코드는 현재 테스트 중인 코드로 아직 개발자를 위한 alert 창이 존재한다.</p>
<ul>
<li>인증 번호 받기</li>
</ul>
<pre><code class="language-jsx">const getAuthNum = async () =&gt; {
  try {
    const response = await fetch(PHONE_AUTH_API, {
      method: &quot;POST&quot;,
      body: JSON.stringify({
        phone_number: phoneNumber,
      }),
    });
    const result = await response.json();
  } catch {
    // code..
  }
};</code></pre>
<ul>
<li>인증 번호 인증하기</li>
</ul>
<pre><code class="language-jsx">const validateAuthNum = async () =&gt; {
  try {
    setOnValidating(true);
    const response = await fetch(AUTH_CHECK_API, {
      method: &quot;POST&quot;,
      body: JSON.stringify({
        phone_number: phoneNumber,
        auth_number: authNumber,
      }),
    });
    const result = await response.json();
    if (result.message === &quot;SIGNUP&quot;) {
      navigation.push(&quot;SignUp&quot;, { phoneNumber: phoneNumber });
    } else if (result.message === &quot;SIGNIN&quot;) {
      alert(&quot;이미 가입된 회원입니다. 로그인 시켜줄게요!&quot;);
      AsyncStorage.setItem(&quot;data&quot;, result.token);
      Keyboard.dismiss();
      setOnValidating(false);
      navigation.replace(&quot;BottomTabNavigator&quot;);
    } else if (result.message === &quot;DENY&quot;) {
      setOnValidating(false);
      alert(&quot;잘못된 인증번호 입니다.&quot;);
    } else {
      alert(&quot;버그 발견&quot;);
    }
  } catch {
    // code..
  }
};</code></pre>
<p>이미 회원가입한 기존 유저라면 토큰을 저장하고 바로 메인 화면으로 넘어가게 된다. 신규 회원의 경우 회원가입 페이지로 넘어간다. 휴대폰 번호를 입력하는 &lt; TextInput &gt; 컴포넌트 코드에 넘겨주는 많은 props가 기억에남는다.</p>
<pre><code class="language-jsx">&lt;PhoneNumberInput
  style={focusPhoneNum &amp;&amp; styles.focused}
  value={phoneNumber}
  placeholder=&quot;전화번호를 입력해주세요&quot;
  maxLength={PHONE_NUMBER_LENGTH}
  onChangeText={(phoneNumber) =&gt; setPhoneNumber(phoneNumber)}
  textContentType=&quot;telephoneNumber&quot;
  keyboardType=&quot;number-pad&quot;
  selectionColor={&quot;#A0C95E&quot;}
  onFocus={() =&gt; setFocusPhoneNum(true)}
  onBlur={() =&gt; {setFocusPhoneNum(false)}}
/&gt;</code></pre>
<h1 id="회원가입">회원가입</h1>
<p>실제 당근마켓 회원가입 페이지와 상관없이 회원가입 페이지는 아래와 같이 구현하였다.</p>
<table>
<thead>
<tr>
<th><img src="https://images.velog.io/images/open_h/post/09a46abb-76d8-42da-944b-afc9a51e6f1e/IMG_1050.PNG" alt=""></th>
<th><img src="https://images.velog.io/images/open_h/post/af264bb1-4558-4b3d-a642-6e1343ead491/IMG_1051.PNG" alt=""></th>
<th><img src="https://images.velog.io/images/open_h/post/ca2c9667-f88f-4e4c-b9e2-a91fb5532623/IMG_1052.PNG" alt=""></th>
</tr>
</thead>
</table>
<p>사용자로부터 현재 위치를 인증받아 그 곳의 한글 주소명을 받아온다. 회원가입시 필요한 정보는 사용자의 현재 주소 법정동의 지역 코드, 휴대폰 번호, 닉네임 세 가지가 필요하다. 위치 인증을 하면서 지역 코드를 받아오고, 백엔드 통신으로 닉네임 중복 검사를 해서 통과해야만 가입이 가능하다.</p>
<p>expo-location, kakao map REST API를 사용하여 현재 위치에 대한 주소와 지역코드를 확인하였고, 화면에 보이는 지도는 react-native-maps(google map)을 사용하여 현재 위치에 마커로 표시하였다. React Native에서 지원하는 웹뷰로 카카오 지도를 보여줄 수도 있지만, 구글 맵의 경우 React Native에서 라이브러리가 굉장히 잘 되어 있었기에 화면에 보여주는 지도는 구글 지도를 사용하게 되었다.</p>
<pre><code class="language-jsx">const getCurrentLocation = async () =&gt; {
  setMyTown(&quot;동내 인증중...&quot;);
  const { status } = await Location.requestPermissionsAsync();
  if (status !== &quot;granted&quot;) {
    Alert(&quot;현재 위치에 대한 정보를 받아 오고싶어요&quot;);
    return;
  }
  const myLocation = await Location.getCurrentPositionAsync({});
  setCoordinate({
    latitude: myLocation.coords.latitude,
    longitude: myLocation.coords.longitude,
  });
  const townName = await getTownName([
    myLocation.coords.latitude,
    myLocation.coords.longitude,
  ]);
​
  setMyTown(townName);
};
​
const getTownName = async (coordinate) =&gt; {
  const response = await fetch(
    `https://dapi.kakao.com/v2/local/geo/coord2regioncode.json?x=${coordinate[1]}&amp;y=${coordinate[0]}`,
    {
      method: &quot;GET&quot;,
      headers: {
        Authorization: `KakaoAK ${KIWI_REST_API_KEY}`,
      },
    }
  );
  const result = await response.json();
​
  // [0]: 법정동, [1]: 행정동
  setTownCode(result.documents[0].code);
  return result.documents[0].address_name;
};
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[React Native 당근 마켓 클론 (1)]]></title>
            <link>https://velog.io/@open_h/React-Native-KiwiMarket-1</link>
            <guid>https://velog.io/@open_h/React-Native-KiwiMarket-1</guid>
            <pubDate>Fri, 01 Jan 2021 17:17:49 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>React Native로 2주동안 당근마켓 앱 클론 &#39;키위 마켓&#39;을 시작했다. 프론트엔드 2명과 백엔드 2명, 총 4명의 팀원이 함께 했다. 이 글은 5일차를 마무리하며 정리하는 글이다.</p>
</blockquote>
<h1 id="초기-세팅">초기 세팅</h1>
<p>React Native는 처음이었지만 초기 세팅의 중요성은 어느정도 알고 있었다. 그래서 2주 라는 짧은 기간이었지만 초기 세팅에 거의 2일을 투자하였다. 대략적으로 아래와 같은 구조를 만들었다. 아래 Navigator 구조는 초창기 세팅에 사용한 구조이다. 지금은 조금더 내용이 추가되었으나 기본적인 Navigator 구조는 아래 그림과 같은 형태를 유지하고 있다. </p>
<p><img src="https://images.velog.io/images/open_h/post/f31372f7-77fa-4322-8069-2b4f759af557/Navigator%20Structure2.png" alt=""></p>
<p>초기세팅에서 만든 모든 스크린에는 버튼을 만들어 간단하게 페이지 이동 버튼을 미리 만들어 두었다. 폴더 구조도 미리 만들어 빈 파일이라도 미리 만들어 두었다. 함수형 컴포넌트, styled-component, redux를 사용할 예정이었기에 그에 맞추어 초기 세팅을 진행하였다. 아래는 현재 프로젝트 src 폴더 구조이며 constants라는 폴더가 추가된 것을 제외하고 초기 세팅 당시와 거의 동일하다. </p>
<p><img src="https://images.velog.io/images/open_h/post/be280ddd-febf-4f2d-ad74-142ecaa28c63/%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-01-02%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%201.19.14.png" alt=""></p>
<ul>
<li>assets : 이미지 등 사용되는 자료</li>
<li>components : 공통으로 사용되는 컴포넌트</li>
<li>constants : 화면 가로 세로 길이 등 공통으로 사용되는 레이아웃 고정 변수</li>
<li>data : 목 데이터 등 데이터 관리</li>
<li>navigations : 네비게이션 파일들과 Root Navigator 관리</li>
<li>redux : 리덕스 reducers, actions 관리</li>
<li>screens : 앱에 사용되는 모든 스크린 관리, 모든 스크린은 각자의 폴더에 담겨 있으며 스크린 고유의 컴포넌트는 해당 폴더 안에 들어있다.</li>
<li>styles : styled-component에 사용되는 mixin.js와 공통 colors, margins 등을 관리하는 theme.js가 있다. theme은 styled-component의 ThemeProvider가 사용되었다.</li>
</ul>
<h1 id="초기-개발-과정">초기 개발 과정</h1>
<blockquote>
<p>2주라는 짧은 기한으로 우리는 안드로이드는 제외하고 iOS에 기준을 두기로 했다. 레이아웃과 기능 등 iOS 기준으로 개발하게 되었다. 초기세팅을 하면서 우리는 프로젝트에서 구현할 스크린들을 정하여 trello backlog에 두었다. 초기 세팅 후에는 scrum 방식으로 앱의 규모를 조금씩 키워갈 수 있도록 우선순위를 정하여 해야할 업무를 분담하였다.</p>
</blockquote>
<h3 id="공문이-답이다">공문이 답이다.</h3>
<p>React Native 고유의 컴포넌트 사용법과 React Navigation 사용법을 익히기 위해 공문에 크게 의존했다. 공문이 매우 잘 되어있어 웬만한 내용은 구글 검색보다 공문을 읽는 것이 초기 개발 과정에 훨씬 더 도움이 되었다. 라이브러리 사용도 많았기에 라이브러리 README 문서를 참조하는 일도 많았다.</p>
<h3 id="react-native-컴포넌트-사용하기">React Native 컴포넌트 사용하기</h3>
<p>제일 처음 나는 상세 페이지로 시작하게 되었는데 첫날 짠 모든 코드의 구조를 다음날 뒤엎었다. 처음에는 <code>ScrollView</code> 내부에 <code>FlatList</code>를 두 번 사용하는 구조였다. 하지만 스크롤이 가능한 컴포넌트내부에 스크롤이 가능한 컴포넌트가 있어 scroll nesting이 일어나게 되는 문제가 발생하였다. 따라서 상세 페이지 구조를 <code>SectionList</code> 를 사용하여 아래와 같은 구조로 만들었다.</p>
<p><img src="https://images.velog.io/images/open_h/post/95ed6187-8fb3-4e8e-9dcd-a0cbdebad7e6/ItemDetailScreen.gif" alt=""></p>
<pre><code class="language-jsx">&lt;ItemDetailContainer&gt;
  &lt;ItemDetailHeader goBack={goBack} whiteHeader={whiteHeader} /&gt;
  &lt;SectionList
    onScroll={handleScroll}
    scrollEventThrottle={16}
    keyExtractor={(item, index) =&gt; index}
    sections={section}
    renderItem={({ item }) =&gt; (
      &lt;View style={styles.flexRow}&gt;
        &lt;ShortItemCard goItemDetail={goItemDetail} imgUrl={item[0]} /&gt;
        &lt;ShortItemCard goItemDetail={goItemDetail} imgUrl={item[1]} /&gt;
      &lt;/View&gt;
    )}
    renderSectionHeader={SectionHeader}
    ListHeaderComponent={HeaderComponent(productDetail)}
    ListFooterComponent={&lt;EmptyFooterMargin /&gt;}
    /&gt;
  &lt;ItemDetailFooter /&gt;
&lt;/ItemDetailContainer&gt;</code></pre>
<p>React Native 코어 컴포넌트들에는 각각 수많은 Props가 있었고, 어떤 기능과 레이아웃을 구현하기 위해 공문을 보는 것이 가장 정확하였다. </p>
<h3 id="재사용-그리고-또-재사용">재사용 그리고 또 재사용</h3>
<p>React에서 했던 것처럼 재사용 가능한 컴포넌트를 찾아서 컴포넌트를 최대한 재사용하였다. 중복되는 스타일은 mixin.js에서 관리해 styled-component를 만들었다. 컴포넌트가 아니더라도 현재 재사용과 공통으로 사용되는 것들을 관리하는 파일만 아래와 같다.</p>
<ul>
<li><p><code>src/components</code> : 재사용되는 모든 컴포넌트가 있다. 예를 들어 Headers.js라는 파일이 있다. Root Navigator에서 <code>headerShown: false</code> 옵션을 주어 스크린마다 직접 만든 Header가 존재하기에 재사용되는 모든 헤더를 여기서 관리한다.</p>
</li>
<li><p><code>src/constants/Layout.js</code> : 레이아웃과 관련된 재사용 값들이 모두 여기에 있다. 주로 Dimension과 직접적으로 연관되어 화면의 가로 세로 길이와 관련된 값들을 관리하고 있다.</p>
</li>
<li><p><code>src/styles/mixin.js</code> : styled-component를 생성하기 위해 자주 사용하는 css 세트를 styled.css 로 만들어 관리하는 곳이다. 지금까지는 이 파일이 가장 유용하다고 느껴졌다.</p>
<pre><code class="language-jsx">// 제일 만만한 속성
export const flexCenter = css`
  display: flex;
  flex: 1;
  justify-content: center;
  align-items: center;
`;
// 디테일 페이지에 이 속성이 5번 사용되었다.
export const flexRowMarginXView = css`
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: center;
  width: ${windowWidth - 32}px;
  padding: 16px 0;
  margin-left: 16px;
  border: 1px solid transparent;
  border-bottom-color: ${({ theme }) =&gt; theme.colors.gray};
`;
// 이외에도 자주 사용되는 버튼 스타일 등이 있다.</code></pre>
</li>
<li><p><code>src/styles/theme.js</code> : 자주 사용하는 색들이나, margin 값들이 여기에 있다. 메인 키위 테마색이나, 화면 좌우로 16px씩 마진을 주는 등의 데이터가 담겨있으며 ThemeProvider로 사용되는 파일이다.</p>
</li>
<li><p><code>src/config.js</code> : 백엔드와 API 통신을 위한 주소를 관리하고 있다.</p>
</li>
<li><p><code>src/utils.js</code> : 공통으로 사용되는 함수들을 관리하고 있다. 예를들어 매너온도에 따라 상태바가 얼마나 채워지는지 계산하는하는 함수가 있다.</p>
</li>
</ul>
<p><img src="https://images.velog.io/images/open_h/post/d104c060-3a84-4c4a-b8b9-b4db8a702b37/mannerTemp.gif" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[1차 프로젝트 후기 : 개발 관점에서]]></title>
            <link>https://velog.io/@open_h/team-project1-dev</link>
            <guid>https://velog.io/@open_h/team-project1-dev</guid>
            <pubDate>Sun, 27 Dec 2020 00:18:24 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://velog.io/@open_h/team-project1">마켓컬리 후기</a>는 따로 작성하였다. 이 글에서는 내가 기술적으로 어떤 경험을 했고 어떤 성장을 했는지 기록한다.</p>
</blockquote>
<h1 id="프로젝트-소개">프로젝트 소개</h1>
<h3 id="프로젝트-시연-영상-링크"><a href="https://drive.google.com/file/d/1xeqEAU5As-_2ZoDxUpgFIaOhW9dWftVU/view?usp=sharing">프로젝트 시연 영상 링크</a></h3>
<p><strong>프로젝트 기간</strong></p>
<ul>
<li>2020.12.14 ~ 2020.12.24 (총 11일)</li>
</ul>
<p><strong>Front-End</strong> </p>
<ul>
<li>HTML, CSS, JavaScript</li>
<li>React(CRA)</li>
<li>Sass</li>
</ul>
<p><strong>Back-End</strong></p>
<ul>
<li>Django</li>
<li>CORS Header</li>
<li>Bcrypt</li>
<li>PyJWT</li>
<li>MySQL</li>
<li>AqueryToo</li>
</ul>
<p><strong>협업 도구</strong></p>
<ul>
<li>Git, Github</li>
<li>Slack : 비대면 소통</li>
<li>Trello : 일정관리 및 작업 현황 공유</li>
<li>Notion : 팀 내 개발 자료, 규칙, 안건 등 기록</li>
</ul>
<p><strong>내가 맡은 역할</strong></p>
<p>제품 상세 페이지, 장바구니, 결제 페이지, Aside, Footer</p>
<p>제품 상세 페이지의 데이터는 백엔드로부터 받아온다. 같은 페이지 내에서 매뉴탭에 따른 스크롤 이동이 있으며 상품 종류별 동적 라우팅이 가능하다. 상품 리뷰, 상품 문의 게시판 페이지네이션도 상세 페이지에 있다.</p>
<p>장바구니 페이지는 백엔드로부터 받아온 장바구니 데이터를 냉동, 냉장, 상온 상품별로 필터링하여 섹션 구분 및 섹션 접기 펴기가 가능하다. 장바구니에서 사용자의 각 액션(수량 변경, 삭제, 선택, 전체 선택, 선택 삭제 등)마다 백엔드와 통신을 한다. 실시간으로 무료 배승 가능 유무, 총 마일리지 적립액, 총 할인액, 총 결제액 등을 계산한다.</p>
<h1 id="내가-배운-것">내가 배운 것</h1>
<h3 id="1-리액트-활용">1. 리액트 활용</h3>
<ul>
<li>라이프 사이클 활용</li>
<li>재사용 가능한 컴포넌트 작성</li>
</ul>
<p>페이지네이션을 구현할 때와 비동기 함수를 사용할 때 라이프 사이클을 이해하고 사용하였다. 프로젝트 중 모든 컴포넌트를 처음으로 합쳤을 때 서로의 페이지에 라우팅을 하는 기능이 당연히 있었다. 제품 상세 페이지로 이동할 때, 브라우저에서 현재 스크롤 위치에 고정된 채로 이동하는 문제가 발생했다. 제품 상세 페이지로 이동할 때는 스크롤이 가장 위로 올라가길 바랬는데! 그런데 그 현상을 보고 &#39;<code>componentDidUpdate</code>&#39; 에 그냥 윈도우 scroll값을 0으로 주면 되겠는데? 라는 생각이 바로 들었고, 구글링 하지 않고 바로 해결했다는 것이 뿌듯했다.</p>
<p>게시판 컴포넌트가 중복되었지만, 게시판 형태는 조금 달랐다. 조건부 렌더링과 게시판 컴포넌트가 props로 받는 값을 통해 다른 게시판이 생기더라도 유동적으로 대응할 수 있는 재사용 가능한 컴포넌트를 작성하였다.</p>
<h3 id="2-비동기-코드-작성하기">2. 비동기 코드 작성하기</h3>
<ul>
<li>백엔드와 통신에서 async await 문법 활용</li>
<li>비동기 관련 문제 해결</li>
</ul>
<p>처음에는 <code>fetch().then().then()</code> 의 형태로 코드를 짜려고 했다. 하지만 비동기 문법을 학습하고 활용해보지 않았기에 시도해 보았다. 특히 장바구니 페이지에서는 하나의 사용자 액션에 2번의 백엔드 통신이 이루어졌기에 비동기 순서가 중요한 과정이 있었다. 그 과정에서 백엔드와 통신 종류별로 async 함수를 따로 작성하여 비동기 문제를 깔끔하게 해결할 수 있었다. 장바구니 아이템에 대해 삭제 요청(DELETE), 수량 변경/선택 토글 요청(PATCH)이 끝난 후에 다시 GET method로 변경된 장바구니 데이터를 가져오는 형태였다. 두 요청을 async 함수로 분리하여 작성했다. </p>
<pre><code class="language-js">// 비동기 순서를 직관적으로 만들어주는 async await 문법.
selectItem = async e =&gt; {
  const id = e.target.id;
  const className = e.target.className;

  const response = await fetch(`${CART_API}`, {
    method: &quot;PATCH&quot;,
    headers: {
      Authorization: localStorage.getItem(&quot;token&quot;),
    },
    body: JSON.stringify({
      cart_item_id: id,
      select: className === &quot;fa-check-circle fas purple&quot; ? &quot;False&quot; : &quot;True&quot;,
    }),
  });
  // error handle codes

  // 그 다음 실행시킬 async 함수이다.
  // 동기적인 형태로 위에서 아래로 코드를 읽을 수 있다는 장점이 있다.
  this.getCartData();
};</code></pre>
<p>서버가 항상 켜져있는 상황이 아니었기에 aysnc 문법을 활용할 때 아래와 같이 <code>try catch</code> 를 활용하여 서버가 꺼져있을 때 목데이터를 가져오는 코드를 사용할 수 있었다. <code>fetchWithTimeout</code> 은 사용자 함수로 최대 <code>fetch</code> 시간을 내가 정해서 일정 시간이 지나면 강제로 목 데이터를 화면에 띄우도록 설정하였다.</p>
<pre><code class="language-js">getCartData = async () =&gt; {
  try {
    const response = await fetchWithTimeout(`${CART_API}`, {
      method: &quot;GET&quot;,
      headers: {
        Authorization: localStorage.getItem(&quot;token&quot;),
      },
    });
    const data = await response.json();
    this.setState({ cartData: data.items_in_cart });
  } catch {
    const response = await fetch(`data/cartdata.json`);
    const data = await response.json();
    let cartData = data.items_in_cart;
    cartData = cartData.map(data =&gt; {
      data.selected = true;
      return data;
    });

    this.setState({ cartData: cartData });
  }
};</code></pre>
<p>만약  <code>fetch().then().then()</code> 이었다면 then 안에 다시 비동기 코드가 들어가는 형태의 코드가 되었을 것이다.</p>
<h3 id="3-깔끔한-코드-작성하기">3. 깔끔한 코드 작성하기</h3>
<ul>
<li>가독성이 좋으며 수정 가능한 코드 작성하기</li>
<li>Short-circuit, 삼항 연산자, 고정 값 변수로 분리하기 등 활용</li>
</ul>
<p>아래는 구현한 페이지네이션과 코드에서 고정값으로 분리했던 변수들이다. </p>
<p><img src="https://images.velog.io/images/open_h/post/b08a2cda-57e7-48fb-a7ec-f44b5297e943/pagination.gif" alt=""></p>
<pre><code class="language-js">const LIMIT_PER_PAGE = 10; // 한 페이지 최대 게시글 수
const PAGES_NUM = 5; // 한 페이지 내 페이지네이션 갯수 5개
const INITIAL_PAGES = Array.from({ length: PAGES_NUM }, (_, i) =&gt; i + 1); // 초기 페이지 1~5

//pages의 초기 값은 INITIAL_PAGES이다.
{pages.map((page, idx) =&gt; {
  return (
    &lt;li key={idx}&gt;
      &lt;button
      id={pages[idx]}
      className={currentPage === pages[idx] ? &quot;current-page&quot; : &quot;&quot;}
      onClick={clickPage}
      &gt;
      {page}
    &lt;/button&gt;
    &lt;/li&gt;
  );
})}</code></pre>
<p>위 코드에서 고정값인 변수들만 바꾸어주면 바로 원하는 형태가 나온다. 필요에 따라(실제로 이메일 사이트에 이런 기능이 흔히 있다) 사용자에게 위 값을 변경하도록 코드를 수정할 수도 있다.</p>
<p>아래는 자주 활용했던 형태의 코드로 short-circuit을 활용하여 조건부 렌더링을 구현하였다. 필요에 따라 정보를 숨기거나 보여줄 수 있다.</p>
<pre><code class="language-js">{showHits &amp;&amp; &lt;th className=&quot;lookup&quot;&gt;조회&lt;/th&gt;}</code></pre>
<p>리액트에서 사용자 컴포넌트를 차례대로 한 화면에 보여주기 위해 아래와 같은 코드를 작성하였다.</p>
<pre><code class="language-jsx">const MENU_COMPONENTS = {
  1: ItemDescription,
  2: ItemImage,
  3: DetailInfo,
  4: CustomerReview,
  5: ItemInquire,
};
const MENU_NAME = [&quot;ItemDescription&quot;, &quot;ItemImage&quot;, &quot;DetailInfo&quot;, &quot;CustomerReview&quot;, &quot;ItemInquire&quot;];

{MENU_NAME.map((name, idx) =&gt; {
  const ComponentName = MENU_COMPONENTS[idx + 1];
  return (
    &lt;div name={name} key={name}&gt;
      &lt;ItemDetailMenu menuTabId={idx + 1} scrollToMenu={this.scrollToMenu} /&gt;
      &lt;ComponentName
        paramsid={this.props.match.params.id}
        menuTabId={idx + 1}
        itemData={itemData}
        /&gt;
    &lt;/div&gt;
  );
})}</code></pre>
<p>위의 다섯가지 메뉴 컴포넌트는 가장 위에 아래와 같은 메뉴탭이 공통적으로 존재하며 메뉴 탭을 누르면 해당하는 위치로 스크롤하게 된다.</p>
<p><img src="https://images.velog.io/images/open_h/post/9d4d481c-2f46-4552-bdcc-a53adc639370/%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%202020-12-27%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%208.03.13.png" alt=""></p>
<h3 id="4-타인의-코드-읽는-능력-향상">4. 타인의 코드 읽는 능력 향상</h3>
<p>프로젝트 전에도 그렇고 프로젝트 중에도 나는 질문을 많이 받는 편이었다. 내가 읽은 절대적인 코드량을 비교하면 내 코드보다 다른 사람의 코드를 훨씬 많이 읽었다. 그리고 확실히 읽는 속도가 빨라졌다. 피아노도 초견을 계속하다보면 늘 수 밖에 없는 것과 비슷하다고 생각했다. 그리고 배운 점들이 많다. 솔직히 초보 개발자끼리 작성하는 코드가 막 뛰어나거나 하진 않을 것이다. 하지만 반대로 어떤 코드가 가독성이 낮은지 알 수 있었고, 그런 코드 작성을 피해야 한다는 것을 알기도 했다.</p>
<p>글을 잘 쓰는 사람의 글은 술술 읽힌다. 코드도 마찬가지였다. 코드를 잘 작성하는 사람은 슥 읽어도 그 코드가 어떤 코드인지 알 수 있었다. 이는 협업과 유지보수에서 엄청난 장점이 될 것이라고 생각했다. 코드를 잘 작성한 분에게 질문을 받을 때는 질문의 요지와 발생한 문제를 빠르고 정확하게 이해할 수 있었다. 그리고 잘 쓴 코드일 수록 해결 방법도 깔끔하게 제시할 수 있었다. 반대로 말하면 스파게티 코드는 문제가 발생해도 그 해결책을 제시받기 어렵다는 뜻이기도 하다.</p>
<p>스택오버플로우 등에 질문을 할 때 질문도 잘 해야한다고 한다. 본인의 코드가 잘 작성이 되었다면 다른분들에게 설명하기도 쉬울 것이다. 즉, 좋은 코드에서 문제가 발생했다면 스스로 해결하기도 훨씬 용이해 질 것이고 도와주는 사람의 입장에서도 어렵지 않게 좋은 해결책을 제시할 가능성이 높다는 뜻이다.</p>
<blockquote>
<p>많은 것을 배울 수 있었던 프로젝트였다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[1차 프로젝트 후기]]></title>
            <link>https://velog.io/@open_h/team-project1</link>
            <guid>https://velog.io/@open_h/team-project1</guid>
            <pubDate>Sat, 26 Dec 2020 06:10:56 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>마켓컬리 클론 프로젝트가 끝났다. 정말 즐거운 2주였고, 결과도 만족하는 프로젝트였다. <a href="https://velog.io/@open_h/team-project1-dev">개발 기술적인 성장</a>도 있었다. 하지만 나에게는 그것보다 개발팀의 팀워크를 경했다는 점이 더 좋았다. 이 글에서는 개인적인 개발 실력 향상은 제외하고 그 외적인 경험을 정리하고자 한다.</p>
</blockquote>
<p><img src="https://images.velog.io/images/open_h/post/1dc2c911-b5e2-4742-9f06-1bc5db8de48d/IMG_0957.JPG" alt=""></p>
<h1 id="1-팀워크">1. 팀워크</h1>
<p>팀 프로젝트를 하며 팀워크에 중요한 두 가지가 <strong>&#39;케미&#39;</strong>와 <strong>&#39;실력&#39;</strong>이라고 느꼈다. 그럼 그 중에 무엇이 더 중요할까? 개개인마다 답이 다를 것이다. 그러나 나는 실과 바늘처럼 두 가지 모두 포기할 수 없는 기준이라고 생각한다.</p>
<p>물론 상황에 따라 중요한 기준이 달라질 수도 있다. 만약 제한 시간 내에 급하게 어떤 결과물을 낼 때는 실력이라는 잣대가 더 중요할 수도 있다. 하지만 오래 같이 팀으로 일해야 할 상황에는 실력보다 케미가 더 중요할 수도 있다. 그러나 두 가지 모두 중요한 기준이 된다는 것은 모두가 동의할 것이다.</p>
<h3 id="1-케미">(1) 케미</h3>
<p>조직문화를 일컫는 말로 &#39;컬쳐 핏&#39;이나 &#39;케미&#39; 라는 용어가 사용되고 있다. 팀 프로젝트 이전에도 물론 그 단어를 들어본 적이 있다. 하지만 이번 프로젝트로 그 단어의 뜻이 무엇인지 직접 느끼게 되었으며 팀워크에 팀 문화와 분위기가 얼마나 중요한지 알게 되었다. </p>
<p>프로젝트 시작전 걱정했던 부분이기도 하다. 팀 분위기가 좋지 않다면 개개인이 낼 수 있는 개발적인 아웃풋보다 더 적게 낼 수 밖에 없는 것은 당연하다. 이는 &#39;개발에 대한 고민&#39;에 쏟을 에너지를 &#39;사람과의 관계&#39;에 붓게 될 수 있기 때문이다. 즉, 케미가 맞지 않는 팀이라는 것은, 개인의 에너지를 본업에 온전히 몰두할 수 있는 것이 아닌 환경이 아니라는 뜻이다. 그러나 좋은 분위기는 팀에 대한 애정을 만들어 낼 뿐만 아니라 능률도 올라가는 것은 당연지사다. 그리고 마음에 드는 사람, 나와 티키타카가 잘 되는 사람과 일할 때 사람은 더 즐겁게 일할 것이다. 그런 것들이 쌓여 결국 더 좋은 결과물로 이어질 것이라 믿는다.</p>
<p>그런 의미에서 우리 팀원은 굉장히 합이 잘 맞는 팀이었다고 생각한다. 프로젝트가 시작되기 며칠전부터 개인 공부를 하더라도 일찍 나와서 서로에게 힘이 되어 주었다. 프로젝트를 시작하는 당일에도 팀내의 규칙을 만들어 노션으로 공유하였고, 업무 분담도(나는 프론트엔드 였으므로 프론트 엔드끼리) 나름대로 첫날에 잘 진행되었다. 스탠드업 미팅을 매일 아침 9시 30분에 진행 하였고, 그 시간에는 진지하게 개발 관련 소통이 잘 이루어졌다.</p>
<p>점심은 상황에 맞게 해결하고 저녁은 가능한 한 같이 먹기로 했다. 아침부터 밤 늦게까지 함께하면서 모두가 힘들었지만 서로 농담도 던지며 활기찬 분위기를 유지했다. 장난도 서로 잘 받아쳐주고 모두의 노력으로 재미있는 환경에서 개발할 수 있게 되었다. 사실 이 점이 중요하다고 본다. 나는 개발 자체는 좋아했었고, 심지어 그 개발하는 환경조차 즐겁고 재미있었던 것이다.</p>
<h3 id="2-개발-실력">(2) 개발 실력</h3>
<p>당연하게도 잘하는 분과 개발하는 것은 즐겁다. 잘하는 분과 일하는 것은 일의 과정이 수월하고 시원시원하게 해결되는 경우가 많다는 뜻이다. 뿐만 아니라 개발적으로 배울 점들이 매우 많다는 의미이기도 하다.</p>
<p>나는 특히 실력이 좋으신 PM님과 함께 일하면서 배운 것이 많아 팀 프로젝트가 정말 즐거웠다. 프론트엔드 입장에서 2주 프로젝트에서 원하는 API들을 백엔드가 뚝딱뚝딱 만들어 준다는 것으로도 굉장히 즐거운 일이다. 그러나 스탠드업 미팅과 개인적인 대화를 통해 백엔드와 프론트의 소통에 무엇이 필요한지를 배웠다는 점이 내게는 더 의미있었다. 백엔드에 대해서 깊게 알지 못하는 내가 백엔드인 PM님과 대화하며 대략적인 큰 그림을 머릿속에 그릴 수 있게 되었다. 학교에서 들었던 데이터베이스 MySQL 수업과 컴퓨터 네트워크 수업 내용은 이론이었고, 실제 프로젝트에서 어떻게 생각을 해야 하는지 그 틀을 잡을 수 있었다.</p>
<p>하지만 그 무엇보다도 <strong>프론트엔드 개발자가 왜 백엔드도 어느정도 공부해야하는지</strong>를 몸소 느낀 것이 더 나에게는 의미있었다. 필요성을 느끼고 공부하는 것과 필요해보여서 공부하는 것에는 의지와 동기에서 차이가 나기 마련이다.</p>
<h1 id="2-소통">2. 소통</h1>
<p>프로젝트가 끝날 때쯤 들었던 생각. &#39;이거 2주 프로젝트라면 한 3일은 기획해야 겠는데?&#39;라는 생각이다. 3일이라는 절대적인 시간이 필요하다는 게 아니라 처음에 소통을 잘하고 계획을 잘한 상태로 프로젝트를 시작하는 것이 얼마나 중요한지 알게 된 것이다.</p>
<h3 id="1-백엔드와-소통">(1) 백엔드와 소통</h3>
<p>백엔드와 프론트엔드 사이에 우리팀은 매일 스탠드업 미팅을 알차게 한 덕분에 꽤 괜찮은 편이었다고 생각한다. 그러나 분명 문제는 계속해서 발생했고 발생했던 문제의 대부분은 부실한 소통 때문이었다. 프론트가 데이터베이스 모델링에 참가하지 않다보니 백엔드에서 주는 데이터와 프론트가 받는 데이터가 조금 다른 경우가 있었다. 물론 미팅에서 계속해서 서로 소통해서 맞추어 간 덕분에, 프론트에서 만든 목데이터와 백엔드에서 주는 데이터는 거의 유사했다. 그러나 물론 마법처럼 한번에 딱 들어맞지는 않았다.</p>
<h3 id="2-프론트내-소통">(2) 프론트내 소통</h3>
<p>각자의 진행상황을 나름대로 공유한다고 대화도 하고, 트렐로도 사용하고 스탠드업 미팅 때 정리해서 말하기도 했다. 하지만 2주라는 짧은 기간이었고, 각자가 자기 코드를 작성하기 바빠서 서로의 진행상황에 대해 정확히는 알지 못했던 점이 아쉽다. 실제 페이지를 보면서 어느정도 기능까지 구현했는지 파악했다면 더 좋았을 것이라는 아쉬움이 든다. 여유가 있었다면 서로 코드에 대해서 리뷰도 해주었다면 금상첨화였을 것이다. 모든 컴포넌트를 합쳤을 때 문제가 발생하기도 했고 그 문제는 사전에 소통을 더 깊게 했거나, 개발 도중 지속적으로 서로 리뷰하고 상황을 공유했다면 사전에 방지할 수 있는 문제들이었다.</p>
<p>짧은 프로젝트 기간이었던 만큼 소통의 중요성이 더 절실하게 느껴졌던 것 같다. 실제 프로젝트에 들어서는 이러한 소통이 얼마나 더 중요할지 예상되기도 했다.</p>
<h1 id="그래서-그-다음은">그래서 그 다음은?</h1>
<p>이 글을 쓴 이유가 단순히 과거회상과 일기장이 아니기를 스스로에게 바란다. 좋았던 점과 아쉬웠던 점을 떠올리고 배운점들을 다시 되새기며 스스로 성장했으면 한다. 단기적으로는 당장 다가오는 2차 프로젝트에 1차 때 배운 것들을 잘 적용하고 싶다.</p>
<h3 id="2차-프로젝트-시작">2차 프로젝트 시작</h3>
<p>2차 팀 프로젝트로 React Native 프로젝트를 시작하게 되었다. 총 7명이던 1차 프로젝트와 달리 2차 프로젝트는 총 4명이다. 소수의 팀인 만큼 그 장점이 있을 것이라 생각한다. </p>
<p>나는 리엑트에 이제 막 자신감이 생겼는데, React Native를 시작하게 되었다. 그러나 나는 모바일 개발에 더 관심이 있었기에 정말 기대된다. 2차 프로젝트에 React Native를 하고 싶다고 맨토님꼐 위코드 첫달부터 요청을 했었고 결국 할 수 있게 되었다!</p>
<p>굳이 개발분야가 아니라도 나는 감사하게도 원래 새로운 배움에 대한 두려움이 거의 없는 편이다. 성인이 된 내가 키즈 모델을 하고 싶은게 아닌 이상 어떤 분야에서 절대적인 시간 투자가 있다면, 그리고 심지어 즐길 수 있다면 충분히 무엇이든 해낼 수 있다고 생각한다. 나는 이 후기를 적으면서도 빨리 React Native를 프로젝트 시작 전 하루 이틀이라도 미리 공부하고 싶어서 이 후기 작성을 빨리 끝내고 싶은 마음도 있다.</p>
<p>2차 프로젝트를 시작하는 입장에서 1차 프로젝트의 경험이 있다는 것은 정말 의미 있는 것 같다. 1차 프로젝트에서 좋았던 점은 더 잘 적용하고, 아쉬웠던 점은 해결책을 찾아 보완하는 방향으로 2차 프로젝트에서는 더 좋은 경험을 쌓았으면 한다.</p>
<h1 id="팀원-박제">팀원 박제</h1>
<p>팀 프로젝트 후기에 팀원 박제가 빠지면 섭섭할 거 같았다!</p>
<ul>
<li><p>Backend 원희님 : <del>청산가리</del> 팀원 모두가 인정하는 자랑스러운 우리 팀 PM님. 팀을 주도적으로 그리고 바른 방향으로 잘 이끌어 주었다. 이분이 아니었으면 우리팀이 어떻게 되었을지 상상이 안된다. 엄청난 개발 속도로 프론트에게 데이터를 쏟아부어 준 덕분에 프론트엔드 입장에서 너무 감사했다. 마지막날 발표전 급박할 때, 라이브 코딩쇼로 이 세계를 평정하셨다.</p>
</li>
<li><p>Backend 주형님 : <del>폭곰</del> 이지만 우리팀에게는 상냥한 남자. 팀원 중 누군가는 이 분을 API 공장이라고 명명하였다. 나는 주형님이 팀의 윤활유같은 역할이었다고 생각한다. 팀이 잘 굴러가는데 있어서 호탕한 웃음으로 밝은 분위기를 만들어주고 장난을 치면서도 장난을 잘 받아주신 덕분에 팀원 개개인이 개발적으로 좋은 아웃풋을 낼 수 있었다고 생각한다.</p>
</li>
<li><p>Backend 현주님 : <del>실세</del> 우리팀의 실세. 애덤 스미스의 보이지 않는 손과 같은 역할을 담당하셨다. PM님 뒤에는 사실 현주님이 계신다는게 마켓불리 팀 술자리의 정설. 단 한마디로도 우리팀 무력부장을 컨트롤 할 수 있는 유일한 분이다. 나는 채훈님이 현주님과 다음에도 같은팀이 되어 다행이라고 생각한다. 현주님이 아니면 날뛰는 채훈님을 누가 컨트롤 할 수 있을까.</p>
</li>
<li><p>Frontend 찬영님 : <del>핑크</del> 분홍색을 좋아하는 마켓불리 간판 페이지와 네비게이션 바 담당자. 마지막 날 우리의 간판이 흔들거렸지만, 찬영님의 재빠른 대응으로 문제를 해결할 수 있었다. 이분의 핑크 사랑은 기획에서 메인 페이지를 핑크로 물들게 할 뻔 했지만, 다행이 단호했던 다른 팀원들 덕에 그렇게 되지는 않았다.</p>
</li>
<li><p>Frontend 채훈님 : <del>입벌구</del> 항상 팀 내 정치의 중심에 서있는 우리팀 무력부장. 위코드 시작부터 옆자리였고 프로젝트 끝날때까지 단 한번도 떨어진 적이 없는 분이다. 그의 정치 실력은 이미 소문이 자자하다. 채훈님이 만든 재사용 가능한 컴포넌트는 내 페이지에도 사용했는데 거의 라이브러리 수준이었다. 오래 함께한만큼 감동적인 말을 따로 해주고 싶지만 다음에 할게요 채훈님^^</p>
</li>
<li><p>Frontend 민아님 : <del>MtLQ</del> Mina the Login Queen. 프론트엔드 모두가 이분의 페이지를 보고 자꾸 실제 페이지로 착각했다. 코딩할 때 반쯤 엎드린 자세는 이미 우리팀에서 여러번 사진으로 박제 당했다. 자신이 맡은 업무를 항상 완벽하게 해내서 결과물로 보여주는 덕에 프론트엔드 팀원으로서 믿음이 갔고 나는 전혀 걱정없이 프로젝트를 진행할 수 있었다.</p>
</li>
</ul>
<blockquote>
<p>정말 즐겁고 보람찬 2주였습니다. 여러분이기에 가능했던 경험이었습니다. 감사합니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Routes.js 작성하기]]></title>
            <link>https://velog.io/@open_h/react-improve-routes.js</link>
            <guid>https://velog.io/@open_h/react-improve-routes.js</guid>
            <pubDate>Sun, 20 Dec 2020 08:25:39 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>리액트 라우트를 사용하면서 Routes.js 파일을 여러번 개선하였다. 그 과정을 정리하고자 한다.</p>
</blockquote>
<blockquote>
<p>이 글의 코드 개선은 짧은 경험에서 나온 결과물이므로 정답이 아닐 수 있습니다. 더 좋은 방법이 있거나 잘못된 방향으로 코드 개선이 이루어졌다면 그에 관한 소중한 의견 부탁드립니다.</p>
</blockquote>
<h1 id="routesjs-단계별-개선">Routes.js 단계별 개선</h1>
<h3 id="1단계--일단-라우팅을-하자">1단계 : 일단 라우팅을 하자</h3>
<pre><code class="language-jsx">&lt;Router&gt;
  &lt;Switch&gt;
    &lt;Route exact path=&quot;/Home&quot; component={Home}&gt;&lt;/Route&gt;
    &lt;Route exact path=&quot;/Signup&quot; component={Signup}&gt;&lt;/Route&gt;
    &lt;Route exact path=&quot;/Login&quot; component={Login}&gt;&lt;/Route&gt;
    &lt;Route exact path=&quot;/ItemList&quot; component={ItemList}&gt;&lt;/Route&gt;
    &lt;Route exact path=&quot;/ItemDetail&quot; component={ItemDetail}&gt;&lt;/Route&gt;
  &lt;/Switch&gt;
&lt;/Router&gt;</code></pre>
<p>경로에 따라 보여지는 컴포넌트를 다르게 하는 기본적인 리액트 라우트 라이브러리의 기능을 구현한 코드이다.</p>
<h3 id="2단계--항상-보이는-공통-컴포넌트는-분리하자">2단계 : 항상 보이는 공통 컴포넌트는 분리하자</h3>
<pre><code class="language-jsx">&lt;Router&gt;
  &lt;Header /&gt;
  &lt;AsideMenu /&gt;
  &lt;Switch&gt;
    &lt;Route exact path=&quot;/Home&quot; component={Home}&gt;&lt;/Route&gt;
    &lt;Route exact path=&quot;/Signup&quot; component={Signup}&gt;&lt;/Route&gt;
    &lt;Route exact path=&quot;/Login&quot; component={Login}&gt;&lt;/Route&gt;
    &lt;Route exact path=&quot;/ItemList&quot; component={ItemList}&gt;&lt;/Route&gt;
    &lt;Route exact path=&quot;/ItemDetail&quot; component={ItemDetail}&gt;&lt;/Route&gt;
  &lt;/Switch&gt;
  &lt;Footer /&gt;
&lt;/Router&gt;</code></pre>
<p>많은 웹 페이지에는 Header와 Footer가 존재한다. 혹은 사이드 메뉴가 있는 경우도 있다. 이런 공통 컴포넌트는 해당 사이트 내에서 라우팅을 할 때 공통적으로 항상 보여지는 컴포넌트로 위 코드와 같이 분리하여 항상 보여지는 식으로 할 수 있다.</p>
<h3 id="3단계--페이지-별로-공통-컴포넌트-숨기기">3단계 : 페이지 별로 공통 컴포넌트 숨기기</h3>
<pre><code class="language-jsx">const HIDE_ASIDE_MENU = [&quot;/Login&quot;, &quot;/Signup&quot;];
const aside = HIDE_ASIDE_MENU.includes(window.location.pathname) ? null : &lt;AsideMenu /&gt;;

class Routes extends Component {
  render() {
    return (
      &lt;Router&gt;
        &lt;Header /&gt;
        {aside}
        &lt;Switch&gt;
          &lt;Route exact path=&quot;/Home&quot; component={Home}&gt;&lt;/Route&gt;
          &lt;Route exact path=&quot;/Signup&quot; component={Signup}&gt;&lt;/Route&gt;
          &lt;Route exact path=&quot;/Login&quot; component={Login}&gt;&lt;/Route&gt;
          &lt;Route exact path=&quot;/ItemList&quot; component={ItemList}&gt;&lt;/Route&gt;
          &lt;Route exact path=&quot;/ItemDetail&quot; component={ItemDetail}&gt;&lt;/Route&gt;
        &lt;/Switch&gt;
        &lt;Footer /&gt;
      &lt;/Router&gt;
    );
  }
}</code></pre>
<p>하지만 경우에 따라 공통 컴포넌트를 숨기고 싶은 경우도 있다. 만약 로그인 페이지와 회원가입 페이지에서는 사이드 메뉴 AsideMenu를 숨기고 싶을 때 위와 같이 코드를 작성할 수 있다. 만약 추가적인 변경사항이 있을 경우 <code>HIDE_AISDE_MENU</code> 변수를 수정하기만 하면 된다.</p>
<h3 id="4단계--잘못된-path-처리하기-404-페이지">4단계 : 잘못된 path 처리하기, 404 페이지</h3>
<pre><code class="language-jsx">const HIDE_ASIDE_MENU = [&quot;/Login&quot;, &quot;/Signup&quot;, &quot;/ItemCart&quot;];
const aside = HIDE_ASIDE_MENU.includes(window.location.pathname) ? null : &lt;AsideMenu /&gt;;

class Routes extends Component {
  render() {
    return (
      &lt;Router&gt;
        &lt;Header /&gt;
        {aside}

        &lt;Switch&gt;
          &lt;Route exact path=&quot;/Home&quot; component={Home}&gt;&lt;/Route&gt;
          &lt;Route exact path=&quot;/Signup&quot; component={Signup}&gt;&lt;/Route&gt;
          &lt;Route exact path=&quot;/Login&quot; component={Login}&gt;&lt;/Route&gt;
          &lt;Route exact path=&quot;/ItemList&quot; component={ItemList}&gt;&lt;/Route&gt;
          &lt;Route exact path=&quot;/ItemDetail&quot; component={ItemDetail}&gt;&lt;/Route&gt;
          &lt;Route exact path=&quot;/ItemCart&quot; component={ItemCart}&gt;&lt;/Route&gt;
          &lt;Route exact path=&quot;/Payment&quot; component={Payment}&gt;&lt;/Route&gt;
          &lt;Route component={NotFound} /&gt;
        &lt;/Switch&gt;
        &lt;Footer /&gt;
      &lt;/Router&gt;
    );
  }
}</code></pre>
<p>추가적으로 다른 페이지도 추가하고 마지막으로 404 페이지를 구현한 코드이다. 사용자가 주소이름을 잘못 입력하거나 어떠한 이유로 잘못된 path로 접근하려고 할 경우 <code>NotFound</code> 라는 컴포넌트가 <code>Switch</code> 내에서 보여진다. 이는 위에서부터 차례대로 해당하는 페이지가 있는지 탐색한 후 일치하는 경로가 없을 때 마지막으로 <code>NotFound</code>  페이지가 보이는 것이다. 이 컴포넌트에서 404 NOTFOUND에 대한 페이지 구성을 하면 될 것이다.</p>
<h1 id="라우트가-받는-값">라우트가 받는 값</h1>
<p>라우트가 받는 값에 대해 <a href="https://velog.io/@open_h/react-dynamic-routing">이전 글</a>에서 조금 다룬 적이 있다. 라우터가 받는 histoyr, location, match의 내부 값들 중 아래 세 가지 값을 살펴보자.</p>
<ul>
<li><code>location.pathname</code></li>
<li><code>match.path</code></li>
<li><code>match.url</code></li>
</ul>
<p>이 세가지 이름만 보면 다 비슷해 보인다. 콘솔창에 값을 찍어보는 것이 가장 정확할 것이다. 그래서 현재 진행중인 마켓컬리 클론 팀 프로젝트에서 한번 찍어보았다!</p>
<pre><code class="language-jsx">console.log(`location.pathname: ${this.props.location.pathname}`);
console.log(`match.path: ${this.props.match.path}`);
console.log(`match.url: ${this.props.match.url}`);</code></pre>
<p><img src="https://images.velog.io/images/open_h/post/b8019015-94ba-4728-a81d-05e07814e2dc/%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%202020-12-20%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%205.05.59.png" alt=""></p>
<p>하지만 결과는 처참히 같았다. 그러나 아직 방심하긴 이르다. 우리에겐 구글이 있다.</p>
<ul>
<li><code>location.pathname</code> : 현재 브라우저상의 위치를 알려준다. 즉 <code>/ItemDetail</code> 이라는 위치를 나타낸다.</li>
<li><code>match</code> 에서는 해당 Route와 직접적으로 관계된 값만 보여준다. <code>match.path</code> 에서는 <code>/ItemDetail/:id</code> 와 같이 동적 라우팅을 구현했을 때 <code>location.pathname</code> 과 차이가 나타나게 된다. <code>match.url</code> 에서는 그 동적 라우팅에서 id의 값이 주어져있다면 <code>/ItemDetail/3</code> 처럼 나타나게 된다. </li>
</ul>
<p>즉, 내가 <code>console.log</code> 로 확인한 컴포넌트는 아직 동적 라우팅으로 구현된 페이지가 아니기에 똑같이 나타난 것이다. 정리하자면 아래와 같다.</p>
<ul>
<li><code>location.pathname</code> : <code>/path</code></li>
<li><code>match.path</code> : <code>/path/:id</code></li>
<li><code>match.url</code> : <code>/path/43</code></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[리액트 동적 라우팅]]></title>
            <link>https://velog.io/@open_h/react-dynamic-routing</link>
            <guid>https://velog.io/@open_h/react-dynamic-routing</guid>
            <pubDate>Sun, 20 Dec 2020 05:58:11 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>이 글은 <a href="https://velog.io/@open_h/RESTful-API">RESTful API</a>에 관한 글을 작성한 후, 리액트에서 동적 라우팅을 구현하는 방식을 정리한 글이다. RESTful API에 애한 이해를 바탕으로 리액트에서 동적 라우팅 구현을 실제 코드와 함께 알아본다. 그리고 리액트에서 pagination을 어떻게 구현하는지도 알아본다.</p>
</blockquote>
<p>react-route를 사용하여 기본적인 페이지 라우팅을 할 수 있다는 전제하에 작성된 글입니다.
이 글에서 사용한 API 주소입니다. </p>
<ul>
<li><p>데이터: <a href="https://jsonplaceholder.typicode.com/">https://jsonplaceholder.typicode.com/</a></p>
</li>
<li><p>사진: <a href="https://robohash.org/">https://robohash.org/</a></p>
</li>
</ul>
<h1 id="리액트-동적-라우팅dynamic-routing">리액트 동적 라우팅Dynamic Routing</h1>
<h2 id="리액트에서-동적-라우팅">리액트에서 동적 라우팅</h2>
<p>리액트는 SPA로서 여러 화면으로 구성된 웹 어플리케이션을 만들 때 페이지별 라우팅 기능이 필요하다. 페이스북에서 공식적으로 제공되는 라우팅 라이브러리는 없으며, 보통 라우팅을 구현할 때 react-router라는 써드파티 라이브러리를 사용하여 라우팅 기능을 구현한다. </p>
<p>단순히 다른 페이지로 이동한은 것은 쉽게 라우트의 path를 설정하여 구현할 수 있다. 하지만 쇼핑몰의 제품 목록 중에서 하나를 클릭했을 때 해당 제품의 상세 페이지로 이동할 경우 어떻게 될까? <a href="https://velog.io/@open_h/RESTful-API">RESTful API</a>에서 살펴보았던 것처럼, parameter argument와 query string을 이용하면 된다.</p>
<p>아래 그림은 다음 항목 <em>코드 살펴보기</em>에서 살펴볼 코드가 구현하고자 하는 화면의 모습이다. <code>Monsters</code> 라는 페이지 컴포넌트 화면에서 <code>MonstersDetil</code> 이라는 페이지 컴포넌트로 넘어가는 것이다. 이 때, 특정한 카드를 클릭했을 때 그 카드에 해당하는 detail 페이지가 나타나게 동적 라우팅을 구현해야 한다.</p>
<p><img src="https://images.velog.io/images/open_h/post/c47c84cd-d55b-4839-b9d2-c160f203174b/image-20201220133511863.png" alt=""></p>
<h2 id="코드-살펴보기">코드 살펴보기</h2>
<p>리액트에서 <code>Routes.js</code> 파일의 코드부터 바로 살펴보자!</p>
<pre><code class="language-jsx">// Routes.js
export default class Routes extends Component {
  render() {
    return (
      &lt;Router&gt;
        &lt;Switch&gt;
          &lt;Route exact path=&quot;/monsters&quot; component={MonsterList} /&gt;
          &lt;Route exact path=&quot;/monsters/detail/&quot; component={MonsterDetail} /&gt;
          &lt;Route exact path=&quot;/monsters/detail/:id&quot; component={MonsterDetail} /&gt;
        &lt;/Switch&gt;
      &lt;/Router&gt;
    );
  }
}</code></pre>
<p><code>/monsters</code> 에서는 몬스터 사진, 이름, 이메일 주소가 담긴 몬스터 카드의 목록이 나타나고 몬스터 카드를 클릭하면 detail 페이지로 넘어가는 기능을 구현하고자 한다.  <code>/monster/detail/</code> 로 이동할 경우 &#39;어떤 디테일 페이지로&#39; 이동할지에 대한 정보를 가질 수 없다. 그러나 <code>/monsters/detail/:id</code> 라는 path의 경우 클릭한 몬스터의 id에 해당하는 몬스터의 detail 정보를 보여주는 페이지로 이동하는 기능을 구현할 수 있다.</p>
<blockquote>
<p>여기서 들어야 할** 의문점은 그럼 <code>MonsterDetail</code> 이라는 컴포넌트에게 path의 <code>/:id</code> 라는 정보를 어떻게 넘겨줄 수 있는가? 라는 의문이다. </p>
</blockquote>
<p>먼저 <code>MonsterList</code> 컴포넌트에서 <code>MonsterDetail</code> 컴포넌트로 라우팅 되는 코드를 살펴보자. 아래 코드는 하나의 카드 컴포넌트에 대한 코드이다.</p>
<pre><code class="language-jsx">// Card.js
class Card extends Component {
  goToDetail = () =&gt; {
    // 라우팅 할 때 id에 대한 정보를 함께 넘겨준다.
    this.props.history.push(`/monsters/detail/${this.props.id}`);
  };
  render() {
    return (
      &lt;div className=&quot;card-container&quot; onClick={this.goToDetail}&gt;
        &lt;img alt=&quot;monster&quot; src={`https://robohash.org/${this.props.id}`} /&gt;
        &lt;h2&gt;{this.props.name}&lt;/h2&gt;
        &lt;p&gt;{this.props.email}&lt;/p&gt;
      &lt;/div&gt;
    );
  }
}

export default withRouter(Card);</code></pre>
<p>위 코드에서 지금은 다른 부분은 신경쓰지 말고, 카드를 클릭했을 때 <code>onClick</code> , <code>goToDetail</code> 이라는 함수를 통해 다른 컴포넌트로 라우팅하는 코드임을 알아야 한다. 코드를 보면 <code>Card</code> 컴포넌트는 상위컴포넌트로부터 받은 <code>id</code> 를 <code>this.props.id</code> 로 접근한다. 그리고 그 <code>id</code> 값을 <code>this.props.history.push()</code> 에서 넘겨주고 있다. 그리고 img 태그의 주소도 그 <code>id</code> 값에 따라 바뀌고 있다는 것을 확인할 수 있다.</p>
<p>따라서 <code>Routes.js</code> 에서 본 path <code>/monsters/detail/:id</code> 가 <code>id</code> 에 대한 정보를 받아 <code>MonsterDetail</code> 에게 넘겨줄 수 있는 것이다. 이렇게 넘겨주는 것은 리액트 라우터 라이브러리를 사용하기 때문이다. 코드를 다시 살펴보면 <code>goToDetail</code> 함수에서  <code>this.props.history</code> 와 같이 object에 접근하는 dot 문법을 사용하고 있다.</p>
<p>이에 대해서 가장 확실하게 궁금증을 해결시킬 방법은 역시 <code>console.log</code> 로 직접 확인하는 것이다. <code>MonsterDetail</code> 컴포넌트가 받는 <code>props</code> 는 무엇인지 아래 코드로 알아보자. </p>
<pre><code class="language-jsx">// MonsterDetail.js
render() {
  console.log(&quot;this.props는?&quot;, this.props);
  return (
    // code...
  );
}</code></pre>
<p>위와 같이 <code>MonsterDetail</code> 컴포넌트 코드를 작성해서 <code>this.props</code> 가 무엇이 있는지 확인해보자. 사용자가 <code>MonsterList</code> 에서 <code>id</code> 가 5인  <code>Card</code> 를 클릭해 <code>MonsterDetail</code> 페이지로 넘어가는 경우에 <code>console.log</code> 의 결과값은 아래와 같다.</p>
<p><img src="https://images.velog.io/images/open_h/post/b42cdcf4-0f5b-406d-b011-29b21e3082d9/%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%202020-12-20%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%201.11.08.png" alt=""></p>
<blockquote>
<p><code>this.props</code> 에 있는  <code>history</code>, <code>location</code>, <code>match</code> 이 세가지에 대해 알아보아야 할 느낌이 강하게 든다. </p>
</blockquote>
<p>그러나 잠시 멈추고 props를 다시 짚어보기 위해 다른 코드를 하나 살펴보자. 아래 코드는 여러개의 <code>Card</code> 를 보여주기 위해 리액트에서 보통 사용하는 배열 매소드 <code>map</code> 을 사용한 코드이다. <code>CardList</code> 컴포넌트에서는 여러개의 <code>Card</code> 를 렌더한다.</p>
<pre><code class="language-jsx">// CardList.js
return (
  &lt;div className=&quot;card-list&quot;&gt;
    {monsters.map(monster =&gt; {
      return (
        &lt;Card
          key={monster.id}
          id={monster.id}
          name={monster.name}
          email={monster.email}
          /&gt;
      );
    })}
  &lt;/div&gt;
);</code></pre>
<p>위 코드와 같이 보통 props에는 개발자가 하위 컴포넌트에게 전달하고 싶은 변수명을 사용해 데이터를 직접 만들어 직접 넘겨준다. 하지만 라우트로 설정한 컴포넌트는 3가지의 props <code>history</code> , <code>location</code> , <code>match</code> 를 추가적으로 전달받게 된다.</p>
<ul>
<li><code>history</code> : 이 객체에서 <code>push</code> , <code>replace</code> 를 사용해 다른 경로로 이동하거나 앞, 뒤 페이지로 전환 할 수 있다.</li>
<li><code>location</code> : 이 객체는 현재 경로에 대한 정보를 담고 있으며 <code>/about?abc=def</code> 형식의 URL쿼리 정보도 가지고 있다.</li>
<li><code>match</code> : 객체에는 어떤 라우트 매칭이 되었는지에 대한 정보가 담겨 있으며 <code>/about/:myVar</code> 과 같은 형식의 <code>params</code> 정보를 가지고 있다.</li>
</ul>
<p>따라서 <code>MonsterDetail</code> 컴포넌트에서 아래와 같은 코드가 나타난다.</p>
<pre><code class="language-jsx">getData = () =&gt; {
  // 몬스터에 대한 정보: 이름, 이메일을 받아온다.
  fetch(
    `https://jsonplaceholder.typicode.com/users/${this.props.match.params.id}`
  )
    .then((res) =&gt; res.json())
    .then((res) =&gt; this.setState({ data: res }));
};
componentDidMount() {
  this.getData();
}</code></pre>
<p>즉, <code>this.props.history.push()</code> 에서 받은 <code>id</code> 에 대한 정보를 위 코드와 같이 <code>this.props.match.params.id</code> 로 부터 전달받는 것이다. 아래 그림과 같이 정리를 할 수 있다. 만약 <code>Card</code> 컴포넌트에서 <code>history.push</code> 를 할 때 path에 <code>id</code> 값을 그냥 3으로 고정시킨다면, 즉 <code>push.(&#39;monsters/detail/3&#39;)</code> 와 같이 코드를 작성한다면 어떤 카드를 클릭하더라도 id가 3인 몬스터의 정보가 <code>MonsterDetail</code> 페이지에 보여지게 될 것이다. 하지만 백틱 문법을 사용해 주소와 path를 동적으로 표현할 수 있어 클릭한 카드의 상세 정보를 볼 수 있게 되는 것이다.</p>
<p><img src="https://images.velog.io/images/open_h/post/86c50844-6e0c-4c04-8b73-6fd7e6d6887e/image-20201220141853679.png" alt=""></p>
<h2 id="pagination">Pagination</h2>
<p>많은 사이트에서 대량의 정보 한 페이지에 보여주기 힘드므로, 페이지별로 나누어 조금씩 보여준다. 한 페이지에 최대 몇 개의 아이템을 보여줄지 정하고, 사용자가 다음페이지 혹은 이전 페이지 뷰로 바꿀 수 있다. 혹은 페이지 숫자를 누르기도 한다. 이런 기능을 pagination이라고 한다.</p>
<p>리액트의 입장에서 보면 pagination은 하나의 컴포넌트에서 내부 데이터(state)만 달라지는 것이다. 하지만 리액트의 라이프사이클상 <code>componentDidMount</code> 는 최초 1회만 실행되고 컴포넌트의 state가 변하더라도 다시 실행되지 않는 함수이다.</p>
<p>따라서 pagination을 할 때는 <code>componentDidUpdate</code> 를 사용하야 한다. 아래 코드는 실제 <code>pagination</code> 보다 간소화된 경우이지만 pagination에서 <code>componentDidUpdate</code> 를 사용하는 이유를 이해하기에는 충분하다.</p>
<pre><code class="language-jsx">// MonsterDetail.js
getData = () =&gt; {
  fetch(
    `https://jsonplaceholder.typicode.com/users/${this.props.match.params.id}`
  )
    .then((res) =&gt; res.json())
    .then((res) =&gt; this.setState({ data: res }));
};
componentDidUpdate(prevProps, prevState) {
    this.getData();
}
goToPrevious = () =&gt; {
  this.props.history.push(
    `/monsters/detail/${+this.props.match.params.id - 1}`
  );
};
goToNext = () =&gt; {
  this.props.history.push(
    `/monsters/detail/${+this.props.match.params.id + 1}`
  );
};</code></pre>
<blockquote>
<p><code>match.params.id</code> 로 넘어온 데이터는 string type이기에 number type으로 바꿔주기 위해 <code>+</code> 를 붙여주었다.</p>
</blockquote>
<p>위 코드는 단순하기 다음 <code>id</code> 와 이전 <code>id</code> 에 대한 <code>MonsterDetail</code> 페이지로 이동하는 것이다. </p>
<p>물론 실제 pagination에서는 한 페이지에 여러개의 아이템을 보여준다. 하지만 여기서는 pagination을 구현하는 방법을 쉽게 이해하기 위해 위처럼 코드를 구현하였다. 만약 실제 pagination을 구현하려면 pagination이 구현된 API 주소를 사용해야 하며 아래와 같은 RESTful API 주소가 있을 수 있다. limit은 한 페이지에 보여줄 데이터 수, offset은 데이터가 시작하는 위치index일 경우에</p>
<pre><code>GET https://호스트/products?limit=10&amp;&amp;offset=10</code></pre><p>위와 같이 API를 사용할 수 있을 것이다. 다시 monster 예시로 돌아가자. </p>
<p>사실 위 몬스터 코드는 <strong>큰 문제</strong>가 발생한다. <code>this.getData()</code> 함수 내부에는 <code>setState</code> 가 불려지고 있다. 그리고 리액트 라이프사이클 상 <code>setState</code> 로 인해 <code>render</code> 가 다시 일어나고 다시 <code>componentDidUpdate</code> 가 실행된다. 따라서 <code>this.getData()</code> 는 무한으로 실행이 되는 것이다. <code>setState</code> 무한 반복에 빠지게 되버리는 것!</p>
<p>이런 문제를 해결하기 위해서는 <code>componentDidUpdate</code> 함수의 첫번째 인자와 두번째 인자를 사용할 필요가 있다. 리액트에서는  <code>componentDidUpdate</code> 함수의</p>
<ul>
<li>첫번째 인자로 <code>prevProps</code> 이전 props</li>
<li>두번째 인자로 <code>prevState</code> 이전 state</li>
</ul>
<p>로 만들어 두었다. 따라서 <code>setState</code> 의 무한 굴레에서 벗어나기 위해서는 아래와 같은 조건문이 필요하다.</p>
<pre><code class="language-jsx">componentDidUpdate(prevProps, prevState) {
  if (prevProps.match.params.id !== this.props.match.params.id) {
    this.getData();
  }
}</code></pre>
<p>따라서 이전 상태와 다를 경우(즉, 페이지가 바뀔 경우)에만 <code>getData()</code> 를 실행하게 되어 무한의 굴레에 빠지지 않게 된다.</p>
<p>실제 pagination을 구현할 때도 이와 같이 무한의 굴레에 빠지지 않기 위해서는 이전 props에 대한 정보와 현재 props에 대한 정보를 비교하는 로직이 필요할 것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[RESTful API]]></title>
            <link>https://velog.io/@open_h/RESTful-API</link>
            <guid>https://velog.io/@open_h/RESTful-API</guid>
            <pubDate>Sat, 19 Dec 2020 06:16:34 GMT</pubDate>
            <description><![CDATA[<h1 id="restful-api">RESTful API</h1>
<p>REST(Representational State Transfer)란 웹에 존재하는 모든 자원(이미지, 동영상, 텍스트 등의 데이터)에 고유한 URI를 부여하여 해당 자원에 대한 주소를 지정하는 방법론이다. 현재 가장 널리 사용되는 방식이며 RESTful API는 이러한 REST의 특징을 지키면서 API의 엔드포인트 구조를 구현하는 방식이다.</p>
<p>각 엔드포인트는 리소스를 표현하는 고유의 URI 주소를 가지고 있어 해당 리소스에 행할 수 있는 행위를 HTTP 메소드 표현을 처리할 수 있게 된다.</p>
<blockquote>
<p>URI의 개념은 URL의 superset으로 URI가 URL을 포함하고 있는 상위 set이다. URI는 Uniform Resource Identifier, URL은 Uniform Resource Locator이다. 마지막 뒷 단어가 다르다는 점을 주목하자. URL은 요청하는 주소가 실제 파일의 위치에 대한 정보(locator), URI는 의미 있는 식별자(identifier)를 뜻한다. 예를 들어 어떤 pdf파일 하나를 담고 있는 주소(브라우저에 보여지는 주소)를 보면 마지막에 <code>경로.pdf</code> 로 끝나는 경우가 있다. 이는 URL이라고 볼 수 있으며, URI는 URL처럼 특정한 파일을 가르키지는 않는다.</p>
</blockquote>
<p>RESTful API 통신의 간단한 예시를 살펴보자.</p>
<p><strong>클라이언트 Request</strong></p>
<ul>
<li>Method: GET</li>
<li>Protocol: https</li>
<li>Host: 10.10.1.1</li>
<li>Resource: products</li>
</ul>
<blockquote>
<p>GET <a href="https://10.10.1.1/products">https://10.10.1.1/products</a></p>
</blockquote>
<p><strong>서버 Response</strong></p>
<ul>
<li><p>200 OK</p>
<pre><code>{
    &quot;id&quot;: 1,
    &quot;name&quot;: &quot;동물복지 계란&quot;
    &quot;price&quot;: 5000
}</code></pre></li>
</ul>
<p>RESTful API라는 것은 URI 주소가 어떻게 생겼는지 살펴 보면 알 수 있다. REST한 API는 상황에 따라 path parameter와 query parameter를 사용하여 통신을 할 수도 있다. 이 두 가지에 대해 알아보자.</p>
<blockquote>
<p>크롬 브라우저를 사용하여 json 파일을 확인할 경우, json 파일을 사람이 보기 좋게 정렬하고 꾸며주는 크롬 extension들이 있다. 구글에 chrome json extesion과 같은 검색어로 검색 후 설치해서 사용해보자.</p>
</blockquote>
<p>다른 API 구현 방식으로는 GraphQL이라는 방식도 있다. 여기서 다룰 주제는 아니지만, RESTful과의 차이점을 간단하게 정리하자면 프론트엔드 쪽에서 서버의 데이터 전체에 접근해서 필요한 정보를 직접 쿼리해야 하는 방식이다.</p>
<h1 id="path-parameter">Path Parameter</h1>
<p><strong>path parameter</strong>가 무엇인지 예시 API URI로부터 알아보자. </p>
<blockquote>
<p>지금부터 이 글에서 사용할 API 주소는 <a href="https://jsonplaceholder.typicode.com/">https://jsonplaceholder.typicode.com/</a> 라는 무료로 제공되는 테스트 REST API 주소로, 실제로 클릭해서 데이터를 살펴볼 수 있다.</p>
</blockquote>
<p><strong>GET <a href="https://jsonplaceholder.typicode.com/posts">https://jsonplaceholder.typicode.com/posts</a></strong></p>
<p>위 API에서 posts의 모든 데이터를 GET method로 가져올 수 있다.</p>
<p><strong>GET <a href="https://jsonplaceholder.typicode.com/posts/1">https://jsonplaceholder.typicode.com/posts/1</a></strong></p>
<p>posts 중 id가 1인 post의 데이터를 얻을 수 있다.</p>
<p><strong>GET <a href="https://jsonplaceholder.typicode.com/posts/1/comments">https://jsonplaceholder.typicode.com/posts/1/comments</a></strong></p>
<p>post 중 id가 1인 post에 있는 comments의 데이터를 얻을 수 있다.</p>
<p>위와 같이 &#39;path/path/path&#39; 와 같은 형태의 API URI로 통신을 하게 된다. 이것이 path parameter를 사용하여 통신을 하는 것이다. GET의 예시만 들었는데, 물론 <strong>POST, PATCH, DELETE</strong> 와 같은 method로 해당 API URI에서 통신을 할 수도 있다. URI는 같을 수 있으며, 주고받는 데이터 request body, response body가 method별로 다를 것이다.</p>
<p>클라이언트 입장에서는 API 명세서에 맞게 적절한 request body를 보내고 response body를 받으면 된다. 서버 입장에서는 받은 request에 알맞게 response를 보내면 되는 것이다.</p>
<h1 id="query-string">Query String</h1>
<p>마찬가지로 예시 API URI로부터 API가 무엇인지 알아보자.</p>
<p><strong>GET <a href="https://jsonplaceholder.typicode.com/comments?postId=1">https://jsonplaceholder.typicode.com/comments?postId=1</a></strong></p>
<p>형태를 살펴보면 <code>?postId=1</code> 이라는 쿼리문처럼 생긴 부분이 있다. 본능적으로 느껴지는 그것! postId가 1인 comments를 가져오는 건가? 하면 그것이 맞다. 그리고 위 path parameter에서 </p>
<p><strong>GET <a href="https://jsonplaceholder.typicode.com/posts/1/comments">https://jsonplaceholder.typicode.com/posts/1/comments</a></strong></p>
<p>이런 주소를 예시로 들었는데, 이것과 동일한 데이터를 가져오게 되는 것이다. Query string 방법의 경우, 쿼리문 형태이기 때문에 아래와 같이 더 복잡한 조건을 줄수도 있을 것이다.</p>
<p><strong>GET <a href="https://jsonplaceholder.typicode.com/comments?postId=1&amp;id=5">https://jsonplaceholder.typicode.com/comments?postId=1&amp;id=5</a></strong></p>
<p>위 무료 API에서 제공하는 기능은 아니지만, query string을 이용하면 여러 방법으로 받아올 수 있다. id의 역순으로 데이터를 받아오는 것이 구현되어 있다면, 그러한 방식으로 query string을 주소에 작성하여 데이터를 받아 올 수 있다는 것이다. 받아오는 데이터의 갯수를 제한해서 가져오는 것 또한 query string으로 구현할 수도 있다. 검색 기능이 있는 곳에서 검색어에 따른 결과 데이터를 받아올 때도 사용할 수 있을 것이다.</p>
<h1 id="path-parameter-vs-query-string">path parameter VS query string</h1>
<p>위에서 언급했던 것 처럼</p>
<ul>
<li>path parameter: <strong>GET <a href="https://jsonplaceholder.typicode.com/posts/1/comments">https://jsonplaceholder.typicode.com/posts/1/comments</a></strong></li>
<li>query string: <strong>GET <a href="https://jsonplaceholder.typicode.com/comments?postId=1">https://jsonplaceholder.typicode.com/comments?postId=1</a></strong></li>
</ul>
<p>위 주소는 똑같이 postId가 1인 comments 데이터를 가져오는, 같은 결과를 만들어낸다.</p>
<blockquote>
<p>그렇다면, 언제 어떤 방법을 사용해야 할까?</p>
</blockquote>
<p><strong>Query parameter는 Filtering, Sorting, Searching</strong>일 경우에 사용한다. 따라서 <strong>Path parameter</strong>는 따로 정제되지 않은 데이터를 가져올 때 사용하면 된다.</p>
<h1 id="restful-하지-않은-api">RESTful 하지 않은 API</h1>
<p>RESTful 한 API를 이해하기 위해 RESTful하지 않은 API URI과 RESTful하게 바꾼 예시이다.</p>
<p><strong>나쁜 예시</strong> =&gt; <strong>좋은 예시</strong></p>
<ul>
<li>http://호스트/detail_page =&gt; http://호스트/product</li>
<li>http://호스트/main_page =&gt; http://호스트/products</li>
<li>http://호스트/find/product =&gt; http://호스트/product</li>
<li>http://호스트/add/product =&gt; http://호스트/product</li>
<li>http://호스트/product_reviews =&gt; http://호스트/product/1/reviews</li>
<li>http://호스트/product_filter =&gt; http://호스트/product?name=&quot;abc&quot;</li>
</ul>
<p>위 변화의 핵심은 <em>페이지(view)가 중심이 되어 받는 것이 아니라</em> <strong>자원(데이터)가 중심이되어</strong> API를 만들어야 한다는 것이다.</p>
<h1 id="실제-코드">실제 코드</h1>
<p>현재 진행중인 마켓컬리 사이트 클론에서 백엔드와 프론트엔드 통신에 사용한 테스트 코드이다. 아직 완성 단계가 아니라 간단한 코드이다. 아래 코드는 리엑트에서 장바구니에서 하나의 제품에 대해 수량을 추가하는 코드이다.</p>
<pre><code class="language-jsx">addItem = e =&gt; {
    fetch(&quot;http://10.168.1.160:8000/order/cart&quot;, {
      method: &quot;PATCH&quot;,
      body: JSON.stringify({
        cart_item_id: e.target.id,
        delta: &quot;plus&quot;,
      }),
    })
      .then(res =&gt; res.json())
      .then(result =&gt; {
        result.MESSAGE === &quot;SUCCESS&quot; ? this.getData() : console.log(&quot;실패!&quot;);
      });
  };
{/*이외에도 수량 감소, 삭제(DELETE) 등에 대한 함수가 있다.*/}
getData = async () =&gt; {
    const response = await fetch(&quot;http://10.168.1.160:8000/order/cart&quot;);
    const data = await response.json();
    this.setState({ cartData: data.items_in_cart });
  };</code></pre>
<p>우리가 주목해야할 것은 RESTful API 관점에서 주소 이름이다.</p>
<p>장바구니에서 수량 삭제와 추가(PATCH), 아이템 삭제(DELETE), 장바구니 데이터 받아오기(GET) 모두 같은 API URI에서 이루어진다는 점이다. 즉, <strong>장바구니 데이터</strong>가 중심이 되어 <code>order/cart</code> 라는 URI에 method에 따라 행할 수 있는 행위를 정하는 것이다. <code>/order/cart/add</code> 라는 곳에서 수량 조절을 것이 아니라는 점에서 RESTful API라고 할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[위코드 3~4주 되돌아보기]]></title>
            <link>https://velog.io/@open_h/look-back-2</link>
            <guid>https://velog.io/@open_h/look-back-2</guid>
            <pubDate>Sun, 13 Dec 2020 11:35:29 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>위코드 3~4주차 돌아보기.</p>
</blockquote>
<p>위코드에서 첫 한달이 지났다. <a href="https://velog.io/@open_h/wecode-week1-2">1~2주차 때처럼</a> 3~4주차 회고도 작성해보려고 한다. 사실 이 글을 쓰는 날 다음날부터 첫 팀프로젝트가 진행된다. 내 정신은 이미 내일부터 있을 팀 프로젝트에 쏠려있다. 기대감과 긴장감이 동시에 느껴진다. 그래서 당장은 지난 2주보다는 다음 2주 프로젝트에 더 신경쓰이는 것이 사실이다. </p>
<p>억지로 지난 2주를 떠올리며 글을 작성하는 것은 포기했다. 오늘 나는 지난 2주를 가볍게 떠올리며, 2주간 배운 것들과 경험한 것들을 토대로 아래 두 가지 고민을 했다.</p>
<ol>
<li>내일부터 있을 2주 <strong>팀 프로젝트에서 어떻게 해야할지</strong> 생각해보기</li>
<li>오늘 하루 <strong>잠시 개발 공부를 멈추고</strong>, 내가 지금 무엇을 하고 있는지 생각해보기</li>
</ol>
<h1 id="👨💻-지난-2주간-무엇을-했나">👨‍💻 지난 2주간 무엇을 했나</h1>
<h2 id="개발-공부">개발 공부</h2>
<ul>
<li>매일 알고리즘 한 문제 </li>
<li>리액트로 인스타그램 클론</li>
<li>깃과 깃헙 사용</li>
<li>회원가입과 로그인 기능으로 백엔드와 프론트엔드의 첫 만남</li>
<li>그외 개인 공부: 자바스크립트, 리액트 공부 등</li>
</ul>
<h2 id="그-외">그 외</h2>
<ul>
<li><p>프론트엔드 위스타그램 팀: 깃헙에서 동료 리뷰를 처음 해보았다.</p>
<p>처음으로 깃헙에서 다른 분의 코드에 <strong>리뷰</strong>를 남겨보았다. 5명이 한 팀이 되어 같은 기능을 하는 각자의 페이지를 구현하였는데, 서로의 코드를 보고 리뷰를 남겼다. 다른 분들의 코드를 보면서 배우는 점들이 많았다. 빠르게 타인의 코드를 해석하는 능력도 길러진 것 같다.</p>
<p>멘토님의 리뷰를 받고 배우는 점도 많았다. 예를 들어 나는 자바스크립트에서 short-circuit conditionals, evaluation을 알고 있지만 활용하지 못하고 있었다. 가독성의 측면에서 if, else문이 지나치게 사용되고 있을 때 활용할 수 있다는 것을 리뷰를 통해 알 수 있었다. 실제로 내 코드의 많은 부분에서 해당 문법을 적용시킬 수 있다는 것을 알게 되었다.</p>
</li>
<li><p>코로나 격상으로 인해 위코드는 100% 온라인 수업으로 변경되었다. <em>그러나...</em></p>
<p>사실 굉장히 아쉬운 부분이었다. 그러나 누구를 탓할수 없는 문제이기에 내가 변화된 환경에 적응해야했다. 특히 팀 프로젝트가 곧 시작되기에 동기분들 모두가 한 마음이되어 적극적으로 해결방법을 구하고 노력하였다. 방법은 찾았고, 나는 그 방법에 꽤 만족하고 있다.</p>
<blockquote>
<p>불가항력인 어쩔 수 없는 문제를 고민하며 힘들어하지 말고, 내가 해결할 수 있는 문제에만 집중하자.</p>
</blockquote>
</li>
<li><p>동기들과 매우 재미있게 지내고 있다.</p>
<p>무슨 일을 하든 함께하는 사람이 가장 중요하다는 것을 잘 알고 있다. 그리고 나는 우리 동기들 덕분에 재미있는 나날을 보내고 있다. 서로에게 힘이 되고 의지가 되는 것 같다.</p>
<p>그러나 동기분들 각자 위코드에서 느끼는 개발에 대한 고민은 다 다를 수 있을 것이라고 생각한다. 당장 내가 하는 고민과 나의 밥메이트 <del>를 가장한 내 담당 일진</del> 동기 채훈님의 고민은 다를 것이다. 그러나 모두가 개발 역량을 향상시키려는 동일한 목표를 가지고 있다. 그것을 바탕으로 서로 친해지면서 화이팅 넘치고 화목한 분위기가 잘 유지되는 것 같다. <del>특히 선동과 날조로 만들어낸 농담으로 언변을 뽐내는 분들이 즐거운(?) 분위기를 만들고 계신다.</del> </p>
</li>
</ul>
<h1 id="🤜🤛-2주-팀-프로젝트를-위해">🤜🤛 2주 팀 프로젝트를 위해</h1>
<blockquote>
<p>두근두근 팀 프로젝트 기대 중!</p>
</blockquote>
<p>자기 객관화가 쉬운 일은 아니지만 지금까지 나의 모습을 보았을 때 나는 멘탈이 꽤 튼튼하다고 생각한다. 코로나 때문에 정신없이 바뀌는 환경에도 꽤 잘 적응했고 그에 맞게 내 생활을 잘 유지했다. 정신적으로 크게 힘든 일도 없었다. 위코드의 퍼포먼스 코치님과 이야기를 나눌 때에도 코치님께서 나에게 긍정적인 에너지를 느꼈다고 했다. 그리고 코치님은 팀프로젝트에 대해 나에게 이런 조언을 해주셨다. 정확한 워딩은 아니지만 이런 의미였다.</p>
<blockquote>
<p>앞으로 있을 팀 프로젝트에서 팀원 사이의 어떤 충돌로 힘든 일이 있을 수 있다는 것을 <strong>미리 염두해 두기</strong>를 바란다. 물론 큰 충돌 없이 팀 프로젝트가 진행되면 그것대로 좋을 것이다. 그러나 문제가 발생했을 때  <strong>미리 문제 발생 가능성에 대해 스스로 자각해 두었을 때</strong> 그 힘듬과 충격이 미리 대비하지 않은 것보다 덜 할 것이며, 해결책도 침착하게 생각할 수 있을 것이다.</p>
</blockquote>
<p>이 말을 코치님게 듣고 나는 서로가 다르기에 프로젝트가 내 생각대로 흘러가지 않을 것에 대해 한 번더 미리 마음의 준비를 했다.</p>
<p>이전 기수분의 팀 프로젝트 회고를 몇 개 찾아보던 도중에 <a href="https://velog.io/@soom/%EC%8F%98%EB%A6%AC%EC%A7%88%EB%9F%AC-BroKurly">좋은 문구를 발견</a>했다.</p>
<blockquote>
<p>팀을 위한 개인의 희생은 쉽다. 이에 반해 개인의 동기를 유지하며 팀 케미스트리를 만드는 것은 전혀 다른 차원의 문제다. 진정한 팀워크란 전자보단 후자쪽이다.</p>
</blockquote>
<p>이 문구를 읽고 <strong>개인적으로 조금 찔렸다</strong>. 예전부터 나는 팀으로 하는 무언가 혹은 공동체에서 팀을 위한 개인의 희생을 쉽게 하는 편이었고 스스로도 그 사실을 알고 있다. 정말 쉬운 방법이라고 느꼈고, 내 마음도 편하고, 실제로 팀에게 도움이 되는 경우가 많았다. 그러나 누군가 내게 그 희생(혹은 배려)이라고 부를 만한 행동이 <strong>정말로 팀에게</strong> 도움이 되는 것이었냐고 물으면 쉽게 답을 하기 어려울 것이다.</p>
<p>야속하게도 쉬운 방법은 정말 쉬운 이유가 있다. 팀을 위한 개인의 희생운 분명 팀워크에 필요하며 도움이 되며, 때에 따라 꼭 필요하거나 최선의 해결책이 될 수도 있다. 그러나 내 경험상 개인의 희생 혹은 배려는 당장 발생한 문제에 대한 해결법일 뿐이거나 임시방편인 경우가 많았다. 그리고 본질적으로 튼튼한 진짜 팀워크가 유지되려면 결국 개개인의 동기 혹은 자세가 중요하다고 생각했다. </p>
<p>팀의 구성원 모두가 각자 개인의 동기를 가진 채 자신이 팀에 속한 이유를 명확히 알고 그 이유를 통해 존재감 느끼며, 내가 팀에서 무엇을 해야 하는지 자각하고 추가적으로 팀에 대한 애정까지 더해진다면? 그 팀이야말로 <strong>본질적으로 진짜 탄탄한 팀워크</strong>를 보여주지 않을까라는 생각이 든다. 그것을 위해 <strong>당장 내가 할 수 있는 일</strong>은 무엇을까 생각해보았다.</p>
<ol>
<li>일단 <strong>나부터</strong> 개인의 동기를 가지고 팀에 대해 위와 같은 자세를 가져야 할 것이다.</li>
<li>팀원들이 각자 동기를 가지고 위와 같이 팀에 대해 생각할 수 있도록 분위기를 조성한다.</li>
</ol>
<p>쉽지 않은 일이라고 생각한다. 그렇지만 첫 개발 팀 프로젝트인 만큼 내가 생각한대로, 할 수 있는 최선을 다 해보고 싶다.</p>
<h1 id="🥰-휴식">🥰 휴식</h1>
<p>한 달 동안 달려왔다. 잠자고 먹는 시간을 제외하고는 개발 공부에 매진했다. 개발 공부에 절대적인 시간을 많이 투자했다. 목표를 위해 열심히 달려가는 것은 매우 중요하지만, 이 쯤에서 잠시 멈추고 내가 달려가는 방향이 무엇인지 혹은 내가 무얼 위해 달려오고 있었는지 생각해볼 필요가 있다고 느꼈다. </p>
<blockquote>
<p>오늘 하루는 푹 쉬면서, 한 일이라곤 이 글 작성밖에 없다. 😁</p>
</blockquote>
<p>오늘 하루는 생각을 하면서 한달간 달려온 것에 대한 휴식, 팀 프로젝트 시작전 휴식도 겸해서 추진력을 위한 휴식이라고 생각한다. <del>추진력 핑계 좋았다ㅎㅎㅎ</del></p>
<p>맛있는 것도 만들어 먹으면서 힐링했지만, 무엇보다 침대랑 둘이 오래 데이트를 했다.🥰</p>
<p>하루종일 쉬면서 생각 정리도 하다보니 결국 사람은 왜 사는가부터 시작해서(...) 왜 개발자가 되었는지 등 다양한 주제로 많은 고민들을 했다. 오랫만에 개발 관련 기술서적이 아닌 일반 인문 서적도 조금 읽었다. 너무 글이 길어질 것 같고 오늘 블로그 주제와도 동떨어진 생각들이 많아서 여기서는 정리하지는 않으려 한다.</p>
<hr>
<p>무엇보다 내일부터 있을 팀 프로젝트가 너무 설렌다.</p>
<p><strong><em>우리 팀 화이팅!</em></strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바스크립에서 동기와 비동기]]></title>
            <link>https://velog.io/@open_h/javascriptasync</link>
            <guid>https://velog.io/@open_h/javascriptasync</guid>
            <pubDate>Sat, 12 Dec 2020 12:20:05 GMT</pubDate>
            <description><![CDATA[<h1 id="동기-비동기">동기, 비동기</h1>
<p>동기Syncronous와 비동기Asynchronous는 프로그래밍 언어에서 중요한 개념이다. </p>
<ul>
<li><strong>동기</strong>는 요청 후 응답을 받아야 다음 동작을 실행하는 방식을 말하며</li>
<li><strong>비동기</strong>는 요청을 보낸 후 응답과 관계없이 다음 동작을 실행하는 방식이다.</li>
</ul>
<h2 id="자바스크립트에서-동기와-비동기">자바스크립트에서 동기와 비동기</h2>
<p>자바스크립트는 단일 스레드 프로그래밍 언어로 단일 호출 스택이 있어 한 번에 하나의 일을 처리할 수 있다. 그러므로 <strong>자바스크립트는 동기</strong> 방식으로 진행이 된다. 하나의 호출 스택만 있기 때문에 하나의 함수를 처리하는데 매우 오랜 시간이 걸린다면 다음 실행해야할 함수에 지장을 줄 수 있다는 문제가 발생한다.</p>
<p>예를 들어 웹 페이지를 사용자에게 보여줄 때 해당 웹 페이지에 있는 모든 데이터(사진, 글 등)를 받고 나서야 화면이 보여진다고 하자. 서버에서 데이터를 모두 받아올 때까지 시간이 오래 걸릴 수 있으며 사용자 입장에서 웹 페이지를 보는데 너무 느려서 답답할 수 있다. 그러나 데이터를 <strong>받아오는 일을 하는 도중에</strong> 우선 웹 페이지의 기본 레이아웃을 보여주고 보여줄 수 있는 것들을 우선 보여주는 것이 더 바람직할 것이다. 마치 세탁기가 돌아가는 도중에 라면을 끓이는 것처럼!</p>
<p>이것이 비동기의 필요성이다. 그리고 아래와 같은 방법으로 이 문제를 해결할 수 있다.</p>
<ul>
<li>비동기적 callback 함수 사용</li>
<li>ES6 Promise</li>
<li>ES8 async await</li>
</ul>
<p>이전의 비동기 구현 패턴이 가졌던 단점을 극복하기 위해 새로운 문법이 등장한 순서대로 나열한 것이다. 단일 호출 스택을 가진 자바스크립트에서 비동기를 구현할 수 있게 한 위 세가지 방법을 차례대로 알아보자.</p>
<h1 id="call-back">call back</h1>
<p>콜백 함수란 다른 함수의 인자로 이용되는 함수이며 어떤 이벤트에 의해 호출되는 함수이다. 콜백 함수는 아래 코드와 같이 동기적으로 사용될 수도 있다. <code>primtImmediately</code> 라는 이벤트 함수가 인자로 함수를 받는 코드라는 점을 주목하자.</p>
<pre><code class="language-javascript">function printImmediately(callBackFunction) { 
  callBackFunction() 
}
printImmediately(()=&gt;console.log(&#39;synchronous callback&#39;))</code></pre>
<p>단순히 인자로서 함수를 받아 그 함수를 실행한다. 일반적인 자바스크립트 코드이기에 당연히 함수 호출 스택에 따라 동기적으로 실행된다. 우리가 구현하고자 하는 비동기적 과정은 여기서 일어나지 않는다. 우리는 비동기적 callback 함수를 사용해야 한다.</p>
<p>아래와 같이 비동기적인 콜백 함수Asynchronous callback 예제를 보자.</p>
<pre><code class="language-javascript">function printWithDelay(callback, sec){
  setTimeout(callback, sec*1000)
}
printWithDelay(()=&gt;console.log(&quot;async callback&quot;), 2)
console.log(&quot;hello&quot;)</code></pre>
<p>동기적인 방식을 따른다면 <code>printWithDelay()</code> 가 모두 완료한 뒤 &quot;hello&quot;를 출력해야 한다. 하지만<code>setTimeout</code> 을 사용해 2초 뒤에 &quot;async callback&quot;이 비동기적으로 출력된다. 따라서 &quot;hello&quot;가 먼저 출력이 되고 2초 뒤에 &quot;async callback&quot;이 출력이 된다.</p>
<p>다른 예시로 사용자가 어떤 버튼을 클릭했을 때 실행할 함수도 비동기 콜백 함수이다. 그 이전까진 실행하지 않다가 클릭이라는 이벤트가 발생했을 때 콜백 함수를 실행하는 것이다.</p>
<h2 id="어떻게-비동기가-작동하나">어떻게 비동기가 작동하나</h2>
<blockquote>
<p>앞서 자바스크립트는 하나의 호출스택을 가진 단일 스레드 프로그래밍 언어라고 말했다. 따라서 혼자서 비동기를 구현할 수 없다. 위 코드에서 어떻게 비동기가 구현된 것일까?</p>
</blockquote>
<p>자바스크립트 엔진만으로는 비동기적으로 구현할 수 없으므로 자바스크립트 실행 환경(Runtime)은 브라우저에서 제공하는 <strong>Web API</strong>를 사용하여 비동기를 구현하게 된다. DOM 이벤트, setTimeout과 같은 비동기 함수는 web API를 호출하여 콜백 함수를 콜백 큐에 넣는다. 콜백 함수들이 담긴 큐는 특정 시점에서 콜백을 실행시키는 방식이다. 중요한 내용이지만 이 글의 주제와 벗어나는 내용이므로 더 자세한 사항에 대해서는 내가 참고한 블로그 글 링크를 남긴다. </p>
<p>출처 및 자세한 내용: <a href="https://new93helloworld.tistory.com/361">https://new93helloworld.tistory.com/361</a></p>
<h2 id="콜백-지옥">콜백 지옥</h2>
<p>비동기 구현을 위한 첫 번째 방법인만큼 큰 문제가 있다. 콜백 함수가 콜백 함수를 부르고, 그 콜백 함수가 또 다른 콜백함수를 부르는 이른바 콜백 지옥이 발생하는 것이다.</p>
<p><img src="https://images.velog.io/images/open_h/post/73779b1e-7025-4548-8e69-b1e4362fb8fd/callbackhell.jpeg" alt=""></p>
<p>많은 중첩함수가 생겨 가독성과 유지보수면에 끔찍한 코드가 발생한다. 이를 해결하기 위해 Promise가 나오게 되었다.</p>
<h1 id="promise">Promise</h1>
<p>콜백 함수를 사용하지 않고 Promise object를 틍해 <del>지옥에 빠지지 않고</del> 어떻게 깔끔하게 비동기 함수를 처리할 수 있는지 바로 코드부터 확인해보자. 에러 처리에 관한 내용은 이 글에서 자세히 다루지 않는다. </p>
<pre><code class="language-javascript">const myPromise = new Promise((resolve, reject)=&gt;{
  console.log(&quot;doning some heavy work: network, read files&quot;)
  setTimeout(()=&gt;{
    // resolve(&#39;hi&#39;);
    reject(new Error(&#39;this is error msg&#39;));
  }, 2000);
})

myPromise.then(value=&gt;{
  console.log(value) // resolve 가 있다면 &#39;hi&#39; 출력 
})
.catch(error=&gt;{
  console.log(error) // reject에 있는 &#39;this is error msg&#39; 출력
})
.finally(()=&gt;{
  console.log(&#39;finally!!&#39;)
})</code></pre>
<p>위 코드의 경우 2초뒤에 &#39;this is error msg&#39;와 &#39;finally!!&#39; 가 출력된다. 만약, <code>resolve(&#39;hi&#39;)</code> 가 주석처리 되지 않았다면 <code>.then()</code> 에서 &#39;hi&#39; 가 출력되었을 것이다. </p>
<p>콜백 함수의 중첩과 같이 여러 promise 객체를 만들어 중첩시킬 수도 있다. 아래 예시 코드를 보자. 포켓몬 파이리의 진화 과정을 1초마다 보여주는 코드이다.</p>
<pre><code class="language-javascript">const initialPokemon = () =&gt;
  new Promise((resolve, reject)=&gt;{
    setTimeout(()=&gt;resolve(&#39;파이리&#39;), 1000)
  });

const nextPokemon = prevPokemon =&gt;
  new Promise((resolve, reject)=&gt;{
    setTimeout(()=&gt;resolve(`${prevPokemon} =&gt; 리자드`), 1000)
  });

const finalPokemon = prevPokemon =&gt;
  new Promise((resolve, reject)=&gt;{
    setTimeout(()=&gt;resolve(`${prevPokemon} =&gt; 리자몽`), 1000)
  });

initialPokemon() // 1초 소요
  .then(prev=&gt;{
    console.log(prev) // 파이리
    return nextPokemon(prev) // 1초 소요
  })
  .then(prev=&gt;{
    console.log(prev) // 파이리 =&gt; 리자드
    return finalPokemon(prev) // 1초 소요
  })
  .then(console.log) // 파이리 =&gt; 리자드 =&gt; 리자몽</code></pre>
<p>확실히 콜백 지옥보다 훨씬 로직이 깔끔하고 가독성도 뛰어나다는 것을 알 수 있다. 위 코드들을 사용하는 방법의 경우 promise chaining으로 <code>then</code> , <code>catch</code> 매소드를 사용하여 비동기를 관리하고 있다.</p>
<h1 id="async-await">async await</h1>
<p><code>asynce</code> <code>await</code> 는 비동기처리의 최신문법이다. 기존의 promise와 다른 것은 아니고, syntatic sugar일 뿐이다. promise를 사용할 경우에 callback처럼 chaining이 일어나는 것은 마찬가지이다. 따라서 콜백 지옥의 문제가 어느정도 나타날 수 있다는 것이다.</p>
<p>하지만 async await을 사용하면 promise를 &#39;깔끔한 스타일&#39;로 작성할 수 있다. 그러나 무조건 async await이 절대적으로 깔끔한 방법은 아니고 상황에 따라 적절한 것을 선택하면 된다. async await를 사용한 예시 코드를 살펴보자.</p>
<pre><code class="language-javascript">const getTodo = async (id) =&gt; {
  const todoResponse = await fetch(`https://jsonplaceholder.typicode.com/todos/${id}`)
  const todo = await todoResponse.json()
  return todo;
}

getTodo(172).then(console.log)</code></pre>
<p>fetch 함수에서 사용한 <a href="http://jsonplaceholder.typicode.com/">API</a>는 무료 가짜 데이터 REST API를 제공하는 API 주소이다. 간단한게 데이터를 가져오는 과정을 async await 문법을 사용한 코드이다. <code>async</code> 키워드는 함수 앞에 붙이는 키워드이며 <code>await</code> 키워드는 <code>async</code> 키워드가 붙어 있는 함수 내부에서만 사용할 수 있다. <code>await</code> 라는 코드로 의도한 순서대로 코드의 흐름을 제어하고 있다. 일반적인 동기 코드 처리와 동일한 흐름으로 코드를 작성할 수 있기에 코드 읽기가 수월해진다.</p>
<p>한 가지 주의할 것은 <code>async</code> 함수를 호출할 때 명시적으로 promise 객체를 생성하여 리턴하지 않아도 promise 객체가 리턴된다. 따라서 호출하는 코드를 보면 <code>then()</code> 매서드를 사용하여 결과값을 출력하고 있다.</p>
<p>async await의 또다른 장점은 동기와 비동기 구분없이 <code>try</code> <code>catch</code> 로 일관된 예외 처리를 할 수 있다는 점이다!</p>
<pre><code class="language-javascript">const myFunction = async (postId) =&gt; {
  const postResponse = await fetch(
    `https://jsonplaceholder.typicode.com/posts/${postId}`
  )
  const post = await postResponse.json()
  const userId = post.userId

  try {
    const userResponse = await fetch(
      // 1번 주소: `https://jsonplaceholder.typico`
      // 2번 주소: `https://jsonplaceholder.typicode.com/users/${userId}`
    )
    const user = await userResponse.json()
    return user.name || &quot;no data&quot;
  } catch (err) {
    console.log(err)
    return &quot;Unknown&quot;
  }
}

myFunction(15).then(console.log)</code></pre>
<p>위 코드에서 2번 주소가 정상적으로 작동하는 코드이다. <code>postId</code> 를 주면 해당 포스트의 <code>userId</code> 로부터 다시 <code>user</code> 의 이름을 얻어오는 코드이다. 2번 주소일 때, <code>postId</code> 혹은 <code>userId</code> 가 DB에 없을 경우 빈 객체가 반환되는데 그 때는 &quot;no data&quot; 를 반환하도록하였다. 데이터가 존재할 경우 <code>user.name</code> 을 정상적으로 반환한다. </p>
<p>그리고 1번 주소의 경우 에러 상황으로 <code>err</code>를 출력하고 (위 코드에서는 Error: &#39;Failed to fetch&#39; 라는 에러 발생) &quot;Unknown&quot;을 반환하게 된다.</p>
<h2 id="유용한-promise-apis">유용한 Promise APIs</h2>
<pre><code class="language-javascript">//Promise.all
//Promise.race
const getTodo = async (id=&quot;&quot;) =&gt; {
  const todoResponse = await fetch(`https://jsonplaceholder.typicode.com/todos/${id}`)
  const todo = await todoResponse.json()
  return todo;
}

const allExample = async () =&gt; {
  const todo1 = getTodo(1)
  const todo2 = getTodo(2)
  return Promise.all([todo1, todo2])
}

const raceExample = async () =&gt; {
  const todo1 = getTodo(1)
  const todo2 = getTodo(2)
  return Promise.race([todo1, todo2])
}

// todo1과 todo2 모두 출력
allExample().then(console.log)
// todo1과 todo1 중 빨리 되는 것만 출력.
// 실제로 코드 실행 결과 1이 나올 때도 있고 2가 나올 때도 있었다.
for(let i=0; i&lt;5; i++) raceExample().then(todo=&gt;console.log(todo.id))</code></pre>
<p><code>Promise.all</code> 은 순회 가능한 객체에 주어진 <strong>모든 promise 객체를 이행한 후</strong>에 promise 객체를 반환한다. 만약 하나라도 거부하는 경우 첫 번째로 거절된 promise의 이유로 자신도 거부한다. <code>Promise.race</code> 는 순회 가능한 객체에 주어진 promise 들 중에 <strong>가장 먼저 완료된 것</strong>의 결과값을 그대로 이행하거나 거부한다.</p>
<pre><code class="language-javascript">const myFunction = async (postId) =&gt; {
  const postResponse = await fetch(
    `https://jsonplaceholder.typicode.com/posts/${postId}`
  )
  const post = await postResponse.json()
  const userId = post.userId

  try {
    const userResponse = await fetch(
      //`https://jsonplaceholder.typico`
      //`https://jsonplaceholder.typicode.com/users/${userId}`
    )
    const user = await userResponse.json()
    return user.name || &quot;empty&quot;
  } catch (err) {
    console.log(err)
    return &quot;Unknown&quot;
  }
}

myFunction(15).then(console.log)</code></pre>
<p><strong>참고자료</strong></p>
<p>유튜브 <a href="https://youtu.be/s1vpVCrT8f4">드림코딩 by 엘리</a></p>
<p>유튜브 <a href="https://youtu.be/m0icCqHY39U">얄팍한 코딩사전</a></p>
<p>블로그 <a href="https://medium.com/@yoohl/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%B9%84%EB%8F%99%EA%B8%B0-%EB%8F%99%EA%B8%B0-ac9495e42d0">자바스크립트 비동기 처리</a></p>
<p>블로그 <a href="https://www.daleseo.com/js-async-async-await/">https://www.daleseo.com/js-async-async-await/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[인증, 인가, fetch]]></title>
            <link>https://velog.io/@open_h/authentication-authorization-fetch</link>
            <guid>https://velog.io/@open_h/authentication-authorization-fetch</guid>
            <pubDate>Thu, 10 Dec 2020 04:49:58 GMT</pubDate>
            <description><![CDATA[<h1 id="인증--인가">인증 &amp; 인가</h1>
<p>인증과 인가는 API에서 가장 자주 구현되는 기능중 하나이다. 인증이란 유저의 신원(아이디와 패스워드)을 확인하는 절차이며, 인가란 (로그인 한)유저의 요청에 대해 그 요청을 할 수 있는 권한이 있는 유저인가 확인하는 절차이다.</p>
<h2 id="인증authentication">인증Authentication</h2>
<p>가장 먼저 회원가입 절차, 즉 사용자의 아이디와 비밀번호를 서버의 DB에 저장하는 과정이 먼저 필요할 것이다. 이때 비밀번호는 암호화되어 저장된다. 다음으로 로그인을 할 때 사용자가 입력한 아이디와 비밀번호가 DB에 있는지 확인한 후 성공할 경우, 서버에서는 클라이언트에게 허가 토큰(access token)을 전송하게 된다. 사용자는 한 번 로그인 정보를 입력한 후에는 이 토큰을 첨부해서 서버에 요청을 보내게 된다.</p>
<h3 id="비밀번호-암호화">비밀번호 암호화</h3>
<p>비밀번호 암호화에는 일반적으로 단방향 해쉬 함수(one-way hash function)가 쓰인다. 이 함수는 원본 메세지(사용자의 실제 비밀번호)를 변환하여 암호화된 메세지를 생성한다. 예를 들어 hash256이라는 해쉬 함수는 &quot;test password&quot;를 </p>
<p>0b47c69b1033498d5f33f5f7d97bb6a3126134751629f4d0185c115db44c094e</p>
<p>라는 값으로 변환시킨다. &#39;단방향&#39;의 특성상 비밀번호로부터 암호화된 메세지를 만들기는 쉽지만, 반대로 암호화된 메세지를 원본 메세지로 복구할 수 없다. 그러나 원본메세지가 같으면 암호화된 메세지도 같기에 미리 해쉬 함수의 결과값들을 거대한 표로 만들어 놓고 표로부터 해킹을 할 수 있다는 취약점이 있다. 장비를 사용하여 1초당 56개의 다이제스트(해쉬 결과값)을 대입할 수 있다.</p>
<p>이런 문제를 보완하기 위해 일반적으로 사용하는 방법으로 <strong>salting</strong>과 <strong>key stretching</strong>을 사용한다. </p>
<ul>
<li><p>salting은 실제 비밀번호에 추가적인 랜덤 데이터를 더해서 해시값을 계산하는 것이다. </p>
</li>
<li><p>key stretching은 해쉬값을 계산 한 후 그 해쉬값을 다시 해쉬하는 과정을 반복하는 방법이다. key stretching을 사용하면 1초에 50억 개 이상의 다이제스트를 표에서 비교할 수 있는 장비가 1초에 5번 정도만 비교할 수 있게 된다. 컴퓨터 성능이 향상되거나 GPU를 사용하여 해킹의 성능이 올라갈 경우, key stretching도 따라서 더 반복 해쉬를 하여 보완할 수 있다.</p>
</li>
</ul>
<p>이 두 가지를 구현한 해쉬 함수중 가장 널리 사용되는 것이 bcrypt이며 이 함수는 만들어질 때부터 비밀번호를 단방향 암호화하기 위해 만들어진 해쉬함수이다. 이쯤에서 난 아래와 같은 궁금증이 생겼다.</p>
<blockquote>
<p>데이터를 보호한다면서 모두가 동일한 bcrypt를 사용해도 되는걸까?</p>
</blockquote>
<p>이 질문에 대한 답변이다. <strong>원희</strong>님께서 알려주셨다. 케르크호프스의 원리: <a href="https://ko.wikipedia.org/wiki/%EC%BC%80%EB%A5%B4%ED%81%AC%ED%98%B8%ED%94%84%EC%8A%A4%EC%9D%98_%EC%9B%90%EB%A6%AC">링크</a></p>
<p>요약하자면 암호화 방법은 그 방법이 모든 사람에게 알려지더라도 안전해야 하는 것이 더 중요하다는 의미이다. 이런 암호화 해쉬 함수는 굉장히 만들기가 어려우며 개발자의 영역이 아니라 수학자의 영역이라고 볼 수 있다. 즉, 오픈된 방법이라도 해킹하기 어려운 암호화 방법을 채택하는 것이 바람직하는 것이다.</p>
<h3 id="json-web-tokens">JSON Web Tokens</h3>
<p>앞서 언급한 허가 토큰(access token)중 널리 사용되는 기술 중 하나가 JWT(JSON Web Tokens)이다. 유저가 로그인에 성공한 이후에 클라이언트와 서버와 통신할 때 인증된 유저임을 밝히기 위해 로그인 없이 저장된 token을 계속 사용하게 된다.</p>
<p>이 방법은 먼저 퍼포먼스 측면에서 좋다. 무거운 bcrypt call을 계속해서 할 필요가 없다. 그리고 클라이언트 측에서도 실제 아이디와 비밀번호를 저장하는 것이 아니라는 장점이 있따. 쿠키와 같은 브라우저에 토큰을 저장하며 토큰 자체가 특정한 서버에서만 사용할 수 있고 토큰에 기한 설정 등을 할 수 있어 보안 문제도 해결이 된다.</p>
<h2 id="인가authorization">인가Authorization</h2>
<p>인가는 위에서 언급한 JWT를 통해서 구현될 수 있다. 사용자마다 권한이 각자 다를 수 있다. 프리미엄 구독자인지, 사이트 관리 권한자인지, 일반 고객인지 등. 토큰에서 해당 유저 정보를 얻을 수 있으므로 해당 유저가 가진 권한도 확인할 수 있다. 서버에서 클라이언트로 받은 받은 토큰을 복호화해서 user id를 얻으면 데이터베이스에서 해당 유저가 어떤 권한인지 알 수 있게 된다. 서버에서는 권한에 따라 다른 에러 코드를 보낼 수 있다.</p>
<h1 id="fetch-함수-사용법"><code>fetch</code> 함수 사용법</h1>
<p>위에서 이야기한 인증, 인가와 같은 경우에 백엔드와 프론트의 통신이 필요하다. 물론 페이지 렌더링을 위한 일반적인 데이터도 마찬가지이다. 웹 페이지가 백엔드로부터 데이터를 받아올 때 api를 호출하게 된다. 이 때 자바스크립트는 Web API <code>fetch()</code> 함수 혹은 axios 라이브러리를 사용할 수 있다. 실무에서는 axios를 사용하지만, 자바스크립트 내장 함수 <code>fetch()</code> 로도 웬만한 기능을 충분히 구현할 수 있다.</p>
<blockquote>
<p>사실 위 두가지의 사용법 보다는 http 통신 요청과 응답, promise 개념이 더 중요하다. 이 글에서는 우선 <code>fetch</code> 함수의 사용법에 대해 알아본다.</p>
</blockquote>
<h2 id="get">GET</h2>
<p>api 명세서를 보고 <code>fetch</code> 가 어떻게 사용되는지 바로 살펴보자! <code>fetch</code> 함수의 default method는 get이다.</p>
<pre><code>유저 정보를 가져오는 api
base url: https://api.homepage.com
endpoint: user/3
method: get
응답형태:
{
  &quot;success&quot;: boolean,
  &quot;user&quot;: {
    &quot;name&quot;: string,
    &quot;batch&quot;: number
  }
}</code></pre><pre><code class="language-jsx">componentDidMount() {
  const { userId } = this.props;

  // 만약 get method에서 parameter를 전달해야 하는 경우 아래와 같이
  // query string으로 넘겨줘야 할 수도 있습니다.
  fetch(`https://api.homepage.com/user?id=${userId}`)
    .then(res =&gt; res.json())
    .then(res =&gt; {
      if (res.success) {
          console.log(`${res.user.name}` 님 환영합니다);
      }
  });
}</code></pre>
<h2 id="post">POST</h2>
<p>post일 경우에 api 명세서와 <code>fetch</code> 함수 사용법이다.</p>
<pre><code>유저 정보를 저장한다.
base url: https://api.homepage.com/user/signup
endpoint: /user
method: post
요청 body:
{
  &quot;name&quot;: string,
  &quot;batch&quot;: number
}

응답 body:
{
  &quot;success&quot;: boolean
}</code></pre><pre><code class="language-jsx">fetch(&#39;https://api.homepage.com/user/signup&#39;, {
  method: &#39;post&#39;,
  body: JSON.stringify({
    name: &quot;janghyeon&quot;,
    batch: 1
  })
})
  .then(res =&gt; res.json())
  .then(res =&gt; {
  if (res.success) alert(&quot;저장 성공!&quot;);
})</code></pre>
<h2 id="jsonstringify-json"><code>JSON.stringify()</code>, <code>json()</code></h2>
<p>http통신에서 주고받는 데이터는 모두 <strong>string type</strong>이다. 그리고 객체 형태의 데이터가 개발자 입장에서는 더 바람직하다. 이 두 함수 <code>stringify()</code> 와 <code>json()</code> 은  <code>.json</code> 형태의 데이터와 <code>string</code> 형태의 데이터 사이의 변환을 해주는 함수이다.</p>
<p>서버에서 보내주는 response를 <code>response.json()</code> 을 거친 후 직접 콘솔에 출력해보면 객체 형태로 데이터를 보기 쉽다. 그리고 데이터를 전송할 때는 string type으로 전송해야 하기 때문에 <code>JSON.stringify</code> 로 데이터를 가공해준다.</p>
<h2 id="첫-번째-then-함수에-추가되는-로직">첫 번째 then 함수에 추가되는 로직</h2>
<p>하나의 요청에서 응답 body가 올수도 있지만 백엔드 응답에서 body를 주지 않는 경우가 있을 수 있다. 만약 status code만 받고 body가 없을 경우 <code>response.json()</code> 을 호출하면 에가 발생하게 된다. 따라서 body의 유무에 따라 <code>json()</code> 함수를 사용할지 말지에 대한 로직을 추가해야 한다. body의 유무는 백엔드에서 보내주는 status code의 값에 따라 알 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS 배열 순회 함수 몇 가지]]></title>
            <link>https://velog.io/@open_h/javascript-array-method</link>
            <guid>https://velog.io/@open_h/javascript-array-method</guid>
            <pubDate>Sun, 29 Nov 2020 08:36:52 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>자바스크립트의 몇 가지 배열 순회와 관련된 매서드를 정리해본다.</p>
</blockquote>
<p>어떤 매서드를 사용할 때 처음 사용하거나 잘 기억나지 않으면 MDN을 찾아보게 된다. 마찬가지로 배열 매서드도 MDN을 살펴볼 수 있다. 이 때, 중요하게 생각해야 할 점이 있다.</p>
<ol>
<li>매서드가 인자로 무엇을 받는지</li>
<li>매서드가 반환하는 것은 무엇인지.</li>
</ol>
<p>매서드들도 결국 함수임으로 <strong>argument</strong> 와 <strong>return value</strong> 가 무엇인지 파악하는 것이 가장 중요하고, 사실 그것만 파악해도 함수를 사용하는데 무리가 없을 것이다.</p>
<h1 id="1-myarrayforeach">1. myArray.forEach</h1>
<p>콜백함수를 인자로 받아 return 값 없이 배열을 순회하며 콜백함수 내에서 어떤 로직을 실행하면 된다. 아래는 코드는 MDN 자료.</p>
<pre><code class="language-javascript">myArray.forEach(callback(currentvalue[, index[, array]])[, thisArg])
// currentValue: 처리할 현재 요소
// index: 처리할 현재 요소의 index(선택적)
// array: forEach()를 호출한 배열(선택적)
// thisArg: callback을 실행할 때 this로 사용할 값(선택적)
// 반환: undefined</code></pre>
<p>사실 sksms 지금까지 <code>map</code> 이나 <code>reduce</code> 를 사용할 수 있는 상황에서도 forEach만 주구장창 사용해서 익숙한 함수이다. 사실 <code>for</code> 문을 사용해 배열을 순회하는 로직과 차이가 없다. 그래서 차라리 여기서는 <code>for</code> 문으로 배열을 순회하는 것과 <code>forEach</code> 로 순횐하는 것의 차이를 알아보았다.</p>
<p>출처: <a href="https://alligator.io/js/foreach-vs-for-loops/">https://alligator.io/js/foreach-vs-for-loops/</a></p>
<p>둘 중 무엇을 선택할지는 사실 개발자 마음이다. 하지만 선택을 하기 위해 차이점을 알아보자. </p>
<ol>
<li><code>forEach</code> 는 콜백함수를 사용하기에 변수의 scope를 <code>forEach</code> 순회 내부로 제한할 수 있다.</li>
<li><code>for</code> 문을 사용할 때는 <code>i</code> 와 같은 increment variable을 사용하게 되는데 <code>i</code> 와 같은 변수 사용에 있어서 실수가 발생할 수 있다.예를 들어 <code>i</code> 범위나  배열 밖을 참조하는 등의 실수가 발생할 수 있다. 예를들어 그러나 <code>forEach</code> 를 사용하면 따로 그런 </li>
<li>누가봐도 가독성은 <code>forEach</code> 가 더 낫다. 접근할 때 <code>myArray[i]</code> 가 아니라 <code>myElement</code> 와 같은 변수로 접근할 수 있다.</li>
</ol>
<p>위 세가지만 보면<code>forEach</code> 가 압승인 것을 볼 수 있다. 하지만! <code>for</code> 이 더 유용한 경우가 분명 있다. </p>
<ol>
<li>조건에 따라 배열 순회를 중간에 멈춰야 할 경우 <code>for</code> 는 조건과 <code>break</code> 로 <code>for</code> 문을 빠져나올 수 있다. 다시 말해 성능상 우위를 가지게 된다.</li>
</ol>
<p>억지로 <code>forEach</code> 의 콜백함수를 중간에 중단시키는 방법이 있기는 하다. <a href="https://stackoverflow.com/questions/6260756/how-to-stop-javascript-foreach">링크 참조</a>. 그러나 그런 의도로 만들어진 <code>forEach</code> 가 아니며 코드가 못생겨진다.</p>
<p>또한 <code>some()</code> 이나 <code>every()</code> 와 같은 배열 매서드를 사용해도 순회 중간에 멈출 수 있도록 구현할 수 있다.</p>
<h1 id="2-myarraymap">2. myArray.map</h1>
<p>콜백함수를 인자로 받아 <code>myArray</code> 와 동일한 크기의 배열을 반환한다. 아래 코드는 MDN 자료.</p>
<pre><code class="language-javascript">myArray.map(mapFn[, thisArg])
// mapFn: 콜백 함수
// mapFn의 인자는 차례대로 currentValue, index(선택), array(현재 호출된 배열)이다.</code></pre>
<pre><code class="language-javascript">const numbers = [1, 2, 3, 4, 5];
const doubles = numbers.map( number =&gt; number*2 ) // doubles: [2, 4, 6, 8, 10]</code></pre>
<h1 id="3-myarrayreduce">3. myArray.reduce</h1>
<p>콜백함수를 인자로 받아 <strong>하나의 결과 값</strong>을 반환한다. 아래 코드는 MDN 자료.</p>
<pre><code class="language-javascript">myArray.reduce(callback[, initialValue])
// 콜백 함수는 4개의 인자를 가진다
// accumulator: 누산기. 콜백 함수의 반환값을 누적하며 초깃값으로 정해줄 수 있다.
// currentValue: 처리할 현재 요소
// currentIndex(선택적): 처리할 현재 요소의 인덱스
// array(선택적): reduce 함수를 호출한 배열
// initialValue(선택적): 콜백함수 최초 호출시 누산기의 값. 비워둘 경우 배열의 첫 번째 요소 사용. 빈 배열에서 초깃값 없이 reduce를 호출할 경우 오류 발생.</code></pre>
<p>처음 썼을 때 조금 헷갈렷었던 함수이다. 예제를 하나 남긴다!</p>
<pre><code class="language-javascript">function find_longest_word(arr) {
  return arr.reduce((longestWord, currentWord)=&gt;{
    if(longestWord.length &lt; currentWord.length) return currentWord;
    return longestWord;
  })
}</code></pre>
<p><code>reduce</code> 함수 사용에 주의할 점이다. 반환 값으로 누산기 값을 주어야 한다. 위 예제의 경우 가장 긴 단어를 누산기 값으로 반환하고 있다. 더 쉬운 예제를 생각해보면 누산기라는 단어 듯 그대로 배열의 모든 숫자를 더할 때를 생각해도 편하다.</p>
<h1 id="결론">결론</h1>
<p>퍼포먼스 기준, 가독성 기준 등 여러 기준에 따라 필요한 적절한 함수를 골라서 사용하면 된다. 배열 순회와 관련된 함수는 그 외에도 <code>filter</code> , <code>findIndex</code>  등이 있다. 순회가 아니더라도 배열과 관련된 매서드가 많으며 아래 이 <a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array">MDN 링크</a>에서 확인 할 수 있으니 상황에 맞게 골라 쓰자!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[위코드 첫 2주 되돌아보기]]></title>
            <link>https://velog.io/@open_h/wecode-week1-2</link>
            <guid>https://velog.io/@open_h/wecode-week1-2</guid>
            <pubDate>Sun, 29 Nov 2020 01:02:11 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>위코드에서 2주가 지났다. 나는 무엇을 했나? 그리고 나는 무엇을 배웠는가? <del>돈값은 했는가?</del> </p>
</blockquote>
<blockquote>
<p>위코드 2주 생활을 되돌아보며 단기적으로는 위코드 생활을 더 잘하기 위해 2주 생활을 정리하는 시간을 갖고자 한다. 장기적으로도 개발 공부를 이어나갈 수 있는 원동력을 가질 수 있는 발판과 초심을 기록하고자 한다.</p>
</blockquote>
<h1 id="✍️-사전-스터디-1개월">✍️ 사전 스터디 1개월</h1>
<h2 id="우리-사전-스터디팀-😀">우리 사전 스터디팀 😀</h2>
<p>나는 위코드에서 묶어준 인원과 위코드 시작 전, 한달 정도 스터디를 진행했다. 비록 온라인이지만 첫 미팅에 꽤 긴장하면서도 기대를 많이 하면서 했던 걸로 기억한다. 우리팀은 각자 자기 소개와 함께 개발 관련해서 어떻게 공부하고 있었는지 공유했다. 우리 스터디 사람들은 모두 <strong>고수의 냄새</strong>가 났다(그리고 실제로도 고수다). 나는 열심히 해야 겠다는 좋은 자극을 받았다. </p>
<p>우리는 위코드에서 제공하는 사전 스터디 가이드를 참고하여 <strong>각자 수준에 맞게 개인 공부</strong>를 하기로 정했다. 우리는 각자 수준에 맞게 개인적으로 스터디를 진행했고, 이후 온라인 미팅에서 그동안 어떤 것을 공부했는지 공유하기도 하였다. </p>
<blockquote>
<p>불성실한 우리 팀원들이었지만... 온라인 미팅을 항상 열어 주던 팀장님 감사합니다. </p>
</blockquote>
<p>오프라인으로도 시간이 되는 인원들끼리 두 번정도 모였고, 두 번다 좋은 분위기에서 서로에게 자극도 되고 힘도 되는 좋은 시간이었다. 실제로 두 번째 오프라인 미팅 때는 &quot;진작 이렇게 모였으면, 서로 더 힘내서 공부 열심히 할 수 있었을 텐데...&quot;라는 누군가의 의견에 모두가 공감할 정도였다. 나도 사전 스터디가 아쉽게 느껴졌기에 공감했던 말이다.</p>
<p>즉, 사전 스터디를 열심히 안했다면 코로나 탓으로 돌리면 된다!🙃 <del>아니다. 본인 탓이다.</del></p>
<blockquote>
<p>우리 사전 스터디팀 init6 모두 앞으로도 화이팅!</p>
</blockquote>
<h2 id="나는-어떻게-했나-🤨">나는 어떻게 했나 🤨</h2>
<p>사전 스터디는 위코드에서 크게 관여하는 과정은 아니다. 어떻게 보면 오히려 그랬기에 <strong>추후 위코드 3개월</strong>과 <strong>그 이후</strong>를 위해 사전 스터디 1개월 동안 <strong>나에게 맞는 계획</strong>을 열심히 준비했다. <del>돈값을 하기 위한 철저한 몸부림</del> 사전 스터디 기간에 나는 백수였기에 당연하게도 대부분의 시간을 공부에 투자하기로 했다.</p>
<blockquote>
<p>내 MBTI를 잘 몰랐는데 위코드에서 해본 결과 나는 철저한 계획에 동기부여가 되는 스타일이라고 한다. 매우 공감한다. 나는 무언가를 할 때 체계적인 계획이 있어야 심적으로 편안해진다.</p>
</blockquote>
<ol>
<li><p>내 생활 습관을 앞으로 있을 위코드 생활에 맞춘다.</p>
<p>집에서 위워크까지는 너무 멀었다. 위코드가 시작되면 위워크 근처에 고시텔을 잡을 계획을 했고, 6군데 발품을 팔아 한 곳으로 정했다. 예상되는 위코드 생활에 맞추어 절대적인 학습 시간을 늘릴 생각으로 생활 습관을 만들었다. 팀 프로젝트 때 밤을 새며 코딩하는 것을 후기로 많이 봤지만... 그치만... 첫달은 가능할지도...?</p>
</li>
<li><p>위코드에서 추천하는 스터디 자료를 모두 학습한다.</p>
<p>양은 꽤 많았지만, 마인드셋을 위해 전부 할 수 있을 것이라고 <strong>자신감</strong>을 가졌다. 결국 전부 본 것은 아니지만 꽤 많은 양을 보았고, 개인적으로 codecademy라는 유료 사이트(1주일 무료)의 교육 자료 퀄리티가 높았던 것으로 기억한다.</p>
</li>
<li><p>천재지변과 같은 피치 못할 사정이 아니면 하루도 빠짐없이 열심히 공부를 한다.</p>
<p>나는 <em>피치 못할 사정</em>이라는 것을 스스로 잘 만드는 재주가 있었다. 예를 들면 오늘은 피치 못하게 이불이 너무 따뜻하다거나... 오늘은 날씨가 좋으니 어쩔수 없이 약속을 무조건 만들어 밖에 가야 한다거나... 😭</p>
</li>
</ol>
<p>대충 계획의 큰 그림은 위와 같았다. 솔직히 나 스스로 만족할 만큼 공부를 하진 못했다. 3은 거의 실패했지만, 1과 2는 최대한 지키려고 노력했고 어느 정도 성공했다. 이미 사전 스터디는 끝났고 앞으로를 생각하기로 했다.</p>
<h1 id="💻-위코드에서의-첫-2주">💻 위코드에서의 첫 2주</h1>
<h2 id="시간-관리-⏰">시간 관리 ⏰</h2>
<blockquote>
<p>2주전 월요일이 위코드 첫날이었다. 그리고 오늘은 2주가 지난 일요일이다.</p>
</blockquote>
<p>짧은 시간에 높은 효율로 공부하는 것도 중요하지만, 3개월이라는 짧은 기간 특성상 <strong>절대적인 시간</strong>도 많이 투자해야 된다고 생각했다. 첫 주에는 사전스터디 때 갈고닦은(?) 생활 습관을 발휘했다. 평일이고 주말이고 아침에 일찍 위워크에 등장해서 11시 전후로 방으로 돌아와 충분한 숙면을 취했다.</p>
<p>하지만 2주차 때 거리두기 2단계로 격상했고 때문에 위워크에서 9시 이후로 공부를 할 수 없었다. 하지만 이런 경우의 수까지 생각해두는게 진정한 계획러의 자질..! 사실 계획이라기보다 혹시나 해서 돈을 더 주더라도 책상과 의자가 있는(코딩 할 수 있는) 고시텔을 잡았었다. 2주차에는 9시 제한으로 인해 첫 주보다 일찍 자고 더 일찍 일어나 위워크에 도착했다. 주말에는 위워크를 사용하지 못하게 되어 모니터와 노트북을 챙겨들고 고시텔로 돌아왔다. 근처 고시텔이 아니었으면 나의 크고 소중한 모니터를 들고올 엄두를 못냈을 것이다.</p>
<blockquote>
<p>일찍 자고 일찍 일어나는 착한 어른이 생활을 잘 하고 있는 나 칭찬해!</p>
</blockquote>
<p>가만히 앉아서 코딩만 하는 생활이기에 체력과 건강을 위해 아침에 선정릉 둘레로 간단하게 산책하거나 가볍게 뛰기도 하였다. 가끔 귀찮아서 아침 운동을 안나갈 때도 있었지만 적어도 20분이라도 걸으면서 의식적으로 몸을 움직였다.</p>
<h2 id="사람들-👨👩👧👦">사람들 👨‍👩‍👧‍👦</h2>
<p>짧은 시간동안 많은 사람들을 만나고 빠르게 친해졌다. <del>나만 혼자 친해진 것은 아닐 거라 믿는다.</del> 훈련병끼리 같이 힘든 훈련을 하며 단기기간에 빠르게 친해지는 것처럼, 위코드 동기들도 같은 관심사를 가지고 같은  같이 힘들어 하면서 빠르게 친해졌다.</p>
<p>첫날 오티에서 은우님은 본인이 소심한 편이라도 먼저 다가가라고 했다. 사실 소극적인 사람들이라면 이 사실을 굳이 위코드가 아니라도 본인에게 필요한 것이라는 것을 알고 있을 것이다. 내향적인 나도 그래서 이번을 기회라고 생각하며 최대한 사람들에게 먼저 다가가려고 노력했고, 다른 분들도 나처럼 노력한 덕분에 꽤 좋은 분위기가 빠르게 형성되었던 것 같다.</p>
<blockquote>
<p>겉으로는 별거 아닌 것처럼 보이겠지만, 내 기준에서는 두꺼운 철판깔고 처음 보는 분들한테 이만큼 다가간 적은 처음이다.</p>
</blockquote>
<p>나는 어딜 가서 무엇을 하던지 <strong>어떤 사람과 함께하는 지</strong>가 가장 중요한 사항이 생각한다. 나는 위코드에서 모인 분들이 운이 좋게, 혹은 우연치 않게 좋은 사람들만 모인 것은 아니라고 생각한다. 물론 다들 좋은 분들이다. 그러나 좋은 사람들만 모여도 애초에 그 곳의 분위기가 삭막하다면? 개개인이 가진 좋은 모습을 드러낼 수 없을 것이다. 각 집단마다 그 특유의 무드(컬쳐 핏)가 있고, 내가 그것을 만들어 가기도 하지만 내가 내가 맞춰지는 것도 있다. 나는 한 명이지만 친한 친구들과 만날 때 나의 텐션과 회사 사람들과 만날 때의 텐션이 다른 것처럼.</p>
<blockquote>
<p>나는 위코드 멘토분들이 제시한  <strong>&#39;좋은 분위기 설계도&#39;</strong>에 우리 기수 사람들이 잘 녹아들었다고 생각한다. 그리고 지금도 동기분들과 멘토분들 모두가 그 설계를 완성해 나가며 더 좋은 문화로 만들기 위해 계속 노력하고 있다고 믿는다. 덕분에 나는 좋은 사람들을 만날 수 있었다.</p>
</blockquote>
<h2 id="난-무엇이-바뀌었나-😌">난 무엇이 바뀌었나? 😌</h2>
<p>2주동안 개발 공부만 했는데 배운게 없을리 없다. 2주 동안 내가 어떻게 바뀌었는지, 생각나는대로 간단히 나열해보았다.</p>
<ol>
<li><p>개발 블로그를 운영하기 시작했다. 좋은 것 같다! 위코드가 끝나고도 계속 작성할 것이다. <del>제발</del></p>
</li>
<li><p>생각없이 손가락이 하자는대로 작성하던🤪 css를 이젠 뇌를 쓰며 생각이란 걸 하고😗 작성하기 시작했다.</p>
</li>
<li><p>아침형 인간의 삶이 이젠 별로 힘들지 않다. <del>하지만 팀 프로젝트가 시작된다면 어떨까?</del></p>
</li>
<li><p>처음에 시간이 걸리더라도 코드를 잘 짜야 된다. 생각 없이 코드를 짜면 나중에 더 힘들다.</p>
</li>
<li><p>더 많은 것을 배우고 싶다는 생각이 들었다. 너무 즐거운 시간이었고 앞으로도 그럴 것 같았다.</p>
</li>
</ol>
<blockquote>
<p>개인적으로 개발자가 되기로 한 이유 중 하나가 <em>&#39;좋아하는 것을 일로 싶어서&#39;</em>이다. 공부는 재미없지만 개발에 대한 학습은 즐길 수 있어 다행이라고 생각한다.</p>
</blockquote>
<h1 id="👊-앞으로는">👊 앞으로는</h1>
<p>사실 처음에는 이 글에서 <em>개발자가 되기로 한 이유</em>에 대해서 써보려고 했었다. 어느정도 메모를 했었는데, 쓰다보니 그 주제는 위코드가 끝나거나 2개월차를 마치고 나서 쓰면 더 의미가 있을 것 같아 미루려고 한다. 이 글에서는 단기적으로 앞으로 내 위코드 생활에 대해서만 생각해보기로 했다.</p>
<p>짜여진 계획에서 안정감을 찾는 나로서는 어떻게 될 지 모르는 코로나와 거리두기 단계 격상이 가장 불안한 요소이다. 이건 <del>사전 스터디 때처럼</del> 내가 만들어낸 피치못할 사정이 아니라 진짜로 천재지변이기에 어쩔 수 없다는 것을 안다. 하지만 덕분에 위코드의 으쌰으쌰하며 열심히 하는 분위기에만 의존해서 공부만 하려 했던 나의 모습도 되돌아보게 되었다.</p>
<p>물론 좋은 분위기가 있으면 그 곳에서 하는 것이 맞다. 하지만 그런 곳이 없다면? 좋은 분위기와 열심히 할 분위기를 내가 의도적으로 만들어내는 것도 필요하다고 본다. 공부를 하기 위해 집이 아닌 독서실에 찾아가는 학생처럼. 그리고 주말에도 온라인 화상 미팅방을 열어 <strong>모각코</strong> 모여서 각자 코딩을 하는 우리처럼.</p>
<p>이 글을 쓴 계기로 앞으로 혹시 거리두기 단계가 더 격상된다 하더라도 어떻게 개발 공부를 위코드에서 이어나갈지 마음가짐을 정리해보기도 했다. 앞으로 남은 위코드 생활에서 나를 포함하여 동기 모두가 열심히해서 많은 것들을 얻어 갔으면 좋겠다. </p>
<p>마지막으로 훈훈하고 감동적인 사진으로 글을 마무리 짓는다. 😌
<img src="https://images.velog.io/images/open_h/post/b304f964-ad6f-4c83-8e73-d160658e21a0/image%20(3).png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[인스타그램 클론, 배운 점]]></title>
            <link>https://velog.io/@open_h/learned-from-instagram-clone</link>
            <guid>https://velog.io/@open_h/learned-from-instagram-clone</guid>
            <pubDate>Sat, 28 Nov 2020 06:51:50 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>위코드 2주차 과정에서 인스타그램 클로닝, 위스타그램을 바닐라 자바스크립트로 만들며 배웠던 것들과 기억하고 싶었던 점들을 정리했다.</p>
</blockquote>
<h1 id="javascript">JavaScript</h1>
<h2 id="정규표현식">정규표현식</h2>
<p>위스타그램의 id, pw validation을 위해 정규표현식을 처음 써 보았다. 정규표현식은 문자열에서 특정한 규칙이나 내용을 찾거나 찾은 문자열을 대체 혹은 발췌하는데 유용하게 사용할 수 있다. <del>귀찮아서 안내 문구에 영어 변수명을 그대로 가져다온 것이 티난다.</del>
<img src="https://images.velog.io/images/open_h/post/c4dace0b-444e-4baf-9a2c-6eea334a0b8d/westagram_validation.gif" alt=""></p>
<p>정규표현식 예시 코드들이 많다: <a href="https://poiemaweb.com/js-regexp">https://poiemaweb.com/js-regexp</a>
정규표현식 MDN, 역시 MDN: <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions">https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions</a></p>
<h2 id="원하지-않게-페이지가-reload-되는-현상">원하지 않게 페이지가 reload 되는 현상</h2>
<p>나의 경우, 댓글을 다는 기능에서 버튼이 <code>form</code> 태그 안에 있을 경우 button 속성의 기본 값은 &#39;submit&#39;이기 때문에 원하지 않게 페이지가 새로고침이 발생했다. 댓글을 작성하고 올라가는 순간 페이지가 reload되었다. 이를 해결하기 위해  <code>e.preventDefault()</code> 를 콜백 함수에 추가해주거나 <code>from</code> 대신 <code>div</code> 를 사용하거나 방식이 있다. 백앤드와 통신이 없는 프로젝트였기에 나는 아래와 같이 onsubmit 속성을 추가하여 reload를 방지하였다.</p>
<pre><code class="language-html">&lt;form onsubmit=&quot;return false&quot;&gt;&lt;/form&gt;</code></pre>
<h2 id="이벤트-로그를-찍어보기">이벤트 로그를 찍어보기</h2>
<p><code>addEventListener</code> 의 두번째 인자로 넣어주는 콜백함수의 이벤트 인자에 로그를 찍어 확인해보아 해결한 문제들이 많다. 구글링을 했을 때 <code>e.target</code> 등을 사용을 많이 하였고, 나는 그냥 로그를 찍어보았다. 아래 코드와 같이 <code>path[2]</code> 와 같은 코드는 유지보수가 힘들어고, 추후 html구조에 변화가 있을 때 문제가 발생할 수는 있다고 생각한다. 그러나 공부한다는 뜻에서 로그에 찍힌 이벤트 오브젝트의 값들을 추적하여 사용해 보았다. 굉장히 많은 것들이 이벤트 오브젝트에 들어있었으나 아래와 같이 내가 필요한 것을 골라 사용하였다.</p>
<pre><code class="language-javascript">// e에는 댓글 삭제 버튼이나, 좋아요 숫자 증가 버튼 등이 들어 있따.
// 이거 잘못했다가 상위 박스 클릭하면 원하지 않는 html 요소를 선택하게 될 수 있다. 고통 받았음.
const heartNumberElement = e.path[2].children[0];
// 차라리 이렇게 하자.
const heartNumberElement = e.path[2].querySelector(&#39;.comment-heart-number&#39;);</code></pre>
<h2 id="가장-기억에-남는-코드">가장 기억에 남는 코드</h2>
<p><em>문제의 코드</em></p>
<pre><code class="language-javascript">function createDummyUserList() {
  const userNum = dummyUser.length;
  const newUser = document.createElement(&#39;div&#39;);
  newUser.classList.add(&#39;user-in-nav-search-modal&#39;);
  for(let i=0; i&lt;userNum; i++){
    newUser.innerHTML = dummyUser[i];
    console.log(dummyUser[i]);
    console.log(newUser.innerHTML)
    console.log(newUser)
    navSearchUserModal.appendChild(newUser);
  }
}</code></pre>
<p><em>다시 봐도 아찔한 콘솔 창</em>
<img src="https://images.velog.io/images/open_h/post/db4799c5-b5c8-4016-a359-6a968fff259d/%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%202020-11-26%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%207.47.44.png" alt=""></p>
<p>해결된 코드</p>
<pre><code class="language-javascript">function createUserList(UserList) {
  const filteredUserNum = UserList.length; 
  for(let i=0; i&lt;filteredUserNum; i++) {
    createUserElement(UserList[i]);
  }
}

function createUserElement(userName) {
  const newUser = document.createElement(&#39;div&#39;);
  newUser.classList.add(&#39;user-in-nav-search-modal&#39;);
  newUser.innerHTML = userName;
  navSearchUserModal.appendChild(newUser);
}</code></pre>
<p>함수 분리와 변수, 함수명등의 변화가 있었지만, 중요한 점은 <code>createElement</code> 를 반복문 안에서 한다는 점이다. <code>createElement</code> 로 따로 생성한다는 점이 핵심이다. </p>
<p>다른 방법으로는 <a href="https://developer.mozilla.org/en-US/docs/Web/API/Node/cloneNode">cloneNode()</a> 를 사용하는 방법이 있다.</p>
<h2 id="mock-data">mock data</h2>
<p>혼자 프론트쪽만 구현하다 보니 가짜 데이터들이 필요했다. 가짜 데이터를 만드는 것은 쉬우나, 데이터 관리만 하는 js 파일을 따로 만들었다. 만든 mock data 변수(array of objects)들만 export하여 메인 페이지에서 동작하는 <code>main.js</code> 에서 import하였다.</p>
<h2 id="잡담">잡담</h2>
<p>마우스 올리면 해당 변수 데이터 타입 뜨는 vscode 기능이 좋기는 하지만 자바스크립트 코드 자체는 빨간줄 없이 굉장히 <strong><em>태평하다</em></strong>.... HTMLElement를 넣어야 하는 곳에 string을 막 넣어도 일단 실행시켜 준다. 이와 비슷하게 상상하지 못한 곳에서 데이터 타입 불일치로 인해  <code>null</code> 이 나오는 경우도 있었다. 추후에  팀원과 프로젝트를 하게 되면 <del><strong>제발</strong></del> <strong>꼭</strong>  타입스크립트를 사용하자고 <del>구걸해야</del> 의견을 제시해야 겠다.</p>
<h1 id="html-css">HTML, CSS</h1>
<h2 id="semantic-tags">semantic tags</h2>
<p><a href="https://www.freecodecamp.org/news/semantic-html5-elements/">https://www.freecodecamp.org/news/semantic-html5-elements/</a></p>
<p>이 사이트 보고 내 코드에 시멘틱 태그들을  많이 적용시켰다.</p>
<h2 id="원-안에-사진-넣기">원 안에 사진 넣기</h2>
<p>이미지 자체에 원이 될 만큼 radius 주고 <code>object-fit: cover</code> 를 주면 마법같이 된다...?</p>
<p>사실 정사각형 사진만 사용하느라 <code>radius: 100%</code> 를 img 태그에 적용시켰을 때 육안으로 문제점을 발견하지 못하였다. 그러나 길쭉하고 못생긴 사진을 사용해보니 사진이 찌그러진 상태로 원안에 들어가거나 타원형이 되었다(문제점을 알려주신 분...! 감사합니다). 그럴 때 사진을 기준 원에 억지로 맞추는게 아닌 사진이 잘리더라도 <code>object-fit: cover</code> 를 사용하여 사진이 찌그러지는 것을 방지할 수 있따.  </p>
<h2 id="영어만한글-말고-범위밖으로-튀어나가는-기이한-현상">영어만(한글 말고) 범위밖으로 튀어나가는 기이한 현상</h2>
<p>영어 문장이 원하는 박스 밖으로 튀어나가는 문제가 발생했다. 이게 문제가 되는 것이 한글은 이쁘게 박스안에 들어가면서 <strong>알아서 줄바꿈이 일어나는데</strong> 영어는 길어지면 시작부터 통째로 그냥 다음 줄로 넘어가거나 아예 박스 바깥으로 나간다는 것이다.</p>
<p>word-wrap: break-word</p>
<p>를 사용하여 문제를 해결하였다.</p>
<h2 id="픽셀-홀수짝수">픽셀 홀수/짝수</h2>
<p>ex) 짝수 픽셀 박스 안에 홀수 픽셀 사이즈 이미지 들어간 경우. 불편.
<img src="https://images.velog.io/images/open_h/post/10bbe199-109b-4804-9022-6d22fca5d1ec/%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%202020-11-25%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%207.05.26.png" alt=""></p>
<p>수정) 짝수 필섹 안 짝수(위 홀수보다 1작게 하여) 들어감. 편안.
<img src="https://images.velog.io/images/open_h/post/36c679b0-6986-4235-b130-3a79a1a9cd71/%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%202020-11-25%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%207.05.09.png" alt=""></p>
<h2 id="flex는-개구리가-짱이다">flex는 개구리가 짱이다</h2>
<p>레이아웃을 여러개 하다보니 이제 flex는 익숙해진 것 같다. 그래도 위스타그램을 만들 때 flex에 빠르게 익숙해질 수 있게 도와준 아주 유용한 사이트 링크를 기록한다. <del>개구리를 잡자!!!</del></p>
<p><a href="https://flexboxfroggy.com/#ko">https://flexboxfroggy.com/#ko</a></p>
<h2 id="반응형-예시">반응형 예시</h2>
<pre><code class="language-css">@media screen and (max-width: 945px) {
  .main-right {
    display: none;
  }
}</code></pre>
<h2 id="why-my-z-index-not-working">why my z-index not working!!</h2>
<p>이라고 그대로 구글링을 하여 너무 쉽게 문제를 해결했다.</p>
<p><code>z-index</code> 는 그저 숫자 크기 비교로만 작동하지 않는다. 내가 겪은 문제의 경우에도 내가 생각한 대로  <code>z-index</code> 가 동작하지 않았는데, <code>z-index</code> 가 <strong>음수</strong>일 경우, <strong>부모 요소보다 더 아래로 내려갈 수 있다</strong>는 사실을 주의 하니 문제가 해결됬다. 무조건 값이 <code>z-index</code> 10이라고 5인 요소보다 위에 위치하는 것은 아니다 않는다.</p>
<p>내가 겪은 문제 이외에도 전체적으로 <code>z-index</code> 와 관련된 규칙을 정리한 <a href="https://mytory.net/archives/10997">좋은 사이트</a>를 기록한다.</p>
<h1 id="기타">기타</h1>
<h2 id="live-server에서는-되는데-로컬-파일로는-안된다">live server에서는 되는데 로컬 파일로는 안된다?</h2>
<h3 id="cors-에러">CORS 에러</h3>
<p>어떤 모듈을 사용한 자바스크립트 파일 <code>main.js</code>을 html script로 사용하여 해당 html을 로컬에서 열 경우에 <strong>브라우저는 <code>main.js</code>에 import된 로컬 파일(모듈)에 접근하는 &#39;위험한&#39; 행위</strong>를 하게 되는 것이므로 접근이 제한된다. 따라서 &#39;from origin &#39;null&#39; has been blocked by cors policy&#39;와 같은 CORS 에러가 발생한다.</p>
<h2 id="코딩-스타일">코딩 스타일</h2>
<p>언어마다 권장되는 코딩 스타일이 다르다. 대표적인 코딩 스타일로 이 스타일을 지키면 적어도 못생긴 코드는 피할 수 있을 것 같다. 사진으로 한눈에 볼 수 있는 것이 좋다!</p>
<p><img src="https://images.velog.io/images/open_h/post/0c19e21b-0adb-4cb1-8c11-af01b33c22c1/%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%202020-11-28%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%202.56.05.png" alt=""></p>
<p>사진 출처: <a href="https://javascript.info/coding-style">https://javascript.info/coding-style</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[개발자 도구와 Web Storage]]></title>
            <link>https://velog.io/@open_h/devtools-web-storage</link>
            <guid>https://velog.io/@open_h/devtools-web-storage</guid>
            <pubDate>Mon, 23 Nov 2020 01:10:32 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>개인 학습을 위해 만들어진 글입니다.</p>
</blockquote>
<h1 id="chrome-devtools">Chrome DevTools</h1>
<p>크롬, 사파리, 파이어폭스와 같은 브라우저에서는 개발자 도구를 제공한다. 이 글에서는 크롬 브라우저에서 제공하는 개발자 도구를 좀 더 잘 사용하기 위한 내용을 정리한다. 개발자 도구를 잘 사용하면 웹 사이트를 즉각적으로 수정하고 빠르고 효율적으로 웹 페이지를 만들 수 있다.</p>
<p>개발자 도구의 경우 이론으로 어떤 기능이 있는지 공부하는 것보다 직접 크롬에서 하나씩 사용해보는 것이 더 도움이 될 것이다. 여기서는 간단하게 패널에서 어떤 기능들이 있는지만 정리한다.</p>
<p>아래 이미지는 개발자 도구(크롬 화면 &#39;&#39;우클릭-검사&#39;&#39; 혹은 단축키 <code>cmd</code>+<code>opt</code>+<code>i</code>)를 열면 나오는 패널들이다. 필요에 따라 적절한 탭을 이용할 수 있어야 한다. 이 중 Elements, Console, Network, Application 패널에 대해 다루어본다.</p>
<p><img src="https://images.velog.io/images/open_h/post/bdd7de9a-9efc-4010-8f34-4a37053e6811/devtools-panels.png" alt=""></p>
<h2 id="elements-panel">Elements Panel</h2>
<p>Elements panel에서는 DOM과 CSS를 자유롭게 조작하여 실시간으로 브라우저 상에서 사이트의 레이아웃과 디자인을 테스트 할 수 있다. 기존에 내가 가장 자주 사용하던 탭이다.</p>
<ul>
<li><p><code>cmd</code>+<code>z</code> 를 사용하여 로컬에서 변경한 사항을 되돌릴 수 있다. 모든 변경사항을 되돌리려면 Source 패널에서 파일 이름 옆의 revert를 누른다.</p>
</li>
<li><p>Style 부분은 순서는 CSS우선이 높은 것부터 낮은순으로 위에서부터 차례대로 나타나있다. 예를 들어, 부모 요소에서 정해준 색을 자식 요소에서 우선하여 색을 변경한다면 자식 요소의 우선된 색이 더 위에 나타나게 되는 것이다.</p>
</li>
</ul>
<h2 id="console-panel">Console Panel</h2>
<p>Console panel에서는 자바스크립트 코드를 즉시 실행할 수 있으며 디버깅도 가능하다.</p>
<ul>
<li>흔히 사용한은 <code>console.log</code> 의 형태를 보면 알 수 있듯이 <code>console</code> 또한 객체이며 <code>log</code> 를 포함하여 다양한 메소드가 존재한다. 예를 들면 아래 사진과 같이 단순히 <code>console.table</code> 을 사용하여 더 이쁘게 표의 형태로 콘솔 창에서 확인 가능하다. </li>
</ul>
<p><img src="https://images.velog.io/images/open_h/post/83b0591a-d3a9-4f3d-96db-510d178bba58/console-table.png" alt=""></p>
<ul>
<li>프론트엔드의 경우 실제로 디버깅 시 다른 도구보다 이 콘솔을 활용하는 경우가 많다. 백엔드에서 보내주는 <code>response</code> 도 <code>console.log</code> 로 확인할 수 있으며, status code 등도 확인할 수 있다.</li>
</ul>
<h2 id="network-panel">Network Panel</h2>
<p>http 네트워크 통신 확인, API 크롤링, 페이지 로딩 성틍 테스트, 이미지, 영상등의 소스 등을 확인할 수 있는 패널이다. 백엔드쪽과 프론트쪽이 잘 통신하고 있는지 확인 가능하다. 개발자 도구를 열기 전에 페이지를 로딩하였다면 그 기록이 없으니, 개발자 도구를 열고 나서 새로고침을 하면 아래와 같은 화면을 확인할 수 있다.</p>
<p><img src="https://images.velog.io/images/open_h/post/1b6efea4-e5f0-4707-89c3-a62a50b0358d/newtwork-panel.png" alt=""></p>
<p>웹 페이지에 사용되는 데이터들을 백엔드로부터 받아오는 것을 확인 할 수 있다. 위 사진에서 나타나는 이미지 파일들은 물론이고, 네비게이션 바의 카테고리 분류들을 벡엔드로 받아오는 형태라면 <code>.json</code> 파일, 오브젝트 형태의 데이터 덩어리도 이 패널에서  확인할 수 있다.</p>
<blockquote>
<p> 크롬 익스텐션에서 <a href="https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc?hl=ko-KR"> JSONView</a>를 설치하면 아래 사진과 같이 브라우저상에서 보기 좋게 정렬해준다. 무려 색깔까지!</p>
</blockquote>
<p><img src="https://images.velog.io/images/open_h/post/3abcd1b0-d8fb-407e-834c-7c6a07884762/jsonview-extension.png" alt=""></p>
<h2 id="application-panel">Application Panel</h2>
<p>브라우저의 자체적인 저장소이며, <code>local storage</code> , <code>session storage</code> , <code>cookie</code> 를 확인할 수 있다. 로컬 스토리지를 사용할 때 확인 해보았던 패널이다. 클라이언트 쪽에서 저장된 값들을 얼마든지 쉽게 변경 가능하기 때문에(개발자 도구 패널에서 elements 패널의 css 변경하듯이 storage를 변경 가능하며, 심지어 <code>Clear Storage</code> 라는 항목도 있다) 개발자는 이 변경을 고려하여 방어적인 코드를 작성해야 한다.</p>
<p><img src="https://images.velog.io/images/open_h/post/b3e2809b-7767-4605-8179-3bd9112d6f84/application-panel.png" alt=""></p>
<h1 id="web-storage">Web Storage</h1>
<p>HTML5부터 기존에 사용하던 쿠키의 단점을 극복하기 위해 로컬 스토리지와 세션 스토리지가 지원된다. 쿠키를 배제하는 것은 아니고 여전히 쿠키를 상황에 따라 사용 가능하다.</p>
<h2 id="local-storage">Local Storage</h2>
<ul>
<li>사용자가 지우지 않는 이상 영구적으로 브라우저에 남아 있는 데이터이다(영구 저장소). 10Mb 용량.</li>
<li><code>Key-Value</code> 의 객체 형태로 데이터를 저장한다. 모든 창에서 접근 가능하다.</li>
<li>자동 로그인 여부와 같이 지속적으로 필요한 데이터를 저장 할 수 있다. </li>
<li>검색어를 유지하여 사용자가 관심있어할 정보를 띄울 때 UI 정보들을 저장하는 곳으로 사용할 수 있다.</li>
<li>브라우저 저장소이기에 클라이언트 사이드이며 쉽게 해킹당할 수 있다. 따라서 사용자 정보(비밀번호 등)를 이곳에 저장하여서는 안된다. 서비스의 특성이나 기획에 맞게 데이터를 처리해야 한다.</li>
</ul>
<h2 id="session-storage">Session Storage</h2>
<ul>
<li>로컬 스토리지와 다르게 이 곳에 담긴 정보는 윈도우나 브라우저 탭을 닫을 경우 제거된다(임시 저장소).  5Mb 용량</li>
<li><code>Key-Value</code> 의 객체 형태로 데이터를 저장한다.</li>
<li>언어 선택과 같이 잠깐 동안 필요한 정보를 저장하는데 사용할 수 있다. 같은 탭에서만 접근 가능하다.</li>
<li>마찬가지로 클라이언트 사이드로 쉽게 해킹당할 수 있기에 주의하여야 한다.</li>
</ul>
<h2 id="cookie">Cookie</h2>
<ul>
<li>만료일이 없는 session쿠키와 만료일이 포함된 persistent 쿠키가 있으며 시간 제한 설정이 가능하다. 쿠키는 위 두 스토리지와 다르게 requests로 서버에 보내질 수 있어 브라우저와 서버에 모두 저장 가능하기에 프론트와 백엔드 통신과 관련이 있다. 4kb 용량.</li>
<li><code>Key-Value</code> 페어의 (세미콜론으로 구분된)문자열 형태로 데이터를 저장한다(문자열만 가능). 모든 창에서 접근 가능하다.</li>
<li>서비스 약관에 동의했는지 등에 관한 정보를 저장할 수 있다.</li>
</ul>
<h2 id="자바스크립트-데이터-사용-방법">자바스크립트 데이터 사용 방법</h2>
<p><strong>데이터 저장</strong></p>
<ul>
<li><code>localStorage.setItem(&quot;key&quot;, &quot;value&quot;)</code></li>
<li><code>sessionStorage.setItem(&quot;key&quot;, &quot;value&quot;)</code></li>
<li><code>setcookie(&quot;key&quot;, &quot;value&quot;, &quot;지속시간 (s)&quot;)</code></li>
</ul>
<p><strong>데이터 호출</strong></p>
<ul>
<li><code>localStorage.getItem(&quot;key&quot;)</code></li>
<li><code>sessionStorage.getItem(&quot;key&quot;)</code></li>
<li><code>document.cookie</code></li>
</ul>
<p><strong>기타</strong></p>
<p><code>removeItem</code> , <code>clear</code> 등의 공통 메소드들이 존재한다.</p>
<p>Chrome DevTools 문서:</p>
<p><a href="https://developers.google.com/web/tools/chrome-devtools">https://developers.google.com/web/tools/chrome-devtools</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[클로저와 스코프: 클로저 활용 방법]]></title>
            <link>https://velog.io/@open_h/closure-and-scope</link>
            <guid>https://velog.io/@open_h/closure-and-scope</guid>
            <pubDate>Sun, 22 Nov 2020 14:15:44 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>클로저라는 개념은 사실 여러 함수형 프로그래밍 언어에서 등장하는 개념이다. 즉, 자바스크립트의 고유 개념이 아니며 ECMAScript 명세서에서도 클로저를 따로 정의하고 있지 않다. 다양한 책에서 클로저에 대해 한 문장으로 정의할 때 서로 다른 단어를 쓰고 있으며, 추상적인 개념이 늘 그러하듯 짧게 줄인 정의만으로는 이해하기가 쉽지 않다.</p>
<p>자바스크립트에서는 클로저를 이용하여 스코프의 특징과 함수에 대한 명세를 구현하고 있다. 자바스크립트에서의 스코프를 먼저 다루고 클로저에 대해 정리하고자 한다. 그 후에 클로저를 실용적으로 활용하는 코드를 살펴보자.</p>
</blockquote>
<h1 id="스코프scope">스코프Scope</h1>
<p>프로그래밍 언어에서는 &quot;이름:값&quot;의 형태로 변수를 생성하는데 이름(식별자)은 중복이 일어날 가능성이 충분하며 충돌이 일어날 경우 문제가 발생한다. 각 언어마다 스코프라는 규칙을 정하여 이러한 문제를 해결하는데, <strong>스코프란</strong> 식별자identifier를 참조할 때, (유효 범위를 고려하여)어떤 대상을 참조할 지 찾아내기 위한 규칙이다. 프로그래밍 언어에서 전역 변수, 지역 변수에 대해 배울 때 자연스럽게 느끼는 개념이다.</p>
<p>아래 간단한 코드에서 스코프의 의의를 알 수 있다.</p>
<pre><code class="language-javascript">let a = &#39;a in global&#39;;
function foo() {
  let a = &#39;a in foo&#39;;
  console.log(a);
}
foo(); // &#39;a in foo&#39;
console.log(a); // &#39;a in global&#39;</code></pre>
<p>위 코드에서 만약 <code>foo()</code> 안에 변수 <code>a</code> 를 선언하지 않았다면, 함수는 범위를 넓혀 전역 변수 <code>a</code> 에 접근하게 된다(따라서 출력 결과는 모두 &#39;a in global&#39;가 된다). 복잡한 코드의 경우라도 범위를 계속 넓혀가며 결국 전역 스코프까지 찾는 것을 <strong>스코프 체인</strong>이라고 부르며 자바스크립트의 스코프적 특징 중 하나이다.</p>
<h3 id="함수-레벨-스코프와-블록-레벨-스코프">함수 레벨 스코프와 블록 레벨 스코프</h3>
<p>자바스크립트(ES6)에서는 <strong>함수 레벨</strong>과 <strong>블록 레벨</strong>(ES6)의 <strong>렉시컬 스코프</strong>lexical scope 규칙을 따른다. </p>
<p>함수 레벨과 블록 레벨에 대해서는 <a href="https://velog.io/@open_h/var-let-const"><code>var</code> , <code>let</code> , <code>const</code> 의 차이점</a>에서 다루었다. <code>var</code> 로 선언된 변수는 함수 레벨 스코프를 가졌기에, <code>let</code> 과 <code>const</code> 를 지원하는 ES6가 되어서야 자바스크립트에서 블록 레벨 스코프를 지원하게 되었다. ES6를 사용하는 요즘은 아래와 같이 <strong>혼란을 야기하는 함수 레벨 스코프</strong>를 가지는 <code>var</code> 대신 블록 레벨 스코프를 사용하는 <code>let</code> 과 <code>const</code> 로 모두 대체하고(모두 대체가 가능하다) 있다. 추가적으로 함수 선언식으로 만들어진 함수도 함수 레벨 스코프를 갖는다.</p>
<pre><code class="language-javascript">function f(){
    if(true) {
        var a = 10; // function level scope
    }
    console.log(a); // 10
}

function g(){
    if(true){
        let a = 10; // block level scope
    }
  console.log(a) // ReferenceError: a is not defined
}</code></pre>
<h3 id="렉시컬-스코프lexical-scope">렉시컬 스코프Lexical Scope</h3>
<p>렉시컬 스코프는 보통 동적 스코프dynamic scope와 비교하며 자바스크립트는 렉시컬 스코프 규칙을 사용한다. 현대 프로그래밍에서 언어들은 대부분 렉시컬 스코프 규칙을 따르고 있다. 렉시컬 스코프에 대해 설명하기 전, 먼저 예시 코드를 살펴보자.</p>
<pre><code class="language-javascript">let a = &#39;a in global&#39;;
function bar(){
  console.log(a)
}
function foo() {
  let a = &#39;a in foo&#39;;
  bar();
}
bar(); // a in global
foo(); // a in global</code></pre>
<p>만약 <code>foo()</code> 에서 <code>a</code> 를 선언하는 것이 아니라 그저 재할당을 하여 전역 변수를 바꾸었다면 위 코드와 콘솔 결과는 달라졌을 것이다. 하지만 위와 같은 코드라면 두 함수 실행의 결과가 &#39;a in global&#39; 로 나타나는 것으로 예상이 되며 실제로도 그렇다. 사실 이러한 스코핑 규칙을 사실 <strong>렉시컬 스코프</strong>이라고 부른다. </p>
<p>스코프는 함수를 호출할 때가 아니라 <strong>선언</strong>할 때 생기는 것이다. 렉시컬 스코프는 함수를 처음 <strong>선언하는 순간</strong> 함수 내부의 변수는 자기 <strong>스코프로부터 가장 가까운</strong> 곳(스코프 체인)에 있는 변수를 계속 참조하게 된다. 즉, 위 예제 코드의 경우  <code>bar</code> 이 선언되었을 때 이미 <code>a</code> 는 함수 내에 <code>a</code> 라는 식별자가 없으니 스코프 체인으로 글로벌 변수 <code>a</code> 를 참조하게 된다.</p>
<h3 id="동적-스코프dynamic-scope">동적 스코프Dynamic Scope</h3>
<p>렉시컬 스코프처럼 동작하는 것이 당연한 것이 아닐까? 라고 생각할 수도 있기 때문에, 이해를 위해 동적 스코프의 원리도 간단히 알아보자. 위 렉시컬 스코프 자바스크립트 예시 코드가 렉시컬 스코프가 아니라 <em>동적 스코프dynamic scope 규칙을 따른다고 가정</em>하면 아래와 같은 결과가 나온다.</p>
<pre><code class="language-javascript">bar(); // a in foo
foo(); // a in global</code></pre>
<p>렉시컬 스코프와 달리 동적 스코프는 호출 스택call stack에 영향을 받으며 위 코드대로라면 가장 아래에는<code>foo</code> 함수를 호출할 때,  <code>Global</code> 스택이 쌓여있는 상태에서, 그 위에 <code>foo</code> 가 쌓이고 그 다음에는 <code>bar</code> 함수가 스택으로 쌓인다. 동적 스코프는 이 스텍의 영향을 받기 때문에 <code>bar</code> 에서는 <code>a</code> 에 접근할 때, 그 아래 스텍인 <code>foo</code> 의 <code>a</code> 에 접근하게 되어 <code>foo()</code> 는 &#39;a in global&#39;이 아닌 &#39;a in function&#39;을 출력하게 된다. </p>
<p>참고로 렉시컬 스코프와 마찬가지로 <code>bar()</code> 를 호출하였을 때는 바로 &#39;a in global&#39;이 출력되는데 이는 <code>Global</code> 스택 위 <code>bar</code> 스텍이 전부이기에 <code>Global</code> 다음 스택의 전역 변수 <code>a</code> 에 접근하기 때문이다. 실제로 동적 스코프 방식은 Perl과 같은 언어에서 나타나는 결과이다.</p>
<p><img src="https://images.velog.io/images/open_h/post/7eccdd8c-1baa-4778-aa47-5e991b5bcd57/dynamic-scope-call-stack.png" alt=""></p>
<h3 id="자바스크립트와-렉시컬-스코프">자바스크립트와 렉시컬 스코프</h3>
<p>물론 자바스크립트는 렉시컬 스코프 규칙을 따르기 때문에 <strong>호출 스택과 상관 없이</strong> (<code>this</code> 를 제외하고) &quot;이름:값&quot;으로 된 &#39;대응표&#39;를 소스코드를 정의하기 때문에 위 코드는 모두 &#39;a in global&#39;로 나타나는 것이다. 자바스크립트의 스코프는 ECMAScript 언어 명세서에서 <strong>렉시컬 환경</strong>lexical environment과 <strong>환경 레코드</strong>environment record라는 개념으로 정의된다. 여기서 &quot;이름:값&quot;이라는 대응표는 환경 레코드라고 볼 수 있으며, 렉시컬 환경은 이 환경 레코드와 <strong>상위 렉시컬 환경</strong>outer lexical environment에 대한 참조로 이루어진다.</p>
<p>현재 환경 레코드에서 변수를 찾아보고 없다면 바깥 렉시컬 환경을 참조하는식의 스코프 체인 방식이 가능해지며 이름을 찾는데 성공하거나 바깥 렉시컬 환경 참조가 <code>null</code> 이 되었을 때(실패했을 때) 탐색을 멈춘다.</p>
<p><img src="https://images.velog.io/images/open_h/post/a1936e9d-b0e2-482e-8a7f-04b1a3f3d9d9/lexical-scope-environment.png" alt=""></p>
<p>사진 출처: <a href="https://meetup.toast.com/posts/86">https://meetup.toast.com/posts/86</a></p>
<p>이와 관련하여 깊게 이해하기 위해서는 실행 컨텍스트execution context에 대해 알아야 한다. 여기서는 클로저 설명을 위해 짧게 렉시컬 스코프에 대해 정리하였다.</p>
<h1 id="클로저closure">클로저Closure</h1>
<p><a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Closures">MDN</a>에서는 클로저를 </p>
<p><strong>&#39;함수와 그 함수가 선언되었을 때의 렉시컬 환경lexical environment과의 조합이다&#39;</strong></p>
<p>라고 정의하고 있다. 정의상 개념적으로는 모든 함수를 클로저로 칭하는 것 같다. 하지만 실제로 클로저라는 단어를 사용할 때 자바스크립트에서는 <strong>모든 함수를 전부 클로저라 부르지 않는다</strong>. 위 클로저 정의에서 자바스크립트의 경우 함수는 반환된 내부함수를 의미하고 렉시컬 환경은 내부 함수가 선언되었을 때의 스코프를 뜻한다. 먼저 예제 코드를 살펴 보자.</p>
<pre><code class="language-javascript">// 1번 예제 코드
function foo(){
  let a = 10;
  function bar(){
    console.log(a);
  }
  bar();
}
foo();</code></pre>
<p><code>bar</code> 는 일단 <code>foo</code> 의 안에 있어서 <code>foo</code> 를 상위 환경 참조로 저장할 것이며, 렉시컬 스코프 체인을 통해 한 단계 범위를 넓히면 <code>foo</code> 의 <code>a</code> 를 참조하게 된다. 마치 클로저처럼 <em>보이지만</em> <strong>클로저라고 부르지 않는다</strong>. 다음 예제 코드를 보자.</p>
<pre><code class="language-javascript">// 2번 예제 코드
let a = &#39;apple&#39;;
function foo() {
    let a = &#39;grape&#39;;
    function bar() {
        console.log(a); // 순서 1
    }
    return bar;
}
let yoo = foo(); // 순서 3
yoo();</code></pre>
<p>2번 예제 코드의 실행순서를 <code>bar</code> 의 스코프 관점에서 차례대로 나열해보자.</p>
<ol>
<li><code>bar</code> 는 <code>a</code> 를 출력하는 함수로 정의되었다.</li>
<li><code>bar</code> 는 상위 환경 참조로 <code>foo</code> 의 환경을 저장하였다.</li>
<li><code>bar</code> 를 전역 변수 <code>yoo</code> 로(함수 선언식으로) 가져왔다.</li>
<li>전역에서 <code>yoo</code> 를 호출했다.</li>
<li>이제 <code>bar</code> 는 자신의 스코프에서 <code>a</code> 를 찾지만 없다.</li>
<li>따라서 외부 환경 참조를 찾아간다.</li>
<li>외부 환경인 <code>foo</code> 의 스코프에서 <code>a</code> 를 찾을 수 있으며 그 값은 &#39;grape&#39; 이다.</li>
<li>따라서 &#39;grape&#39;가 출력된다.</li>
</ol>
<p>2번 예제 코드는(1번 예제코드와 달리) <strong>클로저를 나타내고 있다</strong>. 두 예제 코드는 비슷하지만 클로저를 나타내는데 있어 중요한 차이점이 있다. </p>
<ol>
<li><code>bar</code> 는 <strong>자신이 생성된 렉시컬 스코프에서 벗어나</strong> 전역에서 <code>yoo</code> 라는 식별자로 호출되었다.</li>
<li>스코프 탐색은 현재 실행 스택(<code>yoo</code> 호출)과 <strong>관련 없는</strong>(동적 스코프와의 차이점이다) <code>foo</code> 를 거쳤다.</li>
</ol>
<p>자바스크립트의 스코프는 렉시컬 스코프라고 앞서 언급했다. 다시 말하면, 소스코드가 작성된 그 문맥에서 스코프가 결정이 되는 것이다. <strong>이미 <code>bar</code> 의 상위 렉시컬 환경을 결정</strong>한 이후에는 <code>bar</code> 과 상관 없는 곳에서 호출한다 하더라도 <code>foo</code> 에서 <code>a</code> 를 탐색하게 된다. 이 때 <code>bar</code> 또는 <code>yoo</code> 와 같은 함수를 클로저라고 부른다.</p>
<h1 id="클로저의-실용적인-활용">클로저의 실용적인 활용</h1>
<p><em>그래서 클로저를 왜 알아야 하는데?</em> 에 대한 답변을 하려면 클로저가 어떻게 <em>잘 활용되는지</em> 알아야 할 것이다. 클로저는 자신이 생성될 때의 환경을 기억해야 하므로 MDN에선은 클로저가 필요하지 않는 작업에서 함수 내에서 함수를 불필요하게 작성하는 것은 처리 속도와 메모리 측면에서 현명하지 않다고 말한다. 여기서는 클로저가 실용적으로 잘 사용될 수 있는 예시들을 정리했다.</p>
<h3 id="전역-변수의-사용-억제">전역 변수의 사용 억제</h3>
<p><strong>전역 변수를 사용하지 않고</strong>도 현재 상태(혹은 값)를 기억하고 변경된 최신 상태를 유지할 수 있다. 어떤 상태를 관리할 때, 클로저가 없다면 (불가피하게)전역 변수를 사용하거나 아예 로컬스토리지를 사용할 수도 있다. 하지만 클로저를 활용하면 전역 변수가 아니라 타이트한 변수 스코핑을 통해 현명하게 상태나 값을 관리할 수 있다.</p>
<p>박스의 색을 토글하는 함수를 전역변수를 사용하지 않고 클로저와 <a href="https://developer.mozilla.org/ko/docs/Glossary/IIFE">즉시실행함수(IIFE)</a>를 활용하여 구현하였다. 아래 코드에서 즉시실행함수가 반환한 (이름없는) 함수는 렉시컬 환경에 속한 변수를 기억하는 클로저가 된다.</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;body&gt;
  &lt;div class=&quot;box&quot; style=&quot;width: 100px; height: 100px; background: green;&quot;&gt;&lt;/div&gt;

  &lt;script&gt;
    // Box Color Toggler
    const box = document.querySelector(&#39;.box&#39;);

    const toggleColor = (function () {
      let isGreen = true;
      // 클로저 반환
      return function () {
        box.style.background = isGreen ? &#39;red&#39; : &#39;green&#39;;
        // 상태 변경
        isGreen = !isGreen;
      };
    })();
    // 박스 클릭 이벤트
    box.addEventListener(&#39;click&#39;, toggleColor);
  &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>예시 코드 처럼 클로저를 사용할 경우 전역 변수를 사용하지 않게 된다. 단순히 <code>true</code> , <code>false</code> 토글 이외에도 클로저가 포함된 함수의 변수를 private한 변수 인 것처럼 쓸 수 있을 것이다. 이렇게 불필요한 전역 변수를 사용하지 않음으로서 얻는 이득은 상당하다. <strong>의도되지 않은 변경</strong>을 개발자가 걱정할 필요가 없기 때문에 코드가 안정적이게 된다. 필요한 곳에서만 해당 변수에 접근할 수 있게 하고 그 외부에서는 접근하지 못하게 막는 코드는 예상치 못한 부작용을 막을 수 있는 좋은 코드라고 할 수 있다.</p>
<h3 id="프라이빗-메소드private-method-흉내내기">프라이빗 메소드private method 흉내내기</h3>
<p><em>이 항목의 제목과 내용은 MDN의 예시를 인용하였다.</em> <a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Closures">MDN link</a></p>
<p>다른 몇몇 언어들은 프라이빗으로 선언할 수 있는 기능을 제공하여 같은 클래스 <strong>내부의 다른 메소드에서만</strong> 그 메소드들을 호출할 수 있게 만들어준다. 프라이빗 메소드는 name space를 관리할 수 있게 하고 코드에 대한 접근을 제한적으로 하여 타이트한 스코프를 설계할 수 있게 해준다는 이점이 있다. 자바스크립트에 클래스와 매소드는 있지만, 자체적인 프라이빗 메소드는 따로 없다. 그러나 클로저를 이용하여 <strong>마치 프라이빗 메소드인 것처럼</strong> 구현할 수 있다.</p>
<ol>
<li>위에 있었던 toggle 예시와 같은 방식으로는 하나의 함수밖에 클로저로 리턴할 수 없어 구현에 제한이 많다. 하지만 아래 예시 코드와 같이 구현할 경우 다양한 함수를 리턴하여 많은 것을 구현 할 수 있다. 코드는 프라이빗 함수와 변수에 접근하는 퍼블릭 함수를 클로저를 이용해 구현한 코드이며, 이렇게 클로저를 사용하는 것을 모듈 패턴module pattern이라고 한다.</li>
</ol>
<pre><code class="language-javascript">let counter = (function() {
  let privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  };   
})();

console.log(counter.value()); // logs 0
counter.increment();
counter.increment();
console.log(counter.value()); // logs 2
counter.decrement();
console.log(counter.value()); // logs 1</code></pre>
<p> <code>counter</code> 라는 객체에 private method <code>increment</code> , <code>decrement</code> , <code>value</code> 를 사용하는 것처럼 구현되었다. <code>counter</code> 내부의 <code>privateCounter</code> 라는 변수에는 개발자가 정해준 (가짜)프라이빗 메소드로만 접근할 수 있는 것이다. </p>
<ol start="2">
<li>아래와 같은 <a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Obsolete_Pages/Core_JavaScript_1.5_Guide/Creating_New_Objects/Using_a_Constructor_Function">생성자 함수</a>로도 위 MDN의 예시 코드와 같은 목적과 기능을 가진 코드를 구현할 수 있다.</li>
</ol>
<pre><code class="language-javascript">function Counter() {
  let counter = 0;
  this.increase = function () {
    return ++counter;
  };
  this.decrease = function () {
    return --counter;
  };
}
const counter = new Counter();
console.log(counter.increase()) // 1
console.log(counter.increase()) // 2
console.log(counter.increase()) // 3
console.log(counter.decrease()) // 2</code></pre>
<hr>
<p>참고 자료:</p>
<p><a href="https://meetup.toast.com/posts/86">https://meetup.toast.com/posts/86</a>
<a href="https://www.zerocho.com/category/JavaScript/post/5740531574288ebc5f2ba97e">https://www.zerocho.com/category/JavaScript/post/5740531574288ebc5f2ba97e</a>
<a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Closures">https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Closures</a>
<a href="https://poiemaweb.com/js-closure">https://poiemaweb.com/js-closure</a></p>
<hr>
<p><em>혹시 잘못된 내용이 있거나 부족한 부분이 있다면 말씀 부탁드립니다. 지적은 진심으로 감사히 받겠습니다!</em></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Hoisting and TDZ]]></title>
            <link>https://velog.io/@open_h/Hoisting-and-TDZ</link>
            <guid>https://velog.io/@open_h/Hoisting-and-TDZ</guid>
            <pubDate>Sat, 21 Nov 2020 08:24:11 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p> <a href="https://velog.io/@open_h/var-let-const">이전 포스팅</a>에서 <code>var</code> 키워드의 경우 <code>let</code> 이나 <code>const</code> 와 달리 변수를 선언하기 이전에도 참조가 가능해지는 문제가 발생한다고 하였다. 이 문제를 이해하기 위해서 호이스팅이라는 개념을 알고 있어야 한다.  <code>var</code> 의 경우 선언 이전에 참조가 가능하기에 호이스팅이 일어나지만 <code>let</code> 과 <code>const</code> 는 호이스팅이 일어나지 않는다고 착각 할 수도 있다. 하지만 <code>let</code> , <code>const</code> 을 포함하여 모든 선언(초기화나 할당과 구분해야 한다. 선언만이다.)은 호이스팅이 일어난다. 심지어 호이스팅이라는 용어는 <a href="https://developer.mozilla.org/ko/docs/Glossary/Hoisting">ES6 이전 표준 명세에서 <strong>사용된 적이 없는</strong> 용어</a>이다. </p>
</blockquote>
<h1 id="hoisting호이스팅">Hoisting호이스팅</h1>
<p>호이스팅은 영어 단어의 뜻 그대로 <strong>끌어올려진다</strong>는 의미를 가진다. 이 용어는 자바스크립트 엔진의 동작 과정 중의 일부분을 <strong>쉽게 이해하기 위한 가상의 개념</strong>이다. 이걸 다르게 말하면 호이스팅에 대해 제대로 이해하기 위해서는 <em>실행 컨텍스트, 자바스크립트 엔진, 컴파일 과정, 스코프, 자바스크립트의 메모리 관리 등</em>을 모두 이해해야 한다는 것이다.
<img src="https://images.velog.io/images/open_h/post/003b6961-17f6-49ec-a9f5-3831a70d052e/%E1%84%8B%E1%85%B5%E1%84%86%E1%85%A1%E1%84%90%E1%85%A1%E1%86%A8.jpg" alt=""></p>
<p><em>공부할 것은 넘쳐 나는데 내 머리는 슬슬 한계가 보이기 시작했다</em>.</p>
<p>그래서 일단 여기서는 엔진의 실제 동작 방식이 아니라 쉽게 이해할 수 있는 가상의 개념만을 간단히 정리하고자 한다. <strong>호이스팅</strong>이란 실제 자바스크립트 엔진 동작 일부 방식을 <strong>&#39;자바스크립트 엔진은 식별자들을 (유효 범위의)최상단으로 끌어올리고 난 후에 다음 코드를 실행한다&#39;</strong> 라고 생각하는 것이며, 이렇게 이해하더라도 코드 해석에는 문제가 없다. </p>
<p>자바스크립트 엔진이 실제로 식별자들을 끌어올리는 것은 아니지만 식별자들을(실제 메모리 상으로는 변화가 없지만, 메모리에 저장하는 방식을 고려했을 때) <strong>편의상 끌어올린 것으로 간주</strong>하는 것이다. 자바스크립트가 실제로 실행되기 이전에 유효 스코프 안에서 변수값들을 모아서 마치 유효 스코프 최상단에 선언한 것 처럼(실제로 코드가 그렇게 바뀌는 것은 아니다) 해석할 수 있다는 것이다.</p>
<blockquote>
<p>여기서 식별자(identifier)의 개념을 한번 더 짚고 넘어가자. 식별자는 코드의 변수, 함수 등을 식별하는 단어로 개발자가 만들어주는 것으로 <code>myArrayVar</code> , <code>myAddFunction</code> 과 같은 변수명, 함수명, 속성명, 메소드명이 그것이다.</p>
</blockquote>
<h3 id="예제-코드-1--끌어올린다는-의미">예제 코드 1 : &#39;&#39;끌어올린다&#39;&#39;는 의미</h3>
<pre><code class="language-javascript">/* 원본 코드 */
console.log(a); // undefined
var a = 10;</code></pre>
<pre><code class="language-javascript">/* 
실제로 코드가 이렇게 변하는 것은 아니지만 컴파일 과정에서
이렇게 호이스팅된 것으로 간주하여 코드를 해석할 수 있다.
*/
var a;
console.log(a);
a = 10;</code></pre>
<p><code>var</code> 로 선언된 변수 <code>a</code> 는 위 코드처럼 식별자들이 호이스팅되어 함수 선언 이전에 함수에 접근하여도 코드가 작동한다. 단 초기화 혹은 할당과는 별개로 선언만 끌어올린다는 것을 주의하자. </p>
<p>이런 코드는 다른 언어를 공부하고 난 후 javascript를 접한 사람들은 이해할 수 없는 동작일 수 있다. javascript를 첫 언어로 공부하더라도 코드가 위에서부터 차례대로 실행되지 않는 듯하여 충분히 헷갈릴 수 있다. 이런 방식은 코드 작성 방법이 자유롭다는 장점이 있지만 한편으로는  <code>var</code> 로 선언된 변수는 변수명 중복 등 개발자 입장에서 error prone해지며 이는 <a href="https://velog.io/@open_h/%EC%99%9C-let%EA%B3%BC-const%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%95%BC-%ED%95%A0%EA%B9%8C"><code>var</code> 키워드를 사용하지 말아야 할 이유</a> 중 하나라고 볼 수 있다.</p>
<h3 id="예제-코드-2--함수-호이스팅과-유효-범위의-최상단">예제 코드 2 : 함수 호이스팅과 유효 범위의 최상단</h3>
<pre><code class="language-javascript">/* 
    원본 코드
    에러가 있어서 실행되진 않지만 에러가 발생하는 줄만 없애주면
    출력되는 값은 주석으로 남겼다.
*/
console.log(a); // undefined
f1(); // undefined
console.log(f2) // undefined
f2(); // TypeError: f2 is not a function
function f1(){
  console.log(b);
  var b = 5;
}
var f2 = function () {
  console.log(c);
  var c = 7;
}
var a = 10;</code></pre>
<pre><code class="language-javascript">/* 아래 코드로 간주하여 코드 해석 가능 */
var a;
function f1(){
  var b;
  console.log(b);
  b = 5;
}
var f2;

console.log(a);
f1();
console.log(f2);
f2();
f2 = function f2() {
  var c;
  console.log(c);
  c = 7;
}
a = 10;</code></pre>
<p><strong>유효 범위의 최상단</strong>이 뜻하는 바를 위 두 코드를 비교하면 서 확인 할 수 있다. 함수 scope 내에서 가장 최상단으로 <code>var b</code> , <code>var c</code> 라는 선언이 이루어 진 것을 볼 수 있다. 전역에서는 변수 <code>a</code> 가 최상단으로 끌어올려졌다.</p>
<p>함수를 선언하는 방식에서 <code>f1</code> 과 같이 function declarations(함수 선언식)으로 함수를 선언할 경우와 다르게 <code>f2</code> 와 같이 function expressions(함수 표현식)으로 함수를 선언한 경우 <strong><code>f2</code> 라는 식별자가 호이스팅될 뿐</strong>, 함수 자체는 끌어올려지지 않는다. 실제로 <code>f2</code> 를 출력했을 때는 <code>undefined</code> 를 확인할 수 있으며, 함수로서 <code>f2()</code> 와 같이 호출했을 때는 <code>TypeError: f2 is not a function</code> 라는 에러가 발생한다.</p>
<p> <code>let</code> 과 <code>const</code> 도 마찬가지로 <code>var</code> 처럼 변수 선언을 위한 키워드이기에 호이스팅이 일어날 것이다. 그런데 <code>const</code> 와 <code>let</code>  키워드로 선언된 변수는 위의 예시 코드의 <code>var</code> 로 선언된 변수처럼 <strong>선언 전에 참조를 할 수 없다.</strong> 최상단으로 변수의 선언을 끌어올렸는데 그 아래에서 참조를 할 수 없다? 그렇다면 <code>let</code> 과 <code>const</code> 는 호이스팅이 일어나지 않는 것일까? 결론부터 말하자면 그렇지 않다.</p>
<h1 id="temporal-dead-zonetdz">Temporal Dead Zone(TDZ)</h1>
<p>전의 질문에 짧고 쉽게 답을 하자면 <code>let</code> 과 <code>const</code> 으로 선언한 변수는 <code>var</code> 처럼 호이스팅이 일어나지만, <code>var</code> 과 다르게 초기화가 되기전까지 TDZ라는 곳에 머물러 초기화(혹은 할당)이 될 때까지 잠시 &#39;죽어있는 상태&#39;이기 때문에 선언 전에 참조가 불가능한 것이다. TDZ를 직역해도 &#39;임시로 죽어 있는 공간&#39;이다. TDZ는 <strong>선언 전에 변수를 사용하는 것을 비허용</strong>하는 개념상의 공간이다. </p>
<p><code>var</code> 과 달리 선언 전에 변수를 사용하지 못하는 것인데, 호이스팅이 일어난 것을 직접 확인할 수 있을까? 다시 말해 TDZ의 변수와 선언되지 않은 변수를 구별할 수 있을까? 먼저 <code>typeof</code> 연산자로 쉽게 확인 할 수 있는 방법이 있다.</p>
<pre><code class="language-javascript">typeof notDefined; // &#39;undefined&#39;</code></pre>
<pre><code class="language-javascript">typeof variable; // ReferenceError : Cannot access &#39;a&#39; before initialization
let variable;</code></pre>
<p>즉, 아예 선언이 되지 않은 변수는 <code>&#39;undefined&#39;</code> type이지만, TDZ에 있는 식별자에는 접근하는 것은 그 자체로 에러를 발생시키고 있다.</p>
<p>다른 방법으로 아래 예시 코드를 통해 <code>let</code> 이 호이스팅이 된다는 것을 확인해보자.</p>
<pre><code class="language-javascript">const a = 3; // 전역 변수
{
  console.log(a); // 당연하게도 전역 변수 3이 출력된다.
}</code></pre>
<pre><code class="language-javascript">const a = 3; // 전역 변수
{
  console.log(a); // ReferenceError: Cannot access &#39;a&#39; before initialization
  const a = 5;
}</code></pre>
<blockquote>
<p>JavaScript에서 <code>{ }</code> 를 자유롭게 사용하여 level of scope를 설정할 수 있다. 극단적으로 <code>{{{ let a = 10; }}}</code> 도 문법상 문제가 없으며 의미없이 <code>{}</code> 비어 있는 스코프를 생성해도 문법상 문제는 없다. 함수가 하나의 스코프가 되는 것처럼 위 코드에서는 보기 쉽게 중괄호를 사용해 스코프를 분리해주었다.</p>
</blockquote>
<p>만약 <code>let</code> 이 호이스팅이 되지 않은 것이라면 두 번째 예시와 같은 에러는 발생하지 않았을 것이다. 중괄호 스코프 내에서 호이스팅이 일어났고 <code>a</code> 가 스코프 내에서 할당이 일어나기 전에 <code>console.log</code> 에서 참조를 하려 했고, 아직 TDZ에 있는 변수 <code>a</code> 를 참조할 수 없기에 <code>ReferenceError</code> 가 발생하는 것이다.</p>
<p>참고로 <code>const</code> , <code>let</code> 말고도 <code>class</code> 구문과 클래스의 <code>constructor()</code> 내부의 <code>super()</code>, 기본 함수 매개변수도 TDZ 제한이 있다. 반면 <code>var</code> , <code>function</code> 구문은 TDZ에 영향을 받지 않으며 현재 스코프에서 호이스팅이 된다. 이것은 이미 위의 <a href="https://velog.io/@open_h/Hoisting-and-TDZ#%EC%98%88%EC%A0%9C-%EC%BD%94%EB%93%9C-2--%ED%95%A8%EC%88%98-%ED%98%B8%EC%9D%B4%EC%8A%A4%ED%8C%85%EA%B3%BC-%EC%9C%A0%ED%9A%A8-%EB%B2%94%EC%9C%84%EC%9D%98-%EC%B5%9C%EC%83%81%EB%8B%A8">예시 코드</a>에서 <code>undefined</code> 를 얻은 <code>var</code> 로 선언된 변수와 호이스팅된 함수 예시 코드로부터 알 수 있었다.</p>
<h1 id="정리">정리</h1>
<p>호이스팅이란 자바스크립트 엔진 동작을 이해하기 쉽게 하기 위해 만들어진 개념으로, 변수 및 함수 선언이 유효 스코프 최상단으로 끌어올려진 후에 코드를 실행하는 것으로 생각하는 것이다.</p>
<p> <code>var</code> , <code>let</code> , <code>const</code> 키워드로 선언된 변수는 선언 부분만 끌어올려진다고 생각할 수 있다. 따라서 <code>var</code> 의 경우에는 변수 선언전에도 참조할 수 있지만 할당을 하지 않았기에 <code>undefined</code> 이다. 그러나 <code>let</code> 과 <code>const</code> 의 경우 식별자가 호이스팅 후 실제 코드에서 <strong>선언되기 전까지</strong> TDZ에 있기 때문에  <code>let</code> , <code>const</code> 선언 코드가 있는 곳 이전에는 해당 변수에 참조할 수 없게 된다.</p>
<p>참고 자료:
<a href="https://ponyfoo.com/articles/es6-let-const-and-temporal-dead-zone-in-depth">https://ponyfoo.com/articles/es6-let-const-and-temporal-dead-zone-in-depth</a>
<a href="https://yuddomack.tistory.com/category/Javascript">https://yuddomack.tistory.com/category/Javascript</a>
<a href="https://dmitripavlutin.com/javascript-variables-and-temporal-dead-zone/">https://dmitripavlutin.com/javascript-variables-and-temporal-dead-zone/</a></p>
<hr>
<p><em>혹시 잘못된 내용이 있거나 부족한 부분이 있다면 말씀 부탁드립니다. 지적은 진심으로 감사히 받겠습니다!</em></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[파이썬 기본 문법과 함수 argument]]></title>
            <link>https://velog.io/@open_h/python-basic-function-argument</link>
            <guid>https://velog.io/@open_h/python-basic-function-argument</guid>
            <pubDate>Thu, 19 Nov 2020 04:49:15 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><em>개인 학습을 위해 만들어진 글입니다.</em></p>
</blockquote>
<blockquote>
<p>python 기본 문법을 정리하였다.</p>
<p>최대한 예제 코드 위주로 정리하였으며, 내가 알고 있던 언어들과 달랐던 점들, 정리하고 넘어가고 싶은 부분만 정리했다.</p>
</blockquote>
<h1 id="python-basic-1">Python Basic 1</h1>
<p>​    내가 알고 있던 언어들과 달랐던 점들을 위주로 정리했다.</p>
<p><strong>Data Type</strong></p>
<p>​    파이썬의 데이터 타입은 Integer정수, Float소숫점숫자, Complex Numbers복소수, String문자열, Boolean이 있다. </p>
<p><strong>Math Expression</strong></p>
<pre><code class="language-python">7/2 == 3.5  # True
7//2 == 3  # False
6/2 == 3 # True, 6/2을 출력하면 3.0이지만 3과 비교하여 True
num+=1 # num = num + 1, 마찬가지로 *=, /=, -=도 지원
3*3 === 27 # True</code></pre>
<p><strong>String Formatting</strong></p>
<pre><code class="language-python"># string concatenation
print(&quot;Hello, &quot; + &quot;World&quot;) # Hello, World
# Python3 f-strings formatting example
year = 2020
# Welcome 2020!, GoodBye 2019...
print(f&quot;&quot;&quot;Welcome {year}!, GoodBye {year-1}...&quot;&quot;&quot;)</code></pre>
<p><strong>Significant Whitespace</strong></p>
<p>​    여기서 whitespace는 indention<strong>들여쓰기</strong>를 뜻한다. 다른 언어들과 달리 들여쓰기는 단순히 코드의 가독성을 위해 존재하는 것이 아니라 <strong>코드의 종속성</strong>을 나타내기에 중요한 사항이다.</p>
<h1 id="python-basic-2">Python Basic 2</h1>
<p><strong>조건문</strong></p>
<pre><code class="language-python"># if, elif, else로 조건문 작성
# and, or, not 사용하여 조건 중첩

if year == 2020 and wantPrint:
    print(&quot;correct current year!&quot;)
elif year == 2021 and wantPrint:
  print(&quot;next year!&quot;)
elif not wantPrint:
  print(&quot;why you don&#39;t want print...?&quot;)
else:
  print(&quot;I don&#39;t know&quot;)</code></pre>
<p><strong>주석</strong></p>
<pre><code class="language-python"># 이 줄은 주석입니다.
print(&quot;Hi there&quot;) # inline comment
&#39;&#39;&#39;
Multiline comment
hahahahaha
python3
&#39;&#39;&#39;</code></pre>
<h1 id="python-function-parameter">Python Function Parameter</h1>
<p><strong>default value parameter</strong></p>
<p>​    파이썬은 함수의 parameter에 default 값을 정의해 주는 문법이 따로 있다. 이 때, default값이 정이된 parmameter의 경우 함수를 호출할 때 해당값이 넘겨주지 않아도 상관없으며 default값이 자동으로 넘어가게 된다. 그러나 여러 parameter를 받는 함수의 경우, default값이 정의되지 않은 <strong>모든</strong> parameter가 순서상 default값이 정의된 parameter보다 <strong>앞쪽에</strong> 위치해야 한다.</p>
<pre><code class="language-python"># 잘못된 parameter 순서
def wrong_function(name=&quot;unknown&quot;, gender, age):
def wrong_function2(gender, name=&quot;unknown&quot;, age)  

# 올바른 parameter 순서, syntax error
def correct_function(gender, age, name=&quot;unknown&quot;):</code></pre>
<p>​    왜 이런 함수 선언이 syntax error인지는 함수 호출할 때를 살펴보면 알 수 있다. 함수 호출시 함수는 인자를 앞에서부터 차례대로 순서대로 넘겨주어야 하기 때문이다. 만약 name을 default 값으로 하고 싶고, 성별과 나이만 입력하고 싶을 경우 <code>wrong_function(&quot;male&quot;, 30)</code> 와 같이 할 경우, name parameter에 &quot;male&quot;이 넘어가게 된다. </p>
<p>​    따라서 함수 파라미터 중 default값을 정해줄 수 없고 <strong>반드시 값을 받아야 하는 파라미터를 앞쪽</strong>에, <strong>default값을 정한 파라미터 경우 뒤에 위치</strong>해야 한다.</p>
<p><strong>variable length arguments</strong></p>
<p>​    variable length arguments는 단어 뜻 그대로 &#39;가변 매개변수&#39;로 매개변수의 수를 다르게 해서 사용할 수 있는 매개변수이다. <code>*arg</code> 와 같은 형태로 파라미터를 작성한다. 위치가 뒤에 있는지, 앞에 있는지 <strong>순서</strong>가 중요하며 <strong>함수 호출 방법</strong>도 신경써야 한다. 함수 호출 시 argument의 위치 positional argument를 신경써야 한다.</p>
<ol>
<li>일반 매개변수 앞에, 가변 매개변수(* string)가 뒤에 온 경우</li>
</ol>
<pre><code class="language-python">def my_function(num, *strings):
  # code

# 올바른 사용
my_function(8, &#39;hi&#39;, &#39;hello&#39;, &#39;안녕&#39;)

# SyntaxError, keyword argument를 사용해서 에러 발생.
my_function(num=1, &#39;a&#39;, &#39;b&#39;, &#39;c&#39;)</code></pre>
<ol start="2">
<li>가변 매개변수가 앞에, 일반 매개변수가 뒤에 온 경우</li>
</ol>
<pre><code class="language-python">def my_function(*srings, num):
  # code

# 올바른 사용
my_function(&#39;abc&#39;, &#39;def&#39;, num=999)

# SyntaxError, keyweord argument를 사용해야 함
my_function(&#39;가&#39;, &#39;나&#39;, 10)</code></pre>
<p><strong>variable length keyword arguments</strong></p>
<p>​    variable length argument와 마찬가지로 함수 호출시 여러 매개변수를 넘길 수 있는데 <code>**kwargs</code> 와 같은 형태로 파라미터를 작성한다. 함수 호출시 반드시 <strong>keyword argument</strong> 의 형태로  값을 넘겨주어야 한다.</p>
<h3 id="function-parameter-problems">Function Parameter Problems</h3>
<hr>
<p><strong>문제 1.</strong></p>
<p>주석으로 문제 해결을 설명하였다.</p>
<pre><code class="language-python">def func_param_with_var_args(name, *args, age):
    print(&quot;name=&quot;,end=&quot;&quot;), print(name)
    print(&quot;args=&quot;,end=&quot;&quot;), print(args)
    print(&quot;age=&quot;,end=&quot;&quot;), print(age)

# TypeError: func_param_with_var_args() missing 1 required keyword-only argument: &#39;age&#39;
# 함수와 같이 parameter 순서를 구성하였다면 age는 반드시 keyword argument를 사용해야 함.
func_param_with_var_args(&quot;정우성&quot;, &quot;01012341234&quot;, &quot;seoul&quot;, 20)

# 아래와 같이 함수 호출 해야함
func_param_with_var_args(&quot;정우성&quot;, &quot;01012341234&quot;, &quot;seoul&quot;, age=20)</code></pre>
<p><strong>문제 2</strong>.</p>
<p>주석으로 문제 해결을 설명하였다.</p>
<pre><code class="language-python">def func_param_with_kwargs(name, age, kwargs, address=0):
    print(&quot;name=&quot;,end=&quot;&quot;), print(name)
    print(&quot;age=&quot;,end=&quot;&quot;), print(age)
    print(&quot;kwargs=&quot;,end=&quot;&quot;), print(kwargs)
    print(&quot;address=&quot;,end=&quot;&quot;), print(address)

# TypeError: func_param_with_kwargs() got an unexpected keyword argument &#39;mobile&#39;
# mobile이라는 keyword argument는 함수에 존재하지 않는다.
func_param_with_kwargs(&quot;정우성&quot;, &quot;20&quot;, mobile=&quot;01012341234&quot;, address=&quot;seoul&quot;)

# 아래와 같이 함수에 있는 parameter 이름을 사용한다.
func_param_with_kwargs(&quot;정우성&quot;, &quot;20&quot;, kwargs=&quot;01012341234&quot;, address=&quot;seoul&quot;)</code></pre>
<p><strong>문제 3.</strong></p>
<pre><code class="language-python">def mixed_params(name=&quot;아이유&quot;, *args, age, **kwargs, address):
    print(&quot;name=&quot;,end=&quot;&quot;), print(name)
    print(&quot;args=&quot;,end=&quot;&quot;), print(args)
    print(&quot;age=&quot;,end=&quot;&quot;), print(age)
    print(&quot;kwargs=&quot;,end=&quot;&quot;), print(kwargs)
    print(&quot;address=&quot;,end=&quot;&quot;), print(address)

mixed_params(20, &quot;정우성&quot;, &quot;01012341234&quot;, &quot;male&quot; ,mobile=&quot;01012341234&quot;, address=&quot;seoul&quot;)</code></pre>
<p>​    위 함수 <code>mixed_params</code> 에서 parameter 순서를 먼저 맞추어줄 필요가 있다. 위 코드대로 실행을 할 경우 <code>**kwargs</code> 는 항상 마지막 parameter가 되어야 하기 때문에 <code>SyntaxError: invalid syntax</code> 에러 메세지가 뜨게 된다. </p>
<p>​    아래 이미지는 파이썬에서 argument의 종류와 순서에 대한 그림이다.</p>
<p><img src="https://images.velog.io/images/open_h/post/45f4c807-dc0f-457f-bd73-7062706200df/python-function-definition-arguments-kind-and-order.jpg" alt=""></p>
<p>이미지 출처: <a href="https://getkt.com/blog/python-keyword-only-arguments/">https://getkt.com/blog/python-keyword-only-arguments/</a>
​    그림과 같이 함수 파라미터의 순서를 올바르게 수정하고 바르게 함수를 호출한 코드와 결과는 아래와 같다.</p>
<pre><code class="language-python">def mixed_params(age, address, name=&quot;아이유&quot;, *args, **kwargs):
    print(&quot;name=&quot;,end=&quot;&quot;), print(name)
    print(&quot;args=&quot;,end=&quot;&quot;), print(args)
    print(&quot;age=&quot;,end=&quot;&quot;), print(age)
    print(&quot;kwargs=&quot;,end=&quot;&quot;), print(kwargs)
    print(&quot;address=&quot;,end=&quot;&quot;), print(address)

mixed_params(20, &quot;seoul&quot;, &quot;정우성&quot;, &quot;01012341234&quot;, &quot;male&quot;, mobile=&quot;01012341234&quot;)</code></pre>
<pre><code>------결과 콘솔창------
name=정우성
args=(&#39;01012341234&#39;, &#39;male&#39;)
age=20
kwargs={&#39;mobile&#39;: &#39;01012341234&#39;}
address=seoul</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[javascript 복습]]></title>
            <link>https://velog.io/@open_h/javascript-review</link>
            <guid>https://velog.io/@open_h/javascript-review</guid>
            <pubDate>Wed, 18 Nov 2020 06:38:37 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><em>개인 학습을 위해 만들어진 글입니다.</em></p>
</blockquote>
<p>TIL 201118, 작성 18일</p>
<blockquote>
<p>자바스크립트 문법을 복습하면서 한번 더 짚고 넘어가고 싶은 내용들을 간단히 정리했다.</p>
</blockquote>
<h1 id="class-object">Class, Object</h1>
<h3 id="oop객체지향-프로그래밍">OOP(객체지향 프로그래밍)</h3>
<p>​    OOP(Object-Oriented Programming)는 프로그램을 설계하는 방법론(혹은 패러다임)으로 프로그램을 Objects객체들의 모임이라는 관점으로 프로그램을 구성하는 것이다. 단어를 직역하면(객체에서 기원한 프로그래밍) 그 의미를 알 수 있다. 유연하고 유지 보수성이 높다는 점 등 때문에 많은 프로그래밍 언어들이 객체지향 프로그래밍을 지원하며 <strong>자바스크립트도 객체지향의 특징을 보이는 언어 중 하나</strong>이다. </p>
<p>​    자바스크립트에서 Object와 Class는 객체지향적인 특징이라고 할 수 있다.</p>
<h3 id="객체object">객체Object</h3>
<pre><code class="language-javascript">let myObj = {
  name: &quot;javascript&quot;
  year: 2020
  hello: function() { console.log(&quot;hello!&quot;)}
}
const myVar = &quot;name&quot;
myObj.hello() // hello!
const secondObj = { a: 10, }
const secondOBj2 = { a: 10, }

secondObj === secondObj2 // false
secondObj = { a: 20 } // TypeError: Assignment to constant variable</code></pre>
<p>​    object에 접근할 때 key에 접근할 때 <code>myObj.name</code> 과 같이 <code>.</code> 연산자를 사용하여 편하게 접근할 수 있는데, <code>myObj[&quot;name&quot;]</code> 와 같은 접근할 수도 있다. 이런 문법이 있는 이유는 key위치에  <code>myObj[myVar]</code> 와 같이 변수가 들어갈 수 있기 때문이다.</p>
<p>​    <code>secondObj</code> 와 같이 <code>const</code> 로 선언된 변수 자체에 객체라 하더라도, property를 추가하거나 변경하는 것은 가능하다. 그러나 <code>secondObj</code> 에 객체를 재할당하는 <code>let</code> 으로 선언된 객체와 달리 <strong>불가능</strong>하다.</p>
<p><strong>Object 순회</strong></p>
<ul>
<li><p><code>Object.keys(myObj)</code> : key를 배열로 반환</p>
</li>
<li><p><code>Object.values(myObj)</code> : 값을 배열로 반환</p>
</li>
<li><p><code>Object.entries(myObj)</code> : [key, 값]을 배열로 반환</p>
<p>예: [ [&quot;a&quot;, 10], [&quot;b&quot;, 20], [&#39;name&#39;, &quot;hyeon&quot;] ] </p>
</li>
<li><p>for-in문</p>
</li>
</ul>
<pre><code class="language-javascript">//ES6
for (let i in arr) {
  console.log(arr[i]);
}
for (let key in obj) {
  const value = obj[key];
  console.log(`${key}: ${value}`);
}</code></pre>
<h3 id="클래스class">클래스Class</h3>
<p>MDN class 링크: <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes">https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes</a></p>
<p>​    Class의 유용성이라 하면 결국 어떤 객체의 틀을 만들어 비슷한 모양의 객체를 쉽게 만들어낼 수 있다는 점에 있다. 매번 객체를 번거롭게 만들어내는 것이 아니라 blueprint처럼 하나의 틀을 만들어 놓고 객체를 만들어내는 것이다(따라서 class의 문법은 object의 문법과 비슷하다). 이 때 class로부터 객체를 만들어내는 것을 instantiate인스턴스화라고 부르며 instance인스턴스는 class를 통해 생성된 객체이다. class는 대문자로 시작하고 CamelCase로 작성한다.</p>
<pre><code class="language-javascript">class Pizza {
    constructor(name, price) {
        this.name = name;
        this.price = price;
    }

  pirceDiscount(discount) {
    return this.price*discount;
  }

  setPrice(newPrice) {
    return this.price = newPrice;
  }
}

const combinationPizza = new Pizza(&quot;Combination Pizza&quot;, 10000);
combinationPizza.setPrice(12000);
combinationPizza.priceDiscount(0.5);</code></pre>
<p><strong>Constructor생성자</strong></p>
<p>​    객체와 클래스의 가장 큰 차이는 constructor라는 생성자 함수라고 할 수 있다. class는 새로운 instance를 생성할 때마다 <code>constructor()</code> 매서드를 호출되며 클래스 내에서 변경 가능한 상태값이면서 클래스내에서 사용할 수 있는 변수를 &#39;맴버 변수&#39;라고 부른다. 맴버변수는 <code>this</code> 키워드를 통해 접근할 수 있다.</p>
<p><strong>method매서드</strong></p>
<p>​    함수 중에서 객체의 프로퍼티 값으로 된 함수를 메서드라고 부른다.</p>
<h1 id="기타">기타</h1>
<p><strong>1. string과 number</strong></p>
<p>​    javascript에서는 자동으로 형 변환이 일어나는 경우가 있다. stringVar + numberVar 의 형태라면 number가 string형이 자동으로 변환된다. error prone하기 때문에 javascript의 단점이라고 볼 수 있다. <del>typescript를 쓰자</del></p>
<ul>
<li><p>string to number 형 변환: <code>Number(&quot;3.141592&quot;)</code> 와 같이 가능하다. <code>parseInt()</code>, <code>parseFloat()</code>도 있다. 숫자로 변환할 수 없다면 NaN이 반환된다.</p>
</li>
<li><p>number to string 형 변환: <code>myNumberVar.toString()</code> 와 같이 가능하다.</p>
</li>
</ul>
<p><strong>2. performance of push, unshift</strong></p>
<p>​    <code>arrName.push(elem)</code>, <code>arrName.unshift()</code> 는 모두 하나의 요소를 배열에 추가하는 함수이다. 하지만 메모리 할당의 측면에서는 매우 다르다.</p>
<ul>
<li>push: 배열 선언과 같이 할당된 배열 데이터에 해당하는 메모리가 가득 차기 전까지는 스택의 형태로  O(1)의 시간복잡도를 가진다. 배열이 커져서 메모리 재할당이 필요한 경우(아주 가끔 일어난다)에만 배열 copy가 일어난다</li>
<li>unshift: 배열 맨 앞에 요소를 추가해서 O(1)의 시간복잡도를 가질 것 같지만, 배열 맨 앞에 요소를 끼워 넣기 위해서는 항상 배열 전체 copy가 일어나면서 메모리 재할당이 일어나기 때문에 unshift가 push보다 느리다.</li>
</ul>
<p>출처: <a href="https://stackoverflow.com/questions/44031591/performance-of-array-push-vs-array-unshift">https://stackoverflow.com/questions/44031591/performance-of-array-push-vs-array-unshift</a></p>
<p><strong>3. slice, split</strong></p>
<p>​    <code>slice(시작 위치, 끝 위치)</code> 는 잘린 부분이 <em>없어지는</em> 것이 아니라 그 부분이 잘려 <em>나온다.</em> <del>왜 내 뇌는 맨날 반대로 기억하는가.</del></p>
<p>​    <code>split()</code> 은 string을 잘라서 배열로 return 한다.</p>
<ul>
<li>예시: <code>arrName = stringName.split(&#39;,&#39;)</code></li>
</ul>
<p><strong>4. Date()</strong></p>
<pre><code class="language-javascript">let rightNow = new Date();
let year = rightNow.getFullYear();
let month = rightNow.getMonth()+1;
let date = rightNow.getDate();
let day = rightNow.getDay();
let currentHour = rightNow.getHours();
let currentMin = rightNow.getMinutes();

//ms로 반환됨
let time = rightNow.getTime();</code></pre>
<p><strong>5. Math object methods</strong></p>
<p><a href="https://www.w3schools.com/js/js_math.asp">https://www.w3schools.com/js/js_math.asp</a></p>
<p><strong>6. Scoping</strong></p>
<p>​    global 변수를 지양하고 최대한 <code>{ ... }</code> 내에서 변수를 새로 만들어야 한다. 즉, block scope를 최대한 나누고 tight한 scoping을 하는 것이 코드의 가독성, 유지보수, 메모리 측면 등 모든 면에서 유리하다.</p>
<p><strong>7. DOM 몇 가지  함수</strong></p>
<pre><code class="language-javascript">function myFunction(){
  let myParent = document.getElementsByTagName(&#39;h1&#39;)[0];
  let myElement = document.createElement(&quot;p&quot;);
  myElement.classList.add(&quot;dom&quot;)
  myElement.innerHTML = &quot;DOM&quot;
  myParent.appendChild(myElement);
}</code></pre>
<p>첫 번째 <code>&lt;h1&gt;</code> 태그의 하위 element로 <code>&lt;p&gt;</code> 태그를 생성하여 넣는 함수이다.</p>
<p><strong>8. arrow function</strong></p>
<p>​    화살표 함수 문법 예시</p>
<pre><code class="language-javascript">// 같은 함수
function getName(name) { return name; }
const getName = name =&gt; { return name; }
const getName = name =&gt; name;
const getName = (name) =&gt; name;

// 같은 함수
function() {}
() =&gt; {}

// 같은 함수, argument 2개 이상은 괄호 생략 불가
const hi = function(a, b) { return a+b; }
const hi = (a, b) =&gt; a+b;</code></pre>
<p><strong>9. array methods</strong></p>
<p>​    <code>Array.map()</code> , <code>Array.forEach()</code> 예제 코드</p>
<pre><code class="language-javascript">const arr = [1, 2, 3, 4, 5];
let oddIdxSum = 0;

// map()은 callback함수를 인자로 받아 배열 반환
const double = arr.map(x =&gt; x*2)
const triple = arr.map(function(x){
  return x*3
})

// forEach()는 callback함수를 인자로 반환 없이 반복문처럼
arr.forEach((elem, idx) =&gt; {
  if(idx%2){
    oddIdxSum += elem;
  }
})</code></pre>
<p><strong>10. 변수명</strong></p>
<p>​    변수명은 <strong>잘</strong> 지어야 한다. 하지만 어렵다. 변수명, 함수명만으로 그 의미를 파악할 수 있는 것이 좋다.</p>
<p>참고자료: <a href="https://geo-python.github.io/site/notebooks/L1/gcp-1-variable-naming.html">https://geo-python.github.io/site/notebooks/L1/gcp-1-variable-naming.html</a></p>
<p>​    <strong><em>변수명 짓다 파산</em></strong></p>
<p><img src="https://miro.medium.com/max/1268/1*t07Qg1oeVKneuX6UDp-SvQ.jpeg" alt="A short summary of Java coding best practices | by Rafiullah Hamedy | Medium"></p>
]]></description>
        </item>
    </channel>
</rss>