<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>donguraemi.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Fri, 25 Aug 2023 14:36:56 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>donguraemi.log</title>
            <url>https://velog.velcdn.com/images/kimyu0218_/profile/33a5cc41-3304-4648-a821-da9569675995/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. donguraemi.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/kimyu0218_" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Next.js] Next.js 시작하기: 4편]]></title>
            <link>https://velog.io/@kimyu0218_/NEXT.js-Next.js-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-4%ED%8E%B8</link>
            <guid>https://velog.io/@kimyu0218_/NEXT.js-Next.js-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-4%ED%8E%B8</guid>
            <pubDate>Fri, 25 Aug 2023 14:36:56 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://velog.io/@kimyu0218_/Next.js-Next.js-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-3%ED%8E%B8">Next.js 시작하기: 3편</a>에서 <code>pages</code> 사용 방법에 대해 알아보았다. 이번엔 <code>app</code>과 <code>page</code>의 차이에 대해 알아보자.</p>
</blockquote>
<p>Next.js 13.4부터는 App 라우터를 안정성 있게 사용할 수 있다. </p>
<ul>
<li><code>app/layout.js</code></li>
</ul>
<pre><code class="language-jsx">/*
    The root layout for the entire application
*/
export default function RootLayout({ children }) {
    return (
      &lt;html&gt;
        &lt;body&gt;{children}&lt;/body&gt;
      &lt;/html&gt;
    );
}</code></pre>
<p>Pages 라우터에서 컴포넌트를 한 겹 감싸기 위해서는 <code>Layout</code> 컴포넌트를 만들고 해당 컴포넌트를 불러와서 사용해야 했다. 하지만 App 라우터는 <code>Layout</code> 컴포넌트를 가져오지 않아도 자동으로 감싸진다. 레이아웃은 중첩하여 사용할 수 있다. 
<img src="https://nextjs.org/_next/image?url=%2Fdocs%2Fdark%2Fnested-layouts-ui.png&w=1920&q=75&dpl=dpl_8oMUWdKGsYCoszLjdTAqnVKazSqs" alt="nested layout"></p>
<ul>
<li><code>app/page.js</code></li>
</ul>
<pre><code class="language-jsx">export default function Page() {
    return (
      &lt;h1&gt;Hello, Next.js!&lt;/h1&gt;
    );
}</code></pre>
<p>💥<strong>app 경로 vs. page 경로</strong></p>
<table>
<thead>
<tr>
<th><code>pages</code> 디렉토리</th>
<th><code>app</code> 디렉토리</th>
<th>경로</th>
</tr>
</thead>
<tbody><tr>
<td><code>index.js</code></td>
<td><code>page.js</code></td>
<td><code>/</code></td>
</tr>
<tr>
<td><code>about.js</code></td>
<td><code>about/page.js</code></td>
<td><code>about</code></td>
</tr>
<tr>
<td><code>blog/[id].js</code></td>
<td><code>blog/[id]/page.js</code></td>
<td><code>/blog/1</code></td>
</tr>
<tr>
<td>___</td>
<td></td>
<td></td>
</tr>
<tr>
<td>#### File Conventions</td>
<td></td>
<td></td>
</tr>
<tr>
<td>Next.js 파일은 중첩된 경로에서 특정 동작으로 UI를 생성하기 위해 특수한 파일들을 사용한다.</td>
<td></td>
<td></td>
</tr>
<tr>
<td>- <code>layout</code> : Shared UI for a segment and its children</td>
<td></td>
<td></td>
</tr>
<tr>
<td>- <code>page</code> : Unique UI of a route and make routes publicly accessible</td>
<td></td>
<td></td>
</tr>
<tr>
<td>- <code>loading</code> : Loading UI for a segment and its children</td>
<td></td>
<td></td>
</tr>
<tr>
<td>- <code>not-found</code> : Not found UI for a segment and its children</td>
<td></td>
<td></td>
</tr>
<tr>
<td>- <code>error</code> : Error UI for a segment and its children</td>
<td></td>
<td></td>
</tr>
<tr>
<td>- <code>global-error</code> : Global Error UI</td>
<td></td>
<td></td>
</tr>
<tr>
<td>- <code>route</code> : Server-side API endpoint</td>
<td></td>
<td></td>
</tr>
<tr>
<td>- <code>template</code> : Specialized re-rendered Layout UI</td>
<td></td>
<td></td>
</tr>
<tr>
<td>- <code>default</code> : Fallback UI for Parallel Routes</td>
<td></td>
<td></td>
</tr>
<tr>
<td>___</td>
<td></td>
<td></td>
</tr>
<tr>
<td>### Creating UI</td>
<td></td>
<td></td>
</tr>
<tr>
<td>App 라우터는 각 경로의 segment에 대한 UI를 생성하기 위해 특수 파일 규칙을 사용한다. <code>page</code>는 경로에 고유한 UI를 표시하고, <code>layout</code>은 하위 경로에서 공유되는 UI를 표시한다.</td>
<td></td>
<td></td>
</tr>
</tbody></table>
<hr>
<h3 id="custom-app">Custom App</h3>
<p><img src="https://nextjs.org/_next/image?url=%2Fdocs%2Fdark%2Froute-segments-to-path-segments.png&w=1920&q=75&dpl=dpl_4ryqh9XkCvMMt4PqN5JwzfWqCh9e" alt="route"></p>
<p>Next.js는 폴더를 사용하여 경로를 정의하는 파일 시스템 기반 라우터를 사용한다. 각 폴더는 URL segment에 매핑된다. 중첩된 경로를 만들려면 폴더를 중첩하면 된다. 특수한 <code>page.js</code> 파일을 사용하면 경로 segment에 공개적으로 접근할 수 있다.</p>
<p><img src="https://nextjs.org/_next/image?url=%2Fdocs%2Fdark%2Fdefining-routes.png&w=1920&q=75&dpl=dpl_4ryqh9XkCvMMt4PqN5JwzfWqCh9e" alt="page file">
위의 예시에서 <code>/dashboard/analytics</code> 경로는 <code>page.js</code> 파일이 없으므로 접근할 수 없다. 반면, <code>/dashboard</code>는 <code>page.js</code>를 하위 파일로 갖고 있기 때문에 접근 가능하다.</p>
<ul>
<li>Create a shared layout between page changes</li>
<li>Inject additional data into pages</li>
<li>Add global CSS</li>
</ul>
<hr>
<h3 id="from-pages-to-app">From Pages to App</h3>
<ul>
<li>Update Next.js application from version 12 to version 13</li>
<li>Upgrade features that work in both the pages and the app directories</li>
<li>Migrate existing application from <code>pages</code> to <code>app</code></li>
</ul>
<hr>
<h4 id="upgrading-new-features">Upgrading New Features</h4>
<ul>
<li><code>&lt;Image /&gt;</code> 컴포넌트 (<code>next/image</code>)</li>
<li><code>&lt;Link /&gt;</code> 컴포넌트 (<code>next/link</code>)</li>
<li><code>&lt;Script /&gt;</code> 컴포넌트 (<code>next/script</code>)</li>
</ul>
<hr>
<h4 id="migrating-from-page-to-app">Migrating from <code>page</code> to <code>app</code></h4>
<ul>
<li><code>app</code> 디렉토리는 중첩된 경로와 레이아웃을 지원한다.</li>
<li>경로 segment를 위한 UI를 만들기 위해 특수한 file convention을 이용한다. <ul>
<li><code>page.js</code> : define UI unique to a route</li>
<li><code>layout.js</code> : define UI that is shared across multiple routes</li>
</ul>
</li>
<li><code>getServerSideProps</code>와 <code>getStaticProps</code> 같은 기존 데이터 fetching 함수들은 <code>app</code>의 새로운 api로 대체된다.</li>
<li><code>pages/_app.js</code>와 <code>pages/_document.js</code>는 <code>app/layout.js</code>의 <code>RootLayout</code>으로 대체된다.</li>
<li><code>pages/_error.js</code>는 <code>error.js</code>, <code>pages/404.js</code>는 <code>not-found.js</code>로 대체된다.</li>
</ul>
<ol>
<li><strong>Creating Root Layout</strong></li>
</ol>
<pre><code class="language-jsx">/* 
    app/layout.js
    app 내부의 모든 경로에 적용되는 루트 레이아웃
*/
export default function RootLayout({ chidren }) {
  return (
    &lt;html&gt;
      &lt;body&gt;{children}&lt;/body&gt;
    &lt;/html&gt;
  );
}</code></pre>
<ul>
<li><code>app</code> 디렉토리는 <code>RootLayout</code>을 포함해야 한다.</li>
<li><code>RootLayout</code>은 <code>&lt;html&gt;</code>과 <code>&lt;body&gt;</code> 태그를 정의해야 한다.</li>
</ul>
<ol start="2">
<li><strong>Migrating <code>next/head</code></strong></li>
</ol>
<p><code>pages</code> 디렉토리에서는 <code>next/head</code>의 리액트 컴포넌트를 사용하여 <code>&lt;title&gt;</code>이나 <code>&lt;meta&gt;</code> 같은 HTML 요소들을 포함하는 <code>&lt;head&gt;</code>를 관리했다. <code>app</code> 디렉토리에서는 built-in SEO로 대체되어 사용된다.</p>
<pre><code class="language-jsx">import { Metadata } from &#39;next&#39;;

export const metadata = {
  title: &#39;My Page Title&#39;
}

export default function Page() {
  return ...
}</code></pre>
<hr>
<h3 id="template">Template</h3>
<p>템플릿은 하위 레이아웃이나 페이지를 감싸는 레이아웃과 유사하다. 하지만 경로 전반에 걸쳐 지속되고 상태롤 유지하는 레이아웃과 달리, 템플릿은 각 하위 항목에 대해 새로운 이스턴스를 만든다. 즉, 템플릿을 공유하는 경로 사이를 왔다갔다할 때, 컴포넌트의 새 인스턴스가 마운트 되고, DOM 요소가 다시 생성되며, 상태가 유지되지 않고 효과가 다시 동기화 된다. 템플릿은 다음 사례에서 유용하게 쓰인다.</p>
<ul>
<li><code>useEffect</code>와 <code>useState</code>에 의존할 때</li>
<li>기본 프레임워크 동작을 변경할 때</li>
</ul>
<pre><code class="language-jsx">export default function Template({ children }) {
    return &lt;div&gt;{children}&lt;/div&gt;
}</code></pre>
<hr>
<h3 id="client-component-vs-server-component">Client Component vs. Server Component</h3>
<p>서버 및 클라이언트 컴포넌트를 사용하면 클라이턴트 측의 풍부한 상호작용과 기존 서버 렌더링의 향상된 성능을 결합하여 서버와 클라이언트를 포괄하는 어플리케이션을 만들 수 있다.</p>
<ul>
<li><strong>Server Component</strong></li>
</ul>
<p>서버 컴포넌트는 서버와 클라이언트를 아우르는 하이브리드 어플리케이션을 만들기 위한 모델이다. SPA처럼 리액트가 전체 어플리케이션을 클라이언트 측에서 렌더링 하는 대신, 목적에 따라 렌더링 위치를 선택할 수 있는 유연성을 제공한다.</p>
<p><img src="https://nextjs.org/_next/image?url=%2Fdocs%2Fdark%2Fthinking-in-server-components.png&w=1920&q=75&dpl=dpl_9nUD6ZVA9T2FvmdteJudYGHgiRhK" alt="example">
위와 같은 페이지가 있다고 가정해보자. 페이지를 작은 컴포넌트들로 나누었을 때, 대다수의 컴포넌트들이 상호작용을 필요로 하지 않고 서버 컴포넌트로 렌더링이 가능하다는 것을 알 수 있다. 서버 컴포넌트를 이용하면 클라이언트 컴포넌트에 비해 어떤 이점이 있을까?</p>
<p>서버 컴포넌트를 이용하면 서버 인프라를 효과적으로 활용할 수 있다. 예를 들어, 이전에 클라이언트 자바스크립트 번들 크기에 영향을 미쳤던 large dependencies가 서버에 대신 남기 대문에 성능을 향상 시킬 수 있다. 이처럼 서버 컴포넌트는 리액트 어플리케이션을 PHP, Ruby on Rails와 유사하게 느끼도록 하지만 리액트의 강력함과 유연성도 사용할 수 있다.</p>
<p>또한 서버 컴포넌트를 사용하면 초기 페이지 로드가 빨라지며 클라이언트 측의 자바스크립트 번들 크기가 줄어든다.</p>
<ul>
<li><strong>Client Component</strong></li>
</ul>
<p>클라이언트 구성 요소를 사용하면 어플리케이션에 상호작용을 추가할 수 있다. 클라이언트 컴포넌트는 페이지 라우터 방식으로 볼 수 있다. </p>
<p><code>use client</code>
<img src="https://nextjs.org/_next/image?url=%2Fdocs%2Fdark%2Fuse-client-directive.png&w=1920&q=75&dpl=dpl_9nUD6ZVA9T2FvmdteJudYGHgiRhK" alt="use client"></p>
<p>💥 <strong>언제 서버/클라이언트 컴포넌트를 사용해야 할까</strong></p>
<p>클라어언트 컴포넌트 사용 사례가 있을 때까지 서버 컴포넌트를 사용하는 게 좋다.</p>
<blockquote>
<p>클라이언트 컴포넌트 사용 사례</p>
</blockquote>
<ul>
<li>상호작용 및 이벤트 : <code>onClick()</code>, <code>onChange()</code> 등</li>
<li>상태 및 수명 주기 사용 : <code>useState()</code>, <code>useReducer()</code>, <code>useEffect()</code> 등</li>
<li>브라우저 전용 API 사용</li>
</ul>
<hr>
<h3 id="linking-and-navigating">Linking and Navigating</h3>
<p>Next.js에서 경로를 탐색하는 방법은 2가지다.</p>
<ol>
<li><code>&lt;Link&gt;</code> 컴포넌트 사용하기</li>
<li><code>useRouter</code> 훅 사용하기</li>
</ol>
<hr>
<h4 id="link-component"><code>&lt;Link&gt;</code> Component</h4>
<p><code>&lt;Link&gt;</code>는 <code>&lt;a&gt;</code> HTML 태그를 확장하여 경로 간 prefetch와 클라이언트 측 탐색을 제공하는 내장 컴포넌트다. <code>next/link</code>에서 가져와 사용하고, <code>href</code> 값을 전달한다.</p>
<pre><code class="language-jsx">/*
    Linking to dynamic routes
*/
import Link from &#39;next/link&#39;

