<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>jason.log</title>
        <link>https://velog.io/</link>
        <description>개발을 통해 많은 것을 배우고 배운 것을 나누고 싶습니다.                            글은 [시리즈] 를 통해 보기 쉽게 분류해두었습니다.</description>
        <lastBuildDate>Tue, 07 Jul 2020 06:00:45 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>jason.log</title>
            <url>https://images.velog.io/images/jason_sj/profile/47c924bb-cbe6-458b-ae8e-b63ee263b6bf/social.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. jason.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/jason_sj" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Node.js Event Loop]]></title>
            <link>https://velog.io/@jason_sj/Node.js-Event-Loop</link>
            <guid>https://velog.io/@jason_sj/Node.js-Event-Loop</guid>
            <pubDate>Tue, 07 Jul 2020 06:00:45 GMT</pubDate>
            <description><![CDATA[<h2 id="개요">개요</h2>
<p>Node.js 로 백엔드 구축을 하던 중 OOP 방법론 적용을 연습해볼 좋은 기회가 있었다. 다른 사람들의 코드를 보며 정말 많은 것을 새로 배웠고 부족함도 많이 느낄 수 있었다.</p>
<p>새롭게 알게된 개념 중에 Node.js app 이 event 기반으로 동작한다는 점이 있었고 &#39;events&#39; module 의 EventEmitter class 를 사용해 다양한 event 를 emit (발생시킴)하고 그 것을 감지해 callback 을 실행시킬 수 있다는 점을 알게 되었다. </p>
<p><strong>즉, 직접 객체 인스턴스의 함수를 호출하는 것이 아니라 객체 별로 event 를 감지하고 감지된 event 에 따라 각자 정해진 함수를 callback 형태로 실행시키는 방식이다.</strong></p>
<h3 id="여기서-의문이-발생했다">여기서 의문이 발생했다.</h3>
<p><strong>Node.js 의 event driven 방식이 내가 기존에 알던 직접 함수 호출 방식과 비교해 어떤 장점을 지니고 있는가?</strong></p>
<p><a href="https://www.tutorialspoint.com/">tutorials point</a> 라는 교육사이트에서 잘 정리된 글을 찾아 학습 및 공유의 목적으로 번역을 남겨보려 한다.</p>
<blockquote>
<p>원글 : <a href="https://www.tutorialspoint.com/nodejs/nodejs_event_loop.htm">Node.js - Event Loop</a></p>
</blockquote>
<h2 id="내용">내용</h2>
<p>[ 작성중... ]</p>
<h2 id="남은-의문점">남은 의문점</h2>
]]></description>
        </item>
        <item>
            <title><![CDATA[React - Clean Components & JSX]]></title>
            <link>https://velog.io/@jason_sj/React-Clean-Components-JSX</link>
            <guid>https://velog.io/@jason_sj/React-Clean-Components-JSX</guid>
            <pubDate>Fri, 19 Jun 2020 10:56:44 GMT</pubDate>
            <description><![CDATA[<h3 id="글을-시작하며">글을 시작하며..</h3>
<p><strong>&#39;어떻게 해야 더 가독성이 높고 효율적인 코드를 작성할 수 있을까?&#39;</strong>
요즘 React 프로젝트를 진행하며 가장 많이 고민되는 부분이다. github 에서 임의의 유사 프로젝트 repository 를 검색해보고 몇가지 장점을 따라해보기도 하며 코드의 퀄리티를 높이기 위한 시도를 해보았다.</p>
<p><strong>&#39;무엇을 따라야 할지 몰라 혼란스러울 때&#39;</strong>
하지만, 다양한 Practice <sub>(<em>&#39;관례&#39;</em> 또는 <em>&#39;방식&#39;</em> 으로 이해하는 것이 맞을 것 같다.)</sub> 들 속에서 무엇을 따르는 것이 Best 인지 가려내는 것은 초보자인 나에겐 너무 어렵고 혼란스러운 일이었다.</p>
<p>계속해서 좋은 Practice 를 찾아 헤매었고, 누군가 속 시원히 가르쳐주면 좋겠다는 생각이 간절해지기도 했다. </p>
<p>그러던 중, 너무나도 적절한 시기에 <a href="https://medium.com/">Medium</a> 에서 내가 그토록 원하던 글 하나를 추천해주었다.</p>
<br>

<hr>
<blockquote>
<p>이 글은 아래 원글을 번역 및 정리한 글입니다.
<u>원글 작성자로부터 미리 허가 받았다는 점을 밝힙니다.</u></p>
</blockquote>
<blockquote>
<p><strong>원 글 : <a href="https://itnext.io/write-clean-er-components-jsx-1e70491baded">Write clean(er) Components &amp; JSX - <em>Dana Janoskova</em>
</a></strong>
*&quot;단지, 글이 더 많은 사람을 도울 수 있다면 좋겠다.&quot;* 며 흔쾌히 허락한 원글 작성자의 생각이 너무 존경스러웠다. 나도 언젠가 도움을 줄 수 있는 개발자로 성장할 것이다.</p>
</blockquote>
<hr>
<br>

<h3 id="개요">개요</h3>
<p><strong>React 사용의 어려운 점은 명확하고 가장 좋은 practice 가 없다는 점과 모순된 정보가 많다는 점이다.</strong>
<code>(이 글을 끝까지 읽기로 결심했던 도입부이다. 가려운 곳을 긁어 줄 것 같은 느낌이 들었다.)</code></p>
<p>그래서 이미 진행 중인 프로젝트에 참여를 하게되면, 전해 받는 코드들 대부분이 많은 버그와 문제점을 갖고 있다. </p>
<p><em>그래서 나 <sub>(원글 작성자)</sub> 는 실제 프로젝트에서 직접 경험한 사례들을 바탕으로 <strong>작은 Guide 와 Refactoring solution</strong> 을 쓰기로 결정했다.</em></p>
<h3 id="1-제약없는-props-와-빈--객체">1. 제약없는 props 와 빈 {} 객체</h3>
<p>나는 single-purpose component 철학을 따른다. 즉, 너무 복잡한 100 줄짜리 긴 컴포넌트들을 피하고 모든 것을 가능한 최대한 나누려고 한다.</p>
<p><code>&lt;UserCard /&gt;</code> 라는 컴포넌트가 있다고 가정하자. 이 컴포넌트의 유일한 목적은 어떤 user object 를 받아 user data 를 보여주는 것이다.</p>
<pre><code class="language-jsx">import React from &quot;react&quot;;
import PropTypes from &quot;prop-types&quot;;

const UserCard = ({ user }) =&gt; {
  return (
    &lt;ul&gt;
      &lt;li&gt;{user.name}&lt;/li&gt;
      &lt;li&gt;{user.age}&lt;/li&gt;
      &lt;li&gt;{user.email}&lt;/li&gt;
    &lt;/ul&gt;
  );
};

UserCard.propTypes = {
  user: PropTypes.object
};

UserCard.defaultProps = {
  user: {}
};

export default UserCard;</code></pre>
<p>이 컴포넌트는 prop (user) 없이는 아무 기능을 하지 못한다. <strong>하지만 아무 prop 을 require 하고 있지 않으며 <code>Cannot access property &#39;name&#39; of undefined</code> error 를 방지하기 위해 default value 로 <code>{}</code>를 두고 있다.</strong> </p>
<p>이처럼 <code>{}</code>를 default value 로 두게되면 component 는 빈 객체를 가지고 render 된다. 즉, 화면 상의 빈 공간을 보여주는 것이다. <strong>하지만 만약 fallback value 나 skeleton loading 을 제공하지 않는다면, data 없이 <code>UserCard</code> 컴포넌트를 렌더할 이유가 없다.</strong> </p>
<p><strong>무엇이 요구되어야 하고 무엇이 요구되지 않아도 되는가?</strong></p>
<p>종종 우리는 잠시 코딩을 중단하고 이해를 해야한다. prop 중 무엇이 꼭 필요하고 무엇은 필수가 아닌지 알아두어야 한다. 그리고 <strong>필수 prop 이 없다면 컴포넌트가 render 되지 않도록 해야한다.</strong></p>
<p>그렇다면, 어떻게 작성되어야 하는가?</p>
<h3 id="2-부모-컴포넌트에서의-conditioning-render-조건부-렌더">2. 부모 컴포넌트에서의 Conditioning Render (조건부 렌더)</h3>
<pre><code class="language-jsx">import React, { useState } from &quot;react&quot;;
import PropTypes from &quot;prop-types&quot;;

export const UserCard = ({ user }) =&gt; {
  return (
    &lt;ul&gt;
      &lt;li&gt;{user.name}&lt;/li&gt;
      &lt;li&gt;{user.age}&lt;/li&gt;
      &lt;li&gt;{user.email}&lt;/li&gt;
    &lt;/ul&gt;
  );
};

UserCard.propTypes = {
  user: PropTypes.object.isRequired
};

export const UserContainer = () =&gt; {
  const [user, setUser] = useState(null);

  // do some apiCall here

  return (
    &lt;div&gt;
      {user &amp;&amp; &lt;UserCard user={user} /&gt;}
    &lt;/div&gt;
  );
};</code></pre>
<p>여기서는 <code>user</code>  객체를 <code>null</code> 로 초기화했다. 이유는 <code>&lt;UserCard /&gt;</code>  를 쉽게 conditioning render (조건 부 렌더) 할 수 있으며, 그와 동시에 초기 값으로 아무런 객체 reference 를 생성하지 않아 메모리를 아낄 수 있다는 장점 때문이다.</p>
<p>또한 data 가 없을 때 보여줄 spinner 와 메세지를 정하는 것도 더 쉬워진다. </p>
<pre><code class="language-jsx">export const UserContainer = () =&gt; {
  const [user, setUser] = useState(null);

  // do some apiCall here

  return (
    &lt;div&gt;
      {user ? &lt;UserCard user={user} /&gt; : &#39;No data available&#39;}
    &lt;/div&gt;
  );
};</code></pre>
<p><strong>반드시 기억해야할 것은 이러한 검사를 해당 컴포넌트에서 보다 부모 컴포넌트에서 하는 것이 훨씬 명확하다는 점이다.</strong> (단지 spinner 와 message 를 보여주기 위해 헛되이 컴포넌트를 렌더하는 경우를 수도 없이 본 것 같다.)</p>
<p>자식 컴포넌트인 <code>&lt;UserCard /&gt;</code>  는 user data 를 보여주는 책임만을 가지고 있다. </p>
<p>API Call 등을 통해 user 객체를 fetch 하고 무엇을 render 할지 결정하는 것은 <code>&lt;UserContainer /&gt;</code> 의 책임이 되어야 한다. 그러므로 <code>&lt;UserContainer /&gt;</code> 가 바로 fallback value 를 보여줄 최적의 장소인 것이다.</p>
<h3 id="3-nesting-을-피하고-대신-early-return-을-선택해라">3. Nesting 을 피하고 대신 Early return 을 선택해라</h3>
<p>nesting 은 일반적으로 프로그래밍 언어에서 읽고 수정하는데에 큰 혼란을 야기한다. (JavaScript, HTML, <code>{}</code> 의 조합인 JSX 에서도 마찬가지다.)</p>
<p>아마도 아래와 같은 코드를 자주 볼 것이다.</p>
<pre><code class="language-jsx">const NestedComponent = () =&gt; {
  // ...

  return (
    &lt;&gt;
      {!isLoading ? (
        &lt;&gt;
          &lt;h2&gt;Some heading&lt;/h2&gt;
          &lt;p&gt;Some description&lt;/p&gt;
        &lt;/&gt;
      ) : &lt;Spinner /&gt;}
    &lt;/&gt;
  )
}</code></pre>
<p>어떻게 개선할 수 있을까?</p>
<pre><code class="language-jsx">const NestedComponent = () =&gt; {
  // ...

  if (isLoading) return &lt;Spinner /&gt;

  return (
    &lt;&gt;
      &lt;h2&gt;Some heading&lt;/h2&gt;
      &lt;p&gt;Some description&lt;/p&gt;
    &lt;/&gt;
  )
}</code></pre>
<p>만약 우리가 Conditioning Render 를 한다면 (&#39;올바른 data가 있는지&#39; /  &#39;page 가 loading 중인지&#39; 등의 조건 확인) , 위와 같이 <strong>early return</strong> 을 선택 할 수 있다.</p>
<p><strong>이를 통해 nesting 을 피할 수 있고 HTML 과 JavaScript 조건문을 혼란스럽게 섞지 않을 수 있으며, 동료나 비 기술팀 협업자에게도 readable 한 코드를 제공할 수 있다.</strong> </p>
<blockquote>
<p>경력이 많아질 수록 누구에게나 readable 한 코드를 작성하는 것이 중요하다는 것을 깨닫게 된다. 우리의 코드는 컴퓨터 뿐만 아니라 사람들도 이해할 수 있어야 한다. reviewer 와 junior 동료들이 더 이해하기 쉬운 코드를 작성해야한다. 몇 줄의 코드를 더 쓰는 나의 희생이 나와 동료들의 시간을 장기적으로 아껴준다면, 나는 기꺼이 그 희생을 감수할 것이다.</p>
</blockquote>
<h3 id="4-jsx-내의-javascript-코드는-가능한-최소화-해라">4. JSX 내의 JavaScript 코드는 가능한 최소화 해라</h3>
<p>위에서 언급한 것처럼, JSX 는 몇가지 언어가 섞여있어 이해하기 조금 까다로운 면이 있다. Senior 인 나도 다른 사람들이 작성한 JSX 코드를 보면 충분히 readable 하지 않다는 것을 종종 발견한다.</p>
<pre><code class="language-jsx">const CustomInput1 = ({ onChange }) =&gt; {
  return (
    &lt;Input onChange={e =&gt; {
      const newValue = getParsedValue(e.target.value);
      onChange(newValue);
    }} /&gt;
  )
}</code></pre>
<p>event handler 가 모두 JSX 내에 정의되어 있고 <code>&lt;CustomInput1 /&gt;</code> 의 prop 인 onChange 또한 함께 호출되고 있다. 이 예시 코드는 동작하지만, 몇몇 사람들은 <code>return()</code> 문을 이해하는데에 애를 먹을 것이다. 실제 프로그램에서는 훨씬더 많은 element 들과 JS logic 들이 포함된다. 그리고 위의 예시처럼 JSX 내에 nesting 하여 작성하면 코드를 쓰는 입장에선 쉬울지 모르겠다.</p>
<pre><code class="language-jsx">const CustomInput2 = ({ onChange }) =&gt; {
  const handleChange = (e) =&gt; {
    const newValue = getParsedValue(e.target.value);
    onChange(newValue);
  };

  return (
    &lt;Input onChange={handleChange} /&gt;
  )
}</code></pre>
<p>이 예시는 이 전 예시보다 단 두 줄이 더 길어졌다. 하지만 logic 을 명확히 구분해두었다. JSX <code>return()</code> 문에서는 JavaScript logic 을 inline 으로 길게 작성하지 않는 것이 좋다. <strong>JSX <code>return()</code> 문에서는 HTML tree 가 어떻게 구조화되어 있는지 명확하게 볼 수 있어야하며, 이는 nesting 이 최소화될때 가장 명확해진다.</strong></p>
<h3 id="5-usecallback-과-useeffect-함께-쓰기">5. useCallback 과 useEffect 함께 쓰기</h3>
<p>hook 이 나오며 사람들은 functional 컴포넌트를 사용하기 시작했다. 컴포넌트 내에서 data 를 fetch 하기 위한 API call 이 필요해졌고 이를 위해 <code>useEffect</code> 라는 lifecycle hook 이 필요해졌다. 최초 버전의 document 에서는 <code>useEffect</code> 에 빈 dependency 배열 <code>[]</code> 을 넣어주면 hook 이 컴포넌트의 mount 와 un-mount 시 에만 동작한다고 설명되어 있다. 그리고 이 방법은 거의 모든 곳에 사용되었다. 하지만 그 후 <code>exhaustive-deps (철저한 의존성)</code> rule 이 나왔다.  </p>
<pre><code class="language-jsx">import React, { useState, useEffect } from &#39;react&#39;

import { fetchUserAction } from &#39;../api/actions.js&#39;

const UserContainer = () =&gt; {
  const [user, setUser] = useState(null);

  const handleUserFetch = async () =&gt; {
    const result = await fetchUserAction();
    setUser(result);
  };

  useEffect(() =&gt; {
    handleUserFetch();
    // eslint-disable-next-line react-hooks/exhaustive-deps 
  }, []);

  if (!user) return &lt;p&gt;No data available.&lt;/p&gt;

  return &lt;UserCard data={user} /&gt;
};</code></pre>
<p>위의 예시처럼 사람들은 <code>exhaustive-deps</code> error 를 comment out 처리하여 그냥 사용했다. 왜냐면 이해가 안됐기 때문이다. 하지만 아직도 이 rule 을 이해하지 못하고 comment out 처리하는 것은 올바른 방법이 아니다.</p>
<p>위와 같은 몇몇의 사람들이 이해하지 못한 것은 바로 <strong>&#39;<code>handleUserFetch()</code>  method 가 매 render 마다 재생성되고 있다는 점 (또는 컴포넌트가 몇번이나 render 되고 있는지)&#39;</strong> 이다. <code>useEffect</code> 내부에서 method를 호출하려면 <code>useCallback</code> 이 필요하다. 이를 통해 <code>handleUserFetch()</code> method 가 재생성되는 것을 방지할 수 있다 (단, dependency 가 변화될 때는 제외).  그리고 그로인해 infinite loop 발생 없이도 <sub>(? 이해가 안되는 부분이다.)</sub> <code>handleUserFetch()</code> 를 <code>useEffect</code> hook의 dependency 로 사용할 수 있다.</p>
<p>아래는 수정된 코드이다.</p>
<pre><code class="language-jsx">import React, { useState, useEffect, useCalllback } from &#39;react&#39;

import { fetchUserAction } from &#39;../api/actions.js&#39;

const UserContainer = () =&gt; {
  const [user, setUser] = useState(null);

  const handleUserFetch = useCalllback(async () =&gt; {
    const result = await fetchUserAction();
    setUser(result);
  }, []);

  useEffect(() =&gt; {
    handleUserFetch();
  }, [handleUserFetch]);

  if (!user) return &lt;p&gt;No data available.&lt;/p&gt;

  return &lt;UserCard data={user} /&gt;
};</code></pre>
<p><strong>만약 원하는 user 의 id 값이 <code>handleUserFetch()</code>의 매개변수로 필요하다면 id 또한 dependency 배열에 포함되어야 한다.</strong> </p>
<p><code>useEffect</code> 와 <code>useCallback</code> 을 이해하기 시작했다면, <code>useState</code> 대신 <code>useReducer</code> 를 사용해 보거나 또는 아래와 같이 useState 의 setter 함수에 function 을 매개변수로 넣는 방식을 시도해보는 것도 좋다.</p>
<pre><code class="language-jsx">const handleUserFetch = useCalllback(async () =&gt; {
  const result = await fetchUserAction();
  // setUser(result);
    setUser((prevUser) =&gt; {...})
}, []);
</code></pre>
<h3 id="6-비의존적-로직은-컨포넌트-외부로-보내기">6. 비의존적 로직은 컨포넌트 외부로 보내기</h3>
<p>아래와 같은 컴포넌트가 있다고 가정하자.</p>
<pre><code class="language-jsx">const UserCard = ({ user }) =&gt; {
  const getUserRole = () =&gt; {
    const { roles } = user;
    if (roles.includes(&#39;admin&#39;)) return &#39;Admin&#39;;
    if (roles.includes(&#39;maintainer&#39;)) return &#39;Maintainer&#39;;
    return &#39;Developer&#39;;
  }

  return (
    &lt;ul&gt;
      &lt;li&gt;{user.name}&lt;/li&gt;
      &lt;li&gt;{user.age}&lt;/li&gt;
      &lt;li&gt;{user.email}&lt;/li&gt;
      &lt;li&gt;{getUserRole()}&lt;/li&gt;
    &lt;/ul&gt;
  );
}</code></pre>
<p>사람들이 class 형 컴포넌트를 점차 쓰지 않게 되면서 위와 같은 구조의 코드를 많이 볼 수 있게 되었다.</p>
<p>이 전 예시와 같이 여기서 <code>getUserRole()</code> method 는 render 시 마다 새로 생성된다. <strong>하지만 여기서는 해당 method를 다른 곳에 dependency 로 제공 (useEffect 또는 자식 컴포넌트의 prop 으로 제공) 하는 것이 아니기 때문에 <code>useCallback</code> 을 사용하는 것은 불필요한 낭비이다.</strong> </p>
<p><strong>여기서 <code>getUserRole()</code> 은 컴포넌트 안에 선언되어 재 생성될 필요가 없는 function 임을 알아야 한다.</strong></p>
<pre><code class="language-jsx">const getUserRole = (roles) =&gt; {
  if (roles.includes(&#39;admin&#39;)) return &#39;Admin&#39;;
  if (roles.includes(&#39;maintainer&#39;)) return &#39;Maintainer&#39;;
  return &#39;Developer&#39;;
}

const UserCard = ({ user }) =&gt; {
  return (
    &lt;ul&gt;
      &lt;li&gt;{user.name}&lt;/li&gt;
      &lt;li&gt;{user.age}&lt;/li&gt;
      &lt;li&gt;{user.email}&lt;/li&gt;
      &lt;li&gt;{getUserRole(user.roles)}&lt;/li&gt;
    &lt;/ul&gt;
  );
}</code></pre>
<p>이 방법으로 <code>getUserRole()</code> 과 같은 function 은 다른 외부 파일에 선언해 import 해서 reusable 하게 쓸 수도 있다.</p>
<h3 id="7-inline-styling-을-사용하지-마라">7. inline styling 을 사용하지 마라</h3>
<p>inline styling 은 js html css 를 혼합시키고 여러가지 부정적인 결과를 초래한다. 특히 inline css 로 설정한 style 은 외부 css 에서 <code>!imoprtant</code> 없이는 수정할 수 없다.</p>
<h3 id="8-올바른-html-문법을-사용하라">8. 올바른 HTML 문법을 사용하라</h3>
<p>(웹표준<sub>HTML5</sub> 에 대한 포스트에서 다룰 예정이다.)</p>
<h3 id="9-context-overuse-를-피해라">9. Context overuse 를 피해라</h3>
<p>(이 부분은 이해가 잘 안되어 나중에 다시 학습할 예정이다.)</p>
<h3 id="마지막---글쓴이의-조언">마지막 - 글쓴이의 조언</h3>
<blockquote>
<p>I have only one advice for you — question your knowledge, your learned approaches and your code. Think and seek whether you can do better. Think outside of the box — forget React and JavaScript, look at the project as a whole and ask yourself what makes the most sense architecture-wise.</p>
</blockquote>
<p><strong>&quot;</strong> 이미 알고 있는 지식, 배운 접근법 그리고 당신의 코드를 의심해라. 당신의 코드를 더 개선할 수 있는지를 생각하고 탐구해라. &#39;React, JavaScript&#39; 라는 틀에서 벗어나 프로젝트 전체를 보고 가장 현명한 구조가 무엇일지 스스로에게 물어라. <strong>&quot;</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[이 시리즈는...]]></title>
            <link>https://velog.io/@jason_sj/%EC%9D%B4-%EC%8B%9C%EB%A6%AC%EC%A6%88%EB%8A%94-12deubxv</link>
            <guid>https://velog.io/@jason_sj/%EC%9D%B4-%EC%8B%9C%EB%A6%AC%EC%A6%88%EB%8A%94-12deubxv</guid>
            <pubDate>Fri, 19 Jun 2020 07:44:44 GMT</pubDate>
            <description><![CDATA[<p><strong>이 시리즈는...</strong></p>
<p>Server 와 Network 에 대해 공부하며 얻은 지식을 저장하는 장소입니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[이 시리즈는...]]></title>
            <link>https://velog.io/@jason_sj/%EC%9D%B4-%EC%8B%9C%EB%A6%AC%EC%A6%88%EB%8A%94</link>
            <guid>https://velog.io/@jason_sj/%EC%9D%B4-%EC%8B%9C%EB%A6%AC%EC%A6%88%EB%8A%94</guid>
            <pubDate>Fri, 19 Jun 2020 07:32:38 GMT</pubDate>
            <description><![CDATA[<p><strong>이 시리즈는...</strong></p>
<p>Vanilla JavaScript 를 공부하며 배운 지식을 기록하는 장소입니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[이 시리즈는...]]></title>
            <link>https://velog.io/@jason_sj/%EC%8B%9C%EB%A6%AC%EC%A6%88%EB%A5%BC-%EC%8B%9C%EC%9E%91%ED%95%98%EB%A9%B0</link>
            <guid>https://velog.io/@jason_sj/%EC%8B%9C%EB%A6%AC%EC%A6%88%EB%A5%BC-%EC%8B%9C%EC%9E%91%ED%95%98%EB%A9%B0</guid>
            <pubDate>Fri, 19 Jun 2020 07:21:55 GMT</pubDate>
            <description><![CDATA[<p><strong>이 시리즈는...</strong>
React 를 공부하고 코딩하며 알게된 지식을 기록하는 곳입니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[create-react-app 에서 API key 숨기기]]></title>
            <link>https://velog.io/@jason_sj/create-react-app-%EC%97%90%EC%84%9C-API-key-%EC%88%A8%EA%B8%B0%EA%B8%B0</link>
            <guid>https://velog.io/@jason_sj/create-react-app-%EC%97%90%EC%84%9C-API-key-%EC%88%A8%EA%B8%B0%EA%B8%B0</guid>
            <pubDate>Mon, 15 Jun 2020 09:36:39 GMT</pubDate>
            <description><![CDATA[<p>CRA(create react app) 에서 API key 와 같은 <strong>주요 정보</strong>를 클라이언트 단에서 또는 깃헙과 같은 공개 저장소에서 숨기기위해 <strong>환경변수</strong>를 주로 사용한다.</p>
<h3 id="환경변수-사용법">환경변수 사용법</h3>
<ol>
<li>src 폴더 외부에 .env 파일을 생성한다.</li>
<li>.env 파일에 아래와 같이 API KEY 를 저장한다.<br>반드시 REACT_APP_ 으로 시작해야한다.<pre><code> REACT_APP_API_KEY=123123</code></pre></li>
<li>js 파일내에서 사용한다.<pre><code class="language-javascript"> const apiKey = process.env.REACT_APP_API_KEY; </code></pre>
</li>
<li>환경변수가 변경되면 서버를 다시 시작해야한다.</li>
<li>.gitignore 에 .env 파일을 추가해 github 에 노출되지 않도록 한다.</li>
</ol>
<blockquote>
<p>CRA 가 아닌 <strong>Node.js</strong> 에서 env 를 사용하는 경우 &#39;dotenv&#39; 라이브러리를 npm 또는 yarn 으로부터 다운받아 사용해야한다.</p>
<pre><code class="language-javascript">require(&#39;dotenv&#39;).config();
// 파일 상단에 위 코드를 넣어준 후 3번과 같이 사용할 수 있다. </code></pre>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[무작위 요청 공격 경험기]]></title>
            <link>https://velog.io/@jason_sj/%EB%AC%B4%EC%9E%91%EC%9C%84-URL-%EC%9A%94%EC%B2%AD-%EA%B3%B5%EA%B2%A9</link>
            <guid>https://velog.io/@jason_sj/%EB%AC%B4%EC%9E%91%EC%9C%84-URL-%EC%9A%94%EC%B2%AD-%EA%B3%B5%EA%B2%A9</guid>
            <pubDate>Tue, 09 Jun 2020 11:41:08 GMT</pubDate>
            <description><![CDATA[<p>AWS EC2 인스턴스에 서빙중이 던 NodeJs 서버로 백엔드 코딩을 하던 중, 우연치 않게 무작위 요청 공격을 포착했다.</p>
<blockquote>
<p>사실 &quot;무작위 요청&quot; 이라는 용어가 맞는진 모르겠으나, 정확한 명칭을 찾지 못해 명명함</p>
</blockquote>
<pre><code class="language-javascript">const Koa = require(&quot;koa&quot;);
const app = new Koa();

app.use((ctx, next) =&gt; {
    console.log(ctx);
})

app.listen(4000, () =&gt; {
    console.log(&#39;Listening to port 4000 !!&#39;);
})</code></pre>
<p>위와 같이 Koa 프레임워크로 구축된 node 서버에 ctx (context 의 줄임말, 웹 요청과 응답에 대한 정보를 담고 있는 객체) 를 console 에 출력하는 코드를 써두고 작업 중이었는데, <strong>갑자기 아래와 같은 log 가 초당 수십개씩 출력되었다.</strong>
<del>(실제 로그를 확인해보니 분당 약 20개 정도의 request 가 약 20분동안 발생했다. 하지만 초당 수십개로 체감할 정도로 많이 당황했었나보다.)</del></p>
<pre><code class="language-bash">{
  request: {  // 클라이언트의 요청 정보 부분
    method: &#39;GET&#39;,
    url: &#39;/program/index.php&#39;, // 클라이언트가 요청한 url (base url 은 생략된다.)
    header: {
      host: &#39;127.0.0.1:4000&#39;,
      connection: &#39;close&#39;,
      &#39;user-agent&#39;: &#39;Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:52.0) Gecko/20100101 Firefox/52.0&#39;,
      &#39;cache-control&#39;: &#39;no-cache&#39;
    }
  },
  response: {    // 서버의 응답 정보 부분
    status: 404,    // 해당하는 url 이 서버에 존재하지 않아 404 로 응답
    message: &#39;Not Found&#39;,
    header: [Object: null prototype] {}
  },
  app: { subdomainOffset: 2, proxy: false, env: &#39;development&#39; },
  originalUrl: &#39;/program/index.php&#39;,
  req: &#39;&lt;original node req&gt;&#39;,
  res: &#39;&lt;original node res&gt;&#39;,
  socket: &#39;&lt;original node socket&gt;&#39;
}


// 위와 유사한 log 가 수십개 찍힘</code></pre>
<p>어떤 request 의 url 에는 /hack.php 등 심상치 않은 이름들도 보여 순간적으로 꽤 무서웠다...</p>
<p><strong>알고 지내던 개발자분에게 문의를 했고 그 결과 이는 공개된 web server 에 흔히 일어나는 무작위 해킹 시도라는 것을 알게되었다.</strong></p>
<p>좀 더 자세히 상황을 파악하기 위해 해당 web server 를 serving 하고 있는 nginx 의 access log 를 확인했다.</p>
<blockquote>
<p>ubuntu 에서 nginx 의 access log 확인하는 명령어 :
(default 위치일 경우)</p>
<pre><code>$ cat /var/log/nginx/access.log</code></pre><p>nginx access log 의 default 양식 해석하는 법
<a href="https://stackoverflow.com/questions/52004391/how-to-read-nginx-access-log">&gt;&gt; stackoverflow 참조</a></p>
</blockquote>
<h3 id="nginx-access-log-내용-일부">nginx access log 내용 (일부)</h3>
<pre><code>(클라이언트 ip 주소) - - [09/Jun/2020:06:25:01 +0000] &quot;GET /phpMyAdmin.old/index.php HTTP/1.1&quot; 200 11 &quot;-&quot; &quot;Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:52.0) Gecko/20100101 Firefox/52.0&quot;
(클라이언트 ip 주소) - - [09/Jun/2020:06:25:01 +0000] &quot;GET /pma-old/index.php HTTP/1.1&quot; 200 11 &quot;-&quot; &quot;Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:52.0) Gecko/20100101 Firefox/52.0&quot;
(클라이언트 ip 주소) - - [09/Jun/2020:06:25:09 +0000] &quot;GET /pma-old/index.php HTTP/1.1&quot; 200 11 &quot;-&quot; &quot;Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:52.0) Gecko/20100101 Firefox/52.0&quot;
(클라이언트 ip 주소) - - [09/Jun/2020:06:25:09 +0000] &quot;GET /claroline/phpMyAdmin/index.php HTTP/1.1&quot; 200 11 &quot;-&quot; &quot;Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:52.0) Gecko/20100101 Firefox/52.0&quot;
(클라이언트 ip 주소) - - [09/Jun/2020:06:25:13 +0000] &quot;GET /claroline/phpMyAdmin/index.php HTTP/1.1&quot; 200 11 &quot;-&quot; &quot;Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:52.0) Gecko/20100101 Firefox/52.0&quot;
(클라이언트 ip 주소) - - [09/Jun/2020:06:25:13 +0000] &quot;GET /typo3/phpmyadmin/index.php HTTP/1.1&quot; 200 11 &quot;-&quot; &quot;Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:52.0) Gecko/20100101 Firefox/52.0&quot;
(클라이언트 ip 주소) - - [09/Jun/2020:06:25:17 +0000] &quot;GET /typo3/phpmyadmin/index.php HTTP/1.1&quot; 200 11 &quot;-&quot; &quot;Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:52.0) Gecko/20100101 Firefox/52.0&quot;
(클라이언트 ip 주소) - - [09/Jun/2020:06:25:21 +0000] &quot;GET /phpma/index.php HTTP/1.1&quot; 200 11 &quot;-&quot; &quot;Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:52.0) Gecko/20100101 Firefox/52.0&quot;
(클라이언트 ip 주소) - - [09/Jun/2020:06:25:25 +0000] &quot;GET /phpma/index.php HTTP/1.1&quot; 200 11 &quot;-&quot; &quot;Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:52.0) Gecko/20100101 Firefox/52.0&quot;
... (생략)

그 외 요청된 url 
/mysql/admin/index.php
/shopdb/index.php
/sql/index.php
등등...</code></pre><p>다양한 URL 을 대상으로 GET 요청이 이뤄졌다. 그리고 그 URL 들은 모두 실제 있을법한 URL 이었다. 만약 내 server 가 저 중 하나의 URL 이라도 사용하고 있었다면 요청한 data 를 응답했거나 올바른 URL 이지만 body 정보가 valid 하지 않다는 등의 error message 를 응답했을 것이다.
(다행히 내 server 는 해당 URL 들을 사용하고 있지 않기 때문에 404 not found error 로 응답했을 것이다.)</p>
<blockquote>
<p>대부분의 대상 URL 에서 보듯 DB 해킹을 주로 노리고 있고 만약 response 를 받아낸다면 해당 DB root 계정의 암호를 알아내기 위해 Brute Force(무작위 대입) 공격으로 이어지는 경우가 많다고 한다.</p>
</blockquote>
<blockquote>
<p>또는, server 가 data 를 응답하는 request URL 을 알아내어 한번에 대량의 트래픽을 발생시키는 DDoS 공격도 가능하다.</p>
<p>물론 개인 포트폴리오나 토이프로젝트 서버에 DDoS 공격을 할 가능성은 희박해보이지만 만약 당하게 된다면, AWS 에 고액의 서버 이용료를 내게될지도 모른다. (개인 이용자들을 위한 면책 정책이 있다고 들어는 봤지만 자세히 알아보진 않았다.) </p>
</blockquote>
<h3 id="남은-의문점">남은 의문점</h3>
<ul>
<li>console 에 출력한 context 객체 내에 response.status 가 404 인것과 상반되게 nginx access log 에서 확인한 상태는 200 으로 기록되어있다. (&quot;GET (url)&quot; 200) 
대상 url 은 해당 server 에 존재하지 않는데 왜 200 일까..?</li>
</ul>
<h3 id="대응">대응</h3>
<ul>
<li>domain 없이 ip 주소를 접속 url 로 사용하고 있는 내 서버는 EC2 인스턴스 재부팅을 통해 새 ip 주소를 받을 예정이다.</li>
<li>내 blog, github 들을 샅샅히 살펴보며 어떻게 ip 주소가 노출됐는지 알아봤지만 정확히는 알 수 없었다. 우선 몇가지 의심되는 부분을 개선했고 앞으로 조심해야겠다.</li>
<li>서버의 acess 정보를 DB 에 모두 기록해두고 정기적으로 확인할 예정이다.</li>
</ul>
<h3 id="결론">결론</h3>
<ol>
<li>무작위 요청 공격은 매우 흔한 일이고 그 자체만으로 치명적이라고 할 순 없지만, 이어질 수 있는 2차 공격에는 충분히 대비를 해두어야 한다.</li>
<li>DB root 계정의 암호는 귀찮더라도 복잡하게 설정하자.</li>
<li>포트폴리오나 토이프로젝트만을 위한 서버일지라도 서버의 access 정보를 관리하는 것이 좋다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[⏰ JS 비동기처리에 새롭게 접근하기]]></title>
            <link>https://velog.io/@jason_sj/JS-%EB%B9%84%EB%8F%99%EA%B8%B0%EC%B2%98%EB%A6%AC%EC%97%90-%EC%83%88%EB%A1%AD%EA%B2%8C-%EC%A0%91%EA%B7%BC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@jason_sj/JS-%EB%B9%84%EB%8F%99%EA%B8%B0%EC%B2%98%EB%A6%AC%EC%97%90-%EC%83%88%EB%A1%AD%EA%B2%8C-%EC%A0%91%EA%B7%BC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 09 Apr 2020 16:18:07 GMT</pubDate>
            <description><![CDATA[<p>Javascript 는 다양한 방법의 비동기처리 문법을 제공합니다.
정리가 필요하여 공부를 하다가, 각 문법들의 &quot;관계&quot;와 &quot;발전 방향&quot;에 대해 접근한 좋은 설명을 들을 기회가 있어 배운 내용과 저의 생각을 정리해보려 합니다.</p>
<blockquote>
<p>이 글은 각 문법에 대한 자세한 설명은 다루지 않습니다.</p>
</blockquote>
<hr>
<p>우선, Javascript 비동기처리 문법의 발전은 크게 아래 두 가지를 목표로 이루어지고 있다고 생각합니다.</p>
<ul>
<li>비동기 코드의 <strong>동작 순서 제어</strong></li>
<li>비동기 코드의 <strong>가독성 향상</strong></li>
</ul>
<p>아래 예시들을 통해 더 자세히 다뤄보겠습니다.</p>
<h2 id="가장-기본적인-비동기-코드">가장 기본적인 비동기 코드</h2>
<pre><code class="language-javascript">  function asyncWork() {
    setTimeout(() =&gt; {
      console.log(&#39;첫번째&#39;);
    }, 3000 * Math.random());

    setTimeout(() =&gt; {
      console.log(&#39;두번째&#39;);
    }, 3000 * Math.random());

    setTimeout(() =&gt; {
      console.log(&#39;세번째&#39;);
    }, 3000 * Math.random());
  }
  asyncWork();</code></pre>
<p><strong>이 코드의 문제점은 코드의 진행 순서를 제어할 수 없다는 점입니다.</strong></p>
<p>얼핏보면, 순서대로 나열된 setTimeout() 함수들이 각각 최대 3초의 random 시간 후 차례대로 동작할 것 같지만,
<em>실제로 세 함수는 거의 동시에 시작한다고 볼 수 있습니다.</em></p>
<p><strong>그 이유는 비동기 함수들이 동기적 순서로 나열되어 있기 때문입니다.</strong></p>
<p>동기적 진행에 따라 setTimeout() 을 차례대로 실행시키지만, 동기적 진행은 background 에서 개별적으로 실행되는 비동기 코드의 존재를 인지하지 못합니다.
즉, 굉장히 짧은 시간만에 다음 동기적 순서의 코드를 실행시키죠.</p>
<p><strong>그러므로, 실제 출력은</strong>
<strong>Random 순서로 문자열 &#39;첫번째&#39;, &#39;두번째&#39;, &#39;세번째&#39; 가 출력됩니다.</strong></p>
<h2 id="순서-제어-문제를-해결한-코드">순서 제어 문제를 해결한 코드</h2>
<pre><code class="language-javascript">  function asyncWork() {
    setTimeout(() =&gt; {
      console.log(&#39;첫번째&#39;);
      setTimeout(() =&gt; {
        console.log(&#39;두번째&#39;);
        setTimeout(() =&gt; {
          console.log(&#39;세번째&#39;);
        }, 3000 * Math.random());
      }, 3000 * Math.random());
    }, 3000 * Math.random());
  }
  asyncWork();</code></pre>
<p><strong>이 코드의 실제 출력은</strong>
<strong>문자열 &#39;첫번째&#39;, &#39;두번째&#39;, &#39;세번째&#39; 가 순서대로 출력됩니다.</strong></p>
<p>각각의 setTimeout() 함수는 먼저 실행될 setTimeout() 의 callback 함수에서 호출되고 있습니다.</p>
<p>각 callback 함수 내에서는 동기적 순서로 console.log(&quot;순서 표시&quot;) 와 다음 setTimeout() 을 호출해주면서 순서 제어 문제를 해결했습니다.</p>
<p><strong>하지만, 이 코드의 문제점은 가독성이 낮다는 것입니다.</strong>
만약 서버와 다수의 비동기 통신이 필요한 함수라면, 단일 통신을 하나하나 호출할 수 밖에 없는 위 코드에서 문제는 더욱 심각해지겠죠.
이러한 코드를 <strong>Callback Hell</strong> 이라고도 부릅니다.</p>
<h2 id="가독성-문제를-완화한-코드">가독성 문제를 완화한 코드</h2>
<pre><code class="language-javascript">  async function asyncWork() {
    try {

      // 첫번째
      let result = await new Promise((resolve, reject) =&gt; {
        setTimeout(() =&gt; {
          console.log(&#39;첫번째&#39;);
          resolve(&#39; result 에 저장되는 값&#39;);
        }, 3000 * Math.random());
      });

      console.log(&#39;동기적 코드 하나를 삽입해보자.&#39;);

      // 두번째
      await new Promise((resolve, reject) =&gt; {
        setTimeout(() =&gt; {
          console.log(&#39;두번째&#39; + result);
          reject(&#39;catch 의 error 에 저장되는 값&#39;);  // catch 로 이동
        }, 3000 * Math.random());
      });

      // 세번째 
      // 위에서 reject 되어 실행되지 않음
      await new Promise((resolve, reject) =&gt; { 
        setTimeout(() =&gt; {
          console.log(&#39;세번째&#39;);
        }, 3000 * Math.random()); 
      });

    } catch (error) {
      console.log(error); // &quot;catch 의 error 에 저장되는 값&quot; 출력되는 곳
    } finally {
      console.log(&#39;finally 는 무조건 실행&#39;);
    }
  }
  asyncWork();

    // 출력 :
      // 첫번째
      // 동기적 코드 하나를 삽입해보자.
      // 두번째 &#39;result 에 저장되는 값&#39;
      // catch 의 error 에 저장되는 값
      // finally 는 무조건 실행</code></pre>
<p><strong>위 코드는 Promise 의 사용이 미숙해 여전히 낮은 가독성 문제를 보이고 있습니다.</strong>
하지만, 더 이상 비동기 코드들이 Callback Hell 로 엮여 있지 않고 동기적 순서(위 -&gt; 아래) 로 나열되어 있음을 확인할 수 있습니다.</p>
<p>여기서 짚고 갈 수 있는 중요한 개념이 있습니다.</p>
<p><strong>``</strong>
<strong>await 는 &quot;async function 내에 한해서&quot; 동기적 순서의 코드 진행을 멈추고 비동기 코드가 종료될 때까지 기다린다.</strong>
<del>(&#39;async function 내에 한해서&#39; 가 중요한 이유는 다음 포스트에 올릴 예정입니다.)</del>
<strong>``</strong>
위 코드 중간에 삽입된 console.log() 는 동기적 코드임에도 비동기 코드들의 존재를 인지하고 종료를 기다린 후 실행됨을 확인할 수 있습니다.
<strong>즉, await 가 동기적 순서를 제어한다는 의미입니다.</strong></p>
<p>이제 async / await , Promise 를 이용하면 비동기 코드도 동기적 순서로 평범하게 나열할 수 있음을 확인했습니다.
<strong>활용해서 가독성을 크게 향상시킬 수 있겠죠. (다음 코드에서 확인)</strong></p>
<blockquote>
<p>주제와는 관련이 적지만, 
Promise 는 매개변수인 resolve 와 reject 중 반드시 하나를 구현부에서 호출해야합니다. 이는 resolve 또는 reject 함수가 해당 Promise 의 상태를 pending (대기) 에서 <strong>fulfilled</strong> (이행) 또는 <strong>rejected</strong> (거부) 로 변경 시키기 때문입니다.
await 는 Promise 의 상태를 인식하여 성공 또는 실패 여부를 판단합니다. </p>
<p><em>만약 resolve, reject 중 아무 것도 호출되지 않으면 해당 Promise 는 pending 상태로 남게되고 다음 코드 (.then() 과 동일) 는 실행되지 않습니다.</em></p>
</blockquote>
<h2 id="가독성을-더-향상시킨-코드-마지막">가독성을 더 향상시킨 코드 (마지막)</h2>
<pre><code class="language-javascript">  // 공통된 비동기 작업을 Promise 하나로 묶어줌
  function make_promise(number) {
    return new Promise((resolve, reject) =&gt; {
      setTimeout(() =&gt; {
        console.log(number);
        resolve(number * 10);
      }, 3000 * Math.random());
    });
  }

  // Promise consume(사용)은 async 함수 내에서 함
  async function asyncWork() {
    try {
      result = await make_promise(1); // number 값
      result = await make_promise(result);
      result = await make_promise(result);
      result = await make_promise(result);
    } catch (error) {
      console.log(error);
    } finally {
      console.log(&#39;무조건 실행&#39;);
    }
  }
  asyncWork();

    // 출력 :
      // 1
      // 10
      // 100
      // 1000
      // 무조건 실행</code></pre>
<p>드디어 마지막 코드입니다.</p>
<p>제가 글의 처음에 언급했던 주제에 맞춰 풀어보겠습니다.</p>
<ol>
<li><p><strong>동작 순서 제어</strong></p>
<ul>
<li><strong>async function</strong> 과 <strong>await</strong> 를 이용했으므로 순서를 제어할 수 있습니다.</li>
</ul>
</li>
<li><p><strong>가독성 향상</strong></p>
<ul>
<li><strong>동일한 동작을 하는 비동기 코드들을 하나의 Promise 에 묶어주어</strong> 가독성을 향상시켰습니다. 조금 더 심화하여 매개변수를 받아 처리할 수 있도록 function 을 선언해 Promise 를 return 해주었습니다.</li>
<li><strong>async / await</strong> 로 비동기 코드들을 동기적 순서로 나열한 것이 가독성을 향상시키기도 했습니다.</li>
</ul>
</li>
</ol>
<hr>
<p>Javascript 비동기처리 문법의 발전 방향에 동의하시나요?
코드의 기능을 익히는데만 급급했던 저에게는 적지 않은 충격을 줬던 접근법이었습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[의외로 간단한 CORS]]></title>
            <link>https://velog.io/@jason_sj/%EC%9D%98%EC%99%B8%EB%A1%9C-%EA%B0%84%EB%8B%A8%ED%95%9C-CORS</link>
            <guid>https://velog.io/@jason_sj/%EC%9D%98%EC%99%B8%EB%A1%9C-%EA%B0%84%EB%8B%A8%ED%95%9C-CORS</guid>
            <pubDate>Thu, 09 Apr 2020 12:54:39 GMT</pubDate>
            <description><![CDATA[<p>CORS 에 대해 처음 알게되고 조사를 했을 때, 인터넷에서 찾은 대부분의 설명들이 저에게는 너무 어려워 이해하기가 쉽지 않았습니다.   </p>
<p>이 글에서는 CORS 를 쉽게 설명해보고 마지막으로 NODE JS 로 구축된 서버를 이용해 아주 간단한 테스트까지 진행해보려 합니다. </p>
<h2 id="cors-의-개념">CORS 의 개념</h2>
<p><del>웹 애플리케이션의 Client 는 본인의 출처 (Origin - 도메인, 프로토콜, 포트) 와 출처가 다른 Server 에 리소스 (Resource - server에 요청한 data) 를 요청할 때 교차 출처 HTTP 요청을 실행합니다. Web server 입장에서 교차 출처 HTTP 요청을 허용하거나 거절하는 것이 CORS 입니다.</del></p>
<p>예시를 통해 좀 더 쉽게 접근해보겠습니다.   </p>
<p><strong>기상청에서 제공하는 공공데이터 RESTful API 서버에 날씨 data를 요청하고 받아와 날씨 정보 UI 를 보여주는 Web App 을 개발한다고 가정하겠습니다.</strong>   </p>
<blockquote>
<p>편의를 위해 우리는 Server 개발은 하지 않고 Client 만 개발한다고 가정하겠습니다. 그리고 우리의 Client 페이지는 정적 싸이트 호스팅 서비스인 Netlify 에 의해 serve 되고 있다고 가정하겠습니다.</p>
</blockquote>
<p>기상청 API 의 도메인이 <a href="http://www.weather.co.kr">www.weather.co.kr</a> 이라고 가정하고,<br>Netlify 에서 호스팅되고 있는 우리의 Client 페이지의 도메인은 myApp.netlify.com 이라고 가정하겠습니다.</p>
<p>이제 우리는 기상청 API 에 날씨 data 를 요청해야합니다.  </p>
<p><strong>하지만 우리 Client 페이지와 기상청 API 가 다른 도메인을 가지고 있는 것 눈치 채셨나요?</strong></p>
<p>Client 의 입장에서 도메인이 다른 Web Server 에 data, 즉 resource 를 요청하는 것. 그 것을 바로 교차 출처 요청 (Cross Origin Request) 라고 하는데요.</p>
<p><strong>Resource 의 주체인 Web server (API) 입장에서 그 요청을 허용하거나 거절하는 것을 CORS (Cross Origin Request Share) 라고 볼 수 있습니다.</strong></p>
<h2 id="node-js-서버-express-에서-cors-를-허용하기">NODE JS 서버 (Express) 에서 CORS 를 허용하기</h2>
<p>우선 CORS 설정은 default 로 거절되어 있습니다. 이를 승인하려면 Server 에 직접 허용 설정을 넣어줘야 합니다.</p>
<pre><code class="language-javascript"> app.get(&quot;/&quot;, (req, res) =&gt; {
      res.header(&quot;Access-Control-Allow-Origin&quot;, &quot;*&quot;);
      res.header(&quot;Access-Control-Allow-Headers&quot;, &quot;X-Requested-With&quot;);
      // 위 두 줄을 추가해주면 CORS 를 허용하게 됩니다.

      res.send({
        corsTest: &quot;succeed&quot;
      });
      // 테스트 목적으로 response (응답할) 데이터를 정해주었습니다.
});</code></pre>
<br/>

<p>설정 완료 후, 해당 서버의 도메인에 접속하면 개발자모드에서 Response Headers 에 위에서 추가한 설정이 포함된 것을 확인할 수 있습니다.</p>
<p><img src="https://images.velog.io/images/jason_sj/post/56a06be7-2461-402f-8705-52b7a2272340/pic2.JPG" alt="브라우저에서 CORS 설정 확인"></p>
<p>이제 이 Web Server(API) 는 다른 도메인 의 Client 가 data 를 요청을 해도 Response 를 보내겠죠.</p>
<h2 id="간단히-test-해보기-">간단히 Test 해보기 :</h2>
<blockquote>
<p>제가 구축한 서버가 글 작성 시점에 http 통신만 가능한 상태라 마찬가지로 http 로 통신하는 website 에서만 Test가 가능한 것을 알게되었습니다. 그리고 어렵지 않게 http 싸이트인 &quot;통계청&quot; 홈페이지를 찾을 수 있었습니다.</p>
</blockquote>
<p><img src="https://images.velog.io/images/jason_sj/post/ade249b9-3948-4ffa-b1c1-0acfe669e3a8/Untitled%202.png" alt="통계청 싸이트에서 테스트"></p>
<p>위 사진의 console 을 보면, fetch 요청에 따라, 제가 서버에 정해둔 data 가 정확히 Response 된 것을 확인할 수 있습니다.</p>
<p><strong>즉, kostat.go.kr 라는 도메인을 가진 통계청 웹싸이트에서 저의 Web Server 에 교차 출처 요청 (Cross Origin Request) 을 보내어 Resource 를 받아낸 것 입니다.</strong></p>
<p>이와 같은 방법으로 응답 받은 data를 이용해 Client 에 표시할 UI 를 만드는 것도 가능할 것입니다.</p>
]]></description>
        </item>
    </channel>
</rss>