<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>요행을 바라지 않는 개발자</title>
        <link>https://velog.io/</link>
        <description>내 이름 고은정, 은을 180deg 돌려 고긍정 🤭</description>
        <lastBuildDate>Sun, 14 Nov 2021 18:38:20 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>요행을 바라지 않는 개발자</title>
            <url>https://images.velog.io/images/edie_ko/profile/65773e6f-750d-48e4-a486-90fceb846e1d/0a6a6b9330337d29fadcffe3a7352c7d.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. 요행을 바라지 않는 개발자. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/edie_ko" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[자료구조 | 해시 테이블 hash table]]></title>
            <link>https://velog.io/@edie_ko/hashtable-with-js</link>
            <guid>https://velog.io/@edie_ko/hashtable-with-js</guid>
            <pubDate>Sun, 14 Nov 2021 18:38:20 GMT</pubDate>
            <description><![CDATA[<p>유저의 비밀번호, 전자서명, 전자투표와 같은 민감한 입력의 무결성을 검증하거나, 혹은 문서 복제 등을 체크하거나, 블록체인에도 사용되는 해시..!
오늘은 해시 테이블 개념을 정리해본다.
마지막에는 자바스크립트의 Object와 Map을 해시 테이블과 관련해 이야기 해보겠다.</p>
<h1 id="해시-테이블">해시 테이블</h1>
<p><strong>Hash table(hash map)이란 해시함수를 사용해서 변환한 값을 index로 삼아 key와 value를 저장하는 자료구조</strong>를 말한다.
다시 말해 해시 테이블은 어떤 특정 값을 받아서 해시 함수에 입력하고, 함수의 출력값을 인덱스로 삼아 데이터를 저장한다.
파이썬의 dictionary, 루비의 Hash, 자바의 Map이 해시 테이블의 대표적인 예다.
해시 테이블의 특징을 나열해보자.</p>
<ul>
<li>기본 연산으로는 search, insert, delete가 있다.</li>
<li>순차적으로 데이터를 저장하지 않는다.</li>
<li>key를 통해서 value를 얻을 수 있다. =&gt; <strong>이진탐색트리나 배열에 비해서 속도가 획기적으로 빠름</strong></li>
<li>커다란 데이터를 해시해서 짧은 길이로 축약할 수 있기 때문에 데이터를 비교할 때 효율적이다.</li>
<li>value는 중복 가능하지만 key는 유니크해야 한다.</li>
<li>수정 가능하다. (=&gt; mutable)</li>
<li>보통 배열로 미리 hash table 사이즈만큼 생성 후에 사용한다.</li>
</ul>
<p>해시 테이블은 key-value가 1:1 매핑되어 있기 때문에 <strong>검색, 삽입, 삭제 과정에서 모두 평균적으로 O(1)의 시간복잡도</strong>를 갖는다.
공간 복잡도는 O(N)인데 여기서 N은 키의 개수다.</p>
<p>| Hash table time complexity in Big O notation |
| :---                                         | :---: | ---: |
| Algorithm                                    | Average | Worst case |
| space                                        | O(n) | O(n) |
| search                                       | O(1) | O(n) |
| insert                                       | O(1) | O(n) |
| delete                                       | O(1) | O(n) |</p>
<p>해시란 단방향 암호화 기법으로 해시 함수를 이용하여 고정된 길이의 암호화된 문자열로 바꿔버리는 것을 의미한다.
이때 매핑 전 원래 데이터의 값을 key, 매핑 후 데이터의 값을 hash value, 매핑하는 과정을 hashing이라고 한다.
아래의 사진을 보자.</p>
<p><img src="https://images.velog.io/images/edie_ko/post/733c9c0f-afc9-4557-acf5-972bd1895993/hashtable.jpeg" alt=""></p>
<p>해시 함수를 이용해서 key를 hash value로 매핑하고, 이 hash value를 index로 삼아 데이터의 value를 buckets(혹은 slots)에 저장했다.
그렇다면 해시 함수는 어떻게 작성할 수 있을까?</p>
<h1 id="해시-함수">해시 함수?</h1>
<p>해시 함수의 간단한 예시를 들어보자. 나눗셈법이다. <br />
어떠한 정수를 10으로 나눈 나머지를 return하는 함수가 있다고 하자. 이 또한 간단한 해시 함수다.
무조건 0~9 사이의 값이 리턴되기 때문이다.</p>
<p>즉 <strong>해시 함수는 임의의 길이를 갖는 메시지를 입력받아서 고정된 길이의 해시값을 출력하는 함수</strong>다. 어떤 입력 값에도 항상 고정된 길이(해시 함수에 따라 비슷한 길이까지 포함)의 해시값을 출력한다.
해시 함수를 통해 입력값은 완전히 새로운 모습의 데이터로 만들어지기 때문에 암호화 영역에서 아주 주요하게 사용되고 있다.
_SHA(Secure Hash Algorithm)_알고리즘이 그 대표적인 예시다.
(궁금하다면 다음의 링크로 들어가 여러 문자열을 넣어서 확인해보자. <a href="https://www.convertstring.com/ko/Hash/SHA256">클릭! - 온라인 SHA256 해시 생성기</a>) <br />
또한 입력 값의 일부가 변경되면 전혀 다른 값을 출력하는데 이것을 가리켜 &#39;눈사태 효과&#39;라고 한다.
이 눈사태 효과로 결과값으로는 입력값을 유추할 수가 없다. 역추적이 안된다는 말은 <strong>&#39;단방향&#39;</strong>으로만 되어 있다는 의미를 내포한다.</p>
<p>하지만 이 해시 함수에도 단점이 존재하는데, 해시 함수는 입력값의 길이가 어떻든 고정된 길이의 값을 출력하기 때문에
입력값이 다르더라도 같은 결과값이 나오는 경우가 있다.</p>
<p>이것을 <strong>&#39;해시 충돌 hash collision&#39;</strong>이라고 표현하며, 충돌이 적은 해시 함수가 좋은 해시함수다.
해시 충돌을 더 자세히 살펴보자.</p>
<h1 id="해시-충돌">해시 충돌</h1>
<p>다음 그림(<a href="https://medium.com/shell-tharsis/hash-collision-5891d7dde54f">출처</a>)을 보자.
ARGOS와 MINIBEEF가 같은 해시 값을 가지며 충돌이 발생했다.</p>
<p> <img
    src='https://miro.medium.com/max/1400/1*i5JV9RiF17ftnGDvuqVFSA.png'
    title='hash_collision'
    alt='hash_collision'
  /></p>
<p>해시 충돌을 설명하기 위해서는 적재율(load factor)이라는 개념이 필요하다.
적재율이란 해시 테이블의 크기에 대한 키의 개수의 비율을 뜻한다. 즉 키의 개수를 <code>K</code>, 해시 테이블의 크기를 <code>N</code>이라고 했을 때 적재율은 <code>K/N</code>이다.
만약 충돌이 발생하지 않을 경우 해시 테이블의 탐색, 삽입, 삭제 연산은 모두 O(1)에 실행되지만, 충돌이 발생할 경우에는
탐색과 삭제 연산이 <code>O(K)</code>만큼 걸리게 된다.</p>
<p><strong>해시 충돌이 1도 없는 해시 함수를 만드는 것은 불가능하다.</strong> 따라서 해시 충돌에 대해 안전하다는 해시 함수는 &#39;충돌을 찾는 게 거~의 희박하다&#39;라는 뜻이다.
이 해시 충돌이 발생하는 근본적인 원인은 <a href="https://ko.wikipedia.org/wiki/%EB%B9%84%EB%91%98%EA%B8%B0%EC%A7%91_%EC%9B%90%EB%A6%AC">비둘기집 원리</a>다.
즉 해시 함수가 무한한 가짓수의 입력값을 받아 유한한 가짓수의 출력값을 생성하는 경우에는 비둘기집 원리에 의해 해시 충돌은 항상 존재한다.
<strong>띠라서 해시 테이블의 충돌을 완화하는 방향으로 문제를 보완해야 한다.</strong></p>
<h1 id="해시-충돌-완화">해시 충돌 완화</h1>
<p>해시 충돌을 완화하는 데는 다양한 방법이 있다. _개방 주소법(open addressing)으로 해시 테이블 크기는 고정하면서 저장할 위치를 잘 찾거나,
분리 연결법 (seperate chaining)으로 해시 테이블의 크기를 유연하게 만드는 방법_이 대표적이다.
아래 이미지<a href="https://st-lab.tistory.com/240?category=856997">(출처)</a>를 보면서 어떤 개념인지 살펴보자.</p>
<p>  <img
    src='https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCoiPf%2Fbtq2qyoJVrN%2FERiH4UbKnKHQyF4R0HGjOk%2Fimg.png'
    title='solutionForCollision'
    alt='solutionForCollision'
  /></p>
<h3 id="1-개방-주소법-open-address">1. 개방 주소법 open address</h3>
<p>open addressing은 한 버킷 당 들어갈 수 있는 엔트리는 하나이지만 해시 함수로 얻은 주소가 아닌, <strong>다른 주소에 데이터를 저장할 수 있도록</strong> 허용하는 방법이다.
하지만 이 방법은 부하율(load factor)이 높을 수록(= 테이블에 저장된 데이터의 밀도가 높을수록) 성능이 급격히 저하된다.
개방 주소법의 주요 목적은 저장할 엔트리를 위한 다음의 slot을 찾는 것인데 이 방법으로 2가지가 널리 쓰인다. - <a href="https://medium.com/@matthewharrilal/open-addressing-resolving-collisions-one-day-at-a-time-49415ca73f71">이미지 출처</a></p>
<h4 id="1-선형-탐사법-linear-probing">(1) 선형 탐사법 Linear Probing</h4>
<p>말 그대로 가장 간단하게 선형으로 순차적 검색을 하는 방법이다. 해시 함수로 나온 해시 index에 이미 다른 값이 저장되어 있다면, <strong>해당 해시값에서 고정 폭을 옮겨 다음 해시값에 해당하는 버킷에 액세스</strong>한다.
여기에 또 다른 데이터가 있다면 또 다시 고정폭으로 이동해 액세스한다.</p>
<p>  <img
    src='https://miro.medium.com/max/614/1*xN0omiiMDelgCQmmg7zv9Q.png'
    title='linear_probing'
    alt='linear_probing'
  /></p>
<p>이 경우에는 특정 해시 값의 주변이 모두 채워져 있는 일차 군집화(primary clustering) 문제에 취약하다.
예를 들어 모든 키가 100이라는 해시값으로 매핑이 될 경우 충돌을 100%가 된다.
따라서 해시 충돌이 해시 값 전체에 균등하게 발생할 때 유용한 방법이다.</p>
<h4 id="2-제곱-탐사법-quadratic-probing">(2) 제곱 탐사법 Quadratic Probing</h4>
<p>선형 탐사법과 동일한데,** 고정폭이 아니라 제곱으로 늘어난다**. 따라서 2^1, 2^2, 2^3로 이동해서 데이터를 할당한다.</p>
<p>  <img
    src='https://miro.medium.com/max/1400/1*K-C6o_R4caYMrCNLkyWazw.jpeg'
    title='quadratic_probing'
    alt='quadratic_probing'
  /></p>
<p>따라서 제곱 탐사법을 이용한 경우 데이터의 밀집도가 선형 탐사법보다 낮기 때문에 다른 해시값까지 영향을 받아서 연쇄적으로 충돌이 발생할 가능성이 적다.
하지만 선형 탐사법보다는 캐시의 성능이 떨어져서 속도의 문제가 발생한다.</p>
<h4 id="3-이중-해싱-double-hashing">(3) 이중 해싱 Double Hashing</h4>
<p>이 외에도 <strong>이중 해싱(double hashing)</strong> 방법이 있다. 이 방법은 탐사할 해시값의 규칙값을 없애서 클러스터링을 방지한다.
즉 해시 함수를 이중으로 사용하는데, 하나는 최초의 해시값을 얻을 때, 다른 하나는 해시 충돌이 일어났을 때 탐사 이동폭을 얻기 위해 사용한다.
이렇게 되면 최초 해시값이 같더라도 탐사 이동폭이 달라지고, 탐사 이동폭이 같더라도 최초 해시값이 달라져 위의 두 방법을 모두 완화할 수 있다.</p>
<h3 id="2-분리-연결법-seperate-chaining">2. 분리 연결법 seperate chaining</h3>
<p>분리 연결법은 개방 주소법과는 달리 한 버킷(슬롯) 당 들어갈 수 있는 엔트리의 수에 제한을 두지 않는다.
이때 버킷에는** 링크드 리스트(linked list)나 트리(tree)**를 사용한다.</p>
<p>  <img
    src='https://d3i71xaburhd42.cloudfront.net/25bbffa9f868a6ad7295241c6af500be64c0db85/4-Figure2-1.png'
    title='seperate_chaining'
    alt='seperate_chaining'
  /></p>
<p>해시 충돌이 일어나더라도 linked list로 노드가 연결되기 때문에 index가 변하지 않고 데이터 개수의 제약이 없다는 장점이 있다.
하지만 메모리 문제를 야기할 수 있으며, 테이블의 부하율에 따라 선형적으로 성능이 저하된다. 따라서 부하율이 작을 경우에는 open addressing 방식이 빠르다.</p>
<blockquote>
<p>위의 2가지 방법 이외에도 해시 테이블의 적재율이 높아진 경우에는 크기가 더 큰 새로운 테이블을 만들어서 기존 데이터를 옮겨서 사용하는 방법이 있다. 혹은 분리 연결법을 사용했을 경우엔 재해싱을 통해서 너무 길어진 리스트의 길이를 나누어서 다시 저장할 수도 있다. 그리고 마지막으로 해시 함수 자체를 올바르게 쓰는 방법이다. 특정 값에 치우치지 않고 해시값을 고르게 만들어내는 좋은 해시 함수를 사용하는 것이다.</p>
</blockquote>
<h1 id="자바스크립트의-object는-해시-테이블일까">자바스크립트의 object는 해시 테이블일까?</h1>
<p>자바스크립트에도 이 해시테이블과 비슷한 것을 찾을 수 있다.
바로 object다. key-value 값으로 이뤄져 있고 key는 유니크해서 key를 index 삼아 value를 찾을 수 있고, 데이터의 삭제가 가능한 그것..
실제로 많은 글을 읽다보면 해시 테이블을 자바스크립트의 객체와 같다고 설명한다.
(eg. dictionaries in Python, Hashes in Ruby, Maps in Java, Objects in JavaScript)
하지만 정확히 자바스크립트 객체는 해시 테이블이 아니다.
V8 엔진을 포함한 대부분의 자바스크립트 엔진은 해시 테이블과 유사하지만 높은 성능을 위해 일반적인 해시 테이블보다 나은 방법으로 객체를 구현한다. (<a href="https://v8.dev/blog/hash-code">V8 블로그 참고</a>)</p>
<blockquote>
<p>자바, C++ 같은 클래스 기반 객체지향 프로그래밍 언어는 사전에 정의된 클래스를 기반으로 객체(인스턴스)를 생성한다. 다시 말해, 객체를 생성하기 이전에 이미 프로퍼티와 메서드가 정해져 있으며 그대로 객체를 생성한다. 객체가 생성된 이후에는 프로퍼티를 삭제하거나 추가할 수 없다. 하지만 자바스크립트는 클래스 없이 객체를 생성할 수 있으며 객체가 생성된 이후라도 동적으로 프로퍼티와 메서드를 추가할 수 있다. <strong>이는 사용하기 매우 편리하지만 성능 면에서는 이론적으로 클래스 기반 객체지향 프로그래밍 언어의 객체보다 생성과 프로퍼티  접근에 비용이 더 많이 드는 비효율적인 방식이다.</strong> <strong>따라서 V8 자바스크립트 엔진에서는 프로퍼티에 접근하기 위해 동적 탐색(dynamic lookup) 대신 히든 클래스(hidden class)라는 방식을 사용해 C++ 객체의 프로퍼티에 접근하는 정도의 성능을 보장</strong>한다. 히든 클래스는 자바와 같이 고정된 객체 레이아웃(클래스)과 유사하게 동작한다.
  <br />- 이웅모, 자바스크립트 Deep Dive</p>
</blockquote>
<p>따라서 자바스크립트의 Object는 key-value의 집합으로 해시 테이블과 유사해보이지만,
자바스크립트 엔진이 object를 해시 테이블의 원리로 실행하지 않기 때문에 해시 테이블이 아니다.</p>
<h2 id="그렇다면-map-set-weakmap-weakset은-해시-테이블일까">그렇다면 Map, Set, WeakMap, WeakSet은 해시 테이블일까?</h2>
<p>또 자바스크립트에서 해시 테이블과 유사한 것으로 언급되는 대상은 Map, Set, WeakMap, WeakSet이다.
실제 ECMAScript에서도 위의 4가지를 설명하며 hash tables를 언급한다. (<a href="https://tc39.es/ecma262/#sec-map-objects">링크</a>)</p>
<blockquote>
<p>Maps must be implemented using either hash tables or other mechanisms that, on average, provide access times that are sublinear on the number of elements in the collection. The data structure used in this specification is only intended to describe the required observable semantics of Maps. It is not intended to be a viable implementation model.</p>
</blockquote>
<p>결국 이 모든 data structure들은 크롬의 V8 엔진, 사파리의 니트로, 파이어폭스의 스파이더몽키, 엣지의 차크라 등등.. 
<strong>자바스크립트를 컴파일하고 실행하는 엔진이 이 데이터 구조들을 어떻게 구현했느냐에 달려있다.</strong>
따라서 &#39;자바스크립트의 해시 테이블은 Map, Set이 있어요~&#39;라는 말은 단순히 V8의 Set과 Map의 get, set, add, has의 시간 복잡도가 O(1)이기 때문에 비슷하다고 여겨지는 것일 뿐, 
원리는 해시 테이블로 구현되지 않았다는 걸 알아두자.</p>
<h2 id="그렇다면-해시-테이블의-대안으로-object와-map-중에-무엇을-고를까">그렇다면 해시 테이블의 대안으로 Object와 Map 중에 무엇을 고를까?</h2>
<p>만약 해시 테이블의 대용으로 object와 Map 중에 고르라면.. 무엇을 사용하면 될까?
MDN에 따르면 데이터 크기가 크거나, entry들을 추가하거나 삭제하는 빈도가 더 높을 수록 object보다 Map을 사용하는 것이 좋다.
위의 상황에서는 object보다 Map이 더 나은 성능을 보이기 때문이다.
그리고 data의 사이즈를 알고 싶을 때 object는 <code>Object.values(obj).length</code>로 접근해서 O(n)이 걸리는데,
Map은 <code>map.size</code>로 접근해서 object보다 더 빠르다.
(cf. the average duration of size determination of a map with 10 million entries: Plain js object =&gt; Max 1.6sec, Map =&gt; &lt; 1ms) <br />
이외에도.. Map은 직접 iteration이 가능하다는 점, key order를 갖는다는 점 등등으로 Map을 사용하면 얻을 수 있는 이점이 많다.
다만.... 이 모든 이점에도 불구하고 object를 사용하는 이유는 데이터를 구현하기 간단하기 때문일 것. (<del>모두 다 알 거라고 생각한다. 언제 get() 써가며 값 넣을거야</del>)</p>
<h2 id="자바스크립트로-해시-테이블-구현하기">자바스크립트로 해시 테이블 구현하기</h2>
<p>번외로 해시 테이블을 자바스크립트로 구현하면 어떻게 작성할 수 있을까?
다음의 보일러플레이트에 코드를 스스로 작성해보자.</p>
<pre><code class="language-js">class HashTable {
  constructor(size) {
    this.data = new Array(size);
  }

  // Hash Function
  _hash(key) {
    let hash = 0;
    for (let i = 0; i &lt; key.length; i++) {
      hash = (hash + key.charCodeAt(i) * i) % this.data.length;
    }
    return hash;
  }

  // Insert key-value pair
  set() {}

  // Lookup a key
  get() {}

  // Return all the keys in the object.
  keys() {}
}

const myHashTable = new HashTable(50);
myHashTable.set(&#39;has_garden&#39;, true);
myHashTable.get(&#39;has_garden&#39;); // true

myHashTable.set(&#39;house_number&#39;, 54);
myHashTable.get(&#39;house_number&#39;); // 54

myHashTable.set(&#39;street_name&#39;, &#39;Main Street&#39;);
myHashTable.get(&#39;street_name&#39;); // &#39;Main Street&#39;

myHashTable.keys(); // [ &#39;has_garden&#39;, &#39;house_number&#39;, &#39;street_name&#39; ]</code></pre>
<p>(모범 답안은 <a href="https://gist.github.com/darshna09/8acffa59e92b01b7aa8a1d8a0352f956">출처</a>에서 확인해볼 수 있다..)</p>
<p>이상 자료구조의 해시 테이블을 정리해보았다!</p>
<h2 id="참고자료">참고자료</h2>
<ul>
<li><a href="https://medium.com/geekculture/ds-with-js-hash-tables-f8fec13ad12a">DS With JS — Hash Tables</a></li>
<li><a href="https://ratsgo.github.io/data%20structure&amp;algorithm/2017/10/25/hash/">해싱, 해시함수, 해시테이블</a></li>
<li><a href="https://www.freecodecamp.org/news/javascript-hash-table-associative-array-hashing-in-js/">JavaScript Hash Table – Associative Array Hashing in JS</a></li>
<li><a href="https://hsp1116.tistory.com/35">해쉬 알고리즘 요약 정리, 테스트 코드</a></li>
<li><a href="https://evan-moon.github.io/2019/06/25/hashtable-with-js/">JavaScript와 함께 해시테이블을 파헤쳐보자</a></li>
<li><a href="https://steemit.com/kr/@yahweh87/2">해시함수란 무엇인가?</a></li>
<li><a href="https://betterprogramming.pub/stop-using-objects-as-hash-maps-in-javascript-9a272e85f6a8">Stop Using Objects as Hash Maps in JavaScript</a></li>
<li><a href="https://baeharam.netlify.app/posts/data%20structure/hash-table">DS 해쉬 테이블(Hash Table)이란?</a></li>
<li><a href="https://medium.com/shell-tharsis/hash-collision-5891d7dde54f">해시 충돌</a></li>
<li><a href="https://st-lab.tistory.com/240?category=856997">자바 - 해시 셋 구현하기</a></li>
<li><a href="https://medium.com/@matthewharrilal/open-addressing-resolving-collisions-one-day-at-a-time-49415ca73f71">Open Addressing: Resolving Collisions One Day At A Time!</a></li>
<li><a href="https://medium.com/front-end-weekly/es6-map-vs-object-what-and-when-b80621932373">ES6 — Map vs Object — What and when?</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바스크립트의 { 스코프 }]]></title>
            <link>https://velog.io/@edie_ko/js-scope</link>
            <guid>https://velog.io/@edie_ko/js-scope</guid>
            <pubDate>Thu, 11 Nov 2021 17:18:05 GMT</pubDate>
            <description><![CDATA[<p>이 글에서는 스코프를 다음과 같은 순서로 설명할 것이다.</p>
<p>1) 스코프 2) 렉시컬 스코프 3) 함수와 블록 스코프
그리고 이 글은 다음 글 &#39;호이스팅&#39;에 이어진다.</p>
<h1 id="1-스코프란">1. 스코프란</h1>
<p>스코프란 식별자가 유효한 범위다.
어떤 경계가 있다고 하자. 이 경계 밖에서 선언한 a 변수는 경계 안팎으로 접근 가능하다.
하지만 경계 안에서 선언한 a 변수는 오직 경계 안에서만 접근 가능하다.</p>
<p>변수는 <strong>자신이 선언된 위치를 기준</strong>으로 본인이 유효한 범위가 결정된다.
즉 모든 식별자는 선언된 위치에 의해 다른 코드가 본인을 참조할 수 있는 유효 범위(스코프)가 결정된다.</p>
<pre><code class="language-js">var name = &#39;edie&#39;;

function sayName() {
  var name = &#39;meng&#39;;
  console.log(name); // meng
}
sayName();

console.log(name); // edie</code></pre>
<p>위의 코드에서 글로벌에서 선언된 &#39;edie&#39;는 어디서든 참조할 수 있다.
하지만 sayName함수 내부에서 선언된 name 변수는 sayName 함수 내부에서만 참조할 수 있다.
따라서 둘의 scope는 다르다.
여기서 전역 스코프와 지역 스코프의 개념을 알 수 있다.
sayName() 함수 안은 <strong>지역 스코프</strong>, 그 밖은 <strong>전역 스코프</strong>라고 할 수 있다.
그리고 지역 변수는 본인의 지역 스코프와 하위 지역 스코프에서 유효하다.</p>
<p>이런 스코프의 경계는 여러 언어에 존재한다.
다만 ES5까지의 자바스크립트는 이러한 경계를 <strong>함수에 의해서만</strong> 생성할 수 있었다.
ES6부터는 블록에 의해서도 스코프 경계가 생성되면서 다른 언어와 비슷하게 되었다.
이러한 블록도 var로 선언된 변수에 대해서는 작용하지 않고 새로 생긴 let과 const, class, strict mode에서의 함수 선언 등에 대해서만 적용된다.
ES6부터는 이 둘을 구분하기 위해 함수 스코프, 블록 스코프라는 용어를 사용한다.</p>
<h2 id="스코프-규칙이-만들어지는-과정">스코프 규칙이 만들어지는 과정</h2>
<p>이런 스코프 규칙은 어떻게 정해지는 걸까?</p>
<p>이 과정을 알기 위해서는 자바스크립트가 &#39;JIT 컴파일링 언어&#39;라는 사실을 먼저 짚고 넘어가야 한다.
자바스크립트는 우선 &#39;인터프리터 언어&#39; 방식으로 실행하고 필요할 때 컴파일 과정을 거친다. 이 방식을 JIT 컴파일링이라고 부른다.
컴파일러 언어가 컴파일링되는 과정은 크게 3단계로 1) 토크나이징 / 렉싱 2) 파싱 3) 코드 생성 순서로 진행된다.
그리고 자바스크립트의 컴파일링은 자바스크립트의 엔진이 담당한다.
크롬 브라우저의 V8엔진, 사파리 브라우저의 JavaScript Core엔진 등은 모두 자바스크립트를 JIT 컴파일링한다.</p>
<h3 id="1-토크나이징--렉싱">1) 토크나이징 / 렉싱</h3>
<p>코드를 읽어들여서 하나의 의미 있는 Token 단위로 조각내는 과정이다.
<code>const a = 1;</code>이라는 코드가 있을 때 자바스크립트 엔진은 이 코드를 다음과 같이 5조각으로 분해한다.
<code>const / a / = / 1 / ;</code> 가장 작지만 문법적인 의미가 있는 토큰 조각으로 분해된 모습이다.</p>
<h3 id="2-파싱">2) 파싱</h3>
<p>파싱은 쪼개진 토큰을 <strong>컴파일러</strong>가 프로그램의 문법 구조를 반영해서 트리 형태로 바꾸는 과정이다.
이 파싱의 결과로 만들어진 트리를 <strong>AST(Abstract Syntax Tree 추상 구문 트리)</strong>라고 한다.
아래 그림을 보자. 간단한 JavaScript 코드가 AST로 만들어진 결과물이다. (이미지 출처)[<a href="https://itnext.io/ast-for-javascript-developers-3e79aeb08343%5D">https://itnext.io/ast-for-javascript-developers-3e79aeb08343]</a></p>
<p><img src="https://images.velog.io/images/edie_ko/post/f1f0533f-d67d-4c2b-ae2c-b398e0a27a31/ast.png" alt=""></p>
<p>자세히 보자. square()라는 함수가 FunctionDeclaration 안에 id와 params, body로 나뉘어 트리 형태로 표현되었다.
아래 그림은 <code>const a = 5;</code>의 AST 결과물이다.</p>
<p><img src="https://images.velog.io/images/edie_ko/post/38204027-dd7c-4911-b208-a84bf9ad22f9/ast1.png" alt=""></p>
<h3 id="3-코드-생성">3) 코드 생성</h3>
<p>토크나이징되고 AST로 만들어진 코드는 실행 코드로 컴파일링 된다.
이 부분은 언어에 따라 혹은 플랫폼에 따라 다르다.
자바스크립트는 이 과정에서 다른 언어의 컴파일링보다 더 복잡한 과정을 거친다(JIT).
JIT 컴파일이란 언어의 컴파일링을 실제 실행하는 시점에(Just In Time) 번역하는 컴파일링 기법이다.</p>
<p>이 3단계의 과정을 통해 다음과 같이 스코프가 만들어진다.</p>
<p><code>var a = 2</code>라는 코드가 있다고 하자.
(1) 컴파일러가 var a를 만나면 스코프에게 변수 a가 스코프에 있는지 묻는다.
변수 a가 이미 있다면 컴파일러는 선언을 무시하고 지나가고, 그렇지 않으면 컴파일러가 새로운 변수 a를 스코프 컬렉션 내에 추가하라고 요청한다.
(2) 그 후 컴파일러는 a = 2를 처리하기 위해 엔진이 실행할 수 있는 코드를 작성한다.
엔진이 실행하는 코드는 먼저 스코프에게 a라 부르는 변수가 현재 스코프 내에서 접근할 수 있는지 확인한다.
가능하다면 엔진은 a를 사용하고, 아니면 바로 다음 바깥의 스코프를 살펴본다.
(3) 만약 다른 스코프에서 변수를 찾으면 변수에 값 2를 넣고, 못 찾으면 계속 다음 바깥의 스코프로 넘어가다가 글로벌 스코프에서도 못 찾는다면 엔진은 &#39;reference error&#39;를 발생시킨다.</p>
<blockquote>
<p> 📌 RHS, LHS - RHS (Right-Hand Side) 참조: console.log(a)에서 a의 값을 찾는
  참조 - LHS (Left-Hand Side) 참조: var a = 2에서 a의 값을 찾는 참조 RHS 참조가
  대상을 찾지 못하면 referenceError 발생한다. LHS 참조가 대상을 찾지 못하면
  암시적으로 글로벌 스코프에 같은 이름의 새로운 변수가 생성된다. 하지만 Strict
  모드일 경우에는 똑같이 referenceError가 발생한다.</p>