export default function PostList({ posts }) {
  return (
    &lt;ul&gt;
      {posts.map((post) =&gt; (
        &lt;li key={post.id}&gt;
          &lt;Link href={`/posts/${post.slug}`}&gt;{post.title}&lt;/Link&gt;
        &lt;/li&gt;
      ))}
    &lt;/ul&gt;
  )
}</code></pre>
<p>💥 <strong>스크롤 위치 조작하기</strong>
Next.js의 App 라우터는 기본적으로 새 경로는 스크롤을 맨 위로 올리고, 앞뒤 탐색은 스크롤 위치를 유지한다. 특정 항목으로 스크롤하려면 url에 <code>#</code>을 추가하여 항목의 위치로 이동할 수 있다.</p>
<pre><code class="language-jsx">&lt;Link href=&#39;/dashboard#settings&#39;&gt;Settings&lt;/Link&gt;</code></pre>
<p>Next.js의 기본 동작을 비활성화 하고 싶다면 <code>scroll={flase}</code>를 전달하면 된다.</p>
<blockquote>
<p><code>useRouter</code>를 사용하는 경우</p>
</blockquote>
<pre><code class="language-jsx">import { useRouter } from &#39;next/navigation&#39;;
router.push(&#39;/dashboard&#39;, {scroll: false});</code></pre>
<hr>
<h4 id="userouter-hook"><code>useRouter</code> Hook</h4>
<p><code>useRouter</code>를 사용하면 프로그래밍 방식으로 경로를 변경할 수 있다. <code>useRouter</code>는 클라이언트 컴포넌트 내부에서 사용되며 <code>next/navigation</code>에서 가져와야 한다.</p>
<pre><code class="language-jsx">&#39;use client&#39;
import { useRouter } from &#39;next/navigation&#39;;

export default function Page() {
  const router = useRouter();
  return (
    &lt;button type=&#39;button&#39; onClick={() =&gt; router.push(&#39;/dashboard&#39;)}&gt;
      Dashboard
    &lt;/button&gt;
  );
}</code></pre>
<hr>
<h3 id="how-routing-and-navigation-works">How Routing and Navigation Works</h3>
<p>App 라우터는 하이브리드 접근 방식을 사용한다. 서버에서 어플리케이션 코드는 경로 segment별로 자동으로 코드 분할이 이루어진다. 그리고 클라이언트에서는 경로 segment를 prefetch하고 캐시한다. </p>
<p>사용자가 새 경로로 이동하면 브라우저는 페이지를 다시 로드하지 않고 변경된 경로 segment만 다시 렌더링하여 탐색 경험과 성능을 향상시킨다.</p>
<ol>
<li><strong>Prefetch</strong></li>
</ol>
<p>사용자가 방문하기 전에 백그라운드에서 경로를 미리 불러오는 것을 의미한다. Next.js에서 경로를 prefetch하는 방법은 두 가지다.</p>
<ul>
<li><code>&lt;Link&gt;</code> 컴포넌트 (경로가 사용자의 뷰포트에 표시되면 자동으로 prefetch)</li>
<li><code>useRouter</code>의 <code>router.prefetch()</code></li>
</ul>
<ol start="2">
<li><strong>Caching</strong></li>
</ol>
<p>Next.js는 라우터 캐시라 불리는 클라이언트 측 메모리 캐시가 존재한다. 사용자가 어플리케이션을 탐색할 때, 미리 가져온 경로 segment와 방문한 경로들이 저장된다. 이는 탐색 시 서버에 새로운 요청을 보내는 대신 캐시를 최대한 재사용하여 성능을 향상시킨다는 의미다.</p>
<ol start="3">
<li><strong>Partial Rendering</strong></li>
</ol>
<p>부분 렌더링은 바뀐 경로의 segment만 렌더링하는 것을 의미한다. 예를 들어 <code>/dashboard/settings</code>와 <code>/dashboard/analytics</code> 사이를 탐색한다고 가정하자. <code>settings</code>와 <code>analytics</code>는 렌더링되지만 서로 공유하는 <code>dashboard</code> 레이아웃은 보존된다. 
<img src="https://nextjs.org/_next/image?url=%2Fdocs%2Fdark%2Fpartial-rendering.png&w=1920&q=75&dpl=dpl_8oMUWdKGsYCoszLjdTAqnVKazSqs" alt="partial rendering"></p>
<ol start="4">
<li><strong>Soft Navigation</strong></li>
</ol>
<p>브라우저는 기본적으로 페이지를 다시 로드하고 어플리케이션의 <code>useState</code> 같은 상태를 초기화하며 사용자의 스크롤 위치 같은 브라우저 상태도 초기화한다. 하지만 Next.js의 App 라우터는 부드러운 탐색을 지원한다. 바뀐 부분만 렌더링하고 리액트와 브라우저 상태를 유지한다.</p>
<ol start="5">
<li><strong>Back and Forward Navigation</strong></li>
</ol>
<p>Next.js는 앞으로/뒤로 가기를 수행해도 스크롤의 위치를 유지하고 라우터 캐시의 경로 segment를 재사용한다.</p>
<hr>
<h3 id="route-group">Route Group</h3>
<p><code>app</code> 디렉토리의 하위 경로는 url 경로로 매핑된다. 하지만 특정 디렉토리명을 url에서 생략하고 싶은 경우가 생길 수 있다. 이때 경로 그룹을 사용한다. <code>(folderName)</code>
<img src="https://nextjs.org/_next/image?url=%2Fdocs%2Fdark%2Froute-group-organisation.png&w=1920&q=75&dpl=dpl_2vHERAE2YgXs7HJnyMKY1LYdJHd7" alt="route group"></p>
<p>경로 그룹은 특정 세그먼트를 레이아웃으로 선택할 수 있다. <code>shop</code>의 하위 페이지인 <code>account</code>와 <code>cart</code>는 레이아웃을 공유해야 한다고 가정하자. <code>shop</code>을 경로 그룹으로 변경하고 <code>(shop)</code>, <code>account</code>와 <code>cart</code>를 하위 디렉토리로 생성하고 그 밑에 <code>page.js</code>를 만든다. <code>account</code>와 <code>cart</code>는 파일 컨벤션에 의해 상위 레이아웃이 적용된다.
<img src="https://nextjs.org/_next/image?url=%2Fdocs%2Fdark%2Froute-group-opt-in-layouts.png&w=1920&q=75&dpl=dpl_2vHERAE2YgXs7HJnyMKY1LYdJHd7" alt="route group to layout"></p>
<hr>
<h3 id="generatestaticparams"><code>generateStaticParams</code></h3>
<p><code>generateStaticParams</code>는 동적 경로와 SSG를 함께 사용하고 싶을 때 사용한다. </p>
<p>Pages 라우터에서는 <code>getStaticPaths</code>와 <code>getStaticProps</code>를 통해 SSG를 구현했다. <code>generateStaticParams</code>는 Pages 라우터의 <code>getStaticPaths</code>를 대체하는 함수다.</p>
<pre><code class="language-jsx">export async function generateStaticParams() {
  const posts = await fetch(&#39;https://.../posts&#39;).then(res =&gt; res.json());
  return posts.map(post =&gt; ({
    id: post.id
  }));
}

export default function Post({ params }) { ... }</code></pre>
<table>
<thead>
<tr>
<th>경로</th>
<th><code>generateStaticParams</code> 반환 타입</th>
</tr>
</thead>
<tbody><tr>
<td><code>/product/[id]</code></td>
<td><code>{ id: string }[]</code></td>
</tr>
<tr>
<td><code>/products/[category]/[product]</code></td>
<td><code>{ category: string, product: string }[]</code></td>
</tr>
<tr>
<td>___</td>
<td></td>
</tr>
<tr>
<td>### Route Handlers</td>
<td></td>
</tr>
<tr>
<td>경로 핸들러는 웹 요청과 응답 api를 이용하여 사용자 경로를 만들 수 있다. 경로 핸들러는 <code>app</code> 디렉토리 내의 `route.js</td>
<td>ts<code>파일에 정의된다. 경로 핸들러는</code>app` 디렉토리에서 중첩될 수 있다.</td>
</tr>
</tbody></table>
<blockquote>
<p>경로 핸들러는 오직 <code>app</code> 디렉토리 내에서만 사용 가능하다. </p>
</blockquote>
<p><img src="https://nextjs.org/_next/image?url=%2Fdocs%2Fdark%2Froute-special-file.png&w=1920&q=75&dpl=dpl_BQJ7zGXm1cuWwSUn2Tq2rghGyJtm" alt="route handler"></p>
<pre><code class="language-jsx">/*
    app/api/route.js
*/
import { NextResponse } from &#39;next/server&#39;;

export async function GET() {
  const res = await fetch(&#39;https://...&#39;, {
    headers: {
      &#39;Content-Type&#39;: &#39;application/json&#39;,
    }
  });
  const data = await res.json();
  return NextResponse.json({data});
}</code></pre>
<hr>
<h4 id="nextresponse">NextResponse</h4>
<pre><code class="language-jsx">import { NextResponse } from &#39;next/server&#39;;</code></pre>
<ul>
<li><code>json()</code> : 주어진 JSON body로 응답 생성</li>
</ul>
<pre><code class="language-jsx">export async function GET(request) {
  return NextResponse.json({ error: &#39;Internal Server Error&#39;}, {status: 500});
}</code></pre>
<ul>
<li><code>redirect()</code></li>
</ul>
<pre><code class="language-jsx">const loginUrl = new URL(&#39;/login&#39;, request.url);
return NextReponse.redirect(loginURL);</code></pre>
<ul>
<li><code>cookies</code></li>
</ul>
<pre><code class="language-jsx">response.cookies.set(&#39;show-banner&#39;, &#39;false&#39;);</code></pre>
<hr>
<p>참고자료
<a href="https://nextjs.org/docs/pages/building-your-application/upgrading/app-router-migration">NEXT.js : app router migration</a>
<a href="https://nextjs.org/docs/app/building-your-application/routing/defining-routes">NEXT.js : defining routes</a>
<a href="nextjs.org/docs/app/building-your-application/routing/route-handlers">NEXT.js : route handlers</a>
<a href="nextjs.org/docs/app/api-reference/functions/next-response">NEXT.js : nextResponse</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js] Next.js 따라하기: 블로그]]></title>
            <link>https://velog.io/@kimyu0218_/Next.js-Next.js-%EB%94%B0%EB%9D%BC%ED%95%98%EA%B8%B0-%EB%B8%94%EB%A1%9C%EA%B7%B8</link>
            <guid>https://velog.io/@kimyu0218_/Next.js-Next.js-%EB%94%B0%EB%9D%BC%ED%95%98%EA%B8%B0-%EB%B8%94%EB%A1%9C%EA%B7%B8</guid>
            <pubDate>Fri, 25 Aug 2023 08:20:38 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>해당 프로젝트는 <a href="https://nextjs.org/learn/basics/create-nextjs-app">Next.js 튜토리얼</a>을 따라만든 것입니다. 튜토리얼의 예시코드는 <a href="https://github.com/vercel/next-learn/tree/master/basics/basics-final">여기</a>에서 확인하실 수 있습니다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/kimyu0218_/post/91fcd5af-547d-4e58-b4e0-c126727d12d3/image.png" alt="example">
튜토리얼을 따라 만들면 위와 같은 결과물을 얻을 수 있다. 헤더에는 작성자의 프로필이 나타나고 아래에 블로그 포스팅 리스트가 나열된다. 포스팅을 클릭하여 포스팅 내용을 보는 것도 가능하다.</p>
<hr>
<h3 id="프로젝트-구조">프로젝트 구조</h3>
<pre><code>├── components
│   ├── date.js
│   ├── layout.js
│   └── layout.module.css
├── lib
│   └── posts.js
├── pages
│   ├── api
│   │    └── hello.js
│   └── posts
│   │    └── [id].js
│   ├── _app.js
│   └── index.js
├── posts
│   ├── pre-rendering.md
│   └── ssg-ssr.md
├── public
│   └── images
│       └── profile.jpg
└── styles
    ├── global.css
    └── utils.module.css</code></pre><p>위의 파일들은 해당 프로젝트에서 편집하는 파일들이다. 각 파일들의 쓰임은 다음과 같다.</p>
<ul>
<li><code>components</code> : 웹 페이지에 사용되는 컴포넌트 관련 파일 저장</li>
<li><code>lib/posts.js</code> : 블로그 포스팅을 불러오는 SSG 메서드 저장</li>
<li><code>pages</code><ul>
<li><code>api</code> : api 저장</li>
<li><code>posts</code> : 특정 블로그 포스트를 클릭하면 실행되는 url로 동적 경로를 지원</li>
</ul>
</li>
<li><code>posts</code> : 블로그 포스팅 내용 저장</li>
<li><code>public</code> : 이미지 같은 정적 자산 저장</li>
<li><code>styles</code> : css 파일 저장</li>
</ul>
<hr>
<h3 id="_appjs와-globalcss"><code>_app.js</code>와 <code>global.css</code></h3>
<blockquote>
<p>모든 페이지에 공통으로 같은 CSS 파일을 적용하고 싶다면 어떻게 해야할까? 페이지마다 CSS 파일을 가져와서 사용할 수도 있지만 페이지가 많아질수록 이는 매우 귀찮은 작업이다.</p>
</blockquote>
<p><code>_app.js</code>는 어플리케이션의 모든 페이지를 감싸는 최상위 리액트 컴포넌트다. 여기에 글로벌 css 파일을 추가하면 모든 페이지에 같은 css가 적용된다.</p>
<pre><code class="language-jsx">import &#39;../styles/global.css&#39;;

export default function App({ Component, pageProps }) {
    return &lt;Component {...pageProps} /&gt;;
}</code></pre>
<hr>
<h3 id="layoutjs"><code>layout.js</code></h3>
<p><img src="https://velog.velcdn.com/images/kimyu0218_/post/fbecaa1a-bd6e-4b29-87dd-d7079fc99f0f/image.png" alt="post result">
위 사진은 메인 페이지에서 첫번째 포스트를 클릭하면 출력되는 화면이다. 메인 페이지와 마찬가지로 작성자의 프로필이 상단에 출력된다. 공통적으로 사용되는 부분을 컴포넌트로 분리하고 페이지에서 해당 컴포넌트를 가져오는 방식으로 구현하면 코드 중복을 줄이고 일관성 있게 코드를 작성할 수 있다.</p>
<pre><code class="language-jsx">import Head from &#39;next/head&#39;;
import Image from &#39;next/image&#39;;
import styles from &#39;./layout.module.css&#39;;
import utilStyles from &#39;../styles/utils.module.css&#39;;
import Link from &#39;next/link&#39;;

const name = &#39;Chae Hyungwon&#39;;
export const siteTitle = &#39;Next.js Sample Website&#39;;

export default function Layout({ children, home }) {
  return (
    &lt;div className={styles.container}&gt;
      &lt;Head&gt;...&lt;/Head&gt;
      &lt;header className={styles.header}&gt;
        {home ? (
          &lt;&gt;
            &lt;Image
              priority
              src=&quot;/images/profile.jpg&quot;
              className={utilStyles.borderCircle}
              height={144}
              width={144}
              alt=&quot;Chae Hyungwon&quot;
            /&gt;
            &lt;h1 className={utilStyles.heading2Xl}&gt;{name}&lt;/h1&gt;
          &lt;/&gt;
        ) : (
          &lt;&gt;
            &lt;Link href=&quot;/&quot;&gt;
              &lt;Image
                priority
                src=&quot;/images/profile.jpg&quot;
                className={utilStyles.borderCircle}
                height={108}
                width={108}
                alt=&quot;ChaeHyungwon&quot;
              /&gt;
            &lt;/Link&gt;
            &lt;h2 className={utilStyles.headingLg}&gt;
              &lt;Link href=&quot;/&quot; className={utilStyles.colorInherit}&gt;
                {name}
              &lt;/Link&gt;
            &lt;/h2&gt;
          &lt;/&gt;
        )}
      &lt;/header&gt;
      &lt;main&gt;{children}&lt;/main&gt;
      {!home &amp;&amp; (
        &lt;div className={styles.backToHome}&gt;
          &lt;Link href=&quot;/&quot;&gt;← Back to home&lt;/Link&gt;
        &lt;/div&gt;
      )}
    &lt;/div&gt;
  );
}</code></pre>
<p>화면에 출력되지 않는 <code>&lt;Head&gt;</code>의 내용은 생략하고 본문만 가져왔다. <code>layout.js</code>의 <code>Layout</code> 컴포넌트는 <code>children</code>과 <code>home</code>을 전달받는다.</p>
<ul>
<li><code>children</code> : 페이지의 메인 내용</li>
<li><code>home</code> : 메인 페이지 여부</li>
</ul>
<p><code>children</code>은 <code>&lt;main&gt;</code> 태그에 감싸져 출력되는 메인 콘텐츠다. <code>home</code>은 메인 페이지의 유무를 나타내는데 <code>home</code>이 전달되는 경우, 메인 페이지로 간주한다. </p>
<pre><code class="language-jsx">{home ? (
  &lt;&gt;
    &lt;Image
      priority
      src=&quot;/images/profile.jpg&quot;
      className={utilStyles.borderCircle}
      height={144}
      width={144}
      alt=&#39;profile&#39;
    /&gt;
    &lt;h1 className={utilStyles.heading2Xl}&gt;{name}&lt;/h1&gt;
  &lt;/&gt;
  ) : (
  &lt;&gt;
    &lt;Link href=&quot;/&quot;&gt;
      &lt;Image
        priority
        src=&quot;/images/profile.jpg&quot;
        className={utilStyles.borderCircle}
        height={108}
        width={108}
        alt=&#39;profile&#39;
      /&gt;
    &lt;/Link&gt;
    &lt;h2 className={utilStyles.headingLg}&gt;
      &lt;Link href=&quot;/&quot; className={utilStyles.colorInherit}&gt;{name}&lt;/Link&gt;
    &lt;/h2&gt;
  &lt;/&gt;
)}</code></pre>
<p>첫번째 삼항 연산자를 해석해보자. 메인 페이지인 경우, 프로필이 144 x 144 크기로 출력된다. 메인 페이지가 아닌 경우, 108 x 108 크기로 출력되며 클릭 시 메인 페이지로 이동한다.</p>
<pre><code class="language-jsx">{!home &amp;&amp; (
  &lt;div className={styles.backToHome}&gt;
    &lt;Link href=&quot;/&quot;&gt;← Back to home&lt;/Link&gt;
  &lt;/div&gt;
)}</code></pre>
<p>두번째 삼항 연산자를 보자. 메인 페이지가 아닌 경우, 메인 페이지로 이동하는 링크가 표시된다. </p>
<hr>
<h3 id="indexjs--블로그-메인-화면-만들기"><code>index.js</code> : 블로그 메인 화면 만들기</h3>
<p>앞에서 만든 <code>Layout</code>을 활용하여 블로그 메인 화면을 만들어보자.</p>
<pre><code class="language-jsx">import Head from &#39;next/head&#39;;
import Link from &#39;next/link&#39;;
import Layout, { siteTitle } from &#39;../components/layout&#39;;
import Date from &#39;../components/date&#39;;
import { getSortedPostsData } from &#39;../lib/posts&#39;;
import utilStyles from &#39;../styles/utils.module.css&#39;;

export default function Home({ allPostsData }) {
  return (
    &lt;Layout home&gt;
      &lt;Head&gt;
        &lt;title&gt;{siteTitle}&lt;/title&gt;
      &lt;/Head&gt;
      &lt;section className={`${utilStyles.headingMd} ${utilStyles.padding1px}`}&gt;
        &lt;h2 className={utilStyles.headingLg}&gt;Blog&lt;/h2&gt;
        &lt;ul className={utilStyles.list}&gt;
          {allPostsData.map(({ id, date, title }) =&gt; (
            &lt;li className={utilStyles.listItem} key={id}&gt;
              &lt;Link href={`/posts/${id}`}&gt;{title}&lt;/Link&gt;
              &lt;br /&gt;
              &lt;small className={utilStyles.lightText}&gt;
                &lt;Date dateString={date} /&gt;
              &lt;/small&gt;
            &lt;/li&gt;
          ))}
        &lt;/ul&gt;
      &lt;/section&gt;
    &lt;/Layout&gt;
  );
}

export async function getStaticProps() {
  const allPostsData = getSortedPostsData();
  return {
    props: {
      allPostsData
    }
  };
}</code></pre>
<p><code>Home</code> 컴포넌트는 <code>allPostsData</code>를 전달받는다. <code>allPostsData</code>는 블로그의 모든 포스팅 정보다. 데이터를 전달받으면 리스트 형식으로 출력한다.</p>
<hr>
<h3 id="postsidjs--동적-경로-만들기"><code>posts/[id].js</code> : 동적 경로 만들기</h3>
<p>벨로그의 포스팅은 <code>velog.io/@[벨로그 프로필]/[포스팅 제목]</code> 형태의 url을 갖는다. 벨로그처럼 외부 데이터를 활용하여 동적으로 경로를 생성해보자.</p>
<p><code>posts/[포스팅 id]</code> 형식의 url을 사용하려면 <code>pages</code>의 하위 디렉토리에 <code>posts</code> 디렉토리를 만들고 그 밑에 <code>[id].js</code> 파일을 만든다.</p>
<pre><code class="language-jsx">import Head from &#39;next/head&#39;;
import Layout from &#39;../../components/layout&#39;;
import Date from &#39;../../components/date&#39;;
import { getAllPostIds, getPostData } from &#39;../../lib/posts&#39;;
import utilStyles from &#39;../../styles/utils.module.css&#39;;

export default function Post({ postData }) {
    return (
    &lt;Layout&gt;
        &lt;Head&gt;
            &lt;title&gt;{postData.title}&lt;/title&gt;
        &lt;/Head&gt;
        &lt;article&gt;
            &lt;h1 className={utilStyles.headingX1}&gt;{postData.title}&lt;/h1&gt;
            &lt;div className={utilStyles.lightText}&gt;
                &lt;Date dateString={postData.date} /&gt;
            &lt;/div&gt;
            &lt;div dangerouslySetInnerHTML={{__html: postData.contentHtml}} /&gt;
        &lt;/article&gt;
    &lt;/Layout&gt;
    );
}

export async function getStaticPaths() {
    const paths = getAllPostIds();
    return {
        paths,
        fallback: false
    }
}

export async function getStaticProps({ params }) {
    const postData = await getPostData(params.id);
    return {
        props: {
            postData
        }
    }
}</code></pre>
<p><code>Home</code> 컴포넌트와 마찬가지로 <code>Layout</code> 컴포넌트를 가져와 사용한다. <code>Post</code> 컴포넌트는 특정 포스팅의 정보인 <code>postData</code>를 전달받는다.</p>
<blockquote>
<p><code>getStaticPaths</code>는 사전 렌더링할 경로를 결정한다. <code>getStaticPaths</code>는 <code>paths</code>를 <code>getStaticProps</code>로 전달한다.</p>
</blockquote>
<hr>
<h3 id="libpostsjs--포스팅-데이터-조회하기"><code>lib/posts.js</code> : 포스팅 데이터 조회하기</h3>
<p>앞에서 <code>getStaticProps</code>와 <code>getStaticPaths</code> 메서드가 나왔는데 이에 대한 설명이 없었다. 여기서 <code>getStatic...</code>에 대해 알아보자.</p>
<p><code>getStatic...</code>은 SSG 방식으로 데이터를 렌더링 한다. SSG는 빌드 시에 데이터를 렌더링하기 때문에 자주 수정되지 않는 데이터를 조회할 때 사용하면 좋다.</p>
<p><code>lib/pages</code>는 다양한 모듈을 사용한다. <code>fs</code>나 <code>path</code> 모듈의 경우 내장되어 있지만 <code>gray-matter</code>나 <code>remark</code>는 설치가 필요하다.</p>
<ul>
<li><a href="npmjs.com/package/gray-matter">gray-matter</a> 설치</li>
</ul>
<pre><code class="language-bash">npm install gray-matter</code></pre>
<ul>
<li><a href="https://www.npmjs.com/package/remark">remark</a> 설치</li>
</ul>
<pre><code class="language-bash">npm install remark</code></pre>
<blockquote>
<ul>
<li><strong>gray-matter</strong> : 문자열이나 파일의 front-matter를 파싱하는 모듈이다. 마크다운 파일로 블로그를 만들 경우 거의 필수로 사용된다.</li>
</ul>
</blockquote>
<ul>
<li><strong>remark</strong> : 마크다운 입력값을 파싱하여 마크다운을 출력한다. <code>remark-html</code>은 마크다운을 HTML로 컴파일한다.</li>
</ul>
<pre><code class="language-jsx">import fs from &#39;fs&#39;;
import path from &#39;path&#39;;
import matter from &#39;gray-matter&#39;;
import { remark } from &#39;remark&#39;;
import html from &#39;remark-html&#39;;

const postsDirectory = path.join(process.cwd(), &#39;posts&#39;);

export function getSortedPostsData() {
    const fileNames = fs.readdirSync(postsDirectory);
    const allPostsData = fileNames.map(fileName =&gt; {
        const id = fileName.replace(/\.md$/, &#39;&#39;);
        const fullPath = path.join(postsDirectory, fileName);
        const fileContents = fs.readFileSync(fullPath, &#39;utf8&#39;);

        const matterResult = matter(fileContents);
        return {
            id,
            ...matterResult.data
        };
    });
    return allPostsData.sort((a, b) =&gt; { return a.data - b.data; });
}

export function getAllPostIds() {
    const fileNames = fs.readdirSync(postsDirectory);
    return fileNames.map(fileName =&gt; {
        return {
          params: {
            id: fileName.replace(/\.md$/, &#39;&#39;)
        }
    }
  });
}

export async function getPostData(id) {
    const fullPath = path.join(postsDirectory, `${id}.md`);
    const fileContents = fs.readFileSync(fullPath, &#39;utf8&#39;);

    const matterResult = matter(fileContents);
    const processedContent = await remark().use(html).process(matterResult.content);
    const contentHtml = processedContent.toString();
    return {
      id,
      contentHtml,
      ...matterResult.data
    };
}</code></pre>
<ul>
<li><code>getSortedPostsData</code> : (in 메인 페이지) 블로그의 전체 포스팅 조회</li>
<li><code>getAllPostIds</code> : (in 포스트 페이지) 블로그의 전체 포스팅 아이디값 조회</li>
<li><code>getPostData</code> : (in 포스트 페이지) id에 해당하는 포스트 데이터 조회</li>
</ul>
<hr>
<h4 id="fallback을-사용하는-이유">Fallback을 사용하는 이유</h4>
<pre><code class="language-jsx">export async function getStaticPaths() {
    const paths = getAllPostIds();
    return {
        paths,
        fallback: false
    }
}</code></pre>
<p><code>getStaticPaths</code>는 <code>paths</code>와 함께 <code>fallback: false</code>를 반환한다. <code>fallback</code>은 뭘까?</p>
<p><code>fallback</code>은 어떤 기능이 제대로 동작되지 않았을 때 대처하는 기능이나 동작을 의미한다. <code>fallback</code>은 다음 값을 가질 수 있다.</p>
<ul>
<li><p><code>false</code> : 유효하지 않은 경로라면 404 page 반환</p>
</li>
<li><p><code>true</code> : <code>getStaticProps</code>의 기능을 다음과 같이 변경</p>
<ul>
<li><code>getStaticPaths</code>로부터 반환된 경로들이 빌드 시에 <code>getStaticProps</code>에 의해 HTML로 렌더링 된다.</li>
<li>빌드 시에 생성된 경로가 아니라면, 404 페이지를 반환하는 대신 페이지의 fallback 버전을 보여준다.</li>
<li>백그라운드에서 요청된 경로에 대해 <code>getStaticProps</code> 함수를 이용하여 HTML과 JSON 파일을 생성한다.</li>
<li>백그라운드 작업이 끝나면 요청된 경로에 해당하는 JSON 파일을 받아 새롭게 렌더링 한다.</li>
<li>새롭게 생성된 페이지를 빌드 시에 렌더링된 페이지 리스트에 추가한다. 이후 같은 경로로 온 요청에 대해 해당 페이지를 재사용한다.</li>
</ul>
</li>
<li><p><code>blocking</code> : <code>true</code>와 비슷하게 동작하지만, 빌드 시에 생성된 경로가 아니라면 fallback 상태를 보여주지 않고 SSR처럼 동작한다.</p>
</li>
</ul>
<hr>
<h4 id="date-fns--날짜-조작하기"><code>date-fns</code> : 날짜 조작하기</h4>
<p><a href="https://www.npmjs.com/package/date-fns">date-fns</a>는 날짜 형식을 조작하는 라이브러리다. 내장된 라이브러리가 아니기 때문에 별도로 설치해야 한다.</p>
<pre><code class="language-bash">npm install date-fns</code></pre>
<pre><code class="language-jsx">import { parseISO, format } from &#39;date-fns&#39;;

export default function Date({ dateString }) {
    const date = parseISO(dateString);
    return (
    &lt;time dateTime={dateString}&gt;
        {format(date, &#39;LLLL d, yyyy&#39;)}
    &lt;/time&gt;
    );
}</code></pre>
<hr>
<h3 id="api-만들기">API 만들기</h3>
<p>API는 Application Programming Interface의 줄임말로, 두 어플리케이션 간 서비스 계약으로 볼 수 있다. 요청과 응답을 사용하여 두 어플리케이션이 서로 통신하는 방법을 정의한다.</p>
<p><code>pages/api</code>의 모든 하위 파일은 <code>/api/*</code>로 매핑되어 api의 엔드포인트로 간주된다. 아주 간단한 api를 만들어보자.</p>
<pre><code class="language-jsx">/*
    /api/hello
*/
export default function handler(req, res) {
    res.status(200).json({text: &#39;Hello&#39;});
}</code></pre>
<hr>
<h3 id="vercel를-이용하여-배포하기">Vercel를 이용하여 배포하기</h3>
<p>Next.js를 배포하는 가장 간단한 방법은 Next.js 제작자가 개발한 Vercel 플랫폼을 이용하는 것이다. Vercel은 정적 및 하이브리드 어플리케이션을 위한 서버리스 플랫폼이다. </p>
<p>Vercel을 사용하기 위해서는 Vercel 계정을 만들어야 한다. Continue with Github를 선택하여 회원 가입을 진행했다. </p>
<p>다음으로 소스코드를 올린 레포지토리를 가져와야 한다. 레포지토리를 성공적으로 가져오면 configure project가 뜬다. 이는 수정하지 않고 기본값을 사용해도 된다. deploy 버튼을 누르면 다음과 같이 성공적으로 배포된 것을 확인할 수 있다. <a href="https://next-js-practice-gcpl8h6wc-kimyu0218.vercel.app/">https://next-js-practice-gcpl8h6wc-kimyu0218.vercel.app/</a>
<img src="https://velog.velcdn.com/images/kimyu0218_/post/64850997-5e2a-4251-af74-dbaca8ca62db/image.png" alt="deploy"></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js] Next.js 시작하기: 3편]]></title>
            <link>https://velog.io/@kimyu0218_/Next.js-Next.js-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-3%ED%8E%B8</link>
            <guid>https://velog.io/@kimyu0218_/Next.js-Next.js-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-3%ED%8E%B8</guid>
            <pubDate>Thu, 24 Aug 2023 10:38:08 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>웹 사이트는 일반적으로 많은 페이지들을 갖고 있다. 어플리케이션에 페이지를 추가하는 방법에 대해 알아보자.</p>
</blockquote>
<h3 id="navigate-between-pages">Navigate Between Pages</h3>
<h4 id="pages-in-nextjs">Pages in Next.js</h4>
<p>Next.js에서 페이지는 <code>pages</code> 디렉토리의 파일에서 가져온 리액트 컴포넌트다. 페이지들은 파일 이름을 기반으로 경로와 연결된다. 즉, 단순히 <code>pages</code> 디렉토리의 하위 js 파일을 만들면 그 경로가 URL 경로가 된다.</p>
<table>
<thead>
<tr>
<th>file name</th>
<th>route</th>
</tr>
</thead>
<tbody><tr>
<td><code>pages/index.js</code></td>
<td><code>/</code></td>
</tr>
<tr>
<td><code>pages/posts/first-post.js</code></td>
<td><code>posts/first-post</code></td>
</tr>
<tr>
<td><img src="https://velog.velcdn.com/images/kimyu0218_/post/28a2d0fe-134f-450a-89db-69602d4a57cd/image.png" alt="path"></td>
<td></td>
</tr>
<tr>
<td>___</td>
<td></td>
</tr>
<tr>
<td>#### Link Component</td>
<td></td>
</tr>
<tr>
<td>웹사이트의 페이지를 연결해야 할 때 우리는 <code>&lt;a&gt;</code> 태그를 사용한다. Next.js에서는 어플리케이션의 페이지들을 연결하기 위해 <code>next/link</code>의 <code>Link</code> 컴포넌트를 사용한다.</td>
<td></td>
</tr>
</tbody></table>
<blockquote>
<p><code>Link</code>는 Next.js에서 SPA를 매우 쉽게 구현할 수 있도록 도와주는 도구다. SPA는 하나의 페이지에서 모든 작업을 처리하는 앱으로, 서버로부터 가져올 데이터가 있다면 ajax 같은 방법을 이용하여 동적으로 로딩한다.</p>
</blockquote>
<pre><code class="language-jsx">import Link from &#39;next/link&#39;;

&lt;h1 className=&#39;title&#39;&gt;
  Read &lt;Link href=&#39;/post/first-post&#39;&gt;this page!&lt;/Link&gt;
&lt;/h1&gt;</code></pre>
<hr>
<h4 id="client-side-navigation">Client-Side Navigation</h4>
<p><code>Link</code> 컴포넌트는 같은 Next.js 어플리케이션의 두 페이지간 클라이언트 측 탐색을 허용한다. 클라이언트 측 탐색은 자바스크립트를 이용한 페이지 전환을 의미한다.</p>
<hr>
<h4 id="code-splitting-and-prefetching">Code splitting and prefetching</h4>
<p>Next.js는 자동으로 코드 분할을 제공한다. 따라서 페이지에 필요한 것만 불러온다. 즉. 홈페이지가 렌더링 되었을 때, 다른 페이지의 코드는 초기에 제공되지 않는다. 이는 홈페이지가 수백개의 페이지를 갖고 있어도 빠르게 로드할 수 있음을 의미한다.</p>
<p>요청한 페이지에 필요한 코드만 불러온다는 것은 페이지가 독립적이다는 것이다. 어떤 페이지가 에러를 던지더라도 어플리케이션의 나머지는 여전히 잘 작동한다.</p>
<p>또한 Next.js는 <code>Link</code> 컴포넌트가 브라우저의 뷰포트에 나타나면 자동으로 백그라운드에서 연결된 페이지에 대한 코드를 프리패치한다. 링크를 클릭하면 대상 페이지의 코드를 이미 백그라운드에서 불러왔기 때문에 페이지 전환이 즉각적으로 이루어진다.</p>
<hr>
<h3 id="assets">Assets</h3>
<p>Next.js는 최상위 레벨의 <code>public</code> 디렉토리에서 이미지 같은 정적 자산들을 제공한다. </p>
<p>일반 HTML은 다음과 같이 이미지를 추가한다.</p>
<pre><code class="language-html">&lt;img src=&#39;/images/profile.jpg&#39; alt=&#39;Kim Rina&#39; /&gt;</code></pre>
<p>하지만 이 방법은 다음을 수동적으로 처리해야 한다.</p>
<ul>
<li>다양한 화면 크기에 따라 이미지가 반응해야 한다.</li>
<li>타사 도구나 라이브러리를 이용하여 이미지를 최적화해야 한다.</li>
<li>뷰포트에 들어갈 때 이미지를 불러온다.</li>
</ul>
<p>Next.js는 이를 컨트롤할 수 있는 <code>Image</code> 컴포넌트를 제공한다.</p>
<hr>
<h4 id="image-component">Image Component</h4>
<p><code>next/image</code>는 HTML의 <code>&lt;img&gt;</code> 태그를 확장한 것으로 기본적으로 이미지 최적화를 제공한다. 이미지 크기 조정을 제공하여 뷰포트가 더 작은 장치에 큰 이미지를 전달하는 것을 막을 수 있다.</p>
<hr>
<h4 id="using-the-image-component">Using the Image Component</h4>
<p>빌드 시에 이미지를 최적화하는 대신, Next.js는 수요가 있을 때 이미지를 최적화한다. 즉, 이미지가 천만 개가 있다하더라도 빌드 시간이 늘어나지 않는다.</p>
<pre><code class="language-jsx">import Image from &#39;next/image&#39;;

const YourComponent = () =&gt; (
  &lt;Image
    src=&#39;/images/profile.jpg&#39;
    height={200}
    width={200}
    alt=&#39;Kim Rina&#39; 
  /&gt;
);</code></pre>
<hr>
<h3 id="metadata">Metadata</h3>
<p>HTML의 <code>&lt;title&gt;</code> 태그처럼 페이지의 메타데이터를 수정하고 싶다면 어떻게 해야 할까? </p>
<blockquote>
<p><code>title</code>은 웹 페이지의 제목을 나타내는 태그다. 웹 페이지 본문에는 보이지 않으며, 브라우저 탭이나 개발자 도구 등에서 확인할 수 있다.</p>
</blockquote>
<pre><code class="language-jsx">import Link from &#39;next/link&#39;;
import Head from &#39;next/head&#39;;

export default function FirstPost() {
    return (
    &lt;&gt;
        &lt;Head&gt;
            &lt;title&gt;First Post&lt;/title&gt;
        &lt;/Head&gt;
        &lt;h1&gt;First Post&lt;/h1&gt;
        &lt;h2&gt;
            &lt;Link href=&#39;/&#39;&gt;Back to home&lt;/Link&gt;
        &lt;/h2&gt;
    &lt;/&gt;
    );
}</code></pre>
<hr>
<h3 id="third-party-javascript">Third-Party JavaScript</h3>
<p>일반적으로 third-party 스크립트는 사이트에 새로운 기능을 추가하기 위해 사용한다. <code>next/script</code>는 HTML의 <code>&lt;script&gt;</code> 태그를 확장한 것으로 추가적인 스크립트를 가져오고 실행할 때 최적화된다.</p>
<pre><code class="language-jsx">import Link from &#39;next/link&#39;;
import Head from &#39;next/head&#39;;
import Script from &#39;next/script&#39;;

export default function FirstPost() {
    return (
    &lt;&gt;
        &lt;Head&gt;
            &lt;title&gt;First Post&lt;/title&gt;
        &lt;/Head&gt;
        &lt;Script
            src=&#39;https://connect.facebook.net/en_US/sdk.js&#39;
            strategy=&#39;lazyOnload&#39;
            onLoad={() =&gt; {
                console.log(&#39;script loaded&#39;);
            }}
        /&gt;
        &lt;h1&gt;First Post&lt;/h1&gt;
        &lt;h2&gt;
            &lt;Link href=&#39;/&#39;&gt;Back to home&lt;/Link&gt;
        &lt;/h2&gt;
    &lt;/&gt;
    );
}</code></pre>
<blockquote>
<p><code>strategy</code> : 언제 third-party 스크립트를 로드할지 조절한다.</p>
</blockquote>
<hr>
<h3 id="css-styling">CSS Styling</h3>
<p>파일 구조를 살펴보면 두 개의 CSS 파일을 포함한<code>styles</code> 폴더를 볼 수 있다.</p>
<ul>
<li><code>globals.css</code> : global stylesheet</li>
<li><code>Home.module.css</code> : CSS module</li>
</ul>
<hr>
<h4 id="css-modules">CSS Modules</h4>
<p>CSS 모듈은 컴포넌트 레벨의 스타일을 적용할 때 사용한다. CSS 클래스를 불러와 사용할 때, 클래스명을 고유한 이름으로 자동 변환해줌으로써 CSS 클래스명이 서로 중첩되는 현상을 방지해주는 기술이다.</p>
<p>특정 모듈만을 위한 CSS 파일 : <code>[모듈명].module.css</code></p>
<blockquote>
<p>CSS 모듈을 적용한 파일에 작성된 클래스는 아래 코드처럼 <code>styles</code> 객체를 활용하여 프로퍼티 형식을 참조할 수 있다.</p>
</blockquote>
<pre><code class="language-jsx">import styles from &#39;./layout.module.css&#39;;

export default function Layout({ children }) {
    return &lt;div className={styles.container}&gt;{children}&lt;/div&gt;
}</code></pre>
<p><img src="https://velog.velcdn.com/images/kimyu0218_/post/749b4efd-0af0-4c2d-bf62-b00194bd14e1/image.png" alt="devTools">
브라우저의 개발자 도구에서 HTML을 살펴보면 <code>Layout</code> 클래스 이름이 다음과 같은 것을 알수 있다. <code>layout_container__...</code> 
이처럼 CSS 모듈은 고유의 클래스 이름을 생성한다. 즉, CSS 모듈을 사용하면 클래스 이름 충돌에 대해 걱정할 필요가 없다.</p>
<hr>
<h4 id="global-styles">Global Styles</h4>
<p>모든 페이지에 CSS 파일을 로드하고 싶다면 어떻게 해야할까? 글로벌 CSS를 불러오려면 <code>pages/_app.js</code> 파일을 생성해야 한다.</p>
<pre><code class="language-jsx">export default function App({ Component, pageProps }) {
    return &lt;Component {...pageProps} /&gt;;
}</code></pre>
<p><code>_app.js</code>는 어플리케이션의 모든 페이지를 감싸는 최상위 리액트 컴포넌트다. Next.js는 <code>pages/_app.js</code>에 글로벌 CSS 파일을 추가하여 사용할 수 있다.</p>
<p>글로벌 CSS 파일은 어디에나 생성할 수 있고 어느 이름이나 가능하다.</p>
<blockquote>
<p><code>pages/_app.js</code>를 업데이트 할 때는 서버를 재시작해야 한다.</p>
</blockquote>
<hr>
<h4 id="using-clsx-library-to-toggle-classes">Using <code>clsx</code> library to toggle classes</h4>
<p><code>clsx</code>는 클래스 이름을 쉽게 전환할 수 있는 라이브러리다.</p>
<pre><code class="language-bash">npm install clsx</code></pre>
<p><code>success</code>와 <code>error</code> 타입을 갖는 <code>Alert</code> 컴포넌트를 만들어보자. <code>sucess</code>는 초록색 텍스트, <code>error</code>는 빨간색 텍스트를 표시한다.</p>
<pre><code class="language-css">/* alert.module.cs */
.success {
    color: green;
}
.error {
    color: red;
}</code></pre>
<pre><code class="language-jsx">import styles from &#39;./alert.module.css&#39;;
import { clsx } from &#39;clsx&#39;;

export default function Alert({ children, type }) {
  return (
    &lt;div
      className={clsx({
        [styles.success]: type === &#39;success&#39;,
        [styles.error]: type === &#39;error&#39;,
      })}
    &gt;
      {children}
    &lt;/div&gt;
  );
}</code></pre>
<hr>
<h3 id="dynamic-routes">Dynamic Routes</h3>
<p>이제 외부 데이터에 의존하는 페이지 경로를 알아보자. Next.js를 사용하면 외부 데이터에 의존하는 경로가 포함된 페이지를 정적으로 생성할 수 있다.</p>
<p><img src="https://nextjs.org/static/images/learn/dynamic-routes/page-path-external-data.png" alt="dynamic routes"></p>
<p>그림처럼 id라는 외부 데이터에 의존하는 <code>posts</code> 경로를 만들어보자. 먼저, <code>pages/posts</code>의 하위에 <code>[id].js</code> 이름의 페이지 파일을 생성한다.</p>
<pre><code class="language-jsx">export default function Post() { // post page
  return &lt;&gt; ... &lt;/&gt;
}</code></pre>
<p>다음으로 페이지와 함께 <code>getStaticPaths</code> 함수를 내보낸다. 해당 함수는 가능한 <code>id</code> 리스트를 반환한다.</p>
<pre><code class="language-jsx">export default function Post() { ... }
export async function getStaticPaths() { ... }</code></pre>
<p>마지막으로 <code>getStaticProps</code>를 구현한다. 
<a href="https://velog.io/@kimyu0218_/Next.js-Next.js-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-2%ED%8E%B8">SSG와 getStaticProps</a></p>
<pre><code class="language-jsx">export default function Post() { ... }
export async function getStaticPaths() { ... }
export async function getStaticProps({ params }) { ... }</code></pre>
<p><img src="https://nextjs.org/static/images/learn/dynamic-routes/how-to-dynamic-routes.png" alt="how to statically generate pages with dynamic routes"></p>
<hr>
<h3 id="api-routes">API Routes</h3>
<p>api 경로를 사용하면 어플리케이션 내에 api 엔드포인트를 생성할 수 있다. 이를 구현하기 위해서는 <code>pages/api</code> 디렉토리 내에 함수를 생성해야 한다. </p>
<p><code>api/hello</code>를 만들기 위해서는 <code>pages/api</code> 내부에 <code>hello.js</code> 이름의 파일을 생성한다.</p>
<pre><code class="language-jsx">export default function handler(req, res) {
  res.status(200).json({text: &#39;Hello&#39;});
}</code></pre>
<hr>
<p>참고자료
<a href="ofcourse.kr/html-course/title-%ED%83%9C%EA%B7%B8">ofcourse : title 태그</a>
<a href="https://www.tcpschool.com/react/react_styling_cssmodule">TCP School : CSS Module</a>
<a href="https://nextjs.org/learn/basics/navigate-between-pages">NEXT.js : Navigate Between Pages</a>
<a href="https://nextjs.org/learn/basics/dynamic-routes/page-path-external-data">NEXT.js : Dynamic Routes</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js] Next.js 시작하기: 2편]]></title>
            <link>https://velog.io/@kimyu0218_/Next.js-Next.js-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-2%ED%8E%B8</link>
            <guid>https://velog.io/@kimyu0218_/Next.js-Next.js-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-2%ED%8E%B8</guid>
            <pubDate>Wed, 23 Aug 2023 15:55:26 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>리액트는 어플리케이션을 구축하고 구조화하는 방법에 대해 무관심하다. Next.js는 어플리케이션을 구조화하는 프레임워크로, 개발 프로세스와 최종 어플리케이션을 더 빠르게 만들도록 최적화를 제공한다.</p>
</blockquote>
<h3 id="how-this-applies-to-nextjs">How this applies to Next.js</h3>
<p>Next.js는 어플리케이션의 development와 production 단계에 대한 기능을 모두 제공한다.</p>
<ul>
<li>Development : 개발자 경험을 개선하기 위해 TypeScript, ESLint 통합, 빠른 새로고침 등을 제공한다.</li>
<li>Production : 사용자와 어플리케이션 사용 경험을 최적화 한다. 성능과 접근성을 높이기 위해 코드 변환을 지원한다.</li>
</ul>
<p>각 환경이 서로 다른 고려사항과 목표를 갖고 있기 때문에 어플리케이션을 development 단계에서 production으로 이동하려면 수행해야 할 작업이 많다. (ex. compile, bundle, minify, code split)</p>
<hr>
<h4 id="what-is-compiling">What is Compiling?</h4>
<p><img src="https://nextjs.org/static/images/learn/foundations/compiling.png" alt="compiling">
개발자들은 JSX, TypeScript 등 개발자 친화적인 언어를 이용하여 코드를 작성한다. 이러한 언어들은 개발자의 효율성을 향상 시키지만, 브라우저가 이해할 수 있는 자바스크립트로 컴파일 되어야 한다. Next.js에서 컴파일은 코드를 편집하는 개발 단계에 속하며 빌드 단계의 일부로 실행된다.</p>
<blockquote>
<p>컴파일은 한 언어로 된 코드를 다른 언어나 해당 언어의 다른 버전으로 출력하는 프로세스를 말한다.</p>
</blockquote>
<hr>
<h4 id="what-is-minifying">What is Minifying?</h4>
<p><img src="https://nextjs.org/static/images/learn/foundations/minifying.png" alt="minifying">
개발자는 사람이 쉽게 읽을 수 있도록 최적화된 코드를 작성한다. 코드는 주석, 공백, 들여쓰기, 여러 줄 등 코드를 실행시키는 데 불필요한 정보들을 포함한다. 축소는 코드의 기능을 유지하면서 주석과 불필요한 코드 형식을 제거하는 과정이다. 즉, 파일 크기를 줄여 어플리케이션의 성능을 향상시킨다.</p>
<hr>
<h4 id="what-is-bundling">What is Bundling?</h4>
<p>개발자는 더 큰 규모의 어플리케이션을 구축하는 데 사용할 수 있는 모듈, 컴포넌트, 함수로 어플리케이션을 쪼갠다. 이러한 내부/외부 모듈과 외부 패키지를 가져오고 내보내어 파일 종속성을 가진 복잡한 웹을 생성한다. 번들링은 사용자가 웹 페이지를 방문할 때 요청하는 횟수를 줄이기 위해 파일이나 모듈을 최적화된 번들로 병합하는 것이다.</p>
<hr>
<h4 id="what-is-code-splitting">What is Code Splitting?</h4>
<p><img src="https://nextjs.org/static/images/learn/foundations/code-splitting.png" alt="code splittting">
일반적으로 개발자는 어플리케이션을 다양한 URL에서 접근할 수 있는 여러 페이지들로 나눈다. 각 페이지들은 어플리케이션의 진입점이 된다. 코드 분할은 어플리케이션 번들을 각 진입점에 필요한 더 작은 덩어리로 분할하는 과정이다. 해당 페이지를 실행시키는 데 필요한 코드만 로딩하여 어플리케이션의 초기 로딩 시간을 개선한다. Next.js는 코드 분할 기능이 내장 되어 있다. <code>pages/</code> 디렉토리 내의 각 파일은 빌드 단계에서 자동으로 자바스크립트 번들로 코드 분할된다.</p>
<ul>
<li>페이지 간에 공유되는 코드는 같은 코드를 다시 다운로드 하는 것을 방지하기 위해 또 다른 번들로 분리된다.</li>
<li>초기 페이지를 불러온 후에, 다른 페이지의 코드를 미리 로드할 수 있다.</li>
<li>동적 불러오기는 최초 로드되는 코드를 수동으로 분할하는 또 다른 방법이다.</li>
</ul>
<hr>
<h3 id="build-time-and-runtime">Build Time and Runtime</h3>
<ul>
<li><strong>Build Time</strong></li>
</ul>
<p>빌드 시간은 production을 위한 어플리케이션 코드를 준비하는 일련의 단계를 의미한다. 어플리케이션을 빌드하면 Next.js는 어플리케이션 코드를 production에 최적화된 코드로 변환한다.</p>
<ul>
<li><strong>Runtime</strong></li>
</ul>
<p>런타임은 어플리케이션이 빌드되고 배포된 후에, 사용자의 요청에 대한 응답으로 어플리케이션이 실행되는 기간을 의미한다.</p>
<hr>
<h3 id="what-is-rendering">What is Rendering?</h3>
<p>리액트에서 작성한 코드를 UI의 HTML 표현으로 변환하려면 피할 수 없는 작업이 있다. 이 프로세스를 렌더링이라고 한다. 렌더링은 서버나 클라이언트에서 발생하고, 빌드 시 미리 혹은 런타임 시 모든 요청에서 발생한다. Next.js를 사용하면 CSR, SSR, SSG의 세가지 유형의 렌더링 방법을 사용할 수 있다.</p>
<blockquote>
<p>Next.js의 장점은 페이지 단위로 가장 적합한 렌더링 방법을 선택할 수 있다는 것이다.</p>
</blockquote>
<hr>
<h4 id="pre-rendering">Pre-Rendering</h4>
<p><img src="https://nextjs.org/static/images/learn/data-fetching/pre-rendering.png" alt="pre-rendering">
<img src="https://nextjs.org/static/images/learn/foundations/pre-rendering.png" alt="pre-rendering">
Next.js는 기본적으로 모든 페이지를 사전에 렌더링 한다. 사전 렌더링은 HTML이 사전에 서버에서 생성된다. 생성된 HTML은 페이지에 필요한 최소한의 자바스크립트 코드만 가지고 있다. 페이지가 브라우저에 의해 로드되면 자바스크립트 코드가 실행되고 페이지가 상호작용할 수 있게 된다. 이 과정을 hydration이라고 한다.</p>
<p>만약 사용하고 있는 어플리케이션이 순수 리액트 어플리케이션이라면, 사전 렌더링이 일어나지 않는다.</p>
<blockquote>
<p>CSR (not Pre-rendering)<br>표준 리액트 어플리케이션에서 브라우저는 서버로부터 빈 HTML을 받는다. 초기 렌더링 작업이 사용자의 장치에서 수행되기 때문에 렌더링되기 전까지 빈 화면을 보게 된다.
<img src="https://nextjs.org/static/images/learn/foundations/client-side-rendering.png" alt="csr"></p>
</blockquote>
<hr>
<h4 id="ssr-vs-ssg">SSR vs. SSG</h4>
<p>Next.js는 두 가지 사전 렌더링 형태를 가지고 있다. 페이지를 위한 HTML이 생성될 때 일어나는 차이점을 살펴보자.</p>
<ul>
<li><strong>SSR</strong> (Server-Side Rendering)</li>
</ul>
<p><img src="https://nextjs.org/static/images/learn/data-fetching/server-side-rendering.png" alt="ssr">
SSR은 매 요청마다 HTML을 생성하는 사전 렌더링 방식이다. CSR의 경우, JSON 데이터와 자바스크립트를 사용하여 상호작용하는 컴포넌트를 만든다. 반면, SSR의 HTML은 렌더링 속도가 빠르지만 상호작용하지 않는 HTML 페이지를 표시한다. </p>
<ul>
<li><strong>SSG</strong> (Static Site Generation)</li>
</ul>
<p><img src="https://nextjs.org/static/images/learn/data-fetching/static-generation.png" alt="ssg">
SSG는 빌드 시에 HTML을 생성하는 사전 렌더링 방식이다. 콘텐츠는 빌드 시에 한 번 생성되고 CDN에 저장되어 매 요청마다 재사용된다.</p>
<blockquote>
<p><code>npm run dev</code>처럼 개발 모드에서는 모든 요청에 대해 페이지가 사전 렌더링 된다. 이는 개발을 좀 더 쉽게 하기 위함이다.</p>
</blockquote>
<p>💥 <strong>언제 SSR을, 언제 SSG를 사용하면 좋을까?</strong>
SSG는 빌드 시에 한 번 생성하여 재사용하기 때문에 서버가 모든 요청에 대해 페이지를 렌더링하는 것보다 훨씬 빠르다. 따라서 가능하면 SSG를 사용하는 것이 좋다.</p>
<p>하지만 사용자 요청에 앞서 페이지를 미리 렌더링할 수 없다면 SSG는 좋은 방법이 아니다. 예를 들어 자주 갱신되는 데이터를 표시하거나 매 요청 페이지 내용이 변경되는 경우가 있다. 이 경우에는 SSR을 사용하는 것이 좋다.</p>
<hr>
<h3 id="what-is-the-network">What is the Network?</h3>
<p>Next.js의 경우, 어플리케이션 코드를 원본 서버, CDN, Edge에 배포할 수 있다.</p>
<ul>
<li><strong>Origin Server</strong> : 어플리케이션 코드의 원본을 저장하고 실행하는 컴퓨터</li>
<li><strong>CDN</strong> (Content Delivery Network)
  클라이언트와 기본 서버 사이의 여러 위치에 HTML과 이미지 같은 정적 내용을 저장한다. 새로운 요청이 들어오면 가장 가까운 CDN에서 캐시된 결과를 전송한다. 각 요청마다 계산이 발생할 필요가 없기 때문에 origin 로드가 줄어들며 지리적으로 더 가까운 위치에서 응답이 오기 때문에 속도가 빠르다. Next.js에서는 사전 렌더링이 미리 수행될 수 있으므로 CDN은 작업의 정적 결과를 저장하는 데 매우 적합하여 콘텐츠 전달을 더 빠르게 만든다.
  <img src="https://nextjs.org/static/images/learn/foundations/cdn.png" alt="cdn"></li>
<li><strong>Edge</strong>
  Edge는 사용자와 가장 가까운 네트워크의 주변부를 의미한다. CDN과 유사하게 전 세계의 여러 위치에 배포된다. 그러나 정적 콘텐츠를 저장하는 CDN과 달리 일부 Edge는 작은 코드 조각을 실행할 수 있다.<blockquote>
<p>전통적으로 클라이언트나 서버에서 수행되었던 작업을 Edge로 이동시킴으로써 클라이언트로 전송되는 코드의 양이 줄고 사용자의 요청의 일부를 Edge가 처리하기 때문에 어플리케이션 성능의 향상으로 이어진다. Next.js는 미들웨어를 사용하여 Edge에서 코드를 실행시킬 수 있다.</p>
</blockquote>
</li>
</ul>
<hr>
<h3 id="static-generation-with-and-without-data">Static Generation with and without Data</h3>
<p>정적 생성은 데이터 유무에 관계없이 수행될 수 있다. 여기서는 외부 데이터를 가져와야 하는 경우만 살펴보겠다.</p>
<p><img src="https://nextjs.org/static/images/learn/data-fetching/static-generation-with-data.png" alt="static generation with data">
일부 페이지의 경우, 외부의 데이터를 먼저 가져오지 않으면 HTML을 렌더링하지 못한다. 예를 들면 파일 시스템에 접근하거나, 외부 api를 가져오거나, 데이터베이스에 질의해야 하는 경우가 있다.</p>
<p>Next.js에서는 <code>getStaticProps</code>를 이용하여 데이터와 함께 정적 생성이 가능하다. 페이지 컴포넌트를 내보낼 때 <code>getStaticProps</code>라는 비동기 함수도 함께 내보낼 수 있다.</p>
<pre><code class="language-jsx">export default function Home(props) { ... }

export async function getStaticProps() {
    const data = ...
    return {
      props: data
    }
}</code></pre>
<blockquote>
<p><code>getStaticProps</code>는 페이지에서만 내보낼 수 있다. 페이지가 아닌 파일에서 내보낼 수 없다.</p>
</blockquote>
<hr>
<h3 id="fetching-data-at-request-time">Fetching Data at Request Time</h3>
<p>빌드 시점이 아니라 요청 시 데이터를 가져와야 하는 경우에는 SSR을 사용한다. SSR을 사용하기 위해서는 <code>getServerSideProps</code>를 내보내야 한다.</p>
<pre><code class="language-jsx">export async function getServerSideProps(param) {
    return {
      props: ...
    }
}</code></pre>
<hr>
<p>참고자료
<a href="https://nextjs.org/learn/foundations/about-nextjs">NEXT.js : About Next.js</a>
<a href="https://nextjs.org/learn/basics/data-fetching">NEXT.js : Pre-rendering and Data Fetching</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js] Next.js 시작하기]]></title>
            <link>https://velog.io/@kimyu0218_/Next.js-Next.js-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@kimyu0218_/Next.js-Next.js-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 22 Aug 2023 12:16:14 GMT</pubDate>
            <description><![CDATA[<h3 id="spa-vs-mpa">SPA vs. MPA</h3>
<p><img src="https://asperbrothers.com/wp-content/uploads/2019/11/spa-mpa-feature-768x416.jpg" alt="SPA vs. MPA"></p>
<ul>
<li><strong>SPA</strong> (Single Page Application)</li>
</ul>
<p>하나의 웹 문서만 불러오는 웹 어플리케이션 구현이다. 서버로부터 완전히 새로운 페이지를 불러오지 않고 브라우저에서 현재의 페이지를 동적으로 다시 작성함으로써 사용자와 소통한다. 데이터를 수정하거나 조회할 때 동적으로 페이지를 구성하기 때문에 페이지가 새로고침 되지 않고 다른 페이지로 넘어가지도 않는다. Vue, Angular, React가 여기에 속한다.</p>
<ul>
<li><strong>MPA</strong> (Mutliple Page Application)</li>
</ul>
<p>여러 개의 페이지로 이루어진 전통적인 웹 어플리케이션이다. 페이지를 이동할 때마다 새로운 페이지를 요청하는데, 모든 템플릿은 서버에서 렌더링하고 완성된 페이지 형태로 클라이언트에게 응답을 전달한다. SPA와 달리 새로고침이 발생한다.</p>
<p>💥 <strong>SPA와 MPA 장단점</strong></p>
<ul>
<li><p>SPA </p>
<ul>
<li>사용자가 새 데이터를 요청할 때마다 페이지를 완전히 다시 불러오지 않기 때문에 속도가 빠르다.</li>
<li>프론트엔드와 백엔드가 강력하게 분리되어 있어 서버 측 개발자가 개발한 api를 사용하여 데이터를 전달 받는다.</li>
<li>검색엔진이 색인할 만한 콘텐츠가 존재하지 않아 검색엔진 최적화(SEO)가 어렵다. </li>
</ul>
</li>
<li><p>MPA</p>
<ul>
<li>사용자가 새 데이터를 요청할 때마다 페이지를 처음부터 다시 불러오기 때문에 속도가 느리다. <ul>
<li>프론트와 백엔드가 상호 의존적이기 때문에 하나의 프로젝트가 모든 코드를 포함된다.</li>
<li>완성된 형태의 HTML 파일을 서버로부터 전달받기 때문에 검색엔진이 페이지를 크롤링하기에 적합하다.</li>
</ul>
</li>
</ul>
</li>
</ul>
<hr>
<h3 id="csr-vs-ssr">CSR vs. SSR</h3>
<ul>
<li><p><strong>CSR</strong> (Client Side Rendering)
<img src="https://www.growth-rocket.com/wp-content/uploads/2020/07/Client-Side-Rendering-Flowchart.jpg" alt="CSR">
CSR은 페이지에 필요한 최소한의 HTML과 자바스크립트를 다운로드한다. 자바스크립트는 DOM을 갱신하고 페이지를 렌더링하는 데 사용된다. 어플리케이션이 처음 로드되면, 페이지가 완전히 로드되기 전에 사용자는 약간의 지연을 인지하게 된다. 페이지를 완전히 불러온 후, 다른 페이지로 이동할 때 전체 페이지를 refresh하지 않고 필요한 데이터를 fetch하거나 일부만 다시 렌더링하기 때문에 속도가 빠르다. </p>
<blockquote>
<p>Next.js에는 CSR을 구현하는 두 가지 방법이 존재한다.</p>
<ul>
<li>SSR 대신 리액트의 <code>useEffect()</code> 훅 사용하기</li>
<li>클라이언트에게 데이터를 fetch하기 위한 라이브러리 사용하기</li>
</ul>
</blockquote>
</li>
</ul>
<ul>
<li><strong>SSR</strong> (Server Side Rendering)
<img src="https://www.growth-rocket.com/wp-content/uploads/2020/07/Server-Side-Rendering-Flowchart.jpg" alt="SSR">
SSR은 서버에서 렌더링하여 완성된 HTML 파일을 로드한다. 클라이언트가 요청할 때마다 각 상황에 맞는 HTML 파일을 넘겨주기 때문에 여러 페이지들이 존재한다. 따라서 MPA와 밀접한 관계가 있다.</li>
</ul>
<p><img src="https://www.growth-rocket.com/wp-content/uploads/2020/07/pros-and-cons.jpg" alt="SSR vs. CSR"></p>
<hr>
<h3 id="nextjs">Next.js</h3>
<p><img src="https://nextjs.org/static/images/learn/foundations/next-app.png" alt="next.js">
React는 기본적으로 CSR 방식을 따르는데, SSR 방식을 사용하려면 개발자가 직접 개발 환경을 구성해야 한다. Next.js는 직접 환경을 구성할 필요 없이, SSR을 쉽게 사용할 수 있도록 도와주는 React 기반 프레임워크다.</p>
<blockquote>
<p>Next.js는 SPA이며 SSR을 기본적으로 사용하고, 페이지 전환 시에는 CSR을 사용한다.</p>
</blockquote>
<blockquote>
<p><strong>웹 어플리케이션 고려사항</strong></p>
</blockquote>
<ul>
<li><strong>User Interface</strong> : how users will consume and interact with your application</li>
<li><strong>Routing</strong> : how users navigate between different parts of your application</li>
<li><strong>Data Fetching</strong> : where your data lives and how to get it</li>
<li><strong>Rendering</strong> : when and where your render static of dynamic content</li>
<li><strong>Integrations</strong> : what third-party services you use and how you connect to them</li>
<li><strong>Infrastructure</strong> : where you deploy, store, and run your application code</li>
<li><strong>Performance</strong> : how to optimize your application for end-users</li>
<li><strong>Scalability</strong> : how your application adapts as your team, data, and traffic grow</li>
</ul>
<hr>
<h3 id="nextjs-개발환경-세팅하기">Next.js 개발환경 세팅하기</h3>
<ul>
<li>설치<pre><code class="language-bash">npx create-next-app@lastest .</code></pre>
</li>
<li>실행<pre><code class="language-bash">npm run dev</code></pre>
</li>
<li>빌드하고 실행 <pre><code class="language-bash">npm run build # .next 폴더에 배포 가능한 버전의 어플리케이션 생성
npm run start # 배포 버전 실행</code></pre>
</li>
</ul>
<hr>
<h3 id="nextjs-프로젝트-구조">Next.js 프로젝트 구조</h3>
<pre><code>├── public
├── app
│   ├── globals.css
│   ├── layout.js
│   └── page.js
├── jsconfig.json
├── next.config.js
├── package.json
├── postcss.config.js
└── tailwind.config.js</code></pre><ul>
<li><code>app</code> : App Router</li>
<li><code>pages</code> : Pages Router</li>
<li><code>public</code> : Static assets to be served</li>
<li><code>src</code> : Optional application source folder</li>
</ul>
<blockquote>
<ul>
<li><code>jsconfig.json</code> : Configuration file for Javascript</li>
<li><code>next.config.js</code> : Configuration file for Next.js</li>
<li><code>package.json</code> : Project dependencies and scripts</li>
<li><code>postcss.config.js</code> : Configuration file for Tailwind CSS</li>
</ul>
</blockquote>
<hr>
<p>참고자료
<a href="https://ko.wikipedia.org/wiki/%EC%8B%B1%EA%B8%80_%ED%8E%98%EC%9D%B4%EC%A7%80_%EC%95%A0%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98">wikipedia : SPA</a>
<a href="https://nextjs.org/docs/pages/building-your-application/rendering/client-side-rendering">NEXT.js : CSR</a>
<a href="https://nextjs.org/docs/pages/building-your-application/rendering/server-side-rendering">NEXT.js : SSR</a>
<a href="https://hanamon.kr/spa-mpa-ssr-csr-%EC%9E%A5%EB%8B%A8%EC%A0%90-%EB%9C%BB%EC%A0%95%EB%A6%AC/">SPA vs. MPA &amp; SSR vs. CSR</a>
<a href="https://nextjs.org/docs">NEXT.js : What is Next.js</a>
<a href="https://nextjs.org/docs/getting-started/project-structure">NEXT.js : Next.js Project Structure</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 리액트 따라하기: 틱택토]]></title>
            <link>https://velog.io/@kimyu0218_/React-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%94%B0%EB%9D%BC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@kimyu0218_/React-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%94%B0%EB%9D%BC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 20 Aug 2023 20:24:35 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>해당 프로젝트는 <a href="https://react.dev/learn/tutorial-tic-tac-toe">틱택토 튜토리얼</a>를 따라만든 것입니다.</p>
</blockquote>
<h3 id="overview">Overview</h3>
<p>로컬 환경에서 리액트 앱을 생성하든 온라인 편집기를 이용하든 초기 프로젝트는 다음과 같은 구조를 갖는다.</p>
<pre><code>├── public
│   └── index.html
├── src
│   ├── App.js
│   ├── index.js
│   └── styles.css
└── package.json</code></pre><ul>
<li><code>App.js</code></li>
</ul>
<p><code>App.js</code>는 컴포넌트를 생성하는 코드를 포함한다. 리액트에서 컴포넌트는 UI의 일부를 나타내고 재사용이 가능한 코드다. 컴포넌트는 어플리케이션에 UI 요소를 렌더링하고, 관리하며 업데이트 하는 데 사용된다.</p>
<pre><code class="language-jsx">export default function Square() {
    return &lt;button className=&#39;square&#39;&gt;X&lt;/button&gt;
}</code></pre>
<blockquote>
<ul>
<li><code>export</code> : 자바스크립트의 키워드로 파일 외부에서 해당 함수에 접근하는 것을 허용한다.</li>
</ul>
</blockquote>
<ul>
<li><code>default</code> : <code>App.js</code>의 파일의 메인 함수가 <code>Square()</code>임을 알린다.</li>
</ul>
<ul>
<li><p><code>styles.css</code>
<code>styles.css</code>는 어플리케이션의 스타일을 정의한 파일이다.</p>
</li>
<li><p><code>index.js</code>
<code>index.js</code>는 <code>App.js</code>에서 만든 컴포넌트와 웹 브라우저를 잇는 다리 역할을 한다. 모든 요소들을 모아 최종 산출물을 만들고 <code>public/index.html</code>에 최종 산출물을 반영한다.</p>
<pre><code class="language-jsx">import React, { StrictMode } from &#39;react&#39;;
import { createRoot } from &#39;react-dom/client&#39;;
</code></pre>
</li>
</ul>
<p>import App from &#39;./App&#39;;</p>
<p>const rootElement = document.getElementById(&#39;root&#39;);
const root = createRoot(rootElement);</p>
<p>root.render(
  <StrictMode>
    <App />
  </StrictMode>
);</p>
<pre><code>___
### 틱택토 보드판 만들기
&gt; 틱택토 보드판을 만들기 전에 먼저 예시코드의 `styles.css` 내용을 복붙하여 사용하는 것을 추천한다.

![result](https://velog.velcdn.com/images/kimyu0218_/post/0f54c966-83ca-47c8-b753-a06905984f0c/image.png)
위의 사진은 최종 산출물을 캡처한 것이다. 보드판은 총 9개의 정사각형 버튼이 필요하다.

1. 보드판 틀 만들기
```jsx
import React from &#39;react&#39;;

function Square({ value }) {
  return &lt;button className=&quot;square&quot;&gt;{value}&lt;/button&gt;;
}

export default function Board() {
  return (
    &lt;&gt;
      &lt;div className=&quot;board-row&quot;&gt;
        &lt;Square value=&quot;1&quot; /&gt;
        &lt;Square value=&quot;2&quot; /&gt;
        &lt;Square value=&quot;3&quot; /&gt;
      &lt;/div&gt;
      &lt;div className=&quot;board-row&quot;&gt;
        &lt;Square value=&quot;4&quot; /&gt;
        &lt;Square value=&quot;5&quot; /&gt;
        &lt;Square value=&quot;6&quot; /&gt;
      &lt;/div&gt;
      &lt;div className=&quot;board-row&quot;&gt;
        &lt;Square value=&quot;7&quot; /&gt;
        &lt;Square value=&quot;8&quot; /&gt;
        &lt;Square value=&quot;9&quot; /&gt;
      &lt;/div&gt;
    &lt;/&gt;
  );
}</code></pre><p>하위 컴포넌트인 <code>Square</code>는 부모 컴포넌트로부터 <code>value</code>를 전달받아 해당 값을 버튼에 표기한다. 부모 컴포넌트인 <code>Board</code>는 9개의 버튼을 포함한다.</p>
<ol start="2">
<li>상호작용하는 버튼 만들기</li>
</ol>
<p>첫번째 산출물은 보드판의 칸에 고유 번호를 붙인 것이다. 하지만 실제 틱택토 게임에서는 다른 로직을 사용해야 한다. 초기 상태의 버튼은 아무 내용을 가지고 있지 않다가 사용자가 클릭하면 X 상태로 표기한다. 이벤트와 <code>useState</code>를 사용하여 컴포넌트의 상태를 변경해보자.</p>
<ul>
<li>버튼에 <code>onClick</code> 이벤트 추가하기</li>
</ul>
<pre><code class="language-jsx">// (사용자가 버튼을 클릭하면 handleClick 함수가 실행된다)
&lt;button className=&quot;square&quot; onClick={handleClick}&gt;
  {value}
&lt;/button&gt;</code></pre>
<ul>
<li>이벤트가 발생하면 컴포넌트의 상태 변경하기<pre><code class="language-jsx">const [value, setValue] = useState(null);
function handleClick() {
setValue(&#39;X&#39;);
}</code></pre>
<blockquote>
<p><code>useState</code>는 컴포넌트가 정보를 기억하도록 만든다. <code>value</code>는 값을 저장하는 변수고 <code>setValue</code>는 <code>value</code>를 변경하는 함수다.</p>
</blockquote>
</li>
</ul>
<pre><code class="language-jsx">import React, { useState } from &#39;react&#39;;

function Square() {
  const [value, setValue] = useState(null);
  function handleClick() {
    setValue(&#39;X&#39;);
  }
  return (
    &lt;button className=&quot;square&quot; onClick={handleClick}&gt;
      {value}
    &lt;/button&gt;
  );
}

export default function Board() {
  return (
    &lt;&gt;
      &lt;div className=&quot;board-row&quot;&gt;
        &lt;Square /&gt;
        &lt;Square /&gt;
        &lt;Square /&gt;
      &lt;/div&gt;
      &lt;div className=&quot;board-row&quot;&gt;
        &lt;Square /&gt;
        &lt;Square /&gt;
        &lt;Square /&gt;
      &lt;/div&gt;
      &lt;div className=&quot;board-row&quot;&gt;
        &lt;Square /&gt;
        &lt;Square /&gt;
        &lt;Square /&gt;
      &lt;/div&gt;
    &lt;/&gt;
  );
}
</code></pre>
<p><img src="https://velog.velcdn.com/images/kimyu0218_/post/d1346d65-4d52-4b46-9b42-16e3f4868304/image.png" alt="board"></p>
<hr>
<h3 id="틱택토-완성하기">틱택토 완성하기</h3>
<p>이전 단계에서 틱택토 보드판을 완성했다. 게임을 완성하기 위해서 O/X를 번갈아서 배치하고 승자를 결정하는 방법이 필요하다.</p>
<ol>
<li>보드판에서 상태 관리하기 </li>
</ol>
<p>각 <code>Square</code> 컴포넌트는 상태를 가지고 있다. 틱택토 게임의 승자를 확인하기 위해서는 <code>Board</code>가 9개의 자식 컴포넌트의 상태를 알아야 한다.</p>
<p><code>Board</code>가 <code>Square</code>의 정보를 조회하는 방법에 대해 고민해보자. 첫번째 방법은 <code>Board</code>가 <code>Square</code>의 상태를 물어보는 것이다. 하지만 해당 방법은 코드가 복잡해지는 단점이 있다. 두번째 방법은 <code>Square</code> 대신 <code>Board</code>에 상태 정보를 저장하는 것이다. <code>Board</code>가 <code>Square</code>의 상태 정보를 저장하고 <code>Square</code>에 prop으로 화면에 표시할 내용을 전달한다.</p>
<ul>
<li><code>Board</code>에서 <code>Square</code> 상태 물어보기</li>
<li><code>Board</code>가 <code>Square</code>의 상태 정보를 저장하고 <code>Square</code>에 props 전달하기</li>
</ul>
<p>해당 프로젝트에서 선택한 방식은 후자다. <code>Square</code>에 있는 <code>useState</code>를 삭제하고 props로 정보를 받아오도록 수정해보자.</p>
<pre><code class="language-jsx">function Square({ value, onSquareClick }) {
  return (
    &lt;button className=&quot;square&quot; onClick={onSquareClick}&gt;
      {value}
    &lt;/button&gt;
  );
}</code></pre>
<p><code>value</code>는 화면에 표시하는 값, <code>onSquareClick</code>은 해당 버튼을 클릭하면 발생하는 이벤트다.</p>
<p>이제 부모 컨포넌트인 <code>Board</code>를 수정해보자. 우선 자식 컨포넌트인 <code>Square</code>의 정보를 저장해야 한다.</p>
<pre><code class="language-jsx">const [squares, setSquares] = useState(Array(9).fill(null));</code></pre>
<p>다음으로 <code>Square</code>가 클릭되었을 때 발생해야 하는 이벤트를 작성한다. <code>idx</code>번째 칸을 클릭하면 상태를 변경한다. <code>setSquares</code> 함수는 리엑트에게 컴포넌트 상태가 변경되었다는 것을 알려주고, 리렌더링을 실행한다.</p>
<pre><code class="language-jsx">function handleClick(idx) {
  const nextSquares = squares.slice();
  nextSquares[idx] = &#39;X&#39;;
  setSquares(nextSquares);
}</code></pre>
<blockquote>
<p><strong>불변성이 중요한 이유</strong><br>
위의 코드에서 <code>.slice()</code> 메서드로 <code>squares</code>의 사본을 만들어 사용하고 있다. <code>squares[idx] = &#39;X&#39;</code>처럼 원본을 바로 변경하지 않고 사본을 만드는 이유는 무엇일까?<br>
첫째, 불변성은 복잡한 기능을 보다 쉽게 구현할 수 있도록 한다. 나중에 게임 히스토리를 구현하여 과거 동작으로 되돌리는 기증을 만들 것이다. 원본을 직접 수정하지 않으면 이전 데이터 버전을 저장할 수 있기 때문에 나중에 재사용하기 편리하다.
둘째, 불변성은 렌더링 비용을 줄일 수 있다. 기본적으로 모든 하위 컴포넌트는 상위 컴포넌트가 렌더링 되면 자동으로 렌더링 된다. 하지만 자식 컴포넌트 중에 변화되지 않은 컴포넌트들도 존재한다. 불변성은 컴포넌트의 변경 여부를 검사하여 렌더링 비용을 줄인다.</p>
</blockquote>
<p>아래 코드는 지금까지 작성한 <code>Board</code>의 전문이다.</p>
<pre><code class="language-jsx">export default function Board() {
  const [squares, setSquares] = useState(Array(9).fill(null));

  function handleClick(idx) {
    const nextSquares = squares.slice();
    nextSquares[idx] = &#39;X&#39;;
    setSquares(nextSquares);
  }
  return (
    &lt;&gt;
      &lt;div className=&quot;board-row&quot;&gt;
        &lt;Square value={squares[0]} onSquareClick={() =&gt; handleClick(0)} /&gt;
        &lt;Square value={squares[1]} onSquareClick={() =&gt; handleClick(1)} /&gt;
        &lt;Square value={squares[2]} onSquareClick={() =&gt; handleClick(2)} /&gt;
      &lt;/div&gt;
      &lt;div className=&quot;board-row&quot;&gt;
        &lt;Square value={squares[3]} onSquareClick={() =&gt; handleClick(3)} /&gt;
        &lt;Square value={squares[4]} onSquareClick={() =&gt; handleClick(4)} /&gt;
        &lt;Square value={squares[5]} onSquareClick={() =&gt; handleClick(5)} /&gt;
      &lt;/div&gt;
      &lt;div className=&quot;board-row&quot;&gt;
        &lt;Square value={squares[6]} onSquareClick={() =&gt; handleClick(6)} /&gt;
        &lt;Square value={squares[7]} onSquareClick={() =&gt; handleClick(7)} /&gt;
        &lt;Square value={squares[8]} onSquareClick={() =&gt; handleClick(8)} /&gt;
      &lt;/div&gt;
    &lt;/&gt;
  );
}</code></pre>
<blockquote>
<p><code>&lt;Square value={squares[0]} onSquareClick={handleClick(0)} /&gt;</code>가 동작하지 않는 이유</p>
</blockquote>
<pre><code>Too many re-renders. React limits the number of renders to prevent an infinite loop.</code></pre><p><code>onSquareClick={handleClick}</code>을 전달하면 되면 사용자가 클릭하기 전에 <code>handleClick</code>이 실행되기 때문이다.</p>
<blockquote>
<blockquote>
<p><code>handleClick(idx)</code>는 <code>setSquares</code>를 호출하여 컴포넌트의 상태를 변경하므로 컴포넌트를 리렌더링하는 호출이다.</p>
</blockquote>
</blockquote>
<ol start="2">
<li>차례 정하기</li>
</ol>
<p>이제 O/X를 번갈아 실행할 차례다. 현재 차례가 x 차례인지 확인하는 <code>xIsNext</code>가 필요하다.</p>
<pre><code class="language-jsx">const [xIsNext, setXIsNext] = useState(true);</code></pre>
<p>클릭 이벤트가 발생했을 때 x의 차례인지 확인하고 칸을 차례에 맞게 갱신한다. 업데이트 후에 차례를 전환한다.</p>
<pre><code class="language-jsx">function handleClick(idx) {
    const nextSquares = squares.slice();
    if (xIsNext) {
      nextSquares[idx] = &#39;X&#39;;
    } else {
      nextSquares[idx] = &#39;O&#39;;
    }
    setSquares(nextSquares);
    setXIsNext(!xIsNext);
  }</code></pre>
<p>하지만 이미 클릭한 칸을 여러 번 클릭할 수 있다. 따라서 이미 클릭한 칸을 다시 클릭할 수 없도록 처리해야 한다.</p>
<pre><code class="language-jsx">function handleClick(idx) {
    if(squares[idx]) {
      return;
    }
    ...
  }</code></pre>
<ol start="3">
<li>승자 결정하기</li>
</ol>
<p>이제 게임의 승자를 가려보자. </p>
<pre><code class="language-jsx">function calculateWinner(squares) {
  const lines = [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8],
    [0, 3, 6],
    [1, 4, 7],
    [2, 5, 8],
    [0, 4, 8],
    [2, 4, 6],
  ];
  for (let i = 0; i &lt; lines.length; i++) {
    const [a, b, c] = lines[i];
    if (squares[a] &amp;&amp; squares[a] === squares[b] &amp;&amp; squares[a] === squares[c]) {
      return squares[a];
    }
  }
  return null;
}</code></pre>
<p>틱택토 게임을 이기기 위해서는 보드판의 한 줄을 완성해야 한다. <code>lines</code>는 보드판에서 만들 수 있는 줄 정보를 담고 있다. <code>for</code>문을 돌며 한 줄을 완성했는지 검사한다. 아래는 <code>Board</code>의 전문이다.</p>
<pre><code class="language-jsx">export default function Board() {
  const [xIsNext, setXIsNext] = useState(true);
  const [squares, setSquares] = useState(Array(9).fill(null));

  function handleClick(idx) {
    if (calculateWinner(squares) || squares[idx]) {
      return;
    }
    const nextSquares = squares.slice();
    if (xIsNext) {
      nextSquares[idx] = &#39;X&#39;;
    } else {
      nextSquares[idx] = &#39;O&#39;;
    }
    setSquares(nextSquares);
    setXIsNext(!xIsNext);
  }

  const winner = calculateWinner(squares);
  let status;
  if (winner) {
    status = &#39;Winner: &#39; + winner;
  } else {
    status = &#39;Next player: &#39; + (xIsNext ? &#39;X&#39; : &#39;O&#39;);
  }
  return (
    &lt;&gt;
      &lt;div className=&quot;status&quot;&gt;{status}&lt;/div&gt;
      &lt;div className=&quot;board-row&quot;&gt;
        &lt;Square value={squares[0]} onSquareClick={() =&gt; handleClick(0)} /&gt;
        &lt;Square value={squares[1]} onSquareClick={() =&gt; handleClick(1)} /&gt;
        &lt;Square value={squares[2]} onSquareClick={() =&gt; handleClick(2)} /&gt;
      &lt;/div&gt;
      &lt;div className=&quot;board-row&quot;&gt;
        &lt;Square value={squares[3]} onSquareClick={() =&gt; handleClick(3)} /&gt;
        &lt;Square value={squares[4]} onSquareClick={() =&gt; handleClick(4)} /&gt;
        &lt;Square value={squares[5]} onSquareClick={() =&gt; handleClick(5)} /&gt;
      &lt;/div&gt;
      &lt;div className=&quot;board-row&quot;&gt;
        &lt;Square value={squares[6]} onSquareClick={() =&gt; handleClick(6)} /&gt;
        &lt;Square value={squares[7]} onSquareClick={() =&gt; handleClick(7)} /&gt;
        &lt;Square value={squares[8]} onSquareClick={() =&gt; handleClick(8)} /&gt;
      &lt;/div&gt;
    &lt;/&gt;
  );
}</code></pre>
<p>클릭 이벤트가 발생할 때마다 승자가 있는지 검사하고, 승자가 나와서 이미 게임이 끝난 경우에는 이벤트가 발생하지 않도록 한다.
<img src="https://velog.velcdn.com/images/kimyu0218_/post/6c8d90fb-1571-4984-a5d9-2b5c6c4668e0/image.png" alt="result01"></p>
<ol start="4">
<li>과거 상태로 되돌리기</li>
</ol>
<p>앞에서 언급한 불변성을 이용하여 과거 상태로 되돌려보자. <code>Board</code>의 길이가 길기 때문에 별도의 <code>Game</code> 컴포넌트를 생성하고 <code>Board</code>를 호출하겠다.</p>
<pre><code class="language-jsx">export default function Game() {
  return (
    &lt;div className=&quot;game&quot;&gt;
      &lt;div className=&quot;game-board&quot;&gt;
        &lt;Board /&gt;
      &lt;/div&gt;
      &lt;div className=&quot;game-info&quot;&gt;
        &lt;ol&gt;{/* TODO */}&lt;/ol&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}</code></pre>
<p><code>Game</code>의 <code>game-info</code>에 히스토리를 기록하고 <code>Board</code>에 히스토리 사항을 반영해야 한다. 따라서 <code>Board</code>의 상태 정보를 <code>Game</code> 컴포넌트로 끌어올려야 한다. <code>history</code>는 보드판의 변경 정보를 기록하고 있으며 현재 보드판의 상태인 <code>currentSquares</code>는 보드판의 마지막 요소다.</p>
<pre><code class="language-jsx">export default function Game() {
  const [xIsNext, setXIsNext] = useState(true);
  const [history, setHistory] = useState([Array(9).fill(null)]); // 2차원 배열로
  const currentSquares = history[history.length - 1];
  ...
}</code></pre>
<p>상태 정보를 부모 컴포넌트로 끌어올렸기 때문에 하위 컴포넌트로 props를 전달해야 한다. <code>onPlay</code>는 부모 컴포넌트의 함수로 <code>handleClick</code> 내에서 호출된다.  <code>Board</code>에서 보드판을 갱신하는 것은 가능하지만, 플레이어의 순서를 변경하고 히스토리를 쌓는 것은 부모 컴포넌트인 <code>Game</code>에서 할 수 있는 작업이기 때문이다.</p>
<pre><code class="language-jsx">function Board({ xIsNext, squares, onPlay }) { 
  ...
  function handleClick(idx) {
    if (calculateWinner(squares) || squares[idx]) {
      return;
    }
    const nextSquares = squares.slice();
    if (xIsNext) {
      nextSquares[idx] = &#39;X&#39;;
    } else {
      nextSquares[idx] = &#39;O&#39;;
    }
    onPlay(nextSquares);
  }
}</code></pre>
<p>플레이어의 차례를 변경하고 히스토리를 갱신하는 함수가 바로 <code>handlePlay</code>다. <code>handlePlay</code>를 다음과 같이 정의하고, <code>Board</code>에 props를 전달한다.</p>
<pre><code class="language-jsx">export default function Game() {
  ...
  function handlePlay(nextSquares) {
    setHistory([...history, nextSquares]);
    setXIsNext(!xIsNext);
  }

  return (
    &lt;div className=&quot;game&quot;&gt;
      &lt;div className=&quot;game-board&quot;&gt;
        &lt;Board xIsNext={xIsNext} squares={currentSquares} onPlay={handlePlay} /&gt;
      &lt;/div&gt;
      ...
    &lt;/div&gt;
  );
}</code></pre>
<blockquote>
<p><code>[...history, nextSquares]</code>는 <code>history</code>를 포함하는 새로운 배열을 만들고, 마지막에 <code>nextSquares</code>를 추가한다는 의미이다. <code>...</code>은 스프레드 구문으로 모든 항목을 순회한다.</p>
</blockquote>
<p>이제 모든 상태 정보를 끌어올렸고 props도 올바르게 전달한다. 다음으로 화면에 히스토리 정보를 출력해보자. <code>moves</code>를 만들어 <code>&lt;ol&gt;</code>의 하위 태그로 넣어준다. <code>moves</code>는 버튼을 포함하고 있는데, 해당 버튼을 클릭하면 <code>jumpTo</code>가 호출되고 과거의 상태로 복원된다.</p>
<pre><code class="language-jsx">export default function Game() {
  function jumpTo(nextMove) {/* TODO */}

  const moves = history.map((squares, move) =&gt; {
    let description;
    if (move &gt; 0) {
      description = &#39;Go to move #&#39; + move;
    } else {
      description = &#39;Go to game start&#39;;
    }
    return (
      &lt;li&gt;
        &lt;button onClick={() =&gt; jumpTo(move)}&gt;{description}&lt;/button&gt;
      &lt;/li&gt;
    );
  });

  return (
    &lt;div className=&quot;game&quot;&gt;
      &lt;div className=&quot;game-board&quot;&gt;
        ...
      &lt;/div&gt;
      &lt;div className=&quot;game-info&quot;&gt;
        &lt;ol&gt;{moves}&lt;/ol&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}</code></pre>
<p>위의 코드를 작성하고 나면 다음과 같은 경고문이 나온다. </p>
<pre><code>Warning: Each child in a list should have a unique &quot;key&quot; prop.</code></pre><p>리스트에 요소를 추가하거나 제거하고, 리스트의 요소를 수정하기 위해서는 리스트의 요소들을 서로 구분할 수 있어야 한다. 즉, 아이템의 고유 key가 필요하다.</p>
<p>리스트가 리렌더링될 때, 리액트는 리스트에 존재하는 모든 키들을 가져가서 이전 리스트의 아이템과 매칭되는 키를 탐색한다.</p>
<ul>
<li>현재 리스트가 이전 리스트에 존재하지 않는 키를 가지고 있으면 컴포넌트를 생성한다.</li>
<li>현재 리스트가 이전 리스트에는 존재하는 키를 가지고 있지 않다면 컴포넌트를 파괴한다.</li>
</ul>
<blockquote>
<p>key는 전역적으로 고유한 값을 가질 필요는 없다. 그들의 형제 사이에서만 고유한 값을 가지면 된다.</p>
</blockquote>
<p>key의 필요성을 알았으니 <code>&lt;li&gt;</code>에 키 속성값을 추가하자. <code>key={move}</code></p>
<p>이제 과거의 상태로 되돌리는 것만 남았다. <code>jumpTo</code>를 구현하기 전에 <code>Game</code> 컴포넌트는 사용자가 보고 있는 단계 정보 를 추적해야 한다. </p>
<pre><code class="language-jsx">const [currentMove, setCurrentMove] = useState(0);</code></pre>
<p><code>currentMove</code>는 사용자가 보고 있는 단계를 의미한다. 해당 변수를 사용하여 <code>xIsNext</code>를 보다 간단하게 계산하고, 화면 정보를 갱신할 수 있다.</p>
<p>짝수번 차례일 때 X의 차례, 홀수번 차례일 때 O의 차례이므로 <code>const xIsNext = currentMove % 2 === 0;</code>로 누구의 차례인지 알 수 있다. 보드판은 마지막 결과를 렌더링 하던 것을 <code>currentMove</code>로 렌더링 하도록 만들면 된다.    </p>
<hr>
<h3 id="최종-코드">최종 코드</h3>
<pre><code class="language-jsx">import React, { useState } from &#39;react&#39;;

function calculateWinner(squares) {
  const lines = [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8],
    [0, 3, 6],
    [1, 4, 7],
    [2, 5, 8],
    [0, 4, 8],
    [2, 4, 6],
  ];
  for (let i = 0; i &lt; lines.length; i++) {
    const [a, b, c] = lines[i];
    if (squares[a] &amp;&amp; squares[a] === squares[b] &amp;&amp; squares[a] === squares[c]) {
      return squares[a];
    }
  }
  return null;
}

function Square({ value, onSquareClick }) {
  return (
    &lt;button className=&quot;square&quot; onClick={onSquareClick}&gt;
      {value}
    &lt;/button&gt;
  );
}

function Board({ xIsNext, squares, onPlay }) {
  function handleClick(idx) {
    if (calculateWinner(squares) || squares[idx]) {
      return;
    }
    const nextSquares = squares.slice();
    if (xIsNext) {
      nextSquares[idx] = &#39;X&#39;;
    } else {
      nextSquares[idx] = &#39;O&#39;;
    }
    onPlay(nextSquares);
  }

  const winner = calculateWinner(squares);
  let status;
  if (winner) {
    status = &#39;Winner: &#39; + winner;
  } else {
    status = &#39;Next player: &#39; + (xIsNext ? &#39;X&#39; : &#39;O&#39;);
  }
  return (
    &lt;&gt;
      &lt;div className=&quot;status&quot;&gt;{status}&lt;/div&gt;
      &lt;div className=&quot;board-row&quot;&gt;
        &lt;Square value={squares[0]} onSquareClick={() =&gt; handleClick(0)} /&gt;
        &lt;Square value={squares[1]} onSquareClick={() =&gt; handleClick(1)} /&gt;
        &lt;Square value={squares[2]} onSquareClick={() =&gt; handleClick(2)} /&gt;
      &lt;/div&gt;
      &lt;div className=&quot;board-row&quot;&gt;
        &lt;Square value={squares[3]} onSquareClick={() =&gt; handleClick(3)} /&gt;
        &lt;Square value={squares[4]} onSquareClick={() =&gt; handleClick(4)} /&gt;
        &lt;Square value={squares[5]} onSquareClick={() =&gt; handleClick(5)} /&gt;
      &lt;/div&gt;
      &lt;div className=&quot;board-row&quot;&gt;
        &lt;Square value={squares[6]} onSquareClick={() =&gt; handleClick(6)} /&gt;
        &lt;Square value={squares[7]} onSquareClick={() =&gt; handleClick(7)} /&gt;
        &lt;Square value={squares[8]} onSquareClick={() =&gt; handleClick(8)} /&gt;
      &lt;/div&gt;
    &lt;/&gt;
  );
}

export default function Game() {
  const [history, setHistory] = useState([Array(9).fill(null)]);
  const [currentMove, setCurrentMove] = useState(0);
  const currentSquares = history[currentMove];
  const xIsNext = currentMove % 2 === 0;

  function handlePlay(nextSquares) {
    const nextHistory = [...history.slice(0, currentMove + 1), nextSquares];
    setHistory(nextHistory);
    setCurrentMove(nextHistory.length - 1);
  }

  function jumpTo(nextMove) {
    setCurrentMove(nextMove);
  }

  const moves = history.map((squares, move) =&gt; {
    let description;
    if (move &gt; 0) {
      description = &#39;Go to move #&#39; + move;
    } else {
      description = &#39;Go to game start&#39;;
    }
    return (
      &lt;li key={move}&gt;
        &lt;button onClick={() =&gt; jumpTo(move)}&gt;
          {description}
        &lt;/button&gt;
      &lt;/li&gt;
    );
  });

  return (
    &lt;div className=&quot;game&quot;&gt;
      &lt;div className=&quot;game-board&quot;&gt;
        &lt;Board xIsNext={xIsNext} squares={currentSquares} onPlay={handlePlay} /&gt;
      &lt;/div&gt;
      &lt;div className=&quot;game-info&quot;&gt;
        &lt;ol&gt;{moves}&lt;/ol&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 리액트 시작하기 2편]]></title>
            <link>https://velog.io/@kimyu0218_/React-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-2%ED%8E%B8</link>
            <guid>https://velog.io/@kimyu0218_/React-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0-2%ED%8E%B8</guid>
            <pubDate>Sat, 19 Aug 2023 18:03:18 GMT</pubDate>
            <description><![CDATA[<h3 id="conditional-rendering">Conditional Rendering</h3>
<p>리액트에는 조건을 작성하는 특별한 문법이 존재하지 않으므로 자바스크립트에서 사용하던 조건문을 그대로 사용하면 된다.</p>
<pre><code class="language-jsx">let content;
if (isLoggedIn) {
  content = &lt;AdminPanel /&gt;
} else {
  content = &lt;LoginForm /&gt;
}
return (
  &lt;div&gt;
    {conent}
  &lt;/div&gt;
);</code></pre>
<p><code>if ... else</code> 대신 삼항 연산자를 사용할 수 있으며 <code>else</code>문이 필요없는 경우 다음 코드로 대체할 수 있다.</p>
<pre><code class="language-jsx">return (
  &lt;div&gt;
    {isLoggedIn &amp;&amp; &lt;AdminPanel /&gt;}
  &lt;/div&gt;
);</code></pre>
<hr>
<h3 id="renderig-lists">Renderig Lists</h3>
<p><code>for</code>문이나 array의 <code>map()</code>을 이용하여 컴포넌트 배열을 반환할 수도 있다.</p>
<pre><code class="language-jsx">const products = [
  { name: &#39;Apple&#39;, id: 1 },
  { name: &#39;Banana&#39;, id: 2 },
  { name: &#39;Carrot&#39;, id: 3 },
];

const listItems = products.map(product =&gt;
  &lt;li key={product.id}&gt;
    {product.name}
  &lt;/li&gt;
);

export default function App() {
  return (
    &lt;ul&gt;{listItems}&lt;/ul&gt;
  );
}</code></pre>
<hr>
<h3 id="responding-to-events">Responding to Events</h3>
<p>컴포넌트 내에 이벤트 핸들러 함수를 선언하여 이벤트에 응답하도록 만들 수 있다.</p>
<pre><code class="language-jsx">function MyButton() {
  function handleClick() {
    alert(&#39;Click!!&#39;);
  }
  return (
    &lt;button onClick={handleClick}&gt;
      Click me
    &lt;/button&gt;
  );
}</code></pre>
<blockquote>
<p><code>onClick</code>은 마우스 이벤트 중 하나로, 사용자가 HTML 요소를 클릭할 때 발생하는 이벤트다.</p>
</blockquote>
<hr>
<h3 id="updating-the-screen">Updating the Screen</h3>
<p>컴포넌트에 정보를 저장하고 출력해보자. 지금 만들 컴포넌트는 버튼을 클릭할 때마다 그 수를 하나씩 증가시키는 버튼이다. 이것을 구현하기 위해서는 컴포넌트에 state를 추가해야 한다.</p>
<blockquote>
<p><code>useState()</code>는 current state와 current state를 업데이트하는 함수를 가진다. 아무 이름이나 지정할 수 있지만 <code>[something, setSomething]</code> 형식을 권장한다.</p>
</blockquote>
<pre><code class="language-jsx">import React, { useState } from &#39;react&#39;;

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

  function handleClick() {
      setCount(count+1);
  }

  return (
    &lt;button onClick={handleClick}&gt;
      Clicked {count} times
    &lt;/button&gt;
  );
}</code></pre>
<hr>
<h3 id="hook">Hook</h3>
<p>Hook은 리액트 버전 16.8부터 리액트 요소로 새롭게 추가 되었다. Hook을 이용하면 기존 클래스 바탕의 코드를 작성할 필요 없이 상태값과 여러 리액트 기능을 사용할 수 있다. 앞서 살펴본 <code>useState</code>가 Hook의 대표적인 예다.</p>
<p>💥 <strong>Built-in React Hooks</strong></p>
<ul>
<li><p><strong>State Hooks</strong> : state는 컴포넌트가 사용자 입력값과 같은 정보를 기억할 수 있도록 만든다.</p>
<ul>
<li><code>useState</code> : 직접적으로 업데이트 할 수 있는 상태 변수 선언</li>
<li><code>useReducer</code> : 리듀서 함수 내에 상태 변수와 업데이트 로직 선언</li>
</ul>
<blockquote>
<p><code>useState</code>와 <code>useReducer</code>는 초기값을 전달하고 상태값과 상태를 설정하는 함수를 반환한다는 점에서 동일하다. 하지만 <code>useReducer</code>는 초기값과 함께 reducer function을 파라미터로 전달해야 한다.</p>
</blockquote>
</li>
<li><p><strong>Context Hooks</strong> : context를 사용하면 컴포넌트가 props를 전달하지 않고 멀리 떨어진 부모로부터 정보를 받을 수 있다. Context hook에는 <code>useContext</code>가 있다.</p>
</li>
<li><p><strong>Ref Hooks</strong>
Refs는 컴포넌트가 랜더링에 사용되지 않는 정보를 가지고 있는 것을 허용한다. state와 달리 ref를 갱신하는 것은 컴포넌트를 다시 렌더링하지 않는다는 것을 의미한다. refs는 built-in browser API처럼 리액트 시스템 이외의 것과 작업할 때 유용하다. 
ex. <code>useRef</code>, <code>useImperativeHandle</code></p>
</li>
<li><p><strong>Effect Hooks</strong><br>effects를 사용하면 컴포넌트가 외부 시스템에 연결하여 동기화할 수 있다. effects는 네트워크, 브라우저 DOM, 애니메이션 처리를 포함한다.</p>
<ul>
<li><code>useEffect</code> : 컴포넌트를 외부 시스템과 연결한다.</li>
<li><code>userLayoutEffect</code> : 브라우저가 리페인팅을 하기 전에 레이아웃을 측정한다.</li>
<li><code>useInsertionEffect</code> : 리액트가 DOM을 변경하기 전에 라이브러리가 동적 CSS를 삽입한다.</li>
</ul>
</li>
<li><p><strong>Performance Hooks</strong> <br>리랜더링을 최적화하는 가장 흔한 방법은 불필요한 작업을 건너뛰는 것이다. 예를 들어 이전 렌더 이후로 데이터가 변경되지 않았으면 리렌더링을 스킵한다.<br>불필요한 연산이나 불필요한 리렌더링을 건너뛰려면 다음 후크 중 하나를 사용하면 된다.</p>
<ul>
<li><code>useMemo</code> : 계산 결과를 캐싱한다.</li>
<li><code>useCallback</code> : 메모제이션된 콜백을 반환한다.</li>
</ul>
</li>
</ul>
<blockquote>
<p>화면 전체를 갱신해야 하는 경우가 발생하면 리렌더링을 거너뛸 수 없다. 이 경우에는 blocking과 non-blocking을 구분하여 성능을 개선할 수 있다.</p>
</blockquote>
<ul>
<li><code>useTransition</code> : (non-blocking) UI를 블락하지 않고 상태를 갱신한다.</li>
<li><code>useDeferredValue</code> : (blocking) UI 업데이트를 연기하고 다른 부분을 먼저 갱신한다.</li>
</ul>
<hr>
<h3 id="passing-props-to-a-component">Passing Props to a Component</h3>
<blockquote>
<p>리액트 컴포넌트는 서로 통신하기 위해 props를 사용한다. 모든 부모 컴포넌트는 props를 전달하며 자식 컴포넌트들에 정보를 전달한다. props는 HTML 속성이나 객체, 배열, 함수를 포함한 어느 자바스크립트 값이든 될 수 있다.</p>
</blockquote>
<ol>
<li>자식 컴포넌트 <code>User</code>에 props 전달하기
props : <code>person</code>, <code>size</code><pre><code class="language-jsx">// parent
export default function Profile() {
return (
 &lt;User
   person={{name: &#39;John&#39;, imageId: &#39;1bX5QH6&#39;}}
   size={100}
   /&gt;
);
}</code></pre>
</li>
<li>자식 컴포넌트의 props 읽기<pre><code class="language-jsx">// child
function User ({person, size}) {
 return (
   &lt;img
     className=&#39;user&#39;
     src={getImageUrl(person)}
     alt={person.name}
     width={size}
     height={size}
     /&gt;
 );
}</code></pre>
<blockquote>
<p>props는 부모와 자식 컴포넌트를 독립적으로 바라보도록 만든다. 예를 들어 <code>Profile</code>에서 <code>person</code>과 <code>size</code>값을 수정할 때 <code>User</code>를 고려할 필요가 없다.</p>
</blockquote>
</li>
</ol>
<p>💥 <strong>props를 사용하는 두 가지 방법</strong></p>
<blockquote>
<p>props는 컴포넌트의 유일한 인자다.</p>
</blockquote>
<ul>
<li>중괄호를 사용하여 개별 prop으로 분리하기<pre><code class="language-jsx">function User({person, size}) {...}</code></pre>
</li>
<li>개별 prop으로 분리하지 않고 사용하기<pre><code class="language-jsx">function User(props) {
let person = props.person;
let size = props.size;
...
}</code></pre>
</li>
</ul>
<hr>
<p>참고자료
<a href="https://react.dev/learn">React docs : Quick Start</a>
<a href="react.dev/reference/react">React docs : Built-in React Hooks</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[웹 브라우저 파헤치기]]></title>
            <link>https://velog.io/@kimyu0218_/%EC%9B%B9-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%ED%8C%8C%ED%97%A4%EC%B9%98%EA%B8%B0</link>
            <guid>https://velog.io/@kimyu0218_/%EC%9B%B9-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%ED%8C%8C%ED%97%A4%EC%B9%98%EA%B8%B0</guid>
            <pubDate>Sat, 19 Aug 2023 13:48:37 GMT</pubDate>
            <description><![CDATA[<h3 id="rendering">Rendering</h3>
<p>웹 개발에서 렌더링이란 HTML, CSS, 자바스크립트 등 개발자가 작성한 문서가 브라우저에 출력되는 과정을 의미한다. 브라우저는 종류마다 서로 다른 렌더링 엔진을 가지고 있다.</p>
<ul>
<li>Chrome : Blink</li>
<li>Safari : WebKit</li>
<li>Firefox : Gecko</li>
</ul>
<hr>
<h3 id="web-browser">Web Browser</h3>
<p>브라우저의 주요 역할은 두가지이다.</p>
<ol>
<li>사용자가 요청한 웹페이지, 이미지, 동영상 등의 자원을 서버에게 요청한다.</li>
<li>서버로부터 전달받은 자원을 화면에 출력한다.</li>
</ol>
<p><img src="https://wd.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/JvSL0B5q1DmZAKgRHj42.png?auto=format&w=741" alt="Chrome Process Architecture">
위의 그림은 크롬의 멀티 프로세스 구조를 나타낸 것이다. 각 프로세스가 어떤 역할을 하는지 알아보자.</p>
<table>
<thead>
<tr>
<th>Process</th>
<th>What it controls</th>
</tr>
</thead>
<tbody><tr>
<td>Browser</td>
<td>Controls &#39;chrome&#39; part of the application including address bar, bookmarks, and back and forward buttons.</td>
</tr>
<tr>
<td>Renderer</td>
<td>Controls anything inside of the tab where a website is displayed.</td>
</tr>
<tr>
<td>Plugin</td>
<td>Controls any plugins used by the website.</td>
</tr>
<tr>
<td>GPU</td>
<td>Handle GPU tasks in isolation from other processes.</td>
</tr>
<tr>
<td><img src="https://wd.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/vl5sRzL8pFwlLSN7WW12.png?auto=format&w=741" alt="Chrome Process Role"></td>
<td></td>
</tr>
</tbody></table>
<p>💥 <strong>Chrome이 멀티 프로세스 구조를 사용하는 이유</strong>
크롬은 브라우저 탭마다 하나의 프로세스를 부여한다. 만약 여러 탭이 하나의 렌더러 프로세스로 돌아간다면 어떻게 될까?</p>
<p>우리는 하나의 탭이 <code>unresponsive</code>, 즉 무응답 상태로 변하면, 해당 탭을 닫고 살아있는 다른 탭으로 이동하여 작업을 계속한다. 하지만 모든 탭들이 하나의 프로세스 위에서 돌아간다면, 모든 탭들이 무응답 상태가 된다. 따라서 크롬은 탭별로 프로세스를 부여하여 위 현상을 방지한다.
<img src="https://wd.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/ZVkrl0QErFtITKPwa6Cq.png?auto=format&w=741" alt="The Benefit of Multi-Process"></p>
<p>💥 <strong>Site Isolation</strong>
Site Isolation은 악성 웹 페이지에서 다른 웹 사이트의 데이터를 쉽게 탈취할 수 없도록 만들어진 Protection으로, 비교적 최근에 도입된 기능이다. </p>
<p>iframe으로 하위 페이지를 부르는 경우를 생각해보자. 이전에는 부모 페이지와 자식 iframe 페이지가 동일 프로세스 상에 존재했다. 서로 같은 프로세스이기 때문에 메모리 공간을 공유하게 되고, 이는 다른 페이지에서 메모리에 접근하여 데이터를 읽을 수 있다는 취약점으로 이어진다. 현재는 iframe에 별도의 렌더러 프로세스를 부여하고 있다.</p>
<hr>
<h3 id="what-happens-in-navigation">What happens in Navigation</h3>
<p>이제 간단한 웹 브라우징 사례를 살펴보자. 사용자가 URL을 타이핑하면, 브라우저가 인터넷에서 데이터를 받아와 페이지를 출력한다. 여기서는 사용자가 요청을 보내고 브라우저가 페이지를 렌더링 하기 위해 준비하는 과정만 다룰 것이다.</p>
<ol>
<li><strong>Starts with a browser process</strong></li>
</ol>
<p><img src="https://wd.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lo3x7Zt4LZ4ltsQQjLns.png?auto=format&w=741" alt="browser process">
브라우저 프로세스는 탭 외부에서 일어나는 모든 것을 담당한다. 브라우저 프로세스는 UI 스레드, 네트워크 스레드, 스토리지 스레드를 포함한다.</p>
<ul>
<li>UI thread : 입력창이나 앞으로/뒤로 가기 버튼 등을 그린다.</li>
<li>Network thread : 인터넷에서 온 데이터를 받기 위해 네트워크 스택을 관리한다.</li>
<li>Storage thread : 파일에 대한 접근 등을 관리한다.</li>
</ul>
<ol start="2">
<li><strong>Handiling input</strong></li>
</ol>
<p>사용자가 주소창에 타이핑 하면, 가장 먼저 UI 스레드가 사용자 입력값이 쿼리인지 URL인지 분석한다.</p>
<ol start="3">
<li><strong>Start navigation</strong></li>
</ol>
<p>사용자가 엔터를 치면, UI 스레드는 웹 사이트에서 내용을 받아오기 위해 network call을 생성한다. 로딩 스피너가 탭에 표시되고, 네트워크 스레드가 DNS를 조회하고 TLS 연결을 생성하는 등 적절한 프로토콜을 거치게 된다.
<img src="https://wd.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/nSD7ognQ9hNFoFOnFQlw.png?auto=format&w=741" alt="start navigation"></p>
<blockquote>
<p>만약 리다이렉트 의미를 가진 300번대의 HTTP 상태코드를 받게 되면, 네트워크 스레드는 UI 스레드에게 서버가 리다이렉트를 요청했다고 전달한다.</p>
</blockquote>
<ol start="4">
<li><strong>Read Response</strong></li>
</ol>
<p>네트워크 스레드는 응답을 받으면 필요시에 스트림의 첫 몇바이트를 확인한다. 응답이 HTML 파일인 경우, 다음 단계에서 렌더러 프로세스가 데이터를 받아 작업을 이어나간다. 
<img src="https://wd.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/pn0zlnxoYgbyzFVKoTc9.png?auto=format&w=741" alt="read response"></p>
<blockquote>
<p>응답이 압축 파일이거나 다른 파일인 경우, 다운로드 매니저에게 넘겨 필요한 데어터를 다운로드 한다.</p>
</blockquote>
<ol start="5">
<li><strong>Find a renderer process</strong></li>
</ol>
<p>네트워크 스레드는 응답에 대한 검사가 끝나면 UI 스레드에게 데이터가 준비 되었다고 전한다. 그러면 UI 스레드는 렌더러 프로세스를 찾는다.
<img src="https://wd.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/VAR3s7k8rIgTrfwEWMIo.png?auto=format&w=741" alt="find rederer process"></p>
<blockquote>
<p>현재 단계에서 렌더러 프로세스를 찾는 것은 비효율적이다. UI 스레드가 네트워크 스레드에게 URL 요청을 전달할 때, UI 스레드는 이미 어떤 사이트를 요청했는지 알고 있다. 따라서 UI 스레드는 네트워크 요청과 동시에 렌더러 프로세스를 찾을 수 있다. 네트워크 스레드가 데이터를 수신했을 때, 이미 렌더러 프로세스는 대기 상태에 있다.</p>
</blockquote>
<ol start="6">
<li><strong>Commit navigation</strong></li>
</ol>
<p>이제 데이터와 렌더러 프로세스가 모두 준비되었으므로, 브라우저 프로세스에서 렌더러 프로세스로 commit을 하기 위한 IPC가 전송된다. 이후 브라우저 프로세스에서 렌더러 프로세스가 commit 되었다는 확인을 받으면 문서가 로딩된다.
<img src="https://wd.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/kL6CLP7fLay9L99vRR3F.png?auto=format&w=741" alt="commit navigation">
해당 단계에서 주소창 갱신되고 보안 표시 및 사이트 설정 UI에 새로운 페이지의 정보가 반영된다. 이외에도 세션 히스토리가 갱신되는 등 다양한 작업이 이루어진다.</p>
<hr>
<h3 id="inner-workings-of-a-renderer-process">Inner workings of a Renderer Process</h3>
<p>이번엔 렌더러 과정을 살펴보자. 렌더러 프로세스는 탭 내부에서 일어나는 모든 것(=web contents)을 담당한다. 
<img src="https://wd.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/uIqf0QQZxF6mHPDWFEjz.png?auto=format&w=741" alt="renderer process">
렌더러의 메인 스레드는 대부분의 코드를 담당한다. 만약 웹 워커나 서비스 워커를 사용하고 있다면 이들이 코드의 일부를 나누어 담당한다. Compositor와 Raster 스레드는 페이지를 효과적이고 매끄럽게 렌더링 하도록 도와주는 역할이다.</p>
<p>렌더러 프로세스의 주요 업무는 HTML, CSS, 자바스크립트를 웹 페이지로 변환하는 것이다.</p>
<ol>
<li><strong>Parsing : Constructing a DOM</strong></li>
</ol>
<p>렌더러 프로세스가 navigation을 위한 commit 메시지를 받고, HTML 데이터를 받기 시작하면, 메인 스레드는 HTML을 DOM으로 변환한다.</p>
<blockquote>
<p>DOM : (Document Object Model) 문서의 구조화된 표현을 제공하며 프로그래밍 언어가 DOM 구조에 접근할 수 있는 방법을 제공하여 문서 구조/스타일/내용 등을 변경할 수 있도록 도와준다.</p>
</blockquote>
<ol start="2">
<li><strong>Subresourcing loading</strong></li>
</ol>
<p>웹 사이트는 주로 HTML을 단독으로 사용하지 않고, 이미지, CSS, 자바스크립트와 같은 외부 자원을 함께 사용한다. 메인 스레드는 파싱하면서 필요한 외부 자원을 하나 하나 호출할 수 있다. 하지만 빠르게 작업을 수행하기 위해 preload scanner를 동시에 실행한다.</p>
<blockquote>
<p>preload scanner는 <code>&lt;img&gt;</code>나 <code>&lt;link&gt;</code> 등을 발견하면 브라우저 프로세스의 네트워크 스레드에게 요청을 보낸다.</p>
</blockquote>
<p><img src="https://wd.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/qmuN5aduuEit6SZfwVOi.png?auto=format&w=964" alt="subresourcing loading"></p>
<p>💥 <strong>자바스크립트는 파싱을 block 한다</strong>
HTML 파서가 <code>&lt;script&gt;</code> 태그를 만나면, HTML 파싱을 멈추고 자바스크립트 코드를 로드하고 파싱하여 실행해야 한다. 이는 자바스크립트가 <code>document.write()</code>처럼 문서를 변경할 수 있기 때문이다.</p>
<ol start="3">
<li><strong>Style calculation &amp; Layout &amp; Paint</strong></li>
</ol>
<p>앞에서도 말했듯이 웹 사이트는 일반적으로 HTML을 단독으로 사용하지 않는다. 웹 사이트를 그리기 위해서는 CSS의 스타일이 필요하다. 메인 스레드는 CSS를 파싱하고, 각 DOM 노드의 스타일을 결정한다. 
<img src="https://wd.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hGqtsAuYpEYX4emJd5Jw.png?auto=format&w=964" alt="style calculation"></p>
<p>이제 렌더러 프로세스는 각 노드의 문서 구조(= DOM 트리)와 스타일을 알고 있다. 하지만 이것만으로는 페이지를 렌더링할 수 없다.</p>
<blockquote>
<p>&quot;빨간색 원과 파란색 사각형을 그려라.&quot;
해당 문장만으로는 주어진 도형들을 어느 위치에 얼만큼 크게 그려야 하는지 알 수 없다.</p>
</blockquote>
<p><img src="https://wd.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/0JqiVwHxNab2YL6qWHbS.png?auto=format&w=964" alt="layout">
레이아웃은 요소들의 기하학적인 위치를 찾는 과정이다. 메인 스레드는 DOM과 computed styles를 살펴보고 xy 좌표와 상자 크기와 같은 정보를 포함한 레이아웃 트리(=CSSOM 트리)를 만든다. 이는 DOM tree와 유사하지만 스타일 정보를 담고 있다는 점에서 차이가 있다.</p>
<ol start="4">
<li><strong>Paint</strong></li>
</ol>
<p>이제 요소의 크기, 모양, 위치를 알고 있으므로 색상만 결정해주면 된다. 메인 스레드는 색상을 칠하기 위해 레이아웃 트리를 검토한다.
<img src="https://wd.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/zs8wNimWDPhu7NIhJDcc.png?auto=format&w=964" alt="paint"></p>
<hr>
<h3 id="compositing">Compositing</h3>
<p>지금까지 브라우저가 문서 구조, 각 요소의 스타일, 기하학적 위치, 페인트 순서를 파악하는 과정을 살펴보았다. 이제 실제로 페이지가 그려지는 과정을 살펴보자. 앞서 등장한 Compositing, Raster 스레드가 여기서 활용된다. </p>
<p>렌더링 과정에서 얻은 정보를 픽셀로 전환하는 과정을 래스터화라고 한다. 가장 간단하게 래스터하는 방법은 뷰포트 내부 부분을 래스터하는 것이다. 사용자가 페이지를 스크롤하면 래스터된 프레임을 이동하고 새롭게 추가된 부분을 래스터화한다.
<a href="https://storage.googleapis.com/web-dev-uploads/video/T4FyVKpzu4WKF1kBNvXepbi08t52/AiIny83Lk4rTzsM8bxSn.mp4">🔗 초기 래스터 방식 확인하기</a></p>
<p>위 방법은 크롬이 처음 출시되었을 때 사용한 방식이다. 현재는 보다 정교한 합성 프로세스를 실행한다.</p>
<p><strong>Compositing</strong>
합성은 페이지의 일부를 레이어로 분리하고 개별적으로 래스터화하여 다음 compositor 스레드라 불리는 별도의 스레드에서 페이지로 합성하는 과정을 의미한다. 스크롤이 발생하면 이미 레이어들이 래스터화 되어 있기 때문에 새 프레임을 합성하기만 하면 된다.
<a href="https://storage.googleapis.com/web-dev-uploads/video/T4FyVKpzu4WKF1kBNvXepbi08t52/Aggd8YLFPckZrBjEj74H.mp4">🔗 합성을 통한 래스터 방식 확인하기</a></p>
<p>💥 <strong>레이어 나누기</strong>
메인 스레드는 어떤 요소가 어떤 레이어에 있는지 알아내기 위해 레이아웃 트리를 돌아다니며 레이어 트리를 만든다.
<img src="https://wd.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/V667Geh9MtTviJjDkGZq.png?auto=format&w=964" alt="divide into layers"></p>
<p>💥 <strong>래스터와 합성</strong>
레이어 트리가 생성되면 메인 스레드는 해당 정보를 Compositor 스레드에 전달한다. Compositor 스레드는 각 레이어를 래스터 하고, 레이어를 타일로 나누어 각 타일을 raster 스레드로 전송한다. Raster 스레드는 각 타일을 래스터하여 GPU 메모리에 저장한다.
<img src="https://wd.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/SL4KO5UsGgBNLrOwb0wC.png?auto=format&w=964" alt="raster and composite off of the main thread"></p>
<blockquote>
<p>Compositor 스레드는 raster 스레드의 우선순위를 지정하여 뷰포트 내의 항목을 먼저 래스터할 수 있다.  </p>
</blockquote>
<p>compositor 스레드는 타일이 래스터되고 나면 draw quads라는 타일 정보를 수집하여 compositor frame을 생성한다.</p>
<ul>
<li>Draw quads : Contains information such as the tile&#39;s location in memory and where in the page to draw the tile taking in consideration of the page compositing.</li>
<li>Compositor frame : A collection of draw quads that represents a frame of a page.</li>
</ul>
<p>compositor frame을 성공적으로 생성하고 나면 IPC를 통해 브라우저 프로세스에 제출된다. compositor frame은 GPU로 전송되어 화면에 표시되고, 스크롤 이벤트가 발생하면 다른 compositor frame을 생성하고 GPU로 전송한다.
<img src="https://wd.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/tG4AzFeS3IdfTSawnFL6.png?auto=format&w=964" alt="composite"></p>
<blockquote>
<p>합성은 메인 스레드를 거치지 않고 수행된다. 즉, 합성 스레드는 스타일 계산이나 자바스크립트 실행을 기다릴 필요가 없다. 하지만 레이아웃이나 페인트를 다시 계산해야 하는 경우에는 메인 스레드가 관여해야 한다.</p>
</blockquote>
<hr>
<p>참고자료
<a href="https://developer.chrome.com/blog/inside-browser-part1/">Inside look at modern web browser #1</a>
<a href="https://developer.chrome.com/blog/inside-browser-part2/">Inside look at modern web browser #2</a>
<a href="https://developer.chrome.com/blog/inside-browser-part3/">Inside look at modern web browser #3</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 리액트 시작하기]]></title>
            <link>https://velog.io/@kimyu0218_/React-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@kimyu0218_/React-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 18 Aug 2023 16:39:59 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>리액트의 핵심적인 역할은 사용자 정의 태그를 만드는 것이다. 다음 코드가 있다고 가정해보자.</p>
</blockquote>
<pre><code class="language-html">&lt;header&gt;
  &lt;h1&gt;
    &lt;a href=&#39;/&#39;&gt;web&lt;/a&gt;
  &lt;/h1&gt;
&lt;/header&gt;
&lt;nav&gt;
  &lt;ol&gt;
    &lt;li&gt;&lt;a href=&#39;/read/1&#39;&gt;html&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&#39;/read/2&#39;&gt;css&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href=&#39;/read/3&#39;&gt;javascript&lt;/a&gt;&lt;/li&gt;
  &lt;/ol&gt;
&lt;/nav&gt;</code></pre>
<p>리액트는 서로 연관된 코드들을 그룹핑하고 이름을 붙여 단순한 태그로 만들 수 있다.</p>
<pre><code class="language-html">&lt;Header&gt;&lt;/Header&gt;
&lt;Nav&gt;&lt;/Nav&gt;</code></pre>
<p>이처럼 복잡한 태그를 숨기고 사용자 정의 태그를 사용할 수 있도록 도와주는 것이 리액트다. 사용자 태그를 타인에게 공유할 수도 있고, 타인의 것을 사용할 수도 있다.</p>
<h3 id="npm--node-package-manager">npm : Node Package Manager</h3>
<p>Node.js를 설치하면 <code>npm</code>이 함께 설치된다. <code>npm</code>은 Node.js에서 사용하는 패키지 관리자로 다음 명령어를 지원한다.</p>
<ul>
<li><code>npm install</code> : install a package (all the dependencies in your project)<pre><code class="language-bash"># 특정 버전 설치하기
npm install &lt;package name&gt;@&lt;version&gt;
# --save / -S : dependencies에 추가
# save-dev / -D : devDependencies에 추가
# -g : 글로벌 패키지에 추가</code></pre>
</li>
<li><code>npm ci</code> : install a project with a clean state (depending on package-lock.json)</li>
<li><code>npm start</code> : start a package</li>
<li><code>npm run &lt;script&gt;</code> : run the script</li>
<li><code>npm test</code> : test a pacakage (=run this project&#39;s tests)</li>
</ul>
<hr>
<h3 id="react-개발환경-세팅하기">React 개발환경 세팅하기</h3>
<blockquote>
<p>개발환경을 세팅하지 않고 리액트를 사용하고 싶다면 <a href="https://stackblitz.com/edit/react-d4n39l?file=src%2FApp.js">온라인 코드 편집기</a>를 사용할 수 있다. </p>
</blockquote>
<ul>
<li><a href="ko.legacy.reactjs.org/docs/create-a-new-react-app.html">추천 툴체인</a>의 Create React App<pre><code class="language-bash">npx create-react-app [directory] # npx command를 사용하기 위해서는 NodeJS가 깔려있어야 한다</code></pre>
</li>
<li><code>npm start</code> : 리액트 개발환경 실행 및 코딩 환경 동작<ul>
<li><code>src/index.js</code> 실행</li>
<li><code>&lt;App /&gt;</code> (=<code>src/App.js</code>) 화면에 출력</li>
</ul>
</li>
</ul>
<hr>
<h3 id="react-빌드하고-실행하기">React 빌드하고 실행하기</h3>
<pre><code class="language-bash">npm run build # 배포판 생성 -&gt; /build 생성
npx serve -s build # 빌드 결과 실행</code></pre>
<hr>
<h3 id="component">Component</h3>
<blockquote>
<p>리액트는 마크업과 로직을 별도의 파일로 분리하는 대신 컴포넌트를 사용하고 있다.</p>
</blockquote>
<p>리액트 어플리케이션은 컴포넌트로 구성되어 있다. 컴포넌트는 로직과 외양을 가진 UI의 한 조각이다. 컴포넌트는 작은 버튼이 될 수도 있고, 한 페이지가 될 수도 있다.</p>
<p>리액트 컴포넌트는 마크업 언어를 리턴하는 자바스크립트 함수다.</p>
<pre><code class="language-jsx">function MyButton() {
  return (
    &lt;button&gt;
      I&#39;m a button
    &lt;/button&gt;
  );
}

export default function MyApp() {
  return (
    &lt;div&gt;
      &lt;h1&gt;Welcome to my app&lt;/h1&gt;
      &lt;MyButton /&gt;
    &lt;/div&gt;
  );
}</code></pre>
<p>함수로 생성한 <code>MyButton</code> 컴포넌트는 또 다른 컴포넌트인 <code>MyApp</code>에 중첩하여 사용할 수 있다. 이때 함수명(=컴포넌트명)은 대문자로 시작하는 것을 권장한다.</p>
<blockquote>
<p>컴포넌트명을 대문자로 시작하여 소문자로 시작하는 일반 HTML 태그와 구분할 수 있다.</p>
</blockquote>
<p>💥 <strong>컴포넌트는 반드시 하나의 최상위 태그를 반환해야 한다</strong>
컴포넌트 내에 여러 태그들을 사용하는 것은 가능하나, 최상위 태그는 반드시 하나여야 한다. 여러 태그들을 반환해야 하는 경우, <code>&lt;div&gt;...&lt;/div&gt;</code>나 <code>&lt;&gt;...&lt;/&gt;</code>로 감싸줘야 한다.</p>
<pre><code class="language-jsx">function Titles() {
    return (
      &lt;&gt;
        &lt;h1&gt;Title&lt;/h1&gt;
        &lt;h2&gt;Sub Title&lt;/h2&gt;
      &lt;/&gt;
    );
}</code></pre>
<hr>
<h3 id="jsx">JSX</h3>
<p>위의 코드를 작성하면서 우리가 알던 자바스크립트 문법과 다소 다르다는 것을 느꼈을 것이다. 자바스크립트도 아니고 HTML도 아닌 이상한 문법의 정체는 바로 JSX다.</p>
<p>JSX는 자바스크립트를 확장한 문법으로, 자바스크립트의 모든 기능을 포함하고 있다. JSX는 자바스크립트에 마크업 언어를 집어넣을 수도 있다. 다음 코드는 중괄호를 사용하여 변수값을 화면에 표시한다.</p>
<pre><code class="language-jsx">const user = {
  name: &#39;John&#39;,
  age: 30
};

function User() {
    return (
      &lt;h2&gt;{user.name}&lt;/h2&gt;
    );
}

export default MyApp() {
  return (
    &lt;div&gt;
      &lt;h1&gt;User&lt;/h1&gt;
      &lt;User /&gt;
    &lt;/div&gt;
  );
}</code></pre>
<p><img src="https://velog.velcdn.com/images/kimyu0218_/post/5c866de8-a429-470e-8c2b-e9332e14290c/image.png" alt="screenshot01"></p>
<hr>
<p>참고자료
<a href="https://docs.npmjs.com/cli/v9/commands">npm Docs : npm CLI Commands</a>
<a href="https://www.youtube.com/watch?v=AoMv0SIjZL8&amp;list=PLuHgQVnccGMCOGstdDZvH41x0Vtvwyxu7">생활코딩 : React 2022년 개정판</a>
<a href="https://react.dev/learn">React docs : Quick Start</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TS] 타입스크립트 시작하기]]></title>
            <link>https://velog.io/@kimyu0218_/TS-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@kimyu0218_/TS-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 18 Aug 2023 13:37:58 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>자바스크립트에도 <code>String</code>, <code>Number</code>, <code>Object</code> 등의 데이터 타입이 존재하지만, 자바스크립트는 전체 코드에 일관되게 할당되었는지 미리 확인해주지 않는다. 아래 코드에서 확인할 수 있듯이 자바스크립트는 숫자가 아닌 값이 들어가는지 확인하지 않는다.</p>
</blockquote>
<pre><code class="language-javascript">// javascript
const add = (a, b) =&gt; a + b;
console.log(add(1, 2)); // 3
console.log(add(&#39;str&#39;, 2)); // str2</code></pre>
<pre><code class="language-typescript">// typescript
const add = (a:number, b:number):number =&gt; a+b;
console.log(add(1, 2)); // 3
console.log(add(&#39;str&#39;, 2)); // Error</code></pre>
<h3 id="typescript를-사용하는-이유">Typescript를 사용하는 이유</h3>
<p>타입스크립트는 다음 특징을 갖는다.</p>
<ul>
<li><p><strong>컴파일 언어</strong></p>
<ul>
<li>자바스크립트는 동적 타입의 인터프리터 언어로 런타임에서 오류를 발견할 수 있다. 반면, 타입스크립트는 정적 타입의 컴파일 언어로 컴파일러나 바벨을 통해 자바스크립트 코드로 변환된다.</li>
<li>타입스크립트는 코드 작성 단계에서 타입을 체크하여 오류를 확인할 수 있고, 미리 타입을 결정하기 때문에 실행 속도가 매우 빠르다.</li>
</ul>
</li>
<li><p><strong>자바스크립트 슈퍼셋</strong> : 타입스크립트는 자바스크립트의 기본 문법에 타입스크립트 문법을 추가한 언어다.</p>
</li>
<li><p><strong>객체지향 지원</strong> : 타입스크립트는 ES6에서 새롭게 사용된 문법을 포함하고, 객체지향 프로그래밍 패턴을 제공한다.</p>
</li>
</ul>
<p>이제 타입스크립트를 사용하는 이유에 대해 알아보자.</p>
<ul>
<li>높은 수준의 코드 탐색과 디버깅 : 타입스크립트는 코드에 목적을 명시하고 목적에 맞지 않는 타입의 변수나 함수들에서 에러를 발생시켜 버그를 사전에 제거한다.</li>
<li>자바스크립트 호환 : 타입스크립트는 자바스크립트와 호환되기 때문에 자바스크립트를 사용할 수 있는 곳이라면 어디서든 사용할 수 있다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/kimyu0218_/post/ea5ccddb-acfd-48ed-80d3-43ec38ab42fa/image.png" alt="typescript01"></p>
<hr>
<h3 id="typescript-기본-문법">Typescript 기본 문법</h3>
<p>타입스크립트는 다양한 데이터 타입을 제공한다.
<code>Boolean</code>, <code>Number</code>, <code>String</code>, <code>Object</code>, <code>Array</code>, <code>Tuple</code>, <code>Enum</code>, <code>Any</code>, <code>Void</code>, <code>Null</code>, <code>Undefined</code>, <code>Never</code></p>
<blockquote>
<p><code>never</code>는 특정 값이 절대 발생할 수 없음을 의미하고, 주로 함수의 리턴 타입으로 사용된다. 함수의 리턴 타입으로 <code>never</code>가 사용될 경우, 항상 오류를 출력하거나 리턴값을 절대로 반환하지 않음을 의미한다. (=무한루프)</p>
</blockquote>
<ul>
<li>변수에 타입 설정하기
```typescript
let str:string = &#39;hi&#39;;
let num:number = 100;
/*
num = &#39;20&#39;;
main.ts(3,1): error TS2322: Type &#39;string&#39; is not assignable to type &#39;number&#39;.</li>
<li>/</li>
</ul>
<p>let arr:Array = [1, 2, 3];
let arr2:number[] = [1, 2, 3];
let arr3:Array<number> = [1, 2, 3];</p>
<p>let obj:object = {};
let obj2:{name:string, age:number} = {
  name: &#39;John&#39;,
  age: 30
};</p>
<pre><code>- 함수에 타입 설정하기
```typescript
const add = (a:number, b:number):number =&gt; a+b;
// &lt;parameter&gt;? : 옵션
const option = (a:string, b?:string, c?:string) =&gt; { console.log(a); };</code></pre><p><strong>인터페이스</strong> : (기본 틀) 타입을 정의한 규칙</p>
<pre><code class="language-typescript">interface User{
  name: string;
  age: number;
}

var user:User = {
  name: &#39;John&#39;,
  age: 30
};</code></pre>
<p><strong>제네릭</strong> : 데이터 타입을 일반화하는 것
제네릭은 클래스 내부에서 사용할 데이터 타입을 외부에서 지정하는 기법이다.</p>
<pre><code class="language-typescript">// CommonJS
function getText&lt;T&gt; (text:T):T {
    return text;
}
// ES6 ver
const getText = &lt;T,&gt;(text:T) =&gt; text;

console.log(getText&lt;string&gt;(&#39;hi&#39;));
console.log(getText&lt;number&gt;(10));
console.log(getText&lt;boolean&gt;(true));</code></pre>
<p>💥 <strong>Tuple 만들기</strong>
튜플은 배열과 유사하나 길이가 고정되고, 각 요소의 타입이 지정되어 있다는 점에서 차이가 있다.</p>
<pre><code class="language-typescript">let tuple:[string, number] = [&#39;hi&#39;, 10];</code></pre>
<p>💥 <strong>Enum</strong>
<code>enum</code>은 Java와 같은 다른 언어에서 흔하게 쓰이는 타입으로, 정수형 상수의 집합이다. <code>enum</code>은 상수에 이름을 붙여 코드의 가독성을 높여준다.</p>
<pre><code class="language-typescript">enum Avengers { IronMan, CaptainAmerica, Thor};
let hero:Avengers = Avengers.IronMan; // == Avengers[0]
console.log(hero); // 0</code></pre>
<p><code>enum</code>의 첫번째 상수값은 0부터 시작하며 사용자 편의로 변경하여 사용할 수도 있다.</p>
<pre><code class="language-typescript">enum Avengers { IronMan = 3, CaptainAmerica, Thor};</code></pre>
<p>💥 <strong>any</strong>
<code>any</code>는 단어 의미 그대로 모든 타입에 대해 허용한다. 기존에 자바스크립트로 구현되어 있는 코드에 타입스크립트를 점진적으로 적용할 때 활용하면 좋다.</p>
<pre><code class="language-typescript">let allType:any = &#39;hi&#39;;
allType = 10;</code></pre>
<hr>
<p>참고자료
<a href="https://www.samsungsds.com/kr/insights/typescript.html">SAMSUNG SDS : 타입스크립트</a>
<a href="https://joshua1988.github.io/ts/guide/basic-types.html">타입스크립트 핸드북</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS] Object 대신 Map을 사용해야 하는 이유]]></title>
            <link>https://velog.io/@kimyu0218_/JS-Object-%EB%8C%80%EC%8B%A0-Map%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%95%BC-%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0</link>
            <guid>https://velog.io/@kimyu0218_/JS-Object-%EB%8C%80%EC%8B%A0-Map%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%95%BC-%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0</guid>
            <pubDate>Fri, 18 Aug 2023 10:35:25 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>자바스크립트는 <code>Object</code>로 key-value 값을 갖는 자료 구조를 만들 수 있다. 하지만 자바스크립트에는 별도의 해시 자료 구조인 <code>Map</code>과 <code>Set</code>이 존재한다. <code>Map</code>과 <code>Set</code>이 존재하는 이유에 대해 알아보자.</p>
</blockquote>
<h3 id="hash-table">Hash Table</h3>
<p>해시 테이블은 key에 value를 매핑할 수 있는 자료구조다. 자바스크립트에서 해시 테이블은 <code>Object</code>, <code>Map</code>, <code>Set</code>이 있다.</p>
<blockquote>
<p>해시 테이블은 key-value 형태의 자료구조다.</p>
</blockquote>
<hr>
<h3 id="object">Object</h3>
<p>객체를 생성하는 방법은 다음 두 가지다.</p>
<ul>
<li>new 생성자 사용하기<pre><code class="language-javascript">let user = Object();</code></pre>
</li>
<li>중괄호 사용하기 (리터럴 방식)<pre><code class="language-javascript">let user = {};</code></pre>
자바스크립트에서 객체의 사용이 빈번하여 Object에 익숙할 것이기 때문에 별도의 설명 없이 코드를 작성하겠다.<pre><code class="language-javascript">// key: value
let user = {
name: &#39;John&#39;,
age: 30
};
</code></pre>
</li>
</ul>
<p>// add property
user.isAdmin = true;</p>
<p>// delete property
delete user.age;</p>
<p>// <property> in <object>
console.log(&#39;name&#39; in user); // true
console.log(&#39;grade&#39; in user); // false</p>
<pre><code>&gt; 복수의 단어를 key로 사용하려는 경우, 따옴표로 묶어서 사용한다.
```javascript
let user = {
  name: &#39;John&#39;,
  age: 30,
  &#39;likes birds&#39;: true
};</code></pre><p>💥 <strong>const를 사용했는데 왜 수정이 가능한 걸까?</strong></p>
<pre><code class="language-javascript">const user = {
  name: &#39;John&#39;;
  ...
};
user.name = &#39;Alice&#39;;</code></pre>
<p>객체는 <code>const</code>로 선언해도 수정이 가능하다. <code>user</code>의 값을 고정할 뿐, 그 내용은 고정하지 않기 때문이다. 따라서 <code>user = ...</code>를 전체적으로 수정할 때만 오류가 발생한다.</p>
<p>💥 <strong>대괄호 표기법이 존재하는 이유</strong></p>
<pre><code class="language-javascript">console.log(user.likes birds); // Error
console.log(user[&#39;likes birds&#39;]); // John</code></pre>
<p>점 표기법은 키가 유효한 변수 식별자인 경우에만 사용할 수 있다. 유효한 변수 식별자란 공백이 없고, 숫자로 시작하지 않으며 <code>_</code>와 <code>$</code>를 제외한 특수 문자가 없어야 한다.</p>
<p>하지만 대괄호 표기법은 키에 어떠한 문자가 있던지 상관없이 동작한다.</p>
<pre><code class="language-javascript">let key = &#39;name&#39;;
console.log(user.key); // undefined
console.log(user[key]); // John</code></pre>
<p>이외에도 대괄호 표기법은 key에 변수를 사용할 수 있다.</p>
<hr>
<h3 id="map">Map</h3>
<p>맵은 key가 있는 데이터를 저장한다는 점에서 객체(<code>Object</code>)와 유사하지만 <em>다양한 자료형을 허용한다</em> 는 점에서 차이가 있다.</p>
<pre><code class="language-javascript">let map = new Map(); // 맵 객체 생성

// map.set(key, value)
map.set(&#39;alice&#39;, 1);

// map.get(key)
console.log(map.get(&#39;alice&#39;)); // 1
console.log(map.get(&#39;bob&#39;)); // undefined

// map.has(key)
console.log(map.has(&#39;alice&#39;)); // true
console.log(map.has(&#39;bob&#39;)); // false

// map.delete(key)
map.delete(&#39;alice&#39;);

// map.clear()
map.clear();

// map.size
console.log(map.size); // 0</code></pre>
<hr>
<h3 id="map-vs-object">Map vs. Object</h3>
<p>앞서 언급했듯이 <code>Map</code>은 키에 다양한 자료형을 허용한다는 점에서 <code>Object</code>와 차이가 있다. <code>Object</code>의 키는 문자열이나 <code>Symbol</code>만 가능하다.</p>
<pre><code class="language-javascript">let obj = { };
let user1 = {
    name: &#39;John&#39;,
    age: 30
};

let user2 = {
    name: &#39;Alice&#39;,
    age: 20
};

obj[user1] = 1;
obj[user2] = 2;

for(let key in obj) {
    console.log(`${key} : ${obj[key]}`);
}
/*
    [object Object] : 2
*/</code></pre>
<p>객체의 키로 <code>user1</code>, <code>user2</code>를 저장했음에도 불구하고, 키값이 <code>[object Object]</code>로 변환되어 저장된다. 게다가 <code>user1</code>과 <code>user2</code>를 구분하지 못한다.</p>
<pre><code class="language-javascript">let map = new Map();

...
map.set(user1, 1);
map.set(user2, 2);

for(let key of map.keys()) {
    console.log(key);
}
/*
{ name: &#39;John&#39;, age: 30 }
{ name: &#39;Alice&#39;, age: 20 }
*/</code></pre>
<p>반면, 맵을 사용하면 객체의 값이 유지된다.</p>
<p>💥 <strong>Map을 객체처럼 사용하기?</strong>
맵도 객체처럼 점 표기법, 대문자 표기법을 이용하여 프로퍼티를 설정할 수 있다. 하지만 이는 맵을 사용하는 올바른 방법이 아니다. </p>
<pre><code class="language-javascript">let map = new Map();

map[&#39;1&#39;] = 1;
map[&#39;2&#39;] = 2;

for(let key in map) {
    console.log(`${key} : ${map[key]}`);
}
/*
1 : 1
2 : 2
*/
console.log(map.keys()); // [Map Iterator] {  }</code></pre>
<p>맵에 객체 문법을 사용하면 해당 key-value는 객체 문법을 통해서만 조회할 수 있다.</p>
<p>💥 <strong>Map은 주소값을 가리킬까?</strong>
자바스크립트에서 데이터 타입은 크게 두 가지로 분리된다.</p>
<ul>
<li><strong>Primitive</strong> : 값을 그대로 할당
ex. <code>Number</code>, <code>String</code>, <code>Boolean</code>, <code>null</code>, <code>undefined</code> </li>
<li><strong>Reference</strong> : 값이 저장된 주소값을 할당
ex. <code>Object</code>, <code>Array</code>, <code>Function</code>, <code>Map</code>, <code>Set</code> 등</li>
</ul>
<pre><code class="language-javascript">let obj1 = {
    name: &#39;John&#39;,
    age: 30,
    friends: [&#39;Alice&#39;, &#39;Bob&#39;]
};

let obj2 = {
    name: &#39;John&#39;,
    age: 30,
    friends: [&#39;Alice&#39;, &#39;Bob&#39;]
};

console.log(obj1 === obj2); // false</code></pre>
<p>참조형 타입은 값이 저장된 주소값을 할당하기 때문에 서로 다른 두 객체의 내용이 같다하더라도 주소가 다르면 서로 다른 객체로 판단한다.</p>
<p>이제 <code>Map</code>에 저장되는 객체가 주소값을 가리키는지 확인해보자.</p>
<pre><code class="language-javascript">let map = new Map();
...
map.set(obj1, 1);
console.log(map.get(obj2)); // undefined</code></pre>
<p>위의 코드는<code>undefined</code>를 출력한다. <code>obj1</code>과 <code>obj2</code>의 내용이 같아도 주소값을 참조하기 때문에 서로 다른 객체로 판단한다. </p>
<pre><code class="language-javascript">obj1.name = &#39;Alice&#39;;
console.log(map.get(obj1)); // 1</code></pre>
<p><code>obj1</code>의 프로퍼티를 변경해도 주소값을 가리키기 때문에 <code>obj1</code>에 해당하는 value 값을 반환한다. </p>
<hr>
<p>참고자료
<a href="https://ko.javascript.info/object">JAVASCRIPT.INFO : object</a>
<a href="https://ko.javascript.info/map-set">JAVASCRIPT.INFO : map &amp; set</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS] 자바스크립트 엔진]]></title>
            <link>https://velog.io/@kimyu0218_/JS-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%97%94%EC%A7%84</link>
            <guid>https://velog.io/@kimyu0218_/JS-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%97%94%EC%A7%84</guid>
            <pubDate>Fri, 18 Aug 2023 07:21:06 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>자바스크립트는 싱글 스레드 언어다. 싱글 스레드란 한 번에 하나의 작업만 수행할 수 있다는 의미다. 그러나 네트워크 요청이나 타이머와 같은 작업을 멀티로 처리해야 하는 경우가 많다. 싱글 스레드로 브라우저가 한 번에 하나의 동작을 수행하게 되면, 파일을 다운로드 받는 동안 웹서핑도 못하고 대기해야 한다.</p>
</blockquote>
<h3 id="javascript-engine">Javascript Engine</h3>
<p>자바스크립트 엔진은 자바스크립트 코드를 실행하는 프로그램이다. 여러 목적으로 자바스크립트 엔진을 사용하지만, 주로 웹 브라우저에서 사용된다.</p>
<ul>
<li>Safari : Nitro</li>
<li>Chrome : V8</li>
<li>Firefox : SpiderMonkey (최초의 자바스크립트 엔진)</li>
</ul>
<blockquote>
<p>엔진의 기본 동작 원리<br>1. 엔진이 스크립트를 읽는다. (=파싱)<br>2. 스크립트를 기계어로 전환한다. (=컴파일)<br>3. 기계어로 전환한 코드를 실행한다.</p>
</blockquote>
<hr>
<h3 id="v8-엔진">V8 엔진</h3>
<p>V8은 웹 브라우저를 만드는 데 기반을 제공하는 오픈소스 자바스크립트 엔진이다. 구글 크롬 브라우저와 안드로이드 브라우저에 탑재되어 있다. </p>
<p>엔진의 주요 구성요소는 다음과 같다.</p>
<ol>
<li><strong>Memory Heap</strong> : 메모리 할당이 일어나는 곳</li>
<li><strong>Call Stack</strong> : 코드 실행에 따라 호출 스택이 쌓이는 곳
<img src="https://joshua1988.github.io/images/posts/web/translation/how-js-works/js-engine-structure.png" alt="javascript engine component"></li>
</ol>
<p>그렇다면 <code>setTimeout()</code>과 같은 내장 api는 어디에 위치하는 걸까?
<img src="https://joshua1988.github.io/images/posts/web/translation/how-js-works/js-engine-runtime.png" alt="javascript engine structure">
위의 그림처럼 자바스크립트 엔진 외에도 자바스크립트에 관여하는 다른 요소들이 존재한다. <code>setTimeout()</code>, <code>Ajax</code>와 같은 api들은 Web API로, 엔진 밖에 별도로 저장된다.</p>
<p>이제 남은 요소들을 알아보자. </p>
<ul>
<li><strong>Callback Queue</strong> : 비동기적 작업이 끝나면 실행되는 함수들이 대기하는 곳</li>
<li><strong>Event Loop</strong> : 비동기 함수들을 적절한 시점에 실행시키는 관리자</li>
</ul>
<hr>
<h3 id="call-stack">Call Stack</h3>
<p>콜 스택은 프로그램에서 현재 실행 중인 서브루틴에 관한 정보를 저장하는 스택 자료구조다. 자바스크립트는 싱글 스레드 기반 언어이기 때문에 콜 스택이 하나다.</p>
<p>콜 스택을 사용하는 주요 목적은 현재 실행 중인 서브루틴의 실행이 끝났을 때, 제어를 반환할 지점을 보관하기 위함이다. </p>
<pre><code class="language-javascript">const asyncFunc = () =&gt; {
  setTimeout(() =&gt; {
      console.log(&#39;second&#39;);
  }, 5000);
};

const foo = () =&gt; {
  console.log(&#39;first&#39;);  
};

const bar = () =&gt; {
  console.log(&#39;third&#39;);
};

foo();
asyncFunc();
bar();</code></pre>
<p>위의 코드를 그림으로 표현해보자. 비동기 함수인 <code>setTimeout()</code>은 Web API로 이동하여 일정 시간을 대기한다. 시간이 만료되면 Callback Queue로 이동하였다가 Event Loop에 의해 실행된다.</p>
<p><img src="https://velog.velcdn.com/images/kimyu0218_/post/64fa5b93-880b-47d9-8dae-7361e26a5a54/image.gif" alt="gif"></p>
<hr>
<h3 id="callback-queue">Callback Queue</h3>
<p>콜백 큐는 비동기적인 작업이 끝난 함수들이 대기하는 공간이다. 콜백 큐는 세부적으로 task queue와 microtask queue로 나뉜다.</p>
<ul>
<li><strong>Task Queue</strong> : <code>setTimeout()</code>, <code>setInterval()</code>, <code>fetch()</code> 등 비동기로 처리되는 함수들의 콜백 함수가 들어간다.</li>
<li><strong>Microtask Queue</strong> : <code>promise.then</code>, <code>process.nextTick</code> 등 우선순위가 높은 함수들의 콜백 함수가 들어간다.</li>
</ul>
<hr>
<h3 id="concurrency-vs-parallelism">Concurrency vs. Parallelism</h3>
<ul>
<li><strong>동시성</strong> : (Concurrency) 동시에 실행되는 것 같이 보이는 것</li>
<li><strong>병렬성</strong> : (Parallelism) 실제로 동시에 여러 작업이 처리되는 것</li>
</ul>
<p><img src="https://1.bp.blogspot.com/-TKQSNUbkqNs/W_UCljTAeSI/AAAAAAAAI3Y/FK_seKThNSghF1eNh0wkb7fwTcPyxanyQCLcBGAs/s1600/1.jpg" alt="concurrency vs. parallelism"></p>
<blockquote>
<p>자바스크립트는 호출 스택이 하나인 싱글 스레드 언어이기 때문에 병렬성이 아닌 동시성을 지원한다.</p>
</blockquote>
<hr>
<p>참고자료
<a href="https://ko.wikipedia.org/wiki/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8_%EC%97%94%EC%A7%84">wikipedia : 자바스크립트 엔진</a>
<a href="https://ko.wikipedia.org/wiki/%EC%BD%9C_%EC%8A%A4%ED%83%9D">wikipedia : 콜 스택</a>
<a href="https://joshua1988.github.io/web-development/translation/javascript/how-js-works-inside-engine/">자바스크립트의 동작 원리</a>
<a href="https://www.codeproject.com/Articles/1267757/Concurrency-vs-Parallelism">Concurrency vs Parallelism</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS] 비동기 2편: async/await]]></title>
            <link>https://velog.io/@kimyu0218_/JS-%EB%B9%84%EB%8F%99%EA%B8%B0-2%ED%8E%B8-asyncawait</link>
            <guid>https://velog.io/@kimyu0218_/JS-%EB%B9%84%EB%8F%99%EA%B8%B0-2%ED%8E%B8-asyncawait</guid>
            <pubDate>Thu, 17 Aug 2023 17:28:38 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="velog.io/@kimyu0218_/JS-%EB%B9%84%EB%8F%99%EA%B8%B0-1%ED%8E%B8-%EC%BD%9C%EB%B0%B1%ED%95%A8%EC%88%98%EC%99%80-Promise">비동기 1편</a>에서 핸들러를 중첩적으로 사용하여 프로미스 지옥이 발생했다. <code>async/await</code>으로 프로미스 지옥을 해결해보자.</p>
</blockquote>
<h3 id="asyncawait">async/await</h3>
<p><code>async</code>와 <code>await</code>은 기존의 <code>Promise</code>를 보다 간결하게 작성할 수 있도록 도와주는 문법이다. 눈에 보이는 문법만 다를 뿐, 내부적으로는 프로미스 방식으로 비동기 작업을 처리한다.</p>
<p><code>async</code>는 함수 앞에, <code>await</code>은 비동기로 처리되는 부분 앞에 붙인다. <code>async</code>는 <code>await</code>을 사용하기 선언문으로 함수 내에서 <code>await</code>을 사용할 수 있도록 만든다.</p>
<blockquote>
<p><code>await</code>을 사용하기 위해서는 <code>async</code>가 필요하다. <code>async</code>가 없는 함수에서 <code>await</code>을 사용하면 에러가 발생한다.</p>
</blockquote>
<p><code>await</code>은 비동기 처리가 완료될 때까지 코드 실행을 일시 중지하고 기다린다. 즉, 비동기 작업을 동기적으로 처리할 수 있도록 도와준다.</p>
<pre><code class="language-javascript">const delay = (sec) =&gt; {
    return new Promise((resolve) =&gt; {
        resolve(sec);
    }, sec);
};

const asyncFunc = async () =&gt; {
  await delay(1000);
  console.log(&#39;1 sec passed&#39;);
};

asyncFunc();</code></pre>
<p>💥 <strong>프로미스 지옥 해결하기</strong></p>
<pre><code class="language-javascript">const write = (path, data) =&gt; {
    return new Promise((resolve, reject) =&gt; {
        fs.writeFile(path, data, (err) =&gt; {
            if(err) {
                reject();
            }
            resolve();
        });
    });
};
const read = (path) =&gt; {
    return new Promise((resolve, reject) =&gt; {
       fs.readFile(path, (err, data) =&gt; {
          if(err) {
              reject();
          }
          resolve(data.toString());
       });
    });
}

write(path, data).then(() =&gt; {
        read(path).then((data) =&gt; {
                write(path, data).then(() =&gt; {
                        read(path).then(...);
                });
        });
});</code></pre>
<p>위의 코드는 <code>then</code> 핸들러를 중첩적으로 사용하여 가독성이 떨어진다. 프로미스 지옥을 <code>async</code>와 <code>await</code>을 사용하여 해결해보자.</p>
<pre><code class="language-javascript">const asyncFunc = async () =&gt; {
    await write(path, data);
    const d1 = await read(path);
    await write(path, d1);
    const d2 = await read(path);
    ...
}

asyncFunc();</code></pre>
<p>파일 입출력을 동기적으로 처리하기 위해 <code>await</code>을 사용했고, <code>await</code>을 사용하기 위한 <code>async</code> 키워드를 함수 앞에 붙여준다. 파일을 작성한 후에 파일 내용을 읽어오고, 읽어온 내용을 다시 파일에 쓴다. 코드가 간결해지고 가독성이 높아진 것을 확인할 수 있다.</p>
<hr>
<p>참고자료
<a href="https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-%EB%B9%84%EB%8F%99%EA%B8%B0%EC%B2%98%EB%A6%AC-async-await">자바스크립트 async/await</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS] 비동기 1편: 콜백함수와 Promise]]></title>
            <link>https://velog.io/@kimyu0218_/JS-%EB%B9%84%EB%8F%99%EA%B8%B0-1%ED%8E%B8-%EC%BD%9C%EB%B0%B1%ED%95%A8%EC%88%98%EC%99%80-Promise</link>
            <guid>https://velog.io/@kimyu0218_/JS-%EB%B9%84%EB%8F%99%EA%B8%B0-1%ED%8E%B8-%EC%BD%9C%EB%B0%B1%ED%95%A8%EC%88%98%EC%99%80-Promise</guid>
            <pubDate>Thu, 17 Aug 2023 16:48:32 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>자바스크립트의 비동기 처리 방식은 총 세 가지다.<br><br>1. 콜백(callback) : 파라미터로 함수를 전달받아 함수 내부에서 실행하는 함수<br>2. <strong>Promise</strong> : 자바스크립트에서 제공하는 비동기를 간편하게 처리할 수 있도록 도와주는 객체 <br>3. <strong>async/await</strong> : 기존의 Promise보다 더 간결하게 작성할 수 있는 문법</p>
</blockquote>
<h3 id="callback">callback</h3>
<pre><code class="language-javascript">setTimeout(() =&gt; {
  console.log(&#39;This is callback&#39;);
}, 1000);</code></pre>
<p>콜백함수는 함수의 파라미터로 함수 자체를 넘겨, 함수 내에서 매개변수 함수를 실행하는 기법이다.</p>
<pre><code class="language-javascript">const fs = require(&#39;fs&#39;);
fs.writeFile(&#39;test.txt&#39;, &#39;async function&#39;, (err) =&gt; {
    if(!err) {
        fs.readFile(&#39;test.txt&#39;, (err, data) =&gt; {
           if(!err) {
               console.log(data.toString()); // async function
           }
        });
    }
});</code></pre>
<p><code>fs</code>는 NodeJS에서 파일 입출력 처리를 담당하는 모듈이다. <code>fs</code> 모듈은 비동기 api와 동기 api를 모두 제공한다. 위의 코드의 <code>writeFile</code>과 <code>readFile</code>은 대표적인 비동기 api다.</p>
<p><code>readFile</code>은 <code>writeFile</code>의 파라미터로 넘겨 <code>writeFile</code>의 콜백함수로 사용된다. 즉, <code>writeFile</code>의 실행이 끝난 뒤에 <code>readFile</code>이 실행된다.</p>
<p>💥<strong>콜백 지옥?</strong></p>
<pre><code class="language-javascript">fs.writeFile(&#39;test.txt&#39;, &#39;data&#39;, (err) =&gt; {
    if(!err) {
        fs.readFile(&#39;test.txt&#39;, (err, data) =&gt; {
            if(!err) {
                fs.writeFile(&#39;test.txt&#39;, data.toString(), (err) =&gt; {
                   if(!err) {
                       fs.readFile(&#39;test.txt&#39;, (err, data) =&gt; {
                          if(!err) { ... } 
                       });
                   } 
                });
            }
        });
    }
});</code></pre>
<p>콜백 함수를 반복적으로 사용하면 콜백 지옥에 빠지게 된다. 콜백 지옥은 콜백 함수를 중첩하여 깊이가 깊어져 가독성이 떨어지는 현상을 말한다. 그렇다면 비동기 작업을 좀 더 간단하게 처리하는 방법은 없을까?</p>
<hr>
<h3 id="promise">Promise</h3>
<p>프로미스는 자바스크립트에서 비동기 처리를 위해 제공하는 객체로, 비동기 작업의 성공/실패의 결과값을 나타낸다. 프로미스를 사용하면 앞서 언급한 콜백 지옥 현상을 해결할 수 있다.</p>
<pre><code class="language-javascript">const promise = new Promise((resolve, reject) =&gt; { });</code></pre>
<p><code>Promise</code> 객체는 <code>new</code> 키워드와 생성자를 통해 생성한다. 파라미터로 <code>resolve</code>, <code>reject</code>를 가질 수 있다. <code>resolve</code>와 <code>reject</code>는 자바스크립트에서 제공하는 콜백함수다.</p>
<ul>
<li><code>resolve(val)</code> : 작업을 성공적으로 처리한 후 <code>val</code>와 함께 호출</li>
<li><code>reject(err)</code> : 에러가 발생하는 경우 에러 객체를 나타내는 <code>err</code>과 함께 호출</li>
</ul>
<blockquote>
<p>프로미스는 <code>resolve()</code>나 <code>reject()</code> 중 하나를 반드시 호출해야 한다. 변경된 상태는 다시 수정할 수 없다.</p>
</blockquote>
<p><strong>Promise 상태</strong>
프로미스는 비동기 작업의 결과를 약속하므로 다음 세 가지 상태 중 하나를 가진다.</p>
<ol>
<li>Pending : (대기) 비동기 작업이 진행중인 상태</li>
<li>Fulfilled : (이행) 비동기 작업이 성공적으로 처리된 상태</li>
<li>Rejected : (거부) 비동기 작업 처리에 실패한 경우</li>
</ol>
<pre><code class="language-javascript">const promise = new Promise((resolve, reject) =&gt; {
  setTimeout(() =&gt; { // (asynchronous function)
    resolve(true); // success
  }, 1000);
});</code></pre>
<p><code>promise</code>는 비동기 함수 <code>setTimeout()</code>를 포함한 비동기 함수다. 1초가 지나기전에 <code>promise</code>를 출력하면 <code>pending</code> 상태를 갖는다. 1초가 지나면 <code>resolve()</code> 메서드에 의해 <code>fulfilled</code> 상태로 변한다. <code>reject()</code> 메서드를 사용하면<code>rejected</code> 상태로 만들 수 있다.</p>
<p><strong>Promise 핸들러</strong>
프로미스는 핸들러를 이용하여 콜백 지옥을 해결한다. 프로미스 객체의 작업 성공 유무를 핸들러를 통해 받아 후속 작업을 처리한다.</p>
<ol>
<li><code>.then()</code> : 프로미스가 fulfilled 되었을 때 실행</li>
<li><code>.catch()</code> : 프로미스가 rejected 되었을 때 실행</li>
<li><code>.finally()</code> : 프로미스 상태와 무관하게 실행</li>
</ol>
<pre><code class="language-javascript">promise
    .then((val) =&gt; { console.log(val); }); // fulfilled
    .catch((err) =&gt; { console.log(err); }); // rejected
    .finally(() =&gt; { });</code></pre>
<p><strong>Promise 정적 메서드</strong>
프로미스 객체는 생성자 함수 외에도 여러 정적 메서드를 제공한다. 정적 메서드는 객체를 초기화하거나 생성하지 않고 바로 사용할 수 있으므로 비동기 작업을 수행하지 않는 함수에서도 프로미스를 활용할 수 있다.</p>
<ul>
<li><code>Promise.resolve()</code></li>
<li><code>Promise.reject()</code></li>
<li><code>Promise.all()</code> : 여러 개의 프로미스 요소들을 한꺼번에 처리해야 할 때 사용한다. 모든 프로미스가 완료될 때까지 기다린 후, 모든 프로미스가 완료되면 <code>then</code> 핸들러를 실행한다.</li>
</ul>
<pre><code class="language-javascript">const promise = (sec) =&gt; {
    return new Promise((resolve) =&gt; {
        setTimeout(() =&gt; {
            resolve(sec);
        }, sec);
    });
}

const promises = [promise(1000), promise(2000), promise(5000)];

Promise.all(promises)
    .then((data) =&gt; {
       console.log(data); // [1000, 2000, 5000]
    });</code></pre>
<ul>
<li><code>Promise.allSettled()</code> : <code>Promise.all()</code>의 업그레이드 버전으로, 모든 프로미스가 처리되면 각각의 상태와 값을 모아놓은 배열을 반환한다.</li>
</ul>
<pre><code class="language-javascript">Promise.allSettled(promises)
    .then((data) =&gt; {
       console.log(data);
    });
/*
[
  { status: &#39;fulfilled&#39;, value: 1000 },
  { status: &#39;fulfilled&#39;, value: 2000 },
  { status: &#39;fulfilled&#39;, value: 5000 }
]
*/</code></pre>
<ul>
<li><p><code>Promise.any()</code> : <code>Promise.all()</code>의 반대 버전으로, 주어진 프로미스 중 하나라도 fulfilled 되면 바로 결과를 반환한다. 만약 모든 프로미스가 rejected 된다면 거부 프로미스를 반환한다.</p>
</li>
<li><p><code>Promise.race()</code> : <code>Promise.any()</code>와 유사하지만 작업의 성공 여부와 상관없이 무조건 처리가 끝난 프로미스 결과를 반환한다.</p>
</li>
</ul>
<p>💥 <strong>콜백 지옥 해결하기</strong>
앞에 등장했던 콜백 지옥 코드를 핸들러를 사용해 해결해보자.</p>
<pre><code class="language-javascript">const write = (path, data) =&gt; {
    return new Promise((resolve, reject) =&gt; {
        fs.writeFile(path, data, (err) =&gt; {
            if(err) {
                reject();
            }
            resolve();
        });
    });
};
const read = (path) =&gt; {
    return new Promise((resolve, reject) =&gt; {
       fs.readFile(path, (err, data) =&gt; {
          if(err) {
              reject();
          }
          resolve(data.toString());
       });
    });
}

write(path, data).then(() =&gt; {
        read(path).then((data) =&gt; {
                write(path, data).then(() =&gt; {
                        read(path).then(...);
                });
        });
});</code></pre>
<p><code>writeFile</code>과 <code>readFile</code>을 프로미스 객체를 반환하는 함수로 분리하고 <code>then</code> 핸들러를 이용하여 후속 작업을 수행한다.</p>
<p>하지만 위의 코드도 <code>then</code> 핸들러를 중첩적으로 사용하여 코드의 가독성이 떨어진다. 콜백 지옥에 이은 프로미스 지옥이 등장했다. 프로미스 지옥을 해결할 방법은 없을까?</p>
<hr>
<p>참고자료
<a href="https://inpa.tistory.com/entry/%F0%9F%8C%90-js-async">자바스크립트 비동기</a>
<a href="https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-%EB%B9%84%EB%8F%99%EA%B8%B0%EC%B2%98%EB%A6%AC-Promise">자바스크립트 Promise</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Synchronous vs. Asynchronous]]></title>
            <link>https://velog.io/@kimyu0218_/JS-%EB%B9%84%EB%8F%99%EA%B8%B0%EC%99%80-Promise</link>
            <guid>https://velog.io/@kimyu0218_/JS-%EB%B9%84%EB%8F%99%EA%B8%B0%EC%99%80-Promise</guid>
            <pubDate>Thu, 17 Aug 2023 10:30:59 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>파일 입출력을 수행할 때, 다음 코드처럼 입출력이 제대로 동작하지 않았던 경험이 한 번쯤은 있을 것이다. <code>test.txt</code>에 문자열을 썼음에도 불구하고 <code>undefined</code>를 읽어온다.</p>
