<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ray Kim</title>
    <description>The latest articles on DEV Community by Ray Kim (@yomandawg).</description>
    <link>https://dev.to/yomandawg</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F468918%2F0e9feea0-171c-4fe6-aa73-55359db87faf.jpeg</url>
      <title>DEV Community: Ray Kim</title>
      <link>https://dev.to/yomandawg</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/yomandawg"/>
    <language>en</language>
    <item>
      <title>React SSR 아키텍처 - Render Server</title>
      <dc:creator>Ray Kim</dc:creator>
      <pubDate>Thu, 31 Dec 2020 02:50:38 +0000</pubDate>
      <link>https://dev.to/yomandawg/react-ssr-2-render-server-olm</link>
      <guid>https://dev.to/yomandawg/react-ssr-2-render-server-olm</guid>
      <description>&lt;p&gt;SSR을 할 경우 리액트 빌드 서버(또는 클라이언트용 웹팩)와 별개로 사용자 요청을 받아 알맞는 리액트 앱을 응답하는 렌더링용 서버가 필요하다.&lt;/p&gt;

&lt;p&gt;렌더링용 서버에선 &lt;code&gt;ReactDOMServer.renderToString&lt;/code&gt; 등의 기본 메서드를 통해 리액트를 간단히 SSR할 수 있었지만, 이는 Node.js(또는 JavaScript runtime) 서버 위에서 작동한다.&lt;/p&gt;

&lt;p&gt;즉, React SSR을 할 경우 Node.js 서버가 가장 알맞은 선택일 것이다.&lt;/p&gt;

&lt;p&gt;그렇다면 효율적인 SSR 서버 구현에 대해 생각해보자.&lt;/p&gt;

&lt;center&gt;&lt;br&gt;&lt;/center&gt;

&lt;h2&gt;
  
  
  계층화
&lt;/h2&gt;

&lt;p&gt;SSR 웹 앱을 백엔드 서버와 렌더링 서버로 분리할 수 있다고 보자.&lt;/p&gt;

&lt;p&gt;이렇게 계층화를 이룰 경우, 렌더링 서버를 추상화 및 스케일아웃할 수 있으며, 백엔드의 여러 서비스를 프록시를 통해 적재적소에 이용할 수 있다.&lt;/p&gt;