</blockquote>
<h2 id="스코프-체인">스코프 체인</h2>
<p>앞서 이야기한 것처럼 변수를 참조할 때 자바스크립트 엔진은 실행 코드의 스코프에서 시작해서 계속 바깥의 상위 스코프로 이동하며 변수를 검색했다.
&#39;식별자 유효범위&#39;를 안에서부터 바깥으로 차례로 검색해나가는 것을 <strong>스코프 체인</strong>이라고 한다.
스코프 체인은 실행 컨텍스트의 렉시컬 환경을 단방향으로 연결한 것이다. 이 연결은 <code>LexicalEnvironment</code>의 두 번째 수집 자료인 <code>outerEnvironmentReference</code>를 통해 이뤄진다.</p>
<p>여기서 중요한 규칙은 상위 스코프의 변수는 하위 스코프에서 접근할 수 있지만, 하위 스코프에서 선언한 식별자는 상위에서 참조할 수 없다는 것이다.</p>
<h1 id="2-렉시컬-스코프">2. 렉시컬 스코프</h1>
<p>어떤 함수가 어디서 또는 어떻게 호출되는지에 상관없이 함수의 렉시컬 스코프는 <strong>함수가 선언된 위치</strong>에 따라 정의된다.
렉시컬 환경의 outerEnvironmentReference에 저장할 참조값, 즉 상위 스코프에 대한 참조는 함수 정의가 평가되는 시점에
함수가 정의된 환경(위치)에 의해 결정된다. 이것이 바로 렉시컬 스코프다.</p>
<h2 id="environment">[[Environment]]</h2>
<p>앞선 글 <a href="https://positiveko.netlify.app/javascript-execution-context">실행 컨텍스트</a>에서 LexicalEnvironment와 variableEnvironment에는
environmentRecord와 outerEnvironmentReference라는 속성이 있다고 했다.
특히 environmentRecord에는 변수와 함수가 저장이 된다. 그리고 outerEnvironmentReference에는 현재 스코프보다 바깥에 있는 environmentRecord를 참고한다.
아래 코드를 보자</p>
<pre><code class="language-js">const author = &#39;edie&#39;;
console.log(author);

function say() {
  const name = &#39;meng&#39;;
  console.log(name);
  console.log(author);
}
say();</code></pre>
<p>say()라는 함수는 함수가 정의된 환경과 호출되는 환경이 다를 수 있기 때문에, 자신이 호출되는 환경과는 상관없이 자신이 정의된 환경, 즉 상위 스코프를 기억해야 한다.
따라서 함수는 [[Environment]]에 자신이 정의된 환경, 즉 상위 스코프의 참조를 저장한다.
이때 [[Environment]]는 현재 실행 중인 실행 컨텍스트의 렉시컬 환경을 가리킨다. 따라서 [[Environment]]가 곧 상위 스코프라고 보면 된다.
또한 이것은 say()라는 함수가 호출되었을 때 생성될 LexicalEnvironment의 outerEnvironmentReference에 저장될 값이다.</p>
<h1 id="3-함수와-블록-레벨-스코프">3. 함수와 블록 레벨 스코프</h1>
<p>앞서 이야기했던 문장을 다시 꺼내보자.
ES5까지의 자바스크립트는 이러한 경계를 <strong>함수에 의해서만</strong> 생성할 수 있었다.
ES6부터는 블록에 의해서도 스코프 경계가 생성되면서 다른 언어와 비슷하게 되었다.
이러한 블록도 var로 선언된 변수에 대해서는 작용하지 않고 새로 생긴 let과 const, class, strict mode에서의 함수 선언 등에 대해서만 적용된다.
ES6부터는 이 둘을 구분하기 위해 함수 스코프, 블록 스코프라는 용어를 사용한다.</p>
<h2 id="1-함수-레벨-스코프">1) 함수 레벨 스코프</h2>
<p>var로 선언된 변수는 오로지 함수의 코드 블록만을 지역 스코프로 인정한다. 이러한 특성을 함수 레벨 스코프라 한다.</p>
<pre><code class="language-js">var i = 10;
// var로 선언된 i는 전역 변수
for (var i = 0; i &lt; 5; i++) {
  console.log(i);
}
console.log(i); // 5</code></pre>
<p>위의 코드에서 for문 안에 선언된 i는 var로 선언되었기 때문에 전역 변수가 된다. 따라서 마지막 줄의 console에는 5가 나온다.
즉, var 키워드로 선언된 변수는 오로지 함수의 코드 블록만을 지역 스코프로 인정한다.
또한 var로 변수를 선언하면 변수 호이스팅에 의해 변수 선언이 스코프의 상위로 끌어 올려진 것처럼 작용한다.
따라서 var로 선언한 변수는 선언 이전에 미리 참조할 수 있다.</p>
<pre><code class="language-js">// 변수 호이스팅에 의해 이미 foo 변수가 선언되었다.
// 변수 foo는 undefined로 초기화된다.
console.log(b); // undefined
// 변수에 값 할당
b = 123;
console.log(b); // 123
// 변수 선언은 런타임 이전에 자바스크립트 엔진에 의해 암묵적으로 실행된다.
var b;</code></pre>
<p>이러한 방식으로 var로 선언한 변수는 전역 변수가 되어 전역 객체의 프로퍼티가 된다.
이 뜻은 전역 변수의 문제점을 쉽게 야기할 수 있다는 뜻이다. (암묵적 결합, 긴 생명 주기, 검색 속도 느림, 네임스페이스 오염)
이러한 var의 특성으로 인해서 기존에는 즉시호출함수(IIFE)를 사용하거나 모듈로 숨기는 방식으로 단점을 극복했다.
하지만 var가 아닌 let, const로 선언해서 블록 레벨 스코프를 사용하도록 하는 것이 좋다.</p>
<h2 id="2-블록-레벨-스코프">2) 블록 레벨 스코프</h2>
<p>let과 const로 선언한 변수는 모든 코드 블록을 지역 스코프로 인정하는 블록 레벨 스코프를 따른다.</p>
<pre><code>let foo = 1;
{
  let foo = 2;
  let bar = 3;
}
console.log(foo); // 1
console.log(bar); // ReferenceError: bar is not defined</code></pre><h2 id="참고-자료">참고 자료</h2>
<ul>
<li><a href="https://itnext.io/ast-for-javascript-developers-3e79aeb08343">AST for JavaScript developers</a></li>
<li><a href="https://gyujincho.github.io/2018-06-19/AST-for-JS-devlopers">자바스크립트 개발자를 위한 AST(번역)</a></li>
<li><a href="https://oowgnoj.dev/review/advanced-js-1">JavaScript, 인터프리터 언어일까?</a></li>
<li>이웅모, &lt;모던 자바스크립트 Deep Dive&gt;</li>
<li>카일 심슨, &lt;You don&#39;t know JS&gt;</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바스크립트의 this는 김춘수의 〈꽃〉이다]]></title>
            <link>https://velog.io/@edie_ko/js-this</link>
            <guid>https://velog.io/@edie_ko/js-this</guid>
            <pubDate>Fri, 05 Nov 2021 14:42:47 GMT</pubDate>
            <description><![CDATA[<p>this는 볼 때마다 새롭다.
내가 이 글을 다 써갈 때 쯤이면.. 더이상 this를 헷갈려하지 않을 수 있을까..?
마지막에는 this와 관련한 다양한 실전 문제를 모아봤다.
문제를 풀어보고 틀린 부분이 있다면 다시 관련한 부분을 찾아서 보도록 하자.</p>
<p><em>참고로 이 글은 <a href="https://velog.io/@edie_ko/js-execution-context">앞선 글 - 자바스크립트의 실행 컨텍스트란 무엇인가요?</a>에 이어지는 글입니다.</em></p>
<h1 id="1-그렇다면-this는-무엇이냐">1. 그렇다면 this는 무엇이냐?</h1>
<p>어느 날 읽던 책에서는 자바스크립트의 <code>this</code>를 김춘수의 &lt;꽃&gt;과 같다고 설명했다.
<code>this</code>가 무엇인지는 함수를 호출한 코드에 달려있으며, <strong>함수가 호출될 때</strong> 동적으로 결정되기 때문이다.
<a href="https://velog.io/@edie_ko/js-execution-context">앞선 글</a>에서 함수가 호출되면 실행 컨텍스트가 생성되고, LexicalEnvironment가 생성되며, environmentRecord 생성, <strong>ThisBinding</strong>, outerEnvironmentReference 결정된다고 했다. 실행 컨텍스트는 곧 함수를 호출할 때 생성되므로, this는 함수를 호출할 때 결정되는 것이다.</p>
<h1 id="2-this-바인딩">2. this 바인딩</h1>
<blockquote>
<p>📌 바인딩이란 식별자와 값을 연결하는 과정을 의미한다. 예를 들어, 변수 선언은 변수 이름(식별자)과 확보된 메모리 공간의 주소를 바인딩하는 것이다. this바인딩은 this(식별자 역할)와 this가 가리킬 객체를 바인딩하는 것이다.
  📌 자바나 C++ 같은 클래스 기반 언어에서 this는 언제나 클래스가 생성하는 인스턴스를 가리킨다. 하지만 자바스크립트의 this는 함수가 호출되는 방식에 따라 this에 바인딩될 값, 즉 this 바인딩이 동적으로 결정된다. 또한 strict mode 역시 this 바인딩에 영향을 준다.
  📌 함수의 상위 스코프를 결정하는 방식인 렉시컬 스코프는 함수 정의가 평가되어 함수 객체가 생성되는 시점에 상위 스코프를 결정한다. 하지만 this 바인딩은 함수 호출 시점에 결정된다.
  <br />- 이웅모, 모던 자바스크립트 Deep Dive</p>
</blockquote>
<p>다시 반복하자. this는 선언 시점이 아닌 런타임 시점에 바인딩되며, 함수 호출 당시 상황에 따라 콘텍스트가 결정된다.
즉 함수 선언 위치와 상관없이 this 바인딩은 오로지 <strong>어디서 어떻게 함수를 호출하느냐</strong>에 따라 결정된다.</p>
<h3 id="함수를-호출하는-방법은-다음과-같이-다양하다">함수를 호출하는 방법은 다음과 같이 다양하다.</h3>
<ul>
<li>일반 함수 호출 (기본 바인딩)</li>
<li>메서드 호출 (암시적 바인딩)</li>
<li>Function.prototype.apply/call/bind 메서드에 의한 간접 호출 (명시적 바인딩)</li>
<li>생성자 함수 호출 (new 바인딩)</li>
</ul>
<p>그리고 this가 가리키는 것이 무엇인지를 알기위해서는 함수 선언 부분이 아니라 <strong>호출하는 지점(호출부)</strong>을 봐야한다.
호출부란 현재 실행 중인 함수 &#39;직전&#39;의 코드 내부에 있다.</p>
<pre><code class="language-js">function sayHi() {
  console.log(&#39;hi&#39;);
  sayHello(); // sayHello() 호출부
}

function sayHello() {
  console.log(&#39;hello&#39;);
  sayHey(); // sayHi() 호출부
}

function sayHey() {
  console.log(&#39;hey&#39;);
}
sayHi(); // sayHi() 호출부</code></pre>
<p>이제 호출부가 무엇인지 안다. 그럼 이제는 호출부가 함수를 호출하는 방법 4가지 중 어디에 속하는지 확인한다.</p>
<pre><code class="language-js">const foo = function() {
  console.log(this);
};

// 1. 일반 함수 호출
// this = 전역 객체
foo(); // window

// 2. 메서드 호출
// this = 메서드를 호출한 객체 obj
const obj = { foo };
obj.foo(); // { foo: ƒ foo() }

// 3. call 호출
// this = 명시적으로 this와 바인딩한 대상 객체
foo.call({ a: &#39;edie&#39; }); // { a: &#39;edie&#39; }

// 4. 생성자 함수 호출
// this = 생성자 함수가 생성한 인스턴스
new foo(); // foo { __proto__: { constructor: ƒ foo() } }</code></pre>
<p>그럼 이제부터는 하나씩 자세하게 살펴보자.</p>
<h2 id="1-일반-함수-호출-기본-바인딩">1) 일반 함수 호출 (기본 바인딩)</h2>
<p>기본적으로 this에는 전역 객체가 바인딩된다.
즉 브라우저 환경에서는 window이고 Node.js 환경에서는 global을 가리킨다.</p>
<pre><code class="language-js">function foo() {
  console.log(this); // window
  function bar() {
    console.log(this); // window
  }
  bar();
}
foo();</code></pre>
<p>그런데 이는 non-strict mode인 경우다. 만약 strict mode인 경우에 일반 함수의 this는 undefined가 바인딩 된다.
this는 객체의 프로퍼티나 메서드를 참조하기 위한 자기 참조 변수이므로 객체를 생성하지 않는 이상 일반 함수에서 this는 의미가 없기 때문이다.</p>
<pre><code class="language-js">function foo() {
  &#39;use strict&#39;;

  console.log(this); // undefined
}
foo();</code></pre>
<h3 id="1-1-콜백-함수-호출-시-그-함수-내부에서의-this">1-1) 콜백 함수 호출 시 그 함수 내부에서의 this</h3>
<p>콜백 함수 내부의 this는 무엇을 가리킨다!라고 확실하게 말할 수 없다. 콜백 함수의 제어권을 가지는 함수가 콜백 함수의 this를 결정한다.
아래 예제를 보며 확인해보자.</p>
<pre><code class="language-js">setTimeout(function() {
  console.log(this); // window;
  // 콜백 함수가 일반 함수로 호출되었으므로 전역 객체가 바인딩된다.
}, 300);

[1, 2, 3, 4, 5].forEach((num) =&gt; {
  console.log(this, num); // window, num;
});

document.body.querySelector(&#39;#id&#39;).addEventListener(&#39;click&#39;, function(e) {
  console.log(this, e); // &#39;#id&#39; 엘리먼트와 클릭 이벤트에 대한 객체
});</code></pre>
<h2 id="2-메서드-호출-암시적-바인딩">2) 메서드 호출 (암시적 바인딩)</h2>
<p>메서드 내부의 this에는 호출한 주체에 대한 정보가 담긴다. 따라서 어떤 함수를 메서드로 호출하는 경우, 호출 주체는 바로 함수명 앞의 객체다. <code>obj.method()</code>의 경우 method의 this는 obj를 가리킨다.
해당 함수를 호출하는 구문 앞에 점 혹은 대괄호 표기가 있다면 그건 메서드 호출이다.</p>
<pre><code class="language-js">const obj = {
  outer: function() {
    console.log(this);
    const inner = function() {
      console.log(this); // 함수 실행이므로 전역객체 window
    };
    inner();
  },
};
obj.outer(); // 메서드 호출이므로 obj</code></pre>
<h2 id="3-functionprototypeapplycallbind-메서드에-의한-간접-호출-명시적-바인딩">3) Function.prototype.apply/call/bind 메서드에 의한 간접 호출 (명시적 바인딩)</h2>
<p>앞에서 살펴보았듯 다양한 상황에서 암시적으로 바인딩되는 this는 여러 이유로(콜백 함수..)
this의 바인딩을 쉽게 예측할 수 없는 상황이 있었다.
이런 문제를 해결하기 위해 명시적으로 this를 고정하는 방법을 이야기해보자.</p>
<h3 id="3-1-call-메서드">3-1) call 메서드</h3>
<blockquote>
<p>func.call(thisArg[, arg1[, arg2[, ...]]])</p>
</blockquote>
<p>call 메서드는 메서드의 호출 주체인 함수를 즉시 실행하도록 하는 명령이다.
첫 번째 인자를 this로 바인딩하고, 이후의 인자들을 호출할 함수의 매개변수로 쓴다.
일반적으로 함수의 this는 전역객체이지만 call을 사용하면서 구체적인 객체를 this로 지정할 수 있다.</p>
<pre><code class="language-js">function foo() {
  console.log(this.a);
}
const obj = { a: 2 };
// 명시적으로 obj를 바인딩하라고 call 했다. this는 obj가 된다.
foo.call(obj); // 2
foo(obj); // undefined</code></pre>
<pre><code class="language-js">function plus(num) {
  this.count += num;
}
plus.count = 0;

for (i = 0; i &lt; 10; i++) {
  if (i &lt; 5) {
    plus.call(plus, i);
  }
}
console.log(plus.count); // 10</code></pre>
<p><em>-위의 예제에 오류가 있어 수정하였습니다. (2021.11.12)</em></p>
<h3 id="3-2-apply-메서드">3-2) apply 메서드</h3>
<blockquote>
<p>func.apply(thisArg, [argsArray])</p>
</blockquote>
<p>call()과 기능적으로 유사하다.
차이점은 call()은 함수에 전달될 매개변수를 받는데, apply()는 인수들의 <strong>배열</strong>을 받는다.</p>
<pre><code class="language-js">function foo(a, b, c) {
  console.log(this, a, b, c);
}
const obj = { a: 2 };

foo.call(obj, 4, 5, 6); // { a: 2 } 4 5 6
// apply는 두 번째 인자로 함수 인수들의 배열을 받는다.
foo.apply(obj, [4, 5, 6]); // { a: 2 } 4 5 6</code></pre>
<p>이 call과 apply를 어떤 상황에서 이용할 수 있을까?
유사배열객체에 배열 메서드를 사용하고 싶을 때 이용할 수 있다.</p>
<pre><code class="language-js">const obj = {
  0: &#39;hi&#39;,
  1: &#39;hello&#39;,
  length: 2,
};

Array.prototype.push.call(obj, &#39;hey&#39;);
// [].push.call(obj, &#39;hey&#39;) 이렇게 써도 동일하다.
console.log(obj);</code></pre>
<p>위와 같은 유사배열 뿐만 아니라 arguments 객체, Node 선택자로 생성된 NodeList도 이런 방식으로 배열 처럼 사용할 수 있다.</p>
<h3 id="3-3-bind-메서드">3-3) bind 메서드</h3>
<blockquote>
<p>func.bind(thisArg[, arg1[, arg2[, ...]]])</p>
</blockquote>
<p>bind 메서드는 apply, call과는 달리 함수를 호출하지 않고 this로 사용할 객체만 전달한다.
즉 call, apply 메서드는 this를 명시적으로 지정하면서 함수 또는 메서드를 호출하는데,
bind 메서드는 this 및 함수에 넘길 인수를 일부 지정해서 새로운 함수를 만든다.</p>
<pre><code class="language-js">function foo() {
  console.log(this);
}
const obj = { a: 2 };

foo.call(obj); // { a: 2 }
foo.apply(obj); // { a: 2 }
// 똑같이 bind로 바꾸면 함수를 호출하지 않는 걸 알 수 있다.
foo.bind(obj); // ƒ bound foo()
// 명시적으로 실행을 추가해주어야 한다.
foo.bind(obj)(); // { a: 2 }</code></pre>
<p>bind는 다음과 같이 메서드 내부의 중첩 함수 또는 콜백 함수의 this가 일치하지 않는 문제를 해결할 수 있다.</p>
<pre><code class="language-js">const person = {
  name: &#39;edie&#39;,
  foo(callback) {
    setTimeout(callback, 100);
  }
};
person.foo(function() {
  console.log(`hi, i&#39;m ${this.name}`); // &quot;hi, i&#39;m &quot; 
  // 일반 함수로 호출된 콜백 함수 내부의 this는 전역객체다. (window.name)
  // 따라서 this.name이 출력되지 않았다.
})</code></pre>
<p>아래와 같이 bind로 callback에 this를 명시해줌으로써 해결할 수 있다.</p>
<pre><code class="language-js">const person = {
  name: &#39;edie&#39;,
  foo(callback) {
    setTimeout(callback.bind(person), 100);
  }
};
person.foo(function() {
  console.log(`hi, i&#39;m ${this.name}`); // &quot;hi, i&#39;m edie&quot;
})</code></pre>
<h2 id="4-생성자-함수-호출-new-바인딩">4) 생성자 함수 호출 (new 바인딩)</h2>
<pre><code class="language-js">function Toy(make, model, year) {
  this.make = make;
  this.model = model;
  this.year = year;
}

