<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Hushed_Mind.log</title>
        <link>https://velog.io/</link>
        <description>개발 공부 블로그</description>
        <lastBuildDate>Fri, 26 Sep 2025 13:09:12 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Hushed_Mind.log</title>
            <url>https://velog.velcdn.com/images/cold_mental/profile/48c84d5c-fd01-4a55-b21d-22996aba044e/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. Hushed_Mind.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/cold_mental" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[타입스크립트 infer 완벽 이해하기]]></title>
            <link>https://velog.io/@cold_mental/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-infer-%EC%99%84%EB%B2%BD-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@cold_mental/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-infer-%EC%99%84%EB%B2%BD-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 26 Sep 2025 13:09:12 GMT</pubDate>
            <description><![CDATA[<h2 id="0-도입--왜-굳이-infer가-필요할까">0) 도입 — “왜 굳이 infer가 필요할까?”</h2>
<p>일반적인 타입 설계는 보통 “타입을 <strong>미리</strong> 적어두는” 방식이야. 그런데 실무에선 이미 어딘가에 <strong>정의된 타입 구조</strong>에서 “일부분만 꺼내 쓰고” 싶을 때가 많다.</p>
<ul>
<li>함수 시그니처에서 반환 타입만 가져오기</li>
<li>함수의 <strong>파라미터 리스트(튜플)</strong>만 뽑기</li>
<li>Promise&lt;...&gt; 안의 결과 타입만 꺼내기</li>
<li>문자열/튜플/객체 타입을 패턴 매칭해서 특정 조각만 얻기</li>
</ul>
<p>이걸 매번 수동으로 재정의하면 <strong>중복</strong>과 <strong>불일치</strong> 지옥이 온다. 함수가 바뀌면 파생 타입도 전부 고쳐야 하거든.
<code>infer</code>는 이런 “기존 타입에서 특정 부분만 자동으로 추출”하는 도구다. <strong>조건부 타입</strong>과 함께 쓰여서 <strong>패턴 매칭 + 타입 변수 추론</strong>을 가능하게 한다.</p>
<h2 id="1-기본-문법--조건부-타입-안에서만-쓴다">1) 기본 문법 — “조건부 타입 안에서만 쓴다”</h2>
<p>형식은 딱 하나.</p>
<pre><code class="language-ts">T extends SomePattern&lt;infer U&gt; ? U : Fallback</code></pre>
<ul>
<li><code>T</code>가 <code>SomePattern&lt;...&gt;</code> 모양과 <strong>매치되면</strong>, 그 안의 일부를 <code>infer U</code>로 추론해서 U를 반환.</li>
<li>매치 안 되면 <code>Fallback</code> (대개 <code>never</code>).</li>
</ul>
<blockquote>
<p>중요한 규칙</p>
</blockquote>
<ul>
<li>infer는 <strong>조건부 타입의 참( ? ) 분기 내부</strong>에서만 선언할 수 있다.</li>
<li>한 번 추론된 <code>U</code>는 그 분기 안에서 <strong>이름을 가진 타입 변수</strong>처럼 자유롭게 쓸 수 있다. </li>
</ul>
<h3 id="2-예제-1--함수-반환-타입-꺼내기-returntype-원리">2) 예제 1 — “함수 반환 타입 꺼내기” (ReturnType 원리)</h3>
<pre><code class="language-ts">type MyReturn&lt;T&gt; = T extends (...args: any[]) =&gt; infer R ? R : never;

function fn(x: number): string {
  return &quot;hello&quot;;
}

type R = MyReturn&lt;typeof fn&gt;; // string</code></pre>
<h4 id="아주-자세한-뜯어보기">아주 자세한 뜯어보기</h4>
<ol>
<li><p><code>typeof fn</code> → <code>(x: number) =&gt; string</code> 라는 <strong>값 → 타입 승격.</strong></p>
</li>
<li><p>조건부 타입에 대입:</p>
</li>
</ol>
<pre><code class="language-ts">// T 자리에 (x: number) =&gt; string 이 들어감
( (x: number) =&gt; string ) extends (...args: any[]) =&gt; infer R ? R : never</code></pre>
<ol start="3">
<li>좌변 함수 타입이 우변 패턴 <code>(...args: any[]) =&gt; something</code> 과** 형태가 같다 → 매칭 성공.**</li>
<li>그 “something” 자리에 있는 타입을 <code>infer R</code>로 빼온다 → <code>R = string</code>.</li>
<li>참 분기 결과 <code>R</code> → 최종 타입 <code>string</code>.</li>
</ol>
<blockquote>
<p>Tip
<code>infer</code>는 *<em>“형태가 맞으면 빼오는” *</em>거다. 그래서 패턴 설계가 핵심이다.</p>
</blockquote>
<h3 id="3-예제-2--파라미터-리스트튜플-꺼내기-parameters-원리">3) 예제 2 — “파라미터 리스트(튜플) 꺼내기” (Parameters 원리)</h3>
<pre><code class="language-ts">type MyParams&lt;T&gt; = T extends (...args: infer P) =&gt; any ? P : never;

type P0 = MyParams&lt;(a: number, b: string) =&gt; void&gt;; // [number, string]</code></pre>
<h4 id="뜯어보기">뜯어보기</h4>
<ul>
<li>이번엔 <code>(...args: infer P)</code>에 함수의 파라미터 리스트 전체가 들어간다.</li>
<li>TS에서 파라미터 리스트는 튜플 타입으로 표현된다 → <code>P = [number, string]</code>.</li>
<li>그래서 결과는 <code>[number, string]</code>.</li>
</ul>
<blockquote>
<p>주의
“튜플”이라서 순서/개별 타입이 <strong>고정</strong>된다. 단순한 any[]가 아니다.</p>
</blockquote>
<h3 id="4-예제-3--promise-내부-타입-풀기-awaited-원리-재귀-포함">4) 예제 3 — “Promise 내부 타입 풀기” (Awaited 원리, 재귀 포함)</h3>
<pre><code class="language-ts">type MyAwaited&lt;T&gt; = T extends Promise&lt;infer U&gt; ? MyAwaited&lt;U&gt; : T;

type A = MyAwaited&lt;Promise&lt;string&gt;&gt;;            // string
type B = MyAwaited&lt;Promise&lt;Promise&lt;number&gt;&gt;&gt;;   // number
type C = MyAwaited&lt;number&gt;;                     // number</code></pre>
<h4 id="뜯어보기-1">뜯어보기</h4>
<ol>
<li><p><code>Promise&lt;string&gt;</code>이면 <code>infer U = string</code> → 다시 <code>MyAwaited&lt;string&gt;</code> 호출 → 더 이상 <code>Promise</code> 아님 → <code>string</code>.</p>
</li>
<li><p><code>Promise&lt;Promise&lt;number&gt;&gt;</code>면</p>
<ul>
<li>1차: <code>U = Promise&lt;number&gt;</code></li>
<li>2차: <code>U = number</code></li>
<li>3차: <code>number</code> 반환.</li>
</ul>
</li>
<li><p><code>number</code>는 <code>Promise&lt;...&gt;</code> 패턴 불일치 → 그냥 <code>T</code> 그대로 반환.</p>
</li>
</ol>
<blockquote>
<p>Tip
재귀를 이용해 “여러 겹 Promise”도 풀어낼 수 있다.</p>
</blockquote>
<h3 id="5-예제-4--튜플-구조-분해-첫-원소--마지막-원소">5) 예제 4 — “튜플 구조 분해: 첫 원소 / 마지막 원소”</h3>
<pre><code class="language-ts">type First&lt;T extends any[]&gt; = T extends [infer F, ...any[]] ? F : never;
type Last&lt;T extends any[]&gt;  = T extends [...any[], infer L] ? L : never;

type F1 = First&lt;[&quot;a&quot;, &quot;b&quot;, &quot;c&quot;]&gt;; // &quot;a&quot;
type L1 = Last&lt;[&quot;a&quot;, &quot;b&quot;, &quot;c&quot;]&gt;;  // &quot;c&quot;
type F2 = First&lt;[]&gt;;              // never (매칭 불가)</code></pre>
<h4 id="뜯어보기-2">뜯어보기</h4>
<ul>
<li><code>[infer F, ...any[]]</code>는 “<strong>첫 칸을 F로 받고 나머지는 아무거나</strong>”라는 패턴.</li>
<li><code>[&quot;a&quot;,&quot;b&quot;,&quot;c&quot;]</code>는 위 패턴과 매치 → <code>F = &quot;a&quot;</code>.</li>
<li>빈 튜플은 매치 자체가 안 돼서 <code>never</code>.</li>
</ul>
<blockquote>
<p>이해 포인트
값에서 구조 분해 <code>const [first, ...rest] = arr</code> 과 <strong>아주 유사한 사고방식</strong>으로 보면 쉽다.</p>
</blockquote>
<h3 id="6-예제-5--문자열-패턴-매칭-템플릿-리터럴--infer">6) 예제 5 — “문자열 패턴 매칭” (템플릿 리터럴 + infer)</h3>
<pre><code class="language-ts">type Split2&lt;S extends string&gt; =
  S extends `${infer A}-${infer B}` ? [A, B] : [S];

type S1 = Split2&lt;&quot;ab-cd&quot;&gt;; // [&quot;ab&quot;,&quot;cd&quot;]
type S2 = Split2&lt;&quot;hello&quot;&gt;; // [&quot;hello&quot;]</code></pre>
<h4 id="뜯어보기-3">뜯어보기</h4>
<ul>
<li>템플릿 리터럴 타입에서도 <code>infer</code>를 쓸 수 있다.</li>
<li><code>&quot;ab-cd&quot;</code>는 <code>${A}-${B}</code> 패턴과 매칭 → <code>A = &quot;ab&quot;</code>, <code>B = &quot;cd&quot;</code>.</li>
<li><code>&quot;hello&quot;</code>는 <code>-</code>가 없어서 매칭 실패 → 기본값 <code>[S]</code>.</li>
</ul>
<blockquote>
<p>확장 아이디어
재귀를 쓰면 <code>Split&lt;&quot;a-b-c&quot;,&quot;-&quot;&gt; → [&quot;a&quot;,&quot;b&quot;,&quot;c&quot;]</code> 같은 것도 가능하다.</p>
</blockquote>
<h3 id="7-분배-조건부-타입과-infer--유니온을-어떻게-처리하나">7) 분배 조건부 타입과 infer — “유니온을 어떻게 처리하나”</h3>
<p>조건부 타입은 <strong>좌변이 유니온이면 분배(distributive)</strong> 된다. 즉 각 원소에 독립적으로 조건을 적용하고 합친다.</p>
<pre><code class="language-ts">type Element&lt;T&gt; = T extends (infer U)[] ? U : T;

type E1 = Element&lt;string[]&gt;;         // string
type E2 = Element&lt;(number | boolean)[]&gt;; // number | boolean
type E3 = Element&lt;string | number[]&gt;; // string | number
// 해석: Element&lt;string&gt; | Element&lt;number[]&gt;
//    -&gt; string | number
</code></pre>
<h4 id="뜯어보기-4">뜯어보기</h4>
<ul>
<li><code>[T = string | number[]</code>
→ <code>Element&lt;string&gt; | Element&lt;number[]&gt;</code>
→ <code>string | number</code>.</li>
</ul>
<blockquote>
<p>주의 포인트
분배가 <strong>원치 않게</strong> 일어날 때가 있다. 그땐 <code>T</code>를 <strong>한 번 감싸</strong> 분배를 끊는다.</p>
</blockquote>
<pre><code class="language-ts">type NoDistrib&lt;T&gt; = [T] extends [infer U] ? U : never; // 분배 억제 트릭</code></pre>
<h3 id="8-infer의-제약한계--여기선-안-된다">8) infer의 제약/한계 — “여기선 안 된다”</h3>
<ol>
<li><strong>조건부 타입의 참 분기 내부</strong>에서만 선언 가능.
아래처럼 거짓 분기에서 선언은 문법적으로 불가.</li>
</ol>
<pre><code class="language-ts">// ❌ 잘못된 예
type Bad&lt;T&gt; = T extends any ? string : infer X;</code></pre>
<ol start="2">
<li><strong>모호하면 추론 실패</strong> → <code>never</code>로 빠지거나 원하는 결과가 안 나온다.</li>
<li><strong>여러 곳에서 상충되는 추론</strong>이 생기면 실패할 수 있다. (서로 다른 타입을 한 변수에 동시에 강제하면 안 됨)</li>
<li><strong>함수 파라미터(반공변 위치) 추론 난이도</strong>
함수 타입에서 입력 위치는 반공변이라 추론이 까다롭다. 가능한 한 <strong>출력 위치</strong>(공변)에서 추론하는 패턴이 안정적.</li>
</ol>
<h3 id="9-자주-쓰는-패턴-정리">9) 자주 쓰는 패턴 정리</h3>
<h4 id="9-1-returntype--parameters">9-1. ReturnType / Parameters</h4>
<pre><code class="language-ts">type MyReturn&lt;T&gt;  = T extends (...a: any[]) =&gt; infer R ? R : never;
type MyParams&lt;T&gt;  = T extends (...a: infer P) =&gt; any ? P : never;</code></pre>
<h4 id="9-2-awaited-중첩-promise-풀기">9-2. Awaited (중첩 Promise 풀기)</h4>
<pre><code class="language-ts">type MyAwaited&lt;T&gt; = T extends Promise&lt;infer U&gt; ? MyAwaited&lt;U&gt; : T;</code></pre>
<h4 id="9-3-튜플-분해">9-3. 튜플 분해</h4>
<pre><code class="language-ts">type Head&lt;T extends any[]&gt; = T extends [infer H, ...any[]] ? H : never;
type Tail&lt;T extends any[]&gt; = T extends [any, ...infer R] ? R : [];
type Last&lt;T extends any[]&gt; = T extends [...any[], infer L] ? L : never;</code></pre>
<h4 id="9-4-템플릿-리터럴-파싱">9-4. 템플릿 리터럴 파싱</h4>
<pre><code class="language-ts">type TrimLeft&lt;S extends string&gt; =
  S extends ` ${infer R}` | `\n${infer R}` | `\t${infer R}` ? TrimLeft&lt;R&gt; : S;</code></pre>
<h3 id="10-실전-미니-응용--api-응답-타입-자동-추출--느슨화">10) 실전 미니 응용 — “API 응답 타입 자동 추출 + 느슨화”</h3>
<pre><code class="language-ts">const endpoints = {
  getUser:  { url: &quot;/user&quot;,  response: { id: 1, name: &quot;string&quot; } },
  getPosts: { url: &quot;/posts&quot;, response: [{ id: 1, title: &quot;string&quot; }] },
} as const;

type ApiResponse&lt;T&gt; = T extends { response: infer R } ? R : never;

function fetchApi&lt;K extends keyof typeof endpoints&gt;(
  key: K
): ApiResponse&lt;(typeof endpoints)[K]&gt; {
  return null as any;
}

const user = fetchApi(&quot;getUser&quot;);
// 타입: { readonly id: 1; readonly name: &quot;string&quot; }

const posts = fetchApi(&quot;getPosts&quot;);
// 타입: readonly [{ readonly id: 1; readonly title: &quot;string&quot; }]

// 리터럴/readonly를 일반 타입으로 느슨화
type Loose&lt;T&gt; =
  T extends string | number | boolean ? (T extends string ? string : T extends number ? number : boolean)
  : T extends readonly (infer U)[] ? Loose&lt;U&gt;[]
  : T extends object ? { [K in keyof T]: Loose&lt;T[K]&gt; }
  : T;

