<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>taek2-0310 님의 블로그</title>
    <link>https://taek2-0310.tistory.com/</link>
    <description>taek2-0310 님의 블로그 입니다.</description>
    <language>ko</language>
    <pubDate>Tue, 2 Jun 2026 15:06:56 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>taek2-0310</managingEditor>
    <item>
      <title>브라우저 렌더링 과정과 DOM</title>
      <link>https://taek2-0310.tistory.com/13</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 브라우저 렌더링 과정 개요&lt;/h2&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;css&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;[요청] &amp;rarr; [HTML 파싱 &amp;rarr; DOM] &amp;rarr; [CSS 파싱 &amp;rarr; CSSOM]
       &amp;rarr; [렌더 트리 생성] &amp;rarr; [레이아웃] &amp;rarr; [페인트]&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;단계설명
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1. 요청/응답&lt;/td&gt;
&lt;td&gt;DNS를 통해 IP 주소를 확인하고 서버에 리소스 요청&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2. DOM 생성&lt;/td&gt;
&lt;td&gt;바이트 &amp;rarr; 문자 &amp;rarr; 토큰 &amp;rarr; 노드 &amp;rarr; DOM 트리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3. CSSOM 생성&lt;/td&gt;
&lt;td&gt;CSS 파일을 동일한 파싱 과정으로 CSSOM 트리 생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4. JS 실행&lt;/td&gt;
&lt;td&gt;자바스크립트 엔진이 AST 생성 &amp;rarr; 바이트코드 실행&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5. 렌더 트리&lt;/td&gt;
&lt;td&gt;DOM + CSSOM 결합 (화면에 표시되는 노드만 포함)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6. 레이아웃&lt;/td&gt;
&lt;td&gt;각 요소의 위치와 크기 계산&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7. 페인트&lt;/td&gt;
&lt;td&gt;픽셀로 화면에 그리기&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 요청과 응답&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저 주소창에 URL을 입력하면:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;URL의 &lt;b&gt;호스트 이름&lt;/b&gt;이 DNS를 통해 &lt;b&gt;IP 주소&lt;/b&gt;로 변환된다.&lt;/li&gt;
&lt;li&gt;해당 IP 주소의 서버에 &lt;b&gt;GET 요청&lt;/b&gt;을 전송한다.&lt;/li&gt;
&lt;li&gt;루트 요청(/)의 경우 서버는 기본적으로 index.html을 응답한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;awk&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;https://www.example.com:80/docs/search?category=js#intro
   │           │          │      │           │        │
 scheme      host        port   path        query  fragment&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;핵심&lt;/b&gt;&lt;span style=&quot;color: #666666; text-align: start;&quot;&gt;: index.html을 파싱하는 중 &amp;lt;link&amp;gt;, &amp;lt;img&amp;gt;, &amp;lt;script&amp;gt; 태그를 만나면 해당 리소스를 서버에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;추가로 요청&lt;/b&gt;&lt;span style=&quot;color: #666666; text-align: start;&quot;&gt;한다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. HTTP 1.1 vs HTTP 2.0&lt;/h2&gt;
&lt;div&gt;구분HTTP/1.1HTTP/2.0
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;요청/응답&lt;/td&gt;
&lt;td&gt;커넥션당 &lt;b&gt;하나씩&lt;/b&gt; 처리&lt;/td&gt;
&lt;td&gt;커넥션당 &lt;b&gt;다중&lt;/b&gt; 처리 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;전송 방식&lt;/td&gt;
&lt;td&gt;순차적 (개별 전송)&lt;/td&gt;
&lt;td&gt;동시 전송&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;속도&lt;/td&gt;
&lt;td&gt;느림&lt;/td&gt;
&lt;td&gt;약 50% 빠름&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. HTML 파싱과 DOM 생성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버에서 HTML을 바이트로 응답받아 다음 과정을 거쳐 &lt;b&gt;DOM 트리&lt;/b&gt;를 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바이트(2진수)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;darr;&amp;nbsp;(meta&amp;nbsp;charset&amp;nbsp;기준&amp;nbsp;디코딩)&lt;br /&gt;문자열&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;darr;&amp;nbsp;(어휘&amp;nbsp;분석&amp;nbsp;&amp;middot;&amp;nbsp;Lexical&amp;nbsp;Analysis)&lt;br /&gt;토큰&amp;nbsp;(문법적&amp;nbsp;의미를&amp;nbsp;가지는&amp;nbsp;코드의&amp;nbsp;최소&amp;nbsp;단위)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;darr;&lt;br /&gt;노드&amp;nbsp;(문서&amp;nbsp;노드&amp;nbsp;/&amp;nbsp;요소&amp;nbsp;노드&amp;nbsp;/&amp;nbsp;어트리뷰트&amp;nbsp;노드&amp;nbsp;/&amp;nbsp;텍스트&amp;nbsp;노드)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;darr;&lt;br /&gt;DOM&amp;nbsp;트리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DOM 트리 예시&lt;/p&gt;
&lt;pre id=&quot;code_1780285684216&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;link rel=&quot;stylesheet&quot; href=&quot;style.css&quot;&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;ul&amp;gt;
      &amp;lt;li id=&quot;apple&quot;&amp;gt;Apple&amp;lt;/li&amp;gt;
      &amp;lt;li id=&quot;banana&quot;&amp;gt;Banana&amp;lt;/li&amp;gt;
    &amp;lt;/ul&amp;gt;
    &amp;lt;script src=&quot;app.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1780285700266&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;document
  └── html
        ├── head
        │     ├── meta [charset=&quot;UTF-8&quot;]
        │     └── link [rel=&quot;stylesheet&quot; href=&quot;style.css&quot;]
        └── body
              ├── ul
              │    ├── li [id=&quot;apple&quot;] &amp;rarr; &quot;Apple&quot;
              │    └── li [id=&quot;banana&quot;] &amp;rarr; &quot;Banana&quot;
              └── script [src=&quot;app.js&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. CSS 파싱과 CSSOM 생성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;렌더링 엔진이 &amp;lt;link&amp;gt; 또는 &amp;lt;style&amp;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;DOM 생성을 일시 중단&lt;/b&gt;한다.&lt;/li&gt;
&lt;li&gt;CSS 파일을 서버에 요청한다.&lt;/li&gt;
&lt;li&gt;HTML과 동일한 파싱 과정(바이트 &amp;rarr; 문자 &amp;rarr; 토큰 &amp;rarr; 노드)을 거쳐 &lt;b&gt;CSSOM&lt;/b&gt; 트리를 생성한다.&lt;/li&gt;
&lt;li&gt;CSS 파싱 완료 후 &lt;b&gt;HTML 파싱을 재개&lt;/b&gt;한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. 렌더 트리 생성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DOM + CSSOM을 결합하여 &lt;b&gt;렌더 트리&lt;/b&gt;를 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;렌더 트리에서 &lt;b&gt;제외&lt;/b&gt;되는 노드:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;lt;meta&amp;gt;, &amp;lt;script&amp;gt;, &amp;lt;head&amp;gt; 같은 화면에 표시되지 않는 노드&lt;/li&gt;
&lt;li&gt;CSS display: none이 적용된 노드&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666; text-align: start;&quot;&gt;visibility: hidden은 공간은 차지하지만 보이지 않으므로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;렌더 트리에 포함&lt;/b&gt;&lt;span style=&quot;color: #666666; text-align: start;&quot;&gt;된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;렌더 트리 완성 후:&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;레이아웃(Layout)&lt;/b&gt;: 각 요소의 위치(x, y)와 크기(width, height) 계산&lt;/li&gt;
&lt;li&gt;&lt;b&gt;페인트(Paint)&lt;/b&gt;: 계산된 정보로 픽셀을 화면에 그리기&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 자바스크립트 파싱과 실행&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML 파싱 중 &amp;lt;script&amp;gt; 태그를 만나면:&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;DOM 생성을 &lt;b&gt;일시 중단&lt;/b&gt;한다.&lt;/li&gt;
&lt;li&gt;자바스크립트 파일을 서버에 요청한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;자바스크립트 엔진&lt;/b&gt;에 제어권을 넘긴다.&lt;/li&gt;
&lt;li&gt;JS 엔진이 코드를 파싱하여 **AST(Abstract Syntax Tree)**를 생성한다.&lt;/li&gt;
&lt;li&gt;AST를 기반으로 &lt;b&gt;바이트코드&lt;/b&gt;를 생성하고 실행한다.&lt;/li&gt;
&lt;li&gt;실행 완료 후 &lt;b&gt;렌더링 엔진으로 제어권 반환&lt;/b&gt; &amp;rarr; HTML 파싱 재개&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. 리플로우와 리페인트&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트에서 DOM API로 DOM이나 CSSOM을 변경하면 렌더 트리가 재생성되며 다음이 발생한다.&lt;/p&gt;
&lt;div&gt;구분설명발생 조건
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;리플로우(Reflow)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;레이아웃 계산을 다시 수행&lt;/td&gt;
&lt;td&gt;요소 추가/삭제, 크기/위치 변경, 창 리사이징&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;리페인트(Repaint)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;재결합된 렌더 트리로 다시 페인트&lt;/td&gt;
&lt;td&gt;리플로우 이후, 또는 색상 등 레이아웃 영향 없는 변경&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;중요&lt;/b&gt;&lt;span style=&quot;color: #666666; text-align: start;&quot;&gt;: 레이아웃에 영향을 주지 않는 변경(색상, 투명도 등)은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;리페인트만&lt;/b&gt;&lt;span style=&quot;color: #666666; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;발생한다. 리플로우 + 리페인트는 비용이 크므로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;가급적 최소화&lt;/b&gt;&lt;span style=&quot;color: #666666; text-align: start;&quot;&gt;해야 한다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9. script 태그 위치와 async/defer&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문제: &amp;lt;head&amp;gt; 안에 &amp;lt;script&amp;gt; 배치 시&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;xml&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;&amp;lt;head&amp;gt;
  &amp;lt;script src=&quot;app.js&quot;&amp;gt;&amp;lt;/script&amp;gt;  &amp;lt;!-- DOM이 완성되기 전에 실행! --&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
  &amp;lt;div id=&quot;app&quot;&amp;gt;...&amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;pre class=&quot;mel&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;// app.js
const $app = document.getElementById('app'); // null &amp;rarr; 에러 발생!
$app.style.color = 'red'; // TypeError&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;해결책 1: &amp;lt;/body&amp;gt; 직전에 배치&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;xml&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;&amp;lt;body&amp;gt;
  &amp;lt;div id=&quot;app&quot;&amp;gt;...&amp;lt;/div&amp;gt;
  &amp;lt;script src=&quot;app.js&quot;&amp;gt;&amp;lt;/script&amp;gt;  &amp;lt;!-- DOM 완성 후 실행 --&amp;gt;
&amp;lt;/body&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;해결책 2: async / defer 어트리뷰트&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;xml&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;&amp;lt;script async src=&quot;app.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script defer src=&quot;app.js&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;구분HTML 파싱JS 로드JS 실행 시점
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;기본&lt;/td&gt;
&lt;td&gt;&lt;b&gt;중단&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;순차&lt;/td&gt;
&lt;td&gt;로드 즉시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;async&lt;/td&gt;
&lt;td&gt;&lt;b&gt;병렬&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;병렬&lt;/td&gt;
&lt;td&gt;로드 완료 즉시 (순서 보장 X)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;defer&lt;/td&gt;
&lt;td&gt;&lt;b&gt;병렬&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;병렬&lt;/td&gt;
&lt;td&gt;HTML 파싱 완료 후 (순서 보장 O)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;권장&lt;/b&gt;&lt;span style=&quot;color: #666666; text-align: start;&quot;&gt;: DOM 조작이 필요한 스크립트는 defer 사용&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10. DOM이란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;**DOM(Document Object Model)**은 HTML 문서를 파싱한 결과물로, 브라우저가 이해할 수 있는 &lt;b&gt;트리 자료구조&lt;/b&gt;다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;노드의 4가지 핵심 타입&lt;/h3&gt;
&lt;div&gt;노드 타입설명
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;문서 노드&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;DOM 트리 최상위 루트. document 객체. 다른 노드에 접근하는 진입점&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;요소 노드&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;HTML 요소를 나타냄. 문서의 &lt;b&gt;구조&lt;/b&gt;를 표현&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;어트리뷰트 노드&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;HTML 요소의 어트리뷰트. 부모 노드 없이 요소 노드에만 연결됨&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;텍스트 노드&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;HTML 요소의 텍스트. 리프 노드(자식 노드 없음). 문서의 &lt;b&gt;정보&lt;/b&gt;를 표현&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노드 상속&lt;/p&gt;
&lt;pre id=&quot;code_1780286668371&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Object
  └── EventTarget
        └── Node
              ├── Document &amp;rarr; HTMLDocument
              ├── Attr
              ├── CharacterData &amp;rarr; Text / Comment
              └── Element &amp;rarr; HTMLElement
                              ├── HTMLInputElement
                              ├── HTMLDivElement
                              └── ...&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;11. 노드 취득 메서드 비교&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;id로 취득&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;coffeescript&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;// 단 하나의 요소 반환. 없으면 null
document.getElementById('apple');&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;태그 이름으로 취득&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;reasonml&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;// HTMLCollection 반환 (live)
document.getElementsByTagName('li');

// 특정 요소의 자손 중에서만 탐색
$ul.getElementsByTagName('li');&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;class로 취득&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;dart&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;// HTMLCollection 반환 (live)
document.getElementsByClassName('fruit');&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;CSS 선택자로 취득 ✅ 권장&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;dart&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;// 첫 번째 요소 하나 반환
document.querySelector('.banana');

// 조건을 만족하는 모든 요소 반환 &amp;rarr; NodeList (non-live)
document.querySelectorAll('ul &amp;gt; li');&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HTMLCollection vs NodeList 비교&lt;/h3&gt;
&lt;div&gt;구분HTMLCollectionNodeList
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;반환 메서드&lt;/td&gt;
&lt;td&gt;getElementsByTagName, getElementsByClassName&lt;/td&gt;
&lt;td&gt;querySelectorAll, childNodes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;live 여부&lt;/td&gt;
&lt;td&gt;&lt;b&gt;항상 live&lt;/b&gt; (실시간 반영)&lt;/td&gt;
&lt;td&gt;대부분 &lt;b&gt;non-live&lt;/b&gt; (childNodes는 live)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;순회 시 주의&lt;/td&gt;
&lt;td&gt;for 문 순회 중 변경되면 오작동 위험&lt;/td&gt;
&lt;td&gt;비교적 안전&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;권장 사용법&lt;/td&gt;
&lt;td&gt;배열로 변환 후 사용 [...$elems]&lt;/td&gt;
&lt;td&gt;배열로 변환 후 사용 권장&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1780286748921&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// HTMLCollection 순회 시 주의사항
const $elems = document.getElementsByClassName('red'); // live!

// ❌ 위험: 순회 중 DOM이 변경되어 일부 요소가 건너뛰어짐
for (let i = 0; i &amp;lt; $elems.length; i++) {
  $elems[i].className = 'blue';
}

// ✅ 안전: 배열로 변환 후 순회
[...$elems].forEach(elem =&amp;gt; elem.className = 'blue');&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;12. DOM 조작&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;텍스트 조작&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;dart&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;// nodeValue: 텍스트 노드의 값을 직접 변경
const $text = document.getElementById('foo').firstChild;
$text.nodeValue = 'changed!';

// textContent: 요소의 모든 텍스트 취득/변경 (HTML 마크업 무시)
document.getElementById('foo').textContent = 'Hello World';&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;innerHTML vs insertAdjacentHTML vs createElement&lt;/h3&gt;
&lt;div&gt;방법장점단점
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;innerHTML&lt;/td&gt;
&lt;td&gt;간단, 직관적&lt;/td&gt;
&lt;td&gt;XSS 취약, 기존 자식 노드 전부 제거 후 재생성&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;insertAdjacentHTML&lt;/td&gt;
&lt;td&gt;기존 노드 유지, 빠름&lt;/td&gt;
&lt;td&gt;XSS 취약&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;createElement + appendChild&lt;/td&gt;
&lt;td&gt;안전, XSS 위험 없음&lt;/td&gt;
&lt;td&gt;코드 복잡&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;reasonml&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;// insertAdjacentHTML 위치 옵션
element.insertAdjacentHTML('beforebegin', '&amp;lt;p&amp;gt;앞&amp;lt;/p&amp;gt;');  // 요소 앞
element.insertAdjacentHTML('afterbegin',  '&amp;lt;p&amp;gt;첫째&amp;lt;/p&amp;gt;'); // 첫 자식 앞
element.insertAdjacentHTML('beforeend',  '&amp;lt;p&amp;gt;마지막&amp;lt;/p&amp;gt;');// 마지막 자식 뒤
element.insertAdjacentHTML('afterend',   '&amp;lt;p&amp;gt;뒤&amp;lt;/p&amp;gt;');   // 요소 뒤&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;노드 생성 및 추가&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;php&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;// 여러 노드 추가 시 DocumentFragment 사용 &amp;rarr; DOM 변경 1회로 최소화
const $fragment = document.createDocumentFragment();

['Apple', 'Banana', 'Orange'].forEach(text =&amp;gt; {
  const $li = document.createElement('li');
  $li.textContent = text;
  $fragment.appendChild($li);
});

document.getElementById('fruits').appendChild($fragment);
// DOM 변경은 단 1회!&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;주요 노드 탐색 프로퍼티&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;stylus&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;// 자식 탐색
node.childNodes          // NodeList (텍스트 노드 포함)
element.children         // HTMLCollection (요소 노드만)
element.firstElementChild
element.lastElementChild

// 부모 탐색
node.parentNode

// 형제 탐색
element.previousElementSibling
element.nextElementSibling&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <author>taek2-0310</author>
      <guid isPermaLink="true">https://taek2-0310.tistory.com/13</guid>
      <comments>https://taek2-0310.tistory.com/13#entry13comment</comments>
      <pubDate>Mon, 1 Jun 2026 13:07:20 +0900</pubDate>
    </item>
    <item>
      <title>자바스크립트 객체와 프로토타입 &amp;mdash; 언제, 왜 쓰는 걸까?</title>
      <link>https://taek2-0310.tistory.com/12</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;객체는 흔히 우리가 너무 많이 쓰지만 프로토타입은 경험할 기회가 흔치 않아요. 저는 항상 무언가를 배우게 되면 &amp;ldquo;그래서 언제 쓰는데?&amp;rdquo;라는 생각이 들기 때문에 이 블로그에도 그런 생각을 담아 작성해보았습니다. 혹시 사용하는 상황이라도 기억해둔다면 &amp;ldquo;아, 이런 상황에서 프로토타입을 쓰던데?&amp;rdquo; 하며 다시 돌아와 개념을 다지고 사용할 수 있지 않을까요?&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1부. 객체 리터럴 &amp;mdash; 객체를 만드는 가장 쉬운 방법&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;객체가 뭔지부터&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트에서 원시값(숫자, 문자열, 불리언 등)을 제외한 거의 모든 값은 객체입니다. 함수도, 배열도, 정규표현식도 객체예요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체는 &lt;b&gt;키(key)와 값(value)으로 이루어진 프로퍼티의 집합&lt;/b&gt;입니다. 그 중에서 값이 함수인 프로퍼티는 특별히 &lt;b&gt;메서드&lt;/b&gt;라고 부릅니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;javascript&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;const person = {
  name: 'Lee',        // 프로퍼티: 상태를 나타냄
  age: 20,
  sayHello() {        // 메서드: 동작을 나타냄
    console.log(`Hi, ${this.name}!`);
  }
};&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 &lt;b&gt;상태(데이터)와 동작(행동)을 하나의 단위로 묶는다&lt;/b&gt;는 겁니다. 관련 있는 것들을 한 곳에 모아 관리하는 거죠.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;객체를 만드는 방법은 5가지&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트는 클래스 없이도 객체를 만들 수 있습니다. 방법이 다섯 가지나 있어요.&lt;/p&gt;
&lt;div&gt;방법코드 예시
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;객체 리터럴&lt;/td&gt;
&lt;td&gt;const obj = { name: 'Lee' }&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Object 생성자 함수&lt;/td&gt;
&lt;td&gt;const obj = new Object()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;생성자 함수&lt;/td&gt;
&lt;td&gt;const obj = new Person('Lee')&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Object.create&lt;/td&gt;
&lt;td&gt;const obj = Object.create(proto)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;클래스(ES6)&lt;/td&gt;
&lt;td&gt;class Person { }&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그럼 각각 언제 쓸까요?&lt;/b&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;✅ 객체 리터럴 &amp;mdash; 단순하고 일회성인 데이터 묶음&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;qml&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;// 딱 한 번만 쓸 설정값, 옵션 객체, 반환값 등
const config = {
  host: 'localhost',
  port: 3000,
  debug: true
};

