<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Code Playground</title>
    <link>https://im-developer.tistory.com/</link>
    <description>비바리퍼블리카(토스)에서 Frontend Developer로 일하고 있습니다.</description>
    <language>ko</language>
    <pubDate>Sun, 14 Jun 2026 03:32:49 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>제이JY</managingEditor>
    <image>
      <title>Code Playground</title>
      <url>https://tistory1.daumcdn.net/tistory/3048153/attach/bd49595eba944c6595e554fd6478b0e0</url>
      <link>https://im-developer.tistory.com</link>
    </image>
    <item>
      <title>[리액트 디자인 패턴] Compound Component Pattern (합성 컴포넌트 패턴) 알아보기</title>
      <link>https://im-developer.tistory.com/238</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;667&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZ0iE1/btrH1a8B7LF/dwIZMhOk1jguw5DdB0blCK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZ0iE1/btrH1a8B7LF/dwIZMhOk1jguw5DdB0blCK/img.jpg&quot; data-alt=&quot;https://unsplash.com/photos/QiWOG1MA9rE&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZ0iE1/btrH1a8B7LF/dwIZMhOk1jguw5DdB0blCK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZ0iE1%2FbtrH1a8B7LF%2FdwIZMhOk1jguw5DdB0blCK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;334&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;667&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://unsplash.com/photos/QiWOG1MA9rE&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글은 &lt;b&gt;Compound Pattern&lt;/b&gt;과 관련된 글의 한글 번역입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;원문 링크: &lt;/b&gt;&lt;a href=&quot;https://www.patterns.dev/posts/compound-pattern/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.patterns.dev/posts/compound-pattern/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1658659831906&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Compound Pattern&quot; data-og-description=&quot;Create multiple components that work together to perform a single task&quot; data-og-host=&quot;www.patterns.dev&quot; data-og-source-url=&quot;https://www.patterns.dev/posts/compound-pattern/&quot; data-og-url=&quot;https://www.patterns.dev/posts/compound-pattern/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dB0cUS/hyPb1a1KGL/qGVy2WvplLtAGEBN9wqh40/img.jpg?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080&quot;&gt;&lt;a href=&quot;https://www.patterns.dev/posts/compound-pattern/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.patterns.dev/posts/compound-pattern/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dB0cUS/hyPb1a1KGL/qGVy2WvplLtAGEBN9wqh40/img.jpg?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Compound Pattern&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Create multiple components that work together to perform a single task&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.patterns.dev&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;b&gt;Compound Pattern&lt;/b&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어플리케이션에서 우리는 종종 서로에게 속한 컴포넌트를 가지게 된다. 이 컴포넌트들은 공통의 상태를 통해 서로에게 의존되어 있고, 로직을 함께 공유한다. 여러분은 종종 select, dropdown 컴포넌트 또는 메뉴 아이템들에서 이러한 형태를 볼 수 있다. &lt;b&gt;Compound component pattern&lt;/b&gt;은 task를 수행하기 위해 다 같이 함께 작동하는 컴포넌트를 생성할 수 있도록 해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Context API&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 예제를 살펴보자: 다람쥐 이미지 목록이 있다! 단순히 다람쥐 이미지들을 보여주는 것 외에, 유저가 이미지를 수정하거나 삭제할 수 있도록 버튼을 추가하려고 한다. 우리는 유저가 컴포넌트를 토글했을 때, 목록을 보여주는 &lt;b&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;FlyOut&lt;/span&gt;&lt;/b&gt; 컴포넌트를 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;iframe src=&quot;https://codesandbox.io/embed/provider-pattern-2-ck29r?fontsize=14&amp;amp;hidenavigation=1&amp;amp;theme=dark&quot;&gt;&lt;/iframe&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FlyOut 컴포넌트는 내부에 기본적으로 다음 3가지를 가지고 있다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;토글 버튼과 List를 가지고 있는 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;FlyOut&lt;/b&gt;&lt;/span&gt; wrapper&lt;/li&gt;
&lt;li&gt;List를 토글하는 &lt;b&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;Toggle&lt;/span&gt;&lt;/b&gt; 버튼&lt;/li&gt;
&lt;li&gt;메뉴 아이템을 가지고 있는 &lt;b&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;List&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Compound component pattern을 리액트의 &lt;b&gt;&lt;a href=&quot;https://reactjs.org/docs/context.html&quot;&gt;Context API&lt;/a&gt;&lt;/b&gt;와 사용하는 것은 이 예제에 완벽하게 들어맞는다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저, FlyOut 컴포넌트를 생성해보자. 이 컴포넌트는 상태를 가지고 있고, 모든 children에게 전달 할 toggle value를 가지고 있는 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;FlyOutProvider&lt;/b&gt;&lt;/span&gt;를 리턴한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const FlyOutContext = createContext();

function FlyOut(props) {
  const [open, toggle] = useState(false);

  const providerValue = { open, toggle };

  return (
    &amp;lt;FlyOutContext.Provider value={providerValue}&amp;gt;
      {props.children}
    &amp;lt;/FlyOutContext.Provider&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 이제 자식에게 &lt;b&gt;open&lt;/b&gt;과 &lt;b&gt;toggle&lt;/b&gt;이라는 value를 전달할 수 있는 stateful한 FlyOut 컴포넌트를 가지게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;Toggle&lt;/b&gt;&lt;/span&gt; 컴포넌트를 만들어보자. 이 컴포넌트는 단순히 유저가 메뉴를 토글하기 위해 클릭할 수 있는 컴포넌트를 렌더한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function Toggle() {
  const { open, toggle } = useContext(FlyOutContext);

  return (
    &amp;lt;div onClick={() =&amp;gt; toggle(!open)}&amp;gt;
      &amp;lt;Icon /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;Toggle&lt;/b&gt;&lt;/span&gt;이 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;FlyOutContext&lt;/b&gt;&lt;/span&gt; provider에 실제로 접근할 수 있도록 만들려면, 이 컴포넌트를 FlyOut의 자식 컴포넌트로 렌더해야 한다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 단순히 자식 컴포넌트로 렌더할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 Toggle 컴포넌트를 FlyOut 컴포넌트의 속성으로 만들 수도 있다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const FlyOutContext = createContext();

function FlyOut(props) {
  const [open, toggle] = useState(false);

  return (
    &amp;lt;FlyOutContext.Provider value={{ open, toggle }}&amp;gt;
      {props.children}
    &amp;lt;/FlyOutContext.Provider&amp;gt;
  );
}

function Toggle() {
  const { open, toggle } = useContext(FlyOutContext);

  return (
    &amp;lt;div onClick={() =&amp;gt; toggle(!open)}&amp;gt;
      &amp;lt;Icon /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

FlyOut.Toggle = Toggle;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 말인즉슨 만약 우리가 FlyOut을 어떤 파일이든 사용하려고 할 때, 우리는 오직 FlyOut만을 import하면 된다는 뜻이다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import React from &quot;react&quot;;
import { FlyOut } from &quot;./FlyOut&quot;;

export default function FlyoutMenu() {
  return (
    &amp;lt;FlyOut&amp;gt;
      &amp;lt;FlyOut.Toggle /&amp;gt;
    &amp;lt;/FlyOut&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;토글만 있는 것은 충분하지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;open&lt;/b&gt;이라는 value를 바탕으로 열리고 닫히며, list 아이템을 가진 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;List&lt;/b&gt;&lt;/span&gt; 또한 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;function List({ children }) {
  const { open } = React.useContext(FlyOutContext);
  return open &amp;amp;&amp;amp; &amp;lt;ul&amp;gt;{children}&amp;lt;/ul&amp;gt;;
}

function Item({ children }) {
  return &amp;lt;li&amp;gt;{children}&amp;lt;/li&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;List&lt;/b&gt;&lt;/span&gt; 컴포넌트는 &lt;b&gt;open&lt;/b&gt;이라는 값이 true인지 false인지에 따라 자식 컴포넌트를 렌더한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Toggle 컴포넌트에 했듯이 List와 Item 또한 FlyOut 컴포넌트의 속성으로 만들자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;const FlyOutContext = createContext();

function FlyOut(props) {
  const [open, toggle] = useState(false);

  return (
    &amp;lt;FlyOutContext.Provider value={{ open, toggle }}&amp;gt;
      {props.children}
    &amp;lt;/FlyOutContext.Provider&amp;gt;
  );
}

function Toggle() {
  const { open, toggle } = useContext(FlyOutContext);

  return (
    &amp;lt;div onClick={() =&amp;gt; toggle(!open)}&amp;gt;
      &amp;lt;Icon /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

function List({ children }) {
  const { open } = useContext(FlyOutContext);
  return open &amp;amp;&amp;amp; &amp;lt;ul&amp;gt;{children}&amp;lt;/ul&amp;gt;;
}

function Item({ children }) {
  return &amp;lt;li&amp;gt;{children}&amp;lt;/li&amp;gt;;
}

FlyOut.Toggle = Toggle;
FlyOut.List = List;
FlyOut.Item = Item;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 이것들을 FlyOut 컴포넌트의 속성으로 사용할 수 있다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예제에서 우리는 유저에게 두 가지 옵션을 주려고 한다: &lt;b&gt;Edit&lt;/b&gt;과 &lt;b&gt;Delete&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;FlyOut.List&lt;/b&gt;&lt;/span&gt;를 생성하여 두 개의 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;FlyOut.Item&lt;/b&gt;&lt;/span&gt; 컴포넌트를 렌더해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나는 &lt;b&gt;Edit&lt;/b&gt; 옵션을, 그리고 나머지 하나는 &lt;b&gt;Delete&lt;/b&gt; 옵션을 말이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import React from &quot;react&quot;;
import { FlyOut } from &quot;./FlyOut&quot;;

export default function FlyoutMenu() {
  return (
    &amp;lt;FlyOut&amp;gt;
      &amp;lt;FlyOut.Toggle /&amp;gt;
      &amp;lt;FlyOut.List&amp;gt;
        &amp;lt;FlyOut.Item&amp;gt;Edit&amp;lt;/FlyOut.Item&amp;gt;
        &amp;lt;FlyOut.Item&amp;gt;Delete&amp;lt;/FlyOut.Item&amp;gt;
      &amp;lt;/FlyOut.List&amp;gt;
    &amp;lt;/FlyOut&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완벽하다! 우리는 방금 어떠한 상태도 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;FlyOutMenu&lt;/b&gt;&lt;/span&gt;에 추가하지 않고, 전체 FlyOut 컴포넌트를 만들었다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Compound pattern은 여러분이 컴포넌트 라이브러리를 구축하는데 도움이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종종 이 패턴을 &lt;a href=&quot;https://react.semantic-ui.com/modules/dropdown/#types-dropdown&quot;&gt;Semantic UI&lt;/a&gt;와 같은 UI 라이브러리를 사용할 때 볼 수 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://reactjs.org/docs/react-api.html#reactchildrenmap&quot;&gt;React.Children.map&lt;/a&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 또한 Compound Component pattern을 해당 컴포넌트의 자식을 mapping하는 방식으로 적용할 수도 있다. 해당 컴포넌트를 추가적인 props와 함께&lt;b&gt; &lt;a href=&quot;https://reactjs.org/docs/react-api.html#cloneelement&quot;&gt;복제&lt;/a&gt;&lt;/b&gt;함으로써 &lt;b&gt;open&lt;/b&gt;과 &lt;b&gt;toggle&lt;/b&gt; 속성을 추가할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;export function FlyOut(props) {
  const [open, toggle] = React.useState(false);

  return (
    &amp;lt;div&amp;gt;
      {React.Children.map(props.children, child =&amp;gt;
        React.cloneElement(child, { open, toggle })
      )}
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 자식 컴포넌트는 복제되고, &lt;b&gt;open&lt;/b&gt;과 &lt;b&gt;toggle&lt;/b&gt; value를 전달받는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 전 예제에서와 같이 Context API를 사용하는 대신, 이 두 value를 이제 props를 통해 접근할 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;iframe src=&quot;https://codesandbox.io/embed/provider-pattern-2-j9l1k?fontsize=14&amp;amp;hidenavigation=1&amp;amp;theme=dark&quot;&gt;&lt;/iframe&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Compound 컴포넌트들은 내부적으로 상태를 다루며 몇몇 자식 컴포넌트들 사이에서만 공유한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Compound 컴포넌트를 사용하면 우리 자신의 별도 상태를 다루는 것에 대해서 걱정할 필요가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Compound 컴포넌트를 import할 때, 해당 컴포넌트에 필요한 자식 컴포넌트를 명시적으로 import할 필요도 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import { FlyOut } from &quot;./FlyOut&quot;;

export default function FlyoutMenu() {
  return (
    &amp;lt;FlyOut&amp;gt;
      &amp;lt;FlyOut.Toggle /&amp;gt;
      &amp;lt;FlyOut.List&amp;gt;
        &amp;lt;FlyOut.Item&amp;gt;Edit&amp;lt;/FlyOut.Item&amp;gt;
        &amp;lt;FlyOut.Item&amp;gt;Delete&amp;lt;/FlyOut.Item&amp;gt;
      &amp;lt;/FlyOut.List&amp;gt;
    &amp;lt;/FlyOut&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;value를 제공하기 위해 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;React.Children.map&lt;/b&gt;&lt;/span&gt; 을 사용하면 &lt;u&gt;컴포넌트 nesting에 제한이 생긴다&lt;/u&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오직 부모 컴포넌트의 직접적인 자식들만 &lt;b&gt;open&lt;/b&gt;과 &lt;b&gt;toggle&lt;/b&gt; props에 대한 접근성이 생기므로, 이 컴포넌트를 다른 어떠한 컴포넌트로도 감쌀 수 없게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;export default function FlyoutMenu() {
  return (
    &amp;lt;FlyOut&amp;gt;
      {/* This breaks */}
      &amp;lt;div&amp;gt;
        &amp;lt;FlyOut.Toggle /&amp;gt;
        &amp;lt;FlyOut.List&amp;gt;
          &amp;lt;FlyOut.Item&amp;gt;Edit&amp;lt;/FlyOut.Item&amp;gt;
          &amp;lt;FlyOut.Item&amp;gt;Delete&amp;lt;/FlyOut.Item&amp;gt;
        &amp;lt;/FlyOut.List&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/FlyOut&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;React.cloneElement&lt;/b&gt;&lt;/span&gt;를 사용하여 엘리먼트를 복제하는 것은 &lt;u&gt;&lt;b&gt;얕은 병합(shallow merge)&lt;/b&gt;을 수행&lt;/u&gt;한다. 이미 존재하는 props가 우리가 전달하는 새로운 props와 병합될 것이다. 만약 이미 존재하는 prop과 같은 이름의 prop을 React.cloneElement에 전달한다면 이 방식이 네이밍 충돌을 일으킬 수 있다. props가 얕에 병합됨에 따라, prop의 value는 우리가 전달한 최신 값으로 덮어씌워질 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;References&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;a href=&quot;https://reactjs.org/docs/render-props.html&quot;&gt;Render Props - React&lt;/a&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;a href=&quot;https://kentcdodds.com/blog/compound-components-with-react-hooks&quot;&gt;React Hooks: Compound Components - Kent C. Dodds&lt;/a&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Frontend</category>
      <category>디자인패턴</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/238</guid>
      <comments>https://im-developer.tistory.com/238#entry238comment</comments>
      <pubDate>Sun, 24 Jul 2022 20:10:54 +0900</pubDate>
    </item>
    <item>
      <title>[Next.js Learn 문서 번역 #6] How Next.js Works - Client &amp;amp; Server 렌더링 방식, CDN과 Edge</title>
      <link>https://im-developer.tistory.com/236</link>
      <description>&lt;div class=&quot;info_post&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;Next.js Learn 문서 일부분에 대한 한글 번역입니다.&lt;/span&gt;&lt;/div&gt;
&lt;div class=&quot;article_view&quot;&gt;
&lt;div class=&quot;tt_article_useless_p_margin contents_style&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;[원문 링크]&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://nextjs.org/learn/foundations/how-nextjs-works/client-and-server&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://nextjs.org/learn/foundations/how-nextjs-works/client-and-server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nextjs.org/learn/foundations/how-nextjs-works/rendering&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://nextjs.org/learn/foundations/how-nextjs-works/rendering&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nextjs.org/learn/foundations/how-nextjs-works/cdns-and-edge&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://nextjs.org/learn/foundations/how-nextjs-works/cdns-and-edge&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Client and Server&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 어플리케이션 맥락에서 &lt;b&gt;client&lt;/b&gt;는 어플리케이션 코드를 위해 서버에 요청을 보내는 유저 디바이스의 브라우저를 지칭한다. 요청을 보내고 나면 서버로부터 받은 응답을 유저가 상호작용할 수 있는 인터페이스로 전환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cgyWmB/btrGktuIDdH/Q3RC6W3MBoU5PBZkC1R8J0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cgyWmB/btrGktuIDdH/Q3RC6W3MBoU5PBZkC1R8J0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cgyWmB/btrGktuIDdH/Q3RC6W3MBoU5PBZkC1R8J0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcgyWmB%2FbtrGktuIDdH%2FQ3RC6W3MBoU5PBZkC1R8J0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1360&quot; height=&quot;512&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Server&lt;/b&gt;는 어플리케이션 코드를 저장하는 컴퓨터의 데이터 센터를 지칭하며, client로부터 요청을 받고, 계산하며, 요청에 대한 적절한 응답을 보낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;True or false&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버는&amp;nbsp;어플리케이션&amp;nbsp;코드를&amp;nbsp;저장할&amp;nbsp;수&amp;nbsp;있고&amp;nbsp;유저&amp;nbsp;요청에&amp;nbsp;응답한다.&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;True&lt;/b&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;What is Rendering?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트로 작성된 코드를 여러분 UI의 HTML로 변환하기 위해서 피할 수 없는 작업 단위가 하나 존재한다. 이 프로세스를 &lt;b&gt;rendering&lt;/b&gt;이라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rendering은 server나 client에서 일어날 수 있다. 또한 빌드 타임에서 생각보다 빨리 일어날 수도 있고 런타임에서 매 요청 시마다 발생할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js에서는 3가지 렌더링 메소드를 사용할 수 있다: &lt;b&gt;Server-Side Rendering&lt;/b&gt;,&amp;nbsp;&lt;b&gt;Static Site Generation&lt;/b&gt;, 그리고&amp;nbsp;&lt;b&gt;Client-Side Rendering&lt;/b&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Pre-Rendering&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Server-Side Rendering과 Static Site Generation은 또한 &lt;b&gt;Pre-Rendering&lt;/b&gt;으로 지칭될 수 있는데, 그 이유는 외부 데이터를 불러오는 것과 리액트 컴포넌트를 HTML로 변환하는 것이 결과물이 client에 전송되기 전에 일어나기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Client-Side Rendering vs. Pre-Rendering&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;표준 리액트 어플리케이션에서 브라우저는 서버로부터 빈 HTML 껍데기와 UI를 구축할 수 있는 JavaScript로 된 지시사항들을 받게 된다. 이 것을 &lt;b&gt;client-side rendering&lt;/b&gt;이라고 부르는데 이유는 초기 렌더링이 유저 디바이스에서 일어나기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;668&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/B6vPZ/btrGlUx3SEC/8YzV1Htl7qUbTNTzzQ81A1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/B6vPZ/btrGlUx3SEC/8YzV1Htl7qUbTNTzzQ81A1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/B6vPZ/btrGlUx3SEC/8YzV1Htl7qUbTNTzzQ81A1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FB6vPZ%2FbtrGlUx3SEC%2F8YzV1Htl7qUbTNTzzQ81A1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1360&quot; height=&quot;668&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;668&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Note: 여러분은 React의 useEffect()나 useSWR같은 훅과 함께 데이터를 불러오기로 결정함으로써 Next.js 어플리케이션에서 특정 컴포넌트를 위해 client-side rendering을 하기로 선택할 수도 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이에 반해, Next.js는 기본적으로 모든 페이지를 &lt;b&gt;pre-render&lt;/b&gt;한다. Pre-rendering은 HTML이 유저 디바이스에서 JavaScript로 생성되는 것이 아니라 서버에서 먼저 생성된다는 것을 뜻한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로, 완전히 client-side에서 렌더되는 앱에서는 유저가 렌더링 작업이 끝나기 전까지는 빈 화면을 보게 된다는 뜻이다. pre-render되는 앱에서는 유저가 이미 구조화된 HTML을 보게 되는 것과 달리 말이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;668&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfhaBl/btrGpCYgNRN/xjQZg86RXJ7pE4MPt9mjQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfhaBl/btrGpCYgNRN/xjQZg86RXJ7pE4MPt9mjQk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfhaBl/btrGpCYgNRN/xjQZg86RXJ7pE4MPt9mjQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfhaBl%2FbtrGpCYgNRN%2FxjQZg86RXJ7pE4MPt9mjQk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1360&quot; height=&quot;668&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;668&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pre-rendering의 두 가지 타입에 대해 얘기해보자:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Server-Side Rendering&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 사이드 렌더링(server-side rendering)에서는, 페이지 HTML이 각 요청마다 서버에서 생성된다. 생성된 HTML, JSON 데이터, 그리고 페이지를 인터랙티브하게 만들어주는 자바스크립트 지시사항들은 클라이언트로 전송된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트에서는 React에서 JSON 데이터와 자바스크립트 코드를 사용하여 컴포넌트를 인터랙티브하게 만드는 동안 (예를 들어서, 버튼에 이벤트 핸들러를 붙이는 것 등) 빠르게 인터랙티브하지 않은 페이지를 보여주는데 HTML이 사용된다. 이러한 프로세스를 &lt;b&gt;hydration&lt;/b&gt;이라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js에서는 &lt;a href=&quot;https://nextjs.org/docs/basic-features/data-fetching/get-server-side-props&quot;&gt;getServerSideProps&lt;/a&gt;를 사용하여 서버 사이드에서 페이지를 렌더할 지 선택할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노트: 리액트 18과 Next 12에서 리액트 서버 컴포넌트의 알파 버전을 소개했다. 서버 컴포넌트는 완전히 서버에서 렌더되며 렌더를 위한 클라이언트 사이드 자바스크립트를 필요로 하지 않는다. 게다가, 서버 컴포넌트는 개발자가 특정 로직을 서버에 두고 클라이언트에는 해당 로직의 결과만을 보내는 것을 허용한다. 이 방식은 클라이언트에 보내는 번들 사이즈를 줄여서 클라이언트 사이드 렌더링 성능을 향상시킨다. 리액트 서버 컴포넌트에 대해 여기서 더 공부해보자.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Static Site Generation&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 사이드 생성(Static Site Generation)에서는 HTML이 서버에서 생성되지만, 서버 사이드 렌더링과는 다르게 런타임에는 서버를 사용하지 않는다. 대신에 어플리케이션이 배포될 때 컨텐츠가 빌드 타임에 한 번만 생성되며, HTML이 &lt;a href=&quot;https://nextjs.org/learn/foundations/how-nextjs-works/cdns-and-edge&quot;&gt;CDN&lt;/a&gt;에 저장되어 매 요청 시마다 재사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Quick Review&lt;/b&gt;:&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단일 Next.js 어플리케이션에서 다수의 렌더링 메소드를 가질 수 있나요?&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;네 - 여러분은 페이지 레벨에서 렌더링 메소드를 결정할 수 있습니다.&lt;/li&gt;
&lt;li&gt;아니오 - 여러분은 처음 한 번만 어플리케이션 렌더링 메소드를 선택할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네 - 여러분은 페이지 레벨에서 렌더링 메소드를 결정할 수 있습니다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;What is the Network?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어플리케이션 코드가 네트워크에 배포되고 나면 어디에 저장되고 어디서 실행되는지 알아두면 도움이 될 것이다. 네트워크는 리소스를 공유하는 연결된 컴퓨터 (또는 서버)라고 생각할 수 있다. Next.js 어플리케이션의 경우, 여러분의 어플리케이션 코드는 오리진 서버(&lt;b&gt;origin servers&lt;/b&gt;), &lt;b&gt;Content Delivery Networks (CDNs)&lt;/b&gt;, 그리고 엣지(&lt;b&gt;the Edge&lt;/b&gt;)에 분산될 수 있다. 이 것들이 각각 어떤 것인지 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Origin Servers&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 말했듯이, 서버는 어플리케이션 코드의 원본 버전을 저장하고 실행하는 메인 컴퓨터를 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;**오리진(origin)**이라는 용어는 &lt;b&gt;CDN 서버&lt;/b&gt;나 &lt;b&gt;엣지 서버&lt;/b&gt;와 같이 어플리케이션 코드가 배포될 수 있는 다른 장소와 구분하기 위해 붙여준 이름이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오리진 서버가 요청을 받으면, 응답을 받기 전에 연산을 수행한다. 연산 작업의 결과는 CDN(Content Delivery Network)으로 옮겨질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Content Delivery Network&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CDN은 (HTML이나 이미지 파일과 같은) 정적 컨텐츠를 전세계의 여러 장소에 저장하며, 클라이언트와 오리진 서버 사이에 존재한다. 새 요청이 들어오면, 유저로부터 가장 가까운 CDN이 캐싱된 결과와 함께 응답한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;708&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dp861C/btrGitI0aW6/WftqnjfK7iWG9NzNknWAo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dp861C/btrGitI0aW6/WftqnjfK7iWG9NzNknWAo1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dp861C/btrGitI0aW6/WftqnjfK7iWG9NzNknWAo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdp861C%2FbtrGitI0aW6%2FWftqnjfK7iWG9NzNknWAo1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1360&quot; height=&quot;708&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;708&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식이 오리진 서버의 짐을 덜어주는데, 그 이유는 연산 작업이 매 요청시마다 일어날 필요가 없기 때문이다. 또한 유저에게 더 빠른 결과물을 가져다 주는데 왜냐하면 지리적으로 유저로부터 가까운 지역에서 응답을 보내주기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js에서는 pre-rendering이 빠르게 끝나기 때문에, 해당 작업의 정적 결과물을 저장하고 컨텐츠를 빠르게 전송하도록 만드는데 &lt;span&gt;CDN이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;적절하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;The Edge&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엣지는 유저에게 가장 가까운 네트워크의 가장자리 (또는 엣지)라는 보편적인 컨셉을 가지고 있다. CDN 또한 &amp;ldquo;엣지&amp;rdquo;의 한 부분으로 간주될 수 있는데, 왜냐하면 CDN 또한 네트워크의 가장자리에 정적 컨텐츠를 저장하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CDN과 비슷하게, 엣지 서버는 전세계 여러 장소에 분산되어 있다. 그러나 정적 컨텐츠를 저장하는 CDN과 다르게, 몇몇 엣지 서버는 코드를 실행할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 말은 유저에게 더 가까운 엣지에서 &lt;b&gt;캐싱&lt;/b&gt;과 &lt;b&gt;코드 실행&lt;/b&gt;을 둘 다 할 수 있다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엣지에서 코드를 실행함으로써, 여러분은 전통적으로 클라이언트 사이드, 서버 사이드에서 수행하던 몇몇 작업들을 엣지로 옮길 수 있다. (&lt;a href=&quot;https://vercel.com/features/edge-functions&quot;&gt;Next.js와 함께 한 예시 하나를 보자&lt;/a&gt;). 이 방식이 어플리케이션의 성능을 더 좋게 만들어줄 수 있다. 왜냐하면 클라이언트로 보내지는 코드의 양을 줄일 수 있고, 유저 요청의 일부분이 오리진 서버로 가지 않아도 되기 때문에 레이턴시를 줄일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js에서 여러분은 &lt;a href=&quot;https://nextjs.org/docs/advanced-features/middleware&quot;&gt;미들웨어&lt;/a&gt;와 함께 엣지에서 코드를 실행시킬 수 있으며, 곧 &lt;a href=&quot;https://nextjs.org/docs/advanced-features/react-18/overview#react-server-components-alpha&quot;&gt;React Server Components&lt;/a&gt;도 사용할 수 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Quick Review&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 중 엣지에서 코드를 실행시키는 것의 장점이 아닌 것은?&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;단일 서버의 부하를 줄여준다&lt;/li&gt;
&lt;li&gt;컨텐츠를 빠르게 전송한다&lt;/li&gt;
&lt;li&gt;레이턴시를 높인다&lt;/li&gt;
&lt;li&gt;성능을 향상시킨다&lt;/li&gt;
&lt;/ul&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;레이턴시를 높인다&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend</category>
      <category>CDN</category>
      <category>client-side rendering</category>
      <category>EDGE</category>
      <category>server-side rendering</category>
      <category>static site generation</category>
      <category>서버사이드렌더링</category>
      <category>클라이언트사이드렌더링</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/236</guid>
      <comments>https://im-developer.tistory.com/236#entry236comment</comments>
      <pubDate>Mon, 4 Jul 2022 00:00:08 +0900</pubDate>
    </item>
    <item>
      <title>[Next.js Learn 문서 번역 #5] How Next.js Works - Compiling, Minifying, Bundling, Code Splitting이란?</title>
      <link>https://im-developer.tistory.com/237</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;(잘못해서 게시물을 삭제해버려서 다시 업로드합니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js 공식 문서에 상단 Learn 버튼 누르면 나오는 튜토리얼 문서인데 좋은 내용인 것 같아서 시리즈로 번역해서 올리려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;[원문 링크]&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://nextjs.org/learn/foundations/how-nextjs-works/compiling&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://nextjs.org/learn/foundations/how-nextjs-works/compiling&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nextjs.org/learn/foundations/how-nextjs-works/minifying&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://nextjs.org/learn/foundations/how-nextjs-works/minifying&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nextjs.org/learn/foundations/how-nextjs-works/bundling&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://nextjs.org/learn/foundations/how-nextjs-works/bundling&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nextjs.org/learn/foundations/how-nextjs-works/code-splitting&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://nextjs.org/learn/foundations/how-nextjs-works/code-splitting&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nextjs.org/learn/foundations/how-nextjs-works/buildtime-and-runtime&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://nextjs.org/learn/foundations/how-nextjs-works/buildtime-and-runtime&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;What is Compiling?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자들은 JSX, TypeScript 그리고 모던 JavaScript와 같은 더 개발 친화적인 언어로 코드를 작성한다. 이런 언어들이 개발자의 자신감과 효율성을 향상시키는 동안, 브라우저가 이해할 수 있도록 JavaScript로 컴파일되어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴파일링은 하나의 언어로 된 코드를 가져다가 같은 언어의 다른 버전이거나 다른 언어로 산출하는 과정을 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;644&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ckDOu8/btrGpEV7CYi/nOUBZ4tdYWvBxMorv56J61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ckDOu8/btrGpEV7CYi/nOUBZ4tdYWvBxMorv56J61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ckDOu8/btrGpEV7CYi/nOUBZ4tdYWvBxMorv56J61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FckDOu8%2FbtrGpEV7CYi%2FnOUBZ4tdYWvBxMorv56J61%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1360&quot; height=&quot;644&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;644&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js에서 컴파일(Compilation)은 여러분이 코드를 편집하는 개발 단계에서 여러분의 어플리케이션을 프로덕션으로 준비시키기 위한 빌드 단계의 한 과정으로써 진행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Quick Review&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;왜 여러분의 웹 어플리케이션 코드는 컴파일 되어야하는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코드를 브라우저가 이해할 수 있는 버전으로 변환하기 위해서&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;What is Minifying?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자는 인간이 읽을 수 있도록 최적화된 코드를 작성한다. 이러한 코드는 주석, 공백, 인덴트, 그리고 복수의 라인들과 같이 불필요한 정보들을 담고 있을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;644&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pOdCl/btrGjFa6XMd/zv4QtVvkcVxljbV8WLWFD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pOdCl/btrGjFa6XMd/zv4QtVvkcVxljbV8WLWFD1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pOdCl/btrGjFa6XMd/zv4QtVvkcVxljbV8WLWFD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpOdCl%2FbtrGjFa6XMd%2Fzv4QtVvkcVxljbV8WLWFD1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1360&quot; height=&quot;644&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;644&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 축소(Minification)는 코드의 기능을 변환시키지 않고 불필요한 코드 포매팅이나 주석을 삭제하는 과정이다. 목적은 파일 크기를 줄여서 어플리케이션의 성능을 향상시키는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js에서 프로덕션을 위한 JavaScript와 CSS 파일은 자동으로 축소된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;True or False&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Minifying은 여러분 코드에서 구문 하이라이팅(syntax highlighting)을 제거한다?&lt;/li&gt;
&lt;/ul&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;False - 구문 하이라이팅은 여러분의 코드 에디터에서 제공하는 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;What is Bundling?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자들은 그들의 어플리케이션을 어플리케이션의 더 큰 부분을 구축하는데 사용될 수 있도록 모듈, 컴포넌트, 그리고 함수로 쪼갠다. 이러한 내부 모듈(혹은 외부 써드 파티 패키지)을 Exporting하거나 Importing하는 것은 파일 의존성을 매우 복잡하게 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;640&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/r30dj/btrGkso3427/wzTL4gacIvanZlAHz9n721/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r30dj/btrGkso3427/wzTL4gacIvanZlAHz9n721/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r30dj/btrGkso3427/wzTL4gacIvanZlAHz9n721/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fr30dj%2FbtrGkso3427%2FwzTL4gacIvanZlAHz9n721%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1360&quot; height=&quot;640&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;640&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;번들링(Bundling)은 유저가 웹 페이지를 방문했을 때 파일 요청 수를 줄이는 것을 목적으로 웹의 의존성을 정리하고 파일(혹은 모듈)을 브라우저를 위해 압축된 번들로 병합(또는 패키징)하는 과정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Quick Review&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹 개발에서 컴파일링과 번들링의 차이점은 무엇인가?&lt;/li&gt;
&lt;/ul&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컴파일링은 코드를 브라우저에 의해 파싱 가능한 형태로 변환하는 것이다. 번들링은 어플리케이션 의존성 그래프를 정리하고 파일 수를 줄이는 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;What is Code Splitting?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자는 주로 어플리케이션을 다른 URL로부터 접근 가능한 여러 개의 페이지로 쪼갠다. 이러한 각각의 페이지들은 어플리케이션의 고유한 &lt;b&gt;진입점&lt;/b&gt;이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 스플리팅은 어플리케이션 번들을 각 진입점에서 필요로하는 작은 청크 단위로 나누는 프로세스이다. 목적은 해당 페이지를 실행하는데 필요한 코드만 로드하여 어플리케이션의 첫 로드 시간을 줄이는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;540&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PlEaR/btrGlhgzPIJ/Kt44DF9Ur7BfPiD63bZDw1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PlEaR/btrGlhgzPIJ/Kt44DF9Ur7BfPiD63bZDw1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PlEaR/btrGlhgzPIJ/Kt44DF9Ur7BfPiD63bZDw1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPlEaR%2FbtrGlhgzPIJ%2FKt44DF9Ur7BfPiD63bZDw1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1360&quot; height=&quot;540&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;540&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js는 코드 스플리팅(Code splitting)을 내장 지원한다. pages/ 경로에 있는 각 파일은 빌드 단계에서 자동으로 각각의 JavaScript 번들로 코드 스플릿된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;더 나아가서:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 페이지에서 공유하는 코드는 모두 별도의 번들로 쪼개져서 추가적인 페이지 이동 시 같은 코드를 다시 다운로드받지 않도록 한다.&lt;/li&gt;
&lt;li&gt;첫 페이지 로드 이후에 Next.js는 유저가 이동할 가능성이 높은 &lt;a href=&quot;https://nextjs.org/docs/api-reference/next/link&quot;&gt;다른 페이지의 코드를 프리 로드&lt;/a&gt;하기 시작할 수 있다.&lt;/li&gt;
&lt;li&gt;무슨 코드를 처음으로 로드할 지 수동으로 분리하는 다른 방법 중 하나로 &lt;a href=&quot;https://nextjs.org/docs/advanced-features/dynamic-import&quot;&gt;Dynamic imports&lt;/a&gt;가 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Quick Review&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 중 코드 스플리팅의 목적이 &lt;b&gt;아닌&lt;/b&gt; 것은?&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;A: 어플리케이션의 첫 로드를 향상시키기 위해&lt;/li&gt;
&lt;li&gt;B: 진입점에 필요한 코드만 로드하는 것&lt;/li&gt;
&lt;li&gt;C: 불필요한 코드를 삭제하는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;C: 불필요한 코드를 삭제하는 것&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Build Time and Runtime&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;빌드 시간&lt;/b&gt;(또는 빌드 단계)은 프로덕션을 위해 어플리케이션 코드를 준비시키는 연속적인 단계를 일컫는 말이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어플리케이션을 빌드하면, Next.js는 여러분의 코드를 &lt;a href=&quot;https://www.notion.so/How-Next-js-Works-ec726b3ec7b44e7a871ea36b1588bcad&quot;&gt;서버&lt;/a&gt;에 배포되어 유저에게 사용될 준비를 마친 프로덕션에 최적화된 파일로 변환할 것이다. 이러한 파일들은 다음을 포함한다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정적으로 생성된 페이지를 위한 HTML 파일&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nextjs.org/learn/foundations/how-nextjs-works/client-and-server&quot;&gt;서버&lt;/a&gt;에서 페이지 &lt;a href=&quot;https://nextjs.org/learn/foundations/how-nextjs-works/rendering&quot;&gt;렌더링&lt;/a&gt;을 하기 위한 JavaScript 코드&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nextjs.org/learn/foundations/how-nextjs-works/client-and-server&quot;&gt;클라이언트&lt;/a&gt;에서 페이지를 인터랙티브하게 만들어주는 JavaScript 코드&lt;/li&gt;
&lt;li&gt;CSS 파일들&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;런타임&lt;/b&gt;(또는 요청 시간)은 어플리케이션이 빌드되고 배포된 이후에 유저 요청에 대한 응답으로써 어플리케이션이 실행되는 시간을 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 섹션에서는 이 섹션에서 소개된 용어 몇 가지(클라이언트, 서버, 그리고 렌더링)에 대해서 알아보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;True or False&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;빌드 시간은 유저 요청에 대한 응답으로 어플리케이션이 내제되는 시간(기간)을 말한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;False (위 설명은 Runtime에 대한 설명이다)&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>Frontend</category>
      <category>Bundling</category>
      <category>code splitting</category>
      <category>Compiling</category>
      <category>Minifying</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/237</guid>
      <comments>https://im-developer.tistory.com/237#entry237comment</comments>
      <pubDate>Sun, 3 Jul 2022 23:35:27 +0900</pubDate>
    </item>
    <item>
      <title>[Next.js Learn 문서 번역 #4] How Next.js Works - Development와 Production 환경 (Next.js는 어떻게 동작하는가)</title>
      <link>https://im-developer.tistory.com/234</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;667&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ThQOM/btrEvNa4Jtk/SV51p2xqHwVasCevAenKZ0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ThQOM/btrEvNa4Jtk/SV51p2xqHwVasCevAenKZ0/img.jpg&quot; data-alt=&quot;https://unsplash.com/photos/u7wE6BwUisw&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ThQOM/btrEvNa4Jtk/SV51p2xqHwVasCevAenKZ0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FThQOM%2FbtrEvNa4Jtk%2FSV51p2xqHwVasCevAenKZ0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;476&quot; height=&quot;317&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;667&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://unsplash.com/photos/u7wE6BwUisw&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js Learn 문서 일부분에 대한 한글 번역입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;[원문 링크]&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://nextjs.org/learn/foundations/how-nextjs-works&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://nextjs.org/learn/foundations/how-nextjs-works&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nextjs.org/learn/foundations/how-nextjs-works/development-and-production&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://nextjs.org/learn/foundations/how-nextjs-works/development-and-production&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;How Next.js Works&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좀 더 상급의 Next.js 피쳐에 대해 공부하기 전에, Next.js가 어떻게 동작하는지 기본을 이해하면 더 도움이 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;강좌 초반에 리액트가 어플리케이션을 빌드하고 구조화하는데 있어서 얼마나 상대적으로 자율성이 있는지에 대해서 얘기했었다 (리액트로 어플리케이션을 구축하는데 정말 여러가지 방법이 존재한다.) Next.js는 어플리케이션을 구조화할 때 필요한 프레임워크와 개발 과정 및 최종 어플리케이션 결과물을 더 빠르게 만드는데 도움을 주는 최적화 등을 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 섹션에서는, 아래의 단계들에서 어플리케이션 코드에 어떤 일이 발생하는지 살펴볼 것이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러분의 코드가 &lt;b&gt;어느 환경에서(where)&lt;/b&gt; 실행되는지: &lt;b&gt;Development vs. Production&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;여러분의 코드가 &lt;b&gt;언제(when)&lt;/b&gt; 실행되는지: &lt;b&gt;Build Time vs. Runtime&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;렌더링이 &lt;b&gt;어디서(where)&lt;/b&gt; 일어나는지: &lt;b&gt;Client vs. Server&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Development and Production Environments&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러분은 환경(environments)을 여러분의 코드가 실행되는 컨텍스트라고 생각해볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;development 환경에서는 여러분의 로컬 머신에서 어플리케이션을 빌드하고 실행한다. &lt;a href=&quot;https://nextjs.org/docs/going-to-production&quot;&gt;Production으로 가는 것&lt;/a&gt;은 어플리케이션이 배포되고, 유저들에게 소비되도록 만드는 과정을 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;How this applies to Next.js (이것들은 어떻게 Next.js에 적용되는가)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js는 어플리케이션의 development와 production 단계 모두를 위한 피쳐를 제공한다. 예를 들어서:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;development 단계에서 Next.js는 개발자와 그들의 어플리케이션 빌드 경험을 최적화해준다. &lt;a href=&quot;https://nextjs.org/docs/basic-features/typescript&quot;&gt;TypeScript&lt;/a&gt;와&amp;nbsp;&lt;a href=&quot;https://nextjs.org/docs/basic-features/eslint&quot;&gt;ESLint integration&lt;/a&gt;,&amp;nbsp;&lt;a href=&quot;https://nextjs.org/docs/basic-features/fast-refresh&quot;&gt;Fast Refresh&lt;/a&gt; 등과 같은 개발자 경험을 향상시키는 것을 목적으로 하는 피쳐들이 제공된다.&lt;/li&gt;
&lt;li&gt;production 단계에서 Next.js는 어플리케이션을 사용하는 최종 사용자와 그들의 경험을 최적화해준다. 이 단계에서는 성능과 접근성을 향상시키기 위해 코드를 변환하는 것을 목표로 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 환경이 다른 고려 사항과 목표를 가지고 있기 때문에, 어플리케이션을 development에서 production으로 옮기기 위해서는 많은 작업들이 진행되어야 한다. 예를 들어서 어플리케이션 코드는 &lt;a href=&quot;https://nextjs.org/learn/foundations/how-nextjs-works/compiling&quot;&gt;compile&lt;/a&gt;되고,&amp;nbsp;&lt;a href=&quot;https://nextjs.org/learn/foundations/how-nextjs-works/bundling&quot;&gt;bundle&lt;/a&gt;되고,&amp;nbsp;&lt;a href=&quot;https://nextjs.org/learn/foundations/how-nextjs-works/minifying&quot;&gt;minified&lt;/a&gt;되고, 그리고&amp;nbsp;&lt;a href=&quot;https://nextjs.org/learn/foundations/how-nextjs-works/code-splitting&quot;&gt;code split&lt;/a&gt; 되어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;The Next.js Compiler (Next.js 컴파일러)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;Next.js는 이러한 코드 변환과 production에 배포하기 쉽도록 만들어주는 기초적인 인프라를 상당 부분 담당한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 것이 가능한 이유는 Next.js가 저레벨 언어인 Rust와 컴파일, 코드 축소(minification), 번들링 등에 사용될 수 있는 플랫폼인 SWC로 만들어진 &lt;a href=&quot;https://nextjs.org/docs/advanced-features/compiler&quot;&gt;compiler&lt;/a&gt;이기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 섹션에서, 각 코드 변환 단계를 알아보도록 할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Quick Review&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 단계들 중에서 production 최적화 과정이 아닌 것은?&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;A. Compiling&lt;/li&gt;
&lt;li&gt;B. Bundling&lt;/li&gt;
&lt;li&gt;C. Routing&lt;/li&gt;
&lt;li&gt;D. Minifying&lt;/li&gt;
&lt;/ul&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C. Routing&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>Frontend</category>
      <category>Next.js</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/234</guid>
      <comments>https://im-developer.tistory.com/234#entry234comment</comments>
      <pubDate>Sun, 12 Jun 2022 14:13:20 +0900</pubDate>
    </item>
    <item>
      <title>[Next.js Learn 문서 번역 #3] From JavaScript to React (React의 Components, Props, State 소개)</title>
      <link>https://im-developer.tistory.com/233</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;667&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/I5rll/btrDYKS83nd/M4tLHuPBr3Il2EAYtnvnF1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/I5rll/btrDYKS83nd/M4tLHuPBr3Il2EAYtnvnF1/img.jpg&quot; data-alt=&quot;https://unsplash.com/photos/KuCGlBXjH_o&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/I5rll/btrDYKS83nd/M4tLHuPBr3Il2EAYtnvnF1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FI5rll%2FbtrDYKS83nd%2FM4tLHuPBr3Il2EAYtnvnF1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;396&quot; height=&quot;264&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;667&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://unsplash.com/photos/KuCGlBXjH_o&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js&amp;nbsp;Learn&amp;nbsp;문서&amp;nbsp;일부분에&amp;nbsp;대한&amp;nbsp;한글&amp;nbsp;번역입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;[원문 링크]&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://nextjs.org/learn/foundations/from-javascript-to-react/react-core-concepts&quot;&gt;https://nextjs.org/learn/foundations/from-javascript-to-react/react-core-concepts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nextjs.org/learn/foundations/from-javascript-to-react/building-ui-with-components&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://nextjs.org/learn/foundations/from-javascript-to-react/building-ui-with-components&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nextjs.org/learn/foundations/from-javascript-to-react/displaying-data-with-props&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://nextjs.org/learn/foundations/from-javascript-to-react/displaying-data-with-props&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nextjs.org/learn/foundations/from-javascript-to-react/adding-interactivity-with-state&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://nextjs.org/learn/foundations/from-javascript-to-react/adding-interactivity-with-state&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;From JavaScript to React (JavaScript에서 React까지) #3&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;React Core Concepts&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트 어플리케이션을 구축하기 위해서 친숙해져야 할 리액트의 3가지 코어 컨셉은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Components&lt;/li&gt;
&lt;li&gt;Props&lt;/li&gt;
&lt;li&gt;State&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 섹션에서, 위 컨셉들을 하나씩 살펴보고 더 공부하기 위해서 필요한 자료들을 제공하려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Building UI with Components&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유저 인터페이스는 &lt;b&gt;컴포넌트&lt;/b&gt;라고 불리는 작은 블록들로 쪼개질 수 있다. 컴포넌트는 독립적이고 재사용 가능한 코드 스니펫을 구축할 수 있도록 만들어준다. 컴포넌트를 레고 블록이라고 생각했을 때, 여러분은 개별적인 블록을 가져다가 결합하여 더 큰 구조물을 만들 수 있다. 만약 UI 조각들을 업데이트해야 한다면, 특정 컴포넌트 또는 블록을 업데이트할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;760&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NwgWN/btrD3RiXZdv/LQbCyHgDPogmnRGb8bosr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NwgWN/btrD3RiXZdv/LQbCyHgDPogmnRGb8bosr0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NwgWN/btrD3RiXZdv/LQbCyHgDPogmnRGb8bosr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNwgWN%2FbtrD3RiXZdv%2FLQbCyHgDPogmnRGb8bosr0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1360&quot; height=&quot;760&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;760&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 모듈성은 점점 커져가는 규모에도 코드를 더욱 유지가능하게 만들어주는데, 왜냐하면 어플리케이션의 나머지 부분을 건드리지 않고 쉽게 컴포넌트를 추가하거나 업데이트, 삭제할 수 있게 해주기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트 컴포넌트의 좋은 점은 이것들이 단지 JavaScript라는 점이다. JavaScript 관점에서 리액트 컴포넌트를 어떻게 만들 수 있는지 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Creating components&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트에서 컴포넌트는 &lt;b&gt;함수&lt;/b&gt;이다. script 태그 안에, header라는 함수를 적어보자:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;&amp;lt;script type=&quot;text/jsx&quot;&amp;gt;
  const app = document.getElementById(&quot;app&quot;)

  function header() {
  }

  ReactDOM.render(&amp;lt;h1&amp;gt;Develop. Preview. Ship.  &amp;lt;/h1&amp;gt;, app)
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴포넌트는 &lt;b&gt;UI 엘리먼트를 리턴하는&lt;/b&gt; 함수이다. 함수의 리턴문 안에는 JSX를 작성할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;&amp;lt;script type=&quot;text/jsx&quot;&amp;gt;
  const app = document.getElementById(&quot;app&quot;)

  function header() {
     return (&amp;lt;h1&amp;gt;Develop. Preview. Ship.  &amp;lt;/h1&amp;gt;)
   }

  ReactDOM.render(, app)
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 컴포넌트를 DOM에 렌더하기 위해, ReactDOM.render() 함수의 첫 번째 인자로 전달할 수 있다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;&amp;lt;script type=&quot;text/jsx&quot;&amp;gt;

  const app = document.getElementById(&quot;app&quot;)

  function header() {
     return (&amp;lt;h1&amp;gt;Develop. Preview. Ship.  &amp;lt;/h1&amp;gt;)
   }

   ReactDOM.render(header, app)
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 만약 여러분이 위 코드를 브라우저에서 실행하려고 하면 아마 에러를 만날 것이다. 정상 작동되게 하려면 해야하는 작업이 두 가지가 있다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫째, 리액트 컴포넌트를 순수 HTML과 JavaScript로부터 구별하기 위해 첫 글자를 대문자로 해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function Header() {
  return &amp;lt;h1&amp;gt;Develop. Preview. Ship.  &amp;lt;/h1&amp;gt;;
}

// Capitalize the React Component
ReactDOM.render(Header, app);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘때, 리액트 컴포넌트를 사용할 때 보통의 HTML 태그를 사용하듯이 &amp;lt;&amp;gt; 꺾쇠 괄화를 사용해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function Header() {
  return &amp;lt;h1&amp;gt;Develop. Preview. Ship.  &amp;lt;/h1&amp;gt;;
}

ReactDOM.render(&amp;lt;Header /&amp;gt;, app);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Nesting Components&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어플리케이션은 보통 단일 컴포넌트보다는 더 많은 컨텐츠를 포함하고 있다. 따라서 리액트 컴포넌트 또한 보통의 HTML 엘리먼트와 같이 컴포넌트 안에 끼워 넣을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, &amp;lt;HomePage&amp;gt;라는 새 컴포넌트를 만들어보자:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function Header() {
  return &amp;lt;h1&amp;gt;Develop. Preview. Ship.  &amp;lt;/h1&amp;gt;;
}
function HomePage() {
  return &amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;;
}

ReactDOM.render(&amp;lt;Header /&amp;gt;, app);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고나서 &amp;lt;Header&amp;gt; 컴포넌트를 새 &amp;lt;HomePage&amp;gt; 컴포넌트 안에 끼워넣어보자:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function Header() {
  return &amp;lt;h1&amp;gt;Develop. Preview. Ship.  &amp;lt;/h1&amp;gt;;
}

function HomePage() {
  return (
    &amp;lt;div&amp;gt;
      {/* Nesting the Header component */}
      &amp;lt;Header /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

ReactDOM.render(&amp;lt;Header /&amp;gt;, app);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Component Trees&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계속해서 이런 방식으로 리액트 컴포넌트를 nesting하여 컴포넌트 트리를 형성할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;682&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oum2Y/btrDXoJODIo/sM9QkYtpAjAfBYw2BOTQG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oum2Y/btrDXoJODIo/sM9QkYtpAjAfBYw2BOTQG0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oum2Y/btrDXoJODIo/sM9QkYtpAjAfBYw2BOTQG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Foum2Y%2FbtrDXoJODIo%2FsM9QkYtpAjAfBYw2BOTQG0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1360&quot; height=&quot;682&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;682&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 상위 레벨의 HomePage 컴포넌트는 Header, Article, Footer 컴포넌트를 가지고 있을 수 있다. 그리고 각 컴포넌트들 또한 그들만의 자식 컴포넌트를 가지고 있을 수 있다. 예를 들어, Header 컴포넌트는 Logo, Title 그리고 Navigation 컴포넌트를 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 모듈 형식은 앱 내의 다른 장소에 컴포넌트를 재사용할 수 있게 만들어준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 프로젝트에서 &amp;lt;HomePage&amp;gt;가 가장 상위 레벨 컴포넌트이고, 이 컴포넌트를 ReactDOM.render() 메소드에 전달할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function Header() {
  return &amp;lt;h1&amp;gt;Develop. Preview. Ship.  &amp;lt;/h1&amp;gt;;
}

function HomePage() {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;Header /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

ReactDOM.render(&amp;lt;HomePage /&amp;gt;, app);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Displaying Data with Props&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지, &amp;lt;Header /&amp;gt; 컴포넌트를 계속 재사용해왔다면, 사용할 때마다 항상 같은 컨텐츠를 화면에 표시했을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function Header() {
  return &amp;lt;h1&amp;gt;Develop. Preview. Ship.  &amp;lt;/h1&amp;gt;;
}

function HomePage() {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;Header /&amp;gt;
      &amp;lt;Header /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 만약 다른 텍스트를 전달하고 싶었거나 또는 외부 소스로부터 데이터를 불러오기 때문에 어떤 정보가 들어갈 지 미리 알 수 없는 상황이라면 어떻게 해야 할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통의 HTML 엘리먼트는 엘리먼트의 행동을 변경하기 위해 정보 조각들을 전달할 수 있는 attribute들을 가지고 있다. 예를 들어서 &amp;lt;img&amp;gt; 엘리먼트의 src attribute를 변경하면, 화면에 보여지는 이미지를 변경할 수 있다. &amp;lt;a&amp;gt; 태그의 href attribute를 변경하면 링크의 목적지를 변경시킬 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트 컴포넌트에도 같은 방법으로 정보 조각들인 property들을 전달할 수 있다. 이것들을 우리는 props라고 부른다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;272&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EDOvs/btrDWUvnJjE/gPaoZi4MuoYsFzSFagTNH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EDOvs/btrDWUvnJjE/gPaoZi4MuoYsFzSFagTNH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EDOvs/btrDWUvnJjE/gPaoZi4MuoYsFzSFagTNH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEDOvs%2FbtrDWUvnJjE%2FgPaoZi4MuoYsFzSFagTNH1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1360&quot; height=&quot;272&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;272&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JavaScript 함수와 비슷하게, 커스텀 인자(또는 props)를 받아서 컴포넌트의 행동 양식을 바꾸거나 화면에 렌더될 때 시각적으로 보여지는 것을 바꾸도록 컴포넌트를 디자인할 수 있다. 그리고나서 이 props를 부모 컴포넌트로부터 자식 컴포넌트로 전달할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노트: 리액트에서 데이터는 컴포넌트 트리의 아래 방향으로 흐른다. 이 것을 다음 섹션에서 얘기 나눌 단방향 데이터 흐름이라고 부르며 데이터가 부모 컴포넌트에서 자식 컴포넌트로 props로 전달될 수 있다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Using props&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HomePage 컴포넌트에서, HTML attribute를 전달하는 것처럼 커스텀 title prop을 Header 컴포넌트에 전달할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;// function Header() {
//   return &amp;lt;h1&amp;gt;Develop. Preview. Ship.  &amp;lt;/h1&amp;gt;
// }

function HomePage() {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;Header title=&quot;React  &quot; /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

// ReactDOM.render(&amp;lt;HomePage /&amp;gt;, app)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 자식 컴포넌트인 Header는 그 props를 &lt;b&gt;함수의 첫 번째 파라미터&lt;/b&gt;로 받을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function Header(props) {
//   return &amp;lt;h1&amp;gt;Develop. Preview. Ship.  &amp;lt;/h1&amp;gt;
// }

// function HomePage() {
//   return (
//     &amp;lt;div&amp;gt;
//       &amp;lt;Header title=&quot;React  &quot; /&amp;gt;
//     &amp;lt;/div&amp;gt;
//   )
// }

// ReactDOM.render(&amp;lt;HomePage /&amp;gt;, app)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 console.log()로 props를 출력해보면, title 프로퍼티를 가진 &lt;b&gt;객체&lt;/b&gt;인 것을 확인할 수 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function Header(props) {
    console.log(props) // { title: &quot;React  &quot; }
//   return &amp;lt;h1&amp;gt;React  &amp;lt;/h1&amp;gt;
// }

// function HomePage() {
//   return (
//     &amp;lt;div&amp;gt;
//       &amp;lt;Header title=&quot;React  &quot; /&amp;gt;
//     &amp;lt;/div&amp;gt;
//   )
// }

// ReactDOM.render(&amp;lt;HomePage /&amp;gt;, app)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;props는 객체이기 때문에 &lt;b&gt;object destructuring&lt;/b&gt;을 사용하여 함수 파라미터 안에서 명시적으로 props의 값들을 네이밍할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function Header({ title }) {
    console.log(title) // &quot;React  &quot;
//  return &amp;lt;h1&amp;gt;React  &amp;lt;/h1&amp;gt;
// }

// function HomePage() {
//   return (
//     &amp;lt;div&amp;gt;
//       &amp;lt;Header title=&quot;React  &quot; /&amp;gt;
//     &amp;lt;/div&amp;gt;
//   )
// }

// ReactDOM.render(&amp;lt;HomePage /&amp;gt;, app)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고나서 &amp;lt;h1&amp;gt; 태그의 컨텐츠를 title 변수로 교체할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function Header({ title }) {
  console.log(title);
  return &amp;lt;h1&amp;gt;title&amp;lt;/h1&amp;gt;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저에서 이 프로젝트를 오픈하면, &amp;ldquo;title&amp;rdquo;이라는 단어를 디스플레이하고 있는 것을 볼 수 있을 것이다. 왜냐하면 리액트는 DOM에 단순 텍스트 string을 렌더하는 것이 여러분의 의도라고 생각할 것이기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 리액트에게 이것이 JavaScript 변수라는 것을 나타낼 필요가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Using Variables in JSX&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러분이 정의한 변수를 사용하기 위해서는 &lt;b&gt;{} 괄호&lt;/b&gt;를 사용해야 하는데, 이 괄호는 JSX 마크업 안에 직접적으로 보통의 JavaScript를 작성할 수 있도록 해주는 특별한 JSX 문법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;// function Header({title}) {
//  console.log(title)
return &amp;lt;h1&amp;gt;{title}&amp;lt;/h1&amp;gt;;
// }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;괄호를 &amp;ldquo;JSX 영토&quot; 안에서 &amp;ldquo;JavaScript 영토&quot; 안으로 들어가게 해주는 입구라고 생각하면 된다. 괄호 안에는 아무 JavaScript 표현식(단일 value로 계산되는 어떤 것)을 추가할 수 있다. 예를 들어:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞에서 다뤘듯이, &lt;b&gt;dot notation과 함께 객체 속성&lt;/b&gt;을 사용할 수도 있다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function Header(props) {
  return &amp;lt;h1&amp;gt;{props.title}&amp;lt;/h1&amp;gt;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;template literal&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function Header({ title }) {
  return &amp;lt;h1&amp;gt;{`Cool ${title}`}&amp;lt;/h1&amp;gt;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;함수의 리턴 값&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function createTitle(title) {
  if (title) {
    return title;
  } else {
    return 'Default title';
  }
}

function Header({ title }) {
  return &amp;lt;h1&amp;gt;{createTitle(title)}&amp;lt;/h1&amp;gt;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 &lt;b&gt;삼항 연산자&lt;/b&gt;:&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function Header({ title }) {
  return &amp;lt;h1&amp;gt;{title ? title : 'Default Title'}&amp;lt;/h1&amp;gt;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 여러분은 어떤 string이든 title prop으로 전달할 수 있고, 심지어 default case를 삼항연산자로 계획해두었기 때문에 title prop으로 아무것도 전달하지 않아도 된다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function Header({ title }) {
  return &amp;lt;h1&amp;gt;{title ? title : 'Default title'}&amp;lt;/h1&amp;gt;;
}

function Page() {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;Header /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴포넌트는 이제 어플리케이션의 다른 부분에 재사용할 수 있도록 포괄적인 title prop을 받을 수 있다. 이제 여러분이 할 일은 단지 title을 변경하는 것 뿐이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function Page() {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;Header title=&quot;React  &quot; /&amp;gt;
      &amp;lt;Header title=&quot;A new title&quot; /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Iterating through lists&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스트 형태로 보여줘야 하는 데이터를 가지는 것은 흔한 일이다. 배열 메소드로 데이터를 조작하고, 스타일은 통일되지만 다른 정보 조각들을 가지는 UI 엘리먼트를 생성할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노트: 리액트는 데이터 fetching에 있어서는 독선적이지 않다. 무슨 뜻이냐면 여러분에게 맞는 어떠한 솔루션이든 고를 수 있다는 뜻이다. 나중에 Next.js에서의 data fetching 옵션들에 대해서 얘기해볼 것이다. 그러나 지금은 데이터를 보여주기 위한 단순한 배열을 사용할 것이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HomePage 컴포넌트에 이름 배열을 추가해보자:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function HomePage() {
  const names = ['Ada Lovelace', 'Grace Hopper', 'Margaret Hamilton'];

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;Header title=&quot;Develop. Preview. Ship.  &quot; /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 array.map() 메소드를 사용하여 배열을 순회하고 &lt;b&gt;화살표 함수&lt;/b&gt;를 사용하여 리스트 아이템 이름을 매핑할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function HomePage() {
  const names = ['Ada Lovelace', 'Grace Hopper', 'Margaret Hamilton'];

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;Header title=&quot;Develop. Preview. Ship.  &quot; /&amp;gt;
      &amp;lt;ul&amp;gt;
        {names.map((name) =&amp;gt; (
          &amp;lt;li&amp;gt;{name}&amp;lt;/li&amp;gt;))}
      &amp;lt;/ul&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;괄호를 사용하여 어떻게 &amp;ldquo;JavaScript&amp;rdquo;와 &amp;ldquo;JSX&amp;rdquo; 영토 사이를 누비고 지나갔는지 주목해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 코드를 실행하면, 리액트는 missing key prop에 대한 경고 메시지를 줄 것이다. 왜냐하면 리액트는 배열 아이템을 식별하여 DOM 엘리먼트를 업데이트하기 위해 고유의 식별값을 필요로 하기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금은 일단 이름이 고유하기 때문에 이름을 그대로 사용할 수 있지만, 아이템 ID와 같이 고유성이 보장되는 값을 사용하는 것을 권장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function HomePage() {
  const names = ['Ada Lovelace', 'Grace Hopper', 'Margaret Hamilton'];

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;Header title=&quot;Develop. Preview. Ship.  &quot; /&amp;gt;
      &amp;lt;ul&amp;gt;
        {names.map((name) =&amp;gt; (
          &amp;lt;li key={name}&amp;gt;{name}&amp;lt;/li&amp;gt;))}
      &amp;lt;/ul&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Adding Interactivity with State&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트가 어떻게 &lt;b&gt;state&lt;/b&gt; 그리고 &lt;b&gt;event handler&lt;/b&gt;를 통해 어플리케이션의 상호 작용성을 더해주는지 탐험해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한가지 예로, 프로젝트에 좋아요 버튼을 만들어보자. 첫째로 코드에 버튼 엘리먼트를 추가한다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function HomePage() {
  const names = ['Ada Lovelace', 'Grace Hopper', 'Margaret Hamilton'];

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;Header title=&quot;Develop. Preview. Ship.  &quot; /&amp;gt;
      &amp;lt;ul&amp;gt;
        {names.map((name) =&amp;gt; (
          &amp;lt;li key={name}&amp;gt;{name}&amp;lt;/li&amp;gt;))}
      &amp;lt;/ul&amp;gt;

      &amp;lt;button&amp;gt;Like&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Listening to Events&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버튼이 클릭됐을 때 뭔가를 하게 만드려면, onClick 이벤트를 사용하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function HomePage() {
  // ...
  return (
    &amp;lt;div&amp;gt;
      {/* ... */}
      &amp;lt;button onClick={}&amp;gt;Like&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트에서 이벤트 이름은 camelCase로 작성된다. onClick 이벤트는 유저 인터랙션에 응답하기 위해 사용 가능한 여러 이벤트 중 하나이다. 예를 들어, input 필드를 위해서 onChange 이벤트를 사용하거나 form을 위해서 onSubmit 이벤트를 사용할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Handling Events&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러분은 이벤트가 트리거되었을 때 해당 이벤트를 &amp;ldquo;핸들링&quot;할 함수를 정의할 수도 있다. 컴포넌트의 리턴문 전에 handleClick()이라는 함수를 만들자:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function HomePage() {
  // ...

  function handleClick() {
    console.log(&quot;increment like count&quot;)
  }

  return (
    &amp;lt;div&amp;gt;
      {/* ... */}
      &amp;lt;button onClick={}&amp;gt;Like&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  )
 }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 이제 onClick 이벤트가 트리거될 때 handleClick 함수를 호출할 수 있게 된다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function HomePage() {
  //    ...
  function handleClick() {
    console.log('increment like count');
  }

  return (
    &amp;lt;div&amp;gt;
      {/* ... */}
      &amp;lt;button onClick={handleClick}&amp;gt;Like&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;State and Hooks&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트는 &lt;a href=&quot;https://reactjs.org/docs/hooks-intro.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;hook&lt;/a&gt;으로 불리는 함수 세트를 가지고 있다. Hook은 컴포넌트에 &lt;b&gt;state&lt;/b&gt;와 같은 부가적인 로직을 추가할 수 있도록 해준다. state는 UI상에서 보통 유저 인터랙션에 의해 트리거되는 시간에 따라 변화하는 정보라고 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;544&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cyOqFP/btrDZuP3Itx/r6ylSrSNUDW3YnYkq4p0ck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cyOqFP/btrDZuP3Itx/r6ylSrSNUDW3YnYkq4p0ck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cyOqFP/btrDZuP3Itx/r6ylSrSNUDW3YnYkq4p0ck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcyOqFP%2FbtrDZuP3Itx%2Fr6ylSrSNUDW3YnYkq4p0ck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1360&quot; height=&quot;544&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;544&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;state를 유저가 좋아요 버튼을 몇 번 클릭한 만큼 숫자를 증가시키고 저장하는데 사용할 수 있다. 사실, 리액트에서 이러한 것들을 관리하는 hook을 useState()라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function HomePage() {
  React.useState();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useState()는 배열을 리턴하고 &lt;b&gt;array destructuring&lt;/b&gt;을 사용하여 컴포넌트 안에서 배열의 값에 접근할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function HomePage() {
  const [] = React.useState();

  // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열의 첫 번째 아이템은 state value이고, 어떤 이름이든 마음대로 네이밍할 수 있다. 어떤 값인지 설명할 수 있는 이름을 사용하는 것을 추천한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function HomePage() {
  const [likes] = React.useState();

  // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열의 두 번째 아이템은 해당 값을 업데이트할 수 있는 함수이다. 함수 이름 또한 아무거나 정할 수 있지만, set이라는 prefix와 함께 업데이트할 state 이름을 그대로 사용하는 것이 보통이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function HomePage() {
  const [likes, setLikes] = React.useState();

  // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;likes state의 초기 값 또한 정할 수 있다: 숫자 0.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function HomePage() {
  const [likes, setLikes] = React.useState(0);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 컴포넌트 안에서 state 변수를 사용하여 초기 state가 잘 작동하는지 확인해볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function HomePage() {
  // ...
  const [likes, setLikes] = React.useState(0);

  return (
    // ...
    &amp;lt;button onClick={handleClick}&amp;gt;Like({likes})&amp;lt;/button&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마침내, 이 전에 정의한 handleClick() 함수 안에서 state 업데이트 함수(setLikes)를 호출할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function HomePage() {
  // ...
  const [likes, setLikes] = useState()

  function handleClick() {
    setLikes(likes + 1)
  }}

  return (
    &amp;lt;div&amp;gt;
      {/* ... */}
      &amp;lt;button onClick={handleClick}&amp;gt;Likes ({likes})&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버튼을 클릭하면 이제 handleClick 함수가 실행될 것이고, 현재의 좋아요 값에 1을 더한 함수 매개변수와 함께 setLikes라는 state 업데이터 함수가 실행될 것이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노트: 컴포넌트 함수의 첫 번째 파라미터로 전달되는 props와 달리, state는 컴포넌트 안에서 초기화되고 저장된다. state 정보를 자식 컴포넌트에 props로 전달 할 수 있지만, state값을 업데이트하는 로직은 state가 최초 생성된 컴포넌트 안에 위치해야 한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Managing State&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 내용은 state의 소개글 정도이고, state를 다루는 것과 리액트 어플리케이션의 데이터 플로우에 대해서 더 공부할 것이 많다. 더 공부하려면 리액트 문서의 &lt;a href=&quot;https://beta.reactjs.org/learn/adding-interactivity&quot;&gt;Adding Interactivity&lt;/a&gt;와&amp;nbsp;&lt;a href=&quot;https://beta.reactjs.org/learn/managing-state&quot;&gt;Managing State&lt;/a&gt;&amp;nbsp;섹션을 살펴보는 것을 추천한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Quick Review&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;props와 state의 차이점은 무엇인가?&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rArr; Props은 컴포넌트로 전달되는 read-only 정보이다. State는 주로 유저 인터랙션으로 트리거되는 시간에 따라 변화되는 정보이다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend</category>
      <category>Component</category>
      <category>props</category>
      <category>react</category>
      <category>State</category>
      <category>리액트</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/233</guid>
      <comments>https://im-developer.tistory.com/233#entry233comment</comments>
      <pubDate>Sun, 5 Jun 2022 17:47:43 +0900</pubDate>
    </item>
    <item>
      <title>[Next.js Learn 문서 번역 #2] From JavaScript to React (React와 JSX)</title>
      <link>https://im-developer.tistory.com/232</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuthZ3/btrDvRXXSC9/08w18ZSFXWgTpdvpDw9OE0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuthZ3/btrDvRXXSC9/08w18ZSFXWgTpdvpDw9OE0/img.jpg&quot; data-alt=&quot;https://unsplash.com/photos/ouVQwCBlQ9E&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuthZ3/btrDvRXXSC9/08w18ZSFXWgTpdvpDw9OE0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcuthZ3%2FbtrDvRXXSC9%2F08w18ZSFXWgTpdvpDw9OE0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;341&quot; height=&quot;341&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1000&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://unsplash.com/photos/ouVQwCBlQ9E&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js Learn 문서 일부분에 대한 한글 번역입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;[원문 링크]&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://nextjs.org/learn/foundations/from-javascript-to-react/getting-started-with-react&quot;&gt;https://nextjs.org/learn/foundations/from-javascript-to-react/getting-started-with-react&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;From JavaScript to React (JavaScript에서 React까지) #2&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러분의 프로젝트에 리액트를 사용하기 위해서는 &lt;a href=&quot;https://unpkg.com/&quot;&gt;unpkg.com&lt;/a&gt;이라고 불리는 외부 웹사이트로부터 두 개의 리액트 스크립트를 로드할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;react&lt;/b&gt;는 리액트 코어 라이브러리이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;react-dom&lt;/b&gt;은 리액트를 DOM과 함께 사용할 수 있도록 만들어주는 DOM에 특화된 method를 제공해준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div id=&quot;app&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;const app = document.getElementById('app');&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순수 자바스크립트로 DOM을 직접 조작하는 대신, 여러분은 react-dom의 ReactDOM.render() 메소드를 사용하여 리액트가 #app 엘리먼트 안에 &amp;lt;h1&amp;gt;을 렌더하도록 할 수 있다.&lt;/p&gt;
&lt;div id=&quot;app&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;const app = document.getElementById('app');
ReactDOM.render(&amp;lt;h1&amp;gt;Develop. Preview. Ship.  &amp;lt;/h1&amp;gt;, app);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 여러분이 위 코드를 브라우저에서 실행하려고 하면, 다음과 같은 syntax 에러를 만나게 될 것이다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;912&quot; data-origin-height=&quot;76&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cjMbrU/btrDoT34rh9/Wq73p2SqImLKBcon5kTgf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cjMbrU/btrDoT34rh9/Wq73p2SqImLKBcon5kTgf1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cjMbrU/btrDoT34rh9/Wq73p2SqImLKBcon5kTgf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcjMbrU%2FbtrDoT34rh9%2FWq73p2SqImLKBcon5kTgf1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;912&quot; height=&quot;76&quot; data-origin-width=&quot;912&quot; data-origin-height=&quot;76&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜냐면 &amp;lt;h1&amp;gt;&amp;hellip;&amp;lt;/h1&amp;gt;이 유효한 자바스크립트가 아니기 때문이다. 이것은 JSX 코드 조각일 뿐이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;What is JSX? (JSX란 무엇인가?)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSX는 여러분이 UI를 HTML과 유사한 문법으로 설명할 수 있도록 만들어주는 자바스크립트를 위한 syntax extentsion(문법 확장팩)이다. JSX의 좋은 점은 다음 &lt;a href=&quot;https://beta.reactjs.org/learn/writing-markup-with-jsx#the-rules-of-jsx&quot;&gt;3가지 JSX 문법&lt;/a&gt;을 제외하고, HTML이나 자바스크립트 이외의 새로운 부호나 문법을 배울 필요가 없다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저는 JSX를 이해하지 못하기 때문에 JSX 코드를 일반적인 자바스크립트로 변환하기 위해서는 &lt;a href=&quot;https://babeljs.io/&quot;&gt;Babel&lt;/a&gt;과 같은 자바스크립트 컴파일러를 필요로 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Adding Babel to your project (프로젝트에 Babel 추가하기)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Babel을 프로젝트에서 사용하려면 다음 스크립트를 복사 붙여넣기 하여 index.html 파일에 넣는다.&lt;/p&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;&amp;lt;script src=&quot;https://unpkg.com/@babel/standalone/babel.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;덧붙여, script 태그의 타입을 type=text/jsx로 바꿔서 어떤 코드를 변환할 지 Babel에게 알려줄 필요도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;html xml&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;&amp;lt;html&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;div id=&quot;app&quot;&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;script src=&quot;https://unpkg.com/react@17/umd/react.development.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script src=&quot;https://unpkg.com/react-dom@17/umd/react-dom.development.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;!-- Babel Script --&amp;gt;
    &amp;lt;script src=&quot;https://unpkg.com/@babel/standalone/babel.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script type=&quot;text/jsx&quot;&amp;gt;
      const app = document.getElementById('app');
      ReactDOM.render(&amp;lt;h1&amp;gt;Develop. Preview. Ship.  &amp;lt;/h1&amp;gt;, app);
    &amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Quick Review&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 리액트 코드는 컴파일되어야 하는가?&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rArr; 리액트가 JSX를 사용하기 때문에 자바스크립트로 컴파일되어야 한다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend</category>
      <category>jsx</category>
      <category>react</category>
      <category>리액트</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/232</guid>
      <comments>https://im-developer.tistory.com/232#entry232comment</comments>
      <pubDate>Sun, 29 May 2022 23:36:20 +0900</pubDate>
    </item>
    <item>
      <title>[Next.js Learn 문서 번역 #1] From JavaScript to React (React는 선언형 UI 라이브러리이다)</title>
      <link>https://im-developer.tistory.com/231</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;823&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JHVWc/btrEyuPbtgs/CwryHHZcryyCkws5rthNg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JHVWc/btrEyuPbtgs/CwryHHZcryyCkws5rthNg1/img.png&quot; data-alt=&quot;https://unsplash.com/photos/6gKx3ESOoFE&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JHVWc/btrEyuPbtgs/CwryHHZcryyCkws5rthNg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJHVWc%2FbtrEyuPbtgs%2FCwryHHZcryyCkws5rthNg1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;373&quot; height=&quot;560&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;823&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://unsplash.com/photos/6gKx3ESOoFE&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js 공식 문서에 상단 Learn 버튼 누르면 나오는 튜토리얼 문서인데 좋은 내용인 것 같아서 시리즈로 번역해서 올리려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;[원문 링크]&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://nextjs.org/learn/foundations/from-javascript-to-react&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://nextjs.org/learn/foundations/from-javascript-to-react&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nextjs.org/learn/foundations/from-javascript-to-react/updating-ui-with-javascript&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://nextjs.org/learn/foundations/from-javascript-to-react/updating-ui-with-javascript&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;[Next.js Learn - 현재까지의 번역본]&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #1b711d;&quot;&gt;&lt;a style=&quot;color: #1b711d;&quot; href=&quot;https://im-developer.tistory.com/231&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[#1] From JavaScript to React (React는 선언형 UI 라이브러리이다)&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #1b711d;&quot;&gt;&lt;a style=&quot;color: #1b711d;&quot; href=&quot;https://im-developer.tistory.com/232&quot;&gt;[#2] From JavaScript to React (React와 JSX)&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #1b711d;&quot;&gt;&lt;a style=&quot;color: #1b711d;&quot; href=&quot;https://im-developer.tistory.com/233&quot;&gt;[#3] From JavaScript to React (React의 Components, Props, State 소개)&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #1b711d;&quot;&gt;&lt;a style=&quot;color: #1b711d;&quot; href=&quot;https://im-developer.tistory.com/234&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[#4] How Next.js Works - Development와 Production 환경 (Next.js는 어떻게 동작하는가)&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #1b711d;&quot;&gt;&lt;a style=&quot;color: #1b711d;&quot; href=&quot;https://im-developer.tistory.com/237&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;[#5] How Next.js Works - Compiling, Minifying, Bundling, Code Splitting이란?&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #1b711d;&quot;&gt;&lt;a style=&quot;color: #1b711d;&quot; href=&quot;https://im-developer.tistory.com/236&quot;&gt;[#6] How Next.js Works - Client &amp;amp; Server 렌더링 방식, CDN과 Edge&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;From JavaScript to React (JavaScript에서 React까지) #1&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Rendering User Interfaces (유저 인터페이스 렌더링)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리액트가 어떻게 동작하는지 이해하려면, 우리는 interactive한 유저 인터페이스(UI)를 만들기 위해 먼저 브라우저가 여러분의 코드를 어떻게 해석하는지 기본적으로 이해해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유저가 웹 페이지를 방문하면, 서버는 HTML 파일을 브라우저에 리턴하는데, 아마 다음과 같이 생겼을 것이다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;804&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHsGMg/btrCNNbL506/mMVkIJx4Um7Xq0TFMOFoc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHsGMg/btrCNNbL506/mMVkIJx4Um7Xq0TFMOFoc0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHsGMg/btrCNNbL506/mMVkIJx4Um7Xq0TFMOFoc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHsGMg%2FbtrCNNbL506%2FmMVkIJx4Um7Xq0TFMOFoc0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1360&quot; height=&quot;804&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;804&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 브라우저가 HTML을 읽고 Document Object Model (DOM)을 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;What is the DOM? (DOM이란 무엇인가?)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DOM은 HTML 엘리먼트들을 표현하는 객체이다. DOM은 여러분의 코드와 유저 인터페이스 사이에서 다리 역할을 하며, 부모와 자식 관계를 가진 트리와 유사한 형태의 구조를 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;802&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgZokr/btrCMXZZOS2/Wc1FThntlLTegMwk2CTkdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgZokr/btrCMXZZOS2/Wc1FThntlLTegMwk2CTkdK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgZokr/btrCMXZZOS2/Wc1FThntlLTegMwk2CTkdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgZokr%2FbtrCMXZZOS2%2FWc1FThntlLTegMwk2CTkdK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1360&quot; height=&quot;802&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;802&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러분은 자바스크립트와 같은 DOM 메소드와 프로그래밍 언어를 사용하여 유저 이벤트를 받거나, 유저 인터페이스의 특정 엘리먼트를 선택, 추가, 업데이트, 삭제 등을 통해 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Manipulating_documents&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;DOM을 조작&lt;/a&gt;할 수 있다. DOM 조작은 특정 엘리먼트를 타겟팅할 수 있게 해주는 것 뿐만 아니라 스타일과 컨텐츠를 바꿀 수 있도록 해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Updating the UI with JavaScript and DOM Methods&lt;br /&gt;(JavaScript와 DOM 메소드로 UI 업데이트하기)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;h1 태그를 여러분의 프로젝트에 추가함으로써 여러분이 어떻게 자바스크립트와 DOM 메소드를 사용할 수 있는지 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 에디터를 열어서 새 index.html 파일을 만든다. HTML 파일 안에 다음 코드를 넣어보자:&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;!-- index.html --&amp;gt;
&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;&amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 div와 유니크한 id를 부여해서 나중에 타겟팅할 수 있게 한다.&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;!-- index.html --&amp;gt;
&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;&amp;lt;div id=&quot;app&quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML 파일 안에서 자바스크립트를 쓰려면 script 태그를 추가한다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;!-- index.html --&amp;gt;
&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;&amp;lt;div id=&quot;app&quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;script type=&quot;text/javascript&quot;&amp;gt;&amp;lt;/script&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크립트 태그 안에서 DOM 메소드인 getElementById()를 사용하여 id를 가진 div 엘리먼트를 선택할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;!-- index.html --&amp;gt;
&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;&amp;lt;div id=&quot;app&quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;script type=&quot;text/javascript&quot;&amp;gt;
      const app = document.getElementById('app');
    &amp;lt;/script&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 &amp;lt;h1&amp;gt; 엘리먼트를 만들기 위해 DOM 메소드를 계속 사용할 수 있다:&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;!-- index.html --&amp;gt;
&amp;lt;html&amp;gt;&amp;lt;body&amp;gt;&amp;lt;div id=&quot;app&quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;script type=&quot;text/javascript&quot;&amp;gt;
      // Select the div element with 'app' id
      const app = document.getElementById('app');

      // Create a new H1 element
      const header = document.createElement('h1');

      // Create a new text node for the H1 element
      const headerContent = document.createTextNode(
        'Develop. Preview. Ship.  ',
      );

      // Append the text to the H1 element
      header.appendChild(headerContent);

      // Place the H1 element inside the div
      app.appendChild(header);
    &amp;lt;/script&amp;gt;&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 것이 잘 동작하는 것을 확인하기 위해, HTML 파일을 원하는 브라우저로 열어보자. 'Develop. Preview. Ship.  '이라고 쓰여진 h1 태그를 볼 수 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;HTML vs the DOM (HTML vs DOM)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.chrome.com/docs/devtools/overview/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;브라우저 개발자 도구&lt;/a&gt;에서 DOM 엘리먼트를 바라보면, h1 엘리먼트를 가진 DOM을 확인할 수 있을 것이다. 해당 페이지의 DOM은 소스 코드(&amp;mdash; 다른 말로 하면 여러분이 생성한 원본 HTML 파일)와 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;664&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GziYr/btrCLlGeOMn/3RWYEe0tq1r7sCZ7YdanqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GziYr/btrCLlGeOMn/3RWYEe0tq1r7sCZ7YdanqK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GziYr/btrCLlGeOMn/3RWYEe0tq1r7sCZ7YdanqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGziYr%2FbtrCLlGeOMn%2F3RWYEe0tq1r7sCZ7YdanqK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1360&quot; height=&quot;664&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;664&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜냐하면 DOM은 여러분이 작성한 자바스크립트 코드에 의해 &lt;b&gt;업데이트된 페이지 컨텐츠&lt;/b&gt;를 나타내는 반면, HTML은 &lt;b&gt;초기 페이지 컨텐츠&lt;/b&gt;를 나타내기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순수 자바스크립트로 DOM을 업데이트하는 것은 매우 강력하지만 복잡하다. 아래 모든 코드를 텍스트를 가진 h1 엘리먼트 하나를 추가하기 위해 작성해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;!-- index.html --&amp;gt;
&amp;lt;script type=&quot;text/javascript&quot;&amp;gt;
  const app = document.getElementById('app');
  const header = document.createElement('h1');
  const headerContent = document.createTextNode('Develop. Preview. Ship.  ');
  header.appendChild(headerContent);
  app.appendChild(header);
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앱의 사이즈와 팀이 커지면 이런 식으로 어플리케이션을 만드는 것은 엄청난 도전이 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 접근법으로는, 개발자가 컴퓨터가 원하는 일을 하게 만들기 위한 명령어를 작성하는데 엄청난 시간을 들여야 한다. 그런데 여러분은 그냥 &lt;b&gt;무엇을&lt;/b&gt; 보여주고 싶은지만 설명하고, 컴퓨터가 &lt;b&gt;어떻게&lt;/b&gt; DOM을 업데이트하는지 알아서 하도록 하면 좋지 않을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Imperative vs Declarative Programming (명령형 vs 선언형 프로그래밍)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 &lt;b&gt;명령형 프로그래밍&lt;/b&gt;의 좋은 예제이다. 유저 인터페이스가 &lt;b&gt;어떻게&lt;/b&gt; 업데이트되어야 하는지 스텝 별로 작성하는 것이다. 그러나 유저 인터페이스를 구축할 때는, 선언적 접근법이 종종 선호되는데, 왜냐하면 개발 프로세스를 더 빠르게 할 수 있기 때문이다. DOM 메소드를 직접 적는 대신에 개발자가 그들이 &lt;b&gt;무엇을&lt;/b&gt; 보여주고 싶은지 선언할 수 있게 된다면 매우 도움이 될 것이다. (이 경우에 h1 태그와 특정 텍스트가 될 것이다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다르게 말하면 &lt;b&gt;명령형 프로그래밍&lt;/b&gt;은 쉐프에게 피자를 만드는 법에 대해 단계적인 가이드를 주는 것이다. &lt;b&gt;선언형 프로그래밍&lt;/b&gt;은 피자를 만드는데 필요한 순서들에 대해서 신경쓰지 않고 피자를 주문하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자가 선언적으로 유저 인터페이스를 구현하는데 도움을 주는 유명한 라이브러리가 바로 &lt;a href=&quot;https://beta.reactjs.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;리액트&lt;/a&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;React: A declarative UI library (리액트: 선언적인 UI 라이브러리)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발자로서, 여러분은 리액트에게 유저 인터페이스에서 어떤 일이 일어나길 원하는지 말할 수 있고, 리액트는 여러분을 대신해서 DOM을 &lt;b&gt;어떻게&lt;/b&gt; 업데이트할 것인지 단계별로 알아낼 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend</category>
      <category>Next.js</category>
      <category>react</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/231</guid>
      <comments>https://im-developer.tistory.com/231#entry231comment</comments>
      <pubDate>Sun, 22 May 2022 15:08:00 +0900</pubDate>
    </item>
    <item>
      <title>[Kasra Khosravi] Why you should use SWC (and not Babel) 한글 번역</title>
      <link>https://im-developer.tistory.com/230</link>
      <description>&lt;figure id=&quot;og_1646548572178&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Why you should use SWC (and not Babel) - LogRocket Blog&quot; data-og-description=&quot;In this post, we'll cover the basics of transpilers and we'll compare Babel and SWC based on setup, execution, and speed.&quot; data-og-host=&quot;blog.logrocket.com&quot; data-og-source-url=&quot;https://blog.logrocket.com/why-you-should-use-swc/#:~:text=In%20general%2C%20we%20see%20a,multi%2Dcore%20async%20operation%20process&quot; data-og-url=&quot;https://blog.logrocket.com/why-you-should-use-swc/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/X7Kpr/hyNC13IvPA/RMuZGClAdX9qXkcTwKFU60/img.png?width=730&amp;amp;height=411&amp;amp;face=0_0_730_411,https://scrap.kakaocdn.net/dn/eu0CqN/hyNCTxQs15/CbaKM4NSDFtWXEIzM45fB0/img.png?width=1200&amp;amp;height=677&amp;amp;face=0_0_1200_677,https://scrap.kakaocdn.net/dn/iU3xw/hyNBU6b4zi/8fwfaLR4hkPIJ7mCH5s7IK/img.png?width=730&amp;amp;height=411&amp;amp;face=0_0_730_411&quot;&gt;&lt;a href=&quot;https://blog.logrocket.com/why-you-should-use-swc/#:~:text=In%20general%2C%20we%20see%20a,multi%2Dcore%20async%20operation%20process&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://blog.logrocket.com/why-you-should-use-swc/#:~:text=In%20general%2C%20we%20see%20a,multi%2Dcore%20async%20operation%20process&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/X7Kpr/hyNC13IvPA/RMuZGClAdX9qXkcTwKFU60/img.png?width=730&amp;amp;height=411&amp;amp;face=0_0_730_411,https://scrap.kakaocdn.net/dn/eu0CqN/hyNCTxQs15/CbaKM4NSDFtWXEIzM45fB0/img.png?width=1200&amp;amp;height=677&amp;amp;face=0_0_1200_677,https://scrap.kakaocdn.net/dn/iU3xw/hyNBU6b4zi/8fwfaLR4hkPIJ7mCH5s7IK/img.png?width=730&amp;amp;height=411&amp;amp;face=0_0_730_411');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Why you should use SWC (and not Babel) - LogRocket Blog&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;In this post, we'll cover the basics of transpilers and we'll compare Babel and SWC based on setup, execution, and speed.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;blog.logrocket.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 글은 &lt;a href=&quot;https://blog.logrocket.com/author/kasrakhosravi/&quot;&gt;Kasra Khosravi&lt;/a&gt;가 작성한 &lt;b&gt;Why you should use SWC (and not Babel)&lt;/b&gt;이란 글을 한글로 번역한 글입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오역이 있을 수 있으니 정확한 내용을 읽고 싶다면 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;a style=&quot;color: #006dd7;&quot; href=&quot;https://blog.logrocket.com/why-you-should-use-swc/#:~:text=In%20general%2C%20we%20see%20a,multi%2Dcore%20async%20operation%20process&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;원문&lt;/a&gt;&lt;/span&gt;을 읽어보시길 바랍니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;왜 여러분은 (바벨이 아니라) SWC를 사용해야 하는가?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;바벨은 무엇인가?&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://babeljs.io/docs/en/&quot; data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;Babel&lt;/span&gt;&lt;/a&gt;은 &lt;a href=&quot;https://www.w3schools.com/js/js_es6.asp&quot; data-token-index=&quot;2&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;ES6&lt;/span&gt;&lt;/a&gt;와 같은 자바스크립트 최신 버전의 코드를 이 전 버전의 코드로 &lt;a href=&quot;https://www.digitalocean.com/community/tutorials/javascript-transpilers-what-they-are-why-we-need-them&quot; data-token-index=&quot;4&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;변환&lt;/span&gt;&lt;/a&gt;시켜주는 도구이며, 심지어 &lt;a href=&quot;https://iamturns.com/typescript-babel/&quot; data-token-index=&quot;6&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;TypeScript&lt;/span&gt;&lt;/a&gt;를 변환시켜 주기도 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바벨은 여러분이 정의한 설정들을 바탕으로 소스 코드를 읽은 후, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions&quot;&gt;arrow functions&lt;/a&gt; 또는 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining&quot;&gt;optional chaining&lt;/a&gt;과 같은 신규 피쳐를 컴파일한다. 이 작업은 바벨의 세 가지 메이저 툴과 함께 진행된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;첫 째로, 바벨의 &lt;a href=&quot;https://github.com/babel/babel/tree/master/packages/babel-parser&quot;&gt;parser&lt;/a&gt;가 자바스크립트 코드를 컴퓨터가 이해할 수 있는 소스 코드 구조인 &lt;a href=&quot;https://en.wikipedia.org/wiki/Abstract_syntax_tree&quot;&gt;Abstract Syntax Tree (AST)&lt;/a&gt;로 변환한다.&lt;/li&gt;
&lt;li&gt;그 다음, 바벨의 &lt;a href=&quot;https://github.com/babel/babel/tree/master/packages/babel-traverse&quot;&gt;traverser&lt;/a&gt;가 AST를 탐색하며 바벨 설정에 정의된 대로 코드를 수정한다.&lt;/li&gt;
&lt;li&gt;마지막으로 바벨의 &lt;a href=&quot;https://github.com/babel/babel/tree/master/packages/babel-generator&quot;&gt;generator&lt;/a&gt;가 수정된 AST를 보통의 코드로 번역한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;228&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oIyNv/btrvhmZs47p/iChQfW1gGBKStE841vrSO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oIyNv/btrvhmZs47p/iChQfW1gGBKStE841vrSO0/img.png&quot; data-alt=&quot;Source:&amp;amp;amp;amp;nbsp; https://www.sitepoint.com/understanding-asts-building-babel-plugin/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oIyNv/btrvhmZs47p/iChQfW1gGBKStE841vrSO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoIyNv%2FbtrvhmZs47p%2FiChQfW1gGBKStE841vrSO0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;880&quot; height=&quot;228&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;228&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Source:&amp;amp;amp;nbsp; https://www.sitepoint.com/understanding-asts-building-babel-plugin/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;(Rust로 쓰여진) 바벨의 대체품&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://swc-project.github.io/&quot;&gt;SWC&lt;/a&gt; 또한 자바스크립트를 위한 트랜스파일러이고, &lt;a href=&quot;https://www.rust-lang.org/&quot;&gt;Rust&lt;/a&gt;로 쓰여졌으며, &lt;a href=&quot;https://github.com/swc-project/website/blob/master/pages/blog/perf-swc-vs-babel.mdx&quot;&gt;Babel보다 훨씬 더 빠르다&lt;/a&gt;. Rust는 뛰어난 성능과 신뢰성으로 잘 알려져있으며, 여러 업무 코드에 부분적으로나 전체적으로 사용되도록 권장되어 왔다. 예를 들어:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.mozilla.org/en-US/&quot;&gt;Firefox&lt;/a&gt;는 Quantum CSS라고 불리는 CSS 렌더러를 다시 작성하기로 결정했고, &lt;a href=&quot;https://hacks.mozilla.org/2017/08/inside-a-super-fast-css-engine-quantum-css-aka-stylo/&quot;&gt;부분적인 성능 향상&lt;/a&gt;을 얻었다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.tilde.io/&quot;&gt;Tilde&lt;/a&gt;는 그들의 Java HTTP 엔드포인트의 특정 부분을 Rust로 재작성했으며, &lt;a href=&quot;https://stackoverflow.blog/2020/01/20/what-is-rust-and-why-is-it-so-popular/&quot;&gt;5GB의 메모리 사용을 50MB로 줄이며&lt;/a&gt; 굉장한 성능 향상을 얻었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Rust가 매우 성능이 뛰어난 이유 중 하나는 Rust만의 &lt;a href=&quot;https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;가비지 컬렉션&lt;/span&gt;&lt;/a&gt;을 다루는 방법인, 더 이상 사용되지 않는 데이터 객체에 의한 메모리 사용을 해소하는 메모리 관리 접근법에 있다. Rust는 컴파일 단계에서 어떤 리소스가 더 이상 필요하지 않은지 결정하여 계속해서 실행할 필요가 없고, 따라서 프로세싱 시간은 줄어들고 성능은 향상된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모두가 알다시피, 코드 트랜스파일링은 비용이 많이 드는 프로세스이고, 그것이 바로 Rust로 쓰여진 트랜스파일러를 사용하면 더 성능이 향상될 수 있는 이유이다. 우리는 더 멀리 살펴볼 것이지만, 우선 우리가 트랜스파일러를 필요로 하는지부터 결정해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;왜 우리는 트랜스파일러를 필요로 하는가?&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 트랜스파일러를 굳이 필요로 하지 않는 경우에 대한 예시이다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;만약 여러분이 대부분을 ES3와 같이 잘 지원되는 자바스크립트 버전을 사용하는 간단한 프로젝트를 구축하고 있는 경우이다. 예를 들어, 이러한 코드를 실행하면 거의 대부분의 브라우저에서 잘 작동할 것이므로, 여러분의 자바스크립트 사용이 대체로 이렇다면, 트랜스파일러 없이 괜찮을 것이다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1646548980938&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// we print it, but we don't agree with it
function saySomething (something) {
    console.log(`${something}. But don't tell anyone.`);
}

saySomething(&quot;I don't like Javascript!&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;여러분이&amp;nbsp;구축하는&amp;nbsp;간단한&amp;nbsp;프로젝트가&amp;nbsp;화살표&amp;nbsp;함수와&amp;nbsp;같이&amp;nbsp;자바스크립트&amp;nbsp;최신&amp;nbsp;버전의&amp;nbsp;것에&amp;nbsp;의존하지만,&amp;nbsp;지원해야&amp;nbsp;하는&amp;nbsp;브라우저&amp;nbsp;&amp;nbsp;또한&amp;nbsp;그&amp;nbsp;새로운&amp;nbsp;피쳐들을&amp;nbsp;지원하는&amp;nbsp;경우이다.&amp;nbsp;예를&amp;nbsp;들어,&amp;nbsp;아래의&amp;nbsp;코드를&amp;nbsp;Chrome&amp;nbsp;최신&amp;nbsp;버전(45+)에서&amp;nbsp;실행하는&amp;nbsp;것은&amp;nbsp;괜찮을&amp;nbsp;것이다:&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;730&quot; data-origin-height=&quot;223&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/r0WKO/btrvbiK0BzA/wdMDexIyaqqK3lFCaOYlZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r0WKO/btrvbiK0BzA/wdMDexIyaqqK3lFCaOYlZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r0WKO/btrvbiK0BzA/wdMDexIyaqqK3lFCaOYlZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fr0WKO%2FbtrvbiK0BzA%2FwdMDexIyaqqK3lFCaOYlZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;730&quot; height=&quot;223&quot; data-origin-width=&quot;730&quot; data-origin-height=&quot;223&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1646548997071&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// we print it, but we don't agree with it
const saySomething = something =&amp;gt; {
  console.log(`${something}. But don't tell anyone.`);
};

saySomething(&quot;I don't like Javascript!&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 경우를 제외하고는 어플리케이션에서 트랜스파일러는 꼭 필요하다. 브라우저는 &lt;a href=&quot;https://v8.dev/&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;V8&lt;/span&gt;&lt;/a&gt;&amp;nbsp;(Chrome),&amp;nbsp;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey&quot; data-token-index=&quot;3&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;SpiderMonkey&lt;/span&gt;&lt;/a&gt;&amp;nbsp;(Firefox), 그리고&amp;nbsp;&lt;a href=&quot;https://en.wikipedia.org/wiki/Chakra_(JScript_engine)&quot; data-token-index=&quot;5&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;Chakra&lt;/span&gt;&lt;/a&gt;&amp;nbsp;(IE)와 같이 여러 다른 종류의 자바스크립트 엔진을 사용한다. 이 것은 준준바스크립트 명세를 사용하더라도, 여러 다른 브라우저들이 해당 표준을 지원하는 타이밍과 지원해주는 정도가 광범위하게 다를 것이라는 것을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 것이 바로 무언가를 깨뜨리거나 새로운 피쳐 사용 기회를 잃지 않으면서 여러 다른 브라우저를 넘어 일관적으로 자바스크립트 코드를 핸들링하는 것이 필요한 이유이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리의 트랜스파일러에 대한 의존은 &lt;a href=&quot;https://www.w3schools.com/js/js_es6.asp&quot;&gt;ES6&lt;/a&gt;&amp;nbsp;또는&amp;nbsp;&lt;a href=&quot;https://www.typescriptlang.org/&quot;&gt;TypeScript&lt;/a&gt;를&amp;nbsp;&lt;a href=&quot;https://www.w3schools.com/js/js_es5.asp&quot;&gt;ES5&lt;/a&gt;로 변환하는 것에 그치지 않는다; 트랜스파일러는 자바스크립트의 미래를 오늘날의 우리들에게 가져다 주며, 우리가 &lt;a href=&quot;https://blog.logrocket.com/5-es2019-features-you-can-use-today/&quot;&gt;ES2019&lt;/a&gt;와 같은 다양한 케이스의 자바스크립트 전환을 다룰 수 있게 해준다. 이 것은 오늘날의 자바스크립트 개발자를 위한 매우 강력한 도구이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 우리는 왜 우리가 트랜스파일러를 필요로 하는지 정리해보았다. 이제 간단한 셋업과 함께 SWC 사용을 테스트해보고 바벨과의 상대적인 성능, 스피드의 차이를 비교해 볼 시간이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;SWC 사용법&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SWC는 NPM 패키지 매니저로 설치해서 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫째로, 여러분의 디렉토리 루트에 다음 명령어를 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1646549064633&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// use `cd` to go to the right directory and then run
mkdir swc_project

// initialize a package.json
npm init

// install swc core as well as its cli tool
npm install --save-dev @swc/core @swc/cli&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸 실행함으로써, 이제 우리는 SWC core와 CLI를 함께 설치할 수 있다. &lt;a href=&quot;https://swc.rs/docs/usage-cli&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;CLI 패키지&lt;/span&gt;&lt;/a&gt;는 터미널에서 명령어로 실행될 수 있고, &lt;a href=&quot;https://swc.rs/docs/usage/core&quot; data-token-index=&quot;3&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;코어 패키지&lt;/span&gt;&lt;/a&gt;는 빌드 셋업 단계에서 우릴 도울 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 단계로, 자바스크립트 파일을 컴파일 하기 위해 CLI 도구에 집중하자. 루트 디렉토리에 아래와 같은 자바스크립트 파일이 존재한다고 생각해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1646549087239&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//  async.js
const fetch = require(&quot;node-fetch&quot;);

async function getData() {
    let res = await fetch(&quot;https://jsonplaceholder.typicode.com/todos/1&quot;);
    let json = await res.json();
    console.log('data', json);
}

getData();
// result:
// ▶Object {userId: 1, id: 1, title: &quot;delectus aut autem&quot;, completed: false}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 파일을 트랜스파일하기 위해 아래의 명령어를 실행할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1646549103432&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 이 커맨드를 실행하면 stdout으로 트랜스파일된 데이터가 만들어질 것이다.
// 그리고 터미널에 프린트될 것이다.
npx swc async.js

// 아래 명령어를 실행하면 `output.js`라고 불리는 새로운 파일을
// 트랜스파일된 데이터와 함께 생성할 것이다.
npx swc async.js -o output.js

// 아래 명령어를 실행하면 `transpiledDir`라는 이름의 디렉토리가 생성되고
// 기존 dir에 있는 모든 파일이 트랜스파일될 것이다.
npx swc src -d transpiledDir&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;노트 &amp;mdash;&lt;/b&gt; 트랜스파일된 파일이 어떻게 생겼는지 보기 위해 &lt;a href=&quot;https://swc-playground.now.sh/&quot;&gt;SWC playground&lt;/a&gt;를 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 두 번째 단계로, 빌드 시스템 안에 SWC를 도구로써 포함시킬 것이다. 이를 위해, 우리는 더 향상된 설정 가능한 빌더로써 &lt;a href=&quot;https://webpack.js.org/&quot;&gt;Webpack&lt;/a&gt;을 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 첫째로, Webpack과 SWC 셋업 시 &lt;b&gt;&lt;span style=&quot;background-color: #dddddd; color: #eb5757;&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;package.json&lt;/span&gt;&lt;/b&gt;이 어떻게 생겼을지 살펴보자. 이 셋업과 함께, &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #eb5757;&quot; data-token-index=&quot;3&quot; data-reactroot=&quot;&quot;&gt;npm run-script build&lt;/span&gt;&lt;/b&gt;&lt;/span&gt; 를 실행해서 Webpack이 우리의 패키지를 빌드하도록 만든다; 추가적으로 우리는 Webpack이 우리 어플리케이션을 서빙하도록 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #eb5757;&quot; data-token-index=&quot;5&quot; data-reactroot=&quot;&quot;&gt;npm run-script start&lt;/span&gt;&lt;/b&gt;&lt;/span&gt; 를 실행할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1646549163545&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;swc-project&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;scripts&quot;: {
    &quot;test&quot;: &quot;echo \&quot;Error: no test specified\&quot; &amp;amp;&amp;amp; exit 1&quot;,
    &quot;build&quot;: &quot;rm -rf ./dist &amp;amp;&amp;amp; webpack&quot;,
    &quot;start&quot;: &quot;webpack-dev-server&quot;
  },
  &quot;license&quot;: &quot;MIT&quot;,
  &quot;devDependencies&quot;: {
    &quot;@swc/core&quot;: &quot;^1.1.39&quot;,
    &quot;css-loader&quot;: &quot;^3.4.0&quot;,
    &quot;html-loader&quot;: &quot;^0.5.5&quot;,
    &quot;html-webpack-plugin&quot;: &quot;^3.2.0&quot;,
    &quot;sass-loader&quot;: &quot;^8.0.0&quot;,
    &quot;style-loader&quot;: &quot;^1.1.1&quot;,
    &quot;swc-loader&quot;: &quot;^0.1.9&quot;,
    &quot;webpack&quot;: &quot;^4.41.4&quot;,
    &quot;webpack-cli&quot;: &quot;^3.3.10&quot;,
    &quot;webpack-dev-server&quot;: &quot;^3.10.1&quot;
  },
  &quot;dependencies&quot;: {
    &quot;node-fetch&quot;: &quot;2.6.0&quot;,
    &quot;react&quot;: &quot;^16.12.0&quot;,
    &quot;react-dom&quot;: &quot;^16.12.0&quot;,
    &quot;regenerator-runtime&quot;: &quot;^0.13.5&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어플리케이션을 빌드하고 시작하기 위한 위 설정은 &lt;a href=&quot;http://&amp;lt;https://webpack.js.org/configuration/&amp;gt;&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;webpack.config.js&lt;/a&gt;&amp;nbsp;파일에 저장되어, Webpack에 의해 자동적으로 픽업될 것이다. 이 파일에서 다음과 같은 몇 가지 것들이 설정된다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;[&lt;a href=&quot;https://webpack.js.org/configuration/output/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;output&lt;/a&gt;]: Webpack이 번들 파일들, assets, 그리고 모든 트랜스파일된 파일을 포함한 파일들을 산출할 장소와 이름을 세팅한다.&lt;/li&gt;
&lt;li&gt;[&lt;a href=&quot;https://webpack.js.org/configuration/dev-server/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;devServer&lt;/a&gt;]: 어디에 컨텐츠를 서빙할 것인지부터 요청을 listen할 포트 정의까지 설정하여 Webpack app을 이 서빙한다.&lt;/li&gt;
&lt;li&gt;[&lt;a href=&quot;https://webpack.js.org/plugins/html-webpack-plugin/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;HTMLWebpackPlugin&lt;/a&gt;]: 우리는 이 플러그인을 정의하여 Webpack 번들이 포함된 HTML 파일을 서빙하는 프로세스를 쉽게 만든다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 이 설정에서 가장 중요한 부분은 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #eb5757;&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;.js&lt;/span&gt;&lt;/b&gt;&lt;/span&gt; 또는 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #eb5757;&quot; data-token-index=&quot;3&quot; data-reactroot=&quot;&quot;&gt;.jsx&lt;/span&gt;&lt;/b&gt;&lt;/span&gt; 파일 확장자를 가지는 자바스크립트 파일을 컴파일 할 수 있게 해주는 &lt;a href=&quot;https://swc-project.github.io/docs/usage-swc-loader&quot; data-token-index=&quot;5&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;swc-loader&lt;/span&gt;&lt;/a&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1646549241576&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// global dependencies
const path = require('path');
const HTMLWebpackPlugin = require(&quot;html-webpack-plugin&quot;);

module.exports = {
  mode: &quot;development&quot;,
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: 'index_bundle.js'
  },
  devServer: {
    contentBase: path.join(__dirname, 'dist'),
    compress: true,
    port: 9000
  },
  module: {
    rules: [
        {
        test: /\.jsx?$/ ,
        exclude: /(node_modules|bower_components)/,
        use: {
            // `.swcrc` in the root can be used to configure swc
            loader: &quot;swc-loader&quot;
        }
      },
      {
        test: /\.html$/,
        use: [
          {
            loader: &quot;html-loader&quot;,
            options: { minimize: true }
          }
        ]
      },
      {
        test: /\.scss/i,
        use: [&quot;style-loader&quot;, &quot;css-loader&quot;, &quot;sass-loader&quot;]
      }
    ]
  },
  plugins: [
    new HTMLWebpackPlugin({
      filename: &quot;./index.html&quot;,
      template: path.join(__dirname, 'public/index.html')
    })
  ]
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Webpack 설정에 셋업된 &lt;b&gt;&lt;span style=&quot;background-color: #dddddd; color: #eb5757;&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;swc-loader&lt;/span&gt;&lt;/b&gt;와 함께, 우리는 자바스크립트 파일을 트랜스파일하는 여정의 절반은 지나왔다. 그러나, 우리는 아직 SWC가 어떻게 우리 파일들을 트랜스파일하는지 알 필요가 있다. SWC는 &lt;a href=&quot;https://swc-project.github.io/docs/configuring-swc.html&quot; data-token-index=&quot;3&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;.swcrc&lt;/span&gt;&lt;/a&gt;라고 불리는 설정 파일을 루트 디렉토리에 정의하는 방식으로 바벨과 비슷한 접근법이라고 할 수 있다. TypeScript를 트랜스파일하는 프로젝트에서 이 설정들이 어떻게 보이는지 살펴 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 설정에서, 우리는 &lt;b&gt;&lt;span style=&quot;background-color: #dddddd; color: #eb5757;&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;.ts&lt;/span&gt;&lt;/b&gt; 확장자 파일만 매칭되도록 하는 &lt;a href=&quot;https://en.wikipedia.org/wiki/Regular_expression&quot; data-token-index=&quot;3&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;Regex&lt;/span&gt;&lt;/a&gt;로써 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #eb5757;&quot; data-token-index=&quot;5&quot; data-reactroot=&quot;&quot;&gt;test&lt;/span&gt;&lt;/b&gt;&lt;/span&gt; 설정을 사용할 것이다. 추가적으로 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #eb5757;&quot; data-token-index=&quot;7&quot; data-reactroot=&quot;&quot;&gt;jsx.parser&lt;/span&gt;&lt;/b&gt;&lt;/span&gt; 설정과 함께 SWC에서 어떤 parser가 (typescript / ecmascript 등의) 트랜스파일에 사용되는지 살펴볼 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 우리의 이용 사례를 위해 어떤 트랜스파일 옵션들이 의도적으로 사용되었는지 정의함으로써, syntax 파싱에 여전히 더 많은 통제권을 가지고 있다. 예를 들어, 이 예시에서 우리는 Typescript &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/decorators.html&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;decorators&lt;/span&gt;&lt;/a&gt;와 &lt;a href=&quot;https://basarat.gitbook.io/typescript/project/dynamic-import-expressions&quot; data-token-index=&quot;3&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;dynamic imports&lt;/span&gt;&lt;/a&gt; 트랜스파일에 관심을 보이고 있지만, &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #eb5757;&quot; data-token-index=&quot;5&quot; data-reactroot=&quot;&quot;&gt;.tsx&lt;/span&gt;&lt;/b&gt;&lt;/span&gt; 확장자 파일을 트랜드파일하는 것은 무시하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1646549262303&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// .swcrc

{
  &quot;test&quot;: &quot;.*.ts$&quot;,
  &quot;jsc&quot;: {
    &quot;parser&quot;: {
      &quot;syntax&quot;: &quot;typescript&quot;,
      &quot;tsx&quot;: false,
      &quot;decorators&quot;: true,
      &quot;dynamicImport&quot;: true
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 위 webpack SWC 예시에서 &lt;a href=&quot;https://reactjs.org/&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;React&lt;/span&gt;&lt;/a&gt;를 사용하길 원한다고 생각해보자. 알다시피 리액트에서는 리액트 컴포넌트를 작성하기 위해 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #eb5757;&quot; data-token-index=&quot;3&quot; data-reactroot=&quot;&quot;&gt;.jsx&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;라는 특정한 파일 확장자를 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1646549281126&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// App.jsx

// global dependencies
import React from 'react';
import ReactDOM from 'react-dom';

const App = () =&amp;gt; {
  return &amp;lt;h1&amp;gt;My SWC App&amp;lt;/h1&amp;gt;;
};

ReactDOM.render(&amp;lt;App /&amp;gt;, document.querySelector(&quot;#root&quot;));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Webpack을 통해 이 파일을 서빙하기 위해서는 우리가 이미 위에서 정의한 것과 같은 올바른 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #eb5757;&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;webpack loader&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;가 필요하다. 또한 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #eb5757;&quot; data-token-index=&quot;3&quot; data-reactroot=&quot;&quot;&gt;.swcrc&lt;/span&gt;&lt;/b&gt;&lt;/span&gt; 파일에 올바른 트랜스파일 세팅을 하는 것 또한 필요하다. 이제 이 접근법과 함께, 우리는 트랜스파일 시에 모던 자바스크립트 (&lt;a href=&quot;https://medium.com/@selvaganesh93/javascript-whats-new-in-ecmascript-2019-es2019-es10-35210c6e7f4b&quot; data-token-index=&quot;5&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;ES2019&lt;/span&gt;&lt;/a&gt;) 최신 피쳐를 사용하면서 그와 더불어 .jsx 파일 또한 지원할 수 있다. 게다가 리액트 프로젝트를 위해 추가적인 트랜스파일 세팅이 필요하다면, &lt;a href=&quot;https://swc.rs/docs/configuration/swcrc&quot; data-token-index=&quot;7&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;손쉽게 사용할 수 있는 다양한 세팅&lt;/span&gt;&lt;/a&gt;이 준비되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1646549296351&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// .swcrc

{
    &quot;jsc&quot;: {
      &quot;parser&quot;: {
        &quot;syntax&quot;: &quot;ecmascript&quot;,
        &quot;jsx&quot;: true
      }
    }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;바벨과 SWC의 속도 비교&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 전에 얘기했듯이, 트랜스파일러 속도는 이 것이 빌드 프로세스에 반영되기 때문에 매우 중요하다. 그리고 많은 개발자들에게 이 단계에 걸리는 시간을 아끼는 것은 매우 중요한 문제이다. 이제 이 두 가지 도구가 스피드 면에서 어떻게 다른지 비교해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫째로, 우리는 바벨과 SWC로 동기적으로 코드 변환을 실행시키는 인위적인 방법으로 두 개를 비교했다. 알다시피 자바스크립트는 &lt;a href=&quot;https://www.red-gate.com/simple-talk/dotnet/asp-net/javascript-single-threaded/&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;싱글 스레드&lt;/span&gt;&lt;/a&gt;이고 실생활 어플리케이션에서 동기적으로 무거운 계산을 실행하는 것은 불가능하다. 그러나 이 방식은 여전히 우리에게 속도 비교의 지표를 제공해줄 것이다. (SWC 프로젝트의 메인테이너에 의해 실행된 테스트에서) 싱글 코어 CPU로 실행했을 때 벤치마크 비교를 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style14&quot;&gt;
&lt;tbody style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Transform&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Speed(operation/second)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Sample Runs&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;SWC (ES3)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;616 ops/sec&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;88&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Babel (ES5)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;34.05 ops/sec&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;58&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 결과는 SWC로 더 비용이 큰 ES3 변환 프로세스를 진행했음에도 불구하고, SWC 트랜스파일 속도가 바벨에 비해 월등히 눈에 띈다는 것을 보여준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제, 더 현실적인 시나리오를 벤치마크해보면, 자바스크립트에서 다뤄지는 실제 시나리오이면서 더 비용이 큰 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;await&amp;nbsp;Promise.all()&lt;/span&gt;&lt;/a&gt; 샘플을 실행해볼 수 있다. 이 벤치마크에서, CPU 코어의 수와 그에 평행하는 연산이 작용한다. 다른 벤치마크를 보면, 두 가지 실험이 진행되었다. 두 가지 모두 &lt;span data-token-index=&quot;3&quot; data-reactroot=&quot;&quot;&gt;8 CPU 코어&lt;/span&gt; 컴퓨터가 사용되었으며, &lt;span data-token-index=&quot;5&quot; data-reactroot=&quot;&quot;&gt;4개의 평행 연산&lt;/span&gt;이 수행되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 실험에서는 &lt;b&gt;&lt;span data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;4&lt;/span&gt;&lt;/b&gt;개의 프로미스가 실행되었다:&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style14&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Transform&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Speed(operation/second)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Sample runs&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;SWC (ES3)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;1704&lt;span&gt;&amp;nbsp;&lt;/span&gt;ops/sec&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;73&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Babel (ES5)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;27.28&lt;span&gt;&amp;nbsp;&lt;/span&gt;ops/sec&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;40&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째 실험에서는 &lt;b&gt;&lt;span data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;100&lt;/span&gt;&lt;/b&gt;개의 프로미스가 실행되었다:&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style14&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Transform&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Speed(operation/second)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Sample runs&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;SWC (ES3)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;2199&lt;span&gt;&amp;nbsp;&lt;/span&gt;ops/sec&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;54&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;Babel (ES5)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;32&lt;span&gt;&amp;nbsp;&lt;/span&gt;ops/sec&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;6&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;노트 &amp;mdash;&lt;/b&gt; 여러분이 직접 테스트를 실행하고 벤치마크 비교하는 것에 관심이 있다면, 이 &lt;a href=&quot;https://github.com/swc-project/node-swc&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;repository&lt;/span&gt;&lt;/a&gt;를 클론하고 터미널에 다음 커맨드를 실행해보길 바란다:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1646549523121&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// clone and cd into the cloned repository
cd node-swc

// Node.js benchmark runner, modelled after Mocha and bencha, based on Benchmark.js.
npm i benchr -g

// run the multicore.js or any other benchmarks under /benches directory
benchr ./benches/multicore.js&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 숫자들에서 우리가 주목해야 할 부분은 바벨의 성능이 비동기 작업을 수행할 때 떨어진다는 것이다. 이 것은 SWC가 &lt;a href=&quot;https://nodejs.org/api/worker_threads.html&quot; data-token-index=&quot;1&quot; data-reactroot=&quot;&quot;&gt;&lt;span&gt;worker thread&lt;/span&gt;&lt;/a&gt;에서 실행되면서 CPU 코어의 수를 잘 늘리는 것과 대조된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전반적으로 두 가지 도구의 속도 차이를 분명히 보았다. SWC는 싱글 스레드 CPU 코어 베이스에서 바벨보다 20배 더 빨랐고, 멀티 코어 비동기 작업 프로세스에서는 약 60배 정도 더 빨랐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;결론&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서 트랜스파일러에 대한 기본 지식을 다루었고, 셋업, 실행 그리고 속도 측면에서 자바스크립트의 두 가지 트랜스파일러를 비교해보았다. 이 글에서 우리는 다음의 것들을 배웠다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;두 가지 방식의 빌드 워크플로우가 매우 비슷하다.&lt;/li&gt;
&lt;li&gt;그러나 SWC는 바벨에 비해 커다란 속도 이점이 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 여러분이 바벨을 사용하고 있고 더 빠른 빌드 시간을 얻기 위해 SWC로의 전환을 고려하고 있다면 다음의 것들을 확인해보자:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 피쳐가 SWC에서 이미 전체적으로 또는 부분적으로 지원되는지, 혹은 전혀 지원되지 않는지 확인해 보자.&lt;/li&gt;
&lt;li&gt;여러분의 빌드 시스템이 SWC를 지원하는지 확인해 보자. (Webpack에선 지원해주더라도 Parcel과 같은 다른 툴에선 지원해주지 않을 수 있다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무튼 SWC와 같은 아이디어는 매우 유망해보이며, 앞으로의 발전에 계속 눈을 기울여야 할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span data-token-index=&quot;0&quot; data-reactroot=&quot;&quot;&gt;Resources&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.reddit.com/r/programming/comments/a8i4ar/swc_superfast_alternative_for_babel/&quot;&gt;https://www.reddit.com/r/programming/comments/a8i4ar/swc_superfast_alternative_for_babel/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.blog/2020/01/20/what-is-rust-and-why-is-it-so-popular/&quot;&gt;https://stackoverflow.blog/2020/01/20/what-is-rust-and-why-is-it-so-popular/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/@rajithaeye/what-is-babel-8dcfdf84ea3e&quot;&gt;https://medium.com/@rajithaeye/what-is-babel-8dcfdf84ea3e&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://scotch.io/tutorials/javascript-transpilers-what-they-are-why-we-need-them&quot;&gt;https://scotch.io/tutorials/javascript-transpilers-what-they-are-why-we-need-them&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/swc-project/swchttps://hacks.mozilla.org/2017/08/inside-a-super-fast-css-engine-quantum-css-aka-stylo/&quot;&gt;https://github.com/swc-project/swchttps://hacks.mozilla.org/2017/08/inside-a-super-fast-css-engine-quantum-css-aka-stylo/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/@boolean/swc-super-fast-javascript-transpiler-written-in-rust-f8f436195cb8&quot;&gt;https://medium.com/@boolean/swc-super-fast-javascript-transpiler-written-in-rust-f8f436195cb8&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/@pramonowang/transpilers-do-you-really-need-it-e9c63686e5fe&quot;&gt;https://medium.com/@pramonowang/transpilers-do-you-really-need-it-e9c63686e5fe&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://news.ycombinator.com/item?id=17175186&quot;&gt;https://news.ycombinator.com/item?id=17175186&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://nicholasjohnson.com/blog/what-is-babel/https://www.sitepoint.com/understanding-asts-building-babel-plugin/&quot;&gt;http://nicholasjohnson.com/blog/what-is-babel/https://www.sitepoint.com/understanding-asts-building-babel-plugin/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Frontend</category>
      <category>swc</category>
      <category>바벨</category>
      <category>트랜스파일러</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/230</guid>
      <comments>https://im-developer.tistory.com/230#entry230comment</comments>
      <pubDate>Sun, 6 Mar 2022 15:56:02 +0900</pubDate>
    </item>
    <item>
      <title>2년차 웹 프론트엔드 개발자의 2021년 회고</title>
      <link>https://im-developer.tistory.com/229</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;667&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bf4hZA/btrr9yWB3vW/ctVJSMUioF7vNzFgKGegT0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bf4hZA/btrr9yWB3vW/ctVJSMUioF7vNzFgKGegT0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bf4hZA/btrr9yWB3vW/ctVJSMUioF7vNzFgKGegT0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbf4hZA%2Fbtrr9yWB3vW%2FctVJSMUioF7vNzFgKGegT0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;512&quot; height=&quot;342&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;667&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정말 오랜만에 블로그 글을 쓰려니까 어색하다. 글도 자꾸 쓸 버릇을 해야 잘 써지는데 간만에 쓰려니 손부터 굳은 느낌이다. 왜 그 동안 아무런 소식이 없었냐면 회사 일이 많이 바쁘기도 했지만 결국 내 성향에서 기인한 것 같다. 블로그 글을 올릴 때 너무 퀄리티가 떨어지는 글을 올리고 싶지 않아서 오랜 시간을 소모하는 편인데, 너무 긴 시간을 들이는게 엄두가 안나서 아예 시작을 안하게 되었다고 할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부트캠프에서 수강생 멘토링을 가끔 하고 있는데 나랑 비슷한 성향의 사람들이 은근히 많이 있다는걸 알게 되었다. 이런 성향은 흔히 &amp;ldquo;완벽주의&amp;rdquo;라는 단어로 지칭된다. 어떤 일을 할 때 내가 원하는 정도의 퀄리티 수준을 유지하기 위해 스스로에게 굉장히 엄격한 기준을 두고 일을 시작하는데, 그 과정에서 시간과 노력을 아주 많이 쓰는 편이다. 그래서 일의 결과물에는 만족하는 편이지만 문제는 일을 하는 동안 너무 에너지가 많이 소모되기 때문에 시작하기도 전에 피해버릴 때가 있다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 부분을 극복하기 위해서 내가 제일 많이 하고 있는 일은 스스로를 너무 몰아 붙이지 않으려고 노력하는 것이다. 특히 어떤 목표를 잡을 때 목표치를 아주 작게 잡는 것이 중요하다. 목표치를 최대한 작게 가져가는 대신 가능하면 꾸준히 하려고 노력한다. 누군가는 꿈은 크게 가져야한다고 할 수도 있다. 맞는 말이지만 당장의 목표를 너무 크고 원대하게 지정하면 뭐부터 해야할 지 감이 안오고 더럭 겁부터 나게 된다. 그래서 일단 스스로의 욕심을 내려놓고 작은 목표를 끝까지 달성하는 것이 더 중요하다고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하여튼 서론이 주절 주절 길었는데... 오늘의 글은 짧더라도 뭐라도 글을 써보자는 작고 소박한 새해 목표를 달성하기 위한 글이다. 새해를 맞아 지난 2021년을 되돌아보는 시간을 가져보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;  Continue&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;일정 조율하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올 해 하반기에 특히나 무지막지하게 회사 일이 바빴다. 마치 테트리스 블록이 쌓이듯이 일감이 쌓이는 느낌을 받았다. 하나를 쳐내면 하나가 쌓이고 정말 공장 컨베이어 벨트 돌리듯이 실험들이 진행되었다. 이렇게 일이 잔뜩 쌓여있고 빨리 개발해 달라고 재촉이 오면 초조해져서 시간 관리하기가 어려운데 이럴수록 더더욱 냉정하게 일정을 정할 필요가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 제일 중요한 것은 나 스스로를 과대 평가하지 않는 것이다. 내가 며칠 풀로 야근을 하더라도 예기치 못한 다른 일들이 중간에 끼어들어서 내 개발 시간을 빼앗아갈 수 있으므로 모든 일정은 최악의 상황을 고려해서 정해야 한다. 항상 최상의 컨디션으로 하루 8시간 이상 풀로 개발했을 때를 가정해서 일정을 잡으면 안된다는 뜻이다. 만약 일정을 내가 컨트롤할 수 없다면 개발 스펙을 조정해야 한다. 작은 단위로 나눠서 우선 순위가 낮은 것들을 스펙 아웃시키는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올 해 잘했다고 생각하는 점은 개발 마일스톤을 정할 때 최대한 내가 해야 하는 작업을 사전에 세밀하게 나누고 시작했다는 점이다. 잘게 task를 쪼갤 수록 각 작업에 걸리는 시간을 정확하게 산출할 수 있고, 우선 순위를 정하기 수월해진다. 개발 작업을 하기 전에 잘게 쪼개놓은 작업들을 JIRA 티켓으로 미리 만들어두고 정리해두면 개발할 때도 보다 가시성있게 개발을 할 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;성공적인 두 개의 스터디&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2021년에 새로 시작한 두 개의 스터디를 올 한해 꾸준히 잘 유지하고 있어서 개인적으로 매우 뿌듯하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 번째 스터디는 1월부터 4명의 스터디 멤버와 하고 있는 &lt;a href=&quot;https://www.notion.so/1-2-c50dd9f7645946f7912d6226610e3345&quot;&gt;영어 번역 스터디&lt;/a&gt;이다. 영어로 된 좋은 개발 블로그 글을 꾸준히 번역하고 공유하는 스터디인데, 영어와 좀 더 친숙해지고 좋은 개발 블로그 글도 많이 읽는 1석 2조의 효과를 누려보자는 취지로 만들게 되었다. 스터디는 성공적으로 잘 안착해서 1월부터 현재까지 꾸준히 이어지고 있다. 중간에 흐지부지되지 않고 1년 동안 잘 될 수 있었던 이유는 목표를 작게 가져간 것 덕분이라고 생각한다. 2주에 한 번 이상, 한 번 할 때 5줄 이상 번역하는 것이 최소한의 목표치이다. 소박하게 운영하다보니 회사 다니면서도 부담 없이 스터디에 참여할 수 있었다. 그리고 무엇보다 기한을 어기면 벌금 3만원이 차감된다는 무시무시한 벌금 정책도 한몫했다. 후후&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 번째 스터디는 알고리즘 스터디인데 바닐라코딩 부트캠프 수료생 분들과 모여서 하고 있고, 회사에서도 동료 몇 명과 같이 하고 있다. 이 스터디 덕분에 알고리즘 문제 풀기와 담 쌓고 지내던 내가 일주일에 1개~3개의 알고리즘 문제를 꼬박 꼬박 풀고 있다. &lt;a href=&quot;https://github.com/jy7123943/LeetCodeAlgorithm&quot;&gt;방금 몇 개 풀었는지 한 번 세봤는데&lt;/a&gt; 스터디 덕분에 올 해 60개의 문제를 풀었다. 문제를 푸는 것 뿐만 아니라 다른 사람들이 어떤 방식으로 푸는지 꾸준히 보고 설명 듣는 것이 정말 크게 도움이 되었다. 2022년에도 꾸준히 참여하고 싶은 스터디이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;b&gt;&lt;b&gt; &amp;zwj;♀️ &lt;/b&gt;&lt;/b&gt;Stop, Start&amp;nbsp;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;불편한 것을 그냥 지나치지 않기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사에서 업무를 하면서 좋은 동료들에게서 배울 점이 정말 많다고 느낀다. 그들이 하는 멋진 일들을 보면서 &amp;ldquo;저 사람은 저렇게 할 생각을 어떻게 했을까?&amp;rdquo;를 많이 생각하곤 하는데 곰곰히 생각해보니 그 것들이 다 &amp;ldquo;불편함을 그냥 넘기지 않는 것&amp;rdquo;에서 기인한다는 것을 알게 되었다. 무슨 말이냐면 예를 들어서 JIRA 티켓을 만들 때 티켓에 Sprint와 Assignee 항목을 매 번 기입해야만 한다고 해보자. 이 부분이 상당히 귀찮은 일이었는데 아무 생각없이 매 번 티켓을 생성하고 수동으로 입력을 해왔다고 생각해보자. 그런데 누군가가 Automation 기능으로 티켓이 생성되면 자동으로 Sprint와 Assignee가 현재의 Active sprint와 티켓 생성자로 입력되도록 설정을 해놓는 것이다. 이 사소한 작업 하나로 팀원 모두의 생산성이 높아질 수 있었다. 즉, &amp;ldquo;귀찮고 성가신 일을 그냥 내버려두지 않고 어떻게 해서든 해결해보려고 하는 노력&amp;rdquo;이 좋은 결과물을 만들어낸다는 것을 느꼈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 이후로 나도 업무 도중에 발생하는 여러 불편함, 그리고 내가 개발한 프로덕트를 사용해보면서 느끼는 사소한 불편함을 그냥 지나치지 않으려고 노력하기 시작했다. 최근에 내가 만든 화면을 사용하다가 탭을 일일이 클릭하면서 화면을 보는게 너무 불편해서 스와이프로 넘기도록 UX를 개선한 사례가 있는데 이런 작은 노력들을 더 많이 할 필요성을 느끼고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;사이드 프로젝트를 해보자&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올 해의 목표는 뭐라도 사이드 프로젝트를 해보는 것이다. 사실 사이드 프로젝트는 모든 개발자의 로망이 아닐까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사에서 개발하는 프로덕트에는 도입하지 못하는, 그러나 해보고 싶은 여러가지 신기하고 재밌는 것들을 사용해서 만들어보고 싶다. 이건 올 해는 꼭 해봐야겠다는 생각이 들어서 벌써 예전에 같은 부트캠프 기수였던 분들 몇 명을 꼬셔서 사이드 프로젝트 모임을 시작했다. 부디 잘 마무리 되어서 2022년 회고 때 Continue 항목에 사이드 프로젝트를 언급할 수 있었으면 좋겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이거 많고도 사실 회고 포인트는 많지만 그러면 글이 너무 장황하게 길어질테고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쓰는 사람도 읽는 사람도 지칠테니 이만 글을 마치려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 나 자신에게 2021년 한 해 동안 정말 수고 많았다고 해주고 싶다. :)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Etc</category>
      <category>회고</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/229</guid>
      <comments>https://im-developer.tistory.com/229#entry229comment</comments>
      <pubDate>Mon, 31 Jan 2022 22:04:34 +0900</pubDate>
    </item>
    <item>
      <title>자바스크립트, 동기 vs 비동기 (Synchronous vs Asynchronous)</title>
      <link>https://im-developer.tistory.com/228</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;2000&quot; data-filename=&quot;2942529.jpg&quot; width=&quot;302&quot; height=&quot;302&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/E99yZ/btrbCt4vrd5/hz8GiKt0u4OCSqVa4jjSY0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/E99yZ/btrbCt4vrd5/hz8GiKt0u4OCSqVa4jjSY0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/E99yZ/btrbCt4vrd5/hz8GiKt0u4OCSqVa4jjSY0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FE99yZ%2FbtrbCt4vrd5%2Fhz8GiKt0u4OCSqVa4jjSY0%2Fimg.jpg&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;2000&quot; data-filename=&quot;2942529.jpg&quot; width=&quot;302&quot; height=&quot;302&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;싱글 스레드(Single Thread) 언어&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트는 싱글 스레드 언어라고 불린다. 여기서 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;스레드(Thread)&lt;/b&gt;&lt;/span&gt;는 프로세스 내에서 실행되는 흐름을 최소 단위를 말한다. 그리고 스레드는 자신만의 프로그램 카운터와 시스템 레지스터, 그리고 스택을 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;프로그램 카운터는 다음에 실행될 명령어를 계속 추적하는 역할을 하고, 레지스터는 CPU가 요청을 처리하는 데 필요한 데이터를 일시적으로 가지고 있는 기억 장치이며 스택은 함수 호출 시에 전달되는 인자나 실행 종료 후 되돌아갈 주소 값, 함수 내의 변수 등을 저장하는 메모리 공간이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1500&quot; data-origin-height=&quot;976&quot; width=&quot;415&quot; height=&quot;270&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btVS2g/btrbvayepsK/R43t3EjOhNkKdaKMx5dl7K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btVS2g/btrbvayepsK/R43t3EjOhNkKdaKMx5dl7K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btVS2g/btrbvayepsK/R43t3EjOhNkKdaKMx5dl7K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtVS2g%2FbtrbvayepsK%2FR43t3EjOhNkKdaKMx5dl7K%2Fimg.png&quot; data-origin-width=&quot;1500&quot; data-origin-height=&quot;976&quot; width=&quot;415&quot; height=&quot;270&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스레드는 프로세스에 할당된 메모리나 자원들을 공유하며, 각각의 스레드가 독립적으로 작업을 수행하기 위해 각자의 레지스터, 스택을 가진다. 스레드는 라이트웨이트 프로세스라고도 불린다. 병행 처리로 어플리케이션의 성능을 향상시킬 수 있기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 자바스크립트 언어에 대해서 생각해보자. 자바스크립트 엔진은 하나의 스레드를 사용하는 싱글 스레드 방식인데, 즉 그 말은 하나의 레지스터, 카운터, 스택을 가지며 한 번에 하나의 작업만 수행한다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트에서 어떤 함수를 호출하면 함수에 전달되는 인자, 함수 내에서 사용하는 변수, 그리고 함수의 실행이 종료되고 되돌아갈 주소 값 등을 실행 컨텍스트라는 객체로 생성한 후에 이 것을 콜 스택에 push한다. 스택에 push되면 바로 함수가 실행되며, 실행이 종료되고 나면 실행 컨텍스트는 다시 pop되어 제거된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1500&quot; data-origin-height=&quot;854&quot; width=&quot;469&quot; height=&quot;267&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bri5s3/btrbwhjAJ7a/D6WzlG36748gVqNutbzlV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bri5s3/btrbwhjAJ7a/D6WzlG36748gVqNutbzlV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bri5s3/btrbwhjAJ7a/D6WzlG36748gVqNutbzlV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbri5s3%2FbtrbwhjAJ7a%2FD6WzlG36748gVqNutbzlV0%2Fimg.png&quot; data-origin-width=&quot;1500&quot; data-origin-height=&quot;854&quot; width=&quot;469&quot; height=&quot;267&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;동기 (Synchronous)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 콜 스택을 가지는 자바스크립트에서 어떤 작업에 1분이라는 시간이 소요된다면 다음 작업은 1분을 기다려야만 실행될 수 있다. 이렇게 앞선 작업의 소요 시간으로 인해 다음 함수의 실행이 중단되는 것을 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;Blocking&lt;/b&gt;&lt;/span&gt;이라고 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1628427374566&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function delay(num) {
  let i = 0;
  while(i &amp;lt; num) {
    i++;
  }
  console.log('delay executed');
}

function foo() {
  console.log('1:  ', new Date());
}

function bar() {
  console.log('2:  ', new Date());
}

foo();
delay(10000000000);
bar();&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;386&quot; data-origin-height=&quot;60&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dJqRj4/btrbxqmRGTN/NE92CVcwMZhIL4ZNAvvSvk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dJqRj4/btrbxqmRGTN/NE92CVcwMZhIL4ZNAvvSvk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dJqRj4/btrbxqmRGTN/NE92CVcwMZhIL4ZNAvvSvk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdJqRj4%2FbtrbxqmRGTN%2FNE92CVcwMZhIL4ZNAvvSvk%2Fimg.png&quot; data-origin-width=&quot;386&quot; data-origin-height=&quot;60&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;delay 함수로 인해 bar 함수는 약 8~9초의 시간이 흐른 후에 실행되었다. 이렇게 현재 실행되고 있는 작업이 종료될 때까지 다음 실행할 작업을 대기하는 것을 동기 처리라고 한다. 동기 방식의 장점은 모든 작업을 순차적으로 진행하기 때문에 작업 순서가 보장된다는 점이다. 대신 앞의 작업이 진행되는 동안 다음 작업들이 지연된다는 단점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;비동기 (Asynchronous)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트가 모든 작업을 동기 방식으로만 처리한다면 생각만 해도 답답해진다. 우리가 웹사이트에서 새로운 작업을 할 때마다 화면이 블로킹될 것이고 유저는 답답해하면서 페이지를 이탈해버릴 것이다. 다행히 자바스크립트에는 비동기 처리 방식이 존재한다. 비동기 방식은 쉽게 말해서 어떤 식당에서 앞의 사람 음식이 나오지 않았더라도 일단 다음 사람의 주문을 계속 받는 방식을 말한다. 주문을 계속 받은 후 먼저 준비된 사람의 음식부터 서빙하는 것이다. &lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;즉, 앞선 작업이 종료되지 않아도 기다리지 않고 다음 작업을 실행하는 것을 말한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1628428589862&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function delay(ms) {
  console.log('delay start');

  setTimeout(() =&amp;gt; {
    console.log('delay executed');
  }, ms);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;383&quot; data-origin-height=&quot;144&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bncB3k/btrbyHWE3ku/n1KDlszEdIsa2xuVNFuwo0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bncB3k/btrbyHWE3ku/n1KDlszEdIsa2xuVNFuwo0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bncB3k/btrbyHWE3ku/n1KDlszEdIsa2xuVNFuwo0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbncB3k%2FbtrbyHWE3ku%2Fn1KDlszEdIsa2xuVNFuwo0%2Fimg.png&quot; data-origin-width=&quot;383&quot; data-origin-height=&quot;144&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아까 전의 delay 함수를 setTimeout으로 변경해보자. 코드는 foo 함수를 실행한 후에 delay를 실행한다. 그리고 delay의 실행이 종료되는 것을 기다리지 않고 바로 bar 함수를 실행한다. bar의 실행이 끝난 후에 콜스택이 비워지면 delay가 실행되고 난 이 후 5초가 흐른 시점에 setTimeout의 콜백 함수가 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 비동기 함수는 앞선 작업이 종료되지 않아도 바로 다음 작업을 실행하여 Blocking을 해결한다는 장점이 있지만 작업의 실행 속도가 보장되지 않는다는 단점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 흔히 사용하는 setTimeout, setInterval과 같은 타이머 함수, HTTP 요청, 이벤트 핸들러는 브라우저에서 제공하는 Web API인데 모두 비동기 방식으로 처리된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이벤트 루프 (Event Loop)&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1459&quot; data-origin-height=&quot;844&quot; width=&quot;497&quot; height=&quot;287&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpkFe0/btrbocDINCe/k8SPEvbqmYNEAURr5VWGt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpkFe0/btrbocDINCe/k8SPEvbqmYNEAURr5VWGt1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpkFe0/btrbocDINCe/k8SPEvbqmYNEAURr5VWGt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcpkFe0%2FbtrbocDINCe%2Fk8SPEvbqmYNEAURr5VWGt1%2Fimg.png&quot; data-origin-width=&quot;1459&quot; data-origin-height=&quot;844&quot; width=&quot;497&quot; height=&quot;287&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 자바스크립트 엔진은 크게 콜 스택과 힙 영역으로 나뉜다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;u&gt;&lt;b&gt;콜 스택(Call Stack)&lt;/b&gt;&lt;/u&gt;은 앞에서 설명했듯이 전역이나 함수 코드 등을 평가하면서 생성한 실행 컨택스트 객체를 push하거나 pop할 수 있는 자료 구조이다.&lt;/li&gt;
&lt;li&gt;&lt;u&gt;&lt;b&gt;힙(Heap)&lt;/b&gt;&lt;/u&gt;은 객체가 저장되는 메모리 공간으로 콜 스택에 저장되는 실행 컨택스트는 힙에 저장된 객체를 참조한다. 객체는 원시 값과는 다르게 런 타임 단계에서 동적으로 메모리 공간의 크기가 결정되는데, 이 때문에 힙은 구조화되어 있지 않다는 특징이 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트 엔진은 이처럼 단순히 함수가 실행되면 순차적으로 콜 스택에 추가와 제거를 반복하면서 정해진 순서대로 작업을 진행한다. 그러다가 비동기 작업을 만나면 비동기 작업의 스케쥴링은 브라우저나 Node.js에 위임하는데, 브라우저에서는 비동기 작업의 처리를 위해 이벤트 루프와 태스트 큐를 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;u&gt;&lt;b&gt;태스트 큐(Task Queue)&lt;/b&gt;&lt;/u&gt;는 비동기 함수의 콜백 함수나 이벤트 핸들러가 일시적으로 보관되는 자료 구조이다. 프로미스의 후속 처리 메소드의 콜백 함수는 태스트 큐가 아니라 마이크로 태스트 큐(Microtask Queue)에 저장되는데 참고로 마이크로 태스트 큐가 우선 순위가 높다. 따라서 setTimeout과 promise 중에서 promise의 콜백 함수가 더 먼저 실행된다.&lt;/li&gt;
&lt;li&gt;&lt;u&gt;&lt;b&gt;이벤트 루프(Event Loop)&lt;/b&gt;&lt;/u&gt;는 비동기 함수의 스케쥴링을 담당한다. 일단 콜 스택에 현재 실행 중인 실행 컨택스트가 있는지, 그리고 태스트 큐나 마이크로 태스트 큐에 대기 중인 함수가 있는지 반복해서 확인한다. 그 후에 콜 스택이 비어있으면서 태스트 큐에 대기 중인 함수가 있다면 선입 선출로 태스트 큐의 작업을 콜 스택으로 이동시켜서 실행시킨다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(이벤트 루프에 대해서는 예전에 작성한 글,&amp;nbsp;&lt;a href=&quot;https://im-developer.tistory.com/113&quot;&gt;자바스크립트,&amp;nbsp;이벤트&amp;nbsp;루프(Event&amp;nbsp;Loop)와&amp;nbsp;동시성(concurrency)에&amp;nbsp;대하여&lt;/a&gt;라는 글에 조금 더 자세하게 정리해두었다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;NOTE:&lt;/b&gt; 자바스크립트가 싱글 스레드 언어라고 했는데, 이 때 싱글 스레드 방식으로 동작하는 것은 브라우저가 아니라 브라우저에 내장된 자바스크립트 엔진만을 말한다는 것을 기억해두자. 자바스크립트 엔진은 싱글 스레드로 동작하지만 브라우저는 멀티 스레드로 동작한다. setTimeout, setInterval과 같은 타이머 함수, HTTP 요청, 이벤트 핸들러 모두 브라우저에서 제공하는 Web API라는 것을 기억하자.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend</category>
      <category>동기</category>
      <category>비동기</category>
      <category>자바스크립트</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/228</guid>
      <comments>https://im-developer.tistory.com/228#entry228comment</comments>
      <pubDate>Sun, 8 Aug 2021 22:55:39 +0900</pubDate>
    </item>
    <item>
      <title>웹 렌더링 방식 (SSR, CSR, SSG) 알아보기</title>
      <link>https://im-developer.tistory.com/227</link>
      <description>&lt;h3 id=&quot;e834&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;초창기 웹 렌더링&lt;/b&gt;&lt;/h3&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;초창기에는 모든 웹 페이지가 정적인 페이지였다. 대부분의 로직은 서버에서 수행되었고, 브라우저는 서버로부터 전달받은 정적인 HTML과 CSS를 단순히 렌더링하는 방식으로 동작했다. 즉, 우리가 웹 사이트에 접속하면 브라우저는 서버에 간단한 HTTP 요청을 전송하고, 서버로부터 전달 받은 HTML을 렌더링하는 방식인 것이다. 화면에 어떠한 변화를 주려고 화면을 전환하면 그 때마다 서버로부터 새로운 HTML을 전송 받아서 다시 렌더링해야 했다. 당연히 매 번 처음부터 새로 렌더링하기 때문에 성능적인 문제도 많았고 화면이 전환될 때마다 화면이 깜박이는 등의 문제가 있었다.&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Ajax의 등장&lt;/b&gt;&lt;/h3&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;그러다 1999년에 자바스크립트를 통해서 서버와 브라우저가 비동기로 데이터를 주고 받을 수 있는 Ajax가 등장하게 되었다. Ajax는 새로운 패러다임의 전환을 일으켰는데, 매 번 전체 페이지에 대한 데이터를 가져올 필요 없이 필요한 부분의 데이터만 불러와서 동적으로 웹 사이트 화면을 변경할 수 있게 된 것이다. 항상 새롭게 렌더링하지 않아도 되기 때문에 깜박임 없이 자연스러운 화면 전환이 가능해졌고, 더 나은 UX를 제공할 수 있게 되었다. 이제 서버에서만 이루어지던 다양한 로직 처리나 HTML 생성을 상당수 클라이언트에서 처리할 수 있게 되었다.&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;이런 과정 속에서 클라이언트 사이드에서 쉽게 화면 처리를 할 수 있도록 여러 가지 라이브러리나 프레임워크가 만들어지기 시작했다. 2006년에는 jQuery가 등장하면서 어려운 DOM 조작을 쉽게 컨트롤할 수 있게 되었다. 오늘날에는 훨씬 복잡하고 거대해진 웹 어플리케이션을 보다 쉽게 구축할 수 있도록 도와주는 Angular, React, Vue 등의 다양한 프레임워크가 활발하게 사용되고 있다.&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Client Side Rendering(CSR)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;React와 같은 다양한 프론트엔드 프레임워크들이 유행하면서 클라이언트 사이드에서 전적으로 웹 렌더링을 책임지는 방식이 보편화되었다. 이제 서버에서 아무 내용이 없는 빈 HTML 껍데기를 보내면 클라이언트에서 동적으로 태그들과 스타일을 생성하여 페이지를 채운다. 이렇게 되면 최초에 HTML을 받는 것 외에는 페이지 렌더링을 위해서 별도의 HTTP 통신을 할 필요가 없어진다. 따라서 서버는 오직 Ajax를 통해 필요한 데이터를 주고 받는 역할만 수행한다. 이러한 방식을 &lt;b&gt;Single Page Applications(SPAs)&lt;/b&gt;라고 부른다. 즉, 최초에 Single Page만 서버로부터 불러와 렌더링하고 그 이후에는 모든 것을 클라이언트 사이드에서 렌더링하는 어플리케이션이다.&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1003&quot; data-origin-height=&quot;838&quot; width=&quot;367&quot; height=&quot;306&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qHOtD/btraXXMfPDY/kQqLUtoxSaIGuYPhgeTsk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qHOtD/btraXXMfPDY/kQqLUtoxSaIGuYPhgeTsk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qHOtD/btraXXMfPDY/kQqLUtoxSaIGuYPhgeTsk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqHOtD%2FbtraXXMfPDY%2FkQqLUtoxSaIGuYPhgeTsk0%2Fimg.png&quot; data-origin-width=&quot;1003&quot; data-origin-height=&quot;838&quot; width=&quot;367&quot; height=&quot;306&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;문제는 클라이언트 사이드 렌더링 방식 또한 여러 가지 문제점들을 야기한다는 것이다. 예를 들면 &lt;b&gt;SEO(Search Engine Optimization)&lt;/b&gt; 문제가 있다. 구글 크롤러와 같은 여러 웹 크롤러들은 웹 사이트의 HTML을 읽어들이고 인덱싱하여 검색 엔진이 해당 페이지를 잘 찾아낼 수 있도록 한다. 그런데 클라이언트 사이드 렌더링 방식으로 만들어진 웹 사이트는 최초에 빈 HTML만 렌더링하기 때문에 크롤러들이 제대로 컨텐츠를 읽어들일 수 없다는 문제가 있다. (최근에는 대부분의 웹 크롤러들이 클라이언트 사이드에서 필요로하는 자바스크립트를 실행하고 있어 이러한 문제들이 어느 정도 해소되기는 했으나 여전히 완벽하지는 않다.)&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;그 외에도 페이지 렌더링을 위해 많은 양의 자바스크립트 코드가 실행되다보니 성능적인 이슈도 생겨났다. 특히 클라이언트 사이드 렌더링 방식은 첫 페이지 로딩이 느리다. 왜냐하면 최초로 서버에서 받은 빈 HTML은 페이지 로드에 필요한 자바스크립트를 참조하고 있는데, 클라이언트에서 렌더링할 때 페이지 로드에 필요한 자바스크립트 코드, 프레임워크나 라이브러리 소스 코드를 모두 불러오기 때문이다. (물론 페이지를 한 번 렌더링하고 난 이후에는 필요한 부분만 렌더링하기 때문에 비교적 효율적이다.)&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;이러한 문제점들이 대두되자 다시 서버 사이드 렌더링을 사용하자는 움직임이 일어났다. 물론 예전과 같은 방식으로 서버 언어를 사용하여 마크업을 생성하지는 않는다. 이제 React와 같은 모던 자바스크립트 라이브러리나 프레임워크를 사용하여 서버 사이드 렌더링을 할 수 있기 때문이다. 서버 사이드 렌더링은&amp;nbsp;마크업 생성을 클라이언트 디바이스에서 하는 것이 아니라 서버에서 한다는 점이 다르다. 이 방식은 클라이언트 사이드 렌더링보다 더 빠르고 SEO 문제를 해결해준다는 장점이 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Modern Server Side Rendering(SSR)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;서버 사이드 렌더링은 위에서도 언급했지만 다음 그림과 같이 진행된다. 브라우저가 서버에 페이지를 요청하면 서버가 필요한 데이터로 HTML을 구성하여 브라우저에 전송한다. 그러면 브라우저에서 응답으로 받은 HTML을 그대로 렌더링한다.&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;974&quot; data-origin-height=&quot;356&quot; width=&quot;419&quot; height=&quot;153&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dMsfhR/btra080QqOf/KFB04M0UTMtghnjZFjRs8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dMsfhR/btra080QqOf/KFB04M0UTMtghnjZFjRs8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dMsfhR/btra080QqOf/KFB04M0UTMtghnjZFjRs8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdMsfhR%2Fbtra080QqOf%2FKFB04M0UTMtghnjZFjRs8k%2Fimg.png&quot; data-origin-width=&quot;974&quot; data-origin-height=&quot;356&quot; width=&quot;419&quot; height=&quot;153&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;애초에 서버에서 모든 컨텐츠를 로드하여 전달하기 때문에 SEO 면에서 이점이 있다. 또한 첫 페이지 로딩이 빨라지는데, 이미 페이지 로드에 필요한 데이터를 서버에서 미리 불러왔기 때문에 클라이언트 사이드에서 별도의 자바스트립트 코드를 더 불러올 필요가 없기 때문이다.&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;그러나 서버 사이드 렌더링 또한 장점만 존재하는 것은 아니다. 서버에서 매 번 동적으로 계산하여 페이지를 렌더링하기 때문에 서버 부하가 생겨날 수 있으며 서버 비용도 많이 든다. 또한 서버로부터 매 번 페이지를 새로 전달받기 때문에 서버에서 페이지를 생성하는 시간이 소요된다.&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;사람들은 이제 이런 생각을 하기 시작했다. 유저의 데이터에 따라 다르게 보여주는 페이지의 경우는 매 번 서버에서 동적으로 생성할 필요가 있지만, 모든 유저에게 항상 같은 내용을 보여주는 페이지는 매 번 동적으로 생성할 필요가 없지 않을까? 바로 이러한 질문에서 Static Site Generation이 탄생했다.&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Static Site Generation(SSG)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 회사 소개 페이지같은 경우 모든 유저에게 항상 동일한 화면이 보이기 때문에 매 번 동적으로 생성할 필요가 없다. 즉, 한 번만 생성한 이후에 CDN으로 어딘가에 저장해두고 필요할 때마다 로드하면 되는 것이다. 웹 개발에 React와 같은 프레임워크를 사용하고 있다면 Next.js나 Gatsby.js 등의 도움을 빌려 아주 쉽게 정적 페이지를 생성할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSG는 다음과 같은 과정으로 생성된다. React에 Next.js나 Gatsby.js 등의 라이브러리를 사용하여 개발한 정적 웹 페이지를 빌드한 후 산출물을 CDN으로 배포한다. 이제 필요할 때마다 CDN으로부터 전달받은 정적 페이지를 유저에게&amp;nbsp;빠르게 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 이러한 정적 페이지가 동적으로 매 번 새롭게 추가되는 경우에는 어떻게 될까? 예를 들어 Gatsby로 개발된 블로그에 새로운 포스트를 계속 업로드한다고 생각해보자. 새롭게 포스트가 업로드되면 그 때 마다 블로그는 다시 빌드되어야 한다. 다행히도 이러한 작업을 매 번 수동으로 직접 해 줄 필요는 없다. 현대 기술을 사용하면 정적 페이지에 무언가 변화가 생길 때마다 매우 쉽게 다시 빌드하여 배포할 수 있다.&lt;/p&gt;
&lt;p id=&quot;701e&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 id=&quot;897a&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Jamstack&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSG와 함께 &lt;a href=&quot;https://jamstack.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Jamstack&lt;/a&gt;이라는 새로운 웹 아키텍쳐가 생겨났다. &lt;b&gt;Jamstack&lt;/b&gt;에서 Jam은 &lt;b&gt;J&lt;/b&gt;avaScript, &lt;b&gt;A&lt;/b&gt;PI, &lt;b&gt;M&lt;/b&gt;arkup의 약자이다. Jamstack은 서버를 전혀 사용하지 않고 정적인 페이지를 만들어 저렴한 비용으로 호스팅하는 방식을 말한다. 즉, 모든 웹 페이지는 빌드 타임에 정적 페이지로 최적화되며 CDN으로부터 서빙되어 적은 비용으로 쉽게 관리된다. 높은 유지 비용이 필요한 서버를 사용하지 않아도 된다는 점에서 많은 이점이 있다. Jamstack 개발을 쉽게 할 수 있도록 도와주는 여러 기술들이 존재하는데 Gatsby, Hugo, Jekyll, Next.js, Netlify 등이 있다. 이런 기술들을 사용하여 배포된 정적 블로그에 새로운 글이 업로드되면 자동으로 새로운 빌드와 배포가 진행되며, 그 과정이 모두 끝나면 유저에게 새로운 컨텐츠가 보여지게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지 웹을 렌더링하는 다양한 방식에 대해 살펴보았다. 웹 사이트를 개발하다보면 특정 페이지는 서버 사이드에서 렌더링하고, 특정 페이지는 정적 페이지로 렌더링하고 싶을 때가 있다. 물론 클라이언트 사이드에서 렌더링하고 싶을 수도 있다. Next.js와 같은 기술을 사용하면 페이지 별로 원하는 방식을 선택하여 개발할 수 있다. 요즘 회사에서 내가 담당한 서비스를 모두 Next.js로 마이그레이션하는 작업을 진행하고 있는데 기존의 클라이언트 사이드 렌더링 방식으로만 작업되었을 때보다 훨씬 더 빠릿해진 페이지를 보며 많은 뿌듯함을 느끼고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Reference&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모던 자바스크립트 Deep Dive / 이웅모 지음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://betterprogramming.pub/server-side-rendering-vs-static-site-generation-53a34872728c&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://betterprogramming.pub/server-side-rendering-vs-static-site-generation-53a34872728c&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1627734368405&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Server-Side Rendering vs. Static Site Generation&quot; data-og-description=&quot;What&amp;rsquo;s the hype?&quot; data-og-host=&quot;betterprogramming.pub&quot; data-og-source-url=&quot;https://betterprogramming.pub/server-side-rendering-vs-static-site-generation-53a34872728c&quot; data-og-url=&quot;https://betterprogramming.pub/server-side-rendering-vs-static-site-generation-53a34872728c&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/UhqUX/hyK4EXkoUp/ufjiNiFzEUuydIZQdkEYKK/img.jpg?width=1200&amp;amp;height=794&amp;amp;face=0_0_1200_794,https://scrap.kakaocdn.net/dn/cNYj42/hyK3N2EMRx/Ccdpk3GjAifrB3ackzFrc1/img.jpg?width=1400&amp;amp;height=927&amp;amp;face=0_0_1400_927,https://scrap.kakaocdn.net/dn/484BA/hyK4RClpRG/bI8VuSdPDq7TPABkKke6qK/img.jpg?width=1400&amp;amp;height=785&amp;amp;face=0_0_1400_785&quot;&gt;&lt;a href=&quot;https://betterprogramming.pub/server-side-rendering-vs-static-site-generation-53a34872728c&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://betterprogramming.pub/server-side-rendering-vs-static-site-generation-53a34872728c&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/UhqUX/hyK4EXkoUp/ufjiNiFzEUuydIZQdkEYKK/img.jpg?width=1200&amp;amp;height=794&amp;amp;face=0_0_1200_794,https://scrap.kakaocdn.net/dn/cNYj42/hyK3N2EMRx/Ccdpk3GjAifrB3ackzFrc1/img.jpg?width=1400&amp;amp;height=927&amp;amp;face=0_0_1400_927,https://scrap.kakaocdn.net/dn/484BA/hyK4RClpRG/bI8VuSdPDq7TPABkKke6qK/img.jpg?width=1400&amp;amp;height=785&amp;amp;face=0_0_1400_785');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Server-Side Rendering vs. Static Site Generation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;What&amp;rsquo;s the hype?&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;betterprogramming.pub&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://kirillibrahim.medium.com/gray-area-on-when-to-use-different-rendering-modes-csr-ssr-ssg-214a636a24a4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://kirillibrahim.medium.com/gray-area-on-when-to-use-different-rendering-modes-csr-ssr-ssg-214a636a24a4&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1627740181467&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Gray Area on When to use Different Rendering Modes CSR, SSR, SSG&quot; data-og-description=&quot;Introduction:&quot; data-og-host=&quot;kirillibrahim.medium.com&quot; data-og-source-url=&quot;https://kirillibrahim.medium.com/gray-area-on-when-to-use-different-rendering-modes-csr-ssr-ssg-214a636a24a4&quot; data-og-url=&quot;https://kirillibrahim.medium.com/gray-area-on-when-to-use-different-rendering-modes-csr-ssr-ssg-214a636a24a4&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bEWuwT/hyK3NhkaXI/K9Eg7wyIfl1sSJlxE1yUQk/img.jpg?width=1200&amp;amp;height=676&amp;amp;face=0_0_1200_676,https://scrap.kakaocdn.net/dn/bdQRC8/hyK4NfHr94/yDPBcUkOUMqOPhVEiIuPBK/img.jpg?width=1200&amp;amp;height=676&amp;amp;face=0_0_1200_676&quot;&gt;&lt;a href=&quot;https://kirillibrahim.medium.com/gray-area-on-when-to-use-different-rendering-modes-csr-ssr-ssg-214a636a24a4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://kirillibrahim.medium.com/gray-area-on-when-to-use-different-rendering-modes-csr-ssr-ssg-214a636a24a4&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bEWuwT/hyK3NhkaXI/K9Eg7wyIfl1sSJlxE1yUQk/img.jpg?width=1200&amp;amp;height=676&amp;amp;face=0_0_1200_676,https://scrap.kakaocdn.net/dn/bdQRC8/hyK4NfHr94/yDPBcUkOUMqOPhVEiIuPBK/img.jpg?width=1200&amp;amp;height=676&amp;amp;face=0_0_1200_676');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Gray Area on When to use Different Rendering Modes CSR, SSR, SSG&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Introduction:&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;kirillibrahim.medium.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend</category>
      <category>CSR</category>
      <category>ssg</category>
      <category>SSR</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/227</guid>
      <comments>https://im-developer.tistory.com/227#entry227comment</comments>
      <pubDate>Sun, 1 Aug 2021 00:11:47 +0900</pubDate>
    </item>
    <item>
      <title>Integration 테스트 코드가 중요한 이유 (React Testing Library, MSW로 작성해보기)</title>
      <link>https://im-developer.tistory.com/226</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;2000&quot; data-filename=&quot;2176931.jpg&quot; width=&quot;332&quot; height=&quot;332&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bp9o5D/btranbe0ZqZ/weU9WTWFevHKK3eWp2ris1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bp9o5D/btranbe0ZqZ/weU9WTWFevHKK3eWp2ris1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bp9o5D/btranbe0ZqZ/weU9WTWFevHKK3eWp2ris1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbp9o5D%2Fbtranbe0ZqZ%2FweU9WTWFevHKK3eWp2ris1%2Fimg.jpg&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;2000&quot; data-filename=&quot;2176931.jpg&quot; width=&quot;332&quot; height=&quot;332&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에 회사에서 서비스 개발 프로젝트를 맡았을 때 내가 작성했던 테스트 코드는 util성 함수들에 대한 unit 테스트 코드가 전부였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;거기에 조금 더 덧붙여서 Redux Saga의 Generator 함수에 대한 간단한 unit 테스트들을 작성하였는데 방법은 매우 간단했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Saga Generator 함수들을 실행했을 때 어떤 이펙트들이 순차적으로 실행되는지 각 단계 별로 mocking하여 테스트하는 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 때 내가 작성했던 테스트 코드 한 대목을 가져와봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1627196819027&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    describe('1. success scenario', () =&amp;gt; {
      let gen: any;

      beforeAll(() =&amp;gt; {
        gen = fetchAllSaga();
      });

      it('yield user and example API calls', () =&amp;gt; {
        expect(gen.next().value).toEqual(
          all([
            call(userAPI.get),
            call(exampleAPI.get),
          ]),
        );
      });

      it('yield example2 API call', () =&amp;gt; {
        const APIResult = [
          { user: {}},
          { data: exampleData },
        ];

        expect(gen.next(APIResult).value).toEqual(
          call(
            example2.post,
            { data: [blabla] },
          ),
        );
      });

      it('successfully update state', () =&amp;gt; {
        const APIResult = { data: [], meta: '' };

        expect(gen.next(APIResult).value).toEqual(
          put(exampleAction.fetchAllSuccess({
            user: {} as User,
            exampleData: mockExample,
            exampleData2: mockExample2,
          })),
        );
      });

      it('ends generator', () =&amp;gt; {
        expect(gen.next().done).toBeTruthy();
      });
    });&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 Saga Generator 함수의 next()의 결과물이 내가 원하는 순서대로 이루어지는지를 테스트하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 작업은&amp;nbsp;&lt;a href=&quot;https://github.com/jfairbank/redux-saga-test-plan&quot;&gt;redux-saga-test-plan&lt;/a&gt; 이란 라이브러리를 사용하면 더욱 간단해진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 어느 순간 다음과 같은 의문점이 들기 시작했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;이렇게 작성한 테스트 코드들이 정말 내가 만든 서비스의 안정성을 100% 보장해주는가?&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;테스트 코드의 목적&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 의문들에 대해서 대답하려면 우리가 테스트 코드를 왜 작성하는지 명확하게 할 필요가 있다. 우리가 테스트 코드를 쓰는 목적은 단순하다. 내가 만든 서비스를 매 번 일일이 실행한 후에 모든 기능을 손수 눌러서 확인하지 않고도 내가 의도한 대로 온전히 작동하는지 빠르게 점검하기 위함이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 내가 어떤 기능을 최초로 개발했다고 해보자. 대부분의 경우 QA 과정을 거치게 되고, 그 과정에서 모든 경우의 수를 직접 서비스를 사용해보면서 테스트할 것이다. 당연히 많은 시간과 노력이 소요된다. 이 과정에서 만약 오류를 발견했다면? 그렇다면 개발자는 그 오류의 원인을 찾아서 다시 개발물을 수정하고 배포한 후에 다시 한 번 QA 프로세스를 거쳐야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약에 QA를 모두 통과했다고 가정해보자. 그런데 새로운 기능을 개발할 때 기존에 개발한 부분과 엮이는 상황이 생길 수 있다. 혹은 기존 코드를 조금 더 개선하기 위해 리팩토링을 하면서 로직을 변경했다고 해보자. 이 때 테스트 코드가 없다면 우리는 다시 한 번 기존 기능에 대한 QA 프로세스를 거쳐야지만 해당 기능의 안정성을 검증할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 만약 QA 과정이 이루어지기 전에 서비스의 안정성을 보장할 수 있는 테스트 코드를 미리 작성해 두었고, 그 테스트 코드가 100% 통과되었다면? 당연히 QA 과정에서 오류가 발생할 확률이 현저히 줄어들 것이고, 기존 코드를 자유롭게 리팩토링하거나 새로운 기능을 개발하더라도 기존 기능에 문제가 없음을 손쉽게 증명할 수 있을 것이다. 결국 내가 작성한 테스트 코드들이 제대로 기능을 하고 있는지를 따져보려면 이러한 역할을 잘 수행하고 있는지를 되짚어보면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;통합(Integration) 테스트 코드가 필요한 이유&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 아까 위에서 언급한 Saga Generator 함수에 대한 테스트 코드나 util 함수에 대한 unit 테스트 코드에 대해 얘기해보자. 위에서 예시로 든 Saga Generator 테스트 코드는 크게 2가지 문제점을 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;첫 째, 테스트 코드가 검증해주는 영역이 매우 지엽적이다&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저 테스트 코드로 검증되는 부분은 다음과 같다. call과 같은 Saga 함수를 통해 API 호출이 발생하는 지 여부, 그리고 그 API의 response로 mock data가 반환된다고 가정했을 때 Redux 스토어에 어떤 상태값들이 저장되는 지 여부. 그 외에 API 호출이 어느 시점에 발생이 되고, 상태가 변경되면 화면에서 어떠한 일이 발생하는지에 대해서는 전혀 검증해주지 않는다. 즉, 저 테스트 코드 만으로는 우리의 어플리케이션이 잘 동작한다고 단언할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;util 함수에 대한 unit 테스트도 마찬가지이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아주 간단한 예시를 들어보자.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1627199293684&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function hasWarningItem (items) {
  return items.find(item =&amp;gt; item.type === 'warning');
}

// ...

const data = await fetch();

if (hasWarningItem(data)) {
  return &amp;lt;WarningSign /&amp;gt;;
}

return &amp;lt;NormalSign /&amp;gt;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 util 함수를 통해 배열 data의 아이템 중 warning 요소가 있으면 WarningSign 컴포넌트를 렌더링하고 그게 아니면 NormalSign을 렌더링하는 상황이라고 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 hasWarningItem 함수의 unit 테스트를 통해 인자로 들어온 배열 중에서 warning 타입이 있는 요소를 리턴하는지 안하는지 여부는 확실하게 검사할 수 있다. 그러나 아래와 같이 hasWarningItem(data)를 검사할 때 실수로 앞에 Logical Not을 붙였다면?&lt;/p&gt;
&lt;pre id=&quot;code_1627199453255&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function hasWarningItem (items) {
  return items.find(item =&amp;gt; item.type === 'warning');
}

// ...

const data = await fetch();

// hasWarningItem을 사용하는 곳에서 실수한다면?
if (!hasWarningItem(data)) {
  return &amp;lt;WarningSign /&amp;gt;;
}

return &amp;lt;NormalSign /&amp;gt;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리의 어플리케이션은 의도한 대로 동작하지 않을 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국, 각각의 unit들을 뭉쳐서 하나의 어플리케이션을 구성할 때 각 연결 부위를 검사할 수 없다면 전체 어플리케이션의 안정성은 보장할 수 없게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;둘 째, 내부 로직을 변경하면 설사 서비스에 아무 문제가 없더라도 테스트 코드가 깨져버린다.&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Saga 테스트 코드를 다시 살펴보면 정말 단순히 API를 호출 함수의 이름만 변경해도 테스트 코드가 깨진다는 것을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1627199901389&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { userAPI, exampleAPI } from '../api'; // 여기서 벌써 테스트가 깨질 것이다.

//...

    describe('1. success scenario', () =&amp;gt; {
      let gen: any;

      beforeAll(() =&amp;gt; {
        gen = fetchAllSaga();
      });

      it('yield user and example API calls', () =&amp;gt; {
        expect(gen.next().value).toEqual(
          all([
            call(userAPI.get),
            call(exampleAPI.get),
          ]),
        );
      });&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 Saga의 구조 자체를 변경하기라도 한다면 실제 서비스는 아무런 문제가 없는데도 테스트 코드가 깨지며, 일일이 유지 보수를 해주어야만 한다. 이는 사실 큰 문제이다. 테스트 코드를 유지하는데 너무 큰 비용이 들어간다는 문제도 있고, 테스트 코드의 결과에 신뢰를 할 수 없게 된다는 문제도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이러한 문제들을 해결하기 위해 우리는 다음과 같은 결론을 내릴 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;- Integration 테스트 코드를 통해 여러 모듈들이 통합된 하나의 어플리케이션이 제대로 동작하는지를 검사해야 한다.&lt;br /&gt;- 내부 코드를 어떻게 리팩토링을 하든 유저에게 보여지는 산출물이 같다면 테스트 코드는 깨지지 않아야 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;React Testing Library와 Mock Service Worker&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 Integration 테스트 코드는 어떻게 쓸 수 있을까? 나는 &lt;a href=&quot;https://jestjs.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Jest&lt;/a&gt;와&amp;nbsp;&lt;a href=&quot;https://testing-library.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;React Testing Library&lt;/a&gt;, &lt;a href=&quot;https://mswjs.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;MSW&lt;/a&gt;를 사용해서 테스트 코드를 작성하고 있는데 내가 실무에서 주로 사용하는 방법으로 간단한 어플리케이션에 대한 테스트 코드를 작성해보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기 아주 간단한 어플리케이션이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 72px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;1&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;2&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 18px; text-align: center;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;966&quot; data-origin-height=&quot;362&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/S81gX/btrao6xbLnz/pxHDaejcPReNXQKEOemZWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/S81gX/btrao6xbLnz/pxHDaejcPReNXQKEOemZWK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/S81gX/btrao6xbLnz/pxHDaejcPReNXQKEOemZWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FS81gX%2Fbtrao6xbLnz%2FpxHDaejcPReNXQKEOemZWK%2Fimg.png&quot; data-origin-width=&quot;966&quot; data-origin-height=&quot;362&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 18px; text-align: center;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;732&quot; data-origin-height=&quot;432&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqppBv/btranV31bUJ/GMJe4l2oPlTF8Bx3Sz6NTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqppBv/btranV31bUJ/GMJe4l2oPlTF8Bx3Sz6NTk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqppBv/btranV31bUJ/GMJe4l2oPlTF8Bx3Sz6NTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqppBv%2FbtranV31bUJ%2FGMJe4l2oPlTF8Bx3Sz6NTk%2Fimg.png&quot; data-origin-width=&quot;732&quot; data-origin-height=&quot;432&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;3&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;4&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 18px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;716&quot; data-origin-height=&quot;628&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/F7Dco/btraptFjzrQ/CsiLqLLbCcO1HZ7Um6Qovk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/F7Dco/btraptFjzrQ/CsiLqLLbCcO1HZ7Um6Qovk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/F7Dco/btraptFjzrQ/CsiLqLLbCcO1HZ7Um6Qovk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FF7Dco%2FbtraptFjzrQ%2FCsiLqLLbCcO1HZ7Um6Qovk%2Fimg.png&quot; data-origin-width=&quot;716&quot; data-origin-height=&quot;628&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 18px;&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;738&quot; data-origin-height=&quot;488&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BUHjU/btraqR0Btdz/QKGllH0okVS5qG98zbzr4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BUHjU/btraqR0Btdz/QKGllH0okVS5qG98zbzr4k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BUHjU/btraqR0Btdz/QKGllH0okVS5qG98zbzr4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBUHjU%2FbtraqR0Btdz%2FQKGllH0okVS5qG98zbzr4k%2Fimg.png&quot; data-origin-width=&quot;738&quot; data-origin-height=&quot;488&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 화면에서&amp;nbsp;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;Let's find!&lt;/span&gt;라는 버튼을 누르면 모달이 뜨고, 잠깐의 로딩을 거쳐 할 일에 대한 정보를 유저에게 보여준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;Close&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;버튼을 누르면 모달 창이 닫힌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자 이제 이 어플리케이션이 잘 작동하는지 검사한다고 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무엇을 테스트해야할 지 모르겠다면 회사에서 QA 과정에서 직접 기능 테스트를 진행할 때 무엇을 테스트하는지 생각해보면 쉽다. 내가 이 어플리케이션의 기능 QA를 한다고 생각하고 필요한 테스트 케이스를 작성해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1627211662302&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;test(&quot;[Let's find you something to do]라는 메시지와 [Let's find!]라는 버튼이 보인다&quot;, () =&amp;gt; {
  // write here
});

test(&quot;[Let's find!] 버튼을 클릭하면 모달창이 뜬다.&quot;, () =&amp;gt; {
  // write here
});

test(&quot;모달창에 Loading 메시지가 보이고, 데이터 로딩이 끝나면 할 일, 최소 인원, 필요한 돈 정보가 보인다&quot;, () =&amp;gt; {
  // write here
});

test(&quot;[Close] 버튼을 클릭하면 모달창이 닫힌다&quot;, () =&amp;gt; {
  // write here
});

test(&quot;데이터를 불러오다가 에러가 발생하면 Error라는 글자가 보인다&quot;, () =&amp;gt; {
  // write here
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 정도의 테스트가 통과한다면 어플리케이션 기능이 정상적으로 동작한다고 간주할 수 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 하나씩 차례대로 코드를 작성해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1627211960632&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import &quot;@testing-library/jest-dom&quot;;
// ^ jest에서 dom testing을 쉽게 할 수 있도록 제공해주는 여러 함수들을 import한다.
// toBeInTheDocument와 같은 메소드들을 사용할 수 있다.

import { render, screen } from &quot;@testing-library/react&quot;;
// ^ 컴포넌트를 render하기 위한 함수
import App from &quot;../App&quot;;
import React from &quot;react&quot;;


test(&quot;[Let's find you something to do]라는 메시지와 [Let's find!]라는 버튼이 보인다&quot;, () =&amp;gt; {
  const { getByText } = render(&amp;lt;App /&amp;gt;);

  expect(getByText(&quot;Let's find you something to do&quot;)).toBeInTheDocument();
  // ^ render 메소드의 return 객체를 destructuring하여 query문을 사용할 수 있다.
  
  expect(screen.getByRole(&quot;button&quot;, { name: &quot;Let's find!&quot; })).toBeInTheDocument();
  // 혹은 screen 객체를 import하여 사용하면 document.body 내의 모든 요소를 query한다.
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;App 컴포넌트를 렌더한 후 내가 의도한대로 텍스트가 출력하는지 쿼리문을 통해 찾는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요소가 존재하지 않는다면 getBy~ 쿼리문은 바로 에러를 throw한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1627212709885&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { fireEvent, render, screen } from &quot;@testing-library/react&quot;;

beforeEach(() =&amp;gt; {
  render(&amp;lt;App /&amp;gt;);
  // ^ 각 테스트 블록마다 새로 App을 render해야 하므로 beforeEach에 작성하여 수고로움을 덜어낸다
});

test(&quot;[Let's find!] 버튼을 클릭하면 모달창이 뜬다.&quot;, () =&amp;gt; {
  const button = screen.getByRole(&quot;button&quot;, { name: &quot;Let's find!&quot; });
  
  fireEvent.click(button);
  
  expect(getByTestId(&quot;modalContainer&quot;)).toBeInTheDocument();
  // 미리 모달창 element에 data-testid를 심어둔 후 테스트에서 사용한다.
  // ex) &amp;lt;div data-testid=&quot;modalContainer&quot;&amp;gt;modal&amp;lt;/div&amp;gt;
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클릭 이벤트는 fireEvent를 사용하여 구현한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1627212918537&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;test(&quot;모달창에 Loading 메시지가 보이고, 데이터 로딩이 끝나면 할 일, 최소 인원, 필요한 돈 정보가 보인다&quot;, () =&amp;gt; {
  // write here
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자 이제 새로운 문제에 봉착했다. 모달을 띄울 때마다 매 번 API를 새로 호출하여 랜덤한 할 일 정보를 보여준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 테스트할 때마다 실제 API 호출을 하게 되면 API 호출에 대한 결과를 내가 컨트롤할 수 없게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 필요한 것이 바로 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;MSW&lt;/b&gt;&lt;/span&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MSW는 Mock Service Worker의 약자로 Service Worker API를 사용하여&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 API 요청을 가로채 mocking을 해주는 라이브러리이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1627213236902&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { rest } from &quot;msw&quot;;
import { setupServer } from &quot;msw/node&quot;;

const mockData = {
  activity: &quot;Go for a walk&quot;,
  type: &quot;relaxation&quot;,
  participants: 1,
  price: 0,
  link: &quot;&quot;,
  key: &quot;4286250&quot;,
  accessibility: 0.1
};

const server = setupServer(
  rest.get(&quot;https://www.boredapi.com/api/activity&quot;, (req, res, ctx) =&amp;gt; (
    res(ctx.json(mockData))
  )),
);

beforeAll(() =&amp;gt; worker.start());
afterEach(() =&amp;gt; worker.resetHandlers());
afterAll(() =&amp;gt; worker.stop());&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세팅 방법도 매우 간단하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 테스트 코드를 실행하면 &lt;i&gt;www.boredapi.com/api/activity&lt;/i&gt;로 API request가 발생할 때마다 MSW에 의해 인터셉트되고 리턴되는 mockData를 통해 항상 일관된 결과를 테스트할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1627213553466&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { fireEvent, render, waitFor, screen } from &quot;@testing-library/react&quot;;

beforeEach(() =&amp;gt; {
  render(&amp;lt;App /&amp;gt;);
});

// ...

test(&quot;모달창에 Loading...이 보이고, 다 로딩되면 할 일, 최소 인원, 필요한 돈 텍스트가 보인다.&quot;, async () =&amp;gt; {
  // 버튼을 누르고 모달창이 뜨면
  fireEvent.click(screen.getByRole(&quot;button&quot;, { name: &quot;Let's find!&quot; }));
  
  // 로딩 화면이 보이고
  expect(screen.getByText(&quot;Loading...&quot;)).toBeInTheDocument();

  // 다 로딩되면
  await waitFor(() =&amp;gt; screen.getByText(/할 일:/));
  // ^ 또는 waitForElementToBeRemoved(() =&amp;gt; screen.getByText(&quot;Loading...&quot;));
 
  // 할 일, 최소 인원, 필요한 돈 정보가 보인다
  expect(screen.getByText(&quot;할 일: Go for a walk&quot;).textContent).toBeInTheDocument();
  expect(screen.getByText(&quot;최소 인원: 1&quot;).textContent).toBeInTheDocument();
  expect(screen.getByText(&quot;필요한 돈: 0$&quot;).textContent).toBeInTheDocument();
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 간단하게 하나의 Flow를 쭉 테스트할 수 있다!  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 어떤 요소가 없어지는지 여부는 어떻게 알 수 있을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1627214120621&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ...

test(&quot;[Close] 버튼을 클릭하면 모달창이 닫힌다&quot;, () =&amp;gt; {
  // 버튼을 누르면 모달창이 보이고
  fireEvent.click(screen.getByRole(&quot;button&quot;, { name: &quot;Let's find!&quot; }));
  expect(screen.getByTestId(&quot;modalContainer&quot;)).toBeInTheDocument();
  
  // 모달창의 닫기 버튼을 누르면
  fireEvent.click(screen.getByRole(&quot;button&quot;, { name: &quot;Close&quot; }));
  
  // 모달창이 화면에서 사라진다
  expect(screen.queryByTestId(&quot;modalContainer&quot;)).not.toBeInTheDocument();
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 점은 요소가 사라졌는지 검사할 때는 queryBy-문을 사용한다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜냐하면 getBy- query문은 요소가 없으면 무조건 에러가 발생시키기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 queryBy-는 요소가 없으면 null을 리턴한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자 이제 마지막으로 에러 상황에 대한 테스트 코드를 작성해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1627214393734&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;test(&quot;데이터를 불러오다가 에러가 발생하면 Error라는 글자가 보인다&quot;, () =&amp;gt; {
  server.use(
    rest.get(&quot;https://www.boredapi.com/api/activity&quot;, (req, res, ctx) =&amp;gt; (
      res(ctx.status(500))
    ))
  );
  
  render(&amp;lt;App /&amp;gt;);
  
  expect(screen.getByText(&quot;Loading...&quot;)).toBeInTheDocument();

  await waitForElementToBeRemoved(() =&amp;gt; screen.getByText(&quot;Loading...&quot;));
  
  expect(screen.getByText(&quot;Error&quot;)).toBeInTheDocument();
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 테스트 블록에서 API의 mock response를 바꿔주고 싶을 때는 server.use 함수를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;response의 status를 400~500대로 설정하면 에러가 발생한 것처럼 mocking할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 화면에 원하는대로 에러 핸들링이 되고 있는지만 테스트해주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자 이제 아까 작성한 Saga Unit 테스트와 이 Integration 테스트를 비교해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Integration 테스트는 어떤 특정한 작은 로직에 대해서만 테스트하는 것이 아니라 앱 전반에 걸쳐 정상적으로 동작하는지 테스트한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Integration 테스트에서의 중요한 포인트는 유저에게 보여지는 최종 산출물에 대해서 테스트해야한다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 내가 내부 로직을 어떤 방식으로 리팩토링하든 유저에게 전달되는 결과물이 같다면 테스트 코드가 깨져서는 안된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 원칙을 최대한 반영하여 작성한다면 생각보다 테스트 코드를 유지 보수하는데 큰 리소스가 낭비되지 않을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Integration 테스트 코드를 앱 전반에 작성해두고 나면 기존 코드를 리팩토링하거나, 새로운 기능을 붙이고 나서 배포를 할 때 더 이상 장애가 발생할까봐 두려움에 떨지 않아도 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;[읽어보면 좋은 글들]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://kentcdodds.com/blog/static-vs-unit-vs-integration-vs-e2e-tests&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://kentcdodds.com/blog/static-vs-unit-vs-integration-vs-e2e-tests&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1627215660906&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Static vs Unit vs Integration vs E2E Testing for Frontend Apps&quot; data-og-description=&quot;What these mean, why they matter, and why they don't&quot; data-og-host=&quot;kentcdodds.com&quot; data-og-source-url=&quot;https://kentcdodds.com/blog/static-vs-unit-vs-integration-vs-e2e-tests&quot; data-og-url=&quot;https://kentcdodds.com/blog/static-vs-unit-vs-integration-vs-e2e-tests&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bfDogg/hyKZ5n5Glk/8ZKX4xoeuVPwMc07z9S0g1/img.png?width=1718&amp;amp;height=783&amp;amp;face=0_0_1718_783,https://scrap.kakaocdn.net/dn/cMwF93/hyKZWknZZL/aPvpCJ6ouKK624BNSOousk/img.png?width=1718&amp;amp;height=783&amp;amp;face=0_0_1718_783,https://scrap.kakaocdn.net/dn/kp3jD/hyKZZg7AWT/tuXwgAJk3i9osz2EJOWaJk/img.jpg?width=1500&amp;amp;height=1061&amp;amp;face=0_0_1500_1061&quot;&gt;&lt;a href=&quot;https://kentcdodds.com/blog/static-vs-unit-vs-integration-vs-e2e-tests&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://kentcdodds.com/blog/static-vs-unit-vs-integration-vs-e2e-tests&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bfDogg/hyKZ5n5Glk/8ZKX4xoeuVPwMc07z9S0g1/img.png?width=1718&amp;amp;height=783&amp;amp;face=0_0_1718_783,https://scrap.kakaocdn.net/dn/cMwF93/hyKZWknZZL/aPvpCJ6ouKK624BNSOousk/img.png?width=1718&amp;amp;height=783&amp;amp;face=0_0_1718_783,https://scrap.kakaocdn.net/dn/kp3jD/hyKZZg7AWT/tuXwgAJk3i9osz2EJOWaJk/img.jpg?width=1500&amp;amp;height=1061&amp;amp;face=0_0_1500_1061');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Static vs Unit vs Integration vs E2E Testing for Frontend Apps&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;What these mean, why they matter, and why they don't&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;kentcdodds.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://kentcdodds.com/blog/common-mistakes-with-react-testing-library&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://kentcdodds.com/blog/common-mistakes-with-react-testing-library&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1627215653753&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Common mistakes with React Testing Library&quot; data-og-description=&quot;Some mistakes I frequently see people making with React Testing Library.&quot; data-og-host=&quot;kentcdodds.com&quot; data-og-source-url=&quot;https://kentcdodds.com/blog/common-mistakes-with-react-testing-library&quot; data-og-url=&quot;https://kentcdodds.com/blog/common-mistakes-with-react-testing-library&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bDTXWi/hyKZ9Ryyxk/YXgZSMlW9E1h2Xxfs4I7K0/img.jpg?width=2070&amp;amp;height=1380&amp;amp;face=0_0_2070_1380,https://scrap.kakaocdn.net/dn/dBjiX4/hyKZ7MYxm0/CQnFJoT4rkoSH18fzYs1A0/img.jpg?width=2070&amp;amp;height=1380&amp;amp;face=0_0_2070_1380,https://scrap.kakaocdn.net/dn/mg3XK/hyKZXjiuKK/4DjTnBbBgI4r0qfaIpSRPk/img.jpg?width=2070&amp;amp;height=1380&amp;amp;face=0_0_2070_1380&quot;&gt;&lt;a href=&quot;https://kentcdodds.com/blog/common-mistakes-with-react-testing-library&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://kentcdodds.com/blog/common-mistakes-with-react-testing-library&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bDTXWi/hyKZ9Ryyxk/YXgZSMlW9E1h2Xxfs4I7K0/img.jpg?width=2070&amp;amp;height=1380&amp;amp;face=0_0_2070_1380,https://scrap.kakaocdn.net/dn/dBjiX4/hyKZ7MYxm0/CQnFJoT4rkoSH18fzYs1A0/img.jpg?width=2070&amp;amp;height=1380&amp;amp;face=0_0_2070_1380,https://scrap.kakaocdn.net/dn/mg3XK/hyKZXjiuKK/4DjTnBbBgI4r0qfaIpSRPk/img.jpg?width=2070&amp;amp;height=1380&amp;amp;face=0_0_2070_1380');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Common mistakes with React Testing Library&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Some mistakes I frequently see people making with React Testing Library.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;kentcdodds.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend</category>
      <category>msw</category>
      <category>react</category>
      <category>TestingLibrary</category>
      <category>테스트코드</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/226</guid>
      <comments>https://im-developer.tistory.com/226#entry226comment</comments>
      <pubDate>Sun, 25 Jul 2021 21:23:09 +0900</pubDate>
    </item>
    <item>
      <title>[LeetCode] Array101 - Find All Numbers Disappeared in an Array</title>
      <link>https://im-developer.tistory.com/225</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;581&quot; data-origin-height=&quot;516&quot; width=&quot;268&quot; height=&quot;238&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cgy6eR/btq52pBGCBz/DlKh3Rmf2sqVSIG7Br99Yk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cgy6eR/btq52pBGCBz/DlKh3Rmf2sqVSIG7Br99Yk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cgy6eR/btq52pBGCBz/DlKh3Rmf2sqVSIG7Br99Yk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcgy6eR%2Fbtq52pBGCBz%2FDlKh3Rmf2sqVSIG7Br99Yk%2Fimg.png&quot; data-origin-width=&quot;581&quot; data-origin-height=&quot;516&quot; width=&quot;268&quot; height=&quot;238&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Find All Numbers Disappeared in an Array&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Given an array &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;nums&lt;/b&gt;&lt;/span&gt; of &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;n&lt;/b&gt;&lt;/span&gt; integers where &lt;b&gt;nums[i]&lt;/b&gt; is in the range &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;[1, n]&lt;/b&gt;&lt;/span&gt;, return an array of all the integers in the range [1, n] that do not appear in nums.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Example 1:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Input: nums = [4,3,2,7,8,2,3,1]
Output: [5,6]
Example 2:&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Example 2:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Input: nums = [1,1]
Output: [2]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Constraints:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;n == nums.length&lt;/li&gt;
&lt;li&gt;1 &amp;lt;= n &amp;lt;= 105&lt;/li&gt;
&lt;li&gt;1 &amp;lt;= nums[i] &amp;lt;= n&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;* Follow up:&lt;/b&gt; Could you do it without extra space and in &lt;b&gt;O(n)&lt;/b&gt; runtime? You may assume the returned list does not count as extra space.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드디어 Array101 시리즈의 마지막 문제다!  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 예를 들어 5개의 숫자가 들어있는 배열이 주어진다고 가정해보자. =&amp;gt; &lt;b&gt;[5, 2, 3, 3, 5]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이 배열에는 1부터 5까지의 숫자만 들어있는데, 1부터 5 중에서 배열에 들어있지 않은 숫자들만 모아서 리턴하는 문제이다. =&amp;gt; &lt;b&gt;[1, 4]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style4&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 가장 쉽게 푸는 방법은 새로운 배열을 만든 다음 비교하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 1부터 5까지 들어있는 배열을 만들고 =&amp;gt; &lt;b&gt;[1, 2, 3, 4, 5]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 주어진 배열을 순회하면서 나온 아이템들을 삭제하는 것이다 =&amp;gt; &lt;b&gt;[1, , , 4, ]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 그리고 숫자가 들어있는 칸만 남겨둔다 =&amp;gt; &lt;b&gt;[1, 4]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 풀면 총 3번 순회해야하고, 새로운 배열도 2번 만들어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에 1부터 5까지 든 배열을 만들기 위해 한 번 순회하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아이템 삭제하기 위해 한 번 순회하고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자가 들어있는 칸만 남기기 위해 한 번 순회해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제에서 &lt;b&gt;Follow up&lt;/b&gt;을 보면 여분의 메모리 공간 없이 in-place로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;O(n) 시간 복잡도를 가지면서 해결하는 방법을 생각해보라고 적혀 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 어떻게 하면 그렇게 해결할 수 있을까 고민만 30분 넘게 하다가 다음과 같은 방법을 생각해냈다..!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style4&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[5, 2, 3, 3, 5]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열의 각 인덱스에 1을 더하면 바로 배열에 들어갈 숫자의 범위가 된다는 것을 이용할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[&lt;span style=&quot;color: #ee2323;&quot;&gt;5&lt;/span&gt;, 2, 3, 3, 5]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;시작 인덱스&lt;/b&gt;&lt;/span&gt;는 0이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;0번 아이템은 5이다. 그렇다는 뜻은 (5 - 1)번째 숫자는 배열에 들어있으므로 후보에서 탈락한다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[5, 2, 3, 3, 5]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[5, 2, 3, 3, undefined]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기 때문에 4번째에 든 숫자를 0번째로 옮기고 4번째 칸은 undefined를 할당해서 비워둔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[&lt;span style=&quot;color: #ee2323;&quot;&gt;5&lt;/span&gt;, 2, 3, 3, undefined]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 0번째 아이템에 숫자가 있으므로 다시 0번째 아이템을 검사한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에도 0번째 아이템은 5다. 그러나 (5 - 1)번째 칸은 이미 undefined이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[5, 2, 3, 3, undefined]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[1, &lt;span style=&quot;color: #ee2323;&quot;&gt;2&lt;/span&gt;, 3, 3, undefined]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기 때문에 0번째 칸에는 (0 + 1)을 한 값을 넣고 시작 인덱스를 1 증가시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[1,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;2&lt;/span&gt;, 3, 3, undefined]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[1, &lt;b&gt;undefined&lt;/b&gt;, &lt;span style=&quot;color: #ee2323;&quot;&gt;3&lt;/span&gt;, 3, undefined]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1번째 아이템은 2이다. 그렇다는 것은 (2 - 1)번째 숫자가 후보에서 탈락한다는 뜻이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1번째 숫자를 1번으로 옮기고(자기 자신을 덮어 쓰므로 아무 변화도 없다.) 1번째 칸에 undefined를 할당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 1번째 칸에 숫자가 없으므로 시작 인덱스를 1 증가시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[1,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;undefined&lt;/b&gt;, &lt;span style=&quot;color: #ee2323;&quot;&gt;3&lt;/span&gt;, 3, undefined]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[1,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;undefined&lt;/b&gt;,&lt;span&gt; &lt;b&gt;&lt;b&gt;undefined&lt;/b&gt;&lt;/b&gt;&lt;/span&gt;, &lt;span style=&quot;color: #ee2323;&quot;&gt;3&lt;/span&gt;, undefined]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2번째 아이템은 3이다. 그러므로 (3 - 1)번째 숫자가 후보에서 탈락한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2번째 숫자를 2번으로 옮기고 (역시 자기 자신을 덮어 쓴다.) 2번째 칸에 undefined를 할당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 2번째 칸에 숫자가 없으므로 시작 인덱스를 1 증가시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3번째 아이템은 3이다. 그러나 (3 - 1) 번째 숫자는 이미 undefined이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[1,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;undefined&lt;/b&gt;,&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;b&gt;undefined&lt;/b&gt;&lt;/b&gt;&lt;/span&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;3&lt;/span&gt;, undefined]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt; &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[1,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;undefined&lt;/b&gt;,&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;b&gt;undefined&lt;/b&gt;&lt;/b&gt;&lt;/span&gt;, 4, &lt;span style=&quot;color: #ee2323;&quot;&gt;undefined&lt;/span&gt;]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 3번째 칸에는 3 + 1을 한 값을 넣고 시작 인덱스를 1 증가 시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4번째 칸에는 undefined이므로 시작 인덱스를 1 증가시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 증가된 인덱스가 배열의 마지막 인덱스보다 크기 때문에 종료한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[1,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;undefined&lt;/b&gt;,&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;b&gt;undefined&lt;/b&gt;&lt;/b&gt;&lt;/span&gt;, 4,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;undefined&lt;/span&gt;]&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종적으로 위와 같은 배열만 남게 된다. 이제 최종적으로 1번 더 순회하면서 undefined를 제거해주면 끝난다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 방법대로 풀면 in-place이면서 단 한 번의 순회로 결과값이 든 배열을 얻을 수 있으며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종적으로 undefined인 값을 필터링만 하면 된다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법을 코드로 풀면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1622296564576&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
 * @param {number[]} nums
 * @return {number[]}
 */
var findDisappearedNumbers = function(nums) {
  let index = 0;

  while (index &amp;lt;= nums.length - 1) {
    if (nums[index] === undefined) {
      index++;
      continue;
    }

    const targetIndex = nums[index] - 1;
    
    if (nums[targetIndex] === undefined) {
      nums[index] = index + 1;
      index++;
    } else {
      nums[index] = nums[targetIndex];
      nums[targetIndex] = undefined;
    }
  }

  return nums.filter(num =&amp;gt; num !== undefined);
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Algorithm</category>
      <category>Algorithm</category>
      <category>JavaScript</category>
      <category>leetcode</category>
      <category>알고리즘</category>
      <category>자바스크립트</category>
      <category>코딩</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/225</guid>
      <comments>https://im-developer.tistory.com/225#entry225comment</comments>
      <pubDate>Sat, 29 May 2021 22:59:00 +0900</pubDate>
    </item>
    <item>
      <title>[LeetCode] Array101 - Third Maximum Number</title>
      <link>https://im-developer.tistory.com/224</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;380&quot; data-origin-height=&quot;265&quot; width=&quot;251&quot; height=&quot;175&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOCwx4/btq5xt4k8oz/lGH0eDO3fAjRdRAvuVUxok/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOCwx4/btq5xt4k8oz/lGH0eDO3fAjRdRAvuVUxok/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOCwx4/btq5xt4k8oz/lGH0eDO3fAjRdRAvuVUxok/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOCwx4%2Fbtq5xt4k8oz%2FlGH0eDO3fAjRdRAvuVUxok%2Fimg.jpg&quot; data-origin-width=&quot;380&quot; data-origin-height=&quot;265&quot; width=&quot;251&quot; height=&quot;175&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Third Maximum Number&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Given integer array nums, return the &lt;u&gt;&lt;b&gt;third maximum number&lt;/b&gt;&lt;/u&gt; in this array. If the third maximum does not exist, return the maximum number.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Example 1:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Input: nums = [3,2,1]
Output: 1
Explanation: The third maximum is 1.
Example 2:&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Example 2:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;Input: nums = [1,2]
Output: 2
Explanation: The third maximum does not exist, so the maximum (2) is returned instead.
Example 3:&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Example 3:&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;Input: nums = [2,2,3,1]
Output: 1
Explanation: Note that the third maximum here means the third maximum distinct number.
Both numbers with value 2 are both considered as second maximum.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Constraints:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1 &amp;lt;= nums.length &amp;lt;= 104&lt;/li&gt;
&lt;li&gt;-231 &amp;lt;= nums[i] &amp;lt;= 231 - 1&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Follow up:&lt;/b&gt; Can you find an &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;O(n)&lt;/b&gt;&lt;/span&gt; solution?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 2문제만 더 풀면 Array101 시리즈가 끝이 난다..  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 이번주에 푼 문제 중에 제일 재밌었던 문제를 적어보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주어진 배열 중에서 3번째로 큰 숫자를 찾아내고, 만약 3번째로 큰 숫자가 없다면 첫 번째로 큰 숫자를 리턴하는 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 점은 중복 숫자는 제외해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sort나 filter 메소드를 사용하면 금방 풀겠지만 중요한건 시간 복잡도가 &lt;b&gt;O(n)&lt;/b&gt;이어야한다는 점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 filter와 Math.max 메소드를 사용해서 푼 방법은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1621749576745&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
 * @param {number[]} nums
 * @return {number}
 */
var thirdMax = function(nums) {
  let filtered = nums;
  let max = Math.max(...nums);
  const result = [max];

  for (let i = 0; i &amp;lt; 2; i++) {
    filtered = filtered.filter(num =&amp;gt; num !== max);
    if (filtered.length === 0) return result[0];

    max = Math.max(...filtered);

    result.push(max);
  }

  return max;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제일 큰 숫자를 찾아내고, 해당 숫자를 배열에서 전부 제거한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸 3번 반복하면 3번째로 큰 숫자가 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 3번째로 큰 숫자가 없다면 제일 큰 숫자를 리턴한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방법의 문제점은 filter를 반복하면서 계속 loop을 돈다는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딱 한 번만 loop을 돌면서 세번째로 큰 숫자를 찾아내야하기 때문에 다른 방법을 고민해보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1621749689796&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
 * @param {number[]} nums
 * @return {number}
 */
var thirdMax = function(nums) {
  let first = nums[0];
  let second = null;
  let third = null;

  for (let i = 1; i &amp;lt; nums.length; i++) {
    if (nums[i] &amp;gt; first) {
      third = second;
      second = first;
      first = nums[i];
    } else if (second === null || nums[i] &amp;gt; second) {
      if (nums[i] !== first) {
        third = second;
        second = nums[i];
      }
    } else if (third === null || nums[i] &amp;gt; third) {
      if (nums[i] !== second) {
        third = nums[i];
      }
    }
  }

  return third === null ? first : third;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그게 바로 이 방법인데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수를 3개 만들어서 숫자가 큰 순서대로 담는 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 아무래도 if문 조건이 복잡해서 조금 코드가 지저분하게 보이는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LeetCode에 답을 submit하고 다른 사람이 푼 코드를 둘러보다가 아주 획기적인 방법을 발견했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1621749905659&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
 * @param {number[]} nums
 * @return {number}
 */
var thirdMax = function(nums) {
  let first = nums[0];
  let second = -Infinity;
  let third = -Infinity;

  for (let i = 1; i &amp;lt; nums.length; i++) {
    if (nums[i] &amp;gt; first) {
      third = second;
      second = first;
      first = nums[i];
    } else if (nums[i] &amp;lt; first &amp;amp;&amp;amp; nums[i] &amp;gt; second) {
      third = second;
      second = nums[i];
    } else if (nums[i] &amp;lt; second &amp;amp;&amp;amp; nums[i] &amp;gt; third) {
      third = nums[i];
    }
  }

  return third === -Infinity ? first : third;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에 내가 푼 방법과 거의 유사하지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;second와 third의 초기값을 -Infinity로 한다는 점이 넘나 굿 아이디어!  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Infinity&lt;/b&gt;는 다른 어떤 수보다 크기 때문에 -Infinity는 다른 어떤 수보다 작다는 것을 활용해서 푼 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Algorithm</category>
      <category>Algorithm</category>
      <category>JavaScript</category>
      <category>leetcode</category>
      <category>알고리즘</category>
      <category>자바스크립트</category>
      <category>코딩</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/224</guid>
      <comments>https://im-developer.tistory.com/224#entry224comment</comments>
      <pubDate>Sun, 23 May 2021 15:09:09 +0900</pubDate>
    </item>
    <item>
      <title>[LeetCode] Arrays101 - Valid Mountain Array</title>
      <link>https://im-developer.tistory.com/223</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Valid&amp;nbsp;Mountain&amp;nbsp;Array&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Given an array of integers&lt;span&gt;&amp;nbsp;&lt;/span&gt;arr, return&lt;span&gt;&amp;nbsp;&lt;/span&gt;true&lt;span&gt;&amp;nbsp;&lt;/span&gt;if and only if it is a valid mountain array.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Recall that arr is a mountain array if and only if:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;arr.length &amp;gt;= 3&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;There exists some&lt;span&gt;&amp;nbsp;&lt;/span&gt;i&lt;span&gt;&amp;nbsp;&lt;/span&gt;with&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;0 &amp;lt; i &amp;lt; arr.length - 1&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;such that:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;arr[0] &amp;lt; arr[1] &amp;lt; ... &amp;lt; arr[i - 1] &amp;lt; arr[i]&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;arr[i] &amp;gt; arr[i + 1] &amp;gt; ... &amp;gt; arr[arr.length - 1]&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;500&quot; data-origin-width=&quot;892&quot; data-origin-height=&quot;563&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/t26wk/btq4Zf6VPSj/TJkGbJgACtN0DUFPqK46j0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/t26wk/btq4Zf6VPSj/TJkGbJgACtN0DUFPqK46j0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/t26wk/btq4Zf6VPSj/TJkGbJgACtN0DUFPqK46j0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ft26wk%2Fbtq4Zf6VPSj%2FTJkGbJgACtN0DUFPqK46j0%2Fimg.png&quot; width=&quot;500&quot; data-origin-width=&quot;892&quot; data-origin-height=&quot;563&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Example 1:&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1621160931722&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Input: arr = [2,1]
Output: false&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Example 2:&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1621160950707&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Input: arr = [3,5,5]
Output: false&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Example 3:&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1621160962736&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Input: arr = [0,3,2,1]
Output: true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Constraints:&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1 &amp;lt;= arr.length &amp;lt;= 104&lt;/li&gt;
&lt;li&gt;0 &amp;lt;= arr[i] &amp;lt;= 104&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 번주에 푼 문제들 중에 제일 재밌었던 문제여서 가져와봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;배열의 숫자들이 마치 산(⛰)처럼 중간으로 갈 수록 크기가 커지고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;끝으로 갈 수록 다시 크기가 작아지는 형태인지 아닌지 판별하는 문제이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 중간에 크기가 같은 숫자가 있거나,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹은 작아졌다 커졌다 여러 번 반복하면 탈락..!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이거는 문제가 그래도 비교적 쉬워서 금방 풀었는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 풀었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1621160999092&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
 * @param {number[]} arr
 * @return {boolean}
 */
var validMountainArray = function(arr) {
  if (arr.length &amp;lt; 3) return false;

  let start = 0;
  let end = arr.length - 1;

  while (arr[start] &amp;lt; arr[start + 1]) {
    start++;
  }

  while (arr[end - 1] &amp;gt; arr[end]) {
    end--;
  }
  
  if (start === end) {
    if (start === 0) return false;
    if (start === arr.length - 1) return false;
    
    return true;
  }
  return false;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞에서부터 배열의 다음 아이템이 현재의 아이템보다 크기가 작아지기 전까지 반복한다. 반복이 끝나면 해당 index를 기억해둔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음, 뒤에서부터 이 전 아이템이 현재의 아이템보다 크기가 커지기 전까지 반복하고, 똑같이 반복이 끝나면 index를 기억한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 index가 같으면서 0이나 배열의 마지막 index가 아니라면 true를, 나머지는 false를 리턴한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 두 index가 모두 0이라면 해당 배열은 [5, 4, 3, 2, 1]과 같이 내림차순 배열이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 두 index가 모두 배열의 마지막 index라면 해당 배열은 [1, 2, 3, 4, 5]와 같이 오름차순 배열이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Algorithm</category>
      <category>Algorithm</category>
      <category>JavaScript</category>
      <category>leetcode</category>
      <category>알고리즘</category>
      <category>자바스크립트</category>
      <category>코딩</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/223</guid>
      <comments>https://im-developer.tistory.com/223#entry223comment</comments>
      <pubDate>Sun, 16 May 2021 19:40:06 +0900</pubDate>
    </item>
    <item>
      <title>[Kent C. Dodds] 어플리케이션 상태 관리 (Application State Management with React 한글 번역)</title>
      <link>https://im-developer.tistory.com/222</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;a href=&quot;http://unsplash.com/photos/YeUVDKZWSZ4&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEqQOz/btq4ylfUvRE/KhJP4HUBibHw3kEh7YRg2k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEqQOz%2Fbtq4ylfUvRE%2FKhJP4HUBibHw3kEh7YRg2k%2Fimg.jpg&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/a&gt;&lt;figcaption&gt;Photo by&amp;nbsp; Rene B&amp;ouml;hmer&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;올 해 1월부터 바닐라코딩 부트캠프 6기를 같이 수료한 몇몇 사람들과 시작한 스터디가 있는데,&lt;/p&gt;
&lt;p&gt;바로 영어로 된 개발 블로그 글을 번역하고 스터디 멤버들과 함께 공유하는 스터디이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이런 스터디를 구상한 이유는 이리 저리 돌아다니면서 요즘 핫하다는 블로그 글을 수집은 많이 하는데 정작 잘 읽지는 않아서이다.&lt;/p&gt;
&lt;p&gt;좋은 글을 꾸준히 많이 읽는 것은 정말 개발자에게 특히 중요한 일이지만 혼자서 하면 작심 삼일이 되고 잘 안하게 되기 마련이다.&lt;/p&gt;
&lt;p&gt;그래서 시작한 스터디인데 생각보다 많은 글들을 읽게 되어 만족도가 높다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;현재까지 번역해서 노션에 정리한 글이 7개 정도 된다.&lt;/p&gt;
&lt;p&gt;물론 1월 중순부터 시작한 것 치고 많지 않아보일 수도 있지만 어쨌든 중요한건 꾸준히 계속 하고 있다는 것이니까!&lt;/p&gt;
&lt;p&gt;오늘은 번역한 글들 중에서 정말 마음에 드는 글을 블로그에도 올려보려고 한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;원문 링크:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://kentcdodds.com/blog/application-state-management-with-react&quot;&gt;kentcdodds.com/blog/application-state-management-with-react&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1620544141150&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Application State Management with React&quot; data-og-description=&quot;How React is all you need to manage your application state&quot; data-og-host=&quot;kentcdodds.com&quot; data-og-source-url=&quot;https://kentcdodds.com/blog/application-state-management-with-react&quot; data-og-url=&quot;https://kentcdodds.com/blog/application-state-management-with-react&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bs3CMB/hyJ9Wxo4rl/TJFY1lAeCGAdXftZZW5Am1/img.jpg?width=720&amp;amp;height=480&amp;amp;face=0_0_720_480,https://scrap.kakaocdn.net/dn/czTnEj/hyJ7U83tg7/Ao5WW4pRBvboMUkgjF1vZk/img.jpg?width=720&amp;amp;height=480&amp;amp;face=0_0_720_480&quot;&gt;&lt;a href=&quot;https://kentcdodds.com/blog/application-state-management-with-react&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://kentcdodds.com/blog/application-state-management-with-react&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bs3CMB/hyJ9Wxo4rl/TJFY1lAeCGAdXftZZW5Am1/img.jpg?width=720&amp;amp;height=480&amp;amp;face=0_0_720_480,https://scrap.kakaocdn.net/dn/czTnEj/hyJ7U83tg7/Ao5WW4pRBvboMUkgjF1vZk/img.jpg?width=720&amp;amp;height=480&amp;amp;face=0_0_720_480');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Application State Management with React&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;How React is all you need to manage your application state&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;kentcdodds.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p style=&quot;text-align: right;&quot; data-ke-size=&quot;size14&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;오역이 있을 수 있습니다. 코멘트로 남겨주시거나 메일 주시면 감사하겠습니다~!&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;어떻게 리액트가 여러분의 어플리케이션 상태를 관리하는데 필요한 전부인지 알아보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;어떤 어플리케이션이든 상태 관리는 틀림없이 가장 어려운 부분이다. 이 것이 바로 수많은 상태 관리 라이브러리들이 존재하고 매일 더 많은 라이브러리들이 생겨나는 이유이다. (그리고 심지어 일부는 다른 라이브러리들을 기반으로 생겨났다. npm에는 수백개의 추상화된 &quot;더 쉬운 &lt;span style=&quot;color: #333333;&quot;&gt;redux&lt;/span&gt;&quot; 가 존재한다.) 상태 관리가 가장 어려운 문제라는 사실에도 불구하고, 나는 상태 관리를 어렵게 하는 이유 중에 하나는 종종 문제를 해결하기 위해 과한 &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;엔지니어링(&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;over-engineer)&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;을 하기 때문이라고 생각한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;내가 개인적으로 리액트를 사용하면서 오랫동안 도입해보려고 시도했던 상태 관리 솔루션 하나가 있는데, 리액트 훅의 출시와 함께 (그리고 엄청난 리액트 context 향상과 함께) 이 상태 관리법은 대폭 단순화되었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;우리는 종종 리액트 컴포넌트를 어플리케이션을 구축하기 위한 레고 블록으로 비유한다. 그리고 사람들은 이 얘기를 들으면, 왜인지는 모르겠지만 이 비유가 상태 측면을 배제한다고 생각한다. 상태 관리 문제를 해결하기 위한 나의 개인적인 해결책에 숨어있는 &quot;비밀&quot;은 우리의 어플리케이션 상태가 어떻게 어플리케이션의 트리 구조를 그리는지를 생각하는 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;redux가 크게 성공할 수 있었던 이유 중에 하나는 react-redux가 &lt;a href=&quot;https://kentcdodds.com/blog/prop-drilling&quot;&gt;prop drilling&lt;/a&gt; 문제를 해결했다는 사실에 있다. 단순히 컴포넌트를 마법같은 connect 함수에 넘기는 방법으로 트리의 다른 부분들을 넘어 데이터를 공유할 수 있다는 사실은 정말 멋진 것이었다. redux가 reducers/action creators/기타 등등을 사용하는 것 또한 멋지지만, 나는 redux가 여기 저기 널리 퍼질 수 있었던 이유가 개발자를 위해 prop drilling이라는 큰 불편함을 해소했다는 데 있다고 확신한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;다음에 적는 것이 바로 내가 지금껏 오직 하나의 프로젝트에서만 redux를 사용했던 이유이다: 나는 지금까지 지속적으로 개발자들이 그들이 사용하는 모든 상태를 redux에 넣는 것을 보았다. 전역 어플리케이션 상태뿐만 아니라 지역 상태까지 말이다. 이는 정말 많은 문제들을 야기했는데, 그 중 가장 중요한 문제는 다음과 같다. &lt;span style=&quot;color: #333333;&quot;&gt;여러분이 어떤 상태의 상호작용을 유지하고 있을 때, 이 상태 인터렉션은&amp;nbsp;&lt;/span&gt;reducer와 action creator, type, 그리고 dispatch 호출들과도 상호작용하며, 궁극적으로 무슨 일이 일어났는지, 그리고 이 일이 &lt;span style=&quot;color: #333333;&quot;&gt;나머지 코드베이스에&lt;/span&gt; 어떤 영향을 미치는지를 파악하려면 &lt;span style=&quot;color: #333333;&quot;&gt;머리속으로&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;&lt;/span&gt;여러 파일들을 열고 코드 속에서 흔적을 추적해야만 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;확실히 하자면, 글로벌한 상태를 두는 것 자체는 정말이지 문제가 없다. 그러나 간단한 상태(예를 들면 모달이 열리는지 안열리는지를 결정하는 상태 또는 form의 input 값을 가지는 상태)를 글로벌하게 두는 것은 큰 문제다. 설상가상으로 이는 확장성에 별로 좋지 않다. 당신의 어플리케이션이 커질수록, 이 문제는 점점 더 커져버린다. 물론 여러분은 다른 reducer들을 연결해서 어플리케이션의 여러 다른 부분들을 관리할 수도 있지만, 모든 action creator와 reducer를 고려하면서 이런 간접적인 조치를 하는 것은 최적의 방법이 아니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;비록 여러분이 Redux를 사용하지 않더라도, 여러분의 모든 어플리케이션 상태를 하나의 객체로 관리하는 것 또한 다른 문제들을 야기할 수 있다. 리액트의 &amp;lt;Context.Provider&amp;gt;가 새로운 값을 가져오면 해당 값을 사용하는 모든 컴포넌트들은 설사 데이터의 작은 일부분만을 담당하는 함수형 컴포넌트라고 할지라도 다시 업데이트되고 render되어야 한다. 이 것은 잠재적인 성능 이슈를 만들 수 있다. (React-Redux v6는 훅(hook)과는 잘 작동하지 않을거라는 것을 인지하기 전까지는 이러한 접근법을 사용하려고 했었고, 이러한 이슈들을 해결하기 위해 v7에서는 다른 접근법을 사용해야만 했다.) 여기서 나의 포인트는 여러분이 만약 상태를 더 논리적으로 분리하고 리액트 트리 상에서 해당 상태를 사용하는 곳 가까이에 위치시킨다면 이러한 문제들을 겪지 않을 거라는 점이다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;여기 더 놀라운 것이 있다. 만약 여러분이 리액트로 어플리케이션을 구축한다면, 이미 여러분의 어플리케이션에는 상태 관리 라이브러리가 설치되어 있다. 심지어 여러분은 npm install이나 yarn add를 실행할 필요도 없다. 이 것은 여러분이 만든 어플리케이션 사용자들로부터 어떠한 추가적인 바이트도 소모하지 않으며, npm에 존재하는 모든 리액트 패키지들과 잘 융합되고, 리액트 팀에 의해 이미 훌륭하게 문서화되어 있다. 바로 리액트 자체 말이다.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;리액트는 상태 관리 라이브러리이다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;여러분이 리액트 어플리케이션을 구축한다면, 여러분은 &amp;lt;App /&amp;gt;에서 시작되어 &amp;lt;input /&amp;gt;들이나 &amp;lt;div /&amp;gt;, 그리고 &amp;lt;button /&amp;gt;들에서 끝나는 컴포넌트 트리를 만들기 위해 다수의 컴포넌트들을 조립할 것이다. 여러분은 어플리케이션이 렌더링하는 낮은 단계의 모든 합성 컴포넌트들을 중앙의 한 장소에서 관리하지는 않을 것이다. 대신에, 각각의 개별적인 컴포넌트가 그것들을 관리하도록 둘 것이고, 이 것이 UI를 구축하는데 가장 효과적인 방법이 될 것이다. 그리고 이 방법을 여러분의 어플리케이션 상태에도 적용할 수 있는데, 바로 여러분이 오늘 한 것과 비슷한 것이다:&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1620543856117&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function Counter() {
  const [count, setCount] = React.useState(0)
  const increment = () =&amp;gt; setCount(c =&amp;gt; c + 1)
  return &amp;lt;button onClick={increment}&amp;gt;{count}&amp;lt;/button&amp;gt;
}

function App() {
  return &amp;lt;Counter /&amp;gt;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://codesandbox.io/s/4qzj73lozx?fontsize=14&amp;amp;hidenavigation=1&amp;amp;theme=dark&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;img src=&quot;https://codesandbox.io/static/img/play-codesandbox.svg&quot; alt=&quot;Edit Application State Management with React&quot; /&gt; &lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;내가 지금 여기서 말하는 모든 것들은 클래스 컴포넌트에도 적용된다는 사실을 기억하자. 훅(&lt;span style=&quot;color: #333333;&quot;&gt;Hook)&lt;/span&gt;은 단지 모든 일들을 조금 더 쉽게 만들어줄 뿐이다. (잠시후에 살펴 볼 context는 특히나 그렇다.)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1620543870312&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Counter extends React.Component {
  state = {count: 0}
  increment = () =&amp;gt; this.setState(({count}) =&amp;gt; ({count: count + 1}))
  
  render() {
    return &amp;lt;button onClick={this.increment}&amp;gt;{this.state.count}&amp;lt;/button&amp;gt;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&quot;알겠어요, Kent, 물론 하나의 컴포넌트에서 관리되는 하나의 상태 요소만 가지는 일은 쉽겠죠. 그런데 상태를 여러 컴포넌트들에 공유해야할 때 당신은 어떻게 하나요? 예를 들어 내가 다음과 같은 일들을 하길 원한다면요?&quot;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1620543891391&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function CountDisplay() {
  // `count`는 어디에서 오는가?
  return &amp;lt;div&amp;gt;The current counter count is {count}&amp;lt;/div&amp;gt;
}

function App() {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;CountDisplay /&amp;gt;
      &amp;lt;Counter /&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&quot;count는 &amp;lt;Counter /&amp;gt;안에서 관리됩니다. 나는 이제 &amp;lt;CountDisplay /&amp;gt;에서 count 값에 접근하고, 그 다음 이 값을 &amp;lt;Counter /&amp;gt;안에서 업데이트하기 위해 상태 관리 라이브러리가 필요해요!&quot;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 문제의 해답은 리액트 그 자체만큼이나 오래되었고 (혹은 더 오래되었을 수도?) 내가 기억하는 한 계속 공식 문서에 있어왔다: &lt;a href=&quot;https://reactjs.org/docs/lifting-state-up.html&quot;&gt;상태 위로 올리기(&lt;/a&gt;&lt;a href=&quot;https://reactjs.org/docs/lifting-state-up.html&quot;&gt;Lifting State Up&lt;/a&gt;&lt;a href=&quot;https://reactjs.org/docs/lifting-state-up.html&quot;&gt;)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&quot;상태 위로 올리기&quot;는 리액트에서 상태 관리 문제를 위한 적법한 답변이고 매우 단단한 방법이다. 이 것을 다음 상황에 어떻게 적용하는지 살펴보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1620543913999&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function Counter({count, onIncrementClick}) {
  return &amp;lt;button onClick={onIncrementClick}&amp;gt;{count}&amp;lt;/button&amp;gt;
}

function CountDisplay({count}) {
  return &amp;lt;div&amp;gt;The current counter count is {count}&amp;lt;/div&amp;gt;
}

function App() {
  const [count, setCount] = React.useState(0)
  const increment = () =&amp;gt; setCount(c =&amp;gt; c + 1)
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;CountDisplay count={count} /&amp;gt;
      &amp;lt;Counter count={count} onIncrementClick={increment} /&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://codesandbox.io/s/4qzj73lozx?fontsize=14&amp;amp;hidenavigation=1&amp;amp;theme=dark&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;img src=&quot;https://codesandbox.io/static/img/play-codesandbox.svg&quot; alt=&quot;Edit Application State Management with React&quot; /&gt; &lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;우리는 방금 전에 누가 상태에 책임이 있는지를 바꿔버렸고, 이 것은 매우 간단하다. 그리고 우리는 &lt;span style=&quot;color: #333333;&quot;&gt;상태들을&lt;span&gt; &lt;span style=&quot;color: #333333;&quot;&gt;계속해서&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;앱의 가장 위쪽으로 완전히 올려버릴 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&quot;물론이죠 Kent, 좋아요, 그렇지만 &lt;a href=&quot;https://kentcdodds.com/blog/prop-drilling&quot;&gt;prop drilling&lt;/a&gt;&amp;nbsp;문제는 어떻게할 건가요?&quot;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;좋은 질문이다. 이 문제에 맞서는 여러분의 첫 방어진은 여러분의 컴포넌트를 구성하는 방법을 바꾸는 것이다. &lt;a href=&quot;https://reactjs.org/docs/context.html#before-you-use-context&quot;&gt;컴포넌트 합성&lt;/a&gt;을 이용해보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아마도 다음과 같은 것 대신에:&lt;/p&gt;
&lt;pre id=&quot;code_1620543930810&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function App() {
  const [someState, setSomeState] = React.useState('some state')
  return (
    &amp;lt;&amp;gt;
      &amp;lt;Header someState={someState} onStateChange={setSomeState} /&amp;gt;
      &amp;lt;LeftNav someState={someState} onStateChange={setSomeState} /&amp;gt;
      &amp;lt;MainContent someState={someState} onStateChange={setSomeState} /&amp;gt;
    &amp;lt;/&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;당신은 이런 방식을 사용할 수 있을 것이다:&lt;/p&gt;
&lt;pre id=&quot;code_1620543967079&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function App() {
  const [someState, setSomeState] = React.useState('some state')
  return (
    &amp;lt;&amp;gt;
      &amp;lt;Header
        logo={&amp;lt;Logo someState={someState} /&amp;gt;}
        settings={&amp;lt;Settings onStateChange={setSomeState} /&amp;gt;}
      /&amp;gt;
      &amp;lt;LeftNav&amp;gt;
        &amp;lt;SomeLink someState={someState} /&amp;gt;
        &amp;lt;SomeOtherLink someState={someState} /&amp;gt;
        &amp;lt;Etc someState={someState} /&amp;gt;
      &amp;lt;/LeftNav&amp;gt;
      &amp;lt;MainContent&amp;gt;
        &amp;lt;SomeSensibleComponent someState={someState} /&amp;gt;
        &amp;lt;AndSoOn someState={someState} /&amp;gt;
      &amp;lt;/MainContent&amp;gt;
    &amp;lt;/&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;만약 위 예시가 명확히 이해되지 않는다면 (왜냐면 위 예시는 매우 억지로 꾸며낸 것이기 때문이다), 내가 말한 것이 어떤 의미인지 여러분이 분명하게 알 수 있도록 도와주는&amp;nbsp;&lt;a href=&quot;https://twitter.com/mjackson&quot;&gt;Michael Jackson&lt;/a&gt;&lt;span style=&quot;color: #333333;&quot;&gt;의&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=3XaXKiXtNjw&quot;&gt;멋진 비디오&lt;/a&gt;들을 봐도 좋다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그럼에도 불구하고 결국 합성조차도 당신이 원하는 일을 하지 못할 수 있고, 그랬을 때 다음 단계는 리액트의 Context API로 뛰어드는 것이다. 이는 실제로 오래도록 &quot;해결책&quot;이 되어주었지만, 오랫동안 &quot;비공식적인&quot; 해결책이기도 했다. 내가 말했듯이, 많은 사람들은 react-redux로 접근했다. 왜냐면 이 것이 사람들이 리액트 문서의 경고 문구를 걱정하지 않고도 적용할 수 있으면서, 내가 언급한 매커니즘을 이용하여 문제를 해결했기 때문이다. 그러나 이제 context는 리액트 API에서 공식적으로 지원되며, 우리는 별다른 문제 없이 바로 이 기능을 사용할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1620562891692&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// src/count/count-context.js
import * as React from 'react'
const CountContext = React.createContext()
function useCount() {
  const context = React.useContext(CountContext)
  if (!context) {
    throw new Error(`useCount must be used within a CountProvider`)
  }
  return context
}
function CountProvider(props) {
  const [count, setCount] = React.useState(0)
  const value = React.useMemo(() =&amp;gt; [count, setCount], [count])
  return &amp;lt;CountContext.Provider value={value} {...props} /&amp;gt;
}
export {CountProvider, useCount}
// src/count/page.js
import * as React from 'react'
import {CountProvider, useCount} from './count-context'
function Counter() {
  const [count, setCount] = useCount()
  const increment = () =&amp;gt; setCount(c =&amp;gt; c + 1)
  return &amp;lt;button onClick={increment}&amp;gt;{count}&amp;lt;/button&amp;gt;
}
function CountDisplay() {
  const [count] = useCount()
  return &amp;lt;div&amp;gt;The current counter count is {count}&amp;lt;/div&amp;gt;
}
function CountPage() {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;CountProvider&amp;gt;
        &amp;lt;CountDisplay /&amp;gt;
        &amp;lt;Counter /&amp;gt;
      &amp;lt;/CountProvider&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://codesandbox.io/s/4qzj73lozx?fontsize=14&amp;amp;hidenavigation=1&amp;amp;theme=dark&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;img src=&quot;https://codesandbox.io/static/img/play-codesandbox.svg&quot; alt=&quot;Edit Application State Management with React&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-size=&quot;size16&quot; data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;노트: 이 특정한 코드 예시는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;매우&lt;/b&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;인위적이며 나는 여러분들이 이러한 특정한 시나리오를 해결하기 위해 context에 바로 도달하는 것을 추천하지&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;않는다&lt;/b&gt;&lt;span style=&quot;color: #666666;&quot;&gt;. 왜 prop drilling이 반드시 문제인 것만은 아니고, 오히려 종종 바람직한 일인지 더 잘 이해하기 위해&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://kentcdodds.com/blog/prop-drilling&quot;&gt;Prop Drilling&lt;/a&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;글을 읽어보길 바란다. context에 너무 빨리 접근하지 말길!&lt;/span&gt;&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 접근법의 멋진 점은 우리의 useCount hook에 상태를 업데이트하기 위한 모든 공통적인 로직들을 넣을 수 있다는 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1620544021624&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function useCount() {
  const context = React.useContext(CountContext)

  if (!context) {
    throw new Error(`useCount must be used within a CountProvider`)
  }

  const [count, setCount] = context
  const increment = () =&amp;gt; setCount(c =&amp;gt; c + 1)

  return {
    count,
    setCount,
    increment,
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://codesandbox.io/s/4qzj73lozx?fontsize=14&amp;amp;hidenavigation=1&amp;amp;theme=dark&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;img src=&quot;https://codesandbox.io/static/img/play-codesandbox.svg&quot; alt=&quot;Edit Application State Management with React&quot; /&gt; &lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그리고 당신은 또한 이 로직을 useState 대신 useReducer로 쉽게 변경할 수도 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1620544042067&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function countReducer(state, action) {
  switch (action.type) {
    case 'INCREMENT': {
      return {count: state.count + 1}
    }
    default: {
      throw new Error(`Unsupported action type: ${action.type}`)
    }
  }
}

function CountProvider(props) {
  const [state, dispatch] = React.useReducer(countReducer, {count: 0})
  const value = React.useMemo(() =&amp;gt; [state, dispatch], [state])
  return &amp;lt;CountContext.Provider value={value} {...props} /&amp;gt;
}

function useCount() {
  const context = React.useContext(CountContext)

  if (!context) {
    throw new Error(`useCount must be used within a CountProvider`)
  }

  const [state, dispatch] = context
  const increment = () =&amp;gt; dispatch({type: 'INCREMENT'})

  return {
    state,
    dispatch,
    increment,
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://codesandbox.io/s/4qzj73lozx?fontsize=14&amp;amp;hidenavigation=1&amp;amp;theme=dark&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; &lt;img src=&quot;https://codesandbox.io/static/img/play-codesandbox.svg&quot; alt=&quot;Edit Application State Management with React&quot; /&gt; &lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 것은 우리에게 엄청나게 많은 유연성을 주고, 매우 복잡도를 매우 낮춰준다. 이 때 위와 같은 방법으로 일을 진행할 때 기억해야 할 몇 가지 중요한 것들이 있는데 다음과 같다:&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;어플리케이션의 모든 것들이 하나의 상태 객체를 필요로 하지는 않는다. 모든 것들을 논리적으로 분리하자. (유저 설정 값들이 반드시 알림과 같은 context 내에 위치해야만 하는 것은 아니다.) 여러분은 이러한 관점으로 여러 개의 provider를 사용해야할 것이다.&lt;/li&gt;
&lt;li&gt;여러분의 모든 context를 전역에서 접근 가능하도록 만들 필요는 없다! &lt;b&gt;상태를 가능한 한 그 상태를 필요로 하는 곳 근처에 두자.&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;2번째 관점을 조금 더 살펴보자. 당신의 앱 트리는 대충 다음과 같이 보여질 수 있다:&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1620544072425&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function App() {
  return (
    &amp;lt;ThemeProvider&amp;gt;
      &amp;lt;AuthenticationProvider&amp;gt;
        &amp;lt;Router&amp;gt;
          &amp;lt;Home path=&quot;/&quot; /&amp;gt;
          &amp;lt;About path=&quot;/about&quot; /&amp;gt;
          &amp;lt;UserPage path=&quot;/:userId&quot; /&amp;gt;
          &amp;lt;UserSettings path=&quot;/settings&quot; /&amp;gt;
          &amp;lt;Notifications path=&quot;/notifications&quot; /&amp;gt;
        &amp;lt;/Router&amp;gt;
      &amp;lt;/AuthenticationProvider&amp;gt;
    &amp;lt;/ThemeProvider&amp;gt;
  )
}

function Notifications() {
  return (
    &amp;lt;NotificationsProvider&amp;gt;
      &amp;lt;NotificationsTab /&amp;gt;
      &amp;lt;NotificationsTypeList /&amp;gt;
      &amp;lt;NotificationsList /&amp;gt;
    &amp;lt;/NotificationsProvider&amp;gt;
  )
}

function UserPage({username}) {
  return (
    &amp;lt;UserProvider username={username}&amp;gt;
      &amp;lt;UserInfo /&amp;gt;
      &amp;lt;UserNav /&amp;gt;
      &amp;lt;UserActivity /&amp;gt;
    &amp;lt;/UserProvider&amp;gt;
  )
}

function UserSettings() {
  // 이 것은 아마도 AuthenticationProvider와 연관된 hook일 것이다
  const {user} = useAuthenticatedUser()
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;각각의 페이지가 해당 페이지 내부 컴포넌트들이 필요로하는 데이터를 가진 고유한 provider를 가질 수 있다는 점을 주목하자. Code splitting은 이런 것에 그냥 딱 작동한다. 당신이 각 provider에 어떻게 데이터를 넣는지는 해당 provider가 사용하는 hook에 달려있고, 또한 당신이 어플리케이션에 데이터를 어떻게 불러오는지에 달려있다. 하지만 여러분은 그것들이 (provider 안에서) 어떻게 작동하는지 알아 볼 시작점을 알고 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;왜 동일 장소 배치(colocation)가 유익한지에 대해 더 많이 알고 싶다면, &quot;&lt;a href=&quot;https://kentcdodds.com/blog/state-colocation-will-make-your-react-app-faster&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;상태 동일 장소 배치는 당신의 리액트 앱을 더 빠르게 만듭니다&lt;/a&gt;&quot;와 &quot;&lt;a href=&quot;https://kentcdodds.com/blog/colocation&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;동일 장소 배치(Colocation)&lt;/a&gt;&quot;라는 블로그 글을 읽어보아라. 그리고 context에 더 많이 알고 싶다면 &quot;&lt;a href=&quot;https://kentcdodds.com/blog/how-to-use-react-context-effectively&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;리액트에서 효과적으로 context를 사용하는 법&lt;/a&gt;&quot;이라는 글을 읽어보아라.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Server Cache vs UI State (서버 캐시 vs UI 상태)&lt;/h2&gt;
&lt;p&gt;마지막으로 덧붙이고 싶은 한 가지가 있다. 세상에는 다양한 종류의 상태가 있지만, 모든 상태의 타입은 (다음) 두 개 중 한 가지에 해당한다:&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;서버 캐시 - 사실상 서버에 저장되어 있고 빠른 접근을 위해 클라이언트에 저장하는 상태. (유저 데이터와 같은 것)&lt;/li&gt;
&lt;li&gt;UI 상태 - 오로지 우리 앱의 인터렉션을 제어하기 위한 UI에서만 유용한 상태. (모달의 isOpen 과 같은 것)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;우리는 두 가지를 하나로 합치면서 실수를 범한다. 서버 캐시는 본질적으로 UI 상태와는 다른 문제이고 따라서 다른 방법으로 관리되어야만 한다. 만약에 여러분이 가지고 있는 것이 사실은 상태값이 아니라 상태값의 캐시라는 사실을 받아들인다면, 여러분은 이제 그것들을 적절하게 생각하기 시작할 것이고, 따라서 &lt;span style=&quot;color: #333333;&quot;&gt;적절하게&lt;/span&gt; 관리할 수 있을 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;여러분은 물론 올바른 useContext를 여기 저기에 사용하며 여러분만의 useState나 useReducer를 통해 스스로 이 것을 관리할 수 있다. 그렇지만 나는 여러분이 바로 본론에 들어갈 수 있도록 돕기 위해 다음과 같이 말하려고 한다. 캐싱은 정말 어려운 문제이고 (몇몇 사람들은 캐싱이 컴퓨터 공학 사상 가장 어려운 것이라고 말한다) 그렇기 때문에 이 문제에는 다른 사람이 이미 만들어둔 훌륭한 업적들을 활용하는 것이 현명할 수 있다고 말이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 것이 바로 내가 이런 종류의 상태를 다루기 위해 &lt;a href=&quot;https://github.com/tannerlinsley/react-query&quot;&gt;react-query&lt;/a&gt;를 사용하고 추천하는 이유이다. 물론 내가 (위에서) 상태 관리 라이브러리가 필요없다고 말했다는 것을 알고 있다. 하지만 나는 react-query가 상태 관리 라이브러리라고는 별로 생각하지 않는다. 나는 이 것이 캐시(Cache)를 다루는 라이브러리라고 생각한다. 그리고 이건 정말 끝내 주게 좋은 라이브러리다. 한 번 살펴보라! &lt;a href=&quot;https://twitter.com/tannerlinsley&quot;&gt;Tanner Linsley&lt;/a&gt;는 정말 똑똑한 친구다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;What about performance? (성능 문제는 어떤가요?)&lt;/h2&gt;
&lt;p&gt;만약 여러분이 위의 조언들을 따른다면, 성능은 거의 문제가 되지 않을 것이다. 특히 &lt;a href=&quot;https://kentcdodds.com/blog/state-colocation-will-make-your-react-app-faster&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;colocation에 대한 조언&lt;/a&gt;을 따른다면 말이다. 그러나 성능이 문제가 될 수 있는 사례들도 분명히 존재한다. 만약 상태와 관련해 성능 문제를 겪고 있다면, 가장 먼저 확인해야하는 것은 얼마나 많은 컴포넌트들이 상태 변화로 인해 re-render가 되는지와 해당 컴포넌트들에게 정말로 그 상태 변화로 인한 re-render가 필요한 것인지 여부를 알아내는 것이다. 만약 (re-render가) 꼭 필요한 것이라면, 성능 문제는 더 이상 상태 관리를 위한 메커니즘과는 상관이 없고, render 속도와 상관이 있어진다. 그리고 그런 경우에 여러분은 &lt;a href=&quot;https://kentcdodds.com/blog/fix-the-slow-render-before-you-fix-the-re-render&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;render 속도를 높일&lt;/a&gt; 필요가 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그러나 만약 DOM 업데이트가 없거나 사이드 이펙트를 필요로 하지 않는데도 렌더링되고 있는 컴포넌트가 많다는 것을 발견했다면, 이 컴포넌트들은 불필요하게 렌더링되고 있는 것이다. 이런 일들은 React에 항상 일어나는 일이고 이 자체로는 보통 문제가 되진 않는다 (그리고 당신은 심지어 이런 불필요한 &lt;a href=&quot;https://kentcdodds.com/blog/fix-the-slow-render-before-you-fix-the-re-render&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;re-render가 더 빨라지도록 만드는 데 먼저 집중해야 한다&lt;/a&gt;). 그러나 이 문제가 정말 장애 요소라면, 여기 React context에서의 상태 문제를 해결하는 몇 가지 소소한 접근 방법들이 있다:&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;당신의 상태를 하나의 큰 저장소 대신 몇 개의 다른 논리적인 조각들로 분리하라. 그러면 어떤 부분적인 상태의 변화가 앱의 모든 컴포넌트 업데이트를 유발시키진 않는다.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kentcdodds.com/blog/how-to-use-react-context-effectively&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;당신의 context provider를 최적화 시켜라.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/pmndrs/jotai&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;jotai&lt;/a&gt;를 사용하자.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;라이브러리 하나를 &lt;span style=&quot;color: #333333;&quot;&gt;또 &lt;/span&gt;추천했다. 사실 리액트에 내장된 상태 관리 추상화가 잘 맞지 않는 유즈 케이스도 가끔은 존재한다. 이러한 유즈 케이스들에는 모든 사용 가능한 추상화 라이브러리 중에서 jotai가 가장 괜찮다. 이런 유즈 케이스가 어떤 경우를 말하는지 궁금하다면, jotai가 어떤 문제들을 해결했는지 아주 잘 설명된 글, '&lt;a href=&quot;https://www.youtube.com/watch?v=_ISAA_Jt9kI&quot;&gt;Recoil: State Management for Today's React - Dave McCabe aka @mcc_abe at @ReactEurope 2020&lt;/a&gt;'을 읽어보자. Recoil과 jotai는 매우 비슷하다. (그리고 비슷한 타입의 문제를 해결한다.) 그러나 나의 (한정된) 사용 경험을 근거로 나는 jotai를 선호한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;어쨌든, 대부분의 앱들은 recoil이나 jotai같은 atomic한 상태 관리 툴을 필요로 하지 않는다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Conclusion (결론)&lt;/h2&gt;
&lt;p&gt;다시 말하지만, 이것들은 여러분이 클래스 컴포넌트를 사용해도 해볼 수 있는 것들이다. (여러분이 꼭 hook을 사용할 필요는 없다.) Hook은 이 문제들을 훨씬 더 쉽게 만들지만, 이러한 철학을 React 15와도 아무 문제 없이 적용할 수 있다. 상태를 가능한 한 지역적으로 두고, prop drilling이 정말 문제가 된다면 오직 context만 사용하라. 이렇게 사용하는 것이 여러분의 상태 인터렉션 관리를 더욱 쉽게 만들어준다.&lt;/p&gt;</description>
      <category>Frontend</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/222</guid>
      <comments>https://im-developer.tistory.com/222#entry222comment</comments>
      <pubDate>Sun, 9 May 2021 21:25:01 +0900</pubDate>
    </item>
    <item>
      <title>[LeetCode] Arrays101 - Remove Duplicates from Sorted Array</title>
      <link>https://im-developer.tistory.com/221</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Remove&amp;nbsp;Duplicates&amp;nbsp;from&amp;nbsp;Sorted&amp;nbsp;Array&lt;/h2&gt;
&lt;p&gt;Given a sorted array&lt;span&gt;&amp;nbsp;&lt;/span&gt;nums, remove the duplicates&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/In-place_algorithm&quot;&gt;&lt;b&gt;in-place&lt;/b&gt;&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;such that each element appears only&lt;span&gt;&amp;nbsp;&lt;/span&gt;once&lt;span&gt;&amp;nbsp;&lt;/span&gt;and returns the new length.&lt;/p&gt;
&lt;p&gt;Do not allocate extra space for another array, you must do this by&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;modifying the input array&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/In-place_algorithm&quot;&gt;in-place&lt;/a&gt;&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;with O(1) extra memory.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Clarification:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Confused why the returned value is an integer but your answer is an array?&lt;/p&gt;
&lt;p&gt;Note that the input array is passed in by&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;reference&lt;/b&gt;, which means a modification to the input array will be known to the caller as well.&lt;/p&gt;
&lt;p&gt;Internally you can think of this:&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1620543059990&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// nums is passed in by reference. (i.e., without making a copy)
int len = removeDuplicates(nums);

// any modification to nums in your function would be known by the caller.
// using the length returned by your function, it prints the first len elements.
for (int i = 0; i &amp;lt; len; i++) {
    print(nums[i]);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Example 1:&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1620543088082&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Input: nums = [1,1,2]
Output: 2, nums = [1,2]
Explanation: Your function should return length = 2, with the first two elements of nums being 1 and 2 respectively. It doesn't matter what you leave beyond the returned length.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Example 2:&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1620543107914&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Input: nums = [0,0,1,1,1,2,2,3,3,4]
Output: 5, nums = [0,1,2,3,4]
Explanation: Your function should return length = 5, with the first five elements of nums being modified to 0, 1, 2, 3, and 4 respectively. It doesn't matter what values are set beyond the returned length.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Constraints:&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;0 &amp;lt;= nums.length &amp;lt;= 3 * 104&lt;/li&gt;
&lt;li&gt;-104&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;lt;= nums[i] &amp;lt;= 104&lt;/li&gt;
&lt;li&gt;nums&amp;nbsp;is sorted in ascending order.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://im-developer.tistory.com/220&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Remove Elements&lt;/a&gt; 문제로 고생을 많이 했더니 비슷한 문제는 비교적 금방 풀었다!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;일단 배열이 정렬이 된 상태로 들어오기 때문에&lt;/p&gt;
&lt;p&gt;pointer를 2개로 두고 값이 변경되는 순간을 인지해서 값을 덮어쓰기 하면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1620543133865&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
 * @param {number[]} nums
 * @return {number}
 */
var removeDuplicates = function(nums) {
    let i = 0;
    
    for (let j = 1; j &amp;lt; nums.length; j++) {
        if (nums[j] !== nums[i]) {
            nums[++i] = nums[j];
        }
    }
    
    return i + 1;
};&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Algorithm</category>
      <category>Algorithm</category>
      <category>JavaScript</category>
      <category>leetcode</category>
      <category>알고리즘</category>
      <category>자바스크립트</category>
      <category>코딩</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/221</guid>
      <comments>https://im-developer.tistory.com/221#entry221comment</comments>
      <pubDate>Sun, 9 May 2021 15:57:56 +0900</pubDate>
    </item>
    <item>
      <title>[LeetCode] Arrays101 - Remove Element</title>
      <link>https://im-developer.tistory.com/220</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Remove&amp;nbsp;Element&lt;/h2&gt;
&lt;p&gt;Given an array&lt;span&gt;&amp;nbsp;&lt;/span&gt;nums&lt;span&gt;&amp;nbsp;&lt;/span&gt;and a value&lt;span&gt;&amp;nbsp;&lt;/span&gt;val, remove all instances of that value&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/In-place_algorithm&quot;&gt;&lt;b&gt;in-place&lt;/b&gt;&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;and return the new length.&lt;/p&gt;
&lt;p&gt;Do not allocate extra space for another array, you must do this by&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;modifying the input array&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/In-place_algorithm&quot;&gt;in-place&lt;/a&gt;&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;with&lt;span&gt;&amp;nbsp;&lt;/span&gt;O(1)&lt;span&gt;&amp;nbsp;&lt;/span&gt;extra memory.&lt;/p&gt;
&lt;p&gt;The order of elements can be changed. It doesn't matter what you leave beyond the new length.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Clarification:&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Confused why the returned value is an integer but your answer is an array?&lt;/p&gt;
&lt;p&gt;Note that the input array is passed in by&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;reference&lt;/b&gt;, which means a modification to the input array will be known to the caller as well.&lt;/p&gt;
&lt;p&gt;Internally you can think of this:&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1620535035774&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// nums is passed in by reference. (i.e., without making a copy)
int len = removeElement(nums, val);

// any modification to nums in your function would be known by the caller.
// using the length returned by your function, it prints the first len elements.
for (int i = 0; i &amp;lt; len; i++) {
    print(nums[i]);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Example 1:&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1620535047682&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Input: nums = [3,2,2,3], val = 3
Output: 2, nums = [2,2]
Explanation: Your function should return length = 2, with the first two elements of nums being 2.
It doesn't matter what you leave beyond the returned length. For example if you return 2 with nums = [2,2,3,3] or nums = [2,2,0,0], your answer will be accepted.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Example 2:&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1620535065889&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Input: nums = [0,1,2,2,3,0,4,2], val = 2
Output: 5, nums = [0,1,4,0,3]
Explanation: Your function should return length = 5, with the first five elements of nums containing 0, 1, 3, 0, and 4. Note that the order of those five elements can be arbitrary. It doesn't matter what values are set beyond the returned length.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Constraints:&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;0 &amp;lt;= nums.length &amp;lt;= 100&lt;/li&gt;
&lt;li&gt;0 &amp;lt;= nums[i] &amp;lt;= 50&lt;/li&gt;
&lt;li&gt;0 &amp;lt;= val &amp;lt;= 100&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;배열에서 특정 값을 찾아서 삭제를 하거나 아니면 맨 뒤로 보내버리고,&lt;/p&gt;
&lt;p&gt;나머지 값들의 개수를 리턴하는 문제이다.&lt;/p&gt;
&lt;p&gt;역시나 어려운 이유는 &lt;b&gt;In-place&lt;/b&gt;로 풀어야한다는 점 때문...&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1620535117056&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
 * @param {number[]} nums
 * @param {number} val
 * @return {number}
 */
var removeElement = function(nums, val) {
    for (let i = 0; i &amp;lt; nums.length; i++) {
        if (nums[i] === val) {
            nums.splice(i, 1);
            i--;
        }
    }
    
    return nums.length;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이렇게 JavaScript에 내장된 splice 메소드를 사용하면 3분만에 쉽게 풀 수 있지만&lt;/p&gt;
&lt;p&gt;이 메소드를 사용하지 않고 풀어내는 방법이 있을게 분명하니 또 머리를 쥐어뜯으며 고민을 시작했다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1046&quot; data-origin-height=&quot;566&quot; data-filename=&quot;blob&quot; width=&quot;510&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9ReH9/btq4tzdiKiF/FBgMHNDMWBG31kw3W0iwx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9ReH9/btq4tzdiKiF/FBgMHNDMWBG31kw3W0iwx1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9ReH9/btq4tzdiKiF/FBgMHNDMWBG31kw3W0iwx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9ReH9%2Fbtq4tzdiKiF%2FFBgMHNDMWBG31kw3W0iwx1%2Fimg.png&quot; data-origin-width=&quot;1046&quot; data-origin-height=&quot;566&quot; data-filename=&quot;blob&quot; width=&quot;510&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그래서 처음 생각해낸 방법은 이런 방법이다.&lt;/p&gt;
&lt;p&gt;앞에서부터 순회를 하다가 2를 만나면 바로 다음 인덱스부터 2가 아닌 값을 찾아서 swap을 하는 방식이다.&lt;/p&gt;
&lt;p&gt;단점은 바로 다음 2를 찾는 일이 너무 에너지를 많이 소모해서 효율성이 좋지 않다는 점이다.&lt;/p&gt;
&lt;p&gt;이 방법을 코드로 표현하면 다음과 같다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1620535127036&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
 * @param {number[]} nums
 * @param {number} val
 * @return {number}
 */
var findIndex = function(array, startIndex, condition) {
    for (let i = startIndex; i &amp;lt;= array.length - 1; i++) {
        if (condition(array[i])) return i;
    }
    return -1;
};

var swap = function(array, index1, index2) {
    const temp = array[index1];
    array[index1] = array[index2];
    array[index2] = temp;
};

var removeElement = function(nums, val) {
    let valIndex = 0;

    for (let i = 0; i &amp;lt;= nums.length - 1; i++) {
        if (nums[i] === val) {
            const nextItemIndex = findIndex(nums, i + 1, (item) =&amp;gt; item !== val);

            if (nextItemIndex === -1) break;
            valIndex++;
            swap(nums, i, nextItemIndex);
        } else {
            valIndex++;
        }
    }
    
    return valIndex;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;딱 봐도 그닥 효율이 좋아보이진 않는다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그래서 다시 고민 고민하다가 생각한 더 나은 방법은 앞뒤에서 동시에 탐색을 하는 방법이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;825&quot; data-origin-height=&quot;435&quot; data-filename=&quot;blob&quot; width=&quot;529&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJ3wLD/btq4rIn3L2D/mwMzx7ip0foDpDgahzcIpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJ3wLD/btq4rIn3L2D/mwMzx7ip0foDpDgahzcIpk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJ3wLD/btq4rIn3L2D/mwMzx7ip0foDpDgahzcIpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJ3wLD%2Fbtq4rIn3L2D%2FmwMzx7ip0foDpDgahzcIpk%2Fimg.png&quot; data-origin-width=&quot;825&quot; data-origin-height=&quot;435&quot; data-filename=&quot;blob&quot; width=&quot;529&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;그러니까 앞에서 시작하는 탐색은 2가 발견할 때까지 index를 더하면서 탐색을 하고,&lt;/p&gt;
&lt;p&gt;뒤에서 시작하는 탐색은 2가 아닌 값을 발견할 때까지 index를 빼면서 탐색한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그리고 앞에서 2를 발견하고 뒤에서 2가 아닌 값을 발견하면 두 값을 swap한다!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이걸 startIndex가 endIndex보다 작거나 같을 때까지 반복하고 나면&lt;/p&gt;
&lt;p&gt;최종적으로는 startIndex가 2가 아닌 값들의 개수가 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1620535141527&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
 * @param {number[]} nums
 * @param {number} val
 * @return {number}
 */
var removeElement = function(nums, val) {
    let startIndex = 0;
    let endIndex = nums.length - 1;
    
    while (startIndex &amp;lt;= endIndex) {
        if (nums[startIndex] === val &amp;amp;&amp;amp; nums[endIndex] !== val) {
            const temp = nums[startIndex];
            nums[startIndex] = nums[endIndex];
            nums[endIndex] = temp;
        }
        
        if (nums[startIndex] !== val) {
            startIndex++;
        }
        if (nums[endIndex] === val) {
            endIndex--;
        }
    }
    
    return startIndex;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;여기까지 해놓고 이만하면 잘 풀었다고 너무 좋아했는데&lt;/p&gt;
&lt;p&gt;솔루션을 보니 훨씬 더 간단하게 해결할 수 있었다는걸 알게되었다.ㅠㅠ&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;The order of elements can be changed. It doesn't matter what you leave beyond the new length.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p&gt;그러니까 위 문장이 이 문제에서 아주 중요한 부분이었다...&lt;/p&gt;
&lt;p&gt;나는 꾸역꾸역 swap을 해서 문제를 해결했는데,&lt;/p&gt;
&lt;p&gt;사실 굳이 swap을 할 필요없이 뒤에서부터 2가 아닌 값을 찾아서 앞의 2를 덮어쓰기만 하면 되는거였다.&lt;/p&gt;
&lt;p&gt;왜냐면 뒤에 있는 값에 뭐가 들어있던 상관이 없으니까..!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;637&quot; data-origin-height=&quot;379&quot; data-filename=&quot;blob&quot; width=&quot;435&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfGJW6/btq4wuo2Bwo/M7yVCY2Ok4z2XmAiHtVfrk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfGJW6/btq4wuo2Bwo/M7yVCY2Ok4z2XmAiHtVfrk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfGJW6/btq4wuo2Bwo/M7yVCY2Ok4z2XmAiHtVfrk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfGJW6%2Fbtq4wuo2Bwo%2FM7yVCY2Ok4z2XmAiHtVfrk%2Fimg.png&quot; data-origin-width=&quot;637&quot; data-origin-height=&quot;379&quot; data-filename=&quot;blob&quot; width=&quot;435&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1620537482150&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
 * @param {number[]} nums
 * @param {number} val
 * @return {number}
 */
var removeElement = function(nums, val) {
    let startIndex = 0;
    let endIndex = nums.length - 1;
    
    while (startIndex &amp;lt;= endIndex) {
        if (nums[startIndex] === val) {
            nums[startIndex] = nums[endIndex];
            endIndex--;
        } else {
            startIndex++;
        }
        
    }
    
    return startIndex;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그리고 진짜 코드로는 제일 간단해보였지만 이해하기는 제일 어려웠던 방법은 다음과 같은 방법이다.&lt;/p&gt;
&lt;p&gt;(이 방법이 가능한 이유는 아이템의 순서를 신경쓰지 않아도 되기 때문이다.)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;681&quot; data-origin-height=&quot;363&quot; data-filename=&quot;blob&quot; width=&quot;465&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cEjyrM/btq4p7a4Olp/1YRiUUVcKmUSEOGWLOqiE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cEjyrM/btq4p7a4Olp/1YRiUUVcKmUSEOGWLOqiE1/img.png&quot; data-alt=&quot;이 방법은 너무 헷갈려서 진짜 index 하나 하나를 종이에 그려가면서 이해했다.ㅠㅠ&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cEjyrM/btq4p7a4Olp/1YRiUUVcKmUSEOGWLOqiE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcEjyrM%2Fbtq4p7a4Olp%2F1YRiUUVcKmUSEOGWLOqiE1%2Fimg.png&quot; data-origin-width=&quot;681&quot; data-origin-height=&quot;363&quot; data-filename=&quot;blob&quot; width=&quot;465&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이 방법은 너무 헷갈려서 진짜 index 하나 하나를 종이에 그려가면서 이해했다.ㅠㅠ&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1620538735468&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
 * @param {number[]} nums
 * @param {number} val
 * @return {number}
 */
var removeElement = function(nums, val) {
    let i = 0;
    
    for (let j = 0; j &amp;lt; nums.length; j++) {
        if (nums[j] !== val) {
            nums[i] = nums[j];
            i++;
        }
    }
    
    return i;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;포인터를 i랑 j 2개로 두고 j는 계속 1씩 증가하되, i는 j번째 아이템이 val이 아닐때만 1씩 증가한다.&lt;/p&gt;
&lt;p&gt;그리고 j번째 아이템이 val이 아닐 때 j번째 아이템을 i번째 아이템으로 덮어쓰는 방법이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Algorithm</category>
      <category>Algorithm</category>
      <category>JavaScript</category>
      <category>leetcode</category>
      <category>알고리즘</category>
      <category>자바스크립트</category>
      <category>코딩</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/220</guid>
      <comments>https://im-developer.tistory.com/220#entry220comment</comments>
      <pubDate>Sun, 9 May 2021 14:51:33 +0900</pubDate>
    </item>
    <item>
      <title>location.href vs location.assign vs location.replace 차이점</title>
      <link>https://im-developer.tistory.com/219</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;1200&quot; width=&quot;226&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yzhze/btq4s36pCOg/8QTGZep6BOBHkucMYQRACK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yzhze/btq4s36pCOg/8QTGZep6BOBHkucMYQRACK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yzhze/btq4s36pCOg/8QTGZep6BOBHkucMYQRACK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fyzhze%2Fbtq4s36pCOg%2F8QTGZep6BOBHkucMYQRACK%2Fimg.jpg&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;1200&quot; width=&quot;226&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어쩌다 회사 회의 시간에 location.href에 string을 할당하는 방식이랑&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;location.assign을 쓰는 방식 중에 어떤거를 쓰는 것이 더 좋을까에 관한 얘기가 나왔는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순간 2개의 차이점이 뭔지 헷갈려서 찾아본 내용을 간단히 정리해보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;window.location&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;read-only&lt;/b&gt; 속성을 가진 window.location 은 document의 현재 location 정보를 담은 Location 객체를 리턴해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;window.location은 read-only Location 객체임에도 불구하고 DOMString을 할당할 수 있다. 이 말인즉슨 대부분의 경우 location이 마치 string인 것처럼 사용할 수 있다는 뜻이다.&lt;/p&gt;
&lt;pre class=&quot;glsl&quot;&gt;&lt;code&gt;// 아래 2개는 완전히 같다.
location = 'http://www.example.com';
location.href = 'http://www.example.com';&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Location properties&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;[Location.ancestorOrigins]:&lt;/b&gt; 주어진 Location 객체와 연관된 document의 모든 조상 browsing context들이 역순으로 담긴 static한 DOMStringList이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;[Location.href]:&lt;/b&gt; 전체 URL을 담고있는 USVString을 리턴하는 stringifier이다. 값이 바뀌면 연관된 document는 새로운 페이지로 이동한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;[Location.protocol]&lt;/b&gt;: URL의 프로토콜 스키마를 담고 있는 USVString]이다. (마지막 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;:&lt;/b&gt;&lt;/span&gt;를 포함한다.)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;[Location.host]&lt;/b&gt;: host를 담고 있는 USVString이다. (&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;:&lt;/b&gt;&lt;/span&gt;와 URL의 포트번호를 포함한다.)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;[Location.hostname]&lt;/b&gt;: URL의 도메인을 담고 있는 USVString이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;[Location.port]&lt;/b&gt;: URL의 포트 번호를 담고 있는 USVString이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;[Location.pathname]&lt;/b&gt;: 최초 / 뒤에 나오는 path들을 담고 있는 USVString이다. (query string이나 fragment는 포함하지 않는다.)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;[Location.search]&lt;/b&gt;: ? 를 포함하여 그 뒤에 나오는 URL의 파라미터나 querystring을 담은 USVString이다. 모던 브라우저들은 querystring으로부터 parameter들을 쉽게 파싱할 수 있도록&amp;nbsp;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams/get#example&quot;&gt;URLSearchParams&lt;/a&gt;나&amp;nbsp;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/URL/searchParams#example&quot;&gt;URL.searchParams&lt;/a&gt;등을 제공해준다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;[Location.hash]&lt;/b&gt;: # 를 포함하여 뒤에 나오는 fragment 식별자를 담은 USVString이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;[Location.origin]&lt;/b&gt;: &amp;nbsp;&lt;b&gt;Read only&lt;/b&gt; - 대표 URL origin의 유니코드 직렬화를 담고 있는 USVString이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Location methods&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;[Location.assign()]&lt;/b&gt;: 파라미터로 전달한 URL에서 리소스를 로드한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;[Location.reload()]&lt;/b&gt;: 리프레시 버튼과 같이 현재의 URL을 다시 로드한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;[Location.replace()]&lt;/b&gt;: (파라미터로 전달된 URL로 리다이렉트하면서) 전달된 URL의 리소스로 현재의 리소스를 교체한다. &lt;br /&gt;&lt;b&gt;assign() 메소드나 href 속성을 교체하는 것과 replace()가 다른 점:&lt;/b&gt; replace()를 사용하고나면 현재의 페이지는 session &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/History&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;History&lt;/a&gt;에 저장되지 않는다. 이 말은 back 버튼을 눌러도 다시 이 전 페이지로 이동할 수 없다는 뜻이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;[Location.toString()]&lt;/b&gt;: 전체 URL을 담고 있는 USVString을 리턴한다. HTMLHyperlinkElementUtils.href 와&amp;nbsp;동일한 기능이지만 값을 수정하기 위해 사용할 수는 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 3개는 완전히 동일한 기능(&lt;span style=&quot;color: #333333;&quot;&gt;새 URL로 이동)&lt;/span&gt;을 한다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;window.location.assign(url);
window.location = url;
window.location.href = url;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;replace 메소드도 똑같이 새 URL로 이동하는 기능이지만 이동하기 직전의 페이지가 history에 추가되지 않는다는 점이 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;lt;기타 참고사항 | 테스트 코드에서 Location mocking하기&amp;gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;integration test code에서 페이지가 제대로 이동하는지 여부를 테스트하고 싶을 때가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트를 위해 location 객체를 사전에 mocking을 해둬야하는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 location이 read-only 객체이기 때문에 그냥 할당을 하는 방식으로 mocking을 하면 TypeScript가&amp;nbsp;에러를 뱉는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 아래 코드를 test-setup 파일에 넣어서 location 객체를 writable 가능하도록 만들어주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;Object.defineProperty(window, 'location', {
  value: {
    ...window.location,
  },
  writable: true,
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Reference&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Window/location&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/API/Window/location&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/7703689/difference-between-window-location-href-window-location-replace-and-window-loca&quot;&gt;https://stackoverflow.com/questions/7703689/difference-between-window-location-href-window-location-replace-and-window-loca&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Frontend</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/219</guid>
      <comments>https://im-developer.tistory.com/219#entry219comment</comments>
      <pubDate>Sun, 9 May 2021 00:33:14 +0900</pubDate>
    </item>
    <item>
      <title>[LeetCode] Arrays101 - Merge Sorted Array</title>
      <link>https://im-developer.tistory.com/218</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;Merge Sorted Array&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;Given two sorted integer arrays&lt;span&gt;&amp;nbsp;&lt;/span&gt;nums1&lt;span&gt;&amp;nbsp;&lt;/span&gt;and&lt;span&gt;&amp;nbsp;&lt;/span&gt;nums2, merge&lt;span&gt;&amp;nbsp;&lt;/span&gt;nums2&lt;span&gt;&amp;nbsp;&lt;/span&gt;into&lt;span&gt;&amp;nbsp;&lt;/span&gt;nums1&lt;span&gt;&amp;nbsp;&lt;/span&gt;as one sorted array.&lt;/p&gt;
&lt;p&gt;The number of elements initialized in&lt;span&gt;&amp;nbsp;&lt;/span&gt;nums1&lt;span&gt;&amp;nbsp;&lt;/span&gt;and&lt;span&gt;&amp;nbsp;&lt;/span&gt;nums2&lt;span&gt;&amp;nbsp;&lt;/span&gt;are&lt;span&gt;&amp;nbsp;&lt;/span&gt;m&lt;span&gt;&amp;nbsp;&lt;/span&gt;and&lt;span&gt;&amp;nbsp;&lt;/span&gt;n&lt;span&gt;&amp;nbsp;&lt;/span&gt;respectively. You may assume that&lt;span&gt;&amp;nbsp;&lt;/span&gt;nums1&lt;span&gt;&amp;nbsp;&lt;/span&gt;has a size equal to&lt;span&gt;&amp;nbsp;&lt;/span&gt;m + n&lt;span&gt;&amp;nbsp;&lt;/span&gt;such that it has enough space to hold additional elements from&lt;span&gt;&amp;nbsp;&lt;/span&gt;nums2.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Example 1:&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1620484777457&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Input: nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
Output: [1,2,2,3,5,6]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Example 2:&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1620484788604&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Input: nums1 = [1], m = 1, nums2 = [], n = 0
Output: [1]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Constraints:&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;nums1.length == m + n&lt;/li&gt;
&lt;li&gt;nums2.length == n&lt;/li&gt;
&lt;li&gt;0 &amp;lt;= m, n &amp;lt;= 200&lt;/li&gt;
&lt;li&gt;1 &amp;lt;= m + n &amp;lt;= 200&lt;/li&gt;
&lt;li&gt;-109&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;lt;= nums1[i], nums2[i] &amp;lt;= 109&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 문제가 대체 뭐라고ㅠㅠ 몇 시간을 머리를 쥐어뜯으며 풀었는데 풀고나니까 너무 뿌듯해서 블로그를 안 켤 수가 없었다.&lt;/p&gt;
&lt;p&gt;어려웠던 이유는 정렬을 &lt;b&gt;in-place&lt;/b&gt;로 해야했기 때문이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그러니까 새로운 배열을 만들어서도 안되고, nums1 배열의 size도 늘려서는 안되고,&lt;/p&gt;
&lt;p&gt;오로지(!) nums1 배열 안에서 알아서 잘 정렬을 해야한다는게 어려움의 포인트...&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1620485067036&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
 * @param {number[]} nums1
 * @param {number} m
 * @param {number[]} nums2
 * @param {number} n
 * @return {void} Do not return anything, modify nums1 in-place instead.
 */
var merge = function(nums1, m, nums2, n) {
    let nums2Position = 0;

    for (let i = m; i &amp;lt; nums1.length; i++) {
        nums1[i] = nums2[nums2Position++];
    }
    
    nums1.sort((l, r) =&amp;gt; l - r);
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;물론 일단은 그냥 아몰랑~ 하면서 쉽게 쉽게 가는 방식으로 풀어봤다.&lt;/p&gt;
&lt;p&gt;그냥 nums1 배열이랑 nums2 배열을 합친 다음에 sort 메소드로 sorting 시켜버린 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;인생이 이렇게 쉽게만 흘러가면 얼마나 좋을까... 싶지만&lt;/p&gt;
&lt;p&gt;문제에서 요구하는 효율적인 방식이 이게 아니란걸 알기 때문에 또 다시 고뇌의 시간 시작.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;노트에다가 계속 썼다 지웠다하면서 이 방법 저 방법 시도해보다가 알아낸 방법은 다음과 같다.&lt;/p&gt;
&lt;p&gt;그러니까 바로 숫자들을 뒤에서부터 비교하는 것이다!&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;755&quot; data-origin-height=&quot;818&quot; data-filename=&quot;blob&quot; width=&quot;453&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7VWaS/btq4p4ehn5d/DzZLtNOlCv1UsHedpkSvzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7VWaS/btq4p4ehn5d/DzZLtNOlCv1UsHedpkSvzk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7VWaS/btq4p4ehn5d/DzZLtNOlCv1UsHedpkSvzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7VWaS%2Fbtq4p4ehn5d%2FDzZLtNOlCv1UsHedpkSvzk%2Fimg.png&quot; data-origin-width=&quot;755&quot; data-origin-height=&quot;818&quot; data-filename=&quot;blob&quot; width=&quot;453&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;코드로 표현하면 다음과 같다!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1620485759764&quot; class=&quot;javascript&quot; style=&quot;margin: 20px auto 0px; display: block; overflow: auto; padding: 15px; color: #383a42; background: #f6f7f8; font-size: 14px; border-radius: 3px; font-family: Menlo, Consolas, Monaco, monospace; border: 1px solid #dddddd; cursor: default; z-index: 1; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var merge = function(nums1, m, nums2, n) {
    let nums1Position = m - 1;
    let nums2Position = n - 1;

    for (let i = nums1.length - 1; i &amp;gt;= 0; i--) {
        if (nums2Position &amp;lt; 0) break;
  
        if (nums1[nums1Position] &amp;gt; nums2[nums2Position]) {
           nums1[i] = nums1[nums1Position--];
        } else {
           nums1[i] = nums2[nums2Position--];
        }
    }
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Algorithm</category>
      <category>Algorithm</category>
      <category>JavaScript</category>
      <category>leetcode</category>
      <category>알고리즘</category>
      <category>자바스크립트</category>
      <category>코딩</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/218</guid>
      <comments>https://im-developer.tistory.com/218#entry218comment</comments>
      <pubDate>Sat, 8 May 2021 23:58:55 +0900</pubDate>
    </item>
    <item>
      <title>[LeetCode] Arrays101 - Squares of a Sorted Array</title>
      <link>https://im-developer.tistory.com/217</link>
      <description>&lt;p&gt;아주 오랜만에 다시 시작한 알고리즘 문제 풀기!&lt;/p&gt;
&lt;p&gt;(그동안 노션으로 휘리릭~ 간단하게 정리하는 것에 맛들려서 블로그에 손을 안댔는데 매우 반성하고 있다. 하하...)&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Squares&amp;nbsp;of&amp;nbsp;a&amp;nbsp;Sorted&amp;nbsp;Array&lt;/h2&gt;
&lt;p&gt;Given an integer array&lt;span&gt;&amp;nbsp;&lt;/span&gt;nums&lt;span&gt;&amp;nbsp;&lt;/span&gt;sorted in&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;non-decreasing&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;order, return&lt;span&gt;&amp;nbsp;&lt;/span&gt;an array of&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;the squares of each number&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;sorted in non-decreasing order.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Example 1:&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1620483434959&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Input: nums = [-4,-1,0,3,10]
Output: [0,1,9,16,100]
Explanation: After squaring, the array becomes [16,1,0,9,100].
After sorting, it becomes [0,1,9,16,100].&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;b&gt;Example 2:&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1620483446617&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Input: nums = [-7,-3,2,3,11]
Output: [4,9,9,49,121]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Constraints:&lt;/b&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;span&gt;1 &amp;lt;= nums.length &amp;lt;=&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;104&lt;/li&gt;
&lt;li&gt;-104&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;lt;= nums[i] &amp;lt;= 104&lt;/li&gt;
&lt;li&gt;nums&lt;span&gt;&amp;nbsp;&lt;/span&gt;is sorted in&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;non-decreasing&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;order.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;b&gt;Follow up:&lt;/b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Squaring each element and sorting the new array is very trivial, could you find an&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;O(n)&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;solution using a different approach?&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1620483605298&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/**
 * @param {number[]} nums
 * @return {number[]}
 */
var squareNum = function(num) {
    return Math.abs(num) ** 2;
};

var sortedSquares = function(nums) {
    return nums.map(squareNum).sort((l, r) =&amp;gt; l - r)
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;처음에는 일단 &lt;span style=&quot;color: #333333;&quot;&gt;(시간 복잡도가 O(n)이 되도록 하라는) &lt;/span&gt;문제의 요구사항을 무시하고 일단 되는대로 풀었다.&lt;/p&gt;
&lt;p&gt;물론 이렇게만 해도 통과된다ㅋㅋ&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그치만 문제의 요구사항은 그게 아니라는걸 아니까 진짜 머리가 터지도록 고민을 했는데&lt;/p&gt;
&lt;p&gt;아무리 생각해도 도저히 번뜩이는 아이디어가 떠오르지 않았다...&lt;/p&gt;
&lt;p&gt;(하 이럴때마다 스스로가 너무 바보같음 ㅠㅠ)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그래서 힌트라도 얻을겸 구글링을 해보니 정말 간단하면서도 싱크빅같은 방법이 있었다.&lt;/p&gt;
&lt;p&gt;(왜 생각해내지 못했을까 흑흑ㅜㅜ)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;숫자를 절대값으로 만들어서 생각해보면 양쪽 끝의 숫자들은 크기가 크고 안쪽으로 가면서 작아진다는 것에서 착안한 방법이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;601&quot; data-origin-height=&quot;625&quot; data-filename=&quot;blob&quot; width=&quot;387&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/o6JRK/btq4t359d9e/YUWSQWPPO1O2MOBfaFx9n1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/o6JRK/btq4t359d9e/YUWSQWPPO1O2MOBfaFx9n1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/o6JRK/btq4t359d9e/YUWSQWPPO1O2MOBfaFx9n1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fo6JRK%2Fbtq4t359d9e%2FYUWSQWPPO1O2MOBfaFx9n1%2Fimg.png&quot; data-origin-width=&quot;601&quot; data-origin-height=&quot;625&quot; data-filename=&quot;blob&quot; width=&quot;387&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;이렇게 양쪽 끝의 값들을 비교해서 크기가 큰 값으로 배열의 끝에서부터 채워나가면 된다.&lt;/p&gt;
&lt;p&gt;이걸 코드로 만들면 아래와 같다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1620484602761&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var squareNum = function(num) {
    return Math.abs(num) ** 2;
};

var sortedSquares = function(nums) {
    const result = [];
    let start = 0;
    let end = nums.length - 1;
    let position = nums.length - 1;
    
    while (position &amp;gt;= 0) {
        if (squareNum(nums[start]) &amp;lt; squareNum(nums[end])) {
            result[position--] = squareNum(nums[end--]);
        } else {
            result[position--] = squareNum(nums[start++]);
        } 
    }
    
    return result;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Reference&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://javascript.plainenglish.io/javascript-algorithm-how-to-square-a-sorted-array-f2410580aa09&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;javascript.plainenglish.io/javascript-algorithm-how-to-square-a-sorted-array-f2410580aa09&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1620484647518&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;JavaScript Algorithm: How to Square a Sorted Array&quot; data-og-description=&quot;Two Approaches&quot; data-og-host=&quot;javascript.plainenglish.io&quot; data-og-source-url=&quot;https://javascript.plainenglish.io/javascript-algorithm-how-to-square-a-sorted-array-f2410580aa09&quot; data-og-url=&quot;https://javascript.plainenglish.io/javascript-algorithm-how-to-square-a-sorted-array-f2410580aa09&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/WO0gg/hyJ7WFnZHO/rGnhkML8eCg8OyKZjNju1K/img.jpg?width=1200&amp;amp;height=803&amp;amp;face=0_0_1200_803,https://scrap.kakaocdn.net/dn/xyEQp/hyJ7SQwbLV/roreu8nHXGPgwSaVxVO1k1/img.jpg?width=60&amp;amp;height=40&amp;amp;face=0_0_60_40&quot;&gt;&lt;a href=&quot;https://javascript.plainenglish.io/javascript-algorithm-how-to-square-a-sorted-array-f2410580aa09&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://javascript.plainenglish.io/javascript-algorithm-how-to-square-a-sorted-array-f2410580aa09&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/WO0gg/hyJ7WFnZHO/rGnhkML8eCg8OyKZjNju1K/img.jpg?width=1200&amp;amp;height=803&amp;amp;face=0_0_1200_803,https://scrap.kakaocdn.net/dn/xyEQp/hyJ7SQwbLV/roreu8nHXGPgwSaVxVO1k1/img.jpg?width=60&amp;amp;height=40&amp;amp;face=0_0_60_40');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;JavaScript Algorithm: How to Square a Sorted Array&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Two Approaches&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;javascript.plainenglish.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Algorithm</category>
      <category>Algorithm</category>
      <category>JavaScript</category>
      <category>leetcode</category>
      <category>알고리즘</category>
      <category>자바스크립트</category>
      <category>코딩</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/217</guid>
      <comments>https://im-developer.tistory.com/217#entry217comment</comments>
      <pubDate>Sat, 8 May 2021 23:38:36 +0900</pubDate>
    </item>
    <item>
      <title>1년차 웹 프론트엔드 개발자의 2020년 회고</title>
      <link>https://im-developer.tistory.com/216</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;407&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BMf3V/btqRAkM6YI5/F3mBBZKP7eEDK4Dp21HjWk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BMf3V/btqRAkM6YI5/F3mBBZKP7eEDK4Dp21HjWk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BMf3V/btqRAkM6YI5/F3mBBZKP7eEDK4Dp21HjWk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBMf3V%2FbtqRAkM6YI5%2FF3mBBZKP7eEDK4Dp21HjWk%2Fimg.jpg&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;407&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;코로나때문에 크리스마스 연휴지만 어디 나가지도 못하고 집에 갇혀 지내는 요즘, 여기저기 돌아다니면서 연말 기분을 내는 대신 집에서 차분히 2020년을 되돌아보며 1년을 회고하자는 마음으로 노트북을 켰다. 올해는 정말 많은 일들이 일어난 해였다. 작년 여름에 개발자가 되겠다고 굳게 결심하고 공부를 시작한 것이 결실을 맺어 올해 1월 6일에 프론트엔드 개발자로 첫 출근을 하였다. 회사에서 정말 좋은 동료들을 만났고, 1년 간 많은 성장을 했다. 내가 1년간 한 것과 아쉬운 것들을 간단하게나마 정리해보려고 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;입사하고 최종 부트캠프에 합격하기까지 (1Q)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;회사에 잘 적응&lt;/li&gt;
&lt;li&gt;부트캠프 기간에 3가지 과제 해결 (레거시 API 교체 / 사내에서 사용하는 라이브러리 제작 및 개선)&lt;/li&gt;
&lt;li&gt;부트캠프 합격&lt;/li&gt;
&lt;li&gt;웹팀 코드 스타일 가이드 작성&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;나는 바닐라코딩이라는 개발 부트캠프를 수료하고 회사에 입사했다. 6개월 간 부트캠프에서 쉽 없이 달려왔기에 회사를 합격하고 나서는 모든 것을 보상받는 기분이었다. 그런데 이게 웬걸. 회사에 합격한 것이 끝이 아니었다. 회사에 입사하면 3개월 간의 수습 기간을 가지는데, 회사에서는 이 3개월을 부트캠프라는 단어로 불렀다. 부트캠프를 끝내고 신나서 입사를 했는데 다시 부트캠프라니...   심지어 부트캠프에서 불합격하면 입사가 취소된다고 했다. 3개월이라는 시간 동안 나라는 사람을 증명하지 못하면 합격이 취소될 지도 모른다는 불안감이 엄습했다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;입사 후 첫 회의를 들어갔는데 도대체 무슨 이야기가 오가는지 아무 것도 이해가 되지 않았다. 처음 듣는 용어들이 막 빠르게 오갔고 컨텍스트 파악은 하나도 되지 않았다. 그 와중에 부트캠프 과제를 받았는데 그 과제를 수행하려면 반드시 이해해야 하는 기존 레거시 코드는 정말이지 너무 복잡해서 구조 파악이 쉽지 않았다. 그래서 이 때의 나는 어떻게 했을까?&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;나는 일단 모든 것을 기록에 남기려고 노력했다. 내가 맡은 과제를 끝내기 위해 꼭 해야 하는 것들이 무엇인지 그 단계를 모두 Jira(업무 관리 및 협업을 위한 툴) 티켓으로 만들었다. 각각의 티켓에는 어떤 것들을 해야하는지와 질문 사항들을 빼곡히 정리했다. 덕분에 해야할 일들을 빼먹지 않고 차근히 해결할 수 있었고, 사수에게 내가 어떤 것들을 수행했고, 어떤 것들을 이해 못했는지 바로 커뮤니케이션할 수 있었다. 신입 때 가장 중요한 것은 내가 모르는 것을 빠르게 커뮤니케이션하는 것이다. 그냥 모른다고 혼자 끙끙 앓고 있으면 시간만 가고 해결되는 것은 아무 것도 없다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;첫 번째 부트캠프 과제를 완료하고 두 번째 과제를 받을 때쯤, 중간 피드백을 듣게 되었다. 거기서 다른 사람의 코드 리뷰에 조금 더 적극적으로 참여했으면 좋겠다는 피드백을 들었다. 우리 회사는 어떤 사람이 쓴 코드든 누구나 자유롭게 코드 리뷰를 할 수 있는 문화를 가지고 있다. 그리고 어떤 코드가 merge되려면 반드시 코드 리뷰를 받아야만 한다. 처음에는 정말 쉽지 않았다. 이제 막 입사한 내가 나보다 한참 전에 입사한 사람들의 코드를 리뷰할 자격이 되는가에 대한 두려움이 컸다. 그래서 첫 달에는 거의 하지 못했고 피드백에 언급되었다. 그래서 두 번째 달에는 가능하면 모든 코드 리뷰에 참여하려고 노력했다. 되도록이면 유의미한 리뷰를 남기기 위해 정말 한 줄 한 줄 열심히 파악했고 마지막 피드백 때 좋은 평가를 받았다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;처음 입사했을 때 웹팀에 코드 스타일 가이드가 따로 존재하지 않아 여러 repo마다 조금씩 다르게 적용되어 있어서 혼란스러웠다. eslint rule로 관리되고 있었지만 그 범위가 작았기 때문에 lint로는 처리할 수 없는 자잘한 예외들이 많았다. 그래서 마크다운 파일로 스타일 가이드 초안을 만들었고, 모든 팀원들의 의견을 취합하여 JavaScript 스타일 가이드 초안을 작성했다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4월 ~ 6월 (2Q)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;건강팀에 합류하여 서비스 개발&lt;/li&gt;
&lt;li&gt;건강 서비스 쪽 레거시 코드 청산&lt;/li&gt;
&lt;li&gt;Product Language 개발에 참여&lt;/li&gt;
&lt;li&gt;비개발자를 위한 테크 지식 발표&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;부트캠프에 합격하고 나는 Wellness 스쿼드라는 이름의 건강 서비스를 관리하는 팀으로 이동했다. 그 팀에서 처음으로 서비스 개발을 하게 되었는데 가장 어려웠던 점은 다른 직군의 사람들과 커뮤니케이션하는 부분이었다. 우리 회사에서 스쿼드는 개발자뿐만 아니라 PO, PM, 디자이너, BD, BO 등 다양한 직군이 모여서 하나의 서비스를 개발하는 팀을 말한다. 기존에 부트캠프를 진행할 때는 서비스와 조금 동떨어진 영역에서 개발자들과 소통했는데, 다른 여러 직군들과 소통을 처음 시작하면서 많은 어려움을 느꼈다. 내가 해야 하는 업무를 개발을 잘 모르는 다른 직군에게 잘 설명하고 왜 해야하는지 설득하는 일은 정말 중요했다. 그래서 최대한 문서를 작성하려고 노력했고 같은 팀에 계셨던 테크 리드분께 많이 배웠다. 그러다 같은 팀의 PO님이 비개발자들이 개발자들과 협업할 때 알아두면 좋은 용어들이나 기술들을 간단히 발표해보면 어떠냐는 제안을 주셨고, 어쩌다보니 전사 비개발자분들을 대상으로 발표를 하게 되었다. 지금 생각하면 참 부족함이 많은 발표였지만 정말 좋은 경험이 되어 주었다. 그리고 커뮤니케이션에 대한 자신감도 많이 높여 주었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;스쿼드에 합류하고 나서는 건강 서비스의 코드를 마이그레이션하면서 레거시 구조를 청산하는 업무를 진행했는데 Redux와 Redux saga를 도입하여 진행했다. 과도한 Dependency injection과 콜백 지옥으로 인해 비즈니스 로직 하나 찾으려면 이 컴포넌트 저 컴포넌트 헤매야하는 불상사를 해결하고 유지보수하기 쉬운 간결한 구조가 되었다. 그 외에도 회사 Product Language 개발 초기에 참여하여 서비스 개발을 위한 여러 스타일 컴포넌트를 제작하였다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;7월 ~ 9월 (3Q)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-ke-size=&quot;size16&quot;&gt;보험 서비스 레거시 코드 청산&lt;/li&gt;
&lt;li data-ke-size=&quot;size16&quot;&gt;서비스 개발하면서 다양한 실험 진행&lt;/li&gt;
&lt;li data-ke-size=&quot;size16&quot;&gt;테크올핸즈에서 실험 성과 발표&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;3분기가 되면서 우리 팀이 건강에서 보험 피쳐를 관리하는 팀으로 변경되었다. 그래서 보험 피쳐를 담당하여 역시 Redux를 도입하여 코드 구조를 개선했고 다양한 서비스 개발을 진행했다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;올 해 3분기는 우리 회사가 데이터 드리븐(Data driven) 회사로 성장하기 위한 초입이었다고 생각한다. 데이터팀에서 어떤 기능을 개발할 때 무조건 A/B 테스팅을 실시하고 그 결과에 따라 의사 결정을 하도록 실험이 장려되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이렇게만 적으면 대체 실험이란 것이 무엇인지 궁금할 수도 있어 좀 더 설명을 적어보자면... 우리는 서비스 개발을 할 때 공급자의 마인드로 기획을 하고, 그 기획서대로 개발을 하여 사용자들에게 제공한다. 그런데 우리가 기획한 서비스가 진짜로 고객이 좋아하는 서비스인지 아무도 알 수 없다. 완전히 공급자의 관점에서 설계된 기획이기 때문이다. 그래서 이 때 실험을 진행할 수 있는데, 유저를 50 : 50으로 나눠서 한 쪽에는 기존 기능을 그대로 제공하고, 나머지 한 쪽에는 새로운 기능을 제공하는 것이다. 이렇게 실험을 시작하고 어느 정도 데이터가 쌓이기 시작하면 대조군과 실험군 중에 고객이 정말 만족한 피쳐가 어떤 것인지 지표로 명확히 확인할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;실험은 더 나은 의사 결정을 객관적인 지표로 정할 수 있게 해주는 정말 좋은 솔루션이었다. 그런데 문제는&lt;/span&gt; 아직 실험을 하는 문화가 정착되지 않았었고, 실험을 어떤 식으로 개발에 적용할 수 있는지 예시가 거의 없었다. 이러한 상황 속에서 우리 팀은 최대한 빠르게 실험 문화를 팀 내에 정착하려고 노력하였다. 그리고 나도 그 변화에 발맞춰 적극적으로 실험 설계를 진행했고, 웹팀에서는 거의 최초로 큰 규모의 실험을 주도적으로 진행했다. 그리고 첫 실험의 결과는 정말 성공적이었다. 실험군의 지표가 어마어마하게 높았던 것이다.  &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실험은 잘 마무리되었고, 성과를 잘 정리해서 매 월 회사에서 진행하는 테크올핸즈에서 성과 발표를 진행했다. 3분기의 가장 큰 성과가 아닌가 싶다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;10월 ~ 12월 (4Q)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li data-ke-size=&quot;size16&quot;&gt;다양한 서비스를 개발하면서 integration 테스트 도입 (&lt;a href=&quot;https://testing-library.com/docs/react-testing-library/intro/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;React testing library&lt;/a&gt;, &lt;a href=&quot;https://mswjs.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;MSW&lt;/a&gt; 등 도입)&lt;/li&gt;
&lt;li data-ke-size=&quot;size16&quot;&gt;새로운 기능에 대한 통합 테스트 커버리지를 상당히 끌어올림&lt;/li&gt;
&lt;li data-ke-size=&quot;size16&quot;&gt;integration 테스트 가이드 문서를 작성하여 팀 내 공유&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;4분기에 내가 이룬 가장 큰 성과는 테스트 코드라고 할 수 있을 것 같다. 사실 내가 개발한 서비스에 unit 테스트 코드는 많이 작성했었는데, 그럼에도 불구하고 서비스를 배포할 때마다 자신감이 항상 낮았다. 사전에 로직에 오류를 발견하지 못하고 QA 때 발견되는 일도 종종 생겼다. 이 문제를 어떻게 개선할 수 있을 지 많은 고민이 있었는데 결론은 integration 테스트 코드의 도입이었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그 동안 진행했던 여러 util 함수들에 대한 unit 테스트, 그리고 saga 코드에 대한 형식적 테스트 코드로는 전체 서비스에 대한 안정성을 보장할 수 없다는 것이 나의 결론이었다. 세부 부품들이 잘 작동한다는 것을 검증했더라도, 그 부품들을 연결하는 방식 자체에 오류가 있다면 작성했던 테스트 코드들은 무용지물이 되기 마련이다. 결국 전체 서비스의 안정성을 위해서는 각 부품들이 잘 연결되었는지를 통합적으로 검증해야한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;사실 integration 테스트가 좋다는 것은 알지만 도입하지 못했던 이유는 1. 어떻게 해야할 지 모르겠고, 2. 시간이 많이 걸릴 것 같다는 두려움 때문이었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;i&gt;1. 어떻게 해야할 지 모르겠다.&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;이 부분은 일단 testing library라는 툴 문서를 읽어보면서 아주 아주 간단한 테스트를 하나라도 작성해보려고 했다. 아주 간단하게 루트 컴포넌트를 렌더링시키고 내가 원하는 컴포넌트가 존재하는지부터 테스트했다. 초기 환경 세팅을 하는 부분은 정말 고통스러웠는데, 하나씩 오류를 해결해나가면서 부딪힌 난관들은 기록을 해두었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;i&gt;2. 시간이 많이 걸릴 것 같다.&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;개발 일정을 산정할 때 무조건 테스트 코드를 작성할 수 있는 시간을 넉넉히 잡았고, 이 부분이 서비스 안정성을 위해 꼭 필요한 일이라고 팀원들을 설득했다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;여러 노력 끝에 4분기에 새로 만든 모든 서비스 코드에 integration 테스트 코드를 작성할 수 있었다. 테스트 코드를 작성하면서 가장 좋았던 것은 테스트 코드로 오히려 전체 개발 시간이 단축되었다는 점이다. 테스트 코드를 계속 적성하다보니 어느 정도 반복되는 패턴들을 익혀 작성 시간이 점점 줄어들었다. 그리고 무엇보다 QA 때 에러가 발생하는 일이 0건으로 줄었다! 그래서 배포 직전에 급하에 코드를 수정하는 일이 없어졌다.&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;1년 동안 성장한 것들&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;커뮤니케이션 스킬을 많이 배웠다. &lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;나는 항상 말보다는 글쓰는 것을 선호하고 조리있게 내 의견을 말하는 것에 자신감이 낮았는데 1년 동안 많은 개선이 있었다고 생각한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt; 문서 커뮤니케이션을 더 잘하려고 노력했고, 4분기에 많이 개선되었다고 생각한다. 개발하기 전에 무조건 테크 스펙 문서를 작성했고, 개발 일정을 아주 작은 task 단위로 잘게 쪼개서 최대한 정확히 산정하려고 노력했다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;의사 결정을 보다 분명히 할 수 있게 되었다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;입사 초반에는 의사 결정을 잘 하지 못해서 정말 힘들었던 기억이 난다. 그래도 1년이 지난 지금의 나는 입사 초기에 비해서는 훨씬 더 명확히 의사 결정을 할 수 있는 사람이 된 것 같다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;기술적인 성장&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;사실 내 코딩 실력이 얼마나 더 나아졌는지에 대해서는 객관적으로 판단 못하겠다. 그래도 1년 동안 꾸준히 바닐라코딩 같은 기수 멤버들과 스터디를 진행했다. JavaScript 공부를 꾸준히 했고, 서로 공부한 내용을 공유하는 시간들을 가졌다. 여기 저기서 얻는 새로운 개발 트렌드를 놓치지 않으려고 블로그 글도 읽고 유튜브 강의들도 보았다.&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;테스트 코드에 대한 많은 공부를 했고 개선을 이루었으며 다음 분기에도 꾸준히 더 나은 테스트 코드를 작성하기 위해 노력할 것이다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;바닐라코딩 프렙 과정의 과제 코드 리뷰어로 활동했다. 내가 수료했던 부트캠프의 과제들을 다시 보니 반갑기도 하고, 과제하시는 분들 코드를 보며 많은 인사이트를 얻었다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;아쉬운 점들&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;도메인에 대한 관심을 많이 가지지 못한 점
&lt;ul style=&quot;list-style-type: disc;&quot;&gt;
&lt;li&gt;우리 회사는 개발자이기 때문에 개발만 해도 되는 회사는 아니다. 개발자이지만 기획에도 자유롭게 참여할 수 있고, 피드백을 주어야하는데 도메인 자체에 대한 관심을 많이 쏟지는 못했던 것이 아쉽다. 물론 개발할 때는 일정에 치여서 개발하느라 정신 없기도 했지만 그럼에도 불구하고 도메인 자체에 공부를 좀 해야겠다고 느꼈다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;프론트 개발 외의 다양한 공부를 못한 점
&lt;ul style=&quot;list-style-type: disc;&quot;&gt;
&lt;li&gt;네트워크 통신이나 서버, 도커 등등 이런 분야의 공부를 거의 하지 못한 점이 너무 아쉽다. 내년에는 이 쪽 분야로도 공부를 꼭 해야겠다는 다짐을 해본다...&lt;/li&gt;
&lt;li&gt;블로그 글을 자주 쓰지 못해서 아쉽다. 사실 바쁘다는 것은 핑계인 것 같고, 얼마든지 쓸 수 있는 시간이 많았는데 잘 안쓰게 되었던 것... 반성해본다ㅠㅠ&lt;/li&gt;
&lt;li&gt;주제가 어떤 것이든 사이드 프로젝트를 진행해보지 못한 점. 내년에는 서버쪽 공부도 좀 더 해서 작은 미니 프로젝트를 하나라도 해봐야겠다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;올 해 코로나도 터지고 정말 다사다난했던 2020년이었다.&lt;/p&gt;
&lt;p&gt;개발자로서의 첫 단추를 잘 끼운 것 같아 뿌듯하고 내년에 더 많이 성장할 수 있길 바란다.&lt;/p&gt;
&lt;p&gt;끝!&lt;/p&gt;</description>
      <category>Etc</category>
      <category>회고</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/216</guid>
      <comments>https://im-developer.tistory.com/216#entry216comment</comments>
      <pubDate>Thu, 31 Dec 2020 18:39:08 +0900</pubDate>
    </item>
    <item>
      <title>새로 알게 된 react-router 관련 토막 상식 (Query parameter가 바뀔 때마다 re-render 시키는 법)</title>
      <link>https://im-developer.tistory.com/215</link>
      <description>&lt;pre id=&quot;code_1607857226021&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { ExampleComponent } from './example';

// ...
&amp;lt;Route path='/example' component={ ExampleComponent } /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;회사에서 react-router를 쓰면서 주로 위와 같은 방식으로 routing을 하다가&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;router v5 버전부터 아래와 같이 Route 컴포넌트 안에 child 컴포넌트를 합성하는 방식이 더 권장되는 것을 발견하고 바꾸었다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1607857371911&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { ExampleComponent } from './example';

// ...
&amp;lt;Route path='/example'&amp;gt;
  &amp;lt;ExampleComponent /&amp;gt;
&amp;lt;/Route&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;근데 이걸로 바꾸는 순간 한 가지 이슈가 발생했는데...  &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;바로 이 이슈였다. 탭을 이동할 때마다 주소 뒤에 query parameter가 바뀌고,&lt;/p&gt;
&lt;p&gt;이 query parameter가 바뀔 때마다 UI가 바뀌는 화면이 있었는데,&lt;/p&gt;
&lt;p&gt;Routing 방식을 바꾸니까 갑자기 탭을 아무리 클릭해도 화면이 안 바뀌는 것이었다(!)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그래서 살펴보니, query parameter가 바뀌어도 컴포넌트가 re-render가 안되는 것이 문제였다.&lt;/p&gt;
&lt;p&gt;그러니까 URL path 자체는 동일하고 뒤에 query parameter만 바뀌었기 때문인 것으로 추정된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1607857703666&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Route path='/example'&amp;gt;
  { () =&amp;gt; &amp;lt;ExampleComponent /&amp;gt; }
&amp;lt;/Route&amp;gt;

&amp;lt;Route
  path='/example'
  component={ ExampleComponent }
/&amp;gt;

&amp;lt;Route
  path='/example'
  render={ () =&amp;gt; &amp;lt;ExampleComponent /&amp;gt; }
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;즉, query paramter value가 바뀌어서 location 객체가 업데이트될 때마다 re-render를 하고 싶으면 위와 같이 사용을 해주어야만 했다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Component&lt;/h4&gt;
&lt;pre id=&quot;code_1607857993332&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Route
  path='/example'
  component={ ExampleComponent }
/&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;우리가 Route의 component 속성을 사용하면, router는 React.createElement 메소드를 통해서 새로운 리액트 엘리먼트를 생성한다. 이 말은 만약에 component에 inline function을 넣어버리면 render를 할 때마다 새 컴포넌트를 매번 만들어낸다. 따라서 이미 존재하는 컴포넌트를 업데이트하는 것이 아니라 &lt;span style=&quot;color: #333333;&quot;&gt;매 번 새 컴포넌트 mount, unmount를 반복하는 것이다. 그래서 만약에 inline function을 사용하고 싶으면 render 속성을 사용하는 것이 좋다.&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Render&lt;/h4&gt;
&lt;pre id=&quot;code_1607858285709&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Route
  path='/example'
  render={ (routerProps) =&amp;gt; &amp;lt;ExampleComponent { ...routerProps } /&amp;gt; }
/&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;render 속성을 사용하면 위에서 언급한 문제없이 inline function을 사용할 수 있게 된다. 그리고 편하게 routerProps를 새 컴포넌트에 주입해줄 수도 있다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UUxKi/btqPYSTjhlk/xoomrGzQlnSzYCnwiRlmF0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UUxKi/btqPYSTjhlk/xoomrGzQlnSzYCnwiRlmF0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UUxKi/btqPYSTjhlk/xoomrGzQlnSzYCnwiRlmF0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUUxKi%2FbtqPYSTjhlk%2FxoomrGzQlnSzYCnwiRlmF0%2Fimg.jpg&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그렇다면, 위와 같이 component, render 속성을 이용하지 않고,&lt;/p&gt;
&lt;p&gt;Route 컴포넌트 안에 children으로 컴포넌트를 사용하면서도&lt;/p&gt;
&lt;p&gt;query parameter가 바뀌었을때 re-render가 일어나도록 하는 방법은 없는 것일까?&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;i&gt;댓츠노노! 그렇지 않다.&lt;/i&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;react-router 라이브러리에서 제공하는 useLocation hook을 사용하면 가능하다.&lt;/p&gt;
&lt;p&gt;그러니까 query parameter를 window.location 객체에서 가져다쓰는 것이 아니라,&lt;/p&gt;
&lt;p&gt;useLocation() hook을 통해 가져와서 쓰면, query parameter가 바뀔 때마다 location hook을 사용하는 컴포넌트가 re-render된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1607863893292&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useLocation } from 'react-router-dom';
import { parse } from 'query-string';

function ExampleComponent () {
  const { exampleQuery } = parse(useLocation().search);
  
  //...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 방법이 사실 더 좋은 이유는 component나 render 속성을 쓰면,&lt;/p&gt;
&lt;p&gt;query parameter가 바뀔때마다 지정해준 컴포넌트의 하위 컴포넌트들이 모두 re-render가 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그러다 그냥 &amp;lt;Route&amp;gt;&amp;lt;ExampleComponent /&amp;gt;&amp;lt;/Route&amp;gt; 요렇게 사용하고&lt;/p&gt;
&lt;p&gt;필요한 곳에서만 useLocation hook을 사용하면, 이 hook을 사용하는 곳에서만 re-render가 일어난다!&lt;/p&gt;</description>
      <category>Frontend</category>
      <category>react</category>
      <category>Router</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/215</guid>
      <comments>https://im-developer.tistory.com/215#entry215comment</comments>
      <pubDate>Sun, 13 Dec 2020 21:54:38 +0900</pubDate>
    </item>
    <item>
      <title>페이스북에서 만든 React 상태 관리 라이브러리, Recoil.js</title>
      <link>https://im-developer.tistory.com/214</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;329&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dDRz8P/btqPYkbf219/DdYs2dbRWDQ5rQIrUVKBYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dDRz8P/btqPYkbf219/DdYs2dbRWDQ5rQIrUVKBYk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dDRz8P/btqPYkbf219/DdYs2dbRWDQ5rQIrUVKBYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdDRz8P%2FbtqPYkbf219%2FDdYs2dbRWDQ5rQIrUVKBYk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;329&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;리액트 상태 관리&lt;/h3&gt;
&lt;p&gt;리액트로 개발을 할 때 리액트에서 기본으로 제공하는 상태 관리 메소드들을 사용하는 것이 가장 간단하고 편리하겠지만, 약간의 한계는 분명히 있다. 리액트 컴포넌트의 상태는&amp;nbsp;상위에서 하위로 단방향으로 흘러갈 수밖에 없는데, 이 과정에서 거대한 트리 구조가 만들어지고, 무수히 많은 re-render가 일어난다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;자식 컴포넌트에서 부모 컴포넌트의 상태를 제어하기 위해서는 부모 컴포넌트가 본인이 가지고 있는 상태를 제어할 수 있는 메소드들을 자식 컴포넌트에 전달을 해주어야만 가능하다. 그러니까 자식 컴포넌트에서 상위에서 전달된 메소드들로 무언가 상태를 변경하면 부모 컴포넌트들부터 하위에 연결된 컴포넌트들 전부가 re-render 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;리액트에 Context API라는 것이 생기면서 리액트에서도 상위의 상태값을 중간에 여러 컴포넌트들을 거치지 않고 다이렉트로 접근해서 조작할 수 있게 되었는데, 이 것 또한 약간의 한계는 있다. Context는 오로지 하나의 단일 value만 저장할 수 있고, 특정 상태 구독자만을 위해 저장하는 것도 어렵다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이러한 점들 때문에 리액트에서는 상태값을 가지는 주체인 트리의 중심 코드와 해당 상태들을 끌어다 쓰는 잎사귀 코드를 분리하는 것이 매우 어려웠다. 이러한 점들 때문에 리액트에서 조금 더 쉽게 상태를 관리할 수 있도록 여러 외부 라이브러리들이 등장하기 시작했는데, 그 중에서 가장 핫했던 라이브러리는 단연코 Redux일 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Redux의 등장&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7OQnB/btqPWZSN09e/vmAKBu5TlO7YCV7sLlruk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7OQnB/btqPWZSN09e/vmAKBu5TlO7YCV7sLlruk0/img.png&quot; data-alt=&quot;Flux design pattern&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7OQnB/btqPWZSN09e/vmAKBu5TlO7YCV7sLlruk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7OQnB%2FbtqPWZSN09e%2FvmAKBu5TlO7YCV7sLlruk0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Flux design pattern&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Redux는 약간 변형되긴 했지만 Flux 디자인 패턴을 바탕으로 만들어졌는데, 사용자의 액션이 일어나면 그 이벤트가 dispatch되고, Reducer에게 전달된다. 그리고 이 Reducer에서 immutable한 새로운 state 객체를 만들어서 store에 update하면 view에 반영이 되는 식이다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Redux를 사용하면서 가장 편리했던 것은, 액션이 뷰에 반영되기 까지의 그 흐름이 너무나 일관되고, 명확해서 디버깅이 쉽다는 점이었다. 그리고 Redux와 같이 사용하면 편리한 여러 비동기 제어 미들웨어들이 많다. 회사에서도 지금 Redux와 함께 Redux Saga로 비즈니스 로직을 관리하고 있는데 일단 한 번 이 패턴에 익숙해지면 비즈니스 로직을 구현하는 것이 매우 쉬워진다. Generator 함수를 통해 비동기 로직을 마치 동기 로직과 같이 보이도록 코드를 짤 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그러나 불편한 점도 많은데, 예를 들면 Redux를 사용하면 이 라이브러리의 패턴에 맞게 무수히 많은 행사 코드가 필요한다는 점이다. 액션 객체를 생성하는 함수, 액션 객체를 받아서 각 상황에 맞는 새로운 상태 객체를 만들어주는 reducer, 스토어에 저장된 상태를 가져다 쓰기 위한 selector 등등, 정말 많은 코드들이 필요하다. 초기 보일러 플레이트를 위해 액션, 리듀서 등등을 잔뜩 생성해주고, Store를 만들어서 루트에 연결을 해주어야 하는데, 정말 번거롭기 그지없다. 이런 문제를 해결하기 위해 조금이라도 편하게 생성할 수 있도록 도와주는 redux-toolkit이라는 라이브러리가 나왔지만 그럼에도 불구하고 Redux 라이브러리를 처음에 세팅하는 것이 정말 불편하다는 것은 써본 사람들이라면 다들 공감할 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Recoil.js의 탄생&lt;/h3&gt;
&lt;p&gt;그래서 페이스북에서 새로운 라이브러리를 만들었는데, 바로 Recoil.js!&lt;/p&gt;
&lt;p&gt;Recoil 공식 홈페이지에서 이 라이브러리가 어떤 목적으로 만들어졌는지, 컨셉은 무엇인지 등을 읽어봤는데 간단하게 요약해보면 이렇다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Recoil은 보일러 플레이트 필요 없이 간단하게 상태 관리를 도와주는 라이브러리이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Recoil은 atom(공유할 state)에서부터 selector(순수 함수)를 통해 리액트 컴포넌트까지 아래로 흐르는 데이터 플로우 그래프를 만들 수 있도록 해준다. 여기서 &lt;b&gt;Atom&lt;/b&gt;은 컴포넌트들이 구독할 수 있는 상태의 단위를 말하고, &lt;b&gt;Selector&lt;/b&gt;는 그 상태를 동기적으로나 비동기적으로 변경할 수 있는 순수 함수들을 말한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;나는 Recoil을 써보려고 심플한 To do App을 만들어봤는데, 써보면서 느낀 점은 Recoil 라이브러리는 정말 쉽게 시작할 수 있다는 점이다. 그리고 hook 형태로 되어 있어서 이해가 쉽다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;969&quot; data-origin-height=&quot;659&quot; width=&quot;577&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGjsmz/btqPZJaE7xu/XpYsRALooSKJMeLAzhjJF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGjsmz/btqPZJaE7xu/XpYsRALooSKJMeLAzhjJF0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGjsmz/btqPZJaE7xu/XpYsRALooSKJMeLAzhjJF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGjsmz%2FbtqPZJaE7xu%2FXpYsRALooSKJMeLAzhjJF0%2Fimg.png&quot; data-origin-width=&quot;969&quot; data-origin-height=&quot;659&quot; width=&quot;577&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;예전에 Redux 라이브러리는 초기에 보일러 코드를 써야하는 것도 너무 많고, 그 흐름을 이해하는 데 오랜 시간이 걸렸는데, Recoil은 Redux에 비해서 정말 단순하고 쉽다. 근본적으로 Atom이라는 것과 Selector, 이 2가지만 이해해도 일단 단순한 것들은 금방 만들 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Atom&lt;/h4&gt;
&lt;p&gt;Atom은 상태의 단위를 말한다. Atom 하나가 상태 하나이다. 물론 새로운 값으로 업데이트가 가능하고 구독도 할 수 있다. 만약에 &lt;span style=&quot;color: #333333;&quot;&gt;atom&lt;/span&gt;이 업데이트되면, 이 &lt;span style=&quot;color: #333333;&quot;&gt;atom&lt;/span&gt;을 구독하고 있는 모든 컴포넌트들이 새로운 값과 함께 re-render가 된다. Atom은 런타임 환경에서도 생성될 수 있다. Atom을 리액트의 로컬 컴포넌트 상태값을 대체해서 사용할 수도 있다. 만약에 같은 atom을 여러 컴포넌트에서 사용한다면, 해당 컴포넌트들은 상태를 공유하게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1607854114040&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const numberState = atom({
  key: 'numberState',
  default: 0,
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Atom은 atom 함수를 통해 생성하는데, 위와 같이 Key와 default value를 지정하면 된다. 물론 이 key는 unique해야 한다. 이 key는 상태를 지속하는데도 사용되고, 디버깅에서도 사용되고 그리고 다른 고급 API들을 사용할 때도 사용된다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1607854354165&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function Example() {
  const [number, setNumber] = useRecoilState(numberState);
  return (
    &amp;lt;button onClick={() =&amp;gt; setNumber((prevNum) =&amp;gt; prevNum + 1)}&amp;gt;
      { number }
    &amp;lt;/button&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;하나의 상태를 업데이트하는 여러 hook이 존재하는데, 그 중에서 가장 기본이 되는 hook은 useRecoilState다. 그냥 리액트에서 기본으로 제공하는 useState와 동일한데, 다른 점은 이 상태값이 다른 컴포넌트들에게 공유가 된다는 점이다.&amp;nbsp;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1607854767856&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function Example2() {
  const number = useRecoilValue(numberState);
  return &amp;lt;p&amp;gt;{ number }&amp;lt;/p&amp;gt;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Example 컴포넌트 내에서 사용된 이 number 값은 다른 컴포넌트 내에서도 얼마든지 가져다 쓸 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Selector&lt;/h4&gt;
&lt;p&gt;Selector는 순수 함수로 atom이나 다른 selector를 받을 수 있다. 만약에 어떤 selector, A가 구독하는 atom이나 selector들이 업데이트가 된다면, 이 A selector 역시 다시 계산이 된다. 컴포넌트는 atom을 직접 구독할 수도 있지만 selector를 구독할 수도 있다. 그렇기 때문에 만약에 어떤 selector가 다시 계산되면, 그 selector를 구독하는 컴포넌트 역시 re-render된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Selector는 state로 저장된 값을 베이스로 해서 값을 계산하는 순수 함수이다. 이러한 역할을 함으로써 수많은 불필요한 상태 값들이 생겨나는 것을 막고 진짜 꼭 필요한 상태만을 가질 수 있도록 한다. 예를 들자면 이렇다. 만약에 To do list를 만든다고 했을때, 유저가 입력하는 할 일 들이 배열의 형태로 저장되는 상태값은 꼭 필요한 값일 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1607855294613&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const todoListState = atom({
  key: 'todoListState',
  default: [],
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그런데 만약에 그 할 일 리스트의 총 개수와 유저가 완료한 일의 개수를 화면에 표시한다고 해보자. 이 때, todoList 배열의 총 길이와 완료된 일만 모은 List 배열의 길이를 각각 따로 state로 저장할 필요는 없을 것이다. 바로 이 때 selector를 유용하게 사용할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1607855376356&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const todoListStateSelector = selector({
  key: 'todoListStateSelector',
  get: ({get}) =&amp;gt; {
    const todoList = get(todoListState);

    return {
      totalCount: todoList.length,
      completeCount: todoList.filter(item =&amp;gt; item.is_complete).length,
    };
  },
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;즉, 이렇게 selector 함수를 사용해서, todoListState 값을 가져온 후, 해당 list 데이터를 기반으로 원하는 전체 개수와 완료된 일의 개수를 계산해서 컴포넌트에 전달하면 된다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위 selector는 계산하는데 필요한 todoListState를 구독하고 있기 때문에, todoListState가 업데이트될 때마다 다시 계산된다. 그렇기 때문에 컴포넌트의 입장에서는 selector와 atom은 같은 기능을 하는 인터페이스라고 할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1607856201862&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function TodoList() {
  const todoList = useRecoilValue(todoListStateSelector);
  return (
    &amp;lt;ul&amp;gt;
      { todoList.map(todo =&amp;gt; &amp;lt;li key={ todo.id }&amp;gt;{ todo.text }&amp;lt;/li&amp;gt;) }
    &amp;lt;/ul&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;컴포넌트 내에서 Selector를 구독하려면 useRecoilValue와 같은 hook을 사용할 수 있다. useRecoilState는 사용할 수 없는데, 그 이유는 selector는 writable하지 않기 때문이다. 즉, 오로지 값을 읽을 수만 있지 값을 업데이트할 수는 없다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;여기까지 Recoil의 아주 베이직한 컨셉을 살짝 봤는데, 간단하게 요약해보자면 이렇다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Recoil에서 하나의 상태는 하나의 atom 단위로 관리된다. atom으로 만들어진 상태는 여러 컴포넌트들끼리 공유할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;useRecoilState라는 hook은 리액트의 useState와 거의 똑같이 동작하고, atom 값을 구독할 수도 있고, 값을 업데이트할 수도 있다.&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;값을 단순 구독만 하기 위해서는 useRecoilValue라는 hook을 사용한다.&lt;/li&gt;
&lt;li&gt;Selector는 atom으로 저장된 상태 값을 내가 원하는 대로 가공해서 사용하기 위해 사용하는 순수 함수이다.&lt;/li&gt;
&lt;li&gt;컴포넌트에서 Selector를 atom과 똑같은 방식으로 구독할 수 있지만, selector로 값을 update할 수는 없다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;사실, 이 2개만 알아도 엄청 간단한 앱은 만들 수 있다.&lt;/p&gt;
&lt;p&gt;다만 이제 비동기 로직을 처리하거나 복잡한 일들을 하려면 더 깊이 공부해야하는데 그건 &lt;span style=&quot;color: #333333;&quot;&gt;기회되면&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;다음 포스팅에 정리해보려고 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Reference&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://recoiljs.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;recoiljs.org/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1607856752603&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Recoil&quot; data-og-description=&quot;A state management library for React.&quot; data-og-host=&quot;recoiljs.org&quot; data-og-source-url=&quot;https://recoiljs.org/&quot; data-og-url=&quot;https://recoiljs.org/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bbansk/hyIxuYJ680/rVHpmDqmOkrwGL3TwZZOnK/img.png?width=1420&amp;amp;height=646&amp;amp;face=0_0_1420_646&quot;&gt;&lt;a href=&quot;https://recoiljs.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://recoiljs.org/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bbansk/hyIxuYJ680/rVHpmDqmOkrwGL3TwZZOnK/img.png?width=1420&amp;amp;height=646&amp;amp;face=0_0_1420_646');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Recoil&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;A state management library for React.&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;recoiljs.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/jy7123943/practice-recoil&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;github.com/jy7123943/practice-recoil (연습해보려고 만든 간단한 todo app repo입니다.)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1607856768662&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;object&quot; data-og-title=&quot;jy7123943/practice-recoil&quot; data-og-description=&quot;Contribute to jy7123943/practice-recoil development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/jy7123943/practice-recoil&quot; data-og-url=&quot;https://github.com/jy7123943/practice-recoil&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/hMIoX/hyIyWe0zEV/prvq7KyATZAbsPw4qg86kK/img.jpg?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400&quot;&gt;&lt;a href=&quot;https://github.com/jy7123943/practice-recoil&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/jy7123943/practice-recoil&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/hMIoX/hyIyWe0zEV/prvq7KyATZAbsPw4qg86kK/img.jpg?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;jy7123943/practice-recoil&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Contribute to jy7123943/practice-recoil development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend</category>
      <category>react</category>
      <category>recoil</category>
      <category>상태관리</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/214</guid>
      <comments>https://im-developer.tistory.com/214#entry214comment</comments>
      <pubDate>Sun, 13 Dec 2020 19:48:22 +0900</pubDate>
    </item>
    <item>
      <title>JavaScript, 숫자 타입이 아닌 값을 숫자로 바꾸는 다양한 방법 - feat. Number()/parseInt/Unary plus(+)/Unary negation(-)</title>
      <link>https://im-developer.tistory.com/213</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;258&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ba7xwx/btqMNwsvEkp/K1v0sKtIBGFsiNQmZLhEG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ba7xwx/btqMNwsvEkp/K1v0sKtIBGFsiNQmZLhEG1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ba7xwx/btqMNwsvEkp/K1v0sKtIBGFsiNQmZLhEG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fba7xwx%2FbtqMNwsvEkp%2FK1v0sKtIBGFsiNQmZLhEG1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;258&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;JavaScript에서 숫자 타입이 아닌 값을 숫자로 바꿀 때는&lt;/p&gt;
&lt;p&gt;주로 &lt;b&gt;Number&lt;/b&gt;나 &lt;b&gt;parseInt&lt;/b&gt;를 사용한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Number&lt;/h3&gt;
&lt;p&gt;When used as a function, Number(value) converts a string or other value to the Number type. If the value can't be converted, it returns NaN.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Number는 함수로 사용 시 string이나 다른 값들을 Number type으로 변환해준다.&lt;/p&gt;
&lt;p&gt;만약에 값을 변환할 수 없을 때는 NaN을 리턴한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;parseInt&lt;/h3&gt;
&lt;p&gt;The&amp;nbsp;parseInt()&amp;nbsp;function&amp;nbsp;parses&amp;nbsp;a&amp;nbsp;string&amp;nbsp;argument&amp;nbsp;and&amp;nbsp;returns&amp;nbsp;an&amp;nbsp;integer&amp;nbsp;of&amp;nbsp;the&amp;nbsp;specified&amp;nbsp;radix&amp;nbsp;(the&amp;nbsp;base&amp;nbsp;in&amp;nbsp;mathematical&amp;nbsp;numeral&amp;nbsp;systems).&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;parseInt는 string 타입의 인자를 파싱하여 특정 기수의 integer를 리턴한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;pre id=&quot;code_1604826953416&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// parsing:
parseInt(&quot;20px&quot;);       // 20
parseInt(&quot;hello123&quot;);   // NaN
parseInt(&quot;10100&quot;, 2);   // 20
parseInt(&quot;2e1&quot;);        // 2

// type conversion
Number(&quot;20px&quot;);       // NaN
Number(&quot;2e1&quot;);        // 20, exponential notation&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;예를 들어서 &quot;20px&quot;이라는 값을 parseInt에 넣으면&lt;/p&gt;
&lt;p&gt;마지막 숫자가 아닌 글자들을 그냥 무시하고 숫자로 바꿀 수 있는 20이 number로 리턴된다.&lt;/p&gt;
&lt;p&gt;(그렇지만 숫자가 아닌 글자로 시작하는 값은 숫자로 끝나더라도 숫자로 변환되지 않고 NaN이 리턴된다.)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그러나 Number에 &quot;20px&quot;을 넣으면 그냥 NaN이 리턴된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1604827227525&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Number(&quot;010&quot;);         // 10
parseInt(&quot;010&quot;);       // 8, implicit octal
parseInt(&quot;010&quot;, 10);   // 10, decimal radix used

Number(&quot;0xF&quot;);   // 15
parseInt(&quot;0xF&quot;); //15&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Number는 8진수는 인지하지 못하지만 16진수는 인지한다.&lt;/p&gt;
&lt;p&gt;parseInt는 8진수, 16진수 모두 인지한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;사실 JavaScript에는 숫자가 아닌 값을 숫자로 바꿀 때&lt;/p&gt;
&lt;p&gt;위 함수 외에도 Unary operator를 사용할 수 있는데 Unary plus(+), unary negation(-) 두 가지가 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Unary plus(+)&lt;/h3&gt;
&lt;pre id=&quot;code_1604827762892&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;+x&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;unary plus는 + 기호를 사용하며 피연산자의 앞에 위치한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;피연산자를 평가하여 숫자가 아닌 경우에 숫자로 변환하는 역할을 한다.&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;물론 unary negation(-) 또한 숫자가 아닌 값을 숫자로 변환해주지만,&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;unary plus(+)가 가장 빠르고 쉽게 숫자로 바꾸는 방법이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이유는 unary plus는 다른 어떠한 연산 없이 그냥 숫자로만 바꿔주기 때문이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;(unary negation은 숫자를 음수로 바꿔버린다.)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;unary plus는 문자열을 숫자를 담고 있는 문자열 뿐만 아니고, true, false, null과 같은 값들도 숫자로 바꿔주며,&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;10진수와 16진수 포맷이 지원된다. 음수도 지원되지만 10진수로만 가능하다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;BigInt 값에 사용하게 되면 TypeError를 일으키니 알아두자.&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;만약에 파싱할 수 없는 값이 있다면 NaN이라고 평가한다.&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1604828176497&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const x = 1;
const y = -1;

console.log(+x); // 1
console.log(+y); // -1&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그냥 숫자에 사용할 경우 아무런 연산을 하지 않는다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1604828326400&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;+&quot;123&quot; // 123
+&quot;010&quot; // 10
+&quot;12px&quot; // NaN
+&quot;hello123&quot; // NaN&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위와 같이 숫자로 변환해주고, 12px과 같은 값을 넣은 경우 NaN이 된다.&lt;/p&gt;
&lt;p&gt;(12px을 숫자 12로 변환하고 싶을 때는 parseInt를 사용하자!)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1604828212573&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;+true  // 1
+false // 0
+null  // 0
+&quot;&quot;  // 0
+function(val){ return val } // NaN
+1n    // throws TypeError: Cannot convert BigInt value to number&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이렇게 숫자랑 상관없는 다양한 값들도 숫자로 변환해준다.&lt;/p&gt;
&lt;p&gt;true는 1, 그 외에 falsy 값들은 0으로 변환해주고,&lt;/p&gt;
&lt;p&gt;변환할 수 없는 값들은 NaN이 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Unary&amp;nbsp;negation&amp;nbsp;(-)&lt;/h3&gt;
&lt;pre id=&quot;code_1604828606109&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-x&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;unary plus와 동일한 역할을 하며, 다만 얘는 이름에서 유추할 수 있듯이 피연산자를 음수로 만든다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1604828680024&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const x = 3;
const y = -x; // -3&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;숫자가 아닌 값을 변환할 때는 unary plus와 동일하게 작동된다.&lt;/p&gt;
&lt;p&gt;(다만 조금 다른 점은 BigInt에 사용해도 Type Error가 나지 않는다.)&lt;/p&gt;
&lt;pre id=&quot;code_1604828891851&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-&quot;123&quot; // -123
-&quot;010&quot; // -10
-&quot;12px&quot; // NaN
-&quot;hello123&quot; // NaN

-true  // -1
-false // -0
-null  // -0
-&quot;&quot;  // -0
-function(val){ return val } // NaN
-1n    // -1n&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend</category>
      <category>JavaScript</category>
      <category>자바스크립트</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/213</guid>
      <comments>https://im-developer.tistory.com/213#entry213comment</comments>
      <pubDate>Sun, 8 Nov 2020 18:51:06 +0900</pubDate>
    </item>
    <item>
      <title>[Kent C. dodds - Epic React] Advanced React Hooks(1) - useReducer 이해하기</title>
      <link>https://im-developer.tistory.com/212</link>
      <description>&lt;h3 id=&quot;usereducer&quot;&gt;useReducer&lt;/h3&gt;
&lt;pre id=&quot;code_1604815879032&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const [state, dispatch] = useReducer(reducer, initialArg, init);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;대부분 useState hook으로 리액트의 상태 관리를 하게 되는데, 가끔 상태 로직을 상태 변화가 일어나는 컴포넌트로부터 분리시키고 싶을 때 useReducer를 유용하게 사용할 수 있다. useReducer는 주로 복잡한 상태 로직을 가졌거나 다음 state가 기존 state에 의존성을 가질 때 사용된다. 또한 useReducer를 사용하면 &lt;a href=&quot;http://reactjs.org/docs/hooks-faq.html#how-to-avoid-passing-callbacks-down&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;하위 컴포넌트에게 callback 함수가 아니라 dispatch 함수를 넘겨줄 수 있어&lt;/a&gt; 유지보수 관점에서 매우 편리해진다. (하위 컴포넌트들에게 계속 prop으로 전달, 전달, 전달을 할 필요가 없어지니까!)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;사실 Redux의 주요 작동 원리 중 하나라서 만약에 Redux를 자주 사용했다면 useReducer가 어떻게 동작하는지 쉽게 이해할 수 있을 것이다. 보통 useReducer는 state 객체와 함께 사용되지만 좀 더 쉽게 이해하기 위해서 Epic React 강의에 나온대로 single value와 함께 사용한 예제를 살펴보자~!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ck2nFv/btqMNwMINIj/nlLBMbPI1SnGend2QMCYz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ck2nFv/btqMNwMINIj/nlLBMbPI1SnGend2QMCYz0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ck2nFv/btqMNwMINIj/nlLBMbPI1SnGend2QMCYz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fck2nFv%2FbtqMNwMINIj%2FnlLBMbPI1SnGend2QMCYz0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1604817390716&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, { useState } from &quot;react&quot;;

const initialName = &quot;Joe&quot;;

export default function App() {
  const [name, setName] = useState(initialName);
  const handleChange = (e) =&amp;gt; setName(e.target.value);

  return (
    &amp;lt;div className=&quot;App&quot;&amp;gt;
      &amp;lt;label&amp;gt;
        Name:
        &amp;lt;input defaultValue={initialName} onChange={handleChange} /&amp;gt;
      &amp;lt;/label&amp;gt;
      &amp;lt;p&amp;gt;Hello,My name is {name}&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;엄청나게 간단한 앱이다. input 창에 이름을 치면 아래 해당 이름에 따라 글자가 바뀌는데,&lt;/p&gt;
&lt;p&gt;useState hook을 useReducer hook으로 바꾸면 아래와 같이 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1604817538703&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, { useReducer } from &quot;react&quot;;

const nameReducer = (prevName, newName) =&amp;gt; newName;
const initialName = &quot;Joe&quot;;

export default function App() {
  const [name, setName] = useReducer(nameReducer, initialName);
  const handleChange = (e) =&amp;gt; setName(e.target.value);

  return (
    &amp;lt;div className=&quot;App&quot;&amp;gt;
      &amp;lt;label&amp;gt;
        Name:
        &amp;lt;input defaultValue={initialName} onChange={handleChange} /&amp;gt;
      &amp;lt;/label&amp;gt;
      &amp;lt;p&amp;gt;Hello,My name is {name}&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위 코드는 아까 useState hook을 사용한 것과 정확히 동일한 기능을 한다.&lt;/p&gt;
&lt;p&gt;여기서 중요하게 살펴봐야할 것은, useReducer에 첫 번째 인자로 넣은 nameReducer 함수의 2개의 arguments다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;1. prevName: 기존의 state값이다.&lt;/p&gt;
&lt;p&gt;2. newName: dispatch 함수(위의 경우에 setName)가 호출될 때의 state 값이다. 주로 이것을 'action'이라고 부른다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 위 앱에서 input의 값이 바뀔 때마다 다음과 같은 흐름으로 실행된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;- input의 value가 바뀔 때마다 setName(dispatch)이 바뀐 input value값과 함께 실행된다.&lt;/p&gt;
&lt;p&gt;- &lt;span style=&quot;color: #333333;&quot;&gt;setName(dispatch)이&lt;/span&gt; 실행되면, nameReducer가 실행되는데, 첫 번째 인자로 기존의 name값이 들어오고, 두 번째 인자로는 바뀐 input value값이 들어온다.&lt;/p&gt;
&lt;p&gt;- reducer 함수가 바뀐 input value을 return 하므로 해당 값으로 state가 업데이트된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LYHHC/btqMM3YmYYQ/R6X1BQ7SjpkDkxTmkXyfwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LYHHC/btqMM3YmYYQ/R6X1BQ7SjpkDkxTmkXyfwk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LYHHC/btqMM3YmYYQ/R6X1BQ7SjpkDkxTmkXyfwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLYHHC%2FbtqMM3YmYYQ%2FR6X1BQ7SjpkDkxTmkXyfwk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1604823418883&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, { useReducer } from &quot;react&quot;;
import &quot;./styles.css&quot;;

const countReducer = (prevState, step) =&amp;gt; prevState + step;
const initialValue = 0;

export default function App() {
  const [count, setCount] = useReducer(countReducer, initialValue);
  const increment = () =&amp;gt; setCount(1);

  return (
    &amp;lt;div className=&quot;App&quot;&amp;gt;
      &amp;lt;p&amp;gt;{count}&amp;lt;/p&amp;gt;
      &amp;lt;button onClick={increment}&amp;gt;increment&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 increment 버튼을 클릭하면 화면의 숫자가 하나씩 증가하는 앱을 만들어보자.&lt;/p&gt;
&lt;p&gt;reducer 함수를 통해 기존 state에 dispatch의 인자로 넘기는 숫자를 더한 값을 update하도록 만들었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;즉, 버튼을 클릭하면 dispatch가 실행되는데,&lt;/p&gt;
&lt;p&gt;dispatch와 함께 실행되는 1과 함께 countReducer가 실행되고&lt;/p&gt;
&lt;p&gt;원래 state에 1이 더해진 값이 새로운 상태로 업데이트된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;클래스 컴포넌트의 setState와 똑같은 state updater 함수 만들기&lt;/h3&gt;
&lt;p&gt;이제 useReducer의 초기 값을 object로 바꾸고, object state를 업데이트하는 함수를 useReducer로 만들어보자.&lt;/p&gt;
&lt;p&gt;(클래스 컴포넌트를 사용해봤다면 this.setState 함수를 통해 state 객체를 통째로 업데이트해본 적이 있을 것이다.)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1604824183586&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, { useReducer } from &quot;react&quot;;
import &quot;./styles.css&quot;;

const countReducer = (prevState, newState) =&amp;gt; ({ ...prevState, ...newState });
const initialValue = { count: 0 };

export default function App() {
  const [state, setState] = useReducer(countReducer, initialValue);

  const { count } = state;
  const increment = () =&amp;gt; setState({ count: count + 1 });

  return (
    &amp;lt;div className=&quot;App&quot;&amp;gt;
      &amp;lt;p&amp;gt;{count}&amp;lt;/p&amp;gt;
      &amp;lt;button onClick={increment}&amp;gt;increment&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위와 같이 만들면 된다!&lt;/p&gt;
&lt;p&gt;initialValue는 count라는 property와 초기값 0을 가진 객체이다.&lt;/p&gt;
&lt;p&gt;이제, setState라는 dispatch 함수를 실행시킬 때마다 count 값에 1을 더하는 action 객체와 함께 호출한다.&lt;/p&gt;
&lt;p&gt;그러면 countReducer 함수에서 기존 state객체에 새 state 객체를 합치면서 상태값이 업데이트된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이렇게 만들면 클래스 컴포넌트의 setState와 똑같은 기능을 하는 함수가 만들어진다.&lt;/p&gt;
&lt;p&gt;그런데 알고있다시피 setState는 객체 뿐만 아니라 함수를 인자로 넘길 수 있어야 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;setState(currentState =&amp;gt; ({ count: &lt;span style=&quot;color: #333333;&quot;&gt;currentState + 1 &lt;/span&gt;}));&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이렇게 함수를 넣었을때도 동일하게 작동하려면 어떻게 해야할까?&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1604824926815&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, { useReducer } from &quot;react&quot;;
import &quot;./styles.css&quot;;

const countReducer = (state, action) =&amp;gt; ({
  ...state,
  ...(typeof action === &quot;function&quot; ? action(state) : action)
});
const initialValue = { count: 0 };

export default function App() {
  const [state, setState] = useReducer(countReducer, initialValue);

  const increment = () =&amp;gt;
    setState((currentState) =&amp;gt; ({ count: currentState.count + 1 }));

  const { count } = state;

  return (
    &amp;lt;div className=&quot;App&quot;&amp;gt;
      &amp;lt;p&amp;gt;{count}&amp;lt;/p&amp;gt;
      &amp;lt;button onClick={increment}&amp;gt;increment&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;countReducer를 위와 같이 바꾸면 된다! 넘나 간단하다  &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;action 인자의 type이 function이면 state를 인자로 넣어서 실행시킨 후,&lt;/p&gt;
&lt;p&gt;리턴되는 객체를 state에 합쳐서 업데이트를 하고,&lt;/p&gt;
&lt;p&gt;type이 객체면 그냥 바로 state에 업데이트하면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Redux 스타일의 reducer 만들어보기&lt;/h3&gt;
&lt;p&gt;이제 redux 라이브러리를 쓰면서 많이 봐왔던 dispatch, action, reducer를 만들어보도록 하자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1604825449737&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, { useReducer } from &quot;react&quot;;
import &quot;./styles.css&quot;;

const countReducer = (state, action) =&amp;gt; {
  switch (action.type) {
    case &quot;INCREMENT&quot;:
      return {
        ...state,
        count: state.count + action.step
      };
    default:
      throw new Error(&quot;Not a valid action type&quot;);
  }
};
const initialState = { count: 0 };

export default function App() {
  const [state, dispatch] = useReducer(countReducer, initialState);
  const step = 1;

  const increment = () =&amp;gt; {
    dispatch({ type: &quot;INCREMENT&quot;, step });
  };

  const { count } = state;

  return (
    &amp;lt;div className=&quot;App&quot;&amp;gt;
      &amp;lt;p&amp;gt;{count}&amp;lt;/p&amp;gt;
      &amp;lt;button onClick={increment}&amp;gt;increment&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;reducer 함수에 switch 구문을 써서 action으로 들어오는 객체의 type에 따라&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;새로운 state를 리턴하도록 만들어준다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;action 객체의 타입이 '&lt;span style=&quot;color: #333333;&quot;&gt;INCREMENT'라면&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;기존 state 객체의 count에 action 객체로 들어오는 step value를 더해주도록 했다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이제 INCREMENT라는 타입과 step 객체와 함께 dispatch 함수를 실행시키면,&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;countReducer에 의해 기존 count 값에 step인 1을 더한 값이 든 새로운 객체가 새로운 state로 업데이트된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1604825951121&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, { useReducer } from &quot;react&quot;;
import &quot;./styles.css&quot;;

const countReducer = (state, action) =&amp;gt; {
  switch (action.type) {
    case &quot;INCREMENT&quot;:
      return {
        ...state,
        count: state.count + action.step
      };
    case &quot;DECREMENT&quot;:
      return {
        ...state,
        count: state.count - action.step
      };
    default:
      throw new Error(&quot;Not a valid action type&quot;);
  }
};

const initialState = { count: 0 };

export default function App() {
  const [state, dispatch] = useReducer(countReducer, initialState);
  const step = 1;

  const increment = () =&amp;gt; {
    dispatch({ type: &quot;INCREMENT&quot;, step });
  };

  const decrement = () =&amp;gt; {
    dispatch({ type: &quot;DECREMENT&quot;, step });
  };

  const { count } = state;

  return (
    &amp;lt;div className=&quot;App&quot;&amp;gt;
      &amp;lt;p&amp;gt;{count}&amp;lt;/p&amp;gt;
      &amp;lt;button onClick={increment}&amp;gt;increment&amp;lt;/button&amp;gt;
      &amp;lt;button onClick={decrement}&amp;gt;decrement&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;countReducer에 여러 가지 type을 추가해서 다양하게 사용할 수 있다!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;솔직히 그 동안 redux를 쓰면서도 redux 내부에서 어떤 구조로 움직이는지 완전히 파악하지는 못했는데&lt;/p&gt;
&lt;p&gt;useReducer 동작 원리를 들여다보니 훨씬 이해가 잘 되는 것 같다.&lt;/p&gt;</description>
      <category>Frontend</category>
      <category>EpicReact</category>
      <category>hooks</category>
      <category>react</category>
      <category>useReducer</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/212</guid>
      <comments>https://im-developer.tistory.com/212#entry212comment</comments>
      <pubDate>Sun, 8 Nov 2020 18:06:34 +0900</pubDate>
    </item>
    <item>
      <title>[Kent C. dodds - Epic React] React hooks(2) - HTTP Request &amp;amp; Error Handling</title>
      <link>https://im-developer.tistory.com/211</link>
      <description>&lt;p&gt;Kent C. dodds의 Epic React 강의에서 &lt;b&gt;React hooks - &lt;/b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;useEffect: &lt;span style=&quot;color: #006dd7;&quot;&gt;HTTP requests&lt;/span&gt;&lt;/b&gt; 챕터를&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;정리해보려고 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;일단 간단하게 API를 호출해서 화면에 API 응답으로 온 랜덤 강아지 이미지를 보여주는 예시를 만들어보려고 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1604213714579&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const fetchRandomDog = (error) =&amp;gt;
  fetch(&quot;https://dog.ceo/api/breeds/image/random&quot;).then((data) =&amp;gt; {
    if (error) {
      throw error;
    }
    return data.json();
  });&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;그리고 에러가 나는 상황을 연출하기 위해 인자로 error가 넘어오면 error를 throw하고,&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;그 외의 경우에는 fetch받은 data를 JSON format으로 변환해서 return 시켜주기로 했다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1604214208101&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React from &quot;react&quot;;
import &quot;./styles.css&quot;;

const fetchRandomDog = (error) =&amp;gt;
  fetch(&quot;https://dog.ceo/api/breeds/image/random&quot;).then((data) =&amp;gt; {
    if (error) {
      throw error;
    }
    return data.json();
  });

export default function App() {
  const [loading, setLoading] = React.useState(false);
  const [image, setImage] = React.useState(null);
  const [error, setError] = React.useState(null);

  const fetchDog = (_, error) =&amp;gt; {
    setLoading(true);
    fetchRandomDog(error).then(
      ({ message }) =&amp;gt; {
        setImage(message);
        setLoading(false);
      },
      (error) =&amp;gt; {
        setError(error);
        setLoading(false);
      }
    );
  };

  React.useEffect(() =&amp;gt; {
    fetchDog();
  }, []);

  if (error) {
    return (
      &amp;lt;div className=&quot;App&quot;&amp;gt;
        &amp;lt;div&amp;gt;{error.message}&amp;lt;/div&amp;gt;
        &amp;lt;button type=&quot;button&quot; onClick={fetchDog}&amp;gt;
          Retry
        &amp;lt;/button&amp;gt;
      &amp;lt;/div&amp;gt;
    );
  }

  return (
    &amp;lt;div className=&quot;App&quot;&amp;gt;
      {loading ? (
        &amp;lt;div&amp;gt;is Loading...&amp;lt;/div&amp;gt;
      ) : (
        &amp;lt;&amp;gt;
          &amp;lt;button type=&quot;button&quot; onClick={fetchDog}&amp;gt;
            Change photo
          &amp;lt;/button&amp;gt;
          &amp;lt;button
            type=&quot;button&quot;
            onClick={(e) =&amp;gt;
              fetchDog(e, new Error(&quot;oops! something went wrong&quot;))
            }
          &amp;gt;
            Throw error
          &amp;lt;/button&amp;gt;
          &amp;lt;img src={image} alt=&quot;random dog&quot; /&amp;gt;
        &amp;lt;/&amp;gt;
      )}
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;433&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rxIra/btqMfjGwMss/9yZcr6xxgj4LzXtuZfUdhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rxIra/btqMfjGwMss/9yZcr6xxgj4LzXtuZfUdhk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rxIra/btqMfjGwMss/9yZcr6xxgj4LzXtuZfUdhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrxIra%2FbtqMfjGwMss%2F9yZcr6xxgj4LzXtuZfUdhk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;433&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 앱은 대충 이렇게 생겼다.&lt;/p&gt;
&lt;p&gt;Change photo 버튼을 클릭하면 매 번 새로운 사진을 HTTP request를 통해 가져오고 화면에 보여준다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위 상황에서 문제는 에러가 발생하고 난 이후인데,&lt;/p&gt;
&lt;p&gt;Throw error 버튼을 클릭하면 아래와 같은 에러뷰가 뜬다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;526&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPo0WD/btqMdUOcP34/TYm8wokaGaqP9zxy2sVx10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPo0WD/btqMdUOcP34/TYm8wokaGaqP9zxy2sVx10/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPo0WD/btqMdUOcP34/TYm8wokaGaqP9zxy2sVx10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPo0WD%2FbtqMdUOcP34%2FTYm8wokaGaqP9zxy2sVx10%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;526&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이 상황에서 내가 Retry 버튼을 누르면 &lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;다시 fetch가 일어나서 정상적으로 사진을 가져와야하는데&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;fetch가 일어날때마다 가져온 데이터를 콘솔창에 출력하도록 한 후에 &lt;span style=&quot;color: #333333;&quot;&gt;어떻게 되는지 살펴보자!&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1604214854724&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const fetchDog = (_, error) =&amp;gt; {
  setLoading(true);
  fetchRandomDog(error).then(
    ({ message }) =&amp;gt; {
      console.log(message);

      setImage(message);
      setLoading(false);
    },
    (error) =&amp;gt; {
      setError(error);
      setLoading(false);
    }
  );
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;2020-11-01 16.12.40.gif&quot; data-origin-width=&quot;599&quot; data-origin-height=&quot;405&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c9icT4/btqMdFjss1q/wPr3hTGXXqn7Hu9chVrGY0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c9icT4/btqMdFjss1q/wPr3hTGXXqn7Hu9chVrGY0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c9icT4/btqMdFjss1q/wPr3hTGXXqn7Hu9chVrGY0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/c9icT4/btqMdFjss1q/wPr3hTGXXqn7Hu9chVrGY0/img.gif&quot; data-filename=&quot;2020-11-01 16.12.40.gif&quot; data-origin-width=&quot;599&quot; data-origin-height=&quot;405&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;분명 fetch가 일어남에도 불구하고 화면은 계속 에러 뷰만 보여주고 있다.&lt;/p&gt;
&lt;p&gt;그 이유는 fetch가 일어나서 image state가 업데이트되더라도&lt;/p&gt;
&lt;p&gt;기존에 업데이트된 error state가 리셋되지 않았기 때문이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이걸 해결하려면 매번 fetch가 시작되기 전에 setError(null)을 통해 error state를 reset해 주어야 한다!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1604215022294&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const fetchDog = (_, error) =&amp;gt; {
  setLoading(true);
  setError(null); // Reset Error
  fetchRandomDog(error).then(
    ({ message }) =&amp;gt; {
      setImage(message);
      setLoading(false);
    },
    (error) =&amp;gt; {
      setError(error);
      setLoading(false);
    }
  );
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 얼마나 귀찮은 일이란 말인가!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그래서 이걸 해결하는 간단한 방법이 있는데,&lt;/p&gt;
&lt;p&gt;HTTP request의 상태를 나타내는 string 값을 value로 가지는 state를 하나 만들어서 처리해주는 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;idle: no request made yet&lt;/li&gt;
&lt;li&gt;pending: request started&lt;/li&gt;
&lt;li&gt;resolved: request successful&lt;/li&gt;
&lt;li&gt;rejected: request failed&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1604215462211&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export default function App() {
  const [status, setStatus] = React.useState(&quot;idl&quot;);
  const [image, setImage] = React.useState(null);
  const [error, setError] = React.useState(null);

  const fetchDog = (_, error) =&amp;gt; {
    setStatus(&quot;pending&quot;);
    fetchRandomDog(error).then(
      ({ message }) =&amp;gt; {
        setImage(message);
        setStatus(&quot;resolved&quot;);
      },
      (error) =&amp;gt; {
        setError(error);
        setStatus(&quot;rejected&quot;);
      }
    );
  };

  React.useEffect(() =&amp;gt; {
    fetchDog();
  }, []);

  if (status === &quot;rejected&quot;) {
    return (
      &amp;lt;div className=&quot;App&quot;&amp;gt;
        &amp;lt;div&amp;gt;{error.message}&amp;lt;/div&amp;gt;
        &amp;lt;button type=&quot;button&quot; onClick={fetchDog}&amp;gt;
          Retry
        &amp;lt;/button&amp;gt;
      &amp;lt;/div&amp;gt;
    );
  }

  return (
    &amp;lt;div className=&quot;App&quot;&amp;gt;
      {status === &quot;pending&quot; ? (
        &amp;lt;div&amp;gt;is Loading...&amp;lt;/div&amp;gt;
      ) : (
        &amp;lt;&amp;gt;
          &amp;lt;button type=&quot;button&quot; onClick={fetchDog}&amp;gt;
            Change photo
          &amp;lt;/button&amp;gt;
          &amp;lt;button
            type=&quot;button&quot;
            onClick={(e) =&amp;gt;
              fetchDog(e, new Error(&quot;oops! something went wrong&quot;))
            }
          &amp;gt;
            Throw error
          &amp;lt;/button&amp;gt;
          &amp;lt;img src={image} alt=&quot;random dog&quot; /&amp;gt;
        &amp;lt;/&amp;gt;
      )}
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이렇게 처리해주면 error를 매 번 reset 해주지 않아도 원하는 데로 작동한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이제 위 코드를 조금 더 개선한다면 state값을 하나의 객체로 저장하는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1604216155157&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const [state, setState] = React.useState({
  status: &quot;idl&quot;,
  image: null,
  error: null
});

const fetchDog = (_, error) =&amp;gt; {
  setState({ status: &quot;pending&quot; });
  fetchRandomDog(error).then(
    ({ message }) =&amp;gt; {
      setState({ status: &quot;resolved&quot;, image: message });
    },
    (error) =&amp;gt; {
      setState({ status: &quot;rejected&quot;, error });
    }
  );
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;기존에는 fetch가 일어난 이후 setImage와 setStatus를 각각 호출하고,&lt;/p&gt;
&lt;p&gt;그 결과 2번의 re-render가 일어나게 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그런데 하나의 object에 state값을 저장하고 위와 같이 state를 update해준다면 딱 한 번만 re-render가 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ErrorBoundary&lt;/h3&gt;
&lt;p&gt;만약에 Network 에러는 전혀 없었지만 다른 곳에서 예상치 못한 에러가 발생한 경우에는 어떻게 해야할까?&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;다음과 같은 경우를 생각해보자.&lt;/p&gt;
&lt;pre id=&quot;code_1604217536647&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const [state, setState] = React.useState({
  status: &quot;idl&quot;,
  image: { message: null },
  error: null
});

// ...

return (
  &amp;lt;img src={image.message} alt=&quot;random dog&quot; /&amp;gt;
);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;초기 image state 값이 위와 같은 객체이고, image.message를 사용해서 이미지를 표현해주고 있는데&lt;/p&gt;
&lt;p&gt;정상적으로 fetch가 이루어졌으나 데이터가 { message: '' } 와 같은 형태의 객체 대신에 null이 왔다고 가정해보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1604217635848&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const fetchDog = (_, error) =&amp;gt; {
  setState({ status: &quot;pending&quot; });
  fetchRandomDog(error).then(
    (data) =&amp;gt; {
      setState({ status: &quot;resolved&quot;, image: null });
    },
    (error) =&amp;gt; {
      setState({ status: &quot;rejected&quot;, error });
    }
  );
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;물론 내가 그렇게 나오도록 API를 조작할 수는 없으니,&lt;/p&gt;
&lt;p&gt;null이 왔다고 가정한 후에 &lt;span style=&quot;color: #333333;&quot;&gt;대충 위와 같이 image state 값으로 null을 업데이트해주었다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;현재 Network 에러가 발생했을 때만 에러뷰가 보여지도록 설계되어 있기 때문에&lt;/p&gt;
&lt;p&gt;위 케이스에서는 유저에게 그 저 하얀색 공백 페이지만 보여질 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;바로 이런 경우에 필요한 것이 ErrorBoundary이다.&lt;/p&gt;
&lt;p&gt;안타깝게도 현재까지 ErrorBoundary는 class component로만 선언이 가능하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1604217358005&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class ErrorBoundary extends React.Component {
  state = { error: null };

  static getDerivedStateFromError(error) {
    return { error };
  }

  render() {
    const { error } = this.state;
    if (error) {
      return &amp;lt;this.props.FallbackComponent error={error} /&amp;gt;;
    }

    return this.props.children;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;대충 위와 같은 구조로 만들 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;초기 state에 error를 저장해주고,&lt;/p&gt;
&lt;p&gt;getDerivedStateFromError method를 통해 error가 발생했을때 인자로 error를 받을 수 있다.&lt;/p&gt;
&lt;p&gt;이 때 getDerivedStateFromError에서 return해주는 값으로 state가 update된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그러면 error가 존재하면 인자로 넘어 온 에러뷰를 보여주고,&lt;/p&gt;
&lt;p&gt;error가 없을 때는 children을 return하도록 하면 된다!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1604218118892&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React from &quot;react&quot;;
import ReactDOM from &quot;react-dom&quot;;

import App from &quot;./App&quot;;

class ErrorBoundary extends React.Component {
  state = { error: null };

  static getDerivedStateFromError(error) {
    return { error };
  }

  render() {
    const { error } = this.state;
    if (error) {
      return &amp;lt;this.props.FallbackComponent error={error} retry={() =&amp;gt; window.reload()} /&amp;gt;;
    }

    return this.props.children;
  }
}

const FallbackComponent = ({ error, retry }) =&amp;gt; (
  &amp;lt;div className=&quot;App&quot;&amp;gt;
    &amp;lt;div&amp;gt;{error.message}&amp;lt;/div&amp;gt;
    &amp;lt;button type=&quot;button&quot; onClick={retry}&amp;gt;Retry&amp;lt;/button&amp;gt;
  &amp;lt;/div&amp;gt;
);

const rootElement = document.getElementById(&quot;root&quot;);
ReactDOM.render(
  &amp;lt;React.StrictMode&amp;gt;
    &amp;lt;ErrorBoundary FallbackComponent={FallbackComponent}&amp;gt;
      &amp;lt;App /&amp;gt;
    &amp;lt;/ErrorBoundary&amp;gt;
  &amp;lt;/React.StrictMode&amp;gt;,
  rootElement
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이렇게 컴포넌트 상위에 ErrorBoundary를 감싸주면,&lt;/p&gt;
&lt;p&gt;ErrorBoundary 하위 컴포넌트에서 발생한 에러를 모두 감지하여 핸들링할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1604218212125&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export default function App() {
  const [state, setState] = React.useState({
    status: &quot;idl&quot;,
    image: { message: null },
    error: null
  });

  const fetchDog = (_, error) =&amp;gt; {
    setState({ status: &quot;pending&quot; });
    fetchRandomDog(error).then(
      (data) =&amp;gt; {
        setState({ status: &quot;resolved&quot;, image: data });
      },
      (error) =&amp;gt; {
        setState({ status: &quot;rejected&quot;, error });
      }
    );
  };

  React.useEffect(() =&amp;gt; {
    fetchDog();
  }, []);

  const { status, image, error } = state;

  if (status === &quot;rejected&quot;) {
    // Throw error to let ErrorBoundary handle this error
    throw error;
  }

  return (
    &amp;lt;div className=&quot;App&quot;&amp;gt;
      {status === &quot;pending&quot; ? (
        &amp;lt;div&amp;gt;is Loading...&amp;lt;/div&amp;gt;
      ) : (
        &amp;lt;&amp;gt;
          &amp;lt;button type=&quot;button&quot; onClick={fetchDog}&amp;gt;
            Change photo
          &amp;lt;/button&amp;gt;
          &amp;lt;button
            type=&quot;button&quot;
            onClick={(e) =&amp;gt;
              fetchDog(e, new Error(&quot;oops! something went wrong&quot;))
            }
          &amp;gt;
            Throw error
          &amp;lt;/button&amp;gt;
          &amp;lt;img src={image.message} alt=&quot;random dog&quot; /&amp;gt;
        &amp;lt;/&amp;gt;
      )}
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 네트워크 에러가 발생했을 때,&lt;/p&gt;
&lt;p&gt;에러뷰를 App 컴포넌트 내에 만들 필요가 없이 그냥 throw하기만 하면,&lt;/p&gt;
&lt;p&gt;ErrorBoundary가 핸들링해준다!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;react-error-boundary&lt;/h3&gt;
&lt;p&gt;ErrorBoundary를 만들어쓰기 귀찮다면 &lt;a href=&quot;https://github.com/bvaughn/react-error-boundary&quot;&gt;react-error-boundary&lt;/a&gt;라는 유용한 패키지가 있다.&lt;/p&gt;
&lt;p&gt;(강의에서는 아래 라이브러리 사용을 매우 권장했다!)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1604219270347&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function ErrorFallback({error, resetErrorBoundary}) {
  return (
    &amp;lt;div role=&quot;alert&quot;&amp;gt;
      &amp;lt;p&amp;gt;Something went wrong:&amp;lt;/p&amp;gt;
      &amp;lt;pre&amp;gt;{error.message}&amp;lt;/pre&amp;gt;
      &amp;lt;button onClick={resetErrorBoundary}&amp;gt;Try again&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}

function Bomb() {
  throw new Error('  CABOOM  ')
}

function App() {
  const [explode, setExplode] = React.useState(false)
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;button onClick={() =&amp;gt; setExplode(e =&amp;gt; !e)}&amp;gt;toggle explode&amp;lt;/button&amp;gt;
      &amp;lt;ErrorBoundary
        FallbackComponent={ErrorFallback}
        onReset={() =&amp;gt; setExplode(false)}
        resetKeys={[explode]}
      &amp;gt;
        {explode ? &amp;lt;Bomb /&amp;gt; : null}
      &amp;lt;/ErrorBoundary&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;onReset이라는 prop으로 state를 초기화하는 함수를 넣어주고,&lt;/p&gt;
&lt;p&gt;FallbackComponent에서 resetErrorBoundary라는 함수를 재시도 버튼과 연결해주면,&lt;/p&gt;
&lt;p&gt;재시도 버튼을 클릭했을때 state가 초기화된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;여튼 다양한 기능들을 제공해주니 나중에 한 번 프로젝트에 적용해봐야겠다.&lt;/p&gt;</description>
      <category>Frontend</category>
      <category>EpicReact</category>
      <category>ErrorBoundary</category>
      <category>hooks</category>
      <category>react</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/211</guid>
      <comments>https://im-developer.tistory.com/211#entry211comment</comments>
      <pubDate>Sun, 1 Nov 2020 17:58:14 +0900</pubDate>
    </item>
    <item>
      <title>[Kent C. dodds - Epic React] React hooks(1) - Lazy initial state / useRef</title>
      <link>https://im-developer.tistory.com/210</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;399&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uAjoa/btqLLGuOVDo/RTCgOKwT9uHVlK1z8GdyKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uAjoa/btqLLGuOVDo/RTCgOKwT9uHVlK1z8GdyKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uAjoa/btqLLGuOVDo/RTCgOKwT9uHVlK1z8GdyKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuAjoa%2FbtqLLGuOVDo%2FRTCgOKwT9uHVlK1z8GdyKk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;399&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;정말 오랜만에 글을 쓰려고 블로그를 켰더니 너무 낯설다.&lt;/p&gt;
&lt;p&gt;회사 일이 바쁘다는 핑계로 살짝 멀리했었는데 블로그야 미안...&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;회사 슬랙에 &lt;b&gt;Kent C. dodds&lt;/b&gt;의 &lt;a href=&quot;https://epicreact.dev&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Epic React 강의&lt;/a&gt;에 관심있는 사람 있으면 같이 공동구매해서 듣자는 제안이 올라왔는데&amp;nbsp;&lt;/p&gt;
&lt;p&gt;쿨하게 회사에서 지원을 해줘서 공짜로 들을 수 있게 되었다.  &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;강의 챕터는 요렇게 구성되어있다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;넘나 알찬 구성  &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;React&amp;nbsp;Fundamentals&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;React&amp;nbsp;Hooks&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Advanced&amp;nbsp;React&amp;nbsp;Hooks&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Advanced&amp;nbsp;React&amp;nbsp;Patterns&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;React&amp;nbsp;Performance&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Testing&amp;nbsp;React&amp;nbsp;Apps&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;React&amp;nbsp;Suspense&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Build&amp;nbsp;an&amp;nbsp;Epic&amp;nbsp;React&amp;nbsp;App&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Epic&amp;nbsp;React&amp;nbsp;Expert&amp;nbsp;Interviews&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;오늘은 &lt;b&gt;React Hooks&amp;nbsp;&lt;/b&gt;파트를 절반 정도 들었는데,&lt;/p&gt;
&lt;p&gt;오늘 들었던 내용 중에 내가 처음 알게 된 것, 재밌게 본 것 한 두가지를 정리해보려고 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Lazy&amp;nbsp;initial&amp;nbsp;state&lt;/h3&gt;
&lt;p&gt;React 공식 문서에 useState 설명 밑에 쬐끄만한 소제목으로 적혀있는 내용인데 이런 기능이 있는 줄 오늘 강의보고 처음 알았다.  &lt;/p&gt;
&lt;p&gt;(물론 initialState 값을 넣을 때 엄청나게 복잡한 계산을 해야하는 경우가 거의 없어서 알았어도 많이 썼을 것 같지는 않지만 어쨌든!)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;만약에 내가 어떤 값들을 localStorage에 저장해서 새로고침을 해도 내가 입력한 값들이 그대로 화면에 보여지도록 한다고 생각해보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1603633671115&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function Name() {
  const [name, setName] = React.useState(
    window.localStorage.getItem('name') || ''
  );
  
  React.useEffect(() =&amp;gt; {
    window.localStorage.setItem('name', name);
  }, []);
  
  const handleChange = (event) =&amp;gt; {
    setName(event.target.value);
  }
  
  return (
    &amp;lt;&amp;gt;
      &amp;lt;input value={name} onChange={handleChange} /&amp;gt;
      {name ? &amp;lt;p&amp;gt;Hello, {name}&amp;lt;/p&amp;gt; : 'Please type your name'}
    &amp;lt;/&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;대충 위와 같은 컴포넌트가 만들어질거다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;여기서 name을 state로 관리하는데 초기 name 값을&lt;/p&gt;
&lt;p&gt;localStorage에 저장된 name 값이 있으면 그 값으로 지정하고,&lt;/p&gt;
&lt;p&gt;값이 없는 경우에는 빈 string을 입력한다고 해보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그러면 매 번 Name 컴포넌트가 실행되면 로컬 스토리지에서 값을 조회한 후 가져오는 로직을 수행하게 된다.&lt;/p&gt;
&lt;p&gt;그런데 문제는 사실 초기 State로 가져오는 값은, 이 컴포넌트를 최초로 렌더링할 때 딱 한 번만 가져오면 된다.&lt;/p&gt;
&lt;p&gt;이 후에 유저가 input에 어떤 값을 입력하게 되면 더 이상 초기값은 화면에 보여질 필요가 없어진다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그러나 그냥 위와 같이 초기 state값을 설정하면 이 컴포넌트가 re-render가 될 때마다&lt;/p&gt;
&lt;p&gt;쓸데없이 로컬 스토리지에서 name값을 탐색을 하게 되어 성능에 안좋은 영향을 줄 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이런 문제를 해결하는 방법은 아주 간단한데 바로 리액트의 useState hook의 초기 state로&lt;/p&gt;
&lt;p&gt;value가 아니라 value를 리턴하는 function을 전달하는 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1603634247524&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function Name() {
  const [name, setName] = React.useState(() =&amp;gt; {
    const valueInStorage = window.localStorage.getItem('name');
    
    return valueInStorage || '';
  });
  
  React.useEffect(() =&amp;gt; {
    window.localStorage.setItem('name', name);
  }, []);
  
  const handleChange = (event) =&amp;gt; {
    setName(event.target.value);
  }
  
  return (
    &amp;lt;&amp;gt;
      &amp;lt;input value={name} onChange={handleChange} /&amp;gt;
      {name ? &amp;lt;p&amp;gt;Hello, {name}&amp;lt;/p&amp;gt; : 'Please type your name'}
    &amp;lt;/&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 리액트는 컴포넌트가 최초로 렌더링될 때 딱 한 번만 useState 인자로 전달받은 함수를 호출하여 초기 값을 세팅할 것이다!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1603633106139&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const [state, setState] = useState(() =&amp;gt; {
  const initialState = someExpensiveComputation(props);
  return initialState;
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(^ 공식 문서에 있는 예제)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Bailing out of a state update&lt;/h3&gt;
&lt;p&gt;요거는 강의에 나온 내용은 아니지만... 공식 문서에서&amp;nbsp;&lt;span style=&quot;color: #333333;&quot;&gt;lazy initial state 부분을&lt;/span&gt;&amp;nbsp;찾아서 읽다가 같이 읽은 내용인데&lt;/p&gt;
&lt;p&gt;만약에 State hook을 기존 state와 동일한 값으로 업데이트한다면&lt;/p&gt;
&lt;p&gt;리액트는 하위 자식들을 렌더하거나 effects를 호출하지 않고 그냥 실행을 종료한다고 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그리고 리액트는 Object.is와 동일한 알고리즘으로 값을 비교한다는 말이 나오는데&lt;/p&gt;
&lt;p&gt;이 참에 Object.is가 어떤 알고리즘으로 비교하는지 정리하자면...&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;Object.is(value1, value2)&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;Object.is는 2개의 인자를 받아서 비교를 한 후에, 값이 같으면 true 같지 않으면 false를 return한다.&lt;/p&gt;
&lt;p&gt;그리고 다음과 같은 경우에 두 값이 같다고 판단한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp; 1) 두 값이 undefined일 때&lt;/p&gt;
&lt;p&gt;&amp;nbsp; 2) 두 값이 null일 때&lt;/p&gt;
&lt;p&gt;&amp;nbsp; 3) 두 값이 둘 다 true나 false일 때&lt;/p&gt;
&lt;p&gt;&amp;nbsp; 4) 두 값이 같은 길이의 같은 글자를 같은 순서로 사용한 string일 때&lt;/p&gt;
&lt;p&gt;&amp;nbsp; 5) 두 값이 같은 reference를 가진 object일 때&lt;/p&gt;
&lt;p&gt;&amp;nbsp; 6) 두 값이 같은 숫자일 때 (둘 다 +0 / -0 / NaN / 혹은 0과 NaN이 아닌 같은 값의 숫자)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;얼핏보면&amp;nbsp;strict equality operator인 ===과 똑같은 방식이라고 생각할 수 있지만 약간 다른 점이 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;===는 -0과 +0을 같은 값이라고 여기지만 Object.is는 아니다.&lt;/li&gt;
&lt;li&gt;===는 NaN과 NaN을 다른 값이라고 여기지만 Object.is는 같은 값이라고 여긴다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cRhZow/btqLFSps2OF/lANNslJKpoCDLueQxa5VR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cRhZow/btqLFSps2OF/lANNslJKpoCDLueQxa5VR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cRhZow/btqLFSps2OF/lANNslJKpoCDLueQxa5VR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcRhZow%2FbtqLFSps2OF%2FlANNslJKpoCDLueQxa5VR0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;useRef - Is&amp;nbsp;there&amp;nbsp;something&amp;nbsp;like&amp;nbsp;instance&amp;nbsp;variables?&lt;/h3&gt;
&lt;p&gt;useRef를 배울 때 거의 DOM을 reference하기 위해서 사용한다는 식으로 배웠던 것 같다.&lt;/p&gt;
&lt;p&gt;React는 가상 DOM을 사용하니까 실제 DOM을 조작하기 위해서 useRef 기능으로 DOM의 reference를 저장해서 사용하는 식이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그런데 사실 useRef hook은 DOM reference로 사용하기 위한 것만은 아니다.&lt;/p&gt;
&lt;p&gt;오늘 강의에서 다양하게 useRef를 활용한 예제 코드들을 보고 이참에 정리해보고자 공식 문서 내용을 가져왔다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;ref 객체는 current라는 property로 이루어져 있는데, 이 current property는 mutable하며 어떤 value든지 가지고 있을 수 있다.&lt;/p&gt;
&lt;p&gt;조금 더 자세히 설명하자면&amp;nbsp;useRef는 current라는 property를 가지고 있는 plain object를 생성한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;useRef()를 사용하는 것과 {current: ...}라는 객체를 직접 만들어 쓰는 것의 차이는&lt;/p&gt;
&lt;p&gt;useRef는 우리에게 매 렌더링 시 항상 같은 reference의 객체를 제공해준다는 점이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래 예시처럼 useEffect hook 안에서도 current에 값을 자유롭게 할당할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1603635703366&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function Timer() {
  const intervalRef = useRef();

  useEffect(() =&amp;gt; {
    const id = setInterval(() =&amp;gt; {
      // ...
    });
    intervalRef.current = id;
    return () =&amp;gt; {
      clearInterval(intervalRef.current);
    };
  });

  // ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;(^ 공식 문서에 있는 예제)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;만약 위와 같이 useEffect hook안에서 interval을 세팅하고 clear한다면 ref를 굳이 사용할 필요가 없다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;그냥 useEffect hook안의 local variable인 id를 사용하면 되니까...!&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그런데 만약에 useEffect 바깥에 있는 이벤트 핸들러에서&lt;/p&gt;
&lt;p&gt;useEffect 안에 선언된 interval을 clear해주고 싶다면? ref가 아주 유용해진다.&lt;/p&gt;
&lt;pre id=&quot;code_1603635720022&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  // ...
  function handleCancelClick() {
    clearInterval(intervalRef.current);
  }
  // ...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;반드시 알고 있어야하는 점은 useRef는 current property의 값이 변경되더라도 &lt;/span&gt;컴포넌트를 re-render 시키지 않는다!&lt;/p&gt;
&lt;p&gt;만약에 값이 변경될 때마다 특정 코드를 실행시키고 싶다면 &lt;a href=&quot;https://reactjs.org/docs/refs-and-the-dom.html#callback-refs&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;callback ref&lt;/a&gt; &lt;span style=&quot;color: #333333;&quot;&gt;(주로 DOM node의 reference를 저장하기 위해 사용한다)&lt;/span&gt;라는 것을 활용해야 한다.&lt;/p&gt;</description>
      <category>Frontend</category>
      <category>EpicReact</category>
      <category>hooks</category>
      <category>react</category>
      <category>useRef</category>
      <category>useState</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/210</guid>
      <comments>https://im-developer.tistory.com/210#entry210comment</comments>
      <pubDate>Sun, 25 Oct 2020 23:57:52 +0900</pubDate>
    </item>
    <item>
      <title>알아두면 유용한 TypeScript의 Utility type 정리</title>
      <link>https://im-developer.tistory.com/209</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;469&quot; width=&quot;311&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFAPXB/btqE2tV9ALl/7RZaSZl9UD1jDNXDpQQy5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFAPXB/btqE2tV9ALl/7RZaSZl9UD1jDNXDpQQy5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFAPXB/btqE2tV9ALl/7RZaSZl9UD1jDNXDpQQy5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFAPXB%2FbtqE2tV9ALl%2F7RZaSZl9UD1jDNXDpQQy5k%2Fimg.png&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;469&quot; width=&quot;311&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;TypeScript의 Utility Type&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;업무할 때 종종 튀어 나오는 애들인데 내가 잘 모르는 것 같아서 한 번 쭉 정리해보았다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;정리하다보니 진작 알아뒀으면 유용하게 많이 썼을텐데 싶은 것들이 엄청 많다.&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;심심할 때마다 다시 읽어봐야겠다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Partial&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span&gt;주어진 Type의 모든 property를 optional로 세팅한 Type을 구성한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;다시 말해 주어진 Type의 모든 부분 집합 type을 return한다. 즉, 아래 Todo interface에 title과 description이 정의되어 있는데, Todo 중에서 일부 속성으로만 이루어진 type을 지정하고 싶을 때, Partial을 사용하면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;interface Todo {
    title: string;
    description: string;
}

function updateTodo(todo: Todo, fieldsToUpdate: Partial&amp;lt;Todo&amp;gt;) {
    return { ...todo, ...fieldsToUpdate };
}

const todo1 = {
    title: 'organize desk',
    description: 'clear clutter',
};

const todo2 = updateTodo(todo1, {
    description: 'throw out trash',
});&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Readonly&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Type의 모든 property 속성을 readonly로 세팅한다. 즉, 주어진 모든 property는 재할당 할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;interface Todo {
    title: string;
}

const todo: Readonly&amp;lt;Todo&amp;gt; = {
    title: 'Delete inactive users',
};

todo.title = 'Hello'; // Error: cannot reassign a readonly property&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 utility는 freeze된 객체의 속성을 재할당하거나 하여 런타임에서 에러가 나는 것을 막는 데 사용할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;function freeze&amp;lt;T&amp;gt;(obj: T): Readonly&amp;lt;T&amp;gt;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Record&amp;lt;K,T&amp;gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Record는 property Type을 K로, value Type을 T로 지정할 수 있다. 특정 property를 다른 type으로 매핑하고 싶을 때 유용하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;interface PageInfo {
    title: string;
}

type Page = 'home' | 'about' | 'contact';

const x: Record&amp;lt;Page, PageInfo&amp;gt; = {
    about: { title: 'about' },
    contact: { title: 'contact' },
    home: { title: 'home' },
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위 예시를 보면 더 이해하기 쉬운데, 즉, Record&amp;lt;Page, PageInfo&amp;gt;라고 지정한 객체 x는&lt;/p&gt;
&lt;p&gt;key type이 'home' | 'about' | 'contact'여야 하고,&lt;/p&gt;
&lt;p&gt;Value type이 PageInfo여야 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Pick&amp;lt;T,K&amp;gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;어떤 Type에서 특정 property들인 K를 뽑아서 구성한 타입을 지정한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;interface Todo {
    title: string;
    description: string;
    completed: boolean;
}

type TodoPreview = Pick&amp;lt;Todo, 'title' | 'completed'&amp;gt;;

const todo: TodoPreview = {
    title: 'Clean room',
    completed: false,
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Omit&amp;lt;T,K&amp;gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;Type에서 특정 property, K를 지운 Type을 구성한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;interface Todo {
    title: string;
    description: string;
    completed: boolean;
}

type TodoPreview = Omit&amp;lt;Todo, 'description'&amp;gt;;

const todo: TodoPreview = {
    title: 'Clean room',
    completed: false,
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Exclude&amp;lt;T,U&amp;gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;T에서 U에 지정한 모든 property를 제외하고 구성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;type T0 = Exclude&amp;lt;&quot;a&quot; | &quot;b&quot; | &quot;c&quot;, &quot;a&quot;&amp;gt;;  // &quot;b&quot; | &quot;c&quot;
type T1 = Exclude&amp;lt;&quot;a&quot; | &quot;b&quot; | &quot;c&quot;, &quot;a&quot; | &quot;b&quot;&amp;gt;;  // &quot;c&quot;
type T2 = Exclude&amp;lt;string | number | (() =&amp;gt; void), Function&amp;gt;;  // string | number&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Extract&amp;lt;T,U&amp;gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;T에서 U에 지정한 property만 추출하여 구성한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;type T0 = Extract&amp;lt;&quot;a&quot; | &quot;b&quot; | &quot;c&quot;, &quot;a&quot; | &quot;f&quot;&amp;gt;;  // &quot;a&quot;
type T1 = Extract&amp;lt;string | number | (() =&amp;gt; void), Function&amp;gt;;  // () =&amp;gt; void
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 id=&quot;nonnullablet&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;NonNullable&amp;lt;T&amp;gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;Type에서 undefined와 null을 제외한 Type만 남긴다.&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;type T0 = NonNullable&amp;lt;string | number | undefined&amp;gt;;  // string | number
type T1 = NonNullable&amp;lt;string[] | null | undefined&amp;gt;;  // string[]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Parameters&amp;lt;T&amp;gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;function type T의 parameter들의 Type을 tuple로 구성한다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;declare function f1(arg: { a: number, b: string }): void

type T0 = Parameters&amp;lt;() =&amp;gt; string&amp;gt;;  // []
type T1 = Parameters&amp;lt;(s: string) =&amp;gt; void&amp;gt;;  // [string]
type T2 = Parameters&amp;lt;(&amp;lt;T&amp;gt;(arg: T) =&amp;gt; T)&amp;gt;;  // [unknown]
type T4 = Parameters&amp;lt;typeof f1&amp;gt;;  // [{ a: number, b: string }]
type T5 = Parameters&amp;lt;any&amp;gt;;  // unknown[]
type T6 = Parameters&amp;lt;never&amp;gt;;  // never
type T7 = Parameters&amp;lt;string&amp;gt;;  // Error
type T8 = Parameters&amp;lt;Function&amp;gt;;  // Error&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;ConstructorParameters&amp;lt;T&amp;gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;생성자 함수(&lt;span style=&quot;color: #333333;&quot;&gt;constructor&lt;/span&gt;&lt;/span&gt;&amp;nbsp;function)의 모든 parameter들의 Type을 추출하여 tuple로 구성한다. (만약 T가 함수가 아니라면 &lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;type은&amp;nbsp;&lt;/span&gt;never가 된다.)&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;type T0 = ConstructorParameters&amp;lt;ErrorConstructor&amp;gt;;  // [(string | undefined)?]
type T1 = ConstructorParameters&amp;lt;FunctionConstructor&amp;gt;;  // string[]
type T2 = ConstructorParameters&amp;lt;RegExpConstructor&amp;gt;;  // [string, (string | undefined)?]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;ReturnType&amp;lt;T&amp;gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;function T의 return type을 구성한다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;declare function f1(): { a: number, b: string }

type T0 = ReturnType&amp;lt;() =&amp;gt; string&amp;gt;;  // string
type T1 = ReturnType&amp;lt;(s: string) =&amp;gt; void&amp;gt;;  // void
type T2 = ReturnType&amp;lt;(&amp;lt;T&amp;gt;() =&amp;gt; T)&amp;gt;;  // {}
type T3 = ReturnType&amp;lt;(&amp;lt;T extends U, U extends number[]&amp;gt;() =&amp;gt; T)&amp;gt;;  // number[]
type T4 = ReturnType&amp;lt;typeof f1&amp;gt;;  // { a: number, b: string }
type T5 = ReturnType&amp;lt;any&amp;gt;;  // any
type T6 = ReturnType&amp;lt;never&amp;gt;;  // any
type T7 = ReturnType&amp;lt;string&amp;gt;;  // Error
type T8 = ReturnType&amp;lt;Function&amp;gt;;  // Error
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;InstanceType&amp;lt;T&amp;gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;생성자 함수(&lt;span style=&quot;color: #333333;&quot;&gt;constructor function) T의 instance type을 구성한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;class C {
    x = 0;
    y = 0;
}

type T0 = InstanceType&amp;lt;typeof C&amp;gt;;  // C
type T1 = InstanceType&amp;lt;any&amp;gt;;  // any
type T2 = InstanceType&amp;lt;never&amp;gt;;  // any
type T3 = InstanceType&amp;lt;string&amp;gt;;  // Error
type T4 = InstanceType&amp;lt;Function&amp;gt;;  // Error
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Required&amp;lt;T&amp;gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;모든 T의 property들을 optional이 아닌 required로 세팅하여 구성한다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;interface Props {
    a?: number;
    b?: string;
};

const obj: Props = { a: 5 }; // OK

const obj2: Required&amp;lt;Props&amp;gt; = { a: 5 }; // Error: property 'b' missing
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend</category>
      <category>JavaScript</category>
      <category>TypeScript</category>
      <category>자바스크립트</category>
      <category>타입스크립트</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/209</guid>
      <comments>https://im-developer.tistory.com/209#entry209comment</comments>
      <pubDate>Sun, 21 Jun 2020 22:11:02 +0900</pubDate>
    </item>
    <item>
      <title>[Svelte] Svelte 기초 - Svelte로 Form 다루기 / Custom Event Dispatch하기</title>
      <link>https://im-developer.tistory.com/208</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Form 다루기&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;이제 svelte에서 input 요소들을 어떻게 다루는 지 살펴보도록 하자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- Input Text&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;i&gt;- AddPersonForm.svelte&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1592738972080&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  let name;
  let hairColor;
  let age;

  const handleSubmit = () =&amp;gt; {
    console.log(name, hairColor, age);
  };
&amp;lt;/script&amp;gt;

&amp;lt;h3&amp;gt;Add a new person&amp;lt;/h3&amp;gt;
&amp;lt;form on:submit|preventDefault={handleSubmit}&amp;gt;
  &amp;lt;input type=&quot;text&quot; placeholder=&quot;name&quot; bind:value={name} /&amp;gt;
  &amp;lt;input type=&quot;text&quot; placeholder=&quot;hair color&quot; bind:value={hairColor} /&amp;gt;
  &amp;lt;input type=&quot;number&quot; placeholder=&quot;age&quot; bind:value={age} /&amp;gt;
  &amp;lt;button&amp;gt;Add Person&amp;lt;/button&amp;gt;
&amp;lt;/form&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;먼저 form 태그에 3가지 input 태그를 만들고 각 input에 변수를 binding했다.&lt;/p&gt;
&lt;p&gt;그리고 handleSubmit 함수를 form 태그에 submit 이벤트로 등록해주었다.&lt;/p&gt;
&lt;p&gt;이 때 지난 번에 소개한 preventDefault라는 Event modifier를 이용하여 form 태그의 기본 동작을 막아주었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 form 태그 안에 있는 button을 클릭하면 submit 이벤트가 발생하여 handleSubmit 함수가 실행될 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;i&gt;(bind:value는 아래 글에서 자세히 설명했으므로 설명은 pass~)&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://im-developer.tistory.com/205&quot;&gt;https://im-developer.tistory.com/205&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1592740833898&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[Svelte] Svelte 기초 - Data binding과 Reactive Values&quot; data-og-description=&quot;Input &amp;amp; Data binding Hello {name}! {color} jean! update jean color 이렇게 color 변수를 선언한 후에 아래 {color} jean! 을 출력하면 화면에 black jean!이라는 단어가 출력된다. ({}는 동적인 값을 넣을 때..&quot; data-og-host=&quot;im-developer.tistory.com&quot; data-og-source-url=&quot;https://im-developer.tistory.com/205&quot; data-og-url=&quot;https://im-developer.tistory.com/205&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/YQiHS/hyGtY1MoRm/Q27Iw8r0LSQpFZ2rpdNpc0/img.gif?width=654&amp;amp;height=222&amp;amp;face=0_0_654_222,https://scrap.kakaocdn.net/dn/wdBOj/hyGtRn4aG7/C7gE05e2yWPMGfhnRFBZS0/img.gif?width=654&amp;amp;height=222&amp;amp;face=0_0_654_222,https://scrap.kakaocdn.net/dn/gqHsP/hyGtS8nHGp/5yPplzPQ2Bq1zPWnucCQZk/img.png?width=1316&amp;amp;height=724&amp;amp;face=0_0_1316_724&quot;&gt;&lt;a href=&quot;https://im-developer.tistory.com/205&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://im-developer.tistory.com/205&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/YQiHS/hyGtY1MoRm/Q27Iw8r0LSQpFZ2rpdNpc0/img.gif?width=654&amp;amp;height=222&amp;amp;face=0_0_654_222,https://scrap.kakaocdn.net/dn/wdBOj/hyGtRn4aG7/C7gE05e2yWPMGfhnRFBZS0/img.gif?width=654&amp;amp;height=222&amp;amp;face=0_0_654_222,https://scrap.kakaocdn.net/dn/gqHsP/hyGtS8nHGp/5yPplzPQ2Bq1zPWnucCQZk/img.png?width=1316&amp;amp;height=724&amp;amp;face=0_0_1316_724');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;[Svelte] Svelte 기초 - Data binding과 Reactive Values&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Input &amp;amp; Data binding Hello {name}! {color} jean! update jean color 이렇게 color 변수를 선언한 후에 아래 {color} jean! 을 출력하면 화면에 black jean!이라는 단어가 출력된다. ({}는 동적인 값을 넣을 때..&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;im-developer.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- Checkbox&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1592739033062&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  let name;
  let age;
  let fighting = false;
  let sneaking = false;
  let running = false;
&amp;lt;/script&amp;gt;

&amp;lt;h3&amp;gt;Add a new person&amp;lt;/h3&amp;gt;
&amp;lt;form on:submit|preventDefault={handleSubmit}&amp;gt;
  &amp;lt;input type=&quot;text&quot; placeholder=&quot;name&quot; bind:value={name} /&amp;gt;
  &amp;lt;input type=&quot;number&quot; placeholder=&quot;age&quot; bind:value={age} /&amp;gt;
  &amp;lt;h4&amp;gt;Skills:&amp;lt;/h4&amp;gt;
  &amp;lt;label&amp;gt;
    &amp;lt;input type=&quot;checkbox&quot; bind:checked={fighting} /&amp;gt;fighting&amp;lt;br /&amp;gt;
  &amp;lt;/label&amp;gt;
  &amp;lt;label&amp;gt;
    &amp;lt;input type=&quot;checkbox&quot; bind:checked={sneaking} /&amp;gt;sneaking&amp;lt;br /&amp;gt;
  &amp;lt;/label&amp;gt;
  &amp;lt;label&amp;gt;
    &amp;lt;input type=&quot;checkbox&quot;  bind:checked={running}/&amp;gt;running&amp;lt;br /&amp;gt;
  &amp;lt;/label&amp;gt;
  &amp;lt;button&amp;gt;Add Person&amp;lt;/button&amp;gt;
&amp;lt;/form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;input text나 number는 bind:value를 이용해 바인딩했으나&lt;/p&gt;
&lt;p&gt;문제는 checkbox와 같은 태그이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;한 가지 방법은 각 checkbox 별로 boolean 값을 가지는 변수를 만들고&amp;nbsp;&lt;/p&gt;
&lt;p&gt;checked 속성을 bind해주는 방법이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그러나 이렇게 했을 경우 checkbox 개수만큼 변수의 개수가 늘어나 코드가 지저분해 진다는 단점이 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1592739067368&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  let name;
  let age;
  let skills = [];
  
  const handleSubmit = () =&amp;gt; {
    console.log(skills);
  };
&amp;lt;/script&amp;gt;

&amp;lt;h3&amp;gt;Add a new person&amp;lt;/h3&amp;gt;
&amp;lt;form on:submit|preventDefault={handleSubmit}&amp;gt;
  &amp;lt;input type=&quot;text&quot; placeholder=&quot;name&quot; bind:value={name} /&amp;gt;
  &amp;lt;input type=&quot;number&quot; placeholder=&quot;age&quot; bind:value={age} /&amp;gt;
  &amp;lt;h4&amp;gt;Skills:&amp;lt;/h4&amp;gt;
  &amp;lt;label&amp;gt;
    &amp;lt;input type=&quot;checkbox&quot; bind:group={skills} value=&quot;fighting&quot; /&amp;gt;fighting&amp;lt;br /&amp;gt;
  &amp;lt;/label&amp;gt;
  &amp;lt;label&amp;gt;
    &amp;lt;input type=&quot;checkbox&quot; bind:group={skills} value=&quot;sneaking&quot; /&amp;gt;sneaking&amp;lt;br /&amp;gt;
  &amp;lt;/label&amp;gt;
  &amp;lt;label&amp;gt;
    &amp;lt;input type=&quot;checkbox&quot; bind:group={skills} value=&quot;running&quot; /&amp;gt;running&amp;lt;br /&amp;gt;
  &amp;lt;/label&amp;gt;
  &amp;lt;button&amp;gt;Add Person&amp;lt;/button&amp;gt;
&amp;lt;/form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 경우에는 이렇게 배열을 먼저 선언하고,&lt;/p&gt;
&lt;p&gt;각 checkbox에 &lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;bind:group&lt;/b&gt;&lt;/span&gt;을 이용하여 배열을 바인딩하면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;502&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFlRHS/btqE1DymxO6/k2GGgBr4OQkkzqoZf4c7aK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFlRHS/btqE1DymxO6/k2GGgBr4OQkkzqoZf4c7aK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFlRHS/btqE1DymxO6/k2GGgBr4OQkkzqoZf4c7aK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFlRHS%2FbtqE1DymxO6%2Fk2GGgBr4OQkkzqoZf4c7aK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;502&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 Form에서 fighting과 sneaking을 체크한 후에 버튼을 클릭하고 console 창을 확인해보면,&lt;/p&gt;
&lt;p&gt;이렇게 skills 배열에 fighting과 sneaking이 들어와있는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pmZQH/btqE0xM26OH/3WjNuRiMmrwfuwstJ0Wzj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pmZQH/btqE0xM26OH/3WjNuRiMmrwfuwstJ0Wzj0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pmZQH/btqE0xM26OH/3WjNuRiMmrwfuwstJ0Wzj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpmZQH%2FbtqE0xM26OH%2F3WjNuRiMmrwfuwstJ0Wzj0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- Select&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1592739083513&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  let name;
  let hairColor;
  let age;
  let skills = [];

  const handleSubmit = () =&amp;gt; {
    console.log(name, hairColor, age, skills);
  };
&amp;lt;/script&amp;gt;

&amp;lt;h3&amp;gt;Add a new person&amp;lt;/h3&amp;gt;
&amp;lt;form on:submit|preventDefault={handleSubmit}&amp;gt;
  &amp;lt;input type=&quot;text&quot; placeholder=&quot;name&quot; bind:value={name} /&amp;gt;
  &amp;lt;input type=&quot;number&quot; placeholder=&quot;age&quot; bind:value={age} /&amp;gt;
  &amp;lt;h4&amp;gt;Skills:&amp;lt;/h4&amp;gt;
  &amp;lt;label&amp;gt;
    &amp;lt;input type=&quot;checkbox&quot; bind:group={skills} value=&quot;fighting&quot; /&amp;gt;fighting&amp;lt;br /&amp;gt;
  &amp;lt;/label&amp;gt;
  &amp;lt;label&amp;gt;
    &amp;lt;input type=&quot;checkbox&quot; bind:group={skills} value=&quot;sneaking&quot; /&amp;gt;sneaking&amp;lt;br /&amp;gt;
  &amp;lt;/label&amp;gt;
  &amp;lt;label&amp;gt;
    &amp;lt;input type=&quot;checkbox&quot; bind:group={skills} value=&quot;running&quot; /&amp;gt;running&amp;lt;br /&amp;gt;
  &amp;lt;/label&amp;gt;
  &amp;lt;h4&amp;gt;hairColor:&amp;lt;/h4&amp;gt;
  &amp;lt;select bind:value={hairColor}&amp;gt;
    &amp;lt;option value=&quot;black&quot;&amp;gt;black&amp;lt;/option&amp;gt;
    &amp;lt;option value=&quot;orange&quot;&amp;gt;orange&amp;lt;/option&amp;gt;
    &amp;lt;option value=&quot;brown&quot;&amp;gt;brown&amp;lt;/option&amp;gt;
    &amp;lt;option value=&quot;silver&quot;&amp;gt;silver&amp;lt;/option&amp;gt;
  &amp;lt;/select&amp;gt;
  &amp;lt;button&amp;gt;Add Person&amp;lt;/button&amp;gt;
&amp;lt;/form&amp;gt;

&amp;lt;style&amp;gt;&amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;select 태그에도 bind:value를 사용하여 바인딩할 수 있다.&lt;/p&gt;
&lt;p&gt;이 경우에 바인딩된 hairColor 변수에는 option 태그의 value 값이 들어간다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Custom Event Dispatching&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;만약에 AddPersonForm 내부에서 submit 이벤트가 실행됐을 때,&lt;/p&gt;
&lt;p&gt;각 변수에 저장된 input 값들이 AddPersonForm 상위 컴포넌트에 선언된&lt;/p&gt;
&lt;p&gt;데이터에 추가되려면 어떻게 할 수 있을까?&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Event forwarding&lt;/b&gt;&lt;/span&gt;을 떠올릴 수도 있겠지만, Event forwarding의 경우&lt;/p&gt;
&lt;p&gt;하위 컴포넌트에 이벤트 handler가 등록되어 있지 않을 때 부모 컴포넌트의 handler를 그대로 가져와 사용한다는 개념으로,&lt;/p&gt;
&lt;p&gt;하위 컴포넌트에서 특정 데이터를 상위로 넘겨 줄 방법은 없다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이처럼 하위에서 상위로 데이터 전달을 해야 하는 경우&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;Custom Event dispatcher&lt;/b&gt;&lt;/span&gt;를 이용할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;JavaScript의 Custom Event 기능을 이용한 기능인데, svelte에서 쉽게 사용하도록 여러 메소드를 제공한다.&lt;/p&gt;
&lt;p&gt;(Custom Event는 예전에 아래 포스팅에서 한 번 다뤄본 적이 있다.)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://im-developer.tistory.com/190?category=846746&quot;&gt;https://im-developer.tistory.com/190?category=846746&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1592740273819&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[JS/CustomEvent] 자바스크립트, 커스텀 이벤트 생성하기&quot; data-og-description=&quot;회사에서 이번에 새로 맡은 업무를 기획중인데, Native 앱이 웹뷰에 뭔가를 요청할때마다 웹뷰에서 이벤트가 trigger되도록 만들어야해서 어떻게 할지 엄청 고민을 많이 했었다. 웹뷰가 Native Client��&quot; data-og-host=&quot;im-developer.tistory.com&quot; data-og-source-url=&quot;https://im-developer.tistory.com/190?category=846746&quot; data-og-url=&quot;https://im-developer.tistory.com/190&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dCh7OX/hyGt0rL1YK/EoDiGj00cWnGXUsncvBty1/img.png?width=480&amp;amp;height=480&amp;amp;face=0_0_480_480,https://scrap.kakaocdn.net/dn/dys30B/hyGtTF97UH/1f74JPmHfFcwoRdJTJGPgk/img.png?width=480&amp;amp;height=480&amp;amp;face=0_0_480_480,https://scrap.kakaocdn.net/dn/bRPY2j/hyGvA54Ji7/PAu5RFUx36453lkKfmwzt0/img.png?width=480&amp;amp;height=480&amp;amp;face=0_0_480_480&quot;&gt;&lt;a href=&quot;https://im-developer.tistory.com/190?category=846746&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://im-developer.tistory.com/190?category=846746&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dCh7OX/hyGt0rL1YK/EoDiGj00cWnGXUsncvBty1/img.png?width=480&amp;amp;height=480&amp;amp;face=0_0_480_480,https://scrap.kakaocdn.net/dn/dys30B/hyGtTF97UH/1f74JPmHfFcwoRdJTJGPgk/img.png?width=480&amp;amp;height=480&amp;amp;face=0_0_480_480,https://scrap.kakaocdn.net/dn/bRPY2j/hyGvA54Ji7/PAu5RFUx36453lkKfmwzt0/img.png?width=480&amp;amp;height=480&amp;amp;face=0_0_480_480');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;[JS/CustomEvent] 자바스크립트, 커스텀 이벤트 생성하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;회사에서 이번에 새로 맡은 업무를 기획중인데, Native 앱이 웹뷰에 뭔가를 요청할때마다 웹뷰에서 이벤트가 trigger되도록 만들어야해서 어떻게 할지 엄청 고민을 많이 했었다. 웹뷰가 Native Client��&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;im-developer.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1592739140704&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  import { createEventDispatcher } from 'svelte';

  let dispatch = createEventDispatcher();

  let name;
  let hairColor;
  let age;
  let skills = [];

  const handleSubmit = () =&amp;gt; {
    const person = {
      name,
      hairColor,
      age,
      skills,
      id: Math.random()
    };

    dispatch('addPerson', person);
  };
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;일단 event를 생성하려면 svelt에서 제공하는 '&lt;b&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;createEventDispatcher&lt;/span&gt;&lt;/b&gt;' 함수를 import한다.&lt;/p&gt;
&lt;p&gt;그리고 이 함수를 실행하여 dispatch라는 함수를 생성한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 함수는 &lt;u&gt;&lt;b&gt;2가지 parameter&lt;/b&gt;&lt;/u&gt;를 넣을 수 있는데,&lt;/p&gt;
&lt;p&gt;하나는 string으로&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt; custom event의 이름&lt;/b&gt;&lt;/span&gt;이 된다.&lt;/p&gt;
&lt;p&gt;두 번째 parameter로는 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Custom event handler에 넘겨 줄 데이터&lt;/b&gt;&lt;/span&gt;를 넣을 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제, handlerSubmit이 실행되면, addPerson이라는 이름의 Custom Event가 생성된 후,&lt;/p&gt;
&lt;p&gt;person 이라는 데이터와 함께 이벤트가 dispatch된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1592739184050&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  import Modal from './Modal.svelte';
  import AddPersonForm from './AddPersonForm.svelte';

  let showModal = false;

  const toggleModal = () =&amp;gt; {
    showModal = !showModal;
  };

  let people = [
    { name: 'juno', hairColor: 'brown', age: 25, id: 1 },
    { name: 'mario', hairColor: 'black', age: 45, id: 2 },
    { name: 'lily', hairColor: 'pink', age: 35, id: 3 },
  ];

  const addPerson = ({ detail: person }) =&amp;gt; {
    people = [person, ...people];
    showModal = false;
  };
&amp;lt;/script&amp;gt;

&amp;lt;Modal {showModal} on:click={toggleModal}&amp;gt;
  &amp;lt;AddPersonForm on:addPerson={addPerson} /&amp;gt;
&amp;lt;/Modal&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그러면 이제 AddPersonForm 컴포넌트를 사용하는 곳에서&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아까 지정한 Custom Event의 name을 그대로 사용하여 &lt;b&gt;on:&lt;/b&gt; 과 함께 어떤 특정 함수를 이벤트 handler로 등록할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이벤트 handler로 등록된 함수는 event 객체를 파라미터로 받는데,&lt;/p&gt;
&lt;p&gt;그 객체의 &lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;detail&lt;/b&gt;&lt;/span&gt; 속성으로 person 데이터가 들어오게 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 이 person 데이터를 이용해, 부모 컴포넌트에 선언 된 데이터에&amp;nbsp;&lt;/p&gt;
&lt;p&gt;새로운 데이터를 추가, 삭제, 수정 등 여러 조작을 할 수 있게 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;i&gt;여기서 꼭 주의해야 하는 점은 svelte에서는 어떤 변수가 재할당될 때&lt;br /&gt;&lt;/i&gt;&lt;i&gt;그 변수와 연관된 모든 dom이 reactive하게 반응한다.&lt;br /&gt;&lt;/i&gt;&lt;i&gt;따라서 배열이나 객체 변수가 있다면 해당 배열이나 객체를 복사한 후,&lt;br /&gt;&lt;/i&gt;&lt;i&gt;재할당해주는 방식으로 업데이트해야만 reactive한 app을 개발할 수 있다.&lt;/i&gt;&lt;/blockquote&gt;</description>
      <category>Frontend</category>
      <category>JavaScript</category>
      <category>svelte</category>
      <category>스벨트</category>
      <category>자바스크립트</category>
      <category>코딩</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/208</guid>
      <comments>https://im-developer.tistory.com/208#entry208comment</comments>
      <pubDate>Sun, 21 Jun 2020 21:01:45 +0900</pubDate>
    </item>
    <item>
      <title>[Svelte] Svelte 기초 - Svelte로 Modal 만들기 (Conditional Styles / Event Forwarding / Props / Event Modifiers / Slots)</title>
      <link>https://im-developer.tistory.com/207</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;2020-06-21 20.26.17.gif&quot; data-origin-width=&quot;1320&quot; data-origin-height=&quot;878&quot; width=&quot;592&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NLmtx/btqE2um97XK/I52ksZ0y6hjK5sEQwR7xF1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NLmtx/btqE2um97XK/I52ksZ0y6hjK5sEQwR7xF1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NLmtx/btqE2um97XK/I52ksZ0y6hjK5sEQwR7xF1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/NLmtx/btqE2um97XK/I52ksZ0y6hjK5sEQwR7xF1/img.gif&quot; data-filename=&quot;2020-06-21 20.26.17.gif&quot; data-origin-width=&quot;1320&quot; data-origin-height=&quot;878&quot; width=&quot;592&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;br /&gt;이제 Svelte로 Modal을 만들어보려고 한다.&lt;/p&gt;
&lt;p&gt;Modal 컴포넌트를 만들어서 해당 컴포넌트를 특정 condition에 의해 보여줬다 안보여줬다 toggle할 수 있도록 만들어야 할 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;일단 App.svelte와 동일한 폴더에 Modal.svelte를 만들어보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Conditional Styles&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1592729985799&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  let showModal = true;
  let isPromo = true;
&amp;lt;/script&amp;gt;

{#if showModal}
  &amp;lt;div class=&quot;backdrop&quot; class:promo={isPromo}&amp;gt;
    &amp;lt;div class=&quot;modal&quot;&amp;gt;
      &amp;lt;p&amp;gt;Sign up for offers!&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
{/if}

&amp;lt;style&amp;gt;
  .backdrop {
    width: 100%;
    height: 100%;
    position: fixed;
    top: 0;
    left: 0;
    background: rgba(0, 0, 0, 0.8);
  }

  .modal {
    padding: 10px;
    border-radius: 10px;
    max-width: 400px;
    margin: 10% auto;
    text-align: center;
    background: white;
  }

  .promo .modal {
    background: crimson;
    color: white;
  }
&amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위 Modal 컴포넌트는 showModal 변수가 &lt;b&gt;true&lt;/b&gt;일 때만 보여진다.&lt;/p&gt;
&lt;p&gt;그리고 &lt;b&gt;class:promo={ isPromo }&lt;/b&gt;는 바로 conditional styles이다.&lt;/p&gt;
&lt;p&gt;즉, 조건에 따라 class name을 줬다 안줬다 할 수 있는 기능이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;class:promo={ isPromo } 라고 하면 isPromo 변수가 true일 때만 promo라는 class name이 적용된다는 뜻이다.&lt;/p&gt;
&lt;p&gt;(물론 isPromo 대신 다른 조건문이 들어갈 수도 있다.)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Props&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1592736579209&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  export let message = 'default value';
  export let showModal = true;
  export let isPromo = true;
&amp;lt;/script&amp;gt;

{#if showModal}
  &amp;lt;div class=&quot;backdrop&quot; class:promo={isPromo}&amp;gt;
    &amp;lt;div class=&quot;modal&quot;&amp;gt;
      &amp;lt;p&amp;gt;{message}&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
{/if}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아까의 Modal은 내부 텍스트가 정적인 텍스트로 정해져 있었기 때문에 재사용이 불가능했다.&lt;/p&gt;
&lt;p&gt;그러나 message 변수를 만들고 이 message를 출력하게 함으로써 재사용 가능한 모달 컴포넌트로 만들 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;물론 이 message는 외부에서 주입할 수 있어야할 것이다.&lt;/p&gt;
&lt;p&gt;변수 선언 앞에 export를 붙이면 외부에서 주입할 수 있는 prop이 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;만약에 변수에 어떤 값을 할당했다면, 이 것은 default value로 작동한다.&lt;/p&gt;
&lt;p&gt;즉, 외부에서 어떤 값도 주입하지 않았을 때 기본 값으로 적용된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1592736745053&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  import Modal from './Modal.svelte';

  let showModal = true;
&amp;lt;/script&amp;gt;

&amp;lt;Modal message='I am a prop value' {showModal} /&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 App.svelte에서 Modal 컴포넌트를 import한 후,&lt;/p&gt;
&lt;p&gt;showModal과 message를 prop으로 전달하면 Modal 컴포넌트가 보여진다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;528&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdcAlo/btqE2ljENP0/w0BUdGErCeIBuBTMyOcBTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdcAlo/btqE2ljENP0/w0BUdGErCeIBuBTMyOcBTk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdcAlo/btqE2ljENP0/w0BUdGErCeIBuBTMyOcBTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdcAlo%2FbtqE2ljENP0%2Fw0BUdGErCeIBuBTMyOcBTk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;528&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이는 showModal 변수를 true로 설정했기 때문이다.&lt;/p&gt;
&lt;p&gt;이제 button을 만들고 button을 클릭했을 때만 Modal이 보여지도록 설정해보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Event Forwarding&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1592736959164&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  import Modal from './Modal.svelte';

  let showModal = true;
  
  const toggleModal = () =&amp;gt; {
    showModal = !showModal;
  };
&amp;lt;/script&amp;gt;

&amp;lt;Modal message='I am a prop value' {showModal} /&amp;gt;
&amp;lt;button on:click={toggleModal}&amp;gt;Open Modal&amp;lt;/button&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Modal 컴포넌트를 보여주고 안보여주고 여부는 showModal 변수에 의해서 결정되고,&lt;/p&gt;
&lt;p&gt;이 변수는 Modal 컴포넌트에 prop으로 주입된다.&lt;/p&gt;
&lt;p&gt;문제는 이 변수는 Modal 컴포넌트 내부에서 제어할 수 없다는 점이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;따라서 위와 같이 App.svelte에서 showModal을 toggle하는 함수를 만들어 준 다음&lt;/p&gt;
&lt;p&gt;Modal을 여는 버튼에 click 이벤트로 등록해주어야 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;문제는 button을 클릭하여 Modal 창이 열린 후 다시 Modal을 닫을 수가 없다는 점이다.&lt;/p&gt;
&lt;p&gt;왜냐면 Modal이 화면 전체를 가려버리기 때문에 Modal 내부 요소에 다시 toggleModal 함수를 등록해주어야하기 때문이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 때 사용할 수 있는 방법이 바로 Event Forwarding이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1592737275057&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  import Modal from './Modal.svelte';

  let showModal = true;
  
  const toggleModal = () =&amp;gt; {
    showModal = !showModal;
  };
&amp;lt;/script&amp;gt;

&amp;lt;Modal message='I am a prop value' {showModal} on:click={toggleModal} /&amp;gt;
&amp;lt;button on:click={toggleModal}&amp;gt;Open Modal&amp;lt;/button&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;일단 Modal 컴포넌트에 click 이벤트로 toggleModal 함수를 등록한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1592737310692&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  export let message = 'default value';
  export let isPromo = false;
  export let showModal = false;
&amp;lt;/script&amp;gt;

{#if showModal}
  &amp;lt;div class=&quot;backdrop&quot; class:promo={isPromo} on:click&amp;gt;
    &amp;lt;div class=&quot;modal&quot;&amp;gt;
      &amp;lt;p&amp;gt;{message}&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
{/if}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그리고 Modal 컴포넌트 내부에서 .backdrop div에 on:click을 등록한다.&lt;/p&gt;
&lt;p&gt;이 때 값을 넣지 않고 on:click만 적는다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이렇게 이벤트를 등록했으나 할당된 함수가 없는 경우,&lt;/p&gt;
&lt;p&gt;스벨트 컴포넌트는 상위의 부모 컴포넌트에 등록된 on:click 함수를 그대로 사용한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;2020-06-21 20.03.50.gif&quot; data-origin-width=&quot;1320&quot; data-origin-height=&quot;878&quot; width=&quot;591&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dnIFHh/btqE0OOi1XN/zJqOqXlWT9j4or1HCezeKk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dnIFHh/btqE0OOi1XN/zJqOqXlWT9j4or1HCezeKk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dnIFHh/btqE0OOi1XN/zJqOqXlWT9j4or1HCezeKk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/dnIFHh/btqE0OOi1XN/zJqOqXlWT9j4or1HCezeKk/img.gif&quot; data-filename=&quot;2020-06-21 20.03.50.gif&quot; data-origin-width=&quot;1320&quot; data-origin-height=&quot;878&quot; width=&quot;591&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Event Modifiers&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;위 방법의 단점은 Modal 컴포넌트의 backdrop 영역(: 반투명한 검정색 영역)을 클릭했을 때만 Modal이 닫혀야하는데,&lt;/p&gt;
&lt;p&gt;모달의 하얀 부분을 클릭해도 닫힌다는 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 것을 해결하기 위해서는&lt;b&gt; Event modifiers&lt;/b&gt;를 이용하면 된다.&lt;/p&gt;
&lt;p&gt;Event Modifiers에는 다음과 같은 6가지 종류가 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;preventDefault&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&amp;mdash; handler 함수를 실행하기 전에 event.preventDefault()를 호출한다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;stopPropagation&lt;/b&gt;&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; event가 다음 element 요소로 전파되는 것을 막기 위해 event.stopPropagation()을 호출한다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;passive&lt;/b&gt;&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; touch/wheel 이벤트에서 scrolling performance를 향상시킨다. (Svelte는 이렇게 해도 안전한 경우에 자동으로 이 기능을 추가할 것이다.)&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;capture&lt;/b&gt;&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; bubbling 단계 대신 capture 단계에 handler를 트리거한다.&amp;nbsp;(&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture&quot;&gt;MDN docs&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;once&lt;/b&gt;&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; 한 번 handler가 실행되면 이벤트를 바로 삭제한다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #5f6d2b;&quot;&gt;&lt;b&gt;self&lt;/b&gt;&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;mdash; element가 event.target인 경우에만 handler를 트리거한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;굉장히 유용한 기능이 많다. 하여튼 위 Modal 케이스에 적용하면 딱 좋은 Event modifier가 바로 self이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1592737966338&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  export let message = 'default value';
  export let isPromo = false;
  export let showModal = false;
&amp;lt;/script&amp;gt;

{#if showModal}
  &amp;lt;div class=&quot;backdrop&quot; class:promo={isPromo} on:click|self&amp;gt;
    &amp;lt;div class=&quot;modal&quot;&amp;gt;
      &amp;lt;p&amp;gt;{message}&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
{/if}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;즉, 위와 같이 Modal 컴포넌트의 on:click 옆에 |self를 추가하면&amp;nbsp;&lt;/p&gt;
&lt;p&gt;정확히 .backdrop을 클릭했을 때만 이벤트가 트리거되어 Modal이 닫힌다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Slots&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;React에서는 children이라는 개념이 있다.&lt;/p&gt;
&lt;p&gt;즉, 버튼 컴포넌트가 있다고 했을 때, &amp;lt;Button&amp;gt;&amp;lt;/Button&amp;gt;이라고 사용한 후,&lt;/p&gt;
&lt;p&gt;버튼 컴포넌트 사이에 다른 dom element를 넣으면&lt;/p&gt;
&lt;p&gt;버튼 컴포넌트 내부에서 children을 사용한 부분에 해당 dom element가 들어가게 된다.&lt;/p&gt;
&lt;p&gt;그리고 이는 컴포넌트를 재사용하는 데 매우 유용하게 쓰인다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;svelte에도 굉장히 비슷한 개념이 있는데, 바로 slot이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- Unnamed slot&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;i&gt;App.svelte&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1592738319656&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Modal {showModal} on:click={toggleModal}&amp;gt;
  &amp;lt;h3&amp;gt;Add a new person&amp;lt;/h3&amp;gt;
  &amp;lt;form&amp;gt;
    &amp;lt;input type=&quot;text&quot; placeholder=&quot;name&quot; /&amp;gt;
    &amp;lt;input type=&quot;text&quot; placeholder=&quot;hair color&quot; /&amp;gt;
    &amp;lt;button&amp;gt;Add Person&amp;lt;/button&amp;gt;
  &amp;lt;/form&amp;gt;
&amp;lt;/Modal&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;i&gt;Modal.svelte&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1592738375313&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{#if showModal}
  &amp;lt;div class=&quot;backdrop&quot; class:promo={isPromo} on:click|self&amp;gt;
    &amp;lt;div class=&quot;modal&quot;&amp;gt;
      &amp;lt;slot /&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
{/if}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;위와 같이 Modal 사이에 넣은 dom들은 Modal 컴포넌트에서 &lt;b&gt;&amp;lt;slot /&amp;gt;&lt;/b&gt;이라고 선언된 자리에 들어간다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;- Named slot&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;i&gt;App.svelte&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1592738487410&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Modal {showModal} on:click={toggleModal}&amp;gt;
  &amp;lt;form&amp;gt;
    &amp;lt;input type=&quot;text&quot; placeholder=&quot;name&quot; /&amp;gt;
    &amp;lt;input type=&quot;text&quot; placeholder=&quot;hair color&quot; /&amp;gt;
    &amp;lt;button&amp;gt;Add Person&amp;lt;/button&amp;gt;
  &amp;lt;/form&amp;gt;
  &amp;lt;div slot=&quot;title&quot;&amp;gt;
    &amp;lt;h3&amp;gt;Add a new person&amp;lt;/h3&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/Modal&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;u&gt;Modal.svelte&lt;/u&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1592738525827&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{#if showModal}
  &amp;lt;div class=&quot;backdrop&quot; class:promo={isPromo} on:click|self&amp;gt;
    &amp;lt;div class=&quot;modal&quot;&amp;gt;
      &amp;lt;slot name=&quot;title&quot; /&amp;gt;
      &amp;lt;slot /&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
{/if}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;named slot은 dom에 slot이라는 속성을 추가한 다음 값을 할당해준 경우를 말한다.&lt;/p&gt;
&lt;p&gt;slot으로 지정해 준 string이 바로 slot의 name이 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 slot은 &lt;b&gt;&amp;lt;slot name=&quot;slot-name&quot; /&amp;gt;&lt;/b&gt;과 같은 형태로 사용된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend</category>
      <category>JavaScript</category>
      <category>svelte</category>
      <category>스벨트</category>
      <category>자바스크립트</category>
      <category>코딩</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/207</guid>
      <comments>https://im-developer.tistory.com/207#entry207comment</comments>
      <pubDate>Sun, 21 Jun 2020 20:27:22 +0900</pubDate>
    </item>
    <item>
      <title>[Svelte] Svelte 기초 - Loop / If-else blocks / Inline Event Handlers</title>
      <link>https://im-developer.tistory.com/206</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;2020-06-21 17.54.57.gif&quot; data-origin-width=&quot;1320&quot; data-origin-height=&quot;878&quot; width=&quot;506&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmENnp/btqE1uIkHb5/3BEpadwxJKxrEPEY0Q0ZgK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmENnp/btqE1uIkHb5/3BEpadwxJKxrEPEY0Q0ZgK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmENnp/btqE1uIkHb5/3BEpadwxJKxrEPEY0Q0ZgK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bmENnp/btqE1uIkHb5/3BEpadwxJKxrEPEY0Q0ZgK/img.gif&quot; data-filename=&quot;2020-06-21 17.54.57.gif&quot; data-origin-width=&quot;1320&quot; data-origin-height=&quot;878&quot; width=&quot;506&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Loops&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1592728705244&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  let people = [
    { name: 'juno', hairColor: 'brown', age: 25, id: 1 },
    { name: 'mario', hairColor: 'black', age: 45, id: 2 },
    { name: 'lily', hairColor: 'pink', age: 35, id: 3 },
  ];
&amp;lt;/script&amp;gt;

&amp;lt;main&amp;gt;
  {#each people as person (person.id)}
    &amp;lt;div&amp;gt;
      &amp;lt;h4&amp;gt;{person.name}&amp;lt;/h4&amp;gt;
      &amp;lt;p&amp;gt;{person.age} years old, {person.hairColor} hair&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  {:else}
    &amp;lt;p&amp;gt;There are no people to show...&amp;lt;/p&amp;gt;
  {/each}
&amp;lt;/main&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;svelte에서 loop을 돌며 dom 을 그릴 때는 each block을 사용한다.&lt;/p&gt;
&lt;p&gt;중간에 else block을 이용해서 만약에 배열에 데이터가 하나도 없을 경우 보여 줄 dom을 결정할 수도 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1592728874465&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{#each people as person, index (person.id)}
  &amp;lt;div&amp;gt;{index + 1} : {person.name}&amp;lt;/div&amp;gt;
{/each}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 index를 가져올 수도 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1592728918839&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{#each people as { name }, index (person.id)}
  &amp;lt;div&amp;gt;{index + 1} : {name}&amp;lt;/div&amp;gt;
{/each}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;person이라는 이름 대신 destructuring을 활용할 수도 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;If else blocks&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1592729371612&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  let num = 3;
&amp;lt;/script&amp;gt;

{#if num &amp;gt; 20}
  &amp;lt;p&amp;gt;Greater than 20&amp;lt;/p&amp;gt;
{:else if num &amp;gt; 5}
  &amp;lt;p&amp;gt;Greater than 5&amp;lt;/p&amp;gt;
{:else}
  &amp;lt;p&amp;gt;Not greater than 5&amp;lt;/p&amp;gt;
{/if}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;if-else 조건에 의해 dom을 조작하고 싶다면 &lt;b&gt;if, else if, else block&lt;/b&gt;을 적절히 활용하면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Inline Event Handlers&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1592729066435&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  let people = [
    { name: 'juno', hairColor: 'brown', age: 25, id: 1 },
    { name: 'mario', hairColor: 'black', age: 45, id: 2 },
    { name: 'lily', hairColor: 'pink', age: 35, id: 3 },
  ];

  const handleClick = (id) =&amp;gt; {
    // delete the person from people
    people = people.filter(person =&amp;gt; person.id !== id);
  };
&amp;lt;/script&amp;gt;

&amp;lt;main&amp;gt;
  {#each people as person (person.id)}
    &amp;lt;div&amp;gt;
      &amp;lt;h4&amp;gt;{person.name}&amp;lt;/h4&amp;gt;
      &amp;lt;p&amp;gt;{person.age} years old, {person.hairColor} hair&amp;lt;/p&amp;gt;
      &amp;lt;button on:click={() =&amp;gt; handleClick(person.id)}&amp;gt;delete&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  {/each}
&amp;lt;/main&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;event를 등록하는 일은 React와 거의 동일하다.&lt;/p&gt;
&lt;p&gt;다만 문법이 약간 달라서 on:click={} 과 같은 방식을 이용한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1592729201093&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  const handleClick = (event) =&amp;gt; {
    
  };
&amp;lt;/script&amp;gt;

&amp;lt;main&amp;gt;
  &amp;lt;button on:click={handleClick}&amp;gt;delete&amp;lt;/button&amp;gt;
&amp;lt;/main&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이벤트 함수 이름을 직접적으로 넣으면 첫 번째 파라미터로 event 객체가 자동으로 넘어온다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1592729261341&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  const handleClick = (event, message) =&amp;gt; {
    console.log(message); // 'parameter'
  };
&amp;lt;/script&amp;gt;

&amp;lt;main&amp;gt;
  &amp;lt;button on:click={(e) =&amp;gt; handleClick(e, 'parameter')}&amp;gt;delete&amp;lt;/button&amp;gt;
&amp;lt;/main&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;만약에 parameter를 넘겨주고 싶다면 위와 같은 방식으로 넘겨줄 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend</category>
      <category>JavaScript</category>
      <category>svelte</category>
      <category>스벨트</category>
      <category>자바스크립트</category>
      <category>코딩</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/206</guid>
      <comments>https://im-developer.tistory.com/206#entry206comment</comments>
      <pubDate>Sun, 21 Jun 2020 17:55:33 +0900</pubDate>
    </item>
    <item>
      <title>[Svelte] Svelte 기초 - Data binding과 Reactive Values</title>
      <link>https://im-developer.tistory.com/205</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;532&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0e7pI/btqEzdA3GBi/jXPyQAQjyiJ3RrQtkatxvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0e7pI/btqEzdA3GBi/jXPyQAQjyiJ3RrQtkatxvK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0e7pI/btqEzdA3GBi/jXPyQAQjyiJ3RrQtkatxvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0e7pI%2FbtqEzdA3GBi%2FjXPyQAQjyiJ3RrQtkatxvK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;532&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Input &amp;amp; Data binding&lt;/h3&gt;
&lt;pre id=&quot;code_1591104320514&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  export let name;
  let color = 'black';

  const handleClick = () =&amp;gt; {
    color = 'blue';
  };
&amp;lt;/script&amp;gt;

&amp;lt;main&amp;gt;
  &amp;lt;h1&amp;gt;Hello {name}!&amp;lt;/h1&amp;gt;
  &amp;lt;p&amp;gt;{color} jean!&amp;lt;/p&amp;gt;
  &amp;lt;button on:click={handleClick}&amp;gt;update jean color&amp;lt;/button&amp;gt;
&amp;lt;/main&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이렇게 color 변수를 선언한 후에 아래 &lt;b&gt;&amp;lt;p&amp;gt;{color} jean!&amp;lt;/p&amp;gt;&lt;/b&gt;을 출력하면 화면에 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;black jean!&lt;/b&gt;&lt;/span&gt;이라는 단어가 출력된다.&lt;/p&gt;
&lt;p&gt;({}는 동적인 값을 넣을 때 사용한다.)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;color 변수의 값을 버튼을 클릭했을 때 동적으로 바꿔주고 싶다면 클릭 이벤트를 등록하면 된다.&lt;/p&gt;
&lt;p&gt;Svelte에서 &lt;b&gt;inline 이벤트&lt;/b&gt;를 등록하는 방법은 아주 쉬운데, 먼저 script에서 함수를 선언해준 다음,&lt;/p&gt;
&lt;p&gt;아래 태그에 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;on:click&lt;/b&gt;&lt;/span&gt;으로 등록하면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Svelte에서는 click, mousemove 등 모든 이벤트를 소문자로 등록한다.&lt;/p&gt;
&lt;p&gt;이렇게 하면 버튼을 클릭하면 화면에 &lt;b&gt;black jean!&lt;/b&gt;이 &lt;b&gt;blue jean!&lt;/b&gt;으로 바뀐다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1591185205996&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  export let name;
  let color = 'black';

  const handleClick = () =&amp;gt; {
    color = 'blue';
  };
  
  const handleInput = (e) =&amp;gt; {
    color = e.target.value;
  };
&amp;lt;/script&amp;gt;

&amp;lt;main&amp;gt;
  &amp;lt;h1&amp;gt;Hello {name}!&amp;lt;/h1&amp;gt;
  &amp;lt;p&amp;gt;{color} jean!&amp;lt;/p&amp;gt;
  &amp;lt;button on:click={handleClick}&amp;gt;update jean color&amp;lt;/button&amp;gt;
  &amp;lt;input type='text' on:input={handleInput} /&amp;gt;
&amp;lt;/main&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;만약에 input text를 추가한 후에 내가 타이핑한 string을 color 변수로 업데이트해주고 싶다면&lt;/p&gt;
&lt;p&gt;input 태그에 &lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;on:input&lt;/b&gt;&lt;/span&gt;을 사용하면 된다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;자바스크립트에서 element.addEventListener()로 등록하는 이벤트 핸들러로 등록하는 함수에는&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Event 객체가 인자로 들어오는데, Svelte에서도 마찬가지다.&lt;/p&gt;
&lt;p&gt;인자로 들어오는 event 객체의 target.value로 input value에 접근할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위와 같이 등록하면 input에 타이핑한 내용이 실시간으로 화면에 반영된다.&lt;/p&gt;
&lt;p&gt;그런데 위 코드는 단방향 데이터 바인딩만 되어 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;무슨 뜻이냐하면, 사용자가 input에 yellow를 타이핑하면 color 변수의 값은 yellow가 되고 화면에도 yellow jean!이라고 뜬다.&lt;/p&gt;
&lt;p&gt;이 때 사용자가 버튼을 클릭하면 color 변수의 값은 blue가 되고 화면에도 &quot;blue jean!&quot;이라고 뜨지만,&lt;/p&gt;
&lt;p&gt;input 창에는 여전히 사용자가 입력한 yellow가 그대로 남아있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;만약에 사용자가 버튼을 클릭했을 때 input에 쓰여진 yellow가 blue로 바뀌게하고 싶다면 어떻게 해야 할까?&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;- Two way Data binding&lt;/h4&gt;
&lt;pre id=&quot;code_1591185444994&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  export let name;
  let color = 'black';

  const handleClick = () =&amp;gt; {
    color = 'blue';
  };
  
  const handleInput = (e) =&amp;gt; {
    color = e.target.value;
  };
&amp;lt;/script&amp;gt;

&amp;lt;main&amp;gt;
  &amp;lt;h1&amp;gt;Hello {name}!&amp;lt;/h1&amp;gt;
  &amp;lt;p&amp;gt;{color} jean!&amp;lt;/p&amp;gt;
  &amp;lt;button on:click={handleClick}&amp;gt;update jean color&amp;lt;/button&amp;gt;
  &amp;lt;input type='text' on:input={handleInput} value={color} /&amp;gt;
&amp;lt;/main&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;요런 방식을 쉽게 생각할 수 있을 것이다.&lt;/p&gt;
&lt;p&gt;input value 값을 color 값으로 지정하는 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1591186159482&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  export let name;
  let color = 'black';

  const handleClick = () =&amp;gt; {
    color = 'blue';
  };
&amp;lt;/script&amp;gt;

&amp;lt;main&amp;gt;
  &amp;lt;h1&amp;gt;Hello {name}!&amp;lt;/h1&amp;gt;
  &amp;lt;p&amp;gt;{color} jean!&amp;lt;/p&amp;gt;
  &amp;lt;button on:click={handleClick}&amp;gt;update jean color&amp;lt;/button&amp;gt;
  &amp;lt;input type='text' bind:value={color} /&amp;gt;
&amp;lt;/main&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;근데 Svelte에서는 더더욱 간단하게 처리할 수 있다.&lt;/p&gt;
&lt;p&gt;바로 &lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;bind:value&lt;/b&gt;&lt;/span&gt;를 활용하는 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이렇게 하면 handleInput 메소드도 필요 없어지며,&lt;/p&gt;
&lt;p&gt;사용자가 input에 입력하는 내용이 바로 color 변수에 반영되어 화면이 변경되고,&lt;/p&gt;
&lt;p&gt;버튼을 클릭했을 때 input text도 바로 바뀐다! 정말 신기하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1591186337824&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  export let name;
  let color = 'black';

  const handleClick = () =&amp;gt; {
    color = 'blue';
  };
&amp;lt;/script&amp;gt;

&amp;lt;main&amp;gt;
  &amp;lt;h1&amp;gt;Hello {name}!&amp;lt;/h1&amp;gt;
  &amp;lt;p style='color: {color}'&amp;gt;{color} jean!&amp;lt;/p&amp;gt;
  &amp;lt;button on:click={handleClick}&amp;gt;update jean color&amp;lt;/button&amp;gt;
  &amp;lt;input type='text' bind:value={color} /&amp;gt;
&amp;lt;/main&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Svelte에서는 심지어 &lt;b&gt;style에 바로 동적인 Data binding이 가능&lt;/b&gt;하다!&lt;/p&gt;
&lt;p&gt;위와 같이 변경하면 color 값이 변경될 때마다 text 색상이 같이 변경될 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;2020-06-03 21.14.12.gif&quot; data-origin-width=&quot;563&quot; data-origin-height=&quot;305&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bu2GfQ/btqECMaFFNs/ZHa1G10u1ktpBWR0NUZ9aK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bu2GfQ/btqECMaFFNs/ZHa1G10u1ktpBWR0NUZ9aK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bu2GfQ/btqECMaFFNs/ZHa1G10u1ktpBWR0NUZ9aK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bu2GfQ/btqECMaFFNs/ZHa1G10u1ktpBWR0NUZ9aK/img.gif&quot; data-filename=&quot;2020-06-03 21.14.12.gif&quot; data-origin-width=&quot;563&quot; data-origin-height=&quot;305&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Reactive Values&lt;/h3&gt;
&lt;pre id=&quot;code_1591186654226&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  let firstName = 'Jimi';
  let lastName = 'Hendrix';
  let color = 'black';
&amp;lt;/script&amp;gt;

&amp;lt;main&amp;gt;
  &amp;lt;p&amp;gt;{firstName} {lastName} - {color} jean!&amp;lt;/p&amp;gt;
  &amp;lt;input type='text' bind:value={firstName} /&amp;gt;
  &amp;lt;input type='text' bind:value={lastName} /&amp;gt;
  &amp;lt;input type='text' bind:value={color} /&amp;gt;
&amp;lt;/main&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;만약에 위와 같이 firstName과 lastName을 위와 같이 입력하고 싶다고 해보자.&lt;/p&gt;
&lt;p&gt;firstName과 lastName을 각각 쓰는 것도 좋지만, 한꺼번에 묶어서 편리하게 사용할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1591186808780&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  let firstName = 'Jimi';
  let lastName = 'Hendrix';
  let color = 'black';
  
  $: fullName = `${firstName} ${lastName}`;
&amp;lt;/script&amp;gt;

&amp;lt;main&amp;gt;
  &amp;lt;p&amp;gt;{fullName} - {color} jean!&amp;lt;/p&amp;gt;
  &amp;lt;input type='text' bind:value={firstName} /&amp;gt;
  &amp;lt;input type='text' bind:value={lastName} /&amp;gt;
  &amp;lt;input type='text' bind:value={color} /&amp;gt;
&amp;lt;/main&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;바로 요렇게! 이렇게 사용되는 값을 &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Reactive Value&lt;/b&gt;&lt;/span&gt;라고 부른다.&lt;/p&gt;
&lt;p&gt;문법은 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;$:&lt;/b&gt;&lt;/span&gt; 와 함께 원하는 변수명을 지어주면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;firstName이 바인딩된 &lt;span style=&quot;color: #333333;&quot;&gt;input에 이름을 타이핑하면 fullName에도 그대로 반영된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;2020-06-03 21.22.55.gif&quot; data-origin-width=&quot;654&quot; data-origin-height=&quot;222&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0kGme/btqEBO8bpLC/8c1N4SxH9albDI5LRw3aNk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0kGme/btqEBO8bpLC/8c1N4SxH9albDI5LRw3aNk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0kGme/btqEBO8bpLC/8c1N4SxH9albDI5LRw3aNk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/0kGme/btqEBO8bpLC/8c1N4SxH9albDI5LRw3aNk/img.gif&quot; data-filename=&quot;2020-06-03 21.22.55.gif&quot; data-origin-width=&quot;654&quot; data-origin-height=&quot;222&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Reactive Statement&lt;/h3&gt;
&lt;pre id=&quot;code_1591187053302&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  let firstName = 'Jimi';
  let lastName = 'Hendrix';
  let color = 'black';

  $: fullName = `${firstName} ${lastName}`;  // reactive value
  $: console.log(color); // reactive statement
  $: {
      console.log(color);
      console.log(fullName);
  }
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;값 뿐만 아니라 statement도 특정 값에 반응하도록 만들 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;$:&lt;/b&gt; &lt;/span&gt;뒤에 특정 value와 함께 statement를 적어주면 그 값이 바뀔 때마다 해당 statement가 변경된다.&lt;/p&gt;
&lt;p&gt;만약에 여러 줄의 statement를 같이 엮어주고 싶다면 $: 뒤에 {}를 쓰고 여러 줄을 적어주면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1591187202409&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$: {
  console.log(color);
  console.log(fullName);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위 statement만 남겨두고 실행해보면 이렇게 작동된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;2020-06-03 21.29.16.gif&quot; data-origin-width=&quot;815&quot; data-origin-height=&quot;688&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ex6e8e/btqEALEzlFP/ttAYfCL298fvf4KPTkneTk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ex6e8e/btqEALEzlFP/ttAYfCL298fvf4KPTkneTk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ex6e8e/btqEALEzlFP/ttAYfCL298fvf4KPTkneTk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/ex6e8e/btqEALEzlFP/ttAYfCL298fvf4KPTkneTk/img.gif&quot; data-filename=&quot;2020-06-03 21.29.16.gif&quot; data-origin-width=&quot;815&quot; data-origin-height=&quot;688&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Frontend</category>
      <category>JavaScript</category>
      <category>svelte</category>
      <category>스벨트</category>
      <category>자바스크립트</category>
      <category>코딩</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/205</guid>
      <comments>https://im-developer.tistory.com/205#entry205comment</comments>
      <pubDate>Tue, 2 Jun 2020 22:28:53 +0900</pubDate>
    </item>
    <item>
      <title>[Svelte] Svelte 첫 시작 - Setting up a Svelte App</title>
      <link>https://im-developer.tistory.com/204</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=zojEMeQGGHs&amp;amp;list=PL4cUxeGkcC9hlbrVO_2QFVqVPhlZmz7tO&quot;&gt;https://www.youtube.com/watch?v=zojEMeQGGHs&amp;amp;list=PL4cUxeGkcC9hlbrVO_2QFVqVPhlZmz7tO&lt;/a&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=zojEMeQGGHs&amp;amp;list=PL4cUxeGkcC9hlbrVO_2QFVqVPhlZmz7tO&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/cD6i1n/hyGgkJQ9Wj/cW3qPNL9kWhxlSopfOcW50/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot; data-video-origin-width=&quot;860&quot; data-video-origin-height=&quot;484&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/zojEMeQGGHs&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;나는 주로 유투브 영상을 보면서 공부하는 것을 좋아하는데, 스벨트 관련 정말 괜찮은 시리즈가 있어서 위에 첨부했다.&lt;/p&gt;
&lt;p&gt;총 35강인데 한 강의 당 10분이 넘지 않는 짧은 길이라서 틈틈히 짬내서 보면 좋을 듯 하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아무튼 이 강의를 쭉 훑으면서 중요한 내용들을 블로그에 정리해보려고 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Template Installation&lt;/h3&gt;
&lt;p&gt;일단 Svelte는 마치 React의 Create-react-app 처럼 기본적인 세팅이 되어 있는&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt; 템플릿을 제공&lt;/b&gt;&lt;/span&gt;하고 있다.&lt;/p&gt;
&lt;p&gt;그래서 나와 같은 Svelte Beginner들이 쉽게 설치하고 프로젝트를 시작할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;degit&lt;/b&gt;이라는 npm package를 통해 &lt;b&gt;sveltejs/template Github repo&lt;/b&gt;에서 템플릿을 클론 받으면 되는데 방법은 매우 간단하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/sveltejs/template&quot;&gt;https://github.com/sveltejs/template&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1591014342136&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;object&quot; data-og-title=&quot;sveltejs/template&quot; data-og-description=&quot;Template for building basic applications with Svelte - sveltejs/template&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/sveltejs/template&quot; data-og-url=&quot;https://github.com/sveltejs/template&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/crw3G5/hyGgirLBrX/mRzj46sxOs0W4KTg5AbMt0/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400&quot;&gt;&lt;a href=&quot;https://github.com/sveltejs/template&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/sveltejs/template&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/crw3G5/hyGgirLBrX/mRzj46sxOs0W4KTg5AbMt0/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;sveltejs/template&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Template for building basic applications with Svelte - sveltejs/template&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1591014396717&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npx degit sveltejs/template svelte-app
cd my-project&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;또는&lt;/p&gt;
&lt;pre id=&quot;code_1591014481261&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm install -g degit
degit sveltejs/template my-project
cd my-project&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;터미널로 원하는 디렉토리에 위 명령어를 치기만 하면 된다.&lt;/p&gt;
&lt;p&gt;npx를 이용해도 되고 degit 라이브러리를 global로 설치한 후 사용해도 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1591014549315&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;npm install
npm run dev&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;클론 받고 npm install로 packages를 설치한 후 npm run dev를 치면&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Webpack과 같은 역할을 하는 Rollup이 Local dev 서버를 실행하여 앱이 구동된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/F4Y0y/btqEzAH9LFJ/6dQTvcXJK4FjY77DZLPQn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/F4Y0y/btqEzAH9LFJ/6dQTvcXJK4FjY77DZLPQn0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/F4Y0y/btqEzAH9LFJ/6dQTvcXJK4FjY77DZLPQn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FF4Y0y%2FbtqEzAH9LFJ%2F6dQTvcXJK4FjY77DZLPQn0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Rollup은 Webpack과 같이 file들을 watch하며 코드 컴파일 설정들을 담당한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;그리고 코드를 컴파일하여 번들 파일을 산출하는 역할을 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;(기본적으로 서버는 localhost 요청에만 응답하는데, 다른 컴퓨터와의 연결도 허용하고 싶다면&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;package.json에서 &lt;/span&gt;&lt;span&gt;sirv &lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;command에 --host 0.0.0.0. 옵션을 추가하면 된다.)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;브라우저에서 localhost:5000을 열어보면 기본 템플릿이 구동되고 있을 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이제 src/App.svelte 파일에서 간단한 문구를 수정한 후에 브라우저를 살펴보면 자동으로 바뀐 문구로 업데이트된 것을 확인할 수 있다.&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHezA4/btqEzhIO7RP/VIZQXqQZ1l8ObKBMUGM8jK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHezA4/btqEzhIO7RP/VIZQXqQZ1l8ObKBMUGM8jK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHezA4/btqEzhIO7RP/VIZQXqQZ1l8ObKBMUGM8jK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHezA4%2FbtqEzhIO7RP%2FVIZQXqQZ1l8ObKBMUGM8jK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Basic Directory&lt;/h3&gt;
&lt;p&gt;Svelte 템플릿의 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;기본 폴더 구조&lt;/b&gt;&lt;/span&gt;는 다음과 같다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1591015288264&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;- public/
  - build/
    - bundle.js
    - bundle.css
  - index.html
  - global.css
- src/
  - App.svelte
  - main.js
- package.json
- package-lock.json
- rollup.config.js&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;public&lt;/b&gt;은 마지막 production 코드와 같이 번들링된 Output 파일들이 들어갈 곳이다. 즉 배포를 위한 파일들이 들어간다.&lt;br /&gt;브라우저에 serving하기 위한 index.html이라던지 style 파일들도 여기에 들어간다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;src&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;는 svelte 컴포넌트 파일들과 svelte 컴포넌트를 묶어서 document.body에 넣어줄 main.js 파일이 들어간다.&lt;br /&gt;&lt;/span&gt;(우리가 작성 할 99%의 코드들이 여기에 속할 것이다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;.svelte&lt;/b&gt;로 작성된 파일들은 하나의 컴포넌트 단위이며, 여러 개의 svelte 컴포넌트를 모아서 실제 DOM에 주입하는 방식으로 페이지가 구성된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3 Different Sections&lt;/span&gt;&lt;span style=&quot;overflow:hidden;line-height:0px&quot;&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;svelte는 3개의 섹션으로 구성된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;&lt;span style=&quot;color: #666666;&quot;&gt;- src/App.svelte&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1591015908516&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  export let name;
&amp;lt;/script&amp;gt;

&amp;lt;main&amp;gt;
  &amp;lt;h1&amp;gt;Hello {name}!&amp;lt;/h1&amp;gt;
  &amp;lt;p&amp;gt;Have a nice day!&amp;lt;/p&amp;gt;
&amp;lt;/main&amp;gt;

&amp;lt;style&amp;gt;
  main {
    text-align: center;
    padding: 1em;
    max-width: 240px;
    margin: 0 auto;
  }

  h1 {
    color: #ff3e00;
    text-transform: uppercase;
    font-size: 4em;
    font-weight: 100;
}

@media (min-width: 640px) {
  main {
    max-width: none;
  }
}
&amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;script 섹션: 컴포넌트 로직이 들어 가는 곳이다.&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;HTML 템플릿: 최종적으로 DOM으로 주입되는 요소들&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;style 태그: 해당 컴포넌트에 입혀질 CSS 스타일&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Target and Props&lt;/h3&gt;
&lt;p&gt;main.js 파일은 이 앱의 가장 첫 스타트를 맡고 있는 파일이다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;(React에서 주로 src/index.js 파일과 같은 역할을 한다.)&lt;/p&gt;
&lt;p&gt;즉, 가장 최초로 구동되어 세팅되는 파일이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;i&gt;- src/main.js&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1591016185212&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import App from './App.svelte';

const app = new App({
  target: document.body,
  props: {
    name: 'world'
  }
});

export default app;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;main.js는 App svelte 컴포넌트를 import하여 (&lt;span style=&quot;color: #333333;&quot;&gt;svelte 컴포넌트는 따로 export를 해주지 않아도 자동으로 import해서 사용 가능하다!) &lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;그 컴포넌트의 새로운 instance 객체를 만든다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;새 Instance 객체를 만들 때 인자로 넣어주는 객체로 &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;target&lt;/b&gt;&lt;/span&gt;과 &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;props&lt;/b&gt;&lt;/span&gt;를 설정할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;target&lt;/b&gt;은 이 컴포넌트를 &lt;b&gt;DOM 또는 HTML document에서 정확히 어디에 주입하고 싶은지를 설정하는 property&lt;/b&gt;이다.&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;위 예제에서는 document의 body 태그를 target으로 설정하고 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;(원하면 Document.querySelector() 등을 이용해 다른 dom을 select할 수도 있다.)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cAB3yH/btqEyJskOdj/KasYuH0yPsNYwvj8a65uo0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cAB3yH/btqEyJskOdj/KasYuH0yPsNYwvj8a65uo0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cAB3yH/btqEyJskOdj/KasYuH0yPsNYwvj8a65uo0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcAB3yH%2FbtqEyJskOdj%2FKasYuH0yPsNYwvj8a65uo0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;public/index.html이 바로 이 모든 앱을 브라우저에 서빙하는 역할을 하는데,&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;브라우저의 개발자 도구로 살펴보면 body 태그에 main 태그가 주입된 것을 확인할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;아무튼 main.js는 결과적으로 index.html의 document 태그를 셀렉하여&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;모든 App 템플릿을 주입하는 일을 하고 있는 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;b&gt;props&lt;/b&gt; property는 컴포넌트에 data를 넘겨주는 역할을 하게 되는데, &lt;/span&gt;위 예제에서는 name이라는 이름의 string data를 넘겨주고 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;여기서 가장 중요한 포인트는 .svelte로 생성되는 컴포넌트들이 main.js 파일에서 target으로 설정된 DOM에 주입된다는 사실이다.&lt;/p&gt;
&lt;p&gt;사실 앱에 더 많은 컴포넌트들이 생기고 규모가 커지면 모든 컴포넌트를 위와 같은 방식으로 DOM에 주입하지는 않는다.&lt;/p&gt;
&lt;p&gt;대신에 top-level 컴포넌트인 App.svelted 컴포넌트에 여러 컴포넌트들을 중첩한 후 이 App 컴포넌트 하나만 DOM에 직접 주입한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;(리액트에서도 여러 컴포넌트들을 하나의 Root 컴포넌트에 모은 후 주입하는 것과 동일하다.)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Outputs&lt;/h3&gt;
&lt;p&gt;여하튼 이렇게 만든 앱을 빌드하면 어떤 일이 벌어질까?&lt;/p&gt;
&lt;p&gt;앱을 빌드하면 Svelte는 모든 컴포넌트들을 하나의 단일 JavaScript 번들 파일로 컴파일한다.&lt;/p&gt;
&lt;p&gt;그리고 public/build 디렉토리에 산출물을 생성하게 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;i&gt;- public/index.html&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1591017331572&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
  &amp;lt;meta charset='utf-8'&amp;gt;
  &amp;lt;meta name='viewport' content='width=device-width,initial-scale=1'&amp;gt;

  &amp;lt;title&amp;gt;Svelte app&amp;lt;/title&amp;gt;

  &amp;lt;link rel='icon' type='image/png' href='/favicon.png'&amp;gt;
  &amp;lt;link rel='stylesheet' href='/global.css'&amp;gt;
  &amp;lt;link rel='stylesheet' href='/build/bundle.css'&amp;gt;

  &amp;lt;script defer src='/build/bundle.js'&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/head&amp;gt;

&amp;lt;body&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;그리고 이렇게 생성된 번들 파일은 public/index.html에 연결되어 브라우저에 서빙된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Examples&lt;/h3&gt;
&lt;p&gt;이제 아주 간단하게 버튼을 클릭해서 변수 값을 바꿔보는 일을 해보자!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;i&gt;- src/App.svelte&lt;/i&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1591017593441&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  export let name;
  let color = 'black';
&amp;lt;/script&amp;gt;

&amp;lt;main&amp;gt;
  &amp;lt;h1&amp;gt;Hello {name}!&amp;lt;/h1&amp;gt;
  &amp;lt;p&amp;gt;{color} jean!&amp;lt;/p&amp;gt;
  &amp;lt;button&amp;gt;update jean color&amp;lt;/button&amp;gt;
&amp;lt;/main&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위와 같이 color 라는 변수를 새로 만들고 'black'이라는 값을 할당했다.&lt;/p&gt;
&lt;p&gt;그리고 버튼을 하나 생성해주었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFHsrb/btqEzABpO5B/4xQ0ctjoTen59NGRFGhnv0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFHsrb/btqEzABpO5B/4xQ0ctjoTen59NGRFGhnv0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFHsrb/btqEzABpO5B/4xQ0ctjoTen59NGRFGhnv0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFHsrb%2FbtqEzABpO5B%2F4xQ0ctjoTen59NGRFGhnv0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1591017851577&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  export let name;
  let color = 'black';

  const handleClick = () =&amp;gt; {
    color = 'blue';
  };
&amp;lt;/script&amp;gt;

&amp;lt;main&amp;gt;
  &amp;lt;h1&amp;gt;Hello {name}!&amp;lt;/h1&amp;gt;
  &amp;lt;p&amp;gt;{color} jean!&amp;lt;/p&amp;gt;
  &amp;lt;button on:click={handleClick}&amp;gt;update jean color&amp;lt;/button&amp;gt;
&amp;lt;/main&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;click 이벤트는 위와 같은 형태로 추가할 수 있다.&lt;/p&gt;
&lt;p&gt;이제 버튼을 클릭하면 handleClick 함수가 실행되어 color 변수의 값이 black에서 blue로 변할 것이고,&lt;/p&gt;
&lt;p&gt;브라우저에서 &amp;lt;p&amp;gt;{color} jean!&amp;lt;/p&amp;gt; 부분이 즉각적으로 업데이트될 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqAvvl/btqEze6Ne1D/4ZLxvY04UTSJWAW67GNyJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqAvvl/btqEze6Ne1D/4ZLxvY04UTSJWAW67GNyJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqAvvl/btqEze6Ne1D/4ZLxvY04UTSJWAW67GNyJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqAvvl%2FbtqEze6Ne1D%2F4ZLxvY04UTSJWAW67GNyJ0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;짠! 이렇게 아주 쉽게 동적 데이터를 이벤트 핸들러와 엮을 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend</category>
      <category>JavaScript</category>
      <category>svelte</category>
      <category>스벨트</category>
      <category>자바스크립트</category>
      <category>코딩</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/204</guid>
      <comments>https://im-developer.tistory.com/204#entry204comment</comments>
      <pubDate>Mon, 1 Jun 2020 22:39:53 +0900</pubDate>
    </item>
    <item>
      <title>[Svelte] 스벨트란 무엇인가? Reactive App 개발을 위한 새로운 접근법</title>
      <link>https://im-developer.tistory.com/203</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;530&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sOCzW/btqEyfSfEek/kdrDQKj33TY7mcOCa7ETRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sOCzW/btqEyfSfEek/kdrDQKj33TY7mcOCa7ETRK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sOCzW/btqEyfSfEek/kdrDQKj33TY7mcOCa7ETRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsOCzW%2FbtqEyfSfEek%2FkdrDQKj33TY7mcOCa7ETRK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;530&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;요즘 관심가는 기술 중 하나인 스벨트(Svelte)에 대해서 차근 차근 공부해보려고 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;b&gt;Svelte(&lt;b&gt;스벨트)란&lt;/b&gt;&lt;/b&gt;?&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;스벨트가 뭔지 궁금해서 Svelte 홈페이지에 들어가서 소개문을 읽어보았다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Svelte is a tool for building &lt;span style=&quot;color: #009a87;&quot;&gt;fast&lt;/span&gt; web applications.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Svelte는 &lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;빠른&lt;/b&gt;&lt;/span&gt; 웹 어플리케이션을 구축하기 위한 도구이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;It is similar to JavaScript frameworks such as React and Vue, which share a goal of making it easy to build slick &lt;span style=&quot;color: #ef6f53;&quot;&gt;interactive&lt;/span&gt; user interfaces.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Svelte는 &lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;interactive&lt;/b&gt;&lt;/span&gt;한 유저 인터페이스를 구축한다는 데 있어 React나 Vue와 같은 JavaScript 프레임워크, 라이브러리와 비슷하다.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;But there's a crucial difference: Svelte converts your app into ideal JavaScript at&amp;nbsp;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;i&gt;build time&lt;/i&gt;&lt;/span&gt;, rather than interpreting your application code at&amp;nbsp;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;i&gt;run time&lt;/i&gt;&lt;/span&gt;. This means you don't pay the performance cost of the framework's abstractions, and you don't incur a penalty when your app first loads.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;그러나 매우 중요한 차이점이 있는데, Svelte는 우리의 앱을 &lt;b&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;i&gt;실행 시점(Run time)&lt;/i&gt;&lt;/span&gt;&lt;/b&gt;에 해석하는 것이 아니라 &lt;b&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;i&gt;빌드 시점(Build time)&lt;/i&gt;&lt;/span&gt;&lt;/b&gt;에 JavaScript로 변환한다. 이 말의 의미는 프레임워크 추상화를 위한 퍼포먼스에 더 이상 비용을 들이지 않아도 된다는 뜻이다. 그리고 앱의 첫 로드 시에도 빠른 구동이 가능하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;You can build your entire app with Svelte, or you can add it incrementally to an existing codebase. You can also ship components as standalone packages that work anywhere, without the overhead of a dependency on a conventional framework.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;전체 앱을 Svelte로 구축할 수도 있고, 이미 존재하는 코드 베이스에 점진적으로 적용할 수도 있다. 또한 전통적인 프레임워크와의 dependency와 상관없이 어디서든 사용할 수 있는 독립적인 컴포넌트로 만들어 사용할 수도 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;그러니까 Svelte는 &lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;Reactive&lt;/b&gt;&lt;/span&gt; web app과 interface를 만드는 데 사용되는 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;Compiler&lt;/b&gt;&lt;/span&gt;이다. 여기서 주목해야 할 단어는 Reactive와 Compiler이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Reactive app&lt;/b&gt;이라는 것은 이런 의미이다. 만약에 오늘 날씨를 보여주는 앱이 있다고 하자. 앱에 날씨 정보를 상태 값으로 저장한 후 날씨가 바뀔 때마다 상태 값이 업데이트될 것이다. 그리고 날씨가 바뀌어 상태 값이 변경되는 즉시 브라우저에 반영해주어야 한다. 이렇게 상태의 변화가 즉각적으로 반영되는 앱들을 reactive라고 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이러한 Reactive app을 구축하기 위해 많은 개발자들이 Angular, Vue, 또는 React와 같은 프레임워크, 라이브러리 등을 이용했다. (React는 심지어 이름부터 &quot;React&quot;다.  ) Svelte는 이렇게 Reactive app을 만드는 데 사용된다는 점에 있어서 React와 같은 기존 라이브러리와 유사하다. Svelte는 SPA(Single Page Application) 전체를 개발하거나 혹은 페이지의 작은 일부분을 개발할 때 사용 가능하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그렇다면 Svelte가 기존의 React같은 라이브러리와 다른 점이 무엇일까?&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Svelte와 React의 차이&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Framework가 아닌 Compiler&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;Svelte는 Framework가 아닌 Compiler이다. 즉, 기존에 React와 같은 라이브러리는 production 배포 시 React 라이브러리 자체가 같이 배포되며, 실행 시점에 우리 앱의 코드들을 해석한다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그러나 Svelte는 실행 시점이 아닌 빌드 시점에 모든 코드들을 single vanilla JavaScript 번들로 컴파일한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;기존의 React와 같은 프레임워크 라이브러리들을 생각해보자. 프로젝트를 컴파일하기 위해 우리는 Webpack과 Babel 등을 호출하여 프로젝트 파일들을 번들하고, React와 React-dom 라이브러리 또한 번들에 포함된다. 반면에 Svelte는 컴파일러이기 때문에 자체적으로 컴포넌트를 컴파일한다. 또한 Rollup(Webpack과 비슷한 모듈 번들러)의 트리 쉐이킹을 잘 활용하여 실제 코드에서 사용하는 부분만 번들링한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;즉, Svelte로 앱을 만든 후 빌드하면 단 하나의 자바스크립트 파일이 생성되어 이 파일 하나만 배포하면 되고 Svelte 라이브러리는 배포할 필요가 없다. 그렇기 때문에 Svelte로 만든 결과물들은 대개 앱 running이 매우 빠르다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;No Virtual DOM&lt;/h4&gt;
&lt;p&gt;React는 가상 DOM을 사용하는 대표적 프레임워크 라이브러리이다. 우리가 React에서 render() 메소드를 사용해 component를 만들면, 가상 DOM 객체가 만들어진다. 앱의 상태가 업데이트되는 매 순간마다 새로운 가상 DOM 객체가 만들어진다. 프레임워크의 업무는 실제 DOM과 가상 DOM을 비교해서 진짜 필요한 변화만 실제 DOM에 적용하는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;React 팀은 라이브러리를 런칭하면서 대부분의 DOM operation은 매우 느리기 때문에 가상 DOM을 이용하여 필요할 때만 실제 DOM을 업데이트하는 방식이 매우 빠르다고 설명했다. &lt;span&gt;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;그리고 또한 React는 state가 업데이트되어 매 번 전체 앱을 다시 렌더할 때 성능에 대한 걱정이 전혀 필요없다고 말해왔다. &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;그러나 현실은 모든 경우에 가상 DOM 방식이 빠르고 좋은 것은 아니다. 만약 정말로 React가 주장한대로 re-render 시에 성능을 걱정할 필요가 전혀 없다면, shouldComponentUpdate와 같은 Life Cycle이 생기지 않았을 것이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;가상 DOM과 실제 DOM을 비교하는 작업은 많은 에너지를 소모한다. 만약 어떤 컴포넌트에서 name이라는 prop이 'John'에서 'Sam'으로 바뀌었다고 해보자. 가상 DOM과 실제 DOM을 비교하여 업데이트하는 과정은 간단하게 다음과 같이 설명할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1591003907490&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function HelloMessage(props) {
  return (
    &amp;lt;div className=&quot;greeting&quot;&amp;gt;
      Hello {props.name}
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;기존 스냅샷과 새로운 스냅샷은 동일한 div를 가지고 있을 것이다.&lt;/li&gt;
&lt;li&gt;기존 div의 모든 attribute를 새로운 div와 비교하여 하나라도 변경된 것이 있는지 확인한다. (이 경우에는 className이라는 attribute만 존재하며 변경되지 않았다.)&lt;/li&gt;
&lt;li&gt;자식 요소들을 모두 비교한 후 text 요소만 변경된 것을 확인하여 실제 DOM을 업데이트한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;위 케이스의 경우 1, 2, 3번 중에서 3번만 의미가 있는 작업이다. 따라서 1, 2번을 생략하고 바로 3번 작업만 수행하게 된다면 지금보다 훨씬 효율적일 것이다! 그리고 이 것이 바로 Svelte의 컨셉이다.&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;즉, Svelte는 실제 DOM에 변경 사항을 적용하기 전에 diff를 비교하고 컴포넌트를 그리기 위한 시간을 소비하는 과정을 모두 생략하고 바로 실제 DOM을 변경한다. 또한 어떤 요소에 변화가 일어나는 지 알고 있기 때문에 빠르게 타겟 요소만 변경할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Svelte는 가상 DOM을 사용하지 않고도 React와 비슷한 프로그래밍 모델을 사용할 수 있도록 만들었고, 그 덕분에 매우 빠른 성능적 이점이 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;넘나 재미있는 컨셉이고 실제로 React로 만든 앱보다 실행 속도가 얼마나 빠를 지 궁금하다.&lt;/p&gt;
&lt;p&gt;얼른 공부해서 이것 저것 만들어봐야겠다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;(Svelte 웹 사이트를 대충 둘러보니 정리가 매우 잘 되어 있어서 좋다. ☺️)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://svelte.dev/&quot;&gt;https://svelte.dev/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1590998971092&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Svelte &amp;bull; Cybernetically enhanced web apps&quot; data-og-description=&quot;Svelte is a radical new approach to building user interfaces. Whereas traditional frameworks like React and Vue do the bulk of their work in the browser, Svelte shifts that work into a compile step that happens when you build your app. Instead of using tec&quot; data-og-host=&quot;svelte.dev&quot; data-og-source-url=&quot;https://svelte.dev/&quot; data-og-url=&quot;https://svelte.dev/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/fNqRf/hyGgk3QoRV/HWLlR9izsR5WgxatXvJZyk/img.png?width=1000&amp;amp;height=500&amp;amp;face=0_0_1000_500,https://scrap.kakaocdn.net/dn/t9ugB/hyGgnGfMfa/eB4SnxvoX9nuc3bBGa6U31/img.png?width=1000&amp;amp;height=500&amp;amp;face=0_0_1000_500,https://scrap.kakaocdn.net/dn/8KSjz/hyGgqbT5kI/Ffwe7pziDJErWU2wnfVrvk/img.png?width=1000&amp;amp;height=250&amp;amp;face=0_0_1000_250&quot;&gt;&lt;a href=&quot;https://svelte.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://svelte.dev/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/fNqRf/hyGgk3QoRV/HWLlR9izsR5WgxatXvJZyk/img.png?width=1000&amp;amp;height=500&amp;amp;face=0_0_1000_500,https://scrap.kakaocdn.net/dn/t9ugB/hyGgnGfMfa/eB4SnxvoX9nuc3bBGa6U31/img.png?width=1000&amp;amp;height=500&amp;amp;face=0_0_1000_500,https://scrap.kakaocdn.net/dn/8KSjz/hyGgqbT5kI/Ffwe7pziDJErWU2wnfVrvk/img.png?width=1000&amp;amp;height=250&amp;amp;face=0_0_1000_250');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Svelte &amp;bull; Cybernetically enhanced web apps&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Svelte is a radical new approach to building user interfaces. Whereas traditional frameworks like React and Vue do the bulk of their work in the browser, Svelte shifts that work into a compile step that happens when you build your app. Instead of using tec&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;svelte.dev&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend</category>
      <category>JavaScript</category>
      <category>svelte</category>
      <category>스벨트</category>
      <category>자바스크립트</category>
      <category>코딩</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/203</guid>
      <comments>https://im-developer.tistory.com/203#entry203comment</comments>
      <pubDate>Mon, 1 Jun 2020 18:44:27 +0900</pubDate>
    </item>
    <item>
      <title>iOS 디바이스에서 body의 scroll을 막는 방법 (How to prevent body scrolling on iOS?)</title>
      <link>https://im-developer.tistory.com/201</link>
      <description>&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;375&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7LGWc/btqEwJfpvZy/qjNt5E9cKhZDZIuOsQZlhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7LGWc/btqEwJfpvZy/qjNt5E9cKhZDZIuOsQZlhK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7LGWc/btqEwJfpvZy/qjNt5E9cKhZDZIuOsQZlhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7LGWc%2FbtqEwJfpvZy%2FqjNt5E9cKhZDZIuOsQZlhK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;375&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;지난 주부터 회사에서 해결이 안되서 골머리를 앓았던 문제에 대해서 얘기해보려고 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;보통의 UI에서 대부분 모달(팝업이라고도 부른다)이 뜨면&lt;/p&gt;
&lt;p&gt;모달 뒤에 body 영역을 반투명한 검정색 레이어로 덮어서 모달의 컨텐츠가 더 도드라지게 만든다.&lt;/p&gt;
&lt;p&gt;이 반투명한 검정색 영역을 주로 Dim 영역이라고 부른다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;보통 팝업창 내에 컨텐츠가 길어서 스크롤이 있는 경우에는&lt;/p&gt;
&lt;p&gt;팝업 내부에만 스크롤이 잘 되게 하기 위해서 Dim 영역 뒤에 있는 body의 scroll은 막는 경우가 많다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그리고 웹에서는 scroll 막는 것도 쉽게 처리할 수 있다.&lt;/p&gt;
&lt;p&gt;바로 팝업이 떴을 때 body 태그에 overflow: hidden을 걸어줘서 scroll을 못하게끔 막아버리는 방법이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. body 태그, overflow: hidden;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;코드 예시를 보면 다음과 같다.&lt;/p&gt;
&lt;p&gt;나는 주로 이런 기능은 &lt;b&gt;Higher Order Component(HOC)&lt;/b&gt;를 사용해서 만드는 것을 좋아한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;HOC로 만들면 Scroll Lock 기능이 필요한 컴포넌트를 리턴할 때&lt;/p&gt;
&lt;p&gt;HOC로 감싸서 리턴하는 방식으로 쉽게 기능을 적용할 수 있어서 편리하기 때문이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1590932158838&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export const withScrollLock = &amp;lt;P extends {}&amp;gt;(
  Feature: React.FC&amp;lt;P&amp;gt;,
): React.FC&amp;lt;P&amp;gt; =&amp;gt; (props: P) =&amp;gt; {
    const body = document.querySelector('body') as HTMLElement;
    
    useEffect(() =&amp;gt; {
      body.style.overflow = 'hidden';
      
      return () =&amp;gt; {
        body.style.removeProperty('overflow');
      };
    }, []);

    return &amp;lt;Feature { ...props } /&amp;gt;;
  };
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위와 같은 HOC를 만든 다음,&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1590932439489&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { withScrollLock } from '@/helpers';

const Modal = () =&amp;gt; {
  // ...
};

export default withScrollLock(Modal);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;요렇게 사용하면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 이 Modal 컴포넌트는 화면에 보여지는 즉시, body 태그에 overflow: hidden 속성을 적용시켜&lt;/p&gt;
&lt;p&gt;body의 스크롤을 막아버릴 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그런데 문제는 바로 이 방식이 &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;i&gt;&lt;b&gt;모바일 safari 브라우저에서 안된다&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;는 것이다. 또르르..  .&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;모바일 safari에서 안된다는 말은 iOS 웹뷰에 적용할 수 없다는 말이다...ㅜㅜ&lt;/p&gt;
&lt;p&gt;그래서 iOS 디바이스에도 적용할 수 있는 새로운 방법을 찾아 나섰는데 처음 찾아낸 방법은 이거였다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. pointer-events: none;&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1590932675229&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export const withScrollLock = &amp;lt;P extends {}&amp;gt;(
  Feature: React.FC&amp;lt;P&amp;gt;,
): React.FC&amp;lt;P&amp;gt; =&amp;gt; (props: P) =&amp;gt; {
    const body = document.querySelector('body') as HTMLElement;
    const scrollPosition = window.pageYOffset;

    useEffect(() =&amp;gt; {
      body.style.overflow = 'hidden';
      body.style.pointerEvents = 'none';

      return () =&amp;gt; {
        body.style.removeProperty('overflow');
        body.style.removeProperty('pointer-events');
      };
    }, []);

    return &amp;lt;Feature { ...props } /&amp;gt;;
  };&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;pointer-events&lt;/b&gt;&lt;/span&gt;라는 CSS 속성을 이용한 방법이다.&lt;/p&gt;
&lt;p&gt;pointer-events 속성은 특정 element의 트리거 역할을 설정한다. 이 속성은 none을 적용하면 이벤트 핸들러가 등록된 상태의 element의 이벤트 트리거로서의 역할을 강제로 막을 수도 있다(!)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그리고 놀랍게도 위 방법으로 잘 해결되는 듯 보였다.!&lt;/p&gt;
&lt;p&gt;최신 디바이스가 아닌 예전 iOS 기기의 오래된 OS 버전으로 테스트해보기 전까지는...&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;최신 OS 에서는 위 방법으로 잘 되었으나 12 버전 이하 기기로 테스트해보니 제대로 기능을 하지 못했다...&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3. touchmove eventListener&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;그 다음 고심해본 방법은 &lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;touchmove&lt;/b&gt;&lt;/span&gt; 이벤트 리스너를 달아서 &lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;e.preventDefault();&lt;/b&gt;&lt;/span&gt;로 기본 터치 동작을 막아버리는 것이었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1590933063261&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export const withScrollLock = &amp;lt;P extends {}&amp;gt;(
  Feature: React.FC&amp;lt;P&amp;gt;,
): React.FC&amp;lt;P&amp;gt; =&amp;gt; (props: P) =&amp;gt; {
    const body = document.querySelector('body') as HTMLElement;
    const lockScroll = e =&amp;gt; e.preventDefault();

    useEffect(() =&amp;gt; {
      body.addEventListener('touchmove', lockScroll, { passive: false });
      body.style.overflow = 'hidden';

      return () =&amp;gt; {
        body.removeEventListener('touchmove', lockScroll, { passive: false });
        body.style.removeProperty('overflow');
      };
    }, []);

    return &amp;lt;Feature { ...props } /&amp;gt;;
  };&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 방법은 아주 확실하게 모든 iOS 디바이스에서 완벽하게 스크롤을 차단하지만 치명적인 문제가 있다.&lt;/p&gt;
&lt;p&gt;바로 모달 창 안의 child element의 스크롤도 전부 막아버린다는 것이다..!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그러니까 body 태그의 touchmove 기본 동작을 막아버리는데 event 버블링, 캡쳐링에 의해&lt;/p&gt;
&lt;p&gt;하위 요소들의 touchmove 까지 막힌다는 문제가 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;모달 내부 컨텐츠 길이가 길어서 스크롤이 필요한 경우에 모달 내부 컨텐츠의 스크롤이 작동하지 않게 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4. body-scroll-lock npm package&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;사실 이 &lt;a href=&quot;https://github.com/willmcpo/body-scroll-lock&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;npm 패키지&lt;/a&gt;를 다운받아 사용하는 것이 가장 쉬운 방법일 것이다.&lt;/p&gt;
&lt;p&gt;모든 디바이스에서 Scroll Lock을 완벽히 적용할 수 있도록 도와주는 패키지이고 TypeScript도 지원된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1590936178983&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { disableBodyScroll, enableBodyScroll } from 'body-scroll-lock';

export const withScrollLock = &amp;lt;P extends {}&amp;gt;(
  Feature: React.FC&amp;lt;P&amp;gt;,
): React.FC&amp;lt;P&amp;gt; =&amp;gt; (props: P) =&amp;gt; {
    const body = document.querySelector('body') as HTMLElement;

    useEffect(() =&amp;gt; {
      disableBodyScroll(body);

      return () =&amp;gt; {
        enableBodyScroll(body);
      };
    }, []);

    return &amp;lt;Feature { ...props } /&amp;gt;;
  };&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;사용 방법도 이렇게 간단하다. 그런데 이 패키지도 body 하위 요소들의 스크롤을 막아버린다는 문제가 여전히 있다.&lt;/p&gt;
&lt;p&gt;docs를 읽어보면 &lt;b&gt;allowTouchMove&lt;/b&gt;라는 속성을 통해 특정 요소의 스크롤은 허용하는 옵션이 있는데,&lt;/p&gt;
&lt;p&gt;예제와 동일하게 적용했는데도 하위 iOS 디바이스에서 제대로 동작하지 않았다.&lt;/p&gt;
&lt;p&gt;내가 잘 못 적용한 걸수도 있는데 일단 촉박한 시간 내에 잘 해결이 안되어 이 패키지는 패쓰!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5. position: fixed;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;돌고 돌아서 이 문제를 가지고 밤 늦게까지 야근하면서 별의 별 방법을 다 시도해보다가&lt;/p&gt;
&lt;p&gt;결국 최종 해결법으로 채택한 방법은 다음과 같다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1590936375888&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export const withScrollLock = &amp;lt;P extends {}&amp;gt;(
  Feature: React.FC&amp;lt;P&amp;gt;,
): React.FC&amp;lt;P&amp;gt; =&amp;gt; (props: P) =&amp;gt; {
    const body = document.querySelector('body') as HTMLElement;
    const scrollPosition = window.pageYOffset;

    useEffect(() =&amp;gt; {
      body.style.overflow = 'hidden';
      body.style.pointerEvents = 'none';
      body.style.position = 'fixed';
      body.style.top = `-${scrollPosition}px`;
      body.style.left = '0';
      body.style.right= '0';

      return () =&amp;gt; {
        body.style.removeProperty('overflow');
        body.style.removeProperty('pointer-events');
        body.style.removeProperty('position');
        body.style.removeProperty('top');
        body.style.removeProperty('left');
        body.style.removeProperty('right');

        window.scrollTo(0, scrollPosition);
      };
    }, []);

    return &amp;lt;Feature { ...props } /&amp;gt;;
  };&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;body 태그에 &lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;position: fixed;&lt;/b&gt;&lt;/span&gt; 속성을 주는 것이다!&lt;/p&gt;
&lt;p&gt;이 방법의 문제점은 혹시 모를 UI 부작용이 있을 수 있다는 것인데,&lt;/p&gt;
&lt;p&gt;일단 적어도 내가 작업하는 UI에서는 아무 문제 없이 완벽하게 스크롤이 차단되었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;다만, body 태그에 position: fixed를 걸기 때문에&lt;/p&gt;
&lt;p&gt;사용자가 body 스크롤을 어느 정도 내린 상태에서 모달 창을 켰을 때&lt;/p&gt;
&lt;p&gt;body 스크롤이 맨 위로 가버린다는 문제가 있었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 방법을 해결하기 위해 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;window.pageYOffset&lt;/b&gt;&lt;/span&gt; 속성을 이용해서&lt;/p&gt;
&lt;p&gt;모달 창이 화면에 보여진 시점의 스크롤 위치를 기억해두고,&lt;/p&gt;
&lt;p&gt;top 속성에 음수 값으로 적용한다. 그러면 그 스크롤 위치에 고정된 상태로 보여진다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그리고 모달 창이 꺼졌을 때에도 여전히 그 위치에 스크롤이 된 상태여야 하기 때문에&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;window.scrollTo()&lt;/b&gt;&lt;/span&gt; 메소드를 이용해 스크롤 위치를 이동시킨다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 방법이 현재까지 내가 알아 낸 여러 방법들 중에서 가장 완벽하게 모든 디바이스에서 Scroll Lock을 주는 방법이다.&lt;/p&gt;</description>
      <category>Frontend</category>
      <category>CSS</category>
      <category>JavaScript</category>
      <category>react</category>
      <category>ScrollLock</category>
      <category>리액트</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/201</guid>
      <comments>https://im-developer.tistory.com/201#entry201comment</comments>
      <pubDate>Mon, 1 Jun 2020 00:02:57 +0900</pubDate>
    </item>
    <item>
      <title>[알고리즘/자바스크립트] Codility, MaxSliceSum 문제 풀이</title>
      <link>https://im-developer.tistory.com/202</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;slice.jpeg&quot; data-origin-width=&quot;2500&quot; data-origin-height=&quot;1875&quot; width=&quot;323&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sCPxE/btqEwfS99ES/iSmfMI3KOHsIgGqUrZKyak/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sCPxE/btqEwfS99ES/iSmfMI3KOHsIgGqUrZKyak/img.jpg&quot; data-alt=&quot;&amp;amp;quot;slice&amp;amp;quot;라는 단어에서 파생된 썸네일 사진... &quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sCPxE/btqEwfS99ES/iSmfMI3KOHsIgGqUrZKyak/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsCPxE%2FbtqEwfS99ES%2FiSmfMI3KOHsIgGqUrZKyak%2Fimg.jpg&quot; data-filename=&quot;slice.jpeg&quot; data-origin-width=&quot;2500&quot; data-origin-height=&quot;1875&quot; width=&quot;323&quot; height=&quot;NaN&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;&quot;slice&quot;라는 단어에서 파생된 썸네일 사진... &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;MaxSliceSum&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;A non-empty array A consisting of N integers is given. A pair of integers (P, Q), such that 0 &amp;le; P &amp;le; Q &amp;lt; N, is called a&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;i&gt;slice&lt;/i&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;of array A. The&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;i&gt;sum&lt;/i&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;of a slice (P, Q) is the total of A[P] + A[P+1] + ... + A[Q].&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Write a function:&lt;/p&gt;
&lt;pre id=&quot;code_1590928845493&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(A);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;that, given an array A consisting of N integers, returns the maximum sum of any slice of A.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;For example, given array A such that: &lt;b&gt;[3, 2, -6, 4, 0]&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;the function should return 5 because:&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: square;&quot; data-ke-list-type=&quot;square&quot;&gt;
&lt;li&gt;(3, 4) is a slice of A that has sum 4,&lt;/li&gt;
&lt;li&gt;(2, 2) is a slice of A that has sum &amp;minus;6,&lt;/li&gt;
&lt;li&gt;(0, 1) is a slice of A that has sum 5,&lt;/li&gt;
&lt;li&gt;no other slice of A has sum greater than (0, 1).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;Write an&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;b&gt;efficient&lt;/b&gt;&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;algorithm for the following assumptions:&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: square;&quot; data-ke-list-type=&quot;square&quot;&gt;
&lt;li&gt;N is an integer within the range [&lt;span&gt;1&lt;/span&gt;..&lt;span&gt;1,000,000&lt;/span&gt;];&lt;/li&gt;
&lt;li&gt;each element of array A is an integer within the range [&lt;span&gt;&amp;minus;1,000,000&lt;/span&gt;..&lt;span&gt;1,000,000&lt;/span&gt;];&lt;/li&gt;
&lt;li&gt;the result will be an integer within the range [&amp;minus;2,147,483,648..2,147,483,647].&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;오늘 풀어본 문제는 여기 저기서 자주 나오는 문제이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;예전 부트캠프에서도 비슷한 문제를 풀었던 기억이 있고 여러 알고리즘 사이트에 항상 단골로 있는 문제다.&lt;/p&gt;
&lt;p&gt;그런데 오랜만에 풀어봤더니 기억이 다 리셋됐는지 어떻게 푸는 지 한참 고민하다가 여러 시행착오 끝에 풀었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;문제는 간단하다. 음수를 포함하여 숫자가 든 배열이 주어졌을 때&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;해당 배열에서 연속적인 숫자의 합이 가장 큰 숫자를 리턴하는 문제이다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;예를 들어서 배열이 [3, 2, -6, 4, 0]이 주어진다면 연속하는 숫자의 합이 3 + 2인 5가 가장 크기 때문에 5를 리턴해야 한다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;만약 배열의 숫자가 모두 음수라면, 즉 [-1, -5, -2, -7]이라면 제일 큰 음수인 -1이 리턴되어야 한다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;(음수끼리는 더할 수록 점점 더 숫자가 작아지므로...)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1590929528502&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(A) {
  let max = 0;

  A.reduce((sum, currentNum) =&amp;gt; {
    if ((sum + currentNum) &amp;lt; max) {
      return 0;
    }

    return max = sum + currentNum;
  }, 0);
    
  return max;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;처음에 음수를 제대로 고려하지 않고 위와 같이 풀었다가 테스트 케이스 대부분에서 틀려서 25점이 나왔다.  &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;대충 컨셉은 이렇다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;max라는 변수에 초기값 0을 할당해둔다.&lt;/li&gt;
&lt;li&gt;A 배열을 순회하면서 누적해서 더한 값, sum에 현재 숫자를 더해서 max 변수와 크기를 비교한다.&lt;/li&gt;
&lt;li&gt;max보다 크기가 작으면 답이 될 수 없으므로 누적값을 0으로 초기화시키고,&lt;/li&gt;
&lt;li&gt;max보다 크기가 크거나 같으면 현재 숫자를 누적한 값을 max에 할당한다.&lt;/li&gt;
&lt;li&gt;순회가 끝나면 max를 리턴한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;i&gt;이렇게 되면 음수가 섞여있으면 죄다 음수를 무시하고 0을 리턴해버리는데다가&lt;/i&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;i&gt;음수만 있는 배열은 무조건 0을 리턴하게 되어 버린다.&lt;/i&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그래서 0을 고려해서 다시 고민 고민한 끝에 다음과 같이 문제를 풀었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1590929119924&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(A) {
  let max = 0;

  const sum = A.reduce((prevSum, currentNum) =&amp;gt; {
    let nextSum = prevSum + currentNum;
 
    if (nextSum &amp;lt; 0) {
      return 0;
    }

    if (nextSum &amp;lt; max) {
      return nextSum;
    }

     return max = nextSum;
  }, 0);

  return max === 0 ? Math.max(...A) : max;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;누적된 값에 현재 숫자를 더한 값, nextSum이 0보다 작으면 누적된 값을 0으로 초기화한다.&lt;/li&gt;
&lt;li&gt;만약 0보다 작지는 않지만 max보다 작으면 그 숫자에 계속 값을 누적한다.&lt;/li&gt;
&lt;li&gt;만약 max보다 크다면 max를 그 누적 값으로 교체한다.&lt;/li&gt;
&lt;li&gt;배열의 순회가 끝나고 max 변수의 값이 0이라면 배열의 값들이 모두 음수나 0이라는 뜻이므로, A 배열의 Max 값을 구해 return한다.&lt;/li&gt;
&lt;li&gt;0이 아니라면 max 값을 바로 리턴하면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;2중 반복문이 있거나 하지 않고 flat하게 배열을 순회하면 답이 나오기 때문에 &lt;b&gt;O(N)&lt;/b&gt;의 시간 복잡도를 가진다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/crcUbz/btqExBHvg0c/I6KHz5CWp77W9BmGS165R0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/crcUbz/btqExBHvg0c/I6KHz5CWp77W9BmGS165R0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/crcUbz/btqExBHvg0c/I6KHz5CWp77W9BmGS165R0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcrcUbz%2FbtqExBHvg0c%2FI6KHz5CWp77W9BmGS165R0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3yv1P/btqEw2r2zK3/f1gAobnzfUYwk6VbjFNg8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3yv1P/btqEw2r2zK3/f1gAobnzfUYwk6VbjFNg8K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3yv1P/btqEw2r2zK3/f1gAobnzfUYwk6VbjFNg8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3yv1P%2FbtqEw2r2zK3%2Ff1gAobnzfUYwk6VbjFNg8K%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Algorithm</category>
      <category>Algorithm</category>
      <category>codility</category>
      <category>JavaScript</category>
      <category>알고리즘</category>
      <category>자바스크립트</category>
      <category>코딩</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/202</guid>
      <comments>https://im-developer.tistory.com/202#entry202comment</comments>
      <pubDate>Sun, 31 May 2020 22:16:11 +0900</pubDate>
    </item>
    <item>
      <title>비율에 따라 줄어드는 SVG 이미지 구현하기 with CSS 고군분투</title>
      <link>https://im-developer.tistory.com/200</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;462&quot; height=&quot;NaN&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vrWsl/btqEnHg3FeR/gKGpobh7qNUUp9erFYVrC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vrWsl/btqEnHg3FeR/gKGpobh7qNUUp9erFYVrC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vrWsl/btqEnHg3FeR/gKGpobh7qNUUp9erFYVrC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvrWsl%2FbtqEnHg3FeR%2FgKGpobh7qNUUp9erFYVrC0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;462&quot; height=&quot;NaN&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;이번 주에 회사에서 개발하다가 씨름했던 &lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;&quot;비율에 따라 줄어드는 이미지 구현기&quot;&lt;/b&gt;&lt;/span&gt;를 정리해보려고 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;음, 그러니까 주어졌던 요구사항은 이러했다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;우리는 리액트로 개발을 하고 있다.&lt;/li&gt;
&lt;li&gt;이미지는 &lt;b&gt;SVG&lt;/b&gt;로 주어지며 되도록이면 .svg 파일이 아닌&amp;nbsp;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;SVG를 리턴하는 React 컴포넌트&lt;/b&gt;&lt;/span&gt;로 처리되었으면 한다.&lt;/li&gt;
&lt;li&gt;예를 들어서 이미지의 사이즈가 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;300 * 200&lt;/b&gt;&lt;/span&gt;이고 이미지를 감싸는 컨테이너 사이즈가 &lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;400 * 200&lt;/b&gt;&lt;/span&gt;이라고 해보자.&lt;br /&gt;이 경우에 &lt;b&gt;이미지의 높이가 200보다 작아진다면 이미지 사이즈는 400 * 200의 비율대로 줄어들어야 한다.&lt;/b&gt;&lt;br /&gt;반대로 &lt;b&gt;이미지의 높이가 200보다 커진다면 이미지는 커지면 안되고 양쪽 여백만 늘어나야 한다.&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;특정 비율대로 줄어들고 늘어나는 이미지를 구현하는 일은 &lt;span style=&quot;color: #333333;&quot;&gt;약간의 CSS 트릭을 쓴다면 &lt;/span&gt;그렇게 어렵지는 않다.&lt;/p&gt;
&lt;p&gt;예를 들어서 300 * 200 사이즈의 이미지가 400 * 200의 비율대로 줄어들고 늘어나야 한다면??&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;265&quot; data-theme-id=&quot;light&quot; data-default-tab=&quot;result&quot; data-user=&quot;JY712&quot; data-slug-hash=&quot;bGVZmao&quot; data-pen-title=&quot;Resizable image in certain ratio&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/JY712/pen/bGVZmao&quot;&gt; Resizable image in certain ratio&lt;/a&gt; by juyeonH (&lt;a href=&quot;https://codepen.io/JY712&quot;&gt;@JY712&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;
&lt;script src=&quot;https://static.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;다음과 같은 trick으로 해결할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1590310164203&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.container {
  position: relative;
  overflow: hidden;
  height: 0;
  padding-top: calc(200 / 400 * 100%);
  background: rgba(0, 0, 0, 0.5);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이미지를 감싸는 컨테이너에 위와 같은 css를 줌으로써 전체 높이를 padding-top을 통해 조절하며,&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;calc(200 / 400 * 100%)&lt;/b&gt;&lt;/span&gt;를 통해 400 * 200 비율로 계산하도록 조정한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1590310320756&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;img {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
  margin: auto;
  height: 100%;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;안의 이미지에는 absolute 포지션에 top, left, bottom, right에 모두 0을 주고 height를 container의 높이로 맞춘다.&lt;/p&gt;
&lt;p&gt;margin: auto로 가운데 정렬을 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위 trick은 W3Schools 사이트에도 나와있는 방법이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.w3schools.com/howto/howto_css_aspect_ratio.asp&quot;&gt;https://www.w3schools.com/howto/howto_css_aspect_ratio.asp&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1590310421293&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;How To - Aspect Ratio / Height Equal to Width&quot; data-og-description=&quot;How TO - Aspect Ratio Learn how to maintain the aspect ratio of an element with CSS. Aspect Ratio Create flexible elements that keep their aspect ratio (4:3, 16:9, etc.) when resized: What is aspect ratio? The aspect ratio of an element describes the propo&quot; data-og-host=&quot;www.w3schools.com&quot; data-og-source-url=&quot;https://www.w3schools.com/howto/howto_css_aspect_ratio.asp&quot; data-og-url=&quot;https://www.w3schools.com/howto/howto_css_aspect_ratio.asp&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bsY0zl/hyF93Inwjf/YnJGTZURntx5kIKVHMmtrk/img.png?width=653&amp;amp;height=331&amp;amp;face=0_0_653_331&quot;&gt;&lt;a href=&quot;https://www.w3schools.com/howto/howto_css_aspect_ratio.asp&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.w3schools.com/howto/howto_css_aspect_ratio.asp&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bsY0zl/hyF93Inwjf/YnJGTZURntx5kIKVHMmtrk/img.png?width=653&amp;amp;height=331&amp;amp;face=0_0_653_331');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;How To - Aspect Ratio / Height Equal to Width&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;How TO - Aspect Ratio Learn how to maintain the aspect ratio of an element with CSS. Aspect Ratio Create flexible elements that keep their aspect ratio (4:3, 16:9, etc.) when resized: What is aspect ratio? The aspect ratio of an element describes the propo&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.w3schools.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2054&quot; data-origin-height=&quot;1144&quot; data-filename=&quot;2020-05-24 18.01.01.gif&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bktwSv/btqEn9dhIPh/Ya7RmhrK0lwkzZhr9kqfkk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bktwSv/btqEn9dhIPh/Ya7RmhrK0lwkzZhr9kqfkk/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bktwSv/btqEn9dhIPh/Ya7RmhrK0lwkzZhr9kqfkk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bktwSv/btqEn9dhIPh/Ya7RmhrK0lwkzZhr9kqfkk/img.gif&quot; data-origin-width=&quot;2054&quot; data-origin-height=&quot;1144&quot; data-filename=&quot;2020-05-24 18.01.01.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 위 gif처럼 300 * 200 사이즈의 이미지는 400 * 200의 컨테이너 안에서&amp;nbsp;&lt;/p&gt;
&lt;p&gt;컨테이너의 비율에 맞춰서 늘어났다 줄어들었다 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 방법의 문제점은 &lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;특정 사이즈 이상 늘어났을 때, (즉, 이미지 높이가 200이 넘어갈 때)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;이미지 크기를 fix 시킬 수 없다는 점이다(!)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;왜냐면 전체 컨테이너의 높이를 height이 아닌 padding-top으로 조정하고 있기 때문이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그리고 max-height은 있어도 max-padding-top이란 속성은 없다는 문제가 있다...  &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그러니까 이미지 높이가 200보다 작아지면 위 gif처럼 줄어들어야 하지만&lt;/p&gt;
&lt;p&gt;이미지 높이가 200보다 커지면 이미지 사이즈를 300 * 200으로 고정하고 양 옆의 여백만 늘어나게 만들어야 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;여기서 진짜 별의 별 방법을 다 써보면서 해봤는데 결론적으로 다음과 같이 해결했다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;265&quot; data-theme-id=&quot;light&quot; data-default-tab=&quot;result&quot; data-user=&quot;JY712&quot; data-slug-hash=&quot;VwvREVa&quot; data-pen-title=&quot;Resizable image2&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/JY712/pen/VwvREVa&quot;&gt; Resizable image2&lt;/a&gt; by juyeonH (&lt;a href=&quot;https://codepen.io/JY712&quot;&gt;@JY712&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;
&lt;script src=&quot;https://static.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1590311421002&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.container {
  height: calc(200 / 400 * 100vw);
  max-height: 200px;
  background: rgba(0, 0, 0, 0.5);
}

.image-container {
  width: 100%;
  height: 100%;
  background: url(&quot;https://dummyimage.com/300x200/000/fff&quot;) no-repeat center / contain;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;가장 핵심인 부분은 전체 컨테이너의 max-height을 200px로 고정시키고,&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;height&lt;/b&gt;&lt;/span&gt;을 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;200 / 400 * 100vw&lt;/b&gt;&lt;/span&gt;로 처리했다는 점이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;즉, 400 * 200 비율로 컨테이너 높이를 가변적으로 처리했다.&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;그리고 그 안에 들어가는 이미지를 &lt;b&gt;background&lt;/b&gt;로 처리했다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;이유는 background-size: contain을 통해 쉽게 이미지 영역을 자르지 않고 컨테이너 영역을 꽉 채우기 위해서이다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;전체 컨테이너가 400 * 200 비율로 줄어들었다 늘어났다 하며,&lt;/p&gt;
&lt;p&gt;그 안의 이미지는 부모 컨테이너를 꽉 채우기 때문에 원하던 스펙대로 구현된다.&lt;/p&gt;
&lt;p&gt;게다가 전체 컨테이너에 max-height을 줌으로써 이미지 높이가 200이 넘어가면 더 이상 늘어나지 않도록 구현했다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;2020-05-24 18.13.41.gif&quot; data-origin-width=&quot;2054&quot; data-origin-height=&quot;1144&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRdccf/btqEmvIFKFh/3fZoxfoEbktRRjF2ex3IK0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRdccf/btqEmvIFKFh/3fZoxfoEbktRRjF2ex3IK0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRdccf/btqEmvIFKFh/3fZoxfoEbktRRjF2ex3IK0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bRdccf/btqEmvIFKFh/3fZoxfoEbktRRjF2ex3IK0/img.gif&quot; data-filename=&quot;2020-05-24 18.13.41.gif&quot; data-origin-width=&quot;2054&quot; data-origin-height=&quot;1144&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;짜잔~!  &lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 문제는 &lt;b&gt;SVG 컴포넌트를 리턴하는 React 컴포넌트&lt;/b&gt;를 &lt;b&gt;Background image&lt;/b&gt;로 어떻게 설정하냐는 것이었는데,&lt;/p&gt;
&lt;p&gt;요거는 React에 기본 내장되어 있는 ReactDOMServer의 renderToStaticMarkup 메소드로 해결했다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1590311855374&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ReactDOMServer.renderToStaticMarkup(element)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;ReactDOMServer에는 renderToString()과&amp;nbsp;&lt;/span&gt;renderToStaticMarkup()이란 메소드가 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 id=&quot;rendertostring&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;renderToString()&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;renderToString은 React 엘리먼트를 최초의 HTML에 렌더하는 메소드로, HTML string을 리턴한다. server에서 HTML을 생성한 후, 페이지 첫 로드 시 빠르게 마크업을 내려줄 때 사용한다. SEO 목적으로 빠르게 서버사이드 렌더할 때 유용한 메소드이다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #333333;&quot;&gt;renderToStaticMarkup()&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;renderToStaticMarkup은 renderToString과 비슷한 컨셉이지만 React가 내부적으로 사용하는 DOM attribute들을 생성하지 않는다. 따라서 간단한 static 페이지를 React로 생성할 때 유용한 메소드이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1590312418839&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { renderToStaticMarkup } from 'react-dom/server';
import React from 'react';

const SvgComponent = () =&amp;gt; (
  &amp;lt;svg xmlns='http://www.w3.org/2000/svg'&amp;gt;
    &amp;lt;rect fill='red' width={100} height={100} /&amp;gt;
  &amp;lt;/svg&amp;gt;
);

const ParentComponent = () =&amp;gt; {
  const svgString = encodeURIComponent(renderToStaticMarkup(&amp;lt;SvgComponent /&amp;gt;));

  return (
    &amp;lt;div
      style={{
        backgroundImage: `url('data:image/svg+xml;utf8, ${svgString}')`,
      }}
    /&amp;gt;
  );
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;따라서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;renderToStaticMarkup의 인자로 SVG 컴포넌트를 넣은 후&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;encodeURIComponent로 인코딩하면 &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;background 이미지로 처리할 수 있게 된다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;+ 위와 같이 처리했다가 그 다음 날 회사 가서 생각해보니 부모 컨테이너에 height, max-height을 주었기 때문에 자식 컴포넌트에 이미지를 굳이 background로 처리하지 않아도 된다는 것을 깨달았다...&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그래서 background로 처리되는 로직을 지우고 부모 컨테이너 안에 svg 컴포넌트를 바로 넣되, svg의 width, height을 '100%'로 변경하여 최종 반영했다~!&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Reference&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/57789530/render-react-svg-component-as-backgroundimage&quot;&gt;https://stackoverflow.com/questions/57789530/render-react-svg-component-as-backgroundimage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://reactjs.org/docs/react-dom-server.html&quot;&gt;https://reactjs.org/docs/react-dom-server.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend</category>
      <category>CSS</category>
      <category>JavaScript</category>
      <category>react</category>
      <category>리액트</category>
      <category>코딩</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/200</guid>
      <comments>https://im-developer.tistory.com/200#entry200comment</comments>
      <pubDate>Sun, 24 May 2020 18:37:17 +0900</pubDate>
    </item>
    <item>
      <title>[알고리즘/자바스크립트] Codility, Dominator 문제 풀이</title>
      <link>https://im-developer.tistory.com/199</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Dominator&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;An array A consisting of N integers is given. The&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;i&gt;dominator&lt;/i&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;of array A is the value that occurs in more than half of the elements of A.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;For example, consider array A such that [3, 4, 3, 2, 3, -1, 3, 3].&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;The dominator of A is 3 because it occurs in 5 out of 8 elements of A (namely in those with indices 0, 2, 4, 6 and 7) and 5 is more than a half of 8.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Write a function&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;function solution(A);&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;that, given an array A consisting of N integers, returns index of any element of array A in which the dominator of A occurs. The function should return &amp;minus;1 if array A does not have a dominator.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;For example, given array A such that [3, 4, 3, 2, 3, -1, 3, 3]&lt;/p&gt;
&lt;p&gt;the function may return 0, 2, 4, 6 or 7, as explained above.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Write an&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;b&gt;efficient&lt;/b&gt;&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;algorithm for the following assumptions:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;N is an integer within the range [&lt;span&gt;0&lt;/span&gt;..&lt;span&gt;100,000&lt;/span&gt;];&lt;/li&gt;
&lt;li&gt;each element of array A is an integer within the range [&lt;span&gt;&amp;minus;2,147,483,648&lt;/span&gt;..&lt;span&gt;2,147,483,647&lt;/span&gt;].&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;오늘도 비교적 쉬운 난이도의 알고리즘 문제 하나를 풀어보았다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;(요즘 너무 어려운 알고리즘 문제로 끙끙거리다가 좌절감 느끼고 아예 포기해버리는 대신에&lt;/p&gt;
&lt;p&gt;적당한 난이도의 문제를 꾸준히 풀려고 노력하고 있다.)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;문제를 간단히 해석하면 이렇다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;숫자로 구성된 배열 A가 [3, 4, 3, 2, 3, -1, 3, 3]와 같이 주어졌다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;이 배열의 Dominator는 바로 숫자 3이다. 왜냐하면 숫자 8개 중에 5개(0, 2, 4, 6, 7번째 숫자)가 3이기 때문이다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;바로 Dominator 숫자가 존재하는 인덱스 아무거나 하나를 리턴하면 되는 문제이다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;즉, 배열 A의 Dominator인 숫자 3이 0, 2, 4, 6, 7번째 존재하므로 이 숫자들 중에 아무거나 하나만 리턴하면 된다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;단, Dominator가 존재하지 않는다면 -1을 리턴하면 된다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;배열 길이의 범위는&lt;b&gt; 0~100,000&lt;/b&gt;이며, 배열에 들어가는 숫자의 범위는 &lt;b&gt;&amp;minus;2,147,483,648 ~ 2,147,483,647&lt;/b&gt;이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1590305846114&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(A) {
    const counter = A.reduce((counter, current, index) =&amp;gt; {
        counter[current] = counter[current] ? counter[current].concat(index) : [index];
        return counter;
    }, {});
    
    const sorted = Object.values(counter)
        .sort((left, right) =&amp;gt; right.length - left.length);
    const isDominator = sorted[0].length &amp;gt; (A.length / 2);
    
    return isDominator ? sorted[0][0] : -1;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;처음에 생각했던 방법은 이렇다.&lt;/p&gt;
&lt;p&gt;배열을 순회하면서 counter라는 객체에 값을 저장하는데,&lt;/p&gt;
&lt;p&gt;counter의 key로 배열의 값을 넣고 value로 배열의 index를 저장한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그러니까 즉, &lt;span style=&quot;color: #333333;&quot;&gt;[3, 4, 3, 2, 3, -1, 3, 3] 배열을 순회하면&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;'3': [0] =&amp;gt; &lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;counter 객체에 먼저 '3': [0]을 저장한다. (3이라는 숫자가 0번째에 있으므로)&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;'4': [1] =&amp;gt; &lt;/span&gt;그 다음 '4': [1]을 저장한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;'3': [0, 2] =&amp;gt; 그 다음 숫자 3은 counter에 이미 존재하므로 '3': [0]에 2(두 번째 숫자 3의 인덱스)를 추가한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;...&lt;/p&gt;
&lt;p&gt;이렇게 반복하고 나면 counter 객체는 이렇게 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1590306691121&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{ '3': [0, 2, 4, 6, 7], '4': [1], '2': [3], '-1': [5] }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이제 위 counter 객체의 value값만 배열로 모아서 정렬을 하는데,&lt;/p&gt;
&lt;p&gt;각 배열의 길이를 기준으로 정렬한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1590306761255&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[[0, 2, 4, 6, 7], [1], [3], [5]]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;배열의 맨 첫번째 배열의 길이가 A 배열 길이의 절반보다 크면 Dominator이므로 그 배열의 첫번째 인덱스를 리턴한다.&lt;/p&gt;
&lt;p&gt;만약 Dominator가 아니라면 -1을 리턴한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 방법은 정확도 면에서는 100점을 받았지만&lt;/p&gt;
&lt;p&gt;전체 배열을 전부 순회하고 sort를 위해 다시 한 번 순회해야하므로 효율성 점수가 낮았다.&lt;/p&gt;
&lt;p&gt;그래서 위 방법을 개선해서 아래와 같이 풀고 통과했다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1590305874267&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(A) {
    const counter = {};
    const standard = A.length / 2;
    
    for (var i = 0; i &amp;lt; A.length; i++) {
        if (counter[A[i]]) {
            counter[A[i]].push(i);
        } else {
            counter[A[i]] = [i];
        }

        if (counter[A[i]].length &amp;gt; standard) {
            return counter[A[i]][0];
        }
    }
    
    return -1;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;A 배열을 for 문으로 순회하면서 아까와 같은 방식으로 counter 객체에 저장을 한다.&lt;/p&gt;
&lt;p&gt;다른 점이라면 저장을 할 때마다 A 배열 길이의 절반과 비교해서 Dominator인지 아닌지 판별을 한다는 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;만약에 순회하는 중간에 dominator를 발견하면 그 즉시 저장된 index를 리턴한다.&lt;/p&gt;
&lt;p&gt;만약에 for문을 다 순회했다면 dominator가 없었다는 뜻이므로 -1을 리턴한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 방법은 아주 최악의 경우에도 배열을 딱 한 번만 순회하면 되기 때문에 &lt;b&gt;O(N)&lt;/b&gt;의 시간 복잡도를 가진다.&lt;/p&gt;
&lt;p&gt;만약 Dominator를 빠르게 찾는다면 중간에 순회를 멈추기 때문에 최선의 경우에는 시간 복잡도가 &lt;b&gt;O(N * logN)&lt;/b&gt;이 될 수도 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dcZknD/btqEnImF0Pd/d5av189gZo7xl5C9KOpn80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dcZknD/btqEnImF0Pd/d5av189gZo7xl5C9KOpn80/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dcZknD/btqEnImF0Pd/d5av189gZo7xl5C9KOpn80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdcZknD%2FbtqEnImF0Pd%2Fd5av189gZo7xl5C9KOpn80%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kYe4t/btqEoA9wgvc/WTwWisHk9dALW0bYcHWdU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kYe4t/btqEoA9wgvc/WTwWisHk9dALW0bYcHWdU1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kYe4t/btqEoA9wgvc/WTwWisHk9dALW0bYcHWdU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkYe4t%2FbtqEoA9wgvc%2FWTwWisHk9dALW0bYcHWdU1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Algorithm</category>
      <category>Algorithm</category>
      <category>codility</category>
      <category>JavaScript</category>
      <category>알고리즘</category>
      <category>자바스크립트</category>
      <category>코딩</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/199</guid>
      <comments>https://im-developer.tistory.com/199#entry199comment</comments>
      <pubDate>Sun, 24 May 2020 17:00:10 +0900</pubDate>
    </item>
    <item>
      <title>memo, useMemo, useCallback으로 React 성능 최적화하기</title>
      <link>https://im-developer.tistory.com/198</link>
      <description>&lt;p&gt;&lt;a href=&quot;https://medium.com/swlh/optimizing-react-performance-with-memo-usememo-and-usecallback-11fb34f4a3fa&quot;&gt;https://medium.com/swlh/optimizing-react-performance-with-memo-usememo-and-usecallback-11fb34f4a3fa&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1589682296773&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Optimizing React performance with memo, useMemo, and useCallback&quot; data-og-description=&quot;Optimizing component performance through memoization is one of the most underused techniques in React. Memoization is a somewhat advanced&amp;hellip;&quot; data-og-host=&quot;medium.com&quot; data-og-source-url=&quot;https://medium.com/swlh/optimizing-react-performance-with-memo-usememo-and-usecallback-11fb34f4a3fa&quot; data-og-url=&quot;https://medium.com/swlh/optimizing-react-performance-with-memo-usememo-and-usecallback-11fb34f4a3fa&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cqt0wa/hyF44Nt55y/Iq5s8ihFIbO2QAxWja6T2K/img.jpg?width=1200&amp;amp;height=799&amp;amp;face=0_0_1200_799,https://scrap.kakaocdn.net/dn/x30lr/hyF3RCdGst/3qPJ1rlrs3DdNZehjE0SQk/img.jpg?width=60&amp;amp;height=39&amp;amp;face=0_0_60_39&quot;&gt;&lt;a href=&quot;https://medium.com/swlh/optimizing-react-performance-with-memo-usememo-and-usecallback-11fb34f4a3fa&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://medium.com/swlh/optimizing-react-performance-with-memo-usememo-and-usecallback-11fb34f4a3fa&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cqt0wa/hyF44Nt55y/Iq5s8ihFIbO2QAxWja6T2K/img.jpg?width=1200&amp;amp;height=799&amp;amp;face=0_0_1200_799,https://scrap.kakaocdn.net/dn/x30lr/hyF3RCdGst/3qPJ1rlrs3DdNZehjE0SQk/img.jpg?width=60&amp;amp;height=39&amp;amp;face=0_0_60_39');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Optimizing React performance with memo, useMemo, and useCallback&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Optimizing component performance through memoization is one of the most underused techniques in React. Memoization is a somewhat advanced&amp;hellip;&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;medium.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot;&gt;&lt;b&gt;- 아래 글은 위 글을 번역한 글입니다. -&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bF3Emi/btqEcncOBH2/tUXkfwJk09ttRw56nTfuS1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bF3Emi/btqEcncOBH2/tUXkfwJk09ttRw56nTfuS1/img.jpg&quot; data-alt=&quot;사진 출처:&amp;amp;amp;nbsp;https://knowledgeone.ca/5-factors-influencing-memory-process/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bF3Emi/btqEcncOBH2/tUXkfwJk09ttRw56nTfuS1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbF3Emi%2FbtqEcncOBH2%2FtUXkfwJk09ttRw56nTfuS1%2Fimg.jpg&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;사진 출처:&amp;nbsp;https://knowledgeone.ca/5-factors-influencing-memory-process/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;memoization&lt;/b&gt;&lt;/span&gt;을 통한 컴포넌트 성능 최적화는 React에서 가장 잘 안쓰이는 기술 중에 하나이다. Memoization은 React의 고급 기술 중에 하나이고, 95%의 경우에는 그닥 중요치 않다. React의 &lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;reconciliation 과정&lt;/b&gt;&lt;/span&gt;(React가 어떤 컴포넌트를 업데이트할 지 안할 지 여부를 결정하는 알고리즘적 방법론)과 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;가상 돔&lt;/b&gt;&lt;/span&gt;(virtual DOM, React가 DOM을 업데이트하는 방식)이 매우 빨라서 대부분의 경우 우리는 memoization을 통한 최적화가 얼마나 성능을 끌어올려주는 지 알아차리지 못한다. 대부분의 React 사용자들은 성능이 우리가 알아차릴 수 있을만큼 눈에 띄게 떨어졌을 때야 비로소 성능 최적화 도입을 고려해보기 시작한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;i&gt;컴포넌트의 성능을 최적화하기 위해 성능이 느려질때까지 기다리는 것이 정말 좋은 방법인가? &lt;br /&gt;&lt;/i&gt;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;i&gt;우리가 도로를 건설할 때 교통량 등을 고려하지 않고 그냥 원래의 기능만 하도록 하는 것이 옳은가?&lt;/i&gt;&lt;/span&gt;&lt;/b&gt;&lt;/blockquote&gt;
&lt;p&gt;물론 아니다! 성능이 문제가 될 때까지 기다리지 말고 지금 바로 성능 최적화 기술을 배우고 컴포넌트를 만들 때 사용해보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h1 id=&quot;6987&quot;&gt;useMemo&lt;/h1&gt;
&lt;p&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;useMemo&lt;/b&gt;&lt;/span&gt;는 매 번 render할 때마다 메모리가 많이 소모되는 값들을 계산하지 않고 functional components를 최적화하는데 도움을 준다. useMemo는 dependency 리스트를 생성하는 것을 도와주며, 그 중 하나가 변경되면 바로 값을 계산한다. 예를 들어 다음과 같은 컴포넌트를 생각해보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1589681786825&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// useMemo를 사용하지 않은 컴포넌트
import React from &quot;react&quot;;

const computeValueFromProp = (prop) =&amp;gt; {
  // 에너지가 많이 소모되는 계산이 일어남
}

const ComponentThatRendersOften = ({ prop1, prop2 }) =&amp;gt; {
  const valueComputedFromProp1 = computeValueFromProp(prop1);
  
  return (
    &amp;lt;&amp;gt;
       &amp;lt;div &amp;gt;{valueComputedFromProp1}&amp;lt;/div&amp;gt;
       &amp;lt;div &amp;gt;{prop2}&amp;lt;/div&amp;gt;
    &amp;lt;/&amp;gt;
  );
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위 컴포넌트가 다시 render되는 순간에 &lt;b&gt;valueComputedFromProp&lt;/b&gt;을 매 번 다시 계산할 것이다. 컴포넌트가 매우 작으면 상관없겠지만 만약 이 컴포넌트가 더 많은 props를 가진 큰 컴포넌트인데, state가 업데이트되었다고 생각해보자. prop이나 state가 바뀌는 순간 마다, 이 컴포넌트는 값들을 다시 계산하거나 다시 render할 것이다. 이 값들을 다시 계산할 필요가 없는 경우에도 굳이 다시 계산하는 것은 매우 비효율적이다. 이상적으로 &lt;b&gt;valueComputedFromProp1&lt;/b&gt;은 &lt;b&gt;&lt;u&gt;prop1이 변경되었을 때만 다시 계산되어야 하고&lt;/u&gt;&lt;/b&gt;, prop2가 변경되었을 때는 계산되지 않아야 한다. 그럼 이제 useMemo를 통해 prop1이 변경되었을 때만 값을 계산하도록 바꿔보자.&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1589682268241&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// useMemo를 사용한 예제
import React, { useMemo } from &quot;react&quot;;

const computeValueFromProp = (prop) =&amp;gt; {
  // 에너지가 많이 소모되는 계산이 일어남
}

const ComponentThatRendersOften = ({ prop1, prop2 }) =&amp;gt; {

const valueComputedFromProp1 = useMemo(() =&amp;gt; {
  return computeValueFromProp(prop1)
}, [prop1]);

return (
    &amp;lt;&amp;gt;
       &amp;lt;div &amp;gt;{valueComputedFromProp1}&amp;lt;/div&amp;gt;
       &amp;lt;div &amp;gt;{prop2}&amp;lt;/div&amp;gt;
    &amp;lt;/&amp;gt;
  );
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;이제 valueComputedFromProp1은 prop1이 변경될 때만 다시 계산된다.&lt;/b&gt; 만약에 prop2가 업데이트된 상태로 re-render가 실행되었다면 valueComputedFromProp1은 마지막으로 계산된 값을 사용하고 다시 계산하지 않는다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;useMemo는 &lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;2개의 인자&lt;/b&gt;&lt;/span&gt;를 가진다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;계산된 값을 return하는 callback 함수&lt;/li&gt;
&lt;li&gt;useMemo에게 언제 다시 계산해야할지 알려 줄 dependency 리스트 배열&lt;/li&gt;
&lt;/ol&gt;
&lt;p id=&quot;6802&quot;&gt;useMemo의 두 번째 인자로 넣은 [prop1]은 prop1이 변경되었을 때만 계산을 다시 하라고 알려주기 위한 배열이다. 만약 우리가 prop1뿐만 아니고 prop2가 변경되었을 때도 다시 계산하는 것이 필요하다면 배열은 [prop1, prop2]가 될 것이다. 만약에 최초의 mount 시에만 계산을 하고 싶다면 빈 배열 []을 넣으면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;useMemo는 복잡한 값들을 가지고 있고 자주 re-render되는 함수형 컴포넌트들이 아주 큰 성능 향상을 할 수 있도록 도와준다.&lt;/p&gt;
&lt;p id=&quot;b0fa&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h1 id=&quot;7531&quot;&gt;useCallback&lt;/h1&gt;
&lt;p&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;useCallback&lt;/b&gt;&lt;/span&gt;은 useMemo와 거의 비슷한 개념이다. 유일하게 다른 점이라면, useCallback은 &quot;값(value)&quot;을 기억(memoizing)하는 대신에 &lt;b&gt;&quot;함수(function)&quot;&lt;/b&gt;를 기억한다. 아래 예제를 살펴보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1589682856072&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, { useState } from 'react';

const ComponentThatRendersOften = ({ cb1, cb2 }) =&amp;gt; {
  const [state, setState] = useState(...);
  
  const expensiveFunction = () =&amp;gt; {
    // 에너지가 많이 소모되는 계산이 일어남
    ...    
    setState(...);
    cb1();
  };
  
  return (
    &amp;lt;button onClick={expensiveFunction} /&amp;gt;
  );
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;useMemo와 같이 위 컴포넌트는 re-render될 때마다 expensiveFunction을 다시 생성한다. 만약 많은 prop들을 가지고 있거나 state 변화가 많아 re-render가 자주 일어난다면 위와 같이 매 번 다시 함수를 생성하는 것이 에너지를 많이 소모하는 일일 것이다. 이 때 우리는 이 함수를 컴포넌트 스코프(scope, 범위) 밖으로 옮겨서 매 번 다시 렌더하지 않도록 만들 수도 있다. 그러나 그렇게 할 경우에는 prop들과 state, state setters들을 매 번 넘겨줘야 하기 때문에 매우 번거롭고 가독성이 떨어질 수 있다. 그렇다면 이제 이 함수를 useCallback을 사용하여 우리가 필요할 때만 다시 생성하도록 만들어보자.&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1589683207130&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, { useState, useCallback } from 'react';

const ComponentThatRendersOften = ({ cb1, cb2 }) =&amp;gt; {
  const [state, setState] = useState(...);

  const expensiveFunction = useCallback(() =&amp;gt; {
    // 에너지가 많이 소모되는 계산이 일어남
    setState(...);
    cb1();
  }, [cb1]);

  return (
    &amp;lt;button onClick={expensiveFunction} /&amp;gt;
  );
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;이제 이 컴포넌트는 expensiveFunction을 cb1이 변경된 후 re-render될 때만 다시 생성한다.&lt;/b&gt; (setState는 절대 변경되지 않으므로 dependency 배열에 넣을 필요가 없다.)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;useCallback&lt;/span&gt;는 &lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;2개의 인자&lt;/b&gt;&lt;/span&gt;를 가진다.&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;기억된 채로 리턴된 callback 함수&lt;/li&gt;
&lt;li&gt;useCallback에게 언제 함수를 재생성할 지 알려 줄 dependency 리스트 배열&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;useMemo와 동일하게 useCallback의 두 번째 인자로 넣은 배열 [cb1]은 cb1이 변경되었을 때만 다시 재 생성하라고 알려주기 위한 목적으로 사용되었다. 만약에 cb2와 state가 변경되었을 때도 재생성하고 싶다면 [cb1, cb2, state]가 될 것이다. 만약 첫 mount 시에만 생성하고 싶다면 빈 배열을 넣으면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;/span&gt;useCallback또한 useMemo처럼 자주 변경되어 많은 re-render를 일으키는 함수가 포함된 함수형 컴포넌트에 사용하면 매우 큰 성능 향상을 일으킬 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h1 id=&quot;19ca&quot;&gt;memo&lt;/h1&gt;
&lt;p&gt;다른 memoization 기술들과는 논외로, &lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;memo&lt;/b&gt;&lt;/span&gt;는 아마 가장 개념화하고 이해하기 어렵지만 가장 중요한 기술 중 하나일 것이다. 간단하게 말하면 memo는 우리의 컴포넌트가 항상 기본적으로 re-render되는 것을 막는다. 즉, 컴포넌트 내부의 state나 prop들이 &lt;b&gt;얕게(shallowly)&lt;/b&gt;&amp;nbsp;변경되었을 때만 re-render되도록 한다. 아래 예제를 살펴보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1589683894212&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, { useState } from 'react';

const Text = ({ text }) =&amp;gt; {
  return &amp;lt;p &amp;gt;{text}&amp;lt;/p&amp;gt;
};

const ParentComponent = () =&amp;gt; {
  const [firstName, setFirstName] = useState('');
  const [lastName, setLastName] = useState('');
  
  return (
    &amp;lt;&amp;gt;
      &amp;lt;input onChange={(e) =&amp;gt; setFirstName(e.target.value)} /&amp;gt;
      &amp;lt;input onChange={(e) =&amp;gt; setLastName(e.target.value)} /&amp;gt;
      &amp;lt;Text text='Your name is:' /&amp;gt;
      &amp;lt;Text text={firstName} /&amp;gt;
      &amp;lt;Text text={lastName} /&amp;gt;
    &amp;lt;/&amp;gt;
  );
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;기본적으로 ParentComponent가 업데이트되면 3개의 Text 컴포넌트들이 re-render된다. 사용자가 각 input 박스에 얼마나 많은 글자들을 입력할지 생각해보자. 매 번 input에 입력되는 text가 바뀔 때마다 Text 컴포넌트가 re-render될 것이다. 위 예제는 매우 간단하지만, 많은 child 컴포넌트를 가지는 큰 컴포넌트에서 얼마나 많은 성능 이슈가 야기할 지 쉽게 추측할 수 있다. 이제 memo 기술을 이용해 이 컴포넌트를 최적화시키고 모든 기본 default re-render를 막아보자.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1589684221339&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, { useState, memo } from 'react';

// 컴포넌트를 memo로 감싼다.
const Text = memo(({ text }) =&amp;gt; {
  return &amp;lt;p &amp;gt;{text}&amp;lt;/p&amp;gt;
});

const ParentComponent = () =&amp;gt; {
  const [firstName, setFirstName] = useState('');
  const [lastName, setLastName] = useState('');
  
  return (
    &amp;lt;&amp;gt;
      &amp;lt;input onChange={(e) =&amp;gt; setFirstName(e.target.value)} /&amp;gt;
      &amp;lt;input onChange={(e) =&amp;gt; setLastName(e.target.value)} /&amp;gt;
      &amp;lt;Text text='Your name is:' /&amp;gt;
      &amp;lt;Text text={firstName} /&amp;gt;
      &amp;lt;Text text={lastName} /&amp;gt;
    &amp;lt;/&amp;gt;
  );
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 firstName과 lastName이 변경될 때마다 그것과 연관된 각각의 Text 컴포넌트만 업데이트된다. memo로 감싸진 Text 컴포넌트는 해당 컴포넌트의 prop들이 얕게(shallowly) 변경되었을 때만 다시 render된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;만약 firstName과 관련된 input 값이 변경되면, 그 값을 prop으로 받는 두 번째 Text 컴포넌트만 업데이트된다. &lt;b&gt;컴포넌트가 다시 업데이트할 지 말 지를 결정할 때, Text 컴포넌트는 현재의 text prop과 새로운 text prop을 비교하여 prop이 동일하면 update하지 않는다.&lt;/b&gt; 때문에 첫 번째와 세 번째 Text 컴포넌트는 전과 같은 prop을 가지고 있어 업데이트가 되지 않는다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;여기까지 memo가 그렇게 많이 복잡해보이지는 않는다. 단지 컴포넌트가 기본적으로 re-render하는 특성을 컴포넌트의 prop이 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;얕게&lt;/b&gt;&lt;/span&gt; 변경되었을 때만 re-render하도록 변경할 뿐이다. 이제 우리는 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;얕은(shallowly)&lt;/b&gt;&lt;/span&gt;이라는 단어가 강조되었다는 것을 알아차릴 수 있다. memo의 가장 큰 특징이다. 어떤 컴포넌트를 업데이트할 지 결정하기 위해 prop들을 비교할 때 memo는 prop들을 얕게 비교한다. prop value가 변화할 때, 그에 따라 memo가 우리의 컴포넌트를 다시 re-render 하는지 안하는지 살펴보자.&lt;/p&gt;
&lt;p id=&quot;9fb6&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1589685140634&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 이러한 prop 변화는 memo가 항상 유효한 re-render를 일으킨다.
Before: 'hi'
After:  'bye'

Before: 0
After:  1

Before: [1, 2, 3]
After:  [1, 2, 4]

Before: { a: 'apple', p: 'peach' }
After:  { a: 'apple', p: 'plum' }

Before: undefined
After: null

// 이러한 prop 변화는 memo가 re-render를 일으킨다고 보장하지 않는다.
Before: { person: { name: { first: 'henry' } }
After:  { person: { name: { first: 'john' } } }

Before: [ { greeting: 'hi' } ]
After: [ { greeting: 'bye' } ]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;memo는 1 레벨 깊이의 얕은 비교를 통한 업데이트만 보장하기 때문에 만약 컴포넌트가 &lt;span style=&quot;color: #333333;&quot;&gt;(1 레벨 이상의) &lt;/span&gt;깊은 prop 값들을 가지고 있다면 memo를 사용하지 않는 것이 좋다. 만약 우리 컴포넌트가 깊은 prop 구조를 가지고 있다면 memoize보다는 다음 예제와 같은 다른 방법을 고려해볼 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1589685382960&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, { memo } from 'react';

const ComponentWithPotentialComplexProps = ({ complexProp }) =&amp;gt; {
  return (...)
};

const MemoizedComponent = memo(ComponentWithPotentialComplexProps);

const ParentComponent = ({ prop ) =&amp;gt; {
  return (
    &amp;lt;&amp;gt;
      &amp;lt;MemoizedComponent
        complexProp={{ animal: 'cat' }}
      /&amp;gt;
      &amp;lt;ComponentWithPotentialComplexProps
        complexProp={prop}
      /&amp;gt;
    &amp;lt;/&amp;gt;
  );
};&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위 예제의 첫 번째 경우에서 우리는 하드 코딩된 prop({ animal: 'cat' })이 절대 값을 바꾸지 않을 것이라는 것을 알고 있다. 따라서 우리는 그 값이 깊은 비교할 필요가 없다는 것을 알기 때문에 안전하게 memoize할 수 있다. 그러나 두 번째 경우에는 어떤 prop이 전달될 지 모르고 그 prop이 몇 차수일 지 모르기 때문에 memoize를 사용할 수 없다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h1 id=&quot;448a&quot;&gt;When to use each&lt;/h1&gt;
&lt;p&gt;memoization은 언제 사용해야 할 지 판단하는 것이 가장 어렵다. 아래는 memoization이 좋은 선택일 지 판단하는 데 도움을 줄 간단한 체크리스트이다. when to use 경우를 대부분 만족하고 when not to use 경우에 해당하지 않는다면 memoization을 사용하는 것이 좋다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;useMemo&lt;/b&gt;&lt;/h4&gt;
&lt;p id=&quot;cad7&quot;&gt;&lt;b&gt;What it does&lt;/b&gt;:&lt;/p&gt;
&lt;p&gt;dependency들이 변경되었을 때만 값(value)을 다시 계산하도록 기억(memoize)한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;4bbd&quot;&gt;&lt;b&gt;When to use it:&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 re-render 시가 아니라 특정 prop이나 state가 변경되었을 때만 값을 다시 계산하고 싶을 때&lt;/li&gt;
&lt;li&gt;당신의 값 계산 프로세스가 매우 에너지 소모가 많을 때 (regex's, json 파싱, 큰 배열의 순회 등)&lt;/li&gt;
&lt;li&gt;함수형 컴포넌트일 때&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;b86b&quot;&gt;&lt;b&gt;When to NOT use it:&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;계산하기 간단한 값들일 때 (useMemo는 가독성을 떨어뜨릴 수 있다.)&lt;/li&gt;
&lt;li&gt;클래스 컴포넌트일 때&lt;/li&gt;
&lt;li&gt;해당 함수를 간단하게 컴포넌트 scope 밖으로 옮길 수 있을 때 (prop callback을 불러일으키거나 state 업데이트와 관련 없을 때)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 id=&quot;b9a2&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;useCallback&lt;/b&gt;&lt;/h4&gt;
&lt;p id=&quot;3018&quot;&gt;&lt;b&gt;What it does&lt;/b&gt;:&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;dependency들이 변경되었을 때만 함수(function)를 다시 생성하도록 기억(memoize)한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;7901&quot;&gt;&lt;b&gt;When to use it:&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 re-render 시가 아니라 특정 prop이나 state가 변경되었을 때만 함수를 다시 생성하고 싶을 때&lt;/li&gt;
&lt;li&gt;함수가 re-render 시 마다 다시 생성되기에 너무 비용 소모가 큰 경우에&lt;/li&gt;
&lt;li&gt;함수형 컴포넌트일 때&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;bb08&quot;&gt;&lt;b&gt;When to NOT use it:&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;매 re-render 시마다 다시 생성해도 비용 소모가 크지 않을 때&lt;/li&gt;
&lt;li&gt;클래스 컴포넌트일 때&lt;/li&gt;
&lt;li&gt;state나 prop 변화와 상관없는 함수여서 컴포넌트 scope 밖에 선언해도 될 때 (또는 한 두개의 state/prop과 관련있어 컴포넌트 scope 밖에 선언하고 인자로 쉽게 넘겨줄 수 있을 때)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 id=&quot;35e6&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;memo&lt;/b&gt;&lt;/h4&gt;
&lt;p id=&quot;ff16&quot;&gt;&lt;b&gt;What it does&lt;/b&gt;:&lt;/p&gt;
&lt;p&gt;함수형 컴포넌트를 감싸서 해당 컴포넌트의 prop이나 state가 얕게(shallowly) 변경되었을 때만 re-render하도록 만든다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p id=&quot;bcc4&quot;&gt;&lt;b&gt;When to use it:&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;prop이 변경되었을때만 re-render하고 싶을 때 (내부 state 업데이트 시에는 여전히 re-render가 일어남)&lt;/li&gt;
&lt;li&gt;플랫한 prop을 가지고 있을 때 (1 레벨 초과하는 복잡한 object prop일 경우 사용하지 말 것)&lt;/li&gt;
&lt;li&gt;컴포넌트가 리액트 트리에서 중위에서 상위 레벨인 경우에&lt;/li&gt;
&lt;li&gt;컴포넌트가 자주 re-render되는 경우&lt;/li&gt;
&lt;li&gt;함수형 컴포넌트일 때&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;77cd&quot;&gt;&lt;b&gt;When to NOT use it:&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클래스 컴포넌트일 때&lt;/li&gt;
&lt;li&gt;prop이 복잡한 object이고, 깊은 변화(deep change) 시 re-render되는 상황일 경우&lt;/li&gt;
&lt;li&gt;state나 prop 변화와 상관없는 함수여서 컴포넌트 scope 밖에 선언해도 될 때 (또는 한 두개의 state/prop과 관련있어 컴포넌트 scope 밖에 선언하고 인자로 쉽게 넘겨줄 수 있을 때)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;위의 3가지 memoization 기술은 단지 빠르고 최적화된 React 어플리케이션을 개발하는 데 도움을 줄 뿐만 아니라 우리의 React 기술을 다음 레벨로 끌어올려주는 데도 도움을 준다. 최적화 기회를 찾아내는 일은 기초 레벨의 React 개발자들과 차별점이 될 수 있다. memoizing 기술을 사용하여 우리와 우리의 사용자들이 성능이 향상된 어플리케이션을 사용할 수 있도록 하자!&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend</category>
      <category>JavaScript</category>
      <category>memo</category>
      <category>react</category>
      <category>useCallback</category>
      <category>useMemo</category>
      <category>리액트</category>
      <category>자바스크립트</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/198</guid>
      <comments>https://im-developer.tistory.com/198#entry198comment</comments>
      <pubDate>Sun, 17 May 2020 12:58:01 +0900</pubDate>
    </item>
    <item>
      <title>[알고리즘/자바스크립트] Codility, PermCheck 문제 풀이</title>
      <link>https://im-developer.tistory.com/197</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Codility, PermCheck&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;A non-empty array A consisting of N integers is given.&lt;/p&gt;
&lt;p&gt;A&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;i&gt;permutation&lt;/i&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;is a sequence containing each element from 1 to N once, and only once.&lt;/p&gt;
&lt;p&gt;For example, array A such that:&lt;/p&gt;
&lt;p&gt;A[0] = 4 A[1] = 1 A[2] = 3 A[3] = 2&lt;/p&gt;
&lt;p&gt;is a permutation, but array A such that:&lt;/p&gt;
&lt;p&gt;A[0] = 4 A[1] = 1 A[2] = 3&lt;/p&gt;
&lt;p&gt;is not a permutation, because value 2 is missing.&lt;/p&gt;
&lt;p&gt;The goal is to check whether array A is a permutation.&lt;/p&gt;
&lt;p&gt;Write a function:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;function solution(A);&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;that, given an array A, returns 1 if array A is a permutation and 0 if it is not.&lt;/p&gt;
&lt;p&gt;For example, given array A such that:&lt;/p&gt;
&lt;p&gt;A[0] = 4 A[1] = 1 A[2] = 3 A[3] = 2&lt;/p&gt;
&lt;p&gt;the function should return 1.&lt;/p&gt;
&lt;p&gt;Given array A such that:&lt;/p&gt;
&lt;p&gt;A[0] = 4 A[1] = 1 A[2] = 3&lt;/p&gt;
&lt;p&gt;the function should return 0.&lt;/p&gt;
&lt;p&gt;Write an&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;b&gt;efficient&lt;/b&gt;&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;algorithm for the following assumptions:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;N is an integer within the range [&lt;span&gt;1&lt;/span&gt;..&lt;span&gt;100,000&lt;/span&gt;];&lt;/li&gt;
&lt;li&gt;each element of array A is an integer within the range [&lt;span&gt;1&lt;/span&gt;..&lt;span&gt;1,000,000,000&lt;/span&gt;].&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;오랜만에 간단한 알고리즘 문제를 풀어보았다. 쉬운 문제인데다가 간만에 푸니까 재미있다.&lt;/p&gt;
&lt;p&gt;문제는 주어진 배열이 &lt;b&gt;Permutation(순열)인지 체크하는 문제&lt;/b&gt;인데, 아주 간단하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;Permutation&lt;/b&gt;&lt;/span&gt;이란, 1부터 N까지의 중복 없는 수열이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;예를 들어서 배열이 &lt;b&gt;[4, 1, 3, 2]&lt;/b&gt;라면 1부터 4까지의 중복 없는 수열이므로 Permutation이라고 할 수 있다.&lt;/p&gt;
&lt;p&gt;그러나 배열이 &lt;b&gt;[4, 1, 3]&lt;/b&gt;이라면 중간에 2가 빠졌으므로 Permutation이 아니다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;문제는 A 배열을 인자로 받은 후 Permutation인 경우 1을 리턴하고 Permutation이 아닌 경우 0을 리턴하는 문제이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;N은 1부터 100,000까지의 숫자이며, A 배열의 숫자의 범위는 1부터 1,000,000,000이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1589080763090&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(A) {
  A.sort((l, r) =&amp;gt; l - r);
  
  if (A[0] !== 1) {
    return 0;
  }
  
  for (let i = 0; i &amp;lt; A.length - 1; i++) {
    if (A[i + 1] - A[i] !== 1) {
      return 0;
    }
  }
    
  return 1;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;내가 푼 방법은 위와 같다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;A 배열을 오름차순으로 정렬한다.&lt;/li&gt;
&lt;li&gt;A 배열의 첫 번째 숫자가 1이 아니라면 Permutation 조건에 해당하지 않으므로 0을 리턴한다.&lt;/li&gt;
&lt;li&gt;0부터 A.length - 1 까지 순회를 하면서 A[i + 1]과 A[i]의 차가 1이 아닌 경우 0을 리턴한다.&lt;/li&gt;
&lt;li&gt;위 조건에 해당하지 않는 경우 Permutation이므로 1을 리턴한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;532&quot; height=&quot;NaN&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PbF7r/btqD3dtvzIP/A8JrGFxGdeRArueSQCLGO0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PbF7r/btqD3dtvzIP/A8JrGFxGdeRArueSQCLGO0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PbF7r/btqD3dtvzIP/A8JrGFxGdeRArueSQCLGO0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPbF7r%2FbtqD3dtvzIP%2FA8JrGFxGdeRArueSQCLGO0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;532&quot; height=&quot;NaN&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;즉, 배열의 숫자를 정렬하게 되면 중복이 없고 1부터 시작하는 수열이라면 각 숫자의 차가 1이라면 Permutation이고,&lt;/p&gt;
&lt;p&gt;그렇지 않은 경우에는 Permutation이 아니게 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cC8fF6/btqD0h5oIVg/Zu7WvtYKSa2y4ZAs8U1KG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cC8fF6/btqD0h5oIVg/Zu7WvtYKSa2y4ZAs8U1KG0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cC8fF6/btqD0h5oIVg/Zu7WvtYKSa2y4ZAs8U1KG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcC8fF6%2FbtqD0h5oIVg%2FZu7WvtYKSa2y4ZAs8U1KG0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQR4Zr/btqD1NvJBE7/SxlaL63Anf9VLvlYDVuKk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQR4Zr/btqD1NvJBE7/SxlaL63Anf9VLvlYDVuKk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQR4Zr/btqD1NvJBE7/SxlaL63Anf9VLvlYDVuKk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQR4Zr%2FbtqD1NvJBE7%2FSxlaL63Anf9VLvlYDVuKk1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Algorithm</category>
      <category>Algorithm</category>
      <category>codility</category>
      <category>JavaScript</category>
      <category>알고리즘</category>
      <category>자바스크립트</category>
      <category>코딩</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/197</guid>
      <comments>https://im-developer.tistory.com/197#entry197comment</comments>
      <pubDate>Sun, 10 May 2020 12:30:25 +0900</pubDate>
    </item>
    <item>
      <title>[React] Infinite Scroll(무한 스크롤) with Intersection Observer</title>
      <link>https://im-developer.tistory.com/196</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;infinite scroll.gif&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6xygt/btqDw30ltgZ/LP336NHwG67eKNuxaHB9jk/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6xygt/btqDw30ltgZ/LP336NHwG67eKNuxaHB9jk/img.gif&quot; data-alt=&quot;이미지 출처: http://brianyang.com/infinite-scroll-techniques-in-react/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6xygt/btqDw30ltgZ/LP336NHwG67eKNuxaHB9jk/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/6xygt/btqDw30ltgZ/LP336NHwG67eKNuxaHB9jk/img.gif&quot; data-filename=&quot;infinite scroll.gif&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;600&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이미지 출처: http://brianyang.com/infinite-scroll-techniques-in-react/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;b&gt;Intersection Observer API&lt;/b&gt;는 &quot;상위 레벨 Document의 Viewport 또는 부모 요소&quot;와 &quot;타겟 요소&quot; 간 교차 지점의 변화를 비동기적으로 관찰할 수 있는 방법을 제공한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;Intersection Observer가 등장하기 전에는 어떤 요소가 화면에 보여지는지 감지하는 것은 매우 복잡한 일이었다. 따라서 요소의 visibility를 감지하고 이벤트를 주는 것은 사용자가 접근하는 여러 브라우저와 웹 사이트들을 느리게 만드는 원인 중 하나였다. 그러나 웹 기술이 발전하면서 요소 visibility 감지의 필요성이 점점 높아졌고, Intersection 정보들이 필요하게 되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span&gt;이미지의 Lazy Loading이나 페이지 스크롤 시 컨텐츠 로딩&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;무한 스크롤 구현&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;광고들이 실제로 유저에게 노출되는지 보고하기 위해&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;유저가 결과를 보는지에 따라 특정 애니메이션이나 task를 수행할 지 여부를 결정하기 위해&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;과거에는 이러한 작업을 하기 위해서 event handlers들을 이용했다. 그러나 이러한 코드들이 모두 메인 쓰레드에서 실행되기 때문에 성능 문제로 연결될 수 있다는 단점이 있었다. 예를 들어서 scroll event listener를 등록한다고 하자. 유저가 아주 약간만 스크롤을 움직여도 scroll listener가 계속 발생할 것이고 성능에 안좋은 영향을 미칠 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;반면 Intersection Observer API는 관찰하고자 하는 타켓 요소가 &quot;다른 요소 또는 viewport&quot;에 들어오거나 나갈 때마다 감지하여 미리 등록된 콜백 함수를 실행시킨다. 이 경우에 더 이상 웹 사이트는 요소의 intersection을 관찰하기 위해 메인 쓰레드를 사용할 필요가 없어진다. 다만 Intersection Observer로는 아주 정확하게 몇 pixel이 교차되었는지는 알아낼 수 없다. 그렇기 때문에 정확한 pixel 값 보다는 대략 N%가 교차되었는지 정하는 식으로 사용한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;pre id=&quot;code_1587292501769&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let options = {
  root: document.querySelector('#scrollArea'),
  rootMargin: '0px',
  threshold: 1.0
}

let observer = new IntersectionObserver(callback, options);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Intersection Observer API&lt;/b&gt;는 위와 같이 callback 함수와 옵션 객체를 인자로 받는다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Callback:&lt;/b&gt; target이라고 불리는 특정 요소가 device의 viewport나 특정 요소(옵션의 root)에 교차되었을때 callback 함수가 실행된다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;options:&lt;/b&gt;&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;root:&lt;/b&gt;&lt;/span&gt; target의 visibility를 검사하기 위한 viewport로 사용될 요소로 target의 상위 요소여야만 한다.&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;기본값:&lt;/b&gt; &lt;/span&gt;browser viewport (아무 값도 설정하지 않거나 null로 설정할 경우 browser viewport를 기본값으로 한다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;rootMargin: &lt;/b&gt;&lt;/span&gt;root 주변의 margin을 말하며, CSS의 margin 속성과 비슷하게 &quot;10px 20px 30px 40px&quot; (top, right, bottom, left)로 줄 수 있다. %값으로도 줄 수 있으며 이 값에 의해 root 요소의 박스 모델을 기준으로 범위가 늘어나거나 줄어들거나 한다.
&lt;ul style=&quot;list-style-type: disc;&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;기본값:&lt;/b&gt; &lt;/span&gt;0px&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;threshold:&lt;/b&gt;&lt;/span&gt; 단일 숫자이거나 숫자가 든 배열을 지정할 수 있다. target 요소와 root 간의 intersection 비율을 말한다. (target 요소가 얼마나 보여지는지 0 ~ 1의 비율로 나타낸 값)
&lt;ul style=&quot;list-style-type: disc;&quot;&gt;
&lt;li&gt;만약 50% 정도만 교차되었을 때 콜백 함수를 실행시키고 싶다면 0.5를 지정하면 된다.&lt;/li&gt;
&lt;li&gt;0은 1 px이라도 보여지면 콜백을 실행시킨다는 뜻이고, 1은 모든 px이 전부 화면에 보여져야만 콜백을 실행시킨다는 뜻이다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;기본값:&lt;/b&gt; &lt;/span&gt;0&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;지난 번 포스팅에서 redux/toolkit과 redux-saga를 적용하여 unsplash 이미지들을 불러오는 간단한 페이지에&lt;/p&gt;
&lt;p&gt;Intersection Observable API를 이용하여 무한 스크롤을 구현해보았다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1587293485230&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useEffect } from 'react';

export const useInfinteScroll = ({
  root = null,
  target,
  onIntersect,
  threshold = 1.0,
  rootMargin = '0px',
}) =&amp;gt; {
  useEffect(() =&amp;gt; {
    const observer = new IntersectionObserver(onIntersect, {
      root,
      rootMargin,
      threshold,
    });

    if (!target) {
      return;
    }

    observer.observe(target);

    return () =&amp;gt; {
      observer.unobserve(target);
    };
  }, [target, root, rootMargin, onIntersect, threshold]);
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;먼저 useInfiniteScroll이라는 함수를 만들었다.&lt;/p&gt;
&lt;p&gt;이 함수는 Intersection Observer의 callback 함수와 설정 값들을 인자로 받아서&lt;/p&gt;
&lt;p&gt;useEffect 콜백 함수 안에서 observer 인스턴스를 생성한 후 구독을 해주는 역할을 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1587293350567&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, { useEffect, useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { unsplashAction, unsplashSelector } from './slice';
import { useInfinteScroll } from '../../hooks';

import './styles.css';
import Loader from '../../components/Loader';
import ErrorView from '../../components/ErrorView';

const ImageGrid = () =&amp;gt; {
  const dispatch = useDispatch();
  const [target, setTarget] = useState(null);

  const {
    isLoading,
    images,
    error
  } = useSelector(unsplashSelector.all);

  useInfinteScroll({
    target,
    onIntersect: ([{ isIntersecting }]) =&amp;gt; {
      if (isIntersecting) {
        dispatch(unsplashAction.loadMore());
      }
    }
  });

  useEffect(() =&amp;gt; {
    dispatch(unsplashAction.load());
  }, []);

  if (isLoading) {
    return &amp;lt;Loader /&amp;gt;;
  }

  if (error) {
    return &amp;lt;ErrorView /&amp;gt;;
  }

  return (
    &amp;lt;div className='content'&amp;gt;
      &amp;lt;section className='grid'&amp;gt;
        {images.map(image =&amp;gt; (
          &amp;lt;div
            key={ image.id }
            className={`item item-${Math.ceil(
              image.height / image.width,
            )}`}
          &amp;gt;
            &amp;lt;img
              src={ image.urls.small }
              alt={ image.user.username }
            /&amp;gt;
        &amp;lt;/div&amp;gt;
        ))}
        &amp;lt;div
          ref={ setTarget }
          className='last-item'
        &amp;gt;
          &amp;lt;Loader size='s' /&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/section&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};

export default ImageGrid;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 처음 페이지 로드 시에는 unsplashAction.load() 액션을 실행시켜 이미지를 로드한후,&lt;/p&gt;
&lt;p&gt;observer를 생성, 등록해준다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;observer 생성 시 target 요소는 리스트의 맨 마지막 빈 div로 설정했다.&lt;/p&gt;
&lt;p&gt;그리고 observer의 콜백 함수에서 isIntersecting이 true인 경우에 unsplashAction.loadMore() 액션을 로드하여&lt;/p&gt;
&lt;p&gt;추가 이미지를 불러오게끔 해주었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;font-size: 1.25em;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;Github repo:&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/jy7123943/redux-saga-practice/blob/redux-toolkit/src/features/ImageGrid/index.js&quot;&gt;https://github.com/jy7123943/redux-saga-practice/blob/redux-toolkit/src/features/ImageGrid/index.js&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1587293952208&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;object&quot; data-og-title=&quot;jy7123943/redux-saga-practice&quot; data-og-description=&quot;reference: https://github.com/wtjs/what-the-splash - jy7123943/redux-saga-practice&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/jy7123943/redux-saga-practice/blob/redux-toolkit/src/features/ImageGrid/index.js&quot; data-og-url=&quot;https://github.com/jy7123943/redux-saga-practice&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bcWR82/hyFKs1KEXa/5aqwBDUbS8YcoIgXcK5cf1/img.jpg?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400&quot;&gt;&lt;a href=&quot;https://github.com/jy7123943/redux-saga-practice/blob/redux-toolkit/src/features/ImageGrid/index.js&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/jy7123943/redux-saga-practice/blob/redux-toolkit/src/features/ImageGrid/index.js&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bcWR82/hyFKs1KEXa/5aqwBDUbS8YcoIgXcK5cf1/img.jpg?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;jy7123943/redux-saga-practice&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;reference: https://github.com/wtjs/what-the-splash - jy7123943/redux-saga-practice&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;font-size: 1.25em;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;Reference:&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1587293798125&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Intersection Observer API&quot; data-og-description=&quot;The Intersection Observer API provides a way to asynchronously observe changes in the intersection of a target element with an ancestor element or with a top-level document's viewport.&quot; data-og-host=&quot;developer.mozilla.org&quot; data-og-source-url=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API&quot; data-og-url=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/WMecD/hyFI1ragG1/qPjWHawtEn30BzCAP7YSf0/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600,https://scrap.kakaocdn.net/dn/0r8xx/hyFI1dDJaR/ztSyzHbrfVqy4MqxXM3Sh0/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/WMecD/hyFI1ragG1/qPjWHawtEn30BzCAP7YSf0/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600,https://scrap.kakaocdn.net/dn/0r8xx/hyFI1dDJaR/ztSyzHbrfVqy4MqxXM3Sh0/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Intersection Observer API&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;The Intersection Observer API provides a way to asynchronously observe changes in the intersection of a target element with an ancestor element or with a top-level document's viewport.&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;developer.mozilla.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;a href=&quot;https://medium.com/@saravananr_93203/infinite-scroll-in-react-made-easy-with-intersection-observer-33bdb5fa9cf6&quot;&gt;https://medium.com/@saravananr_93203/infinite-scroll-in-react-made-easy-with-intersection-observer-33bdb5fa9cf6&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1587293833546&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Infinite scroll in React made easy with Intersection Observer&quot; data-og-description=&quot;When we want to do infinite scroll list what will get strike in our mind is that let&amp;rsquo;s listen for scroll event, when the scroll reach down&amp;hellip;&quot; data-og-host=&quot;medium.com&quot; data-og-source-url=&quot;https://medium.com/@saravananr_93203/infinite-scroll-in-react-made-easy-with-intersection-observer-33bdb5fa9cf6&quot; data-og-url=&quot;https://medium.com/@saravananr_93203/infinite-scroll-in-react-made-easy-with-intersection-observer-33bdb5fa9cf6&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/nBTUA/hyFI3JjhCO/vKQ00BnP7MK4yEPNyPvsvk/img.jpg?width=1200&amp;amp;height=797&amp;amp;face=0_0_1200_797,https://scrap.kakaocdn.net/dn/HjesN/hyFJcsIzez/0mYTAsC11h2VQuKCuptPpK/img.jpg?width=60&amp;amp;height=39&amp;amp;face=0_0_60_39&quot;&gt;&lt;a href=&quot;https://medium.com/@saravananr_93203/infinite-scroll-in-react-made-easy-with-intersection-observer-33bdb5fa9cf6&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://medium.com/@saravananr_93203/infinite-scroll-in-react-made-easy-with-intersection-observer-33bdb5fa9cf6&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/nBTUA/hyFI3JjhCO/vKQ00BnP7MK4yEPNyPvsvk/img.jpg?width=1200&amp;amp;height=797&amp;amp;face=0_0_1200_797,https://scrap.kakaocdn.net/dn/HjesN/hyFJcsIzez/0mYTAsC11h2VQuKCuptPpK/img.jpg?width=60&amp;amp;height=39&amp;amp;face=0_0_60_39');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Infinite scroll in React made easy with Intersection Observer&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;When we want to do infinite scroll list what will get strike in our mind is that let&amp;rsquo;s listen for scroll event, when the scroll reach down&amp;hellip;&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;medium.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;a href=&quot;https://medium.com/@swatisucharita94/react-infinite-scroll-with-intersection-observer-api-db3998e52d63&quot;&gt;https://medium.com/@swatisucharita94/react-infinite-scroll-with-intersection-observer-api-db3998e52d63&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1587293853285&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;React infinite scroll with Intersection Observer API&quot; data-og-description=&quot;In this article, I want to spread some light on the amazing feature called &amp;ldquo;Intersection Observer API&amp;rdquo;. Using intersection observer API&amp;hellip;&quot; data-og-host=&quot;medium.com&quot; data-og-source-url=&quot;https://medium.com/@swatisucharita94/react-infinite-scroll-with-intersection-observer-api-db3998e52d63&quot; data-og-url=&quot;https://medium.com/@swatisucharita94/react-infinite-scroll-with-intersection-observer-api-db3998e52d63&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/KtqWW/hyFIZtm8Uw/urwQxxigLKkQo3uXydm0A1/img.png?width=1200&amp;amp;height=553&amp;amp;face=0_0_1200_553,https://scrap.kakaocdn.net/dn/bB20lv/hyFKtTTFU6/F1rUZCgCNOlrYU14T9X4fk/img.png?width=60&amp;amp;height=27&amp;amp;face=0_0_60_27,https://scrap.kakaocdn.net/dn/bgWdcN/hyFI6F1D49/1NkBUcEkVYE2fCigkSVrNK/img.png?width=60&amp;amp;height=45&amp;amp;face=0_0_60_45&quot;&gt;&lt;a href=&quot;https://medium.com/@swatisucharita94/react-infinite-scroll-with-intersection-observer-api-db3998e52d63&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://medium.com/@swatisucharita94/react-infinite-scroll-with-intersection-observer-api-db3998e52d63&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/KtqWW/hyFIZtm8Uw/urwQxxigLKkQo3uXydm0A1/img.png?width=1200&amp;amp;height=553&amp;amp;face=0_0_1200_553,https://scrap.kakaocdn.net/dn/bB20lv/hyFKtTTFU6/F1rUZCgCNOlrYU14T9X4fk/img.png?width=60&amp;amp;height=27&amp;amp;face=0_0_60_27,https://scrap.kakaocdn.net/dn/bgWdcN/hyFI6F1D49/1NkBUcEkVYE2fCigkSVrNK/img.png?width=60&amp;amp;height=45&amp;amp;face=0_0_60_45');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;React infinite scroll with Intersection Observer API&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;In this article, I want to spread some light on the amazing feature called &amp;ldquo;Intersection Observer API&amp;rdquo;. Using intersection observer API&amp;hellip;&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;medium.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;a href=&quot;https://y0c.github.io/2019/06/30/react-infinite-scroll/&quot;&gt;https://y0c.github.io/2019/06/30/react-infinite-scroll/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1587293861957&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;React Infinite scroll 구현하기&quot; data-og-description=&quot;React로 Infinite scroll을 구현하면서 정리한 글이다. Infinite scroll?Infinite scroll은 한 번에 모든 컨텐츠를 렌더링 하지 않고 페이지 내용을 아래로 스크롤하면 새로운 컨텐츠를 덧붙여서 렌더링 하는&amp;hellip;&quot; data-og-host=&quot;y0c.github.io&quot; data-og-source-url=&quot;https://y0c.github.io/2019/06/30/react-infinite-scroll/&quot; data-og-url=&quot;https://y0c.github.io/2019/06/30/react-infinite-scroll/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bfkRYw/hyFJbm0Nik/UB8Aj8kKJ5yBG7KOZglcOK/img.png?width=700&amp;amp;height=350&amp;amp;face=0_0_700_350,https://scrap.kakaocdn.net/dn/nka2w/hyFKuL2EZ3/jvKAQr197W7yKRC79uXS50/img.png?width=700&amp;amp;height=350&amp;amp;face=0_0_700_350,https://scrap.kakaocdn.net/dn/bDzXDn/hyFI665u2x/KxoslUTSzozCmgvywnkBo0/img.jpg?width=1024&amp;amp;height=768&amp;amp;face=0_0_1024_768&quot;&gt;&lt;a href=&quot;https://y0c.github.io/2019/06/30/react-infinite-scroll/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://y0c.github.io/2019/06/30/react-infinite-scroll/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bfkRYw/hyFJbm0Nik/UB8Aj8kKJ5yBG7KOZglcOK/img.png?width=700&amp;amp;height=350&amp;amp;face=0_0_700_350,https://scrap.kakaocdn.net/dn/nka2w/hyFKuL2EZ3/jvKAQr197W7yKRC79uXS50/img.png?width=700&amp;amp;height=350&amp;amp;face=0_0_700_350,https://scrap.kakaocdn.net/dn/bDzXDn/hyFI665u2x/KxoslUTSzozCmgvywnkBo0/img.jpg?width=1024&amp;amp;height=768&amp;amp;face=0_0_1024_768');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;React Infinite scroll 구현하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;React로 Infinite scroll을 구현하면서 정리한 글이다. Infinite scroll?Infinite scroll은 한 번에 모든 컨텐츠를 렌더링 하지 않고 페이지 내용을 아래로 스크롤하면 새로운 컨텐츠를 덧붙여서 렌더링 하는&amp;hellip;&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;y0c.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;a href=&quot;https://scotch.io/tutorials/infinite-scroll-in-react-using-intersection-observer&quot;&gt;https://scotch.io/tutorials/infinite-scroll-in-react-using-intersection-observer&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1587293881144&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Infinite Scroll in React Using Intersection Observer&quot; data-og-description=&quot;The most dreaded features when building frontend apps are features that need you to take control of the scroll events, behavior and properties. Not only are they hard to implement because of numbers crunching, but more often they are prone to affect perfor&quot; data-og-host=&quot;scotch.io&quot; data-og-source-url=&quot;https://scotch.io/tutorials/infinite-scroll-in-react-using-intersection-observer&quot; data-og-url=&quot;http://scotch.io/tutorials/infinite-scroll-in-react-using-intersection-observer&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/QbH4j/hyFI1Y0H39/h7SFzDJ7JGHqKF8AKU9peK/img.jpg?width=1500&amp;amp;height=750&amp;amp;face=0_0_1500_750,https://scrap.kakaocdn.net/dn/cPff9U/hyFKmULGoA/e6K7GKPv8ZOm3EBL8f87q1/img.jpg?width=1500&amp;amp;height=750&amp;amp;face=0_0_1500_750,https://scrap.kakaocdn.net/dn/brn3dx/hyFKraHS3j/cQhJyfwXbvDoIQhDxiW9sk/img.jpg?width=1050&amp;amp;height=525&amp;amp;face=0_0_1050_525&quot;&gt;&lt;a href=&quot;https://scotch.io/tutorials/infinite-scroll-in-react-using-intersection-observer&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://scotch.io/tutorials/infinite-scroll-in-react-using-intersection-observer&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/QbH4j/hyFI1Y0H39/h7SFzDJ7JGHqKF8AKU9peK/img.jpg?width=1500&amp;amp;height=750&amp;amp;face=0_0_1500_750,https://scrap.kakaocdn.net/dn/cPff9U/hyFKmULGoA/e6K7GKPv8ZOm3EBL8f87q1/img.jpg?width=1500&amp;amp;height=750&amp;amp;face=0_0_1500_750,https://scrap.kakaocdn.net/dn/brn3dx/hyFKraHS3j/cQhJyfwXbvDoIQhDxiW9sk/img.jpg?width=1050&amp;amp;height=525&amp;amp;face=0_0_1050_525');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Infinite Scroll in React Using Intersection Observer&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;The most dreaded features when building frontend apps are features that need you to take control of the scroll events, behavior and properties. Not only are they hard to implement because of numbers crunching, but more often they are prone to affect perfor&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;scotch.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend</category>
      <category>InfiniteScroll</category>
      <category>IntersectionObserver</category>
      <category>JavaScript</category>
      <category>react</category>
      <category>리액트</category>
      <category>무한스크롤</category>
      <category>자바스크립트</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/196</guid>
      <comments>https://im-developer.tistory.com/196#entry196comment</comments>
      <pubDate>Sun, 19 Apr 2020 20:04:35 +0900</pubDate>
    </item>
    <item>
      <title>[Redux saga] 리덕스 사가에 대해서 (Redux toolkit과 같이 사용해보기)</title>
      <link>https://im-developer.tistory.com/195</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beIpPa/btqDlOIUW54/NUEumie8saM9aWmlkIg0n1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beIpPa/btqDlOIUW54/NUEumie8saM9aWmlkIg0n1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beIpPa/btqDlOIUW54/NUEumie8saM9aWmlkIg0n1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeIpPa%2FbtqDlOIUW54%2FNUEumie8saM9aWmlkIg0n1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;요즘 회사에서 예전 레거시 코드를 리팩토링하는 과정에서 &lt;b&gt;Redux/toolkit&lt;/b&gt;과 &lt;b&gt;redux-saga&lt;/b&gt;를 처음으로 도입해보았다.&lt;/p&gt;
&lt;p&gt;프로젝트 규모가 그리 크지 않아서 과연 복잡한 초기 세팅과 러닝 커브를 모두 극복할만큼&lt;/p&gt;
&lt;p&gt;redux의 효과가 클 것인가에 대해서 계속 논의했고, 더 큰 프로젝트를 하기 전에&lt;/p&gt;
&lt;p&gt;지금 규모의 프로젝트에서 한 번 도입을 해보고 괜찮다고 생각이 되면 앞으로도 계속 사용하자는 결론이 나왔다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그리하여 도입한 redux-toolkit과 redux-saga는 생각보다 더더욱 만족스러운 결과를 만들어주었다.&lt;/p&gt;
&lt;p&gt;일단 redux-saga를 도입하고 나서 좋아진 것으로는 아래와 같은 것들이 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;테스트 코드 쓰기 매우 편해졌다.&lt;/li&gt;
&lt;li&gt;비동기 로직 흐름을 이해하기가 쉬워졌다.&lt;/li&gt;
&lt;li&gt;prop drilling이 줄었다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그래서 오늘은 redux-saga에 대해 간단히 정리해보려고 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;redux-saga란&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;redux-saga&lt;/b&gt;&lt;/span&gt;는 어플리케이션의 &lt;b&gt;사이드 이펙트&lt;/b&gt;(&lt;span style=&quot;color: #333333;&quot;&gt;데이터 fetch와 같은 비동기 로직이나 브라우저 캐시에 접근하는 것과 같은 순수하지 않은 것들&lt;/span&gt;)를 더 효과적으로 관리하려고 만들어졌다. 즉, &lt;u&gt;&lt;b&gt;효과적으로 실행하고, 쉽게 테스트하고, 쉽게 에러 핸들링을 하자!&lt;/b&gt;&lt;/u&gt;는 목적으로 만들어졌다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그래서 saga는 어플리케이션에서 오로지 사이드 이펙트에만 반응하도록 만들어진 별도 쓰레드와 같다고 할 수 있다. redux-saga는 redux 미들웨어로, 보통의 리덕스 액션으로 시작되고, 중단되며, 취소될 수 있다. 또한 redux 어플리케이션의 모든 상태 값에 접근할 수 있고, redux 액션들을 dispatch할 수도 있다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;redux-saga는 비동기 플로우를 쉽게 읽고, 쓰고, 테스트할 수 있도록 ES6의 &lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;Generator&lt;/b&gt;&lt;/span&gt;라는 개념을 사용한다. 이 Generator를 차용한 덕분에 비동기 코드가 마치 스탠다드한 동기 코드처럼 보여진다. (마치 async/await과 같지만 더 멋진 점들이 많다.) redux-thunk와 다르게 콜백 지옥에 빠질 일도 없고, 비동기 로직을 쉽게 테스트할 수 있으며, 액션들을 순수한 상태로 둘 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Example&lt;/h3&gt;
&lt;p&gt;그럼 이 비동기 로직을 어떻게 처리하는지 예제 코드를 통해서 살펴보려고 한다.&lt;/p&gt;
&lt;p&gt;먼저 &lt;a href=&quot;https://github.com/wtjs/what-the-splash&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/wtjs/what-the-splash&lt;/a&gt; repo의 코드를 clone받아서 saga를 연습하는데 사용하였다.&lt;/p&gt;
&lt;p&gt;이 repo는 무료 이미지를 가져올 수 있는 unsplash의 API로 이미지들 배열을 fetch해서 화면에 뿌려주는 매우 간단한 리액트 페이지이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1586658865576&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, { Component } from 'react';

import './styles.css';

const key = '5f96323678d05ff0c4eb264ef184556868e303b32a2db88ecbf15746e6f25e02';

class ImageGrid extends Component {
  state = {
    images: [],
  };

  componentDidMount() {
    fetch(`https://api.unsplash.com/photos/?client_id=${key}&amp;amp;per_page=28`)
      .then(res =&amp;gt; res.json())
      .then(images =&amp;gt; {
        this.setState({
          images,
        });
      });
  }

  render() {
    const { images } = this.state;
    return (
      &amp;lt;div className=&quot;content&quot;&amp;gt;
        &amp;lt;section className=&quot;grid&quot;&amp;gt;
          {images.map(image =&amp;gt; (
            &amp;lt;div
              key={image.id}
              className={`item item-${Math.ceil(
                image.height / image.width,
              )}`}
            &amp;gt;
              &amp;lt;img
                src={image.urls.small}
                alt={image.user.username}
              /&amp;gt;
          &amp;lt;/div&amp;gt;
          ))}
        &amp;lt;/section&amp;gt;
      &amp;lt;/div&amp;gt;
    );
  }
}

export default ImageGrid;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;즉, 위와 같이 componentDidMount에서 fetch 함수를 사용하여 이미지를 가져오고 이미지들을 화면에 뿌려주는 것이 전부이다.&lt;/p&gt;
&lt;p&gt;일단 위 repo가 꽤 오래전에 만들어진 repo여서 class component를 사용하고 있는데,&lt;/p&gt;
&lt;p&gt;전부 functional component로 바꾸고 redux-toolkit과 saga를 이용해서 바꿔보려고 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333; font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;&lt;i&gt;src/api/&lt;/i&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333; font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;&lt;i&gt;src/components/&lt;/i&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333; font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;&lt;i&gt;src/features/&lt;/i&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333; font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; ImageGrid/&lt;/i&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333; font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; index.js&lt;/i&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333; font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; slice.js&lt;/i&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333; font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;&lt;i&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; saga.js&lt;/i&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333; font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;&lt;i&gt;src/store/&lt;/i&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333; font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;&lt;i&gt;src/App.js&lt;/i&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333; font-family: 'Nanum Gothic';&quot;&gt;&lt;b&gt;&lt;i&gt;src/index.js&lt;/i&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;폴더 디렉토리 구조는 위와 같이 변경할 예정이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;예전에는 actions, reducers, constants 등등 각 기능 별로 폴더를 나누는 것을 권장했다면,&lt;/p&gt;
&lt;p&gt;redux-toolkit에서 actions와 reducer등을 한 방에 만들어주는 redux-toolkit의 createSlice가 나오면서&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;features/ 폴더에 각 화면 별로 폴더를 나누고 그 폴더 안에&lt;/p&gt;
&lt;p&gt;관련 컴포넌트 파일, actions와 reducer가 들어있는 slice.js 파일, 그리고 saga.js 파일을 한꺼번에 묶는 것이 권장되고 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;(이러한 패턴을 &lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;ducks 패턴&lt;/b&gt;&lt;/span&gt;이라고 한다.)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;&lt;b&gt;src/features/ImageGrid/slice.js&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1586658994700&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { createSelector, createSlice } from '@reduxjs/toolkit';

const initialState = {
  isLoading: false,
  images: [],
  error: null
};

const reducers = {
  load: (state) =&amp;gt; {
    state.isLoading = true;
  },
  loadSuccess: (state, { payload: images }) =&amp;gt; {
    state.isLoading = false;
    state.images = images;
  },
  loadFail: (state, { payload: error }) =&amp;gt; {
    state.isLoading = false;
    state.error = error;
  }
}

const name = 'UNSPLASH';
const slice = createSlice({
  name, initialState, reducers
});

const selectAllState = createSelector(
  state =&amp;gt; state.isLoading,
  state =&amp;gt; state.images,
  state =&amp;gt; state.error,
  (isLoading, images, error) =&amp;gt; {
    return { isLoading, images, error };
  }
);

export const unsplashSelector = {
  all: state =&amp;gt; selectAllState(state[UNSPLASH])
};

export const UNSPLASH = slice.name;
export const unsplashReducer = slice.reducer;
export const unsplashAction = slice.actions;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;slice.js 파일을 보면, UNSPLASH라는 이름으로 createSlice를 이용해 slice를 생성하고,&lt;/p&gt;
&lt;p&gt;그 slice 객체에 자동으로 생성된 name과 reducer, actions 등을 export해주고 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;&lt;b&gt;src/features/ImageGrid/saga.js&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1586659912948&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { call, put, takeLatest } from 'redux-saga/effects';
import { getSplashImage } from '../../api';
import { unsplashAction } from './slice';

function* handleImageLoad() {
  const { loadSuccess, loadFail } = unsplashAction;
  try {
    const images = yield call(getSplashImage);

    yield put(loadSuccess(images));
  } catch (err) {
    yield put(loadFail(err));
  }
}

export function* watchUnsplash() {
  const { load } = unsplashAction;

  yield takeLatest(load, handleImageLoad);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 제일 중요한 saga 로직을 살펴보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;먼저 saga.js에는 보통 2개의 generator 함수가 선언되는데,&lt;/p&gt;
&lt;p&gt;하나는 보통 watch~라는 이름으로 생성되는 함수이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 &lt;b&gt;watchUnsplash&lt;/b&gt;함수에서는 takeLatest라는 함수 안에 load 액션 함수를 인자로 넣고,&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;handleImageLoad 함수를 두 번째 인자로 넣는다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그러면 이제 load 액션이 dispatch되는 순간 saga에서 그 액션을 take한 후,&lt;/p&gt;
&lt;p&gt;handleImageLoad generator 함수를 실행시킨다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그럼 &lt;b&gt;handleImageLoad&lt;/b&gt; 함수를 살펴보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 함수는 getSplashImage라는 api 함수를 call이라는 saga effect 함수에 넣은 후 yield 시키고 있다.&lt;/p&gt;
&lt;p&gt;그러면 saga 미들웨어에서 알아서 getSplashImage라는 비동기 액션을 실행시킨 후 그 결과물을 images라는 변수에 담아준다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그 후 put이라는 effect 함수에 loadSuccess 액션 객체를 넣고 있는데,&lt;/p&gt;
&lt;p&gt;이 때 위에서 fetch 결과물로 받은 images 배열을 인자로 넣어주고 있다.&lt;/p&gt;
&lt;p&gt;이 인자는 action 객체의 payload 속성으로 들어가며 위 slice.js에서 생성된 reducer에 의해 새로운 상태값으로 업데이트된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;try catch문을 사용하여 만약에 에러가 catch된다면&lt;/p&gt;
&lt;p&gt;loadFail이라는 액션 객체를 생성하여 put 이펙트 함수에 넣어주면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;너무나도 간단하게 비동기 로직이 정리되며, 마치 비동기가 아닌 동기 코드처럼 보여진다(!)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 이렇게 생성된 saga와 slice들을 연결을 해주어야 동작하게 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;&lt;b&gt;src/store/index.js&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1586660516013&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { combineReducers } from '@reduxjs/toolkit';
import { configureStore } from '@reduxjs/toolkit';
import createSagaMiddleware from 'redux-saga';

import { all } from 'redux-saga/effects';
import { UNSPLASH, unsplashReducer } from '../features/ImageGrid/slice';
import { watchUnsplash } from '../features/ImageGrid/saga';

export const rootReducer = combineReducers({
  [UNSPLASH]: unsplashReducer,
});

const sagaMiddleware = createSagaMiddleware();
function* rootSaga() {
  yield all([
    watchUnsplash(),
  ])
}

const createStore = () =&amp;gt; {
  const store = configureStore({
    reducer: rootReducer,
    devTools: true,
    middleware: [sagaMiddleware]
  });

  sagaMiddleware.run(rootSaga);

  return store;
}

export default createStore;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위와 같이 store를 생성하는 함수 createStore를 만들어주었다.&lt;/p&gt;
&lt;p&gt;이 함수는 redux/toolkit에서 제공해주는 configureStore라는 함수를 이용하여 store를 생성한다.&lt;/p&gt;
&lt;p&gt;그리고 sagaMiddleware.run() 을 통해 saga 미들웨어를 실행시킨다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이렇게 만든 createStore 함수를 이용해 App.js에서 store를 생성한 후, Provider 컴포넌트에 prop으로 내려줌으로써 모든 설정이 끝난다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;&lt;b&gt;src/App.js&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1586660769308&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, { Component } from 'react';
import { Provider } from 'react-redux';
import createStore from './store';

import Header from './components/Header';
import ImageGrid from './features/ImageGrid';

const store = createStore();

class App extends Component {
  render() {
    return (
      &amp;lt;Provider store={store}&amp;gt;
        &amp;lt;div&amp;gt;
          &amp;lt;Header /&amp;gt;
          &amp;lt;ImageGrid /&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/Provider&amp;gt;
    );
  }
}

export default App;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;&lt;b&gt;src/features/ImageGrid/index.js&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1586660811950&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import React, { useEffect } from 'react';
import { useSelector } from 'react-redux';
import { useDispatch } from 'react-redux';
import Loader from '../../components/Loader';
import ErrorView from '../../components/ErrorView';
import { unsplashAction, unsplashSelector } from './slice';
import './styles.css';

const ImageGrid = () =&amp;gt; {
  const dispatch = useDispatch();
  const { isLoading, images, error } = useSelector(unsplashSelector.all);

  useEffect(() =&amp;gt; {
    const { load } = unsplashAction;

    dispatch(load());
  }, []);

  if (isLoading) {
    return &amp;lt;Loader /&amp;gt;;
  }

  if (error) {
    return &amp;lt;ErrorView /&amp;gt;;
  }

  return (
    &amp;lt;div className=&quot;content&quot;&amp;gt;
      &amp;lt;section className=&quot;grid&quot;&amp;gt;
        {images.map(image =&amp;gt; (
          &amp;lt;div
            key={ image.id }
            className={`item item-${Math.ceil(
              image.height / image.width,
            )}`}
          &amp;gt;
            &amp;lt;img
              src={ image.urls.small }
              alt={ image.user.username }
            /&amp;gt;
        &amp;lt;/div&amp;gt;
        ))}
      &amp;lt;/section&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};

export default ImageGrid;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 ImageGrid 컴포넌트에서 load 액션을 dispatch하면&amp;nbsp;&lt;/p&gt;
&lt;p&gt;사가 미들웨어의 handleImageLoad가 실행되어 비동기 fetch가 일어나고&lt;/p&gt;
&lt;p&gt;그 결과물이 업데이트되어 컴포넌트가 다시 리렌더가 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;업데이트된 상태값들은 selector를 이용해 꺼내 쓴다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위 코드는 github에 올려두었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;practice-saga&lt;/span&gt;&lt;/b&gt; 브랜치에는 redux/toolkit을 사용하지 않고&amp;nbsp;&lt;/p&gt;
&lt;p&gt;액션, 리듀서 등을 일일이 따로 만들어준 후 redux-saga를 사용한 버전이 들어있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;&lt;b&gt;redux-toolkit&lt;/b&gt;&lt;/span&gt; 브랜치에는 practice-saga에서 만든 결과물에 redux/toolkit을 도입해서&amp;nbsp;&lt;/p&gt;
&lt;p&gt;간단한 구조로 리팩토링한 코드가 들어있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/jy7123943/redux-saga-practice/tree/practice-saga/src&quot;&gt;https://github.com/jy7123943/redux-saga-practice/tree/practice-saga/src&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/jy7123943/redux-saga-practice/tree/redux-toolkit/src&quot;&gt;https://github.com/jy7123943/redux-saga-practice/tree/redux-toolkit/src&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend</category>
      <category>JavaScript</category>
      <category>react</category>
      <category>Redux</category>
      <category>SAGA</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/195</guid>
      <comments>https://im-developer.tistory.com/195#entry195comment</comments>
      <pubDate>Sun, 12 Apr 2020 12:17:35 +0900</pubDate>
    </item>
    <item>
      <title>[Js] 자바스크립트, Generator 함수에 대하여</title>
      <link>https://im-developer.tistory.com/193</link>
      <description>&lt;p&gt;공부해야지 해야지 하고 맨날 뒤로 미루다가 오늘 드디어 &lt;b&gt;Generator&lt;/b&gt;에 대해서 정리를 해보려고 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Generator는 실행을 잠시 멈췄다가 나중에 다시 접근할 수 있는 아주 특이한 형태의 함수이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Generator 함수는 나중에 다시 접근하기 위해서 context(즉 변수값)를 저장된 상태로 남겨둔다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Generator는 주로 Promise와 결합하여 사용되며, Callback 지옥같은 비동기 프로그래밍의 문제점들을 많이 완화시켜준다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;일반 함수&lt;/h3&gt;
&lt;p&gt;예를 들어서 아래와 같은 간단한 함수를 살펴보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1585466641461&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function sayHi() {
  // statements
  return 'hi';
  return 'hello'; // was never executed
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위 함수를 실행시키면 첫 번째 return문인 &lt;b&gt;return 'hi'&lt;/b&gt;를 만나자마자 함수의 실행이 종료될 것이다. 따라서 return 'hello'는 절대 실행되지 않는다. 그렇다면 Generator 함수는 어떨까?&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Generator 함수&lt;/h3&gt;
&lt;pre id=&quot;code_1585466744276&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function* sayHiGenerator(params) {
  yield 'hello';
  yield 'world';
  // statements
  return 'hi';
}

const resultGenerator = sayHiGenerator();
console.log(resultGenerator); // { [Iterator] } -&amp;gt; generator object&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Generator 함수는 함수 키워드 뒤에 &lt;b&gt;*&lt;/b&gt; 문자가 붙는 형태이다. 그리고 함수 내부에 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;yield&lt;/b&gt;&lt;/span&gt;라는 키워드가 사용된다.&lt;/p&gt;
&lt;p&gt;Generator 함수는 호출되더라도 함수의 body를 즉시 실행하지 않는다. 대신 &lt;b&gt;함수의 iterator 객체를 반환&lt;/b&gt;한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1585466873900&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.log(resultGenerator.next()); // { value: 'hello', done: false } -&amp;gt; still in suspender state
console.log(resultGenerator.next()); // { value: 'world', done: false } -&amp;gt; still in suspender state
console.log(resultGenerator.next()); // { value: 'hi', done: true } -&amp;gt; finished
console.log(resultGenerator.next()); // { value: undefined, done: true } -&amp;gt; nothing to return here
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그리고 반환된 iterator 객체의 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;next()&lt;/b&gt;&lt;/span&gt; 메소드가 호출되면 Generator 함수의 body가 yield expression이 나타날때까지 실행된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;next() 메소드&lt;/h3&gt;
&lt;p&gt;next() 메소드는 다음과 같은 형태의 객체를 리턴한다.&lt;/p&gt;
&lt;pre id=&quot;code_1585464351254&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  value: 'hello', // yielded value
  done: false
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위 객체에서 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;value&lt;/b&gt;&lt;/span&gt;는 yield expression이 반환할 값(즉, yielded value)이고,&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;done&lt;/b&gt;&lt;/span&gt;은 Generator 함수가 가장 마지막 값을 yield했는지 여부를 boolean 값으로 나타낸 것을 의미한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Generator 함수 내에 return statement가 있다면, 그 statement를 만나는 즉시 Generator 함수가 종료하게 된다.&lt;/p&gt;
&lt;p&gt;(따라서 done 속성이 true가 되며, 리턴된 값이 value가 된다.)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;만약에 Generator 내부에서 에러가 발생한다면, Generator의 body 안에서 catch되어 핸들링되지 않는 이상 Generator 함수가 종료된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Generator가 존료되고 나면 next() 메소드 호출이 더 이상 불가능하며 다음 객체만 반환할 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1585466573271&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  value: undefined,
  done: true
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Generator with for-of&lt;/h4&gt;
&lt;p&gt;만약에 sayHiGenerator 함수를 실행해서 반환받은 iterator 객체를 for-of 문으로 iterate하게 되면&lt;/p&gt;
&lt;p&gt;next 함수로 반환되는 각 객체의 value값을 순회할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1585467057528&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const resultGeneratorForOf = sayHiGenerator();

for (const val of resultGeneratorForOf) {
  console.log(val); // hello world
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;next() 메소드에 인자를 넣어 호출하면?&lt;/h4&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;next() 메소드에 특정 값을 인자로 넣어 호출하면, 진행을 멈췄던 위치의 yield expression을 인자값으로 교체하고 그 위치에서 다시 실행한다. 다음 예제 코드를 살펴보자.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1585467418646&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function* newGenerator() {
  yield 'something'; // yeild keyword는 우리가 바깥에서 value를 넣는 것을 허용함

  const final = yield 'give me value';
  return final; // customize this return value outside
}

const newGeneratorIt = newGenerator();

console.log(newGeneratorIt.next()); // { value: 'something', done: false }
console.log(newGeneratorIt.next()); // { value: 'give me value', done: false }
console.log(newGeneratorIt.next('custom value')); // { value: undefined -&amp;gt; 'custom value', done: true }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;첫 번째 next() 함수를 호출하면 &lt;b&gt;yield 'something'&lt;/b&gt;에서 종료하여 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;'something'&lt;/b&gt;&lt;/span&gt;을 value로 리턴한다.&lt;/p&gt;
&lt;p&gt;두 번째 next() 함수를 호출하면 &lt;b&gt;yield 'give me value'&lt;/b&gt;에서 종료하며&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt; 'give me value'&lt;/b&gt;&lt;/span&gt;를 리턴한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;만약 여기서 next() 함수를 다시 호출하면 이번엔 value가 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;undefined&lt;/b&gt;&lt;/span&gt;가 된다.&lt;/p&gt;
&lt;p&gt;왜냐면 next 함수를 호출할때 인자로 넣은 값이 없어 final에 어떤 값도 할당되지 않았기 때문이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그러나 'custom value'라는 string을 인자로 넣어 실행하면 value에&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt; 'custom value'&lt;/b&gt;&lt;/span&gt;가 들어간다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1585468359683&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function* testGenerator() {
  yield 1;

  const val = yield 2;
  const final = yield 3;
  return val; // customize this return value outside
}

const testGeneratorIt = testGenerator();

console.log(testGeneratorIt.next()); // { value: 1, done: false }
console.log(testGeneratorIt.next()); // { value: 2, done: false }
console.log(testGeneratorIt.next(100)); // { value: 3, done: false }
console.log(testGeneratorIt.next()); // { value: undefined -&amp;gt; 100, done: true }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위 함수를 살펴보면 3번째 next를 호출했을때 100을 인자로 넣어주고 있다.&lt;/p&gt;
&lt;p&gt;그러면 4번째로 next() 호출 시 value에 100이 든 객체가 리턴된다.&lt;/p&gt;
&lt;p&gt;그 이유는 Generator가 final이 아닌 val을 리턴하고 있기 때문이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;즉, 첫번째 호출 시 yield 1을 만나 value가 1인 객체가 리턴된다.&lt;/p&gt;
&lt;p&gt;그 다음 호출 시 yield 2를 만나 value가 2인 객체가 리턴된다.&lt;/p&gt;
&lt;p&gt;그 다음, yield 3을 만나 value가 3인 객체가 리턴되며, 여기서 인자로 들어간 100이 val에 할당된다.&lt;/p&gt;
&lt;p&gt;그 다음 val을 리턴하므로 value가 100인 객체가 리턴된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1585472255915&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function* testGenerator() {
  yield 1;

  const val = yield 2;
  const final = yield 3;
  return final; // customize this return value outside
}

const testGeneratorIt = testGenerator();

console.log(testGeneratorIt.next()); // { value: 1, done: false }
console.log(testGeneratorIt.next()); // { value: 2, done: false }
console.log(testGeneratorIt.next()); // { value: 3, done: false }
console.log(testGeneratorIt.next(100)); // { value: undefined -&amp;gt; 100, done: true }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;만약 final을 return한다면 위와 같이 4번째로 next()를 호출할때 넣어준 인자값이 리턴된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1585468811507&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function* gen() {
  while(true) {
    var value = yield null;
    console.log(value);
  }
}

var g = gen();
console.log('g.next(1)', g.next(1));
console.log('g.next(2)', g.next(2));&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위 함수를 실행하면 다음과 같은 결과물이 나온다.&lt;/p&gt;
&lt;p&gt;처음에 g.next(1)을 실행하면 yield null을 만나 value가 null인 객체를 리턴한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 때 1이 콘솔에 찍히지 않는 이유는 1이 들어간 시점에는 Generator가 아직 아무런 값도 yield하지 않았기 때문이다.&lt;/p&gt;
&lt;p&gt;따라서 yield null을 한 후, 다음 next(2) 메소드 호출 시 인자로 들어간 2를 콘솔창에서 출력한다.&lt;/p&gt;
&lt;p&gt;그리고 value가 null인 객체를 다시 반환하게 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;603&quot; height=&quot;120&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNfUll/btqC3qAYMTb/HedMIPGBGviCefKRxdnbwK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNfUll/btqC3qAYMTb/HedMIPGBGviCefKRxdnbwK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNfUll/btqC3qAYMTb/HedMIPGBGviCefKRxdnbwK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNfUll%2FbtqC3qAYMTb%2FHedMIPGBGviCefKRxdnbwK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;603&quot; height=&quot;120&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Reference:&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=b6UdHGJNqTw&amp;amp;list=PLMV09mSPNaQlWvqEwF6TfHM-CVM6lXv39&amp;amp;index=3&amp;amp;t=0s&quot;&gt;https://www.youtube.com/watch?v=b6UdHGJNqTw&amp;amp;list=PLMV09mSPNaQlWvqEwF6TfHM-CVM6lXv39&amp;amp;index=3&amp;amp;t=0s&lt;/a&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;video&quot; data-ke-style=&quot;alignCenter&quot; data-video-host=&quot;youtube&quot; data-video-url=&quot;https://www.youtube.com/watch?v=b6UdHGJNqTw&amp;amp;list=PLMV09mSPNaQlWvqEwF6TfHM-CVM6lXv39&amp;amp;index=3&amp;amp;t=0s&quot; data-video-thumbnail=&quot;https://scrap.kakaocdn.net/dn/PdaCW/hyFtvScBU2/ygJk5HaKFERBbYKFkVaQP0/img.jpg?width=1280&amp;amp;height=720&amp;amp;face=0_0_1280_720&quot; data-video-width=&quot;860&quot; data-video-height=&quot;484&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/b6UdHGJNqTw?start=0&quot; width=&quot;860&quot; height=&quot;484&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend</category>
      <category>generator</category>
      <category>JavaScript</category>
      <category>자바스크립트</category>
      <category>코딩</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/193</guid>
      <comments>https://im-developer.tistory.com/193#entry193comment</comments>
      <pubDate>Sun, 29 Mar 2020 18:01:23 +0900</pubDate>
    </item>
    <item>
      <title>TypeScript declarations 파일에 Webpack Alias 적용하기</title>
      <link>https://im-developer.tistory.com/194</link>
      <description>&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZHj3J/btqCSgrJroB/ka0SkSOOljFLDJWHKtqVP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZHj3J/btqCSgrJroB/ka0SkSOOljFLDJWHKtqVP1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZHj3J/btqCSgrJroB/ka0SkSOOljFLDJWHKtqVP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZHj3J%2FbtqCSgrJroB%2Fka0SkSOOljFLDJWHKtqVP1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://im-developer.tistory.com/186?category=879485&quot;&gt;https://im-developer.tistory.com/186?category=879485&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1584771782052&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Webpack, React, Typescript, Jest 환경에서 Alias 적용하기 (절대 경로 import)&quot; data-og-description=&quot;프로젝트의 크기가 커지고 복잡해질수록 상대 경로로 module을 import하기 힘들어진다. 여기 저기 왔다 갔다 하면서 나한테 필요한 파일들을 찾아내야하는데 여간 귀찮은 일이 아닐 수 없다. import React from '..&quot; data-og-host=&quot;im-developer.tistory.com&quot; data-og-source-url=&quot;https://im-developer.tistory.com/186?category=879485&quot; data-og-url=&quot;https://im-developer.tistory.com/186&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ehkEPV/hyFmQP4sDE/oLJkzBZopw6eHnxz3lGdOK/img.jpg?width=626&amp;amp;height=500&amp;amp;face=0_0_626_500,https://scrap.kakaocdn.net/dn/s7G0l/hyFmROZ0EQ/rkAkqHlkpR3KlEnRUJxO60/img.jpg?width=626&amp;amp;height=500&amp;amp;face=0_0_626_500,https://scrap.kakaocdn.net/dn/BU3CI/hyFmSUEMcq/eIXbMliQlZHoarbkOEInA0/img.jpg?width=626&amp;amp;height=500&amp;amp;face=0_0_626_500&quot;&gt;&lt;a href=&quot;https://im-developer.tistory.com/186?category=879485&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://im-developer.tistory.com/186?category=879485&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ehkEPV/hyFmQP4sDE/oLJkzBZopw6eHnxz3lGdOK/img.jpg?width=626&amp;amp;height=500&amp;amp;face=0_0_626_500,https://scrap.kakaocdn.net/dn/s7G0l/hyFmROZ0EQ/rkAkqHlkpR3KlEnRUJxO60/img.jpg?width=626&amp;amp;height=500&amp;amp;face=0_0_626_500,https://scrap.kakaocdn.net/dn/BU3CI/hyFmSUEMcq/eIXbMliQlZHoarbkOEInA0/img.jpg?width=626&amp;amp;height=500&amp;amp;face=0_0_626_500');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Webpack, React, Typescript, Jest 환경에서 Alias 적용하기 (절대 경로 import)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;프로젝트의 크기가 커지고 복잡해질수록 상대 경로로 module을 import하기 힘들어진다. 여기 저기 왔다 갔다 하면서 나한테 필요한 파일들을 찾아내야하는데 여간 귀찮은 일이 아닐 수 없다. import React from '..&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;im-developer.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위 포스팅에서 Webpack과 TypeScript 환경에서 어떻게 Webpack Alias를 적용하는지 알아보았다.&lt;/p&gt;
&lt;p&gt;저대로 잘 적용해서 사용하고 있었는데 개발 중에 문제가 하나 발견되었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;foo.ts와 bar.ts라는 파일이 있다고 가정해보자.&lt;/p&gt;
&lt;p&gt;그리고 foo.ts 파일에서 interface를 선언한 후 export 해준다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;font-size: 1.12em;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;foo.ts&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1584771952701&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface Foo {
  sample: string;
}

export { Foo };&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그리고 bar.ts 파일에서 Foo를 import한 후에 다시 re-export 해주는 상황을 생각해보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;font-size: 1.12em;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;bar.ts&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1584772008555&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { Foo } from '@/a';

// ...

export { Foo };&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;tsconfig.json&lt;/b&gt;&lt;/span&gt;에 &lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;'declarations'&lt;/b&gt;&lt;/span&gt; property를 true로 설정하고&lt;/p&gt;
&lt;p&gt;위 파일들을 TypeScript Compiler로 컴파일하면 declarations file들이 생성된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;문제는 d.ts 파일들에 alias 설정이 적용이 안된다는 점이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;font-size: 1.12em;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;bar.d.ts&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1584772264960&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { Foo } from &quot;@/a&quot;;
// ...
export { Foo };&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;즉, 위와 같이 &lt;b&gt;'@'&lt;/b&gt;가 상대 경로로 변환되지 않고 그대로 생성되어&lt;/p&gt;
&lt;p&gt;Foo 모듈을 찾지 못하는 에러가 발생한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;How to solve?&lt;/h3&gt;
&lt;p&gt;위 문제를 해결하기 위해 여기 저기를 찾아다니다가 &lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;ttypescript&lt;/b&gt;&lt;/span&gt; 와 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;typescript-transform-paths&lt;/b&gt;&lt;/span&gt; 라이브러리를 발견했다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&lt;/span&gt;1. 라이브러리 설치 후, &lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;tsconfig.json&lt;/b&gt;&lt;/span&gt;에 아래 설정을 추가한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1584772817768&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&quot;plugins&quot;: [{
  &quot;transform&quot;: &quot;typescript-transform-paths&quot;,
  &quot;afterDeclarations&quot;: true
}],&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;2. &lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;package.json&lt;/b&gt;&lt;/span&gt;에&lt;b&gt; &lt;span style=&quot;background-color: #dddddd;&quot;&gt;tsc&lt;/span&gt; &lt;/b&gt;를 &lt;b&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;ttsc&lt;/span&gt;&lt;/b&gt; 로 바꿔준다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1584772841702&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&quot;build&quot;: &quot;ttsc -p tsconfig.json &amp;amp;&amp;amp; webpack&quot;,&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 &lt;b&gt;d.ts&lt;/b&gt; 파일들의 alias가 정상적으로 compile된다!&amp;nbsp;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1584772888287&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { Foo } from &quot;./a&quot;;
// ...
export { Foo };&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;&lt;/span&gt;Reference:&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://medium.com/@joshuaavalon/webpack-alias-in-typescript-declarations-81d2b6c0dcd6&quot;&gt;https://medium.com/@joshuaavalon/webpack-alias-in-typescript-declarations-81d2b6c0dcd6&lt;/a&gt;&lt;span&gt; &lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1584773092662&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Webpack alias in TypeScript declarations&quot; data-og-description=&quot;Build TypeScript library with alias&quot; data-og-host=&quot;medium.com&quot; data-og-source-url=&quot;https://medium.com/@joshuaavalon/webpack-alias-in-typescript-declarations-81d2b6c0dcd6&quot; data-og-url=&quot;https://medium.com/@joshuaavalon/webpack-alias-in-typescript-declarations-81d2b6c0dcd6&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/vWJOv/hyFmIEygiV/ngaKEIxKZFnID5tviuKXJ0/img.jpg?width=1005&amp;amp;height=600&amp;amp;face=0_0_1005_600,https://scrap.kakaocdn.net/dn/pe8fO/hyFmTlKcWr/kMEnkAmv8xOZfRyy82XYC1/img.jpg?width=60&amp;amp;height=35&amp;amp;face=0_0_60_35&quot;&gt;&lt;a href=&quot;https://medium.com/@joshuaavalon/webpack-alias-in-typescript-declarations-81d2b6c0dcd6&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://medium.com/@joshuaavalon/webpack-alias-in-typescript-declarations-81d2b6c0dcd6&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/vWJOv/hyFmIEygiV/ngaKEIxKZFnID5tviuKXJ0/img.jpg?width=1005&amp;amp;height=600&amp;amp;face=0_0_1005_600,https://scrap.kakaocdn.net/dn/pe8fO/hyFmTlKcWr/kMEnkAmv8xOZfRyy82XYC1/img.jpg?width=60&amp;amp;height=35&amp;amp;face=0_0_60_35');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Webpack alias in TypeScript declarations&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Build TypeScript library with alias&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;medium.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Etc</category>
      <category>alias</category>
      <category>JavaScript</category>
      <category>TypeScript</category>
      <category>webpack</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/194</guid>
      <comments>https://im-developer.tistory.com/194#entry194comment</comments>
      <pubDate>Sat, 21 Mar 2020 15:46:31 +0900</pubDate>
    </item>
    <item>
      <title>[JS] 자바스크립트, Nullish Coalescing Operator (다른 논리 연산자와 비교)</title>
      <link>https://im-developer.tistory.com/192</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;490&quot; height=&quot;349&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dfGFtZ/btqCD8HN3i9/oYuvS78kAe1VTYL8uLvs4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dfGFtZ/btqCD8HN3i9/oYuvS78kAe1VTYL8uLvs4K/img.png&quot; data-alt=&quot;a, b, c, d, e를 console.log()로 출력하면 뭐가 찍힐까요?&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dfGFtZ/btqCD8HN3i9/oYuvS78kAe1VTYL8uLvs4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdfGFtZ%2FbtqCD8HN3i9%2FoYuvS78kAe1VTYL8uLvs4K%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;490&quot; height=&quot;349&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;a, b, c, d, e를 console.log()로 출력하면 뭐가 찍힐까요?&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;오늘은 자바스크립트의 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;Nullish coalescing operator&lt;/b&gt;&lt;/span&gt;에 대해서 간단히 정리해보려고 한다.&lt;/p&gt;
&lt;p&gt;(TypeScript 3.7부터 지원된다!)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;coalesce는 사전을 찾아보니 뭔가를 합친다, 병합하다 이런 뜻을 가지고 있다.&lt;/p&gt;
&lt;p&gt;아마 Nullish는 null과 같은 것들을 말하는 것일거다.&lt;/p&gt;
&lt;p&gt;그러니까 null과 같은 것들을 병합하는 연산자(?)라고 할 수 있을 것 같다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;물론 뭔 말인지 하나도 모르겠으니 MDN의 설명을 찾아보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Nullish coalescing operator(??)는 논리 연산자로&lt;/p&gt;
&lt;p&gt;왼쪽 피연산자가 null이나 undefined일 때, 오른쪽 피연산자를 return한다.&lt;/p&gt;
&lt;p&gt;반대의 경우에는 왼쪽 피연산자가 return된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그럼 이제 내가 맨 처음에 냈던 문제의 답을 알아보자!&lt;/p&gt;
&lt;pre id=&quot;code_1583973130225&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let a = null ?? 'hello';
let b = '' ?? true;
let c = false ?? true;
let d = 0 ?? 1;
let e = undefined ?? 'world';

console.log(a);	// 'hello'
console.log(b); // ''
console.log(c); // false
console.log(d); // 0
console.log(e); // 'world'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 OR 연산자(||)와 어떻게 다른지 알 수 있을 것이다.&lt;/p&gt;
&lt;p&gt;OR 연산자는 왼쪽 피연산자가 null, undefined 뿐만 아니라 falsy값이면 오른쪽 피연산자를 return한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1583973354976&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let a = null || 'hello';
let b = '' || true;
let c = false || true;
let d = 0 || 1;
let e = undefined || 'world';

console.log(a);	// 'hello'
console.log(b); // true
console.log(c); // true
console.log(d); // 1
console.log(e); // 'world'
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Short-circuiting&lt;/h3&gt;
&lt;pre id=&quot;code_1583973947139&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;leftExpr ?? rightExpr&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;다른 논리연산자와 마찬가지로 왼쪽 피연산자가 null이나 undefined가 아니어서&lt;/p&gt;
&lt;p&gt;오른쪽 피연산자를 계산할 필요가 없는 상황이라면 오른쪽 피연산자는 평가되지 않는다.&lt;/p&gt;
&lt;p&gt;(즉, 왼쪽 피연산자만 return하고 오른쪽 피연산자는 무시된다.)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;No chaining with AND or OR operators&lt;/h3&gt;
&lt;pre id=&quot;code_1583973906579&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;null || undefined ?? &quot;foo&quot;; // raises a SyntaxError&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;?? 연산자는 AND(&amp;amp;&amp;amp;)와 OR(||)연산자와 직접적으로 같이 사용하는 것은 불가능하다.&lt;/p&gt;
&lt;p&gt;위와 같이 사용하면 SyntaxError가 발생할 것이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1583974030650&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(null || undefined) ?? &quot;foo&quot;; // returns &quot;foo&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그렇지만 소괄호와 함께 사용하면 가능하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;Reference&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1583974105567&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Nullish coalescing operator&quot; data-og-description=&quot;The nullish coalescing operator (??) is a logical operator that returns its right-hand side operand when its left-hand side operand is null or undefined, and otherwise returns its left-hand side operand.&quot; data-og-host=&quot;developer.mozilla.org&quot; data-og-source-url=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator&quot; data-og-url=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/F6pTn/hyFeVyzMWa/T8xbOzNX9c00Mh8jkkNxLk/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600,https://scrap.kakaocdn.net/dn/bUrdtx/hyFgmagG1Q/Nwn1rAjI2mtipgsLmXCtSk/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/F6pTn/hyFeVyzMWa/T8xbOzNX9c00Mh8jkkNxLk/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600,https://scrap.kakaocdn.net/dn/bUrdtx/hyFgmagG1Q/Nwn1rAjI2mtipgsLmXCtSk/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Nullish coalescing operator&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;The nullish coalescing operator (??) is a logical operator that returns its right-hand side operand when its left-hand side operand is null or undefined, and otherwise returns its left-hand side operand.&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;developer.mozilla.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend</category>
      <category>JavaScript</category>
      <category>연산자</category>
      <category>자바스크립트</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/192</guid>
      <comments>https://im-developer.tistory.com/192#entry192comment</comments>
      <pubDate>Thu, 12 Mar 2020 09:50:27 +0900</pubDate>
    </item>
    <item>
      <title>[JS] 자바스크립트, Optional Chaining</title>
      <link>https://im-developer.tistory.com/191</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;247&quot; height=&quot;245&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmVaUj/btqCzAMacDJ/5Zsjoa8wKJJlP4HnyXtZDK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmVaUj/btqCzAMacDJ/5Zsjoa8wKJJlP4HnyXtZDK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmVaUj/btqCzAMacDJ/5Zsjoa8wKJJlP4HnyXtZDK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcmVaUj%2FbtqCzAMacDJ%2F5Zsjoa8wKJJlP4HnyXtZDK%2Fimg.jpg&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;247&quot; height=&quot;245&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;휴, 회사 업무 하랴, 공부하랴 요즘 정신이 없다 &lt;/p&gt;
&lt;p&gt;오늘은 어제 업무하다가 새롭게 알게 된 &lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;Optional Chaining&lt;/b&gt;&lt;/span&gt;에 대해서 아주 간략하게 정리해보려고 한다.&lt;/p&gt;
&lt;p&gt;(Optional Chaining은 타입스크립트 3.7부터 지원된다!)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Summary&lt;/h3&gt;
&lt;p&gt;Optional chaining은&amp;nbsp; &lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;&amp;nbsp;?. &lt;/b&gt;&lt;/span&gt;&amp;nbsp;operator와 함께 사용한다.&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&lt;b&gt;&amp;nbsp;.&lt;/b&gt; &lt;/span&gt;chaining operator와 비슷하게 사용이 가능한데,&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;&amp;nbsp;? &lt;/span&gt;&lt;/b&gt;를 붙임으로서 그 속성이 존재하는 경우에만 chaining을 계속하고&lt;/p&gt;
&lt;p&gt;존재하지 않으면 &lt;b&gt;undefined&lt;/b&gt;를 리턴하게 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Example&lt;/h3&gt;
&lt;pre id=&quot;code_1583888164265&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const person = {
  name: 'Alice',
  job: {
    companyName: 'abc',
    title: 'developer'
  }
};

console.log(person.job.companyName); // 'abc'
console.log(person.pet.name); // error&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;만약에 위와 같은 person 객체가 존재한다고 해보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;person.job.companyName을 접근하면 'abc'가 출력될 것이다.&lt;/p&gt;
&lt;p&gt;그런데 만약에 person에 존재하지 않는 속성을 체이닝하면 에러가 발생한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1583888341763&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const person = {
  name: 'Alice',
  job: {
    companyName: 'abc',
    title: 'developer'
  }
};

console.log(person.job.companyName); // 'abc'
console.log(person.pet?.name); // undefined&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;이 때 Optional Chaining을 사용하면 에러가 발생하는 대신 undefined가 리턴된다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Syntax&lt;/h3&gt;
&lt;p&gt;이러한 Optional Chaining은 object의 prop뿐만 아니고 배열, 함수에도 적용할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1583888461145&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;obj?.prop
obj?.[expr]
arr?.[index]
func?.(args)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Description&lt;/h3&gt;
&lt;pre id=&quot;code_1583888483939&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let nestedProp = obj.first &amp;amp;&amp;amp; obj.first.second;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;즉, 그 동안은 객체가 깊게 nested된 경우에 에러를 내지 않기 위해서 위와 같은 방법을 사용했어야만 했다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1583888539806&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let nestedProp = obj.first?.second;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;그러나 이제는 위와 같이 적용하여 에러 없이 원하는 결과물을 얻을 수 있다! (너무 편리해보인다. 이걸 이제서야 알았다니...)&lt;/p&gt;
&lt;p&gt;?. 를 만나면 JavaScript는 알아서 그 속성이 null이나 undefined인지 검사하고, null과 undefined가 아닌 경우에만 체이닝을 계속한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1583888814078&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;let result = someInterface.customMethod?.();&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;함수와 함께 사용할때는 위와 같이 함수 이름 뒤에 Optional Chaining을 붙이고 함수를 실행시키면&lt;/p&gt;
&lt;p&gt;customMethod라는 함수가 존재하는 경우에만 함수를 실행하고, 존재하지 않으면 undefined를 리턴한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1583887527485&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Optional chaining&quot; data-og-description=&quot;The ?. operator functions similarly to the. chaining operator, except that instead of causing an error if a reference is nullish (null or undefined), the expression short-circuits with a return value of undefined.&quot; data-og-host=&quot;developer.mozilla.org&quot; data-og-source-url=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining&quot; data-og-url=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cMxjLs/hyFeQiUH0I/D2YAkqp83kmFZ0gQd0U5R0/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600,https://scrap.kakaocdn.net/dn/bf5RQp/hyFeN7xKEd/nnkkjqc4Is1BpcaaOkrTm1/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cMxjLs/hyFeQiUH0I/D2YAkqp83kmFZ0gQd0U5R0/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600,https://scrap.kakaocdn.net/dn/bf5RQp/hyFeN7xKEd/nnkkjqc4Is1BpcaaOkrTm1/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Optional chaining&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;The ?. operator functions similarly to the. chaining operator, except that instead of causing an error if a reference is nullish (null or undefined), the expression short-circuits with a return value of undefined.&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;developer.mozilla.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend</category>
      <category>JavaScript</category>
      <category>자바스크립트</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/191</guid>
      <comments>https://im-developer.tistory.com/191#entry191comment</comments>
      <pubDate>Wed, 11 Mar 2020 10:11:43 +0900</pubDate>
    </item>
    <item>
      <title>[JS/CustomEvent] 자바스크립트, 커스텀 이벤트 생성하기</title>
      <link>https://im-developer.tistory.com/190</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;281&quot; height=&quot;281&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pDx7m/btqCzAQ5dq6/zDXEkevv47XhjSeTsn0IEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pDx7m/btqCzAQ5dq6/zDXEkevv47XhjSeTsn0IEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pDx7m/btqCzAQ5dq6/zDXEkevv47XhjSeTsn0IEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpDx7m%2FbtqCzAQ5dq6%2FzDXEkevv47XhjSeTsn0IEK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;281&quot; height=&quot;281&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;회사에서 이번에 새로 맡은 업무를 기획중인데,&lt;/p&gt;
&lt;p&gt;Native 앱이 웹뷰에 뭔가를 요청할때마다 웹뷰에서 이벤트가 trigger되도록 만들어야해서 어떻게 할지 엄청 고민을 많이 했었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;웹뷰가 Native Client에 Data를 요청하는 프로세스는 이미 구현이 되어있는데,&lt;/p&gt;
&lt;p&gt;Native Client가 필요할때마다 웹뷰에게 특정 이벤트를 실행하도록 요청하는 프로세스가 구현이 안되어있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.android.com/guide/webapps/webview?hl=ko&quot;&gt;https://developer.android.com/guide/webapps/webview?hl=ko&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1583553540627&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;WebView에서 웹 앱 빌드 &amp;nbsp;|&amp;nbsp; Android 개발자 &amp;nbsp;|&amp;nbsp; Android Developers&quot; data-og-description=&quot;웹 애플리케이션 또는 웹페이지만 클라이언트 애플리케이션의 일부로 제공하려는 경우 WebView를 사용하면 됩니다. WebView 클래스는 Android의 View 클래스의 확장으로, 웹페이지를 활동 레이아웃의 일부로 표시할 수 있게 해 줍니다. 탐색 컨트롤이나 주소 표시줄 등 완전히 개발된 웹브라우저의 기능은 전혀 포함되어 있지 않습니다. WebView의 모든 작업은 기본적으로 웹페이지를 표시하는 것입니다. 일반적으로 WebView를 사용하는 것이 도움이&quot; data-og-host=&quot;developer.android.com&quot; data-og-source-url=&quot;https://developer.android.com/guide/webapps/webview?hl=ko&quot; data-og-url=&quot;https://developer.android.com/guide/webapps/webview?hl=ko&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://developer.android.com/guide/webapps/webview?hl=ko&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.android.com/guide/webapps/webview?hl=ko&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;WebView에서 웹 앱 빌드 &amp;nbsp;|&amp;nbsp; Android 개발자 &amp;nbsp;|&amp;nbsp; Android Developers&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;웹 애플리케이션 또는 웹페이지만 클라이언트 애플리케이션의 일부로 제공하려는 경우 WebView를 사용하면 됩니다. WebView 클래스는 Android의 View 클래스의 확장으로, 웹페이지를 활동 레이아웃의 일부로 표시할 수 있게 해 줍니다. 탐색 컨트롤이나 주소 표시줄 등 완전히 개발된 웹브라우저의 기능은 전혀 포함되어 있지 않습니다. WebView의 모든 작업은 기본적으로 웹페이지를 표시하는 것입니다. 일반적으로 WebView를 사용하는 것이 도움이&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;developer.android.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Native 앱과 웹뷰가 어떻게 소통하는지 열심히 찾아보니,&lt;/p&gt;
&lt;p&gt;일단 Native 앱에서 웹뷰 내부에 선언된 JavaScript 함수를 실행하는 방식으로 웹뷰에게 무언가를 전달할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;그렇다면 웹뷰에서 어떤 함수를 정의한 다음에,&lt;/p&gt;
&lt;p&gt;Native가 그걸 실행할때마다 웹뷰 컴포넌트가 mount된 상태에서 등록된&lt;/p&gt;
&lt;p&gt;특정 이벤트 함수가 실행되도록 해야했다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;그래서 찾다가 발견한 CustomEvent!&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;우리가 흔히 사용하는 click, change, submit 등의 이벤트와 마찬가지로 &lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;CustomEvent를 생성해서 사용할 수 있다는 것!&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 CustomEvent로 생성한 이벤트를 dispatch해주는 웹뷰 함수를 Native가 실행해주면&lt;/p&gt;
&lt;p&gt;Native가 원할때마다 웹뷰의 이벤트를 trigger할 수 있지 않을까? 하는 생각을 하게 되었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p style=&quot;font-size: 1.25em;&quot; data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;new CustomEvent()&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1583555317391&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;event = new CustomEvent(typeArg, customEventInit);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;CustomEvent는 생성자 함수이며 위와 같이 사용하여 이벤트 instance를 생성한다.&lt;/p&gt;
&lt;p&gt;2개의 Parameters를 가지는데, 다음과 같다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;typeArg: &lt;/b&gt;이벤트 이름&lt;/li&gt;
&lt;li&gt;&lt;b&gt;customEventInit: &lt;/b&gt;Optional한 속성으로 'detail'이라는 field를 가지고 있는 객체이다.
&lt;ul style=&quot;list-style-type: disc;&quot;&gt;
&lt;li&gt;&lt;b&gt;detail: &lt;/b&gt;이 속성은 readonly 속성으로, 처음 이벤트 생성할 때에만 값을 넣어줄 수 있다. 만약에 아무것도 넣지 않으면 null이 할당된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;customEvent는 익스플로러에서는 지원하지 않지만&lt;/p&gt;
&lt;p&gt;다음과 같은 polyfill을 사용하면 익스플로러 9 이상의 브라우저에서는 사용할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1583555225492&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;(function () {

  if ( typeof window.CustomEvent === &quot;function&quot; ) return false;

  function CustomEvent ( event, params ) {
    params = params || { bubbles: false, cancelable: false, detail: null };
    var evt = document.createEvent( 'CustomEvent' );
    evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail );
    return evt;
   }

  window.CustomEvent = CustomEvent;
})();&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;EventTarget.addEventListener()&lt;/h4&gt;
&lt;pre id=&quot;code_1583555369815&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;EventTarget.addEventListener(typeArg, listener);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이제 아까 생성한 이벤트 이름을 넣어서 이벤트를 등록해주어야 한다.&lt;/p&gt;
&lt;p&gt;이렇게 되면 나중에 이벤트가 dispatch될 때마다 등록된 listener 함수가 실행된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;EventTarget.dispatchEvent()&lt;/h4&gt;
&lt;pre id=&quot;code_1583555110843&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;EventTarget.dispatchEvent(event);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아까 위에서 생성된 이벤트 instance를 dispatchEvent라는 함수의 인자로 넣어 trigger할 수 있다.&lt;/p&gt;
&lt;p&gt;dispatchEvent 함수가 실행되면, addEventListener로 등록된 이벤트가 발생하여&lt;/p&gt;
&lt;p&gt;listener 함수가 실행되는 구조이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1583555533763&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
  &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
  &amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&amp;gt;
  &amp;lt;title&amp;gt;Document&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
  &amp;lt;input type=&quot;text&quot; id=&quot;inputText&quot; readonly /&amp;gt;
  &amp;lt;button type=&quot;button&quot; id=&quot;btnMessage&quot;&amp;gt;Toggle Message&amp;lt;/button&amp;gt;
  &amp;lt;script&amp;gt;
    const $input = document.querySelector('#inputText');
    const $btn = document.querySelector('#btnMessage');

    const event = new CustomEvent('setDefaultMessage', {
      detail: {
        message: 'sample message'
      }
    });

    $input.addEventListener('setDefaultMessage', function(e) {
      $input.value = $input.value ? '' : e.detail.message;
    });

    $btn.addEventListener('click', function(e) {
      $input.dispatchEvent(event);
    });
  &amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아주 간단한 예제 코드를 만들어보았다.&lt;/p&gt;
&lt;p&gt;setDefaultMessage라는 이름의 이벤트를 생성하여 버튼을 클릭할때마다 이벤트가 실행되도록 만들었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend</category>
      <category>CustomEvent</category>
      <category>JavaScript</category>
      <category>이벤트</category>
      <category>자바스크립트</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/190</guid>
      <comments>https://im-developer.tistory.com/190#entry190comment</comments>
      <pubDate>Sat, 7 Mar 2020 13:33:32 +0900</pubDate>
    </item>
    <item>
      <title>[TypeScript] 타입스크립트 - Generic</title>
      <link>https://im-developer.tistory.com/189</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;typescript.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/py3ZE/btqCrlgfhxg/ZXydnkHRNJ64ziICTgC3Gk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/py3ZE/btqCrlgfhxg/ZXydnkHRNJ64ziICTgC3Gk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/py3ZE/btqCrlgfhxg/ZXydnkHRNJ64ziICTgC3Gk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fpy3ZE%2FbtqCrlgfhxg%2FZXydnkHRNJ64ziICTgC3Gk%2Fimg.png&quot; data-filename=&quot;typescript.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;720&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;function generateString(message: string): string {
  return message;
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;function generateNumber(message: number): number {
    return message;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위 예시와 같이 string을 인자로 받아서 string을 리턴하는 함수와&lt;/p&gt;
&lt;p&gt;number를 인자로 받아서 number를 리턴하는 함수가 있다고 해보자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;두 개 모두 인자를 받아서 그대로 리턴하는 아주 단순한 함수이다.&lt;/p&gt;
&lt;p&gt;그런데 하나는 string을 처리하고 하나는 number를 처리한다는 차이점 밖에 없다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이렇게 동일한 로직을 수행하는 함수를 단지 타입이 다르다는 이유로&lt;/p&gt;
&lt;p&gt;별개의 함수로 구현한다는 것은 매우 비효율적인 일이다.&lt;/p&gt;
&lt;p&gt;바로 이러한 상황에 유용한 것이 바로 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;Generic&lt;/b&gt;&lt;/span&gt;이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;verilog&quot;&gt;&lt;code&gt;function generate&amp;lt;T&amp;gt;(message: T): T {
  return message;
}

generate&amp;lt;string&amp;gt;('Mark');
generate&amp;lt;number&amp;gt;(3);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Generic은 위와 같이 사용한다.&lt;/p&gt;
&lt;p&gt;함수 이름 옆에 &lt;b&gt;&amp;lt;&amp;gt;&lt;/b&gt;를 쓰고 &amp;lt;&amp;gt; 안에 Generic으로 사용할 문자를 적어준다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;예시에서는 T라고 적었다. 이제 이 함수는 T를 인자로 받아서 T를 리턴하게끔 되어있는데,&lt;/p&gt;
&lt;p&gt;이제 함수를 사용하는 시점에 Generic type을 지정해줄 수 있다!&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;p&gt;만약 함수 호출 시 &amp;lt;&amp;gt;로 Generic type을 쓰지 않으면 인자로 넣은 값으로 Ts compiler가 T를 추론한다.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Generic type을 쓰면 T를 내가 지정한 타입을 기준으로 판단한다.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1583239714677&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const a: string[] = ['a'];
const b: Array = ['b']; // 내부적으로 generic을 이용한거임&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;TypeScript에서 Array type을 지정할때 2가지 형태가 있는데,&lt;/p&gt;
&lt;p&gt;아래와 같은 표기가 바로 TypeScript에서 내부적으로 Generic을 이용한 표기이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;function hello&amp;lt;T&amp;gt;(messages: T[]): T {
  return messages[0];
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;배열의 item의 type을 Generic과 사용하고 싶다면 위와 같이 적용하면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;type PracticeGeneric = &amp;lt;T&amp;gt;(message: T) =&amp;gt; T;

const practice: PracticeGeneric = &amp;lt;T&amp;gt;(message: T): T =&amp;gt; {
  return message;
};

practice&amp;lt;string&amp;gt;('hello world').length; // 제대로 작동 안하는 경우가 많아서 잘 사용하지 않음

class Something&amp;lt;T&amp;gt; {
  private color: T;

  constructor(color: T) {
        this.color = color;
  }
}

const obj1 = new Something&amp;lt;string&amp;gt;('red');
const obj2 = new Something&amp;lt;string&amp;gt;(3); // error&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;클래스에서 Generic은 위와 같이 사용한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;Generic with extends&lt;/h3&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;class Person&amp;lt;T extends string | number&amp;gt; {
  private name: T;

  constructor(name: T) {
    this.name = name;
  }
}

new Person(false); // error&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Generic은 extends와 같이 사용할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;Generic with multiple types&lt;/h3&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;class Person&amp;lt;T, P&amp;gt; {
  private name: T;
  private age: P;

  constructor(name: T, age: P) {
    this.name = name;
    this.age = age;
  }
}

new Person&amp;lt;string, number&amp;gt;('Elise', 10);&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;여러 개의 Generic도 사용 가능하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;Type lookup system&lt;/h3&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;interface Person {
  name: string;
  age: number;
}

const person: Person = {
  name = 'Jane';
  age: 20;
};

function getProperty&amp;lt;T, K extends keyof T&amp;gt;(obj: T, key: K): T[K] {
  return obj[key];
}

function setProperty&amp;lt;T, K extends keyof T&amp;gt;(obj: T, key: K, value: T[K]): void {
  obj[key] = value;
}

getProperty(person, 'name');
setProperty(person, 'job', 'developer');&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위와 같이 Generic을 이용하여 많은 것들을 할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3&gt;Keyof&lt;/h3&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;interface Person {
  name: string;
  age: number;
}

type Prop = keyof Person; // 'name' | 'age'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위에서 살짝 언급된 keyof는 어떤 interface의 key만 type으로 지정하는 것이다.&lt;/p&gt;
&lt;p&gt;즉, Prop은 Person interface의 name이나 age로만 사용 가능하다.&lt;/p&gt;</description>
      <category>Frontend</category>
      <category>JavaScript</category>
      <category>TypeScript</category>
      <category>자바스크립트</category>
      <category>타입스크립트</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/189</guid>
      <comments>https://im-developer.tistory.com/189#entry189comment</comments>
      <pubDate>Tue, 3 Mar 2020 21:49:48 +0900</pubDate>
    </item>
    <item>
      <title>[TypeScript] 타입스크립트 - Type assertions, Type alias</title>
      <link>https://im-developer.tistory.com/188</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4XWdB/btqCsGRn3Ft/Krlr8jcDakr83b1gkEgEMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4XWdB/btqCsGRn3Ft/Krlr8jcDakr83b1gkEgEMk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4XWdB/btqCsGRn3Ft/Krlr8jcDakr83b1gkEgEMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4XWdB%2FbtqCsGRn3Ft%2FKrlr8jcDakr83b1gkEgEMk%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;각잡고 글 쓰려고 마음 먹으니 너무 부담스럽고 잘 안하게 되어서&lt;br /&gt;아주 가볍게 요약 정리하는 느낌으로 타입스크립트 기초 내용들을 하나씩 정리해보려고 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;h2&gt;Type assertions&lt;/h2&gt;
&lt;p&gt;실제 데이터 구조를 바꿔주는 형 변환이 아니고 단지 &amp;lsquo;타입이 이것이다&amp;rsquo;라는 것을 typescript 컴파일러에게 알려주는 것을 의미&lt;/p&gt;
&lt;p&gt;(즉, 개발자가 확실히 이 타입이라고 확신하는 경우에만 사용한다.)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;변수 as 강제할 타입&lt;/li&gt;
&lt;li&gt;&amp;lt;강제할타입&amp;gt;변수&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;let someValue: any = 'this is a string';

let strLength: number = (&amp;lt;string&amp;gt;someValue).length;
let strLength2: number = (someValue as string).length;  // jsx에서는 as를 많이 사용한다. (&amp;lt;&amp;gt; 사용 시 헷갈릴 수 있기 때문)&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&amp;nbsp;&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2&gt;Type alias&lt;/h2&gt;
&lt;p&gt;인터페이스와 비슷해 보인다. (대체할 수 있는 부분이 꽤 있음)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Primitive, Union Type, Tuple에 사용 가능&lt;/li&gt;
&lt;li&gt;반복적으로 같은 타입을 계속 사용하기 귀찮을때 Type alias 선언한 후 재사용하기 위해 사용&lt;/li&gt;
&lt;li&gt;만들어진 타입의 refer로 사용하는 것이지 타입을 만드는 것이 아님&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Aliasing Union Type&lt;/h4&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;let foo: string | number = 0;
foo = 'hello';&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;⬇️&lt;/p&gt;
&lt;pre class=&quot;gauss&quot;&gt;&lt;code&gt;type StringOrNumber = string | number;

let bar: StringOrNumber = 0;
bar = 'world';&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Aliasing Tuple&lt;/h4&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;let person: [string, number] = ['Jane', 10];&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;⬇️&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;type PersonTuple = [string, number];

let another: PersonTuple = ['Tom', 21];  &lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Interface와 차이점&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;
&lt;p&gt;Alias 사용 시 지정한 이름으로 타입을 생성하는 것이 아니고 reference로만 사용&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Type alias는 새로운 이름을 생성하지 않는다. object literal type으로만 인지&lt;/li&gt;
&lt;li&gt;Interface 정의 시 어디에서나 사용 가능한 새로운 이름을 생성한다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Type alias는 interface와 달리 상속이 불가능 (extends, implements가 안됨)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot;&gt;
&lt;li&gt;이 부분은 2.7 버전부터 변경되어 type alias도 다른 타입으로부터 상속이 가능해짐&lt;/li&gt;
&lt;li&gt;그러나 되도록이면 상속이 필요한 경우 Interface 사용을 권장&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Type alias와 Generic&lt;/h4&gt;
&lt;p&gt;generic은 다음 포스팅에서 정리할 예정이긴한데, 어쨌든 type alias도 generic과 함께 사용 가능.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1583155803475&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;type Container&amp;lt;T&amp;gt; = { value: T };

let container: Container&amp;lt;number&amp;gt; = { value: 123 };&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend</category>
      <category>JavaScript</category>
      <category>TypeScript</category>
      <category>자바스크립트</category>
      <category>타입스크립트</category>
      <author>제이JY</author>
      <guid isPermaLink="true">https://im-developer.tistory.com/188</guid>
      <comments>https://im-developer.tistory.com/188#entry188comment</comments>
      <pubDate>Mon, 2 Mar 2020 22:24:43 +0900</pubDate>
    </item>
  </channel>
</rss>