const woody = new Toy(&#39;PIXAR&#39;, &#39;cowboy&#39;, 1995);
const wallE = new Toy(&#39;PIXAR&#39;, &#39;robot&#39;, 2008);
const remy = Toy(&#39;PIXAR&#39;, &#39;rat&#39;, 2007);

console.log(woody.make); // &#39;PIXAR&#39;
console.log(wallE.model); // &#39;robot&#39;
console.log(remy); // undefined. new 생성자를 사용하지 않았기 때문에 일반함수로 호출되어 this는 전역객체를 가리킨다.</code></pre>
<p>어떤 함수를 new 명령어와 함께 호출하면 생성자로서 동작하게 된다.
객체지향 언어에서는 생성자를 클래스, 클래스를 통해 만든 객체를 인스턴스라고 한다. 생성자 함수란 new 연산자로 함께 호출해서 인스턴스를 생성하는 함수를 일컫는다.
따라서 생성자는 인스턴스를 만들기 위한 일종의 <strong>틀</strong>이다. 그리고 생성자 함수로 호출된 경우 this는 새로 만들어진 인스턴스 자신이 된다.</p>
<p>더 자세하게 이 과정을 풀어보자.</p>
<pre><code class="language-js">function Toy(make, model, year) {
  // 1. 암묵적으로 인스턴스가 생성되고 this에 바인딩된다.
  console.log(this); // Toy { __proto__: { constructor: ƒ Toy() } }

  // 2. this에 바인딩되어 있는 인스턴스를 초기화한다.
  this.make = make;
  this.model = model;
  this.year = year;
  this.getAge = function() {
    return new Date().getFullYear() - this.year + 1;
  };
  // 3. 완성된 인스턴스가 바인딩된 this가 암묵적으로 반환된다.
}

// 4. 인스턴스 생성. Toy 생성자 함수는 암묵적으로 this를 반환한다.
const woody = new Toy(&#39;PIXAR&#39;, &#39;cowboy&#39;, 1995);
console.log(woody);
// Toy {
//   make: &#39;PIXAR&#39;,
//   model: &#39;cowboy&#39;,
//   year: 1995,
//   getAge: ƒ (),
//   __proto__: { constructor: ƒ Toy() }
// }
console.log(woody.year); // &#39;PIXAR&#39;
console.log(woody.getAge()); // 27</code></pre>
<ul>
<li>1번에서 new 생성자로 인해 <strong>빈 객체(인스턴스)가 생성</strong>된다. 암묵적으로 생성된 빈 객체는 <strong>this에 바인딩</strong>된다.
그리고 이 처리는 런타임 이전에 실행된다.</li>
<li>2번에서 생성자 함수 내부의 코드가 한 줄씩 실행되며 this에 바인딩되어 있는 <strong>인스턴스를 초기화</strong>한다.
즉, this에 바인딩되어 있는 인스턴스에 프로퍼티나 메서드를 추가하면서 초기화하거나 고정값을 할당한다.</li>
<li>3번에서 생성자 함수 내부 처리가 끝나면 완성된 <strong>인스턴스가 바인딩된 this가 암묵적으로 반환</strong>된다.</li>
</ul>
<p>생성자 함수 내부에서 값을 직접 반환하는 경우에는 어떻게 될까?
결과는 어떤 값을 반환하느냐에 달려있다.
아래 예제를 확인하자.</p>
<p>먼저 명시적으로 객체를 반환하는 경우다.</p>
<pre><code class="language-js">function Toy(make, model, year) {
  console.log(this); // Toy { __proto__: { constructor: ƒ Toy() } }

  this.make = make;
  this.model = model;
  this.year = year;
  this.getAge = function() {
    return new Date().getFullYear() - this.year + 1;
  };
  // 아래처럼 명시적으로 객체를 반환하면 암묵적인 this반환이 무시된다.
  return {};
}

const woody = new Toy(&#39;PIXAR&#39;, &#39;cowboy&#39;, 1995);
console.log(woody); // {}</code></pre>
<p>아래는 명시적으로 원시 값을 반환하는 경우다.</p>
<pre><code class="language-js">function Toy(make, model, year) {
  console.log(this); // Toy { __proto__: { constructor: ƒ Toy() } }

  this.make = make;
  this.model = model;
  this.year = year;
  this.getAge = function() {
    return new Date().getFullYear() - this.year + 1;
  };
  // 명시적으로 원시값을 반환하면 암묵적인 this가 반환된다.
  return &#39;우디&#39;;
}

const woody = new Toy(&#39;PIXAR&#39;, &#39;cowboy&#39;, 1995);
console.log(woody);
// Toy {
//   make: &#39;PIXAR&#39;,
//   model: &#39;cowboy&#39;,
//   year: 1995,
//   getAge: ƒ (),
//   __proto__: { constructor: ƒ Toy() }
// }</code></pre>
<p>따라서 생성자 함수 내부에서 명시적으로 this가 아닌 값을 반환하는 경우 생성자 함수의 기본 동작을
훼손할 수 있으므로 return문을 생략해야 한다.</p>
<h1 id="3-this-바인딩-예외">3. this 바인딩 예외</h1>
<h2 id="3-1-화살표-함수">3-1. 화살표 함수</h2>
<p>화살표 함수는 함수 자체의 this 바인딩을 갖지 않는다.
ES6의 화살표 함수는 일반적인 바인딩 규칙을 무시하고 렉시컬 스코프로 this를 바인딩한다.
즉 화살표 함수 내부에서 this를 참조하면 상위 스코프의 this를 그대로 참조한다. (=== &#39;lexical this&#39;)
그렇다면 화살표 함수가 중첩된 경우에는 어떻게 될까..?
이럴 경우 inner 화살표 함수는 outer 화살표 함수 바깥의 상위 함수 중에서 화살표 함수가 아닌 함수의 this를 참조한다.</p>
<pre><code class="language-js">// 1)
const foo = () =&gt; console.log(this);
foo(); // window

// 2)
(function() {
  const foo = () =&gt; {
    const bar = () =&gt; {
      console.log(this);
    };
    bar();
  };
  foo();
}.call({ a: 1 })); // { a: 1 }</code></pre>
<p>화살표 함수로 메서드를 정의하는 경우와 프로토타입 객체의 프로퍼티에 화살표를 할당하는 경우에도 동일한 문제가 발생한다.</p>
<pre><code class="language-js">// 화살표 함수로 메서드를 정의하는 경우
// bad
const person = {
  name: &#39;edie&#39;,
  sayHi: () =&gt; console.log(`hi ${this.name}`)
}
person.sayHi(); // &#39;hi&#39;;

// good
const person = {
  name: &#39;edie&#39;,
  sayHi() {
    console.log(`hi ${this.name}`)
  }
}
person.sayHi(); // &#39;hi edie&#39;;

// 프로토타입 객체의 프로퍼티에 화살표 함수를 할당하는 경우
// bad
function Person(name) {
  this.name = name;
}

Person.prototype.sayHi = () =&gt; console.log(`hi ${this.name}`);
const Person = new Person(&#39;edie&#39;);
person.sayHi(); // hi

// good
function Person(name) {
  this.name = name;
}

Person.prototype.sayHi = function() { console.log(`hi ${this.name}`); };
const Person = new Person(&#39;edie&#39;);
person.sayHi(); // hi edie</code></pre>
<p><em>-위의 예제에 오타가 있어 정정하였습니다. (2021.11.12)</em></p>
<h2 id="3-2-별도의-인자로-this를-받는-경우-thisarg">3-2. 별도의 인자로 this를 받는 경우 (thisArg)</h2>
<p>콜백 함수를 인자로 받는 메서드 중 일부에 경우 추가로 this로 지정할 객체(thisArg)를 인자로 지정할 수 있는 경우가 있다.
이러한 메서드의 thisArg 값을 지정하면 콜백 함수 내부에서 this값을 원하는 대로 변경할 수 있다. 주로 배열 메서드(forEach, map, filter, some, every, find, findIndex, flatMap)가 많으며 Set과 Map의 일부 메서드(forEach)에도 포함된다.</p>
<pre><code class="language-js">const report = {
  sum: 0,
  count: 0,
  add: function () {
    const args = Array.prototype.slice.call(arguments);
    args.forEach(function(entry) {
      this.sum += entry;
      ++this.count;
      // this를 thisArg로 넣어주었다.
    }, this);
  }
}

report.add(2, 4, 6);
console.log(report.sum, report.count);</code></pre>
<h2 id="3-3-call-apply-bind에-첫-번째-인자로-null--undefined를-넘기는-경우">3-3. call, apply, bind에 첫 번째 인자로 null | undefined를 넘기는 경우</h2>
<p>call, apply, bind에 첫 번째 인자로 null | undefined를 넘기는 경우 this바인딩이 무시되고 기본 바인딩 규칙이 적용된다.</p>
<pre><code class="language-js">function foo() {
  console.log(this.a);
}
var a = 2;
foo.call(null); // 2</code></pre>
<h1 id="4-어떤-thisbinding이-가장-강력할까">4. 어떤 thisBinding이 가장 강력할까?</h1>
<p>이제까지 this의 다양한 바인딩에 대해 정리해보았다. 그런데 만약 여러 개의 규칙이 중복으로 적용된 경우엔 어떻게 알 수 있을까?
그때는 우선순위가 높은 this binding을 기준으로 확인하면 된다.</p>
<ul>
<li>1등: new의 호출로 새로 생성된 객체</li>
<li>2등: call, apply, bind로 주어진 객체</li>
<li>3등: 호출의 주체인 콘텍스트 객체로 호출된 경우 그 콘텍스트 객체</li>
<li>4등: 기본 바인딩인 경우 &#39;strict mode&#39;인 경우 undefined, &#39;non-strict mode&#39;에서는 전역 객체</li>
</ul>
<p>따라서 this 바인딩을 알기 위해서는 <strong>어디서 어떻게 함수를 호출하는지</strong> 찾고,
위의 순서에 따라 this가 무엇을 가리키는지 예측해보자.</p>
<h1 id="5-this-문제-풀이">5. this 문제 풀이</h1>
<p>본인이 확실하게 이해한 건지 확인할 수 있는 문제를 추가해보았다.
아래 문제를 보면서 console.log에는 어떻게 나올지 예상해보자.
답은 제일 마지막에 한꺼번에 적겠다.</p>
<h4 id="1번">1번</h4>
<pre><code class="language-js">var name = &#39;edie&#39;;

var user = {
  name: &#39;meng&#39;,
  getName: function() {
    console.log(this.name); // (1)

    var inner = function() {
      console.log(this.name); // (2)
    };
    inner();
  },
};

user.getName();</code></pre>
<h4 id="2번">2번</h4>
<pre><code class="language-js">var name = &#39;edie&#39;;

var user = {
  name: &#39;meng&#39;,
  getName: function() {
    console.log(this.name);
  },
  age: 30,
  child: {
    age: 15,
    underTwenty: function() {
      return this.age &lt; 20;
    },
  },
};

user.getName(); // (1)
user.child.underTwenty(); // (2)
user.parentUnderTwenty = user.child.underTwenty;
user.parentUnderTwenty(); // (3)</code></pre>
<h4 id="3번">3번</h4>
<pre><code class="language-js">const object = {
  message: &#39;Hello, World!&#39;,
  getMessage() {
    const message = &#39;Hey, World!&#39;;
    return this.message;
  },
};
console.log(object.getMessage());</code></pre>
<h4 id="4번">4번</h4>
<pre><code class="language-js">function Person(name) {
  this.name = name;
  this.getName = () =&gt; this.name;
}
const person = new Person(&#39;Edie&#39;);
console.log(person.getName());
const { getName } = person;
console.log(getName());</code></pre>
<h4 id="5번">5번</h4>
<pre><code class="language-js">const obj = {
  name: &#39;edie&#39;,
  sayHi() {
    console.log(`hi, ${this.name}`);
  },
};
setTimeout(obj.sayHi, 1000);</code></pre>
<h4 id="6번-아래에-콘솔에-edie가-로그되려면-어떻게-코드를-수정해야-할까">6번. 아래에 콘솔에 &#39;edie&#39;가 로그되려면 어떻게 코드를 수정해야 할까?</h4>
<pre><code class="language-js">const person = {
  name: &#39;edie&#39;,
};

function sayHi() {
  console.log(this.name); // &#39;edie&#39;
}</code></pre>
<h4 id="7번">7번</h4>
<pre><code class="language-js">const obj = {
  who: &#39;edie&#39;,
  sayHi() {
    return `hi, ${this.who}!`;
  },
  sayBye: () =&gt; {
    return `goobye, ${this.who}!`;
  },
};
console.log(obj.sayHi());
1;
console.log(obj.sayBye());
2;</code></pre>
<h4 id="8번">8번</h4>
<pre><code class="language-js">var length = 1;
function callback() {
  console.log(this.length);
}

const object = {
  length: 2,
  method1(callback) {
    callback();
  },
  methods2: (callback) =&gt; callback(),
};
object.method1(callback); // (1)
object.methods2(callback); // (2)</code></pre>
<h4 id="9번">9번</h4>
<pre><code class="language-js">var length = 2;
function callback() {
  console.log(this.length);
}

const obj = {
  length: 3,
  method1() {
    arguments[0]();
  },
  method2(callback) {
    callback();
  },
  method3: (callback) =&gt; {
    callback();
  },
  method4() {
    callback();
  },
};
obj.method1(callback, &#39;1&#39;, &#39;2&#39;, &#39;3&#39;, &#39;4&#39;); // (1)
obj.method2(callback, &#39;1&#39;, &#39;2&#39;, &#39;3&#39;, &#39;4&#39;); // (2)
callback.bind(obj)(); // (3)
obj.method3(callback); // (4)
obj.method4(); // (5)</code></pre>
<h4 id="정답">정답</h4>
<pre><code class="language-js">1.
(1) &#39;meng&#39; (2) &#39;edie&#39;
2.
(1) &#39;meng (2) true (3) false
3.
&#39;Hello, World!&#39;
4.
(1)&#39;Edie&#39; (2) &#39;Edie&#39;
5.
&#39;hi, &#39;
6.
(방법 1) person property에 sayHi를 추가해서 person.sayHi() 호출
const person = {
  name: &#39;edie&#39;,
  sayHi
};

function sayHi() {
  console.log(this.name); // &#39;edie&#39;
};

person.sayHi();

(방법 2) call, apply, bind 활용하여 명시적 바인딩
sayHi.call(person);
sayHi.apply(person);
sayHi.bind(person)();

7.
(1)&#39;hi, edie!&#39;
(2)&#39;goobye, undefined!&#39;
8.
(1) 1 (2) 1
9.
(1) 5 (2) 2 (3) 3 (4) 2 (5) 2</code></pre>
<br />
<br />
<br />

<h2 id="참고-자료">참고 자료</h2>
<ul>
<li>이웅모, &lt;모던 자바스크립트 Deep Dive&gt;</li>
<li>정재남, &lt;코어 자바스크립트&gt;</li>
<li>카일 심슨, &lt;You don&#39;t know JS&gt;</li>
<li><a href="https://dmitripavlutin.com/javascript-this-interview-questions/">7 Interview Questions on &quot;this&quot; keyword in JavaScript. Can You Answer Them?</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[실행 컨텍스트란 무엇인가요?]]></title>
            <link>https://velog.io/@edie_ko/js-execution-context</link>
            <guid>https://velog.io/@edie_ko/js-execution-context</guid>
            <pubDate>Thu, 04 Nov 2021 15:17:40 GMT</pubDate>
            <description><![CDATA[<h1 id="1-실행-컨텍스트란">1. 실행 컨텍스트란</h1>
<blockquote>
<p> 📌 <strong>실행 컨텍스트(execution context)</strong>는 실행할 코드에 제공할 환경 정보들을
  모아놓은 객체로, 자바스크립트의 동적 언어로서의 성격을 가장 잘 파악할 수 있는
  개념이다. <br />- 정재남, 코어자바스크립트</p>
</blockquote>
<p>실행 컨텍스트는 자바스크립트 코드가 실행되는 환경이다. 모든 JavaScript 코드는 실행 컨텍스트 내부에서 실행된다고 생각하면 된다.<br />
즉 함수가 실행되면 함수 실행에 해당하는 <code>실행 컨텍스트</code>가 생성되고, 자바스크립트 엔진에 있는 콜 스택에 차곡차곡 쌓인다.<br />
그리고 가장 위에 쌓여있는 컨텍스트와 관련 있는 코드를 실행하면서(LIFO), 전체 코드의 환경과 순서를 보장하게 된다.
실행 컨텍스트는 식별자(변수, 함수, 클래스 등의 이름)를 등록하고 관리하는 스코프와 코드 실행 순서 관리를 구현한 내부 매커니즘으로, 실행 컨텍스트는 곧 자바스크립트의 핵심 원리다.</p>
<h1 id="2-실행-컨텍스트는-언제-생성될까">2. 실행 컨텍스트는 언제 생성될까?</h1>
<p>자바스크립트 엔진이 스크립트를 처음 마주할 때 전역 컨텍스트를 생성하고, 콜 스택에 <code>push</code> 한다.
엔진이 스크립트를 쭉 읽어내려가면서 함수 호출을 발견할 때마다, 함수의 실행 컨텍스트를 스택에 <code>push</code> 한다.
중요한 점은 함수 실행 컨텍스트는 함수가 <strong>실행</strong>될 때! 만들어진다는 점이다.<br />
함수를 선언할 때가 아니라 실행할 때다.</p>
<p>실행 컨텍스트를 만들 수 있는 방법으로는 1) 전역공간, 2) 함수, 3) eval() 함수가 있다.
이 중에서 eval 함수는 문자열로 된 자바스크립트 코드를 전달하면 그게 그대로 실행되는 함수인데, 속도나 보안이 좋지 않아 현재는 거의 쓰지 않는다. <del>없다고 생각하자</del></p>
<blockquote>
<p><code>&quot;eval is evil&quot; - Douglas Crockford</code></p>
</blockquote>
<p>따라서 자동으로 생성되는 전역공간과 eval을 제외하면, <strong>실행 컨텍스트가 생성되는 시점은 곧 함수를 실행하는 시점이다.</strong></p>
<h1 id="3-실행-컨텍스트의-종류">3. 실행 컨텍스트의 종류</h1>
<p>앞서 실행 컨텍스트를 만들 수 있는 방법으로 전역공간, 함수, eval()이 있다고 했다.
따라서 실행 컨텍스트도 3가지로 나뉜다.</p>
<ul>
<li><p>전역 실행 컨텍스트: 전역 영역에 존재하는 코드.
모든 스크립트 코드는 전역 실행 컨텍스트 안에서 실행된다.
프로그램에 단 한 개만 존재하며 실행 컨텍스트의 기본이다. 함수 밖에 있는 코드는 전역 실행 컨텍스트에 있다.
브라우저의 경우에는 <code>window</code>객체, Node.js의 경우엔 <code>global</code>객체가 곧 전역 실행 컨텍스트가 된다.</p>
</li>
<li><p>함수 실행 컨텍스트: 함수 내에 존재하는 코드.
함수가 실행될 때마다 만들어지는 실행 컨텍스트이다.
각 함수는 고유의 실행 컨텍스트를 가지며, 함수가 실행되거나 call 될 때에만 생성된다.</p>
</li>
<li><p>eval() 실행 컨텍스트: eval 함수로 실행되는 코드.
이제 쓰지 않는 eval() 함수에 의해 생성되는 실행 컨텍스트이다.</p>
</li>
</ul>
<h1 id="4-실행-컨텍스트의-구조">4. 실행 컨텍스트의 구조</h1>
<p>실행 컨텍스트는 어떻게 이루어져 있을까?
실행 컨텍스트는 <code>LexicalEnvironment</code> 컴포넌트와 <code>variableEnvironment</code> 컴포넌트로 구성된다.
아래의 그림을 참고하자. </p>
<p><img src="https://images.velog.io/images/edie_ko/post/8a602077-2ef6-40f3-87ef-11bd54a53ec5/execution-context.jpg" alt=""></p>
<p>함수가 호출되면 전역 공간에 있던 코드의 제어권이 함수의 내부로 이동하면서 함수 코드를 평가하기 시작한다. 함수 코드 평가는 아래 순서대로 진행된다.</p>
<blockquote>
<p>1) 함수 실행 컨텍스트 생성 
2) 함수 LexicalEnvironment 생성 
  2-1) 함수 environmentRecord 생성
  2-2) ThisBinding
  2-3) outerEnvironmentReference 결정</p>
</blockquote>
<p>더 자세하게 각각의 요소들을 알아보자. </p>
<ul>
<li><p><code>variableEnvironment</code>: 현재 컨텍스트 내의 <strong>식별자들에 대한 정보 + 외부 환경 정보</strong>를 담는다. 
선언 시점의 <code>LexicalEnvironment</code>의 <strong>스냅샵</strong>으로 변경 사항은 반영되지 않는다.
실행 컨텍스트를 생성할 때 <code>variableEnvironment</code>에 정보를 먼저 담은 다음,
이를 복사해서 <code>LexicalEnvironment</code>를 만든다.</p>
<ul>
<li><code>environment record</code> (snapshot)</li>
<li><code>outer environment reference</code> (snapshot)</li>
</ul>
</li>
<li><p><code>LexicalEnvironment(어휘적 환경)</code>: 처음에는 <code>variableEnvironment</code>와 같지만 변경 사항이
실시간으로 반영된다.</p>
<ul>
<li><code>environmentRecord(환경 레코드)</code>: 함수 안의 코드가 실행되기 전에 현재 컨텍스트와 관련된 코드의 식별자 정보가 저장된다. (매개변수의 이름, 함수 선언, 변수명 등)
즉 코드가 실행되기 전에 자바스크립트 엔진은 이미 해당 환경에 속한 코드의 변수명 등을 모두 알고 있게 된다. (호이스팅)</li>
<li><code>outerEnvironmentReference(외부 렉시컬 환경에 대한 참조)</code>: 상위 스코프를 가리킨다. 즉 현재 <code>environment record</code>보다 바깥에 있는 <code>environment record</code>를 참고한다는 뜻이며 해당 실행 컨텍스트를 생성한 함수의 바깥 환경을 가리킨다.
따라서 자바스크립트 엔진이 현재 렉시컬 환경에서 변수를 찾을 수 없다면 외부 환경에서 찾는다는 것을 뜻한다.
만약 상위 스코프에서도 해당 식별자를 찾을 수 없다면 참조 에러(uncaught reference error)를 발생시킨다.</li>
</ul>
</li>
<li><p><code>ThisBinding</code>: <code>this</code> 식별자가 바라봐야 할 대상 객체. 실행 컨텍스트가 활성될 때 this가 지정되지 않은 경우 this에는 전역 객체가 저장된다. </p>
<blockquote>
<p>🏆 <strong>LexicalEnvironment 렉시컬 환경 (어휘적 환경)</strong> <br />
렉시컬 환경은 식별자와 식별자에 바인딩된 값, 그리고 상위 스코프에 대한 참조를 기록하는 자료구조로 실행 컨텍스트를 구성하는 컴포넌트이다. <strong>실행 컨텍스트 스택이 코드의 실행 순서를 관리</strong>한다면 <strong>렉시컬 환경은 스코프와 식별자를 관리</strong>한다. 렉시컬 환경은 키와 값을 갖는 객체 형태의 스코프를 생성해서, 식별자를 키로 등록하고 식별자에 바인딩된 값을 관리한다.</p>
</blockquote>
</li>
</ul>
<h1 id="5-실행-컨텍스트의-생성과-작동-과정">5. 실행 컨텍스트의 생성과 작동 과정</h1>
<p>이번에는 아래의 코드를 보며 실행 컨텍스트의 생성과 식별자 검색 과정을 알아보자.</p>
<pre><code class="language-js">const str = &#39;안녕&#39;;

function outer() {
  function inner() {
    const greeting = &#39;하이&#39;;
    console.log(greeting);
    console.log(str);
  }
  inner();
}
outer();

console.log(str);</code></pre>
<blockquote>
<ol>
<li>시작: 전역 컨텍스트가 생성된다. 전역 컨텍스트의 <code>environmentRecord</code>에 <code>{ str, outer }</code> 식별자를 저장한다. 전역 컨텍스트는 가장 최상위 컨텍스트이므로 <code>outerEnvironmentReference</code>는 <code>null</code>이다. (this: 전역객체)</li>
<li>1, 3번째 줄: 전역 스코프에 있는 변수 <code>str</code>에 &#39;안녕&#39;을, <code>outer</code>에 함수를 할당한다.</li>
<li>10번째 줄: <code>outer</code> 함수를 호출한다. 전역 컨텍스트의 코드는 10번째 줄에서 잠시 중단되고, <code>outer</code> 실행 컨텍스트가 활성화되어 3번째 줄로 이동한다.</li>
<li>3번째 줄: <code>outer</code> 실행 컨텍스트의 <code>environmentRecord</code>에 <code>{ inner }</code> 식별자를 저장한다.
<code>outerEnvironmentReference</code>에는 <code>outer</code> 함수가 선언될 당시의 <code>LexicalEnvironment</code>가 담긴다(<code>===전역 컨텍스트의 LexicalEnvironment</code>).
이를 <code>{ GLOBAL, { a, outer }}</code>라고 표기하자. 첫 번째는 실행 컨텍스트의 이름, 두 번째는 <code>environmentRecord</code> 객체다. (this: 전역객체)</li>
<li>4번째 줄: <code>outer</code> 스코프에 있는 변수 <code>inner</code>에 함수를 할당한다.</li>
<li>8번째 줄: <code>inner</code> 함수를 호출한다. 여기서 <code>outer</code> 실행 컨텍스트의 코드는 임시 중단되고, <code>inner</code> 실행 컨텍스트가 활성화되어 4번째 줄로 이동한다.</li>
<li>5번째 줄: <code>inner</code> 실행 컨텍스트의 <code>environmentRecord</code>에 <code>{ greeting }</code> 식별자를 저장한다.
<code>outerEnvironmentReference</code>엔 <code>inner</code> 함수가 선언될 당시의 <code>LexicalEnvironment</code>가 담긴다. <code>inner</code>함수는 <code>outer</code>함수에서 선언되었으므로
<code>outer</code> 함수의 <code>LexicalEnvironment</code>, 즉 <code>{ outer, { inner }}</code>를 참조복사한다. (this: 전역객체)</li>
<li>6번째 줄: <code>environmentRecord</code>에 있는 <code>greeting</code>을 찾아서 실행한다.</li>
<li>7번째 줄: 식별자 <code>str</code>에 접근하려고 한다. 이때 자바스크립트 엔진은 활성화된 실행 컨텍스트의 <code>LexicalEnvironment</code>에 접근한다.
첫 요소의 <code>environmentRecord</code>에서 <code>str</code>이 있는지 찾아보고, 없으면 <code>outerEnvironmentReference</code>에 있는 <code>environmentRecord</code>로 넘어가는 식으로 계속해서 검색한다.
여기서는 전역 <code>LexicalEnvironment</code>에 <code>str</code>이 있으므로 &#39;안녕&#39;을 출력한다.</li>
<li>8번째 줄: <code>inner</code> 함수 실행이 종료된다. <code>inner</code> 실행 컨텍스트가 콜 스택에서 제거되고, <code>outer</code> 실행 컨텍스트가 다시 활성화되면서 9번째 줄로 이동한다.</li>
<li>10번째 줄: <code>outer</code> 함수 실행이 종료된다. <code>outer</code> 실행 컨텍스트가 콜 스택에서 제거되고, 전역 컨텍스트가 다시 활성화 된다.</li>
<li>13번째 줄: 전역 컨텍스트의 <code>environmentRecord</code>에서 <code>str</code>을 검색해서 실행한다.</li>
<li>완료: 모든 코드의 실행이 종료되어 전역 컨텍스트가 콜 스택에서 제거되고 종료된다.</li>
</ol>
</blockquote>
<p>다음 편에서는
<code>this</code>에 대해 다뤄보도록 하겠다...</p>
<p>adios...🍷</p>
<h2 id="참고-자료">참고 자료</h2>
<ul>
<li><a href="https://262.ecma-international.org/10.0/#sec-lexical-environments">https://262.ecma-international.org/10.0/#sec-lexical-environments</a></li>
<li><a href="https://betterprogramming.pub/javascript-internals-execution-context-bdeee6986b3b">https://betterprogramming.pub/javascript-internals-execution-context-bdeee6986b3b</a></li>
<li>코어자바스크립트, 정재남</li>
<li>모던 자바스크립트 Deep Dive, 이웅모</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[setTimeout(foo, 0)에서 foo는 정말 0ms 후에 실행될까?]]></title>
            <link>https://velog.io/@edie_ko/javascript-eventloop</link>
            <guid>https://velog.io/@edie_ko/javascript-eventloop</guid>
            <pubDate>Thu, 28 Oct 2021 18:13:31 GMT</pubDate>
            <description><![CDATA[<h3 id="들어가기-전에">들어가기 전에..</h3>
<p>다음과 같은 코드가 있다고 하자.
콘솔에는 어떻게 나타날까?
<img src="https://images.velog.io/images/edie_ko/post/9bb2ceb7-d872-4323-a963-8adaf6267933/setTimeout.png" alt=""></p>
<p>답은 &#39;1 2 3 4 5 yes&#39;다. <br />
왜 console.log를 0초 안에 뱉어내라고 <code>setTimeout()</code> 함수를 넣었는데도 불구하고 &#39;yes&#39;가 가장 늦게 콘솔에 찍힌 것일까?
이번 글에서는 자바스크립트가 비동기 처리를 하는 방식에 대해 알아보고 이벤트루프에 대해 정리해보고자 한다.</p>
<h1 id="1-자바스크립트가-비동기-처리를-하는-방식">1. 자바스크립트가 비동기 처리를 하는 방식</h1>
<h2 id="1-왜-비동기-처리를-해야하나">1) 왜 비동기 처리를 해야하나</h2>
<p>자바스크립트는 싱글스레드 언어다. <br />하나의 싱글 콜 스택만을 가지고 있다는 뜻이며, 자바스크립트의 엔진은 한 번에 하나의 태스크만 실행할 수 있다는 뜻이다. <br />
자바스크립트의 함수를 호출하면 함수 실행 컨택스트가 생성된다. 이때 생성된 함수 실행 컨텍스트는 콜 스택에 푸시되고 함수 코드가 실행된다. <br />
그리고 함수 코드의 실행이 끝나면 실행 컨텍스트 스택에서 팝되어 제거되는 방식이다.
이 과정은 스택 자료구조 기반으로 이뤄진 콜스택에 의해 동기적으로 이뤄진다.</p>
<pre><code class="language-js">const sleep = (func, delay) =&gt; {
  const delayUntil = Date.now() + delay;
  while (Date.now() &lt; delayUntil);
  func();
};

const foo = () =&gt; console.log(&#39;foo&#39;);
const bar = () =&gt; console.log(&#39;bar&#39;);

sleep(foo, 10 * 1000);
bar();</code></pre>
<p>위의 <code>sleep()</code> 함수는 처리되는 데 10초가 걸린다. 그 다음 <code>bar()</code>함수는 10초 뒤에 동기적으로 실행된다. <br />
흔히 브라우저가 리페인트되고 렌더링될 때 1초에 60프레임을 리페인트 하는 게 이상적이고,
적당한 LCP(Largest contentful Paint) 시간이 2.5초라고 하는데 이런 블로킹이 생기면 사용자는 이 사이트가 느리다고 생각할 것이다. <br />
그 10초 동안의 <code>블로킹(작업 중단)</code>을 어떻게 막을 수 있을까?
(cf. 느린 동작이 스택에 남아있는 것을 보통 블로킹이라고 말한다.) <br />
이때 <u>비동기 처리</u>를 해주면 된다.</p>
<pre><code class="language-js">const foo = () =&gt; console.log(&#39;foo&#39;);
const bar = () =&gt; console.log(&#39;bar&#39;);

setTimeout(foo, 10 * 1000);
bar();</code></pre>
<p>콘솔에는 &#39;bar&#39;가 먼저 표시되고, 10초 뒤 &#39;foo&#39;가 나타난다.
이처럼 현재 실행 중인 태스크가 종료되지 않은 상태라 해도 다음 태스크를 곧바로 실행하는 방식이 비동기 처리다.</p>
<p>따라서 실행이 오래 걸리는 태스크를 처리해야할 때 발생하는 블로킹을 막고, 싱글스레드 언어인 자바스크립트의 동기적인 실행 컨택스트 스택을 벗어나 효율적으로 스케줄링하기 위해 비동기 처리를 사용한다.
<code>setTimeout()</code>, <code>setInterval()</code>, <code>HTTP request</code>, <code>DOM Event</code>는 모두 비동기 처리 방식으로 동작한다.
즉, API를 통해 Data를 받아오는 과정, Data를 서버에 업로드하는 과정, HTML 요소로 만든 애니메이션 효과 등은 모두 비동기적으로 처리되는 것이다.</p>
<blockquote>
<p> 💡 <strong>console.log도 비동기다.</strong> <br />
  console.* 메서드는 공식적으로 자바스크립트의 일부분이 아니다. 정확히는 호스팅 환경에
  추가된 기능이다. 따라서 console.log 메소드는 브라우저의 유형과 상황에 따라 출력할
  데이터가 만들어진 직후에도 콘솔창에 바로 뜨지 않을 수도 있다. 브라우저가 console을
  비동기적으로 처리해야 성능상 유리하기 때문이다. 예상치 못한 결과값이 콘솔에 표시될
  때에는 콘솔의 실행 지연으로 인한 원인일 가능성도 염두에 두자.</p>
</blockquote>
<h2 id="2-그렇다면-비동기는-어떻게-동작할까">2) 그렇다면 비동기는 어떻게 동작할까?</h2>
<p>다시 한번 돌이켜보자. 자바스크립트 엔진은 싱글 스레드로 동작한다.
자바스크립트 엔진은 그저 요청하는 태스크를 순서대로 하나씩 처리하는 동기 처리 방식에 따라 움직일 뿐이다.
하지만 브라우저는 멀티 스레드로 동작한다. 그리고 자바스크립트 엔진은 반드시 호스팅(웹 브라우저, Node.js 서버) 환경에서 실행된다.</p>
<p>앞서 본 <code>setTimeout()</code> 예제 코드에서 foo와 bar 코드는 동시에 시작하는 것처럼 느껴진다.
우리가 흔히 보는 웹페이지에서도 이미지를 띄우면서 HTTP 요청으로 데이터를 전송하는 등 많은 것들이 동시에 일어나는 것처럼 보인다.
이렇게 여러 동작을 동시다발적으로 처리하는 <code>자바스크립트의 동시성</code>을 가능하게 하는 것. <code>동시성 처리 모델의 기본 원리</code>는 바로 <code>이벤트 루프</code>다.
따라서 비동기 처리를 위해서 비동기 코드의 평가와 실행은 자바스크립트 엔진이 담당하지만, 호출 스케줄링을 위한 함수의 등록은 호스팅 환경이 담당한다.
그리고 그 요청을 스케줄링하는 것이 <code>이벤트 루프</code>다.</p>
<h2 id="3-그렇다면-이벤트-루프는-어디에-있을까">3) 그렇다면 이벤트 루프는 어디에 있을까?</h2>
<p>자바스크립트의 엔진을 살펴보자.
자바스크립트 엔진은 2개의 영역으로 이루어져 있다. <code>자바스크립트 = 콜 스택 + 메모리 힙</code>이다.</p>
<h3 id="콜-스택">콜 스택</h3>
<ul>
<li>함수 등의 코드 평가 과정에서 생성된 실행 컨텍스트가 추가되고 제거되는 실행 컨텍스트 스택.</li>
<li>함수를 호출하면 함수 실행 컨텍스트가 순차적으로 콜 스택에 푸시되어 동기적으로 실행된다.</li>
</ul>
<h3 id="메모리-힙">메모리 힙</h3>
<ul>
<li>객체가 저장되는 메모리 공간. 콜 스택의 요소인 실행 컨텍스트는 힙에 저장된 객체를 참조한다.</li>
<li>메모리 힙에는 자바스크립트 코드에서 정의한 여러 객체와 값이 저장된다.</li>
<li>객체는 원시값과는 달리 크기가 정해져 있지 않기 때문에 할당해야 할 메모리 공간의 크기는 동적 할당 된다. 즉 객체가 저장되는 메모리 힙은 구조화되어 있지 않다.</li>
</ul>
<p>이게 전부다. 이벤트 루프는 자바스크립트 엔진에 있는 것이 아니다. (하지만.. ES6부터는 달라진다.. 3. Promise와 이벤트 루프에서 소개하겠다)
자바스크립트 엔진은 ECMAScript 스펙에 나와있는 표준에 따라 구현되었는데, ECMAScript에는 이벤트 루프에 대한 내용이 없다.
이벤트 루프는 자바스크립트 엔진을 둘러싼 <code>환경</code>에 있다.
바로 브라우저 혹은 Node.js 환경에서 태스크 큐와 이벤트 루프를 제공한다.</p>
<h3 id="태스크-큐">태스크 큐</h3>
<ul>
<li>setTimeout이나 setInterval 과 같은 비동기 함수의 콜백 함수 또는 이벤트 핸들러가 일시적으로 보관되는 영역.</li>
<li>태스크 큐에 일시적으로 보관된 함수들은 비동기 처리 방식으로 동작한다.</li>
</ul>
<h3 id="이벤트-루프">이벤트 루프</h3>
<ul>
<li>콜 스택에 현재 실행 중인 실행 컨텍스트가 있는지, 그리고 태스크 큐에 대기 중인 함수가 있는지 반복해서 확인한다.</li>
<li>만약 콜 스택이 비어 있고 태스크 큐에 대기 중인 함수가 있다면 이벤트 루프는 순차적으로 태스크 큐에 대기 중인 함수를 콜 스택으로 이동시킨다.</li>
<li>FIFO (First In First Out)</li>
</ul>
<p>아래 그림을 보면서 실제로 이벤트 루프가 어디에 있는지 찾아보자.</p>
<h3 id="브라우저-환경">브라우저 환경</h3>
<p><img src="https://images.velog.io/images/edie_ko/post/fdd494f7-48ad-4b87-b77f-1f645219a429/browser.png" alt=""></p>
<h3 id="nodejs-환경">Node.js 환경</h3>
<p><img src="https://images.velog.io/images/edie_ko/post/df84936b-0c36-44f3-b67d-9f6c205fbfb6/nodejs.jpg" alt=""></p>
<h2 id="4-비동기-처리-방식을-더-자세하게-풀어보자">4) 비동기 처리 방식을 더 자세하게 풀어보자</h2>
<p>간단한 예제를 들고왔다. (참고 - 모던자바스크립트 Deep Dive) <br />
아래의 함수는 어떻게 동작할까?
천천히 생각해보자.</p>
<pre><code class="language-js">const foo = () =&gt; console.log(&#39;foo&#39;);
const bar = () =&gt; console.log(&#39;bar&#39;);

setTimeout(foo, 0);
bar();</code></pre>
<blockquote>
<p>(📌1) 전역 코드가 평가되어 전역 실행 컨텍스트가 생성되고 콜 스택에 push된다.<br />
(📌2) 전역 코드가 실행되기 시작하며 setTimeout 함수가 호출된다. 이때 setTimeout 함수의 함수 실행 컨텍스트가 생성되고 콜스택에 푸시되어 현재 실행 중인 실행 컨텍스트가 된다.
브라우저의 Web API인 타이머 함수도 함수이므로 함수 실행 컨텍스트를 생성한다.<br />
(📌3) setTimeout 함수가 실행되면 <code>콜백 함수</code>를 호출 스케줄링하고 종료되어 콜 스택에서 pop된다.
이때 호출 스케줄링, 즉 타이머 설정과 타이머가 만료되면 콜백 함수를 태스크 큐에 푸시하는 것은 <code>브라우저의 역할</code>이다.<br />
(📌4) 브라우저가 수행하는 4-1과 자바스크립트 엔진이 수행하는 4-2는 병렬로 처리된다.<br />
(📌4-1) 브라우저는 타이머를 설정하고 타이머의 만료를 기다린다.
이후 타이머가 만료되면 콜백 함수 foo가 태스크 큐에 푸시된다. 위 예제의 경우 지연 시간이 0이지만, 지연 시간이 4ms 이하인 경우(eg.크롬 브라우저) 최소 지연 시간 4ms가 지정된다. 
따라서 4ms 후에 콜백 함수 foo가 태스크 큐에 푸시되어 대기하게 된다. 이 처리 또한 자바스크립트 엔진이 아니라 브라우저가 수행한다.
이처럼 setTimeout 함수로 호출 스케줄링한 콜백 함수는 정확히 지연 시간 후에 호출된다는 보장은 없다.
지연 시간 이후에 콜백 함수가 태스크 큐에 푸시되어 대기하게 되지만 콜 스택이 비어야 호출되므로 약간의 시간차가 발생할 수 있기 때문이다.<br />
(📌4-2) bar 함수가 호출되어 bar 함수의 함수 실행 컨택스트가 생성되고 콜 스택에 푸시되어 현재 실행 중인 실행 컨택스트가 된다.
이후 bar 함수가 종료되어 콜 스택에서 pop된다. 이때 브라우저가 타이머를 설정한 후 4ms가 경과했다면 foo 함수는 아직 태스크 큐에서 대기 중이다.<br />
(📌5) 전역 코드 실행이 종료되고 전역 실행 콘텍스트가 콜 스택에서 팝된다. 이로서 콜 스택에는 아무런 실행 컨텍스트도 존재하지 않게 된다.<br />
(📌6) 이벤트 루프에 의해 콜 스택이 비어 있음이 감지되고 태스크 큐에서 대기 중인 콜백 함수 foo가 이벤트 루프에 의해 콜 스택에 push된다. 즉 콜백 함수 foo의 함수 실행 컨텍스트가 생성되고 콜 스택에 푸시되어 현재 실행 중인 실행 컨텍스트가 된다.
이후 foo 함수가 종료되어 콜 스택에서 pop된다.<br /></p>
</blockquote>
<p>setTimeout(foo, 0)이 바로 실행되지 않는 이유를 이젠 이해할 수 있다! <br />
그래도 이해가 되지 않는다면...
그 유명한 <a href="https://youtu.be/8aGhZQkoFbQ">Philip Roberts의 What the heck is the event loop</a> 영상을 보고 오자!
(<a href="https://vimeo.com/96425312">이 버전</a>도 있다)</p>
<h1 id="2-이벤트-루프는-어떤-형태일까">2. 이벤트 루프는 어떤 형태일까?</h1>
<p>앞서 본 예제에서 setTimeout의 콜백 함수는 태스크 큐에 푸시되어 대기하다가 콜 스택이 비게 되면 비로소 콜 스택에 푸시되어 실행됐다.
이벤트 루프의 역할을 코드로 아주 간단하게 나타내어 보자면 다음과 같다.</p>
<pre><code class="language-js">const eventLoop = []; // 이벤트 루프는 FIFO 특성을 지닌다.
const event = null;

while (true) {
  // 콜 스택이 비어있는지 계속 확인한다.
  if (eventLoop.length &gt; 0) {
    event = eventLoop.shift(); // 먼저 들어온 것부터 스케줄링 한다.
    try {
      event(); // 이벤트 실행!
    } catch (err) {
      reportError(err);
    }
  }
}</code></pre>
<p>MDN에서는 이벤트 루프를 아래와 같이 표현한다.</p>
<pre><code class="language-js">while (queue.waitForMessage()) {
  queue.processNextMessage();
}</code></pre>
<p>이벤트 루프는 매번 순회하면서 콜 스택이 깨끗한지 체크한다. 이걸 Tick이라고 한다.
틱이 발생할 때마다 큐에 쌓여있는 이벤트(콜백 함수)를 꺼내어 실행한다.
setTimeout()과 같은 함수는 타이머만 설정할 뿐, 타이머가 끝나면 환경이 콜백을 이벤트 루프에 삽입한 뒤 틱에서 콜백을 꺼내어 실행하는 것이다.
setTimeout()에 인자로 넘긴 지연 시간이 지켜지지 않는 이유가 여기에 있다.
<code>setTimeout(()=&gt;console.log, 0)</code>에서 0은 <strong>보장된 시간이 아니라 요청을 처리하기 위해 필요한 최소의 시간</strong>이다.
이벤트 루프는 &#39;현재 실행중인 태스크가 없는지&#39;, &#39;태스크 큐에 태스크가 있는지&#39; 확인하며 매번 Tick하면서 기회를 엿보고 있을 것이다.</p>
<h1 id="3-promise와-이벤트-루프">3. Promise와 이벤트 루프</h1>
<p>자.. 이번엔 ES6부터 추가된 Promise를 살펴보자.
이제 우린 <code>setTimeout(foo, 0)</code> 같은 것들이 바로 실행되지 않는 이유를 알게되었다.
하지만 Promise가 들어간 코드는 어떻게 실행될까?
다음 코드의 콘솔들이 어떤 순서로 찍힐지 한번 예상해보자.</p>
<pre><code class="language-js">console.log(&#39;script start&#39;);

setTimeout(function() {
  console.log(&#39;setTimeout&#39;);
}, 0);

Promise.resolve()
  .then(function() {
    console.log(&#39;promise1&#39;);
  })
  .then(function() {
    console.log(&#39;promise2&#39;);
  });

console.log(&#39;script end&#39;);</code></pre>
<p>답은 아래와 같다.</p>
<pre><code>script start
script end
promise1
promise2
setTimeout</code></pre><p>왜 setTimeout()의 콜백이 Promise 콜백보다 느리게 동작한 것일까?</p>
<h3 id="es6-microtask-queue">ES6 microtask queue</h3>
<p>Microtask queue(혹은 Job queue)는 ES6에서 Promise와 함께 소개된 개념이다.
마이크로태스크 큐는 태스크 큐와는 다른 별도의 큐다. 마이크로태스크 큐를 사용하는 대표적인 함수가 Promise다.
기존의 태스크 큐 = 매크로태스크(Macrotask) 큐라고 한다.</p>
<blockquote>
<p> 💡 <b>Macrotask queue를 이용하는 함수</b>
  : setTimeout(), setInterval(), setImmediate(), requestAnimationFrame, I/O, UI
  렌더링 <br />
  💡 <b>Microtask queue를 이용하는 함수</b>
  process.nextTick(), Promise, queueMicrotask</p>
</blockquote>
<p>마이크로태스크 큐는 기존의 태스큐와 비교해서 보다 우선순위가 높다. 
따라서, 이벤트 루프는 콜 스택이 비면 먼저 마이크로태스크 큐에서 대기하고 있는 함수를 가져와서 실행한다.
그리고 마이크로태스크 큐가 빈 후에야 태스크 큐에서 대기하고 있는 함수를 가져와서 실행한다. <br />
아래 이미지를 보며 이해해보자.
  <img
    src='https://res.cloudinary.com/practicaldev/image/fetch/s--05Fi8vBq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/42eatw03fcha0e1qcrf0.gif'
    title='microtask-queue'
    alt='microtask-queue'
  /><a
    href='https://dev.to/lydiahallie/javascript-visualized-promises-async-await-5gke'
    target='_blank'
    rel='noopener'>
    (이미지 출처에 다양한 사진들이 많다.)
  </a></p>
<p>따라서 정리해보자면,</p>
<ul>
<li>Macrotask queue (= task queue, message queue)<ul>
<li>HTML 파싱, DOM 생성, 메인 스레드를 구성하는 JS code, 그리고 페이지 로드나 네트워크 이벤트, 타이머와 같은 여러 이벤트를 포함한다.</li>
<li>지연 시간이 0초인 <code>setimeout()</code>으로 새로운 매크로태스크를 스케줄링 할 수 있다</li>
</ul>
</li>
<li>Microtask queue (= job queue)<ul>
<li>Promise의 후속 처리 메서드의 콜백 함수를 처리한다.</li>
<li>다른 이벤트 핸들링이나 렌더링 혹은 또 다른 매크로태스크가 실행되기 전에 완료된다.</li>
<li>마이크로태스크는 바로 다음 마이크로태스크를 실행한다. 따라서 그 사이에는 UI 혹은 네트워크 변화가 없다. </li>
<li>브라우저가 리렌더 되기 전에 실행되므로, 마이크로태스크 큐의 작업이 늦게 처리된다면 브라우저의 UI 렌더링이 지연될 수 있다.</li>
</ul>
</li>
</ul>
<h1 id="4-마무리-이벤트-루프를-막지-말자">4. 마무리: 이벤트 루프를 막지 말자</h1>
<blockquote>
<p>Think about async.
Don&#39;t block the event loop.
-Philip Roberts-</p>
</blockquote>
<p>우선 event loop를 막지 않아야 한다.
스택에 필요없는 느린 코드를 쌓아서 브라우저가 할 일을 못하게 하지 말아야 한다.
예를 들어 콜스택에서 어떤 함수가 너무 오랫동안 실행되고 있으면 이벤트루프가 메시지큐를 확인하지 않는다.
그러면 함수의 동작이 길어져서 사용자가 화면을 클릭하더라도 이벤트가 발생하지 않고, 화면이 버벅이거나 심한 경우 동작하지 않는 문제가 발생한다.
따라서 함수의 단위는 작게 잘라서 작성해서 작성하는 것이 좋다.</p>
<p>또 너무 오래 걸리는 작업이 있다면 앞서 배운 <code>setTimeout(callback, 0)</code>과 같은 문법으로 지연시키는 방법도 있다.
태스크 큐로 callback을 넘겨주면서 적절하게 태스크를 분산시키는 것이다.</p>
<p>특히, 이미지 처리나 애니메이션이 너무 잦아졌을 때 큐 관리에 주의를 기울어야 한다.
이 경우 싱글 스레드인 자바스크립트의 단점을 보완해서 멀티스레딩을 가능하게 해주는 웹 워커 API를 활용하는 것도 방법일 수 있겠다.</p>
<p><br /><br /></p>
<h3 id="참고-자료">참고 자료</h3>
<ul>
<li>NHNCloud | 자바스크립트와 이벤트 루프 <a href="https://meetup.toast.com/posts/">https://meetup.toast.com/posts/</a></li>
<li>MDN | 동시성 모델과 이벤트 루프 <a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/EventLoop">https://developer.mozilla.org/ko/docs/Web/JavaScript/EventLoop</a></li>
<li>이벤트 루프와 매크로·마이크로태스크 <a href="https://ko.javascript.info/event-loop">https://ko.javascript.info/event-loop</a></li>
<li>Tasks, microtasks, queues and schedules <a href="https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/">https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/</a></li>
<li>JavaScript Visualized: Promises &amp; Async/Await <a href="https://dev.to/lydiahallie/javascript-visualized-promises-async-await-5gke">https://dev.to/lydiahallie/javascript-visualized-promises-async-await-5gke</a></li>
<li><a href="https://medium.com/official-podo/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A3%A8%ED%94%84-%EB%A7%88%EC%9D%B4%ED%81%AC%EB%A1%9C%ED%83%9C%EC%8A%A4%ED%81%AC-microtasks-%EC%99%80-%EB%A7%A4%ED%81%AC%EB%A1%9C%ED%83%9C%EC%8A%A4%ED%81%AC-macrotasks-4563cdc324b0">자바스크립트 이벤트 루프: 마이크로태스크(Microtasks)와 매크로태스크(Macrotasks)</a></li>
<li>이웅모, &lt;모던 자바스크립트 Deep Dive&gt;</li>
<li>카일 심슨, &lt;You Don&#39;t know JavaScript&gt;</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[라이트하우스와 함께한 성능 개선 고군분투기]]></title>
            <link>https://velog.io/@edie_ko/lighthouse-performance</link>
            <guid>https://velog.io/@edie_ko/lighthouse-performance</guid>
            <pubDate>Sun, 24 Oct 2021 14:59:52 GMT</pubDate>
            <description><![CDATA[<p>네 정말 아무것도 몰랐습니다.
제가 신입 개발자로 커리어를 시작하면서 피땀눈물로 만든 지도 사이트의 성능이 .. 이렇게 좋지 않을 줄은 저도 몰랐어요. ㅇ&lt;-&lt; ...<br />
정말 아~무것도 몰랐던 신입 개발자가 라이트하우스로 퍼포먼스 점수 30점을 올렸던 경험담을 소개합니다.
참고로 라이트하우스 점수는 4가지 기준으로 산정됩니다. <code>Performance</code>, <code>Accessibility</code>, <code>Best Practice</code>, <code>SEO</code>.
이 글은 <code>Performance</code> 개선 과정에 대해서만 다루도록 하겠습니다.</p>
<h1 id="1-라이트하우스를-도입하게-된-이유">1. 라이트하우스를 도입하게 된 이유</h1>
<p>올해 2월부터 만들기 시작한 지도 서비스를 6월에 정식으로 런칭하고서 숨가쁘게 달려온 4개월을 반추하는데,
스스로 자랑스럽다기보다는 이제까지 그냥 &#39;동작&#39;하는 코드를 작성했을 뿐이라는 불안감이 가득했습니다. <br />
사수 없이 혼자서 프론트엔드를 도맡아 작성한 이 코드가 &#39;옳은 것&#39;이었을지, 잘못되었다면 어떤 부분일지 조언을 얻을 곳도 마땅치 않았어요.<br />
더불어 저에겐 <code>&#39;더 빠르고 성능이 좋은&#39; 서비스에 대한 욕심</code>이 있었습니다. 더 나은 사용자 경험을 제공하고 싶었어요.<br />
그리고 앞으로 계속 이 서비스를 꾸준히 개선하기 위해서는 <code>객관적으로 수치화된 목표가 절실</code>하다는 생각이 들었습니다.
이러한 이유로... 런칭 후 본격적으로 라이트하우스 점수를 이용하기 시작했습니다.</p>
<h1 id="2-현상-파악">2. 현상 파악</h1>
<p>런칭했을 때 라이트하우스 Performance 점수는 24점이었습니다. 하지만 점점 기능이 추가되고 코드가 쌓이면서 최저 16점까지 하락했습니다.
그래서 이 점수를 변곡점으로 삼아 성능개선에 힘을 쏟게 됩니다.
<img src="https://images.velog.io/images/edie_ko/post/cd295b6f-f350-4955-a458-31d76c89d976/%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-10-21%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%2012.42.03.png" alt=""></p>
<p>그런데 잠깐... 그럼 목표는 퍼포먼스 점수 100점을 만드는 것이었을까요?
저는 먼저 정확한 현상을 파악하고, 문제를 진단한 뒤에 목표를 세우고 싶었습니다.</p>
<p>먼저 목표를 정하기 전에 성능을 향상시켜야 하는 사이트의 특성을 정리했습니다.</p>
<p>제가 만든 사이트는 지도 기반의 서비스입니다.
지도는 마우스 드래그를 1mm만 움직이더라도 새로운 지역의 타일 이미지와, 지도 위에 띄울 오버레이 및 마커 데이터를 계산해 새롭게 로드합니다.</p>
<p>그래서 비슷한 기능을 제공하는 다른 사이트는 어떻게 운영하고 있을지 궁금했어요. 직접 레퍼런스를 얻기 위해 다른 지도 사이트는 라이트하우스가 몇 점인지 측정해보았습니다.</p>
<p>직방 39점, 마이프차 20점, 호갱노노 54점, 카카오맵 53점, 네이버지도 94점이었습니다.
(device Desktop / 21.10.20 기준 / 지도 중앙 위치 정자역 부근)<br />
이러한 사이트를 둘러보면서 &#39;유저의 액션에 빠르게 인터렉션하며 마커를 불러올 수 있도록 데이터 fetching을 관리하는 것이 핵심이겠다&#39;라는 생각이 들었습니다. 또한 거의 모든 사이트가 카카오 혹은 네이버 지도 라이브러리를 사용하기 때문에 카카오와 네이버를 제외한 지도사이트의 성능 지표는 한계가 있을 수 있겠다라는 생각도 들었어요.</p>
<p>주의사항이 있습니다.
참고로 위의 점수는 많은 변수에 의해 영향을 받습니다. 사용자의 컴퓨터 성능 혹은 CPU 작업량, 지도의 위치에 따라 로드하는 데이터의 양.. <br />
특히 카카오맵과 네이버지도의 경우, 다른 사이트와는 달리 마커 데이터를 먼저 불러오지 않습니다. 검색을 해야 검색 쿼리에 해당하는 마커 데이터를 불러오는 방식입니다.
따라서 검사할 때 이를 유의해서 체크해야하며, 최대한 여러번 라이트하우스를 실행하여 오차범위를 체크하는 것이 좋습니다.</p>
<p>라이트하우스에서는 다음과 같은 요소로도 점수가 변동할 수 있다고 설명합니다.</p>
<ul>
<li>A/B tests or changes in ads being served</li>
<li>Internet traffic routing changes</li>
<li>Testing on different devices, such as a high-performance desktop and a low-performance laptop</li>
<li>Browser extensions that inject JavaScript and add/modify network requests</li>
<li>Antivirus software</li>
</ul>
<h1 id="3-주요-과정">3. 주요 과정</h1>
<h3 id="1-data-fetching-방식-변경">1) data fetching 방식 변경</h3>
<blockquote>
<p>웹 사이트의 성능 = 시간 + 리소스</p>
</blockquote>
<p>웹 사이트의 성능은 시간과 리소스를 기준으로 평가할 수 있습니다. 적은 리소스를 사용하면서도 빠른 시간 내에 실행된다면 보다 좋은 성능이라고 말할 수 있겠죠..
제가 성능 개선을 위해 먼저 했던 일은 사이트가 이용하는 리소스를 최대한으로 줄이는 것이었습니다.
당시 렌더링에 따른 불필요한 API 콜이 발생하고 있었는데, useCallback 혹은 debounce로 이벤트를 줄여도 최소 1번의 중복 요청이 발생했습니다.
이를 해결하기 위해 필요한 요청만 할 수 있도록 Redux Saga의 <a href="https://redux-saga.js.org/docs/api/#takelatestpattern-saga-args">takeLatest</a> 메소드 혹은 <a href="https://react-query.tanstack.com/">React Query</a> 도입 중에서 고민하던 중,
최종적으로 React Query를 도입하기로 결정했습니다. <br />
그 이유는 1) 그동안 사용하던 리덕스의 의존도를 낮추고 싶었고 2) 리액트 쿼리의 캐싱 기능으로 기존 리덕스에서 주기적으로 데이터를 리패치해주어야 하는 불편함을 줄일 수 있다고 판단했기 때문입니다.
이를 도입하면서 데이터 캐싱으로 서버 데이터를 중복 호출하지 않으면서도 setState의 횟수를 줄였고, dispatch 사용을 줄이며 Redux 의존도를 낮출 수 있었습니다.
또한 <a href="https://web.dev/tbt/">Total Blocking Time</a>을 530ms에서 140ms로 줄이면서 유저의 action에 더 빠르게 반응할 수 있도록 개선할 수 있었습니다.</p>
<blockquote>
<p>💡 <strong>TBT란?</strong> <br />
  FCP(First Contentful Paint)와 TTI(Time to Interactive) 사이 총 시간을 의미합니다.
  메인 스레드에서 실행 시간이 긴 작업(50ms 이상 실행되는 작업)이 있을 때마다 메인 스레드는 Blocking된 것으로 간주되는데, 브라우저가 진행 중인 작업을 중단할 수 없기 때문입니다.
 <img src="https://images.velog.io/images/edie_ko/post/d2267629-87df-4211-902c-b866b78e375c/%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-10-24%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%203.30.15.png" alt="">
  따라서 이 Blocking된 시간 동안 발생한 사용자의 상호 작용은 해당 작업이 끝날 때까지 기다려야 응답할 수 있습니다.
   <a href="https://web.dev/tbt/">(이미지 출처: web dev)</a></p>
</blockquote>
<p>참고로 <a href="https://googlechrome.github.io/lighthouse/scorecalc/#FCP=3000&amp;SI=5800&amp;FMP=4000&amp;TTI=7300&amp;FCI=6500&amp;LCP=4000&amp;TBT=600&amp;CLS=0.26&amp;device=desktop&amp;version=8&amp;version=6&amp;version=5">Lighthouse Scoring Calculator</a>에 따르면,
Lighthouse 버전 8의 performance 수치에서 TBT(Total Blocking Time)의 비중은 30%나 차지합니다.</p>
<p><img src="https://images.velog.io/images/edie_ko/post/fb16c38c-ea0b-4d3e-823e-0211b2c6768e/%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-10-24%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%203.35.24.png" alt=""></p>
<p>이 가중치는 라이트하우스 버전 6 때보다 5%나 더 높아진 수치인데요,
라이트하우스에서는 사용자 기준의 성능 측정을 위해 주기적으로 리서치를 진행해서 user-perceived한 퍼포먼스를 측정하기 위해 가중치를 변경하고 있다고 합니다.</p>
<h3 id="2-코드-스플리팅">2) 코드 스플리팅</h3>
<p>그 다음으로 주요했던 방법은 코드 스플리팅이었습니다.
코드 스플리팅이란 페이지를 로딩하는 초기부터 미리 로딩할 필요가 없는 코드를 나누어서 빼놓고, 사용 시점이 되었을 때 리소스를 불러올 수 있도록 하는 기법입니다.</p>
<p><img src="https://images.velog.io/images/edie_ko/post/74331084-3ef1-4c8e-90d2-4c8ea200312e/code_splitting.jpg" alt=""></p>
<p>위 사진을 보면 이전 50%에 달하던 사용하지 않는 JavaScript 코드(좌측 상단)를 스플리팅하여 20%대로 줄였습니다(우측 하단).
먼저 코드 스플리팅을 위해서는 <code>bundle-analyer</code>로 코드의 구성을 확인해야 합니다.
저의 경우 eject하지 않은 cra 기반의 프로젝트이므로, <a href="https://github.com/svengau/cra-bundle-analyzer">cra-bundle-analyzer</a>를 사용했습니다.
리액트에서는 초기 렌더링에 사용되지 않는 컴포넌트를 추후에 사용될 때에 동적으로 불러올 수 있는 <a href="https://ko.reactjs.org/docs/react-api.html#reactlazy">React.lazy</a>메서드가 있습니다.
또한 이러한 Lazy 컴포넌트들은 <a href="https://ko.reactjs.org/docs/react-api.html#reactsuspense">Suspense</a>로 감싸서 로딩이 될 때, fallback 컴포넌트(eg. Loading 컴포넌트)가 보여지도록 처리할 수 있습니다.</p>
<pre><code class="language-jsx">import React, { Suspense } from &#39;react&#39;;

const OtherComponent = React.lazy(() =&gt; import(&#39;./OtherComponent&#39;));
const AnotherComponent = React.lazy(() =&gt; import(&#39;./AnotherComponent&#39;));

function MyComponent() {
  return (
    &lt;div&gt;
      &lt;Suspense fallback={&lt;div&gt;Loading...&lt;/div&gt;}&gt;
        &lt;section&gt;
          &lt;OtherComponent /&gt;
          &lt;AnotherComponent /&gt;
        &lt;/section&gt;
      &lt;/Suspense&gt;
    &lt;/div&gt;
  );
}</code></pre>
<p>특히 React의 경우 SPA의 특성으로 인해 코드 스플리팅은 필수라고 생각합니다. 다만 코드를 어떠한 기준으로 스플리팅할지는 코드스플리팅을 실제로 적용하고 로직의 실행을 확인하는 과정을 거쳐 신중하게 이뤄져야 합니다.
코드 분할은 모듈 혹은 페이지 기반으로 이뤄지는데, 이 사이트의 경우 페이지 별로 코드의 로직 분리가 명확히 이뤄질 수 있었기 때문에 페이지에 기반해 1차로 분할할 수 있었습니다.
결과적으로 초기 렌더링에 걸리는 시간(<a href="https://web.dev/i18n/ko/fcp/">First Contentful Paint</a>)을 4.7s에서 1.6s로 줄일 수 있었고, 이것만으로도 퍼포먼스 수치를 약 10점 가량 향상시킬 수 있었습니다.
추후 모듈 바탕으로도 추가 분할을 진행할 예정입니다.</p>
<blockquote>
<p> 💡 <strong>FCP란?</strong> <br />
  FCP(First Contentful Paint)는 사용자가 페이지를 로드했을 때부터 페이지 콘텐츠의 일부가 화면에 렌더링될 때까지의 시간입니다. 일반적으로 FCP가 1.8초 이내여야 우수한 사용자 경험을 제공하고 있다고 판단합니다.
    <img
      src='https://web-dev.imgix.net/image/admin/3UhlOxRc0j8Vc4DGd4dt.png?auto=format'
      title='fcp'
      alt='fcp'
    /> 위의 사진에서 FCP가 된 상태는 2번째 사진입니다. 모든 로딩이 완료된 5번째 사진은 <a href="https://web.dev/lcp/">LCP (Large Contentful Paint)</a>라고 합니다. (cf. 정확히 LCP는 페이지 내에서 가장 로드가 큰 콘텐츠를 불러온 시점을 뜻합니다..) <br />
  따라서 FCP는 사용자가 페이지에서 무언가가 진행되고 있다는 걸 인지하는 시점이고, LCP는 사용자가 해당 페이지를 사용할 수 있다고 인지하는 시점입니다.</p>
</blockquote>
<p>아래부터는 점수 상승 효과가 극적이지는 않았지만 1-3점 사이로 성능을 개선할 수 있었던 방법들을 소개합니다.</p>
<h3 id="3-사용하지-않는-번들-트리셰이킹">3) 사용하지 않는 번들 트리셰이킹</h3>
<p>사용하지 않는 코드를 제거하는 방식을 <code>트리셰이킹</code>이라고 합니다.</p>
<pre><code class="language-tsx">import body from &#39;main&#39;; // 전체 임포트
import { eye, nose, mouth } from &#39;main&#39;; // 사용하는 일부만 임포트</code></pre>
<p>따라서 Lodash를 모듈에서 실제로 사용하고 있는 코드만 One by one으로 임포트하는 방식으로 변경했습니다.</p>
<pre><code class="language-tsx">import _ from &#39;lodash&#39;; // Full import, the largest bundle size
import { map, isNil } from &#39;lodash&#39;; // Curly brackets import, as same as full import
import isNil from &#39;lodash-es/isNil&#39;; // Module import, the smallest bundle size</code></pre>
<p><a href="https://www.blazemeter.com/blog/the-correct-way-to-import-lodash-libraries-a-benchmark">이 글</a>을 참고해 수정하였습니다. <br />
아래 사진을 보면 1) lodash 전체를 임포트해서 사용한 경우 lodash 사이즈는 72.46KB입니다. curly brackets로 임포트 하더라도 결과는 같습니다. <br />
하지만 one-by-one으로 임포트한 경우 19.97KB로 작은 사이즈로 줄어든 것을 확인할 수 있습니다.</p>
<p><img src="https://images.velog.io/images/edie_ko/post/28d969b6-f23c-4305-a619-652fb0d00844/lodash.png" alt=""></p>
<p><code>lodash</code>가 아닌 <code>lodash-es</code>를 사용해야 한다는 글도 보았습니다<a href="https://ui.toast.com/weekly-pick/ko_20180716">(참고)</a>. <br />
하지만 오히려 lodash-es를 사용하는 것이 번들사이즈가 더 커지게 한다는 레퍼런스를 참고해서 사용하지 않았습니다.
추가로 <a href="https://github.com/lodash/babel-plugin-lodash">babel-plugin-lodash</a>와 <a href="https://github.com/lodash/lodash-webpack-plugin">lodash-webpack-plugin</a>를 사용하면 가장 효과적으로 번들 사이즈를 줄일 수 있습니다.</p>
<h3 id="4-image-lazy-loading">4) Image lazy loading</h3>
<p>브라우저에서 로드하는 리소스 중에서 높은 확률로 가장 큰 용량을 차지하는 건 &#39;이미지&#39;입니다.
브라우저는 대체로 다운로드할 파일의 갯수가 한 도메인에 6개 이상이면 다운로드가 지연됩니다. (HTTP 1.1 기준으로 도메인 마다 6개의 TCP 연결만 허용 <a href="https://docs.pushtechnology.com/cloud/latest/manual/html/designguide/solution/support/connection_limitations.html">참고</a>)
로드해야 할 이미지 파일이 6개를 초과할 경우, 6개까지는 다운로드 되고 6개 이상은 큐에 쌓인다는 뜻입니다. 따라서 바로 필요하지 않은 이미지의 로딩 시점을 뒤로 미루는 것 많으로도 FCP와 LCP를 크게 개선할 수 있었습니다.</p>
<p><img src="https://images.velog.io/images/edie_ko/post/7ad6f73a-fc8c-4695-9605-98cb99582c27/maximum_connections.png" alt=""></p>
<p>이미지에 lazy loading을 적용하는 방법은 다양합니다. 그 중 제가 사용했던 방법은 다음과 같습니다.</p>
<p>1) 먼저 이미지에 정확한 width와 height 부여하고 Skeleton UI 활용함으로써 불필요한 레이아웃 변경 (<a href="https://web.dev/cls/">Cumulative Layout Shift</a>)을 방지했습니다.
2) <code>loading=&#39;lazy&#39;</code>를 적극 활용하되, 해당 프로퍼티가 적용되지 않는 브라우저를 위해 Intersection observer를 활용하여 레이지 로딩할 수 있도록 보완했습니다.
3) 큰 용량의 파일인 경우 작은 용량의 placeholder image를 먼저 띄우고 큰 용량의 파일이 로드 완료 되었을 때 교체해주는 방식을 사용했습니다.</p>
<p>추가적으로 파일의 포맷을 바꾸는 방법도 있습니다.
WebP나 AVIF와 같은 포맷을 사용하면 평균적으로 30% 용량을 축소할 수 있지만 safari와 같은 브라우저에서는 지원되지 않습니다. 이럴 때에는 <code>&lt;picture&gt;</code>태그를 사용해서 WebP를 사용할 수 없는 브라우저일 때에 본래의 png 파일을 불러오게 하면서 점진적으로 향상되도록 처리해줄 수 있습니다.</p>
<pre><code class="language-html">&lt;picture&gt;
  &lt;source srcset=&quot;logo.webp&quot; type=&quot;image/webp&quot; /&gt;
  &lt;img src=&quot;logo.png&quot; alt=&quot;logo&quot;/&gt;
&lt;/picture&gt;</code></pre>
<h3 id="5-데이터-구조-변경">5) 데이터 구조 변경</h3>
<p> 지도를 움직일 때마다 생성되고 삭제되는 수백개의 마커 데이터를 백엔드에서 받아올 때 Array로 이뤄져 있었습니다. 그리고 매번 그 배열을 loop을 돌면서 마커들의 교집합, 차집합을 찾으며 수정되었습니다. 
 그 과정에서 불필요한 루프를 돌지 않도록 해시맵의 형태를 가진 Object로 데이터형을 변경해서 리팩토링했습니다.
 더 나아가 new Map()으로 최종 리팩토링을 거쳤는데, 그 이유는 Map은 잦은 key-value entries의 추가와 삭제가 발생할 때에는 object보다 더 나은 성능을 보이기 때문입니다.
 참고 - <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map">MDN</a>, <a href="https://betterprogramming.pub/stop-using-objects-as-hash-maps-in-javascript-9a272e85f6a8">Stop Using Objects as Hash Maps in JavaScript</a></p>
<h3 id="6-그-외">6) 그 외</h3>
<ul>
<li>더 빠른 계산 시간을 위해 forEach, Lodash 메소드를 사용하던 반복 방식을 for문으로 리팩토링했습니다. <a href="https://github.com/dg92/Performance-Analysis-JS">레퍼런스</a></li>
<li>otf, ttf와 더불어 woff2, woff를 사용한 폰트 파일을 추가했습니다. <a href="https://d2.naver.com/helloworld/4969726">레퍼런스</a></li>
<li>애니메이션 추가 시 JavaScript로 DOM에 접근하는 대신 CSS를 최대한 이용해서, 메인 스레드가 아닌 컴포지터 스레드를 최대한 사용하도록 했습니다.</li>
<li>애니메이션 사용 시 transform의 translate, rotate, scale, opacity 등을 이용하여 GPU 가속이 적용되도록 했습니다. </li>
<li>useMemo, useCallback을 적절히 사용하면서 재렌더링되는 컴포넌트를 제한했습니다.</li>
</ul>
<h1 id="4-결론">4. 결론</h1>
<blockquote>
</blockquote>
<ul>
<li>라이트하우스 퍼포먼스 점수가 높다 !== 좋은 코드를 썼구나..</li>
<li>라이트하우스 퍼포먼스 점수가 높다 !== 좋은 서비스를 제공하고 있구나..</li>
</ul>
<p>라이트하우스의 점수가 절대적으로 높다고 해서 좋은 코드를 썼다고 할 수는 없다고 생각합니다.
다만 사수의 도움을 받을 수 없는 환경에 놓인 신입 개발자, 혹은 어떤 요소들이 사이트의 성능에 영향을 미치는지 분석해보고 싶은 개발자, 나의 결과물을 객관적으로 수치화해서 목표를 갖고 싶은 개발자 분들이 사용한다면 굉장히 효율적인 학습 tool이 되지 않을까요.</p>
<p>매번 라이트하우스 점수를 체크하다보니, 처음에는 chrome devtool을 사용해 배포할 때마다 체크를 했습니다. 이제는 CI/CD로 배포 때마다 라이트하우스를 자동화 체크할 수 있도록 스크립트를 작성해서 확인하고 있습니다. (정확하지는 않아 devtool로 한번 더 확인합니다.)</p>
<blockquote>
<p>마틴 파울러 왈.. &quot;컴퓨터가 이해할 수 있는 코드는 바보라도 작성할 수 있다. 좋은 프로그래머는 사람이 이해할 수 있는 코드를 작성한다.&quot;</p>
</blockquote>
<p>최근엔 클린코드 스터디를 진행하면서 나와 같이 일하는 사람과 사용하는 사람을 위한 코드를 작성하기 위해 공부하고 있습니다. <br />
(저희 스터디에서 만들고 있는 <a href="https://scope-team.github.io/clean-code/">클린코드 book</a> 보러오세요! 책의 핵심을 요약해서 정리하고 있습니다. - 12월 5일까지 매주 일요일마다 업데이트 됩니다.)</p>
<p>앞으로도 꾸준히 성능을 개선해서 더 발전된 성능 개선기를 쓸 수 있도록 하겠습니다. 읽어주셔서 감사합니다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Error | 모바일 브라우저에서 100vh 적용 오류 해결 (ios/android)]]></title>
            <link>https://velog.io/@edie_ko/Tip-%EB%AA%A8%EB%B0%94%EC%9D%BC-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%EC%97%90%EC%84%9C-100vh-%EC%A0%81%EC%9A%A9-%EC%98%A4%EB%A5%98-%ED%95%B4%EA%B2%B0-iosandroid</link>
            <guid>https://velog.io/@edie_ko/Tip-%EB%AA%A8%EB%B0%94%EC%9D%BC-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%EC%97%90%EC%84%9C-100vh-%EC%A0%81%EC%9A%A9-%EC%98%A4%EB%A5%98-%ED%95%B4%EA%B2%B0-iosandroid</guid>
            <pubDate>Tue, 20 Jul 2021 13:59:52 GMT</pubDate>
            <description><![CDATA[<h1 id="문제-상황">문제 상황</h1>
<p>반응형 웹을 구축하던 도중, <code>100vh</code>가 PC 웹에서는 제대로 작용하나 모바일 웹에서 제대로 적용되지 않는 문제를 발견했습니다.</p>
<p>vh는 viewport height에 해당하는 단위로 해당 브라우저의 높이에 따라서 상대적으로 적용되는 단위입니다.
예를 들어, 화면의 높이가 100px이라면 1vh는 1px이 됩니다.</p>
<p>하지만 모바일에서는 상단에 위치한 url바와 하단에 있는 네이게이션바로 인해서 100vh를 설정하더라도 아래 혹은 윗 부분이 잘리는 현상이 발생합니다.
상하단에 위치한 바 아래로 화면이 감춰져버리는 것이죠..
<img src="https://images.velog.io/images/edie_ko/post/7e7e7dcc-61d2-43a8-997f-70b4133ee666/image.png" alt=""></p>
<h1 id="원인">원인</h1>
<p><img src="https://images.velog.io/images/edie_ko/post/e44ac5cd-8d7a-4320-ae8f-64e86883e39b/image.png" alt=""> <em>(image from <a href="https://css-tricks.com/the-trick-to-viewport-units-on-mobile/">The trick to viewport units on mobile</a>)</em></p>
<ul>
<li>url바는 상황에 따라 보여지거나 없어지는 경우가 있습니다. url바가 가변할 수 있는 데에 비해서, 100vh는 그에 따라 다시 측정되지 않습니다.</li>
<li>ios safari 브라우저의 경우, 상단의 노치와 url바 혹은 하단의 툴바로 인해 화면의 크기(viewport)를 실제 보여지는 윈도우 innerHeight보다 크게 잡습니다. 그래서 document의 height을 100vh로 잡아 작성하더라도 실제 safari 모바일 화면에서는 스크롤바가 생깁니다.</li>
</ul>
<h1 id="해결">해결</h1>
<h2 id="1-자바스크립트--css로-해결">1) 자바스크립트 + CSS로 해결</h2>
<p>먼저 <code>vh</code>를 선언해줍니다. 
리액트의 경우에는 useEffect로 화면이 처음 렌더링 되었을 때 <code>vh</code>를 <code>window.innerHeight*0.01</code> 에 맞추어 세팅되도록 선언해줍니다. 그리고 document의 스타일에 <code>--vh</code>라는 프로퍼티로 만들어 둔 <code>vh</code>를 값으로 넣어줍니다.</p>
<pre><code>let vh = 0;

useEffect(() =&gt; {
  vh = window.innerHeight * 0.01;
  document.documentElement.style.setProperty(&#39;--vh&#39;, `${vh}px`);
}, []);</code></pre><p>스타일은 아래와 같이 적용합니다.</p>
<pre><code>height: 100vh (❌😵)

height: calc(var(--vh, 1vh) * 100); (👌🏻✨)</code></pre><p>혹은 브라우저가 리사이즈될 여지가 있다면 다음과 같이 작성해도 됩니다.</p>
<pre><code>const setVh = () =&gt; {
  document.documentElement.style.setProperty(&#39;--vh&#39;, `${window.innerHeight}px`)
};
window.addEventListener(&#39;resize&#39;, setVh);
setVh();</code></pre><p>그리고 스타일은 아래처럼 작성합니다.</p>
<pre><code>:root {
   --vh: 100%;
}

html,
body {
    height: 100vh;
    height: var(--vh);
}</code></pre><p>아래 사진을 보면 좌측이 문제 상황, 우측이 해결된 상황입니다.
하단 네비게이션 바에 가려져서 보이지 않던 저장 버튼이 제대로 화면의 끝에 위치한 것을 볼 수 있습니다.
<img src="https://images.velog.io/images/edie_ko/post/2dc96f15-fd46-44eb-9e42-8da74b55cb83/image.png" alt=""></p>
<p>저는 JS와 CSS로 해결했지만, 아래와 같은 방법도 있다고 합니다.</p>
<h2 id="2-css로-해결">2) CSS로 해결</h2>
<p><img src="https://images.velog.io/images/edie_ko/post/49cecaa6-9e1f-4a50-bd81-26bdf29d2017/image.png" alt=""> <em>(image from <a href="https://dev.to/cydstumpel/4-things-in-web-development-i-ve-learned-in-2020-so-far-3cg">4 things in web development I&#39;ve learned in 2020, so far</a>)</em></p>
<p><code>-webkit-fill-available</code>이라는 프로퍼티로 해결하는 방법이 있습니다. 
<img src="https://images.velog.io/images/edie_ko/post/343edc57-df5d-4136-b66c-7ada601836e1/image.png" alt=""> <em>(image from <a href="https://twitter.com/allthingssmitty/status/1254151507412496384">https://twitter.com/allthingssmitty/status/1254151507412496384</a>)</em></p>
<p>적용할 대상에 다음과 같이 CSS를 작성합니다.</p>
<pre><code>min-height: 100vh;
min-height: -webkit-fill-available; </code></pre><p>혹은 아래와 같이 적용해봅니다.</p>
<pre><code>height: 100vh;
height: -webkit-fill-available;
height: fill-available;</code></pre><h3 id="reference">reference</h3>
<p><a href="https://css-tricks.com/the-trick-to-viewport-units-on-mobile/">The trick to viewport units on mobile</a>
<a href="https://medium.com/quick-code/100vh-problem-with-ios-safari-92ab23c852a8">100vh problem with iOS Safari</a>
<a href="https://dev.to/cydstumpel/4-things-in-web-development-i-ve-learned-in-2020-so-far-3cg">4 things in web development I&#39;ve learned in 2020, so far</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[신입 개발자 혼자 어디까지 만들 수 있을까?]]></title>
            <link>https://velog.io/@edie_ko/opn-launch-review</link>
            <guid>https://velog.io/@edie_ko/opn-launch-review</guid>
            <pubDate>Sat, 19 Jun 2021 07:17:11 GMT</pubDate>
            <description><![CDATA[<p><em>이 글을 이 세상의 모든 사수가 없는 신입 개발자 분들께 바칩니다.. ^^b</em></p>
<h1 id="0-시작">0. 시작</h1>
<p>저는 오픈닥터의 유일한 프론트엔드 개발자입니다. 작년 10월 본격적으로 프론트엔드 개발 공부를 시작했고, 작년 12월 한 달간 오픈닥터라는 신생 스타트업에서 인턴을 하던 중 올해 1월 말에 감사하게도 채용 오퍼를 받아 근무하고 있습니다😇</p>
<p>처음에는 용기가 부족했습니다. 시니어 개발자 없이 주니어 혼자 개발을 해야한다는 것이... 하지만 한편으로는 정말 큰 기회라는 생각이 들었어요. 혼자 기획 단계에서부터 출발해 개발-배포-운영에 이르는 모든 것을 다 해보고 싶다는 욕심이 있었고, 저 혼자 어디까지 만들 수 있을지 너무 궁금했습니다. </p>
<p>갓 태어난 신입 개발자라 지금 굉장히 코며들고 있는데…! 이걸 해보면 내가 정말 개발을 좋아하는지, 열의가 있는지 리얼하게 알 수 있을 것 같았습니다. </p>
<p>이렇게 1월 18일 오픈닥터의 프론트엔드 개발자로서 커리어를 시작합니다🙏🏻</p>
<p><br><br></p>
<h1 id="1소개">1.소개</h1>
<p>오픈닥터는 &#39;의사들이 진료에만 집중할 수 있게 만들자&#39;라는 방향성을 가지고 의사 및 전략 컨설턴트 주도로 모인 팀입니다. 저희가 첫 번째로 시작한 프로젝트는 &#39;의사들의 개원을 돕는 서비스&#39;를 만드는 것이었어요. 오픈닥터 홈페이지는 개원을 앞둔 의사들이 이용할 수 있는 <u>개원 관리 툴</u>입니다. </p>
<h2 id="1-🗺-지도-페이지">1) 🗺 지도 페이지</h2>
<p>지역별 의원 정보를 제공합니다. 데이터 사이언티스트분이 모은 전국의 52,000여 개의 의원-병원 정보와 다양한 데이터를 바탕으로 <strong>개원 매력도</strong>를 제공하고 있습니다. </p>
<p><img src="https://images.velog.io/images/edie_ko/post/92fa1100-644e-490e-b9c0-83e0a196a9dc/opn_map1.gif" alt=""></p>
<p>지도 페이지는 프론트 3개월차 신입에게 가장 큰 난관이었습니다. 두 개의 난제는 리액트의 라이프사이클과 상태 관리에 카카오맵을 최대한 자연스럽게 얹는 문제, 리액트 컴포넌트를 자바스크립트 SDK인 카카오맵에 어떻게 연결할지에 대한 부분이었습니다. </p>
<p>처음에는 지도에 마커를 어떻게 띄워야 하는지도 몰랐을 정도로 리액트에 대한 이해도도 많이 부족했고 반응형 지도를 다루는 것이 정말 미숙했습니다. 
그 과정에서 리액트의 상태 관리와 여러 훅을 깊게 고민해는 기회가 되었습니다. 특히 카카오맵에 대해선 <a href="https://devtalk.kakao.com/c/map-api">카카오 개발자 포럼</a>의 거의 모든 글을 읽으며 많은 정보를 얻을 수 있었습니다. (카카오 개발자 포럼의 doji.doo님의 글 👍🏻👍🏻)   </p>
<p>제일 재밌고 제일 챌린징했으며 앞으로 개선하고 싶은 게 가장 많은 페이지입니다.🥺  </p>
<h3 id="🏷-두-개의-탭">🏷 두 개의 탭</h3>
<p>진료과 및 전문의 필터링</p>
<p><img src="https://images.velog.io/images/edie_ko/post/e41cd71f-0df9-4693-8fca-e021bda0f279/opn_tab4.gif" alt=""></p>
<p>지도를 기반으로 받아오는 데이터들을 어떻게 하면 두 개의 탭까지 효율적으로 가져올 수 있을까 고민하며 디렉토리 구조와 컴포넌트의 구조의 설계를 고심했던 부분이었습니다. </p>
<p>탭과 지도는 한 몸이어야 하는데 자꾸 따로 놀려고 하기에🥲 어르고 달래서 지금의 오픈닥터의 중추가 되었습니다. 
앞으로 계속 개선시켜서 사용성이 좋은 탭을 만들고 싶어요. </p>
<h3 id="🔎-행정동-자세히-보기">🔎 행정동 자세히 보기</h3>
<p>최대 21개의 그래프로 행정동의 디테일한 정보를 담았습니다.</p>
<p><img src="https://images.velog.io/images/edie_ko/post/3a544e55-66de-4ab4-8f07-958281a19b72/opn_detail1.gif" alt=""></p>
<p>21개의 그래프를 각기 다른 유형으로 시각화시키는 작업에서 가장 중점적으로 두었던 부분은, 백엔드와의 소통 그리고 재사용성이 높은 모듈의 작성입니다. </p>
<p>어떻게 하면 모듈화를 더 빠르고 효용성 높게 작성할 수 있는지, 어디까지 모듈화를 해야 하는지, 많은 데이터를 어떠한 데이터 스키마로 어떻게 콜해서 어떻게 업데이트할지에 대해 고민할 수 있는 기회였습니다. </p>
<h3 id="📍-행정동-비교하기">📍 행정동 비교하기</h3>
<p>행정동 및 진료과 별로 도출한 점수를 한 곳에서 볼 수 있습니다.</p>
<p><img src="https://images.velog.io/images/edie_ko/post/5e227c90-00fa-4863-a7f5-56aa9f4ee4bc/opn_comparison1.gif" alt=""></p>
<p>유저가 개원하고 싶은 행정동을 모아 한 곳에서 볼 수 있도록 합니다. 더 편하게 비교하고 한 눈에 파악할 수 있도록 발전시켜야 할 부분이 많은 요소입니다.</p>
<h3 id="🧐-검색">🧐 검색</h3>
<p>행정동, 의원, 장소, 주소 검색</p>
<p><img src="https://images.velog.io/images/edie_ko/post/e9f92317-7004-4fba-86bd-d5fe7c6607fa/opn_search1.gif" alt=""></p>
<p>카카오 검색 api와 백엔드 api를 동시에 콜하면서 어떻게하면 효율적으로 서버와 연결할 수 있을까에 대해 고민했던 부분입니다. </p>
<p>이렇게 해도.. 되는 건가..😭? 라는 생각이 자꾸만 들면서 여러 명의 개발자 분들께 조언을 얻기도 했던 기능입니다. 집단지성으로 무사히 구현했지만, 한편으론 앞으로 고도화시켜야 할 부분이 많고 욕심이 나는 기능이기도 합니다. </p>
<h3 id="📌-내-장소-저장">📌 내 장소 저장</h3>
<p>폴더링 및 메모 추가 기능</p>
<p><img src="https://images.velog.io/images/edie_ko/post/2fd4564c-66ac-4e02-bdc3-cd9316e98a34/opn_bookmark1.gif" alt=""></p>
<p>처음에는 간단한 즐겨찾기 방식이었으나 베타테스트 중 테스터의 FB를 거쳐 폴더링 및 메모 기능이 추가되었습니다. </p>
<p>꽤 간단해보여서 금방 끝낼 것이라고 생각했는데 이미 많은 기능 개발을 끝내놓은 상황에서 미리 설계했던 구조를 많이 뜯어내야 했기에 목표로 했던 기간보다 3일이 더 소요됐습니다. </p>
<p>유지 보수를 위한 코드, 확장성 있는 코드가 이래서 필요하다는 것을 몸소 체험했고, 제 무식했던 지난 날을 반성하며.. 이게 바로 코딩인가를 조금이나마 경험했습니다. 저는 갈 길이 머네요. </p>
<h3 id="🚶-로드뷰">🚶 로드뷰</h3>
<p><img src="https://images.velog.io/images/edie_ko/post/213dac6c-3720-4adb-af2a-5726b1550193/opn_roadview7.gif" alt=""></p>
<p>비교적 최근에 구현했던 기능이고, 카카오맵 api에 적응된 덕분인지 상대적으로 빠르게 구현할 수 있었습니다. </p>
<p>로드뷰 기능을 추가해달라라는 요청에 디자인에서부터 작은 로드뷰와 전체 로드뷰의 로직을 나누어 구상했습니다. 유저가 어떻게 하면 로드뷰를 편하게 볼 수 있을까 고민하며 내가 유저라면 이렇게 만들겠다고 생각했던 기능들을 추가했습니다. </p>
<p>요즘은 이 로드뷰를 코딩이 잘 안 풀릴 때 제주도 성산일출봉 보러 가는 데 씁니다^^b</p>
<h3 id="🔥-요즘-뜨는-동네">🔥 요즘 뜨는 동네</h3>
<p>점수를 조정해 높은 점수의 행정동을 진한색으로 표시합니다.</p>
<p><img src="https://images.velog.io/images/edie_ko/post/f5a1cfee-ad60-4e26-a0aa-8997b8d47b11/opn_popular1.gif" alt=""></p>
<p>슬라이더로 점수를 조절하면 진한 색으로 표시해줍니다. 초기에는 모든 행정동 마커들이 한 가지 색상이었는데, 3가지 색상으로 바뀌면서 가시성이 좋아졌습니다. </p>
<h2 id="2-📝-개원-체크리스트">2) 📝 개원 체크리스트</h2>
<p><img src="https://images.velog.io/images/edie_ko/post/7cc1579a-c9df-493e-ab8a-092d87a3a356/opn_checklist5.gif" alt=""></p>
<p>개원을 위해 거쳐야하는 과정을 8단계로 구분해 체크 및 기록하고 바로 프린트할 수 있습니다. </p>
<h2 id="3-📕-개원-리포트">3) 📕 개원 리포트</h2>
<p><img src="https://images.velog.io/images/edie_ko/post/06b04cc4-7bb1-4076-87c8-8eef5c6d86d8/opn_report.gif" alt="">
개원에 필요한 정보를 리포트 형식으로 제공하는 게시판입니다. </p>
<h2 id="4-👩-회원가입-및-마이페이지">4) 👩 회원가입 및 마이페이지</h2>
<p><img src="https://images.velog.io/images/edie_ko/post/5eb19133-c893-471b-9bdc-2ed8b11c9dfa/opn_signup.gif" alt="">
빠른 가입을 위해 자동완성 기능을 추가했습니다. </p>
<h1 id="2-어떻게-개발했나">2. 어떻게 개발했나?</h1>
<p><img src="https://images.velog.io/images/edie_ko/post/f9f09908-9027-4006-9465-cd9024899371/ezgif-1-74547af8186f.gif" alt=""></p>
<h3 id="🥁-先생각-後타이핑">🥁 先생각 後타이핑</h3>
<p>많은 기능을 빠른 시간 내에 구현해야 했기 때문에, 정해진 기간 내에 보다 실수 없이 만들어 내기 위해 코드 작성 전 로직을 정리하는 걸 중요하게 생각했어요.
실제로 이렇게 메모장에 생각을 미리 정리하며 케이스를 구분 짓고 예외 처리까지 정리한 후 진행했던 게 가장 큰 도움이 되었습니다.
예를 들어 <u><strong>로드뷰 기능</strong></u>을 구현한다고 했을 때,</p>
<ul>
<li><strong>로드뷰의 레이아웃?</strong> 우측 하단에 작은 로드뷰, 전체 화면 로드뷰 2가지 방식으로 보여주자!</li>
<li><strong>우측 하단의 작은 로드뷰?</strong> 작은 로드뷰의 디폴트 크기를 정해놓되, 로드뷰의 크기를 유저가 드래그해서 조정할 수 있도록 해주자(전체 뷰포트의 padding 30px 범위까지). 맵 워커의 이동에 따라 로드뷰가 바뀌지만 지도의 중심위치는 바뀌지 않게 해서 반응형 지도의 데이터 로드를 줄여주자. 로드뷰 상에서 위치 이동 시 지도는 고정하되 맵워커는 이동. 지도 클릭 시 로드뷰 데이터가 없을 경우 가장 최단 거리의 로드뷰 데이터가 있는 좌표로 맵 워커를 이동시키자  (...)</li>
<li><strong>전체 화면 로드뷰?</strong> 로드뷰 안에 작은 지도를 추가하고 맵 워커 이동에 따라 지도의 중심위치를 바꾸어 주자. 전체 화면 모드 종료 후 <code>로드뷰의 좌표 === 지도의 중심위치</code>  (...)</li>
</ul>
<p>세세한 기능 구현 사항을 미리 정해두고 글을 그대로 코드로 옮겼더니 비교적 빠른 시간 내에 구현할 수 있었습니다. </p>
<h3 id="🎬-문제-발생-시-떠오르는-여러-해결책을-나열해보고-적용해보기">🎬 문제 발생 시 떠오르는 여러 해결책을 나열해보고 적용해보기</h3>
<p>예를 들어 Map에 있는 마커 위에 마우스를 오버했을 때 나타나는 모달이 있는데, 이 마커가 지도 가장자리에 있을 때에는 모달이 창 밖으로 떠버리는 문제가 있다고 합시다. 
그러면 먼저 아이디어를 생각했어요.</p>
<ul>
<li><strong>아이디어</strong>: &#39;viewport 기준으로 modal 크기 이상으로 mouse 포인터가 위치할 경우 modal의 위치가 더이상 나가지 않도록 조정하는 formatter를 넣어주자&#39;</li>
<li><strong>어떻게</strong>:
  먼저 포인터의 위치를 가져오는 방법은 두 가지를 시도해볼 수 있겠는데,
  1) modal 생성 후 useEffect로 addEventlistener mousemove 실행. modal 생성 삭제는 map의 마커에서 관리 (...)
  2) map의 마커에 mouseEnter 했을 때 addEventlistener 실행하고 mouseleave 하면서 함수 종료하기 (...)
  -&gt; 2번 먼저 시도해보자!</li>
<li><strong>실행</strong>: 화면을 4분면으로 나눠서 각각의 면에서 모달의 위치를 바꾸어주는 수식을 다르게 적용하는 formatter 함수를 만들자. (...) 이걸로 <code>const markerLocation = {xAnchor: xFormatter(...), yAnchor: yFormatter(...)}</code> 요런 식으로 어쩌구 저쩌구 (...)</li>
</ul>
<h3 id="🏆-우선순위-정하고-무조건-중요한-것부터">🏆 우선순위 정하고 무조건 중요한 것부터</h3>
<p>백엔드 개발자와 PM과 끊임없이 커뮤니케이션하면서 정한 우선순위를 바탕으로 업무를 처리했어요. 하나의 기능이 아닌, 홈페이지 전체를 맡았기 때문에 구현해야 하는 기능이 많았고 우선순위에 따른 업무 진행이 필수적이었습니다.
백엔드분과 api 작업 실행 순서와 가장 효율적으로 작업을 마칠 수 있는 데이터 스키마에 대해서 지속해서 이야기를 나누었습니다.
그리고 PM분과 전체적, 부분적인 작업 실행 상황과 일정에 영향을 주는 변수라든지, 세부적인 구현 사항에 대해서 세세하게 커뮤니케이션했습니다. 
많은 커뮤니케이션으로 세부 일정을 계속 조정해나가면서 개발했던 것이 코딩하는 것만큼이나 중요했다고 생각합니다.
<br></p>
<h1 id="3-배운-점">3. 배운 점</h1>
<ul>
<li>유지와 보수를 생각하는 더 넓은 시야로 코드를 작성한다.</li>
<li>코드의 의존성을 낮추고 하나의 레이어에 많은 기능을 부여하지 않는 코드 작성하기.</li>
<li>일정을 고려해 일단 굴러가게 만든다. 그리고 점점 더 잘 굴러가게 만든다.</li>
<li>리팩토링은 다 끝내고 하는 게 아니라 그냥 매일 하는 것. 클린 코드 공부하자!</li>
<li>최적화는 매우 비싼 놈이라서.. 집착하고 시간을 투자하지 않으면 쉽게 얻을 수 없다. </li>
<li>자동화, 디버깅, 트러블슈팅은 앞으로 더 공부해야 됨.</li>
</ul>
<br>

<h1 id="4-아쉬운-점">4. 아쉬운 점</h1>
<ul>
<li>와 정말 많이 만들었네 뿌듯하다가도 눈 감았다 뜨면 항상 아쉽다.. 더 멋지고 튼튼한 프로덕트를 만들고 싶다</li>
<li>개발은 코딩의 범위를 넘어서 비즈니스에 대한 문제를 해결하는 것이라는 것을 it G ma..</li>
<li>테스트 코드 작성 못 한 것.. (E2E를 꼭 작성해보고 싶다)</li>
<li>보수적으로 스택을 정한 것 (새로운 스택을 도입하면 일정을 맞추지 못할 것이라는 걱정으로 소극적인 선택을 했다)</li>
</ul>
<br>

<h1 id="5-제작-상세">5. 제작 상세</h1>
<p><code>제작 기간</code>: Total 약 4개월 (110일)</p>
<p>2월 18일 깃 레포지토리 생성. 
3월 18일 베타버전 배포 및 베타 테스트 시작. 
6월 7일 런칭🎉</p>
<p><code>스택</code>:  React / JavaScript / TypeScript / Redux / Styled Components </p>
<p><code>기타 라이브러리 및 협업 툴</code></p>
<ul>
<li>이용 서비스 및 주요 라이브러리: Kakao map api / Kakao search api / Antd / Lodash / Recharts </li>
<li>배포: AWS S3 / cloudfront</li>
<li>협업툴: Figma / Trello / Github</li>
<li>에러 관리: Sentry</li>
</ul>
<br>
<br>


<h1 id="6-마치며">6. 마치며</h1>
<p>결코 쉽지 않았던 4개월간의 개발을 마치고 런칭을 하게 되었습니다.
혼자 a부터 z까지 도맡아 했던 건 정말 귀중한 시간이었고, 무척 힘들었던 시간이었습니다.
항상 내가 치는 이 코드가 맞을까..? 라는 물음이 가득했지만 회사에 프론트는 저 뿐이었기 때문에 오로지 스스로 알아내는 수밖에 없었어요.</p>
<p>미디엄, 스택오버플로우와 온갖 개발자 블로그를 매일 들여다보았고, 2개의 스터디를 추가로 진행하며 문제에 대해 조언을 얻고 서로 의견을 나눠가며 방법을 터득해나갔어요. 
그 과정에서 개발자로서 지식을 공유하는 일이 다른 사람에게 얼마나 도움이 되는 일인지 깨닫게 되었고,
이 글 또한 그런 연유에서 쓰게 되었습니다.</p>
<p>사수가 없는 회사에서 근무를 한다는 것이 최적의 상황은 절대 아니고, 대부분의 사람들은 절대 가면 안된다라고 할지는 모르겠지만 저에게는 적어도 인생의 큰 변곡점이 되었거든요.
누군가가 신입으로 사수가 없는 곳에서 근무하겠다고 한다면 저는 응원하겠습니다. 모든 걸 혼자 다 할 수 있어야 하고, 누구에게도 일을 떠넘길 수 없는 극한의 상황에서😇 더 무거운 책임감을 가지고 강하게 자랄 수 있다는 장점이 있습니다..^^ (물론 심적으로 부담이 되고 스트레스를 받을 때도 있었지만, 그럴 땐 그냥 나는 코딩하는 기계다..라고 생각하며 극복하면.. ^^7)</p>
<p>아직 결코 자랑이 되지 않는 실력을 갖추고 있지만, 이 회고를 발판삼아 권태에 빠지지 않고 계속 탄탄한 서비스 만들어나가겠습니다.
작은 것들을 꾸준히 자랑으로 삼아 그 작은 것들의 총합이 절대 가볍지 않은 개발자의 삶을 살고 싶습니다.</p>
<p>끝까지 읽어주셔서 감사합니다!</p>
<p><br><br><br><br></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JavaScript | 프로토타입 Prototype]]></title>
            <link>https://velog.io/@edie_ko/JavaScript-Prototype</link>
            <guid>https://velog.io/@edie_ko/JavaScript-Prototype</guid>
            <pubDate>Fri, 04 Jun 2021 17:14:02 GMT</pubDate>
            <description><![CDATA[<p><em>사진출처 - Unsplash</em></p>
<h3 id="0-프로토타입이란-prototype">0. 프로토타입이란? Prototype</h3>
<p>자바스크립트의 <strong>프로토타입</strong>은 무엇일까요?
프로토타입 객체는 다른 객체의 원형이 되는 객체입니다.</p>
<p>흔히 알려진대로 자바스크립트는 프로토타입 기반의 언어입니다.
Class가 아닌 프로토타입 같은 <strong>객체 원형</strong>을 기반으로 새로운 객체를 만들어낼 수 있고, 객체를 확장하며 객체 지향적인 프로그래밍을 가능하게 합니다.
ES2015부터 JS에서도 Class를 지원하기 시작했지만, 클래스라는 문법이 추가된 것일 뿐 자바스크립트가 클래스 기반으로 바뀐 것이 아니며 여전히 프로토타입을 기반으로 합니다.</p>
<blockquote>
<p>💡 자바스크립트의 모든 객체는 자신의 부모 역할을 하는 객체와 연결되어 있습니다. 그리고 이것은 마치 객체지향의 상속 개념과 같이 부모 객체의 프로퍼티를 마치 자신의 것처럼 쓸 수 있는 것 같은 특징이 있습니다. 자바스크립트에서는 이런 부모 객체를 <strong>프로토타입 객체</strong>라고 부릅니다. 즉 자바스크립트의 모든 객체는 자신의 프로토타입을 가리키는 <code>[[Prototype]]</code>이라는 숨겨진 프로퍼티를 가집니다.</p>
</blockquote>
<p><strong>Object.getPrototypeOf()</strong></p>
<p>프로토타입의 접근은 과거 ECMAScript 2015 이전까지는 <code>__proto__</code> 프로퍼티를 이용해서 접근했으나, 현재는 <code>Object.getPrototypeOf()</code> 메서드를 활용합니다.
또한 프로토타입을 수정하기 위해 <code>Object.setPrototypeOf()</code>를 사용합니다.</p>
<h3 id="1-객체-인스턴스-생성과-프로토타입">1. 객체 인스턴스 생성과 프로토타입</h3>
<pre><code class="language-javascript">const person = {};
person.name = &#39;positiveko&#39;;
person.nickname = &#39;긍정코&#39;
person.age = 77;</code></pre>
<p>위처럼 새로운 빈 객체를 만들고 할당 구문을 통해 프로퍼티를 덧붙여봅니다.
하지만 value만 다른 또 다른 person 객체를 만드려면 매번 개별적으로 프로퍼티를 새로 할당해주어야 합니다.
따라서 <strong>클래스처럼 한 곳에서 프로퍼티와 메서드를 통합해서 설정하는 수단</strong>이 있다면 좋겠죠.
자바스크립트는 다른 Java와 C++ 같은 객체 지향 언어와 마찬가지로 생성자로 객체를 생성할 수 있는 <code>new 연산자</code>를 제공합니다.
하지만 <code>new 연산자</code>는 단순히 빈 객체를 새로 생성할 뿐입니다. <strong>이 때, 프로토타입이 새로 생성되는 객체에 대한 청사진 역할을 할 수 있습니다.</strong></p>
<p>new 연산자를 사용할 때와 사용하지 않았을 때를 비교해보겠습니다.
아무 것도 하지 않는 Person() 함수를 정의하고 두 가지 방법으로 호출해봅니다.</p>
<pre><code class="language-javascript">function Person(){};

Person.prototype.name = () =&gt; &#39;positiveko&#39;;

// 1: 일반 함수로 호출
const person1 = Person();
console.log(person1); // undefined

// 2: new 연산자로 호출
const person2 = new Person();
console.log(person2); // Person { __proto__: { constructor: ƒ Person(), name: ƒ () } }
console.log(person2.name()); // &#39;positiveko&#39;</code></pre>
<p>일반 함수로 실행했을 때는 반환 값이 undefined 입니다.
하지만 <code>new 연산자</code>로 호출했을 때는 새로운 객체가 생성되고 이 객체가 함수의 콘텍스트로 설정됩니다. 
<strong>즉, 함수의 프로토타입이 새로운 객체 생성을 위한 일종의 청사진으로 작동한다는 것을 보여줍니다.</strong> </p>
<h4 id="함수-내에서-this-매개변수를-사용하여-객체를-초기화할-수도-있습니다">함수 내에서 this 매개변수를 사용하여 객체를 초기화할 수도 있습니다.</h4>
<pre><code class="language-javascript">function Person(){
  this.nickname = &#39;긍정코&#39;
  this.introduce = () =&gt; `Hello, I&#39;m ${this.nickname}`
};

Person.prototype.introduce = () =&gt; this.nickname;

const person = new Person;
console.log(person.introduce()); // &quot;Hello, I&#39;m 긍정코&quot;</code></pre>
<p>위의 예제를 보면 prototype 메서드 대신 생성자 함수 내에서 정의한 프로퍼티가 우선한다는 것을 알 수 있습니다. </p>
<blockquote>
<p>💡 초기화 진행 순서를 정리해보자면, </p>
</blockquote>
<p>  1) 프로토타입의 프로퍼티들이 새로 만들어진 객체 인스턴스와 바인딩된다.
  2) 생성자 함수 내에서 정의한 프로퍼티들이 객체 인스턴스에 추가된다.</p>
<p>잊지맙시다... 생성자 내에서 바인딩한 것이 항상 프로토타입에서 바인딩된 것보다 우선한다는 걸...!</p>
<h4 id="constructor-프로퍼티는-객체의-생성자를-참조합니다">constructor 프로퍼티는 객체의 생성자를 참조합니다.</h4>
<pre><code class="language-javascript">function Person(){
  this.nickname = &#39;긍정코&#39;
  this.introduce = () =&gt; `Hello, I&#39;m ${this.nickname}`
};

const positiveko = new Person();
console.log(positiveko.constructor); // ƒ Person()
positiveko.constructor.prototype.name = &#39;positiveko&#39;;
console.log(positiveko.name); // &#39;positiveko&#39;

const mengkki = new Person();
console.log(mengkki.name); // &#39;positiveko&#39;</code></pre>
<p>여기서 알 수 있는 중요한 사실은,
객체의 어떤 프로퍼티를 참조할 때 객체에 해당 프로퍼티가 없다면 프로토타입에서 그 프로퍼티를 찾는다는 점입니다.</p>
<blockquote>
</blockquote>
<p>💡 다시 한번 이 과정을 정리해보자면, </p>
<p>  1) 객체의 프로퍼티를 참조하면, 그 객체는 자신에게 해당 프로퍼티가 있는지 검사한다. 만약 해당 프로퍼티가 존재한다면 그 값을 사용하고, 없다면...
  2) 그 객체와 관련된 프로토타입에 해당 프로퍼티가 있는지 검사한다. 만약 있다면 프로토타입에 있는 값을 사용하고, 없다면...
  3) 그 값은 <code>undefined</code>이다.</p>
<h3 id="2-생성자와-객체-타입">2. 생성자와 객체 타입</h3>
<h4 id="instanceof-연산자">instanceof 연산자</h4>
<pre><code class="language-javascript">function Person(){
};

const positiveko = new Person();

console.log(positiveko instanceof Person); // true
console.log(positiveko.constructor === Person); // true</code></pre>
<p>위의 예제를 보면 instanceof 연산자를 통해 어떤 생성자 함수를 사용하여 인스턴스를 만들었는지를 알 수 있습니다.
또 constructor 프로퍼티를 이용해서 인스턴스를 생성한 원본 함수를 알 수 있네요.</p>
<p>그럼 한번 이 constructor를 가지고 두 명의 사람을 만들어봅시다.</p>
<pre><code class="language-javascript">function Person(){
};

const person1 = new Person();
const person2 = new person1.constructor();

console.log(person2 instanceof Person); // true
console.log(person1 !== person2) // true</code></pre>
<p>먼저 person1을 Person 생성자를 통해 만들어줍니다. 그리고 person2는 constructor 프로퍼티로 만들어주었습니다.
person2는 Person 생성자 함수를 사용한 인스턴스라는 것을 보여주지만, person1과 person2는 다른 인스턴스라고 나옵니다.
이를 통해 원본 Person 생성자 함수를 직접적으로 사용하지 않더라도 constructor 프로퍼티를 사용해서 Person 생성자 함수를 참조할 수 있다는 것을 알 수 있습니다.</p>
<h3 id="3-상속과-프로토타입-체인">3. 상속과 프로토타입 체인</h3>
<p>이번에는 prototype을 사용해서 어떻게 상속을 구현할 수 있는지 알아봅시다.
가장 간단해보이는 방법으로 시도해봅니다.</p>
<pre><code class="language-javascript">function Person(){};
Person.prototype.speak = function(){};
// 말하는 사람을 구현했다.

function Cat(){};
Cat.prototype = {speak: Person.prototype.speak};
// 사람이 말을 하는 것처럼 고양이도 말하게 해보자.

const cat = new Cat();
console.log(cat instanceof Cat); // true
console.log(cat instanceof Person); // false
console.log(cat instanceof Object); // true</code></pre>
<p>cat이 말하도록 프로토타입에 추가해보았지만 결국 Cat은 Person 프로토타입의 기능을 상속받지 못했습니다. 어떻게하면 사람의 speak 메서드를 물려줄 수 있을까요?</p>
<h4 id="person-생성자를-사용해보자">Person 생성자를 사용해보자!</h4>
<pre><code class="language-javascript">function Person(){};
Person.prototype.speak = function(){};

function Cat(){};
Cat.prototype = new Person();
// 이번에는 Person 생성자를 Cat의 프로토타입에 넣어주었다.

const cat = new Cat();

console.log(cat instanceof Cat); // true
console.log(cat instanceof Person); // true
console.log(cat instanceof Object); // true</code></pre>
<p>이번에는 Person의 인스턴스를 Cat의 프로토타입으로 지정해서 Cat이 Person을 상속하도록 합니다. 
이제 cat은 말하는 고양이가 되었습니다... 🐈</p>
<h4 id="objectcreate-이용하여-상속받을-수도-있다">Object.create() 이용하여 상속받을 수도 있다.</h4>
<p>ECMAScript 2015에 추가된 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create">Object.create()</a>를 사용해서 첫 번째 인수로 프로토타입을 넘겨줄 수도 있습니다.</p>
<pre><code class="language-javascript">const obj = {
  name: &#39;edie&#39;,
  introduce: function() {
    return `Hello, I&#39;m ${this.name}`
  } 
}

const person = Object.create(obj);
console.log(person.introduce()); // &quot;Hello, I&#39;m edie&quot;</code></pre>
<h4 id="더-읽어보면-좋은-글">더 읽어보면 좋은 글</h4>
<p><a href="https://medium.com/@bluesh55/javascript-prototype-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-f8e67c286b67">[Javascript ] 프로토타입 이해하기 (오승환님 블로그)</a>
<a href="http://insanehong.kr/post/javascript-prototype/">Javascript 기초 - Object prototype 이해하기 (Insanehong 님 블로그)</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[CSS | last-child는 이제 쓰지 않습니다]]></title>
            <link>https://velog.io/@edie_ko/css-enabling-pattern</link>
            <guid>https://velog.io/@edie_ko/css-enabling-pattern</guid>
            <pubDate>Sat, 29 May 2021 18:10:32 GMT</pubDate>
            <description><![CDATA[<p><em>사진 출처 - unsplash</em></p>
<h3 id="0-들어가면서">0. 들어가면서</h3>
<p>오늘은 그동안 등한시하던 CSS를 되짚어보는 시간입니다.
그동안 여기저기서 읽고 들은 건 많은데, 글로 정리되지 않아 제대로 활용해보지 못했던 CSS 프로퍼티들을 간단하게 정리합니다. </p>
<h3 id="1-last-child를-사용하지-않고-not-가상클래스로-enabling-패턴-적용하기">1. last-child를 사용하지 않고 :not 가상클래스로 enabling 패턴 적용하기</h3>
<p>아래와 같은 리스트가 있습니다. </p>
<pre><code class="language-html">  &lt;ul&gt;
    &lt;li&gt;1&lt;/li&gt;
    &lt;li&gt;2&lt;/li&gt;
    &lt;li&gt;3&lt;/li&gt;
    &lt;li&gt;4&lt;/li&gt;
    &lt;li&gt;5&lt;/li&gt;
  &lt;/ul&gt;</code></pre>
<p>만약 리스트 사이사이 보더 라인을 넣어주고 싶다면, 전체 li 태그에 밑줄을 넣어주고 마지막 child는 제거하는 방식으로 해왔습니다. 아래처럼요.(SCSS 기준)</p>
<pre><code class="language-scss">li {
  border-bottom: 1px solid black;

  :last-child {
    border-bottom: none;
  }
}</code></pre>
<p>여기서 어떻게하면 last-child 선택자를 이용해서 disabling 해주는 대신, <strong>enabling 패턴</strong>으로 작성할 수 있을까요? 
<a href="https://developer.mozilla.org/ko/docs/Web/CSS/:not">:not (부정 negation) 가상 클래스</a> 를 이용하면 됩니다.
:not() 선택자는 인자로 들어온 것을 제외한 나머지에 대해 스타일 프로퍼티를 넣을 수 있습니다.</p>
<pre><code class="language-scss">li:not(:last-child) {
  border-bottom: 1px solid black; 
}</code></pre>
<p>&#39;last-child를 제외한 나머지 li에 밑줄을 넣어줘!&#39;라는 뜻입니다.
이렇게 :not 가상 클래스를 이용해서 더 간결하게 작성할 수 있습니다.</p>
<p>혹은 <a href="https://developer.mozilla.org/ko/docs/Web/CSS/Adjacent_sibling_combinator">+ (인접 선택자)</a>를 사용할 수 있습니다.
<code>+ 선택자</code> 는 선택자 앞의 요소 바로 뒤에 있는 요소만 선택해서 적용합니다.
따라서 이번에는 border-top을 적용해주면 되겠네요.</p>
<pre><code class="language-scss">li + li {
  border-top: 1px solid black;
}</code></pre>
<p>enabling CSS에 대한 자세한 내용은 <a href="https://www.silvestar.codes/articles/you-want-a-single-enabling-selector-not-the-one-that-disables-the-rule-of-the-previous-one/">여기</a>를 참고합니다.</p>
<h3 id="2-important보다-더-간단하게-all-프로퍼티">2. !important보다 더 간단하게 all 프로퍼티</h3>
<p>기존의 CSS 프로퍼티를 override 하고 싶을 때 <code>!important</code> 프로퍼티를 많이 사용합니다.
다른 세팅을 무시하고 내가 바로 지금 적은 것만 적용하고 싶을 때 사용하는 방법인데요,
만약 !important를 적용해야 할 프로퍼티가 많아진다면 일일이 !important를 적어야겠죠. 
그럴 때는 <a href="https://developer.mozilla.org/ko/docs/Web/CSS/all">all property</a>를 사용해서 초깃값으로 세팅해줄 수 있습니다.
(SCSS 기준)</p>
<pre><code class="language-html">&lt;button&gt;
  &lt;span&gt;Hello&lt;/span&gt;
&lt;/button&gt;</code></pre>
<pre><code class="language-scss">button {
  color: red !important;

  span {
    all: initial;
  }
}</code></pre>
<p><code>all: initial</code> 적용만으로 span의 CSS 설정은 초깃값으로 돌아갑니다.
이외에도 inherit, unset이 있습니다. 자세한 내용은 <a href="https://developer.mozilla.org/ko/docs/Web/CSS/all">MDN</a>을 참고합니다.</p>
<h3 id="3-css를-scss처럼-쓸-수-있게-해주는-is-where-selector">3. CSS를 SCSS처럼 쓸 수 있게 해주는 :is, :where selector</h3>
<p>저는 SCSS를 사용하고 있습니다. CSS를 사용하지 않는 가장 큰 이유는 SCSS의 nesting 때문이었는데요,
이제는 <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:is">:is</a>와 <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:where">:where 선택자</a>를 이용해서 CSS에서도 SCSS의 네스팅을 어느 정도 구현할 수 있습니다.</p>
<pre><code class="language-css">section h1, article h1, aside h1, nav h1 {
  font-size: 25px;
}

/* :is selector를 사용한다면 조금 더 간단해집니다*/
:is(section, article, aside, nav) h1 {
  font-size: 25px;
}

/* :where selector도 마찬가지입니다.*/
:where(section, article, aside, nav) h1 {
  font-size: 25px;
}</code></pre>
<p>그렇다면 <code>:is selector</code> 와 <code>:where selector</code>의 차이점은 무엇일까요?
바로 우선순위의 차이입니다.</p>
<blockquote>
<p>💡  <strong>The selectors inside :is() count towards the specificity of the overall selector</strong>, and class selectors have a higher specificity than element selectors. <strong>However, selectors inside :where() have specificity 0</strong>, so the orange footer link will be overridden by our simple selector. - MDN</p>
</blockquote>
<p>:where 선택자는 다른 간단한 선택자에 의해서 override 될 수 있지만, :is 선택자는 더 높은 우선순위를 갖습니다.
자세한 내용은 <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:where#examples">여기</a>를 참고합니다.</p>
<h3 id="4-media로-css로-다크모드-구현하기">4. @media로 CSS로 다크모드 구현하기</h3>
<p>최근 다크 모드를 지원하는 게 트렌드인데요, 사용자의 눈의 피로를 줄여주고 UX까지 향상하는 다크모드는 <code>CSS @media 쿼리</code>를 이용해 만들 수 있습니다.</p>
<p><a href="https://developer.mozilla.org/ko/docs/Web/CSS/@media/prefers-color-scheme">prefers-color-scheme</a>는 CSS의 미디어 특성입니다. 유저의 OS가 라이트 모드인지 다크 모드인지를 탐지하는 데에 사용할 수 있습니다.</p>
<pre><code class="language-css">:root {
  --bg-color: #fff;
  --text-color: #111;
  --cta-bg-color: #00a0f9;
  --cta-text-color: #fff;
  --btn-bg-color: #222;
  --btn-text-color: #fff;
}

@media (prefers-color-scheme: dark) {

  :root:not([theme=&quot;light&quot;])  {
    --bg-color: #111;
    --text-color: #fff;
    --cta-bg-color: #ae63e4;
    --cta-text-color: #e3e4e8;
    --btn-bg-color: #fff;
    --btn-text-color: #222;
  }
}</code></pre>
<p>자세한 다크모드 적용 방법은 <a href="https://www.youtube.com/watch?v=Jnn88lzJjWs&amp;t=2s">여기</a>를 참고합니다.</p>
<hr>
<p>정리한 내용은 여기까지입니다.</p>
<p>감사합니다!😇</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Mistakes | 주니어 리액트 개발자인 내가 실수하고 있었던 것 ]]></title>
            <link>https://velog.io/@edie_ko/Mistakes-%EC%A3%BC%EB%8B%88%EC%96%B4-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EA%B0%9C%EB%B0%9C%EC%9E%90%EC%9D%B8-%EB%82%B4%EA%B0%80-%EC%8B%A4%EC%88%98%ED%95%98%EA%B3%A0-%EC%9E%88%EC%97%88%EB%8D%98-%EA%B2%83</link>
            <guid>https://velog.io/@edie_ko/Mistakes-%EC%A3%BC%EB%8B%88%EC%96%B4-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EA%B0%9C%EB%B0%9C%EC%9E%90%EC%9D%B8-%EB%82%B4%EA%B0%80-%EC%8B%A4%EC%88%98%ED%95%98%EA%B3%A0-%EC%9E%88%EC%97%88%EB%8D%98-%EA%B2%83</guid>
            <pubDate>Wed, 19 May 2021 13:55:32 GMT</pubDate>
            <description><![CDATA[<h3> 주니어 리액트 개발자인 내가 실수하고 있었던 것</h3>

<br />

<p>주니어 개발자 4개월차... 이대로 괜찮은가? 
그동안 실수했던 것들을 반성하며 회고하는 시간을 갖습니다...😶
조금은 부끄럽지만 계속 부끄러워지는 것보다 한번 부끄럽고 마는 것이 좋습니다. 
파편적인 지식이라 이 또한 틀릴 수도 있습니다.
댓글로 틀린 내용에 대해서 말씀해주시면 5개월차 실수 모음집, 6개월차 실수 모음집을 만드는 데에 도움이 됩니다.</p>
<br />
<br />

<h3 id="1-이전-상태를-기반으로-새로운-상태를-세팅하기">1. 이전 상태를 기반으로 새로운 상태를 세팅하기</h3>
<p>새로운 상태는 이전 상태에 기반한 setState를 만들어서 업데이트 해야합니다. 그 이유는 setState이가 비동기적으로 작동해서 상태값을 batch(일정 시간 동안 변화하는 상태를 일괄 처리)로 변경하기 때문입니다.</p>
<p><code>Bad👎🏻</code> (This can work yes, but this is not safe.)</p>
<pre><code class="language-jsx">const [isState, setState] = useState(false);
const toggleBtn = () =&gt; setState(!isState); // (x)

return (
  &lt;Button onClick={toggleBtn} /&gt;
)</code></pre>
<p><code>Good👍🏻</code> <br />
아래와 같이 작성합니다.</p>
<pre><code class="language-jsx">const [isState, setState] = useState(false);
const toggleBtn = () =&gt; setState(isState =&gt; !isState); // (o)

return (
  &lt;Button onClick={toggleBtn} /&gt;
)</code></pre>
<p>이전 상태가 주어진 setState가 새로운 상태를 리턴할 수 있도록 바꾸어 줍니다. </p>
<blockquote>
<p>💡 <strong>왜 <code>setState(!isState)</code>가 아니라 <code>setState(isState =&gt; !isState)</code>로 적어야 할까요?</strong>
(댓글로 남겨주신 질문에 대한 답을 추가 정리합니다.) <br/>
React에서 setState는 state 값을 변경할 때 사용하는 함수입니다.
setState의 동작을 코드로 제가 아는 선에서 대략적으로 나타내보면 다음과 같습니다.</p>
</blockquote>
<pre><code class="language-typescript">  type TCallBack = (prev: object) =&gt; object
  let state: object;
  const setState = (partialState: object | TCallBack) =&gt; {
   state = typeof partialState === &#39;function&#39; ? partialState(state) : partialState;
  };</code></pre>
<p>위처럼 setState는 첫 번째 인자를 object나 function으로 받을 수 있는데요, object가 인자일 경우 <code>state = object</code>로 업데이트 됩니다.<br/> 여기까진 문제가 없습니다.
하지만 이전의 상태를 기반으로 연속적으로 업데이트 해야할 때에는 (eg. <code>새로운 상태 = 이전 상태 + 1</code>, <code>setState(!isState)</code>) setState가 비동기적으로 작동하기 때문에 안전하지 않습니다.</p>
<pre><code class="language-javascript">const [state, setState] = useState(0);
setState(state + 1);
setState(state + 1);
setState(state + 1);</code></pre>
<p>위와 같은 상황에서 state = 3이 아니라 1이 될 수도 있고.. 랜덤하게 변경이 됩니다.
따라서 setState의 인자로 콜백을 넣어준다면, (eg. <code>setState(prevState =&gt; prevState + 1)</code>, <code>setState(isState =&gt; !isState)</code>)
콜스택에 쌓이며 호출된 순서대로 작동하고 최신 상태를 기반으로 실행되기 때문에 안전하게 state를 변경할 수 있습니다.</p>
<h3 id="2-state-얕은-복사">2. state 얕은 복사</h3>
<p>리액트에선 state의 불변성을 지켜주고 setState 함수를 통해 상태 업데이트를 해주어야 합니다. 
만약 state의 불변성을 지켜주지 않는다면 컴포넌트 렌더링이 무분별하게 일어날지도 모르고, 컴포넌트를 최적화하기 어려워지겠죠.</p>
<pre><code class="language-javascript">const person = {
  name: &#39;edie&#39;,
  age: &#39;77&#39;,
  dateJoined: &#39;2021-05-18&#39;,
  language: {first: &#39;javascript&#39;, second: &#39;typescript&#39;}
}

const copiedPerson = {...person};
copiedPerson.language.first = &#39;python&#39;;

console.log(person);
console.log(copiedPerson);

// {
//   name: &#39;edie&#39;,
//   age: &#39;77&#39;,
//   dateJoined: &#39;2021-05-18&#39;,
//   language: { first: &#39;python&#39;, second: &#39;typescript&#39; }
// }
// {
//   name: &#39;edie&#39;,
//   age: &#39;77&#39;,
//   dateJoined: &#39;2021-05-18&#39;,
//   language: { first: &#39;python&#39;, second: &#39;typescript&#39; }
// }</code></pre>
<p>person의 langauge.first가 python으로 바뀐 것이 보이시죠.
이럴 땐 다음과 같이 바꾸어야 합니다.</p>
<pre><code class="language-javascript">const copiedPerson = {...person};
copiedPerson.language = {...person.language, first: &#39;python&#39;};

console.log(person);
console.log(copiedPerson);

// {
//   name: &#39;edie&#39;,
//   age: &#39;77&#39;,
//   dateJoined: &#39;2021-05-18&#39;,
//   language: { first: &#39;javascript&#39;, second: &#39;typescript&#39; }
// }
// {
//   name: &#39;edie&#39;,
//   age: &#39;77&#39;,
//   dateJoined: &#39;2021-05-18&#39;,
//   language: { first: &#39;python&#39;, second: &#39;typescript&#39; }
// }</code></pre>
<p>위처럼 참조 데이터 안에 또 다른 참조 데이터가 있을 땐, 그 분기점마다 스프레드 연산자를 사용해야 합니다.
혹은 데이터 구조가 좀 더 복잡해진다면 <code>Immer.js</code>의 <code>produce</code> 함수를 사용해서 상태의 불변성을 지키며 업데이트할 수도 있습니다.</p>
<h3 id="3--nullish-coalescing과--or-operator">3. ?? Nullish Coalescing과 || OR operator</h3>
<p>흔히 value가 <code>undefined</code>나 <code>null</code>인 경우를 알기 위해 OR operator를 많이 씁니다.</p>
<pre><code class="language-javascript">const value = undefined;
console.log(value || 1); // 1
console.log(value ?? 1); // 1

const value1 = null;
console.log(value1 || 1); // 1
console.log(value1 ?? 1); // 1</code></pre>
<p>이처럼 OR operator(||)는 좌측이 false인 경우, 우측의 값을 리턴합니다.
그런데 undefined도 false이고 null도 false이지만, <strong>0 또한 false이고 &#39;&#39; 또한 false</strong>입니다.
따라서 값에 0이나 &#39;&#39;가 들어올 수도 있는 경우 다음처럼 ||와 ??를 구분해서 사용해야 합니다.</p>
<pre><code class="language-javascript">const value2 = 0;
console.log(value2 || 1); // 1
console.log(value2 ?? 1); // 0

const value3 = &#39;&#39;;
console.log(value3 || null); // null
console.log(value3 ?? null); // &#39;&#39;</code></pre>
<p>이처럼 Nullish coalescing operator(??)는 좌측이 undefined인 경우에만 우측의 값을 리턴하고, 그 외의 경우에는 그대로 리턴합니다.</p>
<h3 id="4-json-mock-data-만들기">4. JSON mock data 만들기</h3>
<p>JSON Mock data를 만들 때, 일일히 double quote를 치고 있던 제 모습이 기억납니다.
그냥 JSON.stringify()를 쓰면 되는 걸...</p>
<pre><code class="language-javascript">const person = {
  name: &#39;edie&#39;,
  age: &#39;77&#39;,
  dateJoined: &#39;2021-05-18&#39;,
  language: &#39;javascript&#39;
}

// JSON.stringify(value, replacer, space)
JSON.stringify(person, null, 2);

// &#39;{
//   &quot;name&quot;: &quot;edie&quot;,
//   &quot;age&quot;: &quot;77&quot;,
//   &quot;dateJoined&quot;: &quot;2021-05-18&quot;,
//   &quot;language&quot;: &quot;javascript&quot;
// }&#39;</code></pre>
<h3 id="5-컴포넌트에-boolean-props-true-표시-x-string-props-브라켓-x">5. 컴포넌트에 boolean props true 표시 X, string props 브라켓 X</h3>
<p>컴포넌트에 전달하는 boolean props가 true일 때에는 따로 true를 표시하지 않습니다. string props는 컬리 브라켓을 쓸 필요 없이 double quote로만 전달합니다.</p>
<pre><code class="language-jsx">&lt;Components disabled={true} /&gt; // (x)
&lt;Components disabled /&gt; // (o)

&lt;Components title={&quot;제목&quot;} /&gt; // (x)
&lt;Components title={&#39;제목&#39;} /&gt; // (x)
&lt;Components title={`제목`} /&gt; // (x)
&lt;Components title=&quot;제목&quot; /&gt; // (o)</code></pre>
<p>자세한 내용은 <a href="https://github.com/airbnb/javascript/blob/master/react/README.md">Airbnb React/JSX Style Guide</a>를 참고합니다.</p>
<h3 id="6-여러-api-동시에-완료시키기">6. 여러 API 동시에 완료시키기</h3>
<p>한 개의 컴포넌트 안에서 여러개의 API를 연결해야 하는 상황이라면 첫 번째의 response와 마지막 열두 번째의 response까지 텀이 발생해서 순차적으로 view가 뜨는 문제가 발생할 수 있습니다.
한 번에 12개의 API를 연결해야하는 컴포넌트에서 모든 view를 한꺼번에 띄울 수 없을까 고민하다가 사용하게 된 것은 Promise.all과 Promise.allSettled이었습니다.</p>
<p>Promise.all, Promise.allSettled 둘 다 모두 array 안에 있는 모든 request가 resolved 되면 결과를 return합니다. 차이점은 Promise.all은 중간에 하나의 request가 rejected 되면 모든 request의 실행이 중단되고 다음의 request는 실행되지 않지만, Promise.allSettled는 하나가 rejected 되더라도 모든 request가 실행되고 결과를 return한다는 점입니다.</p>
<p>따라서 용도와 목적에 따라 all과 allSettled를 구분하여 사용하면 됩니다.</p>
<pre><code class="language-javascript">  const axiosBookmark = async () =&gt; {
    const sendingUrls = [
      getDongBookmark(),
      getLegalDongBookmark(),
      getKakaoBookmark(),
      getHospitalBookmark(),
    ];

    const response = await Promise.allSettled(sendingUrls);

    console.log(response);
  };

  useEffect(() =&gt; {
    axiosBookmark();
  }, []);</code></pre>
<p>다음과 같은 결과가 리턴됩니다.
<img src="https://images.velog.io/images/edie_ko/post/91104c02-4bf0-45f8-9d4f-f1d684b36de6/allsettled.png" alt=""></p>
<p>각각의 request가 <code>fullfilled</code> 혹은 <code>rejected</code>되었는지를 알려주고 그에 따른 결과 data를 얻을 수 있습니다.</p>
<h3 id="7-네스팅하지-않고-early-return-하기">7.. 네스팅하지 않고 early return 하기</h3>
<pre><code class="language-jsx">function PracticeComponent() {
  const [isLoading, setLoading] = useState(true);

  return (
    &lt;&gt;
      {isLoading ? (
        &lt;Spin /&gt;
      ) : (
        &lt;PracticeBox&gt;
          &lt;p&gt;Title&lt;/p&gt;
          &lt;span&gt;contents&lt;/span&gt;
        &lt;/PracticeBox&gt;
      )}
    &lt;/&gt;
  );
}

export default PracticeComponent;</code></pre>
<p>위처럼 삼항 연산자로 쓰는 경우가 많았습니다. 하지만 다음처럼 네스팅하지 않고 얼리 리턴해버리면 더 코드가 깔끔해 보입니다.</p>
<pre><code class="language-jsx">function PracticeComponent() {
  const [isLoading, setLoading] = useState(true);

  if (isLoading) return &lt;Spin /&gt;;

  return (
    &lt;PracticeBox&gt;
      &lt;p&gt;Title&lt;/p&gt;
      &lt;span&gt;contents&lt;/span&gt;
    &lt;/PracticeBox&gt;
  );
}

export default PracticeComponent;</code></pre>
<h3 id="마무리">마무리</h3>
<p>정리한 실수는 여기까지 입니다. <br />
이보다 더 많은 실수들이 있지만 내용을 보다 더 자세하게 담고 싶은 생각에 다른 포스트로 업데이트를 해보려합니다.
읽어주셔서 감사합니다😇</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JavaScript | 클로저 Closure]]></title>
            <link>https://velog.io/@edie_ko/JavaScript-%ED%81%B4%EB%A1%9C%EC%A0%80-Closure</link>
            <guid>https://velog.io/@edie_ko/JavaScript-%ED%81%B4%EB%A1%9C%EC%A0%80-Closure</guid>
            <pubDate>Sun, 14 Mar 2021 12:10:48 GMT</pubDate>
            <description><![CDATA[<p> 요즘 꾸준히 읽고 있는 Medium 글에서 &quot;클로저를 모르고 자바스크립트를 한다는 건, 영어 문법에 대한 이해도 없이 영어 말하기를 하는 것과 같다.&quot;는 문장을 읽고 클로저에 대해 TIL을 써보기로 했습니다.</p>
<h2 id="0-클로저란-closure">0. 클로저란? Closure</h2>
<p> 클로저는 자바스크립트뿐만 아니라 여러 함수형 프로그래밍 언어도 가지고 있는 개념이다. 클로저에 대한 일반적인 정의를 나열해보자면, </p>
<blockquote>
<p>💡 A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function&#39;s scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.</p>
</blockquote>
<blockquote>
<p>💡 함수를 선언할 때 만들어지는 유효범위가 사라진 후에도 호출할 수 있는 함수 - 존 레식</p>
</blockquote>
<blockquote>
<p>💡 자신이 생성될 때의 스코프에서 알 수 있었던 변수들 중 언젠가 자신이 실행될 때 사용할 변수들만을 기억하여 유지시키는 함수 - 유인동</p>
</blockquote>
<blockquote>
<p>💡 외부 함수보다 중첩 함수가 더 오래 유지되는 경우 중첩 함수는 이미 생명 주기가 종료한 외부 함수의 변수를 참조할 수 있다. 이러한 중첩 함수를 클로저라고 부른다. - 이웅모</p>
</blockquote>
<blockquote>
<p>💡 클로저란 어떤 함수에서 선언한 변수를 참조하는 내부함수를 외부로 전달할 경우, 함수의 실행 컨텍스트가 종료된 후에도 해당 변수가 사라지지 않는 현상. - 정재남</p>
</blockquote>
<p> 즉 함수 안에서 함수를 선언하고 사용하는 상황에서 <strong>클로저란 함수와 함수의 lexical 스코프를 포함하는 걸 뜻한다</strong>. <strong>생성될 당시의 환경을 기억하는 함수</strong>라고 생각하자. 
<br><br> </p>
<p><u>여기서 렉시컬 스코프란?</u></p>
<p>자바스크립트는 렉시컬 스코프를 따르는 프로그래밍 언어이다. 자바스크립트 엔진은 함수를 어디서 호출했는지가 아니라 함수를 어디에 정의했는지에 따라 상위 스코프를 결정한다. 이를 렉시컬 스코프(정적 스코프)라 한다. 다른 말로 실행 컨텍스트의 렉시컬 환경이라 한다. </p>
<blockquote>
<p>외부 함수 안에 내부 함수가 있는 상황에서, outer 함수의 실행이 종료되면 inner 함수를 반환하면서 outer 함수의 생명 주기가 종료된다. 즉, outer 함수의 실행 컨텍스트가 실행 컨텍스트 스택에서 제거된다. 이때 outer 함수의 실행 컨텍스트는 실행 컨텍스트 스택에서 제거되지만 outer 함수의 렉시컬 환경까지 소멸하는 것은 아니다. outer 함수의 렉시컬 환경은 inner 함수에 의해 참조되고 있고 inner 함수는 전역 변수 innerFunc에 의해 참조되고 있으므로 가비지 컬렉션의 대상이 되지 않기 때문이다. 가비지 컬렉터는 누군가가 참조하고 있는 메모리 공간을 함부로 해제하지 않는다.
<em>정재남, &lt;코어자바스크립트&gt;</em></p>
</blockquote>
<p>즉 클로저를 사용하면 외부함수의 실행 컨텍스트가 스택에서 제거되어도 내부함수에 있는 변수에 접근할 수 있다. </p>
<h2 id="1-클로저의-사용">1. 클로저의 사용</h2>
<blockquote>
<p>Closures are frequently used in JavaScript for <strong>object data privacy</strong>, in event handlers and callback functions, and in partial applications, currying, and other functional programming patterns.</p>
</blockquote>
<p><strong>클로저는 상태를 안전하게 변경하고 유지하기 위해 사용한다.</strong> 즉 상태가 의도치 않게 변경되지 않도록 상태를 안전하게 은닉하고, 특정 함수에게만 상태 변경을 허용하여 상태를 안전하게 변경하고 유지하기 위해 사용한다.</p>
<p>자바스크립트는 클래스가 없고 전역변수를 기반으로 하기 때문에 변수가 외부에 노출된다. (클래스에는 public, private, protected 같은 접근 제한자를 선언해서 프로퍼티와 메서드의 공개 범위를 한정할 수 있는데 자바스크립트에는 이러한 제한자가 없고 기본적으로 public 하다.) 따라서 자바스크립트에서는 이 클로저를 이용해서 private한 변수를 만들 수 있다.</p>
<h2 id="2-클로저의-예시">2. 클로저의 예시</h2>
<pre><code>const num = 1;
const foo = () =&gt; {
  const num = 3;
  const bar = () =&gt; {
    console.log(num);
  }
  return bar;
}
const baz = foo();
baz();</code></pre><p>여기서 클로저는 bar() 함수이다. 이 함수의 렉시컬 스코프는 자신이 생성된 foo()의 스코프라고 할 수 있다. baz라는 변수에 담긴 foo()의 스코프가 클로저인 bar의 렉시컬 스코프가 되기 때문이다. 따라서 baz()가 전역적으로 실행이 되더라도 <code>const num = 1</code>이라는 전역 변수를 가져오는 것이 아니라, 자신의 렉시컬 스코프인 foo() 함수 내의 <code>const num = 3</code>을 가져오게 된다.</p>
<p>다음의 함수를 보자.</p>
<pre><code>const increase = () =&gt; {
  let num = 0;
  return ++num;
}

console.log(increase());
console.log(increase());
console.log(increase());
console.log(increase());</code></pre><p>이 결과값은 어떻게 될까? 답은 1, 1, 1, 1이다. increase()라는 함수가 실행이 종료되면 가바지 컬렉터에서는 참조가 끝난 데이터를 없애버리기 때문에 항상 increase() 함수 안에 있는 num은 0으로 초기화된다.
그렇다면 다음의 함수는?</p>
<pre><code>const foo = () =&gt; {
  let num = 0;
  return () =&gt; {
    return ++num;
  }
}
const increase = foo();

console.log(increase());
console.log(increase());
console.log(increase());
console.log(increase());</code></pre><p>이 함수의 결과는 1, 2, 3, 4가 된다.
increase라는 변수에 foo()라는 함수를 담고, 그 안에는 클로저를 넣었다. 이 때의 클로저는 <code>num</code>이라는 변수를 가비지 컬렉터가 수거하지 않도록 참조하며 변수를 안전하게 유지해준다.
이러한 경우에 메모리를 계속 차지하고 있으므로 더이상 클로저를 사용하지 않을 때에는 null을 이용해서 메모리를 초기화해 주어야한다. </p>
<p><br><br><br><br><br><br></p>
<p>참고 자료
<a href="https://meetup.toast.com/posts/86">https://meetup.toast.com/posts/86</a>
코어자바스크립트 
자바스크립트 딥다이브
<a href="https://medium.com/dailyjs/i-never-understood-javascript-closures-9663703368e8">https://medium.com/dailyjs/i-never-understood-javascript-closures-9663703368e8</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React | 컴포넌트의 역할에 따른 구분]]></title>
            <link>https://velog.io/@edie_ko/React-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EC%9D%98-%EC%97%AD%ED%95%A0%EC%97%90-%EB%94%B0%EB%A5%B8-%EA%B5%AC%EB%B6%84</link>
            <guid>https://velog.io/@edie_ko/React-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EC%9D%98-%EC%97%AD%ED%95%A0%EC%97%90-%EB%94%B0%EB%A5%B8-%EA%B5%AC%EB%B6%84</guid>
            <pubDate>Mon, 01 Mar 2021 17:16:15 GMT</pubDate>
            <description><![CDATA[<h2 id="0-이런-구조로-만드는-것이-옳을까요-🧐">0. 이런 구조로 만드는 것이 옳을까요.. 🧐?</h2>
<p>지금까지 리액트를 처음 배우고 3개의 프로젝트를 진행하면서 그다지 의문을 갖지 않았었다.
하지만 점점 진행하는 프로젝트가 복잡해지면서 드는 의문이 있었다.
내가 만든 컴포넌트 구조가 옳은 것일까? 하는 의문.</p>
<p>오늘은 이러한 질문에서 시작되어 찾아본 자료들을 정리해본다.</p>
<h2 id="1-컴포넌트는-기능에-따라-2가지로-나뉜다">1. 컴포넌트는 기능에 따라 2가지로 나뉜다</h2>
<p>React Component를 구분하는 방식에는 여러가지가 있다.</p>
<blockquote>
<p>Container / Presentational
Fat / Skinny
Stateful / Stateless
Smart / Dumb
Pure / Unpure
etc...</p>
</blockquote>
<p>여러가지 구분이 있고 모두가 완벽히 같은 구분은 아니지만, 이렇게 이분하는 핵심은 하나다.
<strong><code>재사용성</code>과 <code>유지보수성</code>을 높이기 위함이다.</strong></p>
<p>리액트에서의 컴포넌트는 다양한 역할을 한다.
다양한 작업을 하나의 컴포넌트에서 처리한다면
가독성이 떨어지고 코드가 복잡해질 뿐더러 디버깅을 하거나 재사용하기 어렵다.
단순히 view와 action을 각각의 컴포넌트로 만든다고 생각하자.</p>
<p>다음은 리액트 개발자 Dan Abramov가 설명하는 Presentational Components와 Container Components을 기본 골조로 내가 사용했던 방법을 덧붙여 정리해보았다.</p>
<h2 id="2-컨테이너--프레젠테이셔널-컴포넌트">2. 컨테이너 &amp; 프레젠테이셔널 컴포넌트</h2>
<h3 id="1-컨테이너-컴포넌트-container-components-how-things-work">1) 컨테이너 컴포넌트 (Container Components): How things work.</h3>
<blockquote>
<p>A container does data fetching and then renders its corresponding sub-component. That&#39;s it.</p>
</blockquote>
<ul>
<li>Action: 어떻게 작동하는지를 담당한다. </li>
<li>프레젠테이셔널과 컨테이너 컴포넌트를 포함하고 있을 수는 있지만 자체적인 스타일이나 DOM 마크업을 갖고 있지 않다.</li>
<li>데이터와 다른 컴포넌트에 대한 함수를 제공한다.</li>
<li>data에 대한 state를 갖고 있으며 다른 프레젠테이셔널 컴포넌트에게 props로 데이터를 전달한다.</li>
</ul>
<h3 id="2-프레젠테이셔널-컴포넌트-presentational-components-how-things-look">2) 프레젠테이셔널 컴포넌트 (Presentational Components): how things look.</h3>
<ul>
<li>View: 어떻게 보이는지를 담당한다.</li>
<li>useState, dispatch, useCallback 등을 사용하지 않는다. 다만 자체적인 UI에 대한 state는 가질 수 있다.(data에 대한 state는 제외)</li>
<li>프레젠테이셔널과 컨테이너 컴포넌트를 포함할 수 있고 자체적인 DOM 마크업과 스타일을 가지고 있다.</li>
<li><code>this.props.children</code>을 통해 다른 컴포넌트로부터 props를 넘겨받는다.</li>
<li>다른 컴포넌트에 대한 의존성이 없다.</li>
<li>state, 라이프사이클 훅, 성능 최적화가 필요하지 않다면 functional components로 작성된다. </li>
</ul>
<h2 id="3-역할을-분배하는-것의-장점">3. 역할을 분배하는 것의 장점?</h2>
<p>한 달 정도 프레젠테이셔널 컴포넌트(이하 PC)와 컨테이너 컴포넌트(이하 CC)로 구분하여 프로젝트를 진행해 보았다. 앞서 제기한 의문과 더불어 디자인 패턴에 관심을 갖게 되면서, &#39;간단하게 PC와 CC를 구분하는 것에서부터 출발해보자...&#39;하는 생각에 시작한 것이었는데.</p>
<p>확실히 컴포넌트 작성에서부터 기능을 나눠서 생각하니 컴포넌트 재활용이 비교적 쉬워졌다고 해야할까... 
전에는 &#39;재사용성&#39;을 늘려보고 싶다라는 생각으로 무작정 써놓았던 컴포넌트를 재사용했는데, 후에 컴포넌트 규모가 커지면서 점점 복잡해지는 로직으로 골머리를 앓았었다..🤧 
하지만 컴포넌트 작성에서부터 컴포넌트의 목적과 기능을 중심으로 구분하여 재사용을 전제로 하고 출발하니 &#39;이렇게 컴포넌트를 재사용하는 것인가..🤔?&#39;라는 생각이 들 정도로 코드 작성이 보다 명확해진다.
다만 완벽하게 100% 분리하고는 있진 않다. 그 이유는, 1)컴포넌트를 분리한다고 해서 재사용할 수 있는 컴포넌트가 아닐 경우 혹은 2)기능과 UI가 밀접하게 관련이 있어서 분리하면 오히려 가독성이 떨어지는 경우가 있기 때문이다. </p>
<p>요즈음 컴포넌트 구조와 더불어 디렉토리 구조에 대한 고민도 하고 있는데, 
이에 대한 내용은 어느 정도 프로젝트가 마무리되면 정리해서 올려볼까 한다.</p>
<p>참고자료 
<a href="https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0#.c26mmyryu">https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0#.c26mmyryu</a>
<a href="https://kyounghwan01.github.io/blog/React/container-presenter-dessign-pattern/#presentational-container-%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%9E%80">https://kyounghwan01.github.io/blog/React/container-presenter-dessign-pattern/#presentational-container-%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%9E%80</a>
<a href="https://blog.naver.com/backsajang420/221368885149">https://blog.naver.com/backsajang420/221368885149</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JavaScript | 싱글스레드인 자바스크립트가 비동기 처리를 하는 법]]></title>
            <link>https://velog.io/@edie_ko/JavaScript-%EC%8B%B1%EA%B8%80%EC%8A%A4%EB%A0%88%EB%93%9C%EC%9D%B8-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EA%B0%80-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B2%98%EB%A6%AC%EB%A5%BC-%ED%95%98%EB%8A%94-%EB%B0%A9%EC%8B%9D</link>
            <guid>https://velog.io/@edie_ko/JavaScript-%EC%8B%B1%EA%B8%80%EC%8A%A4%EB%A0%88%EB%93%9C%EC%9D%B8-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EA%B0%80-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B2%98%EB%A6%AC%EB%A5%BC-%ED%95%98%EB%8A%94-%EB%B0%A9%EC%8B%9D</guid>
            <pubDate>Mon, 01 Mar 2021 16:05:06 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>자바스크립트 비동기 시리즈</strong>
<a href="https://velog.io/@edie_ko/JavaScript-Callback-%ED%95%A8%EC%88%98">Callback 함수</a>
<a href="https://velog.io/@edie_ko/JavaScript-Promise-%EA%B0%9C%EB%85%90%EB%B6%80%ED%84%B0-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-12">Promise 개념부터 이해하기 (1/2)</a>
<a href="https://velog.io/@edie_ko/JavaScript-Promise-%EA%B0%9C%EB%85%90%EB%B6%80%ED%84%B0-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-22">Promise 개념부터 이해하기 (2/2)</a>
<a href="https://velog.io/@edie_ko/JavaScript-%EB%B9%84%EB%8F%99%EA%B8%B0%EC%9D%98-%EA%BD%83-Async-Await">비동기의 꽃, Async와 Await</a>
...에 이어지는 글입니다😇</p>
</blockquote>
<p>내용 상으론 가장 맨 처음에 작성해야할 것 같은 내용이다.
콜백, Promise, Asnyc, Await을 말하기 이전에, 
왜 자바스크립트는 싱글스레드 언어이며 왜 비동기처리를 해주어야 하는지에 대해 정리해보자.</p>
<h2 id="자바스크립트는-싱글스레드-언어이다">자바스크립트는 싱글스레드 언어이다.</h2>
<p><code>one thread == one call stack == one thing at a time</code></p>
<p>자바스크립트는 <u>메인 스레드 하나 콜스택 하나</u>를 갖고 있는, <strong>싱글스레드</strong>로 동작하는 언어이다. 
싱글스레드 언어이기 때문에, 한 번에 하나의 작업만 수행할 수 있다. 다른 작업이 중간에 끼어들 수 없고, 기존에 수행하던 작업이 끝나야 다음 작업을 이어서 실행한다.</p>
<p>만약, 하나의 문서 안에 여러 개의 함수가 있다고 하자.
여러 함수 사이에 동영상을 불러오거나 미디어 파일을 서버로 보내는 등, 덩치가 큰 작업을 해야한다면?
이 함수를 처리하는 시간 동안 다음의 작업들을 동시에 미리 진행하도록 만들어줘야 한다.
(마치 세탁기를 돌려놓고 청소기를 돌리거나 설거지를 하는 것처럼)</p>
<p>이걸 자바스크립트는 어떻게 해결할까?
비동기 처리? 
<em>하지만 자바스크립트는 그 자체만으로 비동기 처리를 하지 않는다.</em>
그렇다면 비동기 요청은 어떻게 이뤄지는 것일까?
<br><br></p>
<h2 id="자바스크립트의-작동-방식">자바스크립트의 작동 방식</h2>
<p>자바스크립트의 런타임은 <code>메모리 힙(memory heap)</code>과 <code>콜스택(call stack)</code>으로 구성되어 있다.
아래 사진은 V8 엔진의 간략한 모습이다.</p>
<p><img src="https://images.velog.io/images/edie_ko/post/7286b363-2cb4-4353-bf9a-cca799378088/image.png" alt="">
<code>메모리 힙</code>: 메모리 할당을 담당.
<code>콜스택</code>: 함수의 호출 순서를 저장. 코드가 호출되면서 실행컨텍스트가 스택으로 쌓이는 곳. </p>
<p>V8(구글의 V8이 가장 유명한 자바스크립트 엔진)과 같은 자바스크립트 엔진은 하나의 <code>콜스택</code>을 사용한다. (하나의 콜스택, 즉 싱글스레드 언어.) 
이 <code>콜스택</code>에는 메인스레드에서 호출되는 함수들이 순차적으로 쌓인다. 이 함수들은 <strong>LIFO(Last In First Out) 방식</strong>으로 실행되면서 <code>콜스택</code>에서 사라진다. </p>
<p><u>차곡차곡 <code>콜스택</code>에 쌓이다가 비동기 처리를 하는 함수(e.g. <code>setTimeout()</code>)를 만나면 어떻게 될까?</u></p>
<p><img src="https://images.velog.io/images/edie_ko/post/1a6bbcb0-40d5-461d-9de9-d2f58613a62b/image.png" alt=""> </p>
<p>비동기 처리를 하는 함수가 <code>콜스택</code>에 들어오면 바로 <code>Web API</code>로 보내지고 <code>콜스택</code>에서는 사라진다. 비동기 함수는 자바스크립트 엔진을 구동하는 환경인, 브라우저나 Nods.js가 담당한다. </p>
<p>시간이 지난 후 <code>Web API</code>에서 처리가 완료된 비동기 함수는 일이 끝나면 그 결과와 콜백이 <code>callback queue</code>에 추가된다.
그리고 다시 <code>이벤트 루프</code>에 의해 <code>콜스택</code>에 쌓인다.
즉, 브라우저는 <code>콜스택</code>이 비워질 때마다 <code>callback queue</code>에서 가장 오래된 작업을 꺼내와 <code>콜스택</code>에 넣는데 이를 <code>이벤트 루프</code>라고 부른다.</p>
<h2 id="결국-비동기는-브라우저가-하는-것">결국 비동기는 브라우저가 하는 것</h2>
<p>실제 자바스크립트에는 setTimeout()과 같은 비동기 함수가 포함되어 있지 않다. 싱글스레드인 자바스크립트 엔진만으로는 이 비동기 함수를 처리할 수 없고 비동기 처리를 브라우저에게 위임한다. 
자바스크립트가 구동되는 환경은 브라우저 혹은 Node.js이며, 여기에서는 여러 개의 스레드가 사용되기 때문에 자바스크립트에게 스레드를 지원해주기 때문에 가능한 일이다. 그리고 여러 스레드가 존재하는 환경에서 하나의 콜스택을 사용하는 자바스크립트 엔진과 연동하기 위해 사용하는 장치가 바로 <code>이벤트 루프</code>이다.</p>
<p><img src="https://miro.medium.com/max/1400/1*sOz5cj-_Jjv23njWg_-uGA.gif" alt="">
<a href="https://blog.bitsrc.io/understanding-asynchronous-javascript-the-event-loop-74cd408419ff">사진 출처</a></p>
<p><a href="https://www.youtube.com/watch?v=8aGhZQkoFbQ">What the heck is the event loop anyway? | Philip Roberts | JSConf EU</a> 
이 전체적인 사이클을 이해하는 데 가장 도움이 되었던 영상이다.
(12:47 부터 보면 됩니다!)</p>
<p><br><br><br><br><br></p>
<p>참고자료
<a href="https://medium.com/@vdongbin/javascript-%EC%9E%91%EB%8F%99%EC%9B%90%EB%A6%AC-single-thread-event-loop-asynchronous-e47e07b24d1c">https://medium.com/@vdongbin/javascript-%EC%9E%91%EB%8F%99%EC%9B%90%EB%A6%AC-single-thread-event-loop-asynchronous-e47e07b24d1c</a>
<a href="https://meetup.toast.com/posts/89">https://meetup.toast.com/posts/89</a>
<a href="https://blog.sessionstack.com/how-does-javascript-actually-work-part-1-b0bacc073cf">사진 출처</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React | 이미지 업로드하기 (with Axios)]]></title>
            <link>https://velog.io/@edie_ko/React-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%97%85%EB%A1%9C%EB%93%9C%ED%95%98%EA%B8%B0-with-Axios</link>
            <guid>https://velog.io/@edie_ko/React-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%97%85%EB%A1%9C%EB%93%9C%ED%95%98%EA%B8%B0-with-Axios</guid>
            <pubDate>Mon, 15 Feb 2021 12:15:48 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/edie_ko/post/0695ef91-4edb-4211-890d-4ebd22630ab8/imageuploader.gif" alt=""></p>
<p>지난 프로젝트에서 로컬의 이미지 파일을 업로드하는 기능을 2번 구현했었다. 유저의 프로필을 바꾸기 위해 사용하는 기능이었고, 로컬의 이미지 파일을 업로드해서 이미지를 바꿀 수 있도록 만들었다. </p>
<p>1) 첫 번째는 Cloudinary라는 클라우드 서비스를 이용했고,
2) 두 번째 때에는 백엔드의 S3에 이미지를 업로드하는 방식이었다.</p>
<p>이 과정을 정리해보려고 한다.</p>
<h2 id="1-input-type--file">1. Input type = file</h2>
<p><img src="https://images.velog.io/images/edie_ko/post/64408a30-37a1-485d-a8db-67de7618dac9/image.png" alt="">먼저 <code>&lt;input type=&quot;file&quot;&gt;</code>를 이용해 파일 업로드 버튼을 만들어주었다.
<a href="https://developer.mozilla.org/ko/docs/Web/HTML/Element/Input/file">공식 문서</a>에서 볼 수 있듯이 <code>type=&#39;file&#39;</code>로 지정해주고 <code>accept=&#39;image/*&#39;</code> 속성을 넣어서 image 확장자만 선택적으로 업로드하도록 해주었다.
그리고 input을 숨겨버리기 위해 <code>display: none;</code> 속성을 주었다. 그대신 input에 useRef로 Ref를 걸어서 image upload button에 연결시켰다.</p>
<p><img src="https://images.velog.io/images/edie_ko/post/389db03b-5530-4fdc-96c5-bf4a65976a85/image.png" alt=""></p>
<h2 id="2-formdata">2. formData</h2>
<p>파일을 업로드 할 때에는 <code>FormData()</code> 객체를 이용한다. (<a href="https://developer.mozilla.org/ko/docs/Web/API/FormData/FormData">공식문서</a>)</p>
<blockquote>
<p>const formData = new FormData(form)</p>
</blockquote>
<p><code>FormData</code> 객체는 XMLHttpRequest를 사용하여 파일을 전송할 수 있도록 key-value 쌍들의 집합을 컴파일 해준다.
formData 객체에 파일을 추가하기 위해서는 <code>append(key, value)</code> 메서드를 이용해서 key-value 값을 추가해주면 된다. (이외에도 <code>delete()</code>, <code>get()</code>과 같은 메서드를 활용할 수 있다)</p>
<p>최종 코드는 아래와 같다. </p>
<p><img src="https://images.velog.io/images/edie_ko/post/a2c53593-0c97-450d-9e22-9301c38bb0e3/image.png" alt="">
최종적으로는 파일이 업로드 되는 시간 동안 로딩 스피너가 보여지도록 useState를 추가했다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TypeScript | Generic 제네릭 (feat. TypeScript 두 달차 후기)]]></title>
            <link>https://velog.io/@edie_ko/TypeScript-Generic-%EC%A0%9C%EB%84%A4%EB%A6%AD-feat.-TypeScript-%EB%91%90-%EB%8B%AC%EC%B0%A8-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@edie_ko/TypeScript-Generic-%EC%A0%9C%EB%84%A4%EB%A6%AD-feat.-TypeScript-%EB%91%90-%EB%8B%AC%EC%B0%A8-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Sun, 14 Feb 2021 09:26:56 GMT</pubDate>
            <description><![CDATA[<h2 id="0-타입스크립트-두-달-후기">0. 타입스크립트 두 달 후기</h2>
<p>타입스크립트를 사용한 지 두 달이 되어간다.
그동안 <strong>초보적인 수준에서</strong> 타입스크립트를 배우고 적용하고 적응해가는 과정이 <strong>예상했던 것보다</strong> 어렵지 않았다. 
내가 느꼈던 타입스크립트의 장점은,</p>
<p><strong>1) VSCode + TypeScript = 환상의 호흡</strong>: TypeScript를 지원하는 IDE를 사용하면 자동완성 기능이라든지, 툴팁으로 어떤 arguments들이 들어가야하는지를 알려주는 기능을 통해 코드를 작성하는 시간을 단축할 수 있었다. (특히 VSCode를 사용하면서 TypeScript에게 폴인럽..💛)</p>
<p><strong>2) 디버깅을 하기 전에 미리 에러 방지</strong>: 타입스크립트를 자바스크립트로 컴파일하는 과정에서 미리 타입 에러를 체크해서 에러가 있다면 아예 컴파일 조차 되지 않는다. 이런 이유로 런타임 전에 오류를 사전 체크할 수 있는 장점이 있었다. 또한 디버깅하면서도 오류를 바로잡을 때, 타입만 제대로 정의해주었다면 어떤 인자로 오류가 난 것인지 파악하기 수월했다.</p>
<p><strong>3) 수많은 documentation들</strong>: 막상 컴파일 에러가 발생하더라도, 타입스크립트 관련한 수많은 문서들과 스택오버플로우와 함께라면 크게 두렵지 않았다. 수많은 오류를 해결하면서 새로운 스택을 배우는 것에 대한 막연한 두려움을 쉽게 떨쳐버릴 수 있게 해준 과정이었다.</p>
<p>하지만 사용하면 할수록 <em>타입스크립트의 올바른 사용(?)에 대한 의문과 갈증</em> 같은 것이 생겼는데...
예를 들어 jsx에서의 event 타입 정의라든지, thrid party library의 타입 정의라든지(그동안 any를 남발하곤 했다).. 혹은 재사용하는 컴포넌트의 경우, 미리 타입을 정의해버리면 재활용하는 데에 있어서 난항을 겪곤 했다. 
그러면서 any를 써야하는 경우가 종종 생겼는데, 사실 any를 쓰면 기존 자바스크립트와 다를 것이 없기 때문에 내가 제대로 타입스크립트를 활용하고 있는 것인지 의문이 들었다🧐.. 
<br><br></p>
<p>앞으로 타입스크립트에 대해 공부하며..
올바른 사용을 지향하고 꾸준한 리팩토링을 하겠다고 약속하며..😇
오늘은 <u>제네릭</u>에 대해 정리해보려고 한다.</p>
<p><br><br><br></p>
<h2 id="1-제네릭이란-왜-제네릭을-써야하나">1. 제네릭이란? 왜 제네릭을 써야하나?</h2>
<p><u>Generic이란 데이터의 타입을 일반화한다(generalize)한다는 것을 뜻한다.</u>
Generic은 자료형을 정하지 않고 여러 타입을 사용할 수 있게 해준다.
즉, <strong>선언 시점이 아니라 생성 시점에 타입을 명시하여 하나의 타입만이 아닌 다양한 타입을 사용할 수 있도록 하는 기법</strong>이다. 한번의 선언으로 다양한 타입에 <u>&#39;재사용&#39;이 가능하다는 장점</u>이 있다.</p>
<p>제네릭을 쓰지 않을 경우, 불필요한 타입 변환을 하기 때문에 프로그램의 성능에 악영향을 미치기도 하는데, 제네릭을 사용하게되면 따로 타입 변환을 할 필요가 없어서 프로그램의 성능이 향상되는 장점이 있다. (<a href="https://post.naver.com/viewer/postView.nhn?volumeNo=29721395&amp;memberNo=10381152">링크</a>)</p>
<p><br><br><br></p>
<h2 id="2-generic이-없다면">2. generic이 없다면?</h2>
<p><img src="https://images.velog.io/images/edie_ko/post/f05da4ea-4856-49cf-b6a5-8ee8c7182373/image.png" alt="">위처럼 generic을 쓰지 않는다면, 1) 타입을 미리 지정하거나 2) any를 이용하여 구현할 수 있다. </p>
<p>1) 타입을 미리 지정하자면, 확실한 타입체크가 이뤄질 수 있겠지만 항상 number라는 타입을 받아야하므로 범용성이 떨어진다. 
2) 그렇다고 any를 사용한다면 자료의 타입을 제한할 수 없을 뿐더러, 이 function을 통해 어떤 타입의 데이터가 리턴되는지 알 수 없다.</p>
<p>이런 경우에 사용할 수 있는 것이 제네릭이다.
<br><br><br></p>
<h2 id="3-generic-이렇게-써보자">3. generic 이렇게 써보자!</h2>
<h4 id="1-generic-기본적-사용">1) Generic 기본적 사용</h4>
<p><img src="https://images.velog.io/images/edie_ko/post/e3daaa93-7578-4ecf-94cb-8f026aa77e70/image.png" alt=""><code>&lt;T&gt;(arg: T):T</code> 여기서 <code>&lt;T&gt;</code>는 Type의 약자로 제네릭을 선언할 때 T를 관용적으로 사용한다. 여기에서 다이아몬드 연산자 안에 있는 T는 타입변수라고 한다.
이제 이 identify 함수는 T라는 타입 변수를 갖게 된다. 그리고 argument와 return의 type은 T라는 타입 변수로 동일하다. </p>
<p>만약 여기서 length를 확인하는 코드를 쓰면 어떻게 될까?<img src="https://images.velog.io/images/edie_ko/post/7b0a2881-803b-427e-9e2d-c1389de4c46a/image.png" alt=""> 에러가 난다. 왜냐하면 입력값과 리턴값의 타입을 동일하게 정해두기만 했으므로, 인자에 length 프로퍼티가 없는 number 타입이 들어왔을 때 유효하지 않기 때문이다. 따라서 다음과 같이 세부적으로 변수 타입을 지정해줄 수 있다. <img src="https://images.velog.io/images/edie_ko/post/f38c8dd1-ffc2-4ecb-9888-f3452023a5e5/image.png" alt=""> 이제 이 함수는 T라는 변수 타입을 받고, 인자로는 배열 형태의 T를 받는다. 즉, <code>identify([1, 2, 3])</code>과 같은 형태로 사용할 수 있다.
ps. 혹은 <code>&lt;T extends Lengthwise&gt;</code>처럼 Lengthwise를 extends해서 사용하면 부분적으로 length property가 있는 타입의 경우 에러 없이 사용할 수 있다. </p>
<h4 id="2-두-개-이상의-generic-사용하기">2) 두 개 이상의 Generic 사용하기</h4>
<p><img src="https://images.velog.io/images/edie_ko/post/99cea239-52d1-4999-ba19-92926ee91dfe/image.png" alt="">두 변수의 타입이 다를 경우 두 가지의 타입 변수가 필요하다. 이 때 제네릭을 사용할 수 있다. 관용적으로 사용하는 T 다음으로 오는 알파벳을 순서대로 사용하면 된다. </p>
<h4 id="3-상속-이용하기-extends">3) 상속 이용하기 (extends)</h4>
<p><img src="https://images.velog.io/images/edie_ko/post/38a3a699-ba0d-439b-a032-438c80c9cd88/image.png" alt="">타입스크립트 작성 중 메서드 관련 오류가 날 때가 잦은데, 이런 방법을 사용할 수도 있었다. (<a href="https://darrengwon.tistory.com/802">출처</a>) 위는 toString 메서드를 가지고 있는 객체를 extends해서 에러를 해결하는 예시이다. 필요한 메서드를 추가하는 것뿐만 아니라 하나의 메소드만 사용가능하도록 제한할 수도 있다. </p>
<h4 id="4-조건부-타입으로-generic-사용하기">4) 조건부 타입으로 Generic 사용하기</h4>
<p><img src="https://images.velog.io/images/edie_ko/post/5905b74a-009c-401c-b9ea-fb737be14fba/image.png" alt="">위처럼 특정 인터페이스 내에서 extends와 삼항 연산자를 이용해서 타입을 지정할 수도 있다.</p>
<h4 id="5-jsx에서-arrow-function으로-제네릭-사용하기">5) JSX에서 arrow function으로 제네릭 사용하기</h4>
<p><code>const foo = &lt;T extends {}&gt;(x: T):T =&gt; x</code>를 사용하면 된다. 다이아몬드 연산자가 HTML태그가 아니라 제네릭이라는 힌트를 주기 위해 <code>extends {}</code>를 사용한다.
다만 이 경우, T가 object에 제한되기 때문에 type-safety를 위해 <code>extends unknown</code>을 사용할 수 있다. 
<code>const foo = &lt;T extends unknown&gt;(x: T) =&gt; x</code></p>
<p><br><br><br></p>
<h3 id="참고자료">참고자료</h3>
<p><a href="https://www.typescriptlang.org/docs/handbook/generics.html">https://www.typescriptlang.org/docs/handbook/generics.html</a>
<a href="https://www.xspdf.com/help/52564514.html">https://www.xspdf.com/help/52564514.html</a>
<a href="https://post.naver.com/viewer/postView.nhn?volumeNo=29721395&amp;memberNo=10381152">https://post.naver.com/viewer/postView.nhn?volumeNo=29721395&amp;memberNo=10381152</a>
<a href="https://muang-kim.tistory.com/86">https://muang-kim.tistory.com/86</a>
<a href="https://darrengwon.tistory.com/802">https://darrengwon.tistory.com/802</a>
<a href="https://hyunseob.github.io/2017/01/14/typescript-generic/">https://hyunseob.github.io/2017/01/14/typescript-generic/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React | 유지, 보수를 위한 API 통신은 어떻게 할까? (Axios 모듈화)]]></title>
            <link>https://velog.io/@edie_ko/React-%EC%9C%A0%EC%A7%80-%EB%B3%B4%EC%88%98%EB%A5%BC-%EC%9C%84%ED%95%9C-API-%ED%86%B5%EC%8B%A0%EC%9D%80-%EC%96%B4%EB%96%BB%EA%B2%8C-%ED%95%A0%EA%B9%8C-Axios-%EB%AA%A8%EB%93%88%ED%99%94</link>
            <guid>https://velog.io/@edie_ko/React-%EC%9C%A0%EC%A7%80-%EB%B3%B4%EC%88%98%EB%A5%BC-%EC%9C%84%ED%95%9C-API-%ED%86%B5%EC%8B%A0%EC%9D%80-%EC%96%B4%EB%96%BB%EA%B2%8C-%ED%95%A0%EA%B9%8C-Axios-%EB%AA%A8%EB%93%88%ED%99%94</guid>
            <pubDate>Sun, 03 Jan 2021 14:23:43 GMT</pubDate>
            <description><![CDATA[<p>요즈음 Node.js 세션을 통해 배운 &#39;모듈화&#39;개념과, 기업 협업 프로젝트에서 피드백 받은 &#39;API 통신 방식&#39;에 대해 기록해보려고 한다.</p>
<p>최근에 배웠던 Node.js 세션에서 가장 인상 깊었던 부분은 <strong><code>Why do we modularize our applications?</code></strong> 에 대한 내용이었다.</p>
<h2 id="왜-우린-모듈화를-해야할까">왜 우린 모듈화를 해야할까?</h2>
<blockquote>
</blockquote>
<ul>
<li><strong>확장성(extensibility)</strong>
확장성을 고려하지 않은 코드는 시스템의 규모가 커질수록 문제가 생길 확률이 높다.</li>
<li><strong>재사용성(reusability)</strong>
  반복되는 로직을 함수로 분리하는 코드상의 재사용성 뿐만 아니라, 우리가 설계한 구조가 재사용 되어야 한다.</li>
<li><strong>유지-보수 가능성(maintability)</strong>
  여러 로직이 뒤엉켜 있는 코드는 유지 보수가 안된다.  </li>
<li><strong>가독성(readability)</strong>
어려운 로직 일수록 더 가독성이 높아야 한다. 어려운 로직을 쉽고 간단하게 구현하는 것이 좋은 코드다. 
프로젝트의 구조 또한 한 눈에 그려져야 한다.</li>
<li><strong>테스트 가능성(testability)</strong>
  테스트를 하기 쉬운 코드는 모듈화가 잘 되어 있고, 한 가지 역할만 하는 함수 단위의 코드를 의미한다.
  프로젝트의 구조도 추상화가 잘 되어있고, 역할이 잘 나뉘어 있는 구조가 테스트하기 쉬운 구조다.
   <em>출처: 깔끔한 파이썬 탄탄한 백엔드 - 송은우 저</em></li>
</ul>
<p>출처: 위코드 Node.js Notion   (by 최 준 멘토님)
 <br>
 <br>
 이 수업을 들은 뒤부터는 보다 확장성을 위한, 재사용이 가능한, 유지와 보수에 쉬운 컴포넌트를 만드려고 노력하고 있다. 
하지만 정작 가장 자주 쓰이고, 똑같은 코드를 반복하는 &#39;API 통신&#39;에 있어서 모듈화를 하지 않았고 이에 대해 좋은 피드백을 받았다.
<br></p>
<h2 id="기존에-사용하던-api-통신-방식은-다음과-같다">기존에 사용하던 API 통신 방식은 다음과 같다.</h2>
<p>먼저 config.js에 server로 변수를 지정해 서버 IP 주소를 넣어준다. (이 또한 환경변수로 .env에 숨겨준다). 그리고 각각의 API 변수에 엔드포인트를 넣어서 export해서 사용하는 방식이었다.
<img src="https://images.velog.io/images/edie_ko/post/42d0a634-d75c-4abc-a6ac-678087ce1327/image.png" alt="">
그리고 try catch에 async await으로 작성하거나 다음과 같이 then then으로 비동기 처리해주는 방식..<em>(코린이 티😇)</em>
<img src="https://images.velog.io/images/edie_ko/post/ea489cbb-8e82-4c6b-8d66-fc072ee6b178/image.png" alt=""></p>
<br>

<h2 id="자-일단-받은-피드백을-정리해보자면">자 일단, 받은 피드백을 정리해보자면</h2>
<h3 id="1-server로-export하지-않고-server-주소가-들어가있는-apiclient-함수를-만들어서-export하자">1) {SERVER}로 export하지 않고 server 주소가 들어가있는 apiClient 함수를 만들어서 export하자!</h3>
<p>당시 query 검색을 위해서 그냥 서버주소를 통채로 가져와 query만 변수 처리를 해주는 리터럴 방식을 쓰고 있었다. 
<em>(url: <code>${SERVER}/map/search?search=${query}</code> 이런식으로..다가)</em>
하지만 이렇게 쓰게 되면 백엔드와 통신할 일이 굉장히 많기 때문에 SERVER를 매번 넣어주는 것 보다 axios와 기본 세팅이 되어 있는 apiClient 훅을 만들어서 사용하는 것이 좋겠다는 아-아주 유용한 피드백을 받았다.</p>
<h3 id="2-async-await을-쓰자">2) async, await을 쓰자</h3>
<p>코드 가독성에는 async, await으로 간결하게 작성하는 것이 최고겠쥬..
fetch then then으로 처음 api 통신을 배워서 아직까지도 then을 애용하던 나였다...</p>
<br>

