<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>kykim_dev.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Wed, 16 Feb 2022 07:17:35 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>kykim_dev.log</title>
            <url>https://images.velog.io/images/kykim_dev/profile/5eaf7b80-e76e-4da1-b04b-cc5df6710735/IMG_7237.JPG</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. kykim_dev.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/kykim_dev" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[React #15] 관심사의 분리(Separation of Concerns, SoC)와 Custom Hook]]></title>
            <link>https://velog.io/@kykim_dev/%EA%B4%80%EC%8B%AC%EC%82%AC%EC%9D%98-%EB%B6%84%EB%A6%ACSeparation-of-Concerns-SoC%EC%99%80-Custom-Hook</link>
            <guid>https://velog.io/@kykim_dev/%EA%B4%80%EC%8B%AC%EC%82%AC%EC%9D%98-%EB%B6%84%EB%A6%ACSeparation-of-Concerns-SoC%EC%99%80-Custom-Hook</guid>
            <pubDate>Wed, 16 Feb 2022 07:17:35 GMT</pubDate>
            <description><![CDATA[<h2 id="separation-of-concerns">Separation of Concerns</h2>
<ul>
<li>하나의 함수, 변수, 클래스, 컴포넌트에게 한번에 너무 많은 일(concerns)을 부여하게 되면 그 코드를 읽는 사람은 혼란스러울 수 있다.</li>
<li>가장 간단한 해결책은 <strong>한번에 한 가지 걱정만 하도록 단위를 잘게 나누는 것</strong>이다. 즉, <strong>코드는 단위 별로 하나의 관심사만 갖도록 하고 그 관심사에 대해서만 충실히 동작하도록 만들어야 한다.</strong></li>
<li>그렇게 되면 어떤 문제가 생겼을 때 전체 기능을 파악하기 위해 읽어야 하는 코드의 단위가 줄어들게 되고, 코드에 대한 파악이 빨라지므로 문제를 효과적으로 해결할 수 있다.<code>Divide &amp; Conquer</code> (분할 - 정복)의 용이</li>
<li>또한 하나의 수정사항으로 인해 전체가 수정되는 것이 아니라, 해당 사항이 있는 일부분만 변경시키기 때문에 변화에 대해서도 내구성을 갖추게 된다.</li>
<li>컴퓨터 공학에서는 이렇게 &#39;한번에 한 가지만 걱정해도 괜찮도록&#39; 각각의 관심사에 따라 코드를 분리하는 기법을 <code>관심사의 분리</code> 라고 부른다.</li>
<li>관심사의 분리가 적절히 구현된 코드에서는 <code>Loose Coupling</code> (낮은 결합도, 각각의 코드가 서로 얽혀있지 않고 독립적으로 잘 분리되어 있음)과 <code>High Cohesive</code> (높은 응집도, 유사한 내용끼리 비슷한 위치에 잘 모여 있음)와 같은 특성을 발견할 수 있다.</li>
</ul>
<h2 id="관심사의-분리-장점">관심사의 분리 장점</h2>
<ul>
<li>코드가 더욱 명료해짐 : 자신이 어떤 일을 하고, 어떤 목적을 가지고 설계된 코드인지 보다 잘 드러나게 됨.</li>
<li>코드 재사용성이 올라감 : 여러 역할이 엉켜있는 코드보다, 역할 별로 잘 분리되어 있는 코드를 재사용하기가 쉬움.</li>
<li>유지 보수가 용이함 : 변경 사항이 발생했을 때 해당 관심사에 연관된 코드만 수정하면 됨.</li>
<li>테스트 코드를 작성하기 쉬워짐 : 얽혀있는 로직보다 분리되어 있는 로직에 대한 테스트가 보다 더 간단해짐<h3 id="예시">예시</h3>
<pre><code class="language-javascript">const loginForm = document.getElementsByClassName(&#39;loginForm&#39;)[0];
const loginBtn = document.getElementById(&#39;loginBtn&#39;);
</code></pre>
</li>
</ul>
<p>function handleInput() {
  const idValue = document.getElementById(&#39;id&#39;).value;
  const pwValue = document.getElementById(&#39;pw&#39;).value;</p>
<p>  const isIdValid = idValue.length &gt; 1;
  const isPasswordValid = pwValue.length &gt; 1;
  const isLoginInputValid = isIdValid &amp;&amp; isPasswordValid;</p>
<p>  loginBtn.disabled = !isLoginInputValid
  loginBtn.style.opacity = isLoginInputValid ? 1 : 0.3;
  loginBtn.style.cursor = isLoginInputValid ? &#39;pointer&#39; : &#39;default&#39;;
}</p>
<p>const init = () =&gt; {
  loginForm.addEventListener(&#39;input&#39;, handleInput);
};</p>
<p>init();</p>
<pre><code>```javascript
function $(selector) {
  return document.querySelector(selector)
}

const loginForm = $(&#39;.loginForm&#39;);
const loginBtn = $(&#39;#loginBtn&#39;);

const validator = {
  id: (id) =&gt; id.length &gt; 1,
  password: (password) =&gt; password.length &gt; 1,
}

function validateForm(form, validator) {  
  for (const key in form) {
    const value = form[key]
    const isValid = validator[key](value)

    if (!isValid) return false
  }

  return true
}

function handleButtonActive(button, isFormValid) {
  button.disabled = isFormValid ? false : true;
  button.style.opacity = isFormValid ? 1 : 0.3;
  button.style.cursor = isFormValid ? &#39;pointer&#39; : &#39;default&#39;;  
}

function handleInput() {
  const isFormValid = validateForm({
    id: $(&#39;.id&#39;).value,
    password: $(&#39;.pw&#39;).value
  }, validator)

  handleButtonActive(loginBtn, isFormValid)
}

function init() {
  loginForm.addEventListener(&#39;input&#39;, handleInput);
};

init();</code></pre><h2 id="view와-logic의-분리">View와 Logic의 분리</h2>
<ul>
<li><p>&#39;관심사의 분리&#39;를 프론트엔드 개발에 적용할 수 있다. </p>
</li>
<li><p>Redux와 Create React App의 창시자로 React 생태계에서 아주 유명한 Dan Abramov는 2015년 본인의 블로그에 <code>Presentational - Container</code> 패턴으로 React 컴포넌트를 관리하는 방법을 제안했다. (<a href="https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0">본문 링크</a>)</p>
</li>
<li><p><strong>Presentational Component</strong> : UI Only 컴포넌트 (<code>View</code>)</p>
<pre><code class="language-jsx">  const UserList = ({ users }) =&gt; {
      return (
          &lt;ul&gt;
              {users.map(user =&gt; {
                  return &lt;li key={user.id}&gt;{user.name}&lt;/li&gt;
              })}
          &lt;/ul&gt;
      )
  }</code></pre>
<ul>
<li>state, effect 등 로직 없음 → 화면을 렌더링하는 데 필요한 코드만 존재 (갖더라도 UI에 대한 상태만)</li>
<li>함수 컴포넌트로 구현</li>
</ul>
</li>
<li><p><strong>Container Component</strong> : Logic Only 컴포넌트 (<code>Logic</code>)</p>
<pre><code class="language-jsx">  class UserListContainer extends React.Component {
      state = {
          users: []
      }

      componentDidMount() {
          fetchUsers(&quot;/users&quot;)
              .then(res =&gt; res.json())
              .then(res =&gt; this.setState({ users: res.users }))
      }

      render() {
          return &lt;UserList users={this.state.users}&gt;
      }
  }</code></pre>
<ul>
<li>데이터 호출, state, 필터링 등 복잡한 로직을 처리하는 코드만 존재 (UI 관련 코드를 가져서는 안됨)</li>
<li>클래스형 컴포넌트로 구현</li>
</ul>
</li>
<li><p>이 패턴이 제안될 당시에는 Hooks가 존재하지 않았다. 그래서 함수 컴포넌트는 State와 Effect를 처리할 수 없었고, UI를 그리는 데만 제한적으로 사용되었다. 반면 클래스형 컴포넌트는 State와 Life Cycle API(Effect에 대응됨)를 사용해 React의 모든 기능을 구현할 수 있었다.
=&gt; 이렇게 View와 Logic을 분리함으로써 컴포넌트는 더 작은 영역에 대해 더 확실한 책임을 지는 여러 개의 컴포넌트로 분할된다.</p>
</li>
<li><p>View에 집중하는 Presentational Component는 단순히 주입 받은 정보를 렌더링할 뿐(ex. <code>(state, props) ⇒ UI</code>)이다. 주입 받은 정보만 올바르다면 컴포넌트는 항상 올바른 UI를 리턴하게 된다. 로직과 분리되었기 때문에 여러 곳에서 재활용하기도 쉽고, 여러 가지 Container Component와 조합하기도 쉽다.</p>
</li>
<li><p>하지만 Hooks의 등장으로 더이상 이 패턴을 권장하지 않는다.</p>
</li>
</ul>
<h2 id="custom-hook">Custom Hook</h2>
<ul>
<li>Custom Hook은 이름이 <code>use</code>로 시작하는 자바스크립트 함수다.</li>
<li>Custom Hook을 사용하면 지금까지 컴포넌트 내부에 한 덩이로 결합하여 사용했던 State와 Effect를 다음과 같이 분리하여 사용할 수 있다. 마치 여러 벽돌을 끼워 맞춰 건물을 만들듯이 React 컴포넌트를 여러 Hook을 조합하는 방식으로 개발할 수 있게 된다.</li>
<li>또한 로직을 독립적인 함수로 분리함으로서 하나의 로직을 여러 곳에서 반복적으로 재사용할 수 있다.</li>
</ul>
<pre><code class="language-jsx">const UserList = ({ users }) =&gt; {
    const [users, setUsers] = useState([])

    useEffect(() =&gt; {
        fetchUsers(&quot;/users&quot;)
            .then(res =&gt; res.json())
            .then(res =&gt; setUsers(res.users))
    }, [])

    return (
        &lt;ul&gt;
            {users.map(user =&gt; {
                return &lt;li key={user.id}&gt;{user.name}&lt;/li&gt;
            })}
        &lt;/ul&gt;
    )
}</code></pre>
<ul>
<li>View와 Logic을 분리해보면 아래와 같다.</li>
</ul>
<pre><code class="language-jsx">const UserList = ({ users }) =&gt; {
    // Logic
    const users = useGetUserList()

    // View
    return (
        &lt;ul&gt;
            {users.map(user =&gt; {
                return &lt;li key={user.id}&gt;{user.name}&lt;/li&gt;
            })}
        &lt;/ul&gt;
    )
}</code></pre>
<pre><code class="language-jsx">const useGetUserList = () =&gt; {
    const [users, setUsers] = useState([])

    useEffect(() =&gt; {
        fetchUsers(&quot;/users&quot;)
            .then(res =&gt; res.json())
            .then(res =&gt; setUsers(res.users))
    }, [])

    return users
}</code></pre>
<ul>
<li><code>UserList</code> 의 내용을 파악해야 하는 입장에서 적절하게 지어진 Custom Hook의 이름과, 그 Hook이 리턴하는 <code>user</code> 라는 이름의 변수를 통해 세부 구현사항은 모르겠지만, 어쨌든 저것이 유저들에 대한 정보라는 사실은 어렵지 않게 알 수 있게 된다.</li>
<li>이제 유저의 정보를 가져오는 세부 구현 사항에 관해서는 <code>UserList</code> 라는 컴포넌트를 살펴보지 않고 <code>useGetUserList</code> hook만 집중하여 살펴보면 된다.</li>
</ul>
<h3 id="여러-기능이-포함된-컴포넌트의-분리">여러 기능이 포함된 컴포넌트의 분리</h3>
<ul>
<li>사용자에게 input을 받고, 해당 내용을 서버로 전송하는 기능과 화면의 스크롤 높이, 위치 등을 받아오는 기능이 모두 들어있는 경우</li>
</ul>
<pre><code class="language-jsx">const UserInput = () =&gt; {
    const [size, setSize] = useState({ width: 0, height: 0 })
    const [position, setPosition] = useState({ x: 0, y: 0 })
    const [userInfo, setUserInfo] = useState({
        username: &quot;&quot;,
        id: &quot;&quot;,
        password: &quot;&quot;,
        email: &quot;&quot;
    })

    const handleUserInput = (e) =&gt; {
        const { name, value } = e.target

        setUserInfo(prev =&gt; ({ ...prev, [name]: value }))
    }

    useEffect(() =&gt; {
        const handleDocumentSize = () =&gt; { ... }

        document.addEventListener(&quot;resize&quot;, handleDocumentSize)
        return () =&gt; {
            document.removeEventListener(&quot;resize&quot;, handleDocumentSize)
        }
    }, [])

    useEffect(() =&gt; {
        const handleDocumentPosition = () =&gt; { ... }

        document.addEventListener(&quot;resize&quot;, handleDocumentPosition)
        return () =&gt; {
            document.removeEventListener(&quot;resize&quot;, handleDocumentPosition)
        }
    }, [])

    return (
        &lt;div&gt;
            &lt;input name=&quot;username&quot; onChange={handleUserInput} /&gt;
            &lt;input name=&quot;id&quot; onChange={handleUserInput} /&gt;
            &lt;input name=&quot;password&quot; onChange={handleUserInput} /&gt;
            &lt;input name=&quot;email&quot; onChange={handleUserInput} /&gt;
        &lt;div /&gt;
    )
}</code></pre>
<ul>
<li>두 가지 Custom Hook으로 나누어 분리해보면, 먼저 <code>UserInput</code> 에 Logic을 추출하여 View 만 남았고, 유저 정보와 관련된 <code>useUserInput</code> 과 화면 크기 조절시마다 문서의 크기와 위치를 리턴하는 <code>useDocumentResize</code> 두 가지 로직으로 분리되어 있음을 확인할 수 있다. </li>
</ul>
<pre><code class="language-jsx">const UserInput = () =&gt; {
    const { userInfo, handleUserInfo } = useUserInput()
    const { size, position } = useDocumentResize()

    return (
        &lt;div&gt;
            &lt;input name=&quot;username&quot; onChange={handleUserInput} /&gt;
            &lt;input name=&quot;username&quot; onChange={handleUserInput} /&gt;
            &lt;input name=&quot;username&quot; onChange={handleUserInput} /&gt;
            &lt;input name=&quot;username&quot; onChange={handleUserInput} /&gt;
        &lt;div/&gt;
    )
}</code></pre>
<ul>
<li>원래 <code>UserInput</code> 에 한덩어리로 있던 유저 정보 입력 관련한 로직이 <code>useUserInfo</code> 로 분리되었다.</li>
</ul>
<pre><code class="language-jsx">const useUserInfo = () =&gt; {
    const [userInfo, setUserInfo] = useState({
        username: &quot;&quot;,
        id: &quot;&quot;,
        password: &quot;&quot;,
        email: &quot;&quot;
    })

    const handleUserInput = (e) =&gt; {
        const { name, value } = e.target

        setUserInfo(prev =&gt; ({ ...prev, [name]: value }))
    }

    return { userInfo, handleUserInput }
}</code></pre>
<ul>
<li>이전에 <code>UserInput</code> 에 한덩어리로 있던 화면 리사이즈 관련한 로직이 <code>useDocumentResize</code> 로 관심사에 따라 분리되었다.</li>
</ul>
<pre><code class="language-jsx">const useDocumentResize = () =&gt; {
    const [size, setSize] = useState({ width: 0, height: 0 })
    const [position, setPosition] = useState({ width: 0, height: 0 })

    useEffect(() =&gt; {
        const handleDocumentSize = () =&gt; { ... }

        document.addEventListener(&quot;resize&quot;, handleDocumentSize)
        return () =&gt; {
            document.removeEventListener(&quot;resize&quot;, handleDocumentSize)
        }
    }, [])

    useEffect(() =&gt; {
        const handleDocumentPosition = () =&gt; { ... }

        document.addEventListener(&quot;resize&quot;, handleDocumentPosition)
        return () =&gt; {
            document.removeEventListener(&quot;resize&quot;, handleDocumentPosition)
        }
    }, [])

    return { size, position }
}</code></pre>
<ul>
<li>모든 경우에 대해 무조건적으로 이런 식의 분리가 권장되는 것은 아니다. 섣부른 추상화는 오히려 코드를 여기저기로 분리시켜 변경을 힘들게 만든다. 추상화가 필요한 경우와 그렇지 않은 경우를 잘 판단하여 코드를 분리해야 한다. </li>
</ul>
<h3 id="참고">참고</h3>
<h3 id="1-custom-hook의-이름은-use로-시작되어야-한다">1) Custom Hook의 이름은 <code>use</code>로 시작되어야 한다.</h3>
<ul>
<li>React에서 Hook의 동작을 처리하는 내부적인 규칙과도 관련이 되어 있고, 공식적인 컨벤션이기 때문에 Custom Hook을 작성할 때는 꼭 <code>use-</code> 로 시작하는 이름을 지어야 한다.</li>
</ul>
<h3 id="2-hook-안에서-다른-hook을-호출할-수-있다">2) Hook 안에서 다른 Hook을 호출할 수 있다.</h3>
<ul>
<li>Hook은 State, Effect를 포함할 수 있는 자바스크립트 함수이고, 또 다른 Hook 역시 동일하게 State, Effect를 포함하고 있는 자바스크립트 함수이기 때문에 Hook과 Hook이 중첩 호출될 수 있다.</li>
</ul>
<pre><code class="language-jsx">import { useParams } from &quot;react-router-dom&quot;

const useGetUserList = () =&gt; {
    const [users, setUsers] = useState([])
    **const { id } = useParams()**

    useEffect(() =&gt; {
        fetchUsers(`/users/${id}`)
            .then(res =&gt; res.json())
            .then(res =&gt; setUsers(res.users))
    }, [])

    return users
}</code></pre>
<h3 id="3-같은-hook을-사용하는-두-개의-컴포넌트는-state를-공유하지-않는다">3) 같은 Hook을 사용하는 두 개의 컴포넌트는 state를 공유하지 않는다.</h3>
<ul>
<li>두 Custom Hook은 서로 호출되는 위치와 타이밍이 다르며, 애초에 서로 다른 스코프(유효범위)를 생성하기 때문에 컴포넌트를 여러번 호출하는 것처럼 완전히 독립적으로 작동한다.</li>
</ul>
<h2 id="use-cases-of-cumstom-hook">Use Cases of Cumstom Hook</h2>
<h3 id="1-usetoggle">1) useToggle</h3>
<ul>
<li><p>boolean 타입의 state를 toggle하는 기능 예시.</p>
<pre><code class="language-jsx">  export const useToggle = (initialValue = false) =&gt; {
    const [state, setState] = useState(initialValue);

      const handleToggle = () =&gt; {
          setState(prev =&gt; !prev)
      }

    return [state, handleToggle]
  };</code></pre>
</li>
</ul>
<h3 id="2-uselockbodyscroll">2) useLockBodyScroll</h3>
<ul>
<li><p>Modal이 띄워져 배경 스크롤을 막아둬야 할 때 사용합니다.</p>
<pre><code class="language-jsx">  const useLockBodyScroll = () =&gt; {
    useLayoutEffect(() =&gt; {
      document.body.style.overflow = &#39;hidden&#39;;
      return () =&gt; {
        document.body.style.overflow = &#39;&#39;;
      };
    }, []);
  };</code></pre>
</li>
</ul>
<h3 id="3-useoutsideclick">3) useOutsideClick</h3>
<ul>
<li><p>특정 DOM의 바깥 지점이 클릭 되었는지를 파악하기 위해 해당 DOM의 ref를 인자로 넘기고, 클릭이 일어났을 때의 동작을 두 번째 인자로 정의해둡니다.</p>
<pre><code class="language-jsx">  import { useState, useEffect, useRef } from &quot;react&quot;;

  function App() {
    const ref = useRef();
    const [isModalOpen, setModalOpen] = useState(false);

      // 활용 예시
    **useOnClickOutside(ref, () =&gt; setModalOpen(false));**

    return (
      &lt;div&gt;
        {isModalOpen ? (
          **&lt;div ref={ref}&gt;**
            👋 Hey, I&#39;m a modal. Click anywhere outside of me to close.
          &lt;/div&gt;
        ) : (
          &lt;button onClick={() =&gt; setModalOpen(true)}&gt;Open Modal&lt;/button&gt;
        )}
      &lt;/div&gt;
    );
  }</code></pre>
<pre><code class="language-jsx">  // Custom Hook이 Side Effect만 일으키고 return 값이 없으면
  // 굳이 리턴 값을 정의해주지 않아도 됩니다!
  function useOnClickOutside(ref, handler) {
    useEffect(
      () =&gt; {
        const listener = (event) =&gt; {
          if (!ref.current || ref.current.contains(event.target)) {
            return;
          }
          handler(event);
        };

        document.addEventListener(&quot;mousedown&quot;, listener);
        document.addEventListener(&quot;touchstart&quot;, listener);
        return () =&gt; {
          document.removeEventListener(&quot;mousedown&quot;, listener);
          document.removeEventListener(&quot;touchstart&quot;, listener);
        };
      },

      [ref, handler]
    );
  }</code></pre>
</li>
</ul>
<h3 id="4-usefetch">4) useFetch</h3>
<ul>
<li>특정 url에 대한 GET 요청에 대해 <code>loading</code>, <code>data</code>, <code>error</code> 세 가지 값을 리턴하도록 하는 Custom Hook</li>
<li><code>loading</code>: 데이터가 요청 중일 때는 true, 그 이외의 경우에는 false를 리턴</li>
<li><code>data</code>: 서버에서 가져온 값. 초기값은 undefined, 완료된 후에는 해당 값을 리턴</li>
<li><code>error</code>: 요청 도중 error가 일어났을 때 에러 객체를 리턴하고, 그 이외의 경우에는 false를 리턴</li>
</ul>
<pre><code class="language-javascript">//usefetch.js
import { useState, useEffect } from &quot;react&quot;;

//url을 인자로 받아온다.
const useFetch = url =&gt; {
  const [data, setData] = useState(null);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() =&gt; {
    setTimeout(() =&gt; {
      fetch(url)    
        .then(res =&gt; {
          if (!res.ok) {
            throw Error(&quot;could not fetch the data for that resource&quot;);
          }
          return res.json();
        })
        .then(_data =&gt; {
          setData(_data);
          setIsPending(false);
          setError(null);
        })
        .catch(err =&gt; {
          setIsPending(false);
          setError(err.message);
        });
    }, 1000);
  }, [url]);

  return { data, isLoading, error };
};

export default useFetch;
    export default function Component() {
      const url = &quot;http://jsonplaceholder.typicode.com/posts&quot;
      const { loading, data, error } = useFetch(url)

      return &lt;div&gt;{status}&lt;/div&gt;
    }</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Data 호출을 위한 fetch() 사용]]></title>
            <link>https://velog.io/@kykim_dev/Data-%ED%98%B8%EC%B6%9C%EC%9D%84-%EC%9C%84%ED%95%9C-fetch-%EC%82%AC%EC%9A%A9</link>
            <guid>https://velog.io/@kykim_dev/Data-%ED%98%B8%EC%B6%9C%EC%9D%84-%EC%9C%84%ED%95%9C-fetch-%EC%82%AC%EC%9A%A9</guid>
            <pubDate>Wed, 16 Feb 2022 07:01:35 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>백앤드로부터 데이터를 받아오려면 api를 호출하고 데이터를 응답 받는다. 이 때 자바스크립트 Web API fetch() 함수를 쓰거나 axios 라이브러리를 사용할 수 있다.
<strong>Web API는 클라이언트 측에서 사용할 수 있는 자바스크립트 내장함수</strong>로 현업에서는 axios를 많이 사용한다.</p>
</blockquote>
<h2 id="fetch-함수-기본">fetch() 함수 기본</h2>
<pre><code class="language-javascript">fetch(&#39;api 주소&#39;)
  .then(res =&gt; res.json())
  .then(res =&gt; {
    // data를 응답 받은 후의 로직
  });

fetch(&#39;api 주소&#39;)
  .then(function(res) {
    return res.json();
  })
  .then(function(res) {
    // data를 응답 받은 후의 로직
  });</code></pre>
<ul>
<li>위 코드에서 변수의 scope는 각 함수이므로 첫 번째 then와 두 번째 then 안에 있는 res 값은 서로 다르다.</li>
</ul>
<h2 id="fetch---method-get">fetch() - method &#39;get&#39;</h2>
<ul>
<li>fetch() 함수의 default method는 get이다. <pre><code class="language-javascript">//api 명세
설명: 유저 정보를 가져온다.
base url: https://api.google.com
endpoint: /user/3
method: get
응답형태:
  {
      &quot;success&quot;: boolean,
      &quot;user&quot;: {
          &quot;name&quot;: string,
          &quot;batch&quot;: number
      }
  }
</code></pre>
</li>
</ul>
<p>// 호출하기
fetch(&#39;<a href="https://api.google.com/user/3&#39;">https://api.google.com/user/3&#39;</a>)
  .then(res =&gt; res.json())
  .then(res =&gt; {
    if (res.success) {
        console.log(<code>${res.user.name}</code> 님 환영합니다);
    }
  });</p>
<pre><code>- api 주소를 상황에 맞게 유동적으로 바꿔야 할 경우 
```javascript
import React, { useEffect } from &#39;react&#39;;

function User(props) {
  useEffect(() =&gt; {
      const { userId } = props;
        fetch(`https://api.google.com/user/${userId}`)
          .then(res =&gt; res.json())
          .then(res =&gt; {
            if (res.success) {
                console.log(`${res.user.name}` 님 환영합니다);
            }
        });      
    }, []);
  ...
}</code></pre><h2 id="fetch---method-post">fetch() - method &#39;post&#39;</h2>
<ul>
<li>fetch() post인 경우에는 fetch() 함수에** method 정보를 인자로 넘겨주어야 한다.**<pre><code class="language-javascript">설명: 유저를 저장한다.
base url: https://api.google.com
endpoint: /user
method: post
요청 body:
  {
      &quot;name&quot;: string,
      &quot;batch&quot;: number
  }
</code></pre>
</li>
</ul>
<p>응답 body:
    {
        &quot;success&quot;: boolean
    }</p>
<p>fetch(&#39;<a href="https://api.google.com/user&#39;">https://api.google.com/user&#39;</a>, {
    method: &#39;post&#39;,
    body: JSON.stringify({
        name: &quot;kayoung&quot;,
        batch: 1
    })
  })
  .then(res =&gt; res.json())
  .then(res =&gt; {
    if (res.success) {
        alert(&quot;저장 완료&quot;);
    }
  })</p>
<pre><code>1. 두 번째 인자에 method 와 body를 보내준다.
2. method는 post
3. body는 JSON형태로 보내기 위해 JSON.stringfy() 함수에 객체를 인자로 전달하여 JSON형태로 변환한다.
- **fetch 함수는 post로 데이터를 보낼 때 JSON.stringfy를 해주어야 하는 반면, axios는 객체를 그대로 작성해도 되는 장점**이 있다. 이렇듯 axios는 소소한 편의성을 제공해주고, 요청과 응답에 대한 확장성 있는 기능을 만들 수도 있습니다.
## fetch() - method &#39;get&#39;에 parameter 전달
- path parameter가 아닌 query string으로 넘겨줘야 할 경우, api 주소 뒤에 붙여준다.
```javascript
설명: 유저 정보를 가져온다.
base url: https://api.google.com
endpoint: /user
method: get
query string: ?id=아이디
응답형태:
    {
        &quot;success&quot;: boolean,
        &quot;user&quot;: {
            &quot;name&quot;: string,
            &quot;batch&quot;: number
        }
    }

fetch(&#39;https://api.google.com/user?id=3&#39;)
  .then(res =&gt; res.json())
  .then(res =&gt; {
    if (res.success) {
        console.log(`${res.user.name}` 님 환영합니다);
    }
  });</code></pre><h2 id="resjson">res.json()</h2>
<pre><code class="language-javascript">fetch(&#39;https://api.google.com/user&#39;, {
    method: &#39;post&#39;,
    body: JSON.stringify({
        name: &quot;kayoung&quot;,
        batch: 1
    })
  })
  .then(res =&gt; {       // 첫 번째 then
    console.log(res);  

    return res.json();
  })
  .then(res =&gt; {       // 두 번째 then
    if (res.success) {
        alert(&quot;저장 완료&quot;);
    }
  })</code></pre>
<ul>
<li>첫 번째 then 함수에 전달된 인자(res)는 http 통신 요청과 응답에서 &#39;응답&#39;의 정보를 담고 있는 객체로, Response Object라고 한다.</li>
<li>응답으로 받은 JSON 데이터를 사용하기 위해서는 Response Object 의 json 함수를 호출하고, return 해야하는데, 두 번째 then 함수에서 응답 body의 데이터를 받을 수 있다.</li>
</ul>
<h2 id="에러-처리---첫-번째-then-함수에-로직-추가">에러 처리 - 첫 번째 then 함수에 로직 추가</h2>
<pre><code class="language-javascript">설명: 유저를 저장한다.
base url: https://api.google.com
endpoint: /user
method: post
요청 body:
    {
        &quot;name&quot;: string,
        &quot;batch&quot;: number
    }

응답 body:
    1. 제대로 저장했으면 status code를 200 전달. 응답 body는 없음
    2. 권한 오류가 생기면 status code를 403으로 전달하고. 응답 body는 아래와 같음
        {
            success: false,
            message: &quot;권한이 없습니다&quot;
        }

fetch(&#39;https://api.google.com/user&#39;, {
    method: &#39;post&#39;,
    body: JSON.stringify({
        name: &quot;kayoung&quot;,
        batch: 1
    })
  })
  .then(res =&gt; {
    if (res.status === 200) {
        alert(&quot;저장 완료&quot;);
    } else if (res.status === 403) {
        return res.json();
    }
  })
  .then(res =&gt; {
    console.log(&quot;에러 메시지 -&gt;&quot;, res.message);
  })</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[컴퓨팅 사고(Computational Thinking) ]]></title>
            <link>https://velog.io/@kykim_dev/%EC%BB%B4%ED%93%A8%ED%8C%85-%EC%82%AC%EA%B3%A0Computational-Thinking</link>
            <guid>https://velog.io/@kykim_dev/%EC%BB%B4%ED%93%A8%ED%8C%85-%EC%82%AC%EA%B3%A0Computational-Thinking</guid>
            <pubDate>Wed, 16 Feb 2022 07:00:08 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>컴퓨팅적 방법론과 모델을 통해 우리는 혼자서는 만들 수 없는 시스템을 설계하고 어려운 문제를 해결할 수 있을 거라는 자신감을 어을 수 있다. 
컴퓨팅적 사고를 한다는 것은 컴퓨터 공학의 기본 개념을 끌어와 문제를 해결하고, 시스템을 설계하고, 인간의 행동을 이해할 수 있다는 것이다. - Computational Tinking M.Jeannette</p>
</blockquote>
<h2 id="개발자란">개발자란?</h2>
<p> =&gt; <strong>세상의 문제를 코딩(기술)으로 해결하는 사람</strong>이라고 할 수 있다. 하지만 개발자가 하는 일의 범위는 아래와 같이 넓다.
<img src="https://images.velog.io/images/kykim_dev/post/95e1fa97-6e6f-4bfa-9710-65ebe1db2ac2/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-14%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%209.47.37.png" alt=""></p>
<h2 id="컴퓨팅-사고">컴퓨팅 사고</h2>
<ul>
<li><p>컴퓨터(사람이나 기계)가 효과적으로 일을 수행할 수 있도록 <strong>문제를 정의</strong>하고, <strong>그에 대한 답을 기술하는 것이 포함된 사고 과정</strong> 일체</p>
</li>
<li><p>&#39;인형 뽑기&#39;를 예로 들어보자. 인형 뽑기 위해 수행해야 하는 작업은 아래와 같다.
인형뽑기 기계를 본다 -&gt; 달려간다 -&gt; 천원을 넣는다 -&gt; 천원을 잃는다</p>
</li>
<li><p>하지만, 여기에서 우리가 생각하지 못한 변수, 좀더 세분화된 단계가 있다.
레버를 오른쪽으로 움직인다-&gt; 레버를 뒤쪽으로 움직인다 -&gt; 선택 버튼을 누른다 -&gt; 집게가 인형에 제대로 걸렸는가?-&gt; 뽑기 성공 or 뽑기 실패
<img src="https://images.velog.io/images/kykim_dev/post/c29e7eb5-d7e9-41ee-858f-6d12b1fff5a9/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-14%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%209.53.54.png" alt=""></p>
</li>
<li><p>이를 위와 같이 구조화할 수 있다. &#39;컴퓨터 사고&#39;는 즉, &#39;문제 해결 능력&#39;이라고 할 수 있다.</p>
<h3 id="컴퓨팅-사고를-위한-방법">컴퓨팅 사고를 위한 방법</h3>
<blockquote>
<p>&lt;클린코드&gt;
모든 컴퓨터 프로그래밍은 3가지 키워드로 이루어진다.</p>
</blockquote>
</li>
<li><p>순차 : 코드는 순차적으로 읽힌다. </p>
</li>
<li><p>분기 : 조건문 (if, while문..)</p>
</li>
<li><p>반복 : for문..</p>
</li>
<li><p>문제를 이해하고, 문제를 작게 분해해서, 어떻게 해결할지 생각해보자
=&gt; 컴퓨터가 알아듣게 생각하고 코드로 옮기는 것</p>
</li>
<li><p>문제 해결 능력을 키우려면? =&gt; 학습량, 검색, 생각을 먼저할 것</p>
</li>
</ul>
<h4 id="참고">참고</h4>
<ul>
<li><a href="https://www.cs.columbia.edu/~wing/ct-korean.pdf">https://www.cs.columbia.edu/~wing/ct-korean.pdf</a></li>
<li><a href="https://brunch.co.kr/@brunchjwshim/43#:~:text=%EC%BD%94%EB%94%A9%EC%9D%84%20%ED%95%98%EB%A0%A4%EB%A9%B4%20%EB%B0%98%EB%93%9C%EC%8B%9C%20%EC%95%8C%EC%95%84%EC%95%BC,%EB%AC%B8%EC%A0%9C%EB%93%A4%EA%B3%BC%20%EC%96%B4%EB%96%BB%EA%B2%8C%20%EC%97%B0%EA%B2%B0">https://brunch.co.kr/@brunchjwshim/43#:~:text=%EC%BD%94%EB%94%A9%EC%9D%84%20%ED%95%98%EB%A0%A4%EB%A9%B4%20%EB%B0%98%EB%93%9C%EC%8B%9C%20%EC%95%8C%EC%95%84%EC%95%BC,%EB%AC%B8%EC%A0%9C%EB%93%A4%EA%B3%BC%20%EC%96%B4%EB%96%BB%EA%B2%8C%20%EC%97%B0%EA%B2%B0</a></li>
<li>Flow Chart 만들기: <a href="https://app.diagrams.net/">https://app.diagrams.net/</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React #14] 페이지네이션과 Query Parameter]]></title>
            <link>https://velog.io/@kykim_dev/Pagination-query-parameter</link>
            <guid>https://velog.io/@kykim_dev/Pagination-query-parameter</guid>
            <pubDate>Sun, 13 Feb 2022 08:03:47 GMT</pubDate>
            <description><![CDATA[<h2 id="pagination">Pagination</h2>
<ul>
<li>백엔드에서 가지고 있는 데이터는 많고, 그 데이터를 한 화면에 전부 보여줄 수 없는 경우에 사용. 모든 데이터를 한번에 보여줄 수 없다면 일정 길이로 끊어서 전달해야 한다.
(ex: 게시판의 &quot;이전/다음 페이지&quot;를 끊어 보여주는 기능)</li>
<li>프론트엔드에서 <strong>현재의 위치(Offset)</strong>과 추가로 보여줄 <strong>컨텐츠의 수(Limit)</strong>를 백엔드에 전달, 백엔드에서는 그에 해당하는 데이터를 끊어 보내주는 방식으로 구현한다.</li>
</ul>
<h2 id="query-parameter">Query Parameter</h2>
<ul>
<li><p>이 과정에서 <strong>Query Parameter(혹은, Query String, 쿼리 스트링)를</strong> 사용한다. </p>
</li>
<li><p>쿼리 스트링이란 말 그대로 해당 엔드포인트에 대해 질의문(query)를 보내는 요청을 뜻한다. </p>
</li>
<li><p>예를 들어, <code>localhost:8000/products?limit=10&amp;offset=5</code> 라는 주소가 있다고 가정했을때, API 뒷 부분에 붙어있는 <code>?</code> 로 시작하는 텍스트가 쿼리스트링이다. </p>
</li>
<li><p><code>?limit=10&amp;offset=5</code> 의 경우, <strong>*&quot;limit이 10이면서 offset이 5일 경우의 product 페이지를 보여달라&quot;*</strong> 는 요청으로 해석된다.</p>
</li>
<li><p><code>?</code> : 쿼리스트링의 시작. url 에서 <code>?</code> 기호는 유일무이</p>
</li>
<li><p><strong>limit</strong> : 한 페이지에 보여줄 데이터 수</p>
</li>
<li><p><strong>offset</strong> : 데이터가 시작하는 위치(index)</p>
</li>
<li><p><code>parameter=value</code> 로 필요한 파라미터의 값을 적습니다.</p>
</li>
<li><p>파라미터가 여러개일 경우 <code>&amp;</code>를 붙여서 여러개의 파라미터를 넘길 수 있다.</p>
</li>
</ul>
<h2 id="useloacationsearch">useLoacation().search</h2>
<h3 id="동적-라우팅-vs-페이지네이션">동적 라우팅 vs 페이지네이션</h3>
<ul>
<li>동적 라우팅</li>
</ul>
<ol>
<li>리스트 페이지에서 카드를 클릭</li>
<li>url 이동. 이때, 카드의 고유한 id 값이 url 에 포함됩니다.</li>
<li>이동한 페이지에서, url 에 담겨있는 id 값을 <code>useParams</code> 훅을 이용하여 가져온다.</li>
<li>가져온 id 값을 이용하여 데이터를 요청한다.</li>
</ol>
<ul>
<li>페이지네이션</li>
</ul>
<ol>
<li>리스트 페이지에서 페이지 이동 버튼을 클릭</li>
<li>url 이동. 이때 url 에는 각 버튼에 <strong>해당하는 쿼리스트링이 포함</strong></li>
<li>이동한 페이지에서, url 에 담겨있는 쿼리스트링을 <strong><code>useLocation</code> 훅을 이용하여</strong> 가져온다.</li>
<li>가져온 쿼리스트링을 이용하여 데이터를 요청한다.</li>
</ol>
<ul>
<li>Path Parameter 에 대한 정보가 <code>useParams</code> 훅이 반환한 객체 안에 담겨있었듯이, 쿼리스트링에 대한 정보는 <code>useLocation</code> 훅이 반환한 객체의 search 프로퍼티 안에 담겨있다.
```jsx</li>
<li><em>// current url -&gt; localhost:3000/products?offset=10&amp;limit=10*</em></li>
</ul>
<p>function ProductList() {
    const location = useLocation();</p>
<pre><code>**console.log(location.search) // ?offset=10&amp;limit=10

return (
    ...
)**</code></pre><p>}</p>
<pre><code>* limit, offSet은 네이밍으로 바뀔 수 있다. (ex: pageNumber)
- 이를 통해 url 에서 쿼리스트링 정보를 받아와서, 해당 정보를 통해 데이터를 요청할 수 있다.

```jsx
fetch(`${API}${location.search}`)
    .then(res =&gt; res.json())
    .then(res =&gt; ...)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[React #13] 동적 라우팅과 Path Parameter, 그리고 관련 Hooks]]></title>
            <link>https://velog.io/@kykim_dev/React-Dynamic-Routing-Path-Parameter</link>
            <guid>https://velog.io/@kykim_dev/React-Dynamic-Routing-Path-Parameter</guid>
            <pubDate>Sun, 13 Feb 2022 08:01:44 GMT</pubDate>
            <description><![CDATA[<h2 id="라우팅을-위한-기본-코드-구조">라우팅을 위한 기본 코드 구조</h2>
<pre><code class="language-javascript">// index.html
&lt;!DOCTYPE html&gt;
  &lt;head&gt;
    &lt;title&gt;React App&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;div id=&quot;root&quot;&gt;&lt;/div&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre>
<pre><code class="language-javascript">// index.js
ReactDOM.render(&lt;Router /&gt;, document.getElementById(&quot;root&quot;));</code></pre>
<pre><code class="language-javascript">// Router.js
const Router = () =&gt; {
  return (
    &lt;BrowserRouter&gt;
            &lt;Nav /&gt;
      &lt;Routes&gt;
        &lt;Route path=&quot;/&quot; element={&lt;App /&gt;} /&gt;
        &lt;Route path=&quot;/users&quot; element={&lt;Users /&gt;} /&gt;
        &lt;Route path=&quot;/products&quot; element={&lt;Products /&gt;} /&gt;
        &lt;Route path=&quot;*&quot; element={&lt;NotFound /&gt;} /&gt;
      &lt;/Routes&gt;
            &lt;Footer /&gt;
    &lt;/BrowserRouter&gt;
  );
};
</code></pre>
<ul>
<li><code>index.html</code> : public/index.html에 위치하며 React 페이지 로드 시 가장 먼저 호출되는 영역</li>
<li><code>index.js</code> : React 앱을 렌더하고 index.html의 <code>div#root</code> 이하에 끼워넣는 역할</li>
<li><code>Router.js</code> : React 앱이 경로에 따라 어떤 컴포넌트를 보여줄지 결정하는 역할 (화면 바꿔 끼우기)</li>
</ul>
<h2 id="정적-라우팅static-routing이란">정적 라우팅(Static Routing)이란?</h2>
<pre><code class="language-javascript">&quot;/&quot;         =&gt; &lt;App /&gt;
&quot;/users&quot;    =&gt; &lt;Users /&gt;
&quot;/products&quot; =&gt; &lt;Products /&gt;</code></pre>
<h2 id="동적-라우팅dynamic-routing이란">동적 라우팅(Dynamic Routing)이란?</h2>
<p><img src="https://images.velog.io/images/kykim_dev/post/5b60cebf-ebd2-4e9b-8689-bddead1a5250/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-13%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%202.16.32.png" alt=""></p>
<ul>
<li>일반적인 웹 사이트는 전체 아이템이 보여지는 <code>리스트 페이지</code>와 해당 아이템의 디테일 한 정보로 구성된<code>상세 페이지</code>로 구성된다.</li>
<li>도메인 주소 끝에 해당 아이템의 id값이 추가되어 페이지를 이동하고, 이동한 페이지에서는 id값에 해당하는 데이터가 보여지는 것을 볼 수 있다.</li>
<li><blockquote>
<p>url 을 살펴보면 url 마지막에 특정 id 값이 들어가고(<code>/32692</code>, <code>/53424</code>), 해당 id 값에 따라 서로 다른 상세 페이지 정보가 화면에 그려지는 것을 볼 수 있다. </p>
</blockquote>
</li>
<li><em>id 값에 따라 무수히 많은 url 이 나타날 것이고, 각각의 모든 url 에 대해 미리 경로의 형태와 갯수를 결정할 수 없게 된다.*</em></li>
<li>즉, URL에 들어갈 id를 변수처럼 다뤄야 할 필요성이 생긴 것입니다.</li>
<li>이처럼 정적이지 않은, 동적일 수 있는 경로에 대하여 라우팅을 하는 것을 <strong>동적 라우팅(Dynamic Routing)</strong>이라고 한다.
=&gt; 라우트 경로에 특정 값을 넣어 해당하는 페이지로 이동할 수 있게 하는 것을 <strong>동적 라우팅</strong>이라고 한다.</li>
</ul>
<h3 id="동적인-라우팅을-처리하는-방법-path-parameter-query-parameter">동적인 라우팅을 처리하는 방법: Path Parameter, Query Parameter</h3>
<ul>
<li><p>동적인 경로를 처리할 수 있는 방법은 Path Parameter와 Query Parameter가 있다.
1) Path Parameter</p>
<pre><code class="language-javascript">// Bad
&quot;/users/1&quot; =&gt; &lt;Users id={1} /&gt;
&quot;/users/2&quot; =&gt; &lt;Users id={2} /&gt;
&quot;/users/3&quot; =&gt; &lt;Users id={3} /&gt;
&quot;/users/4&quot; =&gt; &lt;Users id={4} /&gt;
&quot;/users/5&quot; =&gt; &lt;Users id={5} /&gt;

// Good
&quot;/users/:id&quot; =&gt; &lt;Users /&gt; // useParams().id</code></pre>
<p>2) Query Parameter</p>
<pre><code class="language-javascript">// Bad
&quot;/search?keyword=리액트&quot;    : &lt;Search keyword=&quot;리액트&quot; /&gt;
&quot;/search?keyword=라우팅&quot;    : &lt;Search keyword=&quot;라우팅&quot; /&gt;
&quot;/search?keyword=쿼리스트링&quot; : &lt;Search keyword=&quot;쿼리스트링&quot; /&gt;
&quot;/search?keyword=SPA&quot;     : &lt;Search keyword=&quot;SPA&quot; /&gt;

// Good
&quot;/search?keyword=something&quot; : &lt;Search /&gt; // useLocation().search</code></pre>
<h2 id="path-parameter">Path Parameter</h2>
<pre><code>localhost:3000/product/2
localhost:3000/product/45
localhost:3000/product/125</code></pre></li>
<li><p><strong>라우트 경로 끝에 들어가는 각기 다른 id 값들을 저장하는 매개 변수</strong></p>
<pre><code class="language-javascript">&lt;Router&gt;
&lt;Switch&gt;
  &lt;Route exact path=&#39;/product/:id&#39; component ={producitDetail} /&gt;
&lt;/Switch&gt;
&lt;/Router&gt;</code></pre>
</li>
<li><p><code>:</code>는 Path Parameter가 올 것을 의미</p>
</li>
<li><p><code>id</code>는 해당 Path Prameter의 이름</p>
</li>
</ul>
<h3 id="동적-라우팅-흐름">동적 라우팅 흐름</h3>
<p><img src="https://images.velog.io/images/kykim_dev/post/38a9e50a-ab1b-45a5-9c87-087a0d686c07/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-13%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%202.30.27.png" alt=""></p>
<ol>
<li>카드를 클릭하면, onClick 이벤트시 발생하는 navigate 함수를 통해 <code>/product/1</code> 로 이동. URL 이 <code>/product/1</code> 로 변하면, Router 컴포넌트에 정의되어 있는<code>path=&#39;/product/:id&#39;</code> 에 따라, ProductDetail 컴포넌트 마운트</li>
<li>ProductDetail 컴포넌트에서는 백엔드에 id 가 <code>1</code> 인 아이템에 대한 정보 요청</li>
<li>응답으로 받은 정보를 <code>setData</code> 함수를 통해 <code>data</code> 라는 state에 저장하고, 이를 통해 상세 페이지 UI가 그려진다.</li>
</ol>
<h3 id="usenavigate-uselocation-useparams-hook">useNavigate, useLocation, useParams Hook</h3>
<ul>
<li><p>React Router 에서는 useNavigate, useLocation, useParams 라는 라우팅 관련한 훅을 제공한다.</p>
<h4 id="usenavigate-hook">useNavigate Hook</h4>
<pre><code class="language-javascript">function Product(props) {
const navigate = useNavigate();

const goToDeatil = () =&gt; {
  navigate(`/product/${props.id}`);
}

return (
      &lt;div className=&quot;productContainer&quot; onClick={goToDetail}&gt;
          ...
      &lt;/div&gt;
  )
}</code></pre>
</li>
<li><p><code>useNavigate</code> 훅을 실행하면 페이지를 이동시키는(url을 변경시키는) <code>함수</code>를 반환한다. 위 예제 코드에서 해당 함수를 <code>navigate</code> 라는 변수에 할당했다. 따라서, <code>navigate()</code>와 같은 방식으로 함수를 호출할 수 있습니다.</p>
</li>
<li><p><code>navigate()</code> 함수의 인자에 이동하고자 하는 url(ex. /products)을 전달하면, 해당 url로 화면을 이동한다.</p>
<pre><code class="language-javascript">navigate(&#39;/product/1&#39;); // &#39;/product/1&#39; 로 이동</code></pre>
</li>
<li><p>인자에 정수값을 넣어주면 브라우저 방문 기록에 남아있는 경로들을 앞 뒤로 탐색할 수 있다.</p>
<pre><code class="language-javascript">navigate(-1); // 뒤로 가기
navigate(-2); // 뒤로 2페이지 가기
navigate(1);  // 앞으로 가기</code></pre>
<h4 id="uselocation-hook">useLocation Hook</h4>
<pre><code class="language-javascript">function ProductDetail(props) {
  const location = useLocation();

  console.log(location);

return (
      ...
  )
}</code></pre>
</li>
<li><p><code>useLocation</code> 훅을 실행하면 경로 정보를 담고 있는 <code>객체</code> 를 반환한다. 위 코드에서 해당 객체를 <code>location</code> 이라는 변수에 할당해 주었다. location 변수를 콘솔로 출력해 보면 다음과 같은 로그가 출력 된다.</p>
<pre><code class="language-javascript">{
pathname: &#39;/product/1&#39;,  //현재 경로 값
search: &#39;&#39;, // 현재 경로의 query parameter 값
hash: &#39;&#39;, 
state: null, 
key: &#39;default&#39;
}</code></pre>
</li>
</ul>
<h4 id="useparams-hook">useParams Hook</h4>
<pre><code class="language-javascript">// 현재경로: /product/1

function ProductDetail(props) {
  const params = useParams();

  console.log(params);

  return(
    ...
   )
    }

//반환 
{
  id: 1 //id 프로퍼티 키 네임은 Router에서 :id로 표기해준 값
}</code></pre>
<ul>
<li>url에 담겨있는 path parameter에 대한 정보를 가져올 수 있다.</li>
<li>정보는 parameter 객체로 반환하고, route에서 지정한 key 이름으로 반환한다.<pre><code class="language-javascript">&lt;BrowserRouter&gt;
  &lt;Routes&gt;
      &lt;Route path=&#39;/product/:id&#39; element={&lt;ProductDetail /&gt;} /&gt;
  &lt;/Routes&gt;
&lt;/BrowserRouter&gt;</code></pre>
<h4 id="useparamsid">useParams().id</h4>
</li>
<li>URL에 담긴 id 값을 가져올 때 <code>useParam</code> 훅을 이용한다. Path Parameter 로 명시해둔 값은 useParams 훅이 리턴하는 객체에 담기기 때문이다.<pre><code class="language-javascript">// ProductDetail.js
// current url -&gt; localhost:3000/product/1
</code></pre>
</li>
</ul>
<p>function ProductDetail() {
    const params = useParams();</p>
<pre><code>console.log(params.id) // 1

return (
    ...
);    </code></pre><p>}</p>
<pre><code>- 따라서 `useEffect` 훅에서 해당 `id` 값을 통해 서버에 요청을 보내는 것을 통해 원하는 정보를 받아올 수 있다.
```javascript
useEffect(() =&gt; {
  fetch(`${API}/${params.id}`)
        .then(res =&gt; res.json())
        .then(res =&gt; setData(res));
},[]);</code></pre><h3 id="정리">정리</h3>
<p><img src="https://images.velog.io/images/kykim_dev/post/e0939084-a8c4-4272-979f-20032379607b/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202022-02-13%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.34.04.png" alt=""></p>
<ul>
<li><p>리스트 페이지의 개별 상품을 클릭 
→ <code>navigate(&quot;/product/1&quot;)</code> 로 상세 페이지로 이동한다.</p>
</li>
<li><p>상세 페이지로 이동하면 url은 <code>&quot;http://localhost:3000/product/1&quot;</code> 과 같이 변경되어 있다.</p>
</li>
<li><p>페이지에 필요한 데이터를 useEffect 에서 fetching 한다.</p>
</li>
<li><p>필요한 id는 URL에 존재하므로 <code>useParams().id</code> 에서 가져올 수 있다.</p>
</li>
<li><p>해당 id를 가지고 백엔드에서 만들어준 API를 호출한다.</p>
<pre><code>  ```jsx
  function ProductDetail() {
    const params = useParams();

    useEffect(() =&gt; {
      const productId = params.id;
          **fetch(`http://123.456.789:8000/products/${productId}`) // 1**
              .then(res =&gt; res.json())
              .then(res =&gt; setData(res));
    },[]);

    return (
      ...
    )
  }
  ```</code></pre></li>
<li><p>서버에서 가져온 데이터(<code>res</code>)를 컴포넌트의 <code>data</code> state 에 저장해준다.</p>
</li>
<li><p>state 에 담긴 데이터로 컴포넌트 UI 를 <code>render</code> 해준다.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React #11] Array, Object in State]]></title>
            <link>https://velog.io/@kykim_dev/React-11-Array-Object-in-State</link>
            <guid>https://velog.io/@kykim_dev/React-11-Array-Object-in-State</guid>
            <pubDate>Wed, 05 Jan 2022 14:21:21 GMT</pubDate>
            <description><![CDATA[<h2 id="array-in-state">Array in State</h2>
<pre><code class="language-javascript">import React, { useState } from &quot;react&quot;;

const options = [&quot;Bell Pepper&quot;, &quot;Sausage&quot;, &quot;Pepperoni&quot;, &quot;Pineapple&quot;];
// Static data = does not change. 정적 데이터는 리렌더링될 필요가 없기 떄문에 함수 밖에 쓴다. 

export default function PersonalPizza() {
  const [selected, setSelected] = useState([]);
  //selected array, dynamic data = it changes, based on user&#39;s action.

  const toggleTopping = ({target}) =&gt; {
    const clickedTopping = target.value;
    setSelected((prev) =&gt; {
     // check if clicked topping is already selected
      if (prev.includes(clickedTopping)) {
        // filter the clicked topping out of state
        return prev.filter(t =&gt; t !== clickedTopping);
      } else {
        // add the clicked topping to our state
        return [clickedTopping, ...prev];
        //When updating an array in state, we do not just add new data to the previous array. We replace the previous array with a brand new array. This means that any information that we want to save from the previous array needs to be explicitly copied over to our new array.
      }
    });
  };

  return (
    &lt;div&gt;
      {options.map(option =&gt; (
        &lt;button value={option} onClick={toggleTopping} key={option}&gt;
          {selected.includes(option) ? &quot;Remove &quot; : &quot;Add &quot;}
          {option}
        &lt;/button&gt;
      ))}
      &lt;p&gt;Order a {selected.join(&quot;, &quot;)} pizza&lt;/p&gt;
    &lt;/div&gt;
  );
}</code></pre>
<h3 id="예제---grocery-cart">예제 - Grocery Cart</h3>
<ul>
<li>item 클릭 시 cart에 담기고, 담긴 item 클릭시 삭제<pre><code class="language-javascript">import React, { useState } from &quot;react&quot;;
import ItemList from &quot;./ItemList&quot;;
import { produce, pantryItems } from &quot;./storeItems&quot;;
</code></pre>
</li>
</ul>
<p>export default function GroceryCart() {
  // declare and initialize state 
  const [cart, setCart] = useState([]);</p>
<p>  const addItem = (item) =&gt; {
   setCart((prev) =&gt; {
     return [item, ...prev]
   })
   };</p>
<p>  const removeItem = (targetIndex) =&gt; {
    setCart((prev) =&gt; {
      return prev.filter((item, index) =&gt; index !== targetIndex)
  })
  };</p>
<p>  return (
    <div>
      <h1>Grocery Cart</h1>
      <ul>
        {cart.map((item, index) =&gt; (
          &lt;li onClick={() =&gt; removeItem(index)} key={index}&gt;
            {item}
          </li>
        ))}
      </ul>
      <h2>Produce</h2>
      <ItemList items={produce} onItemClick={addItem} />
      <h2>Pantry Items</h2>
      <ItemList items={pantryItems} onItemClick={addItem} />
    </div>
  );
}</p>
<pre><code>
## Object in State
- 연관된 변수의 묶음은 객체로 처리한다. 
```javascript
export default function Login() {
  const [formState, setFormState] = useState({});

  const handleChange = ({ target }) =&gt; {
    const { name, value } = target;
    setFormState((prev) =&gt; ({
      ...prev, //{...oldObject, newKey: newValue}
      [name]: value
    }));
  };
  // 이전 값을 기준으로 state 업데이틀 위한 state setter callback function 사용
 // JavaScript that our curly brackets refer to a new object to be returned. We use ..., the spread operator, to fill in the corresponding fields from our previous stat

  return (
    &lt;form&gt;
      &lt;input
        value={formState.firstName}
        onChange={handleChange}
        name=&quot;firstName&quot;
        type=&quot;text&quot;
      /&gt;
      &lt;input
        value={formState.password}
        onChange={handleChange}
        type=&quot;password&quot;
        name=&quot;password&quot;
      /&gt;
 // reuse our event handler across multiple inputs by using the input tag’s name attribute to identify which input the change event came from
    &lt;/form&gt;
  );
}</code></pre><h3 id="예제---edit-profile">예제 - Edit Profile</h3>
<pre><code class="language-javascript">import React, { useState } from &quot;react&quot;;

export default function EditProfile() {
  const [profile, setProfile] = useState({});

  const handleChange = ({ target }) =&gt; {
    const {name, value} = target;
    setProfile((prev) =&gt; ({
      ...prev,
      [name]: value
    }));
  };

  const handleSubmit = (event) =&gt; {
    event.preventDefault();
    alert(JSON.stringify(profile, &#39;&#39;, 2));
  };

  return (
    &lt;form onSubmit={handleSubmit}&gt;
      &lt;input
        value={profile.firstName || &#39;&#39;}
        name=&quot;firstName&quot;
        type=&quot;text&quot;
        placeholder=&quot;First Name&quot;
        onChange={handleChange}
      /&gt;
      &lt;input
        value={profile.lastName || &#39;&#39;}
        type=&quot;text&quot;
        name=&quot;lastName&quot;
        placeholder=&quot;Last Name&quot;
        onChange={handleChange}
      /&gt;
      &lt;input
        value={profile.bday || &#39;&#39;}
        type=&quot;date&quot;
        name=&quot;bday&quot;
        onChange={handleChange}
      /&gt;
      &lt;input
        value={profile.password || &#39;&#39;}
        type=&quot;password&quot;
        name=&quot;password&quot;
        placeholder=&quot;Password&quot;
        onChange={handleChange}
      /&gt;
      &lt;button type=&quot;submit&quot;&gt;Submit&lt;/button&gt;
    &lt;/form&gt;

  );
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React #12] The Effect Hook (feat.useEffect())]]></title>
            <link>https://velog.io/@kykim_dev/React-12-The-Effect-Hook-feat.useEffect</link>
            <guid>https://velog.io/@kykim_dev/React-12-The-Effect-Hook-feat.useEffect</guid>
            <pubDate>Wed, 05 Jan 2022 13:07:23 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>웹 사이트를 만들다 보면 화면에 보일 수 있는 데이터를 서버에서 받아오기도 해야 하고, state가 바뀔 때마다 함수를 실행시키거나, 이벤트 리스너를 달았다가 해제하는 등의 동작이 필요할 수 있다. useEffect hook은 바로 이럴 때 유용하게 쓸 수 있다.</p>
</blockquote>
<ul>
<li>Hooks가 나오기 이전에는 함수 컴포넌트는 props 폼의 데이터를 받아오거나 렌더되는 JSX를 리턴하는 역할에 그치지 않았다. 그러나, State Hook은 함수 컴포넌트의 컴포넌트 state 폼 안에서 dynamic data를 처리할 수 있게 해주었다. </li>
<li>Effect Hook은 다음과 같이 자바스크립트 코드가 실행된 후 작동한다.<ul>
<li>백엔드 서버에서 데이터를 fetching 할 때</li>
<li>데이터의 스트림을 subscribing 할 때</li>
<li>타이머, 인터벌을 매니징할 때</li>
<li>DOM에서 변화하는 요소를 읽어낼 때</li>
</ul>
</li>
<li>위와 같은 요소에서 각각의 렌더링 후 일어나는 이유는 &#39;side effects(부수 효과)&#39; 때문이다.</li>
<li>Effect Hook이 사용되는 세가지 key moments</li>
</ul>
<ol>
<li>컴포넌트가 DOM에 처음으로 추가/마운트 되서 렌더링 될 때</li>
<li>state/props가 바뀌어서 컴포넌트가 재렌더링될 때</li>
<li>컴포넌트가 DOM에서 지워지거나/언마운트될 때</li>
</ol>
<pre><code class="language-javascript">//Class.js
import React, {Component} from &#39;react&#39;;

export default class PageTitle extends Component {
  constructor(props) {
    super(props);
    this.state = {
      name: &#39;&#39;
    };
  }

  componentDidMount() {
    document.title = this.state.name;
  }

  componentDidUpdate() {
    document.title == `Hi, ${this.state.name}`;
  }

  render() {
    return (
      &lt;div&gt;
        &lt;p&gt;Use the input field below to rename this page!&lt;/p&gt;
        &lt;input 
          onChange={({target}) =&gt; this.setState({ name: target.value })} 
          value={this.state.name} 
          type=&#39;text&#39; /&gt;
      &lt;/div&gt;
    );
  }
}

//Function.js
import React, {Component} from &#39;react&#39;;

export default class PageTitle extends Component {
  constructor(props) {
    super(props);
    this.state = {
      name: &#39;&#39;
    };
  }

  componentDidMount() {
    document.title = this.state.name;
  }

  componentDidUpdate() {
    document.title == `Hi, ${this.state.name}`;
  }

  render() {
    return (
      &lt;div&gt;
        &lt;p&gt;Use the input field below to rename this page!&lt;/p&gt;
        &lt;input 
          onChange={({target}) =&gt; this.setState({ name: target.value })} 
          value={this.state.name} 
          type=&#39;text&#39; /&gt;
      &lt;/div&gt;
    );
  }
}
</code></pre>
<ul>
<li>class형에서 <code>componentDidMount()``componentDidUpdate()</code>로 구분해서 세팅된 것이 함수형에서는<code>useEffect()</code>로 한번에 처리할 수 있어 효율적이다.</li>
</ul>
<h2 id="the-effect-hook---useeffect">The Effect Hook - useEffect()</h2>
<ul>
<li>useEffect를 react 라이브러리에서 import 한다.
<code>import React, {useEffect} from &#39;react&#39;;</code></li>
<li>Effect Hook은 우리가 안에 있는 다른 함수를 호출하기 위해 사용하기 때문에 useEffect() 함수를 호출할 때는 아무것도 리턴하지 않는다.</li>
<li>useEffect() 함수에 첫번째 인자는 우리가 리액트 컴포넌트가 리턴될 때 부르고 싶은 콜백 함수다.<pre><code class="language-javascript">import React, { useState, useEffect } from &#39;react&#39;;
</code></pre>
</li>
</ul>
<p>function PageTitle() {
  const [name, setName] = useState(&#39;&#39;);</p>
<p>  useEffect(() =&gt; {
    document.title = <code>Hi, ${name}</code>;
  });</p>
<p>  return (
    <div>
      <p>Use the input field below to rename this page!</p>
      &lt;input onChange={({target}) =&gt; setName(target.value)} value={name} type=&#39;text&#39; /&gt;
    </div>
  );
}</p>
<pre><code>- 위의 예시에서 effect는 `() =&gt; { document.title = name; }`가 된다.

effect안에서 현재 state는 어떻게 사용될까?
- effect가 컴포넌트가 렌더링 된 후 에 일어난다고 하더라도 함수 컴포넌트 스코프에 접근이 가능하다.
- 리액트가 컴포넌트를 렌더링하면 DOM이 업데이트 되고, 그 후 effect가 일어난다. =&gt; 처음부터 끝까지 모든 렌더링에서 발생한다.

### Clean up Effects
- 이벤트 리스너를 여러개 붙일 때는 Effect를 제거해주는 것이 필요하다.
이벤트를 제거해줌으로써 불필요한 메모리가 낭비되는 것을 막을 수 있다.
- clean up fuction을 설정해주지 않으면 새로운 이벤트 리스너가 컴포넌트가 재렌더링 될 때마다 DOM의 document object에 붙는다. 
=&gt; 버그를 유발하거나 퍼포먼스가 감소할 수 있다.
- effect는 모든 렌더링 후에 발생하기 때문에 cleanup 함수 또한 각각의 재렌더링 때마다, 언마운팅 전에 일어난다.
- 모든 effect가 함수를 리턴하기 때문에 useEffect() Hook은 항상 cleanup function으로 인식한다. cleanup function은 선택사항이지만, 메모리 손실이 큰 코드의 경우 써주는 것이 좋다.
```javascript
useEffect(()=&gt;{
  document.addEventListener(&#39;keydown&#39;, handleKeyPress);
  return () =&gt; {
    document.removeEventListener(&#39;keydown&#39;, handleKeyPress);
  };
})</code></pre><h3 id="control-when-effects-are-called---dependency-array">Control When Effects are Called - dependency array([])</h3>
<ul>
<li>useEffect()에서 첫번째 인자 함수가 호출될 때 컴포넌트가 렌더링 될 때마다 실행된다. </li>
<li>effect가 첫번째 렌더링 이후에만 호출되기를 원한다면 두번째 인자에 빈 배열을 넣어준다. 이를 dependency array(의존성 배열)이라고 부른다. </li>
<li>빈 의존성 배열은 1) 컴포넌트가 처음 마운트될 때 effect를 불러오고, 2) 컴포넌트가 언마운트 될 떄 cleanup function이 effect에 의해 리턴될 때 호출한다.<pre><code class="language-javascript">useEffect(() =&gt; {
alert(&quot;component rendered for the first time&quot;);
return () =&gt; {
  alert(&quot;component is being removed from the DOM&quot;);
};
}, []); </code></pre>
</li>
<li>위의 예시에서 빈 배열이 없다면, alerts은 컴포넌트가 렌더링 될 때마다 실행된다. <h3 id="fetch-data">Fetch Data</h3>
</li>
<li>컴포넌트에서 데이터를 받을 때도 빈 배열을 넣어주면, 첫 렌더링 이후 데이터를 fetch 할 수 있다. </li>
<li>서버로부터 응답을 불러올 때, state Hook에서 state setter를 설정해 로컬 컴포넌트에 있는 서버의 response의 데이터를 저장할 수 있다. 
=&gt; State Hook과 Effect Hook을 함께 사용하면 매 렌더링마다 불필요한 새로운 데이터를 fetching해 오는 것을 막을 수 있다. <pre><code class="language-javascript">useEffect(() =&gt; {
document.title = `You clicked ${count} times`;
}, [count]); 
// Only re-run the effect if the value stored by count changes</code></pre>
</li>
<li>빈 배열의 의존성 배열은 의존할 요소가 없기 때문에 다시 실행될 필요가 없다. 즉, effect가 바뀌지 않고 한번만 호출되면 된다는 것이다.</li>
<li>빈 배열이 아닌 의존성 배열은 의존성 배열의 변수 값이 변화하면 재실행되고, 그렇지 않으면 실행되지 않는다. 즉 변화가 일어나면 effect는 다시 호출된다. <h3 id="rules-of-hooks">Rules of Hooks</h3>
Hooks을 쓸 때는 두 가지의 규칙이 있다.
1) top level에 쓴다.<ul>
<li>React에서 Virtual DOM을 빌드할 떄, 라이브러리는 유저 인터렉션에 따라 우리가 정의한 컴포넌트를 계속해서 정의한다. React는 정의된 컴포넌트의 함수에서 Hook으로 불러온 데이터를 계속 추적한다. 이러한 이유로, Hook을 가장 위에서 호출해야 한다.</li>
<li>loops, conditions, nested functions 안에 쓸 수 없다.<pre><code class="language-javascript">if (userName !== &#39;&#39;) {
useEffect(() =&gt; {
localStorage.setItem(&#39;savedUserName&#39;, userName);
});
}
</code></pre>
</li>
</ul>
</li>
</ul>
<p>useEffect(() =&gt; {
  if (userName !== &#39;&#39;) {
    localStorage.setItem(&#39;savedUserName&#39;, userName);
  }
});</p>
<p>```
2) React 함수형으로 쓴다. </p>
<ul>
<li>Hooks는 클래스형에서 쓸 수 없고, 함수형 컴포넌트에서 쓸 수 있다.</li>
<li>다른 곳에서 쓸 수 있는 유일한 경우는 custom hooks로 쓸 때다. </li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React #10] React Hooks로 함수형, state 효율적으로 쓰기(feat.useState())]]></title>
            <link>https://velog.io/@kykim_dev/React-10-React-Hooks%EB%A1%9C-%ED%95%A8%EC%88%98%ED%98%95-state-%ED%9A%A8%EC%9C%A8%EC%A0%81%EC%9C%BC%EB%A1%9C-%EC%93%B0%EA%B8%B0feat.useState</link>
            <guid>https://velog.io/@kykim_dev/React-10-React-Hooks%EB%A1%9C-%ED%95%A8%EC%88%98%ED%98%95-state-%ED%9A%A8%EC%9C%A8%EC%A0%81%EC%9C%BC%EB%A1%9C-%EC%93%B0%EA%B8%B0feat.useState</guid>
            <pubDate>Mon, 03 Jan 2022 11:57:38 GMT</pubDate>
            <description><![CDATA[<h2 id="hooks-개발-배경">Hooks 개발 배경</h2>
<p>React의 Class형 컴포넌트로 모든 프로그래밍을 하던 시절, 개발자들은 React팀에 다음과 같은 feedback을 던진다.</p>
<ul>
<li>&quot;Class형은 컴포넌트 간 재사용하기 어려워요!&quot;</li>
<li>&quot;Class형은 복잡하고 테스트하는데 시간을 많이 잡아먹어요!&quot;</li>
<li>&quot;Class형은 많은 개발자들이 혼란을 겪고, 버그도 엄청 많이 나와요!&quot;
=&gt; 이러한 피드백을 반영해 React팀은 React 16.8+ 버전에서 &#39;Hooks&#39;을 선보였다.
Hooks의 등장으로 간단한 <strong>함수형 컴포넌트만</strong>으로 클래스형 컴포넌트로만 할 수 있었던 온갖 아름다운 동작들을 구현할 수 있게 되었다!</li>
</ul>
<h2 id="hooks">Hooks</h2>
<blockquote>
<p>React Hooks는 함수에서 컴포넌트의 내부 states와 post-rendering side effetcts를 관리할 수 있게 해주었다.</p>
</blockquote>
<ul>
<li>Hooks는 클래스 안에서는 작동하지 않는다. 함수형 컴포넌트와 hooks는 클래스 컴포넌트의 대체제가 아니라, &quot;선택사항&quot;이다. </li>
<li>React는 내장 hooks 기능을 제공한다. useState(), useEffect(), useContext(), useReducer(), useRef()...
<a href="https://reactjs.org/docs/hooks-reference.html">full list</a></li>
</ul>
<h2 id="the-state-hook---usestate">The State Hook - useState()</h2>
<ul>
<li><strong>함수형 컴포넌트의 state를 업데이트할 때 쓰는 hook</strong>으로 react component를 구성하기 위해 가장 흔하게 사용하는 hook이다.</li>
<li>useState를 react 라이브러리에서 import 한다.
<code>import React, {useState} from &#39;react&#39;;</code></li>
<li>useState()는 react 내부의 JavaScript 함수 정의로, 두 가지의 값을 갖는 배열로 리턴된다.
<code>const [current state, state setter] = useState();</code><ul>
<li>current state : this state의 현재 값</li>
<li>state setter: this state 값이 업데이트 되어서 사용할 수 있는 함수<pre><code class="language-javascript">import React, {useState} from &quot;react&quot;;
</code></pre>
</li>
</ul>
</li>
</ul>
<p>function Toggle() {
  const [toggle, setToggle] = useState();</p>
<p>  return (
    <div>
      <p>The toggle is {toggle}</p>
      &lt;button onClick={() =&gt; setToggle(&quot;on&quot;)}&gt;On</button>
      &lt;button onClick={() =&gt; setToggle(&quot;off&quot;)}&gt;Off</button>
    </div>
   );
}</p>
<pre><code>- `setToggle()`은 `onClick` 이벤트 리스너로 호출된다.
- `toggle`값과 컴포넌트를 새로운 값으로 재렌더링하기 위해서는 `setToggle()` 함수를 다음 스테이트 값과 함께 argument처럼 호출해주기만 하면 된다. 
(class 인스턴스로 함수를 바인딩하고, constructor를 쓰고, this 키워드를 쓰고.. 따위는 신경쓰지 않아도 된다! 그저 State Hook으로 간단하게 스테이트를 업데이트 할 수 있게 된 것!🤭)
&gt; React에 state setter 신호를 호출한다는 것은 컴포넌트의 재렌더링(re-render)과 이에 따라 컴포넌트 정의에 따른 전체 함수가 다시 호출되어야한다는 것을 의미한다. 
`useState`를 쓰면 React 컴포넌트에서 다른 부분으로 넘어갈 때 **현재 스테이트의 값을 계속 추적할 수 있다.**
### State 초기화
- State Hook으로 stiring을 포함한 원시 데이터 타입, 배열, 객체까지 관리할 수 있다.
- state를 초기화하기 위해서는 useState() 함수 호출에 초기값을 argument로 전달해준다.  
```const [isLoading, setIsLoading] = useState(true);```
1) 첫번째 렌더링에서 초기값이 사용된다. 
2) 두번째 state setter(setIsLoading)가 호출되면 React는 초기값을 무시하고, 새로운 값을 사용한다. 
3) 컴포넌트가 재렌더링되면, React는 이전 렌더링에 사용했던 값을 계속 사용한다.
&gt; 초기값을 설정하지 않으면 undefined가 뜬다. 동작하는데는 문제가 없으나 코드를 명확하게 하기 위해서는 명시해주는 것이 좋다. 만약 첫 렌더링에서 보여줄 값이 없으면 null처리를 해주는 것이 좋다.
### JSX 외부에서 State setter 사용하기
- 이벤트 핸들러를 붙여줄 때 해당 로직을 분리해주면 가독성 높고 효율적인 코드를 작성할 수 있다.
```javascript
const handleChange = (event) =&gt; {
  const newEmail = event.target.value;
  setEmail(newEmail);
}

=&gt; const handleChange = (event) =&gt; setEmail(event.target.value);

//Object destructing
=&gt; const handleChange = ({target}) =&gt; setEmail(target.value);</code></pre><h3 id="이전-state로부터-setting-하기">이전 state로부터 setting 하기</h3>
<ul>
<li>다음에 나올 state 값이 현재 state에서 계산이 되어야 하는 경우가 있다.
이때, 가장 좋은 방법은 콜백 함수를 지정해 두는 것이다. <pre><code class="language-javascript">import React, { useState } from &#39;react&#39;;
</code></pre>
</li>
</ul>
<p>export default function QuizNavBar({ questions }) {
  const [questionIndex, setQuestionIndex] = useState(0);</p>
<p>  // define event handlers 
  const goBack = () =&gt; setQuestionIndex((prevQuestionIndex) =&gt; prevQuestionIndex - 1);
  const goToNext = () =&gt; setQuestionIndex((prevQuestionIndex) =&gt; prevQuestionIndex + 1);
  // determine if on the first question or not 
  const onFirstQuestion = questionIndex === 0;
  const onLastQuestion = questionIndex === questions.length - 1;</p>
<p>  return (
    <nav>
      <span>Question #{questionIndex + 1}</span>
      <div>
        <button onClick={goBack} disabled={onFirstQuestion}>
          Go Back
        </button>
        <button onClick={goToNext} disabled={onLastQuestion}>
          Next Question
        </button>
      </div>
    </nav>
  );
}
```</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[WEB #14] RESTful API (feat. path/query parameter) ]]></title>
            <link>https://velog.io/@kykim_dev/WEB-14-RESTful-API-feat.-pathquery-parameter</link>
            <guid>https://velog.io/@kykim_dev/WEB-14-RESTful-API-feat.-pathquery-parameter</guid>
            <pubDate>Tue, 02 Nov 2021 02:59:26 GMT</pubDate>
            <description><![CDATA[<h1 id="rest-api">REST API</h1>
<ul>
<li><strong>RE</strong>presentational <strong>S</strong>tate <strong>T</strong>ransfer의 약자로 웹상의 여러 리소스(이미지, 동영상, 데이터)에 고유한 URI를 부여해 자원에 대한 주소를 지정(HTTP Method)하는 방법론, 규칙을 의미한다.</li>
<li>API 시스템을 구현하기 위한 아키텍쳐 중 가장 널리 사용되는 형식 (Graphql, GRPC, REST..)
=&gt; <strong>RESTful API</strong>는 REST 특징을 지키면서 API를 제공한다는 의미이다.
결국, HTTP 요청을 보낼 때 어떤 <strong>URI</strong>, 어떤 <strong>HTTP method</strong>를 사용할 것인가에 대한 약속
<img src="https://images.velog.io/images/kykim_dev/post/25bdccf1-4a38-44da-8f2f-613c596be1c4/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-11-02%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%2010.27.26.png" alt="">
장점: <strong>self-descriptiveness</strong>, 지정된 형식이 있어 요청 자체만으로 API 목적이 쉽게 이해된다.
단점: 표준규약이 없어 안티패턴(*실제 많이 사용되는 패턴이지만 비효율적이거나 비생산적인 패턴)으로 작성되는 경우가 흔하다.<h3 id="기본-배경-지식">기본 배경 지식</h3>
</li>
<li><em>URI(Uniform Resource Identifier)*</em></li>
<li>해당 사이트의 특정 자원의 위치를 구조와 함께 나타내는 유일한 주소 </li>
<li><code>https://finace.naver.com/login</code>에서 &#39;https:// ~ com&#39;은 URL, <code>/login</code>은 URI다.</li>
<li><em>HTTP Method*</em></li>
<li>HTTP request가 의도하는 action(GET, POST, DELETE, PUT)을 정의한 것</li>
<li><em>Payload*</em></li>
<li>HTTP request에서 server로 보내는 데이터 (body)<h1 id="restful-api">RESTful API</h1>
</li>
<li>웹 데이터 전송 방식 중 하나로 API의 엔드포인트 구조를 구현하는 방식이다.</li>
<li>API에서 전송하는 자원(resource)을 URI로, 각 자원의 행위를 HTTP method로 정의한다.</li>
<li>엔드포인트는 리소스를 표현하는 고유한 URI를 갖고, 해당 리소스의 행위를 표현하는 HTTP method를 처리할 수 있게 한다. 
<img src="https://images.velog.io/images/kykim_dev/post/9872dca5-73c4-4a39-ba2d-f4ecd94bdab0/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-11-02%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%2010.35.40.png" alt=""></li>
<li>Method와 Resource 정보를 protocol과 host에 URI로 담아 request하면, 엔드포인트에 따라 해당 정보를 Reponse로 받아온다. 
⚠️ 사용자가 주소창에서 보는 실제 URL이 아닌 클라이언트의 프론트엔드 개발자가 백엔드개발자에게 기능을 요청할 때 보내는 주소! 페이지 단위가 아닌 기능 단위로 구성됨을 유의!</li>
</ul>
<h3 id="설계-규칙">설계 규칙</h3>
<ul>
<li>URI 정보를 명확하게 표현한다. resource는 명사를 사용한다.
GET/user/1 -&gt; GET/users/1 </li>
<li>resource에 대한 행위를 HTTP Method(GET, POST, PUT, DELETE)로 표현한다. 
URI에 method를 포함하지 않는다. GET delete/user/1 -&gt; DELETE/users/1
URI에 동사를 포함하지 않는다. GET/user/show/1 -&gt; GET/users/1, POST insert/user/2 -&gt; POST/users/2</li>
<li>resource 간 관계가 있는 경우 &#39;/&#39;로 표현한다.  <code>/리소스/고유ID/관계 리소스</code> GET /users/{user_id}/profile</li>
<li>파일의 경우 payload의 포맷을 나타내기 위한 파일 확장자를 URI에 포함시키지 않는다. GET user/1/profile-photo.jpg(x) -&gt; GET user/1/profile-photo (이때, payload의 포맷은 headers에 accept 사용)</li>
<li>URI는 / 구분자를 사용해 자원의 계층 관계를 나타낸다. 따라서 마지막 문자로 /를 포함하지 않는다.</li>
<li>URI가 길어지는 경우 -을 사용해 가독성을 높인다. &#39;_&#39;, &#39;대문자&#39;는 사용하지 않는다. </li>
</ul>
<hr>
<h1 id="rest-api-통신">REST API 통신</h1>
<ul>
<li>상황에 따라 Query parameters (GET parameters), Path parameters로 통신할 수 있다. <h2 id="path-parameters">Path parameters</h2>
</li>
<li>End-point에 경로를 지정하여 요청하는 방식 </li>
<li>GET Method
<img src="https://images.velog.io/images/kykim_dev/post/5c942a58-28c9-42ce-b972-3588626c122b/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-11-02%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%2011.18.08.png" alt=""></li>
<li>POST, PATCH Method
<img src="https://images.velog.io/images/kykim_dev/post/dd500c04-d450-4964-821e-b54f4acb1727/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-11-02%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%2011.18.52.png" alt=""></li>
<li>DELETE Method
<img src="https://images.velog.io/images/kykim_dev/post/e17f794b-c105-4178-b510-e62f1ba7d9fb/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-11-02%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%2011.19.39.png" alt=""><h2 id="query-parameter">Query parameter</h2>
</li>
<li>End-point에 query문으로 요청하는 방식</li>
<li>데이터를 조건으로 거르거나(filtering), 특정 방식으로 정렬하거나(ordering), 검색(searching) 등 조건에 맞추어 정제된 결과물을 얻을 수 있다.<br><img src="https://images.velog.io/images/kykim_dev/post/3ab6376e-9e05-4d34-bc90-e992db491ee6/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-11-02%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%2011.20.42.png" alt=""><img src="https://images.velog.io/images/kykim_dev/post/5e5944db-7812-47d9-8272-87ee07b4f449/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-11-02%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%2011.21.17.png" alt=""></li>
<li>신상품순(-id), 가격순 등 필터링에 사용할 수 있다. 
<img src="https://images.velog.io/images/kykim_dev/post/32b662d5-877d-4ec5-b97b-e7caeca400df/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-11-02%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%2011.22.40.png" alt="">
<img src="https://images.velog.io/images/kykim_dev/post/ded01f9b-1cb5-47ab-8954-a0cec041bc66/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-11-02%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%2011.24.00.png" alt=""><h3 id="path-parameters-vs-query-parameter">Path parameters vs Query parameter</h3>
</li>
<li>Path parameter에 비해 Query parameter는 
조금 더 정제된 정보 Filtering, Sorting, Searching, Pagination를 가져올 때 쓴다.</li>
<li>검색 데이터가 없을 경우, Path parameter는 오류를 출력하고, Query parameter는 빈 페이지를 보여준다.  </li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[WEB #13] 인증과 인가(개인정보 관리)]]></title>
            <link>https://velog.io/@kykim_dev/WEB-13-%EC%9D%B8%EC%A6%9D%EA%B3%BC-%EC%9D%B8%EA%B0%80</link>
            <guid>https://velog.io/@kykim_dev/WEB-13-%EC%9D%B8%EC%A6%9D%EA%B3%BC-%EC%9D%B8%EA%B0%80</guid>
            <pubDate>Mon, 01 Nov 2021 10:04:35 GMT</pubDate>
            <description><![CDATA[<h1 id="인증authentication">인증(Authentication)</h1>
<ul>
<li><strong>유저의 identification을 확인하는 절차</strong> (아이디, 비밀번호 확인)</li>
<li>우리 서비스를 누가, 어떻게 사용하는지 추적하기 위해 사용한다.</li>
<li>인증에는 아이디, 이메일 주소, 비밀번호* 등이 필요하다. <h3 id="비밀번호-관리">비밀번호 관리</h3>
</li>
<li>비밀번호는 개인정보보호법에 따라 &#39;암호화&#39;해서 관리해주어야 한다. (법규상 강제)</li>
<li>비밀번호는 Database에 저장 시 개인정보를 해싱(난독화)하여 복원할 수 없도록 한다.</li>
<li>통신 시 개인 정보를 주고받을 때 SSL을 적용하여 암호화한다. (HTTPS 프로토콜)<h3 id="암호화-방법">암호화 방법</h3>
<h4 id="단방향-해쉬-함수one-way-hash-fuction">단방향 해쉬 함수(one-way hash fuction)</h4>
</li>
<li>해시(hash)함수는 자료구조에서 빠른 자료와 검색, 데이터의 위변조 체크를 위해 쓰이며, 복원이 불가능해 암호학적 용도로 사용한다. (예: MD5, SHA-1, SHA-256)</li>
<li>단방향 해시 함수는 원본 메시지를 변환하여 암호화된 메시지인 다이제스트(digest)를 생성한다. 원본 메시지를 알면 암호화된 메시지를 구하기는 쉽지만 암호화된 메시지로는 원본 메시지를 구할 수 없어서 단방향성(one-way) 이라고 한다.</li>
<li>예를 들어, &quot;test password&quot;를 hash256이라는 해쉬 함수를 사용하면 <code>0b47c69b1033498d5f33f5f7d97bb6a3126134751629f4d0185c115db44c094e</code> 값이 나온다.</li>
<li>만일 &quot;test password2&quot;를 hash256 해쉬 함수를 사용하면 <code>d34b32af5c7bc7f54153e2fdddf251550e7011e846b465e64207e8ccda4c1aeb</code> 값이 나온다. 실제 비밀번호는 비슷하지만 해쉬 함수 값은 완전히 틀린것을 볼 수 있다. 이러한 효과를 avalance라고 하는데 비밀번호 해쉬 값을 해킹을 어렵게 만드는 하나의 요소이다.</li>
<li>결과만 보고 식별이 불가능하기 때문에 완벽해보이지만, 같은 알고리즘으로 해싱하면 같은 결과가 도출되기 때문에 위험할 수 있다. (Rainbow Table attack: 가능한 경우의 수를 모두 해시값으로 만들어 판매하는 서비스)
=&gt; salting, Key Streching 개발됨<h4 id="salting--keystretching-소금치고-늘리기">Salting &amp; KeyStretching (소금치고 늘리기!)</h4>
<img src="https://images.velog.io/images/kykim_dev/post/c2e1a4b4-1d74-4d48-8f7a-0b37d1906178/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-11-01%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.00.01.png" alt=""></li>
<li>솔팅(Salting)은 실제 비밀번호 이외에 추가적으로 랜덤 데이터를 더해서 해시값을 계산하는 방법이다. </li>
<li>입력한 비밀번호와 임의로 생성한 문자열을 합쳐서 해싱, 이 해싱값 결과를 저장한다.</li>
<li>비교를 위해 해시값과 솔트값을 같이 저장해야 한다.</li>
<li>키 스트레칭(Key Stretching)은 단방향 해쉬값을 계산한 후 그 해시값을 또 해쉬하는 반복 작업으로, 해커가 패스워드 무작위 대입을 통해 해시값을 계산하는데 필요한 시간을 대폭 늘려 원본 값을 유추하기 어렵게 만든다.<h4 id="bcrypt">bcrypt</h4>
</li>
<li>Salting &amp; Key Streching 개념들을 적용해주는 대표적 라이브러리</li>
<li>처음부터 비밀번호를 단방향 암호화하기 위해 만들어진 해쉬 함수로 hash 결과값에 솔트값, 해시값 및 반복횟수를 같이 보관하기 때문에 DB 설계가 편리하다는 장점이 있다.</li>
</ul>
<h3 id="로그인-절차">로그인 절차</h3>
<ol>
<li>유저 아이디와 비밀번호 생성<pre><code class="language-javascript">POST /auth HTTP/1.1
Host: localhost:5000
Content-Type: application/json
</code></pre>
</li>
</ol>
<p>{
    &quot;username&quot;: &quot;joe&quot;,
    &quot;password&quot;: &quot;pass&quot;
}</p>
<pre><code>2. 유저 비밀번호 암호화 -&gt; DB에 저장.
3. 유저 로그인 -&gt; 아이디와 비밀번호 입력
4. 유저가 입력한 비밀번호 암호화 -&gt; 암호화되서 DB에 저정된 유저 비밀번호와 비교
5. 일치하면 로그인 성공
6. 로그인에 성공하면 `access token`을 클라이언트에게 전달
```javascript
// 유저 로그인
POST /auth HTTP/1.1
Host: localhost:5000
Content-Type: application/json

{
    &quot;username&quot;: &quot;joe&quot;,
    &quot;password&quot;: &quot;pass&quot;
}</code></pre><pre><code class="language-javascript">// access token
HTTP/1.1 200 OK
Content-Type: application/json

{
    &quot;access_token&quot;: &quot;eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZGVudGl0eSI6MSwiaWF0IjoxNDQ0OTE3NjQwLCJuYmYiOjE0NDQ5MTc2NDAsImV4cCI6MTQ0NDkxNzk0MH0.KPmI6WSjRjlpzecPvs3q_T3cJQvAgJvaQAPtk1abC_E&quot;
}</code></pre>
<ol start="7">
<li>서버에서 access token을 복호화해 유저 정보를 얻는다. 유저는 로그인 성공후 다음부터는 <code>access token</code>을 첨부해서 request를 서버에 전송함으로서 매번 로그인 해도 되지 않도록 한다.
(<a href="https://velog.io/@kykim_dev/Web-HTTP">HTTP를 알고 주소 쓰기</a> 참조)<pre><code class="language-javascript">{
user_id = 1
}</code></pre>
<h3 id="json-web-tokenjwt">JSON Web Token(JWT)</h3>
</li>
</ol>
<ul>
<li>access token을 생성하는 널리 사용되는 기술중 하나로, 유저 정보를 담은 JSON 데이터를 암호화해서 클라이언트와 서버간에 주고 받는다.<h4 id="jwt-구성">JWT 구성</h4>
<img src="https://images.velog.io/images/kykim_dev/post/ec021eed-cebf-42d6-a7ee-5a244eff856c/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-11-01%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%204.17.15.png" alt=""></li>
<li><strong>헤더(header)</strong>: 토큰 타입의 해시 알고리즘 정보로 BASE64 방식으로 인코딩 되어 JWT의 가장 첫부분에 기록됨 (암호화 x) 예: {&quot;alg&quot;:&quot;HS256&quot;, &quot;typ&quot;:&quot;JWT&quot;}</li>
<li><strong>내용(payload)</strong>: 내용에는 exp와 같이 만료시간을 나타내는 공개 클레임, 클라이언트와 서버간 협의하에 사용하는 비공개 클레임. 두가지 요소를 조합하여 작성한 뒤 BASE64 인코딩해 두번째 요소로 위치 예: {&quot;user-id&quot;: 1, &quot;exp&quot;:1539517391}</li>
<li><strong>서명(signature)</strong>: JWT가 원본 그대로라는 것을 확인.
BASE64URL 인코드된 header와 payload 그리고 JWT secret(별도 생성)을 헤더에 지정된 암호 알고리즘으로 암호화하여 전송 (복호화 가능)</li>
<li>프론트엔드가 JWT -&gt; 백엔드 API 서버 전송 -&gt; JWT 서명부분 복호화하여 서버 생성 유무 확인 
=&gt; header와 payload는 암호화가 아닌 BASE64 인코딩한 것이므로 누구나 원본을 볼 수 있으니 개인정보를 담아선 안된다. <h3 id="access-token을-사용하는-이유">Access token을 사용하는 이유</h3>
</li>
<li>무거운 bcrypt call을 받지 않고, 간단한 해쉬 만으로 퍼포먼스를 낼 수 있다. </li>
<li>Client-side storage방식
쿠키와 같이 실제 ID와 패스워드가 저장되지 않고, 다른 사이트에서 재사용할 수 없기 때문에 안전하다.</li>
</ul>
<h1 id="인가authorization">인가(Authorization)</h1>
<ul>
<li>유저가 요청하는 request를 실행할 수 있는 <strong>권한이 있는 유저인가를 확인하는 절차</strong>다.</li>
<li>예를 들어, 해당 유저는 고객 정보를 볼 수 있는 있지만 수정할 수는 없다 </li>
<li>Authroization도 JWT를 통해서 구현 될 수 있다.<ul>
<li><code>access token</code>을 통해 해당 유저 정보를 얻을 수 있음으로 해당 유저가 가지고 있는 권한(permission)도 확인 할 수 있다.</li>
</ul>
</li>
</ul>
<h2 id="인가-절차">인가 절차</h2>
<ol>
<li>Authentication 절차를 통해 <code>access token</code>을 생성한다. <code>access token</code>에는 유저 정보를 확인할 수 있는 정보가 들어가 있어야 한다 (예: user id).</li>
<li>유저가 request를 보낼때 <code>access token</code>을 첨부해서 보낸다.</li>
<li>서버에서는 유저가 보낸 <code>access token</code>을 복호화 한다.</li>
<li>복호화된 데이터를 통해 user id를 얻는다.</li>
<li>user id를 사용해서 database에서 해당 유저의 권한(permission)을 확인하다.</li>
<li>유저가 충분한 권한을 가지고 있으면 해당 요청을 처리한다.</li>
<li>유저가 권한을 가지고 있지 않으면 Unauthorized Response(401) 혹은 다른 에러 코드를 보낸다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[WEB #12] SPA vs MPA, CSR vs SSR ]]></title>
            <link>https://velog.io/@kykim_dev/WEB-12-SPA-vs-MPA-CSR-vs-SSR</link>
            <guid>https://velog.io/@kykim_dev/WEB-12-SPA-vs-MPA-CSR-vs-SSR</guid>
            <pubDate>Tue, 26 Oct 2021 04:32:02 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>SPA는 3세대 웹이 등장하면서 기존의 웹이 제공해주지 못했던 풍부한 UX를 구현할 수 있게 해준 중요한 개념이다. SPA는 CSR과 SSR, 두 가지 방법으로 지원할 수 있다.</p>
</blockquote>
<h1 id="spa-vs-mpa">SPA vs MPA</h1>
<ul>
<li>MPA는 Multi Page Application의 약자로 정적 웹사이트로 개발된 HTML 페이지가 여러개로 구성된다. (2세대)</li>
<li>SPA는 Single Page Application의 약자로 3세대 인터넷의 발달로 동적 웹을 구현할 수 있게 되면서 단일 HTML에 페이지를 바꾸지 않고 필요한 부분을 JavaScript로 바꿔줄 수 있게 되었다.</li>
<li>cf. <a href="https://velog.io/@kykim_dev/WEB-2-WEB-%EC%84%9C%EB%B9%84%EC%8A%A4%EC%9D%98-%EC%97%AD%EC%82%AC%EC%99%80-%EB%B0%9C%EC%A0%84-feat.%EA%B0%9C%EB%B0%9C%EC%9E%90%EC%9D%98-%EC%9D%BC%EC%9E%90%EB%A6%AC">SPA vs MPA의 개념</a> </li>
</ul>
<h1 id="csr-vs-ssr">CSR vs SSR</h1>
<ul>
<li>SPA는 CSR과 SSR, 두 가지 방법으로 구현할 수 있는데, CSR은 밀 키트와 같은 &#39;비조리형 배달 음식&#39;에 SSR은 완제품으로 배달하는 &#39;조리형 배달 음식&#39;에 비유할 수 있다.</li>
<li><strong>비조리형 배달 음식 (CSR)</strong><ul>
<li>식당에서 재료만 포장하여 고객에게 배송 (index.html, index.js 등)</li>
<li>재료를 받은 고객이 최종 조리하여 음식 완성 후 식사</li>
<li>최종 형태를 갖추게 되는 단계는 <strong>클라이언트 단</strong></li>
</ul>
</li>
<li><strong>조리형 배달 음식 (SSR)</strong><ul>
<li>식당에서 모든 조리 완료 후 고객에게 배송</li>
<li>조리가 완료된 음식을 고객이 받아 바로 식사</li>
<li>최종 형태를 갖추게 되는 단계는 <strong>서버 단</strong><h2 id="client-side-renderingcsr">Client Side rendering(CSR)</h2>
</li>
</ul>
</li>
<li><strong>웹 페이지의 렌더링이 클라이언트(브라우저) 측에서 일어나는 것</strong>을 의미한다.</li>
<li>브라우저는 최초 요청에서 html, js, css 확장자의 파일을 차례로 다운로드하는데, 최초로 불러온 html의 내용은 비어있다. (index.html: html, body 태그만 존재)</li>
<li>JavaScript 파일의 다운로드가 완료된 다음, 해당 js 파일이 DOM을 빈 HTML 위에 그리기 시작한다.</li>
<li><strong>백엔드 호출을 최소화 할 수 있음</strong><ul>
<li>최초 호출 때만 html, js, css를 요청</li>
<li>이후에는 화면에서 변화가 일어나야 하는 만큼의 데이터만 요청한다. (ex. JSON)</li>
</ul>
</li>
<li><strong>라우팅(새로운 페이지로 이동)을 하더라도 html 자체가 바뀌는 것이 아니라 JavaScript 차원에서 새로운 화면을 그려낸다.</strong><ul>
<li>렌더링 속도가 빨라 트래픽이 많은 경우 용이하다.</li>
</ul>
</li>
<li>ex) Create React App (CRA)은 CSR만 제공하는 어플리케이션으로 별도의 초기 설정 없이 SPA 사이트를 구현할 수 있게 도와준다.
=&gt; 그러나 <strong>CSR의 단점은 검색엔진최적화(SEO)에 취약</strong>하다는 단점이 있다.</li>
</ul>
<h2 id="server-side-renderingssr">Server Side Rendering(SSR)</h2>
<ul>
<li>위에서 언급했던 CSR과 SSR의 차이에서 알 수 있듯, SSR은 서버에서 첫 페이지의 렌더링을 클라이언트 측이 아닌 서버 측에서 처리해주는 방식.</li>
<li>CSR과 비교하면,<ul>
<li>1<strong>) UX 측면에서 유리</strong><ul>
<li>CSR에 비해 페이지를 구성하는 속도는 늦어질 수 있지만, 전체적으로 사용자에게 보여주는 콘텐츠 구성이 완료되는 시점은 빨라진다.</li>
<li>Code Splitting (코드 분할)<ul>
<li>만약 첫 페이지 구성에 불필요한 JS 파일을 받아온다면?</li>
<li>그 번들 파일의 크기가 크다면?</li>
<li>불필요한 내용들은 받아오지 않고 서버 단에서 첫 페이지에 필요한 정적인 부분만 처리한 뒤 JS는 나중에 필요한 부분만 필요할 때 로드할 수 있다면?</li>
</ul>
</li>
</ul>
</li>
<li><strong>2) SEO 측면에서 유리</strong><ul>
<li>서버에서 사용자에게 보여줄 페이지를 모두 구성하여 사용자에게 보여주는 방식이기 때문에 CSR의 단점인 &quot;첫 페이지 깡통&quot; 상태를 극복할 수 있음.</li>
</ul>
</li>
<li>주의) 페이지를 잘못 구성할 경우 <strong>CSR에 비해 서버 부하가 커지거나 / 첫 로딩이 매우 느려질 수 있다</strong></li>
</ul>
</li>
</ul>
<h2 id="search-engine-optimizationseo">Search Engine Optimization(SEO)</h2>
<ul>
<li>웹 크롤러가 웹 페이지를 크롤링 할 때 CSR로 구성된 페이지는 빈 페이지로 인식하고 제대로 크롤링을 하지 못할 수 있다. </li>
<li>CSR에서 렌더링되는 첫 페이지는 비어있는 index.html이기 때문에 JavaScript 파일이 다운로드 되어 페이지가 로드 될 때까지 빈 상태로 인식할 수 있기 때문이다. =&gt; 검색 노출 안됨!</li>
<li>반대로 SSR 방식은 첫 페이지가 로드되면 모든 정보가 담긴 전체 페이지를 보여주기 때문에 크롤링에 용이해 검색 노출이 잘 될 수 있다. 
=&gt; 따라서 SPA의 장점을 살리면서도 SEO도 같이 챙길 수 있는 방법을 고안해냈다. =&gt; SSR
cf. 웹 사이트 도메인에 <code>/robot.txt</code>를 검색해보면 각 웹사이트 별 크롤링 가능/불가능 항목을 볼 수 있다.</li>
</ul>
<h2 id="spa-for-ssr-보완책">SPA for SSR (보완책)</h2>
<ul>
<li>CSR과 SSR을 혼합해서 쓸 수 있다. </li>
<li>1) 첫 렌더링은 SSR로 2) 이후 렌더 부터는 CSR로
<img src="https://images.velog.io/images/kykim_dev/post/3c0493d5-2c94-4cfa-8d3e-ad382add1253/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-10-26%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%201.24.45.png" alt=""></li>
<li>SSR의 요소<ul>
<li>node.js로 구성된 프론트엔드 서버</li>
<li>pyhton, django로 되어 있는 백엔드 서버</li>
</ul>
</li>
<li>SSR 과정<ol>
<li>유저가 브라우저에 <strong><code>/</code></strong>를 입력.</li>
<li>미리 실행되고 있는 FE 서버가 요청을 받고 SSR 실행</li>
<li>만들어진 HTML을 브라우저에게 보냄.</li>
<li>브라우저가 응답받은 HTML을 그림.</li>
<li>HTML에 기능을 부여할 <code>**index.js**</code>파일을 다운로드 받음. (hydration)</li>
<li>다운로드가 완료된 이후, <strong><code>go to second</code></strong> 링크를 클릭.</li>
<li><strong><code>/second</code></strong>로 라우팅하고 second 페이지 코드를 생성.</li>
</ol>
</li>
</ul>
<h3 id="hydration">hydration</h3>
<ul>
<li>서버에서 전송한 정적 문서를 데이터 변경에 반응할 수 있는 동적 형태로 변환하는 클라이언트 측 프로세스를 말한다.</li>
<li>render와 동일하지만, DOM은 이미 그려져 있는 상태이기 때문에 event handler만 부착하는 식으로 작동한다.<pre><code class="language-javascript">import React from &quot;react&quot;;
import ReactDom from &quot;react-dom&quot;;
import App from &quot;./App&quot;;
</code></pre>
</li>
</ul>
<p>const initialData = window.<strong>INITIAL_DATA</strong>;</p>
<p>ReactDom.hydrate(
  <App page={initialData?.page} />,
  document.getElementById(&quot;root&quot;)
);
// 출처: &lt;실전 리액트 프로그래밍&gt;, 이재승 저</p>
<p>```</p>
<h3 id="nextjs">Next.js</h3>
<ul>
<li>서버사이드렌더링(SSR)과 code split 등을 지원하는 ReactJS 전용 프레임워크</li>
<li>생산성을 위해 주로 채택하며, SSR의 CRA를 간단히 구성할 수 있다. </li>
<li>SSR 외에도 React App에 필요한 여러 필수 기능을 제공한다.
(동적 라우팅, pre-rendering, CSS-in-JS)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React #9] styled-components로 효과적인 스타일링]]></title>
            <link>https://velog.io/@kykim_dev/React-9-styled-components%EB%A1%9C-%ED%9A%A8%EA%B3%BC%EC%A0%81%EC%9D%B8-%EC%8A%A4%ED%83%80%EC%9D%BC%EB%A7%81</link>
            <guid>https://velog.io/@kykim_dev/React-9-styled-components%EB%A1%9C-%ED%9A%A8%EA%B3%BC%EC%A0%81%EC%9D%B8-%EC%8A%A4%ED%83%80%EC%9D%BC%EB%A7%81</guid>
            <pubDate>Mon, 25 Oct 2021 03:56:52 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>현대 앱이 컴포넌트를 기반으로 발전해가면서 CSS 스타일링 방법론 또한 &#39;컴포넌트를 기반&#39;으로 재구성되고 있다. 이러한 발전 속에서 등장한 패러다임이 &#39;CSS-in-JS&#39;이며 그 중 가장 인기있는 라이브러리가 &#39;sytled-components&#39;이다.</p>
</blockquote>
<h2 id="css-in-js의-등장">CSS in JS의 등장</h2>
<ul>
<li>모든 기술은 문제를 해결하기 위해 등장했다. &#39;styled-components&#39; 또한 이전 기술의 문제점을 보완하기 위해 등장했다.</li>
<li>pure CSS의 문제점을 보완하기 위해 CSS preprocessor(전처리기)로 SASS, Scss가 등장했지만 여전히 문제점이 있다.</li>
<li>CSS 클래스명을 붙이기 어렵다. </li>
<li>정해진 가이드가 없으면 구조가 복잡해진다.</li>
<li>방대한 스타일 정보로 인한 스크롤 지옥</li>
<li>CSS 클래스로 조건부 스타일링의 한계</li>
<li>동적인 변화를 표현하기에 한계 </li>
<li>css-in-js는 2018년 이후 폭발적으로 성장해왔고, styled-components는 npm 다운로드 수 기준으로 가장 인기있는 <strong>css-in-js 라이브러리</strong>다.<h3 id="css-in-js의-장점">CSS-in-JS의 장점</h3>
</li>
<li>클래스명을 해시값으로 자동 생성하고, 클래스명 오염을 방지한다.</li>
<li>JavaScript에서 CSS 문법을 온전하게 사용할 수 있다.</li>
<li>JavaScript의 동적인 값들을 온전하게 사용할 수 있다.</li>
<li><strong>컴포넌트 단위로 추상화</strong>하여 적용하기 알맞다.
=&gt; 컴포넌트와 스타일이 하나의 파일로 결합되어 모듈화가 수월해진다.</li>
</ul>
<h2 id="styled-components">styled-components</h2>
<ul>
<li>설치
<code>npm install --save styled-components</code></li>
<li>styled-components는 &#39;고유한 클래스 명을 자동으로 생성&#39;해 서로 다른 스타일이 전역에서 중복 적용될 우려를 줄여주고,</li>
<li>렌더링 되는 컴포넌트에 맞는 스타일 컴포넌트를 자동으로 내보내주는 등 <strong>컴포넌트의 스타일 작성과 관리를 효율적으로 할 수 있다</strong>는 장점이 있다.  </li>
</ul>
<h2 id="시작하기">시작하기</h2>
<ul>
<li><code>styled-components</code> 라이브러리에서 import 해온 <code>styled</code> 객체를 이용한다.</li>
<li>html 태그 이름 뒤 Tagged Template 문법을 활용해 CSS 속성을 정의한다. (Template Literals(``)의 확장)<pre><code class="language-javascript">import styled from &#39;styled-components&#39;;
</code></pre>
</li>
</ul>
<p>render(
  <Wrapper>
      <Title>
       Hello World!
        </Title>
  </Wrapper>
);</p>
<p>const Wrapper = styled.section<code>padding: 4em;
 background: papayawhip;</code>;</p>
<p>const Title = styled.h1<code>font-size: 1.5em;
 text-align: center;
 color: blue;</code>;</p>
<pre><code>## styled-components 사용하기
### Adapting based on props
```javascript
import styled from &#39;styled-components&#39;;

render(
  &lt;div&gt;
   &lt;Button&gt;Normal&lt;/Button&gt;
   &lt;Button width=&quot;100&quot;&gt;Primary&lt;/Button&gt;
  &lt;/div&gt;
);

const Button = styled.button`
 background: ${props =&gt; props.width &lt; 200 ? &quot;blue&quot; : &quot;white&quot;};
 color : ${props =&gt; props.primary ? &quot;white&quot; : &quot;blue&quot;};
`;</code></pre><ul>
<li>props를 기반으로 삼항 연산자를 붙여 스타일의 변화를 줄 수 있다.</li>
</ul>
<h3 id="extending-styles">Extending Styles</h3>
<pre><code class="language-javascript">import styled from &#39;styled-components&#39;;

render(
  &lt;div&gt;
   &lt;Button&gt;Normal&lt;/Button&gt;
   &lt;TomatoAnchorButton&gt;Tomato Button&lt;/TomatoAnchorButton&gt;
  &lt;/div&gt;
);

const Button = styled.div`
 color: blue;
 font-size: 14px;
 margin: 5px;
 padding: 16px 0;
 border: 2px solid blue;
`;

const TomatoAnchorButton = styled(Button.withComponent(&quot;a&quot;))`
 color: tomato;
 border-color: tomato;
`;</code></pre>
<ul>
<li>중복 요소가 없는 일반적인 <code>Button</code> 속성을 정의하고, 이 속성을 바탕으로 확장된 속성(<code>TomatoAnchorButton</code>)을 정해준다. </li>
<li>이때 중복되는 속성은 새로운 속성으로 덮어진다.<h3 id="nesting">Nesting</h3>
<pre><code class="language-javascript">import styled from &#39;styled-components&#39;;
</code></pre>
</li>
</ul>
<p>render(
  &lt;&gt;
   <Thing>Hello world!</Thing>
   <Thing>How ya doing?</Thing>
   <Thing className = "something">
       <span className="contents">The sun is shining</span>
   </Thing>
   <div>Pretty nice day today.</div>
   <Thing>Don&#39;t you think?</Thing>
   <div className="something-else">
    <Thing>Splendid</Thing> 
  </div>
  &lt;/&gt;
);</p>
<p>const Thing = styled.div`
 color: blue;</p>
<p> &amp;:hover {
  color: red;
}</p>
<p>&amp;.something </p>
<p>.something-else &amp; {
  border: 1px solid;
}
`;</p>
<pre><code>### Global Style &amp; ThemeProvider
- 전역 스타일을 적용할 때 쓴다. 
- `reset` 설치, `createGlobalStyle` 에 전역 스타일을 넣고, `ThemeProvider`를 통해 자주 사용하는 폰트, 색상 등을 지정해 원하는 곳에 가져다 쓸 수 있다.
- styled-reset 설치 `npm install --save styled-components styled-reset`
```javascript
import { createGlobalStyle } from &#39;styled-components&#39;;
import reset from &#39;styled-reset&#39;;

const GlobalStyle = createGlobalStyle`
  ${reset}

    // 전역스타일

`;

export default GlobalStyle;</code></pre><ul>
<li><code>index.js</code>에 import 해준다.<pre><code class="language-javascript">import React from &quot;react&quot;;
import ReactDOM from &quot;react-dom&quot;;
import GlobalStyle from &#39;./styles/GlobalStyle&#39;;
import { ThemeProvider } from &quot;styled-components&quot;;
import Routes from &quot;./Routes&quot;;
import theme from &quot;./styles/theme&quot;;
</code></pre>
</li>
</ul>
<p>ReactDOM.render(
    &lt;&gt;
    <GlobalStyle />
    <ThemeProvider theme={theme}>
      <Routes />
    </ThemeProvider>
    &lt;/&gt;,
  document.getElementById(&quot;root&quot;)
);</p>
<pre><code>```javascript
// theme.js

const theme = {
  background: &quot;#FFFEFC&quot;,
  white: &quot;#FFFFFF&quot;,
  vermilion: &quot;#ff7425&quot;,
  orange: &quot;#FF9900&quot;,
  opacityOrange: &quot;rgba(242,153,74,0.5)&quot;,
  yellow: &quot;#FFD66C&quot;,
  grey: &quot;rgba(196,196,196,0.3)&quot;,
  middleGrey: &quot;rgba(65,65,65,0.4)&quot;,
  deepGrey: &quot;#828282&quot;,
  lightOrange: &quot;rgba(255,195,170,0.3)&quot;,
  fontColor: &quot;#2D2B2B&quot;,
  fontTitle: &quot;&#39;Alata&#39;, sans-serif;&quot;,
  fontContent: &quot;&#39;Noto Sans KR&#39;, sans-serif;&quot;,
};

export default theme;</code></pre><pre><code class="language-javascript">// theme 사용

const Container = styled.div`
  background-color: ${props =&gt; props.theme.background}
`;</code></pre>
<h3 id="mixin">Mixin</h3>
<ul>
<li>CSS를 import해서 속성을 정의하고 원하는 요소에 쓸 수 있다.<pre><code class="language-javascript">import { css } from &quot;styled-components&quot;
</code></pre>
</li>
</ul>
<p>const Navigation = styled.nav<code>position: fixed;
  left: 0;
  top: 0;
  right: 0;
  ${Sticky}</code>;</p>
<p>const Sticky = css<code>position: fixed !important;
  background-color: white;
  border-bottom: 1px solid rgba(0, 0, 0, 0.11);
  box-shadow: 0 0 5px rgba(0, 0, 0, 0.11);
  transition: all 0.6s ease-in-out;
  color: black;</code>;</p>
<p>//</p>
<p>const RingVariant = (radius, stroke = &quot;10&quot;) =&gt; css<code>position: absolute;
  border-radius: 50%;
  height: ${radius * 2}px;
  width: ${radius * 2}px;
  border: ${stroke}px solid rgba(0, 0, 0, 0.5);</code>;</p>
<pre><code>
### Attaching additional props
```javascript
render(
  &lt;div&gt;
    &lt;Input placeholder=&quot;A small text input&quot; /&gt;
    &lt;br /&gt;
    &lt;Input placeholder=&quot;A bigger text input&quot; size=&quot;2em&quot; /&gt;
  &lt;/div&gt;
);

//attr()의 매개변수에 객체로 속성 부여
const Input = styled.input.attrs(props =&gt; ({
  // we can define static props
  type: &quot;password&quot;,

  // or we can define dynamic ones
  size: props.size || &quot;1em&quot;,
}))`
  color: palevioletred;
  font-size: 1em;
  border: 2px solid palevioletred;
  border-radius: 3px;

  /* here we use the dynamically computed prop */
  margin: ${props =&gt; props.size};
  padding: ${props =&gt; props.size};
`;</code></pre><h3 id="animation">Animation</h3>
<ul>
<li>애니메이션 및 스타일 효과를 줄 때는 <code>keyframes</code>를 사용한다.<pre><code class="language-javascript">// Create the keyframes
const rotate = keyframes`
from {
  transform: rotate(0deg);
}
to {
  transform: rotate(360deg);
}
`;
</code></pre>
</li>
</ul>
<p>// Here we create a component that will rotate everything we pass in over two seconds
const Rotate = styled.div<code>display: inline-block;
  animation: ${rotate} 2s linear infinite;
  padding: 2rem 1rem;
  font-size: 1.2rem;</code>;
render(
  <Rotate>&lt; 💅🏾 &gt;</Rotate>
);
```</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Project] 맥주와 함께하는 숙소 예약 서비스 'BeerBnB' ]]></title>
            <link>https://velog.io/@kykim_dev/Project-%EB%A7%A5%EC%A3%BC%EC%99%80-%ED%95%A8%EA%BB%98%ED%95%98%EB%8A%94-%EC%88%99%EC%86%8C-%EC%98%88%EC%95%BD-%EC%84%9C%EB%B9%84%EC%8A%A4-BeerBnB</link>
            <guid>https://velog.io/@kykim_dev/Project-%EB%A7%A5%EC%A3%BC%EC%99%80-%ED%95%A8%EA%BB%98%ED%95%98%EB%8A%94-%EC%88%99%EC%86%8C-%EC%98%88%EC%95%BD-%EC%84%9C%EB%B9%84%EC%8A%A4-BeerBnB</guid>
            <pubDate>Tue, 19 Oct 2021 13:32:42 GMT</pubDate>
            <description><![CDATA[<h2 id="프로젝트-소개">프로젝트 소개</h2>
<ul>
<li>메인 서비스 <ul>
<li>맥주, 소주, 위스키 등 &#39;술과 어울리는 숙소&#39;를 테마로 지역별, 유형별 원하는 숙소를 검색하고, 예약할 수 있다.  </li>
<li>&#39;호스트 등록&#39;을 서비스를 통해 내 숙소를 쉽게 등록하고 호스트가 될 수 있다.  </li>
</ul>
</li>
<li>프로젝트 기간 : 2021.08.02.-2021.08.13. </li>
<li>모티브 페이지: <a href="https://www.airbnb.co.kr/">에어비엔비</a></li>
<li>참여자: 총 6명. Front-end(김가영, 이나현, 배윤아, 황문실), Back-end(김도담, 박종규)</li>
<li>구현 기능 (페이지 기준)<ol>
<li>메인 페이지 : 날짜, 지역 별 숙소 검색이 가능한 Navigation bar</li>
<li>소셜 로그인 : 카카오 API 활용 소셜 로그인 </li>
<li>숙소 리스트 페이지 : 선택 숙소 리스트 및 카카오 지도 API 활용 숙소 마킹 </li>
<li>숙소 상세 페이지 : 숙소 정보 및 예약(기간, 금액) 기능</li>
<li>마이 페이지 : 예약 숙소 리스트 </li>
<li>호스트 등록 페이지 : 카카오 주소 API 활용 숙소 form(text, image, 주소) 등록 </li>
</ol>
</li>
<li>GitHub repository : <a href="https://github.com/Kayoungkimjs/23-2nd-BeerBnB-frontend">Front-end</a> </li>
</ul>
<h2 id="데모-영상">데모 영상</h2>
<p>!youtube[9DcR3fp_jr4]</p>
<h2 id="사용-기술">사용 기술</h2>
<h3 id="front-end">Front-End</h3>
<ul>
<li>React.js, styled-components, axios, react-google-maps/api, react-slick, moment, react-dates, react-daum-postcode, react-kakao-login<h3 id="back-end">Back-End</h3>
</li>
<li>Python, Django web framework, Bcrypt, My SQL<h3 id="common">Common</h3>
</li>
<li>RESTful API, trello<h2 id="직접-구현-사항">직접 구현 사항</h2>
<h3 id="소셜-로그인">소셜 로그인</h3>
</li>
<li>카카오 API를 활용해 카카오 로그인으로 서비스를 이용할 수 있음.</li>
<li>로그인 시 navigation bar에 &#39;로그아웃&#39;, &#39;마이 페이지&#39; 버튼 생성
<img src="https://images.velog.io/images/kykim_dev/post/0b70232d-76a5-4c71-b9c7-9d32c4c9cfd7/%E1%84%85%E1%85%A9%E1%84%80%E1%85%B3%E1%84%8B%E1%85%B5%E1%86%AB.gif" alt=""><h3 id="주요-코드">주요 코드</h3>
<pre><code class="language-javascript">const handleLogin = () =&gt; {
  Kakao.Auth.login({
    scope: &#39;profile_nickname, profile_image, birthday&#39;,
    success: function (response) {
      fetch(`${API.LOGIN}`, {
        method: &#39;GET&#39;,
        headers: {
          Authorization: response.access_token,
        },
      })
        .then(res =&gt; res.json())
        .then(res =&gt; {
          if (res) {
            localStorage.setItem(&#39;token&#39;, res);
            setIsLoggedIn(true);
            console.log(res);
          }
        });
    },
    fail: function (error) {
      alert(&#39;아이디와 비밀번호를 확인해주세요:)&#39;);
    },
  });
};
</code></pre>
</li>
</ul>
<pre><code>#### 소셜 로그인 프로세스
- `Kakao.Auth.login` 함수를 사용해 카카오 로그인을 실행하고, `scope`에 필요한 정보를 가져온다. (사이트 내 프로필 등록을 위한 닉네임, 사진, 생년월일 정보를 가져옴)  
- `success` 콜백에서 카카오에서 사용자 토큰을 받아, fetch로 백엔드 서버에 넘겨준다.
- 백엔드 서버에서 우리 사이트에서 사용할 토큰을 주면, 프론트 localStorage에 저장한다.

### 호스트 등록 페이지
- 숙소 정보(text), 게스트 인원(증감 버튼), 숙소 요금, 이미지 업로드 등 다양한 form type 사용
![](https://images.velog.io/images/kykim_dev/post/a95b9d60-1dd5-4fb7-8b02-e543af804a23/%E1%84%8C%E1%85%AE%E1%84%89%E1%85%A9%E1%84%83%E1%85%B3%E1%86%BC%E1%84%85%E1%85%A9%E1%86%A8.gif)
- 카카오 주소 API, `axios`를 사용해 주소 찾기 기능 구현
![](https://images.velog.io/images/kykim_dev/post/034ec891-9b22-4f2a-9ee8-0c0001aee9fb/%E1%84%92%E1%85%A9%E1%84%89%E1%85%B3%E1%84%90%E1%85%B3%E1%84%83%E1%85%B3%E1%86%BC%E1%84%85%E1%85%A9%E1%86%A8.gif)

## 프로젝트 회고 🌟
### 프로그래밍(기술 구현) 측면
- 소셜 API 활용
이번 프로젝트에서 가장 기대했던 부분으로 여러 카카오 API를 내가 필요한 기능에 직접 구현해보고 싶었다. 
&#39;로그인 정도야 하루면 가능하지!&#39;라고 생각했던 초기와 달리 일주일이 걸려서야 겨우 UI 팝업을 만들어낼 수 있었고, 이후 메인 페이지와의 연결에서도 많은 어려움을 겪었다. 
➡️ **공식 문서, 개념의 중요성**을 다시 한 번 느끼게 된 계기가 되었다. 공식 문서를 제대로 보지 않고 잘 안될 때마다 구글링을 했는데, 역시 돌아와 공식 문서에서 답을 찾았다. 시간이 걸리더라도 공식 문서를 제대로 읽고, 이해하자.
➡️ 인증/인가, 로그인 구현 원리에 대해 제대로 이해한 후 코드를 짰다면 학습이 많이 되었을 것 같다. 코드를 구현하는데 급급해 원리를 이해하지 못하고 후에서야 다시 공부했다. 
- GitHub rebase
효율적인 협업을 위한 &#39;rebase&#39;를 진행했다. 처음 진행이라 나의 실수로 다른 분들의 코드에 피해가 갈까봐 조마조마했다. 
➡️ 깃과 깃헙은 양파같은 녀석이다. 공부하면 할수록 어려운데 또 결과는 효과적이라 매력적이다. 많이 연습하고 실패해보면서 익혀두어야겠다. 
- ReactJS function형 &amp; Styled-Component
class형보다 function형은 JS를 더 많이 닮았고 그렇기 때문에 직관적이다. state를 관리하는 것도 class보다 단순하다. 그러나, 코드로만 봤을 때는class형이 componet간의 유기적 관계, 흐름이 잘 드러나는 것 같았다. 
styled-component는 별도의 스타일 페이지 없이 해당 component 페이지 내에서 함께 쓸 수 있어 효율적이다. 하지만 CSS, Scss에 비해 제약이 있어 스타일 설정이 어려웠다.
 ➡️ Redux, axios 등 component를 효과적으로 관리할 수 있는 프레임워크를 공부해야겠다. 

### 프로젝트 매니지먼트 측면 
- 효과적, 효율적 소통을 가능케 한 &#39;프로젝트 기획&#39;과 &#39;API 정의서&#39;
이번 프로젝트는 시작과 그 과정이 매끄러웠다. 
많은 이유가 있겠지만 가장 큰 역할은 &#39;프로젝트 기획서&#39;와 &#39;API 정의서&#39;가 했다. 
일잘러 팀원의 제안으로 구성할 페이지 별 API, 기능을 설명하는 프로젝트 기획서를 작성하고 서로 어떤 작업을 할지 공유했으며, API 정의서를 통해 프론트-백이 매번 묻지 않아도 기능별 API를 확인할 수 있었다. 
- SCRUM의 진가
매일 오전 팀 전원이 참여하는 Daily Standup Meeting은 15분 내로 간단한 인사와 함께 간략하게 &#39;오늘의 할 일&#39;과 &#39;blocker&#39;를 나누었다. 
blocker에 대해 이야기하면 팀원들이 서로 의견을 내고, 어려운 부분을 도와주거나 프로젝트 방향을 바꾸어 나갈 수 있어 프로젝트 일정을 지연시킬 수 있는 요소들을 사전에 방지할 수 있었다. 그리고 무엇보다 프로젝트에 임하면서 생기는 심리적 부담감과 스트레스를 덜어줄 수 있었다.  
- 프로젝트 나무를 보는 눈
아쉬운 점도 물론 있다. 프로젝트를 기획하는 과정에서 내가 하는 기능의 프론트 부분 뿐 아니라 백엔드와 어떻게 통신하고 연결해야하는지 등에 대한 전반적인 이해가 필요했는데 부족했다. 그 결과 잘못된 데이터 전달 형식의 코드를 멋지게 짰고, 거듭 수정해야했다. 
모르면 물어보고, 중간중간 더 자주 백엔드 개발자님과 소통해야겠다는 큰 깨달음을 얻었다. </code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[React #8] React-Router로 SPA 구현하기]]></title>
            <link>https://velog.io/@kykim_dev/react-router</link>
            <guid>https://velog.io/@kykim_dev/react-router</guid>
            <pubDate>Tue, 12 Oct 2021 08:43:40 GMT</pubDate>
            <description><![CDATA[<h1 id="spa">SPA</h1>
<ul>
<li>SPA(Single Page Application)은 페이지가 1개인 어플리케이션을 뜻한다.</li>
<li>HTML에서는 페이지 수만큼 HTML 파일이 존재하지만, React에서 .html 파일의 개수는 1개다. 즉, React는 SPA 기반으로 작동된다.</li>
<li>한 웹페이지 안에서 여러 개의 페이지를 보여줄 때 라우팅(routing)이 필요하다. 
참고: <a href="https://velopert.com/3417">https://velopert.com/3417</a></li>
</ul>
<h1 id="routing--react-router">Routing / React Router</h1>
<ul>
<li><strong>라우팅이란 다른 경로(url 주소)에 따라 다른 화면(view)을 보여주는 것</strong>을 의미한다.</li>
<li>React에 내장되어 있지 않은 Third-party library로 <code>React-router</code>는 React의 라우팅 기능을 위해 가장 많이 사용하는 라이브러리다.</li>
<li><code>Router</code>는 컴포넌트 URL에서 서버에 들어오는 클라이언트의 request 경로를 결정해 &#39;보고싶은 부분만 변화&#39;시켜 준다. <h2 id="react-router-설치">react-router 설치</h2>
<code>npm install --save react-router-dom</code><h2 id="routes-컴포넌트-구현">Routes 컴포넌트 구현</h2>
<pre><code class="language-javascript">import React from &#39;react&#39;;
import { BrowserRouter as Router, Switch, Route } from &#39;react-router-dom&#39;;
import Login from &#39;./pages/Login/Login&#39;;
import Signup from &#39;./pages/Signup/Signup&#39;;
import Main from &#39;./pages/Main/Main&#39;;
</code></pre>
</li>
</ul>
<p>class Routes extends React.Component {
  render() {
    return (
      <Router>
          <Nav />
          <Switch>
           <Route exact path="/" component={Login} />
     <Route exact path="/signup" component={Signup} />
         <Route exact path="/main" component={Main} />
        </Switch>
    <Footer />
      </Router>
     )
   }
 }</p>
<p> export default Routes;</p>
<pre><code>- `react-router-dom`을 설치하면 프로젝트에 router components를 import 할 수 있다.
- React Router는 여러 router components를 제공하는데, 가장 많이 쓰이는 것이 `BrowserRouter`다. 
- 이 BrowserRouter를 상단 컴포넌트(top-level component)에 import 해준다. `import { BrowserRouter as Router} from ‘react-router-dom’;`
=&gt; 페이지가 reload 될 때 URL이 바뀌는 것을 막아주고, URL이 바뀔 때 Router가 현재 props의 URL 경로에 따라 어떤 자식 컴포넌트를 보여줄 것인지를 결정할 수 있다. 
- Route component는 현재 경로에 따라 작동하며, 각각의 route component는 1) `Router` 안에서 렌더링 되어야하며, 2) 렌더링 될 URL이 있는 `path` prop을 가져야 하며, 3) 그 path와 맞는 component를 감싸고 있어야 한다.
- 만약, router에 path가 없으면, 항상 렌더링 된다. 그러므로 현재 상태와 관계없이 사용자에게 보여주고 싶은 component라면 `&lt;Component /&gt;`로 넣어주면 된다. 
&gt; `&lt;Route exact path&gt;`에서 &#39;exact&#39;를 넣어주는 이유는 exact가 없으면 &#39;/&#39;가 들어간 모든 페이지가 화면에 노출된다. 정확하게 &#39;/&#39;, &#39;/signup&#39;만 보여줘!를 의미하는 것! 

## index.js
`ReactDOM.render(&lt;Routes /&gt;, document.getElementById(&#39;root&#39;));`
- CRA로 만든 앱에 라우팅 기능을 적용하려면 index.js를 수정해야 한다. 
- &lt;App /&gt; 컴포넌트 대신 라우팅을 설정한 컴포넌트 &lt;Routes /&gt;로 변경해야 한다. 

## Route 이동하기
### 1. `&lt;Link&gt;` 컴포넌트 사용하는 방법
```javascript
import React from &#39;react&#39;;
import { Link } from &#39;react-router-dom&#39;;

class Login extends React.Component {
  render() {
    return (
      &lt;div&gt;
        &lt;Link to=&quot;/signup&quot;&gt;회원가입&lt;/Link&gt;
      &lt;/div&gt;
    )
  }
}

export default Login;</code></pre><ul>
<li><code>react-router-dom</code>에서 제공하는 <code>&lt;Link&gt;</code> 컴포넌트는 DOM에서 <code>&lt;a&gt;</code>로 변환(Compile) 된다. cf. JSX - Babel - JavaScript</li>
<li><code>&lt;a&gt;</code> 태그와 마찬가지로 <code>&lt;Link&gt;</code> 태그도 지정한 경로로 바로 이동시켜주는 기능을 한다.</li>
<li><code>&lt;Link&gt;</code>는 <code>to</code> prop을 갖는다. (=href) 
cf) <code>&lt;a&gt;</code> vs <code>&lt;Link&gt;</code>
<code>&lt;a&gt;</code> - 외부 사이트로 이동하는 경우
<code>&lt;Link&gt;</code> - 프로젝트 내에서 페이지 전환하는 경우<blockquote>
<p> <code>&lt;a&gt;</code> 태그는 페이지를 새로 불러오면서 앱이 초기화되는 렌더링을 해 상태 값을 잃고 속도가 저하될 수 있다. <code>&lt;Link&gt;</code> 컴포넌트는 HTML History API를 사용해 브라우저의 주소만 바꿔준다.</p>
</blockquote>
<h3 id="cf-navlink">cf. NavLink</h3>
</li>
<li><code>&lt;Link&gt;</code>와 같이 페이지를 전환하는 경우 사용할 수 있으며 이 때 링크는 자동적으로 <code>&#39;active&#39;</code> 클래스를 적용한다. </li>
<li>네비게이션 메뉴, CSS style에서 <code>.active</code>를 적용해 active와 inactive를 구분해야하는 링크에 유용하게 적용할 수 있다.</li>
<li><code>activeClassName</code>을 적용해 따로 클래스명을 줄 수 있다.<h3 id="2-thispropshistorypush로-구현">2. <code>this.props.history.push()</code>로 구현</h3>
<pre><code class="language-javascript">import React from &#39;react&#39;;
import { withRouter } from &#39;react-router-dom&#39;;
</code></pre>
</li>
</ul>
<p>class Login extends React.Component {</p>
<p>  goToMain = () =&gt; {
    if(response.message === &quot;valid user&quot;) {
      this.props.history.push(&#39;/main&#39;);
   } else {
     alert (&quot;회원 가입을 해주세요&quot;);
     this.props.history.push(&#39;/signup&#39;);
   }
 }</p>
<p>  render() {
    return (
      <div>
       <button
         className="loginBtn"
         onClick={this.goToMain}
       > 로그인
       </button>
    )
  }
}</p>
<p>export default withRouter(Login);</p>
<p>```</p>
<ul>
<li>JavaScript 로직에서 이동이 필요한 경우 쓴다.</li>
<li>받은 history의 push 메서드의 인자로 Routes.js에서 설정한 path를 넘겨주면, 해당 라우트로 이동할 수 있다.</li>
<li>Route에 연결되어 있지 않은 컴포넌트에서 props에 route정보(history)를 받으려면 export하는 컴포넌트를 <code>withRouter</code>로 감싸주어야 한다.
=&gt; <code>withRouter</code>와 같이 해당 컴포넌트를 감싸주는 것을 <strong>Higher Order Component(HOC)</strong>라고 한다. <h3 id="두-가지-방법의-활용">두 가지 방법의 활용</h3>
</li>
</ul>
<ol>
<li><code>&lt;Link&gt;</code></li>
</ol>
<ul>
<li>클릭 시 바로 이동하는 로직 구현시 사용한다.
ex) Navigation bar, Aside, 아템 리스트 페이지에서 아이템 클릭 시 상세페이지 이동 등</li>
</ul>
<ol start="2">
<li><code>this.props.history.push()</code></li>
</ol>
<ul>
<li>페이지 전환시 추가로 처리해야 하는 로직이 있는 경우 사용한다.
ex) 로그인 버튼 클릭시<ul>
<li>Backend API로 데이터(User Info) 전송</li>
<li>User Data 인증 / 인가</li>
<li>response message</li>
<li>Case 1 : 회원 가입되어 있는 사용자 &gt; Main 페이지로 이동</li>
<li>Case 2 : 회원 가입이 되어 있지 않은 사용자 &gt; Signup 페이지로 이동</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React #7]  Component Lifecycle(생애주기) Methods]]></title>
            <link>https://velog.io/@kykim_dev/React-7-Component-Lifecycle%EC%83%9D%EC%95%A0%EC%A3%BC%EA%B8%B0-Methods</link>
            <guid>https://velog.io/@kykim_dev/React-7-Component-Lifecycle%EC%83%9D%EC%95%A0%EC%A3%BC%EA%B8%B0-Methods</guid>
            <pubDate>Tue, 12 Oct 2021 02:42:37 GMT</pubDate>
            <description><![CDATA[<h1 id="component-lifecycle">Component Lifecycle</h1>
<ul>
<li>React components는 create, render, DOM에 추가, 업데이트, 삭제될 수 있다. 이 모든 스텝들을 &#39;컴포넌트의 생명주기(lifecycle)&#39;라고 한다.</li>
<li>lifecyle은 Mounting - Updating - Unmounting의 3단계를 거친다. 
<img src="https://images.velog.io/images/kykim_dev/post/a5080322-343d-4bd5-bc9c-a84a8fbd48e9/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-10-11%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%2012.54.53.png" alt=""> 출처: codeacademy<h3 id="1-mounting---컴포넌트-호출">1. Mounting - 컴포넌트 호출</h3>
</li>
<li><strong>컴포넌트가 초기화되고 DOM에 놓이는 첫 순간</strong>으로 Mount 되지 않으면 화면에서 볼 수 없다.</li>
<li><code>constructor()</code>, <code>render()</code>, <code>componentDidMount()</code> 메서드가 실행된다.<h3 id="2-updating---상태-업데이트">2. Updating - 상태 업데이트</h3>
</li>
<li>state나 props의 상태 변화에 따라 컴포넌트가 업데이트 될 때</li>
<li>정적인 컴포넌트(예: 로고)를 제외하고 컴포넌트의 상태값이 변하거나, 다른 props가 컴포넌트에 전달될 때 업데이트 된다. </li>
<li><code>render()</code>, <code>componentDidUpdate()</code> 메서드가 실행된다.<h3 id="3-unmounting---제거">3. Unmounting - 제거</h3>
</li>
<li>컴포넌트가 DOM에서 삭제될 때</li>
<li><code>componentWillUnmount()</code> 메서드가 실행된다.</li>
</ul>
<h1 id="lifecycle-methods">Lifecycle Methods</h1>
<p>컴포넌트의 생애주기 각각의 부분을 의미한다. 각각의 메서드는 React.component class에서 제공하는 메서드로 class 메서드를 생성할 때 사용할 수 있고, lifecycle에 따라 각자의 메서드가 호출된다. </p>
<pre><code class="language-javascript">import React from &#39;react&#39;;

export class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = { date: new Date() };
  }
  render() {
    return (
      &lt;div&gt;
        {this.props.isPrecise
          ? this.state.date.toISOString()
          : this.state.date.toLocaleTimeString()}
      &lt;/div&gt;
    );
  }
  startInterval() {
    let delay;
    if (this.props.isPrecise) {
      delay = 100;
    } else {
      delay = 1000;
    }
    this.intervalID = setInterval(() =&gt; {
      this.setState({ date: new Date() });
    }, delay);
  }
  componentDidMount() {
    this.startInterval();
  }
  componentDidUpdate(prevProps) {
    if (this.props.isPrecise === prevProps.isPrecise) {
      return;
    }
    clearInterval(this.intervalID);
    this.startInterval();
  }
  componentWillUnmount() {
    clearInterval(this.intervalID);
  }
}

ReactDOM.render(&lt;Clock /&gt;, document.getElementById(&#39;app&#39;));</code></pre>
<h3 id="mounting-단계-메서드">Mounting 단계 메서드</h3>
<p><code>constructor()</code> mounting 단계에서 일어나는 첫번째 메소드
<code>render()</code> mounting 단계 후반에 일어나는 메서드. 컴포넌트를 최초로 렌더링할때 필요하며 updating 단계에서 컴포넌트를 재실행(re-render)한다. 
<code>componentDidMount()</code></p>
<ul>
<li>mounting 단계의 제일 마지막 메서드로 1) Constructor 2) render() 3)componentDidMount 순으로 실행된다.
즉, <strong>componentDidMount는 컴포넌트가 초기 렌더링된 후 실행된다!</strong></li>
<li>위의 예시에서 <code>setInterval()</code> 함수는 본인 컴포넌트 내에서 값이 업데이트 되는 경우이므로 componentDidMount에 쓴다.
❓ render()에 쓰면 안되나? 
=&gt; render()는 mounting 단계, updating 단계에서 두 번 실행되는 함수로 너무 자주 실행되 버그가 일어날 수 있다.
❓ 그럼 constructor()에 쓰면 안되나? </li>
<li><blockquote>
<p>mounting 단계에서 일어나는데 *단일 책임 원칙(Single Responsibility Principle(SRP))에 어긋나고, 버그를 일으킬 수 있다. </p>
</blockquote>
</li>
<li>*단일 책임 원칙: 객체 지향 프로그래밍에서 단일 책임 원칙(single responsibility principle)이란 모든 클래스는 하나의 책임만 가지며, 클래스는 그 책임을 완전히 캡슐화해야 함을 일컫는다.</li>
</ul>
<h3 id="updating-단계-메서드">Updating 단계 메서드</h3>
<p>props나 state를 변경하거나 컴포넌트에 props를 전달하는 경우 update를 유발한다. 
컴포넌트를 업데이트할 때 쓰는 메서드는 다양한데(<a href="https://reactjs.org/docs/react-component.html#updating">updating 메서드 종류</a>), 주로 <code>componentDidUpdate()</code>와 <code>render()</code>를 쓴다. 
<code>componentDidUpdate(prevProps, prevState, snapshot)</code> </p>
<ul>
<li>업데이트가 일어난 직후 실행되는 메서드로 조건에 맞게 써주어야 한다.<pre><code class="language-javascript">componentDidUpdate(prevProps) {
// Typical usage (don&#39;t forget to compare props):
if (this.props.userID !== prevProps.userID) {
  this.fetchData(this.props.userID);
}
}</code></pre>
<code>render()</code> 컴포넌트가 업데이트 될 때 render()가 다시 호출되면서 변경된 부분을 보여준다.</li>
</ul>
<h3 id="unmounting-단계-메서드">Unmounting 단계 메서드</h3>
<p><code>compoentWillUnmount()</code></p>
<ul>
<li>컴포넌트를 종료할 때 Unmounting 단계에서 쓴다. </li>
<li>컴포넌트를 제대로 종료하지 않으면 버그를 일으키거나 AJAX의 데이터를 많이 쓰고, DOM의 활동이 느려지는 등 많은 부작용이 발생할 수 있다.</li>
<li><code>setInterVal()</code>과 같이 별다른 조치가 없으면 계속 작동하는 함수는 <code>clearInterval()</code>로 제대로 끝내준다. 
(컴퓨터에 USB를 걍 뽑아버리는 것과 &#39;종료하기&#39; 처리 해주는 것의 차이가 아닐까..?)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React #6] .map()만 잘써도 React 좀 한다지(feat. key)]]></title>
            <link>https://velog.io/@kykim_dev/React-6-.map%EB%A7%8C-%EC%9E%98%EC%8D%A8%EB%8F%84-React-%EC%A2%80-%ED%95%9C%EB%8B%A4%EC%A7%80feat.-key</link>
            <guid>https://velog.io/@kykim_dev/React-6-.map%EB%A7%8C-%EC%9E%98%EC%8D%A8%EB%8F%84-React-%EC%A2%80-%ED%95%9C%EB%8B%A4%EC%A7%80feat.-key</guid>
            <pubDate>Fri, 08 Oct 2021 08:00:52 GMT</pubDate>
            <description><![CDATA[<h1 id="map-메서드">.map() 메서드</h1>
<ul>
<li>자바스크립트 배열 메서드인<code>.map()</code> 함수는 React에서도 코드를 효율적으로 구성하는 데 유용하게 사용할 수 있다.</li>
<li><strong>반복되는 컴포넌트를 렌더링하기 위해 자바스크립트의 내장 함수인 map()을 사용한다.</strong></li>
<li>map() 함수는 파라미터로 전달된 함수를 사용해, 배열 각 요소를 원하는 규칙에 따라 변환한 다음 새로운 배열을 생성한다. 
참조: <a href="https://velog.io/@kykim_dev/JavaScript-15-Basic-of-JavaScript-Array-methods">https://velog.io/@kykim_dev/JavaScript-15-Basic-of-JavaScript-Array-methods</a><pre><code class="language-javascript">const strings = [&#39;Home&#39;, &#39;shop&#39;, &#39;About Me&#39;]
</code></pre>
</li>
</ul>
<p>const listItems = strings.map(string =&gt; <li>{string}</li>);</p>
<ul>{listItems}</ul>         
```
- `strings` 배열에서 `.map()`을 호출하면, 새로운 `<li>` 배열을 생성한다.
- `<ul><ul/>`안에는 `.map()`에서 호출된 `<li></li>`목록이 담긴다.

<h1 id="keys">Keys</h1>
<ul>
<li>JSX 안에서 리스트를 생성할 때, 키(key)가 필요하다.</li>
<li><code>key</code>는 JSX 속성으로 HTML의 <code>id</code> 속성처럼 이 속성 값은 특정값으로 쓰인다.</li>
<li><code>key</code>는 *<em>렌더링 시 컴포넌트 배열에 어떤 변화가 일어났는지 더욱 빠르게 알아내기 위해 사용한다. *</em></li>
<li><code>key</code>는 화면상으로 보이는 변화는 없다. React는 내부적으로 리스트의 순서를 찾는데, key가 없다면 React가 list의 아이템들의 순서를 제대로 찾지 못할 수 있는 것이다.</li>
<li>모든 리스트에 key가 필요한 것은 아니다. key가 필요한 조건은 다음과 같다.
1) 리스트 아이템이 다음 순서로 렌더링 될 때 그 절차가 기록되어야 할 때. (투 두 리스트에서 완료된 항목은 저장되어야 한다) 
2) 리스트의 순서가 섞일 수 있을 때. (검색 기록은 다음 렌더링에서 섞일 수 있다)<pre><code class="language-javascript">import React from &#39;react&#39;;
import ReactDOM from &#39;react-dom&#39;;
</code></pre>
</li>
</ul>
<p>const people = [&#39;Rowe&#39;, &#39;Prevost&#39;, &#39;Gare&#39;];</p>
<p>const peopleLis = people.map((person, i) =&gt;  &lt;li key={&#39;person_&#39; + i}&gt;{person}</li>);</p>
<p>ReactDOM.render(<ul>{peopleLis}</ul>, document.getElementById(&#39;app&#39;));</p>
<p>```</p>
<h2 id="key-설정하기">Key 설정하기</h2>
<ul>
<li>map() 함수 인자로 전달되는 함수 내부에서 컴포넌트 props를 설정하는 것과 같다. 단, key 값은 유일한 값이어야 한다. </li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Practice] JavaScript로 Instagram 로그인 & 댓글 페이지 구현]]></title>
            <link>https://velog.io/@kykim_dev/Practice-JavaScript%EB%A1%9C-Instagram-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EB%8C%93%EA%B8%80-%ED%8E%98%EC%9D%B4%EC%A7%80-%EA%B5%AC%ED%98%84</link>
            <guid>https://velog.io/@kykim_dev/Practice-JavaScript%EB%A1%9C-Instagram-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EB%8C%93%EA%B8%80-%ED%8E%98%EC%9D%B4%EC%A7%80-%EA%B5%AC%ED%98%84</guid>
            <pubDate>Fri, 08 Oct 2021 02:54:06 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>HTML, CSS, JavaScript를 학습한 후 HTML, CSS를 활용해 로그인 페이지, SNS 피드 페이지 레이아웃을 잡고, JavaScript/DOM으로 로그인, 댓글 입력/삭제 기능을 구현해 보았다.  </p>
</blockquote>
<ul>
<li><a href="https://velog.io/@kykim_dev/CSS-8-CSS-Wrap-up">HTML&amp;CSS 인스타그램 Warm-up</a></li>
</ul>
<h1 id="javascript">JavaScript</h1>
<h2 id="로그인-페이지">로그인 페이지</h2>
<p><img src="https://images.velog.io/images/kykim_dev/post/aeecc45b-01a2-456e-ab5e-ef6f75b64bc8/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-10-08%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%2010.06.57.png" alt=""></p>
<h2 id="구현-기능">구현 기능</h2>
<p>1) 로그인 페이지 레이아웃 (HTML, CSS)
2) 아이디, 비밀번호 유효성 검사 기능
3) input값 입력시 버튼 스타일 변화
4) 로그인 시 main 페이지로 이동 </p>
<h2 id="코드-뜯어보기">코드 뜯어보기</h2>
<pre><code class="language-javascript">&quot;use strict&quot;;

const inputInfo = document.getElementsByClassName(&quot;inputGroup&quot;)[0];
const button = document.getElementById(&quot;inputButton&quot;);

function handleButton(buttonValid) {

    if(buttonValid) {
    button.disabled = false;        
    } else {
    button.disabled = true;
    }

    if (window.event.keyCode === 13) {
        goToMain();
    }
}

function checkId(value) {
    if (value.length &gt; 0) {
        return true;
    } else {
        return false;
    }
}

function checkPw(value) {
    if (value.length &gt; 0) {
        return true;
    } else {
        return false;
    }
}

function handleInput() {
    const userId = document.getElementById(&quot;userInfo&quot;).value;
    const userPw = document.getElementById(&quot;userPw&quot;).value;

    const isValidId = checkId(userId); //T/F를 받게 됨
    const isValidPw = checkPw(userPw);

    if (isValidId &amp;&amp; isValidPw) {
       handleButton(true);
    } else {
       handleButton(false);
    }
}

function goToMain() {
    alert(&quot;환영합니다!:)&quot;);
    location.replace(&#39;http://127.0.0.1:5500/main/main.html&#39;)
}

const init = () =&gt; {
    inputInfo.addEventListener(&quot;input&quot;, handleInput);
    button.addEventListener(&quot;click&quot;, goToMain);
};

init();</code></pre>
<h3 id="init-초기화-함수-필수-x">init() 초기화 함수 (필수 x)</h3>
<ul>
<li>자바스크립트의 entry point(시작점)를 쉽게 찾기 위해 설정한다.</li>
<li>브라우저에게 명료하게 전달할 수 있는 장점이 있다.</li>
</ul>
<h3 id="documentgetelementsbyclassnameinputgroup0-인덱스로-접근하는-이유">document.getElementsByClassName(&quot;inputGroup&quot;)[0] 인덱스로 접근하는 이유</h3>
<ul>
<li><code>getElementsByClassName</code>은 class name이 같은 중복 값을 가져올 수 있다.
/cf/ <code>getElementById</code>에서 Id는 한 개만 존재하며, DOM 요소를 가져온다. </li>
<li>또한, (유사)배열 형태로 값을 가져온다. 요소가 1개 뿐이라고 할지라도 배열 안에 담긴다.
=&gt; 따라서 인덱스로 접근해야 원하는 요소에 정확하게 접근할 수 있다. </li>
</ul>
<h3 id="queryselector-vs-getelementbyclassname">querySelector vs getElementByClassName</h3>
<ul>
<li><code>getElementBy~</code>는 필요한 요소를 앱 브라우저에서 전체에서 찾는다. (Id는 ID로, TagName은 tag name으로..)</li>
<li>&#39;query: 질의, 질문&#39;이라는 뜻으로 *<em>여러 조건을 걸어서 물어볼 수 있다. *</em> (브라우저 전체에서 찾는 것이 아닌 내가 물어보는 조건에 맞는!)
<code>document.querySelector(&quot;#main &gt; .loginBtn &gt; li&quot;)</code></li>
<li>또한, querySelector를 쓰는 것이 메모리 효율에도 좋아 querySelector, querySelectorAll을 쓰는 것을 추천!
/cf/ 참조:  <a href="https://guinatal.github.io/queryselector-vs-getelementbyid/">https://guinatal.github.io/queryselector-vs-getelementbyid/</a></li>
</ul>
<h3 id="event-handler-위임">Event handler 위임</h3>
<p><code>inputInfo.addEventListener(&quot;input&quot;, handleInput);</code>
input 요소가 2개(아이디, 비밀번호)인데, 이벤트 핸들러는 하나 뿐이다.</p>
<ul>
<li>이는 두 개의 태그를 감싸는 요소(form tag)에 &#39;이벤트를 위임&#39;한 것</li>
<li>이벤트 플로우는 전 포스팅 참조
<a href="https://velog.io/@kykim_dev/JavaScript-19-Basic-of-JavaScript-DOM-Event-Flow">https://velog.io/@kykim_dev/JavaScript-19-Basic-of-JavaScript-DOM-Event-Flow</a>
<a href="https://joshua1988.github.io/web-development/javascript/event-propagation-delegation/">https://joshua1988.github.io/web-development/javascript/event-propagation-delegation/</a></li>
</ul>
<h3 id="3항-연산자-사용">3항 연산자 사용</h3>
<ul>
<li>코드를 효율적으로 구성하기 위해서는 3항 연산자를 적극 활용하는 것이 좋다. 코드 길이를 확 줄여준다. 
<code>button.disabled = !buttonValid [? true : false;]</code> ([]생략 가능)</li>
</ul>
<h3 id="windoweventkeycode--13">window.event.keyCode === 13</h3>
<ul>
<li>마우스 클릭 외 enter키를 눌렀을 때 실행되는 이벤트로 13은 enter키 코드를 뜻한다.</li>
</ul>
<h3 id="event-handler-함수에-를-안쓰는-이유">event handler 함수에 ()를 안쓰는 이유</h3>
<p><code>inputInfo.addEventListener(&quot;input&quot;, handleInput함수명);</code></p>
<ul>
<li>eventListener 인자로 함수를 넘길 때 <code>handleInput()</code>이 아닌 <code>handleInput</code>과 같이 함수명만 넘긴다. </li>
<li>함수명에 <code>()</code>를 붙이면 함수를 호출하게 된다. 따라서 이벤트 핸들러 함수에 ()를 붙이면 이벤트가 실행되기도 전에 해당 함수가 바로 실행되어 버린다. </li>
</ul>
<hr>
<h2 id="메인-페이지">메인 페이지</h2>
<p><img src="https://images.velog.io/images/kykim_dev/post/7860f801-ee13-46af-8505-728d26eb88c3/%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-10-08%20%E1%84%8B%E1%85%A9%E1%84%8C%E1%85%A5%E1%86%AB%2011.37.43.png" alt=""></p>
<h2 id="구현-기능-1">구현 기능</h2>
<p>1) 메인 페이지(navigation bar, feed, recommendation, footer) 레이아웃 (HTML, CSS)
2) 댓글 입력/게시 기능
3) 댓글 삭제 기능 </p>
<h2 id="코드-뜯어보기-1">코드 뜯어보기</h2>
<pre><code class="language-javascript">&quot;use strict&quot;;

const replyInput = document.getElementById(&quot;replyInput&quot;);
const replyButton = document.getElementById(&quot;replyButton&quot;);

function addReply(value) {
    const replyList = document.getElementById(&quot;#replyList&quot;);
    const newLists = document.createElement(&quot;li&quot;);
    const newContent = `&lt;span class=&quot;name&quot;&gt;kayoung&lt;/sapn&gt;
    &lt;span class=&quot;description&quot;&gt;${value}&lt;/span&gt;
    &lt;span class=&quot;delete&quot;&gt;x&lt;/span&gt;`

    newLists.innerHTML = newContent;
    deleteEvent(newLists);
    replyList.appendChild(newLists);

    replyInput.value = &quot;&quot;;
}

function deleteEvent(newLists) {
    const deletebutton = document.querySelector(&quot;.delete&quot;);
    deletebutton.addEventListener(&quot;click&quot;, () =&gt; newLists.remove())
}

function validateReply() {
    const replyValue = replyInput.value;
    if(replyValue.length &gt; 0) {  
        addReply(replyValue);
    } else {
        alert(&quot;댓글을 입력해주세요.&quot;)
    }
}

function init() {
    replyButton.addEventListener(&quot;click&quot;, validateReply);
}

init();
</code></pre>
<h3 id="template-literal">Template Literal</h3>
<ul>
<li><p>템플릿 리터럴을 사용하면 element를 효율적으로 만들 수 있다. </p>
</li>
<li><p>Template Literal을 사용하지 않고 구성한 예 (가독성 측면에서는 사용하지 않는 것이 더 좋을 수 있다.)</p>
<pre><code class="language-javascript">function addReply(replyValue) {
  const newReply = document.createElement(&quot;li&quot;);
  const newName = document.createElement(&quot;span&quot;);
  const newContent = document.createElement(&quot;span&quot;);
  const deleteBtn = document.createElement(&quot;span&quot;);

  newName.classList.add(&quot;name&quot;);
  deleteBtn.classList.add(&quot;delete&quot;);
  newName.innerText = &quot;kayoung&quot;;
  newContent.innerText = replyValue;
  deleteBtn.innerText = &quot;x&quot;;

  deleteBtn.addEventListener(&quot;click&quot;, () =&gt; newReply.remove());

  newReply.appendChild(newName);
  newReply.appendChild(newContent);
  newReply.appendChild(deleteBtn);
  replyList.appendChild(newReply);

  replyInput.value=&quot;&quot;;
}</code></pre>
<h3 id="locationreplace-페이지-이동">location.replace() 페이지 이동</h3>
</li>
<li><p>페이지를 이동할 때 <code>location.replace()</code> 또는 <code>location.href</code>를 쓴다. </p>
</li>
<li><p>location.href는 객체 속성, loaction.replace()는 메서드(함수)로 작동된다.</p>
</li>
<li><p>href는 페이지를 이동하는 것이기 때문에 뒤로가기 버튼을 누른경우 이전 페이지로 이동이 가능하지만,</p>
</li>
<li><p>replace는 현재 페이지를 새로운 페이지로 덮어 씌우기 때문에 이전 페이지로 이동이 불가능하다.
출처: 생활코딩 <a href="https://opentutorials.org/module/2919/22904">https://opentutorials.org/module/2919/22904</a></p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Algorithm #4] 공통 시작 단어 반환하기(feat. substring)]]></title>
            <link>https://velog.io/@kykim_dev/Algorithm-4-%EA%B3%B5%ED%86%B5-%EC%8B%9C%EC%9E%91-%EB%8B%A8%EC%96%B4-%EB%B0%98%ED%99%98%ED%95%98%EA%B8%B0feat.-substring</link>
            <guid>https://velog.io/@kykim_dev/Algorithm-4-%EA%B3%B5%ED%86%B5-%EC%8B%9C%EC%9E%91-%EB%8B%A8%EC%96%B4-%EB%B0%98%ED%99%98%ED%95%98%EA%B8%B0feat.-substring</guid>
            <pubDate>Mon, 04 Oct 2021 12:31:28 GMT</pubDate>
            <description><![CDATA[<h2 id="assignment">Assignment</h2>
<p>strs은 단어가 담긴 배열입니다. 공통된 시작 단어(prefix)를 반환해주세요.</p>
<p>예를 들어 strs = [&#39;start&#39;, &#39;stair&#39;, &#39;step&#39;] return은 &#39;st&#39;
strs = [&#39;start&#39;, &#39;wework&#39;, &#39;today&#39;] return은 &#39;&#39;</p>
<h2 id="문제-쪼개기">문제 쪼개기</h2>
<ol>
<li>strs 인자로 받는 getPrefix 함수를 만든다.</li>
<li>비교하는 시작값을 설정한다. strs[0]</li>
<li>인자 길이가 0이면 &#39;&#39; return</li>
<li>strs를 전부 돌면서 모든 인덱스 값을 시작값과 비교해 공통된 단어가 있는지 보고, </li>
<li>있다면 공통된 부분만 잘라서 return</li>
</ol>
<h2 id="문제-해결">문제 해결</h2>
<pre><code class="language-javascript">const getPrefix = strs =&gt; {
  let defaultWord = strs[0];
  if (strs.length === 0) {
    return &#39;&#39;;
  }

  //defaultWord를 모든 인덱스와 비교
  for (let i = 0; i &lt; strs.length; i++) {
    while (strs[i].indexOf(defaultWord) !== 0) {
      defaultWord = defaultWord.substring(0, defaultWord.length -1);
    }
  }
  return defaultWord;
}</code></pre>
<hr>
<h2 id="substring">substring()</h2>
<ul>
<li>인수로 준 문자열의 부분을 반환하는 함수 </li>
<li>slice()와 같다.
<code>string.substring(start[, end])</code></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Algorithm #3] 숫자 반전 비교하기(feat. toString)]]></title>
            <link>https://velog.io/@kykim_dev/Algorithm-3-%EC%88%AB%EC%9E%90-%EB%B0%98%EC%A0%84-%EB%B9%84%EA%B5%90%ED%95%98%EA%B8%B0feat.-toString</link>
            <guid>https://velog.io/@kykim_dev/Algorithm-3-%EC%88%AB%EC%9E%90-%EB%B0%98%EC%A0%84-%EB%B9%84%EA%B5%90%ED%95%98%EA%B8%B0feat.-toString</guid>
            <pubDate>Mon, 04 Oct 2021 12:21:08 GMT</pubDate>
            <description><![CDATA[<h2 id="assginment">Assginment</h2>
<p>숫자인 num을 인자로 넘겨주면, 뒤집은 모양이 num과 똑같은지 여부를 반환해주세요.</p>
<p>num: 숫자 return: true or false (뒤집은 모양이 num와 똑같은지 여부)</p>
<p>예를 들어, num = 123 return false =&gt; 뒤집은 모양이 321 이기 때문
num = 1221 return true =&gt; 반전 1221
num = -121 return false =&gt; 반전 121- 
num = 10 return false =&gt; 반전 01</p>
<h2 id="문제-쪼개기">문제 쪼개기</h2>
<ol>
<li>num을 인자로 받는 sameReverse 함수를 만든다.</li>
<li>인자 num을 string으로 바꾼다. =&gt;defaultNum</li>
<li>reverse()한 값을 저장한다. =&gt; reversed</li>
<li>만약 defaultNum과 reversed가 같다면 return true</li>
<li>다르다면 return false</li>
</ol>
<h2 id="문제-해결">문제 해결</h2>
<pre><code class="language-javascript">const sameReverse = num =&gt; {
  let defaultNum = num.toString();
  let reversed = defaultNum.split(&quot;&quot;).reverse().join(&quot;&quot;);

  if (defaultNum === reversed) {
    return true;
  } else {
    return false;
  }
}</code></pre>
<hr>
<h2 id="objectarraynumberdateprototypetostring">Object/Array/Number/Date....prototype.toString()</h2>
<ul>
<li>객체가 갖고 있는 정보나 값들을 문자열로 리턴한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Algorithm #2] 숫자 뒤집어 반환(feat.  dynamic array methods)]]></title>
            <link>https://velog.io/@kykim_dev/Algorithm-2-%EC%88%AB%EC%9E%90-%EB%92%A4%EC%A7%91%EC%96%B4-%EB%B0%98%ED%99%98feat.-dynamic-array-methods</link>
            <guid>https://velog.io/@kykim_dev/Algorithm-2-%EC%88%AB%EC%9E%90-%EB%92%A4%EC%A7%91%EC%96%B4-%EB%B0%98%ED%99%98feat.-dynamic-array-methods</guid>
            <pubDate>Mon, 04 Oct 2021 12:08:36 GMT</pubDate>
            <description><![CDATA[<h2 id="assignment">Assignment</h2>
<p>reverse 함수에 정수인 숫자를 인자로 받습니다. 그 숫자를 뒤집어서 return해주세요.</p>
<ul>
<li>x: 숫자 return: 뒤집어진 숫자를 반환!</li>
</ul>
<p>예들 들어, x: 1234 return: 4321
x: -1234 return: -4321
x: 1230 return: 321</p>
<h2 id="문제-쪼개기">문제 쪼개기</h2>
<ol>
<li>x를 인자로 받는 reverse 함수를 만든다.</li>
<li>x 인자를 배열로 바꿔준다. 왜? 인덱스로 접근해서 뒤집어주려고</li>
<li>배열 인덱스 순서를 바꿔준다. =&gt; numArray.reverse()</li>
<li>조건1) 마지막 숫자에 -를 붙여준다. (그냥 &#39;-&#39;만 붙이면 NaN 처리됨)</li>
<li>조건2) 숫자 맨 앞의 0은 삭제한다.</li>
<li>배열을 Number로 바꿔서 return 해준다.</li>
</ol>
<h2 id="문제-해결">문제 해결</h2>
<pre><code class="language-javascript">const reverse = x =&gt; {
  let numArray = (x + &#39;&#39;).split(&#39;&#39;).reverse();
  if (numArray.includes(&#39;-&#39;)) {
    numArray.pop();
    numArray.unshift(&#39;-&#39;);
  }
  if (numArray.includes(0)) {
   numArray.shift(0);
  }
  return Number(numArray.join(&#39;&#39;))
}</code></pre>
<hr>
<h2 id="stringprototypesplit">String.prototype.split()</h2>
<ul>
<li>String 객체를 지정한 구분자를 이용하여 여러 개의 문자열로 나누어주는 메서드
<code>str.split([seperator[, limit]])</code></li>
<li>문자열에서 <code>seperator</code> 부분은 삭제되고 남은 문자열이 <strong>배열로 반환</strong>된다. </li>
<li><code>limit</code>을 지정하면 끊는 횟수를 제한할 수 있다.<pre><code class="language-javascript">var myString = &#39;Hello World. How are you doing?&#39;;
var splits = myString.split(&#39;&#39;,3);
//문자열을 공백으로 끊고 처음 3개의 문자열을 반환
console.log(splits); //[&quot;Hello&quot;, &quot;World.&quot;, &quot;How&quot;]</code></pre>
<h2 id="arrayprototypereverse">Array.prototype.reverse()</h2>
</li>
<li>배열의 순서를 반전하는 메서드</li>
<li>split()과 함께 쓸 수 있다. <code>myString.split().reverse()</code></li>
</ul>
<h2 id="arrayprototypepopunshiftshift">Array.prototype.pop()/unshift()/shift()</h2>
<ul>
<li><a href="https://velog.io/@kykim_dev/JavaScript-7-Basic-of-JavaScript-%EB%B0%B0%EC%97%B4Array">https://velog.io/@kykim_dev/JavaScript-7-Basic-of-JavaScript-%EB%B0%B0%EC%97%B4Array</a> 참조</li>
<li>pop(): 배열의 마지막 요소 제거, 마지막 요소의 값 반환</li>
<li>unshift(): 배열의 맨 앞 부분에 요소 추가</li>
<li>shift(): 배열의 첫 번째 요소 제거, 제거된 요소 반환 </li>
</ul>
<h2 id="arraystringprototypeinclude">Array/String.prototype.include()</h2>
<ul>
<li>배열/문자열이 특정 요소를 포함하고 있는지 판별한다.</li>
<li>반환 값은 Boolean (T/F)
<code>arr/str.includes(valueToFind[, fromIndex])</code></li>
</ul>
<h2 id="cf-arraystringprototypeindexof">cf. Array/String.prototype.indexOf</h2>
<ul>
<li>호출한 배열/string 객체에서 주어진 값과 일치하는 첫 번째 인덱스를 반환한다. 일치하지 않으면 -1을 반환한다. 
<code>arr/str.indexOf(searchValue[, fromIndex])</code><h2 id="arrayprototypejoin">Array.prototype.join()</h2>
</li>
<li><strong>배열의 모든 요소를 연결해</strong> <strong>하나의 문자열로 만들어주는</strong> 메서드 
<code>arr.join([seperator])</code><pre><code class="language-javascript">const elements = [&quot;Fire&quot;, &quot;Air&quot;, &quot;Water&quot;];
</code></pre>
</li>
</ul>
<p>console.log(elements.join());
// &quot;Fire,Air,Water&quot;
console.log(elements.join(&quot;&quot;));
// &quot;FireAirWater&quot;
console.log(elements.join(&quot;-&quot;));
// &quot;Fire-Air-Water&quot;</p>
<p>```</p>
<h2 id="number">Number()</h2>
<ul>
<li>Number 객체를 생성하는 생성자 함수</li>
<li><a href="https://velog.io/@kykim_dev/JavaScript-14-Basic-of-JavaScript-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-class">https://velog.io/@kykim_dev/JavaScript-14-Basic-of-JavaScript-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-class</a> 참조</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>