<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ JavaScript - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/korean/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ JavaScript - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/korean/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Fri, 22 May 2026 15:16:07 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/korean/news/tag/javascript/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ 마이크로프론트엔드 동작 원리: iframe부터 Module Federation까지 ]]>
                </title>
                <description>
                    <![CDATA[ 마이크로프론트엔드는 팀이 대규모 프론트엔드 애플리케이션을 구축하고 배포하는 방식을 변화시키고 있습니다. 이 튜토리얼에서는 전통적인 접근 방식부터 현대적인 Module Federation 구현까지 아키텍처 전반을 살펴봅시다. 튜토리얼을 마치면 마이크로프론트엔드가 팀에 맞는 적절한 솔루션인지 평가할 수 있을 겁니다.‌ 목차  * 마이크로프론트엔드란? * 전통적인 마이크로프론트엔드 패턴     * 서버 측 구성  ]]>
                </description>
                <link>https://www.freecodecamp.org/korean/news/how-microfrontends-work-iframes-to-module-federation/</link>
                <guid isPermaLink="false">69c26f23d7a23d04d6b0d2e6</guid>
                
                    <category>
                        <![CDATA[ frontend ]]>
                    </category>
                
                    <category>
                        <![CDATA[ architechture ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Frontend Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Donghwan Jeung ]]>
                </dc:creator>
                <pubDate>Fri, 01 May 2026 06:01:50 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/korean/news/content/images/2026/05/39037981-d514-4f26-8a48-be0cdd9ca29b.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>기사 원문:</strong> <a href="https://www.freecodecamp.org/news/how-microfrontends-work-iframes-to-module-federation/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How Microfrontends Work: From iframes to Module Federation</a>
      </p><p>마이크로프론트엔드는 팀이 대규모 프론트엔드 애플리케이션을 구축하고 배포하는 방식을 변화시키고 있습니다. 이 튜토리얼에서는 전통적인 접근 방식부터 현대적인 Module Federation 구현까지 아키텍처 전반을 살펴봅시다.</p><p>튜토리얼을 마치면 마이크로프론트엔드가 팀에 맞는 적절한 솔루션인지 평가할 수 있을 겁니다.‌</p><h2 id="-">목차</h2><!--kg-card-begin: markdown--><ul>