<h2 id="적용해보자">적용해보자!</h2>
<h3 id="1-apiclientts를-만들었다">1) apiClient.ts를 만들었다.</h3>
<p><strong>axios.create() 메소드</strong>를 이용해서 axios 인스턴스를 생성하도록 만들어준다.
<a href="https://xn--xy1bk56a.run/axios/guide/api.html#%EB%8F%99%EC%8B%9C%EC%84%B1-concurrency">공식문서 참고</a></p>
<pre><code>const instance = axios.create({
  baseURL: &#39;https://some-domain.com/api/&#39;,
  headers: { &#39;X-Custom-Header&#39;: &#39;foobar&#39; },
  timeout: 1000,
});</code></pre><p>저 <strong><code>axios.create()</code></strong>이 핵심..!!✨
저렇게 axios의 .create() 내장 함수를 이용하면 통신할 때 필요한
쿼리나, 헤더, 바디 등등 여러 데이터를 함께 보낼 수 있다.</p>
<p>저렇게 baseURL에 SERVER 주소를 넣어주면 된다. instance에 넣을 수 있는 키값으로 params, data, responseType, xsrfHeaderName 같은 것들을 넣어줄 수 있어서 정말 편리하다!</p>
<p><img src="https://images.velog.io/images/edie_ko/post/802504d7-3e11-48ad-9e50-46d333847118/image.png" alt=""></p>
<p>(이제 이 ts 함수는 재사용성에 있어서 최고, 가장 많이 사용한 훅이 된다..)</p>
<h3 id="2-사용할-때에는-다음과-같이-사용하면-된다">2) 사용할 때에는 다음과 같이 사용하면 된다.</h3>
<p><img src="https://images.velog.io/images/edie_ko/post/19f0cb22-00ff-459a-98c6-05ebf533ba78/image.png" alt=""></p>
<p>이렇게 endpoint와 query만 변수로 넣어주면 된다. 
지금 보니 try catch문도 함께 넣어버리면 될 텐데 왜 저기서 그쳐버렸지..?하는 생각도 든다.. 🤭</p>
<h2 id="정리">정리</h2>
<p>코딩을 배우면 배울수록 재사용이 가능한 코드. 누가 읽어도 가독성이 높은 그런 코드를 작성하고 싶어진다. 마치 레고 조각처럼 어디에든 들어가도 그 기능을 수행할 수 있는 그런 확장성 있는 코드. 요즘 들어 기능 구현에 급급한 그런 코드가 아니라 정성이 묻어난 코드를 작성하고 싶은 욕구가 있었는데, </p>
<p>이런 생각에서 가장 의미 있고 중요한 피드백과 세션 내용이었다.
(준님과 우성님께 감사..🙏🏻)</p>
<p>물론 이런 코드를 작성하기 위해서 그냥 코드를 나열하는 것보다 몇 배의 시간이 들지만... 이번 프로젝트를 통해 내가 아닌 다른 사람을 위한 코드, 오늘이 아닌 내일을 위한 코드를 작성하기 위해 계속 노력해보겠다🔥</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React | 컴포넌트 성능 향상 시키기 (feat. Lodash throttle & debounce)]]></title>
            <link>https://velog.io/@edie_ko/React-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%84%B1%EB%8A%A5-%ED%96%A5%EC%83%81-%EC%8B%9C%ED%82%A4%EA%B8%B0-feat.-Lodash-throttle-debounce</link>
            <guid>https://velog.io/@edie_ko/React-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%84%B1%EB%8A%A5-%ED%96%A5%EC%83%81-%EC%8B%9C%ED%82%A4%EA%B8%B0-feat.-Lodash-throttle-debounce</guid>
            <pubDate>Sun, 03 Jan 2021 13:08:53 GMT</pubDate>
            <description><![CDATA[<p>KAKAOMAP API를 이용해 프로젝트를 진행 중이다.</p>
<p>지난 프로젝트에서 카카오맵 API를 사용했지만, 좌표를 props로 받아와 띄우는 정적인 지도에 지나지 않았다. 그래서 이번에 동적인 지도를 구현하는 데에 있어서, 렌더링이나 성능 개선에 더욱 신경을 쓰면서 개발하는 데에 초점을 맞췄다.
<em>(버벅이는 지도 상상하기도 싫엇 😭..)</em></p>
<p>특히 지도 API에서 지도를 드래그하거나 줌인, 줌아웃을 하면서 여러 기능을 수행하게 된다. 이 과정에서 상당히 빈번한 렌더링이 실행되기 때문에, 예전부터 곁눈질로 봐오던 Lodash의 throttle과 debounce를 적용하기로 했다.
<strong>throttle은 지도 맵의 렌더링</strong>에 적용하였고, <strong>debounce는 쿼리 검색 기능</strong>에 적용하였다.</p>
<h2 id="0-lodash란">0. Lodash란?</h2>
<blockquote>
<p>A modern JavaScript utility library delivering modularity, performance &amp; extras.
모듈화, 성능 및 기타 기능을 제공하는 자바스크립트 유틸리티 라이브러리
<a href="https://lodash.com/">Lodash 공식 문서</a></p>
</blockquote>
<p>공식 문서를 보면 정말 깔끔하게 설명이 잘 되어 있고, 다음에 쓰고 싶은 기능들도 많이 보인다.
과거에는 underscore 라이브러리가 많이 쓰였지만, 현재는 lodash가 더 많이 쓰이고 성능이 뛰어나다는 평가를 받는다.</p>
<h2 id="1-debounce-vs-throttle-">1. debounce vs throttle ?</h2>
<p>debounce와 throttle은 이벤트를 반복적으로 실행할 때, 콜백 함수의 불필요한 실행을 줄여주는 역할을 한다. 불필요한 서버 리퀘스트를 막을 수도 있고, 불필요한 통신을 줄임과 동시에 필요 없는 렌더링 또한 막을 수 있어 컴포넌트의 성능 개선에도 도움을 준다.
특히 외부 API를 사용할 경우 일일 할당량에 제한이 있는 경우가 있는데, 과도한 서버 요청을 막아줄 수 있다는 면에서 필수적으로 사용해야 할 기능이라고 생각한다. 
<em>(나야 나.. YOUTUBE API 일일 할당량 초과해본 사람 🙋🏻‍♀️)</em></p>
<h3 id="1-debounce">1) debounce</h3>
<pre><code>_.debounce(func, [wait=0], [options={}])</code></pre><p>debounce는 이벤트가 <strong>끝난 뒤</strong>에 설정해둔 <strong>시간(wait)이 지나야 콜백(func)이 실행</strong> 된다. 아래 사진을 보면 여러 이벤트들이 하나로 그룹지어지는데 이게 바로 debounce다. 
<img src="https://images.velog.io/images/edie_ko/post/282c25e5-d84d-41dc-93f9-7ea642299503/image.png" alt=""></p>
<h3 id="2-throttle">2) throttle</h3>
<pre><code>_.throttle(func, [wait=0], [options={}])</code></pre><p>throttle은 <strong>콜백 함수(func)를 일정 주기(wait) 내에 한 번만</strong> 호출한다. 특히 scroll, mousemove 이벤트와 같이 짧은 시간에 굉장히 많이 실행되는 이벤트에 사용한다.</p>
<h2 id="2-검색-기능-debounce-적용하기">2. 검색 기능 debounce 적용하기</h2>
<p>먼저 일반적인 검색 input 태그에 onChange로 handleSearch func를 달아 console.log(query)로 찍힌 결과를 보자.</p>
<p><img src="https://images.velog.io/images/edie_ko/post/c924ba11-7b99-4cd4-8a78-e2f699b0ede3/search.gif" alt="">
신사역을 검색하는 데에 자그마치 9개의 query event가 발생했다.
<img src="https://images.velog.io/images/edie_ko/post/47352e8d-7c0e-4f83-86c5-4597f639f6e5/image.png" alt=""></p>
<p>다음은 debounce를 적용해보자.
먼저 <code>import _ from &#39;lodash&#39;</code>를 해준다. 그리고 아래와 같이 input에 ref를 달아준다.
<img src="https://images.velog.io/images/edie_ko/post/8bfdb993-a04f-4970-afdd-6ea1d6a377f3/image.png" alt="">
onChange 이벤트에 <code>handleSearchChange</code>를 달아주되, <code>delayedQueryCall</code>(debounce)을 거쳐 <code>sendQuery</code>(axios)해서 불필요한 통신을 막아준다.
<img src="https://images.velog.io/images/edie_ko/post/b0bc9413-8b68-4974-b440-f035fbe65574/image.png" alt=""><img src="https://images.velog.io/images/edie_ko/post/7b66ae71-479e-4f1f-9336-365908882df5/debounce.gif" alt=""></p>
<p>&#39;신사&#39;를 검색할 때 불필요하게 검색이 되었던 &#39;ㅅ&#39;, &#39;시&#39;, &#39;신ㅅ&#39;같은 쿼리는 없고, 정확하게 &#39;신사&#39; 혹은 &#39;신사역&#39;, &#39;신&#39;을 검색할 수 있게 되었다.</p>
<p>useEffect()의 current property로 접근하는 것이 불필요하다고 여겨질 경우 다음과 같이 리팩토링할 수 있다.</p>
<pre><code>  const delayedQueryCall = useCallback&lt;any&gt;(
  _.debounce((q) =&gt; sendQuery(q), 500), []);    </code></pre><h2 id="3-지도-렌더링-throttle-적용하기">3. 지도 렌더링 throttle 적용하기</h2>
