<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>chacha_fe.log</title>
        <link>https://velog.io/</link>
        <description>나는야 프린이</description>
        <lastBuildDate>Sat, 17 Jun 2023 13:16:54 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>chacha_fe.log</title>
            <url>https://velog.velcdn.com/images/chacha_fe/profile/4e177b9b-1734-4fbc-a830-f933bc111ff5/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. chacha_fe.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/chacha_fe" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[ File Conventions ] Files]]></title>
            <link>https://velog.io/@chacha_fe/File-Conventions-Files</link>
            <guid>https://velog.io/@chacha_fe/File-Conventions-Files</guid>
            <pubDate>Sat, 17 Jun 2023 13:16:54 GMT</pubDate>
            <description><![CDATA[<h1 style="color: #61AFEE">error.js</h1>

<p><code>error.js</code>는 경로 세그먼트에 대한 오류 UI 경계를 정의한다.</p>
<pre><code class="language-jsx">// app/dashboard/error.tsx

&#39;use client&#39; // Error components must be Client Components

import { useEffect } from &#39;react&#39;

export default function Error({
  error,
  reset,
}: {
  error: Error
  reset: () =&gt; void
}) {
  useEffect(() =&gt; {
    // Log the error to an error reporting service
    console.error(error)
  }, [error])

  return (
    &lt;div&gt;
      &lt;h2&gt;Something went wrong!&lt;/h2&gt;
      &lt;button
        onClick={
          // Attempt to recover by trying to re-render the segment
          () =&gt; reset()
        }
      &gt;
        Try again
      &lt;/button&gt;
    &lt;/div&gt;
  )
}</code></pre>
<br/>

<h2 id="props">Props</h2>
<h3 id="error"><code>error</code></h3>
<p><code>error</code> 객체의 인스턴스다. 이 오류는 서버나 클라이언트에서 발생할 수 있다.</p>
<h3 id="reset"><code>reset</code></h3>
<p>응답을 반환하지 않는 error boundary를 재설정하는 함수다.</p>
<h3 id="알아두면-좋은-사항">알아두면 좋은 사항</h3>
<ul>
<li><p><code>error.js</code> boundary는 클라이언트 컴포넌트여야 한다.</p>
</li>
<li><p><code>error.js</code> boundary는 같은 세그먼트의 <code>layout.js</code> 컴포넌트에서 발생한 오류를 처리하지 않는다. 이는 error boundary가 해당 레이아웃 컴포넌트 내에 중첩되어 있기 때문이다.</p>
</li>
<li><p>특정 레이아웃의 오류를 처리하려면 <code>error.js</code> 파일을 레이아웃의 상위 세그먼트에 배치한다.</p>
</li>
<li><p>루트 레이아웃이나 템플릿 내에서 오류를 처리하려면 <code>error.js</code>의 변형인 <code>app/global-error.js</code>를 사용한다.</p>
</li>
</ul>
<br/>

<h2 id="global-errorjs">global-error.js</h2>
<p>root <code>layout.js</code>에서 발생하는 오류를 특별히 처리하려면, 루트 <code>app</code> 디렉토리에 위치한 <code>error.js</code>의 변형인 <code>app/global-error.js</code>를 사용한다.</p>
<pre><code class="language-jsx">// app/global-error.js

&#39;use client&#39;

export default function GlobalError({
  error,
  reset,
}: {
  error: Error
  reset: () =&gt; void
}) {
  return (
    &lt;html&gt;
      &lt;body&gt;
        &lt;h2&gt;Something went wrong!&lt;/h2&gt;
        &lt;button onClick={() =&gt; reset()}&gt;Try again&lt;/button&gt;
      &lt;/body&gt;
    &lt;/html&gt;
  )
}</code></pre>
<br/>

<h3 id="알아두면-좋은-사항-1">알아두면 좋은 사항</h3>
<ul>
<li><code>global-error.js</code>는 활성화되었을 때 루트 <code>layout.js</code>를 대체하므로 자체 <code>&lt;html&gt;</code> 및 <code>&lt;body&gt;</code> 태그를 정의해야 한다.</li>
<li>오류 UI를 디자인하는 동안, <strong>React 개발자 도구</strong>를 사용하여 오류 경계(Error boundaries)를 수동으로 전환하는 것이 도움이 될 수 있다.</li>
</ul>
<br/>

<hr>
<h1 id="layoutjs">layout.js</h1>
<p>레이아웃은 라우트 간에 공유되는 UI다.</p>
<pre><code class="language-jsx">// app/dashboard/layout.tsx

export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return &lt;section&gt;{children}&lt;/section&gt;
}</code></pre>
<br/>

<p>루트 레이아웃은 루트 앱 디렉토리에서 가장 상위에 위치한 레이아웃이다. <code>&lt;html&gt;</code> 및 <code>&lt;body&gt;</code> 태그 및 다른 전역적으로 공유되는 UI를 정의하는 데 사용된다.</p>
<pre><code class="language-jsx">// app/layout.tsx

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    &lt;html lang=&quot;en&quot;&gt;
      &lt;body&gt;{children}&lt;/body&gt;
    &lt;/html&gt;
  )
}</code></pre>
<br/>