<li>마이크로프론트엔드란?
<ul>
<li>전통적인 마이크로프론트엔드 패턴</li>
<li>서버 측 구성</li>
<li>iframe</li>
<li>빌드 타임 통합 – Package</li>
</ul>
</li>
<li>현대적인 마이크로프론트엔드 패턴
<ul>
<li>Module Federation</li>
<li>Single SPA</li>
</ul>
</li>
<li>상세 비교</li>
<li>Module Federation 도입 시 주의할 점
<ul>
<li>설정 복잡도</li>
<li>런타임 고려사항</li>
<li>운영 시 고려사항</li>
</ul>
</li>
<li>결론
<ul>
<li>다음 단계</li>
</ul>
</li>
</ul>
<!--kg-card-end: markdown--><h2 id="--1">마이크로프론트엔드란?</h2><p>백엔드의 마이크로서비스 아키텍처와 비슷한 개념으로 마이크로프론트엔드는 프론트엔드에서도 마이크로서비스 아키텍처와 동일한 많은 이점을 얻을 수 있습니다.</p><p>팀의 자율성을 높이고 배포 위험을 줄이며 개발을 여러 팀에 나눠 진행하기 위해 마이크로프론트엔드를 채택할 수 있습니다. 각 팀마다 기술 스택, 배포 주기 및 워크플로우를 독자적으로 관리해도 사용자에게는 하나의 완성된 서비스로 보입니다.</p><p>‌‌전반적인 아이디어는 하나의 모놀리식 UI에서 벗어나 별도의 팀이 독립적으로 소유, 관리 및 배포할 수 있는 분리된 UI 코드베이스로 전환하는 것입니다.</p><p>마이크로프론트엔드를 가장 쉽게 이해하는 방법은 다음과 같습니다.</p><blockquote>하나의 UI 요소를 다른 UI에 통합</blockquote><p>무엇이 UI 요소가 될 수 있는지 궁금할 수 있습니다. 몇 가지 예시를 살펴보겠습니다.</p><ul><li><strong>페이지</strong> – 특정 팀이 담당하는 웹사이트의 일부입니다. 예를 들어 Auth 팀은 로그인/회원가입 페이지를 마케팅 팀은 마케팅 페이지를 담당할 수 있습니다.</li><li><strong>컴포넌트</strong> – 헤더와 푸터 같은 컴포넌트도 마이크로프론트엔드의 좋은 후보입니다. 비교적 정적이지만 웹사이트 전반에 걸쳐 일관성을 유지해야 하며 서로 다른 페이지를 담당하는 팀과 연동될 수 있습니다.</li><li><strong>위젯</strong> – 예를 들어 추천 위젯은 추천 팀이 담당할 수 있으며 맥락에 따라 페이지의 다양한 위치에 통합될 수 있습니다. 이는 정적 컴포넌트와 다른데 추천 위젯은 맥락에 따라 API를 통해 관련 데이터를 직접 가져오기도 하기 때문입니다(해당 API 역시 추천 팀이 담당합니다).</li></ul><h2 id="--2">전통적인 마이크로프론트엔드 패턴</h2><p>마이크로프론트엔드의 정의를 읽고 나서 "요즘 &nbsp;(Google 같은 대형 기업을 제외하고) 누가 모놀리식 UI로 개발하지?" 라고 생각할 수도 있습니다. 그렇다면 여러분의 팀은 이미 다음과 같은 전통적인 마이크로프론트엔드 중 하나를 사용하고 있을 가능성이 높습니다.</p><h3 id="-server-side-composition-">서버 측 구성 (Server-Side Composition)</h3><p>다양한 조직에서 가장 흔하게 접하는 방식입니다. 웹사이트를 라우트 패턴이나 페이지 단위로 분리하는 방식이 핵심입니다. 예를 들어 <code>/account/*</code>로 시작하는 모든 라우트(<code>/account/login</code>, <code>/account/signup</code> 등)는 계정 팀이 담당하는 모듈로 연결할 수 있습니다. 마찬가지로 마케팅 섹션이라면 <code>/blog/*</code>처럼 별도의 라우트 접두사를 붙일 수 있습니다.</p><p>이 방식은 일반적으로 리버스 프록시 레이어(예: NGINX)에서 구현하며 경로 매칭을 기반으로 트래픽을 적절한 하위 UI 서비스로 라우팅합니다.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/korean/news/content/images/2026/04/fda979cd-c95c-4d48-a7dc-87956672b24d.png" class="kg-image" alt="fda979cd-c95c-4d48-a7dc-87956672b24d" srcset="https://www.freecodecamp.org/korean/news/content/images/size/w600/2026/04/fda979cd-c95c-4d48-a7dc-87956672b24d.png 600w, https://www.freecodecamp.org/korean/news/content/images/size/w1000/2026/04/fda979cd-c95c-4d48-a7dc-87956672b24d.png 1000w, https://www.freecodecamp.org/korean/news/content/images/2026/04/fda979cd-c95c-4d48-a7dc-87956672b24d.png 1416w" sizes="(min-width: 720px) 720px" width="1416" height="638" loading="lazy"></figure><h3 id="iframes"><strong><strong><strong>iframes</strong></strong></strong></h3><p>또 다른 방식은 iframe을 사용하는 것인데 이 방법에는 단점이 적지 않습니다.</p><p>페이지 단위로 동작하는 서버 측 구성과 달리 iframe은 페이지 내부에 위젯 형태로 통합할 수 있습니다. <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/iframe"><code>&lt;iframe&gt;</code></a> 태그를 사용하면 다른 웹사이트를 현재 웹사이트의 일부로 불러올 수 있습니다.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/korean/news/content/images/2026/04/7c24a43a-80d4-45f4-a2df-3de0e0e0bc1c.png" class="kg-image" alt="7c24a43a-80d4-45f4-a2df-3de0e0e0bc1c" srcset="https://www.freecodecamp.org/korean/news/content/images/size/w600/2026/04/7c24a43a-80d4-45f4-a2df-3de0e0e0bc1c.png 600w, https://www.freecodecamp.org/korean/news/content/images/size/w1000/2026/04/7c24a43a-80d4-45f4-a2df-3de0e0e0bc1c.png 1000w, https://www.freecodecamp.org/korean/news/content/images/2026/04/7c24a43a-80d4-45f4-a2df-3de0e0e0bc1c.png 1560w" sizes="(min-width: 720px) 720px" width="1560" height="1043" loading="lazy"></figure><p>대표적인 예시로 트위터 피드나 구글 맵을 띄운 웹사이트를 들 수 있습니다. 이는 외부 위젯을 iframe으로 불러온 사례지만 기업 내부에서 특정 위젯을 iframe 기반으로 구성하는 경우도 있습니다.</p><h3 id="-packages">빌드 타임 통합 - Packages</h3><p>컴포넌트를 UI 라이브러리로 배포해 다른 애플리케이션에 제공하는 방식입니다.</p><p>여러 페이지나 위젯, 또는 헤더 및 푸터처럼 정적 컴포넌트로 이루어진 완전한 앱을 통합할 때 유용하며 실제로도 흔히 활용됩니다.</p><p>일반적으로 한 팀이 컴포넌트를 패키지로 배포하면 다른 팀이 해당 패키지의 특정 버전을 사용합니다.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/korean/news/content/images/2026/04/70385841-28c6-441e-ae4d-c977b3563ecf.png" class="kg-image" alt="70385841-28c6-441e-ae4d-c977b3563ecf" srcset="https://www.freecodecamp.org/korean/news/content/images/size/w600/2026/04/70385841-28c6-441e-ae4d-c977b3563ecf.png 600w, https://www.freecodecamp.org/korean/news/content/images/size/w1000/2026/04/70385841-28c6-441e-ae4d-c977b3563ecf.png 1000w, https://www.freecodecamp.org/korean/news/content/images/size/w1600/2026/04/70385841-28c6-441e-ae4d-c977b3563ecf.png 1600w, https://www.freecodecamp.org/korean/news/content/images/2026/04/70385841-28c6-441e-ae4d-c977b3563ecf.png 2318w" sizes="(min-width: 720px) 720px" width="2318" height="1151" loading="lazy"></figure><p>Widget 컴포넌트는 앱의 의존성 설치 단계에서 가져옵니다. 웹 앱은 이 위젯을 자체 컴포넌트처럼 활용할 수 있으며 하나의 모듈로 함께 빌드되어 사용자에게 전달됩니다.</p><h2 id="--3">현대적인 마이크로프론트엔드 패턴</h2><h3 id="module-federation">Module Federation</h3><p>Module Federation을 사용하면 런타임에 호스트 애플리케이션 안으로 리모트 UI 조각을 불러올 수 있습니다. 전체 페이지, 위젯, 컴포넌트 등이 리모트 UI 조각이 될 수 있습니다.</p><p>Module Federation은 원래 <a href="https://webpack.js.org/concepts/module-federation/">Webpack 5의 기능</a>으로 런타임에 원격 소스에서 JavaScript 코드를 불러오도록 번들러의 기능을 확장했습니다.</p><p>이를 발전시킨 버전인 <a href="https://module-federation.io/">Module Federation 2.0</a>은 RSPack, Vite 등 다른 주요 번들러에서도 사용할 수 있습니다.</p><p>Webpack 5를 사용하고 있더라도 Module Federation 2.0을 권장합니다. 기존 Webpack 5 구현에 존재하는 몇 가지 흔한 문제들을 해결해주기 때문입니다.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/korean/news/content/images/2026/04/fae404c6-efc9-4e0f-8667-4427dbcdfc0f.png" class="kg-image" alt="fae404c6-efc9-4e0f-8667-4427dbcdfc0f" srcset="https://www.freecodecamp.org/korean/news/content/images/size/w600/2026/04/fae404c6-efc9-4e0f-8667-4427dbcdfc0f.png 600w, https://www.freecodecamp.org/korean/news/content/images/size/w1000/2026/04/fae404c6-efc9-4e0f-8667-4427dbcdfc0f.png 1000w, https://www.freecodecamp.org/korean/news/content/images/size/w1600/2026/04/fae404c6-efc9-4e0f-8667-4427dbcdfc0f.png 1600w, https://www.freecodecamp.org/korean/news/content/images/size/w2400/2026/04/fae404c6-efc9-4e0f-8667-4427dbcdfc0f.png 2400w" sizes="(min-width: 720px) 720px" width="2501" height="1144" loading="lazy"></figure><p>Module Federation의 주요 구성 요소들을 이해하기 위해 예시를 하나 살펴보겠습니다.</p><p>블로그 애플리케이션은 콘텐츠 팀이 담당하고 위젯은 추천 팀이 담당한다고 가정해 봅시다.</p><p>이제 콘텐츠 팀이 자신들의 애플리케이션 안에 추천 위젯을 통합하고자 합니다. 두 팀은 서로 다른 도메인에 호스팅된 별도의 코드베이스를 가지고 있으며 콘텐츠 팀의 앱은 <code>website.com</code>에, 추천 팀의 앱은 <code>recommendation.com</code>에 배포되어 있습니다.</p><p>Module Federation으로 이 MFE(MicroFrontend) 통합을 달성하는 방법은 다음과 같습니다.</p><h4 id="remote">Remote</h4><p>JavaScript 파일(예: 유틸리티, 컴포넌트 등)을 외부에 제공하는 remote 역할을 담당합니다.</p><p>이 예시에서는 추천 팀이 담당하는 위젯이 remote 역할을 맡으며 위젯을 외부에 제공하기 위한 설정이 필요합니다.</p><pre><code class="language-typescript">new ModuleFederationPlugin({
  name: 'recommendation',
  exposes: {
    './Widget': './src/Widget.js',
  }
})</code></pre><h4 id="remote-entry">Remote Entry</h4><p>Remote entry는 remote의 진입점(entry point) URL입니다. 하나의 remote는 여러 JavaScript 파일을 외부에 제공할 수 있으며 remoteEntry 파일은 그 모든 파일을 파악하고 있습니다.</p><p>Module Federation은 기본적으로 remote entry 파일을 루트 경로에서 제공합니다. 이 예시에서는 추천 팀이 <code>https://recommendation.com/remoteEntry.js</code>에 remote entry를 제공할 수 있습니다.</p><h4 id="host">Host</h4><p><strong>Remote Entry</strong>를 이용해 하나 이상의 remote로부터 JavaScript를 가져오는 독립적인 웹사이트입니다. remote entry는 특정 remote가 외부에 제공하는 컴포넌트, 유틸리티 등 여러 요소를 담은 앱의 네임스페이스라고 생각하면 이해하기 쉽습니다.</p><p>이 예시에서는 콘텐츠 팀이 Host 역할을 맡으며 remotes 설정 안에 추천 팀의 remote entry를 정의합니다.</p><pre><code class="language-typescript">new ModuleFederationPlugin({
  name: 'content-blog',
  remotes: {
    "recommendation": 'recommendation@https://recommendation.com/remoteEntry.js',
  },
  // ... other configs
})</code></pre><h4 id="shared">Shared</h4><p>Host와 remote 모두 SemVer 형식으로 의존성을 지정할 수 있으며 이는 런타임에 자동으로 조정되어 공유됩니다. React와 같이 싱글톤이어야 하는 공통 프레임워크 의존성이나 공유 가능한 서드파티 라이브러리 등이 이에 해당합니다.</p><p>shared를 올바르게 설정하면 remote에서 UI를 가져올 때 host에 이미 있는 라이브러리나 코드를 클라이언트가 중복으로 다운로드하는 것을 방지할 수 있으며 이는 Module Federation 성능 최적화의 핵심입니다.</p><pre><code class="language-typescript">const deps = require("./package.json").dependencies;

new ModuleFederationPlugin({
  shared: {
    ...deps,
    react: {
      singleton: true,
      requiredVersion: deps.react,
    }
  },
  // ... other configs
})</code></pre><h4 id="imports-and-usage"><strong><strong><strong>Imports and Usage</strong></strong></strong></h4><p>Module Federation을 사용하면 마치 JavaScript 파일이 로컬에 있는 것처럼 import해서 사용할 수 있습니다. remote entry와 필요한 의존성을 가져오는 작업은 Module Federation이 런타임에 내부적으로 처리합니다.</p><pre><code class="language-typescript">// Import is of the format - &lt;remote&gt;/&lt;expose-from-remote&gt;
import Widget from 'recommendation/Widget';

// Render somewhere, making sure to handle loading via Suspense
// &amp; errors via error boundary in React
&lt;ErrorBoundary&gt;
  &lt;Suspense fallback={&lt;Loading /&gt;}
    &lt;Widget /&gt;
  &lt;/Suspense&gt;
&lt;/ErrorBoundary&gt;</code></pre><p>‌‌Module Federation을 간단하게 요약하면</p><blockquote>런타임에 원격 서버에서 JavaScript 코드(컴포넌트, 유틸리티 등)를 가져오면서도 의존성을 공유하고 성능을 유지하는 것입니다.</blockquote><h3 id="single-spa">Single SPA</h3><p>마이크로프론트엔드를 검색하면 <a href="https://single-spa.js.org/">Single SPA</a>가 인기 있는 솔루션으로 자주 등장합니다. 하지만 이 도구가 필요한 상황은 따로 있습니다. 바로 여러 프레임워크(예: React + Angular + Vue)로 구성된 컴포넌트를 하나의 애플리케이션에 통합하는 것입니다. 실제 동작 방식은 다음과 같습니다.</p><p>Single SPA는 URL 라우트에 따라 애플리케이션 전체를 마운트하거나 언마운트하는 JavaScript 라우터 역할을 합니다. 각각의 "single-spa application"은 프레임워크별로 구성된 앱으로 해당 라우트가 활성화될 때 로드됩니다.</p><pre><code class="language-typescript">// Register applications with Single SPA
registerApplication({
 name: '@mycompany/react-app',
 app: () =&gt; System.import('@mycompany/react-app'),
 activeWhen: ['/react-app']
});

registerApplication({
 name: '@mycompany/angular-app', 
 app: () =&gt; System.import('@mycompany/angular-app'),
 activeWhen: ['/angular-app']
});</code></pre><h2 id="--4">상세 비교</h2><!--kg-card-begin: markdown--><table>
<thead>
<tr>
<th>기준</th>
<th>Module Federation</th>
<th>서버 측 구성</th>
<th>iframe</th>
<th>빌드 타임 통합 (Package)</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>독립적 배포</strong></td>
<td>💚 마이크로프론트엔드는 클라이언트에서 런타임에 로드됩니다. 각 팀이 독립적으로 배포할 수 있으며 변경 사항이 즉시 반영됩니다.</td>
<td>💚 각 라우트 패턴이 개별 앱의 배포 주소를 가리키므로 배포는 독립적으로 유지됩니다.</td>
<td>💚 iframe도 런타임에 로드되므로 독립적인 배포가 가능합니다.</td>
<td>💔 배포가 호스트 애플리케이션에 종속됩니다. 패키지에 변경이 생기면 새 버전을 배포하고 호스트 앱에서 버전을 올려야 합니다.</td>
</tr>
<tr>
<td><strong>성능</strong></td>
<td>💚 의존성 공유와 최적화된 로딩을 지원하여 SPA 성능을 유지할 수 있습니다.</td>
<td>💔 애플리케이션 간 이동 시 전체 페이지를 다시 로드해야 하므로 SPA의 이점을 살릴 수 없습니다.</td>
<td>💔 완전히 격리된 환경에서 모든 의존성을 별도로 불러오므로 전체적인 페이지 로딩 속도가 느려집니다.</td>
<td>💚 패키지 통합 시 의존성 중복 제거(<a href="https://yarnpkg.com/cli/dedupe">deduping</a>)를 통해 어느 정도 공유할 수 있지만 적절한 개발 도구가 필요합니다. 그렇지 않으면 동일한 의존성이 중복으로 포함될 수 있습니다.</td>
</tr>
<tr>
<td><strong>확장성 및 유지보수</strong></td>
<td>💚 대규모 환경에서도 잘 동작합니다. 페이지 전체를 페더레이션 컴포넌트로 구성할 수 있으며 가장 작은 단위의 컴포넌트도 서로 다른 리모트에서 가져올 수 있습니다.</td>
<td>💔 동일한 앱처럼 보이게 하려면 헤더/푸터 등을 중복으로 구현해야 하는 경우가 많습니다. 또한 앱 진입점을 라우트 기반으로만 분리할 수 있어 세밀하게 조합하기가 어렵습니다.</td>
<td>💔 페이지 전체를 구성하는 데는 적합하지만 일부만 구성하기에는 한계가 있습니다. 규모가 커질수록 앱이 느려지고 SEO 최적화나 반응형 레이아웃 구성 시 문제가 생길 수 있습니다.</td>
<td>💔 규모가 커질수록 패키지 배포 관리, 업그레이드, 버전 충돌 문제가 발생합니다. CI 도구로 어느 정도 완화할 수 있지만 버전 업그레이드 및 기능, 성능 영향 검증에 여전히 손이 많이 갑니다.</td>
</tr>
<tr>
<td><strong>초기 설정 난이도</strong></td>
<td>💔 현재 앱을 어떻게 구성하고 있느냐에 따라 초기 설정 비용이 높을 수 있습니다. Module Federation을 원하는 대로 세팅하고 싶거나 문제가 발생했을 때 빌드 도구에 대한 깊은 이해가 필요할 수 있습니다. 자세한 내용은 다음 섹션에서 다룹니다.</td>
<td>💚 리버스 프록시 레이어 외에 별다른 결합이 없어 구현이 비교적 간단합니다. 리버스 프록시가 트래픽을 각 서비스로 적절히 라우팅하는 역할을 담당합니다.</td>
<td>💔 통합 자체는 비교적 쉽지만 처리해야 할 엣지 케이스가 많아 생각보다 시간이 오래 걸릴 수 있습니다. 대표적인 예시로는 iframe과 호스트 앱 간 통신, 레이아웃 문제, iframe 경계를 벗어나는 렌더링(예: 토스트), 크로스 도메인 제한과 보안, SEO 및 접근성(a11y) 문제 등이 있습니다.</td>
<td>💔 새 패키지 배포 파이프라인 구축, 변경 이력 관리, 버전 업그레이드, 버전 충돌 해결에 상당한 노력이 필요합니다. 패키지를 변경할 때마다 호스트 앱의 전이적 의존성(간접적으로 설치되는 패키지)에 의도치 않은 영향이 없는지 확인해야 합니다.</td>
</tr>
<tr>
<td><strong>인증 및 권한</strong></td>
<td>💚 앱 설정에 따라 Module Federation 컴포넌트가 데이터 페칭을 위해 리모트 앱의 서버를 직접 호출할 수 있습니다. 리모트가 호스트와 다른 도메인에 있는 경우 CORS 처리와 인증 쿠키 전송 설정이 필요할 수 있습니다.</td>
<td>💚 각 앱이 중앙 인증 서비스와 독립적으로 연동할 수 있습니다.</td>
<td>💔 iframe에서 부모 웹사이트의 인증 쿠키 등 브라우저 정보에 접근하기 어려울 수 있습니다. 특히 iframe URL이 애플리케이션과 다른 도메인/서브도메인에 있는 경우 인증 처리를 위한 별도의 우회 방법이 필요할 수 있습니다.</td>
<td>💚 패키지 컴포넌트는 호스트 애플리케이션 내 프록시 API를 통해 호출하거나 독립 서비스의 API를 직접 호출하는 방식 중 선택할 수 있습니다.</td>
</tr>
<tr>
<td><strong>devloop</strong></td>
<td>💚 Module Federation 2.0은 성숙한 개발 환경을 제공합니다. 여러 앱에 걸쳐 소스맵과 핫 리로딩(HMR)을 지원하며 기본적으로 원활한 통합 경험을 제공합니다. 로컬에서 리모트 앱 주소를 자유롭게 지정해 실제 환경처럼 테스트할 수도 있습니다.</td>
<td>💔 두 서비스가 제대로 연결되는지 확인하려면 두 서비스와 리버스 프록시를 로컬에서 모두 실행해야 하며 설정이 까다로울 수 있습니다.</td>
<td>💔 로컬 테스트만으로는 크로스 도메인 환경에서 발생할 수 있는 iframe 관련 문제를 정확히 재현하기 어렵습니다.</td>
<td>💔 개발 중인 패키지 변경 사항을 호스트 앱에서 로컬로 테스트하려면 별도의 개발 워크플로우가 필요합니다. 일반적으로 패키지를 사전 배포하거나, 로컬 패키지를 링크하거나, yalc 같은 도구를 활용하는 방식으로 처리합니다.</td>
</tr>
<tr>
<td><strong>종합 추천 ✨</strong></td>
<td>독립적인 배포와 릴리스 주기를 원하는 여러 팀이 낮은 결합도로 협업하는 앱에 적합합니다.</td>
<td>더 큰 비즈니스 도메인 안에서 비교적 독립적으로 운영되는 앱(서브도메인 단위)에 적합합니다. "사용자가 각 앱을 얼마나 자주 오가는가?"를 생각해보세요. 자주 오가지 않는다면 적합한 방식입니다.</td>
<td>여러 제약으로 인해 권장하지 않습니다. 다만 서드파티 통합에는 어울릴 수 있습니다. Twitter가 피드 일부를 iframe으로 외부 사이트에 제공하는 방식이 대표적인 예인데, 다른 방식보다 훨씬 간편합니다.</td>
<td>변경사항을 꼼꼼하게 통제해야 하는 앱에 적합합니다. 호스트 앱이 패키지를 직접 업그레이드하고, 사용자에게 릴리스하기 전에 충분히 검증할 수 있습니다.</td>
</tr>
</tbody>
</table>
<!--kg-card-end: markdown--><h2 id="module-federation-"><strong>Module Federation </strong>도입 시 주의할 점</h2><p>Module Federation을 사용할 때 가장 큰 트레이드오프는 초기 설정 비용입니다. 이는 앞선 비교표에서 간략히 다룬 바 있습니다.</p><p>Module Federation으로 통합할 때 예상할 수 있는 다른 어려운 점은 다음과 같습니다.</p><h3 id="--5">설정 복잡도</h3><ol><li><strong>번들러 별 고려사항</strong> - 앱에 따라 Module Federation을 제대로 세팅하려면 번들러가 내부적으로 어떻게 동작하는지 알아야 할 수 있습니다. 예를 들어 Webpack 5에서 리모트가 페더레이션 컴포넌트를 외부에 제공하면서 동시에 자체적인 화면도 렌더링한다면 이를 위한 적절한 청크 설정이 필요합니다. Module Federation은 기본적으로 특정 청크 최적화 전략을 전제로 하며 remoteEntry를 앱의 루트에서 노출하기 때문입니다.</li><li><strong>공유 의존성</strong> - 번들 크기와 로딩 성능을 최적화하려면 공유할 수 있는 의존성을 파악해 최대한 많이 공유해야 합니다. 또한 런타임에서 발생할 충돌을 막기 위해 React 같은 핵심 라이브러리는 싱글톤으로 설정해야 합니다.</li></ol><h3 id="--6">런타임 고려사항</h3><ol><li>크로스 도메인 - 리모트가 <code>remote.website.com</code>처럼 <code>host.website.com</code>과 다른 서브도메인에 배포되어 있다면 호스트 안에서 리모트 컴포넌트가 데이터를 페칭할 때 CORS 문제가 생길 수 있어 서버에 적절한 CORS 설정이 필요합니다. 또한 리모트로 요청할 때 브라우저가 인증 쿠키를 함께 전송하도록 fetch의 <code>credentials</code> 설정도 맞춰야 합니다.</li><li>스타일 충돌 - 리모트 앱의 스타일이 호스트 앱의 스타일을 덮어쓰거나 반대로 리모트 앱의 컴포넌트가 호스트의 스타일을 의도치 않게 상속받지 않도록 해야 합니다. styled-components 사용이나 virtual DOM 활용 등 여러 방법이 있습니다.</li></ol><h3 id="--7">운영 시 고려사항</h3><ol><li><strong>모니터링 및 분석</strong> - 요구사항에 따라 에러 모니터링 서비스 같은 모니터링 스크립트를 공유 인스턴스로 사용할지 아니면 마이크로프론트엔드 컨텍스트 안에서 별도로 실행할지 결정해야 합니다. 리모트에서는 <code>index</code> 파일이 렌더링되는 게 아니라 컴포넌트가 노출되는 방식이라 이 부분이 까다로울 수 있습니다.</li><li><strong>배포 및 캐싱</strong> - 마이크로프론트엔드 리모트 번들은 안정성을 위해 서버에서 직접 제공하기보다 S3 버킷에 호스팅하는 것을 권장합니다. 또한 <code>remoteEntry.js</code>를 제외한 파일에는 장기 캐싱을 적용하는 것이 좋습니다. <code>remoteEntry.js</code>의 파일 이름 뒤에는 일반적으로 해시가 붙지 않으며 로드할 다른 의존성의 링크를 담고 있습니다.</li></ol><h2 id="--8">결론</h2><p>마이크로프론트엔드는 여러 팀이 함께 프론트엔드를 개발할 때 확장성 있는 해결책이며 그 중에서도 Module Federation이 가장 유연한 현대적 방식으로 자리잡고 있습니다.</p><p>서버 측 구성 같은 전통적인 방식도 특정 상황에서는 여전히 유효하지만 Module Federation은 복잡한 애플리케이션에 필요한 런타임 유연성과 성능을 제공합니다.</p><p>최종 선택은 팀 구조, 기술적 요구사항, 그리고 복잡한 구현을 감수할 수 있는지에 따라 달라집니다. 마이크로프론트엔드가 처음이라면 더 단순한 방식부터 시작하고 필요에 따라 Module Federation으로 나아가는 것을 고려해보세요.</p><h2 id="--9">다음 단계</h2><p>이번 게시글에서는 전체적인 그림을 그리는 데 초점을 맞췄습니다. 다음에는 Module Federation을 더 깊이 다루며 기술적으로 어려운 점과 해결 방법을 자세히 살펴볼 예정입니다. 기대해주세요!</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ JSON Web Token 핸드북: 웹 인증을 위한 JWT 활용 가이드 ]]>
                </title>
                <description>
                    <![CDATA[ JWT는 JSON Web Token의 약자이며, 웹 개발을 하다 보면 정말 자주 보게 되는 용어입니다. 기본적으로 JWT는 두 주체 간에 특정한 정보(claim)를 안전하게 표현하고 전달하기 위한 JSON 기반의 개방형 표준입니다. 특히 마이크로서비스 아키텍처나 최신 인증 시스템에서 매우 널리 사용된다는 점이 인상적인 부분입니다. 이 글에서는 JWT가 실제로 무엇인지, 어떻게 구성되어 있는지, 그리고 ]]>
                </description>
                <link>https://www.freecodecamp.org/korean/news/the-json-web-token-handbook-learn-to-use-jwts-for-web-authentication/</link>
                <guid isPermaLink="false">69391e52ffd06f04d7d7edb5</guid>
                
                    <category>
                        <![CDATA[ JSON Web Tokens (JWT) ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ NodeJS ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Jong-Ho Kim ]]>
                </dc:creator>
                <pubDate>Fri, 19 Dec 2025 04:59:54 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/korean/news/content/images/2025/12/json-to-web-token-handbook-cover.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>기사 원문:</strong> <a href="https://www.freecodecamp.org/news/the-json-web-token-handbook-learn-to-use-jwts-for-web-authentication/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">The JSON Web Token Handbook: Learn to Use JWTs for Web Authentication</a>
      </p><p>JWT는 JSON Web Token의 약자이며, 웹 개발을 하다 보면 정말 자주 보게 되는 용어입니다.</p><p>기본적으로 JWT는 두 주체 간에 특정한 정보(claim)를 안전하게 표현하고 전달하기 위한 JSON 기반의 개방형 표준입니다. 특히 마이크로서비스 아키텍처나 최신 인증 시스템에서 매우 널리 사용된다는 점이 인상적인 부분입니다.</p><p>이 글에서는 JWT가 실제로 무엇인지, 어떻게 구성되어 있는지, 그리고 웹 애플리케이션을 어떻게 안전하게 만드는지 자세히 살펴보겠습니다. 글을 다 읽고 나면 왜 개발자들이 매일같이 JWT를 사용하는지 이해할 수 있게 될 것입니다.</p><h2 id="-">이 글에서 다룰 내용</h2><ol><li>사전 지식</li><li>JWT란 무엇인가?</li><li>왜 토큰이 필요한가?</li><li>JWT의 구조: 헤더, 페이로드, 시그니처</li><li>예제: JWT 디코딩하기</li><li>JWT가 보안을 보장하는 방식: 시그니처</li><li>보안 고려사항 및 토큰 관리 방법</li><li>다양한 언어에서 JWT 생성 방법</li><li>실전 구현: Express + MongoDB로 만드는 JWT 인증</li><li>요약</li><li>마지막 한마디</li></ol><h2 id="--1">사전 지식</h2><p>이 가이드를 따라가며 최대한 많은 것을 얻으려면 다음과 같은 내용이 준비되어 있으면 좋습니다:</p><ol><li>JavaScript / Node.js에 대한 기초적인 이해</li><li>로컬 환경에 설치된 Node.js와 npm</li><li>HTTP와 REST API의 기본 개념</li><li>JSON 구조와 역직렬화 / 직렬화 방법</li><li>Express에 대한 기초 지식 (혹은 튜토리얼을 따라갈 수 있는 정도의 지식)</li><li>실행 중인 MongoDB (로컬 또는 원격)</li><li>비동기 코드 / Promise / Async–Await 사용 경험</li><li>환경 변수 및 <code>.env</code> 설정에 대한 이해</li></ol><p>이 글과 함께 볼 수 있는 동영상도 준비되어 있습니다. 텍스트와 함께 영상으로 학습하는 것을 좋아한다면 같이 참고해 보면 좋겠습니다.</p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/6drpx_QcMdg?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" title="What is JWT Authentication? JSON Web Token Tutorial" name="fitvid0"></iframe>
          </div>
        </div>
      </figure><h2 id="jwt-">JWT란 무엇인가?</h2><p>JWT는 오늘날 가장 흔히 인증 용도로 사용되지만, 사실 원래 목적은 조금 달랐습니다. 처음에는 두 주체 간에 정보를 안전하게 교환하기 위한 표준 방식을 제공하기 위해 만들어졌습니다. 이를 위해 <a href="https://datatracker.ietf.org/doc/html/rfc7519">RFC 7519</a>라는 산업 표준 사양이 정의되었으며, JWT가 어떻게 구조화되고, 어떤 방식으로 데이터 교환에 사용되어야 하는지 명시하였습니다. JavaScript를 위한 표준을 정의하는 <a href="https://en.wikipedia.org/wiki/ECMAScript#:~:text=ECMAScript%20\(%2F%CB%88%C9%9Bkm,pages%20across%20different%20web%20browsers.">ECMAScript</a> (혹은 ES)와 비슷한 개념이라고 보면 되겠습니다.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/korean/news/content/images/2025/12/auth1.jpeg" class="kg-image" alt="auth1" srcset="https://www.freecodecamp.org/korean/news/content/images/size/w600/2025/12/auth1.jpeg 600w, https://www.freecodecamp.org/korean/news/content/images/size/w1000/2025/12/auth1.jpeg 1000w, https://www.freecodecamp.org/korean/news/content/images/size/w1600/2025/12/auth1.jpeg 1600w, https://www.freecodecamp.org/korean/news/content/images/2025/12/auth1.jpeg 1919w" sizes="(min-width: 720px) 720px" width="1919" height="1080" loading="lazy"></figure><p>실제 애플리케이션에서는 JWT가 주로 인증 용도로 사용되고 있고, 이 글에서도 그 관점에서 집중하여 설명할 것입니다.</p><p>하지만 기억해야 할 점은, JWT가 단순히 인증만을 위해 디자인된 것은 아니라는 것입니다. 인증을 구현하는 방법은 JWT 말고도 여러 가지가 있고, 그 중 가장 대표적인 대안이 바로 세션 토큰(session token)입니다.</p><h2 id="--2">왜 토큰이 필요한가?</h2><p>우리가 어떤 인증 전략을 사용하든지 간에, 그게 세션 토큰이든 혹은 JWT든 간에, 그 바탕에는 토큰이 필요한 공통된 이유가 있습니다. 바로 HTTP 프로토콜이 상태를 유지하지 않는(stateless) 특성을 갖고 있기 때문입니다.</p><p>브라우저에서 서버로, 혹은 서버끼리 HTTP를 사용해 요청과 응답을 주고받을 때, HTTP 자체는 아무런 정보를 저장하지 않습니다.</p><p><em>Stateless</em>라는 말은, 클라이언트와 서버가 상호작용할 때 HTTP가 이전 요청이나 데이터를 기억하지 않는다는 의미입니다. 다시 말해, 모든 요청은 필요한 정보를 스스로 담아서 보내야만 합니다. HTTP는 자체적으로 데이터를 저장하지 않습니다. 정보를 받으면 바로 “잊어버리는” 구조입니다. 그래서 HTTP를 stateless 프로토콜이라고 부릅니다.</p><p>이를 다른 식으로 생각해 보면 이렇습니다. 어떤 서버에 있는 웹페이지에 접속할 때, 우리는 서버에 어떤 정보를 보낼까요? 단순한 정적(static) 웹사이트라면, 사실 보내는 정보는 많지 않습니다. 그냥 페이지의 URL을 서버에 보내면, 서버는 그에 대응하는 HTML 페이지를 응답으로 돌려줍니다. 이 경우 서버는 별도의 정보를 기억할 필요가 없고, 상태를 유지할 필요도 없습니다. 이것이 바로 HTTP가 디자인된 방식이며, HTTP가 stateless 프로토콜인 이유입니다.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/korean/news/content/images/2025/12/7e6081f5-7d34-462a-9a7d-bcffd0242e00.jpeg" class="kg-image" alt="7e6081f5-7d34-462a-9a7d-bcffd0242e00" srcset="https://www.freecodecamp.org/korean/news/content/images/size/w600/2025/12/7e6081f5-7d34-462a-9a7d-bcffd0242e00.jpeg 600w, https://www.freecodecamp.org/korean/news/content/images/size/w1000/2025/12/7e6081f5-7d34-462a-9a7d-bcffd0242e00.jpeg 1000w, https://www.freecodecamp.org/korean/news/content/images/size/w1600/2025/12/7e6081f5-7d34-462a-9a7d-bcffd0242e00.jpeg 1600w, https://www.freecodecamp.org/korean/news/content/images/2025/12/7e6081f5-7d34-462a-9a7d-bcffd0242e00.jpeg 1919w" sizes="(min-width: 720px) 720px" width="1919" height="1080" loading="lazy"></figure><p>하지만 웹 애플리케이션이 유저마다 다른 응답을 제공하는 동적(dynamic) 웹사이트라면 이야기가 달라집니다. 이때는 URL만 보내서는 충분하지 않습니다. 유저는 URL과 함께 자신의 신원 정보(identity)도 서버에 전달해야 합니다.</p><p>예를 들어, 어떤 유저가 <code>page-1</code>에 접근하고 싶다면 서버에 이렇게 말해야 합니다. “<em>저는 A 유저입니다. </em><code><em>page-1</em></code><em>을 보여 주세요.</em>” 그러면 서버는 해당 유저를 위한 <code>page-1</code>을 응답으로 돌려줍니다. 이후 유저가 다시 “<em>이번에는 <code>page-2</code>를 주세요.</em>”라고 요청하면 어떻게 될까요? HTTP는 stateless이기 때문에, 이 요청에 유저의 신원 정보가 포함되어 있지 않으면, 서버는 누가 요청했는지 알 수 없고 어떤 응답을 보내야 할지 판단할 수 없습니다. 그래서 매 요청마다 유저는 자신의 신원 정보를 함께 보내야 합니다.</p><p>그런데 실제 우리가 사용하는 웹사이트들을 보면, 정말 매번 신원 정보를 보내고 있나요? 예를 들어 Facebook을 생각해 보겠습니다. 한 번 로그인을 하고 나면, 이후에는 다시 로그인 인증을 하지 않아도 홈 화면이나 프로필 페이지에 접근할 수 있습니다.</p><p>그렇다면 질문이 생깁니다. HTTP가 stateless 프로토콜이라면, 어떻게 이런 일이 가능한가요? 웹 애플리케이션은 우리의 브라우징 세션을 어떻게 기억하는 걸까요? 그 답은 웹 애플리케이션이 세션을 여러 방식으로 유지할 수 있기 때문입니다. 그중 가장 흔한 방법 중 하나가 <strong>토큰</strong>을 사용하는 방식입니다.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/korean/news/content/images/2025/12/7b7cdeab-4baa-4cda-bbeb-aaf4e4d4170c.jpeg" class="kg-image" alt="7b7cdeab-4baa-4cda-bbeb-aaf4e4d4170c" srcset="https://www.freecodecamp.org/korean/news/content/images/size/w600/2025/12/7b7cdeab-4baa-4cda-bbeb-aaf4e4d4170c.jpeg 600w, https://www.freecodecamp.org/korean/news/content/images/size/w1000/2025/12/7b7cdeab-4baa-4cda-bbeb-aaf4e4d4170c.jpeg 1000w, https://www.freecodecamp.org/korean/news/content/images/size/w1600/2025/12/7b7cdeab-4baa-4cda-bbeb-aaf4e4d4170c.jpeg 1600w, https://www.freecodecamp.org/korean/news/content/images/2025/12/7b7cdeab-4baa-4cda-bbeb-aaf4e4d4170c.jpeg 1918w" sizes="(min-width: 720px) 720px" width="1918" height="1077" loading="lazy"></figure><h3 id="-session-token-">세션 토큰(Session Token): 고전적인 방식</h3><p>토큰 방식에는 크게 두 가지 옵션이 있습니다. 하나는 <strong>세션 토큰</strong>이 있고, 다른 하나는 <strong>JSON Web Token (JWT)</strong>가 있습니다. 두 방식을 이해해야 JWT가 무엇인지, 왜 쓰이는지 더 분명해집니다.</p><p>어떤 회사의 고객센터를 예로 들어보겠습니다. 고객이 전화를 걸어 불만을 제기합니다. 고객센터 상담사가 제기된 문제를 듣고 여러 가지 해결 방법을 시도하지만, 바로 해결하지 못합니다.</p><p>이때 상담사는 상위 관리자 팀으로 이관하고, 고객에 대한 케이스 파일을 만듭니다. 이 파일에는 고객과의 대화 내용과 여태 시도한 해결 방법들이 기록됩니다. 그리고 고객에게 케이스 번호를 알려줍니다. 그렇게 함으로써 고객이 다음에 전화했을 때, 처음부터 모든 이야기를 반복할 필요가 없어집니다.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/korean/news/content/images/2025/12/c56bb7da-f6dd-4afe-b16b-966149bc7f91.jpeg" class="kg-image" alt="c56bb7da-f6dd-4afe-b16b-966149bc7f91" srcset="https://www.freecodecamp.org/korean/news/content/images/size/w600/2025/12/c56bb7da-f6dd-4afe-b16b-966149bc7f91.jpeg 600w, https://www.freecodecamp.org/korean/news/content/images/size/w1000/2025/12/c56bb7da-f6dd-4afe-b16b-966149bc7f91.jpeg 1000w, https://www.freecodecamp.org/korean/news/content/images/size/w1600/2025/12/c56bb7da-f6dd-4afe-b16b-966149bc7f91.jpeg 1600w, https://www.freecodecamp.org/korean/news/content/images/2025/12/c56bb7da-f6dd-4afe-b16b-966149bc7f91.jpeg 1920w" sizes="(min-width: 720px) 720px" width="1920" height="1080" loading="lazy"></figure><p>다음 날 고객이 다시 전화를 걸어 케이스 번호를 말하면, 상담사는 해당 번호를 가지고 이력과 정보를 확인한 뒤 바로 응대를 할 수 있습니다.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/korean/news/content/images/2025/12/426af5fa-ff38-4ce2-ae1b-48ca1a8f1e6c.jpeg" class="kg-image" alt="426af5fa-ff38-4ce2-ae1b-48ca1a8f1e6c" srcset="https://www.freecodecamp.org/korean/news/content/images/size/w600/2025/12/426af5fa-ff38-4ce2-ae1b-48ca1a8f1e6c.jpeg 600w, https://www.freecodecamp.org/korean/news/content/images/size/w1000/2025/12/426af5fa-ff38-4ce2-ae1b-48ca1a8f1e6c.jpeg 1000w, https://www.freecodecamp.org/korean/news/content/images/size/w1600/2025/12/426af5fa-ff38-4ce2-ae1b-48ca1a8f1e6c.jpeg 1600w, https://www.freecodecamp.org/korean/news/content/images/2025/12/426af5fa-ff38-4ce2-ae1b-48ca1a8f1e6c.jpeg 1920w" sizes="(min-width: 720px) 720px" width="1920" height="1080" loading="lazy"></figure><p>이 시나리오는 웹 애플리케이션에서 세션 토큰을 이용한 인증 방식과 유사합니다. 유저가 인증에 성공하면, 서버는 세션을 생성하고 이를 추적하기 시작합니다. 그리고 해당 세션을 식별하기 위한 세션 ID를 새로 생성해 유저에게 전달합니다. 이는 앞서 예시로 든 고객 케이스 번호와 유사합니다. 이후부터는 유저가 서버에 요청을 보낼 때마다, 세션 ID 또는 토큰이 함께 전달되고, 서버는 전달받은 ID를 기반으로 세션을 조회해 요청을 보낸 클라이언트를 식별합니다. 서버는 동시에 여러 클라이언트를 처리해야 하므로, 이러한 세션 토큰 방식은 인증을 구현하는 데 있어 효과적이고 널리 사용되는 전략으로 자리 잡았습니다.</p><p>클라이언트가 이 세션 ID를 서버로 어떻게 보내느냐는 구현에 따라 달라질 수 있지만, 가장 흔한 방식은 브라우저의 쿠키(cookie)에 세션 ID를 저장하는 것입니다. 이 방법의 장점은, 브라우저가 동일한 서버로 요청을 보낼 때마다 자동으로 쿠키 정보를 헤더에 실어 보내준다는 점입니다. 이는 브라우저의 기본 동작이기 때문에, 별도의 처리 없이도 자연스럽게 동작합니다.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/korean/news/content/images/2025/12/5881de41-571d-40ca-a0f7-4022d8c41754.jpeg" class="kg-image" alt="5881de41-571d-40ca-a0f7-4022d8c41754" srcset="https://www.freecodecamp.org/korean/news/content/images/size/w600/2025/12/5881de41-571d-40ca-a0f7-4022d8c41754.jpeg 600w, https://www.freecodecamp.org/korean/news/content/images/size/w1000/2025/12/5881de41-571d-40ca-a0f7-4022d8c41754.jpeg 1000w, https://www.freecodecamp.org/korean/news/content/images/size/w1600/2025/12/5881de41-571d-40ca-a0f7-4022d8c41754.jpeg 1600w, https://www.freecodecamp.org/korean/news/content/images/2025/12/5881de41-571d-40ca-a0f7-4022d8c41754.jpeg 1920w" sizes="(min-width: 720px) 720px" width="1920" height="1080" loading="lazy"></figure><p>유저가 인증에 성공하면 서버는 브라우저 쿠키에 세션 ID를 저장하고, 이후부터는 모든 요청에 쿠키가 자동으로 포함되어 서버가 유저를 식별할 수 있게 됩니다. 이 방식은 과거에 매우 널리 사용되었지만, 현대적인 애플리케이션 환경에서는 다소 구식이 된 측면도 있습니다.</p><p>이 방식에는 몇 가지 문제가 있습니다. 가장 큰 문제는 서버가 하나뿐이라는 전제를 바탕에 두고 있다는 점입니다. 요즘의 웹 애플리케이션은 보통 여러 대의 서버를 사용합니다. 이런 경우, 유저 앞단에는 로드 밸런서(load balancer)가 있고, 이 로드 밸런서가 어떤 서버에 요청을 보낼건지 결정합니다.</p><p>세션 토큰을 사용하는 상황을 가정해 보겠습니다. 유저가 첫 번째 요청을 보냈고, 로드 밸런서가 이 요청을 <code>Server-1</code>으로 보냅니다. <code>Server-1</code>은 세션 ID를 생성해 클라이언트에게 돌려줍니다. 이후 유저가 또 다른 요청을 보내고, 이번에는 로드 밸런서가 이 요청을 <code>Server-2</code>로 보냅니다. 그런데 <code>Server-2</code>에는 해당 세션 ID에 대한 정보가 없습니다. 그렇다면 이 요청이 누구의 것인지 어떻게 알 수 있을까요?</p><p>일반적인 해결책은 세션 ID를 각 서버가 아니라 공유된 Redis 데이터베이스에 저장하는 것입니다. 이렇게 하면 어느 서버든 Redis에서 세션 ID를 조회할 수 있습니다. 이를 흔히 <strong>Redis cache</strong>라고 부릅니다. 하지만 마이크로서비스 아키텍처에서는 이 방식에 허점이 있습니다. 만약 어떤 이유로 Redis cache가 다운된다면, 서버들은 정상적으로 작동하고 있더라도 인증 메커니즘이 실패할 수 있습니다. 이 지점에서 JSON Web Token이 다른 접근 방식을 제시합니다.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/korean/news/content/images/2025/12/a970e2d9-6663-4a4e-9c63-37ea13470b90.jpeg" class="kg-image" alt="a970e2d9-6663-4a4e-9c63-37ea13470b90" srcset="https://www.freecodecamp.org/korean/news/content/images/size/w600/2025/12/a970e2d9-6663-4a4e-9c63-37ea13470b90.jpeg 600w, https://www.freecodecamp.org/korean/news/content/images/size/w1000/2025/12/a970e2d9-6663-4a4e-9c63-37ea13470b90.jpeg 1000w, https://www.freecodecamp.org/korean/news/content/images/size/w1600/2025/12/a970e2d9-6663-4a4e-9c63-37ea13470b90.jpeg 1600w, https://www.freecodecamp.org/korean/news/content/images/2025/12/a970e2d9-6663-4a4e-9c63-37ea13470b90.jpeg 1920w" sizes="(min-width: 720px) 720px" width="1920" height="1080" loading="lazy"></figure><h3 id="jwt--1">JWT: 현대적인 해결책</h3><p>앞에서 살펴본 고객센터 예시로 다시 돌아가 보죠. 이번에는 전화나 시스템이 전혀 없다고 가정해 보겠습니다. 고객이 직접 회사 사무실로 찾아와 상담사와 대면으로 이야기합니다. 이번에는 상담사가 시스템에 정보를 저장할 수 없으니, 대신 종이에 모든 내용을 적은 뒤 그 종이를 고객에게 건네주며 이렇게 말합니다. “<em>다음에 올 때 이걸 가져오세요.</em>”</p><p>이 방식은 앞서 봤던 개념과 조금 다르죠? 하지만, 이 방식도 여전히 다른 문제가 있습니다. 바로 <strong>유효성(validity)</strong> 문제입니다. 만약 그 고객이 진짜 고객이 아니고 악의적인 의도를 품고 있다면, 상담사는 어떻게 그 고객을 신뢰할 수 있을까요? 다음 날 그 고객이 빈 종이에 같은 정보를 적어 들고 온다면, 상담사는 그 고객의 신원이 유효한지 어떻게 확인할 수 있을까요?</p><p>이때 가능한 해결 방법은, 상담사가 종이를 건네줄 때 직접 서명을 해 두는 것입니다. 그러면 고객이 다음에 그 종이를 가져왔을 때, 상담사는 그 서명을 확인함으로써 해당 종이를 신뢰할 수 있는지 판단할 수 있습니다.</p><p>JSON Web Token은 이와 유사한 방식으로 동작합니다. 여기서는 클라이언트가 인증되었을 때, 서버가 모든 정보를 저장하는 대신 유저의 정보를 서명(signature)과 함께 JSON 토큰 형태로 클라이언트에게 전달합니다. 이후 클라이언트는 다음 요청부터 매번 이 토큰을 함께 전송합니다. 이 토큰에는 이 유저가 어떤 유저인지, 이름이 무엇인지, 그리고 그 외 필요한 정보들이 포함되어 있습니다.</p><p>이 경우 서버는 아무것도 저장하지 않고, 모든 정보는 클라이언트에 유지됩니다. 클라이언트가 이 토큰과 함께 요청을 보낼 때마다, 서버는 토큰을 읽고 어떤 유저가 요청을 보냈는지 식별하고 필요한 데이터를 제공합니다.</p><p>이 토큰은 단순한 ID가 아닙니다. 모든 정보를 담고 있는 JSON 객체이며, 이것이 바로 JSON Web Token입니다. 이 JWT를 어디에 저장할지는 전적으로 클라이언트의 선택입니다. 가장 일반적인 방식은 브라우저의 쿠키나 로컬 스토리지(local storage)에 저장하는 것입니다.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/korean/news/content/images/2025/12/691848c9-e4c2-4b3f-b3f5-06623627e38f.jpeg" class="kg-image" alt="691848c9-e4c2-4b3f-b3f5-06623627e38f" srcset="https://www.freecodecamp.org/korean/news/content/images/size/w600/2025/12/691848c9-e4c2-4b3f-b3f5-06623627e38f.jpeg 600w, https://www.freecodecamp.org/korean/news/content/images/size/w1000/2025/12/691848c9-e4c2-4b3f-b3f5-06623627e38f.jpeg 1000w, https://www.freecodecamp.org/korean/news/content/images/size/w1600/2025/12/691848c9-e4c2-4b3f-b3f5-06623627e38f.jpeg 1600w, https://www.freecodecamp.org/korean/news/content/images/2025/12/691848c9-e4c2-4b3f-b3f5-06623627e38f.jpeg 1920w" sizes="(min-width: 720px) 720px" width="1920" height="1080" loading="lazy"></figure><h2 id="jwt--2">JWT의 구조: 헤더, 페이로드, 시그니처</h2><p>앞서 언급한 것 처럼, 서버는 JSON 객체를 전달받지만, JWT는 일반적인 JSON처럼 보이지는 않습니다.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/korean/news/content/images/2025/12/f74219b8-4a01-4ac4-920b-449faf103520.png" class="kg-image" alt="f74219b8-4a01-4ac4-920b-449faf103520" srcset="https://www.freecodecamp.org/korean/news/content/images/size/w600/2025/12/f74219b8-4a01-4ac4-920b-449faf103520.png 600w, https://www.freecodecamp.org/korean/news/content/images/size/w1000/2025/12/f74219b8-4a01-4ac4-920b-449faf103520.png 1000w, https://www.freecodecamp.org/korean/news/content/images/size/w1600/2025/12/f74219b8-4a01-4ac4-920b-449faf103520.png 1600w, https://www.freecodecamp.org/korean/news/content/images/2025/12/f74219b8-4a01-4ac4-920b-449faf103520.png 1920w" sizes="(min-width: 720px) 720px" width="1920" height="1078" loading="lazy"></figure><p>위 이미지에서는 JWT가 다소 특이하게 보일 수 있습니다. 실제로 이것은 JSON 객체를 인코딩한 형태로, 일종의 복잡하게 압축된 표현 방식입니다. 자세히 살펴보면 JWT는 점(.)으로 구분된 세 부분으로 나뉘어 있다는 것을 알 수 있습니다. 첫 번째 부분은 <strong>헤더(header)</strong>, 두 번째 부분은 본질적으로 데이터를 담고 있는 <strong>JSON 페이로드(payload)</strong>, 그리고 세 번째 부분은 <strong>시그니처(signature)</strong>입니다.</p><p>각 부분을 개별적으로 살펴보면 다음과 같습니다.</p><ul><li><strong>헤더</strong>는 하나의 독립적인 JSON 객체입니다.</li><li><strong>페이로드</strong> 역시 데이터를 포함하고 있는 별도의 JSON 객체입니다.</li><li>세 번째 부분은 <strong>시그니처</strong>입니다.</li></ul><p>그렇다면 시그니처는 무엇일까요? 간단히 말해 토큰의 내용은 시크릿 키(secret key)와 함께 해시(hash)되는데, 그게 바로 시그니처입니다. 여기서 이 시크릿 키는 서버 내부에만 저장되고, JSON Web Token이 서버로 전달되면, 서버는 해당 시크릿 키를 사용해 시그니처가 여전히 유효한지, 중간에 변조되지는 않았는지 등을 확인할 수 있습니다.</p><blockquote>번역자 코멘트: 해시(hash)는 데이터를 고정된 길이의 값으로 변환해 주는 계산 방식입니다. 입력 내용이 조금이라도 달라지면 결과 값은 완전히 달라지게 됩니다. 특히 JWT는 시크릿 키(secret key)와 함께 해시를 하기에, 결과 값을 &nbsp;뒤로 되돌려 원래의 데이터를 얻는 건 불가능에 가깝습니다. 누군가 중간에 JWT를 가로채 데이터를 변조했다면, 서버는 바로 알아챌 수 있습니다.</blockquote><h2 id="-jwt-">예제: JWT 디코딩하기</h2><p>예제를 하나 살펴보겠습니다. JWT를 다루고 그 구조를 이해하기에 가장 좋은 웹사이트는<a href="https://www.jwt.io/"> jwt.io</a>입니다. JWT 하나를 이 사이트에 붙여 넣으면 헤더, 페이로드, 시그니처라는 세 개의 영역이 표시됩니다. 페이로드는 “Decoded Payload” 부분에 나타나며, 실제 내용과 데이터가 들어 있습니다. 이 안에는 ID, 이름을 담고 있는 JSON 객체, 그리고 만료일 등이 포함되어 있는 것을 확인할 수 있습니다.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/korean/news/content/images/2025/12/84c2532f-dc09-4a96-83de-ecd4a24d958f.jpeg" class="kg-image" alt="84c2532f-dc09-4a96-83de-ecd4a24d958f" srcset="https://www.freecodecamp.org/korean/news/content/images/size/w600/2025/12/84c2532f-dc09-4a96-83de-ecd4a24d958f.jpeg 600w, https://www.freecodecamp.org/korean/news/content/images/2025/12/84c2532f-dc09-4a96-83de-ecd4a24d958f.jpeg 636w" width="636" height="367" loading="lazy"></figure><p>"Decoded Header" 부분 역시 완전히 유효한 JSON 객체이며, JWT를 생성하거나 검증할 때 사용된 알고리즘과 토큰의 타입을 명시합니다. &nbsp;</p><p>주요 데이터는 “Decoded Payload” 부분에 들어 있습니다. 세 번째 부분은 시그니처입니다. 여기서 한 가지 중요한 점이 있습니다. 이처럼 복잡하게 섞여보이는 토큰이 어디에서 오는지 궁금할 수 있는데, 그건 사실 매우 단순합니다. “Decoded Payload”에 있는 데이터가 <strong>Base64</strong>로 인코딩되어 있으며, 이것이 바로 이처럼 난해하게 보이는 토큰의 형태를 만듭니다.</p><p>이 JWT의 해당 부분을 복사해 온라인에서 찾을 수 있는 Base64 디코더에 일단 붙여 넣어 보면, 즉시 원래의 데이터를 확인할 수 있습니다.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/korean/news/content/images/2025/12/4ee950a2-2ad0-40b4-8287-fdfea9543a6f.png" class="kg-image" alt="4ee950a2-2ad0-40b4-8287-fdfea9543a6f" srcset="https://www.freecodecamp.org/korean/news/content/images/size/w600/2025/12/4ee950a2-2ad0-40b4-8287-fdfea9543a6f.png 600w, https://www.freecodecamp.org/korean/news/content/images/size/w1000/2025/12/4ee950a2-2ad0-40b4-8287-fdfea9543a6f.png 1000w, https://www.freecodecamp.org/korean/news/content/images/size/w1600/2025/12/4ee950a2-2ad0-40b4-8287-fdfea9543a6f.png 1600w, https://www.freecodecamp.org/korean/news/content/images/2025/12/4ee950a2-2ad0-40b4-8287-fdfea9543a6f.png 1919w" sizes="(min-width: 720px) 720px" width="1919" height="1080" loading="lazy"></figure><p>그렇다면 이것이 의미하는 바는 무엇일까요? 이는 이 데이터를 다시 Base64로 인코딩하면 동일한 토큰이 생성된다는 뜻입니다. 헤더 역시 동일한 방식으로 동작합니다.</p><p>마지막으로, 이 인코딩된 부분에 대해 짚고 넘어가야 할 점이 있습니다. 이것이 보안을 위한 것일까요? 그렇지 않습니다. 이는 오직 편의를 위한 것입니다. JSON 객체는 상당히 길어질 수 있고, 모든 프로그래밍 언어가 이를 동일한 방식으로 처리하지는 않습니다. JavaScript에서는 비교적 쉽지만, 다른 언어에서는 문제가 되는 경우도 있습니다. 그래서 데이터를 다루기 쉽게 만들기 위해 Base64 인코딩을 사용하는 것입니다. 이는 보안을 위한 것이 아니며, 이런 방식의 인코딩은 데이터를 안전하게 만들어 주지 않습니다. 정보는 여전히 공개적으로 확인할 수 있습니다.</p><p>위 그림에서 볼 수 있듯이, 이 Base64 디코더 사이트에 토큰을 입력하는 순간 데이터는 즉시 표시됩니다. 이는 여기에 민감한 정보를 저장해서는 안 된다는 의미입니다. 유저 ID와 같은 유저 식별 정보나 기타 공개 가능한 정보만 담아야 합니다. <strong>비밀번호나 시크릿 키와 같은 정보는 쉽게 읽힐 수 있기 때문에 절대로 토큰에 저장해서는 안 됩니다.</strong> 겉보기에는 인코딩되어 보이지만, 실제로는 공개된 데이터입니다.</p><h2 id="jwt--3">JWT가 보안을 보장하는 방식: 시그니처</h2><p>이제 시그니처(signature)를 통해 보안에 대한 보장이 이루어지는 부분으로 넘어가 보겠습니다. 앞서 고객센터 종이 예시에서는 사람이 직접 손으로 서명을 추가할 수 있었습니다.</p><p>하지만 데이터의 경우, 시그니처를 생성하는 과정은 다릅니다. 데이터에서는 실제 서명 역할을 하는 시크릿 키를 사용해 암호학적으로 시그니처를 생성합니다. 시그니처를 만드는 과정은 다음과 같습니다:</p><ol><li>데이터는 Base64로 인코딩됩니다.</li><li>인코딩된 데이터에 시크릿 키를 결합(concatenate) 합니다.</li><li>이를 다시 Base64로 인코딩합니다.</li></ol><p>사용할 알고리즘은 변경할 수 있지만, 한 번 선택된 알고리즘은 토큰을 생성할 때와 검증할 때 동일하게 사용되어야 합니다. 생성과 검증 과정에서 서로 다른 알고리즘을 사용할 수는 없습니다.</p><p>마지막으로 데이터는 시크릿 키를 사용해 해시됩니다. 이 시크릿 키는 외부에 공개되지 않으며, 오직 서버에만 보관됩니다. 보통은 서버의 보안 저장소(server vault)에 안전하게 저장됩니다. 이 JWT가 서버에 도착하면, 서버는 해당 시크릿 키를 사용해 토큰이 유효한지 검증합니다. 만약 값이 올바르게 일치하지 않으면 “<em>invalid signature</em>”라는 결과가 표시됩니다. 이를 통해 서버는 토큰이 중간에 변조되었는지 여부를 확인할 수 있습니다.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/korean/news/content/images/2025/12/bf017016-d9fd-43cb-836a-eafe4f35540b.jpeg" class="kg-image" alt="bf017016-d9fd-43cb-836a-eafe4f35540b" srcset="https://www.freecodecamp.org/korean/news/content/images/size/w600/2025/12/bf017016-d9fd-43cb-836a-eafe4f35540b.jpeg 600w, https://www.freecodecamp.org/korean/news/content/images/size/w1000/2025/12/bf017016-d9fd-43cb-836a-eafe4f35540b.jpeg 1000w, https://www.freecodecamp.org/korean/news/content/images/2025/12/bf017016-d9fd-43cb-836a-eafe4f35540b.jpeg 1224w" sizes="(min-width: 720px) 720px" width="1224" height="1078" loading="lazy"></figure><p>예를 들어, 시그니처로 <code>love-you-all-from-logicbaselabs</code>를 사용하고 서버에서 이를 검증했을 때 “<em>signature verified</em>”라는 결과가 나온다면, 이는 시크릿 키가 서버에만 존재한다는 것을 보여줍니다. 즉, 공개된 정보가 포함되어 있더라도 토큰의 유효성은 검증될 수 있습니다.</p><p>다만 JSON Web Token은 비밀번호와 같은 개념은 아닙니다. JWT는 주로 유저를 식별하는 역할을 합니다. 서버는 JWT를 확인해 이것이 유효한 유저에게 속한 토큰인지 판단할 수 있습니다. 다시 말해 JWT는 유저의 신원을 나타내며, 시그니처와 함께 보안 요소를 포함하고 있는 매우 중요한 토큰입니다.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/korean/news/content/images/2025/12/a434b453-0a38-41a5-93f3-bd12b46806f3qq.jpeg" class="kg-image" alt="a434b453-0a38-41a5-93f3-bd12b46806f3qq" srcset="https://www.freecodecamp.org/korean/news/content/images/size/w600/2025/12/a434b453-0a38-41a5-93f3-bd12b46806f3qq.jpeg 600w, https://www.freecodecamp.org/korean/news/content/images/size/w1000/2025/12/a434b453-0a38-41a5-93f3-bd12b46806f3qq.jpeg 1000w, https://www.freecodecamp.org/korean/news/content/images/size/w1600/2025/12/a434b453-0a38-41a5-93f3-bd12b46806f3qq.jpeg 1600w, https://www.freecodecamp.org/korean/news/content/images/2025/12/a434b453-0a38-41a5-93f3-bd12b46806f3qq.jpeg 1920w" sizes="(min-width: 720px) 720px" width="1920" height="1078" loading="lazy"></figure><h2 id="--3">보안 고려사항 및 토큰 관리 방법</h2><p>한 가지 반드시 기억해야 할 점이 있습니다. 누군가가 당신의 JWT를 그대로 손에 넣었다면, 즉 완전히 동일한 토큰을 가지고 있다면, 그 사람은 해당 유저로 쉽게 로그인할 수 있습니다. 그저 그 토큰을 포함해 요청을 보내기만 하면 필요한 접근 권한을 얻을 수 있습니다.</p><p>이를 이렇게 생각해 볼 수 있습니다. 누군가가 당신의 Facebook 비밀번호를 알아내면, 당신의 Facebook 계정에 그대로 로그인할 수 있습니다. 마찬가지로 누군가가 PayPal 계정의 PIN을 획득했다면, 그 계정에 쉽게 접근할 수 있습니다. 즉, 가장 중요한 보안 정보를 누군가가 손에 넣는 순간, 이를 보호할 방법은 사실상 없습니다.</p><p>JWT도 동일합니다. 토큰을 클라이언트 측에서 얼마나 안전하게 보관하느냐가 절대적으로 중요합니다. 이런 점에서 우리는 어느 정도 취약할 수밖에 없습니다.</p><p>다만 세션 토큰과의 가장 큰 차이점이 있는데, 세션 토큰의 경우, 계정이 침해되었다고 판단되면 서버에서 해당 세션을 무효화할 수 있습니다. 다시 말해, 그 세션 ID로는 더 이상 누구도 로그인할 수 없게 됩니다.</p><p>하지만 JWT의 경우에는 만료일이 지나기 전까지 토큰이 계속 유효합니다. 따라서 이를 즉시 무효화할 수 있는 직접적인 방법이 없습니다. JWT는 암호학적으로 자기완결적인(self-contained) 구조를 가지며, 서버의 시크릿 키로 서명되어 있기 때문에, 한 번 생성되면 서버에서 바로 폐기할 수 없습니다.</p><p>이를 처리하는 유일한 방법은 웹에서 일반적으로 사용하는 방식인 토큰 차단 목록(denylist)을 관리하는 것입니다. 즉, 서버는 차단된 JWT 토큰들을 별도의 데이터베이스에 저장해 관리합니다. 요청이 들어오면 서버는 먼저 토큰이 형식적으로 유효한지 검증하고, 그 다음 미들웨어를 통해 해당 토큰이 denylist에 포함되어 있는지 확인합니다. 이 목록에 포함되어 있지 않은 경우에만 유저의 접근이 허용됩니다.</p><p>이것이 JSON Web Token을 사용할 때 지켜야 할 기본적인 규칙들입니다. JWT는 어떤 프로그래밍 언어에서도 사용할 수 있으며, 특히 REST API 환경에서 매우 널리 활용됩니다. 또한 마이크로서비스 아키텍처에서도 매우 보편적이고 널리 퍼진 방식입니다.</p><h2 id="-jwt--1">다양한 언어에서 JWT 생성 방법</h2><p>JWT를 생성하는 방법은 사용하는 프로그래밍 언어에 따라 달라집니다. 예를 들어 Node.js에서는<a href="https://www.npmjs.com/package/jsonwebtoken"> jsonwebtoken</a>과 같은 전용 라이브러리가 제공되기 때문에 비교적 쉽게 JWT를 만들 수 있습니다. PHP 역시 JWT를 생성하기 위한 사용하기 쉬운 라이브러리들이 존재합니다. 이처럼 JWT는 특정 프로그래밍 언어에 국한된 기술이 아니라, 어디에서나 사용할 수 있는 범용적인 도구입니다. 많은 사람들이 JWT가 JavaScript 전용이라고 생각하지만, 이는 사실이 아닙니다.</p><p>또한 JWT는 인증 용도로만 사용되는 것이 아니라는 점도 기억해야 합니다. JWT는 어떠한 형태로든 신원을 표현하는 데 사용할 수 있습니다. 예를 들어 콘서트에 입장할 때 일반적인 종이 티켓 대신 JWT를 사용해 접근 권한을 부여할 수도 있습니다. 이때 클라이언트가 해당 JWT를 사용하면, 게이트웨이(gateway)나 서버는 토큰을 읽어 필요한 정보에 대한 접근을 허용하고, 시그니처를 통해 토큰의 유효성을 검증할 수 있습니다.</p><h2 id="-express-mongodb-jwt-">실전 구현: Express + MongoDB로 만드는 JWT 인증</h2><p>이번에는 지금까지 배운 모든 개념을 실제로 적용해 보겠습니다. <a href="https://www.freecodecamp.org/news/the-express-handbook/">Express.js</a>와 <a href="https://www.freecodecamp.org/news/how-to-start-using-mongodb/">MongoDB</a>를 사용해 JWT 인증 시스템을 단계별로 구축할 것입니다.</p><p>처음에는 다소 부담스럽게 느껴질 수도 있지만 차근차근 따라오면 문제없습니다. 한 단계씩 차근차근 진행할 것이며, 마지막에는 실제로 동작하는 프로젝트를 완성하게 될 것입니다. 건물에 층층이 들어가 보듯이, 각 구간을 충분히 살펴보면서 확실한 이해를 얻고 갈 것입니다.</p><h3 id="1-">1. 프로젝트 세팅 &amp; 패키지 설치</h3><p>코드를 작성하기 전에 먼저 Node.js 프로젝트를 세팅하고 필요한 패키지들을 설치해야 합니다.</p><p><strong>Node.js 프로젝트 초기화</strong></p><p>터미널을 열어 다음과 같이 실행해보세요:</p><pre><code class="language-bash">mkdir jwt-auth-demo
cd jwt-auth-demo
npm init -y</code></pre><p>이 명령을 실행하면 기본 세팅이 된 <code>package.json</code> 파일이 생성됩니다.</p><p><strong>패키지 설치</strong></p><p>JWT 인증 시스템을 만들기 위해 다음 패키지들을 설치합니다.</p><pre><code class="language-bash">npm install express mongoose bcryptjs jsonwebtoken dotenv</code></pre><ul><li><code>express</code>: API 를 만들기 위한 빠르고 최소한의 Node.js 웹 프레임워크입니다.</li><li><code>mongoose</code>: MongoDB를 더 쉽게 다루기 위한 ODM(Object Data Modeling) 라이브러리입니다.</li><li><code>bcryptjs</code>: 비밀번호를 해시하기 위한 라이브러리입니다.</li><li><code>jsonwebtoken</code>: JWT 토큰을 생성하고 검증하는 라이브러리입니다.</li><li><code>dotenv</code>: <code>.env</code> 파일에 있는 환경 변수를 불러와서, 비밀 정보들을 안전하게 관리할 수 있게 해줍니다.</li></ul><p><strong>개발용 패키지 설치 (선택 사항)</strong></p><p>개발을 편하게 하기 위해 파일이 변경될 때마다 서버를 자동으로 재시작해 주는 <strong>nodemon</strong>을 설치해 보세요.</p><pre><code class="language-bash">npm install --save-dev nodemon</code></pre><p><code>package.json</code>의 scripts를 다음과 같이 수정합니다.</p><pre><code class="language-json">"scripts": {
  "start": "node server.js",
  "dev": "nodemon server.js"
}</code></pre><ul><li><code>npm start</code> 는 서버를 일반 모드로 실행합니다.</li><li><code>npm run dev</code> 는 <strong>nodemon</strong>을 사용해 코드 변경 시 자동으로 서버를 재시작합니다.</li></ul><h3 id="2-">2. 프로젝트 폴더 구조</h3><pre><code>jwt-auth-demo/
│
├── config/
│   └── db.js
│
├── controllers/
│   └── authController.js
│
├── middlewares/
│   └── authMiddleware.js
│
├── models/
│   └── User.js
│
├── routes/
│   └── auth.js
│
├── services/
│   ├── hashService.js
│   └── jwtService.js
│
├── .env
├── server.js
├── package.json</code></pre><p>각 디렉터리의 역할은 다음과 같습니다.</p><ul><li><code>config/</code>: 데이터베이스 연결 및 환경 설정 관련 코드</li><li><code>controllers/</code>: 각 엔드포인트(Endpoint)의 핵심 비즈니스 로직</li><li><code>middlewares/</code>: 컨트롤러 실행 전에 동작하는 미들웨어(예: 인증 체크)</li><li><code>models/</code>: Mongoose 스키마</li><li><code>routes/</code>: API 엔드포인트의 정의</li><li><code>services/</code>: 비밀번호 해시, JWT 생성/검증 같은 재사용 가능한 로직</li><li><code>.env</code>: 시크릿이나 환경 변수</li><li><code>server.js</code>: 애플리케이션의 엔트리 포인트</li></ul><h3 id="3-">3. 단계별 구현하기</h3><p><strong>Express 서버 초기화</strong></p><p>먼저 가장 기본이 되는 Express 서버를 설정합니다. 이 서버가 유저 회원가입, 로그인과 같은 요청을 받고 응답을 돌려주는 역할을 합니다.</p><p><strong><strong>파일: server.js</strong></strong></p><pre><code class="language-javascript">// server.js

// Import the express library to build our server
const express = require("express");

// Create an instance of express
const app = express();

// Middleware to parse JSON request bodies (important for APIs)
app.use(express.json());

// Default route to test server
app.get("/", (req, res) =&gt; {
  res.send("Hello World! Your server is working 🚀");
});

// Start the server on port 5000
const PORT = process.env.PORT || 5000;
app.listen(PORT, () =&gt; {
  console.log(`Server running on http://localhost:${PORT}`);
});</code></pre><ul><li>Express를 불러와 app인스턴스를 생성합니다.</li><li>JSON 형태의 요청을 역직렬화(parsing)하기 위해 미들웨어를 사용합니다.</li><li>서버가 잘 동작하는지 테스트하기 위해 간단한 <code>/</code> &nbsp;경로의 라우트를 만듭니다.</li><li>마지막으로 5000번 포트에서 서버를 시작합니다.</li></ul><p>이제 테스트를 해봅시다:</p><ul><li><code>node server.js</code> 혹은 <code>npm run dev</code> 를 실행해 봅니다.</li><li>브라우저를 열어 다음과 같은 주소로 가보세요. <code>http://localhost:5000</code>.</li><li>다음과 같은 메세지가 떠야 합니다: <code>Hello World! Your server is working 🚀</code></li></ul><p><strong>MongoDB를 Mongoose로 연결하기</strong></p><p>이제 유저 정보를 저장할 데이터베이스가 필요합니다. 여기서는 MongoDB를 사용하고, Node.js에서 쉽게 접근하기 위해 ODM 라이브러리인 Mongoose를 사용합니다.</p><p><strong><strong>파일: config/db.js</strong></strong></p><pre><code class="language-javascript">// config/db.js

// Import mongoose
const mongoose = require("mongoose");

// Connect to MongoDB using environment variable
const connectDB = async () =&gt; {
  try {
    await mongoose.connect(process.env.MONGO_URI, {
      useNewUrlParser: true,
      useUnifiedTopology: true,
    });
    console.log("✅ MongoDB Connected");
  } catch (err) {
    console.error("❌ MongoDB Connection Error:", err.message);
    process.exit(1); // Stop server if DB fails
  }
};

module.exports = connectDB;</code></pre><p>이제 서버가 MongoDB와 연결될 준비가 되었습니다. 데이터를 삽입/수정/조회하는 모든 작업은 이 데이터베이스를 통해 이루어집니다.</p><p><strong><strong>File: .env</strong></strong></p><pre><code class="language-bash">PORT=5000
MONGO_URI=mongodb://127.0.0.1:27017/jwt-auth-demo
JWT_SECRET=your_super_secret_key
</code></pre><p>.env파일에는 데이터베이스의 URI, JWT 시크릿 키, 서버 포트와 같은 민감한 정보가 저장됩니다. 환경 변수를 사용하면 이러한 비밀 정보를 코드에서 분리해 관리할 수 있고, 소스 파일을 수정하지 않고도 설정을 쉽게 변경할 수 있습니다. 자격 증명(credential)을 보호하기 위해 .env파일은 절대 공개 저장소에 기록(commit)해서는 안 됩니다.</p><p><strong>User 모델 만들기</strong></p><p>이제 데이터베이스 안에서 User가 어떤 구조를 가질지 정의해야 합니다. 각 유저는 <strong>name, email, password</strong>를 갖게 됩니다.</p><p><strong><strong>파일: models/User.js</strong></strong></p><pre><code class="language-javascript">// models/User.js
const mongoose = require("mongoose");

// Define a schema (blueprint of user data)
const userSchema = new mongoose.Schema({
  name: { type: String, required: true },
  email: { type: String, required: true, unique: true },
  password: { type: String, required: true },
});

// Create and export the model
module.exports = mongoose.model("User", userSchema);</code></pre><p>이제 각 유저는 <strong>name, email, hashed password</strong> 필드를 갖는 구조로 저장됩니다. </p><p><strong>해시(Hash) 및 JWT 서비스</strong></p><p>이번에는 비밀번호 해시와 JWT 관리 로직을 각각 별도의 서비스 파일로 분리합니다. 이렇게 하면 코드가 깔끔해지고 재사용성이 높아집니다.</p><p><strong><strong>파일: services/hashService.js</strong></strong></p><pre><code class="language-javascript">//services/hashService.js

const bcrypt = require("bcryptjs");

// Function to hash a plain password
exports.hashPassword = async (plainPassword) =&gt; {
  // bcrypt.hash generates a hashed version of the password
  // The number 10 is the salt rounds, which affects the hashing complexity
  return await bcrypt.hash(plainPassword, 10);
};

// Function to compare a plain password with a hashed password
exports.comparePassword = async (plainPassword, hashedPassword) =&gt; {
  // bcrypt.compare checks if the plain password matches the hashed one
  return await bcrypt.compare(plainPassword, hashedPassword);
};
</code></pre><ul><li><code>hashPassword(plainPassword)</code>:비밀번호를 받아 bcrypt로 해시한 값을 반환합니다. 비밀번호는 절대 평문으로 저장하면 안 됩니다</li><li><code>comparePassword(plainPassword, hashedPassword)</code>: 유저가 입력한 비밀번호와 데이터베이스에 저장된 해시 값을 비교해 일치 여부를 알려줍니다. 일치하면 <code>true</code>를 반환합니다.</li></ul><p><strong><strong>파일: services/jwtService.js</strong></strong></p><pre><code class="language-javascript">// services/jwtService.js

const jwt = require("jsonwebtoken");

// Function to generate a JWT
exports.generateToken = (payload) =&gt; {
  // jwt.sign creates a signed token using our secret key from environment variables
  // expiresIn defines how long the token is valid (1 hour here)
  return jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: "1h" });
};

// Function to verify a JWT
exports.verifyToken = (token) =&gt; {
  // jwt.verify checks if the token is valid and not expired
  return jwt.verify(token, process.env.JWT_SECRET);
};</code></pre><ul><li><code>generateToken(payload)</code>: JWT를 생성합니다. 보통 <code>payload</code>가 유저를 식별할 수 있는 ID나 이메일을 담고 있습니다.</li><li><code>verifyToken(token)</code>: JWT의 유효성을 검증하고 디코딩된 payload를 반환합니다.</li><li>이처럼 JWT 관련 로직을 한 곳에 모아 두면 관리와 유지보수가 훨씬 편해집니다.</li></ul><p><strong>인증 컨트롤러(Auth Controller)</strong></p><p>이 단계에서는 인증과 관련된 모든 로직을 별도의 컨트롤러에서 처리합니다. 이렇게 하면 라우트 정의를 깔끔하게 유지할 수 있고, 비즈니스 로직을 엔드포인트 정의와 분리할 수 있습니다.</p><p><strong><strong>파일: controllers/authController.js</strong></strong></p><pre><code class="language-javascript">// controllers/authController.js

const User = require("../models/User");
const { hashPassword, comparePassword } = require("../services/hashService");
const { generateToken } = require("../services/jwtService");

// Register new user
exports.register = async (req, res) =&gt; {
  try {
    const { name, email, password } = req.body; // Get user input

    // Step 1: Check if user already exists
    const existingUser = await User.findOne({ email });
    if (existingUser)
      return res.status(400).json({ message: "User already exists!" });

    // Step 2: Hash password using hashService
    const hashedPassword = await hashPassword(password);

    // Step 3: Save user to database
    const user = new User({ name, email, password: hashedPassword });
    await user.save();

    // Step 4: Send success response
    res.status(201).json({ message: "User registered successfully!" });
  } catch (err) {
    // Handle errors gracefully
    res.status(500).json({ error: err.message });
  }
};

// Login user
exports.login = async (req, res) =&gt; {
  try {
    const { email, password } = req.body; // Get user input

    // Step 1: Find user by email
    const user = await User.findOne({ email });
    if (!user)
      return res.status(400).json({ message: "Invalid email or password" });

    // Step 2: Compare provided password with hashed password
    const isMatch = await comparePassword(password, user.password);
    if (!isMatch)
      return res.status(400).json({ message: "Invalid email or password" });

    // Step 3: Generate JWT using jwtService
    const token = generateToken({ id: user._id, email: user.email });

    // Step 4: Send success response with token
    res.json({ message: "Login successful!", token });
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
};

// Protected profile route
exports.profile = (req, res) =&gt; {
  // req.user is set by auth middleware after token verification
  res.json({
    message: "Welcome to your profile!",
    user: req.user,
  });
};</code></pre><ul><li>파일: <code>controllers/authController.js</code> 은 인증 관련 로직을 담고 있습니다.</li><li><code>exports.register</code>는 회원가입 로직입니다. 이미 같은 이메일의 유저가 있는지 확인하고, <code>hashService</code> 를 이용하여 비밀번호를 해시한 뒤, 새 유저를 MongoDB에 저장하고, 성공 메세지를 반환합니다.</li><li><code>exports.login</code>은 로그인 로직입니다. 이메일로 유저를 찾고, <code>hashService.comparePassword</code> 를 이용하여 비밀번호를 비교 검증하고, 유효하다면 JWT를 생성해, 토큰을 반환합니다.</li><li><code>exports.profile</code>은 보호된(protected) 프로필 라우트입니다. <code>req.user</code>에 저장된 유저 정보를 응답으로 돌려줍니다.</li><li>컨트롤러를 사용하면 라우트 정의와 실제 비즈니스 로직을 깔끔하게 분리할 수 있습니다.</li></ul><p><strong>인증 미들웨어(Auth Middleware)</strong></p><p>이 단계에서는 JWT를 검증함으로써 라우트를 보호하는 미들웨어를 생성합니다. 인증된 유저만 보호된 엔드포인트에 접근할 수 있습니다.</p><p><strong><strong>파일: middlewares/authMiddleware.js</strong></strong></p><pre><code class="language-javascript">// middlewares/authMiddleware.js

const { verifyToken } = require("../services/jwtService");

// Middleware to protect routes
module.exports = (req, res, next) =&gt; {
  // Step 1: Get Authorization header
  const authHeader = req.headers["authorization"];
  if (!authHeader)
    return res.status(401).json({ message: "No token provided" });

  // Step 2: Extract token from format 'Bearer &lt;token&gt;'
  const token = authHeader.split(" ")[1];
  if (!token) return res.status(401).json({ message: "Malformed token" });

  try {
    // Step 3: Verify token using jwtService
    const decoded = verifyToken(token);

    // Step 4: Attach decoded user info to request object
    req.user = decoded;

    // Proceed to next middleware or route handler
    next();
  } catch (err) {
    // If token is invalid or expired
    res.status(401).json({ message: "Invalid or expired token" });
  }
};</code></pre><ul><li>파일: <code>middlewares/authMiddleware.js</code> 은 라우트를 보호하기 위한 미들웨어입니다.</li><li>1단계: <code>Authorization</code> 헤더가 존재하는지 확인합니다.</li><li>2단계: <code>Bearer &lt;token&gt;</code> 형식에서 실제 토큰을 추출합니다.</li><li>3단계: <code>jwtService.verifyToken</code>을 사용해 토큰을 검증합니다.</li><li>4단계: 디코딩된 유저 정보를 <code>req.user</code>에 추가해 이후 라우트 핸들러에서 사용할 수 있도록 합니다.</li><li>토큰이 없거나, 형식이 올바르지 않거나, 유효하지 않거나, 만료된 경우 미들웨어는 <strong>401 Unauthorized</strong> 응답을 반환합니다. 이를 통해 인증된 유저만 보호된 라우트에 접근할 수 있도록 보장합니다.</li></ul><p><strong>인증 라우트 (Auth Routes)</strong></p><p>이 단계에서는 인증과 관련된 라우트를 정의하고, 이를 컨트롤러와 미들웨어에 연결합니다.</p><p><strong><strong>파일: routes/auth.js</strong></strong></p><pre><code class="language-javascript">// routes/auth.js

const express = require("express");
const router = express.Router();
const authController = require("../controllers/authController");
const authMiddleware = require("../middlewares/authMiddleware");

// Step 1: Register route
// Users send their name, email, and password to this endpoint
router.post("/register", authController.register);

// Step 2: Login route
// Users send email and password to receive JWT
router.post("/login", authController.login);

// Step 3: Protected profile route
// Only accessible to authenticated users with a valid JWT
router.get("/profile", authMiddleware, authController.profile);

module.exports = router;</code></pre><ul><li>파일: <code>routes/auth.js</code> - 인증 엔드포인트를 정의하는 핵심 파일입니다.</li><li><code>router.post("/register", authController.register)</code>: 유저 회원가입을 처리합니다.</li><li><code>router.post("/login", authController.login)</code>: 유저 로그인을 처리하고 JWT 토큰을 생성합니다.</li><li><code>router.get("/profile", authMiddleware, authController.profile)</code>: JWT가 필요한 보호된 라우트입니다. <code>authMiddleware</code>를 통해 인증된 유저만 접근할 수 있도록 보장합니다.</li><li>이처럼 라우트, 컨트롤러, 미들웨어를 함께 사용하면 애플리케이션 구조를 체계적으로 유지할 수 있고, 코드도 보다 전문적으로 관리할 수 있습니다.</li></ul><p><strong>메인 서버 파일</strong></p><p>이 파일은 애플리케이션의 메인 엔트리 포인트입니다. 서버를 설정하고, 데이터베이스에 연결하며, 모든 라우트를 등록하는 역할을 합니다.</p><p><strong><strong>파일: server.js</strong></strong></p><pre><code class="language-javascript">// server.js

require("dotenv").config(); // Step 1: Load environment variables from .env
const express = require("express");
const connectDB = require("./config/db");

const app = express();

// Step 2: Connect to MongoDB
connectDB();

// Step 3: Middleware to parse JSON request bodies
app.use(express.json());

// Step 4: Mount auth routes
// All auth-related routes will start with /api/auth
app.use("/api/auth", require("./routes/auth"));

// Step 5: Default route to test server
app.get("/", (req, res) =&gt; {
  res.send("Hello World! Your server is working 🚀");
});

// Step 6: Start server on PORT from .env or default 5000
const PORT = process.env.PORT || 5000;
app.listen(PORT, () =&gt; {
  console.log(`Server running on http://localhost:${PORT}`);
});</code></pre><ul><li><strong>환경 변수 불러오기</strong>: <code>dotenv</code>를 사용해 비밀 정보와 설정 값을 코드와 분리해 관리합니다.</li><li><strong>MongoDB 연결</strong>: <code>config/db.js</code>에 정의된 <code>connectDB()</code>를 호출해 데이터베이스에 연결합니다.</li><li><strong>미들웨어</strong>: <code>express.json()</code>을 사용해 Express가 JSON 요청을 파싱할 수 있도록 합니다.</li><li><strong>라우트 등록</strong>: <code>app.use("/api/auth", ...)</code>를 통해 모든 인증 관련 라우트를 등록합니다.</li><li><strong>기본 라우트</strong>: 서버가 정상적으로 실행 중인지 확인하기 위한 간단한 GET 엔드포인트입니다.</li><li><strong>서버 시작</strong>: <code>app.listen</code>을 호출해 설정된 포트에서 서버를 실행합니다.</li></ul><h3 id="api-">API 테스트 하기</h3><p>이번에는 Postman이나 기타 HTTP 클라이언트와 같은 도구를 사용해 JWT 인증 API를 테스트하는 방법을 살펴봅니다.</p><p>테스트를 시작하기 전에 서버가 실행 중인지 반드시 확인해야 합니다. 서버가 실행되고 있지 않다면, 터미널을 열고 다음 명령을 실행합니다.</p><pre><code class="language-bash">npm run dev</code></pre><p>혹은</p><pre><code class="language-bash">node server.js</code></pre><p>이 명령을 실행하면 <code>.env</code> 파일에 정의된 포트(기본값은 <code>5000</code>)에서 서버가 시작됩니다.</p><p>MongoDB가 실행 중인지도 확인해 보세요. 로컬 MongoDB를 사용하고 있다면 다음 명령으로 실행할 수 있습니다:</p><pre><code class="language-bash">mongod
</code></pre><p>로컬 MongoDB를 사용하지 않는 경우에는, 연결된 MongoDB 서비스가 정상적으로 활성화되어 있는지 확인합니다.</p><p>항상 터미널에 오류 메시지가 있는지 확인해 보세요. 서버나 데이터베이스가 정상적으로 시작되지 않으면 API 요청은 동작하지 않습니다.</p><p><strong>회원가입 하기</strong></p><p>요청:</p><pre><code class="language-curl">POST http://localhost:5000/api/auth/register
Content-Type: application/json

{
  "name": "sumit",
  "email": "sumit@example.com",
  "password": "mypassword"
}</code></pre><p>응답:</p><pre><code class="language-json">{
  "message": "User registered successfully!"
}</code></pre><p>이 요청은 유저 정보를 포함해 <code>http://localhost:5000/api/auth/register</code>로 POST 요청을 전송합니다. 요청이 성공하면 확인 메시지를 응답으로 받게 됩니다.</p><p><strong>로그인 하기</strong></p><p>요청:</p><pre><code class="language-curl">POST http://localhost:5000/api/auth/login
Content-Type: application/json

{
  "email": "sumit@example.com",
  "password": "mypassword"
}</code></pre><p>응답:</p><pre><code class="language-json">{
  "message": "Login successful!",
  "token": "&lt;JWT_TOKEN&gt;"
}</code></pre><p>이 요청은 이메일과 비밀번호를 포함해 <code>http://localhost:5000/api/auth/login</code>으로 POST 요청을 전송합니다. 자격 증명이 올바른 경우, 보호된 라우트에 접근하기 위한 JWT를 응답으로 받게 됩니다.</p><p><strong>보호된 라우트에 접근하기</strong></p><p>요청:</p><pre><code class="language-curl">GET http://localhost:5000/api/auth/profile
Authorization: Bearer &lt;JWT_TOKEN&gt;</code></pre><p>응답:</p><pre><code class="language-json">{
  "message": "Welcome to your profile!",
  "user": {
    "id": "...",
    "email": "sumit@example.com",
    "iat": ...,
    "exp": ...
  }
}</code></pre><p>이 요청은 <code>Bearer</code> 방식으로 <code>Authorization</code> 헤더에 JWT를 포함하여 전송합니다.</p><ul><li>유효한 토큰만 이 보호된 라우트에 접근할 수 있습니다.</li><li><code>iat</code>와 <code>exp</code>는 각각 토큰이 발급된 시점(issued at)과 만료 시점(expiry time)을 의미합니다.</li></ul><p><strong>참고</strong>: 보호된 라우트에 접근할 때는 항상 <code>Authorization: Bearer &lt;token&gt;</code> 헤더를 포함해야 합니다.</p><h2 id="-summary-">요약(Summary)</h2><p>이 글에서는 JSON Web Token(JWT)이 무엇인지와 웹 인증에서 어떤 역할을 하는지를 전반적으로 살펴보았습니다. HTTP가 상태를 유지하지 않는(stateless) 프로토콜이라는 점과 그로 인해 토큰이 필요한 이유를 설명했으며, 전통적인 세션 토큰 방식과 JWT를 비교했습니다.</p><p>또한 JWT의 구조와 보안 메커니즘을 다루고, Node.js, Express, MongoDB를 사용한 실제 구현 방법을 단계별로 살펴보았습니다. 이와 함께 보안 고려사항, 토큰 관리 방식, 그리고 JWT 인증 API를 테스트하는 방법도 함께 설명했습니다.</p><h3 id="--4">핵심 포인트 정리:</h3><ol><li><strong>JWT란 무엇인가?</strong></li></ol><ul><li>JWT는 RFC 7519에 정의된, 두 주체 간의 정보를 안전하게 표현하기 위한 JSON 기반 개방형 표준입니다.</li><li>현대적인 웹 애플리케이션과 마이크로서비스 아키텍처에서 인증 용도로 널리 사용됩니다.</li><li>세션 토큰의 대안으로 활용됩니다.</li></ul><p><strong>2. HTTP의 Stateless 특성</strong></p><ul><li>HTTP는 요청 간 상태를 유지하지 않기 때문에, 각 요청은 필요한 모든 정보를 포함해야 합니다.</li><li>동적인 웹 애플리케이션에서는 유저 세션을 유지하기 위해 세션 토큰이나 JWT와 같은 토큰을 사용합니다.</li></ul><p><strong>3. 세션 토큰</strong></p><ul><li>서버가 세션 ID를 생성해 저장하고, 보통 쿠키를 통해 클라이언트에 전달하는 방식입니다.</li><li>단일 서버 환경에서는 효과적으로 동작하지만, 여러 서버 환경에서는 Redis와 같은 공유된 저장소가 필요합니다. </li><li>해당 캐시가 다운되면 인증 시스템이 영향을 받을 수 있습니다.</li></ul><p><strong>4. JWT: 현대적인 해결책</strong></p><ul><li>서버는 서명된 JSON 토큰을 클라이언트에 전달하고, 클라이언트는 이를 매 요청마다 전송합니다.</li><li>서버 측에 별도의 세션 저장소가 필요 없으며, 모든 유저 정보는 토큰 안에 포함됩니다.</li><li>시그니처를 통해 토큰의 유효성과 무결성이 보장됩니다.</li></ul><p><strong>5. JWT 구조</strong></p><ul><li>JWT는 헤더, 페이로드, 시그니처 세 부분으로 구성됩니다.</li><li>헤더와 페이로드는 Base64로 인코딩된 JSON 객체이며, 시그니처는 시크릿 키를 사용한 해시 값입니다.</li><li>Base64 인코딩은 보안을 위한 것이 아니라, 데이터를 다루기 쉽게 하기 위한 표현 방식입니다.</li></ul><p><strong>6. JWT 디코딩</strong></p><ul><li><a href="https://www.jwt.io/">jwt.io</a>와 같은 도구를 사용하면 JWT의 헤더, 페이로드, 시그니처를 쉽게 확인할 수 있습니다.</li><li>페이로드는 누구나 읽을 수 있으므로, 민감한 정보는 JWT에 저장해서는 안 됩니다.</li></ul><p><strong>7. JWT 보안</strong></p><ul><li>시그니처는 시크릿 키와 암호학적 알고리즘을 사용해 생성됩니다.</li><li>서버는 시크릿 키를 이용해 토큰의 무결성을 검증합니다.</li><li>JWT는 유저를 식별하기 위한 수단이며, 비밀번호와 같은 역할을 하지는 않습니다.</li></ul><p><strong>8. 보안 고려사항 및 토큰 관리</strong></p><ul><li>JWT가 탈취되면, 만료 시점까지 공격자가 해당 유저로 위장할 수 있습니다.</li><li>JWT는 직접적인 무효화가 어렵기 때문에, 차단 목록을 통해 관리합니다.</li><li>세션 토큰은 서버에서 직접 무효화할 수 있다는 차이가 있습니다.</li></ul><p><strong>9. 다양한 언어에서의 JWT</strong></p><ul><li>JWT는 언어에 독립적인 표준으로, Node.js, PHP 등 다양한 언어에서 구현할 수 있습니다.</li><li>인증뿐 아니라 다양한 형태의 신원을 표현하는 데도 활용할 수 있습니다.</li></ul><p><strong>10. 실전 구현: Express + MongoDB로 JWT 인증 만들기</strong></p><ul><li>프로젝트 세팅 및 패키지 설치</li><li>폴더 구조 설계</li><li>Express 서버 초기화</li><li>MongoDB 연결</li><li>유저 모델 생성</li><li>비밀번호 해시 및 JWT 서비스 구현</li><li>인증 컨트롤러와 미들웨어 작성</li><li>인증 라우트 구성</li><li>메인 서버 파일 구성</li><li>API 테스트 방법 안내</li></ul><p><strong>11. API 테스트</strong></p><ul><li>Postman과 같은 도구를 사용해 회원가입, 로그인, 보호된 라우트 접근 방법을 설명했습니다.</li><li>요청 및 응답 예시를 통해 전체 흐름을 확인할 수 있습니다.</li></ul><p><strong>12. 요약</strong></p><ul><li>JWT는 안전하고, stateless하며, 널리 사용되는 인증 방식입니다.</li><li>다만 보안은 토큰을 얼마나 안전하게 보관하고 관리하느냐에 크게 좌우된다는 점을 항상 염두에 두어야 합니다.</li></ul><h2 id="--5">마지막 한마디</h2><p>이 튜토리얼에서 사용된 모든 소스 코드는 <a href="https://github.com/logicbaselabs/jwt-auth-demo">해당 GitHub 저장소</a>에서 확인할 수 있습니다. 내용이 조금이라도 도움이 되었다면, 별을 눌러 응원의 뜻을 전해 주셔도 좋습니다.</p><p>또한 이 글이 유익하다고 느껴지셨다면, 도움이 될 만한 다른 분들과 자유롭게 공유해 주세요. 의견이나 피드백이 있다면 X에서 <a href="https://x.com/sumit_analyzen">@sumit_analyzen</a>, Facebook의 <a href="https://www.facebook.com/sumit.analyzen">@sumit.analyzen</a>으로 저를 언급해 주시거나, <a href="https://www.youtube.com/@logicBaseLabs">코딩 튜토리얼을 시청</a>하거나 <a href="https://www.linkedin.com/in/sumitanalyzen/">LinkedIn</a>에서 연결해 주셔도 감사하겠습니다.</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 자바스크립트 setTimeout()로 타이머 설정하기 ]]>
                </title>
                <description>
                    <![CDATA[  이번 튜토리얼에서는 자바스크립트의 내장 함수인 setTimeout()이 어떻게 작동되는지 코드 예시를 통해 이해하는 시간을 가져보겠습니다. 자바스크립트에서 setTimeout() 사용하기 setTimeout()은 특정 시간이 지난 다음에 코드를 실행하는 함수입니다. 자바스크립트 코드에 일종의 타이머를 설정하는 것입니다. setTimeout() 함수 예제 setTimeout(function(){     console.log("Hello World"); }, 2000); console.log("setTimeout() example..."); 위 코드는 콘솔에 "setTimeout() ]]>
                </description>
                <link>https://www.freecodecamp.org/korean/news/how-to-set-a-timer-in-javascript/</link>
                <guid isPermaLink="false">656fb72f1f7e5f04036d3ff7</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Heegu Yang ]]>
                </dc:creator>
                <pubDate>Wed, 06 Dec 2023 13:53:35 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/korean/news/content/images/2023/12/mainImage.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>기사 원문:</strong> <a href="https://www.freecodecamp.org/news/javascript-settimeout-how-to-set-a-timer-in-javascript-or-sleep-for-n-seconds/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">JavaScript setTimeout() – How to Set a Timer in JavaScript or Sleep for N Seconds</a>
      </p><p></p><p>이번 튜토리얼에서는 자바스크립트의 내장 함수인 <code>setTimeout()</code>이 어떻게 작동되는지 코드 예시를 통해 이해하는 시간을 가져보겠습니다.</p><h2 id="-settimeout-">자바스크립트에서 setTimeout() 사용하기</h2><p><code>setTimeout()</code>은 특정 시간이 지난 다음에 코드를 실행하는 함수입니다. 자바스크립트 코드에 일종의 타이머를 설정하는 것입니다.</p><p>setTimeout() 함수 예제</p><pre><code class="language-js">setTimeout(function(){
    console.log("Hello World");
}, 2000);

console.log("setTimeout() example...");
</code></pre><p>위 코드는 콘솔에 "setTimeout() example..."를 먼저 출력하고, 2초가 지난 다음 "Hello World"을 출력합니다.</p><p><code>setTimeout()</code> 함수 문법은 이렇습니다:</p><p>setTimeout() 함수 문법</p><pre><code class="language-js">setTimeout(function, milliseconds, parameter1, parameter2, ...);
</code></pre><p><code>setTimeout()</code> 함수의 첫 번째 매개변수에는 여러분이 실행시킬 자바스크립트 함수를 넣습니다. <code>function</code>에 함수 자체를 넣을 수도 있고, 아래와 같이 기명 함수로 넣기도 합니다.</p><p>setTimeout() 함수에 기명함수 사용한 예시</p><pre><code class="language-js">function greeting(){
  console.log("Hello World");
}

setTimeout(greeting);
</code></pre><p>그리고 다음 코드가 실행되기 전까지 기다리도록 <code>milliseconds</code>를 매개 변수로 전달할 수도 있습니다.</p><p>1초는 1,000 밀리초입니다. 만약 3초를 기다리게 하고 싶다면, <code>3000</code>을 두 번째 매개 변수로 전달해야 합니다.</p><p>3초간 기다리는 setTimeout() 함수</p><pre><code class="language-js">function greeting(){
  console.log("Hello World");
}

setTimeout(greeting, 3000);
</code></pre><p>만약 두 번째 매개 변수를 생략한다면, <code>setTimeout()</code>은 <code>function</code> 함수를 즉시 실행합니다.</p><p>마지막으로, <code>setTimeout()</code> 함수에 매개 변수들을 추가로 넣을 수도 있습니다.</p><p>setTimeout()에 추가로 매개변수 전달하기</p><pre><code class="language-js">function greeting(name, role){
  console.log(`Hello, my name is ${name}`);
  console.log(`I'm a ${role}`);
}

setTimeout(greeting, 3000, "Nathan", "Software developer");
</code></pre><p>여러분은 아래와 같이 "함수에 바로 매개 변수를 바로 전달하면 안될까?"라고 물을 수도 있습니다.</p><pre><code class="language-js">setTimeout(greeting("Nathan", "Software developer"), 3000);
</code></pre><p>그럼 자바스크립트는 기다림 없이 <code>function</code>을 실행하게 됩니다. 왜냐하면 첫 번째 매개 변수로 함수 참조(function reference)가 아닌 함수 호출(function call)을 전달했기 때문입니다.</p><p>따라서 함수에 어떤 매개변수들을 전달하고 싶다면, <code>setTimeout()</code> 함수를 통해 전달해야 합니다.</p><p>그러나 <code>setTimeout()</code>에 추가적인 매개 변수를 전달하는 경우는 매우 드물기 때문에 이에 대해 크게 걱정하지 않아도 됩니다.</p><h2 id="settimeout-">setTimeout 함수 취소하는 방법</h2><p><code>clearTimeout()</code>를 사용하면 <code>setTimeout()</code>의 함수 실행을 취소할 수 있습니다. 이를 위해 <code>clearTimeout()</code> 함수는 <code>setTimeout()</code>으로부터 반환된 <code>id</code>를 필요로 합니다.</p><p>clearTimeout() 문법</p><pre><code class="language-js">clearTimeout(id);
</code></pre><p>여기 <code>clearTimeout()</code> 함수를 사용하는 예시가 있습니다.</p><p>clearTimeout() 함수 활용 예시</p><pre><code class="language-js">const timeoutId = setTimeout(function(){
    console.log("Hello World");
}, 2000);

clearTimeout(timeoutId);
console.log(`Timeout ID ${timeoutId} has been cleared`);
</code></pre><p>만약 여러 개의 <code>setTimeout()</code> 함수들을 사용할 경우, 각 함수 호출에서 반환되는 ID 값들을 저장한 다음, 함수의 수 만큼 <code>clearTimeout()</code> 함수를 호출해야 합니다.</p><h2 id="-">결론</h2><p><code>setTimeout()</code> 함수는 특정 함수의 실행 시기를 설정할 수 있는 자바스크립트 내장 함수입니다. 대기 시간을 전달하기 위해 밀리초 기준의 시간을 전달합니다. 예를 들어, 1초를 기다리게 하고 싶다면 1,000 밀리초를 입력하는 식이죠.</p><p><code>setTimeout()</code>의 함수 실행을 취소하고 싶다면, <code>clearTimeout()</code> 함수를 사용하면 됩니다. 이때 <code>setTimeout()</code> 함수를 호출 시 반환되는 ID 값을 전달해야 합니다.</p><h2 id="--1">이 글을 읽어주셔서 감사합니다</h2><p>자바스크립트에 대해 더 많이 알고싶으시다면, 저의 웹사이트 sebhastian.com를 방문하세요. 이곳에서 상세한 설명과 코드 예시가 포함된 <a href="https://sebhastian.com/javascript-tutorials/">자바스크립트 프로그래밍에 대한 100개 이상의 튜토리얼</a>을 얻을 수 있습니다. 튜토리얼의 주제는 문자열 조작, 날짜 조작, 배열과 객체 함수, 자바스크립트 알고리즘 등 다양합니다.</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 자바스크립트 프로미스 튜토리얼 - 자바스크립트의 프로미스를 이행하거나 거부하는 방법 ]]>
                </title>
                <description>
                    <![CDATA[ 프로미스는 자바스크립트에서 비동기 처리를 위한 중요한 요소이지만 프로미스를 이해하고 적용하는 것은 그렇게 쉽지 않다고 생각하셨을 수도 있습니다. 하지만 절 따라오세요, 여러분은 혼자가 아닙니다! 프로미스는 프로미스를 몇 년 동안 다뤄본 많은 웹 개발자들에게도 어려운 주제입니다. 이 글에서는 제가 지난 몇 년간 자바스크립트의 프로미스에 대해 배운 내용을 공유하면서 이러한 인식을 한번 깨보겠습니다. ]]>
                </description>
                <link>https://www.freecodecamp.org/korean/news/javascript-promise-tutorial-how-to-resolve-or-reject-promises-in-js/</link>
                <guid isPermaLink="false">65336269722f6d03ea1726b0</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Nayoung Gu ]]>
                </dc:creator>
                <pubDate>Sat, 21 Oct 2023 10:29:46 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/korean/news/content/images/2023/10/cover-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>기사 원문:</strong> <a href="https://www.freecodecamp.org/news/javascript-promise-tutorial-how-to-resolve-or-reject-promises-in-js/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">JavaScript Promise Tutorial – How to Resolve or Reject Promises in JS</a>
      </p><!--kg-card-begin: markdown--><h4 id=""><code>프로미스</code>는 자바스크립트에서 비동기 처리를 위한 중요한 요소이지만 프로미스를 이해하고 적용하는 것은 그렇게 쉽지 않다고 생각하셨을 수도 있습니다. 하지만 절 따라오세요, 여러분은 혼자가 아닙니다!</h4>
<p>프로미스는 프로미스를 몇 년 동안 다뤄본 많은 웹 개발자들에게도 어려운 주제입니다.</p>
<p>이 글에서는 제가 지난 몇 년간 자바스크립트의 프로미스에 대해 배운 내용을 공유하면서 이러한 인식을 한번 깨보겠습니다. 도움이 되었으면 좋겠습니다.</p>
<h2 id="">자바스크립트의 프로미스란</h2>
<p><code>프로미스</code>는 자바스크립트의 특수 객체입니다. 프로미스는 <code>비동기</code> 작업이 성공적으로 수행되면 값을 생성하고, 시간 초과나 네트워크 오류 등으로 인해 실패하면 에러를 생성합니다.</p>
<p>성공적인 이행은 <code>resolve</code> 함수 호출, 그리고 에러는 <code>reject</code> 함수 호출을 통해 확인할 수 있습니다.</p>
<p>프로미스는 다음과 같이 생성자를 통해 만들 수 있습니다.</p>
<pre><code class="language-js">let promise = new Promise(function(resolve, reject) {    
    // 비동기 호출 후 resolve 혹은 reject 하는 곳
});
</code></pre>
<p>대부분의 경우 프로미스는 비동기 처리에 사용됩니다. 하지만 엄밀히 말하면 동기와 비동기 작업을 모두 이행/거절할 수 있습니다.</p>
<h2 id="">잠시만요, 비동기 작업을 위한 <code>콜백</code> 함수가 있지 않나요?</h2>
<p>네! 맞아요. 자바스크립트에는 <code>콜백</code> 함수가 있습니다. 하지만 콜백은 자바스크립트에서 특별한 개념은 아닙니다. <code>비동기</code> 호출이 (성공 또는 에러와 함께) 완료되면 결과를 반환하는 일반 함수입니다.</p>
<p><code>비동기</code>란 지금 당장이 아니라 미래에 발생한다는 의미입니다. 콜백은 주로 네트워크 통신, 업로드/다운로드, 데이터베이스 조회 등과 같은 상황에서 사용됩니다.</p>
<p><code>콜백</code>은 유용하긴 하지만 엄청난 단점도 가지고 있습니다. 때로 다른 콜백에 포함된 콜백 내부에 또 다른 콜백이 있을 수도 있습니다. 진심으로요! 이 "콜백 헬"을 예시와 함께 이해해봅시다.</p>
<h3 id="pizzahub">콜백 헬을 피하는 방법 - PizzaHub 예시</h3>
<p>PizzaHub에서 🍕 야채 마르게리타 피자를 주문한다고 해봅시다. 주문을 넣으면 PizzaHub은 자동적으로 우리의 위치를 파악하고 근처의 피자 가게를 찾은 뒤 우리가 요청한 피자가 주문 가능한지 찾아볼 것입니다.</p>
<p>주문이 가능하다면 피자와 함께 무료로 받을 수 있는 음료를 파악하고 마침내 주문을 넣게 됩니다.</p>
<p>주문이 성공적으로 완료되면 확인 문자를 받게 됩니다.</p>
<p>이 과정을 콜백 함수를 사용해 코드로 표현하면 어떨까요? 다음과 같이 생각해 봤습니다.</p>
<pre><code class="language-js">function orderPizza(type, name) {

    // pizzahub에 문의하기
    query(`/api/pizzahub/`, function(result, error){
       if (!error) {
           let shopId = result.shopId;

           // 가게에 피자 문의하기
           query(`/api/pizzahub/pizza/${shopid}`, function(result, error){
               if (!error) {
                   let pizzas = result.pizzas;

                   // 피자 가능 여부 확인
                   let myPizza = pizzas.find((pizza) =&gt; {
                       return (pizza.type===type &amp;&amp; pizza.name===name);
                   });

                   // 무료 음료 확인
                   query(`/api/pizzahub/beverages/${myPizza.id}`, function(result, error){
                       if (!error) {
                           let beverage = result.id;

                           // 주문 준비
                           query(`/api/order`, {'type': type, 'name': name, 'beverage': beverage}, function(result, error){
                              if (!error) {
                                  console.log(`Your order of ${type} ${name} with ${beverage} has been placed`);
                              } else {
                                  console.log(`Bad luck, No Pizza for you today!`);
                              }
                           });

                       }
                   })
               }
           });
       } 
    });
}

// orderPizza 메서드 호출
orderPizza('veg', 'margherita');
</code></pre>
<p>위의 <code>orderPizza</code> 함수를 잘 살펴봅시다.</p>
<p>이 함수는 근처 피자 가게의 id를 찾기 위해 API를 호출합니다. 그리고 그 가게에서 주문 가능한 피자의 리스트를 받습니다. 그 중에 주문한 피자가 있는지 확인하고 그 피자와 함께 오는 음료를 확인하기 위해 또 다른 API 호출을 하게 됩니다. 최종적으로 주문 API를 통해 주문을 넣게 됩니다.</p>
<p>여기서 모든 API에 콜백을 사용하고 있습니다. 이 때문에 계속해서 콜백 안에 콜백을 넣는 방식으로 코드를 작성하게 됩니다.</p>
<p>이렇게 해서 우리는 <code>콜백 지옥</code>에 빠지게 됩니다. 누가 이런 코드를 원할까요? 게다가 가독성이 좋지 않고 에러가 발생하기도 쉬운 코드 피라미드가 생겨나게 됩니다.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/11/callback-hell.png" alt="콜백 지옥의 예시 모양" width="600" height="400" loading="lazy"></p>
<p align="center">콜백 지옥 및 멸망의 피라미드 예시</p>
<p><code>콜백 지옥</code>을 빠져나오거나 들어가지 않을 수 있는 몇 가지 방법들이 있습니다. 가장 흔한 방법은 <code>프로미스</code>나 <code>비동기</code> 함수를 사용하는 것입니다. 하지만 <code>async</code> 함수를 잘 이해하려면 <code>프로미스</code>를 먼저 잘 이해해야 합니다.</p>
<p>그럼 프로미스에 대해서 시작해 봅시다.</p>
<h2 id="">프로미스의 상태 이해하기</h2>
<p>복습해보자면, 프로미스는 다음과 같은 생성자 문법을 통해 생성될 수 있습니다.</p>
<pre><code class="language-js">let promise = new Promise(function(resolve, reject) {
  // 실행할 함수
});
</code></pre>
<p>생성자 함수는 인자로 함수를 받습니다. 이 함수는 <code>실행 함수</code>라고 불립니다.</p>
<pre><code class="language-js">// 실행 함수는 프로미스 생성자 함수에 인자로 전달된다
function(resolve, reject) {
    // 로직이 위치할 곳
}
</code></pre>
<p>실행 함수는 <code>resolve</code>와 <code>reject</code>라는 두 가지 인자를 받습니다. 이 둘은 자바스크립트에 의해 제공되는 콜백 함수입니다. 여러분이 작성할 로직은 <code>new Promise</code>가 생성되면 자동적으로 호출될 실행 함수 안에 위치하게 됩니다.</p>
<p>프로미스를 효과적으로 사용하려면 실행 함수는 <code>resolve</code> 혹은 <code>reject</code> 콜백 함수들 중 하나를 호출해야 합니다. 이 둘에 대해선 잠시 후에 더 자세히 배워보겠습니다.</p>
<p><code>new Promise()</code> 생성자는 <code>프로미스</code> 객체를 반환합니다. 실행 함수는 비동기 처리를 다루기 때문에, 반환된 프로미스 객체는 작업이 시작되거나, 완료될 때 (resolved), 그리고 에러를 반환할 때(rejected)를 알려줄 수 있어야 합니다.</p>
<p><code>프로미스</code> 객체는 다음과 같은 내부 프로퍼티를 갖습니다.</p>
<ol>
<li><code>state(상태)</code> - 이 프로퍼티는 다음과 같은 값을 갖습니다.</li>
</ol>
<ul>
<li><code>pending(대기)</code>: 실행 함수가 작업을 시작했을 때</li>
<li><code>fulfilled(이행)</code>: 프로미스가 이행되었을 때</li>
<li><code>rejected(거부)</code>: 프로미스가 거부되었을 때</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/11/states_1.png" alt="프로미스의 상태" width="600" height="400" loading="lazy"></p>
<p align="center">프로미스의 세 가지 상태</p>
<ol start="2">
<li><code>result(결과)</code> - 이 프로퍼티는 다음과 같은 값을 갖습니다.</li>
</ol>
<ul>
<li><code>undefined</code>: <code>상태</code> 값이 <code>대기</code> 중일 때</li>
<li><code>value</code>: <code>resolve(value)</code>가 호출되었을 때</li>
<li><code>error</code>: <code>reject(error)</code>가 호출되었을 때</li>
</ul>
<p>이 내부 프로퍼티들은 코드로 접근할 수는 없지만 확인은 가능합니다. 즉 디버거 도구를 활용해 <code>state</code>와 <code>result</code> 프로퍼티 값을 확인할 수 있지만 직접적으로 프로그램을 사용해 접근할 수는 없습니다.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/11/promise_state_inspect.png" alt="개발자 도구에서 프로미스 확인하기" width="600" height="400" loading="lazy"></p>
<p align="center">프로미스의 내부 프로머티를 확인할 수 있다</p>
<p>프로미스의 상태는 <code>pending</code>, <code>fulfilled</code> 혹은 <code>rejected</code>가 될 수 있습니다. 이행(resolved) 또는 거부(rejected)된 프로미스는 <code>settled</code> 되었다고 표현합니다.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/11/states_2.png" alt="프로미스가 처리된 상태" width="600" height="400" loading="lazy"></p>
<p align="center">처리(settled)된 프로미스는 이행(fulfilled) 혹은 거부(rejected)된 것이다</p>
<h3 id="">프로미스가 이행되거나 거부되는 방법</h3>
<p>다음은 <code>I am done</code>이라는 값과 함께 즉시 이행되는(<code>fulfilled</code> 상태) 프로미스의 예시입니다.</p>
<pre><code class="language-js">let promise = new Promise(function(resolve, reject) {
    resolve("I am done");
});
</code></pre>
<p>다음은 <code>Something is not right!</code>이라는 에러 메세지와 함께 거절되는 (<code>rejected</code> 상태) 프로미스입니다.</p>
<pre><code class="language-js">let promise = new Promise(function(resolve, reject) {
    reject(new Error('Something is not right!'));
});
</code></pre>
<p>주의할 점</p>
<blockquote>
<p>프로미스 실행 함수는 오직 하나의 <code>resolve</code> 혹은 <code>reject</code> 함수를 호출해야 합니다. 상태가 한번 변경되면 (pending =&gt; fulfilled 혹은 pending =&gt; rejected) 끝이기 때문입니다. 그 이후의 <code>resolve</code> 혹은 <code>reject</code>는 무시됩니다.</p>
</blockquote>
<pre><code class="language-js">let promise = new Promise(function(resolve, reject) {
  resolve("I am surely going to get resolved!");

  reject(new Error('Will this be ignored?')); // 무시됨
  resolve("Ignored?"); // 무시됨
});
</code></pre>
<p>위의 예시에서, 오직 첫 번째 것만 이행되고 나머지 것들은 모두 무시됩니다.</p>
<h2 id="">프로미스를 만들고 처리하는 방법</h2>
<p><code>프로미스</code>는 (대부분 비동기적으로) 작업을 처리하기 위해 실행 함수를 사용합니다. (프로미스의 결과를 사용하는) 소비 함수는 실행 함수가 이행(성공)되거나 거부된(에러) 시점을 알아야 합니다.</p>
<p><code>.then()</code>, <code>.catch()</code>, <code>finally()</code>와 같은 핸들러 메서드는 실행 함수와 소비 함수를 이어줌으로써 프로미스가 <code>이행</code>되거나 <code>거부</code>되었을 때 서로 동기화될 수 있도록 해줍니다.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/11/consumer_executor.png" alt="실행 함수와 소비 함수 예시" width="600" height="400" loading="lazy"></p>
<p align="center">실행 함수와 소비 함수</p>
<h3 id="then"><code>.then()</code> 메서드를 사용하는 방법</h3>
<p><code>.then()</code> 메서드는 결과(resolve) 또는 에러(reject)를 다룰 때 프로미스 객체에 의해 호출됩니다.</p>
<p>이 메서드는 인자로 두 개의 함수를 받습니다. 보통 <code>.then()</code> 메서드는 프로미스의 실행 결과를 알고자 하는 소비 함수로부터 실행되어야 합니다.</p>
<pre><code class="language-js">promise.then(
  (result) =&gt; { 
     console.log(result);
  },
  (error) =&gt; { 
     console.log(error);
  }
);
</code></pre>
<p>성공했을 때의 결과만 알고 싶다면 다음과 같이 하나의 인자만 전달해줄 수도 있습니다.</p>
<pre><code class="language-js">promise.then(
  (result) =&gt; { 
      console.log(result);
  }
);
</code></pre>
<p>실패한 결과만 다루고 싶다면 다음과 같이 첫 번째 인자로 <code>null</code>을 전달해주면 됩니다.</p>
<pre><code class="language-js">promise.then(
  null,
  (error) =&gt; { 
      console.log(error)
  }
);
</code></pre>
<p>하지만 잠시후에 보게될 <code>.catch()</code> 메서드를 사용하면 더 나은 방법으로 에러를 처리할 수 있습니다.</p>
<p><code>.then()</code>과 <code>.catch()</code> 핸들러를 사용해서 결과와 에러를 처리한 몇가지 예시를 살펴봅시다. 몇 가지 실제 비동기 요청을 통해서 더 재밌게 배워보겠습니다. 포켓몬에 대한 정보를 얻고 그 결과를 프로미스를 사용해서 이행/거부하기 위해 <a href="https://pokeapi.co/">PokeAPI</a>를 사용하겠습니다.</p>
<p>먼저, PokeAPI URL을 인자로 받은 뒤 프로미스를 반환하기 위한 제네릭 함수를 만듭니다. API 호출이 성공적으로 이루어지면 이행된 프로미스가 반환됩니다. 거부된 프로미스는 어떠한 종류의 에러로부터 반환될 수 있습니다.</p>
<p>이제부터 프로미스를 만들어 작업하기 위해 여러 예시에서 이 함수를 사용하겠습니다.</p>
<pre><code class="language-js">function getPromise(URL) {
  let promise = new Promise(function (resolve, reject) {
    let req = new XMLHttpRequest();
    req.open("GET", URL);
    req.onload = function () {
      if (req.status == 200) {
        resolve(req.response);
      } else {
        reject("There is an Error!");
      }
    };
    req.send();
  });
  return promise;
}
</code></pre>
<p align="center">프로미스를 위한 유틸리티 메서드</p>
<p>예시 1: 50가지의 포켓몬 정보 얻기</p>
<pre><code class="language-js">const ALL_POKEMONS_URL = 'https://pokeapi.co/api/v2/pokemon?limit=50';

// 아래 함수는 위에서 언급했었죠!
let promise = getPromise(ALL_POKEMONS_URL);

const consumer = () =&gt; {
    promise.then(
        (result) =&gt; {
            console.log({result}); // 50 가지의 포켓몬 결과 출력
        },
        (error) =&gt; {
            // 유효한 URL이라면 실행되지 않는 부분
            console.log('We have encountered an Error!'); // 에러 출력
    });
}

consumer();
</code></pre>
<p>예시 2: 유효하지 않은 URL일 때</p>
<pre><code class="language-js">const POKEMONS_BAD_URL = 'https://pokeapi.co/api/v2/pokemon-bad/';

// 아래 코드는 URL의 404 에러로 인해 거절됩니다
let promise = getPromise(POKEMONS_BAD_URL);

const consumer = () =&gt; {
    promise.then(
        (result) =&gt; {
            // 프로미스는 이행(resolve)되지 않았기 때문에 이 부분은 실행되지 않습니다.
            console.log({result});
        },
        (error) =&gt; {
            // 거절된(rejected) 프로미스는 이 부분을 실행할 것입니다.
            console.log('We have encountered an Error!'); // 에러 출력
        }
    );
}

consumer();
</code></pre>
<h3 id="catch"><code>.catch()</code> 메서드를 사용하는 방법</h3>
<p>프로미스의 에러(거부)를 다룰 때 이 메서드를 사용할 수 있습니다. <code>.then()</code>의 첫 번째 인자로 <code>null</code>을 전달해주는 문법은 에러를 다루기 좋은 방법은 아닙니다. 그래서 같은 작업을 위해 <code>.catch()</code>를 사용해 더 깔끔한 문법으로 작성할 수 있습니다.</p>
<pre><code class="language-js">// 유효하지 않은 URL(404 에러)로 인해 거부될 프로미스
let promise = getPromise(POKEMONS_BAD_URL);

const consumer = () =&gt; {
    promise.catch(error =&gt; console.log(error));
}

consumer();
</code></pre>
<p>프로미스 실행 함수나 핸들러에서 <code>reject</code> 함수를 실행하는 대신 <code>new Error("Something wrong!")</code>과 같은 에러를 던지게 되면 이 프로미스는 거부된 것으로 해석될 것입니다. 즉 <code>.catch</code> 핸들러 메서드에 의해 에러가 잡히게 됩니다.</p>
<p>프로미스 실행 함수와 핸들러 함수에서 일어나는 <em>동기적인</em> 예외 상황에서도 같게 동작합니다.</p>
<p>다음은 프로미스가 거부된 것으로 처리되어 <code>.catch</code> 핸들러 메서드가 호출된 예시입니다.</p>
<pre><code class="language-js">new Promise((resolve, reject) =&gt; {
  throw new Error("Something is wrong!");// reject 함수를 실행하지 않습니다
}).catch((error) =&gt; console.log(error)); 
</code></pre>
<h3 id="finally"><code>.finally()</code> 메서드를 사용하는 방법</h3>
<p><code>.finally()</code> 핸들러는 로더를 멈추거나 실시간 연결을 끊는 등 정리를 위해 사용됩니다. <code>finally</code> 메서드는 프로미스의 <code>성공</code>이나 <code>거부</code> 여부와 상관 없이 실행됩니다. 이 메서드는 .then()이나 .catch()를 또 실행할 수 있는 다음 핸들러에게 결과 값이나 에러를 전달합니다.</p>
<p>세 메서드를 함께 이해할 수 있는 예시를 살펴보겠습니다.</p>
<pre><code class="language-js">let loading = true;
loading &amp;&amp; console.log('Loading...');

// 프로미스 얻기
promise = getPromise(ALL_POKEMONS_URL);

promise.finally(() =&gt; {
    loading = false;
    console.log(`Promise Settled and loading is ${loading}`);
}).then((result) =&gt; {
    console.log({result});
}).catch((error) =&gt; {
    console.log(error)
});
</code></pre>
<p>추가 설명</p>
<ul>
<li><code>.finally()</code> 메서드는 로딩을 <code>false</code>로 설정합니다.</li>
<li>프로미스가 이행되면 <code>.then()</code> 메서드가 실행됩니다. 프로미스가 에러와 함께 거부되면 <code>.catch()</code> 메서드가 실행됩니다. <code>.finally()</code>는 이행 혹은 거부와는 관계 없이 실행됩니다.</li>
</ul>
<h2 id="">프로미스 체인이란</h2>
<p><code>promise.then()</code>은 항상 프로미스를 반환합니다. 이 프로미스는 <code>pending</code>이라는 <code>상태</code>와 <code>undefined</code>라는 <code>결과</code>를 갖게될 것입니다. 이를 통해 다음 프로미스에 <code>.then</code> 메서드를 호출할 수 있게 해줍니다.</p>
<p>첫 번째 <code>.then</code> 메서드가 반환한 결과 값은 다음 <code>.then</code> 메서드가 받을 수 있습니다. 두 번째 결과는 이제 세 번째 <code>.then()</code>으로 전달될 수 있고 이런 방식으로 계속 이어질 수 있습니다. 이 방식은 프로미스를 밑으로 전달해주는 <code>.then</code> 메서드 체인을 형성합니다. 이 현상은 <code>프로미스 체인</code>이라고 불립니다.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/image-105.png" alt="프로미스 체인" width="600" height="400" loading="lazy"></p>
<p align="center">프로미스 체인</p>
<p>다음 예제를 봅시다.</p>
<pre><code class="language-js">let promise = getPromise(ALL_POKEMONS_URL);

promise.then(result =&gt; {
    let onePokemon = JSON.parse(result).results[0].url;
    return onePokemon;
}).then(onePokemonURL =&gt; {
    console.log(onePokemonURL);
}).catch(error =&gt; {
    console.log('In the catch', error);
});
</code></pre>
<p>먼저 프로미스를 이행하고 첫번째 포켓몬을 얻기 위한 URL을 추출합니다. 그렇게 얻은 결과는 프로미스 형태로 다음 .then() 핸들러로 전달됩니다. 결과는 다음과 같습니다.</p>
<pre><code class="language-js">https://pokeapi.co/api/v2/pokemon/1/
</code></pre>
<p><code>.then</code> 메서드는 다음 중 하나를 반환합니다.</p>
<ul>
<li>결과 (이미 살펴본 것이죠)</li>
<li>새로운 프로미스</li>
</ul>
<p>에러를 반환할 수도 있습니다.</p>
<p>다음 예시는 결과와 새로운 프로미스를 반환하는 <code>.then</code> 메서드로 이루어진 프로미스 체인을 만든 것입니다.</p>
<pre><code class="language-js">// 다수의 then과 catch로 이루어진 프로미스 체인
let promise = getPromise(ALL_POKEMONS_URL);

promise.then(result =&gt; {
    let onePokemon = JSON.parse(result).results[0].url;
    return onePokemon;
}).then(onePokemonURL =&gt; {
    console.log(onePokemonURL);
    return getPromise(onePokemonURL);
}).then(pokemon =&gt; {
    console.log(JSON.parse(pokemon));
}).catch(error =&gt; {
    console.log('In the catch', error);
});
</code></pre>
<p>첫 번째 <code>.then()</code>은 추출한 URL을 결과로 반환합니다. 이 URL은 해당 URL을 인자로 받는 새로운 프로미스를 반환하는 두 번째 <code>.then</code>으로 전달됩니다.</p>
<p>이 프로미스가 이행되면 포켓몬에 대한 정보 얻을 수 있는 체인으로 전달됩니다. 결과는 다음과 같습니다.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/11/image-159.png" alt="콘솔로 확인한 프로미스 체인의 결과" width="600" height="400" loading="lazy"></p>
<p align="center">프로미스 체인의 결과</p>
<p>에러나 프로미스 거부가 발생하면 프로미스 체인의 .catch 메서드가 실행됩니다.</p>
<p>중요한 점: <code>.then</code>을 여러번 호출한다고 프로미스 체인이 생성되는 것은 아닙니다. 다음과 같이 코드를 작성하면 버그만 발생할 것입니다.</p>
<pre><code class="language-js">let promise = getPromise(ALL_POKEMONS_URL);

promise.then(result =&gt; {
    let onePokemon = JSON.parse(result).results[0].url;
    return onePokemon;
});
promise.then(onePokemonURL =&gt; {
    console.log(onePokemonURL);
    return getPromise(onePokemonURL);
});
promise.then(pokemon =&gt; {
    console.log(JSON.parse(pokemon));
});
</code></pre>
<p>같은 프로미스에 <code>.then</code> 메서드를 세 번 호출하고 있지만 프로미스를 전달하고 있지는 않습니다. 이것은 프로미스 체인과는 다릅니다. 위 예시의 결과로 에러가 나올 것입니다.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/11/image-160.png" alt="프로미스 체인을 잘못 사용했을 때 나타나는 에러" width="600" height="400" loading="lazy"></p>
<h2 id="">여러 개의 프로미스를 처리하는 방법</h2>
<p>.then, .catch, .finally 같은 핸들러 메서드와는 별개로 프로미스 API에서 사용 가능한 여섯 가지의 정적 메서드가 있습니다. 첫 네 개의 메서드들은 프로미스 배열을 받아 병렬적으로 실행합니다.</p>
<ol>
<li>Promise.all</li>
<li>Promise.any</li>
<li>Promise.allSettled</li>
<li>Promise.race</li>
<li>Promise.resolve</li>
<li>Promise.reject</li>
</ol>
<p>하나씩 살펴봅시다.</p>
<h3 id="promiseall">Promise.all() 메서드</h3>
<p><code>Promise.all([promises])</code>는 인자로 프로미스의 집합(예를 들면, 배열)을 받고 병렬로 실행시킵니다.</p>
<p>이 메서드는 모든 프로미스가 이행(resolve)되기를 기다린 후에 프로미스의 결과를 배열로 반환합니다. 만약 프로미스 중 어떤 하나라도 거절(reject)되거나 에러로 인해 실패한다면 다른 모든 프로미스의 결과는 무시될 것입니다.</p>
<p>세 마리의 포켓몬에 대한 정보를 얻기 위해 세 개의 프로미스를 생성해봅시다.</p>
<pre><code class="language-js">const BULBASAUR_POKEMONS_URL = 'https://pokeapi.co/api/v2/pokemon/bulbasaur';
const RATICATE_POKEMONS_URL = 'https://pokeapi.co/api/v2/pokemon/raticate';
const KAKUNA_POKEMONS_URL = 'https://pokeapi.co/api/v2/pokemon/kakuna';

let promise_1 = getPromise(BULBASAUR_POKEMONS_URL);
let promise_2 = getPromise(RATICATE_POKEMONS_URL);
let promise_3 = getPromise(KAKUNA_POKEMONS_URL);
</code></pre>
<p>Promise.all() 메서드에 프로미스 배열을 전달해 사용합니다.</p>
<pre><code class="language-js">Promise.all([promise_1, promise_2, promise_3]).then(result =&gt; {
    console.log({result});
}).catch(error =&gt; {
    console.log('에러 발생');
});
</code></pre>
<p>결과:<br>
<img src="https://www.freecodecamp.org/news/content/images/2020/11/image-161.png" alt="Promise.all() 메서드의 처리 결과" width="600" height="400" loading="lazy"></p>
<p>결과에서 볼 수 있듯, 모든 프로미스의 결과가 반환됩니다. 모든 프로미스를 실행시키는 시간은 프로미스가 실행되는 데 걸리는 최대 시간과 같습니다.</p>
<h3 id="promiseany">Promise.any() 메서드</h3>
<p><code>all()</code> 메서드와 비슷한 <code>Promise.any([promises])</code> 또한 병렬로 처리할 프로미스를 배열로 받습니다. 이 메서드는 모든 프로미스가 이행(resolve)되기를 기다리지 않습니다. 프로미스 중 하나라도 settled 되면 이행이 종료됩니다.</p>
<pre><code class="language-js"> Promise.any([promise_1, promise_2, promise_3]).then(result =&gt; {
     console.log(JSON.parse(result));
 }).catch(error =&gt; {
     console.log('에러 발생');
 });
</code></pre>
<p>결과는 이행(resolve)된 프로미스 중 하나가 될 것입니다.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/11/image-162.png" alt="프로미스가 하나라도 이행된 경우의 콘솔 결과" width="600" height="400" loading="lazy"></p>
<h3 id="promiseallsettled">Promise.allSettled() 메서드</h3>
<p><code>Promise.allSettled([promises])</code> 메서드는 모든 프로미스가 settled(resolve/reject) 되기를 기다린 후에 객체로 구성된 배열을 결과로 반환합니다. 성공적으로 이행된다면 결과는 상태(fulfilled/rejected)와 결괏값을 포함할 것입니다. 거절된(rejected) 상태라면 에러에 대한 이유를 반환할 것입니다.</p>
<p>이행된 프로미스들의 예시를 살펴보겠습니다.</p>
<pre><code class="language-js">Promise.allSettled([promise_1, promise_2, promise_3]).then(result =&gt; {
    console.log({result});
}).catch(error =&gt; {
    console.log('에러입니다!');
});
</code></pre>
<p>결과:<br>
<img src="https://www.freecodecamp.org/news/content/images/2020/11/image-163.png" alt="모든 프로미스가 이행된 경우의 콘솔 결과" width="600" height="400" loading="lazy"></p>
<p>프로미스 중 하나인 promise_1이 거부된다면 다음과 같은 결과가 나옵니다.</p>
<pre><code class="language-js">let promise_1 = getPromise(POKEMONS_BAD_URL);
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/11/image-164.png" alt="promise_1만 거부된 경우의 콘솔 결과" width="600" height="400" loading="lazy"></p>
<h3 id="promiserace">Promise.race() 메서드</h3>
<p><code>Promise.race([promises])</code>는 (가장 빠른) 첫번째 프로미스가 settled 되기를 기다린 후에 그에 따른 결과/에러를 반환합니다.</p>
<pre><code class="language-js">Promise.race([promise_1, promise_2, promise_3]).then(result =&gt; {
    console.log(JSON.parse(result));
}).catch(error =&gt; {
    console.log('에러 발생');
});
</code></pre>
<p>이행된(resolved) 가장 빠른 프로미스의 결과는 다음과 같습니다.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/11/image-165.png" alt="race 메서드의 콘솔 출력 결과" width="600" height="400" loading="lazy"></p>
<h3 id="promiseresolvereject">Promise.resolve/reject 메서드</h3>
<p><code>Promise.resolve(value)</code>는 전달된 값과 함께 프로미스를 이행합니다. 다음과 같습니다.</p>
<pre><code class="language-js">let promise = new Promise(resolve =&gt; resolve(value));
</code></pre>
<p><code>Promise.reject(error)</code>은 다음과 같이 전달된 에러와 함께 프로미스를 거부합니다.</p>
<pre><code class="language-js">let promise = new Promise((resolve, reject) =&gt; reject(error));
</code></pre>
<h2 id="pizzahub">PizzaHub 예시를 프로미스로 바꿀 수 있나요?</h2>
<p>그럼요, 한번 해봅시다. <code>query</code> 메서드가 프로미스를 반환한다고 가정해봅시다. 여기 query() 메서드의 예시가 있습니다. 실제로 이 메서드는 데이터베이스와 소통해서 결과를 반환합니다. 지금 경우에는 하드코딩되어 있지만 같은 역할을 수행합니다.</p>
<pre><code class="language-js">function query(endpoint) {
  if (endpoint === `/api/pizzahub/`) {
    return new Promise((resolve, reject) =&gt; {
      resolve({'shopId': '123'});
    })
  } else if (endpoint.indexOf('/api/pizzahub/pizza/') &gt;=0) {
    return new Promise((resolve, reject) =&gt; {
      resolve({pizzas: [{'type': 'veg', 'name': 'margherita', 'id': '123'}]});
    })
  } else if (endpoint.indexOf('/api/pizzahub/beverages') &gt;=0) {
    return new Promise((resolve, reject) =&gt; {
      resolve({id: '10', 'type': 'veg', 'name': 'margherita', 'beverage': 'coke'});
    })
  } else if (endpoint === `/api/order`) {
    return new Promise((resolve, reject) =&gt; {
      resolve({'type': 'veg', 'name': 'margherita', 'beverage': 'coke'});
    })
  }
}
</code></pre>
<p>다음은 <code>콜백 지옥</code>을 리팩토링 해보겠습니다. 이를 위해 먼저 몇개의 논리적인 함수를 만들겠습니다.</p>
<pre><code class="language-js">// 가게 id 반환
let getShopId = result =&gt; result.shopId;

// 가게의 피자 리스트가 담긴 프로미스 반환
let getPizzaList = shopId =&gt; {
  const url = `/api/pizzahub/pizza/${shopId}`;
  return query(url);
}

// 고객의 요청에 맞는 피자를 프로미스로 반환
let getMyPizza = (result, type, name) =&gt; {
  let pizzas = result.pizzas;
  let myPizza = pizzas.find((pizza) =&gt; {
    return (pizza.type===type &amp;&amp; pizza.name===name);
  });
  const url = `/api/pizzahub/beverages/${myPizza.id}`;
  return query(url);
}

// 주문 후 프로미스 반환
let performOrder = result =&gt; {
  let beverage = result.id;
   return query(`/api/order`, {'type': result.type, 'name': result.name, 'beverage': result.beverage});
}

// 주문 확인
let confirmOrder = result =&gt; {
    console.log(`Your order of ${result.type} ${result.name} with ${result.beverage} has been placed!`);
}
</code></pre>
<p>필요한 프로미스를 만들기 위해 위 함수들을 사용합니다. 여기서 <code>콜백 지옥</code> 예제와 비교해볼 수 있습니다. 아래 코드는 확실히 깔끔하고 우아하네요.</p>
<pre><code class="language-js">function orderPizza(type, name) {
  query(`/api/pizzahub/`)
  .then(result =&gt; getShopId(result))
  .then(shopId =&gt; getPizzaList(shopId))
  .then(result =&gt; getMyPizza(result, type, name))
  .then(result =&gt; performOrder(result))
  .then(result =&gt; confirmOrder(result))
  .catch(function(error){
    console.log(`Bad luck, No Pizza for you today!`);
  })
}
</code></pre>
<p>마지막으로 다음과 같이 피자 종류와 이름을 전달해서 orderPizza() 메서드를 호출합니다.</p>
<pre><code class="language-js">orderPizza('veg', 'margherita');
</code></pre>
<h2 id="">다음으로 배울 내용</h2>
<p>여기까지 거의 읽으셨다면 축하드립니다! 이제 여러분은 자바스크립트의 프로미스에 대해 더 잘 이해할 수 있으실 겁니다. 이 글에서 사용된 모든 예제는 이 <a href="https://github.com/atapas/js-promise-example">깃허브 레포지토리</a>에 있습니다.</p>
<p>다음으로는 더 간단하게 코드를 작성할 수 있는 자바스크립트의 <code>async</code> 함수를 배울 차례입니다. 자바스크립트의 프로미스를 학습할 수 있는 가장 좋은 방법은 작은 예제를 만들고 쌓아가는 것입니다.</p>
<p>Angular, React, Vue 등과 같은 프레임워크나 라이브러리 사용과는 관계 없이 비동기 처리는 필수적입니다. 즉, 일을 더 잘 처리하기 위해선 프로미스를 이해해야 합니다.</p>
<p>또한 이제 여러분은 <code>fetch</code> 메서드가 더 쉽게 느껴지실 겁니다.</p>
<pre><code class="language-js">fetch('/api/user.json')
.then(function(response) {
    return response.json();
})
.then(function(json) {
    console.log(json); // {"name": "tapas", "blog": "freeCodeCamp"}
});
</code></pre>
<ul>
<li><code>fetch</code> 메서드는 프로미스를 반환합니다. 그래서 여기에 <code>.then</code> 핸들러를 호출할 수 있습니다.</li>
<li>나머지는 우리가 글에서 배웠던 프로미스 체인에 관한 내용입니다.</li>
</ul>
<h2 id="">마치기 전에</h2>
<p>지금까지 읽어주셔서 감사합니다. <a href="https://twitter.com/tapasadhikary">트위터(@tapasadhikary)</a>에서 소통해요.</p>
<p>좋아하실 만한 글들을 추천드릴게요.</p>
<ul>
<li><a href="https://blog.greenroots.info/javascript-undefined-and-null-lets-talk-about-it-one-last-time">JavaScript undefined and null: Let's talk about it one last time!</a></li>
<li><a href="https://blog.greenroots.info/javascript-equality-comparison-with-and-objectis">JavaScript: Equality comparison with ==, === and Object.is</a></li>
<li><a href="https://www.freecodecamp.org/news/javascript-this-keyword-binding-rules/">The JavaScript <code>this</code> Keyword +5 Key Binding Rules Explained for JS Beginners</a></li>
<li><a href="https://www.freecodecamp.org/news/javascript-typeof-how-to-check-the-type-of-a-variable-or-object-in-js/">JavaScript TypeOf - How to Check the Type of a Variable or Object in JS</a></li>
</ul>
<p>여기까지입니다. 다음 글에서 만나요. 그때까지 모두 잘 지내세요.</p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 자바스크립트로 연결 리스트를 구현하는 방법 ]]>
                </title>
                <description>
                    <![CDATA[ 여러분이 만약 데이터 구조를 공부하고 있다면, 연결 리스트(linked list)는 반드시 알아야 할 구조 중 하나입니다. 연결 리스트를 좀 더 이해하고 싶거나 구현하는 방법이 궁금하다면 이 게시글이 도움을 줄 것입니다. 이 글에서는 연결 리스트가 무엇이고, 유사한 특징을 가진 배열(array)과 어떤 차이점이 있는지, 그리고 이를 자바스크립트로 어떻게 구현하는지 알아보겠습니다. 연결 리스트란 연결 ]]>
                </description>
                <link>https://www.freecodecamp.org/korean/news/implementing-a-linked-list-in-javascript/</link>
                <guid isPermaLink="false">645c9f03670e16066f8f29bd</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Heegu Yang ]]>
                </dc:creator>
                <pubDate>Fri, 12 May 2023 06:32:21 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/korean/news/content/images/2023/05/5f9c9a86740569d1a4ca2622.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>기사 원문:</strong> <a href="https://www.freecodecamp.org/news/implementing-a-linked-list-in-javascript/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Implement a Linked List in JavaScript</a>
      </p><p>여러분이 만약 데이터 구조를 공부하고 있다면, 연결 리스트(linked list)는 반드시 알아야 할 구조 중 하나입니다. 연결 리스트를 좀 더 이해하고 싶거나 구현하는 방법이 궁금하다면 이 게시글이 도움을 줄 것입니다.</p><p>이 글에서는 연결 리스트가 무엇이고, 유사한 특징을 가진 배열(array)과 어떤 차이점이 있는지, 그리고 이를 자바스크립트로 어떻게 구현하는지 알아보겠습니다.</p><h2 id="-">연결 리스트란</h2><p>연결 리스트는 선형적인 데이터 구조라는 점에서 배열과 유사합니다. 하지만 배열과 달리, 연결 리스트의 요소(elements)들은 특정 메모리 주소나 인덱스에 저장되지 않습니다. 오히려 각 요소는 포인터 또는 다음 객체에 대한 링크를 가지는 독립적인 객체에 가깝습니다.</p><p>연결 리스트의 각 요소를 노드(node)라 부릅니다. 노드는 일반적으로 데이터 그리고 다음 노드를 가리키는 링크, 이 2가지 아이템으로 구성됩니다. 참고로 데이터의 유형은 다양하게 올 수 있습니다. 아래 도표를 보겠습니다.</p><figure class="kg-card kg-image-card"><img src="https://res.cloudinary.com/dvj2hbywq/image/upload/v1590572188/Group_14_5_bvpwu0.png" class="kg-image" alt="연결리스트의 구조 이미지" width="600" height="400" loading="lazy"></figure><p>연결 리스트의 가장 첫 번째 지점을 헤드(head)라 부릅니다. 헤드는 연결 리스트의 첫 번째 노드를 의미합니다. 마지막 노드는 null을 가르킵니다. 만약 연결 리스트가 비어있는 경우, 헤드는 null을 참조하게 됩니다.</p><p>자바스크립트로 연결 리스트를 표현하자면 이렇게 표현할 수 있습니다:</p><pre><code class="language-javascript">const list = {
    head: {
        value: 6
        next: {
            value: 10                                             
            next: {
                value: 12
                next: {
                    value: 3
                    next: null	
                    }
                }
            }
        }
    }
};
</code></pre><h2 id="--1">연결 리스트의 장점</h2><ul><li>연결 리스트는 데이터 구조의 큰 틀을 바꾸지 않고 노드를 추가하거나 삭제하기 쉽다는 장점이 있습니다. 이러한 점이 배열과 대비되는 점입니다.</li></ul><h2 id="--2">연결 리스트의 단점</h2><ul><li>연결 리스트는 탐색이 느립니다. 배열과 달리, 연결 리스트는 데이터에 무작위 접근(random access)을 할 수 없기 때문입니다. 노드들은 첫 번째 노드부터 순차적으로만 접근해야 합니다.</li><li>연결 리스트는 배열보다 더 많은 메모리를 사용합니다. 왜냐하면 각 노드는 포인터를 담고 있기 때문입니다.</li></ul><h2 id="--3">연결 리스트의 유형</h2><p>연결 리스트는 크게 3가지 유형이 있습니다.</p><ul><li>단일 연결 리스트(Singly Linked Lists) : 각 노드는 하나의 포인터만 가집니다. 우리가 위에서 이야기한 유형이 단일 연결 리스트입니다.</li><li>이중 연결 리스트(Doubly Linked Lists) : 각 노드는 2개의 포인터를 가지는데, 하나는 다음 노드를 그리고 나머지 하나는 이전 노드를 가르킵니다.</li><li>원형 연결 리스트(Circular Linked Lists) : 연결 리스트를 응용한 유형으로, 마지막 노드의 포인터가 첫 노드 또는 특정 노드를 가르키고 있는 마치 루프 형태를 가지는 유형을 말합니다.</li></ul><h2 id="--4">자바스크립트로 리스트 노드 구현하기</h2><p>연결 리스트(Singly Linked Lists의 경우)의 노드는 데이터와 포인터, 총 2개의 요소를 가진다고 했습니다. 자바스크립트로 아래와 같이 구현할 수 있습니다.</p><pre><code class="language-javascript">class ListNode {
    constructor(data) {
        this.data = data
        this.next = null                
    }
}
</code></pre><h2 id="--5">자바스크립트로 연결 리스트 구현하기</h2><p>아래는 생성자(constructor)를 사용하여 연결 리스트 클래스를 구현하는 코드입니다. 헤드 노드에 아무 값도 전달하지 않으면 null로 초기화됩니다.</p><pre><code class="language-js">class LinkedList {
    constructor(head = null) {
        this.head = head
    }
}
</code></pre><h2 id="--6">함께 사용하기</h2><p>위에서 만들었던 클래스를 사용해서 연결 리스트를 구현하겠습니다. 우선, &nbsp;<code>node1</code>와 &nbsp;<code>node2</code> 두 개의 노드를 만들어 주세요. 그리고 <code>node1</code>에 <code>node2</code> 를 가르키는 포인터도 만들어 주세요.</p><pre><code class="language-js">let node1 = new ListNode(2)
let node2 = new ListNode(5)
node1.next = node2
</code></pre><p>그다음, &nbsp;<code>node1</code>를 사용하여 연결 리스트를 만들겠습니다.</p><pre><code class="language-js">let list = new LinkedList(node1)
</code></pre><p>우리가 만든 리스트에서 노드를 호출해 봅시다.</p><pre><code class="language-js">console.log(list.head.next.data) //returns 5
</code></pre><h2 id="--7">연결 리스트 메소드들</h2><p>다음은 연결 리스트를 위한 4개의 헬퍼 메소드를 구현해 보겠습니다.</p><ol><li><code>size()</code></li><li><code>clear()</code></li><li><code>getLast()</code></li><li><code>getFirst()</code></li></ol><h2 id="1-size-">1. size()</h2><p><code>size()</code> 메소드는 연결 리스트에 있는 노드들의 개수를 반환합니다.</p><pre><code class="language-js">size() {
    let count = 0; 
    let node = this.head;
    while (node) {
        count++;
        node = node.next
    }
    return count;
}
</code></pre><h2 id="2-clear-">2. clear()</h2><p><code>clear()</code> 메소드는 리스트를 비우는 역할을 합니다.</p><pre><code class="language-js">clear() {
    this.head = null;
}
</code></pre><h2 id="3-getlast-">3. getLast()</h2><p><code>getLast()</code> 메소드는 연결 리스트의 마지막 노드를 반환합니다.</p><pre><code class="language-js">getLast() {
    let lastNode = this.head;
    if (lastNode) {
        while (lastNode.next) {
            lastNode = lastNode.next
        }
    }
    return lastNode
}
</code></pre><h2 id="4-getfirst-">4. getFirst()</h2><p><code>getFirst()</code> 메소드는 연결 리스트의 첫 번째 노드를 반환합니다.</p><pre><code class="language-js">getFirst() {
    return this.head;
}
</code></pre><h2 id="--8">마치며</h2><p>이번 글에서는 연결 리스트가 무엇이고 자바스크립트로 어떻게 구현하는지 알아보았습니다. 그리고 연결 리스트의 다양한 유형들과 각 장단점도 함께 알아보았습니다.</p><p>이 글을 재미있게 읽으셨길 바랍니다.</p><p>저의 새로운 글에 대한 알람을 받고 싶다면 여기를 클릭해 주세요.</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 자바스크립트 데이터 타입들: Typeof 설명 ]]>
                </title>
                <description>
                    <![CDATA[  typeof는 호출했을 때 변수의 타입을 반환하는 자바스크립트 키워드입니다. 이를 사용하여 함수 매개변수의 유효성을 검사하거나 변수가 정의되어 있는지 확인할 수 있습니다. 그 외에 다른 용도들도 있습니다. typeof 연산자는 코드에서 변수의 타입을 쉽게 확인할 수 있는 방법이기 때문에 유용합니다. 이것은 자바스크립트가 동적 타입 언어 [https://stackoverflow.com/questions/2690544/what-is-the-difference-between-a-strongly-typed-language-and-a-statically-typed] 이기 때문에 중요합니다. 이는 변수를 생성할 ]]>
                </description>
                <link>https://www.freecodecamp.org/korean/news/javascript-data-types-typeof-explained/</link>
                <guid isPermaLink="false">6436fe64c44e4506867281a0</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Jung Hoon Lee ]]>
                </dc:creator>
                <pubDate>Wed, 12 Apr 2023 21:46:34 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/korean/news/content/images/2023/04/5f9c9e80740569d1a4ca3d75.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>기사 원문:</strong> <a href="https://www.freecodecamp.org/news/javascript-data-types-typeof-explained/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">JavaScript Data Types: Typeof Explained</a>
      </p><h4></h4><h3 id="typeof-"><code>typeof</code>는 호출했을 때 변수의 타입을 반환하는 자바스크립트 키워드입니다. 이를 사용하여 함수 매개변수의 유효성을 검사하거나 변수가 정의되어 있는지 확인할 수 있습니다. 그 외에 다른 용도들도 있습니다.</h3><p><code>typeof</code> 연산자는 코드에서 변수의 타입을 쉽게 확인할 수 있는 방법이기 때문에 유용합니다. 이것은 자바스크립트가 <a href="https://stackoverflow.com/questions/2690544/what-is-the-difference-between-a-strongly-typed-language-and-a-statically-typed">동적 타입 언어</a>이기 때문에 중요합니다. 이는 변수를 생성할 때 타입을 할당하지 않아도 된다는 뜻입니다. 변수는 이런 식으로 제한이 되지 않으므로 프로그램의 실행 중에 타입이 바뀔 수 있습니다.</p><p>예제:</p><pre><code class="language-javascript">var x = 12345; // number
x = 'string'; // string
x = { key: 'value' }; // object
</code></pre><p>위의 예제에서 볼 수 있듯이, 자바스크립트의 변수는 프로그램이 실행되는 동안 타입이 바뀔 수 있습니다. 프로그래머로서 이를 추적하기는 어려울 수 있고, 여기서 <code>typeof</code> 연산자가 유용합니다.</p><p><code>typeof</code> 연산자는 변수의 현재 타입을 나타내는 문자열을 반환합니다. <code>typeof(변수)</code> 또는 <code>typeof 변수</code>로 입력하여 사용합니다. 이전 예제로 돌아가서 각 단계의 변수 <code>x</code>의 타입을 확인하는데 이를 사용할 수 있습니다.</p><pre><code class="language-javascript">var x = 12345;
console.log(typeof x) // number
x = 'string';
console.log(typeof x) // string
x = { key: 'value' };
console.log(typeof x) // object
</code></pre><p>이는 함수 안에서 변수의 타입을 확인하고 적절히 계속해 나가는 데 유용할 수 있습니다.</p><p>문자열이나 숫자 변수를 가져올 수 있게 하는 함수의 예제입니다.<br>예제:</p><pre><code class="language-javascript">function doSomething(x) {
  if(typeof(x) === 'string') {
    alert('x is a string')
  } else if(typeof(x) === 'number') {
    alert('x is a number')
  }
}
</code></pre><p><code>typeof</code> 연산자가 유용할 수 있는 또 다른 방법은 코드 안에서 액세스하려고 시도하기 전에 변수가 정의되어 있는지 확인하는 것입니다. 이는 만약에 정의되어 있지 않은 변수에 액세스를 시도하려고 할 때 발생할 수 있는 프로그램 오류를 막을 수 있습니다.</p><pre><code class="language-javascript">function(x){
  if (typeof(x) === 'undefined') {
    console.log('variable x is not defined');
    return;
  }
  // continue with function here...
}
</code></pre><p><code>typeof</code> 연산자의 결과는 숫자를 확인할 때 항상 기대했던 결과를 주지는 않을 것입니다.<br>숫자는 여러 가지 이유로 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NaN">Nan(Not A Number : 숫자가 아님)</a> 값으로 바뀔 수 있습니다.</p><pre><code class="language-javascript">console.log(typeof NaN); //"number"
</code></pre><p>아마도 오브젝트 안의 숫자에 액세스하는 것을 잊어서 오브젝트 자체에 숫자를 곱하려고 시도했을 수도 있습니다.</p><pre><code class="language-javascript">var x = 1;
var y = { number: 2 };
console.log(x * y); // NaN
console.log(typeof (x * y)); // number
</code></pre><p>이것이 유용한 유효성 검사 방법임에도 불구하고 자바스크립트는 좀 이상한 부분이 있고 그중의 하나는 특정 명령어에 대한 <code>typeof</code>의 결과이기 때문에 주의해야 합니다. 예를 들어 자바스크립트에서는 많은 것들이 단지 <code>objects</code>로 취급됩니다.</p><pre><code class="language-javascript">var x = [1,2,3,4];
console.log(typeof x)  // object

console.log(typeof null)  // object
</code></pre><p><br></p><hr><p><br></p><p>만약 이글이 도움이 되었다면, <a href="https://twitter.com/intent/tweet?text=JavaScript%20Data%20Types%3A%20Typeof%20Explained%0A%0A">트윗해 주세요</a>.</p><p>무료로 코딩하는 법을 배우세요. freeCodeCamp의 오픈 소스 과정은 4만 명이 넘는 사람들이 개발자로 직업을 얻도록 도왔습니다. <a href="https://www.freecodecamp.org/learn/">시작하기</a>.</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 자바스크립트에서 `.splice()` 배열 메서드 사용하기 ]]>
                </title>
                <description>
                    <![CDATA[ 자바스크립트의 splice() 메서드는 배열 객체에 사용할 수 있는 내장 메서드입니다. 이는 기존 요소를 삭제하거나 교체하여 배열의 내용을 변경하며, 제거된 요소가 담긴 별도의 배열을 새로 반환합니다. 이 기사에서는 splice() 메서드를 사용하여 배열 요소를 삭제, 추가, 또는 교체하는 방법에 대해 설명하겠습니다. 먼저 배열의 요소를 삭제하는 방법부터 살펴봅시다. splice() 메서드로 배열 요소 삭제하기 ]]>
                </description>
                <link>https://www.freecodecamp.org/korean/news/javascript-splice-method/</link>
                <guid isPermaLink="false">63e3b17f9b0e65067a8f24d0</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Boyeon Ihn ]]>
                </dc:creator>
                <pubDate>Mon, 13 Feb 2023 05:34:28 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/korean/news/content/images/2023/02/javascript-splice-method.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>기사 원문:</strong> <a href="https://www.freecodecamp.org/news/javascript-splice-how-to-use-the-splice-js-array-method/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">JavaScript Splice – How to Use the .splice() JS Array Method</a>
      </p><!--kg-card-begin: markdown--><h3 id="splice">자바스크립트의 <code>splice()</code> 메서드는 배열 객체에 사용할 수 있는 내장 메서드입니다. 이는 기존 요소를 삭제하거나 교체하여 배열의 내용을 변경하며, 제거된 요소가 담긴 별도의 배열을 새로 반환합니다.</h3>
<p>이 기사에서는 <code>splice()</code> 메서드를 사용하여 배열 요소를 삭제, 추가, 또는 교체하는 방법에 대해 설명하겠습니다. 먼저 배열의 요소를 삭제하는 방법부터 살펴봅시다.</p>
<h2 id="splice"><code>splice()</code> 메서드로 배열 요소 삭제하기</h2>
<p>다음과 같은 내용이 담긴 <code>months</code>라는 배열이 있다고 가정해보겠습니다. 배열 이름은 달, 월을 뜻하는 <code>months</code>지만 요일이 배열에 섞여있는 것을 확인할 수 있습니다.</p>
<pre><code class="language-js">// 월과 요일명이 혼합된 배열
let months = ["January", "February", "Monday", "Tuesday"];
</code></pre>
<p><code>splice()</code> 메서드를 사용하여 <code>months</code> 메서드에서 요일 이름을 제거하는 동시에 요일만 포함된 새 배열을 만들 수 있습니다. 이렇게 말이죠.</p>
<pre><code class="language-js">// 요일이 담긴 배열 생성하기 
let months = ["January", "February", "Monday", "Tuesday"];
let days = months.splice(2); // 인덱스 2부터 배열 변경

console.log(days); // ["Monday", "Tuesday"]
</code></pre>
<p><code>splice()</code> 메서드를 사용할 때는 <code>start</code> 매개변수에 꼭 값을 전달해야 하는데, 이 매개변수는 배열의 변경을 시작할 인덱스를 지정합니다. 위의 예시에서 인수로 전달된 <code>2</code>는 <code>splice()</code> 메서드가 인덱스 <code>2</code>에서부터 요소를 제거하기 시작할 것이라는 뜻입니다.</p>
<p><code>deleteCount</code>라는 두 번째 매개변수에 값을 전달하면 배열에서 제거할 요소의 수를 지정할 수도 있습니다. 예를 들어, 하나의 요소만 제거하기 위해 다음과 같이 숫자 <code>1</code>을 두 번째 인수로 전달할 수 있습니다. (역주: 영어 원문 기사에서는 이 매개변수를 <code>removeCount</code>라고 표기했지만, 가장 최근 업데이트된 <a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/splice">MDN Web Docs</a> 설명서에 따라 <code>splice()</code>의 두 번째 매개변수를 <code>deleteCount</code>라고 지칭하겠습니다.)</p>
<pre><code class="language-js">let months = ["January", "February", "Monday", "Tuesday"];
let days = months.splice(2, 1); // 요소 하나만 삭제

console.log(days); // ["Monday"]
console.log(months); // ["January", "February", "Tuesday"]
</code></pre>
<p><code>deleteCount</code> 매개변수를 생략하면 <code>splice()</code>는 지정된 <code>start</code> 인덱스부터 배열의 끝까지의 모든 요소를 제거합니다.</p>
<h2 id="splice"><code>splice()</code>를 사용해 배열 요소 제거하고 새로 추가하기</h2>
<p><code>splice()</code> 메서드로 요소를 삭제하는 동시에 바로 새 요소를 추가할 수도 있습니다. <code>deleteCount</code> 매개변수 뒤에 배열에 추가할 요소를 전달하기만 하면 됩니다.</p>
<p>필수 매개변수와 선택적 매개변수를 모두 전달하는 경우의 문법은 다음과 같습니다.</p>
<pre><code class="language-js">// 필수 매개변수와 선택적 매개변수가 포함된 splice() 문법 
Array.splice(start, deleteCount, newItem, newItem, newItem, ...)
</code></pre>
<p>다음 예시에서는 <code>months</code> 배열에 "March"와 "April"을 추가하면서 "Monday"와 "Tuesday"를 제거하는 방법을 보여줍니다.</p>
<pre><code class="language-js">let months = ["January", "February", "Monday", "Tuesday"];
let days = months.splice(2, 2, "March", "April"); // 요소 두 개를 제외하고, 다른 요소를 추가

console.log(days); // ["Monday", "Tuesday"]
console.log(months); // ["January", "February", "March", "April"]
</code></pre>
<h2 id="">요소를 제거하지 않고 새 배열 요소 추가하기</h2>
<p>마지막으로 숫자 <code>0</code>을 <code>deleteCount</code> 매개변수에 전달하여 기존 요소를 삭제하지 않고도 새 요소를 추가할 수 있습니다. 요소가 제거되지 않을 경우 빈 배열이 반환되며, 경우에 따라 반환된 빈 배열을 변수에 저장해서 사용할 수 있습니다.</p>
<p>다음 예시에서는 기존 요소를 삭제하지 않고 "February" 요소 옆에 새 요소 "March"를 추가하는 것을 보여줍니다. 앞서 언급한 것처럼 반환되는 값이 빈 배열이므로 이를 별도로 저장하지 않아도 됩니다.</p>
<pre><code class="language-js">let months = ["January", "February", "Monday", "Tuesday"];
months.splice(2, 0, "March"); // 두 번째 매개변수가 0이므로 기존 요소 모두 유지

console.log(months); 
// ["January", "February", "March", "Monday", "Tuesday"]
</code></pre>
<h2 id="">마치며</h2>
<p>이렇게 <code>splice()</code> 메서드의 사용법을 배웠습니다. 수고하셨어요!</p>
<p>이 기사에서 소개된 내용 중 핵심적인 두 가지를 정리해볼까요?</p>
<ol>
<li>우선, <code>splice()</code> 메서드는 배열의 요소를 삭제하거나 새로 추가할 때 가장 자주 사용됩니다. 경우에 따라 언급된 예시처럼 요소가 섞인 배열을 분리할 때도 <code>splice()</code>를 사용할 수 있습니다.</li>
<li>또한 이 메서드는 제거된 요소가 담긴 새 배열을 반환하며, 반환된 배열을 자유롭게 변수에 할당할 수 있습니다. 배열에서 <code>0</code>개의 요소를 제거하면 <code>splice()</code>는 빈 배열을 반환하는데, 이런 경우에는 별도로 변수에 저장하지 않아도 됩니다.</li>
</ol>
<h2 id="">이 기사를 읽어주셔서 감사합니다!</h2>
<p>자바스크립트에 대해 더 알고 싶다면 <a href="https://sebhastian.com/javascript-tutorials/">제 사이트</a>를 방문해주세요. 문자열 조작, 날짜 조작, 배열 및 객체 메서드, 알고리즘 문제 해설 등 제가 자바스크립트 프로그래밍과 관련해 작성한 100개 이상의 기사를 확인하실 수 있습니다.</p>
<p>꼭 한 번 <a href="https://sebhastian.com/">확인해보세요</a>, 감사합니다! 😉</p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 자바스크립트에서 toString 메서드로 숫자를 문자열로 변환하는 방법 ]]>
                </title>
                <description>
                    <![CDATA[ toString() 메서드는 임의의 숫자형(number) 값을 문자열(string)로 변환하는 자바스크립트 Number 객체의 내장 메서드입니다. 자바스크립트에서 toString() 메서드 사용하기 toString() 메서드를 사용하려면 숫자형 값으로 메서드를 호출하기만 하면 됩니다. 다음 예시에서는 숫자형 24를 문자열로 변환하는 방법을 보여줍니다. 콘솔에 출력된 str 변수의 값이 문자열이기 때문에 큰 따옴표로 표시된 것을 확인할 수 있습니다. // `toString()` 메서드를 ]]>
                </description>
                <link>https://www.freecodecamp.org/korean/news/javascript-number-to-string/</link>
                <guid isPermaLink="false">63e3b1249b0e65067a8f24c0</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Boyeon Ihn ]]>
                </dc:creator>
                <pubDate>Mon, 13 Feb 2023 05:33:30 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/korean/news/content/images/2023/02/javascript-number-to-string.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>기사 원문:</strong> <a href="https://www.freecodecamp.org/news/javascript-number-to-string-how-to-use-tostring-to-convert-an-int-into-a-string/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">JavaScript Number to String – How to Use toString to Convert an Int into a String</a>
      </p><!--kg-card-begin: markdown--><h3 id="tostringnumberstringnumber"><code>toString()</code> 메서드는 임의의 숫자형(<code>number</code>) 값을 문자열(<code>string</code>)로 변환하는 자바스크립트 <code>Number</code> 객체의 내장 메서드입니다.</h3>
<h2 id="tostring">자바스크립트에서 <code>toString()</code> 메서드 사용하기</h2>
<p><code>toString()</code> 메서드를 사용하려면 숫자형 값으로 메서드를 호출하기만 하면 됩니다. 다음 예시에서는 숫자형 <code>24</code>를 문자열로 변환하는 방법을 보여줍니다. 콘솔에 출력된 <code>str</code> 변수의 값이 문자열이기 때문에 큰 따옴표로 표시된 것을 확인할 수 있습니다.</p>
<pre><code class="language-js">// `toString()` 메서드를 사용해 숫자형에서 문자열로 변환하기
var num = 24;
var str = num.toString();

console.log(num); // 24
console.log(str); // "24"
</code></pre>
<p><code>toString()</code> 메서드를 숫자형인 값에 즉시 호출할 수 있지만, 값을 괄호 <code>()</code>안에 표시해야합니다. 괄호를 생략하면 자바스크립트에서 <code>Invalid or unexpected token</code> 에러가 발생하니 주의하세요.</p>
<p>또한 <code>toString()</code> 메서드는 다음 예시와 같이 부동소수점 숫자와 음수를 변환할 수도 있습니다.</p>
<pre><code class="language-js">// toString 메서드로 다양한 숫자 유형을 문자열로 변환하기
24.toString(); // Error: Invalid or unexpected token
(24).toString(); // "24"
(9.7).toString(); // "9.7"
(-20).toString(); // "-20"
</code></pre>
<p>마지막으로, <code>toString()</code> 메서드에는 <code>radix</code>(기수) 또는 <code>base</code> 매개변수를 전달할 수 있습니다. <code>radix</code> 매개변수는 10진법(base 10)으로 표현된 숫자를 다른 진수로 변환합니다. 단, 변환된 진수의 자료형은 문자열이라는 것을 명심해야 합니다.</p>
<p>변환 가능한 진법은 다음을 포함합니다.</p>
<ul>
<li>두 개의 숫자(0과 1)만을 사용하는 이진법(binary system, base 2)</li>
<li>세 개의 숫자(0, 1, 2)를 사용하는 삼진법(ternary system, base 3)</li>
<li>네 개의 숫자(0, 1, 2, 3)를 사용하는 사진법(quaternary system, base 4)</li>
<li>그리고 0에서 9까지의 숫자와 로마 문자 A에서 Z까지의 조합을 가진 36진법(hexatridecimal system, base 36)까지 변환할 수 있습니다.</li>
</ul>
<pre><code class="language-js">// radix 매개변수를 전달하는 toString() 메서드 기본 문법
Number.toString(radix);
</code></pre>
<p><code>radix</code> 매개변수는 값이 최소 2부터 36까지인 숫자형일 수 있습니다. 다음은 10진수 5를 2진수(base 2)로 변환하는 예시입니다.</p>
<pre><code class="language-js">// toString() 메서드를 사용해 10진수를 2진수로 변환하기 
var str = (5).toString(2);

console.log(str); // "101"
</code></pre>
<p>위 코드의 10진수 <code>5</code>는 <code>101</code>에 해당하는 이진수로 변환된 다음 문자열로 최종 변환됩니다.</p>
<h2 id="tostring"><code>toString()</code> 메서드를 다른 자료형과 함께 사용하기</h2>
<p><code>toString()</code> 메서드는 숫자 자료형을 변환하는 것 외에도 다른 자료형을 문자열로 변환할 때 사용할 수 있습니다.</p>
<p>예를 들어 다음과 같이 배열(array)를 문자열로 변환할 수 있습니다.</p>
<pre><code class="language-js">// toString() 메서드로 배열을 문자열로 변환하기 
var arr = [ "Nathan", "Jack" ];
var str = arr.toString();

console.log(str); // "Nathan,Jack"
</code></pre>
<p>이번에는 논리타입 불린(boolean)형을 문자열로 변환해봅시다.</p>
<pre><code class="language-js">var bool = true;
var str = bool.toString();

console.log(str); // "true"
</code></pre>
<p>하지만 일반적으로 <code>toString()</code> 메서드는 다른 자료형을 변환하기보단 숫자형을 문자열로 변환하는데 가장 자주 사용되리라 생각합니다. 저도 보통 그런 용도로 이 메서드를 사용합니다. :)</p>
<h2 id="">이 기사를 읽어주셔서 감사합니다!</h2>
<p>관심이 있으시다면 제가 쓴 <a href="https://sebhastian.com/javascript-tofixed/">Rounding Numbers with toFixed() method(영문 기사: toFixed() 메서드로 숫자 반올림하기)</a> 그리고 <a href="https://sebhastian.com/javascript-absolute-value-math-abs/">Calculating Absolute Value with Math.abs() method(영문 기사: Math.abs() 메서드로 숫자의 절대값 계산하기)</a>와 같은 기사를 한 번 읽어보시길 바랍니다. 이 두 기사는 가장 자주 받는 JavaScript 관련 질문 중 두 가지에 대한 내용입니다.</p>
<p>또한 웹 개발 튜토리얼(대부분 자바스크립트 관련)에 대한 <a href="https://sebhastian.com/newsletter/">무료 뉴스레터</a>도 발행합니다.</p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 자바스크립트 Void 0 - 도대체 javascript:void(0);는 무슨 뜻일까? ]]>
                </title>
                <description>
                    <![CDATA[  > Void란 단어를 사전에서 찾아보면 “완전히 비어있는 공간”이라는 뜻을 찾아볼 수 있습니다. 하지만 이 단어를 프로그래밍에서 사용한다면 “아무것도” 반환하지 않는 걸 확인할 수 있습니다. 정확히 말하면, “빈 값”을 반환합니다. void 키워드란? 함수가 비어있단(void) 뜻은, 그 함수가 아무것도 반환하지 않은 것을 의미합니다. 이것은 자바스크립트에서 함수가 직접적으로 undefined 을 반환하는 것과 ]]>
                </description>
                <link>https://www.freecodecamp.org/korean/news/jabaseukeuribteu-void-0-dodaece-javascript-void-0-neun-museun-ddeusilgga/</link>
                <guid isPermaLink="false">63d922f341a99b065fb5ab11</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Yeseul Lee ]]>
                </dc:creator>
                <pubDate>Wed, 01 Feb 2023 09:29:17 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/korean/news/content/images/2023/01/68747470733a2f2f63646e2d6d656469612d322e66726565636f646563616d702e6f72672f77313238302f3566396339383131373430353639643161346361313766622e6a7067.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>기사 원문:</strong> <a href="https://www.freecodecamp.org/news/javascript-void-keyword-explained/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">JavaScript Void 0 – What Does javascript:void(0); Mean?</a>
      </p><h4></h4><blockquote>Void란 단어를 사전에서 찾아보면 “완전히 비어있는 공간”이라는 뜻을 찾아볼 수 있습니다. 하지만 이 단어를 프로그래밍에서 사용한다면 “아무것도” 반환하지 않는 걸 확인할 수 있습니다. 정확히 말하면, “빈 값”을 반환합니다.</blockquote><h1 id="void-">void 키워드란?</h1><p>함수가 비어있단(void) 뜻은, 그 함수가 아무것도 반환하지 않은 것을 의미합니다. 이것은 자바스크립트에서 함수가 직접적으로 <code>undefined</code>을 반환하는 것과 비슷합니다.</p><pre><code class="language-javascript">function und() {
  return undefined;
}
und();
</code></pre><p>또는 다음과 같이 작성하여 간접적으로 <code>undefined</code>을 반환할 수도 있습니다.</p><pre><code class="language-javascript">function und() {}
und();
</code></pre><p>2개의 숫자를 더하거나, 숫자 5개의 평균을 구하는 등의 표현식(Expressions)이나 문장(Statements)을 위의 함수에서 사용해도, 그 결과가 반환되지 않습니다.</p><p>이제 우리는 <code>void</code> 키워드가 무엇인지 압니다. 그렇다면 <code>javascript:void(0)</code>은 무엇일까요?</p><h1 id="javascript-void-0-"><code>javascript:void(0)</code>란?</h1><p>만약 <code>javascript:void(0)</code>를 반으로 나눈다면, <code>javascript:</code>와 <code>void(0)</code>가 됩니다. 각 부분에 대해 자세히 살펴보겠습니다.</p><h3 id="javascript-"><code>javascript:</code></h3><p>이것은 <strong>가상 URL</strong>이라고 할 수 있습니다. 브라우저에서 <code>&lt;a&gt;</code>태그의 <code>href</code>의 값으로 <code>javascript:</code>을 받는다면 이 값을 참조된 경로로 취급하지 않고, 쌍점(:) 뒤에 오는 것을 자바스크립트의 코드로 해석합니다.</p><p>예를 들어,</p><pre><code class="language-html">&lt;a href="javascript:console.log('javascript');alert('javascript')"&gt;Link&lt;/a&gt;
</code></pre><p>“Link”를 클릭하면 결과는 다음과 같습니다.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2020/10/image-119.png" class="kg-image" alt="콘솔 로그와 alert창이 뜬 브라우저 스크린샷" width="600" height="400" loading="lazy"></figure><p>위에서 본 것처럼, 브라우저는 <code>href</code>를 참조된 경로로 취급하지 않습니다. 대신 “javascript:” 이후부터 쌍반점(;)을 기준으로 나눠서 각각의 자바스크립트의 코드로 처리합니다.</p><h3 id="void-0-"><code>void(0)</code></h3><p>void 연산자는 주어진 표현식을 평가하고 <code>undefined</code>를 반환합니다.</p><p>예를 들어,</p><pre><code class="language-javascript">const result = void (1 + 1);
console.log(result);
// undefined
</code></pre><p><code>1 + 1</code>의 연산은 실행이 되지만 <code>undefined</code>가 반환됩니다. 또 다른 예제를 통해 확인해보겠습니다.</p><pre><code class="language-html">&lt;body&gt;
  &lt;h1&gt;Heading&lt;/h1&gt;
  &lt;script&gt;
    void ((document.body.style.backgroundColor = "red"),
    (document.body.style.color = "white"));
  &lt;/script&gt;
&lt;/body&gt;
</code></pre><p>위 코드의 결과는 다음과 같습니다.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2020/10/image-122.png" class="kg-image" alt="빨간 배경의 하얀 글자 사진" width="600" height="400" loading="lazy"></figure><p>다음은 또 다른 예시입니다.</p><pre><code class="language-javascript">console.log(void 0 === undefined);
// true
</code></pre><h3 id="javascript-void-0--1"><code>javascript:</code>와 <code>void(0)</code>의 결합</h3><p>때에 따라 링크를 클릭했을 때, 다른 페이지로 이동하거나 페이지가 다시 로딩되는 것을 원하지 않을 때가 있습니다. <code>javascript:</code>을 사용하면 현재 페이지에 아무런 변화 없이 코드를 실행할 수 있습니다.</p><p><code>void(0)</code>를 사용하는 것은 <strong>아무것도</strong> 하지 말라는 뜻입니다. 즉, 다시 로딩하지 않고, 경로를 이동하지 않고, 어떤 코드도 실행하지 말라는 것과 같습니다.</p><p>예를 들어,</p><pre><code class="language-html">&lt;a href="javascript:void(0)"&gt;Link&lt;/a&gt;
</code></pre><p>“Link”라는 단어는 브라우저에서 링크로 처리됩니다. 예를 들어 포커스를 줄 수는 있지만 “Link”를 클릭하더라도 새 페이지로 이동하지 않습니다.</p><p><code>0</code>은 <code>void</code>에게 전달된 인수(Arguments)이며, 아무것도 하지 않고, 아무것도 반환하지 않습니다.</p><p>위에서 보다시피, 자바스크립트 코드 또한 <code>void</code>메서드에 인수로 전달될 수 있습니다. 이렇게 하면 링크 요소가 코드를 실행할 수 있지만 여전히 동일한 페이지를 유지합니다.</p><p>예를 들어:</p><pre><code class="language-html">&lt;a
  id="link"
  href="javascript:void(
document.querySelector('#link').style.color = 'green'
)"
  &gt;Link&lt;/a
&gt;
</code></pre><p>“Link” 버튼을 클릭하면 다음과 같은 결과가 나타납니다.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2020/10/image-121.png" class="kg-image" alt="초록색의 Link 글자" width="600" height="400" loading="lazy"></figure><p><code>void</code>를 사용하면 브라우저에 아무것도 반환하지 말라고 (또는 <code>undefined</code>를 반환하라고) 지시합니다.</p><p><code>javascript:void(0)</code>를 사용한 또 다른 사례는, 때때로 <code>&lt;a&gt;</code>태그를 사용해 링크를 클릭했을 때, 자바스크립트의 코드를 실행하고 싶지만, 경로를 바꾸는 게 불필요한 경우가 있습니다. 이럴 때, 실행하고 싶은 표현식을 <code>void</code>에 인수로 전달하면 됩니다.</p><h1 id="-">결론</h1><p>이 짧은 글에서 우리는 <code>void</code>연산자가 무엇인지, 어떻게 작동하는지, <code>&lt;a&gt;</code>태그 속성(Attributes)의 <code>href</code>에서 가상 URL인 <code>javascript:</code>가 어떻게 사용되는지 알아봤습니다.</p><p>이것은 클릭 시 페이지가 다른 페이지로 이동하거나 현재 페이지를 다시 로딩하지 않는 확실한 방법입니다.</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 자바스크립의 콜백 함수 – 자바스크립트에서 콜백 함수가 무엇이고 어떻게 사용하는지 알아봅시다 ]]>
                </title>
                <description>
                    <![CDATA[ 여러분이 프로그래밍과 익숙하시다면, 함수가 무엇이고 어떻게 사용하는지 알고 계실 겁니다. 그러나 콜백 함수가 뭔지 정확히 알고 있나요? 콜백 함수는 자바스크립트에서 중요한 파트 중 하나이고 콜백 함수가 어떻게 작동하는지 알고 계신다면 자바스크립트를 훨씬 더 잘 다룰 수 있게 될 것입니다. 그래서 이번 글에서는 콜백 함수가 무엇인지 그리고 몇 가지 예제를 통해 ]]>
                </description>
                <link>https://www.freecodecamp.org/korean/news/https-www-freecodecamp-org-news-javascript-callback-functions-what-are-callbacks-in-js-and-how-to-use-them/</link>
                <guid isPermaLink="false">63c2d20fa06cec064ac5d46c</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Yerim Kang ]]>
                </dc:creator>
                <pubDate>Thu, 19 Jan 2023 06:48:29 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/korean/news/content/images/2023/01/caspar-camille-rubin-7SDoly3FV_0-unsplash-1.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>기사 원문:</strong> <a href="https://www.freecodecamp.org/news/javascript-callback-functions-what-are-callbacks-in-js-and-how-to-use-them/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">JavaScript Callback Functions – What are Callbacks in JS and How to Use&nbsp;Them</a>
      </p><p>여러분이 프로그래밍과 익숙하시다면, 함수가 무엇이고 어떻게 사용하는지 알고 계실 겁니다. 그러나 콜백 함수가 뭔지 정확히 알고 있나요? 콜백 함수는 자바스크립트에서 중요한 파트 중 하나이고 콜백 함수가 어떻게 작동하는지 알고 계신다면 자바스크립트를 훨씬 더 잘 다룰 수 있게 될 것입니다.</p><p>그래서 이번 글에서는 콜백 함수가 무엇인지 그리고 몇 가지 예제를 통해 자바스크립트에서 어떻게 동작하는지를 알려드리려고 합니다.</p><h2 id="-"><strong>콜백 함수가 무엇인가요?</strong></h2><p>자바스크립트에서는 함수는 객체입니다. 함수의 파라미터로서 객체를 전달할 수 있을까요? 네, 할 수 있습니다.</p><p>그래서 우리는 함수의 파라미터로서 다른 함수에 전달하고 감싼 함수의 내부에서 그 함수를 호출합니다. 헷갈리신다고요? 아래에 예시를 보여드리겠습니다.</p><pre><code>function print(callback) {
    callback();
}
</code></pre><p>print() 함수는 다른 함수를 파라미터로 받아서 내부에서 그것을 호출하고 있습니다. 자바스크립트에서는 가능합니다. 그리고 이걸 우리는 “콜백”이라고 하기로 했어요. 다른 함수의 파라미터로써 전달되는 함수를 콜백 함수라고 합니다. 그러나 그게 다는 아니에요.</p><p>아래 비디오에서 콜백 함수의 종류를 시청하실 수 있습니다.</p><h3 id="--1"><strong>콜백 함수가 왜 필요한가요?</strong></h3><p>자바스크립트는 코드를 위에서 아래로 순차적으로 실행합니다. 그러나, 코드가 다른 행위가 일어난 뒤에 실행되는 경우도 있고 순차적으로 실행되지 않을 때도 있습니다. 이런 걸 비동기 프로그래밍이라고 합니다.</p><p>콜백은 태스크가 끝나기 전에 함수가 실행되지 않는 것을 보장합니다. 다르게 말하자면 콜백은 그 태스크가 끝난 직후에 실행될 것입니다. 콜백은 비동기 자바스크립트 코드를 작성할 수 있도록 해주고 여러 문제와 에러들로부터 안전하게 지켜줍니다.</p><p>자바스크립트에서 콜백 함수를 만드는 방법은 어떤 함수의 파라미터로써 함수를 넘기고 어떤 행위나 태스크가 완료된 직후에 콜백 함수를 호출하는 것입니다.</p><h2 id="--2"><strong>콜백을 만드는 방법</strong></h2><p>위에 설명한 내용들을 이해하기 위해, 간단한 예제로 시작해 보겠습니다. 콘솔에 로그를 찍어보려고 하는데, 이것은 꼭 3초 뒤에 출력되어야 합니다.</p><pre><code>const message = function() {
    console.log("This message is shown after 3 seconds");
}

setTimeout(message, 3000);
</code></pre><p>setTimeout 함수는 자바스크립트에 내장된 함수입니다. 이 함수는 주어진 시간(밀리세컨드 단위) 이후에 함수를 호출하거나 표현식을 평가합니다. 그래서 여기 message 함수는 3초가 지난 후에 호출됩니다. (1초 = 1000 밀리세컨드)</p><p>다르게 이야기하자면, message 함수는 어떤 일이 일어나기 전이 아니라 뒤에(여기서는 3초가 지난 후에) 호출됩니다. 그래서 이 message 함수는 콜백 함수의 예시라고 할 수 있죠.</p><h3 id="--3"><strong>비동기 함수가 무엇인가요?</strong></h3><p>다르게는 비동기 함수를 다른 함수에 호출하는 방법 말고 함수 내부에 직접적으로 정의하는 방법도 있습니다. 이런 방법은 아래와 같습니다.</p><pre><code>setTimeout(function() {
    console.log("This message is shown after 3 seconds");
}, 3000);
</code></pre><p>보시다시피 여기서 콜백 함수는 이름이 없고 자바스크립트에서 이름이 없는 함수 정의를 "익명 함수"라고 합니다. 이것은 위의 샘플과 정확하게 똑같이 작동합니다.</p><h3 id="--4"><strong>화살표 함수 모양의 콜백</strong></h3><p>사용자의 선호에 따라 똑같은 콜백 함수를 ES6 화살표 함수로 사용할 수 있습니다. 화살표 함수는 자바스크립트 내에서 새로운 타입의 함수 표현입니다.</p><pre><code>setTimeout(() =&gt; {
    console.log("This message is shown after 3 seconds");
}, 3000);
</code></pre><h2 id="--5"><strong>이벤트 처리는요?</strong></h2><p>자바스크립트는 이벤트 기반 프로그래밍 언어입니다. 콜백 함수를 이벤트 선언을 위해 사용하기도 합니다. 예를 들어 사용자가 버튼을 클릭하기를 바란다고 해봅시다.</p><pre><code>&lt;button id="callback-btn"&gt;Click here&lt;/button&gt;
</code></pre><p>이번에는 사용자가 버튼을 클릭했을 때 콘솔에 메시지를 남겨 보도록 하겠습니다.</p><pre><code>document.queryselector("#callback-btn")
    .addEventListener("click", function() {
      console.log("User has clicked on the button!");
});
</code></pre><p>첫 번째로 버튼의 id 값을 사용하여 버튼을 선택하고 addEventListener 메소드를 사용하여 이벤트 리스너를 추가했습니다. 이벤트 리스너 함수는 두 개의 파라미터를 필요로 합니다. 첫 번째 파라미터로는 이벤트의 타입인 “클릭”이고 두 번째 파라미터는 버튼이 클릭되었을 때 메시지를 남기는 콜백 함수입니다.</p><p>보기와 같이, 콜백 함수는 자바스크립트에서 이벤트 정의를 위해 사용되기도 합니다.</p><h2 id="--6"><strong>마무리</strong></h2><p>콜백 함수는 자바스크립트에서 종종 사용됩니다. 이 포스트가 콜백 함수가 정확히 어떤 방식으로 작동하고 더 쉽게 사용하는 방법들을 이해하는 데 도움이 되었으면 합니다. 다음에는 비슷한 주제인 자바스크립트 프로미스에 대해서 새로운 포스트로 배워보실 수 있도록 하겠습니다.</p><p>웹 개발에 대해 더 알고 싶으시다면, 제 유튜브를 팔로우 해주셔도 좋습니다!</p><p>글 읽어주셔서 감사합니다!</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Var, Let, Const의 차이점은? ]]>
                </title>
                <description>
                    <![CDATA[  ES2015(ES6)에서 반짝이는 새로운 기능들이 많이 등장했습니다. 2020년이 된 지금, 많은 JavaScript 개발자들이 그 기능들을 사용하기 시작했을 것이며 또 익숙해졌을텐데요. 여전히 그 중 몇몇은 일부 개발자들에게 미스터리로 남아있을 수 있습니다. ES6에 포함된 기능 중 하나는 변수 선언에 사용할 수 있는 let 및 const의 추가입니다. 지금껏 애용해온 var와 다른 점은 무엇일까요? ]]>
                </description>
                <link>https://www.freecodecamp.org/korean/news/var-let-constyi-caijeomeun/</link>
                <guid isPermaLink="false">63bd4d078b28df063f433e18</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ YOUNGHYUN BAE ]]>
                </dc:creator>
                <pubDate>Thu, 12 Jan 2023 06:54:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/korean/news/content/images/2023/01/5f9c9bd4740569d1a4ca2e24.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>기사 원문:</strong> <a href="https://www.freecodecamp.org/news/var-let-and-const-whats-the-difference/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">Var, Let, and Const – What's the Difference?</a>
      </p><p></p><p>ES2015(ES6)에서 반짝이는 새로운 기능들이 많이 등장했습니다. 2020년이 된 지금, 많은 JavaScript 개발자들이 그 기능들을 사용하기 시작했을 것이며 또 익숙해졌을텐데요. 여전히 그 중 몇몇은 일부 개발자들에게 미스터리로 남아있을 수 있습니다.</p><p><br>ES6에 포함된 기능 중 하나는 변수 선언에 사용할 수 있는 <code>let</code> 및 <code>const</code>의 추가입니다. 지금껏 애용해온 <code>var</code>와 다른 점은 무엇일까요? 이 글은 여전히 명확히 이해하지 못 한 당신을 위한 것입니다.</p><h2 id="var"><strong><strong>Var</strong></strong></h2><p>ES6의 등장 이전에는 문제점들이 있음에도 불구하고 <code>var</code>로 변수를 선언하는 것이 지배적이었는데요. 따라서 새로운 변수 선언 방식이 등장할 수밖에 없었습니다. 우선, 문제점에 대해 논의하기 전에 <code>var</code> 자체에 대해 더 이해해봅시다.</p><h3 id="scope-of-var"><strong><strong>Scope of var</strong></strong></h3><p><strong>범위</strong>는 기본적으로 변수를 사용할 수 있는 위치를 의미합니다. <code>var</code> 선언은 전역 범위 혹은 함수 범위로 지정됩니다.</p><p><br><code>var</code>변수가 함수 외부에서 선언될 때의 범위는 전역입니다. 즉, 함수 블록 외부에서 <code>var</code>를 사용하여 선언된 모든 변수를 전체 윈도우 상에서 사용할 수 있는 것이죠.</p><p><br><code>var</code>가 함수 내에서 선언될 때는 함수 범위로 지정됩니다. 즉, 해당 함수 내에서만 사용하고 접근할 수 있습니다.</p><p><br>자세한 이해를 돕기위해 아래의 예제를 살펴봅시다.</p><pre><code class="language-javascript">    var greeter = "hey hi";
    
    function newFunction() {
        var hello = "hello";
    }
</code></pre><p>여기서 <code>hello</code>가 함수 범위인 반면에, <code>greeter</code>는 함수 밖에 존재하므로 전역 범위를 가지게 됩니다.</p><pre><code class="language-javascript">    var tester = "hey hi";
    
    function newFunction() {
        var hello = "hello";
    }
    console.log(hello); // error: hello is not defined
</code></pre><p><code>hello</code>는 <code>newFunction()</code> 함수 밖에서 사용할 수 없기 때문에 에러가 발생하게 됩니다.</p><h3 id="var-">var 변수는 재선언되고, 업데이트될 수 있습니다.</h3><p>같은 범위 내에서라면 아래의 두 경우와 같이 수정이 가능하며 에러가 발생하지 않습니다.</p><pre><code class="language-javascript">    var greeter = "hey hi";
    var greeter = "say Hello instead";
</code></pre><pre><code class="language-javascript">    var greeter = "hey hi";
    greeter = "say Hello instead";
</code></pre><h3 id="var--1">var의 호이스팅</h3><p>호이스팅이란 변수와 함수 선언이 맨 위로 이동되는 자바스크립트 매커니즘인데요. 아래와 같이 코드를 짜면:</p><pre><code class="language-javascript">    console.log (greeter);
    var greeter = "say hello"
</code></pre><p>자바스크립트는 다음과 같이 해석하기 때문에:</p><pre><code class="language-javascript">    var greeter;
    console.log(greeter); // greeter is undefined
    greeter = "say hello"
</code></pre><p><code>var</code>변수는 범위 내에서 맨 위로 올려지고, 값은 <code>undefined(정의되지 않음)</code>으로 초기화됩니다.</p><h3 id="var--2">var의 문제점</h3><p>아래의 예제를 토대로 <code>var</code>를 사용할 시 따라오는 취약점에 대해 이야기해보겠습니다.</p><pre><code class="language-javascript">    var greeter = "hey hi";
    var times = 4;

    if (times &gt; 3) {
        var greeter = "say Hello instead"; 
    }
    
    console.log(greeter) // "say Hello instead"
</code></pre><p><code>time &gt; 3</code>가 <code>true</code>를 반환하기 때문에 <code>greeter</code>는 <code>"say Hello instead"</code>로 재정의됩니다. 의도적으로 재정의한 것이라면 괜찮겠지만, 변수 <code>greeter</code>가 이미 정의되어 있다는 사실을 인식하지 못한 경우에는 문제가 됩니다.</p><p>만약 코드의 다른 부분에서 <code>greeter</code>를 사용한 적이 있다면 뜻밖의 출력 결과에 놀랄 수 있습니다. 그리고 많은 버그를 발생시킬 수 있기 때문에 <code>let</code>과 <code>const</code>가 필요하게 된 것이죠.</p><h2 id="let"><strong><strong>Let</strong></strong></h2><p><code>var</code> 선언에 대한 개선을 반영한 <code>let</code>이 현재 변수 선언에서 선호되고 있습니다. 방금 다뤘던 <code>var</code>의 문제점을 해결할 수 있었던 이유에 대해 살펴봅시다.</p><h3 id="-let">블록 범위 let</h3><p>블록은 <code>{}</code>로 바인딩된 코드 청크인데요. 하나의 블록은 중괄호 속에서 존재하며, 중괄호 안에 있는 것은 모두 블록 범위입니다.</p><p><code>let</code>으로 선언된 변수는 해당 블록 내에서만 사용가능합니다. 예를 들어 설명하자면:</p><pre><code class="language-javascript">   let greeting = "say Hi";
   let times = 4;

   if (times &gt; 3) {
        let hello = "say Hello instead";
        console.log(hello);// "say Hello instead"
    }
   console.log(hello) // hello is not defined
</code></pre><p>중괄호로 감싸진 <code>hello</code> 변수가 정의된 블록 외부에서 <code>helllo</code>를 사용했더니 에러가 반환되는 것을 확인할 수 있습니다. <code>let</code> 변수는 블록 범위이기 때문이죠.</p><h3 id="let-">let은 업데이트될 수 있지만, 재선언은 불가능하다.</h3><p><code>var</code>와 마찬가지로 <code>let</code>으로 선언된 변수는 해당 범위 내에서 업데이트될 수 있습니다. 하지만, <code>var</code>와 달리 <code>let</code> 변수는 범위 내에서 다시 선언할 수 없습니다. 아래의 기능이 작동하는 동안:</p><pre><code class="language-javascript">    let greeting = "say Hi";
    greeting = "say Hello instead";
</code></pre><p>이와 같은 에러가 발생합니다:</p><pre><code class="language-javascript">    let greeting = "say Hi";
    let greeting = "say Hello instead"; // error: Identifier 'greeting' has already been declared
</code></pre><p>그러나 동일한 변수가 다른 범위 내에서 정의된다면, 에러는 더 이상 발생하지 않습니다.</p><pre><code class="language-javascript">    let greeting = "say Hi";
    if (true) {
        let greeting = "say Hello instead";
        console.log(greeting); // "say Hello instead"
    }
    console.log(greeting); // "say Hi"
</code></pre><p>오류가 없는 이유는 무엇일까요? 두 예제가 서로 다른 범위를 가지므로 서로 다른 변수로 취급되기 때문입니다. 따라서 <code>var</code>보다 <code>let</code>이 더 나은 선택이 될 수 있는 것이죠. <code>let</code>을 사용하는 경우라면, 변수가 범위 내에서만 존재하기 때문에 이전에 이미 사용한 적이 있는 변수 명에 대해서 더 이상 신경쓰지 않아도 좋습니다.또한, 범위 내에서 동일한 변수를 두 번 이상 선언할 수 없기 때문에 앞서 설명한 <code>var</code>의 문제가 발생하지 않습니다.</p><h3 id="let--1">let의 호이스팅</h3><p><code>var</code>와 마찬가지로 <code>let</code> 선언은 맨 위로 끌어올려집니다. <code>undefined(정의되지 않음)</code>으로 초기화되는 <code>var</code>와 다르게 <code>let</code>의 키워드는 초기화되지 않습니다. 선언 이전에 <code>let</code> 변수를 사용하려고 시도한다면 <code>Reference Error(참조 오류)</code>가 발생할 것입니다.</p><h2 id="const"><strong><strong>Const</strong></strong></h2><p><code>const</code>로 선언된 변수는 일정한 상수 값을 유지합니다. <code>const</code> 선언은 <code>let</code> 선언과 몇 가지 유사점을 공유합니다.</p><h3 id="-const">블록 범위 const</h3><p><code>let</code> 선언처럼 <code>const</code> 선언도 선언된 블록 범위 내에서만 접근 가능합니다.</p><h3 id="const-">const는 업데이트도, 재선언도 불가능하다</h3><p><code>const</code>로 선언된 변수의 값이 해당 범위 내에서 동일하게 유지됨을 의미합니다. 업데이트하거나 다시 선언할 수가 없는 것이죠. <code>const</code>로 변수를 선언한 경우에는 다음과 같은 두 작업을 수행할 수 없습니다:</p><pre><code class="language-javascript">    const greeting = "say Hi";
    greeting = "say Hello instead";// error: Assignment to constant variable. 
</code></pre><pre><code class="language-javascript">    const greeting = "say Hi";
    const greeting = "say Hello instead";// error: Identifier 'greeting' has already been declared
</code></pre><p>따라서 모든 <code>const</code> 선언은 선언하는 당시에 초기화되어야 합니다.</p><p>개체의 경우는 다소 다른 점이 있는데요. <code>const</code> 개체는 업데이트할 수 없지만, 개체의 '속성'은 업데이트할 수 있습니다. <code>const</code> 객체를 다음과 같이 선언했다면:</p><pre><code class="language-javascript">    const greeting = {
        message: "say Hi",
        times: 4
    }
</code></pre><p>아래와 같은 작업은 불가능하지만:</p><pre><code class="language-javascript">    greeting = {
        words: "Hello",
        number: "five"
    } // error:  Assignment to constant variable.
</code></pre><p>다음의 코드는 가능합니다:</p><pre><code class="language-javascript">    greeting.message = "say Hello instead";
</code></pre><p>오류를 반환하지 않고 <code>greeting.message</code> 값이 업데이트됩니다.</p><h3 id="const--1">const의 호이스팅</h3><p><code>let</code>과 마찬가지로 <code>const</code> 선언도 맨 위로 끌어올려지지만, 초기화되지는 않습니다.</p><p>세 가지 변수 선언법의 차이점에 대해서 총정리하자면:</p><ul><li><code>var</code> 선언은 전역 범위 또는 함수 범위이며, <code>let</code>과 <code>const</code>는 블록 범위이다.</li><li><code>var</code> 변수는 범위 내에서 업데이트 및 재선언할 수 있다. <code>let</code> 변수는 업데이트할 수 있지만, 재선언은 할 수 없다. <code>const</code> 변수는 업데이트와 재선언 둘 다 불가능하다.</li><li>세 가지 모두 최상위로 호이스팅된다. 하지만 <code>var</code> 변수만 <code>undefined(정의되지 않음)</code>으로 초기화되고 <code>let</code>과 <code>const</code> 변수는 초기화되지 않는다.</li><li><code>var</code>와 <code>let</code>은 초기화하지 않은 상태에서 선언할 수 있지만, <code>const</code>는 선언 중에 초기화해야한다.</li></ul><h2 id="var-let-const-">Var, Let, Const에 대한 설명 영상</h2><figure class="kg-card kg-embed-card"><iframe src="https://scrimba.com/scrim/coede4c58a4461640298cc925?embed=freecodecamp,mini-header,no-sidebar" width="100%" height="420" title="Embedded content" loading="lazy" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 19px; vertical-align: middle;"></iframe></figure><p><br>이게 전부랍니다. 질문 혹은 추가해야할 내용이 있다면 알려주세요.</p><p>읽어주셔서 감사합니다 :)</p><p><br></p><p><br></p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 자바스크립트에서 문자열을 숫자로 변환하는 방법 ]]>
                </title>
                <description>
                    <![CDATA[  자바스크립트에서 문자열을 숫자로 변환하는 방법은 여러 가지가 있습니다. 코드로는 어떻게 생겼을까요? 이번 글에서는 문자열을 숫자로 변환하는 11가지 방법을 소개합니다. Number() 함수를 이용해 문자열을 숫자로 변환하는 방법 문자열을 숫자로 변환하는 한 가지 방법은 Number() 함수를 사용하는 것입니다.아래 예시에 "12"라는 문자열 값을 가진 quantity가 있습니다. const quantity = "12"; quantity를 typeof ]]>
                </description>
                <link>https://www.freecodecamp.org/korean/news/how-to-convert-a-string-to-a-number-in-javascript/</link>
                <guid isPermaLink="false">63a5072d6ff1bc0647f6d34e</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Janghan Park ]]>
                </dc:creator>
                <pubDate>Fri, 23 Dec 2022 07:28:23 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/korean/news/content/images/2022/12/thumb.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>기사 원문:</strong> <a href="https://www.freecodecamp.org/news/how-to-convert-a-string-to-a-number-in-javascript/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Convert a String to a Number in JavaScript</a>
      </p><p></p><h3 id="-">자바스크립트에서 문자열을 숫자로 변환하는 방법은 여러 가지가 있습니다. 코드로는 어떻게 생겼을까요?</h3><p><br>이번 글에서는 문자열을 숫자로 변환하는 11가지 방법을 소개합니다.</p><h2 id="number-"><code>Number()</code> <b>함수를 이용해 문자열을 숫자로 변환하는 방법</b></h2><p>문자열을 숫자로 변환하는 한 가지 방법은 <code>Number()</code> 함수를 사용하는 것입니다.아래 예시에 <code>"12"</code>라는 문자열 값을 가진 <code>quantity</code>가 있습니다.</p><p></p><pre><code class="language-js">const quantity = "12";
</code></pre><p></p><p><code>quantity</code>를 <code>typeof</code> 연산자를 통해 실행해 보면 문자열 타입을 반환합니다.</p><pre><code class="language-js">console.log(typeof quantity);
</code></pre><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/05/Screen-Shot-2022-05-01-at-9.50.17-AM.png" class="kg-image" alt="Screen-Shot-2022-05-01-at-9.50.17-AM" width="600" height="400" loading="lazy"></figure><p></p><p>아래 예시처럼 <code>Number</code> 함수를 이용해 <code>quantitiy</code>를 변환할 수 있습니다.</p><pre><code class="language-js">Number(quantity);
</code></pre><p></p><p>다시 <code>typeof</code> 연산자를 이용해 이제는 숫자 타입이라는 것을 확인할 수 있습니다.</p><pre><code class="language-js">console.log(typeof Number(quantity));
</code></pre><p><br>숫자로 변환될 수 없는 값을 넘겨준다면 <code>NaN</code>(Not a Number)를 반환합니다.</p><pre><code class="language-js">console.log(Number("awesome"));
</code></pre><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/05/Screen-Shot-2022-05-01-at-10.00.34-AM.png" class="kg-image" alt="Screen-Shot-2022-05-01-at-10.00.34-AM" width="600" height="400" loading="lazy"></figure><p><br><br></p><h2 id="parseint-"><code>parseInt()</code> <b>함수를 이용해 문자열을 숫자로 변환하는 방법</b></h2><p><br>문자열을 숫자로 변환하는 또 다른 방법으로는 <code>parseInt()</code> 함수를 이용하는 것입니다. 이 함수는 문자열과 기수(radix, base)를 인자로 받습니다.</p><p>기수는 수의 진법 체계에서 기준이 되는 값을 나타내는 2부터 36까지의 정수입니다. 예를 들어 기수를 2로 하면 2진수가 되고, 기수 값을 10으로 하면 10진수가 됩니다.</p><p>문자열을 숫자로 변한 해보기 위해 위에 예제에서 사용했던 <code>quantity</code>를 사용해 보겠습니다.</p><pre><code class="language-js">const quantity = "12";
console.log(parseInt(quantity, 10));
</code></pre><p></p><p><code>quantity</code> 변수를 <code>"12.99"</code>로 바꾸면 무슨 일이 일어날까요? <code>parseInt()</code> 가 그대로 숫자 12.99를 반환할까요?<br></p><pre><code class="language-js">const quantity = "12.99";
console.log(parseInt(quantity, 10));
</code></pre><p></p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/05/Screen-Shot-2022-05-01-at-10.45.08-AM.png" class="kg-image" alt="Screen-Shot-2022-05-01-at-10.45.08-AM" width="600" height="400" loading="lazy"></figure><p><br>위에서 볼 수 있듯이 결과는 어림한 정수입니다. 만약 반환값으로 실수를 받고 싶다면 <code>parseFloat()</code>를 사용하면 됩니다.</p><p></p><h2 id="parsefloat-"><code>parseFloat()</code> <b>함수를 이용해 문자열을 숫자로 변환하는 방법</b></h2><p><br><code>parseFloat</code> 함수는 값을 받아 실수를 반환합니다. 실수의 예로는 12.99 또는 3.14와 같은 것들이 있습니다.</p><p></p><p><code>parseFloat()</code>를 이용해 위의 예제를 수정하면 실수인 12.99가 반환됩니다.<br></p><pre><code class="language-js">const quantity = "12.99";
console.log(parseFloat(quantity));
</code></pre><p></p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/05/Screen-Shot-2022-05-01-at-10.55.03-AM.png" class="kg-image" alt="Screen-Shot-2022-05-01-at-10.55.03-AM" width="600" height="400" loading="lazy"></figure><p><br></p><p>문자열 앞 혹은 뒤에 공백이 있어도 <code>parseFloat()</code>는 이전과 같이 문자열을 실수로 변환합니다.<br></p><pre><code class="language-js">const quantity = "   12.99    ";
console.log(parseFloat(quantity));
</code></pre><p><br></p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/05/Screen-Shot-2022-05-01-at-11.05.35-AM.png" class="kg-image" alt="Screen-Shot-2022-05-01-at-11.05.35-AM" width="600" height="400" loading="lazy"></figure><p></p><p>문자열의 첫 글자가 숫자로 변환할 수 없는 경우 <code>NaN</code>을 반환합니다.</p><pre><code class="language-js">const quantity = "F12.99";
console.log(parseFloat(quantity));
</code></pre><p><br></p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/05/Screen-Shot-2022-05-01-at-11.08.33-AM.png" class="kg-image" alt="Screen-Shot-2022-05-01-at-11.08.33-AM" width="600" height="400" loading="lazy"></figure><p><br></p><h2 id="--1">(<code>+</code>) <b>단항 더하기 연산자를 이용해 문자열을 숫자로 변환하는 방법</b></h2><p></p><p>단항 더하기 연산자(<code>+</code>)는 문자열을 숫자로 반환하며 피연산자 앞에 위치합니다.</p><pre><code class="language-js">const quantity = "12";
console.log(+quantity);
</code></pre><p></p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/05/Screen-Shot-2022-05-01-at-11.14.51-AM.png" class="kg-image" alt="Screen-Shot-2022-05-01-at-11.14.51-AM" width="600" height="400" loading="lazy"></figure><p>단항 더하기 연산자(<code>+</code>)는 문자열을 실수로 반환하는 데에도 사용될 수 있습니다.</p><pre><code class="language-js">const quantity = "12.99";
console.log(+quantity);
</code></pre><p></p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/05/Screen-Shot-2022-05-01-at-11.16.38-AM.png" class="kg-image" alt="Screen-Shot-2022-05-01-at-11.16.38-AM" width="600" height="400" loading="lazy"></figure><p></p><p>문자열이 숫자로 변환될 수 없는 경우에는 <code>NaN</code>을 반환합니다.</p><p></p><pre><code class="language-js">const quantity = "awesome";
console.log(+quantity);
</code></pre><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/05/Screen-Shot-2022-05-01-at-11.18.10-AM.png" class="kg-image" alt="Screen-Shot-2022-05-01-at-11.18.10-AM" width="600" height="400" loading="lazy"></figure><p></p><h2 id="-1-"><b>문자열에 곱하기 1을 하여 문자열을 숫자로 변환하는 방법</b></h2><p><br></p><p>문자열을 숫자로 변환하는 다른 방법으로는 기본 수학 연산을 이용하는 것입니다. 문자열에 1을 곱하면 숫자를 반환받게 됩니다.</p><pre><code class="language-js">const quantity = "12";
console.log(quantity * 1);
</code></pre><p><br></p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/05/Screen-Shot-2022-05-01-at-11.42.58-AM.png" class="kg-image" alt="Screen-Shot-2022-05-01-at-11.42.58-AM" width="600" height="400" loading="lazy"></figure><p><br></p><p>결과에서 알 수 있듯이 <code>quantity</code> 값에 1을 곱하면 정수 12를 반환합니다. 하지만 어떻게 가능한 것일까요?</p><p></p><p>아래 예제에서 자바스크립트는 문자열 값을 숫자로 변환하고 수학 연산을 수행합니다. 그러나 문자열이 숫자로 변환될 수 없는 경우에는 수학 연산을 하지 않고 <code>NaN</code>을 반환하게 됩니다.</p><p><br></p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/05/Screen-Shot-2022-05-01-at-11.18.10-AM.png" class="kg-image" alt="Screen-Shot-2022-05-01-at-11.18.10-AM" width="600" height="400" loading="lazy"></figure><p></p><p>이와 같은 방법은 실수에도 똑같이 적용됩니다.</p><pre><code class="language-js">const quantity = "10.5";
console.log(quantity * 1);
</code></pre><p></p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/05/Screen-Shot-2022-05-01-at-11.56.19-AM.png" class="kg-image" alt="Screen-Shot-2022-05-01-at-11.56.19-AM" width="600" height="400" loading="lazy"></figure><p><br></p><h2 id="-1--1"><b>문자열에 나누기 1을 하여 문자열을 숫자로 변환하는 방법</b></h2><p>곱하기 1을 하는 대신 문자열에 나누기 1을 할 수 있습니다. 자바스크립트는 문자열을 숫자로 변환하여 수학 연산을 수행합니다.</p><pre><code class="language-js">const quantity = "10.5";
console.log(quantity / 1);
</code></pre><p></p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/05/Screen-Shot-2022-05-01-at-12.08.37-PM.png" class="kg-image" alt="Screen-Shot-2022-05-01-at-12.08.37-PM" width="600" height="400" loading="lazy"></figure><p><br></p><h2 id="-0-"><b>문자열에 빼기 0을 하여 문자열을 숫자로 변환하는 방법</b></h2><p><br>문자열에 0을 빼는 것도 한 가지 방법이 될 수 있습니다. 이전처럼 자바스크립트는 문자열을 숫자로 변환 후 수학 연산을 수행합니다.</p><pre><code class="language-js">const quantity = "19";
console.log(quantity - 0);
</code></pre><p></p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/05/Screen-Shot-2022-05-01-at-12.11.59-PM.png" class="kg-image" alt="Screen-Shot-2022-05-01-at-12.11.59-PM" width="600" height="400" loading="lazy"></figure><p></p><h2 id="--2"><b>비트 부정 연산자(<code>~</code>)를 이용해 문자열을 숫자로 변환하는 방법</b></h2><p></p><p>비트 부정 연산자(<code>~</code>)는 피연산자의 비트를 뒤집습니다. 그리고 난 후 부호가 있는 32비트 정수로 변환합니다. 부호가 있는 32비트 정수란 32비트(4바이트) 안에서 표현할 수 있는 값을 나타냅니다.</p><p>하나의 비트 부정 연산자(<code>~</code>)를 숫자에 사용하면 -(x + 1) 연산을 실행하게 됩니다.</p><pre><code class="language-js">console.log(~19);
</code></pre><p></p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/05/Screen-Shot-2022-05-01-at-12.20.18-PM.png" class="kg-image" alt="Screen-Shot-2022-05-01-at-12.20.18-PM" width="600" height="400" loading="lazy"></figure><p></p><p>하지만 두 개의 비트 부정 연산자(<code>~</code>)를 사용하면 문자열을 숫자로 변환합니다.</p><pre><code class="language-js">const quantity = "19";
console.log(~~quantity);
</code></pre><p><br></p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/05/Screen-Shot-2022-05-01-at-12.28.16-PM.png" class="kg-image" alt="Screen-Shot-2022-05-01-at-12.28.16-PM" width="600" height="400" loading="lazy"></figure><p><br>위와 같은 방법은 실수에는 적용되지 않습니다. 왜냐하면 결괏값으로 정수를 반환하기 때문입니다.</p><pre><code class="language-js">const quantity = "19.99";
console.log(~~quantity);
</code></pre><p><br></p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/05/Screen-Shot-2022-05-01-at-12.31.16-PM.png" class="kg-image" alt="Screen-Shot-2022-05-01-at-12.31.16-PM" width="600" height="400" loading="lazy"></figure><p><br></p><p>만약 숫자가 아닌 문자에 사용할 경우 0을 반환하게 됩니다.</p><pre><code class="language-js">const quantity = "awesome";
console.log(~~quantity);
</code></pre><p><br></p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/05/Screen-Shot-2022-05-01-at-12.32.45-PM.png" class="kg-image" alt="Screen-Shot-2022-05-01-at-12.32.45-PM" width="600" height="400" loading="lazy"></figure><p><br>변수의 값이 너무 크면 연산이 실행되지 않는 한계가 있습니다. 그래서 부호가 있는 32비트 정수인지 확인하는 것이 중요합니다.</p><pre><code class="language-js">const quantity = "2700000000";
console.log(~~quantity);
</code></pre><p><br></p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/05/Screen-Shot-2022-05-01-at-12.36.16-PM.png" class="kg-image" alt="Screen-Shot-2022-05-01-at-12.36.16-PM" width="600" height="400" loading="lazy"></figure><p><br></p><p>이 <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_NOT">문서</a> 를 통해 비트 부정 연산자(<code>~</code>)에 대해 더 알아볼 수 있습니다.</p><h2 id="math-floor-"><code>Math.floor()</code> <b>함수를 이용해 문자열을 숫자로 변환하는 방법</b></h2><p><br><code>Math.floor()</code> 함수를 이용해 문자열을 숫자로 변환할 수 있습니다. 이 함수는 값에 버림을 하여 가장 가까운 정수를 반환합니다.</p><p></p><pre><code class="language-js">const quantity = "13.4";
console.log(Math.floor(quantity));
</code></pre><p></p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/05/Screen-Shot-2022-05-01-at-12.44.53-PM.png" class="kg-image" alt="Screen-Shot-2022-05-01-at-12.44.53-PM" width="600" height="400" loading="lazy"></figure><p><br>앞선 예제들과 같이 숫자가 아닌 문자를 경우 결괏값으로 NaN을 받습니다.</p><p></p><pre><code class="language-js">const quantity = "awesome";
console.log(Math.floor(quantity));
</code></pre><p></p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/05/Screen-Shot-2022-05-01-at-12.46.08-PM.png" class="kg-image" alt="Screen-Shot-2022-05-01-at-12.46.08-PM" width="600" height="400" loading="lazy"></figure><h2 id="math-ceil-"><code>Math.ceil()</code> <b>함수를 이용해 문자열을 숫자로 변환하는 방법</b></h2><p></p><p><code>Math.ceil()</code> 함수는 숫자를 가장 가까운 정수까지 올림합니다.</p><p></p><pre><code class="language-js">const quantity = "7.18";
console.log(Math.ceil(quantity));
</code></pre><p></p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/05/Screen-Shot-2022-05-01-at-12.48.15-PM.png" class="kg-image" alt="Screen-Shot-2022-05-01-at-12.48.15-PM" width="600" height="400" loading="lazy"></figure><p></p><h2 id="math-round-"><code>Math.round()</code> <b>함수를 이용해 문자열을 숫자로 변환하는 방법</b></h2><p></p><p><code>Math.round()</code> 함수는 가장 가까운 정수까지 반올림합니다.</p><p></p><p>값이 6.3이면 <code>Math.round()</code>는 6을 반환합니다.</p><p></p><pre><code class="language-js">const quantity = "6.3";
console.log(Math.round(quantity));
</code></pre><p></p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/05/Screen-Shot-2022-05-01-at-12.50.20-PM.png" class="kg-image" alt="Screen-Shot-2022-05-01-at-12.50.20-PM" width="600" height="400" loading="lazy"></figure><p><br>그러나 값을 6.5로 바꿀 경우에는 함수 <code>Math.round()</code>는 7을 반환합니다.</p><p></p><pre><code class="language-js">const quantity = "6.5";
console.log(Math.round(quantity));
</code></pre><p></p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2022/05/Screen-Shot-2022-05-01-at-12.51.35-PM.png" class="kg-image" alt="Screen-Shot-2022-05-01-at-12.51.35-PM" width="600" height="400" loading="lazy"></figure><p><br></p><h1 id="--3"><b>결론</b></h1><p></p><p>이번 글에서는 문자열을 숫자로 변환하는 11가지 방법을 소개해 드렸습니다.</p><p></p><p>윗글에서 언급된 11가지 다른 방법들입니다.</p><p><code>Number()</code> 함수 사용</p><p><code>parseInt()</code> 함수 사용</p><p><code>parseFloat()</code> 함수 사용</p><p>단항 더하기 연산자(<code>+</code>) 사용</p><p>문자열에 곱하기 1</p><p>문자열에 나누기 1</p><p>문자열에 숫자 0을 빼기</p><p>비트 부정 연산자(<code>~</code>) 사용</p><p><code>Math.floor()</code> 함수 사용</p><p><code>Math.ceil()</code> 함수 사용</p><p><code>Math.round()</code> 함수 사용</p><p></p><p>이 글을 재밌게 읽어주셨길 바라며 여러분의 자바스크립트 여정에 행운이 가득하길 희망합니다.</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Debounce(디바운스) - JavaScript에서 함수를 지연시키는 방법 (JS ES6 예제) ]]>
                </title>
                <description>
                    <![CDATA[ JavaScript에서 debounce(디바운스)는 유저가 입력할 때마다 코드를 오직 한 번씩만 실행되도록 해주는 함수입니다. 검색 박스의 제안, 텍스트 필드의 자동 저장, 버튼의 더블 클릭의 제거가 모두 debounce를 이용하는 사례입니다. 이번 튜토리얼에서는 JavaScript에서 어떻게 debounce 함수를 만들어보는지 배워볼 것입니다. debounce가 뭔가요? debounce는 전자 공학에서 온 용어입니다. 버튼을 누를 때, 예를 들어 TV 리모컨이라고 ]]>
                </description>
                <link>https://www.freecodecamp.org/korean/news/debounce-dibaunseu-javascripteseo-hamsureul-jiyeonsikineun-bangbeob-js-es6-yeje/</link>
                <guid isPermaLink="false">63988198387939063fcf5568</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Jeong Won Yoo ]]>
                </dc:creator>
                <pubDate>Tue, 20 Dec 2022 18:59:39 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/korean/news/content/images/2022/12/teaser.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>기사 원문:</strong> <a href="https://www.freecodecamp.org/news/javascript-debounce-example/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">Debounce – How to Delay a Function in JavaScript (JS ES6 Example)</a>
      </p><p>JavaScript에서 debounce(디바운스)는 유저가 입력할 때마다 코드를 오직 한 번씩만 실행되도록 해주는 함수입니다. 검색 박스의 제안, 텍스트 필드의 자동 저장, 버튼의 더블 클릭의 제거가 모두 debounce를 이용하는 사례입니다.</p><p>이번 튜토리얼에서는 JavaScript에서 어떻게 debounce 함수를 만들어보는지 배워볼 것입니다.</p><h2 id="debounce-">debounce가 뭔가요?</h2><p><strong>debounce</strong>는 전자 공학에서 온 용어입니다. 버튼을 누를 때, 예를 들어 TV 리모컨이라고 해본다면, 버튼에서 손을 떼려고 하기도 전에 신호는 아주 빠르게 리모컨의 마이크로칩으로 흐르고, 이것이 디바운스(debounce) 되어 마이크로칩은 이렇게 여러 번 '클릭'한 것을 등록해버립니다.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2021/01/debounce-button.png" class="kg-image" alt="버튼이 클릭되었을 때 신호가 흐르는 양상을 보여주는 그림으로, 고점과 저점 사이 신호가 무시되는 지점이 파란색 칠이 되어 있다. 신호가 고점과 저점을 여러 번 반복하는 지점이 무시되는 지점으로 설명되고 있다. " width="600" height="400" loading="lazy"><figcaption>버튼이 클릭되었을 때 신호가 흐르는 양상을 보여주는 그림으로, 고점과 저점 사이 신호가 무시되는 지점이 파란색 칠이 되어 있다. 신호가 고점과 저점을 여러 번 반복하는 지점이 무시되는 지점으로 설명되고 있다.</figcaption></figure><p>이 과정을 줄이려면, 일단 버튼으로부터 신호를 받았다면, 마이크로칩은 물리적으로 다시 버튼을 누르는 것이 불가능한, 약 몇 마이크로초 동안 버튼으로부터 온 신호를 처리하지 않습니다.</p><h2 id="javascript-debounce">JavaScript에서의 Debounce</h2><p>JavaScript에서의 사용 예도 비슷합니다. 사용 사례에 따라 오직 한 번만 함수를 실행하고 싶습니다.</p><p>방문자가 타이핑을 끝내고 난 뒤에만 검색 질의(query)에 대한 제안 옵션을 보여주고 싶다고 해봅시다.</p><p>아니면 양식의 내용을 저장하고자 하지만, 매번 '저장'이 발생한다면 데이터베이스를 거쳐야 하니, 사용자가 해당 내용을 적극적으로 변경하지 않을 때만 이를 하고 싶다고 해봅시다.</p><p>어떤 분들은 Window 95에 너무 익숙해진 나머지 이제 모든 곳에 더블 클릭하는, 제가 정말 좋아하는 이야기도 있고 말이죠. 😁</p><p>간단하게 debounce 함수를 구현해보았습니다. <a href="https://codepen.io/ondrabus/pen/WNGaVZN">CodePen은 이곳에서</a> 확인해보세요. </p><pre><code class="language-js">function debounce(func, timeout = 300) {
  let timer;
  return (...args) =&gt; {
    clearTimeout(timer);
    timer = setTimeout(() =&gt; {
      func.apply(this, args);
    }, timeout);
  };
}
function saveInput() {
  console.log('Saving data');
}
const processChange = debounce(() =&gt; saveInput());
</code></pre><p>입력창에서는 이렇게 쓰입니다.</p><pre><code class="language-html">&lt;input type="text" onkeyup="processChange()" /&gt;
</code></pre><p>버튼에서는 이렇게 쓰입니다.</p><pre><code class="language-html">&lt;button onclick="processChange()"&gt;Click me&lt;/button&gt;
</code></pre><p>window 이벤트</p><pre><code class="language-js">window.addEventListener('scroll', processChange);
</code></pre><p>간단한 JS 함수 같은 요소에도 쓸 수 있습니다.</p><p>자 그럼 어떤 일이 일어날까요? <code>debounce</code>는 특별한 함수로, 아래 두 가지 일을 처리합니다.</p><ul><li><em>timer</em> 값의 스코프(scope)를 할당하고</li><li>함수가 지정된 시간에 작동되도록 스케줄링합니다.</li></ul><p>가장 먼저 다룬 텍스트 입력창의 사례로 어떻게 이것이 작동하는지 설명해보도록 하겠습니다.</p><p>방문자가 첫 글자를 입력하고 키에서 손을 뗄 때, <code>debounce</code>는 우선 <code>clearTimeout(timer)</code>을 가지고 timer를 재설정합니다. 이때, 스케줄에 잡은 것이 아무것도 없으니, 이 단계가 꼭 필수적이진 않습니다. 이후 주어진 함수인 <code>saveInput()</code>을 300 밀리세컨드마다 실행되도록 스케줄 합니다.</p><p>그런데 만일 방문자가 계속 작성하는 동안 키에서 손을 뗄 때마다 <code>debounce</code>가 한 번 더 실행된다고 해봅시다. 매번 timer를 재설정한다는 것은 다시 말해 <code>saveInput()</code>과 함께 직전에 예정한 것을 취소하고 이다음의 300 밀리세컨드라는 새로운 시간으로 다시 스케줄 한다는 것입니다. 이는 방문자가 300 밀리세컨드 이내에 계속 키를 입력하는 한 지속됩니다.</p><p>마지막 스케줄은 제거되지 않으므로, 마침내 <code>saveInput()</code>이 호출됩니다.</p><h2 id="-">반대의 경우 - 뒤이은 이벤트를 무시하는 방법</h2><p>방금까지의 방법은 자동 저장이나 제안 옵션을 보여줄 때 좋습니다. 하지만 버튼 하나를 여러 번 클릭하는 사례에서는 어떨까요? 우리는 마지막 클릭까지 기다리는 대신 첫 번째 클릭에서 이를 등록하고, 나머지를 무시하고자 할 것입니다. <a href="https://codepen.io/ondrabus/pen/bGwmXjN">CodePen은 이곳에서</a></p><pre><code class="language-js">function debounce_leading(func, timeout = 300) {
  let timer;
  return (...args) =&gt; {
    if (!timer) {
      func.apply(this, args);
    }
    clearTimeout(timer);
    timer = setTimeout(() =&gt; {
      timer = undefined;
    }, timeout);
  };
}
</code></pre><p>여기에서는 첫 번째로 버튼이 클릭 되었을 때 발생한 첫 번째 <code>debounce_leading</code> 호출에서 <code>saveInput()</code> 함수가 동작합니다. timer는 300 밀리세컨드 이후 사라지도록 설정했습니다. 이 시간 내 뒤이은 모든 버튼 클릭은 이미 지정된 timer가 있으므로, timer가 사라지기까지 남은 시간을 계속해서 300 밀리세컨드로 초기화합니다.</p><h2 id="-debounce">라이브러리에서 구현된 Debounce</h2><p>이 글에서는 여러분이 어떻게 JavaScript에서 debounce를 구현하고, 웹사이트 요소에서 발생하는 이벤트를 잘 debounce 하기 위한 사용 방법을 보여드렸습니다.</p><p>그러나 여러분이 원하지 않는다면 직접 프로젝트에서 <em>debounce</em> 구현하지 않아도 됩니다. 이미 널리 쓰이는 JS 라이브러리에 구현되어 있습니다. 아래는 몇 가지 예시입니다.</p><!--kg-card-begin: html--><table>
<thead>
<tr>
<th>라이브러리</th>
<th>사용 예</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="http://benalman.com/projects/jquery-throttle-debounce-plugin/">jQuery (라이브러리를 통한)</a></td>
<td><code>$.debounce(300, saveInput);</code></td>
</tr>
<tr>
<td><a href="https://lodash.com/docs/4.17.15#debounce">Lodash</a></td>
<td><code>_.debounce(saveInput, 300)</code></td>
</tr>
<tr>
<td><a href="https://underscorejs.org/#debounce">Underscore</a></td>
<td><code>_.debounce(saveInput, 300);</code></td>
</tr>
</tbody>
</table><!--kg-card-end: html--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ JavaScript를 사용해 JSON 객체 구문분석(Parse)하기 ]]>
                </title>
                <description>
                    <![CDATA[ JSON(JavaScript Object Notation의 약자)은 어디를 가든 흔히 접할 수 있는 파일 형식입니다. 웹 애플리케이션을 사용해 본 적이 있다면 JSON을 사용해 서버와 기기 간에 데이터를 구축, 저장 및 전송과 같은 작업을 해봤을 가능성이 높습니다. 이 기사에서는 JSON과 JavaScript의 차이점을 간략하게 설명한 다음 브라우저와 Node.js 프로젝트에서 JavaScript를 사용해 JSON을 구문분석(parsing)하는 다양한 방법을 ]]>
                </description>
                <link>https://www.freecodecamp.org/korean/news/javascript-json-parse/</link>
                <guid isPermaLink="false">63882d7037582306394d7ca6</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Boyeon Ihn ]]>
                </dc:creator>
                <pubDate>Wed, 07 Dec 2022 07:14:12 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/korean/news/content/images/2022/12/602358380a2838549dcc2554.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>기사 원문:</strong> <a href="https://www.freecodecamp.org/news/json-stringify-example-how-to-parse-a-json-object-with-javascript/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">JSON Stringify Example – How to Parse a JSON Object with JS</a>
      </p><!--kg-card-begin: markdown--><h3 id="jsonjavascriptobjectnotationjson">JSON(JavaScript Object Notation의 약자)은 어디를 가든 흔히 접할 수 있는 파일 형식입니다. 웹 애플리케이션을 사용해 본 적이 있다면 JSON을 사용해 서버와 기기 간에 데이터를 구축, 저장 및 전송과 같은 작업을 해봤을 가능성이 높습니다.</h3>
<p>이 기사에서는 JSON과 JavaScript의 차이점을 간략하게 설명한 다음 브라우저와 Node.js 프로젝트에서 JavaScript를 사용해 JSON을 구문분석(parsing)하는 다양한 방법을 살펴보겠습니다.</p>
<h2 id="jsonjavascript">JSON과 JavaScript의 차이</h2>
<p>JSON은 일반 JavaScript처럼 보이지만 JSON을 텍스트 파일과 유사한 데이터 형식으로 이해하는 것이 좋습니다. JSON이 JavaScript와 매우 비슷해 보이는 이유는 JSON은 JavaScript 문법에서 영감을 받아 만들어졌기 때문입니다.</p>
<p>JSON 객체와 JSON 배열의 예시를 본 후 JavaScript 객체와 비교해보겠습니다.</p>
<h3 id="jsonjavascriptobjectliteral">JSON 객체와 JavaScript 객체 리터럴(object literal)</h3>
<p>우선 JSON 객체 문법을 보여드리겠습니다.</p>
<pre><code class="language-json">{
  "name": "Jane Doe",
  "favorite-game": "Stardew Valley", 
  "subscriber": false
}
</code></pre>
<figcaption>jane-profile.json</figcaption>
<p>JSON 객체와 일반 JavaScript 객체(객체 리터럴이라고도 합니다)의 주요 차이점은 따옴표입니다. JSON 객체의 모든 키(key)와 문자열 유형 값은 큰따옴표(<code>"</code>)로 감싸야 합니다.</p>
<p>반면 JavaScript 객체 리터럴은 조금 더 유연합니다. 객체 리터럴을 사용하면 키와 문자열을 큰따옴표로 묶을 필요가 없습니다. 키에 작은따옴표(<code>'</code>)를 사용하거나 따옴표 자체를 생략해도 됩니다.</p>
<p>위에 코드를 JavaScript 객체 리터럴 문법으로 변환해본다면 다음과 같습니다.</p>
<pre><code class="language-js">const profile = {
  name: 'Jane Doe',
  'favorite-game': 'Stardew Valley',
  subscriber: false
}
</code></pre>
<p>위의 예시에서는 <code>'favorite-game'</code> 키가 작은따옴표로 묶인 것을 확인할 수 있습니다. 객체 리터럴 문법에서는 대시(<code>-</code>)로 구분된 키는 따옴표로 감싸야 하기 때문입니다.</p>
<p>따옴표를 사용하지 않으려면 키를 카멜 표기법(camel case)인 <code>favoriteGames</code> 또는 언더바 표기법(underscore case)인 <code>favorite_game</code>으로 수정하면 됩니다.</p>
<h3 id="jsonjavascript">JSON 배열과 JavaScript 배열</h3>
<p>JSON 배열은 JavaScript 배열과 거의 같은 방식으로 작동하며 문자열, 불린(boolean), 숫자 및 기타 JSON 개체를 포함할 수 있습니다.</p>
<p>예시:</p>
<pre><code class="language-json">[
  {
    "name": "Jane Doe",
    "favorite-game": "Stardew Valley",
    "subscriber": false
  },
  {
    "name": "John Doe",
    "favorite-game": "Dragon Quest XI",
    "subscriber": true
  }
]
</code></pre>
<figcaption>profiles.json</figcaption>
<p>JavaScript 문법에서는 다음과 같이 표시됩니다.</p>
<pre><code class="language-js">const profiles = [
  {
    name: 'Jane Doe',
    'favorite-game': 'Stardew Valley',
    subscriber: false
  },
  {
    name: 'John Doe',
    'favorite-game': 'Dragon Quest XI',
    subscriber: true
  }
];
</code></pre>
<h2 id="json">JSON은 사실 문자열</h2>
<p>JSON 또한 객체와 배열이 있다면 일반 JavaScript 객체 리터럴이나 배열처럼 프로그램에서 사용할 수 없는지 의문이 들 수도 있습니다.</p>
<p>하지 못하는 이유는 JSON은 사실 문자열에 불과하기 때문입니다.</p>
<p>예를 들어 <code>jane-profile.json</code> 또는 <code>profiles.json</code>과 같은 별도의 파일에 JSON을 작성하면 해당 파일은 JavaScript처럼 보이지만 사실 JSON 객체 또는 JSON 배열 형식의 문자열이 담깁니다.</p>
<p>이때 API에 요청하면 다음과 같은 내용이 반환됩니다.</p>
<pre><code class="language-json">{"name":"Jane Doe","favorite-game":"Stardew Valley","subscriber":false}
</code></pre>
<p>텍스트 파일과 마찬가지로 프로젝트에서 JSON을 사용하려면 해당 파일을 구문분석하거나 프로그래밍 언어가 이해할 수 있는 형식으로 변경해야 합니다. 예를 들어, Python에서 JSON 객체를 구문분석하면 딕셔너리(dictionary)가 생성됩니다.</p>
<p>이러한 이해를 바탕으로 JavaScript에서 JSON을 구문분석하는 다양한 방법을 배워볼까요?</p>
<h2 id="json">브라우저에서 JSON 구문분석하기</h2>
<p>일반적으로 API를 통해 데이터를 수신하거나 보낼 때 브라우저에서 JSON으로 작업하는 경우가 많습니다.</p>
<p>몇 가지 예를 살펴보겠습니다.</p>
<h3 id="fetchjson"><code>fetch</code>를 사용해 JSON 구문분석하기</h3>
<p>API에서 데이터를 가져오는 가장 쉬운 방법은 <code>.json()</code> 메서드를 통해 JSON 응답을 JavaScript 객체 리터럴 또는 배열로 자동으로 구문분석하는 <code>fetch</code>를 사용하는 것입니다.</p>
<p>다음 예시는 무료 <a href="https://api.chucknorris.io/">Chuck Norris Jokes API</a>에서 개발자 관련 유머 데이터에 대한 GET 요청을 하기 위해 <code>fetch</code>를 사용하는 코드입니다.</p>
<pre><code class="language-js">fetch('https://api.chucknorris.io/jokes/random?category=dev')
  .then(res =&gt; res.json()) // .json() 메서드는 JSON 응답을 JavaScript 객체 리터럴로 구문분석합니다.
  .then(data =&gt; console.log(data));
</code></pre>
<p>브라우저에 해당 코드를 실행하면 콘솔에 다음과 같은 내용이 표시됩니다.</p>
<pre><code class="language-js">{
    "categories": ["dev"],
    "created_at": "2020-01-05 13:42:19.324003",
    "icon_url": "https://assets.chucknorris.host/img/avatar/chuck-norris.png",
    "id": "elgv2wkvt8ioag6xywykbq",
    "updated_at": "2020-01-05 13:42:19.324003",
    "url": "https://api.chucknorris.io/jokes/elgv2wkvt8ioag6xywykbq",
    "value": "Chuck Norris's keyboard doesn't have a Ctrl key because nothing controls Chuck Norris."
}
</code></pre>
<p>해당 내용은 JSON 객체처럼 보이지만 실제로는 JavaScript 객체 리터럴이기 때문에 프로그램에서 자유롭게 사용할 수 있습니다.</p>
<h3 id="jsonstringifyjson"><code>JSON.stringify()</code>로 JSON 문자열로 변환하기</h3>
<p>API로 데이터를 보내고 싶다면 어떻게 해야 할까요?</p>
<p>예를 들어 Chuck Norris Jokes API에 농담 데이터를 보내 다른 사람들이 나중에 읽을 수 있도록 하고 싶다면 말이죠.</p>
<p>먼저 보내고 싶은 농담 데이터를 JavaScript 객체 리터럴로 작성합니다.</p>
<pre><code class="language-js">const newJoke = {
  categories: ['dev'],
  value: "Chuck Norris's keyboard is made up entirely of Cmd keys because Chuck Norris is always in command."
};
</code></pre>
<p>그런 다음, API로 데이터를 보내기 위해서는 방금 생성한 농담 객체 리터럴을 JSON 문자열로 변환해야 합니다.</p>
<p>JavaScript에는 이런 작업을 실행하기 위한 매우 유용한 메서드가 포함되어 있습니다 - 바로 <code>JSON.stringify()</code>입니다.</p>
<pre><code class="language-js">const newJoke = {
  categories: ['dev'],
  value: "Chuck Norris's keyboard is made up entirely of Cmd keys because Chuck Norris is always in command."
};

console.log(JSON.stringify(newJoke)); // {"categories":["dev"],"value":"Chuck Norris's keyboard is made up entirely of Cmd keys because Chuck Norris is always in command."}

console.log(typeof JSON.stringify(newJoke)); // string
</code></pre>
<p>이 예시에서 객체 리터럴을 JSON 문자열로 변환하는 것처럼 <code>JSON.stringify()</code>는 배열에서도 똑같이 작동합니다.</p>
<p>마지막으로 JSON 문자열로 변환된 농담 데이터를 POST 요청과 함께 API로 다시 보내면 됩니다.</p>
<p>Chuck Norris Jokes API에는 실제로 이 기능이 없지만, 있다고 가정하면 코드는 다음과 같을 것입니다.</p>
<pre><code class="language-js">const newJoke = {
  categories: ['dev'],
  value: "Chuck Norris's keyboard is made up entirely of Cmd keys because Chuck Norris is always in command."
};

fetch('https://api.chucknorris.io/jokes/submit', { // fake API endpoint
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify(newJoke), // JavaScript 객체 리터럴을 JSON 문자열로 변환하기
})
  .then(res =&gt; res.json())
  .then(data =&gt; console.log(data))
  .catch(err =&gt; {
    console.error(err);
  });
</code></pre>
<p>이렇게 가져온 JSON을 <code>fetch</code>로 구문분석하고 <code>JSON.stringify()</code>를 사용해 JavaScript 객체 리터럴을 JSON 문자열로 변환했습니다.</p>
<h3 id="json">브라우저에서 로컬 JSON 파일 작업하기</h3>
<p>안타깝게도 브라우저에서 로컬 JSON 파일을 로딩하는 것은 가능하지 않고 권장하지 않습니다.</p>
<p>로컬 파일을 로딩하려고 하면 <code>fetch</code>에서 오류가 발생합니다. 예를 들어 농담 데이터가 포함된 JSON 파일이 있다고 가정해봅시다.</p>
<pre><code class="language-json">[
  {
    "categories": ["dev"],
    "created_at": "2020-01-05 13:42:19.324003",
    "icon_url": "https://assets.chucknorris.host/img/avatar/chuck-norris.png",
    "id": "elgv2wkvt8ioag6xywykbq",
    "updated_at": "2020-01-05 13:42:19.324003",
    "url": "https://api.chucknorris.io/jokes/elgv2wkvt8ioag6xywykbq",
    "value": "Chuck Norris's keyboard doesn't have a Ctrl key because nothing controls Chuck Norris."
  },
  {
    "categories": ["dev"],
    "created_at": "2020-01-05 13:42:19.324003",
    "icon_url": "https://assets.chucknorris.host/img/avatar/chuck-norris.png",
    "id": "ae-78cogr-cb6x9hluwqtw",
    "updated_at": "2020-01-05 13:42:19.324003",
    "url": "https://api.chucknorris.io/jokes/ae-78cogr-cb6x9hluwqtw",
    "value": "There is no Esc key on Chuck Norris' keyboard, because no one escapes Chuck Norris."
  }
]
</code></pre>
<figcaption>jokes.json</figcaption>
<p>이 JSON 파일을 구문분석하고 농담 목록을 간단한 HTML 페이지에 만들고 싶다면 어떻게 해야 할까요?</p>
<p>예시:</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;head&gt;
    &lt;meta charset="utf-8" /&gt;
    &lt;meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /&gt;
    &lt;meta name="viewport" content="width=device-width" /&gt;
    &lt;title&gt;Fetch Local JSON&lt;/title&gt;
  &lt;/head&gt;
  &lt;script&gt;
    fetch("./jokes.json", { mode: "no-cors" }) // disable CORS because path does not contain http(s)
      .then((res) =&gt; res.json())
      .then((data) =&gt; console.log(data));
  &lt;/script&gt;
&lt;/html&gt;
</code></pre>
<figcaption>index.html</figcaption>
<p>위 예시의 HTML 페이지를 만들고 브라우저에서 여는 경우, 콘솔에는 다음과 같은 메시지가 출력됩니다.</p>
<p><code>Fetch API cannot load file://&lt;path&gt;/jokes.json. URL scheme "file" is not supported</code></p>
<p>기본적으로 브라우저는 보안상의 이유로 로컬 파일에 대한 액세스를 허용하지 않습니다. 이런 제한은 사용자의 보안과 안전을 위한 것이기 때문에 이 기본 동작을 변경하려고 하면 안 됩니다.</p>
<p>대신 로컬 JSON 파일을 JavaScript로 변환하는 것이 가장 좋은 방법입니다. JSON 문법은 JavaScript와 매우 비슷하므로 이런 변환 작업은 어렵지 않습니다.</p>
<p>먼저 새 파일을 만들고 JSON 데이터를 변수로 선언하기만 하면 됩니다. 다음과 같이 말이죠.</p>
<pre><code class="language-js">const jokes = [
  {
    "categories": ["dev"],
    "created_at": "2020-01-05 13:42:19.324003",
    "icon_url": "https://assets.chucknorris.host/img/avatar/chuck-norris.png",
    "id": "elgv2wkvt8ioag6xywykbq",
    "updated_at": "2020-01-05 13:42:19.324003",
    "url": "https://api.chucknorris.io/jokes/elgv2wkvt8ioag6xywykbq",
    "value": "Chuck Norris's keyboard doesn't have a Ctrl key because nothing controls Chuck Norris."
  },
  {
    "categories": ["dev"],
    "created_at": "2020-01-05 13:42:19.324003",
    "icon_url": "https://assets.chucknorris.host/img/avatar/chuck-norris.png",
    "id": "ae-78cogr-cb6x9hluwqtw",
    "updated_at": "2020-01-05 13:42:19.324003",
    "url": "https://api.chucknorris.io/jokes/ae-78cogr-cb6x9hluwqtw",
    "value": "There is no Esc key on Chuck Norris' keyboard, because no one escapes Chuck Norris."
  }
]
</code></pre>
<figcaption>jokes.js</figcaption>
<p>그런 다음 별도의 스크립트로 HTML 페이지에 추가합니다.</p>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;head&gt;
    &lt;meta charset="utf-8" /&gt;
    &lt;meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /&gt;
    &lt;meta name="viewport" content="width=device-width" /&gt;
    &lt;title&gt;Fetch Local JSON&lt;/title&gt;
  &lt;/head&gt;
  &lt;script src="jokes.js"&gt;&lt;/script&gt;
  &lt;script&gt;
    console.log(jokes);
  &lt;/script&gt;
&lt;/html&gt;
</code></pre>
<p>이제 코드에서 농담 배열을 자유롭게 사용할 수 있습니다.</p>
<p>JavaScript 모듈을 사용해 같은 작업을 수행할 수도 있지만, 이 기사에서는 모듈 사용에 대한 소개는 생략하겠습니다.</p>
<p>하지만 컴퓨터에 Node.js가 설치된 상태에서 로컬 JSON files로 작업하고 싶다면 어떻게 해야 할까요? 이어서 살펴보겠습니다.</p>
<h2 id="nodejsjson">Node.js에서 JSON 구문분석하기</h2>
<p>Node.js는 브라우저 외부에서 JavaScript를 실행할 수 있는 JavaScript 런타임입니다. Node.js에 대한 소개는 <a href="https://www.freecodecamp.org/news/the-definitive-node-js-handbook-6912378afc6e/">여기(영문 기사)</a>에서 확인할 수 있습니다.</p>
<p>Node.js를 사용하여 컴퓨터에서 로컬로 코드를 실행하든 서버에서 전체 웹 응용 프로그램을 실행하든, 기본적인 Node.js와 JSON 사용법은 알아두는 것이 좋습니다.</p>
<p>다음 예에서는 같은 <code>jokes.json</code> 파일을 사용하겠습니다.</p>
<pre><code class="language-json">[
  {
    "categories": ["dev"],
    "created_at": "2020-01-05 13:42:19.324003",
    "icon_url": "https://assets.chucknorris.host/img/avatar/chuck-norris.png",
    "id": "elgv2wkvt8ioag6xywykbq",
    "updated_at": "2020-01-05 13:42:19.324003",
    "url": "https://api.chucknorris.io/jokes/elgv2wkvt8ioag6xywykbq",
    "value": "Chuck Norris's keyboard doesn't have a Ctrl key because nothing controls Chuck Norris."
  },
  {
    "categories": ["dev"],
    "created_at": "2020-01-05 13:42:19.324003",
    "icon_url": "https://assets.chucknorris.host/img/avatar/chuck-norris.png",
    "id": "ae-78cogr-cb6x9hluwqtw",
    "updated_at": "2020-01-05 13:42:19.324003",
    "url": "https://api.chucknorris.io/jokes/ae-78cogr-cb6x9hluwqtw",
    "value": "There is no Esc key on Chuck Norris' keyboard, because no one escapes Chuck Norris."
  }
]
</code></pre>
<figcaption>jokes.json</figcaption>
<h3 id="requirejson"><code>require()</code>로 JSON 파일 구문분석하기</h3>
<p>가장 쉬운 방법부터 시작해보겠습니다.</p>
<p>로컬 JSON 파일이 있다면 <code>require()</code>를 사용해 다른 Node.js 모듈처럼 로딩하기만 하면 됩니다.</p>
<pre><code class="language-js">const jokes = require('./jokes.json');
</code></pre>
<p>JSON 파일이 자동으로 구문분석되고 프로젝트에 이 파일을 바로 사용할 수 있습니다.</p>
<pre><code class="language-js">const jokes = require('./jokes.json');

console.log(jokes[0].value); // "Chuck Norris's keyboard doesn't have a Ctrl key because nothing controls Chuck Norris."
</code></pre>
<p>이 작업은 동기적으로(synchronous) 처리되므로 전체 파일의 구문분석이 완료할 때까지 프로그램이 일시 중지되기 때문에 JSON 파일이 크면 프로그램 속도가 느려질 수 있다는 점을 주의하세요.</p>
<p>또한 JSON을 이 방법으로 구문분석하면 파일 전체가 메모리에 로딩되므로 정적 JSON 파일에만 이 방법을 사용하는 것이 좋습니다. 프로그램이 실행되는 동안 JSON 파일이 변경된다면 해당 프로그램을 다시 시작하고 업데이트된 JSON 파일을 구문분석할 때까지 변경 사항에 접근할 수 없습니다.</p>
<h3 id="fsreadfilesyncjsonparsejson"><code>fs.readFileSync()</code>와 <code>JSON.parse()</code>를 사용해 JSON 파일 구문분석하기</h3>
<p>두 번째 방법은 Node.js 프로젝트에서 JSON 파일을 구문분석하는 더 보편적인 방법입니다. <code>fs</code>(파일 시스템) 모듈로 파일을 읽은 다음 <code>JSON.parse()</code>로 구문분석합니다.</p>
<p><code>fs.readFileSync()</code> 메서드를 사용해 이 작업을 실행하는 방법에 대해 알아보겠습니다. 먼저 프로젝트에 <code>fs</code> 모듈을 추가합니다.</p>
<pre><code class="language-js">const fs = require('fs');
</code></pre>
<p>그런 다음 <code>jokes.json</code> 파일의 출력된 내용을 저장할 새 변수를 만들고 <code>fs.readFileSync()</code> 메서드를 할당합니다.</p>
<pre><code class="language-js">const fs = require('fs');
const jokesFile = fs.readFileSync();
</code></pre>
<p><code>fs.readFileSync()</code> 메서드는 몇 가지 인수를 사용합니다. 첫 번째 인수는 읽을 파일의 경로입니다.</p>
<pre><code class="language-js">const fs = require('fs');
const jokesFile = fs.readFileSync('./jokes.json');
</code></pre>
<p>지금 <code>jokesFile</code>을 콘솔에 출력한다면, 다음과 같은 내용이 표시됩니다.</p>
<pre><code>&lt;Buffer 5b 0a 20 20 7b 0a 20 20 20 20 22 63 61 74 65 67 6f 72 69 65 73 22 3a 20 5b 22 64 65 76 22 5d 2c 0a 20 20 20 20 22 63 72 65 61 74 65 64 5f 61 74 22 3a ... 788 more bytes&gt;
</code></pre>
<p>이런 내용은 <code>fs</code> 모듈이 파일을 읽고 있지만 파일의 인코딩이나 형식을 알지 못한다는 것을 의미합니다. <code>fs</code>은 JSON과 같은 텍스트 기반 파일뿐만 아니라 거의 모든 파일을 로딩하는 데 사용될 수 있으므로 해당 파일이 어떻게 인코딩되는지 알려줘야 합니다.</p>
<p>텍스트 기반 파일의 경우 인코딩은 일반적으로 <code>utf8</code>입니다.</p>
<pre><code class="language-js">const fs = require('fs');
const jokesFile = fs.readFileSync('./jokes.json', 'utf8');
</code></pre>
<p>이제 <code>jokesFile</code>를 콘솔에 출력하면 파일의 내용이 표시됩니다.</p>
<p>하지만 로딩이 완료된 파일은 여전히 문자열입니다. 다른 메서드를 사용해 <code>jokesFile</code>을 JavaScript 객체 또는 배열로 구문분석해야 하는데, 이때 <code>JSON.parse()</code>를 사용하면 됩니다.</p>
<pre><code class="language-js">const fs = require('fs');
const jokesFile = fs.readFileSync('./jokes.json', 'utf8');
const jokes = JSON.parse(jokesFile);

console.log(jokes[0].value); // "Chuck Norris's keyboard doesn't have a Ctrl key because nothing controls Chuck Norris."
</code></pre>
<p>메서드 이름에서 알 수 있듯이 <code>JSON.parse()</code>는 JSON 문자열을 사용해 JavaScript 객체 리터럴 또는 배열로 구문분석합니다.</p>
<p>위의 <code>require</code> 메서드와 마찬가지로 <code>fs.readFileSync()</code>는 동기 메서드입니다. 즉, 큰 파일을 처리해야 할 때 프로그램이 느려질 수 있습니다.</p>
<p><code>fs.readFileSync()</code>와 <code>JSON.parse()</code>를 사용해 JSON 파일 구문분석하는 방법 또한 파일을 한 번만 읽고 메모리에 로딩합니다. 파일이 도중에 변경된다면 어느 시점에서 파일을 다시 읽어야 합니다. 파일을 쉽게 다시 읽을 수 있는 간단한 기능을 만드는 것이 좋겠죠. 다음과 같이 말이에요.</p>
<pre><code class="language-js">const fs = require('fs');
const readFile = path =&gt; fs.readFileSync(path, 'utf8');

const jokesFile1 = readFile('./jokes.json');
const jokes1 = JSON.parse(jokesFile1);

console.log(jokes1[0].value); // "Chuck Norris's keyboard doesn't have a Ctrl key because nothing controls Chuck Norris."

// jokes.json 파일이 어느 지점에서 변경됩니다 

const jokesFile2 = readFile('./jokes.json');
const jokes2 = JSON.parse(jokesFile2);

console.log(jokes2[0].value); // "Chuck Norris's keyboard is made up entirely of Cmd keys because Chuck Norris is always in command."
</code></pre>
<h3 id="fsreadfilejsonparsejson"><code>fs.readFile()</code>과 <code>JSON.parse()</code>를 사용해 JSON 구문분석하기</h3>
<p><code>fs.readFile()</code> 메서드는 <code>fs.readFileSync()</code>와 매우 비슷하지만, 비동기적으로 작동한다는 중요한 차이점이 있습니다. 읽으려고 하는 파일 크기가 크고 로딩 작업이 진행되는 동안 나머지 코드의 실행이 지연되지 않도록 하려면 <code>fs.readFile()</code>을 매우 유용하게 사용할 수 있습니다.</p>
<p>간단한 예시를 봅시다.</p>
<pre><code class="language-js">const fs = require('fs');

fs.readFile('./jokes.json', 'utf8');
</code></pre>
<p><code>jokesFile</code>과 같은 변수에 메서드를 할당하지 않는다는 점을 제외하면 지금까지 <code>fs.readFileSync()</code>를 사용한 작업과 유사합니다. <code>fs.readFile()</code>은 비동기식이기 때문에 이후의 코드는 파일 읽기가 끝나기 전에 실행됩니다.</p>
<p><code>fs.readFileSync()</code>와 또 다른 점은 바로 <code>fs.readFile()</code>는 JSON 구문분석하는 콜백 함수를 사용해 내부에 있는 JSON을 구문분석한다는 것입니다.</p>
<pre><code class="language-js">const fs = require('fs');

fs.readFile('./jokes.json', 'utf8', (err, data) =&gt; {
  if (err) console.error(err);
  const jokes = JSON.parse(data);

  console.log(jokes[0].value);
});

console.log("이게 먼저 실행됩니다!");
</code></pre>
<p>그러면 콘솔에 다음 내용이 출력됩니다.</p>
<pre><code>이게 먼저 실행됩니다!
Chuck Norris's keyboard doesn't have a Ctrl key because nothing controls Chuck Norris.
</code></pre>
<p><code>fs.readFileSync()</code>와 마찬가지로 <code>fs.readFile()</code>도 파일을 메모리에 로딩하므로 파일이 변경되면 다시 읽어야 합니다.</p>
<p>또한 <code>fs.readFile()</code>이 비동기적으로 작동하지만, 작업이 완료되면 읽히는 전체 파일을 메모리로 로딩한다는 점은 <code>fs.readFile()</code>과 같습니다. 대용량 파일이 있는 경우 대신 <a href="https://www.freecodecamp.org/news/node-js-streams-everything-you-need-to-know-c9141306be93/">Node.js 스트림(stream)</a>을 사용하는 것을 추천합니다.</p>
<h3 id="nodejsjsonstringifyjson">Node.js에서 <code>JSON.stringify()</code>를 사용해 JSON 문자열로 변환하기</h3>
<p>마지막으로, Node.js로 JSON을 구문분석하면 API 응답으로 JSON을 반환해야 할 수도 있습니다.</p>
<p>다행히 이 작업은 브라우저와 같은 방식으로 작동합니다. <code>JSON.stringify()</code>를 사용해 JavaScript 객체 리터럴 또는 배열을 JSON 문자열로 변환하면 됩니다.</p>
<pre><code class="language-js">const newJoke = {
  categories: ['dev'],
  value: "Chuck Norris's keyboard is made up entirely of Cmd keys because Chuck Norris is always in command."
};

console.log(JSON.stringify(newJoke)); // {"categories":["dev"],"value":"Chuck Norris's keyboard is made up entirely of Cmd keys because Chuck Norris is always in command."}
</code></pre>
<p>이렇게 지금까지 브라우저 및 Node.js 프로젝트에서 JSON 관련 작업하는 데 필요한 여러 방법에 대해 살펴보았습니다.</p>
<p>이제 나가서 JSON을 마음껏 구문분석하거나 문자열화하세요!</p>
<p>제가 이 기사에서 빠트린 내용이 있거나, 독자분께서 선호하시는 JSON을 구문분석하는 방법이 있다면, <a href="https://twitter.com/kriskoishigawa">트위터를</a> 통해 알려주세요!</p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ JavaScript에서 가장 잘 알려진 HTTP 요청 방법 ]]>
                </title>
                <description>
                    <![CDATA[ JavaScript는 서버 단의 자원으로부터 데이터를 전송받거나 전송할 수 있는 HTTP 요청을 만들 수 있는 훌륭한 모듈(modules)과 메서드(method)가 있습니다. 이 글에서는, JavaScript 내에서 잘 알려진 여러 HTTP 요청 방법을 살펴보려고 합니다. Ajax Ajax는 전통적인 방법으로 비동기(asynchronous) HTTP 요청을 보냅니다. HTTP POST를 통해 데이터를 보낼 수 있고, HTTP GET을 통해 데이터를 전송받을 ]]>
                </description>
                <link>https://www.freecodecamp.org/korean/news/javascripteseo-gajang-jal-alryeojin-http-yoceong-bangbeob-2/</link>
                <guid isPermaLink="false">637ec5e74d426206f9a4fc8a</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Jeong Won Yoo ]]>
                </dc:creator>
                <pubDate>Wed, 07 Dec 2022 07:01:57 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/korean/news/content/images/2022/11/1_gqHgCNubMncv7EwWNdArGQ.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>기사 원문:</strong> <a href="https://www.freecodecamp.org/news/here-is-the-most-popular-ways-to-make-an-http-request-in-javascript-954ce8c95aaa/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">Here are the most popular ways to make an HTTP request in JavaScript</a>
      </p><p>JavaScript는 서버 단의 자원으로부터 데이터를 전송받거나 전송할 수 있는 HTTP 요청을 만들 수 있는 훌륭한 모듈(modules)과 메서드(method)가 있습니다. 이 글에서는, JavaScript 내에서 잘 알려진 여러 HTTP 요청 방법을 살펴보려고 합니다.</p><h1 id="ajax">Ajax</h1><p>Ajax는 전통적인 방법으로 비동기(asynchronous) HTTP 요청을 보냅니다. HTTP POST를 통해 데이터를 보낼 수 있고, HTTP GET을 통해 데이터를 전송받을 수 있습니다. 한 번 살펴보고 <code>GET</code> 요청을 보내봅시다. 저는 무작위 데이터를 JSON 형태로 반환하는 JSONPlaceholder라는 개발자를 위한 무료 온라인 REST API를 사용하려고 합니다.</p><p>Ajax로 HTTP 호출을 보내기 위해서는 새로운 <code>XMLHttpRequest()</code> 메서드를 초기화하고, URL 엔드포인트(endpoint)와 HTTP 메서드(이 경우에는 GET이겠죠)를 지정해야 합니다. 마지막으로, <code>open()</code> 메서드를 사용해 HTTP 메서드와 URL 엔드포인트를 연결하고 <code>send()</code> 메서드로 요청을 실행합니다.</p><p>HTTP 응답(response)을 콘솔로 찍어볼 수 있는데, <code>readystatechanged</code> 이벤트가 발생했을 때 호출한 이벤트 핸들러를 포함한 <code>XMLHttpRequest.onreadystatechange</code> 속성을 사용해봅시다.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/1*zXtlRe4yRF3tZkFFvBhZeA.png" class="kg-image" alt="위에서 설명한 내용이 이미지로 삽입되어 있다. 이미지 속 내용은 아래 코드와 동일하다." width="800" height="349" loading="lazy"><figcaption>위에서 설명한 내용이 이미지로 삽입되어 있다. 이미지 속 내용은 아래 코드와 동일하다.</figcaption></figure><pre><code class="language-js">
const Http = new XMLHttpRequest();

const url = 'https://jsonplaceholder.typicode.com/posts';

Http.open('GET', url);
Http.send();
Http.onreadystatechange = (e) =&gt; {
	console.log(Http.responseText);
};</code></pre><p>브라우저 콘솔을 보면, 반환된 JSON 형식의 배열 데이터를 확인할 수 있습니다. 하지만, 어떻게 요청이 완료되었는지 알 수 있을까요? 다른 말로 하자면, Ajax를 통해 응답을 어떻게 처리할 수 있는 걸까요?</p><p><code>onreadystatechange</code> 속성은 두 가지 메서드, <code>readyState</code>와 <code>status</code>로 우리가 보낸 요청의 상태를 확인하도록 해 줍니다.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/1*UfZf6qaZwNh5Mptft4WIZA.png" class="kg-image" alt="위에서 설명한 내용이 이미지로 삽입되어 있다. 이미지 속 내용은 아래 코드와 동일하다." width="800" height="376" loading="lazy"><figcaption>위에서 설명한 내용이 이미지로 삽입되어 있다. 이미지 속 내용은 아래 코드와 동일하다.</figcaption></figure><pre><code class="language-js">
// 역자 주 : 이하 일부 이미지와 함께 제공되는 코드는 원글에는 존재하지 않지만, 번역 과정에서 대체 텍스트를 제공할 목적으로 추가되었습니다.

const Http = new XHMLHttpRequest();
const url = 'https://jsonplaceholder.typicode.com/posts';

Http.open('GET', url);
Http.send();

Http.onreadystatechange = function () {
	if (this.readyState == 4 &amp;&amp; this.status == 200) {
		console.log(Http.responseText);
	}
};</code></pre><p>만일 <code>readyState</code>가 4와 동일하다면, 이는 요청이 완료되었다는 의미입니다. <code>readyState</code> 속성에는 총 다섯 가지 응답이 있습니다. <a href="https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/readyState">이곳</a>에서 더 확인해보세요.</p><p>JavaScript로 직접 Ajax 호출을 하는 것 말고도, jQuery 메서드인 <code>$.Ajax</code> 같은 HTTP 호출을 만들어내는 강력한 메서드가 존재합니다. 이제부터 이에 대해 이야기해봅시다.</p><h2 id="jquery-">jQuery 메서드</h2><p>jQuery는 HTTP 요청을 쉽게 다루게 해주는 다양한 메서드를 가지고 있습니다. 이 메서드를 사용하려면, jQuery 라이브러리를 프로젝트에 포함해야 합니다.</p><pre><code class="language-html">&lt;script src='https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js'&gt;&lt;/script&gt;</code></pre><h3 id="-ajax">$.ajax</h3><p>jQuery Ajax는 HTTP 호출을 만들어내는 가장 쉬운 방법 중 하나입니다.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/1*vZ4BqVQfsvtpJm_RCsCE2Q.png" class="kg-image" alt="위에서 설명한 내용이 이미지로 삽입되어 있다. 이미지 속 내용은 아래 코드와 동일하다." width="800" height="429" loading="lazy"><figcaption>위에서 설명한 내용이 이미지로 삽입되어 있다. 이미지 속 내용은 아래 코드와 동일하다.</figcaption></figure><pre><code class="language-js">
$(document).ready(function() {
	const Url = 'https://jsonplaceholder.typicode.com/posts';
	$('.btn').click(function() {
		$.ajax({
			url: Url,
			type: "GET",
			success: function(result) {
				console.log(result)
			},
			error:function(error) {
				console.log(`Error ${error})
			}
		})
	})
})</code></pre><p>이 $.ajax 메서드는 많은 파라미터를 받는데, 이중 일부는 필수적이고 일부는 선택적으로 사용됩니다. 이 메서드는 전달받은 응답을 다룰 수 있도록 두 가지 콜백(callback) 옵션인 <code>success</code>와 <code>error</code>를 가집니다.</p><h3 id="-get-">$.get 메서드</h3><p>$.get 메서드는 GET 요청을 실행하기 위해 사용됩니다. 엔드포인트와 콜백 함수 이렇게 두 가지 파라미터를 받습니다.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/1*2koN5FJuT68WIyRKTihe5w.png" class="kg-image" alt="위에서 설명한 내용이 이미지로 삽입되어 있다. 이미지 속 내용은 아래 코드와 동일하다." width="797" height="287" loading="lazy"><figcaption>위에서 설명한 내용이 이미지로 삽입되어 있다. 이미지 속 내용은 아래 코드와 동일하다.</figcaption></figure><pre><code class="language-js">const Url = 'https://jsonplaceholder.typicode.com/posts';

$('.btn').click(function () {
	$.get(Url, function (data, status) {
		console.log(`${data}`);
	});
});</code></pre><h3 id="-post">$.post</h3><p><code>$.post</code> 메서드는 서버에 데이터를 보내는 또 다른 방법입니다. <code>url</code>, 전송하고 싶은 데이터, 콜백 함수 이렇게 세 가지 파라미터를 받습니다.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/1*ql6Yp1EJfD7850GXhErwyw.png" class="kg-image" alt="위에서 설명한 내용이 이미지로 삽입되어 있다. 이미지 속 내용은 아래 코드와 동일하다." width="695" height="355" loading="lazy"><figcaption>위에서 설명한 내용이 이미지로 삽입되어 있다. 이미지 속 내용은 아래 코드와 동일하다.</figcaption></figure><pre><code class="language-js">
const Url = 'https://jsonplaceholder.typicode.com/posts';
const data = {
	name: 'said',
    id: 23,
};

$('.btn').click(function () {
    $.post(Url, data, function (data, status) {
	    console.log(`${data} and status is ${status}`);
	});
});</code></pre><h3 id="-getjson">$.getJSON</h3><p><code>$.getJSON</code> 메서드는 오직 JSON 형식으로만 데이터를 반환합니다. <code>url</code>과 콜백 함수 두 가지 파라미터를 받습니다.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/1*hdcFdVHiBiRAo1YOi_Kt0Q.png" class="kg-image" alt="위에서 설명한 내용이 이미지로 삽입되어 있다. 이미지 속 내용은 아래 코드와 동일하다." width="755" height="291" loading="lazy"><figcaption>위에서 설명한 내용이 이미지로 삽입되어 있다. 이미지 속 내용은 아래 코드와 동일하다.</figcaption></figure><pre><code class="language-js">
const Url = 'https://jsonplaceholder.typicode.com/posts';

$('.btn').click(function () {
    $.getJSON(Url, function (result) {
	    console.log(result);
    });
});</code></pre><p>jQuery는 요청을 하거나 원격 서버에 데이터를 보내기 위해 이 모든 메서드를 가지고 있습니다. 하지만 아래 예제에서 보시는 것처럼 이 모든 메서드를 <code>$.ajax</code> 메서드 하나로 합쳐 보낼 수도 있습니다.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/1*soPARjfQXMcZ5ccPK1QMmA.png" class="kg-image" alt="위에서 설명한 내용이 이미지로 삽입되어 있다. 이미지 속 내용은 아래 코드와 동일하다." width="800" height="420" loading="lazy"><figcaption>위에서 설명한 내용이 이미지로 삽입되어 있다. 이미지 속 내용은 아래 코드와 동일하다.</figcaption></figure><pre><code class="language-js">
const Url = 'https://jsonplaceholder.typicode.com/posts';

$('.btn').click(function () {
	const data = { name: 'said', id: 23 };

    $.ajax({
        url: Url,
        type: 'GET', // 또는 POST
        // data: data, // 만일 POST 타입을 쓴다면
        // dataType: JSON 또는 HTML, XML, TXT, jsonp
        success: function (result) {
	        console.log(result);	
        },
        error: function (error) {
	        console.log(`Error ${error}`);
        },
	});
});</code></pre><h2 id="fetch">fetch</h2><p><code>fetch</code>는 비동기 요청을 보낼 수 있도록 해주는 새롭게 등장한 강력한 web API입니다. 사실 <code>fetch</code>는 제가 가장 좋아하는, HTTP 요청을 만들 수 있는 최고의 방법 중 하나입니다. 이는 ES6의 훌륭한 기능 중 하나인 "Promise"를 반환합니다. ES6가 익숙하지 않으시다면, <a href="https://www.freecodecamp.org/news/write-less-do-more-with-javascript-es6-5fd4a8e50ee2">이 기사</a>에서 더 읽어보실 수 있습니다. Promise는 더 영리한 방법으로 비동기 요청을 다룰 수 있게 해 줍니다. <code>fetch</code>가 기술적으로 어떻게 동작하는지 한 번 살펴봅시다.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/1*kz6k4VRs0RiVCasWR0pCow.png" class="kg-image" alt="fetch를 이용해 HTTP 요청을 보내는 예시. 이미지 속 내용은 아래 코드와 동일하다." width="800" height="270" loading="lazy"><figcaption>위에서 설명한 내용이 이미지로 삽입되어 있다. 이미지 속 내용은 아래 코드와 동일하다.</figcaption></figure><pre><code class="language-js">
const Url = 'https://jsonplaceholder.typicode.com/posts';

fetch(Url)
.then((data) =&gt; {return data.json()})
.then((res) =&gt; {console.log(res)})</code></pre><p><code>fetch</code> 함수는 <code>엔드포인트</code> URL이라는 파라미터를 필수적으로 받습니다. 아래 예제에서 보시는 것처럼 여러 다른 선택 가능한 파라미터도 있습니다.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/1*QasrBgYZcU4BBFHqD2bBdg.png" class="kg-image" alt="fetch를 이용해 HTTP 요청을 보내는 예시 두 번째에서는 선택적 파라미터에 대한 코드가 적혀 있다. 이미지 속 내용은 아래 코드와 동일하다." width="800" height="497" loading="lazy"><figcaption>위에서 설명한 내용이 이미지로 삽입되어 있다. 이미지 속 내용은 아래 코드와 동일하다.</figcaption></figure><pre><code class="language-js">
const Url = 'https://jsonplaceholder.typicode.com/posts';

const Data = {
    name: 'Said',
    id: 23,
};

// 선택적 파라미터

const othePram = {
    headers: {
	    'content-type': 'application/json, charset=UTF-8',
    },
    body: Data,
    method: 'POST',
};

fetch(Url, othePram)
.then((data) =&gt; {return data.json()})
.then((res) =&gt; {console.log(res)})
.catch((error) =&gt; console.log(error));</code></pre><p>여기서 보시는 것처럼, <code>fetch</code>는 HTTP 요청을 만들기 위한 다양한 이점들이 있습니다. <a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch">이곳</a>에서 더 많이 알아보세요. 추가적으로, fetch 안에서는 서버 단에 요청을 보내거나 요청을 받을 수 있는 <a href="https://github.com/axios/axios">axios</a> 같은 다른 모듈이나 플러그인이 있습니다.</p><h2 id="axios">Axios</h2><p>Axios는 HTTP 요청을 만들고, 다양한 좋은 기능까지 제공하는 오픈 소스 라이브러리입니다. 어떻게 동작하는지 한 번 확인해봅시다.</p><h3 id="-">사용 방법</h3><p>우선, Axios를 가지고 있어야겠죠. 프로젝트에 Axios를 포함하는 두 가지 방법이 있습니다.</p><p>제일 먼저 npm을 이용할 수 있습니다:</p><pre><code class="language-bash">npm install axios --save</code></pre><p>이후 이를 임포트(import)합니다.</p><pre><code class="language-bash">import axios from 'axios'</code></pre><p>두 번째로 CDN을 이용해 포함하는 방법입니다.</p><pre><code class="language-html">&lt;script src='https://unpkg.com/axios/dist/axios.min.js'&gt;&lt;/script&gt;</code></pre><h3 id="axios-">axios로 요청하기</h3><p>Axios를 통해 여러분은 서버에 데이터를 받거나, 데이터를 보낼 수 있는 <code>GET</code>과 <code>POST</code>를 사용할 수 있습니다.</p><h3 id="get-">GET:</h3><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/1*4wmqiPsSN5mdgjJiRaKVZg.png" class="kg-image" alt="Axios로 GET 요청을 보내는 예제 코드. 이미지 속 내용은 아래 코드와 동일하다." width="781" height="440" loading="lazy"><figcaption>위에서 설명한 내용이 이미지로 삽입되어 있다. 이미지 속 내용은 아래 코드와 동일하다.</figcaption></figure><pre><code class="language-js">
const Url = 'https://jsonplaceholder.typicode.com/posts';

axios
.get(Url)
.then((data) =&gt; console.log(data))
.catch((err) =&gt; console.log(err));

// 파라미터와 함께

const Url = 'https://jsonplaceholder.typicode.com/posts';
const params = {
    name: 'Said',
    id: 21,
};

axios.get(Url, params)
.then((data) =&gt; console.log(data))
.catch((err) =&gt; console.log(err));</code></pre><p><code>axios</code>는 하나의 필수 파라미터를 갖고, 두 번째로 선택 가능한 파라미터를 갖기도 합니다. 이 경우는 간단한 쿼리(query)로 데이터를 받습니다.</p><h3 id="post-">POST:</h3><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/1*ey6-vwsrm9RAhyoU15u6xQ.png" class="kg-image" alt="Axios로 POST 요청을 보내는 예제 코드. 이미지 속 내용은 아래 코드와 동일하다." width="755" height="412" loading="lazy"><figcaption>위에서 설명한 내용이 이미지로 삽입되어 있다. 이미지 속 내용은 아래 코드와 동일하다.</figcaption></figure><pre><code class="language-js">const Url = 'https://jsonplaceholder.typicode.com/posts';

const user = {
    name: 'Said',
    id: 21,
};
axios({
    method: 'post',
    url: Url,
    data: {
    user,
	},
})
.then((data) =&gt; console.log(data))
.catch((err) =&gt; console.log(err));</code></pre><p><a href="https://github.com/axios/axios">Axios</a>는 "Promise"를 반환합니다. 이에 익숙하다면, 여러분은 promise가 여러 요청을 실행할 수 있다는 것도 아실 겁니다. 여러분은 axios를 통해 동일한 작업을 할 수 있고, 동시에 여러 요청을 실행할 수 있습니다.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/1*40Pji4utVKPpC7-dePfC6Q.png" class="kg-image" alt="Axios의 axios.all 메서드를 통해서 두 가지 GET 요청을 보내는 예제 코드. 이미지 속 내용은 아래 코드와 동일하다." width="800" height="396" loading="lazy"><figcaption>위에서 설명한 내용이 이미지로 삽입되어 있다. 이미지 속 내용은 아래 코드와 동일하다.</figcaption></figure><pre><code class="language-js">
function getUser() {
	const userUrl = 'https://jsonplaceholder.typicode.com/users';
	return axios.get(userUrl);
}

function getPost() {
    const postUrl = 'https://jsonplaceholder.typicode.com/posts';
    return axios.get(postUrl);
}

axios.all([getUser(), getPost()])
.then((users, posts) =&gt; {
    console.log(users);
    console.log(posts);
})
.catch((err) =&gt; console.log(err));</code></pre><p>Axios는 여러 다른 메서드와 옵션을 지원합니다. <a href="https://github.com/axios/axios">이곳</a>에서 더 탐구해볼 수 있습니다.</p><h2 id="angular-httpclient">Angular HttpClient</h2><p>Angular는 Angular 앱에서 동작하는 자체 HTTP 모듈을 가지고 있습니다. RxJS 라이브러리를 사용해 비동기 요청을 처리하고 많은 옵션을 제공해 HTTP 요청을 수행하도록 합니다.</p><h3 id="angular-httpclient-">Angular HttpClient를 사용해 서버로 보낼 요청 호출하기</h3><p>Angular HttpClient를 통해 요청을 발생시키려면, Angular 앱 내부에서 우리의 코드를 실행해야 합니다. 하나 만들어보았습니다. 만일 Angular에 익숙하지 않으시다면, 저의 <a href="https://medium.freecodecamp.org/learn-how-to-create-your-first-angular-app-in-20-min-146201d9b5a7">20분 안에 첫 Angular 앱 만드는 방법</a>을 확인해보세요.</p><p>제일 먼저 해야 할 일은 <code>app.module.ts</code>에서 <code>HttpClientModule</code>을 가져오는 것입니다.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/1*iFuW5Fbp91VR5gwQ6XNMEQ.png" class="kg-image" alt="HttpClientModule 사용 예제. 이미지 속 내용은 아래 코드와 동일하다." width="758" height="632" loading="lazy"><figcaption>위에서 설명한 내용이 이미지로 삽입되어 있다. 이미지 속 내용은 아래 코드와 동일하다.</figcaption></figure><pre><code class="language-ts">import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angluar/core';
// HttpModule
import { HttpClient } from '@angular/common/http';

import { AppComponent } from './app.component';

@NgModule({
	declaration: [
    	AppComponent
    ],
	imports: [
        BrowserModule,
        // HttpModule
		HttpClientModule,
	],
    providers: [],
    bootstrap: [AppComponent],
})

export class AppModule {}</code></pre><p>그리고 요청을 처리할 서비스를 생성해야 합니다. <a href="https://angular.io/cli">Angular CLI</a>를 통해서 쉽게 이 서비스를 만들 수 있습니다.</p><pre><code class="language-js">ng g service FetchdataService</code></pre><p>이제 <code>fetchdataServce.ts</code> 서비스에서 HttpClient를 가져오고(import), 생성자(constructor) 안에 이를 주입(inject)합니다.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://cdn-media-1.freecodecamp.org/images/1*kKwELAhSSpnN8DvIgdOfcQ.png" class="kg-image" alt="HttpClientModule을 주입하는 예제. 이미지 속 내용은 아래 코드와 동일하다." width="685" height="461" loading="lazy"><figcaption>위에서 설명한 내용이 이미지로 삽입되어 있다. 이미지 속 내용은 아래 코드와 동일하다.</figcaption></figure><pre><code class="language-ts">import { Injectable } from '@angular/core';
// HttpClient를 가져옵니다.
import { HttpClient } from '@angular/common/http';

@Injectable() {
export class FetchdataService {
    constructor(private http:HttpClient) {}
    
    getData(url: string) {
    	return this.http.get(url)
	}
}</code></pre><p>그리고 <code>app.component.ts</code>에서 <code>fetchdataService</code>를 가져옵니다.</p><pre><code class="language-js">// 가져오기
import { FetchdataService } from './fetchdata.service';</code></pre><p>마지막으로 서비스를 호출하고, 실행합니다.</p><p><code>app.component.ts</code>:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/korean/news/content/images/2022/11/image.png" class="kg-image" alt="app.component.ts 파일에서 서비스를 호출하고 실행하는 예제. 이미지 속 내용은 아래 코드와 동일하다" srcset="https://www.freecodecamp.org/korean/news/content/images/size/w600/2022/11/image.png 600w, https://www.freecodecamp.org/korean/news/content/images/2022/11/image.png 788w" sizes="(min-width: 720px) 720px" width="788" height="620" loading="lazy"><figcaption><a href="https://cdn-media-1.freecodecamp.org/images/1*OrRe183Yaclt19n5ZQ194Q.png">app.component.ts 파일에서 서비스를 호출하고 실행하는 예제. 이미지 속 내용은 아래 코드와 동일하다</a></figcaption></figure><pre><code class="language-ts">@Component({
    selector: 'app-post',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.css'],
    providers: [FetchdataService],
})

export class AppComponent implements OnInit {
    posts;
    Url = 'https://jsonplaceholder.typicode.com/posts';

    // HttpClient를 주입하기
    constructor(private srv: FetchdataService) {}

    getPosts(): void {
        this.srv.getData(this.Url)
        	.subscribe((data) =&gt; 	console.log(data));
	}

    ngOnInit() {
	    this.getPost();
    }
}</code></pre><p><a href="https://stackblitz.com/edit/angular-httpclinent">Stackblitz</a>에서 데모 예제를 확인해볼 수 있습니다.</p><h2 id="--1">마치며</h2><p>우리는 JavaScript에서 HTTP 호출 요청을 보내는 가장 알려진 여러 방법을 살펴보았습니다.</p><p>시간을 내주셔서 감사합니다. 이 글이 좋았다면, 좋아요를 눌러주시고, <a href="https://twitter.com/SaidHYN">Twitter</a>에도 만나러 와주세요.</p><p>그나저나, 저는 최근에 저의 모바일 애플리케이션 중 하나를 위해 훌륭한 소프트웨어 엔지니어 그룹과 일했습니다. 이 조직은 훌륭했고, 제품은 그 어떤 회사나 제가 함께 일했던 프리랜서들보다도 훨씬 빠르게 출시되었습니다. 솔직히 다른 프로젝트에도 이 그룹을 추천하고 싶다고 생각했습니다. 연락이 필요하시면 이메일을 주세요 - <a><em>said@devsdata.com</em></a></p> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