// API 응답 데이터 임시 가공
const user = {
  id: response.userId,
  name: response.userName,
};&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 구조의 객체를 여러 개 만들 필요가 없을 때 가장 편합니다. 코드가 짧고 직관적이에요.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;✅ 생성자 함수 / 클래스 &amp;mdash; 같은 구조의 객체를 여러 개 만들 때&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;actionscript&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;// 같은 구조의 객체가 여러 개 필요할 때
function Person(name, age) {
  this.name = name;
  this.age = age;
}

const lee = new Person('Lee', 20);
const kim = new Person('Kim', 25);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lee, kim 처럼 같은 틀(구조)의 인스턴스를 찍어낼 때 씁니다. 객체 리터럴로 하면 같은 코드를 반복 작성해야 하니까요.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;✅ Object.create &amp;mdash; 프로토타입을 직접 지정할 때&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;javascript&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;const animal = {
  breathe() { console.log('숨 쉰다'); }
};

const dog = Object.create(animal); // animal을 프로토타입으로 지정
dog.bark = function() { console.log('멍!'); };

dog.breathe(); // 숨 쉰다 (animal에서 상속)
dog.bark();    // 멍!&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상속 관계를 매우 명시적으로 설정하고 싶을 때, 또는 null 프로토타입 객체(순수한 데이터 저장용 객체)가 필요할 때 씁니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;dart&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;// 프로토타입 체인이 없는 순수 객체 (Map 대신 쓸 때)
const dict = Object.create(null);
dict['hello'] = '안녕';
// toString, hasOwnProperty 같은 기본 메서드가 없어서 키 충돌 걱정 없음&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;솔직히 ✅ Object.create 같은 건 본 적이 없어서 알아봤는데 예전에는 자바스크립트에서 class를 표현할 방법이 없어 사용했었다고 해요. 그런데 자바스크립트에 class 문법이 생기면서 잘 사용되지 않게 되었다고 합니다. 그마저도 요즘은 class 대신 객체 리터럴을 사용하는 경우도 많아서 그냥 이런 게 있구나 정도로만 알아두시면 될 것 같아요.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ES6 축약 문법&amp;nbsp;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;프로퍼티 축약&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;delphi&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;const name = 'Lee';
const age = 20;

// 변수명과 키 이름이 같으면 생략 가능
const person = { name, age };
// { name: 'Lee', age: 20 } 과 동일&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수에서 객체를 반환하거나 상태를 모아서 넘길 때 자주 씁니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;reasonml&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;function getUser(id) {
  const name = fetchName(id);
  const email = fetchEmail(id);
  return { name, email }; // 축약 표현
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;메서드 축약&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;actionscript&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;// 예전 방식
const obj = {
  greet: function() { ... }
};

// ES6 방식
const obj = {
  greet() { ... }
};&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 짧아진 것이 아니라 ES6 메서드 축약으로 정의한 메서드는 내부 동작이 다릅니다(non-constructor, super 사용 가능). 클래스 메서드처럼 동작한다고 이해하면 됩니다.&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  AI에게 물어본 &quot;내부 동작이 다릅니다(non-constructor, super 사용 가능).&quot;이 뭘 의미할까?&lt;br /&gt;1. non-constructor &amp;mdash; &quot;이건 생성자로 못 써&quot;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;new로 호출할 수 없다는 뜻이에요.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;actionscript&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;// 일반 function으로 정의한 메서드
const obj1 = {
  greet: function() { return 'hi'; }
};

// ES6 축약으로 정의한 메서드
const obj2 = {
  greet() { return 'hi'; }
};

// 일반 function &amp;rarr; new로 호출 가능 (constructor)
new obj1.greet(); // ✅ 되긴 됨 (빈 객체 생성됨)

// ES6 축약 &amp;rarr; new로 호출 불가 (non-constructor)
new obj2.greet(); // ❌ TypeError: obj2.greet is not a constructor&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 솔직히 &lt;b&gt;객체 안에 있는 메서드를 new로 호출할 일은 현실에서 없어요.&lt;/b&gt; 그래서 이 차이가 실무에서 체감되는 경우는 거의 없고, &quot;아 그런 차이가 있구나&quot; 정도로만 알면 충분해요.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. super &amp;mdash; 이게 실제로 중요한 차이&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;super는 &lt;b&gt;부모 클래스의 메서드를 호출할 때&lt;/b&gt; 쓰는 키워드예요.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;scala&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;class Animal {
  speak() {
    return '소리를 냅니다';
  }
}

class Dog extends Animal {
  speak() {
    const parent = super.speak(); // 부모의 speak() 호출
    return `멍! (${parent})`;
  }
}

const d = new Dog();
d.speak(); // &quot;멍! (소리를 냅니다)&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까진 자연스럽죠. 문제는 &lt;b&gt;객체 리터럴 안에서 super를 쓰고 싶을 때&lt;/b&gt; 발생해요.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;javascript&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;const animal = {
  speak() {
    return '소리를 냅니다';
  }
};

// ❌ 일반 function으로 정의하면 super 못 씀
const dog1 = {
  speak: function() {
    return super.speak(); // SyntaxError!
  }
};

// ✅ ES6 축약으로 정의하면 super 사용 가능
const dog2 = {
  speak() {
    return super.speak(); // 정상 동작
  }
};

Object.setPrototypeOf(dog2, animal);
dog2.speak(); // &quot;소리를 냅니다&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;super를 쓰려면 &lt;b&gt;ES6 축약 메서드 문법으로 정의해야 한다&lt;/b&gt;는 게 핵심이에요.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;근데 실무에서 객체 리터럴에 super 쓸 일이 있나?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;솔직히 &lt;b&gt;클래스 쓰면 해결되는 경우가 대부분&lt;/b&gt;이라 자주 보이진 않아요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그나마 쓰이는 경우는 Vue 옵션 API나 믹스인 패턴처럼 객체를 조합하는 상황 정도인데, 현대 코드에선 이것도 클래스나 컴포지션 패턴으로 대체되는 추세라서요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로 이 두 차이는 &lt;b&gt;&quot;ES6 메서드 축약은 그냥 짧은 게 아니라 내부적으로 다르게 동작한다&quot;&lt;/b&gt; 는 걸 알려주는 포인트예요. 실무 체감보다는 동작 원리 이해 쪽에 가깝습니다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;계산된 프로퍼티 이름&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;nsis&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;const prefix = 'btn';

const ui = {
  [`${prefix}-primary`]: '#3b82f6',
  [`${prefix}-danger`]: '#ef4444',
};
// { 'btn-primary': '#3b82f6', 'btn-danger': '#ef4444' }&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키를 동적으로 결정해야 할 때 유용합니다. 팩토리 패턴이나 설정 객체를 생성할 때 자주 쓰입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 프로토타입 &amp;mdash; 자바스크립트 상속의 실체&lt;/h2&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;div&gt;
&lt;div&gt;
&lt;pre class=&quot;javascript&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;function Circle(radius) {
  this.radius = radius;
  this.getArea = function() {  // ❌ 문제!
    return Math.PI * this.radius ** 2;
  };
}

const c1 = new Circle(1);
const c2 = new Circle(2);
const c3 = new Circle(3);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;c1, c2, c3가 가진 getArea는 &lt;b&gt;내용이 완전히 동일한데 3개가 따로 만들어집니다.&lt;/b&gt; 인스턴스가 1000개면 같은 함수가 1000개 생기는 거예요. 메모리 낭비이고 성능에도 악영향을 줍니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;jboss-cli&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;console.log(c1.getArea === c2.getArea); // false &amp;mdash; 다른 함수 객체!&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&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;프로토타입에 한 번만 정의&lt;/b&gt;하고, 모든 인스턴스가 그걸 공유하면 됩니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;javascript&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;function Circle(radius) {
  this.radius = radius; // 인스턴스마다 다른 값 &amp;rarr; 각자 소유
}

// 공통 동작은 프로토타입에 한 번만
Circle.prototype.getArea = function() {
  return Math.PI * this.radius ** 2;
};

const c1 = new Circle(1);
const c2 = new Circle(2);

console.log(c1.getArea === c2.getArea); // true &amp;mdash; 같은 함수 공유!&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;원칙:&lt;/b&gt; 인스턴스마다 달라지는 값(상태)은 직접 소유하고, 모든 인스턴스가 똑같이 하는 행동(메서드)은 프로토타입에서 공유한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;프로토타입 체인 &amp;mdash; 어떻게 찾아가는가&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;객체에서 프로퍼티를 찾을 때 자바스크립트는 이렇게 동작합니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;reasonml&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;me.hasOwnProperty('name') 호출 시:

1. me 객체 안에서 hasOwnProperty 찾음 &amp;rarr; 없음
2. me.__proto__ (= Person.prototype) 에서 찾음 &amp;rarr; 없음
3. Person.prototype.__proto__ (= Object.prototype) 에서 찾음 &amp;rarr; 있음! 실행&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 &lt;b&gt;프로토타입 체인&lt;/b&gt;입니다. 최상위는 항상 Object.prototype이고, 거기서도 없으면 undefined를 반환합니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;javascript&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;function Person(name) { this.name = name; }
Person.prototype.sayHello = function() {
  console.log(`Hi, ${this.name}!`);
};

const me = new Person('Lee');

me.sayHello();              // 'Hi, Lee!'  &amp;larr; Person.prototype에서 상속
me.hasOwnProperty('name'); // true         &amp;larr; Object.prototype에서 상속
me.foo;                    // undefined    &amp;larr; 체인 끝까지 없으면 undefined&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3각 관계: 생성자 함수 &amp;harr; 프로토타입 &amp;harr; 인스턴스&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;delphi&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;function Person(name) { this.name = name; }
const me = new Person('Lee');

// 세 가지가 서로 연결되어 있음
Person.prototype.constructor === Person;  // true
me.__proto__ === Person.prototype;        // true
me.constructor === Person;               // true (프로토타입 통해 상속)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 3각 관계를 알아야 프로토타입 교체나 instanceof 동작을 이해할 수 있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;정적 메서드 vs 프로토타입 메서드 &amp;mdash; 언제 뭘 쓸까?&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;zephir&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;function MathUtils() {}

// 프로토타입 메서드 &amp;mdash; 인스턴스가 있어야 호출 가능
MathUtils.prototype.double = function(n) {
  return n * 2;
};

// 정적 메서드 &amp;mdash; 인스턴스 없이 바로 호출 가능
MathUtils.square = function(n) {
  return n * n;
};

const util = new MathUtils();
util.double(5);    // 10 ✅
MathUtils.square(5); // 25 ✅
util.square(5);    // ❌ TypeError&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;판단 기준:&lt;/b&gt; 메서드 내부에서 this(인스턴스)를 참조하는가?&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;this 참조 필요 &amp;rarr; &lt;b&gt;프로토타입 메서드&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;this 참조 불필요 &amp;rarr; &lt;b&gt;정적 메서드&lt;/b&gt;로 만드는 게 더 명확&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 예시로 보면:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;dart&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;// Object.create &amp;mdash; 정적 메서드 (인스턴스 없이 씀)
const obj = Object.create(null);

// obj.hasOwnProperty &amp;mdash; 프로토타입 메서드 (인스턴스가 씀)
obj.hasOwnProperty('key');&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;프로퍼티 섀도잉 &amp;mdash; 오버라이딩의 실체&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;javascript&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;Person.prototype.sayHello = function() {
  console.log(`Hi, ${this.name}`);   // 프로토타입 메서드
};

const me = new Person('Lee');

// 인스턴스에 같은 이름의 메서드 추가
me.sayHello = function() {
  console.log(`Hey! ${this.name}`);  // 인스턴스 메서드
};

me.sayHello(); // &quot;Hey! Lee&quot; &amp;mdash; 인스턴스 메서드가 우선&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&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;div&gt;
&lt;div&gt;
&lt;pre class=&quot;awk&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;delete me.sayHello;
me.sayHello(); // &quot;Hi, Lee&quot; &amp;mdash; 이제 프로토타입 메서드 호출&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;instanceof와 프로퍼티 확인&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;instanceof &amp;mdash; 프로토타입 체인 확인&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;javascript&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;me instanceof Person  // Person.prototype이 me의 체인에 있는가?
me instanceof Object  // Object.prototype이 me의 체인에 있는가?&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;constructor 프로퍼티가 아니라 프로토타입 체인 상에 존재하는지를 확인하는 거라는 점이 중요합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;프로퍼티 존재 확인&lt;/h4&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;groovy&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;const person = { name: 'Lee', address: 'Seoul' };

// in 연산자: 프로토타입 체인 전체에서 확인
'name' in person       // true
'toString' in person   // true (Object.prototype에 있으니까!)

// hasOwnProperty: 자기 자신의 프로퍼티만 확인
person.hasOwnProperty('name')      // true
person.hasOwnProperty('toString')  // false&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상속받은 것까지 포함해서 볼 때는 in, 자기 것만 볼 때는 hasOwnProperty.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;프로퍼티 열거 &amp;mdash; for...in의 함정&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;processing&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;const person = {
  name: 'Lee',
  address: 'Seoul',
  __proto__: { age: 20 }  // 상속받은 프로퍼티
};

// for...in: 상속받은 것까지 열거
for (const key in person) {
  console.log(key); // name, address, age 모두 출력!
}

// 자기 것만 열거하려면 hasOwnProperty 필터링 필요
for (const key in person) {
  if (!person.hasOwnProperty(key)) continue;
  console.log(key); // name, address만 출력
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 번거로움 때문에 보통 아래를 더 권장합니다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;prolog&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;Object.keys(person)    // ['name', 'address'] &amp;mdash; 자신의 열거 가능한 키만
Object.values(person)  // ['Lee', 'Seoul']
Object.entries(person) // [['name', 'Lee'], ['address', 'Seoul']]&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열 순회에는 for...in 대신 for, forEach, for...of를 씁니다. 배열도 객체라서 for...in으로 돌리면 숫자 인덱스 외에 추가 프로퍼티도 딸려 나올 수 있거든요.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3부. 정리 &amp;mdash; 언제 무엇을 쓸 것인가&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;객체 생성 방법 선택 기준&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;pgsql&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;단순 데이터 묶음, 한 번만 씀
  &amp;rarr; 객체 리터럴 { }

같은 구조의 객체를 여러 개 만들어야 함
  &amp;rarr; 생성자 함수 / 클래스

프로토타입을 직접 지정하고 싶음
  &amp;rarr; Object.create()

순수한 데이터 저장소 (메서드 없는 맵처럼)
  &amp;rarr; Object.create(null)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;메서드 정의 위치 선택 기준&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;kotlin&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;인스턴스마다 다르게 동작해야 함 (this 참조 필요)
  &amp;rarr; 프로토타입 메서드

인스턴스와 무관한 유틸성 기능 (this 참조 불필요)
  &amp;rarr; 정적 메서드&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;프로퍼티 열거/확인 선택 기준&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;reasonml&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;프로토타입 체인 포함해서 확인
  &amp;rarr; in 연산자

자기 자신 것만 확인
  &amp;rarr; hasOwnProperty

자기 자신 것만 열거
  &amp;rarr; Object.keys / Object.values / Object.entries

상속 포함 열거 (드물게)
  &amp;rarr; for...in (hasOwnProperty 필터링 병행)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <author>taek2-0310</author>
      <guid isPermaLink="true">https://taek2-0310.tistory.com/12</guid>
      <comments>https://taek2-0310.tistory.com/12#entry12comment</comments>
      <pubDate>Mon, 25 May 2026 11:36:22 +0900</pubDate>
    </item>
    <item>
      <title>this, 클로저 사실 나도 잘 몰라...ㅡJavaScript 완전 정복</title>
      <link>https://taek2-0310.tistory.com/11</link>
      <description>&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;this란?  &lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;this는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수&lt;/b&gt;다. 가장 일반적으로는 객체의 메서드 안에서 사용되며 이를 통해 동일한 메서드를 서로 다른 객체에서 재사용할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;일반 변수는 선언된 위치(렉시컬 스코프)에 의해 값이 결정되지만 this는 다르다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;함수가 어떤 방식으로 호출(바인딩)되었는지&lt;/b&gt;에 따라 가리키는 값이 동적으로 결정된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;쉽게 비유하자면 &quot;나&quot;라는 단어와 같다. &quot;나는 밥을 먹었다&quot;에서 '나'가 누구인지는 그 말을 누가 했느냐에 따라 달라지는 것처럼 this도 누가(어떤 컨텍스트가) 함수를 호출했느냐에 따라 달라진다.&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;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;this가 결정되는 4가지 규칙  &lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;1. 기본 바인딩&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;아무런 컨텍스트 없이 함수를 단독 호출하면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;this는 전역 객체&lt;/b&gt;를 가리킨다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;그리고 이 this는 브라우저에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;window 객체&lt;/b&gt;가 된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1779358871671&quot; class=&quot;javascript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function greet() {
  console.log(this); // window (브라우저)
}

greet();&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;2. 암시적 바인딩&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;객체의 메서드로 호출되면 this는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;그 객체&lt;/b&gt;를 가리킨다.&lt;/p&gt;
&lt;pre id=&quot;code_1779358871671&quot; class=&quot;pgsql&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;const user = {
  name: '철수',
  greet() {
    console.log(this.name); // '철수'
  }
};

user.greet(); // this === user&lt;/code&gt;&lt;/pre&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;size18&quot;&gt;3. 명시적 바인딩&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;함수가 호출될 때 apply, call 또는 bind가 사용되었다면 첫번째 인자로 전달하는 값에 this 를 바인딩 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;bind는 새로운 함수를 반환하며 this를 영구 고정한다는 점에서 call/apply와 다르다.&lt;/p&gt;
&lt;pre id=&quot;code_1779358871672&quot; class=&quot;pgsql&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;jsfunction greet() {
  console.log(this.name);
}

const user = { name: '철수' };

greet.call(user);        // '철수'
greet.apply(user);       // '철수'
const bound = greet.bind(user);
bound();                 // '철수'&lt;/code&gt;&lt;/pre&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;size18&quot;&gt;4. new 바인딩&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;new 키워드로 생성자 함수를 호출하면, this는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;새로 생성되는 인스턴스 객체&lt;/b&gt;를 가리킨다&lt;/p&gt;
&lt;pre id=&quot;code_1779358871672&quot; class=&quot;javascript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function Person(name) {
  this.name = name; // 새 인스턴스에 바인딩
}

const p = new Person('민준');
console.log(p.name); // '민준'&lt;/code&gt;&lt;/pre&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;&lt;b&gt;❗️우선순위: new &amp;gt; 명시적(bind) &amp;gt; 암시적(메서드) &amp;gt; 기본❗️&lt;/b&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;화살표 함수의 this &amp;mdash; 완전히 다른 규칙❓&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;화살표 함수는 this를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;아예 갖지 않는다.&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;대신 자신이 선언된 위치의 외부 스코프 this를 그대로 캡처한다.&lt;b&gt;(클로저)&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1779358871673&quot; class=&quot;javascript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;const person = {
	name : 'Seo',
    sayName : function() {
    	innerFun = function() {
        	return `안녕하세요 ${this.name}님`
         }
       console.log(innerFun())  /// 안녕하세요 ''님
    }
 }
 
 person.sayName()&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f8f9fa; color: #212529; text-align: start;&quot;&gt;innerFun 함수 앞에 마침표(.) 을 붙여서 호출하지도 않았고, bind, call, apply 를 사용하지도 않았다. 일반 함수 호출 되었기 때문에 여기서 this 는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;window&lt;span style=&quot;background-color: #f8f9fa; color: #212529; text-align: start;&quot;&gt;가 된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1779358871673&quot; class=&quot;javascript&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;const person = {
	name : 'Seo',
    sayName : function() {
    	innerFun =() =&amp;gt; {
        	return `안녕하세요 ${this.name}님` /// 안녕하세요 Seo님
         }
       console.log(innerFun())
    }
 }
 
 person.sayName()&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f8f9fa; color: #212529; text-align: start;&quot;&gt;여기서도 innerFun 은 일반함수로 호출 되었으나 innerFun 이 화살표 함수로 선언이 되어 있다. 화살표 함수에서의 this 는 자신의 상위 스코프를 따르기 때문에 여기서 this 는 person 객체 안에 선언된 name 이 된다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f8f9fa; color: #212529; text-align: start;&quot;&gt;클로저 - 기억하는 함수  &lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;클로저의 정의&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MDN은 클로저를 이렇게 정의한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;A closure is the combination of a function and the lexical environment within which that function was declared.&quot; 클로저는 함수와 그 함수가 선언된 렉시컬 환경의 조합이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;풀어 말하면 &lt;b&gt;외부 함수가 종료된 후에도 내부 함수가 외부 함수의 변수를 참조할 수 있는 현상&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이게 왜 가능하냐면 함수 객체는 생성될 때 [[Environment]]라는 내부 슬롯에 자신이 정의된 위치의 상위 스코프 참조를 저장한다.(함수가 살아있는 한 유지)&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1779359783748&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); }; // [[Environment]] = outer의 렉시컬 환경
  return inner;
}