<h2 id="props-1">Props</h2>
<h3 id="children--필수적-">children ( 필수적 )</h3>
<p>레이아웃 컴포넌트는 <code>children</code> prop을 받아서 사용해야 한다. 렌더링 중에 <code>children</code>은 레이아웃이 감싸고 있는 라우트 세그먼트로 채워진다. 주로 자식 레이아웃 (있는 경우) 또는 페이지의 컴포넌트일 것이다. 그러나 해당되는 경우 <code>Loading</code>이나 <code>Error</code>와 같은 다른 특수 파일일 수도 있다.</p>
<h3 id="params--선택적-">params ( 선택적 )</h3>
<p>루트 세그먼트에서 해당 레이아웃까지의 <strong>동적 라우트</strong> 매개변수 객체다.</p>
<table>
<thead>
<tr>
<th>Example</th>
<th>URL</th>
<th>params</th>
</tr>
</thead>
<tbody><tr>
<td>app/dashboard/[team]/layout.js</td>
<td>/dashboard/1</td>
<td>{ team: &#39;1&#39; }</td>
</tr>
<tr>
<td>app/shop/[tag]/[item]/layout.js</td>
<td>/shop/1/2</td>
<td>{ tag: &#39;1&#39;, item: &#39;2&#39; }</td>
</tr>
<tr>
<td>app/blog/[...slug]/layout.js</td>
<td>/blog/1/2</td>
<td>{ slug: [&#39;1&#39;, &#39;2&#39;] }</td>
</tr>
</tbody></table>
<pre><code class="language-jsx">// app/shop/[tag]/[item]/layout.tsx

export default function ShopLayout({
  children,
  params,
}: {
  children: React.ReactNode
  params: {
    tag: string
    item: string
  }
}) {
  // URL -&gt; /shop/shoes/nike-air-max-97
  // `params` -&gt; { tag: &#39;shoes&#39;, item: &#39;nike-air-max-97&#39; }
  return &lt;section&gt;{children}&lt;/section&gt;
}</code></pre>
<br/>

<h3 id="알아두면-좋은-점">알아두면 좋은 점</h3>
<p><strong>레이아웃은 <code>searchParams</code> prop을 받지 않는다.</strong></p>
<p><strong>page</strong>와 달리 <strong>layout</strong> 컴포넌트는 <code>searchParams</code> prop을 받지 않는다. 이는 공유된 레이아웃이 탐색 중에 다시 렌더링되지 않기 때문에 navigation 사이에 오래된 <code>searchParams</code>가 발생할 수 있기 때문이다.</p>
<p>클라이언트 측 navigation을 사용할 때, Next.js는 두 라우트 사이에서 공통 레이아웃 아래에 있는 페이지의 일부만 자동으로 렌더링한다.</p>
<br/>

<p>다음 디렉토리 구조의 예를 들어보면, <code>dashboard/layout.tsx</code>는 <code>/dashboard/settings</code> 및 <code>/dashboard/analytics</code>의 공통 레이아웃이다.</p>
<pre><code class="language-jsx">app
└── dashboard
    ├── layout.tsx
    ├── settings
    │   └── page.tsx
    └── analytics
        └── page.js</code></pre>
<br/>

<p><code>/dashboard/settings</code>에서 <code>/dashboard/analytics</code>로 이동할 때, <code>/dashboard/analytics</code>의 <code>page.tsx</code>가 서버에서 렌더링된다. 왜냐하면 이것은 변경된 UI이기 때문이다. </p>
<p>반면, <code>dashboard/layout.tsx</code>는 두 라우트 사이에서 공통으로 사용되기 때문에 다시 렌더링되지 않는다.</p>
<p>이 성능 최적화는 레이아웃을 공유하는 페이지 간의 navigation을 빠르게 만들어준다. 왜냐하면 해당 페이지의 데이터 가져오기와 렌더링만 실행하면 되기 때문에 공유 레이아웃이 자체 데이터를 가져오는 라우트 전체를 실행할 필요가 없기 때문이다.</p>
<p><code>dashboard/layout.tsx</code>가 다시 렌더링되지 않기 때문에, 레이아웃 서버 컴포넌트의 <code>searchParams</code> prop은 navigation 후에 <strong>오래된(stale)</strong> 상태가 될 수 있다.</p>
<p>대신에, Page의 <code>searchParams</code> prop이나 Client 컴포넌트에서 <code>useSearchParams</code> 훅을 사용하여 클라이언트에서 최신 <code>searchParams</code>와 함께 다시 렌더링되도록 한다.</p>
<br/>

<h3 id="rootlayout">RootLayout</h3>
<ul>
<li><p><code>app</code> 디렉토리에는 루트 <code>app/layout.js</code>가 포함되어야 한다.</p>
</li>
<li><p>루트 레이아웃은 <code>&lt;html&gt;</code> 및 <code>&lt;body&gt;</code> 태그를 정의해야 한다.</p>
</li>
<li><p>루트 레이아웃에는 수동으로 <code>&lt;title&gt;</code> 및 <code>&lt;meta&gt;</code>와 같은 <code>&lt;head&gt;</code> 태그를 추가해서는 안된다. 대신에 <code>&lt;head&gt;</code> 요소의 스트리밍과 중복 제거와 같은 고급 요구 사항을 자동으로 처리하는 <code>Metadata API</code>를 사용해야 한다.</p>
</li>
<li><p><strong>route groups</strong>를 사용하여 여러 개의 루트 레이아웃을 생성할 수 있다.</p>
</li>
<li><p>여러 개의 루트 레이아웃을 통해 navigation할 경우 전체 페이지 로드가 발생합니다(클라이언트 측 navigation이 아닌 경우). 예를 들어, <code>app/(shop)/layout.js</code>를 사용하는 <code>/cart</code>에서 <code>app/(marketing)/layout.js</code>를 사용하는 <code>/blog</code>로 이동할 경우 전체 페이지 로드가 발생합니다. 이는 여러 개의 루트 레이아웃에만 해당됩니다.</p>
</li>
</ul>
<br/>

<hr>
<h1 id="loadingjs">loading.js</h1>
<p><code>loading.js</code>는 <strong>Suspense</strong>를 기반으로 한 즉시 로딩 상태를 생성할 수 있다.</p>
<p>기본적으로 이 파일은 <strong>서버 컴포넌트</strong>다. 그러나 <code>&quot;use client&quot;</code> 지시문을 통해 클라이언트 컴포넌트로 사용할 수도 있다.</p>
<pre><code class="language-jsx">// app/feed/loading.js

export default function Loading() {
  // Or a custom loading skeleton component
  return &lt;p&gt;&#39;Loading...&#39;&lt;/p&gt;
}</code></pre>
<br/>

<hr>
<h1 id="not-foundjs">not-found.js</h1>
<p><code>not-found.js</code>은 라우트 세그먼트 내에서 <code>notFound</code> 함수가 throw될 때 UI를 렌더링하는 데 사용된다. 커스텀 UI를 제공하는 것 외에도, Next.js는 <code>404 HTTP</code> 상태 코드를 반환한다.</p>
<pre><code class="language-jsx">// app/blog/not-found.js

import Link from &#39;next/link&#39;

export default function NotFound() {
  return (
    &lt;div&gt;
      &lt;h2&gt;Not Found&lt;/h2&gt;
      &lt;p&gt;Could not find requested resource&lt;/p&gt;
      &lt;p&gt;
        View &lt;Link href=&quot;/blog&quot;&gt;all posts&lt;/Link&gt;
      &lt;/p&gt;
    &lt;/div&gt;
  )
}</code></pre>
<br/>

<h3 id="알아두어야-할-점">알아두어야 할 점</h3>
<p><code>notFound()</code> 오류를 예상하여 catch하는 것 외에도, 루트 <code>app/not-found.js</code> 파일은 전체 애플리케이션에서 일치하지 않는 URL을 처리한다. 이는 사용자가 앱에서 처리되지 않은 URL을 방문할 경우 <code>app/not-found.js</code> 파일에서 내보낸 UI가 표시됨을 의미한다.</p>
<br/>

<hr>
<h1 id="pagejs">page.js</h1>
<p><code>page</code>는 라우트에 고유한 UI다.</p>
<h3 id="params선택적"><code>params(선택적)</code></h3>
<p>루트 세그먼트부터 해당 페이지까지의 <strong>동적 라우트</strong> 매개변수를 담고 있는 객체다. 예를 들면 다음과 같다.</p>
<table>
<thead>
<tr>
<th>Example</th>
<th>URL</th>
<th>params</th>
</tr>
</thead>
<tbody><tr>
<td>app/shop/[slug]/page.js</td>
<td>/shop/1</td>
<td>{ slug: &#39;1&#39; }</td>
</tr>
<tr>
<td>app/shop/[category]/[item]/page.js</td>
<td>/shop/1/2</td>
<td>{ category: &#39;1&#39;, item: &#39;2&#39; }</td>
</tr>
<tr>
<td>app/shop/[...slug]/page.js</td>
<td>/shop/1/2</td>
<td>{ slug: [&#39;1&#39;, &#39;2&#39;] }</td>
</tr>
</tbody></table>
<h3 id="searchparams선택적"><code>searchParams(선택적)</code></h3>
<p>현재 URL의 검색 매개변수를 포함하는 객체다. 예를 들면 다음과 같다.</p>
<table>
<thead>
<tr>
<th>URL</th>
<th>searchParams</th>
</tr>
</thead>
<tbody><tr>
<td>/shop?a=1</td>
<td>{ a: &#39;1&#39; }</td>
</tr>
<tr>
<td>/shop?a=1&amp;b=2</td>
<td>{ a: &#39;1&#39;, b: &#39;2&#39; }</td>
</tr>
<tr>
<td>/shop?a=1&amp;a=2</td>
<td>{ a: [&#39;1&#39;, &#39;2&#39;] }</td>
</tr>
</tbody></table>
<br/>

<h3 id="알아두어야-할-점-1">알아두어야 할 점</h3>
<ul>
<li><p><code>searchParams</code>는 값을 미리 알 수 없는 동적 API다. 이를 사용하면 페이지가 요청 시 동적 렌더링으로 선택된다.</p>
</li>
<li><p><code>searchParams</code>는 <code>URLSearchParams</code> 인스턴스가 아닌 일반 JavaScript 객체를 반환한다.</p>
</li>
</ul>
<br/>

<hr>
<h1 id="routejs">route.js</h1>
<p>Route Handlers를 사용하면 Web <strong>요청</strong> 및 <strong>응답</strong> API를 사용하여 특정 라우트에 대한 사용자 정의 요청 핸들러를 만들 수 있다.</p>
<h2 id="http-methods">HTTP Methods</h2>
<p><code>route.js</code>을 사용하면 특정 라우트에 대한 사용자 정의 요청 핸들러를 생성할 수 있다. 다음 <strong>HTTP 메서드</strong>가 지원된다.  <code>GET</code>, <code>POST</code>, <code>PUT</code>, <code>PATCH</code>, <code>DELETE</code>, <code>HEAD</code> 및 <code>OPTIONS</code>.</p>
<pre><code class="language-jsx">// route.js

export async function GET(request: Request) {}

export async function HEAD(request: Request) {}

export async function POST(request: Request) {}

export async function PUT(request: Request) {}

export async function DELETE(request: Request) {}

export async function PATCH(request: Request) {}

// If `OPTIONS` is not defined, Next.js will automatically implement `OPTIONS` and  set the appropriate Response `Allow` header depending on the other methods defined in the route handler.
export async function OPTIONS(request: Request) {}</code></pre>
<br/>


<p>Route Handlers는 <code>app</code> 디렉토리 내에서만 사용할 수 있다. API Routes (<code>pages</code>)와 Route Handlers (<code>app</code>)를 함께 사용할 필요는 없으며, Route Handlers는 모든 사용 사례를 처리할 수 있어야 한다.</p>
<br/>

<h2 id="parameters">Parameters</h2>
<h3 id="request-선택적">request (선택적)</h3>
<p><code>request</code> 객체는 <strong>NextRequest</strong> 객체로, Web <strong>요청 API</strong>의 확장이다. <code>NextRequest</code>를 사용하면 수신된 요청에 대한 추가적인 제어를 할 수 있다. 이를 통해 <code>cookies</code>에 쉽게 액세스하고 확장된 파싱된 URL 객체인 <code>nextUrl</code>에 쉽게 접근할 수 있다.</p>
<h3 id="context-선택적">context (선택적)</h3>
<pre><code class="language-jsx">// app/dashboard/[team]/route.js

export async function GET(request, context: { params }) {
  const team = params.team // &#39;1&#39;
}</code></pre>
<p>현재 <code>context</code>의 유일한 값은 <code>params</code>이며, 이는 현재 라우트의 <strong>동적 라우트 매개변수</strong>를 포함하는 객체다.</p>
<h2 id="nextresponse">NextResponse</h2>
<p>Route Handlers는 <code>NextResponse</code> 객체를 반환함으로써 Web <strong>응답 API</strong>를 확장할 수 있다. 이를 통해 쿠키 설정, 헤더 설정, 리다이렉트 및 리라이트를 쉽게 처리할 수 있다.</p>
<br/>

<hr>
<h1 id="route-segment-config">Route Segment Config</h1>
<p>Route Segment 옵션은 다음 변수들을 직접 내보내는 방식으로 <strong>Page</strong>, <strong>Layout</strong> 또는 <strong>Route Handler</strong>의 동작을 구성할 수 있도록 한다.</p>
<table>
<thead>
<tr>
<th>Option</th>
<th>Type</th>
<th>Default</th>
</tr>
</thead>
<tbody><tr>
<td><a href="https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamic">https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamic</a></td>
<td>&#39;auto&#39;</td>
<td>&#39;force-dynamic&#39;</td>
</tr>
<tr>
<td><a href="https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamicparams">https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamicparams</a></td>
<td>boolean</td>
<td>true</td>
</tr>
<tr>
<td><a href="https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#revalidate">https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#revalidate</a></td>
<td>false</td>
<td>&#39;force-cache&#39;</td>
</tr>
<tr>
<td><a href="https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#fetchcache">https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#fetchcache</a></td>
<td>&#39;auto&#39;</td>
<td>&#39;default-cache&#39;</td>
</tr>
<tr>
<td><a href="https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#runtime">https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#runtime</a></td>
<td>&#39;nodejs&#39;</td>
<td>&#39;edge&#39;</td>
</tr>
<tr>
<td><a href="https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#preferredregion">https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#preferredregion</a></td>
<td>&#39;auto&#39;</td>
<td>&#39;global&#39;</td>
</tr>
</tbody></table>
<pre><code class="language-jsx">// layout.tsx / page.tsx / route.ts

export const dynamic = &#39;auto&#39;
export const dynamicParams = true
export const revalidate = false
export const fetchCache = &#39;auto&#39;
export const runtime = &#39;nodejs&#39;
export const preferredRegion = &#39;auto&#39;

export default function MyComponent() {}</code></pre>
<br/>

<p>현재 구성 옵션의 값은 정적으로 분석 가능해야 한다. 예를 들어, <code>revalidate = 600</code>은 유효하지만 <code>revalidate = 60 * 10</code>은 유효하지 않다.</p>
<br/>

<h2 id="options">Options</h2>
<h3 id="dynamic"><code>dynamic</code></h3>
<p>레이아웃이나 페이지의 동적 동작을 완전히 정적으로 또는 완전히 동적으로 변경한다.</p>
<pre><code class="language-jsx">// layout.tsx / page.tsx / route.ts

export const dynamic = &#39;auto&#39;
// &#39;auto&#39; | &#39;force-dynamic&#39; | &#39;error&#39; | &#39;force-static&#39;</code></pre>
<br/>

<p><code>app</code> 디렉토리의 새로운 모델은 <code>page</code> 디렉토리의 <code>getServerSideProps</code>와 <code>getStaticProps</code>의  바이너리 <strong>all-of-nothing</strong> 모델보다는 <code>fetch</code> 요청 수준의 세분화된 캐싱 제어를 선호한다. <code>dynamic</code> 옵션은 이전 모델로의 편리한 복귀 방법이며 간단한 마이그레이션 경로를 제공한다.</p>
<ul>
<li><p>*<em><code>auto (기본값)</code> *</em>
동적 동작으로 전환하지 않는 한 모든 컴포넌트가 동적 동작에 참여하지 않도록 최대한 캐싱한다.</p>
</li>
<li><p><strong><code>force-dynamic</code></strong>
모든 <code>fetch</code> 요청의 캐싱을 비활성화하고 항상 재검증하여 레이아웃이나 페이지의 동적 렌더링과 동적 데이터 가져오기를 강제한다. 이 옵션은 다음과 동일하다.</p>
<ul>
<li><code>pages</code> 디렉토리의 <code>getServerSideProps()</code></li>
<li>레이아웃이나 페이지의 모든 <code>fetch()</code> 요청의 옵션을 <code>{ cache: &#39;no-store&#39;, next: { revalidate: 0 } }</code>로 설정하기.</li>
<li>세그먼트 구성을 <code>export const fetchCache = &#39;force-no-store&#39;</code>로 설정하기.</li>
</ul>
</li>
<li><p><strong><code>error</code></strong>
동적 함수나 동적 <code>fetch</code> 사용 시 오류가 발생하여 레이아웃이나 페이지의 정적 렌더링과 정적 데이터 가져오기를 강제한다. 이 옵션은 다음과 동일하다.</p>
<ul>
<li><code>pages</code> 디렉토리의 <code>getStaticProps()</code></li>
<li>레이아웃이나 페이지의 모든 <code>fetch()</code> 요청의 옵션을 <code>{ cache: &#39;force-cache&#39; }</code>로 설정하기.</li>
<li>세그먼트 구성을 <code>fetchCache = &#39;only-cache&#39;, dynamicParams = false</code>로 설정하기.</li>
<li><code>dynamic = &#39;error&#39;</code>는 <code>dynamicParams</code>의 기본값을 <code>true</code>에서 <code>false</code>로 변경한다. 
<code>generateStaticParams</code>에 의해 생성되지 않은 동적 매개변수를 가진 동적으로 렌더링되는 페이지로 다시 돌아가려면 <code>dynamicParams = true</code>를 수동으로 설정해야 한다.</li>
</ul>
</li>
<li><p><strong><code>force-static</code></strong>
<code>cookies()</code>, <code>headers()</code>, <code>useSearchParams()</code>를 비워진 값으로 반환하여 레이아웃이나 페이지의 정적 렌더링과 정적 데이터 가져오기를 강제한다.</p>
</li>
</ul>
<br/>

<h3 id="dynamicparams"><code>dynamicParams</code></h3>
<p><code>generateStaticParams</code>로 생성되지 않은 동적 세그먼트가 방문될 때 어떤 동작이 수행되는지를 제어한다.</p>
<pre><code class="language-jsx">// layout.tsx / page.tsx

export const dynamicParams = true // true | false,</code></pre>
<ul>
<li><p><strong><code>true (기본값)</code></strong>
generateStaticParams`에 포함되지 않은 동적 세그먼트는 필요할 때 생성된다.</p>
</li>
<li><p><strong><code>false</code></strong>
<code>generateStaticParams</code>에 포함되지 않은 동적 세그먼트는 404를 반환한다.</p>
</li>
<li><p>이 옵션은 페이지 디렉토리의 <code>getStaticPaths</code>의 <code>fallback: true | false | blocking</code> 옵션을 대체한다.</p>
</li>
<li><p><code>dynamicParams = true</code>인 경우 세그먼트는 <strong>스트리밍 서버 렌더링</strong>을 사용한다.</p>
</li>
<li><p><code>dynamic = &#39;error&#39;</code>와 <code>dynamic = &#39;force-static&#39;</code>을 사용하면 <code>dynamicParams</code>의 기본값이 <code>false</code>로 변경된다.</p>
</li>
</ul>
<h3 id="revalidate"><code>revalidate</code></h3>
<p>레이아웃이나 페이지의 기본 재유효화 시간을 설정한다. 이 옵션은 개별 <code>fetch</code> 요청에서 설정한 <code>revalidate</code> 값에 대체되지 않는다.</p>
<pre><code class="language-jsx">// layout.tsx / page.tsx / route.ts

export const revalidate = false
// false | &#39;force-cache&#39; | 0 | number</code></pre>
<ul>
<li><p><strong><code>false (기본값)</code></strong>
<code>&#39;force-cache&#39;</code>로 캐시 옵션을 설정하거나 동적 함수가 사용되기 전에 발견된 모든 fetch 요청을 캐시하는 기본 heuristic. 사실상 <code>revalidate: Infinity</code>와 동일하며, 리소스를 무기한으로 캐시해야 함을 의미한다. 개별 <code>fetch</code> 요청에서 <code>cache: &#39;no-store&#39;</code> 또는 <code>revalidate: 0</code>을 사용하여 캐시를 피하고 라우트를 동적으로 렌더링하는 것도 가능하다. 또는 <code>revalidate</code>을 라우트의 기본값보다 작은 양의 양수로 설정하여 라우트의 재유효화 빈도를 증가시킬 수 있다.</p>
</li>
<li><p><strong><code>0</code></strong>
동적 함수나 동적 데이터 가져오기가 발견되지 않아도 레이아웃이나 페이지가 항상 동적으로 렌더링되도록 보장한다. 이 옵션은 캐시 옵션을 설정하지 않은 <code>fetch</code> 요청의 기본값을 <code>&#39;no-store&#39;</code>로 변경하지만, <code>&#39;force-cache&#39;</code>로 옵트인하거나 양수 <code>revalidate</code>를 사용하는 <code>fetch</code> 요청은 변경하지 않는다.</p>
</li>
<li><p><strong><code>number (초 단위)</code></strong>
레이아웃이나 페이지의 기본 재유효화 빈도를 <code>n</code>초로 설정한다.</p>
</li>
</ul>
<br/>

<p><strong>revalidate 빈도</strong></p>
<ul>
<li>단일 라우트의 각 레이아웃과 페이지에서 가장 낮은 <code>revalidate</code> 값이 전체 라우트의 재유효화 빈도를 결정한다. 이를 통해 하위 페이지도 상위 레이아웃과 동일한 빈도로 재유효화된다.</li>
<li>개별 <code>fetch</code> 요청은 라우트의 기본 <code>revalidate</code>보다 낮은 <code>revalidate</code>를 설정하여 전체 라우트의 재유효화 빈도를 증가시킬 수 있다. 이를 통해 특정 기준에 따라 특정 라우트에 대해 동적으로 더 빈번한 재유효화를 선택적으로 사용할 수 있다.</li>
</ul>
<br/>

<h3 id="runtime"><code>runtime</code></h3>
<pre><code class="language-jsx">// layout.tsx / page.tsx / route.ts

export const runtime = &#39;nodejs&#39;
// &#39;edge&#39; | &#39;nodejs&#39;</code></pre>
<br/>

<h3 id="preferredregion"><code>preferredRegion</code></h3>
<pre><code class="language-jsx">// layout.tsx / page.tsx / route.ts

export const preferredRegion = &#39;auto&#39;
// &#39;auto&#39; | &#39;global&#39; | &#39;home&#39; | [&#39;iad1&#39;, &#39;sfo1&#39;]</code></pre>
<p><code>preferredRegion</code> 및 지원되는 지역에 대한 지원은 배포 플랫폼에 따라 다르다.</p>
<p><code>preferredRegion</code>이 지정되지 않은 경우, 가장 가까운 상위 레이아웃의 옵션을 상속한다.
루트 레이아웃은 모든 지역을 기본값으로 갖는다.</p>
<br/>

<h3 id="generatestaticparams"><code>generateStaticParams</code></h3>
<p><code>generateStaticParams</code> 함수는 <strong>동적 라우트 세그먼트</strong>와 함께 사용하여, 요청 시간이 아닌 빌드 시간에 정적으로 생성될 경로 세그먼트 매개변수 목록을 정의하는 데 사용될 수 있다.</p>
<hr>
<h1 id="metadata-files-api-reference">Metadata Files API Reference</h1>
<p>파일 기반의 메타데이터는 라우트 세그먼트에 특수한 메타데이터 파일을 추가하여 정의할 수 있다.</p>
<p>각 파일 규칙은 정적 파일(예: <code>opengraph-image.jpg</code>) 또는 코드를 사용하여 파일을 생성하는 동적 변형(예: <code>opengraph-image.js</code>)을 사용하여 정의할 수 있다.</p>
<p>파일이 정의되면 Next.js는 자동으로 파일을 제공하며 (생산 환경에서는 캐싱을 위해 해시가 포함됨) 관련된 헤드 요소를 올바른 메타데이터로 업데이트한다. 이는 자산의 URL, 파일 유형 및 이미지 크기와 같은 정보를 포함한다.</p>
<p><a href="https://nextjs.org/docs/app/api-reference/file-conventions/metadata">File Conventions: Metadata Files</a></p>
<hr>
<p>[출처]
<a href="https://nextjs.org/docs/app/api-reference/file-conventions">https://nextjs.org/docs/app/api-reference/file-conventions</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ Components ] <Script/>]]></title>
            <link>https://velog.io/@chacha_fe/Components-Script</link>
            <guid>https://velog.io/@chacha_fe/Components-Script</guid>
            <pubDate>Thu, 15 Jun 2023 12:45:41 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-jsx">// app/dashboard/page.tsx

import Script from &#39;next/script&#39;

export default function Dashboard() {
  return (
    &lt;&gt;
      &lt;Script src=&quot;https://example.com/script.js&quot; /&gt;
    &lt;/&gt;
  )
}</code></pre>
<br/>

<hr>
<h2 id="props">Props</h2>
<p>아래는 <code>&lt;Script&gt;</code>에서 사용 가능한 요소들이다.</p>
<table>
<thead>
<tr>
<th>Prop</th>
<th>Example</th>
<th>Type</th>
<th>Required</th>
</tr>
</thead>
<tbody><tr>
<td><a href="https://nextjs.org/docs/app/api-reference/components/script#src">https://nextjs.org/docs/app/api-reference/components/script#src</a></td>
<td>src=&quot;<a href="http://example.com/script&quot;">http://example.com/script&quot;</a></td>
<td>String</td>
<td>Required unless inline script is used</td>
</tr>
<tr>
<td><a href="https://nextjs.org/docs/app/api-reference/components/script#strategy">https://nextjs.org/docs/app/api-reference/components/script#strategy</a></td>
<td>strategy=&quot;lazyOnload&quot;</td>
<td>String</td>
<td>-</td>
</tr>
<tr>
<td><a href="https://nextjs.org/docs/app/api-reference/components/script#onload">https://nextjs.org/docs/app/api-reference/components/script#onload</a></td>
<td>onLoad={onLoadFunc}</td>
<td>Function</td>
<td>-</td>
</tr>
<tr>
<td><a href="https://nextjs.org/docs/app/api-reference/components/script#onready">https://nextjs.org/docs/app/api-reference/components/script#onready</a></td>
<td>onReady={onReadyFunc}</td>
<td>Function</td>
<td>-</td>
</tr>
<tr>
<td><a href="https://nextjs.org/docs/app/api-reference/components/script#onerror">https://nextjs.org/docs/app/api-reference/components/script#onerror</a></td>
<td>onError={onErrorFunc}</td>
<td>Function</td>
<td>-</td>
</tr>
</tbody></table>
<br/>

<hr>
<h2 id="필수적인-props">필수적인 Props</h2>
<p><code>&lt;Script /&gt;</code> 컴포넌트는 다음과 같은 속성이 필요하다.</p>
<h3 id="src">src</h3>
<p>외부 스크립트의 URL을 지정하는 경로 문자열이다. 이는 절대 외부 URL이나 내부 경로일 수 있다. <code>src</code> 속성은 인라인 스크립트를 사용하지 않는 한 필수다.</p>
<br/>

<hr>
<h2 id="선택적인-props">선택적인 Props</h2>
<p>아래는 <code>&lt;Script /&gt;</code> 의 선택적인 요소들이다.</p>
<h3 id="strategy">strategy</h3>
<p>스크립트의 로딩 전략이다. 사용할 수 있는 네 가지 다른 전략이 있다.</p>
<ul>
<li><code>beforeInteractive</code> : Next.js 코드 및 페이지 하이드레이션 이전에 로드된다.</li>
<li><code>afterInteractive</code> : (default) 일부 하이드레이션 이후에 로드된다.</li>
<li><code>lazyOnload</code> : 브라우저가 비활성 상태일 때 로드된다.</li>
<li><code>worker</code> : (실험적인) 웹 워커에서 로드된다.</li>
</ul>
<br/>

<h3 id="beforinteractive"><code>beforInteractive</code></h3>
<p><code>beforeInteractive</code> 전략으로 로드되는 스크립트는 초기에 서버로부터 HTML에 삽입되며, Next.js 모듈 이전에 다운로드되고 페이지 하이드레이션 이전에 순서대로 실행된다.</p>
<p>이 전략으로 표시된 스크립트는 처음부터 사전로드되고 가져와진다. 그러나 실행은 페이지 하이드레이션을 차단하지 않는다.</p>
<p><code>beforeInteractive</code> 스크립트는 루트 레이아웃(<code>app/layout.tsx</code>) 내에 배치되어야 하며, 전체 사이트에서 필요한 스크립트를 로드하는 데 사용된다 (즉, 응용 프로그램의 어떤 페이지가 서버 측에서 로드되었을 때 스크립트가 로드된다).</p>
<p>이 전략은 페이지의 어떤 부분이 상호 작용 가능해지기 전에 가져와야 하는 중요한 스크립트에만 사용해야 한다.</p>
<p><code>beforeInteractive</code>로 설정된 스크립트는 컴포넌트 내에서 어디에 배치되었는지와 상관없이 항상 HTML 문서의 헤드(<code>head</code>)에 삽입된다.</p>
<pre><code class="language-jsx">// app/layout.tsx

import Script from &#39;next/script&#39;

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    &lt;html lang=&quot;en&quot;&gt;
      &lt;body&gt;{children}&lt;/body&gt;
      &lt;Script
        src=&quot;https://example.com/script.js&quot;
        strategy=&quot;beforeInteractive&quot;
      /&gt;
    &lt;/html&gt;
  )
}</code></pre>
<br/>

<p><code>beforeInteractive</code>를 사용하여 가능한 빨리 로드되어야 하는 몇 가지 스크립트의 예시는 다음과 같다.</p>
<ul>
<li>봇 감지기 (Bot detectors)</li>
<li>쿠키 동의 관리자 (Cookie consent managers)</li>
</ul>
<br/>

<h3 id="afterinteractive"><code>afterInteractive</code></h3>
<p><code>afterInteractive</code> 전략을 사용하는 스크립트는 HTML에 클라이언트 측에서 삽입되며, 페이지의 일부 (또는 전체) 하이드레이션 이후에 로드된다. 이것은 Script 컴포넌트의 기본 전략이며, 가능한 빨리 로드되어야 하지만 first-party Next.js 코드 이전에는 로드되지 않아야 하는 스크립트에 사용해야 한다.</p>
<p>afterInteractive 스크립트는 어떤 페이지나 레이아웃에도 배치할 수 있으며, 해당 페이지 (또는 페이지 그룹)가 브라우저에서 열릴 때만 로드되고 실행된다.</p>
<pre><code class="language-jsx">// app/page.js

import Script from &#39;next/script&#39;

export default function Page() {
  return (
    &lt;&gt;
      &lt;Script src=&quot;https://example.com/script.js&quot; strategy=&quot;afterInteractive&quot; /&gt;
    &lt;/&gt;
  )
}</code></pre>
<br/>

<p><code>afterInteractive</code>에 적합한 스크립트의 몇 가지 예시는 다음과 같다.</p>
<ul>
<li>태그 관리자 (Tag managers)</li>
<li>분석 도구 (Analytics)</li>
</ul>
<br/>

<h3 id="lazyonload"><code>lazyOnload</code></h3>
<p><code>lazyOnload</code> 전략을 사용하는 스크립트는 브라우저가 비활성 상태일 때 HTML에 클라이언트 측에서 삽입되며, 페이지의 모든 리소스가 가져온 후에 로드된다. 이 전략은 조금 뒤에 로드되어도 되는 백그라운드 또는 우선순위가 낮은 스크립트에 사용해야 한다.</p>
<p><code>lazyOnload</code> 스크립트는 어떤 페이지나 레이아웃에도 배치할 수 있으며, 해당 페이지 (또는 페이지 그룹)가 브라우저에서 열릴 때만 로드되고 실행된다.</p>
<pre><code class="language-jsx">import Script from &#39;next/script&#39;

export default function Page() {
  return (
    &lt;&gt;
      &lt;Script src=&quot;https://example.com/script.js&quot; strategy=&quot;lazyOnload&quot; /&gt;
    &lt;/&gt;
  )
}</code></pre>
<br/>


<p>즉시 로드할 필요가 없고 <code>lazyOnload</code>로 가져올 수 있는 스크립트의 예시로는 다음이 있습니다:</p>
<ul>
<li>채팅 지원 플러그인 (Chat support plugins)</li>
<li>소셜 미디어 위젯 (Social media widgets)</li>
</ul>
<br/>

<h3 id="worker"><code>worker</code></h3>
<p>worker 전략은 아직 안정화되지 않았으며, <code>app</code> 디렉토리에서는 아직 작동하지 않는다.</p>
<p><code>worker</code> 전략을 사용하는 스크립트는 주 스레드에서 벗어나 웹 워커로 전달되어 주 스레드를 해제하고 중요한 first-party 리소스만 처리되도록 한다. 이 전략은 모든 스크립트에 사용할 수 있지만, 모든 타사 스크립트를 지원하는 것이 보장되지 않는 고급 사용 사례다.</p>
<p>전략으로 worker를 사용하려면 <code>next.config.js</code>에서 <code>nextScriptWorkers</code> 플래그를 활성화해야 한다.</p>
<pre><code class="language-jsx">// next.config.js

module.exports = {
  experimental: {
    nextScriptWorkers: true,
  },
}</code></pre>
<br/>

<p>현재 <code>worker</code> 스크립트는 <code>pages/</code> 디렉토리에서만 사용할 수 있다.</p>
<pre><code class="language-jsx">// pages/home.tsx

import Script from &#39;next/script&#39;

export default function Home() {
  return (
    &lt;&gt;
      &lt;Script src=&quot;https://example.com/script.js&quot; strategy=&quot;worker&quot; /&gt;
    &lt;/&gt;
  )
}</code></pre>
<br/>

<h3 id="onload"><code>onLoad</code></h3>
<p>onLoad는 아직 서버 컴포넌트에서 작동하지 않으며 <code>클라이언트 컴포넌트</code>에서만 사용할 수 있다. 또한, <code>onLoad</code>는 <code>beforeInteractive</code>와 함께 사용할 수 없다. 대신 <code>onReady</code>를 사용하는 것을 고려하라.</p>
<p>일부 타사 스크립트는 스크립트가 로드된 후에 콘텐츠를 인스턴스화하거나 함수를 호출하기 위해 사용자가 JavaScript 코드를 한 번 실행해야 하는 경우가 있다. 로딩 전략으로 <code>afterInteractive</code> 또는 <code>lazyOnload</code>를 사용하여 스크립트를 로드하는 경우, <code>onLoad</code> 속성을 사용하여 로드된 후에 코드를 실행할 수 있다.</p>
<br/>

<p>다음은 <code>lodash</code> 라이브러리가 로드된 후에만 lodash 메서드를 실행하는 예시다.</p>
<pre><code class="language-jsx">// app/page.tsx

&#39;use client&#39;

import { useRef } from &#39;react&#39;
import Script from &#39;next/script&#39;

export default function Page() {
  const mapRef = useRef()

  return (
    &lt;&gt;
      &lt;div ref={mapRef}&gt;&lt;/div&gt;
      &lt;Script
        id=&quot;google-maps&quot;
        src=&quot;https://maps.googleapis.com/maps/api/js&quot;
        onReady={() =&gt; {
          new google.maps.Map(mapRef.current, {
            center: { lat: -34.397, lng: 150.644 },
            zoom: 8,
          })
        }}
      /&gt;
    &lt;/&gt;
  )
}</code></pre>
<br/>

<h3 id="onready"><code>onReady</code></h3>
<p>onReady는 아직 Server Components에서 작동하지 않으며, <code>Client Components</code>에서만 사용할 수 있다.</p>
<p>일부 타사 스크립트는 스크립트가 로드되고 컴포넌트가 재마운트될 때마다 (예: 라우트 이동 후) 사용자가 JavaScript 코드를 실행해야 하는 경우가 있다. <code>onReady</code> 속성을 사용하여 스크립트가 처음 로드될 때와 이후의 모든 컴포넌트 재마운트 후에 코드를 실행할 수 있다.</p>
<br/>

<p>다음은 컴포넌트가 재마운트될 때마다 <code>Google Maps JS embed</code>를 다시 인스턴스화하는 예시다.</p>
<pre><code class="language-jsx">// app/page.tsx

&#39;use client&#39;

import { useRef } from &#39;react&#39;
import Script from &#39;next/script&#39;

export default function Page() {
  const mapRef = useRef()

  return (
    &lt;&gt;
      &lt;div ref={mapRef}&gt;&lt;/div&gt;
      &lt;Script
        id=&quot;google-maps&quot;
        src=&quot;https://maps.googleapis.com/maps/api/js&quot;
        onReady={() =&gt; {
          new google.maps.Map(mapRef.current, {
            center: { lat: -34.397, lng: 150.644 },
            zoom: 8,
          })
        }}
      /&gt;
    &lt;/&gt;
  )
}</code></pre>
<br/>

<h3 id="onerror"><code>onError</code></h3>
<p>onError는 아직 Server Components에서 작동하지 않으며, <code>Client Components</code>에서만 사용할 수 있다. <code>onError</code>는 <code>beforeInteractive</code> 로딩 전략과 함께 사용할 수 없다.</p>
<p>때로는 스크립트 로드에 실패했을 때 이를 감지하는 것이 유용할 수 있다. <code>onError</code> 속성을 사용하여 이러한 오류를 처리할 수 있다.</p>
<pre><code class="language-jsx">// app/page.tsx

&#39;use client&#39;

import Script from &#39;next/script&#39;

export default function Page() {
  return (
    &lt;&gt;
      &lt;Script
        src=&quot;https://example.com/script.js&quot;
        onError={(e: Error) =&gt; {
          console.error(&#39;Script failed to load&#39;, e)
        }}
      /&gt;
    &lt;/&gt;
  )
}</code></pre>
<br/>

<hr>
<p>[출처]
<a href="https://nextjs.org/docs/app/api-reference/components/script">https://nextjs.org/docs/app/api-reference/components/script</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ Compontents ] <Link/>]]></title>
            <link>https://velog.io/@chacha_fe/Compontents-Link</link>
            <guid>https://velog.io/@chacha_fe/Compontents-Link</guid>
            <pubDate>Thu, 15 Jun 2023 12:42:26 GMT</pubDate>
            <description><![CDATA[<p><strong><code>&lt;Link&gt;</code></strong>는 React 컴포넌트로, HTML <strong><code>&lt;a&gt;</code></strong> 요소를 확장하여 prefetching(사전로드) 및 클라이언트 측에서 라우트 간 탐색을 제공한다. 이는 Next.js에서 라우트 간 탐색을 위한 주요 방법이다.</p>
<p>Next.js 애플리케이션에서 <strong><code>&lt;Link&gt;</code></strong>를 사용하면 클라이언트 측 렌더링 및 탐색을 자동으로 처리한다. 즉, 사용자가 <strong><code>&lt;Link&gt;</code></strong> 컴포넌트를 클릭하면 브라우저 전체 새로고침 없이 페이지 전환이 발생한다. 이로 인해 더 빠르고 원활한 사용자 경험이 가능해진다.</p>
<br/>

<p>다음은 Next.js에서 <strong><code>&lt;Link&gt;</code></strong> 컴포넌트를 사용하는 예시다.</p>
<pre><code class="language-jsx">import Link from &#39;next/link&#39;

export default function Page() {
  return &lt;Link href=&quot;/dashboard&quot;&gt;Dashboard&lt;/Link&gt;
}</code></pre>
<br/>

<hr>
<h2 id="props">Props</h2>
<p><code>&lt;Link/&gt;</code>의 구성 요소는 아래와 같다.</p>
<table>
<thead>
<tr>
<th>Prop</th>
<th>Example</th>
<th>Type</th>
<th>Required</th>
</tr>
</thead>
<tbody><tr>
<td><a href="https://nextjs.org/docs/app/api-reference/components/link#href-required">https://nextjs.org/docs/app/api-reference/components/link#href-required</a></td>
<td>href=&quot;/dashboard&quot;</td>
<td>String or Object</td>
<td>Yes</td>
</tr>
<tr>
<td><a href="https://nextjs.org/docs/app/api-reference/components/link#replace">https://nextjs.org/docs/app/api-reference/components/link#replace</a></td>
<td>replace={false}</td>
<td>Boolean</td>
<td>-</td>
</tr>
<tr>
<td><a href="https://nextjs.org/docs/app/api-reference/components/link#prefetch">https://nextjs.org/docs/app/api-reference/components/link#prefetch</a></td>
<td>prefetch={false}</td>
<td>Boolean</td>
<td>-</td>
</tr>
</tbody></table>
<br/>

<p><code>&lt;Link&gt;</code>에는 className이나 <code>target=&quot;_blank&quot;</code>와 같은 <code>&lt;a&gt;</code> 태그 속성을 props로 추가할 수 있으며, 이러한 속성은 내부의 <code>&lt;a&gt;</code> 요소로 전달된다.</p>
<h3 id="href"><code>href</code></h3>
<p>이동할 경로 또는 URL이며 필수적인 요소다.</p>
<pre><code class="language-jsx">&lt;Link href=&quot;/dashboard&quot;&gt;Dashboard&lt;/Link&gt;</code></pre>
<br/>

<p><code>href</code> 는 object를 허용한다. </p>
<pre><code class="language-jsx">// Navigate to /about?name=test
&lt;Link
  href={{
    pathname: &#39;/about&#39;,
    query: { name: &#39;test&#39; },
  }}
&gt;
  About
&lt;/Link&gt;</code></pre>
<br/>

<h3 id="replace"><code>replace</code></h3>
<p>기본값은 <code>false</code>다. <code>true</code>로 설정할 경우, <code>next/link</code>는 브라우저의 기록 스택에 새 URL을 추가하는 대신 현재의 기록 상태를 대체한다.</p>
<pre><code class="language-jsx">import Link from &#39;next/link&#39;

export default function Page() {
  return (
    &lt;Link href=&quot;/dashboard&quot; replace&gt;
      Dashboard
    &lt;/Link&gt;
  )
}</code></pre>
<br/>

<h3 id="prefetch"><code>prefetch</code></h3>
<p>기본값은 <code>true</code>다. <code>true</code>로 설정할 경우, <code>next/link</code>는 백그라운드에서 페이지(<code>prefetch={false}</code>)를 사전로드한다. 이는 클라이언트 측 탐색의 성능을 향상시키는 데 유용하다. 뷰포트에 있는 <code>&lt;Link /&gt;</code>(초기에나 스크롤을 통해)는 사전로드된다.</p>
<p><code>prefetch={false}</code>를 전달하여 사전로드 기능을 비활성화할 수 있다. 사전로드 기능은 프로덕션 환경에서만 활성화된다.</p>
<br/>

<hr>
<h2 id="예시">예시</h2>
<h3 id="동적-라우트-연결">동적 라우트 연결</h3>
<p>동적 라우트의 경우, 링크의 경로를 생성하기 위해 <code>템플릿 리터럴</code>을 사용하는 것이 편리할 수 있다.</p>
<br/>
예를 들어, 동적 라우트 `app/blog/[slug]/page.js`에 대한 링크 목록을 생성할 수 있다.

<pre><code class="language-jsx">// app/blog/page.js

import Link from &#39;next/link&#39;

function Page({ posts }) {
  return (
    &lt;ul&gt;
      {posts.map((post) =&gt; (
        &lt;li key={post.id}&gt;
          &lt;Link href={`/blog/${post.slug}`}&gt;{post.title}&lt;/Link&gt;
        &lt;/li&gt;
      ))}
    &lt;/ul&gt;
  )
}</code></pre>
<br/>

<h3 id="middleware">Middleware</h3>
<p>인증 또는 사용자를 다른 페이지로 리다이렉션하는 등 다른 목적을 위해 미들웨어를 사용하는 것이 일반적이다. 미들웨어를 와 함께 <code>&lt;Link/&gt;</code> 컴포넌트로 제대로 된 사전로드와 리다이렉트를 하기 위해서는 Next.js에게 표시할 URL과 사전로드할 URL을 모두 알려주어야 한다. 이렇게 하면 미들웨어에 필요한 올바른 경로를 사전로드하기 위해 불필요한 fetch를 방지할 수 있다.</p>
<p>예를 들어, 인증 및 방문자 뷰를 가진 <code>/dashboard</code> 경로를 제공하려는 경우, 다음과 같이 미들웨어에 다음과 유사한 내용을 추가하여 사용자를 올바른 페이지로 리디렉션할 수 있다.</p>
<pre><code class="language-jsx">// middleware.js

export function middleware(req) {
  const nextUrl = req.nextUrl
  if (nextUrl.pathname === &#39;/dashboard&#39;) {
    if (req.cookies.authToken) {
      return NextResponse.rewrite(new URL(&#39;/auth/dashboard&#39;, req.url))
    } else {
      return NextResponse.rewrite(new URL(&#39;/public/dashboard&#39;, req.url))
    }
  }
}</code></pre>
<br/>

<p>이 경우, <Link /> 컴포넌트에서 다음 코드를 사용하면 된다.</p>
<pre><code class="language-jsx">import Link from &#39;next/link&#39;
import useIsAuthed from &#39;./hooks/useIsAuthed&#39;

export default function Page() {
  const isAuthed = useIsAuthed()
  const path = isAuthed ? &#39;/auth/dashboard&#39; : &#39;/dashboard&#39;
  return (
    &lt;Link as=&quot;/dashboard&quot; href={path}&gt;
      Dashboard
    &lt;/Link&gt;
  )
}</code></pre>
<br/>

<hr>
<p>[출처]
<a href="https://nextjs.org/docs/app/api-reference/components">https://nextjs.org/docs/app/api-reference/components</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[함수와 일급 객체]]></title>
            <link>https://velog.io/@chacha_fe/%ED%95%A8%EC%88%98%EC%99%80-%EC%9D%BC%EA%B8%89-%EA%B0%9D%EC%B2%B4</link>
            <guid>https://velog.io/@chacha_fe/%ED%95%A8%EC%88%98%EC%99%80-%EC%9D%BC%EA%B8%89-%EA%B0%9D%EC%B2%B4</guid>
            <pubDate>Tue, 30 May 2023 14:40:24 GMT</pubDate>
            <description><![CDATA[<h2 id="일급-객체">일급 객체</h2>
<p>다음과 같은 조건을 만족하는 객체를 <code>일급 객체</code>라 한다.</p>
<ol>
<li><p>무명의 리터럴로 생성할 수 있다. 즉, 런타임에 생성이 가능하다.</p>
</li>
<li><p>변수나 자료구조(객체, 배열)에 저장할 수 있다.</p>
</li>
<li><p>함수의 매개변수에 전달할 수 있다.</p>
</li>
<li><p>함수의 반환값으로 사용할 수 있다.</p>
</li>
</ol>
<br/>

<p>자바스크립트의 함수는 일급 객체다.</p>
<pre><code class="language-tsx">// 1. 함수는 무명의 리터럴로 생성할 수 있다.
// 2. 함수는 변수에 저장할 수 있다.
// 런타임에 함수 리터럴이 평가되어 함수 객체가 생성되고 변수에 할당된다.
const increase = function(num){
    return ++num;
}

const perdicates = { increase };

// 3. 함수의 매개변수에 전달할 수 있다.
// 4. 함수의 반환값으로 사용할 수 있다.
function makeCounter(predicate){
    let num = 0;

    return function () {
        num = predicate(num);
        return num;
    };
}

const increaser = makeCounter(predicates.increase);
console.log(increaser()); // 1
console.log(increase()); // 2</code></pre>
<br/>

<p>함수가 일급 객체라는 것은 함수를 객체와 동일하게 사용할 수 있다는 의미다. <strong>객체는 값이므로 함수는 값과 동일하게 취급할 수 있다.</strong></p>
<p>일급 객체로서 함수가 가지는 가장 큰 특징은 <strong>일반 객체와 같이 함수의 매개변수에 전달할 수 있으며, 함수의 반환값으로 사용할 수도 있다는 것</strong>이다.</p>
<br/>

<hr>
<h2 id="함수-객체의-프로퍼티">함수 객체의 프로퍼티</h2>
<p>함수 객체는 <code>arguments</code>, <code>caller</code>, <code>length</code>, <code>name</code>, <code>prototype</code>이라는 일반 객체에 없는 고유의 프로퍼티를 가진다.</p>
<br/>

<h3 id="1-arguments-프로퍼티">1. arguments 프로퍼티</h3>
<p>함수 객체의 <code>arguments</code> 프로퍼티 값은 <code>arguments</code> 객체다. <code>arguments</code> 객체는 함수 호출시 전달된 인수들의 정보를 담고 있는 순회 가능한 <strong>유사 배열 객체</strong>이며, <strong>함수 내부에서 지역 변수처럼 사용</strong>된다.</p>
<pre><code class="language-tsx">function multiply(x,y){
    console.log(arguments);
    return x * y;
}

multiply(1,2);</code></pre>
<p><img src="https://velog.velcdn.com/images/chacha_fe/post/0ed412e3-4c02-443a-98f1-c7ac2449f2f3/image.png" alt=""></p>
<ul>
<li><p><code>arguments</code>의 프로퍼티 키는 인수의 순서, 프로퍼티 값은 인수의 값을 나타낸다.</p>
</li>
<li><p><code>arguments</code>의 <code>callee</code> 프로퍼티는 호출한 함수를 가리킨다.</p>
</li>
<li><p><code>arguments</code>의 <code>length</code> 프로퍼티는 인수의 개수를 나타낸다.</p>
</li>
</ul>
<br/>

<p>매개변수의 개수보다 인수를 더 많이 전달한 경우 초과된 인수는 무시된다. 그렇다고 초과된 인수가 그냥 버려지는 것은 아니다. 모든 인수는 암묵적으로 <code>arguments</code> 객체의 프로퍼티로 보관된다.</p>
<br/>

<p><code>arguments</code> 객체는 매개변수 개수를 확정할 수 없는 <strong>가변 인자 함수</strong>를 구현할 때 유용하다.</p>
<p><code>arguments</code> 객체는 유사 배열 객체다. 유사 배열 객체란 <code>length</code> 프로퍼티를 가진 객체로 for 문으로 순회할 수 있는 객체를 말한다.</p>
<pre><code class="language-tsx">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>
<br/>

<h3 id="2-caller-프로퍼티">2. caller 프로퍼티</h3>
<p><code>caller</code> 프로퍼티는 ECMAScript 사양에 포함되지 않은 비표준 프로퍼티다.</p>
<p>함수 객체의 <code>caller</code> 프로퍼티는 <strong>함수 자신을 호출한 함수를 가리킨다.</strong></p>
<pre><code class="language-tsx">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>
<br/>

<h3 id="3-length-프로퍼티">3. length 프로퍼티</h3>
<p>함수 객체의 <code>length</code> 프로퍼티는 <strong>함수를 정의할 때 선언한 매개변수의 개수를 가리킨다.</strong></p>
<pre><code class="language-tsx">function foo(){}
console.log(foo.length); // 0

function bar(a,b,c){}
console.log(bar.length); // 3</code></pre>
<br/>

<p><code>arguments</code> 객체의 <code>length</code> 프로펕티는 <strong>인자의 개수</strong>를 가리킨다. 하지만 함수 객체의 <code>length</code> 프로퍼티는 <strong>매개변수의 개수</strong>를 가리킨다.</p>
<br/>

<h3 id="4-name-프로퍼티">4. name 프로퍼티</h3>
<p>함수 객체의 <code>name</code> 프로퍼티는 함수 이름을 나타낸다. <code>name</code> 프로퍼티는 ES6 이전까지는 비표준이었다가 ES6에서 정식 표준이 되었다.</p>
<p>ES5와 ES6에서 동작의 차이가 있다.</p>
<pre><code class="language-tsx">// 기명 함수 표현식
var namedFunc = function foo(){};
console.log(namedFunc.name); // foo

// 익명 함수 표현식
var anonymousFunc = function (){};
// ES5 : name 프로퍼티는 빈 문자열을 값으로 갖는다.
// ES6 : name 프로퍼티는 함수 객체를 가리키는 변수 이름을 값으로 갖는다.
console.log(anonymousFunc.name); // anonymousFunc</code></pre>
<br/>

<h3 id="5-proto-접근자-프로퍼티">5. <strong>proto</strong> 접근자 프로퍼티</h3>
<p>모든 객체는 <code>[[Prototype]]</code> 이라는 내부 슬롯을 갖는다. <code>[[Prototype]]</code>  내부 슬롯은 객체지향 프로그래밍의 상속을 구현하는 프로토타입 객체를 가리킨다.</p>
<p><code>__proto__</code> 프로퍼티는 <code>[[Prototype]]</code>  내부 슬롯이 가리키는 프로토타입 객체에 간접적으로 접근하기 위해 사용하는 접근자 프로퍼티다. </p>
<pre><code class="language-tsx">const obj = { a: 1 };

// 객체 리터럴 방식으로 생성한 객체의 프로토타입 객체는 Object.prototypedlek.
console.log(obj.__proto__ === Object.prototype); // true

// 객체 리터럴 방식으로 생성한 객체는 프로토타입 객체인 Object.prototype의 프로퍼티를 상속받는다.
// hasOwnProperty 메서드는 Object.prototype의 메서드다.
console.log(obj.hasOwnProperty(&#39;a&#39;)); // true
console.log(obj.hasOwnProperty(&#39;__proto__&#39;)); // false</code></pre>
<br/>

<p><strong>💡 <code>hasOwnProperty</code> 메소드</strong></p>
<p><code>hasOwnProperty</code> 메서드는 이름에서 알 수 있듯이 인수로 전달받은 프로퍼티 키가 객체 고유의 프로퍼티 키인 경우에만 <code>true</code>를 반환하고 상속받은 프로토타입의 프로퍼티 키인 경우 <code>false</code>를 반환한다.</p>
<br/>

<h3 id="6-prototype-프로퍼티">6. prototype 프로퍼티</h3>
<p><code>prototype</code> 프로퍼티는 생성자 함수로 호출할 수 있는 함수 객체, 즉 <code>constructor</code>만이 소유하는 프로퍼티다.</p>
<p>일반 객체와 생성자 함수로 호출할 수 없는 <code>non-constructor</code>에는 <code>prototype</code> 프로퍼티가 없다.</p>
<pre><code class="language-tsx">// 함수 객체는 prototype 프로퍼티를 소유한다.
(function () {}).hasOwnProperty(&#39;prototype&#39;); // true

// 일반 객체는 prototype 프로퍼티를 소유하지 않는다.
({}).hasOwnProperty(&#39;prototype&#39;); // false</code></pre>
<br/>

<p><code>prototype</code> 프로퍼티는 함수가 객체를 생성하는 생성자 함수로 호출될 때 생성자 함수가 생성할 인스턴스의 프로토타입 객체를 가리킨다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[생성자 함수에 의한 객체 생성]]></title>
            <link>https://velog.io/@chacha_fe/%EC%83%9D%EC%84%B1%EC%9E%90-%ED%95%A8%EC%88%98%EC%97%90-%EC%9D%98%ED%95%9C-%EA%B0%9D%EC%B2%B4-%EC%83%9D%EC%84%B1</link>
            <guid>https://velog.io/@chacha_fe/%EC%83%9D%EC%84%B1%EC%9E%90-%ED%95%A8%EC%88%98%EC%97%90-%EC%9D%98%ED%95%9C-%EA%B0%9D%EC%B2%B4-%EC%83%9D%EC%84%B1</guid>
            <pubDate>Tue, 30 May 2023 11:59:38 GMT</pubDate>
            <description><![CDATA[<p>객체 리터럴에 의한 객체 생성 방식은 가장 일반적이고 간단한 객체 생성 방식이다. 객체는 객체 리터럴 이외에도 다양한 방법으로 생성할 수 있다.</p>
<p>객체 리터럴을 사용하여 객체를 생성하는 방식과 생성자 함수를 사용하여 객체를 생성하는 방식과의 장단점을 알아보자.</p>
<br/>

<h1 id="object-생성자-함수">Object 생성자 함수</h1>
<p><code>new</code> 연산자와 함께 <code>Object</code> 생성자 함수를 호출하면 빈 객체를 생성하여 반환한다. 빈 객체를 생성한 이후 프로퍼티 또는 메서드를 추가하여 객체를 완성시킬 수 있다.</p>
<pre><code class="language-tsx">// 빈 객체의 생성
const person = new Object();

person.name = &#39;Han&#39;;

console.log(person); // { name: &quot;Han&quot; }</code></pre>
<p><strong>생성자 함수란</strong> <code>new</code> <strong>연산자와 함께 호출하여 객체(인스턴스)를 생성하는 함수를 말한다. 생성자 함수에 의해 생성된 객체를 인스턴스라 한다.</strong></p>
<p>자바스크립트는 이외에도 String, Number, Boolean, Function, Array, Date, RegExp, Promise 등의 빌트인 생성자 함수를 제공한다.</p>
<br/>

<hr>
<h1 id="생성자-함수">생성자 함수</h1>
<h2 id="1-객체-리터럴에-의한-객체-생성-방식의-문제점">1. 객체 리터럴에 의한 객체 생성 방식의 문제점</h2>
<p>객체 리터럴에 의한 객체 생성 방식은 직관적이고 간편하다. 하지만 <strong>객체 리터럴에 의한 객체 생성 방식은 단 하나의 객체만 생성한다.</strong> 따라서 동일한 프로퍼티를 갖는 객체를 여러 개 생성해야 하는 경우 매번 같은 프로퍼티를 기술해야 하기 때문에 비효율적이다.</p>
<pre><code class="language-tsx">const circle1 = {
    raduis: 5,
    getDiameter(){
        return 2 * this.radius    
    }
}

const circle2 = {
    raduis: 10,
    getDiameter(){
        return 2 * this.radius    
    }
}</code></pre>
<br/>

<h2 id="2-생성자-함수에-의한-객체-생성-방식의-장점">2. 생성자 함수에 의한 객체 생성 방식의 장점</h2>
<p>생성자 함수에 의한 객체 생성 방식은 마치 객체(인스턴스)를 생성하기 위한 템플릿(클래스)처럼 생성자 함수를 사용하여 프로퍼티 구조가 동일한 객체 여러 개를 간편하게 생성할 수 있다.</p>
<pre><code class="language-tsx">// 생성자 함수
function Circle(radius){
    // 생성자 함수 내부의 this는 생성자 함수가 생성할 인스턴스를 가리킨다.
    this.radius = radius;
    this.getDiameter = function () {
        return 2 * this.radius;
    }
}

// 인스턴스 생성
const circle1 = Circle(5);
const circle2 = Circle(10);</code></pre>
<br/>

<p>만약 <code>new</code> 연산자와 함께 생성자 함수를 호출하지 않으면 생성자 함수가 아닌 일반 함수로 동작한다.</p>
<pre><code class="language-tsx">const circle3 = Circle(15);

// 일반 함수로서 호출된 Circle은 반환문이 없으므로 암묵적으로 undefined를 반환한다.
console.log(circle3);

// 일반 함수로서 호출된 Circle 내의 this는 전역 객체를 가리킨다.
console.log(radius); // 15</code></pre>
<br/>

<h2 id="3-생성자-함수의-인스턴스-생성-과정">3. 생성자 함수의 인스턴스 생성 과정</h2>
<p>생성자 함수의 역할은 프로퍼티 구조가 동일한 인스턴스를 생성하기 위한 템플릿(클래스)으로서 동작하여 <strong>인스턴스를 생성하는 것</strong>과 생성된 <strong>인스턴스를 초기화(인스턴스 프로퍼티 추가 및 초기값 할당)하는 것</strong>이다.</p>
<p>생성자 함수가 인스턴스를 생성하는 것은 필수적이고, 생성된 인스턴스를 초기화하는 것은 옵션이다.</p>
<pre><code class="language-tsx">// 생성자 함수
function Circle(radius){
    // 인스턴스 초기화
    this.radius = radius;
    this.getDiameter = function () {
        return 2 * this.radius;
    }
}

// 인스턴스 생성
const circle1 = new Circle(5); </code></pre>
<br/>

<p>생성자 함수 내부 코드를 살펴보면 <code>this</code>에 프로퍼티를 추가하고 전달된 인수를 프로퍼티 초기 값으로 할당하여 인스턴스를 처기화 한다. 하지만 인스턴스를 생성하고 반환하는 코드는 보이지 않는다. (예 : return { … } )</p>
<br/>

<p>자바스크립트 엔진은 암묵적으로 인스턴스를 생성하고 인스턴스를 초기화한 후 암묵적으로 인스턴스를 반환한다.</p>
<br/>

<h3 id="1-인스턴스-생성과-this-바인딩">1) 인스턴스 생성과 this 바인딩</h3>
<p>암묵적으로 빈 객체(인스턴스)가 생성되고 <code>this</code>에 바인딩된다. 이 처리는 런타임 이전에 실행된다.</p>
<pre><code class="language-tsx">function Circle(radius){
    // 암묵적으로 인스턴스가 생성되고 this에 바인딩된다.
    console.log(this);

    this.radius = radius;
    this.getDiameter = function () {
        return 2 * this.radius;
    }
}</code></pre>
<br/>

<h3 id="2-인스턴스-초기화">2) 인스턴스 초기화</h3>
<p>생성자 함수에 기술되어 있는 코드가 실행되어 <code>this</code>에 바인딩 되어 있는 인스턴스를 초기화한다.</p>
<pre><code class="language-tsx">function Circle(radius){
    // 1. 암묵적으로 인스턴스가 생성되고 this에 바인딩된다.
    console.log(this);

    // 2. this에 바인딩되어 있는 인스턴스를 초기화한다.
    this.radius = radius;
    this.getDiameter = function () {
        return 2 * this.radius;
    }
}</code></pre>
<br/>

<h3 id="3-인스턴스-반환">3) 인스턴스 반환</h3>
<p>생성자 함수 내부의 모든 처리가 끝나면 완성된 인스턴스가 바인딩된 <code>this</code>를 암묵적으로 반환한다.</p>
<pre><code class="language-tsx">function Circle(radius){
    // 1. 암묵적으로 인스턴스가 생성되고 this에 바인딩된다.

    // 2. this에 바인딩되어 있는 인스턴스를 초기화한다.
    this.radius = radius;
    this.getDiameter = function () {
        return 2 * this.radius;
    }

    // 3. 완성된 인스턴스가 바인딩된 this를 암묵적으로 반환한다.
}</code></pre>
<br/>

<p>만약 <code>this</code>가 아닌 다른 객체를 명시적으로 반환하면 <code>this</code>가 반환되지 못하고 <code>return</code> 문에 명시한 객체가 반환된다.</p>
<pre><code class="language-tsx">function Circle(radius){
    this.radius = radius;
    this.getDiameter = function () {
        return 2 * this.radius;
    }

    // 명시적으로 객체를 반환하면 암묵적인 this 반환이 무시된다.
    return {};
}

const circle = new Circle(1);
console.log(circle); // {};</code></pre>
<br/>

<p>하지만 명시적으로 <code>원시 값</code>을 반환하면 원시 값 반환은 무시되고 암묵적으로 <code>this</code>가 반환된다.</p>
<pre><code class="language-tsx">function Circle(radius){
    this.radius = radius;
    this.getDiameter = function () {
        return 2 * this.radius;
    }

    // 명시적으로 원시 값 반환 시에 암묵적으로 this가 반환된다.
    return 100;
}

const circle = new Circle(1);
console.log(circle); // Circle {radius:1, getDiameter:f}</code></pre>
<br/>

<p>생성자 함수 내부에서 명시적으로 this가 아닌 다른 값을 반환하는 것은 생성자 함수의 기본 동작을 훼손한다. 따라서 <strong>생성자 함수 내부에서</strong> <code>return</code> <strong>문을 반드시 생략한다.</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로퍼티 어트리뷰트]]></title>
            <link>https://velog.io/@chacha_fe/%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0-%EC%96%B4%ED%8A%B8%EB%A6%AC%EB%B7%B0%ED%8A%B8</link>
            <guid>https://velog.io/@chacha_fe/%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0-%EC%96%B4%ED%8A%B8%EB%A6%AC%EB%B7%B0%ED%8A%B8</guid>
            <pubDate>Sun, 28 May 2023 14:40:12 GMT</pubDate>
            <description><![CDATA[<h2 id="내부-슬롯과-내부-메서드">내부 슬롯과 내부 메서드</h2>
<p><code>내부 슬롯</code>과 <code>내부 메서드</code>는 자바스크립트 엔진의 구현 알고리즘을 설명하기 위해 ECMAScript 사양에서 사용하는 <code>의사 프로퍼티</code>와 <code>의사 메서드</code>다. <code>([[ … ]])</code> 이중 대괄호로 감싼 이름들이 내부 슬롯과 내부 메서드다.</p>
<p>내부 슬롯과 내부 메서드는 자바스크립트 엔진의 내부 로직이므로 <strong>원칙적으로 자바스크립트는 내부 슬롯과 내부 메서드에 직접적으로 접근하거나 호출할 수 있는 방법을 제공하지 않는다.</strong> <strong>단, 일부 내부 슬롯과 내부 메서드에 한하여 직접적으로 접근할 수 있는 수단을 제공하기는 한다.</strong></p>
<pre><code class="language-tsx">const o = {};

o.[[Prototype]] // Uncaught SyntaxError

o.__proto__ // Object.prototype</code></pre>
<br/>

<hr>
<h2 id="프로퍼티-어트리뷰트와-프로퍼티-디스크립터-객체">프로퍼티 어트리뷰트와 프로퍼티 디스크립터 객체</h2>
<p>자바스크립트 엔진은 프로퍼티를 생성할 때 프로퍼티의 상태를 나타내는 프로퍼티 어트리뷰터를 기본값으로 자동 정의한다.</p>
<p>프로퍼티 어트리뷰트는 자바스크립트의 엔진이 관리하는 내부 상태 값인 아래의 내부 슬롯이다.</p>
<ul>
<li><p><code>[[Value]]</code></p>
</li>
<li><p><code>[[Writable]]</code></p>
</li>
<li><p><code>[[Enumerable]]</code></p>
</li>
<li><p><code>[[Configurable]]</code></p>
</li>
</ul>
<p>따라서 프로퍼티 어트리뷰트에 직접 접근할 수 없지만 <code>Object.getOwnPropertyDescriptor</code> 메서드를 사용하여 간접적으로 확인할 수는 있다.</p>
<br/>

<p><code>Object.getOwnPropertyDescriptor</code> 메서드를 호출할 때 첫 번째 매개변수에는 객체의 참조를 전달하고, 두 번째 매개변수에는 프로퍼티 키를 문자열로 전달한다.</p>
<p>이 때 <code>Object.getOwnPropertyDescriptor</code> 메서드는 프로퍼티 어트리뷰트 정보를 제공하는 <code>프로퍼티 디스크립터 객체</code>를 반환한다.</p>
<pre><code class="language-tsx">const person = { name:&#39;Lee&#39; };

person.age = 20;

console.log(Object.getOwnPropertyDescriptor(person, &#39;name&#39;));
// { value:&#39;Lee&#39;, writable: true, enumerable: true, configurable: true }

// Object.getOwnPropertyDescriptors 메서드를 사용하면 모든 프로퍼티의 프로퍼티 어트리뷰트
// 정보를 제공하는 프로퍼티 디스크립터 객체들을 반환한다.
console.log(Object.getOwnPropertyDescriptor(person);
/*
    {
        name: { value:&#39;Lee&#39;, writable: true, enumerable: true, configurable: true },
        age: { value:20, writable: true, enumerable: true, configurable: true } 
    }
*/</code></pre>
<br/>

<hr>
<h2 id="데이터-프로퍼티와-접근자-프로퍼티">데이터 프로퍼티와 접근자 프로퍼티</h2>
<p>프로퍼티는 데이터 프로퍼티와 접근자 프로퍼티로 구분할 수 있다. </p>
<br/>

<h3 id="1-데이터-프로퍼티">1. 데이터 프로퍼티</h3>
<p>키와 값으로 구성된 일반적인 프로퍼티다.</p>
<p>데이터 프로퍼티는 다음과 같은 프로퍼티 어트리뷰트를 갖는다. 이 프로퍼티 어트리뷰트는 자바스크립트 인젠이 프로퍼티를 생성할 때 기본값으로 자동 정의된다.</p>
<ol>
<li><code>[[Value]]</code></li>
</ol>
<ul>
<li><p>프로퍼티 디스크립터 객체의 프로퍼티는 <code>value</code>다.</p>
</li>
<li><p>프로퍼티 키를 통해 프로퍼티 값에 접근하면 반환되는 값이다.</p>
</li>
<li><p>프로퍼티 키를 통해 값을 변경하면 <code>[[Value]]</code>에 값을 재할당한다. 이때 프로퍼티가 없으면 프로퍼티를 동적 생성하고 생성된 프로퍼티의 <code>[[Value]]</code> 에 값을 저장한다.</p>
</li>
</ul>
<br/>

<ol start="2">
<li><code>[[Writable]]</code></li>
</ol>
<ul>
<li><p>프로퍼티 디스크립터 객체의 프로퍼티는 <code>writable</code>다.</p>
</li>
<li><p>프로퍼티 값의 변경 가능 여부를 나타내며 불리언 값을 갖는다.</p>
</li>
<li><p><code>[[Writable]]</code> 의 값이 <code>false</code>인 경우 값을 변경할 수 없는 읽기 전용 프로퍼티가 된다.</p>
</li>
</ul>
<br/>

<ol start="3">
<li><code>[[Enumerable]]</code></li>
</ol>
<ul>
<li><p>프로퍼티 디스크립터 객체의 프로퍼티는 <code>enumerable</code>다.</p>
</li>
<li><p>프로퍼티의 열거 가능 여부를 나타내며 불리언 값을 갖는다.</p>
</li>
<li><p>값이 <code>false</code>인 경우 <code>for…in문</code>이나 <code>Object.keys</code> 메서드 등으로 열거할 수 없다.</p>
</li>
</ul>
<br/>

<ol start="4">
<li><code>[[Configurable]]</code></li>
</ol>
<ul>
<li><p>프로퍼티 디스크립터 객체의 프로퍼티는 <code>configurable</code>다.</p>
</li>
<li><p>프로퍼티 재정의 가능 여부를 나타내며 불리언 값을 갖는다.</p>
</li>
<li><p>값이 <code>false</code>인 경우 해당 프로퍼티 삭제, 값의 변경이 금지된다. 단, <code>true</code>인 경우 값의 변경과 writable을 false로 변경하는 것은 허용된다.</p>
</li>
</ul>
<br/>

<h3 id="2-접근자-프로퍼티">2. 접근자 프로퍼티</h3>
<p>자체적으로 값을 갖지 않고 다른 데이터 프로퍼티의 값을 읽거나 저장할 때 호출되는 접근자 함수로 구성된 프로퍼티다. 아래와 같은 프로퍼티 어트리뷰를 갖는다.</p>
<ol>
<li><code>[[Get]]</code></li>
</ol>
<ul>
<li><p>프로퍼티 디스크립터 객체의 프로퍼티는 <code>get</code>다.</p>
</li>
<li><p>접근자 프로퍼티를 통해 데이터 프로퍼티의 값을 읽을 때 호출되는 접근자 함수다. 즉, 접근자 프로퍼티 키로 프로퍼티 값에 접근하면 프로퍼티 어트리뷰트 <code>[[Get]]</code> 의 값, 즉 getter 함수가 호출되고 그 결과가 프로퍼티 값으로 반환된다.</p>
</li>
</ul>
<br/>

<ol start="2">
<li><code>[[Set]]</code></li>
</ol>
<ul>
<li>프로퍼티 디스크립터 객체의 프로퍼티는 <code>set</code>다.</li>
<li>접근자 프로퍼티를 통해 데이터 프로퍼티의 값을 저장할 때 호출되는 접근자 함수다. 접근자 프로퍼티 키로 프로퍼티 값을 저장하면 setter 함수가 호출되고 그 결과가 프로퍼티 값으로 저장된다.</li>
</ul>
<br/>

<ol start="3">
<li><code>[[Enumerable]]</code></li>
</ol>
<ul>
<li>데이터 프로퍼티와 동일하다.</li>
</ul>
<br/>

<ol start="4">
<li><code>[[Configurable]]</code></li>
</ol>
<ul>
<li>데이터 프로퍼티와 동일하다.</li>
</ul>
<br/>

<p>접근자 함수는 getter/setter 함수라고도 부른다.</p>
<pre><code class="language-tsx">const person = {
    // 데이터 프로퍼티
    firstName: &#39;minsu&#39;,
    lastName: &#39;kim&#39;,

    // fullName은 접근자 함수로 구성된 접근자 프로퍼티다.
    // getter 함수
    get fullName(){
        return `${this.firstName} ${this.lastName}`
    }

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

console.log(person.fullName); // minsu kim

person.fullName = &#39;sejun han&#39;;
console.log(person); // { firstName: &#39;sejun&#39;, lastName: &#39;han&#39; }
</code></pre>
<p>접근자 프로퍼티는 자체적으로 값을 가지지 않으며 다만 데이터 프로퍼티의 값을 읽거나 저장할 때 관여할 뿐이다.</p>
<br/>

<hr>
<h2 id="프로퍼티-정의">프로퍼티 정의</h2>
<p>프로퍼티 정의란 새로운 프로퍼티를 추가하면서 프로퍼티 어트리뷰트를 명시적으로 정의하거나, 기존 프로퍼티의 프로퍼티 어트리뷰트를 재정의하는 것을 말한다.</p>
<br/>

<p><code>Object.defineProperty</code>메서드를 사용하면 프로퍼티의 어트리뷰트를 정의할 수 있다.</p>
<pre><code class="language-tsx">const person = {}&#39;

Object.defineProperty(person,&#39;firstName&#39;,{
    value:&#39;sejun&#39;,
    writable:true,
    enumable:true.
    configurable
})</code></pre>
<br/>

<hr>
<h2 id="객체-변경-방지">객체 변경 방지</h2>
<p>자바스크립트는 객체의 변경을 방지하는 다양한 메서드를 제공하낟. 객체 변경 방지 메서드들은 객체의 변경을 금지하는 강도가 다르다.</p>
<br/>

<h3 id="1-객체-확장-금지">1. 객체 확장 금지</h3>
<p><code>Object.preventExtensions</code> 메서드는 객체의 확장을 금지한다. 즉, 금지된 객체는 프로퍼티 추가가 금지된다.</p>
<pre><code class="language-tsx">const person = {
    name:&#39;sejun&#39;
}

// 확장 금지
Object.preventExtensions(person); 

person.age = 20;

console.log(person); // { name: &#39;sejun&#39; }</code></pre>
<br/>

<h3 id="객체-밀봉">객체 밀봉</h3>
<p><code>Object.seal</code> 메서드는 객체를 밀봉한다. 밀봉된 객체는 읽기와 쓰기만 가능하다.</p>
<pre><code class="language-tsx">const person = {
    name:&#39;sejun&#39;
}

// 밀봉
Object.seal(person);

// 추가 및 삭제는 불가능하다.
person.age = 20;
delete person.name
console.log(person); // { name: &#39;sejun&#39; }

// 수정은 가능하다.
person.name = &#39;ing&#39;;
console.log(person); // { name: &#39;ing&#39; }</code></pre>
<br/>

<h3 id="객체-동결">객체 동결</h3>
<p><code>Object.freeze</code> 메서드는 객체를 동결한다. 동결된 객체는 읽기만 가능하다.</p>
<pre><code class="language-tsx">const person = {
    name:&#39;sejun&#39;
}

// 동결
Object.freeze(person);

// 추가, 삭제, 수정 불가능하다.
person.age = 20;
delete person.name
person.name = &#39;ing&#39;;
console.log(person); // { name: &#39;sejun&#39; }
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ Components ] <Image/>]]></title>
            <link>https://velog.io/@chacha_fe/Components-Image-8ynwab5c</link>
            <guid>https://velog.io/@chacha_fe/Components-Image-8ynwab5c</guid>
            <pubDate>Thu, 25 May 2023 16:24:00 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-tsx">import Image from &#39;next/image&#39;;

export default function Page() {
  return (
    &lt;Image
      src=&quot;/profile.png&quot;
      width={500}
      height={500}
      alt=&quot;Picture of the author&quot;
    /&gt;
  );
}</code></pre>
<hr>
<h2 id="props">Props</h2>
<p>아래는 <code>&lt;Image&gt;</code> 컴포넌트에서 사용 가능한 props다.</p>
<table>
<thead>
<tr>
<th>Prop</th>
<th>Example</th>
<th>Type</th>
<th>Required</th>
</tr>
</thead>
<tbody><tr>
<td><a href="https://nextjs.org/docs/app/api-reference/components/image#src">https://nextjs.org/docs/app/api-reference/components/image#src</a></td>
<td>src=&quot;/profile.png&quot;</td>
<td>String</td>
<td>Yes</td>
</tr>
<tr>
<td><a href="https://nextjs.org/docs/app/api-reference/components/image#width">https://nextjs.org/docs/app/api-reference/components/image#width</a></td>
<td>width={500}</td>
<td>Integer (px)</td>
<td>Yes</td>
</tr>
<tr>
<td><a href="https://nextjs.org/docs/app/api-reference/components/image#height">https://nextjs.org/docs/app/api-reference/components/image#height</a></td>
<td>height={500}</td>
<td>Integer (px)</td>
<td>Yes</td>
</tr>
<tr>
<td><a href="https://nextjs.org/docs/app/api-reference/components/image#alt">https://nextjs.org/docs/app/api-reference/components/image#alt</a></td>
<td>alt=&quot;Picture of the author&quot;</td>
<td>String</td>
<td>Yes</td>
</tr>
<tr>
<td><a href="https://nextjs.org/docs/app/api-reference/components/image#loader">https://nextjs.org/docs/app/api-reference/components/image#loader</a></td>
<td>loader={imageLoader}</td>
<td>Function</td>
<td>-</td>
</tr>
<tr>
<td><a href="https://nextjs.org/docs/app/api-reference/components/image#fill">https://nextjs.org/docs/app/api-reference/components/image#fill</a></td>
<td>fill={true}</td>
<td>Boolean</td>
<td>-</td>
</tr>
<tr>
<td><a href="https://nextjs.org/docs/app/api-reference/components/image#sizes">https://nextjs.org/docs/app/api-reference/components/image#sizes</a></td>
<td>sizes=&quot;(max-width: 768px) 100vw&quot;</td>
<td>String</td>
<td>-</td>
</tr>
<tr>
<td><a href="https://nextjs.org/docs/app/api-reference/components/image#quality">https://nextjs.org/docs/app/api-reference/components/image#quality</a></td>
<td>quality={80}</td>
<td>Integer (1-100)</td>
<td>-</td>
</tr>
<tr>
<td><a href="https://nextjs.org/docs/app/api-reference/components/image#priority">https://nextjs.org/docs/app/api-reference/components/image#priority</a></td>
<td>priority={true}</td>
<td>Boolean</td>
<td>-</td>
</tr>
<tr>
<td><a href="https://nextjs.org/docs/app/api-reference/components/image#placeholder">https://nextjs.org/docs/app/api-reference/components/image#placeholder</a></td>
<td>placeholder=&quot;blur&quot;</td>
<td>String</td>
<td>-</td>
</tr>
<tr>
<td><a href="https://nextjs.org/docs/app/api-reference/components/image#style">https://nextjs.org/docs/app/api-reference/components/image#style</a></td>
<td>style={{objectFit: &quot;contain&quot;}}</td>
<td>Object</td>
<td>-</td>
</tr>
<tr>
<td><a href="https://nextjs.org/docs/app/api-reference/components/image#onloadingcomplete">https://nextjs.org/docs/app/api-reference/components/image#onloadingcomplete</a></td>
<td>onLoadingComplete={img =&gt; done())}</td>
<td>Function</td>
<td>-</td>
</tr>
<tr>
<td><a href="https://nextjs.org/docs/app/api-reference/components/image#onload">https://nextjs.org/docs/app/api-reference/components/image#onload</a></td>
<td>onLoad={event =&gt; done())}</td>
<td>Function</td>
<td>-</td>
</tr>
<tr>
<td><a href="https://nextjs.org/docs/app/api-reference/components/image#onerror">https://nextjs.org/docs/app/api-reference/components/image#onerror</a></td>
<td>onError(event =&gt; fail()}</td>
<td>Function</td>
<td>-</td>
</tr>
<tr>
<td><a href="https://nextjs.org/docs/app/api-reference/components/image#loading">https://nextjs.org/docs/app/api-reference/components/image#loading</a></td>
<td>loading=&quot;lazy&quot;</td>
<td>String</td>
<td>-</td>
</tr>
<tr>
<td><a href="https://nextjs.org/docs/app/api-reference/components/image#blurdataurl">https://nextjs.org/docs/app/api-reference/components/image#blurdataurl</a></td>
<td>blurDataURL=&quot;data:image/jpeg...&quot;</td>
<td>String</td>
<td>-</td>
</tr>
</tbody></table>
<hr>
<h2 id="필수적인-props">필수적인 Props</h2>
<p>이미지 컴포넌트에는 <code>src</code>, <code>width</code>, <code>height</code>, <code>alt</code> 속성이 필요하다.</p>
<pre><code class="language-tsx">import Image from &#39;next/image&#39;;

export default function Page() {
  return (
    &lt;div&gt;
      &lt;Image
        src=&quot;/profile.png&quot;
        width={500}
        height={500}
        alt=&quot;Picture of the author&quot;
      /&gt;
    &lt;/div&gt;
  );
}</code></pre>
<br/>

<h3 id="src"><code>src</code></h3>
<p>src 속성에는 정적으로 가져온 이미지 파일 또는 경로 문자열을 사용해야한다. loader 속성에 따라 절대적인 외부 URL 또는 내부 경로가 될 수 있다.
외부 URL을 사용하는 경우 <code>next.config.js</code>의 <strong>remotePatterns</strong>에 추가해야 한다.</p>
<br/>

<h3 id="width"><code>width</code></h3>
<p><code>width</code> 속성은 픽셀로 표시된 렌더링된 너비를 나타내므로 이미지의 크기에 영향을 준다.</p>
<p><strong>정적으로 가져온 이미지</strong>이거나 <code>fill</code> 속성이 있는 이미지를 제외하고는 필수다.</p>
<br/>

<h3 id="height"><code>height</code></h3>
<p><code>height</code> 속성은 픽셀로 표시된 렌더링된 높이를 나타내므로 이미지의 크기에 영향을 줍니다.</p>
<p><strong>정적으로 가져온 이미지</strong>이거나 <code>fill</code> 속성이 있는 이미지를 제외하고는 필수다.</p>
<br/>

<h3 id="alt"><code>alt</code></h3>
<p><code>alt</code> 속성은 스크린 리더 및 검색 엔진을 위해 이미지를 설명하는 데 사용된다. 이미지가 비활성화되었거나 이미지 로드 중에 오류가 발생한 경우 대체 텍스트로 사용된다.</p>
<p><strong>이미지의 의미를 변경하지 않고 이미지를 대체할 수 있는 텍스트</strong>를 포함해야 한다. 이미지를 보충하기 위한 것이 아니며 이미지 위 또는 아래의 캡션에서 이미 제공된 정보를 반복해서는 안된다.</p>
<p>이미지가 순전히 <strong>장식적인 목적으로 사용되거나 사용자를 위한 것이 아닌 경우</strong>, <code>alt</code> 속성은 빈 문자열 (<code>alt=&quot;&quot;</code>)이어야 한다.</p>
<br/>

<hr>
<h2 id="선택적인-props">선택적인 Props</h2>
<p><code>&lt;Image /&gt;</code> 컴포넌트는 필수 속성 이외에도 여러 가지 추가 속성을 받을 수 있다. </p>
<br/>

<h3 id="loader"><code>loader</code></h3>
<p>이미지 URL을 해결하는 데 사용되는 사용자 정의 함수다.</p>
<p><code>loader</code>는 다음 매개변수를 사용하여 이미지에 대한 URL 문자열을 반환하는 함수다.</p>
<ul>
<li><p><code>src</code></p>
</li>
<li><p><code>width</code></p>
</li>
<li><p><code>quality</code></p>
</li>
</ul>
<br/>

<p>사용자 정의 로더를 사용하는 예시는 다음과 같다.</p>
<pre><code class="language-tsx">import Image from &#39;next/image&#39;;

const imageLoader = ({ src, width, quality }) =&gt; {
  return `https://example.com/${src}?w=${width}&amp;q=${quality || 75}`;
};

export default function Page() {
  return (
    &lt;Image
      loader={imageLoader}
      src=&quot;me.png&quot;
      alt=&quot;Picture of the author&quot;
      width={500}
      height={500}
    /&gt;
  );
}</code></pre>
<br/>

<p>또한 prop을 전달하지 않고 애플리케이션의 모든 <code>next/image</code> 인스턴스를 구성하기 위해 <code>next.config.js</code>의 <strong>loaderFile</strong> 구성을 사용할 수도 있다.</p>
<br/>

<h3 id="fill"><code>fill</code></h3>
<p>부모 요소를 채우도록 이미지를 크기 조정하는 <code>boolean</code> 값이다. 이를 사용하면 <code>width</code>와 <code>height</code>를 설정하는 대신 이미지가 부모 요소를 채우게 된다.</p>
<pre><code class="language-tsx">fill={true} // {true} | {false}</code></pre>
<br/>

<p>부모 요소는 <code>position: &quot;relative&quot;</code>, <code>position: &quot;fixed&quot;</code> 또는 <code>position: &quot;absolute&quot;</code> 스타일을 지정해야 한다.</p>
<p>기본적으로 img 요소는 자동으로 <code>position: &quot;absolute&quot;</code> 스타일이 지정된다.</p>
<p>기본 이미지 맞춤 동작은 이미지를 컨테이너에 맞게 늘리는 것이다. 컨테이너에 맞게 letterbox 처리된 이미지에는 <code>object-fit: &quot;contain&quot;</code>을 설정하는 것이 좋다. 이렇게 하면 이미지의 가로세로 비율을 유지한 채로 컨테이너에 맞게 표시된다.</p>
<p>또는 <code>object-fit: &quot;cover</code>&quot;를 사용하면 이미지가 전체 컨테이너를 채우고 가로세로 비율을 유지하기 위해 잘린다. 이를 올바르게 보이려면 <code>overflow: &quot;hidden&quot;</code> 스타일을 부모 요소에 지정해야 한다.</p>
<br/>

<h3 id="sizes"><code>sizes</code></h3>
<p>이미지의 다양한 브레이크포인트에서 이미지의 너비에 대한 정보를 제공하는 문자열이다. <code>sizes</code>의 값은 <code>fill</code>을 사용하는 이미지나 반응형 크기를 가진 이미지의 성능에 큰 영향을 미친다.</p>
<br/>

<p><code>sizes</code> 속성은 이미지 성능과 관련하여 두 가지 중요한 목적을 제공한다.</p>
<ol>
<li><p><code>sizes</code>의 값은 브라우저가 <code>next/image</code>의 자동 생성 소스 세트에서 어떤 크기의 이미지를 다운로드할지 결정하는 데 사용된다. 브라우저가 선택할 때, 페이지에서 이미지의 크기를 아직 알지 못하므로, 뷰포트와 동일한 크기 또는 더 큰 크기의 이미지를 선택한다. <code>sizes</code> 속성을 사용하여 실제로 이미지가 전체 화면보다 작을 것임을 브라우저에 알릴 수 있다. <code>fill</code> 속성이 있는 이미지에서 <code>sizes</code> 값이 지정되지 않은 경우, 기본값으로 <code>100vw</code>(전체 화면 너비)가 사용된다.</p>
</li>
<li><p><code>sizes</code> 속성은 <code>next/image</code>가 자동으로 이미지 소스 세트를 생성하는 방식을 구성한다. <code>sizes</code> 값이 없는 경우, 고정 크기 이미지에 적합한 작은 소스 세트가 생성된다. <code>sizes</code>가 정의된 경우, 반응형 이미지에 적합한 큰 소스 세트가 생성된다. sizes 속성에 <code>50vw</code>와 같이 뷰포트 너비의 백분율을 나타내는 크기가 포함된 경우, 소스 세트는 필요하지 않은 값은 포함하지 않도록 자르게 된다.</p>
</li>
</ol>
<br/>

<p>예를 들어, 스타일링이 모바일 장치에서는 전체 너비로, 태블릿에서는 2열 레이아웃으로, 데스크탑 디스플레이에서는 3열 레이아웃으로 이미지를 표시하는 것을 알고 있다면, 다음과 같은 sizes 속성을 포함해야 한다.</p>
<pre><code class="language-tsx">import Image from &#39;next/image&#39;;

export default function Page() {
  return (
    &lt;div className=&quot;grid-element&quot;&gt;
      &lt;Image
        fill
        src=&quot;/example.png&quot;
        sizes=&quot;(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw&quot;
      /&gt;
    &lt;/div&gt;
  );
}</code></pre>
<br/>

<p>이 예제의 <code>sizes</code>는 성능 지표에 큰 영향을 줄 수 있다. <code>33vw</code> sizes가 없는 경우, 서버에서 선택한 이미지는 실제로 필요한 크기의 3배다. 파일 크기는 너비의 제곱에 비례하므로, <code>sizes</code>가 없으면 사용자는 필요한 크기보다 9배 큰 이미지를 다운로드하게 된다.</p>
<br/>

<h3 id="priority"><code>priority</code></h3>
<p>값이 <code>true</code>인 경우, 이미지는 우선 순위가 높은 이미지로 간주되어 preload된다. <code>preiority</code>가 있는 이미지를 사용하는 경우, 레이지 로딩은 자동으로 비활성된다.</p>
<pre><code class="language-tsx">priority={false} // {false} | {true}</code></pre>
<br/>

<p><strong>Largest Contentful Paint (LCP)</strong> 요소로 감지된 이미지에는 <code>priority</code> 속성을 사용해야 한다. 서로 다른 이미지가 다른 뷰포트 크기에 대한 LCP 요소일 수 있으므로 여러 우선 순위 이미지를 가질 수도 있다.</p>
<p>이 속성은 페이지 로딩 시 화면 상단에 표시되는 이미지에만 사용해야 한다. 기본값은 <code>false</code>다.</p>
<br/>

<h3 id="placeholder"><code>placeholder</code></h3>
<p>이미지가 로드되는 동안 사용할 placeholder다. 가능한 값은 <code>blur</code> 또는 <code>empty</code>다. 기본값은 <code>empty</code>다.</p>
<pre><code class="language-tsx">placeholder = &#39;empty&#39;; // {empty} | {blur}</code></pre>
<br/>

<p><code>blur</code>로 설정할 경우, plaehodler로 <code>blurDataURL</code> 속성이 사용된다. <code>src</code>가 정적 import에서 가져온 객체이고 가져온 이미지가 <code>.jpg</code>, <code>.png</code>, <code>.webp</code>, <code>.avif</code>인 경우, <code>blurDataURL</code>은 자동으로 채워진다.</p>
<p>동적 이미지의 경우, <code>blurDataURL</code> 속성을 제공해야 한다. <strong>Plaiceholder</strong>와 같은 솔루션은 <code>base64</code> 생성에 도움이 될 수 있다.</p>
<p><code>empty</code>로 설정할 경우, 이미지가 로드되는 동안 placeholder가 없으며, 빈 공간만 표시된다.</p>
<br/>

<hr>
<h2 id="고급-props">고급 props</h2>
<br/>

<h3 id="style"><code>style</code></h3>
<p>기본 이미지 요소에 CSS style을 전달할 수 있다.</p>
<pre><code class="language-tsx">const imageStyle = {
  borderRadius: &#39;50%&#39;,
  border: &#39;1px solid #fff&#39;,
};

export default function ProfileImage() {
  return &lt;Image src=&quot;...&quot; style={imageStyle} /&gt;;
}</code></pre>
<br/>

<p>필요한 <code>width</code>와 <code>height</code> 속성은 스타일링과 상호작용할 수 있다는 점을 기억해야 한다. 이미지의 너비를 수정하기 위해 스타일링을 사용하는 경우, 이미지의 가로세로 비율을 유지하기 위해 height를 <code>auto</code>로 스타일링해야 한다. 그렇지 않으면 이미지가 왜곡될 수 있다.</p>
<br/>

<h3 id="onloadingcomplete"><code>onLoadingComplete</code></h3>
<p>이미지가 완전히 로드되고 <strong>placeholder</strong>가 제거된 후 호출되는 콜백 함수다.</p>
<p>콜백 함수는 <code>&lt;img&gt;</code> 요소에 대한 참조를 포함한 하나의 인수와 함께 호출된다.</p>
<pre><code class="language-tsx">&lt;Image onLoadingComplete={(img) =&gt; console.log(img.naturalWidth)} /&gt;</code></pre>
<br/>

<h3 id="onload"><code>onLoad</code></h3>
<p>이미지가 <strong>로드</strong>될 때 호출되는 콜백 함수다.</p>
<pre><code class="language-tsx">&lt;Image onLoad={(e) =&gt; console.log(e.target.naturalWidth)} /&gt;</code></pre>
<p>참고로, load 이벤트는 플레이스홀더가 제거되고 이미지가 완전히 디코딩되기 전에 발생할 수 있다.</p>
<p>대신에 <code>onLoadingComplete</code>를 사용한다.</p>
<br/>

<h3 id="onerror"><code>onError</code></h3>
<p>이미지 로드 시에 호출되는 콜백 함수다.</p>
<pre><code class="language-tsx">&lt;Image onError={(e) =&gt; console.error(e.target.id)} /&gt;</code></pre>
<br/>

<h3 id="loading"><code>loading</code></h3>
<p>이미지 로딩 동작이다. 기본값은 <code>lazy</code>다.</p>
<p><code>lazy</code>로 설정할 경우, 이미지가 뷰포트로부터 계산된 거리에 도달할 때까지 이미지 로딩을 지연시킨다.</p>
<p><code>eager</code>로 설정할 경우, 이미지를 즉시 로드한다.</p>
<pre><code class="language-tsx">loading = &#39;lazy&#39;; // {lazy} | {eager}</code></pre>
<br/>

<p> 이 속성은 고급 사용 사례에만 적합하다. <code>eager</code>로 이미지를 로드하는 것은 일반적으로 성능에 해를 입힐 수 있다. 대신에 이미지를 즉시 사전로드하는 <code>priority</code> 속성을 사용하는 것을 권장한다.</p>
<br/>

<h3 id="blurdataurl"><code>blurDataURL</code></h3>
<p>성공적으로 로드되기 전에 <code>src</code> 이미지 대신 사용할 플레이스홀더 이미지로 사용될 <strong>데이터 UR</strong>L다. <code>placeholder=&quot;blur&quot;</code>와 함께 사용될 때에만 효과가 있다.</p>
<p>반드시 <code>base64</code>로 인코딩된 이미지여야 한다. 이미지는 확대되고 흐려지므로 매우 작은 이미지(10px 이하)를 권장한다. 더 큰 이미지를 플레이스홀더로 사용하면 애플리케이션의 성능에 문제가 발생할 수 있다.</p>
<p>다음을 시도해 보자:</p>
<ul>
<li><p>기본 <code>blurDataURL</code> 속성 데모</p>
</li>
<li><p><code>blurDataURL</code> 속성을 사용한 shimmer 효과 데모</p>
</li>
<li><p><code>blurDataURL</code> 속성을 사용한 색상 효과 데모</p>
</li>
</ul>
<br/>

<h3 id="unoptimized"><code>unoptimized</code></h3>
<p><code>true</code>로 설정할 경우, 원본 이미지가 품질, 크기 또는 형식을 변경하지 않고 그대로 제공된다. 기본값은 <code>false</code>다.</p>
<pre><code class="language-tsx">unoptimized = {false} // {false} | {true}</code></pre>
<pre><code class="language-tsx">import Image from &#39;next/image&#39;;

const UnoptimizedImage = (props) =&gt; {
  return &lt;Image {...props} unoptimized /&gt;;
};</code></pre>
<br/>

<p>Next.js 12.3.0부터는 다음 구성을 사용하여 <code>next.config.js</code>를 업데이트하여 모든 이미지에이 속성을 할당할 수 있다.</p>
<pre><code class="language-tsx">module.exports = {
  images: {
    unoptimized: true,
  },
};</code></pre>
<br/>

<h3 id="다른-props">다른 props</h3>
<p><code>&lt;Image /&gt;</code> 컴포넌트의 다른 속성은 다음을 제외하고 기본적으로 내부 <code>img</code> 요소로 전달된다.</p>
<ul>
<li><p><code>srcSet.</code> 대신 <strong>Device Sizes</strong>를 사용한다.</p>
</li>
<li><p><code>decoding</code>은 항상 <code>&quot;async&quot;</code>다.</p>
</li>
</ul>
<br/>

<h2 id="구성-옵션">구성 옵션</h2>
<p>props 외에도 <code>next.config.js</code>에서 Image 컴포넌트를 구성할 수 있다.</p>
<br/>

<h3 id="remotepatterns"><code>remotePatterns</code></h3>
<p>악의적인 사용자로부터 애플리케이션을 보호하기 위해 외부 이미지 사용에 대한 구성이 필요하다. 이를 위해 Next.js 이미지 최적화 API에서 계정의 외부 이미지만 제공되도록 보장한다. 다음과 같이 <code>next.config.js</code> 파일의 <code>remotePatterns</code> 속성으로 외부 이미지를 구성할 수 있다.</p>
<pre><code class="language-tsx">// next.config.js

module.exports = {
  images: {
    remotePatterns: [
      {
        protocol: &#39;https&#39;,
        hostname: &#39;example.com&#39;,
        port: &#39;&#39;,
        pathname: &#39;/account123/**&#39;,
      },
    ],
  },
};</code></pre>
<br/>

<p>위의 예제는 <code>next/image</code>의 <code>src</code> 속성이 <strong><code>https://example.com/account123/</code>로</strong> 시작해야 함을 보장한다. 다른 프로토콜, 호스트명, 포트 또는 일치하지 않는 경로는 <code>400 Bad Request</code>로 응답한다.</p>
<br/>

<p>다음은 <code>next.config.js</code> 파일의 <code>remotePatterns</code> 속성에 대한 또 다른 예제다.</p>
<pre><code class="language-tsx">module.exports = {
  images: {
    remotePatterns: [
      {
        protocol: &#39;https&#39;,
        hostname: &#39;**.example.com&#39;,
      },
    ],
  },
};</code></pre>
<br/>

<p>위의 예제에서는 <strong><code>https://img1.example.com</code></strong> 또는 <strong><code>https://me.avatar.example.com</code></strong>으로 시작하는 <code>src</code> 속성을 허용한다. 또한 임의의 수의 하위 도메인을 허용한다. 다른 프로토콜이나 일치하지 않는 호스트명은 <code>400 Bad Request</code>로 응답한다.</p>
<br/>
와일드카드 패턴은 경로명과 호스트명 모두에 사용할 수 있으며 다음 구문을 따른다.

<ul>
<li><p><code>*</code>는 단일 경로 세그먼트 또는 서브도메인을 일치시킨다.</p>
</li>
<li><p><code>**</code>는 끝에서 경로 세그먼트의 임의의 수 또는 시작에서 서브도메인의 임의의 수를 일치시킨다.</p>
</li>
<li><p><code>**</code>구문은 패턴의 중간에서 작동하지 않는다.</p>
</li>
</ul>
<br/>

<h3 id="domains"><code>domains</code></h3>
<p><code>remotePatterns</code>와 유사하게, <code>domains</code> 구성은 외부 이미지에 대한 허용된 호스트명 목록을 제공하는 데 사용될 수 있다.</p>
<p>하지만 <code>domains</code> 구성은 와일드카드 패턴 일치를 지원하지 않으며 프로토콜, 포트 또는 경로를 제한할 수 없다.</p>
<p>악의적인 사용자로부터 애플리케이션을 보호하기 위해 <code>domains</code> 대신 <code>strict remotePatterns</code>를 구성하는 것을 권장한다. <code>domains</code>는 도메인에서 제공되는 모든 콘텐츠를 소유한 경우에만 사용한다.</p>
<br/>

<p>다음은 <code>next.config.js</code> 파일의 <code>domains</code> 속성 예제다.</p>
<pre><code class="language-tsx">module.exports = {
  images: {
    domains: [&#39;assets.acme.com&#39;],
  },
};</code></pre>
<br/>

<h3 id="loaderfile"><code>loaderFile</code></h3>
<p>Next.js의 내장된 이미지 최적화 API 대신 클라우드 제공업체를 사용하여 이미지를 최적화하려는 경우, 다음과 같이 <code>next.config.js</code>에서 <code>loaderFile</code>을 구성할 수 있다.</p>
<pre><code class="language-tsx">module.exports = {
  images: {
    loader: &#39;custom&#39;,
    loaderFile: &#39;./my/image/loader.js&#39;,
  },
};</code></pre>
<br/>

<p>이는 Next.js 애플리케이션의 루트와 상대적인 파일을 가리켜야 한다. 파일은 문자열을 반환하는 기본 함수를 내보내야 한다. 아래 예제를 참고한다.</p>
<pre><code class="language-tsx">export default function myImageLoader({ src, width, quality }) {
  return `https://example.com/${src}?w=${width}&amp;q=${quality || 75}`;
}</code></pre>
<br/>

<p>또는 <code>next/image</code>의 각 인스턴스를 구성하기 위해 <code>loader</code> prop을 사용할 수도 있다.</p>
<br/>

<hr>
<h2 id="고급-설정">고급 설정</h2>
<p>다음 구성은 고급 사용 사례를 위한 것으로 일반적으로 필요하지 않다. 아래의 속성을 구성하는 경우, 향후 업데이트에서 Next.js의 기본값을 재정의한다.</p>
<br/>

<h3 id="devicesizes"><code>deviceSizes</code></h3>
<p>사용자의 기대하는 장치 너비를 알고 있다면, <code>next.config.js</code>의 <code>deviceSizes</code> 속성을 사용하여 장치 너비에 대한 목록을 지정할 수 있다. 이러한 너비는 <code>next/image</code> 컴포넌트가 <code>sizes</code> prop을 사용할 때 사용되며, 사용자의 장치에 적합한 이미지가 제공된다.</p>
<p>구성을 제공하지 않으면 아래의 <strong>기본값</strong>을 사용한다.</p>
<pre><code class="language-tsx">module.exports = {
  images: {
    deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
  },
};</code></pre>
<br/>

<h3 id="imagesizes"><code>imageSizes</code></h3>
<p><code>next.config.js</code> 파일에서 <code>images.imageSizes</code> 속성을 사용하여 이미지 너비의 목록을 지정할 수 있다. 이러한 너비는 장치 너비 배열과 연결되어 이미지 <strong>srcset</strong>을 생성하는 데 사용된다.</p>
<p>두 개의 별도 목록을 사용하는 이유는 imageSizes가 <code>sizes</code> prop을 제공하는 이미지에만 사용되기 때문이다. <code>sizes</code> prop은 이미지가 화면의 전체 너비보다 작음을 나타낸다. 따라서 <strong>imageSizes의 크기는 deviceSizes의 가장 작은 크기보다 작아야 한다.</strong></p>
<p>구성을 제공하지 않으면 아래의 <strong>기본값</strong>을 사용한다.</p>
<pre><code class="language-tsx">module.exports = {
  images: {
    imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
  },
};</code></pre>
<br/>

<h3 id="formats"><code>formats</code></h3>
<p>기본 <strong>이미지 최적화 API</strong>는 요청의 <code>Accept</code> 헤더를 통해 브라우저에서 지원하는 이미지 형식을 자동으로 감지한다.</p>
<p><code>Accept</code> 헤더가 구성된 형식과 일치하는 경우, 배열에서 첫 번째 일치 항목이 사용된다. 따라서 배열의 순서가 중요하다. 일치하는 항목이 없는 경우 (또는 소스 이미지가 <strong>애니메이션</strong>인 경우), 이미지 최적화 API는 원본 이미지 형식으로 대체된다.</p>
<p>구성을 제공하지 않으면 아래의 <strong>기본값</strong>을 사용한다.</p>
<pre><code class="language-tsx">module.exports = {
  images: {
    formats: [&#39;image/webp&#39;],
  },
};</code></pre>
<br/>

<p>다음 구성으로 <code>AVIF</code> 지원을 활성화할 수 있다.</p>
<pre><code class="language-tsx">module.exports = {
  images: {
    formats: [&#39;image/avif&#39;, &#39;image/webp&#39;],
  },
};</code></pre>
<br/>

<p>일반적으로 <code>AVIF</code>는 인코딩에 20% 더 오래 걸리지만 <code>WebP</code>에 비해 20% 더 작게 압축된다. 이는 이미지가 처음 요청될 때 일반적으로 느리게 작동하고, 그 후 캐시된 요청은 더 빠르게 작동할 것을 의미한다.</p>
<p>Next.js 앞에 Proxy/CDN을 사용하여 자체 호스팅하는 경우, Proxy가 <code>Accept</code> 헤더를 전달하도록 구성해야 한다.</p>
<br/>

<hr>
<h2 id="캐싱-동작">캐싱 동작</h2>
<p>이미지는 요청 시 동적으로 최적화되고 <code>&lt;distDir&gt;/cache/images</code> 디렉터리에 저장된다. 최적화된 이미지 파일은 만료될 때까지 후속 요청에 대해 제공된다. 캐시된 만료된 파일과 일치하는 요청이 들어오면, 만료된 이미지가 즉시 제공된다. 그런 다음 이미지는 다시 최적화되며, 백그라운드에서 캐시에 새로운 만료 날짜로 저장된다.(이를 재유효화라고도 함)</p>
<br/>

<p>이미지의 캐시 상태는 <code>x-nextjs-cache</code> 응답 헤더의 값으로 확인할 수 있다. 가능한 값은 다음과 같다.</p>
<ul>
<li><p><code>MISS</code> : 경로가 캐시에 없음 (첫 번째 방문 시 최대 한 번 발생)</p>
</li>
<li><p><code>STALE</code> : 경로가 캐시에 있지만 재유효화 시간을 초과하여 백그라운드에서 업데이트됨</p>
</li>
<li><p><code>HIT</code> : 경로가 캐시에 있고 재유효화 시간을 초과하지 않음</p>
</li>
</ul>
<br/>

<p>만료 시간 (혹은 Max Age)은 <code>minimumCacheTTL</code> 구성 또는 상위 이미지의 <code>Cache-Control</code> 헤더 중 더 큰 값에 의해 정의된다. 구체적으로는 <code>Cache-Control</code> 헤더의 <code>max-age</code> 값을 사용한다. <code>s-maxage</code>와 <code>max-age</code> 둘 다 발견되면 <code>s-maxage</code>가 우선한다. <code>max-age</code>는 CDN과 브라우저를 포함한 하위 클라이언트로 전달된다.</p>
<ul>
<li><p>upstream 이미지에 <code>Cache-Control</code> 헤더가 포함되지 않거나 값이 매우 낮은 경우 캐시 지속 시간을 늘리기 위해 <code>minimumCacheTTL</code>을 구성할 수 있다.</p>
</li>
<li><p><code>deviceSizes</code>와 <code>imageSizes</code>를 구성하여 생성 가능한 이미지 수를 줄일 수 있다.</p>
</li>
<li><p><code>formats</code>를 구성하여 여러 형식을 비활성화하고 단일 이미지 형식을 선호할 수 있다.</p>
</li>
</ul>
<br/>

<h3 id="minimumcachettl"><code>minimumCacheTTL</code></h3>
<p>캐시된 최적화된 이미지의 TTL(Time to Live)을 초 단위로 구성할 수 있다. 많은 경우에는 파일 내용을 자동으로 해싱하고 <code>불변</code>의 <code>Cache-Control</code> 헤더로 이미지를 영구적으로 캐시하는 <strong>정적 이미지 가져오기</strong>를 사용하는 것이 좋다.</p>
<pre><code class="language-tsx">module.exports = {
  images: {
    minimumCacheTTL: 60,
  },
};</code></pre>
<br/>

<p>최적화된 이미지의 만료 시간 (또는 Max Age)은 <code>minimumCacheTTL</code> 또는 상위 이미지의 <code>Cache-Control</code> 헤더 중 더 큰 값에 의해 정의된다.</p>
<p>이미지별로 캐싱 동작을 변경해야 하는 경우, <code>headers</code>를 구성하여 상위 이미지에 <code>Cache-Control</code> 헤더를 설정할 수 있다. (예: <code>/some-asset.jpg</code>, <code>/_next/image</code> 자체가 아닌)</p>
<p>현재 캐시를 무효화하는 메커니즘이 없으므로, <code>minimumCacheTTL</code>을 낮게 유지하는 것이 좋다. 그렇지 않으면 <code>src</code> prop을 수동으로 변경하거나 <code>&lt;distDir&gt;/cache/images</code>를 삭제해야 할 수도 있다.</p>
<br/>

<h3 id="disablestaticimages"><code>disableStaticImages</code></h3>
<p>기본 동작은 <code>import icon from &#39;./icon.png</code>과 같은 정적 파일을 가져와 <code>src</code> 속성에 전달하는 것을 허용한다.</p>
<p>다른 플러그인과 충돌하는 경우에는 이 기능을 비활성화하고 다른 방식으로 가져오기를 기대하는 경우가 있을 수 있다.</p>
<p><code>next.config.js</code>에서 정적 이미지 가져오기를 비활성화할 수 있다.</p>
<pre><code class="language-tsx">module.exports = {
  images: {
    disableStaticImages: true,
  },
};</code></pre>
<br/>

<h3 id="dangerouslyallowsvg"><code>dangerouslyAllowSVG</code></h3>
<p>디폴트 <strong>loader</strong>는 몇 가지 이유로 SVG 이미지를 최적화하지 않는다. 첫째로, SVG는 벡터 형식이므로 손실 없이 크기를 조정할 수 있다. 둘째로, SVG는 HTML/CSS와 많은 기능을 공유하기 때문에 적절한 <strong>콘텐츠 보안 정책 (CSP) 헤더</strong>가 없으면 취약점이 발생할 수 있다.</p>
<br/>

<p>기본 이미지 최적화 API를 사용하여 SVG 이미지를 제공해야 하는 경우, <code>next.config.js</code>에서 <code>dangerouslyAllowSVG</code>를 설정할 수 있다.</p>
<pre><code class="language-tsx">module.exports = {
  images: {
    dangerouslyAllowSVG: true,
    contentDispositionType: &#39;attachment&#39;,
    contentSecurityPolicy: &quot;default-src &#39;self&#39;; script-src &#39;none&#39;; sandbox;&quot;,
  },
};</code></pre>
<br/>


<p>또한, 브라우저가 이미지를 다운로드하도록 <code>contentDispositionType</code>을 설정하는 것과 이미지에 포함된 스크립트가 실행되지 않도록 <code>contentSecurityPolicy</code>를 설정하는 것을 강력히 권장한다.</p>
<br/>

<hr>
<h2 id="움직이는-이미지">움직이는 이미지</h2>
<p>디폴트 <strong>loader</strong>는 자동으로 애니메이션 이미지를 이미지 최적화 우회하고 원본 이미지 그대로 제공한다.</p>
<p>애니메이션 파일에 대한 자동 감지는 최선을 다하며 GIF, APNG, WebP를 지원한다. 특정 애니메이션 이미지에 대해 명시적으로 이미지 최적화를 우회하려면 <code>unoptimized</code> prop을 사용한다.</p>
<br/>

<hr>
<h2 id="알려진-브라우저-버그">알려진 브라우저 버그</h2>
<p>이 <code>next/image</code> 컴포넌트는 브라우저의 네이티브 <strong>지연 로딩</strong>을 사용하며, 이는 Safari 15.4 이전의 오래된 브라우저에서 즉시 로딩으로 대체될 수 있다. 희미한 placeholder를 사용하는 경우, Safari 12 이전의 오래된 브라우저는 빈 placeholder로 대체될 수 있다. <code>weight</code>/<code>height</code>가 자동(auto)인 스타일을 사용하는 경우, <strong>종횡비</strong>를 유지하지 않는 Safari 15 이전의 오래된 브라우저에서 <strong>레이아웃 이동(Layout Shift)</strong>이 발생할 수 있다. </p>
<br/>

<p><strong>Safari 15 및 16</strong>은 로딩 중에 회색 테두리를 표시한다. Safari 16.4에서 이 <strong>문제가 수정</strong>되었다. 가능한 해결책은 다음과 같다.</p>
<ul>
<li><p>CSS <code>@supports (font: -apple-system-body) 및 (-webkit-appearance: none)을 사용하여 img[loading=&quot;lazy&quot;] { clip-path: inset(0.6px) }</code>를 지정한다.</p>
</li>
<li><p>이미지가 화면에 표시되는 영역 위에 위치한다면 <code>priority</code>를 사용한다.</p>
</li>
</ul>
<br/>

<p>Firefox 67 이상에서는 로딩 중에 흰색 배경이 표시된다. 가능한 해결책은 다음과 같다.</p>
<ul>
<li><p>AVIF <code>format</code>을 활성화한다.</p>
</li>
<li><p><code>placeholder=&quot;blur&quot;</code>을 사용한다.</p>
</li>
</ul>
<br/>

<hr>
<p>[ 출처 ]
<a href="https://nextjs.org/docs/app/api-reference/components/image#caching-behavior">https://nextjs.org/docs/app/api-reference/components/image#caching-behavior</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[let, const 키워드와 블록 레벨 스코프]]></title>
            <link>https://velog.io/@chacha_fe/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/@chacha_fe/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>Thu, 25 May 2023 13:27:10 GMT</pubDate>
            <description><![CDATA[<h2 id="var-키워드로-선언한-변수의-문제점">var 키워드로 선언한 변수의 문제점</h2>
<h3 id="1-변수-중복-선언-허용">1. 변수 중복 선언 허용</h3>
<p><code>var</code> 키워드로 선언한 변수는 중복 선언이 가능하다.</p>
<pre><code class="language-tsx">var x = 1;
var y = 1;

// var 키워드로 선언된 변수는 같은 스코프 내에서 중복 선언을 허용한다.
// 초기화문이 있는 변수 선언문은 자바스크립트 엔진에 의해 var 키워드가 없는 것처럼 동작한다.

var x = 100;

// 초기화 없는 변수 선언문은 무시된다.
var y;

console.log(x); // 100
console.log(y); // 1</code></pre>
<br/>

<p><code>var</code> 키워드로 선언한 변수를 중복 선언하면 <strong>초기화문</strong>(변수 선언과 동시에 초기값을 할당하는 문) 유무에 따라 다르게 동작한다.</p>
<ul>
<li>초기화문이 있는 변수 선언문은 자바스크립트 엔진에 의해 var 키워드가 없는 것처럼 동작한다.</li>
<li>초기화문이 없는 변수 선언문은 무시된다. 이때 에러는 발생하지 않는다.</li>
</ul>
<br/>

<h3 id="2-함수-레벨-스코프">2. 함수 레벨 스코프</h3>
<p><code>var</code> 키워드로 선언한 변수는 오로지 <strong>함수의 코드 블록만을 지역 스코프로 인정</strong>한다.</p>
<pre><code class="language-tsx">var x = 1;

if(true){
    // x는 전역 변수다. 이미 선언된 전역 변수 x가 있으므로 x 변수는 중복 선언된다.
    // 이는 의도치 않게 변수값이 변경되는 부작용을 발생시킨다.
    var x = 10;
}

console.log(x); // 10</code></pre>
<br/>

<h3 id="3-변수-호이스팅">3. 변수 호이스팅</h3>
<p>변수 호이스팅에 의해 <code>var</code> 키워드로 선언한 변수는 변수 선언문 이전에 참조할 수 있다. 단, 할당문 이전에 변수를 참조하면 언제나 <code>undefind</code>를 반환한다.</p>
<pre><code class="language-tsx">// 이 시점에는 변수 호이스팅에 의해 이미 foo 변수가 선언되었다. (1. 선언 단계)
// 변수 foo는 undefined로 초기화된다. (2. 초기화 단계)
console.log(foo); // undefined

// 변수에 값을 할당(3. 할당 단계)
foo = 123;

console.log(foo); // 123

// 변수 선언은 런타임 이전에 자바스크립트 엔진에 의해 암묵적으로 실행된다.
var foo;</code></pre>
<br/>

<p>변수 선언문 이전에 변수를 참조하는 것은 변수 호이스팅에 의해 에러를 발생시키지는 않지만 프로그램의 흐름상 맞지 않을뿐더러 가독성을 떨어뜨리고 오류를 발생시킬 여지를 남긴다.</p>
<br/>

<hr>
<h2 id="let-키워드">let 키워드</h2>
<p>ES6에서는 새로운 변수 선언 키워드인 <code>let</code>과 <code>const</code>를 도입했다.</p>
<h3 id="1-변수-중복-선언-금지">1. 변수 중복 선언 금지</h3>
<p><code>var</code> 키워드로 이름이 동일한 변수를 중복 선언하면 아무런 에러가 발생하지 않는다. 하지만 <code>let</code> 키워드로 이름이 같은 변수를 중복 선언하면 문법 에러가 발생한다.</p>
<pre><code class="language-tsx">var foo = 123;
// var 키워드로 선언된 변수는 같은 스코프 내에서 중복 선언을 허용한다.
// 아래 변수 선언문은 자바스크립트 엔진에 의해 var 키워드가 없는 것처럼 동작한다.
var foo = 456;

let bar = 123;
// let이나 const 키워드로 선언된 변수는 같은 스코프 내에서 중복 선언을 허용하지 않는다.
let bar = 456; // SyntaxError</code></pre>
<br/>

<h3 id="2-블록-레벨-스코프">2. 블록 레벨 스코프</h3>
<p>let 키워드로 선언한 변수는 <code>모든 코드 블록</code>(함수 , if 문, for 문, while 문, try/catch 문 등)을 <strong>지역 스코프로 인정하는 블록 레벨 스코프를 따른다.</strong></p>
<pre><code class="language-tsx">let foo = 1; // 전역 변수 

{
    let foo = 2; // 지역 변수
    let bar = 3; // 지역 변수
}

console.log(foo); // 1
console.log(bar); // ReferenceError</code></pre>
<br/>

<h3 id="3-변수-호이스팅-1">3. 변수 호이스팅</h3>
<p><code>let</code> 키워드로 선언한 변수는 변수 호이스팅이 발생하지 않는 것처럼 동작한다.</p>
<pre><code class="language-tsx">console.log(foo); // ReferenceError
let foo;</code></pre>
<br/>

<p><code>var</code> 키워드와 달리 <code>let</code> 키워드로 선언한 변수는 <strong>선언 단계와 초기화 단계가 분리되어 진행</strong>된다.</p>
<p>즉, 런타임 이전에 자바스크립트 엔진에 의해 암묵적으로 선언 단계가 먼저 실행되지만 초기화 단계는 변수 선언문에 도달했을 때 실행된다.</p>
<p>만약 초기화 단계가 실행되기 전에 변수에 접근하려고 하면 참조 에러가 발생한다. let 키워드로 선언한 변수는 스코프의 시작 지점부터 초기화 단계 시작 지점(변수 선언문)까지 변수를 참조할 수 없다.</p>
<br/>

<p><strong>스코프의 시작 지점부터 초기화 시작 지점까지 변수를 참조할 수 없는 구간을 <code>일시적 사각지대(TDZ)</code>라고 한다.</strong></p>
<pre><code class="language-tsx">let foo = 1; // 전역 변수</code></pre>
<br/>

<h3 id="4-전역-객체와-let">4. 전역 객체와 let</h3>
<p><code>var</code> 키워드로 선언한 전역 변수와 전역 함수, 그리고 선언하지 않은 변수에 값을 할당한 암묵적 전역은 전역 객체 <code>window</code>의 프로퍼티가 된다. 전역 객체의 프로퍼티를 참조할 때 <code>window</code>를 생략할 수 있다.</p>
<pre><code class="language-tsx">// 전역 변수
var x = 1;

// 암묵적 전역
y = 2;

// 전역 함수
function foo() {}

// var 키워드로 선언한 전역 변수는 전역 객체 window의 프로퍼티다.
console.log(window.x); // 1;

// 전역 객체 window의 프로퍼티는 전역 변수처럼 사용할 수 있다.
console.log(x); // 1

// 암묵적 전역은 전역 객체 window의 프로퍼티다.
console.log(window.y); // 2
console.log(y); // 2

// 함수 선언문으로 정의한 전역 함수는 전역 객체 window의 프로퍼티다.
console.log(window.foo); // f foo() {}

// 전역 객체 window의 프로퍼티는 전역 변수처럼 사용할 수 있다.
console.log(foo); // f foo() {}</code></pre>
<br/>

<p><code>let</code> 키워드로 선언한 전역 변수는 전역 객체의 프로퍼티가 아니다. 즉, <code>window.foo</code>와 같이 접근할 수 없다.</p>
<p><code>let</code> 전역 변수는 보이지 않는 개념적인 블록 내에 존재하게 된다.</p>
<pre><code class="language-tsx">let x = 1;

// let, const 키워드로 선언한 전역 변수는 전역 객체 window의 프로퍼티가 아니다.
console.log(window.x); // undefined;
console.log(x); // 1</code></pre>
<br/>

<hr>
<h2 id="const-키워드">const 키워드</h2>
<p><code>const</code> 키워드는 <strong>상수</strong>를 선언하기 위해 사용한다.</p>
<h3 id="1-선언과-초기화">1. 선언과 초기화</h3>
<p><code>const</code> <strong>키워드로 선언한 변수는 반드시 선언과 동시에 초기화해야 한다.</strong></p>
<pre><code class="language-tsx">const foo = 1;

const bar; // SyntaxError</code></pre>
<br/>

<h3 id="2-재할당-금지">2. 재할당 금지</h3>
<p><code>const</code> <strong>키워드로 선언한 변수는 재할당이 금지된다.</strong></p>
<pre><code class="language-tsx">const foo = 1;
foo = 2; // TypeError: Assignment to constant variable.</code></pre>
<br/>

<h3 id="3-상수">3. 상수</h3>
<p><code>const</code> 키워드로 선언한 변수에 <strong>원시 값</strong>을 할당한 경우 변수 값을 변경할 수 없다. 원시 값은 변경 불가능한 값이므로 재할당 없이 값을 변경할 수 있는 방법이 없기 때문이다.</p>
<p>변수의 상대 개념인 <strong>상수는 재할당이 금지된 변수를 말한다.</strong> </p>
<p><code>const</code> <strong>키워드로 선언된 변수에 원시 값을 할당한 경우 원시 값은 변경할 수 없는 값이고</strong> <code>const</code> <strong>키워드에 의해 재할당이 금지되므로 할당된 값을 변경할 수 있는 방법은 없다.</strong></p>
<br/>

<h3 id="4-const-키워드와-객체">4. const 키워드와 객체</h3>
<p><code>const</code> 키워드로 선언된 변수에 <strong>객체</strong>를 할당한 경우 값을 변경할 수 있다. 변경 불가능한 값인 원시 값은 재할당 없이 변경할 수 있는 방법이 없지만 변경 가능한 객체는 재할다 없이도 직접 변경이 가능하기 때문이다.</p>
<pre><code class="language-tsx">const person = {
    name: &#39;Lee&#39;
};

// 객체는 변경 가능한 값이다. 따라서 재할당 없이 변경이 가능하다.
person.name = &#39;Kim&#39;;

console.log(person); // { name: &#39;Kim&#39; }</code></pre>
<br/>

<p><code>const</code> 키워드는 재할당을 금지할 뿐 <code>불변</code>을 의미하지는 않는다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[전역 변수의 문제점]]></title>
            <link>https://velog.io/@chacha_fe/%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/@chacha_fe/%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>Tue, 23 May 2023 13:38:58 GMT</pubDate>
            <description><![CDATA[<h2 id="변수의-생명-주기">변수의 생명 주기</h2>
<br/>

<h3 id="1-지역-변수의-생명-주기">1. 지역 변수의 생명 주기</h3>
<p>변수는 자신이 선언된 위치에서 생성되고 소멸한다. <strong>전역 변수의 생명 주기는 애플리케이션의 생명 주기와 같다.</strong> 하지만 <strong>지역변수의 생명주기는 함수의 생명 주기와 일치한다.</strong></p>
<pre><code class="language-jsx">function foo(){
    var x = &#39;local&#39;; // 변수 x 생성 -&gt; 변수 x에 값 할당
    console.log(x); // local
    return x; // 변수 x 소멸 
}

foo();
console.log(x); // ReferenceError</code></pre>
<p>함수 몸체 내부에서 선언된 지역 변수의 생명 주기는 함수의 생명 주기와 대부분 일치하지만 지역 변수가 함수보다 오래 생존하는 경우도 있다. (예: 클로저)</p>
<br/>

<h3 id="2-전역-변수의-생명-주기">2. 전역 변수의 생명 주기</h3>
<p>전역 변수의 생명 주기는 전역 객체의 생명주기와 일치한다.</p>
<pre><code class="language-jsx">var x = &#39;global&#39;; // 전역 변수 x 생성 -&gt; 전역 변수 x에 값 할당

function foo(){
    var x = &#39;local&#39;; // 지역 변수 x 생성 -&gt; 지역 변수 x에 값 할당
    console.log(x);
    return x;  // 지역변수 x 소멸
} 

foo();
console.log(x);</code></pre>
<br/>

<hr>
<h2 id="전역-변수의-문제점">전역 변수의 문제점</h2>
<br/>

<h3 id="암묵적-결합">암묵적 결합</h3>
<p>전역 변수를 선언한 의도는 전역에서 참조하고 할당할 수 있는 변수를 사용하겠다는 것이다.</p>
<p>이는 모든 코드가 전역 변수를 참조하고 변경할 수 있는 <code>암묵적 결합</code>을 허용하는 것이다. 변수의 유효 범위가 크면 클수록 코드의 가독성은 나빠지고 <strong>의도치 않게 상태가 변경될 수 있는 위험성</strong>도 높아진다.</p>
<br/>

<h3 id="긴-생명-주기">긴 생명 주기</h3>
<p><strong>전역 변수는 생명 주기가 길다</strong>. 따라서 메모리 리소스도 오랜 기간 소비한다. </p>
<p>또한 var 키워드는 변수의 중복 선언을 허용하므로 생명 주기가 긴 전역 변수는 변수 이름이 중복될 가능성이 있다. 변수 이름이 중복되면 재할당이 이뤄진다.</p>
<br/>

<h3 id="스코프-체인-상에서-종점에-존재">스코프 체인 상에서 종점에 존재</h3>
<p>전역 변수는 스코프 체인 상에서 종점에 존재한다. 이는 변수를 검색할 때 전역 변수가 가장 마지막에 검색된 다는 것을 의미한다. 즉, <strong>전역 변수의 검색 속도가 가장 느리다</strong>는 단점이 있다.</p>
<br/>

<h3 id="네임스페이스-오염">네임스페이스 오염</h3>
<p>자바스크립트의 가장 큰 문제점 중 하나는 파일이 분리되어 있다 해도 <strong>하나의 전역 스코프를 공유</strong>한다는 것이다. 따라서 다른 파일 내에서 동일한 이름으로 명명된 전역 변수나 전역 함수가 같은 스코프 내에 존재할 경우 예상치 못한 결과를 가져올 수 있다.</p>
<br/>

<hr>
<h2 id="전역-변수의-사용을-억제하는-방법">전역 변수의 사용을 억제하는 방법</h2>
<p>전역 변수는 반드시 사용해야할 이유가 있을 때 사용해야하며 변수의 스코프는 좁을수록 좋다. </p>
<br/>

<h3 id="즉시-실행-함수">즉시 실행 함수</h3>
<p>즉시 실행 함수는 함수 정의와 동시에 호출되는 즉시 실행 함수는 단 한 번만 호출된다. <strong>모든 코드를 즉시 실행 함수로 감싸면 모든 변수는 즉시 실행 함수의 지역 변수가 된다.</strong> 이러한 특성을 통해 전역 변수의 사용을 제한하는 방법이다.</p>
<pre><code class="language-jsx">(function(){
    var foo = 10; // 즉시 실행 함수의 지역 변수
})()

console.log(foo); // ReferenceError</code></pre>
<br/>

<h3 id="네임스페이스-객체">네임스페이스 객체</h3>
<p>전역에 <strong>네임스페이스</strong> 역할을 담당할 객체를 생성하고 전역 변수처럼 사용하고 싶은 변수를 프로퍼티 형태로 추가하는 방법이다.</p>
<pre><code class="language-jsx">var MYAPP = {}; // 전역 네임스페이스 객체

MYAPP.name = &#39;Lee&#39;;

console.log(MYAPP.name); // Lee</code></pre>
<p>네임스페이스를 분리해서 식별자 충돌을 방지하는 효과는 있으나 네임스페이스 객체 자체가 전역 변수에 할당되므로 그다지 유용한 방법은 아니다.</p>
<br/>

<h3 id="모듈-패턴">모듈 패턴</h3>
<p>모듈 패턴은 클래스를 모방해서 관련이 있는 변수와 함수를 모아 즉시 실행 함수로 감싸 하나의 모듈을 만든다. 모듈 패턴은 자바스크립트의 강력한 기능인 <strong>클로저를 기반</strong>으로 동작한다. 모듈 패턴의 특징은 전역 변수의 억제는 물론 <strong>캡슐화</strong>까지 구현할 수 있다는 것이다.</p>
<pre><code class="language-jsx">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</code></pre>
<br/>

<h3 id="es6-모듈">ES6 모듈</h3>
<p>ES6 모듈을 사용하면 더는 전역 변수를 사용할 수 없다. ES6 모듈은 파일 자체의 독자적인 모듈 스코프를 제공한다. 모듈 내에서 <code>var</code> 키워드로 선언한 변수는 더는 전역 변수가 아니며 window 객체의 프로퍼티도 아니다.</p>
<p><code>script</code> 태그에 <code>type=’module’</code> 속성을 추가하면 로드된 자바스크립트 파일은 모듈로서 동작한다.</p>
<pre><code class="language-jsx">&lt;script type=&#39;module&#39; scr=&#39;lib.ms&#39;&gt;&lt;/script&gt;
&lt;script type=&#39;module&#39; scr=&#39;app.ms&#39;&gt;&lt;/script&gt;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ Optimizing ] Lazy Loading]]></title>
            <link>https://velog.io/@chacha_fe/Optimizing-Lazy-Loading</link>
            <guid>https://velog.io/@chacha_fe/Optimizing-Lazy-Loading</guid>
            <pubDate>Tue, 23 May 2023 11:11:11 GMT</pubDate>
            <description><![CDATA[<p>Next.js의 레이지 로딩은 경로를 렌더링하는 데 필요한 JavaScript 양을 줄여 애플리케이션의 초기 로딩 성능을 향상시키는 데 도움이 된다.</p>
<p>레이지 로딩을 사용하면 클라이언트 컴포넌트와 가져온 라이브러리의 <strong>로딩을 지연</strong>시킬 수 있으며, 필요할 때만 클라이언트 번들에 포함시킬 수 있다. ( 예: 사용자가 모달을 열 때까지 모달의 로딩을 지연 )</p>
<br/>

<p>Next.js에서 레이지 로딩을 구현하는 두 가지 방법이 있다.</p>
<ul>
<li><code>next/dynamic</code>을 사용한 동적 임포트</li>
<li><code>React.lazy()</code>와 <code>Suspense</code>를 사용</li>
</ul>
<br/>

<p>기본적으로 서버 컴포넌트는 자동으로 <strong>코드를 분할</strong>하며, <strong>스트리밍</strong>을 사용하여 서버에서 클라이언트로 UI 조각을 점진적으로 전송할 수 있다. <strong>레이지 로딩은 클라이언트 컴포넌트에 적용</strong>된다.</p>
<br/>

<hr>
<h2 id="nextdynamic"><code>next/dynamic</code></h2>
<p><code>next/dynamic</code>은 <code>React.lazy(</code>)와 <code>Suspense</code>의 복합체다. <code>app</code>과 <code>page</code> 디렉토리에서 동일한 방식으로 동작하여 점진적인 마이그레이션을 가능하게 한다.</p>
<br/>

<hr>
<h2 id="예시">예시</h2>
<br/>

<h3 id="클라이언트-컴포넌트에서-불러오기">클라이언트 컴포넌트에서 불러오기</h3>
<pre><code class="language-jsx">// app/layout.js

&#39;use client&#39;;

import { useState } from &#39;react&#39;;
import dynamic from &#39;next/dynamic&#39;;

// Client Components:
const ComponentA = dynamic(() =&gt; import(&#39;../components/A&#39;));
const ComponentB = dynamic(() =&gt; import(&#39;../components/B&#39;));
const ComponentC = dynamic(() =&gt; import(&#39;../components/C&#39;), { ssr: false });

export default function ClientComponentExample() {
  const [showMore, setShowMore] = useState(false);

  return (
    &lt;div&gt;
      {/* Load immediately, but in a separate client bundle */}
      &lt;ComponentA /&gt;

      {/* Load on demand, only when/if the condition is met */}
      {showMore &amp;&amp; &lt;ComponentB /&gt;}
      &lt;button onClick={() =&gt; setShowMore(!showMore)}&gt;Toggle&lt;/button&gt;

      {/* Load only on the client side */}
      &lt;ComponentC /&gt;
    &lt;/div&gt;
  );
}</code></pre>
<br/>

<h3 id="ssr-건너뛰기">SSR 건너뛰기</h3>
<p><code>React.lazy()</code>와 Suspense를 사용할 때 기본적으로 클라이언트 컴포넌트는 사전 렌더링(SSR)된다.</p>
<p>특정 클라이언트 컴포넌트의 사전 렌더링을 비활성화하려면 <code>ssr</code> 옵션을 <code>false</code>로 설정할 수 있다.</p>
<pre><code class="language-jsx">const ComponentC = dynamic(() =&gt; import(&#39;../components/C&#39;), { ssr: false });</code></pre>
<br/>

<h3 id="서버-컴포넌트-가져오기">서버 컴포넌트 가져오기</h3>
<p>서버 컴포넌트를 동적으로 가져온 경우 서버 컴포넌트 자체가 아닌 서버 컴포넌트의 자식인 클라이언트 컴포넌트만 지연 로딩된다.</p>
<pre><code class="language-jsx">// app/page.js

import dynamic from &#39;next/dynamic&#39;;

// Server Component:
const ServerComponent = dynamic(() =&gt; import(&#39;../components/ServerComponent&#39;));

export default function ServerComponentExample() {
  return (
    &lt;div&gt;
      &lt;ServerComponent /&gt;
    &lt;/div&gt;
  );
}</code></pre>
<br/>

<h3 id="외부-라이브러리-로딩">외부 라이브러리 로딩</h3>
<p><code>import()</code> 함수를 사용하여 필요한 시점에 외부 라이브러리를 로드할 수 있다. 이 예제에서는 퍼지 검색을 위해 외부 라이브러리인 <code>fuse.js</code>를 사용한다. 사용자가 검색 입력란에 입력을 시작한 후에만 해당 모듈이 클라이언트에서 로드된다.</p>
<pre><code class="language-jsx">&#39;use client&#39;;

import { useState } from &#39;react&#39;;

const names = [&#39;Tim&#39;, &#39;Joe&#39;, &#39;Bel&#39;, &#39;Lee&#39;];

export default function Page() {
  const [results, setResults] = useState();

  return (
    &lt;div&gt;
      &lt;input
        type=&quot;text&quot;
        placeholder=&quot;Search&quot;
        onChange={async (e) =&gt; {
          const { value } = e.currentTarget;
          // Dynamically load fuse.js
          const Fuse = (await import(&#39;fuse.js&#39;)).default;
          const fuse = new Fuse(names);

          setResults(fuse.search(value));
        }}
      /&gt;
      &lt;pre&gt;Results: {JSON.stringify(results, null, 2)}&lt;/pre&gt;
    &lt;/div&gt;
  );
}</code></pre>
<br/>

<h3 id="커스텀-로딩-컴포넌트-추가">커스텀 로딩 컴포넌트 추가</h3>
<pre><code class="language-jsx">import dynamic from &#39;next/dynamic&#39;;

const WithCustomLoading = dynamic(
  () =&gt; import(&#39;../components/WithCustomLoading&#39;),
  {
    loading: () =&gt; &lt;p&gt;Loading...&lt;/p&gt;,
  },
);

export default function Page() {
  return (
    &lt;div&gt;
      {/* The loading component will be rendered while  &lt;WithCustomLoading/&gt; is loading */}
      &lt;WithCustomLoading /&gt;
    &lt;/div&gt;
  );
}</code></pre>
<br/>

<h3 id="importing-named-exports">Importing Named Exports</h3>
<p>이름 지정된 내보내기를 동적으로 가져오려면 <code>import()</code> 함수에서 반환된 Promise에서 해당 내보내기를 반환할 수 있다.</p>
<pre><code class="language-jsx">// components/hello.js

&#39;use client&#39;;

export function Hello() {
  return &lt;p&gt;Hello!&lt;/p&gt;;
}</code></pre>
<pre><code class="language-jsx">// app/page.js

import dynamic from &#39;next/dynamic&#39;;

const ClientComponent = dynamic(() =&gt;
  import(&#39;../components/ClientComponent&#39;).then((mod) =&gt; mod.Hello),
);</code></pre>
<br/>

<hr>
<p>[ 출처 ]
<a href="https://nextjs.org/docs/app/building-your-application/optimizing/lazy-loading">https://nextjs.org/docs/app/building-your-application/optimizing/lazy-loading</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ Optimizing ] Static Assets]]></title>
            <link>https://velog.io/@chacha_fe/Optimizing-Static-Assets</link>
            <guid>https://velog.io/@chacha_fe/Optimizing-Static-Assets</guid>
            <pubDate>Tue, 23 May 2023 11:09:15 GMT</pubDate>
            <description><![CDATA[<p>Next.js는 루트 디렉토리에 <code>public</code>이라는 폴더 아래와 같이 이미지와 같은 정적 파일을 제공할 수 있다.</p>
<p>public 내부의 파일은 기본 URL (<code>/</code>)을 기준으로 코드에서 참조할 수 있다.</p>
<br/>

<p>예를 들어, <code>public</code> 내부에 <code>me.png</code>를 추가하면 다음 코드로 이미지에 액세스할 수 있다.</p>
<pre><code class="language-jsx">import Image from &#39;next/image&#39;;

function Avatar() {
  return &lt;Image src=&quot;/me.png&quot; alt=&quot;me&quot; width=&quot;64&quot; height=&quot;64&quot; /&gt;;
}

export default Avatar;</code></pre>
<br/>

<p>이 폴더는 <code>robots.txt</code>, <code>favicon.ico</code>, <code>Google Site Verification</code> 및 다른 정적 파일(.html 포함)에도 유용하다.</p>
<ul>
<li><p>디렉토리 이름이 <code>public</code>인지 확인해야한다. 이름을 변경할 수 없으며, 정적 에셋을 제공하는 유일한 디렉토리다.</p>
</li>
<li><p><code>pages/</code> 디렉토리에 있는 파일과 같은 이름의 정적 파일이 있는지 확인하여 오류가 발생하지 않도록 해야 한다.</p>
</li>
<li><p>Next.js에서는 <strong>빌드 시점</strong>에 <code>public</code> 디렉토리에 있는 에셋만 제공된다. 런타임에서 추가된 파일은 사용할 수 없다. 지속적인 파일 저장을 위해 <strong>AWS S3</strong>와 같은 타사 서비스를 사용하는 것을 권장한다.</p>
</li>
</ul>
<br/>

<hr>
<p>[ 출처 ]
<a href="https://nextjs.org/docs/app/building-your-application/optimizing/static-assets">https://nextjs.org/docs/app/building-your-application/optimizing/static-assets</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ Optimizing ] Metadata]]></title>
            <link>https://velog.io/@chacha_fe/Optimizing-Metadata</link>
            <guid>https://velog.io/@chacha_fe/Optimizing-Metadata</guid>
            <pubDate>Tue, 23 May 2023 11:08:11 GMT</pubDate>
            <description><![CDATA[<p>Next.js에는 <strong>SEO</strong> 및 <strong>웹 공유성</strong>을 향상시키기 위해 애플리케이션의 <strong>메타데이터</strong>(예: HTML <code>head</code> 요소 내부의 <code>meta</code> 및 <code>link</code> 태그)를 정의할 수 있는 <strong>Metadata API</strong>가 있다.</p>
<br/>

<p>애플리케이션에 메타데이터를 추가하는 두 가지 방법이 있다.</p>
<ol>
<li><p><strong>구성 기반 메타데이터</strong>
<code>layout.js</code> 또는 <code>page.js</code> 파일에서 <strong>정적 메타데이터 객체</strong>나 <strong>동적</strong> <code>generateMetadata</code> 함수를 내보낸다.</p>
</li>
<li><p><strong>파일 기반 메타데이터</strong>
라우트 세그먼트에 정적 또는 동적으로 생성된 특수 파일을 추가한다.</p>
</li>
</ol>
<br/>

<p>이러한 두 가지 옵션 모두 Next.js가 페이지에 대한 관련 <code>&lt;head&gt;</code> 요소를 자동으로 생성한다.</p>
<p>또한 <code>ImageResponse</code> 생성자를 사용하여 <strong>동적 OG 이미지</strong>를 생성할 수도 있다.</p>
<br/>

<hr>
<h1 id="정적-메타데이터">정적 메타데이터</h1>
<p>정적 메타데이터를 정의하려면 <code>layout.js</code> 또는 정적 <code>page.js</code> 파일에서 <code>Metadata</code> 객체를 내보낸다.</p>
<pre><code class="language-jsx">// layout.tsx or page.tsx

import { Metadata } from &#39;next&#39;;

export const metadata: Metadata = {
  title: &#39;...&#39;,
  description: &#39;...&#39;,
};

export default function Page() {}</code></pre>
<br/>

<hr>
<h1 id="동적-메타데이터">동적 메타데이터</h1>
<p>동적 값을 필요로하는 메타데이터를 가져오기 위해 <code>generateMetadata</code> 함수를 사용할 수 있다.</p>
<pre><code class="language-jsx">// app/products/[id]/page.tsx

import { Metadata, ResolvingMetadata } from &#39;next&#39;;

type Props = {
  params: { id: string };
  searchParams: { [key: string]: string | string[] | undefined };
};

export async function generateMetadata(
  { params, searchParams }: Props,
  parent?: ResolvingMetadata,
): Promise&lt;Metadata&gt; {
  // read route params
  const id = params.id;

  // fetch data
  const product = await fetch(`https://.../${id}`).then((res) =&gt; res.json());

  // optionally access and extend (rather than replace) parent metadata
  const previousImages = (await parent).openGraph?.images || [];

  return {
    title: product.title,
    openGraph: {
      images: [&#39;/some-specific-page-image.jpg&#39;, ...previousImages],
    },
  };
}

export default function Page({ params, searchParams }: Props) {}</code></pre>
<br/>

<h3 id="💡-알아두면-좋은-점">💡 알아두면 좋은 점</h3>
<ul>
<li><p><strong>정적 및 동적 메타데이터</strong>(<code>generateMetadata</code>를 통한)는 <code>Server Components</code>에서만 지원된다.</p>
</li>
<li><p>라우트를 렌더링할 때, Next.js는 <code>generateMetadata</code>, <code>generateStaticParams</code>, <code>layout</code>, <code>page</code> 및 <code>Server Components</code> 전체에서 동일한 데이터에 대한 중복된 <code>fetch</code> 요청을 자동으로 제거한다. <code>fetch</code>를 사용할 수 없는 경우 <strong>React 캐시</strong>를 사용할 수 있다.</p>
</li>
<li><p>Next.js는 <code>generateMetadata</code> 내부에서 데이터를 가져오는 것이 완료될 때까지 UI를 클라이언트로 스트리밍하기 전까지 대기한다. 이는 스트리밍 응답의 첫 부분에 <code>&lt;head&gt;</code> 태그가 포함되도록 보장한다.</p>
</li>
</ul>
<br/>

<hr>
<h1 id="파일-기반-메타데이터">파일 기반 메타데이터</h1>
<p>이러한 특수 파일들은 메타데이터로 사용할 수 있다.</p>
<ul>
<li><p><code>favicon.ico, apple-icon.jpg</code>, 그리고 <code>icon.jpg</code></p>
</li>
<li><p><code>opengraph-image.jpg</code>와 <code>twitter-image.jpg</code></p>
</li>
<li><p><code>robots.txt</code></p>
</li>
<li><p><code>sitemap.xml</code></p>
</li>
</ul>
<p>이러한 파일들은 정적 메타데이터로 사용하거나 코드로 프로그래밍적으로 생성할 수 있다.</p>
<br/>

<hr>
<h1 id="동작">동작</h1>
<p>파일 기반 메타데이터는 더 높은 우선순위를 가지며, 구성 기반 메타데이터보다 우선하여 적용된다.</p>
<br/>

<h2 id="기본-필드-default-fields">기본 필드 (Default Fields)</h2>
<p>경로에서 메타데이터를 정의하지 않더라도 항상 추가되는 두 가지 기본 메타 태그가 있다.</p>
<ul>
<li><p><code>meta charset</code> 태그는 웹 사이트의 문자 인코딩을 설정한다.</p>
</li>
<li><p><code>meta viewport</code> 태그는 다른 기기에 맞게 웹 사이트의 뷰포트 너비와 확대/축소를 설정한다.</p>
</li>
</ul>
<br/>

<pre><code class="language-jsx">&lt;meta charset=&quot;utf-8&quot; /&gt;
&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot; /&gt;</code></pre>
<p>디폴트 <code>viewport</code> 메타 태그를 덮어쓸 수 있다.</p>
<br/>

<h2 id="순서-ordering">순서 (Ordering)</h2>
<p>메타데이터는 순서대로 평가되며, 최종 페이지.js 세그먼트에 가장 가까운 세그먼트에서부터 시작하여 루트 세그먼트까지 내려간다. 예를 들면 다음과 같다.</p>
<ol>
<li><p><code>app/layout.tsx</code> (루트 레이아웃)</p>
</li>
<li><p><code>app/blog/layout.tsx</code> (중첩된 블로그 레이아웃)</p>
</li>
<li><p><code>app/blog/[slug]/page.tsx</code> (블로그 페이지)</p>
</li>
</ol>
<br/>

<h2 id="병합-merging">병합 (Merging)</h2>
<p><strong>평가 순서</strong>를 따라 동일한 경로의 여러 세그먼트에서 내보낸 메타데이터 객체는 얕은 병합을 통해 경로의 최종 메타데이터 출력을 형성하기 위해 함께 병합된다. 중복된 키는 순서에 따라 교체된다.</p>
<p>이는 이전 세그먼트에서 정의된 <code>openGraph</code> 및 <code>robots</code>와 같은 중첩 필드를 가진 메타데이터가 마지막 세그먼트에 의해 덮어씌워진다는 것을 의미한다.</p>
<br/>

<h3 id="1-필드-덮어쓰기">1. 필드 덮어쓰기</h3>
<pre><code class="language-jsx">// app/layout.ts

export const metadata = {
  title: &#39;Acme&#39;,
  openGraph: {
    title: &#39;Acme&#39;,
    description: &#39;Acme is a...&#39;,
  },
};</code></pre>
<pre><code class="language-jsx">// app/blog/page.js

export const metadata = {
  title: &#39;Blog&#39;,
  openGraph: {
    title: &#39;Blog&#39;,
  },
};

// Output:
// &lt;title&gt;Blog&lt;/title&gt;
// &lt;meta property=&quot;og:title&quot; content=&quot;Blog&quot; /&gt;</code></pre>
<ul>
<li><p><code>app/layout.js</code>의 <code>title</code>은 <code>app/blog/page.js</code>의 <code>title</code>에 의해 대체된다.</p>
</li>
<li><p><code>app/blog/page.js</code>에서 <code>openGraph</code> 메타데이터를 설정하기 때문에 <code>app/layout.js</code>의 모든 <code>openGraph</code> 필드가 대체된다. <code>openGraph.description</code>이 없는 것에 주목해야한다.</p>
</li>
</ul>
<br/>

<p>일부 중첩된 필드를 세그먼트 간에 공유하면서 다른 필드를 덮어쓰고 싶다면 별도의 변수로 분리할 수 있다.</p>
<pre><code class="language-jsx">// app/shared-metadata.js

export const openGraphImage = { images: [&#39;http://...&#39;] };</code></pre>
<pre><code class="language-jsx">// app/page.js

import { openGraphImage } from &#39;./shared-metadata&#39;;

export const metadata = {
  openGraph: {
    ...openGraphImage,
    title: &#39;Home&#39;,
  },
};</code></pre>
<pre><code class="language-jsx">// app/about/page.js

import { openGraphImage } from &#39;../shared-metadata&#39;;

export const metadata = {
  openGraph: {
    ...openGraphImage,
    title: &#39;About&#39;,
  },
};</code></pre>
<br/>

<p>위의 예시에서는 <code>app/layout.js</code>와 <code>app/about/page.js</code> 간에 OG 이미지가 공유되고 제목은 다르다.</p>
<br/>

<h3 id="2-필드-상속">2. 필드 상속</h3>
<pre><code class="language-jsx">// app/layout.ts

export const metadata = {
  title: &#39;Acme&#39;,
  openGraph: {
    title: &#39;Acme&#39;,
    description: &#39;Acme is a...&#39;,
  },
};</code></pre>
<pre><code class="language-jsx">// app/about/page.js

export const metadata = {
  title: &#39;About&#39;,
};

// Output:
// &lt;title&gt;About&lt;/title&gt;
// &lt;meta property=&quot;og:title&quot; content=&quot;Acme&quot; /&gt;
// &lt;meta property=&quot;og:description&quot; content=&quot;Acme is a...&quot; /&gt;</code></pre>
<ul>
<li><p><code>app/layout.js</code>의 <code>title</code>은 <code>app/about/page.js</code>의 <code>title</code>에 의해 대체된다.</p>
</li>
<li><p><code>app/about/page.js</code>에서 <code>openGraph</code> 메타데이터를 설정하지 않았기 때문에 <code>app/layout.js</code>의 모든 <code>openGraph</code> 필드가 상속된다.</p>
</li>
</ul>
<br/>

<hr>
<h1 id="동적-이미지-생성">동적 이미지 생성</h1>
<p><code>ImageResponse</code> 생성자를 사용하면 JSX와 <strong>CSS</strong>를 사용하여 <strong>동적 이미지</strong>를 생성할 수 있다. 이는 오픈 그래프 이미지, Twitter 카드 등의 소셜 미디어 이미지를 생성하는 데 유용하다.</p>
<p><code>ImageResponse</code>는 <strong>Edge Runtime</strong>을 사용하며, Next.js는 자동으로 캐시된 이미지에 올바른 헤더를 추가하여 성능을 향상시키고 다시 계산을 줄이는 데 도움을 준다.</p>
<br/>

<p>사용하려면 <code>next/server</code>에서 <code>ImageResponse</code>를 가져올 수 있다.</p>
<pre><code class="language-jsx">// app/about/route.jsx

import { ImageResponse } from &#39;next/server&#39;;

export const runtime = &#39;edge&#39;;

export async function GET() {
  return new ImageResponse(
    (
      &lt;div
        style={{
          fontSize: 128,
          background: &#39;white&#39;,
          width: &#39;100%&#39;,
          height: &#39;100%&#39;,
          display: &#39;flex&#39;,
          textAlign: &#39;center&#39;,
          alignItems: &#39;center&#39;,
          justifyContent: &#39;center&#39;,
        }}
      &gt;
        Hello world!
      &lt;/div&gt;
    ),
    {
      width: 1200,
      height: 600,
    },
  );
}</code></pre>
<br/>

<p><code>ImageResponse</code>는 <strong>Route Handlers</strong> 및 파일 기반 메타데이터를 포함한 다른 Next.js API와 잘 통합된다. 예를 들어, <code>opengraph-image.tsx</code> 파일에서 <code>ImageResponse</code>를 사용하여 빌드 시간에 또는 요청 시간에 동적으로 오픈 그래프 이미지를 생성할 수 있다.</p>
<p><code>ImageResponse</code>는 flexbox와 절대 위치 지정을 포함한 일반적인 CSS 속성, 사용자 정의 폰트, 텍스트 줄 바꿈, 가운데 정렬 및 중첩 이미지를 지원한다.</p>
<br/>

<h3 id="💡--알아두면-좋은-점">💡  알아두면 좋은 점</h3>
<ul>
<li><p><code>ImageResponse</code>는 HTML과 CSS를 PNG로 변환하기 위해 <strong>@vercel/og</strong>, <strong>Satori</strong> 및 <strong>Resvg</strong>를 사용한다.</p>
</li>
<li><p><strong>Edge Runtime</strong>만 지원된다. 기본 Node.js 런타임은 작동하지 않는다.</p>
</li>
<li><p>flexbox와 CSS 속성의 일부만 지원된다. 고급 레이아웃(예: <code>display: grid</code>)은 작동하지 않는다.</p>
</li>
<li><p>최대 번들 크기는 <code>500KB</code>다. 번들 크기에는 JSX, CSS, 폰트, 이미지 및 기타 에셋이 포함된다. 제한을 초과하는 경우 에셋의 크기를 줄이거나 런타임에서 가져오는 방식을 고려해야 한다.</p>
</li>
<li><p><code>ttf</code>, <code>otf</code> 및 <code>woff</code> 폰트 형식만 지원된다. 폰트 구문 분석 속도를 극대화하기 위해 <code>woff</code>보다는 <code>ttf</code> 또는 <code>otf</code>가 선호된다.</p>
</li>
</ul>
<br/>

<hr>
<h1 id="json-ld">JSON-LD</h1>
<p><strong>JSON-LD</strong>는 구조화된 데이터를 검색 엔진이 내용을 이해하는 데 사용할 수 있는 형식이다. 예를 들어, 사람, 이벤트, 조직, 영화, 책, 레시피 및 여러 종류의 개체를 설명하는 데 사용할 수 있다.</p>
<p>JSON-LD에 대한 현재 권장 사항은 구조화된 데이터를 <code>layout.js</code> 또는 <code>page.js</code> 컴포넌트에서 <code>&lt;script&gt;</code> 태그로 렌더링하는 것이다. 예를 들면 다음과 같다.</p>
<pre><code class="language-jsx">// app/products/[id]/page.tsx

export default async function Page({ params }) {
  const product = await getProduct(params.id);

  const jsonLd = {
    &#39;@context&#39;: &#39;https://schema.org&#39;,
    &#39;@type&#39;: &#39;Product&#39;,
    name: product.name,
    image: product.image,
    description: product.description,
  };

  return (
    &lt;section&gt;
      {/* Add JSON-LD to your page */}
      &lt;script
        type=&quot;application/ld+json&quot;
        dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
      /&gt;
      {/* ... */}
    &lt;/section&gt;
  );
}</code></pre>
<br/>

<p>Google의 <strong>Rich Results Test</strong> 또는 일반적인 <strong>Schema Markup Validator</strong>를 사용하여 구조화된 데이터를 유효성 검사하고 테스트할 수 있다.</p>
<p><code>schema-dts</code>와 같은 커뮤니티 패키지를 사용하여 TypeScript로 JSON-LD를 작성할 수 있다.</p>
<br/>

<hr>
<p>[ 출처 ]
<a href="https://nextjs.org/docs/app/building-your-application/optimizing/metadata">https://nextjs.org/docs/app/building-your-application/optimizing/metadata</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ Optimizing ] Scripts]]></title>
            <link>https://velog.io/@chacha_fe/Optimizing-Scripts</link>
            <guid>https://velog.io/@chacha_fe/Optimizing-Scripts</guid>
            <pubDate>Tue, 23 May 2023 11:04:01 GMT</pubDate>
            <description><![CDATA[<h2 id="레이아웃-스크립트">레이아웃 스크립트</h2>
<p>여러 경로에 대해 타사 스크립트를 로드하려면 <code>next/script</code>를 가져오고 스크립트를 직접 레이아웃 컴포넌트에 포함시킨다.</p>
<pre><code class="language-jsx">// app/dashboard/layout.tsx

import Script from &#39;next/script&#39;;

export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    &lt;&gt;
      &lt;section&gt;{children}&lt;/section&gt;
      &lt;Script src=&quot;https://example.com/script.js&quot; /&gt;
    &lt;/&gt;
  );
}</code></pre>
<br/>

<p>사용자가 폴더 경로 (예: <code>dashboard/page.js</code>) 또는 중첩된 경로 (예: <code>dashboard/settings/page.js</code>)에 액세스할 때 타사 스크립트가 가져온다. Next.js는 동일한 레이아웃에서 여러 경로를 탐색하더라도 스크립트가 한 번만 로드되도록 보장한다.</p>
<br/>

<hr>
<h2 id="애플리케이션-스크립트">애플리케이션 스크립트</h2>
<p>모든 경로에 대해 타사 스크립트를 로드하려면 <code>next/script</code>를 가져오고 스크립트를 직접 루트 레이아웃에 포함시킨다.</p>
<pre><code class="language-jsx">// app/layout.tsx

import Script from &#39;next/script&#39;;

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    &lt;html lang=&quot;en&quot;&gt;
      &lt;body&gt;{children}&lt;/body&gt;
      &lt;Script src=&quot;https://example.com/script.js&quot; /&gt;
    &lt;/html&gt;
  );
}</code></pre>
<br/>

<p>이 스크립트는 애플리케이션의 <strong>모든 경로</strong>에 액세스할 때 로드되고 실행된다. Next.js는 사용자가 여러 페이지 사이를 이동하더라도 스크립트가 한 번만 로드되도록 보장한다.</p>
<p>성능에 불필요한 영향을 최소화하기 위해 타사 스크립트를 특정 페이지나 레이아웃에만 포함하는 것을 권장한다.</p>
<br/>

<hr>
<h2 id="전략-strategy">전략 (Strategy)</h2>
<p><code>next/script</code>의 기본 동작은 페이지나 레이아웃에서 타사 스크립트를 로드할 수 있게 해준다. 그러나 <code>strategy</code> 속성을 사용하여 로딩 동작을 세밀하게 조정할 수 있다.</p>
<ul>
<li><p><code>beforeInteractive</code> : Next.js 코드 및 페이지 하이드레이션 전에 스크립트를 로드한다.</p>
</li>
<li><p><code>afterInteractive</code> : (기본값) 일부 하이드레이션 이후에 스크립트를 조기에 로드한다.</p>
</li>
<li><p><code>lazyOnload</code> : 브라우저 idle time에 스크립트를 늦게 로드한다.</p>
</li>
<li><p><code>worker</code> : (실험적 기능) 웹 워커에서 스크립트를 로드한다.</p>
</li>
</ul>
<br/>

<hr>
<h2 id="스크립트를-웹-워커로-오프로드-실험적-기능">스크립트를 웹 워커로 오프로드 (실험적 기능)</h2>
<p>워커 전략은 아직 안정적이지 않으며 <code>app</code> 디렉토리와 함께 작동하지 않는다. 주의하여 사용해야한다.</p>
<p>워커 전략을 사용하는 스크립트는 <strong>Partytown</strong>과 함께 웹 워커에서 오프로드되고 실행된다. 이를 통해 메인 스레드를 애플리케이션 코드의 나머지 부분에 할당하여 사이트의 성능을 향상시킬 수 있다.</p>
<p>이 전략은 여전히 실험적이며 <code>next.config.js</code>에서 <code>nextScriptWorkers</code> 플래그가 활성화된 경우에만 사용할 수 있다.</p>
<pre><code class="language-jsx">// next.config.js

module.exports = {
  experimental: {
    nextScriptWorkers: true,
  },
};</code></pre>
<br/>

<p>그런 다음 <code>next</code>를 실행한다. (보통 <code>npm run dev</code> 또는 yarn dev). 그러면 Next.js가 필요한 패키지를 설치하기 위한 안내를 제공한다.</p>
<pre><code class="language-jsx">npm run dev</code></pre>
<br/>

<p>다음과 같은 지침을 볼 수 있다 :  <code>npm install @builder.io/partytown</code> 명령을 실행하여 Partytown을 설치한다.</p>
<p>설정이 완료되면 <code>strategy=&quot;worker&quot;</code>를 정의하면 Partytown이 자동으로 애플리케이션에 인스턴스화되고 스크립트가 웹 워커로 오프로드된다.</p>
<pre><code class="language-jsx">// pages/home.tsx

import Script from &#39;next/script&#39;;

export default function Home() {
  return (
    &lt;&gt;
      &lt;Script src=&quot;https://example.com/script.js&quot; strategy=&quot;worker&quot; /&gt;
    &lt;/&gt;
  );
}</code></pre>
<br/>

<hr>
<h2 id="인라인-스크립트">인라인 스크립트</h2>
<p>스크립트 구성 요소는 외부 파일에서 로드되지 않는 인라인 스크립트도 지원한다. 중괄호(<code>{}</code>) 안에 JavaScript를 작성하여 사용할 수 있다.</p>
<pre><code class="language-jsx">&lt;Script id=&quot;show-banner&quot;&gt;
  {`document.getElementById(&#39;banner&#39;).classList.remove(&#39;hidden&#39;)`}
&lt;/Script&gt;</code></pre>
<br/>

<p>또는 <code>dangerouslySetInnerHTML</code> 속성을 사용하여 작성할 수도 있다.</p>
<pre><code class="language-jsx">&lt;Script
  id=&quot;show-banner&quot;
  dangerouslySetInnerHTML={{
    __html: `document.getElementById(&#39;banner&#39;).classList.remove(&#39;hidden&#39;)`,
  }}
/&gt;</code></pre>
<br/>

<p>Next.js가 스크립트를 추적하고 최적화하기 위해 인라인 스크립트에는 <code>id</code> 속성을 할당해야 한다.</p>
<br/>

<hr>
<h2 id="추가-코드-실행-executing-additional-code">추가 코드 실행 (Executing Additional Code)</h2>
<p>이벤트 핸들러를 Script 컴포넌트와 함께 사용하여 특정 이벤트가 발생한 후에 추가 코드를 실행할 수 있다.</p>
<ul>
<li><p><code>onLoad</code> : 스크립트가 로드된 후에 코드를 실행한다.</p>
</li>
<li><p><code>onReady</code> : 스크립트가 로드된 후와 컴포넌트가 마운트될 때마다 코드를 실행한다.</p>
</li>
<li><p><code>onError</code> : 스크립트 로드에 실패한 경우에 코드를 실행한다.</p>
</li>
</ul>
<br/>

<p>이러한 핸들러는 <code>next/script</code>가 가져와지고 <code>&quot;use client&quot;</code>가 코드의 첫 번째 줄로 정의된 <strong>클라이언트 컴포넌트</strong> 내에서만 작동한다.</p>
<br/>

<hr>
<h2 id="추가-속성-additional-attributes">추가 속성 (Additional Attributes)</h2>
<p>Script 컴포넌트에서 사용되지 않는 <code>nonce</code>나 사용자 정의 데이터 속성과 같은 여러 DOM 속성을 <code>&lt;script&gt;</code> 요소에 할당할 수 있다. 추가 속성을 포함하면 최종으로 최적화된 <code>&lt;script&gt;</code> 요소로 자동으로 전달된다.</p>
<pre><code class="language-jsx">// app/page.tsx

import Script from &#39;next/script&#39;;

export default function Page() {
  return (
    &lt;&gt;
      &lt;Script
        src=&quot;https://example.com/script.js&quot;
        id=&quot;example-script&quot;
        nonce=&quot;XUENAJFW&quot;
        data-test=&quot;script&quot;
      /&gt;
    &lt;/&gt;
  );
}</code></pre>
<br/>

<hr>
<p>[ 출처 ]
<a href="https://nextjs.org/docs/app/building-your-application/optimizing/scripts">https://nextjs.org/docs/app/building-your-application/optimizing/scripts</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ Optimizing ] Fonts]]></title>
            <link>https://velog.io/@chacha_fe/Optimizing-Fonts</link>
            <guid>https://velog.io/@chacha_fe/Optimizing-Fonts</guid>
            <pubDate>Tue, 23 May 2023 11:00:56 GMT</pubDate>
            <description><![CDATA[<p><code>next/font</code>는 폰트를 자동으로 최적화하고(사용자 정의 폰트 포함) 개인 정보 보호와 성능 향상을 위해 외부 네트워크 요청을 제거한다. <a href="https://www.youtube.com/watch?v=L8_98i_bMMA">참고자료</a></p>
<p><code>next/font</code>에는 모든 폰트 파일에 대한 <strong>내장 자동 셀프 호스팅 기능</strong>이 포함되어 있다. 이는 사용되는 기본 CSS <code>size-adjust</code> 속성 덕분에 웹 폰트를 최적으로 로드하여 레이아웃 시프트를 제로로 만들 수 있다.</p>
<p>이 새로운 폰트 시스템을 사용하면 Google Fonts를 효율성과 개인 정보 보호를 고려하여 편리하게 사용할 수도 있다. CSS와 폰트 파일은 빌드 시간에 다운로드되고 정적 에셋의 나머지와 함께 자체 호스팅된다. <strong>브라우저에서 Google로 요청이 전송되지 않는다.</strong></p>
<br/>

<hr>
<h2 id="google-fonts">Google Fonts</h2>
<p>Google 폰트를 자동으로 <strong>셀프 호스팅</strong>한다. 폰트는 배포에 포함되어 배포와 동일한 도메인에서 제공된다. <strong>브라우저에서 Google로 요청이 전송되지 않는다.</strong></p>
<p><code>next/font/google</code>에서 사용하려는 폰트를 함수로 가져와서 시작한다. 최상의 성능과 유연성을 위해 <strong>변수 폰트</strong>를 사용하는 것을 권장한다.</p>
<pre><code class="language-jsx">// app/layout.tsx

import { Inter } from &#39;next/font/google&#39;;

// If loading a variable font, you don&#39;t need to specify the font weight
const inter = Inter({
  subsets: [&#39;latin&#39;],
  display: &#39;swap&#39;,
});

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    &lt;html lang=&quot;en&quot; className={inter.className}&gt;
      &lt;body&gt;{children}&lt;/body&gt;
    &lt;/html&gt;
  );
}</code></pre>
<br/>

<p>변수 폰트를 사용할 수 없는 경우, <code>weight</code>를 지정해야 한다.</p>
<pre><code class="language-jsx">import { Roboto } from &#39;next/font/google&#39;;

const roboto = Roboto({
  weight: &#39;400&#39;,
  subsets: [&#39;latin&#39;],
  display: &#39;swap&#39;,
});

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    &lt;html lang=&quot;en&quot; className={roboto.className}&gt;
      &lt;body&gt;{children}&lt;/body&gt;
    &lt;/html&gt;
  );
}</code></pre>
<br/>

<p><strong>배열</strong>을 사용하여 여러 <code>weight</code>와<code>/</code>또는 스타일을 지정할 수 있다.</p>
<pre><code class="language-jsx">const roboto = Roboto({
  weight: [&#39;400&#39;, &#39;700&#39;],
  style: [&#39;normal&#39;, &#39;italic&#39;],
  subsets: [&#39;latin&#39;],
  display: &#39;swap&#39;,
});</code></pre>
<br/>

<p>여러 단어로 된 폰트 이름에는 밑줄 (<code>_</code>)을 사용한다. 예: Roboto Mono는 Roboto_Mono로 가져와야 한다.</p>
<br/>

<h3 id="서브셋-지정specifying-a-subset">서브셋 지정(Specifying a subset)</h3>
<p>Google 폰트는 자동으로 <strong>서브셋</strong>으로 처리된다. 이렇게 하면 폰트 파일의 크기가 줄어들고 성능이 향상된다. 미리로드할 서브셋을 정의해야 한다. <code>preload</code>가 <code>true</code>인 상태에서 어떤 서브셋도 지정하지 않으면 경고가 발생한다.</p>
<p>이를 함수 호출에 추가하여 수행할 수 있다.</p>
<pre><code class="language-jsx">// app/layout.tsx

const inter = Inter({ subsets: [&#39;latin&#39;] });</code></pre>
<br/>

<h3 id="여러-폰트-사용하기using-multiple-fonts">여러 폰트 사용하기(Using Multiple Fonts)</h3>
<p>애플리케이션에서 여러 폰트를 가져와 사용할 수 있다. 두 가지 접근 방식이 있다.</p>
<p>첫 번째 접근 방식은 유틸리티 함수를 생성하여 폰트를 내보내고 가져와 필요한 곳에 <code>className</code>을 적용하는 것이다. 이렇게 하면 폰트가 렌더링될 때만 사전로드된다.</p>
<pre><code class="language-jsx">// app/fonts.ts

import { Inter, Roboto_Mono } from &#39;next/font/google&#39;;

export const inter = Inter({
  subsets: [&#39;latin&#39;],
  display: &#39;swap&#39;,
});

export const roboto_mono = Roboto_Mono({
  subsets: [&#39;latin&#39;],
  display: &#39;swap&#39;,
});</code></pre>
<pre><code class="language-jsx">// app/layout.tsx

import { inter } from &#39;./fonts&#39;;

export default function Layout({ children }: { children: React.ReactNode }) {
  return (
    &lt;html lang=&quot;en&quot; className={inter.className}&gt;
      &lt;body&gt;
        &lt;div&gt;{children}&lt;/div&gt;
      &lt;/body&gt;
    &lt;/html&gt;
  );
}</code></pre>
<pre><code class="language-jsx">// app/page.tsx
import { roboto_mono } from &#39;./fonts&#39;;

export default function Page() {
  return (
    &lt;&gt;
      &lt;h1 className={roboto_mono.className}&gt;My page&lt;/h1&gt;
    &lt;/&gt;
  );
}</code></pre>
<br/>

<p>위의 예제에서는 <code>Inter</code>가 전역으로 적용되고, <code>Roboto Mono</code>는 필요할 때 가져와 적용할 수 있다.</p>
<p>또는 <strong>CSS 변수</strong>를 생성하고 원하는 CSS 솔루션과 함께 사용할 수도 있다.</p>
<pre><code class="language-jsx">import { Inter, Roboto_Mono } from &#39;next/font/google&#39;;
import styles from &#39;./global.css&#39;;

const inter = Inter({
  subsets: [&#39;latin&#39;],
  variable: &#39;--font-inter&#39;,
  display: &#39;swap&#39;,
});

const roboto_mono = Roboto_Mono({
  subsets: [&#39;latin&#39;],
  variable: &#39;--font-roboto-mono&#39;,
  display: &#39;swap&#39;,
});

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    &lt;html lang=&quot;en&quot; className={`${inter.variable} ${roboto_mono.variable}`}&gt;
      &lt;body&gt;
        &lt;h1&gt;My App&lt;/h1&gt;
        &lt;div&gt;{children}&lt;/div&gt;
      &lt;/body&gt;
    &lt;/html&gt;
  );
}</code></pre>
<pre><code class="language-jsx">// app/global.css

html {
  font-family: var(--font-inter);
}

h1 {
  font-family: var(--font-roboto-mono);
}</code></pre>
<br/>

<p>위의 예제에서는 <code>Inter</code>가 전역으로 적용되고, <code>&lt;h1&gt;</code> 태그에는 Roboto Mono로 스타일이 지정된다.</p>
<p>클라이언트가 다운로드해야 하는 추가 리소스인 각 새로운 폰트마다 신중하게 여러 폰트를 사용하는 것을 권장한다.</p>
<br/>

<hr>
<h2 id="로컬-폰트">로컬 폰트</h2>
<p><code>next/font/local</code>을 가져와서 로컬 폰트 파일의 <code>src</code>를 지정하라. 최상의 성능과 유연성을 위해 <strong>변수 폰트</strong>를 사용하는 것을 권장한다.</p>
<pre><code class="language-jsx">// app/layout.tsx

import localFont from &#39;next/font/local&#39;;

// Font files can be colocated inside of `app`
const myFont = localFont({
  src: &#39;./my-font.woff2&#39;,
  display: &#39;swap&#39;,
});

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    &lt;html lang=&quot;en&quot; className={myFont.className}&gt;
      &lt;body&gt;{children}&lt;/body&gt;
    &lt;/html&gt;
  );
}</code></pre>
<br/>

<p>단일 폰트 패밀리에 여러 파일을 사용하려면 <code>src</code>를 <strong>배열</strong>로 지정할 수 있다.</p>
<pre><code class="language-jsx">const roboto = localFont({
  src: [
    {
      path: &#39;./Roboto-Regular.woff2&#39;,
      weight: &#39;400&#39;,
      style: &#39;normal&#39;,
    },
    {
      path: &#39;./Roboto-Italic.woff2&#39;,
      weight: &#39;400&#39;,
      style: &#39;italic&#39;,
    },
    {
      path: &#39;./Roboto-Bold.woff2&#39;,
      weight: &#39;700&#39;,
      style: &#39;normal&#39;,
    },
    {
      path: &#39;./Roboto-BoldItalic.woff2&#39;,
      weight: &#39;700&#39;,
      style: &#39;italic&#39;,
    },
  ],
});</code></pre>
<br/>

<hr>
<h2 id="tailwind-css와-사용">Tailwind CSS와 사용</h2>
<p><code>next/font</code>는 <strong>CSS 변수</strong>를 통해 <code>Tailwind CSS</code>와 함께 사용할 수 있다.</p>
<br/>

<p>아래 예제에서는 <code>next/font/google</code>에서 <code>Inter</code> 폰트를 사용한다. (Google이나 로컬 폰트에서 원하는 폰트를 사용할 수 있다). <code>변수</code> 옵션을 사용하여 폰트를 로드하고 CSS 변수 이름을 정의하고 <code>inter</code>에 할당한다. 그런 다음, <code>inter.variable</code>을 사용하여 CSS 변수를 HTML 문서에 추가한다.</p>
<pre><code class="language-jsx">// app/layout.tsx

import { Inter, Roboto_Mono } from &#39;next/font/google&#39;;

const inter = Inter({
  subsets: [&#39;latin&#39;],
  display: &#39;swap&#39;,
  variable: &#39;--font-inter&#39;,
});

const roboto_mono = Roboto_Mono({
  subsets: [&#39;latin&#39;],
  display: &#39;swap&#39;,
  variable: &#39;--font-roboto-mono&#39;,
});

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    &lt;html lang=&quot;en&quot; className={`${inter.variable} ${roboto_mono.variable}`}&gt;
      &lt;body&gt;{children}&lt;/body&gt;
    &lt;/html&gt;
  );
}</code></pre>
<br/>

<p>마지막으로, <code>Tailwind CSS config</code>에 <strong>CSS 변수</strong>를 추가한다.</p>
<pre><code class="language-jsx">// tailwind.config.js
/** @type {import(&#39;tailwindcss&#39;).Config} */
module.exports = {
  content: [
    &#39;./pages/**/*.{js,ts,jsx,tsx}&#39;,
    &#39;./components/**/*.{js,ts,jsx,tsx}&#39;,
  ],
  theme: {
    extend: {
      fontFamily: {
        sans: [&#39;var(--font-inter)&#39;],
        mono: [&#39;var(--font-roboto-mono)&#39;],
      },
    },
  },
  plugins: [],
};</code></pre>
<br/>

<p>이제 <code>font-sans</code>와 <code>font-mono</code> 유틸리티 클래스를 사용하여 폰트를 요소에 적용할 수 있다.</p>
<br/>

<hr>
<h2 id="사전-로드preloading">사전 로드(Preloading)</h2>
<p>사이트의 페이지에서 폰트 함수를 호출할 때, 해당 폰트는 전역으로 사용 가능하고 모든 경로에 사전로드되는 것은 아니다. 대신, 사용된 파일의 종류에 따라 관련 경로에만 폰트가 사전로드된다.</p>
<ul>
<li><p><code>고유한 페이지</code>인 경우, 해당 페이지의 고유 경로에 사전로드된다.</p>
</li>
<li><p><code>레이아웃</code>인 경우, 해당 레이아웃으로 래핑된 모든 경로에 사전로드된다.</p>
</li>
<li><p><code>루트 레이아웃</code>인 경우, 모든 경로에 사전로드된다.</p>
</li>
</ul>
<br/>

<hr>
<h2 id="폰트-재사용reusing-fonts">폰트 재사용(Reusing Fonts)</h2>
<p><code>localFont</code> 또는 Google 폰트 함수를 호출할 때마다 해당 폰트는 애플리케이션 내에서 하나의 인스턴스로 호스팅된다. 따라서 동일한 폰트 함수를 여러 파일에서 로드하는 경우, 동일한 폰트의 여러 인스턴스가 호스팅된다.</p>
<p>이 경우 다음을 권장한다.</p>
<ul>
<li><p>공유 파일에서 폰트 로더 함수를 호출한다.</p>
</li>
<li><p>상수로 export 한다.</p>
</li>
<li><p>해당 폰트를 사용하려는 각 파일에서 해당 상수를 import 한다.</p>
</li>
</ul>
<br/>

<hr>
<p>[ 출처 ]
<a href="https://nextjs.org/docs/app/building-your-application/optimizing/fonts">https://nextjs.org/docs/app/building-your-application/optimizing/fonts</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ Optimizing ] Images]]></title>
            <link>https://velog.io/@chacha_fe/Optimizing-Images</link>
            <guid>https://velog.io/@chacha_fe/Optimizing-Images</guid>
            <pubDate>Tue, 23 May 2023 10:58:14 GMT</pubDate>
            <description><![CDATA[<p>Next.js의 <code>Image</code> 컴포넌트는 HTML <code>&lt;img&gt;</code> 요소를 확장하여 다음과 같은 기능을 제공한다.</p>
<ol>
<li><p><strong>크기 최적화</strong></p>
<p> WebP 및 AVIF와 같은 현대적인 이미지 형식을 사용하여 각 장치에 맞게 올바르게 크기 조정된 이미지를 자동으로 제공한다.</p>
</li>
<li><p><strong>시각적 안정성</strong>
이미지가 로드될 때 자동으로 <code>레이아웃 이동을 방지</code>한다.</p>
</li>
<li><p><strong>빠른 페이지 로딩</strong>
이미지는 웹 브라우저의 네이티브 지연 로딩을 사용하여 뷰포트에 진입할 때만 로드되며, 선택적으로 흐릿한 플레이스홀더를 사용할 수 있다.</p>
</li>
<li><p><strong>자산 유연성</strong>
원격 서버에 저장된 이미지를 포함하여 필요에 따라 이미지 크기를 실시간으로 조정할 수 있다.</p>
</li>
</ol>
<br/>

<hr>
<h2 id="사용법">사용법</h2>
<pre><code class="language-jsx">import Image from &#39;next/image&#39;;</code></pre>
<br/>

<h3 id="로컬-이미지local-images">로컬 이미지(Local Images)</h3>
<p>로컬 이미지를 사용하려면 <code>.jpg</code>, <code>.png</code> 또는 <code>.webp</code> 이미지 파일을 가져와야 한다.</p>
<p>Next.js는 가져온 파일을 기반으로 이미지의 <strong>너비와 높이를 자동으로 결정한다.</strong> 이 값은 이미지가 로드되는 동안 누적 레이아웃 변화(<code>Cumulative Layout Shift</code>)를 방지하는 데 사용된다.</p>
<pre><code class="language-jsx">// app/page.js

import Image from &#39;next/image&#39;;
import profilePic from &#39;./me.png&#39;;

export default function Page() {
  return (
    &lt;Image
      src={profilePic}
      alt=&quot;Picture of the author&quot;
      // width={500} automatically provided
      // height={500} automatically provided
      // blurDataURL=&quot;data:...&quot; automatically provided
      // placeholder=&quot;blur&quot; // Optional blur-up while loading
    /&gt;
  );
}</code></pre>
<br/>

<p>동적인 <code>await import()</code> 또는 <code>require()</code>은 지원되지 않는다. <code>import</code>는 정적이어야 하므로 빌드 시간에 분석할 수 있어야 한다.</p>
<br/>

<h3 id="원격-이미지remote-images">원격 이미지(Remote Images)</h3>
<p>원격 이미지를 사용하려면 <code>src</code> 속성에 URL 문자열을 제공해야 한다.</p>
<p>Next.js는 빌드 과정에서 원격 파일에 액세스할 수 없으므로 <code>width</code>, <code>height</code> 및 선택적인 <code>blurDataURL</code> 속성을 수동으로 제공해야 한다.</p>
<p><code>너비</code>와 <code>높이</code> 속성은 이미지의 올바른 종횡비를 추론하고 이미지 로딩으로 인한 <strong>레이아웃 변화를 방지</strong>하는 데 사용된다. 너비와 높이는 이미지 파일의 렌더링 크기를 결정하지는 않는다.</p>
<pre><code class="language-jsx">import Image from &#39;next/image&#39;;

export default function Page() {
  return (
    &lt;Image
      src=&quot;https://s3.amazonaws.com/my-bucket/profile.png&quot;
      alt=&quot;Picture of the author&quot;
      width={500}
      height={500}
    /&gt;
  );
}</code></pre>
<br/>

<p>이미지 최적화를 안전하게 허용하려면 <code>next.config.js</code>에서 지원되는 URL 패턴 목록을 정의한다. 악의적인 사용을 방지하기 위해 가능한 한 구체적으로 지정해야한다. </p>
<br/>

<p>예를 들어, 다음 구성은 특정 AWS S3 버킷에서만 이미지를 허용한다.</p>
<pre><code class="language-jsx">// next.config.js

module.exports = {
  images: {
    remotePatterns: [
      {
        protocol: &#39;https&#39;,
        hostname: &#39;s3.amazonaws.com&#39;,
        port: &#39;&#39;,
        pathname: &#39;/my-bucket/**&#39;,
      },
    ],
  },
};</code></pre>
<br/>

<p>이미지 <code>src</code>에 상대적인 URL을 사용하려면 로더(<code>loader</code>)를 사용한다.</p>
<br/>

<h3 id="도메인domains">도메인(Domains)</h3>
<p>원격 이미지를 최적화하면서도 내장된 Next.js 이미지 최적화 API를 사용하고 싶을 수 있다. 이를 위해 로더(<code>loader</code>)를 기본 설정으로 유지하고 Image <code>src</code> 속성에 절대 URL을 입력하면 된다.</p>
<p>악의적인 사용자로부터 애플리케이션을 보호하기 위해 <code>next/image</code> 컴포넌트와 함께 사용할 원격 호스트 이름 목록을 정의해야 한다.</p>
<br/>

<h3 id="loaders">Loaders</h3>
<p>앞서 언급한 예제에서 원격 이미지에 대해 부분 URL(<code>&quot;/me.png&quot;</code>)이 제공되었다. 이는 로더 아키텍처 덕분에 가능한 것이다.</p>
<p>로더는 이미지의 URL을 생성하는 함수다. 이는 제공된 <code>src</code>를 수정하고 다른 크기로 이미지를 요청하기 위해 여러 URL을 생성한다. 이러한 여러 URL은 자동으로 <code>srcset</code>을 생성하는 데 사용되어 방문자가 뷰포트에 적합한 크기의 이미지를 제공받을 수 있다.</p>
<p>Next.js 애플리케이션의 기본 로더는 내장된 이미지 최적화 API를 사용하여 웹의 어느 곳에서나 이미지를 최적화한 다음 Next.js 웹 서버에서 직접 제공한다. CDN이나 이미지 서버에서 이미지를 직접 제공하고 싶다면 몇 줄의 JavaScript로 고유한 로더 함수를 작성할 수 있다.</p>
<p><code>loader</code> prop으로 이미지당 로더를 정의하거나 <code>loaderFile</code> <strong>configuration</strong>을 사용하여 애플리케이션 수준에서 로더를 정의할 수 있다.</p>
<br/>

<hr>
<h2 id="우선-순위priority">우선 순위(priority)</h2>
<p>각 페이지의 가장 큰 콘텐츠가 되는 이미지에 <code>priority</code> 속성을 추가해야 한다. 이렇게 함으로써 Next.js는 이미지를 로딩하기 위해 특별한 우선순위(예: <code>preload</code> 태그 또는 우선순위 힌트)를 부여할 수 있으며, <strong>LCP</strong>(Largest Contentful Paint)에 의미 있는 향상을 가져올 수 있다.</p>
<p><strong>LCP</strong> 요소는 일반적으로 페이지의 뷰포트 내에서 가장 큰 이미지 또는 텍스트 블록이다. <code>next dev</code>를 실행할 때 <strong>LCP</strong> 요소가 <code>priority</code> 속성이 없는 <code>&lt;Image&gt;</code>인 경우 콘솔 경고가 표시된다.</p>
<p>LCP 이미지를 식별한 후에는 다음과 같이 속성을 추가할 수 있다.</p>
<pre><code class="language-jsx">import Image from &#39;next/image&#39;;
import profilePic from &#39;../public/me.png&#39;;

export default function Page() {
  return &lt;Image src={profilePic} alt=&quot;Picture of the author&quot; priority /&gt;;
}</code></pre>
<br/>

<hr>
<h2 id="이미지-사이징image-sizing">이미지 사이징(Image Sizing)</h2>
<p>이미지가 성능에 가장 많은 영향을 주는 방법 중 하나는 이미지가 로드되는 동안 페이지에서 다른 요소들을 밀어내어 레이아웃이 변경되는 레이아웃 시프트(<code>layout shift</code>)다. 이러한 성능 문제는 사용자에게 귀찮음을 야기하여 <code>Cumulative Layout Shift</code>라는 자체적인 <strong>Core Web Vital</strong>을 가지게 되었다. 이미지 기반 레이아웃 시프트를 피하기 위해서는 항상 이미지의 크기를 지정해야 한다. 이렇게 함으로써 브라우저는 이미지가 로드되기 전에 정확히 충분한 공간을 예약할 수 있다.</p>
<br/>

<p><code>next/image</code>는 좋은 성능 결과를 보장하기 위해 설계되었으므로 레이아웃 시프트에 기여하지 않을 수 있는 방식으로 사용해야 하며, 다음 중 하나의 방식으로 크기를 지정해야 한다.</p>
<ol>
<li><p><strong>정적 임포트</strong>를 사용하여 자동으로 크기를 결정한다.</p>
</li>
<li><p><code>width</code>와 <code>height</code> 속성을 사용하여 명시적으로 크기를 지정한다.</p>
</li>
<li><p><code>fill</code>을 사용하여 부모 요소에 맞추어 이미지 크기를 암시적으로 지정한다. 이렇게 함으로써 이미지는 부모 요소를 채우도록 확장된다.</p>
</li>
</ol>
<br/>

<h3 id="💡--이미지-크기를-모르는-경우">💡  이미지 크기를 모르는 경우</h3>
<p>이미지의 크기를 알지 못하는 소스에서 이미지에 접근하는 경우 다음과 같은 방법이 있다.</p>
<ol>
<li><p><strong><code>fill</code> 사용</strong>
fill 속성을 사용하면 이미지가 부모 요소에 따라 크기가 결정된다. CSS를 사용하여 이미지의 부모 요소에 페이지에서 공간을 제공하고 <code>sizes</code> 속성을 사용하여 미디어 쿼리 브레이크 포인트와 일치하도록 할 수 있다. 또한 <code>fill</code>, <code>contain</code> 또는 <code>cover</code>와 <code>object-position</code>을 함께 사용하여 이미지가 해당 공간을 어떻게 차지할지 정의할 수 있다.</p>
</li>
<li><p><strong>이미지 정규화</strong>
제어 가능한 소스에서 이미지를 제공하는 경우 이미지를 특정 크기로 정규화하기 위해 이미지 파이프라인을 수정하는 것을 고려한다.</p>
</li>
<li><p><strong>API 호출 수정</strong>
애플리케이션이 이미지 URL을 API 호출을 통해 가져오는 경우 (예: CMS로부터), API 호출을 수정하여 URL과 함께 이미지의 크기를 반환할 수 있다.</p>
</li>
</ol>
<br/>

<p>이미지 크기 조정에 대한 제안된 방법이 모두 작동하지 않는 경우 <code>next/image</code> 컴포넌트는 표준 <code>&lt;img&gt;</code> 요소와 함께 페이지에서 잘 작동하도록 설계되었다.</p>
<br/>

<hr>
<h2 id="스타일링styling">스타일링(Styling)</h2>
<p><code>Image</code> 컴포넌트의 스타일링은 일반 <code>&lt;img&gt;</code> 요소의 스타일링과 유사하지만 몇 가지 지침을 염두에 두어야 한다.</p>
<ol>
<li><strong><code>styled-jsx</code> 대신 <code>className</code> 또는 <code>style</code>을 사용한다.</strong></li>
</ol>
<ul>
<li>대부분의 경우 <code>className</code> 속성을 사용하는 것을 권장한다. 이는 가져온 CSS 모듈, 글로벌 스타일시트 등이 될 수 있다.</li>
<li>인라인 스타일을 지정하기 위해 <code>style</code> 속성을 사용할 수도 있다.</li>
<li><code>styled-jsx</code>는 현재 컴포넌트에 대해 범위가 지정되므로 사용할 수 없다. (스타일을 전역으로 표시하지 않는 한)</li>
</ul>
<ol start="2">
<li><strong><code>fill</code>을 사용할 때, 부모 요소는 <code>position: relative</code>를 가져야 한다.</strong></li>
</ol>
<ul>
<li>이는 해당 레이아웃 모드에서 이미지 요소가 올바르게 렌더링되기 위해 필요하다.</li>
</ul>
<ol start="3">
<li><strong><code>fill</code>을 사용할 때, 부모 요소는 <code>display: block</code>을 가져야 한다.</strong></li>
</ol>
<ul>
<li>이것은 <code>&lt;div&gt;</code> 요소의 기본값이지만 그렇지 않은 경우에는 명시해야 한다.</li>
</ul>
<br/>

<hr>
<h2 id="구성configuration">구성(Configuration)</h2>
<p><code>next/image</code> 컴포넌트와 Next.js Image Optimization API는 <code>next.config.js</code> 파일에서 구성할 수 있다. 이러한 구성은 <strong>원격 이미지 사용, 사용자 정의 이미지 브레이크포인트 정의, 캐싱 동작 변경</strong> 등을 가능하게 한다.</p>
<br/>

<hr>
<p>[ 출처 ]
<a href="https://nextjs.org/docs/app/building-your-application/optimizing/images">https://nextjs.org/docs/app/building-your-application/optimizing/images</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ Optimizing ] Optimizations]]></title>
            <link>https://velog.io/@chacha_fe/Optimizing-Optimizations</link>
            <guid>https://velog.io/@chacha_fe/Optimizing-Optimizations</guid>
            <pubDate>Tue, 23 May 2023 10:54:16 GMT</pubDate>
            <description><![CDATA[<p>Next.js에는 애플리케이션의 속도와 <strong>Core Web Vitals</strong>를 개선하기 위해 설계된 다양한 내장 최적화 기능이 제공된다. </p>
<br/>

<hr>
<h2 id="내장-컴포넌트">내장 컴포넌트</h2>
<p>내장 컴포넌트는 일반적인 UI 최적화를 구현하는 복잡성을 추상화한다. 이러한 컴포넌트는 다음과 같다.</p>
<h3 id="images">Images</h3>
<p>네이티브 <code>&lt;img&gt;</code> 요소를 기반으로 구축된다. 이미지 컴포넌트는 이미지의 성능을 최적화하기 위해 지연 로딩 및 기기 크기에 따른 이미지 자동 조정을 수행한다.</p>
<h3 id="link">Link</h3>
<p>네이티브 <code>&lt;a&gt;</code> 태그를 기반으로 구축된다. 링크 컴포넌트는 페이지를 백그라운드에서 미리 가져와서 페이지 전환을 더 빠르고 부드럽게 만든다.</p>
<h3 id="scripts">Scripts</h3>
<p>네이티브 <code>&lt;script&gt;</code> 태그를 기반으로 구축된다. 스크립트 컴포넌트를 사용하면 타사 스크립트의 로딩과 실행을 제어할 수 있다.</p>
<br/>

<hr>
<h2 id="메타데이터">메타데이터</h2>
<p>메타데이터는 검색 엔진이 콘텐츠를 더 잘 이해할 수 있도록 도와준다(이는 더 좋은 SEO 결과로 이어질 수 있음). 또한, 소셜 미디어에서 콘텐츠가 어떻게 표시되는지를 사용자 정의할 수 있으며, 이를 통해 다양한 플랫폼에서 보다 매력적이고 일관된 사용자 경험을 만들 수 있다.</p>
<p>Next.js의 메타데이터 API를 사용하면 페이지의 <code>&lt;head&gt;</code> 요소를 수정할 수 있다.</p>
<p>메타데이터는 두 가지 방식으로 구성할 수 있다.</p>
<h3 id="구성-기반-메타데이터config-based-metadata">구성 기반 메타데이터(<strong>Config-based Metadata</strong>)</h3>
<p><code>layout.js</code> 또는 <code>page.js</code> 파일에서 정적 메타데이터 객체 또는 동적 <code>generateMetadata</code> 함수를 내보낸다.</p>
<h3 id="파일-기반-메타데이터">파일 기반 메타데이터</h3>
<p>경로 세그먼트에 정적 또는 동적으로 생성된 특수 파일을 추가한다.</p>
<p>또한, <strong>imageResponse</strong> 생성자를 사용하여 JSX 및 CSS로 동적 Open Graph 이미지를 생성할 수도 있다.</p>
<br/>

<hr>
<h2 id="정적-자산static-assets">정적 자산(Static Assets)</h2>
<p>Next.js의 <code>/public</code> 폴더를 사용하여 이미지, 폰트 및 기타 파일과 같은 정적 자산을 제공할 수 있다.</p>
<p><code>/public</code> 폴더 내의 파일은 CDN 공급자에 의해 <strong>캐시</strong>될 수 있으므로 효율적으로 전달된다.</p>
<br/>

<hr>
<h2 id="분석-및-모니터링">분석 및 모니터링</h2>
<p>대규모 애플리케이션의 경우, Next.js는 인기있는 분석 및 모니터링 도구와 통합되어 애플리케이션의 성능을 이해하는 데 도움을 준다. 자세한 정보는 , <a href="https://nextjs.org/docs/pages/building-your-application/optimizing/open-telemetry">OpenTelemetry</a>,  <a href="https://nextjs.org/docs/pages/building-your-application/optimizing/instrumentation">Instrumentation</a> 를 통해 확인할 수 있다.</p>
<br/>

<hr>
<p>[ 출처 ]
<a href="https://nextjs.org/docs/app/building-your-application/optimizing">https://nextjs.org/docs/app/building-your-application/optimizing</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ Styling ] Styling]]></title>
            <link>https://velog.io/@chacha_fe/Styling-Styling</link>
            <guid>https://velog.io/@chacha_fe/Styling-Styling</guid>
            <pubDate>Tue, 23 May 2023 10:52:17 GMT</pubDate>
            <description><![CDATA[<p>Next.js는 애플리케이션의 스타일링을 위해 다양한 방법을 지원한다.</p>
<ul>
<li><p><code>Global CSS</code> : 전통적인 CSS에 익숙한 사용자에게는 간단하고 익숙한 방법이지만, 애플리케이션이 커짐에 따라 CSS 번들이 커지고 스타일 관리가 어려워질 수 있다.</p>
</li>
<li><p><code>CSS Module</code> : 이름 충돌을 피하고 유지 관리성을 향상시키기 위해 로컬 범위로 제한된 CSS 클래스를 생성한다.</p>
</li>
<li><p><code>Tailwind CSS</code> : 유틸리티 기반의 CSS 프레임워크로, 유틸리티 클래스를 조합하여 빠르게 사용자 정의 디자인을 만들 수 있다.</p>
</li>
<li><p><code>Sass</code> : 변수, 중첩된 규칙, 믹스인과 같은 기능을 추가하여 CSS를 확장하는 인기 있는 CSS 전처리기다.</p>
</li>
<li><p><code>CSS-in-JS</code> : JavaScript 컴포넌트에 CSS를 직접 포함시켜 동적이고 로컬 범위의 스타일링을 가능하게 한다.</p>
</li>
</ul>
<br/>

<hr>
<h2 id="css-modules">CSS Modules</h2>
<p>Next.js는 <code>.module.css</code> 확장자를 사용하여 CSS Modules를 내장 지원한다.</p>
<p>CSS Modules는 자동으로 고유한 클래스 이름을 생성하여 <strong>CSS를 로컬 범위로 제한</strong>한다. 이를 통해 충돌 걱정 없이 다른 파일에서 동일한 클래스 이름을 사용할 수 있다. 이러한 동작으로 CSS Modules는 컴포넌트 수준의 CSS를 포함하기에 이상적인 방법이다.</p>
<br/>

<h3 id="css-modules-예시">CSS Modules 예시</h3>
<pre><code class="language-jsx">// app/dashboard/layout.tsx

import styles from &#39;./styles.module.css&#39;;

export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return &lt;section className={styles.dashboard}&gt;{children}&lt;/section&gt;;
}</code></pre>
<pre><code class="language-jsx">// app/dashboard/styles.module.css

.dashboard {
  padding: 24px;
}</code></pre>
<p>CSS Modules은 선택적인 기능이며, <code>.module.css</code> 확장자를 가진 파일에만 활성화된다. 일반 <code>&lt;link&gt;</code> 스타일시트와 글로벌 CSS 파일은 여전히 지원된다.</p>
<p>프로덕션 환경에서는 모든 CSS Module 파일이 자동으로 많은 축소 및 코드 분할된 <code>.css</code> 파일로 연결된다. 이러한 <code>.css</code> 파일은 애플리케이션에서 핫 실행 경로를 나타내며, 애플리케이션을 그리기(paint) 위해 최소한의 CSS만 로드되도록 보장한다.</p>
<br/>

<h3 id="전역-스타일">전역 스타일</h3>
<p><code>global.css</code>를 통해 전역 스타일을 적용시킬 수 있다.</p>
<pre><code class="language-jsx">// app/global.css

body {
  padding: 20px 20px 60px;
  max-width: 680px;
  margin: 0 auto;
}</code></pre>
<br/>

<p>루트 레이아웃 (<code>app/layout.js</code>) 내에서 <code>global.css</code> 스타일시트를 가져와 애플리케이션의 모든 라우트에 스타일을 적용한다.</p>
<pre><code class="language-jsx">// app/layout.tsx

// These styles apply to every route in the application
import &#39;./global.css&#39;;

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    &lt;html lang=&quot;en&quot;&gt;
      &lt;body&gt;{children}&lt;/body&gt;
    &lt;/html&gt;
  );
}</code></pre>
<br/>

<h3 id="외부-스타일-시트">외부 스타일 시트</h3>
<p>외부 패키지에서 발행한 스타일시트는 <code>app</code> 디렉토리 내의 어디에서든, 컴포넌트와 함께 사용할 수 있다.
외부 스타일시트는 npm 패키지로부터 직접 가져와야 하거나 코드베이스와 함께 다운로드하여 동일한 위치에 놓아야 한다. <code>&lt;link rel=&quot;stylesheet&quot; /&gt;</code>는 사용할 수 없다.</p>
<pre><code class="language-jsx">// app/layout.tsx

import &#39;bootstrap/dist/css/bootstrap.css&#39;;

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    &lt;html lang=&quot;en&quot;&gt;
      &lt;body className=&quot;container&quot;&gt;{children}&lt;/body&gt;
    &lt;/html&gt;
  );
}</code></pre>
<br/>

<h3 id="추가-기능">추가 기능</h3>
<p>Next.js에서는 스타일 추가 작성 환경을 개선하는 추가 기능이 포함되어 있다.</p>
<ul>
<li><p><code>next dev</code>로 로컬에서 실행할 때, 로컬 스타일시트(전역 스타일시트 또는 CSS 모듈)는 편집을 저장하는 동안 변경 사항이 즉시 반영되도록 <strong>Fast Refresh</strong>를 활용한다.</p>
</li>
<li><p><code>next build</code>로 프로덕션 빌드를 생성할 때, CSS 파일은 더 적은 수의 축소된 <code>.css</code> 파일로 번들링되어 스타일을 검색하는 데 필요한 네트워크 요청 수를 줄인다.</p>
</li>
<li><p>JavaScript를 비활성화하면 스타일은 프로덕션 빌드 (<code>next start</code>)에서도 로드된다. 그러나 Fast Refresh를 사용하려면 여전히 <code>next dev</code>에서 JavaScript가 필요하다.</p>
</li>
</ul>
<br/>

<hr>
<h2 id="tailwind-css">Tailwind CSS</h2>
<p>Tailwind CSS는 Next.js와 아주 적합한 유틸리티 우선 CSS 프레임워크이다.</p>
<br/>

<h3 id="tailwind-css-설치">Tailwind CSS 설치</h3>
<p>Tailwind CSS 패키지를 설치하고 <code>init</code> 명령을 실행하여 <code>tailwind.config.js</code> 및 <code>postcss.config.js</code> 파일을 생성한다.</p>
<pre><code class="language-jsx">npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p</code></pre>
<br/>

<h3 id="tailwind-설정">Tailwind 설정</h3>
<p><code>tailwind.config.js</code> 파일 내에서 Tailwind CSS 클래스 이름을 사용할 파일에 경로를 추가한다.</p>
<p><code>postcss.config.js</code>를 수정할 필요는 없다.</p>
<pre><code class="language-jsx">// tailwind.config.js

/** @type {import(&#39;tailwindcss&#39;).Config} */
module.exports = {
  content: [
    &#39;./app/**/*.{js,ts,jsx,tsx,mdx}&#39;, // Note the addition of the `app` directory.
    &#39;./pages/**/*.{js,ts,jsx,tsx,mdx}&#39;,
    &#39;./components/**/*.{js,ts,jsx,tsx,mdx}&#39;,

    // Or if using `src` directory:
    &#39;./src/**/*.{js,ts,jsx,tsx,mdx}&#39;,
  ],
  theme: {
    extend: {},
  },
  plugins: [],
};</code></pre>
<br/>

<h3 id="tailwind-스타일-불러오기">Tailwind 스타일 불러오기</h3>
<p>Tailwind가 생성한 스타일을 애플리케이션의 전역 스타일시트에 주입하기 위해 Tailwind가 사용할 Tailwind CSS 지시문을 추가한다.</p>
<pre><code class="language-jsx">// app/globals.css

@tailwind base;
@tailwind components;
@tailwind utilities;</code></pre>
<br/>

<p>루트 레이아웃 (<code>app/layout.tsx</code>) 내에서 <code>globals.css</code> 스타일시트를 가져와 애플리케이션의 모든 라우트에 스타일을 적용한다.</p>
<pre><code class="language-jsx">// app/layout.tsx

import type { Metadata } from &#39;next&#39;;

// These styles apply to every route in the application
import &#39;./globals.css&#39;;

export const metadata: Metadata = {
  title: &#39;Create Next App&#39;,
  description: &#39;Generated by create next app&#39;,
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    &lt;html lang=&quot;en&quot;&gt;
      &lt;body&gt;{children}&lt;/body&gt;
    &lt;/html&gt;
  );
}</code></pre>
<br/>

<h3 id="유틸리티-클래스-사용">유틸리티 클래스 사용</h3>
<pre><code class="language-jsx">export default function Page() {
  return &lt;h1 className=&quot;text-3xl font-bold underline&quot;&gt;Hello, Next.js!&lt;/h1&gt;;
}</code></pre>
<br/>

<h3 id="turbopack와-함께-사용">Turbopack와 함께 사용</h3>
<p>Next.js 13.1부터는 <strong>Turbopack</strong>을 사용하여 Tailwind CSS와 PostCSS를 지원한다.</p>
<br/>

<hr>
<h2 id="css-in-js">CSS-in-JS</h2>
<p>런타임 JavaScript가 필요한 CSS-in-JS 라이브러리는 <strong>현재 서버 컴포넌트에서 지원되지 않는다</strong>. CSS-in-JS를 사용하는 경우, 서버 컴포넌트와 스트리밍과 같은 최신 React 기능을 사용하려면 라이브러리 작성자가 최신 버전의 React를 지원해야 한다.</p>
<br/>

<p>다음 라이브러리는 앱 디렉토리의 클라이언트 컴포넌트에서 지원된다.</p>
<ul>
<li><code>styled-jsx</code></li>
<li><code>styled-components</code></li>
<li><code>tamagui</code></li>
<li><code>style9</code></li>
<li><code>vanilla-extract</code></li>
</ul>
<br/>

<p>다음은 현재 지원 작업이 진행 중인 라이브러리다.</p>
<ul>
<li><code>emotion</code></li>
<li><code>Material UI</code></li>
<li><code>Chakra UI</code></li>
</ul>
<br/>

<p>서버 컴포넌트를 스타일링하려면 <code>CSS Module</code>이나 <code>PostCSS</code> 또는 <code>Tailwind CSS</code>와 같이 CSS 파일을 출력하는 기타 솔루션을 사용하는 것을 권장한다.</p>
<br/>

<h3 id="app에서-css-in-js-구성">App에서 CSS-in-JS 구성</h3>
<p>CSS-in-JS를 구성하는 것은 세 단계의 선택적인 프로세스로 진행된다.</p>
<ol>
<li><p>렌더링 중에 모든 CSS 규칙을 수집하는 스타일 레지스트리</p>
</li>
<li><p>규칙이 사용될 가능성이 있는 콘텐츠 이전에 규칙을 삽입하는 새로운 <code>useServerInsertedHTML</code> 훅</p>
</li>
<li><p>초기 서버 사이드 렌더링 중에 앱을 스타일 레지스트리로 래핑하는 클라이언트 컴포넌트</p>
</li>
</ol>
<br/>

<h3 id="styled-jsx"><code>styled-jsx</code></h3>
<p>Client Components에서 <code>styled-jsx</code>를 사용하려면 v5.1.0을 사용해야 한다. 먼저 새로운 레지스트리를 만든다.</p>
<pre><code class="language-jsx">// app/registry.tsx

&#39;use client&#39;;

import React, { useState } from &#39;react&#39;;
import { useServerInsertedHTML } from &#39;next/navigation&#39;;
import { StyleRegistry, createStyleRegistry } from &#39;styled-jsx&#39;;

export default function StyledJsxRegistry({
  children,
}: {
  children: React.ReactNode;
}) {
  // Only create stylesheet once with lazy initial state
  // x-ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state
  const [jsxStyleRegistry] = useState(() =&gt; createStyleRegistry());

  useServerInsertedHTML(() =&gt; {
    const styles = jsxStyleRegistry.styles();
    jsxStyleRegistry.flush();
    return &lt;&gt;{styles}&lt;/&gt;;
  });

  return &lt;StyleRegistry registry={jsxStyleRegistry}&gt;{children}&lt;/StyleRegistry&gt;;
}</code></pre>
<br/>

<p>그런 다음 루트 레이아웃을 레지스트리로 래핑한다.</p>
<pre><code class="language-jsx">// app/layout.tsx

import StyledJsxRegistry from &#39;./registry&#39;;

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    &lt;html&gt;
      &lt;body&gt;
        &lt;StyledJsxRegistry&gt;{children}&lt;/StyledJsxRegistry&gt;
      &lt;/body&gt;
    &lt;/html&gt;
  );
}</code></pre>
<br/>

<h3 id="styled-components">Styled-Components</h3>
<p>다음은 <strong><code>styled-components@v6.0.0-rc.1</code></strong> 이상을 구성하는 방법의 예시다.</p>
<br/>

<p>먼저, <strong><code>styled-components</code></strong> API를 사용하여 렌더링 중에 생성된 모든 CSS 스타일 규칙을 수집하는 전역 레지스트리 컴포넌트와 해당 규칙을 반환하는 함수를 만든다. 그런 다음 <strong><code>useServerInsertedHTML</code></strong> 훅을 사용하여 레지스트리에 수집된 스타일을 루트 레이아웃의 <strong><code>&lt;head&gt;</code></strong> HTML 태그에 삽입한다.</p>
<pre><code class="language-jsx">// lib/registry.tsx

&#39;use client&#39;;

import React, { useState } from &#39;react&#39;;
import { useServerInsertedHTML } from &#39;next/navigation&#39;;
import { ServerStyleSheet, StyleSheetManager } from &#39;styled-components&#39;;

export default function StyledComponentsRegistry({
  children,
}: {
  children: React.ReactNode;
}) {
  // Only create stylesheet once with lazy initial state
  // x-ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state
  const [styledComponentsStyleSheet] = useState(() =&gt; new ServerStyleSheet());

  useServerInsertedHTML(() =&gt; {
    const styles = styledComponentsStyleSheet.getStyleElement();
    styledComponentsStyleSheet.instance.clearTag();
    return &lt;&gt;{styles}&lt;/&gt;;
  });

  if (typeof window !== &#39;undefined&#39;) return &lt;&gt;{children}&lt;/&gt;;

  return (
    &lt;StyleSheetManager sheet={styledComponentsStyleSheet.instance}&gt;
      {children}
    &lt;/StyleSheetManager&gt;
  );
}</code></pre>
<br/>

<p>루트 레이아웃의 <code>children</code> prop을 스타일 레지스트리 컴포넌트로 래핑한다.</p>
<pre><code class="language-jsx">// app/layout.tsx

import StyledComponentsRegistry from &#39;./lib/registry&#39;;

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    &lt;html&gt;
      &lt;body&gt;
        &lt;StyledComponentsRegistry&gt;{children}&lt;/StyledComponentsRegistry&gt;
      &lt;/body&gt;
    &lt;/html&gt;
  );
}</code></pre>
<br/>

<p><strong>💡  알아두면 좋은 점</strong></p>
<ul>
<li><p>서버 렌더링 중에 스타일은 전역 레지스트리로 추출되고 HTML의 <code>&lt;head&gt;</code>에 플러시된다. 이를 통해 스타일 규칙이 사용하는 콘텐츠 이전에 배치되도록 보장된다. 앞으로는 스타일을 삽입할 위치를 결정하기 위해 예정된 React 기능을 사용할 수도 있다.</p>
</li>
<li><p>스트리밍 중에는 각 청크의 스타일이 수집되어 기존 스타일에 추가된다. 클라이언트 측 수화(<code>hydration</code>)가 완료된 후, <code>styled-components</code>가 기존대로 작동하고 추가적인 동적 스타일을 삽입한다.</p>
</li>
<li><p>스타일 레지스트리에 대해 트리의 최상위에 클라이언트 컴포넌트를 사용하는 이유는 CSS 규칙을 이렇게 추출하는 것이 더 효율적이기 때문이다. 이렇게 하면 후속 서버 렌더링에서 스타일을 다시 생성하는 것을 피하고, 서버 컴포넌트 페이로드로 전송되는 것을 방지할 수 있다.</p>
</li>
</ul>
<br/>

<hr>
<p>[ 출처 ]
<a href="https://nextjs.org/docs/app/building-your-application/styling">https://nextjs.org/docs/app/building-your-application/styling</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[스코프]]></title>
            <link>https://velog.io/@chacha_fe/%EC%8A%A4%EC%BD%94%ED%94%84</link>
            <guid>https://velog.io/@chacha_fe/%EC%8A%A4%EC%BD%94%ED%94%84</guid>
            <pubDate>Fri, 19 May 2023 09:54:19 GMT</pubDate>
            <description><![CDATA[<h2 id="스코프란">스코프란?</h2>
<p>모든 식별자(변수 이름, 함수 이름, 클래스 이름 등)는 <strong>자신이 선언된 위치에 의해 다른 코드가 식별자 자신을 참조할 수 있는 유효 범위가 결정</strong>된다. 이를 스코프라 한다. 즉, <code>스코프는 식별자가 유효한 범위</code>를 말한다.</p>
<br/>

<p>아래 예제에서 자바스크립트 엔진은 이름이 같은 두 개의 변수 중에서 어떤 변수를 참조해야 할 것인지를 결정해야 한다. 이를 <code>식별자 결정</code>이라 한다. </p>
<pre><code class="language-jsx">var x = &#39;global&#39;;

function foo(){
    var x = &#39;local&#39;;
    console.log(x);
}

foo(); // &#39;local&#39;

console.log(x); // &#39;global&#39;</code></pre>
<br/>

<p>자바스크립트 엔진은 코드를 실행할 때 <code>코드의 문맥</code>을 고려한다.</p>
<p>코드가 어디서 실행되며 주변에 어떤 코드가 있는지를 <code>렉시컬 환경</code>이라 부른다. 즉, 코드의 문맥은 렉시컬 환경으로 이뤄진다. 이를 구현한 것이 <code>실행 컨텍스트</code>이며, <strong>모든 코드는 실행 컨텍스트에서 평가되고 실행된다.</strong></p>
<br/>

<hr>
<h2 id="스코프의-종류">스코프의 종류</h2>
<p>스코프는 전역과 지역으로 구분할 수 있다.</p>
<p>변수는 자신이 <strong>선언된 위치</strong>에 의해 자신이 유효한 범위인 스코프가 결정된다.</p>
<br/>

<h3 id="전역-스코프">전역 스코프</h3>
<p>전역이란 <strong>코드의 가장 바깥 영역</strong>을 말한다. 전역은 전역 스코프를 만든다. 전역에 변수를 선언하면 전역 스코프를 갖는 전역 변수가 된다. <code>전역 변수는 어디서든지 참조할 수 있다.</code></p>
<br/>

<h3 id="지역-스코프">지역 스코프</h3>
<p>지역이란 <strong>함수 몸체 내부</strong>를 말한다. 지역은 지역 스코프를 만든다. 지역에 변수를 선언하면 지역 스코프를 갖는 지역 변수가 된다. <code>지역 변수는 자신의 지역 스코프와 하위 지역 스코프에서 유효하다.</code></p>
<br/>

<hr>
<h2 id="스코프-체인">스코프 체인</h2>
<p>함수는 중첩될 수 있으므로 함수의 지역 스코프도 중첩될 수 있다. 이는 스코프가 <strong>함수의 중첩에 의해 계층적 구조</strong>를 갖는다는 것을 의미한다.</p>
<p>모든 스코프는 하나의 계층적 구조로 연결되며, 모든 지역 스코프의 최상위 스코프는 전역 스코프다.</p>
<p>이렇게 <code>스코프가 계층적으로 연결된 것을 스코프 체이닝</code>이라 한다.</p>
<br/>

<p>자바스크립트 엔진은 코드를 실행하기에 앞서 <code>렉시컬 환경</code>을 생성한다. 변수 선언이 실행되면 변수 식별자가 렉시컬 환경에 <strong>키</strong>로 등록되고, 변수 할당이 일어나면 렉시컬 환경의 변수 식별자에 해당하는 값을 변경한다.</p>
<br/>

<h3 id="스코프-체인에-의한-변수-검색">스코프 체인에 의한 변수 검색</h3>
<p>자바스크립트 엔진은 스코프 체인을 따라 변수를 참조하는 코드의 스코프에서 시작해서 상위 스코프 방향으로 이동하며 선언된 변수를 검색한다. 절대 하위 스코프로 내려가면 식별자를 검색하는 일은 없다.</p>
<p><strong>상위 스코프에서 유효한 변수는 하위 스코프에서 자유롭게 참조할 수 있지만 하위 스코프에서 유효한 변수를 상위 스코프에서 참조할 수 없다.</strong></p>
<br/>

<hr>
<h2 id="함수-레벨-스코프">함수 레벨 스코프</h2>
<p>함수 레벨 스코프는 코드 블록이 아닌 <strong>함수에 의해서만 지역 스코프가 생성</strong>된다는 의미이다.</p>
<br/>

<h3 id="블록-레벨-스코프와의-차이">블록 레벨 스코프와의 차이</h3>
<ul>
<li><p>블록 레벨 스코프는 모든 함수 몸체만이 아닌 모든 코드 블록이 지역 스코프를 생성한다.</p>
</li>
<li><p>var 키워드로 선언한 함수는 함수 레벨 스코프를 가지며 let const 키워드로 선언한 함수는 블록 레벨 스코프를 가진다.</p>
</li>
</ul>
<br/>

<hr>
<h2 id="렉시컬-스코프">렉시컬 스코프</h2>
<p>아래 예제의 실행 결과는 <code>bar</code> 함수의 상위 스코프가 무엇인지에 따라 결정된다.</p>
<pre><code class="language-jsx">var x = 1;

function foo(){
    var x = 10;
    bar();
}

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

foo();
bar();</code></pre>
<ol>
<li><strong>함수를 어디서 호출</strong>했는지에 따라 함수의 상위 스코프를 결정</li>
<li><strong>함수를 어디서 정의</strong>했는지에 따라 함수의 상위 스코프를 결정</li>
</ol>
<br/>

<h3 id="동적-스코프">동적 스코프</h3>
<p>함수가 호출되는 시점에 동적으로 상위 스코프를 결정</p>
<br/>

<h3 id="렉시컬-스코프-1">렉시컬 스코프</h3>
<p>함수 정의가 평가되는 시점에 상위 스코프가 정적으로 결정</p>
<br/>

<p>자바스크립트는 렉시컬 스코프를 따르므로 함수를 어디서 호출했는지가 아닌 <code>함수를 어디서 정의했는지에 따라 상위 스코프를 결정</code>한다. 함수가 호출된 위치는 상위 스코프 결정에 어떠한 영향도 주지 않는다.</p>
<p><code>자바스크립트에서 함수의 상위 스코프는 언제나 자신이 정의된 스코프다.</code></p>
<p>이처럼 함수의 상위 스코프는 함수 정의가 실행될 때 정적으로 결정된다. 함수 정의가 실행되어 생성된 함수 객체는 이렇게 결정된 상위 스코프를 기억한다. 함수가 호출될 때마다 함수의 상위 스코프를 참조할 필요가 있기 때문이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ Data Fetching ] Server Actions]]></title>
            <link>https://velog.io/@chacha_fe/Data-Fetching-Server-Actions</link>
            <guid>https://velog.io/@chacha_fe/Data-Fetching-Server-Actions</guid>
            <pubDate>Fri, 19 May 2023 08:55:40 GMT</pubDate>
            <description><![CDATA[<p><strong>Server Actions</strong>은 Next.js의 알파 기능으로, <strong>React Actions</strong>을 기반으로 구축되었다. 이 기능을 사용하면 서버 측 데이터 변경, 클라이언트 측 JavaScript 감소 및 점진적으로 향상된 폼을 구현할 수 있다.</p>
<pre><code class="language-jsx">import { cookies } from &#39;next/headers&#39;;

export default function AddToCart({ productId }) {
  async function addItem(data) {
    &#39;use server&#39;;

    const cartId = cookies().get(&#39;cartId&#39;)?.value;
    await saveToDb({ cartId, data });
  }

  return (
    &lt;form action={addItem}&gt;
      &lt;button type=&quot;submit&quot;&gt;Add to Cart&lt;/button&gt;
    &lt;/form&gt;
  );
}</code></pre>
<br/>

<hr>
<h1 id="server-action-규칙">Server Action 규칙</h1>
<p>Next.js 프로젝트에서 <strong>Server Actions</strong>을 사용하려면 <strong>experimental</strong> <code>serverActions</code> 플래그를 활성화해야 한다.</p>
<pre><code class="language-jsx">// next.config.js

module.exports = {
  experimental: {
    serverActions: true,
  },
};</code></pre>
<br/>

<h2 id="생성">생성</h2>
<p><code>&quot;use server&quot;</code> 지시문이 함수 본문의 맨 위에 있는 비동기 함수를 정의함으로써 Server Action을 생성할 수 있다. 이 함수는 <strong>직렬화 가능한 인수</strong>와 React Server Components 프로토콜을 기반으로 하는 <strong>직렬화 가능한 반환 값</strong>이 있어야 한다.</p>
<pre><code class="language-jsx">// app/components/component.js

async function myAction() {
  &#39;use server&#39;;
  // ...
}</code></pre>
<br/>

<p>또한 파일의 최상위에 <code>&quot;use server&quot;</code> 지시문을 사용할 수도 있다. 이는 여러 개의 Server Action을 내보내는 단일 파일이 있는 경우 유용하며, <strong>Client Component에서 Server Action을 가져오는 경우에는 필수적이다.</strong></p>
<pre><code class="language-jsx">// app/actions.js

&#39;use server&#39;;

export async function myAction() {
  // ...
}</code></pre>
<br/>

<p>최상위에  <code>&quot;use server&quot;</code> 지시문을 사용할 경우, 모든 내보내기(export)는 Server Action으로 간주된다.</p>
<br/>

<h2 id="호출">호출</h2>
<p>다음 방법을 사용하여 Server Actions를 호출할 수 있다.</p>
<ul>
<li><p><code>action</code> 사용 : React의 <code>action</code> 속성을 사용하여 <code>&lt;form&gt;</code> 요소에서 Server Action을 호출할 수 있다.</p>
</li>
<li><p><code>formAction</code> 사용 : React의 <code>formAction</code> 속성을 사용하여 <code>&lt;button&gt;</code>, <code>&lt;input type=&quot;submit&quot;&gt;</code> 및 <code>&lt;input type=&quot;image&quot;&gt;</code> 요소를 <code>&lt;form&gt;</code>에서 처리할 수 있다.</p>
</li>
<li><p><code>startTransition</code>을 사용한 사용자 정의 호출 : <code>action</code>이나 <code>formAction</code>을 사용하지 않고 <code>startTransition</code>을 사용하여 Server Actions를 호출할 수 있다. <strong>이 방법은 Progressive Enhancement를 비활성화한다.</strong></p>
</li>
</ul>
<h3 id="1-action"><code>1. action</code></h3>
<p>React의 <code>action</code> 속성을 사용하여 <code>&lt;form&gt;</code> 요소에서 Server Action을 호출할 수 있다. action 속성으로 전달된 Server Actions는 사용자 상호작용에 대한 비동기적인 부작용(asynchronous side effects)으로 작동한다.</p>
<br/>

<p>action은 HTML의 기본 action과 유사하다.</p>
<pre><code class="language-jsx">export default function AddToCart({ productId }) {
  async function addItem(data) {
    &#39;use server&#39;;

    const cartId = cookies().get(&#39;cartId&#39;)?.value;
    await saveToDb({ cartId, data });
  }

  return (
    &lt;form action={addItem}&gt;
      &lt;button type=&quot;submit&quot;&gt;Add to Cart&lt;/button&gt;
    &lt;/form&gt;
  );
}</code></pre>
<br/>

<h3 id="2-starttransition-을-사용한-사용자-지정-호출"><strong><code>2. startTransition</code> 을 사용한 사용자 지정 호출</strong></h3>
<p><code>action</code>이나 <code>formAction</code>을 사용하지 않고도 Server Actions를 호출할 수도 있다. 이를 위해 <code>useTransition</code> 훅이 제공하는 <code>startTransition</code>을 사용할 수 있으며, 이는 form, button 또는 input 외부에서 Server Actions를 사용하고자 할 때 유용하다.</p>
<br/>

<p><code>startTransition</code>을 사용하면 Progressive Enhancement 기능이 기본적으로 비활성화된다.</p>
<pre><code class="language-jsx">// app/components/example-client-component.js

&#39;use client&#39;;

import { useTransition } from &#39;react&#39;;
import { addItem } from &#39;../actions&#39;;

function ExampleClientComponent({ id }) {
  let [isPending, startTransition] = useTransition();

  return (
    &lt;button onClick={() =&gt; startTransition(() =&gt; addItem(id))}&gt;
      Add To Cart
    &lt;/button&gt;
  );
}</code></pre>
<pre><code class="language-jsx">// app/actions.js

&#39;use server&#39;;

export async function addItem(id) {
  await addItemToDb(id);
  revalidatePath(`/product/${id}`);
}</code></pre>
<br/>

<h3 id="3-starttransition-없이-사용자-지정-호출"><code>3. startTransition</code> 없이 사용자 지정 호출</h3>
<p><strong>Server Mutations</strong>을 수행하지 않는 경우, 다른 함수와 마찬가지로 함수를 직접 prop으로 전달할 수 있다.</p>
<pre><code class="language-jsx">// app/posts/[id]/page.tsx

import kv from &#39;@vercel/kv&#39;;
import LikeButton from &#39;./like-button&#39;;

export default function Page({ params }: { params: { id: string } }) {
  async function increment() {
    &#39;use server&#39;;
    await kv.incr(`post:id:${params.id}`);
  }

  return &lt;LikeButton increment={increment} /&gt;;
}</code></pre>
<pre><code class="language-jsx">// app/post/[id]/like-button.tsx

&#39;use client&#39;;

export default function LikeButton({
  increment,
}: {
  increment: () =&gt; Promise&lt;void&gt;;
}) {
  return (
    &lt;button
      onClick={async () =&gt; {
        await increment();
      }}
    &gt;
      Like
    &lt;/button&gt;
  );
}</code></pre>
<br/>

<h2 id="개선사항">개선사항</h2>
<h3 id="1-실험적인-useoptimistic">1. 실험적인 <code>useOptimistic</code></h3>
<p>실험적인 <code>useOptimistic</code> 훅은 애플리케이션에서 낙관적 업데이트를 구현하는 방법을 제공한다. 낙관적 업데이트는 앱이 더 반응적으로 보이도록하여 사용자 경험을 향상시키는 기술이다.</p>
<p>Server Action이 호출되면 응답을 기다리지 않고 예상 결과를 반영하여 UI가 즉시 업데이트된다.</p>
<pre><code class="language-jsx">&#39;use client&#39;;

import { experimental_useOptimistic as useOptimistic } from &#39;react&#39;;
import { send } from &#39;./actions.js&#39;;

export function Thread({ messages }) {
  const [optimisticMessages, addOptimisticMessage] = useOptimistic(
    messages,
    (state, newMessage) =&gt; [...state, { message: newMessage, sending: true }],
  );
  const formRef = useRef();

  return (
    &lt;div&gt;
      {optimisticMessages.map((m) =&gt; (
        &lt;div&gt;
          {m.message}
          {m.sending ? &#39;Sending...&#39; : &#39;&#39;}
        &lt;/div&gt;
      ))}
      &lt;form
        action={async (formData) =&gt; {
          const message = formData.get(&#39;message&#39;);
          formRef.current.reset();
          addOptimisticMessage(message);
          await send(message);
        }}
        ref={formRef}
      &gt;
        &lt;input type=&quot;text&quot; name=&quot;message&quot; /&gt;
      &lt;/form&gt;
    &lt;/div&gt;
  );
}</code></pre>
<br/>

<h3 id="2-실험적인-useformstatus">2. 실험적인 <code>useFormStatus</code></h3>
<p>실험적인 <code>useFormStatus</code> 훅은 Form Action 내에서 사용할 수 있으며, <code>pending</code> 속성을 제공한다.</p>
<pre><code class="language-jsx">import { experimental_useFormStatus as useFormStatus } from &#39;react-dom&#39;;

function Submit() {
  const { pending } = useFormStatus();

  return (
    &lt;input
      type=&quot;submit&quot;
      className={pending ? &#39;button-pending&#39; : &#39;button-normal&#39;}
      disabled={pending}
    &gt;
      Submit
    &lt;/input&gt;
  );
}</code></pre>
<br/>

<h3 id="3-점진적-향상--progressive-enhancement-">3. 점진적 향상 ( Progressive Enhancement )</h3>
<p>Progressive Enhancement(점진적 향상)은 JavaScript가 비활성화되었거나 JavaScript로드에 실패한 경우에도 <code>&lt;form&gt;</code>이 제대로 작동하도록 하는 기능이다. 이를 통해 JavaScript를 사용하지 않아도 사용자가 양식과 상호작용하고 데이터를 제출할 수 있다.</p>
<br/>

<p>Server Form Actions 및 Client Form Actions 모두 Progressive Enhancement를 지원하며, 다음 두 가지 전략 중 하나를 사용한다.</p>
<ul>
<li><p><strong>Server Action</strong>을 직접 <code>&lt;form&gt;</code>에 전달하면 <strong>JavaScript가 비활성화</strong>되어도 양식이 상호작용 가능하다.</p>
</li>
<li><p><strong>Client Action</strong>을 <code>&lt;form&gt;</code>에 전달하면 양식은 여전히 상호작용 가능하지만, 액션은 양식이 하이드레이션될 때까지 대기열에 추가된다. <code>&lt;form&gt;</code>은 선택적 하이드레이션으로 우선순위가 부여되므로 빠르게 진행된다.</p>
</li>
</ul>
<br/>

<pre><code class="language-jsx">&#39;use client&#39;;

import { useState } from &#39;react&#39;;
import { handleSubmit } from &#39;./actions.js&#39;;

export default function ExampleClientComponent({ myAction }) {
  const [input, setInput] = useState();

  return (
    &lt;form action={handleSubmit} onChange={(e) =&gt; setInput(e.target.value)}&gt;
      {/* ... */}
    &lt;/form&gt;
  );
}</code></pre>
<p>두 경우 모두 하이드레이션 이전에 양식은 상호작용할 수 있다. Server Action은 클라이언트 JavaScript에 의존하지 않는 추가 이점이 있지만, 상호작용성을 희생하지 않고 필요한 경우에는 여전히 Client Action과 함께 추가 동작을 구성할 수 있다.</p>
<br/>

<hr>
<h1 id="예시">예시</h1>
<h2 id="클라이언트-컴포넌트와-함께-사용">클라이언트 컴포넌트와 함께 사용</h2>
<h3 id="import">Import</h3>
<p>Server Actions는 Client Components 내에서 정의할 수 없지만, 가져올 수는 있다. Client Components에서 Server Actions를 사용하려면, 최상위에 <code>&quot;use server&quot;</code> 지시문이 포함된 파일에서 액션을 가져와야 한다.</p>
<pre><code class="language-jsx">// app/actions.js

&#39;use server&#39;;

export async function addItem() {
  // ...
}</code></pre>
<pre><code class="language-jsx">// app/components/example-client-component.js

&#39;use client&#39;;

import { useTransition } from &#39;react&#39;;
import { addItem } from &#39;../actions&#39;;

function ExampleClientComponent({ id }) {
  let [isPending, startTransition] = useTransition();

  return (
    &lt;button onClick={() =&gt; startTransition(() =&gt; addItem(id))}&gt;
      Add To Cart
    &lt;/button&gt;
  );
}</code></pre>
<br/>

<h3 id="props">Props</h3>
<p>Server Actions을 가져오는 것이 권장되지만, 경우에 따라 Server Action을 <strong>프롭(prop)</strong>으로 Client Component에 전달하고 싶을 수도 있다.</p>
<p>예를 들어, 액션 내에서 동적으로 생성된 값을 사용하고자 할 때, Server Action을 프롭으로 전달하는 것이 적절한 해결책일 수 있다.</p>
<pre><code class="language-jsx">// app/components/example-server-component.js

import { ExampleClientComponent } from &#39;./components/example-client-component.js&#39;;

function ExampleServerComponent({ id }) {
  async function updateItem(data) {
    &#39;use server&#39;;
    modifyItem({ id, data });
  }

  return &lt;ExampleClientComponent updateItem={updateItem} /&gt;;
}</code></pre>
<pre><code class="language-jsx">// app/components/example-client-component.js

&#39;use client&#39;;

function ExampleClientComponent({ updateItem }) {
  async function action(formData: FormData) {
    await updateItem(formData);
  }

  return (
    &lt;form action={action}&gt;
      &lt;input type=&quot;text&quot; name=&quot;name&quot; /&gt;
      &lt;button type=&quot;submit&quot;&gt;Update Item&lt;/button&gt;
    &lt;/form&gt;
  );
}</code></pre>
<br/>

<h3 id="온디맨드-재검증revalidation">온디맨드 재검증(revalidation)</h3>
<p>Server Action에 전달되는 데이터는 액션을 호출하기 전에 유효성 검사 또는 필터링될 수 있다.</p>
<p>예를 들어, 액션을 인자로 받아들이는 래퍼 함수를 생성하고, 유효하다면 해당 액션을 호출하는 함수를 반환하는 방식으로 구현할 수 있다.</p>
<pre><code class="language-jsx">// app/actions.js

&#39;use server&#39;;

import { withValidate } from &#39;lib/form-validation&#39;;

export const action = withValidate((data) =&gt; {
  // ...
});</code></pre>
<pre><code class="language-jsx">// lib/form-validation

export function withValidate(action) {
  return (formData: FormData) =&gt; {
    &#39;use server&#39;;

    const isValidData = verifyData(formData);

    if (!isValidData) {
      throw new Error(&#39;Invalid input.&#39;);
    }

    const data = process(formData);
    return action(data);
  };
}</code></pre>
<br/>

<h3 id="헤더-사용">헤더 사용</h3>
<p>서버 액션 내에서는 <code>쿠키</code> 및 <code>헤더</code>와 같은 수신 요청 헤더를 읽을 수 있다. 이를 통해 서버 액션 로직 내에서 요청 헤더에서 정보를 검색하고 활용할 수 있다.</p>
<pre><code class="language-jsx">import { cookies } from &#39;next/headers&#39;;

async function addItem(data) {
  &#39;use server&#39;;

  const cartId = cookies().get(&#39;cartId&#39;)?.value;

  await saveToDb({ cartId, data });
}</code></pre>
<br/>

<p>또한 서버 액션 내에서 쿠키를 수정할 수 있다.</p>
<pre><code class="language-jsx">import { cookies } from &#39;next/headers&#39;;

async function create(data) {
  &#39;use server&#39;;

  const cart = await createCart():
  cookies().set(&#39;cartId&#39;, cart.id)
  // or
  cookies().set({
    name: &#39;cartId&#39;,
    value: cart.id,
    httpOnly: true,
    path: &#39;/&#39;
  })
}</code></pre>
<br/>

<hr>
<h1 id="용어-설명">용어 설명</h1>
<h2 id="actions">Actions</h2>
<p>사용자 상호작용에 대한 비동기적인 부수효과를 수행하며, 오류 처리와 <strong>낙관적 업데이트</strong>에 대한 내장된 솔루션을 제공한다. HTML 기본 동작과 유사하다.</p>
<h2 id="form-actions">Form Actions</h2>
<p>웹 표준 <code>&lt;form&gt;</code> API에 통합된 액션으로, 기본적인 점진적 향상과 <strong>로딩 상태</strong>를 지원한다. HTML 기본 <code>formaction</code>과 유사하다.</p>
<h2 id="server-functions">Server Functions</h2>
<p>서버에서 실행되지만 클라이언트에서 호출할 수 있는 함수다.</p>
<h2 id="server-actions">Server Actions</h2>
<p>액션으로 호출되는 서버 함수다.</p>
<h3 id="server-mutations">Server Mutations</h3>
<p>데이터를 변경하고 <code>redirect</code>, <code>revalidatePath</code> 또는 <code>revalidateTag</code>를 호출하는 서버 액션이다.</p>
<br/>

<hr>
<p>[ 출처 ]
<a href="https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions">https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ Data Fetching ] Revalidating Data]]></title>
            <link>https://velog.io/@chacha_fe/Data-Fetching-Revalidating-Data</link>
            <guid>https://velog.io/@chacha_fe/Data-Fetching-Revalidating-Data</guid>
            <pubDate>Fri, 19 May 2023 08:50:33 GMT</pubDate>
            <description><![CDATA[<p>Next.js를 사용하면 전체 사이트를 <strong>다시 빌드할 필요 없이</strong> 특정 정적 경로를 업데이트할 수 있다. 재유효화(<strong>증분적 정적 재생성</strong>으로도 알려짐)를 사용하면 정적인 이점을 유지하면서 수백만 개의 페이지로 확장할 수 있다.</p>
<br/>

<p>Next.js에서는 두 가지 유형의 재유효화가 있다.</p>
<ul>
<li><p><code>Background</code> : 특정 시간 간격마다 데이터를 재유효화한다.</p>
</li>
<li><p><code>On-Demand</code> : 업데이트와 같은 이벤트에 따라 데이터를 재유효화한다.</p>
</li>
</ul>
<br/>

<hr>
<h2 id="background-revalidation">Background Revalidation</h2>
<p>특정 간격으로 캐시된 데이터를 재유효화하려면 <code>fetch()</code>에서 <code>next.revalidate</code> 옵션을 사용하여 리소스의 캐시 유효기간을 설정할 수 있다. (초 단위)</p>
<pre><code class="language-jsx">fetch(&#39;https://...&#39;, { next: { revalidate: 60 } });</code></pre>
<br/>

<p>fetch를 사용하지 않고 데이터를 재유효화하려면(fetch가 아닌 외부 패키지나 쿼리 빌더 사용) 경로 세그먼트 구성을 사용할 수 있다.</p>
<pre><code class="language-jsx">// app/page.tsx

export const revalidate = 60; // revalidate this page every 60 seconds</code></pre>
<p>fetch 외에도 <code>cache</code>를 사용하여 데이터를 재유효화할 수 있다.</p>
<br/>

<h3 id="작동-방식">작동 방식</h3>
<ol>
<li><p>빌드 시 정적으로 렌더링된 라우트에 대한 요청이 발생하면 초기에는 캐시된 데이터가 표시된다.</p>
</li>
<li><p>초기 요청 이후 60초 이전에 해당 라우트로의 모든 요청도 캐시되어 즉시 표시된다.</p>
</li>
<li><p>60초 윈도우 이후, 다음 요청은 여전히 캐시된(구식) 데이터를 표시한다.</p>
</li>
<li><p>Next.js는 백그라운드에서 데이터 재생성을 트리거한다.</p>
</li>
<li><p>라우트가 성공적으로 생성된 후, Next.js는 캐시를 무효화하고 업데이트된 라우트를 표시한다. 백그라운드 재생성이 실패한 경우, 이전 데이터는 변경되지 않은 상태로 유지된다.</p>
</li>
</ol>
<br/>

<p>생성되지 않은 라우트 세그먼트에 대한 요청이 발생하면 Next.js는 첫 번째 요청 시 동적으로 라우트를 렌더링한다. 이후 요청은 캐시로부터 정적인 라우트 세그먼트를 제공한다.</p>
<br/>



<p><strong>💡 참고 사항</strong></p>
<p>상위 데이터 provider에서 캐싱이 기본적으로 활성화되어 있는지 확인하라. 그렇지 않으면 (예: <code>useCdn: false</code>를 사용하여) 재유효화를 통해 ISR 캐시를 업데이트할 신선한 데이터를 가져올 수 없다. 캐싱은 요청되는 엔드포인트에 대해 <code>Cache-Control</code> 헤더를 반환할 때 CDN에서 발생할 수 있다. Vercel의 ISR은 캐시를 전역적으로 유지하고 롤백을 처리한다.</p>
<br/>

<hr>
<h2 id="on-demand-revalidation"><strong>On-demand Revalidation</strong></h2>
<p>60으로 재유효화 시간을 설정하면 모든 방문자는 사이트의 동일한 생성된 버전을 1분 동안 볼 수 있다. 캐시를 무효화하는 유일한 방법은 1분이 지난 후에 페이지를 방문하는 경우다.</p>
<p>Next.js 앱 라우터는 라우트나 캐시 태그를 기반으로 컨텐츠를 필요에 따라 재유효화하는 기능을 지원한다. 이를 통해 특정 fetch에 대한 Next.js 캐시를 수동으로 지울 수 있어서 다음과 같은 경우 사이트를 업데이트하기가 더욱 쉬워진다.</p>
<ul>
<li>헤드리스 CMS에서 컨텐츠가 생성되거나 업데이트되는 경우</li>
<li>전자상거래 메타데이터가 변경되는 경우(가격, 설명, 카테고리, 리뷰 등)</li>
</ul>
<br/>

<h3 id="온디맨드-재유효화-사용">온디맨드 재유효화 사용</h3>
<p>데이터는 경로(<code>revalidatePath</code>) 또는 캐시 태그(<code>revalidateTag</code>)별로 온디맨드로 재유효화될 수 있다.
<br/></p>
<p>예를 들어, 다음과 같은 fetch에서는 캐시 태그 <code>collection</code>을 추가한다.</p>
<pre><code class="language-jsx">// app/page.tsx

export default async function Page() {
  const res = await fetch(&#39;https://...&#39;, { next: { tags: [&#39;collection&#39;] } });
  const data = await res.json();
  // ...
}</code></pre>
<br/>

<p>이 캐시된 데이터는 <strong><code>Route Handler</code></strong>에서 revalidateTag를 호출하여 필요에 따라 온디맨드로 재유효화될 수 있다.</p>
<pre><code class="language-jsx">// app/api/revalidate/route.ts

import { NextRequest, NextResponse } from &#39;next/server&#39;;
import { revalidateTag } from &#39;next/cache&#39;;

export async function GET(request: NextRequest) {
  const tag = request.nextUrl.searchParams.get(&#39;tag&#39;);
  revalidateTag(tag);
  return NextResponse.json({ revalidated: true, now: Date.now() });
}</code></pre>
<br/>

<hr>
<h2 id="error-handling-and-revalidation">Error Handling and Revalidation</h2>
<p>데이터 재유효화 시도 중 오류가 발생하면 마지막으로 성공적으로 생성된 데이터가 캐시로부터 계속 제공된다. 다음 요청에서 Next.js는 데이터를 다시 재유효화하기 위해 재시도한다.</p>
<br/>

<hr>
<p>[ 출처 ]
<a href="https://nextjs.org/docs/app/building-your-application/data-fetching/revalidating">https://nextjs.org/docs/app/building-your-application/data-fetching/revalidating</a></p>
]]></description>
        </item>
    </channel>
</rss>