</blockquote>
<pre><code class="language-javascript">const fs = require(&#39;fs&#39;);
fs.writeFile(&#39;test.txt&#39;, &#39;async function&#39;, (err) =&gt; { });
const content = fs.readFile(&#39;test.txt&#39;, (err) =&gt; { });
console.log(content); // undefined</code></pre>
<h3 id="synchronous-vs-asynchronous">Synchronous vs. Asynchronous</h3>
<p><img src="https://miro.medium.com/v2/resize:fit:720/format:webp/1*Y41dOkntUbR3I4UCJBx9Xg.png" alt="async vs. sync 02"></p>
<blockquote>
<p>코드가 위에서 아래로 차례대로 동작하는 방식을 동기(synchrnous)라 한다.</p>
</blockquote>
<ul>
<li><p><strong>동기</strong> (Synchronous) : 작업 시간을 함께 맞춰 실행한다는 의미로, <em>요청한 작업에 대해 완료 여부를 따져 순차적으로 처리하는 방식</em> 이다.</p>
</li>
<li><p><strong>비동기</strong> (Asynchronous) : <em>요청한 작업에 대해 완료 여부를 따지지 않는 방식</em> 이다. 즉, 특정 작업의 완료를 기다리지 않기 때문에 다른 작업을 동시에 수행할 수 있다. 자바스크립트의 대표적인 비동기 함수로는 <code>setTimeout()</code>과 <code>fetch()</code>가 있다.</p>
</li>
</ul>
<p>💥 <strong>비동기 방식을 사용하는 이유</strong>
비동기 방식은 요청한 작업에 대해 완료 여부와 무관하게 다음 작업을 수행할 수 있는 방식이다. I/O 작업처럼 느린 작업이 발생할 때, 동시에 다른 작업을 처리하여 성능 향상에 도움을 준다.</p>
<pre><code>SYNCHRONOUS
   |--------A--------|
                     |--------B--------|</code></pre><pre><code>ASYNCHRONOUS
   |--------A--------|
         |--------B--------|</code></pre><p>동기 방식은 A 작업이 끝난 후에 B 작업을 수행하지만, 비동기 방식은 A 작업 수행 중에 새롭게 들어온 B 작업을 동시에 수행할 수 있다.</p>
<ul>
<li>동기 : 요청한 작업을 순차적으로 수행</li>
<li>비동기 : 요청한 작업을 순서와 관계없이 무작위로 수행</li>
</ul>
<hr>
<h3 id="blocking-vs-non-blocking">Blocking vs. Non-Blocking</h3>
<p>블로킹과 논블로킹은 다른 작업의 요청을 처리하기 위해 현재 작업을 block(차단/대기) 하는지에 따라 나뉜다.</p>
<blockquote>
<p>동기/비동기, 블로킹/논블로킹은 서로 다른 개념이다.</p>
</blockquote>
<ul>
<li>동기/비동기 : 작업의 순차적인 흐름 유무</li>
<li>블로킹/논블로킹 : 작업의 흐름 차단 유무</li>
</ul>
<p>💥 <strong>setTimeout()</strong></p>
<pre><code class="language-javascript">setTimeout(() =&gt; {
    console.log(&#39;1 sec passed&#39;);
}, 1000);
console.log(&#39;end&#39;);</code></pre>
<p><code>setTimeout()</code>은 자바스크림트의 내장 함수로 일정 시간을 기다린 후에 내부 함수를 실행한다. </p>
<p><code>setTimeout()</code>은 비동기 함수이자 논블로킹 함수다. 위의 코드는 <code>end</code>가 먼저 출력되고, 1초 후에 <code>1 sec passed</code>가 출력된다. 작업의 흐름이 순차적이지 않고 (=비동기) <code>console.log(&#39;end&#39;)</code>를 block하지 않는다. (=논블로킹)</p>
<hr>
<p>참고자료
<a href="https://inpa.tistory.com/entry/%F0%9F%91%A9%E2%80%8D%F0%9F%92%BB-%EB%8F%99%EA%B8%B0%EB%B9%84%EB%8F%99%EA%B8%B0-%EB%B8%94%EB%A1%9C%ED%82%B9%EB%85%BC%EB%B8%94%EB%A1%9C%ED%82%B9-%EA%B0%9C%EB%85%90-%EC%A0%95%EB%A6%AC">동기/비동기 &amp; 블로킹/논블로킹</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[함수형 프로그래밍 (FP)]]></title>
            <link>https://velog.io/@kimyu0218_/%ED%95%A8%EC%88%98%ED%98%95-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</link>
            <guid>https://velog.io/@kimyu0218_/%ED%95%A8%EC%88%98%ED%98%95-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</guid>
            <pubDate>Tue, 15 Aug 2023 15:02:11 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>함수형 프로그래밍은 기존의 절차적, 객체지향 프로그래밍과는 다른 방식이다. 자료 처리를 수학적 함수의 계산으로 취급하고 가변 데이터를 멀리하는 프로그래밍의 패러다임이다.</p>
</blockquote>
<h3 id="프로그래밍-패러다임">프로그래밍 패러다임</h3>
<p>프로그래밍 패러다임은 프로그래머에게 프로그래밍의 관점을 갖게 해주고, 결정하는 역할을 한다. 예를 들어 객체지향 프로그래밍은 프로그램을 상호작용하는 객체들의 집합으로 바라보도록 한다.</p>
<p>서로 다른 프로그래밍 언어는 서로 다른 프로그래밍 패러다임을 지원한다. 자바가 객체지향 프로그래밍을 지원하는 반면, 하스켈은 함수형 프로그래밍을 지원한다. 하지만 프로그래밍 언어가 여러 프로그래밍 패러다임을 지원하기도 한다.</p>
<ul>
<li><p>구조적 vs. 비구조적</p>
<blockquote>
<p>비구조적 프로그래밍은 하나의 연속된 덩어리에 모든 코드를 넣는 프로그래밍 패러다임으로, 코드의 특정 부분으로 건너뛰는 GOTO문에 의존한다. 구조화 되지 않은 원시 코드는 가독성이 떨어져 디버깅이 어렵다.</p>
</blockquote>
</li>
<li><p>명령형(how) vs. 선언형(what) : 명령형 프로그래밍은 <em>어떻게 할 것인가</em> 에, 선언형 프로그래밍은 <em>무엇을 할 것인가</em> 에 초점을 둔다.</p>
<blockquote>
<ul>
<li>명령형 : <code>반죽을 짜고, 돌돌 말아 토마토 소스를 넣고, 치즈를 넣고, 햄을 넣고, 파인애플을 넣고, 섭씨 200도로 예열된 돌가마에 구워...</code></li>
<li>선언형 : <code>하와이안 피자 주세요.</code></li>
</ul>
<hr>
<p><code>SELECT * FROM users WHERE country = &#39;서울&#39;</code><br>SQL 언어는 선언형 프로그래밍의 대표적인 예다. 서울에 사는 사용자들의 정보를 가져오는 <code>what</code>에 초점을 맞출 뿐, 테이블에서 어떻게 정보를 조회하는지 <code>how</code>에 대한 구현은 알 수 없다.</p>
</blockquote>
</li>
</ul>
<hr>
<h3 id="함수형-프로그래밍-⊂-선언형">함수형 프로그래밍 (⊂ 선언형)</h3>
<p>함수형 프로그래밍은 다음과 같은 특징들을 가진다.</p>
<ul>
<li>과정보다 결과에 초점을 둔다.</li>
<li>무엇(what)을 할 지를 강조한다.</li>
<li>데이터의 불변성을 유지해야 한다.</li>
<li>반복문 대신 재귀를 사용한다.</li>
</ul>
<hr>
<h3 id="1급-객체">1급 객체</h3>
<p>1급 객체는 다른 객체들에 적용할 수 있는 연산을 모두 지원하는 객체를 의미한다. 다음 조건을 모두 충족하면 1급 객체에 해당한다.</p>
<ol>
<li>변수나 데이터 구조에 할당할 수 있다.</li>
<li>객체의 파라미터로 전달할 수 있다.</li>
<li>객체의 반환값으로 사용할 수 있다.</li>
</ol>
<blockquote>
<p>자바스크립트에서 함수는 1급 객체다.</p>
</blockquote>
<hr>
<h3 id="참조-투명성과-부작용">참조 투명성과 부작용</h3>
<ul>
<li>참조 투명성 : 동일한 입력에 대해 항상 같은 값을 반환</li>
<li>부작용 : 함수가 결과값 이외의 다른 상태를 변경시키는 것
  ex. 전역/정적변수를 수정하거나 파라미터를 변경</li>
</ul>
<hr>
<h3 id="순수함수">순수함수</h3>
<p>순수함수는 동일한 입력에 대해 항상 같은 값을 반환한다. 함수 내부에서 인자의 값을 변경하거나 프로그램의 상태를 변경하지 않기 때문에 함수의 실행이 프로그램의 실행에 영향을 미치지 않는다.</p>
<pre><code class="language-javascript">const add = (a, b) =&gt; a + b;</code></pre>
<hr>
<h3 id="불변성">불변성</h3>
<p>함수형 프로그래밍은 데이터의 불변성을 유지해야 한다. 데이터의 변경이 필요한 경우, 원본 데이터를 변경하지 않고, 그 데이터의 복사본을 만들어 사용한다.</p>
<pre><code class="language-javascript">const add = (person) =&gt; { return {...person, age: person.age+1}; };</code></pre>
<hr>
<h3 id="선언형-함수">선언형 함수</h3>
<p>선언형 함수는 어떻게 할 것인가보다 무엇을 할 것인가에 초점을 맞춘다. </p>
<pre><code class="language-javascript">const multifly = (nums, n) =&gt; {
  return nums.map((num) =&gt; num * n);
};</code></pre>
<hr>
<h3 id="고차함수">고차함수</h3>
<p>고차함수는 함수를 파라미터로 전달받거나 결과로 반환하는 함수다. </p>
<ul>
<li>함수를 파라미터로 전달하는 경우<pre><code class="language-javascript">const double = (num) =&gt; num * 2;
const highDouble = (func, num) =&gt; func(num);</code></pre>
</li>
<li>함수를 리턴하는 경우<pre><code class="language-javascript">const sub = (a) =&gt; (b) =&gt; a - b;
console.log(sub(2)(1)); // 1</code></pre>
</li>
<li>함수를 파라미터로 받고, 함수를 리턴하는 경우<pre><code class="language-javascript">const double = (num) =&gt; num * 2;
const doubleSub = (a, func) =&gt; (b) =&gt; a - func(b);
console.log(doubleSub(2, double)(1)); // 0</code></pre>
</li>
</ul>
<hr>
<h3 id="javascript-array-고차함수">Javascript Array 고차함수</h3>
<ul>
<li><p><code>forEach()</code> : for문을 대체하는 고차함수 (<em>리턴값 X</em> )
```javascript
/*
  item :현재 요소
  index : 배열 인덱스 (생략 가능)
  thisArg : 콜백함수에서 this로 사용할 값 (생략 가능)</p>
<p>  배열의 각 요소에 대해 오름차순으로 콜백함수 실행</p>
</li>
<li><p>/
arr.forEach((item) =&gt; { });</p>
<pre><code>```javascript
// num 배열의 요소 합 구하기
const num = [1, 2, 3, 4, 5];
let total = 0;
num.forEach((item) =&gt; total += item);</code></pre></li>
<li><p><code>map()</code> : for문을 대체하는 고차함수 (<em>리턴값 O</em> )
```javascript
/*
  item : 현재 요소
  index :  배열 인덱스 (생략 가능)
  array : 참조한 배열 (생략 가능)
  thisArg : 콜백함수에서 this로 사용할 값 (생략 가능)</p>
<p>  배열 내의 모든 요소에 대해 주어진 함수를 호출한 결과를 모아 새로운 배열을 반환</p>
</li>
<li><p>/
arr.map((item) =&gt; { }, thisArg);</p>
<pre><code>```javascript
const num = [1, 2, 3, 4, 5];
const res = num.map((item) =&gt; {
   return (item % 2) ? &#39;even&#39; : &#39;odd&#39;;
});</code></pre><blockquote>
<p><code>forEach()</code>와 <code>map()</code>은 배열을 순회한다는 점에서 동일하지만 <code>forEach()</code>는 각 요소를 참조한 연산이 이루어지고 <code>map()</code>은 각 요소를 다른 값으로 맵핑한 새로운 배열이 반환된다는 점에서 차이가 있다.</p>
</blockquote>
</li>
<li><p><code>filter()</code> : 배열을 순회하며 콜백함수의 반환값이 <code>true</code>인 요소로만 구성된 새로운 배열 생성 (<em>리턴값 O</em> )</p>
<pre><code class="language-javascript">// num 배열의 요소에서 홀수만 뽑아내기
const num = [1, 2, 3, 4, 5];
const odd = num.filter((item) =&gt; item % 2);</code></pre>
</li>
<li><p><code>reduce()</code> : 배열의 각 요소에 대해 리듀서를 실행하고 결과값을 반환
```javascript
/*
  acc : 누산기, 순회하면서 계속 더해져서 합쳐지는 값
  curr : 현재 요소
  index : 배열 인덱스 (생략 가능)
  array : 참조한 배열 (생략 가능)
  initialValue : 콜백 최초 호출에서 acc 누산기에 제공하는 값 (생략 가능)</p>
</li>
<li><p>/
arr.reduce((acc, curr) =&gt; { }, initialValue);</p>
<pre><code>```javascript
// num 배열의 요소 합 구하기
const num = [1, 2, 3, 4, 5];
const res = num.reduce((acc, curr) =&gt; acc + curr, 0);</code></pre><blockquote>
<p><code>initialValue</code>가 있는 경우 <code>acc</code>값은 <code>initialValue</code>가 지정한 값이 된다. 하지만 <code>initalValue</code>가 없는 경우, <code>acc</code>은 배열의 첫번째 값이 된다.</p>
</blockquote>
</li>
</ul>
<hr>
<p>참고자료
<a href="https://ko.wikipedia.org/wiki/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D_%ED%8C%A8%EB%9F%AC%EB%8B%A4%EC%9E%84">wikipedia : 프로그래밍 패러다임</a>
<a href="https://ko.wikipedia.org/wiki/%ED%95%A8%EC%88%98%ED%98%95_%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D">wikipedia : 함수형 프로그래밍</a>
<a href="code-lab1.tistory.com/245">함수형 프로그래밍</a>
<a href="https://hanamon.kr/javascript-%EA%B3%A0%EC%B0%A8%ED%95%A8%EC%88%98%EC%99%80-%EC%BD%9C%EB%B0%B1-%EC%9D%BC%EA%B8%89%EA%B0%9D%EC%B2%B4%EB%9E%80/">고차함수와 1급 객체</a>
<a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array">mdn web docs : Array</a>
<a href="https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-%EB%B0%B0%EC%97%B4-%EA%B3%A0%EC%B0%A8%ED%95%A8%EC%88%98-%EC%B4%9D%EC%A0%95%EB%A6%AC-%F0%9F%92%AF-mapfilterfindreducesortsomeevery?search=%EC%84%A0%EC%96%B8%ED%98%95">javascript array 고차함수</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS] 클래스: ES6 Class와 프로토타입]]></title>
            <link>https://velog.io/@kimyu0218_/JS-%ED%81%B4%EB%9E%98%EC%8A%A4</link>
            <guid>https://velog.io/@kimyu0218_/JS-%ED%81%B4%EB%9E%98%EC%8A%A4</guid>
            <pubDate>Mon, 14 Aug 2023 15:29:23 GMT</pubDate>
            <description><![CDATA[<h3 id="class">Class</h3>
<p>클래스는 객체 지향 프로그래밍에서 특정 객체를 생성하기 위해 변수와 메서드를 정의한 일종의 틀이다. 클래스는 객체를 정의하기 위한 상태(멤버 변수)와 메서드(함수)로 구성된다.</p>
<hr>
<h3 id="es6-class">ES6 Class</h3>
<p>ES6 클래스 문법은 객체 지향적으로 표현하기 위해 새롭게 추가된 문법이다. ES6가 등장하기 전까진 prototype을 통해 클래스를 비슷하게 구현했다. 하지만 ES6의 클래스도 생김새만 다를 뿐, 내부적으로 프로토타입 방식으로 동작한다.</p>
<blockquote>
<p>class는 내부적으로 프로토타입으로 동작하기 때문에 <code>typeof</code>로 타입을 출력할 경우, <code>function</code>을 반환한다.</p>
</blockquote>
<pre><code class="language-javascript">// prototype (ES5)
let Meal = function(food) { // 생성자 함수
  this.food = food;
}
Meal.prototype.eat = function() {}; // 메서드</code></pre>
<pre><code class="language-javascript">// class (ES6)
class Meal{
  constructor(food) { // 생성자
      this.food = food;
  }
  eat() {} // 메서드
}

console.log(typeof Meal); // function</code></pre>
<hr>
<h3 id="prototype">Prototype</h3>
<p>자바스크립트는 프로토타입 기반 언어다. 자바스크립트의 객체는 <code>prototype</code>이라는 은닉 속성을 가지는데, 이는 자신의 프로토타입이 되는 다른 객체를 가리킨다. </p>
<p>앞서 설명했듯이 기존 자바스크립트는 클래스가 없어 함수를 통해 클래스를 흉내냈다.</p>
<pre><code class="language-javascript">function Person() {
    this.eyes = 2;
      this.nose = 1;
}

let kim = new Person();
let park = new Person();</code></pre>
<p><code>kim</code>과 <code>park</code>은 함수와 new를 통해 생성된 Person 객체다. 하지만 눈이 2개, 코가 1개라는 공통적인 특징을 가지고 있음에도 불구하고 <code>eyes</code>와 <code>nose</code>가 2개씩, 총 4개가 할당된다. 이러한 문제는 <code>prototype</code>으로 해결할 수 있다.</p>
<pre><code class="language-javascript">function Person() {}
Person.prototype.eyes = 2;
Person.prototype.nose = 1;</code></pre>
<p><code>Person.prototype</code>이라는 객체가 어딘가에 존재하고, <code>eyes</code>와 <code>nose</code>를 해당 객체에 넣어 <code>kim</code>과<code>park</code>이 공유하여 사용한다.</p>
<p>💥 <strong>ES6 class의 내부 동작 방식</strong></p>
<pre><code class="language-javascript">class Meal{
  constructor(food) { // 생성자
      this.food = food;
  }
  eat() {} // 메서드
}</code></pre>
<ol>
<li><code>Meal</code>이라는 이름을 가진 함수를 생성한다. 함수 본문은 생성자 메서드인 <code>constructor</code>에서 가져오며, 생성자 메서드가 없는 경우, 본문이 없는 함수가 생성된다.</li>
<li>클래스 내부에서 정의된 <code>eat</code>을 <code>Meal.prototype</code>에 저장한다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/kimyu0218_/post/150297c4-e5d2-47ed-98ac-405f8562a891/image.png" alt="es6 to prototype"></p>
<hr>
<h3 id="클래스-정의-방법">클래스 정의 방법</h3>
<ol>
<li><p><strong>Class Declaration</strong></p>
<pre><code class="language-javascript">class myClass {
constructor() {}
method1() {}
method2() {}
}</code></pre>
</li>
<li><p><strong>Class Expression</strong> : class 표현식은 이름을 가질 수도 있고, 익명으로 사용될 수도 있다.</p>
<pre><code class="language-javascript">let c1 = class MyClass {
 constructor() {}
 method1() {}
 method2() {}
};
</code></pre>
</li>
</ol>
<p>let c2 = class {
  constructor() {}
  method1() {}
  method2() {}
};</p>
<pre><code>---

### 접근 제한자
접근 제한자는 말 그대로 접근을 제한하기 위해 사용된다. 필요에 따라 클래스와 인터페이스를 다른 패키지에서 사용하지 못하도록 막을 필요가 있기 때문이다. 접근 제한자의 종류는 다음과 같다.

| public | protected | default | private |
| ---- | ---- | ---- | ---- |
| 모든 접근 허용 | 같은 패키지(폴더)의 객체와 상속관계의 객체들만 허용 | 같은 패키지(폴더)에 있는 객체만 허용 | 현재 객체 내에서만 허용 |


---

### Javascript Class
```javascript
class Meal{
  static beverage = &#39;coke&#39;;
  publicField;
  #privateField;

  constructor(food) { // constructor
      this.food = food;
  }

  eat() {} // method

  get food() { // getter
    return this._food;
  }

  set food(val) { // setter
    this._food = val;
  }

  static getBeverage() { // static method (Meal.getBeverage로 호출)
    return this.beverage;
  }
}</code></pre><ul>
<li><strong>constructor</strong> : <code>class</code>로 생성된 객체를 생성하고 초기화하기 위한 특수한 메서드다. <code>constructor</code>는 클래스 안에 하나만 존재할 수 있다. 만약 클래스에 여러 개의 <code>constructor</code>가 존재하는 경우, 에러가 발생한다.</li>
<li><strong>method</strong> : getter나 setter의 경우 get과 set을 붙인다.</li>
<li><strong>property</strong> : 프로퍼티는 크게 public과 private으로 나뉜다. public은 <code>&lt;instance name&gt;.&lt;property name&gt;</code>으로 프로퍼티의 값을 설정하고 읽을 수 있다. 반면, private은 클래스의 바깥에서 접근할 수 없다. 프로퍼티의 값은 <code>&lt;property name&gt; = &lt;value&gt;</code>로 설정할 수 있다. </li>
<li><strong>static</strong> : 정적 메서드는 클래스의 인스턴스가 아닌 클래스 이름으로 곧바로 호출되는 메서드를 의미한다. 메서드명 앞에 <code>static</code> 키워드를 붙이면 해당 메서드는 정적 메서드가 된다.</li>
</ul>
<p>💥 <strong>prototype에 저장되는 것과 아닌 것</strong></p>
<table>
<thead>
<tr>
<th>prototype에 저장</th>
<th>개별 객체에 저장</th>
</tr>
</thead>
<tbody><tr>
<td>constructor, method</td>
<td>property</td>
</tr>
</tbody></table>
<hr>
<p>참고자료
<a href="https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-ES6-Class-%EB%AC%B8%EB%B2%95-%EC%99%84%EB%B2%BD-%EC%A0%95%EB%A6%AC">JS class</a>
<a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Inheritance_and_the_prototype_chain">mdn web docs : inheritance and prototype chain</a>
<a href="https://ko.javascript.info/class">JAVASCRIPT.INFO : class</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[정규 표현식]]></title>
            <link>https://velog.io/@kimyu0218_/%EC%A0%95%EA%B7%9C-%ED%91%9C%ED%98%84%EC%8B%9D</link>
            <guid>https://velog.io/@kimyu0218_/%EC%A0%95%EA%B7%9C-%ED%91%9C%ED%98%84%EC%8B%9D</guid>
            <pubDate>Mon, 14 Aug 2023 12:20:17 GMT</pubDate>
            <description><![CDATA[<h3 id="정규-표현식">정규 표현식</h3>
<p>정규 표현식은 문자열에서 특정 문자 조합을 찾기 위한 패턴이다. 자바스크립트에서 정규 표현식은 객체로서 <code>RegExp</code>의 메서드를 사용할 수 있다.</p>
<hr>
<h3 id="정규-표현식-만들기">정규 표현식 만들기</h3>
<p>정규 표현식은 다음 두 가지 방법으로 만들 수 있다.</p>
<ol>
<li>리터럴 방식 : <code>/</code>로 패턴을 감싸 사용<pre><code class="language-javascript">const regex = /ab+c/;</code></pre>
</li>
<li><code>RegExp</code> 객체 생성자 호출<pre><code class="language-javascript">const regex = new RegExp(&#39;ab+c&#39;);</code></pre>
</li>
</ol>
<hr>
<h3 id="정규-표현식의-특수문자">정규 표현식의 특수문자</h3>
<table>
<thead>
<tr>
<th>특정 문자 매칭 패턴</th>
<th>검색 기준 패턴</th>
<th>반복 패턴</th>
<th>그룹 패턴</th>
</tr>
</thead>
<tbody><tr>
<td><strong>.</strong> : 줄바꿈을 제외한 모든 문자</td>
<td><strong>|</strong> : or</td>
<td><strong>?</strong> : 최대 1개</td>
<td><strong>()</strong> : 그룹화 + 캡처</td>
</tr>
<tr>
<td><strong>\d</strong> : 숫자</td>
<td><strong>[]</strong> : 괄호 안의 문자들 중 하나</td>
<td><strong>*</strong> : 0개 이상</td>
<td><strong>(?:)</strong> : 그룹화</td>
</tr>
<tr>
<td><strong>\w</strong> : 밑줄 문자를 포함한 영숫자</td>
<td><strong>[^]</strong> : 괄호 안의 문자를 제외한 것</td>
<td><strong>+</strong> : 1개 이상</td>
<td></td>
</tr>
<tr>
<td><strong>\s</strong> : 공백</td>
<td><strong>^</strong> : 문자열의 시작</td>
<td><strong>{n}</strong> : n개</td>
<td></td>
</tr>
<tr>
<td></td>
<td><strong>$</strong> : 문자열의 끝</td>
<td><strong>{min, max}</strong> : min개 이상 max개 이하</td>
<td></td>
</tr>
<tr>
<td>___</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>### 정규 표현식 플래그 : 고급 검색 옵션</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>정규 표현식은 전역 탐색이나 대소문자 무시 등 고급 검색 옵션을 지정하는 플래그를 가질 수 있다.</td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody></table>
<pre><code class="language-javascript">// literal
const re = /ab+c/gi;

// RegExp
const re = new RegExp(&#39;ab+c&#39;, &#39;gi&#39;);</code></pre>
<ul>
<li><code>i</code> : (ignore case) 대소문자 무시</li>
<li><code>g</code> : (global) 전역 탐색 - 문자열 내의 모든 패턴 검색</li>
<li><code>m</code> : (multi line) 여러 줄에 걸쳐 탐색</li>
</ul>
<p>💥 <strong>global 옵션</strong>
global을 뜻하는 <code>g</code> 플래그는 문자열 내의 모든 패턴을 검색한다. 전역 탐색이 아닌 경우에는 패턴과 일치하는 문자열을 찾으면 더 이상 탐색하지 않는다.</p>
<table>
<thead>
<tr>
<th>global 값이 false인 경우</th>
<th>global 값이 true인 경우</th>
</tr>
</thead>
<tbody><tr>
<td><img src="https://velog.velcdn.com/images/kimyu0218_/post/d9ca3acb-91e0-4fe2-87b7-c92e78640d8a/image.png" alt="not global"></td>
<td><img src="https://velog.velcdn.com/images/kimyu0218_/post/0b6467e3-6703-428b-8e8e-680bd4d0a7c2/image.png" alt="global"></td>
</tr>
<tr>
<td>___</td>
<td></td>
</tr>
<tr>
<td>### RegExp의 test 메서드</td>
<td></td>
</tr>
<tr>
<td>RegExp 객체는 <code>exec()</code>과 <code>test()</code>를 갖는다. 여기서는 <code>test()</code>메서드만 다루겠다.</td>
<td></td>
</tr>
</tbody></table>
<p><code>test()</code> 메서드는 주어진 문자열이 정규 표현식을 만족하는지 판별하고, 그 여부(true/false)를 반환한다.</p>
<pre><code class="language-javascript">const regex = /abc/;
const str = &#39;abcd&#39;;
console.log(regex.test(str)); // true</code></pre>
<p>💥 <strong>정규 표현식으로 완전 일치 탐색하기</strong>
위의 코드처럼 <code>test()</code> 메서드는 패턴과 일치하는 부분 문자열이 존재한다면 <code>true</code>를 반환한다. 어떻게 하면 정규식과 완전 일치하는 문자열인지 알 수 있을까? </p>
<p>앞서 나온 <code>^</code>과<code>$</code> 특수 문자를 사용하면 된다. <code>^</code>는 문자열의 시작을, <code>$</code>는 문자열의 끝을 의미하기 때문에 해당 특수문자를 적어주면 <code>abc</code>와 일치하는 부분 문자열이 있더라도 해당 문자열 앞뒤로 패턴과 일치하지 않는 문자가 존재한다면 <code>false</code>를 리턴한다.</p>
<pre><code class="language-javascript">const regex = /^abc$/;
const str = &#39;abcd&#39;;
console.log(regex.test(str)); // false</code></pre>
<hr>
<h3 id="정규-표현식-테스트-사이트--regexr">정규 표현식 테스트 사이트 : <a href="https://regexr.com/">RegExr</a></h3>
<p>정규식을 작성하다보면 내가 작성한 규칙이 맞는지 헷갈릴 때가 있다. 위 사이트를 이용하면 Text의 예제들이 정규식 패턴에 검색되는지 실시간으로 확인할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/kimyu0218_/post/0b6467e3-6703-428b-8e8e-680bd4d0a7c2/image.png" alt="example01"></p>
<p><img src="https://velog.velcdn.com/images/kimyu0218_/post/aeee91cc-62c8-49f4-ba52-54c36489b7fa/image.png" alt="example02">
위의 사진처럼 복잡한 정규식을 작성할 때 매우 유용하다. 사진의 <code>(?&lt;name&gt;)</code>은 캡처한 그룹에 이름을 붙이는 문법이다.</p>
<hr>
<p>참고자료
<a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Regular_expressions">mdn web docs - regular expression</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS] 함수 정의와 호이스팅]]></title>
            <link>https://velog.io/@kimyu0218_/JS-%ED%95%A8%EC%88%98-%EC%A0%95%EC%9D%98-%ED%98%B8%EC%9D%B4%EC%8A%A4%ED%8C%85</link>
            <guid>https://velog.io/@kimyu0218_/JS-%ED%95%A8%EC%88%98-%EC%A0%95%EC%9D%98-%ED%98%B8%EC%9D%B4%EC%8A%A4%ED%8C%85</guid>
            <pubDate>Mon, 14 Aug 2023 10:32:38 GMT</pubDate>
            <description><![CDATA[<h3 id="function">Function</h3>
<p>함수는 특정한 작업을 수행하도록 설계된 독립적인 블록을 의미한다. 자바스크립트에서의 함수는 하나의 data type으로, 변수에 할당하거나, 함수에 property를 지정하는 것이 가능하다.</p>
<blockquote>
<p>자바스크립트의 함수는 <code>undefined</code>를 반환한다. 다른 값을 반환받고 싶다면 return문이 필요하다.</p>
</blockquote>
<hr>
<h3 id="hoisting">Hoisting</h3>
<p>호이스팅은 인터프리터가 변수와 함수의 메모리 공간을 선언 전에 미리 할당하는 것을 의미한다.</p>
<p>호이스팅을 설명할 때 주로 <em>&quot;변수의 선언과 초기화를 분리한 후, 선언만 코드의 최상단으로 옮기는 것&quot;</em> 으로 말하곤 한다. 따라서 변수를 선언하는 코드보다 사용하는 코드가 먼저 등장해도 에러가 발생하지 않는다. 다만, 선언과 초기화를 함께 수행하는 경우, 선언 코드까지 실행되어야 에러가 발생하지 않는다.</p>
<p>이후에 설명할 Function Declaration의 경우, 함수의 선언보다 함수 호출이 앞서도 정상적으로 동작한다. 하지만 Function Expression은 선언과 초기화가 함께 수행되기 때문에 정상적으로 동작하지 않는다.</p>
<hr>
<h3 id="함수-정의-방법">함수 정의 방법</h3>
<ol>
<li><p><strong>Function Declaration</strong></p>
<pre><code class="language-javascript">function func(param) { }
func(10);</code></pre>
<p>함수에 이름을 붙이고, 함수명으로 함수를 호출한다. 함수 정의 전에 함수를 호출해도 정상적으로 동작한다.
<img src="https://velog.velcdn.com/images/kimyu0218_/post/5bd0c0df-5d9d-4f06-908b-b7d2cc961778/image.png" alt="function declaration example"></p>
</li>
<li><p><strong>Function Expression</strong></p>
<pre><code class="language-javascript">// (Function Declaration과 달리, 이름이 없는 익명 함수를 사용)
const func = function (param) { };
</code></pre>
</li>
</ol>
<p>// ES6 ver
const func = (param) =&gt; {};</p>
<p>```
  함수를 변수에 담고, 변수명으로 함수를 호출한다. 함수 정의 전에 함수를 호출할 수 없다. Function Expression의 경우, ES6의 화살표 함수를 사용하여 함수를 정의할 수 있다.
  <img src="https://velog.velcdn.com/images/kimyu0218_/post/bf869389-df9f-494b-8fd3-d8b18c42a35e/image.png" alt="function expression example"></p>
<hr>
<p>참고자료
<a href="http://www.tcpschool.com/javascript/js_function_basic">TCP School : function basic</a>
<a href="https://developer.mozilla.org/ko/docs/Glossary/Hoisting">mdn web docs : hoisting</a>
<a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/function">mdn web docs : function</a>
<a href="https://homzzang.com/b/js-55">function definition</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[NodeJS] CommonJS vs. ESM]]></title>
            <link>https://velog.io/@kimyu0218_/NodeJS-CommonJS-vs.-ESM</link>
            <guid>https://velog.io/@kimyu0218_/NodeJS-CommonJS-vs.-ESM</guid>
            <pubDate>Mon, 14 Aug 2023 08:42:53 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-javascript">const module1 = require(&#39;module1&#39;); // CommonJS
import module1 from &#39;module1&#39;; // ES6</code></pre>
<blockquote>
<p>자바스크립트로 개발을 하다보면 <code>require</code>나 <code>import</code>로 외부 라이브러리를 불러오는 코드를 자주 볼 수 있다. <code>require</code>은 CommonJS 키워드, <code>import</code>는 ES6에서 사용되는 키워드다.</p>
</blockquote>
<hr>
<h3 id="modules">Modules</h3>
<p>모듈은 프로그램의 구성 요소로, 관련 데이터와 함수를 하나로 묶은 단위를 의미한다. 일반적으로 하나의 소스 파일에 모든 함수를 작성하지 않고, 함수의 기능별로 별도의 모듈을 구성한다. 이처럼 프로그램 코드를 기능별로 나누어 독립된 파일에 저장하여 관리하는 방식을 모듈화라고 한다.
<img src="https://hacks.mozilla.org/files/2018/03/04_import_graph.png" alt="module-graph"></p>
<hr>
<h3 id="javascript-module-system">Javascript Module System</h3>
<ul>
<li><strong>AMD</strong> : <code>require.js</code> 라이브러리를 통해 개발된, 가장 오래된 모듈 시스템</li>
<li><strong>CommonJS</strong> : NodeJS 서버를 위해 만들어진 모듈 시스템</li>
<li><strong>UMD</strong> : (Universal Module Definition) AMD와 CommonJS와 같은 다양한 모듈 시스템을 함께 사용하기 위해 만들어진 모듈 시스템</li>
<li><strong>ES module</strong> : (ESM) ES6에 도입된 모듈 시스템</li>
</ul>
<hr>
<h3 id="es-module">ES Module</h3>
<p>ESM은 ES6에 도입된 모듈 시스템이다. <code>import</code>, <code>from</code>, <code>export</code>, <code>default</code>처럼 모듈 관리 전용 키워드를 사용하여 가독성이 좋다. 또한, <code>Named Parameter</code>와 같이 CommonJS에서 지원하지 않는 기능들도 가지고 있다.</p>
<hr>
<h3 id="객체-내보내기불러오기">객체 내보내기/불러오기</h3>
<p>CommonJS는 <code>exports</code>나 <code>module.exports</code>를 이용하여 객체를 내보내고, <code>require</code>를 통해 객체를 불러온다.</p>
<ul>
<li><code>exports</code>를 이용한 내보내기/불러오기<pre><code class="language-javascript">// (func.js)
exports.func1 = func1;
exports.func2 = func2;
</code></pre>
</li>
</ul>
<p>// (main.js)
const func = require(&#39;./func&#39;);
func.func1(10);
func.func2(10);</p>
<pre><code>- `module.exports`를 이용한 내보내기/불러오기
```javascript
// (func.js)
const func = {
  func1: function(n) {
  },
  func2: function(n) {
  }
}
module.exports = func;

// (main.js)
const func = require(&#39;./func&#39;);
func.func1(10);
func.func2(20);</code></pre><blockquote>
<p><code>exports</code>와 <code>module.exports</code> 모두 객체를 내보내는 공통적인 기능을 갖고 있지만 <code>exports</code>는 property 방식을 사용하고, <code>module.exports</code>는 곧바로 사용할 수 있다는 점에서 차이가 았다.</p>
</blockquote>
<p>반면, ES6는 <code>export</code> 키워드를 사용하여 객체를 내보내고, 다른 프로그램에서 <code>import</code>문으로 객체를 가져와 사용할 수 있다. <code>export</code>에는 named와 default 두 종류의 내보내기 방식이 있다.</p>
<ul>
<li>named 방식으로 내보내기<pre><code class="language-javascript">// 개별적으로 내보내기
export const func1 = function(num) {};
export const func2 = function(num) {};
</code></pre>
</li>
</ul>
<p>// 목록으로 내보내기
export {func1, func2};</p>
<pre><code>- default 방식으로 내보내기
```javascript
export default function() {}; // export {func as default} 도 가능</code></pre><blockquote>
<p>named 방식은 여러 값을 내보낼 때 유용하다. 이때, 불러와 사용할 때는 내보낸 이름과 동일한 이름을 사용해야 한다. 반면, default는 아무 이름으로 가져와 사용할 수 있다.</p>
</blockquote>
<hr>
<h3 id="cjs와-es6의-내보내기불러오기-비교-정리">CJS와 ES6의 내보내기/불러오기 비교 정리</h3>
<p><strong>CommonJS</strong></p>
<ul>
<li><p>내보내기</p>
<ul>
<li><code>exports</code> 변수의 property로 내보낼 객체 할당</li>
<li><code>module.exports</code>의 변수로 내보낼 객체 할당</li>
</ul>
</li>
<li><p>불러오기 : <code>require</code> 키워드 사용</p>
</li>
</ul>
<p><strong>ES6</strong></p>
<ul>
<li>내보내기 : <code>export</code>를 사용하여 named/default 방식으로 내보낼 객체 선정</li>
<li>불러오기 : <code>import ~ from</code> 키워드 사용</li>
</ul>
<hr>
<p>참고자료
<a href="http://www.tcpschool.com/c/c_complie_module">TCP School : module</a>
<a href="https://ko.javascript.info/modules-intro">JAVASCRIPT.INFO : module</a>
<a href="https://www.daleseo.com/js-module-import/">ESM import</a>
<a href="https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/">mozilla : ES modules: A cartoon deep-dive</a>
<a href="https://dydals5678.tistory.com/97">exports vs. module.exports</a>
<a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/export">mdn web docs : export</a></p>
]]></description>
        </item>
    </channel>
</rss>