<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>내가 보려고 만든 개발 공부 일지</title>
    <link>https://kwangsunny.tistory.com/</link>
    <description>Front-End 관련 공부를 하면서 정리한 내용을 기록하는 곳입니다</description>
    <language>ko</language>
    <pubDate>Wed, 15 Apr 2026 02:10:50 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>kwangsunny</managingEditor>
    <image>
      <title>내가 보려고 만든 개발 공부 일지</title>
      <url>https://tistory1.daumcdn.net/tistory/4810232/attach/3bcda5565a7642389a8df7d3b0474a38</url>
      <link>https://kwangsunny.tistory.com</link>
    </image>
    <item>
      <title>브라우저 렌더링 - 리플로우(reflow) 와 리페인트(repaint)</title>
      <link>https://kwangsunny.tistory.com/42</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;어느 기술 인터뷰에서 리플로우와 리페인트에 대해 설명해 달라는 질문을 받은적이 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아는대로 설명을 하긴 했지만 언제 어떻게 발생되고 성능적으로 어떻게 개선할 수 있을지에 대해서는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세하게 대답하지 못했었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이번 포스팅에서는 리플로우와 리페인트가 무엇이고 개발할때 어떤 부분을 신경써야 하는지 정리해보려 한다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;브라우저 렌더링 과정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저의 역할은 사용자가 보고자 하는 웹페이지를 서버에 요청하고 서버에서 보내준 응답을 받아&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저 화면에 그려주는 것이다. 보통 서버는 응답으로 html 파일을 보내준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저의 &lt;b&gt;렌더링 엔진은 이 html 파일을 해석하여 DOM 트리와 CSSOM 트리를 만들고, 이 둘을 결합하여 렌더 트리를 만든다&lt;/b&gt;. 이렇게 만들어진 렌더 트리를 기반으로 UI가 그려지게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 &lt;a href=&quot;https://kwangsunny.tistory.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://kwangsunny.tistory.com/&lt;/a&gt; 에 접속하려는 상황이라 가정해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저는 위 주소로 네트워크 요청을 보낸다. 이 요청은 먼저 DNS 서버에 들린다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 DNS (domain name server) 란?&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴퓨터, 스마트폰, 등 네트워크 통신을 하는 모든 기기들은 숫자로 이루어진 주소를 통해 다른 기기를 찾고 요청과 응답을 주고받는데 이 숫자 주소를 IP 라고 한다. IP는&amp;nbsp;192.168.0.0&amp;nbsp;뭐&amp;nbsp;이런&amp;nbsp;식으로&amp;nbsp;생겼는데&amp;nbsp;이&amp;nbsp;형태는&amp;nbsp;사람들이&amp;nbsp;인지&amp;nbsp;하기 &lt;br /&gt;어렵다. 그래서 위 IP대신&amp;nbsp;&lt;a href=&quot;http://www.google.com&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;www.google.com&lt;/a&gt;&amp;nbsp;같이&amp;nbsp;사람이&amp;nbsp;읽기&amp;nbsp;쉽고&amp;nbsp;기억하기도&amp;nbsp;쉬운&amp;nbsp;단어들로&amp;nbsp;이루어진&amp;nbsp;주소를&amp;nbsp;사용하는데&amp;nbsp;이를&amp;nbsp;도메인&amp;nbsp;이라고&amp;nbsp;한다.&amp;nbsp;하지만&amp;nbsp;실제로&amp;nbsp;다른&amp;nbsp;기기의&amp;nbsp;위치를&amp;nbsp;찾기&amp;nbsp;위해선&amp;nbsp;숫자&amp;nbsp;주소인&amp;nbsp;IP가&amp;nbsp;필요하다. &lt;br /&gt;그래서 도메인과 IP 를 매핑해 놓은 서버가 있는데 이 서버가 DNS 서버다.&lt;span style=&quot;background-color: #ffffff; color: #5b6170;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #5b6170;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저&amp;nbsp;요청은&amp;nbsp;DNS&amp;nbsp;서버에서&amp;nbsp;&lt;a href=&quot;https://kwangsunny.tistory.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://kwangsunny.tistory.com/&lt;/a&gt;&amp;nbsp;에&amp;nbsp;해당하는&amp;nbsp;IP&amp;nbsp;를&amp;nbsp;찾아&amp;nbsp;티스토리&amp;nbsp;서버로&amp;nbsp;가게된다. &lt;br /&gt;티스토리 서버는 위 주소에 해당하는 리소스를 응답으로 보내줄 것이다. &lt;br /&gt;이 응답은 개발자 도구 (단축키 F12) 의 네트워크 탭에서 확인 가능하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;915&quot; data-origin-height=&quot;414&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJpPxb/btrycjZ6DUp/Unc1V6bgQpT521ilAe5tOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJpPxb/btrycjZ6DUp/Unc1V6bgQpT521ilAe5tOk/img.png&quot; data-alt=&quot;서버가 보내준 응답 (html 파일)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJpPxb/btrycjZ6DUp/Unc1V6bgQpT521ilAe5tOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJpPxb%2FbtrycjZ6DUp%2FUnc1V6bgQpT521ilAe5tOk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;915&quot; height=&quot;414&quot; data-origin-width=&quot;915&quot; data-origin-height=&quot;414&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;서버가 보내준 응답 (html 파일)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;렌더링&amp;nbsp;엔진은&amp;nbsp;이&amp;nbsp;html&amp;nbsp;파일을&amp;nbsp;처음부터&amp;nbsp;한줄한줄&amp;nbsp;읽고&amp;nbsp;해석하여&amp;nbsp;브라우저가&amp;nbsp;이해할&amp;nbsp;수&amp;nbsp;있는&amp;nbsp;DOM&amp;nbsp;트리를&amp;nbsp;생성&amp;nbsp;하는데&amp;nbsp;이&amp;nbsp;작업을&lt;b&gt;&amp;nbsp;html&amp;nbsp;파싱&amp;nbsp;&lt;/b&gt;이라&amp;nbsp;한다. &lt;br /&gt;&lt;br /&gt;렌더링&amp;nbsp;엔진이&amp;nbsp;html을&amp;nbsp;파싱하는&amp;nbsp;도중&amp;nbsp;CSS파일을&amp;nbsp;로드하는&amp;nbsp;link&amp;nbsp;태그나&amp;nbsp;style&amp;nbsp;태그를&amp;nbsp;만나게&amp;nbsp;되면&amp;nbsp;CSS파일을&amp;nbsp;서버에&amp;nbsp;요청하고&amp;nbsp;이&amp;nbsp;CSS&amp;nbsp;파일을&amp;nbsp;파싱하여&amp;nbsp;CSSOM&amp;nbsp;트리를&amp;nbsp;생성한다. &lt;br /&gt;CSS를 파싱하는 동안 html 파싱은 멈추지 않고 계속 진행된다. 그래서 CSS 파싱과 html 파싱은 어느쪽이 먼저 끝나게 될지 알 수 없다. CSS 파싱이 먼저 끝나고 html 파싱이 끝나게 된다면 스타일이 모두 입혀진(완성된) html 요소가 브라우저 화면에 보일것이다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 CSS 파싱이 html 파싱보다 더 늦게 끝나면? 아무 스타일도 입혀지지 않은 앙상한 모습의 UI 가 화면에 보일것이다. 아마 가끔씩 어떤 사이트에 접속했을때 해당 페이지가 스타일 없이 뼈대만 보이다가 몇초 지나거나 새로고침을 하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그제서야 페이지가 제대로 보이는 경험을 해본적이 한 번 쯤은 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상황은 아마 link 태그에 걸려있는 외부 CSS파일의 용량이 크거나 인터넷 연결이 좋지 않아 CSS 파일을 로드하는게&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;늦어져서 그 동안 html은 다 파싱이 되었지만, CSS가 뒤늦게 파싱됨에따라 렌더링 트리가 다시 재구성 되면서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;발생하는 상황일 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 &lt;b&gt;CSS 관련 링크나 style 태그는 꼭 html 문서의 상단에&lt;/b&gt; (보통 head 태그 사이) 위치시키는것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;렌더링 엔진은 html 을 위에서부터 내려가며 파싱하기 때문에 CSS관련 태그들을 html 하단에 위치시키면&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;그만큼 CSS 파싱도 늦어져서 위와같은 상황이 생길 수 있기 때문이다.&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어쨋거나&amp;nbsp;렌더링&amp;nbsp;엔진은&amp;nbsp;이렇게&amp;nbsp;생성된&amp;nbsp;DOM&amp;nbsp;트리와&amp;nbsp;CSSOM&amp;nbsp;트리를&amp;nbsp;결합하여&amp;nbsp;렌더&amp;nbsp;트리를&amp;nbsp;만든다. &lt;br /&gt;렌더 트리에는 화면에 보이지 않는 요소들은 포함되지 않는다. 예를 들어 &lt;b&gt;DOM 트리의 html, head, meta 등의 비 시각적 요소들이나 display : none 속석이 적용된 요소들은 렌더 트리에서 제외된다.&lt;/b&gt; &lt;br /&gt;그리고 이 렌더 트리를 기반으로 HTML 요소의 위치, 크기 등 기하학적인 모든 부분을 계산하여 실제로 화면에 그리게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 만약 html 파싱도중 스크립트 태그를 만나게 되면 어떻게 될까? 스크립트는 link 태그나 style 태그를 만났을때와는 달리 html 파싱을 멈추고 스크립트를 다운로드한다. 스크립트 로딩이 완료되면 자바스크립트 엔진이 스크립트를 실행한다. 이 과정이 끝나면 렌더링 엔진은 파싱이 중단됐던 곳으로 다시 돌아가 html 파싱을 계속하게 된다. &lt;br /&gt;&lt;br /&gt;스크립트 태그를 만나면 html 파싱이 중단되기 때문에 스크립트 태그의 위치도 중요하다.&lt;br /&gt;아래&amp;nbsp;예시&amp;nbsp;코드를&amp;nbsp;보자.&lt;/p&gt;
&lt;pre id=&quot;code_1649053118950&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;link rel=&quot;stylesheet&quot; href=&quot;style.css&quot;&amp;gt;
        &amp;lt;script src=&quot;big-size.js&quot;&amp;gt;&amp;lt;/script&amp;gt;   &amp;lt;!-- (A) --&amp;gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
    	&amp;lt;div id=&quot;root&quot;&amp;gt;&amp;lt;/div&amp;gt;  &amp;lt;!-- (B) --&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버가 위의 html 파일을 응답으로 줬다고 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;렌더링 엔진은 &amp;lt;html&amp;gt; 부터 아래로 내려가며 파싱을 진행하다가 link 태그를 만나 style.css 를 로드하고 CSSOM 트리를 구성한다. CSSOM을 구성하는 동안 파싱은 멈추지 않고 계속 되고 (A) 에서 스크립트 태그를 만나게된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때, html 파싱은 중단되고 big-size.js 를 로딩한다. big-size.js 를 로딩하는 동안 html 파싱이 중단되기 때문에&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(B) 부분의 div 는 그려지지 않게 되서 화면엔 아무것도 없는 흰색 바탕만 보이게 될것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 다른 스크립트에서 이 div 에 접근하려고 해도 아직 이 div 는 없기 때문에 에러가 발생할것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 문제 때문에 로딩이 오래 걸리는 스크립트, 혹은 html 요소에 접근하는 코드가 있는 스크립트는 body 의 닫는 태그 바로 위에 위치시킨다.&amp;nbsp; &amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1649053701456&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;link rel=&quot;stylesheet&quot; href=&quot;style.css&quot;&amp;gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
    	&amp;lt;div id=&quot;root&quot;&amp;gt;&amp;lt;/div&amp;gt; 
        &amp;lt;script src=&quot;big-size.js&quot;&amp;gt;&amp;lt;/script&amp;gt; 
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 스크립트의 위치를 바꿔주면 모든 html 요소가 화면에 다 그려지고 난 후에 스크립트를 로딩하기 때문에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;텅빈화면이 보인다거나 div 에 접근할때 에러가 발생하거나 하는 문제는 생기지 않을것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크립트의 위치를 바꾸는것 이외에 스크립트의&lt;b&gt; defer 와 async 속성&lt;/b&gt;을 사용해줄 수도 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1649053861967&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script src=&quot;A.js&quot; defer&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;B.js&quot; async&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;defer 혹은 async 속성이 있는 스크립트를 만나면 브라우저는 html 파싱을 멈추지 않고 스크립트 로딩과 파싱을&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동시에 진행하게 된다. 즉, &lt;b&gt;스크립트 로딩이 비동기적으로 수행된다.&lt;/b&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 두 속성의 차이는 스크립트의 실행 시점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;defer 는 스크립트 로딩이 완료되어도 실행되지 않고 html 문서가 모두 파싱될때까지 기다렸다가 &lt;b&gt;파싱이 완료되면 그때&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실행된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 async 는 html 문서의 파싱 완료 여부와 상관없이 &lt;b&gt;스크립트가 로딩되면 바로 스크립트를 실행&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 async 속성의 스크립트 내에서 어떤 html 요소에 접근하는 코드가 있을때, 그 html 요소가 아직 파싱되지 않았다면 에러가 발생할 수 있지만, defer 속성이라면 모든 html 문서가 다 그려진 후 실행되므로 이런 에러는 발생할 일이 없을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 차이 때문에 html 요소에 접근할 일이 없고 독립적으로 수행되어야 하는 스크립트에 async 속성을 활용하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 html 문서가 로딩된 후 실행되어야 하는 스크립트에는 defer 속성을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 모듈 스크립트 &amp;lt;script type=&quot;module&quot;&amp;gt;&amp;lt;/script&amp;gt; 는 defer 속성이 기본값이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 모듈 스크립트는 html 문서 어디에 위치하든 모든 html 요소에 접근할 수 있게된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버로부터 받은 html 파일은 이런 과정을 거쳐서 브라우저 화면에 그려지게 된다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 이후에 스크립트를 통한 DOM 조작이 발생하면 렌더링 엔진은 어떤 작업을 하게될까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;리플로우 와 리페인트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리플로우와 리페인트는 DOM 요소가 시각적으로 변경됐을때, 이 변화를 다시 계산하고 화면에 그려주는 작업이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://kwangsunny.tistory.com/39&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;매크로 / 마이크로 태스크&lt;/a&gt; &lt;span&gt;포스팅에서 렌더링은 매크로태스크 하나 처리 -&amp;gt; 모든 마이크로태스크 처리 -&amp;gt; 렌더링 작업&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;순서로 진행된다고 했었는데, 이 렌더링 작업이 리플로우와 리페인트를 뜻한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;리플로우 (reflow) &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;DOM 요소의 기하학적 속성이 변경될때, 브라우저 사이즈가 변할때, 스타일시트가 로딩되었을때&lt;/b&gt; 발생하는 변화들을 다시 계산 해주는 작업을 뜻하고 레이아웃(Layout) 이라고도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 요인에 의해 변화된 요소 주변의 모든 (부모, 자식, 형제) 요소들도 영향을 받게되는데 결국 DOM 요소의 하나의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시각적 변화가 DOM 트리 전체에 대해 다시 계산을 수행하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 문서 중간에 위치한 div 의 font-size 값을 20px --&amp;gt; 100px 로 변경시켰다고 가정해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 div의 폰트 크기가 커진만큼 주변의 요소들이 밀려나거나 크기가 변하게 될것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cn3Q2b/btryro8yCFr/2GCgWg8TRYgR9wkBooslkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cn3Q2b/btryro8yCFr/2GCgWg8TRYgR9wkBooslkK/img.png&quot; data-origin-width=&quot;451&quot; data-origin-height=&quot;403&quot; data-is-animation=&quot;false&quot; style=&quot;width: 46.4251%; margin-right: 10px;&quot; data-widthpercent=&quot;46.97&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cn3Q2b/btryro8yCFr/2GCgWg8TRYgR9wkBooslkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcn3Q2b%2Fbtryro8yCFr%2F2GCgWg8TRYgR9wkBooslkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;451&quot; height=&quot;403&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b623NU/btrytjSYnbs/gBHrU1ex0qt5Kzwqy8OZrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b623NU/btrytjSYnbs/gBHrU1ex0qt5Kzwqy8OZrK/img.png&quot; data-origin-width=&quot;494&quot; data-origin-height=&quot;391&quot; width=&quot;294&quot; height=&quot;233&quot; data-widthpercent=&quot;53.03&quot; style=&quot;width: 52.4121%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b623NU/btrytjSYnbs/gBHrU1ex0qt5Kzwqy8OZrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb623NU%2FbtrytjSYnbs%2FgBHrU1ex0qt5Kzwqy8OZrK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;494&quot; height=&quot;391&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;박스1 의 font-size 를 100px 로 키운 모습&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림을 보면 박스1 의 폰트 크기가 증가된 만큼 주변 요소들의 위치와 크기들이 달라졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, html 요소 하나의 시각적 변화 만으로도 주변 요소들의 위치와 크기도 모두 계산해줘야 하고, 이 주변 요소들의&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변화가 인접한 또다른 요소들에게도 다시 영향을 미치게 되어 &lt;b&gt;결국 DOM 트리 전체에 대한 계산 작업이 발생하게 된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 계산 작업을 통해 렌더 트리가 업데이트 되고 리페인트가 실행되어 화면에 그려준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 리플로우는 대충 봐도 굉장이 비용이 많이 드는 작업임을 알 수 있다.&amp;nbsp; &amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 이렇게 직접적으로 요소의 시각적 속성을 바꿔주는 행위말고, 단순히 어떤 메서드를 호출하거나 프로퍼티 값을 가져오는 것만으로도 리플로우가 발생하는 경우가 있는데, 몇 가지 예를 들면 다음과 같은 것들이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;elem.offsetLeft,&lt;span&gt;&amp;nbsp;&lt;/span&gt;elem.offsetTop,&lt;span&gt;&amp;nbsp;&lt;/span&gt;elem.offsetWidth,&lt;span&gt;&amp;nbsp;&lt;/span&gt;elem.offsetHeight,&lt;span&gt;&amp;nbsp;&lt;/span&gt;elem.offsetParent&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;elem.clientLeft,&lt;span&gt;&amp;nbsp;&lt;/span&gt;elem.clientTop,&lt;span&gt;&amp;nbsp;&lt;/span&gt;elem.clientWidth,&lt;span&gt;&amp;nbsp;&lt;/span&gt;elem.clientHeight&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;elem.getClientRects(),&lt;span&gt;&amp;nbsp;&lt;/span&gt;elem.getBoundingClientRect()&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;window.scrollX,&lt;span&gt;&amp;nbsp;&lt;/span&gt;window.scrollY&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;window.innerHeight,&lt;span&gt;&amp;nbsp;&lt;/span&gt;window.innerWidth&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;window.getComputedStyle()&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 자세한 내용들은 아래 링크를 참조하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://gist.github.com/paulirish/5d52fb081b3570c81e3a&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://gist.github.com/paulirish/5d52fb081b3570c81e3a&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1649082692081&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;What forces layout/reflow. The comprehensive list.&quot; data-og-description=&quot;What forces layout/reflow. The comprehensive list. - what-forces-layout.md&quot; data-og-host=&quot;gist.github.com&quot; data-og-source-url=&quot;https://gist.github.com/paulirish/5d52fb081b3570c81e3a&quot; data-og-url=&quot;https://gist.github.com/paulirish/5d52fb081b3570c81e3a&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dapIG7/hyNUtGZMBL/uHjC9m6CgU3yBAkAL832X0/img.png?width=1280&amp;amp;height=640&amp;amp;face=0_0_1280_640,https://scrap.kakaocdn.net/dn/bn03Gf/hyNUpYTLu1/9lxukv0NwbDNOhX9NWxqnk/img.png?width=1027&amp;amp;height=619&amp;amp;face=0_0_1027_619&quot;&gt;&lt;a href=&quot;https://gist.github.com/paulirish/5d52fb081b3570c81e3a&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://gist.github.com/paulirish/5d52fb081b3570c81e3a&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dapIG7/hyNUtGZMBL/uHjC9m6CgU3yBAkAL832X0/img.png?width=1280&amp;amp;height=640&amp;amp;face=0_0_1280_640,https://scrap.kakaocdn.net/dn/bn03Gf/hyNUpYTLu1/9lxukv0NwbDNOhX9NWxqnk/img.png?width=1027&amp;amp;height=619&amp;amp;face=0_0_1027_619');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;What forces layout/reflow. The comprehensive list.&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;What forces layout/reflow. The comprehensive list. - what-forces-layout.md&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;gist.github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 위와 같은 코드들이 리플로우를 발생시키는 이유는 무엇일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약에 어떤 요소의 실제 가로 길이를 알고 싶다면 어떻게 해야 할까.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;div.style.width 이렇게 하면 알 수 있을까? 만약 인라인 스타일로 width 값을 명시해 줬다면 이렇게 해도 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 대부분의 경우 스타일은 클래스를 사용해 적용할 것이기 때문에 div.style.width 은 빈 문자열을 반환할 것이다. 혹은 인라인 스타일로 width : 50%; 이런식으로 픽셀값이 아닌 상대값을 주면, 이 요소의 실제 width의 픽셀값은 화면 사이즈에 따라 매순간 달라질것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 말해, 우리가 알고싶은 값은 요소의 계산된 스타일 (computed style) 값이고, 위의 메서드 혹은 프로퍼티값을&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용하면 요소의 계산된 스타일 값을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 만약 div.clientHeight 를 쓰면 이&lt;b&gt; div의 최신값을 계산하기 위해 리플로우가 발생하게 된다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 이 메서드들과 프로퍼티들은 브라우저의 리플로우 최적화를 중단시키는데 자세한 내용은 글 후반에 더 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;리페인트 (repaint)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변경된 요소를 실제로 화면에 그려주는 작업을 리페인트라고 한다. 그래서 리플로우가 발생하면 필연적으로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리페인트가 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리페인트도 굉장히 무거운 작업이긴 하지만 리플로우 처럼 모든 요소들에 대한 기하학적 정보들을 계산해주는 작업은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아니기 때문에 리플로우 보다는 &lt;span&gt;상대적으로&lt;span&gt; &lt;/span&gt;&lt;/span&gt;훨씬 가벼운 작업이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 배경색이나 visibility 속성이 변했을때는 리플로우는 발생하지 않고 리페인트만 발생하는데&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이유는 위치, 크기, 테두리 두께, 폰트 사이즈 같이 &lt;b&gt;기하학적인 변화가 아닌 단순히 색상만 바뀌기 때문&lt;/b&gt;이다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 css 속성중 display : none -- (A) 와&amp;nbsp;&lt;span&gt;visibility : hidden -- (B)&lt;span&gt; 을 구분할 필요가 있다.&lt;/span&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(A) 와 (B) 둘 다 화면에서 보이지 않게 만드는 css 속성이다. 그런데 (B) 는 화면에서 보이진 않아도 그 영역은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그대로 차지하고 있는 반면, (A)는 보이지도 않고, 영역도 없어지게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, display : none 이 적용된 요소는 렌더 트리에서 제외된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;display : none 이 적용된 요소는 그 영역도 없어지기 때문에 주면 요소들이 그 공간을 밀고 들어오게 되어&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위치와 크기가 변하게 될 것이고 이는 곧 리플로우와 리페인트를 발생시킬 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 visibility : hidden 이 적용된 요소는 단순히 보이지 않게 될 뿐, 크기나 위치가 변하는게 아니기 때문에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리플로우는 발생하지 않고 리페인트만 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마찬가지 이유로 background-color 로 요소의 배경색만 바꿔준 경우도 리페인트만 발생하게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 정말 그런지 예제로 확인 해보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/voMUA/btryt6Tpdtu/vZDhTtS807NArKkZKmeYo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/voMUA/btryt6Tpdtu/vZDhTtS807NArKkZKmeYo1/img.png&quot; data-origin-width=&quot;982&quot; data-origin-height=&quot;886&quot; data-is-animation=&quot;false&quot; style=&quot;width: 48.5683%; margin-right: 10px;&quot; data-widthpercent=&quot;49.14&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/voMUA/btryt6Tpdtu/vZDhTtS807NArKkZKmeYo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvoMUA%2Fbtryt6Tpdtu%2FvZDhTtS807NArKkZKmeYo1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;982&quot; height=&quot;886&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0a8az/btryrWxfwjx/7yFkVPCo9OWjqwZoQkDoMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0a8az/btryrWxfwjx/7yFkVPCo9OWjqwZoQkDoMk/img.png&quot; data-origin-width=&quot;990&quot; data-origin-height=&quot;863&quot; data-is-animation=&quot;false&quot; style=&quot;width: 50.2689%;&quot; data-widthpercent=&quot;50.86&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0a8az/btryrWxfwjx/7yFkVPCo9OWjqwZoQkDoMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0a8az%2FbtryrWxfwjx%2F7yFkVPCo9OWjqwZoQkDoMk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;990&quot; height=&quot;863&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;좌 : 요소의 높이 변경&amp;nbsp; /&amp;nbsp; 우 : 요소의 배경색 변경&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 실험은 크롬 개발자 도구의 performance 탭에서 div의 css를 수정했을때 발생하는 이벤트 로그를 캡쳐한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 좌측 그림의 노란색 동그라미 부분을 보면, 콘솔에서 요소의 높이값을 변경해주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 위쪽 동그라미친 &lt;b&gt;Layout&lt;/b&gt; 이라고 된 부분이 바로 리플로우를 뜻한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 아래쪽에 &lt;b&gt;Paint&lt;/b&gt; 라고 써진 초록색 사각형은 리페인트 단계를 뜻한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 배경색만 red 로 변경해준 오른쪽 그림을 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘 보면 왼쪽과는 달리 Layout 단계 없이 바로 Paint 를 해주고 있는 모습이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기하학적인 변화 없이 단순 색상만 변경 되었을땐 리플로우 없이 리페인트만 발생한다는것을 확인할 수 있는 결과이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 크롬 개발자 도구에는 재밌는 기능이 있는데, 바로 화면에서 리페인트가 실시간으로 발생하는 모습을 하이라이트 해주는 기능이다. 백번 설명 보다 한 번 보는게 더 도움이 되듯이 이 기능을 사용하면 리페인트가&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;언제 일어나는지 더 피부에 와닿을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 기능은 개발자 도구 rendering 탭의 Paint flashing 이라는 항목을 체크하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;716&quot; data-origin-height=&quot;133&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BFSvW/btrytkrmTfF/aHk2f1kaqZYuYCkOZgJkjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BFSvW/btrytkrmTfF/aHk2f1kaqZYuYCkOZgJkjk/img.png&quot; data-alt=&quot;하이라이트 on&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BFSvW/btrytkrmTfF/aHk2f1kaqZYuYCkOZgJkjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBFSvW%2FbtrytkrmTfF%2FaHk2f1kaqZYuYCkOZgJkjk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;716&quot; height=&quot;133&quot; data-origin-width=&quot;716&quot; data-origin-height=&quot;133&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;하이라이트 on&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1471&quot; data-origin-height=&quot;711&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c7BXXe/btryt5tWu2p/Xh0BRUCTwKxZjkkqKQw411/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c7BXXe/btryt5tWu2p/Xh0BRUCTwKxZjkkqKQw411/img.gif&quot; data-alt=&quot;요소의 시각적 변화가 생길때 마다 리페인트 발생&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c7BXXe/btryt5tWu2p/Xh0BRUCTwKxZjkkqKQw411/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/c7BXXe/btryt5tWu2p/Xh0BRUCTwKxZjkkqKQw411/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1471&quot; height=&quot;711&quot; data-origin-width=&quot;1471&quot; data-origin-height=&quot;711&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;요소의 시각적 변화가 생길때 마다 리페인트 발생&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;리플로우, 리페인트 줄이기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리플로우와 리페인트가 아주 무거운 작업이라는걸 알았으니 이제 이 작업을 어떻게 하면 줄일 수 있을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리플로우는 모든 요소들을 다 계산해주는 작업이므로 리페인트보다 무거운 작업이다. 또 리플로우가 발생하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필연적으로 리페인트도 발생한다. 그러니 우리는 리플로우가 최소한으로 발생되게 코드를 잘 작성해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1. DOM 속성 변경 코드 그룹핑 하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크립트로 여러 DOM의 속성을 변경했다고 가정해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 그만큼 리플로우가 발생할 것이다. 하지만 코드를 어떻게 작성하냐에 따라 리플로우 발생 횟수를 줄일 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6rET4/btryt6sS5ca/SrtY3F8RpeyjLi9Jf480E0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6rET4/btryt6sS5ca/SrtY3F8RpeyjLi9Jf480E0/img.jpg&quot; data-origin-width=&quot;678&quot; data-origin-height=&quot;580&quot; data-is-animation=&quot;undefined&quot; style=&quot;width: 50.9048%; margin-right: 10px;&quot; data-widthpercent=&quot;51.5&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6rET4/btryt6sS5ca/SrtY3F8RpeyjLi9Jf480E0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6rET4%2Fbtryt6sS5ca%2FSrtY3F8RpeyjLi9Jf480E0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;678&quot; height=&quot;580&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IFAIi/btryrp1ix2e/LWxG9iVEBUpL0O6ZpEjGb0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IFAIi/btryrp1ix2e/LWxG9iVEBUpL0O6ZpEjGb0/img.jpg&quot; data-origin-width=&quot;623&quot; data-origin-height=&quot;566&quot; data-is-animation=&quot;undefined&quot; style=&quot;width: 47.9324%;&quot; data-widthpercent=&quot;48.5&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IFAIi/btryrp1ix2e/LWxG9iVEBUpL0O6ZpEjGb0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIFAIi%2Fbtryrp1ix2e%2FLWxG9iVEBUpL0O6ZpEjGb0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;623&quot; height=&quot;566&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;좌 : DOM 수정, 나눠서 수행&amp;nbsp; /&amp;nbsp; 우 : DOM 수정 그룹화 하여 수행&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 예시 모두 div 각각에 높이를 10px 만큼 더해주는 코드이다. 다만 왼쪽은 각 div 별로 계산된 높이값을 가져와 10px 만큼 더해주고 있고, 오른쪽은 계산된 높이값을 가져오는 것과 높이를 10px만큼 더해주는 코드를 그룹화 하여 작성했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 결과 왼쪽은 리플로우(Layout) 이 3번 발생한것에 반해, 오늘쪽은 리플로우가 한 번 발생했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 실행 순서만 바꿔줬는데 왜 이런 차이가 생겼을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 브라우저는 기본적으로 리플로우를 줄이기 위한 전략을 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그 전략은 &lt;span style=&quot;color: #ef5369;&quot;&gt;요소의 변경을&amp;nbsp;지금 당장&amp;nbsp;처리하지 않고 큐에 저장해뒀다가 일정시간이 지나거나 처리할 변경 작업이 일정량 쌓였을때 리플로우를 수행하는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 오른쪽 예시부터 보면, 각 div 들의 높이를 한번에 몰아서 수정하고 있는데, 브라우저는 이 높이 변화들을 큐에 쌓아뒀다가 일정 시간이 지나면 한 번에 처리하기 때문에 리플로우가 한 번 발생한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 왼쪽 예시는 div의 높이 변경을 한 번에 처리하지 않고 clientHeight 한번, 높이 수정 한번 이런 식으로 해주고있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 설명했던 것처럼 clientHeight 같은 프로퍼티는 요소의 최신값을 가져오기 위해 계산 작업이(리플로우) 발생한다고 했다. &lt;b&gt;이미 큐에 쌓여있는 작업들은 요소의 최신값 계산에 영향을 줄 수 있기 때문에 브라우저는 큐에 쌓인 작업들을&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;모두 비운 다음 clientHeight 값을 반환한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 왼쪽 예시는 요소의 변경 작업이 큐에 쌓이기전에 clientHeight를 매번 실행하므로써 오른쪽 예시와는 달리 리플로우가 3번이나 발생한 것이다.&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2. 리플로우 유발 메서드는 별도 저장해 사용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법은 위 예시와 연결되는 내용이다. 리플로우를 유발시키는 메서드, 프로퍼티는 매번 호출하지 말고 변수에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저장한 후 사용하자.&lt;/p&gt;
&lt;pre id=&quot;code_1649136922717&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 나쁜 예
for(let i=1; i&amp;lt;10; i++){
    div.style.left = (div1.getBoundingClientRect().left + i) + 'px';
}