const innerFunc = outer(); // outer 실행 컨텍스트는 스택에서 제거
innerFunc(); // 10 &amp;mdash; 그러나 x는 여전히 참조 가능&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;outer의 실행 컨텍스트가 콜 스택에서 제거되어도 inner의 [[Environment]]가 outer의 렉시컬 환경을 참조하고 있기 때문에 가비지 컬렉션의 대상이 되지 않는다.&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;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;li&gt;&lt;b&gt;외부 함수보다 더 오래 유지된다 (외부로 반환된다)&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1779359980430&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;

  // 클로저 &amp;mdash; 상위 스코프 참조 + 외부로 반환
  function bar() {
    console.log(x);
  }
  return bar;
}&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;클로저 왜 쓰지?&lt;/h3&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;외부에서 접근할 수 없는 private 상태를 만들기 위해서&lt;/b&gt;다.&lt;/p&gt;
&lt;pre id=&quot;code_1779360181812&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 전역 변수 방식 &amp;mdash; 누구나 변경 가능한 취약한 구조
let num = 0;
const increase = function() { return ++num; };&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1779360190230&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 클로저 방식 &amp;mdash; num은 외부에서 직접 접근 불가
const increase = (function() {
  let num = 0;
  return function() {
    return ++num;
  };
}());

increase(); // 1
increase(); // 2
increase(); // 3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉시 실행 함수(IIFE)로 num을 감싸면 외부에서 직접 접근이 불가능해진다. 반환된 함수만이 num을 변경할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;증가와 감소 모두 필요하다면 메서드를 가진 객체를 반환하면 된다:&lt;/p&gt;
&lt;pre id=&quot;code_1779360276318&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const counter = (function() {
  let num = 0;

  return {
    increase() { return ++num; },
    decrease() { return num &amp;gt; 0 ? --num : 0; }
  };
}());

counter.increase(); // 1
counter.increase(); // 2
counter.decrease(); // 1&lt;/code&gt;&lt;/pre&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;전역변수 사용의 최소화&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;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;정보의 접근 제한 (캡슐화)&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;this&lt;/td&gt;
&lt;td&gt;클로저&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;결정 시점&lt;/td&gt;
&lt;td&gt;함수 &lt;b&gt;호출&lt;/b&gt; 시점&lt;/td&gt;
&lt;td&gt;함수 &lt;b&gt;정의&lt;/b&gt; 시점&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;핵심 질문&lt;/td&gt;
&lt;td&gt;누가 이 함수를 호출했나?&lt;/td&gt;
&lt;td&gt;어디서 이 함수가 정의됐나?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>taek2-0310</author>
      <guid isPermaLink="true">https://taek2-0310.tistory.com/11</guid>
      <comments>https://taek2-0310.tistory.com/11#entry11comment</comments>
      <pubDate>Fri, 22 May 2026 16:53:32 +0900</pubDate>
    </item>
    <item>
      <title>useRef와 함께 알아야 할 React 훅들</title>
      <link>https://taek2-0310.tistory.com/10</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. useRef &amp;mdash; &quot;렌더링 없이 값을 기억하고 싶을 때&quot;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useRef는 두 가지 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;① 렌더링을 유발하지 않는 변경 가능한 값 저장소&lt;/p&gt;
&lt;pre id=&quot;code_1779025102445&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const countRef = useRef(0);

const handleClick = () =&amp;gt; {
  countRef.current += 1;
  console.log(countRef.current); // 렌더링 없이 값 업데이트
};&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;구분useStateuseRef&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;값 변경 시 렌더링&lt;/td&gt;
&lt;td&gt;O&lt;/td&gt;
&lt;td&gt;X&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;렌더링 간 값 유지&lt;/td&gt;
&lt;td&gt;O&lt;/td&gt;
&lt;td&gt;O&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;용도&lt;/td&gt;
&lt;td&gt;UI에 반영할 상태&lt;/td&gt;
&lt;td&gt;내부적으로만 필요한 값&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;② DOM 엘리먼트 직접 참조&lt;/p&gt;
&lt;pre id=&quot;code_1779025148193&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const inputRef = useRef(null);

const focusInput = () =&amp;gt; {
  inputRef.current.focus(); // DOM API 직접 호출
};

return &amp;lt;input ref={inputRef} /&amp;gt;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;언제 useRef를 쓸까?&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이전 렌더링의 props/state 값을 기억할 때&lt;/li&gt;
&lt;li&gt;setInterval / setTimeout ID 저장할 때&lt;/li&gt;
&lt;li&gt;애니메이션 프레임 ID 관리할 때&lt;/li&gt;
&lt;li&gt;DOM 엘리먼트에 직접 접근해야 할 때&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. useCallback &amp;mdash; &quot;함수 참조가 바뀌면 안 될 때&quot;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useCallback은 함수를 메모이제이션한다. 의존성 배열이 바뀌지 않으면 &lt;b&gt;이전 렌더링에서 생성된 동일한 함수 참조&lt;/b&gt;를 반환한다.&lt;/p&gt;
&lt;pre id=&quot;code_1779025342792&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ❌ 매 렌더링마다 새로운 함수 생성
const handleSubmit = () =&amp;gt; {
  sendData(inputValue);
};

// ✅ inputValue가 바뀔 때만 새로운 함수 생성
const handleSubmit = useCallback(() =&amp;gt; {
  sendData(inputValue);
}, [inputValue]);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;useRef와의 관계&lt;/b&gt;: useCallback은 내부적으로 useRef와 유사한 메커니즘으로 이전 값을 기억한다. 직접 조합하면 의존성 없이 최신 값을 참조하는 안정적인 콜백을 만들 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1779025443625&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 안정적인 콜백 패턴 (useRef + useCallback 조합)
const callbackRef = useRef(callback);
callbackRef.current = callback;

const stableCallback = useCallback((...args) =&amp;gt; {
  return callbackRef.current(...args);
}, []); // 의존성 배열이 비어있어도 항상 최신 callback 호출&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;언제 useCallback을 쓸까?&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;React.memo로 감싼 자식 컴포넌트에 함수를 prop으로 전달할 때&lt;/li&gt;
&lt;li&gt;useEffect의 의존성 배열에 함수가 들어갈 때&lt;/li&gt;
&lt;li&gt;함수 생성 비용이 클 때 (드문 경우)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;남용 주의&lt;/b&gt;: 단순한 함수에 useCallback을 무조건 감싸면 오히려 메모리와 성능에 부담이 된다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. useMemo &amp;mdash; &quot;계산 결과를 재사용하고 싶을 때&quot;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useMemo는 &lt;b&gt;값&lt;/b&gt;을 메모이제이션한다. useCallback이 함수를 캐싱한다면, useMemo는 함수의 &lt;b&gt;반환값&lt;/b&gt;을 캐싱한다.&lt;/p&gt;
&lt;pre id=&quot;code_1779025631757&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ❌ 렌더링마다 무거운 계산 반복
const sortedList = items.sort((a, b) =&amp;gt; b.score - a.score);

// ✅ items가 바뀔 때만 재계산
const sortedList = useMemo(() =&amp;gt; {
  return items.sort((a, b) =&amp;gt; b.score - a.score);
}, [items]);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;useCallback vs useMemo 비교&lt;/b&gt;:&lt;/p&gt;
&lt;pre id=&quot;code_1779025653991&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 아래 두 표현은 동일하다
const memoizedFn = useCallback(fn, deps);
const memoizedFn = useMemo(() =&amp;gt; fn, deps);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구분useCallbackuseMemo&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;메모이제이션 대상&lt;/td&gt;
&lt;td&gt;함수 자체&lt;/td&gt;
&lt;td&gt;함수의 반환값&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;반환 타입&lt;/td&gt;
&lt;td&gt;함수&lt;/td&gt;
&lt;td&gt;임의의 값&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;주요 용도&lt;/td&gt;
&lt;td&gt;콜백 안정화&lt;/td&gt;
&lt;td&gt;계산 최적화&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;언제 useMemo를 쓸까?&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;큰 배열의 필터링/정렬 같은 무거운 연산을 캐싱할 때&lt;/li&gt;
&lt;li&gt;객체/배열을 생성해 자식 컴포넌트에 prop으로 전달할 때 (참조 동일성 유지)&lt;/li&gt;
&lt;li&gt;파생 상태(derived state)가 복잡한 계산을 필요로 할 때&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>taek2-0310</author>
      <guid isPermaLink="true">https://taek2-0310.tistory.com/10</guid>
      <comments>https://taek2-0310.tistory.com/10#entry10comment</comments>
      <pubDate>Sun, 17 May 2026 22:48:26 +0900</pubDate>
    </item>
    <item>
      <title>실행 컨텍스트 ㅡ 호이스팅만 알아서 뭐할래? 먼저 근본을 알아야지!</title>
      <link>https://taek2-0310.tistory.com/9</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;당신이 이미 겪어본 이상한 동작&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 코드를 보자.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;sqf&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;console.log(name); // undefined... 왜?
var name = '철수';
console.log(name); // '철수'&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에러가 아니다. undefined다. 선언도 하기 전에 변수를 참조했는데 에러가 나지 않는다. 대부분의 개발자는 이걸 &quot;호이스팅 때문&quot;이라고 설명하고 넘어간다. 하지만 &lt;b&gt;호이스팅은 현상의 이름이지, 원인이 아니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 이 현상이 왜 생기는지를 JS 엔진이 코드를 처리하는 방식 즉 &lt;b&gt;실행 컨텍스트&lt;/b&gt; 수준에서 완전히 해부한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. JS 엔진은 코드를 두 번 훑는다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JS 엔진은 코드를 실행하기 전에 &lt;b&gt;반드시 평가 단계를 먼저 거친다.&lt;/b&gt; 이 평가 단계와 실제 실행 단계, 총 두 번의 훑기가 일어난다.&lt;/p&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;Creation phase (평가 단계)&lt;/b&gt; &amp;mdash; 코드를 실행하기 전, 엔진이 실행 환경을 세팅하는 단계&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Execution phase (실행 단계)&lt;/b&gt; &amp;mdash; 코드를 위에서 아래로 한 줄씩 실제로 실행하는 단계&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;var name이 undefined로 찍히는 이유가 바로 여기 있다. Creation phase에서 엔진은 var name이라는 선언을 미리 발견하고, name이라는 식별자를 등록해둔다. 단 값은 아직 undefined로만 초기화된다. Execution phase가 되어서야 = '철수' 부분이 처리된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드가 올라가는 게 아니다. &lt;b&gt;엔진이 먼저 한 번 훑고 준비를 마친 다음 실행에 들어가는 것&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 준비 단계의 결과물이 바로 &lt;b&gt;실행 컨텍스트(Execution Context)&lt;/b&gt; 다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;따라서!&amp;nbsp;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;소스코드 평가 과정이 끝나면 비로소 선언문을 제외한 소스코드가 순차적으로 실행되기 시직한다. 즉 런타임이 시작된다. 이때 소스코드 실행에 필요한 정보&amp;rsquo; 즉 변수나 힘수의 침조를 실행 컨텍스트가 관리히는 스코프에서 검색해서 취득한다. 그리고 변수 값의 변경 등 소스코드의 실행 결괴는 다시 실행 컨텍스트가 관리하는 스코프에 등록된다&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 실행 컨텍스트 안에는 무엇이 있는가&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 컨텍스트는 코드가 실행되기 위해 필요한 환경 정보를 담은 객체다. 내부에는 세 개의 슬롯이 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Variable Environment&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;식별자 등록 장부다. var로 선언된 변수와 함수 선언문이 이 단계에서 등록된다. Creation phase에서 var 변수는 undefined로, 함수 선언문은 함수 객체 전체로 초기화된다. 이것이 함수 선언문과 함수 표현식의 동작 차이를 만드는 이유다.&lt;/p&gt;
&lt;div&gt;
&lt;pre class=&quot;javascript&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;// 함수 선언문 &amp;mdash; Creation phase에서 함수 전체가 등록됨
hello(); // 'Hello!' &amp;mdash; 정상 작동

function hello() {
  console.log('Hello!');
}

// 함수 표현식 &amp;mdash; var greet는 undefined로만 등록됨
greet(); // TypeError: greet is not a function

var greet = function() {
  console.log('Hi!');
};&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Lexical Environment&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스코프 체인의 실체다. 두 가지 컴포넌트로 이루어진다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Environment Record&lt;/b&gt; &amp;mdash; 현재 스코프에서 선언된 식별자들을 실제로 저장하는 공간&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Outer Lexical Environment Reference&lt;/b&gt; &amp;mdash; 상위 스코프를 가리키는 참조. 이것이 스코프 체인을 만든다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;식별자를 검색할 때 엔진은 현재 Environment Record를 먼저 찾고 없으면 Outer Reference를 따라 상위 스코프로 올라간다. 전역 스코프까지 올라갔는데도 없으면 ReferenceError가 발생한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ThisBinding&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 실행 컨텍스트에서 this가 가리키는 값이다. this는 선언 위치가 아닌 &lt;b&gt;실행 컨텍스트가 생성되는 시점에 결정&lt;/b&gt;된다. 이 슬롯이 있기 때문에 같은 함수도 어떻게 호출하느냐에 따라 this가 달라진다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 콜 스택 &amp;mdash; 실행 컨텍스트의 생명주기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 컨텍스트는 스택 구조로 관리된다. 이를 &lt;b&gt;실행 컨텍스트 스택&lt;/b&gt;&amp;nbsp;흔히 &lt;b&gt;콜 스택(Call Stack)&lt;/b&gt; 이라고 부른다.&lt;/p&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;전역 코드 평가 &amp;rarr; Global EC 생성 &amp;rarr; 스택에 push&lt;/li&gt;
&lt;li&gt;함수 호출 &amp;rarr; 새 EC 생성 &amp;rarr; 스택에 push (현재 실행 중인 EC는 일시 중단)&lt;/li&gt;
&lt;li&gt;함수 실행 완료 &amp;rarr; 해당 EC를 스택에서 pop &amp;rarr; 이전 EC 재개&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 코드를 기준으로 이 흐름을 따라가 보자.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;javascript&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;var x = 1;

function foo() {
  var y = 2;

  function bar() {
    var z = 3;
    console.log(x + y + z); // 6
  }

  bar();
}

