<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>annmmww.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Tue, 21 Apr 2026 03:28:29 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>annmmww.log</title>
            <url>https://velog.velcdn.com/images/ann-algorithm/profile/a2159380-1206-45c0-9e74-80615fe945f5/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. annmmww.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/ann-algorithm" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[여행/체험 예약 플랫폼] 트러블슈팅 - tailwind-merge 커스텀 토큰 충돌 해결기]]></title>
            <link>https://velog.io/@ann-algorithm/%EC%97%AC%ED%96%89%EC%B2%B4%ED%97%98-%EC%98%88%EC%95%BD-%ED%94%8C%EB%9E%AB%ED%8F%BC-%ED%8A%B8%EB%9F%AC%EB%B8%94%EC%8A%88%ED%8C%85</link>
            <guid>https://velog.io/@ann-algorithm/%EC%97%AC%ED%96%89%EC%B2%B4%ED%97%98-%EC%98%88%EC%95%BD-%ED%94%8C%EB%9E%AB%ED%8F%BC-%ED%8A%B8%EB%9F%AC%EB%B8%94%EC%8A%88%ED%8C%85</guid>
            <pubDate>Tue, 21 Apr 2026 03:28:29 GMT</pubDate>
            <description><![CDATA[<h2 id="📌-문제">📌 문제</h2>
<p>SecondaryButton는 내부적으로 ButtonBase를 사용하고 있었고, typography는 아래처럼 분리돼 있었습니다.</p>
<pre><code class="language-ts">const typo = {
  primary: &#39;text-14 md:text-16 font-bold&#39;,
  secondary: &#39;text-14 md:text-16 font-medium&#39;,
  label: &#39;text-14 md:text-16 font-medium&#39;,
};

const toneClassMap = {
  secondary: {
    default:
      &#39;bg-primary-500 text-white hover:bg-primary-hover disabled:bg-white disabled:text-gray-200 disabled:border disabled:border-gray-200&#39;,
    accent:
      &#39;bg-white text-gray-600 border border-gray-200 disabled:bg-white disabled:text-gray-200 disabled:border-gray-200&#39;,
  },
};

className={cx(
  baseClassName,
  shapes[variant],
  typo[variant],
  toneClassMap[variant][tone],
  className
)}</code></pre>
<p>겉으로 보면 text-14와 text-gray-600은 서로 다른 역할이라 같이 살아야 합니다.</p>
<p>text-14: 글자 크기
text-gray-600: 글자 색상
그런데 <span style="background-color:#FFEBCD">실제로는 text-14가 사라져서, 모바일 구간에서 글자 크기가 14px로 줄지 않았습니다.
</span></p>
<h2 id="📌-배경">📌 배경</h2>
<p>프로젝트는 Tailwind CSS v4 스타일로 typography 토큰을 직접 정의하고 있었습니다.</p>
<pre><code>@theme inline {
  --text-14: 0.875rem; /* 14px */
  --text-16: 1rem; /* 16px */
}</code></pre><p>즉, text-14 자체는 잘못된 클래스가 아니었습니다.
문제는 클래스 생성이 아니라, 클래스를 합치는 과정에 있었습니다.</p>
<p><span style="background-color:#FDF5E6">프로젝트의 cx() 유틸은 내부적으로 tailwind-merge를 사용하고 있었습니다.</span></p>
<pre><code class="language-ts">import { cx as cvaCX } from &#39;class-variance-authority&#39;;
import { twMerge } from &#39;tailwind-merge&#39;;

export const cx = (...inputs: ClassValue[]): string =&gt; twMerge(cvaCX(inputs));</code></pre>
<p><span style="background-color:#FFEBCD">tailwind-merge는 충돌하는 Tailwind 클래스를 자동으로 정리해 주는 라이브러리입니다.</span>
예를 들어 <span style="background-color:#E2D6FF">p-2 p-4가 같이 있으면 마지막 p-4만 남기는 식입니다.
</span>
문제는 <span style="background-color:#FFEBCD">이 라이브러리가 기본 설정만으로는 text-14를 프로젝트의 커스텀 font-size 토큰으로 알지 못했다는 점입니다.</span></p>
<h2 id="접근">접근</h2>
<p>원인을 찾을 때는 아래 순서로 확인했습니다.</p>
<ul>
<li>text-14가 Tailwind에서 실제로 정의되어 있는지 확인</li>
<li>SecondaryButton가 아니라 ButtonBase에서 최종 className이 어떻게 합쳐지는지 확인</li>
<li>twMerge()가 text-14를 제거하는지 직접 재현</li>
</ul>
<p>직접 재현해보면 원인이 분명해집니다.</p>
<pre><code>twMerge(&#39;text-14 text-gray-600&#39;)
// 결과: &#39;text-gray-600&#39;</code></pre><p>즉, <span style="background-color:#FFEBCD">tailwind-merge가 text-14와 text-gray-600를 같은 text-* 그룹으로 보고, 뒤에 오는 text-gray-600만 남기고 있었습니다.</span></p>
<p>여기서 <span style="background-color:#E2D6FF">중요한 포인트는 md:text-16은 살아남았다는 점입니다</span>.</p>
<ul>
<li><p><code>text-14</code>는 기본 구간 클래스</p>
</li>
<li><p><code>md:text-16</code>은 md 반응형 구간 클래스</p>
<p>둘은 modifier가 다르기 때문에 md:text-16은 유지되고, 기본 구간의 text-14만 지워졌습니다.
그래서 <span style="background-color:#FFEBCD">결과적으로 모바일에서는 기본 글자 크기처럼 보이고, md 이상에서는 16px이 적용되는 상태가 됐습니다.</span></p>
</li>
</ul>
<h2 id="📌-해결법">📌 해결법</h2>
<h3 id="1-text-14px-같은-arbitrary-value로-바꾸기">1. text-[14px] 같은 arbitrary value로 바꾸기</h3>
<p><code>text-[14px] md:text-[16px] font-medium</code></p>
<p>이 방법의 이유는 단순합니다.</p>
<ul>
<li>tailwind-merge가 arbitrary value는 비교적 명확하게 구분할 수 있음</li>
<li>빠르게 문제를 해결할 수 있음</li>
<li>해당 컴포넌트만 바로 수정 가능함</li>
</ul>
<p>하지만 단점도 있습니다.</p>
<ul>
<li>디자인 토큰 체계를 컴포넌트 안으로 흩뿌리게 됨</li>
<li><code>--text-14</code>, <code>--text-16</code>처럼 이미 정의한 토큰의 의미가 약해짐</li>
<li>같은 문제가 다른 컴포넌트에서 반복될 수 있음</li>
</ul>
<h3 id="2-tailwind-merge를-확장해서-프로젝트-토큰을-알려주기">2. tailwind-merge를 확장해서 프로젝트 토큰을 알려주기</h3>
<p><code>import { extendTailwindMerge } from &#39;tailwind-merge&#39;</code>;
이 방법의 이유는 구조적인 해결이기 때문입니다.</p>
<ul>
<li><code>text-14</code>, <code>text-16</code>을 프로젝트의 정식 font-size 토큰으로 인식시킬 수 있음</li>
<li>기존 컴포넌트 API를 바꾸지 않아도 됨</li>
<li>Button뿐 아니라 앞으로의 모든 컴포넌트에 같은 기준을 적용할 수 있음</li>
<li>typography 토큰 시스템을 유지할 수 있음</li>
</ul>
<p>단점은 한 가지입니다.
초기에 merge 설정을 한 번 관리해줘야 함</p>
<h3 id="3-tailwind-merge를-아예-제거하기">3. tailwind-merge를 아예 제거하기</h3>
<p>이 방법도 이론상 가능합니다.</p>
<p>클래스가 지워지지 않으니 당장 문제는 없어질 수 있음
하지만 추천하기 어렵습니다.</p>
<ul>
<li><code>px-3 px-4</code>, <code>rounded-lg rounded-xl</code> 같은 진짜 충돌 정리가 사라짐</li>
<li>전체 프로젝트의 클래스 병합 안정성이 떨어짐</li>
<li>지금 문제 하나를 해결하려고 더 넓은 범위의 편의성을 잃게 됨</li>
</ul>
<h2 id="📌-적용">📌 적용</h2>
<p>이번에는 2번, 즉 tailwind-merge 확장 방식을 선택했습니다.</p>
<p>선택한 이유는 명확했습니다.</p>
<ul>
<li>이미 프로젝트가 text-14, text-16, text-14-body 같은 토큰 기반 구조를 갖고 있었음</li>
<li>문제의 본질은 컴포넌트가 아니라 merge 설정이었음</li>
<li>한 곳에서 고치면 모든 사용처에 동일하게 적용됨</li>
<li>앞으로 body typography 토큰까지 같은 방식으로 안전하게 사용할 수 있음</li>
</ul>
<p>즉, “버튼 하나만 급하게 고치는 방식”보다 “토큰 시스템 자체를 제대로 이해시키는 방식”이 더 맞는 해결책이었습니다.</p>
<h2 id="📌-적용한-해결-코드">📌 적용한 해결 코드</h2>
<pre><code>import { cx as cvaCX } from &#39;class-variance-authority&#39;;
import type { ClassValue } from &#39;clsx&#39;;
import { extendTailwindMerge } from &#39;tailwind-merge&#39;;

const twMerge = extendTailwindMerge({
  extend: {
    theme: {
      text: [
        &#39;10&#39;,
        &#39;12&#39;,
        &#39;13&#39;,
        &#39;14&#39;,
        &#39;16&#39;,
        &#39;18&#39;,
        &#39;20&#39;,
        &#39;24&#39;,
        &#39;32&#39;,
        &#39;14-body&#39;,
        &#39;16-body&#39;,
        &#39;18-body&#39;,
        &#39;20-body&#39;,
      ],
    },
  },
});

export const cx = (...inputs: ClassValue[]): string =&gt; twMerge(cvaCX(inputs));</code></pre><p><span style="background-color:#FFEBCD">핵심은 theme.text에 프로젝트의 typography 토큰을 등록한 것입니다.</span>
이제 <span style="background-color:#FFC0CB">tailwind-merge는 text-14를 “색상 후보”가 아니라 “font-size 토큰”으로 이해합니다.
</span></p>
<h2 id="적용-결과">적용 결과</h2>
<p>적용 전에는 아래 병합 결과에서 text-14가 사라졌습니다.</p>
<pre><code>twMerge(&#39;text-14 text-gray-600&#39;)
// &#39;text-gray-600&#39;</code></pre><p>적용 후에는 두 클래스가 함께 유지됩니다.</p>
<pre><code>twMerge(&#39;text-14 text-gray-600&#39;)
// &#39;text-14 text-gray-600&#39;</code></pre><p>실제 UI 관점에서 보면 결과는 다음과 같습니다.</p>
<ul>
<li>모바일 구간에서 SecondaryButton의 글자 크기가 정상적으로 14px 적용</li>
<li>md 이상에서는 md:text-16이 그대로 적용</li>
<li>text-gray-600, text-white 같은 색상 클래스도 정상 유지</li>
<li>기존 컴포넌트 사용 방식은 바꾸지 않아도 됨</li>
</ul>
<p>정리하면, 이번 이슈는 Tailwind CSS의 문제가 아니라 tailwind-merge가 프로젝트의 커스텀 typography 토큰을 모르고 있었던 것이 원인이었고, 가장 좋은 해결책은 토큰 체계를 유지한 채 merge 설정을 확장하는 것이었습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[여행/체험 예약 플랫폼] 프로젝트 Init]]></title>
            <link>https://velog.io/@ann-algorithm/%EC%97%AC%ED%96%89%EC%B2%B4%ED%97%98-%EC%98%88%EC%95%BD-%ED%94%8C%EB%9E%AB%ED%8F%BC-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-Init</link>
            <guid>https://velog.io/@ann-algorithm/%EC%97%AC%ED%96%89%EC%B2%B4%ED%97%98-%EC%98%88%EC%95%BD-%ED%94%8C%EB%9E%AB%ED%8F%BC-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-Init</guid>
            <pubDate>Sun, 19 Apr 2026 08:04:48 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-여행체험-예약-플랫폼-재구축-프로젝트">📌 여행/체험 예약 플랫폼 재구축 프로젝트</h1>
<p>기존에 부트캠프에서 진행했던 프로젝트를 기반으로 전반적인 구조를 리팩토링하는 프로젝트입니다.</p>
<p>단순한 코드 개선을 넘어, 부족했던 부분은 보완하고 잘 만들어진 구조는 적극적으로 차용하여 더 완성도 높은 서비스로 발전시키는 것을 목표로 합니다.</p>
<p>또한 이번 프로젝트에서는 단순 구현에 그치지 않고,
이전에 놓쳤던 개념이나 이해가 부족했던 부분들을 다시 짚으며 기술적 이해도를 확실히 높이는 것에 집중합니다.</p>
<h1 id="📌-기술-스택">📌 기술 스택</h1>
<ul>
<li>프레임워크: Next.js 15 (App Router)</li>
<li>언어: TypeScript 5</li>
<li>스타일링: Tailwind CSS</li>
<li>패키지 매니저: npm</li>
</ul>
<h1 id="📌-목표">📌 목표</h1>
<p><strong>1. 상태 관리</strong></p>
<ul>
<li>전역 상태 관리 라이브러리 도입 및 구조 설계</li>
<li>기존 props drilling 구조 개선</li>
<li>클라이언트 상태와 서버 상태의 역할 분리</li>
</ul>
<p><strong>2. 서버 통신</strong></p>
<ul>
<li>API 호출 및 데이터 관리 방식 개선</li>
<li>비동기 상태 처리 및 캐싱 전략 학습</li>
</ul>
<p><strong>3. 테스트 코드 작성</strong></p>
<ul>
<li>단위 테스트 및 컴포넌트 테스트 작성</li>
<li>테스트 가능한 구조 설계 경험</li>
</ul>
<h1 id="📌-app-router--react-query-캐싱-전략-설계">📌 App Router + React Query 캐싱 전략 설계</h1>
<h2 id="🎯-왜-이-구조를-선택했는가">🎯 왜 이 구조를 선택했는가</h2>
<p>App Router에서는 기본적으로 fetch 기반의 캐싱이 제공됩니다.(물론 뭐 설정 등에 따라 또 달라지지만)
하지만 다음과 같은 경우에는 한계가 있습니다.</p>
<ul>
<li>사용자별로 달라지는 개인화 데이터</li>
<li>무한 스크롤 / 동적 데이터</li>
<li>클라이언트에서 지속적으로 상태가 변경되는 경우</li>
</ul>
<p>그래서 이번 프로젝트에서는 다음과 같은 역할 분리를 선택했습니다:</p>
<ul>
<li>공용 데이터 → Next.js fetch 캐싱</li>
<li>동적 / 개인화 데이터 → React Query</li>
</ul>
<p>핵심은
<span style="background-color:#FFC0CB"><strong>서버에서 미리 데이터를 준비하고, 클라이언트에서 그대로 재사용하는 구조</strong>입니다.
</span></p>
<h2 id="🔄-데이터-흐름">🔄 데이터 흐름</h2>
<h3 id="1-서버에서-prefetch">1. 서버에서 prefetch</h3>
<p>서버 컴포넌트에서 React Query를 이용해 데이터를 미리 가져옵니다.</p>
<pre><code class="language-ts">const queryClient = new QueryClient()

await queryClient.prefetchQuery({
  queryKey: [&#39;posts&#39;],
  queryFn: getPosts,
})</code></pre>
<p>👉 이 단계에서 데이터는 React Query 캐시에 저장된 상태입니다.</p>
<h3 id="2-dehydrate-서버-→-클라이언트">2. dehydrate (서버 → 클라이언트)</h3>
<pre><code class="language-ts">&lt;HydrationBoundary state={dehydrate(queryClient)}&gt;
  &lt;Posts /&gt;
&lt;/HydrationBoundary&gt;</code></pre>
<p>서버에서 만든 캐시를 
직렬화해서 클라이언트로 전달</p>
<p>👉 이 과정이 없으면 클라이언트는 다시 요청하게 됩니다.</p>
<h3 id="3-hydrate--usequery-클라이언트">3. hydrate + useQuery (클라이언트)</h3>
<pre><code class="language-ts">&#39;use client&#39;

const { data } = useQuery({
  queryKey: [&#39;posts&#39;],
  queryFn: getPosts,
})</code></pre>
<p>이미 캐시가 존재하기 때문에
즉시 데이터 사용 가능
이후 필요하면 자동으로 refetch</p>
<h2 id="☑️-이-구조의-장점">☑️ 이 구조의 장점</h2>
<p><strong>1. 빠른 초기 렌더링</strong>
서버에서 데이터 준비 → 바로 렌더링</p>
<p><strong>2. 네트워크 요청 최소화</strong>
Hydration을 통해 초기 중복 요청을 줄일 수 있으며, staleTime 설정에 따라 백그라운드 재검증 여부를 제어할 수 있음</p>
<p><strong>3. 자연스러운 UX</strong>
캐시된 데이터 즉시 표시
백그라운드에서 최신 데이터 갱신 (SWR 패턴)</p>
<p><strong>4. 상태 공유</strong>
props drilling 없이 useQuery로 데이터 접근</p>
<h2 id="☑️-그래서-언제-react-query를-사용할-것인가">☑️ 그래서... 언제 React Query를 사용할 것인가</h2>
<p>위에서 말했듯이 모든 데이터에 사용하는 것은 오히려 비효율적입니다.</p>
<h4 id="✅-react-query가-적합한-경우">✅ React Query가 적합한 경우</h4>
<ul>
<li>무한 스크롤</li>
<li>검색 결과</li>
<li>사용자 맞춤 데이터</li>
<li>자주 변경되는 데이터</li>
</ul>
<h4 id="❌-next-fetch가-더-적합한-경우">❌ Next fetch가 더 적합한 경우</h4>
<ul>
<li>SEO가 중요한 페이지</li>
<li>공용 데이터 (상품 목록, 상세 페이지 초기 데이터)</li>
<li>자주 변하지 않는 데이터</li>
</ul>
<h1 id="📌-초기-설정">📌 초기 설정</h1>
<p><strong>1. 코드 스타일 관리</strong></p>
<ul>
<li>ESLint 설정</li>
<li>Prettier 설정</li>
</ul>
<p><strong>2. 디자인 시스템 기초 작업</strong></p>
<ul>
<li>디자인 토큰 정의<ul>
<li>색상 팔레트 구성</li>
<li>폰트 매핑 설정</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Page Router 핵심 정리 (2)]]></title>
            <link>https://velog.io/@ann-algorithm/Page-Router-%ED%95%B5%EC%8B%AC-%EC%A0%95%EB%A6%AC-2</link>
            <guid>https://velog.io/@ann-algorithm/Page-Router-%ED%95%B5%EC%8B%AC-%EC%A0%95%EB%A6%AC-2</guid>
            <pubDate>Tue, 14 Apr 2026 16:57:26 GMT</pubDate>
            <description><![CDATA[<p>완강 후 정리하려니, 여기가 어느 부분인지...</p>
<p><a href="https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-nextjs/dashboard?cid=333250">한 입 크기로 잘라먹는 Next.js</a></p>
<h1 id="📌-사전-렌더링과-데이터-페칭">📌 사전 렌더링과 데이터 페칭</h1>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/e1e185a5-18c1-487c-a973-12376fc72322/image.png">

<p>지난 시간까지는 목데이터를 통해 데이터를 렌더링했음</p>
<blockquote>
<p>이번엔 실제 데이터를 가져오자!
서버로부터 실제 데이터를 불러오는 데이터 페칭 기능을 살펴볼 것</p>
</blockquote>
<p>그 전에, 넥스트 이전, 그러니까 기존 리액트에서는 어떻게 백엔드 서버로부터 데이터를 페칭했는가?</p>
<h2 id="☑️-리액트에서의-데이터-페칭">☑️ 리액트에서의 데이터 페칭</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/3a4a32b9-1a68-494e-9c06-14030c4b7272/image.png" width=400>

<p>먼저 페이지 역할을 하는 컴포넌트를 만듦</p>
<br>

<h4 id="1-불러온-데이터를-보관할-state-생성">(1) 불러온 데이터를 보관할 state 생성</h4>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/c8d37beb-c3fc-4d92-b16c-705426470642/image.png" width=500>

<p>이 컴포넌트 내부에 서버로부터 불러올 데이터를 저장하기 위한 state를 하나 만듦</p>
<h4 id="2--데이터-페칭-함수">(2)  데이터 페칭 함수</h4>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/e13e88a7-8816-4a42-b1e3-549b2d89ffc3/image.png" width=500>

<p>fetchDate라는 함수를 만들어서,
해당 함수 내에서 fetch 메서드를 사용해서 서버로부터 데이터를 불러온 다음
그 데이터를 setState 메서드를 통해 현재의 state 값으로 업데이트</p>
<h4 id="3-컴포넌트-마운트-시점에-fetchdata-호출">(3) 컴포넌트 마운트 시점에 fetchData 호출</h4>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/c12522ab-4547-4304-9078-460797dd2b43/image.png" width=300>

<p>데이터 페칭 함수까지 만들었다면,
컴포넌트가 마운트 되었을 때 useEffect를 호출해서 
딱 1번만 위에서 만든 fetchData를 호출
➡️ 데이터를 페칭한 다음 state에 보관할 수 있게 설정</p>
<h4 id="4-데이터-로딩-중일-때의-예외-처리">(4) 데이터 로딩 중일 때의 예외 처리</h4>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/81051e37-95f3-4083-a406-318013d7772e/image.png" width=400>

<p>데이터 페칭이 완료되지 않았을 때의 예외처리를 위해
조건문을 통해 컴포넌트가 로딩 중임을 표시</p>
<h3 id="정리하자면">정리하자면,</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/52fc3854-2177-43a3-8d46-e39510e7ae4b/image.png">

<ol>
<li>불러온 데이터를 보관할 state 생성</li>
<li>데이터 페칭 함수 생성</li>
<li>컴포넌트 마운트 시점에 fetchData 호출</li>
<li>데이터 로딩 중일 때의 예외 처리</li>
</ol>
<p>와 같은 프로세스에는 단점이 있음</p>
<p><span style="background-color:#FFEBCD"><strong>초기 접속 요청부터 백엔드 서버로부터 불러온 데이터가 로딩되기까지 오래 걸림</strong></span></p>
<p><em>WHY?</em>
<span style="background-color:#FFC0CB">백엔드 서버에게 보내는 데이터 요청이, 컴포넌트가 마운트 된 이후에나 발생</span>
<span style="background-color:#E2D6FF">즉, 데이터 요청 자체가 늦게 시작하기 때문에 데이터를 불러오는 속도도 늦어짐</span></p>
<br>

<p>CSR(클라이언트 사이드 렌더링)은 브라우저가 렌더링을 직접 처리
그러니까 실제로 화면에 그려지기까지,
HTML이 렌더링도 하고, 자바스크립트도 직접 실행하느라 FCP가 늦어진다고 했음</p>
<br>

<p>위처럼 컴포넌트가 마운트된 이후에서야,
백엔드 서버에게 데이터를 요청하도록 코드를 작성하면,
<span style="background-color:#E2D6FF">느린 FCP를 거치고 나서야 백엔드 서버에게 데이터를 요청하기 때문에
데이터의 로딩이 완료되기까지 추가적인 시간이 더 소요됨</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/1436d0c7-01b2-4ea4-8354-52edb11be850/image.png">

<p>FCP 이후에 또 한 번 데이터를 기다려야 하는 시간이 추가되기 때문에
안 그래도 화면도 늦게 나타나는데,
데이터까지 기다리는 불편함이 초래</p>
<br>

<p>하지만 넥스트는 느린 FCP 문제를 해결하기 위해 사전 렌더링이란 방식으로 동작</p>
<h2 id="☑️-넥스트에서의-데이터-페칭">☑️ 넥스트에서의 데이터 페칭</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/6fa628f5-1562-4964-b029-7469f51fc4b4/image.png">

<p><span style="background-color:#FFEBCD">완성된 HTML을 바로 사용자에게 보여주므로 리액트의 느린 FCP 문제 해결</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/8dc2996e-70cd-4824-9e00-efd74173e3bf/image.png">

<p>이렇게 사전 렌더링을 진행하는 과정에서</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/193eda01-bc4a-4cec-9d98-c2551b3e15a6/image.png">


<p><span style="background-color:#FFEBCD">백엔드 서버로부터 현재 페이지에서 필요한 데이터를 미리 불러오도록 설정</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/8d91cea9-bff2-426b-beba-acb67f6c7404/image.png">

<p>그래서 훨씬 빠른 타이밍에 백엔드 서버로 데이터를 요청하고, 응답받을 수 있고</p>
<p>이로 인해 <span style="background-color:#FFE4E1">브라우저에 전달하는 HTML파일에
이미 백엔드 서버로부터 불러온 데이터가 다 포함되어 있음</span>
➡️ <span style="background-color:#FFC0CB">추가적인 로딩 없이 한 번에 보여주기 가능</span></p>
<h2 id="☑️-리액트-vs-넥스트">☑️ 리액트 vs 넥스트</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/da3608de-978c-47e9-9a02-955d62526ce3/image.png">

<p>결국결국결국</p>
<p>넥스트가 더 빠르게 렌더링된다는 말씀!</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/9505f033-0cc1-4616-b3e2-37fc65445d6c/image.png">


<p>데이터를 요청하는 시점이 확연히 다름!</p>
<h2 id="☑️-넥스트의-사전-렌더링">☑️ 넥스트의 사전 렌더링</h2>
<p>넥스트에서는 접속 요청과 동시에 백엔드에 데이터를 요청한다고 했음</p>
<p>그런데 만약 이 데이터 용량이 너무 크거나
백엔드 서버의 상태가 너무 좋지 않아서 응답이 오래 걸리면
유저는 어떻겠음?</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/64db275b-7d57-4858-80d6-f48ee5cb7638/image.png">

<p>당연히 사전 렌더링이 완료되기 전까지 꽤 오랜 시간 기다려야 함</p>
<br>

<p>그런데 넥스트는 이런 식으로 오래 걸릴 것으로 예상되는 페이지의 경우</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/3d74c69c-db47-40d4-9acc-a16fb6fdc54d/image.png">

<p>특별히 빌드 타임에
그러니까 <span style="background-color:#FFEBCD">빌드하는 시간에 사전 렌더링을 미리 하도록 설정 가능</span></p>
<br>

<p>이처럼 여러 사전 렌더링 방식이 있음</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/541c43e7-e20c-4136-b1cc-c0bbe7734b48/image.png">

<p>하나하나 알아보자</p>
<hr>
<h1 id="📌-ssr-1-소개-및-실습">📌 SSR 1. 소개 및 실습</h1>
<blockquote>
<p><strong>서버 사이드 렌더링 (SSR, Server Side Rendering)</strong></p>
</blockquote>
<ul>
<li>가장 기본적인 사전 렌더링 방식</li>
<li>요청이 들어올 때마다 사전 렌더링을 진행함</li>
</ul>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/c4b6a385-847a-4c06-8204-91fe587bcb9d/image.png">

<p>가장 기본적인 사전 렌더링을,
넥스트에서는 SSR이라고 함</p>
<br>


<p><span style="background-color:#E0FFFF">pages 폴더 아래에 index.tsx 파일 아래에서
이제 이 페이지를 SSR 동식으로 동작하도록 해보자</span></p>
<h2 id="☑️-getserversideprops-함수">☑️ getServerSideProps 함수</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/6ea293a8-40dd-47b2-ae6a-8424a91a374c/image.png" width=500>

<p><span style="background-color:#FFEBCD">이 함수를 선언하면, 이제 이 인덱스 페이지는 SSR 방식으로 사전 렌더링이 이루어짐</span></p>
<br>

<p>브라우저에서 <code>localhost:3000</code> 경로로 인덱스 페이지를 요청하여,
<span style="background-color:#FFE4E1">넥스트 서버가 사전 렌더링을 할 때,</span></p>
<p><span style="background-color:#FFEBCD">이 페이지 컴포넌트보다 먼저 실행되어서
인덱스 페이지 컴포넌트에 필요한 데이터를 백엔드 서버로부터 불러온다든가 하는 기능</span>
(백엔드, 서드 파티로부터 데이터를 불러옴)</p>
<p><span style="background-color:#FFFACD">그리고 페이지 컴포넌트 실행</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/869efd51-37ba-4431-b87f-f476759d30a8/image.png" width=500>
(이미 코드에 전부 다 작성되어 있어 강의 자료를 가져옴)

<p>아무튼 이렇게 미리 데이터를 가져와 리턴해서,
이 객체를 홈 컴포넌트에 전달 가능</p>
<p>이 <span style="background-color:#FFEBCD">리턴값은
반드시 props라는 객체 프로퍼티를 포함하는 단 하나의 객체</span></p>
<p>그래야 Next.js가 이 객체를 읽어와 페이지 역할을 하는 컴포넌트에 전달 가능
일종의 프레임워크 문법</p>
<br>

<p>그럼 이 객체를 홈 컴포넌트(페이지 컴포넌트)에선 어떻게 받느냐
기존 리액트에서 props 받아오듯 받아오면 됨</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/1fc48ddb-3fe0-4918-9ed6-f5c5518b2710/image.png">

<p>(타입스크립트 사용 중이니 타입 오류를 해결하기 위해 any로 정의)</p>
<p>출력해보면,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/b85489bb-ef64-4dd5-9f1a-b413a5cc9d7b/image.png" width=500>

<p>hello가 잘 출력됨</p>
<br>

<h2 id="☑️-주의할-점">☑️ 주의할 점</h2>
<h4 id="getserversideprops-함수-실행">getServerSideProps 함수 실행</h4>
<p><span style="background-color:#FFEBCD">이 함수는 사전 렌더링을 하는 과정에서 딱 한 번만 실행이 되고,
서버 측에서만 실행됨!</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/b118c0f5-74eb-4eca-b5f9-fb250f969e26/image.png">

<p>그래서 <span style="background-color:#FFC0CB">이 함수에서 console.log 함수를 호출해봐야,
브라우저에선 출력되지 않음</span></p>
<p>대신 터미널에는 출력됨</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/14d0d3ff-8fdf-44d1-92bf-a1d64fb76fb7/image.png">

<p>(첨부한 코드는 강사님 꺼, 밑에 파란 터미널은 나의 코드(강사님 코드와 같음)로 실행한 결과)</p>
<br>

<p>따라서 이 함수 내에서
<span style="background-color:#FFEBCD">브라우저 환경에서만 이용할 수 있는 window.location을 통해 접근하려고 하면 오류 발생</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/435257a8-f0f4-457e-9239-62cecbf7fe49/image.png">

<p>자바스크립트의 window는 브라우저인데,
<span style="background-color:#E2D6FF">서버 환경에서만 실행되는 getServerSideProps 함수에서는 브라우저를 읽을 수 없음</span></p>
<p>따라서 window는 undefined가 되는데,
undefined에 점 표기법으로 location이란 프로퍼티를 꺼내려고 하니까 오류 발생</p>
<br>

<p>오류 화면을 보면 윈도우가 정의되지 않았다는 메시지가 나옴
➡️ 그래서 window.alert(), window.confirm() 같은 것도 다 안 됨</p>
<br>

<h4 id="home-컴포넌트의-실행">Home 컴포넌트의 실행</h4>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/0e3d3a46-11e5-4922-8495-e78fff1b458b/image.png">

<p><span style="background-color:#E2D6FF">Home 컴포넌트 또한 사실 서버에서 먼저 실행된 다음 브라우저에서 한 번 더 실행됨</span></p>
<ul>
<li>브라우저로부터 접속 요청을 받았을 때 <span style="background-color:#FFE4E1">사전 렌더링을 위해 서버 측에서 한 번 Home 컴포넌트 실행</span></li>
<li>브라우저에서 자바스크립트 번들 형태로 전달 되어서, <span style="background-color:#FFE4E1">브라우저 측에서 실행될 때(즉 하이드레이션 과정이 실행될 때) 한 번 더 실행</span></li>
</ul>
<p>➡️ 서버에서 한 번, 브라우저에서 한 번, 홈 컴포넌트는 총 두 번 실행됨</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/4351bc89-f54f-4ef3-91cd-b18c931c9128/image.png">

<br>

<p>요점이 뭐냐면,
<span style="background-color:#E2D6FF">Home 컴포넌트도 서버에서 한 번은 실행될 테니까
따라서 Home 컴포넌트도 window 같은 객체를 사용할 수 없음</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/c05f9446-80a2-4fe0-847e-622c9c40b7d9/image.png">

<p>서버에서는 window가 undefined가 되어서,
window의 location, 즉 undefined를 호출하는 꼴이 되어버리기 때문에 안 됨</p>
<br>

<h2 id="☑️-그럼-브라우저에서만-실행되는-코드를-작성하려면">☑️ 그럼 브라우저에서만 실행되는 코드를 작성하려면?</h2>
<p><span style="background-color:#FFC0CB">여러가지 방법이 있지만, 가장 쉬운 건 useEffect
</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/ef61ddf8-618a-470a-aa0c-2932c7eadf19/image.png">

<p><span style="background-color:#FFC0CB">이 코드는 서버에서 실행하지 않음</span></p>
<p><span style="background-color:#E2D6FF">조건 자체가 컴포넌트가 마운트된 이후,
  그러니까 화면에 나타난 이후 실행하는 함수를 만드는 것이라서,</span>
➡️ 서버 측에서 실행되지 않고 브라우저에서만 실행됨</p>
<h2 id="☑️-getserversideprops로부터-전달받는-props의-타입은">☑️ getServerSideProps로부터 전달받는 props의 타입은?</h2>
<p>대부분 타입 정의는 이미 넥스트에서 제공하고 있음</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/b3650eb4-e547-4e6c-b1fa-ea483aeb9f93/image.png" width=600>

<p>그래서 <strong>InferGetServerSidePropsType을 import</strong>하고,
<strong>제네릭으로 만든 getServerSideProps를 넣어주면,</strong>
이 타입이 방금 만든 <span style="background-color:#E2D6FF">getServerSideProps 함수의 반환값 타입을 자동으로 추론</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/68ba10a0-b2e1-4586-9404-b72311a26cad/image.png">

<p>잘 정의되었는지 확인해보기 위해서
구조분해 할당으로 받았던 매개변수 data 대신 아예 props로 바꿔주고,</p>
<p>마우스를 올려 타입이 어떻게 정의되었는지 보면
<span style="background-color:#FFE4E1">data는 string으로 반환값 타입이 잘 정의됨</span></p>
<hr>
<h1 id="📌-ssr-2-실습">📌 SSR 2. 실습</h1>
<p>일단 백엔드 서버 가동</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/6bc273ae-17d5-4b3a-be4e-fd029127b42e/image.png">

<br>

<h2 id="☑️-인덱스-페이지">☑️ 인덱스 페이지</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/b81f6587-12a8-4bf6-bea2-f942283fb692/image.png">

<p>이 두 가지 API 호출을 담당할 함수를 만들자
두 API 모두 index.tsx에 작성</p>
<h3 id="book-모든-도서-불러오기">/book 모든 도서 불러오기</h3>
<p>근데 index.tsx에 모두 작성하면 너무 복잡하니까
fetch하는 함수는 분리하자</p>
<h4 id="fetch-booksts">fetch-books.ts</h4>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/cb1c32ca-c472-4b51-a5b5-d053fd1d1108/image.png">

<p>lib 폴더를 만들어주고
모든 도서를 불러오는 함수를 만들자</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/36b44af1-9a38-4319-a2ea-f6e997f4361c/image.png">

<ul>
<li>비동기 함수를 하나 만들어줌</li>
<li>서버의 주소를 작성함</li>
<li>API를 호출하는 코드를 작성하기 위해, 실패했을 수도 있으니 try문</li>
</ul>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/f5c03fe5-ac3c-4c38-a0b4-e13f77d5c280/image.png">

<ul>
<li>fetch 메서드 호출</li>
</ul>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/1cdfae14-9077-4941-9a32-e80385bd3d66/image.png">

<ul>
<li>fetch 메서드가 실패할 수도 있으니,
조건문으로 예외처리</li>
</ul>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/709df48a-2d00-46ad-86a8-ca752d2371c6/image.png">

<p>이 조건문에 걸리지 않으면 요청이 성공일 테니까,
fetch 메서드의 응답값을 json 포맷으로 변환해서 return</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/e180519b-fb9d-4c3b-b217-34bae16e6183/image.png">

<p>catch 문은,
console.error 메서드로 에러 메시지를 출력하고
빈 배열을 반환</p>
<br>

<p>간단하지만 예외처리를 완료했으니
fetchBooks 함수의 반환값 타입도 정의하자면
이 함수는 서버로부터 모든 데이터를 불러와서 반환하는 함수이니 비동기로 반환</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/43d75638-06f6-465b-99c4-1e854894d499/image.png">

<p>Book의 타입을 interface로 정의하고,
이 타입을</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/f930ee27-ad48-487b-91c6-c91f7ad0c41a/image.png">

<p>반환값 타입으로 프로미스와 제네릭으로 명시
근데 여러 개 불러올 거니까 배열로</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/a7daa9e9-c2ea-4112-9c6f-de7b2bcf7e17/image.png" width=700>

<p>이처럼 fetchBooks 함수 완성</p>
<h4 id="indextsx">index.tsx</h4>
<p>이제 getServerSideProps에서, 위에서 만든 fetchBooks 함수를 호출</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/4ed9fab5-0eac-4eba-a65b-4c45dd1dd303/image.png">

<p>비동기 함수이니 await를 쓰면, 이 함수에도 async 키워드를 붙여야 함</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/20afd9b9-d417-4a58-aff8-f707bcc5096b/image.png">

<br>


<img src="https://velog.velcdn.com/images/ann-algorithm/post/c5484b66-e2da-4bc5-bb1b-33436be884b5/image.png">


<p>이제 이 allBooks를 props로 전달하고,
Home 컴포넌트에서 잘 받는지 console.log로 출력</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/f3a1222a-af69-4ec2-9089-cd16b014b0df/image.png">

<img src="https://velog.velcdn.com/images/ann-algorithm/post/d8c7b169-c6dc-4111-a0bc-46135d53b08c/image.png">

<p>그럼 이렇게 서버와 브라우저에 동시에 데이터가 나옴</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/ec84251f-a6da-4c5f-8ae4-502d0a9f2a00/image.png" width=500>


<p>이렇게 받은 데이터를 map 메서드로 렌더링하자</p>
<p>그럼 이제 목 데이터가 아닌
디비 서버에 등록된 모든 도서가 잘 렌더링되고 있음</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/db6d6bf0-edc9-43ab-bce3-fc4ac380d218/image.png" width=500>

<br>

<h3 id="bookrandom-랜덤추천-도서-불러오기">/book/random 랜덤(추천) 도서 불러오기</h3>
<h4 id="fetchrandombookts">fetchRandomBook.ts</h4>
<p>같은 방식으로 fetchRandomBook 함수를 분리하자</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/ef0b9fa3-5355-4464-abc7-fe5cabdb1002/image.png" width=600>

<ul>
<li>API 호출<ul>
<li>인수로는 API 주소 전달</li>
</ul>
</li>
<li>실패할 수 있으니 try-catch문<ul>
<li>조건문으로 실패 시 에러 호출해서 에러 던지고,</li>
<li>성공 시 해당 데이터를 json 형태로 return</li>
</ul>
</li>
<li>catch문은 에러를 출력하고 빈 배열 return</li>
<li>반환값 타입은 비동기로 반환하니까 Promise</li>
<li>제네릭으로 BookData 배열</li>
</ul>
<br>

<h4 id="indextsx-1">index.tsx</h4>
<p>이제 index 페이지로 넘어와서</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/72000f6f-a5ea-40ab-a70e-999bef3f2ad7/image.png">

<p>방금 만든 fetchRandomBooks로 결과값을 받아와
props로 데이터를 페이지 컴포넌트에 전달</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/44ad547b-dddb-420b-8932-208011de07d6/image.png" width=600>

<p>컴포넌트로 해당 데이터를 전달 받아서,
map 메서드로 렌더링</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/87bd68f3-3757-49fd-8087-0f8adaee8f78/image.png">

<p>이제 새로고침할 때마다 새로운 추천도서를 불러옴
index 페이지에, 필요한</p>
<h3 id="추천-도서와-랜덤-도서를-동시에-불러와보자">추천 도서와, 랜덤 도서를 동시에 불러와보자</h3>
<p>지금 요청이 어떻게 이루어짐?</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/37af1a65-cdfe-4ba5-9f13-7b8014b11e19/image.png" width=500>

<p>지금은 fetchBooks를 실행하고,
그 다음 fetchRandomBooks를 실행하는 <span style="background-color:#FFE4E1">직렬적 방식
</span></p>
<br>

<p><span style="background-color:#E0FFFF">이 모든 도서, 추천 도서 함수를 병렬로 실행해보자</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/6ac838b4-8b61-4082-9880-5bc47d9276c1/image.png">

<p>위 두 함수 호출을 지우고,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/73612489-c7c5-45f4-80ca-70c02c4ad794/image.png" width=500>

<p>const 배열로 두 데이터를 받아오고,</p>
<p>어디로부터 받아오냐면
<span style="background-color:#FFC0CB">Promise의 all이란 메서드를 호출한 결과로부터 받아옴</span></p>
<p>Promise.all 이라는 메서드는,
<span style="background-color:#FFEBCD">인수로 전달한 배열 안에 들어 있는 모든 비동기 함수를 동시에 실행시켜주는 메서드
</span></p>
<p>그래서 인수로 배열을 넣고,
첫 번째 아이템에는 fetchBooks(),
두 번째 아이템에는 fetchRandomBooks()
를 넣으면</p>
<p><span style="background-color:#E2D6FF">두 함수가 동시에 병렬로 작동하고, allBooks와 recoBooks 데이터를 동시에 불러옴</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/dc416afb-c425-4a89-a951-9a5b94cc17f5/image.png" width=700>


<p>이렇게 인덱스 페이지의 index.tsx 완성</p>
<br>

<h2 id="☑️-search-페이지">☑️ search 페이지</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/5f1ca1e6-ed4c-44f6-92d8-20b676e1b98c/image.png">

<p>여기 검색하면,
이동하게 될 search 페이지에도 검색 결과 데이터를 불러올 수 있게 설정하자</p>
<h3 id="search-페이지의-indextsx">search 페이지의 index.tsx</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/26f717a3-22d1-4291-a318-6fbe52659b38/image.png">


<p>먼저 search.tsx에 getServerSideProps 함수 추가
➡️ search 페이지는 SSR 방식으로 동작</p>
<br>

<p>이제 <span style="background-color:#E2D6FF">이 함수 내에서 검색 결과를 백엔드 서버로부터 불러와서 컴포넌트에 전달해야 함</span></p>
<br>

<p>그러려면 브라우저를 키워 주소창을 보면</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/ca0a0a15-bb59-48dd-9590-5b90668e3356/image.png">

<p>이와 같이 <span style="background-color:#FFE4E1">쿼리 스트링으로 전달된 검색어를 읽어와서
검색어에 해당하는 데이터를 백엔드 서버로부터 불러오는 기능을 만들어야 됨</span>
➡️ 그러면 <span style="background-color:#FFE4E1">getServerSideProps에서 이 검색어에 해당하는 데이터(쿼리스트링의 값)을 읽어와야 함</span>
(fetch를 여기서 해야 하니까)</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/f95083b4-0b5e-47df-9eec-3d74de176e11/image.png" width=700>

<p><strong><span style="background-color:#FFEBCD">context란 매개변수를 이용해야 함</span></strong>
이건 넥스트에서 제공</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/08a13e23-264b-423a-aaba-becec40acbb5/image.png">

<p>이 context라는 매개변수에는 현재 브라우저로부터 받은 요청에 대한 모든 정보가 다 포함되어 있음</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/9c207a38-219c-47b9-97b7-50ea8ea879c3/image.png">

<p>출력해보면 이와같이 데이터가 나오는데, 
이 중 <span style="background-color:#FFEBCD">query란 프로퍼티로 쿼리 스트링이 잘 전달됨</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/37c4fd82-e1ec-4911-84b5-afe50f7ba9cb/image.png">

<p>이걸 꺼내서 쓰자</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/dfa6541d-a5f6-4db3-bf70-2d1e251f6023/image.png" width=400>

<p>이렇게 context의 query의 q를 꺼내와서 쓸 수 있음</p>
<h3 id="api-요청-확인">API 요청 확인</h3>
<p>백엔드 서버로부터 검색어에 해당하는 검색 결과를 불러와보자</p>
<p>스웨거를 통해서 어떤 주소로 보내야 하는지 확인해 보면,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/86e24914-bd75-41a0-a2d2-c458c3622adb/image.png">

<img src="https://velog.velcdn.com/images/ann-algorithm/post/ac6bccc1-9829-4894-bbce-ec335fc0e5ff/image.png">

<p>Request URL을 확인 가능</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/3a76383d-c0e0-48c7-ba95-6e71112e2e13/image.png">

<p>Response 도 확인 가능</p>
<br>

<h3 id="fetch-booksts-1">fetch-books.ts</h3>
<p>그러니까 위 검색 API를 호출하는 새로운 함수를 만들어야 함
근데 <span style="background-color:#FFE4E1">기존 함수를 확장하자</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/2293fcd0-d369-4536-a931-ea0a20e3bc91/image.png">


<p>만들었던 fetchBooks 함수에 매개변수 q를 추가하고,
전달받지 않을 수도 있으니 선택적 프로퍼티로 설정
➡️ 전달받지 않아도 됨</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/a6420e72-9eb0-40ef-b544-4398dadba9ed/image.png" width=500>

<p>만약 q가 있다면
검색 결과를 불러오는 API주소로 요청하기 위해 url 수정</p>
<p>이때 url의 선언도 const에서 let으로 수정함
요청 url에 쿼리 스트링이 있다면, 해당 쿼리 스트링을 붙이도록 수정</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/8e4fc653-1f7c-4ac7-9ec7-44d0d847aeaf/image.png">

<p>인덱스 페이지에서는 q없이 이 함수를 호출하고,</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/34c20366-bd7b-4086-8730-28ceb2d39280/image.png">

<p><span style="background-color:#E2D6FF">서치 페이지에서는 검색어를 인수로 전달해서 검색 결과를 불러옴</span>
이 <span style="background-color:#B0E0E6">인수는 string이거나, stringArray거나, undefined일 수 있으니 string으로 단언
</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/9b834fe8-b783-4d90-b625-985b7154a3fe/image.png" width=700>

<p>이제 이 결과를 props 객체로 페이지 컴포넌트에 전달</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/ab7e30fc-d31a-404b-ba2c-a2a4d6a2653e/image.png" width=800>

<p>페이지 컴포넌트에서는,
구조분해 할당으로 해당 객체를 받아오며
<span style="background-color:#E6E6FA">위에서 살펴 본 넥스트의 내장 타입을 활용하고 제네릭으로 typeof까지 넣어서 타입 추론</span></p>
<p>기존 mock데이터는 지워주고,
받아온 books 데이터를 map 메서드로 이용해서 렌더링</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/275b2b41-8444-4135-a0f2-b2b794f271cc/image.png" width=700>

<p>이렇게 search 페이지의 index.tsx 완성</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/7077754e-c184-4f83-9f5c-bb87def9810a/image.png">

<p>서치 페이지의 기능이 잘 구현됨</p>
<h2 id="☑️-book-페이지">☑️ book 페이지</h2>
<h3 id="book-페이지의-indextsx">book 페이지의 index.tsx</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/ac3e7323-b828-4ffa-bada-118c1f2bffd7/image.png">

<p>이 getServerSideProps 함수를 추가하여
북 페이지도 SSR 방식으로 동작</p>
<br>

<p>북 페이지는,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/4cc014f0-e841-4fca-9b4c-d625396f0274/image.png">

<p>URL 파라미터로 불리는 값으로 전달되는 
이 도서의 아이디를 기준으로 해당 도서의 데이터를 불러와야 함</p>
<ul>
<li>1 ➡️ 1번 도서 데이터</li>
<li>13 ➡️ 13번 도서 데이터</li>
</ul>
<p>이 함수 내에서 URL 파라미터를 불러와야 함</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/7bc6b7a4-d928-4d59-a601-04beb841fe31/image.png" width=800>

<p>쿼리스트링처럼, URL파라미터를 불러오기 위해
context 사용</p>
<p><span style="background-color:#FFEBCD">URL 파라미터는 context의 params라는 프로퍼티에 있음</span></p>
<p>근데 <span style="background-color:#FFE4E1">URL 파라미터가 없을 수도 있으므로
! 단언으로 값이 있을 거라고 단언</span></p>
<p>이렇게 단언해도 괜찮은 이유는
<span style="background-color:#E2D6FF">이 페이지인 [id].tsx 자체가 URL 파라미터가 하나 있어야 접근 가능한 페이지이기 때문에</span>
이 페이지에 URL 파라미터가 아예 없다는 건 말이 되지 않기 떄문에
undefined가 아닐 거라고 단언</p>
<h3 id="api-요청-확인-1">API 요청 확인</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/3ab6b29c-e25a-4198-9485-a4838a02d22d/image.png">

<p>이 API를 사용해서 특정 도서의 데이터를 불러와보자</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/a011d00b-5dbc-489f-819c-545f717d5293/image.png">

<p>리퀘스트 형식 확인하고,</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/3b40d576-fe4e-437f-9659-ca8eb378737f/image.png">

<p>리스폰스 형식도 확인</p>
<h3 id="fetch-one-bookts">fetch-one-book.ts</h3>
<p>이제 fetch 함수를 만들자</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/72eaba95-7b4c-4026-9b69-b6fd38981cf2/image.png">

<ul>
<li>이 함수가 불러와야 하는 도서의 아이디를 매개변수 아이디로 받아오게 함<ul>
<li>이 매개변수의 타입은 number</li>
</ul>
</li>
<li>API 요청 주소 명시</li>
<li>마찬가지로 fetch 메서드를 통해서 url을 전달하고, try-catch로 예외처리<ul>
<li>근데 <span style="background-color:#FFC0CB">에러일 때 빈 배열을 반환하면 안 됨</span>
➡️ null 반환</li>
</ul>
</li>
<li><span style="background-color:#E6E6FA">반환값 타입은 비동기니까 Promise로, BookData 타입 값으로 반환하는데,
실패할 수도 있으니까 null값으로도 반환할 수 있게 유니온 타입</span></li>
</ul>
<br>

<p>다시 북 페이지로 돌아가,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/3028d88c-0e0b-4e48-98ea-6ba1501b31d7/image.png" width=700>

<p><span style="background-color:#FFEBCD">context로 가져온 URL파라미터에 해당하는 id는 기본적으로 string 타입을 갖기 때문에,
인수로 전달할 때는 number로 형 변환</span></p>
<p>이제 fetch메서드를 통해서 받아온 book 데이터를 props로 전달</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/6bd460b8-d450-466f-bf4e-509a5a3dfa9d/image.png">

<p>페이지 컴포넌트에서는 해당 book 데이터를 props로 받아서
구조분해 할당으로 각 프로퍼티에 해당하는 데이터를 가져오는데,
타입 오류가 발생!</p>
<p><span style="background-color:#FDF5E6">왜냐하면 이 book이 null일 수도 있기 때문에!</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/5f8ec442-b44e-4d80-b1e0-1487b0c329a0/image.png">

<p>그래서 예외처리를 추가하면, 타입 오류가 사라지고 북 페이지에 해당하는 데이터를 가져옴</p>
<p>렌더링은 기존 목 데이터 사용하던 것에서 수정할 필요가 없음</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/19d7d377-4b43-48ec-b942-d421e5519677/image.png" width=700>

<p>이렇게 book페이지의 index.tsx 완성</p>
<br>

<p>이제 서버 사이드 렌더링 방식(SSR)을 통해 모든 페이지 작동 완료</p>
<hr>
<h1 id="📌-ssg-1-소개">📌 SSG 1. 소개</h1>
<h2 id="☑️-ssr-복습">☑️ SSR 복습</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/7537ee75-1608-447c-af99-bf5d8ad9d20f/image.png">

<p><span style="background-color:#FFEBCD">한번 응답을 마친 페이지도,
다시 요청이 들어오면
새롭게 다시 사전 렌더링을 진행
</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/51f0719f-d625-4362-99d9-aeacbfec1524/image.png">

<p>매번 백엔드 서버에 새로운 데이터를 요청</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/ce998ad5-1b64-417d-8e9f-576a3f5c63d5/image.png">

<p><span style="background-color:#FFC0CB">그렇게 되면 최신으로 데이터를 유지할 수 있지만,</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/04d65c5e-072b-41f0-906c-fdc4a0802dd2/image.png">

<p><span style="background-color:#E2D6FF">데이터의 용량이 너무 크거나 서버 상태가 좋지 않으면
데이터 응답 속도가 너무 느려짐</span></p>
<p>만약 이 응답 속도가 10초가 소요된다면,
이거 때문에 10초 동안 꼼짝없이 브라우저 로딩을 기다려야 함
➡️ <span style="background-color:#FFEBCD">매번 새롭게 사전 렌더링을 진행, 데이터도 매번 새롭게 불러오기 때문에 생기는 특징</span></p>
<h2 id="☑️-ssg-정적-사이트-생성">☑️ SSG 정적 사이트 생성</h2>
<blockquote>
<p><strong>정적 사이트 생성 (SSG, Static Site Genreation)</strong></p>
</blockquote>
<ul>
<li>SSR의 단점을 해결하는 사전 렌더링</li>
<li>빌드 타임에 페이지를 미리 사전렌더링 해둠</li>
</ul>
<p><span style="background-color:#FDF5E6">SSR이라는 방식의 치명적인 단점을 해결하는 새로운 사전 렌더링 방식</span>
<span style="background-color:#FFEBCD">빌드 타임에 미리 페이지를 사전 렌더링을 한다는 특징</span>이 있음</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/41590ecb-6e19-47b0-a3fa-4278dde0c264/image.png">

<p><span style="background-color:#FFEBCD"><code>npm run build</code>일 때
사전 렌더링을 진행해서 딱 한번만 페이지를 생성</span>
<span style="background-color:#FFC0CB">그리고 다시는 새롭게 페이지를 생성하지 않음
</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/e5a4a849-cfec-4a27-a592-9a478a6c08ef/image.png">

<p>빌드 타임에 만들어두었던 HTML 페이지를 응답</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/21f47e1a-c3f6-484a-8bef-5aa01d24d43a/image.png">

<p>그러면 <span style="background-color:#E2D6FF">사용자는 매우 빠르게 완성된 화면을 봄
</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/6460cb82-8d11-479f-8b44-2b8444c39d1f/image.png">

<p>백엔드와의 상호작용이 필요하다고 하더라도
이 과정이 특정 상황으로 인해서 오래 걸린다고 해도</p>
<p><span style="background-color:#E2D6FF">모든 상황은 서버가 가동되기 이전인 빌드 타임에만 일어나기 때문에,</span>
<span style="background-color:#FFEBCD">빌드 타임 이후 발생하는 접속 요청에는 굉장히 빠른 속도로 이미 만들어진 페이지를 바로 응답 가능</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/6d029691-550b-489f-bec0-3ea245f5fcd7/image.png">

<p>서버로부터 받은 HTML 페이지 렌더링</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/8c7d3765-c3af-43e5-8e99-5112daff5a9d/image.png">

<p>이후 과정은 앞서 살펴본 SSR 방식과 동일하게 진행됨</p>
<ul>
<li>서버로부터 받은 HTML 페이지를 화면에 렌더링</li>
<li>넥스트 서버가 JS 번들을 후속으로 전달하면 브라우저에서 이를 실행해서 하이드레이션
➡️ 상호작용이 가능한 페이지로 거듭남</li>
</ul>
<br>

<h2 id="☑️-ssg의-장단점">☑️ SSG의 장단점</h2>
<h3 id="ssg의-장점">SSG의 장점</h3>
<blockquote>
<p>사전 렌더링에 많은 시간이 소요되는 페이지더라도
사용자의 요청에는 매우 빠른 속도로 응답 가능</p>
</blockquote>
<h3 id="ssg의-단점">SSG의 단점</h3>
<blockquote>
<p>매번 똑같은 페이지만 응답함
최신 데이터 반영은 어려움</p>
</blockquote>
<p>빌드 타임 이후에는 다시는 페이지를 새로 사전 렌더링하지 않기 때문에
(즉, 페이지를 새로 생성하지 않기 때문에)</p>
<p>접속 요청을 보내더라도 매번 똑같은 페이지만 응답하여 최신 데이터 반영이 어려움</p>
<p>따라서,
<span style="background-color:#E2D6FF">데이터가 자주 업데이트되지 않는 정적인 페이지에 적합한 사전 렌더링 방식
</span></p>
<hr>
<h1 id="📌-ssg-2-정적-경로에-적용하기">📌 SSG 2. 정적 경로에 적용하기</h1>
<blockquote>
<p>서버 켜기</p>
</blockquote>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/49622465-8c78-4eab-9767-dc125dd08380/image.png">

<p>넥스트 서버 켜기</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/14856bd4-543d-4f0e-a4f5-74f2485186d5/image.png">

<p>백엔드 서버 켜기</p>
<h2 id="☑️-인덱스-페이지-1">☑️ 인덱스 페이지</h2>
<blockquote>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/23927299-4cf9-4601-b5c6-fd048bb571f1/image.png">

<p>getServerSideProps 함수를 export로 내보내면, 해당 파일이 담당하는 페이지는 SSR로 담당한다고 했음
<span style="background-color:#E2D6FF">특정 페이지를 SSG 방식으로 작동시키는 방법도 비슷함
</span></p>
</blockquote>
<p>이 함수의 이름을 바꾸면 됨</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/793b2701-9763-48a4-ae7d-2b219e12e112/image.png" width=500>

<p>이제 이 페이지는 SSG 방식으로 동작</p>
<p>이 함수는 <span style="background-color:#FFE4E1">사전 렌더링 과정에서 필요한 데이터를 불러와서,
불러온 데이터를
props 안에 객체 형태로 컨포넌트에 전달</span></p>
<br>

<p><span style="background-color:#FFE4E1">이제 페이지 컴포넌트에서는
(이전에 본 SSR에서 봤던 것처럼) 전달받은 데이터를 props로 받아와서 컴포넌트 내부에서 자유롭게 사용</span></p>
<p>단, 이제 이 프롭스의 타입을 정의할 때 기존과 다름</p>
<p>(Before)</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/6e76f058-152c-4579-ac0a-c78cbcb0cf74/image.png" width=600>


<p>(After)</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/e8ed4ff3-62c3-4364-81ac-ad35743648cf/image.png" width=600>

<p>위와 같이 props의 타입을 바꿈</p>
<p><span style="background-color:#FFEBCD"><strong>InferGetStaticPropsType</strong>은 함수의 반환값 타입을 자동으로 추론해서 props의 타입을 설정해주는 역할을 함</span></p>
<br>

<p>이제 이 인덱스 페이지는 SSG 방식으로 동작하도록 수정했으니,
이 페이지가 빌드 타임에 딱 한 번만 실행되는지 확인</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/51b5f09f-c44b-42d7-91fd-04c4f4ad5b38/image.png" width=500>

<p>사전 렌더링이 빌드 타임에만 딱 한 번 이루어질 것이라서
<code>console.log(&quot;인덱스 페이지&quot;);</code> 이 함수도 딱 한 번만 실행</p>
<p><span style="background-color:#FFEBCD">그리고 브라우저에게 새롭게 페이지를 요청해도 이 메시지가 두 번 출력되지 않을 것</span></p>
<br>

<p>그런데!</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/5c518c12-6f54-4dcc-a618-6eafd2e3d299/image.png">

<p>응, 아니야^^</p>
<p>지금 요청이 들어올 때마다 사전 렌더링이 진행되는데,(마치 SSR처럼!)
왜냐하면, 지금 넥스트 앱을 개발모드로 실행하고 있기 때문</p>
<p>개발 모드는 수정 결과가 바로바로 동작하기 때문에,
SSR이든 SSG든 계속 사전 렌더링을 하고 있음</p>
<p><span style="background-color:#FFE4E1">빌드 타임이 있으려면 빌드를 해야지!</span></p>
<br>

<pre><code class="language-tsx">npm run build</code></pre>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/4ebc2b16-f105-4b40-ae07-7adc0a0d3819/image.png">

<p>그러면 정적 페이지 생성을 위한 데이터를 수집하고,</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/89b07bb5-afc4-4eef-bee7-425c684a6018/image.png">

<p>그 이후로 SSG로 동작하도록 설정한 페이지가 생성됨
앞서 설정한 getStaticProps 함수가 빌드 타임에 잘 실행되고 있음</p>
<p>저 &quot;인덱스 페이지&quot;가 실행되는 것으로 확인</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/d3e9f39f-f4f3-4acc-afc4-bdf9e03247d7/image.png">

<p>페이지별 빌드 결과</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/c2422ed1-0071-4121-865d-06c527ed3839/image.png">

<h4 id="●-html로-사전-렌더링된-페이지">● HTML로 사전 렌더링된 페이지</h4>
<p><span style="background-color:#FFEBCD">getStaticProps를 사용해서 데이터를 불러오는 페이지</span>
인덱스 페이지에만 이 기호가 붙어 있음</p>
<h4 id="○-정적인-페이지">○ 정적인 페이지</h4>
<p><span style="background-color:#FFEBCD">그냥 기본으로 설정된 SSG 페이지</span>
<span style="background-color:#E2D6FF">SSG처럼 정적인 페이지인데 getStaticProps를 설정해두지 않은 페이지</span>
➡️ 페이지로서 만들어 놓긴 했지만 아무 설정하지 않은 페이지</p>
<p><span style="background-color:#E2D6FF">넥스트는 이렇게 아무것도 적용하지 않은 페이지를 
기본값으로 정적인 페이지로 빌드 타임에 사전 렌더링하도록 설정</span></p>
<p>기본값은 SSG와 동일하게 동작</p>
<p>404 페이지, tst 페이지가 그 예시</p>
<h4 id="f--동적인-페이지">f : 동적인 페이지</h4>
<p>다이나믹 페이지 혹은 동적인 페이지라고 하며,
server-rendered on demand: 주문형
➡️ 이 페이지는 계속해서 사전 렌더링이 될 거야, 라는 식으로 이해</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/9034637e-2632-48bc-8d29-4c6a9700894c/image.png">


<p>북 페이지, 서치 페이지 외에도
api 밑의 파일에도 SSR이 붙는데,
<span style="background-color:#E2D6FF">넥스트가 기본적으로 모든 API Routes들을 다이나믹하게
즉, SSR로 작동하도록 설정하기 때문</span></p>
<br>

<p>이제 이것을 프로덕션 모드로 실행</p>
<pre><code>npm run start</code></pre><img src="https://velog.velcdn.com/images/ann-algorithm/post/e22de510-1892-447d-b86a-75cfdcccd210/image.png">

<p>이제 인덱스 페이지는 SSG 방식으로 빌드 이후 다시는 생성되지 않도록 설정해둔 페이지이므로
서버 측 로그에 아무 메시지도 출력되지 않고
getStaticProps 함수가 다시는 실행되지 않음을 확인</p>
<h2 id="☑️-서치-페이지">☑️ 서치 페이지</h2>
<p>서치 페이지에서의 SSG도 마찬가지로 바꿔줌</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/8b4bc327-bd4a-47e8-a560-a6b955f80468/image.png">

<p>먼저 getServerSideProps를 getStaticProps로 함수 이름을 바꿔줌</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/5c2da30e-f016-46e6-9311-31b806e774f1/image.png">

<p>컨텍스트 타입도 바꿔주어야 함</p>
<p>이제 서치 페이지 역시 SSG 방식으로 작동할 준비가 되었는데,
<span style="background-color:#E2D6FF">지금 context 매개변수로부터 쿼리 스트링을 가져온 라인에서 타입 오류가 발생하고 있음</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/4b50f36a-23ab-49a7-ade4-e9bdd521ea25/image.png">

<p><span style="background-color:#E2D6FF">context 객체에 쿼리스트링을 보관하는 query라는 프로퍼티가 없다는 뜻</span></p>
<p>진짜 없음</p>
<br>

<p>왜냐하면 getStaticProps 함수에 전달되는 
<span style="background-color:#FFEBCD">이 context라는 매개변수에는 쿼리 스트링을 포함하고 있는 쿼리 프로퍼티가 존재하지 않음</span></p>
<p>그 이유는,
<span style="background-color:#FDF5E6">getStaticProps, 그러니까 SSG 방식은 앞서 말했듯 빌드 타임에 딱 한 번만 실행</span>
그런데 <span style="background-color:#FFE4E1">빌드 타임에 쿼리 스트링을 알 수 없음</span></p>
<p>쿼리 스트링은 브라우저에서 사용자가 직접 입력한 검색어나, 리스트의 정렬 기준 같은 게 전달되는 공간이기 때문에
<span style="background-color:#E2D6FF">결론적으로 쿼리 스트링에 어떠한 값이 들어올지는 빌드 타임에 getStaticProps 함수 안에서 알아낼 방법이 없음
</span></p>
<br>

<p>따라서, 서치 페이지에서는 SSG를 쓸 수 없다!
엄밀히 말하면 검색 결과를 서버로부터 불러오는 동작을 수행할 수 없음
(쿼리 스트링을 꺼내올 수가 없어서)</p>
<br>

<p>그럼에도 불구하고, 이러한 페이지를 SSG 방식으로 동작하고 싶다면,
<span style="background-color:#FFEBCD">현재 스트링을 꺼내와서 해당 값을 기준으로 검색 결과를 불러오는 과정을,
사전 렌더링 과정 이후, 페이지 역할을 하는 컴포넌트에서 직접 진행</span>
즉, 리액트 앱에서 했던 방식대로 데이터 페칭</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/f3ccac7f-5b9d-4d18-aa40-feb4ea23d92f/image.png">

<p>빌드 타임 중에는 어차피 쿼리스트링을 불러올 수 없으니
getStaticProps는 아예 주석처리를 하고,</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/2f3f25e3-0ea7-4093-b0e6-41d3ecd7b694/image.png">

<p>props로 받아올 것도 없으니,
페이지 컴포넌트에서는 props를 삭제</p>
<p>대신에 컴포넌트 내부에서 useRouter 훅을 호출해서
이 훅의 반환값에서, query 프로퍼티의 값을 꺼내 쿼리 스트링을 꺼내옴</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/79fc48f2-71b9-40fb-9a2d-2e3bd05d363b/image.png">

<p>useEffect를 사용해서 검색어인 q가 변경되고,
변경된 p에 값이 있다면(공백이 아니라면)
그 안에서 검색 결과를 불러오는 로직이 작동될 것
➡️ 이것을 state에 보관해 보자</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/52d022a6-446a-418f-a38d-5003035baa07/image.png">

<p>(state 선언)
검색 결과를 저장할 state를 useState 훅을 이용해서 만듦
이 books state의 타입을 배열 타입으로 저장하게 함</p>
<p>(결과를 fetch로 가져오고, 이를 state에 저장)
검색 결과를 불러올 함수 fetchSearchResult는
비동기로 작성하여, fetch 함수를 호출하고
인수로는 현재 검색어 q를 스트링 타입으로 단언하여 전달
검색 결과를 데이터라는 이름으로 불러옴
결과를 state에 저장함</p>
<p>(검색어가 변경되면, 결과를 가져오게 함)
useEffect 안에서 검색어 q가 변경되었을 때
변경된 p에 값이 있다면(공백이 아니라면)
작성한 fetchSearchResult 함수를 호출</p>
<p><span style="background-color:#FDF5E6">이제 이 서치 페이지는 getStaticProps와 getServerSideProps가 모두 없어져
기본적으로 SSG 방식으로 동작</span></p>
<br>

<p>기본적으로 SSG 방식으로 동작하지만,
쿼리 스트링으로 전달되는 검색어를 빌드 타임에는 알 수 없기 때문에
사전 렌더링 과정에서는 이 페이지의 레이아웃, 즉 div 태그 정도만 렌더링됨</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/626e5c80-8fc2-406f-8270-025388fbd999/image.png">

<p>그리고 컴포넌트가 마운트된 이후에
브라우저(클라이언트 사이드)측에서 컴포넌트가 다시 실행되면서,
직접 쿼리 스트링으로 검색어를 불러와서 검색 결과 데이터를 클라이언트 사이드 측에서 렌더링함</p>
<br>

<p>다시 빌드해보자</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/559ff1a4-c51d-4335-8b77-4a16a06a1cd5/image.png">

<p>search  페이지가 f에서 ㅇ가 되었음</p>
<br>

<p>이제 <code>npm run start</code>로 프로덕션 모드로 가동해서
네트워크 탭을 열고 새로고침하면,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/08553587-6872-4fde-bd9c-4d475411d2f4/image.png" width=500>

<p>넥스트 서버 측에서 보내주는 파일을 볼 수 있는데,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/d2af8224-87f9-450d-8464-202ece5a412c/image.png">

<p>그 중 가장 위에 가장 처음으로 보내주는 사전 렌더링 결과인 html 파일을 클릭해보면,</p>
<p><span style="background-color:#FFEBCD">검색 결과 데이터는 제외하고 나머지 부분만 렌더링해서 브라우저에게 보내줌</span></p>
<p>이제는 <span style="background-color:#FFE4E1">클라이언트 측에서,
컴포넌트 안 쪽의 useEffect를 통해 기존 리액트 앱의 방식대로 동작함</span></p>
<h2 id="☑️-정리">☑️ 정리</h2>
<p>index 페이지처럼
빌드 타임에 사전 렌더링할 때 데이터를 불러오고 싶으면 getStaticProps라는 함수를 사용하고</p>
<p>search 페이지처럼 쿼리 스트링을 사용해도 빌드 타임에 데이터를 불러올 수 없는 페이지는
데이터를 그냥 리액트 앱에서,
클라이언트 사이드 측에서 직접 페칭해서 불러오도록 설정하는 것도 가능</p>
<hr>
<h1 id="📌-ssg-3-동적-경로에-적용하기">📌 SSG 3. 동적 경로에 적용하기</h1>
<p>동적 경로를 갖는 페이지도 SSG 라는 사전 렌더링 방식으로 작동하게 해보자</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/00a79562-b237-47bf-b6b5-f6931b0eed88/image.png">

<p>먼저 이 함수를 getStaticProps로 바꿔줌
당연히 매개변수 context 타입도 GetStaticPropsContext로 바꿔야 함</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/69466f11-ed1b-46eb-8b13-45d165ffb1d2/image.png">


<br>

<p>이제 페이지 컴포넌트도 props의 타입을 바꿔야 함</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/2e8bbf3b-a40d-497a-8b2e-046b9bf0102f/image.png">

<p>이제 저장하고,
북 페이지 렌더링이 되는지 확인</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/e0fd567e-920f-411c-a361-8ea9d4985d78/image.png">

<p>안 돼 돌아가</p>
<p><span style="background-color:#FFEBCD">다이나믹한, 동적인 경로를 갖는 SSG 페이지에는 
getStaticPaths 라는 함수가 추가로 필요하다고 함</span></p>
<p><span style="background-color:#E2D6FF">왜냐하면 이 북 페이지가 동적 경로를 갖는 페이지이기 때문</span></p>
<br>

<p>동적 경로가 뭐였더라?</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/564c7e3a-9a7f-4e0e-93bc-361f8e179aec/image.png">

<p>URL의 book 뒤에, id라는 URL 파라미터를 포함한 여러 개의 경로를 가능하게 하는 것
즉, 현재 URL 파라미터로 전달하는 id 값마다 오른 쪽에 보이는 여러 개의 페이지를 렌더링할 수 있음</p>
<p>각 id마다 해당 id를 갖는 도서 페이지를 각각 렌더링함</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/a56bc06c-dad5-4227-8e71-b9d88423c1f6/image.png">

<p>그렇기 때문에 이 페이지를 넥스트 서버 측에서
빌드 타임에 SSG 방식으로 사전 렌더링을 통해 미리 생성해 두기 위해서는</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/86a73992-383b-4f0f-ab07-860d3339637d/image.png">

<p><span style="background-color:#FFEBCD">선수 과정으로 이 페이지에 어떠한 URL 파라미터들이 존재할 수 있는지
그럼으로써 어떠한 경로가 존재할 수 있는지 설정하는 과정이 반드시 필요</span></p>
<p><span style="background-color:#E2D6FF">그렇지 않으면 어떤 경로가 있는지 모르니 사전 렌더링 진행조차 불가</span></p>
<p>따라서, 사전 렌더링 이전에 빌드 타임에 어떤 경로들이 존재하는지 설정하는 작업을 해야 함</p>
<br>

<p>예를 들어, URL 파라미터의 id 값이 1, 2, 3번이 있을 수 있다고 설정하면,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/0daa5a75-206a-4e5f-ae04-2f4202ecb551/image.png">

<br>

<p>그 다음 과정인 사전 렌더링 과정에서 이러한 경로에 있는 모든 페이지를 각각 생성하게 됨</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/8dd96c34-4c3a-476b-858a-2cdeea02ff09/image.png">

<p>그러니까 URL 파라미터로 전달되는 값이 1번, 2번, 3번이 있을 수 있다고 설정했으니
사전 렌더링 과정에서도 book/1.html, book/2.html, book/3.html 이라는
3개의 html 문서를 사전 렌더링하게 되는 것</p>
<p>따라서 빌드 종료 후 접속 요청 시
앞서 만들어둔 해당 페이지를 전달함</p>
<br>

<p>결론적으로 넥스트 앱의 북 페이지처럼
동적인 경로를 갖도록 설정이 된 페이지에 SSG를 적용시키려면
반드시 사전 렌더링이 진행되기 전에 이 페이지에 존재할 수 있는
모든 경로를 직접 설정하는 작업을 선수로 진행 필요</p>
<p>이때 이 역할을 하는 함수가 아까 오류 페이지에서 본 <strong>getStaticPaths</strong>라는 함수
➡️ 이 함수를 호출해서 현재 이 페이지에 존재할 수 있는 경로를 설정하고,
그렇게 설정된 경로에 해당하는 페이지를 getStaticProps 함수를 일일이 한 번씩 다 호출해서
사전에 여러 개의 페이지를 렌더링하는 방식으로 동작</p>
<h2 id="☑️-실습">☑️ 실습</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/585512f0-0e39-4d2e-8668-4b8d0172e3a8/image.png">

<p>book 폴더의 [id].tsx 파일 내에서,
getStaticPaths 함수를 명시하고, 해당 함수 내에서 객체를 리턴함</p>
<br>


<h3 id="paths-배열">paths 배열</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/26478d95-4de9-4319-a09b-a7af6e1074f0/image.png">

<p>객체 안에 paths라는 값으로,
이 페이지에 어떠한 URL 파라미터가 존재할 수 있는지 배열로 반환하도록 설정</p>
<br>

<p>예를 들어 아까처럼, 1, 2, 3번이 있을 수 있다고 설정하려면
path 배열 안에 하나의 경로 아이템을 객체로 설정</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/4cb5cf54-e196-4448-9ed6-5b48f0a17a16/image.png">

<p>URL 파라미터를 의미하는 params라는 값으로 id는 문자열 1</p>
<p>URL 파라미터로 1, 2, 3번이 들어올 수 있고,
그럼 book/1, book/2, book/3이라는 페이지가 존재할 수 있다고 설정한 것</p>
<p>참고로,
<span style="background-color:#E2D6FF">이때 URL 파라미터의 값은 문자열로만 명시 </span>
&quot;1&quot;, &quot;2&quot;,...</p>
<p>그래야만 넥스트가 이 경로를 정상적으로 읽을 수 있음
프레임워크 문법상 URL 파라미터의 값은 문자열로 설정</p>
<br>


<h3 id="fallback-옵션">fallback 옵션</h3>
<p>이렇게 path 값을 설정했으면 추가적으로 설정할 옵션이 있음</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/fcd05808-2cd4-42cf-b2b7-0d4204b7557f/image.png">

<blockquote>
<p>fallback: 대체, 대비책, 보험 등
➡️ 만약 브라우저에서 넥스트 서버에게 book/4와 같은 경로로 접속 요청하게 되면
path 값으로 설정한 URL에 해당하지 않는,
존재하지 않는 것 같은 URL 로 접속 요청하게 되면
어떻게 할 건지 대비책을 설정하는 옵션</p>
</blockquote>
<p>➡️ 이 중에 위처럼 작성한,
<span style="background-color:#FFEBCD">false: 아묻따 Not Found 페이지 반환</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/0d783a88-1a3a-4f2f-bf49-3ac20802f231/image.png">

<p>이렇게 없는 페이지로 취급함</p>
<h3 id="빌드-결과">빌드 결과</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/7ebe5f55-a07e-4824-b18e-b0ccd5c9e507/image.png">

<p>북 페이지를 정적으로 잘 설정해둠</p>
<br>

<p>또한, 이러한 정적인 페이지는
.next 폴더 안에 실제로 산출물로 저장됨</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/d3519ced-e24c-44ab-9eac-861c4827b0c2/image.png">

<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/2063c235-5633-42ba-840a-00305bde28ba/image.png">

<p>사전렌더링이 되어 있음</p>
<p>그래서 book/1로 요청이 오면
넥스트 서버는 이 html 파일을 지체 없이 보내주어서
매우 빠르게 동작할 것!!</p>
<hr>
<h1 id="📌-ssg-4-fallback-옵션-설정하기">📌 SSG 4. Fallback 옵션 설정하기</h1>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/224a32df-3b73-45cd-b1e9-f24797789018/image.png">

<p>다양한 동적 경로를 갖는 book 페이지에 SSG 방식의 사전 렌더링을 적용해주기 위해
getStaticPaths라는 함수를 작성해서,
이 함수 안에서 path라는 값을 리턴해서 현재 어떤 페이지가 존재할 수 있는지 직접 설정</p>
<blockquote>
<p>위 예시 코드에 따르면 다음 3개의 페이지가 존재할 수 있음</p>
</blockquote>
<ul>
<li>book/1.html</li>
<li>book/2.html</li>
<li>book/3.html</li>
</ul>
<p>이때 fallback이라는 옵션의 값을 false로 설정하여,
이 path에 명시하지 않은 경로의 요청이 들어오면
(즉 없는 경로로 요청 시)
자동으로 Not Found 페이지를 반환하도록 설정함</p>
<br>

<p>그런데 이렇게 설정하면,
나중에 새로운 도서가 등록 되었을 때
(4번이나 5번 같은 도서들이 등록되어 페이지가 새롭게 추가될 필요가 있을 때)</p>
<p>이 fallback 옵션에 따라
미리 설정한 paths 외에는 모두 Not Found 페이지를 반환할 것이라
정상적으로 모든 도서의 페이지를 제공하기 어렵게 될 것</p>
<p>이럴 때는 fallback 옵션을 다른 것으로 설정함</p>
<blockquote>
<p><strong>fallback 옵션 설정</strong></p>
</blockquote>
<ul>
<li>false: 404 Not Found 반환</li>
<li>blocking: 즉시 생성 (Like SSR)</li>
<li>true: 즉시 생성 + 페이지만 미리 반환</li>
</ul>
<h2 id="☑️-blocking-옵션">☑️ blocking 옵션</h2>
<blockquote>
<p>없는 경로로 요청이 들어왔을 때
해당하는 페이지를 마치 SSR 방식처럼 넥스트 서버 측에서 사전 렌더링을 거쳐 생성해서 반환</p>
</blockquote>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/dbe1f543-d4ad-4fcc-aaba-c209f9c087ea/image.png">

<p>이렇게 세 개의 페이지만 생성해 두었는데,</p>
<br>

<p>만약 브라우저에서 book/100 같은 존재하지 않는 경로로 요청을 보내면</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/a667e32f-0cc4-4d88-af1e-856263c85be1/image.png">

<p>false 옵션은 Not Found를 반환했음</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/862ddf5d-e629-4c85-b424-b5505a7c454a/image.png">

<p><span style="background-color:#FFEBCD">blocking은 SSR 방식처럼 실시간으로 요청받은 페이지를 사전 렌더링해서 브라우저에게 반환
</span></p>
<p><span style="background-color:#E2D6FF">그렇기 때문에 이 옵션을 사용하면 빌드 타임에 사전에 생성해두지 않은 페이지도 사용자에게 제공 가능</span></p>
<h3 id="실습">실습</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/67398095-a6c3-4295-85a9-8527ba564936/image.png">

<br>

<h3 id="빌드">빌드</h3>
<p><code>npm run build</code></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/d593a905-5dbb-4e58-a0ea-bc21bac331f7/image.png">

<p>여전히 3개의 페이지만 사전 렌더링 됐지만,</p>
<p><code>npm run start</code></p>
<p>프로덕션 모드 가동 시</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/bcced2f4-a762-4b68-bc71-33733cfef52d/image.png">

<p>4번 페이지는 paths에 없었지만 실시간으로 생성되는 것 확인 가능</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/bb41e8cf-996d-4391-9973-c05c51040617/image.png">

<p>4번 페이지도 정적 페이지로 잘 생성되어서 .next 에 저장됨</p>
<br>

<p>이렇게 빌드 타임 이후에 생성된 페이지들은
처음 요청할 때는 즉각적으로 생성이 되어야 해서
SSR 방식으로 동작해서 비교적 느리게 페이지가 렌더링되지만</p>
<p>한 번만 만들어두면
자동으로 넥스트 서버에 저장이 되어서
그 이후의 요청에는 페이지를 새로 요청할 필요가 없음</p>
<p>그래서 새로고침하면 매우 빠른 속도로 화면에 렌더링 됨</p>
<br>

<p>이 방식은 SSR + SSG 가 결합된 형태고,
이렇게 동작하는 이유는 <span style="background-color:#FFEBCD">fallback 옵션이 blocking</span>이기 때문임</p>
<p>그래서 우리가 추후 도서의 상세 페이지 같은 동적인 페이지를 구현할 때,
빌드 타임에 모든 도서의 id를 불러오기 어려운 상황이라면</p>
<p>그때는 fallback의 blocking이라는 옵션을 이용해서,
첫 번째 요청에는 SSR 방식으로 페이지를 새로 생성하여 신규 데이터를 반영하도록 하고,
그 이후 요청에는 SSG 방식으로 저장된 페이지를 매우 빠르게 반환하도록 할 수 있음</p>
<h3 id="blocking-방식의-주의사항">blocking 방식의 주의사항</h3>
<p>존재하지 않았던 페이지를 SSR 방식으로 새롭게 생성할 때
백엔드 서버에 추가적인 데이터를 요청해야 한다고 해서,</p>
<p>페이지의 생성 시간, 그러니까 사전 렌더링하는 시간이 길어지면,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/e8c31912-8265-432b-87a3-226ec4909fa9/image.png">

<p>브라우저에 넥스트 서버가 아무것도 응답하지 않아서
어쩔 수 없이 로딩이 발생</p>
<p>페이지의 크기에 따라 꽤 오랜 시간을 기다려야 할 수도 있음</p>
<h2 id="☑️-true-옵션">☑️ true 옵션</h2>
<p>위 blocking 옵션의 주의사항을 해결하기 위해
fallback의 세 번째 옵션인 true를 활용</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/75a02bab-dec4-4cb9-9393-51c580a6704e/image.png">

<br>

<p>그러면 브라우저로부터 존재하지 않는 페이지를 요청받았을 때</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/1062acd9-d1af-4ebb-871d-6bc3508f7ca0/image.png">

<p>일단 <span style="background-color:#FFEBCD">props가 없는 페이지를 빠르게 생성해서
즉시 브라우저에게 지체 없이 반환</span></p>
<p>이 props는 페이지에 필요한 데이터를 계산하는
getStaticProps 함수가 페이지 컴포넌트에게 전달하는 페이지에 필요한 데이터를 말하는 것임
(페이지 컴포넌트에서 props로 받잖아요잉)</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/0daad872-461a-438a-a660-6737653e5903/image.png">

<p>그래서 백엔드 서버로부터 불러온 도서 데이터 같은 것들이
방금 말한 props에 포함되는데,</p>
<p>이 props가 없는 페이지라고 하면
그 데이터가 없는 페이지임</p>
<p>그래서 데이터를 백엔드 서버로부터 불러오는 복잡하고 오래 걸리는 과정은 생략하고
일단 <span style="background-color:#FFEBCD">컴포넌트가 렌더링하는 레이아웃만 렌더링하기 위해 props가 없는 페이지를 사전 렌더링해서 반환</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/bb0e44de-2657-4fdf-834f-af1cecb8aefd/image.png">


<p>그 다음 <span style="background-color:#FFEBCD">이 페이지에 필요한 데이터인 props만 따로 계산해서,
계산이 완료되면 그때 브라우저에게 후속으로 데이터만 따로 보내주게 됨</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/660a7993-812d-48f6-b413-3fe89b97fa67/image.png">

<p>브라우저 입장에서는 일단 데이터가 없는 버전의 페이지를 만들어서
화면에 렌더링 해두었다가
(이때 <span style="background-color:#E2D6FF">로딩 바를 보여준다든지</span>)</p>
<p>나중에 서버가 props 계산을 마쳐 props값만 따로 전달하면
데이터가 들어오는 것이니까,
이 데이터를 포함한 페이지를 화면에 뒤늦게 렌더링</p>
<p>이렇게 하면
빌드 타임에 생성해 놓지 않은 페이지를 fallback blocking 옵션처럼 그대로 제공하면서도
동시에 사용자에게 긴 로딩 시간 대신에 데이터가 없는 버전의 페이지라도 일단 먼저 보여줌</p>
<h3 id="실습-1">실습</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/8f39d00b-2839-43c9-b2c6-69ad2059f492/image.png">

<p>fallback 옵션을 true로 변경</p>
<br>

<p>이때 paths로 설정하지 않는 경로로 접속 요청이 들어오면
props가 존재하지 않는 상태의 페이지를 먼저 사전 렌더링해서 브라우저에게 보내주니까,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/1231571b-50df-4c72-a511-7eec8a5025ba/image.png">

<p>데이터를 가져오는 부분은 생략하고,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/839bfe17-f43b-4149-bcc8-3890f55fa744/image.png">

<p>페이지 컴포넌트만 빠르게 사전 렌더링해서 보내주는 것임</p>
<br>

<p>그리고 넥스트 서버에서 뒤늦게</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/d506907e-c020-44d8-83a0-917a05650b12/image.png">

<p>이 함수를 실행하고, 데이터를 계산해서
후속으로 브라우저에게 book과 같은 데이터를 따로 전달함</p>
<h3 id="빌드-및-확인">빌드 및 확인</h3>
<p>먼저 빌드 <code>npm run build</code></p>
<p>그리고 .next 폴더를 확인해서
빌드 타임에 생성된 페이지 확인</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/036f4390-caa3-4008-90be-386063f7d919/image.png">

<p>우선 코드 상 paths에 설정한 대로 1, 2, 3번이 잘 생성됨</p>
<br>

<p>이제 우리가 설정한 fallback 옵션이 잘 동작하는지 확인</p>
<p><code>npm run start</code></p>
<br>

<p>브라우저에서 네트워크 탭의 속도를 조절하고</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/b414b80e-d421-4179-bda9-a6162d0abf0c/image.png">

<br>

<p>요청을 보내보면,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/a90747d1-2bb1-434b-a307-706eb60e28d3/image.png">

<p>처음엔 로딩 페이지 등으로 대체해서 보여주다가,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/e8ebedee-ce40-403b-b1dd-35709a06464a/image.png">

<p>뒤늦게 데이터를 렌더링함</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/af3dfb89-a0a6-452b-a1f5-51c916eb9e4a/image.png">

<p>가장 먼저 받은 html 페이지는 이와 같은데,</p>
<p>왜냐하면 코드 상에서,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/ecc47fcf-f2bb-4ce3-b230-28a3d926964a/image.png">


<p>book props가 없을 경우 위와 같이 렌더링하도록 했었기 때문임</p>
<br>

<h3 id="완성도-높이기">완성도 높이기</h3>
<p>그런데 컴포넌트가 getStaticProps의 계산 결과를 받지 못한 상태를 fallback 상태라고 하는데,
이 fallback 상태일 뿐인데,</p>
<p>위처럼 오류가 발생했다고 하면
사용자는 오류인 줄 알 것임</p>
<p>웹의 완성도를 높이기 위해 이 렌더링되는 텍스트를 로딩이라고 수정하면 좋을 것 같은데,...</p>
<br>

<p>이 텍스트를 통째로 바꾸면
로딩이 끝난 이후, 즉 fallback 상태가 끝난 이후
진짜 문제가 발생해도 로딩 중이라고만 출력될 테니,</p>
<p><span style="background-color:#FFC0CB">페이지 컴포넌트가 fallback 상태에 빠져 있을 때만,
로딩 중입니다, 라고 렌더링되도록 수정</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/544bc02a-630e-4239-8962-3c86164c38cb/image.png">

<p>그러기 위해서는
useRouter 훅으로 router 객체를 받아와서
이 객체가 fallback인지 확인(isFallback 프로퍼티를 통해서)</p>
<p>fallback이면 데이터를 기다리는 중이니까 로딩 중이라고 렌더링</p>
<p>이후에 fallback이 끝난 후에도 book으로 props(데이터)가 들어오지 않으면
찾을 수 없다는 텍스트를 렌더링</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/115be38f-00a8-41ab-851c-e321a1090a52/image.png">

<p>데이터가 있는 페이지는 로딩 후에 데이터가 잘 렌더링되고,</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/d395f2d3-e097-4778-bec3-c57b65294250/image.png">

<p>진짜 존재하지 않는 데이터면,
로딩 후 데이터가 없다고 렌더링됨</p>
<br>

<p>그리고 또!!
만약에!!!</p>
<p>이렇게 존재하지 않는 페이지로 들어왔을 때
Not Found 페이지로 보내고 싶으면</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/b036a1d1-e887-4641-b0f4-31f75b3f9bd0/image.png">


<p>getStaticProps 함수 안에 
book 데이터를 불러왔는데 존재하지 않으면
오류가 있다고 판단해서,</p>
<p>notFound라는 프로퍼티 값을 true로 설정하면
404 페이지로 리다이렉트 함</p>
<br>

<p>빌드를 다시 하고 프로덕션 모드로 실행해보면,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/cc360974-10cc-4562-85fc-fd40a4efc5ac/image.png">

<p>로딩 되다가 Not Found 페이지로 이동</p>
<h2 id="☑️-정리-1">☑️ 정리</h2>
<p><strong>fallback 옵션</strong></p>
<ul>
<li>false: 존재하지 않은 경로로 요청이 왔을 때 404 Not Found 페이지로 이동</li>
<li>blocking: SSR 방식처럼 실시간으로 페이지를 사전 렌더링해서 응답</li>
<li>true: blocking 옵션처럼 SSR 방식으로 실시간으로 페이지를 사전 렌더링해서 응답하지만,
이때 페이지의 생성을 끝까지 기다렸다가 응답하는 게 아니라
데이터가 없는 fallback 상태의 페이지부터 반환하고
그 뒤에 데이터는 후속으로 나중에 보내주는 방식<ul>
<li>이를 위해 isFallback 이라는 프로퍼티를 이용할 수 있음</li>
</ul>
</li>
</ul>
<hr>
<h1 id="📌-isr-1-소개-및-실습">📌 ISR 1. 소개 및 실습</h1>
<blockquote>
<p><strong>ISR (Incremental Static Regeneration)</strong>
: 증분 정적 재생성
단순히 SSG 방식으로 생성된 정적 페이지를
일정 시간을 주기로 다시 생성하는 기술</p>
</blockquote>
<h2 id="☑️-기존-ssg-방식과-이-단점">☑️ 기존 SSG 방식과 이 단점</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/a808d2dd-c531-4cb6-b774-dbbde48b7f71/image.png">

<p>어떤 페이지를 SSG 방식으로 동작하도록 설정해서 
해당 페이지를 이렇게 빌드 타임에 미리 정적으로 생성하도록 설정해두면,
<span style="background-color:#FFEBCD">이렇게 생성된 페이지는 이 시간 이후 다시 생성되지 않음
</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/08bbfd02-64c9-42c6-8fb9-430eedc49f0c/image.png">

<p>따라서 <span style="background-color:#FFC0CB">이 페이지를 언제 요청하더라도
똑같은 페이지만 반환</span></p>
<br>

<p>이는 <strong><span style="background-color:#E2D6FF">속도는 빠르지만 대신 최신 데이터를 반영하기에는 어렵다는 단점이 존재</span></strong>했음</p>
<br>

<h2 id="☑️-isr-방식">☑️ ISR 방식</h2>
<p>ISR 방식을 사용하면
<span style="background-color:#FFE4E1">SSG 방식으로 빌드 타임에 생성된 <strong>정적인 페이지를 마치 음식처럼 유통기한 설정</strong></span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/65c5dc25-e4ee-4578-915d-e3457498e82f/image.png">

<p>그래서 이 <span style="background-color:#FDF5E6">유통기한이 지나기 전까지는 매번 똑같은 페이지를 응답하다가,</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/0c496c24-0b75-4db6-8ac6-44153d204ad7/image.png">

<p><span style="background-color:#FFEBCD">유통기한이 끝나면, 넥스트 서버에서 새롭게 해당 페이지를 정적으로 생성</span>해서</p>
<p>이후 발생하는 접속 요청부터는 새로운 페이지를 반환할 수 있도록
일정 시간을 주기로 페이지를 다시 생성하도록 설정 가능</p>
<br>

<p>그러나 <strong>시간이 타이머처럼 칼같이 업데이트하는 건 아님!!</strong></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/79d881e5-fc56-412d-a338-44447c76fde7/image.png">

<p>시간이 왼쪽에서 오른쪽으로 흘러간다고 가정할 때,</p>
<p><span style="background-color:#FFE4E1">SSG 방식대로 빌드 타임에 처음 페이지를 한 번 생성하면서
ISR을 적용해서 60초 이후에 이 페이지를 다시 생성하도록 설정</span>해두게 되면.,</p>
<p><span style="background-color:#FFEBCD">60초가 지나기 전의 요청에는 V1 버전의 페이지를 반환하다가, 
60초 이후에 접속 요청이 발생하면 원래 가진 페이지를 반환</span>하고,
<strong><span style="background-color:#FFEBCD">서버에선 이때 새로운 페이지를 재생성</span></strong>함</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/68b72a45-32ca-4e9c-adf7-cfda800148c2/image.png">

<p>이때 최신 데이터를 반영한 새로운 버전의 페이지가 새롭게 생성되고,
이후 <span style="background-color:#FFEBCD">발생하는 요청에는 새롭게 생성된 V2 버전의 페이지가 반환이 되면서
새로운 데이터가 반영된 페이지가 브라우저의 화면에까지 잘 렌더링될 것</span></p>
<br>

<h2 id="☑️-isr의-특징">☑️ ISR의 특징</h2>
<p>그래서 이렇게 특정 시간 이후 정적으로 생성된 페이지를 새롭게 재생성하는
ISR이라는 방식은 기본적으로 이미 만들어져 있는 페이지를 반환하기 때문에,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/53180e6d-1a25-4f37-84a0-095cf13d5664/image.png">

<p>굉장히 빠른 속도로 브라우저에게 응답이 가능하다는 기존 SSG 방식의 장점과</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/52e3ad22-1395-420b-b52f-a93d8470412b/image.png">

<p>주기적으로 페이지를 업데이트해줄 수 있기 때문에 최신 데이터를 반영할 수 있다는 SSR의 장점까지</p>
<p>함께 가지고 있는 렌더링 전략!</p>
<br>

<h2 id="☑️-실습-1">☑️ 실습</h2>
<pre><code>npm run start</code></pre><p>로 프로덕션 시작</p>
<br>

<p>어디에 ISR 방식을 적용할까?</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/94fac038-fda7-4dee-a09a-0802a5fb6af5/image.png">

<p>이 인덱스 페이지의 추천 도서는 어떨까?</p>
<p>랜덤으로 세 가지의 책을 렌더링해야 하는데,
<span style="background-color:#FFE4E1">지난번에 SSG로 작성해버려서
이 부분은 브라우저에서 새로고침을 눌러서 새로 접속 요청을 해도
추천하는 도서가 항상 계속 고정이 되어 있음</span></p>
<br>

<p>그래서 이 인덱스 페이지에다 ISR을 적용해서
이 추천도서를 일정 시간을 주기로 변경해보자</p>
<p>어떻게 하냐면,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/70aff569-aac2-4059-958a-0347a9076410/image.png">

<p>이 getStaticProps 의 리턴 객체에
<span style="background-color:#FFEBCD">revalidate라는 프로퍼티를 추가!</span>
(revalidate는 재검증하다, 라는 뜻)</p>
<p><span style="background-color:#FFEBCD">값으로는, 몇 초 주기로 이 페이지를 다시 생성할 건지 초 단위로 명시</span>
➡️ <strong>이 페이지의 유통기한!</strong></p>
<p>이 인덱스 페이지를 3초 주기로 재검증하겠다고 설정한 것</p>
<br>

<p>다시 빌드해보면,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/721ce57e-b5b8-413b-a487-20eb4c69d4cf/image.png">

<p>이제 이 인덱스 페이지에 ISR이 3초 간격으로 적용되었음을 확인</p>
<p>이후 다시 프로덕션 모드로 실행하면,
ISR이 인덱스 페이지에서 잘 적용된 것이 확인 가능</p>
<br>

<p><span style="background-color:#FFC0CB">앞으로 Next.js로 페이지를 구현할 때 이 ISR 기법을 이용해 페이지를 구성하는 것 추천
</span></p>
<br>

<hr>
<h1 id="📌-isr-2-주문형-재검증on-demand-isr">📌 ISR 2. 주문형 재검증(On-Demand ISR)</h1>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/73925726-dbb2-4c65-8750-9370f04e982a/image.png">

<p>ISR 방식은 미리 생성해둔 정적 페이지를 응답하기 때문에
매우 빠른 속도로 동작하는 SSG 방식의 장점과 </p>
<p>특정 시간을 주기로 페이지를 다시 생성해서 
최신 데이터까지 함께 반영할 수 있다는 SSR 방식의 장점까지</p>
<p>함께 갖는 가장 강력한 사전 렌더링 방식</p>
<br>

<p>그런데! 이 ISR 방식을 추천하지만</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/6ecb6aeb-f2c7-45d4-9abb-ec4a582da6ad/image.png">

<p>시간 기반의 ISR이 적용하기 어려운 페이지가 있음!</p>
<br>

<h2 id="☑️-기존-isr의-문제점">☑️ 기존 ISR의 문제점</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/b7392433-7ad6-4ef9-b46c-e2ae86a80c4a/image.png">

<p>이러한 페이지는 시간과 상관없이
<span style="background-color:#FFEBCD">수정과 삭제 등 우리의 행동에 따라 페이지의 데이터가 즉각적으로 업데이트 되어야 함!</span></p>
<ul>
<li>게시글 수정: 페이지 데이터를 업데이트해서 보이게 해야 함</li>
<li>게시글 삭제: 페이지 데이터를 삭제해서 안 보이게 해야 함</li>
</ul>
<br>

<p>그래서 이걸 기존 ISR 방식대로 적용하려면,</p>
<p>예를 들면 지금처럼 60초마다 설정해뒀다면,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/9e418a9c-f67e-4d82-9d59-d7234f24c430/image.png">

<p>60초가 되기 전에 게시글 수정이 이루어질 수 있음
(수정이야 언제든 될 수 있음)</p>
<br>

<p>기존 방식은 60초로 설정한 시간이 되기 전에 업데이트가 일어나도,
60초가 되기 이전에는 수정 이전 버전의 페이지를 렌더링함</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/2396e6f4-839f-4ca2-98e4-692ef46c6191/image.png">

<p>그럼 <span style="background-color:#FFEBCD">60초가 지나기 전에 접속한 유저들은 게시글이 수정되기 전 이전 버전의 페이지를 보게 될 것</span>
➡️ <span style="background-color:#E2D6FF">최신 데이터를 즉각적으로 반영하기 어려움
</span></p>
<br>

<p>추가로 반대 케이스가 있음.</p>
<p>만약에 60초 간격으로 페이지를 다시 생성하도록 설정했는데,
실제 사용자의 게시글은 24시간 이후 발생함</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/aa51f553-f1b3-432b-8aeb-ba4616e2d050/image.png">

<p>그러면 <span style="background-color:#FFEBCD">최신 데이터 반영에는 타이밍에 따라 문제가 없을 수도 있지만,
</span>
(24시간 이후 발생한) 수정 전에, <span style="background-color:#E2D6FF">불필요하게 페이지를 생성중
</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/d425e047-adcf-4fce-ab14-d514f1f62d4d/image.png">

<p>왜냐하면 <span style="background-color:#FFEBCD">페이지는 사용자가 수정을 했든 말든 60초마다 재생성되고 있음</span></p>
<p>실제로는 페이지를 재생성할 필요가 없는데도!
불필요하게!</p>
<br>

<h2 id="☑️-그럼-그냥-ssr로-처리하면-되지-않나">☑️ 그럼 그냥 SSR로 처리하면 되지 않나?</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/8d3226ba-dedb-4317-aa28-b17897c0254c/image.png">

<p>SSR방식처럼,
<span style="background-color:#FFEBCD">브라우저가 요청할 때마다 
매번 새롭게 페이지를 사전 렌더링하기 때문에 응답시간이 많이 느려지고,
동시에 접속자까지 많이 몰리면 서버의 부하가 커져버림</span>
(할 일이 많으니까!)</p>
<p>그러니까 정적인 페이지로 처리해주는 게 좋은데,
위에서 말했듯이 ISR 방식으로는 많이 부족함</p>
<br>

<p>그래서 기존 아래처럼 시간을 기반으로 페이지를 업데이트 하는 기존의 ISR 방식이 아닌,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/4766f7e7-ba5e-4e67-9d0f-60ecb98b3130/image.png">

<p><span style="background-color:#FFC0CB">요청을 기반으로도 페이지를 업데이트할 수 있는 새로운 ISR 방식도 제공하고 있음</span></p>
<br>

<h2 id="☑️-on-demand-isr">☑️ On-Demand ISR</h2>
<blockquote>
<p>요청을 받을 때마다 페이지를 다시 생성하는 ISR</p>
</blockquote>
<p>그래서 주문이 들어올 때마다 한다고
On-Demand인 것임</p>
<br>

<p>그래서 <span style="background-color:#FFE4E1">실제 게시글을 수정해서 페이지의 데이터 업데이트가 진짜 필요해졌을 때</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/ce25b36d-ee54-4596-b14e-c6a394a30fe4/image.png">

<p>이렇게 Next.js 서버에 우리가 직접 페이지를 재생성하라는 요청인
<span style="background-color:#FFEBCD">페이지 Revalidate 요청을 보내서
페이지를 이때 다시 생성!</span></p>
<p>쉽게 말하면, <span style="background-color:#E2D6FF">이 방식은 페이지의 업데이트를 직접 트리거링할 수 있음
</span></p>
<p>이렇게 하면 대부분의 페이지에 최신 데이터를 반영하면서도 페이지를 정적 페이지로써 처리해줄 수 있음!</p>
<br>

<h2 id="☑️-실습-2">☑️ 실습</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/5ad7c623-c14a-40ef-98ac-3cb150ee97ac/image.png">

<p>일단 인덱스 페이지를 기본 SSG로 변경</p>
<br>

<h3 id="api-라우츠-생성">API 라우츠 생성</h3>
<p><strong><span style="background-color:#FFC0CB">Revalidate 요청을 처리할 새로운 API 라우츠를 만들자!</span></strong></p>
<br>

<p>먼저 pages의 API 폴더 아래에 새로운 파일 생성
<span style="background-color:#E6E6FA">우리가 했던 것처럼 API 라우트 핸들러를 만들어야 함!</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/6a81144b-f5f8-4248-a4ab-d4b063854daf/image.png">

<p>우선 리퀘스트와 리스판스를
NextApiRequest와 NextApiResponse 형태로 전달 받음</p>
<p>이제 이 핸들러에서 인덱스 페이지를 요청 받았을 때
revalidate, 즉 재생성 시켜주도록 코드를 작성해보자</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/bb1629fb-3f81-4bc9-bf2e-823c7b846a25/image.png">

<p>그래서 await res 객체의 revalidate라는 메서드를 호출하고</p>
<p>이 <span style="background-color:#FFEBCD">revalidate 함수의 인수로는,
어떤 페이지를 revalidate 시키려고 하는지,
해당 페이지의 경로를 넣어주면 됨</span></p>
<p>인덱스 페이지의 revalidate(재생성) 시킬 거니까 /로 인수로</p>
<br>

<p>그리고 실패할 수도 있으니 try-catch문</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/d8bc0eda-2509-447b-9ff9-0e29e95faef0/image.png">

<p><span style="background-color:#B0E0E6">요청이 성공했다는 가정 하에
<code>revalidate: true</code>로 설정해서 인덱스 페이지의 재생성이 완료되었다고 응답</span></p>
<p><span style="background-color:#E6E6FA">revalidate가 실패할 땐 res 객체의 status를 500으로 설정해서
revalidate가 실패했음을 알리고
응답으로 보낼 메시지는 send 메서드로 보냄</span></p>
<br>

<p>이제 이 API에 revalidate라는 주소로 접속 요청을 하면 이 handler가 실행</p>
<p><span style="background-color:#FFEBCD">이 응답 객체의 revalidate 메서드가 호출되어서
인수로 전달한 이 인덱스 경로를 다시 생성하게 됨</span></p>
<p>그리고 페이지 재생성이 성공하면 <code>revalidate: true</code>란 응답이 돌아올 것</p>
<h3 id="작동-확인">작동 확인</h3>
<pre><code>npm run build</code></pre><pre><code>npm run start</code></pre><p>이제 인덱스 페이지는 아무리 새로고침을 눌러도 변화하지 않음</p>
<br>

<p>그런데 만약에 인덱스 페이지를 재생성하게 설정한
API Routes로 요청을 보내면</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/18f3d1d2-4453-4744-ab9e-c90cc2801141/image.png">

<p>성공 응답을 받고</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/5004492e-89d9-4ef7-80d3-29c5703b554f/image.png">

<p>콘솔 출력을 확인</p>
<br>

<p>그리고 인덱스 페이지로 돌아가서 새로고침을 누르면 데이터가 변함</p>
<p>그러니까 <span style="background-color:#FDF5E6">우리가 revalidate 요청을 날려서 인덱스 페이지를 On-Demand ISR 방식으로 생성한 것임</span></p>
<p>이렇듯 <span style="background-color:#FFEBCD">On-Demand ISR이라는 사전 렌더링 방식은
API 라우츠를 통해 요청을 받았을 때 특정 페이지를 다시 생성하도록 만들 수 있어서</span></p>
<p><span style="background-color:#E2D6FF">사용자의 행동에 따라 데이터가 업데이트 된다거나
특정 조건에 따라 데이터가 업데이트 되어야 하는 페이지를,
정적 페이지로써 유지하고 싶을 때 사용할 수 있다는 점을 알아둘 것</span></p>
<br>

<p>이건 대부분의 케이스를 커버할 수 있는 강력한 사전 렌더링 방식이니까
오늘날 대부분 Next.js로 구축된 웹 서비스에서 활발히 사용되고 있음</p>
<hr>
<h1 id="📌-seo-설정하기">📌 SEO 설정하기</h1>
<blockquote>
<p>기본적인 검색 엔진을 최적화하자!</p>
</blockquote>
<h2 id="☑️-파비콘과-섬네일-파일-이동">☑️ 파비콘과 섬네일 파일 이동</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/93d21d96-1ab3-4ffc-bac4-e2d17a771663/image.png">

<p>public 폴더 안에 새 파비콘과 썸네일 파일 이동</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/6f26aef4-1895-486c-a1b5-d7a956b39a3d/image.png">

<p>파비콘 교체 완료!</p>
<p>(_document.tsx 에 <code>&lt;link rel=&quot;icon&quot; href=&quot;/favicon.ico&quot; /&gt;</code> 코드가 있어야 함)</p>
<h2 id="☑️-페이지별-seo">☑️ 페이지별 SEO</h2>
<p>불필요한 console.log와 불필요한 import는 삭제하고,
주석도 제거</p>
<h2 id="index-페이지">index 페이지</h2>
<p>Next.js에서는 React와 달리 각 페이지별로 메타 태그 설정 가능</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/3faade67-76e7-4045-a1e2-20de72f1db9b/image.png">

<p>대신 Head를 import 해야 함</p>
<p>(<span style="background-color:#B0E0E6">next/document는 _document.tsx 파일에만 사용되는 컴포넌트임</span>)</p>
<br>

<p>이제 return문 안에 기존 HTML의 메타 태그를 작성하듯 원하는 태그를 작성</p>
<p>이 인덱스 페이지가 카카오톡이나 페이스북 같은 sns에 링크로 공유될 때
필요한 썸네일, 타이틀, 설명 등의 오픈 그래프 태그를 함께 설정해보자</p>
<blockquote>
<p><strong>오픈 그래프 태그</strong>
웹페이지를 SNS에 공유할 때, 링크 미리보기 모양을 정해주는 메타데이터</p>
</blockquote>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/d95118c5-cc20-4ea9-a090-627b3241a43d/image.png">

<p><code>og:title</code>: 공유될 때 보이는 제목
<code>og:description</code>: 설명 문구
<code>og:image</code>: 썸네일 이미지
<code>og:url</code>: 대표 URL
<code>og:type</code>: 페이지 유형, 보통 웹사이트면 website</p>
<p>content에는 파일이나, 표시될 내용을 적어줌</p>
<p>이러한 <span style="background-color:#FFEBCD">컴포넌트 내부 태그는 셀프 클로징 필수!</span></p>
<p>기본적인 인덱스 페이지의 SEO 설정 마무리</p>
<br>

<p>이제 개발자 도구를 열면</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/10dd1ea8-bec9-43d0-9e85-dd9023ab12b1/image.png">

<p>메타태그가 잘 설정 된 것 확인 가능!</p>
<h3 id="search-페이지">search 페이지</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/0d9d6e3c-eb63-46c6-92d7-a36289069af7/image.png">

<p>서치 페이지에도 설정</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/75bf18cc-e3ab-4eb1-821b-c060e364c6b9/image.png">

<p>적용된 것 확인</p>
<h3 id="book-페이지">book 페이지</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/c4798986-b125-400e-89a0-57798620da31/image.png">

<p>북 페이지는 살짝 수정해서,
서버에서 가져온 데이터를 메타 태그로 적용</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/fd1ca67a-4d40-43a3-b424-153928c63b0b/image.png">


<p>적용된 것 확인</p>
<h3 id="주의할-점">주의할 점</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/bc0540d9-2971-497d-a944-882a5bbfe0d0/image.png">


<p>북 페이지는 동적 경로를 갖는 SSG 페이지
그리고 fallback 옵션으로 true
(미리 paths에 만들어둔 페이지만 미리 빌드 타임에 만들어두고
그 외 경로는 SSR 방식으로 요청이 들어왔을 때 서버에 생성)</p>
<p>이때 브라우저가 페이지의 생성을 너무 오래 기다리지 않게
일단 데이터가 없는 fallback 상태의 페이지 반환</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/04dfc3be-b6ed-4a15-a63e-7306dcf62281/image.png">

<p>이 함수가 계산하는 페이지 컴포넌트에 필요한 props가 없으면
fallback 상태의 페이지 먼저 반환하는데,</p>
<p>그래서 빌드 타임에 생성되지 않은 경로로 접속 요청을 날리면</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/7ce2efc6-cb09-4782-8a9c-ae355cb800ac/image.png">

<p>설정한 메타 태그가 다 빠져 있음</p>
<br>

<p>코드를 보면
페이지 컴포넌트에서,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/c42039f0-9361-45ee-886d-6b326745027a/image.png">

<p>현재 이 페이지가 fallback 상태면
이 문구를 렌더링하는데</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/2b97230a-5e6c-4f69-9d3f-3ee63ed294a3/image.png">

<p>위 <span style="background-color:#FFEBCD">메타 태그는 fallback 상태가 끝나고 나서야,
그러니까 데이터가 다 들어오고 나서야 페이지에 추가됨</span></p>
<br>

<p>지금처럼 이렇게 <span style="background-color:#FFEBCD">메타 태그를 fallback 상태가 아닐 때에만
데이터가 불러와졌을 때만 적용이 되게 설정을 해두면,
페이지를 처음 요청했을 땐 메타 태그가 적용이 되지 않아
SEO 설정이 안 되어 버림</span></p>
<br>

<p>그럼 어떻게 해야 함?</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/c42039f0-9361-45ee-886d-6b326745027a/image.png">

<p>이 fallback 상태에도
기본적인 메타 태그를 설정이 되도록 추가해야 함</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/67d19f0f-2539-4dd4-9d45-f4df944126db/image.png">

<p>따라서 해당 조건문 안에 Head 태그를 작성하여,
페이지가 로딩 중인 상황이라도 기본적인 메타태그가 설정되게 함</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/4f17a6fb-fd7d-49e6-ba69-296a07a41c26/image.png">

<p>이제 페이지가 fallback 상태에 있을 때에도
이런 식으로 기본적인 메타태그를 리턴해줌</p>
<p>이와 같이 설정해두는 것이 좋음!</p>
<br>

<hr>
<h1 id="📌-배포하기">📌 배포하기</h1>
<h2 id="vercel에-가입">vercel에 가입</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/2054e602-854c-4280-abcf-88f4fca64ce5/image.png">

<h2 id="vercel-설치">vercel 설치</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/369894f4-c0d8-46ec-a3bb-a80387f95e13/image.png">

<p>Vercel CLI를 내 컴퓨터 전체에서 사용할 수 있도록 설치</p>
<h2 id="vercel-로그인">vercel 로그인</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/ca901454-3027-4712-9bc8-84632b58c6c8/image.png">

<h2 id="vercel-배포">vercel 배포</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/045fc5b5-4be2-4061-bb4b-fd099d9d13fb/image.png">

<p>지금 지정한 폴더의 프로젝트를 Vercel에 연결하고 배포할까요? 라는 질문</p>
<ul>
<li>yes를 고르면</li>
<li>이 로컬 폴더를 Vercel 프로젝트와 연결하고</li>
<li>설정을 만든 뒤 바로 배포까지 진행합니다.</li>
</ul>
<p>이 프로젝트를 어느 계정/팀 소속으로 만들지 묻는 것</p>
<ul>
<li>개인 계정</li>
<li>팀 계정</li>
<li>조직 계정</li>
</ul>
<p>Vercel에 만들어져 있는 기존 프로젝트와 연결할지 묻는 것</p>
<ul>
<li>yes면 → 기존 Vercel 프로젝트와 이 로컬 폴더를 연결</li>
<li>no면 → 새 프로젝트를 만듦</li>
</ul>
<p>Vercel에 생성될 프로젝트 이름을 정하는 단계</p>
<ul>
<li>Vercel 대시보드에서 보이는 이름</li>
<li>기본 배포 URL 이름</li>
<li>프로젝트 식별용 이름</li>
</ul>
<p>실제 앱 코드가 어느 폴더에 들어 있나요? 를 묻는 것</p>
<ul>
<li>./ 는 현재 폴더</li>
</ul>
<p>자동 감지한 설정을 직접 수정할까요? 라는 질문</p>
<ul>
<li>추가적인 고급 설정까지 더 바꿀지 묻는 것</li>
</ul>
<ol>
<li>현재 폴더를 배포 대상으로 삼고</li>
<li>내 계정 소속으로</li>
<li>새 Vercel 프로젝트를 만들고</li>
<li>이름은 05-seo</li>
<li>코드 위치는 현재 폴더</li>
<li>Next.js 설정은 자동 감지값 사용</li>
<li>추가 설정 수정 없이 바로 배포</li>
</ol>
<p>주소가 나오면 그 주소로 접속 시</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/74f0c871-77d1-40fa-b86b-8fcf9e8b7bbb/image.png">

<p>화면은 나오지만 데이터가 없음</p>
<p>왜냐하면 백엔드 서버가 로컬에서 실행되고 있고,
버셀 서버에서는 내 로컬을 모르니까.</p>
<p>백엔드 서버도 같이 배포해야 함</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/44bf685c-0637-4919-ba6b-566ab8ea6f1b/image.png">

<p>같은 방식으로 배포</p>
<br>

<p>그리고 각 lib에 만든 fetch 함수의 url 교체</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/7e47b82b-db3c-44a5-bfcc-860e01d9094a/image.png">

<img src="https://velog.velcdn.com/images/ann-algorithm/post/9914c8a6-3d7c-407c-bace-6d66c37382a5/image.png">

<img src="https://velog.velcdn.com/images/ann-algorithm/post/16ab5d59-9017-485a-a228-1885bf20cc46/image.png">


<br>

<p>이제 배포를 다시 해야 함</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/9521503d-9db1-44f9-a10e-c2694c44d1c1/image.png">

<p>프로덕션 모드로 재배포</p>
<br>

<p>웹에선 빌드되는 상태를 볼 수 있음</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/c2727eac-79f1-4534-8904-d29a49e27bf2/image.png">

<img src="https://velog.velcdn.com/images/ann-algorithm/post/6f0356e3-33eb-4a0e-9062-541f5c72ba37/image.png">

<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/1113adde-1d01-4f22-a955-696c2f126bd2/image.png">

<p>잘 배포가 되면 Ready로 나옴</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/bb86d1ab-8944-469e-8ab3-2c694d6d4328/image.png">

<p>이제 데이터를 잘 배포함!
(사실 vercel에 환경변수까지 설정해줬다.)</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/aa4a049b-63f9-4827-a7d7-ef913f6ed017/image.png">

<p>KEY는 DATABASE_URL로,
VALUE는 처음 로컬의 .env에 설정한 값으로 해주면 됨</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/127be733-1144-4bb3-9032-85a024dc2235/image.png">

<img src="https://velog.velcdn.com/images/ann-algorithm/post/92af9079-6820-45da-87e5-240a34829947/image.png">

<p>메타 태그, 오픈 그래프 태그도 모두 적용 확인~</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[타입스크립트 기본]]></title>
            <link>https://velog.io/@ann-algorithm/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EA%B8%B0%EB%B3%B8</link>
            <guid>https://velog.io/@ann-algorithm/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EA%B8%B0%EB%B3%B8</guid>
            <pubDate>Tue, 17 Mar 2026 06:04:30 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8/dashboard?cid=330452">한 입 크기로 잘라먹는 타입스크립트(TypeScript)
</a></p>
<p>반 년 만에 재개하는 타입스크립트 포스팅...</p>
<h1 id="📌-기본-타입이란">📌 기본 타입이란?</h1>
<blockquote>
<p>타입스크립트가 자체적으로 제공하는 타입</p>
</blockquote>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/ef2e2d91-1ce2-415a-9fcf-d156c04a3c58/image.png">

<p>위 그림은
타입스크립트가 제공하는 여러 개의 기본타입을 계층에 따라 분류한 타입 계층도</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/c6751c65-d827-4c4c-b542-2708a595c5a0/image.png">

<p>자바스크립트에서 사용하는 타입도,
생소한 타입도 있음</p>
<p>그리고 <span style="background-color:#FDF5E6">각각의 타입은 부모와 자식 관계를 이루며 계층을 형성</span>
이 부모와 자식 관계를 어떤 기준으로 맺는지는 다음에!</p>
<p>number, string과 같은 원시 타입,
그리고 object, Array와 같은 비원시 타입
마지막으로 unknown, any, void, 와 같은 타입스크립트에서만 제공하는 타입도 살펴볼 것</p>
<h2 id="실습">실습</h2>
<pre><code>npm init</code></pre><p>새로운 패키지를 초기화하고</p>
<pre><code>npm i @types/node</code></pre><p>Node.js의 내장 기능들에 대한 타입 정보를 제공하는 패키지 설치</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/9627f7dd-10f0-4824-93aa-61530bf01ef9/image.png">

<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/74dca626-e713-4bff-a35a-7d519d57b499/image.png" width=500>

<p>@types/node가 설치된 것 확인</p>
<br>

<p>이제 tsconfig 파일을 만듦
새 파일로 만들었음</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/e7c56905-ed1f-4764-a810-380b9559d3ca/image.png">

<img src="https://velog.velcdn.com/images/ann-algorithm/post/8d99217d-f0b1-4446-b658-39dbf2db8cdd/image.png" width=400>

<pre><code>- target: 변환되는 자바스크립트 코드가 사용할 자바스크립트 버전 명시
- module: 변환되는 자바스크립트 코드가 사용할 모듈 시스템 정리
- outDir: 컴파일 결과 생성되는 자바스크립트 파일이 위치할 경로 설정
- strict: 엄격한 타입 검사
- moduleDetection: 모든 타입스크립트 파일을 개별 모듈로 취급
- skipLibCheck: .d.ts 확장자를 가진 타입 선언 파일 전체(type declaration files) 에 대한 타입 검사를 하지 않도록 함
- include: 컴파일할 파일이 있는 경로</code></pre><br>

<p>이제 타입스크립트 파일을 만들자!
src 폴더를 만들고</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/abfbf9e0-af87-4d71-b731-108e15befc89/image.png">

<p>컴파일 하면,</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/bb0e7df5-6d24-4843-89a8-e7d8b7322f8e/image.png">

<img src="https://velog.velcdn.com/images/ann-algorithm/post/e54ab6d5-8763-4f83-9de8-67262c028f5c/image.png">

<p>dist 폴더가 생성되고
index.js 파일이 생성됨</p>
<p>이 파일에는 export 키워드가 있는데
moduleDetection 옵션을 force로 설정해뒀기 때문
➡️ 따라서 자동으로 컴파일러가 추가</p>
<br>

<p>이 컴파일된 자바스크립트 파일은 node로 실행</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/ef4ea22a-9717-48ea-905f-fcf0348e170c/image.png">

<p>오류가 발생한다면...</p>
<p>Node가 export를 이해못하는 중</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/dd136200-91d8-4116-8ccf-3e396469223c/image.png">

<p>따라서 package.json에 타입을 추가한 후 재실행</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/9ff8e8c4-c9bd-46a8-b5df-e08b9cef2643/image.png">

<p>오류 없이 실행됨</p>
<hr>
<h1 id="📌-원시-타입과-리터럴-타입">📌 원시 타입과 리터럴 타입</h1>
<h2 id="원시-타입-primitive-type">원시 타입: Primitive Type</h2>
<blockquote>
<p>하나의 값만 저장하는 타입</p>
</blockquote>
<p>배열이나 객체는 여러 개의 값을 저장하지만,
원시 타입은 숫자면 숫자, 문자면 문자, 딱 하나만 저장 가능</p>
<br>

<p>src에 새 파일을 만들자
(index.ts는 삭제)</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/17bbfa47-dea6-469a-a1d8-ad8f56c16090/image.png">

<h3 id="number-타입">number 타입</h3>
<blockquote>
<p>숫자를 의미하는 모든 값 다 포함</p>
</blockquote>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/2af7290b-bbb9-4f90-96a4-cf60e66a4c61/image.png" width=400>

<p>숫자값 123 저장</p>
<p>위와 같이
<span style="background-color:#FFEBCD">변수 이름 뒤에 콜론 그 뒤에 타입을 정의</span>하는 문법을
타입스크립트에서는 <strong>type 주석</strong> 혹은 <strong>type annotation</strong>이라고 함
이렇게 콜론과 함께 어떤 변수의 타입을 정의하는 방식은
타입스크립트에서 가장 기본적으로 변수의 타입을 정의하는 방식</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/0046ec40-3e4d-4224-9575-ef527860fe51/image.png" width=500>

<p>음의 소수, 양의 소수는 물론이고
양의 무한대, 음의 무한대,
그리고 Not a Number라는 특수한 숫자도 포함됨</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/a618a0e8-b5c5-4b2f-9b91-0cbb36a23266/image.png">

<p>문자열을 넣으려고 하면 오류 발생
➡️ 에디터 상에서 프로그래머가 알 수 있게 명시됨</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/9e805787-1fda-491b-96bd-cf70ff78eb1c/image.png">

<p>문자열 전용 메서드도 당연히 사용 불가</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/d9a94f1f-12a1-4865-8fa4-bb52b27b998d/image.png">

<p>넘버 타입의 값에만 적용 가능한 메서드는 당연히 가능</p>
<h3 id="string-타입">string 타입</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/9d8e6bf1-238b-4338-bf2b-b81b7636dcfc/image.png" width=500>

<p>: 뒤에 string이라고 명시</p>
<p>싱글 쿼터, 더블 쿼터, 백틱도 가능하고,
템플릿 리터럴 문자열도 가능</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/d59bac5f-415d-497c-815e-aa8b5518a07d/image.png">

<p>string 타입으로 정의한 변수에 number 타입의 값, 즉 숫자를 넣으려면 오류 발생
<br></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/9f257f81-a040-4f61-82f2-a3709964318b/image.png">

<p>number 타입 전용 메서드도 불가능
<br></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/c77713a8-8f5e-4495-baec-7e8e93d32c47/image.png">

<p>문자열 전용 메서드는 가능</p>
<h3 id="boolean-타입">Boolean 타입</h3>
<blockquote>
<p>참과 거짓만 저장하는 타입</p>
</blockquote>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/82142921-b49e-4746-96b0-e05268eaf61e/image.png" width=500>

<p>물론 당연히 이 변수에 숫자나 문자열을 저장하려면 오류</p>
<h3 id="null-타입">null 타입</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/238a98c7-f1cf-4f04-8958-933f737814e2/image.png" width=500>

<p>이 변수에는 null 값 외의 다른 값은 담을 수 없음</p>
<h3 id="undefined-타입">undefined 타입</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/ce5ff5e8-7e31-4ef4-a3b6-812b285852fa/image.png" width=500>

<p>이와 같이 null과 undefined는 타입스크립트에서 별도의 타입으로 존재</p>
<h3 id="그런데">그런데!</h3>
<p>자바스크립트에선 어떤 변수를 number 타입으로 만들어도 넣을 값이 없으면 잠시 null을 넣기도 하는데</p>
<p>타입스크립트는 이게 비허용!!</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/e27f5e9e-ac94-4004-a540-f27fa7a19b1a/image.png">


<p>왜냐하면 null이란 값은 null 타입이 별도로 존재하고
number 타입 안에 포함되는 값이 아니기 때문에</p>
<p><span style="background-color:#E0FFFF">그럼 진짜로 중간에 넣을 값이 없어서 잠시 null을 넣고 싶은 경우에는?</span></p>
<p><span style="background-color:#FFEBCD">컴파일러 옵션을 조절해서
null 타입이 아닌 number 타입 같은 변수에도 임시로 null 값을 넣게 하기</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/f3b5d47c-7f1c-455f-8b54-6dccade59c76/image.png" width=500>

<p>분홍색으로 표시된 <strong>strictNullChecks</strong> 옵션 추가
<span style="background-color:#E2D6FF">이것 설정 시 TS 서버 재시작</span></p>
<p>이름부터가 엄격한 null 검사
➡️ 쉽게 말해서 null 타입이 아닌 변수에 null 값을 할당하는 걸 허용할지 말지에 대한 옵션</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/98f7b19d-b544-4bb9-8842-11ce745597bd/image.png">

<p>false 시 허용한다는 것
(null 타입이 아닌 변수에 null 값을 할당하는 걸 허용)</p>
<br>

<p>개발하는 상황에 따라
변수에 null 값을 임시로 넣어야 하는 상황이 많으면
이 옵션을 false로 설정하고 개발</p>
<p>무조건 안전한 코드만 쓰겠다고 하면
이게 기본적으로 true이니, 아예 명시하지 않거나
true를 명시하고 개발하면 됨</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/f3b5d47c-7f1c-455f-8b54-6dccade59c76/image.png" width=500>

<p>strictNullChecks 옵션은 strict 옵션의 하위 옵션
따라서 strict 옵션이 켜져 있으면 strictNullChecks 옵션도 켜지는 것</p>
<p>근데 지금처럼 하위 옵션 strictNullChecks을 따로 명시함으로써,
상위 옵션 strict이 true여도 false로 명시하고 사용 가능</p>
<br>

<p>강의에서는 안전하게 실습할 것이니
그냥 지우고 함
(그럼 다시 TS 서버 재시작)</p>
<h2 id="리터럴-타입">리터럴 타입</h2>
<p>number나 string 처럼
해당 타입에 해당하는 값이라면, 그 범위 내의 모든 값이 저장할 타입 뿐만 아니라</p>
<p><span style="background-color:#FFEBCD">딱 하나의 값만 포함하는 리터럴 타입이라는 독특한 타입이 존재</span></p>
<p>그 자체가 타입이 되는 그런 타입</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/0fa38b21-dc8a-47ba-b66f-38a588354f4a/image.png" width=400>

<p>변수에 타입을 그 값 자체로 정의하면
이제 이 변수에는 이 값 외 다른 값을 넣을 수 없음</p>
<p>값으로 만든 타입 = 리터럴 타입
(왜냐하면 리터럴 = 값)</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/138d0811-f254-42b8-b15a-3a40c152affb/image.png">

<p>다른 값을 넣으면 오류가 남</p>
<br>

<p>string 도 마찬가지</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/957928df-093c-456c-8d5d-a8076af717fe/image.png" width=500>

<br>

<p>boolean도 마찬가지</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/f42645aa-1225-4bd3-a76f-50aac88076ba/image.png" width=500>

<br>

<p>이처럼 타입스크립트는 타입 안에 포함되는 값 중에 하나를 마치 타입인 것처럼 정의해서 사용 가능
나중에 복합적인 타입을 만들 때 유용하게 사용</p>
<hr>
<h1 id="📌-배열과-튜플">📌 배열과 튜플</h1>
<h2 id="배열">배열</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/3c4ba62c-31cf-4ddf-8a09-b89c135e55c3/image.png" width=400>

<p>타입 정의 없이 숫자값을 담는 배열을 자바스크립트처럼 쓰면 위와 같음</p>
<br>

<p>이를 타입 어노테이션으로 타입 정의를 하려면,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/a6431294-c763-4b7f-baf4-1ba1620a8dd2/image.png" width=500>

<p>콜론, 타입을 명시하고
<span style="background-color:#FFEBCD">이것이 배열임을 알리기 위해 대괄호 []를 명시함</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/523ea97e-61ea-4252-b727-e7894c0c67d5/image.png" width=500>

<p>문자열 배열도 마찬가지
string이란 타입을 적고
대괄호를 적어 배열 타입임을 알려줌</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/f8cd1681-7554-4530-b2d2-2c0dfeaf5f4e/image.png" width=500>

<p>또한, 이렇게 배열 타입 정의도 가능</p>
<p>이렇게 <span style="background-color:#FFEBCD">꺽쇠를 열고 타입을 집어 넣는 문법을
<strong>제네릭 문법</strong>이라고 함</span></p>
<p>(강의에서는 앞으로 타입 어노테이션 방식으로 타입을 정의할 것)</p>
<br>

<p>지금까지는 배열에 들어가는 요소의 타입이 하나밖에 없었지만
(그러니까 숫자 값만 들어가는 배열 or 문자열 값만 들어가는 배열이었는데)
만약 배열에 들어가는 요소들의 타입이 다양하다면?</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/2fcee390-68bf-4bde-ab3d-14669a9a5c11/image.png" width=500>

<p>어떻게 타입을 정의해야 할까?</p>
<p>이때는 에디터에 마우스를 올려보자</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/f682944d-6a57-4e7e-966e-323abf790fbe/image.png">

<p>타입스크립트가 어떻게 추론하는지 알 수 있음</p>
<p>소괄호 안에 요소의 타입이 써 있음
<code>string | number</code>
➡️ 배열의 요소가 string 타입이거나 number 타입일 수 있음</p>
<p><span style="background-color:#E2D6FF">이를 유니온 타입이라고 함
</span></p>
<br>

<p>이를 활용해 타입 주석을 달아보자</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/ffd0b107-e0d7-44c3-85c1-b6c41b8165ff/image.png" width=700>

<p>배열에 들어가는 요소들의 타입이 다양할 경우
위와 같이 정의할 수 있음</p>
<br>

<h2 id="다차원-배열">다차원 배열</h2>
<blockquote>
<p>배열 안에 배열, 그러니까 2차원 배열이나 3차원 배열</p>
</blockquote>
<p>다차원 배열의 타입을 정의하자</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/408ff479-38dc-463b-86e5-91c7654a110d/image.png" width=400>

<p>이렇게 배열이 있고 그 안에 또 배열이 들어갈 수 있음</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/0801f8d3-af27-4e82-ba8f-9ad94999493c/image.png" width=500>

<p>이렇게 쓸 수 있다는 건 알겠음
그래서 이 배열은 어떻게 정의?</p>
<p>지금은 숫자 타입의 배열만 저장하고 있으니까,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/7f0ef144-bf40-403f-a9ea-4c7956219c14/image.png" width=500>

<p><span style="background-color:#FFE4E1">숫자 타입의 배열을 배열로 저장한다는 뜻으로 보자</span></p>
<h2 id="튜플">튜플</h2>
<p>튜플이란,</p>
<blockquote>
<p>길이와 타입이 고정된 배열</p>
</blockquote>
<p>자바스크립트의 배열은 길이와 타입이 고정되어 있지 않음
개수를 마음대로 늘릴 수도 있고,
배열에 들어가는 요소의 타입도 자유로움</p>
<p>그리고,
타입스크립트의 배열은 배열에 들어가는 요소의 타입을 고정시킬 수는 있지만,
길이까지 고정시킬 수는 없음</p>
<p>그러나,
튜플은 타입과 길이를 모두 고정 가능</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/c71c66d7-f872-43f5-8a64-4a191e3f0c8e/image.png" width=500>

<p>이렇게 각 요소의 타입을 지정해주면
지정한 타입이 해당 자리의 요소의 타입이고,
지정한 만큼 길이가 지정됨</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/d916b244-b99b-4683-af94-f883f4786b75/image.png">

<p>길이나 타입을 만족하지 않으면 요소를 담을 수 없음
당연히 순서가 바뀌어도 안 됨</p>
<br>

<p><span style="background-color:#FFC0CB">튜플은 별도로 존재하는 자료형이 아니고 그냥 배열임</span></p>
<p>그래서 tsc를 사용하여 컴파일하고,
자바스크립트 코드를 보면</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/8ebcd407-7efc-41c0-86f2-d0ff85902891/image.png">

<p>그냥 배열임</p>
<br>

<p>그래서 push나 pop 같은 배열 메서드를 사용하여
값을 넣거나 제거하는 것이 가능</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/93d8e656-5cd4-489b-af81-6053919d47bc/image.png" width=500>

<p>근데 <span style="background-color:#E2D6FF">길이를 2개로 고정해뒀는데,
push로 한 개 더 넣어도 오류가 발생하지 않음</span></p>
<p><span style="background-color:#FFEBCD">배열 메서드를 사용할 때는 튜플의 길이 제한이 발동하지 않음
이걸 자바스크립트의 배열이라고 생각하기 때문에 알아보지 못함
</span></p>
<p>pop 메서드를 여러 번 사용해도 어떤 오류도 발생하지 않음
(1번 push 했으니 3번 pop 해도 되지 않나? 싶어서 4번 pop해봤지만, 역시 오류가 나지 않음)</p>
<p>➡️ 따라서 튜플 타입을 사용할 땐,
배열 메서드를 사용하여 push나 pop 같은 방식으로
요소를 추가하거나 제거하는 기능을 사용할 때 각별히 주의하는 것이 좋음</p>
<br>

<h3 id="튜플은-언제-유용하게-사용할-수-있을까">튜플은 언제 유용하게 사용할 수 있을까?</h3>
<p>예를 들어 유저 정보를 2차원 배열로 저장하는 배열을 생각해보자</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/8b59371c-2fdc-4b4f-a8fc-b457b3673814/image.png" width=300>

<p>이렇게 배열의 규칙을 정리해서 만들었는데,
동료가 눈치 없이 이상하게 유저를 추가함</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/5bf86a60-89e8-4f9a-8f3e-d27557aec6a5/image.png" width=300>

<br>

<p>보기에는 그럴 수도 있다 싶지만<del>(아니, 그럴 수 없어)</del>
나는 0번 인덱스를 이름으로 정리해두고
toUpperCase 같은 메서드도 사용함</p>
<p>그런데 이때 0번 인덱스에 숫자 타입의 값을 적어두는 바람에 오류가 발생할 수도 있음</p>
<br>

<p>그래서 이때도 타입을 정의해주면 됨</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/478287a5-d34e-4e76-9ade-9bf7720025a1/image.png">

<p>[string, number] 튜플을 []를 사용하여 배열로 정의했기 때문에
[4, &quot;한입넥스트&quot;]라는 데이터는 타입이 맞지 않으므로 오류라고 감지</p>
<p>이럴 때 활용할 수 있다!😇</p>
<hr>
<h1 id="📌-객체">📌 객체</h1>
<h2 id="객체-타입-선언">객체 타입 선언</h2>
<p>간단한 객체를 하나 만들어보자</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/df7a8873-03d1-4875-9d16-a55b89adc60a/image.png" width=400>

<p>유저의 정보를 저장하는 객체를 만들었음
이때 이 객체를 담고 있는 이 변수 유저의 타입은 뭐가 되어야 할까?</p>
<br>

<p>타입스크립트에서는 object라는,
객체를 의미하는 타입이 있음</p>
<p>위 user도 객체니까,
object타입으로 정의해보자</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/02f40cdf-6bd0-44b2-9b0c-b16acc253c3a/image.png" width=500>

<p>이렇게 정의하면 문제가 생김</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/5022383f-aea2-40ea-8ad5-36d6f4357def/image.png">

<p>점 표기법으로 객체의 프로퍼티에 접근하려면 오류가 발생
object 타입에 id 프로퍼티가 없다고 함</p>
<p>왜? id는 정의되어 있는데?</p>
<br>

<p>왜냐하면 <span style="background-color:#FFEBCD">타입스크립트에 object라는 타입은
이 값이 객체다! 라는 정보 외에는 없는 타입</span>이기 때문에
<span style="background-color:#FFC0CB">object로 정의하면 객체의 프로퍼티나 메서드에 뭐가 있는지
이 타입은 알 수가 없음</span></p>
<p>즉, 변수를 object로 정의하면
<span style="background-color:#FDF5E6">이 변수는 객체이긴 한데, 그 이상은 몰라
</span>라고 하는 것과 동일한 것임</p>
<br>

<p>근데 우리는 어쨌든 객체를 정확히 타입으로 만들고 싶단 말임!
➡️ <span style="background-color:#E6E6FA">객체 리터럴 타입 활용</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/2750e35a-5d21-465a-aa3f-b68f71a78027/image.png" width=300>

<p>이렇게 프로퍼티의 타입까지 모두 정의!
<span style="background-color:#FFE4E1">객체 리터럴 문법과 비슷하기 때문에</span>
<span style="background-color:#FFEBCD">중괄호를 이용해 객체의 타입을 정의하는 방식을
<strong>객체 리터럴 타입</strong>이라고 함</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/8c51c99f-a311-4dad-905e-94b6cba0b480/image.png">

<p>이렇게 객체 리터럴 타입으로 변수 유저의 타입을 정의하면,
점 표기법으로 id 프로퍼티에 접근하는 코드도 오류없이 수행됨</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/3615877f-2038-495b-b46f-c4343cfeb7f1/image.png">


<p>id 프로퍼티 위에 마우스를 올려보면,
우리가 객체 리터럴 타입으로 정의한 대로
id 프로퍼티 타입이 number로 나타내는 것 확인 가능</p>
<br>

<p>결론적으로 객체의 타입을 정의할 때 object를 사용하면,
객체인 건 알지만
프로퍼티나 메서드에 접근하면 오류가 나기 때문에 잘 쓰지 않고,</p>
<p>이렇게 <span style="background-color:#E2D6FF">객체의 모든 프로퍼티들의 타입까지 구조적으로 다 정의할 수 있는 방식인
객체의 리터럴 타입을 사용</span>한다고 알아두면 됨</p>
<br>

<p>복습 겸 강아지 객체를 만들어 보자</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/9e4153e9-6ab2-4a66-9eb5-d8c06ec06f0f/image.png" width=400>

<p>이렇게 객체의 타입을 프로퍼티 기반으로 정의</p>
<p>C나 자바 같은 정적인 타입 시스템 언어를 사용해본 사람이라면(나?)
이렇게 객체 타입을 정의할 때 프로퍼티를 일일이 나열하면서,
프로퍼티를 기반으로 객체 타입을 정의하는 문법이 당황스러울 수 있음</p>
<blockquote>
<p>위 말만 들으면 사실, C나 자바도 타입을 정의하는 방식인데 뭐가 다르지? 싶었음</p>
</blockquote>
<p>Java의 객체 정의 방식</p>
<pre><code>class Person {
    String name;
    int name;
}</code></pre><blockquote>
<p>Person이라는 이름이 있는 타입을 만들고 그 안에 필드 선언</p>
<p>타입스크립트에서의 객체 타입 정의 방식</p>
</blockquote>
<pre><code>type Person = {
    name: string;
    age: number;
}</code></pre><p>비슷해 보인단 말임🥹</p>
<blockquote>
<p>하지만, <span style="background-color:#FFEBCD">타입스크립트는 이 객체는 이 프로퍼티를 가져야 한다고 형태를 나열하는 것</span>
그러니까, 다시 말해 모양이 같으면 다른 타입으로 취급된다고 함</p>
<p>예를 들면,</p>
</blockquote>
<pre><code>type A = { name: string };
type B = { name: string };
&gt;
let a: A = { name: &quot;홍길동&quot; };
let b: B = a;  // 가능</code></pre><p>Java 관점에서 A와 B는 다른 타입이지만,
<span style="background-color:#FFEBCD">타입스크립트에서는 구조가 같기 때문에 동일하게 취급된다고 함</span></p>
<blockquote>
<p>그래서 직접 또 돌려봄</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/71997025-f67e-4cac-9ef6-399dfd406e3d/image.png">

<img src="https://velog.velcdn.com/images/ann-algorithm/post/b2af1c34-ef28-4d54-ad83-d7ebf636aca1/image.png">

<p>다시 말해 <span style="background-color:#E2D6FF">타입스크립트는 name만 있으면 A타입이라고 하자~</span>가 되는 언어임</p>
</blockquote>
<p>다시 돌아가서,</p>
<p>사실 대부분의 언어에서는
타입을 정의할 때 객체면 전부 Object고,
문자열이면 전부 String인 식인데,</p>
<p>타입스크립트는 
User 객체만의 타입을 id, name 프로퍼티 기반으로 정의하고
Dog 객체만의 타입을 name, color 프로퍼티 기반으로 정의</p>
<p><span style="background-color:#E2D6FF">마치 id와 name이 있어야 User 객체고,
name과 color가 있어야 Dog 객체인 것</span></p>
<br>

<p>정리하자면,
object 같은 단순한 이름으로 타입을 정의하는 게 아니라
이 객체를 이루는 프로퍼티나 메서드가 어떻게 생겼는지
객체의 구조를 기준으로 타입을 정의
➡️ <span style="background-color:#FFC0CB">구조적 타입 시스템</span>라고 함</p>
<p>혹은 프로퍼티를 기준으로 타입을 결정하는 시스템이니까
➡️ <span style="background-color:#FFC0CB">프로퍼티 기반 타입 시스템</span>이라고도 함
(이건 공식적인 명칭은 아닌 듯)</p>
<br>

<p>자바의 경우에는 이름을 기준으로 정의하기 때문에
<span style="background-color:#E2D6FF">명목적 타입 시스템</span>이라고 함</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/ae4e0f85-8a79-4417-b710-30248a280e11/image.png" width=500>
(챗지피티 땡큐)

<br>

<h2 id="선택적-프로퍼티optional-property">선택적 프로퍼티(Optional Property)</h2>
<p>가끔 어떤 프로퍼티는 없어도 되는 경우가 있음</p>
<p>user의 경우,
이름은 아는데 아이디는 모를 수가 있음</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/d00e4340-0b8e-444b-ac82-3c54cc720d2a/image.png" width=300>

<p>그런데 이미 user라는 변수의 타입을
id와 name이라는 두 개의 프로퍼티를 갖는 객체로 정의해놨기 때문에
id를 모른다고 작성하지 않으면 오류 발생
➡️ 선택적 프로퍼티라고 명시해야 함</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/92d4b0b4-19a8-4e93-94fd-6416d905ae88/image.png" width=300>

<p>프로퍼티 뒤에 물음표 추가
물음표가 붙으면, 해당 프로퍼티가 있어도 되고 없어도 됨</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/b808c751-d892-4523-8e5c-10712467b840/image.png">

<p>근데 만약 있으면 number 타입이어야 함!
➡️ <span style="background-color:#FFEBCD">선택적 프로퍼티</span> 혹은 <span style="background-color:#FFEBCD">옵셔널 프로퍼티</span>라고 함</p>
<h2 id="읽기-전용-프로퍼티">읽기 전용 프로퍼티</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/1ad8826e-9957-4ef4-aaa3-18960a740b64/image.png" width=400>

<p>타입까지 야무지게 지정한 객체</p>
<p>그런데 자바스크립트에서는 객체의 프로퍼티에 접근해서 프로퍼티의 밸류를 바꿀 수 있음!</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/ffc6743e-a195-4389-a294-c41ea67afdeb/image.png" width=400>

<p>프로퍼티가, 딱 봐도 바꾸면 안 될 것 같이 생김</p>
<br>

<p>따라서 <span style="background-color:#FFEBCD">readonly라는 키워드를 사용해서 읽기 전용 프로퍼티로 만들어줌</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/d4589fad-10a0-4272-9bad-a0f68510b8ba/image.png">

<p>그럼 해당 프로퍼티를 변경할 수 없음</p>
<hr>
<h1 id="📌-타입-별칭과-인덱스-시그니처">📌 타입 별칭과 인덱스 시그니처</h1>
<h2 id="타입-별칭">타입 별칭</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/77509e6b-98df-4dcf-a9b8-df9e14433cfc/image.png" width=400>

<p>위와 같은 변수가 있다고 하자
위와 같이 유저의 정보를 저장하는 변수를 하나 더 선언해보자
(유저가 하나이면,... 슬프니까. 여럿이어야 하니까)</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/c1917026-8caa-4cbe-9922-f475b7981485/image.png" width=400>

<p>정의하는 것만 중복 코드이고,
길이도 길게 차지함</p>
<br>

<p>타입을 마치 변수처럼 정의해서 사용하는
<strong>타입 별칭</strong>을 만들어 코드의 중복을 제거하자</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/e5f2f6e5-0911-437b-b9e9-aa90229c8a78/image.png" width=400>

<p>유저를 저장할 객체의 타입을 작성할 것이기 때문에
User라는 타입을 선언하고,
변수에 값을 초기화</p>
<p>그리고 안의 프로퍼티, 객체를 채워줌</p>
<p>➡️ 타입 별칭으로 만든 유저라는 타입이 생성됨</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/98d6d74d-e4a4-4408-bfb7-0787867b3eec/image.png" width=300>

<p>그러면 위처럼 변수User의 타입 어노테이션으로 객체 타입 리터럴을 일일이 작성할 필요가 없고,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/d64ebf52-28ad-4662-959f-1dede3fb1377/image.png" width=300>

<p>대신 이 타입 별칭을 가져다가 씀</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/de0282ee-b9bb-45fe-90d2-433da9117720/image.png" width=300>

<p>위와 같이
매번 프로퍼티의 타입을 쓸 필요가 없고, 중복 코드가 제거됨</p>
<br>

<p>만약 User 객체 타입에 새로운 프로퍼티가 필요하다면,
타입 별칭에 새로 추가하면 됨</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/d71b94af-c38e-4837-b836-734f6e6d8c20/image.png">

<p>그럼 User라는 타입으로 정의되어 있는 모든 변수에 공통적으로 반영됨</p>
<p><span style="background-color:#FFC0CB">따라서 앞으로 공통적으로 적용되어야 하는 User 같은 타입의 경우에는,
대부분 타입 별칭을 이용할 것</span></p>
<br>

<p><span style="background-color:#E0FFFF">그리고 이 타입 별칭을 사용할 때 조심해야 할 것!</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/02c8c9e7-f30e-41d3-ad45-8881f789c26a/image.png" width=500>

<p>let 키워드로 선언하는 변수처럼,
<span style="background-color:#FFEBCD">동일한 스코프에 중복된 이름으로 타입 별칭을 선언하면 오류 발생</span>
마치 변수의 이름같음</p>
<p><span style="background-color:#FFE4E1">그렇기 때문에 타입 별칭을 선언할 때는 같은 스코프 내에서 중복되지 않도록</span> 해야 함</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/17e1b93e-e640-4a76-bc9c-d264627d5595/image.png" width=300>

<p>만약 함수 내에 User를 정의하면,
이는 함수 안팎에서 각각의 스코프를 따라감</p>
<p>그러니까 함수 밖의 User는 함수 밖의 타입이고,
함수 안의 User는 함수 안의 타입이라서,
함수 내에서 User 타입을 사용하면 함수 안의 User타입을 따라감
(이렇게 말하면 어렵지만, 자바스크립트의 스코프를 공부했거나, 다른 프로그래밍 언어를 공부했다면 무슨 뜻인지 대충 알 것이다.)</p>
<br>

<p>아무튼,
타입스크립트의 타입 관련 코드는 컴파일 결과 자바스크립트 코드에선 다 제거됨
그렇기 때문에 타입 별칭으로 만든 타입도 제거됨</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/e6b752c2-552b-4cf5-a5e7-ce4220ea7ab1/image.png">

<p>이렇게 타입스크립트에서
<span style="background-color:#FFEBCD">타입을 마치 변수처럼 정의하도록 도와주는 문법</span>이
<strong>타입 별칭(타입 Alias)</strong>임</p>
<h2 id="인덱스-시그니처">인덱스 시그니처</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/8b22e958-db53-458f-bb17-2f035f38f9a9/image.png" width=300>

<p>이렇게 객체를 만들고
국가의 이름과, 영문 코드인 객체를 만듦</p>
<br>

<p>타입 별칭으로 정의하면,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/94d98f74-577b-40b5-b27f-1cd0df4a7dd7/image.png" width=300>

<p>위와 같이 정의하게 됨</p>
<br>

<p>그런데 지금이야 3개의 프로퍼티밖에 없지만,
만약에 초거대 글로벌 서비스가 되어 약 200개의 가까운 모든 국가의 코드를 넣어야 한다면?</p>
<p>➡️ 타입 별칭도 모든 프로퍼티를 다 넣어줘야 함...</p>
<p>세상에...😱</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/b1197cd5-865f-464f-9b79-ef7192d5047b/image.png">

<p>그런데 가만 살펴보면
프로퍼티가 다 string 타입임
국가 코드인 value도  string 타입
<span style="background-color:#FFEBCD">➡️ 키가 string 타입이고, value가 string 타입인 프로퍼티는 모두 허용하도록 타입을 만들면
어떤 국가를 추가하든 문제가 되지 않음</span></p>
<br>

<p><span style="background-color:#FFEBCD">key와 value의 규칙을 기준으로 객체의 타입을 정의할 수 있는 문법</span>
➡️ <strong>인덱스 시그니처</strong></p>
<p><img src="![](https://velog.velcdn.com/images/ann-algorithm/post/f46c7fc3-8aea-4b09-a7fd-f8d8b546cd7f/image.png)
" width=300></p>
<p>대괄호 안에 key의 타입을 쓰고 
콜론,
그리고 value의 타입 명시</p>
<br>

<p>그러면 객체 contryCodes의 타입을,
우리가 만든 타입 별칭으로 지정 가능</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/a64e9354-07e6-47b4-a5ea-2704f5eba8a4/image.png" width=400>

<p>이제 오류 없이
정상적으로 타입을 잘 정의함</p>
<br>

<p>이와 같이
key와 value의 타입을 기준으로 규칙을 이용해서
유연하게 객체의 타입을 정의하는 문법을 인덱스 시그니처라고 함</p>
<p><span style="background-color:#FDF5E6">key와 value의 타입이 어떤 규칙을 가지고 움직이는 객체의 타입을 정의할 때 굉장히 유용하게 사용 가능</span></p>
<br>

<p>연습 삼아 객체 하나 더</p>
<p>countryNumberCode 라는 객체를 만들어보자</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/f25996a4-7e1a-4905-bb81-7c9e3bd4e2af/image.png" width=400>

<p>이렇게 key는 string이지만, value는 number 타입인 객체를 선언하려면?</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/02c5daca-d3ff-44d8-aeaa-2a313861164f/image.png" width=500>

<p>위와 같이 인덱스 시그니처를 이용해 간결하게 타입 정의 가능</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/d6de556a-af14-4702-a69c-adb02ed5cbb8/image.png" width=500>

<p>활용 가능</p>
<br>

<p>또 다른 특징 한 가지는,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/4c14607f-c170-4a39-8594-84f3bc55966f/image.png" width=500>

<p><span style="background-color:#FFC0CB">객체의 프로퍼티를 지워도 상관 없음</span>
왜냐하면! <span style="background-color:#FFEBCD">인덱스 시그니처 타입은 규칙을 위반하지만 않으면 모든 객체 허용</span></p>
<p>지금 이 객체는 아무 프로퍼티가 없음
➡️ 위반할 프로퍼티가 없음</p>
<p>아무런 프로퍼티가 없으니까 규칙을 위반할 것도 없는데,
이런 건 때로 주의해야 함</p>
<br>

<p>따라서 꼭 있어야 할 프로퍼티가 있다면,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/3717c6fb-f0bf-45a3-bf08-a364f7fb0b01/image.png">

<p>필수 프로퍼티를 적어주면,
해당 프로퍼티를 꼭 적어야 한다고 에디터가 알려줌</p>
<br>

<p>마음이 바뀌어서
나라 숫자 코드뿐 아니라 영어 코드도 저장하고 싶을 때,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/4c6c54a2-2543-43b1-8a40-eeacec270ce1/image.png" width=500>
(당연히 오류남)

<p>그럼, Korea는 허락해보자</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/98a310fd-1379-4db0-ae33-8f7f37ca9037/image.png">

<p>(응, 돌아가)</p>
<br>

<p>우리가 인덱스 시그니처를 사용하는 어떤 객체 타입에서
<span style="background-color:#FFEBCD">추가적인 프로퍼티를 정의하려면
추가적인 프로퍼티의 value의 타입은
인덱스 시그니처의 value의 타입과 일치하거나 호환해야 함</span></p>
<p>그런데, Korea라는 프로퍼티의 value 타입이 string이고,
인덱스 시그니처의 value의 타입은 number이기 때문에 문제가 생김</p>
<p>따라서 이는 불가함~</p>
<hr>
<h1 id="📌-enum-타입">📌 Enum 타입</h1>
<blockquote>
<p>여러 가지 값에 이름을 부여해 열거해두고 사용하는 타입</p>
</blockquote>
<p><span style="background-color:#FFEBCD">자바스크립트에는 없고
타입스크립트에만 제공되는 타입</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/34ae8f5c-15e0-45e9-88ad-11ff033683c3/image.png" width=500>

<p>세 명의 유저가 있다고 가정
각각의 유저에 롤을 부여</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/495d0793-a83c-4cf7-947b-1a8e2e47ab5d/image.png" width=500>

<p>유저의 권한은 이렇게 숫자로 배정하는 방법을 많이 사용
이렇게 숫자로 각각의 권한을 설정한 다음 계속 개발하다 보면
언젠가 한 번은 까먹을 수도 있음</p>
<p><span style="background-color:#FDF5E6">숫자만 보고 기억하기 어렵기 때문</span>
➡️ 타입스크립트의 Enum을 활용</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/c7879bac-d699-4c1f-b673-5a251180d772/image.png" width=300>

<p>enum이란 키워드를 적고 이름을 적고(Role),
중괄호를 열고 각 권한을 써줌</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/b57aa2a9-e876-4830-b61a-9432e9258d45/image.png" width=300>

<p>각각의 멤버에 원하는 대로 값을 지정</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/0a32f4e0-6b9f-40a5-a28f-c06b49d2f797/image.png" width=500>

<p>그리고 각 변수의 role을 enum을 활용하여 지정
➡️ <span style="background-color:#FFEBCD">각각의 role이란 프로퍼티에 0, 1, 2가 저장됨</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/2799b0e0-4bd6-4947-b499-4d168156738e/image.png">

<p>콘솔로 출력해보면 할당된 값 확인</p>
<br>

<p>만약 숫자를 0이 아닌 10부터 할당하고 싶으면</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/fac75e86-ae4b-401a-9103-c91c2c0932c7/image.png" width=300>

<p>맨 위에 숫자 10 명시</p>
<br>

<p>중간부터 다른 숫자 할당도 가능</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/0d38cc9b-4b74-438c-a20a-4c7fb0f14a74/image.png" width=300>

<p>그럼 0, 10, 11 순으로 할당됨</p>
<br>

<p><span style="background-color:#FFC0CB">enum을 사용할 땐 숫자를 자동으로 할당할 수도 있고,
직접 시작하는 숫자를 지정할 수도 있음
</span></p>
<p>이렇게 <span style="background-color:#FFEBCD">긱각의 멤버의 값이 숫자로 할당되는 enum을 숫자형 enum</span>이라고 함</p>
<br>

<p>문자열 값도 할당할 수 있음</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/c33a0587-1b92-4790-9da7-4559cc867f7d/image.png" width=300>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/ec3e85f7-b0b1-4114-9791-f0ade71a11fb/image.png">

<p>프로퍼티에 할당해 보면,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/9cf019b3-6c45-4ddc-aee7-36c5597e6cf6/image.png">

<p>콘솔로 출력해보면 할당된 값 확인</p>
<br>

<h2 id="enum을-활용할-때-좋은-점">enum을 활용할 때 좋은 점</h2>
<p>language enum을 사용하지 않으면</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/6045dd44-106f-4070-9a08-9f1f8445837a/image.png" width=500>

<p>language 프로퍼티에 값을 할당하다가,
korea인지 ko인지 Ko-KR인지 헷갈리는데
enum을 활용하면 헷갈리지 않고 바로바로 확인 가능</p>
<h2 id="enum의-의문점">enum의 의문점</h2>
<p>타입스크립트의 타입 관련된 코드는
(role이나 language 같은 enum은) 다 사라진다는데</p>
<p>프로퍼티에 enum을 점 표기법으로 가져와서 할당해도 됨?</p>
<br>

<p>결론부터 말하자면,
<span style="background-color:#FFC0CB">enum은 컴파일 결과 사라지지 않음</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/d3ae02b1-5fcb-43c2-9bc0-dda6c5999285/image.png" width=500>

<p>복잡하지만,
enum이 자바스크립트의 객체로 변환되는 중</p>
<br>

<p>결론적으로 타입스키립트의 enum은 컴파일 결과 사라지지 않고,
자바스크립트의 객체로 변환되므로
코드 상에서 점 표기법이나 값을 사용하듯 사용할 수 있음</p>
<hr>
<h1 id="📌-any와-unknown-타입">📌 Any와 Unknown 타입</h1>
<blockquote>
<p>자바스크립트에는 없고, 타입스크립트에만 있는 타입</p>
</blockquote>
<h2 id="☑️-any">☑️ Any</h2>
<blockquote>
<p>특정 변수의 타입을 우리가 확실히 모를 때 사용할 수 있는 타입</p>
</blockquote>
<p>예를 들면,</p>
<pre><code class="language-tsx">let anyVar = 10;</code></pre>
<p><span style="background-color:#FFE4E1">anyVar는 매우 범용적으로 사용해야 한다고 가정</span>
지금은 숫자 10이지만 나중에는 문자열이 되어야 한다고 하자</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/b340ef2c-b293-49d2-abc7-2419ceee6e1b/image.png">

<p>오류가 발생함</p>
<p>왜냐하면,
<span style="background-color:#FFC0CB">타입스크립트는 우리가 변수의 타입을 지정하지 않아도 
초기화하는 값을 기준으로 변수의 타입을 자동으로 추론하기 때문에</span></p>
<p>anyVar는 숫자값 10으로 초기화
➡️ 자동으로 number 타입으로 추론
그러니까 이후 문자열 값을 넣으려면 오류 발생</p>
<br>

<p>만약 자바스크립트 변수 쓰듯이 타입 검사 없이,
즉 타입 상관없이 아무 값이나 담고 싶다면?
<span style="background-color:#FFEBCD">any 타입을 이 변수에 지정
</span></p>
<p><span style="background-color:#FFEBCD">any는 우리 말로 모든 또는 누구나, 라는 뜻으로 볼 수 있음</span>
➡️ any 타입 = 어떤 타입(아무 타입도 상관 ❌)</p>
<p><span style="background-color:#FFC0CB">즉, 변수의 타입을 any로 지정하면 어떤 타입이든 이 변수에 넣을 수 있음
</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/081f8059-ba33-45bf-a3dc-c72938f31e6a/image.png" width=500>

<p>따라서 <span style="background-color:#FFC0CB">불리언, 객체, 함수도 넣을 수 있음</span>
<span style="background-color:#FFC0CB">문자열 메서드나 숫자 메서드도 제약 없이 사용 가능
</span></p>
<br>

<pre><code class="language-tsx">let num: number = 10;
num = anyVar;</code></pre>
<p>number 타입의 변수를 만들고,
그 number 타입의 변수에 any 타입의 변수를 넣어도
타입 오류가 발생하지 않음</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/e2f8e70a-d7b7-4be5-adab-fbe1dba5e9d5/image.png" width=500>

<p><span style="background-color:#FFEBCD">any타입은 변수에 지정할 경우,
모든 타입의 값을 다 할당 받을 수 있고
</span>
<span style="background-color:#FFEBCD">모든 타입의 변수에 다 any타입의 값을 집어넣을 수 있음</span></p>
<p>따라서 <span style="background-color:#E2D6FF">타입스크립트의 타입 검사를 다 통과하는 치트키 같은 타입</span></p>
<br>

<h3 id="any의-문제">any의 문제</h3>
<p>이제 이걸 실행 시켜 보자</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/42dc4159-ec0d-4fbe-9f5c-c2ccc3564e0f/image.png">

<p>오류가 발생!</p>
<p>왜냐면, 지금 마지막으로 들어간 값이 함수인데,
문자열 메서드인 toUpperCase 메서드를 호출하려고 하니 당연히 안 됨
➡️ 타입 에러가 런타임 도중 발생</p>
<br>

<p><strong>이게 문제!!!</strong></p>
<br>

<p><span style="background-color:#FFEBCD">any 타입은 타입 검사를 어찌되든 다 통과하는 치트키 같은 타입
= 타입 검사를 안 함</span></p>
<p><span style="background-color:#E2D6FF">변수에 any 타입을 지정한다는 건,
타입스크립트의 이점을 포기한다는 것</span></p>
<p>즉, 오류가 있는 코드도 다 검사를 통과하고
<span style="background-color:#FFE4E1">런타임 도중 에러가 발생하는 최악의 상황을 유발</span></p>
<p>그렇기 때문에 any타입은 가능한 한 최대한 사용하지 않는 것이 좋음</p>
<h2 id="☑️-unknown-타입">☑️ Unknown 타입</h2>
<p>any와 비슷하지만 조금 다름</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/609936a0-8daf-444c-af31-e32af0e872e6/image.png" width=400>

<p>타입은 unknown이라고 지정
여기에 문자열이든 숫자든 함수든 객체든,
any 타입의 변수를 선언한 것처럼
아무 타입의 값이나 집어넣을 수 있음</p>
<p>지금까지느 any와 똑같음
➡️ <span style="background-color:#FFC0CB">우리가 변수에 어떤 타입이 들어올지 모르겠는 경우
any나 unknown 둘중 하나를 쓸 수 있음</span></p>
<br>

<p>차이점이 있다면,
unknown 타입은 any 타입과 다르게 
모든 값을 저장할 순 있지만, 그 반대는 안 됨</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/58f41b8a-a03d-4fb8-af1e-84edf25e21bd/image.png">

<p>any 타입은 number 타입에도 값을 저장할 수 있었지만,
unknown은 불가</p>
<p>당연히 number가 아닌 다른, 모든 타입의 변수에 unknown 타입의 값을 넣을 수 없음</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/9612e797-1530-4308-86d2-dff1ec24e29a/image.png">

<p>또 any와 다르게 toUpperCase 같은 특정 타입의 메서드를 사용할 수 없음</p>
<p>덧셈, 뺄셈, 곱셈, 나눗셈 같은 연산도 불가함</p>
<br>

<p>단,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/4c12fa33-f4ab-4f51-bc4d-46405b4b83f5/image.png">

<p>만약에 unknown 타입의 값을 활용하고 싶다면
조건문으로 typeof 연산자를 통해
<span style="background-color:#FFC0CB">이 unknown 변수가 number 타입임을 확실히 밝혀주었을 때만
unknown 타입의 변수를 number 타입으로 정제해서 사용 가능</span>
➡️ 이러한 과정을 <span style="background-color:#FFEBCD">타입 정제 혹은 타입 좁히기</span>라고 함</p>
<br>

<p>중요한 건 any 타입과 unknown 타입은 변수의 타입으로 지정하면,
이 변수는 모든 타입의 값을 다 할당받을 수 있지만,
any 타입은 반대로도 가능
unknown 타입은 반대로는 불가</p>
<p>그렇기 때문에 <span style="background-color:#FFE4E1">변수에 저장할 값의 타입이 확실하지 않을 때는
any 타입보다 unknown 타입이 나음</span></p>
<p>적어도 <span style="background-color:#E2D6FF">어떤 연산이나 어떤 메서드나 어떤 변수에나 값을 넣을 수는 없기 때문에
런타임 에러를 일으키는 any 타입보다는 unknown이 나음</span></p>
<hr>
<h1 id="📌-void와-never-타입">📌 Void와 Never 타입</h1>
<h2 id="☑️-void">☑️ Void</h2>
<blockquote>
<p>공허라는 뜻</p>
</blockquote>
<p>아무것도 없다.</p>
<p>즉, void 타입은 아무것도 없음을 의미하는 타입</p>
<br>

<pre><code class="language-tsx">function func1() {
  return &quot;hello&quot;;
}</code></pre>
<p>함수가 하나 있음
그냥 문자열을 반환</p>
<blockquote>
<p>참고로 타입스크립트에서는 함수의 반환 값에도 타입 정의 가능
매개변수를 작성하는 소괄호 뒤에 타입 주석을 쓰면 됨</p>
</blockquote>
<pre><code class="language-tsx">function func1(): string {
  return &quot;hello&quot;;
}</code></pre>
<blockquote>
<p>이 함수의 반환값은 string 타입이다라고 명시하는 것임</p>
</blockquote>
<br>

<pre><code class="language-tsx">function func2(): void {
  console.log(&quot;hello&quot;);
}</code></pre>
<p>이렇게 함수가 아무것도 반환하지 않을 때의
반환값 타입은? ➡️ void 타입</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/18d984b5-db2a-4352-9ad1-342694d5dc53/image.png">

<p>변수의 타입에도 void 정의 가능
➡️ 어떤 값도 저장 못하고 오직 undefined만 담을 수 있음</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/501939b2-604c-49b5-b0ef-7e345e84b517/image.png">

<p><span style="background-color:#E2D6FF">예외적으로 strictNullChecks 검사를 끄면
void에 null을 저장할 수 있음</span>
➡️ <span style="background-color:#FFC0CB">왜냐하면 이 옵션이 꺼지면, null이 어느 타입의 변수에도 들어갈 수 있으니까</span></p>
<br>

<p>별거 아니지만,
굳이 void의 필요성은?</p>
<p>우리는 이미 자바스크립트를 배울 때 아무것도 없을 때 나타내는 값이 undefined라고 배움
null도 있고</p>
<p>굳이 반환값이 없는 함수의 반환값 타입을 정의할 때
undefined나 null이 아닌
void를 쓰는 이유는?</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/6f6037a2-63d9-4054-9ae9-780c28de9ad1/image.png">

<p>이렇게 함수의 반환값을 undefined로 정의하면
이 함수는 undefined 값을 반환해야 함</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/a34a9ee6-543d-423f-8328-3f66b8a89536/image.png">

<p>혹은 이런 식으로</p>
<p>(근데, 타입스크립트 버전이 올라가면서 지금은 undefined로 타입값을 정의하고,
아무것도 반환하지 않아도 문제 없음)</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/343a7b3f-69c9-4b0c-b434-cee6eba583df/image.png">

<img src="https://velog.velcdn.com/images/ann-algorithm/post/c8ffc3b3-cd85-46d3-86b7-37a630c6dd58/image.png">

<p>그런데 null은 반환값 타입으로 정의하면
<code>return null</code>이 필수</p>
<br>

<p>따라서 return문을 사용하고 싶지 않은 경우
함수의 반환값 타입으로 void 사용</p>
<h2 id="☑️-never">☑️ Never</h2>
<blockquote>
<p>존재하지 않는,
불가능한 타입</p>
</blockquote>
<br>

<p>새로운 함수를 만들어보자</p>
<pre><code class="language-tsx">function func3() {
  while (true) {}
}</code></pre>
<p>무한루프를 도는 함수</p>
<p>아무것도 반환하지 않으니까 반환값은 void?</p>
<p>void는 함수가 정상적으로 종료되지만,
반환값이 없어서 void 타입인 것임</p>
<p><span style="background-color:#FFE4E1">이 함수는 반환할 수가 없어서 애초에 정상적인 종료가 되지 않기 때문에
이 함수가 뭔가를 반환하는 것 자체가 모순</span></p>
<br>

<pre><code class="language-tsx">function func3(): never {
  while (true) {}
}</code></pre>
<p>따라서 정상적으로 종료 자체가 되지 않기 때문에
함수에 반환값이 있는 것 자체가 모순일 경우
반환값을 never로 지정</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/bf05242e-9f1e-4481-888f-f80e4214451c/image.png" width=400>

<p>자바스크립트는 실행 도중 에러를 던져줄 수 있었음
이때는 실행되면 바로 프로그램이 중지되기 때문에
반환값으로 never가 제일 적합</p>
<p>즉, never는 불가능이고 모순을 의미함</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/959dda8c-ea52-452d-a7e8-e0302120cabf/image.png">


<p><span style="background-color:#FFEBCD">never도 변수에 정의할 수 있지만
void와 마찬가지로 아무 값도 담을 수 없음</span></p>
<p>심지어 undefined를 담을 수 있던 void와는 달리
never는 undefined도 담을 수 없음
<span style="background-color:#FFEBCD">null도 담을 수 없음</span></p>
<p>tsconfig.json에서 strictNullChecks 옵션을 꺼도 불가</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/fcf252ea-e1c2-4eea-90d2-4ab054757025/image.png">

<p><span style="background-color:#FFEBCD">any 타입의 값도 never 타입의 변수에는 담을 수 없음</span></p>
<br>

<p>이렇게 never 타입은 변수의 타입으로 활용하면
그 어떤 값도 저장할 수 없는,
그 어떤 값도 저장하는 게 말이 안 되는
변수의 타입을 정의할 때도 활용 가능</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Page Router 핵심 정리 (1)]]></title>
            <link>https://velog.io/@ann-algorithm/Page-Router-%ED%95%B5%EC%8B%AC-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@ann-algorithm/Page-Router-%ED%95%B5%EC%8B%AC-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Mon, 16 Mar 2026 10:10:40 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-페이지-라우터를-소개합니다">📌 페이지 라우터를 소개합니다.</h1>
<blockquote>
<p>현재 많은 기업에서 사용되고 있는 안정적인 라우터
리액트 라우터처럼 특정 조건을 기준으로 웹 서비스 내 페이지를 분할하고,
또 그렇게 분할된 페이지 간의 이동을 처리하는 등의 페이지 라우팅 처리</p>
</blockquote>
<h2 id="페이지-라우터">페이지 라우터</h2>
<p><span style="background-color:#FFEBCD">그 이름에 걸맞게 <code>Pages</code>라는 폴더의 구조를 기반으로 페이지를 라우팅</span></p>
<br>

<p>이게 무슨 말이냐면,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/23bb4c0d-c168-4037-889b-01df306e8b9e/image.png" width=300>

<p>pages 폴더 아래에 위와 같은 자바스크립트 파일이 있으면,
<span style="background-color:#FFEBCD">자동으로 이 파일들의 경로와 이름에 따라서 페이지 라우팅이 제공
</span></p>
<br>

<p>예를 들면,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/e09cea3e-79c8-40ba-9ee9-92bdb541bb06/image.png" width=500>

<p>브라우저에서 <code>/</code> 경로로 접속을 요청하면,
➡️ index.js 파일에 있는 컴포넌트가 페이지로써 화면에 렌더링되고
브라우저에서 <code>~/about</code> 경로로 접속을 요청하면,
➡️ about.js 파일에 있는 컴포넌트가 페이지로써 화면에 렌더링됨</p>
<p>이렇듯 <span style="background-color:#FFEBCD">페이지 라우터는 pages라는 폴더 아래에 들어있는 파일명 기반으로 페이지 라우팅을 자동으로 제공</span></p>
<br>

<p><span style="background-color:#FFEBCD">폴더의 이름으로도 페이지 라우팅을 설정할 수 있음
</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/cb972551-d5fa-42d5-9393-bd18b0a314aa/image.png" width=500>

<p><code>~/about</code>이라는 경로로 요청이 오면,
<span style="background-color:#FFEBCD">pages 폴더 아래에 about.js라는 파일이 없으니까
about 폴더 아래 index.js 파일에 있는 컴포넌트가 페이지로 렌더링됨</span></p>
<p>마찬가지로,
<code>~/item</code>이라는 경로로 요청이 오면
<span style="background-color:#FFEBCD">pages 폴더 아래에 item.js라는 파일이 없으니까
item 폴더 아래 index.js 파일에 있는 컴포넌트가 페이지로 렌더링됨</span></p>
<br>

<p>또한, 동적 경로를 갖는 페이지의 라우팅도 간단하게 설정 가능</p>
<h2 id="동적-경로란">동적 경로란?</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/a2c43001-8419-435c-b2ea-c2dfe1e3bd81/image.png" width=500>

<blockquote>
<p>경로상에, 변할 수 있는 가변적인 값을 포함하고 있는 경로
➡️ 블로그의 게시글 페이지나, 쇼핑몰의 상품별 상세 페이지 같은 곳에 자주 활용됨</p>
</blockquote>
<p>Next.js의 페이지 라우터에서 이렇게 동적 경로에 대응하는 페이지를 만들고 싶다면
<code>[id].js</code>라는 파일을 만들면,
이 파일은 동적 경로에 대응하는 페이지의 역할을 하게 됨</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/0979d466-17ba-4c2f-b8ef-aec0c39e3908/image.png" width=500>

<p>따라서 동적 경로에 있는 가변적인 값이
대괄호 속에 있는 id에 하나씩 맵핑되는 식으로 페이지 라우팅이 설정됨</p>
<h2 id="실습">실습</h2>
<blockquote>
</blockquote>
<pre><code class="language-tsx">npx create-next-app@14 section02</code></pre>
<p>npx: Node Package Executor
npmjs.com(라이브러리들이 실제로 올라가 있는 서버)에 등록된 최신 버전의 node package를 다운로드 없이 바로 실행시키는 명령어</p>
<blockquote>
<p>create next app: next.js 공식 문서에서 안내하고 있는 새로운 넥스트 앱을 생성하는 node.js 패키지
그러니까 보일러 플레이트 같은 것</p>
</blockquote>
<p>➡️ 즉, npx라는 도구를 사용해서 create next app이라는 새로운 next.js  앱을 생성할 것
(15가 나왔지만, 이 당시 더 안정적인 14버전을 사용)</p>
<blockquote>
</blockquote>
<p>section02는 패키지 이름</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/6b3aa4c0-db2b-4d1e-96b8-9723654306e2/image.png">

<p><del>설치할 라이브러리는 위와 같이...
거의 기본이었던 것 같음</del></p>
<p><code>import alias</code>의 경우,
<span style="background-color:#FFEBCD">절대 경로로 모듈을 import할 수 있도록 도와주는 기능</span></p>
<p>예를 들어</p>
<pre><code>import A from &quot;@/components/~&quot;</code></pre><p>라고 사용하면,
@는 /src 경로를 의미하기 때문에
더 간결하게 사용이 가능</p>
<br>

<p>이렇게 프로젝트를 만들면</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/156322c7-8e61-4473-bfc7-2537723cabf6/image.png">

<p>next.js도 node.js의 패키지이기 때문에
의존성을 보관하는 node_modules 폴더나,
패키지 정보를 보관하는 package.json이란 파일이 존재</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/07bfa3c9-c5b4-49de-b7aa-09f8300e194b/image.png">

<p>스크립트도 설정하고,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/a00201db-d2d5-4054-b5f0-fbcfc5044a8a/image.png">

<p>설치할 패키지도 명시</p>
<br>

<p>이 프로젝트를 개발 모드로 실행할 때는</p>
<pre><code>npm run dev</code></pre><p>로 실행함</p>
<br>

<p>next.js는 어쨌든 React를 기반으로 만들어진 확장판 개념의 프레임워크이기 때문에
next.js도 React와 흡사한 폴더 구조를 가지고 있음</p>
<blockquote>
<p>public 폴더
: 파비콘, 로고, 페이지 내부에서 사용할 이미지 파일과 같은 정적인 파일 보관</p>
</blockquote>
<p>src 폴더
-&gt; pages 폴더: 페이지 역할을 할 컴포넌트를 보관하는 파일을 경로에 맞게 보관
-&gt; styles 폴더: 컴포넌트나 앱의 전체적인 스타일을 담당하는 css 파일 보관</p>
<br>

<p>인덱스 경로의 페이지 역할을 하는 <code>index.tsx</code> 파일에 들어가,
<strong>Home</strong>이라는, default로 내보내진 페이지 역할을 하는 컴포넌트가 작성되어 있음</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/9cfc298f-152c-473c-b9ee-8be06209c728/image.png">

<p><span style="background-color:#FFC0CB">이 Home 컴포넌트가 리턴하고 있는 HTML 요소들이 우리가 next.js 앱에서, 인덱스 페이지에 접속했을 때 나타나는 요소들</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/a31a3bc9-45e2-4d32-8c0e-afae726c2a6a/image.png">
(왜인지 모르겠지만 이미지가 전부 깨지고 있다...)

<img src="https://velog.velcdn.com/images/ann-algorithm/post/3a3ae3af-dc29-41aa-9b10-e0e05e366dc1/image.png">

<p>그래서 일단 이미지를 다 지워줌</p>
<br>

<p>만약 <code>&lt;main&gt;</code> 태그 안 쪽에 <code>&lt;h1&gt;</code> 태그를 추가해보면,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/efc32f69-64a9-4983-bf19-4ca168ae51dc/image.png" width=500>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/c66766ac-fb8c-4396-b611-d13442242573/image.png">

<p>잘 렌더링 됨</p>
<br>

<p>페이지 라우팅 실습을 해보면,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/9874788f-a9b2-4fd4-924e-efbd38b9b032/image.png">

<p>pages 폴더 아래에 test.tsx라는 파일을 생성하고,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/3adab3ef-1005-4f00-b49c-e77cfa29db80/image.png" width=400>

<p>해당 페이지에 컴포넌트 작성</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/4e0b0553-22f9-493e-ba13-009d972bab09/image.png">

<p>파일명인 /test 경로로 접속하니,
위에 작성된 페이지 컴포넌트가 화면에 렌더링됨</p>
<br>

<h3 id="span-stylebackground-colore0ffff그런데-_apptsx와-_documnettsx가-뭘까span"><span style="background-color:#E0FFFF">그런데, <code>_app.tsx</code>와 <code>_documnet.tsx</code>가 뭘까?</span></h3>
<p>위 두 파일은, 페이지의 역할을 하는 페이지는 아니고,
넥스트 앱의 모든 페이지에 공통적으로 적용될 로직이나,
또는 공통적으로 적용될 레이아웃 또는 데이터를 다루기 위해 필요한 파일</p>
<h3 id="_apptsx">_app.tsx</h3>
<blockquote>
<p>리액트의 <code>app.tsx</code>와 동일한 역할</p>
</blockquote>
<ul>
<li>모든 컴포넌트의 부모 컴포넌트 역할</li>
<li>루트 컴포넌트</li>
</ul>
<p>즉, 동일하게 루트 컴포넌트 역할을 함</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/6b93fdcb-eff2-4d52-9e6a-b3d455647abd/image.png" width=600>

<p>props를 살펴 보면,</p>
<p><span style="background-color:#FFEBCD"><strong>Component</strong>: 현재 페이지 역할을 할 컴포넌트
<strong>pageProps</strong>: Component에 전달될 페이지의 props를 모두 객체로 보관</span></p>
<p>따라서, return문을 보면
페이지 역할을 할 컴포넌트를 렌더링하면서,
props는 전달받은 pageProps 그대로 구조분해 할당으로 전달</p>
<p>결론적으로 next.js에서 어떤 페이지를 렌더링하던 간에
_app.tsx 컴포넌트 밑에 페이지 역할을 하는 컴포넌트가 렌더링되는 이 구조로 렌더링됨</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/cec0eb02-50b2-42a2-86a4-75ea6d79b70e/image.png">

<p>이것을 브라우저로 살펴보면,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/763a58b7-9cdc-4426-8469-ee19c20c0915/image.png">

<p>App 컴포넌트 아래에 Page 컴포넌트가 있는 걸 알 수 있음</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/329d82af-cfc9-4f8b-b00d-0d31ed2675cc/image.png">

<p>렌더링되어야 할 페이지 컴포넌트의 page 라는 함수를 잘 전달 받고 있고
pageProps는 아무것도 없는 것까지 확인</p>
<br>

<p>그렇기 때문에
이 next.js 앱에서 모든 페이지에 공통적으로 타나타야 되는,
예를 들면 Header 같은 공통 요소가 필요하다면,
이 App 컴포넌트의 return 문에 추가</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/3f6c16b7-2139-4c5c-a965-a0984c1fa745/image.png" width=500>

<p>그러면 이 프로젝트의 어떤 페이지를 가도 해당 header가 나타남</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/ef0c5805-796e-4b46-a80e-351b345a1371/image.png">

<p>/test 경로에도,
/ 경로에도 나타나는 걸 볼 수 있음</p>
<p>(만약 /에서 안 나오면, 기존 퍼블리싱 때문 + 모바일 사이즈로 볼 때 문제가 있을 수도)
(따라서 기존 요소를 다 지우거나, 하면 나옴)</p>
<h4 id="정리">정리</h4>
<p>따라서 이 App 컴포넌트는, 모든 페이지 컴포넌트들의 부모 역할을 하는 next.js 앱의 루트 컴포넌트
➡️ <span style="background-color:#FFEBCD">전체 페이지에 공통으로 포함되는 (헤더 컴포넌트 같은) 컴포넌트 또는 레이아웃을 렌더링 한다거나, 
비즈니스 로직을 작성할 수 있는 공간</span></p>
<h3 id="_documenttsx">_document.tsx</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/40f1d664-7c65-4b8d-98a6-25b6dd53e924/image.png" width=600>

<p><span style="background-color:#FFEBCD">모든 페이지에 공통적으로 적용이 되어야 하는 next.js 앱의 HTML 코드를 설정하는 컴포넌트</span>
기존의 리액트 앱의 index.html과 비슷한 역할</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/741fb2c3-946c-4c88-95af-49504dbbbb40/image.png">

<p>위처럼 <span style="background-color:#FDF5E6">랭귀지 속성</span>을 바꾼다든지,</p>
<p>혹은 <span style="background-color:#FDF5E6">모든 페이지에 다 적용이 되어야 하는 메타 태그를 설정</span>한다거나,
<span style="background-color:#FDF5E6">폰트</span>를 불러온다거나,
<span style="background-color:#FDF5E6">char-set</span>을 설정한다거나,
<span style="background-color:#FDF5E6">구글 애널리틱스 같은 서드파티 스크립트</span>를 넣는다거나, 등등의</p>
<p><span style="background-color:#FFEBCD">페이지 전체에 다 적용되는 html 태그를 관리하기 위해 사용</span></p>
<h3 id="nextconfigmjs">next.config.mjs</h3>
<p>next 앱의 설정 관리</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/86b7b950-f375-44f6-a20e-13881b41128b/image.png" width=500>

<p>이것은 실습을 위해 지금은 false로 끔</p>
<p>왜 꺼야 하냐?
➡️ 이 모드가 켜져 있으면
<span style="background-color:#E2D6FF">리액트 앱의 존재하는 잠재적인 문제를 검사하기 위해서
개발모드로 실행했을 때 컴포넌트를 두 번 실행</span></p>
<p>따라서, 콘솔을 찍는다든지
디버깅하기가 불편하기 때문에 <strong>false</strong></p>
<hr>
<h1 id="📌-페이지-라우팅-설정하기">📌 페이지 라우팅 설정하기</h1>
<p>앞으로는 하나의 프로젝트를 만들어보며 실습할 것!</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/4394f7ae-2cc8-4993-b983-f60a990e9974/image.png">

<p>간단한 도서 조회 및 리뷰 서비스를 만들어보자!</p>
<br>

<blockquote>
<p>section02 폴더에서,</p>
</blockquote>
<pre><code>npm run dev</code></pre><p>로 서버 가동</p>
<br>

<ul>
<li>index.tsx 파일의 기본적인 요소 전부 제거</li>
<li>_app.tsx 파일이 globals.css 파일을 import 하고 있으므로, globals.css 파일 내용 모두 제거</li>
<li>Home.module.css 파일도 전부 제거</li>
</ul>
<p>만약 페이지에 오류가 발생하면
index.tsx 파일의 import문까지 모두 제거</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/5217755a-0f8b-48b6-abca-d05303b81973/image.png">

<p>그러면 말끔한 대문(?)을 마주할 수 있음</p>
<br>

<h2 id="search-페이지">search 페이지</h2>
<p>서치 페이지는 브라우저 상에서
/search 라는 경로로 접속할 수 있어야 하므로</p>
<p><span style="background-color:#E2D6FF">프로젝트에 pages 폴더 아래에 새 파일로 search.tsx 파일 새로 생성</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/3e81d4f7-76b4-4db1-95eb-7aa1b5cd43ce/image.png">

<p>그리고 해당 파일에 컴포넌트를 만들면,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/abda7e2d-6fc8-4db8-b9b7-b115927ae005/image.png" width=500>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/e7227779-500a-4607-9982-c089cb7d5f8e/image.png">

<p>해당 경로로 요청 시 컴포넌트 렌더링</p>
<br>

<p>폴더로 분리하고 싶으면</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/e9001705-915c-4e8c-951b-b7add877e0e0/image.png">

<p>해당 경로를 이름으로 한 폴더 아래 index.tsx를 만들어주면,
pages 아래에 search.tsx 파일이 없어서
search 폴더 아래에 있는 index.tsx 파일의 컴포넌트를 페이지로 렌더링</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/99c29fa4-5000-48b1-8d87-6fdaab4412b0/image.png">

<p>해당 경로로 요청 시 컴포넌트 렌더링되는 것 확인</p>
<br>

<p>이 <span style="background-color:#FFEBCD">search 페이지 밑에 더 다양한 경로를 중첩으로 만들고 싶다면,
중첩 라우팅 가능</span></p>
<p><span style="background-color:#E2D6FF">pages/search 폴더 아래에 해당 경로로 사용할 이름으로 파일 생성</span>
그 파일에 페이지 역할을 할 컴포넌트 생성</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/fbe9d2f3-8372-4edb-ba1f-b79d539177ae/image.png">

<img src="https://velog.velcdn.com/images/ann-algorithm/post/755d5c5e-fac0-49f7-9457-ed87f47decbf/image.png" width=500>

<p>그러면 /search/setting 경로로 요청 시 작성한 컴포넌트가 렌더링됨</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/eebd4064-1e92-4baf-bd9f-bd45bdd4c4b2/image.png">

<br>

<p>물론 폴더로 분리하는 것도 가능
아래처럼</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/e5fb2d7c-18ff-471f-ba2f-364165a5ff8f/image.png">

<br>

<h3 id="쿼리-스트링-설정">쿼리 스트링 설정</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/4b343f13-3665-4222-a74d-f539217e13cc/image.png">

<p>이런 식의 URL을 본 적이 있을 것</p>
<p>쿼리 스트링은,
<span style="background-color:#FFEBCD">경로에서 물음표(?)와 함께 표현되며,
주로 검색 기능을 제공하는 웹 사이트에서 사용자의 검색어나 필터링 조건을 전달할 때 사용됨</span></p>
<p><span style="background-color:#E2D6FF">페이지 경로 자체에는 영향을 주지 않고,
Next.js 프로젝트에서도 해당 페이지가 쿼리 스트링을 쓰는 페이지라고 해서
폴더 구조를 따로 변경해야 하거나, 하진 않음</span></p>
<br>

<p>대신 전달된 쿼리 스트링의 값을,
이 서치 페이지 컴포넌트에서 직접 꺼내서 사용하려면,
<span style="background-color:#FFEBCD">그때는 파일의 최상단에 <strong>useRoute</strong>라는 훅을 Next Router라는 패키지로부터 불러와야 함</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/930bef59-3abb-4839-b982-d03ccb4dcdad/image.png">

<p>import를 하려면 두 가지 패키지가 뜨는데,
<span style="background-color:#FFC0CB">이중 next/router에서 불러와야 함</span>
(next/navigation은 App Router에서 쓸 것)</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/dae865bf-2b27-4ff0-b59c-4544850d81cc/image.png" width=500>

<p><strong>useRoute</strong>: 이름 그대로 router라는 객체를 컴포넌트 내부에서 사용할 수 있도록 반환하는 함수
➡️ router 변수 안에, router라는 객체가 저장
<span style="background-color:#FFEBCD">이 객체에는 우리가 필요한 대부분의 라우팅과 관련된 정보가 다 저장되어 있음</span></p>
<br>

<p>이 router 객체를 출력해보면,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/007e986a-a7d1-41d7-8540-cad175c0068d/image.png">

<p><span style="background-color:#FFC0CB">back이나 push 처럼 뒤로가기나 다른 경로로 이동하는 메서드도 있고</span></p>
<p><span style="background-color:#E2D6FF">객체 형태로 전달한 쿼리 스트링 값도 query 안에 잘 들어가 있음</span></p>
<br>

<p>근데 왜 콘솔 메시지가 두 번 출력되지?</p>
<p><span style="background-color:#FFEBCD">Next.js 앱이 우리가 전달한 쿼리 스트링을 읽는 과정에, 컴포넌트를 한 번 더 렌더링시키기 때문</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/ce1ae119-5a0b-4e13-ba72-47812d5bce40/image.png">

<p>그래서 <span style="background-color:#FFE4E1">첫 번째로 출력된 ㅌ메시지에는,
아직 쿼리 스트링을 읽어오지 못해
쿼리에 빈 객체가 출력됨</span></p>
<p>하지만 <span style="background-color:#FFE4E1">두 번째로 출력된 메시지에는,
우리가 전달한 쿼리 스트링을 잘 읽어오고 있는 것도 확인 가능</span></p>
<br>

<p>아무튼, 이렇게 쿼리 스트링이 router라는 객체 안에 담겨있으니
코드에서 해당 스트링 값을 꺼내올 수 있음</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/724e5119-40f5-4f3c-9092-ce7b48a645c4/image.png" width=500>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/29bda871-fe74-4d62-b15f-aca50ee715c0/image.png">

<p>쿼리 스트링이 화면까지 잘 렌더링되고 있음</p>
<p>이 쿼리스트링은 페이지 경로에는 영향을 주지 않기 때문에
디렉토리에 별도 설정이 없지만,
<span style="background-color:#FFEBCD">컴포넌트 내부에서 쿼리스트링의 값을 읽어오기 위해
useRouter라는 훅을 불러와서 라우터의 쿼리라는 프로퍼티를 통해 불러올 수 있음</span></p>
<h2 id="book-페이지">Book 페이지</h2>
<p>Book 페이지는 <code>localhost:3000/book/100</code>과 같이 가변적인 값을 포함하는 동적 경로 사용
이렇게 가변적인 값을 URL 파라미터라고 함</p>
<p>이렇게 URL 파라미터를 사용하는 동적 경로를 갖는 페이지를 생성하려면,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/5cb12df5-fa47-4c60-b2d0-04fc5b05bfed/image.png">

<p>위와 같이 대괄호 안에 id값이 있는 파일명 형태로 파일 생성</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/3134a3ac-8e12-4eca-a44a-a08959a56025/image.png" width=500>

<p>파일 안에 이렇게 페이지 컴포넌트를 만들어줌</p>
<br>

<p>위처럼 설정하면,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/0142a978-89a0-48e1-9fce-68441ca349bf/image.png" width=500>

<p>Next.js는 대괄호가 포함된 파일명을 보고,
이 파일은 URL 파라미터를 갖는 동적 경로에 대응하는 파일이라고 인식해서
<span style="background-color:#FFC0CB">URL 파라미터의 값이 뭐가 되든, 이 파일에 작성된 컴포넌트를 페이지로써 화면에 렌더링 시키도록 설정</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/a1f76851-8a30-48f1-84a8-a338363bbc75/image.png">

<p>1이 아니라 다른 값이어도 같은 페이지가 렌더링됨</p>
<br>

<p>그리고 <span style="background-color:#FFE4E1">이렇게 전달된 URL 파라미터의 값을 페이지 컴포넌트에서 꺼내서 사용하려면,
마찬가지로 useRoute 훅을 이용</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/c57b3711-d1f9-45c1-a80b-535e13ad0193/image.png" width=500>

<p>이 훅의 반환값인 router 객체를 살펴보면,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/cd1e126f-86a0-4e2e-9c0d-3fb4a3af389f/image.png">

<p><span style="background-color:#FFEBCD">query라는 프로퍼티에, 전달한 URL 파라미터가 id라는 이름으로 저장됨</span>
앞서 살펴본 쿼리 스트링과 똑같은 방식으로 라우터 객체에 저장됨</p>
<p>이렇게 저장되는 이유는
<span style="background-color:#FFC0CB">대응할 파일의 이름을 [id].tsx로 저장했기 때문에 id라는 이름으로 매핑
</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/e2528917-74ad-4aaa-848f-9dd9c30cdebe/image.png">

<br>

<p>이 id값만 따로 쓰고 싶은 경우 
useRouter 훅의 반환값인 router 객체에서 파라미터 값만 가져오면 됨</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/32510698-f743-4392-8e12-5b70f69b4acc/image.png" width=500>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/99629fde-3e87-4697-b21c-d575bba11b1b/image.png">

<p>가져온 id값으로
렌더링해온다든지...</p>
<h3 id="만약-id가-중첩되면">만약 id가 중첩되면?</h3>
<p><code>localhost:3000/book/225/225/255</code> 경로로 접속하면?
현재는 404 Not Found가 발행될 것</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/12aac8b1-7e0e-4325-9789-422f34abf372/image.png">

<p>왜냐하면,
<span style="background-color:#FFEBCD">지금 프로젝트에서 [id].tsx 파일은 /book 경로 뒤에 
딱 하나의 id값만 전달이 되는 그런 경로에만 대응하도록 동작이 되기 때문에</span>
<strong>지금처럼 여러 개의 id가 연달아 전달되는 경로에는 대응 불가</strong></p>
<br>

<p>위와 같이 여러 개의 id가 연달아 전달되는 경로에도 대응하는 범용적인 페이지를 만들고 싶다면,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/3933168f-545e-4992-96bc-5a37fa8646c3/image.png">

<p><code>[...id].tsx</code>
<span style="background-color:#FFE4E1">...은 book이라는 경로 뒤에 여러 개의 id가 연달아 들어올 수 있고, 그러한 모든 id에 다 대응하겠다는 뜻</span></p>
<br>

<p><span style="background-color:#FFEBCD">Next.js에서는 이런 식으로 설정되어 있는 경로를,
&quot;모든 경로를 다 잡아채겠다&quot;라는 뜻에서
<strong>캐치 올 세그먼트</strong>라고 함</span></p>
<p>세그먼트 === 구간
➡️ <span style="background-color:#FFC0CB">구간, 경로 상에 슬래시로 구분되는 모든 구간에 대응하겠다는 의미</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/46b55ef0-eed1-4ce4-895f-1e25484e21f2/image.png">

<p>파일명을 [...id].tsx로 정하고,
id값을 콘솔에서 출력하면
이 id는 url로 전달된 여러 파라미터 값을 배열로 받기 때문에</p>
<p>오른쪽 화면처럼 확인 가능함</p>
<h3 id="캐치-올-세그먼트로-대응할-수-없는-경로">캐치 올 세그먼트로 대응할 수 없는 경로</h3>
<p>바로
<code>localhost:3000/book</code> 뒤에 아무 id값도 오지 않는 경우</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/e29a9f4c-ab0c-4a32-95a5-486ff443497e/image.png">

<p>이 경우 캐치 올 세그먼트도 대응이 불가능함</p>
<br>

<p>왜냐하면,
<span style="background-color:#FFEBCD">캐치 올 세그먼트는, 뒤에 어떤 id값이든 나와야 대응이 되기 때문</span>
그런데 지금은 /book으로 끝났으니까 404가 리턴됨</p>
<h4 id="이를-위해-대응하려면">이를 위해 대응하려면?</h4>
<p>(1) 디폴트 페이지를 만들어 대응하거나,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/6e12a5d5-0c8c-45e1-a26a-9a5e95a70ee6/image.png">

<br>

<p>(2) 파일 이름을 변경하여, 완전히 범용적인 페이지로 생성
<code>[[...id]].tsx</code>로 설정
➡️ <strong>옵셔널 캐치 올 세그먼트</strong>라고 함</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/40ffbd8f-7452-42bc-87d9-ae7fe3f95cec/image.png">

<p>이제 /book뒤에 경로가 들어오든 안 들어오든
모두 대응 가능한 페이지가 됨</p>
<h3 id="존재하지-않는-경우-나타나는-404-페이지">존재하지 않는 경우 나타나는 404 페이지</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/f1b45df2-30bd-4872-a86f-b3068bd17b75/image.png">

<p>이때 렌더링하는 페이지를 만들려면?</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/d5d00023-dc86-4991-b618-5cd2bb75de8c/image.png">

<p><span style="background-color:#FFEBCD">pages 폴더 아래에 <code>404.tsx</code> 파일을 만들어 페이지 역할을 할 컴포넌트를 만들면 됨</span></p>
<hr>
<h1 id="📌-네비게이팅">📌 네비게이팅</h1>
<p>Next.js는 리액트의 확장판
➡️ 리액트의 장점이었던 CSR 방식의 빠르고 쾌적한 페이지 이동을 그대로 계씅</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/83901701-d267-40a6-af12-feae2dfc1e62/image.png">

<p>이번에도 Next.js에서
어떻게 하면 CSR 방식으로 페이지를 이동시킬 수 있는지 초점을 맞춰보자</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/765fe634-9ea2-4699-b4cc-4655da01b32c/image.png">

<p>이 App 컴포넌트 안에 있는 <code>&lt;header&gt;</code> 요소 안에 간단한 네비게이션 바를 만들자
(누르면 다른 페이지로 이동할 수 있는...)</p>
<br>

<p>HTML 코드를 작성할 때,
다른 페이지로 이동하는 링크를 만들고 싶을 땐</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/06b67afc-12e8-4886-8308-11298602f9f6/image.png">

<p>이렇게 <code>&lt;a&gt;</code>태그를 사용</p>
<p>그런데 이 <code>&lt;a&gt;</code> 태그 방식은 CSR 방식이 아니라 일반적인 방식으로,
<span style="background-color:#FFE4E1">서버에게 새로운 페이지를 매번 다시 요청하는 방식으로 페이지를 이동시키기 때문에</span>
<span style="background-color:#FFE4E1">비교적 느린 방식으로(원하는 방식이 아님) 페이지를 이동시키게 됨</span></p>
<br>

<p><span style="background-color:#FFEBCD">Next.js 앱에서는 a 태그가 아닌 내장 컴포넌트인 링크 컴포넌트를 이용</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/cfe6c4f4-e90f-47ff-8fd9-132cddc4be36/image.png" width=400>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/18d2b218-23c8-46d3-9901-997fc37ae955/image.png" width=500>

<p>import문으로 컴포넌트를 가져오고,
<code>&lt;Link&gt;</code> 태그는 기본적으로 <code>&lt;a&gt;</code> 태그와 사용법이 동일</p>
<p><span style="background-color:#FFEBCD">href 속성으로 이동하고자 하는 페이지의 경로를 표시</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/9fe4a67b-3d00-4a9a-8e3c-db8f5586dcaf/image.png">

<p>그럼 위처럼 <span style="background-color:#E2D6FF">브라우저의 CSR 방식으로 페이지를 이동시키는 링크가 잘 렌더링됨</span></p>
<br>

<p>다른 페이지로 이동하는 링크도 만들어보자</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/5e519433-60c7-41e1-99d6-bf6a6d3a9217/image.png">

<p>간격이 너무 없을 땐 Link 태그 사이 <code>&amp;nbsp</code> 를 사용</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/42874f08-3399-4f3f-b7e7-ae4c793f448d/image.png">

<p>이제 페이지를 이동시킬 때마다 
CSR 렌더링 방식으로 쾌적하고 빠르게 페이지 이동이 제공됨</p>
<br>

<p>정리하자면,</p>
<p><code>&lt;a&gt;</code> 태그 방식은 CSR 방식이 아니라 일반적인 방식
즉, 서버에게 새로운 페이지를 매번 다시 요청하는 방식으로 페이지 이동
= 비교적 느리기에 원하지 않는 방식이기 때문에</p>
<p><code>&lt;a&gt;</code> 태그보다는 <code>&lt;Link&gt;</code> 컴포넌트를 이용!</p>
<br>

<p>위처럼 링크 컴포넌트를 이용해서 CSR 방식의 링크 생성 외에,
<span style="background-color:#E0FFFF">어떤 함수가 실행이 된다거나,
어떠한 이벤트가 발생했을 때에도 페이지를 이동시키는 방식이 있음</span></p>
<h2 id="프로그래매틱한-페이지">프로그래매틱한 페이지</h2>
<p>사용자가 링크를 직접 클릭했을 때 페이지를 이동시키는 방식이 아니라,
<span style="background-color:#FFEBCD">특정 버튼이 클릭되었거나, 특정 조건이 만족했을 경우 페이지 이동</span></p>
<h3 id="1️⃣-버튼을-추가해서-페이지-이동">1️⃣ 버튼을 추가해서 페이지 이동</h3>
<p>버튼을 추가해서 해당 버튼을 클릭했을 때
이벤트 핸들러 안에서 페이지를 이동시키는 기능 구현</p>
<h4 id="1-버튼-추가">(1) 버튼 추가</h4>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/0df12f49-24c0-4df1-afbd-2bd6cbe91574/image.png">

<p>먼저 코드에 버튼을 추가하고,
(버튼을 클릭했을 때의 이벤트가 없어서 페이지를 이동하진 않음)</p>
<h4 id="2-온클릭-함수-추가">(2) 온클릭 함수 추가</h4>
<p>버튼이 클릭되었을 때 동작할 이벤트 핸들러 생성</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/99006307-4ba4-4843-b38e-7651c23ce7b5/image.png" width=500>

<ul>
<li>useRouter import</li>
<li>router에 라우터 객체를 저장</li>
<li>push 메서드를 호출해, 인수로 이동하고 싶은 경로 명시</li>
</ul>
<p><span style="background-color:#FDF5E6">이제 버튼을 클릭하면,
onClickButton 함수가 실행되고
라우터 객체의 push 메서드가 실행됨</span></p>
<p>그럼 인수로 전달받은 경로로
페이지를 CSR 방식으로 이동</p>
<br>

<p>이 방식으로
<span style="background-color:#FFEBCD">컴포넌트 내부에서 특정 조건을 만족했다거나
useEffect를 통해 어떠한 상황을 가정했을 때,
함수 내부에서도 페이지를 CSR 방식으로 이동시킬 수 있음!</span></p>
<br>

<p>그외에
라우터 객체에는 push 말고도
<span style="background-color:#E2D6FF">뒤로 가기를 방지하면서 페이지를 이동시키는 replace나,
뒤로 가기를 시키는 back 같은 메서드도 있음</span></p>
<hr>
<h1 id="📌-프리페칭">📌 프리페칭</h1>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/486c7cf5-dacb-405b-bd60-43bec0028f25/image.png">

<p>즉, 사전에 불러온다는 뜻인데,
바로 페이지를 사전에 불러온다!</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/9f450437-d592-4202-ba66-6d3ac2f4ef4f/image.png">

<p>Next.js의 프리페칭은 <span style="background-color:#FFEBCD">사용자가 현재 보고 있는 페이지에서,
링크를 통해 이동할 수 있는,
현재 연결되어 있는 모든 페이지를 사전에 미리 불러와 놓는 기능</span>임
<span style="background-color:#FFC0CB">➡️ 현재 사용자가 보고 있는 페이지에서, 이동할 가능성이 있는 모든 페이지를 미리 불러놓는 기능</span></p>
<br>

<p>‼️ Next.js가 이런 기능을 기본적으로 제공하는 이유는
사용자들이 다른 페이지로 이동하기 위해서 이런 웹 페이지 내부의 링크를 클릭하기 전에
현재 페이지에서 이동이 가능한 모든 페이지들에 필요한 데이터를 미리 불러와 놓음으로써
페이지 이동을 매우 빠른 속도로 지체 없이 처리하기 위함</p>
<br>

<p>생각해보면,
이 프리페칭인란 기능은</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/0186f3c6-46a2-4f4e-8251-963b6a10d0d3/image.png">

<p>Next.js에서는 초기 접속 요청이 발생했을 때,
서버가 브라우저에게 사전렌더링된 HTML 페이지를 응답한 이후에
모든 자바스크립트 코드를 번들 형태로 전달</p>
<p>초기 접속 요청이 종료된 이후에 발생하는 페이지 이동은
서버에게 별도의 추가적인 요청 없이
브라우저 측에서 직접 자바스크립트 코드를 실행시켜서,
리액트 앱을 직접 실행하여 필요한 컴포넌트를 교체하는 방식으로,
브라우저가 클라이언트 사이드 렌더링 방식으로 처리함</p>
<p>그러니까, <span style="background-color:#E0FFFF">페이지 이동을 하더라도 서버에게 추가적인 리소스를 요청할 필요가 없는데
왜 프리페칭 같은 기능이 필요한 걸까?</span></p>
<br>

<p><span style="background-color:#FFC0CB">프리페칭은 빠른 페이지 이동을 위해 제공되는 기능임!
</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/35caa7d2-2191-4d17-a0e6-d4d5f88a3086/image.png">

<p>그래서,
이미 초기 접속 요청이 완료가 되어서 페이지가 렌더링 되었는데
왜 그 상태에서 다른 페이지로 이동하기 위해 추가적인 데이터를 왜 또 불러와야 하냐면,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/831bc30e-6e67-4b26-916f-7e4a132f2062/image.png">

<p><span style="background-color:#FFEBCD">Next.js는 우리가 작성해둔 모든 리액트 컴포넌트를 자동으로 페이지별로 분리해서 저장을 미리 해두기 때문</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/e2ada11f-29a8-4556-8cde-415a0e996db0/image.png">

<p>사실은 사전렌더링 과정에서 자바스크립트 번들 파일을 전달할 때,
<span style="background-color:#FFEBCD">모든 페이지에 필요한 자바스크립트 코드가 전달되는 게 아니고,
현재 페이지에 해당하는 자바스크립트 코드만 전달됨</span></p>
<p>예를 들어 /search라는 경로로 search 페이지로 접속 요청을 보냈다면
다시 전달되는 자바스크립트 번들 파일에는 search 페이지에 해당하는 코드들만 전달됨</p>
<br>

<p><span style="background-color:#E2D6FF">왜냐하면,
전달되는 자바스크립트 코드의 양을 줄이기 위해서!</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/8b472597-89a1-4a44-a9a8-046d9a885a09/image.png">


<p>항상 초기 접속 요청이 있을 때마다
모든 페이지에 해당하는 자바스크립트 코드를 매번 다 번들링해서 전달하게 되면,
한 방에 전달하는 파일의 용량이 너무 커지게 됨
➡️ <span style="background-color:#FFEBCD">전달되는 용량이 커지기 때문에 다운로드 받는 속도도, 브라우저에서 느려짐</span></p>
<p>또한, 자바스크립트 코드를 실행해서 
<span style="background-color:#FFC0CB">브라우저에 렌더링 되어 있는 HTML과 연결돠는 하이드레이션 과정도 오래 걸리고
결국 앱에 상호작용할 수 있게 되는 시간인 TTI도 최종적으로 늦어짐</span></p>
<br>

<p>따라서 Next.js는 경제적으로 이 문제를 해결하기 위해서
사용자가 현재 접속을 요청한 페이지에 해당하는 자바스크립트 코드들만 따로따로 보내주게 되는 것
(처음에 봤던 사전렌더링 방식)</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/61f59b7e-9586-414f-bdfb-00f1550fc60f/image.png">

<br>

<p>그러면 초기 접속 이후로 발생하는 페이지 이동은 CSR 방식으로 추가적인 요청없이 바로 처리되는 게 아님!</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/22fb883f-04e0-4646-ba25-bc7a664c2c0f/image.png">

<p>페이지로 이동하게 되면,
초기 접속한 페이지에서 ➡️ 다른 페이지로 이동하게 될 텐데
<span style="background-color:#FFEBCD">Next.js는 초기 접속 시 현재 접속 요청한 페이지에 해당하는 자바스크립트 코드만 보내준다고 했으니,
매번 자바스크립트 코드를 추가로 불러와야 하는 과정이 필요</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/83f9e6c9-1e1f-4ca1-aaf7-a23d23b674e3/image.png">


<p>그런데 이게 하이드레이션은 빨라질 수 있지만 페이지 이동은 느려지고 비효율적인 것!
➡️ 그래서 프리페칭 등장</p>
<br>

<p>다시,
<span style="background-color:#E0FFFF">프리페칭이 뭐냐면,</span>
<span style="background-color:#FFEBCD">현재 사용자가 보고 있는 웹 페이지에서
링크가 존재한다든가
아니면 버튼이 존재해서 이동할 수 있는 가능성이 있는 모든 페이지의
자바스크립트 코드를 미리 불러와놓는 과정</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/586f94a5-26a4-420c-86d3-c6d481174d4e/image.png">

<p>위 단점을,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/15152e07-eeaf-4caa-be70-91e9dba4ac22/image.png">

<p>아래처럼 해결!</p>
<p><span style="background-color:#FFEBCD">초기 접속이 완료된 이후 곧바로 페이지 이동이 이루어지기 전에
프리페칭이 발생해서 현재 페이지와 연결된,
현재 페이지에서 이동할 수 있는 모든 페이지들의 자바스크립트 코드를 미리 불러와 놓기 때문에</span></p>
<p><span style="background-color:#FFE4E1">페이지를 이동할 때는 추가적인 데이터를 서버에게 요청할 필요가 없어서
기존처럼 CSR방식의 장점대로 굉장히 빠른 속도로 페이지 이동이 가능해짐</span></p>
<p>즉, 페이지를 이동할 때마다 자바스크립트 코드를 서버에서 불러와야 했던 단점을,
페이지 이동이 발생하기 전에 미리 부르니까 정작 페이지를 이동할 때는 빠르다!</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/59fc0b50-b836-478e-bb76-7e2f8d4e9995/image.png">

<p>결국 이런 방식으로 동작한다고 소개했던 Next.js의 사전 렌더링은,
초기 접속 요청 시에 모든 페이지에 대한 자바스크립트 파일이 다 전달되는 건 아니었고,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/aeec3a18-a9cc-4e99-aab9-e152ffef92cd/image.png">

<p>현재 접속 요청이 발생한 페이지에 해당하는 자바스크립트 번들 파일만 전달되고,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/3750e6c1-4fc3-413a-b242-1699d13ee3a5/image.png">

<p>그 이후 페이지 이동을 빠르게 제공하기 위해서,</p>
<p>페이지에 접속한 이후에는 프리페칭이라는 기능을 통해,
현재 페이지에서 이동할 수 있는 페이지에 대한 자바스크립트 코드를 사전에 미리 불러와 놓게 된다고 정리</p>
<p>그리고 이를 통해
초기 접속 요청 시 하이드레이션 빠르게 처리하면서,
프리페칭을 통해 이후 페이지 이동까지 빠르게 처리</p>
<h2 id="실습-1">실습</h2>
<p><code>npm run dev</code> 처럼 개발 모드로 가동하면 프리페칭이 동작하지 않음
매번 페이지마다 필요한 자바스크립트 코드를 서버로부터 매번 불러오기 때문
<br></p>
<p>따라서 빌드에서 실행하는 프로덕션 모드로 실행</p>
<pre><code class="language-tsx">npm run build</code></pre>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/11203f2c-bbbf-4697-9d51-017d4b4fa4f9/image.png">

<p>빌드 결과가 페이지별로 출력</p>
<p>자바스크립트 번들의 용량이 출력되고,
스플릿팅 되는 것까지 확인(라우트 별로 표시: (/, /404, /book/[[...id]], /search, /test))</p>
<br>

<pre><code class="language-tsx">npm run start</code></pre>
<p>프로덕션 모드로 실행</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/028953f9-655c-4b65-91b6-8bcdb69f8c0c/image.png">

<p>헤더로 인해
<span style="background-color:#E2D6FF">search 페이지와 book 페이지는 프리페칭이 완료된 상태라서
search 페이지로 이동해도 네트워크 탭에서는 아무 요청도 추가적으로 요청하지 않음</span></p>
<p>가끔 페이지에 대한 자바스크립트 코드를 다시 불러오는 네트워크 로그가 뜨긴 하는데
캐시가 만료되어서 다시 불러온 거고
기본적인 동작은 그대로 진행됨</p>
<br>

<p>단, 예외가 하나 있음</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/aaa518ab-ad67-493f-9859-55c52ba1ffa4/image.png">

<p>index 페이지에서
[/test 페이지로 이동] 버튼을 클릭하면,
맨 마지막, 번들을 추가로 불러옴
➡️ 이 /test 페이지는 프리페칭이 이루어져 있지 않은 거임</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/540e010a-cddf-4230-ad19-34c38652a860/image.png" width=600>

<p>App 컴포넌트를 보면
<span style="background-color:#E2D6FF">프리페칭이 이루어진 search 페이지와 book 페이지는 Link 컴포넌트로 구현되어 있지만</span></p>
<p><span style="background-color:#FFC0CB">test 페이지는 프로그래매틱하게 페이지를 이동하도록 설정해두었기 때문</span></p>
<br>

<p>결론적으로, <span style="background-color:#FFEBCD">Link 컴포넌트로 명시된 경로가 아니라면,
프리페칭이 일어나지 않음</span></p>
<br>

<p>만약 /test에도 프리페칭 시켜주고 싶으면
App 컴포넌트가 마운트되었을 때, 즉 처음 화면에 그려지게 되었을 때</p>
<p><span style="background-color:#FFEBCD">router 객체의 특정 메서드를 통해 직접 프로그래매틱하게 이 test 페이지를 프리페칭하도록 코드 작성</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/f4f8e1eb-109c-428c-ae0b-836bda99002c/image.png">

<p>컴포넌트 내부에서 <strong>useEffect</strong>호출
마운트 되었을 때만이니까, 의존성 배열은 비워두고</p>
<br>

<p>그 다음 <span style="background-color:#FFC0CB">콜백함수 내에 router 객체의 prefetch 라는 메서드 호출</span>
인수로는 어떤 페이지를 프리페칭할 건지 넣어주기</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/1f88589e-93c6-4f28-bc8c-22878843755f/image.png" width=500>

<p>이제 App 컴포넌트가 화면에 마운트 되고 나서,
곧바로 prefetch 함수가 실행되어서
테스트 페이지이에 대한 프리페칭이 실행될 것!</p>
<br>

<p>프로젝트를 재시작하면 반영됨</p>
<p>이렇게 프로그래매틱하게 이동하는 페이지도 프리페칭을 시키고 싶다면
이렇게 라우터 객체의 프리페치 메서드를 통해 특정 페이지를 명시적으로 프리페칭하도록 설정 가능</p>
<h2 id="자동으로-진행되는-링크-컴포넌트의-프리페칭-강제-해제">자동으로 진행되는 링크 컴포넌트의 프리페칭 강제 해제</h2>
<p>Link 컴포넌트는 자동으로 프리페칭이 적용
잘 접속할 것 같지 않으면 프리페칭하지 않게 설정</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/eae78632-8ba5-40b8-949a-f29f0aa9857f/image.png" width=500>

<p>그러면 index 페이지가 열려도 search 페이지에 필요한 자바스크립트 코드는
프리페칭되지 않음</p>
<p>(마우스 올리면 프리페칭된다!)</p>
<hr>
<h1 id="📌-api-routes">📌 API Routes</h1>
<blockquote>
<p>Next.js 앱에서 API를 구축할 수 있게 하는 기능</p>
</blockquote>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/6f4b5378-ba64-40f7-997b-9c83d71c5c7b/image.png">

<p>이 기능을 이용하면
마치 백엔드 API 서버가 하는 일을 동일하게 간단한 API를 구축해서
클라이언트, 즉 브라우저로부터 요청을 받아 데이터베이스에서 데이터를 꺼내온다든가
아니면 또 다른 서드파티에 데이터를 불러와서 전달을 한다든가,
하는 일련의 동작을 직접 만들어볼 수 있음</p>
<h2 id="실습-2">실습</h2>
<pre><code class="language-tsx">npm run dev</code></pre>
<p>개발모드로 실행</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/efa68ffa-d8cb-463a-a13e-0305ad71c232/image.png">

<p>이 코드는 브라우저 화면을 만드는 코드가 아니라,
서버에서 실행되는 API 엔드포인트를 만드는 코드</p>
<p>위와 같은 경로의 파일은,
<span style="background-color:#FFEBCD">웹 페이지가 아닌 API 라우트로서 API 응답을 정의하는 파일로 자동적으로 설정</span>이 되고
API의 경로는 이 폴더 구조에 맞춰 <code>/api/hello</code>란 경로를 가짐</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/c3c37587-e499-46cb-adf1-4a6a00ab191c/image.png" width=700>

<p>브라우저를 통해 이런 경로로 요청하게 되면,
해당 파일 안에 써져 있는 디폴트로 보내지는 함수가 실행이 되며,
API가 작동</p>
<p>두 개의 매개변수를 통해
request와 response에 대한 정보를 받고 있고</p>
<ul>
<li>request: 클라이언트의 요청 정보가 들어 있는 객체 타입</li>
<li>response: 서버가 응답할 때 사용하는 객체 타입</li>
</ul>
<pre><code>{
  res.status(200).json({ name: &quot;John Doe&quot; });
}</code></pre><br>

<p>이 API를 호출하면 서버가
아래와 같은 응답을 줌</p>
<pre><code>{
  &quot;name&quot;: &quot;John Doe&quot;
}</code></pre><p>또한 status 메서드를 통해서 상태 코드를 200번으로 설정</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/a1afa58c-d6b4-468e-b03a-cf04b824d41d/image.png">

<p>이렇게 응답받는 것도 확인</p>
<br>

<p>이렇게 <span style="background-color:#FFEBCD">Next.js에서는 pages라는 폴더 아래에 api라는 폴더를 만들고
해당 폴더 안에 새로운 파일을 배치시켜주면
그 파일들은 API 라우트로서 웹 페이지를 정의하는 파일이 아닌,
API 응답을 정의하는 모드로 설정</span>이 됨</p>
<h2 id="우리만의-api">우리만의 API</h2>
<blockquote>
<p>현재 시간을 반환하는 API를 만들어 보자</p>
</blockquote>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/be46e9bf-9989-41ed-9a74-1c0bd304408e/image.png" width=700>

<p>위처럼,
요청과 응답 타입을 import하고,
매개변수로 넣고
현재 시간을 보관하는 date 객체를 만들어 응답하도록 명시</p>
<p>time이라는 api가 호출되면, 이 핸들러 함수가 실행되고
핸들러 함수에서 새로운 date 객체를 만들고
response의 응답 값으로 json 객체를 응답하도록 솔정됨</p>
<p>이제 브라우저에서 요청을 보내면</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/a1a2f424-7cd1-46bc-8100-2167cad7473f/image.png">

<p>현재 시간이 locale 문자열로 변환되어 잘 들어옴</p>
<br>

<p>이 기능이 자주 사용되진 않음(..)</p>
<hr>
<h1 id="📌-스타일링">📌 스타일링</h1>
<blockquote>
<p>Next.js의 스타일링 설정 방법</p>
</blockquote>
<p>Next.js 앱의 스타일링은,
사실상 리액트의 컴포넌트 스타일을 설정하는 과정과 동일함</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/d4b9f4c0-7bdb-4160-8835-81e4e230aaa2/image.png">

<p>스타일링 할 거니까
여기에 css 파일을 넣어줌</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/0360f410-ff76-4652-b784-fdee0114b3f9/image.png">

<p>응, 안 돼~</p>
<br>

<p>뭐가 문제냐면 <span style="background-color:#FFEBCD">Global CSS 파일은 import할 수 없음</span>
➡️ <span style="background-color:#E2D6FF"><strong>제한하는 이유</strong>: 다른 페이지에 작성된 CSS 코드와 충돌날 수 있기 때문에</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/18ab7c83-51a2-4a86-b060-2ebf8639ebe8/image.png">

<p>이렇게 css 파일을 만들어 놓으면,</p>
<ul>
<li>test.css 파일에는 h1이라는 클래스의 color 스타일을 블루,</li>
<li>index.css 파일에는 h1이라는 클래스의 color 스타일을 레드로 정의</li>
</ul>
<p>➡️ <span style="background-color:#FDF5E6">index 페이지에 접속했다가(이미 index.css가 한 번 로딩됨)
test 페이지로 이동했을 때
이 index.css과 test.css 파일이 브라우저에 함께 로딩</span>됨</p>
<p>즉, 어떤 순간에는 브라우저가</p>
<pre><code>/* index.css */
.h1 { color: red; }

/* test.css */
.h1 { color: blue; }</code></pre><p>이런 식으로 둘 다 가지고 있는 상태가 되고,</p>
<p>브라우저는 &quot;같은 <code>.h1</code> 클래스에 색이 두 개네?&quot; 하고 보게 됨</p>
<br>

<p>이 클래스네임을 페이지별로
겹치지 않게 잘 분할해서 사용할 수 있다면 좋겠지만,</p>
<p>현실적으로 다뤄야 하는 프로젝트의 규모가 커지면 커질수록
스타일 파일의 개수가 늘어나고
그에 대한 클래스네임도 많아지니
문제가 발생할 가능성이 아주 좋아짐</p>
<p>이러한 문제를 애초 원천 차단하기 위해,
<span style="background-color:#E2D6FF">별도의 페이지 파일이나 어떠한 컴포넌트 파일에서 별도로 css파일을 그대로 import하는 걸 제한</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/acdd8242-5c25-43b4-a758-4721c48c3c36/image.png">

<p><span style="background-color:#FFC0CB">App 컴포넌트에서만 예외적으로 css 파일을 불러오는 걸 허용함</span>
다시 말해, App 컴포넌트가 아니면, css 파일을 그대로 불러오는 건 불가능</p>
<br>

<p>난 소소하게 index 페이지에 소소하고 작은 스타일 하나를 설정하고 싶은데
import 자체가 불가능하면?
➡️ <span style="background-color:#FFEBCD">Next.js에서 기본적으로 제공하는 css 모듈이란 기능을 활용하면됨</span></p>
<p><span style="background-color:#E2D6FF">기존의 css파일을 마치 모듈처럼 사용하도록 도와주는 기술</span>
<span style="background-color:#FFEBCD">: css 파일에 작성해둔 클래스 네임이 중복되지 않도록 클래스 네임을 자동으로 유니크한 이름으로 파일마다 변환해줌</span></p>
<br>

<p>index.css 파일의 이름을
➡️ index.module.css 파일로 바꾸자</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/8fd120c5-ecfc-4083-8075-3f6f9d9a4257/image.png">

<p>이름을 위와 바꾸고,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/d4483c11-1fe6-412f-bf8a-3626ccbce185/image.png" width=500>

<p>이렇게 style 되어 있던 코드를</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/545629cf-56a3-4bb3-9942-047a769360d7/image.png" width=500>

<p>인라인 스타일을 지우고 클래스 네임(className) 설정</p>
<p>그러면 css파일에 설정했던,
h1에 설정된 color: red라는 스타일이
태그에 자동으로 적용됨</p>
<p>그러면서도 className이 다른 파일과 겹치지 않게 유니크하게 설정됨</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/eb526902-e365-4676-a44b-2e27a40ac80f/image.png">

<br>

<p>유니크한 클래스 네임을 더 살펴 보면,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/58fc9dcb-ade2-46b6-8d56-b4c8e4d9d782/image.png" width=500>

<p><code>&lt;h2&gt;</code> 태그를 하나 추가하고,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/8e0c83df-534d-4ff0-8ad0-fb106a804796/image.png">

<p>css 파일에서 <code>&lt;h2&gt;</code>의 스타일을 설정하고,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/321aab2e-79f7-4e15-870b-8ba3531ea4dc/image.png" width=500>

<p>다시 컴포넌트에서 <code>&lt;h2&gt;</code>의 스타일을 적용</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/71215bb2-92f6-4ff1-beda-cc72a39bc0e8/image.png">

<p>브라우저에서 확인하면,
페이지별로 클래스 네임이 겹쳐서 발생할 수 있는 문제를 해결하기 위해
유니크한 클래스 네임으로 변환</p>
<hr>
<h1 id="📌-글로벌-레이아웃-설정하기">📌 글로벌 레이아웃 설정하기</h1>
<blockquote>
<p>모든 페이지에 다 적용되는 글로벌 레이아웃을 설정하는 방법 확인</p>
</blockquote>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/d70476ed-70ec-4ed4-b8a4-634c05145a8b/image.png" width=500>

<p>이 서비스의 글로벌 레이아웃을 우리의 앱에 설정해보자</p>
<ul>
<li>메인 컨테이너<ul>
<li>안 쪽에는 페이지의 내용</li>
<li>맨위 header</li>
<li>맨아래 footer</li>
</ul>
</li>
<li>컨테이너 뒷쪽엔 그레이 톤으로 백그라운드 컬러 설정</li>
<li>index 페이지 뿐만 아니라 search 페이지에도 header와 footer가 있음</li>
</ul>
<br>

<p>글로벌 레이아웃을 적용하려면 어떤 파일을 수정해야 할까?</p>
<p>➡️ <span style="background-color:#FFEBCD">Next.js 앱의 모든 페이지의 부모 컴포넌트 역할을 하는 루트 컴포넌트인 App 컴포넌트의 레이아웃을 적용</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/bec84b1a-a04f-4b91-a197-009258c21039/image.png" width=700>

<p>일단 기존 코드 다 지우고 header와 footer만 둔 상태</p>
<p>이렇게 <span style="background-color:#FFE4E1">App 컴포넌트에 header와 footer를 작성하면
모든 페이지가 header, main, footer 영역이 생김</span></p>
<p>이렇게 <span style="background-color:#FFEBCD">모든 페이지에 다 적용이 되어야 하는 글로벌 레이아웃을 적용할 때는
루트 컴포넌트인 App 컴포넌트에 이렇게 페이지 컴포넌트를 포함하는 구조</span>로 만들어야 함</p>
<br>

<p>그런데 <span style="background-color:#E2D6FF">App 컴포넌트 안에 레이아웃을 구성하는 코드가 너무 길어지면,
가독성이 떨어지기 때문에
보통은 글로벌 레이아웃을 위한 별도의 컴포넌트 파일로 만들어 코드 분리
</span></p>
<p>먼저, src 폴더 안에 components 폴더를 만듦</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/189a5b0c-43bb-4605-bc0f-0e5625d7ba1e/image.png">

<p><span style="background-color:#FFEBCD">페이지 역할을 하는 컴포넌트는 children을 명시해서,
이 컴포넌트를 children으로 리턴</span>하도록
(이때 props는 객체 타입이니까, 내부 타입은 ReactNode란 타입을 갖는다고 정의)</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/0d14b1a8-7f2b-4367-888b-8c22dd7eb69d/image.png" width=700>

<p>App 컴포넌트 내부의 코드를 제거하고,
<span style="background-color:#FFEBCD">방금 만든 글로벌 레이아웃 컴포넌트를 불러와서, 페이지 컴포넌트를 감싸서 children으로 넘겨줌</span></p>
<h2 id="퍼블리싱">퍼블리싱</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/22a40a32-a8b1-48bc-82e3-b9aa357f6433/image.png">

<ul>
<li>로고 텍스트 누르면 index 페이지로 이동</li>
</ul>
<br>

<p>헤더 안에 링크 연결</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/cfc0b327-c4d4-4b96-86e0-bea7abc1f1b7/image.png" width=500>

<br>

<p>푸터는 텍스트 명시</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/14b16862-94ec-4ef6-b1a6-95061cb57b91/image.png" width=500>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/492245bd-8097-43e8-9692-dc6b480ff827/image.png">

<h2 id="스타일링">스타일링</h2>
<ul>
<li>헤더는 링크 스타일 생략</li>
<li>푸터는 그레이톤 텍스트로 렌더링</li>
</ul>
<br>

<p>먼저 전체 백그라운드를 회색 톤으로 바꾸기 위해서 globals.css 수정</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/5e862a15-873a-4e31-8b98-c7ef694482d3/image.png">

<ul>
<li>여백 없게</li>
<li>백그라운드 컬러 변경</li>
</ul>
<br>

<h3 id="글로벌-레이아웃-컴포넌트를-위한-스타일링">글로벌 레이아웃 컴포넌트를 위한 스타일링</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/5be08e20-903c-4c41-b1d2-844fae039c41/image.png">

<p>위 파일을 만들고,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/a99e0ecf-bd7e-4a50-b297-53d6bed597bf/image.png">

<p>해당 파일을 레이아웃 컴포넌트에 import</p>
<p>이제 스타일링 후 레이아웃 컴포넌트에 적용해보자</p>
<br>

<h4 id="기본-컨테이너">기본 컨테이너</h4>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/ab2300a0-663a-4c37-9549-35c7a67504c4/image.png">

<ul>
<li>백그라운드 컬러: 하얀색</li>
<li>최대 넓이</li>
<li>최소 높이: 화면을 가득 채우도록</li>
<li>마진: 화면 가운데 배치</li>
</ul>
<p>css 파일에 스타일링 적용 후</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/07156e40-90b0-4472-81e1-212fcd7609ac/image.png">

<p>컴포넌트에 적용</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/71fa9194-2612-4c3a-bea6-916d862b4ff1/image.png">

<p>위처럼 메인 컨테이너와 바깥 영역에 적용된 것 확인</p>
<br>

<p>추가로,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/c49aeaec-6ebb-475e-8061-5fe3a0ba0585/image.png" width=500>

<p>컨테이너 경계선에 그림자를 주고
내부 여백도 좀 추가</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/0112b2bd-f640-4748-a83e-d3e522f2c081/image.png">


<h4 id="header-스타일링">header 스타일링</h4>
<p>스타일링을 추가로 하면,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/61a8998e-ce26-4701-9d20-a9296d967392/image.png" width=300>

<ul>
<li>높이</li>
<li>폰트 굵기</li>
<li>줄 높이 : 높이와 똑같이</li>
<li>폰트 사이즈</li>
</ul>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/f772476e-d038-48c0-b433-05225b9433a9/image.png">

<p>header 태그에 적용</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/07044a46-037d-4e0f-86e6-0dd952ed1b37/image.png">

<p>적용은 되었으나 링크 스타일이 기본적으로 적용되어 있음
(링크 태그로 만들어졌기 때문에)</p>
<br>

<p>css 파일에서 header 요소에
a 태그의 스타일링을 해줌</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/76e5fd1f-1fc9-4547-a104-1e5defbaf6bc/image.png" width=400>

<ul>
<li>컬러 고정</li>
<li>밑줄 제거</li>
</ul>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/ba76a6f5-c37c-4a1a-90fb-2282bf6446af/image.png">

<br>

<h4 id="main과-footer-스타일링">main과 footer 스타일링</h4>
<p>main과 footer의 스타일</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/c76d4990-d131-48f0-8f52-1ea72d4dc1ca/image.png" width=300>

<ul>
<li>main은 header와 멀어지지 않도록</li>
<li>footer는 상하로 여유를 줌</li>
</ul>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/9c5f411a-df0b-425e-8047-abac89797586/image.png">

<p>컴포넌트에 적용</p>
<h4 id="완성본">완성본</h4>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/0078be7f-d517-4fb9-808e-9e4ab687ef90/image.png">

<hr>
<h1 id="📌-페이지별-레이아웃-설정하기">📌 페이지별 레이아웃 설정하기</h1>
<blockquote>
<p>각각의 페이지에 필요한 특수한 레이아웃을 개별 페이지별로 적용하기</p>
</blockquote>
<h2 id="실습-3">실습</h2>
<pre><code>npm run dev</code></pre><img src="https://velog.velcdn.com/images/ann-algorithm/post/030d3ee1-a379-4157-ab23-79327124ed1a/image.png">

<p>완성된 페이지에 있는 서치바는,
index 페이지 말고 search 페이지에도 공통으로 존재</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/03a369ab-1f66-47b7-a399-13b9b7657cc9/image.png">

<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/e87c613e-fd1f-45bb-a776-4fd81f3952fd/image.png">

<p>그러나 도서 아이템을 클릭해서 접속하는 book 페이지에는
서치바가 존재하지 않음</p>
<br>

<h3 id="서치바가-있는-페이지">서치바가 있는 페이지</h3>
<p>서치바가 존재하는 페이지를 위한 별도의 레이아웃 컴포넌트를 만들자</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/55b2164c-3470-4f8c-8b76-9d873c150149/image.png">

<p>components 폴더 아래에 해당 파일을 만들고,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/fcf03b28-5470-4142-ad86-115c24c485e9/image.png">

<p>임시로 활용할 레이아웃 컴포넌트 작성</p>
<p><span style="background-color:#FFE4E1">children이란 props를 받고, 페이지 컴포넌트 전달</span>
<span style="background-color:#FFEBCD">여기에 서치바 역할을 할 요소도 만듦</span></p>
<p>이제 이 레이아웃을 index 페이지와 search 페이지에 각각 적용</p>
<h4 id="index-페이지에-적용">index 페이지에 적용</h4>
<p>어떻게 해야 할까?</p>
<p>우선
App 컴포넌트 안에 글로벌 레이아웃 밑에 Searchable 레이아웃을 넣자</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/b1b4c266-4f0b-4b7f-a5c4-31d3b0952e4d/image.png" width=500>


<p>이렇게 하면?
➡️ <span style="background-color:#E2D6FF">모든 페이지에 공통적으로 서치 바가 나타남</span>
즉, 적용하지 않으려 했던 book 페이지에도 서치 바가 나타남</p>
<p><span style="background-color:#FFC0CB">따라서 App 컴포넌트에 중첩하는 식으로는 설정 불가능</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/47ab4570-ddf8-4217-ba16-f3c5f8d3e7ef/image.png" width=500>

<p>일단 App 컴포넌트는 원복해주고</p>
<br>

<p><span style="background-color:#FFEBCD">해당 레이아웃(ex. 임시 서치바)이 적용되기 원하는,
예를 들면 index.tsx 같은 페이지에
이 페이지 역할을 하는 컴포넌트인 Home 컴포넌트에 레이아웃 추가</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/212da88f-5c16-464a-b797-266920425183/image.png">

<p>이 메서드가 페이지 컴포넌트를, page라는 이름의 매개변수로 받아서
방금 만든 레이아웃 SearchableLayout으로 감싼 형태로 return</p>
<p>매개변수 page는 리액트 컴포넌트이므로 ReactNode</p>
<p>이렇게 <span style="background-color:#FFEBCD">getLayout 함수를 추가하면,
이 getLayout 함수는 App 컴포넌트에서 사용되고,
index 페이지에 서치바를 적용</span></p>
<br>

<p><span style="background-color:#E0FFFF">index 페이지 컴포넌트인 Home은 함수인데 메서드를 추가하는가?</span>
<span style="background-color:#FFEBCD">자바스크립트의 모든 함수는 사실 다 객체</span>임
따라서 메서드가 추가 가능</p>
<blockquote>
<h4 id="함수와-객체-getlayout-관련-보충자료"><strong>함수와 객체 (getLayout 관련) 보충자료</strong></h4>
<p><a href="https://reactjs.winterlood.com/0f33b159-6b19-433b-8db4-68d6b4a122e0">https://reactjs.winterlood.com/0f33b159-6b19-433b-8db4-68d6b4a122e0</a></p>
</blockquote>
<br>

<p>이제 이 getLayout을 App 컴포넌트에 불러와야 함</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/624f522d-4a6e-4541-850f-79b4e2ec5a98/image.png">

<p>이때부터 어렵기 때문에 두 눈 똑바로 떠야 함👀</p>
<p><span style="background-color:#FFEBCD">페이지 라우터에서 각 페이지는 App 컴포넌트를 통해 렌더링된다고 했음</span>
<span style="background-color:#E2D6FF">App 컴포넌트의 Component는 현재 렌더링할 페이지이고, 
pageProps는 그 페이지에 전달되는 props</span></p>
<p>따라서, index 페이지의 경우에도
App 컴포넌트 props인 Component로 index 페이지 컴포넌트(Home 컴포넌트)가 전달됨
이때 getLayout 변수에 앞서 index 페이지 컴포넌트에 메서드로 추가한 getLayout이 저장됨</p>
<br>

<p>이 getLayout이 잘 저장되었는지 브라우저 콘솔에 출력해보면</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/27c50f6d-f9e3-4145-8e52-b63b6f0b6f6b/image.png">

<p>가 나오는데,
클릭해보면 어떤 함수인지 알 수 있음</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/2062ea73-d98f-48ae-85ba-5db4bcdeafce/image.png">

<p>우리가 만든 메서드로 잘 나오고 있음</p>
<p><span style="background-color:#FFEBCD">getLayout에 우리가 만든 서치바 레이아웃이 저장되니,
<code>_app.tsx</code>에서 이 메서드를 사용하면 index 페이지에 서치바 레이아웃이 적용됨</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/27a2161c-af81-4ea5-9bb9-36f320f6beaf/image.png" width=700>

<p><span style="background-color:#FDF5E6">getLayout이라는 메서드는,
인수로 페이지 역할을 하는 컴포넌트를 전달받아서,
그 결과값으로 SearchableLayout이 적용된 새로운 컴포넌트를 리턴</span>하도록 만들어져 있으니까,</p>
<p>위처럼 <span style="background-color:#FFEBCD">getLayout이라는 함수에 의해 현재 페이지 역할을 하는 컴포넌트가,
SearchableLayout으로 감싸진 형태로 렌더링될 것</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/7d7703ae-bb93-4e6c-a2eb-b8fef96fae4a/image.png">

<p>이제 서치바가 적용된 index 페이지가 렌더링됨</p>
<blockquote>
<p>정리하자면,
App 컴포넌트에서는 어떤 페이지로 접속 요청이 오든간에
일단 이 App 컴포넌트가 루트 컴포넌트로서 렌더링</p>
</blockquote>
<p>그렇기 때문에 이 App 컴포넌트는,
현재 접속 요청이 온 페이지 역할을 하는 컴포넌트(index 페이지)로 접속 요청이 왔다면,
index.tsx의 이 Home 컴포넌트를,</p>
<blockquote>
<p>Component라는 props로 전달받음</p>
<p>이 Component(페이지 역할을 하는 컴포넌트)함수와 동시에 객체이므로
메서드로 추가 가능
➡️ getLayout으로 추가하고, 이를 가져와
중괄호와 함께 호출 시 별도의 레이아웃 적용 가능</p>
</blockquote>
<h4 id="search-페이지에-적용">search 페이지에 적용</h4>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/833194eb-5ee4-4e5d-b220-143b099cb843/image.png" width=500>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/82886348-304f-4a4b-86ac-7dda815c1dc0/image.png">

<p>이와 같이 페이지 컴포넌트의 메서드를 추가하고,
App 컴포넌트에서 그 메서드를 읽으면 개별적으로 레이아웃이 적용 가능</p>
<h3 id="예외-처리">예외 처리</h3>
<p>근데, getLayout 메서드가 적용되지 않은 페이지로 접속하면 문제가 발생함</p>
<p>book 페이지로 접속해보자</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/76488c56-81cb-43a5-9965-81b8c191a32d/image.png">

<p>왜냐하면,
<span style="background-color:#E0FFFF">book 페이지에는 getLayout이라는 메서드를 추가한 적이 없기 때문에
</span></p>
<br>

<p>해당 페이지에는 별도로 메서드를 추가한 적이 없는데,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/88feba69-cf61-434e-b47d-caa96a6d17bc/image.png" width=500>

<br>

<p>App 컴포넌트에서는 getLayout을 저장해서 호출하고 있기 때문에,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/270f5824-1fdc-4793-95f4-157367b1ca40/image.png" width=700>

<p>에러가 발생하는 것</p>
<p><span style="background-color:#FFC0CB">getLayout에 undefined가 저장되어서, 이를 호출하려고 하니까</span> 오류가 발생하게 됨</p>
<p>이렇게 <span style="background-color:#FFEBCD">getLayout이란 메서드가 추가되지 않은 경우에는
별도의 레이아웃이 적용되지 않도록 예외 처리 필요</span></p>
<br>


<pre><code class="language-tsx">const getLayout = Component.getLayout ?? ((page: ReactNode) =&gt; page);
// 앞에 있는 Component.getLayout이 undefined일 때에는,
// 이 getLayout이라는 변수의 페이지를 매개변수로 받아서 그대로 리턴</code></pre>
<p>?? 연산자를 통해 undefined일 땐 getLayout에 page라는 매개변수를 받아 그냥 리턴
➡️ 현재의 페이지 그냥 그대로 return</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/31478285-a672-4c15-87f7-d3c6717e2c0d/image.png">

<p>예외처리까지 완료</p>
<h3 id="타입-오류">타입 오류</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/4e152013-d8c1-4293-bb1d-e8fc11b4c262/image.png">

<p>&quot;너님이 지금 컴포넌트에서 getLayout메서드를 꺼내서 쓰고 있는데,
나는 그 메서드를 모른다.&quot;란 뜻</p>
<p>당연함. 우리가 임의로 만들어둔 메서드임
➡️ 기본적인 타입 정보에는 포함이 되어 있지 않음</p>
<br>

<p>따라서,
<span style="background-color:#FFEBCD">이 컴포넌트의 타입에 getLayout이라는 메서드가 존재할 거라고, 
타입 정보를 추가해주면 됨</span>
➡️ 존재하는 요소라고 판단</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/75e753d5-1ac9-448f-8198-e58b47cfe388/image.png" width=500>

<p>타입 별칭으로 <code>type NextPageWithLayout</code> 별도의 타입 생성</p>
<p>원래 Next에서 제공하는 페이지 컴포넌트의 기본 타입인 NextPage를 설정해주고,
교집합으로 이 NextPage라는 기본 타입에다 getLayout 추가</p>
<p>그래서 <span style="background-color:#FFE4E1">getLayout 메서드의 타입 정보로 page라는 매개 변수를 받는데,
ReactNode 타입이고,
반환값로 똑같은 ReactNode 페이지 반환</span></p>
<p>이렇게 만든 <span style="background-color:#FFEBCD"><strong>NextPageWithLayout</strong></span>이란 타입은
<span style="background-color:#FFEBCD">기존의 넥스트에서 제공하는 페이지 컴포넌트 타입 NextPage
&amp;
getLayout이라는 타입의 메서드가 추가</span>된 타입으로 정의</p>
<p>따라서 Props에 이 타입을 정의</p>
<pre><code class="language-tsx">export default function App({
  Component,
  pageProps,
}: AppProps &amp; { Component: NextPageWithLayout }) {}</code></pre>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/3e6c6c6e-e423-4cd5-b691-564440f2ff32/image.png" width=700>

<p>빨간 줄을 해결했음!</p>
<br>

<h3 id="서치바-스타일링">서치바 스타일링</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/1c2e5746-0597-47a5-bfa2-12d00fc5850c/image.png" width=500>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/c952e747-2c56-4914-a2ab-645a5892337e/image.png">

<br>

<h3 id="검색어에-따른-페이지-이동">검색어에 따른 페이지 이동</h3>
<p>input태그에 사용자가 검색어를 입력하고,
검색 버튼 클릭 시 페이지 이동</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/067450d3-3ee1-4cd7-b290-f14f11a6ef73/image.png" width=700>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/8fd2c07d-f826-4fbb-8274-e96d74da3fdd/image.png">

<br>

<h3 id="스타일링-1">스타일링</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/53e4c5c0-84e7-4c6e-85e2-bd20e4960314/image.png">

<p>먼저 css 파일을 생성</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/e77b590b-fddf-4a83-a12d-86b7abf14c2f/image.png">

<img src="https://velog.velcdn.com/images/ann-algorithm/post/29f1583c-f56d-4c0c-afe5-9a6e16cc9596/image.png">


<p>tsx 파일에 import
각 태그에 className 명시</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/57b79c68-4413-4d5d-b194-c5d5bfad1f4a/image.png" width=500>

<p>css 파일 작성</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/07909d57-e398-41d5-984a-2d8b9e737793/image.png">

<p>완성!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[중급 프로젝트] 검색 컴포넌트 만들기]]></title>
            <link>https://velog.io/@ann-algorithm/%EC%A4%91%EA%B8%89-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B2%80%EC%83%89-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@ann-algorithm/%EC%A4%91%EA%B8%89-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B2%80%EC%83%89-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Mon, 16 Feb 2026 08:24:02 GMT</pubDate>
            <description><![CDATA[<h1 id="공통-컴포넌트-정리-searchdropdown-설계와-구현">공통 컴포넌트 정리: SearchDropdown 설계와 구현</h1>
<h2 id="1-왜-별도-searchdropdown이-필요했나">1) 왜 별도 SearchDropdown이 필요했나</h2>
<p>기존 <code>Dropdown</code> 공통 컴포넌트는 &quot;목록 열기/닫기 + 아이템 선택&quot;에는 적합했지만,
아래 요구사항을 한 번에 만족시키기엔 역할이 부족했다.</p>
<ul>
<li>목록 안에서 사용자 이름 검색</li>
<li>선택된 사용자 아바타/이름을 트리거 입력창에 표시</li>
<li>바깥 영역 클릭 또는 <code>Esc</code> 입력으로 닫기</li>
</ul>
<p>그래서 단순 드롭다운을 확장하는 대신, 검색과 선택 상태를 포함한 <code>SearchDropdown</code>을 별도 컴포넌트로 분리했다.</p>
<h2 id="2-구조-compound-component--context">2) 구조: Compound Component + Context</h2>
<p><code>SearchDropdown</code>은 compound component 패턴으로 구성했다.</p>
<ul>
<li><code>SearchDropdown</code>: 상태 소유 (<code>searchData</code>, <code>isOpen</code>, <code>selectedUser</code>)</li>
<li><code>SearchDropdown.Trigger</code>: 입력/트리거 렌더링</li>
<li><code>SearchDropdown.UserList</code>: 필터링된 목록 렌더링</li>
<li><code>SearchDropdown.UserItem</code>: 항목 선택 처리</li>
</ul>
<p>상위에서 Context로 상태/핸들러를 공유하고, 하위는 필요한 값만 꺼내 사용한다.</p>
<pre><code class="language-tsx">// src/components/searchDropdown/SearchDropdown.tsx
export const SearchDropdown = ({ children, className = BASE }: SearchDropdownProps) =&gt; {
  const [searchData, setSearchData] = useState(&#39;&#39;);
  const [isOpen, setIsOpen] = useState(false);
  const [selectedUser, setSelectedUser] = useState&lt;User | null&gt;(null);

  const handleSearch = (data: string) =&gt; setSearchData(data);

  const handleToggle = useCallback(() =&gt; {
    setSearchData(&#39;&#39;);
    setIsOpen((prev) =&gt; !prev);
  }, []);

  const handleClose = useCallback(() =&gt; {
    setIsOpen(false);
    setSearchData(&#39;&#39;);
  }, []);

  const dropdownRef = useDropdownClose(handleClose);

  return (
    &lt;SearchDropdownContext.Provider
      value={{
        searchData,
        handleSearch,
        isOpen,
        handleToggle,
        selectedUser,
        setSelectedUser,
      }}
    &gt;
      &lt;div ref={dropdownRef} className={className}&gt;
        {children}
      &lt;/div&gt;
    &lt;/SearchDropdownContext.Provider&gt;
  );
};

SearchDropdown.Trigger = SearchDropdownTrigger;
SearchDropdown.UserList = SearchDropdownUserList;
SearchDropdown.UserItem = SearchDropdownUserItem;</code></pre>
<p>핵심은 루트에서 상태를 모아두고, 사용 시점에는 아래처럼 선언적으로 조합하는 점이다.</p>
<pre><code class="language-tsx">&lt;SearchDropdown&gt;
  &lt;SearchDropdown.Trigger label=&quot;담당자 선택&quot; /&gt;
  &lt;SearchDropdown.UserList items={users}&gt;
    {(it) =&gt; (
      &lt;SearchDropdown.UserItem
        key={it.id}
        user={it}
        onSelect={() =&gt; setSelected(it)}
        isSelected={selected?.id === it.id}
      /&gt;
    )}
  &lt;/SearchDropdown.UserList&gt;
&lt;/SearchDropdown&gt;</code></pre>
<h2 id="3-동작-포인트">3) 동작 포인트</h2>
<h3 id="3-1-trigger-검색어-입력--선택-사용자-오버레이">3-1) Trigger: 검색어 입력 + 선택 사용자 오버레이</h3>
<p><code>selectedUser</code>가 있고 검색어가 비어 있으면, 입력창 위에 선택 사용자 정보를 오버레이로 표시한다.</p>
<pre><code class="language-tsx">// src/components/searchDropdown/SearchDropdownTrigger.tsx
const showSelectedPlaceholder = !!selectedUser &amp;&amp; searchData === &#39;&#39;;

{showSelectedPlaceholder &amp;&amp; (
  &lt;div className=&quot;absolute ... pointer-events-none&quot;&gt;
    &lt;Image src={selectedUser.profileImg} alt={selectedUser.content} width={22} height={22} /&gt;
    &lt;span&gt;{selectedUser.content}&lt;/span&gt;
  &lt;/div&gt;
)}

&lt;input
  type=&quot;text&quot;
  onClick={handleToggle}
  value={searchData}
  placeholder={!selectedUser ? label : &#39;&#39;}
  onChange={(e) =&gt; handleSearch(e.target.value)}
/&gt;</code></pre>
<h3 id="3-2-userlist-조건부-렌더링--필터링">3-2) UserList: 조건부 렌더링 + 필터링</h3>
<p>드롭다운이 열렸을 때만 목록을 렌더링하고, 검색어로 필터링한다.</p>
<pre><code class="language-tsx">// src/components/searchDropdown/SearchDropdownUserList.tsx
if (!isOpen) return null;

const filtered =
  searchData === &#39;&#39;
    ? items
    : items.filter((it) =&gt; it.content.toLowerCase().includes(searchData.toLowerCase()));

if (filtered.length === 0) return null;

return &lt;div className={className}&gt;{filtered.map(children)}&lt;/div&gt;;</code></pre>
<h3 id="3-3-useritem-선택-이벤트--닫기-처리">3-3) UserItem: 선택 이벤트 + 닫기 처리</h3>
<p>항목을 클릭하면 상위 선택 로직(<code>onSelect</code>) 실행 후, 컨텍스트 상태를 업데이트하고 드롭다운을 닫는다.</p>
<pre><code class="language-tsx">// src/components/searchDropdown/SearchDropdownUserItem.tsx
onClick={() =&gt; {
  onSelect();
  setSelectedUser(user);
  handleToggle();
}}</code></pre>
<h3 id="3-4-usedropdownclose-공통-닫기-훅-재사용">3-4) useDropdownClose: 공통 닫기 훅 재사용</h3>
<p><code>SearchDropdown</code>은 바깥 클릭/<code>Esc</code> 처리 로직을 커스텀 훅으로 분리해 재사용한다.</p>
<pre><code class="language-tsx">// src/hooks/useDropdownClose.tsx
document.addEventListener(&#39;mousedown&#39;, handleClickOutside);
document.addEventListener(&#39;keydown&#39;, handlePressEsc);</code></pre>
<h3 id="3-5-그외-고려한-것">3-5) 그외 고려한 것</h3>
<ul>
<li>선택한 게 없을 때의 렌더링</li>
<li>선택한 게 있을 때의 렌더링</li>
<li>위 각각의 경우에 드롭다운 리스트 렌더링</li>
<li>선택과 미선택 시의 렌더링 등</li>
</ul>
<h2 id="4-마무리">4) 마무리</h2>
<p>이번 <code>SearchDropdown</code>은 단일 UI를 넘어서,
&quot;검색 + 선택 + 닫힘 제어&quot;를 공통 패턴으로 묶어 재사용성을 높인 사례다.</p>
<p>핵심은 다음 두 가지였다.</p>
<ul>
<li>루트 컴포넌트에서 상태를 집중 관리하고, 하위 컴포넌트를 조합형으로 노출한다.</li>
<li>닫힘 처리 같은 횡단 관심사는 훅으로 분리해 일반 <code>Dropdown</code>과 동일하게 재사용한다.</li>
</ul>
<p>이렇게 분리해 두면 이후 멀티 선택, 비동기 검색, 키보드 네비게이션 같은 요구사항도 비교적 작은 변경으로 확장할 수 있다.</p>
<h2 id="5-예시">5) 예시</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/8e29cd6b-2fe7-4ab8-bb31-b5df7667f266/image.png">
]]></description>
        </item>
        <item>
            <title><![CDATA[Next.js를 소개합니다.]]></title>
            <link>https://velog.io/@ann-algorithm/Next.js%EB%A5%BC-%EC%86%8C%EA%B0%9C%ED%95%A9%EB%8B%88%EB%8B%A4</link>
            <guid>https://velog.io/@ann-algorithm/Next.js%EB%A5%BC-%EC%86%8C%EA%B0%9C%ED%95%A9%EB%8B%88%EB%8B%A4</guid>
            <pubDate>Wed, 11 Feb 2026 17:42:17 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-nextjs를-소개합니다">📌 Next.js를 소개합니다.</h1>
<h2 id="☑️-nextjs는-어떤-기술일까">☑️ Next.js는 어떤 기술일까?</h2>
<blockquote>
<p>리액트만을 위한 <strong>리액트 전용의 웹 개발 프레임워크</strong>
리액트보다 더 강력하고 편하게, 더 넓은 범위의 개발에 활용 가능
즉, <strong>리액트의  확장판</strong></p>
</blockquote>
<ul>
<li>버셀에서 개발하고 오픈 소스로 운영</li>
<li>인프런, 랠릿 등 다수의 메이저 사이트들이 활용</li>
</ul>
<p>➡️ 현대 웹 개발 기술의 중심</p>
<h2 id="☑️-인기의-비결은">☑️ 인기의 비결은?</h2>
<p><span style="background-color:#FFC0CB">Next.js는 라이브러리가 아닌 <strong>프레임워크</strong>임</span></p>
<p>리액트란 기술은, 자기 자신을 UI 개발을 위한 자바스크립트 라이브러리라고 소개했었음</p>
<h3 id="라이브러리와-프레임워크">라이브러리와 프레임워크</h3>
<p>이 차이가 뭘까?
대략 비슷하지만 엄밀히는 구분됨</p>
<p><span style="background-color:#FFEBCD">기능 구현의 주도권이 누구에게 있는가?</span>로 구분</p>
<h4 id="주도권이-개발자에게-있다-라이브러리">주도권이 개발자에게 있다? 라이브러리</h4>
<ul>
<li>기능 구현을 원하는 방향으로 진행</li>
<li>쓰고 싶은 도구, 쓰고 싶은 기술을 씀</li>
</ul>
<p>➡️ 어떤 기능을 구현하는 데에 있어서 제약 없이 개발자들이 자유롭게 개발 가능
ex) 페이지 라우팅 기능을 구현해야 할 때, 리액트 라우터를 떠올리겠지만
꼭 그럴 필요 없이 Tanstack 라우터 같은 기술을 사용해도 문제 없음</p>
<p>즉, <span style="background-color:#FDF5E6">자유도가 높다</span></p>
<h4 id="주도권이-개발자에게-없다-프레임워크">주도권이 개발자에게 없다? 프레임워크</h4>
<ul>
<li>프레임워크가 자체적으로 제공하는 기능을 이용하거나,
허용하는 범위 내에서만 추가 도구 사용 가능</li>
</ul>
<p>➡️ 페이지 라우팅 기능을 구현해야 할 때,
내가 원하는 라우터를 찾아서 설치하는 게 아니라
프레임워크인 Next.js가 제공하는 페이지 라우터나 앱 라우터 같은 기능만 이용해야 함
(원하는 라우터를 선택해서 할 수 없음)</p>
<p>즉,<span style="background-color:#FDF5E6">자유도가 낮다</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/79185f6f-9130-4b53-b513-d7e8c22a7270/image.png">

<br>

<h4 id="자유도가-높은-게-좋은-것인가">자유도가 높은 게 좋은 것인가?</h4>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/239cbb48-cba6-423d-b8c4-bcb1516efbe0/image.png" width=500>

<p>자유도가 너무 높아도 문제
➡️ <span style="background-color:#FFC0CB">그만큼, 기본 기능이 적음</span></p>
<p><span style="background-color:#FFEBCD">리액트와 같은 라이브러리는
렌더링 등 주요 기능을 제외한 모든 기능은 다 만들어서 써야 함</span>
혹은 <span style="background-color:#FFEBCD">라이브러리를 직접 찾아서 써야 함</span></p>
<br>

<p>넥스트는 웹 개발에 있어서 모든 필수적인 기능은 거의 다 기본적으로 제공
ex) 페이지 라우팅, 최적화, 서버 사이드 렌더링 등
➡️ 따라서 가져다 이용만 하면 됨</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/2248df29-0ee2-4370-b2b2-9dfc1f1f25af/image.png">

<p>기본적으로 제공되는 기능을 이용해서
상당히 많은 부분을 간단히 해결하기 때문에
훨씬 편하고 빠르게 웹 서비스 구축 가능</p>
<p>따라서,
넥스트는 기존의 리엑트에 추가적인 기능을 덧붙여둔 리액트의 확장판이기 때문에
이미 리액트를 알고 있는 사람들은 몇 가지만 배우면 쉽고 재미있게 배울 수 있음</p>
<hr>
<h1 id="📌-nextjs의-사전-렌더링-이해하기">📌 Next.js의 사전 렌더링 이해하기</h1>
<p>넥스트의 기본적인 동작 방식은,
사전 렌더링이라는 기능 안에서 대부분 설명이 됨</p>
<h2 id="☑️-사전렌더링">☑️ 사전렌더링?</h2>
<blockquote>
<p>브라우저의 요청에 사전에 렌더링이 완료된 HTML을 응답하는 렌더링 방식
기존 리액트의 Client Side Rendering의 단점을 효율적으로 해결하는 기술</p>
</blockquote>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/9fb3c1b1-7dd8-4b95-ae98-62d889f73029/image.png">

<p>사용자가 브라우저를 통해 서버로 <strong>초기 접속 요청</strong>
서버는 <span style="background-color:#FFEBCD">자바스크립트로 작성해둔 모든 리액트 컴포넌트를 다 실행</span>해서
이 컴포넌트를 <span style="background-color:#FFEBCD">HTML 페이지로 미리 다 변환 후 렌더링</span>시킨 후
<strong><span style="background-color:#FFEBCD">서버 측에서 렌더링된 HTML 파일을 브라우저에게 응답해주는 렌더링 프로세스</span></strong>
➡️ <strong>사전 렌더링</strong></p>
<p>_<span style="background-color:#E0FFFF">이게 어떻게, 기존 리액트의 클라이언트 사이드 방식의 문제를 해결하는 걸까?</span>_</p>
<h2 id="☑️-리액트의-렌더링은-어떻게-전달될까">☑️ 리액트의 렌더링은 어떻게 전달될까?</h2>
<h3 id="클라이언트-사이드-렌더링이란">클라이언트 사이드 렌더링이란?</h3>
<blockquote>
<p>✅ <strong>Client Side Rendering(CRS)</strong>
React.js 앱의 기본적인 렌더링 방식
클라이언트(브라우저)에서 직접 화면을 렌더링하는 방식</p>
</blockquote>
<p>클라이언트인 웹 브라우저가, 
우리가 작성한 모든 자바스크립트 코드, 즉 우리가 작성한 리액트 앱을 실행시켜서
렌더링을 직접 브라우저 차원에서 처리하는 방식</p>
<h3 id="클라이언트-사이드-렌더링-매커니즘">클라이언트 사이드 렌더링 매커니즘</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/e654fa78-800a-4dda-af9f-049084eca425/image.png">

<p>유저, 브라우저, 서버가 있다고 할 때,
사용자가 브라우저를 통해 서버로 초기 접속 요청을 보내면,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/f6b767b9-3c9d-45a2-aacb-198ae34204e0/image.png" width=500>

<p>서버는 일단 빈 껍데기인 index.html을 브라우저로 보냄</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/90d24486-e9b5-4447-bd86-ef6f21f9c79d/image.png">


<p>그럼 브라우저는 서버로부터 받은 이 html 파일을 일단 화면에 렌더링</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/328ba18d-4dd7-4eee-be76-379ef3db272d/image.png" width=300>

<p><span style="background-color:#FDF5E6">그러나 사용자의 화면에는 아무것도 안 나옴</span>
<span style="background-color:#E2D6FF">지금 브라우저가 서버로부터 받은 HTML파일은 빈 껍데기이기 때문</span></p>
<br>

<p>위와 같이 끝나지는 않고,</p>
<p>서버는 이어서 브라우저에게
그동안 작성한 모든 자바스크립트 코드
만들어둔 리액트 앱을 하나의 자바스크립트 파일로 묶어서(Bundling)
후속으로 브라우저로 보내줌</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/4b4ba16a-d9de-467c-b312-44878c2348ce/image.png">


<br>

<p>브라우저는 번들링된 자바스크립트 파일, 즉 리액트 앱을 직접 실행</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/d2669611-447e-48b8-a60e-f524a0c92684/image.png">

<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/800ada79-5729-4a0b-9382-672ea7a222ca/image.png">

<p>리액트 앱이다 보니 리액트 컴포넌트들이 이때서야 실제로 화면에 나타남</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/a20ec080-01f2-4cd1-b414-0301885af4bb/image.png">

<p>이제야 유저는 자신이 요청한 웹 페이지를 볼 수 있게 됨</p>
<br>

<p><span style="background-color:#FFC0CB">리액트가 이런 클라이언트 사이드 렌더링으로 동작하는 이유는,
초기 접속 이후에 일어나는 페이지 이동을 매우 빠르고 쾌적하게 처리한다는 장점이 있음
</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/1b9d1519-d87e-4a16-8cd3-8caa7e5b7ae0/image.png">

<p><span style="background-color:#FFEBCD">이 자바스크립트 번들에는,
이 서비스에서 접근 가능한 모든 컴포넌트 코드가 존재</span>
➡️ 웹 사이트에 필요한 전체 코드</p>
<br>

<p><span style="background-color:#FFE4E1">따라서 초기 접속 과정 이후,
링크나 버튼을 클릭해서 페이지를 이동해도
서버에 새로운 페이지를 요청할 필요가 없음</span></p>
<p>왜냐하면,
브라우저는 리액트 앱을 가지고 있고,
리액트 앱에는 모든 페이지에 필요한 컴포넌트가 다 포함이 되어 있기 때문</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/577b8403-2b12-4a60-8aca-ff177304a0dd/image.png">

<p>페이지 이동 요청이 있어도,
브라우저가 자체적으로 아까 서버로부터 받은 리액트 앱을 실행해서
현재 이동해야 하는 페이지에 맞도록
적절히 컴포넌트를 갈아끼우면 되기 때문에
굉장히 빠른 속도로 페이지 이동이 가능</p>
<h3 id="클라이언트-사이드-렌더링의-단점은">클라이언트 사이드 렌더링의 단점은?</h3>
<p><span style="background-color:#FFC0CB"><strong>초기 접속 속도가 느리다!</strong></span> &lt;- 아주 치명적</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/5134ae72-836e-4eea-952d-1e0d8bb4bda8/image.png">

<p>왼쪽 위 초기 접속 요청이 발생한 시점부터,
왼쪽 아래 화면이 렌더링되기까지 오랜 시간이 소요됨</p>
<p>즉, 요청 시작 시점부터 실제 콘텐츠가 렌더링 되기까지 꽤 오랜 시간이 소요됨</p>
<p>브라우저가 화면/콘텐츠를 렌더링하기까지는
빈 껍데기 HTML도 받아와야 하고,
자바스크립트 번들 파일 즉, 리액트 앱도 받아야 하고
최종적으로 이 JS를 실행도 해야 하고,
➡️ 브라우저가 하는 일이 너무 많아서 이 과정이 너무 오래 걸림!</p>
<h3 id="fcpfirst-content-for-paint">FCP(First Content for Paint)</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/ceff96b5-2338-44d9-b670-d61fbe99f7d9/image.png">

<p>요청이 시작된 시점으로부터, 콘텐츠가 화면에 처음 나타는 데 걸리는 시간
: 요청 시작 ↔ 콘텐츠 렌더링</p>
<p><span style="background-color:#FDF5E6">이 FCP는 웹 페이지의 성능을 대표할 정도로 굉장히 중요한 지표</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/d88fc321-96f1-40ba-a083-adc9bb330055/image.png">

<p>이 FCP가 조금이라도 늦어지는 경우
위 지표처럼 사용자의 이탈률이 굉장히 가파른 폭으로 크게 증가</p>
<br>

<p><img src="https://velog.velcdn.com/images/ann-algorithm/post/0d8bb158-d001-45a1-8026-7dee946579ca/image.png" alt=""></p>
<p>하지만 리액트는 이렇게 클라이언트 사이드 렌더링이라는 방식으로 동작하기 때문에,</p>
<p>즉, 초기 접속 요청 시에 브라우저가 HTML 렌더링과 리액트 앱까지 실행해야 하기 때문에
결과적으로 컨텐츠가 화면에 렌더링된 시점인 FCP가 꽤 늦어지는 치명적인 단점이 있음</p>
<blockquote>
<ul>
<li>장점: 초기 접속 이후의 페이지 이동이 빠름</li>
</ul>
</blockquote>
<ul>
<li>단점: FCP(초기 접속 속도)까 느림</li>
</ul>
<h2 id="☑️-그래서-등장한-사전-렌더링">☑️ 그래서 등장한 사전 렌더링</h2>
<p><span style="background-color:#FFC0CB">리액트의 단점인 CSR의 단점을 해결함</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/ce0b7758-5b1b-4ac1-a235-710383769b1d/image.png">

<p>유저가 브라우저를 통해 서버에 초기 접속 요청을 보냄</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/5db0e35e-dac5-45b4-8581-7c96096c9540/image.png">

<p><span style="background-color:#FFEBCD">서버가 서버 측에서 자바스크립트 코드를
리액트 앱으로 직접 실행 시켜서,
우리가 만든 모든 리액트 컴포넌트를 HTML로 변환</span>
<span style="background-color:#FFC0CB">➡️ 즉, 사전에 렌더링 수행</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/c8a519f6-d6a4-4588-b7ed-b786d79f959a/image.png">

<p>이미 사전에 렌더링된 HTML 파일이 전달됨</p>
<br>

<p>앞서 CSR에는 HTML이 빈 껍데기 파일이었지만,
지금은 사전에 렌더링된 HTML 파일을 보내준다는 차이점이 생김</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/ff8c7278-b39c-4c23-8e24-e732c5014d3c/image.png">

<img src="https://velog.velcdn.com/images/ann-algorithm/post/f51c5bcc-bcbc-44a0-a62f-4c3d779e4b83/image.png">

<p>그래서 사용자는 바로 화면에 렌더링된 화면을 볼 수 있음!
➡️ FCP가 굉장히 크게 단축</p>
<br>

<blockquote>
<p>📝 <strong>렌더링이란?</strong>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/663e7dec-361c-4d72-a9f1-631d6cb27196/image.png"></p>
<p>서버 측에서 자바스크립트 코드, 즉 리액트 앱을 실행해 HTML로 변환하는 과정도 렌더링이라고 했고,
브라우저가 컨텐츠를 화면에 그리는 과정도 렌더링이라고 했는데
... 뭐지?</p>
<p>렌더링의 뜻은 두 개!</p>
</blockquote>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/edd21da0-29d9-460b-8373-b3b3bd0ccdb1/image.png">
>
>**JS실행(렌더링)**
> **자바스크립트 코드(리액트 컴포넌트)를 HTML로 변환하는 과정**
-자바스크립트를 렌더링한다.
-JS를 렌더링한다.
-컴포넌트를 렌더링한다.
>
>**화면에 렌더링**
브라우저가 서버로부터 받은 HTML코드를 **화면에 그려내는 작업**
즉, 실제 콘텐츠를 그려내는 작업
-화면에 렌더링한다.

<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/fe13aad7-083f-44ec-b2b4-fafeeaa0a3bd/image.png">

<p>이제 콘텐츠가 화면에 렌더링되어
콘텐츠가 채워진 화면만 나타나는데,</p>
<p><span style="background-color:#FFE4E1">이게 다 끝난 게 아님!</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/5192b213-fcf0-44ff-b296-56931264fe2c/image.png">

<p><span style="background-color:#FDF5E6">브라우저가 지금은 서버로부터 HTML 파일 하나만 받은 상태이기 때문에,
클릭이나 페이지 이동 같은 인터렉션, 즉 상호작용은 아직 동작하지 않음</span>
<span style="background-color:#E2D6FF">웹 페이지의 상호작용은 자바스크립트가 처리하는 영역이기 때문에!</span></p>
<p>따라서 아직 이벤트를 처리할 자바스크립트 코드가 없어서 이 시점에는 상호작용이 불가능
➡️ 아직 완성된 건 아님</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/541fd19f-8a8d-4b89-b310-8120c2536a91/image.png">

<p>따라서 넥스트 서버가 <span style="background-color:#FDF5E6">곧바로 브라우저에게 
후속으로 우리가 작성한 자바스크립트 코드,
즉 리액트 앱을 번들링해서 브라우저에게 보내주게 됨</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/ed19a6e6-997b-4f67-b018-088521113886/image.png">

<p>브라우저는 서버로부터 받은 자바스크립트 코드,
리액트 앱을 직접 실행해서
현재 화면에 렌더링되어 있는 HTML 요소와 연결</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/652a27ac-901c-4ca2-8d14-c4d7b23fe334/image.png">

<p>기존의 HTML로만 렌더링해뒀기 때문에
아무런 상호작용도 발생할 수 없단 웹 페이지에 자바스크립트 코드들이 쏟아짐</p>
<p>➡️ 아주 생기 있는 완성된 페이지가 완성!</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/9f01a42c-91b3-4518-a852-d6acb2e6e8e5/image.png">

<p>그래서 이렇게 HTML과 자바스크립트를 연결하는 과정을,
넥스트에서는 마치 메말라 있던 HTML 요소들에
자바스크립트라는 물을 뿌려주는 것과 비슷한 느낌
➡️ &quot;수화&quot; 또는 &quot;하이드레이션&quot;이라고 함</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/61fa5c83-f1da-4eba-a50a-3ff7e73344f7/image.png">

<p>이 상호작용까지 가능해진 시점을
<strong>Time To Interactive ➡️ TTI</strong>라고 함</p>
<p>ex) TTI 3초: 요청부터 하이드레이션이 종료되는 시간이 3초</p>
<p><span style="background-color:#E0FFFF">이 이후에 발생하는 페이지 이동 요청은?
➡️ 클라이언트 사이드 렌더링 방식으로 처리</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/6367b3c6-aff7-4e58-9386-7968985614ea/image.png">

<p>이 과정을 간소화하고 이 다음 요청을 살펴보면,</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/35fe27fb-c628-46e8-b2f2-b590c30bb21d/image.png">

<p>이렇게 추가 요청이 있을 때는?</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/bd25d1d2-9456-40f0-a5b2-eecb2e64717f/image.png">

<p>이때부터는 CSR과 똑같은 방식으로 페이지 이동이 처리됨</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/4f13b3d7-d993-4d1e-b5ca-2062441b44d1/image.png">

<p>브라우저가 서버에게 별도의 페이지를 추가로 요청하지 않고,
<span style="background-color:#FFEBCD"> 자바스크립트 코드를 실행해서,
즉 리액트 앱을 실행해서
컴포넌트를 교체하는 방식으로</span>
페이지 이동이 효율적으로 진행됨</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/671d0550-d9ee-429c-bddd-1a8cbf0bf712/image.png">

<p>이렇게 되는 이유는,
초기 접속 요청 과정에서 서버가 브라우저에게
하이드레이션을 위해 자바스크립트 번들을 전달했기 때문</p>
<p>➡️ 전달된 자바스크립트 번들 파일은 그냥 리액트 컴포넌트들이 들어있는 리액트 앱임</p>
<p>그래서 페이지 이동이 발생했을 때 원래 리액트 앱이 그랬던 것처럼
브라우저가 직접 자바스크립트 코드를 실행해서,
컴포넌트만 딱 교체하는 방식으로 빠르고 쾌적하게 페이지 이동 가능</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/4f710750-96d4-4a56-aa79-65a634a64739/image.png">

<p>그래서 그러므로,
<span style="background-color:#FDF5E6">넥스트는 초기 접속 요청 과정에서는
이렇게 서버 측에서 자바스크립트 코드를 HTML로 미리 렌더링하는 사전 렌더링이라는 방식을 통해
기존 리액트의 단점이었던 FCP를 빠르게 개선할 수 있으면서도</span>
<span style="background-color:#FFEBCD">동시에 그 이후 발생하는 페이지 이동은, 기존의 CSR 방식을 그대로 처리해서
리액트 앱의 장점인 빠르고 쾌적한 페이지 이동은 그대로 계승해서 장점을 가져옴</span></p>
<h1 id="📌-실습용-백엔드-서버-세팅하기">📌 실습용 백엔드 서버 세팅하기</h1>
<p>이건 그냥...
먼저 백엔드 서버를 만들고,
supabase에 내 서버를 만들어 백엔드 서버와 해당 디비 서버 연결</p>
<ul>
<li>supabase에 데이터베이스 서버를 만듦</li>
<li>강사님 깃헙에 있는 백엔드 서버를 다운 받음</li>
<li>환경변수 파일에 데이터베이스 서버와 연결할 URL과 패스워드 기입</li>
<li><code>npm i</code>로 패키지 다운로드</li>
<li><code>npx prisma db push</code> 데이터베이스 초기화 ➡️ 테이블 생김</li>
<li><code>npm run seed</code> 초기 데이터 채우기</li>
<li><code>npm run start</code> 백엔드 서버 실행</li>
</ul>
<p>잘 실행되고 연결되면, 스웨거를 볼 수 있음</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/3e6cb12e-2faa-49df-94e0-8e4bf6200d59/image.png">

<img src="https://velog.velcdn.com/images/ann-algorithm/post/3ab18c98-5765-40db-abdd-cec15071e4a2/image.png">

<br>

<p>그외 꿀팁,
아래와 같이 명령어를 입력하면,
Prisma가 제공하는 웹 기반 DB GUI를 실행할 수 있음</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/6942e228-8cc1-41cc-8602-182801dae162/image.png">

<p>그럼 브라우저를 통해 테이블을 볼 수 있다</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/67abdd20-ff10-4442-8554-2467ee2d54e7/image.png">

<h1 id="📌-본격적인-학습에-앞서">📌 본격적인 학습에 앞서</h1>
<p>넥스트에서 제공하는 라우터는</p>
<ul>
<li>페이지 라우터</li>
<li>앱 라우터</li>
</ul>
<p>두 가지가 있음</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/dc46f880-e22c-4fe6-bc3b-fc0318841433/image.png">

<p>페이지 라우터는,
구 버전의 라우터이고</p>
<p>앱 라우터는,
신규 라우터이며,
리액트 서버 컴포넌트나, 다양한 신규 기능이 제공됨</p>
<br>

<p>앱 라우터는 페이지 라우터의 단점을 개선했으므로,
페이지 라우터를 알아야 앱 라우터도 알 수 있음!</p>
<p>따라서 둘다 배울 것!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Context]]></title>
            <link>https://velog.io/@ann-algorithm/Context</link>
            <guid>https://velog.io/@ann-algorithm/Context</guid>
            <pubDate>Sat, 31 Jan 2026 07:09:00 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-context">📌 Context</h1>
<p><strong>React Context</strong>는 <span style="background-color:#FFEBCD">컴포넌트 간의 데이터를 전달하는 또 다른 방법</span>이고,
기존의 Props가 가지고 있던 단점을 해결할 수 있음</p>
<h2 id="☑️-props의-단점">☑️ Props의 단점</h2>
<p><span style="background-color:#FFEBCD">Props는 부모 ➡️ 자식으로만 데이터를 전달할 수 있음</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/f3549b7d-99c6-4724-a396-cd0f5b193b7c/image.png">

<ul>
<li>한 단계 아래로 전달하는 건 문제 되지 않음</li>
</ul>
<br>


<img src="https://velog.velcdn.com/images/ann-algorithm/post/56939584-9162-411d-bd34-e66c7ff2a0e6/image.png">

<p>하지만,
계층 구조가 이렇게 두 단계 이상 깊어지면,
<span style="background-color:#FFE4E1">App 컴포넌트에서 B 컴포넌트에게 데이터를 전달할 때
props를 이용하게 되면 다이렉트로 전달 불가능</span></p>
<p>왜냐하면, props는 부모에서 자식으로만 데이터를 전달할 수 있기 때문에,
자식 A라는 컴포넌트가 중간 다리 역할을 해야  함
➡️ 먼저 자식 A에게 데이터를 전달하고, 그 다음 자식 A가 자식 B에게 데이터를 연달아 전달하는 방식으로 props를 이용</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/c28571ca-ad3f-42cd-91a0-ec1da4bfd387/image.png">

<p>To-Do 아이템을 만들 때,
계층 구조상 아이템 수정이나 삭제 기능은
<span style="background-color:#FDF5E6">App 컴포넌트가 가지고 있는 onUpdate나 onDelete 같은 함수는 
건너건너 TodoItem 컴포넌트에게 전달했어야 했음
</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/3656d90e-ab28-4b59-b011-e31e0bbf1cce/image.png">

<p>현재 App 컴포넌트와 TodoItem 컴포넌트는 부모-자식 관계가 아니기 때문에
중간 다리로 List 컴포넌트를 거쳐서
데이터를 이중으로 전달해야 했음</p>
<br>

<p>위와 같이,
props를 이용하면 데이터를 중간 다리를 거쳐 전달하는 경우가 많은데
좋은 방법은 당연히 아님</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/fdbb4c67-3901-45f7-8a3e-cee00f21aa77/image.png">

<p><span style="background-color:#FFC0CB">서비스가 커지고,
더 많은 컴포넌트가 그 사이에 존재하면
그때그때 props를 추가해야 하고,
추후, props명이 변경될 때면 일일히 이름을 변경해야 함</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/b14cad07-d4ef-4ab2-bbe3-06d5acb5b550/image.png">

<p>리액트에서는 이 상황을
<span style="background-color:#FDF5E6">props가 드릴처럼 땅을 파고 내려가는 것 같다고 해서
<strong>Props Drilling</strong>이라고 함</span></p>
<br>

<p>이러한 문제들을 해결하기 위해
React Context를 사용해보고자 함</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/61bc8f0f-4945-46ca-8d9c-44d2a3dc43a2/image.png">

<p>Context는,
<span style="background-color:#FFEBCD"><strong>데이터를 보관하는 일종의 데이터 보관소 역할을 하는 객체</strong></span></p>
<blockquote>
<p>컨텍스트를 새롭게 생성 후,
App 컴포넌트에서 원래 자식 컴포넌트에게 전달되던 함수를 컨텍스트에 보관하면,
이제부터 props를 이용하지 않고
직통으로 컨텍스트를 통해 필요한 데이터 공급 가능</p>
</blockquote>
<p>그리고 이러한 <span style="background-color:#FDF5E6">이러한 컨텍스트는 여러 개 만들 수 있음</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/c9be58e5-254e-4b5a-bb36-ed315aca2f6e/image.png">

<p>왼쪽의 L 자식 컴포넌트는 A 컨텍스트의 데이터만 공급받을 수 있고,
오른쪽의 R 자식 컴포넌트는 B 컨텍스트의 데이터만 공급받을 수 있도록 설정 가능</p>
<hr>
<h1 id="📌-context-사용하기">📌 Context 사용하기</h1>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/05aa1a5c-47a7-4254-b584-e48ae5269fe6/image.png">

<p>App 컴포넌트에서 선언한,
onUpdate와, onDelete는 TodoList 컴포넌트의 props로 전달되어
TodoItem 컴포넌트까지 props로 전달되어야 함
➡️ <strong>Props Drilling</strong></p>
<br>

<h2 id="☑️-context-생성">☑️ Context 생성</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/0d0c5967-2eff-453d-b7c6-bd53ab8559e5/image.png">

<p>새로운 컨텍스트 객체 생성</p>
<blockquote>
<p>보통은 컴포넌트 외부에 컨텍스트를 선언
<span style="background-color:#FDF5E6">만약, 안에 선언하면 App 컴포넌트가 리렌더링되면 컨텍스트도 새로 생성하게 됨</span></p>
</blockquote>
<p>근데 이 컨텍스트는 데이터를 하위에 있는 컴포넌트에 공급할 뿐이고,
<span style="background-color:#FDF5E6">앱 컴포넌트가 리렌더링될 때마다 다시 생성될 필요가 없음</span></p>
<p><span style="background-color:#FFE4E1">따라서 특수한 경우가 아니면,
컨텍스트의 생성은 컴포넌트 외부에 선언함</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/377d39ee-29ac-478c-bc1c-666db4cfdd84/image.png">

<img src="https://velog.velcdn.com/images/ann-algorithm/post/e6f19e7b-0b88-4652-898f-4bee7010e1d3/image.png">

<p>콘솔에 출력해보면 위와 같은 결과가 나옴
➡️ 여러 개의 프로퍼티를 가지는 <strong>객체</strong></p>
<p>다양한 정보가 컨텍스트에 동작하고,
대부분은 내부 동작용</p>
<p>우리는 </p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/13f6541d-bada-4026-8a22-536b9ba4f4d0/image.png">

<p>위, Provider 공급자, 제공자만 보면 됨
➡️ <span style="background-color:#FFEBCD">컨텍스트가 공급할 데이터를 설정하거나, 컨텍스트의 데이터를 공급받을 컴포넌트를 설정하기 위해 사용</span></p>
<p>사실은 컨텍스트도 컴포넌트임</p>
<br>

<h2 id="☑️-데이터-공급">☑️ 데이터 공급</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/cc62e7a4-6299-4d44-b169-a41f5dd30d56/image.png">

<p>위와 같이 리턴문에 사용
<span style="background-color:#FFC0CB">이제 이 사이에 있는 컴포넌트는,
<strong>TodoContext</strong>의 데이터를 공급받을 수 있음</span></p>
<p>공급할 데이터는
<span style="background-color:#FFEBCD">컴포넌트에 value라는 prop로 전달</span></p>
<br>

<p>onCreate, todos, onUpdate, onDelete 다 컨텍스트로 공급할 거니까</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/d6a653e0-029c-4fbf-9178-a998184a32cc/image.png">

<p>이렇게 value를 통해서 전달</p>
<br>

<p>이렇게 Provider 컴포넌트의 자식, 또는 자손 컴포넌트는 value props를 통해서
우리가 설정한 컨텍스트가 공급하는 값들을 다이렉트로 꺼내와서 언제든지 사용할 수 있는 상태가 되도록</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/da98ca1d-d02e-4fc8-a5f5-a8982fde8029/image.png">

<ul>
<li><span style="background-color:#FFC0CB">TodoContext란 컨텍스트 생성</span></li>
<li><span style="background-color:#FFC0CB">해당 컨텍스트의 Provider를 Editor 컴포넌트와 List 컴포넌트의 부모 컴포넌트로 둠</span></li>
</ul>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/857f6d37-e4c5-4158-8563-b05e703aafac/image.png">

<p>그리고 props로 <span style="background-color:#FFC0CB">함수와 state를 객체로 묶어 전달</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/67b7fdb1-7e98-4f03-9009-17c62595e7f1/image.png">

<p>이제 Provider 아래 있는 컴포넌트는,
<span style="background-color:#FFC0CB">TodoContext에 저장된 데이터를 바로 다이렉트로 공급 받을 수 있음</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/bee71fef-4f4b-48e8-9949-ca8d58605e42/image.png">

<p>이제부터 Editor 컴포넌트는 TodoContext로부터 onCreate 함수를 불러와 사용이 가능하고,
List 컴포넌트는 todos state를,
TodoItem 컴포넌트는 함수를 
다이렉트로 불러올 수 있음</p>
<p>컨텍스트 Provider 컴포넌트 안에 있는 컴포넌트는,
해당 컨텍스트의 데이터를 공급 받아 이용이 가능</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/ce226914-bb81-47a4-9a0f-ee7f02248c9b/image.png">

<p>브라우저의 컴포넌트에서도 확인 가능
설정한 대로 Editor와 List를 감싸고 있음</p>
<br>

<h2 id="☑️-데이터-꺼내기">☑️ 데이터 꺼내기</h2>
<p>이제 데이터를 꺼내서 써보자</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/5bd02286-0d50-4a7b-8e35-5783d537e41e/image.png">

<p>Editor가 받던 Props를 제거하고,
컨텍스트에서 해당 데이터를 꺼내올 것임</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/ce2fe009-3ff7-40d2-a71d-59c91b0cbebb/image.png">

<p><span style="background-color:#FFC0CB">useContext라는 훅을 사용하고,
인수로는 불러오고자 하는 컨텍스트를 넣음</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/12c95dc6-f46f-441e-a1bc-f3174ecc384d/image.png">

<p>(당연한 얘기지만(??)) 해당 컨텍스트는 export를 해야 함)</p>
<br>

<p>아무튼 이제 useContext라는 리액트 훅은
인수로 전달한 컨텍스트로부터** 공급된 데이터를 반환**</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/163e5533-6840-40bc-8a58-ce41767a6740/image.png">

<p><span style="background-color:#FDF5E6">그렇기 때문에 데이터라는 변수에 TodoContext가 제공한 값들이 들어있음</span></p>
<p>이제 값을 잘 불러왔는지,
콘솔을 통해 확인 한 번 해보면,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/23d2150c-a691-4deb-a41f-e6771335ea45/image.png">

<p>App 컴포넌트에서 Provider 컴포넌트에게 props로 공급했던 함수가
Editor 컴포넌트에게 잘 공급됨</p>
<br>


<img src="https://velog.velcdn.com/images/ann-algorithm/post/4b5fd7e5-a2ae-4900-9a67-5be8f6a5e676/image.png">

<p><span style="background-color:#FDF5E6">Editor 컴포넌트에서는 onCreate만 사용하면 되니까,
구조분해 할당을 이용해 불러옴</span></p>
<p>이제 props가 아닌 다이렉트로 데이터를 공급 완료!</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/ed6376eb-61b5-4ccc-8bad-49afab3a4335/image.png">

<ul>
<li>List 컴포넌트도 props를 제거</li>
<li>useContext를 통해 컨텍스트에서 데이터를 받아옴
<span style="background-color:#FDF5E6">(onUpdate, onDelete는 List 컴포넌트에서 사용하지 않을 것이므로 받아오지 않음)</span></li>
</ul>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/e345b02c-75e1-4389-997c-c80c492a4daf/image.png">

<p>TodoItem에서 해당 함수들을 직접 받아올 것이므로,
내려주는 props에서도 제거</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/1cbc94c7-fdff-4b11-ab9e-5737e14b2b29/image.png">

<p>TodoItem에서도 onUpdate, onDelete 함수를 props로 받아오지 않고,
<span style="background-color:#FDF5E6">useContext를 통해 구조분해 할당으로 받아오기</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/5bfc9dae-65d2-4d02-9fce-33ebf2f79a31/image.png">

<p>그런데 useCallback과 memo로 적용한
TodoItem들의 최적화가 풀려서
다른 아이템들도 리렌더링이 됨</p>
<p>🫠</p>
<hr>
<h1 id="📌-context-분리하기">📌 Context 분리하기</h1>
<p>컨텍스트를 적용하니,
이전에 적용해 두었던 최적화가 풀리는 문제를 확인</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/98662b2d-099b-496f-9868-4c5493aa5264/image.png">

<p><span style="background-color:#FFE4E1">프로바이더 컴포넌트도 엄연히 리액트의 컴포넌트이기 때문에,</span>
App 컴포넌트로부터 value props로
제공받는 todos(state), onCreate, onUpdate, onDelete를 감싸고 있는 객체가 바뀌게 되면
<span style="background-color:#FFC0CB">즉, 쉽게 말하면 props가 바뀌게 되면 리렌더링 발생</span></p>
<p>_<span style="background-color:#E0FFFF">근데, 이 객체가 왜 바뀜?</span>_</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/8b2aa0c9-30b4-4df8-80c0-cc9cbae32957/image.png">

<p>새로운 아이템을 추가/수정/삭제했을 때
<span style="background-color:#FFC0CB">todos state가 바뀌어서,
다시 객체를 생성해서 넘겨주게 됨</span>
➡️ <span style="background-color:#FDF5E6">그래서 프로바이더 컴포넌트도 리렌더링이 발생</span></p>
<p>따라서,
Editor, List, TodoItem 같은 하위 컴포넌트도
부모 컴포넌트가 리렌더링 되었기에,
자식 컴포넌트도 함께 리렌더링이 발생</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/e7350a5a-c5ea-4100-a154-9db945bf5e32/image.png">

<p>이전에, TodoItem 컴포넌트에 최적화를 적용하기 위해서
memo 메서드를 써서 자신이 받는 props가 바뀌지 않으면
아예 리렌더링을 발생시키지 않도록 설정해둔 적이 있음</p>
<p>_<span style="background-color:#E0FFFF">그런데 왜 리렌더링이 발생?</span>_</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/38171a46-2f10-45e5-b45d-b777163f3ecb/image.png">

<p>todo 아이템을 추가/수정/삭제할 경우,
<span style="background-color:#FFEBCD">todos state가 바뀌어서,
App 컴포넌트가 리렌더링이 되고,
프로바이더 컴포넌트에게 value props로 전달하는 객체 자체가 다시 생성됨</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/01867639-ddc0-4c40-a41b-e65c56b79d68/image.png">

<p>그렇기 때문에 TodoItem 컴포넌트에서,
useContext를 호출해서
<span style="background-color:#E6E6FA">TodoContext로부터 불러온 이 객체 자체가 다시 생성되어서
결국 TodoItem도 리렌더링이 발생한다는 것</span></p>
<p><span style="background-color:#E2D6FF">메모를 적용했더라도, useContext로 불러온 값이 변경되면,
이것은 props가 변경된 것과 동일하게 리렌더링을 발생</span>시킴</p>
<h2 id="☑️-문제-해결">☑️ 문제 해결</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/31e7da06-d12c-44e5-8c83-f0cef4030854/image.png">

<p><span style="background-color:#FFC0CB">현재 하나인 TodoContext를 두 개의 컨텍스트로 분리</span></p>
<ul>
<li>todos처럼 state이므로 변경될 수 있는 값은 TodoStateContext를 만들어 공급</li>
<li>반면에, 변경되지 않는 값들은(예전에 useCallback으로 묶어둔 적도 있는)
또 컨텍스트를 만들어 분리해서 공급</li>
</ul>
<p>➡️ todos state가 변경되어서 TodoStateContext가 공급하는 값이 바뀌어도 
함수는 바뀌지 않기 때문에,
<span style="background-color:#FFEBCD">TodoDispatchContext가 공급하는 값은 변경되지 않음</span></p>
<br>

<h3 id="두-개의-프로바이더-생성">두 개의 프로바이더 생성</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/f89f72e8-4e5c-4042-8439-40477a95090e/image.png">

<p>프로바이더 생성</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/5e80f529-74dc-4750-bcc1-44aaea9e75e2/image.png">

<p><span style="background-color:#FFEBCD">todos state는 TodoStateContext의 프로바이더에게만 value props로 공급</span>해주고,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/62a92067-809e-43d3-8ea3-1846b3bf6242/image.png">

<p>나머지 onCreate, onUpdate, onDelete 같은 변치 않을 함수는
<span style="background-color:#FFEBCD">TodoDispatchContext의 프로바이더에게만 value props로 공급</span>해주면 됨</p>
<h3 id="각-컴포넌트에서-데이터를-받아옴">각 컴포넌트에서 데이터를 받아옴</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/3545e3aa-f9c7-436e-8f28-a9f52d4bd13c/image.png">

<p>TodoEditor는 새로운 Todo 아이템을 추가해야 하니까,
<span style="background-color:#FFEBCD">TodoDispatchContext로부터 onCreate를 꺼내와서 쓰면 됨</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/9d911269-bb2f-4b5a-8ec0-86939f938c19/image.png">

<p>TodoITem 컴포넌트도
<span style="background-color:#FFEBCD">TodoDispatchContext로부터 onUpdate, onDelete를 꺼내와서 쓰면 됨
</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/761cfc65-9bd9-41bd-af3c-f6ecedb387b2/image.png">

<p>마지막으로 List 컴포넌트는
todos state를 가져와야 하니까,
<span style="background-color:#FFEBCD">TodoStateContext로부터 todos를 꺼내와서 씀</span></p>
<br>

<h3 id="동작-매커니즘">동작 매커니즘</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/0e3db7a9-1752-4ea0-8586-65049cd24cc2/image.png">

<p>만약 todos state가 갑자기 업데이트(수정/삭제/생성)되어도,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/bb442363-866c-488e-bf44-39b13ff6d5bf/image.png">

<p>다른 컴포넌트는 리렌더링되지 않고,
TodoList 컴포넌트만 리렌더링 될 것</p>
<br>

<p>원래라면 
부모 컴포넌트가 리렌더링되면 리스트와 에디터, 아이템이 모두 렌더링되는 게 맞지만</p>
<p><span style="background-color:#FFEBCD">이렇게 컨텍스트도 분리하고, 메모를 적용해놓았기 때문에
TodoItem에는 리렌더링이 이때는 발생하지 않게 되는 것</span>
➡️ 이 구조대로 코드 변경</p>
<h3 id="코드에-적용">코드에 적용</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/edb4dd61-b708-4c51-8f68-cac8923ad799/image.png">

<p>기존 컨텍스트를 두 개로 분리</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/343e4172-76ee-41b9-ad9f-4411455b5d12/image.png">

<p>각 컨텍스트는 자신이 관리할 데이터를 가지고 Provider 컴포넌트로 각 컴포넌트를 포함
(아까 위 흐름도를 참고...)</p>
<br>

<p>App 컴포넌트의 todos state가 변경되어 리렌더링이 되면 객체가 다시 생성될 건데,
그외는 useMemo로 객체를 다시 생성하지 않도록 하자</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/7c5d3151-f997-49f7-b56d-a9d2cd86e91e/image.png">

<img src="https://velog.velcdn.com/images/ann-algorithm/post/42489e61-5e39-455e-b653-5aaf96a9cff6/image.png">

<p>한번 마운트된 이후로는 리렌더링되어도 다시 생성하지 않도록
deps는 빈 배열로 둠</p>
<p><span style="background-color:#FFC0CB">변경되지 않을 것들은 useMemo의  콜백함수 안에서 리턴</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/85b46961-263f-4ea1-989f-e58bba50e9cf/image.png">

<p>이제 이 <span style="background-color:#FFEBCD">memoizedDispatch 변수를 전달하면 됨</span>
➡️ TodoDispatchContext가 공급하는 값은 어떠한 상황에서도 변경되지 않을 것</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/d52313c0-22cd-4ec9-b56a-f16a4aaafe02/image.png">

<p>Editor 컴포넌트는,
onCreate를 TodoDispatchContext로부터 함수를 받음</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/f25e5468-2851-48bb-bb5d-0af3797bb987/image.png">

<p>List 컴포넌트에선,
todo를 value로 저장하면 구조 분해가 아니라 바로 받아올 수 있음
(지금은 객체로 저장했음...)</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/e4d8c05c-17d6-4098-b2f6-8224eb26f866/image.png">

<p>TodoItem 컴포넌트에선,
onUpdate, onDelete 함수를 구조분해 할당으로 받아옴</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/d026b71a-fb6b-4087-b009-4cfe345f0eb9/image.png">

<p>최적화 적용 확인</p>
<br>

<h2 id="☑️-정리">☑️ 정리(?)</h2>
<p>Props drilling을 방지하고,
해당 컨텍스트를 통해 넘기는 객체 때문에 리렌더링되는 걸 방지하기 위해...
컨텍스트를 분리하기도 하고,
useMemo로 다시 감싸줌</p>
<h2 id="그런데">그런데...</h2>
<p>이 컨텍스트는 전역 상태 관리 용도로 사용하기엔 좋지 않다고 함
...</p>
<p>따라서 React의 상태 관리에 대해 더 공부해 보자</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[최적화]]></title>
            <link>https://velog.io/@ann-algorithm/%EC%B5%9C%EC%A0%81%ED%99%94</link>
            <guid>https://velog.io/@ann-algorithm/%EC%B5%9C%EC%A0%81%ED%99%94</guid>
            <pubDate>Wed, 28 Jan 2026 15:49:01 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-최적화란">📌 최적화란?</h1>
<blockquote>
<p>웹 서비스의 성능을 개선하는 모든 행위</p>
</blockquote>
<p>아주 단순한 것부터 아주 어려운 방법까지 매우 다양함</p>
<h3 id="일반적인-웹-서비스-최적화-방법">일반적인 웹 서비스 최적화 방법</h3>
<ul>
<li>서버의 응답 속도 개선</li>
<li>이미지, 폰트, 코드 파일 등의 정적 파일 로딩 개선</li>
<li>불필요한 네트워크 요청 줄임</li>
</ul>
<h3 id="리액트-앱-내부의--최적화-방법">리액트 앱 내부의  최적화 방법</h3>
<ul>
<li>컴포넌트 내부의 불필요한 연산 방지</li>
<li>컴포넌트 내부의 불필요한 함수 재생성 방지</li>
<li>컴포넌트 내부의 불필요한 리렌더링 방지</li>
</ul>
<hr>
<h1 id="📌-usememo와-연산-최적화">📌 useMemo와 연산 최적화</h1>
<h2 id="☑️-usememo">☑️ useMemo</h2>
<blockquote>
<p>&quot;메모이제이션&quot; 기법을 기반으로 불필요한 연산을 최적화하는 리액트 룩
자매품: useCallback</p>
</blockquote>
<blockquote>
<p>📝 <strong>메모이제이션</strong>
기억해두기, 메모해두기라는 뜻</p>
</blockquote>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/61dfb6ee-fbb9-49ee-b9d3-3a6088c602f4/image.png">

<img src="https://velog.velcdn.com/images/ann-algorithm/post/2d88c28e-8101-4675-9c43-048a48a9b362/image.png">

<p>위와 같이 &quot;반복적으로 수행하는 동일한 연산&quot;을 매번 새롭게 하는 대신</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/e2e91e45-e7da-4758-af43-efdfa9a73163/image.png">

<img src="https://velog.velcdn.com/images/ann-algorithm/post/0fcd6126-20f9-4f3a-b7b2-e9219cd4cc55/image.png">

<p>최초로 한 번 계산했을 때의 결과값을 메모리 어딘가에 보관해둔 다음,
다시 이 연산이 필요해지면 저장된 결과값을 바로 돌려주는 기법
➡️ 최초로 한 번 연산을 수행해서 결과값을 저장해둔 후에는
<span style="background-color:#FFC0CB">매번 똑같은 연산을 불필요하게 다시 수행할 필요가 없기 때문에
프로그래밍 성능 저하를 방지</span></p>
<blockquote>
<p>일상에서도,...
레스토랑에서 시킨 메뉴가 독특할 때,
지나가는 사람이 그 메뉴가 무엇인지 물어보면...
그때마다 메뉴판을 열어 한참 찾아보고 대답 해주지 않고,
머릿속에 있는 메뉴의 이름을 기억했다가 말해주는 것
➡️ 메모이제이션</p>
</blockquote>
<p>위와 같은 방식을 사용하기 위해 <strong>useMemo</strong>를 사용하면,
특정 연산의 결과 값 기억 가능</p>
<h2 id="☑️-실습">☑️ 실습</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/ffb66ff2-c2c2-41ed-8de4-b1b6f060caf4/image.png" width=500>

<p>위 리스트 컴포넌트 안의 todo의 상태를 분석해서 수치로 제공하는 함수 만들기</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/ebcac31b-7b59-44ba-8d6d-7a73bca35c31/image.png" width=600>

<p>새로운 함수를 만들어,</p>
<ul>
<li>전체 todo 아이템의 개수를 저장하는 <strong>totalCount</strong> 저장
➡️ 따라서 todo의 length로 초기화</li>
<li>전체 todo 아이템 중 완료된 todo의 개수 <strong>doneCount</strong> 저장
➡️ filter 메서드를 통해 isDone 이라는 프로퍼티가 참인 아이템만 필터링해서 length로 그 길이 저장</li>
<li>또 완료되지 않은 todo의 개수는 <strong>totalCount</strong>에서 <strong>doneCount만큼</strong> 뺀 값 저장</li>
</ul>
<p>위 세 개의 값을 return 객체로 묶어 내보냄</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/5eade824-5be4-416f-9cf6-8122bc992307/image.png" width=600>

<p>이제 리스트 컴포넌트가 리렌더링될 때마다
<span style="background-color:#FFC0CB">방금 만든 함수를 호출해서 구조분해 할당을 이용해 값을 받아옴</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/abdf0ec1-4568-4471-b458-21a478e832ae/image.png" width=500>

<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/0ec412ab-7842-4d6a-a1e4-fbc2ac5f948f/image.png">

<p>수치 값을 렌더링</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/da66283f-2f2e-40eb-aed5-16c66a77810e/image.png" width=600>


<p>그런데 이 연산 과정은 <span style="background-color:#E2D6FF">filter라는 배열 메서드를 이용하고 있기 때문에
(필터 메서드는 배열 내에서 전체 요소를 한 번씩 다 순회하기 때문에)</span>
<span style="background-color:#FFC0CB">todo state에 보관된 데이터의 개수가 증가하면 증가할수록 더 오래 걸리는 함수가 됨</span></p>
<p>이 함수가 불필요하게 호출되는 경우를 방지해야 하는데,
이 함수는 컴포넌트 내에서 바로 호출되고 있기 때문에
결국 이 리스트 컴포넌트가 리렌더링될 때마다 새로 호출됨</p>
<h3 id="실제로-확인">실제로 확인</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/2a8b53bd-bcd3-4072-b9e1-69b00eebf5cc/image.png" width=500>

<p>콘솔로그를 출력하고 직접 확인해보면</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/8fe98712-c334-4ff6-8107-0642edf506f6/image.png" width=500>

<p>서치바에 값을 입력만 해도,
getAnalyzedData 함수가 호출되고 있음
➡️ 명백한 낭비!</p>
<p>(실제로 어떤 todo 데이터가 추가되거나 삭제되거나 완료되어서 수치 결과가 변하는 것도 아닌데!)
이 서치바에 뭔가를 검색한다고 해서,
<span style="background-color:#FDF5E6">이 함수를 다시 호출할 필요가 전혀 없음</span></p>
<p><span style="background-color:#E2D6FF">물론 새 todo가 추가되거나 수정되거나 삭제되면 호출 되는 게 맞음</span></p>
<p><strong>결론적으로</strong>
<span style="background-color:#FFEBCD">연산 자체를 메모이제이션 할 수 있는 방법이 필요하고,
이럴 때 사용하는 게 <strong>useMemo</strong>라는 훅</span>
➡️ 특정 조건이 만족했을 떄 결과 값을 다시 계산하도록 설정 가능</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/400c9d63-aa85-4103-83ef-c5d8361f848f/image.png">

<img src="https://velog.velcdn.com/images/ann-algorithm/post/63fa6405-7cce-4dd4-82f2-884e5b9f9d71/image.png">

<p>import를 추가하고,
컴포넌트 내부에서 useMemo 호출</p>
<ul>
<li>첫 번째 인수: 콜백함수</li>
<li>두 번째 인수: 의존성 배열</li>
</ul>
<p>이전에 배운 useEffect에서 deps(의존성 배열)에 들어가는 값이 바뀌면
콜백함수를 다시 실행하는 것과 비슷한 매커니즘
<span style="background-color:#FFC0CB">useMemo도 똑같이 deps에 포함된 값이 변경되었을 때에만 첫 번째 인수로 전달한 콜백함수 반환</span>
<span style="background-color:#FFEBCD">추가로, 해당 콜백 함수가 반환하는 값을 useMemo는 그대로 반환</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/65a6edad-6006-4518-9da4-d6146fd91c0a/image.png">

<p>예를 들어 위 결과값을 받아 사용까지 가능</p>
<br>

<p>그렇기 때문에,
useMemo의 첫 번째 인수인 콜백 함수에는 메모이제이션하고 싶은 연산 복붙</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/653c0eb9-447d-4d4e-b4ec-f487e10b569a/image.png" width=500>

<p>위 부분을 복붙해서,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/a14e11df-5356-4985-88f6-b821edc4036f/image.png" width=500>

<p>useMemo의 첫 번째 인수에 넣음</p>
<p><span style="background-color:#FFC0CB">첫 번째 인수로 전달한 콜백함수가 반환하는 값을 그대로 반환</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/dd1a99d4-27ee-48c7-bd91-1abe0a2ee474/image.png" width=500>

<p>그대로 받아오기도 가능,
의존성 배열에 todo를 입력하여,
todo에 의존하도록...</p>
<p>왜냐하면 빈 배열로 두면,
(컴포넌트가 렌더링될 때만, 첫 번째 인수에 들어간 콜백 함수의 연산 수행과 반환이 이루어짐)
todo state의 값이 변경되어도,
새로운 todo가 추가/삭제/수정되어도
이 연산은 다시 실행되지 않음</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/9ee7f430-9276-432c-9244-aaac82b78f2f/image.png" >

<p>useMemo가 deps에 의존하는 값이 없어서,
체크를 더 해도, 수치가 갱신되지 않음
➡️ 오류!!🤬</p>
<br>

<p>원하는 건!
서치 바에 입력이 되었을 땐 수행하지 않고,
todo 데이터가 추가되었을 땐 수행하긴 해야 함
(todo 데이터를 분석하는 함수니까)</p>
<p><span style="background-color:#FFC0CB">따라서 의존성 배열에 todo를 추가해야 함</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/7f0b7971-bc40-441b-b504-1ef19a651539/image.png">

<p>잘 갱신되고,</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/6852cbda-228f-48ed-9c52-e5bbf05a8660/image.png">

<p><span style="background-color:#FFE4E1">서치 바 업데이트로는
useMemo 내의 연산을 다시 하지 않는 것을 볼 수 있음!</span></p>
<hr>
<h1 id="📌-reactmemo와-컴포넌트-렌더링-최적화">📌 React.memo와 컴포넌트 렌더링 최적화</h1>
<blockquote>
<p>컴포넌트를 인수로 받아,
최적화된 컴포넌트로 만들어 반환</p>
</blockquote>
<pre><code class="language-js">const MemoizedComponent = memo(Component);</code></pre>
<p>이건 <span style="background-color:#FFEBCD">리액트의 내장 메서드</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/044e43f8-0c8c-44a2-abae-86654b00d754/image.png">

<p><span style="background-color:#E6E6FA">리액트의 컴포넌트를 인수로 받아서,
해당 컴포넌트에 최적화 기능을 추가한 다음
결과값으로 반환</span></p>
<br>

<p>이렇게 <span style="background-color:#FFEBCD">최적화 기능이 추가된 컴포넌트는 props를 기준으로 메모이제이션됨</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/e17d674c-3328-4526-96dc-b0d07c2df894/image.png">

<p><span style="background-color:#FFC0CB">이 MemoizedComponent는 부모 컴포넌트가 리렌더링 되더라도,
자신이 받는 props가 바뀌지 않으면,</span>
<span style="background-color:#FFC0CB"><strong>다시는 리렌더링이 발생하지 않도록 메모이제이션</strong></span>
➡️ <span style="background-color:#E2D6FF">불필요한 리렌더링이 방지되어 최적화가 이루어짐</span></p>
<h2 id="☑️-실습-1">☑️ 실습</h2>
<h3 id="header-컴포넌트-최적화">Header 컴포넌트 최적화</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/9164b7a0-8eb9-4d42-b7f3-0a6cd3251e8f/image.png">

<p>체크박스를 누르면,
Header, Editor, List 컴포넌트까지 모두 리렌더링 발생</p>
<p>todo 데이터 하나 눌렀을 뿐인데,
화면의 모든 컴포넌트가 다시 리렌더링되고 있음</p>
<p>코드 상에서
Header 컴포넌트가 App 컴포넌트의 자식으로 배치되어 있고,
부모 컴포넌트인 App 컴포넌트의 리렌더링에 따라 똑같이 렌더링되고 있음</p>
<br>

<p><em>굳이, 날짜를 렌더링하는 Header 컴포넌트가 리렌더링될 필요가 없음</em></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/adea4b4a-9549-4fb6-8ac5-f8828c82216d/image.png" width=600>

<p>Header는 props로 받는 값도 없고,...</p>
<p>따라서 이러한 Header 컴포넌트의 불필요한 리렌더링을 방지하기 위해
React의 memo라는 메서드를 사용해보자</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/0e68f8e8-aaae-49e8-913b-c58a26c89823/image.png" width=500>

<ul>
<li>import 추가</li>
<li>컴포넌트 바깥에서 메모 메서드를 호출하고
<span style="background-color:#FFC0CB">인수로 최적화하고 싶은 컴포넌트인 Header를 그대로 넣음</span>

</li>
</ul>
<br>

<p>이제 memo 메서드는 인수로 받은 Header 컴포넌트를 props가 변경되지 않았을 때에는
리렌더링하지 않도록 최적화해서 반환해야 함</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/3ac0c9ee-5cfb-4407-8a3d-3c14c339ca0c/image.png" width=500>

<p>위와 같이 변수에 저장해서 export문에서 내보내면 됨</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/35fdc660-1389-43e0-8b97-a688a81b50a8/image.png" width=500>

<p>경고가 뜨긴 하는데,
리액트의 동작에는 영향을 주지 않음</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/ef056557-d6d9-4a44-8979-617ae13614de/image.png" width=500>

<p>해당 옵션을 꺼버리면 됨</p>
<br>

<p>이렇게 React의 memo 메서드를 사용해서 컴포넌트를 최적화하면,
내보낸 memoizedHeader 컴포넌트는 자신이 받는 props가 바뀌지 않으면
다시는 리렌더링되지 않음</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/f91b8a0b-a37c-45f4-8c22-9ba23a92bf98/image.png" width=500>

<p>위처럼 단축해서 export해도 됨</p>
<h3 id=""></h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/f55825c3-4d53-4e47-9325-c209282b6bc7/image.png">

<p>출근하기 todo를 수정하는데,
굳이 다른 아이템 모두 리렌더링이 되고 있음
➡️ 엄연히 불필요한 리렌더링</p>
<p>todo 아이템 컴포넌트들도 props가 변경되지 않으면 리렌더링되지 않도록
메모 메서드를 이용해서 최적화</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/6443641b-64d0-47c5-b804-509e9c754796/image.png" width=500>

<p>위와 같이 memo 메서드를 이용해서 최적화</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/4c0555ad-18df-41bd-aaa3-b9069c8cba55/image.png" width=650>

<p>그럼 이제 TodoItem 컴포넌트는,
위 props가 바뀌지 않는 이상
렌더링이 발생하지 않을 것</p>
<br>

<p>그럼 이제 다시 체크 박스를 눌러가며,
렌더링 되는 걸 보면...</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/c03df134-a021-427c-ac2b-13955428254e/image.png">

<p><del>응 여전함</del></p>
<p>memo 메서드가 제대로 동작하지 않음</p>
<p>분명 memo 메서드를 이용해서,
TodoItem 컴포넌트의 props가 바뀌지 않을 경우
리렌더링 되지 않도록 설정했는데,
<em>왜 첫 번째 todo 아이템을 수정하는데, 두 번째 세 번째 아이템 컴포넌트도 리렌더링 되는 것인가?</em></p>
<p><span style="background-color:#E2D6FF"><strong>왜냐하면,</strong></span>
이 체크박스를 클릭해서 todo 아이템을 변경하면,
App 컴포넌트에 있는 todo state의 값을 바꾸게 되면
App 컴포넌트가 리렌더링 됨
➡️ App 컴포넌트가 다시 호출됨</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/fa934cc6-e439-4d28-af08-16ab070a9df9/image.png" width=500>

<p><span style="background-color:#FDF5E6">따라서 위 onCreate, onUpdate 함수도 새로 만들어짐</span></p>
<blockquote>
<p>함수는 <strong>객체 타입</strong>임
새롭게 생성된 위 함수들이 같은 동작을 하더라도,
새롭게 생성될 때마다 다른 값으로 인식됨</p>
<p>함수는 객체 타입에 해당하므로,
변수에 주소값이 저장되는데,
객체 간의 비교는 이 주소값을 기반으로 수행됨</p>
</blockquote>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/72156708-d851-42f6-86e2-fbdf9945a2cd/image.png">
> 위 두 개의 객체는 같아 보이지만, ===로 비교하면, 다른 객체라고 평가됨
>
> <span style="background-color:#E2D6FF">따라서 객체 타입에 해당하는 함수 또한 컴포넌트가 리렌더링 되면서
새롭게 다시 생성되면
주소값이 계속 바뀌기 때문에
사실상 매번 다른 값으로 생성되는 것으로 판단</span>됨
> ➡️ <span style="background-color:#FFC0CB">즉, TodoItem 컴포넌트에게 전달되는 onUpdate, onDelete가 
매번 App 컴포넌트가 리렌더링될 때마다
매번 새롭게 생성이 되어서 전달이 되고 있던 것임</span>

<p>memo 메서드는 props가 바뀌었을 때만
컴포넌트를 리렌더링하도록 최적화하기 때문에,</p>
<p><strong>매번 리렌더링될 때마다</strong>
현재의 props와 과거의 props를 비교
➡️ 두 개의 props가 같은 값인지 다른 값인지 판단해서,
TodoItem 컴포넌트를 리렌더링 할지말지 결정</p>
<p><span style="background-color:#FFEBCD">이 memo 메서드는 얕은 비교로 비교하기 때문에,
객체 타입의 값은 무조건 서로 다른 값이라고 판단되는 것</span>
➡️ props가 바뀐다고 판단되기 때문에,</p>
<p>결과적으로 브라우저에서 하나의 todo 아이템을 수정하게 되면
다른 todo 아이템들에서도 결국 모두 리렌더링이 발생하는 것</p>
<br>

<p>이렇게 객체 타입의 값을 props로 받고 있는 컴포넌트를 예를 들어보면,
memo 메서드를 적용한다기만 한다고 해서 최적화가 제대로 이루어지지 않음</p>
<p>App 컴포넌트에서 이 함수 자체를 메모이제이션해서,
리렌더링이 되더라도 다시 생성되지 않게 방지하려면, <strong>useCallback을</strong> 이용해야 함</p>
<br>

<p>TodoItem의 memo 메서드 안에,
<strong>두 번재 인수로 콜백함수를 추가로 전달해서</strong>
최적화 기능을 커스터마이징</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/9a0de73e-cf14-4f08-9433-b718676cc1ab/image.png" width=500>

<p>메모 함수의 두 번재 인수로 콜백 함수를 전달할 수 있는데,
<strong>이 콜백 함수는 보통 생략하지만</strong>
전달하게 되면,</p>
<p>memo 메서드는 부모 컴포넌트가 리렌더링될 때마다,
컴포넌트의 props를 바뀌었는지 아닌지 스스로 판단하는 대신,
➡️ <span style="background-color:#FFEBCD">콜백함수의 매개변수로 과거의 props인 prevProps와 미래의 props인 nextProps를 전달해서,
이 함수의 반환값에 따라 props가 바뀌었는지 안 바뀌었는지 판단
</span></p>
<p>콜백함수에서</p>
<ul>
<li>true를 반환하면 Props가 바뀌지 않았다고 판단</li>
<li>false를 반환하면 Props가 바뀌었다고 판단</li>
</ul>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/4c0555ad-18df-41bd-aaa3-b9069c8cba55/image.png" width=650>

<p>TodoItem 컴포넌트가 받는
컴포넌트의 props 중에 onUpdate, onDelete 빼고
그 외의 props가 바뀌었을 때만 리렌더링을 시켜주자</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/579e2eae-33b7-427e-b096-127f05192548/image.png" width=500>

<p>네 개의 값이 바뀌지 않았다면 true가 반환해서 리렌더링하지 말라고 설정</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/0f3a205e-38a3-4e1a-b43a-21670ca1d11f/image.png" width=500>

<p>이제 해당 컴포넌트만 리렌더링이 되는 걸 볼 수 있음</p>
<br>

<blockquote>
<p>컴포넌트를 인수로 받아서 해당 컴포넌트에 최적화나, 
메모이제이션 같은 추가적인 기능을 덧붙여 기능이 추가된
컴포넌트를 반환하는 memo와 같은 메서드를
리액트에서는 <strong>고차 함수 컴포넌트</strong>라고 함</p>
<p>Higher Order Component = HOC
한 번 호출하는 것만으로도 컴포넌트에 새로운 기능 부여 가능
➡️ 복잡한 리액트 앱을 구축할 때 꽤나 자주 쓰는 방식</p>
</blockquote>
<hr>
<h1 id="📌-usecallback과-함수-재생성-방지">📌 useCallback과 함수 재생성 방지</h1>
<p>memo 메서드는 현재 컴포넌트의 props가 변경되었는지 얕은 비교로 판단하기 때문에
<span style="background-color:#FFE4E1">onUpdate나 onDelete 같은 함수,
객체 타입의 값을 props로 전달할 때는 제대로된 최적화가 이루어지지 않아서</span>
별도로 콜백 함수를 추가적으로 전달해서,
일일히 하나하나의 props의 값이 바뀌었는지 비교해야 함</p>
<p>아래처럼...</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/579e2eae-33b7-427e-b096-127f05192548/image.png" width=500>

<p>그런데 매번 이런 식이면 불편함
<span style="background-color:#FDF5E6">props의 이름이 바뀌거나 추가되면,
매번 조건을 수정하거나 추가해야 하기 때문</span></p>
<p>이럴 때는 그냥 onUpdate나, onDelete 같은 함수가
애초에 생성되지 않기 최적화
➡️ <span style="background-color:#FFC0CB"><strong>useCallback</strong>을 사용</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/90caa269-0a59-4732-9e90-c77fef9c8161/image.png" width=500>

<p>App 컴포넌트에,
useCallback 사용</p>
<ul>
<li>첫 번째 인수로는 최적화하고 싶은 함수
그러니까 <span style="background-color:#FFC0CB">불필요하게 재생성되지 않도록 방지하고 싶은 함수</span></li>
<li>두 번째 인수로는 의존성 배열</li>
</ul>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/6c3ad6d8-2c12-4cf5-a3c3-2612f98dcce7/image.png" width=500>


<p>이 useCallback은 기본적으로
<span style="background-color:#FFC0CB">우리가 첫 번째 인수로 전달한 콜백함수를 그대로 생성해서 반환</span>
➡️ 따라서 변수에 담을 수 있음</p>
<p><span style="background-color:#FFC0CB">이렇게 생성되는 함수를 deps가 변경되었을 때만 다시 생성</span>
<span style="background-color:#E6E6FA">즉, 함수를 렌더링</span></p>
<blockquote>
<p>의존성 배열이 비어있으면,
이 컴포넌트가 최초로 한 번 렌더링 될 때
마운트 될 때에만 이 함수를 생성하고,
그 뒤에는 리렌더링이 발생해도 이 함수를 생성하지 않음
➡️ <span style="background-color:#E2D6FF">onDelete, onUpdate 같은 함수를 생성하지 않게 최적화 가능</span></p>
</blockquote>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/90caa269-0a59-4732-9e90-c77fef9c8161/image.png" width=500>

<p>useCallback을 호출하고,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/26956997-a463-4675-8675-2976d1c40151/image.png" width=400>

<p>첫 번째 인수로는 메모이제이션 함수를 넣어야 하니,
onDelete 함수를 세미콜론만 빼고 복사해서 익명 함수로 복사해서 붙여넣기</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/5f71d4dd-5724-462a-ac98-ffca2e4e8303/image.png" width=400>

<p><strong>deps에는 빈 배열을 넣어서 onDelete를 다시는 생성하지 않도록 하기</strong></p>
<p>기존 onDelete 함수는 지워주기</p>
<p>➡️ 이제 onDelete 함수는 마운트 되었을 때 딱 한 번만 생성
이후에는 컴포넌트가 리렌더링 되어도 재생성되지 않도록 최적화됨</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/c1030088-fdeb-43d9-a4ae-68215cf354b2/image.png" width=500>

<p>세 함수 모두 마운트 이후 생성되지 않도록 최적화 완성</p>
<br>

<p>이제 TodoItem 컴포넌트에서 더 이상,
별도의 컴포넌트를 memo 메서드에 전달하지 않아도
props로 받는 onUpdate와 onDelete 함수가 다시 생성되지 않은 채로 받게 될 것</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/23d6b96d-81bd-4723-92d0-9a39e59b8950/image.png" width=500>

<p>따라서 이전에 했던 것처럼 memo 메서드만 적용한
TodoItem을 내보내도 최적화 가능</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/5f031398-2594-4963-a79b-a3ceed14fe8d/image.png">


<br>

<h2 id="이러한-최적화는-언제-하는가">이러한 최적화는 언제 하는가?</h2>
<ul>
<li>너무 이른 타이밍에 해도 문제가 되고</li>
<li>너무 많이 최적화해도 문제가 될 수 있음</li>
</ul>
<p>따라서,
리액트 앱을 최적화할 때는 하나의 프로젝트를 거의 완성한 상태에서 최적화</p>
<p>기능을 구현, 완성 후 최적화 진행</p>
<p><span style="background-color:#FDF5E6">왜냐하면, 이런 useCallback 같은 메서드를 적용하고 나면,
새로운 기능을 덧붙이거나 수정할 때 최적화가 풀리거나 아예 고장남</span>
➡️ 구현이 완료되고 마지막에 최적화 하는 걸 권장</p>
<br>

<p>그리고 모든 것에 최적화하면 안 되고,
최적화가 필요할 것 같은 연산이나 함수나 컴포넌트에만 최적화</p>
<p>왜냐하면,
Header 컴포넌트를 최적화한 memo 메서드는 매우 단순해 보이지만
당연히 연산이 필요함</p>
<ul>
<li>props의 값을 비교하거나,</li>
<li>메모이제이션을 위해메모리에 컴포넌트의 결과 값을 보관한다든가</li>
</ul>
<p>그래서 이렇게 최적화된 컴포넌트가 고작 별거 아닌 UI를 렌더링하는 컴포넌트면
리렌더링이 빠를 수도...</p>
<br>

<p>따라서 Header 같은 사소한 컴포넌트는 최적화를 잘 하지 않고...
TodoItem 컴포넌트처럼 유저의 행동에 따라 개수가 많아지는 컴포넌트나,
함수를 많이 가지고 있어서 코드가 무거운 컴포넌트에 한해
최적화를 수행하는 것을 권장</p>
<blockquote>
<p><strong>아티클 &quot;When to use useMemo, useCallback&quot;</strong>
<a href="https://goongoguma.github.io/2021/04/26/When-to-useMemo-and-useCallback/">https://goongoguma.github.io/2021/04/26/When-to-useMemo-and-useCallback/</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[모던 자바스크립트 39장. DOM]]></title>
            <link>https://velog.io/@ann-algorithm/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-39%EC%9E%A5.-DOM</link>
            <guid>https://velog.io/@ann-algorithm/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-39%EC%9E%A5.-DOM</guid>
            <pubDate>Mon, 26 Jan 2026 13:40:34 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>📝 <strong>DOM</strong>
브라우저의 렌더링 엔진은 HTML 문서를 파싱하여
브라우저가 이해할 수 있는 자료구조인 DOM을 생성
DOM은 HTML 문서의 계층적 구조와 정보를 표현하며,
이를 제어할 수 있는 API, 즉 프로퍼티와 메서드를 제공하는 트리 자료구조</p>
</blockquote>
<h1 id="📌-노드">📌 노드</h1>
<h2 id="☑️-html-요소와-노드-객체">☑️ HTML 요소와 노드 객체</h2>
<blockquote>
<p><strong>HTML 요소</strong>
HTML 문서를 구성하는 개별적인 요소</p>
</blockquote>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/c1343512-326a-4649-b1c3-2fa18442350f/image.png">

<br>

<p><span style="background-color:#FFEBCD">HTML 요소는 렌더링 엔진에 의해 파싱되어 DOM을 구성하는 요소 노드 객체로 변환</span></p>
<p>이때 </p>
<ul>
<li>HTML 요소의 어트리뷰트는 어트리뷰트 노드로,</li>
<li>HTML 요소의 텍스트 콘텐츠는 텍스트 노드로</li>
</ul>
<p>변환</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/aab8b76d-8736-4a2e-a134-a3e1be9234a8/image.png">

<br>

<p>HTML 문서는 HTML 요소들의 집합
➡️ HTML 요소는 중첩 관계를 가짐</p>
<p>즉, HTML 요소의 콘텐츠 영역(시작 태그와 종료 태그)에는,
텍스트뿐만 아니라 다른 HTML 요소도 포함 가능</p>
<p><span style="background-color:#FFC0CB">이때 HTML 요소 간에는 중첩 관계에 의해 계층적인 부자 관계가 형성</span>
이러한 HTML 요소 간의 부자 관계를 반영하여,
<span style="background-color:#FDF5E6">HTML 문서의 구성 요소인 HTML 요소를 객체화한 모든 노드 객체들을 트리 자료 구조로 구성</span></p>
<h2 id="☑️-트리-자료구조">☑️ 트리 자료구조</h2>
<p>노드들의 계층 구조로 이뤄짐</p>
<p>즉, 트리 자료구조는 부모 노드와 자식 노드로 구성되어
<span style="background-color:#FFEBCD">노드 간의 계층적 구조(부자, 형제 관계)를 표현하는 비선형 자료구조</span></p>
<p><span style="background-color:#FFEBCD">노드 객체들로 구성된 트리 자료구조를 DOM이라고 함</span>
DOM 트리라고 부르기도 함</p>
<h2 id="☑️-노드-객체의-타입">☑️ 노드 객체의 타입</h2>
<h3 id="문서-노드document-node">문서 노드(document node)</h3>
<ul>
<li>DOM 트리의 최상위에 존재하는 루트 노드</li>
<li>document 객체를 가리킴</li>
</ul>
<p><span style="background-color:#FFEBCD">document 객체는 브라우저가 렌더링한 HTML 문서 전체를 가리키는 객체</span>
전역 객체 window의 document 프로퍼티에 바인딩되어 있음
<span style="background-color:#FDF5E6">➡️ 따라서 문서 노드는 window.document 또는 document로 참조 가능</span></p>
<p><span style="background-color:#E2D6FF">브라우저 환경의 모든 자바스크립트 코드는 script 태그에 의해 분리되어 있어도 하나의 전역 객체 window를 공유</span>
<span style="background-color:#FFC0CB">따라서 모든 자바스크립트 코드는 
전역 객체 window의 document 프로퍼티에 바인딩 되어 있는 하나의 document 객체를 바라봄</span>
즉, HTML 문서당 document 객체는 유일</p>
<p>문서 노드, 즉 document 객체는 DOM 트리의 루트 노드
➡️ DOM 트리의 노드들에 접근하기 위한 진입점 역할 담당
<span style="background-color:#FFC0CB">즉 요소, 어트리뷰트, 텍스트 노드에 접근하려면 문서 노드를 통해야</span></p>
<h3 id="요소-노드element-node">요소 노드(element node)</h3>
<ul>
<li>HTML 요소를 가리키는 객체</li>
</ul>
<p>요소 노드는 HTML 요소 간의 중첩에 의해 부자 관계를 가지며,
이 부자 관계를 통해 정보를 구조화
➡️ 요소 노드는 문서의 구조를 표현</p>
<h3 id="어트리뷰트-노드attribute-node">어트리뷰트 노드(attribute node)</h3>
<ul>
<li>HTML 요소의 어트리뷰트를 가리키는 객체</li>
</ul>
<p>어트리뷰트 노드는 어트리뷰트가 지정된 HTML 요소의 요소 노드와 연결되어 있음</p>
<p>단, 요소 노드는 부모 노드와 연결되어 있지만,
어트리뷰트 노드는 부모 노드와 연결되어 있지 않고 요소 노드에만 연결되어 있음</p>
<p><span style="background-color:#FFEBCD">즉, 어트리뷰트 노드는 부모 노드가 없으므로 요소 노드의 형제 노드는 아님</span>
<span style="background-color:#FFC0CB">따라서 어트리뷰트 노드에 접근하여, 어트리뷰트를 참조하거나 변경하려면
먼저 요소 노드에 접근해야</span></p>
<h3 id="텍스트-노드text-node">텍스트 노드(text node)</h3>
<ul>
<li>HTML 요소의 텍스트를 가리키는 객체</li>
</ul>
<p>요소 노드가 문서의 구조를 표현한다면
<span style="background-color:#FFEBCD">텍스트 노드는 문서의 정보를 표현한다고 할 수 있음</span></p>
<p>요소 노드의 자식 노드이며,
자식 노드를 가질 수 없는 리프 노드
➡️ 텍스트 노드는 DOM 트리의 최종단</p>
<p><span style="background-color:#FFC0CB">따라서 텍스트 노드에 접근하려면
먼저 부모 노드인 요소 노드에 접근해야</span></p>
<h2 id="☑️-노드-객체의-상속-구조">☑️ 노드 객체의 상속 구조</h2>
<p>DOM은 구성하는 노드 객체는 자신의 구조와 정보를 제어할 수 있는 DOM API 사용 가능
➡️ 이를 통해 노드 객체는 자신의 부모, 형제, 자식을 탐색할 수 있으며,
<span style="background-color:#FFEBCD">자신의 어트리뷰트와 텍스트를 조작 가능</span></p>
<blockquote>
<p>DOM을 구성하는 노드 객체는 ECMASCript 사양에 정의된 표준 빌트인 객체가 아니라,
브라우저 환경에서 추가적으로 제공하는 호스트 객체</p>
</blockquote>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/edb7d937-a5a4-4553-b43a-dd6cbd56aaae/image.png">

<p>하지만 노드 객체도 자바스크립트 객체이므로 프로토타입에 의한 상속 구조를 가짐</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/724c1e39-19da-440a-98e9-48250ce0bb91/image.png" width=500>

<p>모든 노드 객체는 Object, EventTarget, Node 인터페이스를 상속 받음</p>
<ul>
<li>문서노드는 Document, HTMLDocument 인터페이스 상속</li>
<li>어트리뷰트는 Attr 인터페이스 상속</li>
<li>텍스트 노드는 CharacterData 인터페이스 상속</li>
<li>요소 노드는 Element 인터페이스 상속
또한 HTMLElement와 태그의 종류별로 세분화된 HTMLHtmlElement, HTMLHeadElement, HTMLBodyElement, HTMLUListElement 등의 인터페이스 상속</li>
</ul>
<br>

<p>프로토타입 체인 관점에서 보면,
input 요소를 파싱하여 객체화한 input 요소 노드 객체는 HTMLInputElement, HTMLElement, Element, Node, EventTarget, Object의 프로토타입에 바인딩되어 있는 프로토타입 객체 상속</p>
<p><span style="background-color:#FFEBCD">즉, input 요소 노드 객체는 프로토타입 체인에 있는 모든 프로토타입의 프로퍼티나 메서드를 상속 받아 사용 가능</span></p>
<p>배열이 객체인 동시에 배열인 것처럼,
input 요소 노드 객체도 다양한 특성을 갖는 객체
➡️ 특성을 나타내는 기능을 상속을 통해 제공받음</p>
<table>
<thead>
<tr>
<th>input 요소 노드 객체의 특성</th>
<th>프로토타입을 제공하는 객체</th>
</tr>
</thead>
<tbody><tr>
<td>객체</td>
<td>Object</td>
</tr>
<tr>
<td>이벤트를 발생시키는 객체</td>
<td>EventTarget</td>
</tr>
<tr>
<td>트리 자료구조의 노드 객체</td>
<td>Node</td>
</tr>
<tr>
<td>브라우저가 렌더링할 수 있는 웹 문서의 요소(HTML, XML, SVG)를 표현하는 객체</td>
<td>Element</td>
</tr>
<tr>
<td>웹 문서의 요소 중에서 HTML 요소를 표현하는 객체</td>
<td>HTMLElement</td>
</tr>
<tr>
<td>HTML 요소 중에서 input 요소를 표현하는 객체</td>
<td>HTMLInputElement</td>
</tr>
</tbody></table>
<br>

<p>노드 객체에는 </p>
<ul>
<li>노드 객체의 종류, 즉 노드 타입에 상관없이 모든 노드 객체가 공통으로 갖는 기능</li>
<li>노드 타입에 따라 고유한 기능</li>
</ul>
<p>도 있음</p>
<p>예를 들어,
모든 객체는 공통적으로 이벤트를 발생시킬 수 있음
➡️ 이벤트에 관련된 기능(EventTarget.addEventListener, EventTarget.removeEventListener 등)은 EventTarget 인터페이스가 제공됨</p>
<p>모든 노드 객체는, 트리 자료구조의 노드로서 공통적으로</p>
<ul>
<li>트리 탐색 기능
(Node.parentNode, Node.childNodes, Node.previousSibling, Node.nextSibling 등)</li>
<li>노드 정보 제공 기능(Node.nodeType, Node.nodeName 등)</li>
</ul>
<p>필요
➡️ Node 인터페이스가 위와 같은 기능 제공</p>
<p>HTML 요소가 객체화된 요소 노드 객체는 HTML 요소가 갖는 공통적인 기능이 있음
ex) input 요소 노드 객체와, div 요소 노드 객체는 모두 HTML 요소의 스타일을 나타내는 style 프로퍼티가 있음
➡️ HTML 요소가 갖는 공통적인 기능은 HTMLElement 인터페이스 제공</p>
<p>또한, 요소 노드 객체는 HTML 요소의 종류에 따라 고유한 기능도 있음
ex) input 요소 노드 객체는 value 프로퍼티가 필요하지만,
div 요소 노드 객체는 value 프로퍼티가 필요하지 않음
➡️ 필요한 기능을 제공하는 인터페이스(HTMLInputElement, HTMLDivElement 등)가 HTML 요소의 종류에 따라 각각 다름</p>
<p><span style="background-color:#FDF5E6">노드 객체는 공통된 기능일수록 프로토타입 체인의 상위에,
개별적인 고유 기능일수록 프로토타입 체인의 하위에 프로토타입 체인을 구축하여,
노드 객체에 필요한 기능, 즉 프로퍼티와 메서드를 제공하는 상속 구조를 가짐</span></p>
<p>DOM은 HTML 문서의 계층적 구조와 정보를 표현하는 것뿐만 아니라,
<span style="background-color:#FFEBCD">노드 객체의 종류, 즉 노드 타입에 따라 필요한 기능을 프로퍼티와 메서드의 집합인 DOM API로 제공</span>
➡️ <span style="background-color:#FFC0CB">이 DOM API를 통해 HTML의 구조나 내용, 또는 스타일 등을 동적으로 조작 가능</span>
(상속을 통해 사용 가능)</p>
<h1 id="📌-요소-노드-취득">📌 요소 노드 취득</h1>
<p><span style="background-color:#FFEBCD">HTML의 구조나 내용 또는 스타일 등을 동적으로 조작하려면, 
먼저 요소 노드를 취득해야 함</span></p>
<p>텍스트 노드는 요소 노드의 자식 노드이고,
어트리뷰트는 요소 노드와 연결되어 있기 때문에,
텍스트 노드와 오트리뷰트 노드를 조작할 때는 요소 노드에 먼저 접근해야 함</p>
<p>HTML 문서 내의 h1 요소의 텍스트를 변경하고 싶은 경우,
DOM트리 내에 존재하는 h1 요소 노드를 취득하고,
취득한 요소 노드의 자식 노드인 텍스트 노드를 변경하면
h1 요소의 텍스트가 변경됨</p>
<h2 id="☑️-id를-이용한-요소-노드-취득">☑️ id를 이용한 요소 노드 취득</h2>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;body&gt;
    &lt;ul&gt;
      &lt;li id=&quot;apple&quot;&gt;Apple&lt;/li&gt;
      &lt;li id=&quot;banana&quot;&gt;Banana&lt;/li&gt;
      &lt;li id=&quot;orange&quot;&gt;Orange&lt;/li&gt;
    &lt;/ul&gt;
    &lt;script&gt;
      // id값이 &#39;banana&#39;인 요소 노드를 탐색해서 반환
      // 두 번째 li 요소가 파싱되어 생성된 요소 노드가 반환
      const $elem = document.getElementById(&#39;banana&#39;);

      // 취득한 요소 노드의 style.color 프로퍼티 값 변경
      $elem.style.color = &#39;red&#39;;
    &lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>getElementById는 인수로 전달한 id값을 갖는 하나의 요소 노드를 탐색하여 반환
반드시 문서 노드인 document를 통해 호출해야</p>
<br>

<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;body&gt;
    &lt;ul&gt;
      &lt;li id=&quot;banana&quot;&gt;Apple&lt;/li&gt;
      &lt;li id=&quot;banana&quot;&gt;Banana&lt;/li&gt;
      &lt;li id=&quot;banana&quot;&gt;Orange&lt;/li&gt;
    &lt;/ul&gt;
    &lt;script&gt;
      // id값이 &#39;banana&#39;인 요소 노드를 탐색해서 반환
      // 첫 번째 li 요소가 파싱되어 생성된 요소가 반환
      const $elem = document.getElementById(&#39;banana&#39;);

      // 취득한 요소 노드의 style.color 프로퍼티 값 변경
      $elem.style.color = &#39;red&#39;;
    &lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>id는 HTML 문서 내에서 유일한 값이어야
공백 문자로 구분하여 여러 개의 값을 가질 수 없음
중복된 id값을 갖는 HTML 요소가 여러 개 존재하더라도 에러는 발생하지 않고,
인수로 전달된 id 값을 갖는 첫 번째 요소 노드만 반환됨</p>
<br>

<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;body&gt;
    &lt;ul&gt;
      &lt;li id=&quot;apple&quot;&gt;Apple&lt;/li&gt;
      &lt;li id=&quot;banana&quot;&gt;Banana&lt;/li&gt;
      &lt;li id=&quot;orange&quot;&gt;Orange&lt;/li&gt;
    &lt;/ul&gt;
    &lt;script&gt;
      // id 값이 &#39;grape&#39;인 요소 노드를 탐색하여 반환
      // ➡️ null이 반환
      const $elem = document.getElementById(&#39;grape&#39;);

      // 취득한 요소 노드의 style.color 프로퍼티 값 변경
      $elem.style.color = &#39;red&#39;;
      // ➡️ TypeError
    &lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>인수로 전달된 id 값을 갖는 HTML 요소가 존재하지 않는 경우
getElementById 메서드는 null을 반환</p>
<br>

<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;body&gt;
    &lt;div id=&quot;foo&quot;&gt;&lt;/div&gt;
    &lt;script&gt;
      // HTML 요소에 id 어트리뷰트를 부여하면,
      // id 값과 동일한 이름의 전역 변수가 암묵적으로 선언되고,
      // 해당 노드 객체가 할당됨
      console.log(foo === document.getElementById(&#39;foo&#39;)); //true

      // 암묵적 전역으로 생성된 전역 프로퍼티는 삭제되지만, 전역 변수는 삭제되지 않음
      delete foo;
      console.log(foo); // &lt;div id=&quot;foo&quot;&gt;&lt;/div&gt;
    &lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<br>

<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;body&gt;
    &lt;div id=&quot;foo&quot;&gt;&lt;/div&gt;
    &lt;script&gt;
      let foo = 1;

      // id 값과 동일한 이름의 전역 변수가 이미 선언되어 있으면 노드 객체가 재할당되지 않음
      console.log(foo); // 1
    &lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<h2 id="☑️-태그-이름을-이용한-요소-노드-취득">☑️ 태그 이름을 이용한 요소 노드 취득</h2>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;body&gt;
    &lt;ul&gt;
      &lt;li id=&quot;apple&quot;&gt;Apple&lt;/li&gt;
      &lt;li id=&quot;banana&quot;&gt;Banana&lt;/li&gt;
      &lt;li id=&quot;orange&quot;&gt;Orange&lt;/li&gt;
    &lt;/ul&gt;
    &lt;script&gt;
      // 태그 이름이 li인 요소 노드를 모두 탐색하여 반환
      // 탐색된 요소 노드는 HTMLCollection 객체에 담겨 반환
      // HTMLCollection 객체는 유사 배열 객체이면서 이터러블
      const $elem = document.getElementByTagName(&#39;li&#39;);

      // 취득한 모든 요소 노드의 style.color 프로퍼티 값 변경
      [... $elems].forEach(elem =&gt; { elem.style.color = &#39;red&#39;; });
    &lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<br>

<pre><code class="language-js">// 모든 요소 노드를 탐색하여 반환
const $elem = document.getElementByTagName(&#39;*&#39;);</code></pre>
<p>HTML 문서의 모든 요소 노드 취득</p>
<br>

<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;body&gt;
    &lt;ul id=&quot;fruits&quot;&gt;
      &lt;li&gt;Apple&lt;/li&gt;
      &lt;li&gt;Banana&lt;/li&gt;
      &lt;li&gt;Orange&lt;/li&gt;
    &lt;/ul&gt;
    &lt;script&gt;
      // DOM 전체에서 태그 이름이 li인 요소 노드를 모두 탐색하여 반환
      const $lisFromDocument = document.getElementsByTagName(&#39;li&#39;);
      console.log($lisFromDocument); // HTMLCollection(4) [li, li, li, li]

      // ul#fruit 요소의 자손 노드 중에서 태그 이름이 li인 요소 노드를 모두 탐색하여 반환
      const $fruits = document.getElementById(&#39;fruits&#39;);
      const $lisFromFruits = $fruits.getElementsByTagName(&#39;li&#39;);
      console.log($lisFromFruits); // HTMLCollection(3) [li, li, li]
    &lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<h2 id="☑️-class를-이용한-요소-노드-취득">☑️ class를 이용한 요소 노드 취득</h2>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;body&gt;
    &lt;ul id=&quot;fruits&quot;&gt;
      &lt;li class=&quot;fruit apple&quot;&gt;Apple&lt;/li&gt;
      &lt;li class=&quot;fruit banana&quot;&gt;Banana&lt;/li&gt;
      &lt;li class=&quot;fruit orange&quot;&gt;Orange&lt;/li&gt;
    &lt;/ul&gt;
    &lt;script&gt;
      // class 값이 &#39;fruit&#39;인 요소 노드를 모두 탐색하여 HTMLCollection 객체에 담아 반환
      const $elems = document.getElementsByClassNAme(&#39;fruit&#39;);

      // 취득한 모든 요소의 CSS color 프로퍼티 값 변경
      [... $elems].forEach(elem =&gt; { elem.style.color = &#39;red&#39;; });

      // class 값이 &#39;fruit apple&#39;인 요소 노드를 모두 탐색하여 HTMLCollection 객체에 담아 반환
      const $apples = document.getElementsByClassName(&#39;fruit apple&#39;);

      // 취득한 모든 요소 노드의 style.color 프로퍼티 값 변경
      [... $apples].forEach(elem =&gt; { elem.style.color = &#39;blue&#39;; });
    &lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<br>

<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;body&gt;
    &lt;ul id=&quot;fruits&quot;&gt;
      &lt;li class=&quot;apple&quot;&gt;Apple&lt;/li&gt;
      &lt;li class=&quot;banana&quot;&gt;Banana&lt;/li&gt;
      &lt;li class=&quot;orange&quot;&gt;Orange&lt;/li&gt;
    &lt;/ul&gt;
    &lt;div class=&quot;banana&quot;&gt;Banana&lt;/div&gt;
    &lt;script&gt;
      // DOM 전체에서 class 값이 &#39;banana&#39;인 요소 노드를 모두 탐색하여 반환
      const $bananasFromDocument = document.getElementsByClassName(&#39;banana&#39;);
      console.log($bananasFromDocument);
      // HTMLCollection(2) [li.banana, div.banana]

      // #fruits 요소의 자손 노드 중에서 class 값이 &#39;banana&#39;인 요소 노드를 모두 탐색하여 반환
      const $fruits = document.getElementById(&#39;fruits&#39;);
      const $bananasFromFruits = $fruits.getElementsByClassName(&#39;banana&#39;);

      console.log($bananasFromFruits); // HTMLCollection [li.banana]
    &lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<h2 id="☑️-css-선택자를-이용한-요소-노드-취득">☑️ CSS 선택자를 이용한 요소 노드 취득</h2>
<h3 id="queryselector">querySelector</h3>
<ul>
<li>인수로 전달한 CSS 선택자를 만족시키는 요소 노드가 여러 개인 경우 첫 번째 요소 노드만 반환</li>
<li>인수로 전달된 CSS 선택자를 만족시키는 요소 노드가 존재하지 않는 경우 null을 반환</li>
<li>인수로 전달한 CSS 선택자가 문법에 맞지 않는 경우 DOMException 에러 발생</li>
</ul>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;body&gt;
    &lt;ul&gt;
      &lt;li class=&quot;apple&quot;&gt;Apple&lt;/li&gt;
      &lt;li class=&quot;banana&quot;&gt;Banana&lt;/li&gt;
      &lt;li class=&quot;orange&quot;&gt;Orange&lt;/li&gt;
    &lt;/ul&gt;
    &lt;script&gt;
      // class 어트리뷰트 값이 &#39;banana&#39;인 첫 번째 요소 노드를 탐색하여 반환
      const $elem = document.querySelector(&#39;.banana&#39;);

      // 취득 요소 노드의 style.color 프로퍼티 값 변경
      $elem.style.color = &#39;red&#39;;
    &lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<h3 id="queryselectorall">querySelectorAll</h3>
<p>인수로 전달한 CSS 선택자를 만족시키는 모든 요소 노드를 탐색하여 반환
NodeList 객체로 반환</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;body&gt;
    &lt;ul&gt;
      &lt;li class=&quot;apple&quot;&gt;Apple&lt;/li&gt;
      &lt;li class=&quot;banana&quot;&gt;Banana&lt;/li&gt;
      &lt;li class=&quot;orange&quot;&gt;Orange&lt;/li&gt;
    &lt;/ul&gt;
    &lt;script&gt;
      // ul 요소의 자식 요소인 li 요소를 모두 탐색하여 반환
      const $elem = document.querySelectorAll(&#39;ul &gt; li&#39;);
      // 취득한 요소 노드들은 NodeList 객체에 담겨 반환
      console.log($elems); // NodeList(3) [li.apple, li.banana, li.orange]

      // 취득한 모든 요소 노드의 style.color 프로퍼티 값을 변경
      // NodeList는 forEach 메서드 제공
      $elems.forEach(elem =&gt; { elem.style.color = &#39;red&#39;; });
    &lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>querySelector, querySelectorAll 메서드는,
getElementById, getElementsBy*** 메서드보다 다소 느림</p>
<p>하지만 CSS 선택자 문법을 사용하여 구체적인 조건으로
요소 노드를 취득할 수 있고,
일관된 방식으로 요소 노드를 취득할 수 있다는 장점이 있음</p>
<h2 id="☑️-특정-요소-노드를-취득할-수-있는지-확인">☑️ 특정 요소 노드를 취득할 수 있는지 확인</h2>
<p>Element.prototype.matches 메서드는 인수로 전달한 CSS 선택자를 통해 특정 요소 노드를 취득할 수 있는지 확인</p>
<p>이벤트 위임 시 유용하게 사용</p>
<h2 id="☑️-htmlcollection과-nodelist">☑️ HTMLCollection과 NodeList</h2>
<h3 id="htmlcollection">HTMLCollection</h3>
<p>노드 객체의 상태 변화를 실시간으로 반영하는 살아 있는 DOM 컬렉션 객체
유사 배열 객체이며 이터러블</p>
<h3 id="nodelist">NodeList</h3>
<p>실시간으로 노드 객체의 상태 변경을 반영하지는 않음</p>
<p>다만, childNodes 프로퍼티가 반환하는 NodeList 객체는
실시간으로 노드 객체의 상태 변경을 반영</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;body&gt;
    &lt;ul id=&quot;fruits&quot;&gt;
      &lt;li&gt;Apple&lt;/li&gt;
      &lt;li&gt;Banana&lt;/li&gt;
      &lt;li&gt;Orange&lt;/li&gt;
    &lt;/ul&gt;
    &lt;script&gt;
      const $fruits = document.getElementById(&#39;fruits&#39;);

      // childeNodes 프로퍼티는 NodeList 객체(live) 반환
      const { childNodes } = $fruits;

      ...
    &lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<p><span style="background-color:#FFC0CB">노드 객체의 상태 변경과 상관없이 안전하게 DOM 컬렉션을 사용하려면 
HTMLCollection이나 NodeList 객체를 배열로 변환하여 사용하는 것을 권장</span></p>
<h1 id="📌-노드-탐색">📌 노드 탐색</h1>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/7e7b6e96-7613-4778-8b60-fcdc8db64a66/image.png" width=500>

<p>노드 탐색 프로퍼티를 사용하여 형제 노드나 부모 노드를 탐색 가능</p>
<h2 id="☑️-자식-노드-탐색">☑️ 자식 노드 탐색</h2>
<table>
<thead>
<tr>
<th>프로퍼티</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>Node.prototyle.childNodes</td>
<td>자식 노드를 모두 탐색하여 DOM 컬렉션 객체인 NodeList에 담아 반환 <br> childNodes 프로퍼티가 반환한 NodeList에는 요소 노드뿐 아니라 텍스트 노드도 포함되어 있을 수 있음</td>
</tr>
<tr>
<td>Element.prototype.children</td>
<td>자식 노드 중에서 요소 노드만 모두 탐색하여 DOM 컬렉션 객체인 HTMLCollection에 담아 반환<br>children 프로퍼티가 반환한 HTMLCollection에는 텍스트 노드가 포함되지 않음</td>
</tr>
<tr>
<td>Node.prototype.firstChild</td>
<td>첫 번째 자식 노드를 반환<br>firstChild 프로퍼티가 반환한 노드는 텍스트 노드이거나 요소 노드</td>
</tr>
<tr>
<td>Node.prototype.lastChild</td>
<td>마지막 자식 노드를 반환<br>lastChild 프로퍼티가 반환한 노드는 텍스트 노드이거나 요소노드</td>
</tr>
<tr>
<td>Element.prototype.firstElementChild</td>
<td>첫 번째 자식 요소 노드를 반환<br>firstElementChild 프로퍼티는 요소 노드만 반환</td>
</tr>
<tr>
<td>Element.prototype.lastElementChild</td>
<td>마지막 자식 요소 노드를 반환<br>lastElementChild 프로퍼티는 요소 노드만 반환</td>
</tr>
</tbody></table>
<h2 id="☑️-자식-노드-확인">☑️ 자식 노드 확인</h2>
<p>Node.prototype.hasChildNodes 메서드 사용
자식 노드가 존재하면 true,
자식 노드가 존재하지 않으면 false</p>
<p>자식 노드 중에 텍스트 노드가 아닌 요소 노드가 존재하는지 확인하려면,
hasChildNodes 메서드 대신 children.length 또는 Element 인터페이스의 childElementCount 프로퍼티 사용</p>
<h2 id="☑️-부모-노드-탐색">☑️ 부모 노드 탐색</h2>
<p>Node.prototype.parentNode 프로퍼티 사용</p>
<h2 id="☑️-형제-노드-탐색">☑️ 형제 노드 탐색</h2>
<table>
<thead>
<tr>
<th>프로퍼티</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>Node.prototype.previousSibling</td>
<td>부모 노드가 같은 형제 노드 중에서 자신의 이전 형제 노드 탐색 후 반환<br>previousSibling 프로퍼티가 반환하는 형제 노드는 요소 노드뿐만 아니라 텍스트 노드일 수 있음</td>
</tr>
<tr>
<td>Node.prototype.nextSibling</td>
<td>부모 노드가 같은 형제 노드 중에서 자신의 다음 형제 노드 탐색 후 반환<br>nextSibling 프로퍼티가 반환하는 형제 노드는 요소 노드뿐만 아니라 텍스트 노드일 수 있음</td>
</tr>
<tr>
<td>Element.prototype.previousElementSibling</td>
<td>부모 노드가 같은 형제 요소 노드 중에서 자신의 이전 형제 노드를 탐색하여 반환 <br>previousElementSibling 프로퍼티는 요소 노드만 반환</td>
</tr>
<tr>
<td>Element.prototype.nextElementSibling</td>
<td>부모 노드가 같은 형제 요소 노드 중에서 자신의 다음 형제 노드를 탐색하여 반환 <br>nextElementSibling 프로퍼티는 요소 노드만 반환</td>
</tr>
</tbody></table>
<h2 id="☑️-노드-정보-취득">☑️ 노드 정보 취득</h2>
<table>
<thead>
<tr>
<th>프로퍼티</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>Node.prototype.nodeType</td>
<td>노드 객체의 종류, 즉 노드 타입을 나타내는 상수 반환 <br>노드 타입 상수는 Node에 정의되어 있음 <br> - Node.ELEMENT_NODE: 요소 노드 타입을 나타내는 상수 1 반환 <br> - Node.TEXT_NODE: 텍스트 노드 타입을 나타내는 상수 3 반환 <br> - Node.DOCUMENT_NODE: 문서 노드 타입을 나타내는 상수 9 반환</td>
</tr>
<tr>
<td>Node.prototype.nodeName</td>
<td>노드의 이름을 문자열로 반환 <br> - 요소 노드: 대문자 문자열로 태그 이름(&quot;UL&quot;, &quot;LI&quot; 등)을 반환 <br> - 텍스트 노드: 문자열 &quot;#text&quot;를 반환 <br> - 문서 노드: 문자열 &quot;#document&quot;를 반환</td>
</tr>
</tbody></table>
<h1 id="📌-요소-노드의-텍스트-조작">📌 요소 노드의 텍스트 조작</h1>
<h2 id="☑️-nodevalue">☑️ nodeValue</h2>
<p>setter와 getter가 모두 존재
➡️ 참조와 할당 모두 가능</p>
<p>단, 노드 객체의 값을 반환하기 때문에
텍스트 노드가 아닌 노드의 nodeValue 프로퍼티를 참조하면 null 반환</p>
<h2 id="☑️-textcontent">☑️ textContent</h2>
<p>setter와 getter가 모두 존재
➡️ 참조와 할당 모두 가능</p>
<p>텍스트 노드뿐 아니라 Element 노드에서도 잘 동작</p>
<p>textContent는 마크업을 해석하지 않고 “순수 텍스트”로 다룸
요소 내부에 다른 요소가 있어도 전부 텍스트로 평탄화해서 가져옴</p>
<h1 id="📌-dom-조작">📌 DOM 조작</h1>
<h2 id="☑️-innethtml">☑️ innetHTML</h2>
<p>요소 노드의 HTML 마크업을 취득하거나 변경
➡️ 요소 노드의 콘텐츠 영역 내에 포함된 모든 HTML 마크업을 문자열로 취득</p>
<p>이것으로 노드를 교체하거나, 삭제가 가능
구현이 간단하고 직관적</p>
<p>사용자로부터 입력받은 데이터를 그대로 innerHTML 프로퍼티에 할당하면
크로스 사이트 스크립팅 공격에 취약함</p>
<h2 id="☑️-insertadjacenthtml-메서드">☑️ insertAdjacentHTML 메서드</h2>
<p>기존 요소를 제거하지 않으면서 위치를 지정해 새로운 요소 삽입</p>
<h2 id="☑️-노드-생성과-추가">☑️ 노드 생성과 추가</h2>
<p>DOM은 ㄴ노드를 직접 생성/삽입/삭제/치환하는 메서드도 제공</p>
<h3 id="요소-노드-생성">요소 노드 생성</h3>
<p>Document.prototype.createElement(tagName) 메서드는 요소 노드를 생성하여 반환
➡️ 태그 이름을 나타내는 문자열을 인수로 전달</p>
<h3 id="텍스트-노드-생성">텍스트 노드 생성</h3>
<p>Document.prototype.createTextNode(text) 메서드는 텍스트 노드를 생성하여 반환</p>
<h3 id="텍스트-노드를-요소-노드의-자식-노드로-추가">텍스트 노드를 요소 노드의 자식 노드로 추가</h3>
<p>Node.prototype.appendChild(childNode) 메서드는 매개변수 childNode에게 인수로 전달한 노드를
appendChild 메서드를 호출한 노드의 마지막 자식 노드로 추가</p>
<h2 id="☑️-노드-이동">☑️ 노드 이동</h2>
<p>DOM에 이미 존재하는 노드를 pappendChild 또는 insertBefore 메서드를 사용하여 DOM에 다시 추가하면,
현재 위치에서 노드를 제거하고 새로운 위치에 노드를 추가
➡️ 즉 노드가 이동</p>
<h2 id="☑️-노드-복사">☑️ 노드 복사</h2>
<p>Node.prototype.cloneNode([deep: true | false]) 메서드는 노드의 사본을 생성하여 반환</p>
<h2 id="☑️-노드-교체">☑️ 노드 교체</h2>
<p>Node.prototype.replaceChild(newChild, ildChild) 메서드는 자신을 호출한 노드의 자식 노드를 다른 노드로 교체</p>
<h2 id="☑️">☑️</h2>
<p>Node.prototype.removeChild(child) 메서드는 child 매개변수에 인수로 전달한 노드를 DOM에서 삭제</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[모던 자바스크립트 38장. 브라우저의 렌더링 과정]]></title>
            <link>https://velog.io/@ann-algorithm/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-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/@ann-algorithm/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-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>Mon, 26 Jan 2026 13:38:49 GMT</pubDate>
            <description><![CDATA[<p>자바스크립트는 웹 브라우저 외에서
서버 사이드 애플리케이션 개발에서도 사용할 수 있는 범용 개발 언어</p>
<p>그러나 아직까지도 자바스크립트가 가장 많이 사용되는 분야는
<span style="background-color:#FDF5E6">웹 브라우저에서 동작하는 웹 페이지/애플리케이션의 클라이언트 사이드</span>임</p>
<p>대부분의 프로그래밍 언어는 운영체제나 가상 머신 위에서 실행되지만,
<span style="background-color:#FFEBCD">웹 애플리케이션의 클라이언트 사이드 자바스크립트는 브라우저에서 HTML, CSS와 함께 실행</span>
<span style="background-color:#FFE4E1">따라서, 브라우저 환경을 고려할 때 더 효율적인 클라이언트 사이드 자바스크립트 프로그래밍이 가능</span></p>
<blockquote>
<p>📝 <strong>파싱(parsing)</strong>
프로그래밍 언어의 문법에 맞게 작성된 텍스트 문서를 읽어 들여 실행하기 위해,
텍스트 문서의 문자열을 토큰으로 분해(어휘 분석)하고,
토큰에 문법적 의미와 구조를 반영하여, 트리 구조의 자료구조인 파스 트리(parse tree)를 생성하는 일련의 과정
일반적으로 파싱이 완료된 후에는 파스 트리를 기반으로 중간 언어인 바이트 코드를 생성하고 실행</p>
</blockquote>
<blockquote>
<p>📝 <strong>렌더링(rendering)</strong>
HTML, CSS, 자바스크립트로 작성된 문서를 파싱하여 브라우저에 시각적으로 출력하는 것</p>
</blockquote>
<h3 id="브라우저의-렌더링-과정">브라우저의 렌더링 과정</h3>
<ol>
<li>브라우저는 HTML, CSS, 자바스크립트, 이미지, 폰트 파일 등 렌더링에 필요한 리소스를 요청하고 서버로부터 응답을 받음</li>
<li>브라우저의 렌더링 엔진은 서버로부터 HTML과 CSS를 파싱하여 DOM과 CSSOM을 생성
이들을 결합하여 렌더 트리 생성</li>
<li>브라우저의 자바스크립트 엔진은 서버로부터 응담된 자바스크립트를 파싱하여 AST 생성,
바이트코드로 변환하여 실행
자바스크립트는 DOM API를 통해 DOM이나 CSSOM을 변경 가능
변경된 DOM과 CSSOM은 다시 렌더 트리로 결합</li>
<li>렌더 트리를 기반으로 HTML 요소의 레이아웃(위치와 크기)을 계산하고,
브라우저 화면에 HTML 요소를 페인팅</li>
</ol>
<h1 id="📌-요청과-응답">📌 요청과 응답</h1>
<p><strong>브라우저의 핵심 기능</strong>은 
<span style="background-color:#FFEBCD">필요한 리소스(HTML, CSS, 자바스크립트, 이미지, 폰트 등의 정적 파일 또는 서버가 동적으로 생성한 데이터)를
서버에 요청하고, 
서버로부터 응답 받아 브라우저에 시각적으로 렌더링</span>하는 것</p>
<p>서버에 요청을 전송하기 위해 브라우저는 주소창을 제공
이 브라우저 주소창에 <span style="background-color:#FFEBCD">URL을 입력하고, 엔터 키를 누르면, <strong>URL의 호스트 이름이 DNS를 통해 IP주소로 변환</strong>되고, 이 IP 주소를 갖는 서버에게 요청을 전송</span>함</p>
<p>예를 들어,
브라우저의 주소창에 <code>https://annmmww.com</code>을 입력하고, 엔터 키를 누르면
루트 요청(/, 스킴과 호스트만으로 구성된 URI에 의한 요청)이 <code>annmmww.com</code> 서버로 전송</p>
<p>루트 요청에는 명확히 리소스를 요청하는 내용이 없지만,
일반적으로 서버는 루트 요청에 대해 암묵적으로 index.html을 응답하도록 기본 설정
➡️ 즉, <code>https://annmmww.com</code>은 <code>https://annmmww.com/index.html</code>과 같은 요청</p>
<p>따라서 서버는 루트 요청에 대해,
서버의 루트 폴더에 존재하는 정적 파일 index.html을 클라이언트(브라우저)로 응답</p>
<p>index.html이 아닌 다른 정적 파일을 서버에 요청하려면, 
브라우저의 주소창에 <code>https://annmmww/assets/data/data.json</code>과 같이 
요청할 정적 파일의 경로(서버의 루트 폴더 기준)와 파일 이름을,
URI의 호스트 뒤에 path에 기술하여 서버에 요청
➡️ 서버는 루트 폴더의 assets/data 폴더 내에 있는 정적 파일 data.json을 응답</p>
<p><span style="background-color:#FDF5E6">반드시 브라우저의 주소창을 통해 서버에게 정적 파일만 요청할 수 있는 건 아님</span>
자바스크립트를 통해 동적으로 서버에 정적/동적 데이터를 요청 가능</p>
<p><span style="background-color:#E2D6FF">요청과 응답은 개발자 도구의 Network에서 확인 가능</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/b08b4fdb-09ac-4726-8ac4-76a3c7e7102f/image.png">

<p>위 그림을 보면 CSS, js파일, 이미지, 폰트 등등까지도 확인 가능</p>
<p><em>요청하지 않은 리소스인데?</em>
브라우저의 렌더링 엔진이 <u>HTML(index.html)을 파싱하는 도중에 외부 리소스를 로드하는 태그, 즉 CSS 파일을 로드하는 link 태그, 이미지 파일을 로드하는 img 태그, 자바스크립트를 로드하는 script태그 등을 만나면,</u> <span style="background-color:#FFC0CB">HTML의 파싱을 일시 중단하고, 해당 리소스 파일을 서버로 요청하기 때문</span></p>
<h1 id="📌-http-11과-http-20">📌 HTTP 1.1과 HTTP 2.0</h1>
<p>HTTP는 웹에서 브라우저와 서버가 통신하기 위한 프로토콜(규약)</p>
<h2 id="http-11">HTTP 1.1</h2>
<p>HTTP/1.1은 기본적으로 커넥션당 하나의 요청과 응답만 처리
즉, 여러 개의 요청을 한 번에 전송할 수 없고, 응답 또한 불가</p>
<p>따라서 HTML 문서 내에 포함된 여러 개의 리소스 요청, 즉 CSS 파일을 로드하는 link 태그, 이미지 파일을 로드하는 img 태그, 자바스크립트를 로드하는 script 태그 등에 의한 리소스 요청이 <span style="background-color:#FFC0CB">개별적으로 전송되고 응답 또한 개별적으로 전송</span></p>
<p>이처럼 리소스의 동시 전송이 불가하여,
요청할 리소스 개수에 비례하여 응답 시간도 증가</p>
<h2 id="http-20">HTTP 2.0</h2>
<p>커넥션당 여러 개의 요청과 응답,
즉 다중 요청/응답이 가능</p>
<p>따라서 여러 리소스의 동시 전송이 가능하므로,
HTTP/1.1에 비해 페이지 로드 속도가 약 50% 정도 빠르다고 알려져 있음</p>
<h1 id="📌-html-파싱과-dom-생성">📌 HTML 파싱과 DOM 생성</h1>
<p>브라우저의 요청에 의해 서버가 응답한 <strong>HTML 문서는 문자열로 이루어진 순수한 텍스트임</strong>
순수한 텍스트인 HTML 문서를 브라우저에 시각적인 픽셀로 렌더링하려면,
<span style="background-color:#FFEBCD">HTML 문서를 브라우저가 이해할 수 있는 자료구조(객체)로 변환하여 메모리에 저장해야 함</span></p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;link rel=&quot;stylesheet&quot; href=&quot;style.css&quot;&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;ul&gt;
      &lt;li id=&quot;apple&quot;&gt;Apple&lt;/li&gt;
      &lt;li id=&quot;banana&quot;&gt;Banana&lt;/li&gt;
      &lt;li id=&quot;orange&quot;&gt;Orange&lt;/li&gt;
    &lt;/ul&gt;
    &lt;script src=&quot;app.js&quot;&gt;&lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>브라우저의 렌더링 엔진은 위 응답받은 HTML 문서를 파싱하여,
<span style="background-color:#FFEBCD">브라우저가 이해할 수 있는 자료구조인 DOM을 생성</span></p>
<h1 id="📌-css-파싱과-cssom-생성">📌 CSS 파싱과 CSSOM 생성</h1>
<p>렌더링 엔진은 HTML을 처음부터 한 줄씩 순차적으로 파싱하여,
DOM을 생성해 나감</p>
<p>이처럼 렌더링 엔진은 DOM을 생성해 나가다가 CSS를 로드하는 link 태그나 style 태그를 만나면,
DOM 생성을 일시 중단</p>
<p>link 태그의 href 어트리뷰트에 지정된 CSS 파일을 서버에 요청하여
로드한 CSS 파일이나 style 태그 내의 CSS를 HTML과 동일한 파싱 과정을 거치며 해석하여,
(파싱 과정: 바이트 -&gt; 문자 -&gt; 토큰 -&gt; 노드 -&gt; CSSOM)
<strong>CSSOM을 생성</strong></p>
<p>이후 CSS 파싱을 완료하면,
HTML 파싱이 중단된 지점부터 다시 HTML을 파싱하여 DOM 생성 재개</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;link rel=&quot;stylesheet&quot; href=&quot;style.css&quot;&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;ul&gt;
      &lt;li id=&quot;apple&quot;&gt;Apple&lt;/li&gt;
      &lt;li id=&quot;banana&quot;&gt;Banana&lt;/li&gt;
      &lt;li id=&quot;orange&quot;&gt;Orange&lt;/li&gt;
    &lt;/ul&gt;
    &lt;script src=&quot;app.js&quot;&gt;&lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>위 index.html에는 CSS파일을 로드하는 link 태그가 있음
렌더링 엔진은 meta 태그까지 HTML을 순차적으로 해석한 다음,
link 태그를 만나면 DOM 생성을 일시 중단하고,
link 태그의 href 어트리뷰트에 지정된 CSS 파일을 서버에 요청하게 됨</p>
<p>CSS 파일이 응답되면,
CSSOM을 생성하는 것임</p>
<h1 id="📌-렌더-트리-생성">📌 렌더 트리 생성</h1>
<p>렌더링 엔진은 서버로부터 응담된 HTML과 CSS를 파싱하여 각각 DOM과 CSSOM 생성
그리고 <span style="background-color:#FFEBCD">DOM과 CSSOM은 렌더링을 위해 렌더 트리로 결합</span></p>
<p>렌더 트리는 렌더링을 위한 트리 구조의 자료구조
<span style="background-color:#FFC0CB">따라서, 브라우저 화면에 렌더링되지 않는 노드(meta 태그, script 태그 등)와,
CSS에 의해 비표시(display: none)되는 노드는 포함하지 않음</span>
다시 말해, <span style="background-color:#FFEBCD"> 렌더 트리는 브라우저 화면에 렌더링 되는 노드만으로 구성됨
</span></p>
<p>이후 완성된 렌더 트리는 각 HTML 요소의 레이아웃(위치와 크기)을 계산하는 데 사용되며 
브라우저 화면에 픽셀을 렌더링하는 페인팅 처리에 입력</p>
<p>위 브라우저의 렌더링 과정은 반복해서 실행 가능
예를 들어 아래와 같은 경우 반복해서 레이아웃 계산과 페인팅이 재차 실행됨</p>
<ul>
<li>자바스크립트에 의한 노드 추가 또는 삭제</li>
<li>브라우저 창의 리사이징에 의한 뷰포트 크기 변경</li>
<li>HTML 요소의 레이아웃(위치, 크기)에 변경을 발생시키는
width/height, margin, padding, border, display, position, top/right/bottom/left 등의 스타일 변경</li>
</ul>
<p><span style="background-color:#FDF5E6">레이아웃 계산과 페인팅을 다시 실행하는 리렌더링은 이용이 많이 드는, <u>즉 성능에 악영향을 주는 작업</u>임</span>
따라서 가급적 리렌더링이 빈번하게 발생하지 않도록 주의 필요</p>
<h1 id="📌-자바스크립트-파싱과-실행">📌 자바스크립트 파싱과 실행</h1>
<p>HTML 문서를 파싱한 결과물로서 생성된 <span style="background-color:#FFEBCD">DOM은 HTML 문서의 구조와 정보뿐만 아니라,
HTML 요소와 스타일 등을 변경할 수 있는 프로그래밍 인터페이스로서 DOM API 제공</span></p>
<p>즉, 자바스크립트 코드에서 DOM API를 사용하면,
이미 생성된 DOM을 동적으로 조작 가능</p>
<p>CSS 파싱 과정과 마찬가지로 렌더링 엔진은 HTML을 한 줄씩 순차적으로 파싱하며 DOM을 생성하다가,
자바스크립트 파일을 로드하는 scipt 태그나,
자바스크립트 코드를 콘텐츠로 담은 script 태그를 만나면
DOM 생성을 일시 중단</p>
<p>script 태그의 scr 어트리뷰트에 정의된 자바스크립트 파일을 서버에 요청하여,
로드한 자바스크립트 파일이나 scipt 태그 내의 자바스크립트 코드를 파싱하기 위해
자바스크립트 엔진에 제어권을 넘김</p>
<p>이후 자바스크립트 파싱과 실행이 종료되면,
렌더링 엔진으로 다시 제어권을 넘겨
HTML 파싱이 중단된 지점부터 다시 HTML 파싱을 시작하여, DOM 생성 재개</p>
<p>자바스크립트 파싱과 실행은 브라우저의 렌더링 엔진이 아닌 자바스크립트 엔진이 처리
자바스크립트 엔진은 자바스크립트 코드를 파싱하여,
CPU가 이해할 수 있는 저수준 언어로 변환하고 실행하는 역할</p>
<p>자바스크립트 엔진의 예시</p>
<ul>
<li>구글크롬과 Node.js의 V8</li>
<li>파이어폭스의 SpiderMonkey</li>
<li>사파리의 JavaScriptCore 등</li>
</ul>
<p>모든 자바스크립트 엔진은 ECMAScript 사양 준수</p>
<p>렌더링 엔진으로부터 제어권을 넘겨받은 자바스크립트 엔진은 자바스크립트 코드 파싱
렌더링 엔진이 HTML과 CSS를 파싱하여 DOM과 CSSOM을 생성하듯
자바스크립트 엔진은 자바스ㅡ립트를 해석하여 AST(Abstract Syntax Tree, 추상적 구문 트리) 생성</p>
<p>AST를 기반으로 인터프리터가 실행할 수 있는 중간코드인 바이트코드를 생성하여 실행</p>
<h2 id="토크나이징tokenizing">토크나이징(Tokenizing)</h2>
<p>단순한 문자열인 자바스크립트 소스코드를 어휘 분석하여 
문법적 의미를 갖는 코드의 최소 단위인 토큰으로 분해</p>
<h2 id="파싱parsing">파싱(Parsing)</h2>
<p>토큰들의 집합을 구문 분석하여 AST 생성
AST는 토큰에 문법적 의미와 구조를 반영한 트리 구조의 자료 구조</p>
<p><span style="background-color:#E6E6FA">AST를 사용하면 TypeScript, Babel, Prettier 같은 트랜스파일러를 구현 가능</span></p>
<h2 id="바이트코드-생성과-실행">바이트코드 생성과 실행</h2>
<p>파싱의 결과물로서 생성된 AST는 인터프리터가 실행할 수 있는 중간 코드인 바이트 코드로 변환되고,
인터프리터에 의해 실행됨</p>
<p><span style="background-color:#E6E6FA">V8 엔진의 경우 
자주 사용되는 코드는 터보팬이라 불리는 컴파일러에 의해 
최적화된 머신 코드로 컴파일되어 성능을 최적화</span></p>
<p><span style="background-color:#E6E6FA">코드의 사용 빈도가 적어지면 다시 디옵티마이징되기도...</span></p>
<h1 id="📌-리플로우와-리페인트">📌 리플로우와 리페인트</h1>
<p>만약 자바스크립트 코드에 DOM이나 CSSOM을 변경하는 DOM API가 사용된 경우 
DOM이나 CSSOM이 변경됨</p>
<p>변경된 DOM과 CSSOM은 다시 렌더 트리로 결합되고 
변경된 렌더 트리를 기반으로 레이아웃과 페인트 과정을 거쳐 브라우저 화면에 다시 렌더링</p>
<p>➡️ 이를 리플로우, 리페인트라고 함</p>
<blockquote>
<p><strong>리플로우</strong>
레이아웃 계산을 다시 하는 것
노드 추가/삭제, 요소의 크기/위치 변경, 윈도우 리사이징 등 레이아웃에 영향을 주는 변경이 발생한 경우에 한하여 실행</p>
</blockquote>
<blockquote>
<p><strong>리페인트</strong>
재결합된 렌더 트리를 기반으로 다시 페인트하는 것</p>
</blockquote>
<p>리플로우와 리페인트가 반드시 순차적으로 동시에 실행되는 것은 아님
레이아웃에 영향이 없는 변경은 리플로우 없이 리페인트만 실행</p>
<h1 id="📌-자바스크립트-파싱에-의한-html-파싱-중단">📌 자바스크립트 파싱에 의한 HTML 파싱 중단</h1>
<p>렌더링 엔진과 자바스크립트 엔진은 병렬적으로 파싱을 실행하지 않고,
직렬적으로 파싱을 수행</p>
<p><span style="background-color:#FFC0CB">브라우저는 동기적</span>으로,
위에서 아래 방향으로 순차적으로 HTML, CSS, 자바스크립트를 파싱하고 실행</p>
<p><span style="background-color:#FDF5E6">script 태그의 위치에 따라 HTML 파싱이 블로킹되어 DOM 생성이 지연될 수 있음</span></p>
<p>만약 DOM을 변경하는 DOM API를 사용할 때
DOM의 생성이 완료되지 않은 상태면 문제가 발생할 수 있음</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;link rel=&quot;stylesheet&quot; href=&quot;style.css&quot;&gt;
    &lt;script&gt;
      /*
      DOM API인 document.getElementById는 DOM에서 id가 &#39;apple&#39;인 HTML 요소를 취득
      아래 DOM API가 실행되는 시점에는 아직 id가 &#39;apple&#39;인 HTML 요소를 파싱하지 않았기 때문에
      DOM에는 id가 &#39;apple&#39;인 HTML 요소가 포함되어 있지 않음
      따라서 아래 코드는 정상적으로 id가 &#39;apple&#39;인 HTML 요소를 취득하지 못함
      */
      const $apple = document.getElementById(&#39;apple&#39;);

      // id가 &#39;apple&#39;인 HTML 요소의 CSS color 프로퍼티 값을 변경
      // 이때 DOM에는 id가 &#39;apple&#39;인 HTML 요소가 포함되어 있지 않기 때문에 에러가 발생
      $apple.style.color = &#39;red&#39;; // TypeError
    &lt;/script&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;ul&gt;
      &lt;li id=&quot;apple&quot;&gt;Apple&lt;/li&gt;
      &lt;li id=&quot;banana&quot;&gt;Banana&lt;/li&gt;
      &lt;li id=&quot;orange&quot;&gt;Orange&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>따라서 위 예제는 제대로 동작하지 않음</p>
<br>

<p><span style="background-color:#FDF5E6">따라서 body 요소 아래에 자바스크립트를 위치시키는 것이 좋음</span></p>
<ul>
<li>DOM이 완성되지 않은 상태에서 자바스크립트가 DOM을 조작하면 에러 발생할 수 있음</li>
<li>자바스크립트 로딩/파싱/실행으로 인해 
HTML 요소들의 렌더링에 지장받는 일이 발생하지 않아 페이지 로딩 시간이 단축됨</li>
</ul>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;link rel=&quot;stylesheet&quot; href=&quot;style.css&quot;&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;ul&gt;
      &lt;li id=&quot;apple&quot;&gt;Apple&lt;/li&gt;
      &lt;li id=&quot;banana&quot;&gt;Banana&lt;/li&gt;
      &lt;li id=&quot;orange&quot;&gt;Orange&lt;/li&gt;
    &lt;/ul&gt;
    &lt;script&gt;
      /*
      DOM API인 document.getElementById는 DOM에서 id가 &#39;apple&#39;인 HTML 요소를 취득
      아래 코드가 실행되는 시점에는 id가 &#39;apple&#39;인 HTML 요소의 파싱이 완료되어 DOM 요소에 포함
      ➡️ 정상적으로 동작
      */
      const $apple = document.getElementById(&#39;apple&#39;);

      // apple 요소의 css color 프로퍼티 값을 변경
      $apple.style.color = &#39;red&#39;;
    &lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>자바스크립트가 실행될 시점에는 이미 렌더링 엔진이 HTML요소를 모두 파싱하여 DOM 생성을 완료한 이후
➡️ DOM이 완성되지 않은 상태
따라서 자바스크립트가 DOM을 조작하는 에러가 발생할 우려가 없음</p>
<p><span style="background-color:#E2D6FF">또한 자바스크립트가 실행되기 전에 DOM 생성이 완료되어 렌더링되므로 페이지 로딩 시간이 단축되기도 함</span></p>
<h1 id="📌-script-태그의-asyncdefer-어트리뷰트">📌 script 태그의 async/defer 어트리뷰트</h1>
<p>자바스크립트 파싱에 의한 DOM 생성이 중단되는 문제를 근본적으로 해결하기 위해
HTML5부터, script 태그에 async와 defer 어트리뷰트가 추가됨</p>
<p><span style="background-color:#FFEBCD">async와 defer 어트리뷰트는 다음과 같이 src 어트리뷰트를 통해
외부 자바스크립트 파일을 로드하는 경우에만 사용 가능</span>
<span style="background-color:#FFC0CB">즉 src 어트리뷰트가 없는 인라인 자바스크립트에는 사용할 수 없음</span></p>
<pre><code class="language-html">&lt;script async src=&quot;extern.js&quot;&gt;&lt;/script&gt;
&lt;script defer src=&quot;extern.js&quot;&gt;&lt;/script&gt;</code></pre>
<p><span style="background-color:#FFEBCD">async와 defer 어트리뷰트를 사용하면
HTML 파싱과 외부 자바스크립트 파일의 로드가 비동기적으로 동시에 진행</span>
<span style="background-color:#E2D6FF">하지만 자바스크립트 실행 시점에 차이가 존재</span></p>
<h2 id="async-어트리뷰트">async 어트리뷰트</h2>
<p><span style="background-color:#FFEBCD">HTML 파싱과 외부 자바스크립트 파일의 로드가 비동기적으로 동시에 진행됨</span>
<span style="background-color:#FFC0CB">단, 자바스크립트의 파싱과 실행은 자바스크립트 파일의 로드가 완료된 직후 진행됨</span>
이때 HTML 파싱이 중단됨</p>
<p>여러 개의 script 태그에 async 어트리뷰트를 지정하면,
<span style="background-color:#FFE4E1">script 태그의 순서와는 상관없이 로드가 완료된 자바스크립트가 먼저 실행되어
순서가 보장되지 않음</span>
따라서, 순서 보장이 필요한 script 태그에는 async 어트리뷰트를 지정하지 않아야 함</p>
<h2 id="defer-어트리뷰트">defer 어트리뷰트</h2>
<p>async 어트리뷰트와 마찬가지로 <span style="background-color:#FFEBCD">HTML 파싱과 외부 자바스크립트 파일의 로드가 비동기적으로 동시에 진행</span>
<span style="background-color:#FFC0CB">단 자바스크립트의 파싱과 실행은 HTML 파싱이 완료된 직후,
즉 DOM 생성이 완료된 직후 진행</span></p>
<p><span style="background-color:#E2D6FF">따라서 DOM 생성이 완료된 이후 실행되어야 할 자바스크립트에 유용
</span></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[useReducer]]></title>
            <link>https://velog.io/@ann-algorithm/useReducer</link>
            <guid>https://velog.io/@ann-algorithm/useReducer</guid>
            <pubDate>Sun, 04 Jan 2026 14:47:57 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-usereducer를-소개합니다">📌 useReducer를 소개합니다</h1>
<blockquote>
<p>컴포넌트 내부에 새로운 state를 생성하는 리액트 훅
➡️ 모든 useState는 useReducer로 대체 가능</p>
</blockquote>
<p>차이점은,
<span style="background-color:#FFC0CB">상태 관리 코드를 컴포넌트 외부로 분리 가능</span>하다는 것</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/0dce4b50-c08a-4d29-b5b5-7341850bea1f/image.png">

<p><em>useState는 “현재 렌더링 중인 컴포넌트 인스턴스”에 상태를 붙이는데,
컴포넌트 밖(모듈 최상단 등)에서 상태 관리 코드를 쓰면 “어느 컴포넌트의 상태인지” 알 수 없기 때문</em></p>
<p><span style="background-color:#FDF5E6">따라서 useState를 통해서 state를 생성하면,
onCreate처럼 state를 관리하는 코드, 즉 상태를 관리하는 코드를 컴포넌트 내부에 작성해야 함</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/05e5e97c-8846-4707-a2da-55edb97ba939/image.png">

<p><span style="background-color:#FDF5E6">useReducer를 사용하면,
컴포넌트 내부에선 state 생성만 하고,
state 관리는 Reducer라는 함수를 통해 컴포넌트 외부에서 관리하도록 코드 분리</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/9e6d46c0-8aad-4486-b52a-a6c05ce647a4/image.png" width=400>

<p>이전에는 todo 데이터를 관리하는 state를 <strong>App 컴포넌트 내부에서 useState를 통해 만들었음</strong>
➡️ todos와 setTodos는 App 컴포넌트에서만 접근 가능
따라서, 이 state를 관리하는 코드 또한 반드시 App 컴포넌트 내부에만 작성이 되어야 했음</p>
<p><strong>문제점‼️</strong>
state에 새로운 값을 추가하는 onCreate, 특정 값을 수정하는 onUpdate, 특정 값을 제거하는 onDelete와 같은 todo state를 관리하는 코드들이 App 컴포넌트 내부에 작성되어야 했고,
➡️ 너무 길어지게 됨</p>
<p>여기서 더 복잡하고, 더 다양한 상태 변화를 제공하려면?
<u>컴포넌트 안에 더 긴 코드를 작성해야 함</u></p>
<p>하지만,
<span style="background-color:#FDF5E6">App 컴포넌트의 가장 주된 역할은 UI를 렌더링하는 것임</span>
이와 같이 state를 관리하는 코드가 너무 많아지면 주객전도
<span style="background-color:#FFE4E1">UI를 렌더링하는 코드보다 상태를 관리하는 코드가 훨씬 더 길고 복잡하기 때문</span>
➡️ 파일을 열었을 때 코드의 가독성이 떨어지고, 유지보수도 어려움</p>
<p><span style="background-color:#FFC0CB">UI를 렌더링하는 코드와 상태 관리 코드를 분리시키기 위해 useReducer라는 리액트 훅이 필요</span></p>
<h2 id="실습">실습</h2>
<h3 id="컴포넌트-생성">컴포넌트 생성</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/bfab33d4-235e-448d-ac22-30ef706a4940/image.png" width=300>

<p>테스트를 위한 컴포넌트 생성</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/78970301-0447-4331-ae3c-d971e07a86c5/image.png">

<ul>
<li>해당 컴포넌트 import</li>
<li>컴포넌트 렌더링</li>
</ul>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/d786fb30-6859-4d47-a714-6263be841213/image.png">

<br>

<p>이제 해당 컴포넌트에 간단한 카운터 기능 생성</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/a229cc20-7075-4d84-9036-a756def86649/image.png" width=300>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/0af66dc5-7afb-492b-bc14-74cd16804f4d/image.png" width=500>

<ul>
<li>useState와 동일하게, 새로운 state를 생성해서 배열의 첫 번째 요소로 반환</li>
<li>dispatch라고 불리는, 상태 변화를 요청하기만 하는 함수 반환
(dispatch: 발송하다
➡️ 상태 변화가 있어야 한다는 사실을 알리는(발송하는) 함수)</li>
</ul>
<p>이제 <span style="background-color:#FFE4E1">컴포넌트 내부에서 dispatch 함수를 호출하면,
상태 변화가 요청되고
그러면 useReducer가 상태 변화를 실제로 처리하게 될 함수를 호출하게 됨</span>
➡️ <span style="background-color:#FFC0CB">그 함수는 직접 만들어야 함</span></p>
<br>

<h4 id="reducer-함수">reducer 함수</h4>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/1f19143d-7638-495b-85d4-7efce195d5e4/image.png" width=500>

<p>컴포넌트 바깥에 
<span style="background-color:#FFC0CB"><strong>reducer라는 함수를 만들고,</strong>
useReducer에 이렇게 만든 reducer라는 함수를 인수로 넣음</span>(첫 번째 인수)</p>
<blockquote>
<p><strong>reducer</strong>
: 변환기
➡️ 상태를 실제로 변화시키는 변환기 역할</p>
</blockquote>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/156c190e-9ff3-4382-8e4c-6cd9f3e35a81/image.png" width=550>

<p>useReducer의 
첫 번째 인수는
위처럼 상태를 실제로 변화시키는 변환기 역할을 하는 reducer라는 함수를 만들어 넣고,
<span style="background-color:#FFC0CB">두 번째 인수로는 state의 초기값 전달</span></p>
<p>따라서,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/586734b3-178e-4f9b-a0b2-21a41fbb6b6e/image.png" width=500>

<p>컴포넌트의 최종적인 useReducer 호출은 위와 같음</p>
<h3 id="컴포넌트-기능-추가">컴포넌트 기능 추가</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/862b14aa-ac70-405b-8fb9-4795b5545454/image.png">


<p>이제 버튼을 클릭하면 state값이 1씩 증가하게 해야 함</p>
<blockquote>
<p>컴포넌트 내부에서, 버튼이 클릭되었을 때
<strong>dispatch 함수를 호출해서 상태 변화를 발송, 즉 요청하게 됨</strong></p>
</blockquote>
<br>

<h4 id="dispatch">dispatch</h4>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/5359c591-6d4c-4281-a745-016ce49cabb8/image.png" width=500>

<p>컴포넌트 안에 onClickPlus라는 함수를 만들어서
버튼의 이벤트 핸들러로 설정</p>
<p>그리고 <span style="background-color:#FFC0CB">이 함수 내부에서 dispatch를 호출해서 상태 변화 요청</span>
➡️ <span style="background-color:#FFE4E1">인수로는 상태가 어떻게 변화되길 원하는지 그 정보를 전달할 것</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/d17459e5-c6bd-4e6a-bf7c-7ed32dd22068/image.png" width=400>

<p>보통 dispatch 안에는 객체 형태의 인수</p>
<ul>
<li>type : 상태를 어떻게 변화시킬지
문자열로 적음</li>
<li>data: 증가시킬 수</li>
</ul>
<p>➡️ + 버튼을 누르면 state값을 <strong>1 증가</strong>시키길 원함</p>
<pre><code>dispatch로 상태 변화 요청 시
요청 내용: 객체(&quot;값을 1만큼 증가시켜줘&quot;)</code></pre><p>이렇게 <span style="background-color:#FFEBCD">인수로 전달되는, 요청의 내용을 담고 있는 객체</span>를
<strong>Action 객체</strong>라고 함</p>
<blockquote>
<p>Action 객체를 인수로 전달하면서, dispatch 함수를 호출하면
useReducer가 요청을 처리하기 위해
실제로 상태를 변화시키는 reducer 함수 호출</p>
</blockquote>
<br>

<h4 id="reducer-함수-완성">reducer 함수 완성</h4>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/1e9b4326-d940-4fec-acc4-46feb233772f/image.png" width=400>

<p>reducer 함수의 인수</p>
<ul>
<li>현재의 state 값</li>
<li>Action 객체</li>
</ul>
<p><span style="background-color:#FFC0CB">따라서 매개변수로 받은 state 값과, 액션 객체를 이용해 실제 state 값 변경</span></p>
<blockquote>
<p>🤔 <strong>상태 변화 함수도 없는데(to~), 어떻게 state를 변경할 수 있는가?</strong>
reducer 함수에서 새로운 state를 반환하기만 하면 됨
그 반환된 값을 useReducer가 불러와서 실제 state 값을 변경시킴</p>
</blockquote>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/fcb00d5f-8998-4d3a-abd3-9d1d86cacc86/image.png" width=500>

<p>매개변수 action의 type이 &quot;INCREASE&quot;면,
현재 상태 state에 action의 data(<code>1</code>)를 더한 값 반환</p>
<h4 id="새로운-상태-변화---버튼">새로운 상태 변화 [<code>-</code>] 버튼</h4>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/f7cd0ea8-8961-4c0f-a591-2894f1fc804d/image.png" width=500>

<p>이벤트 핸들러 내 dispatch 안에 객체 전달</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/ba2ed2d1-d03d-4b40-a5a4-2a1a5112212d/image.png" width=500>

<p>reducer 함수에 &quot;DECREASE&quot; 분기 추가
현재 상태 state에 action의 data(<code>1</code>)를 뺀 값 반환</p>
<h4 id="분기가-많아지면">분기가 많아지면,</h4>
<p>reducer 함수 안에 action의 type이 너무 많아질 것 같으면</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/c984a495-cd3e-406c-aac6-f1a67dda45f0/image.png" width=500>

<p>switch문으로 작성하는 게 일반적</p>
<hr>
<h1 id="📌-투두리스트-업그레이드">📌 투두리스트 업그레이드</h1>
<h2 id="app-컴포넌트">App 컴포넌트</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/07572193-6851-4c49-89e3-f7407fc69163/image.png" width=500>

<ul>
<li>useState를 지우고,</li>
<li>useReducer 훅 호출<ul>
<li>reducer의 첫 번째 인수로 전달</li>
<li>mock 데이터를 두 번째 인수로 전달</li>
</ul>
</li>
</ul>
<blockquote>
<p>onCreate, onUpdate, onDelete와 같은 상태변화 함수를 reducer 함수로 옮긴 후,
dispatch 함수만 호출하도록 수정</p>
</blockquote>
<h2 id="oncreate">onCreate</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/1c92fa47-bdc2-4cc6-aee6-4da6a154e45a/image.png">

<ul>
<li>추가 버튼을 클릭하면,</li>
<li>코드 상에 이 onCreate 함수가 실행</li>
<li>dispatch 함수가 호출</li>
<li>그 결과로 reducer 함수가 호출될 것</li>
</ul>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/218bd926-e8ef-4e0b-9472-c69284e902b0/image.png" width=400>

<ul>
<li>reducer 함수에서는 현재 state값과 action 객체를 받아와,</li>
<li>이 action 객체의 값에 따라 변화된 state 값 리턴</li>
<li>todo 아이템을 새로 생성</li>
<li>새로운 state의 값으로 새로운 배열 반환<ul>
<li>첫 번째 요소에는 새롭게 추가될 데이터인 action.data를 넣고,</li>
<li>그 뒤에 state로 기존의 state 값 펼쳐주기
➡️ 이 action.data는 onCreate 함수에서 dispatch 함수를 호출하면서, 전달한 새로운 todo 아이템</li>
</ul>
</li>
</ul>
<h2 id="onupdate">onUpdate</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/89e058ea-25d0-4a09-97e9-4322336fcdba/image.png">

<ul>
<li>type: UPDATE</li>
<li>data 대신 targeId라는 프로퍼티로, 어떤 요소를 수정할 것인지 명시</li>
</ul>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/bbc2f6e2-141d-4f44-88a8-840cfe3bb247/image.png" width=700>

<ul>
<li>map을 이용해서</li>
<li>현재 state 안에 있는 id 중에 action의 targetId와 일치하는 요소 있는지 확인<ul>
<li>있다면, <code>...item</code>으로 기존의 todo 아이템의 값을 나열하고, 해당 item의 isDone 반전</li>
<li>없다면, 그냥 현재 값 리턴</li>
</ul>
</li>
</ul>
<h2 id="ondelete">onDelete</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/54a3ace1-788f-4812-8092-698fc85feae7/image.png">

<ul>
<li>type: DELETE</li>
<li>data 대신 targeId라는 프로퍼티로, 어떤 요소를 삭제할 것인지 명시</li>
</ul>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/5c41bf6a-044e-40da-bfa3-360968f56647/image.png" width=700>

<ul>
<li>filter를 이용해서</li>
<li>아이템의 id가 action의 targetId와 같지 않은 요소만 필터링 하도록 만ㄷ름</li>
<li>디폴트 추가</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[라이프사이클]]></title>
            <link>https://velog.io/@ann-algorithm/%EB%9D%BC%EC%9D%B4%ED%94%84%EC%82%AC%EC%9D%B4%ED%81%B4</link>
            <guid>https://velog.io/@ann-algorithm/%EB%9D%BC%EC%9D%B4%ED%94%84%EC%82%AC%EC%9D%B4%ED%81%B4</guid>
            <pubDate>Fri, 02 Jan 2026 17:09:03 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%EB%A6%AC%EC%95%A1%ED%8A%B8/dashboard">한 입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지 by 이정환
</a></p>
<h1 id="📌-라이프사이클이란">📌 라이프사이클이란?</h1>
<p>인간에게는 영유아 ~ 아동기 ~ 청소년기 ~ 청년기 ~ 성인기 ~ 노년기가 있음
이러한 생애주기처럼
어떠한 것이 탄생부터 죽음에 이르기까지의 단계를 나누어 놓은 것을 <strong>라이프사이클</strong>이라고 함</p>
<p>리액트 컴포넌트도 이러한 생애주기가 있음
인간과 단계가 다를 뿐임</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/bd1be539-a3d0-4584-9529-d24c4b57c4a6/image.png">

<h2 id="mount마운트">Mount(마운트)</h2>
<blockquote>
<p>컴포넌트가 처음 <strong>탄생</strong>하는 순간
즉, 화면에 처음 렌더링되는 순간</p>
</blockquote>
<p>A 컴포넌트가 Mount되었다,
A 컴포넌트가 화면에 처음 렌더링되었다,
...</p>
<h2 id="update업데이트">Update(업데이트)</h2>
<blockquote>
<p>마운트 이후 컴포넌트가 다시 렌더링되는 순간
즉, 리렌더링 될 때를 의미</p>
</blockquote>
<p>A 컴포넌트가 업데이트 되었다,
A 컴포넌트가 리렌더링 되었다,
...</p>
<h2 id="unmount언마운트">UnMount(언마운트)</h2>
<blockquote>
<p>컴포넌트가 화면에서 사라지는 순간
즉, 렌더링에서 제외되는 순간</p>
</blockquote>
<p>A 컴포넌트가 언마운트 되었다,
A 컴포넌트가 화면에서 사라졌다,</p>
<br>

<p>이 라이프사이클을 이해한다면,
원하는 타이밍에 컴포넌트에 원하는 작업을 실행하도록 할 수 있음</p>
<p>예를 들면,
컴포넌트가 화면에 처음 나타났을 때 즉, 마운트 되었을 때
백엔드 서버에 요청을 보내서 데이터를 불러오는 기능을 만들 거나</p>
<p>컴포넌트가 리렌더링 될 때, 변경된 값이 무엇인지 콘솔에 출력하는 기능을 만들 거나</p>
<p>컴포넌트가 언마운트 되어서 화면에서 사라질 때
컴포넌트가 사용하던 여러 가지 유형의 메모리를 정리하도록 할 수 있음</p>
<br>

<p>이렇게 컴포넌트의 라이프 사이클의 단계별로
<strong>컴포넌트들이 각각 다른 작업을 수행하도록 만드는 것</strong>을 <span style="background-color:#FFEBCD">라이프사이클 제어</span>라고 함</p>
<p>그리고 <strong>useEffect</strong>를 사용하면, 이 라이프 사이클 제어를 쉽게 구현 가능</p>
<hr>
<h1 id="📌-useeffect-사용하기">📌 useEffect 사용하기</h1>
<h2 id="사이드-이펙트란">사이드 이펙트란?</h2>
<blockquote>
<p>부수적인 효과, 파생되는 효과로 해석 가능</p>
</blockquote>
<p>과식하면 살이 찐다.
➡️ 과식을 했기 때문에 발생하는 파생적인 효과
(살이 찐다 = 사이드 이펙트)</p>
<p>어떠한 동작에 따라 파생되는 효과를 사이드 이펙트라고 함</p>
<h2 id="useeffect란">useEffect란?</h2>
<blockquote>
<p>리액트 컴포넌트의 사이드 이펙트를 제어하는 리액트 훅</p>
</blockquote>
<p>즉, 컴포넌트의 동작에 따라 파생되는 여러 효과</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/17e6ccc6-fc44-4085-b9e4-2105f6500dde/image.png" width=500>

<p>예를 들면, 왼쪽의 동작을 통해, 오른쪽의 사이드 이펙트가 발생하게 제어 하려면,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/5a234155-99cd-459a-8385-d184da0a006d/image.png" width=500>

<p>useEffect를 사용할 수 있음</p>
<h2 id="실습">실습</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/20be65d5-b7b0-4dd5-bb25-8b25cc7e915c/image.png" width=300>

<ul>
<li>컴포넌트 안에서 useEffect를 호출하고,<ul>
<li>첫 번째 인수로는 콜백함수</li>
<li>두 번째 인수로는 배열</li>
</ul>
</li>
</ul>
<p>➡️ useEffect는 <span style="background-color:#FFC0CB">두 번째 인수로 전달한 배열의 값이 바뀌면,</span>
사이드이펙트로 <span style="background-color:#FFC0CB">첫 번째 인수로 전달한 콜백함수 실행</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/fb5d11f8-aac7-4bec-abb1-0428af537d6e/image.png" width=350>

<p>따라서 count(state값)를 넣어주면
useEffect는 count의 값이 바뀔 때마다
첫 번째 인수로 전달한 콜백 함수를 실행하게 됨</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/46b35ee1-1981-4847-89c8-972c21e4122e/image.png" width=400>

<p>콜백함수를 채워줌</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/d6b2d7d1-bf30-40c5-8f19-0718a61eabbd/image.png">

<ul>
<li>컴포넌트가 렌더링 될 때 최초로 count 값이 초기화되기 때문에
<code>count: 0</code> 출력
(즉, <span style="background-color:#FDF5E6">state값을 초기화 할 때도 useEffect가 변화 감지</span>)</li>
<li>버튼을 클릭해서, count 값을 변경하게 되면, 그때마다 변경된 count 값이 콘솔에 출력</li>
</ul>
<p><span style="background-color:#FFC0CB">useEffect에 의해서 count값이 변경될 때마다 콘솔에 로그가 출력됨</span>
➡️ <span style="background-color:#FFEBCD">useEffect란 훅은, 두 번째 인수로 전달한 배열에 의존
</span>
이 배열을 <strong>의존성 배열</strong>이라고 함
혹은 <strong>dependency array</strong>
혹은 <strong>deps</strong></p>
<p>이 deps에는 값을 여러 개 넣어도 됨</p>
<br>

<p>deps에 여러 값을 넣어보기 위해 새로운 state를 만들어봄</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/08a07f83-0ae1-4ff7-b647-009b6a696a82/image.png" width=600>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/33fc6a59-a3ec-4a77-8b7f-5988cf4b315b/image.png" width=400>

<p>임시 폼을 만들고
해당 폼에 입력 시 새로운 state를 업데이트하기 위해,
onChange 임시로 콜백함수를 만듦
(매개변수를 받아오고, 해당 매개변수 값 출력)</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/ad919a9b-c162-4405-96fc-7cba97710566/image.png" width=500>

<p>UI에서 보면,
이 input 폼에 입력하는 값들은,
코드의 input이라는 state에 보관</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/a6751937-b78a-4763-a6ca-f2ec8cf5f2e0/image.png" width=500>

<p>그러나 콘솔에는 아무 메시지 뜨지 않음
useEffect의 콜백함수가 실행되지 않았다는 뜻,,,
deps에 count만 들어있기 때문</p>
<p>따라서 input이 바뀔 때 useEffect의 콜백 함수를 실행시키고 싶다면,
➡️ 의존성 배열의 input을 추가</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/e696cc4a-6a9d-4497-bb9f-33355a3429a1/image.png" width=500>

<p>이제 useEffect의 콜백 함수는 count나 input(둘다 state) 중 하나의 값만 바뀌어도
컴포넌트가 리렌더링 되고 콜백 함수 실행</p>
<blockquote>
<p>정확히 말하면,
❗ useEffect가 리렌더링을 발생시키는 것은 아니고,
<strong>원인</strong>
count 또는 input이라는 state가 변경됨
<strong>결과</strong>
컴포넌트가 리렌더링
이후 dependency 배열을 비교해서
변경이 감지되면 effect 실행
<span style="background-color:#FFC0CB">리렌더링이 발생했을 때, 이전 렌더의 값과 비교해서 달라졌으면 effect가 실행</span></p>
</blockquote>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/89526b07-88e7-4f41-b9b0-ac29d39056e9/image.png" width=600>

<p>따라서 콜백함수의 console.log 함수도 약간 수정</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/e61d3e30-e692-4b51-b320-64e66844a06d/image.png">

<p>이후 input의 값이나, count의 값이 바뀔 때 출력이 잘 되는 것 확인 가능</p>
<h2 id="의문🤔-그냥-이벤트-핸들러에-코드를-추가하면-안-되나">의문🤔 그냥, 이벤트 핸들러에 코드를 추가하면 안 되나?</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/ab748533-52b4-4ac6-b256-096dcf36ad9a/image.png">

<p>useEffect를 쓰지 않고,
onClickButton 이벤트 핸들러에서 이 setCount를 호출해서 값을 변경하고,
변경된 값을 console.log로 출력하면 안 되는 건가?</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/8aa1e739-8521-46cc-8f3f-4607314a96ff/image.png">

<p>그러면, 변경되기 이전의 값을 콘솔에 출력함</p>
<p>왜냐하면 <span style="background-color:#FFC0CB">setCount라는 리액트의 상태 변화 함수는 비동기로 동작함</span>
setCount가 호출되고 완료되기 전에,
console.log로 count값을 출력함으로써, <span style="background-color:#FDF5E6">count값이 변경되기 이전의 값을 콘솔에 출력함</span></p>
<p>따라서,
<span style="background-color:#FFC0CB">리액트의 state는 비동기로 업데이트 되기 때문에</span> 변경된 state의 값을 사용해서
<span style="background-color:#E2D6FF">사이드 이펙트에 해당하는 부가적인 작업을 진행하려면 useEffect를 이용</span></p>
<hr>
<h1 id="📌-useeffect로-라이프사이클-제어하기">📌 useEffect로 라이프사이클 제어하기</h1>
<blockquote>
<p>리액트 컴포넌트는 라이프사이클을 가짐</p>
</blockquote>
<h2 id="1️⃣-마운트-탄생">1️⃣ 마운트: 탄생</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/b2666972-a9f1-4f32-bd68-d056c0ca8cb9/image.png" width=400>

<ul>
<li><strong>deps: 빈 배열</strong></li>
</ul>
<p>➡️ <span style="background-color:#FFC0CB">콜백함수는 이 컴포넌트가 <strong>처음 마운트 될 때만 동작</strong></span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/0c026858-1d7a-4fac-8a20-35402f236557/image.png" width=500>
(플러스 버튼을 많이 눌러 보아도, 콘솔 로그는 한 번만 출력)

<p><span style="background-color:#E2D6FF">useEffect는 deps에 있는 값이 변경되어야만 실행되기 때문에</span>
이 콜백함수는 컴포넌트가 처음 마운트될 때 이후에는 다시는 실행되지 않음
따라서 아무리 state의 값(count)을 변경해도,
콘솔 로그가 두 번 이상 출력되지 않음</p>
<p>즉 <span style="background-color:#FFEBCD">컴포넌트가 마운트 되었을 때만 최초로 한 번 실행시키고 싶은 코드가 있다면,
이렇게 useEffect를 호출하고 deps로 빈 배열을 전달하면 됨</span></p>
<h2 id="2️⃣-업데이트-변화-리렌더링">2️⃣ 업데이트: 변화, 리렌더링</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/44c62545-fb3b-466c-97af-c97d9397e1ee/image.png" width=400>

<ul>
<li><strong>deps: 생략</strong></li>
</ul>
<p>➡️ <span style="background-color:#FFC0CB">콜백 함수는 <strong>마운트 될 때 한 번 실행, 그리고 컴포넌트가 리렌더링될 때마다 계속 실행</strong></span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/b583ad41-ecbd-48f5-9b09-4728194c2f4a/image.png" width=500>

<p><span style="background-color:#FDF5E6">마운트 시점에 마운트와 업데이트가 동시에 한 번 출력되고,</span>
컴포넌트가 리렌더링시키면 업데이트가 계속 콘솔에 출력</p>
<br>

<p>그런데 마운트 시점을 제외하고,
<strong>업데이트가 되는 순간에만 콜백함수를 실행하고 싶다면</strong>
<span style="background-color:#FDF5E6">컴포넌트가 마운트되었는지 안 되었는지 체크하는 변수</span>를 <strong>useRef</strong>를 이용
<span style="background-color:#FFE4E1">(Ref 객체는 돔 요소를 참조하는 것뿐만 아니라 컴포넌트의 변수로도 자주 활용됨)</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/f9df11c4-6ae8-47d3-88e0-a9f2878a0ae6/image.png" width=600>

<p>isMount.current가 false라면,
아직 컴포넌트가 마운트되지 않은 상황
➡️ 따라서 isMount의 current 값을 마운트되었단 의미로 true로 바꾼 후 return으로 함수 종료</p>
<p><span style="background-color:#FFC0CB">따라서 처음 마운트 될 때는 콘솔 로그가 출력되지 않음</span></p>
<p><span style="background-color:#E2D6FF">이후 컴포넌트가 리렌더링 되어서 다시 useEffect의 콜백 함수를 호출하면 콘솔 로그 출력 가능</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/aa7d55e6-1c33-4de2-97a2-e4ac51a2f24a/image.png" width=500>

<p>마운트될 때는 mount만,
업데이트 될 때는 update만 출력되는 것을 확인</p>
<blockquote>
<p>이와 같이 컴포넌트의 업데이트 단계에서만 코드를 실행시키고 싶다면,
레퍼런스 객체를 생성해서 flag로 사용하면 됨</p>
</blockquote>
<h2 id="3️⃣-언마운트-죽음">3️⃣ 언마운트: 죽음</h2>
<h3 id="클린업">클린업</h3>
<blockquote>
<p>특정 함수가 실행되고 종료된 후에, 미처 정리하지 못한 사항을 처리
useEffect의 콜백 함수가 반환하는 함수를 클린업 함수라고 함</p>
</blockquote>
<h3 id="클린업을-이용해-언마운트-제어하기">클린업을 이용해 언마운트 제어하기</h3>
<p>카운터의 넘버가 짝수일 때만 화면에 렌더링될 컴포넌트를 만듦</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/76dea4d0-e12d-4ed0-99b3-5b392fd4b27c/image.png" width=400>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/62a21a2c-899f-4475-ba57-d16a0b018e37/image.png" width=500>

<p>그리고 컴포넌트를 렌더링하는 부분 추가
따라서 state인 count의 값이 짝수면 Even 컴포넌트 호출, 아니면 맒</p>
<br>

<p>그리고 Even 컴포넌트에서
useEffect를 이용해 Even 컴포넌트가 언마운트 되는 시점 제어</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/926faae8-22d9-4562-bedd-e4ade34cf489/image.png" width=400>

<ul>
<li>첫 번째 인수 내 콜백 함수에서, <strong>새로운 함수 return</strong></li>
<li><strong>deps: 빈 배열</strong></li>
</ul>
<p>➡️ <span style="background-color:#FFC0CB">콜백 함수가 반환하는 함수를 클린업, 또는 정리 함수라고 함</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/61c6bd5b-6730-4824-abd3-1bc9c6d30087/image.png" width=400>

<p>이 정리함수는 useEffect가 끝날 때 실행됨</p>
<p>지금처럼 deps를 빈배열로 전달하면,
이 useEffect는 마운트가 될 때 실행이 되고,
종료는 언마운트될 때 종료가 되고, 그때가 되면 정리 함수를 호출하게 됨</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/b339b68b-3d6f-4b2a-b3da-799fb5f674e9/image.png" width=500>

<p><span style="background-color:#FDF5E6">따라서 Even 컴포넌트가 사라지면, 정리함수가 호출됨</span></p>
<br>

<p>컴포넌트가 마운트 될 때 어떤 데이터를 불러오는 코드를 작성하고,
업데이트 되었을 때 현재 업데이트 된 state의 값들이 정상적인 값인지 검사하고,
마지막으로 컴포넌트가 화면에서 사라질 때 컴포넌트에서 쓰고 있는 메모리를 해제하는 최적화 작업이 가능할 것</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[모던 자바스크립트 19장. 프로토타입]]></title>
            <link>https://velog.io/@ann-algorithm/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-19%EC%9E%A5</link>
            <guid>https://velog.io/@ann-algorithm/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-19%EC%9E%A5</guid>
            <pubDate>Mon, 22 Dec 2025 07:24:22 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-객체지향-프로그래밍">📌 객체지향 프로그래밍</h1>
<p>특징이나 성질을 나타내는 속성
이 속성 중 필요한 속성만 간추리면, 추상화</p>
<p>속성을 통해 여러 개의 값을 하나의 단위로 구성한 복합적인 자료구조 -&gt; 객체
= 상태를 나타내는 데이터 + 조작할 수 있는 동작</p>
<h1 id="📌-상속과-프로토-타입">📌 상속과 프로토 타입</h1>
<p>상속이란?
어떤 객체의 프로퍼티 또는 메서드를 다른 객체가 상속받아 그대로 사용할 수 있는 것</p>
<p>자바스크립트는 프로토타입을 기반으로 상속을 구현하여 불필요한 중복 제거</p>
<pre><code>예를 들면, 현대자동차임

자동차 설계도가 있고,
이 설계도로 자동차를 찍어냄 -&gt; 인스턴스

현대자동차는 좋은 차량용 소프트웨어를 가지고 있음
그런데 굳이 생산하는 차량 대당 소프트웨어를 만들 필요는 없음
중앙의 서버를 공유하면 됨

&quot;그러나 생성자 함수로 인스턴스를 생성하면, 차마다 소프트웨어를 만들게 됨&quot;

굳이?</code></pre><p>하지만 상속을 통해 불필요한 중복 제거
-&gt; 차마다 각자의 소프트웨어를 만들 필요가 없음</p>
<h1 id="📌-프로토타입-객체">📌 프로토타입 객체</h1>
<p>프로토타입 객체란, 객체지향 프로그래밍의 근간을 이루는 객체 간 상속을 구현하기 위해 사용</p>
<p>어떤 객체의 상위 객체로서, 다른 객체에 공유 프로퍼티(메서드 포함)를 제공
➡️ 프로토타입을 상속 받은 하위(자식) 객체는 상위 객체의 프로퍼티를 자신의 프로퍼티처럼 자유롭게 사용 가능</p>
<h2 id="☑️-__proto__-접근자-프로퍼티">☑️ <code>__proto__</code> 접근자 프로퍼티</h2>
<h3 id="__proto__는-접근자-프로퍼티"><code>__proto__</code>는 접근자 프로퍼티</h3>
<p>[[Prototype]] 내부 슬롯에 직접 접근할 수 없고,
<code>__proto__</code> 접근자 프로퍼티를 통해 간접적으로 [[Prototype]] 내부 슬롯의 값, 즉 프로토타입에 접근 가능</p>
<h3 id="__proto__-접근자-프로퍼티는-상속을-통해-사용됨"><code>__proto__</code> 접근자 프로퍼티는 상속을 통해 사용됨</h3>
<p><code>__proto__</code> 접근자 프로퍼티는 객체가 직접 소유하는 프로퍼티가 아니라 Object.prototype의 프로퍼티
➡️ 모든 객체는 상속을 통해 <code>Object.prototype.__proto__</code> 접근자 프로퍼티 사용 가능</p>
<h3 id="__proto__-접근자-프로퍼티를-통해-프로토타입에-접근하는-이유"><code>__proto__</code> 접근자 프로퍼티를 통해 프로토타입에 접근하는 이유</h3>
<p><span style="background-color:#FFC0CB">상호 참조에 의해 프로토타입 체인이 생성되는 걸 방지하기 위해서</span></p>
<p>프로토타입 체인은 단방향 링크드리스트로 구현되어야 함
따라서, 순환 참조하는 프로토타입 체인이 만들어지면, 프로토타입 체인 종점이 존재하지 않고,
프로토타입 체인에서 프퍼티를 검색할 때 무한루프에 빠짐</p>
<h3 id="__proto__-접근자-프로퍼티를-코드-내에서-직접-사용하는-것은-권장하지-않는다"><code>__proto__</code> 접근자 프로퍼티를 코드 내에서 직접 사용하는 것은 권장하지 않는다.</h3>
<p>모든 객체가 <code>__proto__</code>  접근자 프로퍼티를 사용할 수 있는 것은 아님
따라서 <code>__proto__</code> 접근자 프로퍼티 대신 프로토타입의 참조를 취득하고 싶은 경우 get/set 메서드를 사용</p>
<h2 id="☑️-함수-객체의-prototype-프로퍼티">☑️ 함수 객체의 prototype 프로퍼티</h2>
<p>함수 객체만이 소유하는 prototype 프로퍼티는 생성자 함수가 생성할 인스턴스의 프로토타입을 가리킴</p>
<p>따라서, 생성자 함수로서 호출할 수 없는 함수, non-constructor인 화살표 함수와 ES6 메서드 축약 표현으로 정의한 메서드는 prototype 프로퍼티를 소유하지 않으며 프로토타입도 생성하지 않음</p>
<p>모든 객체가 가지고 있는 (엄밀히, Object.prototype으로부터 상속받은) <code>__proto__</code> 접근자 프로퍼티와,
함수 객체만이 가지고 있는 prototype 프로퍼티는 결국 동일한 프로토타입을 가리킴</p>
<p>그러나 사용하는 주체가 다르다
<code>__proto__</code> 접근자 프로퍼티 : 모든 객체
prototype 프로퍼티 : 생성자 함수</p>
<h2 id="☑️-프로토타입의-constructor-프로퍼티와-생성자-함수">☑️ 프로토타입의 constructor 프로퍼티와 생성자 함수</h2>
<p>모든 프로토타입은 constructor 프로퍼티를 가짐</p>
<p>constructor 프로퍼티는 prototype 프로퍼티로 자신을 참조하고 있는 생성자 함수를 가리킴
이 연결은, 생성자 함수가 생성될 때 (즉 함수 객체가 생성될 때)이뤄짐</p>
<h1 id="📌-리터럴-표기법에-의해-생성된-객체의-생성자-함수와-프로토타입">📌 리터럴 표기법에 의해 생성된 객체의 생성자 함수와 프로토타입</h1>
<p>생성자 함수에 의해 생성된 인스턴스는, 위에 설명한 것처럼 생성자 함수와 연결됨
이때 constructor 프로퍼티가 가리키는 생성자 함수는, 인스턴스를 생성한 생성자 함수</p>
<br>

<p>리터럴 표기법에 의한 객체 생성 방식은, new 연산자를 사용하지 않기도 한다.
물론 이때도 프로토타입이 존재하지만,</p>
<p>리터럴 표기법에 의해 생성된 객체의 경우 프로토타입의 constructor 프로퍼티가 가리키는 생성자 함수가 반드시 객체를 생성한 생성자 함수라고 단정 불가</p>
<br>

<p>객체 리터럴이 평가될 때는 추상 연산 OrdinaryObjectCreate를 호출하여 빈 객체를 생성하고 프로퍼티를 추가
따라서 생성자 함수 호출과는 세부 내용이 다름</p>
<br>

<p>하지만 큰 틀에서,
리터럴 표기법으로 생성한 객체도 생성자 함수로 생성한 객체와 본질적인 면에서 큰 차이가 없음</p>
<h1 id="📌-프로토타입의-생성-시점">📌 프로토타입의 생성 시점</h1>
<p>리터럴 표기법에 의해 생성된 객체도 생성자 함수와 연결은 되어 있음
즉, 모든 객체는 생성자 함수와 연결되어 있음</p>
<p>프로토타입은 생성자 함수가 생성되는 시점에 더불어 생성</p>
<h1 id="📌-객체-생성-방식과-프로토타입의-결정">📌 객체 생성 방식과 프로토타입의 결정</h1>
<p>객체는 여러 가지 방법으로 생성되지만,
추상 연산 OrdinaryObjectCreate에 의해 생성된다는 공통점이 있음</p>
<p>프로토타입은 이 추상 연산 OrdinaryObjectCreate에 전달되는 인수에 의해 결정됨</p>
<h1 id="📌-프로토타입-체인">📌 프로토타입 체인</h1>
<p>자바스크립트는 객체의 프로퍼티와 메서드에 접근하려고 할 때,
해당 객체에 접근하려는 프로퍼티가 없다면 [[Prototype]] 내부 슬롯의 참조를 따라 자신의 부모 역할을 하는 프로토타입의 프로퍼티를 순차적으로 검색
➡️ 프로토타입 체인</p>
<h1 id="📌-오버라이딩과-프로퍼티-섀도잉">📌 오버라이딩과 프로퍼티 섀도잉</h1>
<p>프로토타입 프로퍼티와 같은 이름의 프로퍼티를 인스턴스에 추가하면, 
프로토타입 체인을 따라 프로토타입 프로퍼티를 검색하여 프로토타입 프로퍼티를 덮어 쓰는 게 아니라 
인스턴스 프로퍼티로 추가</p>
<p>이처럼 상속 관계에 의해 프로퍼티가 가려지는 현상
= 프로퍼티 섀도잉</p>
<h1 id="📌-프로토타입의-교체">📌 프로토타입의 교체</h1>
<p>프로토타입은 임의의 다른 객체로 변경
부모 객체인 프로토타입을 동적으로 변경 가능하다는 것
-&gt; 객체 간의 상속 관계를 동적으로 변경 가능</p>
<ul>
<li>생성자 함수에 의한 프로토타입의 교체</li>
<li>인스턴스에 의한 프로토타입의 교체</li>
</ul>
<h1 id="📌-instanceof-연산자">📌 instanceof 연산자</h1>
<p>우변의 생성자 함수의 prototype에 바인딩된 객체가 좌변의 객체의 프로토타입 체인 상에 존재하면 true,
그렇지 않은 경우 false로 평가</p>
<h1 id="📌-직접-상속">📌 직접 상속</h1>
<ul>
<li>Object.create에 의한 직접 상속</li>
<li>객체 리터럴 내부에서 <code>__proto__</code>에 의한 직접 상속</li>
</ul>
<h1 id="📌-정적-프로퍼티메서드">📌 정적 프로퍼티/메서드</h1>
<blockquote>
<p>생성자 함수로 인스턴스를 생성하지 않아도 참조/호출할 수 있는 프로퍼티/메서드</p>
</blockquote>
<h1 id="📌-프로퍼티-존재-확인">📌 프로퍼티 존재 확인</h1>
<ul>
<li>in 연산자</li>
<li>Object.prototype.hasOwnProperty 메서드</li>
</ul>
<h1 id="📌-프로퍼티-열거">📌 프로퍼티 열거</h1>
<ul>
<li>for... in 문</li>
<li>Object.keys/values/entries 메서드</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[모던 자바스크립트 18장. 함수와 일급 객체]]></title>
            <link>https://velog.io/@ann-algorithm/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-18%EC%9E%A5</link>
            <guid>https://velog.io/@ann-algorithm/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-18%EC%9E%A5</guid>
            <pubDate>Mon, 22 Dec 2025 07:23:31 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-일급-객체">📌 일급 객체</h1>
<h2 id="일급-객체의-조건">일급 객체의 조건</h2>
<blockquote>
<ol>
<li>무명의 리터럴로 생성 가능. 즉 런타임에 생성 가능</li>
<li>변수 자료구조(객체, 배열 등)에 저장 가능</li>
<li>함수의 매개변수에 전달 가능</li>
<li>함수의 반환값으로 사용 가능</li>
</ol>
</blockquote>
<p>자바스크립트 함수는 다음 예제와 같이 위의 조건을 모두 만족하므로 일급 객체</p>
<pre><code class="language-js">// 1. 함수는 무명의 리터럴로 생성할 수 있다.
// 2. 함수는 변수에 저장할 수 있다.
// 런타임(할당 단계)에 함수 리터럴이 평가되어 함수 객체가 생성되고 변수에 할당된다.
const increase = function (num) {
  return ++num;
};

const decrease = function (num) {
  return --num;
};

// 2. 함수는 객체에 저장할 수 있다.
const ausx = { increase, decrease };

// 3. 함수의 매개변수에 전달할 수 있다.
// 4. 함수의 반환값으로 사용할 수 있다.
function makeCounter(aux) {
  let num = 0;

  return function () {
    num = aux(num);
    return num;
  };
}

// 3. 함수는 매개변수에게 함수를 전달할 수 있다.
const increaser = makeCounter(auxs.increase);
console.log(increaser()); // 1
console.log(increaser()); // 2

// 3. 함수는 매개변수에게 함수를 전달할 수 있다.
const decreaser = makeCounter(auxs.decrease);
console.log(decreaser()); // -1
console.log(decreaser()); // -2</code></pre>
<p>함수 === 일급 객체
➡️ 함수를 객체와 동일하게 사용할 수 있다는 의미</p>
<p>또한, <span style="background-color:#FFEBCD">객체는 값이므로,
함수는 값과 동일하게 취급 가능</span>
➡️ <span style="background-color:#FFC0CB">함수는 값을 사용할 수 있는 곳(변수 할당문, 객체의 프로퍼티 값, 배열의 요소, 함수 호출의 인수, 함수 반환문)이라면 어디서든지 리터럴로 정의 가능</span>
➡️ 런타임에 함수 객체로 평가됨</p>
<p>또한, <span style="background-color:#FFEBCD">일반 객체와 같이 함수의 매개변수에 전달</span> 가능
<span style="background-color:#FFEBCD">함수의 반환값으로도 사용</span> 가능
<span style="background-color:#FDF5E6">이는, 함수형 프로그래밍을 가능케 하는 자바스크립트의 장점 중 하나</span></p>
<p>또한, 함수는 객체이지만 일반 객체와 차이가 있음
<span style="background-color:#FFEBCD">일반 객체는 호출할 수 없지만, 함수 객체는 호출 가능</span>
그리고 <span style="background-color:#FFEBCD">함수 객체는 일반 객체에는 없는 함수 고유의 프로퍼티를 소유</span></p>
<h1 id="📌-함수-객체의-프로퍼티">📌 함수 객체의 프로퍼티</h1>
<p>함수는 객체
즉, 함수도 프로퍼티를 가질 수 있음</p>
<pre><code class="language-js">function square(number) {
  return number * number;
}

console.dir(square);</code></pre>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/f425b15d-2bd8-4554-9399-452bcaf5e168/image.png">

<p>square 함수 객체의 자기 자신(own) 프로퍼티는 length, name, prototype, arguments, caller인데 확인 가능</p>
<br>

<p>square 함수의 모든 프로퍼티의 프로퍼티 어트리뷰트를 Object.getOwnPropertyDescriptors 메서드로 확인해보자</p>
<pre><code class="language-js">function square(number) {
  return number * number;
}

/*
length: 
{value: 1, writable: false, enumerable: false, configurable: true}
name: {value: &#39;square&#39;, writable: false, enumerable: false, configurable: true}
prototype: {value: {…}, writable: true, enumerable: false, configurable: false}
...
(교재와는 다른데 그 이유는, 실행 모드나 환경에 따라 다를 수 있기 때문)
*/
console.log(Object.getOwnPropertyDescriptors(square));

// __proto__는 square 함수의 프로퍼티가 아니다.
console.log(Object.getOwnPropertyDescriptor(square, &#39;__proto__&#39;)); // undefined

// __proto__는 Object.prototype 객체의 접근자 프로퍼티다.
// square 함수는 Object.prototype 객체로부터 __proto__ 접근자 프로퍼티를 상속받는다.
console.log(Object.getOwnPropertyDescriptor(Object.prototype, &#39;__proto__&#39;));
// {enumerable: false, configurable: true, get: ƒ, set: ƒ}</code></pre>
<p>이처럼, <span style="background-color:#FFE4E1">arguments, caller, length, name, property 프로퍼티는 모두 함수 객체의 데이터 프로퍼티</span>
<span style="background-color:#FDF5E6">이는, 함수 객체 고유의 프로퍼티</span></p>
<p>하지만, <span style="background-color:#E2D6FF"><code>__proto__</code>는 접근자 프로퍼티</span>며,
<span style="background-color:#E2D6FF">함수 객체 고유의 프로퍼티가 아니라, Object.prototype 객체의 프로퍼티를 상속 받은 것
</span></p>
<p>Object.prototype 객체의 프로퍼티는 모든 객체가 상속 받아 사용 가능
➡️ Object.prototype 객체의 <code>__proto__</code> 접근자 프로퍼티는 모든 객체가 사용 가능</p>
<h2 id="☑️-arguments-프로퍼티">☑️ arguments 프로퍼티</h2>
<p>함수 객체의 arguments 프로퍼티 값은 arguments 객체였지만,
이는 표준에서 폐지되었으므로 현재는 사용하지 않음</p>
<p>arguments 객체는 함수 호출 시 전달된 인수들의 정보를 담고 있는
순회 가능한 유사 배열 객체이며,
함수 내부에서 지역 변수처럼 사용
➡️ 함수 외부에서는 참조할 수 없음</p>
<p>사용 시에는
<span style="background-color:#FDF5E6">함수 내부에서 지역 변수처럼 제공되는 arguments 객체를 참조</span>하도록 함</p>
<p>자바스크립트는 함수의 매개변수와 인수의 개수가 일치하는지 확인하지 않음
➡️ 함수 호출 시 매개변수 개수만큼 전달하지 않아도 에러가 발생하지 않음</p>
<pre><code class="language-js">function multiply(x, y) {
  console.log(arguments);
  return x * y;
}

console.log(multiply());        // NaN
console.log(multiply(1));        // NaN
console.log(multiply(1, 2));    // 2
console.log(multiply(1, 2, 3));    // 2</code></pre>
<p><span style="background-color:#FFEBCD">함수를 정의할 때 선언한 매개변수는, 함수 몸체 내부에서 변수와 동일하게 취급됨</span>
즉, 함수가 호출되면 함수 몸체 내에서 암묵적으로 매개변수가 선언되고,
undefined로 초기화된 후 인수가 할당됨</p>
<p>선언된 매개변수의 개수보다 적게 전달한 경우, 인수가 전달되지 않은 매개변수는 undefined로 초기화된 상태 유지
만약, 매개변수의 개수보다 많이 전달한 경우, 초과된 인수는 무시됨</p>
<p>(초과된 인수는 버려지지 않고, 암묵적으로 보관됨)</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/1bcf0122-cf42-4f38-a69e-7c0341e114f3/image.png">

<p>위 예시에서 볼 수 있듯이,
<span style="background-color:#FFEBCD">arguments 객체는 인수를 프로퍼티 값으로 소유</span>하고,
<span style="background-color:#FFEBCD">프로퍼티 키는 인수의 순서</span>를 나타냄</p>
<p>arguments 객체의 
<span style="background-color:#FFEBCD">callee 프로퍼티는 호출되어 arguments 객체를 생성한 함수, 즉 함수 자신</span>을 가리키고,
<span style="background-color:#FFEBCD">length 프로퍼티는 인수의 개수</span>를 가리킴</p>
<br>

<p>선언된 매개변수의 개수와 함수를 호출할 때, 
전달하는 인수의 개수를 확인하지 않는 자바스크립트의 특성 때문에
➡️ <span style="background-color:#FDF5E6">함수가 호출되면 인수 개수를 확인하고
이에 따라 함수의 동작을 달리 정의할 필요가 있음</span></p>
<p>이때 유용하게 사용되는 것이 arguments 객체</p>
<pre><code class="language-js">function sum() {
  let res = 0;

  // arguments 객체는 length 프로퍼티가 있는 유사 배열 객체이므로 for문으로 순회할 수 있다.
  for (let i = 0; i &lt; arguments.length; i++) {
    res += arguments[i];
  }

  return res;
}

console.log(sum());            // 0
console.log(sum(1, 2));        // 3
console.log(sum(1, 2, 3));    // 6</code></pre>
<p>유사 배열 객체란, length 프로퍼티를 가진 객체로 for문으로 순회할 수 있는 객체
단, 배열이 아니므로 배열 메서드 사용 시 에러가 남</p>
<p>arguments 객체를 배열로 변환해야, 배열 메서드를 사용할 수 있고...</p>
<p>이러한 번거로움을 해결하기 위해 ES6에서는 Rest 파라미터를 도입</p>
<pre><code class="language-js">// ES6 Rest parameter
function sum(...args) {
  return args.reduce((pre, cur) =&gt; pre + cur, 0);
}

console.log(sum(1, 2));            // 3
console.log(sum(1, 2, 3, 4, 5));// 15</code></pre>
<h2 id="☑️-caller-프로퍼티">☑️ caller 프로퍼티</h2>
<p>ECMAScript 사양에 포함되지 않은 비표준 프로퍼티
이후 표준화될 예정도 없는 프로퍼티</p>
<blockquote>
<p>함수 자신을 호출한 함수를 가리킴</p>
</blockquote>
<pre><code class="language-js">function foo(func) {
  return func();
}

function bar() {
  return &#39;caller : &#39; + bar.caller;
}

// 브라우저에서 실행한 결과
console.log(foo(bar));    // caller : function foo(func) {...}
console.log(bar());        // caller : null</code></pre>
<h2 id="☑️-length-프로퍼티">☑️ length 프로퍼티</h2>
<blockquote>
<p>선언한 매개변수의 개수</p>
</blockquote>
<pre><code class="language-js">function foo() {}
console.log(foo.length); // 0

function bar(x) {
  return x;
}
console.log(bar.length); // 1

function baz(x, y) {
  return x * y;
}
console.log(baz.length); // 2</code></pre>
<p>arguments 객체의 length 프로퍼티 함수와, 함수 객체의 length 프로퍼티의 값은 다를 수 있음</p>
<p>arguments 객체의 length 프로퍼티는 인자의 개수를 가리키고,
함수 객체의 length 프로퍼티는 매개변수의 개수를 가리킴</p>
<h2 id="☑️-name-프로퍼티">☑️ name 프로퍼티</h2>
<blockquote>
<p>함수 이름</p>
</blockquote>
<p>ES6에서 정식 표준이 됨</p>
<p><span style="background-color:#FFEBCD">익명 함수 표현식</span>의 경우 <span style="background-color:#FFC0CB">ES5에서 name 프로퍼티는 빈 문자열을 값으로 가짐</span>
하지만 <span style="background-color:#FFC0CB">ES6에서는 함수 객체를 가리키는 식별자를 값으로 가짐</span></p>
<pre><code class="language-js">// 기명 함수 표현식
var namedFunc = function foo() {};
console.log(namedFunc.name); // foo

// 익명 함수 표현식
var anonymousFunc = function() {};
// ES5: name 프로퍼티는 빈 문자열을 값으로 갖는다.
// ES6: name 프로퍼티는 함수 객체를 가리키는 변수 이름을 값으로 갖는다.
console.log(anonymousFunc.name); // anonymousFunc

// 함수 선언문(Function declaration)
function bar() {}
console.log(bar.name); // bar</code></pre>
<p><span style="background-color:#FDF5E6">함수를 호출할 때는 함수 이름이 아닌 함수 객체를 가리키는 식별자로 호출</span></p>
<h2 id="☑️-__proto__-접근자-프로퍼티">☑️ <code>__proto__</code> 접근자 프로퍼티</h2>
<p>모든 객체는 [[Prototype]]이라는 내부 슬롯을 가짐
[[Prototype]] 내부 슬롯은 객체지향 프로그래밍의 상속을 구현하는 프로토타입 객체</p>
<p><code>__proto__</code> 프로퍼티는
<span style="background-color:#FFEBCD">[[Prototype]] 내부 슬롯이 가리키는 프로토타입 객체에 접근하기 위해 사용하는 접근자 프로퍼티</span></p>
<p><span style="background-color:#FFC0CB">[[Prototype]] 내부 슬롯에는 직접 접근할 수 없고, <code>__proto__</code> 프로퍼티 접근자 프로퍼티를 통해 간접적인 접근 방법을 제공하는 경우에 한하여 접근 가능</span></p>
<h2 id="☑️-prototype-프로퍼티">☑️ prototype 프로퍼티</h2>
<p>prototype 프로퍼티는 생성자 함수로 호출할 수 있는 함수 객체, 즉 constructor 만이 소유하는 프로퍼티
일반 객체와 생성자 함수로 호출할 수 없는 non-constructor에는 prototype 프로퍼티가 없음</p>
<pre><code class="language-js">// 함수 객체는 prototype 프로퍼티를 소유한다.
(function () {}).hasOwnProperty(&#39;prototype&#39;); // -&gt; true

// 일반 객체는 prototype 프로퍼티를 소유하지 않는다.
({}).hasOwnProperty(&#39;prototype&#39;); // -&gt; false</code></pre>
<p>prototype 프로퍼티는 함수가 객체를 생성하는 생성자 함수로 호출될 때 생성자 함수가 생성할 인스턴스의 프로토타입 객체를 가리킴</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React.js 입문]]></title>
            <link>https://velog.io/@ann-algorithm/React.js-%EC%9E%85%EB%AC%B8</link>
            <guid>https://velog.io/@ann-algorithm/React.js-%EC%9E%85%EB%AC%B8</guid>
            <pubDate>Tue, 09 Dec 2025 15:56:46 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%EB%A6%AC%EC%95%A1%ED%8A%B8/dashboard">한 입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지 by 이정환
</a></p>
<h1 id="📌--실습-준비하기">📌  실습 준비하기</h1>
<h2 id="프로젝트-설치">프로젝트 설치</h2>
<pre><code>npm create vite@latest</code></pre><p>앱 프로젝트 만들기</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/b80a896c-5136-4142-a431-6f6a7f06c067/image.png" width=500>

<p>로컬 환경에서 웹 서비스를 실행할 수 있음</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/3de8cec0-2c45-4ee1-8bee-a831aca07f51/image.png" width=500>

<p>웬만하면 설치하여서 사용 권장</p>
<h2 id="실습을-위한-옵션-몇-가지">실습을 위한 옵션 몇 가지</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/a124e2e5-9f40-4fb1-ad86-1fb41671dc71/image.png" width=500>

<p>일단 rules 부분만 확인해서 내용 수정</p>
<pre><code>rule: {
      &quot;no-unused-vars&quot;: &quot;off&quot;,
      &quot;react/prop-types&quot;: &quot;off&quot;,
},</code></pre><ul>
<li>사용하지 않는 변수는 오류로 나타내지 않기</li>
<li>React 컴포넌트에서 Prop Types를 정의하도록 요구하기</li>
</ul>
<hr>
<h1 id="📌-react-컴포넌트">📌 React 컴포넌트</h1>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/2ab2ca38-6ce6-48bd-a526-0de98a3e3108/image.png" width=350>

<p>리액트에서는 자바스크립트로 짠 함수가 <strong>html 태그</strong>를 반환하도록 설정 가능
➡️ 이렇게 html 태그들을 반환하는 함수를 특별히 컴포넌트라고 함</p>
<p>그리고 컴포넌트를 부를 때는 함수의 이름을 따서 부름
➡️ <strong>App 컴포넌트</strong></p>
<h2 id="직접-컴포넌트-만들어보기">직접 컴포넌트 만들어보기</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/127f6898-2557-42c3-b1bf-312e5bb41074/image.png" width=350>

<p>위와 같이 <strong>Header 컴포넌트</strong>를 만들어봄</p>
<p>이것은 아래와 같이</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/fe53d2bf-a1d9-4382-964f-2dfb93476886/image.png" width=350>

<p>화살표 함수로 만들 수 있음</p>
<p><span style="background-color:#FDF5E6">함수 말고 클래스를 이용해서 만들 수 있지만 함수로 컴포넌트를 만드는 게 훨씬 일반적이고 대중적임</span></p>
<p>또한 <span style="background-color:#FFC0CB">컴포넌트는 첫 글자가 대문자</span>
그래야 리액트에서 컴포넌트라고 인정</p>
<h3 id="만든-컴포넌트-렌더링">만든 컴포넌트 렌더링</h3>
<p>이제 <strong>Header</strong>를 어떻게 렌더링?</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/8785cbba-4a2e-44c9-8822-b98f84a47dcc/image.png" width=350>

<p><span style="background-color:#FDF5E6">html 태그를 작성하듯이 컴포넌트 배치</span>
➡️ App 컴포넌트가 화면에 렌더링될 때 Header 컴포넌트의 리턴문도 불러와서 같이 렌더링</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/8cbeed3e-d356-4b6a-a20a-d4c89a08bc2b/image.png" width=500>

<p>새로 만든 Header 컴포넌트처럼,
다른 컴포넌트의 리턴문 내부에서 포함되는 이러한 컴포넌트를 <strong>&quot;자식 컴포넌트&quot;</strong>라고 함</p>
<p>반대로 App 컴포넌트는 <strong>부모 컴포넌트</strong></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/7d66401d-3bb5-4fe1-895e-54e49d09e40c/image.png" width=500>


<p>이런 식으로 계층 구조로 컴포넌트 간의 표현 가능</p>
<p>만약 만들었던 Header 컴포넌트 외의 컴포넌트(Main, Footer...) 화면에 렌더링하기 위해서는
<span style="background-color:#FFEBCD">Header 컴포넌트처럼 App 컴포넌트의 자식 컴포넌트로 배치해야 함</span></p>
<p>결론적으로,
리액트의 모든 컴포넌트들은 화면에 렌더링되기 위해서
앱 컴포넌트의 자식 컴포넌트로서 존재해야 함</p>
<p>➡️ 모든 리액트 컴포넌트는 앱 컴포넌트를 최상위, 조상으로 갖는 계층 구조를 갖게 됨</p>
<p>그리고,
이 모든 컴포넌트의 조상 역할을 하는 앱 컴포넌트는
<span style="background-color:#FDF5E6">특별히 모든 컴포넌트의 뿌리 역할을 한다고 해서 <strong>루트 컴포넌트</strong>라고 함</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/651bbddb-a530-4a03-a7f7-b5c552320159/image.png" width=500>

<p>루트 컴포넌트는 main.jsx라는 파일에 렌더 메서드의 인수로 전달된 컴포넌트이기 때문에,
➡️ 원하는 대로 변경 가능</p>
<p>위와 같이 루트 컴포넌트로 <strong>Hello 컴포넌트</strong>로 설정</p>
<p><span style="background-color:#E2D6FF">하지만 관례상 App이라는 이름을 갖는 컴포넌트를 루트 컴포넌트로 설정</span></p>
<h2 id="컴포넌트-폴더별로-분리">컴포넌트 폴더별로 분리</h2>
<p><span style="background-color:#FFEBCD">모듈화를 위해 컴포넌트별로 각 파일에 나눠 작성</span></p>
<p><span style="background-color:#FFC0CB">src 폴더 아래에, 
루트 컴포넌트인 App 컴포넌트 외 추가적인 컴포넌트를 모아두는 <strong>components</strong>라는 폴더 생성</span></p>
<p>Hello 컴포넌트의 경우 루트 컴포넌트가 아니므로,
components 폴더 아래에 새로운 파일인 Header.jsx 생성
App에 들어있던 Header 컴포넌트 코드를 복사해서 저장</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/e16f42e8-b0b1-48c8-8092-98c7ed5f5f0d/image.png" width=350>

<p>Header 컴포넌트가 파일로 분리되었으니
렌더링하려면 import문 추가</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/be589505-c654-4e73-8c00-ea314268fc5f/image.png" width=400>

<p>vite로 만든 리액트 앱에는 import문에 확장자를 쓰지 않아도 됨
ex) <code>import Header from &quot;./components/Header.jsx&quot;</code></p>
<h2 id="정리">정리</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/d16406d2-1239-42a8-838d-6ce81079bb07/image.png">

<p>위와 같이 렌더링하고자 하는 컴포넌트들이 있다면,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/b0ee2574-2bf0-4afc-b16c-e94967ca2873/image.png" width=400>

<p>src/components 폴더 아래에 해당 파일들을 저장해두고,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/d8555dc6-6a1a-4dc3-9ee8-97229f8b098e/image.png" width=450>

<p>import문과 함께 렌더링할 수 있음</p>
<hr>
<h1 id="📌-jsx---ui-표현하기">📌 JSX - UI 표현하기</h1>
<h2 id="jsx">JSX</h2>
<blockquote>
<p>리액트 컴포넌트가 화면에 나타나는 UI 요소들을 표현할 수 있도록 도와줌</p>
</blockquote>
<p><strong>JSX</strong> : 확장된 자바스크립트 문법
➡️ 일종의 자바스크립트 확장판</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/3332e1f5-da26-41e1-be3c-0242839615bc/image.png" width=500>


<img src="https://velog.velcdn.com/images/ann-algorithm/post/07ee6dab-29e7-4105-82a7-84352c5de43b/image.png" width=500>

<p><span style="background-color:#FFC0CB">JSX는 자바스크립트 코드 안에서 html 코드를 삽입 가능하게 해줌</span>
➡️ 직관적이고 가독성 높은 컴포넌트 생성 가능</p>
<p>이와같이,
<span style="background-color:#FFEBCD">리액트 컴포넌트는 자바스크립트와 html 태그를 혼용해서 사용 가능</span></p>
<p>또한, 단순히 html 태그를 반환하는 것을 넘어서,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/fdcdd102-982a-4e4d-a9b9-67e4d5367fb2/image.png" width=500>
(강의자료 감사)

<p>위 자료와 같이 <span style="background-color:#E2D6FF">동적으로 특정 변수의 값을 html로 렌더링할 수 있음</span></p>
<h2 id="실습">실습</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/14669bb7-f818-4752-a3fe-0037d8ace3da/image.png" width=350>

<p>위처럼 동적으로 특정 변수의 값을 렌더링하면 아래와 같은 화면을 볼 수 있음</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/d3fe0516-079b-4603-bb21-4368084306d5/image.png" width=500>

<p><span style="background-color:#FFC0CB">또한, JSX는 중괄호 안에 다양한 타입이나 연산식을 넣고 렌더링할 수 있음</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/6bee8553-b5cf-4043-aa6d-624234dec1b4/image.png" width=500>

<h2 id="주의할-점">주의할 점</h2>
<p><span style="background-color:#FDF5E6">중괄호 안에는 자바스크립트 표현식만 넣을 수 있음</span></p>
<h3 id="자바스크립트-표현식이란">자바스크립트 표현식이란?</h3>
<blockquote>
<p>삼항 연산자, 변수, 값, 한 줄의 코드가 특정한 값으로 평가될 수 있는 코드</p>
</blockquote>
<p><span style="background-color:#FFEBCD">즉, 중괄호 안에 if문, for문 같은 제어문은 오류 발생</span>
왜냐하면, 한 줄로써 값으로 평가될 수 없기 때문에</p>
<p>또한, <span style="background-color:#FFC0CB">JSX에서 숫자, 문자열, 또는 배열의 값만 정상적으로 렌더링</span>
<span style="background-color:#FDF5E6">boolean, undefined, null 과 같은 값은 오류를 발생하진 않지만, 화면에 렌더링되지 않음</span></p>
<br>

<p>메인 컴포넌트 안에 객체 값을 그대로 렌더링하면,
➡️ 그냥 브라우저 화면이 백지가 됨 = 오류 발생</p>
<p>이때는, 개발자 도구로 확인하면 오류 확인 가능</p>
<p><span style="background-color:#E6E6FA">객체는 리액트의 자식으로 유효하지 않아서, 렌더링 불가</span>
<span style="background-color:#E2D6FF">하지만 점 표기법을 이용해서 프로퍼티는 렌더링 가능</span></p>
<h3 id="모든-태그는-닫혀야-함">모든 태그는 닫혀야 함</h3>
<p><span style="background-color:#FFEBCD">즉, <code>&lt;h1&gt;</code>이 있으면 <code>&lt;/h1&gt;</code>도 있어야 함</span>
html에서는 <code>&lt;img&gt;</code> 같은 건 닫히지 않은 상태여도 괜찮지만
<span style="background-color:#FDF5E6">JSX는 모두 닫혀 있어야 해서, 닫힘 태그나 셀프 클로징 태그를 달아야 함</span></p>
<h3 id="최상위-태그는-하나여야-함">최상위 태그는 하나여야 함</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/4acefbf8-8fbb-4532-96a4-b42cfbe4c589/image.png">

<p>보면, <code>&lt;div&gt;</code>와 <code>&lt;main&gt;</code> 태그가 최상위에 같이 있는데
➡️ 오류 발생</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/ff39768e-f097-4161-bf34-79a557e4f00d/image.png" width=500>

<p><span style="background-color:#FDF5E6">최상위에은 하나의 태그만 존재해야 오류가 발생하지 않음</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/e999ff00-b4bd-46a6-808d-346487e6b3da/image.png" width=500>

<p>최상위 태그로 쓸만한 마땅한 태그가 없다면,
빈 태그 활용
➡️ JSX는 최상위 태그가 있다고 판단하지만,
<span style="background-color:#FDF5E6">실제 렌더링될 때는 최상위 태그가 없는 것처럼 렌더링 됨</span></p>
<h2 id="활용">활용</h2>
<h3 id="삼항연산자-활용">삼항연산자 활용</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/e9afca29-3aff-4938-bb1d-263031766179/image.png" width=600>

<p>컴포넌트가 조건에 따라 각각 다른 UI를 렌더링</p>
<p><code>user.isLogin</code> 조건에 따라 <code>로그인</code>을 출력하거나, <code>로그아웃</code>을 출력</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/295d7c31-ea92-4c21-8515-b5042724d185/image.png">

<h3 id="조건문-활용">조건문 활용</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/e7cf30e9-3b54-4c93-85de-308d68435fb8/image.png" width=450>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/d80b02c7-9aa1-45e7-87f0-6f11b9e4eda4/image.png">

<p>(같은 렌더링 화면인데, 코드는 짜기 나름)</p>
<h2 id="스타일-적용">스타일 적용</h2>
<h3 id="dom요소에-직접-스타일-속성-설정">DOM요소에 직접 스타일 속성 설정</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/d9918636-26b2-4f6d-818f-063c5a86d3f3/image.png" width=600>

<p>스타일 객체로 설정
➡️ 객체니까 중괄호 두 번</p>
<p><span style="background-color:#FFC0CB">프로퍼티를 쓸 땐 카멜 케이스로 <strong>backgroundColor</strong>로 작성</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/d9026fe5-7a84-4f3a-a437-ba3c0c8fe8b4/image.png" width=500>

<p><span style="background-color:#FFEBCD">JSX에서는 DOM 요소의 스타일 속성을 직접 설정하여 스타일링 가능</span></p>
<p>그러나 위와 같이
리턴문 안에 스타일링 코드를 직접 작성하면,
스타일 규칙이 많아질수록 가독성이 떨어짐<del>(그럼 tailwind는...?)</del>
➡️ 별도의 CSS 파일을 만들어 스타일링 가능</p>
<h3 id="css-파일-생성">CSS 파일 생성</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/546f71d3-b594-4272-aa54-381ac63fdc60/image.png" width=450>
Main.css 파일

<img src="https://velog.velcdn.com/images/ann-algorithm/post/e893aa90-220b-41c6-944f-688effc6d50d/image.png" width=500>

<p>Main.css 파일을 import
<code>&lt;div&gt;</code>태그를 보면 <strong>class</strong>가 아닌 <strong>className</strong>이란 속성으로 클래스 설정</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/d2e9e19e-71ea-404c-b295-1aaa1585d5b7/image.png" width=500>

<p>원래 HTML에선 <strong>class</strong>라고 했지만,
지금은 자바스크립트와 HTML을 같이 쓰고 있고,
자바스크립트의 예약어인 <strong>class</strong>를 쓸 수 없으므로
<span style="background-color:#FFEBCD"><strong>className</strong>으로 설정해야 함</span></p>
<hr>
<h1 id="📌-props로-데이터-전달하기">📌 Props로 데이터 전달하기</h1>
<h2 id="props란">Props란?</h2>
<blockquote>
<p>리액트 컴포넌트에게 값을 전달하는 방법</p>
</blockquote>
<p>리액트에서는 페이지를 컴포넌트 단위로 나누어서 레고를 조립하듯 개발</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/37726039-5f89-444a-98b7-a1c883e2ef18/image.png" width=500>

<p>메뉴 버튼을 보면
버튼은 다 똑같이 생기고,
안에 그려진 아이콘만 다름</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/763ee3d9-6078-48ab-820e-5d12f54e42f1/image.png" width=500>

<p><span style="background-color:#FFE4E1">버튼 컴포넌트를 만들어서 반복적으로 렌더링하면 됨</span>
➡️ 버튼 컴포넌트는 하나만 만들고,
이미지와 텍스트만 바꿔가며 여러 번 반복해서 렌더링</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/e5a24c67-d338-4631-8ff1-a586d66e04c0/image.png" width=500>

<p>메뉴 버튼처럼 <span style="background-color:#FDF5E6">구조는 같지만, 세부적인 내용만 조금씩 다르게 렌더링하도록 설정</span>해주기 위해서는 <span style="background-color:#FFC0CB">각각의 버튼 컴포넌트들이 어떤 버튼(아이콘)을 렌더링할 것인지, 결정하는 값을 전달해야 함</span></p>
<p><span style="background-color:#FDF5E6">마치 함수를 호출하면서 인수를 전달하듯이...</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/1359858d-7e2d-4e6d-94f4-f78e87ed1f78/image.png" width=500>

<p>텍스트와 이미지로 원하는 아이콘을 렌더링</p>
<ul>
<li>텍스트로 &quot;메일&quot;, 이미지로는 &quot;mail.png&quot;</li>
<li>텍스트로 &quot;카페&quot;, 이미지로는 &quot;cafe.png&quot;</li>
<li>텍스트로 &quot;블로그&quot;, 이미지로는 &quot;blog.png&quot;</li>
</ul>
<p>리액트에서는 부모 컴포넌트가 자식 컴포넌트에게 마치 함수의 인수를 전달하듯이 원하는 값 전달 가능</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/c79a3986-08a1-4065-9192-7e1afc280362/image.png" width=500>

<p>이렇게 <span style="background-color:#FFEBCD">컴포넌트에 전달된 값을 <strong>props</strong>라고 함</span>
(Properties의 줄임말)</p>
<h3 id="정리-1">정리</h3>
<p>이렇게 props를 이용하면,
컴포넌트를 마치 함수를 호출하듯이 전달하는 값에 따라서
각각 다른 UI를 렌더링하도록 만들 수 있기 때문에
<strong>props</strong>는 리액트의 핵심 개념</p>
<h2 id="실습-1">실습</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/1d297dd0-b86b-49a7-95ab-71589b22e7bd/image.png" width=400>

<p>버튼 컴포넌트 생성</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/7f8b1435-b36d-4b1f-a097-e123f6f2ed0a/image.png" width=450>

<p>버튼 컴포넌트 렌더링</p>
<p>현재는 이 버튼 컴포넌트에 별다른 props를 주지 않았으므로,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/da2111f2-8b11-476b-a3c9-9e2d2c02cb37/image.png" width=500>

<p>버튼 컴포넌트만 그대로 3번 렌더링</p>
<h3 id="props-전달">props 전달</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/774344c7-3089-4632-be61-3756c76cd6d9/image.png" width=400>

<p>버튼 컴포넌트에, 전달받은 props를 출력하도록 수정</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/dd3af5a9-fc91-48eb-a883-9fc21a0972fd/image.png" width=450>

<p>버튼 컴포넌트를 렌더링하면서, props를 전달</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/109d1ebd-554e-4d56-8b0b-15a511b536a8/image.png" width=500>

<p>세 개의 버튼 컴포넌트를 렌더링했기 때문에,
출력도 세 개
그리고, <span style="background-color:#FFC0CB">props의 값이 객체 형태로 담겨서 전달됨</span></p>
<p>props라는 매개변수에 객체 형태로 저장되어 있으니까,
<span style="background-color:#E2D6FF">점 표기법을 사용해서 렌더링하도록 설정 가능</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/851bf7e1-168a-496d-b591-775f2e913537/image.png" width=500>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/600243cc-ed0d-41eb-8ab1-714d9b7ad423/image.png" width=500>

<p>그럼, 해당 프로퍼티의 값이 렌더링 되는 것 확인</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/9fd04de3-d21d-4dd5-8e7f-9569c6e024ff/image.png" width=450>

<p>넘겨받은 props로 스타일도 지정 가능</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/bf733844-9436-4097-bcfd-a364bf2dcacc/image.png" width=600>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/6b162458-4f91-4ad1-a18a-a6d80eae5cb7/image.png">

<p>color를 받은 버튼은 해당 컬러로 렌더링이 되지만,
받지 못한 버튼은 기본값인 검정으로 렌더링됨
<br></p>
<p>그리고 해당 값을 버튼의 텍스트로 렌더링하면,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/75842868-2f78-422d-9ca4-52d43535a75d/image.png" width=500>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/3a18096e-e532-44cb-8d7d-186023df4866/image.png">

<p><span style="background-color:#FFC0CB">두 번째, 세 번째 버튼은 props로 컬러를 전달해주지 않아서 <code>-</code>까지만 렌더링</span></p>
<p>모양새도 별로지만
<span style="background-color:#E2D6FF">props로 color 값이 무조건 들어올 거라고 예상하고, 코딩하다가 오류가 발생할 수도 있음</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/4d03a778-5dc1-4a01-b893-eb1da200049d/image.png" width=500>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/d0eea308-6ec3-47f7-897d-95afd74cdbc0/image.png" width=500>

<p><span style="background-color:#FDF5E6">color props가 없어서, 값이 전달되지 않으면 undefined가 되는데,
여기에 점 표기법을 쓰고 toUpperCase라는 메서드를 호출하려니까 오류 발생</span></p>
<p>자동으로 설정될 기본값을 설정해서 오류를 해결</p>
<p>(최신의 리액트에서는)</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/400683e6-46a3-4df1-ae02-0ea97f1b21fe/image.png" width=500>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/7b103d54-4463-4084-8299-81f68ab82b82/image.png">


<p>이렇게 props를 전달받을 때 props의 기본값을 설정할 수 있음</p>
<p>즉, <span style="background-color:#FFC0CB">props의 기본값을 설정할 때는</span>
<span style="background-color:#FFC0CB">props를 구조분해할당 문법으로 받아오게 하고,
구조분해할당 문법에서 기본값을 입력하여 props의 기본값 설정</span></p>
<h3 id="스프레드-연산자-전달">스프레드 연산자 전달</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/b8929fdb-9753-45d9-9ad1-96d68661ace8/image.png" width=500>

<p>여러 개의 값을 props로 전달해야 한다면,
<span style="background-color:#FFC0CB">그 값을 하나의 객체로 묶어서 스프레드 연산자를 통해 한 방에 전달하도록 할 수 있음</span></p>
<br>


<p><span style="background-color:#B0E0E6">또한, props에는 문자열이나 숫자 같은 자바스크립트 값뿐만 아니라
html요소나, 리액트 컴포넌트도 전달 가능</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/777687ba-c753-4f42-b61a-2d03b034958d/image.png" width=450>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/b088c0f0-ff02-4aa0-a661-6952c7e06317/image.png" width=550>

<p>html요소는 Button 컴포넌트에 <strong>children</strong>이라는 이름의 props로 자동으로 전달
구조분해 할당 문법에서 <strong>children</strong>이라는 이름으로,
자식 요소라는 <code>&lt;div&gt;</code>태그를 받아옴</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/f43ab744-0ad4-4b0f-a520-362e22eedb34/image.png" width=500>
<br>

<p>또한 <span style="background-color:#B0E0E6">html뿐만 아니라, 컴포넌트도 children props로 전달하는 게 가능</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/b61f8aa4-5d1a-44f4-8773-251e87f01651/image.png" width=400>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/6d98e56d-0a4f-446a-b7c7-0b33bbffd9b7/image.png" width=400>

<p>컴포넌트를 받아와 렌더링할 수 있음</p>
<hr>
<h1 id="📌-이벤트-처리하기">📌 이벤트 처리하기</h1>
<h2 id="이벤트-핸들링이란">이벤트 핸들링이란?</h2>
<p><strong>이벤트</strong>란?
웹 내부에서 발생하는 사용자의 행동
ex) 버튼 클릭, 메세지 입력, 스크롤 등등</p>
<p><strong>핸들링</strong>이란?
다루다는 뜻
➡️ <span style="background-color:#FDF5E6">이벤트가 발생했을 때 그것을 처리하는 것</span></p>
<h2 id="버튼을-클릭했을-때-어떠한-동작을-수행하게-하려면">버튼을 클릭했을 때, 어떠한 동작을 수행하게 하려면</h2>
<p>(예시)</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/b22f8716-7836-4941-a4ec-e31a0ec6fd5e/image.png" width=500>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/c4b04f00-9b4b-430b-9142-18166206a4e4/image.png">

<img src="https://velog.velcdn.com/images/ann-algorithm/post/1ed63995-4a96-40ab-971a-cb7460bdc12a/image.png">

<p><span style="background-color:#FFE4E1">버튼 클릭 시, 콘솔 로그로 props로 받은 메뉴 이름 출력</span></p>
<p>이런 식으로 <span style="background-color:#FFC0CB">클릭과 같은 이벤트가 발생했을 때 처리(실행)할 수 있도록 설정된 함수</span>를,
<strong>이벤트 핸들러</strong>라고 함</p>
<p>이벤트 핸들러를 설정하는 법
(1) 위처럼, <strong>onClick</strong> 내에 익명 함수로 설정
(2) 아래처럼, 함수를 선언하여 해당 함수 전달</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/46fbe666-44ac-438d-aa6d-dcf58002ec5a/image.png" width=500>

<p>화살표 함수(혹은 선언문) 하나를 만들어서,
버튼이 클릭되었을 때 실행할 명령문을 기재</p>
<p>따라서, <span style="background-color:#FFC0CB"><strong>onClick</strong>에 <strong>onClickButton</strong>이란 함수가 설정이 되면서,
이 버튼의 클릭 이벤트 핸들러로
<strong>onClickButton</strong>이란 함수가 설정된 것</span></p>
<h2 id="주의">주의</h2>
<p><span style="background-color:#E2D6FF">이벤트 핸들러를 설정할 때,
호출 결과를 전달하지 말 것</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/7366044f-f125-4331-b0b4-c01ec5a22e40/image.png" width=500>

<p>위 코드는 함수의 결과(반환값 등)를 전달하는 것</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/7e5a8a73-bf37-4898-a5e9-676fb327bab8/image.png" width=450>

<p>클릭 이벤트 외에 다른 이벤트도 마찬가지임</p>
<br>

<p>리액트에서 발생하는 모든 이벤트는,
이벤트 핸들러 함수를 호출하면서,
<span style="background-color:#FFC0CB">호출된 이벤트 핸들러 함수에 매개변수로, 이벤트 객체라는 것을 제공</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/4cbdfa40-ec15-4953-b0e1-6b7343747a6e/image.png" width=450>

<p>따라서 객체 e라는 매개변수를 출력해보면,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/2c9162be-0ce4-46f1-8d14-223c8fd932ef/image.png" width=400>

<p><strong>SyntheticBaseEvent</strong>라는 객체가 출력
= 매개변수 e에 저장된 이벤트 객체</p>
<p><span style="background-color:#E6E6FA">SyntheticBaseEvent의 Synthetic은 합성이란 뜻으로,
합성 이벤트 객체, 이런 뜻</span></p>
<h3 id="합성-이벤트란">합성 이벤트란,</h3>
<blockquote>
<p>모든 웹 브라우저의 이벤트 객체를 하나의 포맷으로 통일한 형태</p>
</blockquote>
<p>크롬, 사파리, 엣지, 웨일 등...
브라우저를 만드는 회사는 굉장히 많음
= 웹 브라우저마다 동작이 조금씩 다름</p>
<p>가령, IE는 최신 자바스크립트 기능은 거의 쓸 수 없음</p>
<br>

<p>내가 쓰려는 자바스크립트나, CSS의 기능이 어느 브라우저에서 되는지 확인하는 사이트도 있음</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/46f0ba42-58a3-40c3-9bb6-f20c731e3315/image.png" width=500>

<p><span style="background-color:#FDF5E6">이러다 보니까 이벤트 객체도 브라우저마다 조금씩 다름</span></p>
<p>예를 들면, 크롬에서는 이벤트가 발생한 요소를 가져오는 타겟이라는 속성을, 다른 브라우저에서는 다르게 부를 수도 있다는 것임</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/984cce6d-be91-42b2-ac1c-6dd21d242617/image.png" width=500>

<p>따라서 크로스 브라우징 이슈를 해결해주는 친구가
리액트의 합성 이벤트</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/8f28a6dd-1f0b-47ca-9db7-2353d30a38db/image.png" width=500>

<p><span style="background-color:#FFEBCD">여러 브라우저의 규격을 참고해서 하나의 통일된 규격으로 이벤트 객체를 포맷팅</span>
➡️ 일종의 통합 규격</p>
<p>따라서 리액트 개발자는 브라우저별 이벤트가 다른 건 신경쓸 필요가 없고
이 합성 이벤트 객체만 잘 사용하면 됨</p>
<h4 id="정리-2">정리</h4>
<p>SyntheticBaseEvent객체란, <span style="background-color:#FFE4E1">모든 브라우저에서 사용할 수 있는 통합된 규격의 이벤트 객체</span>임</p>
<p>따라서, 이 합성 이벤트 객체 안에는 발생한 이벤트와 관련된 모든 정보가 다 들어있음
➡️ 리액트에서는 이러한 합성 이벤트 객체를 제공하기 때문에 크로스 브라우징 이슈에서도 비교적 자유로움</p>
<hr>
<h1 id="📌-state로-상태관리">📌 State로 상태관리</h1>
<h2 id="state란">State란?</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/161ca03d-3d4b-4ef9-8f4d-5bc4391e1c04/image.png" width=500>

<p>어떤 사물이 현재 가지고 있는 모양이나 형편
모든 사물은 자신이 갖는 현재의 상태, 즉 현재의 state에 따라서 각각 다른 모양이나 다른 동작을 함</p>
<p>만약 전구를 끄면, 켜진 상태의 전구 ➡️ 꺼진 상태의 전구
만약 전구를 켜면, 꺼진 상태의 전구 ➡️ 켜진 상태의 전구</p>
<p>이렇듯 state, 상태란 <span style="background-color:#FDF5E6">어떠한 사물이 현재 가지고 있는 모양이나 형태를 정의하는 값이면서,
동시에 변화할 수 있는 동적인 값</span></p>
<p><span style="background-color:#FFC0CB">리액트의 컴포넌트는 자신의 형태나 모양을 정의하는 이런 state를 가질 수 있음</span>
➡️ <span style="background-color:#E2D6FF">state 값(현재의 상태)에 따라, 각각 다른 UI를 화면에 렌더링</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/5ce81549-424f-4cf4-a00e-e0e3425e941f/image.png" width=500>

<p>예를 들면, 전구를 렌더링하는 컴포넌트를 만듦
이 전구의 state 값이 off면, 꺼진 상태의 전구 렌더링</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/45644a90-8068-42a4-a793-a90b937fab6d/image.png" width=500>

<p>이 전구의 state 값이 on으로 바뀌면,
<span style="background-color:#FDF5E6">리액트가 state의 값이 바뀐 걸 감지해서</span> 컴포넌트를 다시 렌더링</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/569f8b55-c5af-468a-9ff4-ad61bef58a1e/image.png" width=500>

<p>따라서, 켜진 상태의 전구가 렌더링됨</p>
<p>➡️ <span style="background-color:#FFEBCD">이렇게 컴포넌트가 다시 렌더링되는 걸 <strong>리렌더</strong> 또는 <strong>리렌더링</strong>이라고 함</span></p>
<br>

<p>결론적으로 리액트에서는 
각각의 컴포넌트에 이 컴포넌트의 상태를 의미하는 값이자,
변할 수 있는 값인 state를 만들 수 있으며,
<span style="background-color:#FFC0CB">하나의 컴포넌트에 여러 개의 state를 만드는 것도 가능</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/4744048b-460e-4c66-b690-4a1f37feab82/image.png" width=500>

<h2 id="실습-2">실습</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/5f42e62a-04cb-437b-80bd-b176c37635a7/image.png" width=450>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/b29f7c6c-8727-4265-a967-f43782f4426f/image.png" width=450>

<p><span style="background-color:#FFC0CB">두 개의 요소가 들어간 배열 출력</span></p>
<ul>
<li>첫 번째 요소 : 값이 없음<ul>
<li>새로운 state 값</li>
<li>지금은 초기값을 넣지 않아서 <strong>undefined</strong></li>
</ul>
</li>
<li>두 번째 요소 : 함수<ul>
<li>state 값을 변경하는, 즉 상태를 변화시키는 함수가 들어 있음</li>
<li><strong>상태 변화 함수</strong>라고 부름</li>
</ul>
</li>
</ul>
<p>새로운 state를 생성하는 <strong>useState</strong>라는 함수는,
인수로는 state의 초기값을 받아서
➡️ 두 개의 요소를 담은 배열 반환
<span style="background-color:#FFE4E1">그리고 배열의 구조 분해 할당 문법을 이용하여 바로 사용</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/de99ac14-75d8-4f85-ad67-2508d415076d/image.png" width=450>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/51c4e1b6-2ce7-41dd-8a75-934a22f0fe68/image.png" width=450>

<p>따라서 할당받은 변수로 바로 사용할 수 있음</p>
<p>가령, 아래처럼 구조 분해 할당 문법을 사용하지 않고 바로 변수에 저장하면</p>
<pre><code class="language-jsx">const result = useState(0); 
// result는 [현재값, 값을 업데이트하는 함수]가 들어있는 배열</code></pre>
<p>각 요소에 접근하기 위해 <code>result[0]</code>, <code>result[1]</code> 이런 식으로 써야 함</p>
<h3 id="버튼을-만들어서-클릭할-때마다-state를-증가시키기">버튼을 만들어서, 클릭할 때마다 state를 증가시키기</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/2cbf6813-6db0-457d-9165-7ee6370c1f78/image.png" width=500>

<p>컴포넌트 내에 새로운 state 생성
버튼을 클릭했을 때 state값을 변경하도록 하면,
➡️ 리액트가 내부적으로 이 state의 변경 여부를 감지해서
이 컴포넌트를 리렌더링</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/814b20ee-449f-4d98-8ec9-a9e5a351a9e2/image.png" width=450>

<p>함수 컴포넌트를 리렌더링한다는 말은,
이 컴포넌트 역할을 하고 있는 App 함수를 다시 호출하고,
새롭게 반환한 값을 화면에 다시 렌더링</p>
<h4 id="리렌더링-동작-과정-정리">리렌더링 동작 과정 정리</h4>
<pre><code>- App 컴포넌트가 최초로 렌더링될 때는 state 값이 초기값인 0
- 클릭해서 state 값이 업데이트가 되면 App 컴포넌트의 역할을 하는 함수가 다시 호출됨
- 그러면 state값이 1인 상태로 즉, 업데이트가 된 상태를 다시 리턴문을 통해서 반환
- 업데이트된 state의 값이 화면에 즉각적으로 반영</code></pre><p>단순하게 따지면
컴포넌트의 state 값이 바뀌면 컴포넌트가 값을 리턴하고,
화면을 다시 그림</p>
<h3 id="새로운-state--전구의-onoff를-저장하는-state-만들기">새로운 state : 전구의 on/off를 저장하는 state 만들기</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/b57f2a68-7200-47e3-a113-90d8dd0bafd6/image.png" width=500>

<p>위와 같이 새로운 state를 추가하고,
버튼을 누르면 해당 state의 상태값을 ON/OFF로 바꾸는 이벤트 핸들러 추가</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/de829886-01a7-45a4-8c57-c921da219695/image.png" width=500>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/fd182137-363a-4172-a071-4074ed4c0f9c/image.png" width=300>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/3a19c6ef-6d80-43c9-8ae5-131d0486b0b8/image.png" width=300>

<p>상태에 따라 버튼의 텍스트도 변경 가능</p>
<br>

<h3 id="usestate-대신-let-const로-자바스크립트-변수를-만들면">useState 대신 let, const로 자바스크립트 변수를 만들면?</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/279399f5-4986-4278-b76c-2f4f6740bb0e/image.png" width=500>

<p>할 수는 있음</p>
<p>그러나 변수로 전구의 ON/OFF 상태를 저장하면,
<span style="background-color:#FDF5E6">버튼을 클릭해서 변수의 값이 바뀌어도 컴포넌트가 리렌더링 하지 않음</span>
➡️ 따라서 버튼을 클릭해도 변화가 일어나지 않음</p>
<p><span style="background-color:#E2D6FF">리액트 컴포넌트는 일반적인 변수가 아니라 state 값이 변화했을 때만 리렌더링</span></p>
<hr>
<h1 id="📌-state를-props로-전달하기">📌 state를 props로 전달하기</h1>
<h2 id="전구-불-밝히기">전구 불 밝히기</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/a6072e3f-13c2-499f-a8cb-fe0d1f8598a3/image.png" width=500>

<p>전구의 색상을 바꾸는 부분을 컴포넌트로 분리
버튼을 눌러 App 컴포넌트의 state를 바꾸면,
Bulb 컴포넌트가 리렌더링
➡️ Bulb와 같은 자식 컴포넌트는, 부모로부터 받는 props의 값이 바뀌면 리렌더링 발생</p>
<pre><code class="language-jsx">const Bulb = ({ light }) =&gt; {
  console.log(light); // light 상태 확인 콘솔 로그 추가
  return (
    &lt;div&gt;
      {light === &quot;ON&quot; ? (
        &lt;h1 style={{ backgroundColor: &quot;orange&quot; }}&gt;ON&lt;/h1&gt;
      ) : (
        &lt;h1 style={{ backgroundColor: &quot;gray&quot; }}&gt;OFF&lt;/h1&gt;
      )}
    &lt;/div&gt;
  );
};</code></pre>
<p>버튼 클릭 시 ➡️ props로 제공되는 라이트의 값이 변경 ➡️ Bulb 컴포넌트 재호출 ➡️ 전구 다시 렌더링</p>
<h2 id="근데-카운터도-같이-증가-중">근데 카운터도 같이 증가 중</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/a51b17eb-497f-4588-81b3-929eae481746/image.gif" width=500>

<p>클릭해서 카운트를 올리는 count state 값만 변경되길 기대했는데,
(Bulb에게 props로 전달한 light state의 값을 변경한 게 아닌데)</p>
<p><span style="background-color:#FFE4E1">왜 <code>+</code>를 클릭하면, 콘솔로그로 light의 상태를 출력하면서, Bulb 컴포넌트가 리렌더링 되는 것인가?</span></p>
<h3 id="리액트-컴포넌트가-리렌더링하는-세-가지-상황">리액트 컴포넌트가 리렌더링하는 세 가지 상황</h3>
<ol>
<li>자신이 관리하는 state의 값이 변경될 때</li>
<li>제공받는 props의 값이 변경될 때</li>
<li>부모 컴포넌트가 리렌더링되면, 자식 컴포넌트도 리렌더링
➡️ 그래서 지금 Bulb 컴포넌트가 계속 리렌더링</li>
</ol>
<p><code>+</code> 버튼을 클릭하면 App 컴포넌트의 count state의 값이 변경됨
1번 조건에 따라, App 컴포넌트는 자신이 가진 state의 값이 변경되어서 App 컴포넌트가 리렌더링</p>
<p>Bulb 컴포넌트는 App 컴포넌트의 자식 컴포넌트이므로, 
3번 조건에 따라 App 컴포넌트가 리렌더링되어, Bulb 컴포넌트도 리렌더링 발생</p>
<br>

<p>Bulb 컴포넌트는 count의 증가와는 아무 관련이 없는데
부모 컴포넌트가 리렌더링됨으로써 불필요하게 리렌더링 되는 중
➡️ 이와 같은 자식 컴포넌트들이 불필요하게 리렌더링되면, 성능이 저하될 수밖에 없음</p>
<h2 id="컴포넌트-분리">컴포넌트 분리</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/3ae21e57-1f00-44ed-8e91-fdfee8243a36/image.png" width=500>

<p><span style="background-color:#FFC0CB">이렇게 관련 없는 두 개의 state는 하나의 컴포넌트에 몰아두지 말고 서로 다른 컴포넌트로 분리</span></p>
<h3 id="counter-컴포넌트">counter 컴포넌트</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/d70c85ef-f0ee-4f6b-a06b-c45f331cbf24/image.png" width=500>

<p>Counter 컴포넌트를 분리하고, 각 컴포넌트가 state를 가짐</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/0f7eae5e-6f9c-4f5e-92c9-c38ca08ce074/image.gif" width=500>

<p>이후, 각 컴포넌트가 state를 각자 관리하므로
불필요한 리렌더링을 막음</p>
<h3 id="파일-분리">파일 분리</h3>
<p>각 컴포넌트는 파일별로 분리할 수 있음</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/4d5bbbd5-bb99-4ec2-9e7b-c79460a52d16/image.png" width=400>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/2464a413-d6a4-4942-a2ed-3ec9e2de16b1/image.png" width=500>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/20249a28-86c0-45d4-9a83-e15c442bbf97/image.png" width=500>

<hr>
<h1 id="📌-state로-사용자-입력-관리하기-1">📌 state로 사용자 입력 관리하기 1</h1>
<h2 id="회원가입-폼을-렌더링하는-컴포넌트-만들기">회원가입 폼을 렌더링하는 컴포넌트 만들기</h2>
<h3 id="컴포넌트-준비">컴포넌트 준비</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/c277a5ca-27df-43f2-a388-b5f42e0dc5a1/image.png" width=500>

<p>App 컴포넌트와</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/d0fb25f6-9600-4cbd-8edb-658df06f4547/image.png" width=450>

<p>회원가입 폼을 위한 컴포넌트</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/8fa8dadf-7d0a-446d-b181-8695ea1d42e3/image.png" width=450>

<h3 id="이름-입력받기">이름 입력받기</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/d49d60e9-0759-4e79-8ff5-4fe5497a33b5/image.png" width=500>

<p>콘솔 로그로 이벤트 객체를 출력하면,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/9531f908-550b-48de-8f64-ff3ea070b8cb/image.png" width=500>

<p>위와 같이 출력되고,
이 이벤트 객체에는 우리가 찾고 있는 inpout에 사용자가 입력한 값이 그대로 있음</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/e944a777-8488-4c21-a92a-c0a747eae836/image.png" width=500>

<p>➡️ 즉, <code>e.target.value</code> 안에는 input에 입력한 텍스트가 있음</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/1e417860-a227-42b6-862b-d657166e5b65/image.png" width=500>

<p>최종적으로,
입력한 값들이 위 코드의 로직을 통해 name state에 저장될 것</p>
<br>

<p>만약 input 폼 안에 초기값을 설정하고 싶다면
name이란 state를 생성할 때, 초기값을 넣으면 됨</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/13600cad-11e2-4bc8-87ba-005950487590/image.png" width=600>

<p>그리고 name을 <span style="background-color:#FFC0CB">input의 value</span>로 설정</p>
<p>보통 리액트의 상태를 이용해서,
사용자의 입력을 저장하고 처리할 때에는
이렇게 초기값을 설정하는 경우도 꽤 많이 존재
➡️ <span style="background-color:#E2D6FF">onChange 속성만 설정하지 말고,
value 속성까지 함께 설정해야 함</span></p>
<h3 id="생년월일-입력받기">생년월일 입력받기</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/ff9fa0dc-8304-4971-8f92-aef46567184b/image.png" width=600>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/ce5ce641-d036-445a-9d27-f25ee19ab72c/image.png" width=350>

<p>날짜 선택기인 date picker 렌더링
(이 모양은 브라우저에 따라서 다를 수 있음)</p>
<br>

<p>생년월일 input에 사용자가 입력하는 값도 state를 통해서 보관</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/ed2d26d3-7388-4c29-a296-6ee887f8c407/image.png" width=600>

<br>

<h3 id="국적-입력받기">국적 입력받기</h3>
<p><span style="background-color:#FFE4E1">국적은 셀렉트 박스로 입력받기</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/3d584ceb-ccf1-4d2a-8fe3-2a5c8d674de1/image.png" width=600>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/73d4620c-7bdd-471e-a65b-525bf16e1c00/image.png" width=250>

<p>초기값으로 한국 설정
<span style="background-color:#FDF5E6">셀렉트 태그는 기본적으로 옵션 중에 맨 위 옵션을 초기값으로 사용하기 때문</span></p>
<br>

<p><span style="background-color:#FFE4E1">만약 아무것도 선택되지 않은 상태로 초기값을 설정하고 싶은 경우 빈 옵션 초기값으로 설정</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/f53ec5e8-8d27-46e5-b459-69c407a85dc9/image.png" width=400>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/2da5b0f7-a060-49de-b1f0-139c8c062944/image.png" width=250>

<br>

<p>이제 셀렉트 태그에 사용자들이 선택하는 값도 state를 통해 보관</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/8253925e-3f7a-45bf-9fbb-cde02fd66e93/image.png" width=500>

<br>

<p>전체 적용 코드</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/2c8e3a1f-4b50-4ee5-b5ae-7d2efbf4739a/image.png" width=600>

<br>

<p>셀렉트 박스의 경우
<span style="background-color:#FFC0CB">화면에 실제로 표시되는 선택지와, 실제 코드 상에서 사용할 value 값을 다르게 선택 가능</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/fd1ce90d-8ccd-463e-9e44-12e909ad0362/image.png" width=500>

<p>한국이라는 option의 value를 kr로 설정하면,
<span style="background-color:#FDF5E6">한국을 선택했을 때 실제로 코드 내에서는 kr이라는 value로 저장됨</span></p>
<p><span style="background-color:#FFE4E1">따라서 선택지에는 친절하고 길게 텍스트를 명시하고 내부적인 value로는 더 간결한 값을 사용하는 경우가 많음</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/0d27c771-a84e-4e12-9b48-5dd68281ead0/image.png" width=600>

<h3 id="자기소개-폼">자기소개 폼</h3>
<p><span style="background-color:#FFE4E1">input 폼이 아닌 textarea 폼으로 입력 받음</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/080bfe64-3759-45b7-b8f0-6bd451ded6f9/image.png" width=300>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/66af4228-e2a7-4287-a0fc-8ccd6e502923/image.png" width=300>

<p><span style="background-color:#FFC0CB">input 태그와 달리 여러 줄을 입력 가능</span>
<span style="background-color:#B0E0E6">그러나 내부 동작은 input 태그와 완전히 같음</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/034b86bf-5441-4c40-97be-5e0827c8d2f0/image.png" width=600>

<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/ff047ecf-fa1e-4ab2-8535-a14962070d64/image.png" width=300>


<p>회원가입 폼 완성</p>
<hr>
<h1 id="📌-state로-사용자-입력-관리하기2">📌 state로 사용자 입력 관리하기2</h1>
<p>위 섹션에서는 Register 컴포넌트 안에
네 가지 정보를 입력 받는 폼을 각각 만들고
해당 폼에 입력되는 정보를 각각 새로운 state를 생성하여 따로따로 보관</p>
<p>따라서 코드를 보면 비슷한 부분이 굉장이 많음</p>
<pre><code>모든 이벤트 핸들러에서 set함수로 상태 변화 함수를 호출하고,
인수로는 e.target.value를 똑같이 전달하고,
4개의 state가 사실상 같은 방식으로 활용되고 있음</code></pre><p>이 방식이 반복되니 비효율적이라고 판단됨</p>
<br>

<p>모든 state를 하나의 객체로 묶어 관리하면,
여러 개의 state를 만들지 않아도 될 것 같고...</p>
<p>이벤트 핸들러도 하나로 합쳐볼 수 있을 것 같고...</p>
<br>

<p>리팩토링 해보자</p>
<h2 id="상태-정리">상태 정리</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/b5bc6a8a-622e-4329-ac26-86f96bb071aa/image.png" width=400>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/9f51bbdb-d52d-4ffc-9866-9b2b1501652c/image.png" width=500>

<p>(위에 써있는 대로)
새로운 객체를 생성해서,
name 프로퍼티의 값을 수정하고,
그 외에 기존의 input state에 들어있던 프로퍼티 값들(birth, country)를
스프레드 연산자를 이용해서 기존의 값 유지
➡️ 변경하고자 하는 값만 바꿔주도록 코드 작성</p>
<p>name만 바꾸고 싶어도 <code>setInput({ name: 새값 })</code>처럼 일부만 넣어서 업데이트하면
React는 객체를 “합쳐서” 업데이트해주지 않고 통째로 교체</p>
<p><code>...input</code>을 빼면</p>
<pre><code>setInput({ name: newName });</code></pre><p>이렇게 되면 state가 아예 <code>{ name: newName }</code>만 남고,
기존에 있던 birth, country, bio는 객체에서 없어져버림</p>
<p>name만 바꾸고 싶어도 일부만 넣어서 업데이트하면
React는 객체를 “합쳐서” 업데이트해주지 않고 통째로 교체
➡️ <span style="background-color:#FDF5E6">기존의 값들은 유지하고, 변경하고자 하는 값만 바꿔주도록 코드 작성</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/f73e9b7c-0226-4adc-a3b6-5baf3ce991b3/image.png" width=650>

<p>전체적으로 코드에 적용한 모습</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/60abe126-02af-4302-be7a-66b02915f7c6/image.png" width=500>

<p>적용한 모습.
일단 빈 객체를 출력</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/e0289975-2114-4d78-a07d-f5c9dcad22db/image.png" width=500>

<p>이름을 쓰자. 이름을 입력하는 만큼 name 프로퍼티가 업데이트되는 것 확인</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/69de1d77-373b-48b6-bd0f-48fcb947d3a3/image.png" width=500>

<p>생일 프로퍼티를 바꿨지만,
name 프로퍼티는 그대로 유지되었음</p>
<br>

<p>이렇게 여러 개의 state로 나눠서 관리하던 네 가지의 데이터를,
객체 형태로 만들어서 하나의 state로 관리하는 방법에 대해 배웠음
➡️ 나중에 복잡한 상태를 직접 관리해야 할 때 큰 도움이 될 것</p>
<h2 id="핸들러-정리">핸들러 정리</h2>
<p>여전히 핸들러는 4개가 존재하고 있음</p>
<p>이벤트 핸들러를 다 지워주고 아래와 같은 함수를 작성</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/c9e7cb8e-3b0b-48ea-8bda-154832249aee/image.png" width=450>

<p><code>e.target.name</code> : 그 input의 name 속성 값 (예: <code>&quot;name&quot;</code>, <code>&quot;birth&quot;</code>, <code>&quot;country&quot;</code>)
<code>e.target.value</code> : 사용자가 입력한 값</p>
<pre><code>&lt;input name=&quot;name&quot; value={input.name} onChange={onChange} /&gt;
&lt;input name=&quot;birth&quot; value={input.birth} onChange={onChange} /&gt;
&lt;input name=&quot;country&quot; value={input.country} onChange={onChange} /&gt;</code></pre><p>input의 name 속성 값은 그냥 HTML에서 input 태그에 붙이는 “이 입력칸의 키(이름표)”임</p>
<p>따라서, 해당 input 태그에 붙은 이름표를 프로퍼티의 키, 입력한 값을 밸류로 받아 set함수를 통해 상태를 업데이트함</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/19be621e-6d3f-48c2-b113-f375c2c9e79f/image.png" width=600>

<p>이렇게 작성하니 전체적인 코드가 깔끔해졌음</p>
<h2 id="동작원리">동작원리</h2>
<p>모든 input, 모든 select, 모든 textarea 태그의 <strong>onChange</strong> 이벤트 핸들러를
방금 만든 통합 이벤트 핸들러 <strong>onChange</strong>로 설정했기 때문에
이제 어디에 어떤 값을 입력하든 이 <strong>onChange</strong>가 실행</p>
<p>이 함수가 실행되면 setInput 상태 변화 함수를 호출하고,
인수로 객체를 만들어서 전달
스프레드 연산자로 기존 input의 값을 나열하고,
마지막에는 프로퍼티의 키를 명시하는 자리에 대괄호를 열고
이 대괄호 안에 <code>e.target.name</code>을 넣음</p>
<p><code>e.target.name</code>에는 뭐가 있을까?
<span style="background-color:#FDF5E6">이벤트가 발생한 태그의 name 속성에 설정된 값이 들어있음</span></p>
<br>

<p>만약 브라우저에서 생년월일을 입력하면</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/3cefff86-8f36-40e1-b29e-24c0975bb2c3/image.png">

<p>코드 상에서 위 input 태그의 이벤트 발생</p>
<p>이때 이벤트의 타겟, 즉 e.target은 input 태그이고,
이 태그의 name은 birth
➡️ 그래서 <code>birth: e.target.value</code>처럼 코드가 작성하는 것</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/e6082815-c65a-4fee-a45e-f1921341b32e/image.png" width=500>

<p>입력 이벤트의 name과 value를 출력해보자</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/feed763b-1271-4f83-980d-395a52448a88/image.png" width=500>

<p>name 속성에 입력하는 경우 이벤트 발생 로그</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/552849c2-9a4f-4838-b564-7bd2ced013f3/image.png" width=500>

<p>birth 속성에 입력하는 이벤트 발생 로그</p>
<hr>
<h1 id="📌-useref로-컴포넌트의-변수-생성하기">📌 useRef로 컴포넌트의 변수 생성하기</h1>
<h2 id="useref">useRef</h2>
<blockquote>
<p>새로운 Reference 객체를 생성하는 기능</p>
</blockquote>
<pre><code class="language-js">const refObject = useRef();</code></pre>
<p>이렇게 생성한 레퍼런스 객체는 컴포넌트 내부의 변수로서 일반적인 값들 저장
그래서 어쩌면, useState와 비슷
➡️ 두 기능 모두 우리가 컴포넌트 내부에서 쓰고 싶은 변수를 생성한다는 점에서 동일</p>
<p>값이 변경되었을 때 컴포넌트를 리렌더링 시키는 useState와 달리,
<span style="background-color:#FFEBCD">useRef로 생성한 변수는 값이 변경되더라도 컴포넌트를 리렌더링시키지는 않음</span>
<span style="background-color:#FFC0CB">➡️ 렌더링에 영향을 미치고 싶지 않은 변수를 생성할 때 useRef 이용</span></p>
<p>또한, <span style="background-color:#FFEBCD">useRef를 이용하면, 컴포넌트가 렌더링하는 특정 DOM 요소에 접근 가능</span>
➡️ 해당 요소 조작이 가능</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/33bc0852-169f-4e76-acfe-464ff68dbfe3/image.png" width=500>

<p>위처럼, 특정 요소에 포커스를 준다든지,
혹은 해당 요소의 스타일을 갑자기 변경시키는 동작도 손쉽게 구현 가능</p>
<h2 id="실습1️⃣">실습1️⃣</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/45bfcbe4-a307-44dd-9970-a0db74b2d24f/image.png" width=500>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/96534ab9-d488-44f3-8a2d-ccb4ea59e87d/image.png">

<p>방금 생성한 레퍼런스 객체</p>
<p>결국 레퍼런스 객체란
<span style="background-color:#FFEBCD">current 프로퍼티에 현재 보관할 값을 담아두기만 하는 자바스크립트 객체</span></p>
<br>

<p>그렇기 때문에 useRef로 새로운 레퍼런스를 생성할 때
인수로 0이라는 초기값을 전달하면,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/6733adf1-0fb3-4806-974c-96d505a36454/image.png" width=500>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/87eac457-e4a5-4db1-a379-93e0f6ca66b5/image.png" width=500>

<p>current프로퍼티에 초기값을 저장한 레퍼런스 객체 확인
이런 식으로 초기값을 설정하는 것도 가능</p>
<p>cuurent 같은 프로퍼티(=레퍼런스 객체의 값)를 사용하고 싶으면, 점 표기법으로 접근하면 됨</p>
<h3 id="특징-이-객체는-값이-변되었다고-컴포넌트를-리렌더링시키지-않음">특징: 이 객체는 값이 변되었다고 컴포넌트를 리렌더링시키지 않음</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/6353be99-310a-4bd7-8295-d8fc1ce909af/image.png" width=500>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/a1f5ce3f-458d-42a1-8375-33db1f5dead0/image.png">

<img src="https://velog.velcdn.com/images/ann-algorithm/post/9f90b903-73f5-4611-9ead-027b81daf69e/image.png">

<p><code>ref+1</code> 버튼을 클릭하면,</p>
<ul>
<li>온클릭 이벤트 핸들러로 refObj의 current 값을 1 증가</li>
<li>해당 값을 출력</li>
</ul>
<p>숫자가 증가하는 레퍼런스 객체의 값이 출력되지만
리렌더링을 유발하지 않기 때문에 onClick 이벤트 핸들러만 실행되는 중</p>
<br>

<p>리렌더링이 안 되는 걸 확인해보기 위해 아래와 같이 실습</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/9de50881-385c-4d0f-ba83-da507063e621/image.png" width=500>

<p>레퍼런스 객체를 이렇게 선언하고, 페이지 새로고침</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/6353be99-310a-4bd7-8295-d8fc1ce909af/image.png" width=500>

<p>위에서 실습했던
ref+1 버튼을  만들고,
<span style="background-color:#FFC0CB">해당 버튼을 계속 클릭해도 이벤트 핸들러만 실행될 뿐
위 &quot;Register 렌더링&quot; 콘솔 로그는 출력되지 않음
</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/7f8a44c2-d0c9-4f1f-8c47-b57de9715a2f/image.png" width=500>

<p>➡️ refObj는 컴포넌트 내부에서 렌더링에 영향을 미치지 않아야 되는 변수를 생성할 때 활용할 수 있음</p>
<br>

<p>이제 이 레퍼런스 객체를 이용해서
레지스터 컴포넌트가 렌더링하고 있는 4개의 폼에, 사용자가 얼마나 많은 횟수의 변경을 일으켰는지
수정 횟수를 카운트하는 기능을 만들어보자</p>
<p>버튼 태그는 삭제하고,
레퍼런스 객체의 current의 숫자를 조정하였음</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/cdbb9b5c-b956-4666-8ee7-3b471d5ea68d/image.png" width=500>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/8f54a82c-c1b7-4c35-807b-62ee252d70b6/image.png" width=500>

<p>수정횟수를 잘 출력하는 것 확인</p>
<h2 id="실습2️⃣">실습2️⃣</h2>
<p>새로운 레퍼런스 객체를 생성해서,
<span style="background-color:#FDF5E6">이 컴포넌트가 렌더링하는 DOM 요소를 직접 조작</span>
회원 가입 제출 기능을 구현하자</p>
<br>

<p>제출 버튼 생성</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/5cc005a6-bc27-43ce-b52d-182073efe79a/image.png" width=500>

<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/3b618a47-e6b5-4fff-9645-833e625011e3/image.png" width=500>

<p>제출 시 이름을 제대로 입력했는지 확인할 것</p>
<p>만약 이름을 입력하지 않은 경우
이름을 입력하는 DOM 요소 포커스
➡️ return문 안에 있는 이름을 입력하는 DOM 요소인 input 태그에 접근할 수 있어야 함</p>
<p>어떻게 접근?
➡️ 레퍼런스 객체를 이용할 것</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/8396aaaa-581a-4240-9d93-231368d2e661/image.png" width=500>

<p>새로 만든 레퍼런스 객체를
input 태그의 ref 속성으로 넣어줌</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/7e08c32d-2f15-4881-a9a1-fb58ca74020f/image.png" width=450>

<p><span style="background-color:#FFC0CB">이렇게 하면 input 태그가 렌더링하는 DOM요소가
inputRef라는 레퍼런스 오브젝트에 저장됨</span></p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/e3c9ecb8-b86c-4ce3-8584-bfdb78437849/image.png" width=500>

<p>이제 제출 버튼이 클릭되었을 때
inputRef.current 값을 출력하도록 작성</p>
<p>새로고침 후 제출 버튼을 누르면</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/5f7372f9-39d7-49c0-b36e-85e6a74c1b9d/image.png" width=500>

<p>콘솔 로그로 input 태그와 DOM요소가 잘 출력되는 것 확인
(근데 지금 봤는데 저기 왜 timeLog지?)</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/9d1c7973-319e-43f8-bfd8-e6357a6c9f34/image.png" width=450>

<p>inputRef.current 값에 우리가 접근하고자 하는 DOM요소(input태그)가 저장되어 있다는 걸 아니까,
여기에 focus라는 메서드 호출</p>
<p>브라우저 새로고침 후 제출 버튼 클릭 시
이름을 입력하는 DOM요소의 포커스가 잘 실행되는 것 확인 가능</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/116f96e1-352a-4087-a662-70abbcfcd9da/image.gif" width=500>

<h2 id="심화적인-내용">심화적인 내용</h2>
<p>컴포넌트 내부에서 리렌더링을 유발하지 않는,
countRef 같은 변수를 만들고 싶다면,
사실은 useRef라는 기능을 이용하지 않아도</p>
<pre><code class="language-js">let count = 0</code></pre>
<p>이렇게 변수로 나타낼 수 있지 않을까?</p>
<p>즉, 왜 굳이 useRef를 써야 하는가?</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/0a13027b-3218-447a-a858-5269790474c7/image.png" width=500>

<p>그 결과,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/af320763-3d55-4815-a58f-9ade66a3ade7/image.png">

<p>아무리 많은 수정을 해도 그냥 1만 출력
➡️ count 변수의 값이 1로 고정이 되어 있음</p>
<br>

<p>input 값을 입력했는데, 왜 저런 현상이 나타날까?</p>
<pre><code>onChange 이벤트 핸들러가 실행되면서 state의 값을 변경
setInput으로 인하여 ➡️ 레지스터 컴포넌트가 리렌더링됨

컴포넌트가 리렌더링되면서,
컴포넌트 역할을 하는 레지스터 함수가 다시 호출되고
함수 안에 있는 코드도 다시 실행
➡️ let count = 0도 다시 실행
즉, 리셋되었다.

따라서, 브라우저에서 이름이나 생년월일을 수정하면
count 변수의 값은 0으로 다시 리셋
따라서 출력되는 값은 1로 고정</code></pre><p>그러나 <span style="background-color:#FFE4E1">useRef나 useState를 이용해서 만든
리액트의 특수한 변수는 컴포넌트가 리렌더링 되어도 리셋되지 않음</span></p>
<p>따라서 컴포넌트 내부의 변수가 필요하다면,
let 키워드가 아닌,
useRef나, (렌더링에 영향 주고 싶으면) useState로 만들어야 함</p>
<br>

<p>그러면,
컴포넌트가 리렌더링될 때 count라는 변수가 초기화되지만 않으면 문제 없지 않음?
그럼 count 변수의 선언을 컴포넌트 외부에 한다면?</p>
<p>실제로 그러면 카운팅은 잘 됨</p>
<p>그러나 이게 좋은 방법도 아니고, 문제 해결법도 아님</p>
<p>왜냐하면,
레지스터 컴포넌트를 한 번만 렌더링하는 상황이면 문제가 없지만,</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/1240ea36-1202-4e3e-b81c-080c763186a1/image.png" width=300>

<p>위와 같이 App.jsx의 앱 컴포넌트에서,
레지스터 컴포넌트를 두 번 렌더링하면 문제가 발생함</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/29869afa-bfc3-498d-a4d0-2fe337ca73c4/image.png">

<p><span style="background-color:#FDF5E6">두 개의 레지스터 컴포넌트가 하나의 변수를 공유하게 됨</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/fead746a-ea8d-4787-8738-07b2d38f92cc/image.png">

<p>우리가 기대한 건 각각 컴포넌트별로 카운트를 따로 세는 거지,
위와 같이 하나의 변수를 공유하는 게 아님</p>
<br>

<p>앱 컴포넌트에서 레지스터 컴포넌트를 두 번 렌더링 한 건,
register.jsx 라는 자바스크립트 파일에 있는 레지스터 함수를 그냥 두 번 호출한 것</p>
<p>그러면 <u>파일 전체가 두 번 실행된 게 아니라,</u>
Register 함수만 두 번 호출
➡️ 따라서 count라는 변수는 한 번만 선언 되었고,
두 개의 함수가 똑같은 변수를 나눠 쓰고 있음</p>
<br>

<p><span style="background-color:#FFC0CB">리액트에서는 특별한 경우가 아니면,
컴포넌트 외부에 변수를 선언하는 게 권장되지 않음</span></p>
<p><span style="background-color:#FDF5E6">따라서 이러한 변수를 사용하고 싶다면, useRef를 이용하는 게 가장 좋다.</span></p>
<h1 id="📌-react-hooks">📌 React Hooks</h1>
<blockquote>
<p>📝 <strong>리액트 훅</strong>
클래스 컴포넌트의 기능을 함수 컴포넌트에서도 이용할 수 있도록 도와주는 메서드</p>
</blockquote>
<p>2017년 이전에
과거의 리액트에서 대부분의 사람들은 클래스로만 컴포넌트를 만듦</p>
<table>
<thead>
<tr>
<th>Class 컴포넌트</th>
<th>Function 컴포넌트</th>
</tr>
</thead>
<tbody><tr>
<td>모든 기능을 이용할 수 있음</td>
<td>UI 렌더링만 할 수 있음</td>
</tr>
<tr>
<td>ex) State, Ref, etc...</td>
<td></td>
</tr>
</tbody></table>
<p>지금처럼 함수로도 만들 수 있었지만,
리액트의 모든 기능을 이용하려면 클래스 컴포넌트를 사용해야 해서
대부분은 클래스 컴포넌트 사용</p>
<p>근데 클래스 컴포넌트는,
클래스란 문법을 이용해야 해서
함수 컴포넌트에 비해 굉장히 복잡</p>
<p>문법이 간결한 함수 컴포넌트에서도 리액트의 모든 기능을 이용하고 싶었음</p>
<p>따라서, 리액트 훅이라는 기능 개발</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/a15b5e70-3493-478e-ab3d-2f153bc5be72/image.png" width=500>

<br>

<p>함수 컴포넌트에서도, 클래스 컴포넌트의 기능을 마치 낚아채듯 가져와서 사용할 수 있게 하기에
➡️ 훅이라고 명명</p>
<p>그래서 굳이 문법이 복잡한 클래스 컴포넌트를 쓸 필요가 없고,
<span style="background-color:#FDF5E6">특별한 상황이 아니면 모든 컴포넌트를 다 함수 컴포넌트로 만듦</span></p>
<p><em>위에서 살펴본 useRef, useState 같은 리액트의 내장 함수는 사실 모두 리액트 훅이었음</em></p>
<ul>
<li>useState : State 기능을 낚아채오는 Hook</li>
<li>useRef: Reference 기능을 낚아채오는 Hook</li>
</ul>
<p>두 함수는 모두 use라는 접두사를 쓰고 있는데,
<span style="background-color:#FDF5E6">리액트 훅에는 이름 앞에 use라는 접두사가 붙는 특징이 있음</span></p>
<p>그리고 이 각각의 훅은 단수형인 Hook, 개념 자체를 가리키는 말은 Hooks라고 함</p>
<br>

<p>또한, <span style="background-color:#FFC0CB">리액트 훅은 함수 컴포넌트 내부에서만 호출되고,
조건문, 반복문 내부에선 호출 불가</span></p>
<p><span style="background-color:#FFE4E1">use라는 접두사를 이용해서 나만의 새로운 훅인 커스텀 훅을 만드는 것도 가능</span></p>
<h2 id="실습-3">실습</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/467c8361-2ae6-4ea1-940c-c24f754305b6/image.png" width=300>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/fa8c2d86-95cb-4128-b82c-2c195d12be55/image.png" width=500>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/dba50886-0134-49f1-9b82-d66cf1295cc9/image.png" width=500>

<p>위와 같이 컴포넌트 준비</p>
<h3 id="1-훅은-함수-컴포넌트나-커스텀-훅-내부에서만-호출-가능">1. 훅은 함수 컴포넌트나 커스텀 훅 내부에서만 호출 가능</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/a07f083b-4042-4d5d-a349-50ee76a2bfe1/image.png" width=500>

<p>React Hook은 반드시 React 함수 컴포넌트 내부(또는 커스텀 훅 내부)에서만 호출할 수 있는데,
현재 최상위 스코프에서 훅을 호출하고 있음</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/d2c16420-2bbd-4fad-b69c-95538909e108/image.png">

<p>브라우저에서도 경고가 뜸</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/ae66a5be-67bb-4f09-8d3f-59d2348b6406/image.png" width=400>

<p>따라서 이렇게 함수 컴포넌트 안에 써져야 함</p>
<h3 id="2-조건부로-호출될-수-없다">2. 조건부로 호출될 수 없다.</h3>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/7983b96d-0a11-4ae4-a8ed-a6f99a39d8b5/image.png" width=500>

<p><span style="background-color:#FFC0CB">훅은 조건문이나, 반복문 안에서 호출 될 수 없음</span>
이는 리액트에서 절대 허용될 수 없음</p>
<p>왜냐하면,
리액트가 내부적으로 컴포넌트를 호출해서 화면에 렌더링할 때,
조건문이나 반복문 내부에서 훅을 호출하면
<span style="background-color:#FFE4E1">서로 다른 훅들의 호출 순서가 엉망이 되는 현상이 발생해서 내부적인 오류가 발생할 수 있음</span></p>
<p>따라서 <span style="background-color:#FDF5E6">훅은 컴포넌트 안에서만 호출</span></p>
<h3 id="3-나만의-훅-커스텀-훅을-직접-만들-수-있다">3. 나만의 훅, 커스텀 훅을 직접 만들 수 있다.</h3>
<p>예를 들어서, 컴포넌트에 간단한 input을 하나 만들고,
staate로 이 input에 입력되는 값을 관리할 경우</p>
<ul>
<li>새로운 state 생성</li>
<li>이벤트 핸들러 하나 생성<ul>
<li>이 이벤트 핸들러에서 매개변수 e를 받아서, setInput에 e.target.value를 전달하는 방식으로,
state의 입력 값을 보관할 수 있었음</li>
</ul>
</li>
<li>input 태그의 value에 input, 그리고 onChange로는 onChange설정</li>
</ul>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/3084d63f-cf29-4591-8eed-4c6c11a6cafb/image.png" width=500>

<p>쪼끄만 input 하나 관리하는 코드가 너무 긺</p>
<p>그리고 이러한 컴포넌트가 엄청 많이 생성되어야 한다면,
각각의 컴포넌트마다 state를 만들고,
이벤트 핸들러를 만드는 코드를 중복으로 매번 작성해야 함</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/8db8fe7c-a131-40fc-93e5-1687c89b5d4e/image.png" width=500>

<p>그래서 이 부분을 별도의 함수로 만듦</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/a1b41649-43f3-47f3-b4a5-5cefebd90c67/image.png" width=500>

<p>이렇게 만들면 될 것 같다!</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/71330b21-173d-4173-b854-a290e8ea4be0/image.png" width=500>

<p>그런데, 정작 사용하려면 오류가 발생</p>
<p><span style="background-color:#FDF5E6">리액트 컴포넌트가 아닌 일반적인 자바스크립트 함수에서는 useState 같은 훅을 호출하면 오류가 발생</span>
<span style="background-color:#FFC0CB">함수 컴포넌트가 직접 만든 커스텀 훅에서만 useState를 호출할 수 있기 때문</span></p>
<p>따라서 <span style="background-color:#E2D6FF">getInput 함수를 커스텀 훅이라는 우리가 직접 만든 리액트 훅으로 바꿔 주어야 함</span></p>
<br>

<p><span style="background-color:#FFC0CB">함수의 이름 앞에 use만 써주면,
리액트는 내부적으로 use라는 접두사를 사용하는 함수를 커스텀 훅이라고 판단</span>해서,
또 다른 리액트 훅을 내부에서 호출해도 오류를 발생시키지 않음</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/60b0baf1-8435-451b-a493-f260a5e3808e/image.png" width=500>
(정말로 이름만 바꾸었다. getInput -> useInput)

<br>

<p>따라서 이렇게 input같이 <span style="background-color:#FDF5E6">컴포넌트마다 반복되어서 동작하는 로직</span>이 있고,
<span style="background-color:#FDF5E6">해당 로직이 useState와 같은 훅을 사용하는 로직</span>이면,
그 로직은 <strong>커스텀 훅을 만들어서 분리</strong>해줄 수 있음</p>
<br>

<img src="https://velog.velcdn.com/images/ann-algorithm/post/5d607832-a4bd-42e2-80ae-2875cd600c34/image.png" width=500>

<p>그렇게 분리된 로직은,
여러 번 반복해서 사용하는 걸 가능하게 함</p>
<br>

<p>또 <span style="background-color:#FDF5E6">훅은 파일을 분리해서 관리함</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/7b61ba33-b847-4501-bf53-bf62bfaa3218/image.png" width=500>

<p><strong>src/hooks</strong>라는 폴더 안에, Hook이라는 이름으로 보관하는 게 일반적임</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/6b017121-3ab6-47cf-b018-1d6557debcf0/image.png" width=500>

<p>그럼 함수 컴포넌트 내에는 이렇게 사용할 수 있음</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[모던 자바스크립트 15장. let, const 키워드와 블록 레벨 스코프]]></title>
            <link>https://velog.io/@ann-algorithm/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-15%EC%9E%A5.-let-const-%ED%82%A4%EC%9B%8C%EB%93%9C%EC%99%80-%EB%B8%94%EB%A1%9D-%EB%A0%88%EB%B2%A8-%EC%8A%A4%EC%BD%94%ED%94%84</link>
            <guid>https://velog.io/@ann-algorithm/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-15%EC%9E%A5.-let-const-%ED%82%A4%EC%9B%8C%EB%93%9C%EC%99%80-%EB%B8%94%EB%A1%9D-%EB%A0%88%EB%B2%A8-%EC%8A%A4%EC%BD%94%ED%94%84</guid>
            <pubDate>Mon, 08 Dec 2025 19:56:57 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-var-키워드로-선언한-변수의-문제점">📌 var 키워드로 선언한 변수의 문제점</h1>
<h2 id="변수-중복-선언">변수 중복 선언</h2>
<pre><code class="language-js">var x = 1;
var y = 1;

// var 키워드로 선언된 변수는 같은 스코프 내에서 중복 선언을 허용한다.
// 초기화문이 있는 변수 선언문은 자바스크립트 엔진에 의해 var 키워드가 없는 것처럼 동작한다.
var x = 100;
// 초기화문이 없는 변수 선언문은 무시된다.
var y;

console.log(x); // 100
console.log(y); // 1</code></pre>
<p>동일한 이름의 변수가 이미 선언되어 있는 것을 모르고,
변수를 중복 선언하면서 값까지 할당했다면
➡️ 의도치 않게 먼저 선언된 변수 값이 변경되는 부작용이 발생한다.</p>
<h2 id="함수-레벨-스코프">함수 레벨 스코프</h2>
<p>var 키워드로 선언한 변수는 함수의 코드 블록만을 지역 스코프로 인정한다.
따라서, 의도치 않게 전역 변수가 중복 선언되는 경우가 발생한다.</p>
<h2 id="변수-호이스팅">변수 호이스팅</h2>
<p>var 키워드로 변수를 선언하면 변수 호이스팅에 의해 변수 선언문이 스코프의 선두로 끌어 올려진 것처럼 동작한다.</p>
<p>이를 통해 에러가 발생하는 건 아니지만,
프로그램의 흐름상 맞지 않을뿐더러 가독성을 떨어뜨리고 오류를 발생시킬 여지를 남긴다.</p>
<h1 id="📌-let-키워드">📌 let 키워드</h1>
<h2 id="변수-중복-선언-금지">변수 중복 선언 금지</h2>
<p>let 키워드로 이름이 같은 변수를 중복 선언하면, 문법 에러가 발생한다.</p>
<h2 id="블록-레벨-스코프">블록 레벨 스코프</h2>
<p>let 키워드로 선언한 변수는 모든 코드 블록(함수, if문, for문, while문, try/catch 문 등)을 지역 스코프로 인정하는 <strong>블록 레벨 스코프</strong>를 따른다.</p>
<h2 id="변수-호이스팅-1">변수 호이스팅</h2>
<p>var 키워드로 선언한 변수와 달리,
let 키워드로 선언한 변수는 변수 호이스팅이 발생하지 않는 것처럼 동작한다.</p>
<pre><code class="language-js">console.log(foo); // ReferenceError: foo is not defined
let foo;</code></pre>
<br>

<p>var 키워드로 선언한 변수는,
런타임 이전에 자바스크립트 엔진에 의해 암묵적으로 &quot;선언 단계&quot;와 &quot;초기화 단계&quot;가 한번에 진행된다.</p>
<p>즉, 선언 단계에서 스코프(실행 컨텍스트의 렉시컬 환경)에 변수 식별자를 등록해,
자바스크립트 엔진에 변수의 존재를 알린다.
그리고 즉시, 초기화 단계에서 undefined로 변수를 초기화한다.</p>
<br>

<p><span style="background-color:#FFC0CB">let 키워드로 선언한 변수는 &quot;선언 단계&quot;와 &quot;초기화 단계&quot;가 분리되어 진행된다.</span>
<span style="background-color:#FDF5E6">즉, 런타임 이전에 자바스크립트 엔진에 의해 암묵적으로 선언 단계가 먼저 실행되지만,
초기화 단계는 변수 선언문에 도달했을 때 실행된다.
</span></p>
<p>만약 초기화 단계가 실행되기 이전에 변수에 접근하려고 하면, <strong>참조 에러</strong>가 발생한다.</p>
<p><span style="background-color:#FDF5E6">let 키워드로 선언한 변수는 스코프의 시작 지점부터 초기화 단계 시작 지점(변수 선언문)까지 변수를 참조할 수 없다.</span>
➡️ 스코프의 시작 지점부터 초기화 시작 지점까지 변수를 참조할 수 없는 구간 = <strong>일시적 사각지대(TDZ)</strong></p>
<pre><code class="language-js">// 런타임 이전에 선언 단계가 실행된다. 아직 변수가 초기화되지 않았다.
// 초기화 이전에 일시적 사각지대에서는 변수를 참조할 수 없다.
console.log(foo); // ReferenceError: foo is not defined

let foo; // 변수 선언문에서 초기화 단계가 실행된다.
console.log(foo); // undefined

foo = 1;
console.log(foo); // 1</code></pre>
<br>

<p>let 키워드로 선언한 변수는 변수 호이스팅이 발생하지 않는 것처럼 보이지만,
그렇지 않다.</p>
<pre><code class="language-js">let foo = 1; // 전역 변수

{
  console.log(foo); // ReferenceError: Caanot access &#39;foo&#39; before initialization
  let foo = 2; // 지역 변수
}</code></pre>
<p>let 키워드로 선언한 변수의 경우,
변수 호이스팅이 발생하지 않는다면
위 예제는 전역 변수 foo의 값을 출력해야 한다.</p>
<p><span style="background-color:#FFC0CB">하지만 let 키워드로 선언한 변수도 여전히 호이스팅이 발생하기 때문에 참조 에러가 발생한다.
</span></p>
<blockquote>
<p>자바스크립트는 ES6에서 도입된 let, const를 포함해서 모든 선언(var, let, const, function, function*, class 등)을 호이스팅한다. 
단, ES6에서 도입된 let, const, class를 사용한 선언문은 호이스팅이 발생하지 않은 것처럼 동작한다.</p>
</blockquote>
<h2 id="전역-객체와-let">전역 객체와 let</h2>
<p>var 키워드로 선언한 전역 변수와 전역 함수, 그리고 선언하지 않은 변수에 값을 할당한 암묵적 전역은 전역 객체 window의 프로퍼티가 된다고 했다.</p>
<br>

<p>let 키워드로 선언한 전역 변수는 전역 객체의 프로퍼티가 아니다. 즉, window.foo와 같이 접근할 수 없다.
<span style="background-color:#E6E6FA">let 전역 변수는 보이지 않는 개념적인 블록(전역 렉시컬 환경의 선언적 환경 레코드)내에 존재하게 된다.</span></p>
<pre><code class="language-js">// 이 예제는 브라우저 환경에서 실행해야 한다.

let x = 1;

// let, const 키워드로 선언한 전역 변수는 전역 객체 window의 프로퍼티가 아니다.
console.log(window.x); // undefined
console.log(x); // 1</code></pre>
<h1 id="📌-const-키워드">📌 const 키워드</h1>
<p>대부분의 특징은 let 키워드와 대부분 동일하다.</p>
<p>const 키워드는 상수를 선언하기 위해 사용한다.</p>
<h2 id="선언과-초기화">선언과 초기화</h2>
<p>const 키워드로 선언한 변수는 반드시 선언과 동시에 초기화해야 한다.</p>
<p>그렇지 않으면 문법 에러가 발생한다.</p>
<pre><code class="language-js">const foo; // SyntaxError: Missing initializer in const declaration</code></pre>
<h2 id="재할당-금지">재할당 금지</h2>
<p>var 또는 let 키워드로 선언한 변수는 재할당이 자유로우나,
const 키워드로 선언한 변수는 재할당이 금지된다.</p>
<pre><code class="language-js">const foo = 1;
foo = 2; // TypeError: Assignment to constant variable.</code></pre>
<h2 id="상수">상수</h2>
<p>상태 유지와 가독성, 유지보수의 편의를 위해 적극적으로 사용해야 한다.</p>
<p>일반적으로 상수의 이름은 대문자로 선언해, 상수임을 명확히 나타낸다.</p>
<pre><code class="language-js">// 세율이 의미하는 0.1은 변경할 수 없는 상수로서 사용될 값이다.
// 변수 이름을 대문자로 선언해 상수임을 명확히 나타낸다.
const TAX_RATE = 0.1;

// 세전 가격
let preTaxPrice = 100;

// 세후 가격
let afterTaxPrice = preTaxPrice + (preTaxPrice * TAX_RATE);

console.log(afterTaxPrice); // 110</code></pre>
<h2 id="const-키워드와-객체">const 키워드와 객체</h2>
<p>const 키워드로 선언된 변수에 원시 값을 할당한 경우 값을 변경할 수 없다.
하지만 <span style="background-color:#FFC0CB">const 키워드로 선언된 변수에 객체를 할당한 경우, 값을 변경할 수 있다.</span></p>
<p>변경 불가능한 값인 원시 값은 재할당 없이 변경(교체)할 수 있는 방법이 없지만,
변경 가능한 값인 객체는 재할당 없이도 직접 변경이 가능하기 때문이다.</p>
<pre><code class="language-js">const person = {
  name: &#39;Lee&#39;
};

// 객체는 변경 가능한 값이다. 따라서 재할당 없이 변경이 가능하다.
person.name = &#39;Kim&#39;;

console.log(person); // {name: &quot;Kim&quot;}</code></pre>
<p>const는 재할당을 금지할 뿐 &quot;불변&quot;을 의미하지는 않는다.
즉, 새로운 값을 재할당하는 건 불가능하지만, 
프로퍼티 동적 생성, 삭제, 프로퍼티 값의 변경을 통해 객체를 변경하는 것은 가능하다.</p>
<p><span style="background-color:#FDF5E6">이때, 객체가 변경되더라도 변수에 할당된 참조 값은 변경되지 않는다.</span></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[모던 자바스크립트 14장. 전역 변수의 문제점]]></title>
            <link>https://velog.io/@ann-algorithm/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-14%EC%9E%A5.-%EC%A0%84%EC%97%AD-%EB%B3%80%EC%88%98%EC%9D%98-%EB%AC%B8%EC%A0%9C%EC%A0%90</link>
            <guid>https://velog.io/@ann-algorithm/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-14%EC%9E%A5.-%EC%A0%84%EC%97%AD-%EB%B3%80%EC%88%98%EC%9D%98-%EB%AC%B8%EC%A0%9C%EC%A0%90</guid>
            <pubDate>Mon, 08 Dec 2025 18:58:25 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-변수의-생명-주기">📌 변수의 생명 주기</h1>
<h2 id="1️⃣-지역-변수의-생명-주기">1️⃣ 지역 변수의 생명 주기</h2>
<p>변수는 선언에 의해 생성되고, 할당을 통해 값을 갖는다.
➡️ 변수는 생성되고 소멸되는 <strong>쌩명 주기</strong>가 있다.</p>
<p>변수에 생명 주기가 없다면 한번 선언된 변수는 프로그램을 종료하지 않는 한 영원히 메모리 공간을 점유하게 된다.</p>
<p>변수는 자신이 선언된 위치에서 생성되고 소멸한다.
전역 변수의 생명 주기는 애플리케이션의 생명 주기와 같다.
함수 내부에서 선언된 지역 변수는 함수가 호출되면 생성되고 함수가 종료되면 소멸된다.</p>
<br>

<p>변수 선언은 선언문이 어디에 있든 상관없이 가장 먼저 실행된다고 했다.(런타임 이전 단계에서 실행)
그러나 이것은 전역 변수에 한정된 것</p>
<p>함수 내부에서 선언한 변수는,
<span style="background-color:#FDF5E6">함수가 호출된 직후에 함수 몸체의 코드가 한 줄씩 순차적으로 실행되기 이전에</span> 자바스크립트 엔진에 의해 먼저 실행된다.</p>
<br>

<p>변수는 하나의 값을 저장하기 위해 확보한 메모리 공간 자체 또는 그 메모리 공간을 식별하기 위해 붙인 이름이다.
➡️ <span style="background-color:#FFC0CB">따라서 변수의 생명 주기는 메모리 공간이 확보된 시점부터, 메모리 공간이 해제되어 가용 메모리 풀에 반환되는 시점까지다.</span></p>
<p>함수 내부에서 선언된 지역 변수는 함수가 생성한 스코프에 등록된다.
(함수가 생성한 스코프는 렉시컬 환경이라 부르는 물리적인 실체가 있다.)
➡️ 변수는 자신이 등록된 스코프가 소멸(메모리 해제)될 때까지 유효하다.</p>
<p><span style="background-color:#FFC0CB">할당된 메모리 공간은 더 이상 누구도 참조하지 않을 때,
가비지 콜렉터에 의해 해제되어 가용 메모리 풀에 반환된다.</span></p>
<p>즉, 누군가가 메모리 공간을 참조하고 있으면 해제되지 않고 확보된 상태로 남아 있다.
스코프 역시, 누군가 스코프를 참조하고 있으면 스코프는 소멸하지 않고 생존하게 된다.</p>
<blockquote>
<p>일반적으로 함수가 종료하면 함수가 생성한 스코프도 소멸한다.
하지만 누군가가 스코프를 참조하고 있다면, 스코프는 해제되지 않고 생존하게 된다.</p>
</blockquote>
<br>

<pre><code class="language-js">var x = &#39;global&#39;;

function foo() {
  console.log(x); // 1️⃣
  var x = &#39;local&#39;;
}

foo();
console.log(x); // global</code></pre>
<pre><code>foo 함수 내부에서 선언된 지역 변수 x는 1️⃣의 시점에 이미 선언되었고, undefined로 초기화되어 있음
따라서 전역 변수 x를 참조하는 것이 아니라, 지역 변수 x를 참조해 값을 출력한다.
즉, 지역 변수는 함수 전체에서 유효하다.
(변수 할당문이 실행되기 이전까지는 undefined 값을 가질 뿐)</code></pre><p>이처럼 <span style="background-color:#FFC0CB">호이스팅은 스코프를 단위로 동작한다.</span></p>
<p>전역 변수의 호이스팅은 전역 변수의 선언이 전역 스코프의 선두로 끌어 올려진 것처럼 동작한다.
➡️ 전역 변수는 전역 전체에서 유효한다.</p>
<p>지역 변수의 호이스팅은 지역 변수의 선언이 지역 스코프의 선두로 끌어 올려진 것처럼 동작한다.
➡️ 지역 변수는 함수 전체에서 유효하다.</p>
<p><span style="background-color:#FFEBCD">호이스팅은 변수 선언이 스코프의 선두로 끌어올려진 것처럼 동작하는 자바스크립트 고유의 특징을 말한다.</span></p>
<h2 id="2️⃣-전역-변수의-생명-주기">2️⃣ 전역 변수의 생명 주기</h2>
<p>전역 변수는 전역 객체의 프로퍼티가 된다.
➡️ 전역 변수의 생명 주기가 전역 객체의 생명 주기와 일치 한다.</p>
<blockquote>
<p>📝 <strong>전역 객체</strong>
코드가 실행되기 이전 단계에, 자바스크립트 엔진에 의해 어떤 객체보다도 먼저 생성되는 특수한 객체다.
전역 객체는 클라이언트 사이드 환경(브라우저)에서는 window,
서버 사이드 환경(Node.js)에서는 global 객체를 의미한다.</p>
</blockquote>
<p>환경에 따라 전역 객체를 가리키는 다양한 식별자(windwo, self, this, frames, global)가 존재했으나, ES11(ECMAScript 11)에서 globalThis로 통일되었다.</p>
<blockquote>
<p>전역 객체는 표준 빌트인 객체(Object, String, Number, Function, Array...)와 환경에 따른 호스트 객체(클라이언트 Web API 또는 호스트 API), 그리고 var 키워드로 선언한 전역 변수와 전역 함수를 프로퍼티로 갖는다.</p>
</blockquote>
<p>브라우저 환경에서 전역 객체는 window이므로,
브라우저 환경에서 var 키워드로 선언한 전역 변수는 전역 객체 window의 프로퍼티다.
전역 객체 window는 웹페이지를 닫기 전까지 유효하다.</p>
<p>다시 말해, 브라우저 환경에서 var 키워드로 선언한 전역 변수는 웹페이지를 닫을 때까지 유효하다.
<span style="background-color:#FDF5E6">var 키워드로 선언한 전역 변수의 생명 주기는 전역 객체의 생명 주기와 일치한다.</span></p>
<h1 id="📌-전역-변수의-문제점">📌 전역 변수의 문제점</h1>
<h2 id="암묵적-결합">암묵적 결합</h2>
<p>전역 변수를 선언한 의도는,
코드 어디서든 참조하고 할당할 수 있는 변수를 사용하겠다는 것이다.
= 모든 코드가 전역 변수를 참조하고 변경할 수 있는, <strong>암묵적 결합</strong>을 허용하는 것이다.</p>
<p>변수의 유효 범위가 크면 클수록 코드의 가독성은 나빠지고,
의도치 않게 상태가 변경될 수 있는 위험성도 높아진다.</p>
<h2 id="긴-생명-주기">긴 생명 주기</h2>
<p>전역 변수는 생명 주기가 길다.
따라서 메모리 리소스도 오랜 기간 소비한다.</p>
<p>또한 전역 변수의 상태를 변경할 수 있는 시간도 길고 기회도 많다.
생명주기가 긴 전역 변수는 변수 이름이 중복될 가능성이 있다.
➡️ 상태 변경에 의한 오류가 발생할 확률이 있다.</p>
<h2 id="스코프-체인-상에서-종점에-존재">스코프 체인 상에서 종점에 존재</h2>
<p>전역 변수는 스코프 체인 상에서 종점에 존재.
따라서 변수를 검색할 때 전역 변수가 가장 마지막에 검색된다는 것을 말한다.
➡️ 전역 변수의 검색 속도가 가장 느리다.
(크진 않지만 분명히 존재)</p>
<h2 id="네임스페이스-오염">네임스페이스 오염</h2>
<p>자바스크립트의 가장 큰 문제점 중 하나는 파일이 분리되어 있다 해도,
하나의 전역 스코프를 공유한다는 것</p>
<p>따라서, <span style="background-color:#E6E6FA">다른 파일 내에서 동일한 이름으로 명명된 전역 변수나, 
전역 함수가 같은 스코프 내에 존재하면 예상치 못한 결과를 가져올 수 있다.</span></p>
<h1 id="📌-전역-변수의-사용을-억제하는-방법">📌 전역 변수의 사용을 억제하는 방법</h1>
<p><span style="background-color:#FFC0CB">지역 변수를 사용하는 것이 좋다.</span><br>변수의 스코프는 좁을수록 좋다.</p>
<p>전역 변수를 절대 사용하지 말라는 건 아니고,
남발을 억제해야 한다.</p>
<h2 id="즉시-실행-함수">즉시 실행 함수</h2>
<p><span style="background-color:#E6E6FA">함수 정의와 동시에 호출되는 즉시 실행 함수는 단 한 번만 호출된다.</span>
<span style="background-color:#FFC0CB">모든 코드를 즉시 실행 함수로 감싸면, 모든 변수는 즉시 실행 함수의 지역 변수가 된다.</span></p>
<pre><code class="language-js">(function () {
  var foo = 10; // 즉시 실행 함수의 지역 변수
  // ...
}());

console.log(foo); // ReferenceError: foo is not defined</code></pre>
<p>이 방법을 사용하면, 전역 변수를 생성하지 않으므로,
<span style="background-color:#FDF5E6">라이브러리 등에 자주 사용된다.</span></p>
<h2 id="네임-스페이스-객체">네임 스페이스 객체</h2>
<p><span style="background-color:#E6E6FA">전역에 네임스페이스 역할을 담당할 객체를 생성하고,
전역 변수처럼 사용하고 싶은 변수를 프로퍼티로 추가하는 방법</span>이다.</p>
<pre><code class="language-js">var MYAPP = {}; // 전역 네임스페이스 객체

MYAPP.name = &#39;Lee&#39;;

console.log(MYAPP.name); // Lee</code></pre>
<br>

<p><span style="background-color:#E6E6FA">네임스페이스 객체에 또 다른 네임스페이스 객체를 프로퍼티로 추가해서,
네임스페이스를 계층적으로 구성할 수도 있다.</span></p>
<pre><code class="language-js">var MYAPP = {}; // 전역 네임스페이스 객체

MYAPP.person = {
  name: &#39;Lee&#39;,
  address: &#39;Seoul&#39;
};

console.log(MYAPP.person.name); // Lee</code></pre>
<p>단, 네임스페이스 객체 자체가 전역 변수에 할당된다.</p>
<h2 id="모듈-패턴">모듈 패턴</h2>
<blockquote>
<p>모듈 패턴은 클래스를 모방해서 관련이 있는 변수와 함수를 모아,
즉시 실행 함수로 감싸 하나의 모듈을 만든다.
모듈 패턴은 자바스크립트의 강력한 기능인 클로저를 기반으로 동작한다.</p>
</blockquote>
<p>➡️ 전역 변수의 억제는 물론 캡슐화까지 구현할 수 있다는 것이다.</p>
<blockquote>
<p>📝 <strong>캡슐화</strong>
객체의 상태를 나타내는 프로퍼티와,
프로퍼티를 참조하고 조작할 수 있는 동작인 메서드를 하나로 묶는 것</p>
<p>객체의 특정 프로퍼티나 메서드를 감출 목적으로 사용하기도 한다. = 정보 은닉</p>
</blockquote>
<p>자바스크립트는 정보 은닉을 위한 접근 제한자를 제공하지 않기 때문에,
모듈 패턴은 전역 네임스페이스의 오염을 막는 기능과,
(한정적이긴 하지만) 정보 은닉을 구현하기 위해 사용된다.</p>
<pre><code class="language-js">var Counter =  (function () {
  // private 변수
  var num = 0;

  // 외부로 공개할 데이터나 메서드를 프로퍼티로 추가한 객체를 반환한다.
  return {
    increase() {
      return ++num;
    },
    decrease() {
      return --num;
    }
  };
}());

// private 변수는 외부로 노출되지 않는다.
console.log(Counter.num); // undefined

console.log(Counter.increase()); // 1
console.log(Counter.increase()); // 2
console.log(Counter.decrease()); // 1
console.log(Counter.decrease()); // 0</code></pre>
<p>즉시 실행 함수는 객체를 반환하는데,
이 객체에는 외부에 노출하고 싶은 변수나 함수를 반환한다.</p>
<ul>
<li>퍼블릭 멤버: 반환되는 객체의 프로퍼티, 외부에 노출</li>
<li>프라이빗 멤버: 외부로 노출하고 싶지 않은 변수나 함수는 반환하는 객체에 추가하지 않음, 외부에서 접근 불가</li>
</ul>
<h2 id="es6-모듈">ES6 모듈</h2>
<p>ES6 모듈을 사용하면 더는 전역 변수를 사용할 수 없다.
<span style="background-color:#FFC0CB">ES6 모듈은 파일 자체의 독자적인 모듈 스코프를 제공한다.</span></p>
<p>따라서 모듈 내에서 var 키워드로 선언한 변수는,
더는 전역 변수가 아니며 window 객체의 프로퍼티도 아니다.</p>
<p>모던 브라우저에서는 ES6 모듈을 사용할 수 없다.
script 태그에 type=&quot;module&quot; 어트리뷰트를 추가하면,
로드된 자바스크립트 파일은 모듈로서 동작한다.
(모듈의 파일 확장자는 mjs를 권장한다.)</p>
<pre><code class="language-html">&lt;script type=&quot;module&quot; src=&quot;lib.mjs&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;module&quot; src=&quot;app.mjs&quot;&gt;&lt;/script&gt;</code></pre>
<p>ES6 모듈은 IE를 포함한 구형 브라우저에서는 동작하지 않으며,
<span style="background-color:#FFE4E1">브라우저의 ES6 모듈 기능을 사용하더라도 트랜스파일링이나 번들링이 필요하기 때문에,
아직까지는 브라우저가 지원하는 ES6 모듈 기능보다는 Webpack 등의 모듈 번들러를 사용하는 것이 일반적</span></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[모던 자바스크립트 13장. 스코프]]></title>
            <link>https://velog.io/@ann-algorithm/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-13%EC%9E%A5.-%EC%8A%A4%EC%BD%94%ED%94%84</link>
            <guid>https://velog.io/@ann-algorithm/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-13%EC%9E%A5.-%EC%8A%A4%EC%BD%94%ED%94%84</guid>
            <pubDate>Mon, 08 Dec 2025 17:08:18 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-스코프란">📌 스코프란?</h1>
<p>스코프(유효범위)는 프로그래밍 언어의 기본적이고, 중요한 개념이다.
더욱이 자바스크립트의 스코프는 다른 언어의 스코프와 구별되는 특징이 있으므로 주의가 필요하다.</p>
<p>또한,
<span style="background-color:#FDF5E6">var 키워드로 선언한 변수와, let, const 키워드로 선언한 변수의 스코프도 다르게 동작한다.</span></p>
<br>

<p>이전에 스코프를 언급한 내용은,
함수의 매개변수는 함수 몸체 내부에서만 참조할 수 있고,
함수 몸체 외부에서는 참조할 수 없다는 개념이 있었다.</p>
<pre><code class="language-js">function add(x, y) {
  // 매개변수는 함수 몸체 내부에서만 참조할 수 있다.
  // 즉, 매개변수의 스코프(유효범위)는 함수 몸체 내부다.
  console.log(x, y); // 2 5
  return x + y;
}

add(2, 5);

// 매개변수는 함수 몸체 내부에서만 참조할 수 있다.
console.log(x, y); // ReferenceError: x is not defined</code></pre>
<p>x, y는 함수의 매개변수이기 때문에 함수의 스코프 외의 영역에선 사용할 수가 없다.</p>
<br>

<p><span style="background-color:#FDF5E6">변수는, 코드의 가장 바깥 영역뿐 아니라 코드 블록이나 함수 몸체 내에서도 선언할 수 있다.</span>
이때 코드 블록이나 함수는 중첩될 수 있다.</p>
<pre><code class="language-js">var var1 = 1; // var1: 코드의 가장 바깥 영역에서 선언한 변수

if (true) {
  var var2 = 2; // var2: 코드 블록 내에서 선언한 변수
  if (true) {
    var var3 = 3; // var3: 중첩된 코드 블록 내에서 선언한 변수
  }
}

function foo() {
  var var4 = 4; // var4: 함수 내에서 선언한 변수

  function bar() {
    var var5 = 5; // var5: 중첩된 함수 내에서 선언한 변수
  }
}

console.log(var1); // 1
console.log(var2); // 2
console.log(var3); // 3
console.log(var4); // ReferenceError: var4 is not defined
console.log(var5); // ReferenceError: var5 is not defined</code></pre>
<p>변수는 자신이 선언된 위치에 의해 자신이 유효한 범위, 즉 다른 코드가 변수 자신을 참조할 수 있는 범위가 결정된다.</p>
<p>변수 외에도
<span style="background-color:#FFC0CB">모든 식별자(변수 이름, 함수 이름, 클래스 이름 등)는 
자신이 선언된 위치에 의해 다른 코드가 식별자 자신을 참조할 수 있는 유효 범위가 결정된다.</span>
➡️ &quot;스코프&quot;</p>
<p><span style="background-color:#FDF5E6">즉, 스코프는 식별자가 유효한 범위를 말한다.</span></p>
<br>

<pre><code class="language-js">var x = &#39;global&#39;;

function foo() {
  var x = &#39;local&#39;;
  console.log(x); // 1️⃣
}

foo();

console.log(x); // 2️⃣</code></pre>
<p><span style="background-color:#FFE4E1">코드의 가장 바깥 영역과,
foo 함수 내부에 같은 이름을 갖는 x 변수를 선언했고,
1️⃣과 2️⃣에서 x 변수를 참조한다.</span></p>
<p>이때 <span style="background-color:#FDF5E6">자바스크립트 엔진은 스코프를 통해 어떤 변수를 참조해야 할 것인지 결정</span>한다.
➡️ 스코프란, 자바스크립트 엔진이 <span style="background-color:#FFEBCD">식별자를 검색할 때 사용하는 규칙</span>이다.</p>
<br>

<p>자바스크립트 엔진은 <span style="background-color:#FFC0CB">코드를 실행할 때 코드의 문맥을 고려한다.</span>
즉, 코드가 어디서 실행되며 주변에 어떤 코드가 있는지에 따라 
위 예제의 1️⃣과 2️⃣처럼 동일한 코드도 다른 결과를 만들어낸다.</p>
<p>위 예제에서,
<span style="background-color:#FFE4E1">코드의 가장 바깥 영역에 선언된 x 변수 2️⃣는 어디서든 참조할 수 있다.</span>
<span style="background-color:#FFE4E1">foo 함수 내부에서 선언된 x 변수 1️⃣은 foo 함수 내부에서만 참조할 수 있고, foo 함수 외부에서는 참조할 수 없다.</span>
➡️ 즉, 두 개의 x 변수는 식별자 이름이 동일하지만 자신이 유효한 범위, 즉 스코프가 다른 별개의 변수이다.</p>
<br>

<p>만약 스코프라는 개념이 없으면,
같은 이름을 갖는 변수는 충돌을 일으킴</p>
<p>식별자는 어떤 값을 구별하여 식별할 수 있는 고유한 이름이다. 따라서 유일해야 하고, 중복될 수 없다.
즉, <span style="background-color:#FDF5E6">하나의 값은 유일한 식별자에 연결되어야 한다.</span></p>
<br>

<p>예를 들어, 파일의 이름은 하나의 파일을 구별하여 식별할 수 있는 식별자다.
➡️ 식별자인 파일 이름은 유일해야 한다.</p>
<p>하지만, 컴퓨터를 사용할 때 하나의 파일 이름만 사용하지는 않는다.
식별자인 파일 이름을 중복해서 사용할 수 있는 이유는 폴더(디렉터리)라는 개념이 있기 때문이다.
만약, 폴더가 없다면 파일 이름은 유일해야 한다.</p>
<p>마찬가지로 프로그래밍 언어에서는 스코프(유효 범위)를 통해 식별자인 변수 이름의 충돌을 방지하여, 같은 이름의 변수를 사용할 수 있게 한다.
➡️ <span style="background-color:#FDF5E6">스코프 내에서 식별자는 유일해야 하지만, 다른 스코프에는 같은 이름의 식별자를 사용할 수 있다.</span></p>
<p><span style="background-color:#E6E6FA">즉, 스코프는 네임 스페이스다.</span></p>
<blockquote>
<p>📌 <strong>var 키워드로 선언한 변수의 중복 선언</strong>
<span style="background-color:#FFC0CB">var 키워드로 선언된 변수는 같은 스코프 내에서 중복 선언이 허용된다.
</span>의도치 않게 변수값이 재할당되어 변경되는 부작용을 발생시킨다.</p>
</blockquote>
<pre><code class="language-js">function foo() {
  var x = 1;
  // var 키워드로 선언된 변수는 같은 스코프 내에서 중복 선언을 허용한다.
  // 아래 변수 선언문은 자바스크립트 엔진에 의해 var 키워드가 없는 것처럼 동작한다.
  var x = 2;
  console.log(x); // 2
}
foo();</code></pre>
<blockquote>
<p>하지만 let이나 const 키워드로 선언된 변수는 같은 스코프 내에서 중복 선언을 허용하지 않는다.</p>
</blockquote>
<pre><code class="language-js">function bar() {
  let x = 1;
  // let이나 const 키워드로 선언된 변수는 같은 스코프 내에서 중복 선언을 허용하지 않는다.
  let x = 2; // SyntaxError: Identifier &#39;x&#39; has already been declared
}
bar();</code></pre>
<h1 id="📌-스코프의-종류">📌 스코프의 종류</h1>
<p>코드는 전역과 지역으로 구분할 수 있다.</p>
<table>
<thead>
<tr>
<th>구분</th>
<th>설명</th>
<th>스코프</th>
<th>변수</th>
</tr>
</thead>
<tbody><tr>
<td>전역</td>
<td>코드의 가장 바깥 영역</td>
<td>전역 스코프</td>
<td>전역 변수</td>
</tr>
<tr>
<td>지역</td>
<td>함수 몸체 내부</td>
<td>지역 스코프</td>
<td>지역 변수</td>
</tr>
</tbody></table>
<p>전역에서 선언된 변수는 전역 스코프를 갖는 전역 변수이고,
지역에서 선언된 변수는 지역 스코프를 갖는 지역 변수다.</p>
<h2 id="1️⃣-전역과-전역-스코프">1️⃣ 전역과 전역 스코프</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/47f681c3-7043-4ebb-917d-0bb6037346d8/image.png" width=500>

<p>전역이란, 코드의 가장 바깥 영역을 말한다.
전역은 전역 스코프를 만든다.
전역에 변수를 선하면 전역 스코프를 갖는 전역 변수가 된다.
➡️ 전역 변수는 어디서든지 참조할 수 있다.</p>
<h2 id="2️⃣-지역과-지역-스코프">2️⃣ 지역과 지역 스코프</h2>
<p>지역이란, 함수 몸체 내부를 말한다.
지역 -&gt; 지역 스코프를 만든다.
진역에 변수를 선언하면 지역 스코프를 갖는 지역 변수가 된다.
➡️ 지역 변수는 자신의 지역 스코프와 하위 지역 스코프에서 유효하다.</p>
<p>위 예제에선, outer 함수 내부에서 선언된 z 변수가 지역 변수다.
지역 변수 z는 자신의 지역 스코프인 outer 함수 내부와,
하위 지역 스코프인 inner 함수 내부에서 참조할 수 있다.</p>
<p>그러나, 지역 변수 z를 outer외(전역)에서 참조하면 참조 에러가 발생한다.</p>
<br>

<p>inner 함수 내부에 선언된 x 변수도 지역 변수다.
지역 변수 x는 자신의 지역 스코프인 함수 inner 내부에서만 참조 가능하다.
따라서, 지역 변수 x를 전역 또는 inner 함수 내부 이외의 지역에서 참조하면 참조 에러가 발생</p>
<br>

<p>inner 함수 내부에서 선언된 x 변수 이외에  이름이 같은 전역 변수 x가 존재한다.
이때 inner 함수 내부에서 x 변수를 참조하면,
전역 변수 x를 참조하는 것이 아니라
inner 함수 내부에서 선언된 x 변수를 참조한다.</p>
<p><span style="background-color:#FFC0CB">이는 자바스크립트 엔진이 스코프 체인을 통해 참조할 변수를 검색했기 때문이다.</span></p>
<h1 id="📌-스코프-체인">📌 스코프 체인</h1>
<p>함수는 전역에서 정의할 수도 있고,
함수 몸체 내부에서 정의할 수도 있다.</p>
<p>함수 몸체 내부에서 함수가 정의된 것을 <strong>함수의 중첩</strong>이라 한다.</p>
<p>함수 몸체 내부에서 정의한 함수 = <strong>중첩 함수(nested function)</strong>
중첩 함수를 포함하는 함수 = <strong>외부 함수(outer function)</strong></p>
<br>

<p>함수는 중첩될 수 있으므로 함수의 지역 스코프도 중첩될 수 있다.
➡️ <span style="background-color:#FDF5E6">스코프가 함수의 중첩에 의해 계층적 구조를 갖는다.</span></p>
<p>다시 말해, 중첩(내부) 함수의 지역 스코프는 중첩 함수를 포함하는 외부 함수의 지역 스코프와 계층적 구조를 갖는다.
➡️ 외부 함수의 지역 스코프를 중첩 함수의 상위 스코프라 한다.</p>
<p>이처럼, <span style="background-color:#FDF5E6">모든 스코프는 하나의 계층적 구조로 연결된다.</span>
<span style="background-color:#FFE4E1">모든 지역 스코프의 최상위 스코프는 전역 스코프다.</span>
➡️ 스코프가 계층적으로 연결된 것을 <strong>스코프 체인</strong>이라 한다.</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/bc222caa-66dc-4330-b5a3-2cd74240df7d/image.png">

<p>위 코드 및 그림에서 스코프 체인은, 
취상위 스코프인 전역 스코프,
전역에서 선언된 outer 함수의 지역 스코프,
outer 함수 내부에서 선언된 inner 함수의 지역 스코프로 이뤄진다.</p>
<p>변수를 참조할 때 <span style="background-color:#FFC0CB">자바스크립트 엔진은 스코프 체인을 통해 변수를 참조하는 코드의 스코프에서 시작하여, 
상위 스코프 방향으로 이동하며 선언된 변수를 검색</span>한다.
➡️ 상위 스코프에서 선언한 변수를 하위 스코프에서도 참조할 수 있다.</p>
<br>

<p>스코프 체인은 물리적인 실체로 존재한다.
<span style="background-color:#FFC0CB">자바스크립트 엔진은 코드(전역 코드와 함수 코드)를 실행하기에 앞서 
위 그림과 유사한 자료구조인 <strong>렉시컬 환경(Lexical Environment)</strong>을 실제로 생성한다.</span></p>
<p>변수 선언이 실행되면, <span style="background-color:#FDF5E6">변수 식별자가 이 자료구조(렉시컬 환경)에 키로 등록되고,
변수 할당이 일어나면 이 자료구조의 변수 식별자에 해당하는 값을 변경</span>한다.
<span style="background-color:#FFE4E1">또한, 변수의 검색도 이 자료구조 상에서 이뤄진다.</span></p>
<blockquote>
<p>📝 <strong>렉시컬 환경(Lexical Environment)</strong>
스코프 체인은 실행 컨텍스트의 렉시컬 환경을 단방향으로 연결한 것이다.
전역 렉시컬 환경은 코드가 로드되면 곧바로 생성되고,
함수의 렉시컬 환경은 함가 호출되면 곧바로 생성된다.</p>
</blockquote>
<blockquote>
<p>📝 <strong>실행 컨텍스트(execution context)</strong>
실행 컨텍스트(execution context)는 <strong>자바스크립트 엔진이 “지금 이 코드를 어떻게 실행할지”를 관리하기 위해 만드는 실행 단위(일종의 실행 정보 묶음)</strong></p>
</blockquote>
<h2 id="1️⃣-스코프-체인에-의한-변수-검색">1️⃣ 스코프 체인에 의한 변수 검색</h2>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/47f681c3-7043-4ebb-917d-0bb6037346d8/image.png" width=500>

<pre><code>4️⃣ x 변수를 참조하는 코드의 스코프인 inner 함수의 지역 스코프에서 x 변수가 선언되었는지 검색한다.
inner 함수 내에는 선언된 변수 x가 존재한다.
➡️ 검색된 변수를 참조하고, 검색을 종료한다.


5️⃣ y 변수를 참조하는 코드의 스코프인 inner 함수의 지역 스코프에서 y 변수가 선언되었는지 검색한다.
inner 함수 내에는 y 변수의 선언이 존재하지 않으므로,
상위 스코프인 outer 함수의 지역 스코프로 이동한다.

outer 함수 내에도 y 변수의 선언이 존재하지 않으므로,
또 다시 상위 스코프인 전역 스코프로 이동한다.

전역 스코프에는 y 변수의 선언이 존재한다.
➡️ 검색된 변수를 참조하고, 검색을 종료한다.


6️⃣ z 변수를 참조하는 코드의 스코프인 inner 함수의 지역 스코프에서 z 변수가 선언되었는지 검색한다.
inner 함수 내에는 z 변수의 선언이 존재하지 않으므로,
하위 스코프인 outer 함수의 지역 스코프로 이동한다.
outer 함수 내에는 z 변수의 선언이 존재한다.
➡️ 검색된 변수를 참조하고, 검색을 종료한다.</code></pre><p>자바스크립트 엔진은 스코프 체인을 따라 변수를 참조하는 코드의 스코프에서 시작해서,
상위 스코프 방향으로 이동하며 선언된 변수를 검색한다.</p>
<p><span style="background-color:#FDF5E6">절대 하위 스코프로 내려가면서, 식별자를 검색하는 일은 없다.</span>
<span style="background-color:#FFC0CB">상위 스코프에서 유효한 변수는 하위 스코프에서 자유롭게 참조할 수 있지만,
하위 스코프에서 유효한 변수를 상위 스코프에서 참조할 수 없다.</span></p>
<br>

<p>스코프 체인으로 연결된 스코프의 계층적 구조는,
부자 관계로 이뤄진 상속과 유사하다.
상속을 통해 부모의 자산을 자식이 자유롭게 사용할 수 있지만, 자식의 자산은 부모가 사용할 수는 없다.</p>
<h2 id="2️⃣-스코프-체인에-의한-함수-검색">2️⃣ 스코프 체인에 의한 함수 검색</h2>
<pre><code class="language-js">function foo() {
  console.log(&#39;global function foo&#39;);
}

function bar() {
  function foo() {
    console.log(&#39;local function foo&#39;);
  }
  foo();
}

bar();</code></pre>
<h1 id="📌-함수-레벨-스코프">📌 함수 레벨 스코프</h1>
<p>지역은 함수 몸체 내부를 말하고, 지역은 지역 스코프를 만든다.
= 함수에 의해서만 지역 스코프가 생성된다는 의미다.</p>
<p>C나, 자바 등을 비롯한 대부분의 프로그래밍 언어는 함수 몸체만이 아니라,
모든 코드 블록(if, for, while, try/catch 등)이 지역 스코프를 만든다.
➡️ <strong>블록 레벨 스코프</strong>라고 한다.</p>
<br>

<p>var 키워드로 선언된 변수는
<span style="background-color:#FFC0CB">오로지 함수의 코드 블록(함수 몸체)만을 지역 스코프로 인정</span>한다.
➡️ <strong>함수 레벨 스코프</strong>라고 한다.</p>
<pre><code class="language-js">var x = 1;

if (true) {
  // var 키워드로 선언된 변수는 함수의 코드 블록(함수 몸체)만을 지역 스코프로 인정한다.
  // 함수 밖에서 var 키워드로 선언된 변수는, 코드 블록 내에서 선언되었다 할지라도 모두 전역 변수다.

  // 지금 이건 if 코드 블록 내부다.
  // 따라서 x는 전역 변수다. 이미 선언된 전역 변수 x가 있으므로 x 변수는 중복 선언된다.
  // 이는 의도치 않게, 변수 값이 변경되는 부작용을 발생시킨다.
  var x = 10;
}

console.log(x); // 10</code></pre>
<p>if 문의 코드 블록 내에서 선언된 x 변수는 전역 변수다.</p>
<p>var 키워드로 선언된 변수는 함수 레벨 스코프만 인정하기 때문에,
함수 밖에서 var 키워드로 선언된 변수는 코드 블록 내에서 선언되어도 모두 전역 변수다.</p>
<p>따라서, 전역 변수 x는 중복 선언되고,
그 결과 의도치 않은 전역 변수의 값이 재할당 된다.</p>
<h1 id="📌-렉시컬-스코프">📌 렉시컬 스코프</h1>
<pre><code class="language-js">var x = 1;

function foo() {
  var x = 10;
  bar();
}

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

foo(); // ?
bar(); // ?</code></pre>
<p>위 예제의 실행 결과는 bar 함수의 상위 스코프가 무엇인지에 결정된다.
두 가지 패턴을 예측할 수 있다.</p>
<ol>
<li><strong>함수를 어디서 호출했는지</strong>에 따라 함수의 상위 스코프를 결정한다.</li>
<li><strong>함수를 어디서 정의했는지</strong>에 따라 함수의 상위 스코프를 결정한다.</li>
</ol>
<p>첫 번재 방식으로 함수의 상위 스코프를 결정한다면,
bar 함수의 상위 스코프는 foo 함수의 지역 스코프와 전역 스코프일 것이다.</p>
<p>두 번째 방식으로 함수의 상위 스코프를 결정한다면,
bar 함수의 상위 스코프는 전역 스코프일 것이다.</p>
<h2 id="첫-번째-방식">첫 번째 방식</h2>
<p><strong>= 동적 스코프</strong></p>
<p>함수를 정의하는 시점에는 함수가 어디서 호출될지 알 수 없다.
➡️ 함수가 호출되는 시점에 동적으로 상위 스코프를 결정해야 하기 때문에 동적 스코프라고 부른다.</p>
<h2 id="두-번째-방식">두 번째 방식</h2>
<p><strong>= 렉시컬 스코프</strong> 또는 <strong>정적 스코프</strong></p>
<p>동적 스코프 방식처럼 상위 스코프가 동적으로 변하지 않고,
함수 정의가 평가되는 시점에 상위 스코프가 정적으로 결정되기 때문에 정적 스코프라고 부른다.</p>
<p>자바스크립트를 비롯한 대부분의 프로그래밍 언어는 렉시컬 스코프를 따른다.</p>
<h2 id="따라서">따라서,</h2>
<p>자바스크립트는 렉시컬 스코프를 따르므로,
함수를 어디서 호출했는지가 아니라,
<strong>함수를 어디서 정의했는지</strong>에 따라 상위 스코프 결정</p>
<p><span style="background-color:#FDF5E6">함수가 호출된 위치는 상위 스코프 결정에 어떠한 영향도 주지 않는다.</span>
➡️ 즉, 함수의 상위 스코프는 언제나 자신이 정의된 스코프다.</p>
<p>이처럼 함수의 상위 스코프는 함수 정의가 실행될 때 정적으로 결정된다.
<span style="background-color:#FDF5E6">함수 정의(함수 선언문 또는 함수 표현식)가 실행되어 생성된 함수 객체는 이렇게 결정된 상위 스코프를 기억한다.</span>
함수가 호출될 때마다 함수의 상위 스코프를 참조할 필요가 있기 때문</p>
<br>

<p>위 예제의 bar 함수는 전역에서 정의된 함수다.
함수 선언문으로 정의된 bar 함수는 전역 코드가 실행되기 전에 먼저 평가되어 함수 객체를 생성한다.</p>
<p>이때 생성된 bar 함수 객체는 자신이 정의된 스코프, 즉 전역 스코프를 기억한다.</p>
<p>그리고 bar 함수가 호출되면 호출된 곳이 어디인지 관계없이,
자신이 기억하고 있는 전역 스코프를 상위 스코프로 사용한다.
➡️ 위 예제 실행 시, 전역 변수 x의 값 1이 두 번 출력된다.</p>
<p>bar가 정의될 때(전역 코드 평가 시점에) 렉시컬 환경이 전역으로 연결되고, 
이후 어디서 호출되든 x는 그 연결(스코프 체인)을 따라 전역의 x=1을 보게 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[모던 자바스크립트 11장. 원시 값과 객체의 비교]]></title>
            <link>https://velog.io/@ann-algorithm/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-11%EC%9E%A5.-%EC%9B%90%EC%8B%9C-%EA%B0%92%EA%B3%BC-%EA%B0%9D%EC%B2%B4%EC%9D%98-%EB%B9%84%EA%B5%90</link>
            <guid>https://velog.io/@ann-algorithm/%EB%AA%A8%EB%8D%98-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-11%EC%9E%A5.-%EC%9B%90%EC%8B%9C-%EA%B0%92%EA%B3%BC-%EA%B0%9D%EC%B2%B4%EC%9D%98-%EB%B9%84%EA%B5%90</guid>
            <pubDate>Mon, 08 Dec 2025 08:46:40 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-들어가기-전에">📌 들어가기 전에…</h1>
<p>원시 타입과 객체 타입은 어떻게 다를까?</p>
<ol>
<li>원시 타입의 값, 즉 원시 값은 변경 불가능한 값이다. 이에 비해 객체(참조) 타입의 값, 즉 객체는 변경 가능한 값이다.</li>
<li>원시 값을 변수에 할당하면 변수(확보된 메모리 공간)에는 실제 값이 저장된다. 이에 비해 객체를 변수에 할당하면 변수(확보된 메모리 공간)에는 참조 값이 저장된다.</li>
<li>원시 값을 갖는 변수는 다른 변수에 할당하면, 원본의 원시 값이 복사되어 전달된다. 이를 값에 의한 전달이라 한다. 이에 비해 객체를 가리키는 변수를 다른 변수에 할당하면, 원본의 참조 값이 복사되어 전달된다. 이를 참조에 의한 전달이라 한다.</li>
</ol>
<h1 id="📌-원시-값">📌 원시 값</h1>
<h2 id="1️⃣-변경-불가능한-값">1️⃣ 변경 불가능한 값</h2>
<blockquote>
<p>원시 타입의 값, 즉 원시 값은 변경 불가능한 값이다.
➡️ 한번 생성된 원시 값은 읽기 전용 값으로서 변경할 수 없다.</p>
</blockquote>
<p>먼저 변수와 값은 구분해서 생각해야 한다.</p>
<ul>
<li><span style="background-color:#FFEBCD">변수</span>: 하나의 값을 저장하기 위해 확보한 메모리 공간 자체 또는 그 메모리 공간을 식별하기 위해 붙인 이름</li>
<li><span style="background-color:#FFEBCD">값</span>: 변수에 저장된 데이터로서 표현식이 평가되어 생성된 결과</li>
</ul>
<p>변경 불가능한 것은 변수가 아니라 값에 대한 진술이다.</p>
<p>즉, “원시 값은 변경 불가능하다”는 말은 <strong>원시</strong> <strong>값 자체를 변경할 수 없다는 것</strong>이지, 변수 값을 변경할 수 없다는 것이 아니다.</p>
<p>변수는 언제든지 재할당을 통해 변수 값을 변경(엄밀히 말하자면 교체)할 수 있다.
➡️ 그래서 <strong>변수</strong>라 명명</p>
<br>

<ul>
<li><span style="background-color:#FFEBCD">상수</span>: 재할당이 금지된 변수<ul>
<li>값을 저장하기 위한 메모리 공간이므로 변수라고 할 수는 있지만, 할당이 한 번만 허용됨</li>
<li>변수 값을 교체할 수 없음
➡️ <span style="background-color:#FDF5E6">상수와 변경 불가능한 값을 동일시할 수는 없음</span></li>
</ul>
</li>
</ul>
<pre><code class="language-js">// const 키워드를 사용해 선언한 변수는 재할당이 금지
// 상수는 재할당이 금지된 변수임
const o = {};

// const 키워드를 사용해 선언한 변수에 할당한 원시 값(상수)은 변경 불가능
// const 키워드를 사용해 선언한 변수에 할당한 객체의 값은 변경 가능
o.a = 1;
console.log(o); // {a: 1}</code></pre>
<br>

<p>원시 값은 변경 불가능한 값, 즉 읽기 전용 값
➡️ 어떤 일이 있어도 불변함</p>
<p>따라서, 이전에도 살펴봤듯이
원시 값을 할당한 변수에 새로운 원시 값을 재할당하면,
메모리 공간에 저장되어 있는 재할당 이전의 원시 값을 변경하는 것이 아니라
<span style="background-color:#FDF5E6">새로운 메모리 공간을 확보하고 재할당한 원시 값을 저장한 후, 변수는 새롭게 재할당한 원시 값을 가리킴</span>
➡️ 변수가 참조하던 메모리 주소가 바뀜</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/d67e214a-6a43-4756-b0a2-26daa8f5b695/image.png">

<br>

<p>원시 값이 변경 가능한 값이라면, 
변수에 새로운 원시 값을 재할당했을 때, 변수가 가리키던 메모리 공간의 주소를 바꿀 필요없이 원시 값 자체를 변경</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/cc364926-a8bb-4634-8fda-908b9d42c6bb/image.png">

<p>➡️ 변수가 참조하는 메모리 공간의 주소가 바뀌지 않음</p>
<br>

<p>하지만 위는 가정일 뿐이고...
<span style="background-color:#FFEBCD">변수 값을 변경하기 위해 원시 값을 재할당하면,
새로운 메모리 공간을 확보하고 재할당한 값을 저장한 후,
변수가 참조하던 메모리 공간의 주소를 변경</span>
➡️ <strong>불변성</strong>이라고 함</p>
<br>

<p><span style="background-color:#FFC0CB">불변성을 갖는 원시 값을 할당한 변수는 재할당 이외에 변수 값을 변경할 수 있는 방법이 없음</span></p>
<p>재할당하는 것 이외에 원시 값인 변수 값을 변경할 수 있다면,
<span style="background-color:#E6E6FA">예기치 않게 변수 값이 변경될 수 있다는 것이고
이는 값의 변경, 즉 상태 변경을 추적하기 어렵게 만듦</span></p>
<h2 id="2️⃣-문자열과-불변성">2️⃣ 문자열과 불변성</h2>
<p>원시 값을 저장하려면 먼저 확보해야 하는 메모리 공간의 크기를 결정해야 함
➡️ 원시 타입별로 메모리 공간의 크기가 미리 정해져 있음
(단, ECMASCript 사양에 문자열 타입(2바이트)과 숫자 타입(8바이트) 이외의 원시 타입은 크기를 명확히 규정하고 있지는 않아서, 브라우저 제조사의 구현에 따라 원시 타입의 크기는 다를 수 있음)</p>
<p>원시 값인 문자열은, <span style="background-color:#FFEBCD">문자열은 0개 이상의 문자로 이뤄진 집합</span>임
<span style="background-color:#FDF5E6">1개의 문자는 2바이트의 메모리 공간에 저장</span>
➡️ 문자열은 몇 개의 문자로 이뤄졌느냐에 따라 필요한 메모리 공간의 크기가 저장</p>
<p>숫자 값은 1도, 1000000도 동일한 8바이트가 필요하지만,
문자열은 (단순하게 생각하면) 1개의 문자로 이뤄진 문자열은 2바이트,
10개의 문자로 이뤄진 문자열은 20바이트 필요</p>
<pre><code class="language-js">// 문자열은 0개 이상의 문자로 이뤄진 집합이다.
var str1 = &#39;&#39;;        // 0개의 문자로 이뤄진 문자열(빈 문자열)
var str2 = &#39;Hello&#39;; // 5개의 문자로 이뤄진 문자열</code></pre>
<p>이 같은 이유로 C에는 char라는 문자 타입만 있을 뿐 문자열 타입은 없음
C에서는 문자열을 문자의 배열로 처리하고,
자바에서는 문자열을 String이라는 객체로 처리</p>
<p>하지만 <span style="background-color:#FDF5E6">자바스크립트는 원시 타입인 문자열 타입을 제공</span>
(장점임... <del>진짜(?)</del>)</p>
<p>아무튼 원시 타입이며, 변경이 불가능함
<span style="background-color:#FFC0CB">문자열이 생성된 이후에는 변경할 수 없다는 것!!</span></p>
<br>

<pre><code class="language-js">var str = &#39;Hello&#39;;
str = &#39;world&#39;;</code></pre>
<ul>
<li>첫 번째 문: 문자열 &#39;Hello&#39; 생성</li>
<li>식별자 str은 문자열 &#39;Hello&#39;가 저장된 메모리 공간의 첫 번째 메모리 셀 주소를 가리킴</li>
<li>두 번째 문: 새로운 문자열 &#39;world&#39;를 메모리에 생성</li>
<li>식별자 str은 문자열 &#39;world&#39;가 저장된 메모리 공간의 두 번째 메모리 셀 주소를 가리킴</li>
</ul>
<p>이때 문자열 &#39;Hello&#39;와 &#39;world&#39;는 모두 메모리에 존재
식별자 str은 문자열 &#39;Hello&#39;를 가리키고 있다가 문자열 &#39;world&#39;를 가리키도록 변경</p>
<br>

<p><span style="background-color:#FFC0CB">문자열은 유사 배열 객체이므로 배열과 유사하게 각 문자에 접근 가능</span></p>
<blockquote>
<p>📝 <strong>유사 배열 객체</strong>
배열처럼 인덱스로 프로퍼티 값에 접근 가능하고,
length 프로퍼티를 갖는 객체</p>
</blockquote>
<ul>
<li>문자열은 마치 배열처럼 인덱스를 통해 각 문자에 접근 가능</li>
<li>문자열은 length 프로퍼티를 갖기 때문에<blockquote>
<p>➡️ 문자열은 유사 배열 객체
즉, for문으로 순회 가능</p>
</blockquote>
<pre><code class="language-js">var str = &#39;string&#39;;
// 문자열은 유사 배열이므로 배열과 유사하게 인덱스를 사용해 각 문자에 접근 가능
console.log(str[0]); // s
// 원시 값인 문자열이 객체처럼 동작한다.
console.log(str.length); // 6
console.log(Str.toUpperCase()); // STRING</code></pre>
갑자기 원시 값인 문자열이 객체일 수도 있다고?
<span style="background-color:#E6E6FA">원시 값을 객체로 사용하면 원시 값을 감싸는 래퍼 객체로 자동 변환됨</span></li>
</ul>
<br>

<pre><code class="language-js">var str = &#39;string&#39;;

// 문자열은 유사 배열이므로 배열과 유사하게 인덱스를 사용해 각 문자에 접근 가능
// 하지만 문자열은 원시 값이므로 변경 불가능. 이때 에러는 발생하지 않음
str[0] = &#39;S&#39;;

console.log(str); // string</code></pre>
<p><code>str[0] = &#39;S&#39;</code>처럼 이미 생성된 문자열의 일부 문자를 변경해도 반영되지 않음
문자열은 변경 불가능한 값이기 때문</p>
<p><span style="background-color:#FFC0CB">이처럼 한 번 생성된 문자열은 읽기 전용 값으로서 변경할 수 없음</span>
따라서 예기치 못한 변경을 막을 수 있고, <span style="background-color:#FDF5E6">신뢰성을 보장</span>함</p>
<p>변수 자체에 새로 문자열을 재할당하는 건 가능
<span style="background-color:#E2D6FF">기존 문자열을 변경하는 게 아 닌 새로운 문자열을 새롭게 할당하는 것</span></p>
<h2 id="3️⃣-값에-의한-전달">3️⃣ 값에 의한 전달</h2>
<pre><code class="language-js">var score = 80;
var copy = score;

console.log(score); // 80
console.log(copy); // 80

score = 100;

console.log(score); // 100
console.log(copy); // ?</code></pre>
<p>위 예제 코드의 핵심은
&quot;변수에 변수를 할당했을 때 무엇이 어떻게 전달되는가?&quot;</p>
<p><code>copy = score</code>에서 score는 변수 값 80으로 평가되므로,
copy에도 80이 할당될 것
➡️ <span style="background-color:#FFC0CB">새로운 숫자 값 80이 생성되어 copy 변수에 할당</span></p>
<p><span style="background-color:#FFC0CB">변수에 원시 값을 갖는 변수를 할당하면, 할당 받는 변수(copy)에는 할당되는 변수(score)의 원시 값이 복사되어 전달</span>
➡️ <strong>값에 의한 전달</strong></p>
<pre><code class="language-js">var score = 80;

// copy 변수에는 score 변수의 값 80이 복사되어 할당
var copy = score;

console.log(score, copy); // 80 80
console.log(score === copy); // true</code></pre>
<p>두 변수는 숫자 값 80을 갖는다는 점에서 동일함</p>
<p>하지만, <span style="background-color:#E2D6FF">score 변수와 copy 변수의 값 80은 다른 메모리 공간에 저장된 별개의 값</span></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/dc9ab668-e9d4-46f4-be19-13f22490aa3a/image.png">

<br>

<pre><code class="language-js">var score = 80;

// copy 변수에는 score 변수의 값 80이 복사되어 할당
var copy = score;

console.log(score, copy); // 80 80
console.log(score === copy); // true

// score 변수와 copy 변수의 값은 다른 메모리 공간에 저장된 별개의 값
// ➡️ score 변수의 값을 변경해도 copy 변수의 값에는 어떠한 영향도 주지 않음
score = 100;

console.log(score, copy); // 100 80
console.log(score === copy); // false</code></pre>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/a9501f4e-5e6c-4a67-b02d-714b822827e3/image.png">

<p>(그림은 그림일 뿐... 다를 수도)</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/a47f34e9-5f52-4914-b12a-e9b86150e944/image.png">

<p>처음엔 같은 메모리(원시 값)을 참조하다가,
어느 한 쪽의 변수에 재할당이 이루어졌을 때
새로운 메모리 공간에 재할당된 값을 저장하도록 동작도 가능</p>
<br>

<p>&quot;값에 의한 전달&quot;이라는 용어는,
자바스크립트를 위한 용어가 아님</p>
<p><span style="background-color:#FFC0CB">엄격하게는 변수에 값이 아니라 메모리 주소가 전달됨</span>
<span style="background-color:#E2D6FF">변수 같은 식별자는 값이 아니라 메모리 주소를 저장하고 있기 때문</span></p>
<blockquote>
<p>식별자는 어떤 값을 구별해서 식별할 수 있는 이름이고,
값은 메모리 공간에 저장이 되어 있음
식별자는 메모리 공간에 저장되어 있는 어떤 값을 구별해서 식별해낼 수 있어야 하므로,
<strong>변수와 같은 식별자는 값이 아니라 메모리 주소를 기억함</strong>
➡️ 즉, 식별자는 메모리 주소에 붙인 이름</p>
</blockquote>
<br>

<pre><code class="language-js">var x = 10;</code></pre>
<p>할당 연산자는 숫자 리터럴 10에 의해 생성된 숫자 값 10이 저장된 메모리 공간의 주소를 전달
식별자 x는 메모리 공간에 저장된 숫자 값 10을 식별 가능</p>
<br>

<pre><code class="language-js">var copy = score;</code></pre>
<p>score는 식별자 표현식으로서 숫자 값 80으로 평가됨</p>
<ul>
<li>새로운 80을 생성(복사)해서 메모리 주소를 전달하는 방식<ul>
<li><span style="background-color:#FDF5E6">할당 시점에 두 변수가 기억하는 메모리 주소가 다름</span></li>
</ul>
</li>
<li>score의 변수값 80의 메모리 주소를 그대로 전달하는 방식<ul>
<li><span style="background-color:#FDF5E6">할당 시점에 두 변수가 기억하는 메모리 주소가 같음</span></li>
</ul>
</li>
</ul>
<p>아무튼,
<strong>&quot;값에 의한 전달&quot;은 값을 전달하는 것이 아니라 메모리 주소를 전달한다는 것</strong>
단, 전달된 메모리 주소를 통해 메모리 공간에 접근하여 <strong>값을 참조</strong></p>
<p>어찌 됐든,
변수에 원시 값을 갖는 변수를 할당하면,
결국은 두 변수의 원시 값은 서로 다른 메모리 공간에 저장된 별개의 값
➡️ 한 쪽의 값이 변경되어도 서로 간섭 불가</p>
<h1 id="📌-객체">📌 객체</h1>
<p>객체는 프로퍼티의 개수가 정해져 있지 않고,
동적으로 추가 및 삭제 가능</p>
<p>프로퍼티의 값에도 제약이 없음</p>
<p>➡️ 객체는 원시 값과 같이, 확보해야 할 메모리 공간의 크기를 사전에 정해둘 수 없음
따라서 경우에 따라 크기가 매우 클 수 있음
프로퍼티에 접근하는 것도 비용이 더 들 수 있음...</p>
<p>객체는 복합적인 자료구조이므로 객체를 관리하는 방식이 복잡하고, 브라우저마다 구현 방식이 다를 수 있음</p>
<p>아무튼, 원시 값과는 다른 방식으로 동작하게 설계되어 있음</p>
<blockquote>
<p>📝 <strong>자바스크립트 객체의 관리 방식</strong>
프로퍼티 키를 인덱스로 사용하는 해시 테이블이라 생각할 수 있지만,
일반적인 해시 테이블보다 나은 방법으로 구현</p>
</blockquote>
<p>자바나 C++ 같은 클래스 기반 객체지향 언어는 사전에 정의된 클래스를 기반으로 객체(인스턴스)를 생성
즉, 객체를 생성하기 전에 이미 프로퍼티와 메서드가 정해져 있으며 그대로 붕어빵 찍어내듯 객체를 생성하고,
생성 이후에는 프로퍼티를 삭제하거나 추가 불가</p>
<blockquote>
</blockquote>
<p>하지만 자바스크립트는 클래스 없이 객체를 생성할 수 있으며,
객체 생성 이후에 동적으로 프로퍼티와 메서드 추가 가능
사용에는 편리하지만,
생성과 접근에 비용이 더 많이 드는 방식</p>
<blockquote>
</blockquote>
<p>따라서 V8 자바스크립트 엔진에서는 프로퍼티에 접근하기 위해 동적 탐색 대신 히든 클래스 방식을 사용</p>
<h2 id="1️⃣-변경가능한-값">1️⃣ 변경가능한 값</h2>
<blockquote>
<p>객체(참조) 타입의 값
즉, 객체는 변경 가능한 값</p>
</blockquote>
<p>변수에 객체를 할당해 보자</p>
<pre><code class="language-js">var person = {
  name: &#39;Lee&#39;,
};</code></pre>
<p>원시 값을 할당한 변수가 기억하는 메모리 주소를 통해 메모리 공간에 접근하면 원시 값에 접근 가능
즉, 원시 값을 할당한 변수는 원시 값 자체를 값으로 가짐</p>
<p><span style="background-color:#FFEBCD">객체를 할당한 변수가 기억하는 메모리 주소를 통해 메모리 공간에 접근하면, 값이 아닌 참조 값에 접근 가능</span>
<span style="background-color:#FDF5E6">참조 값은 생성된 객체가 저장된 메모리 공간의 주소</span></p>
<p>➡️ <span style="background-color:#E2D6FF">객체를 할당한 변수에는, 생성된 객체가 실제로 저장된 메모리 공간의 주소가 저장되어 있음</span>
이 값이 <strong>참조 값</strong>임
변수는 이 참조값을 통해 객체에 접근 가능</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/cd97e949-cf29-446f-8f32-2d316e0f2d8a/image.png">

<p>객체를 할당한 변수를 참조하면,
그 메모리에 저장되어 있는 참조 값을 통해 실제 객체에 접근</p>
<pre><code class="language-js">// 할당이 이뤄지는 시점에 객체 리터럴이 해석되고, 그 결과 객체 생성
var person = {
  name: &#39;Lee&#39;
};

// person 변수에 저장되어 있는 참조 값으로 실제 객체에 접근
console.log(person); // {name: &quot;Lee&quot;}</code></pre>
<p>일반적으로 원시 값을 할당한 변수의 경우 &quot;변수는 ~값을 갖는다&quot; 또는 &quot;변수의 값은 ~다&quot;라고 표현
객체를 할당한 변수의 경우 &quot;변수는 객체를 참조하고 있다&quot; 또는 &quot;변수는 객체를 가리키고 있다&quot;라고 표현
➡️ (예제를 보면) person 변수는 객체 {name: &#39;Lee&#39;}를 가리키고(참조하고) 있음</p>
<p>원시 값은 변경 불가능한 값이므로 원시 값을 갖는 변수의 값을 변경하려면, 재할당 외에는 방법이 없음
하지만 <span style="background-color:#FFC0CB">객체는 변경 가능한 값</span>
➡️ <span style="background-color:#FDF5E6">객체를 할당한 변수는 재할당 없이 객체 변경 가능</span></p>
<p>즉, 재할당 없이 프로퍼티를 동적으로 추가할 수 있고,
프로퍼티 값을 갱신할 수 있으며,
프로퍼티 자체를 삭제할 수 있음</p>
<br>

<pre><code class="language-js">var person = {
  name: &#39;Lee&#39;
};

// 프로퍼티 값 갱신
person.name = &#39;Kim&#39;;

// 프로퍼티 동적 생성
person.address = &#39;Seoul&#39;;

console.log(person); // {name: &quot;Kim&quot;, address: &quot;Seoul&quot;}</code></pre>
<p>객체를 할당한 변수에 재할당을 한 것은 아니므로, 객체를 할당한 변수의 참조 값은 변경되지 않음
객체 자체를 변경할 뿐임</p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/2d355969-a7de-48bd-8582-6a98049f5ab1/image.png">

<br>

<p>객체를 생성하고 관리하는 방식은 매우 복잡하고 비용이 듦
객체를 재할당(변경)할 때마다 원시 값처럼 이전 값을 복사해서 새롭게 생성한다면 명확하고 신뢰성 있겠지만,
객체의 크기가 매우 클 수도 있고, 프로퍼티가 객체일 수도 있고... ➡️ 복사해서 생성하는 비용이 많이 듦</p>
<p>즉, 메모리의 효율적 소비가 어렵고 성능이 나빠짐</p>
<p>따라서, <span style="background-color:#FDF5E6">메모리를 효율적으로 사용하기 위해, 그리고 객체를 효율적으로 사용하기 위해 
객체는 변경 가능한 값으로 설계</span>되어 있음
즉, 메모리 사용의 효율성과 성능을 위해 어느 정도의 구조적인 단점을 감안한 설계</p>
<br>

<p>단, 객체는 원시 값과 다르게 <span style="background-color:#E2D6FF"><strong>여러 개의 식별자가 하나의 객체를 공유할 수 있음</strong></span></p>
<blockquote>
<p>📝 <strong>얕은 복사와 깊은 복사</strong>
객체를 프로퍼티 값으로 갖는 객체의 경우</p>
</blockquote>
<ul>
<li>얕은 복사는 한 단계까지만 복사</li>
<li>깊은 복사는 객체에 중첩된 객체까지 모두 복사</li>
</ul>
<pre><code class="language-js">const o = { x: { y: 1} };

// 얕은 복사
const c1 = { ...o };    // 스프레드 문법
console.log(c1 === o);    // false
console.log(c1.x === o.x); // true</code></pre>
<p>얕은 복사와 깊은 복사로 생성된 객체는 원본과 다른 객체
➡️ 원본과 복사본은 참조 값이 다른 별개의 객체</p>
<p><span style="background-color:#FFC0CB">하지만 얕은 복사는 객체에 중첩되어 있는 객체의 경우 참조 값을 복사하고,
깊은 복사는 객체에 중첩되어 있는 객체까지 모두 복사해서 원시 값처럼 완전한 복사본을 만듦</span></p>
<p>참고)
원시 값을 할당한 변수를 다른 변수에 할당하는 걸 깊은 복사,
객체를 할당한 변수를 다른 변수에 할당하는 걸 얕은 복사,
라고 하기도 함</p>
<h2 id="2️⃣-참조에-의한-전달">2️⃣ 참조에 의한 전달</h2>
<p>여러 개의 식별자가 하나의 객체를 공유한다는 게 무엇인가?
그리고 이로 인해 무슨 문제가 발생하는가?</p>
<pre><code class="language-js">var person = {
  name: &#39;Lee&#39;
};

// 참조 값을 복사(얕은 복사)
var copy = person;</code></pre>
<p>객체를 가리키는 변수(원본, person)를 다른 변수(사본, copy)에 할당하면
<span style="background-color:#FFC0CB">원본의 참조 값이 복사되어 전달</span>됨
➡️ <strong>참조에 의한 전달</strong></p>
<img src="https://velog.velcdn.com/images/ann-algorithm/post/1722e81c-b706-4302-8068-26b236212d09/image.png">

<p>위 그림처럼 copy본에도 원본 person의 참조 값이 저장됨
➡️ 원본 person과 사본 copy는 모두 동일한 객체를 가리킴</p>
<p><span style="background-color:#FFE4E1">즉, 두 개의 식별자가 하나의 객체를 공유함</span></p>
<p>따라서 원본 또는 사본 중 한 쪽에서 객체를 변경(변수에 새로운 객체를 재할당하는 것이 아니라, 객체의 프로퍼티 값을 변경하거나 프로퍼티를 추가, 삭제)하면 서로 영향을 주고 받음</p>
<pre><code class="language-js">var person = {
  name: &#39;Lee&#39;
};

// 참조 값을 복사(얕은 복사)
// copy와 person은 동일한 참조 값을 가짐
var copy = person;

// copy와 person은 동일한 객체 참조
console.log(copy === person); // true

// copy를 통해서, 객체 변경
copy.name = &#39;Kim&#39;;

// person을 통해 객체 변경
person.address = &#39;Seoul&#39;;

// copy와 person은 동일한 객체를 가리킴
// 따라서 어느 한 쪽에서 객체를 변경하면 영향을 받음
console.log(person);    // {name: &quot;Kim&quot;, address: &quot;Seoul&quot;}
console.log(copy);        // {name: &quot;Kim&quot;, address: &quot;Seoul&quot;}</code></pre>
<br>

<p>&quot;값에 의한 전달&quot;과 &quot;참조에 의한 전달&quot;은 
식별자가 기억하는 메모리 공간에 저장되어 있는 값을 복사해서 전달한다는 면에서 동일</p>
<p>단, 식별자가 기억하는 메모리 공간(즉 변수에 저장되어 있는 값)이 원시 값이냐, 참조 값이냐의 차이</p>
<p>➡️ 자바스크립트에는 &quot;참조에 의한 전달&quot;은 존재하지 않고 &quot;값에 의한 전달&quot;만이 존재</p>
<p>다만,...
이 책으로 공부 시
전달되는 값의 종류가 원시 값인지 참조 값인지 구별하기 위해
&quot;값에 의한 전달&quot;과 &quot;참조에 의한 전달&quot;로 구분하여 명시</p>
<br>

<h1 id="정리를-위한-퀴즈">정리를 위한 퀴즈</h1>
<pre><code class="language-js">var person1 = {
  name: &#39;Lee&#39;
};

var person2 = {
  name: &#39;Lee&#39;
};

console.log(person1 === person2); // 1️⃣ ?
console.log(person1.name === person2.name); // 2️⃣ ?</code></pre>
<p><code>===</code>는 변수에 저장되어 있는 값과 타입을 모두 비교
이 연산자를 통해 객체를 할당한 변수를 비교하면 참조 값을 비교하고,
원시 값을 할ㅇ당한 변수를 비교하면 원시 값을 비교함</p>
<p>객체 리터럴은 평가될 때마다 객체를 생성
person1 변수와 person2 변수가 가리키는 객체는 내용은 같지만 다른 메모리에 저장된 별개의 객체
➡️ person1 변수와 person2 변수의 참조 값은 전혀 다른 값</p>
<p>따라서 1️⃣은 false</p>
<p>프로퍼티 값을 참조하는 person1.name과 person2.name은 값으로 평가될 수 있음
따라서 모두 원시 값 &#39;Lee&#39;로 평가되므로</p>
<p>2️⃣는 true</p>
]]></description>
        </item>
    </channel>
</rss>