// 좋은 예
let {left} = div1.getBoundingClientRect();  // reflow 유발 메서드는 변수에 저장해 사용 
for(let i=1; i&amp;lt;10; i++){
    div.style.left = (left + i) + 'px';
    left += i;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3. CSS 수정은 일괄로 변경 해주기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크립트로 CSS 속성을 여러번 수정하는 것은 그만큼 리플로우를 여러번 유발시키게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러니 CSS 수정이 많이 필요한 경우에는 클래스명을 수정하거나 cssText 속성을 이용해 일괄로 변경해주자.&lt;/p&gt;
&lt;pre id=&quot;code_1649126892780&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;style&amp;gt;
    .my-div {
        width: 100px;
        height: 100px;
        padding: 5px;
        border: 5px solid blue;
        background-color: black;
        color: white;
    }
&amp;lt;/style&amp;gt;
&amp;lt;body&amp;gt;
    &amp;lt;div id=&quot;div&quot;&amp;gt;&amp;lt;/div&amp;gt;

    &amp;lt;script&amp;gt;
        // 나쁜 예
        div.style.width = &quot;100px&quot;;   		  // reflow, repaint
        div.style.height = &quot;100px&quot;;   		  // reflow, repaint
        div.style.padding = &quot;5px&quot;;   		  // reflow, repaint
        div.style.border = &quot;5px solid blue&quot;;      // reflow, repaint
        div.style.backgroundColor = &quot;black&quot;;      // repaint
        div.style.color = &quot;white&quot;;		  // repaint

        // 좋은 예1
        div.className = &quot;my-div&quot;;

        // 좋은 예2
        div.style.cssText = &quot;width: 100px; height: 100px; ...&quot;;
    &amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;4. display : none 이용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예시처럼 여러가지 속성을 변경해줄때, 먼저 해당 요소를 display : none 상태로 만들고나서 작업하는 방법도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;display : none 이 적용되면 그 요소는 렌더 트리에서 제외되고 없는 요소 취급당하기 때문에 이 요소에 다른 기하학적&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변화가 일어나도 리플로우나 리페인트는 발생하지 않게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1649135955258&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;div.style.display = &quot;none&quot;;   // (A)

// ... 여러 스타일 속성 수정 ...

div.style.display = &quot;block&quot;;  // (B)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 코드를 작성해 주면 (A) 와 (B) 에서 리플로우, 리페인트가 각각 한 번 씩 발생하므로, 그 사이에 아무리 많은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스타일 값을 변경해줘도 총 2번의 리플로우, 리페인트만 발생하게 될것이다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 어떤 CSS 속성이 리플로우와 리페인트를 유발시키는지 정리해놓은 사이트가 있는데 참고하면 좋을것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://csstriggers.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://csstriggers.com/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1649137261437&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;CSS Triggers&quot; data-og-description=&quot;@PROPERTY_DESCRIPTION@ B G W E Change from default B G W E Subsequent updates&quot; data-og-host=&quot;csstriggers.com&quot; data-og-source-url=&quot;https://csstriggers.com/&quot; data-og-url=&quot;https://csstriggers.com/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://csstriggers.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://csstriggers.com/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;CSS Triggers&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;@PROPERTY_DESCRIPTION@ B G W E Change from default B G W E Subsequent updates&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;csstriggers.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘못된 내용은 댓글로 알려주세요 :)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고 자료&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://d2.naver.com/helloworld/59361&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://d2.naver.com/helloworld/59361&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.phpied.com/rendering-repaint-reflowrelayout-restyle/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.phpied.com/rendering-repaint-reflowrelayout-restyle/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1649137314927&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Rendering: repaint, reflow/relayout, restyle&quot; data-og-description=&quot;2010 update: Lo, the Web Performance Advent Calendar hath moved Dec 17 This post is part of the 2009 performance advent calendar experiment. Stay tuned for the articles to come. UPDATE: Ukraine translation here. Nice 5 &amp;quot;R&amp;quot; words in the title, eh? Let's tal&quot; data-og-host=&quot;www.phpied.com&quot; data-og-source-url=&quot;https://www.phpied.com/rendering-repaint-reflowrelayout-restyle/&quot; data-og-url=&quot;https://www.phpied.com/rendering-repaint-reflowrelayout-restyle/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dndaeP/hyNVWVt2u1/bDDaLKTu3XDW8ZhTf0zai0/img.png?width=630&amp;amp;height=292&amp;amp;face=0_0_630_292,https://scrap.kakaocdn.net/dn/AkMmc/hyNVUDlTjx/Wvliz1FGKxPg2s7ikF5Hpk/img.png?width=630&amp;amp;height=292&amp;amp;face=0_0_630_292,https://scrap.kakaocdn.net/dn/bfcnwY/hyNVIQrUCp/WPMQeL1aTQKLnBNFOigaRk/img.png?width=433&amp;amp;height=616&amp;amp;face=0_0_433_616&quot;&gt;&lt;a href=&quot;https://www.phpied.com/rendering-repaint-reflowrelayout-restyle/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.phpied.com/rendering-repaint-reflowrelayout-restyle/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dndaeP/hyNVWVt2u1/bDDaLKTu3XDW8ZhTf0zai0/img.png?width=630&amp;amp;height=292&amp;amp;face=0_0_630_292,https://scrap.kakaocdn.net/dn/AkMmc/hyNVUDlTjx/Wvliz1FGKxPg2s7ikF5Hpk/img.png?width=630&amp;amp;height=292&amp;amp;face=0_0_630_292,https://scrap.kakaocdn.net/dn/bfcnwY/hyNVIQrUCp/WPMQeL1aTQKLnBNFOigaRk/img.png?width=433&amp;amp;height=616&amp;amp;face=0_0_433_616');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Rendering: repaint, reflow/relayout, restyle&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;2010 update: Lo, the Web Performance Advent Calendar hath moved Dec 17 This post is part of the 2009 performance advent calendar experiment. Stay tuned for the articles to come. UPDATE: Ukraine translation here. Nice 5 &quot;R&quot; words in the title, eh? Let's tal&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.phpied.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>그 외 잡다구리</category>
      <author>kwangsunny</author>
      <guid isPermaLink="true">https://kwangsunny.tistory.com/42</guid>
      <comments>https://kwangsunny.tistory.com/42#entry42comment</comments>
      <pubDate>Tue, 5 Apr 2022 15:00:49 +0900</pubDate>
    </item>
    <item>
      <title>자바스크립트 - 객체 프로퍼티(property) 파헤치기!</title>
      <link>https://kwangsunny.tistory.com/41</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트에는 8가지의 자료형이 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 종류로는 number, string, BigInt, boolean, null, undefined, Symbol, 마지막으로 객체가 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 중 객체를 제외한 나머지들은 딱 하나의 값만 가질 수 있는데, 이런 자료형을 &lt;b&gt;원시타입(primitive type)&lt;/b&gt;이라 하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체는 여러 값들을 포함할 수 있는 하나의 덩어리이며 &lt;b&gt;참조타입(reference type)&lt;/b&gt; 이라 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체가 포함할 수 있는 값들에 제한은 없으며 어떤 자료형이든 객체의 내부 값으로 할당할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;프로퍼티는 키-값 쌍으로 구성&lt;/b&gt;되는데 키는 값을 나타내는 이름이라 생각하면 되고, 이 키로 해당 프로퍼티 값에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;접근할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1648475161815&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let obj = {
    name: 'sunny' // 키(name) + 값(sunny) 쌍 --&amp;gt; 프로퍼티 
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체의 선언 방식은 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1648472785555&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let obj = {}  // (A)
let obj2 = new Object();  // (B)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;(A) 와 (B) 는 같은 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체는 (B)와 같이 자바스크립트의 내장 생성자 함수인 Object에 의해 만들어지는데 이를 축약해서 쓴 형태가 (A)이다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 (A)처럼 객체를 중괄호 방식으로 나타낸것을 &lt;b&gt;객체 리터럴&lt;/b&gt; 이라고 부른다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 객체 리터럴에서 메서드를 선언하는 방식은 두 가지가 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1648475238878&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let obj = {
    goHome: function(){ // (A)
    	console.log('퇴근');
    },
    goToWork(){ // (B)
    	console.log('출근');
    },
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(A)를 축약해서 쓴 형태가 (B)이고, 이런식으로 객체 안에서 좀더 깔끔하게 메서드를 선언해줄 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지는 객체와 프로퍼티에 대해 누구나 아는 내용일 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 객체 프로퍼티는 두 가지로 나눌 수 있고, 또 숨겨진 설정 기능이 존재해 프로퍼티를 더 디테일하게 다룰 수 있는데 이것들이 무엇인지 하나씩 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;객체 프로퍼티의 종류&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체 프로퍼티는 아래와 같이 두 가지로 나뉜다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;데이터 프로퍼티 -&amp;nbsp; 값을 저장하는 프로퍼티&lt;/b&gt; (일반적으로 쓰는 방식)&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;접근자 프로퍼티 -&amp;nbsp; 프로퍼티 값에 접근하려고 할때 혹은 프로퍼티 값을 수정하려 할때 실행되는 메서드&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 프로퍼티는 객체에 값을 할당하고 접근하는 일반적인 방식으로 사용되는 프로퍼티를 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 접근자 프로퍼티는 무엇인지 예제로 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;접근자 프로퍼티 - getter, setter 메서드&lt;/h3&gt;
&lt;pre id=&quot;code_1648472068832&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let obj = {
    get name(){ // getter (획득자)
    	return `name is ${this._name}`;
    },
    set name(value){ // setter (설정자)
    	this._name = value;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;344&quot; data-origin-height=&quot;158&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q0M64/btrxEDTCqcm/mI8VfsgPQ209lZnEMKb2u0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q0M64/btrxEDTCqcm/mI8VfsgPQ209lZnEMKb2u0/img.png&quot; data-alt=&quot;getter setter를 가진 obj의 내부&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q0M64/btrxEDTCqcm/mI8VfsgPQ209lZnEMKb2u0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq0M64%2FbtrxEDTCqcm%2FmI8VfsgPQ209lZnEMKb2u0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;344&quot; height=&quot;158&quot; data-origin-width=&quot;344&quot; data-origin-height=&quot;158&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;getter setter를 가진 obj의 내부&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 코드를 본적이 있는가?&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;get, set 뭔가 함수 같이 생겼는데..? 함수가 맞긴 한데 이 메서드들은 조금 특별하게 취급된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체 리터럴 안에서 저런식으로 get somthing(){...} 혹은 set somthing(){...} 형태로 써주게 되면,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부에서 obj.something 을 할때 getter 함수가 실행되고 obj.something = 'test' 를 하면 setter 가 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 위 코드를 실행하면 아래와 같이 출력된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;291&quot; data-origin-height=&quot;184&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cewjdU/btrxHWSr4tY/RQkXs7k6jhGSQzG0KFX0i1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cewjdU/btrxHWSr4tY/RQkXs7k6jhGSQzG0KFX0i1/img.png&quot; data-alt=&quot;getter, setter 메서드 실행 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cewjdU/btrxHWSr4tY/RQkXs7k6jhGSQzG0KFX0i1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcewjdU%2FbtrxHWSr4tY%2FRQkXs7k6jhGSQzG0KFX0i1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;291&quot; height=&quot;184&quot; data-origin-width=&quot;291&quot; data-origin-height=&quot;184&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;getter, setter 메서드 실행 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;obj.name 을 실행하면 getter 함수가 실행되서 name is 가 붙어서 출력된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음엔 obj._name 이란 값이 없으므로 undefined가 출력되다가, 다음 줄에서 'test' 를 name 에 할당해주면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;setter 메서드가 this._name = 'test' 를 수행하여 name is test 라고 출력된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;(참고로 변수명에 언더바( _ )를 붙이면 '현재 스코프에서만 쓰는 로컬 변수' 라는 일종의 개발자들 사이의 약속인데,&amp;nbsp;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;obj._name = 123 처럼 직접 값을 수정할 수도 있지만 지양하는것이 좋다.)&amp;nbsp;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;i&gt;- getter, setter 제한 사항&lt;/i&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;setter 함수는 인자를 하나만 받도록 강제되는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;obj._name = 'test' 를 했을때 할당해주는 값(이 예제에선 'test')이 setter 함수의 첫번째 인자로 들어간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 setter에 인자가 하나가 아니면 에러가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 getter 는 setter 와 달리 인자를 하나도 받으면 안된다. 만약 인자를 하나라도 받게 되면 에러가 발생한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Dulnt/btrxGbJlhox/LfEX7aTuwqAWoAAT7GgKCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Dulnt/btrxGbJlhox/LfEX7aTuwqAWoAAT7GgKCk/img.png&quot; data-origin-width=&quot;751&quot; data-origin-height=&quot;155&quot; style=&quot;width: 48.846%; margin-right: 10px;&quot; data-widthpercent=&quot;49.42&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Dulnt/btrxGbJlhox/LfEX7aTuwqAWoAAT7GgKCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDulnt%2FbtrxGbJlhox%2FLfEX7aTuwqAWoAAT7GgKCk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;751&quot; height=&quot;155&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bR4EdK/btrxO9ws8cm/tumOzcYqOkHvIZzcGL2GK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bR4EdK/btrxO9ws8cm/tumOzcYqOkHvIZzcGL2GK1/img.png&quot; data-origin-width=&quot;481&quot; data-origin-height=&quot;97&quot; data-is-animation=&quot;false&quot; style=&quot;width: 49.9912%;&quot; data-widthpercent=&quot;50.58&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bR4EdK/btrxO9ws8cm/tumOzcYqOkHvIZzcGL2GK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbR4EdK%2FbtrxO9ws8cm%2FtumOzcYqOkHvIZzcGL2GK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;481&quot; height=&quot;97&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;좌 : setter에 인자가 없을때&amp;nbsp; /&amp;nbsp; 우 : getter에 인자가 있을때&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 getter, setter 는 메서드 축약 방식으로 선언해줘야 그 기능을 발휘한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 get: function name(){...} 같이 기본 방식을 사용하면 obj.name 을 해도 getter 가 작동하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;i&gt;- getter, setter 로 읽기전용, 쓰기전용 프로퍼티 만들기&lt;/i&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;getter 와 setter 를 같이 만들지 않고 하나씩만 만들어줌으로써 해당 프로퍼티를 읽기전용 혹은 쓰기전용 으로 만들 수도 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c930Zv/btrxHXjWaeT/6YjWC8SaETtkIQ6KTbESr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c930Zv/btrxHXjWaeT/6YjWC8SaETtkIQ6KTbESr1/img.png&quot; data-origin-width=&quot;266&quot; data-origin-height=&quot;228&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;51.49&quot; width=&quot;149&quot; height=&quot;128&quot; style=&quot;width: 50.8914%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c930Zv/btrxHXjWaeT/6YjWC8SaETtkIQ6KTbESr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc930Zv%2FbtrxHXjWaeT%2F6YjWC8SaETtkIQ6KTbESr1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;266&quot; height=&quot;228&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dZU0mi/btrxEDmcZuU/viXwY6cdK0cZqAWWtrRLEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dZU0mi/btrxEDmcZuU/viXwY6cdK0cZqAWWtrRLEK/img.png&quot; data-origin-width=&quot;255&quot; data-origin-height=&quot;232&quot; data-is-animation=&quot;false&quot; data-widthpercent=&quot;48.51&quot; width=&quot;159&quot; height=&quot;145&quot; style=&quot;width: 47.9458%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dZU0mi/btrxEDmcZuU/viXwY6cdK0cZqAWWtrRLEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdZU0mi%2FbtrxEDmcZuU%2FviXwY6cdK0cZqAWWtrRLEK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;255&quot; height=&quot;232&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;좌 : getter만 존재&amp;nbsp; /&amp;nbsp; 우 : setter만 존재&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왼쪽처럼 getter 메서드만 있는 경우엔 o.name = 123 처럼 값을 수정해 주려고 해도 setter가 없기 때문에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;값은 수정되지 않고 오로지 값을 읽는것 밖엔 할 수 없게된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 오른쪽처럼 setter만 있는 경우, 값을 수정해줄 수는 있어도 값을 읽지는 못한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런식으로 제한을 걸어두어 객체의 상태값을 마음대로 수정하거나 읽어오는 행위를 방지하는 장치로 활용할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;i&gt;- getter, setter&lt;span&gt; 잘 활용하기&lt;/span&gt;&lt;/i&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;getter, setter를 이용하면 프로퍼티 값을 수정할때 간단한 유효성 체크도 할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1648537263452&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let obj = {
    _userNames: [],
    get user(){
        return this._userNames;
    },
    set user(value){
        if(value.length &amp;gt; 9){
            console.error('이름이 너무 깁니다.');
            return;
        }
        this._userNames.push(value);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;331&quot; data-origin-height=&quot;148&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7mpq3/btrxVkEVlKp/Bf60HSQiSwTPZQKfMq9i60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7mpq3/btrxVkEVlKp/Bf60HSQiSwTPZQKfMq9i60/img.png&quot; data-alt=&quot;실행 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7mpq3/btrxVkEVlKp/Bf60HSQiSwTPZQKfMq9i60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7mpq3%2FbtrxVkEVlKp%2FBf60HSQiSwTPZQKfMq9i60%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;331&quot; height=&quot;148&quot; data-origin-width=&quot;331&quot; data-origin-height=&quot;148&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실행 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제는 getter, setter를 이용해 사용자 이름을 배열에 추가하는 코드인데 추가해줄 이름의 길이가 너무 길면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름이 너무 길다는 에러 메시지를 출력해줘서 사용자 이름이 추가되지 않도록 유효성 체크를 해주는 코드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'sunny' 는 9자를 넘지 않기 때문에 배열에 잘 추가된 모습을 볼 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;i&gt;- getter, setter &lt;b&gt;&lt;i&gt;내장&lt;span&gt; &lt;/span&gt;&lt;/i&gt;&lt;/b&gt;프로퍼티&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/i&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;getter, setter 를 사용하는 대표적인 내장 프로퍼티로 __proto__ 와 cookie 가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;obj.__proto__ 프로퍼티는 점 앞의 객체(obj) 가 참조하는 프로토타입 객체에 접근할 수 있게 해주는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체 내장 프로퍼티이고, 쿠키값을 저장하는 document.cookie 프로퍼티도 접근자 프로퍼티이다.&lt;/p&gt;
&lt;pre id=&quot;code_1648538086003&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;document.cookie = &quot;name=sunny&quot;;
document.cookie = &quot;age=20&quot;;
document.cookie = &quot;nat=korea&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿠키는 키-값 쌍으로 저장되는데 위 코드처럼 문자열로 &quot;키=값&quot; 형태로 값을 할당해주면 새롭게 쿠키가 추가된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 다음 줄에서 cookie에 age 와 nat 값을 연달아 할당해주고 있는데, 이렇게 해도 cookie의 값은 덮어 씌워지지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이유는 &lt;b&gt;cookie가 접근자 프로퍼티라서 getter, setter 가 내부적으로 구현되어있고 새로운 값을 할당할때 덮어씌우지 않고 추가되도록 만들어져 있기 때문&lt;/b&gt;이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;248&quot; data-origin-height=&quot;43&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kS9LX/btrxVkZd6BN/i9QSYtIYaXKBJjdQI8XPP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kS9LX/btrxVkZd6BN/i9QSYtIYaXKBJjdQI8XPP1/img.png&quot; data-alt=&quot;덮어씌워지지 않고 새롭게 추가된 모습&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kS9LX/btrxVkZd6BN/i9QSYtIYaXKBJjdQI8XPP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkS9LX%2FbtrxVkZd6BN%2Fi9QSYtIYaXKBJjdQI8XPP1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;323&quot; height=&quot;56&quot; data-origin-width=&quot;248&quot; data-origin-height=&quot;43&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;덮어씌워지지 않고 새롭게 추가된 모습&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 식으로 접근자 프로퍼티를 활용하면 외부 코드에서는 그냥 일반 프로퍼티에 접근하는 것처럼 보이지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부적으로는 미리 정의된 getter 와 setter로 원하는 작업을 해줄 수 있으므로, 어떤 값에 대해 전처리 작업이&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요한 경우에 접근자 프로퍼티를 유용하게 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;프로퍼티 설정하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지는 객체의 접근자 프로퍼티에 대해 알아보았고, 이제부터는 프로퍼티 자체에 더 집중해 볼것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로퍼티는 값 외에 다른 숨겨진 속성들을 더 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 속성들은 평소엔 사용할 일이 거의 없긴 하지만, 알고 있으면 그만큼 자바스크립트의 이해도가 올라가는 일이니&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공부해보는것도 좋을것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로퍼티의 상세 속성들은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;value&lt;/b&gt; - 우리가 평소에 사용하는 프로퍼티의 값이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;writable&lt;/b&gt; - false 면 값을 수정할 수 없다. (기본값 = true)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;enumarable&lt;/b&gt; - false 면 for...in 반복문으로 열거할 수 없다. (기본값 = true)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;configurable&lt;/b&gt; - false 면 프로퍼티를 삭제할 수도 없고 writable, enumarable 을 수정할 수 없다. (기본값 = true)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 속성들 중 value를 제외한 나머지 속성들을&lt;b&gt; 플래그(flag)&lt;/b&gt; 라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체에 프로퍼티가 만들어지면 그 플래그들의 값은 모두 true로 자동 세팅된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;프로퍼티 descriptor (설명자)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;- Object.getOwnPropertyDescriptor 메서드&lt;/b&gt;&lt;/i&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이 플래그들은 어떻게 확인 할 수 있을까?&lt;/p&gt;
&lt;pre id=&quot;code_1648540925762&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// obj.key 의 플래그들을 얻을 수 있다.
Object.getOwnPropertyDescriptor(obj, 'key')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내장 함수 Object의 getOwnPropertyDescriptor 메서드를 이용하면 해당 객체의 프로퍼티 플래그들을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 인자에는 객체를 넘겨주고, 두 번째 인자는 확인하고픈 프로퍼티의 키를 넘겨준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;583&quot; data-origin-height=&quot;189&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qg70U/btrxKivgHz4/p4d8pLwZq19K4YLFS6C5R0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qg70U/btrxKivgHz4/p4d8pLwZq19K4YLFS6C5R0/img.png&quot; data-alt=&quot;obj.name 의 속성들&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qg70U/btrxKivgHz4/p4d8pLwZq19K4YLFS6C5R0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fqg70U%2FbtrxKivgHz4%2Fp4d8pLwZq19K4YLFS6C5R0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;583&quot; height=&quot;189&quot; data-origin-width=&quot;583&quot; data-origin-height=&quot;189&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;obj.name 의 속성들&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림을 보면&amp;nbsp;&lt;span&gt;getOwnPropertyDescriptor&lt;span&gt;&amp;nbsp;메서드로&lt;/span&gt;&lt;/span&gt; obj 객체의 name 프로퍼티의 값과 플래그들을 담고있는 객체가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반환되는데, &lt;b&gt;이렇게 반환된 객체를 descriptor&lt;/b&gt; 라고 부른다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보다시피 프로퍼티가 만들어질때 플래그들은 모두 기본값으로 true를 가지고 있다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이 플래그값을 수정하려면 어떻게 해야할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;- Object.defineProperty 메서드&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;defineProperty 메서드를 사용하면 프로퍼티의 descriptor를 직접 세팅해줄 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1648559130629&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let obj = {};
Object.defineProperty(obj, 'name', {
    value: 'sunny',
    writable: true,
    configurable: true,
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예시코드를 보면 defineProperty 의 첫번째 인자로 객체, 두번째 인자는 descriptor를 설정해줄 프로퍼티 이름,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막 세 번째 인자로 descriptor 객체를 넘겨주어 직접 obj.name의 값과 플래그들을 설정해주고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 잘보면 enumerable 플래그는 값을 설정해주지 않고 생략했다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;정의되지 않은 프로퍼티를 defineProperty로 새로 만들어줄때 이렇게 플래그를 명시해주지 않으면&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그 플래그의 값은 자동으로 false가 된다.&lt;/b&gt; 한번 확인해 볼까?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;974&quot; data-origin-height=&quot;410&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/G92vd/btrxYcT35nh/FkDUkSJpG2lqx1y8LOKTT1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/G92vd/btrxYcT35nh/FkDUkSJpG2lqx1y8LOKTT1/img.png&quot; data-alt=&quot;defineProperty로 프로퍼티를 만든 후, getOwnPropertyDescriptor로 descriptor 객체 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/G92vd/btrxYcT35nh/FkDUkSJpG2lqx1y8LOKTT1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FG92vd%2FbtrxYcT35nh%2FFkDUkSJpG2lqx1y8LOKTT1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;775&quot; height=&quot;326&quot; data-origin-width=&quot;974&quot; data-origin-height=&quot;410&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;defineProperty로 프로퍼티를 만든 후, getOwnPropertyDescriptor로 descriptor 객체 확인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;enumerable 을 생략하고 프로퍼티를 정의한 후 getOwnPropertyDescriptor 메서드로 obj.name 의 descriptor 객체를&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력해보니, 정말 enumerable 플래그의 값이 false인 것을 확인할 수 있다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;- 접근자 프로퍼티의 descriptor&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 접근자 프로퍼티인 getter, setter의 descriptor도 데이터 프로퍼티와 같은 형태일까? 확인해 보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;759&quot; data-origin-height=&quot;403&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wLUQq/btrxQ5bot9K/0V6O8RkBs9IAYU6pDwKmD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wLUQq/btrxQ5bot9K/0V6O8RkBs9IAYU6pDwKmD1/img.png&quot; data-alt=&quot;접근자 프로퍼티의 descriptor&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wLUQq/btrxQ5bot9K/0V6O8RkBs9IAYU6pDwKmD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwLUQq%2FbtrxQ5bot9K%2F0V6O8RkBs9IAYU6pDwKmD1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;759&quot; height=&quot;403&quot; data-origin-width=&quot;759&quot; data-origin-height=&quot;403&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;접근자 프로퍼티의 descriptor&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오호, 뭔가 다른데?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림에서 볼 수 있듯이, 접근자 프로퍼티는 데이터 프로퍼티와는 달리 writable, value 가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대신 get, set 함수를 가지고 있는 모습이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;글 초반에 getter, setter를 설정할때 객체 리터럴로 선언시에 바로 만들어주는 방식을 예제 코드로 설명했었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이미 선언되고난 이후에 getter, setter를 만들어주려면? defineProperty를 사용해서 만들어주면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1648564357557&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let obj = {}
Object.defineProperty(obj, 'name', {
    get(){
        return '이름 : ' + this._name;
    },
    set(value){
        this._name = value + ' 입니다.';
    }
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;564&quot; data-origin-height=&quot;141&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bejHQh/btrxVHtQBlZ/sBzBU82lDVCIk8VaDzJQFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bejHQh/btrxVHtQBlZ/sBzBU82lDVCIk8VaDzJQFk/img.png&quot; data-alt=&quot;defineProperty로 만든 접근자 프로퍼티&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bejHQh/btrxVHtQBlZ/sBzBU82lDVCIk8VaDzJQFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbejHQh%2FbtrxVHtQBlZ%2FsBzBU82lDVCIk8VaDzJQFk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;564&quot; height=&quot;141&quot; data-origin-width=&quot;564&quot; data-origin-height=&quot;141&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;defineProperty로 만든 접근자 프로퍼티&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제처럼 이미 정의된 객체obj 에 defineProperty를 사용하여 getter와 setter를 만들어줄 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;플래그의 역할&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 객체 프로퍼티는 value 와 세 가지의 플래그들로 이루어져 있다는걸 알게되었다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 descriptor 객체의 각 플래그들의 역할은 무엇일까? 하나씩 알아보도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;1. writable 플래그&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;writable의 값이 true면 프로퍼티에 값을 쓸 수 있고 false면 쓸 수 없다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;609&quot; data-origin-height=&quot;307&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bM8NE5/btrxVGuQqFh/QCd0Xui4oyXOKE0o7a2j10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bM8NE5/btrxVGuQqFh/QCd0Xui4oyXOKE0o7a2j10/img.png&quot; data-alt=&quot;writable == false 일때&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bM8NE5/btrxVGuQqFh/QCd0Xui4oyXOKE0o7a2j10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbM8NE5%2FbtrxVGuQqFh%2FQCd0Xui4oyXOKE0o7a2j10%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;609&quot; height=&quot;307&quot; data-origin-width=&quot;609&quot; data-origin-height=&quot;307&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;writable == false 일때&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;writable 값이 false일때 프로퍼티 값을 수정해주는 예시이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분명 obj.name = 'kwang' 으로 값을 할당해줬지만, 아래에서 obj.name 을 출력해보면 값이 바뀌지 않았고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그대로 'sunny' 인걸 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 getter, setter 설명에서 setter 없이 getter만 만들어서 읽기전용 프로퍼티를 만든것과 같은 결과가 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로, 위 예시를 엄격모드에서 실행하면 &lt;span style=&quot;color: #ef6f53;&quot;&gt;Cannot assign&lt;/span&gt; 에러가 발생한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;901&quot; data-origin-height=&quot;285&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ceoBhx/btrxLSKnBbQ/TBDSsWZAD3ka4XkiIS7T80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ceoBhx/btrxLSKnBbQ/TBDSsWZAD3ka4XkiIS7T80/img.png&quot; data-alt=&quot;엄격모드에서 실행&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ceoBhx/btrxLSKnBbQ/TBDSsWZAD3ka4XkiIS7T80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FceoBhx%2FbtrxLSKnBbQ%2FTBDSsWZAD3ka4XkiIS7T80%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;901&quot; height=&quot;285&quot; data-origin-width=&quot;901&quot; data-origin-height=&quot;285&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;엄격모드에서 실행&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;2. enumerable 플래그&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체를 순회하기 위해서 가장 많이쓰는 방법은 for...in 반복문일 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;for...in 반복문은 열거 가능한 객체 프로퍼티만 가져올 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;b&gt;열거 가능하다는 것은 enumerable == true 인 프로퍼티를 말한다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1648560861571&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let obj = {
    name: 'sunny',
    age: 20,
    nat: 'Korea',
}

for(let key in obj){
    console.log(key);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;309&quot; data-origin-height=&quot;114&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byHd5n/btrxVFW3pZv/LKO9OjbKZESS94p9tHLq3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byHd5n/btrxVFW3pZv/LKO9OjbKZESS94p9tHLq3k/img.png&quot; data-alt=&quot;출력 모습&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byHd5n/btrxVFW3pZv/LKO9OjbKZESS94p9tHLq3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbyHd5n%2FbtrxVFW3pZv%2FLKO9OjbKZESS94p9tHLq3k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;309&quot; height=&quot;114&quot; data-origin-width=&quot;309&quot; data-origin-height=&quot;114&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출력 모습&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;obj의 프로퍼티들을 일반적인 방법으로 만들어졌기 때문에 enumerable 값은 true일 것이고 그래서 for...in 문에서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 프로퍼티에 접근 가능한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 enumerable 값을 false 로 바꾸면 어떻게 되는지 확인해보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;576&quot; data-origin-height=&quot;399&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ccj7dY/btrxH0O6sZe/P6U1oBIMk4cWQBQZP3WKD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ccj7dY/btrxH0O6sZe/P6U1oBIMk4cWQBQZP3WKD0/img.png&quot; data-alt=&quot;enumerable == false 일때&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ccj7dY/btrxH0O6sZe/P6U1oBIMk4cWQBQZP3WKD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fccj7dY%2FbtrxH0O6sZe%2FP6U1oBIMk4cWQBQZP3WKD0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;576&quot; height=&quot;399&quot; data-origin-width=&quot;576&quot; data-origin-height=&quot;399&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;enumerable == false 일때&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;obj.name 프로퍼티의 enumerable 플래그 값을 false로 만들고 for...in 을 실행해보니&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정말 'name' 이 출력되지 않는 모습을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;for...in 문은 사실 객체가 상속받아 쓰는 프로퍼티들도 모두 출력해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 생각해보면 객체는 생성자 함수 Object를 통해 만들어지기 때문에 모든 객체는 Object의 프로토타입 객체를&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참조하게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 Object.prototype에 정의되어있는 toStiring, hasOwnProperty, valueOf 같은 내장 메서드들을 그대로&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상속받듯이 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 신기하게도 for...in 문으로 객체를 순회할때 toString, hasOwnProperty 같은 메서드는 가져오지 않는다. 왜일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그건 바로 이 내장 메서드들의 enumerable은 false로 설정되어있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 바로 확인해보자.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;866&quot; data-origin-height=&quot;209&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dzmTUq/btrxNXSdaZ4/yzhwBxjlH01txGJkHQz5W1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dzmTUq/btrxNXSdaZ4/yzhwBxjlH01txGJkHQz5W1/img.png&quot; data-alt=&quot;내장 메서드의 descriptor&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dzmTUq/btrxNXSdaZ4/yzhwBxjlH01txGJkHQz5W1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdzmTUq%2FbtrxNXSdaZ4%2FyzhwBxjlH01txGJkHQz5W1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;866&quot; height=&quot;209&quot; data-origin-width=&quot;866&quot; data-origin-height=&quot;209&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;내장 메서드의 descriptor&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림에서 보이듯이 toString의 enumerable은 false 이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 일반 객체들을 for...in 으로 순회했을때 toString과 같은 내장 메서드들은 출력되지 않는것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 obj.toString = function(){...} 이런식으로 객체에 직접 같은 이름의 메서드를 오버라이딩 해주면 obj.toString은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당연히 for...in 에서 접근가능 할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이유는 객체 프로퍼티를 검색할때는 항상 제일 가까운곳에 있는 프로퍼티부터 탐색하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;3. configurable 플래그&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;configurable 은 플래그들을 수정할 수 있냐 없냐를 결정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;configurable 값을 false로 설정해주면, 기본적으로 프로퍼티를 삭제할 수 없게 되고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;enumerable, configurable, writable 값들을 수정할 수 없게된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(writable 을 false -&amp;gt; true 로 변경할 순 없지만 true -&amp;gt; false는 가능하다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 &lt;b&gt;한 번 &lt;span&gt;configurable값을 false 로 설정해주면 defineProperty를 사용해도 이를 다시 true로 되돌릴순 없다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;그래서 configurable값을 false로 설정해주는 경우는 어떤 속성이든 &lt;span&gt;절대로&lt;/span&gt; 변경해서는 안되는 프로퍼티인 경우에만&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;사용해야 한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;이렇게 객체 프로퍼티에 대해 알아보았는데, 정리해보니 내용이 꽤 길어졌다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;프로퍼티만 다뤄도 이정도인데.. 역시 JS잘알의 길은 멀고도 험한것같다.&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;잘못된 내용이 있으면 댓글 주세요 :)&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Javascript</category>
      <author>kwangsunny</author>
      <guid isPermaLink="true">https://kwangsunny.tistory.com/41</guid>
      <comments>https://kwangsunny.tistory.com/41#entry41comment</comments>
      <pubDate>Wed, 30 Mar 2022 00:09:11 +0900</pubDate>
    </item>
    <item>
      <title>자바스크립트 - this 바인딩</title>
      <link>https://kwangsunny.tistory.com/40</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트로 개발을 하다보면 this를 다룰일이 많이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트에서 this는 현재 코드가 실행되는 상황에 따라 할당되는 값이 달라지는데, 언제 어떤 값이 this에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;할당되는지 제대로 이해하지 않고 대충 얼버무리고 넘어간다면 어디가서 JS좀 한다 같은 소리는 고이 접어두자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;this 바인딩 이 정도면 다 이해한듯?&quot; &lt;span&gt;그 당시엔&lt;span&gt; 이렇게&amp;nbsp;&lt;/span&gt;&lt;/span&gt;생각이 들었어도, 시간이 흐르고 this로 복잡한 작업을 하다보면 금세 또 헷갈리고 인터넷을 찾게된다. 그래서 이번엔 this에 대해 최대한 정리해 보려한다.&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;엄격모드 - &quot;use strict&quot;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;this에 대해 살펴보기 전에 우선 엄격모드에 대해 알아볼 필요가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 엄격모드에 따라 this가 가리키는 값이 상황에따라 달라지기 때문이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엄격모드란?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트는 계속해서 업데이트되고 있는 언어이다. 그래서 시간이 흐름에따라 새로운 문법들과 기능들이&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가 변경 되면서 발전해왔다. (let, const, 화살표함수, 클래스, 스크립트 모듈화, 등등..)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가된 기능이야 그렇다 쳐도 수정된 기능을 그대로 모두 적용해버리면 수많은 레거시 코드들이 오작동을 일으키게 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 방지하고자 자바스크립트 설계자는 '엄격모드' 에서만 이런 변경사항들이 적용돼도록 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엄격모드를 적용하는 방법은 간단하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크립트 제일 상단에 문자열 &quot;use strict&quot; 를 명시해주면 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 그 다음 줄부터 실행되는 코드들은 모두 모던한 방식으로 작동하게 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 &quot;use strict&quot; 를 스크립트 최상단이 아닌 중간에 쓰면 엄격모드가 적용되지 않으니 이점을 주의하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 스크립트가 아니라 특정 함수에서만 엄격모드를 적용할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제처럼 함수 내부의 가장 상단에 &quot;use strict&quot; 를 명시해주면 이 함수만 엄격모드에서 실행된다.&lt;/p&gt;
&lt;pre id=&quot;code_1647961733382&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function test(){
    'use strict'
    // 이 함수만 엄격모드에서 수행됨
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 모듈 스크립트는 엄격모드가 기본값이라 따로 &quot;use strict&quot; 를 명시할 필요가 없다.&lt;/p&gt;
&lt;pre id=&quot;code_1647960940883&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script type=&quot;module&quot;&amp;gt;
  // 엄격모드로 실행됨
&amp;lt;/script&amp;gt;

&amp;lt;script&amp;gt;
  'use strict' // 스크립트 최상단에 명시해야 엄격모드 적용됨
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;함수와 this&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 함수를 호출했을때, 함수내에 this는 무엇을 담고 있을까.&lt;/p&gt;
&lt;pre id=&quot;code_1647915191031&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function fn(){
    console.log(this); // (A)   -&amp;gt; window ? undefined ?
}
fn();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수를 호출하면 (A)에서 &lt;b&gt;전역객체 window&lt;/b&gt;가 출력될 것이고, 엄격모드라면 &lt;b&gt;undefined&lt;/b&gt; 가 출력될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 this 바인딩의 가장 기본적인 상황이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 자바스크립트에서 모든 함수는 생성자 함수이기도 하다. 그래서 함수를 호출할 때 앞에 new 를 명시하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당함수는 객체를 반환하게 되고 생성자 함수 내부에서 사용된&lt;b&gt; this는 반환된 객체를 가리키게 된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성자 함수로 객체를 생성하는 예시를 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1647962506176&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function Person(){
    this.name = 'sunny';
    this.showThis = function(){
        console.log(this);
    }
}

let me = new Person();
me.showThis(); // (A)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;463&quot; data-origin-height=&quot;109&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/He5R4/btrwVrejBv5/I3qkqIimroVRR1HvR2ynj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/He5R4/btrwVrejBv5/I3qkqIimroVRR1HvR2ynj0/img.png&quot; data-alt=&quot;this 출력 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/He5R4/btrwVrejBv5/I3qkqIimroVRR1HvR2ynj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHe5R4%2FbtrwVrejBv5%2FI3qkqIimroVRR1HvR2ynj0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;463&quot; height=&quot;109&quot; data-origin-width=&quot;463&quot; data-origin-height=&quot;109&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;this 출력 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(A)에서 me.showThis 를 실행하여 this를 출력해보면 this는 생성된 객체 자신인걸 확인 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수가 new를 만났을때 객체가 생성되는 과정을 좀더 자세히 보면 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1647963489331&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function Person(){
    // this = {} --&amp;gt; 빈 객체가 할당된 this가 암묵적으로 만들어진다.
    this.name = 'sunny';
    this.showThis = function(){
        console.log(this);
    }
    // return this; --&amp;gt; 암묵적으로 this를 리턴한다.
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;new Person() 이 실행되면 위 주석에 달린 설명같이 자바스크립트가 암묵적으로 빈 객체 this를 생성하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로퍼티 값들을 (이 예제에선 name, showThis) 할당한 후 함수 끝에서 이 this를 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 new Person() 이 반환한 this가 새로운 객체가 되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 객체 프로퍼티에 할당된 함수 내부에 this가 있다면(위 예시의 showThis),&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 this는 함수가 속한 객체를 가리키게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이에 대한 설명은 다음에 나오는 메서드와 this 에서 더 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;메서드와 this&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트에서 함수는 다재다능하다. 함수는 다른 함수의 인자로 전달될 수 있고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체 프로퍼티에 할당될 수 있고, 객체처럼 쓰일 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 중에서 함수가 어떤 객체의 프로퍼티에 할당되면 이 함수를 메서드 라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;메서드는 this를 통해 메서드가 속한 객체에 접근할 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 함수가 그냥 호출될때 와 메서드로써 호출될때 this는 어떻게 달라지는지 아래 예시를 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1647918418415&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var myName = 'kwang'; // 함수 바깥에서 선언된 var 변수는 전역객체의 프로퍼티가 됨.

function fn(){
    console.log(this);
    console.log(this.myName);
}

let obj = {
    myName: 'sunny',
    func: fn, // (A)
}

fn(); // (B)
obj.func(); // (C)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(A)에서 obj 의 프로퍼티로 함수 fn을 할당해주고 있다. 이제 obj.func 은 obj 의 메서드라고 부를 수 있다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 fn 이 일반함수로써 호출됐을때 와 메서드로서 호출됐을때의 결과는 어떻게 나올까?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;87&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/curQL6/btrwLg5Dndp/KCtYruf4i29kjVai715iSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/curQL6/btrwLg5Dndp/KCtYruf4i29kjVai715iSK/img.png&quot; data-alt=&quot;실행 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/curQL6/btrwLg5Dndp/KCtYruf4i29kjVai715iSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcurQL6%2FbtrwLg5Dndp%2FKCtYruf4i29kjVai715iSK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;87&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;87&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실행 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과는 완전 다르게 나왔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 (B) 처럼 그냥 호출하면 this에는 전역객체 window가 할당되기 때문에 this.myName 도&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;window의 myName 프로퍼티를 출력하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 (C) 에서는 obj의 메서드로서 호출되었기 때문에 이때 fn 내부의 this 는 자신을 호출한&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체가 담기게 되어 this.myName 의 값은 obj.myName와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 객체 A, B가 있다고 할때, A의 메서드를 B의 프로퍼티로 할당해주면 어떻게 될까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B에서 이 메서드를 실행하면 this 는 A, B 중 어떤 객체를 가리키게 될까?&lt;/p&gt;
&lt;pre id=&quot;code_1647925949649&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let A = {
    myName: 'A',
    func: function(){
        console.log(this);
        console.log(this.myName);
    }, 
}

let B = {
    myName: 'B',
    func: A.func,  // (A)
}

A.func();
B.func();&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;237&quot; data-origin-height=&quot;87&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJe3wU/btrwIhqZTBi/okaThExllpzz6Y4a2FglMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJe3wU/btrwIhqZTBi/okaThExllpzz6Y4a2FglMk/img.png&quot; data-alt=&quot;실행 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJe3wU/btrwIhqZTBi/okaThExllpzz6Y4a2FglMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJe3wU%2FbtrwIhqZTBi%2FokaThExllpzz6Y4a2FglMk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;237&quot; height=&quot;87&quot; data-origin-width=&quot;237&quot; data-origin-height=&quot;87&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실행 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행결과를 보면 this는 호출된 함수가 속한 객체를 가리키고 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(A) 에서 B.fucn = A.func 를 해주고 있는데 왠지 B.func 를 실행하면 this 는 A 를 가리킬것 같지만 그렇지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메서드는 자신을 호출한 객체를 this로 할당한다. 그래서 &lt;b&gt;같은 함수를 공유한다 하더라도 this의 값은&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;호출되는 시점에 어떤 객체에서 호출했냐에 따라 그 메서드의 this 값은 달라질 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드상으로 봤을때&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt; this는 점(.) 앞에 객체를 참조하게 된다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 A.func() 에서 func의 this는 점(.)앞에 있는 A 가 되고, 마찬가지로 B.func() 에서 func의 this는 B를 가리키게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 위처럼 메서드가 객체에 의해 실행되는게 아니라 따로 떨어져 나와 단독으로 호출되면 this에는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 값이 들어있을까? 예제를 통해 알아보자.&lt;/p&gt;
&lt;pre id=&quot;code_1647930124158&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let obj = {
    myName: 'sunny',
    func: function(){
    	console.log('name is ' + this.myName);
    }
}

let say = obj.func; // (A)
say();

setTimeout(obj.func, 1000); // (B)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;231&quot; data-origin-height=&quot;62&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9xytU/btrwUlq3d0V/ydDuSpILw6Em8xSR63fyck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9xytU/btrwUlq3d0V/ydDuSpILw6Em8xSR63fyck/img.png&quot; data-alt=&quot;실행 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9xytU/btrwUlq3d0V/ydDuSpILw6Em8xSR63fyck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9xytU%2FbtrwUlq3d0V%2FydDuSpILw6Em8xSR63fyck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;231&quot; height=&quot;62&quot; data-origin-width=&quot;231&quot; data-origin-height=&quot;62&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실행 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(85는 setTimeout이 반환한 타이머값 이므로 무시!)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메서드가 단독으로 호출된다는 의미는 위의 (A), (B) 와 같은 상황들을 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;메서드가 object.method() 와 같은 형태로 호출되지 않고 어떤 변수에 담기거나, 콜백함수로써 다른 함수에 전달되어&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;호출될때, 메서드는 일반함수를 호출할 때와 같은 규칙을 따르게되서 this는 전역객체를 가리키게 된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(A)에서 obj.func 메서드는 say 라는 변수에 담기고 다음 줄에서 obj.func() 형태로 obj 라는 객체에 의해 호출되는것이&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아니라 say() 와 같이 단독으로 호출되고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(B)의 경우 setTimeout의 콜백으로 obj.func 메서드가 전달되고 있는데, setTimeout 은 obj라는 객체가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;있는지는 전혀 알지 못하고 단지 obj.func 이 가리키는 함수만 알고 있을 뿐이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 1초 후에 실행되는 콜백은 일반함수처럼 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;두 실행 결과가 모두 sunny가 아닌 undefined로 나온 이유도 메서드가 &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;일반함수처럼 호출되어 this가 window 객체를 &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;가리키므로 &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;/span&gt;window.myName --&amp;gt; undefined 가 되기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(엄격모드에서 실행했다면 this == undefined 이므로 에러가 발생할 것이다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 여기서 문제.&lt;/p&gt;
&lt;pre id=&quot;code_1647931546402&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;'use strict'
function makeUser() {
  return {
    name: &quot;John&quot;,
    ref: this
  };
};

let user = makeUser(); // (가)

console.log(user.ref.name); // (A)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(A) 에서 출력되는 값은 뭘까? ( 참고로 엄격모드로 실행했다 -&amp;gt; 'use strict' )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정답은 에러가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력 : &lt;span style=&quot;color: #ee2323;&quot;&gt;Cannot&amp;nbsp;read&amp;nbsp;properties&amp;nbsp;of&amp;nbsp;undefined&amp;nbsp;(reading&amp;nbsp;'name')&lt;/span&gt; &amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서 makeUser 함수는 객체를 반환하고 있고, 이 객체는 ref 프로퍼티 값으로 this를 가지고있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(가) 에서는 makeUser함수를 호출하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 makeUser는 객체에 의해 호출된것이 아니라 일반 함수로써 호출된 것이기 때문에, this는 (엄격모드 이므로) undefied를 가리키게 될 것이고, 변수 user는 { name: 'John', ref: undefined } 가 될것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 (A)에서 에러가 발생하게 되는것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 문제 예시는 아래 링크를 참고했다. this 바인딩에 대해 어설프게 알고 있었다면 꽤나 헷갈릴만한 좋은 예시이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;링크에는 문제에 대한 해설이 더 자세히 나와있으니 잘 읽어보면 좋을것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ko.javascript.info/task/object-property-this&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ko.javascript.info/task/object-property-this&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1647933176693&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;객체 리터럴에서 'this' 사용하기&quot; data-og-description=&quot;함수 makeUser는 객체를 반환합니다. 이 객체의 ref에 접근하면 어떤 결과가 발생하고, 그 이유는 뭘까요? function makeUser() { return { name: &amp;quot;John&amp;quot;, ref: this }; }; let user = makeUser(); alert( user.ref.name ); // 결과가&quot; data-og-host=&quot;ko.javascript.info&quot; data-og-source-url=&quot;https://ko.javascript.info/task/object-property-this&quot; data-og-url=&quot;https://ko.javascript.info/task/object-property-this&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/pPRQ5/hyNNrgAeO6/C3dOxatcxkYzUZfOlrr6l1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/PWZCM/hyNLIRLAVk/bheE1M1q3FqRYmoRAa2691/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512&quot;&gt;&lt;a href=&quot;https://ko.javascript.info/task/object-property-this&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ko.javascript.info/task/object-property-this&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/pPRQ5/hyNNrgAeO6/C3dOxatcxkYzUZfOlrr6l1/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/PWZCM/hyNLIRLAVk/bheE1M1q3FqRYmoRAa2691/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;객체 리터럴에서 'this' 사용하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;함수 makeUser는 객체를 반환합니다. 이 객체의 ref에 접근하면 어떤 결과가 발생하고, 그 이유는 뭘까요? function makeUser() { return { name: &quot;John&quot;, ref: this }; }; let user = makeUser(); alert( user.ref.name ); // 결과가&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ko.javascript.info&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;this의 명시적 바인딩&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 메서드가 단독으로 어떤 변수에 담겨서 호출되거나, 콜백함수로써 호출되면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반 함수가 호출될때처럼 this에는 전역객체 (엄격모드에선 undefined) 가 할당된다고 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이런 경우에 this가 바뀌지 않도록 고정해줄 수 있는 방법은 없는걸까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떻게 하면 메서드가 단독으로 호출돼도 this를 잃지 않고 자신이 속한 객체를 this로써 사용할 수 있을지&amp;nbsp; &amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예시를 통해 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 외부 렉시컬 환경 이용하기&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1647934051864&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function test(){
    let obj = {
        myName: 'sunny',
        func: function(){
            console.log('name is ' + this.myName);
        }
    }
    
    setTimeout(obj.func); // (A)
    
    setTimeout(function(){ // (B)
    	obj.func();
    });
}

test();&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;208&quot; data-origin-height=&quot;45&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bybbYn/btrwTM3CYZ7/Dr0UHtIoBy5Y9WCo6hnUL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bybbYn/btrwTM3CYZ7/Dr0UHtIoBy5Y9WCo6hnUL1/img.png&quot; data-alt=&quot;실행 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bybbYn/btrwTM3CYZ7/Dr0UHtIoBy5Y9WCo6hnUL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbybbYn%2FbtrwTM3CYZ7%2FDr0UHtIoBy5Y9WCo6hnUL1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;208&quot; height=&quot;45&quot; data-origin-width=&quot;208&quot; data-origin-height=&quot;45&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실행 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 간단한 방법은 외부 렉시컬 환경에서 해당 객체에 직접 접근하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수는 호출될때 렉시컬 환경이 생성되고 이 렉시컬 환경은 &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;외부의 렉시컬 환경의 참조값을 가지고 있어서&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;외부 변수에 접근할 수 있는데 이 원리를 이용하는 것이다.&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;(A)에서 setTimeout의 콜백으로 메서드를 바로 넘겨준것과 달리 (B) 에서는 익명함수를 넘겨주고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;이 익명함수는 자신의 외부 렉시컬 환경인 test함수의 렉시컬 환경을 알고 있으므로 obj에 접근할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;그래서 obj.func() 형태로 메서드가 호출되었기 때문에&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;(B)의 실행 결과는 undefined 가 아니라 sunny가 출력되게 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 바인딩 메서드 이용하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두번째 방법은 this에 어떤 객체를 바인딩할지 직접 명시해주는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;함수 내장 메서드인 bind / call / apply&lt;/b&gt; 를 사용하면 호출된 메서드 내의 this가 가리키는 값을 직접 정해줄 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;392&quot; data-origin-height=&quot;321&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJ2dAz/btrwTZP6yIL/kiFhkZkYoacPej56PTyNTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJ2dAz/btrwTZP6yIL/kiFhkZkYoacPej56PTyNTK/img.png&quot; data-alt=&quot;함수의 내부 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJ2dAz/btrwTZP6yIL/kiFhkZkYoacPej56PTyNTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJ2dAz%2FbtrwTZP6yIL%2FkiFhkZkYoacPej56PTyNTK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;392&quot; height=&quot;321&quot; data-origin-width=&quot;392&quot; data-origin-height=&quot;321&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;함수의 내부 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림에서 볼 수 있듯이 함수는 new Function() 에 의해 만들어진 객체이고, Function의 프로토타입에 bind, call, apply 가 이미 정의되어 있기 때문에 모든 함수는 bind, call, apply 메서드를 기본으로 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;bind 메서드&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;func.bind(obj) 와 같이 bind 메서드의 첫번째 인자에 객체를 넘겨주면, this가 obj 로 고정된 새로운 함수가 반환된다.&lt;/p&gt;
&lt;pre id=&quot;code_1648025325780&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function fn(){
    console.log(this);
}
let boundFunc = fn.bind({a: '123'});

boundFunc(); // (A)
fn() // (B)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드의 (A) 부분에서 출력되는 내용은 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;667&quot; data-origin-height=&quot;47&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/t9C7K/btrwVrGLTKo/6FuzZ8xdtAx5zuoI5IQT1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/t9C7K/btrwVrGLTKo/6FuzZ8xdtAx5zuoI5IQT1k/img.png&quot; data-alt=&quot;bind 사용에 따라 this의 값이 달라졌다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/t9C7K/btrwVrGLTKo/6FuzZ8xdtAx5zuoI5IQT1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ft9C7K%2FbtrwVrGLTKo%2F6FuzZ8xdtAx5zuoI5IQT1k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;667&quot; height=&quot;47&quot; data-origin-width=&quot;667&quot; data-origin-height=&quot;47&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;bind 사용에 따라 this의 값이 달라졌다&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(B)처럼 그냥 함수를 호출한 경우 당연히 this는 전역객체를 가리키게 되지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(A)에서는 this가 bind 의 첫번째 인자로 넘겨준 객체 {a: '123'} 을 가리키고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 식으로 bind 를 사용하면 메서드가 단독으로 사용될때 처럼 this값이 변하는 상황에 &lt;b&gt;명시적으로 this를&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;고정&lt;/b&gt;해줄 수 있게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법으로 setTimeout의 콜백으로 함수를 넘겨줄때도 bind 메서드를 사용하면 this 값을 의도한대로 쓸 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1648038923889&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function test(){
    let obj = {
        myName: 'sunny',
        func: function(){
            console.log('name is ' + this.myName);
        }
    }
    setTimeout(obj.func.bind(obj)); // 출력 : name is sunny
}

test();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;.bind(obj)를 통해 반환된 함수는 this가 obj로 고정되어 원하는대로 잘 실행되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 bind는&amp;nbsp;첫번째 인자 뒤에 다중 인자를 받을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bind(context, arg1, arg2, arg3, .... ) 이런 식으로 사용할 수 있는데, 두번째부터 넘겨지는 인자들은 bind가 반환한&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수가 실행될때 순서대로 함수의 인수로 들어온다.&lt;/p&gt;
&lt;pre id=&quot;code_1648039785103&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function fn(a, b, c){
    console.log(`${this} / ${a} / ${b} / ${c}`);
    console.log(arguments);
}
let test = fn.bind(null, 1, 2); // (A)
test('test'); // (B)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;759&quot; data-origin-height=&quot;182&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tqauY/btrw63xrYUS/4cgGOoIDlFfY4FK22A9pWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tqauY/btrw63xrYUS/4cgGOoIDlFfY4FK22A9pWK/img.png&quot; data-alt=&quot;바인딩 함수 실행 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tqauY/btrw63xrYUS/4cgGOoIDlFfY4FK22A9pWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtqauY%2Fbtrw63xrYUS%2F4cgGOoIDlFfY4FK22A9pWK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;759&quot; height=&quot;182&quot; data-origin-width=&quot;759&quot; data-origin-height=&quot;182&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;바인딩 함수 실행 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(A)에서 fn의 바인딩 대상으로 null 을 넘겨주고 있는데 이땐 일반함수를 호출할때와 같이 this에는 전역객체가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바인딩된다. 또 그 뒤로 1, 2 를 차례대로 넘겨주고 test 에 바인딩된 새로운 함수가 할당된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(B)에서 바인딩된 test 함수를 실행하면 위와 같은 결과를 얻을 수 있는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;test함수를 호출할때 넘겨준 test보다 bind 메서드를 호출할때 넘겨준 1, 2 가 더 먼저 위치하게되는 모습을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 기능을 이용하면&lt;b&gt; this 뿐만아니라 인자들도 미리 고정시킨 새로운 바인딩 함수를 만들어 사용할 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1648040628815&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function add(a, b){
    console.log(a + b);
}
let add3 = add.bind(null, 3);
let add5 = add.bind(null, 5);

add3(10); // 3 + 10 = 13
add5(10); // 5 + 10 = 15&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 add 함수를 bind 메서드의 인수 고정을 이용해 특정 기능을 수행하는 함수를 만들어낼 수 있고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 부분적용 이라고 부른다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로, bind 메서드를 통해 반환된 함수는 다시 bind를 해도 this값은 바뀌지 않는다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;595&quot; data-origin-height=&quot;158&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8JOBs/btrw64JU7H5/KpeYGNXWfNNwYLEwp2Ezd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8JOBs/btrw64JU7H5/KpeYGNXWfNNwYLEwp2Ezd0/img.png&quot; data-alt=&quot;두 번 bind를 실행한 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8JOBs/btrw64JU7H5/KpeYGNXWfNNwYLEwp2Ezd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8JOBs%2Fbtrw64JU7H5%2FKpeYGNXWfNNwYLEwp2Ezd0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;595&quot; height=&quot;158&quot; data-origin-width=&quot;595&quot; data-origin-height=&quot;158&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;두 번 bind를 실행한 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림에서 볼 수 있듯이, bind 를 통해 반환된 함수가 다시 bind로 다른 객체를 넘겨주어도 this의 값은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바뀌지 않고 처음 바인딩된 객체를 가리킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(인수도 넘겨주었다면, 인수들도 역시 처음 바인딩된 상태에서 바뀌지 않는다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;call, apply 메서드&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;call 과 apply 메서드는 한 가지만 빼고 bind 와 똑같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;차이점은 바로 bind 메서드가 새로운 함수를 반환하는것과 달리 call, apply 메서드는 그 자리에서 함수를 호출한다는 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;422&quot; data-origin-height=&quot;130&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pkmr5/btrw7QYVqtK/wksbqsBHKrGQD5KJIvMezk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pkmr5/btrw7QYVqtK/wksbqsBHKrGQD5KJIvMezk/img.png&quot; data-alt=&quot;call 실행 모습&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pkmr5/btrw7QYVqtK/wksbqsBHKrGQD5KJIvMezk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fpkmr5%2Fbtrw7QYVqtK%2FwksbqsBHKrGQD5KJIvMezk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;422&quot; height=&quot;130&quot; data-origin-width=&quot;422&quot; data-origin-height=&quot;130&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;call 실행 모습&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 결과를 보면 bind와 달리 call은 해당 함수를 그 자리에서 바로 호출한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;apply 메서드는 call과 똑같은데 추가 인자를 넘기는 방식만 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;call 은 bind 메서드처럼 추가 인자들을 하나씩 콤마로 구분지어 넘겨주는데 반해, apply 메서드는 두번째 자리에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;유사배열 객체를&lt;/b&gt; 받는다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;714&quot; data-origin-height=&quot;276&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m05Nm/btrwXyludha/rT84gf22kZHJ2eIAuKCI81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m05Nm/btrwXyludha/rT84gf22kZHJ2eIAuKCI81/img.png&quot; data-alt=&quot;apply 메서드로 추가 인자를 넘기는 모습&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m05Nm/btrwXyludha/rT84gf22kZHJ2eIAuKCI81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm05Nm%2FbtrwXyludha%2FrT84gf22kZHJ2eIAuKCI81%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;714&quot; height=&quot;276&quot; data-origin-width=&quot;714&quot; data-origin-height=&quot;276&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;apply 메서드로 추가 인자를 넘기는 모습&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;call 메서드가 추가 인자를 하나씩 넘기는것과 달리 유사배열 객체(프로퍼티 키가 숫자고, length 프로퍼티를 가진 객체) 를 넘겨주고 있지만 실행 결과는 두 메서드가 똑같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이벤트 콜백과 this&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;html 요소 div 가 있다고 하자. 이 div를 클릭했을때 호출되는 핸들러 함수에서 this는 무엇을 가리키고 있을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;html 요소에 이벤트를 핸들러를 할당하는 방식은 세 가지가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. html 속성값으로 할당&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. on&amp;lt;event&amp;gt; 프로퍼티로 할당&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. addEventListener 로 할당&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 세 가지 방식중 뭘 사용하든&lt;b&gt; 이벤트 콜백에서 this는 이벤트가 발생한 요소를 가리키게된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, this === event.currentTarget 이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 진짜 그렇게 되는지 확인해보자.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1648043540242&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;button onclick=&quot;clickA(event)&quot;&amp;gt;A&amp;lt;/button&amp;gt;
&amp;lt;button id=&quot;btnB&quot;&amp;gt;B&amp;lt;/button&amp;gt;
&amp;lt;button id=&quot;btnC&quot;&amp;gt;C&amp;lt;/button&amp;gt;

&amp;lt;script&amp;gt;
    function clickA(e){
    	console.log(this);
        console.log(e.currentTarget === this);
    }
    
    btnB.onclick = function(e){
    	console.log(this);
        console.log(e.currentTarget === this);
    }
    
    btnC.addEventListener(&quot;click&quot;, function(e){
        console.log(this);
        console.log(e.currentTarget === this);
    });
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트 핸들러 할당 방식 1, 2, 3 순서대로 버튼 A, B, C 를 만들어 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 각각의 핸들러에서는 this값과 해당 이벤트가 발생된 요소(currentTarget)가 같은 놈인지를&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출력해줄 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 번씩 클릭한 결과는 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;666&quot; data-origin-height=&quot;196&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsTmCk/btrw7QSbVR0/QWFojM3WRjwiKFph19ZZNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsTmCk/btrw7QSbVR0/QWFojM3WRjwiKFph19ZZNk/img.png&quot; data-alt=&quot;각 버튼을 클릭한 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsTmCk/btrw7QSbVR0/QWFojM3WRjwiKFph19ZZNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsTmCk%2Fbtrw7QSbVR0%2FQWFojM3WRjwiKFph19ZZNk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;666&quot; height=&quot;196&quot; data-origin-width=&quot;666&quot; data-origin-height=&quot;196&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;각 버튼을 클릭한 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;on&amp;lt;evnet&amp;gt; 방식과 addEventListener 방식의 콜백함수에서 this는 이벤트가 발생한 요소를 가리키고 있는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모습을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 html 속성으로 할당해준 핸들러의 this는 왜 전역객체를 가리키는걸까..?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이유는 간단하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;html 속성으로 할당해준 이벤트는 해당 이벤트가 발생했을때 속성값을 본문으로 삼는 새로운 함수를&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만들어 실행하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &amp;lt;button onclick=&quot;clickA(event)&quot;&amp;gt;A&amp;lt;/button&amp;gt; 이 버튼은 클릭 이벤트가 발생되면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드와 같이 속성값을 실행하게 된다. 그래서 clickA 는 일반함수가 호출될때와 같은 규칙을 따라 this에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전역객체 할당되어 위 결과에서 window 객체가 출력된 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1648044348379&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(function(){
    clickA(event);
})()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 html 속성으로 이벤트 함수를 할당할때 onclick=&quot;func&quot; 가 아니라 onclick=&quot;func()&quot; 이렇게 괄호를 붙여서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 호출하는 형태로 써주는것도 바로 이 이유때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;화살표 함수와 this&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 화살표 함수에서 this는 어떤값을 가리킬지 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화살표 함수는 위의 예시들과는 달리 this를 바인딩하지 않는다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 봤던 this 바인딩은 함수가 호출되는 시점에 발생하는 &lt;b&gt;동적 바인딩&lt;/b&gt; 이지만, 화살표 함수는 &lt;b&gt;정적 바인딩&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;정적 바인딩이란, 함수가 선언된 순간 코드상으로 바로 바깥쪽에 있는 스코프의 this를 사용한다는 뜻이다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;473&quot; data-origin-height=&quot;156&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/q2s6u/btrw45bJGJo/MmnaokGKLkE2xQkOERxhk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/q2s6u/btrw45bJGJo/MmnaokGKLkE2xQkOERxhk0/img.png&quot; data-alt=&quot;화살표함수에서 this의 값&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/q2s6u/btrw45bJGJo/MmnaokGKLkE2xQkOERxhk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fq2s6u%2Fbtrw45bJGJo%2FMmnaokGKLkE2xQkOERxhk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;473&quot; height=&quot;156&quot; data-origin-width=&quot;473&quot; data-origin-height=&quot;156&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;화살표함수에서 this의 값&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;call 메서드를 이용해 객체{a: 'aaa'} 를 this값으로 바인딩하여 test함수를 호출하고있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 setTimeout의 콜백으로 화살표 함수를 넘겨주고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호출된 test 함수의 this는 {a: 'aaa'} 객체를 가리키고 있으므로 setTimeout의 콜백으로 넘겨진 화살표 함수는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드상 자신이 만들어진 위치의 바깥범위(test 함수) 의 this를 그대로 사용하게 된것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 화살표함수가 아닌 일반함수를 콜백으로 넘겨줬다면 window 객체가 출력됐을것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;695&quot; data-origin-height=&quot;203&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4rIsY/btrwWl0hAzi/YcsbsrCTnR9yHxcXp92hy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4rIsY/btrwWl0hAzi/YcsbsrCTnR9yHxcXp92hy0/img.png&quot; data-alt=&quot;화살표함수와 다른 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4rIsY/btrwWl0hAzi/YcsbsrCTnR9yHxcXp92hy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4rIsY%2FbtrwWl0hAzi%2FYcsbsrCTnR9yHxcXp92hy0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;695&quot; height=&quot;203&quot; data-origin-width=&quot;695&quot; data-origin-height=&quot;203&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;화살표함수와 다른 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상황에따라 달라지는 this값을 총정리 해봤는데, 역시 만만치 않다.. 피곤하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘못된 내용이 있다면 댓글 주세요 :)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Javascript</category>
      <category>apply</category>
      <category>BIND</category>
      <category>call</category>
      <category>This</category>
      <category>바인딩</category>
      <category>화살표 함수</category>
      <author>kwangsunny</author>
      <guid isPermaLink="true">https://kwangsunny.tistory.com/40</guid>
      <comments>https://kwangsunny.tistory.com/40#entry40comment</comments>
      <pubDate>Wed, 23 Mar 2022 23:42:20 +0900</pubDate>
    </item>
    <item>
      <title>자바스크립트 - 매크로태스크(Macrotask) 와 마이크로태스크(Microtask) 그리고 작업 스케줄링</title>
      <link>https://kwangsunny.tistory.com/39</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 &lt;a title=&quot;실행 컨택스트&quot; href=&quot;https://kwangsunny.tistory.com/37&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;실행 컨택스트에&lt;/a&gt; 대한 내용을 포스팅 한 적이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복습겸 짧게 요약하면, 실행 컨택스트는 현재 실행중인 코드의 환경 정보를 가진 데이터 구조이고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수가 호출될 때 만들어져서 호출스택에 push 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수가 끝나면 (return 되면) 호출스택에서 pop 되고 메모리에서 제거된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 컨택스트 관리는 자바스크립트 엔진이 수행하는 가장 중요하면서도 기본적인 역할이고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 개념만 제대로 이해해도 대단한 일이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이것만 가지고는 브라우저의 작동 매커니즘을 모두 이해했다고 할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 브라우저에서는 DOM 이벤트, 네트워크 요청, 렌더링, 비동기 처리 등 수 많은 일들이 동시 다발적으로 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무거운 작업을 수행할때도 브라우저가 버벅이거나 먹통이 되지 않도록 앱을 잘 설계하기 위해선&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저가 어떻게 이 모든 것들을 처리하는지를 이해할 필요가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;자바스크립트 실행 환경 - 호스트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트는 원래 브라우저 환경에서 사용하려고 만든 언어이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간이 흘러 자바스크립트는 점점 발전했고, 자바스크립트는 브라우저뿐 아니라 다른 환경에서도 사용되기 시작했는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(대표적인 예로 node.js) 이렇게 &lt;b&gt;자바스크립트가 돌아가는 환경을 호스트(host) 라 부르고, 호스트마다 해당 환경에&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;특화된 기능을 제공한다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저는 웹페이지를 제어하는데 필요한 여러 기능들을 제공하는데 그 기능의 최상단이 바로 window 객체이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;window객체는 전역 객체로써 setTimeout, XMLHttpRequest, alert ... 등 페이지를 제어하기 위한 다양한 메서드를 가지고 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 기능들은 자바스크립트 코어 기능이 아니라 브라우저라는 호스트에서 제공하는 기능으로 Web APIs 라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 그림을 통해 브라우저 환경을 살펴보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;760&quot; data-origin-height=&quot;522&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Q8pqo/btrwiy0evIO/nYtes2kKKKYjEi5jglxnoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Q8pqo/btrwiy0evIO/nYtes2kKKKYjEi5jglxnoK/img.png&quot; data-alt=&quot;브라우저 구성 요소&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Q8pqo/btrwiy0evIO/nYtes2kKKKYjEi5jglxnoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQ8pqo%2Fbtrwiy0evIO%2FnYtes2kKKKYjEi5jglxnoK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;760&quot; height=&quot;522&quot; data-origin-width=&quot;760&quot; data-origin-height=&quot;522&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;브라우저 구성 요소&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 자바스크립트 엔진 부분을 보면, 힙과 호출스택으로 구성되어있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;힙에는 코드에서 선언된 객체가 실제로 할당되는 메모리 공간이다. 호출스택에서 코드를 실행하다 객체를&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만나면 참조값을 통해 힙에 저장되어 있는 객체에 접근한다. 호출스택은 실행 컨택스트들이 쌓이고 실행되는 공간이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흔히 자바스크립트는 싱글 스레드 언어라고 말하는데, 이건 호출스택이 한 개 존재한다는 뜻과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호출스택에서는 한 번에 하나씩 코드를 실행한다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 번에 하나씩...? 그런데 실제 우리는 브라우저에서 웹서핑을 할때 화면이 그려지는 동시에 목록 조회,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크롤, 클릭, 키입력 등등 다양한 행위들을 하는데도 이 모든것들이 동시에 처리되는것 처럼 보인다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트 엔진이 관리하는 호출스택은 하나인데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크 요청, 사용자 이벤트, 렌더링, 등 비동기적으로 발생하는 작업들은 대체 어떻게 처리되는 걸까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;매크로태스크, 마이크로태스크&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 언급했던 비동기 작업들은 태스크큐 라는 저장 공간에 들어간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;태스크큐는 &lt;b&gt;발생한 순서대로 큐에 쌓이고 이벤트 루프에 의해 처리된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;태스크큐는 매크로태스크큐 와 마이크로태스크 로 구분할 수 있는데, 이 둘의 차이는 처리할&lt;b&gt; 작업의 우선순위&lt;/b&gt; 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매크로와 마이크로에 속하는 작업들은 아래와 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;매크로태스크&lt;/b&gt; - DOM 이벤트 콜백, 타이머(setTimeout, setInterval), 스크립트 로딩 등&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;마이크로태스크&lt;/b&gt; - 프로미스 핸들러 (then / catch / finally) + await,&amp;nbsp; 옵저버 (MutationObserver 등)&amp;nbsp; &amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;마이크로태스크는 매크로태스크 보다 우선순위가 높다. &lt;/b&gt;&lt;b&gt;그래서 항상 마이크로태스크의 작업이 더 먼저 처리된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이벤트 루프 (Event Loop)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림을 보면 이벤트 루프가 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이벤트 루프는 자바스크립트 엔진 바깥에서 자바스크립트 엔진을 도와 비동기 작업을 가능하게 해주는 녀석이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처리해야 할 태스크는 Web API 에서 잠깐 대기 하다가, 매크로태스크 또는 마이크로태스크로 들어간다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트 루프는 작업할 태스크가 없으면 대기 상태가 되고, 이때는 CPU를 소모하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러다 태스크가 큐에 들어오면 이벤트 루프는 아래와 같은 순서로 일을한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 호출스택이 비었는지 지속적으로 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 호출스택이 비게 되면 제일먼저 마이크로태스크 큐를 확인하고 가장 오래된 태스크부터 꺼내서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;호출스택으로 전달해 주는데, 이걸 &lt;b&gt;마이크로태스크 큐가 빌 때까지 수행한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 모든 마이크로태스크가 처리된 직후, 렌더링 작업이 필요하면 렌더링을 수행한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 매크로태스크 큐를 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 매크로태스크 큐에서 가장 오래된 태스크 하나를 꺼내 호출스택에 전달해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. 다시 1번 으로 돌아간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 절차를 아래 예시를 통해 더 자세히 살펴보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;561&quot; data-origin-height=&quot;213&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buXvUc/btrwkphn8Y6/qv3EFhxNGPCSj88GKfkpJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buXvUc/btrwkphn8Y6/qv3EFhxNGPCSj88GKfkpJ1/img.png&quot; data-alt=&quot;처리되길 기다리는 태스크들&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buXvUc/btrwkphn8Y6/qv3EFhxNGPCSj88GKfkpJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbuXvUc%2Fbtrwkphn8Y6%2Fqv3EFhxNGPCSj88GKfkpJ1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;561&quot; height=&quot;213&quot; data-origin-width=&quot;561&quot; data-origin-height=&quot;213&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;처리되길 기다리는 태스크들&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 호출스택에 작업들이 많아서 자바스크립트 엔진이 바쁜 와중에 여러 비동기 작업들이 큐에 쌓여있는 상황이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트 루프는 태스크들을 처리하기 위해 호출 스택이 비었는지 계속 확인한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호출스택이 비었다면, 이벤트 루프는 가장 먼저 마이크로태스크 큐에 쌓여있는 태스크들을&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Promise then -&amp;gt; Promise then -&amp;gt; Observer callback 순서로 모두 처리할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 매크로태스크 큐를 처리하기 전에 UI 렌더링 작업이 필요하면 렌더링을 이때 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 매크로태스크 큐의 click callback 을 처리하고 다시 마이크로태스크 큐를 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 마이크로태스크 큐에 처리할 태스크들이 또 쌓여있다면 그것들을 모두 처리한 후 다시 렌더링 작업을 수행하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매크로태스크의 setTimeout callback 을 처리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;527&quot; data-origin-height=&quot;294&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rgn4K/btrwixG4Edl/mw22wIkUq8INnaPdV6hnok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rgn4K/btrwixG4Edl/mw22wIkUq8INnaPdV6hnok/img.png&quot; data-alt=&quot;위 예시의 작업 순서&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rgn4K/btrwixG4Edl/mw22wIkUq8INnaPdV6hnok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Frgn4K%2FbtrwixG4Edl%2Fmw22wIkUq8INnaPdV6hnok%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;527&quot; height=&quot;294&quot; data-origin-width=&quot;527&quot; data-origin-height=&quot;294&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;위 예시의 작업 순서&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 주목할 부분은, 브라우저는&amp;nbsp;&lt;b&gt;매크로태스크 하나를&amp;nbsp;&lt;/b&gt;&lt;b&gt;처리할 때마다 마이크로태스크 전부를 다 처리하고 렌더링을 수행한다&lt;/b&gt;는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 마이크로태스크가 모두 처리되기 전까지는 UI 렌더링이나 네트워크 요청은 절대 일어나지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 코드를 통해 정말 위의 순서대로 실행되는지 확인해 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1647589570370&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
    console.log('시작');

    setTimeout(()=&amp;gt; console.log('타이머')); // (A)

    Promise.resolve()
    .then(()=&amp;gt; console.log('프로미스 1')) // (B)
    .then(()=&amp;gt; console.log('프로미스 2')); // (C)

    console.log('끝'); // (D)
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;233&quot; data-origin-height=&quot;105&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dt7m7e/btrwkw8E7Jn/2tS6Mk7MkVMzINPFuK0Vi1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dt7m7e/btrwkw8E7Jn/2tS6Mk7MkVMzINPFuK0Vi1/img.png&quot; data-alt=&quot;코드 실행 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dt7m7e/btrwkw8E7Jn/2tS6Mk7MkVMzINPFuK0Vi1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdt7m7e%2Fbtrwkw8E7Jn%2F2tS6Mk7MkVMzINPFuK0Vi1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;233&quot; height=&quot;105&quot; data-origin-width=&quot;233&quot; data-origin-height=&quot;105&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;코드 실행 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;console.log 는 일반적인 동기 코드이므로 바로 호출스택에서 실행되므로 '시작' 이 가장 먼저 출력된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(참고로 console.log 도 Web API 인데, Web API 라고 해서 모두 태스크큐로 가는것은 아니다. 비동기적으로 실행되는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Web API 들만 큐로 이동)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(A) 에서 setTimeout을 만나면 자바스크립트 엔진은 호출스택에서 setTimeout을 바로 실행하지 않고 Web API 로 보낸다. Web API는 setTimeout의 지연시간 만큼 지난 후 (예제에선 0ms 이므로 0ms 만큼 대기)&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매크로태스크 큐에 콜백을 넣어준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음에 (B)에서 이행된 상태의 Promise then 을 만나게 되고, 이 then은 마이크로태스크 큐에 들어가게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 (D) 에서 console.log 를 만나 '끝' 을 출력한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 상태를 체크해보면 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호출스택&amp;nbsp; --&amp;gt;&amp;nbsp; x&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매크로태스크 큐&amp;nbsp; --&amp;gt;&amp;nbsp; setTimeout callback&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마이크로태스크 큐&amp;nbsp; --&amp;gt;&amp;nbsp; (첫번째) Promise then&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;콘솔&amp;nbsp; --&amp;gt;&amp;nbsp; '시작' - '끝'&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호출스택이 비어있으므로, 이벤트 루프는 매크로태스크 큐에서 then 을 꺼내 호출스택에 전달한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호출스택에서 then 이 수행되고 나면 두 번째 then 이 매크로태스크 큐에 들어가게된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이벤트 루프는 두 번째 then 을 호출스택으로 전달하고 두 번째 then 까지 모두 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호출스택&amp;nbsp; --&amp;gt;&amp;nbsp; x&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매크로태스크 큐&amp;nbsp; --&amp;gt;&amp;nbsp; setTimeout callback&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마이크로태스크 큐&amp;nbsp; --&amp;gt;&amp;nbsp; x&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;콘솔&amp;nbsp; --&amp;gt;&amp;nbsp; '시작' - '끝' - '프로미스1' - '프로미스2'&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 마이크로태스크가 모두 처리되었으니 매크로태스크의 setTimeout 을 꺼내 처리하면서&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 처리가 완료된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드의 예시는 아래 링크를 참고하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 링크에는 코드를 한 단계씩 실행해볼 수 있는 데모도 있어서 태스크 처리 과정을 이해하는데 많은 도움이 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1647591313325&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Tasks, microtasks, queues and schedules&quot; data-og-description=&quot; &quot; data-og-host=&quot;jakearchibald.com&quot; data-og-source-url=&quot;https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/&quot; data-og-url=&quot;https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bnQ5Xc/hyNKhSH6Ec/Kk7bSB8EMK5EQpOcKb1N00/img.png?width=500&amp;amp;height=500&amp;amp;face=0_0_500_500,https://scrap.kakaocdn.net/dn/caYJq6/hyNKic0M3X/rp74M1uli2BInMRPG8Knnk/img.png?width=500&amp;amp;height=500&amp;amp;face=0_0_500_500,https://scrap.kakaocdn.net/dn/dzGWfx/hyNI1qpxXx/cWS2v5wCX2gJCwGfwgogMK/img.jpg?width=800&amp;amp;height=858&amp;amp;face=162_139_560_537&quot;&gt;&lt;a href=&quot;https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bnQ5Xc/hyNKhSH6Ec/Kk7bSB8EMK5EQpOcKb1N00/img.png?width=500&amp;amp;height=500&amp;amp;face=0_0_500_500,https://scrap.kakaocdn.net/dn/caYJq6/hyNKic0M3X/rp74M1uli2BInMRPG8Knnk/img.png?width=500&amp;amp;height=500&amp;amp;face=0_0_500_500,https://scrap.kakaocdn.net/dn/dzGWfx/hyNI1qpxXx/cWS2v5wCX2gJCwGfwgogMK/img.jpg?width=800&amp;amp;height=858&amp;amp;face=162_139_560_537');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Tasks, microtasks, queues and schedules&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;jakearchibald.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;렌더링 발생 시점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 호출스택과 매크로, 마이크로 태스크가 이벤트 루프에 의해 어떤 순서로 처리되는지 알아보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 작업들 중에 DOM을 조작하는 코드가 수행되었다면, 이 수정 사항을 렌더링해서 화면에 보여주는건 어느 시점에 발생할까?&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저는 호출스택에 작업이 모두 수행되고 비게 되면 렌더링을 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 앞선 설명에서도 언급했듯이 큐에 태스크들이 대기중 이라면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 매크로태스크가 처리되고 마이크로태스크를 모두 처리 후 &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;렌더링을 수행한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;그럼 진짜 이런식으로 렌더링이 발생하는지 코드로 확인해보자.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1647613967864&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;style&amp;gt;
    .animation {
        width: 30px;
        height: 30px;
        margin: 25px;
        border: 5px solid;
        border-color: yellow transparent yellow transparent;
        border-radius: 50%;
        animation: spinner 1s linear infinite;
    }

    @keyframes spinner {
        from {transform: rotate(0);}
        to {transform: rotate(360deg);}
    }
&amp;lt;/style&amp;gt;

&amp;lt;div&amp;gt;
    &amp;lt;div class=&quot;animation&quot;&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;div&amp;gt;
        일반코드 : &amp;lt;span id=&quot;normal&quot; class=&quot;test&quot;&amp;gt;0&amp;lt;/span&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;div&amp;gt;
        매크로 : &amp;lt;span id=&quot;macro&quot; class=&quot;test&quot;&amp;gt;0&amp;lt;/span&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;div&amp;gt;
        마이크로 : &amp;lt;span id=&quot;micro&quot; class=&quot;test&quot;&amp;gt;0&amp;lt;/span&amp;gt;
    &amp;lt;/div&amp;gt;

    &amp;lt;div class=&quot;btn-box&quot;&amp;gt;
        &amp;lt;button onclick=&quot;reset()&quot;&amp;gt;reset&amp;lt;/button&amp;gt;
        &amp;lt;button onclick=&quot;count()&quot;&amp;gt;count&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;

&amp;lt;script defer&amp;gt;
    let normal = document.querySelector('#normal');
    let macro = document.querySelector('#macro');
    let micro = document.querySelector('#micro');

    function count(){
    	// 매크로태스크
        setTimeout(()=&amp;gt;{
            longTask(macro, 3000000);
        });

        // 마이크로태스크
        Promise.resolve().then(()=&amp;gt;{
            longTask(micro, 2000000);
        });
        
        // 일반코드
        longTask(normal, 1000000);
    }

    function longTask(elem, num){
        for(let i=1; i&amp;lt;=num; i++){
            elem.textContent = i;
        }
    }

    function reset(){
        normal.textContent = 0;
        macro.textContent = 0;
        micro.textContent = 0;
    }
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;290&quot; data-origin-height=&quot;213&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dG6l1W/btrwnac7m3D/UOE0DyYz2EfRKhMaIcM7v0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dG6l1W/btrwnac7m3D/UOE0DyYz2EfRKhMaIcM7v0/img.png&quot; data-alt=&quot;위 코드를 실행한 모습&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dG6l1W/btrwnac7m3D/UOE0DyYz2EfRKhMaIcM7v0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdG6l1W%2Fbtrwnac7m3D%2FUOE0DyYz2EfRKhMaIcM7v0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;290&quot; height=&quot;213&quot; data-origin-width=&quot;290&quot; data-origin-height=&quot;213&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;위 코드를 실행한 모습&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 코드를 간단히 설명하자면,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;animation 클래스는 360도 무한 회전하는 애니메이션 css다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 애니메이션을 준 이유는 과연 css 애니메이션 효과도 호출스택이 바쁠때 렌더링되지 않고 멈추게될까..? 하는 생각에 그냥 한 번 넣어봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 화면엔 reset 과 count 버튼 두 개가 존재하는데 reset 을 클릭하면 모든 숫자가 0으로 초기화 되고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;count 버튼을 클릭하면 동기 코드 / 매크로태스크(setTimeout) / 마이크로태스크(Promise)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경우로 나눠서 각 span 의 textContent 를 수정하는 DOM 조작을 발생시킬 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;longTask 함수는 조작할 엘리먼트와 조작 횟수를 인수로 받는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(참고로 for문 1000000번 실행은 약 1초 정도 소요됨)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 케이스별로 렌더링이 언제 발생하는지 알아보자.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;i&gt;케이스1 - 일반코드와 매크로태스크&lt;/i&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반코드와 매크로태스크인 경우만 먼저 실행해보겠다. (Promise 부분은 주석 처리)&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;render1_demo.gif&quot; data-origin-width=&quot;316&quot; data-origin-height=&quot;225&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcoJVZ/btrwlQ7OjyL/ZFKclokDnKhkORBhnnXPF0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcoJVZ/btrwlQ7OjyL/ZFKclokDnKhkORBhnnXPF0/img.gif&quot; data-alt=&quot;일반 코드와 매크로태스크의 렌더링 시점&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcoJVZ/btrwlQ7OjyL/ZFKclokDnKhkORBhnnXPF0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bcoJVZ/btrwlQ7OjyL/ZFKclokDnKhkORBhnnXPF0/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;316&quot; height=&quot;225&quot; data-filename=&quot;render1_demo.gif&quot; data-origin-width=&quot;316&quot; data-origin-height=&quot;225&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;일반 코드와 매크로태스크의 렌더링 시점&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;count 버튼을 클릭하면 일반코드의 숫자가 먼저 바뀌고 약 3초 정도 후에 매크로의 숫자가 바뀐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 매크로태스크는 지연시간이 0 인 setTimeout 이므로 매크로태스크 큐에 들어갈 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 일반코드 longTask 가 호출 스택에 들어가고 1000000번의 반복 작업을 수행할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 for 문의 i 가 변하면서 일반코드 span 의 숫자도 똑같이 값이 더해져가는 모습은 볼 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게다가 count 를 클릭한 순간부터 마우스를 다시 버튼에 올려놓아도 버튼의 색이 변하지 않고 어떤 클릭이나&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크롤도 먹지 않게된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이유는 for문의 1000000번 작업이 호출스택 에서 계속 실행중이라 렌더링을 실행하지 않기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1000000번 작업이 다끝나고 스택이 비워졌을때 비로소 렌더링이 일어나 span의 숫자가 1000000 으로 바뀌는 모습을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 호출스택이 비워졌으니 대기중이던 setTimeout 콜백이 실행되고, 이번엔 3&lt;span&gt;000000번 작업을 수행한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;마찬가지로 호출스택으로 들어간 setTimeout 의 콜백 작업도 3000000번이 모두 완료되고 스택이 비워진 후&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;그제서야 화면에 3000000 이라는 숫자가 렌더링 되는걸 볼 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;(아, 그리고 css 애니메이션 효과는 렌더링 시점과 상관없이 계속 움직이는 걸로 보아 &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;렌더링은 DOM 속성값을 수정했을때만 발생하는걸로)&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;&lt;span&gt;&lt;span&gt;케이스2 - 일반코드와 마이크로태스크&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번엔 setTimeout 부분을 주석하고 일반코드와 마이크로태스크의 렌더링 시점을 보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;render2_demo.gif&quot; data-origin-width=&quot;316&quot; data-origin-height=&quot;225&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Sid5c/btrwmrTZ53I/GntPE9FldkEA0IuCaBAkf1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Sid5c/btrwmrTZ53I/GntPE9FldkEA0IuCaBAkf1/img.gif&quot; data-alt=&quot;일반 코드와 마이크로태스크의 렌더링 시점&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Sid5c/btrwmrTZ53I/GntPE9FldkEA0IuCaBAkf1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/Sid5c/btrwmrTZ53I/GntPE9FldkEA0IuCaBAkf1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;316&quot; height=&quot;225&quot; data-filename=&quot;render2_demo.gif&quot; data-origin-width=&quot;316&quot; data-origin-height=&quot;225&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;일반 코드와 마이크로태스크의 렌더링 시점&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오, 케이스1 과는 조금 다른 결과다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;케이스1 에서는 일반코드가 모두 수행되고 매크로태스크를 처리하기 전에 렌더링이 한 번 발생했는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번엔 일반코드와 마이크로태스크의 렌더링이 같이 일어났다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 결과로 알 수 있는것은, 케이스1 에서는 호출스택이 비워진 후 대기중인 마이크로태스크가 없었기 때문에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 렌더링을 수행해서 렌더링이 일반코드 --&amp;gt; 매크로 순으로 발생했지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;케이스2 에서는 일반코드가 끝나고 마이크로태스크가 존재했기때문에 먼저 마이크로태스크를 처리하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;렌더링을 수행해서 일반코드 와 마이크로의 숫자가 동시에 바뀌는 것이다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;b&gt;케이스3 - 일반코드와 매크로, 마이크로 태스크&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막은 3가지를 한 번에 실행해서 렌더링이 언제 되는지 보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;render3_demo.gif&quot; data-origin-width=&quot;316&quot; data-origin-height=&quot;225&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oXkNf/btrwoo9Qvbi/AEDikZeZb3QC7us8a2Xnvk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oXkNf/btrwoo9Qvbi/AEDikZeZb3QC7us8a2Xnvk/img.gif&quot; data-alt=&quot;일반 코드와 매크로, 마이크로 태스크의 렌더링 시점&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oXkNf/btrwoo9Qvbi/AEDikZeZb3QC7us8a2Xnvk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/oXkNf/btrwoo9Qvbi/AEDikZeZb3QC7us8a2Xnvk/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;316&quot; height=&quot;225&quot; data-filename=&quot;render3_demo.gif&quot; data-origin-width=&quot;316&quot; data-origin-height=&quot;225&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;일반 코드와 매크로, 마이크로 태스크의 렌더링 시점&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 두 케이스들을 보고 대충 결과가 예상됐을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업의 우선순위는 일반코드 --&amp;gt; 마이크로태스크 --&amp;gt; 매크로태스크 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 케이스2 에서 봤듯이 일반코드와 마이크로태스크의 렌더링이 먼저 일어나고 마지막에 매크로태스크의 렌더링이 발생하고 있다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;무거운 작업 스케줄링하기 - setTimeout&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 자바스크립트 엔진이 이벤트 루프와 함께 어떻게 비동기 작업들을 처리하는지, 또 렌더링은 언제 발생하는지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;알게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 앞선 렌더링 시점 예시에서 for문이 수행되고 있는 동안은 렌더링이 발생하지 않아서 숫자가 실시간으로 증가하는 모습도 볼 수 없었고, 클릭이나 스크롤같은 DOM 이벤트들도 큐에서 호출스택이 비워지기만 계속 기다리는 상태여서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저가 아무 반응이 없는 상태가 되버리고 말았다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제를 해결할 방법은 없는것일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제의 원인은 바로 &lt;b&gt;오래걸리는 무거운 작업때문에 호출스택이 비워지지 않기 때문&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마치 화장실칸 안에 있는 사람이 배탈이나서 오랫동안 나오지 못해 화장실 줄이 점점 길어지고 있는 상황인 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 이 문제를 &lt;b&gt;setTimeout&lt;/b&gt;으로 해결할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 사용했던 카운트 예시의 longTask 함수를 setTimeout 으로 스케줄링 하도록 리팩토링 해보자.&lt;/p&gt;
&lt;pre id=&quot;code_1647619391493&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 기존 코드
function longTask(elem){
    for(let i=1; i&amp;lt;=1000000; i++){
        elem.textContent = i;
    }
}

// 스케줄링한 코드
let i = 0;
function longTask(elem){
    do {
        i++;
        elem.textContent = i;
    }while(i % 10000 != 0);

    if(i &amp;lt; 1000000){
        setTimeout(()=&amp;gt; longTask(elem));
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yn6lm/btrwonwlFOo/nOhuACst7K1pc5vduqN9o0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yn6lm/btrwonwlFOo/nOhuACst7K1pc5vduqN9o0/img.gif&quot; data-is-animation=&quot;true&quot; data-origin-width=&quot;304&quot; data-origin-height=&quot;175&quot; data-filename=&quot;scd2_demo.gif&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yn6lm/btrwonwlFOo/nOhuACst7K1pc5vduqN9o0/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fyn6lm%2FbtrwonwlFOo%2FnOhuACst7K1pc5vduqN9o0%2Fimg.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;304&quot; height=&quot;175&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ba7wh5/btrwkpv4GyH/1aaJegrt8R7PpPvQdYDvk0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ba7wh5/btrwkpv4GyH/1aaJegrt8R7PpPvQdYDvk0/img.gif&quot; data-is-animation=&quot;true&quot; data-origin-width=&quot;304&quot; data-origin-height=&quot;175&quot; data-filename=&quot;scd_demo.gif&quot; data-widthpercent=&quot;50&quot; style=&quot;width: 49.4186%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ba7wh5/btrwkpv4GyH/1aaJegrt8R7PpPvQdYDvk0/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fba7wh5%2Fbtrwkpv4GyH%2F1aaJegrt8R7PpPvQdYDvk0%2Fimg.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;304&quot; height=&quot;175&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;좌 : 기존코드&amp;nbsp; /&amp;nbsp; 우 :&amp;nbsp; setTimeout으로 작업 스케줄링 해준 코드&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자, 어떤가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왼쪽의 기존 코드는 작업을 한번에 처리하고 마지막에 한 번 렌더링이 일어나기 때문에 i 가 변하는 모습을 볼 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 리팩토링된 코드는 10000개씩 일을 나눠서 setTimeout으로 작업을 스케줄링 해주고있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 되면 setTimeout 한 세트가 끝나고 호출스택이 비워졌을때 다음 세트를 처리하기 전에 렌더링이 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 10000개 단위로 중간중간 렌더링이 일어나서 i 가 변하는 모습을 볼 수 있는것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게다가, 한 세트가 실행되는 도중에 마우스나 키보드 같은 이벤트가 큐에 쌓여도 그 이벤트들이 다음 setTimeout 보다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 태스크큐에 쌓였으므로, 다음 세트가 실행되기 전에 모든 이벤트들이 처리될 수 있게 되서&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저가 먹통이되는 현상도 일어나지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 모든게 의도한대로 잘 해결된거 같은데, 한가지 주목할 점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 이벤트가 막히지 않고, DOM이 변하는 모습도 실시간으로 볼 수 있게됐지만 작업이 완료되는 총 시간이&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개선전 코드보다 더 오래 걸리고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이유는 &lt;b&gt;setTimeout이 연달아 많이 호출될 경우 최소 4ms만큼 대기 하도록 브라우저가 강제하기 때문&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 지연시간이 0 초라도 이는 최소 지연시간을 의미하는거지 실제론 0초보다 더 많은 시간이 지난후&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;setTimeout 의 콜백이 실행될 수 있다는 점을 꼭 기억해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;잘못된 내용은 댓글로 알려주세요 :)&lt;/p&gt;</description>
      <category>Javascript</category>
      <category>마이크로태스크</category>
      <category>매클로태스크</category>
      <category>이벤트 루프</category>
      <author>kwangsunny</author>
      <guid isPermaLink="true">https://kwangsunny.tistory.com/39</guid>
      <comments>https://kwangsunny.tistory.com/39#entry39comment</comments>
      <pubDate>Sat, 19 Mar 2022 02:07:01 +0900</pubDate>
    </item>
    <item>
      <title>자바스크립트 - debouncing과 throttling 그리고 AbortController</title>
      <link>https://kwangsunny.tistory.com/38</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 채용 공고에 제출한 프론트엔드 과제의 코드리뷰 중 이런 피드백을 받아본적이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;&lt;i&gt;debounce와 throttle 처리가 없는게 아쉽습니다..&lt;/i&gt; &quot; (나 : 디바운스 뭐시기 그게 뭔데  )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 디바운싱과 스로틀링은 용어만 생소할 뿐이지 프론트엔드 개발자라면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 번 쯤은 자기도 모르게 고민해봤거나, 직접 구현했던 적이 있을지도 모르는 프로그래밍 기법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;함수 실행 횟수 제한&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드 개발을 하다보면 이벤트 처리는 흔한 일이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;페이지 내에 스크롤 위치에 따라 모양이 변하는 HTML 요소가 있다고 가정해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자는 먼저 scroll 이벤트를 등록할 것이고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;scroll 이벤트의 콜백함수에서는 스크롤의 현 위치값을 받아서 계산해주는 작업이 필요할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 이 콜백함수는 스크롤이 일어나는 순간마다 실행될것이고, 그만큼 계산 작업도 수없이 많이 발생하게 될것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 다른 예로, 엔터키 없이 검색어를 입력할 때마다 API 요청이 발생하는 검색 컴포넌트를 만들 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 사용자가 입력한 글자 수만큼 불필요하게 너무 많은 API 요청이 일어날 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 코드가 불필요하게 많이 실행되면 자바스크립트 엔진이 바빠지고 브라우저를 버벅이게 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 문제를 개선하기 위해 &lt;b&gt;여러번 호출되는 함수를 일정시간 동안 한 번만 실행하도록 만들어주는 조건을 걸어주는 것&lt;/b&gt;을 바로 디바운싱(debouncing) 과 스로틀링(throttling) 이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;디바운싱 -&lt;/b&gt; 일정 시간내에 연이어 호출되는 함수들을 하나로 취급해 가장 마지막것만 실행시키는 것.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;스로틀링 -&lt;/b&gt; 함수를 실행한 순간부터 일정 시간동안 다시 호출되지 않도록 하는 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뭔가 설명이 비슷해 보이는데, 이 둘의 차이는 일정 시간동안 함수를 최대 한 번은 실행하냐 안하냐 이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 아래 예제 코드를 살펴보자.&lt;/p&gt;
&lt;pre id=&quot;code_1647270396430&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;input onkeyup=&quot;onKeyUp(event)&quot;&amp;gt;
&amp;lt;input onkeyup=&quot;debouncing(event)&quot;&amp;gt;
&amp;lt;input onkeyup=&quot;throttling(event)&quot;&amp;gt;

&amp;lt;script&amp;gt;
    function onKeyUp(e){ // (A)
        console.log('아무처리 X -&amp;gt; ' + e.target.value);
    }
    
    let timer1 = null;
    function debouncing(e){ // (B)
        if(timer1) clearTimeout(timer1);
        timer1 = setTimeout(()=&amp;gt;{
            console.log('디바운싱 -&amp;gt; ' + e.target.value);
        }, 500);
    }

    let timer2 = null;
    function throttling(e){ // (C)
        if(!timer2){
            timer2 = setTimeout(()=&amp;gt;{
                timer2 = null;
                console.log('스로틀링 -&amp;gt; ' + e.target.value);
            }, 500);
        }
    }
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 input 에 글자를 입력할 때마다 console.log 를 실행하는 간단한 예시이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(A) - 아무 처리도 안해준 input&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(B) - 0.5초 간격으로 디바운싱 처리된 input&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(C) - 0.5초 간격으로 스로틀링 처리된 input&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 input에 텍스트를 입력했을때 콘솔에 &lt;span style=&quot;color: #ef5369;&quot;&gt;로그가 찍히는 빈도를 통해&lt;/span&gt; 디바운싱과 스로틀링이 어떻게 작용하는지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이해할 수 있을 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;none_demo.gif&quot; data-origin-width=&quot;502&quot; data-origin-height=&quot;306&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pAmI7/btrvSz6TXD4/Tgt1gVPlTnXZQ8wzLtnQZ0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pAmI7/btrvSz6TXD4/Tgt1gVPlTnXZQ8wzLtnQZ0/img.gif&quot; data-alt=&quot;아무 처리 안했을때&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pAmI7/btrvSz6TXD4/Tgt1gVPlTnXZQ8wzLtnQZ0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/pAmI7/btrvSz6TXD4/Tgt1gVPlTnXZQ8wzLtnQZ0/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;417&quot; height=&quot;254&quot; data-filename=&quot;none_demo.gif&quot; data-origin-width=&quot;502&quot; data-origin-height=&quot;306&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;아무 처리 안했을때&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/enYse2/btrvX72Y7FV/kb8kdBc5z1FQgEfoRiCoKk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/enYse2/btrvX72Y7FV/kb8kdBc5z1FQgEfoRiCoKk/img.gif&quot; data-is-animation=&quot;true&quot; data-origin-width=&quot;502&quot; data-origin-height=&quot;306&quot; data-filename=&quot;debounce_demo.gif&quot; data-widthpercent=&quot;50&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/enYse2/btrvX72Y7FV/kb8kdBc5z1FQgEfoRiCoKk/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FenYse2%2FbtrvX72Y7FV%2Fkb8kdBc5z1FQgEfoRiCoKk%2Fimg.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;502&quot; height=&quot;306&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcSfLx/btrvZIBIEIv/4aYv0DxbeRKKzhBgC7ISX1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcSfLx/btrvZIBIEIv/4aYv0DxbeRKKzhBgC7ISX1/img.gif&quot; data-is-animation=&quot;true&quot; data-origin-width=&quot;502&quot; data-origin-height=&quot;306&quot; data-filename=&quot;throttle_demo.gif&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcSfLx/btrvZIBIEIv/4aYv0DxbeRKKzhBgC7ISX1/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcSfLx%2FbtrvZIBIEIv%2F4aYv0DxbeRKKzhBgC7ISX1%2Fimg.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;502&quot; height=&quot;306&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;좌 : 디바운싱 처리&amp;nbsp; /&amp;nbsp; 우 : 스로틀링 처리&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 아무처리도 하지 않은 예제는 input에 글자를 입력하는 족족 콘솔에 로그를 남긴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 console.log 가 아니라 API 호출을 하는 상황이었다면, 무수히 많은 쓸데없는 요청이 발생했을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 디바운싱 처리를 해준 input은 0.5초 안에 또 함수가 호출되면 이전 스케줄을 취소하고(clearTimeout) 새로 타이머를 설정한다. 그래서 아무리 빠르게 글자를 입력하더라도 0.5초 동안 최대 한번 console.log를 실행하거나 아예 실행이 미뤄지는(0.5초 내에 또 함수가 호출되어 타이머를 새로 맞추는 상황) 상황이 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 디바운싱은 검색창에 입력이 다 끝났을때 API를 호출하는것과 같은 상황에 활용하면 좋다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스로틀링의 결과는 디바운싱과 비슷해 보이지만, 실행 빈도는 디바운싱보다 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디바운싱은 연속으로 빠르게 함수가 실행될 경우 이전 스케줄을 취소하기 때문에 0.5초가 훨씬 지나서 실행될 수도 있지만, 스로틀링은 이전 스케줄을 취소하지 않고 단순히 실행을 무시하기때문에 0.5초 내에 반드시 한 번은 함수를 실행하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크롤을 위나 아래로 한 번만 슥~ 움직여도 수많은 스크롤 이벤트가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 스로틀링을 활용하면 스크롤 이벤트를 일정 시간 간격마다 한 번씩만 실행할 수 있으므로 스로틀링은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크롤 이벤트 처리에 가장 많이 활용된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;비동기 작업 취소하기 - AbortController&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 디바운싱과 스로틀링에 대해 살펴보았고, 한 걸음 더 나아간 케이스를 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디바운싱을 적용하여 일정 시간내에 또 함수가 호출되면 이전 스케줄을 취소하고 타이머를 새로 설정한다고 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 이전 스케줄에 fetch 를 통해 API를 호출하는 코드가 있다면?&amp;nbsp; fetch 요청도 취소해주는게 당연할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이미 호출한 fetch는 어떻게 취소시킬 수 있을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fetch를 실행시키면 프로미스를 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 프로미스는 응답이 오기 전까지 pending(대기) 상태이고 이 pending 상태를 취소하는 기능은 프로미스에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따로 존재하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 사용자 행위에 따라&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;fetch 같은 비동기 작업을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;취소해야만 하는 경우는 얼마든지 있을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 경우에 &lt;b&gt;AbortController&lt;/b&gt;가 그 해답이다.&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;AbortController는&lt;span&gt;&amp;nbsp;생성자 함수로써 new 를 통해 인스턴스를 생성한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1647357093729&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let controller = new AbortController();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 만들어진 controller 객체는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;abort 메서드와 signal 이라는 프로퍼티를 가지고 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;421&quot; data-origin-height=&quot;238&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbY5Ea/btrv6YpY71G/Dq7mK6tnuXNkC6qa63RKY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbY5Ea/btrv6YpY71G/Dq7mK6tnuXNkC6qa63RKY0/img.png&quot; data-alt=&quot;controller 내부 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbY5Ea/btrv6YpY71G/Dq7mK6tnuXNkC6qa63RKY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcbY5Ea%2Fbtrv6YpY71G%2FDq7mK6tnuXNkC6qa63RKY0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;421&quot; height=&quot;238&quot; data-origin-width=&quot;421&quot; data-origin-height=&quot;238&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;controller 내부 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;abort 메서드를 호출하면 signal 객체 내부 상태가 바뀌게 되고, 이를 이용해 비동기 작업을 취소할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용 방법은 매우 간단하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 요청을 취소하고 싶은 순간에 controller.abort 메서드를 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. controller.signal 은 abort 이벤트를 수신한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. controller.signal.aborted 가 true 로 바뀐다. (초기값은 false)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 예제를 통해 확인해보자.&lt;/p&gt;
&lt;pre id=&quot;code_1647358205756&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let controller = new AbortController();
let signal = controller.signal;

// abort 이벤트 등록
signal.addEventListener('abort', ()=&amp;gt; console.log('abort 이벤트 발생!')) // (A)

// aborted 초기값 == false
console.log(signal.aborted); 

// 취소 요청
controller.abort(); // (B)

console.log(signal.aborted);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;201&quot; data-origin-height=&quot;66&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sQgvy/btrv3fNgZEU/LvIIltruSjlkXy6sHoTYJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sQgvy/btrv3fNgZEU/LvIIltruSjlkXy6sHoTYJK/img.png&quot; data-alt=&quot;예제 실행 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sQgvy/btrv3fNgZEU/LvIIltruSjlkXy6sHoTYJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsQgvy%2Fbtrv3fNgZEU%2FLvIIltruSjlkXy6sHoTYJK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;201&quot; height=&quot;66&quot; data-origin-width=&quot;201&quot; data-origin-height=&quot;66&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;예제 실행 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(B) 에서 abort 메서드를 실행하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 abort 이벤트가 발생하게되고, (A) 에서 signal에 abort 이벤트 핸들러를 등록해주었기 때문에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;abort 이벤트를 수신하여 console.log가 실행되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 signal 객체가 갑자기 어떻게 addEventListener를 쓰는거지.? 라고 생각이 들수있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;501&quot; data-origin-height=&quot;360&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kIwsE/btrv8ESsigQ/Qln6Q8lrePzRQs4HXjfrZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kIwsE/btrv8ESsigQ/Qln6Q8lrePzRQs4HXjfrZ0/img.png&quot; data-alt=&quot;signal 객체 해부&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kIwsE/btrv8ESsigQ/Qln6Q8lrePzRQs4HXjfrZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkIwsE%2Fbtrv8ESsigQ%2FQln6Q8lrePzRQs4HXjfrZ0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;501&quot; height=&quot;360&quot; data-origin-width=&quot;501&quot; data-origin-height=&quot;360&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;signal 객체 해부&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;짠! signal 객체를 만든 생성자를 추적해보면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Object --&amp;gt; EventTarget --&amp;gt; AbortSignal --&amp;gt; signal 인걸 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 EventTarget은 자신의 프로토타입에 addEventListener 메서드를 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 signal 객체도 addEventListener를 상속받듯이 그대로 가져와&amp;nbsp;사용할 수 있는것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;AbortController&lt;span&gt;&amp;nbsp;적용&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 AbortController 를 어떻게 사용하는지, 내부 구조는 어떻게 생겼는지 살펴보았고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 AbortController 로 실제 비동기 작업을 취소해보자.&lt;/p&gt;
&lt;pre id=&quot;code_1647395814546&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;button onclick=&quot;request()&quot;&amp;gt;fetch 요청&amp;lt;/button&amp;gt;
&amp;lt;button onclick=&quot;job()&quot;&amp;gt;비동기 작업&amp;lt;/button&amp;gt;
&amp;lt;button onclick=&quot;cancel()&quot;&amp;gt;취소&amp;lt;/button&amp;gt;

&amp;lt;script&amp;gt;
    let controller = new AbortController();
    
    async function request(){
        try{
            let res = await fetch('뭔가-오래걸리는-요청', {
                signal: controller.signal // (A)
            });
        }catch(err){
            console.log(err); // (B)   
        }
    }
    
    function job(){
        new Promise((res, rej)=&amp;gt; {
            setTimeout(()=&amp;gt; res('done!'), 3000);
            controller.signal.addEventListener('abort', ()=&amp;gt; rej('작업 취소')); // (C)
        })
        .then(msg =&amp;gt; console.log(msg))
        .catch(err =&amp;gt; console.log(err));
    }
    
    function cancel(){
    	controller.abort(); // (D)
    }
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예시에는 버튼이 3개가 있고, 클릭 시 각각&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fetch 로 요청&amp;nbsp; /&amp;nbsp; 비동기 작업&amp;nbsp; /&amp;nbsp; 작업 취소 역할을 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 (A) 를 보면 fetch 옵션으로 signal이 존재하고 이 값으로 contorller.signal 객체를 전달 해주고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 fetch는 내부적으로 AbortController를 어떻게 활용해야 하는지 이미 알고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;fetch는 전달받은 signal 객체의 abort 이벤트를 계속 지켜보고 있다가 abort 이벤트가 발생하면 ( (D)를 실행하면 )&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;fetch는 프로미스를 중단하고 에러를 발생시킨다. (reject 실행)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 제어의 흐름은 catch문으로 넘어가게 되고, (B) 에서&lt;i&gt; 'DOMException: The user aborted a request.'&lt;/i&gt; 를 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 식으로 우리는 fetch 요청을 간단히 취소할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;job 함수에서는 프로미스로 커스텀한 비동기 작업을 수행중이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 executor (프로미스의 첫번째 인자로 넘겨진 함수) 에서 signal 에 abort 이벤트 핸들러를 등록해주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3초가 지나기 전에 (D)를 실행하면, executor 에서 등록했던 abort 이벤트 핸들러가 실행되고 reject를 호출해서 에러를 발생 시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 제어의 흐름은 catch 체인으로 이동하게 되고&lt;i&gt; '작업 취소'&lt;/i&gt; 가 출력될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘못된 내용이 있다면 댓글 주세요 :)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Javascript</category>
      <category>AbortController</category>
      <category>debounce</category>
      <category>fetch 취소</category>
      <category>Throttle</category>
      <category>비동기 작업</category>
      <author>kwangsunny</author>
      <guid isPermaLink="true">https://kwangsunny.tistory.com/38</guid>
      <comments>https://kwangsunny.tistory.com/38#entry38comment</comments>
      <pubDate>Wed, 16 Mar 2022 12:02:25 +0900</pubDate>
    </item>
    <item>
      <title>자바스크립트 - 실행 컨택스트(Execution Context) 와 렉시컬 환경(Lexical Environment)</title>
      <link>https://kwangsunny.tistory.com/37</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트를 잘 다루기 위해선 알아야 할 개념들이 정말 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적인 문법부터 시작해서 자료형, 함수, 프로토타입, 모듈, 비동기 처리 등등...&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트 공부는 끝이 없는것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 여러 개념들중 자바스크립트가 어떻게 동작하는지 이해하기 위해 반드시 알고 있어야 할&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 핵심적인 개념이 있는데, 바로 이번 글의 주제인 실행 컨택스트(Excution Context) 이다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실행 컨택스트란 ?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 컨택스트는 함수의 실행, 호이스팅, 렉시컬 환경, 클로저 같은 개념들을 관통하는 하나의 큰 개념이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 실행 컨택스트란 무엇인가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실행 컨택스트는 현재 실행중인 코드에 대한 세부 정보(제어 흐름의 위치, 선언된 변수와 함수, this, arguments 등) 를 담고있는 데이터 구조이다.&lt;/b&gt; (이 구조에 대해선 조금 후에 더 살펴볼것이다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 컨택스트의 종류는 아래와 같이 두 가지가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. Global Execution Context&amp;nbsp;&lt;/b&gt; -&amp;nbsp; 스크립트가 처음 실행될때 생성된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. Function Execution Context&lt;/b&gt;&amp;nbsp; -&amp;nbsp; 함수가 호출될때 생성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(eval 함수로 코드를 실행했을때도 해당 코드의 실행 컨택스트가 만들어지지만 eval 함수는 보안상 이유로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용하지 않도록 권고되고, 오래된 레거시에서만 가끔 볼 수 있는 코드이므로 제외한다.)&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 생성된 Global Execution Context 와 Function Execution Context는 &lt;b&gt;실행 컨택스트 스택 이라는 자료구조에&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;저장 되고 관리된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 스택은 호출스택(call stack) 이라고도 부르고, '스택' 이므로 후입선출 방식으로 컨택스트를 push / pop 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Global Execution Context 는 스크립트 실행 시 가장 처음 생성되므로 항상 스택 가장 아랫부분에 들어가게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후에 코드를 실행하면서 함수가 호출되면 해당 함수의 실행 컨택스트가 만들어지고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Global Execution Context 위로 push 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이제 제어의 흐름은 Global&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Execution Context 에서 함수의 실행 컨택스트로 넘어가고, &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;함수가 리턴되어 끝나게 되면 &lt;/span&gt;&lt;span&gt;해당 함수의 실행 컨택스트가 스택에서 pop 되고 메모리에서 제거된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Global Execution Context 는 앱이 종료되면 스택에서 pop 되고 메모리에서 제거된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드를 보면서 실행 컨택스트 생성부터 스택에 push, pop 되는 과정을 살펴보자.&lt;/p&gt;
&lt;pre id=&quot;code_1646719714094&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
    // 스크립트 실행 -&amp;gt; Global Execution Context 가 생성되고 스택에 push된다.
    
    function A(){
    	B(); // 함수B 의 실행 컨택스트가 생성되고 스택에 push.
    }
    
    function B(){
    	console.log('done!'); 
    }   
    
    A(); // 함수A 의 실행 컨택스트가 생성되고 스택에 push.
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;코드의 진행 순서를 그림으로 표현하면 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;캡처.PNG&quot; data-origin-width=&quot;587&quot; data-origin-height=&quot;887&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cDqKX1/btrvo9HredH/PFV4uYk8mw5hCZhAE5rXk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cDqKX1/btrvo9HredH/PFV4uYk8mw5hCZhAE5rXk0/img.png&quot; data-alt=&quot;실행 컨택스트와 호출 스택&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cDqKX1/btrvo9HredH/PFV4uYk8mw5hCZhAE5rXk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcDqKX1%2Fbtrvo9HredH%2FPFV4uYk8mw5hCZhAE5rXk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;587&quot; height=&quot;887&quot; data-filename=&quot;캡처.PNG&quot; data-origin-width=&quot;587&quot; data-origin-height=&quot;887&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실행 컨택스트와 호출 스택&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실행 컨택스트와 재귀함수&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스택은 한계가 있어서 이 한계를 초과해서 실행 컨택스트가 push 되면,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Maximum call stack&lt;/span&gt; 에러가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 에러는 &lt;span&gt;주로&lt;span&gt; &lt;/span&gt;&lt;/span&gt;재귀함수를 구현할때 종료 조건을 잘못 설정해줘서 맞딱뜨리곤 하는 녀석이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재귀함수란 호출된 함수가 자기 자신을 또 호출하면서 반복작업을 수행하는 함수를 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제는 1부터 인자로 받은 n까지 모두 더해주는 함수이다.&lt;/p&gt;
&lt;pre id=&quot;code_1646723713669&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 1 ~ n 까지 합을 구하는 함수
function sumAll(n){
    if(n == 1) return n;  // 종료 조건
    return n + sumAll(n - 1);   // 자신 호출(재귀호출)
}

sumAll(3); // 6&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 위에서 종료조건을 빼고 함수를 실행하면 아래와 같은 에러가 발생할 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;558&quot; data-origin-height=&quot;179&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Qc5AS/btrvvaSb6S8/8GdiTkGt8nHlFlBBLp6Kb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Qc5AS/btrvvaSb6S8/8GdiTkGt8nHlFlBBLp6Kb0/img.png&quot; data-alt=&quot;호출 스택 초과 에러&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Qc5AS/btrvvaSb6S8/8GdiTkGt8nHlFlBBLp6Kb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQc5AS%2FbtrvvaSb6S8%2F8GdiTkGt8nHlFlBBLp6Kb0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;558&quot; height=&quot;179&quot; data-origin-width=&quot;558&quot; data-origin-height=&quot;179&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;호출 스택 초과 에러&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이제 위 코드가 실행될때 실행 컨택스트가 어떤 식으로 쌓이는지 그림으로 살펴보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;781&quot; data-origin-height=&quot;340&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dWbpUK/btrvpaffUbM/XKpCvXxz6RaIiKglcXDAr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dWbpUK/btrvpaffUbM/XKpCvXxz6RaIiKglcXDAr1/img.png&quot; data-alt=&quot;재귀호출 시 실행 컨택스트&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dWbpUK/btrvpaffUbM/XKpCvXxz6RaIiKglcXDAr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdWbpUK%2FbtrvpaffUbM%2FXKpCvXxz6RaIiKglcXDAr1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;781&quot; height=&quot;340&quot; data-origin-width=&quot;781&quot; data-origin-height=&quot;340&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;재귀호출 시 실행 컨택스트&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제 코드에서 동일한 sumAll 함수를 계속 호출하고 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 sumAll 의 실행 컨택스트는 하나만 만들어지는게 아닐까 하는 생각을 할 수 있는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일한 함수를 여러번 호출하는것은 실행 컨택스트와 아무 상관이 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림에서 볼 수 있듯이 sumAll 함수는 호출될때 마다 독립적인 실행 컨택스트가 생성되고 스택에 push 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 실행 컨택스트는 코드가 실행되는 순간의 정보들을 담고 있고 스택에 차례대로 쌓이고 제거되고를 반복하기 때문에 재귀의 깊이가 아무리 깊어져도 자바스크립트 엔진은 현재 함수가 종료되고 다음에 수행할 부분이 어디인지 기억할 수 있는 것이다.&amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 재귀함수는 꼭 필요한 경우에만 사용해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제는 sumAll 함수를 일반적인 반복문으로 작성한 것이다.&lt;/p&gt;
&lt;pre id=&quot;code_1646728581329&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function sumAll(n){
    var sum = 0;
    for(var i=1; i&amp;lt;=n; i++){
        sum += i;
    }
    return sum;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재귀방식으로 작성하면 sumAll의 실행 컨택스트가 n 의 수만큼 만들어지고 스택에 push 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 n 만큼 메모리 공간을 차지하게 된다는 뜻인다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 위처럼 반복문으로 작성한 sumAll 함수의 실행 컨택스트는 한 번만 만들어지기 때문에 재귀호출보다 메모리 공간이 훨씬 절약된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 재귀함수는 깊이를 알 수 없는 트리형태의 자료구조를 순회할때 와 같이 반복문을 쓰기 어려운&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상황에서만 사용하는게 좋다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;렉시컬 환경 (Lexical Environment)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지는 실행 컨택스트가 뭔지, 또 코드 흐름에 따라 언제 생성되고 사라지는지에 대해 살펴보았는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잠시 다른 주제에 대해 얘기할 필요가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;글 초반에, 실행 컨택스트란 현재 실행중인 코드에 대한 세부 정보를 저장해놓은 내부 데이터 구조라 했었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 이 구조에 대해 살펴보려고 하는데, 그러기 위해선 렉시컬 환경 이라는 개념을 먼저 알고있어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 스크립트 전체&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 코드블록 {...}&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 호출된 함수&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 세 녀석들은 렉시컬 환경 이라는 이론상 객체를 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(여기서 이론상 객체 라는 말은 렉시컬 환경을 설명하기 위해 객체라는 표현을 사용한것일 뿐이므로,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 코드로 렉시컬 환경에 접근할 수는 없다.)&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이 렉시컬 환경은 두 부분으로 구성되어있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 환경 레코드(Environment Record)&lt;/b&gt; &lt;b&gt;- 현재 실행중인 코드 환경의 this값과 선언된 모든 변수와 함수가 저장되는 곳&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 외부 렉시컬 환경 (Outer Lexical Environment) - 외부 렉시컬 환경의 참조값 (외부 변수에 접근할 수 있게 된다)&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;884&quot; data-origin-height=&quot;355&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b42cem/btrvlygoRdk/xbGuDvybEIkHUHkU3XtdK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b42cem/btrvlygoRdk/xbGuDvybEIkHUHkU3XtdK1/img.png&quot; data-alt=&quot;렉시컬 환경 구성 요소&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b42cem/btrvlygoRdk/xbGuDvybEIkHUHkU3XtdK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb42cem%2FbtrvlygoRdk%2FxbGuDvybEIkHUHkU3XtdK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;723&quot; height=&quot;290&quot; data-origin-width=&quot;884&quot; data-origin-height=&quot;355&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;렉시컬 환경 구성 요소&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경 레코드에 선언된 변수가 저장되어 있다는말은 그 변수에 다른 값을 할당 했을때,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경 레코드의 변수값이 할당한 값으로 바뀌게 된다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드에는 어떤 렉시컬 환경이 존재하는지 살펴보자.&lt;/p&gt;
&lt;pre id=&quot;code_1646750115265&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
    // (A)

    let name1 = 'kwang'; // (B)
    var name2 = 'sunny';

    function test(){
        let msg = 'Hi~';
        console.log(`${msg} ${name1} ${name2}`); // (C)
    }
    
    test(); // (D)
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에는 두 개의 렉시컬 환경이 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 전체 스크립트의 렉시컬 환경 (Global Lexical Environment)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 함수test 의 렉시컬 환경&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 렉시컬 환경의 관계를 그림으로 표현하면 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;926&quot; data-origin-height=&quot;265&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bo1fKC/btrvxjVNFD5/Q7IkMO8ZHLkRqxXjf1HYyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bo1fKC/btrvxjVNFD5/Q7IkMO8ZHLkRqxXjf1HYyK/img.png&quot; data-alt=&quot;예시 코드의 렉시컬 환경&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bo1fKC/btrvxjVNFD5/Q7IkMO8ZHLkRqxXjf1HYyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbo1fKC%2FbtrvxjVNFD5%2FQ7IkMO8ZHLkRqxXjf1HYyK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;926&quot; height=&quot;265&quot; data-origin-width=&quot;926&quot; data-origin-height=&quot;265&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;예시 코드의 렉시컬 환경&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행중인 함수 내에서 어떤 변수가 나타나면 제일먼저 자신의 렉시컬 환경의 환경 레코드를 찾는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경 레코드 안에 변수가 존재하지 않는다면, 외부 렉시컬 환경의 참조값을 통해 외부의 렉시컬 환경에서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 변수를 찾게되고 이 과정을 변수를 찾을때까지 반복한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 가장 바깥쪽 렉시컬 환경 (Global Lexical Environment) 에서도 이 변수를 찾지 못하면 Reference 에러가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;전역 렉시컬 환경은 자신이 가장 바깥이기 때문에 외부 렉시컬 환경 값은 null 이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(외부 렉시컬 환경에서 변수값을 찾는다? 클로져의 냄새가 나는데, 그렇다.. 이것은 또 클로져와 관련된 개념이다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드의 실행 순서는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. (A) 에서 전역 렉시컬 환경이 만들어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. (D) 에서 test 함수가 실행되고 test 함수의 렉시컬 환경이 만들어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. (C) 부분에서 사용되는 변수들을 찾기위해 자바스크립트 엔진은 test의 렉시컬 환경의 환경 레코드를 뒤진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. msg 는 존재하지만, name1과 name2는 존재하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. Outer Lexical Environment 를 통해 외부 렉시컬 환경에 접근하여 name1, name2 를 찾는다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 (A) 에서 console.log(name1) 혹은 console.log(name2) 을 실행하면 무엇이 출력될까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. name1 출력 : reference 에러 발생!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. name2 출력 : undefined&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1과 2의 차이는 변수 선언을 let, var 둘 중 무엇으로 했냐의 차이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;렉시컬 환경이 만들어질때 모든 변수와 함수가 환경 레코드에 저장된다고 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 자바스크립트 엔진은 let name1 의 존재를 알고는 있다. 하지만, let은 (const도 동일) 변수가 선언되기 이전엔 &lt;b&gt;uninitialized&lt;/b&gt; 라는 상태를 가지고 있어서 이때 접근하면 에러가 발생하게되고 변수가 선언된 이후부터 접근이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면에 var로 선언된 변수는 렉시컬 환경에 올라가자마자 undefined로 초기화가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 name2는 에러가 나지 않고 undefined 가 출력되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 함수 선언문으로 정의된 함수는 렉시컬 환경에 생성되자마자 메모리에 올라가기 때문에 바로 사용할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(함수 표현식으로 선언된 함수는 제외)&lt;/p&gt;
&lt;pre id=&quot;code_1646756415987&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function fn1(){} // 함수 선언문
let fn2 = function(){} // 함수 표현식&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 현상은 바로 다음에 나올 실행 컨택스트의 생성, 실행 단계 부분에서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호이스팅이 발생하는 이유와 관련해서 더 살펴볼 것이다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 꼭 집고 넘어가야할 것은,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;외부 렉시컬 환경은 함수가 실행되는 시점이 아닌 선언된 시점의 외부 환경을 가리킨다는 사실이다.&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1646758497830&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let a = 'kim';

function fn1() {
  let a = 'sunny';
  fn2();
}

function fn2() {
  console.log(a); 
}

fn1(); // (가)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(가) 에서 출력되는 값은 'kim' 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왠지 fn2 가 fn1 에서 호출되었기 때문에 fn1의 a값인 'sunny' 를 참조할 것 같은데 그렇지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;외부 환경 참조는 함수가 선언된 시점의 외부 환경&lt;/b&gt;이므로 fn2 의 외부 렉시컬 환경은 전역 렉시컬 환경이 되므로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크립트의 a 값을 가져오게된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실행 컨택스트의 생성 단계&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 실행 컨택스트로 돌아와서, 실행 컨택스트는 스크립트가 처음 실행될때 생성되는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Global Execution Context 와 함수가 호출될때 생성되는 Function Execution Context 가 있다고 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이 실행 컨택스트들은 어떻게 생성되는 것일까.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 실행 컨택스트의 내부 구조를 보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;507&quot; data-origin-height=&quot;178&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eI6qRF/btrvqb5OaC7/AkB1rgB5HOplkJjZsi77eK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eI6qRF/btrvqb5OaC7/AkB1rgB5HOplkJjZsi77eK/img.png&quot; data-alt=&quot;실행 컨택스트 내부 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eI6qRF/btrvqb5OaC7/AkB1rgB5HOplkJjZsi77eK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeI6qRF%2Fbtrvqb5OaC7%2FAkB1rgB5HOplkJjZsi77eK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;507&quot; height=&quot;178&quot; data-origin-width=&quot;507&quot; data-origin-height=&quot;178&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;실행 컨택스트 내부 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림과 같이 실행 컨택스트는 두 부분으로 이루어져있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 이전에 설명했던 렉시컬 환경이 여기에 들어가게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 Lexical Environment 는 더 Variable Environment 라는 녀석이 하나 더 있는데 Lexical Environment와&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;큰 차이가 없으므로 그냥 하나의 Lexical Environment로 볼것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 컨택스트 두 단계를 거쳐 생성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 생성 단계 (Creation Phase)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;렉시컬 환경이 생성되고, this 바인딩이 이뤄진다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;렉시컬 환경이의 환경 레코드에 변수와 함수가 저장된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 선언문으로 선언된 함수는 바로 메모리에 올가게 되고, var 로 선언된 변수는 undefined 가 할당되고, let, const 로 할당된 변수는 uninitialized 상태이다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Global 실행 컨택스트일 경우엔 window 에 전역 오브젝트 가 할당되고 this엔 window 가 할당된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Function 실행 컨택스트일 경우엔 window 할당은 없는 대신 argument 객체가 초기화 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 &lt;b&gt;모든 변수가 생성 단계에서 렉시컬 환경에 초기화 되기 때문에 자바스크립트 엔진은 변수들의 존재를 모두 인지하게 되고, 이것이 호이스팅(hoisting) 이 발생하는 이유가 된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(호이스팅 - 변수나 함수가 선언 전에 접근 가능한 현상으로, 그게 마치 선언된 변수들을 모두 최상단으로 끌어올리는것(hoisting) 처럼 보인다 하여 지어진 이름)&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 실행 단계 (Execution Phase)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성 단계에서 결정된 렉시컬 환경을 가지고 있는 상태로, 코드를 한줄씩 실행해 내려간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 과정에서 변수에 값을 할당하거나 하면 렉시컬 환경의 해당 변수 값이 변경된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 두 단계를 거쳐서 생성된 실행 컨택스트들은 앞서 살펴봤던 대로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 흐름에따라 호출 스택에 의해 관리된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주제가 무겁고 어려운 내용이라 완벽하게 이해하려면 시간이 좀 더 필요할것 같다...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 자바스크립트의 여러 개념들을 서로 연관 지어 공부할 수 있는 주제라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마스터하게 된다면 실력적으로 크게 도약할 수 있을것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘못된 내용은 댓글 남겨주세요 :)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고자료&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ko.javascript.info/closure&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ko.javascript.info/closure&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1646758972326&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;변수의 유효범위와 클로저&quot; data-og-description=&quot;&quot; data-og-host=&quot;ko.javascript.info&quot; data-og-source-url=&quot;https://ko.javascript.info/closure&quot; data-og-url=&quot;https://ko.javascript.info/closure&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/F1saU/hyNEiSudjv/m0OkzIL7yPc143icEuTQd0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/euQE6f/hyNEdwXxZV/4kAwY1lkGSY1Gpa1DKvyyK/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512&quot;&gt;&lt;a href=&quot;https://ko.javascript.info/closure&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ko.javascript.info/closure&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/F1saU/hyNEiSudjv/m0OkzIL7yPc143icEuTQd0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/euQE6f/hyNEdwXxZV/4kAwY1lkGSY1Gpa1DKvyyK/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;변수의 유효범위와 클로저&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ko.javascript.info&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://betterprogramming.pub/execution-context-lexical-environment-and-closures-in-javascript-b57c979341a5&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://betterprogramming.pub/execution-context-lexical-environment-and-closures-in-javascript-b57c979341a5&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1646758978684&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Execution Context, Lexical Environment, and Closures in JavaScript&quot; data-og-description=&quot;Advanced JavaScript concepts you should know&quot; data-og-host=&quot;betterprogramming.pub&quot; data-og-source-url=&quot;https://betterprogramming.pub/execution-context-lexical-environment-and-closures-in-javascript-b57c979341a5&quot; data-og-url=&quot;https://betterprogramming.pub/execution-context-lexical-environment-and-closures-in-javascript-b57c979341a5&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ch4BI5/hyNEpYm8AS/KvGZ4Qq0t2gefKH9gXqwx0/img.jpg?width=1200&amp;amp;height=800&amp;amp;face=480_150_810_510,https://scrap.kakaocdn.net/dn/F7D5g/hyNEfnWrv4/D63zsI0TDNeK9TRzyI6Ae1/img.png?width=60&amp;amp;height=47&amp;amp;face=0_0_60_47,https://scrap.kakaocdn.net/dn/bXFljK/hyNEdqcVE4/tK9Ky8Kyhq4SskwwPTWik1/img.jpg?width=1400&amp;amp;height=933&amp;amp;face=568_194_924_583&quot;&gt;&lt;a href=&quot;https://betterprogramming.pub/execution-context-lexical-environment-and-closures-in-javascript-b57c979341a5&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://betterprogramming.pub/execution-context-lexical-environment-and-closures-in-javascript-b57c979341a5&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ch4BI5/hyNEpYm8AS/KvGZ4Qq0t2gefKH9gXqwx0/img.jpg?width=1200&amp;amp;height=800&amp;amp;face=480_150_810_510,https://scrap.kakaocdn.net/dn/F7D5g/hyNEfnWrv4/D63zsI0TDNeK9TRzyI6Ae1/img.png?width=60&amp;amp;height=47&amp;amp;face=0_0_60_47,https://scrap.kakaocdn.net/dn/bXFljK/hyNEdqcVE4/tK9Ky8Kyhq4SskwwPTWik1/img.jpg?width=1400&amp;amp;height=933&amp;amp;face=568_194_924_583');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Execution Context, Lexical Environment, and Closures in JavaScript&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Advanced JavaScript concepts you should know&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;betterprogramming.pub&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Javascript</category>
      <category>Call stack</category>
      <category>execution context</category>
      <category>hoisting</category>
      <category>Lexical environment</category>
      <category>렉시컬 환경</category>
      <category>실행 컨택스트</category>
      <category>호이스팅</category>
      <category>호출 스택</category>
      <author>kwangsunny</author>
      <guid isPermaLink="true">https://kwangsunny.tistory.com/37</guid>
      <comments>https://kwangsunny.tistory.com/37#entry37comment</comments>
      <pubDate>Wed, 9 Mar 2022 02:12:40 +0900</pubDate>
    </item>
    <item>
      <title>자바스크립트 - 배열, 이터러블, 유사배열</title>
      <link>https://kwangsunny.tistory.com/36</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트 공부를 하면서 유사배열이 어쩌고 이터러블이 어쩌고 하는 말을 종종 들었었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 실제로 개발할땐 그냥 배열만 잘 다룰줄 알아도 전혀 문제가 없었기에 굳이 찾아보거나 하진 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이번에 배열, 이터러블, 유사배열 이 세가지 녀석들에 대해 공부한 내용을 정리해 본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 배열&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열은 우리에게 너무나도 친숙한 녀석이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열은 자바스크립트 내장 함수인 Array에 의해 만들어지는 객체 타입의 자료형이다.&lt;/p&gt;
&lt;pre id=&quot;code_1645710838157&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let a = new Array(); // (1)
let b = []; // (2)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열 선언은 위와 같이 할 수 있는데, (1) 과 (2) 는 같은 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(1)을 단축해서 쓴것이 (2)이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;new 키워드를 통해 반환된 값이기 때문에 당연히 배열의 타입도 객체이다.&lt;/p&gt;
&lt;pre id=&quot;code_1645711171528&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let a = [];
typeof a === 'object' // true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;typeof 를 해보면 위와 같이 object 인걸 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종종 진짜 객체와 배열을 구분해야 할 때가 있는데, 이땐 Array 내장 메서드인 isArray 나 instanceof 를 이용하여&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입을 구분할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1645711614842&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let arr = []; 
let obj = {};

Array.isArray(arr); // true
Array.isArray(obj); // false

arr instanceof Array; // true
obj instanceof Array; // false&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 말했듯이 배열은 Array 함수에 new 키워드를 붙여서 만들어진 인스턴스이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 Array 함수의 prototype 객체에 구현되어있는 다양한 내장 메서드들을 마치 상속받듯이 그대로 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 prototype 객체 라는건 갑자기 뭐지..? 라고 생각이 들것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트에서 함수를 선언하면 그 함수는 prototype 이라는 객체를 프로퍼티로 가지게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이 protoype 객체는 new 를 통해 만들어질 수많은 인스턴스들이 공유하게 될 객체 라고 생각하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 Array 함수의 prototype 을 보면 아래와 같이 생겼다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;734&quot; data-origin-height=&quot;469&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cGY2oZ/btruahUnnjH/pT95gnKFk1yH4uHkfQNIKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cGY2oZ/btruahUnnjH/pT95gnKFk1yH4uHkfQNIKK/img.png&quot; data-alt=&quot;내장함수 Array 의 프로토타입&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cGY2oZ/btruahUnnjH/pT95gnKFk1yH4uHkfQNIKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcGY2oZ%2FbtruahUnnjH%2FpT95gnKFk1yH4uHkfQNIKK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;734&quot; height=&quot;469&quot; data-origin-width=&quot;734&quot; data-origin-height=&quot;469&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;내장함수 Array 의 프로토타입&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 배열에 점(.) 만 찍어도 우리가 따로 구현한 적도 없는 filter, map, find 같은 메서드들을 사용할 수 있는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;( 프로토타입은 나중에 따로 다시 정리해야겠다! )&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;2. 이터러블&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;지금까지 배열에 대해 간단히 정리해보았고, 이제 이터러블을 살펴보자.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;iterable, 반복 가능하다는 뜻인데, &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;반복 가능하다는건 for 문법을 사용할 수 있다는 뜻이겠쥬 ?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A. for(let i=0; i&amp;lt;len; i++)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B. for ... of&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C. for ... in&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에 나열한 세가지 for문을 간단히 설명하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A는 기본적인 형태의 반복문 이고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B는 배열에 사용하는 &lt;span&gt;반복문&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C는 객체 프로퍼티를 나열(enumarable) 할때 사용하는 &lt;span&gt;반복문&lt;/span&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 우리가 주목할 녀석은 B, for ... of 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;for...of&lt;/b&gt; 는 배열에 사용하는 반복문이라 했는데 더 정확히 말하면 &lt;b&gt;이터러블 객체에만 사용할 수 있는 반복문&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이터러블 객체란 Symbol.iterator 메서드를 가지고있는 객체&lt;/b&gt;를 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 Symbol.iterator 는 자바스크립트에 내장된 심볼값이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;심볼은 숫자나 문자열 같은 원시타입 중 하나인데 유일한 값을 만들고 싶을때 사용하는 타입이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무튼, 배열이 아닌 &lt;b&gt;일반적인 객체에도 Symbol.iterator 메서드를 추가해 주면 그 객체도&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;for...of 를 사용할 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;for...of 는 처음 호출될때 해당 객체가 Symbol.iterator를 키로 하는 메서드가 있는지를 먼저 찾는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;있다면 메서드를 실행하고 없으면 에러가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Symbol.iterator 메서드는 next() 메서드를 가지고있는 객체를 반환해야하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 반환된 객체를 이터레이터(iterator) 라고 부른다. (반드시 next 라는 이름을 사용해야 한다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이 이터레이터는 {done : boolean, value : any} 와 같은 형태의 객체를 반환해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;done 값이 true면 for...of 는 종료되고 false면 value 값이 저장된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설명이 복잡한데... 지금까지 설명을 토대로 작성한 예시 코드를 보자.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1645717008745&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 일반적인 객체 선언
let obj = {
    count: 5
}

// Symbol.iterator 메서드 구현
obj[Symbol.iterator] = function(){ // (A)
    // 이터레이터 반환
    return {
        cnt: 1,
        end: this.count,
        next: function(){ // (B)
            if(this.cnt &amp;lt;= this.end){
                return {
                    done: false,
                    value: this.cnt++
                }
            } else {
                return {
                    done: true,
                    value: null
                }
            }
        }
    }
}

// obj를 for...of로 실행
for(let val of obj){ // (C)
    console.log(val); // 출력: 1 2 3 4 5
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예시는 일반객체 obj 를 선언하고 Symbol.iterator 메서드를 구현하여 이터러블 하도록 만들어&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;for...of 반복문을 사용할 수 있게 만든 후 obj.count 값만큼 1부터 숫자를 출력하도록 해주는 코드이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 바로 obj를 for...of 로 실행하면 에러가 났을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(C) 부분에서 for...of 는 제일 먼저 obj 가 Symbol.iterator를 가지고 있는지 찾는다.&lt;br /&gt;(A) 같이 객체에 Symbol을 키로 등록할땐 대괄호 문법을 사용해야한다. (점(.) 방식은 에러 발생함)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(B) 와 같이 Symbol.iterator 메서드는 next 메서드를 가지고있는 객체를 반환해야하고 next 메서드는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;{done, value} 형태의 객체를 반환해야 하는데, done === true 가 될때 까지 for...of 는 next() 를 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 말했듯이 for...of 는 이터러블 객체에만 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 위 예시와 같은 일반객체는 Symbol.iterator 메서드를 직접 구현해줘야 for...of 를 사용할 수 있게 되는것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 배열과 문자열, 이 두놈도 for...of 를 사용 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이말은 뭐다? &lt;b&gt;배열과 문자열에도 Symbol.iterator 메서드가 구현되어 있겠구나&lt;/b&gt; 라는걸 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 어디에 구현되어 있을까? 바로 나를 만든 생성자 함수의 프로토타입에 이미 구현되어있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 [ ].__proto__ 와 &quot;&quot;.__proto__ 를 콘솔에서 실행하면 해당 생성자 함수의 프로토타입을 참조할 수 있는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Array 함수와 String 함수의 prototype 객체에 모두 Symbol.iterator 메서드가 존재하는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;( obj.__proto__ 는 new SomeFunc() 를 통해 만들어진 obj가 SomeFunc의 prototype 객체를 참조할 수 있고, 이를 프로토타입 체인이라 불린다.)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c0am5R/btruex9HwCP/xfnkBgLPRye3G13luNrDLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c0am5R/btruex9HwCP/xfnkBgLPRye3G13luNrDLK/img.png&quot; data-origin-width=&quot;416&quot; data-origin-height=&quot;33&quot; data-is-animation=&quot;false&quot; style=&quot;width: 44.1162%; margin-right: 10px;&quot; data-widthpercent=&quot;44.64&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c0am5R/btruex9HwCP/xfnkBgLPRye3G13luNrDLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc0am5R%2Fbtruex9HwCP%2FxfnkBgLPRye3G13luNrDLK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;416&quot; height=&quot;33&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/efrUMC/btrukgsyJ0c/x2FL7fiRBWLYKtAKbPtbY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/efrUMC/btrukgsyJ0c/x2FL7fiRBWLYKtAKbPtbY1/img.png&quot; data-origin-width=&quot;516&quot; data-origin-height=&quot;33&quot; data-is-animation=&quot;false&quot; style=&quot;width: 54.721%;&quot; data-widthpercent=&quot;55.36&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/efrUMC/btrukgsyJ0c/x2FL7fiRBWLYKtAKbPtbY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FefrUMC%2FbtrukgsyJ0c%2Fx2FL7fiRBWLYKtAKbPtbY1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;516&quot; height=&quot;33&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;좌 : [].__proto__ 실행&amp;nbsp; /&amp;nbsp; 우 : &quot;&quot;.__proto__ 실행&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 유사배열&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 유사배열에 대해 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름 그대로 배열은 아닌데 배열 비슷하게 생긴 객체를 유사배열이라 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 두 조건을 충족하면 유사배열 이라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 키값이 정수로 이루어져 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. legnth 프로퍼티를 가지고 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1645719046435&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let notRealArray = {
    0: '유사',
    1: '배열',
    length: 2,
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예시가 바로 유사배열이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열처럼 생겼지만 진짜 배열은 아니기에 filter, map 같은 내장 메서드를 쓸 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 실제 유사배열 객체의 몇가지 예시다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 문자열은 정수값을 인덱스로 쓰고있고 legnth 프로퍼티도 가지고있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;그래서 문자열은 이터러블 이면서 유사배열이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 함수가 호출됐을때 전달받은 모든 인자값들을 담고있는 arguments 객체도 유사배열이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. document.querySelectorAll(셀렉터) 로 받아온 노드 리스트도 정수 키값에 length 를 가지고있는 유사배열이다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 이런 유사배열 객체를 진짜 배열 다루듯 filter, find와 같은 내장 메서드를 쓰고싶은 경우가 많을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 사용 가능한 방법이 &lt;b&gt;Array.from 을 이용&lt;/b&gt;하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Array.from( arr ) 은 arr 가 유사배열 객체 or 이터러블 객체 라면 arr를 진짜 배열로 만들어 반환해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘못된 내용이 있다면 댓글 남겨 주세요 :)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Javascript</category>
      <category>array</category>
      <category>JavaScript</category>
      <category>배열</category>
      <category>유사배열</category>
      <category>이터러블</category>
      <category>자바스크립트</category>
      <author>kwangsunny</author>
      <guid isPermaLink="true">https://kwangsunny.tistory.com/36</guid>
      <comments>https://kwangsunny.tistory.com/36#entry36comment</comments>
      <pubDate>Fri, 25 Feb 2022 01:35:25 +0900</pubDate>
    </item>
    <item>
      <title>자바스크립트 onfocus 와 onblur 그리고 relatedTarget</title>
      <link>https://kwangsunny.tistory.com/35</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;HTML 요소 포커싱&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTLM 요소의 포커싱 관련 이벤트로 onfocus 와 onblur 가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 두 이벤트는 서로 짝꿍이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이름 그대로 HTML 요소가 포커싱 되었을땐 onfocus 이벤트가 발생하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 포커싱 해제 되었을땐 onblur 이벤트가 발생하게된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 '포커싱' 이란 HTML 요소가 마우스 클릭 혹은 Tab키로 선택이 가능한 것을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 HTML 요소가 다 포커싱 가능한것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 포커싱 가능한 HTML 요소로는 a, button, details, input, select, textarea 가 있고 이 요소들을&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대화형 요소 라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 6개 요소들을 제외한 HTML 요소들은 비대화형 요소 라고 부르며 (div, span, ul ... 등 같은 나머지 태그)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 비대화형 요소는 포커싱이 되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 onfocus / onblur 이벤트가 발생하지 않는다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 강제로 비대화형 요소에 포커싱을 줄 수도 있는데, 바로 tabindex 라는 속성을 사용하면 된다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;포커싱 기능 부여 - tabindex&amp;nbsp;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 브라우저 화면에서 Tab키를 누르면 순서대로 화면내 요소들이 순서대로 포커싱 되면서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;넘어가는 모습을 볼 수 있는데, 이 순서를 부여하는 속성이 바로 tabindex다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tabindex='숫자값' 형태로 사용할 수 있고, 숫자값이 작은값 -&amp;gt; 큰값 의 순서로 HTML 요소가 포커싱된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자값으로 음수를 줄 수도 있는데, (보통 -1) 음수를 주게되면 키입력으로 포커싱은 안되지만 여전히 마우스 클릭이나&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;focus() 메소드로는 포커싱이 가능하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;음수값을 주는 이유는 화면에서 보이는것과 상관없이 기능상 포커싱이 필요한 경우가 있기 때문이다.&lt;/p&gt;
&lt;pre id=&quot;code_1644064531066&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;div tabindex='1'&amp;gt;A&amp;lt;/div&amp;gt;
&amp;lt;div tabindex='3'&amp;gt;B&amp;lt;/div&amp;gt;
&amp;lt;div tabindex='2'&amp;gt;C&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예시처럼 하면 Tab키 입력 시 A -&amp;gt; C -&amp;gt; B 순으로 div 태크가 포커싱 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 tabindex 로 포커싱이 가능하게 되었다는 것은 곧 onfocus / onblur 이벤트도 발생한다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래라면 위 예시의 div 태그들은 마우스 클릭이나 Tab키로 포커싱을 줄 수 없고 당연히 onfocus / onblur 이벤트도&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;발생하지 않겠지만 tabindex 값을 줌으로 인해 이게 가능해진 것이다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 tabindex는 사용자 접근성과 관련있는 부분이므로 남용하지말고 꼭 필요한 경우에만 사용해야된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 포커싱이 뭔지 간단히 짚어보았고 이제 relatedTarget 이 무엇인지 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;relatedTarget 이란&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;onblur 이벤트가 발생했을때 인자로 받는 event 객체 안을 보면, relatedTarget 이라는 녀석이 함께 담겨져있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;757&quot; data-origin-height=&quot;88&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bAFVDj/btrsys205AU/D9KRdLtJhvlNpiH0Nr2w91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bAFVDj/btrsys205AU/D9KRdLtJhvlNpiH0Nr2w91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bAFVDj/btrsys205AU/D9KRdLtJhvlNpiH0Nr2w91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAFVDj%2Fbtrsys205AU%2FD9KRdLtJhvlNpiH0Nr2w91%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;757&quot; height=&quot;88&quot; data-origin-width=&quot;757&quot; data-origin-height=&quot;88&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MDN 사이트에는 '이벤트의 보조 대상을 식별한다' 라고 설명이 나와있다.&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1644067364217&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;input name='id' onblur='onblur()'/&amp;gt;
&amp;lt;input name='pw'/&amp;gt;

&amp;lt;script&amp;gt;
    function onblur(e){
    	console.log(e.currentTarget); // A
    	console.log(e.relatedTarget); // B
    }
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제를 보면 id, pw 를 입력하는 input이 두 개가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 id input을 클릭한 후 pw input을 클릭하면, A에는 id input이 담겨 있을 것이고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B에는 pw input이 담겨 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면에 id input이 클릭된 상태에서 pw input이 아닌 빈공간 아무곳을 클릭하게 되면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B에 null이 출력될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 포커싱된 상태인 어떤 HTML 요소가 사용자의 마우스 클릭 등에 의해 포커싱 해제될때&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그 다음으로 포커싱될 HTML 요소가 relatedTarget에 담겨지게 되는 것이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;실제 활용 예제&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 relatedTarget은 어떤 상황에 활용할 수 있을까? 아래 활용 예시를 살펴보자.&lt;/p&gt;
&lt;pre id=&quot;code_1644068335008&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, { useRef, useState } from 'react';
/** @jsxImportSource @emotion/react */
import {css} from '@emotion/react';

const TEST_LIST = [
    'apple',
    'codeing',
    'test',
    'front-end',
    'React',
    'CSS',
    'HTML',
    'Javascript',
    'Typescript',
    'Apollo'
]

function AutoComplete(){
    const [filteredList, setFilteredList] = useState([]);
    const [input, setInput] = useState('');
    const inputRef = useRef(null);

    const onChange = (e)=&amp;gt;{
        const value = e.currentTarget.value;
        filterText(value);
        setInput(value);
    }

    const filterText = (value)=&amp;gt;{
        const searchText = value ? value.toLowerCase() : null;
        const filtered = TEST_LIST.filter(str =&amp;gt; str.toLowerCase().indexOf(searchText) &amp;gt; -1);
        setFilteredList(filtered);
    }

    const onBlur = (e)=&amp;gt;{
        const next = e.relatedTarget;
        if(next instanceof HTMLLIElement){
            setInput(next.textContent);
            inputRef.current.focus();
        }else{
            setInput('');
            filterText('');
        }
    }
    
    const style = css`
        // 생략
    `;

    return(
        &amp;lt;div css={style}&amp;gt;
            &amp;lt;input 
                ref={inputRef}
                onChange={onChange}
                onBlur={onBlur}
                value={input}/&amp;gt;
            &amp;lt;ul className='text-box'&amp;gt;
                {filteredList.map((txt, i) =&amp;gt; (
                    &amp;lt;li key={i} tabIndex={-1}&amp;gt;{txt}&amp;lt;/li&amp;gt;
                ))}
            &amp;lt;/ul&amp;gt;
        &amp;lt;/div&amp;gt;
    );
}

export default AutoComplete;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예시는 필자가 React로 만들어본 입력 자동완성 컴포넌트이다. (css는 주제와 상관 없으므로 생략)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 작동 모습은 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;autoComplete.gif&quot; data-origin-width=&quot;412&quot; data-origin-height=&quot;226&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHU3eX/btrsysu9Wcl/J0zpuCT2kJMIYByXbOaJe1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHU3eX/btrsysu9Wcl/J0zpuCT2kJMIYByXbOaJe1/img.gif&quot; data-alt=&quot;예제 실행 모습&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHU3eX/btrsysu9Wcl/J0zpuCT2kJMIYByXbOaJe1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bHU3eX/btrsysu9Wcl/J0zpuCT2kJMIYByXbOaJe1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;370&quot; height=&quot;203&quot; data-filename=&quot;autoComplete.gif&quot; data-origin-width=&quot;412&quot; data-origin-height=&quot;226&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;예제 실행 모습&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 모습을 보면, 입력된 텍스트에 따라 필터링된 리스트가 하단에 보이게 되는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력 후 빈공간을 클릭하면 필터링 리스트가 사라지지만 리스트값을 클릭하면 리스트는 사라지지 않고 유지된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;onblur 함수에서 relatedTarget 값이 li 태그인지 아닌지 확인하고 li 태그라면 클릭한 필터값으로 input 값을 세팅해주고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;li 태그가 아니라면 필터링값을 빈값으로 세팅해줘서 필터링 리스트를 보이지 않게 처리해주고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 빈공간을 클릭했을때 필터링 리스트가 사라지는 이유는 이때 relatedTarget에 null 이 들어있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 li 태그는 비대화형 요소이기 때문에 tabindex 값을 줘서 포커싱이 가능하도록 만들어줬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래야 리스트를 클릭했을때 li 태그가 포커싱이 되면서 relatedTarget에 담겨지기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 relatedTarget은 HTML 요소의 포커싱에 따라 어떤 동작을 구분지어 수행해야 할때&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;활용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘못된 내용이 있다면 댓글에 남겨주세요 :)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Javascript</category>
      <category>Blur</category>
      <category>Focus</category>
      <category>JavaScript</category>
      <category>relatedTarget</category>
      <category>자바스크립트</category>
      <category>포커싱</category>
      <author>kwangsunny</author>
      <guid isPermaLink="true">https://kwangsunny.tistory.com/35</guid>
      <comments>https://kwangsunny.tistory.com/35#entry35comment</comments>
      <pubDate>Sat, 5 Feb 2022 23:11:44 +0900</pubDate>
    </item>
    <item>
      <title>CSS - 애니메이션으로 로딩마크 만들기</title>
      <link>https://kwangsunny.tistory.com/34</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;화면에 컨텐츠를 보여주기 위해선 API를 호출하고 데이터를 가져와서 해당 UI에 적절히 렌더링 해줘야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 API 요청에 대한 응답이 올때까지 &lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;사용자에게 &quot;요청중&quot; 이라는 상태를 보여주는게 좋은 사용자 경험일 것&lt;/span&gt;&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상태를 로딩마크로 보여주는데 (원이 빙글빙글 돌아간다던가 아니면 텍스트가 움직인다던가)&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 무난한 모양의 로딩마크를 css로 구현하는 법을 끄적거려본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBTTYJ/btrqBG3w2LH/ZPElDtxMMLESo5OII9iMH0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBTTYJ/btrqBG3w2LH/ZPElDtxMMLESo5OII9iMH0/img.gif&quot; data-origin-width=&quot;206&quot; data-origin-height=&quot;126&quot; data-is-animation=&quot;true&quot; data-filename=&quot;rotate0.gif&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBTTYJ/btrqBG3w2LH/ZPElDtxMMLESo5OII9iMH0/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBTTYJ%2FbtrqBG3w2LH%2FZPElDtxMMLESo5OII9iMH0%2Fimg.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;206&quot; height=&quot;126&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BaBvm/btrqAnQRiED/8aWlhTcSE85e0sIM4rCktk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BaBvm/btrqAnQRiED/8aWlhTcSE85e0sIM4rCktk/img.gif&quot; data-is-animation=&quot;true&quot; data-origin-width=&quot;206&quot; data-origin-height=&quot;126&quot; data-filename=&quot;rotate1.gif&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BaBvm/btrqAnQRiED/8aWlhTcSE85e0sIM4rCktk/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBaBvm%2FbtrqAnQRiED%2F8aWlhTcSE85e0sIM4rCktk%2Fimg.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;206&quot; height=&quot;126&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;스타일-1&amp;nbsp; /&amp;nbsp; 스타일-2&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 스타일1, 2 의 차이점은 1은 그냥 일정하게 회전만 하는 모습이고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2는 뭔가 테두리가 늘어났다 줄었다 하면서 좀더 역동적으로 회전하는 모습이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;스타일-1&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1642053016389&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/* style.scss */

.loading {
    div {
        border: 9px solid orange;
        border-radius: 50%;
        border-color: orange orange orange transparent;
        width: 80px;
        height: 80px;
        animation: spinning 1s linear infinite;
    }
}

@keyframes spinning {
    from {
        transform: rotate(0);
    }
    to {
        transform: rotate(360deg);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1642053167105&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;div class='loading'&amp;gt;
	&amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스타일-1 은 사실 너무 간단하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그저 div를 linear 하게 무한회전 하면 끝난다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나 짚고갈 점은 아래 두줄 코드인데&lt;/p&gt;
&lt;pre id=&quot;code_1642053416771&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;border-radius: 50%;
border-color: orange orange orange transparent;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;border-color 는 인자값을 1개만 받을 수도 있지만, 4개까지 받을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인자를 4개 취했을땐 [ 위 - 오른 - 아래 - 왼 ] 순서로 테두리 색이 적용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에선 orange 색으로 3면을 칠하고 왼쪽면만 투명하게 해줬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 border-radius를 둥글게 줘서 마치 도넛의 한쪽을 베어먹은 모양이 되는것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;스타일-2&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1642054103512&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.loading {
    position: relative;
    div {
        position: absolute;
        border: 9px solid orange;
        border-radius: 50%;
        border-color: orange transparent transparent transparent;
        width: 80px;
        height: 80px;
        animation: spinning 1s infinite;

        &amp;amp;:nth-child(1){
            animation-delay: -0.3s;
        }
        &amp;amp;:nth-child(2){
            animation-delay: -0.2s;
        }
        &amp;amp;:nth-child(3){
            animation-delay: -0.1s;
        }
    }
}

@keyframes spinning {
    from {
        transform: rotate(0);
    }
    to {
        transform: rotate(360deg);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1642054179779&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;div class='loading'&amp;gt;
  &amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스타일-2 는 1과 비교했을때 코드가 좀 더 추가되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 &amp;lt;div&amp;gt;가 4개가 되었고 position 값이 absolute 로 바뀌었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 되면 한입 베어문 도넛 4개가 한곳에 겹쳐진 모습이 될것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 중요한건 마치 테두리가 늘었다 줄었다 하듯이 회전하는 모습을 구현하는 것인데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 구현하기 위해서 animation-delay 를 첫번째, 두번째, 세번째 div 에 다른 값을 주어 조금씩 다른 타이밍이에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애니메이션이 실행되도록 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 animation-delay 값을 음수로 줬다. animation-delay 값이 음수라는건 무엇을 의미할까 ?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 animation-duration : 5s 인 애니메이션이 있다고 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 애니메이션이 5초동안 실행된다는 뜻인데 animation-delay : -1s 이렇게 딜레이를 -1초로 주면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애니메이션을 1초 빨리 시작하겠다는 뜻이다. 즉 UI 가 화면에 그려졌을때 0초 부터 시작하는게 아니라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1초일때 모습부터 시작하겠다는 뜻이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 &amp;lt;div&amp;gt;에 animation-delay에 각각 다른 음수값을 준것은 UI 가 화면에 렌더링 되었을때 부터&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;이미 돌아가고 있는 상태&quot; 로 보이도록 만들고 싶어서다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딜레이 시간을 만약 양수 값으로 줬다면, UI가 렌더링 됐을때 모습은 그제서야 딜레이만큼 하나씩 돌아가는 모습일 것이다. (이 부분은 직접 해보면 바로 알 수 있는 부분이다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;마지막으로 animation 에 linear 값을 뺐는데 그렇게되면 기본값인 ease 가 적용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ease 는 일정한 속도가 아닌 속도가 서서히 줄어드는 효과를 주는 값이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4개의 div 에 각각 다른 색을 부여해서 어떻게 움직이는지 아래 모습을 보면 좀더 이해가 빠를 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;rotate2.gif&quot; data-origin-width=&quot;206&quot; data-origin-height=&quot;126&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5hN9D/btrqAaj1p0G/l6OHhtqS6g62EHiTZugEEK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5hN9D/btrqAaj1p0G/l6OHhtqS6g62EHiTZugEEK/img.gif&quot; data-alt=&quot;딜레이에 따라 각각 다른 타이밍에 애니메이션이 적용되는 모습&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5hN9D/btrqAaj1p0G/l6OHhtqS6g62EHiTZugEEK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/5hN9D/btrqAaj1p0G/l6OHhtqS6g62EHiTZugEEK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;206&quot; height=&quot;126&quot; data-filename=&quot;rotate2.gif&quot; data-origin-width=&quot;206&quot; data-origin-height=&quot;126&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;딜레이에 따라 각각 다른 타이밍에 애니메이션이 적용되는 모습&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 간단한 로드마크를 css로 구현해 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>CSS</category>
      <category>CSS</category>
      <category>로드마크</category>
      <category>로딩</category>
      <category>스타일</category>
      <category>애니메이션</category>
      <author>kwangsunny</author>
      <guid isPermaLink="true">https://kwangsunny.tistory.com/34</guid>
      <comments>https://kwangsunny.tistory.com/34#entry34comment</comments>
      <pubDate>Thu, 13 Jan 2022 15:37:47 +0900</pubDate>
    </item>
    <item>
      <title>keydown 이벤트 한글 중복 입력 현상</title>
      <link>https://kwangsunny.tistory.com/33</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;React로 카카오톡과 같은 채팅 컴포넌트를 만들던 중 발견한 버그와&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결한 방법을 기록해봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;865&quot; data-origin-height=&quot;713&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d2sMWe/btroDwPm9Ea/0Sr3jXcjMLK9DWdD9M1sA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d2sMWe/btroDwPm9Ea/0Sr3jXcjMLK9DWdD9M1sA0/img.png&quot; data-alt=&quot;Chatting.tsx&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d2sMWe/btroDwPm9Ea/0Sr3jXcjMLK9DWdD9M1sA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd2sMWe%2FbtroDwPm9Ea%2F0Sr3jXcjMLK9DWdD9M1sA0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;458&quot; height=&quot;378&quot; data-origin-width=&quot;865&quot; data-origin-height=&quot;713&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Chatting.tsx&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 이미지는 내가 만들고있던 Chatting.tsx 컴포넌트다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기록된 메시지들이 보이는 부분이 ChattingList.tsx 이고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메시지를 입력하는 아랫부분이 ChattingInput.tsx 이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ChattingInput 에 텍스트를 입력하고 Enter를 누르거나 전송을 클릭하면 입력된 텍스트가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ChattingList에&amp;nbsp;추가되는 기능을 구현했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데, 영문을 입력하면 정상적으로 ChattingList에 잘 추가가 되었지만 이상하게 한글만 입력하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력된 텍스트와 그 텍스트의 마지막 부분이 잘려서 두번 이벤트가 발생하는 것이었다...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmQ5mT/btroDPnzENF/8cJDar9afbqh3e5MY1s060/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmQ5mT/btroDPnzENF/8cJDar9afbqh3e5MY1s060/img.png&quot; data-origin-width=&quot;183&quot; data-origin-height=&quot;89&quot; style=&quot;width: 53.5469%; margin-right: 10px;&quot; data-widthpercent=&quot;54.18&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmQ5mT/btroDPnzENF/8cJDar9afbqh3e5MY1s060/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmQ5mT%2FbtroDPnzENF%2F8cJDar9afbqh3e5MY1s060%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;183&quot; height=&quot;89&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdNFUs/btroDvXf1eg/lmGYOSGkzhkOW0ej1RIKfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdNFUs/btroDvXf1eg/lmGYOSGkzhkOW0ej1RIKfK/img.png&quot; data-origin-width=&quot;200&quot; data-origin-height=&quot;115&quot; style=&quot;width: 45.2903%;&quot; data-widthpercent=&quot;45.82&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdNFUs/btroDvXf1eg/lmGYOSGkzhkOW0ej1RIKfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdNFUs%2FbtroDvXf1eg%2FlmGYOSGkzhkOW0ej1RIKfK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;200&quot; height=&quot;115&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
  &lt;figcaption&gt;좌 : 영문입력&amp;nbsp; &amp;nbsp; /&amp;nbsp; &amp;nbsp; 우 : 한글입력&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 이미지와 같은 현상이 발생했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 개발자도구에서 breakpoint를 찍고 디버깅을 해보니 이때는 한글 입력도 정상 입력이 되었다..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;breakpoint 를 찍고 멈췄다가 다시 실행시키면 정상동작 하는것을 보면서 아무래도 내가 사용한 이벤트 함수가 동작하는 메커니즘에 의한 현상일 것이라는 생각이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 ChattingInput의 일부 코드이다.&lt;/p&gt;
&lt;pre id=&quot;code_1640186305205&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ChattingInput.tsx
const onKeyDown = (e: React.KeyboardEvent&amp;lt;HTMLTextAreaElement&amp;gt;)=&amp;gt;{
    if(e.code === 'Enter'){
        e.preventDefault();
        handleSendMessage();
    }
}

...

return (
    &amp;lt;div css={style}&amp;gt;
        &amp;lt;textarea value={msg} onKeyDown={onKeyDown} onChange={onChange}/&amp;gt;
        &amp;lt;button className='send-btn' disabled={disabled} onClick={handleSendMessage}&amp;gt;전송&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 keydown 이벤트로 Enter입력을 구분해주었는데, 나는 keydown 이벤트가 '단순히 키가 눌렸을때 발생하는 이벤트' 정도로만 알고있지 내부 동작은 자세히 모른다는 생각이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 구글링을 해보던중 흥미로운 글을 하나 읽게되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;the keydown event is also fired during IME composition, to improve cross-browser compatibility for Chinese, Japanese and Korean users. &lt;br /&gt;We can ignore the keydown event during IME composition, we can check the isComposing property of the event object that&amp;rsquo;s provided by the keydown event handler&amp;nbsp;&lt;br /&gt;.....&lt;br /&gt;Whenever,&amp;nbsp;we&amp;nbsp;type&amp;nbsp;in&amp;nbsp;Chinese,&amp;nbsp;Japanese&amp;nbsp;or&amp;nbsp;Korean,&amp;nbsp;the&amp;nbsp;isComposing&amp;nbsp;property&amp;nbsp;will&amp;nbsp;have&amp;nbsp;the&amp;nbsp;value&amp;nbsp;true&amp;nbsp;and&amp;nbsp;the&amp;nbsp;keyCode&amp;nbsp;property&amp;nbsp;will&amp;nbsp;have&amp;nbsp;the&amp;nbsp;value&amp;nbsp;229.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출처 : &lt;a href=&quot;https://levelup.gitconnected.com/javascript-events-handlers-keyboard-and-load-events-1b3e46a6b0c3&quot;&gt;https://levelup.gitconnected.com/javascript-events-handlers-keyboard-and-load-events-1b3e46a6b0c3&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1640187777903&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;JavaScript Events Handlers &amp;mdash; Keyboard and Load Events&quot; data-og-description=&quot;In JavaScript, events are actions that happen in an app. They&amp;rsquo;re triggered by various things like inputs being entered, forms being&amp;hellip;&quot; data-og-host=&quot;levelup.gitconnected.com&quot; data-og-source-url=&quot;https://levelup.gitconnected.com/javascript-events-handlers-keyboard-and-load-events-1b3e46a6b0c3&quot; data-og-url=&quot;https://levelup.gitconnected.com/javascript-events-handlers-keyboard-and-load-events-1b3e46a6b0c3&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/FmigK/hyMOc6mUQI/KXFzAYidpbmptIfdaGloRk/img.jpg?width=1200&amp;amp;height=900&amp;amp;face=0_0_1200_900,https://scrap.kakaocdn.net/dn/Ud64m/hyMOgnnRLq/1ZvdUQDy85ELBiwXyCWKY1/img.jpg?width=1400&amp;amp;height=1050&amp;amp;face=0_0_1400_1050,https://scrap.kakaocdn.net/dn/GrGe5/hyMOg8KwIp/A6m96Ot8xesHkaHeRTQVw0/img.jpg?width=60&amp;amp;height=40&amp;amp;face=0_0_60_40&quot;&gt;&lt;a href=&quot;https://levelup.gitconnected.com/javascript-events-handlers-keyboard-and-load-events-1b3e46a6b0c3&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://levelup.gitconnected.com/javascript-events-handlers-keyboard-and-load-events-1b3e46a6b0c3&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/FmigK/hyMOc6mUQI/KXFzAYidpbmptIfdaGloRk/img.jpg?width=1200&amp;amp;height=900&amp;amp;face=0_0_1200_900,https://scrap.kakaocdn.net/dn/Ud64m/hyMOgnnRLq/1ZvdUQDy85ELBiwXyCWKY1/img.jpg?width=1400&amp;amp;height=1050&amp;amp;face=0_0_1400_1050,https://scrap.kakaocdn.net/dn/GrGe5/hyMOg8KwIp/A6m96Ot8xesHkaHeRTQVw0/img.jpg?width=60&amp;amp;height=40&amp;amp;face=0_0_60_40');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;JavaScript Events Handlers &amp;mdash; Keyboard and Load Events&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;In JavaScript, events are actions that happen in an app. They&amp;rsquo;re triggered by various things like inputs being entered, forms being&amp;hellip;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;levelup.gitconnected.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 내용을 대충 해석하면, 한글이나 중국어같이 영어가 아닌 글자들은 한 글자가 영어보다 많은 정보를 담고있기 때문에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키입력 순간부터 입력완료 까지 시간이 걸리고, 이는 keydown 이벤트가 이미 발생되고 난 후에도 진행중일 수 있다는 것을 의미하고 그 상태를 &lt;b&gt;isCompsing&lt;/b&gt; 이란 event 객체 속성값으로 확인 할 수 있다는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러니까 ChattingInput 에서 'test' 가 아닌 '테스트' 를 입력했을때 keydown 이벤트 함수는 호출된 상태지만 실제로&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'테스트' 라는 글자는 아직 isComposing == true 상태인 것이고(= 글자가 구성되고있는 중)&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때문에 글자가 중복되서 화면에 찍히는 현상이 나타났던 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 찾은 대안으로 keydown 대신 keypress를 사용했더니 위와같은 문제는 발생하지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이게 올바른 대처법인지는 확실치 않지만 일단은 이게 최선의 해결책인것 같아 이렇게 처리해 주었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>다양한 삽질들</category>
      <category>JavaScript</category>
      <category>keyDown</category>
      <category>이벤트</category>
      <category>한글입력</category>
      <author>kwangsunny</author>
      <guid isPermaLink="true">https://kwangsunny.tistory.com/33</guid>
      <comments>https://kwangsunny.tistory.com/33#entry33comment</comments>
      <pubDate>Thu, 23 Dec 2021 01:07:54 +0900</pubDate>
    </item>
  </channel>
</rss>