<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>192cm.html</title>
        <link>https://velog.io/</link>
        <description>일어나... 취업해야지...</description>
        <lastBuildDate>Fri, 16 Dec 2022 13:52:13 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>192cm.html</title>
            <url>https://velog.velcdn.com/images/untiring_dev/profile/f2049fd0-0a92-455a-a3aa-e0df59942072/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. 192cm.html. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/untiring_dev" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[React] useReducer Hook]]></title>
            <link>https://velog.io/@untiring_dev/React-useReducer-Hook</link>
            <guid>https://velog.io/@untiring_dev/React-useReducer-Hook</guid>
            <pubDate>Fri, 16 Dec 2022 13:52:13 GMT</pubDate>
            <description><![CDATA[<p><code>useReducer</code>는 단일 상태를 관리하는 <code>useState</code>보다 더 복잡한 state를 관리할 때 유용하게 사용할 수 있는 React Hook이다.</p>
<h1 id="usestate와-usereducer의-비교">useState와 useReducer의 비교</h1>
<p>간단한 카운트 앱으로 <code>useState</code>와 <code>useReducer</code>의 차이를 알아보자.</p>
<h3 id="usestate의-카운트-앱">useState의 카운트 앱</h3>
<pre><code class="language-js">import { useState } from &quot;react&quot;;

const App = () =&gt; {
  const [age, setAge] = useState(42);

  // 이전의 state 값을 기반하여 증가/감소
  const plusAge = () =&gt; {
    setState(prevState =&gt; prevState + 1)
  };

  const minusAge = () =&gt; {
    setState(prevState =&gt; prevState - 1)
  };

  return (
    &lt;&gt;
      &lt;h2&gt;{age}&lt;/h2&gt;
      &lt;button onClick={minusAge}&gt;한살 빼기&lt;/button&gt;
      &lt;button onClick={plusAge}&gt;한살 더하기&lt;/button&gt;
    &lt;/&gt;
  );
};

export default App;</code></pre>
<p>이렇게 <code>useState</code>를 이용하여 카운트 앱을 만들었다.
여기서 <code>useState</code>의 두번째 set 함수에 인자로 콜백 함수를 사용하여 현재 state의 값(<u>화면에 출력된 값</u>)을 기반하여 1씩 증가/감소하게 구현하였다.</p>
<h3 id="usereducer의-카운트-앱">useReducer의 카운트 앱</h3>
<p>그럼 이제 <code>useReducer</code>를 이용한 카운트 앱을 보자.</p>
<pre><code class="language-js">import { useReducer } from &quot;react&quot;;

const reducer = (state, action) =&gt; {
  switch (action.type) {
    case &quot;PLUS_AGE&quot;:
      return { ...state, age: state.age + 1 };
    case &quot;MINUS_AGE&quot;:
      return { ...state, age: state.age - 1 };
    default:
      return state;
  }
};

const App = () =&gt; {
  const [age, dispatch] = useReducer(reducer, { age: 42 });

  const plusAge = () =&gt; {
    dispatch({ type: &quot;PLUS_AGE&quot; });
  };

  const minusAge = () =&gt; {
    dispatch({ type: &quot;MINUS_AGE&quot; });
  };

  return (
    &lt;&gt;
      &lt;h2&gt;{age.age}&lt;/h2&gt;
      &lt;button onClick={minusAge}&gt;한살 빼기&lt;/button&gt;
      &lt;button onClick={plusAge}&gt;한살 더하기&lt;/button&gt;
    &lt;/&gt;
  );
};

export default App;</code></pre>
<p>모두 아래 결과처럼 동일한 기능을 한다.
<img src="https://velog.velcdn.com/images/untiring_dev/post/2567e91d-af53-4b74-bfb6-03a0cfe2c1b3/image.gif" alt=""></p>
<p>근데 <code>useState</code>의 카운트 앱보다 훨씬 복잡해보인다.</p>
<p>하지만 <code>useReducer</code>는 이런 간단한 앱보단 다수의 복합적인 state를 다루는 앱(예를 들면, 회원가입 폼 등)에 더 효과적으로 사용될 수 있다.
나는 <code>useReducer</code>를 개인적으로 사용해보고 정리를 하기 위해 간단한 카운트 앱을 했지만 복잡한 상태 관리가 필요하다면 직접 사용해보길 바란다.</p>
<h1 id="usereducer-단계별로-살펴보기">useReducer 단계별로 살펴보기</h1>
<h2 id="usereducer-호출">useReducer 호출</h2>
<p>우선 하나씩 보도록 하자.
Hook을 사용하기 위해 import가 필요하다.</p>
<pre><code class="language-js">import { useReducer } from &quot;react&quot;;</code></pre>
<p>import 후 사용하기 위해 아래처럼 작성한다.</p>
<pre><code class="language-js">const [state, dispatchFunc] = useReducer(ReducerFunc, initialState);</code></pre>
<p>여기서 하나씩 살펴보면,
<code>useReducer</code>도 <code>useState</code>처럼 반환값이 배열인데 첫번째 요소가 <code>state</code>, 두번째 요소가 <code>dispatchFunc</code>이다.</p>
<p>이것은 위의 <code>useReducer</code>의 카운트 앱에 사용된 예시이다.</p>
<pre><code class="language-js">const [age, dispatch] = useReducer(reducer, { age: 42 });</code></pre>
<h3 id="state">state</h3>
<p>여기서 <code>state</code>는 말 그대로 <code>useState</code>와 동일한 역할의 현재의 state 변수이다. 
<code>useReducer</code>의 두번째 인자인 <code>initialState</code>가 최초로 할당된다.</p>
<h3 id="dispatchfunc">dispatchFunc</h3>
<p><code>dispatchFunc</code>는 &#39;상호작용에 따라 변경할 수 있는 디스패치 함수&#39;이다.
쉽게 말하자면 화면에 있는 <code>state</code>를 업데이트하기 위한 함수이다.</p>
<p>중요한 점은 <strong><code>dispatchFunc</code>가 호출이 되면 <code>state</code>를 다른 값으로 업데이트하고 사용된 컴포넌트의 렌더링을 발생시킨다.</strong></p>
<h3 id="usereducerreducerfunc-initialstate">useReducer(ReducerFunc, initialState)</h3>
<h4 id="reducerfunc">ReducerFunc</h4>
<p><code>useReducer</code>의 첫번째 인자 <code>ReducerFunc</code>는 &quot;<code>dispatchFunc</code>가 전달한 <code>action</code> 객체
의 <code>type</code> 프로퍼티에 따라 <code>state</code>를 어떻게 업데이트할지 결정하는 함수이다.</p>
<blockquote>
<p><code>type</code> 프로퍼티가 아닌 다른 것으로도 업데이트 작업을 결정할 수 있다.
단, 보편적으로 어떻게 업데이트를 할지 식별하기 위해 <code>type</code> 프로퍼티를 포함한 <code>action</code> 객체를 사용한다.</p>
</blockquote>
<h4 id="initialstate">initialState</h4>
<p><code>useReducer</code>의 두번째 인자 <code>initialState</code>는 반환하는 배열의 첫번째 요소인 <code>state</code>의 초기값을 설정하는 인자이다.
물론 <code>useState</code>처럼 해당 컴포넌트가 처음 렌더링되었을 때 최초로 <code>state</code>에 할당되며 이후의 렌더링에는 무시되는 값이다.</p>
<h2 id="dispatchfunc-호출">dispatchFunc 호출</h2>
<p>카운트 앱의 <code>dispatchFunc</code>를 보도록 하자.</p>
<pre><code class="language-js">// age의 증가를 위한 함수
const plusAge = () =&gt; {
    dispatch({ type: &quot;PLUS_AGE&quot; });
};

// age의 감소를 위한 함수
const minusAge = () =&gt; {
    dispatch({ type: &quot;MINUS_AGE&quot; });
};</code></pre>
<p><code>dispatchFunc</code>를 보면 뭔가 당황스러울 것이다.
왜냐하면 <code>dispatchFunc</code>는 <code>state</code>를 업데이트하는 함수인데 왜 <code>useState</code>처럼 증가/감소하는 로직이 없기 때문이다. 또한 반환값도 없다.</p>
<p><code>useReducer</code>는 <code>useState</code>와 달리 복잡한 state를 관리하기 때문에 인자로 <code>ReducerFunc</code>에 전달되는 <code>action</code> 객체의 <code>type</code> 프로퍼티에 따라 state 업데이트 동작(action)을 결정하게 된다.</p>
<p>쉽게 말하자면 <code>dispatchFunc</code>는 그저 <strong>state를 어떻게 업데이트할 것인지 결정하기 위해 조건</strong>을 <code>ReducerFunc</code>에 전달한다고 생각하면 된다.</p>
<p>그래서 위처럼 <code>{ type: &quot;PLUS_AGE&quot; }</code>와 <code>{ type: &quot;PLUS_AGE&quot; }</code>인 <code>action</code> 객체를 <code>ReducerFunc</code>에 전달한다.</p>
<p>여기서 <code>type</code>의 값이 모두 <strong>대문자</strong>인 것을 볼 수 있는데 이것은 관례라고 하니 만약 다른 컨벤션을 원한다면 변경해도 된다.</p>
<blockquote>
<p><a href="https://beta.reactjs.org/apis/react/useReducer">React Docs Beta 공식 문서</a>에는 <strong>소문자</strong>로 표기되어 있다.</p>
</blockquote>
<h2 id="reducerfunc-실행state-업데이트">ReducerFunc 실행(state 업데이트)</h2>
<pre><code class="language-js">// ReducerFunc
const reducer = (state, action) =&gt; {
  switch (action.type) {
    case &quot;PLUS_AGE&quot;:
      return { ...state, age: state.age + 1 };
    case &quot;MINUS_AGE&quot;:
      return { ...state, age: state.age - 1 };
    default:
      return state;
  }
};

const App = () =&gt; {
  // ...

  const plusAge = () =&gt; {
    dispatch({ type: &quot;PLUS_AGE&quot; });
  };

  const minusAge = () =&gt; {
    dispatch({ type: &quot;MINUS_AGE&quot; });
  };

  // ...
}</code></pre>
<p><code>ReducerFunc</code>는 대체적으로 <strong>컴포넌트 외부</strong>에 위치한다. 왜냐하면 컴포넌트가 재렌더링이 되면서 새로 생성될 필요가 없기 때문이다.</p>
<p>이제 <code>dispatchFunc</code> 호출로 인해 <code>type</code> 프로퍼티가 있는 <code>action</code> 객체를 전달 받았다고 가정해보자.</p>
<p>예를 들어 <code>plusAge</code> 함수가 버튼 클릭으로 호출이 되어 <code>dispatchFunc</code>가 호출되어 <code>action</code> 객체가 <code>ReducerFunc</code>에 전달되었다.</p>
<p>그럼 <code>ReducerFunc</code>의 내부의 <code>switch</code>문에 의해 해당되는 <code>action.type</code>에 따라 state의 업데이트 방식이 결정된다. </p>
<p>여기서 궁금한 점이 있다면 <code>ReducerFunc</code>의 매개변수 <code>state</code>와 <code>action</code>일 것이다.</p>
<ul>
<li><code>state</code>는 <strong>화면에 있는 기존의 값</strong>을 가리키며</li>
<li><code>action</code>는 <code>dispatchFunc</code>로 부터 전달받은 <code>action</code> 객체이다.</li>
</ul>
<p>이것을 로그로 출력해보면 아래와 같다.</p>
<pre><code class="language-js">// plusAge 함수를 클릭 이벤트로 호출했을 때

const reducer = (state, action) =&gt; {
  console.log(state, action) // { age : 42 }, { type : &quot;PLUS_AGE&quot; }
  switch (action.type) {
    case &quot;PLUS_AGE&quot;:
      return { ...state, age: state.age + 1 };
    case &quot;MINUS_AGE&quot;:
      return { ...state, age: state.age - 1 };
    default:
      return state;
  }
};</code></pre>
<p>처음 클릭 이벤트로 인해 호출되었기 때문에 첫번째 매개변수 <code>state</code>에는 <code>useReducer</code> 호출 단계에서 설정했던 <code>initialState</code>가 출력되었다.</p>
<pre><code class="language-js">                                                👇
const [age, dispatch] = useReducer(reducer, { age: 42 });</code></pre>
<p>그리고 두번째 매개변수 <code>action</code>은 <code>dispatchFunc</code>로부터 전달받은 <code>action</code> 객체가 출력되었다.</p>
<pre><code class="language-js">const plusAge = () =&gt; {
    dispatch({ type: &quot;PLUS_AGE&quot; });
                        👆
};</code></pre>
<p>여기선 <code>action</code> 객체의 <code>type</code>이 <code>&quot;PLUS_AGE&quot;</code>이기 때문에 switch문의 첫번째 case의 반환값이 새로운 state 값으로 업데이트된다.</p>
<blockquote>
<h4 id="스프레드-연산자를-사용해서-기존의-state을-전개한-이유는-🤔">스프레드 연산자(...)를 사용해서 기존의 state을 전개한 이유는? 🤔</h4>
<p>이 카운트 앱에는 굳이 필요가 없어보인다. 왜냐하면 기존에 <code>initialState</code>로 설정한 값의 프로퍼티는 <code>age</code>만 존재하기 때문에 굳이 스프레드 연산자를 사용할 필요없이 <code>{ age : state.age + 1 }</code>만 해주면 된다. <br>
다만, <strong>만약 age 말고도 다른 프로퍼티가 존재할 때</strong> 그냥 <code>{ age : state.age + 1 }</code>만 작성한다면 <strong>&quot;age와 다른 프로퍼티를 포함한 객체&quot;</strong>를 <strong>&quot;age 프로퍼티만 가진 객체&quot;</strong>로 완전히 교체해버리는 문제가 생긴다. <br>
그래서 이를 방지하기 위해 <u>기존의 state를 전개하고 이후에 업데이트할 값을 작성해서 기존의 state에 오버라이드 해주는 것</u>이다.</p>
</blockquote>
<p>기존의 state의 <code>age</code> 값에 <code>1</code>을 더하는 것을 알 수 있다.</p>
<h2 id="usereducer-주의할-점">useReducer 주의할 점</h2>
<ul>
<li><code>useReducer</code> 호출은 반드시 사용할 컴포넌트의 최상위 레벨에서 해야한다. 절대 루프나 조건문에서 호출해서는 안된다.</li>
<li>객체(<code>state</code>, <code>action</code>)의 불변성을 유지해야한다.<pre><code class="language-js">// React의 불변성을 위해 아래처럼 직접 업데이트해서는 안된다.
case &quot;PLUS_AGE&quot;:
return state.age++;
case &quot;MINUS_AGE&quot;:
return state.age--;</code></pre>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] useState Hook]]></title>
            <link>https://velog.io/@untiring_dev/React-useState-Hook</link>
            <guid>https://velog.io/@untiring_dev/React-useState-Hook</guid>
            <pubDate>Tue, 13 Dec 2022 06:10:55 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://beta.reactjs.org/apis/react/useState">React Docs Beta</a> API를 보고 학습한 내용입니다.</p>
</blockquote>
<p><code>useState</code>는 컴포넌트에 상태 변수를 생성할 수 있는 React Hook이다.</p>
<h3 id="사용법">사용법</h3>
<p>React로부터 <code>useState</code>를 가져온 후 <u><strong>컴포넌트의 최상위 레벨</strong></u>에서 선언하여 컴포넌트에 state를 추가한다.
<code>useState</code>는 배열을 반환하는데 그것을 <strong>구조 분해</strong>를 해서 주로 사용한다.</p>
<pre><code class="language-js">import { useState } from &#39;react&#39;;

const App = () =&gt; {
  // 컴포넌트의 최상위 레벨에 위치
  const [state, setState] = useState(initialState)
  // ...
}</code></pre>
<blockquote>
<h4 id="배열의-구조분해할당array-destructuring이란">배열의 구조분해할당(Array Destructuring)이란?</h4>
<p>Array의 값 또는 Object의 속성을 개별 변수로 압축을 해제할 수 있게 해주는 JavaScript 표현식</p>
</blockquote>
<pre><code class="language-js">const [a, b] = [10, 20];
console.log(a) // 10
console.log(b) // 20</code></pre>
<ul>
<li><p><code>state</code> : &#39;현재&#39; 상태의 값, 컴포넌트가 최초로 렌더링될 때 <code>initialState</code>가 할당되며 이후 업데이트 시 <code>initialState</code>를 무시하고 업데이트된 값이 할당된다.</p>
</li>
<li><p><code>setState</code> : state의 값을 변경하는 set 함수이다.</p>
<blockquote>
<p><strong>[state, setState]</strong>는 컨벤션으로 관습적으로 state의 이름에 set을 붙여 useState를 사용한다.</p>
</blockquote>
</li>
<li><p><code>initialState</code> : 컴포넌트가 최초 렌더링시 state에 자동으로 할당되는 초기 값이다. 초기 값으로 어떤 타입이든 들어올 수 있다.</p>
</li>
</ul>
<br>

<h3 id="set-함수-호출-시-컴포넌트는-다시-렌더링된다">set 함수 호출 시 컴포넌트는 다시 렌더링된다.</h3>
<p>set 함수를 호출하면 state의 값을 업데이트를 해서 반영하기 위해 컴포넌트가 다시 렌더링된다.</p>
<p>아래의 예시는 버튼을 클릭하면 state의 값이 업데이트되면서 App 컴포넌트가 다시 렌더링하는지 보기 위한 예제이다.</p>
<pre><code class="language-js">import { useState } from &#39;react&#39;;