foo();&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&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;① 전역 코드 평가&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엔진이 전역 코드를 처음 만나면 &lt;b&gt;Global Execution Context&lt;/b&gt;를 생성한다. Creation phase에서 x와 foo가 등록된다. x는 undefined, foo는 함수 객체로 초기화된다. 이 EC가 스택의 맨 아래에 놓인다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;armasm&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;[ Global EC ]  &amp;larr; 현재 실행 중&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;② 전역 코드 실행&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Execution phase가 시작되면서 x = 1 할당이 처리된다. foo() 호출 라인에 도달하면 Global EC는 일시 중단되고, foo의 EC 생성이 시작된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;③ foo 함수 코드 평가&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;foo의 EC가 생성된다. Creation phase에서 y와 bar가 등록된다. Outer Lexical Environment Reference는 Global EC의 Lexical Environment를 가리킨다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;cs&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;[ foo EC      ]  &amp;larr; 현재 실행 중
[ Global EC   ]&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;④ foo 함수 코드 실행&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;y = 2 할당이 처리되고, bar() 호출에 도달한다. foo EC는 일시 중단된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;⑤ bar 함수 코드 평가&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bar의 EC가 생성된다. z가 등록된다. Outer Reference는 foo EC의 Lexical Environment를 가리킨다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;cs&quot; style=&quot;color: #14181f;&quot;&gt;&lt;code&gt;[ bar EC      ]  &amp;larr; 현재 실행 중
[ foo EC      ]
[ Global EC   ]&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;⑥ bar 함수 코드 실행&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;z = 3이 처리된다. console.log(x + y + z)를 실행하기 위해 식별자를 검색한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;z &amp;rarr; bar EC의 Environment Record에서 발견 &amp;rarr; 3&lt;/li&gt;
&lt;li&gt;y &amp;rarr; bar EC에 없음 &amp;rarr; Outer Reference 따라 foo EC로 이동 &amp;rarr; 발견 &amp;rarr; 2&lt;/li&gt;
&lt;li&gt;x &amp;rarr; foo EC에도 없음 &amp;rarr; 다시 Outer Reference 따라 Global EC로 이동 &amp;rarr; 발견 &amp;rarr; 1&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과: 1 + 2 + 3 = 6&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;⑦ 함수 종료 &amp;mdash; 스택에서 pop&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bar 실행 완료 &amp;rarr; bar EC pop &amp;rarr; foo EC 재개 foo 실행 완료 &amp;rarr; foo EC pop &amp;rarr; Global EC 재개 전역 코드 실행 완료 &amp;rarr; Global EC pop &amp;rarr; 스택 비워짐&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(시각화자료 ⬇️ 다음 단계 버튼을 누를 때마다 foo/bar 예제 기준으로 실행 컨텍스트가 콜 스택에 push/pop되는 걸 실시간으로 보여줘요)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://claude.ai/public/artifacts/809f7f50-2cf3-40bd-a992-b79bf6f5abd8&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://claude.ai/public/artifacts/809f7f50-2cf3-40bd-a992-b79bf6f5abd8&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1778418259135&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;callstack-visualizer.html&quot; data-og-description=&quot;callstack-visualizer.html&quot; data-og-host=&quot;claude.ai&quot; data-og-source-url=&quot;https://claude.ai/public/artifacts/809f7f50-2cf3-40bd-a992-b79bf6f5abd8&quot; data-og-url=&quot;https://claude.ai/public/artifacts/809f7f50-2cf3-40bd-a992-b79bf6f5abd8&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bCVfO1/dJMb887dig7/PSJmuDJ5kxI1MVpg9oDa71/img.png?width=1138&amp;amp;height=640&amp;amp;face=0_0_1138_640,https://scrap.kakaocdn.net/dn/n555D/dJMb87N02ql/4kwVFCjWXqwpsKWMokps10/img.png?width=1138&amp;amp;height=640&amp;amp;face=0_0_1138_640&quot;&gt;&lt;a href=&quot;https://claude.ai/public/artifacts/809f7f50-2cf3-40bd-a992-b79bf6f5abd8&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://claude.ai/public/artifacts/809f7f50-2cf3-40bd-a992-b79bf6f5abd8&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bCVfO1/dJMb887dig7/PSJmuDJ5kxI1MVpg9oDa71/img.png?width=1138&amp;amp;height=640&amp;amp;face=0_0_1138_640,https://scrap.kakaocdn.net/dn/n555D/dJMb87N02ql/4kwVFCjWXqwpsKWMokps10/img.png?width=1138&amp;amp;height=640&amp;amp;face=0_0_1138_640');&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;callstack-visualizer.html&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;callstack-visualizer.html&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;claude.ai&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 마무리 &amp;mdash; 그래서 클로저는 왜 동작하는가&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 살펴본 내용을 정리하면 이렇다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;JS 엔진은 코드를 실행하기 전에 Creation phase를 통해 실행 컨텍스트를 먼저 준비한다&lt;/li&gt;
&lt;li&gt;실행 컨텍스트는 Variable Environment, Lexical Environment, ThisBinding 세 슬롯으로 구성된다&lt;/li&gt;
&lt;li&gt;실행 컨텍스트는 콜 스택으로 관리되며, 함수 호출 시 push, 반환 시 pop된다&lt;/li&gt;
&lt;li&gt;let/const의 TDZ는 버그를 명시적 에러로 드러내기 위한 언어 설계 의도다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 한 가지 질문이 남는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수가 실행을 마치고 실행 컨텍스트가 스택에서 pop되면, 그 함수의 Lexical Environment도 사라져야 한다. 그런데 클로저는 이미 종료된 외부 함수의 변수에 접근할 수 있다. 어떻게?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 컨텍스트가 사라지는 것과 Lexical Environment가 사라지는 것은 다르다. 클로저가 내부 함수의 [[Environment]] 슬롯을 통해 외부 함수의 Lexical Environment를 &lt;b&gt;참조하고 있는 한&lt;/b&gt;, 가비지 컬렉터는 그 객체를 수거하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 클로저의 실체다.&lt;/p&gt;</description>
      <author>taek2-0310</author>
      <guid isPermaLink="true">https://taek2-0310.tistory.com/9</guid>
      <comments>https://taek2-0310.tistory.com/9#entry9comment</comments>
      <pubDate>Sun, 10 May 2026 22:07:43 +0900</pubDate>
    </item>
    <item>
      <title>HTTP 클라이언트 라이브러리 ky ㅡ 키 크다~라는 말은 기분이 나쁘다!    나 (번들 사이즈)작거든!</title>
      <link>https://taek2-0310.tistory.com/8</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;ky란?  &lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ky는 브라우저 내장 fetch API를 기반으로 만든 경량 HTTP 클라이언트입니다. &lt;a href=&quot;https://github.com/sindresorhus&quot;&gt;sindresorhus&lt;/a&gt;가 만들었고 순수 ESM 패키지로 배포됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1778223167979&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pnpm install ky&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1778223183129&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import ky from 'ky';