type User = Loose&lt;typeof user&gt;;   // { id: number; name: string }
type Posts = Loose&lt;typeof posts&gt;; // { id: number; title: string }[]
</code></pre>
<h4 id="뜯어보기핵심-포인트">뜯어보기(핵심 포인트)</h4>
<ul>
<li><code>typeof endpoints</code>로 <strong>값→타입</strong>을 뽑고</li>
<li><code>keyof</code>로 가능한 키를 <strong>유니온</strong>으로 제한</li>
<li>인덱싱 <code>(typeof endpoints)[K]</code>로 <strong>특정 엔드포인트 타입 선택</strong></li>
<li><code>ApiResponse&lt;...&gt;</code>에서 <code>infer R</code>로 <strong>response만 추출</strong></li>
<li><code>Loose&lt;T&gt;</code>로 리터럴/readonly를 <strong>실사용-friendly</strong> 타입으로 변환</li>
</ul>
<h4 id="11-디버깅-요령--왜-내가-원하는-대로-추론이-안-되지">11) 디버깅 요령 — “왜 내가 원하는 대로 추론이 안 되지?”</h4>
<ul>
<li><strong>분배가 개입했는지</strong> 확인: 좌변이 유니온이면 각 원소에 따로 조건이 적용된다.
원치 않으면 <code>[T] extends [...]</code> 트릭으로 분배를 끊어라.</li>
<li><strong>패턴이 너무 빡빡</strong>한지 확인: 매치 실패하면 전부 <code>never</code>로 빠진다. 패턴을 느슨하게 바꿔보자.</li>
<li><strong>반공변 위치</strong>(함수 파라미터)에서 추론하려고 집착하지 말고, <strong>공변 위치</strong>(반환값/프로퍼티)로 옮겨서 추론을 유도하라.</li>
<li><strong>추론된 타입 확인</strong>: IDE에서 마우스 오버하거나, 보조 타입으로 노출해서 확인해라.</li>
</ul>
<pre><code class="language-ts">type Debug&lt;T&gt; = T extends any ? { result: T } : never;
type D = Debug&lt;MyReturn&lt;typeof fn&gt;&gt;;</code></pre>
<h4 id="12-최종-정리">12) 최종 정리</h4>
<ul>
<li><code>infer</code>는 <strong>조건부 타입의 참 분기에서 패턴 매칭으로 타입을 꺼내는 도구</strong>다.</li>
<li>핵심은 <strong>“패턴을 어떻게 설계하느냐”</strong>: 함수/튜플/템플릿 리터럴/객체 어디든 응용 가능.</li>
<li><strong>분배 조건부 타입</strong>을 이해해야 예측 가능한 결과를 얻는다.</li>
</ul>
<p>관광데이터 공모전 준비를 하느라 2달동안 블로그를 못썼다..ㅠㅠ 이제 제출을 해서 다시 타입스크립트 공부를 시작했다! 다시 일주일에 하나씩 써보도록 해야겠다!!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[뜸했던 7월.. 한달동안 뭘 했을까?]]></title>
            <link>https://velog.io/@cold_mental/%EB%9C%B8%ED%96%88%EB%8D%98-7%EC%9B%94..-%ED%95%9C%EB%8B%AC%EB%8F%99%EC%95%88-%EB%AD%98-%ED%96%88%EC%9D%84%EA%B9%8C</link>
            <guid>https://velog.io/@cold_mental/%EB%9C%B8%ED%96%88%EB%8D%98-7%EC%9B%94..-%ED%95%9C%EB%8B%AC%EB%8F%99%EC%95%88-%EB%AD%98-%ED%96%88%EC%9D%84%EA%B9%8C</guid>
            <pubDate>Wed, 13 Aug 2025 12:46:56 GMT</pubDate>
            <description><![CDATA[<p>와... 진짜 한동안 블로그를 아예 안 썼다..
맨날 &#39;써야지 써야지&#39; 생각은 했는데, 이상하게 손이 안 가더라 ㅠㅠ
그 사이 1달동안 뭘 했냐면! 바로 <strong>정보처리기사 실기 준비랑 ADsP 자격증 준비</strong>를 했다!</p>
<h3 id="☠-공부만-하던-나날들">☠ 공부만 하던 나날들</h3>
<p>정보처리기사 실기 준비는 생각보다 버거웠고,
ADsP는 처음에 &#39;에이 이 정도는 쉬운 거 아냐?&#39; 했다가 회귀분석, 나무모형, 지지도 향상도 이런거 보면서 진짜 현타가 왔다..
괜히 만만하게 봤다가 후회를 많이 했다 :(</p>
<p>하루종일 앉아서 이론 보고, 기출 풀고, 또 보고...
나중엔 &#39;이걸 대체 왜 하고 있는 거지?&#39; 싶을 정도로 무기력해졌다.. ㅋㅋㅋㅋ
그러다 문득, 아 이거 자격증 하나 땄다고 세상이 바뀌지 않겠구나 싶더라</p>
<h3 id="🫠-그런데도-괜찮았던-이유">🫠 그런데도 괜찮았던 이유</h3>
<p>그래도 참 신기한게,
하루하루 별거 안 한 것 같아도,
나중에 모아서 보면 분명 무언가 해낸 게 있긴 하더라!</p>
<p>어쨌든 시험은 이제 다 끝났고,
결과는..!! 기다리는 중이다! (붙을 거 같다 ㅎㅎㅎ)</p>
<h3 id="🔄-다시-만들고-싶어졌다">🔄 다시 만들고 싶어졌다</h3>
<p>시험 끝나고 나서 갑자기 좀 손이 근질근질~
&#39;공부 말고 진짜 뭐 좀 만들고 싶다&#39;
그래서 요즘은 친구랑 같이 사이드 프로젝트 하나 기획 중이다.</p>
<p>아직 초기 단계긴 한데,
사용자 문제 정의, 경쟁 서비스 분석도 해보고, 조금씩 화면 구성도 해보려고 한다!</p>
<p>아직은 막 구체화된 건 없지만,
그래도 &quot;내가 만들고 싶은 걸 만드는&quot; 그 느낌이
자격증 공부보다 천 배 ! 아니? 만 배는 재밌다</p>
<h3 id="🌱-지금은-전환점-같은-시기">🌱 지금은 ‘전환점’ 같은 시기</h3>
<p>딱힌 큰 계기나 목표가 있어서 그런 건 아닌데,
왠지 지금이 내 인생에서 좀 전환점 같은 시기라는 생각이 든다 ㅠㅠ..
이대로는 안될 것 같고, 뭔가 더 나아가고 싶고.</p>
<p>그래서 자격증도 도전했고, 공부도 해보고,
이젠 다시 프로젝트도 하고,
나라는 사람의 바닥을 좀 더 단단히 다져보려고 하는 중이다.. 
( 그래서 이번달 말에도 토익을 본다! )
=&gt; 이말은? 이번 달도 블로그를 못 쓸 수도 있다는..? 아마..?</p>
<h3 id="💭-앞으로는">💭 앞으로는</h3>
<p>그래서 당분간은 너무 정리된 글 말고,
그냥 이렇게 일상적인 얘기를 좀 써보려고 한다.</p>
<p>꼭 무언가 알려주는 글이 아니어도 괜찮고,
코드가 없어도 괜찮고, 그냥 요새 나의 일상 정도만 적어도 충분한 것 같다.</p>
<p>빠른 시일 내로 또 적어봐야겠다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[타입스크립트 제네릭으로 Promise 다뤄보기]]></title>
            <link>https://velog.io/@cold_mental/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%A0%9C%EB%84%A4%EB%A6%AD%EC%9C%BC%EB%A1%9C-Promise-%EB%8B%A4%EB%A4%84%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@cold_mental/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%A0%9C%EB%84%A4%EB%A6%AD%EC%9C%BC%EB%A1%9C-Promise-%EB%8B%A4%EB%A4%84%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Sat, 28 Jun 2025 04:05:38 GMT</pubDate>
            <description><![CDATA[<p>하아.. 일주일마다 쓰려고 했는데 또 늦어버렸다.. 그래도 꾸준히 쓰려는 마음이 더 중요한거 아닐까? ㅎㅎㅎ.. 쨋든 오늘도 타입스크립트 관련해서 써보려고 한다.</p>
<h4 id="promsie-다루어보기">Promsie 다루어보기</h4>
<p>처음엔 단순히 비동기 함수를 만들 때 <code>Promise&lt;any&gt;</code> 같은 식으로 다 썼다. 그냥 값이 나중에 오니까 그거 받고 <code>.then()</code> 붙이거나 <code>await</code> 쓰면 되겠지 싶었다. 근데 프로젝트 규모가 커질수록 이런 애매한 타입들이 점점 문제를 일으키기 시작했다.</p>
<p>이 함수가 뭘 반환하는지를 명확히 알 수 없으니, <code>await</code>로 받아도 타입 추론이 안되고, 결국 또 타입을 <strong>단언</strong>하거나 <strong>따로 주석</strong>으로 적는 일이 많아졌다. 이건 타입스크립트를 쓰는 이유랑 거리가 멀어 보였다.</p>
<p>그래서 알게 된 것이 <strong>제네릭을 활용한 Promise 타입 지정</strong>이다.</p>
<hr>
<h1 id="왜-any-는-부족한가">왜 any 는 부족한가..?</h1>
<p>예를 들어서 아래와 같은 API 호출 함수가 있다고 해보자.</p>
<pre><code class="language-ts">function fetchData(url: string): Promise&lt;any&gt; {
    return fetch(url).then(res =&gt; res.join());
}</code></pre>
<p>표면적으로는 문제가 없어 보인다. 하지만 실제로 <code>await fetchData(...)</code>를 한 그다음 줄 부터는 그 갑싱 어떤 구조인지 IDE도 모르고, 나도 확신할 수 없다. 타입스크립트를 쓰는 이유가 그 &quot;확신&quot;을 갖기 위함인데, 이 코드에는 그게 없다..!</p>
<hr>
<h1 id="제네릭으로-타입을-주입받게-만든다면">제네릭으로 타입을 주입받게 만든다면?</h1>
<p>그래서 아래처럼 바꿔본다.</p>
<pre><code class="language-ts">function fetchData&lt;T&gt;(url: string): Promise&lt;T&gt; {
    return fetch(url).then(res =&gt; res.join());
}</code></pre>
<p>이제 이 함수는 <strong>내가 직접 예상하는 타입을 명시적으로 지정할 수 있게 된다.</strong></p>
<pre><code class="language-ts">type User = {
    id: number;
      name: string;
}

const getUser = async () =&gt; {
    const user = await fetchData&lt;User&gt;(&#39;/api/user/1&#39;);
      console.log(user.name); // &lt;- 타입이 보장됨
}</code></pre>
<p>진짜 중요한건 여기서 <code>fetchData</code> 함수가 <code>User</code> 타입을 반환한다는 <strong>약속</strong>을 함수 정의가 아니라 호출부에서 직접 명시했다는 점이다. 이건 엄청나게 큰 차이다. 호출하는 사람이 의도를 명확히 표현할 수 있고, 타입 추론까지 자연스럽게 따라오게 된다.</p>
<hr>
<h1 id="t를-활용한-응답-구조-커스터마이징">T를 활용한 응답 구조 커스터마이징</h1>
<p>더 나아가면, 요즘 흔히 쓰는 API 구조처럼 응답이 다음과 같이 생겨 있을 수 있다.</p>
<pre><code class="language-json">{
    &quot;success&quot; : true,
      &quot;data&quot; : {
        &quot;id&quot; : 1,
          &quot;name&quot; : &quot;Lee&quot;
    }
}</code></pre>
<p>이런 경우에 일반화를 하고 싶다면?</p>
<pre><code class="language-ts">type ApiResponse&lt;T&gt; = {
    success: boolean;
      data: T;
};

function fetchApi&lt;T&gt;(url:string): Promise&lt;ApiResponse&lt;T&gt;&gt; {
    return fetch(url).then(res =&gt; res.json());
}</code></pre>
<p>이렇게 하면 어떤 데이터든 <code>data</code> 안에 들어간다는 구조를 유지하면서도 <code>T</code> 를 통해 유연하게 타입을 바꿀 수 있다.</p>
<pre><code class="language-ts">type Product = {
    id: number;
      price: number;
};

const getProduct = async () =&gt; {
    const response = await fetchApi&lt;Product&gt;(&#39;/api/product/1&#39;);
      if (response.success) {
        console.log(response.data.price); // &lt;- 타입에 안전하게 접근 가능.
    }
}</code></pre>
<p>이걸 써보면 느끼는게 있다.
<strong>제네릭은 단순히 타입을 뿌옇게 만드는 게 아니라, 타입을 더 정확하게 유지하면서도 확장성을 높여주는 방식이다.</strong></p>
<hr>
<h1 id="제네릭-promise는-타입을-연결하는-다리다">제네릭 Promise는 타입을 연결하는 다리다!</h1>
<p>정리하자면, 타입스크립트에서 <code>Promise&lt;T&gt;</code> 는 단순히 &quot;나중에 올 거야&quot;를 표현하는게 아니라,
<strong>&quot;이 값이 어떤 타입인지 정확하게 예측할 수 있어&quot;라는 확신을 유지한 채 비동기 흐름을 다루는 도구</strong>가 된다.</p>
<p>그리고 이걸 제네릭으로 만들면, 다음과 같은 이점이 생긴다</p>
<ul>
<li>API 응답마다 구조가 달라도 <code>T</code> 하나로 통일된 패턴 유지 가능</li>
<li>호출하는 쪽에서 의도를 명확하게 드러낼 수 있다.</li>
<li>타입 추론이 자연스럽게 따라와서 실수를 줄인다.</li>
<li>리팩토링을 할 때도 타입이 따라오므로 유지보수가 수월하다.</li>
</ul>
<hr>
<h1 id="결론">결론</h1>
<p>처음엔 <code>Promise&lt;T&gt;</code>처럼 생긴 문법이 복잡하고 과하다고 느껴질 수 있다.
하지만 프로젝트를 계속 진행을 하다 보면, &quot;이 함수가 어떤 데이터를 반환하지?&quot; 하는 질문이 계속 머릿속에 맴돈다..</p>
<p>그럴 때마다 나는 <code>Promsie&lt;any&gt;</code> 대신 <code>Promise&lt;T&gt;</code> 를 고민해본다.
&quot;이 데이터가 어떤 타입이어야 하고, 그게 함수 호출부에까지 드러나야 하지 않을까?&quot; 라는 생각 말이다.</p>
<p>다음에는 타입 조작이나 조건부, 유틸리티 타입에 대해 알아보려고 한다
그러면 일주일동안 퇴근하고 공부를 해야겠지..ㅠㅠ 열심히 해보자!!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[타입스크립트 제네릭에 대해서]]></title>
            <link>https://velog.io/@cold_mental/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%A0%9C%EB%84%A4%EB%A6%AD%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C</link>
            <guid>https://velog.io/@cold_mental/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%A0%9C%EB%84%A4%EB%A6%AD%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C</guid>
            <pubDate>Mon, 09 Jun 2025 12:56:13 GMT</pubDate>
            <description><![CDATA[<p>요새 일이 너무 바빠서 블로그를 쓸 힘이 없었다.. 거의 3주동안 못썼는데, 다시 마음을 다잡고 일주일에 하나씩은 써보도록 해야겠다 ㅠ_ㅠ</p>
<p>오늘은 타입스크립트의 제네릭에 대해서 알아보도록 할거다.
개발을 하다 보면 어떤 타입에 종속되지 않는 로직을 만들고 싶을 때가 많다. 예를 들어 배열의 첫 번째 값을 반환하는 함수 같은 경우가 대표적인데, 숫자 배열일 수도 있고, 문자열 배열일 수도 있고, 객체 배열일 수도 있다.</p>
<p>그렇다고 매번 <code>any[]</code>로 받을 수는 없다. 그렇게 하면 타입 추론이 사라지고 타입스크립트를 쓰는 의미가 없어진다. 그때 등장하는게 <strong>제네릭(Generic)</strong>이다.</p>
<h1 id="왜-제네릭이-필요할까">왜 제네릭이 필요할까??</h1>
<p>가장 먼저 했던 고민은 이거였다.
&quot;지금 이 함수는 <code>number[]</code>일 때도, <code>string[]</code>일 때도 돌아가는데... 그럼 타입을 뭘로 적어야 하지..?&quot;</p>
<pre><code class="language-ts">function getFirst(arr: any[]): any {
    return arr[0];
}</code></pre>
<p>이렇게 <code>any</code>를 쓰면 모든 타입을 받을 수 있는 것 같긴 한데, 그 순간부터 타입스크립트의 타입 보호를 전혀 못 받게 된다. 예를 들어 아래 코드는 에러가 안 나지만, 런타임에 터질 가능성이 있다.</p>
<pre><code class="language-ts">const name = getFirst([&#39;Lee&#39;, &#39;Kim&#39;]);
name.toUpperCase(); // 문제 없음. 하지만 number 배열을 넣었다면??</code></pre>
<p>그래서 등장하는게 바로 <strong>제네릭 함수</strong>다.</p>
<pre><code class="language-ts">function getFirst&lt;T&gt;(arr: T[]): T {
    return arr[0];
}</code></pre>
<p>여기서 <code>&lt;T&gt;</code>는 그냥 &quot;아무 타입&quot;이라는 의미가 아니라, <strong>호출하는 시점에서 타입을 넘겨 줄 수 있도록 만드는 자리</strong>다. 함수 내부에서는 이 <code>T</code>라는 타입이 계속 이어지니까, 입력이 <code>string[]</code>이면 출력도 <code>string</code>이고, <code>number[]</code>이면 출력도 <code>number</code>가 된다.</p>
<pre><code class="language-ts">const name = getFirst([&#39;Lee&#39;, &#39;Kim&#39;]); // string
const age = getFirst([10,20,30]); // number</code></pre>
<p>타입을 <strong>추론</strong>해주는 것도 좋지만, 필요하면 명시적으로도 적을 수 있다.</p>
<pre><code class="language-ts">const result = getFirst&lt;number&gt;([1,2,3]);</code></pre>
<h1 id="제네릭은-단지-타입을-나중에-정하자는게-아니다">제네릭은 단지 &quot;타입을 나중에 정하자&quot;는게 아니다!</h1>
<p>단순히 &quot;타입을 나중에 정한다&quot;는 것보다 중요한 <strong>타입 간의 관계를 유지해준다</strong>는 거다. 이걸 깨닫고 나서 제네릭이 왜 그렇게 강력한지 체감됐다.</p>
<p>예를 들어, 다음과 같은 코드를 봐보자.</p>
<pre><code class="language-ts">function wrap&lt;T&gt;(value: T): {value: T} {
    return { value };
}</code></pre>
<p>이 함수는 어떤 타입을 받든 간에, 그 타입을 <code>value</code> 라는 키에 넣어 감싼 객체를 반환한다. 중요한 건, 반환 타입이 단순한 <code>{value:any}</code>가 아니라, 전달받은 <code>T</code> 그대로라는 점이다.</p>
<pre><code class="language-ts">const wrapped = wrap(&#39;hello&#39;); // {value: string}</code></pre>
<p>이처럼 제네릭을 쓰면, <strong>입력과 출력이 하나의 타입으로 연결</strong>되기 때문에 타입 안정성이 높아진다. 객체의 일부를 복사하는 유틸 함수나, API 응답을 처리하는 함수에서도 이 패턴이 유용하게 쓰인다.</p>
<h1 id="제네릭을-여러-개-쓸-수도-있다">제네릭을 여러 개 쓸 수도 있다!</h1>
<p>실제로 유틸 함수들을 만들다 보면 두 개 이상의 타입 관계를 설정하고 싶을 때도 있다.</p>
<pre><code class="language-ts">function merge&lt;A,B&gt;(a: A, b: B): A &amp; B {
    return { ...a, ...b };
}

const merged = merge({ name: &#39;Lee&#39; },{ age: 27});
// merged: { name: string; age: number }</code></pre>
<p>여기서 <code>A &amp; B</code>는 타입 병합을 사용한 건데, 두 객체를 합친 결과의 타입도 자동으로 보장된다. 이걸 직접 명시하면 상당히 귀찮고 헷갈리는데, 제네릭으로 자연스럽게 해결된다.</p>
<h1 id="제네릭-제약-조건">제네릭 제약 조건?</h1>
<p>처음엔 제네릭을 쓰다 보면 너무 자유로워서 타입 에러가 잘 안나서 당황했다. 예를 들어</p>
<pre><code class="language-ts">function printLength&lt;T&gt;(value: T) {
    return value.length; // 에러!
}</code></pre>
<p>왜냐하면 T는 어떤 타입일지 모르기 때문에 <code>length</code> 프로퍼티가 있다는 걸 보장할 수 없다는 거다. 그럴 땐 제네릭에 제약을 줄 수 있다.</p>
<pre><code class="language-ts">function printLength&lt;T extends { length: number }&gt;(value: T) {
    return vlaue.length; // 에러 X
}</code></pre>
<p>이렇게 하면 <code>length</code> 가 있는 타입만 받을 수 있게 된다. 문자열, 배열, length가 있는 객체 등은 모두 통과가 된다.</p>
<hr>
<h1 id="마무리하며">마무리하며...</h1>
<p>결국 제네릭은 <strong>타입을 유연하게 받되, 그 사이의 관계를 안전하게 유지</strong>할 수 있도록 도와주는 도구다. 단순히 어떤 타입이든 받아서 쓰는 게 아니라, 호출 시점에 타입이 주입되고 그 타입이 함수 전체를 관통하면서 연결된다는 점이 제네릭의 진짜 핵심이다.</p>
<p>처음엔 괜히 복잡하고 과한 문법처럼 보였지만, 프로젝트 규모가 커지고 타입 복잡도가 올라가면 갈수록 제네릭 없이는 만들 수 없는 함수나 구조들이 생겨난다.</p>
<p>그래서 지금은 그냥 외워서 쓰기보다는, &quot;이 함수의 타입이 고정돼 있지 않고 외부에서 정해졌으면 좋겠다&quot; 싶은 순간마다 제네릭을 떠올려본다.</p>
<p>그게 아마 타입스크립트를 좀 더 잘 쓰는 방향 중 하나일 거라고 믿는다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React - 불필요한 렌더링 방지?]]></title>
            <link>https://velog.io/@cold_mental/React-%EB%B6%88%ED%95%84%EC%9A%94%ED%95%9C-%EB%A0%8C%EB%8D%94%EB%A7%81-%EB%B0%A9%EC%A7%80</link>
            <guid>https://velog.io/@cold_mental/React-%EB%B6%88%ED%95%84%EC%9A%94%ED%95%9C-%EB%A0%8C%EB%8D%94%EB%A7%81-%EB%B0%A9%EC%A7%80</guid>
            <pubDate>Wed, 21 May 2025 12:52:51 GMT</pubDate>
            <description><![CDATA[<p>리액트를 사용하다 보면, 종종 &quot;생각보다 너무 자주 리렌더링이 발생한다&quot;는 문제를 마주하게 된다. 특히, 부모 컴포넌트가 자주 렌더링되면 그 자식 컴포넌트들도 불필요하게 리렌더링되는 경우가 많다. 이런 경우 성능 최적화가 필요한데, 그 대표적인 방법들을 하나씩 고민해보며 정리해보자!!</p>
<hr>
<h1 id="1reactmemo">1.React.memo</h1>
<p><code>React.memo</code>는 함수형 컴포넌트를 메모제이션하여, <strong>props가 변경되지 않으면 리렌더링을 방지하는 고차 컴포넌트(HOC)</strong>이다.</p>
<pre><code class="language-js">const MemoizedComponent = React.memo(MyComponent);</code></pre>
<p>예를 들어, 부모 컴포넌트에서 매번 새로 렌더링될 때 자식 컴포넌트도 같이 렌더링된다면, <code>React.memo</code>를 사용해 props가 바뀌지 않았을 경우 리렌더링을 막을 수 있다.</p>
<p>하지만, 주의할 점은 <code>React.memo</code>는 props의 얕은 비교만 수행한다. 즉, 객체나 배열 같은 참조 타입이 매번 새로 생성되는 경우, props가 변경되는 것으로 인식하로 리렌더링된다.</p>
<p>그래서 이런 경우에는 <strong>useMemo, useCallback</strong>을 조합해서 써야 진짜 효과를 볼 수 있다 !</p>
<hr>
<h1 id="2-usememo-vs-usecallback">2. useMemo vs useCallback</h1>
<h3 id="usememo">useMemo</h3>
<p><code>useMemo</code>는 <strong>계산 비용이 높은 값을 메모제이션</strong>하는데 사용한다.</p>
<pre><code class="language-js">const memoizedValue = useMemo(() =&gt; computeExpensiveValue(a,b), [a,b]);</code></pre>
<p>예를 들어 리스트를 정렬하거나 필터링하는 연산처럼 리렌더링마다 계속 반복하기에는 비싼 작업이 있다면, useMemo를 사용해서 의존성 값이 바뀔 때만 다시 계산되도록 할 수 있다.</p>
<h3 id="usecallback">useCallback</h3>
<p><code>useCallback</code>은 <strong>함수를 메모제이션</strong>하는데 사용된다. 주로 자식 컴포넌트에 콜백을 props로 넘길 때 유용하다.</p>
<pre><code class="language-js">const memoizedCallback = useCallback(() =&gt; doSomething(a,b), [a,b]);</code></pre>
<p>이거 안 쓰면 함수는 매번 새로 정의되기 때문에, 참조가 변경되어 자식 컴포넌트가 불필요하게 리렌더링된느 문제가 생긴다.</p>
<h3 id="그러면-모든-곳에-다-써야돼">그러면 모든 곳에 다 써야돼???</h3>
<p>정답은 <strong>절대 ! 아니다 !</strong></p>
<blockquote>
<p>메모제이션은 &quot;최적화&quot;이지 &quot;기본값&quot;이 아니다.</p>
</blockquote>
<p>이유를 말하자면</p>
<ul>
<li><code>useMemo</code>, <code>useCallback</code>도 <strong>결국 비용이 든다.</strong><ul>
<li>메모리에 값을 저장해야 하고,</li>
<li>의존성 배열을 비교해야 하며,</li>
<li>값이 바뀌엇는지도 판단해야 한다.</li>
</ul>
</li>
</ul>
<p>예를 들어 아래 코드처럼 엄청 단순한 함수는 굳이 <code>useCallback</code>을 쓸 필요가 없다.</p>
<pre><code class="language-js">const handleClick = () =&gt; {
    console.log(&quot;clicked&quot;);
}</code></pre>
<p><del>근데 이런 함수에 쓰는건 미친거....</del></p>
<p>이걸 <code>useCallback(() =&gt; {}, [])</code>로 바꿔도, 실제로 성능이 더 좋아지지 않고, 오히려 복잡성을 추가할 뿐이다.</p>
<p>반대로, 렌더링마다 반복되는 무거운 연산이라면 <code>useMemo</code>가 유리하다.</p>
<hr>
<h1 id="3-react-devtools-profiler">3. React DevTools Profiler</h1>
<p>말로만 &quot;리렌더링이 많다&quot;고 하지 말고, 실제로 확인하는게 가장 확실하다.</p>
<p><code>React DevTools</code>의 <strong>Profiler</strong> 탭을 이용하면,</p>
<ul>
<li>어떤 컴포넌트가</li>
<li>왜 리렌더링 됐는지</li>
<li>얼마나 시간이 걸리는지</li>
</ul>
<p>이런걸 시각적으로 확인할 수 있다.</p>
<p>이를 통해 진짜 병목이 발생하는 부분을 찾아내고, <strong>선별적으로 최적화</strong>하는 것이 가장 이상적인 접근이다.</p>
<hr>
<h1 id="정리하며">정리하며...</h1>
<ol>
<li><strong>React.memo</strong>로 컴포넌트 리렌더링 자체를 막을 수 있다.</li>
<li><strong>useMemo</strong>, <strong>useCallback</strong>은 props나 연산 결과를 메모이제이션해서 불필요한 렌더링/재계산을 줄인다.</li>
<li>하지만 무분별하게 쓰면 오히려 손해다. 메모이제이션도 비용이기 때문.</li>
<li>최적화는 <strong>병목이 보일 때, 꼭 필요한 지점에만.</strong> Profiler로 확인하고 적용하자.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS - Promise ?]]></title>
            <link>https://velog.io/@cold_mental/JS-Promise</link>
            <guid>https://velog.io/@cold_mental/JS-Promise</guid>
            <pubDate>Thu, 15 May 2025 23:14:16 GMT</pubDate>
            <description><![CDATA[<p>이번엔 자바스크립트에서 비동기 처리를 위해 가장 많이 사용되는 방식 중 하나인 Promise에 대해 이야기해보려고 한다. 사실 예전에는 콜백을 사용해서 비동기 작업을 처리했지만, 콜백이 많아지면 코드가 정말 지저분해지고, 무슨 일이 먼저 실행되는지도 헷갈리게 된다. 이걸 보통 &quot;콜백 지옥&quot;이라고 부르기도 한다.</p>
<h1 id="promise란">Promise란?</h1>
<p><code>Promise</code>는 <strong>비동기 작업의 완료나 실패를 처리하기 위한 객체</strong>다. 단순히 &quot;성공했을 때 뭐 하고, 실패했을 땐 이거 하고&quot;를 좀 더 체계적이고 읽기 좋은 방식으로 다루기 위한 개념이라고 보면 된다.</p>
<pre><code class="language-js">const promise = new Promise((resolve, reject) =&gt; {
  const success = true;

  if (success) {
    resolve(&quot;작업 성공!&quot;);
  } else {
    reject(&quot;작업 실패!&quot;);
  }
});

promise
  .then((result) =&gt; {
    console.log(result);
  })
  .catch((err) =&gt; {
    console.error(err);
  });
</code></pre>
<h1 id="promise의-상태-state">Promise의 상태 (State)</h1>
<p>Promise는 <strong>세 가지 상태</strong>를 가진다:</p>
<ul>
<li><code>Pending</code> : 초기 상태, 아직 성공도 실패도 하지 않은 상태</li>
<li><code>Fulfilled</code> : 작업이 성공적으로 완료된 상태 → <code>resolve()</code> 호출됨</li>
<li><code>Rejected</code> : 작업이 실패한 상태 → <code>reject()</code> 호출됨</li>
</ul>
<p>한 번 <code>Fulfilled</code>나 <code>Rejected</code> 상태가 되면, 그 이후로는 절대 다른 상태로 바뀌지 않는다. 이게 Promise가 &quot;결과를 약속한다&quot;는 의미이기도 하다.</p>
<h1 id="왜-promise가-필요했을까">왜 Promise가 필요했을까?</h1>
<p>예전 방식은 보통 이런 식이었다:</p>
<pre><code class="language-js">function fetchData(callback) {
  setTimeout(() =&gt; {
    callback(&quot;데이터 도착!&quot;);
  }, 1000);
}

fetchData((data) =&gt; {
  console.log(data);
});</code></pre>
<p>하지만 이런 방식으로 비동기 작업이 중첩되면 아래처럼 된다:</p>
<pre><code class="language-js">doSomething((result) =&gt; {
  doAnotherThing(result, (next) =&gt; {
    doFinalThing(next, (final) =&gt; {
      console.log(final);
    });
  });
});</code></pre>
<p>이게 바로 콜백 지옥이다. 들여쓰기도 계속 깊어지고, 오류 처리도 어렵고, 가독성도 최악이다. 그래서 나온 해결책이 바로 <strong>Promise</strong>다.</p>
<p>Promise를 사용하면 이렇게 바꿀 수 있다:</p>
<pre><code class="language-js">doSomething()
  .then(doAnotherThing)
  .then(doFinalThing)
  .then(console.log)
  .catch(console.error);</code></pre>
<h1 id="promise의-단점은-없을까">Promise의 단점은 없을까?</h1>
<p>있다. 아무리 좋아 보여도 완벽한 해결책은 아니다. 크게 두 가지 문제를 정리해볼 수 있다.</p>
<h3 id="1-복잡한-에러-처리">1. 복잡한 에러 처리</h3>
<p>단일 체인에서는 <code>.catch()</code> 하나로 커버가 되지만, <strong>중첩된 비동기 흐름</strong>이 생기면 어디서 예외가 발생했는지 명확하게 처리하기가 어렵다. 예를 들어, 중간의 <code>then()</code> 안에서 발생한 오류와 마지막에서 발생한 오류를 구분하기 어렵다.</p>
<pre><code class="language-js">fetch(&quot;/data&quot;)
  .then((res) =&gt; res.json())
  .then((data) =&gt; {
    // 여기서 오류가 나면 catch에서 잡지만
    throw new Error(&quot;중간 오류&quot;);
  })
  .catch((err) =&gt; {
    // 어디서 발생한 오류인지 구체적으로 파악하기 어렵다
    console.error(err);
  });</code></pre>
<h3 id="2-then-체인의-한계-콜백-지옥의-변형">2. then 체인의 한계 (콜백 지옥의 변형)</h3>
<p><code>then()</code> 체인을 너무 많이 쓰다 보면 결국 그 자체가 지저분해지기도 한다. 들여쓰기는 줄어들 수 있지만, <strong>순차 로직이 많을수록 가독성은 여전히 떨어진다</strong>.</p>
<p>그래서 나온 게 바로 <code>async/await</code>. 이건 다음에 따로 정리하겠지만, 결론적으로 Promise를 쓰면서 생긴 한계를 좀 더 자연스럽게 풀어준다.</p>
<h1 id="마무리">마무리</h1>
<ul>
<li><code>Promise</code>는 비동기 처리를 훨씬 깔끔하고 체계적으로 만들었다.</li>
<li><code>resolve</code>와 <code>reject</code>를 통해 성공과 실패를 다룬다.</li>
<li>세 가지 상태(Pending, Fulfilled, Rejected)로 나뉘며, 상태는 한 번 바뀌면 고정된다.</li>
<li>여러 비동기 작업을 연결하거나 병렬 처리할 때 매우 유용하다.</li>
<li>하지만 깊은 에러 처리나 과도한 then 체인에서는 여전히 한계가 있다.</li>
</ul>
<p>그래서 진짜로 실무에선 보통 <code>Promise</code>를 기본 단위로 쓰되, 최종적으로는 <code>async/await</code>와 함께 쓰는 경우가 많다. 다음 글에선 그 얘기도 해보자!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TypeScript 클래스에 대해서]]></title>
            <link>https://velog.io/@cold_mental/TypeScript-%ED%81%B4%EB%9E%98%EC%8A%A4%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C</link>
            <guid>https://velog.io/@cold_mental/TypeScript-%ED%81%B4%EB%9E%98%EC%8A%A4%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C</guid>
            <pubDate>Tue, 06 May 2025 07:59:53 GMT</pubDate>
            <description><![CDATA[<p>최근에 타입스크립트를 조금 더 깊이 있게 파고들고 있는데, 이번엔 &#39;클래스&#39;를 정리해보려고 한다. 자바스크립트에도 클래스가 있지만, 타입스크립트에서는 타입 시스템과 함께 클래스가 훨씬 더 구조적으로 다뤄진다. 특히 접근 제어자나 인터페이스와의 결합이 굉장히 매력적이다.</p>
<hr>
<h1 id="1-타입스크립트에서-클래스란">1. 타입스크립트에서 클래스란?</h1>
<p>자바스크립트의 클래스 문법을 확장한 형태다. 중요한 차이점은 <strong>클래스 필드에 타입을 명시적으로 지정할 수 있다는 것</strong>이다. 타입을 지정하지 않으면 매개변수와 마찬가지로 암시적으로 <code>any</code>로 추론되는데, strict 모드에선 에러가 발생할 수 있다.</p>
<pre><code class="language-ts">class Employee {
  name: string = &quot;&quot;;
  age: number = 0;
  position: string = &quot;&quot;;

  work() {
    console.log(&quot;일함&quot;);
  }
}</code></pre>
<h3 id="생성자를-통한-필드-초기화">생성자를 통한 필드 초기화</h3>
<p>생성자에서 필드를 잘 초기화해주면 클래스 선언 시 초기값은 생략 가능하다.</p>
<pre><code class="language-ts">class Employee {
  name: string;
  age: number;
  position: string;

  constructor(name: string, age: number, position: string) {
    this.name = name;
    this.age = age;
    this.position = position;
  }

  work() {
    console.log(&quot;일함&quot;);
  }
}
</code></pre>
<h3 id="선택적-프로퍼티">선택적 프로퍼티</h3>
<pre><code class="language-ts">class Employee {
  name: string;
  age: number;
  position?: string;

  constructor(name: string, age: number, position?: string) {
    this.name = name;
    this.age = age;
    this.position = position;
  }
}</code></pre>
<hr>
<h1 id="2-클래스는-타입이다">2. 클래스는 타입이다!</h1>
<p>타입스크립트에서는 클래스 자체가 타입으로도 사용된다.</p>
<pre><code class="language-ts">const employee: Employee = {
  name: &quot;&quot;,
  age: 0,
  position: &quot;&quot;,
  work() {},
};</code></pre>
<hr>
<h1 id="3-상속과-super">3. 상속과 super()</h1>
<pre><code class="language-ts">class ExecutiveOfficer extends Employee {
  officeNumber: number;

  constructor(name: string, age: number, position: string, officeNumber: number) {
    super(name, age, position);
    this.officeNumber = officeNumber;
  }
}</code></pre>
<p>파생 클래스에서 생성자를 정의하면 반드시 <code>super()</code>를 호출해야 하며, 생성자의 <strong>최상단</strong>에서 호출해야 한다.</p>
<hr>
<h1 id="4-접근-제어자-access-modifier">4. 접근 제어자 (Access Modifier)</h1>
<p>타입스크립트는 다음과 같은 접근 제어자를 제공한다.</p>
<ul>
<li><code>public</code>: 어디서든 접근 가능 (기본값)</li>
<li><code>private</code>: 클래스 내부에서만 접근 가능</li>
<li><code>protected</code> : 클래스 내부 + 파생 클래스에서만 접근 가능</li>
</ul>
<h4 id="예시">예시:</h4>
<pre><code class="language-ts">class Employee {
  private name: string;
  protected age: number;
  public position: string;

  constructor(name: string, age: number, position: string) {
    this.name = name;
    this.age = age;
    this.position = position;
  }

  work() {
    console.log(`${this.name}이 일함`);
  }
}

class ExecutiveOfficer extends Employee {
  showAge() {
    console.log(this.age); // 가능
  }
}

const emp = new Employee(&quot;Lee&quot;, 30, &quot;Developer&quot;);
emp.name; // ❌
emp.age;  // ❌
emp.position; // ✅</code></pre>
<hr>
<h1 id="5-생성자에서-필드-생략하기">5. 생성자에서 필드 생략하기</h1>
<p>생성자의 매개변수에 접근 제어자를 붙이면 <strong>필드 선언과 this 할당을 생략</strong>할 수 있다.</p>
<pre><code class="language-ts">class Employee {
  constructor(
    private name: string,
    protected age: number,
    public position: string
  ) {}

  work() {
    console.log(`${this.name} 일함`);
  }
}</code></pre>
<p>이게 훨씬 깔끔하다.</p>
<hr>
<h1 id="6-인터페이스-구현">6. 인터페이스 구현</h1>
<p>타입스크립트의 인터페이스는 클래스가 반드시 따라야 하는 <strong>설계도</strong> 역할을 한다.</p>
<pre><code class="language-ts">interface CharacterInterface {
  name: string;
  moveSpeed: number;
  move(): void;
}

class Character implements CharacterInterface {
  constructor(
    public name: string,
    public moveSpeed: number,
    private extra: string
  ) {}

  move() {
    console.log(`${this.moveSpeed} 속도로 이동!`);
  }
}</code></pre>
<p>인터페이스를 통해 클래스의 구조를 강제할 수 있어서 협업이나 코드 관리에 유용하다.</p>
<hr>
<p>이런 구조 덕분에 타입스크립트의 클래스는 훨씬 더 견고하고 유지보수하기 좋은 구조로 코드를 작성할 수 있게 해준다. 다음엔 <code>abstract class</code>, <code>readonly</code>, <code>static</code> 키워드도 정리해봐야겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SSR, 왜 이런 방식이 필요한 걸까]]></title>
            <link>https://velog.io/@cold_mental/SSR-%EC%99%9C-%EC%9D%B4%EB%9F%B0-%EB%B0%A9%EC%8B%9D%EC%9D%B4-%ED%95%84%EC%9A%94%ED%95%9C-%EA%B1%B8%EA%B9%8C</link>
            <guid>https://velog.io/@cold_mental/SSR-%EC%99%9C-%EC%9D%B4%EB%9F%B0-%EB%B0%A9%EC%8B%9D%EC%9D%B4-%ED%95%84%EC%9A%94%ED%95%9C-%EA%B1%B8%EA%B9%8C</guid>
            <pubDate>Mon, 28 Apr 2025 23:41:44 GMT</pubDate>
            <description><![CDATA[<p>SSR이 뭐고, CSR이 뭔지 설명하는 글은 이미 많다.<br>나도 많이 봤다. 근데 이상하게, 머릿속에 확실히 남지 않았다.<br>&quot;아 서버에서 렌더링하는 거야, 브라우저에서 하는 거야&quot;  말로야 할 수 있었는데,<br><strong>&quot;왜 이런 방식을 쓰는지&quot;</strong>,  
<strong>&quot;그 구조가 실제로 어떤 문제를 풀려고 나온 건지&quot;</strong><br>이건 몸으로 체득되지 않았다.</p>
<p>그래서 이번엔, 남한테 설명하려고 하지 않고,<br>그냥 <strong>나 스스로 납득할 때까지 생각한 과정을</strong> 적어보려고 한다.</p>
<hr>
<h2 id="렌더링-결국-시작점을-어디로-잡느냐의-문제">렌더링, 결국 시작점을 어디로 잡느냐의 문제</h2>
<p>처음으로 떠오른 건 이거였다.<br>&quot;웹페이지가 필요하다&quot;라는 요구가 있을 때,<br>그 화면을 <strong>서버가 미리 만들어서 줄 거냐</strong>,  
아니면 <strong>브라우저가 받아서 스스로 만들게 할 거냐</strong><br>이 두 가지 방법밖에 없다는 거다.</p>
<p>그리고 이 선택 하나로,  모든게 달라진다.</p>
<p>서버가 미리 다 만들어서 주면, 브라우저는 그냥 받아서 보여주기만 하면 되니까 빠르겠지.<br>하지만 서버는 매번 열심히 페이지를 만들어야 한다.<br>그리고 브라우저는 받은 걸로는 상호작용을 잘 못할 거다.<br>자기가 직접 조립한 게 아니니까.</p>
<p>반대로 브라우저가 직접 화면을 만들게 하면, 서버는 간단해진다. 
그냥 껍데기 HTML 주고, 스크립트 파일만 넘기면 된다.<br>근데 브라우저는 처음에 그걸 다 해석하고, 만들어야 하니까, 초기 로딩은 느릴 수밖에 없다.</p>
<hr>
<h2 id="ssr이-풀려고-했던-문제">SSR이 풀려고 했던 문제</h2>
<p>생각해보면, 옛날 웹은 거의 다 서버 렌더링이었다.<br>그때는 CSR 같은 개념 자체가 없었다. HTML을 만들어서 보내는 게 당연했으니까.</p>
<p>근데 요즘은 사용자 경험이 너무 중요해졌다. 버튼 누르면 바로 반응해야 하고,<br>페이지 이동할 때 새로고침 같은 거 없이 부드럽게 넘어가야 하고.<br>그래서 SPA(Single Page Application)가 등장했다. CSR 중심으로.</p>
<p>그런데, CSR만 하니까 문제가 생겼다.</p>
<ul>
<li>첫 화면이 너무 늦게 뜬다.</li>
<li>빈 화면을 오래 보여준다.</li>
<li>검색 엔진이 페이지 내용을 제대로 못 읽는다.</li>
</ul>
<p>결국,
<strong>&quot;초기 화면은 빨라야 한다&quot;</strong>,  
<strong>&quot;검색 엔진도 컨텐츠를 읽을 수 있어야 한다&quot;</strong><br>이걸 다시 해결해야 했다.</p>
<p>그래서 다시 서버에서 미리 화면을 만들어주는 방식을<br>다시 끌어온 거다. 그게 바로 SSR.</p>
<hr>
<h2 id="ssr을-쓰면-좋은-점이-뭔가">SSR을 쓰면 좋은 점이 뭔가</h2>
<p>처음에 이걸 생각했을 때, 제일 먼저 떠오른 건<br><strong>&quot;빠른 첫 화면&quot;</strong>이었다.</p>
<p>SSR은 브라우저가 별로 할 일이 없다.<br>그냥 서버가 준 완성된 HTML을 바로 띄우면 된다.<br>그러니까 사용자 입장에서는 클릭하자마자 바로 화면이 뿅 하고 나온다.</p>
<p>특히 모바일 네트워크 느린 환경에서는 이 차이가 진짜 크다.</p>
<p>또 하나,<br><strong>&quot;SEO&quot;</strong>.</p>
<p>서버가 준 HTML 안에는 이미 컨텐츠가 다 들어있다.<br>텍스트, 이미지, 메타데이터 다.<br>검색 엔진 로봇이 그걸 읽어가기 정말 쉽다.</p>
<p>만약 CSR이라면?<br>처음엔 아무것도 없는 빈 껍데기다.<br>크롤러가 JS를 실행해서 동적으로 만들어지는 화면을 기다려야 한다.<br>근데 모든 크롤러가 그걸 잘 지원하는 건 아니니까,<br>결국 노출에 손해를 볼 수 있다.</p>
<hr>
<h2 id="그렇다고-ssr이-무조건-좋은-건-아니다">그렇다고 SSR이 무조건 좋은 건 아니다</h2>
<p>생각할수록 느꼈다.<br>SSR도 만만한 게 아니다.</p>
<p>첫 번째로,<br><strong>서버 부하</strong> 문제가 있다.</p>
<p>CSR은 요청이 오면 그냥 JS 파일 던져주면 끝이다.<br>근데 SSR은 요청 올 때마다, <strong>HTML을 새로 만들어야 한다.</strong><br>어떤 유저가 들어왔는지, 그 사람 데이터에 따라 화면을 조합해야 하고,<br>그걸 전부 서버가 실시간으로 해야 한다.</p>
<p>이건 진짜 서버 리소스를 많이 잡아먹는다.<br>유저가 많아질수록 서버는 버거워진다.</p>
<p>두 번째,<br><strong>상호작용 문제</strong>다.</p>
<p>서버가 HTML은 줄 수 있지만, 거기엔 아직 자바스크립트 기능이 없다.<br>클릭 이벤트 같은 건 동작하지 않는다.<br>결국 브라우저가 추가로 JS 파일을 다운받고,<br>HTML에 다시 기능을 붙이는 작업(hydration)을 해야 한다.</p>
<p>이게 느려지면 초기 화면은 떴는데<br>버튼 눌러도 아무 반응 없는 이상한 상황이 생길 수 있다.</p>
<p>(요즘엔 이걸 줄이려고 Partial Hydration 같은 것도 연구되고 있다.)</p>
<hr>
<h2 id="결국-내린-결론">결국 내린 결론</h2>
<p>SSR이든 CSR이든,<br>단순히 &quot;이게 무조건 낫다&quot;는 없다.</p>
<p>서비스 성격에 따라 다르다.</p>
<ul>
<li><p><strong>블로그, 커머스, 뉴스 사이트</strong>처럼<br>빠른 초기 로딩, SEO가 중요한 경우 → SSR이 낫다.</p>
</li>
<li><p><strong>대시보드, 복잡한 앱형 서비스</strong>처럼<br>실시간 반응성과 부드러운 전환이 중요한 경우 → CSR이 낫다.</p>
</li>
</ul>
<p>그리고 현대 웹은 둘 중 하나만 고집하지 않는다.</p>
<p>초기에는 SSR로 빠르게 보여주고,<br>그 이후엔 CSR처럼 부드럽게 조작하는 하이브리드 방식을 쓴다.</p>
<hr>
<h2 id="진짜로-느낀-것">진짜로 느낀 것</h2>
<p>처음엔 SSR, CSR이 그냥 &quot;렌더링 방식&quot;의 차이인 줄 알았다.<br>근데 진짜 고민하고 정리해보니까,<br>이건 단순 기술 싸움이 아니라<br><strong>&quot;사용자가 언제, 어떻게 화면을 만나야 하는가&quot;</strong>에 대한<br><strong>전략적인 선택</strong>이었다.</p>
<p>기술은 결국, <strong>사용자 경험을 어떻게 설계할까</strong>를 결정하는 수단이라는 걸<br>이번에 진짜 몸으로 느꼈다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[axios, 단순 요청도구가 아니었다.]]></title>
            <link>https://velog.io/@cold_mental/axios-%EB%8B%A8%EC%88%9C-%EC%9A%94%EC%B2%AD%EB%8F%84%EA%B5%AC%EA%B0%80-%EC%95%84%EB%8B%88%EC%97%88%EB%8B%A4</link>
            <guid>https://velog.io/@cold_mental/axios-%EB%8B%A8%EC%88%9C-%EC%9A%94%EC%B2%AD%EB%8F%84%EA%B5%AC%EA%B0%80-%EC%95%84%EB%8B%88%EC%97%88%EB%8B%A4</guid>
            <pubDate>Sun, 27 Apr 2025 23:38:28 GMT</pubDate>
            <description><![CDATA[<p>솔직히 말하면, 나도 처음엔 axios를 단순히 &quot;fetch보다 쓰기 편한 애&quot;정도로만 생각했다.
실제로도 그런 설명을 많이 봐왔다.
하지만 프로젝트를 하면스 API 연동이 점점 많아지고, 에러도 자 주 터지다 보니 문득 이런 생각이 들었다.</p>
<p><strong>&quot;axios가 진짜로 뭘 해주는거지? fetch랑 뭐가 다르고, 내부에서 뭘 조작하고 있는 걸까?&quot;</strong></p>
<p>그래서 하나하나 천천히 파보기로 했다.</p>
<hr>
<h2 id="왜-axios를-쓰는-걸까">왜 axios를 쓰는 걸까?</h2>
<p><code>fetch</code>도 요청은 보낼 수 있다.
하지만 코드를 짜다 보면 이런 반복이 너무 많다.</p>
<pre><code class="language-ts">fetch(&#39;/api/user&#39;, {
    method: &quot;POST&quot;,
      headers: {
        &#39;Content-Type&#39;: &#39;application/json&#39;
    },
      body: JSON.stringify({name:&#39;홍길동&#39;})
})
.then((res) =&gt; {
    if (!res.ok) throw new Error(&quot;에러&quot;);
      return res.json();
})</code></pre>
<ul>
<li>JSON.stringify</li>
<li>res.ok 체크</li>
<li>응답 파싱</li>
</ul>
<p>결국 axios는 이런 걸 대신 해주는 거다.
그런데 여기서 멈추면 진짜 아쉬운게, axios는 단순히 <strong>문법 편하게 만드는 도우미</strong>가 아니다.
<strong>axios는 HTTP 요청을 구조적으로 추상화해서, 우리가 통신 흐름을 통제할 수 있게 해주는 도구</strong>다.</p>
<hr>
<h2 id="요청은-단순하지-않다">요청은 단순하지 않다</h2>
<p>HTTP 요청은 단순히 <code>URL + 데이터</code>만 보내는 게 아니다.
요청을 보내기 위해선 반드시 <strong>본문(body)</strong>의 구조와 <strong>그 구조를 설명해줄 헤더(header)</strong>가 필요하다.
예를 들어 이런 상황을 상상해보자.</p>
<blockquote>
<p>&quot;서버야, 내가 데이터를 보낼 건데 JSON 형식이야! 그러니까 JSON으로 파싱해서 읽어줘!&quot;
이걸 말해주는게 바로 <strong>Content-Type</strong>이다.</p>
</blockquote>
<pre><code class="language-http">Content-Type: application/json</code></pre>
<p>만약 이걸 잘못 보내면 서버는 본문을 제대로 해석하지 못한다.
즉, 우리가 아무리 <code>body</code>를 잘 만들어도, Content-Tytpe이 엉뚱하면 서버는 &quot;이거 뭐지?&quot;하고 에러를 낸다.</p>
<hr>
<h2 id="axios는-이걸-자동으로-해준다">axios는 이걸 자동으로 해준다.</h2>
<pre><code class="language-ts">axios.post(&#39;/api/user&#39;, {name: &#39;홍길동&#39;});</code></pre>
<p>위 코드는 다음과 같이 작동한다</p>
<ul>
<li>내부에서 <code>JSON.stringify()</code> 처리</li>
<li>자동으로 <code>Content-Type: application/json</code> 붙임</li>
<li>응답 JSON 자동 파싱</li>
<li>에러 status code에 따라 catch로 보내줌</li>
</ul>
<p>내가 신경 써야 할 건 오로지 <code>데이터</code>뿐이다.
그게 axios의 핵심 강점 중 하나다.</p>
<hr>
<h2 id="그런데-문제는-content-type은-상황에-따라-바뀐다">그런데 문제는... Content-Type은 상황에 따라 바뀐다.</h2>
<h3 id="예-1-x-www-form-urlencoded">예 1: x-www-form-urlencoded</h3>
<p>HTML <code>&lt;form&gt;</code>에서 데이터를 보낼 때는 보통 이렇게 생긴다</p>
<pre><code class="language-bash">name=홍길동&amp;age=24</code></pre>
<p>이런 방식은 <code>application/x-www-form-urlencoded</code>라는 형식인데, JSON이 아니다.
이때는 직접 Content-Type을 바꿔줘야 한다.</p>
<pre><code class="language-ts">import qs from &#39;qs&#39;;

axios.post(&#39;/api/user&#39;, qs.stringify({name:&quot;홍길동&quot;}), {
    headers: {
        &#39;Content-Type&#39;: &#39;application/x-www-form-urlencoded&#39;
    }
});</code></pre>
<p>그리고 <code>body</code>는 JSON이 아니라 <code>쿼리스트링</code>처럼 만들어야 한다.
(즉, stringify 방식도 바뀜)</p>
<hr>
<h3 id="예-2-파일-업로드---multipartform-data">예 2: 파일 업로드 - multipart/form-data</h3>
<p>이미지나 파일을 업로드할 때는 완전히 다른 형식이 필요하다.</p>
<pre><code class="language-ts">const formData = new FormData();
formData.append(&#39;file&#39;, file);

axios.post(&#39;/upload&#39;, formData);</code></pre>
<p>여기서 주의할 점은,
<code>multipart/form-data</code>는 <strong>boundary라는 복잡한 값이 필요해서 Content-Type을 직접 쓰면 안 된다.</strong></p>
<pre><code class="language-ts">headers: {
    &#39;Content-Type&#39;: &#39;multipart/form-data&#39; // X
}</code></pre>
<p>axios 가 내부에서 알아서 설정하게 두는 게 안전하다.</p>
<hr>
<h2 id="axios는-단순한-요청-도우미를-넘어서">axios는 &quot;단순한 요청 도우미&quot;를 넘어서</h2>
<p>내가 axios를 다시 보게 된 결정적 포인트는 인터셉터였다.</p>
<pre><code class="language-ts">axios.interceptors.request.use((config) =&gt; {
  config.headers.Authorization = `Bearer ${token}`;
  return config;
});</code></pre>
<p>요청을 보내기 전에 가로채서 헤더를 추가할 수 있고,
응답을 받은 후에도 가로채서 메시지를 조작하거나 에러 핸들링도 할 수 있다.
이걸 보고 딱 느꼈다.</p>
<blockquote>
<p>&quot;axios&quot;는 단순히 &quot;요청 한 번 보내자&quot;가 아니라
요청의 전 과정을 내가 가공하고 제어할 수 있는 구조를 제공해주는 도구구나.&quot;</p>
</blockquote>
<hr>
<h2 id="자주-겪은-헷갈리는-포인트들">자주 겪은 헷갈리는 포인트들</h2>
<ol>
<li><code>Content-Type</code>이 json인데 body가 JSON이 아니면?
 -&gt; 서버가 파싱 실패 (400 오류)</li>
<li><code>application/json</code> 쓰면 브라우저가 OPTIONS 요청을 보내는 경우도 있다.
 -&gt; 이건 CORS 사전 요청. 서버에서 OPTIONS 허용해야 함.</li>
<li><code>axios.post(url,string)</code>을 썼는데 <code>Content-Type</code>을 안 바꾸면?
 -&gt; axios는 기본적으로 <code>application/json</code>을 붙이니까, string body는 해석 오류 가능</li>
</ol>
<hr>
<h2 id="정리하면">정리하면..</h2>
<ul>
<li>axios는 단순 요청 도구가 아니라, <strong>HTTP 통신 흐름 전체를 관리할 수 있게 해주는 인터페이스</strong>다.</li>
<li>Content-Type은 <strong>본문의 구조를 서버에 알려주는 설명서</strong>고, 이게 맞지 않으면 서버는 말을 이해 못한다.</li>
<li>상황에 따라 본문 인코딩과 Content-Type이 같이 움직이며, axios는 대부분 자동으로 처리하지만 가끔은 우리가 직접 설정해야 한다.</li>
</ul>
<h2 id="결론">결론</h2>
<p>지금까지는 그냥 &quot;axios 쓰면 편하네~&quot; 하고 썼다면,
앞으로는 이게 <strong>어떤 흐름으로 작동하고</strong>,
<strong>서버와 어떤 형식으로 대화하게 만드는지</strong>를 좀 더 구조적으로 이해하고 다뤄보자.</p>
<p>이해한 만큼 에러는 줄어들고,
이해한 만큼 디버깅은 빨라지고,
이해한 만큼 확장성과 통제가 가능해진다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[useEffect vs useLayoutEffect]]></title>
            <link>https://velog.io/@cold_mental/useEffect-vs-useLayoutEffect</link>
            <guid>https://velog.io/@cold_mental/useEffect-vs-useLayoutEffect</guid>
            <pubDate>Thu, 24 Apr 2025 23:30:50 GMT</pubDate>
            <description><![CDATA[<p>React에서 렌더링 후 어떤 작업을 수행할 때 우리는 흔히 <code>useEffect</code>를 사용한다.<br>하지만 <strong>동일한 문법, 다른 타이밍</strong>으로 작동하는 <code>useLayoutEffect</code>도 존재한다.</p>
<p>그저 &quot;하나는 비동기, 하나는 동기&quot;라고 외워두기보다는,<br><strong>이 둘이 왜 분리되었는지</strong>,  
<strong>리액트의 렌더링 구조상 어떤 목적을 위한 것인지</strong><br><strong>그리고 우리가 언제 어떤 것을 선택해야 하는지</strong>를 구조적으로 이해해보자.</p>
<hr>
<h2 id="왜-둘로-나뉘었을까">왜 둘로 나뉘었을까?</h2>
<p>리액트는 기본적으로 <strong>렌더링 결과를 화면에 출력하는 일을 브라우저에게 위임</strong>한다.<br>즉, 컴포넌트 함수가 실행되면 가상 DOM이 만들어지고,<br>브라우저는 이걸 실제 DOM으로 패치하고, 화면을 그린다.</p>
<p>그런데 여기서 문제가 생긴다.</p>
<p><strong>&quot;렌더링 후 어떤 작업을 해야 할 때, 정확히 언제 해야 할까?&quot;</strong></p>
<p>예를 들어:</p>
<ul>
<li>화면이 그려진 후 데이터를 가져오고 싶다 → 늦게 실행되어도 무방</li>
<li>화면이 그려지기 전에 요소 크기를 계산해 위치를 잡아야 한다 → 화면이 바뀌기 전이어야 함</li>
</ul>
<p>이처럼 렌더링 이후의 타이밍은 <strong>두 종류로 나뉘는 작업의 본질</strong>을 반영한다.</p>
<p>그래서 React는 이 후처리 작업을 위해 <strong>두 개의 훅</strong>을 제공한다:</p>
<ol>
<li><code>useEffect</code> → <strong>렌더링 후, 브라우저 페인트가 끝난 뒤 실행 (비동기)</strong></li>
<li><code>useLayoutEffect</code> → <strong>렌더링 후, DOM이 완성되었지만 브라우저가 아직 그리기 전에 실행 (동기)</strong></li>
</ol>
<hr>
<h2 id="구조적으로-언제-실행되나">구조적으로 언제 실행되나?</h2>
<table>
<thead>
<tr>
<th>단계</th>
<th>설명</th>
<th>관련 훅</th>
</tr>
</thead>
<tbody><tr>
<td>1. 컴포넌트 함수 실행</td>
<td>리렌더링 준비</td>
<td>-</td>
</tr>
<tr>
<td>2. 가상 DOM 생성</td>
<td>JSX → ReactElement → 가상 DOM</td>
<td>-</td>
</tr>
<tr>
<td>3. 실제 DOM 업데이트</td>
<td>브라우저에게 실제 DOM 변경 명령</td>
<td>-</td>
</tr>
<tr>
<td>4. <strong>useLayoutEffect 실행</strong></td>
<td>DOM 완성됨, 브라우저가 그리기 직전</td>
<td>✅</td>
</tr>
<tr>
<td>5. 브라우저가 화면 페인팅</td>
<td>사용자에게 화면 보임</td>
<td>-</td>
</tr>
<tr>
<td>6. <strong>useEffect 실행</strong></td>
<td>렌더링이 끝난 후 (비동기)</td>
<td>✅</td>
</tr>
</tbody></table>
<p>즉,  </p>
<ul>
<li><code>useLayoutEffect</code>는 <strong>DOM을 변경할 수 있는 마지막 기회</strong>  </li>
<li><code>useEffect</code>는 <strong>화면이 사용자에게 보인 후 처리하면 되는 일들</strong></li>
</ul>
<hr>
<h2 id="실무-기준-어떤-작업을-어디에">실무 기준: 어떤 작업을 어디에?</h2>
<h3 id="useeffect에-적합한-작업"><code>useEffect</code>에 적합한 작업</h3>
<ul>
<li>비동기 API 호출</li>
<li>이벤트 리스너 등록/해제</li>
<li>로컬 스토리지 작업</li>
<li>외부 라이브러리 초기화 (시각적 영향 없는 경우)</li>
</ul>
<pre><code class="language-ts">useEffect(() =&gt; {
  fetchData().then(setData);
}, []);</code></pre>
<h3 id="uselayouteffect에-적합한-작업"><code>useLayoutEffect</code>에 적합한 작업</h3>
<ul>
<li>DOM의 크기, 위치 측정</li>
<li>스크롤 조작</li>
<li>CSS 클래스 조작 (렌더링 전 상태 반영)</li>
<li>레이아웃 플리커 방지</li>
</ul>
<pre><code class="language-ts">useLayoutEffect(() =&gt; {
    const height = ref.current.offsetHeight;
    setHeight(height); // setState -&gt; 즉시 DOM 반영된다.
},[]);</code></pre>
<h2 id="왜-uselayouteffect는-위험할-수-있나">왜 useLayoutEffect는 위험할 수 있나?</h2>
<p><code>useLayoutEffect</code>는 <strong>동기적으로 실행</strong>된다.
즉, 이 안에서 오래 걸리는 작업을 하면 <strong>렌더링이 차단</strong>되고,
<strong>UI가 멈춘 듯한 느낌</strong>을 줄 수 있다.</p>
<pre><code class="language-ts">useLayoutEffect(() =&gt; {
    whie (true) {} // 브라우저 멈춤
}, []);</code></pre>
<p>그래서 일반적으로 항상 <code>useEffect</code>를 먼저 쓰고,
<strong>&quot;시각적으로 깜빡임이나 DOM 위치 오류&quot;</strong>가 있을 때만
<code>useLayoutEffect</code>로 바꾸는 방식이 추천된다.</p>
<hr>
<h2 id="예시-깜빡임이-생기는-경우">예시: 깜빡임이 생기는 경우</h2>
<pre><code class="language-ts">// 깜빡임 있는 경우
useEffect(() =&gt; {
    const h = ref.current.offsetHeight;
      setTop(h);
},[]);</code></pre>
<ul>
<li>useEffect는 렌더링 후에 실행</li>
<li>이미 한번 그린 화면에서 위치를 바꾸게 됨 -&gt; <strong>깜빡임</strong></li>
</ul>
<pre><code class="language-ts">// 깜빡임 없는 경우
useLayoutEffect(() =&gt; {
    const h = ref.current.offsetHeight;
      setTop(h);
},[]);</code></pre>
<ul>
<li>useLayoutEffect는 <strong>DOM이 완성되었지만 아직 화면에 그려지기 전</strong></li>
<li>위치 보정 후 브라우저가 그리므로 <strong>자연스러움</strong></li>
</ul>
<hr>
<h2 id="결론">결론</h2>
<p>useEffect와 useLayoutEffect의 차이는
단순히 &quot;빠르냐 느리냐&quot;가 아니라,
<strong>렌더링 이후 작업을 &#39;어느 타이밍에서 통제할 것이냐&#39;</strong>의 문제다.</p>
<p>렌더링 사이클을 정확히 이해하고,
우리가 통제하고자 하는 대상(DOM 구조인지, 시각 결과인지)을 기준으로
적절한 훅을 선택하는 것이 진짜 실력이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[이벤트 전파, 구조부터 활용까지]]></title>
            <link>https://velog.io/@cold_mental/%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%A0%84%ED%8C%8C-%EA%B5%AC%EC%A1%B0%EB%B6%80%ED%84%B0-%ED%99%9C%EC%9A%A9%EA%B9%8C%EC%A7%80</link>
            <guid>https://velog.io/@cold_mental/%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%A0%84%ED%8C%8C-%EA%B5%AC%EC%A1%B0%EB%B6%80%ED%84%B0-%ED%99%9C%EC%9A%A9%EA%B9%8C%EC%A7%80</guid>
            <pubDate>Sun, 20 Apr 2025 23:42:30 GMT</pubDate>
            <description><![CDATA[<p>브라우저에서 버튼을 클릭하거나 키보드를 입력하면,<br>그 이벤트는 단지 한 요소에서만 처리되는 것이 아니다.<br>DOM 트리 전체를 타고 흐르며, <strong>캡처링 → 타겟 → 버블링</strong>이라는 3단계 과정을 거친다.</p>
<p>이 구조를 정확히 이해하는 것은<br><strong>이벤트 위임, 동적 요소 처리, stopPropagation의 쓰임새</strong>를 이해하는 데 필수적이다.</p>
<hr>
<h2 id="이벤트-전파란">이벤트 전파란?</h2>
<p>이벤트 전파(Event Propagation)란,<br>브라우저가 특정 요소에서 이벤트가 발생했을 때,<br>그 이벤트가 <strong>DOM 트리를 따라 상위 또는 하위 요소로 이동하면서 전파되는 흐름</strong>을 말한다.</p>
<p>이벤트 전파는 총 <strong>3단계</strong>로 구성된다:</p>
<ol>
<li><strong>캡처링(Capturing)</strong></li>
<li><strong>타겟(Target)</strong></li>
<li><strong>버블링(Bubbling)</strong></li>
</ol>
<hr>
<h2 id="1단계-캡처링-capturing-phase">1단계: 캡처링 (Capturing Phase)</h2>
<p>이벤트가 <strong>DOM 최상위 요소(document)</strong>에서부터 시작해<br><strong>이벤트가 실제 발생한 타겟 요소까지 내려오는 과정</strong>이다.</p>
<pre><code class="language-html">&lt;body&gt;
  &lt;div id=&quot;outer&quot;&gt;
    &lt;button id=&quot;inner&quot;&gt;Click&lt;/button&gt;
  &lt;/div&gt;
&lt;/body&gt;</code></pre>
<p><code>button</code>을 클릭하면, 이벤트는 다음 순서로 &quot;내려간다&quot;</p>
<pre><code class="language-less">document -&gt; html -&gt; body -&gt; div#outer -&gt; button#inner</code></pre>
<blockquote>
<p>이때 각 단계에서 <code>{capture: true}</code> 옵션이 걸려 있는 리스너만 실행된다.</p>
</blockquote>
<pre><code class="language-js">document.body.addEventListener(
    &quot;click&quot;,
  () =&gt; console.log(&quot;캡쳐링: body&quot;),
  {capture: true}
);</code></pre>
<hr>
<h2 id="2단계-타겟-target-phase">2단계: 타겟 (Target Phase)</h2>
<p>이벤트가 실제로 <strong>발생한 요소(이벤트 타겟)</strong>에 도달한 시점이다.
즉, 사용자가 클릭한 그 요소에서 <code>click</code>,<code>input</code>,<code>change</code> 등 이벤트가 실제 트리거된다.
이 시점에서는 <strong>캡처링/버블링 여부와 상관없이 항상 실행</strong>된다.</p>
<pre><code class="language-js">document.getElementById(&quot;inner&quot;).addEventListener(&quot;click&quot;, () =&gt; {
    console.log(&quot;타깃 단계&quot;);
})</code></pre>
<hr>
<h2 id="3단계-버블링-bubbling-phase">3단계: 버블링 (Bubbling Phase)</h2>
<p>타겟 요소에서 이벤트가 끝나면,
이제는 반대로 DOM 트리를 <strong>상위 요소로 올라가면서 전파</strong>된다.</p>
<pre><code class="language-less">button#inner -&gt; div#outer -&gt; body -&gt; html -&gt; document</code></pre>
<pre><code class="language-js">document.body.addEventListener(&quot;click&quot;, () =&gt; {
    console.log(&quot;버블링: body&quot;);
}); // capture: false 가 기본값</code></pre>
<hr>
<h2 id="실습-예시-캡처링-vs-버블링">실습 예시: 캡처링 vs 버블링</h2>
<pre><code class="language-html">&lt;div id=&quot;outer&quot;&gt;
  &lt;button id=&quot;inner&quot;&gt;Click me&lt;/button&gt;
&lt;/div&gt;</code></pre>
<pre><code class="language-js">document.getElementById(&quot;outer&quot;).addEventListener(
  &quot;click&quot;,
  () =&gt; console.log(&quot;캡처링 - outer&quot;),
  { capture: true }
);

document.getElementById(&quot;outer&quot;).addEventListener(&quot;click&quot;, () =&gt;
  console.log(&quot;버블링 - outer&quot;)
);

document.getElementById(&quot;inner&quot;).addEventListener(&quot;click&quot;, () =&gt;
  console.log(&quot;타겟 - inner&quot;)
);</code></pre>
<h3 id="결과-순서">결과 순서</h3>
<pre><code class="language-sql">캡처링 - outer
타겟 - inner
버블링 - outer</code></pre>
<hr>
<h2 id="전파를-막고-싶다면">전파를 막고 싶다면?</h2>
<pre><code class="language-js">element.addEventListener(&quot;click&quot;, (e) =&gt; {
    e.stopPropagation();
})</code></pre>
<ul>
<li><code>e.stopPropagation()</code> -&gt; <strong>이벤트가 이후 단계로 전달되지 않게 함</strong></li>
<li><code>stopImediatePropagation()</code> -&gt; <strong>현재 요소의 다른 리스너도 실행하지 않음</strong></li>
</ul>
<p>사용 예:</p>
<ul>
<li>모달 내부 클릭은 유지하되, 바깥 영역 클릭 시 닫기</li>
<li>중첩된 이벤트 중 원하는 레벨까지만 실행하고 그 이상은 막기</li>
</ul>
<hr>
<h2 id="이벤트-위임event-delegation과의-연결">이벤트 위임(Event Delegation)과의 연결</h2>
<p>이벤트 전파를 이용하면, 하나의 부모 요소에 리스너를 걸고
<strong>자식 요소의 이벤트까지 처리하는 방식, 즉 이벤트 위임</strong>을 구현할 수 있다.</p>
<pre><code class="language-js">document.getElementById(&quot;list&quot;).addEventListener(&quot;click&quot;, (e) =&gt; {
    if (e.target.matches(&quot;li&quot;) {
        console.log(`클릭한 항목: ${e.target.textContent}`);   
    })
})</code></pre>
<p>이벤트 위임은 <strong>동적 요소 처리, 성능 최적화, 메모리 절약</strong> 등에 매우 효과적이다.</p>
<hr>
<h2 id="마무리">마무리</h2>
<ul>
<li>브라우저는 이벤트 발생 시 <strong>DOM 트리를 따라 위에서 아래로(캡처링), 아래에서 위로(버블링)</strong> 흐름을 따라 이벤트를 전달한다.</li>
<li><code>addEventListener</code>의 세 번째 인자 { capture: true }를 통해 캡처링 리스너 등록이 가능하다.</li>
<li><code>stopPropagation()</code>을 통해 중간에서 이벤트 흐름을 끊을 수 있다.</li>
<li>이 구조는 <strong>이벤트 위임(Event Delegation)</strong> 같은 패턴을 가능하게 하고, 효율적인 DOM 제어를 가능하게 한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바스크립트 함수 깊게 파기]]></title>
            <link>https://velog.io/@cold_mental/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%95%A8%EC%88%98-%EA%B9%8A%EA%B2%8C-%ED%8C%8C%EA%B8%B0</link>
            <guid>https://velog.io/@cold_mental/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%ED%95%A8%EC%88%98-%EA%B9%8A%EA%B2%8C-%ED%8C%8C%EA%B8%B0</guid>
            <pubDate>Mon, 14 Apr 2025 23:56:40 GMT</pubDate>
            <description><![CDATA[<p>자바스크립트의 &quot;함수&quot;는 단순히 코드를 묶는 도구가 아니다.<br><strong>변수처럼 다룰 수 있고, 스코프를 기억하고, 컨텍스트에 반응하며 동작하는 강력한 구조체</strong>다.</p>
<p>이 글에서는 자바스크립트 함수의 정체성과 구조를 차근히 풀어보고,<br>이 개념들이 어떻게 <strong>클로저</strong>, <strong>고차 함수</strong>, <strong>this 바인딩</strong> 같은 기능을 만들어내는지를 깊게 살펴보자.</p>
<hr>
<h2 id="1-함수는-일급-객체다">1. 함수는 일급 객체다</h2>
<blockquote>
<p>자바스크립트에서 함수는 <strong>값처럼 다룰 수 있는 존재</strong>, 즉 <strong>일급 객체</strong>다.</p>
</blockquote>
<p><strong>일급 객체(First-Class Citizen)</strong>라는 건  </p>
<ul>
<li>변수에 할당 가능하고,</li>
<li>함수의 인자로 넘길 수 있고,</li>
<li>함수의 반환값으로 사용할 수 있다는 뜻이다.</li>
</ul>
<pre><code class="language-js">const sayHello = function () {
  return &quot;Hello&quot;;
};

const callFn = function (fn) {
  return fn(); // 함수를 인자로 넘김
};

console.log(callFn(sayHello)); // &#39;Hello&#39;</code></pre>
<p>함수가 이렇게 자유롭게 이동하고 조합될 수 있기 때문에,
자바스크립트에서는 <strong>콜백 패턴, 고차 함수, 함수형 프로그래밍</strong>이 가능해진다.</p>
<hr>
<h2 id="2-함수-표현식과-익명-함수">2. 함수 표현식과 익명 함수</h2>
<p>이러한 &quot;함수는 값이다&quot;라는 개념은 <strong>함수 표현식</strong>으로 이어진다.
함수 선언식과 달리 함수 표현식은 값으로 함수(=익명 함수)를 정의하고 변수에 할당한다.</p>
<pre><code class="language-js">const add = function(a,b) {
  return a + b;
}</code></pre>
<p>익명 함수는 자체적으로 이름을 갖지 않기 때문에 디버깅 시 어려울 수 있지만,
<strong>콜백 함수</strong>나 <strong>즉시 실행 함수(IIFE)</strong>처럼 한 번 쓰고 버릴 함수에서 자주 사용된다.</p>
<pre><code class="language-js">setTimeout(function () {
    console.log(&quot;1초 뒤 실행&quot;);
}, 1000);</code></pre>
<hr>
<h2 id="3-호이스팅과-실행-타이밍">3. 호이스팅과 실행 타이밍</h2>
<p>자바스크립트는 코드 실행 전에 변수와 함수 선언을 <strong>메모리에 올리는(호이스팅)</strong> 특성이 있다.
하지만 <strong>함수 선언식과 함수 표현식은 다르게 동작</strong>한다.</p>
<pre><code class="language-js">console.log(foo()); // 가능
function foo() {
    return &quot;Declared&quot;;
}

console.log(bar()); // 에러
const bar = function() {
    return &quot;Expressed&quot;;
}</code></pre>
<ul>
<li>함수 선언식은 <strong>전체 함수가 메모리에 올라가므로</strong>, 선언 전에 호출 가능</li>
<li>함수 표현식은 <strong>변수만 undefined로 호이스팅</strong>, 함수 자체는 런타임에 할당되므로 선언 전 호출 불가</li>
</ul>
<p>이 차이는 <code>var</code>, <code>let</code>, <code>const</code> 의 스코프와 함께 이해하면 더 명확하다.</p>
<hr>
<h2 id="4-클로저">4. 클로저</h2>
<p>함수가 일급 객체이기 때문에, 함수는 언제 어디서든 호출될 수 있다.
그런데 <strong>함수가 자신이 선언될 당시의 스코프를 기억</strong>한다면 어떤 일이 가능할까?
이게 바로 <strong>클로저(Closure)</strong>다.</p>
<pre><code class="language-js">function outer() {
    const outerVar = &quot;I am from outer&quot;;

      return function inner() {
        return outerVar;
    }; 
}

const innerFunc = outer();
console.log(innerFunc()); // &quot;I am from outer&quot;</code></pre>
<p>inner 함수는 이미 <code>outer()</code>가 끝났음에도, <code>outerVar</code>에 접근이 가능하다.
왜냐하면 함수는 자신이 선언될 때의 <strong>렉시컬 환경</strong>을 함께 기억하기 때문이다.</p>
<p>클로저는</p>
<ul>
<li><strong>정보 은닉(private 변수)</strong></li>
<li><strong>함수형 캐싱</strong></li>
<li><strong>부분 적용 함수</strong> 등 다양한 패턴을 구현할 수 있다.</li>
</ul>
<hr>
<h2 id="5고차-함수">5.고차 함수</h2>
<p>클로저와 일급 객체가 결합되면, 우리는 <strong>함수를 조립하는 패턴</strong>을 만들 수 있다.
그게 바로 <strong>고차 함수</strong>다.</p>
<blockquote>
<p>고차 함수란 <strong>함수를 인자로 받거나 반환하는 함수</strong>를 말한다.</p>
</blockquote>
<pre><code class="language-js">function multiplyBy(factor) {
    return function (num) {
        return num * factor;
    };
}

const double = multiplyBy(2);
console.log(double(5)); // 10</code></pre>
<p>여기서 <code>multiplyBy(2)</code>는 <strong>함수를 반환하는 함수</strong>다.
이런 식으로 코드를 <strong>동작의 흐름이 아닌 조합의 단위로 재사용</strong>할 수 있게 된다.</p>
<hr>
<h2 id="6-화살표-함수와-this-바인딩">6. 화살표 함수와 this 바인딩</h2>
<p>자바스크립트의 함수는 일반 함수와 화살표 함수 두 가지 문법을 제공한다.
여기서 중요한 차이는 <strong>this의 바인딩 방식</strong>이다.</p>
<pre><code class="language-js">const obj = {
    value: 42,
      method: function() {
        setTimeout(() =&gt; {
            console.log(this.value) // 42
        }, 1000);
    },
};</code></pre>
<ul>
<li>일반 함수는 호출 위치 기준으로 this를 바인딩</li>
<li>화살표 함수는 <strong>정의된 위치의 this를 기억(렉시컬 this)</strong></li>
</ul>
<p>이는 특히 <strong>비동기 콜백이나 클래스 내부 메서드</strong>에서 유용하다.</p>
<hr>
<h2 id="마무리">마무리</h2>
<p>함수는 단순히 <code>코드를 실행을 위한 구조</code>가 아니다.
자바스크립트에서는 <strong>스코프를 품은 실행 컨텍스트</strong>이자,
<strong>동작을 값으로 전달하는 데이터 구조</strong>이기도 하다.</p>
<p>이 개념들을 이해하고 나면,
왜 자바스크립트가 콜백, 고차 함수, 클로저, 커링, 컴포지션 같은 함수형 개념을 쉽게 받아들일 수 있는지 알게된다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바스크립트 배열, 깊게 파고들기]]></title>
            <link>https://velog.io/@cold_mental/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%B0%B0%EC%97%B4-%EA%B9%8A%EA%B2%8C-%ED%8C%8C%EA%B3%A0%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@cold_mental/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%B0%B0%EC%97%B4-%EA%B9%8A%EA%B2%8C-%ED%8C%8C%EA%B3%A0%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Sun, 13 Apr 2025 23:36:03 GMT</pubDate>
            <description><![CDATA[<p>우리는 흔히 <code>배열</code>을 단순한 리스트로 생각한다.<br>하지만 자바스크립트에서의 배열은 생각보다 <strong>더 독특한 구조와 동작 원리</strong>를 가지고 있다.<br>이번 글에서는 배열을 &#39;메서드 모음집&#39;으로 보지 않고, <strong>자료구조 그 자체로서 깊이 있게 탐구</strong>해보자.</p>
<hr>
<h2 id="배열은-사실-객체object다">배열은 사실 객체(Object)다</h2>
<pre><code class="language-js">typeof [1, 2, 3]; // &quot;object&quot;</code></pre>
<p>자바스크립트의 배열은 사실 객체다.
정확히 말하면, <strong>숫자 형태의 문자열을 키로 가지는 특수한 객체</strong>이다.</p>
<p>즉,</p>
<pre><code class="language-js">const arr = [1,2,3];
arr[0] === arr[&quot;0&quot;]; // true</code></pre>
<p>배열의 인덱스는 내부적으로 문자열 키 <code>&quot;0&quot;</code>, <code>&quot;1&quot;</code>처럼 처리 된다.
이러한 구조는 자바스크립트 배열이 <strong>연속된 메모리 공간에 값을 저장하는 C 스타일 배열</strong>과는 다르다는 것을 보여준다.</p>
<hr>
<h2 id="배열-인덱스는-사실-키">배열 인덱스는 사실 키</h2>
<p>우리가 <code>arr[2]</code>라고 쓰는 것조차 사실은 아래와 같은 의미다</p>
<pre><code class="language-js">arr[&quot;2&quot;]</code></pre>
<p>자바스크립트는 배열이든 객체든 <strong>모든 프로퍼티를 문자열 키로 저장</strong>한다
배열이라고 해서 예외는 아니다. 다만, 배열은 이 키 중 <strong>정수형 문자열만 인덱스로 간주</strong>하고 특별하게 다룬다.</p>
<hr>
<h2 id="희소-배열sparse-array">희소 배열(Sparse Array)</h2>
<p>아래 예제를 보자</p>
<pre><code class="language-js">const a = [];
a[10] = &quot;hello&quot;;
console.log(a); // [&lt;10 empty items&gt;, &#39;hello&#39;]</code></pre>
<p>이 배열은 실제로 <strong>10개의 요소를 갖는 배열이 아니다.</strong>
중간의 값은 존재하지 않고, 메모리에도 존재하지 않는 <strong>&quot;구멍(empty slot)&quot;</strong>으로 남는다.</p>
<p>이런 배열을 <strong>희소 배열(Sparse Array)</strong>이라고 부른다.
희소 배열은 순회 시 조심해야 한다. <code>forEach</code>, <code>map</code> 등은 빈 슬롯을 건너뛰지만, <code>for</code>, <code>for...in</code>은 그렇지 않다.</p>
<pre><code class="language-js">a.forEach((v) =&gt; console.log(v)); // &quot;hello&quot;만 출력</code></pre>
<hr>
<h2 id="length-의-의미">length 의 의미</h2>
<p>자바스크립트 배열의 <code>length</code>는 <strong>요소의 개수</strong>를 말하지 않는다.
정확히는 <strong>&quot;가장 큰 인덱스 + 1&quot;</strong>을 의미한다.</p>
<pre><code class="language-js">const b = [];
b[3] = &quot;a&quot;;
console.log(b.length); // 4</code></pre>
<h4 id="length를-조작할-수도-있다">length를 조작할 수도 있다.</h4>
<pre><code class="language-js">const c = [1,2,3,4,5];
c.length = 2;
console.log(c); // [1,2]</code></pre>
<p>length를 줄이면 요소가 제거되고,
늘리면 요소가 생기지 않고 빈 슬롯만 만들어진다.</p>
<pre><code class="language-js">c.length = 10;
console.log(c); // [1,2, &lt;8 empty items&gt;]</code></pre>
<hr>
<h2 id="배열은-진짜-배열이-아니다">배열은 &quot;진짜 배열&quot;이 아니다</h2>
<p>C나 Java의 배열은 연속된 메모리 공간에 값이 저장된다.
하지만 자바스크립트 배열은 그렇지 않다.</p>
<blockquote>
<p>배열 요소들은 메모리상 연속적으로 존재하지 않아도 된다.</p>
</blockquote>
<p>때문에 <code>arr[1000000] = 1</code> 같은 코드도 즉시 실행된다.
왜냐하면 단순히 <code>&quot;1000000&quot;</code>이라는 키를 객체에 추가하는 일이기 때문이다.</p>
<hr>
<h2 id="배열이-성능에-나쁜-영향을-줄-수도-있다">배열이 성능에 나쁜 영향을 줄 수도 있다</h2>
<p>V8 엔진(Chrome)은 성능을 위해 배열을 3가지 모드로 최적화한다</p>
<ol>
<li><strong>Packed Element</strong> : 연속된 인덱스에 값이 차 있는 배열</li>
<li><strong>Holey Element</strong> : 중간에 빈 슬롯이 있는 배열</li>
<li><strong>Dictionary Element</strong> : 배열이 너무 희소하거나 인덱스가 매우 클 때</li>
</ol>
<pre><code class="language-js">const x = [1,2,3] // Packed
x[100] = &quot;hi&quot; // Holey -&gt; Dictionary로 퇴화 가능</code></pre>
<hr>
<p>자바스크립트의 배열은 단순한 리스트가 아니다.
진짜 배열을 흉내 내는 객체이며, <strong>객체의 유연함과 배열의 편리함을 동시에 제공하는 자료구조</strong>이다.
하지만 내부 구조를 모르고 무작정 사용하면,
엔전 최적화를 깨뜨리고, 퍼포먼스를 망칠 수도 있다.</p>
<p>다음에는 배열과 객체의 메모리 관리 차이, 배열 메서드 실제 구현 방식 등도 정리를 해보자!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React의 Render Phase와 Commit Phase 이해하기]]></title>
            <link>https://velog.io/@cold_mental/React%EC%9D%98-Render-Phase%EC%99%80-Commit-Phase-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@cold_mental/React%EC%9D%98-Render-Phase%EC%99%80-Commit-Phase-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 09 Apr 2025 23:25:10 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/cold_mental/post/bafa343d-9bd9-4cd3-bda0-569d0bd7ac47/image.png" alt=""></p>
<p>React에서의 렌더링 과정은 단순히 <code>setState()</code> → 재렌더링이 일어나는 것처럼 보이지만,<br>내부적으로는 두 가지 단계인 <strong>Render Phase</strong>와 <strong>Commit Phase</strong>를 거친다.</p>
<p>각 단계는 역할이 명확히 다르고, 특히 <strong>React 18의 Concurrent Mode</strong>에서는 이 구조가 더욱 중요해진다.</p>
<hr>
<h2 id="1-render-phase-렌더-단계">1. Render Phase (렌더 단계)</h2>
<blockquote>
<p><strong>UI 변경 사항을 계산하는 단계</strong></p>
</blockquote>
<ul>
<li>변경된 상태(state)나 props를 기반으로, 어떤 UI를 그려야 하는지를 계산</li>
<li>실제 DOM에는 아무런 변경이 발생하지 않음</li>
<li><strong>가상 DOM(Virtual DOM)</strong>을 기반으로 변화 예측</li>
<li>React의 <strong>Fiber Tree 구조</strong>에 따라 새로운 트리를 생성</li>
</ul>
<h3 id="특징">특징</h3>
<ul>
<li>순수 계산 단계이므로 <strong>중단되거나 다시 시작 가능</strong></li>
<li>비동기적으로 실행 가능 (→ <strong>Concurrent Mode</strong>에서 특히 유용)</li>
<li><strong>useEffect, ref, DOM 조작은 이 시점에 발생하지 않음</strong></li>
</ul>
<hr>
<h2 id="2-commit-phase-커밋-단계">2. Commit Phase (커밋 단계)</h2>
<blockquote>
<p><strong>실제 DOM에 변경 사항을 적용하는 단계</strong></p>
</blockquote>
<ul>
<li>Render Phase에서 결정된 변경 사항을 <strong>실제 DOM에 반영</strong></li>
<li>UI가 시각적으로 업데이트됨</li>
<li>DOM 업데이트 이후, <strong><code>useEffect</code>, <code>useLayoutEffect</code> 등의 사이드이펙트 실행</strong></li>
</ul>
<h3 id="특징-1">특징</h3>
<ul>
<li>이 단계는 <strong>중단되지 않고 반드시 완료되어야 함</strong></li>
<li>빠르게 실행되어야 하며, <strong>UI에 직접 영향을 줌</strong></li>
</ul>
<hr>
<h2 id="요약-render-vs-commit">요약: Render vs Commit</h2>
<table>
<thead>
<tr>
<th>구분</th>
<th>Render Phase</th>
<th>Commit Phase</th>
</tr>
</thead>
<tbody><tr>
<td>역할</td>
<td>변경 사항 계산 (Virtual DOM)</td>
<td>실제 DOM 반영</td>
</tr>
<tr>
<td>DOM 변경</td>
<td>❌ 없음</td>
<td>✅ 있음</td>
</tr>
<tr>
<td>중단 가능 여부</td>
<td>✅ 중단 및 재시작 가능</td>
<td>❌ 불가</td>
</tr>
<tr>
<td>React Hook 실행</td>
<td><code>useMemo</code>, <code>useCallback</code>, <code>render</code> 내부만</td>
<td><code>useEffect</code>, <code>useLayoutEffect</code>, <code>ref</code> 처리</td>
</tr>
<tr>
<td>처리 방식</td>
<td>비동기 가능 (Concurrent Mode)</td>
<td>반드시 동기 처리</td>
</tr>
</tbody></table>
<hr>
<h2 id="동기화될-때의-특징">동기화될 때의 특징</h2>
<h3 id="1-단계적-진행-prioritization">1. 단계적 진행 (Prioritization)</h3>
<ul>
<li><code>render phase</code>가 끝났더라도 React는 즉시 커밋하지 않을 수 있음</li>
<li><strong>우선순위가 높은 작업</strong>이 있다면 먼저 처리</li>
<li>이후 <code>commit phase</code>를 안전하게 실행 → 사용자 경험 최적화</li>
</ul>
<h3 id="2-일관성-있는-병목-처리">2. 일관성 있는 병목 처리</h3>
<ul>
<li>변경된 내용을 모두 Fiber Tree에 먼저 준비한 후 <code>commit</code> 하므로,</li>
<li><strong>DOM의 일관성 유지</strong>가 가능하고, 불필요한 중간 렌더링이 없음</li>
</ul>
<hr>
<h2 id="마무리">마무리</h2>
<ul>
<li><strong>Render Phase</strong>는 &quot;계산&quot;, <strong>Commit Phase</strong>는 &quot;실행&quot;이다</li>
<li>React의 렌더링은 실제 DOM에 바로 영향을 주지 않음 → 성능 최적화 가능</li>
<li>React 18 이상에서는 이 구조가 더욱 중요해짐 (비동기 작업 관리 등)</li>
</ul>
<hr>
<p>다음에는 <code>Concurrent Mode</code>, <code>Fiber Tree</code>, <code>Reconciliation</code>, <code>Effect Cleanup</code> 등의 심화 주제도 정리해보자!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TypeScript 함수 타입 정리]]></title>
            <link>https://velog.io/@cold_mental/TypeScript-%ED%95%A8%EC%88%98-%ED%83%80%EC%9E%85-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@cold_mental/TypeScript-%ED%95%A8%EC%88%98-%ED%83%80%EC%9E%85-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Tue, 08 Apr 2025 23:51:12 GMT</pubDate>
            <description><![CDATA[<p>오랜만에 쓰는 TypeScript 블로그다...<br>요즘 정보처리기사 실기 준비도 하고 있고, 블로그도 이전 중이라 정신이 없다 😭<br>그래도 꾸준히 기록은 해보자! 오늘은 TypeScript에서 <strong>함수의 타입</strong>에 대해 정리해보자.</p>
<hr>
<h2 id="함수의-타입-정의-방법">함수의 타입 정의 방법</h2>
<p>자바스크립트와 유사하지만, <strong>타입을 명시하는 것</strong>이 TypeScript의 핵심이다.</p>
<pre><code class="language-ts">function func(a: number, b: number): number {
    return a+b;
}

// 함수의 반환값 타입은 자동으로 추론되기 때문에 생략가능.
function func(a: number, b: number) {
    return a+b;
}
</code></pre>
<hr>
<h2 id="화살표-함수-타입-정의">화살표 함수 타입 정의</h2>
<pre><code class="language-ts">const add = (a: number, b: number): number =&gt; a + b;

// 화살표 함수도 마찬가지로 생략이 가능하다.
const add = (a: number, b: number) =&gt; a + b;</code></pre>
<hr>
<h2 id="매개변수-기본값-설정하기">매개변수 기본값 설정하기</h2>
<p>아래와 같이 함수의 매개변수에 기본값이 설정되어있으면 타입이 자동으로 추론된다. 이러한 경우에는 타입 정의를 생략해도 된다.</p>
<pre><code class="language-ts">function introduce(name = &quot;홍길동&quot;)  {
    console.log(`name : ${name}`);
}

// 기본값과 다른 매개변수의 타입을 정의하면 오류 발생
function introduce(name:number = &quot;홍길동&quot;) {
    ...
}

// 기본값과 다른 타입의 값을 인수로 전달해도 오류 발생
introudce(1);</code></pre>
<hr>
<h2 id="선택적-매개변수-설정하기">선택적 매개변수 설정하기</h2>
<p><code>?</code> 를 붙이면 해당 매개변수는 <strong>선택적으로 생략 가능</strong>하다.</p>
<pre><code class="language-ts">function introduce(name = &quot;홍길동&quot;, age?: number) {
    console.log(`name : ${name}`);
      console.log(`age : ${age}`);
}

introduce(&quot;홍길동&quot;,24);

introduce(&quot;홍길동&quot;);</code></pre>
<p>선택적 매개변수의 타입은 <code>number | undefined</code>로 추론된다.
따라서 연산 등에 사용하려면 <strong>타입 좁히기</strong>가 필요하다:</p>
<pre><code class="language-ts">function introducee(name = &quot;홍길동&quot;, age?: number) {
    console.log(`name: ${name}`);
      if(typeof age === &quot;number&quot;) {
        console.log(`age: ${age + 10}`);
    }
}</code></pre>
<p>📌 선택 매개변수는 <strong>항상 마지막에 위치</strong>해야 한다!</p>
<hr>
<h2 id="나머지-매개변수">나머지 매개변수</h2>
<p>자바스크립트의 rest 파라미터(나머지 매개변수) 관련 내용이다.</p>
<p>다음과 같이 여러개의 숫자를 인수로 받는 함수가 있다고 가정해보자.</p>
<pre><code class="language-ts">function getSum(...rest) {
    let sum = 0;
      rest.forEach((item) =&gt; (sum += item));
      return sum;
}</code></pre>
<p>getSum 함수는 나머지 매개변수 rest로 배열 형태로 number 타입의 인수들을 담은 배열을 전달 받는다. 이때 rest 파라미터의 타입은 아래와 같이 정의하면 된다.</p>
<pre><code class="language-ts">function getSum(...rest: number[]) {
    let sum = 0;
      rest.forEach((item) =&gt; (sum += item));
      return sum;
}</code></pre>
<p>이때 만약 나머지 매개변수의 길이를 고정하고 싶다면 아래와 같이 튜플 타입을 이용해도 된다.</p>
<pre><code class="language-ts">function getSum(...rest: [number, number, number]) {
    let sum = 0;
      rest.forEach((item) =&gt; (sum += item));
      return sum;
}</code></pre>
<hr>
<h2 id="함수-타입-표현식">함수 타입 표현식</h2>
<p>함수 타입을 <strong>타입 별칭으로 선언</strong>해서 재사용 가능!</p>
<pre><code class="language-ts">type ADD = (a: number, b: number) =&gt; number;

const add: ADD = (a,b) =&gt; a + b;</code></pre>
<p>이렇게 함수 타입 표현식을 이용하면 함수 선언 및 구현 코드와 타입 선언을 분리할 수 있어 유용하다.</p>
<p>함수 타입 표현식은 아래와 같이 여러개의 함수가 동일한 타입을 갖는 경우에 요긴하게 사용된다.</p>
<pre><code class="language-ts">type Operation = (a: number, b: number) =&gt; number;

const ADD: Operation = (a, b) =&gt; a + b;
const SUB: Operation = (a, b) =&gt; a - b;
const MULTIPLY: Operation = (a, b) =&gt; a * b;
const DIVIDE: Operation = (a, b) =&gt; a / b;</code></pre>
<hr>
<h2 id="호출-시그니쳐">호출 시그니쳐</h2>
<p>함수 타입 표현식과 비슷하지만 <strong>객체 형태로 표현 가능</strong></p>
<pre><code class="language-ts">type Operation = {
    (a:number, b:number) : number;
}

const ADD: Operation = (a, b) =&gt; a + b;
const SUB: Operation = (a, b) =&gt; a - b;
const MULTIPLY: Operation = (a, b) =&gt; a * b;
const DIVIDE: Operation = (a, b) =&gt; a / b;

// 호출 시그니처 아래에 프로퍼티를 추가 정의하는 것도 가능
// =&gt; 하이브리드 타입

type Operation = {
    (a:number, b:number) : number;
      name: string;
}

const ADD: Operation = (a, b) =&gt; a + b;
</code></pre>
<hr>
<h2 id="정리">정리</h2>
<table>
<thead>
<tr>
<th><strong>기능</strong></th>
<th><strong>문법 예시</strong></th>
<th><strong>설명</strong></th>
</tr>
</thead>
<tbody><tr>
<td>기본 함수</td>
<td><code>function func(a:number): number</code></td>
<td>매개변수 &amp; 반환 타입 지정</td>
</tr>
<tr>
<td>화살표 함수</td>
<td><code>(a:number, b:number) =&gt; number</code></td>
<td>타입 지정 + 생략 가능</td>
</tr>
<tr>
<td>선택 매개변수</td>
<td><code>age?: number</code></td>
<td>undefined 포함된 유니온 타입</td>
</tr>
<tr>
<td>나머지 매개변수</td>
<td><code>...rest: number[]</code></td>
<td>여러 인수 처리</td>
</tr>
<tr>
<td>함수 타입 별칭</td>
<td><code>type Add = (a:number, b:numbeer) =&gt; number</code></td>
<td>재사용성 ↑</td>
</tr>
<tr>
<td>호출 시그니처</td>
<td><code>{(a:number, b:number): number}</code></td>
<td>하이브리드 타입에도 사용 가능</td>
</tr>
</tbody></table>
<p>오늘은 함수 타입에 대해 간단히 알아봤다.
📌 다음엔 함수 타입의 <strong>호환성, 오버로딩, 사용자 정의 타입 가드</strong>도 이어서 정리해보자!</p>
<p>지금은 정보처리기사 실기 준비와 블로그 이전 때문에 바쁘지만...
꾸준히 기록은 계속하자 💪</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[브라우저 렌더링 파이프라인 정리]]></title>
            <link>https://velog.io/@cold_mental/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EB%A0%8C%EB%8D%94%EB%A7%81-%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@cold_mental/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EB%A0%8C%EB%8D%94%EB%A7%81-%ED%8C%8C%EC%9D%B4%ED%94%84%EB%9D%BC%EC%9D%B8-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Tue, 08 Apr 2025 00:15:40 GMT</pubDate>
            <description><![CDATA[<p>브라우저가 HTML, CSS 파일을 받아서 실제로 화면에 보여주기까지의 과정을<br><strong>&quot;렌더링 파이프라인(Rendering Pipeline)&quot;</strong> 이라고 부른다.  </p>
<p>이 과정을 단계별로 이해하면, 성능 최적화, 레이아웃 디버깅, 애니메이션 처리 등에 큰 도움이 된다.</p>
<hr>
<h2 id="전체-흐름">전체 흐름</h2>
<p><img src="https://sdmntprnorthcentralus.oaiusercontent.com/files/00000000-8564-622f-bb51-bcd62149940e/raw?se=2025-04-08T01%3A02%3A45Z&sp=r&sv=2024-08-04&sr=b&scid=a2f3fb4d-ff97-5742-a56c-ad3393633e0d&skoid=d958ec58-d47c-4d2f-a9f2-7f3e03fdcf72&sktid=a48cca56-e6da-484e-a814-9c849652bcb3&skt=2025-04-07T20%3A39%3A03Z&ske=2025-04-08T20%3A39%3A03Z&sks=b&skv=2024-08-04&sig=F1wdHqYCtsgsGUGG3hJ2QSeRQvc8UlY0c0jCFEviMIg%3D" alt="브라우저 렌더링 파이프라인">  </p>
<hr>
<h2 id="렌더링-파이프라인-6단계">렌더링 파이프라인 6단계</h2>
<h3 id="1-dom-생성">1. DOM 생성</h3>
<ul>
<li>HTML 문서를 브라우저가 바이트로 읽어들인 뒤 문자 → HTML 토큰으로 파싱</li>
<li>이를 기반으로 <strong>DOM Tree</strong> 생성</li>
<li>DOM은 문서의 구조를 나타낸 트리 구조</li>
</ul>
<pre><code class="language-html">&lt;body&gt;
  &lt;div&gt;Hello&lt;/div&gt;
&lt;/body&gt;</code></pre>
<pre><code class="language-txt">DOM Tree
└─ body
   └─ div</code></pre>
<h3 id="2-cssom-생성">2. CSSOM 생성</h3>
<ul>
<li>CSS 파일을 생성하여 <strong>CSSOM Tree</strong> 생성</li>
<li>각 노드는 선택자와 그에 대응하는 스타일 정보를 포함</li>
</ul>
<pre><code class="language-css">div {
    color: red;
}</code></pre>
<pre><code class="language-txt">CSSOM Tree
└─ div
   └─ color: red</code></pre>
<h3 id="3-렌더-트리-생성">3. 렌더 트리 생성</h3>
<ul>
<li><strong>DOM + CSSOM을 결합</strong>하여 렌더 트리 생성</li>
<li>display: none 요소는 제외됨</li>
<li>시각적으로 표현되는 요소만 포함됨</li>
</ul>
<pre><code class="language-txt">Render Tree
└─ div (color: red)</code></pre>
<h3 id="4-레이아웃---akareflow">4. 레이아웃 -&gt; aka.Reflow</h3>
<ul>
<li>각 요소의 <strong>위치와 크기</strong> 계산</li>
<li>뷰포트 크기 등을 고려</li>
<li>이 단계에서 <strong>Reflow</strong> 발생 가능 -&gt; 성능 영향 큼</li>
</ul>
<p>예시 : 창 크기 변경, 글자 수 변화, margin/padding 변경</p>
<h3 id="5-페인팅">5. 페인팅</h3>
<ul>
<li>각 요소에 대한 <strong>시각적 표현을 그림</strong></li>
<li>텍스트, 색상, 테두리, 그림자 등</li>
<li>복잡한 페인팅은 성능에 영향 큼</li>
</ul>
<p>예시 : box-shadow, border-radius, gradient 등</p>
<h3 id="6-컴포지팅">6. 컴포지팅</h3>
<ul>
<li>요소들을 레이어 단위로 나누고 <strong>GPU로 합성</strong></li>
<li>transform, opacity 등은 이 단계에서만 처리</li>
<li>레이아웃/페인트를 다시 하지 않기 때문에 애니메이션 ㅅ ㅓㅇ능이 좋음</li>
</ul>
<hr>
<h3 id="성능-최적화를-위해-기억할-점">성능 최적화를 위해 기억할 점</h3>
<ul>
<li><code>display: none</code>, <code>visibility: hidden</code>, <code>transform</code>, <code>opacity</code> 의 차이 이해</li>
<li><strong>Reflow / Repaint / Compositing 비용</strong> 구분</li>
<li>애니메이션은 가능하면 <code>transform</code>, <code>opacity</code>만 사용 -&gt; GPU 처리</li>
</ul>
<hr>
<p>렌더링 파이프라인을 이해하면, <strong>왜 특정 CSS가 느려지는지, 어떤 DOM 조작이 Reflow를 유발하는지</strong> 예측할 수 있다.<br>
프론트엔드 성능 최적화의 핵심 지식! 꼭 익혀두자 💪</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React 성능 최적화?]]></title>
            <link>https://velog.io/@cold_mental/React-%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94</link>
            <guid>https://velog.io/@cold_mental/React-%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94</guid>
            <pubDate>Thu, 03 Apr 2025 23:42:24 GMT</pubDate>
            <description><![CDATA[<p>React는 기본적으로 빠른 UI를 제공하지만, 컴포넌트 수가 많아지고 복잡한 연산이 늘어날수록<br><strong>불필요한 렌더링이나 초기 로딩 시간</strong>이 성능 저하를 일으킬 수 있다.</p>
<p>이럴 때 사용할 수 있는 주요 <strong>성능 최적화 기법</strong>들을 정리해보자!</p>
<h2 id="1-reactmemo--컴포넌트-메모이제이션">1. <code>React.memo</code> – 컴포넌트 메모이제이션</h2>
<blockquote>
<p><strong>props가 변경되지 않았다면 리렌더링을 막는 고차 컴포넌트(HOC)</strong></p>
</blockquote>
<pre><code class="language-jsx">import React from &#39;react&#39;;

const MyComponent = React.memo(function MyComponent({ text }) {
  console.log(&quot;렌더링!&quot;);
  return &lt;div&gt;{text}&lt;/div&gt;;
});</code></pre>
<p><code>memo</code>를 사용하면 <strong>불필요한 렌더링을 막아주기 때문에</strong> 렌더링 비용이 큰 컴포넌트에서 특히 유용하다.</p>
<hr>
<h2 id="2-usecallback--usememo---함수와-값을-메모제이션">2. <code>useCallback</code> &amp; <code>useMemo</code> - 함수와 값을 메모제이션</h2>
<h3 id="2-1-usecallback---함수를-메모제이션">2-1. <code>useCallback</code> - 함수를 메모제이션</h3>
<pre><code class="language-jsx">const handleClick = useCallback() =&gt; {
    console.log(&quot;클릭됨&quot;);
}, []);</code></pre>
<ul>
<li>의존성 배열이 변하지 않는 한, <strong>같은 함수 인스턴스를 재사용</strong></li>
<li>자식 컴포넌트에 함수를 <code>props</code>로 넘길 때 유용</li>
</ul>
<hr>
<h3 id="2-2-usememo---계산된-값을-메모제이션">2-2. <code>useMemo</code> - 계산된 값을 메모제이션</h3>
<pre><code class="language-jsx">const result = useMemo(() =&gt;{
    return heavyCalculation(a,b);
}, [a,b])</code></pre>
<ul>
<li>의존값이 변경되지 않는 한, <strong>연산을 다시 수행하지 않는다.</strong></li>
<li>비용이 큰 계산 작업에서 유용</li>
</ul>
<hr>
<h3 id="예제-usecallback--memo-같이-사용">예제: useCallback + memo 같이 사용</h3>
<pre><code class="language-jsx">const Child = React.memo(({onClick}) =&gt; {
    console.log(&quot;Child 렌더링&quot;);
      return &lt;button onClick={onClick}&gt;클릭&lt;/button&gt;
});

function Parent() {
    const [count, setCount] = useState(0);

      const handleClick = useCallback(() =&gt; {
        console.log(&quot;Clicked&quot;)!
    }, []);

    return (
        &lt;&gt;
            &lt;Child onClick={handleClick} /&gt;
            &lt;button onClick={() =&gt; setCount(count + 1)}&gt;+1&lt;/button&gt;
        &lt;/&gt;
    )
}</code></pre>
<hr>
<h2 id="3-코드-스플리팅-code-splitting">3. 코드 스플리팅 (Code Splitting)</h2>
<blockquote>
<p>애플리케이션을 여러 개의 <strong>청크(조각)</strong>로 분할하여
필요한 시점에만 로딩 -&gt; 초기 로딩 시간 단축!!</p>
</blockquote>
<h3 id="react에서-코드-스플리팅-도구">React에서 코드 스플리팅 도구</h3>
<ul>
<li><code>React.lazy()</code> - 동적으로 컴포넌트를 로드</li>
<li><code>Suspense</code> - 로딩 중 fallback UI 표시</li>
</ul>
<hr>
<h3 id="예제">예제</h3>
<pre><code class="language-jsx">import React, {Suspense, lazy} from &#39;react&#39;;

const LazyComponent = lazy(() =&gt; import(&#39;./MyComponent&#39;));

function App() {
    return (
        &lt;Suspense fallback={&lt;div&gt;로딩 중...&lt;/div&gt;}&gt;
            &lt;LazyComponent/&gt;
        &lt;/Suspense&gt;
    )
}</code></pre>
<p><code>MyComponent</code>는 실제로 화면에서 필요할 때 <strong>비동기적으로 로드된다.</strong>
-&gt; 초기 렌더링에서는 불필요한 코드가 제외되므로 성능 향상!</p>
<hr>
<h2 id="코드-스플리팅이-필요한-상황">코드 스플리팅이 필요한 상황</h2>
<h3 id="1-초기-로딩-시간이-길어지는-경우">1. 초기 로딩 시간이 길어지는 경우</h3>
<ul>
<li>앱이 크고 복잡할수록 초기 JS 번들 크기가 커짐</li>
<li>사용자가 <strong>처음 보는 화면만 빠르게 로드</strong>되도록 분할하면 UX 개선</li>
</ul>
<h3 id="2-라우트별-분할이-필요한-경우">2. 라우트별 분할이 필요한 경우</h3>
<pre><code class="language-jsx">const Home = lazy(() =&gt; import(&#39;./pages/Home&#39;));
const About = lazy(() =&gt; import(&#39;./pages/About&#39;));</code></pre>
<ul>
<li>SPA 구조에서는 각 페이지 컴포넌트를 나눠서 <strong>필요할 때만 로딩</strong></li>
<li>라우터와 함께 코드 스플리팅하면 자연스럽고 효율적</li>
</ul>
<hr>
<h2 id="마무리">마무리</h2>
<p>React의 성능 최적화는 단순히 렌더링을 줄이는 것을 넘어서
<strong>전체 앱의 구조, 사용자 경험까지 고려한 설계</strong>가 필요하다.</p>
<p>다음에는 고급 성능 전략도 이어서 정리해보도록 하자!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Controlled Component vs Uncontrolled Component 차이]]></title>
            <link>https://velog.io/@cold_mental/Controlled-Component-vs-Uncontrolled-Component-%EC%B0%A8%EC%9D%B4</link>
            <guid>https://velog.io/@cold_mental/Controlled-Component-vs-Uncontrolled-Component-%EC%B0%A8%EC%9D%B4</guid>
            <pubDate>Wed, 02 Apr 2025 23:15:43 GMT</pubDate>
            <description><![CDATA[<h1 id="controlled-component-vs-uncontrolled-component">Controlled Component vs Uncontrolled Component</h1>
<p>React에서는 <code>&lt;input&gt;</code>, <code>&lt;textarea&gt;</code> 등의 폼 요소를 사용할 때<br><strong>입력 값을 어떻게 관리하느냐에 따라 두 가지 방식</strong>으로 나뉜다:</p>
<ul>
<li><strong>Controlled Component (제어 컴포넌트)</strong></li>
<li><strong>Uncontrolled Component (비제어 컴포넌트)</strong></li>
</ul>
<hr>
<h2 id="1-controlled-component-제어-컴포넌트">1. Controlled Component (제어 컴포넌트)</h2>
<blockquote>
<p>React의 <code>state</code>를 통해 입력 값을 관리하는 방식</p>
</blockquote>
<ul>
<li>입력 요소의 <code>value</code> 속성을 상태값과 동기화</li>
<li>사용자가 입력할 때마다 <code>onChange</code>로 상태를 업데이트</li>
<li>모든 입력 데이터를 React가 완전히 제어함</li>
</ul>
<h3 id="장점">장점</h3>
<ul>
<li>실시간 입력 값 검증</li>
<li>입력 값 초기화 및 제어 쉬움</li>
<li>여러 입력 간 상호작용 로직 구현 유리</li>
</ul>
<h3 id="코드-예시">코드 예시</h3>
<pre><code class="language-jsx">import { useState } from &#39;react&#39;;

function ControlledInput() {
  const [text, setText] = useState(&#39;&#39;);

  const handleChange = (e) =&gt; setText(e.target.value);

  return (
    &lt;div&gt;
      &lt;input type=&quot;text&quot; value={text} onChange={handleChange} /&gt;
      &lt;p&gt;입력한 값: {text}&lt;/p&gt;
    &lt;/div&gt;
  );
}</code></pre>
<h2 id="2-uncontrolled-component-비제어-컴포넌트">2. Uncontrolled Component (비제어 컴포넌트)</h2>
<blockquote>
<p>입력 값이 DOM 자체에 저장되고, React가 직접 제어하지 않는 방식</p>
</blockquote>
<ul>
<li><code>useRef</code>를 통해 DOM에 접근</li>
<li>React의 상태 업데이트가 필요 없음</li>
<li>값은 필요할 때 직접 DOM에서 읽음</li>
</ul>
<h3 id="장점-1">장점</h3>
<ul>
<li>불필요한 렌더링이 없음</li>
<li>단순한 폼 처리에 적합</li>
<li>성능상 유리 (특히 대규모 폼에서)</li>
</ul>
<h3 id="코드-예시-1">코드 예시</h3>
<pre><code class="language-jsx">import { useRef } from &#39;react&#39;;

function UncontrolledInput() {
  const inputRef = useRef(null);

  const handleSubmit = () =&gt; {
    alert(`입력한 값: ${inputRef.current.value}`);
  };

  return (
    &lt;div&gt;
      &lt;input type=&quot;text&quot; ref={inputRef} /&gt;
      &lt;button onClick={handleSubmit}&gt;제출&lt;/button&gt;
    &lt;/div&gt;
  );
}</code></pre>
<h2 id="상황에-따른-선택-기준">상황에 따른 선택 기준</h2>
<table>
<thead>
<tr>
<th><strong>상황</strong></th>
<th><strong>추천 방식</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>실시간 유효성 검사 필요</strong></td>
<td>Controlled Component</td>
</tr>
<tr>
<td><strong>폼 데이터를 서버에 제출만 하면 됨</strong></td>
<td>Uncontrolled Component</td>
</tr>
<tr>
<td><strong>복잡한 폼 로직 (다중 입력/조건부 렌더링 등)</strong></td>
<td>Controlled Component</td>
</tr>
<tr>
<td><strong>간단한 입력 필드 또는 빠른 성능이 필요한 경우</strong></td>
<td>Uncontrolled Component</td>
</tr>
</tbody></table>
<h2 id="마무리">마무리</h2>
<ul>
<li>복잡한 폼 제어, 조건 렌더링, 유효성 검사 등의 경우 → <strong>Controlled</strong></li>
<li>단순 입력 후 제출만 필요한 경우 → <strong>Uncontrolled</strong></li>
</ul>
<p>React에서 두 방식은 서로 대체 가능한 것이 아니라 <strong>용도에 맞게 선택</strong>하는 것이 중요하다!
두 방식을 모두 익혀두면 상황에 따라 적절한 구현이 가능하다. 💪</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React의 props 와 state 정리]]></title>
            <link>https://velog.io/@cold_mental/React%EC%9D%98-props-%EC%99%80-state-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@cold_mental/React%EC%9D%98-props-%EC%99%80-state-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Tue, 01 Apr 2025 23:40:50 GMT</pubDate>
            <description><![CDATA[<p>React 컴포넌트를 다룰 때 가장 기본이자 중요한 개념인 <strong><code>props</code>와 <code>state</code></strong>에 대해 정리해보자.<br>둘은 비슷해 보이지만, <strong>역할과 사용하는 목적이 완전히 다르다.</strong></p>
<hr>
<h2 id="1-props란">1. <code>props</code>란?</h2>
<blockquote>
<p><strong>부모 컴포넌트가 자식 컴포넌트에게 전달하는 데이터</strong><br>읽기 전용이며, 자식 컴포넌트는 이를 수정할 수 없다.</p>
</blockquote>
<pre><code class="language-jsx">function ChildComponent(props) {
  // props.name = &quot;New Name&quot;; ❌ 오류 가능
  return &lt;div&gt;{props.name}&lt;/div&gt;;
}

function ParentComponent() {
  return &lt;ChildComponent name=&quot;Eunseop&quot; /&gt;;
}</code></pre>
<p><code>props</code>는 외부에서 전달된 값으로, <strong>컴포넌트 내부에서 변경 불가능한 값</strong>이다.
이를 통해 컴포넌트 간의 데이터 흐름을 <strong>예측 가능하고 안정적</strong>으로 유지할 수 있다.</p>
<hr>
<h2 id="2-state란">2. <code>state</code>란?</h2>
<blockquote>
<p><strong>컴포넌트 내부에서 관리되는 동적인 데이터</strong>
컴포넌트가 리렌더링되는 원인이 된다.</p>
</blockquote>
<pre><code class="language-jsx">import { useState } from &#39;react&#39;

function Counter() {
    const [count, setCount] = useState();

      const handleClick = () =&gt; setCount(prev =&gt; prev + 1);

      return (
        &lt;div&gt;
            &lt;p&gt;현재 카운트: {count}&lt;/p&gt;
            &lt;button onClick={handleClick}&gt;+1&lt;/button&gt;
        &lt;/div&gt;
    )

}</code></pre>
<p><code>state</code>는 사용자 입력, 네트워크 응답 등 <strong>변화하는 데이터</strong>를 처리할 때 사용된다.</p>
<hr>
<h2 id="왜-props는-자식-컴포넌트에서-변하지-않을까">왜 props는 자식 컴포넌트에서 변하지 않을까?</h2>
<blockquote>
<p>React 의 <strong>단뱡향 데이터 흐름(One-way data flow)</strong> 원칙 때문!</p>
</blockquote>
<ul>
<li><code>props</code>는 읽기 전용</li>
<li>자식 컴포넌트는 <code>props</code>를 직접 변경할 수 없음</li>
<li>데이터 흐름이 예측 가능하고, 상태 관리가 쉬워짐</li>
<li>컴포넌트 <strong>재사용성과 독립성</strong>이 높아짐</li>
</ul>
<hr>
<h2 id="props를-변경하고-싶다면">props를 변경하고 싶다면?</h2>
<blockquote>
<p>자식 컴포넌트가 <code>props</code>를 <strong>변경할 수는 없지만,</strong>
<strong>부모 컴포넌트로부터 전달받은 함수를 호출해 상태를 변경할 수는 있다.</strong></p>
</blockquote>
<p>이런 방식은 <strong>상태 끌어올리기(Lifiting State Up)</strong>라고 한다.</p>
<h3 id="예제">예제</h3>
<p>-&gt; <strong>부모 컴포넌트</strong></p>
<pre><code class="language-jsx">import { useState } from &quot;react&quot;
import ChildComponent from &quot;./ChildComponent&quot;


function ParentComponent() {
    const [name, setName] = useState(&quot;Hong&quot;);

      const changeName = () =&gt; setName(&quot;Kim&quot;);


      return (
      &lt;div&gt;
        &lt;h2&gt;Parent: {name}&lt;/h2&gt;
        &lt;ChildComponent name={name} onChangeName={changeName} /&gt;
      &lt;/div&gt;
    )
}</code></pre>
<p>-&gt; <strong>자식 컴포넌트</strong></p>
<pre><code class="language-jsx">function ChildComponent({name, onChangeName}) {
    return (
        &lt;div&gt;
            &lt;p&gt;Child: {name}&lt;/p&gt;
            &lt;button onClick={onChangeName}&gt;이름 변경&lt;/button&gt;
        &lt;/div&gt;
    )
}</code></pre>
<h4 id="결과적으로">결과적으로</h4>
<ul>
<li>상태는 부모가 소유</li>
<li>자식은 상태를 <strong>변경할 수 있는 함수만 전달받음</strong></li>
<li>데이터 흐름은 여전히 <strong>단방향 유지</strong></li>
<li>코드의 <strong>예측 가능성, 유지보수성, 재사용성</strong>이 높아짐</li>
</ul>
<hr>
<h3 id="마무리">마무리</h3>
<ul>
<li><code>props</code>: 외부(부모)에서 전달받는 <strong>읽기 전용 데이터</strong></li>
<li><code>state</code>: 컴포넌트 내부에서 정의하고 관리하는 <strong>변경 가능한 데이터</strong></li>
<li><code>props</code>를 바꾸고 싶을 땐 -&gt; <strong>부모 상태 변경 함수 전달 (상태 끌어올리기)</strong></li>
</ul>
<p>이 둘의 역할을 제대로 , 이해하면, <strong>컴포넌트 구조 설계와 상태 관리</strong>가 훨씬 쉬워진다!
다음에는 <code>Context API</code>, <code>useReducer</code> 등 상태 관리 확장 도구도 정리해보도록 하자!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JavaScript 이벤트 루프]]></title>
            <link>https://velog.io/@cold_mental/JavaScript-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A3%A8%ED%94%84</link>
            <guid>https://velog.io/@cold_mental/JavaScript-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A3%A8%ED%94%84</guid>
            <pubDate>Mon, 31 Mar 2025 23:33:57 GMT</pubDate>
            <description><![CDATA[<h1 id="javascript-이벤트-루프-완벽-정리">JavaScript 이벤트 루프 완벽 정리</h1>
<p>자바스크립트의 <strong>이벤트 루프(Event Loop)</strong> 는<br>싱글 스레드 언어인 JavaScript가 <strong>비동기 작업을 효율적으로 처리할 수 있게 하는 핵심 메커니즘</strong>이다.</p>
<hr>
<h2 id="왜-이벤트-루프가-필요할까">왜 이벤트 루프가 필요할까?</h2>
<p>자바스크립트는 기본적으로 <strong>한 번에 하나의 작업만 처리할 수 있는 싱글 스레드</strong> 언어이다.<br>그런데 어떻게 API 요청, setTimeout, UI 반응 등 다양한 작업을 동시에 처리할 수 있을까?</p>
<p>그 답이 바로 <strong>이벤트 루프</strong>에 있다!</p>
<blockquote>
<p><strong>이벤트 루프는</strong><br>&quot;콜 스택이 비어 있을 때, 대기 중인 비동기 콜백들을 하나씩 실행시켜주는 메커니즘&quot;이다.</p>
</blockquote>
<hr>
<h2 id="핵심-구조-콜-스택--이벤트-루프--태스크-큐">핵심 구조: 콜 스택 + 이벤트 루프 + 태스크 큐</h2>
<p><img src="https://velog.velcdn.com/images/cold_mental/post/84bae77a-f406-406a-a8ea-101c1d3bcee8/image.png" alt=""></p>
<p>JavaScript의 실행 구조는 다음처럼 구성되어 있다.</p>
<ul>
<li><strong>Call Stack</strong>: 현재 실행 중인 함수가 쌓이는 곳 (동기 작업 처리)</li>
<li><strong>Web APIs</strong>: 타이머, AJAX, 이벤트 핸들러 등의 백그라운드 처리 영역</li>
<li><strong>Task Queues</strong>:<ul>
<li><strong>Macrotask Queue</strong>: setTimeout, setInterval 등</li>
<li><strong>Microtask Queue</strong>: Promise.then, queueMicrotask 등</li>
</ul>
</li>
<li><strong>Event Loop</strong>: 위 요소들을 관리하며 비동기 콜백을 콜 스택에 넣어줌</li>
</ul>
<hr>
<h2 id="🔁-이벤트-루프-동작-예시">🔁 이벤트 루프 동작 예시</h2>
<pre><code class="language-ts">console.log(&#39;1&#39;);

setTimeout(() =&gt; {
  console.log(&#39;2&#39;);
}, 0);

Promise.resolve().then(() =&gt; {
  console.log(&#39;3&#39;);
});

console.log(&#39;4&#39;);</code></pre>
<pre><code class="language-ts">// 출력 결과

1
4
3
2</code></pre>
<h3 id="설명">설명</h3>
<ol>
<li><code>&#39;1&#39;</code> -&gt; 동기 -&gt; 즉시 실행</li>
<li><code>setTimeout()</code> -&gt; Web API -&gt; MacroTask Queue에 등록</li>
<li><code>Promise.then()</code> -&gt; MicroTask Queue에 등록</li>
<li><code>&#39;4&#39;</code> -&gt; 동기 -&gt; 즉시 실행</li>
<li>Call Stack 비자마자 -&gt; <strong>MicroTask Queue 우선 실행</strong> -&gt; <code>3</code></li>
<li>그 다음 -&gt; <strong>MacroTask Queue 실행</strong> -&gt; <code>2</code></li>
</ol>
<hr>
<h2 id="settimeoutcallback-0-의-오해">setTimeout(callback, 0) 의 오해</h2>
<pre><code class="language-ts">setTimeout(() =&gt; {
    console.log(&quot;Hello&quot;);
}, 0);</code></pre>
<p>위 코드는 &quot;0초 뒤에 실행&quot; 되는 것이 아니라</p>
<ul>
<li><strong>Web API</strong>에 타이머 등록</li>
<li>최소 0ms 후에 <strong>MacroTask Queue에 들어감</strong></li>
<li><strong>콜 스택이 비었을 때 실행됨</strong></li>
</ul>
<p>즉, <strong>동기 코드가 모두 끝난 후</strong>에 실행이 된다!!</p>
<hr>
<h2 id="태스크-큐의-종류">태스크 큐의 종류</h2>
<table>
<thead>
<tr>
<th><strong>종류</strong></th>
<th><strong>설명</strong></th>
<th><strong>대표 API</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>MacroTask Queue</strong></td>
<td>일반적인 비동기 작업이 대기</td>
<td><code>setTimeout</code>, <code>setInterval</code>, <code>DOM 이벤트</code> 등</td>
</tr>
<tr>
<td><strong>MicroTask Queue</strong></td>
<td>우선순위가 높은 비동기</td>
<td><code>Promise.then()</code>, <code>queueMicrotask()</code> 등</td>
</tr>
</tbody></table>
<hr>
<p>이벤트 루프는 자바스크립트 비동기 처리의 핵심이자,
성능 최적화와 버그 해결에 있어서도 매우 중요한 개념이다.</p>
<p>다음에는 <code>requestAnimationFrame</code>, <code>queueMicrotask</code> 같은 심화 개념도 정리해보자! 🚀 </p>
]]></description>
        </item>
    </channel>
</rss>