<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>minji-kim0524.log</title>
        <link>https://velog.io/</link>
        <description>FE Developer .mjs</description>
        <lastBuildDate>Mon, 02 Feb 2026 11:10:44 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>minji-kim0524.log</title>
            <url>https://velog.velcdn.com/images/minji-kim0524/profile/f7d76a18-1526-43e8-82f3-a3a0a57c1a11/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. minji-kim0524.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/minji-kim0524" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[GitHub vs. GitLab]]></title>
            <link>https://velog.io/@minji-kim0524/GitHub-vs.-GitLab</link>
            <guid>https://velog.io/@minji-kim0524/GitHub-vs.-GitLab</guid>
            <pubDate>Mon, 02 Feb 2026 11:10:44 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/minji-kim0524/post/4eb37db4-cc95-4a98-8bc9-e9dbc553b3bb/image.png" alt=""></p>
<h1 id="소스코드-저장소-호스팅의-양대산맥"><em>소스코드 저장소 호스팅의 양대산맥!</em></h1>
<hr>
<p><code>GitHub</code> 와 <code>GitLab</code> 의 핵심적인 차이는 <strong>DevOps 워크플로우 통합 수준과 사용하는 목적</strong> 이다.</p>
<ul>
<li><code>GitHub</code>: 오픈소스 공유, 커뮤니티 협업(SaaS 위주) 강점, 속도우선 <strong>&lt;&lt;프로젝트 중심&gt;&gt;</strong></li>
<li><code>GitLab</code>: 자체 호스팅가능, 내장된 CI/CD로 올인원 DevOps 플랫폼 지향, 안정성우선 <strong>&lt;&lt;DevOps 중심&gt;&gt;</strong></li>
</ul>
<p>대표적인 차이점 몇가지를 살펴보자면,</p>
<h4 id="1-브랜치">1. 브랜치</h4>
<ul>
<li><code>GitHub</code>: 브랜치를 생성하고 병합하는 것이 용이함 → 신속한 배포가능, 문제 발생시 이전 버전으로의 신속한 복원가능, PR(Pull Request) 의 코드 검토방법</li>
<li><code>GitLab</code>: master 브랜치 외에 배포 환경에 따라 브랜치를 분리하여 관리 → 병합 요청시 MR(Merge Request) 방법으로 까다로운 코드검토 필요</li>
</ul>
<table>
<thead>
<tr>
<th>특징</th>
<th>GitLab</th>
<th>GitHub</th>
</tr>
</thead>
<tbody><tr>
<td>브랜치 전략</td>
<td>환경별 브랜치</td>
<td>기능별 브랜치</td>
</tr>
<tr>
<td>CI/CD</td>
<td>내장형 (강력한 연동)</td>
<td>외부 도구(Actions) 연동</td>
</tr>
<tr>
<td>배포방식</td>
<td>배포중심으로 체계적, 단계적인 배포</td>
<td>개발중심으로 빠른 반영이 우선</td>
</tr>
<tr>
<td>활용예시</td>
<td>기업형, 대규모, 배포단계가 많은 경우</td>
<td>소규모, 신속한 반복, 오픈소스</td>
</tr>
</tbody></table>
<h4 id="2-소프트웨어-서비스">2. 소프트웨어 서비스</h4>
<ul>
<li><code>GitHub</code>: <code>GitLab</code>보다 적은 수의 자체적인 소프트웨어 서비스를 제공하는 대신 외부 프로그램 및 서비스와 통합하는 쉬운 방법 제공 (예: 깃허브 마켓플레이스)</li>
<li><code>GitLab</code>: 완전한 소프트웨어 개발 솔루션 제공 → 올인원 DevOps 플랫폼라 불리움, 지라(Jira)/팀즈/슬랙/G메일같은 플랫폼과의 통합 제공</li>
</ul>
<p>각각에 대해서 자세하게 살펴보자</p>
<h2 id="github">GitHub</h2>
<p>너무나 친숙한 github는 버전관리도구인 <code>git</code> 을 기반으로 소스코드를 클라우드에 저장하고 공유 및 협업하는 <strong>웹 호스팅 플랫폼</strong> 이다. </p>
<p>관련하여 자세하게 다루려면 적은 양이 아니므로 이번 포스팅에서는 간단하게만 정리하고 넘어가고, 나중에 하나씩 살펴보자.</p>
<p>git과 github는 간단하게 아래와 같이 생각하고 넘어가면 되겠다.</p>
<ul>
<li><code>Git</code>: 개인PC에서 동작하는 버전관리도구</li>
<li><code>GitHub</code>: Git으로 관리하는 프로젝트를 올리는 웹사이트</li>
</ul>
<h2 id="gitlab">GitLab</h2>
<p>gitlab에도 종류가 있는데, <code>GitLab.com</code> 과 <code>GitLab</code> 이 있다. (<del><em>처음엔 같은 말인줄 알았다....</em></del>)
두 방식 모두 강력한 내장 CI/CD, 이슈 트래커, 보안 테스트 등의 올인원 데브옵스(DevOps) 플랫폼 기능을 제공한다.</p>
<p>그렇다면 두 가지는 어떤 차이가 있을까?</p>
<p><strong>1. GitLab.com</strong></p>
<ul>
<li>SaaS(클라우드) 서비스 → 회원가입 즉시 사용 가능</li>
<li>장점<ul>
<li>회원가입 후 사용하므로 서버 관리 부담이 없음</li>
<li>즉시 사용 가능함</li>
<li>자동으로 업데이트가 이루어짐</li>
</ul>
</li>
<li>단점<ul>
<li>데이터가 외부 클라우드에 저장되어 보안이 약함</li>
<li>커스터마이징에 제한이 있음</li>
<li>대규모로 사용시 비용측면에서 부담이 될 수 있음</li>
</ul>
</li>
</ul>
<p><strong>2. GitLab</strong></p>
<ul>
<li>자체 설치형(Self-managed) → 자체 서버 직접 설치 필요</li>
<li>장점<ul>
<li>자체 서버를 사용하므로 보안이 강력함</li>
<li>데이터 통제가 가능함</li>
<li>사내 네트워크와 연동 가능</li>
</ul>
</li>
<li>단점<ul>
<li>서버를 관리하는 인력이 필요함</li>
<li>직접 설치 및 유지보수가 필요함</li>
</ul>
</li>
</ul>
<hr>
<h4 id="🌟gitlab의-모습은-어떨지-궁금해서-학습목적으로-가입을-진행해보았다">🌟GitLab의 모습은 어떨지 궁금해서 학습목적으로 가입을 진행해보았다.</h4>
<p>로그인 후 &#39;Create new project&#39; 를 하면 아래와 같은 화면을 볼 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/minji-kim0524/post/de09209b-82b2-4fd0-9f8d-675f3f7c520f/image.png" alt=""></p>
<p>여기서 <code>import project</code> 클릭 후 GitHub를 연동하면 개인 레포지토리를 GitLab에 import할 수도 있다.</p>
<p>테스트로 레포지토리를 import 해보니 아래와 이 상태가 변경되면서 import가 완료되었다.
<img src="https://velog.velcdn.com/images/minji-kim0524/post/43f4a1ff-6f97-418e-958b-508b87e4d1ba/image.png" alt=""></p>
<p>import가 완료된 레포지토리를 클릭하면 GitHub에서 보던 화면과 크게 다르지않은 화면을 볼 수 있다. (<del><em>추후 작업 후 pull/push 테스트도 진행하여 내용을 추가해보겠다.</em></del>)
<img src="https://velog.velcdn.com/images/minji-kim0524/post/7d2ec537-c76d-42cb-94b5-64406c56bf2f/image.png" alt=""></p>
<hr>
<h4 id="✋🏻잠깐-gitlab-이-cicd가-내장되어있다고하는데-github에는-github-actions-가-있잖아">✋🏻잠깐! GitLab 이 CI/CD가 내장되어있다고하는데 Github에는 Github Actions 가 있잖아?</h4>
<p>맞는말이지만 GitLab은 CI/CD기능이 &quot;내장&quot; 되어있어 설정이 간편하지만, Github는 Github Actions라는 별도 도구를 연동해야한다.</p>
<p>두 가지의 대표적인 특징과 어떤 상황에서 어떤 걸 선택하여 사용하면 좋을까?</p>
<ul>
<li><code>GitLab</code>: 단일 애플리케이션 형태의 내장된 강력한 파이프라인 제공 → 전체 DevOps 프로세스를 하나의 플랫폼에서 관리하거나, 자체 서버에 CI/CD 환경을 구축해야할때 사용</li>
<li><code>Github Actions</code>: 마켓플레이스 중심의 유연하고 가벼운 자동화, <code>workflows</code> 라는 설정파일을 기반으로 동작 → 이미 GitHub에서 코드를 관리 중이며, 빠르고 간편하게 빌드 및 배포 자동화 설정을 하고싶을때 사용</li>
</ul>
<p>그래도 두 가지 모두 <code>YAML</code> 기반의 DevOps 도구라는 점은 동일하다.</p>
<p><code>Github Actions</code> 을 살짝 사용해보니 <code>feat</code>브랜치에서 <code>main</code>으로 <code>push</code> 를 하면 (<em>workflows 설정파일 내용을 기반으로</em>) 자동으로 빌드를 진행하여 에러를 체크해주는 것이 편리하게 느껴졌다.</p>
<p><img src="https://velog.velcdn.com/images/minji-kim0524/post/861e64ad-f359-40d1-a521-57e3092933ad/image.png" alt=""></p>
<p>확실히 빌드 및 배포를 자동화 해놓으면 정말 &quot;개발&quot; 에만 집중할 수 있으니 코드의 품질을 높이는데도 도움이 될거라 생각된다.</p>
<hr>
<blockquote>
<p><strong>정리해보자면, <code>GitLab</code>은 대규모 기업형 프로젝트에 유리하고 <code>GitHub</code>은 개인 및 소규모 프로젝트에 유리하다.</strong></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[모던 자바스크립트 Deep Dive] 38장. 브라우저의 렌더링 과정]]></title>
            <link>https://velog.io/@minji-kim0524/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Deep-Dive-38%EC%9E%A5.-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%EC%9D%98-%EB%A0%8C%EB%8D%94%EB%A7%81-%EA%B3%BC%EC%A0%95</link>
            <guid>https://velog.io/@minji-kim0524/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Deep-Dive-38%EC%9E%A5.-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%EC%9D%98-%EB%A0%8C%EB%8D%94%EB%A7%81-%EA%B3%BC%EC%A0%95</guid>
            <pubDate>Thu, 23 Oct 2025 16:44:49 GMT</pubDate>
            <description><![CDATA[<h1 id="우리가-보는-화면-속에서는-어떤-일들이-일어나고-있을까">*&quot;우리가 보는 화면 속에서는 어떤 일들이 일어나고 있을까?&quot;*</h1>
<hr>
<p>구글의 V8 엔진으로 빌드된 자바스크립트 런타임 환경인 <strong>Node.js</strong>가 등장함으로써 웹의 영역에서 벗어나 <code>서버 사이드</code> 애플리케이션 영역까지 커버할 수 있는 <strong>범용 개발 언어</strong>가 되었다.</p>
<p>그래도 역시나 주력 분야는 웹 브라우저 환경에서 동작하는 <code>웹 페이지/애플리케이션</code> 의 클라이언트 사이드이다. (<del><em>major 는 무시못하져</em></del>)</p>
<p>대부분의 프로그래밍 언어는 <em>운영체제(OS)</em> 또는 <em>가상머신(VM)</em> 과 손잡고 실행되지만, <strong>웹 애플리케이션</strong> 의 클라이언트 사이드인 자바스크립트는 <code>HTML</code>, <code>CSS</code> 의 손을 잡고 실행된다.</p>
<p>자, 그럼 프론트엔드라면 모를 수가 없는 <code>HTML</code>, <code>CSS</code>, <code>JavaScript</code> 로 작성된 문서를 어떻게 파싱하여 렌더링하는지 살펴보자!</p>
<blockquote>
<p><strong>✋🏻 잠깐!</strong> 파싱과 렌더링이 뭐야?</p>
</blockquote>
<ul>
<li>파싱(<code>parsing</code>) : 구문 분석이라는 의미로 작성된 텍스트 문서를 프로그래밍 언어가 읽고 실행할 수 있도록 문자열을 <strong>토큰</strong>으로 분해한 후, 토큰에 문법적 의미와 구조를 반영하여 트리 구조의 자료구조를 생성하는 과정을 의미한다.</li>
<li>렌더링(<code>rendering</code>) : HTML, CSS, JavaScript 로 작성된 문서를 <code>파싱</code>하여 브라우저에 <code>시각적</code>으로 출력하는 것을 의미한다.</li>
</ul>
<p>그렇다면 브라우저가 렌더링을 수행하는 과정은 어떻게 될까?
1️⃣ HTML, CSS, JavaScript, 이미지, 폰트 등 렌더링에 필요한 리소스를 <code>요청</code>하고 서버로부터 <code>응답</code> 받는다.
2️⃣ 렌더링 엔진은 응답된 HTML, CSS 를 파싱하여 <code>DOM</code>과 <code>CSSOM</code> 을 생성하고 결합하여 <strong>렌더 트리</strong>를 생성한다.
3️⃣ 자바스크립트 엔진은 응답된 자바스크립트를 <code>파싱</code> 하여 AST를 생성하고 바이트코드로 변환하여 실행한다.
이 때, 자바스크립트는 DOM API를 통해 DOM 이나 CSSOM 변경을 할 수 있으며, 변경된 DOM과 CSSOM은 다시 렌더 트리로 결합된다. (<em>변경상태 업데이트 완료!</em>)
4️⃣ 최종적으로 업데이트 된 렌더 트리를 기반으로 HTML 요소의 레이아웃을 계산하고 우리가 보는 브라우저 화면에 요소를 그린다.</p>
<p>이제 단계별로 하나씩 살펴보자.</p>
<hr>
<h2 id="요청과-응답">요청과 응답</h2>
<p>브라우저의 핵심 기능이라고하면 <strong>필요한 리소스를 서버에 요청하고, 응답받아 브라우저에 시각적으로 렌더링</strong> 하는 것이다.</p>
<p><code>https://www.naver.com/</code></p>
<p>우리에게 너무나 익숙한 이 것, URL 이다.
이 곳에서 검색어를 입력하고 엔터키를 누르면 검색결과를 볼 수 있다.
이것이 우리가 사용하는 과정을 겉으로만 봤을 때이다.
우리가 엔터키를 누르고 검색결과를 보는 간단해보이는 과정이 브라우저에서의 처리과정으로 보면 어떨까?</p>
<p>서버에 리소스를 요청하기위해 브라우저는 주소창을 제공한다.
제공된 주소창에 우리는 요청할 내용을 URL에 담아서 입력하고 엔터키를 누르면 결과가 보인다.</p>
<p><code>https://search.naver.com/search.naver?where=nexearch&amp;sm=top_hty&amp;fbm=0&amp;ie=utf8&amp;query=%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8&amp;ackey=9m6l1yfc</code></p>
<p>위 URL 은 네이버에서 &#39;자바스크립트&#39; 로 검색했을 때 주소창의 모습이다.</p>
<p>검색어를 입력하고 엔터키를 누르면 <strong>루트요청(<code>/</code>)</strong> 과 <strong>호스트(domain, 도메인)</strong> 으로 구성된 요청이 서버로 전송된다.</p>
<p>루트요청에는 리소스를 요청하는 내용이 명확하게 나타나있지 않지만, 암묵적으로 서버는 루트요청에 대해서는 index.html 을 응답하도록 설정되어 있다. 
(<em>이것이 우리가 루트디렉토리에 index.html을 구성하는 이유인 것이다.</em>)</p>
<blockquote>
<p>💡 요청과 응답은 <strong>개발자도구-Network</strong>에서 확인할 수 있다.</p>
</blockquote>
<hr>
<h2 id="http-11과-http-20">HTTP 1.1과 HTTP 2.0</h2>
<h3 id="http란">HTTP란?</h3>
<p><strong>| 웹에서 브라우저와 서버가 통신하기 위한 규약(프로토콜)이다.</strong></p>
<p>그렇다면 HTTP 1.1 과 HTTP 2.0의 차이점은 뭘까?</p>
<p><strong>HTTP 1.1</strong>은 기본적으로 커넥션당 <code>하나의 요청과 응답</code>만 처리한다.
즉, 여러 개의 요청이 들어와도 한 번에 전송할 수 없으며, 응답도 마찬가지이다.
(<em>1.1이라는 숫자에 연관지어서 생각해보면, 1:1 처리방식이라고 생각하면 좋겠다. &quot;하나의 요청엔 하나의 응답만이!&quot;</em>)</p>
<p>그렇기때문에 응답시간은 요청할 리소스의 개수에 비례한다는 단점이 있다.</p>
<p>이러한 단점을 보완하여 나온 것이 <strong>HTTP 2.0</strong>이다. (<em>두-둥🎉</em>)</p>
<p><strong>HTTP 2.0</strong>은 커넥션당 여러 개의 요청와 응답이 가능하므로 HTTP 1.0보다 페이지 로드속도가 약 50%정도 빠르다고 한다.</p>
<hr>
<h2 id="html-파싱과-dom-생성">HTML 파싱과 DOM 생성</h2>
<p>순수한 텍스트로 이루어진 HTML문서를 브라우저에 시각적으로 표현하려면 브라우저가 이해할 수 있는 자료구조(DOM)로 변환하여 메모리에 저장해야 한다.
그 과정은 아래와 같다.
<img src="https://velog.velcdn.com/images/minji-kim0524/post/1897d51a-80ba-4582-b58e-8076d6ba22b9/image.png" alt=""></p>
<p>여기서 DOM은 HTML문서를 파싱한 결과물이라고 볼 수 있다.</p>
<hr>
<h2 id="css-파싱과-cssom-생성">CSS 파싱과 CSSOM 생성</h2>
<p>렌더링 엔진은 HTML 을 한 줄씩 순차적으로 파싱하여 DOM을 생성한다.
그 과정중에서 CSS 로드를 담당하는 <code>link</code>, <code>style</code> 태그를 만나면 DOM 생성을 잠시 멈추고, CSS 로드 담당 태그내용을 먼저 처리한다.</p>
<p>파싱하여 CSSOM을 생성하는 과정은 DOM을 생성하는 과정과 동일하며, CSS파싱을 완료하고나면 일시중지해놨던 DOM 생성을 다시 시작한다.</p>
<hr>
<h2 id="렌더-트리-생성">렌더 트리 생성</h2>
<p>렌더링 엔진은 서버로부터 응답된 HTML과 CSS 파싱과정을 거쳐 DOM와 CSSOM을 생성하고, DOM과 CSSOM은 최종 렌더링을 위해 <code>렌더 트리(render tree)</code>로 결합된다.</p>
<p><strong>렌더 트리</strong>는 렌더링을 위한 트리 구조의 자료구조이기 때문에 브라우저 화면에 렌더링되지 않는 노드(<em>meta, script태그 등</em>)와 CSS에 의해 표시되지 않는(<em>display:none</em>) 노드들은 포함되지 않는다.</p>
<blockquote>
<p><strong>렌더 트리는 브라우저에 렌더링되는 노드만으로 구성되어 있다.</strong></p>
</blockquote>
<p>이렇게 완성된 렌더 트리는 각 HTML 요소의 레이아웃 계산에 사용되며, 브라우저 화면을 렌더링하는 페인팅처리에 입력값으로 사용된다.</p>
<p>브라우저 렌더링 과정은 구성값이 변경되면 반복적으로 실행된다.
이렇게 페인팅을 다시 실행하게하는 <code>리렌더링</code>은 비용이 많이들고, 성능에 악영향을 줄 수 있기 때문에 리렌더링이 자주 발생하지 않도록 주의해야 한다.🚨</p>
<hr>
<h2 id="자바스크립트-파싱과-실행">자바스크립트 파싱과 실행</h2>
<p>자바스크립트 코드에서 <code>DOM API</code> 를 사용하면 이미 생성된 <strong>DOM의 동적 조작</strong>이 가능하다.</p>
<p>CSS 파싱과정에서 살펴본 것처럼 HTML을 한 줄씩 파싱하면서 script 태그를 만나게되면 DOM 생성을 일시중지하고 script 코드를 먼저 실행한다.</p>
<p>CSS때와의 차이점이라고하면 자바스크립트 파싱과 실행은 브라우저의 렌더링 엔진이 아닌 <code>자바스크립트 엔진</code>이 처리한다.
자바스크립트 엔진에는 다양한 종류가 있으며, 모든 자바스크립트 엔진은 ECMAScript 사양을 준수하게 되어있다.</p>
<p><code>렌더링 엔진</code>이 HTML과 CSS를 파싱하여 DOM과 CSSOM을 생성한 것처럼,
<code>자바스크립트 엔진</code>은 자바스크립트를 파싱하여 <strong>AST(추상적 구문 트리)</strong> 를 생성한다. 그 과정을 그림으로 살펴보면 다음과 같다.</p>
<p><img src="https://velog.velcdn.com/images/minji-kim0524/post/b9a8b46f-56c3-44e4-943b-27a0ea76baa6/image.jpg" alt=""></p>
<p>여기서 <code>토크나이징</code>과 <code>파싱</code>, <code>바이트코드 생성과 실행</code> 단계에 대해서 간단하게 살펴보자.</p>
<ul>
<li><code>토크나이징</code> : 단순 문자열인 자바스크립트 소스코드를 문법적 의미를 갖는 코드의 최소 단위인 <strong>토큰</strong>들로 분해하는 과정이다.</li>
<li><code>파싱</code> : 이 장의 초반에 살펴본 개념이지만 한번 더 살펴보자면, 토큰들의 집합을 구문분석 하여 AST 를 생성한다.
(<em>AST 사용시, TypeScript, Babel, Prettier 와같은 트랜스파일러 구현이 가능하다.</em>)</li>
<li><code>바이트코드 생성과 실행</code> : 파싱의 결과물인 AST는 인터프리터가 실행할 수 있는 중간 단계인 <code>바이트코드</code>로 변환되고, 인터프리터에 의해 실행된다.</li>
</ul>
<hr>
<h2 id="리플로우와-리페인트">리플로우와 리페인트</h2>
<p>앞서 살펴보았던 DOM과 CSSOM이 변경되면, 다시 렌더 트리로 결합되고 그렇게 변경된 렌더 트리를 기반으로 페인트 과정을 지나 브라우저 화면에 렌더링되는 과정을 <code>리플로우</code>, <code>리페인트</code> 이라고 한다.</p>
<p><strong>리플로우</strong></p>
<ul>
<li>레이아웃 재계산 하는 것을 의미</li>
<li>노드 추가/삭제, 요소의 크기/위치 변경, 윈도우 리사이징 등 <code>레이아웃</code>에 영향을 주는 변경이 발생한 경우가 해당됨</li>
</ul>
<p><strong>리페인트</strong></p>
<ul>
<li>재결합된 렌더 트리를 기반으로 다시 페인트를 하는 것을 의미</li>
</ul>
<p>변경이 일어났을 때 <code>리플로우</code>, <code>리페인트</code>가 반드시 다 실행되는 것은 아니다.
레이아웃에 영향이 없는 변경은 <code>리페인트</code>만 실행이 되는 것이다.</p>
<hr>
<h2 id="js-파싱에-의한-html-파싱-중단">JS 파싱에 의한 HTML 파싱 중단</h2>
<p>렌더링 엔진과 자바스크립트 엔진의 파싱 과정을 살펴봤을 때, 병렬적으로 파싱을 실행한다기보단 <strong>직렬적</strong> 으로 파싱을 진행한 것이 보였을 것이다.</p>
<p>이처럼 브라우저는 <strong>동기적</strong> 으로 (<em>위에서 아래, 순차적</em>) HTML, CSS, 자바스크립트를 파싱하고 실행한다. 이 말은 script 태그 위치에 따라 HTML 파싱이 블로킹되어 DOM 생성이 지연될 수 있다는 말이다.</p>
<blockquote>
<p><strong>script 태그의 위치는 그만큼 중요하다!</strong></p>
</blockquote>
<p>❌ script 태그의 위치가 올바르지 못할 경우, DOM이 완성되지 않은 상태에서 자바스크립트가 DOM 조작을 하게되어 에러가 발생할 수 있다.</p>
<p>✅ 반면 script 태그를 올바르게 위치시키면, 자바스크립트 로딩/파싱/실행으로 인해 HTML 요소들의 렌더링에 지장받는 일이 발생하지 않아 페이지 로딩시간이 단축되는 효과가 있다.</p>
<hr>
<h2 id="script-태그의-asyncdefer-어트리뷰트">script 태그의 async/defer 어트리뷰트</h2>
<p>자바스크립트 파싱에 의해 DOM 생성이 중단되는 문제를 근본적으로 해결하기 위해서는 어떻게 하는 것이 좋을까?</p>
<p><strong>⇒ script 태그에 <code>async</code>, <code>defer</code> 어트리뷰트를 추가하는 것이다.</strong></p>
<p><code>async</code> 와 <code>defer</code> 어트리뷰트는 <code>src</code> 어트리뷰트를 통해 외부 자바스크립트 파일을 로드하는 경우에만 사용할 수 있다. (<em>src 없이는 못살아~</em>)</p>
<pre><code class="language-html">&lt;script async src=&quot;example.js&quot;&gt;&lt;/script&gt;
&lt;script defer src=&quot;example.js&quot;&gt;&lt;/script&gt;</code></pre>
<blockquote>
<p><strong><code>async</code> 와 <code>defer</code> 어트리뷰트를 사용하면 HTML 파싱과 외부 자바스크립트 파일의 로드가 비동기적으로 진행된다.</strong></p>
</blockquote>
<p><strong>❗️하지만</strong> 자바스크립트의 실행 시점에는 두 가지가 차이가 있다.</p>
<p><strong>async</strong></p>
<ul>
<li>자바스크립트의 파싱과 실행은 자바스크립트 파일 로드가 <code>완료</code>된 직후 진행되며, 이때 HTML 파싱은 중단된다.</li>
<li>여러개의 async 어트리뷰트로 지정한 script 태그가 있는 경우, 작성된 순서가 아닌 로드가 완료된 순서대로 실행된다. → <strong><em>순서보장이 되지 않음❌</em></strong></li>
</ul>
<p><strong>defer</strong></p>
<ul>
<li>자바스크립트의 파싱과 실행은 HTML 파싱이 완료된 직후(<em>DOM 생성이 완료된 직후</em>) 진행된다. → <strong><em>DOM 생성이 완료된 이후 실행되어야 하는 코드에 유리함</em></strong></li>
<li>2025년 현재 기준으로 async와 마찬가지로 IE10 이상에서 지원되지만, <code>defer</code> 의 경우에는 IE6~9에서도 지원은 되나 정상작동하지 않을 수 있다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[모던 자바스크립트 Deep Dive] 37장. Set과 Map]]></title>
            <link>https://velog.io/@minji-kim0524/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Deep-Dive-37%EC%9E%A5.-Set%EA%B3%BC-Map</link>
            <guid>https://velog.io/@minji-kim0524/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Deep-Dive-37%EC%9E%A5.-Set%EA%B3%BC-Map</guid>
            <pubDate>Sat, 18 Oct 2025 19:37:02 GMT</pubDate>
            <description><![CDATA[<h1 id="닮은듯-안닮은듯-👯">*&quot;닮은듯 안닮은듯&quot;* 👯</h1>
<hr>
<h2 id="set이란">Set이란?</h2>
<p><strong>| set객체는 중복되지 않는 유일한 값들의 집합이다.</strong></p>
<p>이러한 <code>Set 객체</code>는 배열과 유사하지만 몇 가지 차이점이 있다.
(<em>배열은 이 반대의 특징을 가진다는 의미겠지?</em>)</p>
<ul>
<li>배열과 다르게 동일한 값을 중복 포함할 수 없다. ❌</li>
<li>배열과 다르게 요소 순서에 의미가 없다. ❌</li>
<li>배열과 다르게 인덱스로 요소에 접근할 수 없다. ❌</li>
</ul>
<p>이렇게 수학적 집합의 특성과 일치하는 덕분에 Set으로 <em>교집합, 합집합, 차집합, 여집합 등</em>을 구현할 수 있다.</p>
<p>그렇다면 Set객체는 어떻게 생성할까?</p>
<h3 id="set-객체의-생성">Set 객체의 생성</h3>
<p><strong>| Set 생성자 함수로 객체를 생성한다.</strong></p>
<p>단, Set 생성자 함수에 인수를 전달하지 않을 경우 <code>빈 객체</code> 가 생성된다.</p>
<pre><code class="language-js">// Set 생성
const set = new Set() // 인수 전달안함
console.log(set) // Set(0) {} 빈 객체 생성</code></pre>
<hr>
<p>앞서 다뤘던 <code>이터러블</code> 을 인수로 전달받아서 객체를 생성하는데, 중복된 값은 요소로 저장되지 않는다. <strong><em>=&gt; Set 객체의 첫 번째 특징이였다!</em></strong></p>
<p>이러한 특성을 활용하면 기존에 <code>filter</code> 를 통해 제거했던 배열의 중복요소를 Set으로 간단하게 진행할 수 있다.</p>
<pre><code class="language-js">// 기존의 filter 활용 방법
const arr1 = array =&gt; array.filter((v, i, selr) =&gt; self.indexOf(v) === i)
console.log(arr([2, 1, 2, 3, 4, 3, 4])) // [2, 1, 3, 4]

// Set 활용 방법
const arr1 = array =&gt; [...new Set(array)]
console.log(arr([2, 1, 2, 3, 4, 3, 4])) // [2, 1, 3, 4]</code></pre>
<hr>
<h3 id="set-객체의-메서드">Set 객체의 메서드</h3>
<h4 id="요소-개수-확인">요소 개수 확인</h4>
<ul>
<li><code>Set.prototype.size</code> 프로퍼티 사용</li>
<li><code>size</code> 프로퍼티는 setter 함수없이 getter 함수만 존재하는 접근자 프로퍼티이기 때문에 요소 개수를 변경은 할 수 없다. (<em>조회만 가능</em>)</li>
</ul>
<pre><code class="language-js">const {size} = new Set([1, 2, 3, 3])
console.log(size) // 3 -&gt; 중복된 값 제외하고 개수 체크</code></pre>
<h4 id="요소-추가">요소 추가</h4>
<ul>
<li><code>Set.prototype.add</code> 메서드 사용</li>
<li><code>add</code> 메서드는 호출할 때마다 새로운 요소가 추가된 Set객체를 반환하므로, 연속적인 호출가능</li>
</ul>
<p>이 때, 중복된 요소를 add 메서드로 추가하여도 에러를 발생시키지않고 <strong>알아서^^</strong> 무시한다. (<em>알잘딱깔센~</em>)</p>
<pre><code class="language-js">const set = new Set()

set.add(1).add(2).add(2)
console.log(set) // Set(2) {1, 2}</code></pre>
<blockquote>
<p>Set 객체는 객체나 배열과 같은 자바스크립트의 모든 값을 요소로 저장할 수 있다!</p>
</blockquote>
<h4 id="특정-요소-존재여부-확인">특정 요소 존재여부 확인</h4>
<ul>
<li><code>Set.prototype.has</code> 메서드 사용</li>
<li><code>has</code> 메서드는 <code>불리언(boolean) 값</code> 반환</li>
</ul>
<pre><code class="language-js">const set = new Set([1, 2, 3])

console.log(set.has(2)) // true
console.log(set.has(4)) // false</code></pre>
<p>그렇다면 요소를 삭제할 때는 어떤 메서드를 사용하면 될까? (<em>우리가 잘 아는 그 단어!</em>)</p>
<h4 id="요소-일괄삭제">요소 (일괄)삭제</h4>
<ul>
<li><code>Set.prototype.delete</code> 메서드 사용</li>
<li><code>delete</code> 메서드는 삭제 성공여부를 <code>불리언(boolean) 값</code> 으로 반환</li>
</ul>
<p>단, <code>delete</code> 메서드를 사용할 때는 삭제하려는 요소의 인덱스가 아니라 <code>요소값</code> 자체를 전달해야한다. 
Set 객체 특성의 3번째였던 내용을 떠올려보자.
Set 객체는 <strong>배열과 다르게 인덱스로 요소에 접근할 수 없다.</strong> 고 하였다.</p>
<p>특정 요소말고 전체 삭제(일괄 삭제)를 하고 싶다면 <code>Set.prototype.clear</code> 메서드를 사용하면 되며, <code>clear</code> 메서드는 언제나 <code>undefined</code> 를 반환한다. </p>
<h4 id="요소-순회">요소 순회</h4>
<ul>
<li><code>Set.prototype.forEach</code> 메서드 사용</li>
<li>콜백 함수와 forEach 메서드의 콜백 함수 내부에서 this 로 사용될 객체를 인수로 전달하며, 3개의 인수를 전달받음 (<em>배열의 <code>forEach</code> 메서드와 유사</em>)<ul>
<li>현재 순회 중인 요소값</li>
<li>현재 순회 중인 요소값</li>
<li>현재 순회 중인 Set 객체 자체</li>
</ul>
</li>
</ul>
<p>Set 객체는 <code>이터러블</code> 이므로 <code>for...of문</code> 으로도 순회할 수 있으며, 스프레드 문법 및 배열 디스트럭처링의 대상이 될 수도 있다.</p>
<h4 id="집합-연산">집합 연산</h4>
<ul>
<li>교집합 <code>intersection</code> 메서드 사용</li>
<li>합집합 <code>union</code> 메서드 사용</li>
<li>차집합 <code>difference</code> 메서드 사용</li>
<li>부분집합 및 상위집합 <code>isSuperset</code> 메서드 사용</li>
</ul>
<hr>
<h2 id="map이란">Map이란?</h2>
<p><strong>| Map 객체는 키와 값의 쌍으로 이루어진 컬렉션이다.</strong></p>
<p>Set이 배열과 유사했다면, <code>Map 객체</code>는 객체와 유사하지만 몇 가지 차이점이 있는데 표로 살펴보자.</p>
<table>
<thead>
<tr>
<th>구분</th>
<th>객체</th>
<th>Map 객체</th>
</tr>
</thead>
<tbody><tr>
<td>키로 사용할 수 있는 값</td>
<td>문자열, 심벌 값</td>
<td>객체를 포함한 모든 값</td>
</tr>
<tr>
<td>이터러블</td>
<td>X</td>
<td>O</td>
</tr>
<tr>
<td>요소 개수 확인방법</td>
<td>Object.keys(obj).length</td>
<td>map.size</td>
</tr>
</tbody></table>
<p>그렇다면 Map객체는 어떻게 생성할까?
방법은 Set객체와 동일하다.</p>
<h3 id="map-객체의-생성">Map 객체의 생성</h3>
<p><strong>| Map 생성자 함수로 객체를 생성한다.</strong></p>
<p>단, Map 생성자 함수에 인수를 전달하지 않을 경우 <code>빈 객체</code> 가 생성된다.</p>
<pre><code class="language-js">// Map 생성
const map = new Map() // 인수 전달안함
console.log(map) // Map(0) {} 빈 객체 생성</code></pre>
<blockquote>
<p>Map 객체도 인수로 전달한 이터러블 중 중복된 키를 갖는 요소가 존재하면 값 덮어쓰기를 하므로 중복된 키를 갖는 요소가 존재할 수 없다.</p>
</blockquote>
<hr>
<h3 id="map-객체의-메서드">Map 객체의 메서드</h3>
<h4 id="요소-개수-확인-1">요소 개수 확인</h4>
<ul>
<li><code>Map.prototype.size</code> 프로퍼티 사용</li>
<li><code>size</code> 프로퍼티는 setter 함수없이 getter 함수만 존재하는 접근자 프로퍼티이기 때문에 요소 개수를 변경은 할 수 없다. (<em>조회만 가능</em>)</li>
</ul>
<p><em>놀랍게도 Set 객체와 똑같은 내용...!</em></p>
<h4 id="요소-추가-1">요소 추가</h4>
<ul>
<li><code>Map.prototype.set</code> 메서드 사용</li>
<li><code>set</code> 메서드는 호출할 때마다 새로운 요소가 추가된 Map객체를 반환하므로, 연속적인 호출가능</li>
</ul>
<p>이 때, 중복된 요소를 set 메서드로 추가하여도 에러를 발생시키지않고 <strong>알아서^^</strong> 덮어쓰기를 한다. (<em>얘도 참 알잘딱깔센</em>)</p>
<h4 id="요소-취득">요소 취득</h4>
<ul>
<li><code>Map.prototype.get</code> 메서드 사용</li>
<li><code>get</code> 메서드의 인수로 전달한 키를 전달하면 Map 객체에서 전달된 키를 갖는 값을 반환하며, 존재하지 않을 경우에는 undefined 를 반환한다.</li>
</ul>
<h4 id="특정-요소-존재여부-확인-1">특정 요소 존재여부 확인</h4>
<ul>
<li><code>Map.prototype.has</code> 메서드 사용</li>
<li><code>has</code> 메서드는 <code>불리언(boolean) 값</code> 반환</li>
</ul>
<h4 id="요소-일괄삭제-1">요소 (일괄)삭제</h4>
<ul>
<li><code>Map.prototype.delete</code> 메서드 사용</li>
<li><code>delete</code> 메서드는 삭제 성공여부를 <code>불리언(boolean) 값</code> 으로 반환</li>
</ul>
<p>특정 요소말고 전체 삭제(일괄 삭제)를 하고 싶다면 <code>Map.prototype.clear</code> 메서드를 사용하면 되며, <code>clear</code> 메서드는 언제나 <code>undefined</code> 를 반환한다. </p>
<h4 id="요소-순회-1">요소 순회</h4>
<ul>
<li><code>Map.prototype.forEach</code> 메서드 사용</li>
<li>콜백 함수와 forEach 메서드의 콜백 함수 내부에서 this 로 사용될 객체를 인수로 전달하며, 3개의 인수를 전달받음 (<em>배열의 <code>forEach</code> 메서드와 유사</em>)<ul>
<li>현재 순회 중인 요소값</li>
<li>현재 순회 중인 요소값</li>
<li>현재 순회 중인 Map 객체 자체</li>
</ul>
</li>
</ul>
<p>Map 객체는 <code>이터러블</code> 이므로 <code>for...of문</code> 으로도 순회할 수 있으며, 스프레드 문법 및 배열 디스트럭처링의 대상이 될 수도 있다.</p>
<hr>
<blockquote>
<p><strong>Set 은 배열과 닮은듯 다르고, Map 은 객체와 닮은듯 다른 형태의 자료구조인 것으로 정리해보자</strong></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[모던 자바스크립트 Deep Dive] 34장. 이터러블]]></title>
            <link>https://velog.io/@minji-kim0524/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Deep-Dive-34%EC%9E%A5.-%EC%9D%B4%ED%84%B0%EB%9F%AC%EB%B8%94</link>
            <guid>https://velog.io/@minji-kim0524/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Deep-Dive-34%EC%9E%A5.-%EC%9D%B4%ED%84%B0%EB%9F%AC%EB%B8%94</guid>
            <pubDate>Sun, 12 Oct 2025 18:56:55 GMT</pubDate>
            <description><![CDATA[<h1 id="내가-기준이야-기준">*&quot;내가 기준이야! 기준!!&quot;*</h1>
<hr>
<h3 id="이터러블-프로토콜이란">이터러블 프로토콜이란?</h3>
<p><strong>| 순회 가능한 데이터 컬렉션(자료구조)을 만들기 위한 약속한 규칙이다.</strong></p>
<p>ES6이전에는 각자의 방법으로 데이터 컬렉션을 순회하였지만, ES6에서는 <code>이터러블</code> 로 통일시켜 <em>&#39;for...of문&#39;</em>, <em>&#39;스프레드 문법&#39;</em>, <em>&#39;배열 디스트럭처링 할당&#39;</em> 의 대상으로 사용할 수 있도록 일원화했다.</p>
<hr>
<h3 id="이터러블이란">이터러블이란?</h3>
<p><strong>| 이터러블 프로토콜을 준수한 객체를 의미한다.</strong></p>
<p>Symbol.iterator 를 프로퍼티 키로 사용한 메서드를 직접 구현하거나 프로토타입 체인을 통해 상속받은 객체를 의미한다.</p>
<p>이터러블은 <code>for...of문</code>으로 순회할 수 있고, 스프레드 문법, 디스트럭처링 할당의 대상으로 사용할 수 있다. 
반면, Symbol.iterator 메서드를 직접 구현하지 않거나 상속받지 않은 일반 객체는 이터러블이 아닌 것으로 판단되기 때문에 <code>for...of문</code> 순회, 스프레드 문법, 디스트럭처링 할당의 대상으로 사용할 수 없다.</p>
<h3 id="이터레이터란">이터레이터란?</h3>
<p><strong>| 이터러블의 Symbol.iterator 메서드를 호출하면 반환되는 것을 의미한다.</strong></p>
<p>이터러블의 Symbol.iterator 메서드가 반환한 이터레이터는 <code>next</code> 메서드를 갖는다.
이 <code>next</code> 메서드는 이터러블의 각 요소 순회를 위한 포인터 역할을하는데, 순회 결과를 나타내는 <strong>이터레이터 리절트 객체</strong>를 반환한다.</p>
<p><strong>이터레이터 리절트 객체</strong> 의 <code>value</code> 프로퍼티는 현재 순회 중인 이터러블의 값을 나타내고, <code>done</code> 프로퍼티는 이터러블의 순회 완료 여부를 나타낸다.</p>
<h3 id="빌트인-이터러블">빌트인 이터러블</h3>
<p><strong>| 자바스크립트는 이터레이션 프로토콜을 준수한 객체인 빌트인 이터러블을 제공한다.</strong></p>
<table>
<thead>
<tr>
<th>이터러블</th>
<th>Symbol.iterator 메서드</th>
</tr>
</thead>
<tbody><tr>
<td>Array</td>
<td>Array.prototype[Symbol.iterator]</td>
</tr>
<tr>
<td>String</td>
<td>String.prototype[Symbol.iterator]</td>
</tr>
<tr>
<td>Map</td>
<td>Map.prototype[Symbol.iterator]</td>
</tr>
<tr>
<td>Set</td>
<td>Set.prototype[Symbol.iterator]</td>
</tr>
<tr>
<td>TypedArray</td>
<td>TypedArray.prototype[Symbol.iterator]</td>
</tr>
<tr>
<td>arguments</td>
<td>arguments[Symbol.iterator]</td>
</tr>
<tr>
<td>DOM 컬렉션</td>
<td>NodeList.prototype[Symbol.iterator] , HTMLCollection.prototype[Symbol.iterator]</td>
</tr>
</tbody></table>
<h3 id="forof-문">for...of 문</h3>
<p>*<em>| 이터러블을 순회하면서 이터러블의 요소를 변수에 할당한다. *</em></p>
<pre><code class="language-js">// 형태 - for...in문과 유사함
for (변수선언문 of 이터러블) {...}</code></pre>
<ol>
<li>for...of문은 내부적으로 이터레이터의 next 메서드를 호출하여 이터러블을 순회한다.</li>
<li>그리고 next 메서드가 반환한 이터레이터 리절트 객체의 value 프로퍼티 값을 변수에 할당한다.</li>
<li>이터레이터 리절트 객체의 done 프로퍼티 값이 false이면 순회를 계속하고 true이면 순회를 중단한다.</li>
</ol>
<h3 id="이터러블과-유사-배열-객체">이터러블과 유사 배열 객체</h3>
<p>마치 배열처럼 인덱스로 프로퍼티 값에 접근할 수 있고 length 프로퍼티를 갖는 객체를 말한다.
유사 배열 객체는 length 프로퍼티를 갖기 때문에 for문으로 순회가 가능하고, 배열처럼 인덱스로 프로퍼티 값에 접근도 가능하다.</p>
<p>단, 단어 그대로 &#39;유사&#39;배열 객체이기 때문에 Symbol.iterator 메서드가 없으므로 for...of 문으로의 순회는 할 수 없다.</p>
<hr>
<p>그렇다면 이터레이션 프로토콜이 왜 필요할까?</p>
<p>이터러블은 데이터 소비자에 의해 사용되므로 데이터 공급자의 역할을 한다고 볼 수 있다.
만약, 다양한 데이터 공급자가 각자의 순회 방식을 갖는다면 데이터 소비자는 그에 맞는 방식을 모두 지원해야하는 불편함을 느끼게 된다. (<del><em>비효율적이다</em></del>)</p>
<p>그렇기때문에 다양한 공급자가 기준인 <code>이터레이션 프로토콜</code>을 준수하도록 규정하면 데이터 소비자는 <code>이터레이션 프로토콜</code>만 지원하도록 구현하면 되므로 무척 효율적이다. (<em>소비자와 공급자를 연결해주는 인터페이스 역할</em>)</p>
<hr>
<h3 id="사용자-정의-이터러블">사용자 정의 이터러블</h3>
<p>이터레이션 프로토콜을 준수하지 않는 일반 객체도 준수하도록 구현을 하면 사용자 정의 이터러블이 될 수 있다.
이를 통해 다양하게 활용을 할 수 있는데, 대표적인 예시로는 다음 두 가지가 있다.</p>
<ul>
<li>피보나치 수열 구현</li>
<li>무한 수열 구현</li>
</ul>
<p>위 두 가지의 공통점이 있는데, 바로. <strong>지연 평가</strong>를 통해 데이터를 생성한다는 것이다.
<strong>지연 평가</strong> 란 데이터가 필요한 시점 이전까지는 데이터를 생성하지 않다가 필요한 시점이 되면 데이터는 생성하는 기법이다.</p>
<p><code>지연 평가</code> 를 사용하면 불필요한 데이터를 미리 생성하지 않고 필요한 순간에 생성하기 때문에 실행 속도가 향상되고, 불필요한 메모리 소비를 하지않아 &#39;무한&#39;도 표현할 수 있다는 장점이 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[모던 자바스크립트 Deep Dive] 33장. Symbol]]></title>
            <link>https://velog.io/@minji-kim0524/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Deep-Dive-33%EC%9E%A5.-Symbol</link>
            <guid>https://velog.io/@minji-kim0524/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Deep-Dive-33%EC%9E%A5.-Symbol</guid>
            <pubDate>Sun, 12 Oct 2025 14:22:30 GMT</pubDate>
            <description><![CDATA[<h1 id="내가-바로-유일무이한-7번째-타입">*&quot;내가 바로 유일무이한 7번째 타입!&quot;*</h1>
<hr>
<h3 id="들어가면서">들어가면서,</h3>
<p>자바스크립트가 ECMAScript로 표준화되면서 6개의 타입(<strong><em>문자열, 숫자, 불리언, undefined, null, 객체 타입</em></strong>)이 존재하게 된다.</p>
<p>그런데 ES6에서 7번째 데이터 타입으로 등장한 것이 있는데, 그게 바로 이번 장에서 살펴볼 <strong>Symbol(심벌)</strong> 이다.</p>
<p><strong>Symbol(심벌)</strong> 은 다른 값과 <em>중복되지 않는</em> <strong>유일무이한 값</strong> 으로, 주로 충돌 위험이 없는 유일한 프로퍼티 키를 만들때 사용한다.</p>
<hr>
<h2 id="symbol심벌">Symbol(심벌)</h2>
<p>그렇다면 변경 불가능한 원시 타입의 값인 Symbol(심벌)은 어떻게 생성할까?</p>
<h3 id="symbol심벌-생성">Symbol(심벌) 생성</h3>
<p>*<em>| 심벌 값은 Symbol 함수를 호출하여 생성하며, 생성된 심벌 값은 외부로 노출되지 않기때문에 확인할 수가 없다. *</em></p>
<pre><code class="language-js">// 심벌 값 생성
const mySymbol = Symbol()

console.log(mySymbol) // Symbol()</code></pre>
<p>Symbol 함수에는 선택적으로 &#39;문자열&#39; 을 인수로 전달할 수 있는데, 전달하는 &#39;문자열&#39;은 심벌 값에 대한 <code>설명</code> 으로, 디버깅 용도로만 사용하며 심벌 값 생성에는 영향을 미치지 않는다.</p>
<pre><code class="language-js">const mySymbol = Symbol(&#39;hello&#39;)

console.log(mySymbol) // Symbol(hello)
console.log(mySymbol.description) // hello
console.log(mySymbol.toString()) // Symbol(hello)

console.log(typeof mySymbol) // symbol
console.log(typeof mySymbol.toString()) // string</code></pre>
<h3 id="symbol심벌의-메서드">Symbol(심벌)의 메서드</h3>
<h3 id="1️⃣-symbolfor">1️⃣ Symbol.for</h3>
<p><strong>| 인수로 전달받은 문자열을 &#39;키&#39;로 사용하여 &#39;키&#39;와 심벌 값의 쌍들이 저장되어 있는 전역 심벌 레지스트리에서 해당 &#39;키&#39;와 일치하는 심벌 값을 검색하는 메서드이다.</strong></p>
<ul>
<li><strong>(검색 성공)</strong> 새로운 심벌 값을 생성하지 않고, 검색된 심벌 값을 반환한다.</li>
<li><strong>(검색 실패)</strong> 새로운 심벌 값을 생성하여 메서드의 인수로 전달된 &#39;키&#39;로 레지스트리에 저장한 다음 생성된 새로운 심벌 값을 반환한다.</li>
</ul>
<p>Symbol 함수는 호출될 때마다 <code>유일무이</code>한 값을 생성하는데, 생성 당시에는 심벌 값 저장소인 레지스트리에서 해당 심벌 값을 검색할 수 있는 &#39;키&#39;가 지정되어 있지 않기 때문에 <del>전역 심벌 레지스트리에 등록 및 관리되지 않는다.</del> </p>
<p>하지만, <code>Symbol.for</code> 메서드를 사용하면 전역에서 중복되지 않는 <code>유일무이</code> 한 심벌 값을 생성하므로 레지스트리를 통해 공유가능한 상태가 되어 관리가 가능해진다.</p>
<h3 id="2️⃣-symbolkeyfor">2️⃣ Symbol.keyFor</h3>
<p><strong>| 전역 심벌 레지스트리에 저장된 심벌 값의 &#39;키&#39;를 추출할 수 있는 메서드이다.</strong></p>
<pre><code class="language-js">// Symbol.for 메서드를 사용하여 심벌 값 생성 → 관리 ✅
const s1 = Symbol.for(&#39;mySymbol&#39;)
Symbol.keyFor(s1) // mySymbol

// Symbol 함수를 사용하여 심벌 값 생성 → 관리 ❌
const s2 = Symbol(&#39;haha&#39;)
Symbol.keyFor(s2) // undefined</code></pre>
<hr>
<p>심벌 값은 다음과 같이 다양하게 활용될 수 있는데, 활용되는 경우들을 간단하게 살펴보자.</p>
<ul>
<li><code>상수</code>를 정의할 때 심벌 값으로 상수 값을 생성한다.</li>
<li>심벌 값으로 <code>프로퍼티 키</code> 를 동적으로 생성할 수 있는데, 심벌 값을 프로퍼티 키로 사용하려면 심벌 값에 <code>대괄호</code> 를 사용하면 된다. (접근할 때도 <code>대괄호</code> 는 써주기!)</li>
<li>심벌 값을 프로퍼티 키로 사용하여 생성한 프로퍼티는 <code>for...in</code>, <code>Object.keys</code> 등의 메서드로 찾을 수 없으므로 외부에 노출할 필요가 없는 프로퍼티 은닉이 가능하다.
(<em>단, ES6에서 도입된 Object.getOwnPropertySymbols 메서드 사용시에는 찾을 수 있다</em>)</li>
</ul>
<p>또한, 심벌 값의 몇 가지 특징으로는 다음과 같은 내용이 있다.</p>
<ul>
<li>일반적으로 표준 빌트인 객체에 사용자 정의 메서드를 직접 추가하여 확장하는 것은 권장하지 않으며, 읽기 전용으로 사용하는 것을 권장한다.</li>
<li>자바스크립트가 기본적으로 제공하는 빌트인 심벌 값이 있으며, <code>Well-known-Symbol</code> 이라고 부른다. </li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[모던 자바스크립트 Deep Dive] 31장. RegExp]]></title>
            <link>https://velog.io/@minji-kim0524/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Deep-Dive-31%EC%9E%A5.-RegExp</link>
            <guid>https://velog.io/@minji-kim0524/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Deep-Dive-31%EC%9E%A5.-RegExp</guid>
            <pubDate>Fri, 26 Sep 2025 02:56:14 GMT</pubDate>
            <description><![CDATA[<h1 id="너-나랑-매칭됐어-💘">*&quot;너 나랑 매칭됐어&quot;* 💘</h1>
<hr>
<h2 id="정규표현식이란">정규표현식이란?</h2>
<p><strong>| 일정한 패턴을 가진 문자열의 집합을 표현하기 위해 사용하는 형식 언어이다.</strong></p>
<p>정규표현식은 문자열을 대상으로 <code>패턴 매칭 기능</code>을 제공한다.
(<em>패턴 매칭 기능: 특정 패턴과 일치하는 문자열을 검색하거나 추출 또는 치환할 수 있는 기능</em>)</p>
<p>정규표현식 사용시, 반복문과 조건문없이 패턴을 정의하고 테스트하여 문자열 체크를 할 수 있다.
(<em>단, 주석 또는 공백은 허용되지 않으며 여러 기호를 혼합하여 사용하기때문에 가독성은 좋지 않다.</em>)</p>
<h3 id="정규표현식-생성">정규표현식 생성</h3>
<p>정규표현식 생성방법에는 두 가지 방법이 있다.</p>
<p>1️⃣ 정규 표현식 리터럴
2️⃣ RegExp 생성자 함수</p>
<p>일반적으로는 <strong>정규 표현식 리터럴</strong>을 사용한다.</p>
<p><strong>정규 표현식 리터럴</strong>은 <code>패턴</code> 과 <code>플래그</code> 로 구성된다.</p>
<blockquote>
<p>여기서 <code>패턴</code> 과 <code>플래그</code>가 무엇인지 궁금할텐데, 곧 나오니 일단 예제만 확인하고 넘어가자 😉</p>
</blockquote>
<pre><code class="language-js">const target = &#39;Is this all there is?&#39;

// 패턴: is
// 플래그: i (대소문자 구별 ❌)
const regexp = /is/i

// test 메서드: 문자열에 대해 정규 표현식 regexp 패턴을 검색하여
// 매칭결과를 불리언 값으로 반환
regexp.test(target) // true</code></pre>
<h3 id="regexp-메서드">RegExp 메서드</h3>
<p>정규 표현식을 사용하는 메서드는 다양하게 있지만, 이번 31장 에서는 3가지만 살펴보자.</p>
<h4 id="1️⃣-regexpprototypeexec">1️⃣ RegExp.prototype.exec</h4>
<p>인수로 전달받은 문자열에 대해 정규 표현식의 패턴을 검색하여 매칭 결과를 <code>배열</code>로 반환하며, 매칭 결과가 없는 경우에는 <code>null</code> 을 반환한다.
(<em>단, 문자열 내의 모든 패턴을 검색하는 <code>g 플래그</code>로 지정해도 <code>첫 번째</code> 매칭 결과만 반환한다.</em>)</p>
<pre><code class="language-js">const target = &#39;Is this all there is?&#39;
const regExp = /is/

regExp.exec(target)
// [&quot;is&quot;, index: 5, input: &quot;Is this all there is?&quot;, groups: undefined]</code></pre>
<h4 id="2️⃣-regexpprototypetest">2️⃣ RegExp.prototype.test</h4>
<p>인수로 전달받은 문자열에 대해 정규 표현식의 패턴을 검색하여 검색 결과를 <code>불리언 값</code> 으로 반환한다.</p>
<pre><code class="language-js">const target = &#39;Is this all there is?&#39;
const regExp = /is/

regExp.test(target) // true</code></pre>
<h4 id="3️⃣-stringprototypematch">3️⃣ String.prototype.match</h4>
<p>대상 문자열과 인수로 전달받은 정규 표현식과의 매칭 결과를 <code>배열</code>로 반환한다.</p>
<p><strong>✋🏻 잠깐!</strong> 그렇다면 <code>exec</code> 메서드와 어떤 차이점이 있을까?
<code>exec</code> 메서드는 모든 패턴을 검색하는 <code>g 플래그</code>로 지정을 해도 <code>첫 번째</code> 매칭 결과만 반환한다고 했다.
하지만, <code>match</code> 메서드는 <code>g 플래그</code>가 지정되면 <code>모든</code> 매칭 결과를 반환한다는 점이 다르다.</p>
<pre><code class="language-js">// g플래그 미지정
const target = &#39;Is this all there is?&#39;
const regExp = /is/

target.match(regExp)
// [&quot;is&quot;, index: 5, input: &quot;Is this all there is?&quot;, groups: undefined]

// g플래그 지정
const target = &#39;Is this all there is?&#39;
const regExp = /is/g

regExp.test(target) // [&quot;is&quot;, &quot;is&quot;]</code></pre>
<hr>
<h3 id="플래그">플래그</h3>
<p><strong>| 플래그는 정규 표현식의 검색 방식을 설정하기 위해 사용한다.</strong></p>
<p>플래그는 총 6가지가 있지만, 중요한 3가지만 살펴보자. (<del><em>TOP3</em></del>)</p>
<table>
<thead>
<tr>
<th>플래그</th>
<th>의미</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>i</td>
<td>Ignore case</td>
<td>대소문자 구별없이 패턴을 검색한다.</td>
</tr>
<tr>
<td>g</td>
<td>Global</td>
<td>대상 문자열 내에서 패턴과 일치하는 모든 문자열을 전역 검색한다.</td>
</tr>
<tr>
<td>m</td>
<td>Multi line</td>
<td>문자열의 행이 바뀌더라도 계속 패턴 검색을 한다.</td>
</tr>
</tbody></table>
<p>이러한 플래그는 <code>옵션</code> 이므로 선택하여 사용할 수 있으며, 순서 상관없이 하나 이상의 플래그를 <code>동시</code>에 설정할 수도 있다.</p>
<p>아무런 플래그를 사용하지 않을 경우에는 기본적으로 대소문자를 <code>구별</code>하여 패턴검색을 한다.
또한, 문자열에 검색 매칭 대상이 1개 이상 존재한다고해도 <code>첫 번째</code> 매칭한 대상만 검색 후 종료한다.</p>
<pre><code class="language-js">const target = &#39;Is this all there is?&#39;

target.match(/is/)
// [&quot;is&quot;, index: 5, input: &quot;Is this all there is?&quot;, groups: undefined]

target.match(/is/i)
// [&quot;Is&quot;, index: 0, input: &quot;Is this all there is?&quot;, groups: undefined]

target.match(/is/g) // [&quot;is&quot;, &quot;is&quot;]

target.match(/is/ig) // [&quot;Is&quot;, &quot;is&quot;, &quot;is&quot;]</code></pre>
<h3 id="패턴">패턴</h3>
<p><strong>| 문자열의 일정한 규칙을 표현하기 위해 사용한다.</strong></p>
<p><code>/</code> 로 열고 닫으며 문자열 <code>따옴표</code> 는 생략한다.
<code>따옴표</code> 를 생략하는 이유는 포함할 경우, 따옴표까지 패턴에 포함하여 검색하기 때문이다.</p>
<p>패턴은 특별한 의미를 가지는 <code>메타문자</code> 또는 <code>기호</code> 로도 표현할 수 있다.</p>
<p>그렇다면 패턴을 표현하는 방법에는 어떤 내용이 있는지 살펴보자.</p>
<h4 id="1️⃣-문자열-검색">1️⃣ 문자열 검색</h4>
<p>패턴에 <code>문자</code> 또는 <code>문자열</code>을 지정하면 검색 대상 문자열에서 지정 문자/문자열을 검색한다.</p>
<p>검색 수행은 RegExp 메서드를 사용하여 검색 대상 문자열과 정규 표현식의 매칭 결과를 구할때 이루어진다.</p>
<p>이 때, 플래그를 생략하면 대소문자를 구별하여 매칭 진행을 한 후 <code>첫 번째</code> 결과만 반환하는데, 대소문자 구별없이 검색을 해야할 경우에는 <code>i 플래그</code> 를 사용한다. (<em>첫 번째 결과만 반환되는 내용은 동일하게 적용됨</em>)</p>
<h4 id="2️⃣-임의의-문자열-검색">2️⃣ 임의의 문자열 검색</h4>
<p><code>.</code> 은 임의의 문자 <code>한 개</code> 를 의미하는데, 문자의 <code>내용</code>은 무엇이든 상관없다.</p>
<p>즉, <code>.</code> 의 갯수는 문자열의 자릿수를 의미하는 것이다.</p>
<ul>
<li><code>...</code> 3자리 문자열</li>
<li><code>..</code> 2자리 문자열</li>
<li><code>.....</code> 5자리 문자열</li>
</ul>
<h4 id="3️⃣-반복-검색">3️⃣ 반복 검색</h4>
<p><code>{m,n}</code> 은 패턴이 <code>최소</code> m번, <code>최대</code> n번 반복되는 문자열을 의미하며, 콤마 뒤에 <del><em>공백</em></del> 이 있으면 정상 동작하지 않는다.</p>
<p>패턴이 <code>{n}</code> 인 경우에는 패턴이 n번 반복되는 문자열을 의미한다. (<code>{n,n}</code> 과 동일의미)
<code>{n,}</code> 패턴의 경우는 <code>최소</code> n번 반복디는 문자열을 의미한다.</p>
<pre><code class="language-js">const target = &#39;A AA B BB Aa Bb AAA&#39;

const regExp = /A{1,2}/g

target.match(regExp) // [&quot;A&quot;, &quot;AA&quot;, &quot;A&quot;, &quot;AA&quot;, &quot;A&quot;]</code></pre>
<p><code>+</code> 가 붙는 경우가 있는데, 이 경우는 패턴이 <code>최소</code> 한 번 이상 반복되는 문자열을 의미힌다.
(<code>{1,}</code> 와 동일한 의미이다.)</p>
<pre><code class="language-js">const target = &#39;A AA B BB Aa Bb AAA&#39;

const regExp = /A+/g

target.match(regExp) // [&quot;A&quot;, &quot;AA&quot;, &quot;A&quot;, &quot;AAA&quot;]</code></pre>
<p><code>?</code> 는 패턴이 <code>최대</code> 한 번(<em>0번 포함</em>) 이상 반복되는 문자열을 의미한다.
(<code>{0,1}</code> 와 동일한 의미이다.)</p>
<h4 id="4️⃣-or-검색">4️⃣ OR 검색</h4>
<p><code>|</code> 가 <strong>or</strong>의 의미를 갖는다.</p>
<pre><code class="language-js">const target = &#39;A AA B BB Aa Bb AAA&#39;

const regExp = /A|B/g

target.match(regExp) // [&quot;A&quot;, &quot;A&quot;, &quot;A&quot;, &quot;B&quot;, &quot;B&quot;, &quot;B&quot;, &quot;A&quot;, &quot;B&quot;]</code></pre>
<p>이 외에도 다양한 형태로 <strong>or</strong> 의 개념이 사용된다. </p>
<ul>
<li>분해되지 않은 단어로 검색하려면 <code>+</code> 를 같이 사용하면 된다. ⇒ <code>/A+|B+/g</code></li>
<li>범위를 지정하기를 원하면 <code>[]</code> 기호 내에 <code>-</code>를 사용하면 된다. ⇒ <code>/[A-Z]+/g</code></li>
<li>대소문자 구별없이 알파벳을 검색하고 싶을 때는 이렇게 사용하면 된다. ⇒ `/[A-Za-z]+/g``</li>
<li>숫자를 검색하고 싶을 때는 이렇게 사용하면 된다. ⇒ <code>/[0-9
]+/g</code></li>
<li>숫자를 검색을 하되 결과에 쉼표로 구분짓고 싶지 않으면 <code>,</code>를 패턴에 포함시키면 된다. ⇒ <code>/[0-9,]+/g</code></li>
</ul>
<h4 id="5️⃣-not-검색">5️⃣ NOT 검색</h4>
<p><code>[...]</code> <strong>내</strong>의 <code>^</code> 는 <code>not</code> 을 의미한다.</p>
<ul>
<li><code>[^0-9]</code> 는 숫자를 <code>제외</code>한 문자를 의미한다.</li>
</ul>
<h4 id="6️⃣-시작-위치로-검색">6️⃣ 시작 위치로 검색</h4>
<p><code>[...]</code> <strong>밖</strong>의 <code>^</code> 는 문자열의 <code>시작</code>을 의미한다.</p>
<ul>
<li><code>/^https/</code> 는 문자열이 &#39;https&#39; 로 시작하는지 검사한다는 의미이다.</li>
</ul>
<h4 id="7️⃣-마지막-위치로-검색">7️⃣ 마지막 위치로 검색</h4>
<p><code>$</code> 은 문자열의 <code>마지막</code>을 의미한다.</p>
<ul>
<li><code>/com$/</code> 은 문자열이 &#39;com&#39;으로 끝나는지 검사한다는 의미이다.</li>
</ul>
<hr>
<h3 id="자주-사용하는-정규표현식">자주 사용하는 정규표현식</h3>
<p>위 패턴들을 이용하여 유독 자주 사용하는 정규표현식들이 있다.</p>
<ul>
<li>특정 단어로 시작하는지 검사</li>
<li>특정 단어로 끝나는지 검사</li>
<li>숫자로만 이루어진 문자열인지 검사 (<code>^\d+$</code> 사용)</li>
<li>하나 이상의 공백으로 시작하는지 검사 (<code>\s</code> 사용)</li>
<li>아이디로 사용가능한지 검사 (알파벳 구분없이 + 반복 패턴 활용)</li>
<li>메일주소 형식에 맞는지 검사</li>
<li>핸드폰번호 형식에 맞는지 검사 (<code>^\d{3]-\d{3,4}\d{4}$</code> 사용)</li>
<li>특수문자 포함여부 검사 (<code>[^A-Za-z0-9]</code> 사용)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[멋쟁이사자처럼 부트캠프 프론트엔드 14기/Next.js] What is Next.js?]]></title>
            <link>https://velog.io/@minji-kim0524/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-14%EA%B8%B0Next.js-What-is-Next.js</link>
            <guid>https://velog.io/@minji-kim0524/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-14%EA%B8%B0Next.js-What-is-Next.js</guid>
            <pubDate>Wed, 24 Sep 2025 15:04:03 GMT</pubDate>
            <description><![CDATA[<h1 id="나보다-잘-나가는-프레임워크-있으면-나와보라그래">*&quot;나보다 잘 나가는 프레임워크 있으면 나와보라그래!&quot;*</h1>
<hr>
<h2 id="nextjs란">Next.js란?</h2>
<p><strong>| React 기반의 메타 프레임워크로 웹 애플리케이션을 구축하는데 사용하는 도구이다.</strong></p>
<p><img src="https://velog.velcdn.com/images/minji-kim0524/post/69b40803-b179-44f6-957d-88ccbfd39069/image.png" alt=""></p>
<p><code>React</code>는 UI구성에 포커스를 맞춘 <code>라이브러리</code>인 반면, <code>Next.js</code>는 React 개념에 <strong>렌더링</strong>과 서비스 제공에 특화된 <strong>추가 기능</strong>을 더해서(<code>+</code>) 제공하는 <code>프레임워크</code>이다. </p>
<p><strong>✋🏻 잠깐!</strong> <code>라이브러리</code> 와 <code>프레임워크</code> 는 어떤거고, 무슨 차이가 있는거야?
<strong>🗂️ 라이브러리</strong> 는 개발자가 특정 기능이 필요할 때 <code>호출</code>하는 <code>코드 모음</code>이다.
<strong>⚙️ 프레임워크</strong> 는 애플리케이션의 전체적인 구조와 흐름을 <code>제공</code>하며, 개발자가 그 틀 안에서 작업하도록 제어권을 가진다.</p>
<p><code>Next.js</code>가 가진 가장 큰 장점은 <strong>복잡한 도구설정의 자동화</strong>가 가능하다는 것이다. 
번들링, 컴파일 등의 작업을 내부적으로 알아서 처리하므로 개발자는 설정에 보내는 시간을 줄여 애플리케이션 구축에 더욱 집중할 수 있다. (<del><em>알잘딱깔센 Next.js 최고</em></del>)</p>
<p>그렇다면 React의 어떤 부분이 아쉬워서 Next.js가 나오게 된걸까?</p>
<h3 id="react만으로는-부족한-이유">React만으로는 부족한 이유</h3>
<p>리액트는 UI 라이브러리로, 화면구성에 특화되어있다.
반면 프로덕션 애플리케이션에 필요한 모든 기능을 제공하지는 않기때문에 리액트만을 이용해서 개발을 하기엔 부족하다.</p>
<p>React 와 Next.js 의 개발환경에는 어떤 차이가 있을까? </p>
<h4 id="react-개발환경">React 개발환경</h4>
<p>위에서 봤듯이 리액트는 <strong>UI 라이브러리</strong>이기 때문에, 개발에 필요한 기능은 개발자가 파악하고 직접 추가해야한다.
자주 쓰이는 개발에 필요한 기능은 다음과 같다.</p>
<ul>
<li>상태관리: <code>Zustand</code>, <code>Redux</code> 등</li>
<li>데이터 가져오기: <code>axios</code>, <code>fetch</code> 등</li>
<li>라우팅: <code>React Router</code>, <code>Tanstack Router</code> 등</li>
<li>번들링: <code>webpack</code>, <code>esbuild</code>, <code>Rspack</code> 등</li>
<li>이미지 최적화: 상황별 별도 도구 필요</li>
<li>코드 분할: 수동 설정 필요</li>
</ul>
<h4 id="nextjs-개발환경">Next.js 개발환경</h4>
<p>Next.js는 React 프레임워크로 실제 개발에 필요한 대부분의 <strong>기능</strong>이 포함되어 자동처리된다. (<del><em>편리성 Good</em></del>)</p>
<ul>
<li>SSR(<em>서버 사이드 렌더링</em>)을 기본적으로 지원한다.</li>
<li><code>라우팅</code> 시스템과 <code>상태 관리</code> 패턴이 빌트인 되어있다.</li>
<li>표준 <code>fetch API</code> 를 활용한다.</li>
<li>캐싱 시스템이 있다.</li>
<li>자동 번들링 및 최적화, 이미지/폰트 자동 최적화를 해준다.</li>
<li>자동 코드 분할이 가능하다.</li>
</ul>
<hr>
<h3 id="nextjs의-주요기능">Next.js의 주요기능</h3>
<p><img src="https://velog.velcdn.com/images/minji-kim0524/post/41cb53b1-1c09-4f22-b1dd-a3b306dfc579/image.png" alt=""></p>
<p><strong>| Next.js는 개발에 필요한 모든 기능을 통합적으로 제공한다.</strong></p>
<table>
<thead>
<tr>
<th>특징</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>라우팅</td>
<td>서버 컴포넌트 위에 구축된 파일 시스템 기반의 라우터로 레이아웃, 중첩된 라우팅, 로딩 상태, 오류 처리등을 지원한다.</td>
</tr>
<tr>
<td>렌더링</td>
<td>클라이언트 및 서버 컴포넌트를 사용한 CSR 및 SSR / Next.js를 사용하여 서버에서 정적 및 동적 렌더링이 더욱 최적화되었다. / Edge, Node.js 런타임에서 스트리밍한다.</td>
</tr>
<tr>
<td>데이터 패칭</td>
<td>React 및 웹 플랫폼에서 사용 가능한 fetch API와 async/await 지원으로 데이터 가져오기가 용이하다.</td>
</tr>
<tr>
<td>스타일링</td>
<td>CSS 모듈, Tailwind CSS, CSS-in-JS 등 다양한 스타일 방법을 지원한다.</td>
</tr>
<tr>
<td>성능 최적화</td>
<td>성능 향상 및 사용자 경험 개선을 위한 이미지, 폰트, 스크립트를 최적화한다.</td>
</tr>
<tr>
<td>타입스크립트</td>
<td>타입스크립트에 대한 지원개선으로 더욱 효율적인 컴파일 처리와 커스텀 타입스크립트 플러그인을 통한 타입검사기능이 제공된다.</td>
</tr>
<tr>
<td>API 레퍼런스</td>
<td>모든 API 디자인이 업데이트 되었다. 🆕✨</td>
</tr>
</tbody></table>
<hr>
<h3 id="nextjs의-라우터-방식">Next.js의 라우터 방식</h3>
<p>Next.js는 다음의 두 가지 라우터 방식을 제공한다.</p>
<h4 id="1️⃣-app-router-권장">1️⃣ App Router (권장)</h4>
<ul>
<li>리액트의 최신 기능을 활용한다. (<em>서버 컴포넌트, 스트리밍 등</em>)</li>
<li>더 직관적인 파일 구조를 가지고 있다.</li>
<li>향상된 성능과 개발자 경험이 제공된다.</li>
</ul>
<h4 id="2️⃣-page-router-레거시">2️⃣ Page Router (레거시)</h4>
<ul>
<li>최초의 Next.js 라우터이다.</li>
<li>기존에 계속 진행해오던 Next.js 프로젝트에서 사용한다.</li>
<li>호환성 유지를 위해서 지속적인 지원은 이루어진다.</li>
</ul>
<p><strong>*⇒ 프로젝트를 새로 시작하는 경우에는 &#39;앱 라우터&#39; 사용을 권장한다. *</strong></p>
<hr>
<h3 id="nextjs를-사용해야하는-이유">Next.js를 사용해야하는 이유</h3>
<ul>
<li>개발 생산성 향상
복잡한 설정없이 개발에만 집중할 수 있다.</li>
<li>성능 최적화
다양한 최적화 기능들이 자동으로 적용된다.</li>
<li>SEO 친화적
서버 사이드 렌더링으로 SEO(검색 엔진 최적화)가 용이하다.</li>
<li>확장성
개인 프로젝트부터 대규모까지 광범위하게 대응이 가능하다.</li>
<li>활발한 생테계
Vercel의 지속적인 지원과 활말한 커뮤니티를 통해 풍부한 리소스를 제공한다.</li>
</ul>
<h3 id="nextjs의-장점">Next.js의 장점</h3>
<h4 id="1-빠른-페이지-로딩">1. 빠른 페이지 로딩</h4>
<ul>
<li>Next.js는 페이지를 미리 렌더링하여 클라이언트로 전송한다.</li>
<li>React의 기본 방식인 클라이언트 렌더링보다 초기 로딩 시간이 단축되기때문에 사용자 경험이 향상된다.<h4 id="2-검색-엔진-최적화">2. 검색 엔진 최적화</h4>
</li>
<li>완성된 HTML이 서버에서 전송되므로 검색 엔진의 콘텐츠 인덱싱이 용이하다.</li>
<li>공유 시, 미리보기가 지원되어 검색 엔진 최적화 서비스를 제공할 수 있다.<h4 id="3-빌트인-최적화-기능">3. 빌트인 최적화 기능</h4>
</li>
<li>Next.js가 웹 폰트, 이미지 등을 자동으로 처리하여 최적화한다.<h4 id="4-react가-서버-컴포넌트">4. React가 서버 컴포넌트</h4>
</li>
<li>리액트 서버 컴포넌트(RSC)는 서버에서 실행된다.</li>
<li>새로운 기능도입으로 RSC를 Next.js로 안정적이게 사용가능하다.</li>
</ul>
<hr>
<h3 id="nextjs-vs-react">Next.js vs. React</h3>
<p><strong>| React: 기본적으로 클라이언트 환경에서 작동한다.</strong>
1️⃣ 서버에서 빈 HTML 다운로드
2️⃣ 서버에서 JavaScript 다운로드
3️⃣ 클라이언트에서 모든 렌더링 수행</p>
<p><strong>| Next.js: 기본적으로 서버 환경에서 작동한다.</strong>
1️⃣ 서버에서 HTML 생성
2️⃣ 완성된 HTML을 클라이언트에 전송
3️⃣ 수화(Hydration) 과정으로 React 연결</p>
<p><strong>✋🏻 잠깐!</strong> 수화(Hydration)가 무슨 의미야?
서버에서 생성된 HTML에 React 기능을 연결하는 과정을 의미한다.
이 과정에서 정적 HTML이 동적 React 컴포넌트로 변환된다.
덕분에 사용자는 즉시 컨텐츠를 볼 수 있고, 상호작용도 가능해진다.</p>
<hr>
<p><strong>🔗 참고자료</strong>
<a href="https://nextjs.org/docs">https://nextjs.org/docs</a>
<a href="https://vercel.com/frameworks/nextjs">https://vercel.com/frameworks/nextjs</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[멋쟁이사자처럼 부트캠프 프론트엔드 14기/Next.js] Web Development History]]></title>
            <link>https://velog.io/@minji-kim0524/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-14%EA%B8%B0Next.js</link>
            <guid>https://velog.io/@minji-kim0524/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-14%EA%B8%B0Next.js</guid>
            <pubDate>Tue, 23 Sep 2025 13:31:32 GMT</pubDate>
            <description><![CDATA[<h1 id="rsc가-등장하기까지">*&quot;RSC가 등장하기까지...&quot;*</h1>
<hr>
<h2 id="웹-개발의-진화">웹 개발의 진화</h2>
<p>*<em>| PHP → RSC *</em></p>
<p><img src="https://velog.velcdn.com/images/minji-kim0524/post/b88bd2c7-da0d-4a34-86ee-17a31f2fa69b/image.jpeg" alt=""></p>
<h3 id="lamp스택과-php시대">LAMP스택과 PHP시대</h3>
<p>오래 전, 대부분의 개발자들이 풀스택 개발의 시작점이였던 <code>LAMP 스택(Linux, Apache, MySQL, PHP)</code> 로 풀스택 웹 개발을 시작했다.</p>
<p><code>PHP</code> 는 <code>HTML</code> 의 상위 집합으로 모든 HTML문서가 유효한 PHP문서가 될 수 있다. (<del><em>호환가능!</em></del>)</p>
<p>서버는 PHP파일을 해석하여 <strong>동적</strong>으로 HTML을 생성하고 사용자에게 전달한다.</p>
<pre><code class="language-php">&lt;?php
    # 사이트 제목 변수
  $siteTitle = &quot;트렌드에 민감한 MZ!&quot;;

  # 데이터베이스 연결
  $link = mysqli_connect(&quot;localhost&quot;, &quot;root&quot;, &quot;passwerd&quot;);
  # 데이터베이스에서 products 데이터 가져오기
  §data = mysqli_query($link, &quot;SELECT * FROM products&quot;);
?&gt;

&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;ko-KR&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot; /&gt;
    &lt;title&gt;&lt;?php echo $siteTitle ?&gt;&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;main&gt;
      &lt;h1&gt;트렌드 제품&lt;/h1&gt;
      &lt;?php 
        while ($item = mysqli_fetch_array($data)) {
          echo &quot;&lt;article&gt;&quot;;
            echo &quot;&lt;h2&gt;&quot; . $item[&#39;title&#39;] . &quot;&lt;/h2&gt;&quot;;
            echo &quot;&lt;p&gt;&quot; . $item[&#39;description&#39;] . &quot;&lt;/p&gt;&quot;;
          echo &quot;&lt;/article&gt;&quot;;
        }
      ?&gt;
    &lt;/main&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<h4 id="php-특징">php 특징</h4>
<ul>
<li><code>HTML</code>과 자유롭게 섞어서 사용할 수 있다.</li>
<li><code>서버</code>에서 실행되기 때문에 완전히 정적인 HTML을 반환한다.</li>
<li><code>데이터베이스(MySQL 등)</code> 와 직접 연결하여 데이터를 가져올 수 있다.</li>
<li>개발 방식이 직관적이고 선언적이다.</li>
</ul>
<h4 id="php-한계">php 한계</h4>
<ul>
<li><code>동적</code> 인터랙션(<em>기사 재정렬, 삭제 등</em>)은 자바스크립트 등 별도의 언어가 있어야 처리가 가능하다.</li>
<li>서버에서만 실행되기 때문에 <code>클라이언트</code>와의 <code>상호작용</code>이 제한적이다.</li>
</ul>
<hr>
<h3 id="react와-csr">React와 CSR</h3>
<p>초기의 리액트 앱은 대부분 클라이언트에서 데이터 요청을 처리했다.
브라우저에서 직접 데이터베이스 쿼리를 할 수 없어서 <strong>API 엔드포인트</strong> 로 데이터를 받아서 처리했다. (<em>SWR;Stale-Whie-Revalidate 라이브러리 사용</em>)</p>
<pre><code class="language-js">import useSWR from &#39;swr&#39;

// 데이터 가져오기 함수
async function fetcher(endpoint) {
  const response = await fetch(endpoint)
  const json = await response.json()
  return json
}

// 클라이언트 컴포넌트
function TrendingProducts() {
  // 데이터 가져오기 요청
  // - data: null
  // - isLoading: true
  const { data, isLoading } = useSWR(
      &#39;/api/get-trending-products&#39;, 
      fetcher
    )

  if (isLoading) {
    return &lt;p&gt;데이터 로딩중...&lt;/p&gt;
  }

  return (
    &lt;main&gt;
      &lt;h1&gt;트렌드 제품&lt;/h1&gt;
      {data.map((item) =&gt; (
        &lt;article key={item.id}&gt;
          &lt;h2&gt;{item.title}&lt;/h2&gt;
          &lt;p&gt;{item.description}&lt;/p&gt;
        &lt;/article&gt;
      ))}
    &lt;/main&gt;
  )
}</code></pre>
<h4 id="csr의-특징">CSR의 특징</h4>
<ul>
<li>데이터는 페이지 로드 후, 네트워크 요청을 받아온다.</li>
<li>첫 렌더링 시에는 데이터가 없으므로 로딩 상태관리가 필요하다.</li>
<li>사용자 경험이 PHP보다 복잡하고 때로는 느릴 수 있다.</li>
</ul>
<hr>
<h3 id="nextjs와-ssr">Next.js와 SSR</h3>
<p>CSR의 문제점을 보완하고 해결하기 위해 <code>Next.js</code> 가 등장했다. (<del><em>두-둥!</em></del>)</p>
<p><code>Next.js</code>는 <strong>서버</strong>에서 먼저 데이터를 가져와서 렌더링하는 SSR(Server Side Rendering)을 지원한다.</p>
<h4 id="ssr의-특징">SSR의 특징</h4>
<ul>
<li>페이지를 요청하면 <strong>서버</strong>가 먼저 데이터를 가져와서 렌더링한다.</li>
<li><code>getServerSideProps</code> 와 같은 특별한 메서드를 사용하여 데이터를 패칭한다.</li>
<li>초기 UI가 서버에서 완성된 상태로 사용자에게 전달된다.</li>
<li>페이지 수준에서만 SSR을 적용할 수 있다.</li>
</ul>
<h4 id="ssr의-한계">SSR의 한계</h4>
<ul>
<li>SSR은 <code>Next.js</code>, <code>Gatsby</code>, <code>Remix</code> 등 프레임워크마다 방식이 다르다. ⇒ <strong><em>표준화 부족</em></strong></li>
<li>SSR 컴포넌트도 클라이언트에서 다시 실행(<em>수화;Hydration</em>) 된다. ⇒ <strong><em>CPU 비용부담 및 성능부담 증가</em></strong></li>
<li>페이지 <code>최상단</code>에서만 SSR 로직을 사용할 수 있다.</li>
</ul>
<hr>
<h3 id="rsc-등장">RSC 등장</h3>
<p>서버와 클라이언트의 경계를 더욱 분명히 하고자 나온 것이 <strong>RSC(React Server Components)</strong> 이다.</p>
<h4 id="rsc의-특징">RSC의 특징</h4>
<ul>
<li>서버/클라이언트에서 실행되는 컴포넌트를 명확히 구분한다.</li>
<li><strong>서버 컴포넌트</strong>에서는 <code>상태</code>, <code>이펙트</code> 등 클라이언트 전용 기능을 사용할 수 없다.</li>
<li><strong>클라이언트 컴포넌트</strong>는 <code>&quot;use client&quot;</code> 라고 디렉티브를 명시해주어야 한다.</li>
<li><code>기본값</code>은 <strong>서버 컴포넌트</strong>이며, <strong>클라이언트 컴포넌트</strong>는 <code>옵트인(Opt-in)</code> 이다.</li>
<li>두 컴포넌트는 자유롭게 혼합하여 사용할 수 있다. (<del><em>이것이 RSC의 장점</em></del>)</li>
</ul>
<h4 id="rsc의-장점">RSC의 장점</h4>
<ul>
<li>서버에서만 실행되는 컴포넌트는 클라이언트로 코드가 전송되지 않는다. 
(<em>SSR의 &#39;클라이언트에서 다시 실행됨으로 인해 CPU 비용부담 및 성능부담 증가&#39; 내용 보완</em>)</li>
<li><code>데이터베이스 요청</code>, <code>API호출</code> 등 <strong>서버 작업</strong>을 컴포넌트 내부에서 직접 수행 가능하다.</li>
<li>클라이언트 컴포넌트와 조합하여 동적 인터랙션을 구현할 수 있다.</li>
</ul>
<hr>
<h3 id="정리하자면">정리하자면,</h3>
<p>RSC는 아직은 새로운 개념에 가까우며, SSR과는 별개의 패러다임이다.
그렇다고 각각 독립적으로 사용하기보다는 함께 사용할 때 더 큰 시너지효과를 낸다.</p>
<p>RSC와 SSR 각각에 해당되는 앱/방식을 살펴보자.</p>
<table>
<thead>
<tr>
<th>구분</th>
<th>SSR</th>
<th>RSC</th>
</tr>
</thead>
<tbody><tr>
<td>표준 클라이언트 앱</td>
<td></td>
<td></td>
</tr>
<tr>
<td>서버 렌더링 앱</td>
<td>✅</td>
<td></td>
</tr>
<tr>
<td>RSC 페이로드</td>
<td></td>
<td>✅</td>
</tr>
<tr>
<td>모던 풀스택 React</td>
<td>✅</td>
<td>✅</td>
</tr>
</tbody></table>
<h4 id="표준-클라이언트-앱">표준 클라이언트 앱</h4>
<ul>
<li>일반적으로 브라우저에서 실행되는 리액트 앱을 의미한다.</li>
<li>SSR이나 RSC없이 모든 UI와 로직이 클라이언트에서 작동한다.</li>
</ul>
<h4 id="서버-렌더링-앱">서버 렌더링 앱</h4>
<ul>
<li>SSR을 사용하면 서버에서 HTML을 미리 렌더링하여 사용자에게 전달할 수 있다.</li>
<li>초기 로딩속도가 빠르며, SEO(검색엔진 최적화)에 유리하다.</li>
<li>RSC만으로는 전통적인 SSR앱을 구현할 수 없기에 함께 사용하는 것이 권장된다.</li>
</ul>
<h4 id="rsc-페이로드">RSC 페이로드</h4>
<ul>
<li>RSC에서 서버가 클라이언트로 데이터를 전달하는 형식이다.</li>
<li>서버에서만 실행되는 컴포넌트의 특성을 활용할 수 있다.</li>
<li>SSR에서는 RSC의 기능을 사용할 수 없다.</li>
</ul>
<h4 id="모던-풀스택-react">모던 풀스택 React</h4>
<ul>
<li>최신 <code>Next.js</code> 에서 지원하는 방식이다.</li>
<li>SSR과 RSC를 모두 활용하여 서버 및 클라이언트의 경계를 명확히 한다.</li>
<li>효율적인 데이터 패칭 및 UI렌더링이 가능하다.</li>
<li>SSR과 RSC가 함께 사용될 때 가장 강력한 풀스택 개발 환경이 만들어진다.</li>
</ul>
<p><strong>✋🏻 잠깐!</strong> 
살펴본 내용중에 <strong>서버 컴포넌트</strong>, <strong>클라이언트 컴포넌트</strong>라는 말이 자주 나왔다.</p>
<p>여기서 <strong>서버 컴포넌트</strong>는 이름에서도 유추할 수 있듯이 서버에서만 렌더링되는 것이 맞지만, <strong>클라이언트 컴포넌트</strong>는 이름과는 달리 서버 및 클라이언트. 즉, 모든 환경에서 렌더링이 된다는 점을 기억하자!</p>
<hr>
<p>** 🔗 참고자료**
<a href="https://www.php.net/">https://www.php.net/</a>
<a href="https://www.digitalocean.com/community/tutorials/how-to-install-lamp-stack-on-ubuntu">https://www.digitalocean.com/community/tutorials/how-to-install-lamp-stack-on-ubuntu</a>
<a href="https://nextjs.org/docs/app/getting-started/server-and-client-components">https://nextjs.org/docs/app/getting-started/server-and-client-components</a>
<a href="https://swr.vercel.app/ko">https://swr.vercel.app/ko</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[멋쟁이사자처럼 부트캠프 프론트엔드 14기/Next.js] React  → NEXT.js]]></title>
            <link>https://velog.io/@minji-kim0524/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-14%EA%B8%B0Next.js-React-NEXT.js</link>
            <guid>https://velog.io/@minji-kim0524/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-14%EA%B8%B0Next.js-React-NEXT.js</guid>
            <pubDate>Mon, 22 Sep 2025 14:10:17 GMT</pubDate>
            <description><![CDATA[<h1 id="개념은-익히고-nextjs로-넘어가자"><em>개념은 익히고 NEXT.js로 넘어가자</em></h1>
<hr>
<p><img src="https://velog.velcdn.com/images/minji-kim0524/post/bbf7feaa-ccdb-4f57-9935-7928f1148aba/image.webp" alt=""></p>
<hr>
<h2 id="csr-이란">CSR 이란?</h2>
<p><strong>| Client Side Rendering 즉, 클라이언트 측에서 렌더링하는 것을 의미한다.</strong></p>
<p>브라우저가 자바스크립트를 실행하여 클라이언트에서 화면을 동적으로 렌더링하는 것이다.</p>
<p>전체적인 사이트 <code>구조</code>와 <code>기능</code>을 자바스크립트 코드로 클라이언트에게 전달한 후 브라우저에서 리액트가 직접 화면을 생성하고 상호작용하도록 하는 방식이 <code>CSR</code> 이다.</p>
<p><img src="https://velog.velcdn.com/images/minji-kim0524/post/10ebf585-9f92-48cc-95aa-40460eb11e22/image.png" alt=""></p>
<h3 id="csr-작동방식">CSR 작동방식</h3>
<p>1️⃣ 사용자가 사이트에 접속한다.
2️⃣ 브라우저가 <code>HTML</code>, <code>CSS</code>, <code>JavaScript</code> 파일을 다운로드 한다.
3️⃣ 브라우저가 <code>JavaScript</code> 를 실행하여 화면을 동적으로 만든다.
4️⃣ 이후 페이지 이동 또는 데이터 변경도 모두 브라우저에서 처리한다.</p>
<p><strong><em>⇒ 모든 것은 브라우저에서 이루어진다. ⭐️</em></strong></p>
<h3 id="csr의-장점">CSR의 장점</h3>
<ul>
<li><strong>서버 부담이 적다.</strong>
서버는 정적인 파일만 전달하면 되므로 단순한 작업만 하면된다.</li>
<li><strong>앱처럼 부드러운 화면 전환이 가능하다.</strong>
<code>SPA(Single Page Application)</code> 방식으로 페이지 전체를 리로딩하는 것이 아니라 일부만 변경할 수 있다.</li>
<li><strong>동적 기능 구현에 용이하다.</strong>
사용자와의 <code>상호작용</code>이 많거나, <code>실시간</code> 데이터가 필요한 서비스에 적합하다.</li>
</ul>
<h3 id="csr의-단점">CSR의 단점</h3>
<ul>
<li><strong>초기 로딩이 느릴 수 있다.</strong>
브라우저가 모든 <code>JavaScript</code> 를 받아 실행하므로 첫 화면 로딩이 늦게 보일 수 있다.</li>
<li><strong>SEO(Search Engine Optimization;검색엔진 최적화)에 불리하다.</strong>
검색엔진이 <code>JavaScript</code> 를 실행하지 못하면 콘텐츠를 제대로 인식하지 못할 수 있다.</li>
</ul>
<hr>
<h2 id="ssr-이란">SSR 이란?</h2>
<p><strong>| Server Side Rendering 즉, 서버 측에서 렌더링하는 것을 의미한다.</strong></p>
<p>서버(Node.js 등)가 사전에 <strong>HTML</strong>을 만들어서 브라우저(클라이언트)에게 전달하는 방식이다.</p>
<p>동일한 전체 사이트를 <code>Node.js</code> 에서 작동시킨 후, 이를 <strong>HTML</strong>로 사전 렌더링한다. 
그리고 마지막으로 이를 클라이언트의 리액트(<em>프론트엔드 프레임워크</em>)에서 가져오도록 하는 것이다. </p>
<p><img src="https://velog.velcdn.com/images/minji-kim0524/post/b7903bba-a3b7-40ab-9a0d-0679005fc55e/image.png" alt=""></p>
<h3 id="ssr-작동방식">SSR 작동방식</h3>
<p>1️⃣ 사용자가 사이트에 접속한다.
2️⃣ 서버가 <code>React</code> 등 프레임워크를 실행하여 해당 페이지의 <strong>HTML</strong> 을 생성한다.
3️⃣ 서버는 완성된 <strong>HTML</strong> 을 브라우저로 전달한다.
4️⃣ 브라우저는 전달받은 <strong>HTML</strong> 을 바로 보여준다.
5️⃣ 이후 리액트 프레임워크가 작동하며 인터랙티브한 기능을 제공한다.</p>
<h3 id="ssr의-장점">SSR의 장점</h3>
<ul>
<li><strong>초기 로딩이 빠르다.</strong>
브라우저가 완성된 HTML을 바로 보여주므로 사용자가 내용을 빠르게 볼 수 있다.</li>
<li><strong>SEO(Search Engine Optimization;검색엔진 최적화)에 유리하다.</strong>
검색엔진이 HTML을 쉽게 읽을 수 있기때문에 검색결과 노출에 유리하다.</li>
<li><strong>공유 및 미리보기가 편리하다.</strong>
링크 공유시, 미리보기 이미지와 텍스트가 잘 표시된다.</li>
</ul>
<h3 id="ssr의-단점">SSR의 단점</h3>
<ul>
<li><strong>서버 부하가 크다.</strong>
사용자가 많으면 서버가 HTML을 계속 만들어야 하므로 서버의 <code>성능</code>이 중요하다.</li>
<li>서버와 클라이언트 모두에서 코드를 실행해야 하므로 <code>복잡성</code>이 증가한다.</li>
</ul>
<hr>
<h2 id="csr-vs-ssr">CSR vs. SSR</h2>
<table>
<thead>
<tr>
<th>구분항목</th>
<th>CSR</th>
<th>SSR</th>
</tr>
</thead>
<tbody><tr>
<td>렌더링 위치</td>
<td>브라우저에서 JavaScript로 렌더링</td>
<td>서버에서 HTML 생성</td>
</tr>
<tr>
<td>초기 로딩</td>
<td>JavaScript 실행 후 렌더링하므로 느릴 수 있음</td>
<td>HTML을 바로 표시하므로 빠름</td>
</tr>
<tr>
<td>SEO</td>
<td>좋지않음 ❌</td>
<td>매우 유리함 ✅</td>
</tr>
<tr>
<td>서버 부하</td>
<td>낮음</td>
<td>높음</td>
</tr>
<tr>
<td>복잡성</td>
<td>클라이언트 중심 관리로 복잡성이 낮음</td>
<td>서버/클라이언트 모두 관리가 필요하므로 복잡성이 높음</td>
</tr>
<tr>
<td>예시</td>
<td>React, Vue, Angular SPA</td>
<td>Next.js, Nuxt.js, Vite SSR</td>
</tr>
</tbody></table>
<ul>
<li>각 방식마다 장단점이 있으므로 서비스의 목적과 상황에 맞게 선택해서 활용하는 것이 좋다.</li>
</ul>
<hr>
<p><strong>🔗 참고자료</strong>
<a href="https://nextjs.org/docs/pages/building-your-application/rendering/client-side-rendering">https://nextjs.org/docs/pages/building-your-application/rendering/client-side-rendering</a>
<a href="https://nextjs.org/docs/pages/building-your-application/rendering/server-side-rendering">https://nextjs.org/docs/pages/building-your-application/rendering/server-side-rendering</a>
<a href="https://nextjs.org/docs/app/guides/migrating/from-vite">https://nextjs.org/docs/app/guides/migrating/from-vite</a>
<a href="https://ko.vite.dev/guide/ssr">https://ko.vite.dev/guide/ssr</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[모던 자바스크립트 Deep Dive] 25장. 클래스]]></title>
            <link>https://velog.io/@minji-kim0524/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Deep-Dive-25%EC%9E%A5.-%ED%81%B4%EB%9E%98%EC%8A%A4</link>
            <guid>https://velog.io/@minji-kim0524/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Deep-Dive-25%EC%9E%A5.-%ED%81%B4%EB%9E%98%EC%8A%A4</guid>
            <pubDate>Mon, 22 Sep 2025 02:01:58 GMT</pubDate>
            <description><![CDATA[<h1 id="내가-가지고-있는-속성을-원해-주문super을-외쳐봐">*&quot;내가 가지고 있는 속성을 원해? 주문(super)을 외쳐봐!&quot;*</h1>
<hr>
<h2 id="클래스-정의방법">클래스 정의방법</h2>
<ul>
<li>클래스는 <code>class</code> 키워드를 사용하여 정의한다.</li>
<li>생성자 함수와 마찬가지로 <code>파스칼 케이스</code>를 사용한다. 
(<em>단, 파스칼 케이스를 사용하지 않아도 에러가 발생하지는 않는다</em>🤷🏻‍♀️)</li>
<li>일반적이지는 않지만 <code>표현식</code>으로 정의할 수도 있다. 
⇒ <code>값</code>으로 사용할 수 있는 <code>일급 객체</code> 이다. (<em>즉, 클래스는 함수다.</em>)</li>
</ul>
<pre><code class="language-js">// 기본형태
class Person {}

// 표현식 정의형태
// 익명 클래스 표현식
const Person = class {}

// 기명 클래스 표현식
const Person = class Myclass {}</code></pre>
<h3 id="클래스의-메서드">클래스의 메서드</h3>
<p>클래스 몸체에서는 다음 세 가지의 메서드를 정의할 수 있다.</p>
<ul>
<li>생성자(constructor)</li>
<li>프로토타입 메서드</li>
<li>정적 메서드</li>
</ul>
<p>클래스와 생성자함수의 정의방식은 참 닮았는데 비교해보면 다음과 같다.</p>
<p><img src="https://velog.velcdn.com/images/minji-kim0524/post/cf5f2c5c-4cc2-43d9-a64f-767652b4c412/image.png" alt=""></p>
<hr>
<h2 id="클래스의-호이스팅">클래스의 호이스팅</h2>
<p>앞서 살펴본 것처럼 <strong>클래스는 함수이다.</strong></p>
<p>다음과같이 클래스 선언문으로 정의한 클래스는 함수 선언문처럼 평가 과정 이전에 먼저 평가되어 함수 객체를 생성한다.</p>
<pre><code class="language-js">class Person {}

console.log(typeof Person) // ?</code></pre>
<p>생성된 함수 객체는 생성자 함수로서 호출할 수 있는 <code>생성자(constructor)</code> 이다.
함수 객체 생성시점에 <code>프로토타입</code> 도 더불어 생성이 된다.
이 때, <a href="https://velog.io/@minji-kim0524/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Deep-Dive-19%EC%9E%A5.-%ED%94%84%EB%A1%9C%ED%86%A0%ED%83%80%EC%9E%85#%EB%A6%AC%ED%84%B0%EB%9F%B4-%ED%91%9C%EA%B8%B0%EB%B2%95%EC%97%90-%EC%9D%98%ED%95%B4-%EC%83%9D%EC%84%B1%EB%90%9C-%EA%B0%9D%EC%B2%B4%EC%9D%98-%EC%83%9D%EC%84%B1%EC%9E%90-%ED%95%A8%EC%88%98%EC%99%80-%ED%94%84%EB%A1%9C%ED%86%A0%ED%83%80%EC%9E%85">19장.프로토타입-리터럴 표기법에 의해 생성된 객체의 생성자 함수와 프로토타입</a>  에서도 살펴본 바와같이 <strong><em>생성자 함수와 프로토타입은 단독으로 존재할 수 없고 언제나 쌍으로 존재한다.</em></strong></p>
<p>단, 클래스는 정의 이전에 참조할 수 없다.
그래서 <code>호이스팅</code>이 발생하지 않는 것처럼 보이지만 그렇지 않다. </p>
<p>클래스 선언문도 변수 선언, 함수 정의와 마찬가지로 <code>호이스팅</code>이 발생하는데, <code>let</code>, <code>const</code> 키워드로 선언한 변수처럼 발생한다.</p>
<p><strong>✋🏻 잠깐!</strong> 이게 무슨 소리야? <code>let</code>, <code>const</code> 키워드로 선언된 건 뭐가 다른데?
<code>let</code>, <code>const</code> 키워드로 선언한 변수는 <strong>일시적 사각지대(TDZ; Temporal Dead Zone)</strong> 에 빠지게된다.</p>
<p>즉, 클래스 선언문도 <code>TDZ</code> 에 빠지게 되므로 호이스팅이 발생했지만 하지않은 것처럼 보이는 것이다.</p>
<blockquote>
<p><em>var, let, const, function, class 키워드로 선언된 모든 식별자는 호이스팅 된다. 왜? 모든 선언문은 런타임 이전에 실행되기 때문이다.</em></p>
</blockquote>
<hr>
<h2 id="클래스의-인스턴스-생성">클래스의 인스턴스 생성</h2>
<p><strong>| 클래스는 생성자 함수이며, new 연산자와 함께 호출되어 인스턴스를 생성한다.</strong></p>
<p>클래스의 존재이유는 <strong>인스턴스 생성</strong>이다. 그렇기 때문에 반드시 <code>new</code> 연산자와 함께 호출해야한다.</p>
<pre><code class="language-js">class Person {}

// ❌ 잘못 호출한 경우 → 에러발생
const me = Person()
// TypeError: Class constructor cannot be invoked without &#39;new&#39;

// ✅ 올바르게 호출한 경우
const me = new Person()</code></pre>
<blockquote>
<p><strong><em>인스턴스 생성 조건1. <code>new</code> 연산자로 호출해라</em></strong></p>
</blockquote>
<p>또한, 클래스 표현식으로 정의된 클래스의 경우에는 <code>식별자</code>를 사용해 인스턴스를 생성하지 않고, <code>기명 클래스</code> 표현식의 클래스 이름을 사용해 인스턴스를 생성하면 <strong>에러가 발생</strong>한다. </p>
<pre><code class="language-js">const Person = class MyClass {}

const me = new Person()

console.log(MyClass) // </code></pre>
<p>위와같은 오류가 발생하는 이유는 클래스 표현식에서 사용한 클래스 이름은 <code>외부</code> 코드에서는 접근이 <code>불가능</code>하기 때문이다. (<em>like 기명함수 표현식</em>)</p>
<blockquote>
<p><strong><em>인스턴스 생성 조건2. 클래스를 가리키는 <code>식별자</code>를 사용하여 인스턴스를 생성해라</em></strong></p>
</blockquote>
<hr>
<h2 id="클래스의-메서드-1">클래스의 메서드</h2>
<p>클래스 몸체에서 정의할 수 있는 메서드는 클래스의 정의방법에서도 살펴본 것처럼 세 가지가 있다.</p>
<ul>
<li>생성자(constructor)</li>
<li>프로토타입 메서드</li>
<li>정적 메서드</li>
</ul>
<h3 id="생성자constructor">생성자(constructor)</h3>
<p><strong>| 인스턴스를 생성하고 초기화하기 위한 특수한 메서드이며, 이름을 변경할 수 없다.</strong></p>
<h4 id="클래스-생성자constructor의-특징">클래스 생성자(constructor)의 특징</h4>
<ul>
<li>클래스 내에 <code>최대 한 개</code>만 존재할 수 있다.
(<em>만약 2개 이상의 포함하게되면 문법 에러가 발생한다.</em>)</li>
<li>생략할 수 있다. → 생략시, 빈 constructor가 암묵적으로 정의된다.</li>
<li>프로퍼티 추가로 인스턴스 초기화 생성이 필요하다면 constructor 내부에서 <code>this</code> 에 <code>인스턴스 프로퍼티</code>를 추가한다.
⇒ 인스턴스 초기화를 위해서는 constructor를 생략하면 안된다.</li>
<li>별도의 반환문을 갖지 않아야 한다.
이 부분은 <a href="https://velog.io/@minji-kim0524/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Deep-Dive-17%EC%9E%A5.-%EC%83%9D%EC%84%B1%EC%9E%90-%ED%95%A8%EC%88%98%EC%97%90-%EB%8C%80%ED%95%9C-%EA%B0%9D%EC%B2%B4-%EC%83%9D%EC%84%B1#%EC%83%9D%EC%84%B1%EC%9E%90-%ED%95%A8%EC%88%98%EC%9D%98-%EB%AA%B8body-%EC%97%90%EC%84%9C%EB%8A%94-%EC%96%B4%EB%96%A4-%EC%9D%BC%EC%9D%B4-%EC%9D%BC%EC%96%B4%EB%82%A0%EA%B9%8C">17장.생성자 함수에 대한 객체 생성</a>에서도 살펴보았듯이, 명시적으로 <code>this</code> 가 아닌 다른 값을 반환하는 것은 클래스의 기본 동작을 방해하는 것이기 때문에 반드시 반환문을 생략해야 한다.</li>
</ul>
<h3 id="프로토타입-메서드">프로토타입 메서드</h3>
<p><strong>| 클래스 몸체에서 정의한 메서드는 생성자 함수에 의한 객체 생성 방식과는 다르게 기본적으로 프로토타입 메서드가 된다.</strong></p>
<p>또한, 생성자 함수와 마찬가지로 클래스가 생성한 인스턴스는 프로토타입 체인에 포함된다.</p>
<p>클래스 몸체에서 정의한 메서드는 객체의 프로토타입에 존재하는 프로토타입 메서드가 되며, 객체는 프로토타입 메서드를 상속받아 사용할 수 있다.</p>
<p>프로토타입 체인은 기존의 모든 객체 생성방식(<em>객체 리터럴, 생성자 함수 등</em>), 클래스에 의해 생성된 객체에도 동일하게 적용된다. 
클래스는 생성자 함수의 역할을 하는 즉, 객체를 생성하는 <code>생성자 함수</code>라고 볼 수 있다.</p>
<h3 id="정적-메서드">정적 메서드</h3>
<p><a href="https://velog.io/@minji-kim0524/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Deep-Dive-19%EC%9E%A5.-%ED%94%84%EB%A1%9C%ED%86%A0%ED%83%80%EC%9E%85#%EC%A0%95%EC%A0%81-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0--%EB%A9%94%EC%84%9C%EB%93%9C">19장.프로토타입-정적 프로퍼티/메서드</a> 에서도 살펴보았듯이 <strong>정적 메서드는 인스턴스를 생성하지 않아도 자신의 프로퍼티와 메서드를 가지기 때문에 호출할 수 있는 메서드이다.</strong></p>
<p>생성자 함수에서는 정적 메서드를 다음과 같이 명시적으로 추가를 했다.</p>
<pre><code class="language-js">function Person(name) {
  this.name = name
}

// 정적 메서드 생성
Person.sayHi = function() {
  console.log(&quot;Hi!&quot;)
}

// 정적 메서드 호출
Person.sayHi() // Hi!</code></pre>
<p>하지만, 클래스에서는 간단하게 메서드에 <code>static</code> 키워드만 붙이면 <strong>정적 메서드</strong>(<em>클래스 메서드</em>)가 된다. (<del><em>와웅!!!!</em></del>)</p>
<pre><code class="language-js">class Person {
  constructor(name) {
    this.name = name
  }

  // 정적 메서드 생성
  static sayHi() {
    console.log(&quot;Hi!&quot;)
  }
}

// 정적 메서드 호출
Person.sayHi() // Hi!</code></pre>
<p>클래스는 <code>클래스 선언문</code>이나 <code>클래스 표현식</code>으로 정의가 평가되는 시점에 함수 객체가 된다.
그래서 정적 메서드는 클래스 정의 후, 별다른 인스턴스를 생성하지 않아도 호출 할 수 있다.</p>
<h3 id="프로토타입-메서드-vs-정적-메서드">프로토타입 메서드 vs. 정적 메서드</h3>
<ol>
<li><p>정적 메서드와 프로토타입 메서드는 속해있는 프로토타입 체인이 다르다.</p>
</li>
<li><p>정적 메서드는 클래스로 호출하고 프로토타입 메서드는 인스턴스로 호출한다.</p>
</li>
<li><p>정적 메서드는 인스턴스 프로퍼티를 참조할 수 없는 반면, 프로토타입 메서드는 인스턴스 프로퍼티를 참조할 수 있다.</p>
</li>
</ol>
<blockquote>
<p>위 세 가지 내용을 다음과같이 공식처럼 정리해보았다.</p>
</blockquote>
<ul>
<li><code>static</code>이 앞에 있다 → <code>정적 메서드</code>이다. → 메서드 호출방법: <code>클래스명.메서드()</code></li>
<li><code>static</code>이 앞에 없다 → <code>프로토타입 메서드</code>(instance)이다. → 메서드 호출방법: <code>인스턴스.메서드()</code></li>
<li>메서드 내부에서 인스턴스 프로퍼티를 참조해야 한다. → <code>this</code> 사용</li>
<li>메서드 내부에서 인스턴스 프로퍼티를 참조하지 않아도 된다. → <code>this</code> 미사용</li>
</ul>
<h3 id="클래스에서-정의한-메서드-특징">클래스에서 정의한 메서드 특징</h3>
<p>클래스에서 정의한 메서드는 다음과 같은 특징이 있다.</p>
<ul>
<li><code>function</code> 키워드를 생략한 <code>축약</code> 표현을 사용한다.</li>
<li>객체 리터럴과는 다르게 메서드 정의 시 <code>콤마</code>가 필요없다.</li>
<li>암묵적으로 <code>strict mode</code> 로 실행된다.</li>
<li><code>for...in</code>문 또는 <code>Object.keys</code> 메서드 등으로 열거할 수 없다.</li>
<li>내부 메서드 <code>[[Constructor]]</code> 를 갖지 않으므로, <code>new</code> 연산자로 호출할 수 없다.</li>
</ul>
<hr>
<h2 id="클래스의-인스턴스-생성과정">클래스의 인스턴스 생성과정</h2>
<p>1️⃣ 인스턴스 생성과 this 바인딩</p>
<ul>
<li><code>new</code> 연산자와 함께 클래스 호출 → 암묵적으로 빈 객체 생성(<strong>클래스가 생성한 인스턴스</strong>)</li>
<li>클래스가 생성한 인스턴스의 <code>프로토타입</code> 으로 클래스의 프로토타입 프로퍼티가 가리키는 객체 설정</li>
<li>인스턴스 this에 바인딩
⇒ constructor 내부의 this는 클래스가 생성한 인스턴스를 가리킨다.</li>
</ul>
<p>2️⃣ 인스턴스 초기화</p>
<ul>
<li>constructor 내부코드 실행 → this에 바인딩되어있는 인스턴스 초기화
(<em>constructor가 인수로 전달받은 초기값으로 인스턴스의 프로퍼티 값을 초기화</em>)</li>
<li>단, constructor가 생략되었다면 이 과정은 생략된다.</li>
</ul>
<p>3️⃣ 인스턴스 반환</p>
<ul>
<li>완성된 인스턴스가 this에 바인딩 → this의 암묵적 반환으로 마무리</li>
</ul>
<hr>
<h2 id="프로퍼티">프로퍼티</h2>
<h3 id="인스턴스-프로퍼티">인스턴스 프로퍼티</h3>
<p><strong>| 인스턴스 프로퍼티는 constructor 내부에서 정의해야한다.</strong></p>
<p>constructor 내부에서 this에 추가한 프로퍼티는 언제나 클래스가 생성한 인스턴스의 프로퍼티가 된다.
클래스는 다른 객체지향 언어처럼 <code>접근 제한자</code>(<em>private, public, protected</em>) 를 지원하지 않기때문에 인스턴스 프로퍼티는 언제나 <code>public</code>하다.</p>
<h3 id="접근자-프로퍼티">접근자 프로퍼티</h3>
<p><strong>| 접근자 프로퍼티는 자체적으로는 값을 가지지않고 다른 데이터 프로퍼티의 값을 읽거나 저장할 때 사용하는 접근자 함수로 구성된 프로퍼티이다.</strong></p>
<p>이러한 접근자 프로퍼티는 클래스에서도 사용할 수 있다.</p>
<p>접근자 프로퍼티는 <code>getter</code> 함수와 <code>setter</code> 함수로 구성되어있다.</p>
<h4 id="getter-함수">getter 함수</h4>
<ul>
<li>인스턴스 프로퍼티에 <code>접근</code>할 때마다 프로퍼티 값을 조작하거나 별도의 행위가 필요할 때 사용한다.</li>
<li>메서드 이름 앞에 <code>get</code> 키워드를 사용하여 정의한다.</li>
<li>취득할 때 사용 → 반드시 무언가를 <code>반환</code> 해야한다.</li>
</ul>
<h4 id="setter-함수">setter 함수</h4>
<ul>
<li>인스턴스 프로퍼티에 값을 <code>할당</code>할 때마다 프로퍼티 값을 조작하거나 별도의 행위가 필요할 때 사용한다.</li>
<li>메서드 이름 앞에 <code>set</code> 키워드를 사용하여 정의한다.</li>
<li>할당할 때 사용 → 반드시 <code>매개변수</code>가 있어야한다.
(<em>단 하나의 값만 할당받으므로 하나의 매개변수만 선언할 수 있다.</em>)</li>
</ul>
<pre><code class="language-js">class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName
    this.laseName = lastName
  }

  // getter 함수
  get fullName() {
    return `${this.firstName} ${this.lastName}`
  }

  // setter 함수
  set fullName(name) {
    [this.firstName, this.lastName] = name.split(&#39; &#39;)
  }
}

// ...</code></pre>
<p>접근자 프로퍼티는 호출하는 것이 아니라 참조(<em>getter</em>)하거나 값을 할당(<em>setter</em>)하는 형식으로 사용한다.</p>
<p>기본적으로 클래스의 메서드는 프로토타입 메서드가 된다.
그러므로 클래스의 <code>접근자</code> 프로퍼티도 프로토타입의 프로퍼티가 된다.</p>
<h3 id="클래스-필드">클래스 필드</h3>
<p><strong>| 클래스 필드란 클래스 기반 객체지향 언어에서 클래스가 생성할 인스턴스의 프로퍼티를 가리킨다.</strong></p>
<p>클래스 기반 객체지향 언어에서의 <code>this</code> 는 언제나 클래스가 <strong>생성할 인스턴스</strong>를 가리킨다.</p>
<ul>
<li>자바스크립트의 클래스 <code>몸체</code>에는 <code>메서드</code>만 선언할 수 있다.
하지만, 자바스크립트의 클래스 몸체에서도 <code>클래스 필드</code>를 정의할 수 있도록 제안이 되어있으며, <em>Chrome 72 이상</em> 의 브라우저와 <em>버전 12 이상</em> 의 Node.js는 이 제안내용을 미리 구현해 놓았다.
⇒ 해당 환경에서는 클래스 몸체에 클래스 필드 정의를 에러발생없이 할 수 있다.</li>
</ul>
<p><strong>✋🏻 잠깐!</strong> 하지만, 2025 9월 현재 찾아본 바에 의하면</p>
<p><strong><em>2021년 1월, TC39 프로세스의 stage3에 클래스 필드로 정의할 수 있는 새로운 표준 사양이 제안됐었고, 2025년 9월 현재 mdn에 공식적으로 등록되어 사용가능한 상태이다.</em></strong> 🥳👏🏻👏🏻🎉</p>
<p><a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Classes#field_%EC%84%A0%EC%96%B8">👉🏻클래스 필드 mdn 보러가기</a></p>
<pre><code class="language-js">class Person {
  name = &quot;Lee&quot;
}

const me = new Person()
console.log(me) // Person{name: &quot;Lee&quot;}</code></pre>
<p>이렇게 클래스 몸체에서 클래스 필드를 정의할 때에는 <code>this</code> 에 클래스 필드 바인딩을 해서는 안된다. ❌
<strong><em>⇒ this는 클래스의 constructor 와 메서드 내에서만 유효하다.</em></strong></p>
<ul>
<li>클래스 필드를 <code>참조</code>하는 경우에는 <code>this</code> 를 반드시 사용하여야 한다.
(<em>자바와 같은 클래스 기반 객체지향 언어에서는 생략이 가능하다.</em>)</li>
<li>클래스 필드에 <code>초기값</code> 을 할당하지 않으면 <code>undefined</code> 를 갖는다.</li>
<li>인스턴스 생성시 외부 초기값으로 클래스 필드를 초기화해야 하는 경우에는, <code>constructor 내부</code>에서 클래스 필드를 참조하여 <code>초기화</code>를 진행하여야 한다.</li>
<li><code>함수</code>는 일급 객체이므로 클래스 필드에 <code>할당</code>할 수 있지만 권장하지는 않는다.</li>
</ul>
<h3 id="private-필드">private 필드</h3>
<p><a href="https://velog.io/@minji-kim0524/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Deep-Dive-24%EC%9E%A5.-%ED%81%B4%EB%A1%9C%EC%A0%80#%EC%BA%A1%EC%8A%90%ED%99%94%EC%99%80-%EC%A0%95%EB%B3%B4%EC%9D%80%EB%8B%89">24장.클로저-캡슐화와 정보은닉</a> 에서 <strong><em>&#39;인스턴스 메서드와 프로토타입을 활용하여 private 흉내를 낼 수는 있지만 완전하다고 할 수는 없다.&#39;</em></strong> 이런 내용을 살펴본적이 있다.</p>
<p>클래스도 역시나 접근 제한자를 지원하지 않아 인스턴스 프로퍼티는 항상 <code>public</code> 이다.
즉, 클래드 필드 정의 제안을 사용하더라도 기본적으로 public이기때문에 외부에 노출이 그대로 된다.</p>
<p><code>private</code> 필드의 앞에 <code>#</code> 을 붙여서 사용을 하고, 참조시에도 마찬가지로 <code>#</code> 을 붙여주면 된다.</p>
<pre><code class="language-js">class Person {
  // private 필드 정의
  #name = &#39;&#39;

  constructor(name) {
    this.#name = name
  }
}

const me = new Person(&#39;Lee&#39;)

// 잘못된 참조 ❌
console.log(me.#name) // SyntaxError
// 올바른 참조 ✅
console.log(me.name) // Lee</code></pre>
<p><code>public</code> 필드는 어디서든 참조할 수 있지만, <code>private</code> 필드는 클래스 <code>내부</code> 에서만 참조할 수 있다.</p>
<p>또한, <code>private</code> 필드는 반드시 클래스 몸체에 정의해야하며, 직접 <code>constructor</code> 에 정의하면 에러가 발생한다.</p>
<table>
<thead>
<tr>
<th>접근 가능성</th>
<th>public</th>
<th>private</th>
</tr>
</thead>
<tbody><tr>
<td>클래스 내부</td>
<td>✅</td>
<td>✅</td>
</tr>
<tr>
<td>자식 클래스 내부</td>
<td>✅</td>
<td>❌</td>
</tr>
<tr>
<td>클래스 인스턴스를 통한 접근</td>
<td>✅</td>
<td>❌</td>
</tr>
</tbody></table>
<p><strong>✋🏻 잠깐!</strong> 하지만, 2025 9월 현재 찾아본 바에 의하면</p>
<p><strong><em>2021년 1월, TC39 프로세스의 stage3에 private 필드를 정의할 수 있는 새로운 표준 사양이 제안됐었고, 2025년 9월 현재 mdn에 공식적으로 등록되어 사용가능한 상태이다.</em></strong> 🥳👏🏻👏🏻🎉</p>
<p><a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Classes/Private_elements">👉🏻private 필드 mdn 보러가기</a></p>
<h3 id="static-필드">static 필드</h3>
<p>정적 <code>메서드</code>는 프로토타입 메서드에서 살펴보았듯이 <code>static</code> 을 앞에 붙여서 정의할 수 있다. 
하지만, <code>static</code> 키워드를 사용하여 정적 <code>필드</code> 를 정의할 수는 없었다.</p>
<p>하지만, <code>private</code> 와 마찬가지로 <code>static</code> 또한 
<strong><em>2021년 1월, TC39 프로세스의 stage3에 static 필드를 정의할 수 있는 새로운 표준 사양이 제안됐었고, 2025년 9월 현재 mdn에 공식적으로 등록되어 사용가능한 상태이다.</em></strong> 🥳👏🏻👏🏻🎉</p>
<pre><code class="language-js">class MyMath {
  //static public 필드 정의
  static PI = 22 / 7
  // static private 필드 정의
  static #num = 10

  // static 메서드
  static increment() {
    return ++MyMath.#num
  }
}

console.log(MyMath.PI) // 3.142857142857143
console.log(MyMath.increment()) // 11</code></pre>
<p><a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Classes/static">👉🏻static 필드 mdn 보러가기</a></p>
<hr>
<h2 id="상속에-의한-클래스-확장">상속에 의한 클래스 확장</h2>
<h3 id="클래스-상속과-생성자-함수-상속">클래스 상속과 생성자 함수 상속</h3>
<p>프로토타입 기반 상속은 프로토타입 <code>체인</code>을 통해 상속받는 개념이였지만, <strong>상속에 의한 클래스 확장</strong>은 기존 클래스를 상속받아 새로운 클래스를 <code>확장</code>하여 정의하는 것이다.</p>
<h3 id="extends-키워드">extends 키워드</h3>
<p><strong>| 상속을 통해 클래스를 확장하기 위해서는 <code>extends</code> 키워드를 사용하여 상속받을 클래스를 정의해야한다.</strong></p>
<pre><code class="language-js">// 수퍼(부모/베이스) 클래스 (상속받을 클래스)
class Base {}

// 서브(자식/파생) 클래스
class Derived extends Base {}</code></pre>
<p>수퍼클래스와 서브클래스는 인스턴스의 프로토타입 체인뿐만 아니라 클래스간의 프로토타입 체인도 생성한다. ⇒ 프로토타입 메서드, 정적 메서드 모두 상속이 가능하다.</p>
<h3 id="동적-상속">동적 상속</h3>
<p><code>extends</code> 키워드는 클래스뿐만 아니라 생성자 함수를 상속받아 클래스 확장을 할 수 도 있다.
(<em>단, <code>extends</code> 키워드 앞에는 반드시 클래스가 와야한다.</em>)</p>
<pre><code class="language-js">// 생성자 함수
function Base(a) {
  this.a = a
}

// 생성자 함수를 상속받는 서브 클래스
class Derived extends Base {}</code></pre>
<p><code>extends</code> 키워드 <code>다음</code>에는 <code>[[Constructor]]</code> 내부 메서드를 갖는 함수 객체로 평가될 수 있는 모든 표현식을 사용할 수 있으며, 이를 통해 동적으로 상속받을 대상을 결정할 수 있다.</p>
<h3 id="서브-클래스의-constructor">서브 클래스의 constructor</h3>
<p>앞서 살펴본 바와같이 클래스에서 constructor를 생략하면 다음과 같은 비어있는 constructor가 암묵적으로 정의된다.</p>
<pre><code class="language-js">constructor() {}</code></pre>
<p>이걸 서브 클래스에서 생략을 하게되면 아래와 같은 형태의 constructor가 암묵적으로 정의된다.
(<em>args는 new 연산자와 함께 클래스를 호출할 때 전달한 인수의 리스트이다.</em>)</p>
<pre><code class="language-js">constructor(...args) { super(...args) }</code></pre>
<p>여기서 <code>super()</code> 는 수퍼 클래스의 <code>constructor(super-constructor)</code> 이다.</p>
<h3 id="super-키워드">super 키워드</h3>
<p><strong>| 함수처럼 호출할 수도 있고 this와 같이 식별자처럼 참조할 수 있는 특수한 키워드이다.</strong></p>
<p><code>super</code> 의 동작방식은 다음과 같으며 하나씩 살펴보자.</p>
<ul>
<li><code>super</code> 호출 → 수퍼 클래스의 <code>constructor(super-constructor)</code> 호출</li>
<li><code>super</code> 참조 → 수퍼 클래스의 메서드 호출</li>
</ul>
<h4 id="1️⃣-super-호출">1️⃣ <code>super</code> 호출</h4>
<p>수퍼 클래스의 constructor 내부에서 추가한 프로퍼티를 그대로 갖는 인스턴스를 생성하면 서브 클래스의 constructor 는 생략할 수 있다. (<em>새로 추가되는 프로퍼티가 없기 때문에</em>)</p>
<p>하지만, 수퍼 클래스의 프로퍼티와 서브 클래스에서 추가한 프로퍼티를 갖는 인스턴스를 생성할 때에는 서브 클래스의 constructor 는 생략할 수 없으며, 수퍼 클래스의 constructor에 해당되는(전달해야하는) 인수가 있다면 서브 클래스의 constructor에서 <code>super</code> 를 통해 전달한다.</p>
<p><code>super</code> 를 호출 할때에는 몇 가지 주의사항이 있다.</p>
<ol>
<li>서브 클래스에서 constructor를 생략하지 않는 경우에는 반드시 <code>super</code> 를 호출해야하며, 호출하지 않을 경우 <code>ReferenceError</code> 가 발생한다.</li>
<li>서브 클래스의 constructor에서 <code>this</code> 를 참조하려면 <code>super</code> 를 먼저 참조해야한다. (<em>순서가 있다!</em>)<pre><code class="language-js">class Base {}
</code></pre>
</li>
</ol>
<p>class Derived extends Base {
  constructor() {
    // 잘못된 순서 ❌
    this.a = 1
    super()
  }
}</p>
<p>class Base {}</p>
<p>class Derived extends Base {
  constructor() {
    // 올바른 순서 ✅
    super()
    this.a = 1
  }
}</p>
<pre><code>3. `super`는 반드시 `서브` 클래스의 constructor 에서만 호출해야하며, 서브 클래스가 아닌 다른 클래스에서 호출 시 `SyntaxError` 가 발생한다.

#### 2️⃣ `super` 참조

`super` 를 참조할 때에도 몇 가지 알아둬야할 내용들이 있다.

1. 서브 클래스의 프로토타입 메서드 내에서 사용되는 `super.메서드명` 에서 `메서드명`은 수퍼 클래스의 프로토타입 메서드를 가리킨다.

- `super`참조를 통해 수퍼 클래스의 메서드를 참조하려면, `super`가 바인딩된 객체 (*수퍼 클래스의 prototype 프로퍼티*)에 바인딩된 프로토타입을 참조할 수 있어야한다.
- `super` 참조는 클래스의 전유물이 아니므로 객체 리터럴에서도 사용할 수 있다.

2. 서브 클래스의 정적 메서드 내에서의 `super.메서드명` 에서 `메서드명`은 수퍼 클래스의 정적 메서드를 가리킨다.

```js
class Base {
  static sayHi() {
    return &quot;Hi!&quot;
  }
}

class Derived extends Base {
  static sayHi() {
    // 여기서의 sayHi는 수퍼 클래스의 정적 클래스인 sayHi 를 가리킨다.
    return `${super.sayHi()} how are you doing?`
  }
}

console.log(Derived.sayHi())</code></pre><h3 id="상속-클래스의-인스턴스-생성과정">상속 클래스의 인스턴스 생성과정</h3>
<h4 id="1️⃣-서브-클래스의-super-호출">1️⃣ 서브 클래스의 super 호출</h4>
<p>자바스크립트 엔진은 클래스 평가 시, 수퍼/서브 클래스 구분을 위해 <code>base</code> , <code>derived</code> 를 값으로 갖는 내부 슬롯인 <code>[[ConstructorKind]]</code> 를 갖는다.</p>
<p>클래스를 상속받지 않는 클래스는 내부 슬롯(<code>[[ConstructorKind]]</code>)의 값이 <code>base</code> 로 설정되고, 상속받는 클래스의 내부 슬롯(<code>[[ConstructorKind]]</code>)의 값은 <code>derived</code> 로 설정된다.</p>
<p>서브 클래스는 직접 인스턴스를 생성하지 않고, 수퍼 클래스에게 인스턴스 생성을 위임하여 상속받는다.
<strong>⇒ 서브 클래스의 <code>constructor</code> 에서 꼭 <code>super</code> 로 호출해야한다.</strong>
(<em><code>super</code> 로 호출하지 않으면 인스턴스를 생성할 수 없다.</em>)</p>
<h4 id="2️⃣-수퍼-클래스의-인스턴스-생성과-this-바인딩">2️⃣ 수퍼 클래스의 인스턴스 생성과 this 바인딩</h4>
<p>수퍼 클래스의 constructor 내부의 코드가 실행되기 전에 암묵적으로 빈 객체를 생성하는데, 이 빈 객체가 클래스가 생성한 인스턴스이다.
생성된 인스턴스는 <code>this</code> 에 바인딩된다. ⇒ 수퍼 클래스의 constructor 내부의 <code>this</code> 는 생성된 인스턴스를 가리킨다.</p>
<h4 id="3️⃣-수퍼-클래스의-인스턴스-초기화">3️⃣ 수퍼 클래스의 인스턴스 초기화</h4>
<p>수퍼 클래스의 constructor가 실행되면 <code>this</code> 에 바인딩되어있는 인스턴스가 초기화된다.</p>
<h4 id="4️⃣-서브-클래스-constructor로의-복귀와-this-바인딩">4️⃣ 서브 클래스 constructor로의 복귀와 this 바인딩</h4>
<p><code>super</code> 호출이 종료되면 흐름은 서브 클래스의 constructor 로 돌아온다.</p>
<p><code>super</code> 호출이 종료되면서 반환한 인스턴스가 <code>this</code> 에 바인딩되기 때문에 서브 클래스는 별도의 인스턴스를 생성하지 않고 그대로 사용한다.</p>
<p>이런 흐름상의 이유로 <code>super</code> 가 호출되지 않으면 인스턴스 생성도, <code>this</code> 바인딩도 할 수 없다.
앞서 <strong>super 키워드</strong> 내용을 살펴볼 때, <em>&#39;순서가 있다!&#39;</em> 라고 한 이유가 바로 이때문이다.</p>
<h4 id="5️⃣-서브-클래스의-인스턴스-초기화">5️⃣ 서브 클래스의 인스턴스 초기화</h4>
<p><code>super</code> 호출 이후 서브 클래스의 constructor에 있는 인스턴스의 초기화가 실행된다.</p>
<h4 id="6️⃣-인스턴스-반환">6️⃣ 인스턴스 반환</h4>
<p>클래스의 모든 처리가 완료되면 완성된 인스턴스가 바인딩된 <code>this</code> 의 암묵적 반환이 실행된다.</p>
<hr>
<h2 id="표준-빌트인-생성자-함수-확장">표준 빌트인 생성자 함수 확장</h2>
<p>앞서 살펴봤듯이 <code>extends</code> 키워드 다음에는 클래스가 아니더라도 <code>[[Constructor]]</code> 내부 메서드를 갖는 함수 객체로 평가될 수 있는 모든 표현식을 사용할 수 있다.</p>
<p><code>String</code>, <code>Number</code>, <code>Array</code> 같은 표준 빌트인 객체도 내부 메서드를 갖는 생성자 함수이므로 <code>extends</code> 키워드를 사용하여 확장 할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[멋쟁이사자처럼 부트캠프 프론트엔드 14기/REACT] REACT Action]]></title>
            <link>https://velog.io/@minji-kim0524/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-14%EA%B8%B0REACT-REACT-Action</link>
            <guid>https://velog.io/@minji-kim0524/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-14%EA%B8%B0REACT-REACT-Action</guid>
            <pubDate>Sat, 20 Sep 2025 13:32:57 GMT</pubDate>
            <description><![CDATA[<h1 id="상태관리-내-마니또가-알아서-해줘-😎">*&quot;상태관리? 내 마니또가 알아서 해줘&quot;* 😎</h1>
<hr>
<p>리액트에서 핵심이 되는게 뭘까? 라고 했을때 많은 것들이 있겠지만, <strong>상태관리</strong>가 빠지면 섭섭할 것 같다.</p>
<p>리액트 훅을 소개하면서 가장 먼저 소개한 훅도 <code>useState</code> 였던만큼 상태관리를 빼놓고 리액트를 얘기할 수는 없다.</p>
<p>사용자가 어떤 동작을 했을 때, 요청을 보내고 응답을 받아 처리하며 <em>대기상태, 에러상태 등</em> 을 <code>useState</code> 를 통해 처리해왔다.</p>
<p><code>useState</code>만을 이용해서도 대기, 에러상태를 관리할 수 있다. </p>
<p>그치만 더 복잡한 상태관리를 해야할 때, 개발자가 상태관리까지 신경쓰지않고 로직에만 집중할 수 있도록 나온 것이 바로 <strong>액션(Action)</strong> 이다. (<del><em>🎬레뒤? 액!션</em></del>)</p>
<hr>
<h2 id="액션action-이란">액션(Action) 이란?</h2>
<p><strong>| 대기상태, 에러, 폼, 낙관적 업데이트를 처리하는 비동기 함수를 의미한다.</strong></p>
<p>다음의 폼 예시를 통해 액션의 도입 전/후를 비교해보자.</p>
<pre><code class="language-js">// 액션 도입 전
import { useState } from &#39;react&#39;
import { updateName } from &#39;@/api&#39;

function UpdateName() {
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState(null)
  const [name, setName] = useState(&#39;&#39;)

  // 이벤트 핸들러를 사용하여 처리
  const handleSubmit = async (e) =&gt; {
      e.preventDefault()

    setLoading(true)
    const error = await updateName(name)
    setLoading(false)
    if (error) {
      setError(error)
      return
    }
    redirect(&#39;/path&#39;)
  }

  return (
    &lt;form 
        onSubmit={handleSubmit}
        aria-describedby={error ? &#39;error-message&#39; : undefined}
      &gt;
    // ...
    &lt;/form&gt;
 )
}

// 액션 도입 후
import { useState, useTransition } from &#39;react&#39;
import { updateName } from &#39;@/api&#39;

function UpdateName() {
  const [loading, startTransition] = useTransition()
  const [error, setError] = useState(null)

    // 액션 함수 : 폼 데이터를 받아 비동기 작업 처리
  const handleSubmit = async (formData) =&gt; {
    startTransition(aync () =&gt; {
        const name = formData.get(&#39;name&#39;)
        const error = await updateName(name)

        if (error) {
          setError(error)
          return
        }

        redirect(&#39;/path&#39;)
    })
  }

  return (
    &lt;form 
        action={handleSubmit} // 액션속성에 액션함수 전달
        aria-describedby={error ? &#39;error-message&#39; : undefined}
      &gt;
    // ...
    &lt;/form&gt;
 )
}</code></pre>
<p>비동기 작업 처리시, 직접 상태관리를 해야했던 도입 전과 달리 도입 후에는 폼 데이터를 받아 비동기 작업을 처리한다.</p>
<p>리액트(<del><em>마니또</em></del>)가 자동으로 <strong>로딩, 에러, 완료상태</strong>를 관리해주기때문에 개발자는 로직에만 집중하면 되는 것이다.</p>
<p><strong>폼</strong>을 예시로 한 것은 <strong>액션</strong>이 자주 사용되는 대표적인 경우가 폼(Form)이라는 점을 기억해두자 😉</p>
<h3 id="액션의-특징">액션의 특징</h3>
<ol>
<li>코드가 간결해진다.</li>
</ol>
<p>-별도의 상태관리없이 비동기 작업처리가 가능하다.
2. 로딩/에러/완료 상태관리를 자동으로 한다.
3. 폼, 버튼 등 다양한 곳에서 액션을 쉽게 적용할 수 있다.
4. <code>useTransition</code> 를 함께 사용하여 대기 상태일때도 부드러운 반응을 보여줄 수 있다.
5. 서버 컴포넌트와 연계하여 클라이언트와 서버사이의 작업을 안전하게 처리할 수 있다.</p>
<blockquote>
<p><strong>💡 참고사항</strong> 
데이터 제출을 자동을 관리하는 액션?</p>
</blockquote>
<ul>
<li>대기상태: 요청 시작 시 대기상태 활성화 → 최종 상태 커밋 후 자동 초기화 진행</li>
<li>낙관적 업데이트: <code>useOptimistic</code> 을 통해 요청을 제출하는 동안 즉각적인 피드백 표시가능</li>
<li>에러상태: 요청 실패 시 <code>Error Boundary</code>를 보여주고 자동으로 낙관적 업데이트를 원래 값으로 돌려놓음</li>
<li>폼: <code>&lt;form&gt;</code> 앨리먼트는 <code>action</code> 및 <code>formAction</code> props에 함수전달하는 것을 지원 → props에 함수가 전달되면 기본적으로 액션사용 및 제출 후 폼 자동 초기화 진행</li>
</ul>
<hr>
<p><strong>✋🏻 잠깐!</strong> 그렇다면 왜 제목에서 *&quot;상태관리? 내 마니또가 알아서 해줘&quot;* 라고 했을까?</p>
<p><code>action={formAction}</code>이 나타나는 순간, 리액트는 이 <code>action</code> 을 관심있게 보기시작한다.</p>
<p>그리곤 상태별로 마니또(리액트)는 행동을 개시한다! </p>
<p><strong>1. 로딩상태</strong></p>
<ul>
<li>action 함수가 실행되면 리액트는 <code>비동기 Promise</code> 추적을 시작한다.</li>
<li><strong>Promise</strong>가 <code>pending</code> 이면 <code>ispending=true</code> 로 상태관리를 자동으로 해주고 컴포넌트 리렌더링을 트리거한다.</li>
<li><strong>⇒ 별도의 <code>useState</code> 사용없이 로딩UI 제어가 가능하다.*</strong></li>
</ul>
<p><strong>2. 에러상태</strong></p>
<ul>
<li>action 함수 내에서 에러가 발생하거나 <code>Promise</code>가 <code>reject</code>되면, 리액트가 알아서 <code>에러처리</code>를 해준다.</li>
<li><strong>⇒ action 함수에서 throw를 한다면 <code>ErrorBoundary</code> 연동까지 해주므로 전역 에러처리가 가능하다.*</strong> (<em>try...catch 를 직접 넣지 않아도 됨</em>)</li>
</ul>
<p><strong>3. 완료상태</strong></p>
<ul>
<li><code>Promise</code> 가 <code>resolve</code> 되면 리액트는 반환값을 <code>state</code>에 살포시 넣어준다.</li>
<li><strong>⇒ 해당 값은 자동으로 컴포넌트 리렌더링을 발생시켜 완료UI를 보여주게 된다.*</strong></li>
</ul>
<hr>
<h3 id="정리하자면">정리하자면,</h3>
<p>액션은 비동기 작업을 더 쉽고 깔끔하게 구현할 수 있는 뉴-페이스이다.</p>
<p>폼 요소에 <code>일반 이벤트 핸들러</code>와 <code>액션 함수</code> 가 연결된 경우의 차이점을 살펴보며 이 글을 마무리하겠다.</p>
<p><strong>🎃이벤트 핸들러</strong></p>
<ul>
<li>이벤트 객체를 전달받음</li>
<li>e.preventDefault() 설정</li>
<li>form.reset() 필요</li>
<li>비동기 함수 코드 (상태 업데이트 요청시, 렌더링 우선 순위를 가짐)</li>
</ul>
<p><strong>🎬액션 함수</strong></p>
<ul>
<li>formData 를 전달받음</li>
<li>별도 설정 필요 없음 (자동 브라우저 기본 작동 방지)</li>
<li>별도 설정 필요 없음 (자동 폼 초기화)</li>
<li>자동 트랜지션(transition)처리가 됨 → 렌더링의 우선순위가 낮아짐 → 당장 급하게 처리할 아이가 아님</li>
<li>사용자가 제출할 때 쓰는거라 즉각적인 반응이 일어나지 않아도됨</li>
</ul>
<hr>
<p><strong>🔗 참고자료</strong>
<a href="https://ko.react.dev/blog/2024/12/05/react-19#actions">https://ko.react.dev/blog/2024/12/05/react-19#actions</a>
<a href="https://ko.react.dev/reference/react-dom/components/form">https://ko.react.dev/reference/react-dom/components/form</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[멋쟁이사자처럼 부트캠프 프론트엔드 14기/REACT] use API Function]]></title>
            <link>https://velog.io/@minji-kim0524/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-14%EA%B8%B0REACT-use-API-Function</link>
            <guid>https://velog.io/@minji-kim0524/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-14%EA%B8%B0REACT-use-API-Function</guid>
            <pubDate>Sat, 20 Sep 2025 09:51:14 GMT</pubDate>
            <description><![CDATA[<h1 id="나는야-1인-2역을-해내지">*&quot;나는야 1인 2역을 해내지&quot;*</h1>
<hr>
<p>리액트 훅이라고하면 일반적으로 <code>use-</code> 접미사로 시작을 한다.</p>
<p>그런데 <code>use</code> 만 덩그러니인 것이 나타났다!?
이건 리액트 훅일까 아닐까? 🧐</p>
<hr>
<h2 id="use-란">use 란?</h2>
<p><strong>| <code>Promise</code> 나 <code>Context</code> 와 같은 데이터를 참조하는 <code>React API</code> 이다.</strong></p>
<pre><code class="language-js">// 형태
const value = use(resource)</code></pre>
<h4 id="매개변수">매개변수</h4>
<p><code>resource</code></p>
<ul>
<li>참조하려는 데이터로, 데이터는 <code>Promise</code> 또는 <code>Context</code> 일 수 있다.</li>
<li><code>Promise</code> 일 경우, 항상 같은 <code>Promise</code> 인스턴스를 전달해야한다.
컴포넌트 내부에서 렌더링될 때마다 새로운 <code>Promise</code> 를 생성하게되면, 무한반복에 빠질 수 있다. ☠️</li>
</ul>
<pre><code class="language-js">// 무한반복에 빠지는 경우
import { Suspense } from &#39;react&#39;
import { ErrorBoundary } from &#39;react-error-boundary&#39;
import { Users, Spinner, ErrorFallback } from &#39;@/components&#39;
import { fetchUsers } from &#39;@/api&#39;

export default function App() {
    // ❌ 렌더링마다 새로운 Promise 생성
    const promise = fetchUsers()

    return (
        &lt;ErrorBoundary FallbackComponent={ErrorFallback}&gt;
            &lt;Suspense fallback={&lt;Spinner /&gt;}&gt;
                &lt;Users promise={promise} /&gt;
            &lt;/Suspense&gt;
        &lt;/ErrorBoundary&gt;
    )
}

// 무한반복에 빠지지 않는 경우
import { Suspense, useMemo } from &#39;react&#39;
import { ErrorBoundary } from &#39;react-error-boundary&#39;
import { Users, Spinner, ErrorFallback } from &#39;@/components&#39;
import { fetchUsers } from &#39;@/api&#39;

export default function App() {
    // ✅ 의존성 배열이 빈 배열이므로 최초 한 번만 생성됨
    const promise = useMemo(() =&gt; fetchUsers(), [])

    return (
        &lt;ErrorBoundary FallbackComponent={ErrorFallback}&gt;
            &lt;Suspense fallback={&lt;Spinner /&gt;}&gt;
                &lt;Users promise={promise} /&gt;
            &lt;/Suspense&gt;
        &lt;/ErrorBoundary&gt;
    )
}</code></pre>
<blockquote>
<p><strong>💡 참고사항</strong>
Next.js 서버 컴포넌트는 렌더링이 한 번만 이루어지므로 <code>use</code> 함수로 <code>Promise</code> 를 직접 사용해도 무한반복이 일어나지 않는다. </p>
</blockquote>
<pre><code class="language-js">export default function ServerComponent() {
    // 서버 컴포넌트는 한 번만 렌더링되므로
    // use 함수에서 직접 Promise를 사용해도 안전합니다.
  const data = use(fetchUsers())
  return (...)
}</code></pre>
<h4 id="반환값">반환값</h4>
<p><code>Promise</code> 또는 <code>Context</code> 에서 참조한 값을 반환한다.</p>
<hr>
<h3 id="use의-특징">use의 특징</h3>
<ul>
<li>일반 훅(<code>useState</code>, <code>useEffect</code>)과는 달리 <strong>조건문</strong>, <strong>반복문</strong> 내에서도 사용이 가능하다.</li>
<li>일반 훅처럼 <strong>함수형 컴포넌트</strong> 또는 <strong>커스텀 훅</strong> 내에서만 사용할 수 있다.</li>
<li>컨텍스트 값을 가져올 때도 사용이 가능하여 <code>useContext</code> 처럼 작동할 수 있다. 
(<em>단, <code>useContext</code> 와의 차이점은 조건문내에서 사용할 수 있다는 점이다.</em>)</li>
<li><code>서버 컴포넌트</code> 에서 데이터를 가져올 때는 <code>use</code> 를 사용하기보다는 <code>async</code> 및 <code>await</code> 를 사용하는 것이 좋다. <code>async</code> 및 <code>await</code>는 <code>await</code> 이 호출된 시점부터 렌더링을 시작하지만, <code>use</code> 는 데이터가 리졸브된 이후부터 컴포넌트를 렌더링하기 때문이다.</li>
<li><code>클라이언트 컴포넌트</code> 에서 <code>Promise</code> 를 생성하는 것보다 <code>서버 컴포넌트</code> 에서 생성하여 클라이언트 컴포너느로 전달하는 것이 좋다. 서버 컴포넌트에서는 렌더링이 한 번만 이루어지는 반면 클라이언트 컴포넌트에서는 렌더링할 때마다 <code>Promise</code>가 재생성되기 때문이다.</li>
</ul>
<hr>
<h3 id="use와-suspense--error-boundary">use와 Suspense &amp; Error boundary</h3>
<p><code>use</code>로 <code>Promise</code>를 호출했을 때 <code>Promise</code> 의 상태에 따라 <code>Suspense</code> 와 <code>Error Boundary</code> 는 다음과같이 작동한다.</p>
<ul>
<li><code>pending</code> → <code>Suspense</code> 의 <code>fallback</code> 를 보여준다.</li>
<li><code>fulfilled</code> → <code>resolve</code> 된 값을 반환하여 화면에 렌더링한다.</li>
<li><code>rejected</code> → <code>Error Boundary</code> 로 에러를 전달한다.</li>
</ul>
<p><strong>✋🏻 잠깐!</strong> 그렇다면 <code>Suspense</code> 와 <code>Error Boundary</code>가 없다면 <code>use</code> 로 <code>Promise</code> 를 호출할 수 없는걸까? </p>
<h4 id="no">&quot;No!&quot;</h4>
<p>호출은 할 수 있지만, <code>pending</code> 상태일 때와 <code>rejected</code>상태일 때의 렌더링처리를 어떻게 해야할지 모르는 상태가 되기때문에 <strong>렌더링 멈춤현상</strong>이 발생하거나 <strong>에러</strong>가 발생할 수 있다.
<strong><em>⇒ 안전하게 동작하기 위해서는 <code>Suspense</code> 와 <code>Error Boundary</code>로 감싸주자!</em></strong></p>
<hr>
<h3 id="정리하자면">정리하자면,</h3>
<p>use는 <strong>함수</strong>의 형태를 가진 <strong>훅</strong>의 규칙을 따르고 훅처럼 <strong>동작</strong>하는 API이다. (<em>1인 2역</em>)</p>
<h4 id="즉-함수-형태로-구현된-새로운-훅-이라고-소개할-수-있겠다">즉, *&quot;함수 형태로 구현된 새로운 훅&quot;* 이라고 소개할 수 있겠다.</h4>
<hr>
<p><strong>🔗 참고자료</strong>
<a href="https://ko.react.dev/reference/react/use">https://ko.react.dev/reference/react/use</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[멋쟁이사자처럼 부트캠프 프론트엔드 14기/REACT] useTransition]]></title>
            <link>https://velog.io/@minji-kim0524/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-14%EA%B8%B0REACT-useTransition</link>
            <guid>https://velog.io/@minji-kim0524/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-14%EA%B8%B0REACT-useTransition</guid>
            <pubDate>Thu, 18 Sep 2025 12:25:34 GMT</pubDate>
            <description><![CDATA[<h1 id="🚦나는야-교통정리원">🚦*&quot;나는야 교통정리원&quot;*</h1>
<hr>
<h3 id="예고편-되돌아보기-🎥">예고편 되돌아보기 🎥</h3>
<p><a href="https://velog.io/@minji-kim0524/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-14%EA%B8%B0REACT-What-is-Suspense">Suspense</a> 글의 마무리에서 예고했던 내용을 되돌아보자.</p>
<p>페이지가 로딩될 때 전체적인 레이아웃은 보이는데, 내용은 희미하게 처리되어 보이는 화면을 본 경험이 있을 것이다. (<del><em>있다고 해주세요 제발</em></del>)</p>
<p>이렇게 상태 전환이 급하지 않아서 이미 공개된 컨텐츠를 대체UI(fallback) 로 숨기느니 이전 페이지를 계속 표시하는 것이 좋겠다고 리액트에게 알려주면 리액트는 컴포넌트의 로딩이 완료될 때까지 이전 페이지로 대기한다.</p>
<hr>
<h2 id="usetransition-이란">useTransition 이란?</h2>
<p><strong>| UI 업데이트의 우선순위를 정하고, 중요한 업데이트는 먼저 처리되도록 해주는 훅이다.</strong></p>
<p>즉, 중요한 업데이트가 낮은 우선순위의 업데이트로 인해 멈춤 현상이 발생하지 않고, 먼저 처리되어 UI 업데이트가 진행될 수 있게 해주는 훅이다. (<del><em>서포트 팍팍</em></del>)</p>
<pre><code class="language-js">// 형태
const [isPending, startTransition] = useTransition()</code></pre>
<h4 id="매개변수">매개변수</h4>
<p>어떠한 매개변수도 받지 않는다. ❌</p>
<h4 id="반환값">반환값</h4>
<p>두 개의 항목이 있는 <code>배열</code> 을 반환한다.</p>
<ul>
<li><code>isPending</code> : 대기 중인 Transition의 존재여부를 알려준다.</li>
<li><code>startTransition 함수</code> : 상태 업데이트를 Transition으로 표시하게 해주는 함수이다.</li>
</ul>
<hr>
<h3 id="starttransition">startTransition</h3>
<pre><code class="language-js">// 형태
startTransition(action)</code></pre>
<h4 id="매개변수-1">매개변수</h4>
<p><code>action</code></p>
<ul>
<li>하나 이상의 <code>set</code> 함수를 호출하여 일부 <code>상태</code> 를 업데이트하는 함수</li>
<li>즉시 <code>action</code> 함수를 호출하고, 호출 중 동기적으로 예약된 모든 상태 업데이트를 Transitions 로 표시한다.</li>
<li><code>action</code> 내에서 상태 업데이트 및 사이트 이펙트를 수행할 수 있으며, 페이지에서의 상호작용을 차단하지 않고 백그라운드에서 작업 수행을 한다.</li>
<li>Transition 내에서의 <code>action</code> 은 실행 순서를 보장하지 않는다.</li>
<li>전달되는 함수는 <code>동기식</code> 이여야 한다. → 리액트는 <code>action</code> 함수를 즉시 실행하여, 실행하는 동안 발생하는 모든 state 업데이트를 Transition으로 표시한다.
더 많은 상태 업데이트를 수행하고자 <code>setTimeout</code> 등의 비동기 코드를 사용하면 Transition으로 표시되지 않는다. </li>
<li>Transition은 여러 개의 action을 포함할 수 있으며, Transition이 진행되는 동안 UI도 계속 반응하여 변경된다.</li>
<li>Transitions 로 표시된 상태 업데이트는 non-blocking 방식으로 처리되며, 불필요한 로딩표시가 나타나지 않는다.</li>
</ul>
<blockquote>
<p><strong>💡 권장사항</strong> 
<code>startTransition</code> 내에서 호출되는 모든 콜백의 이름은 <code>action</code> 이거나 <code>Action</code> 접미사를 포함해야한다. <br /></p>
</blockquote>
<pre><code class="language-js">// 리액트 공식문서 예시
function SubmitButton({ submitAction }) {
  const [isPending, startTransition] = useTransition();
  return (
    &lt;button
      disabled={isPending}
      onClick={() =&gt; {
        startTransition(async () =&gt; {
          await submitAction();
        });
      }}
    &gt;
      Submit
    &lt;/button&gt;
  );
}</code></pre>
<h4 id="반환값-1">반환값</h4>
<p>아무것도 반환하지 않는다.</p>
<pre><code class="language-js">// 사용형태
startTransition(action)

// 사용예시
import { useState, useTransition } from &#39;react&#39;

export default function TabComponent() {
  const [tab, setTab] = useState(&#39;posts&#39;)
  const [isPending, startTransition] = useTransition()

  function handleTabChange(nextTab) {
    startTransition(() =&gt; {
      setTab(nextTab)
    })
  }</code></pre>
<h3 id="starttransition의-특징-및-유의사항">startTransition의 특징 및 유의사항</h3>
<ul>
<li>비동기 요청 이후 (<code>await</code> 사용) 상태 업데이트를 Transition으로 표시하기 위해서는 또 다른 <code>startTransition</code> 으로 감싸야한다.
단, <code>action</code> prop 을 노출할 때에는 Transition 내부에서 <code>await</code> 를 사용해야한다. 
이렇게 사용할 경우, 콜백의 동기/비동기 여부에 상관없이 작동할 수 있으며 <code>startTransition</code> 으로 한번 더 감쌀 필요가 없어진다.<pre><code class="language-js">// 잘못된 방법
startTransition(async () =&gt; {
await someAsyncFunction();
// ❌ await 이후에 startTransition을 사용하지 않음
setPage(&#39;/about&#39;);
});
</code></pre>
</li>
</ul>
<p>// 올바른 방법
startTransition(async () =&gt; {
  await someAsyncFunction();
  // ✅ await <em>이후에</em> startTransition을 사용
  startTransition(() =&gt; {
    setPage(&#39;/about&#39;);
  });
});</p>
<pre><code>- `startTransition` 함수는 안정된 식별성을 가지고 있다. → Effect 의존성에서 생략되는 경우가 대부분이며, 포함한다고해도 Effect가 실행되지 않는다.
- `startTransition` 내에서 동기적으로 `setState` 를 호출해야 효과가 반영된다.
```js
startTransition(() =&gt; {
  setPage(&#39;/about&#39;)
})</code></pre><ul>
<li><code>setTimeout</code>, <code>Promise</code> 등 비동기 코드 내에서 호출할 경우에는 transition이 제대로 작동하지 않는다.<pre><code class="language-js">// ❌ 잘못된 방법
startTransition(() =&gt; {
setTimeout(() =&gt; {
  setPage(&#39;/about&#39;)
}, 1000)
}) 
// ✅ 올바른 방법 (수정)
setTimeout(() =&gt; {
startTransition(() =&gt; {
  // ✅ startTransition 호출 *도중* state 설정
  setPage(&#39;/about&#39;);
});
}, 1000);</code></pre>
</li>
</ul>
<hr>
<h3 id="usetransition의-특징-및-유의사항">useTransition의 특징 및 유의사항</h3>
<ul>
<li><code>startTransition</code>으로 감싸면 중요한 UI는 즉시 반응한다.</li>
<li><code>isPending</code>으로 transition 상태(<em>로딩 중</em>) 자동 표시가 가능하다.</li>
<li>해당 state의 <code>set 함수</code> 에 접근할 수 있는 경우에만 Transition으로 감쌀 수 있다. 일부 prop이나 커스텀 훅의 값에 대한 응답에 Transition을 적용하려면 <code>useDeferredValue</code> 를 사용해야 한다.</li>
<li>Transition 으로 표시된 상태 업데이트가 일어나고 있는 도중에 입력을 시작하면 (<em>다른 상태 업데이트 발생!</em>) 진행되고 있던 업데이트는 중단되고 입력 업데이트(<em>new</em>)를 먼저 처리한 후 다시 시작한다. ⇒ <strong><em>우선순위 결정 후 그게 따라 작업 진행</em></strong></li>
<li>입력을 제어하는 state 변수에는 Transition 을 사용할 수 없다. Transition은 non-blocking 이지만, 변경 이벤트에 대한 응답으로 입력을 업데이트하는 것은 동기적으로 이루어져야하기 때문이다.</li>
</ul>
<blockquote>
<p><strong>✋🏻잠깐!</strong> <code>non-blocking</code> 은 무슨 의미일까?
UI가 버벅거리면서 멈추는 일없이 즉각적인 상호작용을 보장하는 것을 의미한다.</p>
</blockquote>
<ul>
<li>데이터 라이브러리와같은 컴포넌트나 훅 내부가 아닌 곳에서 Transition을 해야하는 경우 또는 <code>isPending</code>이 필요없는 경우에는 독립형인 <code>startTransition</code> 을 호출해야 한다.</li>
</ul>
<blockquote>
<p><strong>✋🏻잠깐!</strong> 그럼 <code>useTransition</code> 이랑 <code>startTransition</code> 이랑 어떻게 구분해서 사용하는게 좋은거야? <br /></p>
</blockquote>
<ul>
<li>컴포넌트 <code>내부</code> 에서 transition 상태(<code>isPending</code>)를 UI에 표시해야 하는 경우 → <strong><em><code>useTransition</code>  사용</em></strong></li>
<li>컴포넌트 <code>외부</code> 에서 transition 을 적용하거나 <code>isPending</code> 이 필요없는 경우 → <strong><em><code>startTransition</code> 사용</em></strong> <br />
⇒ <code>startTransition</code> 은 <code>useTransition</code> 없이도 사용가능한 독립성을 가진 <strong>API 함수</strong> 이다. 결국 두 가지 모두 같은 기능(<em>transition 업데이트 표시</em>)를 가지고 있지만, <code>훅</code> 의 형태로 쓰느냐 <code>함수</code> 의 형태로 쓰느냐의 차이이다.</li>
</ul>
<hr>
<h3 id="usetransition-vs-usedeferredvalue">useTransition vs. useDeferredValue</h3>
<table>
<thead>
<tr>
<th></th>
<th>useTransition</th>
<th>useDeferredValue</th>
</tr>
</thead>
<tbody><tr>
<td><strong>용도</strong></td>
<td>상태 업데이트를 낮은 우선순위로 처리 → 중요UI를 먼저 반영</td>
<td>값 자체의 변경을 지연 → 즉각적 UI와 느린 연산분리</td>
</tr>
<tr>
<td><strong>적용상황</strong></td>
<td>탭 전환, 페이지 이동, 대량 데이터 렌더링 등</td>
<td>입력값, 검색창, 필터 등</td>
</tr>
<tr>
<td><strong>UI반응성</strong></td>
<td>중요도 높은 UI와 느린 상태 변화 분리</td>
<td>즉각적 UI와 느린 연산 분리</td>
</tr>
<tr>
<td><strong>로딩상태</strong></td>
<td><code>isPending</code> 제공</td>
<td>직접 비교하여 표시필요 (<code>deferredValue</code> != <code>value</code>)</td>
</tr>
</tbody></table>
<h4 id="usetransition">useTransition</h4>
<ul>
<li>여러 상태 중 &#39;급한 상태&#39; 와 &#39;급하지 않은 상태&#39;를 구분지어야할 때 사용</li>
<li>상태 변화에도 UI가 멈추지 않게 하고 싶을 때 사용</li>
</ul>
<h4 id="usedeferredvalue">useDeferredValue</h4>
<ul>
<li>입력값에 따라 무거운 연산이 수반될 때 사용</li>
<li>값 자체만 지연시키고 싶을 때 사용</li>
</ul>
<h4 id="공통점">공통점</h4>
<ul>
<li>실제로 느린 연산 또는 컴포넌트가 있을 경우에만 적용하는 것이 효과적이다.</li>
<li><code>Suspense</code>, <code>memo</code> 등과 함께 사용하면 더 자연스러운 UX구현이 가능하다. (<del><em>시너지 효과</em></del>)</li>
</ul>
<hr>
<h3 id="정리하자면">정리하자면,</h3>
<h4 id="starttransition-1">startTransition</h4>
<ul>
<li>함수형태이다.</li>
<li>컴포넌트 <code>외부</code> 에서 작동한다.</li>
<li><code>startTransition</code>에 전달한 함수는 지연없이 <code>즉시 실행</code> 된다.</li>
<li><code>useTransition</code> 에 의존하지 않고 독립적이다. ⇒ <strong><em>단독사용가능</em></strong></li>
<li><code>isPending</code> (<em>Transiton 대기여부 추적</em>) 플래그를 제공하지 않는다. ❌</li>
</ul>
<h4 id="usetransition-1">useTransition</h4>
<ul>
<li>훅의 형태이다.</li>
<li>컴포넌트 <code>내부</code> 에서 작동한다.</li>
<li><code>isPending</code> (<em>Transiton 대기여부 추적</em>) 플래그를 제공한다.</li>
</ul>
<hr>
<p><strong>🔗 참고자료</strong>
<a href="https://ko.react.dev/reference/react/useTransition">https://ko.react.dev/reference/react/useTransition</a>
<a href="https://ko.react.dev/reference/react/startTransition">https://ko.react.dev/reference/react/startTransition</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[멋쟁이사자처럼 부트캠프 프론트엔드 14기/REACT] useDeferredValue]]></title>
            <link>https://velog.io/@minji-kim0524/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-14%EA%B8%B0REACT-useDeferredValue</link>
            <guid>https://velog.io/@minji-kim0524/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-14%EA%B8%B0REACT-useDeferredValue</guid>
            <pubDate>Wed, 17 Sep 2025 17:54:11 GMT</pubDate>
            <description><![CDATA[<h1 id="엇박자의-조화로움을-보여줄게">*&quot;엇박자의 조화로움을 보여줄게&quot;*</h1>
<hr>
<h3 id="예고편-되돌아보기-🎥">예고편 되돌아보기 🎥</h3>
<p><a href="https://velog.io/@minji-kim0524/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-14%EA%B8%B0REACT-What-is-Suspense">Suspense</a> 글의 마무리에서 예고했던 내용을 되돌아보자.</p>
<p>검색어를 입력하는 상황에서 &#39;사&#39; 라고 입력하고 다음 글자로 &#39;과&#39;를 입력하는데 지연이 발생했다고 하자.
자동완성기능에 &#39;사&#39;로 시작하는 단어들이 나왔다가 &#39;과&#39;를 입력하니까 &#39;로딩중...&#39; 이라고 렌더링이 됐다면?
사용자는 😲&quot;이게뭐지?&quot; 하며 어리둥절해할 것이고, 결국 사용자경험(UX)도 좋지않을 것이다.</p>
<p>이럴 때는 &#39;사&#39;라고 입력했을 때의 결과. 즉, 이전 값을 유지한채 &#39;과&#39;에 대한 결과값 로딩을 기다리는 것이 사용자경험 측면에서 더 좋은 방향이다.</p>
<hr>
<p>위 예고편의 문제(<em>사용자 경험 저하</em>)를 해결하는 방법 중 하나로 <code>memo</code> 함수로 느리게 렌더링되는 컴포넌트를 감싸는 방법이 있다.</p>
<p>이 방법으로 불필요한 렌더링을 줄일 순 있으나, 입력값이라는 props는 변경되지 않았으므로 여전히 렌더링이 느려 근본적인 해결책이 되기엔 아쉬운 부분이 있다.</p>
<h4 id="이-때-예고했던-훅을-사용하면-즉각적인-업데이트가-필요한-부분과-조금은-느려도-되는-부분을-분리하여-문제를-해결할-수-있다-살펴봅시다"><em>이 때, 예고했던 훅을 사용하면 즉각적인 업데이트가 필요한 부분과 조금은 느려도 되는 부분을 분리하여 문제를 해결할 수 있다!</em> (<del><em>살펴봅시다</em></del>)</h4>
<h2 id="usedeferredvalue란">useDeferredValue란?</h2>
<p><strong>| 일부 UI 업데이트 반응속도를 지연시킬 수 있는 리액트 훅이다.</strong></p>
<pre><code class="language-js">// 형태
const deferredValue = useDeferredValue(value, initialValue?)</code></pre>
<h4 id="매개변수">매개변수</h4>
<p><code>value</code></p>
<ul>
<li>지연시키려는 값</li>
<li>모든 타입을 값으로 가질 수 있다.</li>
</ul>
<p><code>(optional) initialValue</code></p>
<ul>
<li>컴포넌트 초기 렌더링시 사용할 값</li>
<li>옵션 생략시, 초기 렌더링동안은 값을 지연시키지 않는다.</li>
<li>리액트 19버전부터 설정이 가능해졌다. → 초기 렌더링시 <code>undefined</code> 가 아닌 설정한 값으로 시작할 수 있게 되었다.</li>
</ul>
<h4 id="반환값">반환값</h4>
<p><code>currentValue</code></p>
<ul>
<li>초기 렌더링 중 반환된 <code>지연된 값</code>은 사용자가 <code>제공한 값</code>과 <code>동일</code>하다.</li>
</ul>
<p>이러한 <code>useDeferredValue</code> 의 대표적인 사용예시는 다음과 같으며, 이 예시를 생각하면서 내용을 살펴보자.</p>
<pre><code class="language-js">
import { useState } from &#39;react&#39;
import SlowChild from &#39;./slow-child&#39;

export default function App() {
  const [query, setQuery] = useState(&#39;&#39;)

  return (
    &lt;div className=&quot;app&quot;&gt;
      &lt;label htmlFor=&quot;search-input&quot;&gt;
        검색어 입력
      &lt;/label&gt;
      &lt;input
        id=&quot;search-input&quot;
        type=&quot;text&quot;
        value={query}
        onChange={e =&gt; setQuery(e.target.value)}
        placeholder=&quot;검색어를 입력하세요.&quot;
      /&gt;
      &lt;SlowChild query={query} /&gt;
    &lt;/div&gt;
  )
}

// SlowChild.tsx
export default function SlowChild({ query }) {

    // start 변수 컴포넌트가 렌더링될 때 시간(ms) 저장
  const start = performance.now()

  // 현재 시간에서 start 변수 값을 뺀 값이 
  // 100ms보다 작을 동안 아무 작업도 하지 않고 계속 반복
  // 즉, 아무 일도 않고 100ms 동안 CPU를 점유
  while (performance.now() - start &lt; 100) {
    // 성능 저하 시뮬레이션
  }

  return &lt;p&gt;검색어: {query}&lt;/p&gt;
}</code></pre>
<hr>
<h3 id="suspense와의-관계">Suspense와의 관계</h3>
<p>Suspense로 감싼 컴포넌트를 렌더링할 때 새로운 검색어를 입력하면 &#39;로딩 중...&#39;만 보여주었다.</p>
<p>하지만 <code>useDeferredValue</code>와 함께라면, 새 데이터가 준비될 때까지 이전 데이터(<code>지연된 값</code>)를 렌더링할 수 있다.</p>
<blockquote>
<p><strong>✋🏻잠깐!</strong> 그럼 Suspense를 굳이 왜 쓰는거야? <br />
<code>useDeferredValue</code> 의 반환값인 <code>지연된 값</code>과 <code>입력값</code>이 다를 수가 있는데, 이 때는 보여줄 이전 데이터가 없어진다. 
왜냐면 <code>useDeferredValue</code> 훅은 별도의 로딩 상태를 제공하지않기 때문이다.
이럴 때 <em>짜잔-!</em> 하고 보여주려고 <strong>Suspense</strong> 가 든든하게 컴포넌트를 감싸고 대기중인 것이다. (<del><em>마니또 느낌</em></del>)</p>
</blockquote>
<hr>
<h3 id="usedeferredvalue의-특징-및-유의사항">useDeferredValue의 특징 및 유의사항</h3>
<h4 id="1-실제-성능-병목지점-파악하기">1. 실제 성능 병목지점 파악하기</h4>
<ul>
<li>입력과 동시에 렌더링 되는 컴포넌트가 실제로 성능 문제를 일으키는지 <code>개발자도구-Profiler</code> 를 통해서 확인해야한다.</li>
<li>즉시 렌더링 되어야하는 컴포넌트인지 구분지을 수 있어야 한다.</li>
</ul>
<h4 id="2-입력영역과-결과영역-렌더링-분리하기">2. 입력영역과 결과영역 렌더링 분리하기</h4>
<ul>
<li>입력영역은 즉시 업데이트가 되어야하고, 결과를 보여주는 영역은 나중에 보여지게 하는 것이 중요하다. → 대용량 데이터 처리에 매우 효과적 ✨</li>
</ul>
<h4 id="3-ui-우선순위를-자동으로-조정">3. UI 우선순위를 자동으로 조정</h4>
<ul>
<li>훅을 사용하면 <strong>빠른 작업</strong> 과 <strong>느린 작업</strong> 을 구분지어 <strong>우선순위</strong> 를 자동으로 조정한다.</li>
<li>쓰로틀(<em>Throttle</em>) 과 디바운스(<em>Debounce</em>) 를 사용하지 않고도 부드러운 사용자 경험을 만들 수 있다.</li>
</ul>
<blockquote>
<p><strong>✋🏻잠깐!</strong> 쓰로틀(<em>Throttle</em>) 과 디바운스(<em>Debounce</em>)가 뭔데? <br />
<strong><em>곧 내용 업데이트 예정입니다</em></strong></p>
</blockquote>
<h4 id="4-이전-데이터로-렌더링-유지가능">4. 이전 데이터로 렌더링 유지가능</h4>
<ul>
<li>Suspense 와 함께 사용시, 새로운 데이터를 불러오는 동안 &#39;로딩 중...&#39; 대신 이전 데이터를 보여줄 수 있다. → <em>시너지 효과 뿜뿜!</em></li>
<li>데이터의 로딩준비가 완료되면 자동으로 새로운 데이터로 교체된다.</li>
</ul>
<h4 id="5-캐싱전략도-챙기기">5. 캐싱전략도 챙기기</h4>
<ul>
<li>동일 입력값에 대한 <code>캐싱</code>을 적용하면 불필요한 네트워크 요청과 렌더링을 줄이는게 효과적이다. (<em><code>useDeferredValue</code> 은 그 자체로 추가 네트워크 요청 방지를 하지 않는다.</em>) </li>
</ul>
<h4 id="6-effect-일시중단-시키기">6. Effect 일시중단 시키기</h4>
<ul>
<li><code>useDeferredValue</code>  로 인한 백그라운드 리렌더링이 화면에 <code>커밋</code> 될 때까지 <code>Effect</code> 는 실행되지 않는다. 
(<em>백그라운드 리렌더링 일시 중단 → 데이터 로딩 → UI 업데이트 → Effect 실행</em>)</li>
</ul>
<h4 id="7-가장-최근에-제공입력받은-값이-기준">7. 가장 최근에 제공(입력)받은 값이 기준</h4>
<ul>
<li>리액트는 항상 가장 최근에 제공(입력)받은 값을 기준으로 사용한다.
예를 들어, 검색어를 <code>&#39;바&#39; 입력 → &#39;나&#39; 입력 → &#39;나&#39; 지우고 &#39;보&#39; 입력</code>  이렇게 수정입력했다고하면 리액트는 <em>&#39;바나&#39;</em> 까지를 지연된 값으로 저장해놨다가 <em>&#39;나&#39;</em> 를 지우고 <em>&#39;보&#39;</em> 를 입력하는 순간 저장해둔 <em>&#39;바나&#39;</em>  를 지우고 새로운 값인 &#39;바보&#39;로 지연된 값을 업데이트 하는 것이다.</li>
</ul>
<hr>
<h3 id="정리하자면">정리하자면,</h3>
<p><strong>1. <code>useDeferredValue</code> 의 사용목적은 <code>분리</code> 이다.</strong></p>
<ul>
<li>즉각적으로 반응하길 원하는 값과 잠시 대기했다가 보여주길 원하는 값의 분리</li>
</ul>
<p><strong>2. <code>memo</code> 와 같이 사용해야 최적화가 이루어진다.</strong></p>
<ul>
<li><code>memo</code> 로 감싸지 않을 경우, 입력값이 변경될 때마다 부모 컴포넌트가 리렌더링 되어야 한다.</li>
<li><code>props</code> 변경까지 <code>memo</code> 가 캐치해줄 수는 없지만, <code>memo</code> 마저 없으면 최적화의 취지가 무색해진다.</li>
</ul>
<hr>
<p><strong>🔗 참고자료</strong>
<a href="https://ko.react.dev/reference/react/useDeferredValue">https://ko.react.dev/reference/react/useDeferredValue</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[멋쟁이사자처럼 부트캠프 프론트엔드 14기/REACT] React.lazy]]></title>
            <link>https://velog.io/@minji-kim0524/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-14%EA%B8%B0REACT-React.lazy</link>
            <guid>https://velog.io/@minji-kim0524/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-14%EA%B8%B0REACT-React.lazy</guid>
            <pubDate>Mon, 15 Sep 2025 17:18:15 GMT</pubDate>
            <description><![CDATA[<h1 id="내가-필요한-순간까지-좀-늘어져있어볼까아-🥱">*&quot;내가 필요한 순간까지 좀 늘어져있어볼까아&quot;* 🥱</h1>
<hr>
<p>볼륨이 큰 웹 앱을 만들어가다보면, 컴포넌트도 점점 많아지고 자연스레 코드도 커지게 된다.
이 때, 모든 코드를 빠짐없이 한 번에 다운로드하게되면 페이지 하나를 여는 데도 오랜 시간이 걸리게 된다.</p>
<p>그렇게되면 불필요한 자원낭비와 느려진 로딩속도만 남는다...(<del><em>덩그러니</em></del>)</p>
<p>그렇다면 다양한 컴포넌트를 페이지기능별로 필요할 때만 불러와서 코드를 다운로드하면 어떨까?</p>
<h4 id="nice">&quot;Nice&quot;</h4>
<p>이렇게 페이지기능별로 코드를 불러올 수 있게 하기위한 작업이 <strong>&quot;코드 분할&quot;</strong> 이다.</p>
<h3 id="코드-분할의-장점">코드 분할의 장점</h3>
<ol>
<li>불필요한 코드 다운로드 방지</li>
<li>빠른 속도의 초기 로딩</li>
<li>네트워크 효율성 증가: 사용자의 니즈에 맞춰 필요한 코드만 서버에서 받아온다.</li>
</ol>
<hr>
<h2 id="svg-xmlnshttpwwww3org2000svg-width24-height24-viewbox0-0-24-24-fillnone-strokecurrentcolor-stroke-width2-stroke-linecapround-stroke-linejoinround-classlucide-lucide-hourglass-icon-lucide-hourglasspath-dm5-22h14path-dm5-2h14path-dm17-22v-4172a2-2-0-0-0-586-1414l12-12l-4414-4414a2-2-0-0-0-7-17828v22path-dm7-2v4172a2-2-0-0-0-586-1414l12-12l4414-4414a2-2-0-0-0-17-6172v2svg-lazy-란"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-hourglass-icon lucide-hourglass"><path d="M5 22h14"/><path d="M5 2h14"/><path d="M17 22v-4.172a2 2 0 0 0-.586-1.414L12 12l-4.414 4.414A2 2 0 0 0 7 17.828V22"/><path d="M7 2v4.172a2 2 0 0 0 .586 1.414L12 12l4.414-4.414A2 2 0 0 0 17 6.172V2"/></svg> lazy 란?</h2>
<p><strong>| 컴포넌트가 처음 렌더링될 때까지 해당 컴포넌트의 코드로딩을 지연시키는 기능이다.</strong></p>
<pre><code class="language-js">// 형태
import { lazy } from &#39;react&#39;

const SomeComponent = lazy(load)</code></pre>
<p>코드를 로딩시키는 <code>lazy</code> 는 컴포넌트 <strong>외부</strong>에서 호출하여 지연로딩된 컴포넌트를 선언하여 사용한다.</p>
<h4 id="매개변수">매개변수</h4>
<p><code>load</code> </p>
<ul>
<li>Promise 또는 thenable(<em><code>then</code> 메서드가 있는 Promise 유사객체</em>)을 반환하는 함수이다.</li>
<li>매개변수를 받지 않는다.</li>
<li>반환값인 컴포넌트를 처음 렌더링할 때까지 <code>load</code> 를 호출하지 않는다.</li>
<li>호출 후 해결된 값의 <code>default</code> 를 컴포넌트로 렌더링한다.</li>
<li>반환된 값은 <code>캐시</code> 되므로, 리액트는 <code>load</code> 를 한 번만 호출한다.</li>
<li>Promise가 거부될 경우, 가장 가까운 <code>에러 바운더리</code>가 처리하도록 에러를 <code>throw</code> 한다.</li>
</ul>
<h4 id="반환값">반환값</h4>
<ul>
<li>트리에 렌더링할 수 있는 <code>React 컴포넌트</code> 를 반환한다.</li>
<li>컴포넌트 코드가 로딩되는 동안 렌더링을 시도하면 반환이 일시중지되며, 이 때 로딩상태를 표싷려면 앞에서 살펴본 <code>Suspense</code> 를 사용하면 된다.</li>
<li><code>React.lazy</code> 로 로드되는 컴포넌트는 <strong>반드시</strong> <code>Suspense</code> 로 감싸져야 한다.</li>
</ul>
<pre><code class="language-js">import { lazy, Suspense, useState } from &#39;react&#39;

// 댓글 컴포넌트를 lazy로 가져옴
const Comments = lazy(() =&gt; import(&#39;./Comments&#39;))

function Article() {
  const [showComments, setShowComments] = useState(false)

  return (
    &lt;div&gt;
      &lt;h1&gt;게시글 내용&lt;/h1&gt;
      &lt;button type=&quot;button&quot; onClick={() =&gt; setShowComments(true)}&gt;
        댓글 보기
      &lt;/button&gt;
      {showComments &amp;&amp; (
        &lt;Suspense fallback={&lt;p role=&quot;status&quot;&gt;로딩 중...&lt;/p&gt;}&gt;
          &lt;Comments /&gt; // `React.lazy` 로 로드되는 컴포넌트 → `Suspense` 품으로~
        &lt;/Suspense&gt;
      )}
    &lt;/div&gt;
  )
}</code></pre>
<p>위 예시가 작동하는 방식은 다음과 같다.</p>
<ol>
<li>사용자가 <code>댓글 보기</code> 버튼을 누르기 전까지는 <code>Comments</code> 컴포넌트가 다운로드 되지 않는다.</li>
<li>버튼을 눌러야 네트워크를 통해 코드 다운로드가 진행된다.</li>
<li>코드 로딩이 준비되는 동안 <code>Suspense</code>  의 <code>fallback</code> 이 실행된다. (<del><em>환상의 궁합</em></del>)</li>
</ol>
<blockquote>
<p><strong><code>lazy</code> 와 <code>Suspense</code> 는 시너지 효과를 낸다.</strong> <svg xmlns="http://www.w3.org/2000/svg" width="20" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-zap-icon lucide-zap"><path d="M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z"/></svg></p>
</blockquote>
<hr>
<h3 id="reactlazy-사용시-주의사항">React.lazy 사용시 주의사항</h3>
<ul>
<li><code>lazy</code> 컴포넌트는 다른 컴포넌트 내부에서 선언해서는 안된다. → 항상 모듈 최상위에서 선언해야한다.
다음과 같이 다른 컴포넌트 내부에서 선언할 경우, 리렌더링시 모든 상태가 재설정되는 상황이 발생한다.<pre><code class="language-js">// ❌ 잘못된 방법: 이렇게 하면 다시 렌더링할 때 모든 상태가 재설정된다.
import { lazy } from &#39;react&#39;;
</code></pre>
</li>
</ul>
<p>function Editor() {
  const MarkdownPreview = lazy(() =&gt; import(&#39;./MarkdownPreview.js&#39;));
  // ...
}</p>
<p>// ✅ 올바른 방법: <code>lazy</code> 컴포넌트를 컴포넌트 외부에 선언한다.
import { lazy } from &#39;react&#39;;</p>
<p>const MarkdownPreview = lazy(() =&gt; import(&#39;./MarkdownPreview.js&#39;));</p>
<p>function Editor() {
  // ...
}</p>
<pre><code>- 기본 내보내기(`export default`) 만 지원하기때문에 기본 내보내기로 했을 때에만 `lazy` 적용시 작동이 된다.
기본 내보내기가 아닌 이름으로 내보내기를 할 경우, 가능은 하나 다음과같이 한 번 더 감싸야하는 번거로운 작업이 추가되므로 권장하지 않는다.

```js
const Comments = React.lazy(() =&gt;
  import(&#39;./CommentsModule&#39;).then((module) =&gt; ({
    default: module.Comments
  }))
)</code></pre><hr>
<h3 id="정리하자면">정리하자면,</h3>
<ul>
<li><code>React.lazy</code> 와 <code>Suspense</code> 를 적절히 사용하면 초기 로딩 속도 개선과 네트워크 효율성을 향상시킬 수 있다.</li>
<li><code>Suspense</code> 는 감싸는 방식에따라 UI로딩순서를 달리할 수 있으므로 사용자 경험에 맞춘 설계를 해야한다.</li>
<li>에러 처리와 <code>export default</code> 규칙에 유의해야한다.</li>
</ul>
<hr>
<p><strong>🔗 참고자료</strong>
<a href="https://ko.react.dev/reference/react/lazy">https://ko.react.dev/reference/react/lazy</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[멋쟁이사자처럼 부트캠프 프론트엔드 14기/REACT] What is Suspense?]]></title>
            <link>https://velog.io/@minji-kim0524/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-14%EA%B8%B0REACT-What-is-Suspense</link>
            <guid>https://velog.io/@minji-kim0524/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BC-%EB%B6%80%ED%8A%B8%EC%BA%A0%ED%94%84-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-14%EA%B8%B0REACT-What-is-Suspense</guid>
            <pubDate>Mon, 15 Sep 2025 16:07:01 GMT</pubDate>
            <description><![CDATA[<h1 id="나를-거쳐야-진짜를-볼-수-있어">*&quot;나를 거쳐야 진짜를 볼 수 있어&quot;*</h1>
<hr>
<p>기존에는 로딩상태표시와 에러표시를 해야할 때 <code>상태 변수</code>를 사용하였다.</p>
<p><code>useState</code> 를 활용하여 진행을 해도 문제는 없다.
하지만, 관리해야하는 로딩 및 에러상태가 많아진다면 코드가 길어져 가독성이 떨어질 것이다.</p>
<p>이 때, 리액트에서 기본적으로 제공하는 <strong>Suspense</strong> 를 사용하면 컴포넌트 내에서 로딩 상태를 관리할 필요가 없어진다.</p>
<h2 id="svg-xmlnshttpwwww3org2000svg-width24-height24-viewbox0-0-24-24-fillnone-strokecurrentcolor-stroke-width2-stroke-linecapround-stroke-linejoinround-classlucide-lucide-loader-icon-lucide-loaderpath-dm12-2v4path-dm162-78-29-29path-dm18-12h4path-dm162-162-29-29path-dm12-18v4path-dm49-191-29-29path-dm2-12h4path-dm49-49-29-29svg-suspense란"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-loader-icon lucide-loader"><path d="M12 2v4"/><path d="m16.2 7.8 2.9-2.9"/><path d="M18 12h4"/><path d="m16.2 16.2 2.9 2.9"/><path d="M12 18v4"/><path d="m4.9 19.1 2.9-2.9"/><path d="M2 12h4"/><path d="m4.9 4.9 2.9 2.9"/></svg> Suspense란?</h2>
<p><strong>| <code>Suspense</code> 가 감싸고 있는 자식 요소가 로드되기 전 화면에 대체 UI를 보여주는 로딩상태만 처리하는 기능이다.</strong></p>
<pre><code class="language-js">// 형태
import { Suspense } from &#39;react&#39;

&lt;Suspense fallback={...}&gt;
    &lt;SomeComponent /&gt;
&lt;/Suspense&gt;</code></pre>
<h3 id="속성props">속성(Props)</h3>
<p><strong><code>fallback</code></strong></p>
<ul>
<li>실제 UI가 로딩되기 전까지 렌더링되는 대체 UI이다.</li>
<li>보통 로딩 스피너, 스켈레톤과같은 간단한 <code>placeholder</code> 를 활용한다.</li>
<li><code>fallback</code> 렌더링이 지연될 경우에는 가장 가까운 부모 Suspense 기능이 활성화된다.</li>
</ul>
<h3 id="활성화-조건">활성화 조건</h3>
<p>기본적으로 비동기 데이터 요청시에 활성화가 된다.</p>
<ul>
<li>Suspense가 가능한 프레임워크(<em>Relay, Next.js</em>)를 사용한 데이터를 가져올 때</li>
<li><code>lazy</code> 를 활용한 지연 로딩 컴포넌트에 사용될 때</li>
<li><code>use</code> 를 사용한 캐시된 Promise 값을 읽을 때</li>
</ul>
<p><code>Suspense</code> 는 Effect, 이벤트 핸들러 내부에서 가져오는 데이터는 감지하지 않는다.</p>
<p><code>Suspense</code> 의미를 보면 <strong>로딩 상태만</strong> 처리를 한다고 되어있다.
그렇다면 에러상태는 어떻게 처리해야하는 걸까?
바로 이전에 살펴봤던 <a href="https://velog.io/@minji-kim0524/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BCREACT-Error-Boundary">에러 바운더리</a> 에서 처리한다.</p>
<h3 id="suspense-장점">Suspense 장점</h3>
<ol>
<li>로딩/에러 상태관리가 간편해진다. ⇒ 컴포넌트에서 직접 상태관리할 필요가 없다.</li>
<li>컴포넌트 코드가 단순해진다.</li>
<li>로딩과 에러 UI를 나누어 관리할 수 있다. ⇒ 코드가 깔끔하고 직관적이게 정리된다.</li>
<li>state 또는 props가 변경될 때도 자동으로 <code>fallback</code> UI 가 표시된다. ⇒ 항상 최신 데이터가 준비될 때까지 로딩 UI를 보여준다.</li>
</ol>
<h3 id="알아두어야할-점">알아두어야할 점</h3>
<ul>
<li><code>Suspense</code> 는 어떤 상황에서든지 작동하는 것이 아니다. ⇒ <code>Suspense</code> 를 지원하는 라이브러리나 프레임워크에서만 작동한다. (<em>Next.js, TanStack Query 등</em>)</li>
<li><code>fetch</code> 로 데이터를 불러오는 경우에는 동작하지 않는다.</li>
</ul>
<hr>
<p>기본적으로 Suspense 로 감싸져있는 내부트리는 하나의 단위로 취급된다.
즉, 여러 컴포넌트 중 하나라도 지연되면 로딩상태 대체 UI가 렌더링된다. (<del><em>우리는 하나!</em></del>)</p>
<pre><code class="language-js">&lt;Suspense fallback={&lt;Loading /&gt;}&gt;
  &lt;Biography /&gt; // 로딩완료 → Albums가 로딩완료 되기전까지는 같이 지연상태.
  &lt;Panel&gt;
    &lt;Albums /&gt; // 로딩지연 → Suspense 작동!
  &lt;/Panel&gt;
&lt;/Suspense&gt;</code></pre>
<p>🤔 그런데 이건 너무 비효율적이라는 생각이 든다.
<code>Biography</code>컴포넌트는 데이터 로딩이 완료되었는데도 <code>Albums</code>컴포넌트가 지연되었다고 같은 취급을 받아야 하는 것이다. (<del><em>억울해!!!</em></del>)</p>
<p><code>Biography</code>컴포넌트의 억울함을 풀어줄 방법이 있다.</p>
<h3 id="suspense-중첩사용">Suspense 중첩사용</h3>
<p>컴포넌트가 지연(일시 중단)이 되면 가장 가까운 상위 Suspense 컴포넌트의 fallback이 활성화된다.
이 특징을 활용하면 Suspense를 중첩하여 컴포넌트 로딩순서를 만들 수 있는 것이다.</p>
<p>공식문서에 나와있는 다음 예시를 통해 확인해보자.</p>
<pre><code class="language-js">&lt;Suspense fallback={&lt;BigSpinner /&gt;}&gt;
  &lt;Biography /&gt;
  &lt;Suspense fallback={&lt;AlbumsGlimmer /&gt;}&gt;
    &lt;Panel&gt;
      &lt;Albums /&gt;
    &lt;/Panel&gt;
  &lt;/Suspense&gt;
&lt;/Suspense&gt;</code></pre>
<p>위 예시에서 <code>Biography</code> 컴포넌트는 이제 <code>Albums</code> 컴포넌트의 데이터 로딩이 완료될 때까지 기다리지 않아도 된다.</p>
<p>작동하는 순서는 다음과 같다.</p>
<ol>
<li><code>Biography</code> 가 로딩되지 않은 경우 → <code>fallback(BigSpinner)</code> 실행</li>
<li><code>Biography</code> 로딩완료 → <code>fallback(BigSpinner)</code> 이 실 컨텐츠로 대체</li>
<li><code>Albums</code> 가 로딩되지 않은 경우 → <code>fallback(AlbumsGlimmer)</code> 실행</li>
<li><code>Albums</code> 로딩완료 → <code>fallback(AlbumsGlimmer)</code>이 실 컨텐츠로 대체되고 마무리.</li>
</ol>
<p>이렇게 <code>Suspense</code> 의 중첩을 활용하면 컴포넌트의 로딩 순서를 정할 수 있다.</p>
<hr>
<h3 id="두-둥-예고편-🎥"><em>두-둥</em> 예고편 🎥</h3>
<p>항상 suspense를 사용하는 것이 옳은 것은 아니다.</p>
<p>1️⃣ 검색어를 입력하는 상황에서 &#39;사&#39; 라고 입력하고 다음 글자로 &#39;과&#39;를 입력하는데 지연이 발생했다고 하자.
자동완성기능에 &#39;사&#39;로 시작하는 단어들이 나왔다가 &#39;과&#39;를 입력하니까 &#39;로딩중...&#39; 이라고 렌더링이 됐다면?
사용자는 😲*&quot;이게뭐지?&quot;* 하며 어리둥절해할 것이고, 결국 사용자경험(UX)도 좋지않을 것이다.</p>
<p>이럴 때는 &#39;사&#39;라고 입력했을 때의 결과. 즉, 이전 값을 유지한채 &#39;과&#39;에 대한 결과값 로딩을 기다리는 것이 사용자경험 측면에서 더 좋은 방향이다.</p>
<blockquote>
<p><strong>예고편1. 이 때 사용하는 것이 다음에 소개할 <code>useDeferredValue</code> 훅(<em>지연된 값</em>) 이다.</strong></p>
</blockquote>
<p>2️⃣ 페이지가 로딩될 때 전체적인 레이아웃은 보이는데, 내용은 희미하게 처리되어 보이는 화면을 본 경험이 있을 것이다. (<del><em>있다고 해주세요 제발</em></del>)</p>
<p>이렇게 상태 전환이 급하지 않아서 이미 공개된 컨텐츠를 대체UI(<code>fallback</code>) 로 숨기느니 이전 페이지를 계속 표시하는 것이 좋겠다고 리액트에게 알려주면 리액트는 컴포넌트의 로딩이 완료될 때까지 이전 페이지로 대기한다.</p>
<blockquote>
<p><strong>예고편2. 이 때 사용하는 것이 다음에 소개할 <code>useTransition</code> 훅(<em>UI우선순위 조정</em>) 이다.</strong></p>
</blockquote>
<hr>
<p><strong>🔗 참고자료</strong>
<a href="https://ko.react.dev/reference/react/Suspense">https://ko.react.dev/reference/react/Suspense</a>
<a href="https://ko.react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary">https://ko.react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[모던 자바스크립트 Deep Dive] 24장. 클로저]]></title>
            <link>https://velog.io/@minji-kim0524/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Deep-Dive-24%EC%9E%A5.-%ED%81%B4%EB%A1%9C%EC%A0%80</link>
            <guid>https://velog.io/@minji-kim0524/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Deep-Dive-24%EC%9E%A5.-%ED%81%B4%EB%A1%9C%EC%A0%80</guid>
            <pubDate>Sat, 13 Sep 2025 10:29:49 GMT</pubDate>
            <description><![CDATA[<h1 id="내가-널-기억할게">*&quot;내가 널 기억할게&quot;*</h1>
<hr>
<div align="center"><img src="https://velog.velcdn.com/images/minji-kim0524/post/a65d498f-7010-479d-a3b7-77e4ec55542e/image.jpg" width=400></div>

<p><em>영화 코코(coco)를 아시나요?</em></p>
<p><em>영화 속 수많은 장면들 중에 사진과 같은 장면이 있다. <strong>클로저</strong> 에 대해 알아보기 전에 이 사진을 기억하고서 시작해보면 좋을 것 같아 사진을 넣어보았다.</em> 
<em>사진을 기억하며 클로저에 대해서 알아보자!</em> (<del><em>remember me 🎶</em></del>)</p>
<p><img src="https://velog.velcdn.com/images/minji-kim0524/post/71167e2e-a393-46a7-aa8c-3eb9d4d8c011/image.png" alt=""></p>
<hr>
<p>클로저에 대해 이해하기위해 이전에 다뤘던 내용들은 다시 살펴보자.</p>
<p>1️⃣ 13장.스코프에서 <code>렉시컬 스코프</code>에 대해서 다음 내용을 살펴보았다.</p>
<h4 id="함수를-어디서-호출했는지가-아니라-어디서-정의했는지에-따라-상위-스코프를-결정한다">*&quot;함수를 어디서 호출했는지가 아니라 어디서 정의했는지에 따라 상위 스코프를 결정한다.&quot;*</h4>
<p>즉, 함수의 상위 스코프는 함수를 정의한 위치에 의해 정적으로 결정이 되고난 이후에는 변하지 않는다.</p>
<p>2️⃣ 23장.실행컨텍스트에서 <code>스코프 체인</code>에 대해서 다음 내용을 살펴보았다.</p>
<h4 id="스코프의-실체는-실행-컨텍스트의-렉시컬-환경이며-렉시컬-환경은-자신의-외부-렉시컬-환경에-대한-참조를-통해-상위-렉시컬-환경과-연결된다">*&quot;스코프의 실체는 실행 컨텍스트의 렉시컬 환경이며, 렉시컬 환경은 자신의 &#39;외부 렉시컬 환경에 대한 참조&#39;를 통해 상위 렉시컬 환경과 연결된다.&quot;*</h4>
<p>위 두 가지 내용을 종합하여 렉시컬 스코프에 대한 개념을 다음과 같이 잡아두고 클로저에 대해서 알아보자.</p>
<blockquote>
<p><strong>상위 스코프에 대한 참조는 함수 정의가 평가되는 시점에 함수가 정의된 환경에 의해 결정된다.</strong></p>
</blockquote>
<hr>
<h3 id="함수-객체의-내부-슬롯-environment">함수 객체의 내부 슬롯, [[Environment]]</h3>
<p>함수가 정의되는 환경과 호출되는 환경은 다를 수 있다.</p>
<p>그럼 호출되는 환경이 정의된 환경과 다르더라도 정의된 환경에 대해 기억을 해야하지 않을까?</p>
<p>⇒ 정의된 환경에 대해 기억하는 공간이 바로 함수 자신의 <strong>내부 슬롯 [[Environment]]</strong> 이다.</p>
<p>더 자세히 살펴보자면, 함수 정의가 평가되어 함수 객체를 생성할 때 자신이 정의 된 환경에 의해 결정된 상위 스코프의 참조를 함수 객체 자신의 <strong>내부 슬롯 [[Environment]]</strong> 에 저장한다. 
이 때, 내부슬롯에 저장되는 상위 스코프의 참조는 바로 <em>현재 실행 중인 실행 컨텍스트의 렉시컬 환경</em> 인 셈이다.</p>
<p>함수 정의가 평가되어 함수 객체를 생성하는 시점은 상위 함수가 평가 또는 실행되고 있는 시점이고, 이 때 실행 중인 실행 컨텍스트는 상위 함수의 실행 컨텍스트이기 때문이다.</p>
<blockquote>
<p>정리해보자면,
<strong>함수 객체의 내부 슬롯에 저장된 현재 실행 중인 실행 컨텍스트의 렉시컬 환경의 참조는 상위 스코프이며, 자신이 호출되었을 때 생성될 &#39;외부 렉시컬 환경에 대한 참조&#39;에 저장될 참조값이기도 하다.
함수 객체는 내부 슬롯에 저장한 상위 스코프를 자신이 존재하는 한 기억하는 것이다. (<del><em>like coco</em></del>)</strong></p>
</blockquote>
<p>관련하여 예시로 한번 더 살펴보자.</p>
<pre><code class="language-js">const x = &#39;squirrel&#39;

function moo() {
  const x = &#39;minji&#39;
  hoo() // hoo() 호출위치 → 정의된 위치의 렉시컬 환경에서의 기억을 소환(squirrel)
}

function hoo() {
  // hoo() 정의위치(전역) → 전역 렉시컬 환경 기억(squirrel)
  console.log(x)
}

moo() // squirrel
hoo() // squirrel</code></pre>
<hr>
<h3 id="클로저와-렉시컬-환경">클로저와 렉시컬 환경</h3>
<pre><code class="language-js">const x = &#39;squirrel&#39;

function outer() {
  const x = &#39;minji&#39;
  const inner = function () {console.log(x)}
  return inner
}
// 위 함수가 실행이 되고 outer() 함수는 제 역할을 다 했기 때문에
// 스택에서 pop 되어 사라짐

const innerFunc = outer() // outer() 함수 호출!
innerFunc() // &#39;minji&#39; ⁉️</code></pre>
<p>outer() 함수의 지역변수 값인 <code>minji</code> 이 예시의 결과값으로 나왔다.</p>
<p>outer() 함수는 호출되고나서 inner함수를 반환하며 제 역할을 다 했기때문에 스택에서 제거가 되었다.
스택에서 제거되면서 가지고 있던 지역변수도 함께 제거가 되었을텐데 어떻게 <code>minji</code>이라는 결과가 나온 것일까?</p>
<h4 id="외부-함수outer보다-중첩-함수inner가-더-오래-유지되는-경우-중첩-함수는-제-역할을-다-끝낸-외부-함수의-변수를-참조-할-수-있다-⇒-이러한-중첩-함수를-클로저-라고-한다"><em>외부 함수(outer)보다 중첩 함수(inner)가 더 오래 유지되는 경우, 중첩 함수는 제 역할을 다 끝낸 외부 함수의 변수를 참조 할 수 있다. ⇒ 이러한 중첩 함수를 &quot;클로저&quot; 라고 한다.</em></h4>
<p>이 말에 대해서 자세하게 살펴보자.</p>
<p>자바스크립트의 모든 함수는 자신의 상위 스코프를 기억하고, 기억한 상위 스코프는 함수의 호출위치에 상관없이 계속 기억하고있다.</p>
<p>위 예시에서 <code>inner</code> 함수는 자신이 평가될 때 상위 스코프인 <code>outer</code> 를 내부 슬롯에 저장하였다.
즉, <code>outer</code> 함수의 렉시컬 환경이 <code>inner</code> 함수의 내부 슬롯에 저장되어 기억되고 있는 것이다.
그렇기 떄문에 <code>outer</code> 함수의 실행 컨텍스트가 제거되었어도 렉시컬 환경까지 소멸되지는 않은 것이다.</p>
<p><code>inner</code> 함수에서 <code>outer</code> 함수의 렉시컬 환경을 기억(참조)하고 있기때문에 가비지 컬렉터도 함부로 작업을 시작하지 못한다.</p>
<p>✋🏻 <strong>잠깐!</strong> 
자바스크립트의 모든 함수는 자신의 상위 스코프를 기억한다고 했는데, 그렇다면 중첩 함수는 모두 클로저인가? </p>
<h4 id="no-클로저가-되기위한-성립조건이-있어">&quot;No! 클로저가 되기위한 성립조건이 있어&quot;</h4>
<h3 id="클로저-성립조건">클로저 성립조건</h3>
<p>아래 예시들을 통해 성립조건을 알아보자.</p>
<pre><code class="language-js">function foo() {
  const x = 1
  const y = 2

  function bar() {
    const z = 3
    console.log(z)
  }

  return bar
}

const bar = foo()
bar()</code></pre>
<p>위 예시에서 <code>bar</code> 함수는 중첩함수니까 클로저라고 생각할 수 있다.
하지만 여기서 <code>bar</code> 는 클로저가 아니다.
왜? <code>bar</code> 는 외부 함수인 <code>foo</code> 함수의 중첩 함수이긴 하지만, <code>foo</code> 함수의 어떤 식별자도 참조하고 있지 않기 때문이다.</p>
<blockquote>
<p><strong><em>성립조건 1. 상위 스코프의 식별자를 참조하라</em></strong></p>
</blockquote>
<p>자, 이제 다른 예시를 살펴보자.</p>
<pre><code class="language-js">function foo() {
  const x = 1

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

foo()</code></pre>
<p>이 예시에서의 <code>bar</code> 함수는 중첩 함수에다가 <code>foo</code> 함수의 식별자도 참조하고있다. 그렇다면 <code>bar</code> 는 클로저!?!?</p>
<p>안타깝게도 이번에도 <em>땡!</em> 이다 🙂‍↔️ (<del><em>대체 왜! 뭐때문에!</em></del>)</p>
<p>이유는 중첩 함수 <code>bar</code>가 외부 함수 <code>foo</code> 의 외부로 반환되지 않았기 때문이다.
즉, 외부 함수보다 중첩 함수가 오래 살아남아야하는데 예시에서는 중첩 함수의 생명주기가 외부 함수보다 짧다. (<del><em>bar 는 아주 찰나의 클로저였달까..</em></del>)</p>
<blockquote>
<p><strong><em>성립조건 2. 외부 함수보다 오래 살아남아라</em></strong></p>
</blockquote>
<p>위 성립조건 두 가지를 충족하는 <code>bar</code> 함수가 클로저가 될 수 있는 예시는 다음과 같다.</p>
<pre><code class="language-js">function foo() {
  const x = 1
  const y = 2

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

  return bar
}

const bar = foo()
bar()</code></pre>
<p>✋🏻 <strong>잠깐!</strong> 위 예시에서 중첩 함수 <code>bar</code> 는 <code>foo</code> 함수의 식별자 중 <code>x</code> 만 참조를 하고 있다.
그렇다면 참조되지않는 <code>y</code> 는 어떻게 되는걸까?</p>
<img src="https://velog.velcdn.com/images/minji-kim0524/post/56ba063f-a8b5-4e78-a4eb-964cf39ae594/image.png" width=150> 

<p>소중한 메모리 공간을 생각하여 클로저가 참조하지 않는 식별자는 기억되지 못하고 GC(Garbage Collector)에게 수거된다.</p>
<p>이처럼 클로저에 의해 참조되는 상위 스코프의 변수는 <strong>자유 변수(free variable)</strong> 이라고 부른다.</p>
<p>여기서 클로저라는 말의 의미와 연결될 수 있는데,
<em>클로저란 &quot;함수가 자유 변수에 묶여있다. (닫혀있다)&quot; 라는 의미이다</em></p>
<hr>
<h3 id="클로저의-활용">클로저의 활용</h3>
<p>클로저는 자바스크립트의 강력한 기능 중 하나로 적극적으로 활용하면 유용하게 사용될 수 있다.</p>
<p>유용하게 사용되는 상황들 중 대표적인 상황을 살펴보자.</p>
<h4 id="상태를-안전하게-변경하고-유지하기-위한-상황">상태를 안전하게 변경하고 유지하기 위한 상황</h4>
<p>즉, 상태가 의도치않게 변경되지 않도록 은닉하고 특정 함수에게만 상태 변경을 허용하기 위해 사용되는 것이다.</p>
<p>아래 예시와 함께 살펴보자</p>
<pre><code class="language-js">// 문제상황 👿
let num = 0

const increase = function() {

  return ++num
}

console.log(increase()) // 1
console.log(increase()) // 2
console.log(increase()) // 3</code></pre>
<p>위 예시에서 함수 내에서 사용되는 변수는 전역 변수를통해 관리되고 있다.
이렇게되면 언제든지, 누구나 접근을 할 수 있기 때문에 의도치 않게 값이 변경될 수 있고 그렇게 되면 오류발생으로 이어진다.</p>
<p>카운트 상태를 나타내는 전역 변수인 <code>num</code> 을 다음과같이 안전하게 은닉하는 것이 필요하다.</p>
<pre><code class="language-js">const increase = function() {
  let num = 0 // 은닉 완료 😎

  return ++num
}

console.log(increase()) // 1
console.log(increase()) // 1 ⁉️
console.log(increase()) // 1 ⁉️</code></pre>
<p>이제 <code>num</code> 변수는 <code>increase</code> 함수의 지역 변수로 변경되어 오염될 일은 사라졌다. </p>
<p>그런데 또 다른 문제가 있다.
바로 <code>increase</code> 함수가 호출될 때마다 지역 변수인 <code>num</code> 이 초기화 되어 출력 결과가 언제나 동일하다는 것이다.</p>
<p>이전 상태를 유지해야 num이 하나씩 증가하는 함수의 목적을 실현시킬 수 있다.
이 때 <strong>클로저</strong>를 사용해보면 어떨까?</p>
<pre><code class="language-js">const increase = (function() {
  let num = 0

  // 클로저!
  return function() {
    return ++num
  }
}())

console.log(increase()) // 1
console.log(increase()) // 2
console.log(increase()) // 3</code></pre>
<p>즉시 실행함수가 호출된 이후 클로저 함수를 반환하고 사라진다.
하지만 클로저 함수가 자유변수 <code>num</code> 을 기억하고 있기 때문에 초기화 되지않고 이전 상태를 기억하고 num이 하나씩 증가는 결과를 가져온다.</p>
<hr>
<h3 id="캡슐화와-정보은닉">캡슐화와 정보은닉</h3>
<h4 id="캡슐화란">캡슐화란?</h4>
<p><strong>| 객체의 상태를 나타내는 프로퍼티와 프로퍼티를 참조하고 조작할 수 있는 메서드를 하나로 묶는 것을 말한다.</strong></p>
<h4 id="정보은닉이란">정보은닉이란?</h4>
<p><strong>| 위에서 말한 캡슐화가 객체의 특정 프로퍼티나 메서드를 감출 목적으로 사용되는 것을 말한다.</strong></p>
<p>자바스크립트는 다른 객체지향 프로그래밍 언어와 다르게 <code>public</code>, <code>private</code>, <code>protected</code> 와 같은 공개범위를 한정할 수 있는 접근 제한자를 제공하지 않는다.
그렇기 때문에 자바스크립트 객체의 모든 <code>프로퍼티</code> 와 <code>메서드</code>는 기본적으로 외부에 공개되는 <code>public</code> 으로 선언되어 있다.</p>
<p>인스턴스 메서드와 프로토타입을 활용하여 <code>private</code> 흉내를 낼 수는 있지만 완전하다고 할 수는 없다.</p>
<p>이와 관련한 자세한 내용은 다음장인 <em>25장.클래스</em>에서 살펴보자.</p>
<hr>
<h3 id="클로저-사용시-자주하는-실수">클로저 사용시 자주하는 실수</h3>
<p>다음은 클로저를 사용할 때 자주 발생할 수 있는 실수를 보여주는 예시이다.</p>
<pre><code class="language-js">var funcs =[]

for (var i = 0; i &lt; 3; i++) {
  funcs[i] = function() {return i}
}

for (var j = 0; j &lt; funcs.length; j++) {
  console.log(funcs[j]())
}</code></pre>
<p>위 예시에서 기대하는 결과값은 <code>0, 1, 2</code> 이다.
안타깝게도 실제 결과값은 <code>3</code> 이 출력된다.</p>
<p>이유는 for문에서 변수 선언시 var 키워드로 선언하였기 때문이다.
13장.스코프에서도 살펴봤듯 var키워드는 블록 레벨 스코프가 아니라 함수 레벨 스코프를 갖기때문에 전역 변수이다.</p>
<p>그렇기 떄문에 최종적으로 <code>funcs[j]</code> 함수를 호출하면 <code>3</code>이라는 결과값이 나온다.</p>
<p>위 예시를 기대하는 결과값으로 도출하기 위해서는 어떻게 수정하면 좋을까?</p>
<p>책에 나와있는 클로저를 사용한 방법은 우리가 자주 보던 형태는 아니다. (<del><em>그리고 복잡해보인다</em></del>)</p>
<p>우리에게 익숙한, 자주 보던 형태로 수정해보자!</p>
<pre><code class="language-js">const funcs = []

for (let i = 0; i &lt; 3; i++) {
  funcs[i] = function() {return i}
}

for (let i = 0; i &lt; funcs.length; i++) {
  console.log(funcs[i]())
}</code></pre>
<p><del>*<code>const</code> 와 <code>let</code> 을 보니 마음이 편-안해진다. 😊 *</del></p>
<p>이처럼 let 키워드로 선언한 변수를 사용하면 반복 실행될 때마다 for문 코드 브록의 새로운 렉시컬 환경이 생성되며 식별자의 값을 유지한다. (<del><em>like 스냅샷</em></del>)</p>
<p>단, 함수가 반복문의 코드 블록 내부에서 정의되어야 적용이 되는 내용이다.
반복문의 코드 블록 내부에 함수 정의가 없다면 생성된 렉시컬 환경을 참조하는 곳이 없기때문에 가비지 컬렉션의 타겟이 된다. 👀</p>
<hr>
<h3 id="정리하자면">정리하자면,</h3>
<h4 id="클로저란">클로저란?</h4>
<p>| 외부 함수(outer)보다 중첩 함수(inner)가 더 오래 유지되는 경우, 중첩 함수가 제 역할을 다 끝낸 외부 함수의 변수를 참조하는 것</p>
<h4 id="클로저-성립조건-1">클로저 성립조건</h4>
<p>1️⃣ 상위 스코프의 식별자를 참조하라
2️⃣ 외부 함수보다 오래 살아남아라</p>
<h4 id="클로저의-활용-1">클로저의 활용</h4>
<p>| 상태가 의도치않게 변경되지 않도록 은닉하고 특정 함수에게만 상태 변경을 허용하고자할 때 활용하자</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[멋쟁이사자처럼 부트캠프 프론트엔드 14기/REACT] useContext Hook]]></title>
            <link>https://velog.io/@minji-kim0524/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BCREACT-useContext-Hook</link>
            <guid>https://velog.io/@minji-kim0524/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BCREACT-useContext-Hook</guid>
            <pubDate>Fri, 12 Sep 2025 17:04:36 GMT</pubDate>
            <description><![CDATA[<h1 id="이-state-필요한-사람-나한테서-바로-전달받을-사람-📢">*&quot;이 state 필요한 사람? 나한테서 바로 전달받을 사람?&quot;* 📢</h1>
<hr>
<p>리액트에서는 컴포넌트간 상태공유를 할 때, <strong>부모 컴포넌트</strong>에서 <strong>상태관리</strong>를 하면서 <strong>자식 컴포넌트</strong>에는 상태정보를 <code>props</code> 로 전달한다.</p>
<p>다음과같이 단순한 구조에서는 상태를 공유하고 관리하는 것이 어렵지 않다.</p>
<p><img src="https://velog.velcdn.com/images/minji-kim0524/post/d0ea06fb-5376-4f5d-a219-206708faab4b/image.png" alt=""></p>
<p>하지만, <a href="https://velog.io/@minji-kim0524/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BCREACT-State-Hoisting">상태 끌어올리기</a> 글에서도 다뤘듯이 자식 컴포넌트에서 관리중이던 <code>state</code> 를 부모 컴포넌트로 끌어올려 공유해야하는 상황도 있다.</p>
<p>상태를 끌어올리기만 하는 상황이 있는 것은 아니다. 아래와 같은 상황에서 <code>A컴포넌트</code>에서 <code>C컴포넌트</code> 로 상태를 전달하려면 중간에 <code>C컴포넌트</code> 에 관여하지 않는 컴포넌트에도 <code>props</code> 를 전달하여야 합니다. </p>
<p><img src="https://velog.velcdn.com/images/minji-kim0524/post/77daeae6-31b3-4672-9c89-f39d435d7957/image.png" alt=""></p>
<p>내가 필요한 <code>state</code>도 아닌데 전달만하기위해서 <code>props</code>를 받아서 전달을 해야한다면?
물론, 하면 해! 한다면 할 말이 없지만 무척 비효율적인 처리방식이다.
이러한 처리방식을 <strong>Props Drilling</strong> 이라고한다.</p>
<blockquote>
<p>✋🏻<strong>Props Drilling 이란?</strong>
<code>props</code>를 오로지 하위 컴포넌트로 전달하는 용도로만 사용하는 컴포넌트들을 거치면서 데이터를 전달하는 과정이다.
<code>state</code>를 전달해줄 상위 컴포넌트가 너무 높이 위치해있어서 하위 컴포넌트까지 <code>props</code>로 내려가는것이 드릴과 비슷하여 붙여진 이름이지 않을까싶다.</p>
</blockquote>
<p><code>Props Drilling</code>은 비효율적인 부분뿐만 아니라 코드가 복잡해지고 그로인해 유지보수가 어려워지는 문제까지 야기한다.
또한, 컴포넌트 구조가 변경되면 <code>props</code> 전달경로도 함께 변경되어야하므로 효율을 중시하는 개발에서는 <del><em>질색팔색</em></del>할 일인 것이다. 🤯</p>
<p><img src="https://velog.velcdn.com/images/minji-kim0524/post/1894ea9f-edc9-40a4-a2a4-054756142ff6/image.png" alt=""></p>
<hr>
<p>그렇다면 <code>C컴포넌트</code> 에서 필요한 <code>state</code>니까 <code>A컴포넌트</code> 에서 바로 전달하면 편하고 좋지않을까?!</p>
<p>그래서 등장한 것이 리액트의 <strong>컨텍스트(Context)</strong> 이다.
<strong>컨텍스트(Context)</strong> 를 사용하면 컨텍스트의 내부에 값을 공유할 수 있다.</p>
<h2 id="컨텍스트context란">컨텍스트(<em>Context</em>)란?</h2>
<p><strong>| 부모 컴포넌트가 트리 전체에 데이터를 전달할 수 있도록 해주는 것</strong></p>
<p>자세히 살펴보겠지만 간단하게 <code>context</code>가 진행되는 단계는 다음과 같다.</p>
<ol>
<li>Context <strong>생성</strong><pre><code class="language-js">const Context = createContext(defaultValue)</code></pre>
</li>
<li>데이터가 <strong>필요</strong>한 컴포넌트에서 context를 <strong>사용</strong><pre><code class="language-js">// 레거시 방식 (사용권장 ❌)
function Button() {
return (
 &lt;ThemeContext.Consumer&gt; // .Counsumer는 생략불가능 ❌
     //...
 &lt;/ThemeContext.Consumer&gt;
)
}
</code></pre>
</li>
</ol>
<p>// useContext 방식 (사용권장 ✅)
function Button() {
  const theme = useContext(ThemeContext)
  return <button className={theme} /></p>
<pre><code>3. 데이터를 **지정**하는 컴포넌트에서 context를 **제공**
```js
function App() {
  const [theme, setTheme] = useState(&#39;light&#39;)
  // ...
  return (
    &lt;ThemeContext.Provider value={theme}&gt; // .Provider 생략가능 ✅
        &lt;Page /&gt;
    &lt;/ThemeContext.Provider&gt;
}</code></pre><h3 id="컨텍스트의-특징">컨텍스트의 특징</h3>
<ul>
<li>정적인 값뿐만 아니라 동적인 값도 전달할 수 있다.</li>
<li>값이 변경되면 공유받은 하위 컴포넌트들의 값도 자동으로 업데이트 된다.</li>
<li><code>createContext()</code> 로 생성한 컨텍스트는 완벽하게 분리되어 있기 때문에 서로 다른 리액트 context는 서로에게 영향을 주지 않는다. → <strong><em>하나의 컴포넌트에서 여러 컨텍스트를 사용 또는 제공할 수 있다.</em></strong></li>
</ul>
<h3 id="컨텍스트가-사용되는-대표적인-예시">컨텍스트가 사용되는 대표적인 예시</h3>
<p><strong>1. 상태관리</strong>
<strong>2. 테마관리</strong></p>
<ul>
<li>라이트 / 다크모드 전환시, 상단에 테마 컨텍스트 공급자를 위치시키고 하위 컴포넌트에서는 정보를 읽게하면 모드 전환을 쉽게 할 수 있다.</li>
</ul>
<p><strong>3. 인증관리</strong></p>
<ul>
<li>로그인한 사용자 정보는 여러 컴포넌트에서 필요할 것이다. 이 때, 컨텍스트에 사용자 정보를 저장해두고 필요한 컴포넌트에서 접근하여 사용할 수 있다.</li>
</ul>
<p><strong>4. 라우팅관리</strong></p>
<p>이제 위에서 간단하게 살펴봤던 <code>context</code> 의 진행단계에 대해서 살펴보자.</p>
<h3 id="a-idcreate-context-1️⃣-컨텍스트-생성-a"><a id="create-context"> 1️⃣ 컨텍스트 생성 </a></h3>
<p>컨텍스트를 생성하여 사용하려면 먼저 <code>createContext</code>를 import 해야한다.
이를 통해 <code>컨텍스트 객체</code>를 만들 수 있는데, 생성된 컨텍스트 객체는 <code>공급자(Provider)</code> 와 <code>소비자(Consumer)</code> 를 포함한다.</p>
<p>생성시 매개변수로 <code>defaultValue</code>를 받는데, 이 값은 상위에 일치하는 컨텍스트 제공자가 없을 경우 컨텍스트가 가지는 <code>기본값</code>이다. 
의미있게 설정할 기본값이 없으면 <code>null</code> 로 설정하면되며, 이 값은 정적인 값으로 시간이 지나도 변경되지 않는다.</p>
<p>생성을 하면 위에서 언급한 <code>컨텍스트 객체</code>를 반환하게 되는데, 객체 자체는 어떠한 정보도 가지고 있지 않다.
대신 다음의 몇 가지 속성을 가지고 있다.</p>
<ul>
<li><code>SomeContext</code> : 컴포넌트에 컨텍스트 값을 제공한다.</li>
<li><code>SomeContext.Consumner</code> : 컨텍스트 값을 읽는 방식이며, <code>useContext</code>가 등장하기 전의 방식이다. (<del><em>legacy way</em></del>)</li>
<li><code>SomeContext.Provider</code> : 컨텍스트 제공자로, 컴포넌트를 감싸서 컨텍스트의 값을 모든 내부 컴포넌트에 지정한다. (<del><em>legacy way</em></del>) </li>
</ul>
<p>반환된 <code>컨텍스트 객체</code>를 컴포넌트가 <code>useContext()</code>에 전달하여 컨텍스트 값을 읽을 수 있게 한다.</p>
<pre><code class="language-js">// 기본 형태
import { createContext } from &#39;react&#39;

export const MyContext = createContext(defaultValue)</code></pre>
<pre><code class="language-js">import { createContext } from &#39;react&#39;

export const THEME = {
    light: {
        foreground: &#39;#000&#39;,
        background: &#39;#fff&#39;,
    },
    dark: {
        foreground: &#39;#fff&#39;,
        background: &#39;#000&#39;,    
    }
}

// 다른 컴포넌트에서 사용할 수 있도록 export 
export const ThemeContext = createContext() // createContext의 유일한 인자는 &#39;기본값&#39;임

console.log(ThemeContext.Provider) // 공급자
console.log(ThemeContext.Consumer) // 소비자</code></pre>
<h3 id="2️⃣-컨텍스트-값-제공-provider">2️⃣ 컨텍스트 값 제공 (<em>Provider</em>)</h3>
<p>컨텍스트를 제공하지 않으면 리액트는 생성단계에서 지정한 기본값을 가져온다.
생성한 컨텍스트를 자식 컴포넌트에게 제공하기 위해서는 <code>provider</code> 가 필요하다.</p>
<h3 id="3️⃣-컨텍스트-값-사용-consumer">3️⃣ 컨텍스트 값 사용 (<em>Consumer</em>)</h3>
<p>생성한 컨텍스트를 사용하려면 <code>useContext</code> 라는 리액트 훅이 필요하다.
더불어 생성한 컨텍스트를 사용하려는 컴포넌트에서는 <code>useContext</code> 훅과 함께 <code>생성 컨텍스트</code>도 필요하다.</p>
<pre><code class="language-js">import { useContext } from &#39;react&#39;
import { ThemeContext } from &#39;파일위치&#39;

export default function Toolbar() {
  const [mode] = useContext(ThemeContext) // 컨텍스트 값 읽기
  // ...</code></pre>
<h3 id="⚙️-효율적인-컨텍스트-관리">⚙️ 효율적인 컨텍스트 관리</h3>
<p>컴포넌트 내부에 작성된 컨텍스트 로직을 별도의 파일로 분리하여 사용 및 관리하는 것이 유지보수 측면에서도, 코드의 가독성 측면에서도 좋다.</p>
<ol>
<li>별도의 파일에 사용할 컨텍스트 생성</li>
<li>프로바이더 컴포넌트 작성</li>
</ol>
<ul>
<li>컨텍스트에 실제 값을 넣어서 하위 컴포넌트에 전달해주는 컴포넌트로, <code>상태</code>와 상태 업데이트 <code>함수</code>를 값으로 제공한다.</li>
</ul>
<ol start="3">
<li>프로바이더 컴포넌트 사용</li>
</ol>
<ul>
<li>상태를 공유할 컴포넌트를 프로바이더 컴포넌트로 감싸준다.</li>
<li>프로바이더 컴포넌트에 감싸진 컴포넌트와 그 하위 컴포넌트 모두 상태를 공유받게 된다.</li>
</ul>
<hr>
<p>✋🏻<strong>잠깐!</strong> 지금까지 컨텍스트에 대해 살펴봤는데 그래서 <code>useContext</code>가 뭔데?</p>
<h2 id="usecontext-란">useContext 란?</h2>
<p><strong>| 컴포넌트에서 <code>Context</code> 를 읽고 구독할 수 있는 리액트 훅이다.</strong></p>
<h4 id="형태">형태</h4>
<pre><code class="language-js">const value = useContext(SomeContext)</code></pre>
<h4 id="매개변수">매개변수</h4>
<ul>
<li><code>SomeContext</code> : <code>createContext</code> 로 생성한 컨텍스트로 자체적으로 정보를 담고 있지는 않다. <a href="#create-context">참고</a></li>
</ul>
<h4 id="반환값">반환값</h4>
<ul>
<li>호출하는 컴포넌트에 대한 컨텍스트 값을 반환한다.</li>
<li>트리에서 호출하는 컴포넌트 상위의 가장 가까운 <code>SomeContext</code>에 전달된 값으로 결정된다.</li>
<li>Provider가 없으면 반환되는 값은 생성시 설정한 <code>defaultValue</code>가 되며, 반환된 값은 항상 최신상태이다.</li>
<li>컨텍스트가 변경되면 리액트는 자동으로 변경된 컨텍스트를 읽는 컴포넌트를 리렌더링한다.</li>
</ul>
<blockquote>
<p><strong><em><code>useContext()</code> 는 항상 호출하는 컴포넌트 상위에서 가장 가까운 Provider 를 찾는다. 위쪽 방향으로 찾으며, 호출하는 컴포넌트 내의 Provider 는 고려하지 않는다.</em></strong></p>
</blockquote>
<blockquote>
<p><strong><em>context 를 업데이트하려면 State와 함께 시너지효과를 내보자!</em></strong></p>
</blockquote>
<hr>
<p><strong>🔗 참고자료</strong>
<a href="https://ko.react.dev/reference/react/createContext">https://ko.react.dev/reference/react/createContext</a>
<a href="https://ko.react.dev/reference/react/useContext">https://ko.react.dev/reference/react/useContext</a>
<a href="https://ko.react.dev/learn/passing-data-deeply-with-context">https://ko.react.dev/learn/passing-data-deeply-with-context</a>
<a href="https://ko.react.dev/learn/scaling-up-with-reducer-and-context#moving-all-wiring-into-a-single-file">https://ko.react.dev/learn/scaling-up-with-reducer-and-context#moving-all-wiring-into-a-single-file</a>
<a href="https://kentcdodds.com/blog/prop-drilling">https://kentcdodds.com/blog/prop-drilling</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[멋쟁이사자처럼 부트캠프 프론트엔드 14기/REACT] useReducer Hook]]></title>
            <link>https://velog.io/@minji-kim0524/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BCREACT-useReducer-Hook</link>
            <guid>https://velog.io/@minji-kim0524/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BCREACT-useReducer-Hook</guid>
            <pubDate>Fri, 12 Sep 2025 05:41:35 GMT</pubDate>
            <description><![CDATA[<h1 id="관리해야하는-상태가-많아지고-힘들어질-땐-나를-찾아와">*&quot;관리해야하는 상태가 많아지고 힘들어질 땐 나를 찾아와!&quot;*</h1>
<hr>
<p>&#39;상태관리하는 훅&#39; 이라고하면 자연스럽게 <code>useState</code> 를 떠올리게 된다.
하지만, 관리해야하는 상태의 규모가 커지고 복잡해진다면 <code>useState</code> 만 사용해서 관리하기엔 (<del><em>코드가독성저하, 유지보수등 여러 관리측면으로</em></del>)어려워 질것이다.</p>
<p>그럴때 사용하는 것이 <code>useReducer</code> 라는 훅이다.</p>
<h3 id="usereducer란">useReducer란?</h3>
<p><strong>| 컴포넌트에 <code>reducer</code> 함수를 추가하는 React Hook이다.</strong></p>
<p>이 훅을 사용하면 <strong>상태</strong>와 상태를 변경하는 <strong>로직</strong>을 분리하여 관리할 수 있다.</p>
<p>상태가 어떻게 변해야하는지의 <strong>규칙</strong>은 <code>리듀서 함수</code>에 담고, 상태를 변경할 때는 <code>액션</code>을 전달하여 상태업데이트를 진행한다.
이 방식은 나중에 다룰 내용인 <em>리덕스<em>와 매우 유사하지만, ~~</em>전역 상태에서 사용하는 리덕스</em>~~와 달리 <code>useReducer</code> 는 컴포넌트 내부에서만 사용할 수 있고, 설정이 간단하다.</p>
<blockquote>
<p>✋🏻 <strong>reduce 함수란 뭘까?</strong>
<code>useState</code> 사용 형태를 보면 <code>const [state, setState]</code> 라고해서 상태를 관리하는 함수인 <code>setState</code> 가 있다. 
<code>reduce</code> 가  <code>useReducer</code> 에서는 <code>setState</code> 의 역할을 하는 것이다.
즉, 컴포넌트 내부에 있는 state 로직을 <code>reduce</code> 가 받는것이다.</p>
</blockquote>
<pre><code class="language-js">// 형태
const [state, dispatch] = useReducer(reducer, initialArg, init?)</code></pre>
<p>위와같이 생긴 <code>useReducer</code> 는 다음과 같이 컴포넌트 최상위에서 호출하고 사용(<em>state 관리</em>)한다.</p>
<pre><code class="language-js">// useReducer 호출
import { useReducer } from &#39;react&#39;

// reducer 함수 선언
function reducer(state, action) {
  // ...
}

function Component() {
  const [state, dispatch] = useReducer(reducer, { key: value })
  // ...</code></pre>
<p>위의 기본형태를 활용하여 어떻게 사용하는지에 대한 간단한 예시는 다음과 같다.</p>
<pre><code class="language-js">import { useReducer } from &#39;react&#39;

function Counter() {
  const [state, dispatch] = useReducer(reducer, { count: 0 })

  return (
    &lt;div className=&quot;counter&quot;&gt;
      &lt;p&gt;카운트: {state.count}&lt;/p&gt;
      &lt;button 
          type=&quot;button&quot; 
          onClick={() =&gt; dispatch({ type: &#39;decrement&#39; })}
          aria-label=&quot;카운트 감소&quot;
        &gt;
            -
          &lt;/button&gt;
      &lt;button 
          type=&quot;button&quot; 
          onClick={() =&gt; dispatch({ type: &#39;increment&#39; })}
          aria-label=&quot;카운트 증가&quot;
        &gt;
            -
          &lt;/button&gt;
    &lt;/div&gt;
  )
}

function reducer(state, action) {
  switch (action.type) {
    case &#39;increment&#39;: return { count: state.count + 1 }
    case &#39;decrement&#39;: return { count: state.count - 1 }
    default: return state
  }
}</code></pre>
<blockquote>
<p><strong><em>핵심은 상태를 변경하려면 <code>dispatch</code> 함수를 사용해야하고, 실제 상태 변경 로직은 전부 <code>reducer</code> 함수 내부에 모여있다는 점이다.</em></strong></p>
</blockquote>
<h4 id="매개변수">매개변수</h4>
<p>1️⃣ <code>reducer</code></p>
<ul>
<li>state 업데이트를 진행하는 함수로 반드시 <code>순수함수</code> 여야한다.</li>
<li>인수로 <code>state</code> 와 <code>action</code> 을 받으며, 다음 <code>state</code> 를 반환해야한다.</li>
<li>인수에는 모든 데이터타입이 할당가능하다.</li>
<li><strong><em>실제 상태변경 로직이 모여있는 곳</em></strong></li>
</ul>
<pre><code class="language-js">function (state, action) {
  swtich(action.type) {
    case &#39;added_todo&quot; : {
        return {
            ...state,
            todos: [
              ...state.todos,
              {id: nextId++, text: action.text}
            ]
          }
    }
    // ...
  }
}</code></pre>
<p>2️⃣ <code>initialArg</code></p>
<ul>
<li>초기 state 가 계산되는 값이다.</li>
<li>모든 데이터타입이 할당될 수 있다.</li>
<li>초기 state 가 어떻게 계산되는지는 다음 <code>init</code> 인수에 달렸다.</li>
</ul>
<p>3️⃣ (<em>optional</em>)<code>init</code></p>
<ul>
<li>초기 state를 반환하는 <code>초기화</code> 함수이다.</li>
<li>선택사항인만큼 해당 인수가 할당되지 않았을 경우의 초기 state는 <code>initialArg</code> 로 설정된다.</li>
<li>인수로 할당이 되었다면 초기 state는 <code>init(initialArg)</code> 를 호출한 결과가 할당된다.</li>
</ul>
<h4 id="반환값">반환값</h4>
<p>2개의 엘리먼트로 구성된 <code>배열</code>을 반환한다.
1️⃣ 현재 <code>state</code>: 첫 번째 렌더링에서의 state는 <code>init(initialArg)</code>의 결과 또는 <code>initialArg</code>로 설정된다. (<strong><em>읽기전용으로 변경할 수 없다 ❌</em></strong>)
2️⃣ <code>dispatch</code> 함수: state를 새로운 값으로 업데이트하고 리렌더링을 일으킨다. (<strong><em>상태변경함수</em></strong>)</p>
<p>⇒ 화면을 업데이트하려면 <code>action</code> 객체(<em>사용자가 수행한 활동</em>)를 인수로하여 <code>dispatch</code>함수를 호출해야한다.</p>
<blockquote>
<p>✋🏻 <strong>dispatch란?</strong></p>
</blockquote>
<ul>
<li><code>useReducer</code>에 의해 반환되는 함수이며, state를 새로운 값으로 업데이트하고 리렌더링을 일으킨다.</li>
<li><code>dispatch</code> 함수의 유일한 인수는 <code>action</code>이며, <code>dispatch</code> 함수는 어떤 값도 반환하지 않는다. → <strong><em>다음 렌더링에 사용할 state 변수 업데이트만 진행한다.</em></strong></li>
<li><code>dispatch</code> 함수 호출은 현재 동작하고 있는 코드의 state를 변경하지 않는다. → <strong><em>함수 호출 직후에 state변수를 읽으면 호출 이전의 최신화되기전 값을 참조하게 된다.</em></strong></li>
<li><code>dispatch</code> 함수의 매개변수인 <code>action</code>은 사용자에 의해 수행된 활동이며, 모든 데이터 타입이 할당될 수 있다.</li>
<li><code>action</code> 은 객체형태로 이루어져있으며, <code>type</code> 프로퍼티와 <code>payload</code> 프로퍼티로 구성되어있다.</li>
<li><code>action</code> 의 <code>type</code> 프로퍼티이름은 컴포넌트 내에 단일 상호작용을 설명하는 지역적 특성을 가지고 있다.<pre><code class="language-js">// action 형태
type Action = {type: /*액션이 무엇인지 정의*/, payload: ...}
// dispatch 형태
const [state, dispatch] = useReducer(reducer, { key: value })
function handleClick() {
dispatch({type: action.type /*reducer가 다음 state를 계산하는데 필요한 정보*/})
// ...</code></pre>
</li>
</ul>
<p><strong>useReducer 잘 작성하는 방법</strong></p>
<p><strong>1. 반드시 순수해야 한다.</strong></p>
<ul>
<li><code>reducer</code> 는 렌더링 중에 실행된다. (<code>action</code>은 다음 렌더링까지 대기) → 동일입력 동일출력이 이루어져야한다. ⇒ <strong>순수해야 한다.</strong> </li>
<li>컴포넌트 외부에 영향을 미치는 사이드 이펙트를 수행해서는 안된다.</li>
<li>객체와 배열을 변경하지 않고 업데이트 해야한다.</li>
</ul>
<p><strong>2. <code>action</code> 발생으로 인해 여러 데이터가 변경되더라도, 모든 데이터 변경을 포함하는 하나의 명확한 상호작용을 나타내야한다.</strong></p>
<ul>
<li>모든 <code>action</code> 을 <code>reducer</code>에 기록할 경우, 어떤 상호작용이나 응답이 어떤 순서로 일어났는지를 재구성할 수 있을 정도로 로그가 <code>명확</code>해야 한다. → 디버깅이 용이해진다.</li>
<li>예: <code>reducer</code>가 관리하는 5개의 필드가 있다. 해당 양식에서 &#39;재설정&#39; 버튼을 눌렀을 때, 각각의 필드에서 <code>action</code> 을 발생시키는 것 보다는 하나의 <code>action</code> 을 전송하는 것이 더 합리적이다.</li>
</ul>
<h3 id="usestate-vs-usereducer-비교표"><code>useState</code> vs. <code>useReducer</code> 비교표</h3>
<table>
<thead>
<tr>
<th><strong>비교항목</strong></th>
<th><strong>useState</strong></th>
<th><strong>useReducer</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>코드크기</strong></td>
<td>간단명료 → 단순한 상태에 적합</td>
<td>작성해야하는 코드양 많음 → 여러 이벤트가 상태를 바꾸는 등의 복잡한 상태관리에서는 오히려 코드가 간결해짐</td>
</tr>
<tr>
<td><strong>가독성</strong></td>
<td>상태가 단순하거나 컴포넌트가 작을 때 가독성이 좋음</td>
<td>상태가 복잡하거나 컴포넌트의 규모가 커질수록 가독성이 좋음</td>
</tr>
<tr>
<td><strong>디버깅</strong></td>
<td>잘못된 위치와 이유 파악이 어려움</td>
<td>모든 상태 업데이트와 액션이 한 곳(리듀서)에 모여있어 파악이 쉬움</td>
</tr>
<tr>
<td><strong>테스트</strong></td>
<td>상태 업데이트 로직이 컴포넌트에 의존하고있어 테스트가 어려움</td>
<td>로직(리듀서)이 컴포넌트에 의존하지않는 순수함수로, 독립적으로 분리되어있어 테스트하기에 용이함</td>
</tr>
<tr>
<td><strong>선호도</strong> (취향에 맞게 선택가능)</td>
<td>직관적이며 익숙한 방식</td>
<td>구조적이며 명확한 방식</td>
</tr>
<tr>
<td><strong>혼용 가능여부</strong></td>
<td>다른 hook과 함께 사용가능 / <code>useReducer</code> 와 혼용가능</td>
<td>다른 hook과 함께 사용가능 / <code>useState</code> 와 혼용가능</td>
</tr>
</tbody></table>
<hr>
<p>여기서 조금 더 파고들어가본다면, 배열을 사용해서 리액트 상태를 관리할 때 <code>useState</code> 대신 <code>useImmer</code> 로 사용이 가능하다고 했다.
<code>Immer</code> 라이브러리를 이용하여 일반적인 state에서 객체와 배열을 업데이트 했던 것처럼, <code>Immer</code> 라이브러리를 사용하면 <code>reducer</code> 를 더 간결하게 작성할 수 있다.</p>
<p><code>Immer</code> 라이브러리에서 제공하는 <code>useImmerReducer</code> 를 import 하여 <code>push</code> 또는 <code>arr[i] =</code> 으로 값을 할당함으로써 <code>state</code> 변경이 가능하다.</p>
<p>그런데 여기서 드는 의문은<code>ruducer</code>는 순수함수라고 했는데, <code>state</code> 변경을 하면 <strong>동일입력 동일출력</strong>이 이루어지지않지 않을까?
⇒ <code>Immer</code> 는 내부적으로 변경사항이 반영된 <code>draft</code> 를 이용하여 <code>state</code> 복사본을 생성한다. 그렇기때문에 <code>state</code> 변경이 가능한 것이다.</p>
<hr>
<p><strong>🔗 참고자료</strong>
<a href="https://ko.react.dev/reference/react/useReducer">https://ko.react.dev/reference/react/useReducer</a>
<a href="https://ko.react.dev/learn/extracting-state-logic-into-a-reducer">https://ko.react.dev/learn/extracting-state-logic-into-a-reducer</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[멋쟁이사자처럼 부트캠프 프론트엔드 14기/REACT] Flux Architecture]]></title>
            <link>https://velog.io/@minji-kim0524/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BCREACT-Flux-Architecture</link>
            <guid>https://velog.io/@minji-kim0524/%EB%A9%8B%EC%9F%81%EC%9D%B4%EC%82%AC%EC%9E%90%EC%B2%98%EB%9F%BCREACT-Flux-Architecture</guid>
            <pubDate>Thu, 11 Sep 2025 12:02:15 GMT</pubDate>
            <description><![CDATA[<h1 id="리액트의-단방향-데이터-흐름-패턴">리액트의 단방향 데이터 흐름 패턴</h1>
<hr>
<p><code>useReducer</code> 에 대해서 알아보기전에 리액트의 <code>단방향 데이터흐름 패턴</code>에 대해서 간단하게 살펴보자.</p>
<h3 id="flux-아키텍처란">Flux 아키텍처란?</h3>
<p>페이스북에서만든 <code>데이터 흐름 패턴</code>으로, 데이터의 단방향 흐름을 파악하여 상태관리를 쉽게 할 수 있도록 이해를 높여준다.</p>
<h3 id="flux-아키텍처-구성요소">Flux 아키텍처 구성요소</h3>
<h4 id="1️⃣-액션-action">1️⃣ 액션 (Action)</h4>
<ul>
<li>사용자의 입력이나 이벤트가 해당됨</li>
<li>예: 버튼클릭, 데이터 추가 등</li>
</ul>
<h4 id="2️⃣-디스패처-dispatcher">2️⃣ 디스패처 (Dispatcher)</h4>
<ul>
<li>모든 <code>액션</code>을 받아 <code>스토어</code>에 <code>전달</code>하는 중간 관리자역할</li>
<li>함수가 위치하는 곳</li>
</ul>
<h4 id="3️⃣-스토어-store">3️⃣ 스토어 (Store)</h4>
<ul>
<li>실제 <code>데이터</code>와 <code>상태</code>를 <code>저장</code>하는 곳</li>
<li>디스패처로부터 받은 액션을 데이터로 변경 <code>액션 → 데이터</code></li>
</ul>
<h4 id="4️⃣-뷰-view">4️⃣ 뷰 (View)</h4>
<ul>
<li>사용자에게 보여지는 화면</li>
<li>스토어에서 받아온 <code>데이터</code>를 화면에 <code>렌더링</code></li>
</ul>
<p>Flux 아키텍처를 바탕으로 데이터가 어떻게 한 방향으로 흐르는지 살펴보면 다음과 같다.</p>
<h3 id="flux-아키텍처-데이터-흐름">Flux 아키텍처 데이터 흐름</h3>
<p><img src="https://velog.velcdn.com/images/minji-kim0524/post/78f91b15-8dad-49fe-950f-2e88a178ad10/image.png" alt=""></p>
<pre><code class="language-js">사용자 입력
   ↓
Action : { type, payload } 객체 구성
   ↓
Dispatcher : Action 전달
   ↓
Store : 전달된 Action으로 데이터 변경
   ↓
View : 변경된 데이터로 다시 렌더링

[사용자 입력] → [Action] → [Dispatcher] → [Store] → [View]</code></pre>
<p>디스패처라는 단어를 보니 <del><em>디스패치</em></del> 가 생각이 나면서...
데이터 흐름을 조금 더 쉽게 이해하기위해 나는 이렇게 비유해서 이해해보았다.</p>
<table>
<thead>
<tr>
<th>단계</th>
<th>비유</th>
</tr>
</thead>
<tbody><tr>
<td>액션 (Action)</td>
<td>사건 발생(<del><em>예를들면 열애설</em></del>)</td>
</tr>
<tr>
<td>디스패처 (Dispatcher)</td>
<td>디스패치 기자가 사건정보를 수집</td>
</tr>
<tr>
<td>스토어 (Store)</td>
<td>정보를 전달받은 방송국, 기사를 쓰기시작</td>
</tr>
<tr>
<td>컨트롤러 뷰(Controller View) &amp; 뷰 (View)</td>
<td>인터넷에 기사가 올라가고 확인가능한 상태가 됨</td>
</tr>
</tbody></table>
<p>위 비유는 단순히 각 단계를 이해하기위한 내용이고, 애플리케이션의 데이터 흐름관점으로 단계를 살펴보면 다음과 같다.</p>
<p>먼저, 애플리케이션이 초기화될 때 첫 번째 <code>준비과정</code>을 거친다.</p>
<ol>
<li><strong>스토어</strong> → <strong>디스패처</strong> : *&quot;액션이 들어오면 알려줘!&quot;*</li>
<li><strong>컨트롤러 뷰</strong> → <strong>스토어</strong> : *&quot;업데이트된 최신 상태있어? 이거 맞아?&quot;*</li>
<li><strong>스토어</strong> → <strong>컨트롤러 뷰</strong> : *&quot;여기 업데이트된 최신 상태가 있어! 전달할게!&quot;*</li>
<li><strong>컨트롤러 뷰</strong> → <strong>뷰</strong> : *&quot;(자식)뷰들아, 최신 상태 전달할게 받으렴&quot;*</li>
<li><strong>뷰</strong> : *&quot;전달받은 최신 상태를 렌더링 해볼까나&quot;*</li>
</ol>
<p>위와같은 준비과정이 끝나면 애플리케이션은 <code>사용자 입력</code>을 받아들일 준비가 완료된 것이다.</p>
<p>🪄 이제 사용자 입력을 통해 <code>액션(Action)</code>이 발생했을 때의 데이터 흐름을 살펴보자.</p>
<p><img src="https://velog.velcdn.com/images/minji-kim0524/post/db32eef2-0816-4b98-a1a2-bfed930cbbc8/image.png" alt=""></p>
<ol>
<li><code>사용자 입력 발생!</code></li>
<li><code>뷰 → 액션</code> : *&quot;준비해! (레뒤)&quot;*</li>
<li><code>액션 → 디스패처</code> : *&quot;여기 사건(액션)이 발생했어!&quot;*</li>
<li><code>디스패처 → 스토어</code> : *&quot;(전달된 액션 순서대로) 여기 발생한 액션 전달할게~&quot;*</li>
<li><code>스토어</code> : *&quot;(필요한 액션만 골라서) 보자보자 상황에 맞게 좀 바꿔볼까&quot;*</li>
<li><code>스토어 → 컨트롤러 뷰</code> : *&quot;나 구독하고 있지? 여기 니가 필요할만한 것들을 모아봤어. 선물이야😎&quot;*</li>
<li><code>컨트롤러 뷰 → 뷰</code> : *&quot;자, 업데이트된 데이터(상태)가 들어왔어! 상태에 맞게 화면 렌더링 시작하자구&quot;*</li>
<li><code>뷰</code> : *&quot;업데이트된 데이터(상태)로 렌더링을 시작하지&quot;*</li>
</ol>
<p>리액트는 이러한 Flux 아키텍처의 원리를 활용하여 복잡한 상태관리를 하는 훅인 <code>useReducer</code> 를 제공한다.</p>
<p>다음 글에서는 복잡한 상태를 관리하는 훅인만큼 <del><em>조금은 복잡한</em></del> <code>useReducer</code> 에 대해서 살펴보자!</p>
<hr>
<h3 id="정리하자면">정리하자면,</h3>
<p><img src="https://velog.velcdn.com/images/minji-kim0524/post/3aa001d3-8f85-4af3-8827-9adc5abf4832/image.png" alt=""></p>
<p><strong>Flux 아키텍처의 전체 흐름</strong></p>
<ol>
<li>사용자 요청 → 액션 생선</li>
<li>액션을 디스패처에 전달</li>
<li>디스패처가 액션을 스토어에 전달</li>
<li>스토어가 상태 업데이트 및 콜백 실행</li>
<li>뷰가 상태 변경 감지 및 상태 전달요청</li>
<li>뷰가 전달받은 새로운 상태로 리렌더링</li>
</ol>
<hr>
<p><strong>🔗 참고자료</strong>
<a href="https://facebookarchive.github.io/flux/">https://facebookarchive.github.io/flux/</a>
<a href="https://www.youtube.com/watch?v=i__969noyAM&amp;t=125s">https://www.youtube.com/watch?v=i__969noyAM&amp;t=125s</a></p>
]]></description>
        </item>
    </channel>
</rss>