const data = await ky.get('https://api.example.com/users').json();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딱 이게 전부입니다. fetch를 직접 쓸 때처럼 response.json()을 따로 호출하거나 Content-Type 헤더를 매번 설정할 필요가 없어요.&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;axios 대신 ky를 써야 하는 이유⭐️&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. 성능차이에 비해 번들 사이즈가 압도적으로 작다.&lt;/h4&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;634&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tgxFC/dJMcab5cacu/UaaVG3BtamnnBTsQ8DTuN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tgxFC/dJMcab5cacu/UaaVG3BtamnnBTsQ8DTuN1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tgxFC/dJMcab5cacu/UaaVG3BtamnnBTsQ8DTuN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtgxFC%2FdJMcab5cacu%2FUaaVG3BtamnnBTsQ8DTuN1%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;1280&quot; height=&quot;634&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;634&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #1f2328; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단일 GET 요청: Axios가 약&amp;nbsp;3% 빠름&lt;/li&gt;
&lt;li&gt;POST 요청:&amp;nbsp;Ky가 약 3% 빠름&lt;/li&gt;
&lt;li&gt;동시&amp;nbsp;요청: Axios가 약&amp;nbsp;3% 빠름&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;번들 사이즈에 민감한 프로젝트라면 이 수치가 전부를 말해줍니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;axios&lt;/td&gt;
&lt;td&gt;~13 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ky&lt;/td&gt;
&lt;td&gt;~4 KB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;KY(3KB)와 Axios(13KB)가 번들 차이가 약 4배 차이 발생합니다. axios는 XMLHttpRequest 기반으로 브라우저/Node.js 호환성을 위한 어댑터 레이어가 포함돼 있어서 무겁습니다. ky는 브라우저 네이티브 fetch만 씁니다. 이미 브라우저에 내장된 걸 쓰니까 번들에 실어야 할 게 없어요.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. fetch 기반 &amp;mdash; 폴리필이 필요 없다&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;axios는 내부적으로 XMLHttpRequest를 씁니다. 최신 브라우저 환경에서는 굳이 XHR을 쓸 이유가 없죠. ky는 fetch를 그대로 활용하기 때문에 추가 폴리필 없이 현대 브라우저에서 바로 동작합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Node.js 18+도 fetch가 내장돼 있어서 서버 사이드에서도 별도 설정 없이 씁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;(최근 pwa에 관심이 생겨 찾아보던 중 pwa에 핵심 기능들이 대부분 Service Worker 를 통해 구현된다는 사실을 알게 되었습니다. &lt;span style=&quot;background-color: #ffffff; text-align: start;&quot;&gt;XHR 기반의 axios를 서비스 워커 내에서 사용하려면 별도의 폴리필이나 특수 구성이 필요하고 이로 인해 번들 크기를 증가시킬 수 있다는 것을 알게 습니다.)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3. 더 직관적인 API&lt;/h4&gt;
&lt;pre id=&quot;code_1778223984440&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// axios &amp;mdash; HTTP 에러(4xx, 5xx)는 catch로 떨어짐
try {
  const res = await axios.get('/api/user');
  console.log(res.data); // 한 번 더 .data를 벗겨야 함
} catch (err) {
  if (axios.isAxiosError(err)) {
    console.log(err.response?.status);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1778224022023&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ky &amp;mdash; 훨씬 깔끔
try {
  const data = await ky.get('/api/user').json();
  console.log(data); // 바로 데이터
} catch (err) {
  if (err instanceof HTTPError) {
    console.log(err.response.status);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ky는 4xx, 5xx 응답을 자동으로 HTTPError로 throw합니다. fetch의 가장 큰 함정인 &quot;에러 응답도 resolve된다&quot;는 문제를 해결해줘요.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4. 내장 retry&lt;/h4&gt;
&lt;pre id=&quot;code_1778224066256&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const data = await ky.get('/api/data', {
  retry: {
    limit: 3,
    methods: ['get'],
    statusCodes: [408, 500, 502, 503, 504],
  },
}).json();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크 불안정 상황에서 재시도 로직이 필요할 때, axios는 직접 구현하거나 라이브러리를 추가해야 합니다. ky는 옵션 하나로 끝납니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. Tree-shakeable ESM&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ky는 순수 ESM 패키지입니다. 번들러가 실제로 사용하는 코드만 포함시킬 수 있어요. axios는 CommonJS 기반이라 이 이점을 활용하기 어렵습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;보통 이렇게 사용해요 &lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;공통 인스턴스 설정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매 요청마다 baseURL, 헤더를 반복하지 않도록 인스턴스를 만들어 씁니다.&lt;/p&gt;
&lt;pre id=&quot;code_1778224553299&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import ky from &quot;ky&quot;;

export const api = ky.create({
  prefix: import.meta.env.VITE_BASE_URL,
  retry: 0,
});&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;인증 토큰 자동 주입 (hooks)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;axios의 인터셉터에 해당하는 게 ky의 hooks입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1778224825080&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// lib/api.ts
import ky from 'ky';

export const authApi = ky.create({
  prefixUrl: process.env.NEXT_PUBLIC_API_URL,
  hooks: {
    beforeRequest: [
      request =&amp;gt; {
        const token = localStorage.getItem('access_token');
        if (token) {
          request.headers.set('Authorization', `Bearer ${token}`);
        }
      },
    ],
  },
});&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;토큰 만료 시 자동 갱신&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;액세스 토큰이 만료됐을 때 리프레시 토큰으로 재발급하고 원래 요청을 재시도하는 패턴입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1778224856046&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import ky, { HTTPError } from 'ky';

export const authApi = ky.create({
  prefixUrl: process.env.NEXT_PUBLIC_API_URL,
  hooks: {
    beforeRequest: [
      request =&amp;gt; {
        const token = localStorage.getItem('access_token');
        if (token) {
          request.headers.set('Authorization', `Bearer ${token}`);
        }
      },
    ],
    afterResponse: [
      async (request, options, response) =&amp;gt; {
        if (response.status === 401) {
          // 토큰 갱신 시도
          const refreshToken = localStorage.getItem('refresh_token');
          const refreshed = await ky
            .post('/auth/refresh', { json: { refreshToken } })
            .json&amp;lt;{ accessToken: string }&amp;gt;();

          localStorage.setItem('access_token', refreshed.accessToken);

          // 원래 요청 헤더 업데이트 후 재시도
          request.headers.set('Authorization', `Bearer ${refreshed.accessToken}`);
          return ky(request);
        }
      },
    ],
  },
});&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;공통 에러 처리&lt;/h4&gt;
&lt;pre id=&quot;code_1778224879679&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import ky, { HTTPError } from 'ky';

export const api = ky.create({
  prefixUrl: process.env.NEXT_PUBLIC_API_URL,
  hooks: {
    afterResponse: [
      async (_request, _options, response) =&amp;gt; {
        if (!response.ok) {
          const error = await response.json&amp;lt;{ message: string }&amp;gt;();
          // 전역 토스트 알림 등
          showToast(error.message ?? '요청에 실패했습니다.');
        }
      },
    ],
  },
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 요청에서만 에러를 직접 처리하고 싶다면 throwHttpErrors: false를 쓰면 됩니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;TypeScript와 함께 쓰기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;.json&amp;lt;T&amp;gt;()에 제네릭을 넘기면 타입이 바로 붙습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1778224915512&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface User {
  id: number;
  name: string;
  email: string;
}

// GET
const user = await api.get('users/1').json&amp;lt;User&amp;gt;();

// POST
const created = await api.post('users', {
  json: { name: '홍길동', email: 'hong@example.com' },
}).json&amp;lt;User&amp;gt;();

// 배열
const users = await api.get('users').json&amp;lt;User[]&amp;gt;();&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;마치며 &lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ky가 axios보다 무조건 낫다고 말하기는 어렵습니다. axios는 Node.js 구버전 지원, 업로드 진행률 추적 등 ky가 지원하지 않는 기능이 있고, 생태계도 훨씬 넓어요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 &lt;b&gt;모던 브라우저 환경, 번들 사이즈 최적화, ESM 기반 프로젝트&lt;/b&gt;라면 ky는 충분히 설득력 있는 선택입니다. axios를 관성적으로 써왔다면 한 번 전환을 고려해볼 만해요.&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;a href=&quot;https://github.com/team-kareer/kareer-client/pull/21&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/team-kareer/kareer-client/pull/21&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1778225044969&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;Init(web): Ky 세팅  by huniversal &amp;middot; Pull Request #21 &amp;middot; team-kareer/kareer-client&quot; data-og-description=&quot;  Summary [Init] Ky 서버 통신 라이브러리 세팅&amp;nbsp;#16   Tasks Ky 라이브러리 세팅 JSONPlaceholder Test 코드 실행 KY란? 네이티브 Fetch API를 기반으로 하는 가벼운 HTTP 클라이언트 라이브러리 핵심 특징 Fetch&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/team-kareer/kareer-client/pull/21&quot; data-og-url=&quot;https://github.com/team-kareer/kareer-client/pull/21&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/hSQeX/dJMb8QeqoM3/uTvfdhyoIqacJc0GbkZKUk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bh9o0M/dJMb8SpMcsu/RCMGvbWk3Z1F1TWNfkMZBk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/team-kareer/kareer-client/pull/21&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/team-kareer/kareer-client/pull/21&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/hSQeX/dJMb8QeqoM3/uTvfdhyoIqacJc0GbkZKUk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/bh9o0M/dJMb8SpMcsu/RCMGvbWk3Z1F1TWNfkMZBk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;Init(web): Ky 세팅 by huniversal &amp;middot; Pull Request #21 &amp;middot; team-kareer/kareer-client&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;  Summary [Init] Ky 서버 통신 라이브러리 세팅&amp;nbsp;#16   Tasks Ky 라이브러리 세팅 JSONPlaceholder Test 코드 실행 KY란? 네이티브 Fetch API를 기반으로 하는 가벼운 HTTP 클라이언트 라이브러리 핵심 특징 Fetch&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@antisdun/axios-vs-ky-%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4-%EA%B5%AC%ED%98%84&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@antisdun/axios-vs-ky-%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4-%EA%B5%AC%ED%98%84&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1778225062482&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;axios vs ky 인스턴스 구현&quot; data-og-description=&quot;ky를 사용한 이유 PWA 프로젝트를 진행하면서 서비스 워커에 대해 찾아보던 중, XHR 기반의 axios를 서비스 워커 내에서 사용하려면 별도의 폴리필이나 특수 구성이 필요하고 이로 인해 번들 크기를&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@antisdun/axios-vs-ky-%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4-%EA%B5%AC%ED%98%84&quot; data-og-url=&quot;https://velog.io/@antisdun/axios-vs-ky-인스턴스-구현&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cG5VFC/dJMb9gxpEeG/IY6ieTJzKuFnWhpatrwyxK/img.png?width=1285&amp;amp;height=670&amp;amp;face=0_0_1285_670,https://scrap.kakaocdn.net/dn/9IRX8/dJMb9kmgVj4/FaPQcfRZgqBqvB0nDsA9Q0/img.png?width=1285&amp;amp;height=670&amp;amp;face=0_0_1285_670,https://scrap.kakaocdn.net/dn/bVyAmj/dJMb9kT7boC/E8hzI0grrsAg2GWskg4iQk/img.jpg?width=2305&amp;amp;height=1729&amp;amp;face=0_0_2305_1729&quot;&gt;&lt;a href=&quot;https://velog.io/@antisdun/axios-vs-ky-%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4-%EA%B5%AC%ED%98%84&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@antisdun/axios-vs-ky-%EC%9D%B8%EC%8A%A4%ED%84%B4%EC%8A%A4-%EA%B5%AC%ED%98%84&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cG5VFC/dJMb9gxpEeG/IY6ieTJzKuFnWhpatrwyxK/img.png?width=1285&amp;amp;height=670&amp;amp;face=0_0_1285_670,https://scrap.kakaocdn.net/dn/9IRX8/dJMb9kmgVj4/FaPQcfRZgqBqvB0nDsA9Q0/img.png?width=1285&amp;amp;height=670&amp;amp;face=0_0_1285_670,https://scrap.kakaocdn.net/dn/bVyAmj/dJMb9kT7boC/E8hzI0grrsAg2GWskg4iQk/img.jpg?width=2305&amp;amp;height=1729&amp;amp;face=0_0_2305_1729');&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;axios vs ky 인스턴스 구현&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;ky를 사용한 이유 PWA 프로젝트를 진행하면서 서비스 워커에 대해 찾아보던 중, XHR 기반의 axios를 서비스 워커 내에서 사용하려면 별도의 폴리필이나 특수 구성이 필요하고 이로 인해 번들 크기를&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&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;</description>
      <author>taek2-0310</author>
      <guid isPermaLink="true">https://taek2-0310.tistory.com/8</guid>
      <comments>https://taek2-0310.tistory.com/8#entry8comment</comments>
      <pubDate>Fri, 8 May 2026 16:27:24 +0900</pubDate>
    </item>
    <item>
      <title>변수, 함수, 스코프 &amp;mdash; 엔진이 코드를 읽는 방식</title>
      <link>https://taek2-0310.tistory.com/7</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;선언과 할당이 분리되는 순간, 호이스팅이 시작된다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;var&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;부터&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;let/const&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;까지, 자바스크립트 실행 컨텍스트의 작동 원리를 파헤쳐보자.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;변수 선언과 할당, 그리고 호이스팅 &lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;자바스크립트 엔진은 코드를 실행하기 전,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;컴파일 단계&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;에서 먼저 변수 선언을 스캔한다. 이때&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;var&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;로 선언된 변수는 스코프 최상단으로 &quot;끌어올려진다(hoisted)&quot;. 그러나&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;선언&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;만 올라갈 뿐,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;할당&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;은 원래 자리에 그대로 남는다.&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;color: #185fa5; text-align: start;&quot;&gt;호이스팅&lt;/div&gt;
&lt;p style=&quot;color: #3d3d3a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;인터프리터가 코드를 실행하기 전에 함수, 변수, 클래스 또는 임포트(import)의 선언문을 해당 범위의 맨 위로 끌어올리는 것처럼 보이는 현상&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f1f1f; text-align: start;&quot;&gt;을 뜻합니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;color: #3d3d3a; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #1f1f1f; text-align: start;&quot;&gt;var의 호이스팅&lt;/span&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1777801983942&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 우리가 작성하는 코드
console.log(name); // 에러가 아니라 undefined
var name = &quot;Alice&quot;;
console.log(name); // &quot;Alice&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1777802002791&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;엔진이 실제로 처리하는 순서
[컴파일] var name; // 선언만 최상단으로 끌어올림, 값은 undefined
[런타임] console.log(name); // &amp;rarr; undefined
[런타임] name = &quot;Alice&quot;; // 이제서야 할당
[런타임] console.log(name); // &amp;rarr; &quot;Alice&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;이처럼&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;var&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;는 선언 전에 접근해도&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;ReferenceError&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;가 아닌&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;undefined&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;를 반환한다. 의도치 않은 동작을 유발하기 쉬운 이유다.&lt;/span&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1777802823050&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let의 호이스팅 처리 순서
[컴파일]name 선언 인식 // 초기화는 아직 안 함
[TDZ]console.log(name); // &amp;rarr; ReferenceError!
[런타임]let name = &quot;Alice&quot;; // 이 시점에 초기화 + 할당
[런타임]console.log(name); // &amp;rarr; &quot;Alice&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #141413; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;let 은 호이스팅되지만 TDZ에 갇힘&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;선언은 컴파일 단계에 인식되지만, 초기화는 실행 흐름이 선언문에 도달할 때까지 이루어지지 않는다. 그 사이 구간이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;TDZ(Temporal Dead Zone)&lt;/b&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;이고, 이 구간에 접근하면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;ReferenceError&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;가 발생한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1777802957366&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const x; // SyntaxError: Missing initializer
const y = 1; // OK
y = 2;      // TypeError: Assignment to constant variable&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #141413; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;const은 let과 동일하지만 선언 시 반드시 할당&lt;/p&gt;
&lt;p style=&quot;color: #141413; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;TDZ 동작은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;let&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;과 동일하다. 단,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;선언과 동시에 반드시 값을 할당&lt;/b&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;해야 하며, 이후 재할당이 불가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #141413; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Demilight', 'Noto Sans KR'; color: #006dd7;&quot;&gt;&lt;b&gt;셋 다 호이스팅은 일어난다. 차이는&amp;nbsp;초기화 시점이다.&amp;nbsp;var는 컴파일 단계에&amp;nbsp;undefined로 초기화되고,&amp;nbsp;let/const는 런타임에 선언문에 도달해야 초기화된다.&lt;/b&gt;&lt;/span&gt;&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;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;함수도 호이스팅된다. 그런데&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;함수 선언문&lt;/b&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;과&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;함수 표현식&lt;/b&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;은 완전히 다르게 동작한다. 함수 선언문은 함수 전체가 올라가고, 표현식은 변수 선언부만 올라간다. 표현식을 어떤 키워드(&lt;/span&gt;var&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;let&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;const&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;)로 선언하느냐에 따라 동작이 또 달라진다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1777803320713&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var &amp;mdash; undefined 반환
greet();
// TypeError:
// greet is not a function

var greet = function() {
  console.log(&quot;Hi&quot;);
};&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1777803424662&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let &amp;mdash; TDZ 에러
greet();
// ReferenceError:
// Cannot access
// before initialization

let greet = function() {
  console.log(&quot;Hi&quot;);
};&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1777803442628&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const &amp;mdash; TDZ 에러
greet();
// ReferenceError:
// Cannot access
// before initialization

const greet = function() {
  console.log(&quot;Hi&quot;);
};&lt;/code&gt;&lt;/pre&gt;
&lt;div style=&quot;background-color: #000000; color: #141413; text-align: start;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에러 종류가 다르다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;var&amp;nbsp;&lt;span&gt;표현식은&lt;/span&gt;&amp;nbsp;undefined&lt;span&gt;로&lt;/span&gt; &lt;span&gt;호이스팅되어&lt;/span&gt;&amp;nbsp;TypeError,&amp;nbsp;let/const&amp;nbsp;&lt;span&gt;표현식은&lt;/span&gt; TDZ&lt;span&gt;에&lt;/span&gt; &lt;span&gt;걸려&lt;/span&gt;&amp;nbsp;ReferenceError&lt;span&gt;가&lt;/span&gt; &lt;span&gt;발생한다&lt;/span&gt;.&lt;/p&gt;
&lt;pre id=&quot;code_1777804479903&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sayHi(); // TypeError: sayHi is not a function

const sayHi = () =&amp;gt; console.log(&quot;Hi&quot;);
// const는 아예 TDZ(일시적 사각지대)에 걸린다 (4섹션 참고)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화살표 함수도 동일하다.&lt;/p&gt;
&lt;pre id=&quot;code_1777803551561&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.log(typeof foo); // &quot;function&quot; (함수가 우선)

var foo = &quot;bar&quot;;
function foo() {}

console.log(typeof foo); // &quot;string&quot; (할당 후엔 변수값)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;또한 네이밍을 같을 경우 호이스팅 우선 순위는 변수보다 함수가 우선이다.&lt;/b&gt;&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;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;스코프는 변수가&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;접근 가능한 유효 범위&lt;/b&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;다. 자바스크립트는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;렉시컬(정적) 스코프&lt;/b&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;를 따른다. 함수가 어디서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;호출&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;되느냐가 아니라 어디서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;정의&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;되었느냐가 스코프를 결정한다.&lt;/span&gt;&lt;/p&gt;
&lt;div style=&quot;color: #ba7517; text-align: start;&quot;&gt;var의 스코프 누출&lt;/div&gt;
&lt;p style=&quot;color: #3d3d3a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;for,&lt;span&gt;&amp;nbsp;&lt;/span&gt;if,&lt;span&gt;&amp;nbsp;&lt;/span&gt;while&lt;span&gt;&amp;nbsp;&lt;/span&gt;블록 안의&lt;span&gt;&amp;nbsp;&lt;/span&gt;var는 블록을 벗어나도 살아있다. 함수 경계만 스코프로 인정하기 때문이다.&lt;/p&gt;
&lt;pre id=&quot;code_1777804052574&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for (var i = 0; i &amp;lt; 3; i++) {}
console.log(i); // 3 &amp;mdash; 블록 밖에서도 살아있음!

for (let j = 0; j &amp;lt; 3; j++) {}
console.log(j); // ReferenceError &amp;mdash; 블록 안에서만 유효&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: #141413; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;스코프 체인&lt;/h3&gt;
&lt;p style=&quot;color: #3d3d3a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;변수를 찾을 때 엔진은 현재 스코프에서 시작해 상위 스코프로 거슬러 올라간다. 이 탐색 경로를&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;스코프 체인&lt;/b&gt;이라 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1777804286238&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const a = &quot;전역&quot;;

function outer() {
  const b = &quot;outer&quot;;

  function inner() {
    const c = &quot;inner&quot;;
    console.log(a, b, c); // 셋 다 접근 가능
  }

  inner();
  console.log(c); // ReferenceError &amp;mdash; 상위에선 하위 접근 불가
}
outer();&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;let, const와 블록 레벨 스코프 &lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;ES6에서 등장한&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;let&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;과&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;const&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;블록&lt;span&gt;&amp;nbsp;&lt;/span&gt;{}을 스코프 단위로 인식&lt;/b&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;한다. var&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;가 함수 경계만 인정하는 것과 달리&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;if&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;/&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;for&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;/&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;while&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;블록도 독립된 스코프로 취급한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1777807655981&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let &amp;mdash; 재할당 가능
let count = 0;
count = 1; // OK
count++;   // OK&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1777807668380&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const &amp;mdash; 재할당 불가
const MAX = 100;
MAX = 200; // TypeError!

// 단, 객체 내부는 변경 가능
const obj = { x: 1 };
obj.x = 2; // OK (참조는 유지됨)&lt;/code&gt;&lt;/pre&gt;
&lt;div style=&quot;color: #0f6e56; text-align: start;&quot;&gt;권장 사용 전략&lt;/div&gt;
&lt;p style=&quot;color: #3d3d3a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;기본값으로&lt;span&gt;&amp;nbsp;&lt;/span&gt;const를 쓰고, 재할당이 필요한 경우만&lt;span&gt;&amp;nbsp;&lt;/span&gt;let을 쓴다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;var는 레거시 코드 유지 외에는 사용하지 않는다.&lt;/p&gt;
&lt;pre id=&quot;code_1777808418070&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// var: 모든 클로저가 같은 i를 참조
for (var i = 0; i &amp;lt; 3; i++) {
  setTimeout(() =&amp;gt; console.log(i), 100);
}
// 출력: 3, 3, 3

// let: 각 반복마다 새로운 i가 생성됨
for (let i = 0; i &amp;lt; 3; i++) {
  setTimeout(() =&amp;gt; console.log(i), 100);
}
// 출력: 0, 1, 2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;var는 함수 스코프를 가지기 때문에 반복문이 끝난 후 하나의 동일한 변수 i를 참조한다. 따라서 setTimeout이 실행될 시점에는 이미 i가 3이 되어 있어 모든 출력이 3이 된다.&lt;br /&gt;반면 let은 블록 스코프를 가지며, 반복문이 실행될 때마다 새로운 i가 생성된다. 이로 인해 각 setTimeout은 서로 다른 i를 참조하게 되어 0, 1, 2가 순서대로 출력된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1330&quot; data-origin-height=&quot;586&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ya1SW/dJMcafsU8dY/YKUVzcDkjxexm4tRPO4UwK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ya1SW/dJMcafsU8dY/YKUVzcDkjxexm4tRPO4UwK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ya1SW/dJMcafsU8dY/YKUVzcDkjxexm4tRPO4UwK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYa1SW%2FdJMcafsU8dY%2FYKUVzcDkjxexm4tRPO4UwK%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;1330&quot; height=&quot;586&quot; data-origin-width=&quot;1330&quot; data-origin-height=&quot;586&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #141413; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #141413; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;내가 든 생각!❓&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;let과 const도 호이스팅이 일어난다고 하는데 어차피 TDZ 때문에 초기화 전에는 접근조차 할 수 없잖아?.. 그렇다면 let/const의 호이스팅은 실질적으로 어떤 의미가 있지?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딥다이브 해봤는데 let/const의 호이스팅은 &quot;변수가 존재한다는 사실&quot;을 미리 등록하는 것이고&lt;b&gt;&amp;nbsp;중요한 것은 값을 쓰려고 호이스팅하는 게 아니라, 스코프를 확정짓기 위해 호이스팅하는 것이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트는 위에서 말했듯 렉시컬(정적) 스코프&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;를 따른다. 함수가 어디서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;호출&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;되느냐가 아니라 어디서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;정의&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;되었느냐가 스코프를 결정한다. 따라서 엔진은 실행 전에 스코프 안의 let/const 선언을 미리 인식해 &quot;이 변수는 이 스코프 소속이다&quot;를 확정해둔다. 그래야 렉시컬 스코프 규칙대로 올바른 스코프 체인을 구성할 수 있기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1777809016786&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let x = &quot;전역&quot;;

function foo() {
  console.log(x); // ReferenceError!
  let x = &quot;지역&quot;;
}

foo();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 let이 호이스팅이 안 됐다면 console.log(x)는 스코프 체인을 타고 올라가 전역의 &quot;전역&quot;을 출력했을 것이다. 그러나 실제로는 ReferenceError가 발생한다. let x가 함수 스코프 안에서 호이스팅되어 &quot;이 스코프에 x가 있다&quot;고 이미 등록되었기 때문에, 엔진은 상위 스코프를 탐색하지 않고 현재 스코프의 x를 참조하려다 TDZ에 걸리는 것이다.&lt;/p&gt;</description>
      <author>taek2-0310</author>
      <guid isPermaLink="true">https://taek2-0310.tistory.com/7</guid>
      <comments>https://taek2-0310.tistory.com/7#entry7comment</comments>
      <pubDate>Sun, 3 May 2026 21:02:14 +0900</pubDate>
    </item>
    <item>
      <title>Custom Hook 그거 굳이 써야 해?? ㅡ Custom Hook 제대로 설계하기</title>
      <link>https://taek2-0310.tistory.com/6</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;들어가며  &lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;React를 어느 정도 써본 개발자라면 Custom Hook이 낯설지 않을 겁니다. 그런데 &quot;잘 쓰고 있어?&quot;라고 누군가 묻는다면, 선뜻 대답하기가 어렵습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;Hook을 만들어본 적 있다면 이런 고민 한 번쯤 해봤을 겁니다. &quot;이거 굳이 Hook으로 빼야 하나?&quot; &quot;너무 많은 걸 한 Hook에 넣은 건 아닌가?&quot; 그리고 더 근본적으로는 &amp;mdash; 어떻게 해야 잘 쓰는 건지조차 잘 모르겠다는 느낌.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;오늘은 그 고민을 조금 덜어보고자 합니다. Custom Hook을 언제 만들어야 하는지, 어떤 실수를 피해야 하는지, 그리고 좋은 Hook이란 어떤 모습인지를 컴포넌트의 목적과 책임이라는 관점에서 풀어보겠습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Custom Hook?  &lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;커스텀 훅이란 개발자가 직접 만든 hook으로 몇가지 규칙이 존재한다. &lt;a title=&quot;React 공식 홈페이지&quot; href=&quot;https://ko.legacy.reactjs.org/docs/hooks-rules.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;React 공식 홈페이지&lt;/a&gt; 내용에 따르면 hook은&lt;/span&gt;&lt;/p&gt;
&lt;h4 id=&quot;--%--%EC%B-%-C%EC%--%--%EC%-C%---at%--the%--Top%--Level-%EC%--%--%EC%--%-C%EB%A-%-C%--Hook%EC%-D%--%--%ED%--%B-%EC%B-%-C%ED%--%B-%EC%--%BC%--%ED%--%-C%EB%-B%A--&quot; style=&quot;background-color: #ffffff; color: #333333; text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://leego.tistory.com/entry/React-Custom-hook%EC%9D%84-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EC%A0%84%EC%97%90-%EA%B3%A0%EB%A0%A4%ED%95%B4%EC%95%BC-%ED%95%A0-%EA%B2%83%EB%93%A4#--%--%EC%B-%-C%EC%--%--%EC%-C%---at%--the%--Top%--Level-%EC%--%--%EC%--%-C%EB%A-%-C%--Hook%EC%-D%--%--%ED%--%B-%EC%B-%-C%ED%--%B-%EC%--%BC%--%ED%--%-C%EB%-B%A--&quot;&gt;1. 최상위(at the Top Level)에서만 Hook을 호출해야 한다.&lt;/a&gt;&lt;/h4&gt;
&lt;p style=&quot;font-family: 'Noto Sans Light'; background-color: #ffffff; color: #000000; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;반복문, 조건문 혹은 중첩된 함수 내에서 Hook을 호출하지 않아야 한다.&lt;br /&gt;이 규칙을 따르면 컴포넌트가 렌더링 될 때마다 항상 동일한 순서로 Hook이 호출되는 것이 보장된다. 이러한 점은 React가 useState와 useEffect 가 여러 번 호출되는 중에도 Hook의 상태를 올바르게 유지할 수 있도록 해준다.&lt;/p&gt;
&lt;h4 id=&quot;--%--%EC%--%A-%EC%A-%--%--React%--%ED%--%A-%EC%--%--%--%EB%--%B-%EC%--%--%EC%--%-C%--Hook%EC%-D%--%--%ED%--%B-%EC%B-%-C%ED%--%B-%EC%--%BC%--%ED%--%-C%EB%-B%A--&quot; style=&quot;background-color: #ffffff; color: #333333; text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://leego.tistory.com/entry/React-Custom-hook%EC%9D%84-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EC%A0%84%EC%97%90-%EA%B3%A0%EB%A0%A4%ED%95%B4%EC%95%BC-%ED%95%A0-%EA%B2%83%EB%93%A4#--%--%EC%--%A-%EC%A-%--%--React%--%ED%--%A-%EC%--%--%--%EB%--%B-%EC%--%--%EC%--%-C%--Hook%EC%-D%--%--%ED%--%B-%EC%B-%-C%ED%--%B-%EC%--%BC%--%ED%--%-C%EB%-B%A--&quot;&gt;2. 오직 React 함수 내에서 Hook을 호출해야 한다.&lt;/a&gt;&lt;/h4&gt;
&lt;p style=&quot;font-family: 'Noto Sans Light'; background-color: #ffffff; color: #000000; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;이 규칙을 지키면 컴포넌트의 모든 상태 관련 로직을 소스코드에서 명확하게 보이도록 할 수 있다.&lt;/p&gt;
&lt;p style=&quot;font-family: 'Noto Sans Light'; background-color: #ffffff; color: #000000; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;font-family: 'Noto Sans Light'; background-color: #ffffff; color: #000000; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;뿐만 아니라 custom hook을 사용할 때는 use로 시작해야한다는 네이밍 규칙도 존재합니다.&lt;/p&gt;
&lt;h2 style=&quot;font-family: 'Noto Sans Light'; background-color: #ffffff; color: #000000; text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Custom Hook, 언제 만들어야 할까? &lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;Custom Hook을 만드는 기준은 크게 두 가지 관점으로 나눌 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #141413; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;1: 로직이 반복될 때&lt;/h4&gt;
&lt;h4 style=&quot;color: #3d3d3a; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;가장 직관적인 기준입니다. 같은&lt;span&gt;&amp;nbsp;&lt;/span&gt;useState + useEffect&lt;span&gt;&amp;nbsp;&lt;/span&gt;패턴이 두 곳 이상에서 반복된다면 Hook으로 분리할 타이밍입니다. (React 공식홈페이지 예시)&lt;/h4&gt;
&lt;pre id=&quot;code_1777359354572&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//FriendStatus : 친구가 online인지 offline인지 return하는 컴포넌트
function FriendStatus(props) {
  const [isOnline, setIsOnline] = useState(null);
  useEffect(() =&amp;gt; {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () =&amp;gt; {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}

//FriendListItem : 친구가 online일 때 초록색으로 표시하는 컴포넌트
function FriendListItem(props) {
  const [isOnline, setIsOnline] = useState(null);
  useEffect(() =&amp;gt; {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () =&amp;gt; {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  return (
    &amp;lt;li style={{ color: isOnline ? 'green' : 'black' }}&amp;gt;
      {props.friend.name}
    &amp;lt;/li&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 두 컴포넌트에서 공통으로 사용되는 로직을 분리하여 useFriendStatus 함수로 만들면 코드 재사용성을 높일 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1777359699554&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function FriendStatus(props) {
  const isOnline = useFriendStatus(props.friend.id);

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}

function FriendListItem(props) {
  const isOnline = useFriendStatus(props.friend.id);

  return (
    &amp;lt;li style={{ color: isOnline ? 'green' : 'black' }}&amp;gt;
      {props.friend.name}
    &amp;lt;/li&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 공통 로직을 커스텀 훅으로 분리하면 코드 재사용성을 높일 수 있고 중복 코드를 줄여 전체 코드량을 줄일 수 있습니다.&lt;br /&gt;또한 로직과 UI를 분리할 수 있어 가독성과 유지보수성이 좋아지고 여러 컴포넌트에서 동일한 로직을 일관되게 사용할 수 있다는 장점이 있습니다.&lt;/p&gt;
&lt;h4 style=&quot;color: #141413; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;관점 2: 관심사를 분리할 때&lt;/h4&gt;
&lt;p style=&quot;color: #3d3d3a; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;반복이 없더라도 한 컴포넌트 안에 서로 다른 관심사의 로직이 섞여 있다면 분리할 수 있습니다. 컴포넌트가&lt;span&gt;&amp;nbsp;&lt;/span&gt;&quot;무엇을 렌더링하는가&quot;에만 집중하도록 만드는 것이 목표입니다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #3d3d3a; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #3d3d3a; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;컴포넌트는 본래 &lt;b&gt;&quot;무엇을 렌더링할 것인가&quot;&lt;/b&gt; 라는 하나의 책임을 가집니다. 그런데 데이터를 불러오는 로직, 상태를 관리하는 로직, 이벤트를 처리하는 로직이 한 컴포넌트 안에 뒤섞이다 보면 그 책임의 경계가 무너집니다. 컴포넌트가 렌더링도 하고, 데이터도 가져오고, 상태도 관리하는 모든 것을 다 떠안는 존재가 되어버리는 거죠. 또한 코드를 읽는 사람 입장에서도 &quot;이 컴포넌트가 뭘 하는 컴포넌트지?&quot;라는 질문에 바로 답하기 어려워집니다.&lt;/p&gt;
&lt;p style=&quot;color: #3d3d3a; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이때 Custom Hook으로 로직을 분리하면 각자의 책임이 선명해집니다. 컴포넌트는 화면을 그리는 일만, Hook은 그에 필요한 로직을 처리하는 일만 담당하게 됩니다. 책임이 나뉜 코드는 읽기도 쉽고 고치기도 쉽습니다. &quot;UI를 바꿔야 한다&quot;면 컴포넌트만, &quot;데이터 처리 방식을 바꿔야 한다&quot;면 Hook만 건드리면 되니까요.&lt;/p&gt;
&lt;pre id=&quot;code_1777360615519&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function useInputState(
  initialValue: string = '',
  transformValue: (value: string) =&amp;gt; string = (v: string) =&amp;gt; v
): [value: string, onChange: ChangeEventHandler&amp;lt;HTMLInputElement&amp;gt;];&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1777360635052&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function Example() {
  const [value, onChange] = useInputState('');
  return &amp;lt;input type=&quot;text&quot; value={value} onChange={onChange} /&amp;gt;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 &lt;a title=&quot;토스의 오픈소스 라이브러리 react-simplikit의 useInputState&quot; href=&quot;https://react-simplikit.slash.page/ko/core/hooks/useInputState.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;토스의 오픈소스 라이브러리 react-simplikit의 useInputState&lt;/a&gt; 입니다.&amp;nbsp; 이렇게 사용했을 때 이름만 봐도 input의 상태를 관리하는 Hook임을 바로 알 수 있고 input의 상태 관리라는 하나의 책임만 가지고 있기 때문에 이를 사용하는 컴포넌트는 값을 어떻게 관리할지 고민할 필요 없이 렌더링에만 집중할 수 있습니다.&lt;/p&gt;
&lt;h3 style=&quot;color: #141413; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;만들지 말아야 할 경우도 있다&lt;/h3&gt;
&lt;p style=&quot;color: #3d3d3a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Hook이 단순히&lt;span&gt;&amp;nbsp;&lt;/span&gt;useState&lt;span&gt;&amp;nbsp;&lt;/span&gt;하나만 감싸거나, 한 곳에서만 쓰이는 데 복잡하지도 않다면 굳이 Hook으로 분리할 필요가 없습니다. 추상화는 비용입니다.&lt;/p&gt;
&lt;h2 style=&quot;color: #3d3d3a; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;흔히 하는 실수들 &lt;/h2&gt;
&lt;p style=&quot;color: #141413; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;실수 1: 너무 많은 걸 한 Hook에 몰아넣기&lt;/p&gt;
&lt;p style=&quot;color: #3d3d3a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Hook을 만들다 보면 &quot;이것도 넣고, 저것도 넣고&quot;가 되기 쉽습니다. 그러다 보면 재사용할 수 없는 Hook이 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1777427518556&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function useUserDashboard() {
  // 유저 정보 fetch
  // 알림 목록 fetch
  // 모달 열림 상태
  // 탭 선택 상태
  // ... 다 여기에
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #141413; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #141413; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;실수 2: 의존성 배열 관리 실수&lt;/p&gt;
&lt;p style=&quot;color: #3d3d3a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Hook 내부에서&lt;span&gt;&amp;nbsp;&lt;/span&gt;useEffect를 쓸 때 의존성 배열을 잘못 관리하면 무한 렌더링이나 오래된 값(stale closure) 문제가 생깁니다.&lt;/p&gt;
&lt;pre id=&quot;code_1777427730972&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;useEffect(() =&amp;gt; {
  fetchData(options); // options가 객체라면?
}, [options]); // 매 렌더마다 새 객체 &amp;rarr; 무한 호출&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #141413; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;실수 3: 반환값 설계 실수&lt;/p&gt;
&lt;p style=&quot;color: #3d3d3a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;반환값에 이름이 없거나 일관성이 없으면 사용하는 쪽에서 혼란이 생깁니다. 다음 섹션에서 자세히 다룹니다.&lt;/p&gt;
&lt;h2 style=&quot;color: #3d3d3a; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;좋은 Hook의 인터페이스 설계 &lt;/h2&gt;
&lt;p style=&quot;color: #141413; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;배열 vs 객체, 언제 어떤 걸 쓸까?&lt;/p&gt;
&lt;p style=&quot;color: #3d3d3a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;useState처럼 배열을 반환하면 사용하는 쪽에서 이름을 자유롭게 지을 수 있어 여러 번 사용하기 좋습니다. 반면 반환값이 3개 이상이 되면 객체가 훨씬 읽기 편합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1777428138103&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 2개 이하 &amp;rarr; 배열도 괜찮음 (useState 패턴)
const [isOpen, setIsOpen] = useToggle(false);

// 3개 이상 &amp;rarr; 객체로
const { data, loading, error, refetch } = useFetch(url);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #141413; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;네이밍 컨벤션&lt;/p&gt;
&lt;p style=&quot;color: #3d3d3a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Hook 이름은&lt;span&gt;&amp;nbsp;&lt;/span&gt;use로 시작하는 건 기본이고, 그 다음이 중요합니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;무엇을 하는지가 이름에 드러나야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1777428167785&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 모호한 이름
useData(), useHandler(), useHelper()

// 명확한 이름
useFetch(url)
useLocalStorage(key, defaultValue)
useIntersectionObserver(ref, options)&lt;/code&gt;&lt;/pre&gt;
&lt;div style=&quot;color: #000000; text-align: start;&quot;&gt;
&lt;p style=&quot;color: #141413;&quot; data-ke-size=&quot;size18&quot;&gt;재사용성과 유연성의 트레이드오프&lt;/p&gt;
&lt;p style=&quot;color: #3d3d3a;&quot; data-ke-size=&quot;size16&quot;&gt;Hook을 너무 유연하게 만들려다 보면 옵션 파라미터가 폭발적으로 늘어납니다. 반대로 너무 구체적으로 만들면 재사용이 어렵습니다.&lt;/p&gt;
&lt;p style=&quot;color: #3d3d3a;&quot; data-ke-size=&quot;size16&quot;&gt;좋은 기준은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;지금 필요한 것만 파라미터로&lt;/b&gt;&amp;nbsp;나중에 필요해지면 그때 확장하는 것입니다. 미리 유연하게 만들어둔 Hook은 대부분 복잡도만 높아집니다.&lt;/p&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Hook의 파라미터가 5개를 넘어간다면, 객체 하나로 받는 것을 고려하세요. 그리고 정말 이게 하나의 Hook이어야 하는지 다시 생각해보는 것도 좋습니다.&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;마치며 &amp;mdash; 체크리스트&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #3d3d3a; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Custom Hook을 만들기 전후로 이 체크리스트를 확인해보세요.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;이 Hook이 하나의 관심사만 다루고 있는가?&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;Hook 이름만 보고 무엇을 하는지 알 수 있는가?&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;반환값이 3개 이상이면 객체로 반환하고 있는가?&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;useEffect 의존성 배열에 객체나 함수를 직접 넣지 않는가?&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;이 Hook 없이도 간단하게 해결되는 문제는 아닌가?&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;참고 &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;&lt;span style=&quot;color: #3d3d3a; text-align: start;&quot;&gt;&lt;a href=&quot;https://leego.tistory.com/entry/React-Custom-hook%EC%9D%84-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EC%A0%84%EC%97%90-%EA%B3%A0%EB%A0%A4%ED%95%B4%EC%95%BC-%ED%95%A0-%EA%B2%83%EB%93%A4&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://leego.tistory.com/entry/React-Custom-hook%EC%9D%84-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EC%A0%84%EC%97%90-%EA%B3%A0%EB%A0%A4%ED%95%B4%EC%95%BC-%ED%95%A0-%EA%B2%83%EB%93%A4&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1777428444622&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;[React] Custom hook을 만들기 전에 고려해야 할 것들&quot; data-og-description=&quot;들어가면서  Building your own Hooks lets you extract component logic into reusable functions. 최근 내가 컴포넌트를 설계할 때 가장 중요하게 생각하는 것은 컴포넌트의 역할이 명확하게 나타나는가 이다. 예를 &quot; data-og-host=&quot;leego.tistory.com&quot; data-og-source-url=&quot;https://leego.tistory.com/entry/React-Custom-hook%EC%9D%84-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EC%A0%84%EC%97%90-%EA%B3%A0%EB%A0%A4%ED%95%B4%EC%95%BC-%ED%95%A0-%EA%B2%83%EB%93%A4&quot; data-og-url=&quot;https://leego.tistory.com/entry/React-Custom-hook%EC%9D%84-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EC%A0%84%EC%97%90-%EA%B3%A0%EB%A0%A4%ED%95%B4%EC%95%BC-%ED%95%A0-%EA%B2%83%EB%93%A4&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cjlsQy/dJMb9kmfRnS/e3yFzba5m2Q0mQaflvuUW0/img.jpg?width=800&amp;amp;height=420&amp;amp;face=0_0_800_420,https://scrap.kakaocdn.net/dn/mONVh/dJMb9jgAqwl/JZGLh30tWstfjhg0KGhXEk/img.jpg?width=800&amp;amp;height=420&amp;amp;face=0_0_800_420,https://scrap.kakaocdn.net/dn/rqmcJ/dJMb9aKIdZd/FXZ6FXJWaCdHGCHGdBRRck/img.jpg?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://leego.tistory.com/entry/React-Custom-hook%EC%9D%84-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EC%A0%84%EC%97%90-%EA%B3%A0%EB%A0%A4%ED%95%B4%EC%95%BC-%ED%95%A0-%EA%B2%83%EB%93%A4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://leego.tistory.com/entry/React-Custom-hook%EC%9D%84-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EC%A0%84%EC%97%90-%EA%B3%A0%EB%A0%A4%ED%95%B4%EC%95%BC-%ED%95%A0-%EA%B2%83%EB%93%A4&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cjlsQy/dJMb9kmfRnS/e3yFzba5m2Q0mQaflvuUW0/img.jpg?width=800&amp;amp;height=420&amp;amp;face=0_0_800_420,https://scrap.kakaocdn.net/dn/mONVh/dJMb9jgAqwl/JZGLh30tWstfjhg0KGhXEk/img.jpg?width=800&amp;amp;height=420&amp;amp;face=0_0_800_420,https://scrap.kakaocdn.net/dn/rqmcJ/dJMb9aKIdZd/FXZ6FXJWaCdHGCHGdBRRck/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;[React] Custom hook을 만들기 전에 고려해야 할 것들&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;들어가면서  Building your own Hooks lets you extract component logic into reusable functions. 최근 내가 컴포넌트를 설계할 때 가장 중요하게 생각하는 것은 컴포넌트의 역할이 명확하게 나타나는가 이다. 예를&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;leego.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ko.legacy.reactjs.org/docs/hooks-rules.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://ko.legacy.reactjs.org/docs/hooks-rules.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1777428446774&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;Hook의 규칙 &amp;ndash; React&quot; data-og-description=&quot;A JavaScript library for building user interfaces&quot; data-og-host=&quot;ko.legacy.reactjs.org&quot; data-og-source-url=&quot;https://ko.legacy.reactjs.org/docs/hooks-rules.html&quot; data-og-url=&quot;https://ko.legacy.reactjs.org/docs/hooks-rules.html&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bdwbUc/dJMb8RRU0wg/EL85hkFrNxPdpdezKMPYL0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://ko.legacy.reactjs.org/docs/hooks-rules.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ko.legacy.reactjs.org/docs/hooks-rules.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bdwbUc/dJMb8RRU0wg/EL85hkFrNxPdpdezKMPYL0/img.png?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;Hook의 규칙 &amp;ndash; React&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;A JavaScript library for building user interfaces&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ko.legacy.reactjs.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ko.legacy.reactjs.org/docs/hooks-custom.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://ko.legacy.reactjs.org/docs/hooks-custom.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1777428453502&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;자신만의 Hook 만들기 &amp;ndash; React&quot; data-og-description=&quot;A JavaScript library for building user interfaces&quot; data-og-host=&quot;ko.legacy.reactjs.org&quot; data-og-source-url=&quot;https://ko.legacy.reactjs.org/docs/hooks-custom.html&quot; data-og-url=&quot;https://ko.legacy.reactjs.org/docs/hooks-custom.html&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dqymj5/dJMb88e3OzD/HfErkN2nuqMQ1OHlVqGhCK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://ko.legacy.reactjs.org/docs/hooks-custom.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ko.legacy.reactjs.org/docs/hooks-custom.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dqymj5/dJMb88e3OzD/HfErkN2nuqMQ1OHlVqGhCK/img.png?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;자신만의 Hook 만들기 &amp;ndash; React&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;A JavaScript library for building user interfaces&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ko.legacy.reactjs.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@oka1313/React-Custom-Hooks&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@oka1313/React-Custom-Hooks&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1777428473809&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;[React] Custom Hooks&quot; data-og-description=&quot;Custom Hooks이란 개발자가 스스로 커스텀한 훅을 의미한다. 이를 이용해 반복되는 로직을 함수로 뽑아내어 재사용할 수 있다.&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@oka1313/React-Custom-Hooks&quot; data-og-url=&quot;https://velog.io/@oka1313/React-Custom-Hooks&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dSGIjk/dJMb83Sl2Or/q0rSm1VdLSe8NkmOa9gaF1/img.png?width=2250&amp;amp;height=1182&amp;amp;face=0_0_2250_1182,https://scrap.kakaocdn.net/dn/bKygwk/dJMb81fVLzy/FoEjAqiHFVbzIioiyla2i0/img.png?width=2250&amp;amp;height=1182&amp;amp;face=0_0_2250_1182,https://scrap.kakaocdn.net/dn/bbkC6Z/dJMb9g5efDF/1aSRnfpNuilgylvtuX1ih0/img.png?width=2250&amp;amp;height=1182&amp;amp;face=0_0_2250_1182&quot;&gt;&lt;a href=&quot;https://velog.io/@oka1313/React-Custom-Hooks&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@oka1313/React-Custom-Hooks&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dSGIjk/dJMb83Sl2Or/q0rSm1VdLSe8NkmOa9gaF1/img.png?width=2250&amp;amp;height=1182&amp;amp;face=0_0_2250_1182,https://scrap.kakaocdn.net/dn/bKygwk/dJMb81fVLzy/FoEjAqiHFVbzIioiyla2i0/img.png?width=2250&amp;amp;height=1182&amp;amp;face=0_0_2250_1182,https://scrap.kakaocdn.net/dn/bbkC6Z/dJMb9g5efDF/1aSRnfpNuilgylvtuX1ih0/img.png?width=2250&amp;amp;height=1182&amp;amp;face=0_0_2250_1182');&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;[React] Custom Hooks&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Custom Hooks이란 개발자가 스스로 커스텀한 훅을 의미한다. 이를 이용해 반복되는 로직을 함수로 뽑아내어 재사용할 수 있다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;</description>
      <author>taek2-0310</author>
      <guid isPermaLink="true">https://taek2-0310.tistory.com/6</guid>
      <comments>https://taek2-0310.tistory.com/6#entry6comment</comments>
      <pubDate>Wed, 29 Apr 2026 11:09:21 +0900</pubDate>
    </item>
    <item>
      <title>나는 누구? 여긴 어디? &amp;mdash; JavaScript this 완전 정복</title>
      <link>https://taek2-0310.tistory.com/5</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;this란?  &lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;this는 &lt;b&gt;자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수&lt;/b&gt;다. 가장 일반적으로는 객체의 메서드 안에서 사용되며 이를 통해 동일한 메서드를 서로 다른 객체에서 재사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;일반 변수는 선언된 위치(렉시컬 스코프)에 의해 값이 결정되지만 this는 다르다. &lt;b&gt;함수가 어떤 방식으로 호출(바인딩)되었는지&lt;/b&gt;에 따라 가리키는 값이 동적으로 결정된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;쉽게 비유하자면 &quot;나&quot;라는 단어와 같다. &quot;나는 밥을 먹었다&quot;에서 '나'가 누구인지는 그 말을 누가 했느냐에 따라 달라지는 것처럼 this도 누가(어떤 컨텍스트가) 함수를 호출했느냐에 따라 달라진다.&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;this가 결정되는 4가지 규칙  &lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;1. 기본 바인딩&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;아무런 컨텍스트 없이 함수를 단독 호출하면 &lt;b&gt;this는 전역 객체&lt;/b&gt;를 가리킨다. &lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;그리고 이 this는 브라우저에서 &lt;b&gt;window 객체&lt;/b&gt;가 된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1776773654579&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function greet() {
  console.log(this); // window (브라우저)
}

greet();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;2. 암시적 바인딩&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;객체의 메서드로 호출되면 this는 &lt;b&gt;그 객체&lt;/b&gt;를 가리킨다.&lt;/p&gt;
&lt;pre id=&quot;code_1776773722263&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const user = {
  name: '철수',
  greet() {
    console.log(this.name); // '철수'
  }
};

user.greet(); // this === user&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;3. 명시적 바인딩&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #212529; text-align: start;&quot;&gt;함수가 호출될 때 apply, call 또는 bind가 사용되었다면 첫번째 인자로 전달하는 값에 this 를 바인딩 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;bind는 새로운 함수를 반환하며 this를 영구 고정한다는 점에서 call/apply와 다르다.&lt;/p&gt;
&lt;pre id=&quot;code_1776773952926&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;jsfunction greet() {
  console.log(this.name);
}

const user = { name: '철수' };

greet.call(user);        // '철수'
greet.apply(user);       // '철수'
const bound = greet.bind(user);
bound();                 // '철수'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;4. new 바인딩&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;new 키워드로 생성자 함수를 호출하면, this는 &lt;b&gt;새로 생성되는 인스턴스 객체&lt;/b&gt;를 가리킨다&lt;/p&gt;
&lt;pre id=&quot;code_1776774374806&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function Person(name) {
  this.name = name; // 새 인스턴스에 바인딩
}

const p = new Person('민준');
console.log(p.name); // '민준'&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;❗️우선순위: new &amp;gt; 명시적(bind) &amp;gt; 암시적(메서드) &amp;gt; 기본❗️&lt;/b&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;화살표 함수의 this &amp;mdash; 완전히 다른 규칙❓&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화살표 함수는 this를 &lt;b&gt;아예 갖지 않는다.&lt;/b&gt; 대신 자신이 선언된 위치의 외부 스코프 this를 그대로 캡처한다.&lt;b&gt;(클로저)&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1776775231268&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const person = {
	name : 'Seo',
    sayName : function() {
    	innerFun = function() {
        	return `안녕하세요 ${this.name}님`
         }
       console.log(innerFun())  /// 안녕하세요 ''님
    }
 }
 
 person.sayName()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f8f9fa; color: #212529; text-align: start;&quot;&gt;innerFun 함수 앞에 마침표(.) 을 붙여서 호출하지도 않았고, bind, call, apply 를 사용하지도 않았다. 일반 함수 호출 되었기 때문에 여기서 this 는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;window&lt;span style=&quot;background-color: #f8f9fa; color: #212529; text-align: start;&quot;&gt;가 된다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1776775267367&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const person = {
	name : 'Seo',
    sayName : function() {
    	innerFun =() =&amp;gt; {
        	return `안녕하세요 ${this.name}님` /// 안녕하세요 Seo님
         }
       console.log(innerFun())
    }
 }
 
 person.sayName()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f8f9fa; color: #212529; text-align: start;&quot;&gt;여기서도 innerFun 은 일반함수로 호출 되었으나 innerFun 이 화살표 함수로 선언이 되어 있다. 화살표 함수에서의 this 는 자신의 상위 스코프를 따르기 때문에 여기서 this 는 person 객체 안에 선언된 name 이 된다.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f8f9fa; color: #212529; text-align: start;&quot;&gt;그래서 this 왜 써? &lt;span style=&quot;background-color: #f8f9fa; color: #212529; text-align: start;&quot;&gt;&amp;mdash; &lt;/span&gt;this vs 일반 변수  &lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f8f9fa; color: #212529; text-align: start;&quot;&gt;일반 변수(클로저)&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1776775687581&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function makeUser(name) {
  let _name = name; // 그냥 변수

  return {
    getName() { return _name; },
    setName(n) { _name = n; }
  };
}

const user1 = makeUser('철수');
const user2 = makeUser('영희');&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;span style=&quot;color: #8100c2;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;users&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #14181f;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #b34a00;&quot;&gt;Array&lt;/span&gt;&lt;span style=&quot;color: #2b303b;&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: #8100c2;&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color: #2b303b;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #2b303b;&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #b80a18;&quot;&gt;length&lt;/span&gt;&lt;span style=&quot;color: #14181f;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #008080;&quot;&gt;1000&lt;/span&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #2b303b;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #2b303b;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #2b303b;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #b84f05;&quot;&gt;_&lt;/span&gt;&lt;span style=&quot;color: #2b303b;&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color: #b84f05;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;i&lt;/span&gt;&lt;span style=&quot;color: #2b303b;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #14181f;&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color: #212529; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #0051c2;&quot;&gt;makeUser&lt;/span&gt;&lt;span style=&quot;color: #2b303b;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #008000;&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color: #008000;&quot;&gt;user&lt;/span&gt;&lt;span style=&quot;color: #2b303b;&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color: #008000;&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;color: #2b303b;&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color: #008000;&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color: #2b303b;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #2b303b;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #2b303b;&quot;&gt;;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 &lt;b&gt;유저가 1000명&lt;/b&gt;이면? 함수 내용은 완전히 똑같은데 getName, setName 함수가 &lt;b&gt;1000개&lt;/b&gt; 메모리에 생긴다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1776775823712&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function User(name) {
  this.name = name;
}

User.prototype.getName = function() {
  return this.name; // this가 각 인스턴스를 가리킴
};

const users = Array.from({ length: 1000 }, (_, i) =&amp;gt; new User(`user${i}`));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;getName 함수는 메모리에 &lt;b&gt;딱 1개&lt;/b&gt;만 존재하고, 1000개의 인스턴스가 공유한다. this 덕분에 &quot;내가 지금 어느 인스턴스인지&quot;를 호출 시점에 알 수 있다.&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;/p&gt;
&lt;pre id=&quot;code_1776776197892&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 클로저 방식 &amp;mdash; Admin을 만들려면?
function makeAdmin(name) {
  let _name = name;
  return {
    getName() { return _name; },
    kickUser() { console.log(`${_name}이 유저를 추방`); }
    // getName을 또 써야 함 &amp;mdash; 재사용 불가
  };
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1776776212158&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// this 방식 &amp;mdash; 상속으로 깔끔하게 확장
class User {
  constructor(name) { this.name = name; }
  getName() { return this.name; }
}

class Admin extends User {
  kickUser() { console.log(`${this.name}이 유저를 추방`); }
  // getName은 그냥 물려받음
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;this가 단점이 존재하지 않는건 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;1. 콜백 지옥에서의 바인딩 소실&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;2. 이벤트 리스너로 넘기면 this가 소실&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;3. 같은 함수라도 호출 방식에 따라 this가 달라지기 때문에, 코드를 처음 보는 사람은 this가 무엇인지 추적하기 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;그래도&lt;span&gt; &lt;/span&gt;메서드를&lt;span&gt; &lt;/span&gt;효율적으로&lt;span&gt; &lt;/span&gt;재사용할&lt;span&gt; &lt;/span&gt;수&lt;span&gt; &lt;/span&gt;있고&lt;span&gt;, &lt;/span&gt;객체&lt;span&gt; &lt;/span&gt;단위로&lt;span&gt; &lt;/span&gt;상태를&lt;span&gt; &lt;/span&gt;관리할&lt;span&gt; &lt;/span&gt;수&lt;span&gt; &lt;/span&gt;있다는&lt;span&gt; &lt;/span&gt;점에서&lt;span&gt; &lt;/span&gt;충분히&lt;span&gt; &lt;/span&gt;사용할&lt;span&gt; &lt;/span&gt;가치가&lt;span&gt; &lt;/span&gt;있는&lt;span&gt; &lt;/span&gt;개념이라고&lt;span&gt; &lt;/span&gt;생각한다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;DOM &lt;span&gt;이벤트&lt;/span&gt; &lt;span&gt;핸들러에서의&lt;/span&gt; this &lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번&lt;span&gt; &lt;/span&gt;주차의&lt;span&gt; &lt;/span&gt;내용과&lt;span&gt; &lt;/span&gt;과제가&lt;span&gt; &lt;/span&gt;이벤트&lt;span&gt; &lt;/span&gt;핸들러와&lt;span&gt; &lt;/span&gt;깊이&lt;span&gt; &lt;/span&gt;관련되어&lt;span&gt; &lt;/span&gt;있어&lt;span&gt;, &lt;/span&gt;이에&lt;span&gt; &lt;/span&gt;대해&lt;span&gt; &lt;/span&gt;조금&lt;span&gt; &lt;/span&gt;더&lt;span&gt; &lt;/span&gt;딥다이브해보았다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;이벤트 핸들러로 함수를 등록하면, 그 함수 안의 this는 &lt;b&gt;이벤트가 등록된 DOM 요소&lt;/b&gt; 자체를 가리킨다.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1776777172335&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;button.addEventListener('click', function() {
  console.log(this); // &amp;lt;button&amp;gt; 요소
});&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 55px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; text-align: center; height: 19px;&quot;&gt;&lt;b&gt;this&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center; height: 19px;&quot;&gt;&lt;b&gt;리스너가 등록된 요소&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 50%; text-align: center; height: 19px;&quot;&gt;&lt;b&gt;e.currentTarget&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center; height: 19px;&quot;&gt;&lt;b&gt;리스너가 등록된 요소 (this와 항상 같음)&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; text-align: center; height: 17px;&quot;&gt;&lt;b&gt;e.target&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center; height: 17px;&quot;&gt;&lt;b&gt;실제로 클릭된 요소 (다를 수 있음)&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;e.currentTarget vs e.target 차이&lt;/h4&gt;
&lt;pre id=&quot;code_1776777462216&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 id=&quot;outer&quot;&amp;gt;   &amp;lt;!-- 리스너가 등록된 곳 --&amp;gt;
  &amp;lt;button&amp;gt;클릭&amp;lt;/button&amp;gt;  &amp;lt;!-- 실제 클릭한 곳 --&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1776777488015&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;outer.addEventListener('click', function(e) {
  console.log(e.currentTarget); // div &amp;mdash; 리스너가 등록된 요소
  console.log(e.target);        // button &amp;mdash; 실제로 클릭된 요소
  console.log(this);            // div &amp;mdash; currentTarget과 항상 같음
});&lt;/code&gt;&lt;/pre&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;b&gt;&quot;여러 요소에 같은 함수를 쓰되, 클릭된 그것만 다루고 싶을 때&quot;&lt;/b&gt; 이벤트 핸들러의 this가 빛을 발한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1. 클릭한 요소만 스타일 변경&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1776777724581&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const buttons = document.querySelectorAll('.btn');

buttons.forEach(btn =&amp;gt; {
  btn.addEventListener('click', function() {
    // 클릭한 버튼만 active 클래스 추가
    buttons.forEach(b =&amp;gt; b.classList.remove('active'));
    this.classList.add('active'); // 클릭된 그 버튼
  });
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;탭 UI, 토글 버튼, 네비게이션 메뉴에서 &lt;b&gt;&quot;선택된 것만 강조&quot;&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;size18&quot;&gt;&lt;b&gt;2. 클릭한 요소의 데이터 읽기&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1776777815546&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;document.querySelectorAll('.product-card').forEach(card =&amp;gt; {
  card.addEventListener('click', function() {
    const id = this.dataset.id;       // data-id 속성
    const name = this.dataset.name;   // data-name 속성
    console.log(`${name} 상품 클릭됨`);
    fetchProductDetail(id);
  });
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 함수 하나로 어떤 카드를 클릭했는지 this로 구분.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3. 폼 입력 유효성 검사&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1776777871379&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;document.querySelectorAll('input').forEach(input =&amp;gt; {
  input.addEventListener('blur', function() {
    if (this.value.trim() === '') {
      this.classList.add('error');         // 빈 input에만 에러 표시
      this.placeholder = '필수 입력입니다';
    }
  });
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 input에 같은 리스너를 달아도, this가 &lt;b&gt;지금 포커스 벗어난 그 input&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;size18&quot;&gt;&lt;b&gt;4. 이벤트 위임&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1776777925612&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;document.querySelector('.menu').addEventListener('click', function(e) {
  const clicked = e.target.closest('.menu-item');
  if (!clicked) return;

  // this가 진짜 필요한 순간
  // 부모 안의 모든 형제를 한 번에 리셋
  this.querySelectorAll('.menu-item').forEach(item =&amp;gt; {
    item.classList.remove('active');
  });

  // 클릭된 것만 active
  clicked.classList.add('active');
});&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;리스트 아이템이 100개면 리스너를 100개 달아야 할까? 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;this &amp;rarr; 리스너 등록된 .menu e.target &amp;rarr; 실제 클릭된 .menu-item&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 패턴은 &lt;b&gt;동적으로 추가되는 요소&lt;/b&gt;에도 자동으로 동작해서 굉장히 많이 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고 &lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@o1011/JavaScript-this-%EB%9E%80&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@o1011/JavaScript-this-%EB%9E%80&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1776778068980&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[JavaScript] this 란&quot; data-og-description=&quot;this 란 무엇인가요 ? 자바스크립트에서의 this 는 현재 실행 중인 코드에서 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수입니다. 단 this가 가리키는 값, 즉 this 의 바&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@o1011/JavaScript-this-%EB%9E%80&quot; data-og-url=&quot;https://velog.io/@o1011/JavaScript-this-란&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bEb0wY/dJMb9jgzvv8/R1QWiLs2DVTqpMAFs3e4L1/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500&quot;&gt;&lt;a href=&quot;https://velog.io/@o1011/JavaScript-this-%EB%9E%80&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@o1011/JavaScript-this-%EB%9E%80&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bEb0wY/dJMb9jgzvv8/R1QWiLs2DVTqpMAFs3e4L1/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[JavaScript] this 란&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;this 란 무엇인가요 ? 자바스크립트에서의 this 는 현재 실행 중인 코드에서 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수입니다. 단 this가 가리키는 값, 즉 this 의 바&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/this&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/this&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1776778069146&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;this - JavaScript | MDN&quot; data-og-description=&quot;&quot; data-og-host=&quot;developer.mozilla.org&quot; data-og-source-url=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/this&quot; data-og-url=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/this&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/this&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/this&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;this - JavaScript | MDN&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;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;</description>
      <author>taek2-0310</author>
      <guid isPermaLink="true">https://taek2-0310.tistory.com/5</guid>
      <comments>https://taek2-0310.tistory.com/5#entry5comment</comments>
      <pubDate>Tue, 21 Apr 2026 22:42:46 +0900</pubDate>
    </item>
    <item>
      <title>CSS의 원칙을 깨는 Tailwind, 정말 괜찮은 걸까?</title>
      <link>https://taek2-0310.tistory.com/4</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span&gt;CSS&lt;/span&gt;의&lt;span&gt; &lt;/span&gt;기본&lt;span&gt; &lt;/span&gt;철학은&lt;span&gt; &lt;/span&gt;구조&lt;span&gt;(HTML)&lt;/span&gt;와&lt;span&gt; &lt;/span&gt;표현&lt;span&gt;(CSS)&lt;/span&gt;의&lt;span&gt; &lt;/span&gt;분리이다&lt;span&gt;. &lt;/span&gt;&lt;/b&gt;따라서&lt;span&gt; &lt;/span&gt;아래와&lt;span&gt; &lt;/span&gt;같이&lt;span&gt; HTML&lt;/span&gt;은&lt;span&gt; &lt;/span&gt;버튼이라는&lt;span&gt; &lt;/span&gt;의미만을&lt;span&gt; &lt;/span&gt;담고&lt;span&gt;, &lt;/span&gt;스타일은&lt;span&gt; CSS&lt;/span&gt;로&lt;span&gt; &lt;/span&gt;분리하는&lt;span&gt; &lt;/span&gt;것이&lt;span&gt; &lt;/span&gt;일반적이다&lt;span&gt;. &lt;/span&gt;이러한&lt;span&gt; &lt;/span&gt;방식은&lt;span&gt; &lt;/span&gt;코드의&lt;span&gt; &lt;/span&gt;재사용성을&lt;span&gt; &lt;/span&gt;높이고&lt;span&gt;, &lt;/span&gt;유지보수를&lt;span&gt; &lt;/span&gt;용이하게&lt;span&gt; &lt;/span&gt;하며&lt;span&gt;, &lt;/span&gt;스타일을&lt;span&gt; &lt;/span&gt;일관되게&lt;span&gt; &lt;/span&gt;변경할&lt;span&gt; &lt;/span&gt;수&lt;span&gt; &lt;/span&gt;있도록&lt;span&gt; &lt;/span&gt;한다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1775453462019&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//HTML
&amp;lt;button class=&quot;primary-btn&quot;&amp;gt;확인&amp;lt;/button&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1775453476136&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//CSS
.primary-btn {
  background-color: blue;
  color: white;
  padding: 8px 16px;
}&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;span&gt;또한&amp;nbsp;&lt;/span&gt;인라인&lt;span&gt; &lt;/span&gt;스타일도&lt;span&gt; &lt;/span&gt;존재한다&lt;span&gt;. &lt;/span&gt;이는&lt;span&gt; HTML &lt;/span&gt;내부에&lt;span&gt; &lt;/span&gt;스타일을&lt;span&gt; &lt;/span&gt;직접&lt;span&gt; &lt;/span&gt;작성하는&lt;span&gt; &lt;/span&gt;방식으로&lt;span&gt;, &lt;/span&gt;구조와&lt;span&gt; &lt;/span&gt;표현의&lt;span&gt; &lt;/span&gt;분리라는&lt;span&gt; &lt;/span&gt;장점을&lt;span&gt; &lt;/span&gt;살리기&lt;span&gt; &lt;/span&gt;어렵기&lt;span&gt; &lt;/span&gt;때문에&lt;span&gt; &lt;/span&gt;일반적으로&lt;span&gt; &lt;/span&gt;지양되며&lt;span&gt; &lt;/span&gt;많은&lt;span&gt; &lt;/span&gt;개발자들이&lt;span&gt; &lt;/span&gt;선호하지&lt;span&gt; &lt;/span&gt;않는다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1775453876866&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;button style=&quot;background-color: blue; color: white; padding: 8px 16px;&quot;&amp;gt;
  확인
&amp;lt;/button&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;Tailwind  &lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Tailwind&lt;/span&gt;는&lt;b&gt;&lt;span&gt; HTML&lt;/span&gt;에&lt;span&gt; &lt;/span&gt;미리&lt;span&gt; &lt;/span&gt;정의된&lt;span&gt; &lt;/span&gt;작은&lt;span&gt; &lt;/span&gt;단위의&lt;span&gt; &lt;/span&gt;클래스&lt;span&gt;(utility class)&lt;/span&gt;를&lt;span&gt; &lt;/span&gt;직접&lt;span&gt; &lt;/span&gt;적용해&lt;span&gt; &lt;/span&gt;스타일을&lt;span&gt; &lt;/span&gt;만드는&lt;span&gt; CSS &lt;/span&gt;프레임워크&lt;/b&gt;이다&lt;span&gt;. &lt;/span&gt;개발자&lt;span&gt; &lt;/span&gt;설문에서도&lt;span&gt; &lt;/span&gt;사용률&lt;span&gt; &lt;/span&gt;상위권을&lt;span&gt; &lt;/span&gt;차지하며&lt;span&gt;, &lt;/span&gt;현재&lt;span&gt; &lt;/span&gt;가장&lt;span&gt; &lt;/span&gt;인기&lt;span&gt; &lt;/span&gt;있는&lt;span&gt; CSS &lt;/span&gt;프레임워크&lt;span&gt; &lt;/span&gt;중&lt;span&gt; &lt;/span&gt;하나로&lt;span&gt; &lt;/span&gt;평가받고&lt;span&gt; &lt;/span&gt;있다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1775454534056&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;button class=&quot;bg-blue-500 text-white px-4 py-2 rounded&quot;&amp;gt;
  확인
&amp;lt;/button&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면&lt;span&gt; &lt;/span&gt;위와&lt;span&gt; &lt;/span&gt;같이&lt;span&gt; &lt;/span&gt;인라인&lt;span&gt;&amp;nbsp;&lt;/span&gt;스타일처럼&lt;span&gt;&amp;nbsp;&lt;/span&gt;작성하는&lt;span&gt; Tailwind&lt;/span&gt;는&lt;span&gt; CSS&lt;/span&gt;의&lt;span&gt; &lt;/span&gt;기본&lt;span&gt; &lt;/span&gt;철학을&lt;span&gt; &lt;/span&gt;무시한&lt;span&gt; &lt;/span&gt;것일까&lt;span&gt;? &lt;/span&gt;만약&lt;span&gt; &lt;/span&gt;그렇다면&lt;span&gt;, &lt;/span&gt;어떻게&lt;span&gt; &lt;/span&gt;이렇게&lt;span&gt; &lt;/span&gt;많은&lt;span&gt; &lt;/span&gt;개발자들의&lt;span&gt; &lt;/span&gt;선택을&lt;span&gt; &lt;/span&gt;받을&lt;span&gt; &lt;/span&gt;수&lt;span&gt; &lt;/span&gt;있었을까&lt;span&gt;?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;작동원리⚙️&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 기본 CSS의 작동원리는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 브라우저가 HTML을 로드한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. HTML&lt;span&gt;&amp;nbsp;&lt;/span&gt;을&lt;span&gt; DOM&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(Document Object Model) 로 변환한다.&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;3. &lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;그러면 브라우저는 HTML 문서에 연결된 임베디드 이미지, 동영상, 링크된 CSS같은 대부분의 리소스들을 가져온다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;4. &lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;브라우저는 가져온 CSS를 구문 분석하고 선택자 유형별로 다른 규칙을 다른 &quot;버킷&quot; 으로 정렬한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;5. &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;6. &lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;페이지의 시각적 표시가 화면에 표시된다.&lt;span&gt; (페인팅)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;★ 쉽게 말해 ★&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저는&lt;span&gt; HTML&lt;/span&gt;을&lt;span&gt; DOM&lt;/span&gt;으로&lt;span&gt;, CSS&lt;/span&gt;를&lt;span&gt; CSSOM&lt;/span&gt;으로&lt;span&gt; &lt;/span&gt;변환한&lt;span&gt; &lt;/span&gt;뒤&lt;span&gt; &lt;/span&gt;이를&lt;span&gt; &lt;/span&gt;결합해&lt;span&gt; &lt;/span&gt;화면을&lt;span&gt; &lt;/span&gt;그린다&lt;span&gt;. &lt;/span&gt;이&lt;span&gt; &lt;/span&gt;과정에서&lt;span&gt; CSS&lt;/span&gt;는&lt;span&gt; &lt;/span&gt;선택자(p, span)를&lt;span&gt; &lt;/span&gt;통해&lt;span&gt; &lt;/span&gt;요소를&lt;span&gt; &lt;/span&gt;찾고&lt;span&gt;,&lt;/span&gt;&lt;span&gt;cascade(&lt;/span&gt;스타일이&lt;span&gt; &lt;/span&gt;겹칠&lt;span&gt; &lt;/span&gt;때&lt;span&gt; &lt;/span&gt;어떤&lt;span&gt; &lt;/span&gt;규칙이&lt;span&gt; &lt;/span&gt;적용될지를&lt;span&gt; &lt;/span&gt;결정하는&lt;span&gt; &lt;/span&gt;방식&lt;span&gt;), specificity(&lt;/span&gt;선택자의&lt;span&gt; &lt;/span&gt;구체성에&lt;span&gt; &lt;/span&gt;따른&lt;span&gt; &lt;/span&gt;우선순위&lt;span&gt;), inheritance(&lt;/span&gt;부모&lt;span&gt; &lt;/span&gt;요소로부터&lt;span&gt; &lt;/span&gt;스타일이&lt;span&gt; &lt;/span&gt;전달되는&lt;span&gt; &lt;/span&gt;특성&lt;span&gt;) &lt;/span&gt;&lt;span&gt;&lt;/span&gt;규칙에&lt;span&gt; &lt;/span&gt;따라&lt;span&gt; &lt;/span&gt;최종&lt;span&gt; &lt;/span&gt;스타일을&lt;span&gt; &lt;/span&gt;계산한다&lt;span&gt;. &lt;/span&gt;이후&lt;span&gt; &lt;/span&gt;박스&lt;span&gt; &lt;/span&gt;모델과&lt;span&gt; &lt;/span&gt;레이아웃&lt;span&gt; &lt;/span&gt;규칙에&lt;span&gt; &lt;/span&gt;따라&lt;span&gt; &lt;/span&gt;각&lt;span&gt; &lt;/span&gt;요소의&lt;span&gt; &lt;/span&gt;크기와&lt;span&gt; &lt;/span&gt;위치가&lt;span&gt; &lt;/span&gt;정해지고&lt;span&gt;, &lt;/span&gt;브라우저는&lt;span&gt; &lt;/span&gt;이를&lt;span&gt; &lt;/span&gt;실제&lt;span&gt; &lt;/span&gt;화면에&lt;span&gt; &lt;/span&gt;렌더링한다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3154&quot; data-origin-height=&quot;1262&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYs2Y8/dJMcaiiyLda/1Kfa17h0GmOrmWxwlR3VD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYs2Y8/dJMcaiiyLda/1Kfa17h0GmOrmWxwlR3VD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYs2Y8/dJMcaiiyLda/1Kfa17h0GmOrmWxwlR3VD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYs2Y8%2FdJMcaiiyLda%2F1Kfa17h0GmOrmWxwlR3VD0%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;3154&quot; height=&quot;1262&quot; data-origin-width=&quot;3154&quot; data-origin-height=&quot;1262&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;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;그럼 tailwind는 어떻게 동작할까?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 개발자가 HTML/JSX/TSX 등에 Tailwind 클래스를 작성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Tailwind가 빌드 과정에서 이 클래스들을 분석한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 대응하는 CSS 규칙을 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 브라우저는 HTML과 생성된 CSS를 읽는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. DOM과 CSSOM을 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6. 클래스 선택자에 맞는 스타일을 요소에 적용한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;7.최종 레이아웃과 렌더링이 이루어진다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;630&quot; data-origin-height=&quot;193&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bP2rdf/dJMcad2zPup/mu1Qq4yQhSibwiOI1xjcN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bP2rdf/dJMcad2zPup/mu1Qq4yQhSibwiOI1xjcN0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bP2rdf/dJMcad2zPup/mu1Qq4yQhSibwiOI1xjcN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbP2rdf%2FdJMcad2zPup%2Fmu1Qq4yQhSibwiOI1xjcN0%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;630&quot; height=&quot;193&quot; data-origin-width=&quot;630&quot; data-origin-height=&quot;193&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;겉으로 보면 HTML 안에 스타일 정보가 직접 들어가 있는 것처럼 보여 인라인 스타일과 유사해 보일 수 있다. 하지만 브라우저 관점에서 보면 이는 인라인 스타일이 아니다. Tailwind는 class 속성만을 사용하며 브라우저 입장에서는 여전히 일반적인 클래스 기반 CSS로 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tailwind는 프로젝트 파일을 스캔하여 어떤 클래스가 사용되었는지 분석한 뒤 해당 클래스에 대응하는 CSS 규칙만 생성해 최종 CSS 파일에 포함시킨다. 즉 Tailwind는 빌드 타임에 작동하는 도구이며 브라우저에서는 기존 CSS와 동일한 방식으로 해석되고 적용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tailwind가 많은 사랑을 받는 이유 중 하나는 JIT(Just-In-Time) 컴파일 방식을 사용한다는 점이다. 필요한 스타일을 실시간으로 생성하기 때문에 실제로 사용된 클래스만 CSS로 만들어지며 빌드 속도가 빠르고 최종 파일 크기도 줄어든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한&lt;span&gt; Purge &lt;/span&gt;기능을&lt;span&gt; &lt;/span&gt;통해&lt;span&gt; &lt;/span&gt;사용되지&lt;span&gt; &lt;/span&gt;않는&lt;span&gt; CSS&lt;/span&gt;를&lt;span&gt; &lt;/span&gt;제거함으로써&lt;span&gt; &lt;/span&gt;결과물의&lt;span&gt; &lt;/span&gt;크기를&lt;span&gt; &lt;/span&gt;최소화할&lt;span&gt; &lt;/span&gt;수&lt;span&gt; &lt;/span&gt;있다&lt;span&gt;. &lt;/span&gt;이로&lt;span&gt; &lt;/span&gt;인해&lt;span&gt; &lt;/span&gt;불필요한&lt;span&gt; &lt;/span&gt;스타일이&lt;span&gt; &lt;/span&gt;포함되지&lt;span&gt; &lt;/span&gt;않고&lt;span&gt;&amp;nbsp;&lt;/span&gt;보다&lt;span&gt; &lt;/span&gt;가볍고&lt;span&gt; &lt;/span&gt;효율적인&lt;span&gt; &lt;/span&gt;결과물을&lt;span&gt; &lt;/span&gt;얻을&lt;span&gt; &lt;/span&gt;수&lt;span&gt; &lt;/span&gt;있다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;Tailwind는 왜 나왔을까❓&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;623&quot; data-origin-height=&quot;584&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/szZwc/dJMcagLQfkY/Ww6uoMmksVeK2XCOwNEnuk/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/szZwc/dJMcagLQfkY/Ww6uoMmksVeK2XCOwNEnuk/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/szZwc/dJMcagLQfkY/Ww6uoMmksVeK2XCOwNEnuk/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FszZwc%2FdJMcagLQfkY%2FWw6uoMmksVeK2XCOwNEnuk%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;623&quot; height=&quot;584&quot; data-origin-width=&quot;623&quot; data-origin-height=&quot;584&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어느&lt;span&gt; &lt;/span&gt;정도&lt;span&gt; &lt;/span&gt;규모가&lt;span&gt; &lt;/span&gt;있는&lt;span&gt; &lt;/span&gt;개발을&lt;span&gt; &lt;/span&gt;진행하다&lt;span&gt; &lt;/span&gt;보면&lt;span&gt; CSS &lt;/span&gt;파일은&lt;span&gt; &lt;/span&gt;점점&lt;span&gt; &lt;/span&gt;비대해진다&lt;span&gt;. &lt;/span&gt;또한&lt;span&gt; &lt;/span&gt;협업&lt;span&gt; &lt;/span&gt;과정에서는&lt;span&gt; &lt;/span&gt;해당&lt;span&gt; &lt;/span&gt;클래스가&lt;span&gt; &lt;/span&gt;어디에서&lt;span&gt; &lt;/span&gt;사용되는지&lt;span&gt; &lt;/span&gt;파악하기&lt;span&gt; &lt;/span&gt;어려워&lt;span&gt; &lt;/span&gt;가독성이&lt;span&gt; &lt;/span&gt;떨어지고,&lt;span&gt;&amp;nbsp;&lt;/span&gt;클래스&lt;span&gt; &lt;/span&gt;명을&lt;span&gt; &lt;/span&gt;짓는&lt;span&gt; &lt;/span&gt;것&lt;span&gt; &lt;/span&gt;역시&lt;span&gt; &lt;/span&gt;쉽지&lt;span&gt; &lt;/span&gt;않다는&lt;span&gt; &lt;/span&gt;문제를&lt;span&gt; &lt;/span&gt;겪게&lt;span&gt; &lt;/span&gt;된다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Tailwind&lt;/span&gt;는&lt;span&gt; &lt;/span&gt;이러한&lt;span&gt; &lt;/span&gt;문제를&lt;span&gt; &lt;/span&gt;효과적으로&lt;span&gt; &lt;/span&gt;해결한다&lt;span&gt;. &lt;/span&gt;유틸리티&lt;span&gt; &lt;/span&gt;클래스를&lt;span&gt; &lt;/span&gt;기반으로&lt;span&gt; &lt;/span&gt;필요한&lt;span&gt; &lt;/span&gt;스타일만&lt;span&gt; &lt;/span&gt;조합해&lt;span&gt; &lt;/span&gt;사용하기&lt;span&gt; &lt;/span&gt;때문에&lt;span&gt; CSS &lt;/span&gt;파일이&lt;span&gt; &lt;/span&gt;불필요하게&lt;span&gt; &lt;/span&gt;커지는&lt;span&gt; &lt;/span&gt;것을&lt;span&gt; &lt;/span&gt;방지할&lt;span&gt; &lt;/span&gt;수&lt;span&gt; &lt;/span&gt;있으며&lt;span&gt;&amp;nbsp;&lt;/span&gt;스타일이&lt;span&gt; HTML&lt;/span&gt;에&lt;span&gt; &lt;/span&gt;직접&lt;span&gt; &lt;/span&gt;드러나기&lt;span&gt; &lt;/span&gt;때문에&lt;span&gt; &lt;/span&gt;코드만&lt;span&gt; &lt;/span&gt;보아도&lt;span&gt; &lt;/span&gt;어떤&lt;span&gt; &lt;/span&gt;디자인이&lt;span&gt; &lt;/span&gt;적용되었는지&lt;span&gt; &lt;/span&gt;파악할&lt;span&gt; &lt;/span&gt;수&lt;span&gt; &lt;/span&gt;있다&lt;span&gt;. &lt;/span&gt;또한&lt;span&gt; &lt;/span&gt;의미를&lt;span&gt; &lt;/span&gt;고민해&lt;span&gt; &lt;/span&gt;클래스를&lt;span&gt; &lt;/span&gt;새로&lt;span&gt; &lt;/span&gt;정의할&lt;span&gt; &lt;/span&gt;필요&lt;span&gt; &lt;/span&gt;없이&lt;span&gt;&amp;nbsp;&lt;/span&gt;미리&lt;span&gt; &lt;/span&gt;정의된&lt;span&gt; &lt;/span&gt;규칙을&lt;span&gt; &lt;/span&gt;조합해&lt;span&gt; &lt;/span&gt;사용할&lt;span&gt; &lt;/span&gt;수&lt;span&gt; &lt;/span&gt;있어&lt;span&gt; &lt;/span&gt;네이밍에&lt;span&gt; &lt;/span&gt;대한&lt;span&gt; &lt;/span&gt;부담도&lt;span&gt; &lt;/span&gt;줄어든다&lt;span&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;Tailwind는 기본 철학을 무시한걸까? &lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 Tailwind는 CSS의 기본 철학을 무시한 것일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 질문에 대해 명확하게 그렇다 혹은 아니다라고 단정 짓기는 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전통적인 CSS는 구조와 표현을 파일 단위로 분리하는 방식으로 관심사를 나누어 왔다. 반면 Tailwind는 이러한 분리 방식을 따르기보다는 스타일을 컴포넌트 내부에서 함께 다루며 역할 단위로 응집시키는 접근을 취한다. 즉 분리의 기준이 파일에서 컴포넌트로 이동했다고 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 구조와 표현을 분리하는 이유가 유지보수성과 확장성을 높이기 위함이라면, Tailwind는 오히려 다른 방식으로 그 목적을 달성하고 있는 셈이다. 스타일이 HTML에 직접 드러나기 때문에 코드를 읽는 것만으로도 UI를 이해할 수 있고 어디에서 사용되는지 추적하기도 쉬워진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면&lt;span&gt; Tailwind&lt;/span&gt;는&lt;span&gt; CSS&lt;/span&gt;의&lt;span&gt; &lt;/span&gt;기본&lt;span&gt; &lt;/span&gt;철학을&lt;span&gt; &lt;/span&gt;무시한&lt;span&gt; &lt;/span&gt;것이라기보다&lt;span&gt;&amp;nbsp;&lt;/span&gt;그&lt;span&gt; &lt;/span&gt;철학을&lt;span&gt; &lt;/span&gt;다른&lt;span&gt; &lt;/span&gt;방식으로&lt;span&gt; &lt;/span&gt;해석하고&lt;span&gt; &lt;/span&gt;적용한&lt;span&gt; &lt;/span&gt;도구라고&lt;span&gt; &lt;/span&gt;볼&lt;span&gt; &lt;/span&gt;수도&lt;span&gt; &lt;/span&gt;있지&lt;span&gt; &lt;/span&gt;않을까&lt;span&gt;?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;참고 &lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;a href=&quot;https://velog.io/@kkang123/tailwind-CSS-%EC%9E%91%EB%8F%99-%EC%9B%90%EB%A6%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://velog.io/@kkang123/tailwind-CSS-%EC%9E%91%EB%8F%99-%EC%9B%90%EB%A6%AC&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1775479736672&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;tailwind CSS 작동 원리&quot; data-og-description=&quot;기존 프로젝트에 Tailwind CSS 사용 이유로는 디자인의 일관성을 유지하여 재활용성을 높였고, 커스텀 디자인 작업 시간을 단축하기 위해 적용했었습니다.그리고 더 풀어서 설명하자면 Tailwind CSS는&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@kkang123/tailwind-CSS-%EC%9E%91%EB%8F%99-%EC%9B%90%EB%A6%AC&quot; data-og-url=&quot;https://velog.io/@kkang123/tailwind-CSS-작동-원리&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/nB81v/dJMb9jOnDiX/JphKHtC5r9yDxka7QNSHH1/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500,https://scrap.kakaocdn.net/dn/jVEOl/dJMb9iaRPGc/6bbPnBHzpZJr6RqTreaF7k/img.jpg?width=460&amp;amp;height=460&amp;amp;face=0_0_460_460&quot;&gt;&lt;a href=&quot;https://velog.io/@kkang123/tailwind-CSS-%EC%9E%91%EB%8F%99-%EC%9B%90%EB%A6%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@kkang123/tailwind-CSS-%EC%9E%91%EB%8F%99-%EC%9B%90%EB%A6%AC&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/nB81v/dJMb9jOnDiX/JphKHtC5r9yDxka7QNSHH1/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500,https://scrap.kakaocdn.net/dn/jVEOl/dJMb9iaRPGc/6bbPnBHzpZJr6RqTreaF7k/img.jpg?width=460&amp;amp;height=460&amp;amp;face=0_0_460_460');&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;tailwind CSS 작동 원리&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;기존 프로젝트에 Tailwind CSS 사용 이유로는 디자인의 일관성을 유지하여 재활용성을 높였고, 커스텀 디자인 작업 시간을 단축하기 위해 적용했었습니다.그리고 더 풀어서 설명하자면 Tailwind CSS는&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Learn_web_development/Core/Styling_basics/What_is_CSS&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.mozilla.org/ko/docs/Learn_web_development/Core/Styling_basics/What_is_CSS&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1775479769236&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;CSS 작동 방식 - Web 개발 학습하기 | MDN&quot; data-og-description=&quot;&quot; data-og-host=&quot;developer.mozilla.org&quot; data-og-source-url=&quot;https://developer.mozilla.org/ko/docs/Learn_web_development/Core/Styling_basics/What_is_CSS&quot; data-og-url=&quot;https://developer.mozilla.org/ko/docs/Learn_web_development/Core/Styling_basics/What_is_CSS&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Learn_web_development/Core/Styling_basics/What_is_CSS&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.mozilla.org/ko/docs/Learn_web_development/Core/Styling_basics/What_is_CSS&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;CSS 작동 방식 - Web 개발 학습하기 | MDN&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;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;</description>
      <author>taek2-0310</author>
      <guid isPermaLink="true">https://taek2-0310.tistory.com/4</guid>
      <comments>https://taek2-0310.tistory.com/4#entry4comment</comments>
      <pubDate>Mon, 6 Apr 2026 22:06:57 +0900</pubDate>
    </item>
  </channel>
</rss>