&lt;center&gt;
![계층화](https://dev-to-uploads.s3.amazonaws.com/i/1v2hjqipinli7999zx7f.png)
\
&lt;br&gt;
&lt;/center&gt;

&lt;p&gt;그렇다면 렌더링 서버에 어떤 기능을 포함해야 할까? 막상 계층화를 이뤘다고 해도, 백엔드 서버와 렌더링 서버의 경계는 모호할 수밖에 없다.&lt;/p&gt;

&lt;p&gt;먼저 관심사 분리를 명확히 한 렌더링 서버는 오로지 &lt;em&gt;rendering&lt;/em&gt;에 집중한다고 가정하자.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Express.js 예시&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MyApp&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;renderToString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;html&amp;gt;
      &amp;lt;body&amp;gt;
        &amp;lt;div id="app"&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/div&amp;gt;
        &amp;lt;script src="bundle.js"&amp;gt;&amp;lt;/script&amp;gt;
      &amp;lt;/body&amp;gt;
    &amp;lt;/html&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// 사용자가 요청할 `bundle.js`의 경로&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;static&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;public&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// 모든 path에 대한 동일 `renderer` 처리한다: `req` 오브젝트를 사용해 렌더링한다.&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;위 예시처럼 모든 path(&lt;code&gt;'*'&lt;/code&gt;)에 대해 같은 콜백을 넘겨주자. 이는 &lt;code&gt;req&lt;/code&gt; 객체만을 사용해 렌더링하겠다는 의미이며, 콜백 내부에 렌더링 로직이 들어간다.&lt;/p&gt;

&lt;p&gt;물론 MPA일 경우 페이지별로 라우터를 관리할 수도 있지만, 라우팅은 추후 &lt;code&gt;react-router&lt;/code&gt;이 관여하므로 모든 path에 대한 처리를 동일하게 한다.&lt;/p&gt;

&lt;p&gt;마지막으로 &lt;code&gt;express.static('public')&lt;/code&gt; 미들웨어를 사용하는 이유는 지난 포스트에서 다룬 것과 같이 SSR 후 사용자가 &lt;code&gt;bundle.js&lt;/code&gt;를 추가적으로 요청할 때, 미리 번들링한 파일을 넘겨주는 용도이다.&lt;/p&gt;

&lt;center&gt;&lt;br&gt;&lt;/center&gt;

&lt;h2&gt;
  
  
  라우팅
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;react-router&lt;/code&gt; 라이브러리의 &lt;a href="https://reactrouter.com/web/api/BrowserRouter" rel="noopener noreferrer"&gt;&lt;code&gt;BrowserRouter&lt;/code&gt;&lt;/a&gt;은 브라우저의 location 변화를 감지하여 지정된 라우터에 따라 컴포넌트를 렌더링해준다. 특이한 점은 &lt;code&gt;history.pushState&lt;/code&gt;를 사용하여 브라우저 location을 리로딩 없이 변경해준다는 것이다. 즉, 브라우저 상태변화에 따라 새로운 요청이나 리로딩 없이 UI를 변화시켜준다.&lt;/p&gt;

&lt;p&gt;하지만 서버에선 브라우저와 달리 location 변화가 일어나지 않으며, 오로지 요청(&lt;code&gt;req&lt;/code&gt; 객체의 path)에 대한 라우팅만 가능하다. 또한 location 변화에는 유저 interaction이 필요하며, 서버에서 &lt;code&gt;req&lt;/code&gt;만으로 interaction을 감지할 수는 없다.&lt;/p&gt;

&lt;p&gt;앞서 다뤘듯 렌더링 서버는 &lt;code&gt;app.get('*', () =&amp;gt; {})&lt;/code&gt;을 통해 모든 path에 대한 같은 렌더링 로직을 사용한다. 이런 구조를 변경하여 path에 따라 일일이 렌더링 로직 및 리액트 앱을 관리하기엔 isomorphic 구조를 유지하기도 어렵고 비효율적이다.&lt;/p&gt;

&lt;p&gt;그렇다고 &lt;code&gt;BrowserRouter&lt;/code&gt; 컴포넌트를 바로 보낸다는 것은 클라이언트에게 렌더링을 맡긴다는 의미이다. 브라우저 location 변화를 기준으로 UI를 관리하는 &lt;code&gt;BrowserRouter&lt;/code&gt;는 결국 리액트 앱의 일부분일 뿐이며 &lt;code&gt;bundle.js&lt;/code&gt;에 포함된다.&lt;/p&gt;

&lt;p&gt;그렇다면 원하는 URL에 따라 서버에서 렌더링하여 사용자에게 줄 수 있는 방법이 필요할 것이다. 이 때 사용하는 SSR용 라우터가 바로 &lt;a href="https://reactrouter.com/web/api/StaticRouter" rel="noopener noreferrer"&gt;&lt;code&gt;StaticRouter&lt;/code&gt;&lt;/a&gt;이다.&lt;/p&gt;

&lt;p&gt;다음 예시를 보자.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 렌더링 서버 코드&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&gt;/* Express Request */&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// `StaticRouter`은 제공된 경로(req.path)를 기반으로 렌더링한다.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;StaticRouter&lt;/span&gt; &lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MyRoutes&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;StaticRouter&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;renderToString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;html&amp;gt;
      &amp;lt;body&amp;gt;
        &amp;lt;div id="app"&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/div&amp;gt;
        &amp;lt;script src="bundle.js"&amp;gt;&amp;lt;/script&amp;gt;
      &amp;lt;/body&amp;gt;
    &amp;lt;/html&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;StaticRouter&lt;/code&gt;은 &lt;code&gt;req.path&lt;/code&gt;를 기반으로 리액트 앱을 빌드한다. 이를 이용하면 location 변화에 따른 UI 업데이트가 아닌 처음부터 URL 입력값을 기준으로 렌더링된 HTML을 만들어 줄 수 있다.&lt;/p&gt;

&lt;p&gt;하지만 여전히 클라이언트는 &lt;code&gt;BrowserRouter&lt;/code&gt;을 사용하는 것이 타당하다. 이미 렌더링된 앱에선 새로운 페이지를 로드하는 것이 아닌 상태 변화에 따라 UI를 변경하는 것이 더 효율적이기 때문이다.&lt;/p&gt;

&lt;center&gt;
![Router](https://dev-to-uploads.s3.amazonaws.com/i/lsiy9ocj2cwnemzd82vh.png)
\
&lt;br&gt;
&lt;/center&gt;

&lt;p&gt;리소스(&lt;code&gt;bundle.js&lt;/code&gt;)는 앱 로딩 후 이미 가져온 상태이기 때문에 &lt;code&gt;BrowserRouter&lt;/code&gt;의 사용제한이 없으며 클라이언트가 렌더링을 담당하게 된다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 클라이언트 코드&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;BrowserRouter&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MyRoutes&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;BrowserRouter&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;ReactDOM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hydrate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ReactDOM.hydrate&lt;/code&gt;는 이런 차이를 인지하고, Router 내부의 코드를 알맞게 hydration하므로 &lt;code&gt;StaticRouter&lt;/code&gt;(서버)과 &lt;code&gt;BrowserRouter&lt;/code&gt;(클라이언트)을 교차 사용하는 것이 가능하다.&lt;/p&gt;

&lt;center&gt;&lt;br&gt;&lt;/center&gt;

&lt;h2&gt;
  
  
  번들링
&lt;/h2&gt;

&lt;p&gt;실제로 여기까지 시도해봤다면 서버에서 JSX를 렌더링하지 못한다는 것을 알 수 있다.&lt;/p&gt;

&lt;p&gt;하지만 이는 클라이언트도 마찬가지다. 클라이언트에서 JSX를 '사용'할 수 있는 것처럼 보이는 것은 사실 번들러가 Babel을 이용해 JSX를 트랜스파일하기 때문이다.&lt;/p&gt;

&lt;p&gt;그렇다면 서버의 코드도 마찬가지로 번들러와 Babel을 통해 Node.js가 인식할 수 있는 코드로 번들링되어야 한다.&lt;/p&gt;

&lt;p&gt;이 때, 서버의 웹팩은 &lt;code&gt;target: 'node'&lt;/code&gt;이 되어있어야 하며, isomorphic한 리액트앱을 위해 JSX에 관여하는 Babel 설정은 서버와 클라이언트가 동일해야 한다. [&lt;a href="https://webpack.js.org/concepts/targets/#multiple-targets" rel="noopener noreferrer"&gt;참고&lt;/a&gt;]&lt;/p&gt;

&lt;center&gt;
![렌더링 서버와 라우터](https://dev-to-uploads.s3.amazonaws.com/i/jd6q1yn9ltvsofgtgvog.png)
\&amp;lt;렌더링 서버와 라우터\&amp;gt;
&lt;br&gt;
&lt;/center&gt;

&lt;p&gt;위와 같이 렌더링 서버 코드는 클라이언트 코드와 함께 번들링된다. 그 후 브라우저에서 페이지요청을 보내면 렌더 서버에서 &lt;code&gt;StaticRouter&lt;/code&gt;를 통해 해당 path에 대한 앱(HTML)을 렌더링한 후 응답한다.&lt;/p&gt;

&lt;p&gt;브라우저는 렌더링된 HTML을 받은 후, &lt;code&gt;bundle.js&lt;/code&gt;를 Public에서 받아 hydration을 진행한다. 그리고 앱의 location을 바꿀 때 &lt;code&gt;bundle.js&lt;/code&gt;의 &lt;code&gt;BrowserRouter&lt;/code&gt;을 통해 UI를 업데이트한다.&lt;/p&gt;

</description>
      <category>react</category>
      <category>ssr</category>
    </item>
    <item>
      <title>React SSR 아키텍처 - Redux Integration</title>
      <dc:creator>Ray Kim</dc:creator>
      <pubDate>Tue, 29 Dec 2020 22:36:01 +0000</pubDate>
      <link>https://dev.to/yomandawg/react-ssr-3-redux-integration-3jk2</link>
      <guid>https://dev.to/yomandawg/react-ssr-3-redux-integration-3jk2</guid>
      <description>&lt;p&gt;리액트에서 공식적으로 지원하는 &lt;code&gt;ReactDOM.hydrate&lt;/code&gt; 및 &lt;code&gt;ReactDOMServer.renderToString&lt;/code&gt;을 통해 성공적으로 SSR된 리액트앱을 사용자에게 전달할 수 있었다. 하지만 이 방식으론 동적페이지가 아닌 상태가 존재하지 않는 간단한 페이지 밖에 렌더링하지 못한다.&lt;/p&gt;

&lt;p&gt;상태관리 라이브러리인 Redux를 단순히 리액트 앱에 주입하면 될 것 같지만 SSR에선 &lt;code&gt;store&lt;/code&gt;도 결국 서버에서 만들어야 한다.&lt;/p&gt;


&lt;center&gt;&lt;br&gt;&lt;/center&gt;
&lt;h2&gt;
  
  
  preloadedState
&lt;/h2&gt;

&lt;p&gt;서버에서 아무런 대응없이 상태를 주입한다면 클라이언트에서 새로운 요청을 할 때마다 새로운 상태를 만들 수밖에 없다.&lt;/p&gt;

&lt;p&gt;이 말은 즉, 클라이언트에서 Redux 상태를 지지고 볶아도 새로운 요청을 보내면 페이지 상태가 초기화된다는 말이다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// server code&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;renderer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&gt;/* Express Request */&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// 매 요청마다 새로운 `store`이 만들어진다&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&gt;/* reducers, preloadedState, enhancers */&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;renderToString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Provider&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Provider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;html&amp;gt;
      &amp;lt;body&amp;gt;
        &amp;lt;div id="app"&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/div&amp;gt;
        &amp;lt;script src="bundle.js"&amp;gt;&amp;lt;/script&amp;gt;
      &amp;lt;/body&amp;gt;
    &amp;lt;/html&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;문제의 해결 방법은 꽤나 직관적이다 - 서버에서 초기상태(&lt;code&gt;preloadedState&lt;/code&gt;)를 관리하여 &lt;code&gt;store&lt;/code&gt;을 만들어주면 된다.&lt;/p&gt;

&lt;p&gt;이렇게 하면 &lt;code&gt;store&lt;/code&gt;을 기반으로 리액트앱이 빌드되고, 위처럼 &lt;code&gt;content&lt;/code&gt;를 통해 HTML에 주입된다.&lt;/p&gt;

&lt;p&gt;하지만 역시나 문제가 생긴다. 바로 이 &lt;code&gt;preloadedState&lt;/code&gt;가 클라이언트에게 없다는 것이다. &lt;code&gt;preloadedState&lt;/code&gt;를 이용해 리액트앱을 서버에서 빌드하여 클라이언트에게 보내는 것까진 괜찮지만, 정작 클라이언트는 '상태'는 받고있지 않다.&lt;/p&gt;

&lt;p&gt;클라이언트에 &lt;code&gt;preloadedState&lt;/code&gt;가 없다면 서버와 클라이언트의 상태가 다르다는 것을 뜻하며, 상태가 다르니 만들어지는 리액트앱도 다르다. 즉, hydration 과정에 문제가 생긴다.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://redux.js.org/recipes/server-rendering#inject-initial-component-html-and-state"&gt;Redux 공식문서&lt;/a&gt;에서는 이 문제를 해결하기 위해 &lt;code&gt;preloadedState&lt;/code&gt;를 &lt;code&gt;JSON.stringify&lt;/code&gt;화 시켜 &lt;code&gt;window&lt;/code&gt; 오브젝트에 주입하는 방법을 알려주고 있다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`
  &amp;lt;html&amp;gt;
    &amp;lt;body&amp;gt;
      &amp;lt;div id="app"&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;/div&amp;gt;
      &amp;lt;script&amp;gt;
        window.__PRELOADED_STATE__ = &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;preloadedState&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="sr"&gt;/&amp;lt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;u003c&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;
      &amp;lt;/script&amp;gt;
      &amp;lt;script src="bundle.js"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;/body&amp;gt;
  &amp;lt;/html&amp;gt;
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;replace(/&amp;lt;/g&amp;gt;, '\\u003c')&lt;/code&gt;는 serialization을 위한 것&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;위와 같이 &lt;code&gt;preloadedState&lt;/code&gt;(&lt;code&gt;window.__PRELOADED_STATE__&lt;/code&gt;)를 HTML에 주입시킬 경우 클라이언트에서도 이를 이용한 &lt;code&gt;store&lt;/code&gt;을 만들고 관리할 수 있다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="cm"&gt;/* reducers */&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;__PRELOADED_STATE__&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// HTML에 주입된 preloadedState 이용&lt;/span&gt;
  &lt;span class="cm"&gt;/* enhancers */&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;ReactDOM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hydrate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Provider&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Provider&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;,
&lt;/span&gt;  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;center&gt;&lt;br&gt;&lt;/center&gt;
&lt;h2&gt;
  
  
  Dynamic Configuration
&lt;/h2&gt;

&lt;p&gt;서버에선 원하는 기본값 및 설정을 얼마든지 활용해 &lt;code&gt;preloadedState&lt;/code&gt;를 만들 수 있으며, 이는 클라이언트 요청에 따라 변하는 동적페이지를 만들 수 있는 기반이 된다.&lt;/p&gt;

&lt;p&gt;하지만 아직은 기본값(static configuration)으로 &lt;code&gt;store&lt;/code&gt;을 만들고 있으며, 사용자는 요청과 무관하게 매번 새로운 상태를 받고 있다.&lt;/p&gt;

&lt;p&gt;이를 해결하기 위해 활용할 수 있는 클라이언트의 HTTP &lt;code&gt;request&lt;/code&gt;에는 &lt;code&gt;params&lt;/code&gt;, &lt;code&gt;cookies&lt;/code&gt;, &lt;code&gt;body&lt;/code&gt; 등의 유의미한 정보가 담겨 있으며, 이를 토대로 사용자에 맞는 동적인 &lt;code&gt;store&lt;/code&gt;을 만들 수 있다.&lt;/p&gt;


&lt;center&gt;
&lt;br&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--js5fKjUB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/m5yonzpx3yn1hmlnvrgs.png" alt="SSR Redux Store"&gt;&lt;br&gt;
&amp;lt;SSR Store - Static vs. Dynamic&amp;gt;&lt;br&gt;
&lt;br&gt;&lt;br&gt;
&lt;/center&gt;

&lt;p&gt;위 다이어그램을 보자. Express 서버에서 &lt;code&gt;request&lt;/code&gt;를 활용하여 동적인 &lt;code&gt;preloadedState&lt;/code&gt;(dynamic configuration)를 만들고, 이를 토대로 &lt;code&gt;store&lt;/code&gt;을 만든다. 이를 이용해 리액트앱을 빌드하여 &lt;code&gt;preloadedState&lt;/code&gt;(json)과 함께 HTML에 주입시켜 &lt;code&gt;response&lt;/code&gt;로 보낸다.&lt;/p&gt;

&lt;p&gt;이렇게 사용자 정보를 토대로 &lt;code&gt;store&lt;/code&gt;을 만들 경우 서버는 클라이언트의 활동을 &lt;em&gt;감지&lt;/em&gt;하여 리액트앱을 빌드할 수 있는 효과를 얻을 수 있으며, 사용자는 seemless한 UX를 경험할 수 있다.&lt;/p&gt;


&lt;center&gt;&lt;br&gt;&lt;/center&gt;
&lt;h2&gt;
  
  
  Async Configuration
&lt;/h2&gt;

&lt;p&gt;해결해야할 문제가 아직 더 남았다. 동적으로 상태를 만드는 것까지는 좋았지만, 비동기 처리는 어떻게 해야할까?&lt;/p&gt;

&lt;p&gt;리액트 SSR에서 &lt;code&gt;fetch&lt;/code&gt;와 같은 비동기 처리는 생각보다 복잡한 일이다.&lt;/p&gt;

&lt;p&gt;이는 &lt;code&gt;ReactDOMServer.renderToString&lt;/code&gt;의 동작 방식 때문인데,&lt;/p&gt;



&lt;center&gt;
&lt;br&gt;
&lt;br&gt;&lt;br&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--338NXLzQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/yowc1tyzw9yt0t7d5856.png" alt="Async Configuration"&gt;&lt;br&gt;&lt;br&gt;
&amp;lt;Async Configuration&amp;gt;&lt;br&gt;&lt;br&gt;
&lt;br&gt;&lt;br&gt;&lt;br&gt;
&lt;/center&gt;



&lt;center&gt;
&lt;br&gt;
&lt;br&gt;&lt;br&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--n9iPvoY7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/8mnfrn4js08ldvc8efem.png" alt="Multiple Components"&gt;&lt;br&gt;&lt;br&gt;
&amp;lt;Handling SSR state with multiple components&amp;gt;&lt;br&gt;&lt;br&gt;
&lt;br&gt;&lt;br&gt;&lt;br&gt;
&lt;/center&gt;



&lt;center&gt;
&lt;br&gt;
&lt;br&gt;&lt;br&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--U2g6DOSd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/0mqv4c0lgfgu3aqzfvi7.png" alt="Redux SSR"&gt;&lt;br&gt;&lt;br&gt;
&amp;lt;Redux SSR&amp;gt;&lt;br&gt;&lt;br&gt;
&lt;br&gt;&lt;br&gt;&lt;br&gt;
&lt;/center&gt;

</description>
      <category>react</category>
      <category>ssr</category>
      <category>frontend</category>
      <category>redux</category>
    </item>
    <item>
      <title>React SSR 아키텍처 -  Render Flow</title>
      <dc:creator>Ray Kim</dc:creator>
      <pubDate>Fri, 18 Dec 2020 13:58:50 +0000</pubDate>
      <link>https://dev.to/yomandawg/react-ssr-779</link>
      <guid>https://dev.to/yomandawg/react-ssr-779</guid>
      <description>&lt;p&gt;React SSR은 대체로 Next.js 프레임웍이 담당하는 추세이다. 하지만 좀 더 라이트하게 SSR을 하고 싶을 땐 어떻게 할까?&lt;/p&gt;

&lt;p&gt;Next.js는 거대 프로젝트인 만큼 모두의 니즈에 맞출 수는 없으며, 대체로 엔터프라이즈용 기능이 out-of-the-box 탑재되어있다.&lt;/p&gt;

&lt;p&gt;소프트웨어의 복잡도와 성능(컴퓨팅 속도의)은 상충관계이므로 가벼운 용도의 SSR 서버를 직접 만들어 보는 것도 좋은 접근일 것이다.&lt;/p&gt;

&lt;p&gt;그렇다면 React SSR은 어떻게 설계해야 할까? 먼저 React의 작동 원리부터 생각해보자.&lt;/p&gt;

&lt;center&gt;&lt;br&gt;&lt;/center&gt;

&lt;h2&gt;
  
  
  React Virtual DOM
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://reactjs.org/docs/faq-internals.html" rel="noopener noreferrer"&gt;Virtual DOM&lt;/a&gt;은 의미 그대로 가상으로 DOM을 관리하겠다는 말이다. 리액트 앱은 Virtual DOM을 메모리에서 관리하면서 상태 변화(diff)를 감지하여 브라우저 UI에 반영시킨다.&lt;/p&gt;

&lt;p&gt;그런데 이 가상 DOM이 메모리에서 관리된다면, 브라우저가 아닌 서버 메모리에서 생성해도 될 것이다.&lt;/p&gt;

&lt;p&gt;즉, 요청으로 받은 상태를 기반으로 서버 메모리에서 가상 DOM을 만들고, 이를 기반으로 한 HTML를 응답으로 보낸다면, 사용자는 SSR된 리액트 앱을 사용할 수 있다.&lt;/p&gt;

&lt;p&gt;이것이 React SSR의 기본 개념이다.&lt;/p&gt;

&lt;p&gt;사실 이 방식은 흔히 쓰이는 템플릿엔진을 이용한 SSR과 같고, 리액트를 템플릿으로 하여 만들어진 DOM을 HTML에 주입시키는 것으로 보면 된다.&lt;/p&gt;

&lt;center&gt;
![SSR](https://dev-to-uploads.s3.amazonaws.com/i/wpku7oq5kcb4psmft2cp.png)
\&amp;lt;리액트를 뷰템플릿으로 한 SSR\&amp;gt;
&lt;br&gt;
&lt;/center&gt;

&lt;p&gt;이 과정을 그럼 브라우저와 서버의 통신 과정에서 정리해보자.&lt;/p&gt;

&lt;p&gt;먼저, 브라우저가 서버에 요청을 보내면, 서버는 브라우저가 제공하는 정보(헤더, 상태 등)을 기반으로 가상DOM을 만든다.&lt;/p&gt;

&lt;p&gt;이 DOM은 다음과 같이 서버에서 렌더된 후 그대로 HTML 템플릿에 주입되어 보내진다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Express.js 서버에서 React SSR을 만드는 과정&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Hello World!&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;renderToString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 가상 DOM을 렌더링 후 string 반환&lt;/span&gt;

&lt;span class="c1"&gt;// 렌더링이 완료된 리액트 요소를 템플릿에 주입&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`
  &amp;lt;html&amp;gt;
    &amp;lt;body&amp;gt;
      &amp;lt;div id="app"&amp;gt;
        &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;script src="bundle.js"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;/body&amp;gt;
  &amp;lt;/html&amp;gt;
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Express.js response 사용을 가정한다&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;리액트는 SSR을 염두해 &lt;a href="https://reactjs.org/docs/react-dom-server.html#rendertostring" rel="noopener noreferrer"&gt;&lt;code&gt;ReactDOMServer.renderToString&lt;/code&gt;&lt;/a&gt; 등의 메서드를 제공하며, 이는 렌더링된 HTML string을 반환한다. (클라이언트의 &lt;code&gt;ReactDOM.render&lt;/code&gt;을 대체한다)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;그 후 실제 브라우저가 받는 응답은 다음과 같다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"app"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Hello World!&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"bundle.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;보다시피 리액트가 성공적으로 렌더링 된 것을 볼 수 있다!&lt;/p&gt;

&lt;p&gt;그런데 &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; 번들은 어떻게 만들까?&lt;/p&gt;

&lt;p&gt;아무리 서버에서 렌더링이 완료된 HTML를 가져왔어도, interactive한 UI를 사용하려면 당연히 JavaScript가 필요하다.&lt;/p&gt;

&lt;p&gt;필수 패키지들을 효율적으로 번들링하여 가져올 수는 있지만, 이 번들에 리액트를 어떻게 포함시켜야 하는지가 관건이다.&lt;/p&gt;

&lt;p&gt;즉, 리액트가 제대로 작동하기 위해 리액트 의존성을 번들링하는 것은 문제가 없지만 리액트 컴포넌트를 어떻게 관리할 것이냐에 대한 고민이 생긴다.&lt;/p&gt;

&lt;center&gt;&lt;br&gt;&lt;/center&gt;

&lt;h2&gt;
  
  
  Isomorphic App
&lt;/h2&gt;

&lt;p&gt;React SSR을 개발할 때 isomorphic 구조로 컴포넌트를 관리하는 것은 필수적이다.&lt;/p&gt;

&lt;p&gt;Isomorohic의 사전적 의미는 '동일'이며 isomorphic 리액트 앱이라 함은 서버와 클라이언트의 컴포넌트 구조를 동일하게 관리하는 형태를 일컫는다.&lt;/p&gt;

&lt;p&gt;이렇게 동일 구조를 유지하면 클라이언트에서 &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; 번들의 리액트앱을 렌더링 할 경우 &lt;code&gt;ReactDOM&lt;/code&gt;은 이미 Paint가 완료된 SSR의 HTML과 번들의 가상DOM을 비교하여 리액트JS를 바인딩(또는 &lt;a href="https://reactjs.org/docs/react-dom.html#hydrate" rel="noopener noreferrer"&gt;&lt;em&gt;hydration&lt;/em&gt;&lt;/a&gt;)한다.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;hydration 용도로 클라이언트는 &lt;code&gt;ReactDOM.render&lt;/code&gt; 대신 &lt;code&gt;ReactDOM.hydrate&lt;/code&gt;를 사용한다.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;이 때, isomorphic하게 앱을 관리하지 않는다면 리액트는 진상을 부릴 것이고, 우리의 뜻대로 리액트가 바인딩되지 않을 것이다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 클라이언트의 리액트&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// handler와 같은 JS 요소들이 hydration을 통해 corresponding component에 바인딩된다.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hydration success!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Misplaced Component&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Click Me!&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;ReactDOM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hydrate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 서버의 리액트&lt;/span&gt;

&lt;span class="c1"&gt;// 클라이언트와 구조가 다르다&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Misplaced Component&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Click Me!&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;renderToString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;위의 예시와 같이 클라이언트와 서버의 리액트 구조가 다르면 경우 컴포넌트(Virtual DOM 포함)가 다시 생성되거나 구조를 멋대로 해석해 기능이 제대로 작동하지 않는다.&lt;/p&gt;

&lt;p&gt;미스매치에 대한 &lt;code&gt;ReactDOM.hydrate&lt;/code&gt;의 대응은 safeguard로써 존재하지만, 이런 버그를 그냥 놔둔다면 퍼포먼스는 더 악화되며 SSR을 사용하는 의미가 없다.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;SSR의 목표는 JS요청을 기다리기 전에 빠르게 HTML을 Paint하겠다는 것이다.&lt;br&gt;
응답받은 HTML이 다시 렌더링 된다면 의미가 퇴색된다.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;center&gt;
![Render Flow](https://dev-to-uploads.s3.amazonaws.com/i/qag2zxilrux02srysldz.png)
\&amp;lt;기본적인 Render Flow\&amp;gt;
&lt;br&gt;
&lt;/center&gt;

&lt;p&gt;위 그림에서 브라우저는 첫 번째 요청↔응답에서 바로 마크업을 받아볼 수 있다.&lt;/p&gt;

&lt;p&gt;그 다음 요청인 &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; 번들(bundle.js)은 대체로 HTML보다 사이즈가 훨씬 크다. 느린 네트워크 환경에선 먼저 렌더링된 UI부터 보는 것이 사용자경험에 이로울 것이다.&lt;/p&gt;

&lt;p&gt;최종적으로 서버는 알맞은 번들을 보내고, 클라이언트는 React app을 바인딩하여 interactive한 기능을 사용할 수 있다.&lt;/p&gt;

</description>
      <category>react</category>
      <category>ssr</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Nest.js에서 Pipe의 필요성</title>
      <dc:creator>Ray Kim</dc:creator>
      <pubDate>Thu, 17 Dec 2020 22:09:26 +0000</pubDate>
      <link>https://dev.to/yomandawg/nest-js-pipe-2j12</link>
      <guid>https://dev.to/yomandawg/nest-js-pipe-2j12</guid>
      <description>&lt;p&gt;Nest.js의 Pipe는 굳이 비교하자면 Spring의 Converter 또는 Formatter의 그것과 같다.&lt;/p&gt;

&lt;p&gt;하지만 &lt;em&gt;Middleware&lt;/em&gt;나 &lt;em&gt;helper&lt;/em&gt;가 있는데 왜 &lt;em&gt;Pipe&lt;/em&gt;라는 개념까지 도입한 것일까라는 의문이 생긴다.&lt;/p&gt;

&lt;p&gt;공식문서의 개념에서 &lt;a href="https://docs.nestjs.com/pipes"&gt;Pipe&lt;/a&gt;는 &lt;code&gt;@injectable&lt;/code&gt; decorator을 사용하는 JS class 라고 설명하고 있다. 즉, Provider과 기본 개념이 동일하다.&lt;/p&gt;

&lt;p&gt;그렇다면 먼저 &lt;code&gt;injectable&lt;/code&gt;이 무엇인지 알아보자.&lt;/p&gt;


&lt;center&gt;&lt;br&gt;&lt;/center&gt;
&lt;h2&gt;
  
  
  Injectables
&lt;/h2&gt;

&lt;p&gt;A모 프레임웍에서 에서 파생된 &lt;code&gt;injectable&lt;/code&gt;은 간단히 말해 &lt;a href="https://en.wikipedia.org/wiki/Dependency_injection"&gt;Dependency Injection&lt;/a&gt; 패턴을 적용시키겠다는 말이다.&lt;/p&gt;

&lt;p&gt;Nest는 Provider 패턴으로 쉽게 dependency를 관리 및 공유할 수 있는데,&lt;br&gt;
한 번의 주입으로 만들어진 의존성 인스턴스를 하위 모듈에서 쉽게 접근할 수 있다는 장점이 있다.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;React Context Provider ∝ Nest Module Provider&lt;br&gt;
하위 객체에게 의존성을 주입하는 것은 비슷하다.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;이는 인스턴스 한 개만 관리하기 때문에 디버깅을 간단하게 해줄뿐만 아니라 메모리 누수를 막을 수 있다는 매우 큰 이점을 가져다 준다.&lt;/p&gt;

&lt;p&gt;다음과 같은 예를 보자.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;ExampleService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;memoryLeak&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;ArrayBuffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// ExampleService 인스턴스가 생성될 때마다 10000byte 메모리 누수를 일으킨다.&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ExampleService&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;ExampleModule&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="c1"&gt;// 이를 `Injectable`화 하여 주입하면 Nest는 인스턴스를 재사용하게 된다.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;이같이 한 번의 Provider 주입만으로 &lt;code&gt;injectable&lt;/code&gt;을 하위 모듈간 쉽게 위임하고 재활용할 수 있다.&lt;/p&gt;


&lt;center&gt;
&lt;br&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WZhphdIt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/n6h5pdmubkfvp5noxmug.png" alt="Dependency Injection"&gt;&lt;br&gt;
&amp;lt;Dependency Injection&amp;gt;&lt;br&gt;
&lt;br&gt;&lt;br&gt;
&lt;/center&gt;

&lt;p&gt;명시적 &lt;code&gt;injectable&lt;/code&gt;을 통한 DI패턴은 유용하지만, 여기까지만 보면 그냥 필요에 따라 Provider에 헬퍼나 팩토리 따위를 담아 사용하는 방법이 떠오른다.&lt;/p&gt;

&lt;p&gt;그렇다면 다시 처음으로 돌아가 Pipe는 타 헬퍼과 다른 점이 무엇일까?&lt;/p&gt;


&lt;center&gt;&lt;br&gt;&lt;/center&gt;
&lt;h2&gt;
  
  
  Pipe vs. the World
&lt;/h2&gt;

&lt;p&gt;공식문서를 인용하자면 Pipe는 컨트롤러 handler의 매개변수 전처리를 도와주는 특수한 helper라고 정의할 수 있다. 다시 말해 input으로 들어오는 데이터를 지정된 파이프를 거쳐 들어오게끔 하는 것이다.&lt;/p&gt;

&lt;p&gt;이는 Middleware의 역할이 아닌가라고 볼 수 있지만 Middleware은 Request에 &lt;em&gt;항상&lt;/em&gt; 관여한다면, Pipe는 특정 route handler에 붙어 &lt;em&gt;필요한&lt;/em&gt; 데이터값만 선별해 다룬다는 점에 차이가 있다.&lt;/p&gt;

&lt;p&gt;이렇게 Pipe를 거친 input을 handler에 전달할 경우 각 단일책임원칙을 위배하지 않음과 동시에 알맞게 정제된 반환값을 받을 수 있다.&lt;/p&gt;

&lt;p&gt;또한, Pipe 역시 재사용 가능한 &lt;code&gt;injectable&lt;/code&gt;이므로 어느 라우터에 갖다 붙여도 된다는 장점이 있다.&lt;/p&gt;


&lt;center&gt;
&lt;br&gt;
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Amf-FYR6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/mpskj7cxj195cchgx9x0.png" alt="Request flow"&gt;&lt;br&gt;
&amp;lt;Request flow - Pipe는 모두 재사용 가능하다&amp;gt;&lt;br&gt;
&lt;br&gt;&lt;br&gt;
&lt;/center&gt;

&lt;blockquote&gt;
&lt;p&gt;Pipe 또한 당연하게도 exception filter의 혜택을 누릴 수 있다. 특정 매개변수의 에러를 이곳에서 처리하자.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;center&gt;&lt;br&gt;&lt;/center&gt;
&lt;h2&gt;
  
  
  Pipe use-case
&lt;/h2&gt;

&lt;p&gt;Pipe 역할은 이제 알디시피 '맞춤형' 필터라고 볼 수 있으며 평소 middleware이 하는 역할들을 pipe에게도 맡길 수 있을 것이다.&lt;/p&gt;

&lt;p&gt;태생이 이렇다 보니, Nest 프레임웍은 자주 쓰이는 &lt;a href="https://docs.nestjs.com/pipes#built-in-pipes"&gt;기본 Pipe&lt;/a&gt;들을 제공한다.&lt;/p&gt;

&lt;p&gt;실전으로 들어가 REST API에서 &lt;code&gt;/person/3&lt;/code&gt; endpoint에 요청을 받았다고 생각해보자.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Controller&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;person&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;PersonController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;getPerson&lt;/span&gt;&lt;span class="p"&gt;(@&lt;/span&gt;&lt;span class="nd"&gt;Param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// return a matching person&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;이 때, &lt;code&gt;id&lt;/code&gt;가 항상 &lt;code&gt;number&lt;/code&gt;로만 들어오면 괜찮지만 만약 &lt;code&gt;3a&lt;/code&gt;가 들어올 경우 Pipe를 제공해 변환해주어야 한다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ParseIntPipe에 변환 과정을 위임한다.&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;:id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;getPerson&lt;/span&gt;&lt;span class="p"&gt;(@&lt;/span&gt;&lt;span class="nd"&gt;Param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ParseIntPipe&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// return a matching person&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;이처럼 Pipe를 사용해 아주 간단히 &lt;code&gt;getPerson&lt;/code&gt; handler의 관심사 분리를 할 수 있다.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Pipe용으로 사용할 수 있는 매개변수는 &lt;code&gt;@Param&lt;/code&gt;, &lt;code&gt;@Body&lt;/code&gt;, &lt;code&gt;@Query&lt;/code&gt;가 있다. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;위 예시는 기본적인 Pipe 사용 방법을 제시했지만, 유효성 검증 및 입맛에 따라 커스텀 Pipe도 얼마든지 만들 수 있다.&lt;/p&gt;

</description>
      <category>nest</category>
      <category>pipe</category>
      <category>node</category>
    </item>
  </channel>
</rss>
