<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>혼자공부끄적끄적</title>
    <link>https://doooodle932.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Fri, 7 Aug 2026 05:38:50 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>SH_Roh</managingEditor>
    <image>
      <title>혼자공부끄적끄적</title>
      <url>https://tistory1.daumcdn.net/tistory/4436194/attach/13f149f4e8214a0d8c6be8e5336d14c5</url>
      <link>https://doooodle932.tistory.com</link>
    </image>
    <item>
      <title>더 나은 Git 커밋 메시지 작성하기(커밋 옵션, 컨벤션, 템플릿)</title>
      <link>https://doooodle932.tistory.com/180</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1242&quot; data-origin-height=&quot;1460&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/y0zvi/btsilJlQRB2/kCafqMH8Td0rDzCvMKI57K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/y0zvi/btsilJlQRB2/kCafqMH8Td0rDzCvMKI57K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/y0zvi/btsilJlQRB2/kCafqMH8Td0rDzCvMKI57K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fy0zvi%2FbtsilJlQRB2%2FkCafqMH8Td0rDzCvMKI57K%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;350&quot; height=&quot;411&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1242&quot; data-origin-height=&quot;1460&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무조건 git commit -m으로만 커밋 메시지를 작성했던 지난 날.. 이걸로만은 한계를 느껴 커밋 메시지에 대해 좀 더 알아보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Commit Options&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;커밋 메시지 작성&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;-m&lt;/b&gt;: 인라인 형식으로 바로 커밋 메시지 작성&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;git commit -m &quot;커밋 메시지&quot;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;-a 또는 --all&lt;/b&gt;: 별도의 add 명령어를 사용하지 않고, &lt;u&gt;수정된 모든 파일에 대해 add, commit을 한번에 수행&lt;/u&gt;(단, 한번도 add되지 않은 파일은 add를 따로 해주어야 함)&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;git commit -a -m &quot;커밋 메시지&quot;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;-am&lt;/b&gt;: a, m 옵션을 합친 형태(파일을 스테이지에 올리고 커밋)&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;git commit -am &quot;커밋 메시지&quot;&lt;/blockquote&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;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 커밋을 push하기 전인 경우&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;--amend&lt;/b&gt; 옵션으로 &lt;b&gt;가장 최근에 만든 커밋 메시지 수정&lt;/b&gt;하기&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;git commit --amend -m &quot;수정할 커밋 메시지&quot;&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;수정하고자 하는 커밋이 &lt;b&gt;가장 최근 커밋이 아닌 경우&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;git rebase -i HEAD~[숫자]&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자는 최근까지 실행된 커밋 중 몇 개까지 살펴볼 것인지를 말한다. 예를 들어 2를 입력 시, 최근 커밋보다 이전에 커밋된 2개의 git 메시지를 수정 가능하다.&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;333&quot; data-origin-height=&quot;43&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c3kPxP/btsiliPDnCJ/yiMJApn8NqKSt7rURvuDJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c3kPxP/btsiliPDnCJ/yiMJApn8NqKSt7rURvuDJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c3kPxP/btsiliPDnCJ/yiMJApn8NqKSt7rURvuDJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc3kPxP%2FbtsiliPDnCJ%2FyiMJApn8NqKSt7rURvuDJ0%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;330&quot; height=&quot;43&quot; data-origin-width=&quot;333&quot; data-origin-height=&quot;43&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 명령어를 통해 수정화면에 들어가면 각 커밋 앞에 pick이 적혀있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수정하고자 하는 커밋 앞의 &lt;b&gt;pick을 reword로 변경&lt;/b&gt;한 뒤 저장하면 해당 커밋을 수정할 수 있는 창이 열린다. (i를 눌러 내용을 수정하고, esc를 누른 후 :wq를 입력하면 저장할 수 있다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세한 과정은 아래에 있는 gif를 참고하면 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. 커밋을 이미 push한 경우&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이 경우 다른 개발자들이 해당 변경사항을 이미 pull 받았을 수도 있기 때문에 &lt;u&gt;커밋 메시지를 수정하는 것을 권장하지 않는다&lt;/u&gt;. 하지만 그럼에도 불구하고 반드시 수정해야 하는 경우라면 위에서 설명한 방법으로 &lt;b&gt;커밋 메시지를 수정한 후 git push --force&lt;/b&gt;를 입력하면 된다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;-amend 또는 rebase -i HEAD~로 커밋 메시지 수정&lt;br /&gt;git push --force&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; 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-filename=&quot;gif1.gif&quot; data-origin-width=&quot;1388&quot; data-origin-height=&quot;492&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dhwyZq/btsijYqyHlu/PiZKck4RK5p5dKYaxOsco1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dhwyZq/btsijYqyHlu/PiZKck4RK5p5dKYaxOsco1/img.gif&quot; data-alt=&quot;이미 push한 커밋을 rebase -i로 메시지 수정 후 --force로 푸시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dhwyZq/btsijYqyHlu/PiZKck4RK5p5dKYaxOsco1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/dhwyZq/btsijYqyHlu/PiZKck4RK5p5dKYaxOsco1/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;1388&quot; height=&quot;492&quot; data-filename=&quot;gif1.gif&quot; data-origin-width=&quot;1388&quot; data-origin-height=&quot;492&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이미 push한 커밋을 rebase -i로 메시지 수정 후 --force로 푸시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Commit Message Convention&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;커밋 메시지 컨벤션이란 프로젝트 참여자들이 &lt;u&gt;일관된 형식의 커밋 메시지를 작성하기 위한 규칙&lt;/u&gt;을 말한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;커밋 컨벤션의 중요성&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;일관된 커밋 메시지의 형태를 유지하면 &lt;b&gt;가독성을 높일 수 있고&lt;/b&gt;, &lt;b&gt;소스 변경 이력을 효율적으로 추적&lt;/b&gt;할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;또한 코드 리뷰 및 버그 수정 과정에서 &lt;b&gt;불필요한 의사소통 과정을 간소화&lt;/b&gt;해 &lt;b&gt;프로젝트 관리에 들어가는 시간을 줄일 수 있다&lt;/b&gt;.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;기본 포맷&lt;/h3&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;일반적으로 아래와 같은 형태로 작성한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&amp;lt;type&amp;gt;[optional scope]: &amp;lt;description&amp;gt;&lt;br /&gt;[optional body]&lt;br /&gt;[optional footer(s)]&lt;/blockquote&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;type과 description은 필수적으로 작성하고, scope, body, footer는 필요에 따라 작성해주면 된다. (e.g. feat: 로그인 기능 추가)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;type에는 상황에 따라 다음과 같은 단어들이 들어가면 좋다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #333333; text-align: left;&quot;&gt;FEAT: 새로운 기능 추가&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #333333; text-align: left;&quot;&gt;FIX: 버그 수정&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #333333; text-align: left;&quot;&gt;DOCS: 문서 수정&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #333333; text-align: left;&quot;&gt;STYLE: 스타일 관련 기능(코드 포맷팅, 세미콜론 누락, 코드 자체의 변경이 없는 경우)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #333333; text-align: left;&quot;&gt;REFACTOR: 코드 리팩토링&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #333333; text-align: left;&quot;&gt;TEST: 테스트 코드 추가&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #fcfcfc; color: #333333; text-align: left;&quot;&gt;CHORE: 빌드 업무 수정, 패키지 매니저 수정(ex .gitignore 수정 같은 경우)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;좋은 커밋 메시지를 쓰기 위한 규칙&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 제목과 본문을 빈 행으로 분리한다.&lt;/b&gt;&lt;/h4&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;u&gt;무조건 그 사이에 빈 행이 존재해야 제대로 동작&lt;/u&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;2. 제목 행을 50자로 제한한다.&lt;/b&gt;&lt;/h4&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;(제목을 요약하는 것이 너무 어렵다면 한 번에 커밋하기에 너무 많은 변경을 포함하는 경우일 수도 있다.)&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;3. 제목 행 첫 글자는 대문자로 쓴다.&lt;/b&gt;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4. 제목 행 끝에 마침표를 넣지 않는다.&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;5. 제목 행에 명령문을 사용한다.&lt;/b&gt;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;6. 본문을 72자 단위로 개행한다.&lt;/b&gt;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;7. 어떻게 보다는 무엇과 왜를 설명한다.&lt;/b&gt;&lt;/h4&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Commit Message Template&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커밋 메시지 템플릿을 이용하면 &lt;u&gt;일관된 커밋 메시지를 작성하도록 유도&lt;/u&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;한 후, git config commit.template 명령어로 해당 템플릿 파일을 &lt;b&gt;git config에 등록&lt;/b&gt;하면 템플릿을 사용할 수 있다.&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;먼저 .gitmessage.txt라는 이름의 파일을 생성한 후, 파일에 아래 내용을 붙여넣고 저장한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;################&lt;br /&gt;# &amp;lt;타입&amp;gt; : &amp;lt;제목&amp;gt; 의 형식으로 제목을 아래 공백줄에 작성&lt;br /&gt;# 제목은 50자 이내 / 변경사항이 &quot;무엇&quot;인지 명확히 작성 / 끝에 마침표 금지&lt;br /&gt;# 예) FEAT: 로그인 기능 추가&lt;br /&gt;&lt;br /&gt;# 바로 아래 공백은 지우지 마세요 (제목과 본문의 분리를 위함)&lt;br /&gt;&lt;br /&gt;################&lt;br /&gt;# 본문(구체적인 내용)을 아랫줄에 작성&lt;br /&gt;# 여러 줄의 메시지를 작성할 땐 &quot;-&quot;로 구분 (한 줄은 72자 이내)&lt;br /&gt;&lt;br /&gt;################&lt;br /&gt;# 꼬릿말(footer)을 아랫줄에 작성 (현재 커밋과 관련된 이슈 번호 추가 등)&lt;br /&gt;# 예) Close #7&lt;br /&gt;&lt;br /&gt;################&lt;br /&gt;# FEAT: 새로운 기능 추가&lt;br /&gt;# FIX: 버그 수정&lt;br /&gt;# DOCS: 문서 수정&lt;br /&gt;# STYLE: 스타일 관련 기능(코드 포맷팅, 세미콜론 누락, 코드 자체의 변경이 없는 경우)&lt;br /&gt;# REFACTOR: 코드 리팩토링&lt;br /&gt;# TEST: 테스트 코드 추가&lt;br /&gt;# CHORE: 빌드 업무 수정, 패키지 매니저 수정(ex .gitignore 수정 같은 경우)&lt;br /&gt;################&lt;/blockquote&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;아래 명령어를 통해 템플릿 파일을 설정할 수 있다. 이렇게하면 git commit 명령을 실행할 때 지정한 템플릿 메시지를 편집기에서 매번 사용할 수 있다.(-m 옵션을 사용하지 않고 git commit만 입력하면 된다.)&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;git config --global commit.template .gitmessage.txt&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;config options&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따로 범위 옵션을 지정하지 않으면 git은 기본적으로 --local 옵션을 지정한다. 상황에 따라 맞는 옵션을 선택해 지정해주면 된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;--system&lt;/b&gt;: 해당 시스템에 있는 모든 사용자와 모든 저장소에 적용되는 설정 파일&lt;/li&gt;