const App = () =&gt; {
  const [state, setState] = useState(&#39;상태&#39;);
  const onClick = () =&gt; {
    setState(&#39;업데이트된 상태&#39;);
  };
  console.log(&#39;App 컴포넌트 렌더링&#39;);

  return &lt;button onClick={onClick}&gt;{state}&lt;/button&gt;;
};

export default App;</code></pre>
<p>결과는 아래처럼 작성한 로그가 2번 출력되는 것을 볼 수 있다.
첫번째는 최초 렌더링 시에 출력되었고 두번째는 state가 업데이트된 후에 출력된 것이다.
<img src="https://velog.velcdn.com/images/untiring_dev/post/70ca2e2e-0109-43d9-9b24-3d8b5b451458/image.gif" alt=""></p>
<h3 id="객체를-업데이트하자">객체를 업데이트하자</h3>
<p>만약 객체를 state로 한다고 가정해보자.</p>
<pre><code class="language-js">const [obj, setObj] = useState({ name : &quot;현우&quot;, age : 30, gender : &quot;male&quot; });</code></pre>
<p>여기서 객체를 업데이트하려면 업데이트할 프로퍼티만 작성하면 안된다. 만약 업데이트할 프로퍼티만 작성하면 아래처럼 기존의 객체를 업데이트할 객체로 완전히 오버라이드 되기 때문에 반드시 업데이트를 하지 않는 프로퍼티도 작성을 해줘야한다.</p>
<pre><code class="language-js">const [obj, setObj] = useState({ name : &quot;현우&quot;, age : 30, gender : &quot;male&quot; });

setObj({ name: &quot;철수&quot; });
console.log(obj); // { name : &quot;철수&quot; }</code></pre>
<p>그래서 기존의 객체를 업데이트를 편하게 하기 위해 <strong>스프레드 연산자(전개 연산자)</strong>를 사용하면 된다.</p>
<pre><code class="language-js">const [obj, setObj] = useState({ name : &quot;현우&quot;, age : 30, gender : &quot;male&quot; });

setObj({ ...obj, name: &quot;철수&quot; });
console.log(obj); // { name : &quot;철수&quot;, age : 30, gender : &quot;male&quot; }</code></pre>
<p>스프레드 연산자를 <strong>반드시 앞에</strong> 작성해야 업데이트할 프로퍼티의 값을 새로운 값으로 업데이트할 수 있다.
만약 뒤에 작성하면 기존의 상태와 동일한 상태를 가지게 된다.
업데이트할 새로운 상태 값인 <code>name : &quot;철수&quot;</code>를 기존의 상태 값인 <code>name : &quot;현우&quot;</code>가 덮어버리기 때문이다.</p>
<pre><code class="language-js">setObj({ name: &quot;철수&quot;, ...obj });
console.log(obj); // { name : &quot;현우&quot;, age : 30, gender : &quot;male&quot; }</code></pre>
<br>

<h3 id="이전-상태를-기반으로-상태를-업데이트하기">이전 상태를 기반으로 상태를 업데이트하기</h3>
<p><code>useState</code>의 set 함수를 이용해서 이전 상태를 기반으로 상태를 업데이트 할 수 있다.</p>
<pre><code class="language-js">function handleClick() {
  setAge(age + 1); // setAge(42 + 1)
  setAge(age + 1); // setAge(42 + 1)
  setAge(age + 1); // setAge(42 + 1)
}</code></pre>
<p>위의 예시를 보면 상태인 컴포넌트 내부의 상태인 <code>age</code>는 초기값이 <code>42</code>라고 가정할 때 <code>hanleClick</code> 함수를 호출 시 42에 1이 세 번 더해져 <code>45</code>가 되는 것이 아닌 <strong>1이 한번만 더해진 <code>43</code></strong>이 된다.
이것은 set 함수를 호출했을때 React가 <u><strong>&#39;다음 렌더링&#39;</strong></u>에 state를 업데이트를 하려고 준비하기 때문이다. </p>
<p>즉, 위의 함수의 순서는 이렇다. </p>
<ul>
<li>첫번째 setAge가 호출되면 age의 값은 <code>42</code>이므로 <code>setAge(42+1)</code>이며 React가 &#39;다음 렌더링&#39;에 새로운 state의 값으로 업데이트를 준비한다.</li>
<li>두번째 setAge가 호출되면 여전히 age는 <code>42</code>이므로 <code>setAge(42+1)</code>이며 React가 &#39;다음 렌더링&#39;에 새로운 state의 값으로 업데이트를 준비한다.</li>
<li>세번째 setAge가 호출되면 여전히 age는 <code>42</code>이므로 <code>setAge(42+1)</code>이며 React가 &#39;다음 렌더링&#39;에 새로운 state의 값으로 업데이트를 준비한다.</li>
</ul>
<p>그래서 이전 상태를 기반으로 업데이트해야하는 경우라면 set 함수의 <strong>콜백 함수</strong>를 인자로 해서 사용하면 이전 상태를 기반으로 상태를 업데이트할 수 있다.
위의 코드를 이렇게 고치면 <code>handleClick</code> 함수 호출 시 <code>3</code>이 더해진다.</p>
<pre><code class="language-js">function handleClick() {
  setAge(prevState =&gt; prevState + 1); // setAge(42 =&gt; 42 + 1)
  setAge(prevState =&gt; prevState + 1); // setAge(43 =&gt; 43 + 1)
  setAge(prevState =&gt; prevState + 1); // setAge(44 =&gt; 44 + 1)
}</code></pre>
<br>

<h3 id="업데이트를-했는데-왜-업데이트가-안된-값이-나올까">업데이트를 했는데 왜 업데이트가 안된 값이 나올까?</h3>
<p>set 함수를 호출해서 state를 업데이트했는데 로그를 출력해보면 왜 여전히 이전의 값만 출력이 될까?
내가 React에서 useState를 사용해보면서 가장 많이 겪었던 문제였다.</p>
<p>아래의 코드는 Docs의 예제인데 결과를 자세히 보면 <code>setCount</code>를 실행한 이후에도 로그에는 0이 찍히고 있다.</p>
<pre><code class="language-js">function handleClick() {
  console.log(count);  // 0

  setCount(count + 1); // Request a re-render with 1
  console.log(count);  // Still 0!

  setTimeout(() =&gt; {
    console.log(count); // Also 0!
  }, 5000);
}</code></pre>
<p>이것은 상태가 snapshot처럼 작동하기 때문이다. 상태를 업데이트하면 새로운 상태 값으로 렌더링이 요청이 되지만 이미 실행 중인 이벤트 핸들러의 JavaScript 변수인 <code>count</code>에는 영향을 주지 못하기 때문이다.</p>
<p><strong>만약 해당 함수에서 다음 상태의 값이 필요하다면 set 함수에 전달하기 전에 JavaScript 변수에 저장</strong>하면 된다.</p>
<pre><code class="language-js">function handleClick() {
  const nextCount = count + 1;

  setCount(nextCount);

  console.log(count); // 0
  console.log(nexCount); // 1
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] React-router-dom@5 버전에서 발생한 에러]]></title>
            <link>https://velog.io/@untiring_dev/React-React-router-dom5-%EB%B2%84%EC%A0%84%EC%97%90%EC%84%9C-%EB%B0%9C%EC%83%9D%ED%95%9C-%EC%97%90%EB%9F%AC</link>
            <guid>https://velog.io/@untiring_dev/React-React-router-dom5-%EB%B2%84%EC%A0%84%EC%97%90%EC%84%9C-%EB%B0%9C%EC%83%9D%ED%95%9C-%EC%97%90%EB%9F%AC</guid>
            <pubDate>Thu, 08 Dec 2022 13:19:04 GMT</pubDate>
            <description><![CDATA[<h2 id="발생-상황">발생 상황</h2>
<p>React에서 Router를 설정하고 있었는데 에러가 발생했다.</p>
<blockquote>
<p>Failed prop type: Invalid prop &#39;component&#39; supplied to &#39;Route&#39;: the prop is not a valid React component
<strong>잘못된 <code>props</code>인 &#39;component&#39;가 <code>Route</code>에 제공되었다. 이 <code>props</code>는 유효한 React 컴포넌트가 아니다.</strong></p>
</blockquote>
<p>에러가 발생한 코드는 아래와 같다.</p>
<pre><code class="language-js">import { BrowserRouter as Router, Route } from &quot;react-router-dom&quot;;
import Users from &quot;./user/pages/Users&quot;;

const App = () =&gt; {
  return (
    &lt;Router&gt;
      {/* Route에 접속하려면 도메인 뒤에 path에 적용한 것을 입력해야함 */}
      {/* &lt;Route path=&quot;/&quot;&gt;
        &lt;Users /&gt;
      &lt;/Route&gt; */}
      &lt;Route path=&quot;/&quot; component={&lt;Users/&gt;} /&gt;
    &lt;/Router&gt;
  );
};

export default App;</code></pre>
<p>물론 아래처럼 &#39;component&#39; <code>props</code>를 사용하지 않고 <code>Route</code>의 오프닝 태그와 클로징 태그 사이에 넣어줘도 정상적으로 동작하지만 다른 방법을 확실히 알아가는 것이 중요하니 이 방법으로 해보려 했다. </p>
<h2 id="에러-원인">에러 원인</h2>
<p>에러 메세지에서 힌트를 준 것처럼 <code>Route</code>에 올바르지 않은 <code>props</code>를 전달했거나 <code>props</code>에 올바른 값을 전달하지 못해서 발생한 에러이다.</p>
<h2 id="문제-해결">문제 해결</h2>
<p>우선 React-router-dom 공식 문서에서 올바른 <code>props</code>인지 확인해봤다.
내 React-router-dom은 @5.3.4 버전이기 때문에 <code>@5.x</code>의 공식문서를 확인했다.</p>
<blockquote>
<h2 id="component"><strong>component</strong></h2>
<p>A React component to render only when the location matches. It will be rendered with route props.
<strong>location이 일치할 때만 이 React 컴포넌트를 렌더한다. 이것은 Route props로 렌더할 것이다.</strong></p>
</blockquote>
<p>우선 component라는 props는 있기 때문에 사용할 수 있다.
그러면 <code>props</code>에 넣는 값이 잘못된 것이니 공식 문서에서 사용한 예시를 봤다.</p>
<p>내가 한 Route의 component porps의 값은 <code>&lt;User /&gt;</code>이다.</p>
<pre><code class="language-js">&lt;Route path=&quot;/&quot; component={&lt;Users /&gt;} /&gt;</code></pre>
<p>하지만 React-router-dom의 component 사용 예시는 아래와 같다.</p>
<pre><code class="language-js">// import한 객체를 사용하고 있다...?
&lt;Route path=&quot;/user/:username&quot; component={User} /&gt;</code></pre>
<p>나는 User라는 컴포넌트를 JSX 구문으로 넣었고 React-router-dom의 예시는 컴포넌트를 import한 User 객체 자체를 값으로 적용했다.</p>
<p>이유를 몰랐는데 다른 에러 메세지에 완전 직접적인 힌트를 주고 있었다...</p>
<blockquote>
<p>type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: <Users />. Did you accidentally export a JSX literal instead of a component?
<strong>타입이 유효하지 않습니다. -- 문자열 또는 클래스/함수가 필요하지만 <code>&lt;Users /&gt;</code>를 얻었습니다. 컴포넌트 대신 JSX 리터럴을 실수로 보냈습니까?</strong></p>
</blockquote>
<p>나는 JSX 구문을 값으로 넣었는데 component <code>props</code>의 값으로는 문자열이나 클래스/함수가 필요하다고 한다.</p>
<p>그래서 아래처럼 고쳐주면 해결!</p>
<pre><code class="language-js">import { BrowserRouter as Router, Route } from &quot;react-router-dom&quot;;
import Users from &quot;./user/pages/Users&quot;;

const App = () =&gt; {
  return (
    &lt;Router&gt;
      &lt;Route path=&quot;/&quot; component={Users} /&gt;
    &lt;/Router&gt;
  );
};

export default App;</code></pre>
<p>앞으로 에러 메세지를 자세히 살펴보는 습관을 길러야겠다... 😅</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Express] Express의 정적 Assets 사용하기]]></title>
            <link>https://velog.io/@untiring_dev/Express-Express%EC%9D%98-%EC%A0%95%EC%A0%81-Assets-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@untiring_dev/Express-Express%EC%9D%98-%EC%A0%95%EC%A0%81-Assets-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 05 Dec 2022 14:55:21 GMT</pubDate>
            <description><![CDATA[<p><a href="https://expressjs.com/ko/starter/static-files.html">Express 정적 파일 사용하기 Guide</a></p>
<h2 id="정적-assets-사용">정적 Assets 사용</h2>
<h3 id="정적-assets을-사용하기-전에-정적static-파일은-무엇일까">정적 Assets을 사용하기 전에 정적(Static) 파일은 무엇일까?</h3>
<p>정적 파일을 제공한다는 것은 대표적인 예로 클라이언트 요청에 따라 이미지나 CSS나 JavaScript와 같은 파일을 제공하는 것이다.</p>
<h3 id="정적-assets-사용하기">정적 Assets 사용하기</h3>
<p>Express에서 정적 Assets 파일을 사용하려면 아래의 <code>express</code> 객체의 메서드를 사용해야한다.</p>
<p><code>express.static()</code>은 미들웨어로 요청이 들어오면 응답을 내보내는 식으로 동작한다.
그래서 모든 요청이 들어올 때마다 인자를 호출하는 <code>app.use()</code> 메서드와 같이 사용한다.</p>
<pre><code class="language-js">app.use(express.static(&lt;dirName&gt;))</code></pre>
<p>이 <code>static()</code> 메서드에 우리가 제공하고 싶은 Assets 폴더를 인수로 전달한다.
아래의 코드는 <code>app.css</code> 파일이 있는 public 디렉토리의 콘텐츠 전체를 제공해서 접근할 수 있게 한다.
여기서 <code>/</code> 같은 Path를 붙일 필요없이 디렉토리의 이름만 입력을 하면 된다.</p>
<pre><code class="language-css">/* app.css */
body{
  background-color : sandy;
}</code></pre>
<pre><code class="language-js">app.use(express.static(&#39;public&#39;))</code></pre>
<p>그리고 템플릿 파일에 <code>app.css</code>를 적용했다.</p>
<pre><code class="language-ejs">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
  &lt;link&gt;
    &lt;meta charset=&quot;UTF-8&quot; /&gt;
    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot; /&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&gt;
    &lt;title&gt;&lt;%= name %&gt;&lt;/title&gt;
    &lt;!-- public 디렉토리의 콘텐츠 전체를 static()의 인자로 제공했기 때문에 접근할 수 있다. --&gt;
    &lt;!-- /를 넣고 사용해야한다. --&gt;
    &lt;link rel=&quot;stylesheet&quot; href=&quot;/app.css&quot;&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;h1&gt;Browsing The &lt;%= name %&gt; subreddit&lt;/h1&gt;
    &lt;h2&gt;&lt;%= description %&gt;&lt;/h2&gt;
    &lt;p&gt;&lt;%= subscribers %&gt; Total Subscribers&lt;/p&gt;
    &lt;hr /&gt;
    &lt;% for(let post of posts) { %&gt;
    &lt;article&gt;
      &lt;p&gt;&lt;%= post.title %&gt; - &lt;strong&gt;&lt;%= post.author %&gt;&lt;/strong&gt;&lt;/p&gt;
      &lt;% if(post.img){ %&gt;
      &lt;img src=&quot;&lt;%= post.img %&gt;&quot; alt=&quot;&lt;%= post.title %&gt;&quot; /&gt;
      &lt;% } %&gt;
    &lt;/article&gt;
    &lt;% } %&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<p><code>static()</code>에 public 디렉토리의 콘텐츠를 모두 제공했기 때문에 <code>app.css</code>에 접근이 가능해져 사용할 수 있었다.</p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/04ba5ba9-4122-41a3-9d53-43e5d489eac7/image.png" alt=""></p>
<p>정적 파일에 접근이 가능해져서 아래처럼 <code>localhost:3000/app.css</code> 같은 방식으로 정적 파일에도 접근할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/d3727818-73a8-4b53-aea1-1119ffbcc6ef/image.png" alt=""></p>
<h2 id="경로-문제-해결">경로 문제 해결</h2>
<p>하지만 이전의 views 디렉토리의 경로 문제처럼 <strong>한 단계 상위 폴더로 더 나가서 실행할 경우</strong> 경로 문제가 생긴다.</p>
<p>한 단계 상위 폴더로 나가서 실행하면,
<img src="https://velog.velcdn.com/images/untiring_dev/post/21934c08-2d80-4177-b459-05e6d12f9eba/image.png" alt=""></p>
<p>경로 문제가 발생해서 아래의 <code>background-color</code> 속성이 적용되지 않은 것을 확인할 수 있다.
또한 콘솔에도 <code>app.css</code> 파일을 찾을 수 없다고 한다.</p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/a98f7559-a5a7-4517-9a53-c38e09eed429/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/2273a517-1d98-4ce8-ab68-99f37c6d6559/image.png" alt=""></p>
<p>그 이유도 이전 views 디렉토리와 동일하게 한 단계 상위 폴더에서 <code>app.css</code> 파일을 찾기 때문에 없는 것이다.</p>
<p>그래서 실행하는 index.js 파일이 있는 경로를 아래처럼 절대 경로로 설정해야한다.
<code>__dirname</code>가 index.js 파일의 절대 경로가 되고 public을 추가하면 public 디렉토리의 절대 경로가 된다.</p>
<pre><code class="language-js">app.use(express.static(path.join(__dirname, &quot;public&quot;)));</code></pre>
<p>그럼 다시 스타일 시트가 적용된 화면을 볼 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/03228531-fc81-4fbd-a13a-30d5a7d7b0d9/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Express] EJS의 조건문과 반복문]]></title>
            <link>https://velog.io/@untiring_dev/Express-EJS%EC%9D%98-%EC%A1%B0%EA%B1%B4%EB%AC%B8%EA%B3%BC-%EB%B0%98%EB%B3%B5%EB%AC%B8</link>
            <guid>https://velog.io/@untiring_dev/Express-EJS%EC%9D%98-%EC%A1%B0%EA%B1%B4%EB%AC%B8%EA%B3%BC-%EB%B0%98%EB%B3%B5%EB%AC%B8</guid>
            <pubDate>Mon, 05 Dec 2022 13:39:14 GMT</pubDate>
            <description><![CDATA[<h2 id="ejs-조건문">EJS 조건문</h2>
<pre><code class="language-ejs">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot; /&gt;
    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot; /&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&gt;
    &lt;title&gt;Random&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;h1&gt;Your random number is &lt;%= num %&gt;&lt;/h1&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>이 템플릿은 라우터로부터 랜덤한 숫자 Num을 <code>res.render()</code> 메서드의 두번째 인자인 객체의 형태로 전달받고 있다.
그 후 <code>&lt;%=</code> 구문으로 HTML에 출력되고 있다.</p>
<h3 id="ejs-if문-조건부-렌더링">EJS if문 조건부 렌더링</h3>
<p>근데 만약 이 랜덤한 값이 짝수일 때 <code>h2</code>태그로 짝수인지 홀수인지 출력하려면 어떻게 해야할까?</p>
<p>우선 ejs의 <code>&lt;%=</code>안에는 숫자를 연산도 했었고 지금처럼 변수까지 넣었었다.
그럼 if문을 넣으면 어떨까?
조금 지저분한 코드이긴 하지만 이렇게 넣어보면...</p>
<pre><code class="language-ejs">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot; /&gt;
    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot; /&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&gt;
    &lt;title&gt;Random&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;h1&gt;Your random number is &lt;%= num %&gt;&lt;/h1&gt;
    &lt;!-- if문을 이렇게 사용하는게 맞나..? --&gt;
    &lt;%= if(num % 2 === 0){ %&gt;

    &lt;%= } %&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>문법 에러가 난다. 즉 if문은 저렇게 사용할 수 없다는 것이다.
<img src="https://velog.velcdn.com/images/untiring_dev/post/d305a3f7-b031-4eb1-8e4b-51d3c26e0502/image.png" alt=""></p>
<p>그래서 HTML에 출력하지 않고 로직만 통제하는 방식으로는 가능하다.
<code>&lt;%=</code>는 출력하는 구문이지만 <code>&lt;%</code>는 출력하지 않고 로직만 다룰 수가 있다.</p>
<pre><code class="language-ejs">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot; /&gt;
    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot; /&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&gt;
    &lt;title&gt;Random&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;h1&gt;Your random number is &lt;%= num %&gt;&lt;/h1&gt;
    &lt;% if(num % 2 === 0){ %&gt;
    &lt;h2&gt;That is an even number.&lt;/h2&gt;
    &lt;% } else { %&gt;
    &lt;h2&gt;That is an odd number.&lt;/h2&gt;
    &lt;% } %&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>이렇게 작성하면 정상적으로 동작한다!
<img src="https://velog.velcdn.com/images/untiring_dev/post/3d5dfd59-4f41-4719-96e5-5ae41e63329c/image.gif" alt=""></p>
<p>여기서 더 간단하게 작성하려면 &quot;삼항 연산자&quot;를 사용하여 조건부로 렌더링하면 동일한 결과를 얻을 수 있다.</p>
<pre><code class="language-ejs">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot; /&gt;
    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot; /&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&gt;
    &lt;title&gt;Random&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;h1&gt;Your random number is &lt;%= num %&gt;&lt;/h1&gt;
    &lt;!-- if문과 동일한 결과를 얻는 삼항 연산자 사용 --&gt;
    &lt;h2&gt;
    &lt;%= num%2 === 0 ? &quot;That is an even number.&quot; :&quot;That is an odd number.&quot; %&gt;
    &lt;/h2&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<h2 id="ejs-반복문">EJS 반복문</h2>
<p>이제 반복문을 알아보자.
우선 <code>/cats</code> 라우터를 생성하고 cats라는 상수에 데이터 베이스에서 가져오는 데이터라고 생각하고 임의의 배열을 할당했다.</p>
<pre><code class="language-js">app.get(&quot;/cats&quot;, (req, res) =&gt; {
  // 이 임의의 배열은 데이터 베이스에서 가져온다고 가정한다.
  const cats = [&quot;Blue&quot;, &quot;Rocket&quot;, &quot;Monty&quot;, &quot;Stephanie&quot;, &quot;Winston&quot;];
  res.render(&quot;cats&quot;, { cats })
});</code></pre>
<p>그리고 템플릿인 cats.ejs 파일을 생성하고 기본 구조의 HTML을 작성하고 cats 데이터를 사용할 수 있도록 <code>&lt;%=</code> 구문을 사용하였다.</p>
<pre><code class="language-ejs">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot; /&gt;
    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot; /&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&gt;
    &lt;title&gt;All Cats&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;h1&gt;All The Cats&lt;/h1&gt;
    &lt;!-- 반복문 사용 --&gt;
    &lt;p&gt;&lt;%= cats %&gt;&lt;/p&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>우리가 전달한 배열 데이터는 잘 나오고 있다.
<img src="https://velog.velcdn.com/images/untiring_dev/post/6a18393b-e2bd-429e-a9c9-90d8bfc685a2/image.png" alt=""></p>
<p>이것은 리스트 형식으로 출력되게 하고 싶다면 반복문을 사용해야한다.
반복문 또한 조건문과 다르지 않게 출력되지 않는 <code>&lt;%</code> 구문을 사용하여 로직은 출력하지 않고 결과만 출력이 되게 할 수 있다.</p>
<pre><code class="language-ejs">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot; /&gt;
    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot; /&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&gt;
    &lt;title&gt;All Cats&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;h1&gt;All The Cats&lt;/h1&gt;
    &lt;!-- 반복문 사용 --&gt;
    &lt;ul&gt;
    &lt;!-- for...of 문을 사용해서 배열의 요소를 반복시킨다. 이 반복문의 로직은 출력되지 않는다. --&gt;
      &lt;% for (let cat of cats){%&gt;
      &lt;!-- &lt;%= 구문을 사용해서 값이 출력된다. --&gt;
      &lt;li&gt;&lt;%= cat %&gt;&lt;/li&gt;
      &lt;% } %&gt;
    &lt;/ul&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/637ba772-1109-4c49-beaa-8b54e09f2584/image.png" alt=""></p>
<p>반복문을 이용하는데 예시로 든 <code>for...of</code>만 가능한게 아닌 모든 반복문을 위와 같이 사용할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Express] EJS 보간(interpolation) 구문을 이용한 템플릿에 데이터 전달하기]]></title>
            <link>https://velog.io/@untiring_dev/Express-EJS-%EB%B3%B4%EA%B0%84interpolation-%EA%B5%AC%EB%AC%B8%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%ED%85%9C%ED%94%8C%EB%A6%BF%EC%97%90-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%84%EB%8B%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@untiring_dev/Express-EJS-%EB%B3%B4%EA%B0%84interpolation-%EA%B5%AC%EB%AC%B8%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%ED%85%9C%ED%94%8C%EB%A6%BF%EC%97%90-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%84%EB%8B%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 05 Dec 2022 12:53:34 GMT</pubDate>
            <description><![CDATA[<p>EJS 같은 템플레이팅 엔진을 사용하는 이유는 로직을 더하고 데이터를 보충해서 구성하기 위함이다.
쉽게 말하자면 템플레이팅 엔진은 <strong>반복이나 조건이 있는 여러 값</strong>이 들어가는 HTML의 성능을 올리는 것이 목적이다.</p>
<p>그러기 위해서 EJS 구문을 확실히 익히는 것 또한 중요하다고 할 수 있다.</p>
<blockquote>
<p>EJS 구문을 공부하기 전에 VSCode Extension인 <a href="https://marketplace.visualstudio.com/items?itemName=DigitalBrainstem.javascript-ejs-support">EJS language support</a>을 설치하면 EJS 구문을 하이라이팅을 해줘서 기본 HTML 태그와 구분이 쉬워진다.</p>
</blockquote>
<p>EJS 공식 문서를 참조하면 Tags라는 것이 있는데 이것은 HTML이 아니니 HTML로 취급하지 말라고 지시하기 위해 사용하는 구문이다.</p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/37c6f0a5-0651-46f3-ad0d-6007ef73081c/image.png" alt=""></p>
<p>예를 들면 <code>&lt;%= %&gt;</code> 안에 <code>4 + 5 + 1</code>인 연산을 넣으면 JavaScript가 연산하듯이 결과값을 출력해준다. 즉 이 구문이 HTML이 아니니 HTML이 아니라 JavaScript의 구문이라고 인식을 하는 것이다.</p>
<p>하지만 템플릿에서 로직을 더하는 것은 좋지 않은 방법이다. 왜냐하면 템플릿은 결과를 출력할 때에 집중하는 것이 더 효율적이기 때문이다.
그래서 <strong>템플릿에서 로직을 실행하는 것보다 더 나은 방법으로 결과값을 만든 후 템플릿에게 전달하는 방식</strong>을 권장한다.</p>
<p>예를 들자면 현재 <code>localhost:3000/rand</code>를 요청하면 random.ejs가 렌더링된다.</p>
<pre><code class="language-js">app.get(&quot;/rand&quot;, (req, res) =&gt; {
  res.render(&quot;random&quot;);
});</code></pre>
<p>random.ejs에는 1 ~ 10까지의 숫자 중 무작위로 렌더링해주는 로직이 작성되어있다.</p>
<pre><code class="language-ejs">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot; /&gt;
    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot; /&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&gt;
    &lt;title&gt;Random&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
      &lt;!-- EJS 구문 : 1 ~ 10 사이의 무작위 숫자 렌더링 로직 --&gt;
    &lt;h1&gt;Your random number is &lt;%= Math.floor(Math.random() * 10) + 1 %&gt;&lt;/h1&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>하지만 템플릿에 로직을 더하는 것을 좋지 않기 때문에 무작위 숫자를 먼저 생성 후 템플릿에 전달하는 방식으로 수정을 하면 된다.
여기서 <code>res.render()</code> 메서드의 두번째 인자에 객체 형식으로 프로퍼티를 작성해주면 이 객체가 첫번째 인자인 템플릿에 전달되게 된다.</p>
<pre><code class="language-js">app.get(&quot;/rand&quot;, (req, res) =&gt; {
  // 먼저 무작위 숫자를 생성하고
  const num = Math.floor(Math.random() * 10) + 1;
  // render의 두번째 인자로 적용한다.
  res.render(&quot;random&quot;, { num : num });
});</code></pre>
<p>템플릿에서 전달된 객체를 사용할 때는 <code>key</code> 값으로 사용하면 된다.</p>
<pre><code class="language-ejs">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot; /&gt;
    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot; /&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&gt;
    &lt;title&gt;Random&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
      &lt;!-- 라우트에서 전달한 두번째 인자로 인해 rand 값을 사용할 수 있음 --&gt;
    &lt;h1&gt;Your random number is &lt;%= num %&gt;&lt;/h1&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<h3 id="reqparams와-ejs--구문-사용">req.params와 EJS &lt;%= 구문 사용</h3>
<p>우선 subreddit.ejs 템플릿을 생성하고 라우팅을 했다.</p>
<pre><code class="language-ejs">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot; /&gt;
    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot; /&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&gt;
    &lt;title&gt;&lt;%= subreddit %&gt;&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;h1&gt;Browsing The &lt;%= subreddit %&gt; subreddit&lt;/h1&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<pre><code class="language-js">app.get(&quot;/r/:subreddit&quot;, (req, res) =&gt; {
  const { subreddit } = req.params;
  res.render(&quot;subreddit&quot;, { subreddit });
});</code></pre>
<p>이 방식은 <code>/r/:subreddit</code>의 요청을 받으면 입력한 매개변수를 <code>req.params</code> 프로퍼티를 이용하여 가져와서 렌더링하는 &#39;subreddit.ejs&#39; 템플릿 파일에 객체형식으로 전달하여 템플릿에 <code>&lt;%=</code> 구문으로 데이터를 전달했다.
이렇게 작성하면 매개변수의 값에 따라 페이지의 <code>title</code>과 <code>h1</code>태그의 콘텐츠가 변경될 것이다.</p>
<ul>
<li><p>&#39;hello&#39;을 매개변수로 했을 때
<img src="https://velog.velcdn.com/images/untiring_dev/post/ce29b67d-6d7b-4ace-82fc-123f9a37a996/image.png" alt=""></p>
</li>
<li><p>&#39;goodbye&#39;를 매개변수로 했을 때
<img src="https://velog.velcdn.com/images/untiring_dev/post/e837ccd1-4cad-40a6-8d93-a244a2f77b13/image.png" alt=""></p>
</li>
</ul>
<p>이것을 실제 데이터베이스에서 데이터를 가져와서 사용한다고 하면 아주 유용할 것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Express] EJS views 디렉토리 설정]]></title>
            <link>https://velog.io/@untiring_dev/Express-EJS-views-%EB%94%94%EB%A0%89%ED%86%A0%EB%A6%AC-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@untiring_dev/Express-EJS-views-%EB%94%94%EB%A0%89%ED%86%A0%EB%A6%AC-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Mon, 05 Dec 2022 10:44:42 GMT</pubDate>
            <description><![CDATA[<p>EJS 패키지를 설치하고 <code>app.set()</code>으로 Express에게 EJS를 사용할 것이라고 알려줬다.</p>
<pre><code class="language-js">const express = require(&quot;express&quot;);
const app = express();
const port = 3000;

app.set(&quot;view engine&quot;, &quot;ejs&quot;);

app.get(&quot;/&quot;, (req, res) =&gt; {
  res.render(&quot;home&quot;);
});

app.listen(port, () =&gt; {
  console.log(`Starting server : localhost:${port}`);
});</code></pre>
<p>그런데 여기서 views 디렉토리에 문제가 있다.</p>
<p>views의 상위 디렉토리 내에서 서버를 실행하고 요청을 하면 정상적으로 페이지가 렌더링이 된다.
<img src="https://velog.velcdn.com/images/untiring_dev/post/cde29f48-a3a2-4ab4-8cbc-351e89c303a1/image.png" alt=""></p>
<p>하지만 views의 상위 디렉토리보다 한번 더 올라간다면 서버를 실행하는 액세스는 가능하지만
<img src="https://velog.velcdn.com/images/untiring_dev/post/3570c9d0-c85d-4251-b7fd-11e9c76538ae/image.png" alt=""></p>
<p><code>localhost:3000</code>을 보면 &quot;views 디렉토리 안의 view인 &#39;home&#39;을 찾는데 실패했다.&quot;라는 에러가 난다.
<img src="https://velog.velcdn.com/images/untiring_dev/post/9ca17622-8771-4ec9-88f0-490f4ce2e9ea/image.png" alt=""></p>
<p>어디서 찾고 있는지 보면 우리가 생각하는 경로에서 찾고 있지 않고 있다.</p>
<pre><code>Error: Failed to lookup view &quot;home&quot; in views directory &quot;/Users/hyunwoo-air/Desktop/views&quot;</code></pre><p>그래서 이것을 어디서든 views의 디렉토리를 참조해서 정상적으로 작동하게 하려면 Node의 내장된 <code>path</code> 모듈을 사용해서 해결할 수 있다.</p>
<p><code>path</code> 모듈을 가져와서 경로 세그먼트(join의 문자열 인자)를 합쳐서 정규화하여 하나의 경로로 만드는 메서드인 <code>join()</code>을 통해 현재 실행 중인 파일(index.js)의 있는 현재 디렉토리 <code>__dirname</code>와 home.ejs에 도달하기 위한 나머지 경로인 <code>/views</code>를 붙여준다.</p>
<pre><code class="language-js">const path = require(&quot;path&quot;);

// ...

// /Users/hyunwoo-air/Desktop/Dynamic-html-template- + /views가 된다.
app.set(&quot;views&quot;, path.join(__dirname, &quot;/views&quot;));</code></pre>
<p>이제 다시 서버를 실행을 하고 <code>localhost:3000</code>이 제대로 렌더링되는지 살펴보자.</p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/dda6b8f1-3a80-4631-afe4-568f02abf654/image.png" alt=""></p>
<p>드디어 <code>localhost:3000</code>이 정상적으로 동작하고 있다.</p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/c9e6a555-76db-4231-8e88-12da6ea487f3/image.png" alt=""></p>
<p>이 views 디렉토리의 문제의 경우 자주 발생하는 문제라고 하니 반드시 기억해두는 것이 좋을 것 같다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Express] EJS용 Express 구성하기]]></title>
            <link>https://velog.io/@untiring_dev/Express-EJS%EC%9A%A9-Express-%EA%B5%AC%EC%84%B1%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@untiring_dev/Express-EJS%EC%9A%A9-Express-%EA%B5%AC%EC%84%B1%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 05 Dec 2022 09:48:38 GMT</pubDate>
            <description><![CDATA[<h3 id="기본-세팅">기본 세팅</h3>
<pre><code>npm init -y
npm i express</code></pre><pre><code class="language-js">const express = require(&quot;express&quot;);
const app = express();
const port = 3000;

app.get(&quot;/&quot;, (req, res) =&gt; {
  res.send(&quot;Root routing&quot;);
});

app.listen(port, () =&gt; {
  console.log(`Starting server : localhost:${port}`);
});</code></pre>
<h3 id="템플레이팅-시작">템플레이팅 시작</h3>
<p>템플레이팅을 위해 EJS를 사용하는데 Express에게 알려줘야한다.</p>
<blockquote>
<p><strong>EJS란?</strong>
Embedded JavaScript templating. JavaScript로 HTML 마크업을 생성할 수 있는 간단한 템플릿 언어이다.</p>
</blockquote>
<p>먼저 EJS를 설치를 하고,</p>
<pre><code>npm i ejs</code></pre><p><code>app.set()</code> 메서드로 <code>view engine</code>을 <code>ejs</code>로 설정을 한다.</p>
<pre><code class="language-js">app.set(&quot;view engine&quot;, &quot;ejs&quot;);</code></pre>
<p>이 설정을 마치고 나면 새 Express 앱을 만들고 view engine을 사용할 때 Express는 views나 템플릿이 views 디렉토리 안에 있다고 가정한다.
왜냐하면 현재 작업 중인 디렉토리(<code>process.cwd()</code>)에서 <code>/views</code> 디렉토리도 기본 설정이 되어있기 때문이다.</p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/fd6d6edd-e1ad-467d-a69f-a93c479eea3c/image.png" alt=""></p>
<p>기본 Path를 일치시키기 위해 현재 디렉토리 위치에서 /views 디렉토리를 생성하고 디렉토리 내에 EJS 파일을 생성한다.</p>
<pre><code>mkdir views
touch views/&lt;fileName&gt;.ejs</code></pre><p>그리고 생성한 EJS 파일에 HTML 콘텐츠를 추가한다. EJS 파일에서 표준 HTML을 사용할 수 있기 때문에 원하는대로 HTML을 구성하면 된다.</p>
<p>이렇게 하면 간단한 템플릿이 완성이 된다.</p>
<pre><code class="language-ejs">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot; /&gt;
    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot; /&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&gt;
    &lt;title&gt;Home&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;h1&gt;The Home Page&lt;/h1&gt;
    &lt;p&gt;
      Lorem, ipsum dolor sit amet consectetur adipisicing elit. Nam nostrum
      deserunt, eos, debitis sequi vel saepe at aut quos non esse adipisci
      dolorem nihil error. Nostrum accusantium molestiae nesciunt. Maxime!
    &lt;/p&gt;
    &lt;p&gt;
      Lorem ipsum dolor sit amet consectetur adipisicing elit. Id expedita,
      illum cum ex culpa officia vel adipisci ut neque saepe voluptate
      similique, amet exercitationem, molestias cupiditate minus explicabo
      itaque unde!
    &lt;/p&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>이제 이 템플릿을 라우트 안의 <code>res.send()</code>대신 <code>res.render()</code>을 이용하면 문자열이 아닌 파일이나 템플릿을 보낼 수 있다.
<code>res.render()</code> 메서드는 첫번째 인자인 <code>view</code>(렌더링할 view 파일의 <strong>파일 경로</strong>인 문자열)를 렌더링하고 렌더링된 HTML 문자열을 클라이언트에 보내는 메서드이다.</p>
<p>또한 Path로 <code>views/</code>를 파일명에 적어주지 않아도 되는데 그 이유는 <code>render()</code> 메서드가 이미 views 디렉토리에 이미 존재한다고 가정하기 때문이다.</p>
<pre><code class="language-js">const express = require(&quot;express&quot;);
const app = express();
const port = 3000;

app.set(&quot;view engine&quot;, &quot;ejs&quot;);

app.get(&quot;/&quot;, (req, res) =&gt; {
  res.render(&quot;&lt;fileName&gt;&quot;)
});
app.listen(port, () =&gt; {
  console.log(`Starting server : localhost:${port}`);
});</code></pre>
<p>이렇게 작성하고 서버를 실행 후에 <code>localhost:3000</code>을 보면 렌더된 것을 볼 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/920156d6-4322-4811-adda-2f8af3d60cd3/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[npm] Nodemon]]></title>
            <link>https://velog.io/@untiring_dev/npm-Nodemon</link>
            <guid>https://velog.io/@untiring_dev/npm-Nodemon</guid>
            <pubDate>Mon, 05 Dec 2022 08:17:06 GMT</pubDate>
            <description><![CDATA[<p>Express를 공부하면서 가장 불편했던 것이 파일을 수정하면 실행한 서버를 종료했다가 다시 실행시키고 &#39;수동&#39;으로 계속 반복해야했던 것이다.
이것을 자동으로 서버를 재시작해주는 방법이 있는데 그것이 &#39;Nodemon&#39;이라는 패키지이다.</p>
<h3 id="nodemon-설치하기">Nodemon 설치하기</h3>
<pre><code>npm install -g nodemon</code></pre><p><code>-g</code>를 빼고 전역으로 설치하지 않아도 된다.</p>
<h3 id="nodemon으로-서버-시작하기">Nodemon으로 서버 시작하기</h3>
<p>설치를 해서 nodemon으로 서버를 시작하는 방법은 아래처럼 입력하면 된다.</p>
<pre><code>nodemon &lt;filename&gt;</code></pre><p>index.js를 실행하고 수정 후 저장을 하면 아래처럼 서버를 자동으로 재시작시켜준다.
nodemon은 참고로 경로를 관찰하고 폴더의 js, json 확장자로 시작하는 모든 것을 관찰한다.
<img src="https://velog.velcdn.com/images/untiring_dev/post/9e46242b-6759-4c01-8c2e-5a460d3b996e/image.gif" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Express] 1도 모르는 Express - 쿼리 문자열]]></title>
            <link>https://velog.io/@untiring_dev/Express-1%EB%8F%84-%EB%AA%A8%EB%A5%B4%EB%8A%94-Express-%EC%BF%BC%EB%A6%AC-%EB%AC%B8%EC%9E%90%EC%97%B4</link>
            <guid>https://velog.io/@untiring_dev/Express-1%EB%8F%84-%EB%AA%A8%EB%A5%B4%EB%8A%94-Express-%EC%BF%BC%EB%A6%AC-%EB%AC%B8%EC%9E%90%EC%97%B4</guid>
            <pubDate>Mon, 05 Dec 2022 08:04:16 GMT</pubDate>
            <description><![CDATA[<h3 id="쿼리query-문자열란">쿼리(Query) 문자열란?</h3>
<p>URL의 일부로 물음표 뒤에 위치하며 쿼리 문자열의 한 부분으로 <code>key</code>-<code>value</code> 쌍으로 정보를 담는 문자열이다.</p>
<p>쿼리 문자열을 파싱하고 Express 앱에서 액세스하는 것은 매우 중요하다.</p>
<h4 id="쿼리-문자열에-액세스하기">쿼리 문자열에 액세스하기</h4>
<p><code>req.params</code>처럼 <code>req.query</code> 프로퍼티로 액세스가 가능하다.
쿼리 문자열 또한 <code>key</code>와 <code>value</code>로 이루어져있는 객체이다.</p>
<p><code>localhost:3000/search?q=cards</code> 입력 시 나오는 결과는 아래와 같다.</p>
<pre><code class="language-js">// localhost:3000/search?q=cards 입력 시 
app.get(&quot;/search&quot;, (req, res) =&gt; {
  console.log(req.query);
  res.send(&quot;Query String&quot;)
})</code></pre>
<p>로그에는 <code>{q : &#39;cards&#39;}</code>가 출력되어있다.
즉, <code>localhost:3000/search?&lt;query-key&gt;=&lt;query-value&gt;</code>가 되는 것이다.
<img src="https://velog.velcdn.com/images/untiring_dev/post/0eee28ba-25b7-4ee9-9a3b-84c1cab854f6/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/3e2eb2f4-550b-4be1-95c0-d4921517f53d/image.png" alt=""></p>
<h4 id="복수의-쿼리-문자열-입력하기">복수의 쿼리 문자열 입력하기</h4>
<p>쿼리 문자열 또한 <code>&amp;</code>를 사용하여 여러 개의 쿼리 문자열을 입력할 수 있다.</p>
<p>똑같은 라우팅을 사용하여 여러 개의 쿼리 문자열을 사용해보자.</p>
<pre><code class="language-js">// localhost:3000/search?q=cards&amp;color=red 입력 시 
app.get(&quot;/search&quot;, (req, res) =&gt; {
  console.log(req.query);
  res.send(&quot;Query String&quot;);
})</code></pre>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/b2b49f77-3840-4826-96bf-69506b135044/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/dc79a61c-0aff-4306-a5ea-c6791f496b1b/image.png" alt=""></p>
<p>당연히 접근할 때도 깔끔한 코드를 위해 구조 분해를 하면 더 좋다.</p>
<pre><code class="language-js">// localhost:3000/search?q=cards&amp;color=red 입력 시 
app.get(&quot;/search&quot;, (req, res) =&gt; {
  const { q, color } = req.query;
  res.send(`${q}=${color}`);
})</code></pre>
<p>하지만 사용자들은 이렇게 주소창에 바로 입력하지 않은 GET 요청 form에 입력을 통해 쿼리 문자열을 생성한다.
이 방법은 차차 배워보도록 하겠다 😅</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Express] 1도 모르는 Express - Path 매개변수 :]]></title>
            <link>https://velog.io/@untiring_dev/Express-1%EB%8F%84-%EB%AA%A8%EB%A5%B4%EB%8A%94-Express-Path-%EB%A7%A4%EA%B0%9C%EB%B3%80%EC%88%98</link>
            <guid>https://velog.io/@untiring_dev/Express-1%EB%8F%84-%EB%AA%A8%EB%A5%B4%EB%8A%94-Express-Path-%EB%A7%A4%EA%B0%9C%EB%B3%80%EC%88%98</guid>
            <pubDate>Mon, 05 Dec 2022 07:28:31 GMT</pubDate>
            <description><![CDATA[<h3 id="path의-매개변수란">Path의 매개변수란?</h3>
<p>라우트를 생성해서 그 안에 경로 문자열에 콜론을 이용해서 변수나 경로 변수로 지정하게 끔 하는 것이다.</p>
<pre><code class="language-js">// 경로에 콜론(:)을 사용해서 사용할 수 있다.
app.get(&quot;/r/:params&quot;, (req, res)=&gt;{
    res.send(&quot;THIS IS A /r/:params&quot;)
})</code></pre>
<p>Path의 매개변수를 설정해서 경로에 맞춰서 입력을 해보면 아래와 같은 결과가 나온다.
<code>:</code>뒤에 아무거나 입력을 해도 설정한 텍스트가 똑같이 출력되고 있다.</p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/1ba7ff94-68a7-4bf3-b365-53aec2e63ae1/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/00ea34cf-f51c-47e3-8a42-077fcea70054/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/41a5d263-3bd1-4402-b573-6ef68f96766c/image.png" alt=""></p>
<p>여기서 <code>:params</code>는 무엇어야하는지 정한 것이 아니다. 유효한 건지 혹은 영어인지 등 확인하는 작업이 없다.
말 그대로 일치시키는 <strong>패턴</strong>인 것이다.
우리가 설정한 <code>r/&lt;params&gt;</code>의 패턴만 맞춰주면 모두 우리가 설정한 텍스트를 볼 수 있다.
물론 다른 패턴이라면 아래처럼 &#39;Cannot GET /~&#39;를 볼 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/38ea6e14-4d95-44a1-a8e7-463fa81540ce/image.png" alt=""></p>
<p>매개변수로 입력한 값은 아래처럼 <code>req</code> 객체의 메서드인 <code>params</code> 프로퍼티로 값을 사용할 수 있다.</p>
<pre><code class="language-js">app.get(&quot;/r/:params&quot;, (req, res) =&gt; {
  console.log(req.params);
  res.send(`THIS IS A /r/:params`);
});</code></pre>
<p>여기서 <code>req.params</code>는 객체인데 Path의 매개변수의 이름이 객체의 프로퍼티의 <code>key</code>가 되고 입력한 값이 <code>value</code>가 된다.
<code>localhost:3000/r/hello</code>를 입력해서 아래의 코드를 실행하면 로그에 출력된 것은 <code>객체</code>이며, <code>params</code>라는 <code>key</code>와 <code>hello</code>라는 <code>value</code>를 가진다.</p>
<pre><code class="language-js">// localhost:3000/r/hello 입력 시
app.get(&quot;/r/:params&quot;, (req, res) =&gt; {
    console.log(req.params); // { params : &#39;hello&#39;}
    res.send(`THIS IS A /r/:${req.params.params}`);
});</code></pre>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/c441d005-0f34-4444-bc17-9756dbdf77ac/image.png" alt=""></p>
<p>더욱 깔끔한 코드를 사용을 위해 <strong>객체 구조 분해</strong>로 <code>params</code>를 사용한다.</p>
<pre><code class="language-js">app.get(&quot;/r/:params&quot;, (req, res) =&gt; {
  // 객체 구조 분해
  const { params } = req.params; 
  res.send(`THIS IS A /r/:${params}`);
});</code></pre>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/0403ab83-e899-42e9-8024-42448c7c1664/image.png" alt=""></p>
<h4 id="복수의-매개변수도-가능">복수의 매개변수도 가능!</h4>
<p>Path의 매개변수는 꼭 하나가 아니여도 된다.
<code>/</code>와 <code>:</code>을 이용하여 복수로 추가할 수 있다.</p>
<pre><code class="language-js">// localhost:3000/r/about/1을 입력 시
app.get(&quot;/r/:params/:postId&quot;, (req, res) =&gt; {
  const { params, postId } = req.params;
  res.send(`THIS IS A /r/:${params}/:${postId}`);
});</code></pre>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/a8bb058e-6d05-4317-9e19-8aefb8d13dec/image.png" alt=""></p>
<p>로그로 출력해보면 <code>req.params</code>의 하나의 객체에 각각의 프로퍼티로 나열되어 있다.</p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/3b28d3e2-316b-4bbe-b037-133d3001bf5f/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Express] 1도 모르는 Express - Routing]]></title>
            <link>https://velog.io/@untiring_dev/Express-1%EB%8F%84-%EB%AA%A8%EB%A5%B4%EB%8A%94-Express-Routing</link>
            <guid>https://velog.io/@untiring_dev/Express-1%EB%8F%84-%EB%AA%A8%EB%A5%B4%EB%8A%94-Express-Routing</guid>
            <pubDate>Mon, 05 Dec 2022 05:24:31 GMT</pubDate>
            <description><![CDATA[<h2 id="routing이란">Routing이란?</h2>
<p><a href="https://expressjs.com/en/guide/routing.html">Express Guide - Routing</a></p>
<p>요청과 요청된 경로를 가져와서 응답을 갖는 어떠한 코드에 맞추는 것을 말한다.
예를 들어 경로가 <code>/about</code>이라면 그 경로에 맞는 응답할 콘텐츠를 출력한다.</p>
<h3 id="appget">app.get()</h3>
<p><a href="https://expressjs.com/ko/4x/api.html#app.get.method">Express Docs - app.get()</a></p>
<p>Routing을 사용하기 위해 <code>get()</code> 메서드를 사용하는데 get() 메서드의 설명이다.</p>
<pre><code>Routes HTTP GET requests to the specified path with the specified callback functions.
지정된 콜백 함수를 사용하여 HTTP GET 요청을 지정된 경로로 라우팅합니다.</code></pre><p>아래의 코드처럼 작성할 경우 <strong>설정한 경로로 HTTP GET 요청을 라우팅</strong>하여 실행된 콜백 함수로 인해 응답을 출력한다.</p>
<p>라우팅을 설정할 때 아무것도 작성하지 않는 <code>/</code>는 &quot;루트 라우트&quot;라고 불린다.</p>
<pre><code class="language-js">// 들어오는 요청을 나가는 응답에 라우팅하는 것

app.get(&quot;/&quot;, (req, res)=&gt;{
    res.send(&quot;&lt;h1&gt;HOME ROUTING&lt;/h1&gt;&quot;)
})

app.get(&quot;/about&quot;, (req, res)=&gt;{
    res.send(&quot;&lt;h1&gt;ABOUT ROUTING&lt;/h1&gt;&quot;)
})

app.get(&quot;/portfolio&quot;, (req, res)=&gt;{
    res.send(&quot;&lt;h1&gt;PORTFOLIO ROUTING&lt;/h1&gt;&quot;)
})</code></pre>
<p><code>res</code> 객체의 <code>send()</code> 메서드로 <code>h1</code>태그를 인자로 한 결과 <code>localhost:3000/about</code>에는 아래와 같이 <code>h1</code>태그가 출력되어있는 것을 볼 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/b49536cf-8973-4259-9cc5-7634b4015ee6/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/f39fb22c-fa14-4d80-9c33-f2d598a344e1/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/164c2fd3-2d71-4767-a838-504ae2e2a5f0/image.png" alt=""></p>
<h4 id="⚠️-라우팅되지-않은-경로가-입력되었을-때">⚠️ 라우팅되지 않은 경로가 입력되었을 때</h4>
<p>라우팅되지 않은 경로가 입력되었을 때 &quot;Cannot get /&quot;과 같은 텍스트를 볼 수 있다.
이것을 라우팅되지 않는 경로가 입력되면 다른 텍스트로 변경할 수 있는데 경로에 <code>*</code>을 사용하면 설정할 수 있다.</p>
<pre><code class="language-js">app.get(&quot;*&quot;, (req, res)=&gt;{
    res.send(&quot;&lt;h1&gt;I DON&#39;T KNOW THAT PATH...&lt;/h1&gt;&quot;)
})</code></pre>
<p>입력한 <code>/hello</code> 경로는 라우팅되지 않았기 때문에 위에서 설정한 <code>h1</code>태그가 출력되었다.</p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/f671fc9a-8703-41ed-891a-109b29cfd00e/image.png" alt=""></p>
<p>여기서 중요한 점은 <code>*</code>을 사용한 라우팅을 다른 라우팅들의 위로 올리면 모든 경로를 입력해도 가장 첫번째에 있는 <code>*</code> 라우팅이 항상 맞기 때문에 다음 코드로 넘어가지 않는다. </p>
<pre><code class="language-js">// *를 가장 위로 보내면... 아래와 같은 상황이 발생한다.
app.get(&quot;*&quot;, (req, res) =&gt; {
  res.send(&quot;&lt;h1&gt;I DON&#39;T KNOW THAT PATH...&lt;/h1&gt;&quot;);
});

app.get(&quot;/&quot;, (req, res) =&gt; {
  res.send(&quot;&lt;h1&gt;HOME ROUTING&lt;/h1&gt;&quot;);
});

app.get(&quot;/about&quot;, (req, res) =&gt; {
  res.send(&quot;&lt;h1&gt;ABOUT ROUTING&lt;/h1&gt;&quot;);
});

app.get(&quot;/portfolio&quot;, (req, res) =&gt; {
  res.send(&quot;&lt;h1&gt;PORTFOLIO ROUTING&lt;/h1&gt;&quot;);
});</code></pre>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/f6b30594-0ee6-4200-8af5-6890f1902dda/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/b0a5f265-019e-43fb-a8b4-4f4de9b4cbd5/image.png" alt=""></p>
<h3 id="apppost">app.post()</h3>
<p><a href="https://expressjs.com/ko/4x/api.html#app.post.method">Express Docs - app.post()</a></p>
<p>이제 <code>post()</code> 메서드를 사용해보자. 공식 Docs의 설명이다.</p>
<pre><code>Routes HTTP POST requests to the specified path with the specified callback functions
지정된 콜백 함수를 사용하여 HTTP POST 요청을 지정된 경로로 라우팅합니다.</code></pre><p>이전에 생성했던 라우팅인 <code>/about</code>에 <code>post()</code> 메서드를 사용해보자.</p>
<pre><code class="language-js">app.post(&quot;/about&quot;, (req, res) =&gt; {
    res.send(&quot;&lt;h1&gt;ABOUT POST REQUEST&lt;/h1&gt;&quot;)
})</code></pre>
<p>화면에는 그대로 <code>h1</code>태그인 &quot;ABOUT ROUTING&quot;이 되어있다. &quot;/about&quot; 경로로 브라우저는 기본적으로 GET 메서드로 응답을 받기 때문이다.</p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/f39fb22c-fa14-4d80-9c33-f2d598a344e1/image.png" alt=""></p>
<p><code>get()</code> 메서드가 아닌 <code>post()</code> 메서드를 보려면 포스트맨을 사용해서 보면 된다.
HTTP 메서드를 POST로 변경하고 <code>/about</code> 경로로 라우팅을 하면 설정했던 <code>h1</code>태그가 출력된다.</p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/910946e6-01a8-497c-92a0-3525f452d223/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Express] 1도 모르는 Express - Express 기초와 req, res 객체]]></title>
            <link>https://velog.io/@untiring_dev/Express-Express-%EC%82%AC%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0-1</link>
            <guid>https://velog.io/@untiring_dev/Express-Express-%EC%82%AC%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0-1</guid>
            <pubDate>Mon, 05 Dec 2022 05:23:01 GMT</pubDate>
            <description><![CDATA[<h3 id="express-설치">Express 설치</h3>
<p><code>npm init</code> 후 package.json 파일이 생성된 상태에서 <code>npm install express</code> 명령어를 실행해서 express를 설치한다.</p>
<pre><code class="language-Bash">## 신버전 npm
npm install express

## 구버전 npm
npm install express --save</code></pre>
<h3 id="express-사용하기">Express 사용하기</h3>
<p>새로 js파일을 생성하고 <code>express</code>를 사용하기 위해 <code>require</code>로 불러온다.</p>
<pre><code class="language-js">const express = require(&quot;express&quot;);</code></pre>
<p>그 후에 <code>express</code>를 실행한다.</p>
<pre><code class="language-js">const app = express();</code></pre>
<h3 id="express로-요청-받기">Express로 요청 받기</h3>
<p>listen()을 사용하여 요청을 받을 <strong>포트</strong>를 설정하고 요청을 받으면 실행할 <strong>콜백 함수</strong>를 작성한다.</p>
<pre><code class="language-js">// app.listen(port, callback)
const express = require(&quot;express&quot;);
const app = express();
const port = 3000;

app.listen(port, () =&gt; {
  console.log(`Listening on port ${port}.`);
})</code></pre>
<p>실행하면 정상적으로 터미널에 방금 입력한 로그가 출력된다.
<img src="https://velog.velcdn.com/images/untiring_dev/post/c5e4e992-b7af-40d8-a100-8eb97ae25e52/image.png" alt=""></p>
<p>현재 요청이 내 컴퓨터의 로컬 서버로만 동작한다.
그래서 나는 포트를 3000번으로 설정을 했기 때문에 로컬 기기인 <code>localhost:3000</code>로 이동해야한다.</p>
<p>localhost:3000으로 이동하면 아래의 화면이 보일텐데 이것은 &#39;서버는 있지만 응답을 못 받았다&#39;라는 의미이다.
<img src="https://velog.velcdn.com/images/untiring_dev/post/afcfe2a7-19b3-48e1-a396-7a0ec8b9fbd0/image.png" alt=""></p>
<p>개발자 도구의 네트워크 탭에도 localhost가 <strong>상태 코드 404(Not found)</strong>가 떠있는 것을 볼 수 있다.
&#39;Cannot GET /&#39;과 동일하게 <strong>&#39;실행할 서버는 있지만 응답할 콘텐츠가 없는 상태&#39;</strong>를 의미한다.
<img src="https://velog.velcdn.com/images/untiring_dev/post/f7d31cc7-de6b-443d-88f2-fabc8042f8f9/image.png" alt=""></p>
<h3 id="appuse로-요청-받을-시-콜백-함수-실행하기">app.use로 요청 받을 시 콜백 함수 실행하기</h3>
<p><a href="https://expressjs.com/ko/4x/api.html#app.use">Express API</a>
어디에서 오든지, get이든 post든 상관없이 요청이 오면 콜백을 실행하는 메서드이다.</p>
<p>어떠한 요청이 들어오면 아래의 로그가 출력이 된다.</p>
<pre><code class="language-js">app.use(()=&gt;{
    console.log(&quot;app.use() Run!&quot;)
})</code></pre>
<p>여기서 들어오는 모든 요청은 콜백 함수에서 자동으로 전달되는 두 개의 매개변수에 액세스한다.
하나는 하나는 요청을 의미하는 <code>req</code> 객체, 또 다른 하나는 응답을 의미하는 <code>res</code> 객체이다.</p>
<pre><code class="language-js">app.use((req, res)=&gt;{
    console.log(&quot;app.use() Run!&quot;)
})</code></pre>
<p><code>req</code> 객체는 Express로 만들어져서 콜백 함수에 <strong>첫번째 인자</strong>로 전달이 된다.
HTTP 요청은 JavaScript의 객체가 아닌 텍스트 정보이기 때문에 Express는 HTTP 요청 정보를 파싱해서 전달할 JavaScript 객체 데이터로 변환한다.</p>
<p><code>res</code> 객체는 응답 객체로 요청을 한 누군가에게 보내질 응답을 생성하는데 사용된다.</p>
<p><code>req</code>와 <code>res</code> 객체는 많은 메서드를 사용할 수 있는데 <code>res</code>의 <code>send()</code>를 살펴보면 인자로는 <code>Buffer object</code>, <code>String</code>, <code>object</code>, <code>Boolean</code>, <code>Array</code>가 가능하다고 한다.</p>
<blockquote>
<p><strong>Buffer Object란?</strong>
Node.js 에서 제공하는 <code>Binary data</code>를 담을 수 있는 Object이다. <code>Binary data</code>란 <code>0</code>과 <code>1</code>로 표현되는 데이터를 의미한다.</p>
</blockquote>
<br>

<h4 id="인자가-string일-때">인자가 String일 때</h4>
<p>아래처럼 <code>String</code>을 인자로 넣으면 요청을 받았을 때 <code>app.use()</code>의 콜백 함수가 실행이 되면서 <code>res.send()</code> 메서드가 실행이 된다.</p>
<pre><code class="language-js">app.use((req, res) =&gt; {
  console.log(&quot;app.use() Run!&quot;);
  res.send(&quot;HELLO WORLD!&quot;);
});</code></pre>
<p><code>res.send(&quot;HELLO WORLD!&quot;)</code>가 실행되면 아래처럼 화면에 <code>text</code>태그로 문자열을 담아 출력한 것을 볼 수 있다.
그리고 자동으로 콘텐츠 타입이 <code>text/html</code>로 설정된 것을 알 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/e111c51a-293b-4602-b9d8-10584f9c15cb/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/c1af401c-36a1-439d-b333-1c160d012e05/image.png" alt=""></p>
<p><code>text/html</code>로 변경되었다는 것은 즉, HTML 태그를 넣어도 적용이 된다는 의미이기도 하다.</p>
<pre><code class="language-js">res.send(&quot;&lt;h1&gt;THIS IS HTML TAG.&lt;/h1&gt;&quot;)</code></pre>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/429ddbd1-1420-4ff7-8c67-ba8364fb068f/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/162794c1-5d6b-46e7-afda-4a341fd2b4fd/image.png" alt=""></p>
<h4 id="인자가-object일-때">인자가 Object일 때</h4>
<p><code>res.send({color: &quot;red&quot;})</code>로 객체인 인자를 설정했을 때 콘텐츠 타입이 <code>application/json</code>으로 변경되었다.</p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/932ddb49-65dd-4221-bd1f-b63336c044bb/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/7b4139af-59df-4f8c-92d9-8c42785f8c94/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[npm] Bcrypt를 사용하여 비밀번호 암호화하기]]></title>
            <link>https://velog.io/@untiring_dev/npm-Bcrypt%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-%EB%B9%84%EB%B0%80%EB%B2%88%ED%98%B8-%EC%95%94%ED%98%B8%ED%99%94%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@untiring_dev/npm-Bcrypt%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-%EB%B9%84%EB%B0%80%EB%B2%88%ED%98%B8-%EC%95%94%ED%98%B8%ED%99%94%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 02 Dec 2022 06:15:41 GMT</pubDate>
            <description><![CDATA[<p>DB에 사용자가 입력한 정보를 아래와 같이 POST했었다.</p>
<pre><code class="language-json">{
    &quot;name&quot;: &quot;email1234&quot;,
    &quot;email&quot;: &quot;email1234@naver.com&quot;,
    &quot;password&quot;: &quot;1234567&quot;
}</code></pre>
<p>근데 저장된 DB를 보면 아래처럼 저장되어 있다.
<img src="https://velog.velcdn.com/images/untiring_dev/post/950023a0-1672-4e67-b609-6e9c1ce18f07/image.png" alt=""></p>
<p>누가봐도 보안에 매우 취약해보인다.
특히 password는 관리자조차도 알 수 없게 해야하는데 사용자가 입력한 그대로 입력되어있다.</p>
<p>그래서 password를 관리자도 password가 무엇인지 읽을 수 없게 변환해야한다.
이때 유용한 것이 &#39;Bcrypt&#39;이다.</p>
<h3 id="bcrypt-설치">Bcrypt 설치</h3>
<p>우선 아래 명령어를 실행하여 &#39;Bcrypt&#39;를 설치한다.</p>
<pre><code>npm i bcrypt --save</code></pre><h3 id="bcrypt-사용">Bcrypt 사용</h3>
<p>MongoDB의 Schema를 설정한 User.js로 가서 bcrypt를 <code>require</code>한다.</p>
<pre><code class="language-javascript">const bcrypt = require(&#39;bcrypt&#39;);</code></pre>
<p>우선 <a href="https://www.npmjs.com/package/bcrypt">npm 홈페이지의 Bcrypt 사용법</a>은 아래처럼 작성되어 있다.
<img src="https://velog.velcdn.com/images/untiring_dev/post/b29fb16a-f20c-4a15-9cf2-09eb999c9683/image.png" alt=""></p>
<p>나는 salt와 hash를 자동으로 생성하지 않기 때문에 salt를 우선 생성하고 생성된 salt로 password를 암호화 하는 <strong>Technique 1</strong>로 진행했다.</p>
<pre><code class="language-js">bcrypt.genSalt(saltRounds, function(err, salt) {
    bcrypt.hash(myPlaintextPassword, salt, function(err, hash){
        // Store hash in your password DB.
    });
})</code></pre>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/6a180809-1006-4c68-8b6d-ae60a45494e5/image.png" alt=""></p>
<p>여기서 <code>genSalt()</code>는 bcrypt가 <code>password</code>를 암호화하기 위해 필요한 &#39;salt&#39;라는 것을 생성할 때 사용하는 메서드이다.
인자로는 <code>saltRound</code>와 <code>err</code>의 정보와 <code>salt</code>를 담은 콜백함수이다.</p>
<p><code>genSalt()</code>의 첫번째 인자인<code>saltRounds</code>는 기존에 salting된 <code>password</code>를 몇 번 더 salting을 해서 해시를 도출할 것인가를 결정하는 인자라고 생각하면 된다.</p>
<p>여기서 사용되는 <code>salt</code>란 명칭은 음식에 소금을 치면 사방으로 흩어지듯이 설정한 <code>password</code>에 난수를 무작위로 추가하는 역할을 한다.
이 <code>salt</code>라는 것이 설정한 password를 복잡하게 변경시키는 것이다.</p>
<p>그래서 <code>saltRound</code>를 설정하고 다음으로 넘어간다.</p>
<pre><code class="language-js">// saltRounds를 10번 실행한다.
const saltRounds = 10; </code></pre>
<p>그리고 salt를 생성하기 전에 작성했던 register 라우터를 보면 순서를 잘 생각해야한다.</p>
<pre><code class="language-js">app.post(&quot;/register&quot;, (req, res) =&gt; {
  const user = new User(req.body);

  // 암호화 실행 후...

  // DB에 데이터 저장
  user.save((err, userInfo) =&gt; {
    if (err) return res.json({ success: false, err });
    return res.status(200).json({
      success: true,
    });
  });
});</code></pre>
<p>여기서 <code>user.save()</code>는 사용자가 입력한 데이터가 DB에 저장되는 것을 말하는데 이 메서드가 실행되기 전에 암호화가 진행되어야하는게 순서상 가장 맞다.
그러므로 salt를 생성하는 genSalt 부분을 <code>pre()</code> 메서드를 사용해서  감싸서 진행한다.
pre()는 mongoose에서 온 메서드이다.</p>
<pre><code class="language-js">userSchema.pre(&quot;save&quot;, function (next) {
    const user = this;

    bcrypt.genSalt(saltRounds, function(err, salt) {
        bcrypt.hash(myPlaintextPassword, salt, function(err, hash){
            // Store hash in your password DB.
        });
    })
  next();
});</code></pre>
<p><code>pre()</code> 메서드를 사용해서 User모델(schema)에 사용자 정보를 <code>save</code>하기 전에 두번째 인자인 콜백함수를 실행한다.
<img src="https://velog.velcdn.com/images/untiring_dev/post/a4d160fd-70d2-411c-b69c-6bd42779107e/image.png" alt=""></p>
<p>콜백함수의 파라미터인 <code>next</code>는 <code>pre()</code> 함수가 끝이나면 실행하는 함수로 <code>index.js</code>의 <code>user.save</code>로 결과를 전송한다.</p>
<p>이제 <code>pre()</code> 메서드로 감싼 후 내부 함수를 작성한다.</p>
<pre><code class="language-js">bcrypt.genSalt(saltRounds, function(err, salt) {
      // salt 생성 시 에러가 발생할 경우 user.save에 err 정보를 전송하고 아니면 다음 단계로 넘어간다.
    if(err) return next(err);
      // 
    bcrypt.hash(user.password, salt, function(err, hash){
        if (err) return next(err);
        user.password = hash;
        next();
    });
})</code></pre>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/6793d35a-538b-44cb-9f15-7424a0979a76/image.png" alt=""></p>
<p><code>hash()</code> 메서드는 입력받은 <code>password</code>를 salting하여 암호화된 <code>hash</code>로 생성한다.
이 <code>hash</code>를 생성하는 도중 에러가 발생하면 에러를 <code>user.save</code>으로 전송하고 아니라면 현재 model에 저장된 <code>password</code>에 hash를 오버라이드해주고 다음 단계인 <code>user.save</code> 부분으로 DB에 저장한다.</p>
<h3 id="결과">결과</h3>
<p>그럼 아래의 데이터를 입력했을 때 데이터의 결과는 어떤지 보자.</p>
<pre><code class="language-json">{
    &quot;name&quot;: &quot;test1&quot;,
    &quot;email&quot;: &quot;test1@naver.com&quot;,
    &quot;password&quot;: &quot;1234567&quot;
}</code></pre>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/7aba56b8-d463-4b67-a64d-871f614e13b3/image.png" alt=""></p>
<p>&quot;1234567&quot;으로 설정했던 <code>password</code>가 암호화되어 복잡한 문자열로 변한 것을 볼 수 있다.</p>
<h3 id="느낀점">느낀점</h3>
<p>요즘 공부를 하면서 가장 느끼는 점이 <strong>&#39;공식 문서&#39;</strong>의 중요성이다. 강의를 그냥 들으면서 공부하거나 혼자 공부하면서 모르는 부분을 공식 문서를 이해하지 못하더라도 보기라도 하면 에러가 발생했을 때 어느 정도의 예측이 가능한 것 같은 느낌이 든다.
앞으로 공식문서를 참고하면서 공부를 해나가야겠다.</p>
<p>출처 : 
<a href="https://an-onymous.tistory.com/entry/bcrypt-Hashing%EC%9D%98-%EB%B3%B4%EC%95%88%EC%9D%84-%EB%8D%94-%EB%86%92%EC%9D%B8-Salting-Round">bbanderson님 티스토리 블로그</a>
<a href="https://github.com/kelektiv/node.bcrypt.js#readme">Bcrypt API</a>
<a href="https://mongoosejs.com/docs/api/schema.html#schema_Schema-pre">Mongoose Docs</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[MongoDB] bad auth : Authentication failed]]></title>
            <link>https://velog.io/@untiring_dev/MongoDB-bad-auth-Authentication-failed</link>
            <guid>https://velog.io/@untiring_dev/MongoDB-bad-auth-Authentication-failed</guid>
            <pubDate>Fri, 02 Dec 2022 02:23:11 GMT</pubDate>
            <description><![CDATA[<p>오늘도 강의를 따라하다 에러를 마주쳤다.</p>
<h2 id="발생-상황">발생 상황</h2>
<p>Github에 commit할 경우 mongoose와 연결할 때 사용하는 URI가 아이디와 패스워드를 담고 있어 보안이 취약하다.
그래서 비밀 정보를 보호하기 위해 개발 로컬 환경과 배포 환경마다 해당되는 부분을 따로 받을 수 있게 설정하기 위해 파일을 분기하는 과정에서 발생했다.</p>
<p>그 과정에서 발생한 에러가 아래의 에러이다.
<img src="https://velog.velcdn.com/images/untiring_dev/post/980b70ec-571a-43ed-840a-adf7c916ecde/image.png" alt=""></p>
<pre><code>MongoServerError: bad auth : Authentication failed.</code></pre><p>라는 에러인데 &quot;몽고DB 서버 에러 : 잘못된 인증 : 인증에 실패했다.&quot;라는 것이다.</p>
<p>음.. 개인적으로 경고 메세지가 너무 포괄적이고 강사님과 나의 환경(node와 mongoDB의 버전 차이)이 다르기 때문에 걱정부터 했지만 일단 침착하게 구글링했다 ㅎㅎㅎ..</p>
<blockquote>
<p>I think you&#39;re confused with the mongodb account password and user password. You should use user password, not account password. That was the reason of my case.
출처 : <a href="https://stackoverflow.com/questions/55695565/error-message-mongoerror-bad-auth-authentication-failed-through-uri-string">stackoverflow</a></p>
</blockquote>
<p>stackoverflow에서 다른 분이 올린 질문에 이와 같은 답변이 채택되어 있었다.
<strong>mongoDB의 계정 비밀번호와 사용자 비밀번호를 혼동한게 아니냐</strong>라는 것이다.
하지만 나는 mongoDB 계정 비밀번호와 사용자 비밀번호를 혼동하지 않았지만 우선 <strong>&#39;계정 문제&#39;</strong>이겠구나 하며 코드에서 수정했던 부분을 되짚어봤다.</p>
<p>우선 분기한 파일은 아래와 같다.</p>
<p>index.js에서 연결한 username과 password가 연결할 URI에 노출되어 있었다.</p>
<pre><code class="language-javascript">// index.js
const express = require(&quot;express&quot;);
const app = express();
const port = 3000;
const bodyParser = require(&quot;body-parser&quot;);

const { User } = require(&quot;./models/User&quot;);


app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

// 아래처럼 username과 password가 노출되어있었다.
const username = encodeURIComponent(&lt;username&gt;);
const password = encodeURIComponent(&lt;password&gt;);

const mongoose = require(&quot;mongoose&quot;);

// 노출된 username과 password을 사용한다.
mongoose  .connect(`mongodb+srv://${username}:${password}@cluster0.3rt3shc.mongodb.net/?retryWrites=true&amp;w=majority`,)
  .then(() =&gt; console.log(&quot;MongoDB connected&quot;))
  .catch((err) =&gt; console.log(err));

app.get(&quot;/&quot;, (req, res) =&gt; {
  res.send(&quot;Hello Word! 안녕하세요~ 저는 신현우입니다.&quot;);
});

app.post(&quot;/register&quot;, (req, res) =&gt; {
  const user = new User(req.body);
  user.save((err, userInfo) =&gt; {
    if (err) return res.json({ success: false, err });
    return res.status(200).json({
      success: true,
    });
  });
});

app.listen(port, () =&gt; {
  console.log(`Example app listening on port ${port}`);
});</code></pre>
<p>그래서 이것을 개발/배포 환경에 따라 다르게 위해 파일을 아래처럼 분기하였다.</p>
<pre><code class="language-javascript">// dev.js - 개발 환경에서 사용할 URI
module.exports = {
  mongoURI: `mongodb+srv://${username}:${password}@cluster0.3rt3shc.mongodb.net/?retryWrites=true&amp;w=majority`,
};
</code></pre>
<pre><code class="language-javascript">// prod.js - 배포 한 후 환경에서 사용할 URI
module.exports = {
  mongoURI: process.env.MONGO_URL,
};</code></pre>
<pre><code class="language-javascript">// key.js - production 환경인지 아닌지에 따라 모듈을 export하여 해당 모듈을 사용한다. 
if (process.env.NODE_ENV === &quot;production&quot;) {
  module.exports = require(&quot;./prod&quot;);
} else {
  module.exports = require(&quot;./dev&quot;);
}</code></pre>
<p>그리고 조건을 만족하는 export된 모듈을 사용해서 index.js에 URI를 적용시킨다.</p>
<pre><code class="language-javascript">const express = require(&quot;express&quot;);
const app = express();
const port = 3000;
const bodyParser = require(&quot;body-parser&quot;);
// 상수 config에 export된 모듈을 담아서 사용한다. 이 모듈을 mongoURI가 property로 존재하는 객체이다.
const config = require(&quot;./config/key&quot;);
const { User } = require(&quot;./models/User&quot;);

app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

const username = encodeURIComponent(&lt;username&gt;);
const password = encodeURIComponent(&lt;password&gt;);

const mongoose = require(&quot;mongoose&quot;);
// 여기서 config가 사용된다.
mongoose
  .connect(config.mongoURI)
  .then(() =&gt; console.log(&quot;MongoDB connected&quot;))
  .catch((err) =&gt; console.log(err));

app.get(&quot;/&quot;, (req, res) =&gt; {
  res.send(&quot;Hello Word! 안녕하세요~ 저는 신현우입니다.&quot;);
});

app.post(&quot;/register&quot;, (req, res) =&gt; {
  const user = new User(req.body);
  user.save((err, userInfo) =&gt; {
    if (err) return res.json({ success: false, err });
    return res.status(200).json({
      success: true,
    });
  });
});

app.listen(port, () =&gt; {
  console.log(`Example app listening on port ${port}`);
});</code></pre>
<p>이렇게 했을 때 아래의 에러가 발생한 것..!
<img src="https://velog.velcdn.com/images/untiring_dev/post/980b70ec-571a-43ed-840a-adf7c916ecde/image.png" alt=""></p>
<h2 id="문제-해결">문제 해결</h2>
<p>우선 <code>username</code>과 <code>password</code>를 우선적으로 살펴보았다.
여기서 아.. 내가 dev.js에 <code>username</code>과 <code>password</code>값을 옮겨주지 않았구나..
에러가 발생한 이유가 <code>username</code>과 <code>password</code>가 정의되지 않았기 때문에 인증에 실패했구나..😓</p>
<p>그래서 아래처럼 <code>username</code>과 <code>password</code>를 dev.js로 옮겨주었더니 정상적으로 동작했다.</p>
<pre><code class="language-javascript">const express = require(&quot;express&quot;);
const app = express();
const port = 3000;
const bodyParser = require(&quot;body-parser&quot;);

const config = require(&quot;./config/key&quot;);
const { User } = require(&quot;./models/User&quot;);

app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

// username / password 상수 dev.js로 옮김

const mongoose = require(&quot;mongoose&quot;);
mongoose
  .connect(config.mongoURI)
  .then(() =&gt; console.log(&quot;MongoDB connected&quot;))
  .catch((err) =&gt; console.log(err));

app.get(&quot;/&quot;, (req, res) =&gt; {
  res.send(&quot;Hello Word! 안녕하세요~ 저는 신현우입니다.&quot;);
});

app.post(&quot;/register&quot;, (req, res) =&gt; {
  const user = new User(req.body);
  user.save((err, userInfo) =&gt; {
    if (err) return res.json({ success: false, err });
    return res.status(200).json({
      success: true,
    });
  });
});

app.listen(port, () =&gt; {
  console.log(`Example app listening on port ${port}`);
});</code></pre>
<pre><code class="language-javascript">const username = encodeURIComponent(&lt;username&gt;);
const password = encodeURIComponent(&lt;password&gt;);

module.exports = {
  mongoURI: `mongodb+srv://${username}:${password}@cluster0.3rt3shc.mongodb.net/?retryWrites=true&amp;w=majority`,
};</code></pre>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/022b45df-4e10-4b4e-8e44-2919e2a9836d/image.png" alt=""></p>
<p>에러가 나면 덜컥 겁먹곤 했는데 별거 아니였다..ㅎㅎㅎ
코드를 꼼꼼히 보는 것도 중요하지만 모를 땐 역시 구글링이 최고..👍</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[MongoDB] Model과 Schema가 뭐죠..? 🤔]]></title>
            <link>https://velog.io/@untiring_dev/MongoDB-Mongoose%EC%9D%98-Model%EA%B3%BC-Schema%EB%9E%80</link>
            <guid>https://velog.io/@untiring_dev/MongoDB-Mongoose%EC%9D%98-Model%EA%B3%BC-Schema%EB%9E%80</guid>
            <pubDate>Thu, 01 Dec 2022 13:07:56 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>John ahn님의 강의를 보고 node를 처음 배우기도 하고 mongoDB나 mongoos의 기초도 모르기 때문에 어느정도 스스로 이해해보고자 게시물을 쓴다.</p>
</blockquote>
<p>강의에서 Mongoose의 <code>model</code>과 <code>schema</code>를 다루는데 model이란 schema가 무엇인지 몰라서 강의가 조금 어렵게 느껴졌다. 그래서 역시 구글링을 통해 해결해보고자 한다.</p>
<h2 id="model">Model</h2>
<p>강의에서는 model은 &#39;schema&#39;를 감싸기 위한 역할을 하는 것이라고 한다.
강의 자료에서는 &#39;레코드 생성, 쿼리, 업데이트, 삭제 등을 위한 데이터베이스에 대한 인터페이스를 제공한다&#39;고 한다.</p>
<h2 id="schema">Schema</h2>
<p>강의 자료를 보면 &#39;Schema는 문서의 구조, 기본값, 유효성 검사 등을 정의한다&#39;고 한다.
또한 John ahn님의 예시로 상품에 관련된 글을 작성한다고 가정하면 글을 작성한 사람이 누구인지, 작성한 게시글의 제목은 무엇인지, 설명은 무엇인지에 대한 값의 타입이나 제한(유효성 검사) 같은 것을 지정을 해주는 것이 Schema라고 한다.
조금 더 알아보고 싶어서 공식문서와 여러 게시물을 구글링해서 개념을 정리해보았다.</p>
<blockquote>
<p>A schema is a JSON object that defines the the structure and contents of your 
data.
...
Schemas represent types of data rather than specific values. App Services supports many built-in schema types. These include primitives, like strings and numbers, as well as structural types, like objects and arrays, which you can combine to create schemas that represent custom object types.</p>
</blockquote>
<p>출처 : <a href="https://www.mongodb.com/docs/atlas/app-services/schemas/">MongoDB Documentation</a></p>
<p>공식문서에서는 <strong>데이터의 구조와 콘텐츠(내용)을 정의하는 JSON 객체</strong>라고 설명한다.
Schema는 특정한 값이 아닌 데이터 타입을 나타내고, 문자열, 숫자열 같은 원시 타입의 자료형과 객체, 배열과 같은 구조적 유형(Structural types)가 포함이 되고, 이것들을 결합하여 사용자 정의 객체 유형을 나타내는 Schema를 생성할 수 있다.</p>
<p>Zerocho님의 블로그에서는 아래처럼 설명하고 있다.</p>
<p>Mongoose의 Schema는 사용자가 작성한 스키마를 기준으로 데이터를 DB에 넣기 전에 먼저 검사한다.
그리고 작성한 스키마에 어긋나는 데이터가 있다면 에러를 발생시킨다. 
또한 스키마를 설정할 때 인덱스까지 설정할 수 있고 기본값 또한 설정할 수도 있다.</p>
<p><strong>즉, 데이터 구조에 대한 편의 기능들을 하나로 모아두었다고 생각하면 된다.</strong></p>
<p>강의에서 Schema는 위의 코드처럼 만든다.</p>
<pre><code class="language-javascript">const mongoose = require(&quot;mongoose&quot;);

const userSchema = mongoose.Schema({
  name: {
    type: String,
    maxlength: 50,
  },
  email: {
    type: String,
    trim: true,
    unique: 1,
  },
  password: {
    type: String,
    minlength: 5,
  },
  lastname: {
    type: String,
    maxlength: 50,
  },
  role: {
    type: Number,
    default: 0,
  },
  image: String,
  token: {
    type: String,
  },
  tokenExp: {
    type: Number,
  },
});</code></pre>
<p>코드 내에서 객체 안의 <strong>속성명(property)</strong>이 필드의 이름이 된다.</p>
<p>이 코드를 풀어서 보자면,
<code>email</code>의 자료형은 <code>String</code>이고 유일해야하고, 데이터의 공백을 없앤다.
<code>password</code>의 자료형은 <code>String</code>이고 최소 5글자여야한다.
<code>role</code>의 자료형은 <code>Number</code>이고 <strong>기본값</strong>으로 0을 지정한다. (<code>role</code>의 자료형이 <code>Number</code>인 이유는 1일때 관리자, 0일때 일반 유저로 하기 위함이다.)
만약 필수 값을 원한다면 <code>require</code> 속성명을 추가해서 <code>boolean</code>값을 받아주면 <code>true</code>일 때 해당 필드의 데이터가 반드시 존재해야한다고 설정할 수 있다.</p>
<p>Schema를 정리하자면 <strong>데이터의 구조와 내용을 정의하는 JSON 객체이며, 데이터 구조에 대한 편의 기능을 모아둔 Mongoose의 기능</strong>이라고 생각하면 될 것 같다.</p>
<h3 id="schema-등록">Schema 등록</h3>
<p>작성한 Schema를 등록하려면 <code>model()</code> 함수를 호출해서 인자로 사용하면 된다.</p>
<pre><code class="language-javascript">const User = mongoose.model(&quot;User&quot;, userSchema);</code></pre>
<p><code>model()</code>의 첫번째 인자는 &#39;컬렉션의 이름&#39;이며, 컬렉션에 이름에 대문자가 있다면 강제로 소문자로 변환 후 등록이 된다고 한다.
두번째 인자로 작성했던 <code>schema</code>를 넣으면 <code>User</code>라는 모델에 작성한 <code>userSchema</code>를 적용시키는 거라고 생각하면 되는거 같다.
만약 컬렉션 이름이 강제로 변환되는 것이 맘에 들지 않으면 세번째 인자로 문자열을 넣어 설정하면 된다.</p>
<pre><code class="language-javascript">const User = mongoose.model(&quot;User&quot;, userSchema, &quot;myfreename&quot;);</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[MongoDB] MongoDB Mongoose에 연결하면서 발생한 에러]]></title>
            <link>https://velog.io/@untiring_dev/MongoDB-MongoDB-Mongoose%EC%97%90-%EC%97%B0%EA%B2%B0</link>
            <guid>https://velog.io/@untiring_dev/MongoDB-MongoDB-Mongoose%EC%97%90-%EC%97%B0%EA%B2%B0</guid>
            <pubDate>Thu, 01 Dec 2022 10:49:34 GMT</pubDate>
            <description><![CDATA[<p>John-Ahn님 강의인 &#39;따라하며 배우는 노드, 리액트 시리즈 - 기본 강의&#39;를 들으면서 발생했던 에러이다.</p>
<p>파일에 아래의 코드처럼 작성하였다.</p>
<pre><code class="language-javascript">const express = require(&quot;express&quot;);
const app = express();
const port = 3000;

const mongoose = require(&quot;mongoose&quot;);
mongoose
  .connect(
    &quot;mongodb+srv://&lt;username&gt;:&lt;password&gt;@cluster0.3rt3shc.mongodb.net/?retryWrites=true&amp;w=majority&quot;,
    {
      useNewUrlParser: true,
      useUnifiedTopology: true,
      useCreateIndex: true,
      useFindAndModify: false,
    }
  )
  .then(() =&gt; console.log(&quot;MongoDB connected&quot;))
  .catch((err) =&gt; console.log(err));

app.get(&quot;/&quot;, (req, res) =&gt; {
  res.send(&quot;Hello World! 안녕하세요!&quot;);
});

app.listen(port, () =&gt; {
  console.log(`Example app listening on port ${port}`);
});</code></pre>
<h2 id="발생상황">발생상황</h2>
<p>몽고DB 세팅을 마치고 mongoose에 연결하기 위해 위의 코드를 작성했으나 아래의 에러 발생
<img src="https://velog.velcdn.com/images/untiring_dev/post/23d54eb2-63c6-4d4c-97eb-cf0f7e7548a0/image.png" alt=""></p>
<pre><code>MongoParseError: options usecreateindex, usefindandmodify are not supported</code></pre><p>에러문을 보면 options인 usecreateindex, usefindandmodify는 지원하지 않는다라는 에러!</p>
<h2 id="문제-해결">문제 해결</h2>
<p>어떻게 해결하면 되는지 찾아보니 이런 문제를 겪은 사람이 꽤 있었다보다.</p>
<blockquote>
<p>useNewUrlParser, useUnifiedTopology, useFindAndModify, and useCreateIndex are no longer supported options. Mongoose 6 always behaves as if useNewUrlParser, useUnifiedTopology, and useCreateIndex are true, and useFindAndModify is false. Please remove these options from your code.
출처 : <a href="https://stackoverflow.com/questions/68958221/mongoparseerror-options-usecreateindex-usefindandmodify-are-not-supported">stackoverflow</a></p>
</blockquote>
<p>Mongoose 버전이 6.0 이상이면 <code>useNewUrlParser</code>, <code>useUnifiedTopology</code>, <code>useFindAndModify</code> 및 <code>useCreateIndex</code>는 더 이상 지원되지 않는 옵션이기 때문에 제거하라는 해결방법이다.</p>
<p>왜냐하면 Mongoose 버전 6.0 이상부터는 기본적으로 <code>useNewUrlParser</code>, <code>useUnifiedTopology</code>, <code>useFindAndModify</code>가 <code>true</code>, <code>useCreateIndex</code>가 <code>false</code>로 동작하기 때문이다.</p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/d49c299b-f5cc-408b-885f-933edb3752b0/image.png" alt=""></p>
<p>현재 내가 설치한 Mongoose는 6.7.5 버전이기 때문에 위의 코드를 제거하면 이 에러를 해결할 수 있다.</p>
<pre><code class="language-javascript">const express = require(&quot;express&quot;);
const app = express();
const port = 3000;

const mongoose = require(&quot;mongoose&quot;);
mongoose
  .connect(
    &quot;mongodb+srv://username:&lt;password&gt;@cluster0.3rt3shc.mongodb.net/?retryWrites=true&amp;w=majority&quot;
  )
  .then(() =&gt; console.log(&quot;MongoDB connected&quot;))
  .catch((err) =&gt; console.log(err));

app.get(&quot;/&quot;, (req, res) =&gt; {
  res.send(&quot;Hello World! 안녕하세요!&quot;);
});

app.listen(port, () =&gt; {
  console.log(`Example app listening on port ${port}`);
});</code></pre>
<p>도움받은 링크 : <a href="https://velog.io/@lee951109/MongoDB-MongoParseError-options-usecreateindex-usefindandmodify-are-not-supported">lee951109님 Velog게시글</a></p>
<h1 id="-이어서-발생한-문제">+ 이어서 발생한 문제...</h1>
<h2 id="발생-상황">발생 상황</h2>
<p>Mongoose에 연결하기 위해 몽고DB의 URI를 했지만 에러가 지속적으로 발생했다...
몽고DB URI에 <code>password</code>에 특수문자를 이용해서 인코딩이 되면서 발생한 문제이다.</p>
<pre><code class="language-javascript">const express = require(&quot;express&quot;);
const app = express();
const port = 3000;

const mongoose = require(&quot;mongoose&quot;);
// password 부분에 특수문자인 &#39;@&#39;를 이용해서 발생했다.
mongoose
  .connect(
    &quot;mongodb+srv://username:&lt;password@&gt;@cluster0.3rt3shc.mongodb.net/?retryWrites=true&amp;w=majority&quot;
  )
  .then(() =&gt; console.log(&quot;MongoDB connected&quot;))
  .catch((err) =&gt; console.log(err));

app.get(&quot;/&quot;, (req, res) =&gt; {
  res.send(&quot;Hello World! 안녕하세요!&quot;);
});

app.listen(port, () =&gt; {
  console.log(`Example app listening on port ${port}`);
});</code></pre>
<pre><code>MongoParseError: URI does not have hostname, domain name and tld</code></pre><p>URL에 <code>hostname</code>과 <code>domain name</code>과 <code>tld</code>가 없다라는 의미인데...
아직 아무것도 모르고 따라쳐보는 수준이기 때문에 또 구글링..!</p>
<h2 id="문제-해결-1">문제 해결</h2>
<blockquote>
<p>Solution-1: If you are using any special character in your password you need to encode the particular character as %+ASCII_code_of_the_character below link explains everything.
<a href="https://docs.atlas.mongodb.com/troubleshoot-connection/#special-characters-in-connection-string-password">공식 홈페이지 참고 링크</a></p>
</blockquote>
<p>Solution-2: Click on auto-generate password and paste it into the connection string, it will work.
출처 : <a href="https://stackoverflow.com/questions/55753484/mongoparseerror-uri-does-not-have-hostname-domain-name-and-tld">stackoverflow</a></p>
<h3 id="첫번째-해결-방안">첫번째 해결 방안</h3>
<p>만약 password에 특수문자를 사용하는 경우 특수문자를 인코딩이 필요하다. 공식 홈페이지 링크를 참고하여 수정하는 방법이다.</p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/ddbe10b3-57e4-4645-b19b-bb72aac3118f/image.png" alt="공식홈페이지 트러블슈팅 참고 자료"></p>
<p>위에서 <code>username</code>과 <code>password</code>를 인코딩하기위해 <code>encodeURIComponent</code> 함수를 사용하여 인코딩해준 후 몽고DB URI에 백틱을 이용한 &#39;템플릿 리터럴&#39;을 사용하여 작성해주도록 하자!</p>
<pre><code class="language-javascript">const username = encodeURIComponent(&quot;&lt;username&gt;&quot;)
const password = encodeURIComponent(&quot;&lt;password&gt;&quot;)</code></pre>
<pre><code class="language-javascript">const express = require(&quot;express&quot;);
const app = express();
const port = 3000;

// 특수문자가 있다면 encodeURIComponent 함수로 인코딩 후..
const username = encodeURIComponent(&quot;&lt;username&gt;&quot;)
const password = encodeURIComponent(&quot;&lt;password&gt;&quot;)

const mongoose = require(&quot;mongoose&quot;);

// 템플릿 리터럴로 값을 문자열에 넣는다.
mongoose
  .connect(
    `mongodb+srv://${username}:${password}@cluster0.3rt3shc.mongodb.net/?retryWrites=true&amp;w=majority`
  )
  .then(() =&gt; console.log(&quot;MongoDB connected&quot;))
  .catch((err) =&gt; console.log(err));

app.get(&quot;/&quot;, (req, res) =&gt; {
  res.send(&quot;Hello World! 안녕하세요!&quot;);
});

app.listen(port, () =&gt; {
  console.log(`Example app listening on port ${port}`);
});</code></pre>
<p>성공적으로 URI 연결하는 부분의 then 메서드에 작성한 &#39;MongoDB connected&#39; 로그가 출력이 되었다! 👍
<img src="https://velog.velcdn.com/images/untiring_dev/post/22cac919-ec14-479d-9611-95c1c93ab459/image.png" alt="패스워드문제해결"></p>
<h3 id="두번째-해결-방안">두번째 해결 방안</h3>
<p>첫번째 해결방안으로 해결했다고 안해보면 손해!</p>
<p>두번째 방법은 몽고DB에서 비밀번호를 직접 수정하거나 자동으로 생성하면 된다.</p>
<p>아래의 SECURITY 탭에서 Database Access로 접속하고,
<img src="https://velog.velcdn.com/images/untiring_dev/post/d4da7675-818f-4b9a-887f-3efc6964edd1/image.png" alt=""></p>
<p>Database Users 탭의 User 리스트에서 EDIT 버튼을 클릭하면
<img src="https://velog.velcdn.com/images/untiring_dev/post/54243b72-0804-491a-9978-5dc88bc2222a/image.png" alt=""></p>
<p>새로운 창이 뜨면서 Password Authentication 메뉴에 Edit Password 탭을 눌려서 <code>password</code>를 특수문자 없이 수정하면 된다.
<img src="https://velog.velcdn.com/images/untiring_dev/post/675a8049-32fc-4f30-b260-d59094320652/image.png" alt=""></p>
<p>만약 다시 생성하는 것이 번거롭다면 아래의 Autogenerate Secure Password 버튼을 눌려 자동 생성해주고 <code>password</code>를 기억해두면 된다.
<img src="https://velog.velcdn.com/images/untiring_dev/post/98d2f729-6963-4312-afda-f7a465745532/image.png" alt=""></p>
<p>도움받은 링크 : <a href="https://stackoverflow.com/questions/55753484/mongoparseerror-uri-does-not-have-hostname-domain-name-and-tld">stackoverflow</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS] Prototype과 Prototype chain 👬🏼]]></title>
            <link>https://velog.io/@untiring_dev/JS-Day64.-Prototype%EA%B3%BC-Prototype-chain</link>
            <guid>https://velog.io/@untiring_dev/JS-Day64.-Prototype%EA%B3%BC-Prototype-chain</guid>
            <pubDate>Sun, 29 May 2022 17:50:28 GMT</pubDate>
            <description><![CDATA[<p>지금은 자바스크립트의 마지막 알고리즘과 자료구조에 대해 배우고 있지만, 내가 이해가 안되는 부분이 많아서 가장 주춤했던 부분이 <code>Prototype</code>이 다가왔을 때 부터 인 것 같다. </p>
<p>그래서 시간이 있을 때 꼭 <code>Prototype</code>을 조금이라도 이해하도록 해보자라는 마인드로 오늘 정리를 한다.</p>
<p>현재 참여하는 강의에서도 다루었지만 강의 자료를 바탕으로 여러 가지 정보를 참고했는데 아래에 작성했다.</p>
<blockquote>
</blockquote>
<ul>
<li>생활코딩 Prototype/Prototype Chain</li>
<li>Modern Javascript Deep Dive 19장. 프로토타입</li>
<li>인프런 &quot;코어 자바스크립트&quot;</li>
<li><a href="https://www.youtube.com/watch?v=cg1xvFy1JQQ">NomadCoder 객체지향 프로그래밍 영상</a></li>
</ul>
<blockquote>
<p>그리고 너무 깊게 다룬다면 끝도 없기 때문에 현재로선 이해할 수 있는 &quot;Prototype&quot;의 개념과 &quot;Prototype Chain&quot;에 대해서 스스로 정리해보고자 한다.</p>
</blockquote>
<h2 id="prototype이란">Prototype이란?</h2>
<p>영어인 &quot;Prototype&quot;을 해석하면 어떤 말인가?</p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/3ddc7e95-5d5d-45ca-a45d-7ffaf7691daf/image.png" alt=""></p>
<p>&quot;원형&quot;이라는 뜻이다. 
여기서 원형은 &quot;객체의 원형&quot;을 의미한다.</p>
<p>자바스크립트를 이루고 있는 것은 원시 타입인 <code>number</code>, <code>string</code> 등을 제외한 거의 모든 것이 객체이다.</p>
<p>여기서 객체는 우리가 알고 있듯이 아래처럼 <code>key</code>와 <code>value</code>의 쌍으로 이루어진 <code>Property</code>를 가질 수 있다.</p>
<pre><code class="language-HTML">&lt;script&gt;
  let obj = {
      name : &quot;hyunwoo&quot;, // property
      age : &quot;30&quot; // property
  }
&lt;/script&gt;</code></pre>
<p>위의 객체 <code>obj</code>를 <code>console.dir()</code>을 통해 출력해보면 아래와 같은 결과가 나온다.</p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/010bcb83-4826-410f-8b3c-db073fc8d588/image.png" alt=""></p>
<p>우리가 설정했던 프로퍼티인 <code>name</code>과 <code>age</code>가 보이고, 아래에 우리가 설정하지도 않은 프로퍼티가 여러 개도 함께 출력이 된다.</p>
<p>여기서 우리는 <code>Prototype</code>이라는 단어를 처음 볼 수 있게 된 것이다.</p>
<p>본론으로 들어가자면 <code>Prototype</code>이라는 프로퍼티 내의 속성들은 &quot;생성자&quot;를 통해서 객체가 만들어질 때 그 객체에 연결된다.</p>
<p>아래를 보도록 하자. 
우리는 객체인 변수 <code>obj</code>를 생성했는데 &quot;객체 리터럴&quot; 방식으로 선언을 했다. 하지만 &quot;객체 리터럴&quot; 방식으로 선언한 것은 사실 &quot;생성자 함수&quot;를 통해 생성된 것이라 볼 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/81dbb937-6715-4ab6-8f2f-5ea40ea9c125/image.png" alt=""></p>
<p>그래서 위와 똑같이 <code>console.dir()</code>로 출력했을 때 생성자 함수를 뜻하는 <code>Prototpye</code>의 프로퍼티인 <code>constructor</code>는 함수인 <code>function Object(name, age)</code>를 가리키게 된다.</p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/b26b75cb-811d-469f-8bef-027d0c9ce6b6/image.png" alt=""></p>
<p>여기서 <code>Prototype</code>을 이해하려면 생성자 함수를 이해해야 할 것 같다. </p>
<br>

<h3 id="생성자-함수">생성자 함수</h3>
<p>생성자는 &quot;함수&quot;인데 앞에 <code>new</code> 키워드가 붙어서 새로운 객체인 <code>instance</code>를 생성한 후 반환하는 &quot;생성자 함수&quot;가 된다. 
위에서 <code>new Object()</code>처럼 말이다.</p>
<pre><code class="language-HTML">&lt;script&gt;
  // 생성자 함수 Sub() 선언
  function Sub(name, age){
      this.name = name;
      this.age = age;
  }

  // 새로운 객체 instance 생성
  var obj = new Sub(&quot;hyunwoo&quot;, 30);
&lt;/script&gt;</code></pre>
<p>그런데 우리는 누가봐도 생성자 함수를 통한 객체의 생성은 매우 불편해보인다.
그냥 객체 리터럴을 통해 <code>var obj = { name:&quot;hyunwoo&quot;, age:30}</code>처럼 적으면 훨씬 간단하게 객체를 생성할 수 있는데 말이다.</p>
<p>이 부분에서는 <strong>&quot;객체지향 프로그래밍(OOP)&quot;</strong>가 대답이 될 수 있겠다.</p>
<p>만약 우리가 게임에서 캐릭터를 만든다면 어떻게 만들 수 있을까?</p>
<pre><code class="language-HTML">&lt;script&gt;
  const user1 = {
    name: &quot;John&quot;,
      hp : 85,
      skill : &quot;fire ball&quot;,
      xp : 0,
  }

  const user2 = {
    name: &quot;Jane&quot;,
      hp : 120,
      skill : &quot;strike&quot;,
      xp : 0,
  }

  const user3 = {
    name: &quot;Mike&quot;,
      hp : 120,
      skill : &quot;blink&quot;,
      xp : 0,
  }
&lt;/script&gt;</code></pre>
<p>위처럼 유저 하나 하나의 <code>name</code>, <code>hp</code>, <code>mp</code>, <code>skill</code> 등의 프로퍼티를 생성해줘야한다는 번거로움이 있다.
일일이 생성해야한다면 그만큼 시간과 같이 리소스의 소모도 크고, 실수의 가능성도 커진다. </p>
<p>그래서 위의 예제를 보면 데이터의 <code>xp : 0</code>라는 <code>key</code>와 <code>value</code>가 동일하게 반복되는 것을 볼 수 있다.
이것을 하나의 설계도를 두고 데이터 값만 따로 작성을 한다면 어떨까?</p>
<p>아래의 예제는 아직은 정리를 하지 않은 <strong>클래스(Class)</strong>를 사용한다. 
간단히 말하자면 클래스는 하나의 객체를 만드는 &quot;설계도&quot;이며 &quot;템플릿&quot; 같은 개념이다.</p>
<blockquote>
<p>다음 게시글로 클래스를 이해하고 정리해보도록 하겠다. 😄</p>
</blockquote>
<pre><code class="language-HTML">&lt;script&gt;
  // 유저를 생성할 템플릿 클래스 생성
  class User {
      constructor(name, hp, skill){
          this.name = name;
          this.hp = hp;
          this.skill = skill;
          this.xp = 0;
      }
  }

  const user1 = new User(&quot;John&quot;, 85, &quot;fire ball&quot;);
  const user2 = new User(&quot;Jane&quot;, 120, &quot;strike&quot;);
  const user3 = new User(&quot;Mike&quot;, 120, &quot;blink&quot;);
&lt;/script&gt;</code></pre>
<p>위처럼 작성 시 이전 예제와 같은 값이 출력이 된다. 훨씬 가독성이 쉬워지고 값을 넣기에도 편리해지고 재사용성이 높아진 것을 볼 수 있다.</p>
<p><strong>이것과 똑같이 <code>Prototype</code>을 통해서 코드의 재사용성을 높일 수 있다.</strong></p>
<pre><code class="language-HTML">&lt;script&gt;
  function User(name, hp, skill){
      this.name = name;
      this.hp = hp;
      this.skill = skill;
  }

  // prototype을 통한 xp 프로퍼티 생성
  User.prototype.xp = 0;

  const user1 = new User(&quot;John&quot;, 85, &quot;fire ball&quot;);
&lt;/script&gt;</code></pre>
<p>위처럼 생성자함수의 <code>Prototype</code>를 통해 프로퍼티 <code>xp</code>를 생성했다. 
그리고 <code>user1</code>을 <code>console.log()</code>로 확인을 했을 때 아래처럼 출력이 된다.</p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/f09ca503-da64-4c84-8eef-09a0dd7e69db/image.png" alt=""></p>
<p>근데 우리가 생성한 프로퍼티 <code>xp</code>는 보이지 않는데 어디에 있는 것일까?</p>
<p><code>xp</code>는 바로 <code>Prototype</code> 안에 존재한다.</p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/6423c6c1-c68a-413b-af7a-88ba94a31f9a/image.png" alt=""></p>
<p>그래서 만약 <code>xp</code>의 값을 필요할 때 객체 내에 출력되는 프로퍼티는 아니지만 문제없이 접근이 가능하다.</p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/05972949-8f29-4072-b504-f168897002ad/image.png" alt=""></p>
<p><code>Prototype</code>을 쉽게 생각하자면 <strong>조상으로부터 물려받은 &quot;유전자&quot;라고 할 수 있다.</strong> </p>
<p><strong>조상 중 운동선수가 있을 때 내가 운동에 대한 재능을 가지고 있다는 것은 모르지만 운동종목에서 나도 모르게 능력을 발휘할 수 있는 그런 개념으로 이해하면 될 것 같다. 😅</strong></p>
<p>여기서 물려받는 개념을 <code>Prototype Chain</code>이라고 할 수 있을 것 같다!</p>
<br>

<h2 id="prototype-chain">Prototype Chain</h2>
<p>나는 강의를 들으면서 &quot;체이닝&quot;이라는 말을 많이 들었다.
체인은 내가 생각하는 🔗 사슬을 말하는 것이니 연결된다는 뜻으로 이해했었는데 체이닝 개념이 정확히 일치한다.</p>
<p>&quot;연쇄적으로 이어지는 동작 방식&quot;이라고 생각을 하면 될 것 같다.</p>
<p>실용적인 예제는 아니지만 개념을 이해를 위해 생활코딩에서 가져온 예제를 살펴보도록 하자.</p>
<pre><code class="language-HTML">&lt;script&gt;
  function Ultra(){};
  Ultra.prototype.ultraProp = true; // 이 부분을 주목해야한다.

  function Super(){};
  Super.prototype = new Ultra();

  function Sub(){};
  Sub.prototype = new Super();

  var o = new Sub();
  console.log(o.ultraProp); // 어떤 값이 나올까?
&lt;/script&gt;</code></pre>
<p><code>console.log(o.ultraProp)</code>을 출력하면 어떤 값이 나올까?</p>
<p>나는 처음에 변수 <code>o</code>는 생성자 함수 <code>new Sub()</code>를 통해 <strong>생성된 인스턴스</strong>이고 <code>function Sub(){}</code>를 보아하니 아무런 프로퍼티를 생성하지 않았으니까 <code>undefined</code>가 나오지 않을까? 라고 생각했다.</p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/31e65e19-2fc0-43ca-9131-06d1b00745b3/image.png" alt=""></p>
<p>하지만 나오는 값은 <code>true</code>가 나온다.</p>
<p>코드를 하니씩 설명을 하자면 이렇다.</p>
<pre><code class="language-HTML">&lt;script&gt;
  function Ultra(){}; // 생성자 함수 Ultra() 선언
  Ultra.prototype.ultraProp = true; // Ultra의 Prototype을 통해 프로퍼티 ultraProp 의 키와 값 생성

  function Super(){}; // 생성자 함수 Super() 선언
  Super.prototype = new Ultra(); // Super.prototype을 생성자 함수 Ultra()의 인스턴스로 생성

  function Sub(){}; // 생성자 함수 Sub() 선언
  Sub.prototype = new Super(); // Sub.prototype을 생성자 함수 Super()의 인스턴스로 생성

  var o = new Sub(); // 변수 o를 생성자 함수 Sub()의 인스턴스로 생성
  console.log(o.ultraProp); // true
&lt;/script&gt;</code></pre>
<p>보면 &quot;이게 무슨 코드지?&quot;라는 생각을 가질 수 있다. 솔직히 나도 처음 봤을 때 이걸 어떻게 해석해야하는 건지 몰랐다.</p>
<p>근데 보면 공통적인 부분이 많다. 각 인스턴스인 <code>prototype</code>은 각각의 생성자 함수로 순차적으로 생성되고 있는 것을 볼 수 있다.</p>
<p>여기서 <code>Prototype</code>은 &quot;유전자&quot;라고 했다.</p>
<p>보이지는 않지만 현재 생성자 함수 <code>Ultra</code>가 가진 <code>protoype.ultraProp</code>는 아래의 생성자 함수에 영향을 주고 있다.
각각 생성된 인스턴스들을 <code>console.log()</code>를 통해 살펴보도록 하자.</p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/55d84d4f-77cd-4fff-aaf8-3605de318a7d/image.png" alt=""></p>
<p>점차 내려가면서 <code>Prototype</code>가 늘어나면서 <code>ultraProp</code>가 존재하는 것을 볼 수 있다.
이것은 <code>Prototype</code>의 <code>상속</code>때문인데 그림으로 쉽게 표현해겠다.</p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/a6753cc6-40db-46f6-a2d7-f7f67cb51d03/image.png" alt=""></p>
<p><code>Ultra</code>에서 <code>Prototype</code>을 통해 생성한 <code>ultraProp = true</code>는 내가 설명한 &quot;유전자화&quot;가 되었다.</p>
<p>그래서 상위 객체(인스턴스)인 <code>Ultra</code>로부터 <code>Super</code>가 <code>ultraProp = true</code>를 물려받게 되고</p>
<p>아래의 인스턴스 또한 모두 상속을 받게 된다. </p>
<p>그래서 당사자는 모르지만 가지고 있는 프로퍼티에 접근할 수 있는 것이다.</p>
<p>접근을 통해 나온 결과를 <code>Prototype Chain</code>에 의해 이루어졌다라고 할 수 있을 것 같다.</p>
<br>

<h3 id="prototype을-통해-데이터에-접근할-때-어떤-과정이-일어나는가">Prototype을 통해 데이터에 접근할 때 어떤 과정이 일어나는가?</h3>
<pre><code class="language-HTML">&lt;script&gt;
  function Ultra(){};
  Ultra.prototype.ultraProp = true; // 이 부분을 주목해야한다.

  function Super(){};
  Super.prototype = new Ultra();

  function Sub(){};
  Sub.prototype = new Super();

  var o = new Sub();
  console.log(o.ultraProp); // 어떤 값이 나올까?
&lt;/script&gt;</code></pre>
<p>현재 코드를 다시 보자면 <code>ultraProp</code>를 생성한 곳은 <code>Ultra</code>이다.
하지만 변수 <code>o</code>에서 <code>ultraProp</code>에 접근을 했을 때 <code>true</code>가 반환이 되었다.</p>
<p>과정이 어떻게 된 것인지 보도록 하자.</p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/3a7f58de-9e55-4510-861c-5855a1afdf36/image.png" alt=""></p>
<p>이런 식으로 자신에게서 찾다가 찾지 못하면 상위 객체로 올라가면서 찾아서 결과를 반환하는 것이 <code>Prototype Chain</code>이다.</p>
<p>그리고 중요한 점은 <strong>상위 객체로 순차적으로 올라가면서 검색을 했을 때 먼저 나오는 값을 출력한다</strong>는 것이다.</p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/bd8198c5-5c68-4ebf-b319-29f178c56902/image.png" alt=""></p>
<br>
<hr>
<br>

<h4 id="정리를-마치며-⛳️">정리를 마치며 ⛳️</h4>
<p>오늘은 진짜 공부한 느낌이 들어서 뿌듯한 것 같다.</p>
<p>내가 몰랐던 부분을 깨닫고 이것을 공부해야지라고 계획을 했는데 열심히 준비를 하면서 정리를 하고 나니 더욱 그런 것 같다.</p>
<p>물론 위에 있는 정보를 여러 곳에서 참고를 했고 나의 이해를 바탕으로 작성이 되었기 때문에 잘못된 부분도 있을 것이라고 생각한다.</p>
<p>그래서 다른 분들께 확인을 받고 수정을 해볼 예정이다.</p>
<p>오랜만에 나의 공부한 기록을 적는데 재미있었다. 다음은 클래스를 정리를 할 건데 기대가 된다. 😄</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JS] Array의 핵심 메서드, forEach/filter/map ⭐️]]></title>
            <link>https://velog.io/@untiring_dev/JS-Day63.-Array%EC%9D%98-%ED%95%B5%EC%8B%AC-%EB%A9%94%EC%84%9C%EB%93%9C-forEachfiltermap</link>
            <guid>https://velog.io/@untiring_dev/JS-Day63.-Array%EC%9D%98-%ED%95%B5%EC%8B%AC-%EB%A9%94%EC%84%9C%EB%93%9C-forEachfiltermap</guid>
            <pubDate>Sat, 28 May 2022 07:20:39 GMT</pubDate>
            <description><![CDATA[<p>오늘은 배열의 메서드 중 가장 핵심적으로 자주 사용하게 될 메서드 3개인 </p>
<p><code>forEach</code>, <code>filter</code>, <code>map</code>을 다룰 것이다.</p>
<p>강사님께서도 가장 많이 사용하는 메서드니 다른 메서드는 검색해서 사용하더라도 이 메서드들은 꼭 알아두라고 당부하셨다.</p>
<blockquote>
<p>이 게시물도 마찬가지로 원본 배열에 영향이 간다면 🔄 표시를 할 것이다.</p>
</blockquote>
<br>

<h2 id="arrayprototypeforeach">Array.prototype.forEach()</h2>
<p><code>Array.prototype.forEach()</code>는 주어진 함수(콜백 함수)를 각 요소에 대해 실행하는 메서드이다.</p>
<pre><code>arr.forEach(callback(currentvalue[, index[, array]])[, thisArg])</code></pre><p>매개변수로는 각 요소에 실행될 <code>callback</code>와 이 <code>callback</code> 함수에 들어갈 수 있는 매개변수 <code>currentvalue</code>과 생략이 가능한 <code>index</code>, <code>array</code>가 있고 <code>thisArg</code>가 있다.</p>
<ul>
<li><p><code>callback</code> : 각 요소에 실행될 <code>callback</code>을 의미하는 매개변수이다.</p>
<ul>
<li><p><code>currentvalue</code> : 각 요소를 하나의 매개변수로 가리키는 매개변수이다.</p>
</li>
<li><p><code>index</code> : 각 요소의 <code>index</code>의 정보를 가지는 매개변수이다.</p>
</li>
<li><p><code>array</code> : <code>forEach</code> 메서드를 호출하는 배열을 가리키는 변수이다. 자주 사용하지 않는다.</p>
</li>
</ul>
</li>
</ul>
<blockquote>
<p>❓ 콜백 함수의 매개변수는 매개변수 명을 변경할 수 있다.</p>
</blockquote>
<pre><code class="language-HTML">&lt;script&gt;
  let arr = [1,2,3,4,5];

  arr.forEach((currentValue, index, array)=&gt;{
      console.log(`currentValue : ${currentValue}`, `index : ${index}`);
      console.log(&quot;메서드를 호출한 배열 : &quot;, array);
  })
&lt;/script&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/9408081c-b732-4991-aa5e-02a990a9cc7b/image.png" alt=""></p>
<p>각각의 요소마다 <code>console.log()</code>가 적용이 되었고, 메서드를 호출한 <code>array</code>를 출력하고 있는 것을 볼 수 있다.</p>
<p><code>forEach()</code>는 중요한 점이 <strong>원본 배열을 변형하지 않고 메서드가 항상 반환하는 값</strong>이 <code>undefined</code> 이다. 그래서 메서드를 체이닝해서 사용할 수 없다.</p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/4c523fac-15f8-4d54-9798-4a52c21d5f6a/image.png" alt=""></p>
<h3 id="매개변수-array를-사용하면-원본-배열-변형-가능하다-🔄">매개변수 array를 사용하면 원본 배열 변형 가능하다. 🔄</h3>
<p>메서드가 자체가 직접적으로 원본 배열을 변형하지 않지만 콜백함수의 매개변수인 <code>array</code>를 통해 변경할 원본 배열을 변형할 수 있다. </p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/9b5aa5b3-5b20-4a90-b10d-2621d801d7ee/image.png" alt=""></p>
<p><code>array</code>를 사용해서 원본 배열이 변형할 것을 알 수 있다.</p>
<br>

<h3 id="for문을-foreach로-변환하기-⭐️">for문을 forEach()로 변환하기 ⭐️</h3>
<p>forEach는 각 요소마다 함수를 실행하는 메서드이다. 이것은 요소를 반복하여 연산하는 for문과 비슷하다. 그렇기 때문에 for문을 forEach()로 변환이 가능하다.</p>
<p>우선 for문의 예제를 만들어보도록 하자.
아래의 for문은 각 요소를 변수 <code>i</code>를 통해 실행문을 반복한다.</p>
<pre><code class="language-HTML">&lt;script&gt;
  let arr = [1,2,3,4,5]

  for(let i = 0; i &lt; arr.length; i++){
    arr[i] = arr[i] ** 2; 
  }

  console.log(arr); // [1,4,9,16,25] 
&lt;/script&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/ce0805b8-e52e-4bd1-b2c0-6e45441c6bf0/image.png" alt=""></p>
<p>이것을 <code>forEach()</code>로 변환하면 아래처럼 작성할 수 있다.</p>
<pre><code class="language-HTML">&lt;script&gt;
  let arr = [1,2,3,4,5]

  arr.forEach((element, index, array) =&gt; {
      array[index] = element ** 2;
  })

  console.log(arr); // [1,4,9,16,25] 
&lt;/script&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/7e2e3e5b-cb91-41a0-9149-17a0b879e567/image.png" alt=""></p>
<h2 id="arrayprototypefilter">Array.prototype.filter()</h2>
<p><code>Array.prototype.filter()</code>는 <strong>주어진 함수의 테스트를 통과하는 요소(조건에 true를 반환하는 요소)만</strong>을 반환하여 <strong>새로운 배열로 생성하는 메서드</strong>이다.</p>
<p>말그대로 우리가 쇼핑몰에 들어갔을 때 옵션을 선택해서 보고 싶은 상품만 보는 기능을 가능하게 해주는 메서드라고 생각하면 이해가 쉽다.</p>
<pre><code>arr.filter(callback(element[, index[, array]])[, thisArg])</code></pre><p>이 메서드가 사용하는 매개변수는 위에서 봤던 <code>forEach</code>와 같다.</p>
<ul>
<li><p><code>callback</code> : 각 요소를 테스트하는 콜백함수를 가리키는 매개변수이다. 이 콜백함수의 매개변수로 <code>currentValue</code>, <code>index</code>, <code>array</code>, <code>thisArg</code>가 있다.</p>
<ul>
<li><code>currentValue</code> : 콜백 함수를 실행할 각 요소를 가리키는 매개변수이다. </li>
<li><code>index</code> : 각 요소의 인덱스를 가리키는 매개변수이다.</li>
<li><code>array</code> : 해당 콜백 함수를 호출하는 배열을 가리키는 매개변수이다.</li>
<li><code>thisArg</code> : 콜백 함수를 실행할 때 <code>this</code>가 되는 값을 설정하는 매개변수이다.  </li>
</ul>
</li>
</ul>
<blockquote>
<p>❓ 콜백 함수의 매개변수는 매개변수 명을 변경할 수 있다.</p>
</blockquote>
<p>우선 원본 배열에 영향을 주는지 먼저 살펴보도록 하자.</p>
<pre><code class="language-HTML">&lt;script&gt;
  let arr = [10,20,30,40,50,60]

  // 순회하는 요소 중 3의 배수만을 추출하는 filter()
  arr.filter(currentValue =&gt; currentValue % 3 == 0); // [30,60]

  // 원본 배열은 변하지 않는다.
  console.log(arr); // [10,20,30,40,50,60]
&lt;/script&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/348babb2-c3c9-4695-a756-77e78707395f/image.png" alt=""></p>
<p><code>filter()</code>를 사용하면 줄에서는 추출한 데이터를 반환할 수 있지만 원본 배열은 변하지 않는 것을 확인할 수 있다.</p>
<p>만약 추출한 데이터를 사용하고 싶다면 아래처럼 <strong>변수에 할당을 해서 사용할 수 있다.</strong></p>
<pre><code class="language-HTML">&lt;script&gt;
  let arr = [10,20,30,40,50,60]

  // 순회하는 요소 중 3의 배수만을 추출하는 filter()
  arr.filter(currentValue =&gt; currentValue % 3 == 0);

  // 원본 배열은 변하지 않는다.
  console.log(arr); // [10,20,30,40,50,60]

  // 변수 copyArr에 저장한 
  console.log(copyArr); // [30, 60] 
&lt;/script&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/5546b84a-6bb6-429f-be03-a71daa543a5d/image.png" alt=""></p>
<p>실무에서는 보통 조건에 맞는 데이터를 뽑아내는데 사용한다고 한다.
아래의 <strong>배열 안에 객체가 있는 형태의 데이터</strong>는 아직 정리하지 않는 fetch와 Promise에서 자주 사용하는 데이터 포멧인 <code>JSON</code>이다.</p>
<p>이 JSON을 한번 <code>filter</code>를 통해 데이터를 추출해보도록 하자.</p>
<pre><code class="language-HTML">&lt;script&gt;
  // 데이터를 모아둘 번수 userData

  const userData = [
  {
    &quot;id&quot;: 1,
    &quot;name&quot;: &quot;Leanne Graham&quot;,
    &quot;username&quot;: &quot;Bret&quot;,
    &quot;email&quot;: &quot;Sincere@april.biz&quot;,
    &quot;address&quot;: {
      &quot;street&quot;: &quot;Kulas Light&quot;,
      &quot;suite&quot;: &quot;Apt. 556&quot;,
      &quot;city&quot;: &quot;Gwenborough&quot;,
      &quot;zipcode&quot;: &quot;92998-3874&quot;,
      &quot;geo&quot;: {
        &quot;lat&quot;: &quot;-37.3159&quot;,
        &quot;lng&quot;: &quot;81.1496&quot;
      }
    },
    &quot;phone&quot;: &quot;1-770-736-8031 x56442&quot;,
    &quot;website&quot;: &quot;hildegard.org&quot;,
    &quot;company&quot;: {
      &quot;name&quot;: &quot;Romaguera-Crona&quot;,
      &quot;catchPhrase&quot;: &quot;Multi-layered client-server neural-net&quot;,
      &quot;bs&quot;: &quot;harness real-time e-markets&quot;
    }
  },
  {
    &quot;id&quot;: 2,
    &quot;name&quot;: &quot;Ervin Howell&quot;,
    &quot;username&quot;: &quot;Antonette&quot;,
    &quot;email&quot;: &quot;Shanna@melissa.tv&quot;,
    &quot;address&quot;: {
      &quot;street&quot;: &quot;Victor Plains&quot;,
      &quot;suite&quot;: &quot;Suite 879&quot;,
      &quot;city&quot;: &quot;NewYork&quot;,
      &quot;zipcode&quot;: &quot;90566-7771&quot;,
      &quot;geo&quot;: {
        &quot;lat&quot;: &quot;-43.9509&quot;,
        &quot;lng&quot;: &quot;-34.4618&quot;
      }
    },
    &quot;phone&quot;: &quot;010-692-6593 x09125&quot;,
    &quot;website&quot;: &quot;anastasia.net&quot;,
    &quot;company&quot;: {
      &quot;name&quot;: &quot;Deckow-Crist&quot;,
      &quot;catchPhrase&quot;: &quot;Proactive didactic contingency&quot;,
      &quot;bs&quot;: &quot;synergize scalable supply-chains&quot;
    }
  },
  {
    &quot;id&quot;: 3,
    &quot;name&quot;: &quot;Clementine Bauch&quot;,
    &quot;username&quot;: &quot;Samantha&quot;,
    &quot;email&quot;: &quot;Nathan@yesenia.net&quot;,
    &quot;address&quot;: {
      &quot;street&quot;: &quot;Douglas Extension&quot;,
      &quot;suite&quot;: &quot;Suite 847&quot;,
      &quot;city&quot;: &quot;NewYork&quot;, // 함수에서 true
      &quot;zipcode&quot;: &quot;59590-4157&quot;,
      &quot;geo&quot;: {
        &quot;lat&quot;: &quot;-68.6102&quot;,
        &quot;lng&quot;: &quot;-47.0653&quot;
      }
    },
    &quot;phone&quot;: &quot;1-463-123-4447&quot;,
    &quot;website&quot;: &quot;ramiro.info&quot;,
    &quot;company&quot;: {
      &quot;name&quot;: &quot;Romaguera-Jacobson&quot;,
      &quot;catchPhrase&quot;: &quot;Face to face bifurcated interface&quot;,
      &quot;bs&quot;: &quot;e-enable strategic applications&quot;
    }
  }
 ]


  // filter 사용해서 주소 상의 도시가 &quot;NewYork&quot;이면 user의 정보를 새로운 요소로 반환
  const newyork = userData.filter(user =&gt; user.address.city === &quot;NewYork&quot;)

  // filter로 추출된 데이터 확인
  console.log(newyork) // [{...}, {...}], 아래 이미지 확인

&lt;/script&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/c4332f29-74e4-4202-a5fb-023850f3858b/image.png" alt=""></p>
<p>위의 이미지를 확인해보면 객체의 <code>key</code>인 <code>city</code>의 <code>value</code>가 &quot;NewYork&quot;인 user는 2명인 것을 알 수 있다.</p>
<h2 id="arrayprototypemap">Array.prototype.map()</h2>
<p><code>Array.prototype.map()</code>은 <strong>배열 내의 모든 요소 각각에 대하여 주어진 함수를 호출한 결과를 모아 새로운 배열을 반환</strong>하는 메서드이다.</p>
<pre><code>arr.map(callback(currentValue[, index[, array]])[, thisArg])</code></pre><p>매개변수로는 <code>forEach()</code>와 <code>filter()</code>와 동일한 매개변수를 가지고 있다.</p>
<ul>
<li><p><code>callback</code> : 각 요소에 대해 실행하는 콜백함수를 가리키는 매개변수이다. 이 콜백함수의 매개변수로 <code>currentValue</code>, <code>index</code>, <code>array</code>, <code>thisArg</code>가 있다.</p>
<ul>
<li><code>currentValue</code> : 콜백 함수를 실행할 각 요소를 가리키는 매개변수이다. </li>
<li><code>index</code> : 각 요소의 인덱스를 가리키는 매개변수이다.</li>
<li><code>array</code> : <code>map()</code>를 호출하는 배열을 가리키는 매개변수이다. 보통 자주 사용하지 않는다.</li>
<li><code>thisArg</code> : 콜백 함수를 실행할 때 <code>this</code>가 되는 값을 설정하는 매개변수이다.  </li>
</ul>
</li>
</ul>
<blockquote>
<p>❓ 콜백 함수의 매개변수는 매개변수 명을 변경할 수 있다.</p>
</blockquote>
<p>매개변수를 <code>console.log()</code>를 통해서 매개변수의 결과를 한번 살펴보도록 하자.</p>
<pre><code class="language-HTML">&lt;script&gt;
  let arr = [1,2,3,4,5]

  arr.map((currentValue, index, array) =&gt; {
      console.log(`currentValue : ${currentValue}`, `index : ${index}`)
      console.log(&quot;map을 호출한 배열 : &quot; + array)
  })
&lt;/script&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/ad29e056-9e8a-40c8-9d55-5d4d87e7ec62/image.png" alt=""></p>
<p>그럼 <code>map()</code>의 기능인 모든 요소에 함수를 실행하는 것과 새로운 배열을 반환하는 것을 보도록 하자.</p>
<pre><code class="language-HTML">&lt;script&gt;
  let arr = [1,2,3,4,5]

  // 각 요소의 제곱 값을 구한다.
  arr.map((currentValue) =&gt; {
    currentValue ** 2
  }); // [1,4,9,16,25]

  // 원본 배열에는 영향을 주지 않는다.
  console.log(arr); // [1,2,3,4,5]
&lt;/script&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/50f070f6-92da-4d4c-a8cb-6909125ecbdf/image.png" alt=""></p>
<br>
<hr>
<br>


<h4 id="정리를-마치며-⛳️">정리를 마치며 ⛳️</h4>
<p>배열의 메서드의 정리가 끝났다.</p>
<p>배열의 메서드에서 가장 중요한 <code>forEach()</code>, <code>filter()</code>, <code>map()</code>을 정리를 했는데 <code>filter</code>와 <code>map</code>을 사용하는 것을 정말 열심히 해봐야겠다.</p>
<p>특히 <code>filter()</code>에서 JSON을 통해서 데이터를 불러오는게 아직은 헷갈리는게 많아서 많은 에러를 콘솔창에 띄우곤 했다..</p>
<p>개념은 이해했지만 사용에는 아직 미숙한 것 같아 연습을 더 해보고 게시글에 예제를 추가할 예정이다.</p>
<p>또한 다른 메서드도 잊을 수 있으니 전체적으로 한번 더 복습해야겠다! 🔥</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[모던자바스크립트 Deep Dive] 10장. 객체 리터럴 🦎]]></title>
            <link>https://velog.io/@untiring_dev/%EB%AA%A8%EB%8D%98%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Deep-Dive-10%EC%9E%A5-%EA%B0%9D%EC%B2%B4-%EB%A6%AC%ED%84%B0%EB%9F%B4</link>
            <guid>https://velog.io/@untiring_dev/%EB%AA%A8%EB%8D%98%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Deep-Dive-10%EC%9E%A5-%EA%B0%9D%EC%B2%B4-%EB%A6%AC%ED%84%B0%EB%9F%B4</guid>
            <pubDate>Sun, 15 May 2022 14:25:19 GMT</pubDate>
            <description><![CDATA[<p>함수로 넘어갔는데 함수를 제대로 이해하려면 &#39;객체&#39;를 제대로 알아야한다고 해서 이번 Deep Dive 공부를 &quot;객체&quot;로 선정했다.</p>
<h1 id="객체란">객체란?</h1>
<p>자바스크립트는 객체(Object) 기반의 프로그래밍 언어이며, 자바스크립트를 구성하는 거의 모든 것이 객체이다.</p>
<p><code>number</code>, <code>string</code>, <code>boolean</code> 등 원시 타입(Primitive type)을 제외한 <code>function</code>, <code>Array</code>, <code>RegExp</code> 등은 모두 객체이다.</p>
<ul>
<li><strong>원시 타입(Primitive type)</strong> : 단 하나의 값만 나타냄</li>
</ul>
<pre><code class="language-HTML">&lt;script&gt;
  let num = 1;
  let str = &quot;hello world&quot;;
  let bool = true; 
&lt;/script&gt;</code></pre>
<ul>
<li><strong>객체 타입(Object/Reference type)</strong> : 다양한 타입의 값(객체 타입)을 하나의 단위로 구성한 복합적인 자료구조</li>
</ul>
<pre><code class="language-HTML">&lt;script&gt;
  let user = {
      name : &quot;hyunwoo&quot;,
      age : 30,
  }
&lt;/script&gt;</code></pre>
<h2 id="객체의-구조">객체의 구조</h2>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/1dfecc9f-d95b-486a-845e-d9c6a40c6f6d/image.png" alt=""></p>
<p>객체는 내부에 다양한 타입의 값을 넣을 수 있다.</p>
<p>내부 구조는 아래처럼 구성되어 있다.</p>
<ul>
<li><p>프로퍼티(Property) : <code>key</code>와 <code>value</code>로 구성되는 객체의 상태를 나타내는 값(data)</p>
</li>
<li><p>메서드(Method) : <code>Property</code>를 참조하고 조작할 수 있는 동작</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/8d4836c7-24c1-47e0-8ec7-0fbbe559ed52/image.png" alt=""></p>
<p>메서드는 <code>key</code>에 함수가 <code>value</code>로 사용되는 것을 말한다. 여기서 함수가 <code>value</code>로 사용될 수 있는 이유는 <code>일급객체</code>이기 때문이다.</p>
<blockquote>
<p>❓ 일급객체란 다음 조건을 만족하는 객체를 말한다.</p>
</blockquote>
<p>일단 <strong>함수는 객체</strong>라는 것을 알아야한다. 아래 이미지에서 객체를 <code>console.dir()</code>를 통해 살펴보면 아래처럼 객체 안의 <code>Property</code>로 구성되어 있다.</p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/99f8ceca-a900-491c-9c70-5dcb7cd3bcee/image.png" alt=""></p>
<ul>
<li>무명의 리터럴<strong>(익명함수?)</strong>로 생성할 수 있다. 즉, 런타임(할당 단계)에 생성이 가능하다.</li>
</ul>
<p>아래의 이미지는 함수를 생성하지만 익명으로 생성하는 것이다.
여기서 변수에 함수를 할당하는 것이기 때문에 런타임에 생성하는 것이라고 할 수 있다. </p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/450a138a-a642-426d-811f-5ad9107919ec/image.png" alt=""></p>
<ul>
<li>변수나 자료구조(객체, 배열 등)에 저장할 수 있다.</li>
</ul>
<pre><code class="language-HTML">&lt;script&gt;
  const predicates = {increase, decrease}; // 아래에 설명
&lt;/script&gt;</code></pre>
<pre><code class="language-HTML">&lt;script&gt;
  // const predicates = {increase, decrease}는 아래와 같다.
  const predicates = {
      increase : increase, // 함수 increase
      decrease : decrease, // 함수 decrease 
  }

  // 즉, 메서드가 된 것이다.
  predicates.increase(5); // 6
  predicates.decrease(3); // 2
&lt;/script&gt;</code></pre>
<ul>
<li>함수의 매개변수에 전달할 수 있다.</li>
<li>함수의 반환값으로 사용할 수 있다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/9bb840a6-7944-48af-a536-fc2caaf60407/image.png" alt=""></p>
<h2 id="객체-리터럴에-의한-객체-생성그-외-생성방법">객체 리터럴에 의한 객체 생성(그 외 생성방법)</h2>
<p>자바스크립트는 다양한 객체 생성 방법을 지원하고 있다.</p>
<ul>
<li>객체 리터럴 : <code>const obj = {}</code>, 객체를 생성하는데 가장 일반적이고 간단한 방법</li>
</ul>
<pre><code class="language-HTML">&lt;script&gt;
  const obj = {
      name : &quot;hello&quot;,
    age : 30;
  }
&lt;/script&gt;</code></pre>
<ul>
<li>Object 생성자 함수 : <code>new Object()</code>를 통해 빈 객체를 생성하는 방법</li>
</ul>
<pre><code class="language-HTML">&lt;script&gt;
  const obj = new Object();

  console.log(obj); // {}

  obj.name = &quot;hello&quot;;
  obj.age = 30;
  console.log(obj); // {name:&quot;hello&quot;, age:30}
&lt;/script&gt;</code></pre>
<ul>
<li><p>생성자 함수 : 함수를 통해 객체를 생성하는 방법</p>
<pre><code class="language-HTML">&lt;script&gt;
function obj(name, age){
    this.name = name;
  this.age = age;
}

const obj1 = new obj(&#39;hello&#39;, 30);
console.log(obj1); // {name: &#39;hello&#39;, age: 30}
const obj2 = new obj(&quot;world&quot;, 29);
console.log(obj2); // {name: &#39;world&#39;, age: 29}
&lt;/script&gt;</code></pre>
</li>
<li><p><code>Object.create</code> 메서드</p>
</li>
</ul>
<pre><code class="language-HTML">&lt;script&gt;
  const obj = Object.create(null, {name:{value:&quot;hello&quot;}, age:{value:30}}); // 
&lt;/script&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/0bf08c57-f798-4d2e-982f-6a410c99019a/image.png" alt=""></p>
<ul>
<li>클래스(ES6) : <code>class</code> 키워드를 사용하여 정의하는 방법</li>
</ul>
<pre><code class="language-HTML">&lt;script&gt;
  class obj {};

  class obj {
    constructor(name){
        this.name = name;
    }
  }

  const me = new obj(&quot;Shin&quot;);
&lt;/script&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/fd03b3ce-7321-4ded-9543-53cc64587a8c/image.png" alt=""></p>
<h2 id="property">Property</h2>
<p><strong>객체는 프로퍼티의 집합</strong>이며, 프로퍼티는 <code>key</code>, <code>value</code>의 값으로 구성한다.</p>
<pre><code class="language-HTML">&lt;script&gt;
  const me = {
      name : &#39;hyunwoo&#39;, // key : value 형태의 프로퍼티
      age : 30, // key : value 형태의 프로퍼티
  }
&lt;/script&gt;</code></pre>
<br>

<h3 id="프로퍼티와-식별자-네이밍-규칙">프로퍼티와 식별자 네이밍 규칙</h3>
<p>프로퍼티에서 <code>key</code>는 <strong><a href="https://velog.io/@untiring_dev/JS-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EA%B8%B0%EC%B4%88#%EB%B3%80%EC%88%98%EC%9D%98-%EB%84%A4%EC%9D%B4%EB%B0%8D-%EA%B7%9C%EC%B9%99-%F0%9F%A7%91%EF%B8%8F">식별자 네이밍 규칙</a>을 따르지 않아도 된다</strong>. 하지만 이 경우에는 반드시 따옴표<code>&#39;&#39;</code>나 <code>&quot;&quot;</code>를 사용하여 묶어야 한다.</p>
<pre><code class="language-HTML">&lt;script&gt;
  const me = {
    &#39;first-name&#39;: &#39;hyunwoo&#39;,
      last-name : &#39;Shin&#39;,
      age : 30,
  }
&lt;/script&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/1105c050-a1ea-474c-bce4-01e216b93758/image.png" alt=""></p>
<p>따옴표로 묶지 않으면 <code>Uncaugth SyntaxError</code>가 발생한다.</p>
<p>또한 프로퍼티의 <code>value</code>에 접근할 때 대괄호<code>[]</code>를 사용하는 <code>대괄호 표기법</code>만으로 가능하다.
<code>마침표 표기법</code>으로는 에러 발생은 하지 않지만 접근 할 수 없다.</p>
<pre><code class="language-HTML">&lt;script&gt;
  const me = {
    &#39;first-name&#39;: &#39;hyunwoo&#39;,
      &#39;last-name&#39; : &#39;Shin&#39;,
      age : 30,
  }

  console.log(me[first-name]) // Uncaught ReferenceError: first is not defined
  console.log(me[&#39;first-name&#39;]); // &#39;hyunwoo&#39;
  console.log(me.first-name); // NaN
&lt;/script&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/6cb3dd21-c626-465d-ad27-1fd9e0034214/image.png" alt=""></p>
<p>위의 <code>마침표 표기법</code>으로 할 경우 first와 name 사이의 <code>-</code>를 연산자 <code>-</code>로 인식하기 때문에 <code>String - String = NaN</code>으로 연산 결과가 나온다.</p>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/5954c2a5-66b5-4d20-a522-4f6be7c05e60/image.png" alt=""></p>
<p>또 주의해야하는 점은 <code>대괄호 표기법</code>을 통해서 접근할 경우 <code>[]</code> 내부에 들어가는 값은 반드시 따옴표<code>&quot;&quot;</code>로 묶어야 한다. 묶지 않으면 에러가 발생한다.</p>
<br>

<p><code>key</code>는 네이밍 컨벤션을 따르지 않아도 되기 때문에 <code>&#39;&#39;</code>와 <code>숫자가 앞에 오는 경우</code>, <code>예약어</code> 등을 사용해도 에러가 발생하지 않는다. 물론 에러가 발생하지 않는다고 해서 사용하는 것을 절대 권장하지 않는다.</p>
<pre><code class="language-HTML">&lt;script&gt;
  // 에러가 발생하지 않는다.
  const wrongObj = {
      &#39;&#39; : &#39;&#39;,
    0 : 1,
    var : &#39;&#39;,
      function : &#39;&#39;,
  }
&lt;/script&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/32ad32a4-f76c-452f-a5f0-dfc03a01cdc9/image.png" alt=""></p>
<h3 id="프로퍼티-중복">프로퍼티 중복</h3>
<p>프로퍼티가 중복 선언 시에 나중에 선언한 프로퍼티가 먼저 선언한 프로퍼티를 덮어쓴다.</p>
<pre><code class="language-HTML">&lt;script&gt;
  const wrongObj = {
      name : &#39;hello&#39;,
      name : &#39;world&#39;,
  }

  console.log(wrongObj.name); // &#39;world&#39;
&lt;/script&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/ec27f190-2dd2-4f6b-8a2b-656ffc08c409/image.png" alt=""></p>
<h3 id="프로퍼티-값-갱신">프로퍼티 값 갱신</h3>
<pre><code class="language-HTML">&lt;script&gt;
  const me = {
      name : &#39;hyunwoo&#39;
  }

  me.name = &#39;Shin&#39;;

  console.log(me); // {name : &#39;Shin&#39;}
&lt;/script&gt;</code></pre>
<h3 id="프로퍼티-동적-생성">프로퍼티 동적 생성</h3>
<p>만약 객체 내에 프로퍼티가 없는데 값을 할당하면 <strong>프로퍼티가 동적으로 생성되어 추가되고 그 프로퍼티에 대한 값이 할당</strong>된다.</p>
<pre><code class="language-HTML">&lt;script&gt;
  const me = {
      name : &#39;hyunwoo&#39;
  }

  me.age = 30;

  console.log(me); // {name:&#39;hyunwoo&#39;, age:30}
&lt;/script&gt;</code></pre>
<h3 id="프로퍼티-삭제">프로퍼티 삭제</h3>
<p>프로퍼티를 삭제하고 싶을 때는 <code>delete</code> 연산자를 사용하여 프로퍼티를 삭제할 수 있다.</p>
<p>만약 없는 프로퍼티를 삭제하더라도 오류가 발생하지 않기 때문에 확인을 잘 하는 것이 좋다.</p>
<pre><code class="language-HTML">&lt;script&gt;
  const me = {
      name : &#39;hyunwoo&#39;,
      age : 30,
  }

  delete me.age;
  console.log(me); // {name:&#39;hyunwoo&#39;}

  // 없는 프로퍼티를 삭제하면 오류가 발생하지 않는다.
  delete me.city;
  console.log(me); // {name:&#39;hyunwoo&#39;}
&lt;/script&gt;</code></pre>
<br>

<h2 id="method">Method</h2>
<p>메서드는 <strong>객체에 묶여 있는 함수</strong>를 의미한다.</p>
<pre><code class="language-HTML">&lt;script&gt;
  const name = {
      makeName : function(name){
          console.log(&quot;Hi! This is ${name}&quot;)
      }
  }
&lt;/script&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/4a0a9c71-2f9d-422d-8494-85ae3a01c061/image.png" alt=""></p>
<h3 id="메서드-축약-표현">메서드 축약 표현</h3>
<p>보통 메서드를 정의할 때 <code>function</code> 키워드를 사용하지만, <strong>축약형으로 사용할 수 있다.</strong></p>
<pre><code class="language-HTML">&lt;script&gt;
  const name = {
      makeName : function(name){
          console.log(`Hi! This is ${name}`);
      }
  }

  name.makeName(&#39;hyunwoo&#39;) // &#39;Hi! This is hyunwoo&#39;
&lt;/script&gt;</code></pre>
<p><code>function</code> 키워드와 <code>:</code>을 생략해서 축약형으로 만들면 된다.</p>
<pre><code class="language-HTML">&lt;script&gt;
  const name = {
      makeName(name){
          console.log(`Hi! This is ${name}`);
      }
  }

  name.makeName(&#39;hyunwoo&#39;) // &#39;Hi! This is hyunwoo&#39;
&lt;/script&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/untiring_dev/post/d89000b0-8d86-4e8f-8df4-3662d748eb93/image.png" alt=""></p>
]]></description>
        </item>
    </channel>
</rss>