<p>지도 또한 마찬가지이다. 먼저 throttle을 적용하지 않은 결과다. 
스크롤 이벤트, 드래그 이벤트가 이렇게 무시무시하다.
<img src="https://images.velog.io/images/edie_ko/post/ea915e27-9e47-4bbe-b38e-1d11e0710323/mapp.gif" alt=""></p>
<p>불필요한 렌더링과 서버 요청을 막기 위해, 지도의 좌표를 찾는 addListener(카카오맵 API)에 <code>_.throttle</code>을 적용해주었다.
<img src="https://images.velog.io/images/edie_ko/post/793bb682-890b-4a8f-b173-4843fbd4eacd/image.png" alt=""></p>
<p>결과를 보자.
<img src="https://images.velog.io/images/edie_ko/post/e7815f56-068e-4df1-b8f7-c51c182326ba/throttle.gif" alt=""></p>
<p>콘솔창을 보면 확연히 다른 결과가 느껴진다. 
<em>(뭐랄까.. 촐랑대지 않고 절도있는 느낌이랄까..ㅎㅎ🤷🏻‍♀️)</em></p>
<h2 id="4-정리">4. 정리</h2>
<p><strong>debounce는 이벤트가 끝날 때까지 기다렸다가 시작된다는 점, throttle은 이벤트가 시작되면 일정 주기로 계속 실행한다는 점이 다르다.</strong> 
따라서 확실한 성능 개선을 위해서는 _debounce를 사용하여 이벤트를 한 번만 실행되도록 하는 것이 효과적_일 것이다. 
하지만 _유저가 즉각적인 결과를 요하는 기능에 있어서는 throttle을 사용_하여 유저를 배려합시다! 
다음에는 lodash의 다른 메소드들도 사용해보고 후기를 남겨보겠다.</p>
<p><br><br><br><br><br>
참고자료
<a href="https://lodash.com/">Lodash 공식 문서</a>
<a href="https://css-tricks.com/debouncing-throttling-explained-examples/">Debouncing and Throttling Explained Through Examples</a>
<a href="https://rajeshnaroth.medium.com/using-throttle-and-debounce-in-a-react-function-component-5489fc3461b3">Using throttle and debounce in a React function component</a>
<a href="https://thisblogfor.me/web/throttle_debounce/">throttle &amp; debounce 폴리필</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TypeScript | 기본 타입]]></title>
            <link>https://velog.io/@edie_ko/TypeScript-%EA%B8%B0%EB%B3%B8-%ED%83%80%EC%9E%85</link>
            <guid>https://velog.io/@edie_ko/TypeScript-%EA%B8%B0%EB%B3%B8-%ED%83%80%EC%9E%85</guid>
            <pubDate>Tue, 22 Dec 2020 16:51:54 GMT</pubDate>
            <description><![CDATA[<p>오늘은 타입스크립트의 기본 중의 기본만 요약 정리하며 정리하는 글이다.
(불친절할 수도 있는 너무도 간단한 글!)</p>
<h2 id="타입스크립트-기본-타입">타입스크립트 기본 타입</h2>
<p><img src="https://images.velog.io/images/edie_ko/post/dcb62bb0-0883-4552-aa0e-b6bf1280b1fa/image.png" alt=""></p>
<h2 id="타입스크립트의-함수">타입스크립트의 함수</h2>
<p><img src="https://images.velog.io/images/edie_ko/post/041c15a1-87c9-4b5a-aac2-99beb33aab99/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React + TypeScript | JavaScript에서 TypeScript로 변환 시 에러 모음]]></title>
            <link>https://velog.io/@edie_ko/React-TypeScript-JavaScript%EC%97%90%EC%84%9C-TypeScript%EB%A1%9C-%EB%B3%80%ED%99%98-%EC%97%90%EB%9F%AC-%EC%84%A0%EB%AC%BC-%EC%84%B8%ED%8A%B8</link>
            <guid>https://velog.io/@edie_ko/React-TypeScript-JavaScript%EC%97%90%EC%84%9C-TypeScript%EB%A1%9C-%EB%B3%80%ED%99%98-%EC%97%90%EB%9F%AC-%EC%84%A0%EB%AC%BC-%EC%84%B8%ED%8A%B8</guid>
            <pubDate>Mon, 21 Dec 2020 14:35:26 GMT</pubDate>
            <description><![CDATA[<p>오늘...
기존 <strong>JavaScript로 작성했던 프로젝트(초기 단계)를 TypeScript</strong>로 리팩토링하는 데에 성공했다. 
오늘내로 컴파일이 될까 싶을 정도로 _에러 화수분_이었던 상황...
오전 10시부터 시작한 에러 대환장파티를 오후 4시에 마무리 짓는 데에 성공한다.
그 과정에서 기억하고 싶은 에러를 담아본다.
(TypeScript를 배운 지 3일차 타둥이의 글입니다.. 저와 같은 타입스크립트 스타터들을 위한 포스트임을 감안해주시길 바랍니다..🥺)
<br><br></p>
<h2 id="시작하기-전에">시작하기 전에</h2>
<p>JavaScript로 작성하던 React 프로젝트를 TypeScript로 리팩토링하기 위해서는,</p>
<ol start="0">
<li><p>초기 CRA 명령어인 <code>$ npx create-react-app ts-react-tutorial --typescript</code>이 아니라,
<code>npm install --save typescript @types/node @types/react @types/react-dom @types/jest</code> 혹은 <code>yarn add typescript @types/node @types/react @types/react-dom @types/jest</code> 이 명령어를 사용해야 합니다. (<code>yarn add -g typescript</code>도 필요합니다)</p>
</li>
<li><p>js와 jsx 파일을 모조리 ts, tsx로 바꾸어 주세요.</p>
</li>
<li><p>tsconfig.json 파일을 작성해야 합니다. (참고: <a href="https://typescript-kr.github.io/pages/tsconfig.json.html">tsconfig.json 사용하기</a>)</p>
</li>
<li><p>기존에 사용하던 JavaScript용 라이브러리는 TypeScript용을 찾아서 다시 설치해야 합니다.
<code>&quot;@types/lodash&quot;: &quot;^4.14.149&quot;, &quot;@types/react-slick&quot;: &quot;^0.23.4&quot;,  &quot;@types/redux-logger&quot;: &quot;^3.0.8&quot;</code> 등등...</p>
</li>
<li><p>자 이제 터미널에 뜨는 컴파일 오류를 하나씩 없애나가면 됩니다.</p>
</li>
</ol>
<p>+@ IDE로 VSCode를 사용하시는 경우, 이 과정에서 VSCode가 정신을 못차립니다. 서버를 다시 껐다가 재시동하는 방법, node_modules를 삭제해서 다시 yarn install 해주시거나, VSCode 창을 완전히 껐다가 다시 켜는 방법.. 등 온갖 수단과 방법을 가리지 말고 시도해보세요 😇 </p>
<p><br><br></p>
<h2 id="1-property--does-not-exist-on-type-window--typeof-globalthis--ts2339">1. Property &#39;**&#39; does not exist on type &#39;Window &amp; typeof globalThis&#39;.  TS2339</h2>
<p>Property &#39;kakao&#39; does not exist on type &#39;Window &amp; typeof globalThis&#39;.  TS2339</p>
<pre><code>declare global {
  interface Window {
    kakao: any;
  }
}
const { kakao } = window;</code></pre><p>외부 API인 kakao를 사용할 때, window 객체에 대한 정의가 필요했던 상황.
<strong>window의 interface를 선언</strong>함으로써 간단히 해결</p>
<p><br><br></p>
<h2 id="2-parameter-event-implicitly-has-an-any-typets7006">2. Parameter &#39;event&#39; implicitly has an &#39;any&#39; type.ts(7006)</h2>
<p>div에 onClick 이벤트를 걸어놓은 상황에서 event의 타입을 정의해야 했던 상황.
event: any로 처리해도 컴파일이 가능했지만 확실한 해결책을 찾고 싶었다.
결과적으로 다음과 같은 interface를 export해서 해당 컴포넌트에 import 한 후 사용하는 방법을 택했다.</p>
<pre><code>export interface BaseSyntheticEvent&lt;E = object, C = any, T = any&gt; {
  nativeEvent: E;
  currentTarget: C;
  target: T;
  bubbles: boolean;
  cancelable: boolean;
  defaultPrevented: boolean;
  eventPhase: number;
  isTrusted: boolean;
  preventDefault(): void;
  isDefaultPrevented(): boolean;
  stopPropagation(): void;
  isPropagationStopped(): boolean;
  persist(): void;
  timeStamp: number;
  type: string;
}</code></pre><pre><code>  const tabHandler = (event: BaseSyntheticEvent): void =&gt; {
    if (!isWideTab) {
      setWideTab(!isWideTab);
    }
    const newTabState = { ...tabState };
    const activeTab = event.currentTarget.id;
    for (let key in newTabState) {
      key === activeTab
      ? (newTabState[key as keyof typeof newTabState] = true)
      : (newTabState[key as keyof typeof newTabState] = false);
    }
    setTabState(newTabState);
  };</code></pre><p>  <a href="https://github.com/DefinitelyTyped/DefinitelyTyped/blob/14d95eb0fe90f5e0579c49df136cccdfe89b2855/types/react/index.d.ts#L1142-L1158">참고사이트</a></p>
<p>  <br><br></p>
<h2 id="3-ts7053-element-implicitly-has-an-any-type-because-expression-of-type-string-cant-be-used-to-index-type">3. TS7053: Element implicitly has an &#39;any&#39; type because expression of type &#39;string&#39; can&#39;t be used to index type</h2>
<p>Type &#39;{ tabHome: boolean; tabBookmark: boolean; tabEtc: boolean; }&#39; cannot be used as an index type.  TS2538
객체를 갖고 노는 for ..in 문 작성 시 나타났던 에러다.</p>
<pre><code>    for (let key in newTabState) {
      key === activeTab
      ? (newTabState[key as keyof typeof newTabState] = true)
      : (newTabState[key as keyof typeof newTabState] = false);
    }</code></pre><p>2번 에러와 같은 코드 블럭 내의 에러였고 key를 객체(newTabState)의 keyof typeof로 정의하여 키의 값을 받아와 해결.
<a href="https://stackoverflow.com/questions/55377365/what-does-keyof-typeof-mean-in-typescript">참고 자료</a></p>
<p><br><br></p>
<h2 id="4-ts2533-object-is-possibly-null-or-undefined">4. TS2533: Object is possibly &#39;null&#39; or &#39;undefined&#39;</h2>
<pre><code>  const handleSlide = (pixel: any) =&gt; {
    selectorRef?.current?.scrollTo({
      left: selectorRef?.current?.scrollLeft + pixel,
      behavior: &#39;smooth&#39;,
    });
  };</code></pre><p>null과 undefined 처리를 해주어야 하는 상황. 옵셔널 체이닝으로 디버깅 완료.
<a href="https://stackoverflow.com/questions/40349987/how-to-suppress-error-ts2533-object-is-possibly-null-or-undefined">참고 자료1</a>
<a href="https://stackoverflow.com/questions/57086672/element-implicitly-has-an-any-type-because-expression-of-type-string-cant-b">참고 자료2</a>
<br><br></p>
<h2 id="5-property--does-not-exist-on-type-never--ts2339">5. Property &#39;**&#39; does not exist on type &#39;never&#39;.  TS2339</h2>
<p>TypeScript로 useState작성 시에 <code>const selectorRef = useRef(null);</code>라고만 작성해도 무방하다. 굳이 <code>const selectorRef = useRef&lt;타입&gt;(null);</code> 타입을 작성하지 않아도 되는 이유는 알아서 타입을 유추하기 때문이다. 
다만 ref가 null일 수도 있는 상황에서는 반드시 Generics를 사용해서 표기해주어야 한다.</p>
<pre><code>  const selectorRef = useRef&lt;HTMLDivElement&gt;(null);</code></pre><p><a href="https://velog.io/@velopert/using-hooks-with-typescript">타입스크립트로 리액트 Hooks 사용하기 (useState, useReducer, useRef)</a></p>
<p><br><br></p>
<h2 id="6-cannot-use-jsx-unless-the---jsx-flag-is-provided">6. Cannot use JSX unless the &#39;--jsx&#39; flag is provided</h2>
<p>node_modules를 지워봐도... VSCode를 껐다가 켜는 원시적인 방법을 사용했지만 해결되지 않았던 에러다.
먼저 command + shift + p를 누르고 TypeScript라고 치면 다음과 같은 창이 뜬다. Select a TypeScript Version..을 클릭.
TypeScript version을 4.1.2로 바꾸어 주면 해결!</p>
<p><img src="https://images.velog.io/images/edie_ko/post/1fb5d90e-9ab9-4b75-8ac1-ec554da2e58d/image.png" alt=""><img src="https://images.velog.io/images/edie_ko/post/916b6ffa-9e18-4f99-ab49-5221e6de0155/image.png" alt="">
<a href="https://stackoverflow.com/questions/50432556/cannot-use-jsx-unless-the-jsx-flag-is-provided">참고 자료</a></p>
<p><br><br></p>
<p>이상 ... 자바스크립트로 쓰인 리액트 프로젝트를 타입스크립트로 변환하는 좌충우돌 우왕좌왕 후기였다.
솔직히 나중에 진이 빠져서 any를 남발한 경향이 조금 있긴 하지만😇, 앞으로의 리팩토링을 거쳐서 빈틈 없는 타입 정의로 디버깅이 필요없는 튼튼한 프로젝트를 만들고싶다!💪</p>
<p>그렇다면 앞으로도 let&#39;s fall in love with coding...💋</p>
<p><br><br><br><br></p>
]]></description>
        </item>
    </channel>
</rss>