&lt;li&gt;&lt;b&gt;--global&lt;/b&gt;: 해당 사용자에게만 적용되는 설정 파일(&lt;b&gt;내 로컬에서 커밋하는 모든 프로젝트&lt;/b&gt;에서 동일한 템플릿이 적용됨)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;--local&lt;/b&gt;: 현재 작업 중인 저장소의 git 디렉토리에 있는 파일을 찾는다. 이 파일은 &lt;b&gt;해당 저장소에만 적용&lt;/b&gt;된다.&lt;/li&gt;
&lt;/ul&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;b&gt;git add&lt;/b&gt; 명령어를 통해 변경사항이 있는 파일을 스테이지에 올린다. 그 후 &lt;b&gt;git commit&lt;/b&gt;을 실행한다.(&lt;u&gt;-m 옵션을 빼고&lt;/u&gt; git commit만 입력하면 설정한 템플릿이 나온다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vi 에디터가 나오면 i를 눌러 편집모드로 변경 후 내용을 작성한다. 입력이 완료되었으면 esc를 누른 후 :wq를 입력해 저장해주면 커밋이 완료된다.&lt;/p&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;git push&lt;/b&gt;를 통해 커밋을 원격 저장소에 올릴 수 있다.&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;커밋 메시지 자동화 툴&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;git 커밋 메시지를 자동으로 생성하고 관리하기 위한 다양한 자동화 도구들이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;u&gt;&lt;a href=&quot;https://commitlint.js.org/#/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;commitlint&lt;/a&gt;&lt;/u&gt;: git 커밋 메시지를 검사해 컨벤션을 따르는지 확인&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://typicode.github.io/husky/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;u&gt;Husky&lt;/u&gt;&lt;/a&gt;: git hook을 설정해 커밋 및 푸시 전에 자동화된 작업(커밋 메시지 검사, 코드 포매팅 등)을 설정할 수 있음.&lt;/li&gt;
&lt;/ul&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;h2 data-ke-size=&quot;size26&quot;&gt;References&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;a href=&quot;https://git-scm.com/docs/git-commit&quot;&gt;https://git-scm.com/docs/git-commit&lt;/a&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;a href=&quot;https://yozm.wishket.com/magazine/detail/1974/&quot;&gt;https://yozm.wishket.com/magazine/detail/1974/&lt;/a&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;a href=&quot;https://velog.io/@bky373/Git-커밋-메시지-템플릿&quot;&gt;https://velog.io/@bky373/Git-커밋-메시지-템플릿&lt;/a&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;a href=&quot;https://chanhuiseok.github.io/posts/git-4/&quot;&gt;https://chanhuiseok.github.io/posts/git-4/&lt;/a&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;a href=&quot;https://richone.tistory.com/26&quot;&gt;https://richone.tistory.com/26&lt;/a&gt;&lt;/u&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;</description>
      <category>Git</category>
      <category>commit message</category>
      <category>commit message convention</category>
      <category>git</category>
      <category>git commit option</category>
      <category>git commit template</category>
      <category>git config option</category>
      <category>깃 커밋 옵션</category>
      <category>커밋 메시지</category>
      <category>커밋 메시지 컨벤션</category>
      <category>커밋 메시지 템플릿</category>
      <author>SH_Roh</author>
      <guid isPermaLink="true">https://doooodle932.tistory.com/180</guid>
      <comments>https://doooodle932.tistory.com/180#entry180comment</comments>
      <pubDate>Fri, 2 Jun 2023 02:03:37 +0900</pubDate>
    </item>
    <item>
      <title>yarn unknown workspace 오류 해결</title>
      <link>https://doooodle932.tistory.com/179</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트에 모노레포를 적용해보는 중에 한 워크스페이스에만 패키지를 추가하기 위해 커맨드를 입력했다.&lt;/p&gt;
&lt;pre id=&quot;code_1684777062094&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// yarn workspace [워크스페이스 이름] add [패키지 이름]
yarn workspace admin-front add @mui/material @emotion/react @emotion/styled&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;하지만 Unknown workspace 라는 오류가 발생했다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;641&quot; data-origin-height=&quot;93&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pIc37/btsgNX8Ju5r/8PmwO7NftGPAEMQ3TWhMH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pIc37/btsgNX8Ju5r/8PmwO7NftGPAEMQ3TWhMH0/img.png&quot; data-alt=&quot;Unknown workspace&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pIc37/btsgNX8Ju5r/8PmwO7NftGPAEMQ3TWhMH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpIc37%2FbtsgNX8Ju5r%2F8PmwO7NftGPAEMQ3TWhMH0%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;641&quot; height=&quot;93&quot; data-origin-width=&quot;641&quot; data-origin-height=&quot;93&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Unknown workspace&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결 방법&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 루트 package.json의 workspaces의 값들이 배열로 되어있는지 확인&lt;/h3&gt;
&lt;pre id=&quot;code_1684777305213&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  // ...
  &quot;workspaces&quot;: [
    &quot;common/*&quot;,
    &quot;apps/*&quot;
  ]
 }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;루트 디렉토리에 있는 package.json의 workspaces가 배열로 되어있는지 확인한다.&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;2. workspace 이름과 package.json의 name이 일치하는지 확인&lt;/h3&gt;
&lt;pre id=&quot;code_1684778227856&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;yarn add [워크스페이스 이름] add [패키지]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 커맨드를 실행할 때의 워크스페이스 이름과 추가하려는 워크스페이스에 있는 package.json의 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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 admin-front라는 프로젝트에만 mui를 추가하고 싶은 경우,&lt;/p&gt;
&lt;pre id=&quot;code_1684778297142&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;yarn workspace admin-front add @mui/material @emotion/react @emotion/styled&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;위에서 입력한 admin-front와 apps/admin-front에 있는 package.json의 name이 일치해야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1684778404190&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;admin-front&quot;,
  // ...
}&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;p data-ke-size=&quot;size16&quot;&gt;이 이름이 일치하지 않아서 생긴 문제였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에 프로젝트 세팅을 한 뒤 폴더 이름만 수정했었는데 폴더 이름을 기준으로만 생각하다보니 패키지 이름은 생각하지 못했다. package.json의 name까지 수정을 해주니 해당 오류는 더이상 발생하지 않았다. (아직도 낯설고 어려운 모노레포 &lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: start;&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;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://classic.yarnpkg.com/en/docs/cli/workspace&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://classic.yarnpkg.com/en/docs/cli/workspace&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/60910762/how-can-i-fix-the-yarn-error-unknown-workspace&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://stackoverflow.com/questions/60910762/how-can-i-fix-the-yarn-error-unknown-workspace&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Trouble Shooting</category>
      <category>MonoRepo</category>
      <category>Yarn</category>
      <category>yarn workspace</category>
      <author>SH_Roh</author>
      <guid isPermaLink="true">https://doooodle932.tistory.com/179</guid>
      <comments>https://doooodle932.tistory.com/179#entry179comment</comments>
      <pubDate>Tue, 23 May 2023 03:20:11 +0900</pubDate>
    </item>
    <item>
      <title>Delete `␍`eslint(prettier/prettier) 해결하기</title>
      <link>https://doooodle932.tistory.com/178</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;472&quot; data-origin-height=&quot;122&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckaBg7/btsgtEunsrh/XnZ7V9Hvi1GOCv1fTq8qKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckaBg7/btsgtEunsrh/XnZ7V9Hvi1GOCv1fTq8qKK/img.png&quot; data-alt=&quot;Delete `␍`eslint(prettier/prettier)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckaBg7/btsgtEunsrh/XnZ7V9Hvi1GOCv1fTq8qKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckaBg7%2FbtsgtEunsrh%2FXnZ7V9Hvi1GOCv1fTq8qKK%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;472&quot; height=&quot;122&quot; data-origin-width=&quot;472&quot; data-origin-height=&quot;122&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Delete `␍`eslint(prettier/prettier)&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;612&quot; data-origin-height=&quot;105&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqsrol/btsgtEgQhe3/UrjL68TAgIz6IWr0nvmma1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqsrol/btsgtEgQhe3/UrjL68TAgIz6IWr0nvmma1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqsrol/btsgtEgQhe3/UrjL68TAgIz6IWr0nvmma1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcqsrol%2FbtsgtEgQhe3%2FUrjL68TAgIz6IWr0nvmma1%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;612&quot; height=&quot;105&quot; data-origin-width=&quot;612&quot; data-origin-height=&quot;105&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;VSCode의 Change End of Line Sequence를 통해 바꿔줄 수도 있다. 하지만 이는 근본적인 해결방법이 아니며, 모든 파일마다 바꿔주기에는 너무 번거롭다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;해결방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ESLint 설정 부분에서 rules 옵션에 아래 내용을 추가해주면 된다.&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;.eslintrc.json&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1684416214056&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;rules&quot;: {
    &quot;prettier/prettier&quot;: [
      &quot;error&quot;,
      {
        &quot;endOfLine&quot;: &quot;auto&quot;
      }
    ]
  }
}&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;이는 prettier 2.0 버전 이상에서 endOfLine 옵션의 default 값이 auto에서 lf로 바뀌었기 때문이라고 한다. auto로 바꿔주면 더이상 기존의 오류가 발생하지 않는다. :-)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;LF vs CRLF&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개행 문자는 텍스트의 한 줄이 끝남을 표시하는 문자 또는 문자열이다. 개행 문자에는 &lt;b&gt;Line Feed(LF)&lt;/b&gt;와 &lt;b&gt;Carriage Return(CR)&lt;/b&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 alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SJPbe/btsgwMqZyBY/sUcigRdS5XC9u9Mnza7Mo1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SJPbe/btsgwMqZyBY/sUcigRdS5XC9u9Mnza7Mo1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SJPbe/btsgwMqZyBY/sUcigRdS5XC9u9Mnza7Mo1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/SJPbe/btsgwMqZyBY/sUcigRdS5XC9u9Mnza7Mo1/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;424&quot; height=&quot;212&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&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;span style=&quot;color: #555555; text-align: start;&quot;&gt;CR&amp;nbsp;과&amp;nbsp;LF&amp;nbsp;는&amp;nbsp;타자기&amp;nbsp;시절&amp;nbsp;부터&amp;nbsp;&lt;/span&gt;줄바꿈을 위해&amp;nbsp;&lt;span style=&quot;color: #555555; text-align: start;&quot;&gt;사용하던 방식인데 각각의 의미는 다음과 같다.&lt;/span&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;LF&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 커서를 정지한 상태에서 종이를 한 줄 올리는 동작&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CR&lt;/b&gt; : 종이를 움직이지 않고 커서를 맨 앞줄로 이동하는 동작&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;윈도우는 CRLF로 새 줄을 나타내고 유닉스, 리눅스는 LF로 새 줄을 나타낸다. (macOS에서는 버전 9까지 CR로 새줄을 나타내다가 이후부터 LF를 사용한다고 한다.)&lt;/p&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;a href=&quot;https://noogoonaa.tistory.com/62&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://noogoonaa.tistory.com/62&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Trouble Shooting</category>
      <category>Delete `␍`eslint(prettier/prettier)</category>
      <category>eslint</category>
      <category>prettier</category>
      <author>SH_Roh</author>
      <guid isPermaLink="true">https://doooodle932.tistory.com/178</guid>
      <comments>https://doooodle932.tistory.com/178#entry178comment</comments>
      <pubDate>Thu, 18 May 2023 23:10:45 +0900</pubDate>
    </item>
    <item>
      <title>[JS] 스크롤 프로그레스 바 만들기</title>
      <link>https://doooodle932.tistory.com/177</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;완성본&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;gif1.gif&quot; data-origin-width=&quot;587&quot; data-origin-height=&quot;288&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m0wrw/btsdGG95jeO/Hi0sBpuZtqURdzl0vfk8Pk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m0wrw/btsdGG95jeO/Hi0sBpuZtqURdzl0vfk8Pk/img.gif&quot; data-alt=&quot;프로그레스 바 완성!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m0wrw/btsdGG95jeO/Hi0sBpuZtqURdzl0vfk8Pk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/m0wrw/btsdGG95jeO/Hi0sBpuZtqURdzl0vfk8Pk/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;587&quot; height=&quot;288&quot; data-filename=&quot;gif1.gif&quot; data-origin-width=&quot;587&quot; data-origin-height=&quot;288&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;프로그레스 바 완성!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;HTML&lt;/h2&gt;
&lt;pre id=&quot;code_1683089868037&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=&quot;progressWrap&quot;&amp;gt;
  &amp;lt;div class=&quot;bar&quot;&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;CSS&lt;/h2&gt;
&lt;pre id=&quot;code_1683090035673&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;body {
  margin: 0;
  padding: 0;
  height: 1000px; /* 스크롤을 위해 임시로 추가 */
}

.progressWrap {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 3px;
  background-color: #fff;
}

.bar {
  width: 0%;
  height: inherit;
  position: absolute;
  z-index: 100;
  background-color: #a9c4ff;
  border-top-right-radius: 3px;
  border-bottom-right-radius: 3px;
  transition: width 300ms;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;JS&lt;/h2&gt;
&lt;pre id=&quot;code_1683091526985&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const progressBar = document.querySelector(&quot;.bar&quot;);

let scrollNum = 0;
let documentHeight = 0;

// 전체 문서에서 얼마나 스크롤되었는지 계산
const getPercent = (scroll, total) =&amp;gt; {
  return (scroll / total) * 100;
};

window.addEventListener(&quot;scroll&quot;, () =&amp;gt; {
  // scroll top이기 때문에 height가 5000px이면 5000에서 화면높이를 뺀 값까지만 나온다.
  scrollNum = document.documentElement.scrollTop; // 또는 window.scrollY 사용 가능
  
  // scroll top값을 기준으로 계산할 것이기 때문에 문서의 전체 길이에서 화면 크기만큼 빼준다.
  documentHeight = document.documentElement.scrollHeight - document.documentElement.clientHeight;
  
  // 전체 문서에서 몇 %만큼 스크롤되었는지 계산해 progress bar의 width를 바꿔준다.
  progressBar.style.width = getPercent(scrollNum, documentHeight) + &quot;%&quot;;
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;document.documentElement&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;document.documentElement는 &amp;lt;html&amp;gt;태그와 상응하는 요소로, 다양한 메서드를 지원한다. 이는 유용하지만 몇 가지 주의할 점이 있다.&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. window 객체보다는 document.documentElement 사용하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;창이 차지하는 너비와 높이를 알기 위해 document.documentElement의 clientWidth와 clientHeight를 사용했다. 그런데 window 객체에도 innerHeight, innerWidth 프로퍼티가 있기 때문에 이를 통해 창 크기를 구할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 document.documentElement를 사용하는 이유가 무엇일까?&lt;/p&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;window&lt;/b&gt;.innerHeight/innerWidth의 경우 화면에서 &lt;u&gt;스크롤바가 차지하는 영역을 포함&lt;/u&gt;해 값을 계산하기 때문이다. 그에 반해 clientWidth, clientHeight는 스크롤바가 차지하는 공간을 제외해 너비, 높이 값을 계산한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 스크롤 바 안쪽에서 무언가를 그리거나 위치시킬 때 창의 사이즈를 필요로 하기 때문에 documentElement의 clientHeight/clientWidth를 쓰는 것이 좋다.&lt;/p&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;아래 예시를 통해 window.innerHeight를 사용했을 때 발생할 수 있는 문제에 대해 알아보자.&lt;/p&gt;
&lt;pre id=&quot;code_1683093318240&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;body {
  margin: 0;
  padding: 0;
  height: 1000px;
  width: 110vw; /* 화면 너비를 늘려 가로 스크롤바가 생기도록 함 */
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 화면 너비를 110vw로 늘려서 아래에 가로 스크롤바가 생기도록 한다.&lt;/p&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;그 후 document.documentElement.clientHeight와 window.innerHeight를 비교해보자.&lt;/p&gt;
&lt;pre id=&quot;code_1683093428321&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.log(document.documentElement.clientHeight, window.innerHeight);&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;139&quot; data-origin-height=&quot;60&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cRB1S4/btsdHPsjqMy/PixMxE6ZDk5mE3DfbkB8Z1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cRB1S4/btsdHPsjqMy/PixMxE6ZDk5mE3DfbkB8Z1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cRB1S4/btsdHPsjqMy/PixMxE6ZDk5mE3DfbkB8Z1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcRB1S4%2FbtsdHPsjqMy%2FPixMxE6ZDk5mE3DfbkB8Z1%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;139&quot; height=&quot;60&quot; data-origin-width=&quot;139&quot; data-origin-height=&quot;60&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;console에 찍힌 값을 보면 window.innerHeight는 가로 스크롤바를 포함한 값이 나온다.&lt;/p&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_1683093648721&quot; class=&quot;javascript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;window.addEventListener(&quot;scroll&quot;, () =&amp;gt; {
  // ...
  documentHeight = document.documentElement.scrollHeight - window.innerHeight;
  // ...
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래 코드에서 document.documentElement.clientHeight 부분을 window.innerHeight로 변경해보자. 가로 스크롤바가 없을 경우에는 동일하게 잘 동작하지만, 가로 스크롤바가 있는 경우 예상치 못한 결과가 나올 수 있다.&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-filename=&quot;gif1.gif&quot; data-origin-width=&quot;794&quot; data-origin-height=&quot;892&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qURPZ/btsdIpmEBYJ/oGOuGJILVotHBOgKFlF9Ak/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qURPZ/btsdIpmEBYJ/oGOuGJILVotHBOgKFlF9Ak/img.gif&quot; data-alt=&quot;스크롤을 덜내렸는데도 프로그레스 바의 width가 100%로 되어있다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qURPZ/btsdIpmEBYJ/oGOuGJILVotHBOgKFlF9Ak/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/qURPZ/btsdIpmEBYJ/oGOuGJILVotHBOgKFlF9Ak/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;380&quot; height=&quot;427&quot; data-filename=&quot;gif1.gif&quot; data-origin-width=&quot;794&quot; data-origin-height=&quot;892&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;스크롤을 덜내렸는데도 프로그레스 바의 width가 100%로 되어있다.&lt;/figcaption&gt;
&lt;/figure&gt;
&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;2. '정확한' 문서 전체 높이 값을 알고 싶다면&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;document.documentElement는 문서의 루트 요소에 상응하고, 루트 요소엔 콘텐츠 전부가 들어가기 때문에 보통 문서 전체의 크기를 구할 때 document.documentElement의 scrollWidth/scrollHeight를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 스크롤이 없는 경우 scrollHeight가 clientHeight보다 작을 때가 있어 예상대로 동작하지 않을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그레스 바 구현에서는 스크롤이 없을 경우 scrollTop이 계속 0이기 때문에 이런 상황은 고려하지 않아도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 정확한 문서 전체 높이 값을 얻고 싶다면 아래와 같이 여섯개의 프로퍼티가 반환하는 값 중 최댓값을 골라야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1683189817409&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let scrollHeight = Math.max(
  document.body.scrollHeight, document.documentElement.scrollHeight,
  document.body.offsetHeight, document.documentElement.offsetHeight,
  document.body.clientHeight, document.documentElement.clientHeight
);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;scrollHeight 사용 시 주의할 점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;페이지의 전체 높이를 구할 때 document.documentElement.scrollHeight 대신 document.body.scrollHeight를 사용할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 기본적으로 scrollHeight는 &lt;u&gt;border과 margin을 제외한 컨텐츠의 전체 높이를 반환&lt;/u&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;h4 data-ke-size=&quot;size20&quot;&gt;HTML&lt;/h4&gt;
&lt;pre id=&quot;code_1683190679517&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;body&amp;gt;
  &amp;lt;div class=&quot;progressWrap&quot;&amp;gt;
    &amp;lt;div class=&quot;bar&quot;&amp;gt;&amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
  &amp;lt;div class=&quot;box&quot;&amp;gt;&amp;lt;/div&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;size16&quot;&gt;프로그레스 바 밑에 box라는 클래스명을 가진 div를 추가해준다.&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;CSS&lt;/h4&gt;
&lt;pre id=&quot;code_1683190738654&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.box {
  margin-top: 500px;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 box에 margin 값을 부여해준다.&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;JS&lt;/h4&gt;
&lt;pre id=&quot;code_1683190832359&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;window.addEventListener(&quot;scroll&quot;, () =&amp;gt; {
  // documentElement -&amp;gt; body
  documentHeight = document.body.scrollHeight - document.documentElement.clientHeight;
  
  console.log(document.body.scrollHeight, document.documentElement.scrollHeight);
  // ...
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래 코드에서 document.documentElement를 document.body로 변경해보자.&lt;/p&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;scrollHeight에 margin값이 측정되지 않아 스크롤을 다 내리지 않아도 프로그레스 바가 100%에 도달해버리는 것을 볼 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;gif1.gif&quot; data-origin-width=&quot;401&quot; data-origin-height=&quot;492&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BVg32/btsdXd7wVvE/1Fs6PxHFLuwXzryGmXQKhk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BVg32/btsdXd7wVvE/1Fs6PxHFLuwXzryGmXQKhk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BVg32/btsdXd7wVvE/1Fs6PxHFLuwXzryGmXQKhk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/BVg32/btsdXd7wVvE/1Fs6PxHFLuwXzryGmXQKhk/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;380&quot; height=&quot;466&quot; data-filename=&quot;gif1.gif&quot; data-origin-width=&quot;401&quot; data-origin-height=&quot;492&quot;/&gt;&lt;/span&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;콘솔에 찍어보면 document.body.scrollHeight와 document.documentElement.scrollHeight에 차이가 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;92&quot; data-origin-height=&quot;45&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eGealH/btsdW8yopiI/2d2k1ZRxjtxsqTBP29uBb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eGealH/btsdW8yopiI/2d2k1ZRxjtxsqTBP29uBb1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eGealH/btsdW8yopiI/2d2k1ZRxjtxsqTBP29uBb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeGealH%2FbtsdW8yopiI%2F2d2k1ZRxjtxsqTBP29uBb1%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;92&quot; height=&quot;45&quot; data-origin-width=&quot;92&quot; data-origin-height=&quot;45&quot;/&gt;&lt;/span&gt;&lt;/figure&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 alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;581&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dqAaGV/btsdW9RqxTJ/e1uZr9vzglMfhHxAkVdKW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dqAaGV/btsdW9RqxTJ/e1uZr9vzglMfhHxAkVdKW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dqAaGV/btsdW9RqxTJ/e1uZr9vzglMfhHxAkVdKW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdqAaGV%2FbtsdW9RqxTJ%2Fe1uZr9vzglMfhHxAkVdKW1%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;640&quot; height=&quot;581&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;581&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;scrollHeight는 말 그대로 '&lt;b&gt;content의 전체 길이&lt;/b&gt;'를 반환하기 때문에 margin과 border는 포함하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 body 안 요소의 border나 margin 때문에 의도치 않은 결과가 나올 수 있기 때문에 document.documentElement를 사용하는 것이 좋다.&lt;/p&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;h2 data-ke-size=&quot;size26&quot;&gt;References&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/course/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9D%B8%ED%84%B0%EB%9E%99%ED%8B%B0%EB%B8%8C-%EC%9B%B9/&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;a href=&quot;https://developer.mozilla.org/ko/docs/Web/API/Element/clientHeight&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.mozilla.org/ko/docs/Web/API/Element/clientHeight&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #383838; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;a href=&quot;https://blogpack.tistory.com/706&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;scrollHeight, clientHeight, offsetHeight 의 차이&lt;/a&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ko.javascript.info/size-and-scroll-window&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://ko.javascript.info/size-and-scroll-window&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/21064101/understanding-offsetwidth-clientwidth-scrollwidth-and-height-respectively&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://stackoverflow.com/questions/21064101/understanding-offsetwidth-clientwidth-scrollwidth-and-height-respectively&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;&amp;nbsp;&lt;/p&gt;</description>
      <category>FE/Javascript</category>
      <category>documentElement</category>
      <category>JavaScript</category>
      <category>JS</category>
      <category>Progress Bar</category>
      <category>scrollHeight</category>
      <category>자바스크립트</category>
      <category>프로그레스바</category>
      <author>SH_Roh</author>
      <guid isPermaLink="true">https://doooodle932.tistory.com/177</guid>
      <comments>https://doooodle932.tistory.com/177#entry177comment</comments>
      <pubDate>Thu, 4 May 2023 18:40:52 +0900</pubDate>
    </item>
    <item>
      <title>[JS] 클로저 - 클로저와 렉시컬 환경</title>
      <link>https://doooodle932.tistory.com/176</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;578&quot; data-origin-height=&quot;432&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5QzSW/btsbjjPK8v4/VTSUVq0DVmKTygzJppDIkK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5QzSW/btsbjjPK8v4/VTSUVq0DVmKTygzJppDIkK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5QzSW/btsbjjPK8v4/VTSUVq0DVmKTygzJppDIkK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5QzSW%2FbtsbjjPK8v4%2FVTSUVq0DVmKTygzJppDIkK%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;500&quot; height=&quot;374&quot; data-origin-width=&quot;578&quot; data-origin-height=&quot;432&quot;/&gt;&lt;/span&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;a href=&quot;https://doooodle932.tistory.com/174&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;u&gt;실행 컨텍스트 - (1) 실행 컨텍스트의 역할, 실행 컨텍스트 스택&lt;/u&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://doooodle932.tistory.com/175&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;u&gt;실행 컨텍스트 - (2) 실행&amp;nbsp;컨텍스트의&amp;nbsp;생성과&amp;nbsp;식별자&amp;nbsp;검색&amp;nbsp;과정&lt;/u&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;클로저(Closure)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클로저는 자바스크립트의 고유의 개념이 아닌(ECMAScript 사양에도 등장하지 않는다) 함수를 일급 객체로 취급하는 함수형 프로그래밍 언어에서 사용되는 중요한 특성이다.&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;MDN에서는 클로저에 대해 &lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Closures&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;'&lt;b&gt;함수와 그 함수가 선언된 렉시컬 환경과의 조합&lt;/b&gt;'&lt;/a&gt;으로 정의하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1681890165616&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const x = 1

function outerFunc() {
  const x = 10
  
  function innerFunc() {
    console.log(x)
  }
  innerFunc()
}
outerFunc() // 10&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서 innerFunc의 상위 스코프가 outerFunc이기 때문에 innerFunc 안에서 outerFunc의 변수에 접근 가능하다. 만약 innerFunc의 상위 스코프가 outerFunc가 아닐 경우 outerFunc 함수의 변수에 접근할 수 없다.&lt;/p&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;u&gt;렉시컬 스코프&lt;/u&gt;를 따르는 프로그래밍 언어이기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;렉시컬 스코프&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트 엔진은 함수를 어디에 호출했는지가 아니라 &lt;u&gt;어디에 정의했는지&lt;/u&gt;에 따라 상위 스코프를 결정한다. 이를 렉시컬 스코프라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1681890531584&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const x = 1

function foo() {
  const x = 10
  bar()
}

function bar() {
  console.log(x)
}

foo() // 1
bar() // 1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제에서 foo 함수와 bar 함수는 모두 전역에 정의된 함수이다. 따라서 상위 스코프는 전역이다. 즉, 함수의 상위 스코프는 함수를 정의한 위치에 의해 결정되고 변하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;함수 객체의 내부 슬롯 [[Environment]]&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수가 정의되는 위치와 호출되는 위치는 다를 수 있다. 따라서 렉시컬 스코프가 가능하려면 함수는 자신이 호출되는 환경과는 상관없이 자신이 정의된 환경(상위 스코프)을 기억해야 한다. 이를 위해 함수는 자신의 &lt;b&gt;내부 슬롯 [[Environment]]에&lt;/b&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;함수 객체의 내부 슬롯 [[Environment]]에 저장된 참조가 바로 상위 스코프이고, 이는 &lt;b&gt;자신이 호출되었을 때 생성될 함수 렉시컬 환경의 '외부 렉시컬 환경에 대한 참조'에 저장될 참조값&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1681891555045&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const x = 1

function foo() {
  const x = 10
  bar()
}

function bar() {
  console.log(x)
}

foo() // 1
bar() // 1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예시에서 foo, bar 함수는 전역에서 함수 선언문으로 정의되었다. 따라서 foo, bar는 모두 전역 코드가 평가될 때 평가되어 함수 객체를 생성한다. 이 때 생성된 함수 객체의 내부 슬롯 &lt;u&gt;[[Environment]]에 전역 렉시컬 환경의 참조가 저장&lt;/u&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;u&gt;외부 렉시컬 환경에 대한 참조에&lt;/u&gt;는 함수 객체의 내부 슬롯 &lt;u&gt;[[Environment]]에 저장된 렉시컬 환경의 참조가 할당&lt;/u&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;h2 data-ke-size=&quot;size26&quot;&gt;클로저와 렉시컬 환경&lt;/h2&gt;
&lt;pre id=&quot;code_1681891998653&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const x = 1

function outer() {
  const x = 10
  const inner = function() { console.log(x) }
  
  return inner
}

const innerFunc = outer()
innerFunc() // 10&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예시에서 outer 함수를 호출하면 outer 함수는 중첩함수 inner를 반환한 후 더 이상 실행할 코드가 없으므로 실행 컨텍스트 스택에서 제거된다. outer 함수의 실행 컨텍스트가 제거되었으므로 outer 함수의 지역 변수 x에 접근할 수 없어 보이지만 innerFunc의 실행 결과를 보면 outer 함수의 지역 변수 x값이 출력된 것을 볼 수 있다.&lt;/p&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;외부 함수보다 중첩 함수가 더 오래 유지되는 경우 중첩 함수는 이미 생명 주기가 종료한 외부 함수의 변수를 참조할 수 있다. 이러한 중첩 함수를 클로저(Closure)라고 부른다.&lt;/b&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 alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1851&quot; data-origin-height=&quot;602&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnAxXZ/btsbmfFQgq3/1hUkVcveX8kJykXc9EUQjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnAxXZ/btsbmfFQgq3/1hUkVcveX8kJykXc9EUQjk/img.png&quot; data-alt=&quot;함수 코드 평가 과정 - 함수 객체의 [[Environment]]에 전역 렉시컬 환경을 상위 스코프로 저장&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnAxXZ/btsbmfFQgq3/1hUkVcveX8kJykXc9EUQjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnAxXZ%2FbtsbmfFQgq3%2F1hUkVcveX8kJykXc9EUQjk%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;800&quot; height=&quot;260&quot; data-origin-width=&quot;1851&quot; data-origin-height=&quot;602&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;함수 코드 평가 과정 - 함수 객체의 [[Environment]]에 전역 렉시컬 환경을 상위 스코프로 저장&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;outer 함수가 평가되어 함수 객체를 생성할 때, 현재 실행 중인 컨텍스트의 렉시컬 환경(전역 렉시컬 환경)을 [[Environment]] 내부 슬롯에 저장한다.&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;1840&quot; data-origin-height=&quot;1051&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUG3S0/btsa9LTLOpI/VcHhVPdbwkxgAgAkTeVJI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUG3S0/btsa9LTLOpI/VcHhVPdbwkxgAgAkTeVJI1/img.png&quot; data-alt=&quot;outer의 [[Environment]]에 저장된 렉시컬 환경을 outer 함수 렉시컬 환경의 외부 렉시컬 환경에 대한 참조에 할당&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUG3S0/btsa9LTLOpI/VcHhVPdbwkxgAgAkTeVJI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUG3S0%2Fbtsa9LTLOpI%2FVcHhVPdbwkxgAgAkTeVJI1%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;800&quot; height=&quot;457&quot; data-origin-width=&quot;1840&quot; data-origin-height=&quot;1051&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;outer의 [[Environment]]에 저장된 렉시컬 환경을 outer 함수 렉시컬 환경의 외부 렉시컬 환경에 대한 참조에 할당&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;outer 함수를 호출하면 outer 함수의 렉시컬 환경이 생성되고, outer 함수 객체의 [[Environment]]에 저장된 전역 렉시컬 환경을 outer 함수 렉시컬 환경의 '외부 렉시컬 환경에 대한 참조'에 할당한다. 그리고 중첩 함수 inner가 평가되어 inner의 [[Environment]]에 outer 함수의 렉시컬 환경을 상위 스코프로서 저장한다.&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;1876&quot; data-origin-height=&quot;1054&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NI564/btsblpI3vae/TBsyw7HmRTh5lqwBVVDrX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NI564/btsblpI3vae/TBsyw7HmRTh5lqwBVVDrX0/img.png&quot; data-alt=&quot;outer 함수의 실행 컨텍스트가 제거되어도 전역 변수 innerFunc가 참조하고 있는 inner 함수가 outer 함수를 참조하고 있기 때문에 outer 함수의 렉시컬 환경은 유지된다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NI564/btsblpI3vae/TBsyw7HmRTh5lqwBVVDrX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNI564%2FbtsblpI3vae%2FTBsyw7HmRTh5lqwBVVDrX0%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;800&quot; height=&quot;449&quot; data-origin-width=&quot;1876&quot; data-origin-height=&quot;1054&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;outer 함수의 실행 컨텍스트가 제거되어도 전역 변수 innerFunc가 참조하고 있는 inner 함수가 outer 함수를 참조하고 있기 때문에 outer 함수의 렉시컬 환경은 유지된다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;outer 함수의 실행이 종료하면 inner 함수를 반환하며 outer 함수 실행 컨텍스트가 실행 컨텍스트 스택에서 제거된다. 하지만 &lt;b&gt;outer 함수의 렉시컬 환경까지 소멸하는 것은 아니다&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;전역 변수 innerFunc가 inner 함수를 참조하고, inner 함수의 [[Environment]]가 outer 함수의 렉시컬 환경을 참조하고 있으므로 가비지 컬렉션의 대상이 되지 않기 때문이다. &lt;u&gt;가비지 컬렉터는 누군가가 참조하고 있는 메모리 공간을 함부로 해제하지 않는다.&lt;/u&gt;&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;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;상위 스코프의 어떤 식별자도 참조하지 않는 함수는 클로저가 아니다.&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1681896326095&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function foo() {
  const x = 1
  const y = 2
  
  function bar() {
    const z = 3
    debugger
    console.log(z)
  }
  
  return bar
}

const bar = foo()
bar()&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;280&quot; data-origin-height=&quot;128&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLIZoE/btsboXRN1pR/skKTnd8HPlQeKSDo7DqKpK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLIZoE/btsboXRN1pR/skKTnd8HPlQeKSDo7DqKpK/img.png&quot; data-alt=&quot;Local: 지역 스코프, Script: 현재 실행 중인 함수, Global: 전역 객체&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLIZoE/btsboXRN1pR/skKTnd8HPlQeKSDo7DqKpK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLIZoE%2FbtsboXRN1pR%2FskKTnd8HPlQeKSDo7DqKpK%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;280&quot; height=&quot;128&quot; data-origin-width=&quot;280&quot; data-origin-height=&quot;128&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Local: 지역 스코프, Script: 현재 실행 중인 함수, Global: 전역 객체&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예제의 중첩 함수 bar는 외부 함수 foo보다 더 오래 유지되지만 상위 스코프의 어떤 식별자도 참조하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상위 스코프의 어떤 식별자도 참조하지 않는 경우 대부분의 모던 브라우저는 최적화를 통해 상위 스코프를 기억하지 않는다. 따라서 bar 함수는 클로저라고 할 수 없다.&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;pre id=&quot;code_1681896728837&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function foo() {
  const x = 1
  
  function bar() {
    debugger
    console.log(x)
  }
  bar()
}
foo()&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;276&quot; data-origin-height=&quot;148&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/daE04W/btsbbr1FHmg/ezxSKwTtqRIysdAupkUdVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/daE04W/btsbbr1FHmg/ezxSKwTtqRIysdAupkUdVK/img.png&quot; data-alt=&quot;bar 함수는 클로저이지만 곧바로 소멸한다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/daE04W/btsbbr1FHmg/ezxSKwTtqRIysdAupkUdVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdaE04W%2Fbtsbbr1FHmg%2FezxSKwTtqRIysdAupkUdVK%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;280&quot; height=&quot;150&quot; data-origin-width=&quot;276&quot; data-origin-height=&quot;148&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;bar 함수는 클로저이지만 곧바로 소멸한다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중첩 함수 bar는 상위 스코프의 식별자를 참조하고 있으므로 클로저이다. 하지만 외부 함수보다 중첩 함수 bar의 생명 주기가 짧기 때문에 생명 주기가 종료된 외부 함수의 식별자를 참조할 수 있다는 클로저의 본질에 부합하지 않는다. 따라서 중첩 함수 bar는 일반적으로 클로저라고 하지 않는다.&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;pre id=&quot;code_1681897067052&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function foo() {
  const x = 1
  const y = 2
  
  function bar() {
    debugger
    console.log(x)
  }
  return bar
}
const bar = foo()
bar()&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;274&quot; data-origin-height=&quot;148&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1om20/btsbmetlGLG/0pXZaHlK99q6Zd4FeXdWTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1om20/btsbmetlGLG/0pXZaHlK99q6Zd4FeXdWTk/img.png&quot; data-alt=&quot;상위 스코프의 식별자 중 클로저가 참조하고 있는 식별자만 기억한다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1om20/btsbmetlGLG/0pXZaHlK99q6Zd4FeXdWTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1om20%2FbtsbmetlGLG%2F0pXZaHlK99q6Zd4FeXdWTk%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;280&quot; height=&quot;151&quot; data-origin-width=&quot;274&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;클로저는 중첩 함수가 상위 스코프의 식별자를 참조하고 있고 중첩 함수가 외부 함수보다 더 오래 유지되는 경우에 한정하는 것이 일반적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클로저인 중첩 함수 bar는 상위 스코프의 x, y 식별자 중 x만 참조하고 있다. 이런 경우 대부분의 모던 브라우저는 최적화를 통해 상위 스코프의 식별자 중에서 클로저가 참조하고 있는 식별자만 기억한다. 클로저에 의해 참조되는 상위 스코프의 변수(위의 경우 foo의 x 변수)를 &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;</description>
      <category>FE/Javascript</category>
      <category>Closure</category>
      <category>JavaScript</category>
      <category>JS</category>
      <category>lexical scope</category>
      <category>렉시컬 스코프</category>
      <category>자바스크립트</category>
      <category>클로저</category>
      <author>SH_Roh</author>
      <guid isPermaLink="true">https://doooodle932.tistory.com/176</guid>
      <comments>https://doooodle932.tistory.com/176#entry176comment</comments>
      <pubDate>Wed, 19 Apr 2023 18:42:39 +0900</pubDate>
    </item>
    <item>
      <title>[JS] 실행 컨텍스트 - (2) 실행 컨텍스트의 생성과 식별자 검색 과정</title>
      <link>https://doooodle932.tistory.com/175</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;502&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bATcr9/btr9sgm9ZsR/tgQnjglQX62U4VB5s6w0yK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bATcr9/btr9sgm9ZsR/tgQnjglQX62U4VB5s6w0yK/img.png&quot; data-alt=&quot;스압 주의!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bATcr9/btr9sgm9ZsR/tgQnjglQX62U4VB5s6w0yK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbATcr9%2Fbtr9sgm9ZsR%2FtgQnjglQX62U4VB5s6w0yK%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;960&quot; height=&quot;502&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;502&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;h4 style=&quot;color: #000000;&quot; 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;&lt;a href=&quot;https://doooodle932.tistory.com/174&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;u&gt;실행 컨텍스트 - (1) 실행 컨텍스트의 역할, 실행 컨텍스트 스택&lt;/u&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실행 컨텍스트 - (2) 실행&amp;nbsp;컨텍스트의&amp;nbsp;생성과&amp;nbsp;식별자&amp;nbsp;검색&amp;nbsp;과정&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;실행 컨텍스트의 생성과 식별자 검색 과정&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예시를 통해 실행 컨텍스트의 생성과 식별자 검색 과정에 대해 알아보자.&lt;/p&gt;
&lt;pre id=&quot;code_1681208827731&quot; class=&quot;javascript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var x = 1
const y = 2

function foo (a) {
  var x = 3
  const y = 4
  
  function bar (b) {
    const z = 5
    console.log(a + b + x + y + z)
  }
  bar(10)
}

foo(20) // 42&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;1659&quot; data-origin-height=&quot;932&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Iq0HU/btr9pX2Hmjg/kgV1nwUahPdWEz9uDvr7w1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Iq0HU/btr9pX2Hmjg/kgV1nwUahPdWEz9uDvr7w1/img.png&quot; data-alt=&quot;실행 컨텍스트 생성과 식별자 검색 과정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Iq0HU/btr9pX2Hmjg/kgV1nwUahPdWEz9uDvr7w1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIq0HU%2Fbtr9pX2Hmjg%2FkgV1nwUahPdWEz9uDvr7w1%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;850&quot; height=&quot;478&quot; data-origin-width=&quot;1659&quot; data-origin-height=&quot;932&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. 전역 객체 생성&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전역 객체는 전역 코드가 평가되기 이전에 생성된다. 전역 객체에 빌트인 전역 프로퍼티와 빌트인 전역 함수, 표준 빌트인 객체가 추가되며 동작 환경에 따라 클라이언트 사이드 Web API 또는 특정 환경을 위한 호스트 객체를 포함한다.&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. 전역 코드 평가&lt;/b&gt;&lt;/h2&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;h3 data-ke-size=&quot;size23&quot;&gt;1 - 전역 실행 컨텍스트 생성&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;755&quot; data-origin-height=&quot;733&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0QEbI/btr9AsNVoay/t2SKk8WXshsNavn97tKHs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0QEbI/btr9AsNVoay/t2SKk8WXshsNavn97tKHs1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0QEbI/btr9AsNVoay/t2SKk8WXshsNavn97tKHs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0QEbI%2Fbtr9AsNVoay%2Ft2SKk8WXshsNavn97tKHs1%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;400&quot; height=&quot;388&quot; data-origin-width=&quot;755&quot; data-origin-height=&quot;733&quot;/&gt;&lt;/span&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2 - 전역 렉시컬 환경 생성&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;734&quot; data-origin-height=&quot;735&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Dck6v/btr9qCc65HU/l9usNGKX9w8njkqfutKvj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Dck6v/btr9qCc65HU/l9usNGKX9w8njkqfutKvj0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Dck6v/btr9qCc65HU/l9usNGKX9w8njkqfutKvj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDck6v%2Fbtr9qCc65HU%2Fl9usNGKX9w8njkqfutKvj0%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;400&quot; height=&quot;401&quot; data-origin-width=&quot;734&quot; data-origin-height=&quot;735&quot;/&gt;&lt;/span&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;b&gt;환경 레코드(Environment Record)&lt;/b&gt;와 &lt;b&gt;외부 렉시컬 환경에 대한 참조(OuterLexicalEnvironmentReference)&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;h4 data-ke-size=&quot;size20&quot;&gt;2-1. 전역 환경 레코드 생성&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;726&quot; data-origin-height=&quot;728&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJdU62/btr9BZdqcY8/xPz049Z0LBkE5c3F6IuN60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJdU62/btr9BZdqcY8/xPz049Z0LBkE5c3F6IuN60/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJdU62/btr9BZdqcY8/xPz049Z0LBkE5c3F6IuN60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJdU62%2Fbtr9BZdqcY8%2FxPz049Z0LBkE5c3F6IuN60%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;400&quot; height=&quot;401&quot; data-origin-width=&quot;726&quot; data-origin-height=&quot;728&quot;/&gt;&lt;/span&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;ES6 이전에는 전역 객체가 전역 환경 레코드의 역할을 수행했다. 하지만 ES6의 let, const로 선언한 전역 변수는 전역 객체의 프로퍼티가 되지 않고 개념적인 블록 내에 존재하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 var로 선언한 전역 변수와 let, const로 선언한 전역 변수를 구분해 관리하기 위해 전역 스코프 역할을 하는 전역 환경 레코드는 객체 환경 레코드와 선언적 환경 레코드로 구성되어 있다.&lt;/p&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 Environment Record)&lt;/b&gt;는 &lt;u&gt;var로 선언한 전역 변수&lt;/u&gt;와 &lt;u&gt;함수 선언문으로 정의한 전역 함수&lt;/u&gt;, &lt;u&gt;빌트인 전역 프로퍼티&lt;/u&gt;와 &lt;u&gt;빌트인 전역 함수&lt;/u&gt;, &lt;u&gt;표준 빌트인 객체를 관리&lt;/u&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 &lt;b&gt;선언적 환경 레코드(Declarative Environment Record)&lt;/b&gt;는 &lt;u&gt;let, const로 선언한 전역 변수를 관리&lt;/u&gt;한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;969&quot; data-origin-height=&quot;355&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Pa2YD/btr9z9nBQkz/a5ySWXq3PLcPkdZbM4o9kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Pa2YD/btr9z9nBQkz/a5ySWXq3PLcPkdZbM4o9kk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Pa2YD/btr9z9nBQkz/a5ySWXq3PLcPkdZbM4o9kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPa2YD%2Fbtr9z9nBQkz%2Fa5ySWXq3PLcPkdZbM4o9kk%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;183&quot; data-origin-width=&quot;969&quot; data-origin-height=&quot;355&quot;/&gt;&lt;/span&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;size18&quot;&gt;&lt;b&gt;2-1-1. 객체 환경 레코드 생성&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;726&quot; data-origin-height=&quot;729&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WOK0l/btr9yyabOVF/8aexJfGAHLtSAmCvPOkJH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WOK0l/btr9yyabOVF/8aexJfGAHLtSAmCvPOkJH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WOK0l/btr9yyabOVF/8aexJfGAHLtSAmCvPOkJH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWOK0l%2Fbtr9yyabOVF%2F8aexJfGAHLtSAmCvPOkJH1%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;400&quot; height=&quot;402&quot; data-origin-width=&quot;726&quot; data-origin-height=&quot;729&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체 환경 레코드는 &lt;span style=&quot;background-color: #eeeeee;&quot;&gt;BindingObject&lt;/span&gt;라고 부르는 객체와 연결된다. BindingObject는 '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;var로 선언한 전역 변수, 함수 선언문으로 정의된 전역 함수는 객체 환경 레코드에 연결된 BindingObject를 통해 전역 객체의 프로퍼티와 메서드가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 등록된 식별자를 객체 환경 레코드에서 검색하면 전역 객체의 프로퍼티를 검색하여 반환한다. 이것이 전역 객체를 가리키는 식별자(window) 없이 전역 객체의 프로퍼티를 참조(e.g. window.alert을 alert으로 참조)할 수 있는 메커니즘이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1681225889946&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var x = 1
const y = 2

function foo (a) {
 // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;x는 var로 선언한 변수이기 때문에 '선언 단계'와 '초기화 단계'가 동시에 진행된다. 다시 말해, BindingObject를 통해 전역 객체에 변수 식별자를 키로 등록한 다음, 암묵적으로 undefined를 바인딩한다. 따라서 var 키워드로 선언한 변수는 코드 실행 단계에서 변수 선언문 이전에도 참조할 수 있다. 이것이 변수 호이스팅이 발생하는 원인이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 선언문으로 정의한 함수가 평가되면 함수 이름과 동일한 이름의 식별자를 BindingObject를 통해 전역 객체에 키로 등록하고 생성된 함수 객체를 즉시 할당한다. 즉, 함수 선언문으로 정의한 함수는 함수 선언문 이전에 호출할 수 있다. 이것이 변수 호이스팅과 함수 호이스팅의 차이이다.&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;1477&quot; data-origin-height=&quot;274&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WFHLU/btr9CJJGYSs/hNYzGsiejYtmtKoJmHr3hk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WFHLU/btr9CJJGYSs/hNYzGsiejYtmtKoJmHr3hk/img.png&quot; data-alt=&quot;BindingObject를 통해 var로 선언한 전역변수, 함수 선언문으로 선언한 전역함수가 전역 객체의 프로퍼티와 메서드가 된다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WFHLU/btr9CJJGYSs/hNYzGsiejYtmtKoJmHr3hk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWFHLU%2Fbtr9CJJGYSs%2FhNYzGsiejYtmtKoJmHr3hk%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;800&quot; height=&quot;148&quot; data-origin-width=&quot;1477&quot; data-origin-height=&quot;274&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;BindingObject를 통해 var로 선언한 전역변수, 함수 선언문으로 선언한 전역함수가 전역 객체의 프로퍼티와 메서드가 된다.&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;size18&quot;&gt;&lt;b&gt;2-1-2. 선언적 환경 레코드 생성&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;727&quot; data-origin-height=&quot;729&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFtBzC/btr9BHqnynq/rYDOJCxK1gfxgFOf4TZV31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFtBzC/btr9BHqnynq/rYDOJCxK1gfxgFOf4TZV31/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFtBzC/btr9BHqnynq/rYDOJCxK1gfxgFOf4TZV31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFtBzC%2Fbtr9BHqnynq%2FrYDOJCxK1gfxgFOf4TZV31%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;400&quot; height=&quot;401&quot; data-origin-width=&quot;727&quot; data-origin-height=&quot;729&quot;/&gt;&lt;/span&gt;&lt;/figure&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전역 변수 y는 let, const로 선언한 변수이므로 전역 객체의 프로퍼티가 되지 않기 때문에 window.y와 같이 전역 객체의 프로퍼티로서 참조할 수 없다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;또한 '선언 단계'와 '초기화 단계'가 분리되어 진행되기 때문에 런타임에 실행 흐름이 변수 선언문에 도달하기 전까지 &lt;u&gt;일시적 사각지대(Temporal Dead Zone;&lt;/u&gt;&lt;b&gt;TDZ&lt;/b&gt;&lt;u&gt;)&lt;/u&gt;에 빠지게 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; 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;1480&quot; data-origin-height=&quot;473&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ypGDu/btr9NCJbkGk/pMcIz7hs9jpxAWK1SetlY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ypGDu/btr9NCJbkGk/pMcIz7hs9jpxAWK1SetlY0/img.png&quot; data-alt=&quot;const로 선언한 변수 y가 선언적 환경 레코드에 등록된다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ypGDu/btr9NCJbkGk/pMcIz7hs9jpxAWK1SetlY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FypGDu%2Fbtr9NCJbkGk%2FpMcIz7hs9jpxAWK1SetlY0%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;800&quot; height=&quot;256&quot; data-origin-width=&quot;1480&quot; data-origin-height=&quot;473&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;const로 선언한 변수 y가 선언적 환경 레코드에 등록된다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림에서 y 변수에 바인딩되어 있는 &amp;lt;uninitialized&amp;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;&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;2-2. this 바인딩&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;731&quot; data-origin-height=&quot;725&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzpmYq/btr9BYyTnqp/WdYEnsZaCLChoLzliB1kO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzpmYq/btr9BYyTnqp/WdYEnsZaCLChoLzliB1kO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzpmYq/btr9BYyTnqp/WdYEnsZaCLChoLzliB1kO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzpmYq%2Fbtr9BYyTnqp%2FWdYEnsZaCLChoLzliB1kO0%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;400&quot; height=&quot;397&quot; data-origin-width=&quot;731&quot; data-origin-height=&quot;725&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전역 환경 레코드의 [[GlobalThisValue]] 내부 슬롯에 this가 바인딩된다. 일반적으로 전역 코드에서 this는 전역 객체를 가리키므로 전역 환경 레코드의 [[GlobalThisValue]] 내부 슬롯에는 전역 객체가 바인딩된다.&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;1484&quot; data-origin-height=&quot;585&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Cl8Xh/btr9Mqo2GW9/jUCCAHHG1tEw66771QlJt0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Cl8Xh/btr9Mqo2GW9/jUCCAHHG1tEw66771QlJt0/img.png&quot; data-alt=&quot;[[GlobalThisValue]] 내부 슬롯에는 전역 객체가 바인딩된다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Cl8Xh/btr9Mqo2GW9/jUCCAHHG1tEw66771QlJt0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCl8Xh%2Fbtr9Mqo2GW9%2FjUCCAHHG1tEw66771QlJt0%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;800&quot; height=&quot;315&quot; data-origin-width=&quot;1484&quot; data-origin-height=&quot;585&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;[[GlobalThisValue]] 내부 슬롯에는 전역 객체가 바인딩된다.&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;참고로 객체 환경 레코드와 선언적 환경 레코드에는 this 바인딩이 없다. 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;h4 data-ke-size=&quot;size20&quot;&gt;2-3. 외부 렉시컬 환경에 대한 참조 결정&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;728&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uNKfF/btr9DUpcCTJ/IEbyHq7l4FvGG31qBEH19k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uNKfF/btr9DUpcCTJ/IEbyHq7l4FvGG31qBEH19k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uNKfF/btr9DUpcCTJ/IEbyHq7l4FvGG31qBEH19k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuNKfF%2Fbtr9DUpcCTJ%2FIEbyHq7l4FvGG31qBEH19k%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;400&quot; height=&quot;396&quot; data-origin-width=&quot;736&quot; data-origin-height=&quot;728&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 렉시컬 환경에 대한 참조(Outer Lexical Environment Reference)는 현재 평가 중인 소스코드의 상위 스코프를 가리킨다. 이를 통해 단방향 연결 리스트인 스코프 체인을 구현한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 평가 중인 소스코드는 전역 코드이기 때문에 전역 렉시컬 환경의 OuterLexicalEnvironmentReference에는 null이 할당된다.&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;1483&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RI8Mq/btr9GheRGWm/DfCms19QDgcdHLKP2IAkCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RI8Mq/btr9GheRGWm/DfCms19QDgcdHLKP2IAkCK/img.png&quot; data-alt=&quot;외부 렉시컬 환경에 대한 참조에는 null이 할당된다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RI8Mq/btr9GheRGWm/DfCms19QDgcdHLKP2IAkCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRI8Mq%2Fbtr9GheRGWm%2FDfCms19QDgcdHLKP2IAkCK%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;800&quot; height=&quot;309&quot; data-origin-width=&quot;1483&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;외부 렉시컬 환경에 대한 참조에는 null이 할당된다.&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;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. 전역 코드 실행&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전역 코드가 순차적으로 실행되면서 변수 할당문이 실행되어 전역 변수 x, y에 값이 할당되고 foo 함수가 호출된다.&lt;/p&gt;
&lt;pre id=&quot;code_1681287973848&quot; class=&quot;actionscript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var x = 1
const y = 2

function foo (a) {
 // ...
}&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;이때 동일한 이름의 식별자가 다른 스코프에 여러 개 존재할 수도 있기 때문에 어느 스코프의 식별자를 참조하면 되는지 결정할 필요가 있다. 이를 식별자 결정이라고 한다.&lt;/p&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;현재 실행 중인 실행 컨텍스트는 전역 실행 컨텍스트이므로 전역 렉시컬 환경에서 x, y, foo를 검색한다. 만약 검색할 수 없으면 상위 스코프(외부 렉시컬 환경에 대한 참조가 가리키는 렉시컬 환경)로 이동해 식별자를 검색한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 스코프 체인의 동작 원리다. (전역 렉시컬 환경에서 검색할 수 없는 식별자는 ReferenceError를 발생시킨다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. 함수 코드 평가&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;foo 함수가 호출되면 전역 코드의 실행을 일시 중단하고 foo 함수 내부로 코드의 제어권이 이동한다. 그리고 함수 코드를 평가하기 시작한다.&lt;/p&gt;
&lt;pre id=&quot;code_1681288683404&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function foo (a) {
  var x = 3
  const y = 4
  
  function bar (b) {
    // ...
  }
  bar(10)
}&lt;/code&gt;&lt;/pre&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 - 함수 실행 컨텍스트 생성&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;725&quot; data-origin-height=&quot;554&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pGYyg/btr9N9NyArP/wSGj3kJo8QACDkqQWs1NAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pGYyg/btr9N9NyArP/wSGj3kJo8QACDkqQWs1NAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pGYyg/btr9N9NyArP/wSGj3kJo8QACDkqQWs1NAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpGYyg%2Fbtr9N9NyArP%2FwSGj3kJo8QACDkqQWs1NAK%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;400&quot; height=&quot;306&quot; data-origin-width=&quot;725&quot; data-origin-height=&quot;554&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;foo 함수 실행 컨텍스트를 생성하고 실행 컨텍스트 스택에 푸시한다. 이때 foo 함수 실행 컨텍스트가 실행 중인 실행 컨텍스트가 된다.&lt;/p&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;2 - 함수 렉시컬 환경 생성&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;718&quot; data-origin-height=&quot;551&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oCalX/btr9Me3e3k7/dsKhHKRlFMKVaAxKf4byAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oCalX/btr9Me3e3k7/dsKhHKRlFMKVaAxKf4byAk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oCalX/btr9Me3e3k7/dsKhHKRlFMKVaAxKf4byAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoCalX%2Fbtr9Me3e3k7%2FdsKhHKRlFMKVaAxKf4byAk%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;400&quot; height=&quot;307&quot; data-origin-width=&quot;718&quot; data-origin-height=&quot;551&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;foo 함수 렉시컬 환경을 생성해 foo 함수 실행 컨텍스트에 바인딩한다.&lt;/p&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;2-1. 함수 환경 레코드 생성&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;718&quot; data-origin-height=&quot;548&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ceOAuV/btr9ObkjVd6/CbDd5Ra5ZnIGXnjGAAjyRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ceOAuV/btr9ObkjVd6/CbDd5Ra5ZnIGXnjGAAjyRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ceOAuV/btr9ObkjVd6/CbDd5Ra5ZnIGXnjGAAjyRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FceOAuV%2Fbtr9ObkjVd6%2FCbDd5Ra5ZnIGXnjGAAjyRk%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;400&quot; height=&quot;305&quot; data-origin-width=&quot;718&quot; data-origin-height=&quot;548&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 환경 레코드는 매개변수, arguments 객체, 함수 내부에서 선언한 지역 변수와 중첩 함수를 등록하고 관리한다.&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;1506&quot; data-origin-height=&quot;516&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oTNIN/btr9C4gbKFP/XhSoj2PvnKGuq719BWyUA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oTNIN/btr9C4gbKFP/XhSoj2PvnKGuq719BWyUA1/img.png&quot; data-alt=&quot;매개변수, arguments 객체, 함수 몸체에서 선언된 식별자를 등록한다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oTNIN/btr9C4gbKFP/XhSoj2PvnKGuq719BWyUA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoTNIN%2Fbtr9C4gbKFP%2FXhSoj2PvnKGuq719BWyUA1%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;800&quot; height=&quot;274&quot; data-origin-width=&quot;1506&quot; data-origin-height=&quot;516&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;매개변수, arguments 객체, 함수 몸체에서 선언된 식별자를 등록한다.&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;h4 data-ke-size=&quot;size20&quot;&gt;2-2. this 바인딩&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;735&quot; data-origin-height=&quot;553&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mxnZ0/btr9OyfpEs9/diBKl7Rk6Hj4h2jCerqF51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mxnZ0/btr9OyfpEs9/diBKl7Rk6Hj4h2jCerqF51/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mxnZ0/btr9OyfpEs9/diBKl7Rk6Hj4h2jCerqF51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmxnZ0%2Fbtr9OyfpEs9%2FdiBKl7Rk6Hj4h2jCerqF51%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;400&quot; height=&quot;301&quot; data-origin-width=&quot;735&quot; data-origin-height=&quot;553&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 환경 레코드의 [[ThisValue]] 내부 슬롯에 this가 바인딩된다. 바인딩될 객체는 함수 호출 방식에 따라 결정되며, foo 함수는 일반 함수로 호출되었으므로 this는 전역 객체를 가리킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;foo 함수 내부에서 this를 참조하면 함수 환경 레코드의 [[ThisValue]] 내부 슬롯에 바인딩되어 있는 객체가 반환된다.&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;1490&quot; data-origin-height=&quot;1077&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CTALB/btr9NAEOX43/IupMU0DCW8lgcbTjdcspb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CTALB/btr9NAEOX43/IupMU0DCW8lgcbTjdcspb1/img.png&quot; data-alt=&quot;함수 환경 레코드의 [[ThisValue]]는 함수 호출 방식에 따라 결정된다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CTALB/btr9NAEOX43/IupMU0DCW8lgcbTjdcspb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCTALB%2Fbtr9NAEOX43%2FIupMU0DCW8lgcbTjdcspb1%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;800&quot; height=&quot;578&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;1077&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;함수 환경 레코드의 [[ThisValue]]는 함수 호출 방식에 따라 결정된다.&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;h4 data-ke-size=&quot;size20&quot;&gt;2-3. 외부 렉시컬 환경에 대한 참조 결정&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;733&quot; data-origin-height=&quot;551&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nNY1k/btr9QkVrXQi/iYH0UPYxDEKLWN9FabkaS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nNY1k/btr9QkVrXQi/iYH0UPYxDEKLWN9FabkaS1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nNY1k/btr9QkVrXQi/iYH0UPYxDEKLWN9FabkaS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnNY1k%2Fbtr9QkVrXQi%2FiYH0UPYxDEKLWN9FabkaS1%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;400&quot; height=&quot;301&quot; data-origin-width=&quot;733&quot; data-origin-height=&quot;551&quot;/&gt;&lt;/span&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;foo 함수는 전역 코드에 정의된 전역 함수이다. 따라서 foo 함수 정의는 전역 코드 평가 시점에 평가되고, 이 시점의 실행 중인 실행 컨텍스트는 전역 실행 컨텍스트이다. 따라서 외부 렉시컬 환경에 대한 참조에는 전역 렉시컬 환경의 참조가 할당된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1496&quot; data-origin-height=&quot;1075&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7pC0r/btr9QVgThvJ/QKKkuhi9VKfNUNXckM1ICk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7pC0r/btr9QVgThvJ/QKKkuhi9VKfNUNXckM1ICk/img.png&quot; data-alt=&quot;함수 렉시컬 환경의 외부 렉시컬 환경에 대한 참조는 함수 정의가 평가된 시점에 실행 중인 실행 컨텍스트의 렉시컬 환경의 참조가 할당된다&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7pC0r/btr9QVgThvJ/QKKkuhi9VKfNUNXckM1ICk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7pC0r%2Fbtr9QVgThvJ%2FQKKkuhi9VKfNUNXckM1ICk%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;800&quot; height=&quot;575&quot; data-origin-width=&quot;1496&quot; data-origin-height=&quot;1075&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트 엔진은 함수 객체를 생성할 때 현재 실행 중인 실행 컨텍스트의 렉시컬 환경(상위 스코프)을 함수 객체의 내부 슬롯 [[Environment]]에 저장한다. 함수 렉시컬 환경의 외부 렉시컬 환경에 대한 참조에는 함수 객체의 [[Environment]]에 저장된 렉시컬 환경의 참조이다. 즉, 함수 객체의 내부 슬롯 [[Environment]]가 바로 렉시컬 스코프를 구현하는 메커니즘이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;5. 함수 코드 실행&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;foo 함수의 소스코드가 순차적으로 실행되면서 매개변수에 인수가 할당되고, 변수 할당문이 실행되어 지역 변수 x, y에 값이 할당된다. 그리고 bar 함수가 호출된다.&lt;/p&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;h2 data-ke-size=&quot;size26&quot;&gt;중첩 함수 bar&lt;/h2&gt;
&lt;pre id=&quot;code_1681292192710&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var x = 1
const y = 2

function foo (a) {
  var x = 3
  const y = 4
  
  function bar (b) {
    const z = 5
    console.log(a + b + x + y + z)
  }
  bar(10)
}

foo(20) // 42&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1 - 중첩 함수 코드 평가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bar 함수가 호출되면 bar 함수 내부로 코드 제어권이 이동하고, bar 함수 코드의 평가가 시작된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bar 함수의 실행 컨텍스트 생성과 렉시컬 환경의 생성 환경은 foo 함수 코드 평가와 동일하다.&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;2 - 중첩 함수 코드 실행&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매개변수에 인수가 할당되고, 변수 할당문이 실행되어 지역 변수 z에 값이 할당된다. 그리고 console.log(a + b + x + y + z)가 실행된다. 이 코드는 다음과 같은 순서로 실행된다.&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;1. console 식별자 검색&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;console 식별자를 스코프 체인에서 검색한다. 식별자를 검색할 때는 언제나 현재 실행 중인 실행 컨텍스트의 렉시컬 환경에서부터 검색하기 시작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 중인 실행 컨텍스트는 bar 함수 실행 컨텍스트이기 때문에 bar 함수 실행 컨텍스트의 렉시컬 환경에서 console 식별자를 검색한다. 이곳에는 console 식별자가 없으므로 상위 스코프로 이동해 검색한다. 이곳에도 console 식별자가 없으므로 상위 스코프로 이동해 검색한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;console 식별자는 전역 렉시컬 환경의 객체 환경 레코드의 BindingObject를 통해 전역 객체에서 찾을 수 있다.&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;2. log 메서드 검색&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;console 객체에서 log 메서드를 검색한다. 이때 console 객체의 프로토타입 체인을 통해 메서드를 검색한다.&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;3. 표현식 a + b + x + y + z의 평가&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;console.log에 전달할 인수를 평가하기 위해 a, b, x, y, z 식별자를 검색한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;식별자는 현재 실행 중인 실행 컨텍스트(bar 함수 실행 컨텍스트)의 렉시컬 환경부터 시작해 외부 렉시컬 환경에 대한 참조로 이어지는 렉시컬 환경의 연속에서 검색한다.&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;4. console.log 메서드 호출&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;표현식 a + b + x + y + z가 평가되어 생성한 값을 console.log 메서드에 전달해 호출한다.&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;더는 실행할 코드가 없으므로 bar 함수 코드의 실행이 종료된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 컨텍스트 스택에서 bar 함수 실행 컨텍스트가 제거되고, foo 실행 컨텍스트가 실행 중인 실행 컨텍스트가 된다.&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;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;u&gt;주의할 점&lt;/u&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 컨텍스트 스택에서 bar 함수 실행 컨텍스트가 제거되었다고 해서 bar 함수 렉시컬 환경까지 즉시 소멸되는 것은 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체를 포함한 모든 값은 누군가에 의해 참조되지 않을 때 가비지 컬렉터에 의해 메모리 공간의 확보가 해제되어 소멸된다. bar 함수 렉시컬 환경을 누군가 참조하고 있다면 bar 함수 렉시컬 환경은 소멸하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;6. 함수 코드 실행 종료&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더 이상 실행할 코드가 없으므로 foo 함수 코드의 실행이 종료된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 컨텍스트 스택에서 foo 함수 실행 컨텍스트가 제거되고, 전역 실행 컨텍스트가 실행 중인 실행 컨텍스트가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;7. 전역 코드 실행 종료&lt;/b&gt;&lt;/h2&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;시리즈&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://doooodle932.tistory.com/174&quot;&gt;&lt;u&gt;실행 컨텍스트 - (1) 실행 컨텍스트의 역할, 실행 컨텍스트 스택&lt;/u&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;실행 컨텍스트 - (2) 실행&amp;nbsp;컨텍스트의&amp;nbsp;생성과&amp;nbsp;식별자&amp;nbsp;검색&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;&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2538&quot; data-origin-height=&quot;2538&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/caqDwv/btr9OazaxdR/zxqEZV2rg6GUIS9ndKmBq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/caqDwv/btr9OazaxdR/zxqEZV2rg6GUIS9ndKmBq1/img.png&quot; data-alt=&quot;역시 직접 써보면 더 이해가 잘된다 :)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/caqDwv/btr9OazaxdR/zxqEZV2rg6GUIS9ndKmBq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcaqDwv%2Fbtr9OazaxdR%2FzxqEZV2rg6GUIS9ndKmBq1%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;200&quot; data-origin-width=&quot;2538&quot; data-origin-height=&quot;2538&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;</description>
      <category>FE/Javascript</category>
      <category>JavaScript</category>
      <category>JS</category>
      <category>렉시컬 환경</category>
      <category>실행 컨텍스트</category>
      <category>환경 레코드</category>
      <author>SH_Roh</author>
      <guid isPermaLink="true">https://doooodle932.tistory.com/175</guid>
      <comments>https://doooodle932.tistory.com/175#entry175comment</comments>
      <pubDate>Wed, 12 Apr 2023 19:03:52 +0900</pubDate>
    </item>
    <item>
      <title>[JS] 실행 컨텍스트 - (1) 실행 컨텍스트의 역할, 실행 컨텍스트 스택</title>
      <link>https://doooodle932.tistory.com/174</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;502&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DHzi0/btr9DV2tIvX/B95ridSCKPwMxHRkyBiHy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DHzi0/btr9DV2tIvX/B95ridSCKPwMxHRkyBiHy0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DHzi0/btr9DV2tIvX/B95ridSCKPwMxHRkyBiHy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDHzi0%2Fbtr9DV2tIvX%2FB95ridSCKPwMxHRkyBiHy0%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;960&quot; height=&quot;502&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;502&quot;/&gt;&lt;/span&gt;&lt;/figure&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;&lt;b&gt;실행 컨텍스트 - (1) 실행 컨텍스트의 역할, 실행 컨텍스트 스택&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://doooodle932.tistory.com/175&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;u&gt;실행 컨텍스트 - (2) 실행&amp;nbsp;컨텍스트의&amp;nbsp;생성과&amp;nbsp;식별자&amp;nbsp;검색&amp;nbsp;과정&lt;/u&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;소스코드의 타입&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ECMAScript 사양은 소스코드를 4가지 타입으로 구분한다. 4가지 타입의 소스코드는 실행 컨텍스트를 생성한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;전역 코드&lt;/b&gt;: 전역에 존재하는 소스코드(전역에 정의된 함수, 클래스 등의 내부 코드는 포함 x)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;함수 코드&lt;/b&gt;: 함수 내부에 존재하는 소스코드(중첩된 함수, 클래스 등의 내부 코드는 포함 x)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;eval 코드&lt;/b&gt;: 빌트인 전역 함수인 eval 함수에 인수로 전달되어 실행되는 소스코드&lt;/li&gt;
&lt;li&gt;&lt;b&gt;모듈 코드&lt;/b&gt;: 모듈 내부에 존재하는 소스코드(모듈 내부의 함수, 클래스 등의 내부 코드는 포함 x)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4가지 타입으로 분류하는 이유는 소스코드의 타입에 따라 실행 컨텍스트를 생성하는 과정과 관리 내용이 다르기 때문이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;548&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bapvyV/btr9sjRwphF/SQ2HKgYBu8YDp15OO5gucK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bapvyV/btr9sjRwphF/SQ2HKgYBu8YDp15OO5gucK/img.png&quot; data-alt=&quot;출처 - 자바스크립트 Deep Dive&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bapvyV/btr9sjRwphF/SQ2HKgYBu8YDp15OO5gucK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbapvyV%2Fbtr9sjRwphF%2FSQ2HKgYBu8YDp15OO5gucK%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;550&quot; height=&quot;402&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;548&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처 - 자바스크립트 Deep Dive&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;소스코드의 평가와 실행&lt;/b&gt;&lt;/h2&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;pre id=&quot;code_1681202773808&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var x;
x = 1;&lt;/code&gt;&lt;/pre&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. 소스코드 평가&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;실행 컨텍스트 생성&lt;/li&gt;
&lt;li&gt;변수, 함수 식별자를 키로 렉시컬 환경의 환경 레코드에 등록&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수 선언문 &lt;span style=&quot;background-color: #eeeeee;&quot;&gt;var x;&lt;/span&gt;를 먼저 실행하고, 이때 생성된 변수 식별자 x는 실행 컨텍스트가 관리하는 스코프(렉시컬 환경의 환경 레코드)에 등록되고 undefined로 초기화된다.&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;2. 소스코드 실행&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;런타임 시작&lt;/li&gt;
&lt;li&gt;변수, 함수의 참조를 실행 컨텍스트가 관리하는 스코프에서 검색, 취득&lt;/li&gt;
&lt;li&gt;소스코드의 실행 결과를 스코프에 등록&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소스코드 실행 단계에서는 &lt;span style=&quot;background-color: #eeeeee;&quot;&gt;x = 1;&lt;/span&gt;만 실행된다. x에 값을 할당하려면 먼저 x가 선언된 변수인지 확인해야 한다. 이를 위해 실행 컨텍스트가 관리하는 스코프에 x 변수가 등록되어 있는지 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;x 변수가 스코프에 등록되어 있다면 값을 할당하고 할당 결과를 실행 컨텍스트에 등록해 관리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;실행 컨텍스트의 역할&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드가 실행되려면 다음과 같이 스코프, 식별자, 코드 실행 순서 등의 관리가 필요하다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;선언에 의해 생성된 모든 &lt;b&gt;식별자를 스코프를 구분하여 등록&lt;/b&gt;하고 식별자에 &lt;b&gt;바인딩된 값의 변화를 지속적으로 관리&lt;/b&gt;할 수 있어야 한다.&lt;/li&gt;
&lt;li&gt;스코프 체인을 통해 &lt;b&gt;상위 스코프로 이동하며 식별자를 검색&lt;/b&gt;할 수 있어야 한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;현재 실행 중인 코드의 실행 순서를 변경&lt;/b&gt;(e.g. 함수 호출에 의한 실행 순서 변경)할 수 있어야 하며 &lt;b&gt;다시 되돌아갈 수도&lt;/b&gt; 있어야 한다.&lt;/li&gt;
&lt;/ol&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;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;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;664&quot; data-origin-height=&quot;201&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgrhGQ/btr9wGMN7uv/l9VFlYVQsv0P8S7azY4eS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgrhGQ/btr9wGMN7uv/l9VFlYVQsv0P8S7azY4eS0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgrhGQ/btr9wGMN7uv/l9VFlYVQsv0P8S7azY4eS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgrhGQ%2Fbtr9wGMN7uv%2Fl9VFlYVQsv0P8S7azY4eS0%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;400&quot; height=&quot;121&quot; data-origin-width=&quot;664&quot; data-origin-height=&quot;201&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;실행 컨텍스트 스택(코드 실행 순서 관리)&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1681204501995&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const x = 1

function foo() {
  const y = 2
  
  function bar() {
    const z = 3
    console.log(x + y + z)
  }
  bar()
}

foo() // 6&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;b&gt;실행 컨텍스트 스택&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;1280&quot; data-origin-height=&quot;287&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nA6pz/btr9BHDyDGB/3mY86w4Pspk31T42Q1k8Mk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nA6pz/btr9BHDyDGB/3mY86w4Pspk31T42Q1k8Mk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nA6pz/btr9BHDyDGB/3mY86w4Pspk31T42Q1k8Mk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnA6pz%2Fbtr9BHDyDGB%2F3mY86w4Pspk31T42Q1k8Mk%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;850&quot; height=&quot;191&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;287&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 전역 코드의 평가와 실행&lt;/h4&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;h4 data-ke-size=&quot;size20&quot;&gt;2. foo 함수 코드의 평가와 실행&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전역 함수 foo가 호출되면 &lt;u&gt;전역 코드의 실행은 일시 중단&lt;/u&gt;되고 &lt;u&gt;코드의 제어권이 foo 함수 내부로 이동&lt;/u&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트 엔진은 foo 함수 내부의 함수 코드를 평가해 foo 함수 실행 컨텍스트를 생성하고 실행 컨텍스트 스택에 푸시한다.&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;3. bar 함수 코드의 평가와 실행&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중첩 함수 bar가 호출되면 foo 함수 코드의 실행은 일시 중단되고 코드의 제어권이 bar 함수 내부로 이동한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마찬가지로 자바스크립트 엔진은 bar 함수 실행 컨텍스트를 생성해 실행 컨텍스트 스택에 푸시한다.&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;4. foo 함수 코드로 복귀&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bar 함수가 종료되면 자바스크립트 엔진은 bar 함수 실행 컨텍스트를 실행 컨텍스트 스택에서 제거하고, 코드의 제어권은 다시 foo 함수로 이동한다. 그리고 foo 함수는 더 이상 실행할 코드가 없으므로 종료된다.&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;5. 전역 코드로 복귀&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;foo 함수가 종료되면 foo 함수 실행 컨텍스트를 실행 컨텍스트 스택에서 제거하고, 코드의 제어권은 전역 코드로 이동한다. 그리고 더 이상 실행할 코드가 없으므로 전역 실행 컨텍스트도 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;&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;(running execution context)라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;렉시컬 환경(식별자와 스코프 관리)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;렉시컬 환경(Lexical Environment)은 &lt;b&gt;식별자&lt;/b&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;렉시컬 환경은 다음과 같이 두 개의 컴포넌트로 구성된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;562&quot; data-origin-height=&quot;235&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/drfOCD/btr9sfPmKJJ/JoEbgqCOnQw4x7B3rMmykk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/drfOCD/btr9sfPmKJJ/JoEbgqCOnQw4x7B3rMmykk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/drfOCD/btr9sfPmKJJ/JoEbgqCOnQw4x7B3rMmykk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdrfOCD%2Fbtr9sfPmKJJ%2FJoEbgqCOnQw4x7B3rMmykk%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;380&quot; height=&quot;159&quot; data-origin-width=&quot;562&quot; data-origin-height=&quot;235&quot;/&gt;&lt;/span&gt;&lt;/figure&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;1. 환경 레코드(Environment Record)&lt;/h4&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;h4 data-ke-size=&quot;size20&quot;&gt;2. 외부 렉시컬 환경에 대한 참조(Outer Lexical Environment Reference)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;상위 스코프&lt;/b&gt;를 가리킨다. 이때 상위 스코프란 해당 실행 컨텍스트를 생성한 소스코드를 포함하는 &lt;u&gt;상위 코드의 렉시컬 환경&lt;/u&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;h4 style=&quot;color: #000000;&quot; 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;&lt;b&gt;실행 컨텍스트 - (1) 실행 컨텍스트의 역할, 실행 컨텍스트 스택&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;a href=&quot;https://doooodle932.tistory.com/175&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;실행 컨텍스트 - (2) 실행&amp;nbsp;컨텍스트의&amp;nbsp;생성과&amp;nbsp;식별자&amp;nbsp;검색&amp;nbsp;과정&lt;/a&gt;&lt;/u&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>FE/Javascript</category>
      <category>JavaScript</category>
      <category>JS</category>
      <category>렉시컬 환경</category>
      <category>실행 컨텍스트</category>
      <category>환경 레코드</category>
      <author>SH_Roh</author>
      <guid isPermaLink="true">https://doooodle932.tistory.com/174</guid>
      <comments>https://doooodle932.tistory.com/174#entry174comment</comments>
      <pubDate>Tue, 11 Apr 2023 19:34:31 +0900</pubDate>
    </item>
    <item>
      <title>[VanillaJS, React] 마우스 따라다니는 원 만들기(feat. requestAnimationFrame)</title>
      <link>https://doooodle932.tistory.com/172</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;잘 만들어진 인터랙티브 웹들을 보며 저건 어떻게 만드는걸까 항상 궁금했었다. 인프런을 둘러보다가 &lt;a href=&quot;https://www.inflearn.com/course/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9D%B8%ED%84%B0%EB%9E%99%ED%8B%B0%EB%B8%8C-%EC%9B%B9/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;몇 줄로 끝내는 인터랙티브 웹 개발 노하우&lt;/a&gt;라는 강의를 발견했고, 기초적인 것들을 만들어봐야겠다고 생각했다.&lt;/p&gt;
&lt;figure id=&quot;og_1680187881054&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;몇 줄로 끝내는 인터랙티브 웹 개발 노하우 [초급편] - 인프런 | 강의&quot; data-og-description=&quot;짧은 시간에 실무에서 가장 많이 사용하는 인터랙티브 스킬을 알려드립니다., - 강의 소개 | 인프런&quot; data-og-host=&quot;www.inflearn.com&quot; data-og-source-url=&quot;https://www.inflearn.com/course/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9D%B8%ED%84%B0%EB%9E%99%ED%8B%B0%EB%B8%8C-%EC%9B%B9/&quot; data-og-url=&quot;https://www.inflearn.com/course/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9D%B8%ED%84%B0%EB%9E%99%ED%8B%B0%EB%B8%8C-%EC%9B%B9/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/iLIXU/hyR5tJOBPJ/pPIqxUkwqaXMVxrHWtH8MK/img.gif?width=600&amp;amp;height=346&amp;amp;face=0_0_600_346,https://scrap.kakaocdn.net/dn/9yIaP/hyR6HTZNjM/T8ooGkoP4vigkdlbozKDj1/img.gif?width=600&amp;amp;height=346&amp;amp;face=0_0_600_346&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/course/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9D%B8%ED%84%B0%EB%9E%99%ED%8B%B0%EB%B8%8C-%EC%9B%B9/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.inflearn.com/course/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9D%B8%ED%84%B0%EB%9E%99%ED%8B%B0%EB%B8%8C-%EC%9B%B9/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/iLIXU/hyR5tJOBPJ/pPIqxUkwqaXMVxrHWtH8MK/img.gif?width=600&amp;amp;height=346&amp;amp;face=0_0_600_346,https://scrap.kakaocdn.net/dn/9yIaP/hyR6HTZNjM/T8ooGkoP4vigkdlbozKDj1/img.gif?width=600&amp;amp;height=346&amp;amp;face=0_0_600_346');&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;짧은 시간에 실무에서 가장 많이 사용하는 인터랙티브 스킬을 알려드립니다., - 강의 소개 | 인프런&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.inflearn.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&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;gif1.gif&quot; data-origin-width=&quot;563&quot; data-origin-height=&quot;330&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cn8kZA/btr60vf4f1l/yeW1rfTyzqNODTLt2getk0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cn8kZA/btr60vf4f1l/yeW1rfTyzqNODTLt2getk0/img.gif&quot; data-alt=&quot;미리보기!&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cn8kZA/btr60vf4f1l/yeW1rfTyzqNODTLt2getk0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/cn8kZA/btr60vf4f1l/yeW1rfTyzqNODTLt2getk0/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;450&quot; height=&quot;264&quot; data-filename=&quot;gif1.gif&quot; data-origin-width=&quot;563&quot; data-origin-height=&quot;330&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;미리보기!&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;VanillaJS로 구현해보기&lt;/b&gt;&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;HTML&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1680188806905&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=&quot;box&quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 box라는 클래스명을 가진 div를 하나 만들어준다.&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;CSS&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1680188975642&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.box {
  background-color: lightgray;
  border-radius: 50%;
  width: 80px;
  height: 80px;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 1001;
  pointer-events: none;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;position: absolute;&lt;/b&gt; 지정&lt;/li&gt;
&lt;li&gt;&lt;b&gt;top: 50%; left:50%;&lt;/b&gt; 지정: box의 오른쪽 위 모서리가 body의 가운데에 올 수 있도록 해준다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;transform: translate(-50%, -50%);&lt;/b&gt; 지정: 요소의 가운데 부분이 가운데로 올 수 있도록 한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;pointer-events: none;&lt;/b&gt; 지정: 마우스 클릭 시 box 뒤에 있는 요소를 클릭할 수 있도록 한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;z-index&lt;/b&gt;를 높게 설정해주어 다른 요소들보다 위에 올라올 수 있도록 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;원이 마우스를 따라가도록 만들기&lt;/h3&gt;
&lt;pre id=&quot;code_1680235738823&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const box = document.querySelector(&quot;.box&quot;);

window.addEventListener(&quot;mousemove&quot;, (e) =&amp;gt; {
  box.style.top = e.pageY + &quot;px&quot;;
  box.style.left = e.pageX + &quot;px&quot;;
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;box를 querySelector를 통해 가져온 후 mousemove의 event 값을 console.log로 찍어보면 pageX, clientX, offsetX, screenX 등 여러 값들이 출력되는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;111&quot; data-origin-height=&quot;327&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JxqKJ/btr7c0LVcMl/2fuGEBoA8t4VMRkOOqssf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JxqKJ/btr7c0LVcMl/2fuGEBoA8t4VMRkOOqssf1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JxqKJ/btr7c0LVcMl/2fuGEBoA8t4VMRkOOqssf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJxqKJ%2Fbtr7c0LVcMl%2F2fuGEBoA8t4VMRkOOqssf1%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;110&quot; height=&quot;324&quot; data-origin-width=&quot;111&quot; data-origin-height=&quot;327&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;page? client? offset? screen?&lt;/b&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;pageX, pageY:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;u&gt;스크롤 화면을 포함한 전체 문서&lt;/u&gt;를 기준으로 x, y좌표를 반환한다.&lt;/li&gt;
&lt;li&gt;clientX, clientY: 브라우저 화면에서의 가로, 세로 좌표를 제공한다. 이 때&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;u&gt;스크롤은 무시&lt;/u&gt;하고 해당 페이지의 상단을 0으로 측정한다. (현재 보이는 화면 상에서 어느 지점에 위치하는 지를 의미하기 때문에 스크롤 해도 값은 변하지 않음)&lt;/li&gt;
&lt;li&gt;offsetX, offsetY: 이벤트 대상 객체에서의 상대적 마우스 좌표 위치를 반환한다. (화면 중간에 있는 박스 내부에서 클릭한 위치를 찾을 때 해당 박스의 왼쪽 모서리 좌표가 0이 된다.)&lt;/li&gt;
&lt;li&gt;screenX, screenY:&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;u&gt;브라우저 화면이 아닌&lt;/u&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;모니터 화면을 기준으로 좌표를 제공한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저 화면 내에서 스크롤 화면을 포함한 전체 문서에서 따라다니도록 만들 것이기 때문에 여기서는 pageX, pageY 값을 사용해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 window에서 mousemove 이벤트가 발생할 때마다 box style의 top과 left를 해당 좌표로 이동시켜주면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;gif1.gif&quot; data-origin-width=&quot;563&quot; data-origin-height=&quot;328&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdaxrx/btr63lKXHIr/mSIKKcdFrwWcBWq1ttZ4b0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdaxrx/btr63lKXHIr/mSIKKcdFrwWcBWq1ttZ4b0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdaxrx/btr63lKXHIr/mSIKKcdFrwWcBWq1ttZ4b0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bdaxrx/btr63lKXHIr/mSIKKcdFrwWcBWq1ttZ4b0/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;400&quot; height=&quot;233&quot; data-filename=&quot;gif1.gif&quot; data-origin-width=&quot;563&quot; data-origin-height=&quot;328&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;requestAnimationFrame?&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;591&quot; data-origin-height=&quot;354&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UTqYW/btr7hu0L18U/9xLiUIGtygliflUMPVOoZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UTqYW/btr7hu0L18U/9xLiUIGtygliflUMPVOoZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UTqYW/btr7hu0L18U/9xLiUIGtygliflUMPVOoZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUTqYW%2Fbtr7hu0L18U%2F9xLiUIGtygliflUMPVOoZ0%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;299&quot; data-origin-width=&quot;591&quot; data-origin-height=&quot;354&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1680244936515&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;window.requestAnimationFrame(callback);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 구현해보기 전에 여기에서 사용할 requestAnimationFrame에 대해 알아보자.&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;자바스크립트에는 이벤트 루프가 있어 지속적으로 자바스크립트를 실행한다. 과거에는 애니메이션을 구현하기 위해 setTimeout() 또는 setInterval()을 사용했다.&lt;/p&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이 repaint 시간에 맞추지 못해 다음 사이클로 지연될 수 있었다. 그리고 이렇게 되면 애니메이션이 다음 사이클에 두 번 실행되기 때문에 자연스럽지 못한 애니메이션으로 보일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 문제점을 해결하기 위해 requestAnimationFrame이 등장했다.&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;requestAnimationFrame은 브라우저에게 수행하길 원하는 애니메이션을 알려주고 다음 repaint가 진행되기 전에 해당 애니메이션을 업데이트하는 함수를 호출한다. (애니메이션 전용 API는 아니지만 대부분 애니메이션 활용을 위해 사용된다.)&lt;/p&gt;
&lt;pre id=&quot;code_1680277969824&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let request

const performAnimation = () =&amp;gt; {
  request = requestAnimationFrame(performAnimation)
  //animate something
}

requestAnimationFrame(performAnimation)

//...

cancelAnimationFrame(request) //stop the animation&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 사용할 수 있다. 이는 setTimeout, setInterval을 이용한 애니메이션 구현과 비슷해보이지만 매우 다르게 동작한다.&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;Timeline&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 그림은 setTimeout이나 setInterval을 사용했을 때의 타임라인이다. 보라색은 render, 초록색은 paint, 노란색은 setTimeout/setInterval 코드의 실행을 의미한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1152&quot; data-origin-height=&quot;376&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cI4WT1/btr7dXKvltX/NJTBuDnSfeek1OmOR1Ku30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cI4WT1/btr7dXKvltX/NJTBuDnSfeek1OmOR1Ku30/img.png&quot; data-alt=&quot;https://flaviocopes.com/requestanimationframe/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cI4WT1/btr7dXKvltX/NJTBuDnSfeek1OmOR1Ku30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcI4WT1%2Fbtr7dXKvltX%2FNJTBuDnSfeek1OmOR1Ku30%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;1152&quot; height=&quot;376&quot; data-origin-width=&quot;1152&quot; data-origin-height=&quot;376&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://flaviocopes.com/requestanimationframe/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;setTimeout, setInterval은 paint, render가 일어난 후에 일어난다. 위의 그림에서는 하나의 프레임에 한 번만 사용되었기 때문에 정상적으로 동작한다.&lt;/p&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1148&quot; data-origin-height=&quot;375&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqobvt/btr7dWkxV5d/u877c5UaCMkIzJJ5sLUog1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqobvt/btr7dWkxV5d/u877c5UaCMkIzJJ5sLUog1/img.png&quot; data-alt=&quot;https://flaviocopes.com/requestanimationframe/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqobvt/btr7dWkxV5d/u877c5UaCMkIzJJ5sLUog1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcqobvt%2Fbtr7dWkxV5d%2Fu877c5UaCMkIzJJ5sLUog1%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;1148&quot; height=&quot;375&quot; data-origin-width=&quot;1148&quot; data-origin-height=&quot;375&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://flaviocopes.com/requestanimationframe/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 애니메이션 호출이 지연될 경우, 이벤트 루프의 코드 블로킹 때문에 특정 프레임에서 setTimeout(setInterval)이 실행되지 않는 경우가 생길 수 있다. 그리고 이 경우 애니메이션이 끊기는 현상이 발생한다.&lt;/p&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1148&quot; data-origin-height=&quot;370&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oZk6M/btr7ghOrxG3/7UHxc9PV4fu2D2by7zazOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oZk6M/btr7ghOrxG3/7UHxc9PV4fu2D2by7zazOk/img.png&quot; data-alt=&quot;https://flaviocopes.com/requestanimationframe/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oZk6M/btr7ghOrxG3/7UHxc9PV4fu2D2by7zazOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoZk6M%2Fbtr7ghOrxG3%2F7UHxc9PV4fu2D2by7zazOk%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;1148&quot; height=&quot;370&quot; data-origin-width=&quot;1148&quot; data-origin-height=&quot;370&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://flaviocopes.com/requestanimationframe/&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;&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;requestAnimationFrame은 아래와 같이 동작한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1159&quot; data-origin-height=&quot;369&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/F6Gjs/btr7g8ReUpU/OAbsKgWreVJXcJxbtl8jUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/F6Gjs/btr7g8ReUpU/OAbsKgWreVJXcJxbtl8jUk/img.png&quot; data-alt=&quot;보라색: render, 초록색: paint, 노란색: requestAnimationFrame&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/F6Gjs/btr7g8ReUpU/OAbsKgWreVJXcJxbtl8jUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FF6Gjs%2Fbtr7g8ReUpU%2FOAbsKgWreVJXcJxbtl8jUk%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;1159&quot; height=&quot;369&quot; data-origin-width=&quot;1159&quot; data-origin-height=&quot;369&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;보라색: render, 초록색: paint, 노란색: requestAnimationFrame&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 requestAnimationFrame의 애니메이션은 항상 렌더링과 페인트 이전에 실행되기 때문에 조금 더 예측 가능한 애니메이션을 만들 수 있다. 그리고 이벤트 루프의 지연 및 블로킹 현상이 나타나지 않아 setTimeout, setInterval에 비해 더 부드러운 애니메이션을 제공한다.&lt;/p&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, setInterval은 탭이 보이지 않는 상태에서도 계속 돌아간다. 하지만 requestAnimationFrame은 cpu에 매우 친화적이기 때문에 현재 창이나 탭이 보이지 않으면 애니메이션이 중지된다. 이는 cpu 전력 소모를 줄일 수 있고, 배터리 절약에도 성공적이기 때문에 requestAnimationFrame을 사용하면 브라우저는 리소스 사용을 최적화하고 애니메이션을 더 부드럽게 만들 수 있다.&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;pre id=&quot;code_1680190700176&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const box = document.querySelector(&quot;.box&quot;);

let x = 0;
let y = 0;
let targetX = 0;
let targetY = 0;
const speed = 0.03; // 따라오는 속도

window.addEventListener(&quot;mousemove&quot;, (e) =&amp;gt; {
  x = e.pageX;
  y = e.pageY;
});

const loop = () =&amp;gt; {
  // 애니메이션의 가속, 감속
  targetX += (x - targetX) * speed;
  targetY += (y - targetY) * speed;

  box.style.top = targetY + &quot;px&quot;;
  box.style.left = targetX + &quot;px&quot;;
  
  // 재귀적으로 호출
  window.requestAnimationFrame(loop);
};

loop();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mousemove 이벤트가 발생할 때는 마우스의 x, y 변수만 바꿔주고, requestAnimationFrame에서 바뀐 x, y로 이동을 시켜주는 식으로 코드를 짜주면 된다. (이 때 mousemove 함수 안에서 requestAnimationFrame을 사용하지 않도록 주의하자. 이렇게 되면 함수가 계속 중첩되어 실행된다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;React로 구현해보기&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React 프로젝트에도 한번 적용해보고 싶어서 만들어봤다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;routes/index.tsx&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1680330540504&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useRef } from 'react'

const App = () =&amp;gt; {
  const mouseRef = useRef&amp;lt;HTMLDivElement&amp;gt;(null)

  return(
    &amp;lt;div className={styles.pageContainer}&amp;gt;
      &amp;lt;div className={styles.mouseMoveDiv} ref={mouseRef} /&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}

export default App&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;div를 만들고, 마우스의 움직임에 따라 이 div를 직접 움직여줄 것이기 때문에 ref를 걸어준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 기본적인 스타일을 div에 적용해준다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;routes.module.scss&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1680330832563&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.mouseMoveDiv {
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 1001;
  width: 50px;
  height: 50px;
  pointer-events: none;
  background-color: #000000;
  border-radius: 50%;
  opacity: 0.2;
  transition: display 1000ms ease-in-out;
  transform: translate(-50%, -50%);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;routes/index.tsx&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1680330980432&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let x = 0
let y = 0
let targetX = 0
let targetY = 0
const speed = 0.05

const App = () =&amp;gt; {
  const mouseRef = useRef&amp;lt;HTMLDivElement&amp;gt;(null)
  const requestRef = useRef(0)

  const handleMouseMove: MouseEventHandler = (e) =&amp;gt; {
    x = e.pageX
    y = e.pageY
  }

  const loop = useCallback(() =&amp;gt; {
    targetX += (x - targetX) * speed
    targetY += (y - targetY) * speed

    if (mouseRef.current) {
      mouseRef.current.style.top = `${targetY}px`
      mouseRef.current.style.left = `${targetX}px`
    }

    requestRef.current = requestAnimationFrame(loop)
  }, [])

  useEffect(() =&amp;gt; {
    requestRef.current = requestAnimationFrame(loop)

    return () =&amp;gt; cancelAnimationFrame(requestRef.current)
  }, [loop])
  
  return (
    &amp;lt;div className={styles.pageContainer} onMouseMove={handleMouseMove}&amp;gt;
      &amp;lt;div className={styles.mouseMoveDiv} ref={mouseRef} /&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적인 구현은 바닐라js에서 만든 방식과 비슷하지만 리액트에서는 requestAnimationFrame을 useEffect안에서 실행해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://css-tricks.com/using-requestanimationframe-with-react-hooks/&quot;&gt;https://css-tricks.com/using-requestanimationframe-with-react-hooks/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1680331482648&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;Using requestAnimationFrame with React Hooks | CSS-Tricks&quot; data-og-description=&quot;Animating with requestAnimationFrame should be easy, but if you haven&amp;rsquo;t read React&amp;rsquo;s documentation thoroughly then you will probably run into a few things&quot; data-og-host=&quot;css-tricks.com&quot; data-og-source-url=&quot;https://css-tricks.com/using-requestanimationframe-with-react-hooks/&quot; data-og-url=&quot;https://css-tricks.com/using-requestanimationframe-with-react-hooks/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cnieVj/hyR6FQQlZl/7c1pOOkokWf1Lio8gNa4t0/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/cJZKwm/hyR7Hfa3HE/s26zjKq8kP38k3VeNaRu31/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/mJUKW/hyR6KxNcHP/rzqS9P9vvVckLU3ZUk4rAK/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://css-tricks.com/using-requestanimationframe-with-react-hooks/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://css-tricks.com/using-requestanimationframe-with-react-hooks/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cnieVj/hyR6FQQlZl/7c1pOOkokWf1Lio8gNa4t0/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/cJZKwm/hyR7Hfa3HE/s26zjKq8kP38k3VeNaRu31/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/mJUKW/hyR6KxNcHP/rzqS9P9vvVckLU3ZUk4rAK/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&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;Using requestAnimationFrame with React Hooks | CSS-Tricks&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Animating with requestAnimationFrame should be easy, but if you haven&amp;rsquo;t read React&amp;rsquo;s documentation thoroughly then you will probably run into a few things&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;css-tricks.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;h3 data-ke-size=&quot;size23&quot;&gt;추가적인 효과&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하지만 추가적으로 마우스를 눌렀을 때, 마우스가 브라우저 밖으로 나갔을 때 이벤트를 추가해주었다.&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;routes/index.tsx&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1680331841188&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const App = () =&amp;gt; {
  // ...
  
  // 마우스 누르고 있을 때 div 색 변경
  const handleMouseDown = () =&amp;gt; {
    if (mouseRef.current) {
      mouseRef.current.style.backgroundColor = '#ea3a4b'
    }
  }

  // 마우스를 눌렀다가 뗐을 때 다시 원래 색으로 변경
  const handleMouseUp = () =&amp;gt; {
    if (mouseRef.current) {
      mouseRef.current.style.backgroundColor = '#000000'
    }
  }

  // 마우스가 브라우저 안에 들어올 때
  const handleMouseEnter = () =&amp;gt; {
    if (mouseRef.current) {
      mouseRef.current.style.display = 'block'
    }
  }

  // 마우스가 브라우저 밖으로 나갈 때 div의 display를 none으로 변경
  const handleMouseLeave = () =&amp;gt; {
    if (mouseRef.current) {
      mouseRef.current.style.display = 'none'
    }
  }
  
  return (
    &amp;lt;div
      className={styles.pageContainer}
      onMouseMove={handleMouseMove}
      onMouseDown={handleMouseDown}
      onMouseUp={handleMouseUp}
      onMouseEnter={handleMouseEnter}
      onMouseLeave={handleMouseLeave}
    &amp;gt;
      &amp;lt;div className={styles.mouseMoveDiv} ref={mouseRef} /&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSS-in-JS를 사용하고 있다면 직접 style을 설정해줄 수 있었겠지만, scss에서는 따로 props를 넘길 수 없기 때문에 그냥 ref에서 직접 스타일을 변경해주었다.&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;pre id=&quot;code_1680332010914&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let x = 0
let y = 0
let targetX = 0
let targetY = 0
const speed = 0.05

const App = () =&amp;gt; {
  const mouseRef = useRef&amp;lt;HTMLDivElement&amp;gt;(null)
  const requestRef = useRef(0)

  const handleMouseMove: MouseEventHandler = (e) =&amp;gt; {
    x = e.pageX
    y = e.pageY
  }

  const loop = useCallback(() =&amp;gt; {
    targetX += (x - targetX) * speed
    targetY += (y - targetY) * speed

    if (mouseRef.current) {
      mouseRef.current.style.top = `${targetY}px`
      mouseRef.current.style.left = `${targetX}px`
    }

    requestRef.current = requestAnimationFrame(loop)
  }, [])

  useEffect(() =&amp;gt; {
    requestRef.current = requestAnimationFrame(loop)

    return () =&amp;gt; cancelAnimationFrame(requestRef.current)
  }, [loop])

  const handleMouseDown = () =&amp;gt; {
    if (mouseRef.current) {
      mouseRef.current.style.backgroundColor = '#ea3a4b'
    }
  }

  const handleMouseUp = () =&amp;gt; {
    if (mouseRef.current) {
      mouseRef.current.style.backgroundColor = '#000000'
    }
  }

  const handleMouseEnter = () =&amp;gt; {
    if (mouseRef.current) {
      mouseRef.current.style.display = 'block'
    }
  }

  const handleMouseLeave = () =&amp;gt; {
    if (mouseRef.current) {
      mouseRef.current.style.display = 'none'
    }
  }
  
  return (
    &amp;lt;div
      className={styles.pageContainer}
      onMouseMove={handleMouseMove}
      onMouseDown={handleMouseDown}
      onMouseUp={handleMouseUp}
      onMouseEnter={handleMouseEnter}
      onMouseLeave={handleMouseLeave}
    &amp;gt;
      &amp;lt;div className={styles.mouseMoveDiv} ref={mouseRef} /&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;References&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;http://megaton111.cafe24.com/2016/11/29/clientx-offsetx-pagex-screenx%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;http://megaton111.cafe24.com/2016/11/29/clientx-offsetx-pagex-screenx%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/API/window/requestAnimationFrame&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.mozilla.org/ko/docs/Web/API/window/requestAnimationFrame&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://flaviocopes.com/requestanimationframe/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://flaviocopes.com/requestanimationframe/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@khy226/requestAnimationFrame으로-자연스러운-애니메이션-만들기-feat-react-spring&quot;&gt;https://velog.io/@khy226/requestAnimationFrame으로-자연스러운-애니메이션-만들기-feat-react-spring&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.inflearn.com/questions/485554/requestanimationframe-%EC%9D%B4%ED%95%B4%EA%B0%80-%EC%9E%98-%EC%95%88%EA%B0%80%EC%84%9C-%EA%B7%B8%EB%9F%AC%EB%8A%94%EB%8D%B0%EC%9A%94&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.inflearn.com/questions/485554/requestanimationframe-%EC%9D%B4%ED%95%B4%EA%B0%80-%EC%9E%98-%EC%95%88%EA%B0%80%EC%84%9C-%EA%B7%B8%EB%9F%AC%EB%8A%94%EB%8D%B0%EC%9A%94&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;&amp;nbsp;&lt;/p&gt;</description>
      <category>FE</category>
      <category>JavaScript</category>
      <category>react</category>
      <category>requestAnimationFrame</category>
      <category>TypeScript</category>
      <category>리액트</category>
      <category>바닐라JS</category>
      <author>SH_Roh</author>
      <guid isPermaLink="true">https://doooodle932.tistory.com/172</guid>
      <comments>https://doooodle932.tistory.com/172#entry172comment</comments>
      <pubDate>Sat, 1 Apr 2023 15:55:20 +0900</pubDate>
    </item>
    <item>
      <title>[TIL] 23.03.15 TS 핸드북 - Typeof Type Operator</title>
      <link>https://doooodle932.tistory.com/170</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.typescriptlang.org/ko/docs/handbook/2/typeof-types.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.typescriptlang.org/ko/docs/handbook/2/typeof-types.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1678984097826&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;Documentation - Typeof Type Operator&quot; data-og-description=&quot;타입 컨텍스트에서 typeof 연산자 사용하기.&quot; data-og-host=&quot;www.typescriptlang.org&quot; data-og-source-url=&quot;https://www.typescriptlang.org/ko/docs/handbook/2/typeof-types.html&quot; data-og-url=&quot;https://www.typescriptlang.org/ko/docs/handbook/2/typeof-types.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.typescriptlang.org/ko/docs/handbook/2/typeof-types.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.typescriptlang.org/ko/docs/handbook/2/typeof-types.html&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;Documentation - Typeof Type Operator&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;타입 컨텍스트에서 typeof 연산자 사용하기.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.typescriptlang.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;typeof 타입 연산자&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트에 이미 typeof 연산자가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/typeof&quot;&gt;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/typeof&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1678984169569&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;typeof - JavaScript | MDN&quot; data-og-description=&quot;typeof 연산자는 피연산자의 평가 전 자료형을 나타내는 문자열을 반환합니다.&quot; data-og-host=&quot;developer.mozilla.org&quot; data-og-source-url=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/typeof&quot; data-og-url=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/typeof&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/MF0Yj/hyRWo9uSjj/qXGU8hsfAsFciK7E6YR5Kk/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/typeof&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/typeof&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/MF0Yj/hyRWo9uSjj/qXGU8hsfAsFciK7E6YR5Kk/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080');&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;typeof - JavaScript | MDN&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;typeof 연산자는 피연산자의 평가 전 자료형을 나타내는 문자열을 반환합니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.mozilla.org&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;타입스크립트는 타입 컨텍스트에서 변수나 프로퍼티의 타입을 추론할 수 있는 typeof 연산자를 추가한다.&lt;/p&gt;
&lt;pre id=&quot;code_1678984203005&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let s = 'hello';
let n: typeof s; // let n: string&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;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 미리 정의된 &lt;u&gt;&lt;a href=&quot;https://www.typescriptlang.org/ko/docs/handbook/utility-types.html#returntypetype&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;유틸리티 타입인 ReturnType&amp;lt;T&amp;gt;&lt;/a&gt;&lt;/u&gt;부터 살펴보면, 해당 타입은 함수 타입을 받으면서 반환되는 타입을 리턴한다.&lt;/p&gt;
&lt;pre id=&quot;code_1678984735981&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type Predicate = (x: unknown) =&amp;gt; boolean;
type K = ReturnType&amp;lt;Predicate&amp;gt;; // type K = boolean&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;ReturnType은 함수 &lt;u&gt;타입&lt;/u&gt;을 받기 때문에 함수 이름을 넣으면, 오류가 발생한다.&lt;/p&gt;
&lt;pre id=&quot;code_1678984838389&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function f() {
  return { x: 10, y: 3 };
}

// ERROR: 'f' refers to a value, but is being used as a type here. Did you mean 'typeof f'?
type P = ReturnType&amp;lt;f&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;따라서 값과 타입은 같지 않다는 것을 명심하자. 값 f의 타입을 추론하기 위해 typeof를 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1678984898085&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type P = ReturnType&amp;lt;typeof f&amp;gt;; // type P = { x: number; y: number; }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>TIL</category>
      <category>TS</category>
      <category>typeof</category>
      <category>TypeScript</category>
      <category>타입스크립트</category>
      <category>타입스크립트 핸드북</category>
      <author>SH_Roh</author>
      <guid isPermaLink="true">https://doooodle932.tistory.com/170</guid>
      <comments>https://doooodle932.tistory.com/170#entry170comment</comments>
      <pubDate>Thu, 16 Mar 2023 01:23:09 +0900</pubDate>
    </item>
    <item>
      <title>[TIL] 23.03.13 타입스크립트에서 효율적으로 상수 관리하기</title>
      <link>https://doooodle932.tistory.com/168</link>
      <description>&lt;h2 style=&quot;color: #000000;&quot; data-ke-size=&quot;size26&quot;&gt;효율적으로 상수 관리하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입스크립트에서는 포괄적인 타입(string, number, ...) 외에도 정확한 값을 타입으로 설정하는 것이 가능하다.&lt;/p&gt;
&lt;pre id=&quot;code_1678706693524&quot; class=&quot;typescript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let title: 'typescript';
title = 'javascript'; // Type 'javascript' is not assignable to type 'typescript'&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;타입스크립트에서는 컴파일러가 자동으로 타입을 추론하는 기능을 제공한다. let은 변수의 값을 언제든 바꿀 수 있기 때문에 더 포괄적인 타입으로 값을 추론하고, const의 경우는 변경이 불가능하기 때문에 리터럴 타입으로도 추론된다.&lt;/p&gt;
&lt;pre id=&quot;code_1678706693525&quot; class=&quot;typescript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let title = 'typescript'; // let title: string
const content = 'typescript; // const content: 'typescript'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000;&quot; data-ke-size=&quot;size23&quot;&gt;타입 단언(Type Assertion)을 활용한 상수 관리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;let 변수의 경우에도 const처럼 리터럴 타입으로 추론해줄 수 있는데, 그 때 사용하는 것이 as const이다.&lt;/p&gt;
&lt;pre id=&quot;code_1678706693526&quot; class=&quot;typescript&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let title = 'typescript' as const; // let title: 'typescript'&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_1678786452557&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const Colors = {
  red: '#FF0000',
  blue: '#0000FF',
  green: '#008000'
}&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;278&quot; data-origin-height=&quot;158&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQiid9/btr3IYZ8IN3/y1pGiOsJVCsgYGroOIcuvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQiid9/btr3IYZ8IN3/y1pGiOsJVCsgYGroOIcuvK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQiid9/btr3IYZ8IN3/y1pGiOsJVCsgYGroOIcuvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQiid9%2Fbtr3IYZ8IN3%2Fy1pGiOsJVCsgYGroOIcuvK%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;278&quot; height=&quot;158&quot; data-origin-width=&quot;278&quot; data-origin-height=&quot;158&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Colors 변수 내부에 추론된 값을 보면 각 속성이 원시 타입으로 추론된다. 이는 const로 객체를 선언했지만, 객체 내부의 값들은 언제든지 바꿀 수 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 타입 단언을 통해 Colors 내부값의 타입을 리터럴 타입으로 변경할 수 있고, 편리하게 상수를 관리할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1678788438598&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const Colors = {
  red: '#FF0000',
  blue: '#0000FF',
  green: '#008000'
} as const&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;394&quot; data-origin-height=&quot;160&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ztLV3/btr3QvXeDja/2qMeNM0NEQ8VA6QbCRFOIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ztLV3/btr3QvXeDja/2qMeNM0NEQ8VA6QbCRFOIK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ztLV3/btr3QvXeDja/2qMeNM0NEQ8VA6QbCRFOIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FztLV3%2Fbtr3QvXeDja%2F2qMeNM0NEQ8VA6QbCRFOIK%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;394&quot; height=&quot;160&quot; data-origin-width=&quot;394&quot; data-origin-height=&quot;160&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;enum을 통한 상수 관리&lt;/h3&gt;
&lt;pre id=&quot;code_1678788512818&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export enum Colors {   
    red=&quot;#FF0000&quot;,   
    blue=&quot;#0000FF&quot;,   
    green=&quot;#008000&quot;, 
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;타입스크립트에서는 enum이라는 객체를 통해 상수를 쉽게 관리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;enum은 타입스크립트에서 제공하는 문법을 아래와 같은 특징을 가진다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;enum은 js 객체이나, 내부 속성을 임의로 변경 불가&lt;/li&gt;
&lt;li&gt;enum의 내부 key는 반드시 리터럴 타입(string or number)으로만 사용 가능&lt;/li&gt;
&lt;li&gt;enum의 속성값도 리터럴 타입(string or number)만 사용 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;enum vs as const&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 상수 관리에 enum, as const 중에 어떤 것을 쓰는 것이 더 좋을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;enum과 as const는 탄생한 목적 자체가 다르다. &lt;b&gt;enum&lt;/b&gt;은 &lt;u&gt;서로 연관된 상수들을 하나의 네임스페이스로 묶어 추상화하기 위해&lt;/u&gt; 도입된 것이다. 이를 통해 코드만 보더라도 의도를 명확히 알 수 있어 가독성을 높일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 &lt;b&gt;as const&lt;/b&gt;는 타입 단언의 한 종류로써 &lt;u&gt;리터럴 타입의 추론 범위를 줄이고 값의 재할당을 막기 위한 목적&lt;/u&gt;으로 만들어졌다.&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;Object(or Array) 리터럴의 as const&lt;/h3&gt;
&lt;pre id=&quot;code_1678788754840&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let title = 'typescript' as const&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;as const를 위와 같이 쓸 수도 있지만 그럴 바에는 아예 const로 선언하는 게 좋을 것이다.&lt;/p&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나 Array 타입이라면 얘기가 달라진다. 이 둘은 참조 타입이기 때문에 const로 선언해도 내부 프로퍼티의 추론 범위가 한정되지도 않고, 값을 변경할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1678788876951&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const language = {
  korean: &quot;ko&quot;,						// &quot;ko&quot;로 추론, readonly 프로퍼티로 변경
  english: &quot;en&quot;,					// &quot;en&quot;로 추론, readonly 프로퍼티로 변경
} as const;

// Error: Cannot assign to 'korean' because it is a read-only property
language.korean = &quot;kkk&quot;;
// Error: Cannot assign to 'english' because it is a read-only property.
language.english = &quot;eee&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이럴 때 as const를 사용하면 객체의 모든 프로퍼티들이 readonly로 변경되고, 각 프로퍼티의 타입이 할당된 리터럴 값으로 추론된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중첩 객체(배열)의 프로퍼티들도 as const를 사용하면 readonly로 변경되고 리터럴 값으로 추론된다.&lt;/p&gt;
&lt;pre id=&quot;code_1678789429759&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const obj = {
  a: 10,
  b: [20, 30],
  c: {
    d: {
      e: {
        greeting: &quot;Hello&quot;,
      },
    },
  },
} as const;&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;453&quot; data-origin-height=&quot;267&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bB7qVw/btr3Qx8EJuj/DpGgz9NWQDTgz2z3EdxdJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bB7qVw/btr3Qx8EJuj/DpGgz9NWQDTgz2z3EdxdJk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bB7qVw/btr3Qx8EJuj/DpGgz9NWQDTgz2z3EdxdJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbB7qVw%2Fbtr3Qx8EJuj%2FDpGgz9NWQDTgz2z3EdxdJk%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;453&quot; height=&quot;267&quot; data-origin-width=&quot;453&quot; data-origin-height=&quot;267&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇듯 as const를 사용하면 원시 타입이든 참조 타입이든 값의 재할당을 막기 때문에 의도치않은 변경으로 인한 오류를 없앨 수 있다. 또한, 리터럴 타입의 추론 범위가 리터럴 값 자체로 한정되면서 좀 더 안전하게 코드를 작성할 수 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;References&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://blog.toycrane.xyz/typescript에서-효과적으로-상수-관리하기-e926db079f9&quot;&gt;https://blog.toycrane.xyz/typescript에서-효과적으로-상수-관리하기-e926db079f9&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;a href=&quot;https://velog.io/@logqwerty/Enum-vs-as-const&quot;&gt;https://velog.io/@logqwerty/Enum-vs-as-const&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>TIL</category>
      <category>as const</category>
      <category>Enum</category>
      <category>TS</category>
      <category>TypeScript</category>
      <category>타입 단언</category>
      <category>타입스크립트</category>
      <author>SH_Roh</author>
      <guid isPermaLink="true">https://doooodle932.tistory.com/168</guid>
      <comments>https://doooodle932.tistory.com/168#entry168comment</comments>
      <pubDate>Tue, 14 Mar 2023 02:14:24 +0900</pubDate>
    </item>
  </channel>
</rss>