<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>beomy</title>
    <link>https://beomy.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Sat, 20 Jun 2026 08:58:37 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>버미노트</managingEditor>
    <item>
      <title>[Svelte] 스벨트 입문 A부터 Z까지</title>
      <link>https://beomy.tistory.com/94</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;519&quot; data-origin-height=&quot;139&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rCoa6/btqGdSa10fb/sM2rPI6hzwgTg1fUK2nKs1/tfile.svg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rCoa6/btqGdSa10fb/sM2rPI6hzwgTg1fUK2nKs1/tfile.svg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rCoa6/btqGdSa10fb/sM2rPI6hzwgTg1fUK2nKs1/tfile.svg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrCoa6%2FbtqGdSa10fb%2FsM2rPI6hzwgTg1fUK2nKs1%2Ftfile.svg&quot; width=&quot;100%&quot; data-origin-width=&quot;519&quot; data-origin-height=&quot;139&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;h1&gt;스벨트 입문 강의를 출시했습니다.&lt;/h1&gt;
&lt;p&gt;블로그의 내용과 공식 문서를 토대로 인프런에 &lt;a href=&quot;https://www.inflearn.com/course/스벨트-입문?inst=77d01d70&quot;&gt;스벨트 입문 A부터 Z까지&lt;/a&gt; 강의를 출시했습니다. 공식 문서의 내용을 모두 담아 다른 강의나 다른 블로그를 찾아볼 필요 없이 이 강의 하나면 한 번에 해결하실 수 있습니다.&lt;/p&gt;
&lt;div style=&quot;padding-top: 56.25%; position: relative;&quot;&gt;&lt;iframe src=&quot;https://www.youtube.com/embed/20ihgYRe1fE?showinfo=0&quot; width=&quot;300&quot; height=&quot;225&quot; frameborder=&quot;&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;
&lt;h1&gt;왜 스벨트를 배워야 할까요?&lt;/h1&gt;
&lt;p&gt;스벨트는 2019년에 등장해 무럭무럭 자라나고 있는 새로운 프론트엔트 프레임워크입니다. 가상돔을 사용하지 않아서 React, Vue와 다른 페러다임을 제시한 프레임워크입니다. 스벨트를 배워두면 좋은 이유를 이야기해 볼께요.&lt;/p&gt;
&lt;h2&gt;2019년 관심도 1위!&lt;/h2&gt;
&lt;p&gt;스벨트는 리엑트, 뷰, 앵귤러 프론트엔트 3대장 자리를 위협하며 2019년 관심도 1위를 기록했습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;1913&quot; data-origin-height=&quot;475&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUzt2D/btqGnZmAsA8/U9eKv4M6ifYd9D2VJ9UnJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUzt2D/btqGnZmAsA8/U9eKv4M6ifYd9D2VJ9UnJk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUzt2D/btqGnZmAsA8/U9eKv4M6ifYd9D2VJ9UnJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUzt2D%2FbtqGnZmAsA8%2FU9eKv4M6ifYd9D2VJ9UnJk%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;1913&quot; data-origin-height=&quot;475&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;h2&gt;장점 하나: 낮은 러닝 커브, 코드량 감소&lt;/h2&gt;
&lt;p&gt;React를 처음 접할 때 헉! 하는 부분은 큰 러닝 커브라고 생각됩니다. 스벨트는 러닝 커브가 높지 않아서 입문자분들도 쉽게 코드를 읽어 내려가실 수 있습니다. &lt;i&gt;낮은 러닝 커브는 스벨트가 프론트엔트에서 차지하는 파이를 빠르게 높일 수 있는 장점이 아닐까 싶습니다.&lt;/i&gt;&lt;/p&gt;
&lt;h2&gt;장점 둘: 작은 크기의 번들&lt;/h2&gt;
&lt;p&gt;스벨트는 화면이 변경되었을 때, 변경되는 부분을 정확히 알 수 있어서 가상돔을 사용하지 않습니다. 변경된 부분을 찾기 위한 오버헤드도 발생하지 않고, 런타임도 없어서 번들 크기가 작아 렌더링 속도를 높일 수 있습니다.&lt;/p&gt;
&lt;h2&gt;장점 셋: 스벨트의 빠른 속도&lt;/h2&gt;
&lt;p&gt;번들의 크기가 작고, 코드량이 줄고 이런저런 장점들이 있지만 제가 생각하는 가장 눈에 띄는 장점은 &lt;b&gt;속도&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;802&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Cgbl9/btqGnZNF0v1/Y7s3gkv6wirHhDdELLaZd1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Cgbl9/btqGnZNF0v1/Y7s3gkv6wirHhDdELLaZd1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Cgbl9/btqGnZNF0v1/Y7s3gkv6wirHhDdELLaZd1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCgbl9%2FbtqGnZNF0v1%2FY7s3gkv6wirHhDdELLaZd1%2Fimg.png&quot; width=&quot;100%&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;802&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;스벨트는 리엑트, 뷰, 앵귤러보다도 빠릅니다. 심지어 속도가 개선됐다고 하는 뷰 3.0보다 더 빠르다고 합니다.&lt;/p&gt;
&lt;h1&gt;미리미리 배워두어요&lt;/h1&gt;
&lt;p&gt;이런 추세라면 스벨트가 리엑트, 뷰, 앵귤러 만큼 많은 곳에서 찾는 프레임워크로 성장하는 것은 시간문제로 보입니다. 더 유명해 지기 전에 스벨트 입문강의로 스벨트를 공부해봅시다!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;600&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wuRdN/btqGqbmmDK0/KLJIFd6Euq7294ROfSvX90/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wuRdN/btqGqbmmDK0/KLJIFd6Euq7294ROfSvX90/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wuRdN/btqGqbmmDK0/KLJIFd6Euq7294ROfSvX90/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwuRdN%2FbtqGqbmmDK0%2FKLJIFd6Euq7294ROfSvX90%2Fimg.jpg&quot; width=&quot;100%&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;600&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;h1&gt;부록: 스벨트를 포스팅 했습니다.&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://beomy.github.io/tech/svelte/&quot;&gt;beomy.github.io&lt;/a&gt;에 스벨트에 관한 내용을 포스팅했습니다. 공식 문서를 토대로 스벨트 사용방법을 이야기하였습니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://beomy.github.io/tech/svelte/introduction-svelte/&quot;&gt;[Svelte] Svelte 소개&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://beomy.github.io/tech/svelte/start-svelte/&quot;&gt;[Svelte] Svelte 시작하기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://beomy.github.io/tech/svelte/basic-svelte/&quot;&gt;[Svelte] Svelte 기초 문법&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://beomy.github.io/tech/svelte/reactivity-syntax/&quot;&gt;[Svelte] 반응형을 위한 문법&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://beomy.github.io/tech/svelte/props/&quot;&gt;[Svelte] Props&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://beomy.github.io/tech/svelte/logic-blocks/&quot;&gt;[Svelte] 논리 블록&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://beomy.github.io/tech/svelte/events/&quot;&gt;[Svelte] 이벤트 다루기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://beomy.github.io/tech/svelte/bindings-basic/&quot;&gt;[Svelte] 데이터 바인딩 기초&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://beomy.github.io/tech/svelte/bindings-in-depth/&quot;&gt;[Svelte] 데이터 바인딩 고급&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://beomy.github.io/tech/svelte/lifecycle/&quot;&gt;[Svelte] 라이프 사이클&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://beomy.github.io/tech/svelte/store/&quot;&gt;[Svelte] Store&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://beomy.github.io/tech/svelte/motion/&quot;&gt;[Svelte] Motion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://beomy.github.io/tech/svelte/transition/&quot;&gt;[Svelte] 트랜지션&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://beomy.github.io/tech/svelte/animation/&quot;&gt;[Svelte] 애니메이션&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://beomy.github.io/tech/svelte/action/&quot;&gt;[Svelte] 액션&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://beomy.github.io/tech/svelte/slot/&quot;&gt;[Svelte] Slot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://beomy.github.io/tech/svelte/context-api/&quot;&gt;[Svelte] Context API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://beomy.github.io/tech/svelte/svelte-elements/&quot;&gt;[Svelte] Svelte 요소&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://beomy.github.io/tech/svelte/module-context/&quot;&gt;[Svelte] Module context&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://beomy.github.io/tech/svelte/vue-vs-svelte/&quot;&gt;[Svelte] Vue와 Svelte 비교&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>ETC...</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/94</guid>
      <comments>https://beomy.tistory.com/94#entry94comment</comments>
      <pubDate>Fri, 7 Aug 2020 16:48:13 +0900</pubDate>
    </item>
    <item>
      <title>[Browser] 렌더링 최적화</title>
      <link>https://beomy.tistory.com/93</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Pfr7D/btqAkR8KiEN/yS8TFfVhaSbae94XwzBY0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Pfr7D/btqAkR8KiEN/yS8TFfVhaSbae94XwzBY0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Pfr7D/btqAkR8KiEN/yS8TFfVhaSbae94XwzBY0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPfr7D%2FbtqAkR8KiEN%2FyS8TFfVhaSbae94XwzBY0k%2Fimg.png&quot; width=&quot;100%&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;a href=&quot;https://beomy.github.io/tech/browser/&quot;&gt;beomy.github.io&lt;/a&gt;에 브라우저에 관한 포스팅을 하였습니다.&lt;/p&gt;
&lt;p&gt;주된 내용은 브라우저 구조, 초기 렌더링 최적화, 렌더링 업데이트 최적화에 관련된 내용입니다.&lt;/p&gt;
&lt;h1&gt;1. 자바스크립트 런타임이란?&lt;/h1&gt;
&lt;p&gt;자바스크립트 런타임이란, 간단하게 이야기 하자면 자바스크립트를 실행시키는 환경을 이야기 합니다. 브라우저 역시 자바스크립트를 실행시킬 수 있는 환경이기 때문에 브라우저도 자바스크립트 런타임이라고 이야기 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://beomy.github.io/tech/javascript/javascript-runtime/&quot;&gt;[JavaScript] 자바스크립트 런타임&lt;/a&gt;에서 자바스크립트 런타임이 무엇인지 이야기 합니다.&lt;/p&gt;
&lt;h1&gt;2. 브라우저가 화면을 렌더링 하는 방법&lt;/h1&gt;
&lt;p&gt;브라우저의 구조는 어떻게 되고, 브라우저가 HTML, CSS, JavaScript를 어떻게 화면에 렌더링 하는지 &lt;a href=&quot;https://beomy.github.io/tech/browser/browser-rendering/&quot;&gt;[Browser] 브라우저 렌더링&lt;/a&gt;에서 이야기 합니다.&lt;/p&gt;
&lt;h1&gt;3. 초기 렌더링 최적화 방법&lt;/h1&gt;
&lt;p&gt;Critical Rendering Path란 브라우저가 페이지를 처음 렌더링 할 때, HTML과 CSS, JavaScript가 실행되는 순서를 말합니다.&lt;br&gt;HTML과 CSS, JavaScript가 실행되는 순서를 최적화 한다면 사용자에게 좀 더 빠른 초기 화면을 제공할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://beomy.github.io/tech/browser/critical-rendering-path/&quot;&gt;[Browser] Critical Rendering Path 최적화&lt;/a&gt;에서 초기 렌더링을 최적화 하는 방법에 대해 이야기 합니다.&lt;/p&gt;
&lt;h1&gt;4. 렌더링 업데이트 최적화 방법&lt;/h1&gt;
&lt;p&gt;요즘 웹페이지는 사용자 행동에 따라 변화무쌍하게 화면이 그려집니다. 렌더링을 업데이트 하는 과정인 reflow와 repaint, reflow를 최적화 하는 방법에 대해 &lt;a href=&quot;https://beomy.github.io/tech/browser/reflow-repaint/&quot;&gt;[Browser] Reflow와 Repaint&lt;/a&gt;에서 이야기 합니다.&lt;/p&gt;
&lt;h1&gt;5. 기타: 렌더링 최적화 이해에 도움이 될 수 있는 포스팅&lt;/h1&gt;
&lt;p&gt;1~4번 까지 포스팅을 하면서 가볍게 이야기 한 내용들을 좀더 자세히 이야기하는 포스팅 들입니다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://beomy.github.io/tech/browser/preload-preconnect-prefetch/&quot;&gt;[Browser] 리소스 우선순위 - preload, preconnect, prefetch&lt;/a&gt;에서는 브라우저에게 리소스 우선순위를 알려줘서 빠르게 렌더링 할 수 있는 힌트를 제공하는 방법에 대해 이야기 합니다.&lt;/p&gt;
&lt;p&gt;브라우저는 &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;를 만나게 되면 파싱을 중단하고 스크립트를 다운받고 실행합니다. 파싱이 중단되고 스크립트를 실행하기 때문에 렌더링 속도는 당연히 느려지게 되고 사용성은 떨어집니다. &lt;a href=&quot;https://beomy.github.io/tech/browser/async-defer/&quot;&gt;[Browser] async와 defer&lt;/a&gt;에서 파싱을 중단하지 않고 스크립트를 다운 받을 수 있는 방법에 대해 이야기 합니다.&lt;/p&gt;
&lt;br /&gt;
&lt;br /&gt;

&lt;h4&gt;포스팅들...&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://beomy.github.io/tech/javascript/javascript-runtime/&quot;&gt;[JavaScript] 자바스크립트 런타임&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://beomy.github.io/tech/browser/browser-rendering/&quot;&gt;[Browser] 브라우저 렌더링&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://beomy.github.io/tech/browser/critical-rendering-path/&quot;&gt;[Browser] Critical Rendering Path 최적화&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://beomy.github.io/tech/browser/reflow-repaint/&quot;&gt;[Browser] Reflow와 Repaint&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://beomy.github.io/tech/browser/preload-preconnect-prefetch/&quot;&gt;[Browser] 리소스 우선순위 - preload, preconnect, prefetch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://beomy.github.io/tech/browser/async-defer/&quot;&gt;[Browser] async와 defer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>ETC...</category>
      <category>Async</category>
      <category>Critical Rendering Path</category>
      <category>defer</category>
      <category>preconnect</category>
      <category>prefetch</category>
      <category>preload</category>
      <category>reflow</category>
      <category>repaint</category>
      <category>브라우저 렌더링</category>
      <category>자바스크립트 런타임</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/93</guid>
      <comments>https://beomy.tistory.com/93#entry93comment</comments>
      <pubDate>Mon, 9 Dec 2019 16:22:20 +0900</pubDate>
    </item>
    <item>
      <title>[Vue.JS] Vue.JS 소스코드 분석</title>
      <link>https://beomy.tistory.com/92</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4DHQ1/btqx1y5ndlz/Pp51vLIkSwV94dYhBNvjH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4DHQ1/btqx1y5ndlz/Pp51vLIkSwV94dYhBNvjH0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4DHQ1/btqx1y5ndlz/Pp51vLIkSwV94dYhBNvjH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4DHQ1%2Fbtqx1y5ndlz%2FPp51vLIkSwV94dYhBNvjH0%2Fimg.png&quot; width=&quot;100%&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;a href=&quot;https://beomy.github.io&quot;&gt;github 페이지&lt;/a&gt;에 Vue.JS 소스 코드 분석 포스팅을 올렸습니다.&lt;br&gt;2019년 말에는 &lt;a href=&quot;https://medium.com/the-vue-point/plans-for-the-next-iteration-of-vue-js-777ffea6fabf&quot;&gt;Vue 3.0&lt;/a&gt; 버전을 출시한다고 해, 이 포스트 시리즈는 곧 outdate 될 것 같습니다.. (공들여 작성했는데...)&lt;br /&gt;&lt;br&gt;&lt;a href=&quot;https://beomy.github.io/tech/vuejs/introduction-vue-code-analysis/&quot;&gt;[Inside Vue] 1. Introduction - Vue Code 분석&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;https://beomy.github.io/tech/vuejs/initialize-vue-core-function/&quot;&gt;[Inside Vue] 2. Initialize - Vue 코어 함수&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;https://beomy.github.io/tech/vuejs/initialize-mixin-layer/&quot;&gt;[Inside Vue] 3. Initialize - Mixin Layer&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;https://beomy.github.io/tech/vuejs/initialize-_init-function/&quot;&gt;[Inside Vue] 4. Initialize - init 함수&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;https://beomy.github.io/tech/vuejs/reactive-observer-dep-watcher/&quot;&gt;[Inside Vue] 5. Reactive - Observer, Dep and Watcher&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;https://beomy.github.io/tech/vuejs/reactive-lazy-sync-queue/&quot;&gt;[Inside Vue] 6. Reactive - Watcher가 업데이트 하는 3가지 방법(Lazy, Sync, Queue)&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;https://beomy.github.io/tech/vuejs/view-render-compiler/&quot;&gt;[Inside Vue] 7. View Render - 컴파일러&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;https://beomy.github.io/tech/vuejs/view-render-baseCompile-function/&quot;&gt;[Inside Vue] 8. View Render - baseCompile 함수&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;https://beomy.github.io/tech/vuejs/view-render-patch/&quot;&gt;[Inside Vue] 9. View Render - Patch&lt;/a&gt;&lt;br /&gt;&lt;br&gt;포스팅 내용들이 Vue를 사용하는데 도움이 되길 희망합니다.&lt;/p&gt;</description>
      <category>Vue.JS</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/92</guid>
      <comments>https://beomy.tistory.com/92#entry92comment</comments>
      <pubDate>Thu, 5 Sep 2019 00:17:22 +0900</pubDate>
    </item>
    <item>
      <title>[vuex] Testing</title>
      <link>https://beomy.tistory.com/91</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Vue.png&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/do7hWT/btqwT1UlUFb/uvxLovRsdXuztPNgO03r60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/do7hWT/btqwT1UlUFb/uvxLovRsdXuztPNgO03r60/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/do7hWT/btqwT1UlUFb/uvxLovRsdXuztPNgO03r60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdo7hWT%2FbtqwT1UlUFb%2FuvxLovRsdXuztPNgO03r60%2Fimg.png&quot; data-filename=&quot;Vue.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;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Vuex에서 단위 테스트를 하고자 하는 주된 부분은 mutation과 action입니다.&lt;/p&gt;
&lt;h1&gt;1. Mutation Testing&lt;/h1&gt;
&lt;p&gt;mutation은 전달인자에 완전히 의존하는 함수이기 때문에, mutation을 테스트 하는 것은 매우 간단합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const state = { ... }

// 변이를 이름을 가지는 내보내기를 이용하여 내보냅니다.
export const mutations = { ... }

export default new Vuex.Store({
  state,
  mutations
})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 코드와 같이 mutation과 store를 모두 ES2015의 &lt;code&gt;export&lt;/code&gt;를 사용하여 내보내기 하여 테스트 할 때 사용할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// store.js
export const mutations = {
  increment: state =&amp;gt; state.count++
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// mutations.spec.js
import { expect } from &amp;#39;chai&amp;#39;
import { mutations } from &amp;#39;./store&amp;#39;

// 변이 가져오기
const { increment } = mutations

describe(&amp;#39;mutations&amp;#39;, () =&amp;gt; {
  it(&amp;#39;INCREMENT&amp;#39;, () =&amp;gt; {
    // mock 상태
    const state = { count: 0 }
    // 변이 적용
    increment(state)
    // 결과 확인
    expect(state.count).to.equal(1)
  })
})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 코드는 Mocha + Chai를 사용하여 mutation을 테스팅 하는 예제입니다.&lt;/p&gt;
&lt;h1&gt;2. Action Testing&lt;/h1&gt;
&lt;p&gt;action은 보통 외부 API를 호출하기 때문에, 테스트하기 좀 더 까다로울 수 있습니다. action을 테스트 하기 위해서는 몇가지 조작이 필요합니다. 예를 들어 API 호출을 가상의 서비스로 만들고, mock 데이터(테스트 데이터)로 가상의 서비스를 조작해야 합니다. webpack과 &lt;a href=&quot;https://github.com/plasticine/inject-loader&quot;&gt;inject-loader&lt;/a&gt;를 사용하여 테스트 파일을 묶을 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// actions.js
import shop from &amp;#39;../api/shop&amp;#39;

export const getAllProducts = ({ commit }) =&amp;gt; {
  commit(&amp;#39;REQUEST_PRODUCTS&amp;#39;)
  shop.getProducts(products =&amp;gt; {
    commit(&amp;#39;RECEIVE_PRODUCTS&amp;#39;, products)
  })
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// actions.spec.js

// 인라인 로더에는 require 구문을 사용하십시오.
// inject-loader를 사용하면 조작된 의존성을
// 주입 할 수있는 모듈 팩토리가 반환됩니다.
import { expect } from &amp;#39;chai&amp;#39;
const actionsInjector = require(&amp;#39;inject-loader!./actions&amp;#39;)

// 조작된 모의 응답과 함께 모듈 생성
const actions = actionsInjector({
  &amp;#39;../api/shop&amp;#39;: {
    getProducts (cb) {
      setTimeout(() =&amp;gt; {
        cb([ /* 모의 응답 */ ])
      }, 100)
    }
  }
})

// 예상되는 변이와 함께 테스팅 액션을 도와주는 헬퍼
const testAction = (action, payload, state, expectedMutations, done) =&amp;gt; {
  let count = 0

  // 모의 커밋
  const commit = (type, payload) =&amp;gt; {
    const mutation = expectedMutations[count]

    try {
      expect(type).to.equal(mutation.type)
      if (payload) {
        expect(payload).to.deep.equal(mutation.payload)
      }
    } catch (error) {
      done(error)
    }

    count++
    if (count &amp;gt;= expectedMutations.length) {
      done()
    }
  }

  // 모의 저장소와 전달인자로 액션을 부릅니다.
  action({ commit, state }, payload)

  // 디스패치된 변이가 없는지 확인
  if (expectedMutations.length === 0) {
    expect(count).to.equal(0)
    done()
  }
}

describe(&amp;#39;actions&amp;#39;, () =&amp;gt; {
  it(&amp;#39;getAllProducts&amp;#39;, done =&amp;gt; {
    testAction(actions.getAllProducts, null, {}, [
      { type: &amp;#39;REQUEST_PRODUCTS&amp;#39; },
      { type: &amp;#39;RECEIVE_PRODUCTS&amp;#39;, payload: { /* 모의 응답 */ } }
    ], done)
  })
})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;사용하는 테스트 환경에서 &lt;a href=&quot;https://sinonjs.org/&quot;&gt;Sinon.JS&lt;/a&gt;과 같은 스파이를 사용 할 수 있다면,&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;describe(&amp;#39;actions&amp;#39;, () =&amp;gt; {
  it(&amp;#39;getAllProducts&amp;#39;, () =&amp;gt; {
    const commit = sinon.spy()
    const state = {}

    actions.getAllProducts({ commit, state })

    expect(commit.args).to.deep.equal([
      [&amp;#39;REQUEST_PRODUCTS&amp;#39;],
      [&amp;#39;RECEIVE_PRODUCTS&amp;#39;, { /* 모의 응답 */ }]
    ])
  })
})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 코드와 같이 &lt;code&gt;testAction&lt;/code&gt; 헬퍼 대신에 스파이를 사용하여 코드가 더 간결해 질 수 있습니다.&lt;/p&gt;
&lt;h1&gt;3. Getters Testing&lt;/h1&gt;
&lt;p&gt;getter는 mutation과 같이 테스트하는 것이 매우 간단합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// getters.js
export const getters = {
  filteredProducts (state, { filterCategory }) {
    return state.products.filter(product =&amp;gt; {
      return product.category === filterCategory
    })
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// getters.spec.js
import { expect } from &amp;#39;chai&amp;#39;
import { getters } from &amp;#39;./getters&amp;#39;

describe(&amp;#39;getters&amp;#39;, () =&amp;gt; {
  it(&amp;#39;filteredProducts&amp;#39;, () =&amp;gt; {
    // mock state
    const state = {
      products: [
        { id: 1, title: &amp;#39;Apple&amp;#39;, category: &amp;#39;fruit&amp;#39; },
        { id: 2, title: &amp;#39;Orange&amp;#39;, category: &amp;#39;fruit&amp;#39; },
        { id: 3, title: &amp;#39;Carrot&amp;#39;, category: &amp;#39;vegetable&amp;#39; }
      ]
    }
    // 모의 getter
    const filterCategory = &amp;#39;fruit&amp;#39;

    // getter로 부터 결과를 받습니다
    const result = getters.filteredProducts(state, { filterCategory })

    // 결과 테스트
    expect(result).to.deep.equal([
      { id: 1, title: &amp;#39;Apple&amp;#39;, category: &amp;#39;fruit&amp;#39; },
      { id: 2, title: &amp;#39;Orange&amp;#39;, category: &amp;#39;fruit&amp;#39; }
    ])
  })
})&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;4. 테스트 실행&lt;/h1&gt;
&lt;p&gt;제대로 작성된 mutation과 action은 브라우저 API와 의존성이 없어야 합니다.&lt;/p&gt;
&lt;h3&gt;Node에서 테스트&lt;/h3&gt;
&lt;p&gt;webpack를 사용하여 테스트를 번들로 묶어 Node를 사용하여 직접 실행 할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// webpack.config.js
module.exports = {
  entry: &amp;#39;./test.js&amp;#39;,
  output: {
    path: __dirname,
    filename: &amp;#39;test-bundle.js&amp;#39;
  },
  module: {
    loaders: [
      {
        test: /\.js$/,
        loader: &amp;#39;babel-loader&amp;#39;,
        exclude: /node_modules/
      }
    ]
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;webpack
mocha test-bundle.js&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 방법으로 webpack을 설정하여 테스트 할 수 있습니다. (&lt;code&gt;.babelrc&lt;/code&gt; 도 필요합니다.)&lt;/p&gt;
&lt;h3&gt;브라우저에서 테스트&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;mocha-loader&lt;/code&gt;를 설치합니다.&lt;/li&gt;
&lt;li&gt;webpak 설정에서 &lt;code&gt;entry&lt;/code&gt;를 &lt;code&gt;&amp;#39;mocha!babel!./test.js&amp;#39;&lt;/code&gt;로 변경합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;webpack-dev-server&lt;/code&gt;를 시작합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;localhost:8080/webpack-dev-server/test-bundle&lt;/code&gt;로 이동합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Karma + karma-webpack를 사용한 테스트는 &lt;a href=&quot;https://vue-loader.vuejs.org/guide/testing.html&quot;&gt;vue-loader&lt;/a&gt; 참고 바랍니다.&lt;/p&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://vuex.vuejs.org/kr/guide/testing.html&quot;&gt;https://vuex.vuejs.org/kr/guide/testing.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>Action Testing</category>
      <category>Getters Testing</category>
      <category>karma-webpack</category>
      <category>mocha-loader</category>
      <category>Mutation Testing</category>
      <category>Vue.js</category>
      <category>vuex</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/91</guid>
      <comments>https://beomy.tistory.com/91#entry91comment</comments>
      <pubDate>Fri, 19 Jul 2019 01:33:06 +0900</pubDate>
    </item>
    <item>
      <title>[vuex] Strict Mode와 Form Handling</title>
      <link>https://beomy.tistory.com/90</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXK9e3/btqwQke3uBj/dZreSPvjcCsPBBAMWL79gK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXK9e3/btqwQke3uBj/dZreSPvjcCsPBBAMWL79gK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXK9e3/btqwQke3uBj/dZreSPvjcCsPBBAMWL79gK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXK9e3%2FbtqwQke3uBj%2FdZreSPvjcCsPBBAMWL79gK%2Fimg.png&quot; width=&quot;100%&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;h1&gt;1. Strict Mode&lt;/h1&gt;
&lt;p&gt;strict 모드를 사용하기 위해서는 &lt;code&gt;strict: true&lt;/code&gt;를 Vuex의 store를 만들 때 추가만 하면 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const store = new Vuex.Store({
  // ...
  strict: true
})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;strict 모드에서는 state가 mutation 핸들러 외부에서 변경 될 때 마다 오류를 발생 시킵니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;배포시 strict 모드를 꺼야 합니다.&lt;/strong&gt; strict 모드는 부적절한 state의 변경을 감지하기 위해 state 트리를 관찰하기 때문에, 성능 저하가 발생합니다. 성능 이슈를 해결하기 위해서는 배포 버전에서는 strict 모드를 꺼야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const store = new Vuex.Store({
  // ...
  strict: process.env.NODE_ENV !== &amp;#39;production&amp;#39;
})&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;2. Form Handling&lt;/h1&gt;
&lt;p&gt;strict 모드로 Vuex를 사용하는 경우 &lt;code&gt;v-model&lt;/code&gt;을 사용하는 것이 까다로울 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;input v-model=&amp;quot;obj.message&amp;quot;&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;obj&lt;/code&gt;가 store에서 객체를 반환하는 &lt;code&gt;computed&lt;/code&gt; 속성이라면 &lt;code&gt;v-model&lt;/code&gt;은 사용자의 입력이 있을 때, &lt;code&gt;obj.message&lt;/code&gt;를 직접 변경하려고 합니다. 하지만 strict 모드에서는 state의 변경이 mutation 핸들러 내부에서 수행되지 않기 때문에 오류를 발생시킵니다. 이러한 문제점을 해결하는 2가지 방법에 대해 이야기 하도록 하겠습니다.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;value&lt;/code&gt;와 &lt;code&gt;input&lt;/code&gt;(&lt;code&gt;change&lt;/code&gt;) 사용하기&lt;/h3&gt;
&lt;p&gt;첫번째 방법은 &lt;code&gt;value&lt;/code&gt; 속성과 &lt;code&gt;input&lt;/code&gt; 혹은 &lt;code&gt;change&lt;/code&gt; 등의 이벤트를 이용하는 방법입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;input :value=&amp;quot;message&amp;quot; @input=&amp;quot;updateMessage&amp;quot;&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// ...
computed: {
  ...mapState({
    message: state =&amp;gt; state.obj.message
  })
},
methods: {
  updateMessage (e) {
    this.$store.commit(&amp;#39;updateMessage&amp;#39;, e.target.value)
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// ...
mutations: {
  updateMessage (state, message) {
    state.obj.message = message
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;setter와 getter 사용하기&lt;/h3&gt;
&lt;p&gt;두번째 방법은 &lt;code&gt;computed&lt;/code&gt; 속성에 setter와 getter를 정의하는 방법입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;input v-model=&amp;quot;message&amp;quot;&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// ...
computed: {
  message: {
    get () {
      return this.$store.state.obj.message
    },
    set (value) {
      this.$store.commit(&amp;#39;updateMessage&amp;#39;, value)
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://vuex.vuejs.org/kr/guide/forms.html&quot;&gt;https://vuex.vuejs.org/kr/guide/forms.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>getter</category>
      <category>Setter</category>
      <category>strict mode</category>
      <category>V-Model</category>
      <category>Vue.js</category>
      <category>vuex</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/90</guid>
      <comments>https://beomy.tistory.com/90#entry90comment</comments>
      <pubDate>Tue, 16 Jul 2019 23:43:50 +0900</pubDate>
    </item>
    <item>
      <title>[vuex] Plugins</title>
      <link>https://beomy.tistory.com/89</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YK6NM/btqwNtEZqpv/5OgNhdISdBm5BGg4YP8SK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YK6NM/btqwNtEZqpv/5OgNhdISdBm5BGg4YP8SK0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YK6NM/btqwNtEZqpv/5OgNhdISdBm5BGg4YP8SK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYK6NM%2FbtqwNtEZqpv%2F5OgNhdISdBm5BGg4YP8SK0%2Fimg.png&quot; width=&quot;100%&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;Vuex store는 변이에 대한 훅을 노출하는 &lt;code&gt;plugins&lt;/code&gt; 옵션을 제공합니다. 플러그인은 store를 유일한 전달인자로 받는 함수입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const myPlugin = store =&amp;gt; {
  // 저장소가 초기화 될 때 불립니다.
  store.subscribe((mutation, state) =&amp;gt; {
    // 매 변이시마다 불립니다.
    // 변이는 { type, payload } 포맷으로 제공됩니다.
  })
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const store = new Vuex.Store({
  // ...
  plugins: [myPlugin]
})&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;1. 플러그인 내부에서 변이 커밋하기&lt;/h1&gt;
&lt;p&gt;변이를 커밋한다는 말은 &lt;code&gt;commit&lt;/code&gt; 메소드를 사용하여 &lt;code&gt;mutations&lt;/code&gt;의 핸들러를 호출한다는 말로 사용할 것입니다. 플러그인은 state를 직접 변경 할 수 없습니다. 컴포넌트와 마찬가지로 변이를 커밋하여 핸들러를 호출해야 합니다.&lt;br&gt;플러그인에서 변이를 커밋하여 데이터를 store에 동기화 할 수 있습니다. 예를 들어,&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;export default function createWebSocketPlugin (socket) {
  return store =&amp;gt; {
    socket.on(&amp;#39;data&amp;#39;, data =&amp;gt; {
      store.commit(&amp;#39;receiveData&amp;#39;, data)
    })
    store.subscribe(mutation =&amp;gt; {
      if (mutation.type === &amp;#39;UPDATE_DATA&amp;#39;) {
        socket.emit(&amp;#39;update&amp;#39;, mutation.payload)
      }
    })
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const plugin = createWebSocketPlugin(socket)

const store = new Vuex.Store({
  state,
  mutations,
  plugins: [plugin]
})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 코드와 같이 websocket를 사용하여 플러그인이 있다면, socket이 data를 전송 받게 되면 커밋하여 store를 동기화 하고, 변이가 커밋 된다면, &lt;code&gt;socket.emit(&amp;#39;update&amp;#39;, mutation.payload)&lt;/code&gt;를 사용하여 socket에 변경된 데이터를 전송 할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;2. state 스냅샷 가져오기&lt;/h1&gt;
&lt;p&gt;변이 이전의 state와 변이 이후의 state를 비교해야 할 필요가 있을 수 있습니다. 그러기 위하여, 플러그인의 상태의 스냅샷을 얻어야 합니다. 여기서 스냅샷이란, state가 변경되기 전의 값을 저장하는 객체를 이야기 합니다. 스냅샷을 만들기 위해서는 깊은 복사를 수행해야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const myPluginWithSnapshot = store =&amp;gt; {
  let prevState = _.cloneDeep(store.state)
  store.subscribe((mutation, state) =&amp;gt; {
    let nextState = _.cloneDeep(state)

    // prevState와 nextState를 비교하십시오.

    // 다음 변이를 위한 상태 저장
    prevState = nextState
  })
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;state 스탭샷을 사용하는 플러그인은 개발 모드에서만 사용해야 합니다.&lt;/strong&gt; &lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const store = new Vuex.Store({
  // ...
  plugins: process.env.NODE_ENV !== &amp;#39;production&amp;#39;
    ? [myPluginWithSnapshot]
    : []
})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;webpack 또는 Browserify를 사용하는 경우 위의 코드와 같이 작성 할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;3. 내장 Logger 플러그인&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;vue-devtools를 사용하면 필요 없을 수 있습니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Vuex는 디버깅을 위한 Logger 플로그인을 제공합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;import createLogger from &amp;#39;vuex/dist/logger&amp;#39;

const store = new Vuex.Store({
  plugins: [createLogger()]
})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;createLogger&lt;/code&gt; 함수는 몇가지 옵션을 가집니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const logger = createLogger({
  collapsed: false, // 로그를 가지는 변이 자동 확장
  filter (mutation, stateBefore, stateAfter) {
    // 변이시 항상 로깅해야 한다면 true 를 반환하십시오.
    // 변이는 { type, payload }의 포맷입니다.
    return mutation.type !== &amp;quot;aBlacklistedMutation&amp;quot;
  },
  transformer (state) {
    // 로깅하기전 상태를 변이 하십시오.
    // 예를 들어 특정 하위 트리만 반환합니다.
    return state.subTree
  },
  mutationTransformer (mutation) {
    // 변이는 { type, payload }의 포맷으로 기록됩니다.
    // 원하는 포맷으로 변경할 수 있습니다.
    return mutation.type
  },
  logger: console, // `console` API의 구현체, default `console`
})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Logger 파일은 &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; 태그를 통해 직접 포함 할 수 있습니다. 태그를 통해 직접 포함하였다면, &lt;code&gt;createVuexLogger&lt;/code&gt; 함수가 전역으로 노출됩니다. Logger 플로그인은 개발용으로만 사용되어야 합니다.&lt;/p&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://vuex.vuejs.org/kr/guide/plugins.html&quot;&gt;https://vuex.vuejs.org/kr/guide/plugins.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>createLogger</category>
      <category>createVuexLogger</category>
      <category>Logger Plugin</category>
      <category>Plugins</category>
      <category>subscribe</category>
      <category>Vue.js</category>
      <category>vuex</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/89</guid>
      <comments>https://beomy.tistory.com/89#entry89comment</comments>
      <pubDate>Tue, 16 Jul 2019 22:09:31 +0900</pubDate>
    </item>
    <item>
      <title>[vuex] Modules</title>
      <link>https://beomy.tistory.com/88</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6sCRx/btqwMKTMf3H/EtKWbQyVxxYGJaQ3YhGQA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6sCRx/btqwMKTMf3H/EtKWbQyVxxYGJaQ3YhGQA0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6sCRx/btqwMKTMf3H/EtKWbQyVxxYGJaQ3YhGQA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6sCRx%2FbtqwMKTMf3H%2FEtKWbQyVxxYGJaQ3YhGQA0%2Fimg.png&quot; width=&quot;100%&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;하나의 state 트리를 사용하기 때문에 어플리케이션의 모든 state는 하나의 큰 객체 안에 포함됩니다. 프로젝트의 규모가 커질 수록 store의 크기도 커지게 됩니다. Vuex는 이 문제를 해결하기 위해 store를 모듈로 나눌 수 있는 기능을 제공합니다. 각 모듈은 state, mutation, action, getter, 중첩된 모듈을 포함 할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const moduleA = {
  state: { ... },
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: { ... },
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

store.state.a // -&amp;gt; moduleA&amp;#39;의 상태
store.state.b // -&amp;gt; moduleB&amp;#39;의 상태&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;1. Module Local State&lt;/h1&gt;
&lt;p&gt;모듈의 mutation과 getter의 핸들러의 첫 번째 전달인자는 모듈의 로컬 state가 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const moduleA = {
  state: { count: 0 },
  mutations: {
    increment (state) {
      // state는 지역 모듈 상태 입니다
      state.count++
    }
  },

  getters: {
    doubleCount (state) {
      return state.count * 2
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;action의 핸들러의 첫번째 인자는 context입니다. 내부 모듈에서는 &lt;code&gt;context.state&lt;/code&gt;는 로컬 state를 나타내고, 루트 state는 &lt;code&gt;context.rootState&lt;/code&gt;로 노출됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const moduleA = {
  // ...
  actions: {
    incrementIfOddOnRootSum ({ state, commit, rootState }) {
      if ((state.count + rootState.count) % 2 === 1) {
        commit(&amp;#39;increment&amp;#39;)
      }
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;getter의 핸들러에서도 루트 state를 접근할 수 있습니다. getter의 핸들러의 세번째 인자는 루트 state가 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const moduleA = {
  // ...
  getters: {
    sumWithRootCount (state, getters, rootState) {
      return state.count + rootState.count
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;2. Namespace&lt;/h1&gt;
&lt;p&gt;기본적으로 모듈 내의 action, mutation, getter는 전역 네임스페이스 아래에 등록됩니다. 여러 모듈의 mutation, action의 핸들러를 동일한 이름으로 만들었다면, 여러 모듈이 동일한 mutation 혹은 action에 반응하게 됩니다.&lt;br&gt;만약 모듈이 독립적으로 재사용되기 원한다면, &lt;code&gt;namespaced: true&lt;/code&gt;를 사용하면 됩니다. 모듈이 등록 될 때, 해당 모듈의 모든 getter, action, mutation은 자동으로 모듈의 경로를 기반으로 네임스페이스가 지정됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const store = new Vuex.Store({
  modules: {
    account: {
      namespaced: true,

      // 모듈 자산
      state: { ... }, // 모듈 상태는 이미 중첩되어 있고, 네임스페이스 옵션의 영향을 받지 않음
      getters: {
        isAdmin () { ... } // -&amp;gt; getters[&amp;#39;account/isAdmin&amp;#39;]
      },
      actions: {
        login () { ... } // -&amp;gt; dispatch(&amp;#39;account/login&amp;#39;)
      },
      mutations: {
        login () { ... } // -&amp;gt; commit(&amp;#39;account/login&amp;#39;)
      },

      // 중첩 모듈
      modules: {
        // 부모 모듈로부터 네임스페이스를 상속받음
        myPage: {
          state: { ... },
          getters: {
            profile () { ... } // -&amp;gt; getters[&amp;#39;account/profile&amp;#39;]
          }
        },

        // 네임스페이스를 더 중첩
        posts: {
          namespaced: true,

          state: { ... },
          getters: {
            popular () { ... } // -&amp;gt; getters[&amp;#39;account/posts/popular&amp;#39;]
          }
        }
      }
    }
  }
})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;네임스페이스의 getter, action은 로컬의 &lt;code&gt;getters&lt;/code&gt;, &lt;code&gt;dispatch&lt;/code&gt;, &lt;code&gt;commit&lt;/code&gt;을 전달 받습니다. 즉 동일한 모듈 안에서는 접두어 없이 모듈의 자산(&lt;code&gt;actions&lt;/code&gt;, &lt;code&gt;mutations&lt;/code&gt;, &lt;code&gt;getters&lt;/code&gt;, &lt;code&gt;state&lt;/code&gt;)들을 사용할 수 있습니다.&lt;/p&gt;
&lt;h3&gt;네임스페이스 모듈에서 전역 접근&lt;/h3&gt;
&lt;p&gt;모듈에서 전역의 state나 getter를 사용해야 한다면, &lt;code&gt;rootState&lt;/code&gt;와 &lt;code&gt;rootGetters&lt;/code&gt;가 getter의 핸들러 함수의 3번째, 4번째 인자로 전달 됩니다. 또한 action의 핸들러 함수의 &lt;code&gt;context&lt;/code&gt; 객체의 속성으로도 전역에 접근할 수 있습니다.&lt;br&gt;전역의 action을 dispatch 하거나, mutation을 commit 하려면, &lt;code&gt;dispatch&lt;/code&gt;와 &lt;code&gt;commit&lt;/code&gt;의 3번째 전달인자로 &lt;code&gt;{ root: true }&lt;/code&gt;를 전달하면 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;modules: {
  foo: {
    namespaced: true,

    getters: {
      // `getters`는 해당 모듈의 지역화된 getters
      // getters의 4번째 인자를 통해서 rootGetters 사용 가능
      someGetter (state, getters, rootState, rootGetters) {
        getters.someOtherGetter // -&amp;gt; &amp;#39;foo/someOtherGetter&amp;#39;
        rootGetters.someOtherGetter // -&amp;gt; &amp;#39;someOtherGetter&amp;#39;
      },
      someOtherGetter: state =&amp;gt; { ... }
    },

    actions: {
      // 디스패치와 커밋도 해당 모듈의 지역화된 것
      // 전역 디스패치/커밋을 위한 `root` 옵션 설정 가능
      someAction ({ dispatch, commit, getters, rootGetters }) {
        getters.someGetter // -&amp;gt; &amp;#39;foo/someGetter&amp;#39;
        rootGetters.someGetter // -&amp;gt; &amp;#39;someGetter&amp;#39;

        dispatch(&amp;#39;someOtherAction&amp;#39;) // -&amp;gt; &amp;#39;foo/someOtherAction&amp;#39;
        dispatch(&amp;#39;someOtherAction&amp;#39;, null, { root: true }) // -&amp;gt; &amp;#39;someOtherAction&amp;#39;

        commit(&amp;#39;someMutation&amp;#39;) // -&amp;gt; &amp;#39;foo/someMutation&amp;#39;
        commit(&amp;#39;someMutation&amp;#39;, null, { root: true }) // -&amp;gt; &amp;#39;someMutation&amp;#39;
      },
      someOtherAction (ctx, payload) { ... }
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;네임스페이스 모듈에서 전역 action 등록&lt;/h3&gt;
&lt;p&gt;네임스페이스 모듈에서 전역 action을 등록하려면, &lt;code&gt;root: true&lt;/code&gt;를 표시하고 &lt;code&gt;handler&lt;/code&gt; 함수에 action을 정의 하면 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;{
  actions: {
    someOtherAction ({dispatch}) {
      dispatch(&amp;#39;someAction&amp;#39;)
    }
  },
  modules: {
    foo: {
      namespaced: true,

      actions: {
        someAction: {
          root: true,
          handler (namespacedContext, payload) { ... } // -&amp;gt; &amp;#39;someAction&amp;#39;
        }
      }
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;헬퍼에서 네임스페이스 바인딩&lt;/h3&gt;
&lt;p&gt;헬퍼(&lt;code&gt;mapState&lt;/code&gt;, &lt;code&gt;mapGetters&lt;/code&gt;, &lt;code&gt;mapActions&lt;/code&gt;, &lt;code&gt;mapMutations&lt;/code&gt;)를 사용하여 컴포넌트에 네임스페이스 모듈을 바인딩하려고 하면 조금 장황해 질 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;computed: {
  ...mapState({
    a: state =&amp;gt; state.some.nested.module.a,
    b: state =&amp;gt; state.some.nested.module.b
  })
},
methods: {
  ...mapActions([
    &amp;#39;some/nested/module/foo&amp;#39;, // -&amp;gt; this[&amp;#39;some/nested/module/foo&amp;#39;]()
    &amp;#39;some/nested/module/bar&amp;#39; // -&amp;gt; this[&amp;#39;some/nested/module/bar&amp;#39;]()
  ])
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 코드를 보면 중복된 문자열들을 볼 수 있습니다. 이렇게 코드가 장황해 지는 것을 최소화 하기 위해 헬퍼의 첫 번째 인자에 네임스페이스 경로를 전달 하여 코드를 단순화 할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;computed: {
  ...mapState(&amp;#39;some/nested/module&amp;#39;, {
    a: state =&amp;gt; state.a,
    b: state =&amp;gt; state.b
  })
},
methods: {
  ...mapActions(&amp;#39;some/nested/module&amp;#39;, [
    &amp;#39;foo&amp;#39;, // -&amp;gt; this.foo()
    &amp;#39;bar&amp;#39; // -&amp;gt; this.bar()
  ])
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;또한 &lt;code&gt;createNamespacedHelpers&lt;/code&gt;를 사용하여 네임스페이스 헬퍼를 생성할 수도 있습니다. &lt;code&gt;createNamespaceHelper&lt;/code&gt;는 전달된 네임스페이스 값으로 바인딩된 새로운 헬퍼를 반환합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;import { createNamespacedHelpers } from &amp;#39;vuex&amp;#39;

const { mapState, mapActions } = createNamespacedHelpers(&amp;#39;some/nested/module&amp;#39;)

export default {
  computed: {
    // `some/nested/module`에서 찾음
    ...mapState({
      a: state =&amp;gt; state.a,
      b: state =&amp;gt; state.b
    })
  },
  methods: {
    // `some/nested/module`에서 찾음
    ...mapActions([
      &amp;#39;foo&amp;#39;,
      &amp;#39;bar&amp;#39;
    ])
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;플러그인 개발자를 위한 주의사항&lt;/h3&gt;
&lt;p&gt;개발자가 특정 모듈을 위한 플로그인을 만들고, 사용자가 이 플로그인을 사용한다고 가정했을 때, 개발자가 지정한 네임스페이스와 사용자가 기존에 사용하고 있던 네임스페이스와의 충돌이 발생할 수 있습니다. 이러한 상황을 피하기 위해서 플로그인 옵션을 통해 네임스페이스 값을 전달 받는 것이 좋습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// 플러그인 옵션을 통해 네임스페이스 값 전달
// 그리고 Vuex 플러그인 함수를 반환
export function createPlugin (options = {}) {
  return function (store) {
    // add namespace to plugin module&amp;#39;s types
    const namespace = options.namespace || &amp;#39;&amp;#39;
    store.dispatch(namespace + &amp;#39;pluginAction&amp;#39;)
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;3. 동적 모듈 등록&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;store.registerModule&lt;/code&gt; 메소드를 사용하여 store가 생성된 후에 모듈을 등록 할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;store.registerModule(&amp;#39;myModule&amp;#39;, {
  // ...
})

// `nested/myModule` 중첩 모듈 등록
store.registerModule([&amp;#39;nested&amp;#39;, &amp;#39;myModule&amp;#39;], {
  // ...
})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 코드는 &lt;code&gt;store.state.myModule&lt;/code&gt;과 &lt;code&gt;store.state.nested.myModule&lt;/code&gt;로 노출됩니다.&lt;/p&gt;
&lt;p&gt;동적 모듈 등록을 사용하면 다른 Vue 플로그인도 어플리케이션의 store에 모듈을 연결하여 상태 관리에 Vuex를 활용할 수 있습니다. 쉽게, 동적 모듈을 사용하면 Vue 플로그인에서도 Vuex를 사용할 수 있게 된다고 이야기 할 수 있습니다, 예를 들어 &lt;a href=&quot;https://github.com/vuejs/vuex-router-sync&quot;&gt;vuex-router-sync&lt;/a&gt; 라이브러리는 vue-router와 vuex를 사용한 라이브러리입니다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;store.unregisterModule(moduleName)&lt;/code&gt;을 사용하여 동적으로 등록 된 모듈을 제거할 수도 있습니다. 하지만 이 방법으로는 정적으로 생성된 모듈(store 생성시 선언 되는 모듈)은 제거 할 수 없습니다.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;registerModule&lt;/code&gt; 사용 할 때, state 보존하기&lt;/h3&gt;
&lt;p&gt;새 모듈을 등록할 때, 이전 state를 유지해야 한다면, store에 이미 해당 모듈의 state가 포함되어 있어 덮어 쓰지 않아야 한다면, &lt;code&gt;preserveState&lt;/code&gt; 옵션을 사용하면 됩니다.&lt;br&gt;&lt;code&gt;store.registerModule(&amp;#39;a&amp;#39;, module, { preserveState: true })&lt;/code&gt;와 같이 &lt;code&gt;prserveState: true&lt;/code&gt;를 하면 모듈이 등록 되고, action, mutation, getter가 store에 추가 되지만, state는 변경 되지 않습니다.&lt;/p&gt;
&lt;h1&gt;4. 모듈 재사용&lt;/h1&gt;
&lt;p&gt;동일 모듈을 사용하는 여러 store를 생성하거나, 동일한 모듈을 동일한 store에 여러번 등록 하는 등. 한 모듈에서 여러 인스턴스를 생성해서 사용해야 할 때가 있습니다. state에 일반 객체(예를 들어, &lt;code&gt;state: { ... }&lt;/code&gt;)를 사용하여 모듈의 state를 선언하면 state 객체가 참조에 의해 공유되기 때문에 state의 오염을 일으킵니다.&lt;br&gt;예를 들어, A 모듈을 2번 store에 등록 할 경우 (첫번째 A 모듈을 A1으로 두번째 모듈을 A2로 이야기 하겠습니다.) store에 등록한 A1 모듈의 state와 A2 모듈의 state는 완전히 다른 객체인 것 같지만 참조에 의해 공유 됩니다. 즉 어떤 부분(Object, Array...)은 공유하는 객체입니다.&lt;br&gt;이 문제는 Vue 컴포넌트의 &lt;code&gt;data&lt;/code&gt;를 함수로 만들어 객체를 리턴해야 하는 이슈와 완전히 동일한 이슈입니다. 동일한 이슈이기 때문에 역시 해결 방법도 동일합니다. 함수를 사용하여 모듈 state를 선언합니다. (2.3.0+)&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const MyReusableModule = {
  state () {
    return {
      foo: &amp;#39;bar&amp;#39;
    }
  },
  // 변이, 액션, getters...
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://vuex.vuejs.org/guide/modules.html&quot;&gt;https://vuex.vuejs.org/guide/modules.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>createNamespacedHelpers</category>
      <category>module</category>
      <category>namespace</category>
      <category>preserveState</category>
      <category>registerModule</category>
      <category>unregisterModule</category>
      <category>Vue.js</category>
      <category>vuex</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/88</guid>
      <comments>https://beomy.tistory.com/88#entry88comment</comments>
      <pubDate>Tue, 16 Jul 2019 01:05:56 +0900</pubDate>
    </item>
    <item>
      <title>[vuex] Actions</title>
      <link>https://beomy.tistory.com/87</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vFDns/btqwFz5YbRe/eVr1iGuzISrBqKp8odQagk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vFDns/btqwFz5YbRe/eVr1iGuzISrBqKp8odQagk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vFDns/btqwFz5YbRe/eVr1iGuzISrBqKp8odQagk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvFDns%2FbtqwFz5YbRe%2FeVr1iGuzISrBqKp8odQagk%2Fimg.png&quot; width=&quot;100%&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;Action은 Mutation과 비슷하지만, 몇가지 차이점이 있습니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;state를 변경하지 않고 Mutation의 핸들러 함수를 호출합니다.&lt;/li&gt;
&lt;li&gt;비동기 작업을 수행할 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;pf&quot;&gt;&lt;code&gt;const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    increment (context) {
      context.commit('increment')
    }
  }
})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 코드와 같이 action을 등록 할 수 있습니다. action의 핸들러는 &lt;code&gt;context&lt;/code&gt; 객체를 전달인자로 받습니다. &lt;code&gt;context&lt;/code&gt; 객체는 store의 메소드와 속성들을 가지고 있는 객체입니다. 그래서 &lt;code&gt;context.commit&lt;/code&gt;를 호출하여 Mutation의 핸들러를 호출하거나, &lt;code&gt;context.state&lt;/code&gt;와 &lt;code&gt;context.getters&lt;/code&gt;를 통해 state와 getter에 접근 할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;xquery&quot;&gt;&lt;code&gt;actions: {
  increment ({ commit }) {
    commit('increment')
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;코드를 단순화하기 위해서 ES6에 추가된 &lt;a href=&quot;https://beomy.tistory.com/18&quot;&gt;구조분해할당&lt;/a&gt;를 사용할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;1. Dispatch&lt;/h1&gt;
&lt;p&gt;action의 핸들러는 &lt;code&gt;store.dispatch&lt;/code&gt; 메소드를 사용하여 호출 할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;store.dispatch('increment')&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;store.commit('increment')&lt;/code&gt;를 직접 호출하지 않는 이유는 비동기에 있습니다. Mutation은 반드시 동기적으로 작성되어야 한다고 이전 포스트(&lt;a href=&quot;https://beomy.tistory.com/86&quot;&gt;[vuex] Mutations&lt;/a&gt; 참고)에서 이야기 했습니다.&lt;/p&gt;
&lt;pre class=&quot;xquery&quot;&gt;&lt;code&gt;actions: {
  incrementAsync ({ commit }) {
    setTimeout(() =&amp;gt; {
      commit('increment')
    }, 1000)
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;action의 핸들러에서는 위의 코드와 같이 비동기 작업을 수행 할 수 있습니다.&lt;/p&gt;
&lt;h3&gt;객체 스타일 payload&lt;/h3&gt;
&lt;p&gt;Mutation과 동일하게 객체 스타일 payload 기능을 제공합니다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;// 페이로드와 함께 디스패치
store.dispatch('incrementAsync', {
  amount: 10
})

// 객체와 함께 디스패치
store.dispatch({
  type: 'incrementAsync',
  amount: 10
})&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;비동기 Back-End API 호출&lt;/h3&gt;
&lt;p&gt;action의 실용적인 사용법은 비동기 Back-End API를 호출 하는 것입니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;actions: {
  checkout ({ commit, state }, products) {
    // 장바구니에 현재있는 항목을 저장하십시오.
    const savedCartItems = [...state.cart.added]

    // 결제 요청을 보낸 후 장바구니를 비웁니다.
    commit(types.CHECKOUT_REQUEST)

    // 상점 API는 성공 콜백 및 실패 콜백을 받습니다.
    shop.buyProducts(
      products,
      // 요청 성공 핸들러
      () =&amp;gt; commit(types.CHECKOUT_SUCCESS),
      // 요청 실패 핸들러
      () =&amp;gt; commit(types.CHECKOUT_FAILURE, savedCartItems)
    )
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 예제 코드와 같이 action의 효율적인 사용방법은 Back-End 비동기 API를 호출하고, 응답을 받은 후 Mutation의 핸들러를 호출하여 state를 변경하는 것입니다.&lt;/p&gt;
&lt;h1&gt;2. &lt;code&gt;mapActions&lt;/code&gt;&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;this.$store.dispatch('xxx')&lt;/code&gt;를 사용하여 컴포넌트에서 action의 핸들러 함수를 호출할 수 있습니다. 또 다른 방법으로 &lt;code&gt;mapActions&lt;/code&gt;를 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;import { mapActions } from 'vuex'

export default {
  // ...
  methods: {
    ...mapActions([
      'increment' // this.increment()을 this.$store.dispatch('increment')에 매핑
    ]),
    ...mapActions({
      add: 'increment' // this.add()을 this.$store.dispatch('increment')에 매핑
    })
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;3. Action 사용하기&lt;/h1&gt;
&lt;p&gt;이제는 Action을 어떻게 사용할 수 있는지 이야기 해 보도록 하겠습니다. action은 종종 비동기적으로 작성됩니다. action의 비동기처리는 어떻게 할 수 있는지 이야기 할 것입니다.&lt;br /&gt;&lt;code&gt;store.dispatch&lt;/code&gt;의 반환 값은 &lt;code&gt;Promise&lt;/code&gt;입니다. &lt;code&gt;store.dispatch&lt;/code&gt;로 호출한 action의 핸들러 함수의 &lt;code&gt;Promise&lt;/code&gt; 반환 값을 처리 할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;actions: {
  actionA ({ commit }) {
    return new Promise((resolve, reject) =&amp;gt; {
      setTimeout(() =&amp;gt; {
        commit('someMutation')
        resolve()
      }, 1000)
    })
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;action의 핸들러 함수가 위의 코드와 같이 작성되었다면,&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;store.dispatch('actionA').then(() =&amp;gt; {
  // ...
})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 코드와 같이 &lt;code&gt;Promise&lt;/code&gt;의 비동기처리를 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;// getData() 및 getOtherData()가 Promise를 반환한다고 가정합니다.
actions: {
  async actionA ({ commit }) {
    commit('gotData', await getData())
  },
  async actionB ({ dispatch, commit }) {
    await dispatch('actionA') // actionA가 끝나기를 기다립니다.
    commit('gotOtherData', await getOtherData())
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Promise&lt;/code&gt;를 사용할 수 있다면, 당연히 ES8에 추가된 &lt;code&gt;async/await&lt;/code&gt; 문법(&lt;a href=&quot;https://beomy.tistory.com/45&quot;&gt;[자바스크립트] ES8(ECMA Script 8) - async, await&lt;/a&gt; 참고)도 사용 할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;actions: {
  // ...
  actionB ({ dispatch, commit }) {
    return dispatch('actionA').then(() =&amp;gt; {
      commit('someOtherMutation')
    })
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;또, 위의 코드와 같이 action의 핸들러 안에서 다른 action의 핸들러를 호출 할 수도 있습니다.&lt;/p&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://vuex.vuejs.org/guide/actions.html&quot;&gt;https://vuex.vuejs.org/guide/actions.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>Action</category>
      <category>dispatch</category>
      <category>mapActions</category>
      <category>Vue.js</category>
      <category>vuex</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/87</guid>
      <comments>https://beomy.tistory.com/87#entry87comment</comments>
      <pubDate>Thu, 11 Jul 2019 00:40:08 +0900</pubDate>
    </item>
    <item>
      <title>[vuex] Mutations</title>
      <link>https://beomy.tistory.com/86</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bIOPaD/btqwAmexx3m/CKXOboPw5NW6lfKNv1rxc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bIOPaD/btqwAmexx3m/CKXOboPw5NW6lfKNv1rxc1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bIOPaD/btqwAmexx3m/CKXOboPw5NW6lfKNv1rxc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbIOPaD%2FbtqwAmexx3m%2FCKXOboPw5NW6lfKNv1rxc1%2Fimg.png&quot; width=&quot;100%&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;Vuex의 store에서 실제로 state를 변경할 수 있는 유일한 방법은 변이(Mutation) 하는 것입니다. Vuex의 Mutation은 이벤트와 유사합니다. 각 Mutation에서는 문자열 타입과 핸들러가 있습니다. 핸들러 함수는 실제 state를 수정하는 곳이고, 첫번째 전달인자로 state를 받습니다.&lt;/p&gt;
&lt;pre class=&quot;pf&quot;&gt;&lt;code&gt;const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment (state) {
      // 상태 변이
      state.count++
    }
  }
})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Mutation의 핸들러를 직접 호출 할 수는 없습니다. &lt;code&gt;mutation&lt;/code&gt; 옵션은 이벤트 등록과 비슷합니다. 타입이 &lt;code&gt;increment&lt;/code&gt;인 Mutation가 발생하면 핸들러를 호출합니다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;store.commit('increment')&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Mutation 핸들러를 호출하려면 위의 코드처럼 &lt;code&gt;store.commit&lt;/code&gt;을 사용해야 합니다.&lt;/p&gt;
&lt;h1&gt;1. Commit with payload&lt;/h1&gt;
&lt;p&gt;Matation을 커밋한다는 말은 Mutation 핸들러를 실행 시킨다는 말로 사용할 것입니다. Commit with payload란 Mutation 핸들러에 payload를 전달하는 의미입니다. &lt;code&gt;store.commit&lt;/code&gt;에 payload라고 하는 추가 전달인자를 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;pf&quot;&gt;&lt;code&gt;// ...
mutations: {
  increment (state, n) {
    state.count += n
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;store.commit('increment', 10)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;대부분의 경우 payload는 여러 필드를 포함 할 수 있는 객체를 사용하는 것이 좋습니다.&lt;/p&gt;
&lt;pre class=&quot;pf&quot;&gt;&lt;code&gt;// ...
mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;store.commit('increment', {
  amount: 10
})&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;객체 스타일 payload 커밋&lt;/h3&gt;
&lt;p&gt;Mutation를 커밋하는 또 다른 방법은 &lt;code&gt;type&lt;/code&gt; 속성을 가진 객체를 사용하는 것입니다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;store.commit({
  type: 'increment',
  amount: 10
})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;객체 스타일 커밋을 사용할 때 사용 된 객체는 Mutation 핸들러에 payload로 전달 되기 때문에 핸들러는 동일하게 사용 하면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;pf&quot;&gt;&lt;code&gt;mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;2. 변이(Mutation)는 Vue의 반응성 규칙을 따릅니다.&lt;/h1&gt;
&lt;p&gt;Vuex의 state를 관찰 하고 있는 컴포넌트가 Vuex의 state 변경을 감지하고 자동으로 화면을 업데이트 해줍니다. 자동으로 화면을 업데이트 하기 위해서는 2가지를 지켜야 합니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;사용하는 모든 필드를 store에 초기화 하는 것이 좋습니다.&lt;/li&gt;
&lt;li&gt;객체에 새 속성을 추가 할 때, &lt;code&gt;Vue.set(obj, 'newProp', 123)&lt;/code&gt;을 사용하거나, 객체를 새로운 객체로 교체해야 합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;pf&quot;&gt;&lt;code&gt;state.obj = { ...state.obj, newProp: 123 }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 코드와 같이 &lt;a href=&quot;https://github.com/tc39/proposal-object-rest-spread&quot;&gt;객체 전개 연산자&lt;/a&gt;를 사용하여 새로운 객체로 교체 할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;3. Mutation Type 상수&lt;/h1&gt;
&lt;p&gt;Flux 구현에서 mutation type 상수를 사용하는 것이 일반적입니다. 상수들을 하나의 파일에 저장하면 여러 작업자들이 전체 어플리케이션이 어떤 Mutation이 있는지 한 눈에 파악 할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;arduino&quot;&gt;&lt;code&gt;// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// store.js
import Vuex from 'vuex'
import { SOME_MUTATION } from './mutation-types'

const store = new Vuex.Store({
  state: { ... },
  mutations: {
    // ES2015에서 계산 된 프로퍼티 이름 기능을 사용하여
    // 상수를 함수 이름으로 사용할 수 있습니다
    [SOME_MUTATION] (state) {
      // 변이 상태
    }
  }
})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Mutation Type 상수를 따로 관리할지 결정하는 것은 온전히 개발자의 선택에 달려있습니다.&lt;/p&gt;
&lt;h1&gt;4. Mutation의 핸들러 함수는 반드시 동기적이어야 합니다.&lt;/h1&gt;
&lt;p&gt;Mutation의 핸들러 함수는 반드시 동기적으로 작성되어야 합니다.&lt;/p&gt;
&lt;pre class=&quot;pf&quot;&gt;&lt;code&gt;mutations: {
  someMutation (state) {
    api.callAsyncMethod(() =&amp;gt; {
      state.count++
    })
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;예를 들면, 위의 코드와 같이 비동기로 동작하는 코드가 있습니다. 디버깅 하기 위해 devtool를 이용하여 mutation 로그를 보고 있다고 가정합시다. devtool은 state의 이전과 이후를 스냅샷으로 캡처해야 합니다. 그러나 Mutation이 비동기적으로 동작한다면, 핸들러 함수가 종료되었지만 state가 변경되지 않는 상태가 발생합니다. devtool 입장에서는 핸들러 함수 속에 있는 콜백함수가 실제로 호출되었는지 알 수 있는 방법이 없습니다. 이러한 이유로 Mutation의 핸들러 함수는 반드시 동기적이어야 합니다.&lt;/p&gt;
&lt;h1&gt;5. &lt;code&gt;mapMutations&lt;/code&gt;&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;this.$store.commit('xxx')&lt;/code&gt;를 사용하여 컴포넌트에서 Mutation의 핸들러 함수를 호출할 수 있습니다. &lt;code&gt;store.commit&lt;/code&gt;와 동일한 역할을 하는 헬퍼는 &lt;code&gt;mapMutations&lt;/code&gt; 입니다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;import { mapMutations } from 'vuex'

export default {
  // ...
  methods: {
    ...mapMutations([
      'increment' // this.increment()를 this.$store.commit('increment')에 매핑합니다.
    ]),
    ...mapMutations({
      add: 'increment' // this.add()를 this.$store.commit('increment')에 매핑합니다.
    })
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 코드와 같이 &lt;code&gt;mapMutations&lt;/code&gt;를 사용할 수 있습니다.&lt;/p&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://vuex.vuejs.org/kr/guide/mutations.html&quot;&gt;https://vuex.vuejs.org/kr/guide/mutations.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>COMMIT</category>
      <category>mapMutations</category>
      <category>mutation</category>
      <category>Vue.js</category>
      <category>vuex</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/86</guid>
      <comments>https://beomy.tistory.com/86#entry86comment</comments>
      <pubDate>Wed, 10 Jul 2019 23:23:40 +0900</pubDate>
    </item>
    <item>
      <title>[vuex] Getters</title>
      <link>https://beomy.tistory.com/85</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JBl1N/btqwsYk83ef/IKfg5jMamV21NKsfWMOCw1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JBl1N/btqwsYk83ef/IKfg5jMamV21NKsfWMOCw1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JBl1N/btqwsYk83ef/IKfg5jMamV21NKsfWMOCw1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJBl1N%2FbtqwsYk83ef%2FIKfg5jMamV21NKsfWMOCw1%2Fimg.png&quot; width=&quot;100%&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;state를 계산한 값을 사용해야 할 때가 있습니다.&lt;/p&gt;
&lt;pre class=&quot;stylus&quot;&gt;&lt;code&gt;computed: {
  doneTodosCount () {
    return this.$store.state.todos.filter(todo =&amp;gt; todo.done).length
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;todos&lt;/code&gt;라는 state에서 &lt;code&gt;done&lt;/code&gt; 상태인 리스트를 가져오고 싶을 때 위의 코드와 같이 작성할 수 있습니다. 하지만 여러 컴포넌트에서 이 코드를 사용해야 한다면, 위의 코드를 복사해야 한다는 단점이 있습니다. 동일한 코드를 복사하여 사용하게 된다면 당연히 유지보수는 힘들어지겠죠.&lt;br /&gt;이 단점을 해결하기 하기 위해서 Vuex는 Getter 기능을 제공합니다. store에서 사용하는 컴포넌트의 &lt;code&gt;computed&lt;/code&gt; 속성과 비슷하다고 생각하면 이해하기 쉽습니다. &lt;code&gt;computed&lt;/code&gt; 속성 처럼 종속성에 따라 결과를 캐시하고, 종속성이 변경된 경우에만 다시 계산합니다. 여기서 말한 종속성이란 결과를 계산하기 위해 사용된 원천 자료(source of truth)을 이야기 합니다.&lt;/p&gt;
&lt;pre class=&quot;pf&quot;&gt;&lt;code&gt;const store = new Vuex.Store({
  state: {
    todos: [
      { id: 1, text: '...', done: true },
      { id: 2, text: '...', done: false }
    ]
  },
  getters: {
    doneTodos: state =&amp;gt; {
      return state.todos.filter(todo =&amp;gt; todo.done)
    }
  }
})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;getter의 첫번째 인자값은 &lt;code&gt;state&lt;/code&gt; 입니다.&lt;/p&gt;
&lt;h1&gt;1. Property 스타일&lt;/h1&gt;
&lt;p&gt;getter는 &lt;code&gt;store.getters&lt;/code&gt; 객체를 통해 사용할 수 있습니다. property 값처럼 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;store.getters.doneTodos // -&amp;gt; [{ id: 1, text: '...', done: true }]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;getter는 두번째 전달인자로 &lt;code&gt;getters&lt;/code&gt;를 전달 받습니다. &lt;code&gt;getters&lt;/code&gt;를 사용하여 다른 getter를 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;pf&quot;&gt;&lt;code&gt;getters: {
  // ...
  doneTodosCount: (state, getters) =&amp;gt; {
    return getters.doneTodos.length
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;kotlin&quot;&gt;&lt;code&gt;computed: {
  doneTodosCount () {
    return this.$store.getters.doneTodosCount
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 코드와 같이 컴포넌트에서 getter를 사용할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;2. Method 스타일&lt;/h1&gt;
&lt;p&gt;함수를 반환하여 getter에 전달인자를 넘겨줄 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;pf&quot;&gt;&lt;code&gt;getters: {
  // ...
  getTodoById: (state) =&amp;gt; (id) =&amp;gt; {
    return state.todos.find(todo =&amp;gt; todo.id === id)
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;store.getters.getTodoById(2) // -&amp;gt; { id: 2, text: '...', done: false }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;getter를 메소드처럼 사용할 경우 결과를 캐시하지 않기 때문에, 호출할 때마다 값을 다시 계산하게 됩니다.&lt;/p&gt;
&lt;h1&gt;3. &lt;code&gt;mapGetters&lt;/code&gt;&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;mapGetters&lt;/code&gt;는 store의 getter를 &lt;code&gt;computed&lt;/code&gt; 속성에 매핑합니다.&lt;/p&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;import { mapGetters } from 'vuex'

export default {
  // ...
  computed: {
    // getter를 객체 전개 연산자(Object Spread Operator)로 계산하여 추가합니다.
    ...mapGetters([
      'doneTodosCount',
      'anotherGetter',
      // ...
    ])
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;mapGetters&lt;/code&gt; 사용방법은 이전 포스트에서 이야기한 &lt;code&gt;mapState&lt;/code&gt;와 사용방법이 동일합니다.&lt;/p&gt;
&lt;pre class=&quot;clean&quot;&gt;&lt;code&gt;...mapGetters({
  // this.doneCount를 store.getters.doneTodosCount에 매핑하십시오.
  doneCount: 'doneTodosCount'
})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;객체를 사용하면 getter를 다른 이름으로 매핑할 수 있습니다.&lt;/p&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://vuex.vuejs.org/kr/guide/getters.html&quot;&gt;https://vuex.vuejs.org/kr/guide/getters.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>getter</category>
      <category>mapGetters</category>
      <category>Vue.js</category>
      <category>vuex</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/85</guid>
      <comments>https://beomy.tistory.com/85#entry85comment</comments>
      <pubDate>Tue, 2 Jul 2019 23:06:21 +0900</pubDate>
    </item>
    <item>
      <title>[vuex] State</title>
      <link>https://beomy.tistory.com/84</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9mCAz/btqwoz6umFd/9QuvJeU9kRsQqMk8cp9BhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9mCAz/btqwoz6umFd/9QuvJeU9kRsQqMk8cp9BhK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9mCAz/btqwoz6umFd/9QuvJeU9kRsQqMk8cp9BhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9mCAz%2Fbtqwoz6umFd%2F9QuvJeU9kRsQqMk8cp9BhK%2Fimg.png&quot; width=&quot;100%&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;h1&gt;1. 단일 상태 트리 (Single State Tree)&lt;/h1&gt;
&lt;p&gt;Vuex는 단일 상태 트리(single state tree)를 사용합니다. 즉 이말은, 하나의 객체가 모든 어플리케이션의 state를 포함하고 이 객체가 하나의 원본 소스(single source of truth)입니다. 하나의 어플리케이션은 하나의 store만 가진다는 것을 의미합니다. 단일 상태 트리는 특정 state를 바로 찾을 수 있게 만들고, 현재 앱의 state의 스냅샷을 가져올 수 있어 디버깅을 간편하게 만듭니다.&lt;/p&gt;
&lt;h1&gt;2. Vuex의 state를 Vue 컴포넌트에 가져오기&lt;/h1&gt;
&lt;p&gt;Vuex의 store를 Vue 컴포넌트에서 사용할 수 있는 가장 간단한 방법은 &lt;a href=&quot;https://beomy.tistory.com/49&quot;&gt;computed&lt;/a&gt; 속성을 사용하는 것입니다.&lt;/p&gt;
&lt;pre class=&quot;axapta&quot;&gt;&lt;code&gt;// Counter 컴포넌트를 만듭니다
const Counter = {
  template: `&amp;lt;div&amp;gt;{{ count }}&amp;lt;/div&amp;gt;`,
  computed: {
    count () {
      return store.state.count
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;store.state.count&lt;/code&gt;가 변경 되면, &lt;code&gt;computed&lt;/code&gt; 속성이 다시 평가되고, DOM이 업데이트 됩니다. 그러나 이 패턴은 전역 store 싱글톤에 의존적이게 됩니다. 컴포넌트에서 사용하기 위해서는 store를 사용하는 모든 컴포넌트에서 store를 import해야 합니다.&lt;br /&gt;Vuex는 &lt;code&gt;store&lt;/code&gt; 옵션(&lt;code&gt;Vue.use(Vuex)&lt;/code&gt;에 인해 가능)으로 루트 컴포넌트의 모든 자식 컴포넌트에 store를 주입하는 메커니즘을 제공합니다.&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;const app = new Vue({
  el: '#app',
  // &quot;store&quot; 옵션을 사용하여 저장소를 제공하십시오.
  // 그러면 모든 하위 컴포넌트에 저장소 인스턴스가 삽입됩니다.
  store,
  components: { Counter },
  template: `
    &amp;lt;div class=&quot;app&quot;&amp;gt;
      &amp;lt;counter&amp;gt;&amp;lt;/counter&amp;gt;
    &amp;lt;/div&amp;gt;
  `
})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;루트 인스턴스에 &lt;code&gt;store&lt;/code&gt; 옵션을 제공함으로써, 루트 컴포넌트의 모든 자식 컴포넌트에서는 &lt;code&gt;this.$store&lt;/code&gt;를 사용할 수 있게 됩니다.&lt;/p&gt;
&lt;pre class=&quot;axapta&quot;&gt;&lt;code&gt;const Counter = {
  template: `&amp;lt;div&amp;gt;{{ count }}&amp;lt;/div&amp;gt;`,
  computed: {
    count () {
      return this.$store.state.count
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 코드와 같이 &lt;code&gt;store.state.count&lt;/code&gt;에서 &lt;code&gt;this.$store.state.count&lt;/code&gt;로 &lt;code&gt;store&lt;/code&gt;를 &lt;code&gt;this.$store&lt;/code&gt;로 대체 할 수 있게 됩니다.&lt;/p&gt;
&lt;h1&gt;3. &lt;code&gt;mapState&lt;/code&gt;&lt;/h1&gt;
&lt;p&gt;컴포넌트에서 여러개의 state나 getter를 사용해야 할 때가 있습니다. 그럴 때 모두 &lt;code&gt;computed&lt;/code&gt; 속성에 정의하면 코드가 장황해 질 수 있습니다. &lt;code&gt;mapState&lt;/code&gt;를 사용하면 이러한 문제를 해결 할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;pf&quot;&gt;&lt;code&gt;// 독립 실행 형 빌드에서 헬퍼가 Vuex.mapState로 노출됩니다.
import { mapState } from 'vuex'

export default {
  // ...
  computed: mapState({
    // 화살표 함수는 코드를 매우 간결하게 만들어 줍니다!
    count: state =&amp;gt; state.count,

    // 문자열 값 'count'를 전달하는 것은 `state =&amp;gt; state.count`와 같습니다.
    countAlias: 'count',

    // `this`를 사용하여 로컬 상태에 액세스하려면 일반적인 함수를 사용해야합니다
    countPlusLocalState (state) {
      return state.count + this.localCount
    }
  })
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;computed&lt;/code&gt; 속성에 매핑된 이름과 state의 하위 트리 이름이 같을 때 (예를 들면, &lt;code&gt;store.state.count&lt;/code&gt;를 &lt;code&gt;computed&lt;/code&gt; 속성의 &lt;code&gt;count&lt;/code&gt;에 매핑하려고 할 때 &lt;code&gt;count&lt;/code&gt; 이름이 동일합니다.) &lt;code&gt;mapState&lt;/code&gt;에 string 배열을 넘길 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;pf&quot;&gt;&lt;code&gt;computed: mapState([
  // this.count를 store.state.count에 매핑 합니다.
  'count'
])&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 코드와 같이 더욱 간단하게 코딩 할 수 있게 됩니다.&lt;/p&gt;
&lt;h3&gt;객체 전개 연산자&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;mapState&lt;/code&gt;는 객체를 반환합니다. &lt;code&gt;mapState&lt;/code&gt;의 반환값을 &lt;code&gt;computed&lt;/code&gt; 속성에 할당하기 때문에, 이제까지 이야기한 방법으로는 다른 &lt;code&gt;computed&lt;/code&gt; 속성을 사용할 수 없습니다. 다른 &lt;code&gt;computed&lt;/code&gt; 값들과 store의 값들을 &lt;code&gt;computed&lt;/code&gt;에 정의하여 함께 사용하고 싶을 때, 객체 &lt;a href=&quot;https://github.com/tc39/proposal-object-rest-spread&quot;&gt;전개 연산자 (Object Spread Operator)&lt;/a&gt;를 사용하면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;computed: {
  localComputed () { /* ... */ },
  // 이것을 객체 전개 연산자(Object Spread Operator)를 사용하여 외부 객체에 추가 하십시오.
  ...mapState({
    // ...
  })
}&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;4. 컴포넌트는 여전히 로컬 state를 가질 수 있습니다.&lt;/h1&gt;
&lt;p&gt;(여기서 state는 어플리케이션의 정보를 담고 있는 무언가..라는 의미로 사용하였습니다.)&lt;br /&gt;Vuex를 사용한다고 해서 모든 state를 Vuex의 store에 저장해야 하는 것은 아닙니다. state가 단일 컴포넌트에만 사용하는 경우나 다른 컴포넌트에서 사용하지 않는 state의 경우에는 로컬 state를 사용할 수 있습니다.&lt;/p&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://vuex.vuejs.org/kr/guide/state.html&quot;&gt;https://vuex.vuejs.org/kr/guide/state.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>mapState</category>
      <category>Object Spread Operator</category>
      <category>single state tree</category>
      <category>state</category>
      <category>Store</category>
      <category>this.$store</category>
      <category>Vue.js</category>
      <category>vuex</category>
      <category>객체 전개 연산자</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/84</guid>
      <comments>https://beomy.tistory.com/84#entry84comment</comments>
      <pubDate>Fri, 28 Jun 2019 17:52:03 +0900</pubDate>
    </item>
    <item>
      <title>[vuex] Vuex 시작하기</title>
      <link>https://beomy.tistory.com/83</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d8Yinq/btqwmR5WCGE/3UuORRpxvMPKL1DwBke5bk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d8Yinq/btqwmR5WCGE/3UuORRpxvMPKL1DwBke5bk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d8Yinq/btqwmR5WCGE/3UuORRpxvMPKL1DwBke5bk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd8Yinq%2FbtqwmR5WCGE%2F3UuORRpxvMPKL1DwBke5bk%2Fimg.png&quot; width=&quot;100%&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;모든 Vuex 어플리케이션의 중심에는 store가 있습니다. store는 어플리케이션의 상태를 저장하는 컨테이너입니다. Vuex의 store와 전역객체와 두가지 차이점이 있습니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Vuex store는 반응형입니다. Vuex store가 변경되면 컴포넌트는 변경된 store를 감지하여 화면을 업데이트합니다.&lt;/li&gt;
&lt;li&gt;store의 state를 직접 변경 할 수 없습니다. store의 상태를 변경할 수 있는 유일한 방법은 &lt;code&gt;commit&lt;/code&gt;을 통해 &lt;code&gt;mutations&lt;/code&gt;를 이용하는 방법입니다. 이렇게 하면 state 변화를 추척할 수 있고, 디버깅도 용이해 집니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;1. Vuex 예시&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;state&lt;/code&gt;와 &lt;code&gt;mutations&lt;/code&gt;를 사용한 간단한 Vuex 예제입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// 모듈 시스템(npm 혹은 yarn)을 사용하는 경우 Vue.use(Vuex)를 먼저 호출해야합니다.

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  }
})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 예제와 같이 Vuex를 설정하면, &lt;code&gt;store.state&lt;/code&gt;를 사용하여 &lt;code&gt;state&lt;/code&gt;객체에 접근 할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;store.commit(&amp;#39;increment&amp;#39;)

console.log(store.state.count) // -&amp;gt; 1&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;store.commit&lt;/code&gt;을 이용하여 &lt;code&gt;mutations&lt;/code&gt;의 메소드에 접근 할 수 있고, 이 메소드를 이용하여 &lt;code&gt;state&lt;/code&gt;를 변경 할 수 있습니다.&lt;br&gt;&lt;code&gt;store.state.count&lt;/code&gt;를 직접 변경하지 않고 &lt;code&gt;mutations&lt;/code&gt;를 통해 변경하는 이유는 &lt;code&gt;state&lt;/code&gt; 변화를 추척하기 위해서 입니다. &lt;code&gt;state&lt;/code&gt; 변화를 추척할 수 있기 때문에 디버깅이 편해집니다.&lt;br&gt;컴포넌트 안에서 store의 state를 사용한다는 말은, 쉽게 말해서 컴포넌트의 &lt;code&gt;computed&lt;/code&gt; 속성에 store의 state를 리턴하는 함수를 정의한다는 말입니다.&lt;br&gt;&lt;a href=&quot;https://jsfiddle.net/n9jmu5v7/1269/&quot;&gt;Vuex 카운터 앱&lt;/a&gt;에서 코드를 확인 할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;2. Vuex 어플리케이션 구조&lt;/h1&gt;
&lt;p&gt;이제는 Vuex를 사용하는 어플리케이션의 구조에 대해 이야기 할 것입니다. 이번 내용은 Vuex의 모듈 내용을 아주 약간 포함하고 있지만, 그냥 그런것이 있구나 정도로 이해하고 넘어가셔도 무방할 것 같습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;├── index.html
├── main.js
├── api
│   └── ... # API 요청을 위한 추상화를 포함합니다.
├── components
│   ├── App.vue
│   └── ...
└── store
    ├── index.js          # 모듈을 조합하고 저장소를 내보내는 곳 입니다.
    ├── actions.js        # 루트 액션
    ├── mutations.js      # 루트 변이
    └── modules
        ├── cart.js       # cart 모듈
        └── products.js   # products 모듈&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 프로젝트 구조는 단순한 예제로 Vuex는 코드 구조를 제한하지 않습니다. 다만 3가지 원칙은 반드시 지켜야 합니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Application-level &lt;code&gt;state&lt;/code&gt;는 중앙 집중식 store입니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;state&lt;/code&gt;를 변경하는 유일한 방법은 &lt;code&gt;commit&lt;/code&gt;을 통한 &lt;code&gt;mutations&lt;/code&gt;를 이용하는 것입니다.&lt;/li&gt;
&lt;li&gt;비동기 로직은 캡슐화 되어야 하며, &lt;code&gt;actions&lt;/code&gt;에서 비동기 로직이 사용되어야 합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;위의 3가지 원칙만 지킨다면 프로젝트 구조화 하는 것은 사용자에게 달려 있습니다.&lt;/p&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://vuex.vuejs.org/kr/guide/&quot;&gt;https://vuex.vuejs.org/kr/guide/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://vuex.vuejs.org/kr/guide/structure.html&quot;&gt;https://vuex.vuejs.org/kr/guide/structure.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>Vue.js</category>
      <category>vuex</category>
      <category>Vuex 구조</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/83</guid>
      <comments>https://beomy.tistory.com/83#entry83comment</comments>
      <pubDate>Wed, 26 Jun 2019 15:48:07 +0900</pubDate>
    </item>
    <item>
      <title>[vuex] Vuex란</title>
      <link>https://beomy.tistory.com/82</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/by0K6e/btqwmCOhHLM/4cPBpTDbvB8B05cKllFEg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/by0K6e/btqwmCOhHLM/4cPBpTDbvB8B05cKllFEg1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/by0K6e/btqwmCOhHLM/4cPBpTDbvB8B05cKllFEg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fby0K6e%2FbtqwmCOhHLM%2F4cPBpTDbvB8B05cKllFEg1%2Fimg.png&quot; width=&quot;100%&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;Vuex는 Vue.js 어플리케이션을 위한 상태 관리 패턴 + 라이브러리 입니다. 어플리케이션의 모든 컴포넌트를 위한 중앙 집중식 저장소 역할을 합니다. 상태 변화를 예측하기 쉽도록 도와 줍니다 Vue에서 &lt;a href=&quot;https://github.com/vuejs/vue-devtools&quot;&gt;devtools 확장 프로그램&lt;/a&gt;을 지원하여 디버깅이 매우 편합니다.&lt;/p&gt;
&lt;h1&gt;1. 상태 관리 패턴&lt;/h1&gt;
&lt;p&gt;Vue.js로 작성한 간단한 카운터 앱을 예제로 이야기 하도록 하겠습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;new Vue({
  // 상태
  data () {
    return {
      count: 0
    }
  },
  // 뷰
  template: `
    &amp;lt;div&amp;gt;{{ count }}&amp;lt;/div&amp;gt;
  `,
  // 액션
  methods: {
    increment () {
      this.count++
    }
  }
})&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;상태: 앱을 작동하는 진실의 근원(source of truch)입니다.&lt;/li&gt;
&lt;li&gt;뷰: 상태의 선언적 매핑입니다.&lt;/li&gt;
&lt;li&gt;액션: 뷰에서 사용자 입력에 따라 반응적으로 상태를 바꾸는 방법입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;사용하는 단어가 어려워서 그렇지 간단히 이야기 하면, 상태는 화면을 나타내는 데이터의 근원이고, 뷰는 상태를 화면에 그리는 역할을 하고, 액션은 상태를 바꾸는 역할을 합니다. 상태 관리 패턴이랑, 위의 3가지 중 상태를 관리하는 패턴을 말합니다.&lt;/p&gt;
&lt;h1&gt;2. 단반향 데이터 흐름&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;50%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wycs2/btqwixOT9aW/Uq2LpK4oJp5XDZmjC56NoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wycs2/btqwixOT9aW/Uq2LpK4oJp5XDZmjC56NoK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wycs2/btqwixOT9aW/Uq2LpK4oJp5XDZmjC56NoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fwycs2%2FbtqwixOT9aW%2FUq2LpK4oJp5XDZmjC56NoK%2Fimg.png&quot; width=&quot;50%&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;h2&gt;- 단방향 데이터 흐름의 문제점&lt;/h2&gt;
&lt;p&gt;같은 상태를 여러 컴포넌트가 공유해야 할 경우 크게 두가지를 신경써야 합니다.&lt;/p&gt;
&lt;h4&gt;여러 뷰가 같은 상태에 의존할 때&lt;/h4&gt;
&lt;p&gt;간단히 말하면, 여러개의 뷰가 하나의 상태에 따라 화면이 그려질 때입니다. 여러개가 중첩된 컴포넌트에 데이터를 흘려 보내줘야 할 경우, 컴포넌트의 &lt;code&gt;props&lt;/code&gt;가 장황해 질 수 있습니다. 또한 형제 컴포넌트에는 &lt;code&gt;props&lt;/code&gt;로 데이터를 넘겨줄 수 없기 때문에 형제 컴포넌트에서는 동작하지 않는 문제점이 있습니다.&lt;/p&gt;
&lt;h4&gt;서로 다른 뷰의 작업이 동일한 상태에 반영해야 할 때&lt;/h4&gt;
&lt;p&gt;여러개의 뷰가 하나의 상태를 변경해야 할 경우를 말합니다. 직접 부모/자식 인스턴스를 참조하거나 이벤트를 통해 상태의 복사본을 만들어 변경하고 동기화 하는 해결 방법을 사용해야 합니다.&lt;/p&gt;
&lt;h1&gt;3. Vuex의 필요성&lt;/h1&gt;
&lt;p&gt;위의 2가지 문제는 유지보수가 어려운 코드를 만들어 냅니다. 이 문제를 해결하기 위해서는 컴포넌트에서 공유해야 하는 상태를 추출하고 이를 전역 싱글톤으로 관리해야 합니다. 이 방법을 통해서 컴포넌트 트리는 단순히 커다란 뷰가 되고, 모든 컴포넌트는 트리에 상관없이 상태에 엑세스 하거나 상태를 변경할 수 있게 하는 동작을 트리거 할 수 있게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;50%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1zqjw/btqwi1CcnWq/ttCERuXKbzuqJBSkobdEWK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1zqjw/btqwi1CcnWq/ttCERuXKbzuqJBSkobdEWK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1zqjw/btqwi1CcnWq/ttCERuXKbzuqJBSkobdEWK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1zqjw%2Fbtqwi1CcnWq%2FttCERuXKbzuqJBSkobdEWK%2Fimg.png&quot; width=&quot;50%&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;a href=&quot;https://facebook.github.io/flux/docs/overview.html&quot;&gt;Flux&lt;/a&gt;, &lt;a href=&quot;https://redux.js.org/&quot;&gt;Redux&lt;/a&gt;, &lt;a href=&quot;https://guide.elm-lang.org/architecture/&quot;&gt;The Elm Architecture&lt;/a&gt;에서 영감을 받은 Vuex의 기본 아이디어입니다.&lt;/p&gt;
&lt;h1&gt;4. Vuex 구조&lt;/h1&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;90%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bn4nFN/btqwl5Dpygf/HgaY2FOntDkRQGnlr5e4E0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bn4nFN/btqwl5Dpygf/HgaY2FOntDkRQGnlr5e4E0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bn4nFN/btqwl5Dpygf/HgaY2FOntDkRQGnlr5e4E0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbn4nFN%2Fbtqwl5Dpygf%2FHgaY2FOntDkRQGnlr5e4E0%2Fimg.png&quot; width=&quot;90%&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;ol&gt;
&lt;li&gt;컴포넌트는 &lt;code&gt;Dispatch&lt;/code&gt;를 통해 &lt;code&gt;Actions&lt;/code&gt;을 접근합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Action&lt;/code&gt;에서는 보통 Backend API를 호출하여 값을 가져온 후에, &lt;code&gt;Commit&lt;/code&gt;을 통해 &lt;code&gt;Mutations&lt;/code&gt;에 접근합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Mutations&lt;/code&gt;에서는 크롬 확장 프로그램인 Devtools와 통신을 하여 디버깅이 쉽도록 기능을 제공하는 역할을 할 뿐 이니라, &lt;code&gt;Mutate&lt;/code&gt; 를 통해 &lt;code&gt;State&lt;/code&gt;를 변경 합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;State&lt;/code&gt;가 변경되면 컴포넌트는 다시 렌더링 되어 화면에 나타냅니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;위의 그림은 Vuex의 구조를 간략히 나타내는 큰 그림입니다. 일반적으로 위의 4가지의 순서로 Vuex가 동작합니다.&lt;/p&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://vuex.vuejs.org/&quot;&gt;https://vuex.vuejs.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://beomy.tistory.com/34&quot;&gt;https://beomy.tistory.com/34&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>Vue.js</category>
      <category>vuex</category>
      <category>Vuex 구조</category>
      <category>단방향 데이터 흐름</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/82</guid>
      <comments>https://beomy.tistory.com/82#entry82comment</comments>
      <pubDate>Wed, 26 Jun 2019 01:17:21 +0900</pubDate>
    </item>
    <item>
      <title>[vuex] Vuex 설치하기</title>
      <link>https://beomy.tistory.com/81</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5roXJ/btqwhhrakuu/kezq0cKEFCyM3q9uNfK8X0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5roXJ/btqwhhrakuu/kezq0cKEFCyM3q9uNfK8X0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5roXJ/btqwhhrakuu/kezq0cKEFCyM3q9uNfK8X0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5roXJ%2Fbtqwhhrakuu%2Fkezq0cKEFCyM3q9uNfK8X0%2Fimg.png&quot; width=&quot;100%&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;h1&gt;1. 직접 다운로드 / CDN&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://unpkg.com/vuex&quot;&gt;https://unpkg.com/vuex&lt;/a&gt;&lt;br&gt;위의 링크는 NPM 기반의 CDN 링크를 제공합니다. 항상 최신 NPM 릴리즈 버전을 가르킵니다. &lt;code&gt;https://unpkg.com/vuex@2.0.0&lt;/code&gt;과 같이 특정 버전/태그를 사용할 수 있습니다.&lt;br&gt;&lt;code&gt;vue&lt;/code&gt; 선언 뒤에 &lt;code&gt;vuex&lt;/code&gt;를 추가하면 자동으로 설치됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;script src=&amp;quot;/path/to/vue.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&amp;quot;/path/to/vuex.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;2. NPM / Yarn&lt;/h1&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install vuex --save # NPM
yarn add vuex # Yarn&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 코드와 같이 npm과 yarn을 사용하여 설치할 수 있습니다. 위의 방법으로 설치하기 위해서는 &lt;code&gt;Vue.use()&lt;/code&gt;를 사용해야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;import Vue from &amp;#39;vue&amp;#39;
import Vuex from &amp;#39;vuex&amp;#39;

Vue.use(Vuex)&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;3. 주의 사항&lt;/h1&gt;
&lt;p&gt;Vuex는 사용하기 위해서는 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises&quot;&gt;Promise&lt;/a&gt; 기능이 필요합니다. 만약 대상 브라우저가 &lt;code&gt;Promise&lt;/code&gt;를 지원하지 않는다면, &lt;a href=&quot;https://github.com/stefanpenner/es6-promise&quot;&gt;es6-promise&lt;/a&gt; 등을 사용하여 &lt;code&gt;Promise&lt;/code&gt;를 polyfill 해야 합니다.&lt;/p&gt;
&lt;h3&gt;CDN으로 추가&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;script src=&amp;quot;https://cdn.jsdelivr.net/npm/es6-promise@4/dist/es6-promise.auto.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 코드와 같이 &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;를 선언하면 &lt;code&gt;window.Promise&lt;/code&gt;를 사용할 수 있습니다.&lt;/p&gt;
&lt;h3&gt;NPM / Yarn으로 추가&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install es6-promise --save # NPM
yarn add es6-promise # Yarn&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 코드를 사용하여 패키지를 설치 한 후에,&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;import &amp;#39;es6-promise/auto&amp;#39;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vuex를 사용하기 전에 위의 코드를 아무 곳에나 추가하면 됩니다.&lt;/p&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://vuex.vuejs.org/kr/installation.html&quot;&gt;https://vuex.vuejs.org/kr/installation.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>es6-promise</category>
      <category>Vue.js</category>
      <category>vuex</category>
      <category>vuex 설치</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/81</guid>
      <comments>https://beomy.tistory.com/81#entry81comment</comments>
      <pubDate>Sun, 23 Jun 2019 23:45:27 +0900</pubDate>
    </item>
    <item>
      <title>[vue-router] Lazy Loading Routes</title>
      <link>https://beomy.tistory.com/80</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oO4co/btqv8yNW0zT/fB1XrdhWEEieJ0bbBrbMq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oO4co/btqv8yNW0zT/fB1XrdhWEEieJ0bbBrbMq1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oO4co/btqv8yNW0zT/fB1XrdhWEEieJ0bbBrbMq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoO4co%2Fbtqv8yNW0zT%2FfB1XrdhWEEieJ0bbBrbMq1%2Fimg.png&quot; width=&quot;100%&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;번들러를 사용하여 앱을 빌드할 때, JavaScript 번들이 커지게 되면 페이지 로드하는데 걸리는 시간이 길어지게 됩니다. 각각 라우트 컴포넌트 단위로 나누고 그 라우트에 방문하게 될 때 파일을 로드하는 것이 더 효율적입니다.&lt;/p&gt;
&lt;h1&gt;1. Vue의 비동기 컴포넌트 + Webpack의 코드 분할&lt;/h1&gt;
&lt;p&gt;Vue의 비동기 컴포넌트(&lt;a href=&quot;https://beomy.tistory.com/59&quot;&gt;[Vue.JS] 컴포넌트 (고급:Dynamic &amp;amp; Async Components)&lt;/a&gt; 참고)와 Webpack의 코드 분할(&lt;a href=&quot;https://webpack.js.org/guides/code-splitting/&quot;&gt;Code Splitting&lt;/a&gt; 참고)을 결합하여 Lazy Loading Route를 쉽게 구현 할 수 있습니다.&lt;/p&gt;
&lt;h4&gt;Vue의 비동기 컴포넌트&lt;/h4&gt;
&lt;p&gt;비동기 컴포넌트는 &lt;code&gt;Promise&lt;/code&gt;를 반환하는 팩터리 함수를 이용하여 만들 수 있씁니다. 컴포넌트 정의는 &lt;code&gt;resolve&lt;/code&gt; 함수의 인자로 전달 되어야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const Foo = () =&amp;gt; Promise.resolve({ /* 컴포넌트 정의 */ })&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Webpack의 코드 분할&lt;/h4&gt;
&lt;p&gt;Webpack 2의 &lt;a href=&quot;https://github.com/tc39/proposal-dynamic-import&quot;&gt;Dynamic Import&lt;/a&gt;를 사용하여 코드를 분할 할 수 있습니다. (Lazy Loading Route는 비동기 컴포넌트를 사용하기 때문에 &lt;code&gt;Promise&lt;/code&gt;를 반환하는 Dynamic Import를 사용해야 합니다.)&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;import(&amp;#39;./Foo.vue&amp;#39;) // returns a Promise&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 문법을 사용하기 위해서는, Babel을 사용하고 있는 경우에는 &lt;a href=&quot;https://babeljs.io/docs/en/babel-plugin-syntax-dynamic-import/&quot;&gt;syntax-dynamic-import&lt;/a&gt; 플러그인을 추가해야 올바른 구문 분석을 할 수 있습니다.&lt;/p&gt;
&lt;h4&gt;Vue의 비동기 컴포넌트 + Webpack의 코드 분할&lt;/h4&gt;
&lt;p&gt;위의 두가지 방법을 결합하여 Webpack에 의해 자동으로 코드를 분할하는 비동기 컴포넌트를 정의 할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const Foo = () =&amp;gt; import(&amp;#39;./Foo.vue&amp;#39;)

const router = new VueRouter({
  routes: [
    { path: &amp;#39;/foo&amp;#39;, component: Foo }
  ]
})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;라우트 설정은 아무것도 바꿀 필요가 없습니다. 위에서 이야기 한 방법으로 Lazy loading Route를 구현 할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;2. 같은 Chunk로 컴포넌트 묶기&lt;/h1&gt;
&lt;p&gt;동일한 라우트에 사용되는 모든 컴포넌트를 하나의 비동기 Chunk로 묶을 수 있습니다. 이를 위해 특별한 주석 문법을 사용하고, Chunk 이름만 주석에 제공해 주면 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const Foo = () =&amp;gt; import(/* webpackChunkName: &amp;quot;group-foo&amp;quot; */ &amp;#39;./Foo.vue&amp;#39;)
const Bar = () =&amp;gt; import(/* webpackChunkName: &amp;quot;group-foo&amp;quot; */ &amp;#39;./Bar.vue&amp;#39;)
const Baz = () =&amp;gt; import(/* webpackChunkName: &amp;quot;group-foo&amp;quot; */ &amp;#39;./Baz.vue&amp;#39;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이렇게 하면, Webpack은 동일한 Chunk 이름을 가지는 비동기 모듈을 동일한 Chunk로 그룹화 합니다.&lt;/p&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://router.vuejs.org/guide/advanced/lazy-loading.html&quot;&gt;https://router.vuejs.org/guide/advanced/lazy-loading.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>Async Component</category>
      <category>chunk</category>
      <category>Dynamic Import</category>
      <category>Lazy Loading Route</category>
      <category>syntax-dynamic-import</category>
      <category>vue-router</category>
      <category>Vue.js</category>
      <category>비동기 컴포넌트</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/80</guid>
      <comments>https://beomy.tistory.com/80#entry80comment</comments>
      <pubDate>Wed, 19 Jun 2019 00:24:23 +0900</pubDate>
    </item>
    <item>
      <title>[vue-router] Scroll Behavior</title>
      <link>https://beomy.tistory.com/79</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KZGha/btqv8mlMBsG/0iXtGVVxVHpZXnQdJuAky0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KZGha/btqv8mlMBsG/0iXtGVVxVHpZXnQdJuAky0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KZGha/btqv8mlMBsG/0iXtGVVxVHpZXnQdJuAky0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKZGha%2Fbtqv8mlMBsG%2F0iXtGVVxVHpZXnQdJuAky0%2Fimg.png&quot; width=&quot;100%&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;client-side 라우팅을 할 때, 새로운 라우트로 이동 할 때 스크롤을 맨 위로 위치하게 하거나, 페이지를 다시 로드 했을 때 스크롤 위치를 유지하게 할 수 있습니다. 심지어 스크롤 위치를 원하는데로 커스터 마이징 할 수도 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;이 기능은 브라우저가 &lt;code&gt;history.pushState&lt;/code&gt;를 제공 해야 동작합니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h1&gt;1. &lt;code&gt;scrollBehavior&lt;/code&gt; 함수&lt;/h1&gt;
&lt;p&gt;vue-router 인스턴스를 생성할 때 &lt;code&gt;scrollBehavior&lt;/code&gt; 함수를 정의하면 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const router = new VueRouter({
  routes: [...],
  scrollBehavior (to, from, savedPosition) {
    // return desired position
  }
})&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;전달 인자&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;scrollBehavior&lt;/code&gt; 함수는 &lt;code&gt;to&lt;/code&gt;와 &lt;code&gt;from&lt;/code&gt;, &lt;code&gt;savedPosition&lt;/code&gt;을 전달인자로 받습니다. &lt;code&gt;savedPosition&lt;/code&gt;은 브라우저의 앞으로 가기 혹은 뒤로 가기 동작인 &lt;code&gt;popstate&lt;/code&gt; 네비게이션에만 사용할 수 있습니다.&lt;/p&gt;
&lt;h4&gt;리턴값&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;scrollBehavior&lt;/code&gt; 함수는, &lt;code&gt;{ x: number, y: number }&lt;/code&gt; 또는 &lt;code&gt;{ selector: string, offset? : { x: number, y: number }}&lt;/code&gt; 두가지의 객체를 리턴해야 합니다. falsy 값(거짓으로 판단되는 값들.. 0, 공백 등..) 혹은 빈 객체를 리턴할 경우 스크롤이 일어나지 않습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;scrollBehavior (to, from, savedPosition) {
  return { x: 0, y: 0 }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 예제는 네이게이션이 될 때마다 스크롤이 최상단으로 이동하는 예제입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;scrollBehavior (to, from, savedPosition) {
  if (savedPosition) {
    return savedPosition
  } else {
    return { x: 0, y: 0 }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;앞으로 가기/뒤로 가기 버튼을 눌렀을 때, 위의 코드와 같이 &lt;code&gt;savedPosition&lt;/code&gt;을 리턴 할 경우 native와 유사한 동작을 하게 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;scrollBehavior (to, from, savedPosition) {
  if (to.hash) {
    return {
      selector: to.hash
      // , offset: { x: 0, y: 10 }
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위크 코드와 같이 라우트 메타 필드를 이용하면 좀더 세분화 된 스크롤 동작을 조작할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;2. Async Scrolling&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;2.8.0에 새롭게 추가된 기능입니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;scrollBehavior&lt;/code&gt; 함수가 &lt;code&gt;Promise&lt;/code&gt;를 리턴할 수도 있습니다. 스크롤 되기 원하는 위치를 &lt;code&gt;resolve&lt;/code&gt; 함수의 인자값으로 전달 하면 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;scrollBehavior (to, from, savedPosition) {
  return new Promise((resolve, reject) =&amp;gt; {
    setTimeout(() =&amp;gt; {
      resolve({ x: 0, y: 0 })
    }, 500)
  })
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;좀 더 다양한 구현이 가능하여, 페이지가 전환될 때 스크롤 동작을 좀 더 멋있게 나타낼 수 있습니다.&lt;/p&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://router.vuejs.org/guide/advanced/scroll-behavior.html&quot;&gt;https://router.vuejs.org/guide/advanced/scroll-behavior.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>async scrolling</category>
      <category>scrollBehavior</category>
      <category>vue-router</category>
      <category>Vue.js</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/79</guid>
      <comments>https://beomy.tistory.com/79#entry79comment</comments>
      <pubDate>Mon, 17 Jun 2019 23:51:27 +0900</pubDate>
    </item>
    <item>
      <title>[vue-router] Data Fetching</title>
      <link>https://beomy.tistory.com/78</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/liYp6/btqv8lUE1L6/YchwpoZnWXsto2Ozo6ckY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/liYp6/btqv8lUE1L6/YchwpoZnWXsto2Ozo6ckY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/liYp6/btqv8lUE1L6/YchwpoZnWXsto2Ozo6ckY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FliYp6%2Fbtqv8lUE1L6%2FYchwpoZnWXsto2Ozo6ckY1%2Fimg.png&quot; width=&quot;100%&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가지 방법에 대해 이야기 할 것입니다.&lt;/p&gt;
&lt;p&gt;첫번째 방법은, 네비게이션이 된 후 데이터를 가져오는 방법 입니다.&lt;br&gt;네비게이션을 먼저 동작하고, 활성화 된 라우트 컴포넌트의 라이프 사이클 훅에서 데이터를 가져오는 방법입니다. 데이터를 가져오는 동안에 로딩 상태를 표시합니다.&lt;/p&gt;
&lt;p&gt;두번째 방법은, 네비게이션이 되기 전 데이터를 가져오는 방법입니다.&lt;br&gt;컴포넌트 내부 가드를 사용하여 네비게이션 되기 전에 데이터를 가져오는 방법입니다. 데이터를 가져오는 것이 끝나면 네비게이션이 동작합니다.&lt;/p&gt;
&lt;h1&gt;1. 네비게이션이 된 후 데이터 가져오기&lt;/h1&gt;
&lt;p&gt;이 방법은 즉시 네비게이션이 된 후, 컴포넌트가 렌더링 되어 컴포넌트의 라이프 사이클 훅인, &lt;code&gt;created&lt;/code&gt; 훅에서 데이터를 가져오는 방식입니다. 이 방법은 네트워크를 통해 데이터를 가져오는 동안 로딩 상태를 나타낼 수 있는 기회를 제공합니다. 또한 각각의 view에서 다른 로딩 처리를 할 수도 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$route.params.id&lt;/code&gt;를 사용하여 데이터를 가져오는 &lt;code&gt;Post&lt;/code&gt; 컴포넌트 예제를 만들어 보겠습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;template&amp;gt;
  &amp;lt;div class=&amp;quot;post&amp;quot;&amp;gt;
    &amp;lt;div v-if=&amp;quot;loading&amp;quot; class=&amp;quot;loading&amp;quot;&amp;gt;
      Loading...
    &amp;lt;/div&amp;gt;

    &amp;lt;div v-if=&amp;quot;error&amp;quot; class=&amp;quot;error&amp;quot;&amp;gt;
      {{ error }}
    &amp;lt;/div&amp;gt;

    &amp;lt;div v-if=&amp;quot;post&amp;quot; class=&amp;quot;content&amp;quot;&amp;gt;
      &amp;lt;h2&amp;gt;{{ post.title }}&amp;lt;/h2&amp;gt;
      &amp;lt;p&amp;gt;{{ post.body }}&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;export default {
  data () {
    return {
      loading: false,
      post: null,
      error: null
    }
  },
  created () {
    // fetch the data when the view is created and the data is
    // already being observed
    this.fetchData()
  },
  watch: {
    // call again the method if the route changes
    &amp;#39;$route&amp;#39;: &amp;#39;fetchData&amp;#39;
  },
  methods: {
    fetchData () {
      this.error = this.post = null
      this.loading = true
      // replace `getPost` with your data fetching util / API wrapper
      getPost(this.$route.params.id, (err, post) =&amp;gt; {
        this.loading = false
        if (err) {
          this.error = err.toString()
        } else {
          this.post = post
        }
      })
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 예제는 컴포넌트가 렌더링 되어, &lt;code&gt;created&lt;/code&gt; 훅에서 &lt;code&gt;$route.params.id&lt;/code&gt;를 이용하여 데이터를 가져오는 예제입니다. &lt;code&gt;watch&lt;/code&gt; 옵션에 &lt;code&gt;$route&lt;/code&gt;를 등록하여, 라우트가 변경 되었을 때 다시 데이터를 가져올 수 있도록 만들었습니다. 데이터를 가져오는 동안에는 &lt;code&gt;this.loading&lt;/code&gt;이 &lt;code&gt;true&lt;/code&gt;가 되고, 데이터 가져오는 것이 완료되면 &lt;code&gt;false&lt;/code&gt;가 됩니다.&lt;/p&gt;
&lt;h1&gt;2. 네이게이션이 되기 전에 데이터 가져오기&lt;/h1&gt;
&lt;p&gt;이 방법은 새로운 라우트가 활성화 되기 전에 데이터를 가져오는 방법입니다. 컴포넌트 내부 가드인 &lt;code&gt;beforeRouteEnter&lt;/code&gt; 가드에서 데이터를 가져옵니다. 데이터 가져오기가 완료 된 후에는 &lt;code&gt;next&lt;/code&gt; 함수를 호출해야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;export default {
  data () {
    return {
      post: null,
      error: null
    }
  },
  beforeRouteEnter (to, from, next) {
    getPost(to.params.id, (err, post) =&amp;gt; {
      next(vm =&amp;gt; vm.setData(err, post))
    })
  },
  // when route changes and this component is already rendered,
  // the logic will be slightly different.
  beforeRouteUpdate (to, from, next) {
    this.post = null
    getPost(to.params.id, (err, post) =&amp;gt; {
      this.setData(err, post)
      next()
    })
  },
  methods: {
    setData (err, post) {
      if (err) {
        this.error = err.toString()
      } else {
        this.post = post
      }
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;데이터 가져오기가 완료 되어 &lt;code&gt;next&lt;/code&gt; 함수가 호출되기 전 까지는 이전 view가 유지 됩니다. 그렇기 때문에 데이터를 가져오는 동안에  프로그래스 바나 인디케이터 등, 로딩 상태를 화면에 나타내는 것이 좋습니다. 데이터 가져오기가 실패한 경우에도 실패 메시지를 화면에 출력하는 것이 좋습니다.&lt;/p&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://router.vuejs.org/guide/advanced/data-fetching.html#fetching-before-navigation&quot;&gt;https://router.vuejs.org/guide/advanced/data-fetching.html#fetching-before-navigation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>beforeRouteEnter</category>
      <category>data fetching</category>
      <category>vue-router</category>
      <category>Vue.js</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/78</guid>
      <comments>https://beomy.tistory.com/78#entry78comment</comments>
      <pubDate>Mon, 17 Jun 2019 22:23:07 +0900</pubDate>
    </item>
    <item>
      <title>[vue-router] 트랜지션</title>
      <link>https://beomy.tistory.com/77</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhG3Mo/btqv7qBQC2U/vABt6jUGL4WWEYEGQolG60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhG3Mo/btqv7qBQC2U/vABt6jUGL4WWEYEGQolG60/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhG3Mo/btqv7qBQC2U/vABt6jUGL4WWEYEGQolG60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhG3Mo%2Fbtqv7qBQC2U%2FvABt6jUGL4WWEYEGQolG60%2Fimg.png&quot; width=&quot;100%&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;code&gt;&amp;lt;route-view&amp;gt;&lt;/code&gt;는 사실 동적인 컴포넌트이기 때문에 &lt;code&gt;&amp;lt;transition&amp;gt;&lt;/code&gt; 컴포넌트를 사용하는 것과 동일한 방식으로 트랜지션 효과를 적용 할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;transition&amp;gt;
  &amp;lt;router-view&amp;gt;&amp;lt;/router-view&amp;gt;
&amp;lt;/transition&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;1. 라우트 별 트랜지션&lt;/h1&gt;
&lt;p&gt;위의 코드와 같이 작성하게 되면 모든 라우트에 동일한 트랜지션을 적용하게 됩니다. 각 라우트 컴포넌트가 서로 다른 트랜지션을 갖도록 하기 위해서는, 각각의 라우트 컴포넌트 내에서 다른 이름으로 &lt;code&gt;&amp;lt;transition&amp;gt;&lt;/code&gt;을 사용하면 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const Foo = {
  template: `
    &amp;lt;transition name=&amp;quot;slide&amp;quot;&amp;gt;
      &amp;lt;div class=&amp;quot;foo&amp;quot;&amp;gt;...&amp;lt;/div&amp;gt;
    &amp;lt;/transition&amp;gt;
  `
}

const Bar = {
  template: `
    &amp;lt;transition name=&amp;quot;fade&amp;quot;&amp;gt;
      &amp;lt;div class=&amp;quot;bar&amp;quot;&amp;gt;...&amp;lt;/div&amp;gt;
    &amp;lt;/transition&amp;gt;
  `
}&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;2. 라우트 기반 동적 트랜지션&lt;/h1&gt;
&lt;p&gt;이동할 라우트와 현재 라우트 간의 관계를 기반으로 동적으로 트랜지션을 결정할 수 있는 방법도 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;!-- 동적 트랜지션을 위한 name을 지정합니다. --&amp;gt;
&amp;lt;transition :name=&amp;quot;transitionName&amp;quot;&amp;gt;
  &amp;lt;router-view&amp;gt;&amp;lt;/router-view&amp;gt;
&amp;lt;/transition&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// 그런 다음 부모 구성 요소에서 `$route`를 보고 사용할 트랜지션을 결정합니다
watch: {
  &amp;#39;$route&amp;#39; (to, from) {
    const toDepth = to.path.split(&amp;#39;/&amp;#39;).length
    const fromDepth = from.path.split(&amp;#39;/&amp;#39;).length
    this.transitionName = toDepth &amp;lt; fromDepth ? &amp;#39;slide-right&amp;#39; : &amp;#39;slide-left&amp;#39;
  }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Vue.JS</category>
      <category>route transition</category>
      <category>transition</category>
      <category>vue-router</category>
      <category>Vue.js</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/77</guid>
      <comments>https://beomy.tistory.com/77#entry77comment</comments>
      <pubDate>Sun, 16 Jun 2019 23:15:44 +0900</pubDate>
    </item>
    <item>
      <title>[vue-router] 라우터 메타 필드</title>
      <link>https://beomy.tistory.com/76</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tLETQ/btqv7QF3YOq/dw3o56tNkvm7jytxTMO3sK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tLETQ/btqv7QF3YOq/dw3o56tNkvm7jytxTMO3sK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tLETQ/btqv7QF3YOq/dw3o56tNkvm7jytxTMO3sK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtLETQ%2Fbtqv7QF3YOq%2Fdw3o56tNkvm7jytxTMO3sK%2Fimg.png&quot; width=&quot;100%&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;라우터를 정의할 대 meta 필드를 포함할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const router = new VueRouter({
  routes: [
    {
      path: &amp;#39;/foo&amp;#39;,
      component: Foo,
      children: [
        {
          path: &amp;#39;bar&amp;#39;,
          component: Bar,
          // 메타 필드
          meta: { requiresAuth: true }
        }
      ]
    }
  ]
})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;routes&lt;/code&gt; 설정의 각각의 라우터 객체를 라우트 레코드라고 합니다. 라우트 레코드는 중첩 될 수 있기 때문에, 라우터가 일치하면 2개 이상의 라우트 레코드와 일치 할 수 있습니다.&lt;br&gt;예를 들어, 위의 코드와 같이 라우트를 설정하면, &lt;code&gt;/foo/bar&lt;/code&gt;라는 URL로 접속 하였을 때, 상위 레코드와 하위 레코드 2개가 일치하게 됩니다.&lt;br&gt;라우트와 일치하는 모든 라우트 레코드는 &lt;code&gt;$route.matched&lt;/code&gt; 배열에 저장됩니다.&lt;code&gt;$route.matched&lt;/code&gt; 배열을 뒤져서 라우트 레코드의 메타 필드에 접근 할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;router.beforeEach((to, from, next) =&amp;gt; {
  if (to.matched.some(record =&amp;gt; record.meta.requiresAuth)) {
    // 이 라우트는 인증이 필요하며 로그인 한 경우 확인하십시오.
    // 그렇지 않은 경우 로그인 페이지로 리디렉션하십시오.
    if (!auth.loggedIn()) {
      next({
        path: &amp;#39;/login&amp;#39;,
        query: { redirect: to.fullPath }
      })
    } else {
      next()
    }
  } else {
    next() // 반드시 next()를 호출하십시오!
  }
})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 코드와 같이 라우트에 매칭된 값이 저장되는 &lt;code&gt;to.matched&lt;/code&gt;(&lt;code&gt;to&lt;/code&gt;는 이동할 라우터 객체를 나타냅니다.) 배열 중 각각의 객체에 저장된 &lt;code&gt;meta&lt;/code&gt; 필드를 사용하여 라우트 설정의 메타 필드에 접근 할 수 있습니다.&lt;/p&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://router.vuejs.org/kr/guide/advanced/transitions.html&quot;&gt;https://router.vuejs.org/kr/guide/advanced/transitions.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>$route.matched</category>
      <category>meta</category>
      <category>route meta</category>
      <category>vue-router</category>
      <category>Vue.js</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/76</guid>
      <comments>https://beomy.tistory.com/76#entry76comment</comments>
      <pubDate>Sun, 16 Jun 2019 23:07:22 +0900</pubDate>
    </item>
    <item>
      <title>[vue-router] 네비게이션 가드</title>
      <link>https://beomy.tistory.com/75</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxMAPb/btqv5hrqR2p/QeDXLTFk7iXg2Iq8Z9fbl1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxMAPb/btqv5hrqR2p/QeDXLTFk7iXg2Iq8Z9fbl1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxMAPb/btqv5hrqR2p/QeDXLTFk7iXg2Iq8Z9fbl1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxMAPb%2Fbtqv5hrqR2p%2FQeDXLTFk7iXg2Iq8Z9fbl1%2Fimg.png&quot; width=&quot;100%&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;params나 query가 변경되어도 네비게이션 가드는 실행되지 않습니다. 변경되는 시점을 알고 싶다면, vue 옵션 중 &lt;code&gt;watch&lt;/code&gt; 옵션에 &lt;code&gt;$router&lt;/code&gt; 객체를 등록하거나 컴포넌트 가드인 &lt;code&gt;beforeRouteUpdate&lt;/code&gt;(2.2 버전에서 추가 됨)를 사용해야 합니다.&lt;/p&gt;
&lt;h1&gt;1. Global Before 가드&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;router.beforeEach&lt;/code&gt;를 사용하여 Gobal Before 가드를 사용할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const router = new VueRouter({ ... })

router.beforeEach((to, from, next) =&amp;gt; {
  // ...
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Gobal Before 가드는 모든 네비게이션의 생성된 순서에 따라 호출 됩니다. 이 가드는 비동기적으로 종료(resolved) 될 수 있습니다. 모든 훅들이 종료(resolved)되기 전까지 네비게이션은 중지된 상태가 됩니다.&lt;/p&gt;
&lt;h4&gt;Global Before 가드의 전달인자&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;to: Route&lt;/code&gt; : 이동할 타켓의 Route 객체입니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;from: Route&lt;/code&gt; : 현재의 Route 객체(네비게이션 이동하기 전의 Route 객체)입니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;next: Function&lt;/code&gt; : 이 함수를 호출해야 훅이 종료(resolved) 됩니다. 어떠한 동작을 할 것인지는 next 함수어 어떤 인자값이 전달되는지에 따라 결정됩니다.&lt;ul&gt;
&lt;li&gt;&lt;code&gt;next()&lt;/code&gt; : 다음 훅으로 이동됩니다. 다른 훅으로 이동할 수 있도록 승인을 하는 동작입니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;next(false)&lt;/code&gt; : 현재 네비게이션을 종료합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;next(&amp;#39;/&amp;#39;)&lt;/code&gt; 또는 &lt;code&gt;next({ path: &amp;#39;/&amp;#39; })&lt;/code&gt; : 인자로 전달된 경로로 이동합니다. 현재의 네비게이션은 중단되고 새로운 네비게이션이 시작됩니다. &lt;code&gt;next&lt;/code&gt; 함수에는 &lt;code&gt;replace: true&lt;/code&gt;나 &lt;code&gt;name: &amp;#39;home&amp;#39;&lt;/code&gt;, &lt;code&gt;&amp;lt;router-link&amp;gt;&lt;/code&gt;의 &lt;code&gt;to&lt;/code&gt;에 전달되어야 하는 prop나 &lt;code&gt;router.push&lt;/code&gt; 함수에 전달되어야 하는 값들 모두 인자값으로 사용 가능합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;next(error)&lt;/code&gt; : (2.4.0 이후) &lt;code&gt;next&lt;/code&gt; 함수에 전달된 값이 &lt;code&gt;Error&lt;/code&gt; 인스턴스이면, 네비게이션이 중지되고, 에러는 &lt;code&gt;router.onError()&lt;/code&gt;의 콜백에 전달됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;next&lt;/code&gt; 함수는 항상 호출되어야 합니다. 호출되지 않으면 훅은 종료되지 않습니다.&lt;/strong&gt;&lt;/p&gt;
&lt;h1&gt;2. Global Resolve 가드&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;router.beforeResolve&lt;/code&gt;를 사용하여 Global 가드를 등록할 수 있습니다. &lt;code&gt;router.beforeEach&lt;/code&gt;와 유사합니다. 차이점은 모든 in-component 가드와 비동기 라우터 컴포넌트가 종료 된 후, 네비게이션이 승인되기 바로 전에 호출된다는 것입니다.&lt;/p&gt;
&lt;h1&gt;3. Global After 훅&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;router.afterEach&lt;/code&gt;를 사용하여 Global 훅을 등록할 수 있습니다. 가드라고 이야기하지 않고 훅이라고 이야기 한 이유는 네비게이션을 보호하는 가드의 역할을 하는 &lt;code&gt;next&lt;/code&gt; 함수가 인자로 전달되지 않기 때문입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;router.afterEach((to, from) =&amp;gt; {
  // ...
})
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;4. Route 별 가드&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;beforeEnter&lt;/code&gt; 가드는 route 설정 객체에 직접 정의하여 사용합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const router = new VueRouter({
  routes: [
    {
      path: &amp;#39;/foo&amp;#39;,
      component: Foo,
      beforeEnter: (to, from, next) =&amp;gt; {
        // ...
      }
    }
  ]
})
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;5. 컴포넌트 내부 가드&lt;/h1&gt;
&lt;p&gt;라우트 컴포넌트 내부의 네비게이션 가드를 정의 할 수 있습니다. 컴포넌트 내부 가드는 &lt;code&gt;beforeRouteEnter&lt;/code&gt;, &lt;code&gt;beforeRouteUpdate&lt;/code&gt;, &lt;code&gt;beforeRouteLeave&lt;/code&gt; 3가지가 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const Foo = {
  template: `...`,
  beforeRouteEnter (to, from, next) {
    // called before the route that renders this component is confirmed.
    // does NOT have access to `this` component instance,
    // because it has not been created yet when this guard is called!
  },
  beforeRouteUpdate (to, from, next) {
    // called when the route that renders this component has changed,
    // but this component is reused in the new route.
    // For example, for a route with dynamic params `/foo/:id`, when we
    // navigate between `/foo/1` and `/foo/2`, the same `Foo` component instance
    // will be reused, and this hook will be called when that happens.
    // has access to `this` component instance.
  },
  beforeRouteLeave (to, from, next) {
    // called when the route that renders this component is about to
    // be navigated away from.
    // has access to `this` component instance.
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;code&gt;beforeRouteEnter&lt;/code&gt; 가드&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;beforeRouteEnter&lt;/code&gt; 가드는 네비게이션이 승인된기 전(새로 진입하는 컴포넌트가 생성되기 전)에 호출되기 때문에 &lt;code&gt;this&lt;/code&gt;를 사용할 수 없습니다. 하지만 &lt;code&gt;next&lt;/code&gt; 함수의 콜백은 네비게이션이 승인 된후 호출 되기 때문에 이 콜백을 사용하여 컴포넌트 인스턴스에 접근할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;beforeRouteEnter (to, from, next) {
  next(vm =&amp;gt; {
    // access to component instance via `vm`
  })
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;beforeRouteEnter&lt;/code&gt;에서만 &lt;code&gt;next&lt;/code&gt; 함수의 콜백함수를 지원합니다.&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;beforeRouteUpdate&lt;/code&gt; 가드&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;beforeRouteUpdate&lt;/code&gt; 가드에서는 &lt;code&gt;this&lt;/code&gt;를 사용할 수 있습니다. &lt;code&gt;next&lt;/code&gt;의 콜백함수를 지원할 필요가 없어 &lt;code&gt;next&lt;/code&gt;의 콜백함수를 지원하지 않습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;beforeRouteUpdate (to, from, next) {
  // just use `this`
  this.name = to.params.name
  next()
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;&lt;code&gt;beforeRouteLeave&lt;/code&gt; 가드&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;beforeRouteLeave&lt;/code&gt; 가드는 에디터가 값을 저장하지 않고 페이지를 벗어나는 것을 방지하는데 유용한 가드입니다. &lt;code&gt;next(false)&lt;/code&gt;를 호출하면 네비게이션이 중단됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;beforeRouteLeave (to, from, next) {
  const answer = window.confirm(&amp;#39;Do you really want to leave? you have unsaved changes!&amp;#39;)
  if (answer) {
    next()
  } else {
    next(false)
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;6. 전체 네비게이션 시나리오&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;네비게이션이 트리거 됨.&lt;/li&gt;
&lt;li&gt;비활성되 된 컴포넌트에서 Leave 가드가 호출 됨.&lt;/li&gt;
&lt;li&gt;전역 가드인 &lt;code&gt;beforeEach&lt;/code&gt; 가드가 호출 됨.&lt;/li&gt;
&lt;li&gt;재사용 된느 컴포넌트에서 &lt;code&gt;beforeRouteUpdate&lt;/code&gt; 가드가 호출 됨.&lt;/li&gt;
&lt;li&gt;route 설정에 정의 된 &lt;code&gt;beforeEnter&lt;/code&gt; 가드가 호출 됨.&lt;/li&gt;
&lt;li&gt;비동기 라우터 컴포넌트가 해결 됨.&lt;/li&gt;
&lt;li&gt;활성화 되는 컴포넌트의 &lt;code&gt;beforeRouteEnter&lt;/code&gt; 가드가 호출 됨.&lt;/li&gt;
&lt;li&gt;전역 가드인 &lt;code&gt;beforeResolve&lt;/code&gt; 가드가 호출 됨.&lt;/li&gt;
&lt;li&gt;네비게이션이 완료 됨.&lt;/li&gt;
&lt;li&gt;전역 훅인 &lt;code&gt;afterEach&lt;/code&gt; 훅이 호출 됨.&lt;/li&gt;
&lt;li&gt;DOM 업데이트가 트리거 됨.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;beforeRouteEnter&lt;/code&gt; 가드의 &lt;code&gt;next&lt;/code&gt; 함수에 전달된 콜백 함수가 호출 됨.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://router.vuejs.org/guide/advanced/navigation-guards.html#in-component-guards&quot;&gt;https://router.vuejs.org/guide/advanced/navigation-guards.html#in-component-guards&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>afterEach</category>
      <category>beforeEach</category>
      <category>beforeEnter</category>
      <category>beforeResolve</category>
      <category>beforeRouteEnter</category>
      <category>beforeRouteLeave</category>
      <category>beforeRouteUpdate</category>
      <category>navigation guard</category>
      <category>vue-router</category>
      <category>Vue.js</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/75</guid>
      <comments>https://beomy.tistory.com/75#entry75comment</comments>
      <pubDate>Sat, 15 Jun 2019 00:49:16 +0900</pubDate>
    </item>
    <item>
      <title>[vue-router] HTML5 히스토리 모드</title>
      <link>https://beomy.tistory.com/74</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/1cmQn/btqv3JAW7Dz/MqDO3Kp33TJWknf6j1fPJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1cmQn/btqv3JAW7Dz/MqDO3Kp33TJWknf6j1fPJK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1cmQn/btqv3JAW7Dz/MqDO3Kp33TJWknf6j1fPJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1cmQn%2Fbtqv3JAW7Dz%2FMqDO3Kp33TJWknf6j1fPJK%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;vue-router의 기본 설정은 hash 모드입니다. URL 해시를 사용하기 때문에 URL이 변경될 때 페이지가 다시 로드되지 않습니다.&lt;br /&gt;해시를 제거하기 위해 vue-router는 history 모드도 지원합니다. &lt;code&gt;history.pushState&lt;/code&gt; API를 사용하여 페이지를 다시 로드하지 않고 URL을 변경 할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;brush:js; gutter:false; vim&quot;&gt;&lt;code&gt;const router = new VueRouter({
  mode: 'history',
  routes: [...]
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 코드와 같이 history 모드로 설정 할 수 있습니다. history 모드를 사용할 때, 처음 경로 부터 쭉~ 사용한다면 문제없이 정상적으로 동작합니다. 하지만 중간 경로로 직접 접속하면 404 오류가 발생합니다. &lt;code&gt;http://서버주소/user/1&lt;/code&gt;와 같은 URL로 바로 접속하면 404 오류가 발생합니다. (hash 모드를 사용하면 이런 오류가 없긴 합니다.) 이런 문제를 해결하려면 서버에서 어떠한 경로로 접근하더라도 &lt;code&gt;index.html&lt;/code&gt; 페이지를 제공해야 합니다.&lt;/p&gt;
&lt;h1&gt;1. 서버 설정&lt;/h1&gt;
&lt;h4&gt;Apache&lt;/h4&gt;
&lt;pre class=&quot;brush:js; gutter:false; apache&quot;&gt;&lt;code&gt;
  RewriteEngine On
  RewriteBase /
  RewriteRule ^index\.html$ - [L]
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteRule . /index.html [L]

&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;nginx&lt;/h4&gt;
&lt;pre class=&quot;brush:js; gutter:false; nginx&quot;&gt;&lt;code&gt;location / {
  try_files $uri $uri/ /index.html;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Native Node.js&lt;/h4&gt;
&lt;pre class=&quot;brush:js; gutter:false; coffeescript&quot;&gt;&lt;code&gt;const http = require(&quot;http&quot;)
const fs = require(&quot;fs&quot;)
const httpPort = 80

http.createServer((req, res) =&amp;gt; {
  fs.readFile(&quot;index.htm&quot;, &quot;utf-8&quot;, (err, content) =&amp;gt; {
    if (err) {
      console.log('We cannot open &quot;index.htm&quot; file.')
    }

    res.writeHead(200, {
      &quot;Content-Type&quot;: &quot;text/html; charset=utf-8&quot;
    })

    res.end(content)
  })
}).listen(httpPort, () =&amp;gt; {
  console.log(&quot;Server listening on: http://localhost:%s&quot;, httpPort)
})
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;IIS&lt;/h4&gt;
&lt;pre class=&quot;brush:xml; gutter:false; dust&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;configuration&amp;gt;
  &amp;lt;system.webServer&amp;gt;
    &amp;lt;rewrite&amp;gt;
      &amp;lt;rules&amp;gt;
        &amp;lt;rule name=&quot;Handle History Mode and custom 404/500&quot; stopProcessing=&quot;true&quot;&amp;gt;
          &amp;lt;match url=&quot;(.*)&quot; /&amp;gt;
          &amp;lt;conditions logicalGrouping=&quot;MatchAll&quot;&amp;gt;
            &amp;lt;add input=&quot;{REQUEST_FILENAME}&quot; matchType=&quot;IsFile&quot; negate=&quot;true&quot; /&amp;gt;
            &amp;lt;add input=&quot;{REQUEST_FILENAME}&quot; matchType=&quot;IsDirectory&quot; negate=&quot;true&quot; /&amp;gt;
          &amp;lt;/conditions&amp;gt;
          &amp;lt;action type=&quot;Rewrite&quot; url=&quot;/&quot; /&amp;gt;
        &amp;lt;/rule&amp;gt;
      &amp;lt;/rules&amp;gt;
    &amp;lt;/rewrite&amp;gt;
  &amp;lt;/system.webServer&amp;gt;
&amp;lt;/configuration&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://www.iis.net/downloads/microsoft/url-rewrite&quot;&gt;IIS UrlRewrite&lt;/a&gt;를 설치한 후, 위의 코드와 같이 &lt;code&gt;web.config&lt;/code&gt;에 설정합니다.&lt;/p&gt;
&lt;h4&gt;Caddy&lt;/h4&gt;
&lt;pre class=&quot;brush:js; gutter:false; dts&quot;&gt;&lt;code&gt;rewrite {
    regexp .*
    to {path} /
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Firebase hosting&lt;/h4&gt;
&lt;pre class=&quot;brush:js; gutter:false; json&quot;&gt;&lt;code&gt;{
  &quot;hosting&quot;: {
    &quot;public&quot;: &quot;dist&quot;,
    &quot;rewrites&quot;: [
      {
        &quot;source&quot;: &quot;**&quot;,
        &quot;destination&quot;: &quot;/index.html&quot;
      }
    ]
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;firebase.json&lt;/code&gt;에 위의 코드와 같이 설정합니다.&lt;/p&gt;
&lt;h1&gt;2. 주의 사항&lt;/h1&gt;
&lt;p&gt;위의 이야기 했던 것과 같이 서버를 설정하였다면, 모든 경로가 &lt;code&gt;index.html&lt;/code&gt;를 제공하기 때문에 이제는 404 오류를 확인 할 수 없습니다.&lt;/p&gt;
&lt;pre class=&quot;brush:js; gutter:false; vim&quot;&gt;&lt;code&gt;const router = new VueRouter({
  mode: 'history',
  routes: [
    { path: '*', component: NotFoundComponent }
  ]
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 문제를 해결하기 위해서는 위의 코드와 같이 catch-all 라우터를 구현하여 404 페이지를 만들어야 합니다. 자세한 내용은 &lt;a href=&quot;https://beomy.tistory.com/68#catch-all-routing&quot;&gt;[vue-router] 동적 라우트 매칭&lt;/a&gt;을 참고 바랍니다.&lt;/p&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://router.vuejs.org/guide/essentials/history-mode.html&quot;&gt;https://router.vuejs.org/guide/essentials/history-mode.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>404 Error</category>
      <category>hash mode</category>
      <category>history mode</category>
      <category>vue-router</category>
      <category>Vue.js</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/74</guid>
      <comments>https://beomy.tistory.com/74#entry74comment</comments>
      <pubDate>Fri, 14 Jun 2019 00:04:23 +0900</pubDate>
    </item>
    <item>
      <title>[vue-router] 라우터 컴포넌트에 props 전달</title>
      <link>https://beomy.tistory.com/73</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/c7mdTb/btqv2IuJcJZ/nGlUhH6xYmRpeUb75XzXZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c7mdTb/btqv2IuJcJZ/nGlUhH6xYmRpeUb75XzXZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c7mdTb/btqv2IuJcJZ/nGlUhH6xYmRpeUb75XzXZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc7mdTb%2Fbtqv2IuJcJZ%2FnGlUhH6xYmRpeUb75XzXZK%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;code&gt;$route&lt;/code&gt;를 사용하게 되면, 컴포넌트가 URL에 의족적이게 되어 재사용성이 떨어지게 됩니다. 이 의존성을 제거하기 위해서는 &lt;code&gt;props&lt;/code&gt; 옵션을 사용하면 됩니다,&lt;/p&gt;
&lt;pre class=&quot;brush:js; gutter:false; crmsh&quot;&gt;&lt;code&gt;const User = {
  template: '&amp;lt;div&amp;gt;User {{ $route.params.id }}&amp;lt;/div&amp;gt;'
}
const router = new VueRouter({
  routes: [
    { path: '/user/:id', component: User }
  ]
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 코드는 &lt;code&gt;$route&lt;/code&gt;를 사용한 예제입니다. URL에 의존적이기 때문에 다른 URL를 가지는 컴포넌트에서 재사용하기 어렵습니다.&lt;/p&gt;
&lt;pre class=&quot;brush:js; gutter:false; yaml&quot;&gt;&lt;code&gt;const User = {
  props: ['id'],
  template: '&amp;lt;div&amp;gt;User {{ id }}&amp;lt;/div&amp;gt;'
}
const router = new VueRouter({
  routes: [
    { path: '/user/:id', component: User, props: true },

    // for routes with named views, you have to define the `props` option for each named view:
    {
      path: '/user/:id',
      components: { default: User, sidebar: Sidebar },
      props: { default: true, sidebar: false }
    }
  ]
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 코드는 &lt;code&gt;props&lt;/code&gt; 옵션을 사용한 예제입니다. URL대한 의존성이 제거되기 때문에 코드 재사용이나 테스트하기 쉽습니다.&lt;br /&gt;Named views를 사용할 때는 각각의 named view에 &lt;code&gt;props&lt;/code&gt; 옵션을 설정해 주어야 합니다.&lt;/p&gt;
&lt;h1&gt;1. Boolean mode&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;props&lt;/code&gt;에 &lt;code&gt;true&lt;/code&gt;를 설정하면, &lt;code&gt;route.params&lt;/code&gt;가 컴포넌트의 props에 설정됩니다.&lt;/p&gt;
&lt;h1&gt;2. Object mode&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;props&lt;/code&gt;가 object가 될 수 있습니다. &lt;code&gt;props&lt;/code&gt;에 설정된 값은 컴포넌트의 props가 됩니다.&lt;/p&gt;
&lt;pre class=&quot;brush:js; gutter:false; groovy&quot;&gt;&lt;code&gt;const router = new VueRouter({
  routes: [
    { path: '/promotion/from-newsletter', component: Promotion, props: { newsletterPopup: false } }
  ]
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 모드는 정적인 props를 설정할 때 유용합니다.&lt;/p&gt;
&lt;h1&gt;3. Function mode&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;props&lt;/code&gt;를 함수로 작성 할 수도 있습니다. &lt;code&gt;route&lt;/code&gt;의 값을 임의대로 컴포넌트의 props로 설정 할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;brush:js; gutter:false; groovy&quot;&gt;&lt;code&gt;const router = new VueRouter({
  routes: [
    { path: '/search', component: SearchUser, props: (route) =&amp;gt; ({ query: route.query.q }) }
  ]
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 코드를 예로 들어 이야기 하면, &lt;code&gt;/search?q=vue&lt;/code&gt; 라는 URL로 접속하게 되면, &lt;code&gt;SearchUser&lt;/code&gt; 컴포넌트의 props는 &lt;code&gt;{ query: 'vue' }&lt;/code&gt;가 됩니다.&lt;br /&gt;&lt;code&gt;props&lt;/code&gt;를 함수로 만들 때 유의 사항 중 한가지는 route가 변경되었을 때 호출 되는 함수이기 때문에 이 함수에서 state를 저장하면 안된다는 것입니다.&lt;/p&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://router.vuejs.org/guide/essentials/passing-props.html#object-mode&quot;&gt;https://router.vuejs.org/guide/essentials/passing-props.html#object-mode&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>props</category>
      <category>vue-router</category>
      <category>Vue.js</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/73</guid>
      <comments>https://beomy.tistory.com/73#entry73comment</comments>
      <pubDate>Thu, 13 Jun 2019 00:27:48 +0900</pubDate>
    </item>
    <item>
      <title>[vue-router] Redirect 와 Alias</title>
      <link>https://beomy.tistory.com/72</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/btls09/btqv3IVlCUx/oSNkvTxKnTkyOzIpfDUU9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btls09/btqv3IVlCUx/oSNkvTxKnTkyOzIpfDUU9K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btls09/btqv3IVlCUx/oSNkvTxKnTkyOzIpfDUU9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbtls09%2Fbtqv3IVlCUx%2FoSNkvTxKnTkyOzIpfDUU9K%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;h1&gt;1. Redirect&lt;/h1&gt;
&lt;p&gt;리다이렉션은 vue-router를 설정할 때 정의 할 수 있습니다. 예를 들어 &lt;code&gt;/a&lt;/code&gt;라는 URL로 접속할 때, &lt;code&gt;/b&lt;/code&gt;라는 URL로 리다이렉션을 시켜주고 싶다면,&lt;/p&gt;
&lt;pre class=&quot;brush:js; gutter:false; groovy&quot;&gt;&lt;code&gt;const router = new VueRouter({
  routes: [
    { path: '/a', redirect: '/b' }
  ]
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 코드와 같이 vue-router를 설정 할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;brush:js; gutter:false; groovy&quot;&gt;&lt;code&gt;const router = new VueRouter({
  routes: [
    { path: '/a', redirect: { name: 'foo' }}
  ]
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 코드와 같이 Named router를 사용할 수도 있습니다.&lt;/p&gt;
&lt;pre class=&quot;brush:js; gutter:false; javascript&quot;&gt;&lt;code&gt;const router = new VueRouter({
  routes: [
    { path: '/a', redirect: to =&amp;gt; {
      // 함수는 인수로 대상 라우트를 받습니다.
      // 여기서 path/location 반환합니다.
    }}
  ]
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;또한 동적으로 리다이렉션을 해주기 위해 함수를 사용할 수도 있습니다.&lt;/p&gt;
&lt;h1&gt;2. Alias&lt;/h1&gt;
&lt;p&gt;Redirect는 사용자가 &lt;code&gt;/a&lt;/code&gt;라는 URL에 방문 한다면, &lt;code&gt;/b&lt;/code&gt;라는 URL로 변경하여 &lt;code&gt;/b&lt;/code&gt; URL의 화면을 보여주는 것을 말합니다.&lt;br /&gt;Alias는 사용자가 &lt;code&gt;/a&lt;/code&gt;라는 URL에 방문 한다면, URL에는 &lt;code&gt;/b&lt;/code&gt;라고 표시되시만, 화면에는 &lt;code&gt;/a&lt;/code&gt; URL의 화면을 보여주는 것을 말합니다.&lt;/p&gt;
&lt;pre class=&quot;brush:js; gutter:false; groovy&quot;&gt;&lt;code&gt;const router = new VueRouter({
  routes: [
    { path: '/a', component: A, alias: '/b' }
  ]
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 코드와 같이 Alias를 사용할 수 있습니다.&lt;/p&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://router.vuejs.org/kr/guide/essentials/redirect-and-alias.html&quot;&gt;https://router.vuejs.org/kr/guide/essentials/redirect-and-alias.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>Alias</category>
      <category>redirect</category>
      <category>vue-router</category>
      <category>Vue.js</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/72</guid>
      <comments>https://beomy.tistory.com/72#entry72comment</comments>
      <pubDate>Wed, 12 Jun 2019 23:58:26 +0900</pubDate>
    </item>
    <item>
      <title>[vue-router] Named Routes &amp;amp; Views</title>
      <link>https://beomy.tistory.com/71</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/rIXHX/btqvZiCmU75/rjbGN3rBxC6mFnvXjxrBV1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rIXHX/btqvZiCmU75/rjbGN3rBxC6mFnvXjxrBV1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rIXHX/btqvZiCmU75/rjbGN3rBxC6mFnvXjxrBV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrIXHX%2FbtqvZiCmU75%2FrjbGN3rBxC6mFnvXjxrBV1%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;h1&gt;1. Named Routes&lt;/h1&gt;
&lt;p&gt;vue-router를 사용할 때, 이름이 있는 라우트를 사용하는 것이 더 편리할 때가 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const router = new VueRouter({
  routes: [
    {
      path: &amp;#39;/user/:userId&amp;#39;,
      name: &amp;#39;user&amp;#39;,
      component: User
    }
  ]
})
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 라우트를 설정하였다면,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;router-link :to=&amp;quot;{ name: &amp;#39;user&amp;#39;, params: { userId: 123 }}&amp;quot;&amp;gt;User&amp;lt;/router-link&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;혹은,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;router.push({ name: &amp;#39;user&amp;#39;, params: { userId: 123 }})
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;를 이용하여 라우터를 변경 할 수 있습니다. 위의 두가지 모두 &lt;code&gt;/user/123&lt;/code&gt;으로 경로를 이동합니다.&lt;/p&gt;
&lt;h1&gt;2. Named Views&lt;/h1&gt;
&lt;p&gt;여러개의 view를 중첩하지 않고 동시에 표시해야 할 경우, 이름을 갖는 view를 사용하면 편리합니다. 예를 들어,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;router-view class=&amp;quot;view one&amp;quot;&amp;gt;&amp;lt;/router-view&amp;gt;
&amp;lt;router-view class=&amp;quot;view two&amp;quot; name=&amp;quot;a&amp;quot;&amp;gt;&amp;lt;/router-view&amp;gt;
&amp;lt;router-view class=&amp;quot;view three&amp;quot; name=&amp;quot;b&amp;quot;&amp;gt;&amp;lt;/router-view&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;const router = new VueRouter({
  routes: [
    {
      path: &amp;#39;/&amp;#39;,
      components: {
        default: Foo,
        a: Bar,
        b: Baz
      }
    }
  ]
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 어플리케이션이 구성되어 있다면, 첫번째 &lt;code&gt;&amp;lt;router-view&amp;gt;&lt;/code&gt; 부터 차례로, &lt;code&gt;Foo&lt;/code&gt; 컴포넌트, &lt;code&gt;Bar&lt;/code&gt; 컴포넌트, &lt;code&gt;Baz&lt;/code&gt; 컴포넌트가 렌더링됩니다. 이 때 반드시 vue-router의 &lt;code&gt;components&lt;/code&gt; 옵션을 사용하여야 합니다.&lt;/p&gt;
&lt;h2&gt;중첩된 Named Views&lt;/h2&gt;
&lt;p&gt;물론 Named Views는 중첩되어 사용할 수 있습니다. 중첩하여 사용하기 위해서는 간단히 &lt;code&gt;&amp;lt;router-view&amp;gt;&lt;/code&gt;를 중첩하여 사용하면 됩니다.&lt;/p&gt;
&lt;pre&gt;
/settings/emails                                       /settings/profile
+-----------------------------------+                  +------------------------------+
| UserSettings                      |                  | UserSettings                 |
| +-----+-------------------------+ |                  | +-----+--------------------+ |
| | Nav | UserEmailsSubscriptions | |  +------------&gt;  | | Nav | UserProfile        | |
| |     +-------------------------+ |                  | |     +--------------------+ |
| |     |                         | |                  | |     | UserProfilePreview | |
| +-----+-------------------------+ |                  | +-----+--------------------+ |
+-----------------------------------+                  +------------------------------+
&lt;/pre&gt;

&lt;p&gt;위의 그림의 구조를 보면, &lt;code&gt;UserSettings&lt;/code&gt; 컴포넌트가 있고, &lt;code&gt;UserSettings&lt;/code&gt; 컴포넌트는 &lt;code&gt;Nav&lt;/code&gt; 컴포넌트를 항상 가지고 있고, &lt;code&gt;UserEmailSubscriptions&lt;/code&gt; 컴포넌트 혹은 &lt;code&gt;UserProfile&lt;/code&gt; 컴포넌트, &lt;code&gt;UserProfilePreview&lt;/code&gt; 컴포넌트를 선택적으로 렌더링 해야 하는 구조입니다. 즉 &lt;code&gt;UserEmailSubscriptions&lt;/code&gt;와 &lt;code&gt;UserProfile&lt;/code&gt;, &lt;code&gt;UserProfilePreview&lt;/code&gt;는 중첩된 view 컴포넌트 구조입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- UserSettings.vue --&amp;gt;
&amp;lt;div&amp;gt;
  &amp;lt;h1&amp;gt;User Settings&amp;lt;/h1&amp;gt;
  &amp;lt;NavBar/&amp;gt;
  &amp;lt;router-view/&amp;gt;
  &amp;lt;router-view name=&amp;quot;helper&amp;quot;/&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;{
  path: &amp;#39;/settings&amp;#39;,
  // You could also have named views at the top
  component: UserSettings,
  children: [{
    path: &amp;#39;emails&amp;#39;,
    component: UserEmailsSubscriptions
  }, {
    path: &amp;#39;profile&amp;#39;,
    components: {
      default: UserProfile,
      helper: UserProfilePreview
    }
  }]
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 컴포넌트와 vue-router 설정을 하면 중첩된 Named View를 구현 할 수 있습니다.&lt;/p&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://router.vuejs.org/guide/essentials/named-routes.html&quot;&gt;https://router.vuejs.org/guide/essentials/named-routes.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://router.vuejs.org/guide/essentials/named-views.html#nested-named-views&quot;&gt;https://router.vuejs.org/guide/essentials/named-views.html#nested-named-views&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>named route</category>
      <category>named view</category>
      <category>vue-router</category>
      <category>Vue.js</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/71</guid>
      <comments>https://beomy.tistory.com/71#entry71comment</comments>
      <pubDate>Mon, 10 Jun 2019 23:39:25 +0900</pubDate>
    </item>
    <item>
      <title>[vue-router] 프로그래밍 방식 네비게이션</title>
      <link>https://beomy.tistory.com/70</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/1hjfP/btqvWqH0Lpv/zt50hCt6xepq6jnLLiIV90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1hjfP/btqvWqH0Lpv/zt50hCt6xepq6jnLLiIV90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1hjfP/btqvWqH0Lpv/zt50hCt6xepq6jnLLiIV90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1hjfP%2FbtqvWqH0Lpv%2Fzt50hCt6xepq6jnLLiIV90%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;code&gt;&amp;lt;router-link&amp;gt;&lt;/code&gt;를 사용하여 선언적 네이게이션용 앵커(anchor) 태그를 만드는 것 외에도 vue-router의 인스턴스 메소드를 사용하여 프로그래밍으로 네비게이션을 수행할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;1. &lt;code&gt;router.push(location, onComplete?, onAbort?)&lt;/code&gt;&lt;/h1&gt;
&lt;p&gt;Vue 인스턴스 내부의 &lt;code&gt;$router&lt;/code&gt;를 사용하여 &lt;code&gt;this.$router.push&lt;/code&gt;와 같이 사용할 수도 있습니다.&lt;br /&gt;&lt;code&gt;router.push&lt;/code&gt;는 다른 URL로 이동할 때 사용됩니다. 이 메소드는 새로운 URL을 히스토리 스택에 넣기 때문에 사용자가 브라우저의 뒤로가기 버튼을 클릭하면 이전 URL로 이동하게 됩니다.&lt;br /&gt;&lt;code&gt;&amp;lt;router-link :to=&quot;...&quot;&amp;gt;&lt;/code&gt;를 클릭하면 &lt;code&gt;router.push(...)&lt;/code&gt;를 호출하는 것과 같습니다.&lt;/p&gt;
&lt;h2&gt;전달 인자&lt;/h2&gt;
&lt;p&gt;전달인자는 문자열 또는 객체가 될 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;brush:js; gutter:false; less&quot;&gt;&lt;code&gt;// 리터럴 string
router.push('home')

// object
router.push({ path: 'home' })

// 이름을 가지는 라우트
router.push({ name: 'user', params: { userId: 123 }})

// 쿼리와 함께 사용, 결과는 /register?plan=private 입니다.
router.push({ path: 'register', query: { plan: 'private' }})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;2.2.0 버전 이후 &lt;code&gt;router.push&lt;/code&gt;와 &lt;code&gt;router.replace&lt;/code&gt; 메소드는 두번째와 세번째 전달인자로 &lt;code&gt;onComplete&lt;/code&gt;와 &lt;code&gt;onAbort&lt;/code&gt; 콜백을 제공합니다. 이 콜백은 탐색이 성공적으로 완료되었거나 중단 될 때 호출 됩니다.&lt;/p&gt;
&lt;h1&gt;2. &lt;code&gt;router.replace(location)&lt;/code&gt;&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;router.push&lt;/code&gt;와 같은 역할을 하지만 차이점은 새로운 URL을 히스토리 스택에 추가하지 않습니다. 이름에서 알 수 있듯이 현재 URL을 대체합니다.&lt;br /&gt;&lt;code&gt;&amp;lt;route-link :to=&quot;...&quot; replace&amp;gt;&lt;/code&gt;를 클릭하면 &lt;code&gt;router.replace(...)&lt;/code&gt;를 호출하는 것과 같습니다.&lt;/p&gt;
&lt;h1&gt;3. &lt;code&gt;router.go(n)&lt;/code&gt;&lt;/h1&gt;
&lt;p&gt;이 메소드는 &lt;code&gt;window.history.go(n)&lt;/code&gt;과 비슷하게 히스토리 스택에서 &lt;code&gt;n&lt;/code&gt; 만큼 앞, 뒤로 이동하는 메소드입니다.&lt;/p&gt;
&lt;pre class=&quot;brush:js; gutter:false; gcode&quot;&gt;&lt;code&gt;// 한 단계 앞으로 갑니다. history.forward()와 같습니다. history.forward()와 같습니다.
router.go(1)

// 한 단계 뒤로 갑니다. history.back()와 같습니다.
router.go(-1)

// 3 단계 앞으로 갑니다.
router.go(3)

// 지정한 만큼의 기록이 없으면 자동으로 실패 합니다.
router.go(-100)
router.go(100)
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;4. History 조작&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;router.push&lt;/code&gt;, &lt;code&gt;router.replace&lt;/code&gt;, &lt;code&gt;router.go&lt;/code&gt;는 각각 &lt;code&gt;window.history.pushState&lt;/code&gt;, &lt;code&gt;window.history.replaceState&lt;/code&gt;, &lt;code&gt;window.history.go&lt;/code&gt;에 매칭됩니다.&lt;br /&gt;vue-router의 네비게이션 메소드(&lt;code&gt;push&lt;/code&gt;, &lt;code&gt;replace&lt;/code&gt;, &lt;code&gt;go&lt;/code&gt;)는 모든 라우터 모드(&lt;code&gt;history&lt;/code&gt;, &lt;code&gt;hash&lt;/code&gt;, &lt;code&gt;abstract&lt;/code&gt;)에서 동일하게 동작합니다.&lt;/p&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://router.vuejs.org/kr/guide/essentials/navigation.html&quot;&gt;https://router.vuejs.org/kr/guide/essentials/navigation.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>$router.go</category>
      <category>$router.push</category>
      <category>$router.replace</category>
      <category>router.go</category>
      <category>router.push</category>
      <category>router.replace</category>
      <category>vue-router</category>
      <category>Vue.js</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/70</guid>
      <comments>https://beomy.tistory.com/70#entry70comment</comments>
      <pubDate>Sun, 9 Jun 2019 23:50:00 +0900</pubDate>
    </item>
    <item>
      <title>[vue-router] 중첩된 라우트</title>
      <link>https://beomy.tistory.com/69</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/3dTtR/btqvVE7GDsJ/sfAfxBbh7ypcv9bGlazCWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3dTtR/btqvVE7GDsJ/sfAfxBbh7ypcv9bGlazCWk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3dTtR/btqvVE7GDsJ/sfAfxBbh7ypcv9bGlazCWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3dTtR%2FbtqvVE7GDsJ%2FsfAfxBbh7ypcv9bGlazCWk%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;실제 앱 UI는 일반적으로 여러 단계의 중첩 된 컴포넌트로 이루어져 있습니다. URL의 세그먼트(&lt;code&gt;/&lt;/code&gt;로 구분 되는 각각의 문자열)는 중첩된 컴포넌트의 특정 구조와 일치하는 경우가 매우 많습니다. 예를 들면,&lt;/p&gt;
&lt;pre&gt;
/user/foo/profile                     /user/foo/posts
+------------------+                  +-----------------+
| User             |                  | User            |
| +--------------+ |                  | +-------------+ |
| | Profile      | |  +------------&gt;  | | Posts       | |
| |              | |                  | |             | |
| +--------------+ |                  | +-------------+ |
+------------------+                  +-----------------+

&lt;/pre&gt;

&lt;p&gt;vue-router의 중첩된 라우트를 사용하면 위의 관계를 표현하는 것이 간단해 집니다. 위의 구조(중첩 라우트)대로 vue-router를 사용하여 컴포넌트를 만든다면,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const User = {
  template: `
    &amp;lt;div class=&amp;quot;user&amp;quot;&amp;gt;
      &amp;lt;h2&amp;gt;User {{ $route.params.id }}&amp;lt;/h2&amp;gt;
      &amp;lt;router-view&amp;gt;&amp;lt;/router-view&amp;gt;
    &amp;lt;/div&amp;gt;
  `
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 컴포넌트를 만들 수 있습니다. 그 후 vue-router의 &lt;code&gt;VueRouter&lt;/code&gt; 생성자 옵션은,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const router = new VueRouter({
  routes: [
    { path: &amp;#39;/user/:id&amp;#39;, component: User,
      children: [
        {
          // /user/:id/profile 과 일치 할 때
          // UserProfile은 User의 &amp;lt;router-view&amp;gt; 내에 렌더링 됩니다.
          path: &amp;#39;profile&amp;#39;,
          component: UserProfile
        },
        {
          // /user/:id/posts 과 일치 할 때
          // UserPosts가 User의 &amp;lt;router-view&amp;gt; 내에 렌더링 됩니다.
          path: &amp;#39;posts&amp;#39;,
          component: UserPosts
        }
      ]
    }
  ]
})
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 설정해야 합니다. &lt;code&gt;children&lt;/code&gt; 옵션은 &lt;code&gt;routes&lt;/code&gt;와 같은 라우트 설정 객체의 또 다른 배열 입니다. 필요한 만큰 중첩된 뷰를 사용할 수 있습니다.&lt;/p&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://router.vuejs.org/kr/guide/essentials/nested-routes.html&quot;&gt;https://router.vuejs.org/kr/guide/essentials/nested-routes.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>Children</category>
      <category>vue-router</category>
      <category>Vue.js</category>
      <category>중첩된 라우트</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/69</guid>
      <comments>https://beomy.tistory.com/69#entry69comment</comments>
      <pubDate>Sun, 9 Jun 2019 23:17:03 +0900</pubDate>
    </item>
    <item>
      <title>[vue-router] 동적 라우트 매칭</title>
      <link>https://beomy.tistory.com/68</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/XY7a8/btqvUx7BPUW/9r8FG9vSbwf4IKmgVlKtW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XY7a8/btqvUx7BPUW/9r8FG9vSbwf4IKmgVlKtW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XY7a8/btqvUx7BPUW/9r8FG9vSbwf4IKmgVlKtW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXY7a8%2FbtqvUx7BPUW%2F9r8FG9vSbwf4IKmgVlKtW1%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;code&gt;User&lt;/code&gt; 컴포넌트가 있고, &lt;code&gt;/user/1&lt;/code&gt; 혹은 &lt;code&gt;/user/2&lt;/code&gt;와 같은 라우트에 접근할 때, 라우트에 매칭된 ID에 해당하는 유저 정보를 &lt;code&gt;User&lt;/code&gt; 컴포넌트에 나타내야 한다면,&lt;/p&gt;
&lt;pre class=&quot;brush:js; gutter:false; arduino&quot;&gt;&lt;code&gt;const User = {
  template: '&amp;lt;div&amp;gt;User&amp;lt;/div&amp;gt;'
}

const router = new VueRouter({
  routes: [
    // 동적 세그먼트는 콜론으로 시작합니다.
    { path: '/user/:id', component: User }
  ]
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 코드와 같이 vue-router 설정을 할 수 있습니다.&lt;br /&gt;&lt;code&gt;/user/1&lt;/code&gt;와 &lt;code&gt;/user/2&lt;/code&gt;는 모두 동일한 컴포넌트에 매칭됩니다. 동적 세그먼트는 콜론(&lt;code&gt;:&lt;/code&gt;)으로 표시됩니다. 동적 세그먼크 값은 모두 &lt;code&gt;this.$route.params&lt;/code&gt;에 표시됩니다.&lt;/p&gt;
&lt;pre class=&quot;brush:js; gutter:false; crmsh&quot;&gt;&lt;code&gt;const User = {
  template: '&amp;lt;div&amp;gt;User {{ $route.params.id }}&amp;lt;/div&amp;gt;'
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 코드와 같이 사용자 ID를 표시할 수 있습니다. 여러개의 동적 세그먼트를 가질 수 있습니다. 모든 세그먼트는 &lt;code&gt;this.$route.params&lt;/code&gt;에 매칭됩니다.&lt;br /&gt;&lt;code&gt;this.$route&lt;/code&gt;는 &lt;code&gt;this.$route.params&lt;/code&gt; 뿐만 아니라, &lt;code&gt;this.$route.query&lt;/code&gt;(URL 안에 쿼리가 있다면), &lt;code&gt;this.$route.hash&lt;/code&gt; 등의 정보를 제공합니다.&lt;/p&gt;
&lt;h1&gt;1. Params 변화 감지&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;/user/1&lt;/code&gt;와 &lt;code&gt;user/2&lt;/code&gt;로 라우터가 변경되었을 때, 동일한 컴포넌트는 재사용됩니다. 이런 동작 방식은 이전 컴포넌트를 삭제한 후 다시 렌더링 하는 것 보다 효욜적입니다. 컴포넌트가 재사용 되기 때문에, 컴포넌트의 라이프 사이클 훅은 호출되지 않습니다.&lt;/p&gt;
&lt;pre class=&quot;brush:js; gutter:false; arduino&quot;&gt;&lt;code&gt;const User = {
  template: '...',
  watch: {
    '$route' (to, from) {
      // 경로 변경에 반응하여...
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;params이 변경 되었을 때, 어떠한 코드 동작을 해야 한다면 &lt;code&gt;$route&lt;/code&gt;를 &lt;code&gt;watch&lt;/code&gt;에 등록하거나,&lt;/p&gt;
&lt;pre class=&quot;brush:js; gutter:false; arduino&quot;&gt;&lt;code&gt;const User = {
  template: '...',
  beforeRouteUpdate (to, from, next) {
    // react to route changes...
    // don't forget to call next()
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 코드와 같이 vue-router의 &lt;code&gt;beforeRouteUpdate&lt;/code&gt; 콜백 메소드를 사용하면 됩니다.&lt;/p&gt;
&lt;h1 id=&quot;catch-all-routing&quot;&gt;2. Catch all / 404 Not Found 라우팅&lt;/h1&gt;
&lt;p&gt;어떠한 url 문자열에도 라우터를 매칭시키고 싶다면 &lt;code&gt;*&lt;/code&gt;를 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;brush:js; gutter:false; less&quot;&gt;&lt;code&gt;{
  // will match everything
  path: '*'
}
{
  // will match anything starting with `/user-`
  path: '/user-*'
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;라우터 설정 마지막에 &lt;code&gt;{ path '*' }&lt;/code&gt;를 추가한다면 이 라우터는 404 페이지로 사용할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;brush:js; gutter:false; stylus&quot;&gt;&lt;code&gt;// Given a route { path: '/user-*' }
this.$router.push('/user-admin')
this.$route.params.pathMatch // 'admin'

// Given a route { path: '*' }
this.$router.push('/non-existing')
this.$route.params.pathMatch // '/non-existing'&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;/user-admin&lt;/code&gt; url은 &lt;code&gt;user-*&lt;/code&gt; 라우터에 매칭됩니다. &lt;code&gt;/non-existing&lt;/code&gt; url은 &lt;code&gt;*&lt;/code&gt;라우터에 매칭됩니다. &lt;code&gt;*&lt;/code&gt;을 이용해 매칭된 param 값은 &lt;code&gt;this.$route.params.pathMatch&lt;/code&gt;에 저장됩니다.&lt;/p&gt;
&lt;h1&gt;3. 고급 매칭 패턴&lt;/h1&gt;
&lt;p&gt;vue-router은 라우트 매칭 앤진으로 &lt;a href=&quot;https://router.vuejs.org/kr/guide/essentials/dynamic-matching.html&quot;&gt;path-to-regexp&lt;/a&gt;을 사용합니다. 더 자세한 라우팅 매칭을 사용하길 원한다면 위의 문서를 참고 바랍니다.&lt;/p&gt;
&lt;h1&gt;4. 매칭 우선순위&lt;/h1&gt;
&lt;p&gt;동일한 url이 여러 라우트와 매칭 되는 경우가 있을 수 있습니다. 이 경우에는 먼저 정의 된 라우트가 높은 우선 순위를 가집니다.&lt;/p&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://router.vuejs.org/kr/guide/essentials/dynamic-matching.html&quot;&gt;https://router.vuejs.org/kr/guide/essentials/dynamic-matching.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>catch-all</category>
      <category>route 설정</category>
      <category>vue-router</category>
      <category>Vue.js</category>
      <category>동적 라우트</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/68</guid>
      <comments>https://beomy.tistory.com/68#entry68comment</comments>
      <pubDate>Sun, 9 Jun 2019 00:30:03 +0900</pubDate>
    </item>
    <item>
      <title>[vue-router] 시작하기</title>
      <link>https://beomy.tistory.com/67</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;Vue.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dkJcBT/btqvThj3DFN/7y6l8yZIddpkEgjjKWF4I0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dkJcBT/btqvThj3DFN/7y6l8yZIddpkEgjjKWF4I0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dkJcBT/btqvThj3DFN/7y6l8yZIddpkEgjjKWF4I0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdkJcBT%2FbtqvThj3DFN%2F7y6l8yZIddpkEgjjKWF4I0%2Fimg.png&quot; data-filename=&quot;Vue.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;vue-router는 Vue.JS의 공식 라우터입니다. vue-router를 사용하면 싱글 페이지 어플리케이션을 쉽게 만들 수 있습니다. 이번 포스트에서는 vue-router를 프로젝트에 추가할 때 HTML단과 JavaScript단에서 추가하는 방법에 대해 이야기 할 것입니다.&lt;/p&gt;
&lt;h1&gt;1. HTML&lt;/h1&gt;
&lt;pre class=&quot;brush:html; gutter:false; xml&quot;&gt;&lt;code&gt;&amp;lt;script src=&quot;https://unpkg.com/vue/dist/vue.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;https://unpkg.com/vue-router/dist/vue-router.js&quot;&amp;gt;&amp;lt;/script&amp;gt;

&amp;lt;div id=&quot;app&quot;&amp;gt;
  &amp;lt;h1&amp;gt;Hello App!&amp;lt;/h1&amp;gt;
  &amp;lt;p&amp;gt;
    &amp;lt;!-- 네비게이션을 위해 router-link 컴포넌트를 사용합니다. --&amp;gt;
    &amp;lt;!-- 구체적인 속성은 `to` prop을 이용합니다. --&amp;gt;
    &amp;lt;!-- 기본적으로 `&amp;lt;router-link&amp;gt;`는 `&amp;lt;a&amp;gt;` 태그로 렌더링됩니다.--&amp;gt;
    &amp;lt;router-link to=&quot;/foo&quot;&amp;gt;Go to Foo&amp;lt;/router-link&amp;gt;
    &amp;lt;router-link to=&quot;/bar&quot;&amp;gt;Go to Bar&amp;lt;/router-link&amp;gt;
  &amp;lt;/p&amp;gt;
  &amp;lt;!-- 라우트 아울렛 --&amp;gt;
  &amp;lt;!-- 현재 라우트에 맞는 컴포넌트가 렌더링됩니다. --&amp;gt;
  &amp;lt;router-view&amp;gt;&amp;lt;/router-view&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;router-link&amp;gt;&lt;/code&gt;는 현재 라우트와 일치할 때 자동으로 &lt;code&gt;.router-link-active&lt;/code&gt; 클래스가 추가됩니다.&lt;/p&gt;
&lt;h1&gt;2. JavaScript&lt;/h1&gt;
&lt;pre class=&quot;brush:js; gutter:false; arduino&quot;&gt;&lt;code&gt;// 0. 모듈 시스템 (예: vue-cli)을 이용하고 있다면, Vue와 Vue 라우터를 import 하세요
// 그리고 `Vue.use(VueRouter)`를 호출하세요


// 1. 라우트 컴포넌트를 정의하세요.
// 아래 내용들은 다른 파일로부터 가져올 수 있습니다.
const Foo = { template: '&amp;lt;div&amp;gt;foo&amp;lt;/div&amp;gt;' }
const Bar = { template: '&amp;lt;div&amp;gt;bar&amp;lt;/div&amp;gt;' }

// 2. 라우트를 정의하세요.
// Each route should map to a component. The &quot;component&quot; can
// 각 라우트는 반드시 컴포넌트와 매핑되어야 합니다.
// &quot;component&quot;는 `Vue.extend()`를 통해 만들어진
// 실제 컴포넌트 생성자이거나 컴포넌트 옵션 객체입니다.
const routes = [
  { path: '/foo', component: Foo },
  { path: '/bar', component: Bar }
]

// 3. `routes` 옵션과 함께 router 인스턴스를 만드세요.
// 추가 옵션을 여기서 전달해야합니다.
// 지금은 간단하게 유지하겠습니다.
const router = new VueRouter({
  routes // `routes: routes`의 줄임
})

// 4. 루트 인스턴스를 만들고 mount 하세요.
// router와 router 옵션을 전체 앱에 주입합니다.
const app = new Vue({
  router
}).$mount('#app')

// 이제 앱이 시작됩니다!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;위의 코드와 같이 vue-router을 vue에 주입하게 되면, 각각의 컴포넌트에서는 &lt;code&gt;this.$router&lt;/code&gt;와 &lt;code&gt;this.$route&lt;/code&gt;를 사용하여 vue-router를 사용할 수 있게 됩니다. 위의 예제는 &lt;a href=&quot;https://codepen.io/beomy/pen/KLLBGz?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 확인 할 수 있습니다.&lt;br /&gt;&lt;code&gt;this.$router&lt;/code&gt;는 vue-router의 메소드(&lt;code&gt;push&lt;/code&gt;, &lt;code&gt;replace&lt;/code&gt; 등.. 라우터 이동와 같은..)를 담고 있는 객체입니다.&lt;br /&gt;&lt;code&gt;this.$route&lt;/code&gt;는 현재의 라우터 정보(&lt;code&gt;params&lt;/code&gt;, &lt;code&gt;path&lt;/code&gt; 등..)를 담고 있는 객체입니다.&lt;/p&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://router.vuejs.org/kr/guide/#javascript&quot;&gt;https://router.vuejs.org/kr/guide/#javascript&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>vue-router</category>
      <category>Vue.js</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/67</guid>
      <comments>https://beomy.tistory.com/67#entry67comment</comments>
      <pubDate>Thu, 6 Jun 2019 18:21:51 +0900</pubDate>
    </item>
    <item>
      <title>[Vue.JS] 반응형 시스템</title>
      <link>https://beomy.tistory.com/66</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;820&quot; height=&quot;289&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/993FFB435C6C0F0324?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/993FFB435C6C0F0324?original&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/993FFB435C6C0F0324&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F993FFB435C6C0F0324&quot; width=&quot;820&quot; height=&quot;289&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;Vue의 가장 두드러지는 특징 중 하나는 눈에 띄지 않는 반응형 시스템입니다. 모델은 단순한 JavaScript 객체입니다. 모델이 수정되면 화면이 갱신됩니다. 이번 포스트에서 이러한 반응형 시스템을 좀 더 자세히 알아보려고 합니다.&lt;/p&gt;
&lt;h1&gt;1. MVVM 패턴&lt;/h1&gt;
&lt;p&gt;Vue는 MVVM 패턴에 영감을 받은 프레임워크입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;400&quot; height=&quot;297&quot; alt=&quot;MVVM 패턴&quot; style=&quot;width: 400px; height: 297px;&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/99519A495C6C107D0B?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/99519A495C6C107D0B?original&quot; data-alt=&quot;MVVM 패턴&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99519A495C6C107D0B&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99519A495C6C107D0B&quot; width=&quot;400&quot; height=&quot;297&quot; alt=&quot;MVVM 패턴&quot; style=&quot;width: 400px; height: 297px;&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;MVVM 패턴&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;위의 그림은 MVVM 패턴을 나타내는 그림입니다. 더 자세한 설명은 &lt;a href=&quot;https://beomy.tistory.com/43&quot;&gt;[디자인패턴] MVC, MVP, MVVM 비교&lt;/a&gt;를 참고 바랍니다.&lt;/p&gt;
&lt;p&gt;View는 DOM, View Model은 Vue, Model은 단순한 JavaScript 객체 입니다. View Model은 View와 데이터 바인딩되어 View Model이 변경 되 었을 때 View가 업데이트 됩니다.&lt;/p&gt;
&lt;h1&gt;2. 변경 내용을 추적하는 방법&lt;/h1&gt;
&lt;p&gt;Vue 인스턴스에 &lt;code&gt;data&lt;/code&gt; 옵션을 포함하는 JavaScript 객체를 전달하면 Vue는 모든 속성에 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty&quot;&gt;Object.defineProperty&lt;/a&gt;를 사용하여 getter/setter로 변환합니다. 이것이 Vue는 ES5를 사용할 수 없는 IE8 이하를 지원하지 않는 이유입니다.&lt;/p&gt;
&lt;p&gt;getter/setter는 사용자에게는 보이지 않지만 속성에 접근하거나 수정할 때 Vue가 종속성 추적 및 변경 알림을 수행할 수 있습니다.&lt;/p&gt;
&lt;p&gt;모든 컴포넌트 인스턴스에는 해당 watcher 인스턴스가 있으며, 이 인스턴스는 컴포넌트가 종속적으로 렌더링되는 동안 수정된 모든 속성을 기록합니다. 또한 종속적인 setter가 트리거 되면 watcher에 알리고 컴포넌트가 다시 랜더링됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;820&quot; height=&quot;513&quot; alt=&quot;watcher의 역할&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/995A42455C6C16F820?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/995A42455C6C16F820?original&quot; data-alt=&quot;watcher의 역할&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/995A42455C6C16F820&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F995A42455C6C16F820&quot; width=&quot;820&quot; height=&quot;513&quot; alt=&quot;watcher의 역할&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;watcher의 역할&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;3. 변경 감지 경고&lt;/h1&gt;
&lt;p&gt;최신 JavaScript의 한계(&lt;code&gt;Object.observe&lt;/code&gt;의 포기)로 인해 Vue는 속성의 추가 체거를 감지 할 수 없습니다. Vue는 인스턴스 초기화 할 때 getter/setter 변환 과정을 수행하기 때문에 data 객체에 속성이 있어서 Vue가 변경사항을 감지할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var vm = new Vue({
  data: {
    a: 1
  }
})
// `vm.a` 은 이제 반응적입니다.

vm.b = 2
// `vm.b` 은 이제 반응적이지 않습니다.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;이미 만들어진 인스턴스에 반응 속성을 동적으로 추가하는 것을 허용하지 않지만, &lt;code&gt;Vue.set(object, key, value)&lt;/code&gt; 메소드를 사용하여 중첩 된 객체에 반응성 속성을 추가 할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Vue.set(vm.someObject, &amp;#39;b&amp;#39;, 2)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 루트 수준의 중첩 된 객체에 반응성 속성을 추가할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;this.$set(this.someObject, &amp;#39;b&amp;#39;, 2)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드는 &lt;code&gt;Vue.set&lt;/code&gt;에 별칭으로 &lt;code&gt;this.$set&lt;/code&gt;을 사용한 코드입니다.&lt;/p&gt;
&lt;h1&gt;4. 반응형 속성 선언하기&lt;/h1&gt;
&lt;p&gt;Vue는 반응성 속성을 동적으로 추가 할 수 없기 때문에 반응성 테이터 속성을 빈 값으로라도 초기에 선언하여 Vue 인스턴스를 초기화 해야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var vm = new Vue({
  data: {
    // 빈 값으로 메시지를 선언 합니다.
    message: &amp;#39;&amp;#39;
  },
  template: &amp;#39;{{ message }}&amp;#39;
})
// 나중에 `message`를 설정합니다.
vm.message = &amp;#39;Hello!&amp;#39;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;data&lt;/code&gt; 옵션에 &lt;code&gt;message&lt;/code&gt;를 선언하지 않으면 Vue의 render 함수는 존재하지 않는 속성에 접근하려고 한다는 경고를 발생시킵니다. 모든 반응  속성을 미리 선언하면 나중에 다른 개발자가 코드를 분석 할 때 코드를 더 쉽게 이해 할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;5. 비동기 갱신 큐&lt;/h1&gt;
&lt;p&gt;Vue는 DOM 업데이트를 비동기로 동작합니다. 모든 데이터 변경 사항은 버퍼링됩니다. 즉 동일한 watcher가 여러번 트리거 되더라고 한번만 큐에 푸시 됩니다. 이 버퍼링은 중복된 변경 사항의 불필요한 계산을 줄이고, DOM 조작을 최소화하기 때문에 중요합니다. 그 후에는 이벤트 루프 tick에서 Vue는 큐를 비우고 실제 DOM에 작업(버퍼링으로 중복이 제거된 작업)을 수행합니다.&lt;/p&gt;
&lt;p&gt;예를 들어, &lt;code&gt;vm,someDate = &amp;#39;new value&amp;#39;&lt;/code&gt;를 설정하면, 컴포넌트는 즉시 재 랜더링하지 않습니다. 다음 tick에서 업데이트 됩니다. 대부분의 경우에는 이 작업을 신경 쓸 필요는 없지만, 업데이트 된 후 DOM 상태에 의존적인 작업을 하기 위해서는 까다로울 수 있습니다. Vue는 일반적으로 개발자가 데이터 중심적으로 개발하고 DOM을 직접 수정하지 않는 것을 권장하지만 때로는 직접 DOM을 수정해야 필요도 있습니다.&lt;/p&gt;
&lt;p&gt;Vue가 DOM을 업데이트 된 후의 DOM을 보장하기 위해서는 &lt;code&gt;Vue.nextTick(callback)&lt;/code&gt; 을 사용하면 됩니다. 이 &lt;code&gt;callback&lt;/code&gt;은 DOM이 업데이트 된 후에 호출 되기 때문에 DOM이 업데이트 된 이후임을 보장할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;example&amp;quot;&amp;gt;{{ message }}&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;var vm = new Vue({
  el: &amp;#39;#example&amp;#39;,
  data: {
    message: &amp;#39;123&amp;#39;
  }
})
vm.message = &amp;#39;new message&amp;#39; // 데이터  변경
vm.$el.textContent === &amp;#39;new message&amp;#39; // false
Vue.nextTick(function () {
  vm.$el.textContent === &amp;#39;new message&amp;#39; // true
})
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;또한 &lt;code&gt;this.$nextTick()&lt;/code&gt; 메소드를 제공합니다. 이 메소드는 컴포넌트 인스턴스에서 사용할 때 유용합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Vue.component(&amp;#39;example&amp;#39;, {
  template: &amp;#39;{{ message }}&amp;#39;,
  data: function () {
    return {
      message: &amp;#39;갱신 안됨&amp;#39;
    }
  },
  methods: {
    updateMessage: function () {
      this.message = &amp;#39;갱신됨&amp;#39;
      console.log(this.$el.textContent) // =&amp;gt; &amp;#39;갱신 안됨&amp;#39;
      this.$nextTick(function () {
        console.log(this.$el.textContent) // =&amp;gt; &amp;#39;갱신됨&amp;#39;
      })
    }
  }
})
&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://kr.vuejs.org/v2/guide/reactivity.html&quot;&gt;https://kr.vuejs.org/v2/guide/reactivity.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>MVVM</category>
      <category>reactive system</category>
      <category>this.$nextTick</category>
      <category>this.$set</category>
      <category>Vue.js</category>
      <category>Vue.nextTick</category>
      <category>Vue.set</category>
      <category>watcher</category>
      <category>반응형 시스템</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/66</guid>
      <comments>https://beomy.tistory.com/66#entry66comment</comments>
      <pubDate>Wed, 20 Feb 2019 00:33:47 +0900</pubDate>
    </item>
    <item>
      <title>[Vue.JS] 필터</title>
      <link>https://beomy.tistory.com/65</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;820&quot; height=&quot;289&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/99371F505C6AC7F544?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/99371F505C6AC7F544?original&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99371F505C6AC7F544&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99371F505C6AC7F544&quot; width=&quot;820&quot; height=&quot;289&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;h1&gt;1. 필터 사용방법&lt;/h1&gt;
&lt;p&gt;Vue는 텍스트 형식화 할 수 있는 필터 기능을 제공합니다. 필터는 중괄호 보간법, &lt;code&gt;v-bind&lt;/code&gt; 표현법(2.1.0+ 부터)에서 사용 할 수 있습니다. 필터는 JavaScript 표현식 마지막의 파이프(&lt;code&gt;|&lt;/code&gt;) 기호와 함께 사용해야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- in mustaches --&amp;gt;
{{ message | capitalize }}

&amp;lt;!-- in v-bind --&amp;gt;
&amp;lt;div v-bind:id=&amp;quot;rawId | formatId&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;h1&gt;2. 로컬 필터 정의&lt;/h1&gt;
&lt;p&gt;컴포넌트의 옵션으로 로컬 필터를 정의할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;filters: {
  capitalize: function (value) {
    if (!value) return &amp;#39;&amp;#39;
    value = value.toString()
    return value.charAt(0).toUpperCase() + value.slice(1)
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;h1&gt;3. 전역 필터 정의&lt;/h1&gt;
&lt;p&gt;Vue 인스턴스 생성 전(&lt;code&gt;new Vue()&lt;/code&gt; 전)에 전역으로 필터를 정의할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Vue.filter(&amp;#39;capitalize&amp;#39;, function (value) {
  if (!value) return &amp;#39;&amp;#39;
  value = value.toString()
  return value.charAt(0).toUpperCase() + value.slice(1)
})

new Vue({
  // ...
})&lt;/code&gt;&lt;/pre&gt;&lt;h1&gt;4. 필터 체이닝&lt;/h1&gt;
&lt;p&gt;필터 함수의 첫번째 전달인자는 JavaScript의 표현식의 값입니다. 위의 예제의 &lt;code&gt;capitalize&lt;/code&gt; 필터 함수의 첫번째 전달인자의 값은 &lt;code&gt;message&lt;/code&gt;의 값입니다. 필터는 연결될 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{{ message | filterA | filterB }}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드의 경우에 &lt;code&gt;filterA&lt;/code&gt;의 전달인자는 &lt;code&gt;message&lt;/code&gt;이고, &lt;code&gt;filterB&lt;/code&gt;의 전달인자는 &lt;code&gt;filterA&lt;/code&gt;의 결과 값(&lt;code&gt;filterA&lt;/code&gt; 함수가 &lt;code&gt;message&lt;/code&gt;의 전달인자로 계산되어진 값)이 됩니다.&lt;/p&gt;
&lt;h1&gt;5. 필터 함수 전달인자&lt;/h1&gt;
&lt;p&gt;필터 함수는 일반적인 JavaScript 함수이기 때문에 두개 이상의 전달 인자를 가질 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{{ message | filterA(&amp;#39;arg1&amp;#39;, arg2) }}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드의 &lt;code&gt;filterA&lt;/code&gt; 필터 함수는 3개의 전달 인자를 가지는 함수 입니다. 첫번째 전달인자는 &lt;code&gt;message&lt;/code&gt;의 값이고, 두번째 전달인자는 &lt;code&gt;&amp;#39;arg1&amp;#39;&lt;/code&gt;이라는 문자열입니다. 세번째 전달인자는 &lt;code&gt;arg2&lt;/code&gt; 변수의 값을 전달인자로 가집니다.&lt;/p&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://kr.vuejs.org/v2/guide/filters.html&quot;&gt;https://kr.vuejs.org/v2/guide/filters.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>Filter</category>
      <category>Vue.js</category>
      <category>필터</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/65</guid>
      <comments>https://beomy.tistory.com/65#entry65comment</comments>
      <pubDate>Tue, 19 Feb 2019 00:15:23 +0900</pubDate>
    </item>
    <item>
      <title>[Vue.JS] 플러그인</title>
      <link>https://beomy.tistory.com/64</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;820&quot; height=&quot;289&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/991DD13A5C6976D20A?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/991DD13A5C6976D20A?original&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/991DD13A5C6976D20A&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F991DD13A5C6976D20A&quot; width=&quot;820&quot; height=&quot;289&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;플러그인은 일반적으로 전역으로 기능을 Vue에 추가합니다. 플러그인은 엄격하게 규정된 범위는 없으며, 여러 유형의 플러그인이 있습니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;일부 전역 메소드또는 속성 추가 : &lt;a href=&quot;https://github.com/karol-f/vue-custom-element&quot;&gt;vue-coustom-element&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;하나 이상의 전역 자산 추가 : 디렉티브 / 필터 / 트랜지션 등.. &lt;a href=&quot;https://github.com/vuejs/vue-touch&quot;&gt;vue-touch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;글로벌 mixin으로 일부 컴포넌트 옵션을 추가 : &lt;a href=&quot;https://github.com/vuejs/vue-router&quot;&gt;vue-router&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;일부 Vue 인스턴스 메소드를 &lt;code&gt;Vue.prototype&lt;/code&gt;에 추가&lt;/li&gt;
&lt;li&gt;자체 API를 제공하는 동시에 위의 일부 조합을 주입하는 라이브러리 : &lt;a href=&quot;https://github.com/vuejs/vue-router&quot;&gt;vue-router&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;1. 플러그인 사용하기&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;Vue.use()&lt;/code&gt; 전역 메소드를 호출하여 플러그인을 사용할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// calls `MyPlugin.install(Vue)`
Vue.use(MyPlugin)

new Vue({
  //... options
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 &lt;code&gt;new Vue()&lt;/code&gt;를 호출하여 앱을 시작하기 전에 &lt;code&gt;Vue.use()&lt;/code&gt;를 사용해야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Vue.use(MyPlugin, { someOption: true })&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 선택적으로 옵션을 전달 할 수 있습니다. &lt;code&gt;Vue.use&lt;/code&gt;는 자동으로 같은 플로그인을 두 번 이상 사용하지 못하게 합니다. 동일한 플로그인을 여러번 호출하면 해당 플로그인은 한 번만 설치 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Browserify 또는 Webpack을 통해 CommonJS를 사용할 때
var Vue = require(&amp;#39;vue&amp;#39;)
var VueRouter = require(&amp;#39;vue-router&amp;#39;)

// 잊지 마세요
Vue.use(VueRouter)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;vue-router&lt;/code&gt;와 같이 Vue.JS에서 공식적으로 제공하는 플러그인은 &lt;code&gt;Vue&lt;/code&gt;가 전역으로 선언되어 있다면, 자동으로 &lt;code&gt;Vue.use()&lt;/code&gt;를 호출합니다. 하지만 Common.JS와 같이 모듈러를 사용할 경우 항상 &lt;code&gt;Vue.use()&lt;/code&gt;를 선언하여 사용해야 합니다.&lt;/p&gt;
&lt;h1&gt;2. 플로그인 작성하기&lt;/h1&gt;
&lt;p&gt;Vue.JS 플로그인은 &lt;code&gt;install&lt;/code&gt; 메소드를 노출해야 합니다. &lt;code&gt;install&lt;/code&gt; 함수는 전달인자로 첫번째 전달인자는 Vue 생성자, 두번째 전달인자로 옵션을 가집니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;MyPlugin.install = function (Vue, options) {
  // 1. 전역 메소드 또는 속성 추가
  Vue.myGlobalMethod = function () {
    // 필요한 로직 ...
  }

  // 2. 전역 에셋 추가
  Vue.directive(&amp;#39;my-directive&amp;#39;, {
    bind (el, binding, vnode, oldVnode) {
      // 필요한 로직 ...
    }
    ...
  })

  // 3. 컴포넌트 옵션 주입
  Vue.mixin({
    created: function () {
      // 필요한 로직 ...
    }
    ...
  })

  // 4. 인스턴스 메소드 추가
  Vue.prototype.$myMethod = function (options) {
    // 필요한 로직 ...
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://kr.vuejs.org/v2/guide/plugins.html&quot;&gt;https://kr.vuejs.org/v2/guide/plugins.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://vuejs.org/v2/guide/plugins.html&quot;&gt;https://vuejs.org/v2/guide/plugins.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>plusin</category>
      <category>Vue.js</category>
      <category>Vue.use</category>
      <category>플러그인</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/64</guid>
      <comments>https://beomy.tistory.com/64#entry64comment</comments>
      <pubDate>Mon, 18 Feb 2019 00:31:07 +0900</pubDate>
    </item>
    <item>
      <title>[Vue.JS] Render Functions &amp;amp; JSX</title>
      <link>https://beomy.tistory.com/63</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;820&quot; height=&quot;289&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/999BBC355C62D49329?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/999BBC355C62D49329?original&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/999BBC355C62D49329&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F999BBC355C62D49329&quot; width=&quot;820&quot; height=&quot;289&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;h1&gt;1. 기본&lt;/h1&gt;
&lt;p&gt;대부분의 경우 Vue는 템플릿을 사용하여 HTML을 작성하는 것을 권장합니다. 하지만 때로는 JavaScript를 사용하여 HTML을 작성을 해야 할 때가 있습니다. 이럴 때 &lt;code&gt;render&lt;/code&gt; 함수를 사용하면 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;h1&amp;gt;
  &amp;lt;a name=&amp;quot;hello-world&amp;quot; href=&amp;quot;#hello-world&amp;quot;&amp;gt;
    Hello world!
  &amp;lt;/a&amp;gt;
&amp;lt;/h1&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 작성된 HTML이 있을 때,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;anchored-heading :level=&amp;quot;1&amp;quot;&amp;gt;Hello world!&amp;lt;/anchored-heading&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 컴포넌트를 작성하여 사용할 수 있습니다. 이 때 &lt;code&gt;level&lt;/code&gt; 속성으로 &lt;code&gt;h&lt;/code&gt; 태그의 종류를 바꿀 수 있는 컴포넌트를 작성한다면,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;script type=&amp;quot;text/x-template&amp;quot; id=&amp;quot;anchored-heading-template&amp;quot;&amp;gt;
  &amp;lt;h1 v-if=&amp;quot;level === 1&amp;quot;&amp;gt;
    &amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;
  &amp;lt;/h1&amp;gt;
  &amp;lt;h2 v-else-if=&amp;quot;level === 2&amp;quot;&amp;gt;
    &amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;
  &amp;lt;/h2&amp;gt;
  &amp;lt;h3 v-else-if=&amp;quot;level === 3&amp;quot;&amp;gt;
    &amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;
  &amp;lt;/h3&amp;gt;
  &amp;lt;h4 v-else-if=&amp;quot;level === 4&amp;quot;&amp;gt;
    &amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;
  &amp;lt;/h4&amp;gt;
  &amp;lt;h5 v-else-if=&amp;quot;level === 5&amp;quot;&amp;gt;
    &amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;
  &amp;lt;/h5&amp;gt;
  &amp;lt;h6 v-else-if=&amp;quot;level === 6&amp;quot;&amp;gt;
    &amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;
  &amp;lt;/h6&amp;gt;
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;Vue.component(&amp;#39;anchored-heading&amp;#39;, {
  template: &amp;#39;#anchored-heading-template&amp;#39;,
  props: {
    level: {
      type: Number,
      required: true
    }
  }
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같은 컴포넌트가 작성될 수 있습니다. 하지만 위의 예제는 코드 중복이 많아 장황하게 보입니다. &lt;code&gt;render&lt;/code&gt; 함수를 사용하여 다시 작성해 보면,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Vue.component(&amp;#39;anchored-heading&amp;#39;, {
  render: function (createElement) {
    return createElement(
      &amp;#39;h&amp;#39; + this.level,   // 태그 이름
      this.$slots.default // 자식의 배열
    )
  },
  props: {
    level: {
      type: Number,
      required: true
    }
  }
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 될 수 있습니다. 코드 중복이 없기 때문에 템플릿으로 작성된 코드보다 간단하게 보일 수 있습니다. 이 때, &lt;code&gt;&amp;lt;anchored-heading&amp;gt;&lt;/code&gt; 컴포넌트 안에 있는 &lt;code&gt;Hello wold!&lt;/code&gt;는 &lt;code&gt;slot&lt;/code&gt; 속성이 정의 되어 있지 않기 때문에, &lt;code&gt;$slots.default&lt;/code&gt;에 배열로 저장됩니다.&lt;/p&gt;
&lt;h1&gt;2. Node, Tree, Virtual DOM&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;render&lt;/code&gt; 함수에 대해 이야기하기 전에 브라우저 작동 방식을 먼저 이야기 해 보도록 하겠습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div&amp;gt;
  &amp;lt;h1&amp;gt;My title&amp;lt;/h1&amp;gt;
  Some text content
  &amp;lt;!-- TODO: Add tagline  --&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;브라우저가 위의 코드를 읽게 되면, 아래 그림과 같이 DOM 노드 트리를 만듭니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;820&quot; height=&quot;522&quot; alt=&quot;DOM 노트 트리&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/9901D53D5C62D93208?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/9901D53D5C62D93208?original&quot; data-alt=&quot;DOM 노트 트리&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9901D53D5C62D93208&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9901D53D5C62D93208&quot; width=&quot;820&quot; height=&quot;522&quot; alt=&quot;DOM 노트 트리&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;DOM 노트 트리&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;모든 엘리먼트와 텍스트, 심지어 주석도 노드입니다. 노드는 페이지의 조각입니다. 위의 트리에서 볼 수 있듯이 각 녿는 자식을 가질 수 있습니다. 노드를 효율적으로 업데이트 하는 것은 어렵습니다. 하지만 다행이도 수동으로 업데이트 할 필요는 없습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;h1&amp;gt;{{ blogTitle }}&amp;lt;/h1&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;템플릿에서 위의 코드와 같이 작성하거나,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;render: function (createElement) {
  return createElement(&amp;#39;h1&amp;#39;, this.blogTitle)
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 &lt;code&gt;render&lt;/code&gt; 함수를 사용하면, Vue는 자동으로 페이지를 업데이트 합니다.&lt;/p&gt;
&lt;h2&gt;1) Virtual DOM&lt;/h2&gt;
&lt;p&gt;Vue는 실제 DOM에 필요한 변경사항을 추적하기 위해 virtual DOM을 만듭니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;return createElement(&amp;#39;h1&amp;#39;, this.blogTitle)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;createElement&lt;/code&gt;는 Virtual Node(&lt;code&gt;VNode&lt;/code&gt;)를 리턴합니다. Virtual Node는 실제 DOM 엘리먼트와 정확하게 일치하지는 않습니다. Virtual DOM은 컴포넌트 트리로 만들어진 &lt;code&gt;VNode&lt;/code&gt; 트리입니다.&lt;/p&gt;
&lt;h1&gt;3. &lt;code&gt;createElement&lt;/code&gt; 전달인자&lt;/h1&gt;
&lt;pre&gt;&lt;code&gt;// @returns {VNode}
createElement(
  // {String | Object | Function}
  // HTML 태그 이름, 컴포넌트 옵션 또는 함수 중
  // 하나를 반환하는 함수입니다. 필수 사항.
  &amp;#39;div&amp;#39;,

  // {Object}
  // 템플릿에서 사용할 속성에 해당하는 데이터 객체입니다
  // 데이터 객체입니다. 선택 사항.
  {
    // (아래 다음 섹션에 자세히 설명되어 있습니다.)
  },

  // {String | Array}
  // VNode 자식들. `createElement()`를 사용해 만들거나,
  // 간단히 문자열을 사용해 &amp;#39;text VNodes&amp;#39;를 얻을 수 있습니다. 선택사항
  [
    &amp;#39;Some text comes first.&amp;#39;,
    createElement(&amp;#39;h1&amp;#39;, &amp;#39;A headline&amp;#39;),
    createElement(MyComponent, {
      props: {
        someProp: &amp;#39;foobar&amp;#39;
      }
    })
  ]
)&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;1) 데이터 객체 깊이 알아보기&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;createElement&lt;/code&gt;의 두번째 전달인자인 데이터 객체를 좀 더 자세히 이야기 해보도록 하겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;v-bind:class&lt;/code&gt;와 &lt;code&gt;v-bind:style&lt;/code&gt;이 템플릿에서 특별하게 처리되는 것과 비슷하게 &lt;code&gt;VNode&lt;/code&gt; 데이터 객체의 최상위 필드에 &lt;code&gt;class&lt;/code&gt;와 &lt;code&gt;style&lt;/code&gt;이 있습니다. 또한 일반적인 HTML 속성 뿐만 아니라 &lt;code&gt;innerHTML&lt;/code&gt;과 같은 DOM 속성도 가지고 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  // `v-bind:class` 와 같음
  &amp;#39;class&amp;#39;: {
    foo: true,
    bar: false
  },
  // `v-bind:style` 와 같음
  style: {
    color: &amp;#39;red&amp;#39;,
    fontSize: &amp;#39;14px&amp;#39;
  },
  // 일반 HTML 속성
  attrs: {
    id: &amp;#39;foo&amp;#39;
  },
  // 컴포넌트 props
  props: {
    myProp: &amp;#39;bar&amp;#39;
  },
  // DOM 속성
  domProps: {
    innerHTML: &amp;#39;baz&amp;#39;
  },
  // `v-on:keyup.enter`와 같은 수식어가 지원되지 않으나
  // 이벤트 핸들러는 `on` 아래에 중첩됩니다.
  // 수동으로 핸들러에서 keyCode를 확인해야 합니다.
  on: {
    click: this.clickHandler
  },
  // 컴포넌트 전용.
  // `vm.$emit`를 사용하여 컴포넌트에서 발생하는 이벤트가 아닌
  // 기본 이벤트를 받을 수 있게 합니다.
  nativeOn: {
    click: this.nativeClickHandler
  },
  // 사용자 지정 디렉티브.
  // Vue는 이를 관리하기 때문에 바인딩의 oldValue는 설정할 수 없습니다.
  directives: [
    {
      name: &amp;#39;my-custom-directive&amp;#39;,
      value: &amp;#39;2&amp;#39;,
      expression: &amp;#39;1 + 1&amp;#39;,
      arg: &amp;#39;foo&amp;#39;,
      modifiers: {
        bar: true
      }
    }
  ],
  // 범위 지정 슬롯. 형식은
  // { name: props =&amp;gt; VNode | Array&amp;lt;VNode&amp;gt; } 입니다.
  scopedSlots: {
    default: props =&amp;gt; createElement(&amp;#39;span&amp;#39;, props.text)
  },
  // 이 컴포넌트가 다른 컴포넌트의 자식인 경우, 슬롯의 이름입니다.
  slot: &amp;#39;name-of-slot&amp;#39;,
  // 기타 최고 레벨 속성
  key: &amp;#39;myKey&amp;#39;,
  ref: &amp;#39;myRef&amp;#39;
}&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;2) 전체 예제&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;var getChildrenTextContent = function (children) {
  return children.map(function (node) {
    return node.children
      ? getChildrenTextContent(node.children)
      : node.text
  }).join(&amp;#39;&amp;#39;)
}

Vue.component(&amp;#39;anchored-heading&amp;#39;, {
  render: function (createElement) {
    // kebabCase id를 만듭니다.
    var headingId = getChildrenTextContent(this.$slots.default)
      .toLowerCase()
      .replace(/\W+/g, &amp;#39;-&amp;#39;)
      .replace(/(^\-|\-$)/g, &amp;#39;&amp;#39;)

    return createElement(
      &amp;#39;h&amp;#39; + this.level,
      [
        createElement(&amp;#39;a&amp;#39;, {
          attrs: {
            name: headingId,
            href: &amp;#39;#&amp;#39; + headingId
          }
        }, this.$slots.default)
      ]
    )
  },
  props: {
    level: {
      type: Number,
      required: true
    }
  }
})&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;3) 제약사항&lt;/h2&gt;
&lt;p&gt;컴포넌트 토리의 모든 &lt;code&gt;VNode&lt;/code&gt;는 고유해야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;render: function (createElement) {
  var myParagraphVNode = createElement(&amp;#39;p&amp;#39;, &amp;#39;hi&amp;#39;)
  return createElement(&amp;#39;div&amp;#39;, [
    // 이런 - Vnode가 중복입니다!
    myParagraphVNode, myParagraphVNode
  ])
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드는 &lt;code&gt;VNode&lt;/code&gt;가 중복되었기 때문에 잘못 작성된 코드입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;render: function (createElement) {
  return createElement(&amp;#39;div&amp;#39;,
    Array.apply(null, { length: 20 }).map(function () {
      return createElement(&amp;#39;p&amp;#39;, &amp;#39;hi&amp;#39;)
    })
  )
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;같은 엘리먼트 혹은 컴포넌트를 여러번 사용해야 할 경우 위의 코드와 같이 복제하여 사용해야 합니다.&lt;/p&gt;
&lt;h1&gt;4. 템플릿 기능을 일반 JavaScript로 변경하기&lt;/h1&gt;
&lt;h2&gt;1) &lt;code&gt;v-if&lt;/code&gt;와 &lt;code&gt;v-for&lt;/code&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;ul v-if=&amp;quot;items.length&amp;quot;&amp;gt;
  &amp;lt;li v-for=&amp;quot;item in items&amp;quot;&amp;gt;{{ item.name }}&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;p v-else&amp;gt;No items found.&amp;lt;/p&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 &lt;code&gt;v-if&lt;/code&gt;와 &lt;code&gt;v-for&lt;/code&gt;을 사용한 코드는&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;render: function (createElement) {
  if (this.items.length) {
    return createElement(&amp;#39;ul&amp;#39;, this.items.map(function (item) {
      return createElement(&amp;#39;li&amp;#39;, item.name)
    }))
  } else {
    return createElement(&amp;#39;p&amp;#39;, &amp;#39;No items found.&amp;#39;)
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 &lt;code&gt;render&lt;/code&gt; 함수로 작성될 수 있습니다.&lt;/p&gt;
&lt;h2&gt;2) &lt;code&gt;v-model&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;render&lt;/code&gt; 함수에는 &lt;code&gt;v-model&lt;/code&gt;과 대응되는 기능이 없어, 직접 구현해야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;render: function (createElement) {
  var self = this
  return createElement(&amp;#39;input&amp;#39;, {
    domProps: {
      value: self.value
    },
    on: {
      input: function (event) {
        self.value = event.target.value
        self.$emit(&amp;#39;input&amp;#39;, event.target.value)
      }
    }
  })
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 더 깊은 수준으로 코드를 작성해야 하지만 &lt;code&gt;v-model&lt;/code&gt;에 비해 세부 사항까지 더 많은 제어가 가능합니다.&lt;/p&gt;
&lt;h2&gt;3) 이벤트 및 키 수식어&lt;/h2&gt;
&lt;table class=&quot;txc-table&quot; style=&quot;border: none; border-collapse: collapse; width: 246px;&quot; border=&quot;0&quot; width=&quot;246&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;width: 175px; height: 24px; border: 1px solid #cccccc;&quot;&gt;&lt;p&gt;&amp;nbsp;&lt;b&gt;수식어&lt;/b&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style=&quot;width: 71px; height: 24px; border-bottom: 1px solid #cccccc; border-right: 1px solid #cccccc; border-top: 1px solid #cccccc;&quot;&gt;&lt;p&gt;&amp;nbsp;&lt;b&gt;접두어&lt;/b&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 175px; height: 24px; border-bottom: 1px solid #cccccc; border-right: 1px solid #cccccc; border-left: 1px solid #cccccc;&quot;&gt;&lt;p&gt;&amp;nbsp;&lt;code&gt;.passive&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style=&quot;width: 71px; height: 24px; border-bottom: 1px solid #cccccc; border-right: 1px solid #cccccc;&quot;&gt;&lt;p&gt;&amp;nbsp;&amp;amp;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 175px; height: 24px; border-bottom: 1px solid #cccccc; border-right: 1px solid #cccccc; border-left: 1px solid #cccccc;&quot;&gt;&lt;p&gt;&amp;nbsp;&lt;code&gt;.capture&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style=&quot;width: 71px; height: 24px; border-bottom: 1px solid #cccccc; border-right: 1px solid #cccccc;&quot;&gt;&lt;p&gt;&amp;nbsp;!&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 175px; height: 24px; border-bottom: 1px solid #cccccc; border-right: 1px solid #cccccc; border-left: 1px solid #cccccc;&quot;&gt;&lt;p&gt;&amp;nbsp;&lt;code&gt;.once&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style=&quot;width: 71px; height: 24px; border-bottom: 1px solid #cccccc; border-right: 1px solid #cccccc;&quot;&gt;&lt;p&gt;&amp;nbsp;~&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 175px; height: 24px; border-bottom: 1px solid #cccccc; border-right: 1px solid #cccccc; border-left: 1px solid #cccccc;&quot;&gt;&lt;p&gt;&amp;nbsp;&lt;code&gt;.capture.once&lt;/code&gt; 또는&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;code&gt;.once.capture&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style=&quot;width: 71px; height: 24px; border-bottom: 1px solid #cccccc; border-right: 1px solid #cccccc;&quot;&gt;&lt;p&gt;&amp;nbsp;~!&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;&lt;code&gt;.pssive&lt;/code&gt;, &lt;code&gt;.capture&lt;/code&gt;, &lt;code&gt;.once&lt;/code&gt; 이벤트 수식어는 위의 테이블과 같이 접두어를 제공합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;on: {
  &amp;#39;!click&amp;#39;: this.doThisInCapturingMode,
  &amp;#39;~keyup&amp;#39;: this.doThisOnce,
  `~!mouseover`: this.doThisOnceInCapturingMode
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 &lt;code&gt;.pssive&lt;/code&gt;, &lt;code&gt;.capture&lt;/code&gt;, &lt;code&gt;.once&lt;/code&gt; 이벤트 수식어를 사용할 수 있습니다. 다른 이벤트 수식어 및 키 수식어의 경우, 이벤트의 메소드를 사용할 수 있어 고유한 접두사가 필요하지 않습니다.&lt;/p&gt;
&lt;table class=&quot;txc-table&quot; style=&quot;border: none; border-collapse: collapse; width: 647px;&quot; border=&quot;0&quot; width=&quot;647&quot; cellspacing=&quot;0&quot; cellpadding=&quot;0&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;width: 294px; height: 24px; border: 1px solid #cccccc;&quot;&gt;&lt;p&gt;&lt;b&gt;수식어&lt;/b&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style=&quot;width: 353px; height: 24px; border-bottom: 1px solid #cccccc; border-right: 1px solid #cccccc; border-top: 1px solid #cccccc;&quot;&gt;&lt;p&gt;&amp;nbsp;&lt;b&gt;동등한 핸들러&lt;/b&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 294px; height: 24px; border-bottom: 1px solid #cccccc; border-right: 1px solid #cccccc; border-left: 1px solid #cccccc;&quot;&gt;&lt;p&gt;&amp;nbsp;&lt;code&gt;.stop&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style=&quot;width: 353px; height: 24px; border-bottom: 1px solid #cccccc; border-right: 1px solid #cccccc;&quot;&gt;&lt;p&gt;&amp;nbsp;&lt;code&gt;event.stopPropagation()&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 294px; height: 24px; border-bottom: 1px solid #cccccc; border-right: 1px solid #cccccc; border-left: 1px solid #cccccc;&quot;&gt;&lt;p&gt;&amp;nbsp;&lt;code&gt;.prevent&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style=&quot;width: 353px; height: 24px; border-bottom: 1px solid #cccccc; border-right: 1px solid #cccccc;&quot;&gt;&lt;p&gt;&amp;nbsp;&lt;code&gt;event.preventDefault()&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 294px; height: 24px; border-bottom: 1px solid #cccccc; border-right: 1px solid #cccccc; border-left: 1px solid #cccccc;&quot;&gt;&lt;p&gt;&amp;nbsp;&lt;code&gt;.self&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style=&quot;width: 353px; height: 24px; border-bottom: 1px solid #cccccc; border-right: 1px solid #cccccc;&quot;&gt;&lt;p&gt;&amp;nbsp;&lt;code&gt;if (event.target !== event.currentTarget) return&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 294px; height: 24px; border-bottom: 1px solid #cccccc; border-right: 1px solid #cccccc; border-left: 1px solid #cccccc;&quot;&gt;&lt;p&gt;&amp;nbsp;키 : &lt;code&gt;.enter&lt;/code&gt;, &lt;code&gt;.13&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style=&quot;width: 353px; height: 24px; border-bottom: 1px solid #cccccc; border-right: 1px solid #cccccc;&quot;&gt;&lt;p&gt;&amp;nbsp;&lt;code&gt;if (event.keyCode !== 13) return&lt;/code&gt; (13는 키 코드로 다른 키 코드 사용이 가능합니다.)&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 294px; height: 24px; border-bottom: 1px solid #cccccc; border-right: 1px solid #cccccc; border-left: 1px solid #cccccc;&quot;&gt;&lt;p&gt;&amp;nbsp;Modifiers Keys : &lt;code&gt;.ctrl&lt;/code&gt;, &lt;code&gt;.alt&lt;/code&gt;, &lt;code&gt;.shift&lt;/code&gt;, &lt;code&gt;.meta&lt;/code&gt;&lt;/p&gt;&lt;/td&gt;&lt;td style=&quot;width: 353px; height: 24px; border-bottom: 1px solid #cccccc; border-right: 1px solid #cccccc;&quot;&gt;&lt;p&gt;&amp;nbsp;&lt;code&gt;if (!event.ctrlKey) return&lt;/code&gt; (&lt;code&gt;ctrlKey&lt;/code&gt;를 &lt;code&gt;altKey&lt;/code&gt;, &lt;code&gt;shiftKey&lt;/code&gt; 또는 &lt;code&gt;metaKey&lt;/code&gt;로 각각 변경하십시오.)&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;

&lt;pre&gt;&lt;code&gt;on: {
  keyup: function (event) {
    // 이벤트를 내보내는 요소가 이벤트가 바인딩 된 요소가 아닌 경우
    // 중단합니다.
    if (event.target !== event.currentTarget) return
    // 키보드에서 뗀 키가 Enter키 (13)이 아니며
    // Shift키가 동시에 눌러지지 않은 경우
    // 중단합니다.
    if (!event.shiftKey || event.keyCode !== 13) return
    // 전파를 멈춥니다.
    event.stopPropagation()
    // 엘리먼트 기본 동작을 방지합니다.
    event.preventDefault()
    // ...
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;4) Slots&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;render: function (createElement) {
  return createElement(&amp;#39;div&amp;#39;, this.$slots.default)
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 &lt;code&gt;this.$slots&lt;/code&gt;를 사용하여 정적 슬롯 내용을 구현 할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;render: function (createElement) {
  return createElement(&amp;#39;div&amp;#39;, [
    this.$scopedSlots.default({
      text: this.msg
    })
  ])
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;특정 범위를 가지는 슬롯을 또한 위의 코드와 같이 &lt;code&gt;this.$scopedSlots&lt;/code&gt;에서 &lt;code&gt;VNode&lt;/code&gt;를 리턴하는 함수를 사용하여 구현 할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;render (createElement) {
  return createElement(&amp;#39;div&amp;#39;, [
    createElement(&amp;#39;child&amp;#39;, {
      // 데이터 객체의 `scopedSlots`를 다음 형식으로 전달합니다
      // { name: props =&amp;gt; VNode | Array }
      scopedSlots: {
        default: function (props) {
          return createElement(&amp;#39;span&amp;#39;, props.text)
        }
      }
    })
  ])
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 &lt;code&gt;scopedSlots&lt;/code&gt;를 사용하여 자식 컴포넌트로 특정 범위를 가지는 슬롯을 넘겨 줄 수 있습니다.&lt;/p&gt;
&lt;h1&gt;5. JSX&lt;/h1&gt;
&lt;pre&gt;&lt;code&gt;createElement(
  &amp;#39;anchored-heading&amp;#39;, {
    props: {
      level: 1
    }
  }, [
    createElement(&amp;#39;span&amp;#39;, &amp;#39;Hello&amp;#39;),
    &amp;#39; world!&amp;#39;
  ]
)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;render&lt;/code&gt; 함수를 많이 사용하면 위의 코드와 같이 고통스러워 질 수 있습니다... Vue와 JSX를 함께 사용할 수 있는 &lt;a href=&quot;https://github.com/vuejs/babel-plugin-transform-vue-jsx&quot;&gt;Babel plugin&lt;/a&gt;를 이용할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import AnchoredHeading from &amp;#39;./AnchoredHeading.vue&amp;#39;

new Vue({
  el: &amp;#39;#demo&amp;#39;,
  render (h) {
    return (
      &amp;lt;AnchoredHeading level={1}&amp;gt;
        &amp;lt;span&amp;gt;Hello&amp;lt;/span&amp;gt; world!
      &amp;lt;/AnchoredHeading&amp;gt;
    )
  }
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;render&lt;/code&gt; 함수의 전달인자를 보면, &lt;code&gt;createElement&lt;/code&gt;를 &lt;code&gt;h&lt;/code&gt;라는 별칭으로 사용하는 것을 볼 수 있습니다. Vue 생태계에서 주로 사용되는 별칭입니다. &lt;code&gt;render&lt;/code&gt; 함수에서 JSX를 사용할 때, &lt;code&gt;h&lt;/code&gt;가 항상 전달인자로 선언되어 있어야 합니다.&lt;/p&gt;
&lt;h1&gt;6. 함수형 컴포넌트&lt;/h1&gt;
&lt;p&gt;앞에서 이야기 한 &lt;code&gt;&amp;lt;anchored-heading&amp;gt;&lt;/code&gt; 컴포넌트는 단순한 컴포넌트 입니다. 어떤 상태도 없고 상태를 감시할 필요도, 라이프 사이클 관련 메소드도 없습니다. 단지 &lt;code&gt;props&lt;/code&gt;를 가지는 기능만 있습니다. 이런 컴포넌트로 함수형 컴포넌트로 만들 수 있습니다. 즉 컴포넌트의 상태(&lt;code&gt;data&lt;/code&gt;)가 없고, 인스턴스화 (&lt;code&gt;this&lt;/code&gt; 컨텍스트가 없음)할 필요가 없을 때 함수형 컴포넌트를 사용할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Vue.component(&amp;#39;my-component&amp;#39;, {
  functional: true,
  // 인스턴스의 부족함을 보완하기 위해
  // 이제 2번째에 컨텍스트 인수가 제공됩니다.
  render: function (createElement, context) {
    // ...
  },
  // Props는 선택사항입니다.
  props: {
    // ...
  }
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;2.3.0 이전의 버전에서는 함수형 컴포넌트에서 &lt;code&gt;props&lt;/code&gt;를 사용하려면 &lt;code&gt;props&lt;/code&gt; 옵션을 정의해야 했지만, 2.3.0 이상에서는 &lt;code&gt;props&lt;/code&gt; 옵션을 생략할 수 있습니다. 컴포넌트 노드에서 발견된 모든 속성은 암시적으로 &lt;code&gt;props&lt;/code&gt;로 추출됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;template functional&amp;gt;
&amp;lt;/template&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;2.5.0+ 이후에서는 싱글 파일 컴포넌트(&lt;code&gt;.vue&lt;/code&gt; 파일)를 사용할 경우, 위의 코드와 같이 템플릿 기반의 함수형 컴포넌트를 정의할 수 있습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;props&lt;/code&gt;: 전달받은 &lt;code&gt;props&lt;/code&gt; 객체&lt;/li&gt;
&lt;li&gt;&lt;code&gt;children&lt;/code&gt;: &lt;code&gt;VNode&lt;/code&gt; 자식의 배열&lt;/li&gt;
&lt;li&gt;&lt;code&gt;slots&lt;/code&gt;: 슬롯 객체를 반환하는 함수&lt;/li&gt;
&lt;li&gt;&lt;code&gt;data&lt;/code&gt;: 컴포넌트에 전달된 전체 데이터 객체&lt;/li&gt;
&lt;li&gt;&lt;code&gt;parent&lt;/code&gt;: 상위 컴포넌트에 대한 참조&lt;/li&gt;
&lt;li&gt;&lt;code&gt;listeners&lt;/code&gt;: (2.3.0+) 부모에게 등록된 이벤트 리스너 객체 &lt;code&gt;data.on&lt;/code&gt;의 별칭입니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;injections&lt;/code&gt;: (2.3.0+) inject 옵션을 사용하면 resolved injection을 가집니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;함수형 컴포넌트의 &lt;code&gt;render&lt;/code&gt; 함수의 전달인자인 &lt;code&gt;context&lt;/code&gt;는 객체로 위의 값들을 가집니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Vue.component(&amp;#39;anchored-heading&amp;#39;, {
  render: function (createElement) {
    return createElement(
      &amp;#39;h&amp;#39; + this.level,   // 태그 이름
      this.$slots.default // 자식의 배열
    )
  },
  props: {
    level: {
      type: Number,
      required: true
    }
  }
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;앞 부분에서 &lt;code&gt;&amp;lt;achored-heading&amp;gt;&lt;/code&gt; 컴포넌트를 위의 코드와 같이 &lt;code&gt;render&lt;/code&gt; 함수로 구현하였습니다. 위의 컴포넌트를 함수형 컴포넌트로 변경하는 것은 단순합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Vue.component(&amp;#39;anchored-heading&amp;#39;, {
  functional: true,
  render: function (createElement, context) {
    return createElement(
      &amp;#39;h&amp;#39; + context.props.level,   // 태그 이름
      context.children // 자식의 배열
    )
  },
  props: {
    level: {
      type: Number,
      required: true
    }
  }
})&lt;/code&gt;&lt;/pre&gt;&lt;ol&gt;
&lt;li&gt;&lt;code&gt;functional: true&lt;/code&gt; 를 추가합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;render&lt;/code&gt; 함수의 전달인자에 &lt;code&gt;context&lt;/code&gt;를 추가합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;this.$slots.default&lt;/code&gt;를 &lt;code&gt;context.children&lt;/code&gt;으로 변경합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;this.level&lt;/code&gt;을 &lt;code&gt;context.props.level&lt;/code&gt;로 변경합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;위의 코드와 같이 함수형 컴포넌트로 변경 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;함수형 컴포넌트는 단순한 함수이기 때문에 랜더링에 들어가는 비용이 작습니다. 그러나 &lt;a href=&quot;https://github.com/vuejs/vue-devtools&quot;&gt;Vue 크롬 개발자 도구&lt;/a&gt;의 컴포넌트 트리에서 함수형 컴포넌트를 볼 수는 없습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var EmptyList = { /* ... */ }
var TableList = { /* ... */ }
var OrderedList = { /* ... */ }
var UnorderedList = { /* ... */ }

Vue.component(&amp;#39;smart-list&amp;#39;, {
  functional: true,
  render: function (createElement, context) {
    function appropriateListComponent () {
      var items = context.props.items

      if (items.length === 0)           return EmptyList
      if (typeof items[0] === &amp;#39;object&amp;#39;) return TableList
      if (context.props.isOrdered)      return OrderedList

      return UnorderedList
    }

    return createElement(
      appropriateListComponent(),
      context.data,
      context.children
    )
  },
  props: {
    items: {
      type: Array,
      required: true
    },
    isOrdered: Boolean
  }
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;또한 함수형 컴포넌트는 래퍼 컴포넌트로도 유용하게 사용할 수 있습니다.&lt;/p&gt;
&lt;h2&gt;1) &lt;code&gt;slots()&lt;/code&gt; VS &lt;code&gt;children&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;slots().default&lt;/code&gt;와 &lt;code&gt;children&lt;/code&gt;은 유사하지만 다릅니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;my-functional-component&amp;gt;
  &amp;lt;p slot=&amp;quot;foo&amp;quot;&amp;gt;
    first
  &amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;second&amp;lt;/p&amp;gt;
&amp;lt;/my-functional-component&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 함수형 컴포넌트에 두개의 자식 엘리먼트들이 있을 때, &lt;code&gt;children&lt;/code&gt;은 두개의 단란을 반환하고, &lt;code&gt;slots().default&lt;/code&gt;는 두번째 단락을 반환합니다.&lt;/p&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://kr.vuejs.org/v2/guide/render-function.html&quot;&gt;https://kr.vuejs.org/v2/guide/render-function.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>createElement</category>
      <category>Functional Component</category>
      <category>JSX</category>
      <category>render</category>
      <category>virtual dom</category>
      <category>vnode</category>
      <category>Vue.js</category>
      <category>함수형 컴포넌트</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/63</guid>
      <comments>https://beomy.tistory.com/63#entry63comment</comments>
      <pubDate>Thu, 14 Feb 2019 00:07:47 +0900</pubDate>
    </item>
    <item>
      <title>[Vue.JS] 사용자 정의 디렉티브</title>
      <link>https://beomy.tistory.com/62</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;820&quot; height=&quot;289&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/995424465C5AC8E025?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/995424465C5AC8E025?original&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/995424465C5AC8E025&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F995424465C5AC8E025&quot; width=&quot;820&quot; height=&quot;289&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;h1&gt;1. 시작&lt;/h1&gt;
&lt;p&gt;기본 디렉티브 (&lt;code&gt;v-model&lt;/code&gt;, &lt;code&gt;v-show&lt;/code&gt; 등..) 외에도 사용자가 직접 정의한 디렉티브를 등록 할 수 있습니다. 기본적으로 Vue 2.0의 코드 재사용 및 추상화의 기본 형식은 컴포넌트 입니다. 하지만 일반 엘리먼트의 DOM에 접근이 필요한 경우 사용자 정의 디렉티브가 유용하게 사용 될 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 전역 사용자 정의 디렉티브 v-focus 등록
Vue.directive(&amp;#39;focus&amp;#39;, {
  // 바인딩 된 엘리먼트가 DOM에 삽입되었을 때...
  inserted: function (el) {
    // 엘리먼트에 포커스를 줍니다
    el.focus()
  }
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드는 페이지가 로드되면 해달 엘리먼트에 포커스를 주는 디렉티브 입니다. (참고, 모바일 사파리에서도는 동작하지 않습니다.)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;directives: {
  focus: {
    // 디렉티브 정의
    inserted: function (el) {
      el.focus()
    }
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;디렉티브를 로컬로 등록하기 위해서는 컴포넌트의 옵션에 &lt;code&gt;directives&lt;/code&gt; 옵션을 사용해야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;input v-focus&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;이렇게 정의 된 focus 디렉티브는 위의 코드와 같이 &lt;code&gt;v-focus&lt;/code&gt;로 사용할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;2. 훅 함수&lt;/h1&gt;
&lt;p&gt;디렉티브를 정의 할 때, 여러가지 훅 함수를 제공합니다. 훅 함수는 모두 선택사항입니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;bind&lt;/code&gt;: 디렉티브가 처음 엘리먼트에 바인딩 될 때 한번만 호출됩니다. 이 훅 함수는 일회성 설정을 하는데 유용합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;inserted&lt;/code&gt;: 바인딩 된 엘리먼트가 부모 노드에 삽일 되었을 때 호출됩니다. 부모 노드 존재를 보장하며, 반드시 document 내에 있는 것은 아닙니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;update&lt;/code&gt;: 디렉티브를 포함하는 컴포넌트의 VNode가 업데이트 되었을 때 호출됩니다. 그러나 자식이 업데이트 전일 가능성이 있습니다. 이 훅이 실행 되었을 때는 디렉티브의 값이 변경되었을 수도 변경되지 않았을 수도 있습니다. 디렉티브의 현재 값과 이전 값을 비교하여 불필요한 업데이트를 건너 뛸 수 있습니다. (VNode에 대해서는 다음 포스팅 할 render functions에서 이야기 할 것입니다.)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;componentUpdated&lt;/code&gt;: 포함하고 있는 컴포넌트의 VNode, 그것들의 자식의 VNode가 업데이트 되었을 때 호출됩니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;unbind&lt;/code&gt;: 디렉티브가 엘리먼트로부터 언바인딩 된 경우에만 한번 호출됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;3. 디렉티브 훅 전달인자&lt;/h1&gt;
&lt;p&gt;디렉티브 훅은 전달 인자을 가집니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;el&lt;/code&gt;: 디렉티브가 바인딩 되는 엘리먼트입니다. 이 인자를 통해 DOM을 조작할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;binding&lt;/code&gt;: 아래의 속성들을 가진 객체입니다.  &lt;ul&gt;
&lt;li&gt;&lt;code&gt;name&lt;/code&gt;: 디렉티브 이름, prefix(&lt;code&gt;v-&lt;/code&gt;)가 없습니다.   &lt;/li&gt;
&lt;li&gt;&lt;code&gt;value&lt;/code&gt;: 디렉티브에서 전달 받은 값, 예를 들면 &lt;code&gt;v-my-directive=&amp;quot;1+1&amp;quot;&lt;/code&gt;인 경우 &lt;code&gt;value&lt;/code&gt;는 2입니다.  &lt;/li&gt;
&lt;li&gt;&lt;code&gt;oldValue&lt;/code&gt;: 이전 값, &lt;code&gt;update&lt;/code&gt;와 &lt;code&gt;componentUpdated&lt;/code&gt;에서만 사용할 수 있습니다. 이 값을 통해 디렉티브로 전달 받은 값이 변경 되었는지 확인 할 수 있습니다.  &lt;/li&gt;
&lt;li&gt;&lt;code&gt;expression&lt;/code&gt;: 표현식 문자열, 예를 들면 &lt;code&gt;v-my-directive=&amp;quot;1+1&amp;quot;&lt;/code&gt;인 경우 &lt;code&gt;expression&lt;/code&gt;은 &lt;code&gt;&amp;quot;1+1&amp;quot;&lt;/code&gt;입니다.  &lt;/li&gt;
&lt;li&gt;&lt;code&gt;arg&lt;/code&gt;: 디렉티브의 전달인자, 전달인자가 있는 경우에만 존재합니다. 예를 들면, &lt;code&gt;v-my-directive:foo&lt;/code&gt;이면 &lt;code&gt;&amp;quot;foo&amp;quot;&lt;/code&gt;가 됩니다. &lt;/li&gt;
&lt;li&gt;&lt;code&gt;modifiers&lt;/code&gt;: 디렉티브의 수식어 객체, 수식어가 있는 경우에만 존재합니다. 예를 들면, &lt;code&gt;v-my-directive.foo.bar&lt;/code&gt;이면, 수식어 객체는 &lt;code&gt;{foo: true, bar: true}&lt;/code&gt; 입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;vnode&lt;/code&gt;: Vue 컴파일러가 만든 가상 노드&lt;/li&gt;
&lt;li&gt;&lt;code&gt;oldVnode&lt;/code&gt;: 이전 가상 노드, &lt;code&gt;update&lt;/code&gt;와 &lt;code&gt;componentUpdated&lt;/code&gt;에서만 사용할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;el&lt;/code&gt; 뿐만 아니라 모든 전달인자는 readonly로 사용해야 합니다. 절대 변경하면 안됩니다. 훅을 통해 이 정보들을 전달해야 하는 경우 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset&quot;&gt;dataset&lt;/a&gt;를 사용하면 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;hook-arguments-example&amp;quot; v-demo:foo.a.b=&amp;quot;message&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;Vue.directive(&amp;#39;demo&amp;#39;, {
  bind: function (el, binding, vnode) {
    var s = JSON.stringify
    el.innerHTML =
      &amp;#39;name: &amp;#39;       + s(binding.name) + &amp;#39;&amp;lt;br&amp;gt;&amp;#39; +
      &amp;#39;value: &amp;#39;      + s(binding.value) + &amp;#39;&amp;lt;br&amp;gt;&amp;#39; +
      &amp;#39;expression: &amp;#39; + s(binding.expression) + &amp;#39;&amp;lt;br&amp;gt;&amp;#39; +
      &amp;#39;argument: &amp;#39;   + s(binding.arg) + &amp;#39;&amp;lt;br&amp;gt;&amp;#39; +
      &amp;#39;modifiers: &amp;#39;  + s(binding.modifiers) + &amp;#39;&amp;lt;br&amp;gt;&amp;#39; +
      &amp;#39;vnode keys: &amp;#39; + Object.keys(vnode).join(&amp;#39;, &amp;#39;)
  }
})

new Vue({
  el: &amp;#39;#hook-arguments-example&amp;#39;,
  data: {
    message: &amp;#39;hello!&amp;#39;
  }
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;디렉티브 사용 예제&lt;/p&gt;
&lt;h1&gt;4. 함수 약어&lt;/h1&gt;
&lt;p&gt;대부분의 경우 &lt;code&gt;bind&lt;/code&gt;와 &lt;code&gt;update&lt;/code&gt;에서 같은 동작을 할 때가 있습니다. 다른 훅은 신경쓰지 않고, &lt;code&gt;bind&lt;/code&gt;와 &lt;code&gt;update&lt;/code&gt;에서만 동작을 하는 약어를 제공합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Vue.directive(&amp;#39;color-swatch&amp;#39;, function (el, binding) {
  el.style.backgroundColor = binding.value
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;함수 약어 사용 예제&lt;/p&gt;
&lt;h1&gt;5. 객체 리터럴&lt;/h1&gt;
&lt;p&gt;디렉티브에 여러 값이 필요한 경우, JavaScript 객체를 전달 할 수 있습니다. 디렉티브에 JavaScript 표현식을 사용할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div v-demo=&amp;quot;{ color: &amp;#39;white&amp;#39;, text: &amp;#39;hello!&amp;#39; }&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;Vue.directive(&amp;#39;demo&amp;#39;, function (el, binding) {
  console.log(binding.value.color) // =&amp;gt; &amp;quot;white&amp;quot;
  console.log(binding.value.text)  // =&amp;gt; &amp;quot;hello!&amp;quot;
})&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://kr.vuejs.org/v2/guide/custom-directive.html&quot;&gt;https://kr.vuejs.org/v2/guide/custom-directive.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>bind</category>
      <category>componentUpdated</category>
      <category>Directive</category>
      <category>inserted</category>
      <category>unbind</category>
      <category>Update</category>
      <category>vnode</category>
      <category>Vue.js</category>
      <category>디렉티브</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/62</guid>
      <comments>https://beomy.tistory.com/62#entry62comment</comments>
      <pubDate>Thu, 7 Feb 2019 23:07:09 +0900</pubDate>
    </item>
    <item>
      <title>[Vue.JS] Mixins</title>
      <link>https://beomy.tistory.com/61</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;820&quot; height=&quot;289&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/9987EA3C5C55A0E81D?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/9987EA3C5C55A0E81D?original&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9987EA3C5C55A0E81D&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9987EA3C5C55A0E81D&quot; width=&quot;820&quot; height=&quot;289&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;h1&gt;1. 기초&lt;/h1&gt;
&lt;p&gt;Minxins는 컴포넌트에 재사용 가능한 기능을 배포하는 방법입니다. &lt;code&gt;mixins&lt;/code&gt; 객체는 컴포넌트의 옵션에 정의됩니다. &lt;code&gt;mixins&lt;/code&gt;를 사용하면 해당 mixin의 모든 옵션과 컴포넌트의 옵션들이 &amp;#39;Mixed&amp;#39; 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// define a mixin object
var myMixin = {
  created: function () {
    this.hello()
  },
  methods: {
    hello: function () {
      console.log(&amp;#39;hello from mixin!&amp;#39;)
    }
  }
}

// define a component that uses this mixin
var Component = Vue.extend({
  mixins: [myMixin]
})

var component = new Component() // =&amp;gt; &amp;quot;hello from mixin!&amp;quot;&lt;/code&gt;&lt;/pre&gt;&lt;h1&gt;2. 옵션 병합&lt;/h1&gt;
&lt;p&gt;mixin과 컴포넌트의 옵션이 중첩이 된다면, 두 옵션은 &amp;#39;Merged&amp;#39; 됩니다.&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;data&lt;/code&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;var mixin = {
  data: function () {
    return {
      message: &amp;#39;hello&amp;#39;,
      foo: &amp;#39;abc&amp;#39;
    }
  }
}

new Vue({
  mixins: [mixin],
  data: function () {
    return {
      message: &amp;#39;goodbye&amp;#39;,
      bar: &amp;#39;def&amp;#39;
    }
  },
  created: function () {
    console.log(this.$data)
    // =&amp;gt; { message: &amp;quot;goodbye&amp;quot;, foo: &amp;quot;abc&amp;quot;, bar: &amp;quot;def&amp;quot; }
  }
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;data&lt;/code&gt; 객체는 재귀적 병합을 하며, mixin과 컴포넌트의 &lt;code&gt;data&lt;/code&gt;가 충돌 될 경우, 컴포넌트의 &lt;code&gt;data&lt;/code&gt;가 우선 순위를 갖습니다.&lt;/p&gt;
&lt;h2&gt;라이프 사이클 훅&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;var mixin = {
  created: function () {
    console.log(&amp;#39;mixin hook called&amp;#39;)
  }
}

new Vue({
  mixins: [mixin],
  created: function () {
    console.log(&amp;#39;component hook called&amp;#39;)
  }
})

// =&amp;gt; &amp;quot;mixin hook called&amp;quot;
// =&amp;gt; &amp;quot;component hook called&amp;quot;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;동일한 이름을 가진 라이프 사이클 훅 함수는 배열로 합쳐져 모두 호출됩니다. mixin의 훅 함수가 먼저 호출 되고, 그 후 컴포넌트의 훅 함수가 호출 됩니다.&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;methods&lt;/code&gt;, &lt;code&gt;components&lt;/code&gt;, &lt;code&gt;directives&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;methods&lt;/code&gt;와 &lt;code&gt;components&lt;/code&gt;, &lt;code&gt;directives&lt;/code&gt; 옵션들은 하나의 객체로 머지 됩니다. mixin와 컴포넌트의 옵션이 충돌 된다면 컴포넌트의 옵션이 더 높은 우선순위를 가집니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var mixin = {
  methods: {
    foo: function () {
      console.log(&amp;#39;foo&amp;#39;)
    },
    conflicting: function () {
      console.log(&amp;#39;from mixin&amp;#39;)
    }
  }
}

var vm = new Vue({
  mixins: [mixin],
  methods: {
    bar: function () {
      console.log(&amp;#39;bar&amp;#39;)
    },
    conflicting: function () {
      console.log(&amp;#39;from self&amp;#39;)
    }
  }
})

vm.foo() // =&amp;gt; &amp;quot;foo&amp;quot;
vm.bar() // =&amp;gt; &amp;quot;bar&amp;quot;
vm.conflicting() // =&amp;gt; &amp;quot;from self&amp;quot;&lt;/code&gt;&lt;/pre&gt;&lt;h1&gt;3. 전역 Mixin&lt;/h1&gt;
&lt;p&gt;mixin을 global 하도록 정의할 수 있습니다. mixin을 global로 정의하면, 모든 Vue 인스턴스에 mixin이 적용되어 영향을 미치기 때문에, 주의해서 사용해야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// inject a handler for `myOption` custom option
Vue.mixin({
  created: function () {
    var myOption = this.$options.myOption
    if (myOption) {
      console.log(myOption)
    }
  }
})

new Vue({
  myOption: &amp;#39;hello!&amp;#39;
})
// =&amp;gt; &amp;quot;hello!&amp;quot;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 전역 Mixin은 사용자 정의 옵션을 처리하는데 유용하게 사용 될 수 있습니다. 전역 Mixin은 모든 Vue 인스턴스에 영향을 주기 때문에 신중하게 사용되어야 합니다. 위의 예제와 같이 사용자 지정 옵션 처리에만 사용하는 것이 좋습니다. 중복 적용을 피하기 위해 앞으로 이야기 할 Plugins을 사용하는 것도 좋은 방법입니다.&lt;/p&gt;
&lt;h1&gt;4. 사용자 정의 옵션 병합 전략&lt;/h1&gt;
&lt;p&gt;사용자 지정 옵션을 병합하면, 기본값으로 기존 값을 덮어 쓰게 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Vue.config.optionMergeStrategies.myOption = function (toVal, fromVal) {
  // return mergedVal
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 &lt;code&gt;Vue.config.optionMergeStrategies&lt;/code&gt;를 사용하면, 사용자 정의에 따라 병합됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var strategies = Vue.config.optionMergeStrategies
strategies.myOption = strategies.methods&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 대부분의 경우 object-based 옵션은, &lt;code&gt;methods&lt;/code&gt;를 사용하여 전략을 간당하게 적용할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const merge = Vue.config.optionMergeStrategies.computed
Vue.config.optionMergeStrategies.vuex = function (toVal, fromVal) {
  if (!toVal) return fromVal
  if (!fromVal) return toVal
  return {
    getters: merge(toVal.getters, fromVal.getters),
    state: merge(toVal.state, fromVal.state),
    actions: merge(toVal.actions, fromVal.actions)
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제 코드는 Vuex의 1.x 버전의 병합 전략을 나타낸 예제입니다.&lt;/p&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://vuejs.org/v2/guide/mixins.html&quot;&gt;https://vuejs.org/v2/guide/mixins.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>mixins</category>
      <category>Vue.config.optionMergeStrategies</category>
      <category>Vue.js</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/61</guid>
      <comments>https://beomy.tistory.com/61#entry61comment</comments>
      <pubDate>Tue, 5 Feb 2019 00:23:55 +0900</pubDate>
    </item>
    <item>
      <title>[Vue.JS] 컴포넌트 (고급:Handling Edge Cases)</title>
      <link>https://beomy.tistory.com/60</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;820&quot; height=&quot;289&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/9920813A5C506B9227?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/9920813A5C506B9227?original&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9920813A5C506B9227&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9920813A5C506B9227&quot; width=&quot;820&quot; height=&quot;289&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;h1&gt;1. Element &amp;amp; Component Access&lt;/h1&gt;
&lt;p&gt;대부분의 경우 다른 컴포넌트의 인스턴스에 접근하거나 DOM을 직접 조작하는 것은 피해야 합니다. 그러나 때로는 다른 컴포넌트의 인스턴스에 접근하거나 DOM을 직접 조작해야 할 때가 있습니다.&lt;/p&gt;
&lt;h2&gt;1) 루트 인스턴스 접근&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;$root&lt;/code&gt;를 사용하면 루트 인스턴스에 접근 가능합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// The root Vue instance
new Vue({
  data: {
    foo: 1
  },
  computed: {
    bar: function () { /* ... */ }
  },
  methods: {
    baz: function () { /* ... */ }
  }
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 루트 인스턴스가 있을 때,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Get root data
this.$root.foo

// Set root data
this.$root.foo = 2

// Access root computed properties
this.$root.bar

// Call root methods
this.$root.baz()&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;모든 자식 컴포넌트는 위의 코드와 같이 루트 엘리먼트에 접근하여 루트 인스턴스의 값들을 사용할 수 있습니다.&lt;/p&gt;
&lt;h2&gt;2) 부모 컴포넌트 인스턴스 접근&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;$root&lt;/code&gt;와 유사하게 &lt;code&gt;$parent&lt;/code&gt;를 사용하면 부모 인스턴스에 접근 가능합니다. &lt;code&gt;$parent&lt;/code&gt;를 사용하여 부모 컴포넌트의 데이터를 변경할 경우, 어플리케이션이 커진다면, 어느 자식 컴포넌트에서 데이터를 변경하였는지 알 수 없어 디버깅하기 더 어려워 질 수 있습니다. 하지만 몇몇의 라이브러리들을 사용할 때 부모 인스턴스를 접근하는 것이 유용할 때가 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;google-map&amp;gt;
  &amp;lt;google-map-markers v-bind:places=&amp;quot;iceCreamShops&amp;quot;&amp;gt;&amp;lt;/google-map-markers&amp;gt;
&amp;lt;/google-map&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;예를 들어, 위의 코드와 같이 Google Maps 라이브러리를 사용하여 컴포넌트를 만든다고 할 때, &lt;code&gt;&amp;lt;google-map&amp;gt;&lt;/code&gt; 컴포넌트는 map 프로퍼티로 맵 정보를 가지고 있고, 자식 컴포넌트인 &lt;code&gt;&amp;lt;google-map-markers&amp;gt;&lt;/code&gt; 컴포넌트가 부모 컴포넌트인 &lt;code&gt;&amp;lt;google-map&amp;gt;&lt;/code&gt; 컴포넌트의 맵 정보를 가져와야 할 때, &lt;code&gt;this.$parent.getMap&lt;/code&gt; 을 이용하여 맵 정보를 가져 올 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;google-map&amp;gt;
  &amp;lt;google-map-region v-bind:shape=&amp;quot;cityBoundaries&amp;quot;&amp;gt;
    &amp;lt;google-map-markers v-bind:places=&amp;quot;iceCreamShops&amp;quot;&amp;gt;&amp;lt;/google-map-markers&amp;gt;
  &amp;lt;/google-map-region&amp;gt;
&amp;lt;/google-map&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제와 같이 &lt;code&gt;&amp;lt;google-map-region&amp;gt;&lt;/code&gt; 이라는 컴포넌트가 &lt;code&gt;&amp;lt;google-map&amp;gt;&lt;/code&gt;과 &lt;code&gt;&amp;lt;google-map-markers&amp;gt;&lt;/code&gt; 사이에 사용될 수도 있다고 했을 때(선택적으로 사용되지 않을 수도 있음), &lt;code&gt;&amp;lt;google-map-markers&amp;gt;&lt;/code&gt; 컴포넌트는 &lt;code&gt;&amp;lt;google-map&amp;gt;&lt;/code&gt; 컴포넌트의 맵 정보를,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var map = this.$parent.map || this.$parent.$parent.map&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 사용할 수 있습니다.&lt;/p&gt;
&lt;h2&gt;3) 자식 컴포넌트 인스턴스, 자식 엘리먼트 접근&lt;/h2&gt;
&lt;p&gt;때로, 직접 자식 컴포넌트에 직접 접근해야 할 때가 있습니다. &lt;code&gt;ref&lt;/code&gt;를 사용하면 자식 컴포넌트에 접근이 가능합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;base-input ref=&amp;quot;usernameInput&amp;quot;&amp;gt;&amp;lt;/base-input&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 부모 컴포넌트에서 자식 컴포넌트를 선언하여 사용한 후 &lt;code&gt;ref&lt;/code&gt;를 정의 했다면,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;this.$refs.usernameInput&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 자식 컴포넌트에 직접 접근이 가능합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;input ref=&amp;quot;input&amp;quot;&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;또한 위의 코드와 같이, &lt;code&gt;ref&lt;/code&gt;를 사용하여 기본 엘리먼트에 접근도 가능합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Vue.component(&amp;#39;base-input&amp;#39;, {
  template: &amp;#39;&amp;lt;input ref=&amp;quot;input&amp;quot;&amp;gt;&amp;#39;,
  methods: {
    // Used to focus the input from the parent
    focus: function () {
      this.$refs.input.focus()
    }
  }
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;자식 컴포넌트가 위의 코드과 같이 작성되었다면,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;this.$refs.usernameInput.focus()&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;부모 컴포넌트에서는 위의 코드를 사용하여 자식 컴포넌트의 특정 엘리먼트에 접근도 가능해 집니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;ul&amp;gt;
  &amp;lt;li v-for=&amp;quot;num of len&amp;quot; :key=&amp;quot;num&amp;quot; ref=&amp;quot;refArray&amp;quot;&amp;gt;{{ num }}&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;ref&lt;/code&gt;는 &lt;code&gt;v-for&lt;/code&gt;와 같이 사용할 수 있습니다. 위의 코드와 같이 &lt;code&gt;ref&lt;/code&gt;와 &lt;code&gt;v-for&lt;/code&gt;을 함께 사용한다면,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{refArray: Array(...)}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;this.$refs&lt;/code&gt;의 값은 위의 코드와 같이 key는 &lt;code&gt;refArray&lt;/code&gt;이고 value는 &lt;code&gt;Array&lt;/code&gt;가 됩니다.&lt;/p&gt;
&lt;h4&gt;참고 - &lt;code&gt;$refs&lt;/code&gt; 사용시 유의 사항&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;$refs&lt;/code&gt;는 랜더링 됭 후 값이 채워집니다. 또한 &lt;code&gt;$refs&lt;/code&gt;는 반응적이지 않습니다.(&lt;code&gt;$refs&lt;/code&gt;가 변경 되어도 &lt;code&gt;watch&lt;/code&gt;, &lt;code&gt;computed&lt;/code&gt; 등으로 감지 하지 못함) 그렇기 때문에 &lt;code&gt;template&lt;/code&gt;에서나 &lt;code&gt;computed&lt;/code&gt;에서 &lt;code&gt;$refs&lt;/code&gt;를 사용하는 것은 피해야 합니다.&lt;/p&gt;
&lt;h2&gt;4) 의존성 주입&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;google-map&amp;gt;
  &amp;lt;google-map-region v-bind:shape=&amp;quot;cityBoundaries&amp;quot;&amp;gt;
    &amp;lt;google-map-markers v-bind:places=&amp;quot;iceCreamShops&amp;quot;&amp;gt;&amp;lt;/google-map-markers&amp;gt;
  &amp;lt;/google-map-region&amp;gt;
&amp;lt;/google-map&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드는 부모 컴포넌트 인스턴스 접근에서 다루었던 코드입니다. &lt;code&gt;&amp;lt;google-map&amp;gt;&lt;/code&gt; 컴포넌트의 하위 컴포넌트에서 &lt;code&gt;&amp;lt;google-map&amp;gt;&lt;/code&gt; 컴포넌트의 &lt;code&gt;getMap&lt;/code&gt; 메소드를 접근하려면 &lt;code&gt;$parent&lt;/code&gt;를 사용했습니다. &lt;code&gt;provide&lt;/code&gt;와 &lt;code&gt;inject&lt;/code&gt;를 사용한 의존성 주입을 통해 위의 방법을 구현 할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;provide: function () {
  return {
    getMap: this.getMap
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;&amp;lt;google-map&amp;gt;&lt;/code&gt; 컴포넌트에서는 하위 컴포넌트에서 사용할 수 있게 의존성을 주입할 메소드를 위의 코드와 깉이 정의해야 합니다. 위의 예제에서는 &lt;code&gt;getMap&lt;/code&gt;을 하위 컴포넌트에서 사용할 수 있도록 할 것입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;inject: [&amp;#39;getMap&amp;#39;]&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;하위 컴포넌트에서는 위의 코드와 같이 의정성 주입을 한 메소드를 사용할 수 있습니다. 위의 예제는 &lt;a href=&quot;https://jsfiddle.net/chrisvfritz/tdv8dt3s/&quot;&gt;Fiddle&lt;/a&gt;에서 확인 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;위의 코드와 같이 의존성 주입을 사용하면 &lt;code&gt;$parent&lt;/code&gt;를 사용하지 않고 모든 하위 컴포넌트에서 의존성 주입한 메소드를 사용할 수 있습니다. (위의 코드에서는 &lt;code&gt;getMap&lt;/code&gt; 메소드) 의존성 주입을 사용하면 몇가지 장점이 있습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;부모 컴포넌트는 어떤 하위 컴포넌트에서 의존성 주입한 메소드를 사용하는지 알 필요가 없습니다.&lt;/li&gt;
&lt;li&gt;하위 컴포넌트는 어떤 부모 컴포넌트에서 메소드를 의전송 주입을 하였는지 알 필요가 없습니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$parent&lt;/code&gt;는 전체 인스턴스를 노출시키지만, 의존성 주입을 사용하면 전체 인스턴스를 노출하지 않을 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;2. Programmatic Event Listeners&lt;/h1&gt;
&lt;p&gt;지금까지는 &lt;code&gt;$emit&lt;/code&gt;으로 이벤트를 발생시키고 &lt;code&gt;v-on&lt;/code&gt;으로 발생시킨 이벤트를 감지하는 형태를 사용하였습니다. Vue는 또 다른 이벤트 인터페이스를 제공합니다. 이 방법을 programmatic event listener라 이야기 하도록 하겠습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;$on(eventName, eventHandler)&lt;/code&gt; : &lt;code&gt;eventName&lt;/code&gt;이라는 이벤트를 감지하여 이벤트가 발생할 경우 &lt;code&gt;eventHandler&lt;/code&gt;를 실행합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$once(eventName, eventHandler)&lt;/code&gt; : &lt;code&gt;eventName&lt;/code&gt;이라는 이벤트를 한번만 감지하여 &lt;code&gt;eventHandler&lt;/code&gt;를 실행합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$off(eventName, eventHandler)&lt;/code&gt; : &lt;code&gt;eventName&lt;/code&gt;이라는 이벤트 감지를 해제하여 &lt;code&gt;eventHandler&lt;/code&gt;를 실행합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;위의 이벤트 리스너들은 일반적인 경우 사용할 필요는 없지만 컴포넌트의 인스턴스에서 수동으로 이벤트를 감지해야 할 때 주로 사용됩니다. 다른 서드 파트 라이브러리를 사용할 때 유용하게 사용할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Attach the datepicker to an input once
// it&amp;#39;s mounted to the DOM.
mounted: function () {
  // Pikaday is a 3rd-party datepicker library
  this.picker = new Pikaday({
    field: this.$refs.input,
    format: &amp;#39;YYYY-MM-DD&amp;#39;
  })
},
// Right before the component is destroyed,
// also destroy the datepicker.
beforeDestroy: function () {
  this.picker.destroy()
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드는 2가지 잠제적인 이슈가 있습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;라이프 사이클 훅에서 &lt;code&gt;picker&lt;/code&gt;를 사용해야 할 경우, &lt;code&gt;picker&lt;/code&gt;를 컴포넌트 인스턴스에 저장해야 합니다.&lt;/li&gt;
&lt;li&gt;초기화 코드와, 해제 코드가 분리되어 있어, 코드 분석이 어려울 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;위의 2가지 이슈를 programmatic event listener를 사용하여 해결 할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mounted: function () {
  var picker = new Pikaday({
    field: this.$refs.input,
    format: &amp;#39;YYYY-MM-DD&amp;#39;
  })

  this.$once(&amp;#39;hook:beforeDestroy&amp;#39;, function () {
    picker.destroy()
  })
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위와 같은 방법을 사용하면 &lt;code&gt;pikaday&lt;/code&gt;를 간편하게 여러번 사용할 수 있게 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mounted: function () {
  this.attachDatepicker(&amp;#39;startDateInput&amp;#39;)
  this.attachDatepicker(&amp;#39;endDateInput&amp;#39;)
},
methods: {
  attachDatepicker: function (refName) {
    var picker = new Pikaday({
      field: this.$refs[refName],
      format: &amp;#39;YYYY-MM-DD&amp;#39;
    })

    this.$once(&amp;#39;hook:beforeDestroy&amp;#39;, function () {
      picker.destroy()
    })
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 전체 예제 코드는 &lt;a href=&quot;https://jsfiddle.net/chrisvfritz/1Leb7up8/&quot;&gt;Fiddle&lt;/a&gt;에서 확인 할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;3. 순환 참조&lt;/h1&gt;
&lt;h2&gt;1) 재귀 컴포넌트&lt;/h2&gt;
&lt;p&gt;컴포넌트는 자신의 &lt;code&gt;template&lt;/code&gt;에서 자신을 반복적으로 호출 할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;name: &amp;#39;stack-overflow&amp;#39;,
template: &amp;#39;&amp;lt;div&amp;gt;&amp;lt;stack-overflow&amp;gt;&amp;lt;/stack-overflow&amp;gt;&amp;lt;/div&amp;gt;&amp;#39;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;주의하지 않으면 재귀 컴포넌트는 무한 루프를 만들 수도 있습니다. 그렇기 때문에 재귀 컴포넌트를 사용할 때 &lt;code&gt;v-if&lt;/code&gt; 등을 사용하는 조건부 재귀 컴포넌트 인지 살펴보아야 합니다.&lt;/p&gt;
&lt;h2&gt;2) 컴포넌트간의 순환 참조&lt;/h2&gt;
&lt;p&gt;파일 탐색기와 같이 파일 디렉토리 드리를 작성해 보도록 하겠습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;p&amp;gt;
  &amp;lt;span&amp;gt;{{ folder.name }}&amp;lt;/span&amp;gt;
  &amp;lt;tree-folder-contents :children=&amp;quot;folder.children&amp;quot;/&amp;gt;
&amp;lt;/p&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 &lt;code&gt;tree-folder&lt;/code&gt;라는 컴포넌트를 만듭니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;ul&amp;gt;
  &amp;lt;li v-for=&amp;quot;child in children&amp;quot;&amp;gt;
    &amp;lt;tree-folder v-if=&amp;quot;child.children&amp;quot; :folder=&amp;quot;child&amp;quot;/&amp;gt;
    &amp;lt;span v-else&amp;gt;{{ child.name }}&amp;lt;/span&amp;gt;
  &amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;tree-folder-contents&lt;/code&gt; 컴포넌트는 위의 코드와 같습니다.&lt;/p&gt;
&lt;p&gt;위의 코드를 살펴보면, 두 컴포넌트는 서로가 서로의 자식 컴포넌트이지 부모 컴포넌트입니다. 여기서 역설이 발생합니다. &lt;code&gt;tree-folder&lt;/code&gt; 컴포넌트가 랜더링 되기 위해서는 &lt;code&gt;tree-folder-contents&lt;/code&gt; 컴포넌트가 필요하고, &lt;code&gt;tree-folder-contents&lt;/code&gt; 컴포넌트가 랜더링 되기 위해서는 &lt;code&gt;tree-folder&lt;/code&gt; 컴포넌트가 필요합니다.&lt;/p&gt;
&lt;h3&gt;- 전역으로 컴포넌트 선언하여 순환 참조하기&lt;/h3&gt;
&lt;p&gt;이 역설은 &lt;code&gt;Vue.component&lt;/code&gt;를 사용하여 전역으로 컴포넌트를 등록하면 해결 됩니다.&lt;/p&gt;
&lt;p&gt;그러나 Webpack이나 Browserify등의 모듈러를 통해 지역으로 컴포넌트를 등록할 경우,&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Failed to mount component: template or render function not defined.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;위의 에러가 발생합니다. 위 에러를 해결하기 위한 방법으로 2가지가 있습니다.&lt;/p&gt;
&lt;h3&gt;- 지역으로 컴포넌트 선언하여 순환 참조하기&lt;/h3&gt;
&lt;p&gt;첫번째 방법은 &lt;code&gt;tree-folder&lt;/code&gt; 컴포넌트를 시작점으로 잡고,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;beforeCreate: function () {
  this.$options.components.TreeFolderContents = require(&amp;#39;./tree-folder-contents.vue&amp;#39;).default
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 &lt;code&gt;beforeCreate&lt;/code&gt; 라이프 사이클 훅을 사용하여 동적으로 컴포넌트를 등록하는 방법이 있습니다.&lt;/p&gt;
&lt;p&gt;두번째 방법은 비동기 컴포넌트를 사용하는 방법이 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;components: {
  TreeFolderContents: () =&amp;gt; import(&amp;#39;./tree-folder-contents.vue&amp;#39;)
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 비동기 컴포넌트를 사용하여 위에서 말한 에러를 해결 할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;4. Alternate Template Definitions&lt;/h1&gt;
&lt;h2&gt;1) Inline Templates&lt;/h2&gt;
&lt;p&gt;하위 컴포넌트에 &lt;code&gt;inline-template&lt;/code&gt; 속성을 사용 할 때, 컴포넌트의 내용은 그 하위 컴포넌트의 내용이 됩니다. 보다 유연한 템플릿 작성을 가능하게 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;my-component inline-template&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;p&amp;gt;These are compiled as the component&amp;#39;s own template.&amp;lt;/p&amp;gt;
    &amp;lt;p&amp;gt;Not parent&amp;#39;s transclusion content.&amp;lt;/p&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/my-component&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;inline-template&lt;/code&gt;는 템플릿의 범위를 알아보기 힘들게 만듭니다. 그렇기 때문에 보통은 &lt;code&gt;template&lt;/code&gt; 옵션을 사용하거나 &lt;code&gt;.vue&lt;/code&gt; 파일의 &lt;code&gt;template&lt;/code&gt; 엘리먼트를 사용하는 것이 좋습니다.&lt;/p&gt;
&lt;h2&gt;2) X-Templates&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;script type=text/x-template&amp;quot;&amp;gt;&lt;/code&gt;를 사용하여 템플릿을 정의하는 방법도 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;script type=&amp;quot;text/x-template&amp;quot; id=&amp;quot;hello-world-template&amp;quot;&amp;gt;
  &amp;lt;p&amp;gt;Hello hello hello&amp;lt;/p&amp;gt;
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;Vue.component(&amp;#39;hello-world&amp;#39;, {
  template: &amp;#39;#hello-world-template&amp;#39;
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같은 방법으로 사용할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;5. Controlling Updates&lt;/h1&gt;
&lt;p&gt;Vue의 반응형 시스템의 도움으로 언제 업데이트 되는지 알 수 있습니다. 그러나 반응형 데이터가 변경되지 않았음에도 불구하고 업데이트를 해야 할 경우나, 불필요한 업데이트를 막을 수 있는 방법도 있습니다.&lt;/p&gt;
&lt;h2&gt;1) 강제 업데이트&lt;/h2&gt;
&lt;p&gt;대부분의 경우 강제 업데이트 할 필요가 없습니다. 하지만 &lt;a href=&quot;https://beomy.tistory.com/52#array-caveats&quot;&gt;Array 데이터 주의 사항&lt;/a&gt;이나 &lt;a href=&quot;https://beomy.tistory.com/52#object-caveats&quot;&gt;Object 데이터 주의 사항&lt;/a&gt;을 지키지 않았을 경우 강제 업데이트를 해야 할 때가 있습니다. 이 경우 수동으로 업데이트를 해야 하는데 &lt;code&gt;$forceUpdate&lt;/code&gt;를 사용하여 강제 업데이트를 진행 할 수 있습니다.&lt;/p&gt;
&lt;h2&gt;2) &lt;code&gt;v-once&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;이름에도 알 수 있듯이 한번만 랜더링 되는 컴포넌트를 만들 때 사용됩니다. &lt;code&gt;v-once&lt;/code&gt;를 사용하면 업데이트 되지 않는 정적인 컴포넌트를 랜더링 할 수 있습니다. v-once를 사용하여 업데이트 하지 않아도 되는 엘리먼트들을 정적으로 랜더링 한다면 더 나은 성능을 보일 수 있습니다.&lt;/p&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://vuejs.org/v2/guide/components-edge-cases.html&quot;&gt;https://vuejs.org/v2/guide/components-edge-cases.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&quot;https://kr.vuejs.org/v2/guide/components.html&quot;&gt;https://kr.vuejs.org/v2/guide/components.html&lt;/a&gt;&lt;/p&gt;</description>
      <category>Vue.JS</category>
      <category>$forceUpdate</category>
      <category>$off</category>
      <category>$on</category>
      <category>$once</category>
      <category>$parent</category>
      <category>$refs</category>
      <category>$root</category>
      <category>component</category>
      <category>Inject</category>
      <category>inline-template</category>
      <category>provide</category>
      <category>v-once</category>
      <category>x-template</category>
      <category>순환 참조</category>
      <category>재귀 컴포넌트</category>
      <category>컴포넌트</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/60</guid>
      <comments>https://beomy.tistory.com/60#entry60comment</comments>
      <pubDate>Sat, 2 Feb 2019 22:46:41 +0900</pubDate>
    </item>
    <item>
      <title>[Vue.JS] 컴포넌트 (고급:Dynamic &amp;amp; Async Components)</title>
      <link>https://beomy.tistory.com/59</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;820&quot; height=&quot;289&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;289&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/9976913A5C4C84C20A?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/9976913A5C4C84C20A?original&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9976913A5C4C84C20A&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9976913A5C4C84C20A&quot; width=&quot;820&quot; height=&quot;289&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;289&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;h1&gt;1. 동적(Dynamic) 컴포넌트와 keep-alive&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://beomy.tistory.com/55#dynamic-components&quot;&gt;[Vue.JS] 컴포넌트 (기본)&lt;/a&gt; 에서 동적 컴포넌트에 관한 이야기를 하였습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;component v-bind:is=&amp;quot;currentTabComponent&amp;quot;&amp;gt;&amp;lt;/component&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 &lt;code&gt;is&lt;/code&gt;를 사용하여 동적으로 컴포넌트를 랜더링 할 수 있습니다,&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;672&quot; height=&quot;235&quot; alt=&quot;이전의 tab 상태를 기억하지 못합니다,&quot; data-origin-width=&quot;672&quot; data-origin-height=&quot;235&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/99DBF34F5C4DBDAB29?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/99DBF34F5C4DBDAB29?original&quot; data-alt=&quot;탭 이동시 새로운 컴포넌트가 그려지기 때문에 이전의 tab의 상태를 기억하지 못합니다,&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99DBF34F5C4DBDAB29&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99DBF34F5C4DBDAB29&quot; width=&quot;672&quot; height=&quot;235&quot; alt=&quot;이전의 tab 상태를 기억하지 못합니다,&quot; data-origin-width=&quot;672&quot; data-origin-height=&quot;235&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;탭 이동시 새로운 컴포넌트가 그려지기 때문에 이전의 tab의 상태를 기억하지 못합니다,&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;code&gt;is&lt;/code&gt;를 사용한 동적 컴포넌트 랜더링을 사용하여 위의 그림와 같이 탭 뷰를 만들 수 있습니다. 하지만 탭을 이동 할 때마다, 새로운 컴포넌트가 그려지기 때문에, 이전의 tab의 상태를 기억하지 못합니다. 위의 예제에서는 &lt;code&gt;Posts&lt;/code&gt; 탭에서 &lt;code&gt;Hipster Ipsum&lt;/code&gt;을 클릭 했지만, &lt;code&gt;Archive&lt;/code&gt; 탭으로 변경하고 다시 &lt;code&gt;Posts&lt;/code&gt; 탭으로 돌아오면 이전에 클릭했던 &lt;code&gt;Hipster Ipsum&lt;/code&gt;이 보이는 상태가 아니게 됩니다.&lt;/p&gt;
&lt;p&gt;탭을 변경 할 때, 이전 탭의 상태를 유지하는 것이 성능 상 좋을 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- Inactive components will be cached! --&amp;gt;
&amp;lt;keep-alive&amp;gt;
  &amp;lt;component v-bind:is=&amp;quot;currentTabComponent&amp;quot;&amp;gt;&amp;lt;/component&amp;gt;
&amp;lt;/keep-alive&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 &lt;code&gt;&amp;lt;keep-alive&amp;gt;&lt;/code&gt;를 사용하면 인스턴스를 캐시로 저장 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;keep-alive&amp;gt;&lt;/code&gt;를 사용하면 생성된 탭의 인스턴스를 캐시에 저장 하고 있기 때문에, 탭을 변경 후 돌아 왔을 때 탭을 새로 그리지 않고 이전 탭을 보이게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;679&quot; height=&quot;202&quot; alt=&quot;keep-alive는 이전 상태를 기억합니다,&quot; data-origin-width=&quot;679&quot; data-origin-height=&quot;202&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/99BE293D5C4DBFFE11?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/99BE293D5C4DBFFE11?original&quot; data-alt=&quot;keep-alive는 이전 상태를 기억합니다,&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99BE293D5C4DBFFE11&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99BE293D5C4DBFFE11&quot; width=&quot;679&quot; height=&quot;202&quot; alt=&quot;keep-alive는 이전 상태를 기억합니다,&quot; data-origin-width=&quot;679&quot; data-origin-height=&quot;202&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;keep-alive는 이전 상태를 기억합니다,&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;위의 그림과 같이 &lt;code&gt;Posts&lt;/code&gt; 탭에서 &lt;code&gt;Hipster Ipsum&lt;/code&gt;을 클릭한 후 &lt;code&gt;Archive&lt;/code&gt; 탭을 클릭하고 &lt;code&gt;Posts&lt;/code&gt; 탭으로 돌아온 경우 &lt;code&gt;Posts&lt;/code&gt; 탭을 캐시에 저장하고 있기 때문에 다시 돌아왔을 때, 이전에 클릭한 Hipster &lt;code&gt;Ipsum&lt;/code&gt;이 보이게 됩니다.&lt;/p&gt;
&lt;p&gt;위의 예제는 &lt;a href=&quot;https://jsfiddle.net/chrisvfritz/Lp20op9o/&quot;&gt;Fiddle&lt;/a&gt;에서 확인 할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;2. 비동기(Async) 컴포넌트&lt;/h1&gt;
&lt;p&gt;어플리케이션이 커지면, 작은 부분으로 나누고 필요할 때에 가져와 사용하는 구조를 구현해야 할 때가 있습니다. 비동기 컴포넌트를 사용하여 필요할 때 비동기적으로 컴포넌트를 불러와 사용할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Vue.component(&amp;#39;async-example&amp;#39;, function (resolve, reject) {
  setTimeout(function () {
    // Pass the component definition to the resolve callback
    resolve({
      template: &amp;#39;&amp;lt;div&amp;gt;I am async!&amp;lt;/div&amp;gt;&amp;#39;
    })
  }, 1000)
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 팩토리 함수를 사용하여 비동기 컴포넌트를 구현 할 수 있습니다. 컴포넌트를 가져오는 것이 성공하면 &lt;code&gt;resolve&lt;/code&gt; 콜백을 실행 해야 하고, 실패할 경우 &lt;code&gt;reject&lt;/code&gt; 콜백을 실행해야 합니다. &lt;code&gt;resolve&lt;/code&gt;와 &lt;code&gt;reject&lt;/code&gt;의 인자는 컴포넌트를 생성 할 때의 옵션 값과 동일 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Vue.component(&amp;#39;async-webpack-example&amp;#39;, function (resolve) {
  // This special require syntax will instruct Webpack to
  // automatically split your built code into bundles which
  // are loaded over Ajax requests.
  require([&amp;#39;./my-async-component&amp;#39;], resolve)
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 Webpack을 사용하여 다른 파일로 구분된 컴포넌트를 비동기 컴포넌트로 만들 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Vue.component(
  &amp;#39;async-webpack-example&amp;#39;,
  // The `import` function returns a Promise.
  () =&amp;gt; import(&amp;#39;./my-async-component&amp;#39;)
)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 Webpack 2와 ES2015 문법을 사용하여 비동기 컴포넌트를 만들 수도 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;new Vue({
  // ...
  components: {
    &amp;#39;my-component&amp;#39;: () =&amp;gt; import(&amp;#39;./my-async-component&amp;#39;)
  }
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;지역적으로 컴포넌트를 등록할때는 위의 코드와 같이 사용해야 합니다.&lt;/p&gt;
&lt;h2&gt;1) Handling Loading State&lt;/h2&gt;
&lt;p&gt;2.3.0 이상의 Vue 버전에서 사용할 수 있는 기능입니다. 비동기 컴포넌트를 좀 더 간결하고, 직관적으로 사용할 수 있게 되었습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const AsyncComponent = () =&amp;gt; ({
  // The component to load (should be a Promise)
  component: import(&amp;#39;./MyComponent.vue&amp;#39;),
  // A component to use while the async component is loading
  loading: LoadingComponent,
  // A component to use if the load fails
  error: ErrorComponent,
  // Delay before showing the loading component. Default: 200ms.
  delay: 200,
  // The error component will be displayed if a timeout is
  // provided and exceeded. Default: Infinity.
  timeout: 3000
})
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;2.3.0 이상 부터 지원하는 간결한 비동기 컴포넌트 구현 방법&lt;/p&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://vuejs.org/v2/guide/components-dynamic-async.html&quot;&gt;https://vuejs.org/v2/guide/components-dynamic-async.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>Async Component</category>
      <category>component</category>
      <category>Dynamic Component</category>
      <category>keep-alive</category>
      <category>Vue.js</category>
      <category>컴포넌트</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/59</guid>
      <comments>https://beomy.tistory.com/59#entry59comment</comments>
      <pubDate>Tue, 29 Jan 2019 00:11:12 +0900</pubDate>
    </item>
    <item>
      <title>[Vue.JS] 컴포넌트 (고급:slot)</title>
      <link>https://beomy.tistory.com/58</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;820&quot; height=&quot;289&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/99DAE24E5C49CE301E?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/99DAE24E5C49CE301E?original&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99DAE24E5C49CE301E&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99DAE24E5C49CE301E&quot; width=&quot;820&quot; height=&quot;289&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;h1&gt;1. Slot Content&lt;/h1&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;navigation-link url=&amp;quot;/profile&amp;quot;&amp;gt;
  Your Profile
&amp;lt;/navigation-link&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;부모 컴포넌트에서 자식 컴포넌트를 위의 코드와 같이 사용하여 자식 컴포넌트의 하위 엘리먼트의 값들을 보여주고 싶다면,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;a
  v-bind:href=&amp;quot;url&amp;quot;
  class=&amp;quot;nav-link&amp;quot;
&amp;gt;
  &amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;
&amp;lt;/a&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 자식 컴포넌트가 작성되어야 합니다. 위의 코드들이 랜더링 되면 &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt; 엘리먼트는 &lt;code&gt;Your Profile&lt;/code&gt;이라는 문구로 대체됩니다. &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt;은 문자열 뿐만 아니라 모든 HTML과 대체 될 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;navigation-link url=&amp;quot;/profile&amp;quot;&amp;gt;
  &amp;lt;!-- Add a Font Awesome icon --&amp;gt;
  &amp;lt;span class=&amp;quot;fa fa-user&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;
  Your Profile
&amp;lt;/navigation-link&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같은 기본 HTML 태그 뿐만 아니라,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;navigation-link url=&amp;quot;/profile&amp;quot;&amp;gt;
  &amp;lt;!-- Use a component to add an icon --&amp;gt;
  &amp;lt;font-awesome-icon name=&amp;quot;user&amp;quot;&amp;gt;&amp;lt;/font-awesome-icon&amp;gt;
  Your Profile
&amp;lt;/navigation-link&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같은 사용자 정의 컴포넌트도 &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt;과 대체 될 수 있습니다. 자식 컴포넌트에서 &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt;을 사용하지 않았다면, 부모 컴포넌트에서 자식 컴포넌트의 하위 엘리먼트로 넘겨 준 값들은 모두 무시 됩니다.&lt;/p&gt;
&lt;h1&gt;2. Named Slots&lt;/h1&gt;
&lt;p&gt;부모 컴포넌트에서 자식 컴포넌트로 여러개의 하위 엘리먼트를 넘겨줄 때 이름이 있는 &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt;을 사용한다면 특정 위치를 지정할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div class=&amp;quot;container&amp;quot;&amp;gt;
  &amp;lt;header&amp;gt;
    &amp;lt;!-- We want header content here --&amp;gt;
  &amp;lt;/header&amp;gt;
  &amp;lt;main&amp;gt;
    &amp;lt;!-- We want main content here --&amp;gt;
  &amp;lt;/main&amp;gt;
  &amp;lt;footer&amp;gt;
    &amp;lt;!-- We want footer content here --&amp;gt;
  &amp;lt;/footer&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;예를 들면, 위의 코드와 같이 &lt;code&gt;&amp;lt;base-layout&amp;gt;&lt;/code&gt;이라는 컴포넌트가 있습니다. 하당 컴포넌트에는 &lt;code&gt;&amp;lt;header&amp;gt;&lt;/code&gt;와 &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;footer&amp;gt;&lt;/code&gt; 3개의 정보가 채워져야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div class=&amp;quot;container&amp;quot;&amp;gt;
  &amp;lt;header&amp;gt;
    &amp;lt;slot name=&amp;quot;header&amp;quot;&amp;gt;&amp;lt;/slot&amp;gt;
  &amp;lt;/header&amp;gt;
  &amp;lt;main&amp;gt;
    &amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;
  &amp;lt;/main&amp;gt;
  &amp;lt;footer&amp;gt;
    &amp;lt;slot name=&amp;quot;footer&amp;quot;&amp;gt;&amp;lt;/slot&amp;gt;
  &amp;lt;/footer&amp;gt;
&amp;lt;/div&amp;gt; &lt;/code&gt;&lt;/pre&gt;&lt;p&gt;이 경우에 위의 코드와 같이 이름이 있는 &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt;을 사용할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;base-layout&amp;gt;
  &amp;lt;template slot=&amp;quot;header&amp;quot;&amp;gt;
    &amp;lt;h1&amp;gt;Here might be a page title&amp;lt;/h1&amp;gt;
  &amp;lt;/template&amp;gt;

  &amp;lt;p&amp;gt;A paragraph for the main content.&amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;And another one.&amp;lt;/p&amp;gt;

  &amp;lt;template slot=&amp;quot;footer&amp;quot;&amp;gt;
    &amp;lt;p&amp;gt;Here&amp;#39;s some contact info&amp;lt;/p&amp;gt;
  &amp;lt;/template&amp;gt;
&amp;lt;/base-layout&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;부모 컴포넌트에서는 자식 컴포넌트인 &lt;code&gt;&amp;lt;base-loyout&amp;gt;&lt;/code&gt; 컴포넌트를 위의 코드와 같이 사용할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;base-layout&amp;gt;
  &amp;lt;h1 slot=&amp;quot;header&amp;quot;&amp;gt;Here might be a page title&amp;lt;/h1&amp;gt;

  &amp;lt;p&amp;gt;A paragraph for the main content.&amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;And another one.&amp;lt;/p&amp;gt;

  &amp;lt;p slot=&amp;quot;footer&amp;quot;&amp;gt;Here&amp;#39;s some contact info&amp;lt;/p&amp;gt;
&amp;lt;/base-layout&amp;gt; &lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt;를 사용하지 않고 직접 엘리먼트에 slot 속성을 사용할 수도 있습니다. 이름이 없는 기본 &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt;은 항상 하나만 존재해야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div class=&amp;quot;container&amp;quot;&amp;gt;
  &amp;lt;header&amp;gt;
    &amp;lt;h1&amp;gt;Here might be a page title&amp;lt;/h1&amp;gt;
  &amp;lt;/header&amp;gt;
  &amp;lt;main&amp;gt;
    &amp;lt;p&amp;gt;A paragraph for the main content.&amp;lt;/p&amp;gt;
    &amp;lt;p&amp;gt;And another one.&amp;lt;/p&amp;gt;
  &amp;lt;/main&amp;gt;
  &amp;lt;footer&amp;gt;
    &amp;lt;p&amp;gt;Here&amp;#39;s some contact info&amp;lt;/p&amp;gt;
  &amp;lt;/footer&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;&amp;lt;base-layout&amp;gt;&lt;/code&gt; 컴포넌트 예제는 결국 위의 코드와 같이 랜더링됩니다.&lt;/p&gt;
&lt;h1&gt;3. Default Slot Content&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;slot&lt;/code&gt;은 기본값을 제공합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;button type=&amp;quot;submit&amp;quot;&amp;gt;
  &amp;lt;slot&amp;gt;Submit&amp;lt;/slot&amp;gt;
&amp;lt;/button&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 &lt;code&gt;&amp;lt;submit-button&amp;gt;&lt;/code&gt; 컴포넌트가 있을 때, &lt;code&gt;&amp;lt;submit-button&amp;gt;&lt;/code&gt; 컴포넌트의 하위 엘리먼트가 제공되지 않는다면, 기본값으로 &lt;code&gt;Submit&lt;/code&gt;이 화면에 보이게 됩니다. &lt;code&gt;&amp;lt;submit-button&amp;gt;&lt;/code&gt; 컴포넌트의 하위 엘리먼트가 존재한다면, &lt;code&gt;Submit&lt;/code&gt;이 아닌 &lt;code&gt;&amp;lt;submit-button&amp;gt;&lt;/code&gt; 컴포넌트의 하위 엘리먼트가 화면에 나타나게 됩니다.&lt;/p&gt;
&lt;h1&gt;4. Compilation Scope&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;slot&lt;/code&gt;에 data를 사용하고 싶을 때,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;navigation-link url=&amp;quot;/profile&amp;quot;&amp;gt;
  Logged in as {{ user.name }}
&amp;lt;/navigation-link&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 사용할 수 있습니다. 이 때 사용한 &lt;code&gt;user.name&lt;/code&gt; 은 &lt;code&gt;&amp;lt;navigation-link&amp;gt;&lt;/code&gt; 컴포넌트의 스코프에 있는 값이 아닌, &lt;code&gt;&amp;lt;navigation-link&amp;gt;&lt;/code&gt; 컴포넌트를 사용한 부모 컴포넌트의 스코프에 있는 값입니다.&lt;/p&gt;
&lt;h1&gt;5. Scoped Slots&lt;/h1&gt;
&lt;p&gt;이 기능은 2.1.0 이상의 Vue 버전에서 제공하는 기능입니다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;slot&lt;/code&gt;을 사용할 때 하위 컴포넌트의 데이터를 부모 컴포넌트에서 사용해야 할 때가 있습니다. 예를 들어,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;ul&amp;gt;
  &amp;lt;li
    v-for=&amp;quot;todo in todos&amp;quot;
    v-bind:key=&amp;quot;todo.id&amp;quot;
  &amp;gt;
    {{ todo.text }}
  &amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 &lt;code&gt;&amp;lt;todo-list&amp;gt;&lt;/code&gt;라는 컴포넌트가 있을 때, 위의 컴포넌트는 &lt;code&gt;&amp;lt;toto-list&amp;gt;&lt;/code&gt; 컴포넌트 스코프 안에 있는 &lt;code&gt;todos&lt;/code&gt;의 값을 나타냅니다. 부모 컴포넌트에서 자식 컴포넌트의 data를 사용하기 위해서는,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;ul&amp;gt;
  &amp;lt;li
    v-for=&amp;quot;todo in todos&amp;quot;
    v-bind:key=&amp;quot;todo.id&amp;quot;
  &amp;gt;
    &amp;lt;!-- We have a slot for each todo, passing it the --&amp;gt;
    &amp;lt;!-- `todo` object as a slot prop.                --&amp;gt;
    &amp;lt;slot v-bind:todo=&amp;quot;todo&amp;quot;&amp;gt;
      &amp;lt;!-- Fallback content --&amp;gt;
      {{ todo.text }}
    &amp;lt;/slot&amp;gt;
  &amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;자식 컴포넌트인 &lt;code&gt;&amp;lt;todo-list&amp;gt;&lt;/code&gt; 컴포넌트에서는 위의 코드와 같이 작성되어야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;todo-list v-bind:todos=&amp;quot;todos&amp;quot;&amp;gt;
  &amp;lt;!-- Define `slotProps` as the name of our slot scope --&amp;gt;
  &amp;lt;template slot-scope=&amp;quot;slotProps&amp;quot;&amp;gt;
    &amp;lt;!-- Define a custom template for todo items, using --&amp;gt;
    &amp;lt;!-- `slotProps` to customize each todo.            --&amp;gt;
    &amp;lt;span v-if=&amp;quot;slotProps.todo.isComplete&amp;quot;&amp;gt;✓&amp;lt;/span&amp;gt;
    {{ slotProps.todo.text }}
  &amp;lt;/template&amp;gt;
&amp;lt;/todo-list&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;부모 컴포넌트에서는 위의 코드와 같이 작성하여 자식 컴포넌트의 data를 사용할 수 있습니다. 이 때 &lt;code&gt;slot-scope&lt;/code&gt;는 &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt;에서 사용 되었지만 2.5.0 이상의 버전에서는 일반 엘리먼트에서도 사용 가능해 졌습니다.&lt;/p&gt;
&lt;h2&gt;1) Destructuring &lt;code&gt;slot-scope&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;slot-scope&lt;/code&gt;에 구조분해 문법 사용이 가능합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;todo-list v-bind:todos=&amp;quot;todos&amp;quot;&amp;gt;
  &amp;lt;template slot-scope=&amp;quot;{ todo }&amp;quot;&amp;gt;
    &amp;lt;span v-if=&amp;quot;todo.isComplete&amp;quot;&amp;gt;✓&amp;lt;/span&amp;gt;
    {{ todo.text }}
  &amp;lt;/template&amp;gt;
&amp;lt;/todo-list&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 구조분해 문법을 사용하면 좀 더 간결한 코드가 될 수 있습니다.&lt;/p&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://vuejs.org/v2/guide/components-slots.html&quot;&gt;https://vuejs.org/v2/guide/components-slots.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>component</category>
      <category>named slot</category>
      <category>Slot</category>
      <category>slot-scope</category>
      <category>Vue.js</category>
      <category>컴포넌트</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/58</guid>
      <comments>https://beomy.tistory.com/58#entry58comment</comments>
      <pubDate>Sat, 26 Jan 2019 20:55:02 +0900</pubDate>
    </item>
    <item>
      <title>[Vue.JS] 컴포넌트 (고급:Custrom Events)</title>
      <link>https://beomy.tistory.com/57</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;820&quot; height=&quot;289&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/994D5C385C45DAE13F?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/994D5C385C45DAE13F?original&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/994D5C385C45DAE13F&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F994D5C385C45DAE13F&quot; width=&quot;820&quot; height=&quot;289&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;h1&gt;1. 이벤트 이름&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;$emit&lt;/code&gt;을 이용하여 커스텀 이벤트를 만들 때, 이벤트 이름은 kebab-case를 사용하는 것이 좋습니다.&lt;/p&gt;
&lt;p&gt;컴포넌트의 이름이나 prop의 이름과 달리 이벤트 이름은 자동으로 변환(camelCase &amp;lt;-&amp;gt; kebab-case)되지 않습니다. 이벤트 이름은 정확히 일치하는 이벤트 리스너와 매칭됩니다. 예를 들어,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;this.$emit(&amp;#39;myEvent&amp;#39;)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 &lt;code&gt;myEvent&lt;/code&gt; 이벤트를 발생 시킨다고 할 때, 부모 컴포넌트는&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;my-component v-on:my-event=&amp;quot;doSomething&amp;quot;&amp;gt;&amp;lt;/my-component&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 &lt;code&gt;myEvent&lt;/code&gt;를 받기 위해 &lt;code&gt;v-on:my-event&lt;/code&gt;를 사용한다면, &lt;code&gt;my-event&lt;/code&gt;는 &lt;code&gt;myEvent&lt;/code&gt;가 발생 했다는 것을 감지 할 수 없습니다. 컴포넌트의 이름이나 prop의 이름과 다르게 이벤트 이름은 자바스크립트의 변수나, 속성으로 사용되지 않기 때문에 이벤트 이름은 camelCase나 PascalCase로 작성될 이유가 없습니다.&lt;/p&gt;
&lt;p&gt;HTML에서는 모든 글자를 소문자로 인식하기 때문에 &lt;code&gt;v-on:myEvent&lt;/code&gt;는 결국 &lt;code&gt;v-on:myevnet&lt;/code&gt;와 동일하게 인식됩니다. 결국 &lt;code&gt;this.$emit(&amp;#39;myEvent&amp;#39;)&lt;/code&gt;로 &lt;code&gt;myEvent&lt;/code&gt;를 발생 시켜도 이벤트를 감지하기 못하게 됩니다.&lt;/p&gt;
&lt;p&gt;결론만 간단히 이야기 하자면, 커스텀 이벤트의 이름은 kebab-case를 사용해야 합니다.&lt;/p&gt;
&lt;h1&gt;2. 컴포넌트에서 &lt;code&gt;v-model&lt;/code&gt; 사용하기&lt;/h1&gt;
&lt;p&gt;2.2.0 이상의 Vue 버전에서 추가된 기능입니다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;v-model&lt;/code&gt;은 &lt;code&gt;value&lt;/code&gt; 속성과 &lt;code&gt;input&lt;/code&gt; 이벤트를 함께 사용하는 것과 같습니다. 즉. &lt;code&gt;v-model&lt;/code&gt;을 사용한다는 것은 &lt;code&gt;input&lt;/code&gt; 이벤트가 발생 했을 때, &lt;code&gt;value&lt;/code&gt; 값을 변경하는 것과 동일합니다. 하지만 체크박스나 라디오 버튼의 경우 이벤트가 발생 했을 때, &lt;code&gt;value&lt;/code&gt;값을 변경하는 것이 아닌 &lt;code&gt;checked&lt;/code&gt;의 값을 바꿈으로 동작합니다. 체크박스와 라이오 버튼에서 &lt;code&gt;value&lt;/code&gt;는 다른 목적으로 사용됩니다. 이 때 &lt;code&gt;model&lt;/code&gt; 옵션을 사용하면, 위에서 이야기한 문제를 해결 할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Vue.component(&amp;#39;base-checkbox&amp;#39;, {
  model: {
    prop: &amp;#39;checked&amp;#39;,
    event: &amp;#39;custom-change&amp;#39;
  },
  props: {
    checked: Boolean
  },
  template: `
    &amp;lt;label&amp;gt;
      &amp;lt;input
      type=&amp;quot;checkbox&amp;quot;
      v-bind:checked=&amp;quot;checked&amp;quot;
      v-on:change=&amp;quot;$emit(&amp;#39;custom-change&amp;#39;, $event.target.checked)&amp;quot;
      &amp;gt;{{ checked }}
    &amp;lt;/label&amp;gt;
  `
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 작성된 컴포넌트가 있다면 부모 컴포넌트에서 &lt;code&gt;v-model&lt;/code&gt;은,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;base-checkbox v-model=&amp;quot;lovingVue&amp;quot;&amp;gt;&amp;lt;/base-checkbox&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 사용할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;lovingVue&lt;/code&gt;의 값은 &lt;code&gt;props&lt;/code&gt;의 &lt;code&gt;checked&lt;/code&gt;로 값이 전달 됩니다. 그리고 자식 컴포넌트에서 &lt;code&gt;change&lt;/code&gt; 이벤트가 발생되면, &lt;code&gt;$emit(&amp;#39;custom-change&amp;#39;)&lt;/code&gt;가 실행되고, &lt;code&gt;$emit&lt;/code&gt;의 두번째 인자로 전달된 값으로 &lt;code&gt;lovingVue&lt;/code&gt;의 값이 업데이트 됩니다. 자식 컴포넌트에서 &lt;code&gt;model&lt;/code&gt; 옵션의 prop로 정의 된 &lt;code&gt;checked&lt;/code&gt;는 컴포넌트의 &lt;code&gt;props&lt;/code&gt;에 동일한 이름으로 정의 되어야 합니다.&lt;/p&gt;
&lt;p&gt;위의 예제는 &lt;a href=&quot;https://codepen.io/beomy/pen/rPBQVm?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 확인 할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;3. 컴포넌트에 네이티브 이벤트 바인딩하기&lt;/h1&gt;
&lt;p&gt;컴포넌트의 루트 엘리먼트에서 네이티브 이벤트를 받기 위해서는 &lt;code&gt;v-on&lt;/code&gt; 이벤트 리스너에 &lt;code&gt;.native&lt;/code&gt; 수식어를 사용하면 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;base-input v-on:focus.native=&amp;quot;onFocus&amp;quot;&amp;gt;&amp;lt;/base-input&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 루트 엘리먼트에 네이티브 이벤트 리스너를 등록하면, 유용할 수 있지만, 특정 엘리먼트에는 좋은 방법이 아닐 수 아닙니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;label&amp;gt;
  {{ label }}
  &amp;lt;input
    v-bind=&amp;quot;$attrs&amp;quot;
    v-bind:value=&amp;quot;value&amp;quot;
    v-on:input=&amp;quot;$emit(&amp;#39;input&amp;#39;, $event.target.value)&amp;quot;
  &amp;gt;
&amp;lt;/label&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;예를 들어, &lt;code&gt;&amp;lt;base-input&amp;gt;&lt;/code&gt; 컴포넌트의 위의 코드와 같을 경우, &lt;code&gt;&amp;lt;base-input&amp;gt;&lt;/code&gt; 컴포넌트의 루트 엘리먼트는 &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt;이 되고, &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt;은 &lt;code&gt;focus&lt;/code&gt; 이벤트를 핸들링하지 못하기 때문에, 에러는 발생하지 않지만, &lt;code&gt;v-on:focus.native=&amp;quot;onFocus&amp;quot;&lt;/code&gt;은 동작하지 않습니다.&lt;/p&gt;
&lt;p&gt;이 문제를 해결하려면 &lt;code&gt;$listeners&lt;/code&gt; 속성을 사용하는 것이 좋습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  focus: function (event) { /* ... */ }
  input: function (value) { /* ... */ },
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;$listeners&lt;/code&gt;는 위의 코드와 같이 이벤트 리스너를 담고 있는 &lt;code&gt;Object&lt;/code&gt; 입니다. &lt;code&gt;$listeners&lt;/code&gt; 속성을 사용하면 모든 이벤트 리스너를 특정 자식 엘리먼트로 전달 할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Vue.component(&amp;#39;base-input&amp;#39;, {
  inheritAttrs: false,
  props: [&amp;#39;label&amp;#39;, &amp;#39;value&amp;#39;],
  computed: {
    inputListeners: function () {
      var vm = this
      // `Object.assign` merges objects together to form a new object
      return Object.assign({},
        // We add all the listeners from the parent
        this.$listeners,
        // Then we can add custom listeners or override the
        // behavior of some listeners.
        {
          // This ensures that the component works with v-model
          input: function (event) {
            vm.$emit(&amp;#39;input&amp;#39;, event.target.value)
          }
        }
      )
    }
  },
  template: `
    &amp;lt;label&amp;gt;
      {{ label }}
      &amp;lt;input
        v-bind=&amp;quot;$attrs&amp;quot;
        v-bind:value=&amp;quot;value&amp;quot;
        v-on=&amp;quot;inputListeners&amp;quot;
      &amp;gt;
    &amp;lt;/label&amp;gt;
  `
})&lt;/code&gt;&lt;/pre&gt;&lt;h1&gt;4. &lt;code&gt;.sync&lt;/code&gt; 수식어&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;.sync&lt;/code&gt; 수식어는 2.3.0 이상의 Vue 버전에서 사용할 수 있습니다.&lt;/p&gt;
&lt;p&gt;때때로 부모 컴포넌트와 자식 컴포넌트가 양방향 데이터 통신을 해야 할 때가 있습니다. 하지만 양방향 데이터 통신은 유지보수를 어렵게 만들 수 있습니다. 자식 컴포넌트에서 부모 컴포넌트의 값을 수정하기 때문에, 부모 컴포넌트에서는 어떠한 자식 컴포넌트가 값을 수정한 것인지 파악하기 어렵게 될 수 있습니다. 그렇기 때문에 자식 컴포넌트에서 &lt;code&gt;update:myPropName&lt;/code&gt;을 이용하여 이벤트를 부모 컴포넌트로 전달하는 방법으로 양방향 데이터 통신을 구현할 것입니다.&lt;/p&gt;
&lt;p&gt;자식 컴포넌트에서 &lt;code&gt;title&lt;/code&gt; prop를 업데이트 하려고 할 때,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;this.$emit(&amp;#39;update:title&amp;#39;, newTitle)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 &lt;code&gt;$emit&lt;/code&gt;을 사용하여 부모 컴포넌트로 데이터를 전달 합니다. 그러면 부모 컴포넌트는,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;text-document
  v-bind:title=&amp;quot;doc.title&amp;quot;
  v-on:update:title=&amp;quot;doc.title = $event&amp;quot;
&amp;gt;&amp;lt;/text-document&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 &lt;code&gt;:update:title&lt;/code&gt;를 사용하여 자식 컴포넌트로 부터 데이터를 전달 받습니다. 이와 같은 동작을 &lt;code&gt;.sync&lt;/code&gt; 수식어를 통하여,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;text-document v-bind:title.sync=&amp;quot;doc.title&amp;quot;&amp;gt;&amp;lt;/text-document&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 간편하게 구현 할 수 있습니다.&lt;/p&gt;
&lt;h4&gt;* 참고 - &lt;code&gt;v-bind&lt;/code&gt;와 &lt;code&gt;.sync&lt;/code&gt;를 이용하여 양방향 데이터 통신을 할 때 유의사항&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;v-bind:title.sync=&amp;quot;doc.title + &amp;#39;!&amp;#39;&amp;quot;&lt;/code&gt; 와 같은 표현식은 정상 동작하지 않습니다. &lt;code&gt;v-model&lt;/code&gt;와 유사하게 property 이름만 전달 되어야 합니다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.sync&lt;/code&gt;도 &lt;code&gt;v-bind&lt;/code&gt;를 사용하여 객체를 통째로 &lt;code&gt;props&lt;/code&gt;로 전달 하는 것이 가능합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;text-document v-bind.sync=&amp;quot;doc&amp;quot;&amp;gt;&amp;lt;/text-document&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 사용하면 &lt;code&gt;v-bind&lt;/code&gt;에 객체를 넘기는 것과 동일한 동작을 합니다. 즉 &lt;code&gt;v-bind.sync&lt;/code&gt;에 객체를 넘기면 객체에 있는 모든 값들이 prop로 인식됩니다.&lt;/p&gt;
&lt;h4&gt;* 참고 - &lt;code&gt;v-bind.sync&lt;/code&gt;를 사용할 때 주의 사항&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;v-bind.sync=&amp;quot;{ title: doc.title }&amp;quot;&lt;/code&gt; 과 같이 사용할 수 없습니다. 파싱을 할 때 너무 많은 예외 케이스들이 발생하여 이와 같은 표현식은 사용할 수 없습니다.&lt;/p&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://vuejs.org/v2/guide/components-custom-events.html&quot;&gt;https://vuejs.org/v2/guide/components-custom-events.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>$listeners</category>
      <category>.native</category>
      <category>.sync</category>
      <category>component</category>
      <category>V-Model</category>
      <category>Vue.js</category>
      <category>컴포넌트</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/57</guid>
      <comments>https://beomy.tistory.com/57#entry57comment</comments>
      <pubDate>Thu, 24 Jan 2019 01:12:54 +0900</pubDate>
    </item>
    <item>
      <title>[Vue.JS] 컴포넌트 (고급:props)</title>
      <link>https://beomy.tistory.com/56</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;820&quot; height=&quot;289&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/997B55395C41F8E027?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/997B55395C41F8E027?original&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/997B55395C41F8E027&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F997B55395C41F8E027&quot; width=&quot;820&quot; height=&quot;289&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;h1&gt;1. prop 표기법 (camelCase VS kebab-case)&lt;/h1&gt;
&lt;p&gt;HTML 속성은 대소문자를 구별하지 않습니다. 모든 대문자를 소문자로 인식합니다. Vue에서 HTML을 표현 할 때 보통 kebal-case로 표현하고, JavaScript에서 prop를 받아 사용할 때는 camelCase를 사용합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- kebab-case in HTML --&amp;gt;
&amp;lt;blog-post post-title=&amp;quot;hello!&amp;quot;&amp;gt;&amp;lt;/blog-post&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;HTML에서는 kebal-case(&lt;code&gt;post-title&lt;/code&gt;)를 사용합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Vue.component(&amp;#39;blog-post&amp;#39;, {
  // camelCase in JavaScript
  props: [&amp;#39;postTitle&amp;#39;],
  template: &amp;#39;&amp;lt;h3&amp;gt;{{ postTitle }}&amp;lt;/h3&amp;gt;&amp;#39;
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;JavaScript에서는 camelCase(&lt;code&gt;postTitle&lt;/code&gt;)를 사용합니다.&lt;/p&gt;
&lt;h1&gt;2. prop 타입&lt;/h1&gt;
&lt;pre&gt;&lt;code&gt;props: [&amp;#39;title&amp;#39;, &amp;#39;likes&amp;#39;, &amp;#39;isPublished&amp;#39;, &amp;#39;commentIds&amp;#39;, &amp;#39;author&amp;#39;]&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 &lt;code&gt;props&lt;/code&gt;는 &lt;code&gt;string&lt;/code&gt; 배열로 정의 할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;props: {
  title: String,
  likes: Number,
  isPublished: Boolean,
  commentIds: Array,
  author: Object
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;또한 위의 코드와 같이 prop에 특정 객체 타입을 지정할 수 있습니다. 이렇게 사용하면 prop를 좀 더 구체적으로 문서화 하여 다른 사람이 코드를 보았을 때, 좀 더 이해하기 쉬운 코드가 될 수 있습니다. 뿐만 아니라, 지정된 타입 이외의 타입이 지정되면 콘솔에 경고 메시지가 출력 되기 때문에, 정상적이지 않은 prop가 넘겨진 것을 단번에 알 수도 있습니다.&lt;/p&gt;
&lt;h1&gt;3. prop에 정적, 동적 데이터 넘겨주기&lt;/h1&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;blog-post title=&amp;quot;My journey with Vue&amp;quot;&amp;gt;&amp;lt;/blog-post&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 prop를 통해 정적인 데이터를 넘겨 줄 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- Dynamically assign the value of a variable --&amp;gt;
&amp;lt;blog-post v-bind:title=&amp;quot;post.title&amp;quot;&amp;gt;&amp;lt;/blog-post&amp;gt;

&amp;lt;!-- Dynamically assign the value of a complex expression --&amp;gt;
&amp;lt;blog-post
  v-bind:title=&amp;quot;post.title + &amp;#39; by &amp;#39; + post.author.name&amp;quot;
&amp;gt;&amp;lt;/blog-post&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;또한 위의 코드와 같이 &lt;code&gt;v-bind&lt;/code&gt;를 사용하여 동적인 데이터를 넘겨 줄 수도 있습니다. prop로 모든 값들은 전달이 가능합니다.&lt;/p&gt;
&lt;h2&gt;1) &lt;code&gt;Number&lt;/code&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- Even though `42` is static, we need v-bind to tell Vue that --&amp;gt;
&amp;lt;!-- this is a JavaScript expression rather than a string.       --&amp;gt;
&amp;lt;blog-post v-bind:likes=&amp;quot;42&amp;quot;&amp;gt;&amp;lt;/blog-post&amp;gt;

&amp;lt;!-- Dynamically assign to the value of a variable. --&amp;gt;
&amp;lt;blog-post v-bind:likes=&amp;quot;post.likes&amp;quot;&amp;gt;&amp;lt;/blog-post&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;prop로 &lt;code&gt;Number&lt;/code&gt; 타입을 넘겨 줄 때 &lt;code&gt;v-bind&lt;/code&gt;를 통해 직접 숫자를 넘겨 줄 경우(&lt;code&gt;v-bind:likes=&amp;quot;42&amp;quot;&lt;/code&gt; 와 같이) &lt;code&gt;v-bind&lt;/code&gt;를 사용할 경우 자바스크립트 표현식으로 사용되기 때문에 넘겨진 숫자는 문자열이 아닌 숫자로 인식됩니다.&lt;/p&gt;
&lt;h2&gt;2) &lt;code&gt;Boolean&lt;/code&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- Including the prop with no value will imply `true`. --&amp;gt;
&amp;lt;blog-post is-published&amp;gt;&amp;lt;/blog-post&amp;gt;

&amp;lt;!-- Even though `false` is static, we need v-bind to tell Vue that --&amp;gt;
&amp;lt;!-- this is a JavaScript expression rather than a string.          --&amp;gt;
&amp;lt;blog-post v-bind:is-published=&amp;quot;false&amp;quot;&amp;gt;&amp;lt;/blog-post&amp;gt;

&amp;lt;!-- Dynamically assign to the value of a variable. --&amp;gt;
&amp;lt;blog-post v-bind:is-published=&amp;quot;post.isPublished&amp;quot;&amp;gt;&amp;lt;/blog-post&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;&amp;lt;blog-post is-published&amp;gt;&amp;lt;/blog-post&amp;gt;&lt;/code&gt;와 같이 prop를 정의하고 값을 넘겨주지 않는다면 &lt;code&gt;true&lt;/code&gt; 값이 전달 된 것으로 인식됩니다.&lt;/p&gt;
&lt;h2&gt;3) &lt;code&gt;Array&lt;/code&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- Even though the array is static, we need v-bind to tell Vue that --&amp;gt;
&amp;lt;!-- this is a JavaScript expression rather than a string.            --&amp;gt;
&amp;lt;blog-post v-bind:comment-ids=&amp;quot;[234, 266, 273]&amp;quot;&amp;gt;&amp;lt;/blog-post&amp;gt;

&amp;lt;!-- Dynamically assign to the value of a variable. --&amp;gt;
&amp;lt;blog-post v-bind:comment-ids=&amp;quot;post.commentIds&amp;quot;&amp;gt;&amp;lt;/blog-post&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;4) &lt;code&gt;Object&lt;/code&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- Even though the object is static, we need v-bind to tell Vue that --&amp;gt;
&amp;lt;!-- this is a JavaScript expression rather than a string.             --&amp;gt;
&amp;lt;blog-post
  v-bind:author=&amp;quot;{
    name: &amp;#39;Veronica&amp;#39;,
    company: &amp;#39;Veridian Dynamics&amp;#39;
  }&amp;quot;
&amp;gt;&amp;lt;/blog-post&amp;gt;

&amp;lt;!-- Dynamically assign to the value of a variable. --&amp;gt;
&amp;lt;blog-post v-bind:author=&amp;quot;post.author&amp;quot;&amp;gt;&amp;lt;/blog-post&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;5) 한꺼번에 넘겨주기&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;v-bind:props-name&lt;/code&gt;을 사용하지 않고 &lt;code&gt;v-bind&lt;/code&gt;로 값을 props로 통째로 넘겨줄 수도 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;post: {
  id: 1,
  title: &amp;#39;My Journey with Vue&amp;#39;
}&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;&amp;lt;blog-post v-bind=&amp;quot;post&amp;quot;&amp;gt;&amp;lt;/blog-post&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 값이 &lt;code&gt;v-bind&lt;/code&gt;로 값이 통째로 넘겨 주었다면,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;blog-post
  v-bind:id=&amp;quot;post.id&amp;quot;
  v-bind:title=&amp;quot;post.title&amp;quot;
&amp;gt;&amp;lt;/blog-post&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같은 동작을 하게 됩니다.&lt;/p&gt;
&lt;h1&gt;4. 단방향 데이터 흐름&lt;/h1&gt;
&lt;p&gt;컴포넌트는 부모 컴포넌트에서 자식 컴포넌트를 데이터를 보내는 단방향 데이터 흐름을 가지게 됩니다. 단방향 데이터 흐름은 자식 컴포넌트에서 부모 컴포넌트의 데이터가 변경되는 것을 막기 때문에, 어플리케이션의 데이터 흐름을 단순하게 만들 수 있습니다.&lt;/p&gt;
&lt;p&gt;부모 컴포넌트가 업데이트 되면, 자식 컴포넌트로 넘겨주는 &lt;code&gt;props&lt;/code&gt; 또한 업데이트 되기 때문에, 자식 컴포넌트도 부모 컴포넌트 따라 업데이트 됩니다. 그리고, 자식 컴포넌트에서 &lt;code&gt;props&lt;/code&gt;를 수정하려고 시도 할 때 콜솔 경고를 출력 하게 됩니다.&lt;/p&gt;
&lt;h4&gt;참고 - &lt;code&gt;Object&lt;/code&gt;와 &lt;code&gt;Array&lt;/code&gt; 타입의 prop를 사용 할 경우 유의사항&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;Object&lt;/code&gt;와 &lt;code&gt;Array&lt;/code&gt;를 자식 컴포넌트로 넘겨줄 때는 &lt;code&gt;Object&lt;/code&gt;와 &lt;code&gt;Array&lt;/code&gt;는 reference로 넘겨주기 때문에 &lt;code&gt;Object&lt;/code&gt;, &lt;code&gt;Array&lt;/code&gt; 형태의 prop를 자식 컴포넌트에서 변경 할 때, 부모 컴포넌트도 변경 됩니다.&lt;/p&gt;
&lt;p&gt;자식 컴포넌트에서 부모 컴포넌트로 전달 받은 prop 값을 수정해야 할 필요가 있을 때가 있습니다. &lt;/p&gt;
&lt;h2&gt;1) prop는 초기 값으로 사용되고 이후 로컬 데이터로 사용하고 싶은 경우&lt;/h2&gt;
&lt;p&gt;위의 경우에는 prop 값을 초기 값으로 사용하는 로컬 &lt;code&gt;data&lt;/code&gt;를 선언해야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;props: [&amp;#39;initialCounter&amp;#39;],
data: function () {
  return {
    counter: this.initialCounter
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;prop를 초기 값으로 사용해야 할 경우에는 로컬 데이터를 만들고 prop를 초기 값으로 사용합니다.&lt;/p&gt;
&lt;h2&gt;2) prop를 가공해서 사용해야 할 경우&lt;/h2&gt;
&lt;p&gt;위의 경우에는 &lt;code&gt;computed&lt;/code&gt; 속성을 사용하여 해결 할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;props: [&amp;#39;size&amp;#39;],
computed: {
  normalizedSize: function () {
    return this.size.trim().toLowerCase()
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;prop를 가공해서 사용해야 하는 경우, &lt;code&gt;computed&lt;/code&gt; 속성을 사용합니다.&lt;/p&gt;
&lt;h1&gt;5. prop 유효성 체크&lt;/h1&gt;
&lt;p&gt;prop를 정의 할 때, 몇가지의 요구사항을 체크 할 수 있습니다. 정의 한 요구사항이 만족하지 않을 경우 콘솔 경고를 출력하게 됩니다. 코드를 만든 당사자가 아닌 다른 사람이 컴포넌트를 수정해야 할 때, prop의 요구사항을 정의한다면 코드를 이해하는데 도움이 될 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Vue.component(&amp;#39;my-component&amp;#39;, {
  props: {
    // Basic type check (`null` matches any type)
    propA: Number,
    // Multiple possible types
    propB: [String, Number],
    // Required string
    propC: {
      type: String,
      required: true
    },
    // Number with a default value
    propD: {
      type: Number,
      default: 100
    },
    // Object with a default value
    propE: {
      type: Object,
      // Object or array defaults must be returned from
      // a factory function
      default: function () {
        return { message: &amp;#39;hello&amp;#39; }
      }
    },
    // Custom validator function
    propF: {
      validator: function (value) {
        // The value must match one of these strings
        return [&amp;#39;success&amp;#39;, &amp;#39;warning&amp;#39;, &amp;#39;danger&amp;#39;].indexOf(value) !== -1
      }
    }
  }
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;prop에 몇가지 유효성 검사를 체크할 수 있습니다.&lt;/p&gt;
&lt;h4&gt;참고 - prop의 &lt;code&gt;default&lt;/code&gt;, &lt;code&gt;validator&lt;/code&gt;를 사용할 때 유의 사항&lt;/h4&gt;
&lt;p&gt;prop의 유효성 검사에서 사용되는 &lt;code&gt;default&lt;/code&gt;와 &lt;code&gt;validator&lt;/code&gt; 함수는 Vue의 인스턴스가 생성되기 전에 실행 되기 때문에, &lt;code&gt;default&lt;/code&gt;와 &lt;code&gt;validator&lt;/code&gt; 함수에서는 &lt;code&gt;data&lt;/code&gt;, &lt;code&gt;computed&lt;/code&gt;, &lt;code&gt;method&lt;/code&gt; 등.. 을 사용할 수 없습니다.&lt;/p&gt;
&lt;h2&gt;1) Type 체크&lt;/h2&gt;
&lt;p&gt;prop의 &lt;code&gt;type&lt;/code&gt; 옵션으로 사용할 수 있는 몇가지 기본 타입을 제공합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;String&lt;/li&gt;
&lt;li&gt;Number&lt;/li&gt;
&lt;li&gt;Boolean&lt;/li&gt;
&lt;li&gt;Array&lt;/li&gt;
&lt;li&gt;Object&lt;/li&gt;
&lt;li&gt;Date&lt;/li&gt;
&lt;li&gt;Function&lt;/li&gt;
&lt;li&gt;Symbol&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;위의 8가지 타입 이외의 커스텀 타입의 유효성 체크도 가능합니다. &lt;code&gt;instanceof&lt;/code&gt; 를 통해 타입의 유효성을 체크 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function Person (firstName, lastName) {
  this.firstName = firstName
  this.lastName = lastName
}&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;Vue.component(&amp;#39;blog-post&amp;#39;, {
  props: {
    author: Person
  }
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;prop의 &lt;code&gt;author&lt;/code&gt;를 사용하여 커스텀 타입의 유효성 체크가 가능합니다. 위의 예제는 &lt;code&gt;new Person&lt;/code&gt;으로 prop가 만들어졌는지 체크하게 됩니다.&lt;/p&gt;
&lt;h1&gt;6. Non-Prop 속성&lt;/h1&gt;
&lt;p&gt;non-prop 속성은 attribute로 전달 되었지만, 컴포넌트에서 &lt;code&gt;props&lt;/code&gt;로 정의 되지 않은 속성을 이야기 합니다. 컴포넌트에서는 &lt;code&gt;props&lt;/code&gt;로 정의 된 값 뿐만 아니라 임의의 attribute를 정의 해서 사용할 수 있습니다. 이 기능을 허용하면서 HTML 태그에 커스텀 속성을 추가하는 기타 다른 라이브러리와 함께 사용이 가능해 집니다.&lt;/p&gt;
&lt;h2&gt;1) 기본 속성과 바꾸기/병합하기&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;input type=&amp;quot;date&amp;quot; class=&amp;quot;form-control&amp;quot;&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;bootstrap-date-input&lt;/code&gt; 이라는 컴포넌트가 위의 코드와 같이 정의 되어 있다고 할 때,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;bootstrap-date-input
  data-date-picker=&amp;quot;activated&amp;quot;
  class=&amp;quot;date-picker-theme-dark&amp;quot;
&amp;gt;&amp;lt;/bootstrap-date-input&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;bootstrap-date-input&lt;/code&gt; 컴포넌트를 위의 코드와 같이 사용된다면,&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;class&lt;/code&gt; 속성의 &lt;code&gt;form-control&lt;/code&gt;와 &lt;code&gt;date-picker-theme-dark&lt;/code&gt;는 병합되어, &lt;code&gt;class=&amp;quot;form-control date-picker-theme-dark&amp;quot;&lt;/code&gt;가 됩니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;data-date-picker&lt;/code&gt; 속성은 추가 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;input type=&amp;quot;date&amp;quot; class=&amp;quot;form-control date-picker-theme-dark&amp;quot; data-date-picker=&amp;quot;activated&amp;quot;&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 랜더링됩니다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;class&lt;/code&gt; 속성와 &lt;code&gt;style&lt;/code&gt; 속성은 부모 컴포넌트에서 자식 컴포넌트를 선언 할 때 사용된 속성와 자식 컴포넌트에서 정의 된 속성이 병합이 됩니다. 하지만 그 외의 속성은 부모 컴포넌트에서 자식 컴포넌트를 선언 할 때 사용된 값으로 대체됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;bootstrap-date-input
  type=&amp;quot;text&amp;quot;
  data-date-picker=&amp;quot;activated&amp;quot;
  class=&amp;quot;date-picker-theme-dark&amp;quot;
&amp;gt;&amp;lt;/bootstrap-date-input&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;예를 들어, 위의 코드와 같이 컴포넌트를 사용한다면 실제로는,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;input type=&amp;quot;text&amp;quot; class=&amp;quot;form-control date-picker-theme-dark&amp;quot; data-date-picker=&amp;quot;activated&amp;quot;&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 랜더링 됩니다.&lt;/p&gt;
&lt;h2&gt;2) Attribute 상속 받지 않기&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;inheritAttrs: false&lt;/code&gt;로 설정한다면, 부모 컴포넌트로부터 전달 받은 Attribute가 자식 컴포넌트의 Attribute로 설정되지 않습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;base-input placeholder=&amp;quot;inherit&amp;quot;&amp;gt;&amp;lt;/base-input&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;즉 위의 코드와 같이 작성된다고 해도,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Vue.component(&amp;#39;base-input&amp;#39;, {
  inheritAttrs: false,
  template: &amp;#39;&amp;lt;input /&amp;gt;&amp;#39;
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 &lt;code&gt;inheritAttrs: false&lt;/code&gt;로 정의 되어 있다면 실제 랜더링은,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;input /&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 랜더링 됩니다. 위의 코드는 &lt;a href=&quot;https://codepen.io/beomy/pen/bOXybQ?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 확인 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;inheritAttrs: false&lt;/code&gt;와 &lt;code&gt;$attrs&lt;/code&gt;를 함께 사용한다면 선택적으로 필요한 속성만 자식 컴포넌트에 랜더링 할 수 있게 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;base-input
    v-model=&amp;quot;username&amp;quot;
    class=&amp;quot;username-input&amp;quot;
    placeholder=&amp;quot;Enter your username&amp;quot;
  &amp;gt;&amp;lt;/base-input&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;Vue.component(&amp;#39;base-input&amp;#39;, {
  inheritAttrs: false,
  props: [&amp;#39;label&amp;#39;, &amp;#39;value&amp;#39;],
  template: `
    &amp;lt;label&amp;gt;
      {{ label }}
      &amp;lt;input
        v-bind=&amp;quot;$attrs&amp;quot;
        v-bind:value=&amp;quot;value&amp;quot;
        v-on:input=&amp;quot;$emit(&amp;#39;input&amp;#39;, $event.target.value)&amp;quot;
      &amp;gt;
    &amp;lt;/label&amp;gt;
  `
})

new Vue({
  el: &amp;#39;#app&amp;#39;
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드는 &lt;a href=&quot;https://codepen.io/beomy/pen/roXbRN?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 확인 할 수 있습니다.&lt;/p&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://vuejs.org/v2/guide/components-props.html&quot;&gt;https://vuejs.org/v2/guide/components-props.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>component</category>
      <category>non-prop</category>
      <category>props</category>
      <category>Vue.js</category>
      <category>컴포넌트</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/56</guid>
      <comments>https://beomy.tistory.com/56#entry56comment</comments>
      <pubDate>Mon, 21 Jan 2019 01:23:21 +0900</pubDate>
    </item>
    <item>
      <title>[Vue.JS] 컴포넌트 (기본)</title>
      <link>https://beomy.tistory.com/55</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;820&quot; height=&quot;289&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/9983F6355C28DD7516?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/9983F6355C28DD7516?original&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9983F6355C28DD7516&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9983F6355C28DD7516&quot; width=&quot;820&quot; height=&quot;289&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;이번 포스트에서는 SPA에서 가장 중요하다 이야기 되는 컴포넌트에 관해 이야기 할 것입니다.&lt;/p&gt;
&lt;h1&gt;1. 기본 예제&lt;/h1&gt;
&lt;pre&gt;&lt;code&gt;// Define a new component called button-counter
Vue.component(&amp;#39;button-counter&amp;#39;, {
  data: function () {
    return {
      count: 0
    }
  },
  template: &amp;#39;&amp;lt;button @click=&amp;quot;count++&amp;quot;&amp;gt;You clicked me {{ count }} times.&amp;lt;/button&amp;gt;&amp;#39;
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제는 &lt;code&gt;&amp;lt;button-counter&amp;gt;&lt;/code&gt; 라는 이름으로 재사용이 가능한 컴포넌트입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;button-counter&amp;gt;&amp;lt;button-counter/&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;Vue.component(&amp;#39;button-counter&amp;#39;, {
  data: function () {
    return {
      count: 0
    }
  },
  template: &amp;#39;&amp;lt;button @click=&amp;quot;count++&amp;quot;&amp;gt;You clicked me {{ count }} times.&amp;lt;/button&amp;gt;&amp;#39;
});

new Vue({
  el: &amp;#39;#app&amp;#39;
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제와 같이 컴포넌트를 사용할 수 있습니다. 컴포넌트를 생성할 때 사용되는 옵션은 &lt;code&gt;new Vue&lt;/code&gt;에 사용되는 옵션(&lt;code&gt;data&lt;/code&gt;, &lt;code&gt;computed&lt;/code&gt;, &lt;code&gt;watch&lt;/code&gt;, &lt;code&gt;methods&lt;/code&gt; ...)과 동일 합니다. 단 하나의 예외는 컴포넌트에는 &lt;code&gt;el&lt;/code&gt; 옵션을 사용할 수 없다는 것입니다.&lt;/p&gt;
&lt;p&gt;위의 코드의 결과는 &lt;a href=&quot;https://codepen.io/beomy/pen/REjrKW?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 확인 할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;2. 컴포넌트 재사용&lt;/h1&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;button-counter&amp;gt;&amp;lt;/button-counter&amp;gt;
  &amp;lt;button-counter&amp;gt;&amp;lt;/button-counter&amp;gt;
  &amp;lt;button-counter&amp;gt;&amp;lt;/button-counter&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;Vue.component(&amp;#39;button-counter&amp;#39;, {
  data: function () {
    return {
      count: 0
    }
  },
  template: &amp;#39;You clicked me {{ count }} times.&amp;#39;
});

new Vue({
  el: &amp;#39;#app&amp;#39;
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;컴포넌트는 원하는 만큼 여러번 재사용이 가능합니다. 위의 예제의 버튼들은 클릭 될 때 각각의 &lt;code&gt;count&lt;/code&gt; 값을 나타냅니다. 컴포넌트를 사용할 때 마다 컴포넌트의 인스턴스가 생성되기 때문에 컴포넌트 마다 서로 다른 &lt;code&gt;count&lt;/code&gt;를 가지게 되는 것입니다.&lt;/p&gt;
&lt;p&gt;위의 예제 결과는 &lt;a href=&quot;https://codepen.io/beomy/pen/XozwQX&quot;&gt;CodePen&lt;/a&gt;에서 확인 할 수 있습니다.&lt;/p&gt;
&lt;h4&gt;* 참고 - 컴포넌트의 &lt;code&gt;data&lt;/code&gt; 옵션은 반드시 함수형이어야 합니다.&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;new Vue&lt;/code&gt;에서 옵션으로 넘겨주는 &lt;code&gt;data&lt;/code&gt;는 객체형, 함수형 모두 가능합니다. 하지만 컴포넌트에서 &lt;code&gt;data&lt;/code&gt;는 반드시 함수형이여야 합니다.&lt;/p&gt;
&lt;h1&gt;3. 컴포넌트 구조&lt;/h1&gt;
&lt;p&gt;Vue는 흔히 컴포넌트가 트리 형태로 중첩되어 사용됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;820&quot; height=&quot;317&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/99ABF5455C2CDE1327?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/99ABF5455C2CDE1327?original&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99ABF5455C2CDE1327&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99ABF5455C2CDE1327&quot; width=&quot;820&quot; height=&quot;317&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;/p&gt;
&lt;p&gt;Vue의 template에 컴포넌트가 사용되기 위해서는 Vue가 알 수 있도록 컴포넌트가 등록되어야 합니다. 전역으로 사용할 수 있는 글로벌 방식, 해당인스턴스에서만 사용할 수 있는 로컬 방식 두가지 방법으로 컴포넌트를 등록 할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Vue.component(&amp;#39;my-component-name&amp;#39;, {
  // ... options ...
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 방법을 사용하여 글로벌 방식으로 컴포넌트를 등록할 경우, 모든 Vue 인스턴스에서 컴포넌트를 사용 할 수 있게 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var ComponentA = { /* ... */ }

var ComponentB = {
  components: {
    &amp;#39;component-a&amp;#39;: ComponentA
  },
  // ...
}&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;import ComponentA from &amp;#39;./ComponentA.vue&amp;#39;

export default {
  components: {
    ComponentA
  },
  // ...
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 방법을 사용하여 로컬 방식으로 컴포넌트를 등록할 경우, 컴포넌트를 등록한 Vue 인스턴스에서만 컴포넌트를 사용할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;4. &lt;code&gt;props&lt;/code&gt; 사용하기&lt;/h1&gt;
&lt;p&gt;부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달하기 위해 흔히 사용 됩니다. &lt;code&gt;props&lt;/code&gt;는 &lt;code&gt;data&lt;/code&gt;와 유사하게 사용 가능한 속성입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Vue.component(&amp;#39;blog-post&amp;#39;, {
  props: [&amp;#39;title&amp;#39;],
  template: &amp;#39;&amp;lt;h3&amp;gt;{{ title }}&amp;lt;/h3&amp;gt;&amp;#39;
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제와 같이 &lt;code&gt;props&lt;/code&gt;를 정의하면 정의한 이름으로 template에 사용 가능합니다. &lt;code&gt;props&lt;/code&gt;로 원하는 만큼 전달하여 사용할 수 있으며, 모든 값을 &lt;code&gt;props&lt;/code&gt;로 전달 가능합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;blog-post title=&amp;quot;My journey with Vue&amp;quot;&amp;gt;&amp;lt;/blog-post&amp;gt;
&amp;lt;blog-post title=&amp;quot;Blogging with Vue&amp;quot;&amp;gt;&amp;lt;/blog-post&amp;gt;
&amp;lt;blog-post title=&amp;quot;Why Vue is so fun&amp;quot;&amp;gt;&amp;lt;/blog-post&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;부모 컴포넌트에서는 위의 예제와 같이 자식 컴포넌트로 데이터 전달을 할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;blog-post
    v-for=&amp;quot;post in posts&amp;quot;
    v-bind:key=&amp;quot;post.id&amp;quot;
    v-bind:title=&amp;quot;post.title&amp;quot;
  &amp;gt;&amp;lt;/blog-post&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;Vue.component(&amp;#39;blog-post&amp;#39;, {
  props: [&amp;#39;title&amp;#39;],
  template: &amp;#39;&amp;lt;h3&amp;gt;{{ title }}&amp;lt;/h3&amp;gt;&amp;#39;
})

new Vue({
  el: &amp;#39;#app&amp;#39;,
  data: {
    posts: [
      { id: 1, title: &amp;#39;My journey with Vue&amp;#39; },
      { id: 2, title: &amp;#39;Blogging with Vue&amp;#39; },
      { id: 3, title: &amp;#39;Why Vue is so fun&amp;#39; }
    ]    
  }
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제와 같이 &lt;code&gt;v-for&lt;/code&gt;와 &lt;code&gt;props&lt;/code&gt;를 함께 사용하면 코드 중복 없이 간결하게 코딩이 가능합니다.&lt;/p&gt;
&lt;p&gt;위의 예제는 &lt;a href=&quot;https://codepen.io/beomy/pen/BvYLpy&quot;&gt;CodePen&lt;/a&gt;에서 확인 할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;5. 단일 루트&lt;/h1&gt;
&lt;p&gt;컴포넌트는 단일 루트로 감싸져야 합니다. 이전에 이야기 하면서 예를 들었던 &lt;code&gt;&amp;lt;blog-post&amp;gt;&lt;/code&gt;로 이야기 하자면,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;h3&amp;gt;{{ title }}&amp;lt;/h3&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;&amp;lt;blog-post&amp;gt;&lt;/code&gt; 컴포넌트는 위의 코드와 같이 &lt;code&gt;title&lt;/code&gt;만 나타내는 하나의 엘리먼트만 가지고 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;h3&amp;gt;{{ title }}&amp;lt;/h3&amp;gt;
&amp;lt;div v-html=&amp;quot;content&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;여기에 &lt;code&gt;content&lt;/code&gt;를 나타내는 엘리먼트를 추가하기 위해 위의 예제와 같이 작성하면 에러가 발생하게 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div class=&amp;quot;blog-post&amp;quot;&amp;gt;
  &amp;lt;h3&amp;gt;{{ title }}&amp;lt;/h3&amp;gt;
  &amp;lt;div v-html=&amp;quot;content&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;컴포넌트는 위의 예제와 같이 단일 루트로 감싸져야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;blog-post
  v-for=&amp;quot;post in posts&amp;quot;
  v-bind:key=&amp;quot;post.id&amp;quot;
  v-bind:title=&amp;quot;post.title&amp;quot;
  v-bind:content=&amp;quot;post.content&amp;quot;
  v-bind:publishedAt=&amp;quot;post.publishedAt&amp;quot;
  v-bind:comments=&amp;quot;post.comments&amp;quot;
&amp;gt;&amp;lt;/blog-post&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제와 같이 컴포넌트가 커지고, 많은 데이터를 컴포넌트로 넘겨주어야 할 때 모든 데이터를 &lt;code&gt;props&lt;/code&gt;로 넘겨주는 것은 비효율적일 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;blog-post
    v-for=&amp;quot;post in posts&amp;quot;
    v-bind:key=&amp;quot;post.id&amp;quot;
    v-bind:post=&amp;quot;post&amp;quot;
  &amp;gt;&amp;lt;/blog-post&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;Vue.component(&amp;#39;blog-post&amp;#39;, {
  props: [&amp;#39;post&amp;#39;],
  template: `
    &amp;lt;div class=&amp;quot;blog-post&amp;quot;&amp;gt;
      &amp;lt;h3&amp;gt;{{ post.title }}&amp;lt;/h3&amp;gt;
      &amp;lt;div v-html=&amp;quot;post.content&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  `
})

new Vue({
  el: &amp;#39;#app&amp;#39;,
  data: {
    posts: [
      { id: 1, title: &amp;#39;My journey with Vue&amp;#39;, content: &amp;#39;1st&amp;#39; },
      { id: 2, title: &amp;#39;Blogging with Vue&amp;#39;, content: &amp;#39;2nd&amp;#39; },
      { id: 3, title: &amp;#39;Why Vue is so fun&amp;#39;, content: &amp;#39;3th&amp;#39; }
    ]
  }
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;컴포넌트는 단일 루트 엘리먼트여야 합니다.&lt;/p&gt;
&lt;p&gt;여러 데이터를 자식 컴포넌트로 전달해야 할 때 전달되어야 할 모든 값을 &lt;code&gt;props&lt;/code&gt;로 전달 하는 것이 아니라, 위의 예제와 같이 하나의 객체로 묶어 &lt;code&gt;props&lt;/code&gt;로 넘기는 것이 효율적입니다. 나중에 추가로 값을 전달 해야 할 경우, &lt;code&gt;props&lt;/code&gt;를 추가하는 것이 아니라 객체에 값을 추가하여 넘겨 주는 것으로 코드 변경을 최소화 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;위의 예제는 &lt;a href=&quot;https://codepen.io/beomy/pen/rodxZY?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 확인 할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;6. 부모 컴포넌트로 데이터 전달&lt;/h1&gt;
&lt;p&gt;자식 컴포넌트에서 부모 컴포넌트로 데이터를 전달해야 할 때가 있습니다. 그 때 사용되는 것이 &lt;code&gt;$emit&lt;/code&gt; 입니다.&lt;/p&gt;
&lt;p&gt;예를 들어, &lt;code&gt;&amp;lt;blog-post&amp;gt;&lt;/code&gt; 컴포넌트에서, 텍스트 확대 기능을 추가하기 위해 부모 컴포넌트로 텍스트 확대를 했다는 이벤트를 부모 컴포넌트로 전달해야 한다고 가정해 봅니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;blog-posts-events-demo&amp;quot;&amp;gt;
  &amp;lt;div :style=&amp;quot;{ fontSize: postFontSize + &amp;#39;em&amp;#39; }&amp;quot;&amp;gt;
    &amp;lt;blog-post
      v-for=&amp;quot;post in posts&amp;quot;
      v-bind:key=&amp;quot;post.id&amp;quot;
      v-bind:post=&amp;quot;post&amp;quot;
    &amp;gt;&amp;lt;/blog-post&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#app&amp;#39;,
  data: {
    posts: [
      { id: 1, title: &amp;#39;My journey with Vue&amp;#39;, content: &amp;#39;1st&amp;#39; },
      { id: 2, title: &amp;#39;Blogging with Vue&amp;#39;, content: &amp;#39;2nd&amp;#39; },
      { id: 3, title: &amp;#39;Why Vue is so fun&amp;#39;, content: &amp;#39;3th&amp;#39; }
    ],
    postFontSize: 1
  }
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;부모 컴포넌트는 위의 예제와 같이 작성 될 수 있습니다. &lt;code&gt;postFontSize&lt;/code&gt;로 텍스트의 크기가 정의 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Vue.component(&amp;#39;blog-post&amp;#39;, {
  props: [&amp;#39;post&amp;#39;],
  template: `
    &amp;lt;div class=&amp;quot;blog-post&amp;quot;&amp;gt;
      &amp;lt;h3&amp;gt;{{ post.title }}&amp;lt;/h3&amp;gt;
      &amp;lt;button&amp;gt;Enlarge text&amp;lt;/button&amp;gt;
      &amp;lt;div v-html=&amp;quot;post.content&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  `
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;자식 컴포넌트는 위의 예제와 같이 작성하면, &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;이 눌려젔을 때 부모 컴포넌트의 &lt;code&gt;postFontSize&lt;/code&gt;의 값이 변경 되어야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;button @click=&amp;quot;$emit(&amp;#39;enlarge-text&amp;#39;)&amp;quot;&amp;gt;Enlarge text&amp;lt;/button&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;postFontSize&lt;/code&gt;를 변경해야 한다는 것을 부모 컴포넌트에 알려 주기 위해 위의 코드와 같이 &lt;code&gt;$emit&lt;/code&gt;을 사용합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;blog-post
  v-for=&amp;quot;post in posts&amp;quot;
  v-bind:key=&amp;quot;post.id&amp;quot;
  v-bind:post=&amp;quot;post&amp;quot;
  @enlarge-text=&amp;quot;postFontSize += 0.1&amp;quot;
&amp;gt;&amp;lt;/blog-post&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;부모 컴포넌트에서는 자식 컴포넌트에서 전달한 이벤트를 핸들링 하기 위해 위에 코드와 같이 &lt;code&gt;@enlarge-text&lt;/code&gt; 라는 이벤트 핸들러를 정의해야 합니다.&lt;/p&gt;
&lt;p&gt;위의 예제는 &lt;a href=&quot;https://codepen.io/beomy/pen/gZKqzY?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 확인 할 수 있습니다.&lt;/p&gt;
&lt;h2&gt;1) &lt;code&gt;$emit&lt;/code&gt;으로 부모 컴포넌트에 데이터 전달하기&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;$emit&lt;/code&gt; 메소드의 두번째 파라미터를 사용하면, 부모 컴포넌트로 데이터 전달이 가능합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;button @click=&amp;quot;$emit(&amp;#39;enlarge-text&amp;#39;, 0.1)&amp;quot;&amp;gt;Enlarge text&amp;lt;/button&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제는 부모 컴포넌트로 &lt;code&gt;enlarge-text&lt;/code&gt; 라는 이벤트를 통해 0.1의 값을 전달하는 예제입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;blog-post
  v-for=&amp;quot;post in posts&amp;quot;
  v-bind:key=&amp;quot;post.id&amp;quot;
  v-bind:post=&amp;quot;post&amp;quot;
  @enlarge-text=&amp;quot;postFontSize += $event&amp;quot;
&amp;gt;&amp;lt;/blog-post&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;부모 컴포넌트에서는 자식 컴포넌트로 부터 전달 받은 값을 위의 예제와 같이 &lt;code&gt;$event&lt;/code&gt;를 통해 사용 가능합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;div :style=&amp;quot;{ fontSize: postFontSize + &amp;#39;em&amp;#39; }&amp;quot;&amp;gt;
    &amp;lt;blog-post
      v-for=&amp;quot;post in posts&amp;quot;
      v-bind:key=&amp;quot;post.id&amp;quot;
      v-bind:post=&amp;quot;post&amp;quot;
      @enlarge-text=&amp;quot;onEnlargeText&amp;quot;
    &amp;gt;&amp;lt;/blog-post&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;Vue.component(&amp;#39;blog-post&amp;#39;, {
  props: [&amp;#39;post&amp;#39;],
  template: `
    &amp;lt;div class=&amp;quot;blog-post&amp;quot;&amp;gt;
      &amp;lt;h3&amp;gt;{{ post.title }}&amp;lt;/h3&amp;gt;
      &amp;lt;button @click=&amp;quot;$emit(&amp;#39;enlarge-text&amp;#39;, 0.1)&amp;quot;&amp;gt;Enlarge text&amp;lt;/button&amp;gt;
      &amp;lt;div v-html=&amp;quot;post.content&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  `
})

new Vue({
  el: &amp;#39;#app&amp;#39;,
  data: {
    posts: [
      { id: 1, title: &amp;#39;My journey with Vue&amp;#39;, content: &amp;#39;1st&amp;#39; },
      { id: 2, title: &amp;#39;Blogging with Vue&amp;#39;, content: &amp;#39;2nd&amp;#39; },
      { id: 3, title: &amp;#39;Why Vue is so fun&amp;#39;, content: &amp;#39;3th&amp;#39; }
    ],
    postFontSize: 1
  },
  methods: {
    onEnlargeText: function (enlargeAmount) {
      this.postFontSize += enlargeAmount
    }
  }
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;일반 네이티브 이벤트와 동일하게 이벤트 핸들러를 사용할 수도 있습니다.&lt;/p&gt;
&lt;p&gt;위의 예제는 &lt;a href=&quot;https://codepen.io/beomy/pen/XoYQbW?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 확인 할 수 있습니다.&lt;/p&gt;
&lt;h2&gt;2) 컴포넌트에서 &lt;code&gt;v-model&lt;/code&gt; 사용하기&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;input v-model=&amp;quot;searchText&amp;quot;&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;input&lt;/code&gt; 태그에서 &lt;code&gt;v-model&lt;/code&gt;을 위의 코드와 같이 사용할 경우,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;input
  v-bind:value=&amp;quot;searchText&amp;quot;
  v-on:input=&amp;quot;searchText = $event.target.value&amp;quot;
&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같은 동작을 하게 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;custom-input
  v-bind:value=&amp;quot;searchText&amp;quot;
  v-on:input=&amp;quot;searchText = $event&amp;quot;
&amp;gt;&amp;lt;/custom-input&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;value&lt;/code&gt; attribute는 &lt;code&gt;custom-input&lt;/code&gt;의 &lt;code&gt;value&lt;/code&gt; prop와 바인딩 됩니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;input&lt;/code&gt; 이벤트는 &lt;code&gt;custom-input&lt;/code&gt;에서 &lt;code&gt;$emit&lt;/code&gt;으로 호출되는 이벤트입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;위와 같이 작성이 된 코드는,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Vue.component(&amp;#39;custom-input&amp;#39;, {
  props: [&amp;#39;value&amp;#39;],
  template: `
    &amp;lt;input
      v-bind:value=&amp;quot;value&amp;quot;
      v-on:input=&amp;quot;$emit(&amp;#39;input&amp;#39;, $event.target.value)&amp;quot;
    &amp;gt;
  `
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위와 같이 다시 작성 될 수 있습니다. 위의 코드는 또 다시,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;custom-input v-model=&amp;quot;searchText&amp;quot;&amp;gt;&amp;lt;/custom-input&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 작성될 수 있습니다. 컴포넌트에서 &lt;code&gt;v-model&lt;/code&gt;을 사용하는 방법은 &lt;a href=&quot;https://beomy.tistory.com/57&quot;&gt;[Vue.JS] 컴포넌트 (고급:Custrom Events)&lt;/a&gt; 에서 더 자세히 이야기 하도록 하겠습니다.&lt;/p&gt;
&lt;h1&gt;7. &lt;code&gt;slot&lt;/code&gt; 사용하기&lt;/h1&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;alert-box&amp;gt;
  Something bad happened.
&amp;lt;/alert-box&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;컴포넌트를 사용할 때, HTML 엘리먼트와 같이 위의 코드처럼 컴포넌트에 콘덴츠를 전달해야 할 때가 있습니다. 그럴 때 사용해야 하는 것이 &lt;code&gt;slot&lt;/code&gt; 입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Vue.component(&amp;#39;alert-box&amp;#39;, {
  template: `
    &amp;lt;div class=&amp;quot;demo-alert-box&amp;quot;&amp;gt;
      &amp;lt;strong&amp;gt;Error!&amp;lt;/strong&amp;gt;
      &amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;
    &amp;lt;/div&amp;gt;
  `
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;alert-box&lt;/code&gt; 컴포넌트가 위의 코드와 같이 코딩되어 있다면,&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;322&quot; height=&quot;41&quot; alt=&quot;slot 사용 예제 결과&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/99183A4F5C3DFAA72B?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/99183A4F5C3DFAA72B?original&quot; data-alt=&quot;slot 사용 예제 결과&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99183A4F5C3DFAA72B&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99183A4F5C3DFAA72B&quot; width=&quot;322&quot; height=&quot;41&quot; alt=&quot;slot 사용 예제 결과&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;slot 사용 예제 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;alert-box&amp;gt;Somthing bad happend.&amp;lt;/alert-box&amp;gt;&lt;/code&gt;의 예제 결과는 위의 사진과 같습니다. 위의 예제 코드 결과는 &lt;a href=&quot;https://codepen.io/beomy/pen/gZEPWJ?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 확인 할 수 있습니다. &lt;code&gt;slot&lt;/code&gt;에 대한 좀 더 자세한 이야기는 &lt;a href=&quot;https://beomy.tistory.com/58&quot;&gt;[Vue.JS] 컴포넌트 (고급:slot)&lt;/a&gt; 에서 이야기 하도록 하겠습니다.&lt;/p&gt;
&lt;h1&gt;8. 동적 컴포넌트&lt;/h1&gt;
&lt;p&gt;동적으로 컴포넌트를 전환해야 할 때가 있습니다. 그럴 때 사용하기 유용한 것이 &lt;code&gt;&amp;lt;component&amp;gt;&lt;/code&gt;와 &lt;code&gt;is&lt;/code&gt; 입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- Component changes when currentTabComponent changes --&amp;gt;
&amp;lt;component v-bind:is=&amp;quot;currentTabComponent&amp;quot;&amp;gt;&amp;lt;/component&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;currentTabComponent&lt;/code&gt;의 리턴 값은 컴포넌트의 이름이나 컴포넌트 객체입니다. 위의 예제는 &lt;a href=&quot;https://jsfiddle.net/chrisvfritz/o3nycadu/&quot;&gt;Fiddle&lt;/a&gt;에서 확인 할 수 있습니다. 동적 컴포넌트에 관한 좀 더 자세한 내용은 &lt;a href=&quot;https://beomy.tistory.com/59&quot;&gt;[Vue.JS] 컴포넌트 (고급:Dynamic &amp;amp; Async Components)&lt;/a&gt; 에서 이야기 하도록 하겠습니다.&lt;/p&gt;
&lt;h1&gt;9. DOM 템블릿 문법 경고 해결&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;ol&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; 등의 HTML 태그들의 자식 태그로는 &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;tr&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;option&amp;gt;&lt;/code&gt;등으로 항상 정해져 있습니다. 이러한 규칙을 어기게 되면 DOM은 경고를 뱉어내가 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;table&amp;gt;
  &amp;lt;blog-post-row&amp;gt;&amp;lt;/blog-post-row&amp;gt;
&amp;lt;/table&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;예를 들어, 위의 코드와 같이 작성된 코드가 있다고 할 때, &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt;의 자식 태그들은 &lt;code&gt;&amp;lt;tr&amp;gt;&lt;/code&gt; 등.. 이 와야 하지만 그렇지 않기 때문에 DOM은 경고를 발생시킵니다. &lt;code&gt;&amp;lt;blog-post-row&amp;gt;&lt;/code&gt;가 &lt;code&gt;&amp;lt;tr&amp;gt;&lt;/code&gt; 태그로 구성되어 있다고 해도 경고를 피해 갈 수 없습니다. DOM이 발생하는 경고를 피하기 위해 &lt;code&gt;is&lt;/code&gt;를 사용할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;table&amp;gt;
  &amp;lt;tr is=&amp;quot;blog-post-row&amp;quot;&amp;gt;&amp;lt;/tr&amp;gt;
&amp;lt;/table&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위와 같이 &lt;code&gt;is&lt;/code&gt;를 이용하거나,&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;template 속성을 사용한다 (e.g. &lt;code&gt;template: &amp;#39;...&amp;#39;&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.vue&lt;/code&gt; 파일로 컴포넌트를 생성한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;script type=&amp;quot;text/x-template&amp;quot;&amp;gt;&lt;/code&gt; 를 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;위의 3가지 방법을 사용하면 DOM의 경고를 피할 수 있는 방법이 있습니다.&lt;/p&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://vuejs.org/v2/guide/components.html&quot;&gt;https://vuejs.org/v2/guide/components.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>component</category>
      <category>emit</category>
      <category>is</category>
      <category>props</category>
      <category>Slot</category>
      <category>Vue.js</category>
      <category>컴포넌트</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/55</guid>
      <comments>https://beomy.tistory.com/55#entry55comment</comments>
      <pubDate>Wed, 16 Jan 2019 22:56:30 +0900</pubDate>
    </item>
    <item>
      <title>[Vue.JS] 폼 입력 바인딩</title>
      <link>https://beomy.tistory.com/54</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;820&quot; height=&quot;289&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/995E154D5C0BEFE92E?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/995E154D5C0BEFE92E?original&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/995E154D5C0BEFE92E&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F995E154D5C0BEFE92E&quot; width=&quot;820&quot; height=&quot;289&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;code&gt;input&lt;/code&gt;, &lt;code&gt;textarea&lt;/code&gt;, &lt;code&gt;select&lt;/code&gt; 엘리먼트)에 양방향 데이터 바인딩을 하는 방법을 이야기 할 것입니다.&lt;/p&gt;
&lt;h1&gt;1. &lt;code&gt;v-model&lt;/code&gt; 사용법&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;v-model&lt;/code&gt; 디렉티브를 사용하여 폼 엘리먼트(&lt;code&gt;input&lt;/code&gt;, &lt;code&gt;textarea&lt;/code&gt;, &lt;code&gt;select&lt;/code&gt; 엘리먼트)에 양방향 데이터 바인딩을 할 수 있습니다. &lt;code&gt;v-model&lt;/code&gt;은 모든 폼 엘리먼트의 초기 &lt;code&gt;value&lt;/code&gt;와 &lt;code&gt;checked&lt;/code&gt;, &lt;code&gt;selected&lt;/code&gt; 속성을 무시합니다. Vue 인스턴스의 데이터에 의해서만 동작합니다.&lt;/p&gt;
&lt;h4&gt;참고 - IME 사용시 유의사항&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Input_method&quot;&gt;IME&lt;/a&gt; (중국어, 일본어, 한국어 등..)가 필요한 언어의 경우 IME 중 &lt;code&gt;v-model&lt;/code&gt;이 업데이트 되지 않습니다. IME 중 업데이트 처리를 하기 위해서는 &lt;code&gt;input&lt;/code&gt; 이벤트를 사용해야 합니다.&lt;/p&gt;
&lt;h2&gt;문자열&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;input v-model=&amp;quot;message&amp;quot;&amp;gt;
  &amp;lt;p&amp;gt;메시지: {{ message }}&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#app&amp;#39;,
  data: {
    message: &amp;#39;&amp;#39;
  }
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제 결과는 &lt;a href=&quot;https://codepen.io/beomy/pen/KbpwWO?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 확인 할 수 있습니다.&lt;/p&gt;
&lt;h2&gt;여러줄을 가진 문장&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;span&amp;gt;여러 줄을 가지는 메시지:&amp;lt;/span&amp;gt;
  &amp;lt;p style=&amp;quot;white-space: pre-line&amp;quot;&amp;gt;{{ message }}&amp;lt;/p&amp;gt;
  &amp;lt;br&amp;gt;
  &amp;lt;textarea v-model=&amp;quot;message&amp;quot;&amp;gt;&amp;lt;/textarea&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#app&amp;#39;,
  data: {
    message: &amp;#39;&amp;#39;
  }
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제 결과는 &lt;a href=&quot;https://codepen.io/beomy/pen/jXPEZN?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 확인 할 수 있습니다. &lt;code&gt;&amp;lt;textarea&amp;gt;{{ message }}&amp;lt;/textarea&amp;gt;&lt;/code&gt; 는 등작하지 않습니다.&lt;/p&gt;
&lt;h2&gt;체크박스&lt;/h2&gt;
&lt;p&gt;하나의 체크박스는 boolean 값을 가집니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;input type=&amp;quot;checkbox&amp;quot; id=&amp;quot;checkbox&amp;quot; v-model=&amp;quot;checked&amp;quot;&amp;gt;
  &amp;lt;label for=&amp;quot;checkbox&amp;quot;&amp;gt;{{ checked }}&amp;lt;/label&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#app&amp;#39;,
  data: {
    checked: false
  }
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제 결과는 &lt;a href=&quot;https://codepen.io/beomy/pen/QzbNdE?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 확인 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;여러개의 체크박스는 배열 값을 가집니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;input type=&amp;quot;checkbox&amp;quot; id=&amp;quot;jack&amp;quot; value=&amp;quot;Jack&amp;quot; v-model=&amp;quot;checkedNames&amp;quot;&amp;gt;
  &amp;lt;label for=&amp;quot;jack&amp;quot;&amp;gt;Jack&amp;lt;/label&amp;gt;
  &amp;lt;input type=&amp;quot;checkbox&amp;quot; id=&amp;quot;john&amp;quot; value=&amp;quot;John&amp;quot; v-model=&amp;quot;checkedNames&amp;quot;&amp;gt;
  &amp;lt;label for=&amp;quot;john&amp;quot;&amp;gt;John&amp;lt;/label&amp;gt;
  &amp;lt;input type=&amp;quot;checkbox&amp;quot; id=&amp;quot;mike&amp;quot; value=&amp;quot;Mike&amp;quot; v-model=&amp;quot;checkedNames&amp;quot;&amp;gt;
  &amp;lt;label for=&amp;quot;mike&amp;quot;&amp;gt;Mike&amp;lt;/label&amp;gt;
  &amp;lt;br&amp;gt;
  &amp;lt;span&amp;gt;체크한 이름: {{ checkedNames }}&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#app&amp;#39;,
  data: {
    checkedNames: []
  }
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제 결과는 &lt;a href=&quot;https://codepen.io/beomy/pen/qLdZXM?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 확인할 수 있습니다.&lt;/p&gt;
&lt;h2&gt;라디오&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;input type=&amp;quot;radio&amp;quot; id=&amp;quot;one&amp;quot; value=&amp;quot;One&amp;quot; v-model=&amp;quot;picked&amp;quot;&amp;gt;
  &amp;lt;label for=&amp;quot;one&amp;quot;&amp;gt;One&amp;lt;/label&amp;gt;
  &amp;lt;br&amp;gt;
  &amp;lt;input type=&amp;quot;radio&amp;quot; id=&amp;quot;two&amp;quot; value=&amp;quot;Two&amp;quot; v-model=&amp;quot;picked&amp;quot;&amp;gt;
  &amp;lt;label for=&amp;quot;two&amp;quot;&amp;gt;Two&amp;lt;/label&amp;gt;
  &amp;lt;br&amp;gt;
  &amp;lt;span&amp;gt;선택: {{ picked }}&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#app&amp;#39;,
  data: {
    picked: &amp;#39;&amp;#39;
  }
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제 결과는 &lt;a href=&quot;https://codepen.io/beomy/pen/XoXdmK?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 확인 할 수 있습니다.&lt;/p&gt;
&lt;h2&gt;셀렉트&lt;/h2&gt;
&lt;h3&gt;하나의 셀렉트&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;select v-model=&amp;quot;selected&amp;quot;&amp;gt;
    &amp;lt;option disabled value=&amp;quot;&amp;quot;&amp;gt;Please select one&amp;lt;/option&amp;gt;
    &amp;lt;option&amp;gt;A&amp;lt;/option&amp;gt;
    &amp;lt;option&amp;gt;B&amp;lt;/option&amp;gt;
    &amp;lt;option&amp;gt;C&amp;lt;/option&amp;gt;
  &amp;lt;/select&amp;gt;
  &amp;lt;span&amp;gt;선택함: {{ selected }}&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#app&amp;#39;,
  data: {
    selected: &amp;#39;&amp;#39;
  }
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제 결과는 &lt;a href=&quot;https://codepen.io/beomy/pen/EGPKgZ?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 확인 할 수 있습니다.&lt;/p&gt;
&lt;h3&gt;다중 셀렉트&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;select v-model=&amp;quot;selected&amp;quot; multiple&amp;gt;
    &amp;lt;option&amp;gt;A&amp;lt;/option&amp;gt;
    &amp;lt;option&amp;gt;B&amp;lt;/option&amp;gt;
    &amp;lt;option&amp;gt;C&amp;lt;/option&amp;gt;
  &amp;lt;/select&amp;gt;
  &amp;lt;br&amp;gt;
  &amp;lt;span&amp;gt;Selected: {{ selected }}&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#app&amp;#39;,
  data: {
    selected: []
  }
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제 결과는 &lt;a href=&quot;https://codepen.io/beomy/pen/RErapB?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 확인 할 수 있습니다.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;v-for&lt;/code&gt;을 사용한 동적 옵션 렌더링&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;select v-model=&amp;quot;selected&amp;quot;&amp;gt;
    &amp;lt;option v-for=&amp;quot;option in options&amp;quot; v-bind:value=&amp;quot;option.value&amp;quot;&amp;gt;
      {{ option.text }}
    &amp;lt;/option&amp;gt;
  &amp;lt;/select&amp;gt;
  &amp;lt;span&amp;gt;Selected: {{ selected }}&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#app&amp;#39;,
  data: {
    selected: &amp;#39;A&amp;#39;,
    options: [
      { text: &amp;#39;One&amp;#39;, value: &amp;#39;A&amp;#39; },
      { text: &amp;#39;Two&amp;#39;, value: &amp;#39;B&amp;#39; },
      { text: &amp;#39;Three&amp;#39;, value: &amp;#39;C&amp;#39; }
    ]
  }
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제 결과는 &lt;a href=&quot;https://codepen.io/beomy/pen/Ydwqxv?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 확인 할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;2. 값 바인딩하기&lt;/h1&gt;
&lt;p&gt;라디오, 체크박스, 셀렉트 옵션의 경우, &lt;code&gt;v-model&lt;/code&gt;에 바인딩 되는 값은 보통 정적인 값(문자열 또는 boolean)입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- `picked` 는 선택시 문자열 &amp;quot;a&amp;quot; 입니다 --&amp;gt;
&amp;lt;input type=&amp;quot;radio&amp;quot; v-model=&amp;quot;picked&amp;quot; value=&amp;quot;a&amp;quot;&amp;gt;

&amp;lt;!-- `toggle` 는 true 또는 false 입니다 --&amp;gt;
&amp;lt;input type=&amp;quot;checkbox&amp;quot; v-model=&amp;quot;toggle&amp;quot;&amp;gt;

&amp;lt;!-- `selected`는 &amp;quot;ABC&amp;quot; 선택시 &amp;quot;abc&amp;quot; 입니다 --&amp;gt;
&amp;lt;select v-model=&amp;quot;selected&amp;quot;&amp;gt;
  &amp;lt;option value=&amp;quot;abc&amp;quot;&amp;gt;ABC&amp;lt;/option&amp;gt;
&amp;lt;/select&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;v-model&lt;/code&gt;에 동적으로 값을 바인딩 해야 할 경우가 있습니다. 이 때 &lt;code&gt;v-bind&lt;/code&gt;를 사용하면 됩니다. &lt;code&gt;v-bind&lt;/code&gt;를 사용하면 입력 값이 문자열이 아닌 값도 바인딩 할 수 있습니다.&lt;/p&gt;
&lt;h2&gt;체크박스&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;input
  type=&amp;quot;checkbox&amp;quot;
  v-model=&amp;quot;toggle&amp;quot;
  true-value=&amp;quot;yes&amp;quot;
  false-value=&amp;quot;no&amp;quot;
&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;// 체크된 경우
vm.toggle === &amp;#39;yes&amp;#39;
// 체크 되지 않은 경우
vm.toggle === &amp;#39;no&amp;#39;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;true-value&lt;/code&gt;와 &lt;code&gt;false-value&lt;/code&gt;는 &lt;code&gt;input&lt;/code&gt;의 &lt;code&gt;value&lt;/code&gt;에 영향을 미치지 않기 때문에 브라우저의 &lt;code&gt;form&lt;/code&gt;의 &lt;code&gt;submit&lt;/code&gt;에 포함되지 않습니다.&lt;/p&gt;
&lt;h2&gt;라디오&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;input type=&amp;quot;radio&amp;quot; v-model=&amp;quot;pick&amp;quot; v-bind:value=&amp;quot;a&amp;quot;&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;// 체크 하면:
vm.pick === vm.a&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;v-model&lt;/code&gt;에 라디오 동적 바인딩 하기&lt;/p&gt;
&lt;h2&gt;셀렉트 옵션&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;select v-model=&amp;quot;selected&amp;quot;&amp;gt;
  &amp;lt;!-- inline object literal --&amp;gt;
  &amp;lt;option v-bind:value=&amp;quot;{ number: 123 }&amp;quot;&amp;gt;123&amp;lt;/option&amp;gt;
&amp;lt;/select&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;// 선택 하면:
typeof vm.selected // -&amp;gt; &amp;#39;object&amp;#39;
vm.selected.number // -&amp;gt; 123&lt;/code&gt;&lt;/pre&gt;&lt;h1&gt;수식어&lt;/h1&gt;
&lt;h2&gt;&lt;code&gt;.lazy&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;v-model&lt;/code&gt;은 입력 이벤트 후 데이터를 동기화 합니다(IME는 제외). &lt;code&gt;.lazy&lt;/code&gt; 수식어를 사용하여 &lt;code&gt;change&lt;/code&gt; 이벤트 이후에 동기화 할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- &amp;quot;input&amp;quot; 대신 &amp;quot;change&amp;quot; 이후에 동기화 됩니다. --&amp;gt;
&amp;lt;input v-model.lazy=&amp;quot;msg&amp;quot; &amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;&lt;code&gt;.number&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;.number&lt;/code&gt; 수식어는 &lt;code&gt;v-model&lt;/code&gt;에 사용자 입력이 숫자형으로 업데이트하기 위해 사용됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;input v-model.number=&amp;quot;age&amp;quot; type=&amp;quot;number&amp;quot;&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;type=&amp;quot;number&amp;quot;&lt;/code&gt;을 사용하더라도 사용자 입력 값이 항상 문자열로 바인딩되기 때문에 유용하게 사용할 수 있는 수식어입니다.&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;.trim&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;.trim&lt;/code&gt; 수식어는 &lt;code&gt;v-model&lt;/code&gt;에 사용자 입력 값이 &lt;code&gt;trim&lt;/code&gt; 된 값을 바인딩하기 위해 사용됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;input v-model.trim=&amp;quot;msg&amp;quot;&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://kr.vuejs.org/v2/guide/forms.html&quot;&gt;https://kr.vuejs.org/v2/guide/forms.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>.lazy</category>
      <category>.number</category>
      <category>.trim</category>
      <category>form</category>
      <category>input</category>
      <category>select</category>
      <category>textarea</category>
      <category>V-Model</category>
      <category>Vue.js</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/54</guid>
      <comments>https://beomy.tistory.com/54#entry54comment</comments>
      <pubDate>Sat, 15 Dec 2018 14:01:37 +0900</pubDate>
    </item>
    <item>
      <title>[Vue.JS] 이벤트 핸들링</title>
      <link>https://beomy.tistory.com/53</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;820&quot; height=&quot;289&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/997A70335BFEB42222?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/997A70335BFEB42222?original&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/997A70335BFEB42222&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F997A70335BFEB42222&quot; width=&quot;820&quot; height=&quot;289&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;h1&gt;1. 이벤트 리스너&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;v-on&lt;/code&gt; 디렉티브는 이벤트 리스너입니다. &lt;code&gt;v-on&lt;/code&gt; 디렉티브는 DOM 이벤트가 발행하는지 듣고 있다가 이벤트가 발생 했을 때 JavaScript를 실행합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;button v-on:click=&amp;quot;counter += 1&amp;quot;&amp;gt;Add 1&amp;lt;/button&amp;gt;
  &amp;lt;p&amp;gt;위 버튼을 클릭한 횟수는 {{ counter }} 번 입니다.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#app&amp;#39;,
  data: {
    counter: 0
  }
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제는 &lt;code&gt;v-on&lt;/code&gt;이라는 이벤트 리스너가 &lt;code&gt;click&lt;/code&gt; 이벤트가 발행하는지 듣고 있다가 이벤트가 발생 되었을 때 간단한 JavaScript를 실행하는 예제입니다. &lt;a href=&quot;https://codepen.io/beomy/pen/JemOYX?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 결과를 확인 할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;2. 메소드 이벤트 핸들러&lt;/h1&gt;
&lt;p&gt;대부분의 이벤트 핸들러의 로직은 복잡하기 때문에 &lt;code&gt;v-on&lt;/code&gt;에 JavaScript로 넘겨주기 어렵습니다. 그렇기 때문에 &lt;code&gt;v-on&lt;/code&gt;에 &lt;code&gt;methods&lt;/code&gt;의 함수를 넘겨주는 형태로 구현 하는 것이 좋습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;button v-on:click=&amp;quot;greet&amp;quot;&amp;gt;Greet&amp;lt;/button&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#app&amp;#39;,
  data: {
    name: &amp;#39;Vue.js&amp;#39;
  },
  // 메소드는 `methods` 객체 안에 정의합니다
  methods: {
    greet: function (event) {
      // 메소드 안에서 사용하는 `this` 는 Vue 인스턴스를 가리킵니다
      alert(&amp;#39;Hello &amp;#39; + this.name + &amp;#39;!&amp;#39;)
      // `event` 는 네이티브 DOM 이벤트입니다
      if (event) {
        alert(event.target.tagName)
      }
    }
  }
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;v-on&lt;/code&gt;은 &lt;code&gt;click&lt;/code&gt; 이벤트가 발생하는지 듣고 있다가 &lt;code&gt;click&lt;/code&gt; 이벤트가 발생하면, &lt;code&gt;methods&lt;/code&gt;에 정의한 &lt;code&gt;greet&lt;/code&gt; 함수를 실행하는 예제입니다. 결과는 &lt;a href=&quot;https://codepen.io/beomy/pen/JexxpZ?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 확인 할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;3. 인라인 메소드 핸들러&lt;/h1&gt;
&lt;p&gt;메소드 이름을 &lt;code&gt;v-on&lt;/code&gt;에 직접 바인딩 하는 대신 인라인 JavaScript를 사용하여 &lt;code&gt;methods&lt;/code&gt;의 함수를 호출 할 수도 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;button v-on:click=&amp;quot;say(&amp;#39;hi&amp;#39;)&amp;quot;&amp;gt;Say hi&amp;lt;/button&amp;gt;
  &amp;lt;button v-on:click=&amp;quot;say(&amp;#39;what&amp;#39;)&amp;quot;&amp;gt;Say what&amp;lt;/button&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#app&amp;#39;,
  methods: {
    say: function (message) {
      alert(message)
    }
  }
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드 같이 &lt;code&gt;v-on&lt;/code&gt; 이벤트 리스너에서 &lt;code&gt;methods&lt;/code&gt;에 정의 된 함수를 직접 호출 할 수 있습니다. 인라인으로 &lt;code&gt;methods&lt;/code&gt;에 정의 된 함수를 호출할 때 DOM 이벤트 객체를 사용해야 할 필요가 있을 때가 있습니다. &lt;code&gt;$event&lt;/code&gt;라는 인자를 &lt;code&gt;methods&lt;/code&gt;의 함수에 넘겨주어 DOM 이벤트 객체를 사용할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;button v-on:click=&amp;quot;warn(&amp;#39;Form cannot be submitted yet.&amp;#39;, $event)&amp;quot;&amp;gt;Submit&amp;lt;/button&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#app&amp;#39;,
  methods: {
    warn: function (message, event) {
      // 이제 네이티브 이벤트에 액세스 할 수 있습니다
      if (event) event.preventDefault()
      alert(message)
    }
  }
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;인라인 메소드 핸들러 예제는 &lt;a href=&quot;https://codepen.io/beomy/pen/aQMXLy?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 확인 할 수 있습니다.  &lt;/p&gt;
&lt;h1&gt;4. 이벤트 수식어&lt;/h1&gt;
&lt;p&gt;이벤트 핸들러 내부에서 종종 &lt;code&gt;event.preventDefault()&lt;/code&gt; 나 &lt;code&gt;event.stopPropagation()&lt;/code&gt; 를 호출할 때가 있습니다. Vue에서도 마찬가지로 &lt;code&gt;preventDefault&lt;/code&gt;나 &lt;code&gt;stopPropagation&lt;/code&gt;을 사용해야 할 때가 있습니다. 그 때에 이벤트 핸들러 내부에서 이벤트 객체를 사용할 수도 있지만 &lt;code&gt;v-on&lt;/code&gt; 이벤트 리스너에서 사용할 수도 있습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.stop&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.prevent&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.capture&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.self&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.once&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;v-on&lt;/code&gt; 뒤에 위와 같이 점(.)으로 시작하는 접미사를 사용하면 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- 클릭 이벤트 전파가 중단됩니다 --&amp;gt;
&amp;lt;a v-on:click.stop=&amp;quot;doThis&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;

&amp;lt;!-- 제출 이벤트가 페이지를 다시 로드 하지 않습니다 --&amp;gt;
&amp;lt;form v-on:submit.prevent=&amp;quot;onSubmit&amp;quot;&amp;gt;&amp;lt;/form&amp;gt;

&amp;lt;!-- 수식어는 체이닝 가능합니다 --&amp;gt;
&amp;lt;a v-on:click.stop.prevent=&amp;quot;doThat&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;

&amp;lt;!-- 단순히 수식어만 사용할 수 있습니다 --&amp;gt;
&amp;lt;form v-on:submit.prevent&amp;gt;&amp;lt;/form&amp;gt;

&amp;lt;!-- 이벤트 리스너를 추가할 때 캡처모드를 사용합니다 --&amp;gt;
&amp;lt;!-- 즉, 내부 엘리먼트를 대상으로 하는 이벤트가 해당 엘리먼트에서 처리되기 전에 여기서 처리합니다. --&amp;gt;
&amp;lt;div v-on:click.capture=&amp;quot;doThis&amp;quot;&amp;gt;...&amp;lt;/div&amp;gt;


&amp;lt;!-- event.target이 엘리먼트 자체인 경우에만 트리거를 처리합니다 --&amp;gt;
&amp;lt;!-- 자식 엘리먼트에서는 안됩니다 --&amp;gt;
&amp;lt;div v-on:click.self=&amp;quot;doThat&amp;quot;&amp;gt;...&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;이벤트 수식어를 사용한 순서대로 적용되기 때문에 여러개의 수식어를 사용 할 때 수식어 사용 순서를 고려해야 합니다. 즉 &lt;code&gt;@click.prevent.self&lt;/code&gt;를 사용하면 클릭 이벤트가 발생 했을 때 모든 엘리먼트의 기본 동작을 막을 수 있지만, &lt;code&gt;@click.self.prevent&lt;/code&gt;는 엘리먼트 자체의 기본 동작만 막을 수 있습니다.&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;.once&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;2.1.4에 추가된 이벤트 수식어입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- 클릭 이벤트는 최대 한번만 트리거 됩니다. --&amp;gt;
&amp;lt;a v-on:click.once=&amp;quot;doThis&amp;quot;&amp;gt;&amp;lt;/a&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;네이티브 DOM 이벤트에서만 동작하는 다른 수식어와 달리 &lt;code&gt;.once&lt;/code&gt; 수식어는 컴포넌트 이벤트(컴포넌트에서 발생하는 사용자 정의 이벤트)에서도 사용할 수 있습니다.&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;.passive&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;2.3.0 이후에 추가된 이벤트 수식어입니다. 모바일 환경에서 성능향상에 도움이 되는 이벤트 수식어 입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- 스크롤의 기본 이벤트를 취소할 수 없습니다. --&amp;gt;
&amp;lt;div v-on:scroll.passive=&amp;quot;onScroll&amp;quot;&amp;gt;...&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;지정한 이벤트 핸들러에서 &lt;code&gt;event.preventDefault()&lt;/code&gt;를 호출하지 않는 다는 것을 브라우저에 알리는 이벤트 수식어입니다. &lt;code&gt;.passive&lt;/code&gt; 이벤트 수식어가 없다면 &lt;code&gt;event.preventDefault()&lt;/code&gt;가 호출 될 수도 있기 때문에, 이벤트 핸들러가 동작이 완료 된 후 스크롤됩니다.&lt;/p&gt;
&lt;h1&gt;5. 키 수식어&lt;/h1&gt;
&lt;p&gt;키보드 이벤트를 처리할 때, 특정 키 코드를 확인 해야 할 때가 있습니다. Vue는 키 수식어를 사용하여 특정 키 코드를 처리할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- keyCode가 13일 때만 `vm.submit()`을 호출합니다  --&amp;gt;
&amp;lt;input v-on:keyup.13=&amp;quot;submit&amp;quot;&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Vue는 자주 사용하는 키 코드의 별칭을 제공합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.enter&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.tab&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.delete&lt;/code&gt; (Delete와 Backspace 키 모두 캡처합니다)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.esc&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.space&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.up&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.down&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.left&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.right&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;위의 리스트는 키 수식어의 별칭 목록입니다. &lt;code&gt;config.keyCodes&lt;/code&gt; 객체를 통해 사용자 지정 키 수식어 별칭을 지정할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// `v-on:keyup.f1`을 사용할 수 있습니다.
Vue.config.keyCodes.f1 = 112&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;오토매틱 키 수식어&lt;/h2&gt;
&lt;p&gt;이 기능은 2.5.0 이상의 Vue 버전에서 추가 된 기능입니다. &lt;code&gt;KeyboardEvent.key&lt;/code&gt;를 통해 노출된 유효 키 이름을 수식어로 사용 할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;input @keyup.page-down=&amp;quot;onPageDown&amp;quot;&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위 예제에서 핸들러는 &lt;code&gt;$event.key === &amp;#39;PageDown&amp;#39;&lt;/code&gt; 일 때만 호출 됩니다.&lt;/p&gt;
&lt;h4&gt;참고 -&lt;/h4&gt;
&lt;p&gt;일부 키(&lt;code&gt;.esc&lt;/code&gt;와 모든 화살토 키)는 IE9에서 일관성 없는 &lt;code&gt;key&lt;/code&gt; 값을 가집니다. IE9를 지원해야 하는 경우 내장 별칭을 사용하는 것이 좋습니다.&lt;/p&gt;
&lt;h1&gt;6. 시스템 키 수식어&lt;/h1&gt;
&lt;p&gt;2.1.0 이상의 버전에서 추가된 기능입니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.ctrl&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.alt&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.shift&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.meta&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;위의 수식어를 사용해 해당 수식어 키가 눌러진 경우에는 마우스 또는 키보드 이벤트 리스너를 트리거 할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- Alt + C --&amp;gt;
&amp;lt;input @keyup.alt.67=&amp;quot;clear&amp;quot;&amp;gt;

&amp;lt;!-- Ctrl + Click --&amp;gt;
&amp;lt;div @click.ctrl=&amp;quot;doSomething&amp;quot;&amp;gt;Do something&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;시스템 키 수식어는 &lt;code&gt;keyup&lt;/code&gt; 이벤트와 함께 사용되면 이벤트가 발생할 때 수식어 키가 눌려있어야 합니다. 즉, &lt;code&gt;key.ctrl&lt;/code&gt;은 &lt;code&gt;ctrl&lt;/code&gt;을 누른 상태에서 키를 놓으면 트리거 됩니다. &lt;code&gt;ctrl&lt;/code&gt; 키만 놓으면 트리거 되지 않습니다.&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;.exact&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;2.5.0 이상의 버전에서 추가된 기능입니다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.exact&lt;/code&gt; 수식어를 사용하여 다른 시스템 수식어와 조합해 그 핸들러가 실행되기 위한 정확한 조합을 만들 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- Alt 또는 Shift와 함께 눌린 경우에도 실행됩니다. --&amp;gt;
&amp;lt;button @click.ctrl=&amp;quot;onClick&amp;quot;&amp;gt;A&amp;lt;/button&amp;gt;

&amp;lt;!-- Ctrl 키만 눌려있을 때만 실행됩니다. --&amp;gt;
&amp;lt;button @click.ctrl.exact=&amp;quot;onCtrlClick&amp;quot;&amp;gt;A&amp;lt;/button&amp;gt;

&amp;lt;!-- 아래 코드는 시스템 키가 눌리지 않은 상태인 경우에만 작동합니다. --&amp;gt;
&amp;lt;button @click.exact=&amp;quot;onClick&amp;quot;&amp;gt;A&amp;lt;/button&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;마우스 버튼 수식어&lt;/h2&gt;
&lt;p&gt;2.2.0 이상의 버전에서 추가된 기능입니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.left&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.right&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.middle&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;위의 수식어는 특정 마우스 버튼이 트리거 되었을 때 이벤트 핸들러를 실행합니다.&lt;/p&gt;
&lt;h1&gt;7. HTML 이벤트 리스너를 사용할까?&lt;/h1&gt;
&lt;p&gt;Vue에서 사용하는 이벤트 리스너 등록 방법은 &lt;a href=&quot;https://en.wikipedia.org/wiki/Separation_of_concerns&quot;&gt;관심사 분리(separation of concerns)&lt;/a&gt; 규칙에 어긋난다고 생각할 수 있습니다. (관심사 분리란 JavaScript와 HTML, CSS 분리하는 것, 즉 각각의 역할 별로 분리하는 것을 말합니다.)&lt;/p&gt;
&lt;p&gt;&lt;code&gt;v-on&lt;/code&gt;을 사용하여 이벤트를 핸들링하는 몇가지 장점이 있습니다.&lt;/p&gt;
&lt;h2&gt;1) 핸들러 함수를 찾는 것이 쉽습니다.&lt;/h2&gt;
&lt;p&gt;HTML 템플릿을 간단히 하여 JavaScript 코드 내에서 핸들러 함수를 찾는 것이 더 쉽습니다.&lt;/p&gt;
&lt;h2&gt;2) 순수 로직만 가질 수 있습니다.&lt;/h2&gt;
&lt;p&gt;ViewModel 코드는 순수한 로직만 가지게 됩니다. 또한 JavaScript에서 이벤트 리스너를 수동으로 연결 할 필요가 없으므로  DOM이 필요 없게 됩니다.&lt;/p&gt;
&lt;h2&gt;3) 모든 이벤트 리스너가 자동으로 제거됩니다.&lt;/h2&gt;
&lt;p&gt;ViewModel이 파기되면 모든 이벤트 리스너가 자동으로 제거됩니다. 이벤트 제거에 대한 걱정이 필요 없게 됩니다.&lt;/p&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://kr.vuejs.org/v2/guide/events.html&quot;&gt;https://kr.vuejs.org/v2/guide/events.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>HTML 이벤트 리스너 사용 이유</category>
      <category>V-ON</category>
      <category>Vue.js</category>
      <category>이벤트 리스너</category>
      <category>이벤트 수식어</category>
      <category>이벤트 핸들러</category>
      <category>이벤트 핸들링</category>
      <category>키 수식어</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/53</guid>
      <comments>https://beomy.tistory.com/53#entry53comment</comments>
      <pubDate>Thu, 6 Dec 2018 01:15:37 +0900</pubDate>
    </item>
    <item>
      <title>[Vue.JS] 리스트 렌더링</title>
      <link>https://beomy.tistory.com/52</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;820&quot; height=&quot;289&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/99CDA34A5BF41A0631?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/99CDA34A5BF41A0631?original&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99CDA34A5BF41A0631&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99CDA34A5BF41A0631&quot; width=&quot;820&quot; height=&quot;289&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;code&gt;v-for&lt;/code&gt;을 사용하여 리스트 렌더링 하는 방법에 대해 이야기 할 것입니다.&lt;/p&gt;
&lt;h1&gt;1. &lt;code&gt;v-for&lt;/code&gt;와 배열&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;v-for&lt;/code&gt; 디렉티브를 사용하여 배열을 리스트 렌더링 할 수 있습니다. &lt;code&gt;v-for&lt;/code&gt; 디렉티브에 &lt;code&gt;item in items&lt;/code&gt; 형태의 문법을 넘겨줘야 합니다. &lt;code&gt;items&lt;/code&gt;는 원본 데이터 배열이고 &lt;code&gt;item&lt;/code&gt;은 반복되는 배열의 요소입니다.&lt;/p&gt;
&lt;h2&gt;기본 사용방법&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;ul&amp;gt;
    &amp;lt;li v-for=&amp;quot;item in items&amp;quot;&amp;gt;
      {{ item.message }}
    &amp;lt;/li&amp;gt;
  &amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#app&amp;#39;,
  data: {
    items: [
      { message: &amp;#39;Foo&amp;#39; },
      { message: &amp;#39;Bar&amp;#39; }
    ]
  }
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제는 배열을 사용한 기본적인 리스트 렌더링 예제입니다. 또한 배열의 몇번째 인덱스인지 알 수 있는 인덱스 정보도 제공합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;ul&amp;gt;
    &amp;lt;li v-for=&amp;quot;(item, index) in items&amp;quot;&amp;gt;
      {{ index }} - {{ item.message }}
    &amp;lt;/li&amp;gt;
  &amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#app&amp;#39;,
  data: {
    items: [
      { message: &amp;#39;Foo&amp;#39; },
      { message: &amp;#39;Bar&amp;#39; }
    ]
  }
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제와 같이 배열의 몇번째 요소를 렌더링 하고 있는지 확인 하기 위해서는 &lt;code&gt;(item, index) in items&lt;/code&gt; 문법을 사용하면 됩니다. 또한 자바스크립트의 &lt;code&gt;for .... of ...&lt;/code&gt; 문법과 동일하게 &lt;code&gt;in&lt;/code&gt; 대신 &lt;code&gt;of&lt;/code&gt;를 사용할 수도 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;ul&amp;gt;
    &amp;lt;li v-for=&amp;quot;item of items&amp;quot;&amp;gt;
      {{ item.message }}
    &amp;lt;/li&amp;gt;
  &amp;lt;/ul&amp;gt;  
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#app&amp;#39;,
  data: {
    items: [
      { message: &amp;#39;Foo&amp;#39; },
      { message: &amp;#39;Bar&amp;#39; }
    ]
  }
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;자바스트립트와 동일하게 이터레이터(반복가능한..)에만 &lt;code&gt;of&lt;/code&gt;를 사용 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;v-for&lt;/code&gt;에 배열을 사용한 예제는 &lt;a href=&quot;https://codepen.io/beomy/pen/KrZwxV?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 결과를 확인 할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;2. &lt;code&gt;v-for&lt;/code&gt;와 객체&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;v-for&lt;/code&gt;를 사용하여 객체의 속성을 반복 할 수도 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;ul&amp;gt;
    &amp;lt;li v-for=&amp;quot;value in object&amp;quot;&amp;gt;
      {{ value }}
    &amp;lt;/li&amp;gt;
  &amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#app&amp;#39;,
  data: {
    object: {
      firstName: &amp;#39;John&amp;#39;,
      lastName: &amp;#39;Doe&amp;#39;,
      age: 30
    }
  }
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;배열을 사용하여 리스트 렌더링할 때와 유사하게, &lt;code&gt;key&lt;/code&gt;에 대한 두번째 전달인자를 제공합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;ul&amp;gt;
    &amp;lt;li v-for=&amp;quot;(value, key) in object&amp;quot;&amp;gt;
      {{ key }} : {{ value }}
    &amp;lt;/li&amp;gt;
  &amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#app&amp;#39;,
  data: {
    object: {
      firstName: &amp;#39;John&amp;#39;,
      lastName: &amp;#39;Doe&amp;#39;,
      age: 30
    }
  }
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;또한 인텍스도 제공합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;ul&amp;gt;
    &amp;lt;li v-for=&amp;quot;(value, key, index) in object&amp;quot;&amp;gt;
      {{ index }}. {{ key }} : {{ value }}
    &amp;lt;/li&amp;gt;
  &amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#app&amp;#39;,
  data: {
    object: {
      firstName: &amp;#39;John&amp;#39;,
      lastName: &amp;#39;Doe&amp;#39;,
      age: 30
    }
  }
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;객체를 반복하는 순서는 &lt;code&gt;Object.keys()&lt;/code&gt;의 키 나열 순서에 따라 결정됩니다. 이 순서는 JavaScript 엔진에 따라 다를 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;v-for&lt;/code&gt;에 객체를 사용한 예제는 &lt;a href=&quot;https://codepen.io/beomy/pen/xQpGEa?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 결과를 확인 할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;3. &lt;code&gt;key&lt;/code&gt;&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;key&lt;/code&gt;는 Vue의 각각의 노드에 고유한 ID를 지정해 줄 때 사용됩니다. &lt;code&gt;v-for&lt;/code&gt; 뿐만 아니라 이전 포스트에서 이야기한 &lt;code&gt;v-if&lt;/code&gt;를 사용할 때도 &lt;code&gt;key&lt;/code&gt;가 사용됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- 변경 전 --&amp;gt;
&amp;lt;ul&amp;gt;
  &amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;4&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;&amp;lt;!-- 변경 후 --&amp;gt;
&amp;lt;ul&amp;gt;
  &amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;
  &amp;lt;li&amp;gt;4&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제는 &lt;code&gt;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&lt;/code&gt; 가 제거된 간단한 예제입니다. Vue는 위의 예제의 동작을&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;&lt;/code&gt;에서 &lt;code&gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&lt;/code&gt;로 수정&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;&lt;/code&gt;에서 &lt;code&gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&lt;/code&gt;로 수정&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;&lt;/code&gt;에서 &lt;code&gt;&amp;lt;li&amp;gt;4&amp;lt;/li&amp;gt;&lt;/code&gt;로 수정&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;li&amp;gt;5&amp;lt;/li&amp;gt;&lt;/code&gt; 제거&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;로 총 4번 업데이트 합니다. 이렇게 되면 가상 DOM을 쓰는 것이 무색할 정도로 효율이 좋아 보이지 않습니다. 이 때 &lt;code&gt;key&lt;/code&gt;를 사용하여 최적화 할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- 변경 전 --&amp;gt;
&amp;lt;ul&amp;gt;
  &amp;lt;li key=&amp;quot;1&amp;quot;&amp;gt;1&amp;lt;/li&amp;gt;
  &amp;lt;li key=&amp;quot;2&amp;quot;&amp;gt;2&amp;lt;/li&amp;gt;
  &amp;lt;li key=&amp;quot;3&amp;quot;&amp;gt;3&amp;lt;/li&amp;gt;
  &amp;lt;li key=&amp;quot;4&amp;quot;&amp;gt;4&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;&amp;lt;!-- 변경 후 --&amp;gt;
&amp;lt;ul&amp;gt;
  &amp;lt;li key=&amp;quot;2&amp;quot;&amp;gt;2&amp;lt;/li&amp;gt;
  &amp;lt;li key=&amp;quot;3&amp;quot;&amp;gt;3&amp;lt;/li&amp;gt;
  &amp;lt;li key=&amp;quot;4&amp;quot;&amp;gt;4&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제는 &lt;code&gt;key&lt;/code&gt;를 사용하여 각 노드에 고유한 ID를 지정해 준 예제입니다. &lt;code&gt;key&lt;/code&gt;를 지정해 주게 되면 가상 DOM은 &lt;code&gt;key&lt;/code&gt;를 추적하여 &lt;code&gt;key&lt;/code&gt;가 서로 매칭되는 노드를 업데이트 하게 됩니다. 위의 예제의 동작은&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;li key=&amp;quot;1&amp;quot;&amp;gt;1&amp;lt;/li&amp;gt;&lt;/code&gt; 제거&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;로 1번만 업데이트 하면 됩니다. 그렇기 때문에 &lt;code&gt;v-for&lt;/code&gt;을 사용할 때 항상 &lt;code&gt;key&lt;/code&gt;를 지정해 주는 것이 좋습니다. key 예제는 &lt;a href=&quot;https://codepen.io/beomy/pen/WYMGJB?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 확인 할 수 있습니다.&lt;/p&gt;
&lt;h4&gt;* 참고 - &lt;code&gt;key&lt;/code&gt;는 실제로 렌더링 되지 않습니다.&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;key&lt;/code&gt; 속성은 Vue의 각각의 노드를 구별하기 위한 ID일 뿐 실제로 DOM에 렌더링 되지 않습니다. 또한 &lt;code&gt;key&lt;/code&gt; 속성은 전역으로 고유하지 않아도 됩니다. 형제 노드끼리만 고유한 값이면 됩니다.&lt;/p&gt;
&lt;h1&gt;4. 배열 변경 감지&lt;/h1&gt;
&lt;p&gt;Vue가 배열이 변경 되었는지 감지하여 DOM을 업데이트 하게 하기 위해서는 배열의 메소드들을 사용해야 합니다.&lt;/p&gt;
&lt;h2&gt;변이 메소드&lt;/h2&gt;
&lt;p&gt;Vue가 배열의 변경을 감지 할 수 있는 배열의 메소드들은 아래와 같습니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;push()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pop()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;shift()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;unshift()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;splice()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sort()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;reverse()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;위의 배열의 메소드를 사용할 때 Vue는 배열의 변화를 감지하여 DOM을 업데이트 할 수 있습니다.&lt;/p&gt;
&lt;h2&gt;배열 대체&lt;/h2&gt;
&lt;p&gt;변이 메소드에서 이야기 한 메소드들은 원본 배열 자체를 변형합니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;filter()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;concat()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;slice()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;반면 위의 메소드들은 원본 배열을 변형하지 않고 항상 새로운 배열을 반환합니다. 배열이 변형되지 않는 위의 메소드들을 사용할 때는 이전의 배열을 새로운 배열로 바꿔줘야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;ul&amp;gt;
    &amp;lt;li v-for=&amp;quot;value in array&amp;quot;&amp;gt;{{ value }}&amp;lt;/li&amp;gt;
  &amp;lt;/ul&amp;gt;
  &amp;lt;button @click=&amp;quot;filter&amp;quot;&amp;gt;Filter&amp;lt;/button&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#app&amp;#39;,
  data: {
    array: [1, 2, 3, 4]
  },
  methods: {
    filter() {
      this.array = this.array.filter(x =&amp;gt; x &amp;gt; 1);
    }
  }
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드처럼 작성할 경우 전체 목록을 다시 렌더링 한다고 생각할 수 있지만 Vue는 가상 DOM을 사용하여 변화된 부분만 DOM에 반영하기 때문에 전체 목록을 다시 렌더링 하지 않고, 효율적으로 DOM을 업데이트 할 수 있습니다.&lt;/p&gt;
&lt;h2&gt;주의 사항&lt;/h2&gt;
&lt;p&gt;배열의 변화를 감지하지 못하는 몇몇의 방법이 있습니다. 이 방법을 사용할 때 주의가 필요합니다.&lt;/p&gt;
&lt;h3&gt;1) 인덱스로 배열에 있는 항목을 직접 설정하는 경우&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;this.items[indexOfItem] = newValue&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;인덱스로 배열에 있는 항목을 직점 설정한 경우 배열 변경을 감지할 수 없습니다.&lt;/p&gt;
&lt;p&gt;위의 코드는 Vue가 배열의 변경을 감지 할 수 없습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;this.items.splice(indexOfItem, 1, newValue)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;splice&lt;/code&gt;를 사용하여 배열 변경을 감지 할 수 있게 합니다.&lt;/p&gt;
&lt;p&gt;배열의 변경을 감지하기 위해서는 위와 같이 &lt;code&gt;splice&lt;/code&gt;로 구현해야 합니다.&lt;/p&gt;
&lt;h3&gt;2) 배열 길이를 수정하는 경우&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;this.items.length = newLength&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;배열 길이를 수정하는 경우 배열 변경을 감지 할 수 없습니다.&lt;/p&gt;
&lt;p&gt;위의 코드와 같이 직접 배열 길이를 수정하는 경우 Vue는 배열의 변경을 감지 할 수 없습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;this.items.splice(newLength)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;splice&lt;/code&gt;를 사용하여 배열 변경을 감지 할 수 있게 합니다.&lt;/p&gt;
&lt;p&gt;배열의 변경을 감지하기 위해서는 위와 같이 &lt;code&gt;splice&lt;/code&gt;로 구현해야 합니다.&lt;/p&gt;
&lt;h1&gt;5. 객체 변경 감지에 관한 주의사항&lt;/h1&gt;
&lt;p&gt;Vue는 속성 추가 및 삭제를 감지하지 못합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var vm = new Vue({
  data: {
    a: 1
  }
})
// `vm.a` 는 반응형입니다.

vm.b = 2
// `vm.b` 는 반응형이 아닙니다.&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;동적으로 추가된 속성은 반응형이 아닙니다.&lt;/p&gt;
&lt;p&gt;Vue는 새로운 반응형 속성을 독적으로 추가하는 것이 불가능합니다. 동적으로 반응형 속성을 추가하는 몇가지 방법이 있습니다.&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;Vue.set(object, key, vale)&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Vue.set(object, key, value)&lt;/code&gt;를 사용하여 동적으로 반응형 속성을 추가할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var vm = new Vue({
  data: {
    userProfile: {
      name: &amp;#39;Anika&amp;#39;
    }
  }
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위와 같이 &lt;code&gt;userProfile&lt;/code&gt; 객체에 새로운 반응형 속성 &lt;code&gt;age&lt;/code&gt;를 추가하기 위해서는&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Vue.set(vm.userProfile, &amp;#39;age&amp;#39;, 27)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위와 같이 &lt;code&gt;Vue.set&lt;/code&gt;을 사용하여 새로운 반응형 속서을 추가 할 수 있습니다. Vue 인스턴스 메소드인 &lt;code&gt;$set&lt;/code&gt;을 사용할 수도 있습니다. (&lt;code&gt;Vue.set&lt;/code&gt;은 전역 메소드 입니다.)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;vm.$set(this.userProfile, &amp;#39;age&amp;#39;, 27)&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;&lt;code&gt;Object.assign()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Object.assign()&lt;/code&gt;이나 &lt;code&gt;_.extend()&lt;/code&gt;를 사용해 기존 객체에 새 속성을 추가 할 수도 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;this.userProfile = Object.assign({}, this.userProfile, {
  age: 27,
  favoriteColor: &amp;#39;Vue Green&amp;#39;
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;Object.assign()&lt;/code&gt;을 사용하면 새로운 객체를 만들어 다시 할당해 주어야 합니다.&lt;/p&gt;
&lt;h1&gt;6. 필터링 / 정렬 된 결과 표시하기&lt;/h1&gt;
&lt;p&gt;원본 데이터를 실제로 변경하지 않고 필터링 되거나 정렬 된 배열을 표시해야 할 필요가 있습니다. 이 경우 필터링 된 배열이나 정렬된 배열을 반환하는 &lt;code&gt;computed&lt;/code&gt; 속성을 만들 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;ul&amp;gt;
    &amp;lt;li v-for=&amp;quot;n in evenNumbers&amp;quot;&amp;gt;{{ n }}&amp;lt;/li&amp;gt;
  &amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#app&amp;#39;,
  data: {
    numbers: [ 1, 2, 3, 4, 5 ]
  },
  computed: {
    evenNumbers() {
      return this.numbers.filter(function (number) {
        return number % 2 === 0
      })
    }
  }
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;computed&lt;/code&gt; 속성을 사용할 수 없는 경우 &lt;code&gt;mehods&lt;/code&gt; 속성을 사용할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;ul&amp;gt;
    &amp;lt;li v-for=&amp;quot;n in evenNumbers&amp;quot;&amp;gt;
      &amp;lt;div v-for=&amp;quot;number in multiplex(n)&amp;quot;&amp;gt;{{ number }}&amp;lt;/div&amp;gt;
    &amp;lt;/li&amp;gt;
  &amp;lt;/ul&amp;gt;  
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#app&amp;#39;,
  data: {
    numbers: [ 1, 2, 3, 4, 5 ]
  },
  computed: {
    evenNumbers() {
      return this.numbers.filter(function (number) {
        return number % 2 === 0
      })
    }
  },
  methods: {
    multiplex(number) {
      return number * 2
    }
  }
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제는 &lt;a href=&quot;https://codepen.io/beomy/pen/ZmojYK?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 결과를 확인 할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;7. Range &lt;code&gt;v-for&lt;/code&gt;&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;v-for&lt;/code&gt;는 숫자를 사용할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;span v-for=&amp;quot;n in 10&amp;quot;&amp;gt;{{ n }} &amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;v-for&lt;/code&gt;는 숫자를 사용할 수 있습니다.&lt;/p&gt;
&lt;p&gt;위의 예제는 &lt;a href=&quot;https://codepen.io/beomy/pen/vQjaWq?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 결과를 확인 할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;8. &lt;code&gt;v-for&lt;/code&gt; 템플릿&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; 태그와 함께 &lt;code&gt;v-for&lt;/code&gt;을 사용할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;ul&amp;gt;
    &amp;lt;template v-for=&amp;quot;number in numbers&amp;quot;&amp;gt;
      &amp;lt;li&amp;gt;{{ number }}&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;{{ number * number }}&amp;lt;/li&amp;gt;
    &amp;lt;/template&amp;gt;
  &amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#app&amp;#39;,
  data: {
    numbers: [1, 2, 3, 4]
  }
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt;와 &lt;code&gt;v-for&lt;/code&gt;는 함께 사용 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;위 코드의 예제는 &lt;a href=&quot;https://codepen.io/beomy/pen/mQLjKy?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 확인 할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;9. &lt;code&gt;v-for&lt;/code&gt;와 &lt;code&gt;v-if&lt;/code&gt;&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;v-for&lt;/code&gt;와 &lt;code&gt;v-if&lt;/code&gt;도 역시 함께 사용할 수 있습니다. 동일한 노드에 &lt;code&gt;v-for&lt;/code&gt;와 &lt;code&gt;v-if&lt;/code&gt;가 모두 존재한다면, &lt;code&gt;v-for&lt;/code&gt;가 더 높은 우선순위를 가집니다. 즉, 루프가 반복될 때 마다 &lt;code&gt;v-if&lt;/code&gt;가 실행됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;ul&amp;gt;
      &amp;lt;li v-for=&amp;quot;number in numbers&amp;quot; v-if=&amp;quot;number % 2 === 0&amp;quot;&amp;gt;{{ number }}&amp;lt;/li&amp;gt;
  &amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#app&amp;#39;,
  data: {
    numbers: [1, 2, 3, 4]
  }
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;v-for&lt;/code&gt;가 &lt;code&gt;v-if&lt;/code&gt; 보다 우선순위가 높습니다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;v-for&lt;/code&gt;가 동작을 할지 동작을 하지 말아야 하는지를 결정해야 한다면, 상위 엘리먼트(또는 &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt;)에서 &lt;code&gt;v-if&lt;/code&gt;를 사용해야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;ul v-if=&amp;quot;numbers.length&amp;quot;&amp;gt;
    &amp;lt;li v-for=&amp;quot;number in numbers&amp;quot;&amp;gt;{{ number }}&amp;lt;/li&amp;gt;
  &amp;lt;/ul&amp;gt;
  &amp;lt;ul&amp;gt;
    &amp;lt;template v-if=&amp;quot;numbers.length&amp;quot;&amp;gt;
      &amp;lt;li v-for=&amp;quot;number in numbers&amp;quot;&amp;gt;{{ number }}&amp;lt;/li&amp;gt;
    &amp;lt;/template&amp;gt;
  &amp;lt;/ul&amp;gt;  
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#app&amp;#39;,
  data: {
    numbers: [1, 2, 3, 4]
  }
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드는 &lt;a href=&quot;https://codepen.io/beomy/pen/QJrBzg?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 결과를 확인할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;10. &lt;code&gt;v-for&lt;/code&gt;와 컴포넌트&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;v-for&lt;/code&gt;은 사용자 정의 컴포넌트에서도 사용할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;my-component v-for=&amp;quot;item in items&amp;quot; :key=&amp;quot;item.id&amp;quot;&amp;gt;&amp;lt;/my-component&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;반복할 데이터를 컴포넌트로 전달하려면 &lt;code&gt;props&lt;/code&gt;를 사용해야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;my-component
  v-for=&amp;quot;(item, index) in items&amp;quot;
  v-bind:item=&amp;quot;item&amp;quot;
  v-bind:index=&amp;quot;index&amp;quot;
  v-bind:key=&amp;quot;item.id&amp;quot;
&amp;gt;&amp;lt;/my-component&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;사용자 정의 컴포넌트에서 데이터를 사용하기 위해 &lt;code&gt;props&lt;/code&gt;로 데이터를 넘겨준 예제를 살펴도록 하겠습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;todo-list-example&amp;quot;&amp;gt;
  &amp;lt;input
    v-model=&amp;quot;newTodoText&amp;quot;
    @keyup.enter=&amp;quot;addNewTodo&amp;quot;
    placeholder=&amp;quot;Add a todo&amp;quot;
  &amp;gt;
  &amp;lt;ul&amp;gt;
    &amp;lt;li
      is=&amp;quot;todo-item&amp;quot;
      v-for=&amp;quot;(todo, index) in todos&amp;quot;
      :key=&amp;quot;todo.id&amp;quot;
      :title=&amp;quot;todo.title&amp;quot;
      @remove=&amp;quot;todos.splice(index, 1)&amp;quot;
    &amp;gt;&amp;lt;/li&amp;gt;
  &amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;const TodoItem = {
  template: `
    &amp;lt;li&amp;gt;
      {{ title }}
      &amp;lt;button @click=&amp;quot;$emit(&amp;#39;remove&amp;#39;)&amp;quot;&amp;gt;X&amp;lt;/button&amp;gt;
    &amp;lt;/li&amp;gt;`,
  props: [&amp;#39;title&amp;#39;]
};

new Vue({
  el: &amp;#39;#todo-list-example&amp;#39;,
  data: {
    newTodoText: &amp;#39;&amp;#39;,
    todos: [
      {
        id: 1,
        title: &amp;#39;Do the dishes&amp;#39;,
      },
      {
        id: 2,
        title: &amp;#39;Take out the trash&amp;#39;,
      },
      {
        id: 3,
        title: &amp;#39;Mow the lawn&amp;#39;
      }
    ],
    nextTodoId: 4
  },
  methods: {
    addNewTodo: function () {
      this.todos.push({
        id: this.nextTodoId++,
        title: this.newTodoText
      })
      this.newTodoText = &amp;#39;&amp;#39;
    }
  },
  components: {
    TodoItem
  }
}) &lt;/code&gt;&lt;/pre&gt;&lt;p&gt;사용자 정의 컴포넌트에 &lt;code&gt;v-for&lt;/code&gt; 사용하기&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;is=&amp;quot;todo-item&amp;quot; : &amp;lt;ul&amp;gt;&lt;/code&gt; 엘리먼트 안에는 &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt; 엘리먼트만 사용 가능합니다. 위의 예제에서 &lt;code&gt;is=&amp;quot;todo-item&amp;quot;&lt;/code&gt;을 사용하지 않고, &lt;code&gt;&amp;lt;todo-item&amp;gt;&lt;/code&gt;을 사용할 수 있지만 브라우저의 구문 오류를 해결하기 위해 &lt;code&gt;is=&amp;quot;todo-item&amp;quot;&lt;/code&gt;을 사용하여 브라우저 구문 오류를 해결 하였습니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;:title&lt;/code&gt;이라는 &lt;code&gt;props&lt;/code&gt;를 사용하여 사용자 정의 컴포넌트에 데이터를 넘겨줍니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@romve&lt;/code&gt;라는 &lt;code&gt;props&lt;/code&gt;를 사용하여 사용자 정의 컴포넌트에 이벤트 리스너를 넘겨줍니다. 사용자 정의 컴포넌트에서 &lt;code&gt;$emit(&amp;#39;remove&amp;#39;)&lt;/code&gt; 호출시 &lt;code&gt;@remove&lt;/code&gt;에 넘겨진 함수가 실행됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;위의 예제는 &lt;a href=&quot;https://codepen.io/beomy/pen/GwGJqx?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 결과를 확인 할 수 있습니다.&lt;/p&gt;</description>
      <category>Vue.JS</category>
      <category>Key</category>
      <category>template</category>
      <category>v-for</category>
      <category>v-if</category>
      <category>Vue.js</category>
      <category>Vue.set</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/52</guid>
      <comments>https://beomy.tistory.com/52#entry52comment</comments>
      <pubDate>Sun, 25 Nov 2018 21:42:04 +0900</pubDate>
    </item>
    <item>
      <title>[Vue.JS] 조건부 렌더링</title>
      <link>https://beomy.tistory.com/51</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;820&quot; height=&quot;289&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/99FC2D485BEC1F7816?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/99FC2D485BEC1F7816?original&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99FC2D485BEC1F7816&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99FC2D485BEC1F7816&quot; width=&quot;820&quot; height=&quot;289&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;code&gt;v-if&lt;/code&gt;와 &lt;code&gt;v-show&lt;/code&gt;를 이용하여 조건부 렌더링을 이야기 합니다.&lt;/p&gt;
&lt;h1&gt;1. &lt;code&gt;v-if&lt;/code&gt;&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;v-if&lt;/code&gt; 디렉티브를 사용하여 조건부 렌더링을 사용할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;h1 v-if=&amp;quot;ok&amp;quot;&amp;gt;Yes&amp;lt;/h1&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#app&amp;#39;,
  data: {
    ok: true,
  },
});&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;&lt;code&gt;v-else&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;v-if&lt;/code&gt; 디렉티브는 &lt;code&gt;v-else&lt;/code&gt; 디렉티브와 함께 사용 할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;h1 v-if=&amp;quot;ok&amp;quot;&amp;gt;Yes&amp;lt;/h1&amp;gt;
  &amp;lt;h1 v-else&amp;gt;No&amp;lt;/h1&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#app&amp;#39;,
  data: {
    ok: true,
  },
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제는 &lt;a href=&quot;https://codepen.io/beomy/pen/aQJGvx?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 결과를 확인 할 수 있습니다.&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;v-else-if&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;v-else-if&lt;/code&gt; 디렉티브는 2.1.0에 추가되었습니다. 이름에서 알 수 있듯이 else if 블록 역할을 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;div v-if=&amp;quot;type === &amp;#39;A&amp;#39;&amp;quot;&amp;gt;
    A
  &amp;lt;/div&amp;gt;
  &amp;lt;div v-else-if=&amp;quot;type === &amp;#39;B&amp;#39;&amp;quot;&amp;gt;
    B
  &amp;lt;/div&amp;gt;
  &amp;lt;div v-else-if=&amp;quot;type === &amp;#39;C&amp;#39;&amp;quot;&amp;gt;
    C
  &amp;lt;/div&amp;gt;
  &amp;lt;div v-else&amp;gt;
    Not A/B/C
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#app&amp;#39;,
  data: {
    type: &amp;#39;C&amp;#39;,
  },
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;v-else-if&lt;/code&gt;는 &lt;code&gt;v-if&lt;/code&gt; 또는 &lt;code&gt;v-else-if&lt;/code&gt; 뒤에 사용할 수 있습니다. 위의 예제는 &lt;a href=&quot;https://codepen.io/beomy/pen/xQXemJ?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 결과를 확인 할 수 있습니다.&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt;와 &lt;code&gt;v-if&lt;/code&gt;로 조건부 그룹 만들기&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;v-if&lt;/code&gt;는 디렉티브이기 때문에 하나의 엘리먼트에 추가되어야 합니다. 그렇다면 여러개의 엘리먼트에 조건부 렌더링을 추가하기 위해서는 각각의 엘리먼트에 &lt;code&gt;v-if&lt;/code&gt;를 사용해야 할 것입니다. 하지만 HTML에 실제로 렌더링 되지 않는 태그인 &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt;를 사용하면 각각의 엘리먼트에 &lt;code&gt;v-if&lt;/code&gt;를 사용하지 않고 조건부 그룹을 만들어 조건부 렌더링을 사용 할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;template v-if=&amp;quot;ok&amp;quot;&amp;gt;
    &amp;lt;h1&amp;gt;Title&amp;lt;/h1&amp;gt;
    &amp;lt;p&amp;gt;Paragraph 1&amp;lt;/p&amp;gt;
    &amp;lt;p&amp;gt;Paragraph 2&amp;lt;/p&amp;gt;
  &amp;lt;/template&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#app&amp;#39;,
  data: {
    ok: true,
  },
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt;에는 &lt;code&gt;v-if&lt;/code&gt;를 사용할 수 있습니다. 또한 최종 렌더링 결과에는 &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; 엘리먼트가 포함되지 않습니다. 위의 예제는 &lt;a href=&quot;https://codepen.io/beomy/pen/vQxrKp?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 결과를 확인 할 수 있습니다.&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;key&lt;/code&gt;를 이용하여 재사용 가능한 엘리먼트 제어&lt;/h2&gt;
&lt;p&gt;Vue는 효율적인 렌더링을 위해 때때로는 처음부터 렌더링하지 않고 기존의 엘리먼트를 다시 사용합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;template v-if=&amp;quot;loginType === &amp;#39;username&amp;#39;&amp;quot;&amp;gt;
      &amp;lt;label&amp;gt;사용자 이름&amp;lt;/label&amp;gt;
      &amp;lt;input placeholder=&amp;quot;사용자 이름을 입력하세요&amp;quot;&amp;gt;
    &amp;lt;/template&amp;gt;
    &amp;lt;template v-else&amp;gt;
      &amp;lt;label&amp;gt;이메일&amp;lt;/label&amp;gt;
      &amp;lt;input placeholder=&amp;quot;이메일 주소를 입력하세요&amp;quot;&amp;gt;
    &amp;lt;/template&amp;gt;
    &amp;lt;button @click=&amp;quot;changeLoginType&amp;quot;&amp;gt;로그인 유형 변경&amp;lt;/button&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#app&amp;#39;,
  data: {
    loginType: &amp;#39;username&amp;#39;,
  },
  methods: {
    changeLoginType() {
      if (this.loginType === &amp;#39;username&amp;#39;) {
        this.loginType = &amp;#39;email&amp;#39;;
      } else {
        this.loginType = &amp;#39;username&amp;#39;
      }
    }
  }
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위 코드에서 &lt;code&gt;loginType&lt;/code&gt;이 바뀌어도 사용자가 입력한 내용은 지워지지 않습니다. Vue는 엘리먼트를 재사용하여 &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; 자체가 대체되지 않고 &lt;code&gt;placeholder&lt;/code&gt;만 변경됩니다. 위의 코드는 &lt;a href=&quot;https://codepen.io/beomy/pen/GwMaop?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 확인 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;엘리먼트를 재사용하지 않길 원한다면, &lt;code&gt;key&lt;/code&gt; 속성을 사용하면 됩니다. 즉 &lt;code&gt;key&lt;/code&gt; 속성을 사용하여 완전 별개의 엘리먼트인 것을 명시하면 Vue는 엘리먼트를 재사용하지 않고 완전히 대체하여 렌더링 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;div&amp;gt;
    &amp;lt;template v-if=&amp;quot;loginType === &amp;#39;username&amp;#39;&amp;quot;&amp;gt;
      &amp;lt;label&amp;gt;사용자 이름&amp;lt;/label&amp;gt;
      &amp;lt;input placeholder=&amp;quot;사용자 이름을 입력하세요&amp;quot; key=&amp;quot;username-input&amp;quot;&amp;gt;
    &amp;lt;/template&amp;gt;
    &amp;lt;template v-else&amp;gt;
      &amp;lt;label&amp;gt;이메일&amp;lt;/label&amp;gt;
      &amp;lt;input placeholder=&amp;quot;이메일 주소를 입력하세요&amp;quot; key=&amp;quot;email-input&amp;quot;&amp;gt;
    &amp;lt;/template&amp;gt;
    &amp;lt;button @click=&amp;quot;changeLoginType&amp;quot;&amp;gt;로그인 유형 변경&amp;lt;/button&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#app&amp;#39;,
  data: {
    loginType: &amp;#39;username&amp;#39;,
  },
  methods: {
    changeLoginType() {
      if (this.loginType === &amp;#39;username&amp;#39;) {
        this.loginType = &amp;#39;email&amp;#39;;
      } else {
        this.loginType = &amp;#39;username&amp;#39;
      }
    }
  }
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; 엘리먼트는 &lt;code&gt;key&lt;/code&gt; 속성이 없기 때문에 엘리먼트를 재사용합니다. &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; 엘리먼트는 속성을 사용하여 완전히 대체하여 렌더링합니다. 위의 예제는 &lt;a href=&quot;https://codepen.io/beomy/pen/BGwgBJ?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 확인 할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;2. &lt;code&gt;v-show&lt;/code&gt;&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;v-show&lt;/code&gt; 디렉티브를 사용하여 조건부 렌더링을 사용할 수도 있습니다. 사용법은 &lt;code&gt;v-if&lt;/code&gt;와 유사합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;h1 v-show=&amp;quot;ok&amp;quot;&amp;gt;Yes&amp;lt;/h1&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#app&amp;#39;,
  data: {
    ok: true,
  },
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;v-show&lt;/code&gt;는 DOM에 항상 그려집니다. &lt;code&gt;v-show&lt;/code&gt;에 &lt;code&gt;false&lt;/code&gt;가 넘겨지면 &lt;code&gt;display:none&lt;/code&gt;이 되어 DOM에는 있지만 화면에 나타나지 않는 상태가 됩니다. &lt;code&gt;v-show&lt;/code&gt;는 &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt;를 지원하지 않습니다.&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;v-if&lt;/code&gt; VS &lt;code&gt;v-show&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;v-if&lt;/code&gt; 디렉티브를 사용한 엘리먼트의 이벤트 리스너와 자식 컴포넌트들이 DOM에 제거되거나 삽입되는 진짜 조건부 렌더링입니다. 반면 &lt;code&gt;v-show&lt;/code&gt;는 DOM에 항상 삽입되고 &lt;code&gt;display&lt;/code&gt; style을 통해 화면에 그려지거나 그려지지 않는 조건부 렌더링입니다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;v-if&lt;/code&gt;는 토글 비용(화면에 그려지거나 그려지지 않는데 사용되는 비용)이 높고, 초기 렌더링 비용이 낮습니다. 반면 &lt;code&gt;v-show&lt;/code&gt;는 토글 비용이 낮고 초기 렌더링 비용이 높습니다. 그렇기 때문에 자주 변하는 컴포넌트에 조건부 렌더링을 사용해야 한다면 &lt;code&gt;v-show&lt;/code&gt;를 사용하는 것이 좋습니다. 런타임 시 조건이 바뀌지 않다면 &lt;code&gt;v-if&lt;/code&gt;를 사용하는 것이 좋습니다.&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;v-if&lt;/code&gt;와 &lt;code&gt;v-for&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;v-for&lt;/code&gt; 디렉티브에 관련 내용은 이 후의 포스트에서 더 자세히 이야기 하도록 하겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;v-if&lt;/code&gt;와 &lt;code&gt;v-for&lt;/code&gt;을 함께 사용한다면, &lt;code&gt;v-for&lt;/code&gt;가 더 높은 우선순위를 가집니다. 즉 &lt;code&gt;v-for&lt;/code&gt;로 리스트 렌더링을 하면서 각각의 리스트는 &lt;code&gt;v-if&lt;/code&gt; 조건에 걸리게 됩니다.&lt;/p&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://kr.vuejs.org/v2/guide/conditional.html&quot;&gt;https://kr.vuejs.org/v2/guide/conditional.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>template</category>
      <category>v-else</category>
      <category>v-else-if</category>
      <category>v-for</category>
      <category>v-if</category>
      <category>v-show</category>
      <category>Vue.js</category>
      <category>조건부 렌더링</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/51</guid>
      <comments>https://beomy.tistory.com/51#entry51comment</comments>
      <pubDate>Tue, 20 Nov 2018 00:36:05 +0900</pubDate>
    </item>
    <item>
      <title>[Vue.JS] 클래스와 스타일 바인딩</title>
      <link>https://beomy.tistory.com/50</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;820&quot; height=&quot;289&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/99052D475BE2F1A431?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/99052D475BE2F1A431?original&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99052D475BE2F1A431&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99052D475BE2F1A431&quot; width=&quot;820&quot; height=&quot;289&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;/p&gt;
&lt;h1&gt;1. HTML 클래스 바인딩&lt;/h1&gt;
&lt;p&gt;객체로 클래스를 바인딩 하는 방법, 배열로 클래스를 사용하는 방법, 컴포넌트와 함께 사용하는 방법 3가지를 이야기 하도록 하겠습니다.&lt;/p&gt;
&lt;h2&gt;객체 구문&lt;/h2&gt;
&lt;p&gt;클래스를 동적으로 바인딩하기 위해서는 &lt;code&gt;v-bind:class&lt;/code&gt;(약어 &lt;code&gt;:class&lt;/code&gt;)를 사용하면 됩니다. &lt;code&gt;:class&lt;/code&gt;에 객체를 전달해서 클래스를 바인딩 할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div :class=&amp;quot;{ active: isActive }&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위 예제에서 &lt;code&gt;isActive&lt;/code&gt;가 &lt;code&gt;true&lt;/code&gt;일 경우 &lt;code&gt;active&lt;/code&gt; 클래스가 바인딩 됩니다. 객체를 이용하여 여러 클래스를 바인딩 할 수 있습니다. 또한 일반 &lt;code&gt;class&lt;/code&gt; 속성과 함께 사용할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div class=&amp;quot;static&amp;quot;
     :class=&amp;quot;{ active: isActive, &amp;#39;text-danger&amp;#39;: hasError }&amp;quot;&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제처럼 &lt;code&gt;class&lt;/code&gt;와 &lt;code&gt;:class&lt;/code&gt;를 함께 사용 할 수 있으며, &lt;code&gt;:class&lt;/code&gt;에 객체를 넘겨주어 여러 클래스를 바인딩 할 수 있습니다. &lt;code&gt;:class&lt;/code&gt;에 넘겨준 객체의 값 중 &lt;code&gt;isActive&lt;/code&gt;가 &lt;code&gt;true&lt;/code&gt;, &lt;code&gt;hasError&lt;/code&gt;가 &lt;code&gt;false&lt;/code&gt;  일 때, &lt;code&gt;&amp;lt;div class=&amp;quot;static active&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt;와 같이 랜더링 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;isActive&lt;/code&gt;와 &lt;code&gt;hasError&lt;/code&gt;의 값이 업데이트 되면, 바인딩 되는 클래스도 따라 업데이트 됩니다. &lt;code&gt;hasError&lt;/code&gt;가 &lt;code&gt;true&lt;/code&gt;가 된다면 &lt;code&gt;&amp;lt;div class=&amp;quot;static active text-danger&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt;가 랜더링 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;:class&lt;/code&gt;에 바인딩 되는 객체는 반드시 인라인일 필요는 없습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div :class=&amp;quot;classObject&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;data: {
  classObject: {
    active: true,
    &amp;#39;text-danger&amp;#39;: false
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제와 같이 &lt;code&gt;data&lt;/code&gt;를 사용하여 클래스를 바인딩 할 수 있습니다. &lt;code&gt;data&lt;/code&gt; 뿐만 아니라 &lt;code&gt;computed&lt;/code&gt;를 이용하여 클래스를 바인딩 할 수도 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div :class=&amp;quot;classObject&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;data: {
  isActive: true,
  error: null
},
computed: {
  classObject: function () {
    return {
      active: this.isActive &amp;amp;&amp;amp; !this.error,
      &amp;#39;text-danger&amp;#39;: this.error &amp;amp;&amp;amp; this.error.type === &amp;#39;fatal&amp;#39;
    }
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제와 같이 &lt;code&gt;computed&lt;/code&gt;를 사용하여 클래스를 바인딩 할 수도 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://codepen.io/beomy/pen/NENNda?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 객체로 클래스 바인딩 하는 예제를 확인 할 수 있습니다.&lt;/p&gt;
&lt;h2&gt;배열 구문&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;:class&lt;/code&gt;에 배열을 전달하여 클래스를 바인딩 할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div :class=&amp;quot;[activeClass, errorClass]&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;data: {
  activeClass: &amp;#39;active&amp;#39;,
  errorClass: &amp;#39;text-danger&amp;#39;
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제는 &lt;code&gt;&amp;lt;div class=&amp;quot;active text-danger&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt;로 랜더링 됩니다. 배열에 있는 클래스를 조건부 랜더링을 하고 싶다면 삼항 연산자를 이용하면 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div :class=&amp;quot;[isActive ? activeClass : &amp;#39;&amp;#39;, errorClass]&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제는 &lt;code&gt;errorClass&lt;/code&gt;를 항상 비인딩 되며, &lt;code&gt;isActive&lt;/code&gt;가 &lt;code&gt;true&lt;/code&gt;일 때에만 &lt;code&gt;activeClass&lt;/code&gt;가 바인딩 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div :class=&amp;quot;[{ active: isActive }, errorClass]&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제와 같이 배열 구문안에 객체 구문을 함께 사용할 수도 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://codepen.io/beomy/pen/NErjgp?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 배열로 클래스 바인딩 하는 예제를 확인 할 수 있습니다.&lt;/p&gt;
&lt;h2&gt;컴포넌트와 함께 사용하는 방법&lt;/h2&gt;
&lt;p&gt;사용자 정의 컴포넌트에서 &lt;code&gt;class&lt;/code&gt; 속성을 사용하면, 컴포넌트의 루트 엘리먼트에 &lt;code&gt;class&lt;/code&gt;가 추가 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;Child class=&amp;quot;baz boo&amp;quot;&amp;gt;&amp;lt;/Child&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;const Child = {
  template: &amp;#39;&amp;lt;div class=&amp;quot;foo bar&amp;quot;&amp;gt;Child&amp;lt;/div&amp;gt;&amp;#39;,
}

new Vue({
  el: &amp;#39;#app&amp;#39;,
  components: {
    Child
  }
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제는 &lt;code&gt;&amp;lt;Child /&amp;gt;&lt;/code&gt;는 &lt;code&gt;&amp;lt;div class=&amp;quot;foo bar baz boo&amp;quot;&amp;gt;Child&amp;lt;/div&amp;gt;&lt;/code&gt;로 랜더링 됩니다. &lt;code&gt;:class&lt;/code&gt; 바인딩도 동일하게 사용할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;Child :class=&amp;quot;{ active: isActive }&amp;quot;&amp;gt;&amp;lt;/Child&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;const Child = {
  template: &amp;#39;&amp;lt;div class=&amp;quot;foo bar&amp;quot;&amp;gt;Child&amp;lt;/div&amp;gt;&amp;#39;,
}

new Vue({
  el: &amp;#39;#app&amp;#39;,
  data: {
    isActive: true
  },
  components: {
    Child
  }
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제에서 &lt;code&gt;isActive&lt;/code&gt;가 &lt;code&gt;true&lt;/code&gt; 일 경우, &lt;code&gt;&amp;lt;div class=&amp;quot;foo bar active&amp;quot;&amp;gt;Child&amp;lt;/div&amp;gt;&lt;/code&gt;로 랜더링 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://codepen.io/beomy/pen/jQrgor?editors=1010&quot;&gt;CodePe&lt;/a&gt;&lt;a href=&quot;https://codepen.io/beomy/pen/jQrgor?editors=1010&quot;&gt;n&lt;/a&gt;에서 컴포넌트와 함께 클래스 바인등 하는 예제를 확인 할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;2. 인라인 스타일 바인딩&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;v-bind:style&lt;/code&gt;(약어 &lt;code&gt;:style&lt;/code&gt;)을 사용하여 인라인 스타일을 적용 할 수 있습니다. &lt;code&gt;:style&lt;/code&gt;에 객체 혹은 배열을 넘겨주어 사용할 수 있습니다.&lt;/p&gt;
&lt;h2&gt;객체 구문&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;:style&lt;/code&gt;에 객체를 넘겨 주어 인라인 스타일을 사용 할 수 있습니다. 넘겨주는 객체의 속성 이름으로 camalCase와 kebab-case를 사용 할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;div :style=&amp;quot;{ color: activeColor, &amp;#39;font-size&amp;#39;: fontSize + &amp;#39;px&amp;#39; }&amp;quot;&amp;gt;인라인 스타일 바인딩&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#app&amp;#39;,
  data: {
    activeColor: &amp;#39;red&amp;#39;,
    fontSize: 30
  }
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제 처럼 &lt;code&gt;:style&lt;/code&gt;에 객체를 직접 바인딩 하여 사용 할 수도 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;div :style=&amp;quot;styleObject&amp;quot;&amp;gt;인라인 스타일 바인딩&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#app&amp;#39;,
  data: {
    styleObject: {
      color: &amp;#39;red&amp;#39;,
      fontSize: &amp;#39;13px&amp;#39;
    }
  }
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제와 같이 &lt;code&gt;data&lt;/code&gt;를 사용하여 인라인 스타일을 적용 할 수도 있습니다. &lt;code&gt;:class&lt;/code&gt;와 동일하게 &lt;code&gt;computed&lt;/code&gt;를 사용하여 인라인 스타일을 적용 할 수도 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;div :style=&amp;quot;computedStyleObject&amp;quot;&amp;gt;인라인 스타일 바인딩&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#app&amp;#39;,
  data: {
    activeColor: &amp;#39;red&amp;#39;,
    fontSize: 30,
  },
  computed: {
    computedStyleObject() {
      return {
        color: this.activeColor,
        fontSize: this.fontSize + &amp;#39;px&amp;#39;
      };
    }
  }
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제와 같이 &lt;code&gt;computed&lt;/code&gt;를 사용하여 인라인 스타일을 바인딩 할 수도 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://codepen.io/beomy/pen/RqogjR&quot;&gt;CodePen&lt;/a&gt;에서 객체로 인라인 스타일을 바인딩하는 예제를 확인 할 수 있습니다.&lt;/p&gt;
&lt;h2&gt;배열 구문&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;:style&lt;/code&gt;에 배열을 넘겨주어 여러개의 스타일 객체를 사용하 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;app&amp;quot;&amp;gt;
  &amp;lt;div v-bind:style=&amp;quot;[baseStyles, overridingStyles]&amp;quot;&amp;gt;배열 구문&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#app&amp;#39;,
  data: {
    baseStyles: {
      color: &amp;#39;red&amp;#39;,
      fontSize: &amp;#39;13px&amp;#39;
    },
    overridingStyles: {
      backgroundColor: &amp;#39;black&amp;#39;
    }
  },
}); &lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제는 &lt;a href=&quot;https://codepen.io/beomy/pen/aQpVpR?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 결과를 확인 할 수 있습니다.&lt;/p&gt;
&lt;h2&gt;자동 접두사&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;:style&lt;/code&gt;에 브라우저 &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/Vendor_Prefix&quot;&gt;벤더 접두어&lt;/a&gt;가 필요한 CSS 속성을 사용하면, Vue는 자동으로 브라우저에 맞는 접두어를 감지하여 스타일을 적용합니다.&lt;/p&gt;
&lt;h2&gt;다중 값 제공&lt;/h2&gt;
&lt;p&gt;2.3.0 이상의 Vue 버전에서 지원하는 기능입니다. &lt;code&gt;:style&lt;/code&gt;에 접두어가 있는 여러 스타일을 배열로 전달 할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div :style=&amp;quot;{ display: [&amp;#39;-webkit-box&amp;#39;, &amp;#39;-ms-flexbox&amp;#39;, &amp;#39;flex&amp;#39;] }&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 코드와 같이 &lt;code&gt;:style&lt;/code&gt;을 사용하면, 배열에 있는 스타일 중 브라우저가 지원하는 마지막 값으로 스타일이 적용됩니다. 위의 코드는 flexbox의 접두어가 붙지 않는 버전을 지원하는 브라우저의 경우 &lt;code&gt;display: flex&lt;/code&gt; 스타일이 적용됩니다.&lt;/p&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://kr.vuejs.org/v2/guide/class-and-style.html&quot;&gt;https://kr.vuejs.org/v2/guide/class-and-style.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>:class</category>
      <category>:style</category>
      <category>Vue.js</category>
      <category>벤더 접두어</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/50</guid>
      <comments>https://beomy.tistory.com/50#entry50comment</comments>
      <pubDate>Tue, 13 Nov 2018 23:41:18 +0900</pubDate>
    </item>
    <item>
      <title>[Vue.JS] computed와 watch</title>
      <link>https://beomy.tistory.com/49</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;820&quot; height=&quot;289&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/99C829335BCF326A03?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/99C829335BCF326A03?original&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99C829335BCF326A03&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99C829335BCF326A03&quot; width=&quot;820&quot; height=&quot;289&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;이번 포스트에서는 Vue.JS에서 비슷한 특징을 가진 &lt;code&gt;computed&lt;/code&gt; 속성과 &lt;code&gt;watch&lt;/code&gt; 속성의 특징을 이야기 하려고 합니다.&lt;/p&gt;
&lt;h1&gt;1. &lt;code&gt;computed&lt;/code&gt; 속성&lt;/h1&gt;
&lt;p&gt;템플릿 문법(&lt;a href=&quot;http://beomy.tistory.com/48&quot;&gt;[Vue.JS] 템플릿 문법&lt;/a&gt; 참고)에서 JavaScript 표현식을 사용하면, 쉽게 원하는 데이터를 DOM에 그릴 수 있습니다. 하지만 복잡한 연산을 템플릿 안에서 하게 되면, 코드를 이해하고, 유지보수하기 어려워집니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;example&amp;quot;&amp;gt;
  {{ message.split(&amp;#39;&amp;#39;).reverse().join(&amp;#39;&amp;#39;) }}
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제는 템플릿 문법 안에서 JavaScript를 사용하여 &lt;code&gt;message&lt;/code&gt;를 역순으로 출력한 예제입니다. 지금은 단순해 보일 수도 있지만 좀 더 복잡한 로직을 사용한다면 &lt;code&gt;computed&lt;/code&gt; 속성을 사용하는 것이 좋습니다.&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;computed&lt;/code&gt; 사용 방법&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;example&amp;quot;&amp;gt;
  &amp;lt;p&amp;gt;원본 메시지: &amp;quot;{{ message }}&amp;quot;&amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;역순으로 표시한 메시지: &amp;quot;{{ reversedMessage }}&amp;quot;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#example&amp;#39;,
  data: {
    message: &amp;#39;안녕하세요&amp;#39;
  },
  computed: {
    // 계산된 getter
    reversedMessage: function () {
      // `this` 는 vm 인스턴스를 가리킵니다.
      return this.message.split(&amp;#39;&amp;#39;).reverse().join(&amp;#39;&amp;#39;)
    }
  }
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제에서 &lt;code&gt;computed&lt;/code&gt;로 &lt;code&gt;reversedMessage&lt;/code&gt;를 선언했습니다. &lt;code&gt;reversedMessage&lt;/code&gt;를 getter 함수로 사용할 수 있게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;computed&lt;/code&gt; 속성도 템플릿에 데이터 바인딩 할 수 있습니다. &lt;code&gt;reversedMessage&lt;/code&gt;는 &lt;code&gt;message&lt;/code&gt;에 의존적이기 때문에, &lt;code&gt;message&lt;/code&gt;의 값이 바뀌면 &lt;code&gt;reversedMessage&lt;/code&gt;의 값도 따라 바뀌게 됩니다. 즉 &lt;code&gt;message&lt;/code&gt;가 바뀔 때 &lt;code&gt;reversedMessage&lt;/code&gt;도 변경 되며, &lt;code&gt;reversedMessage&lt;/code&gt;와 바인딩 된 DOM이 업데이트하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;reversedMessage&lt;/code&gt;를 보면 return되는 값으로 무엇을 나타낼지 선언하는, 선언형 프로그래밍 방식입니다.(&lt;a href=&quot;https://ko.wikipedia.org/wiki/%EC%84%A0%EC%96%B8%ED%98%95_%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D&quot;&gt;위키백과 선언형 프로그래밍&lt;/a&gt; 참고) 앞으로 이야기 할 &lt;code&gt;watch&lt;/code&gt;는 명령형 프로그래밍 방식으로 &lt;code&gt;computed&lt;/code&gt;의 선언형 프로그래밍과 차이를 보입니다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://codepen.io/beomy/pen/GYPEMR?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 위 예제 결과를 확인 할 있습니다.&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;computed&lt;/code&gt;의 캐싱 VS &lt;code&gt;methods&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;템플릿 문법의 JavaScript 표현식에서 &lt;code&gt;methods&lt;/code&gt;를 사용하여 위 예제의 &lt;code&gt;computed&lt;/code&gt;를 사용 한 결과와 동일한 결과를 얻을 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;example&amp;quot;&amp;gt;
  &amp;lt;p&amp;gt;원본 메시지: &amp;quot;{{ message }}&amp;quot;&amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;역순으로 표시한 메시지: &amp;quot;{{ reversedMessage() }}&amp;quot;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#example&amp;#39;,
  data: {
    message: &amp;#39;안녕하세요&amp;#39;
  },
  methods: {
    reversedMessage: function () {
      return this.message.split(&amp;#39;&amp;#39;).reverse().join(&amp;#39;&amp;#39;)
    }
  }
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;computed&lt;/code&gt;를 사용하는 방법과 &lt;code&gt;methods&lt;/code&gt;를 사용하는 방법의 최종 결과는 동일합니다. &lt;code&gt;computed&lt;/code&gt;와 &lt;code&gt;methods&lt;/code&gt;의 차이점은 &lt;code&gt;computed&lt;/code&gt;는 종속 대상(&lt;code&gt;reversedMessage&lt;/code&gt;의 종속 대상은 &lt;code&gt;message&lt;/code&gt;)을 캐싱한다는 것입니다. 그렇기 때문에 &lt;code&gt;computed&lt;/code&gt;는 종속 대상이 변경 될 때만 함수를 호출합니다. &lt;code&gt;message&lt;/code&gt; 값이 변하지 않는 한 &lt;code&gt;reversedMessage&lt;/code&gt;를 여러번 호출하여도 다시 계산하지 않고 캐싱한 결과를 즉시 반환합니다.&lt;/p&gt;
&lt;p&gt;시간이 많이 걸리는 계산을 할 때, &lt;code&gt;computed&lt;/code&gt;을 사용하면 더 좋은 호율의 어플리케이션을 만들 수 있습니다. 캐싱을 하지 않고, 호출 할 때마다 새롭게 계산을 해야 하는 경우에는 &lt;code&gt;methods&lt;/code&gt;를 사용해야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;example&amp;quot;&amp;gt;
  &amp;lt;p&amp;gt;원본 메시지: &amp;quot;{{ message }}&amp;quot;&amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;역순으로 표시한 메시지: &amp;quot;{{ reversedMessageByComputed }}&amp;quot;&amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;역순으로 표시한 메시지: &amp;quot;{{ reversedMessageByComputed }}&amp;quot;&amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;역순으로 표시한 메시지: &amp;quot;{{ reversedMessageByMethods() }}&amp;quot;&amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;역순으로 표시한 메시지: &amp;quot;{{ reversedMessageByMethods() }}&amp;quot;&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#example&amp;#39;,
  data: {
    message: &amp;#39;안녕하세요&amp;#39;
  },
  computed: {
    reversedMessageByComputed() {
      console.log(&amp;#39;computed reversedMessage&amp;#39;)
      return this.message.split(&amp;#39;&amp;#39;).reverse().join(&amp;#39;&amp;#39;)
    }
  },
  methods: {
    reversedMessageByMethods: function () {
      console.log(&amp;#39;methods reversedMessage&amp;#39;)
      return this.message.split(&amp;#39;&amp;#39;).reverse().join(&amp;#39;&amp;#39;)
    }
  }
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위 예제의 실행 결과를 보면 &lt;code&gt;computed&lt;/code&gt;는 캐싱하고 있기 때문에, &lt;code&gt;computed&lt;/code&gt;의 &lt;code&gt;reversedMessageByComputed&lt;/code&gt;는 한 번, &lt;code&gt;methods&lt;/code&gt;의 &lt;code&gt;reversedMessageByMethods&lt;/code&gt;는 두 번 호출 되는 것을 확인 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://codepen.io/beomy/pen/RedRJv?editors=1012&quot;&gt;CodePen&lt;/a&gt;에서 위 예제 결과를 확인 할 수 있습니다.&lt;/p&gt;
&lt;h4&gt;주의사항 - &lt;code&gt;computed&lt;/code&gt;의 캐싱&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;Date.now()&lt;/code&gt;와 같이 아무 곳에도 의존하지 않는 &lt;code&gt;computed&lt;/code&gt; 속성은 절대로 업데이트 되지 않습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;computed: {
  now: function () {
    return Date.now()
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;업데이트 되지 않습니다.&lt;/p&gt;
&lt;p&gt;위의 예시와 같이 &lt;code&gt;Date.now()&lt;/code&gt;를 리턴하는 &lt;code&gt;computed&lt;/code&gt;의 경우 업데이트 되지 않기 때문에, 몇 번을 사용하던 동일한 시간을 나타냅니다. 매번 호출 할 때 마다 현재 시간을 나타내고 싶다면, &lt;code&gt;computed&lt;/code&gt;가 아닌 &lt;code&gt;methods&lt;/code&gt;를 이용해야 합니다.&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;computed&lt;/code&gt; VS &lt;code&gt;watch&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Vue.JS는 데이터가 변경 되었을 때 호출되는 콜백 함수를 정의하는 &lt;code&gt;watch&lt;/code&gt; 속성을 제공합니다. &lt;code&gt;watch&lt;/code&gt;는 감시할 데이터를 지정하고 그 데이터가 바뀌면 어떠한 함수를 실행하라는 방식의 명령형 프로그래밍(&lt;a href=&quot;https://ko.wikipedia.org/wiki/%EB%AA%85%EB%A0%B9%ED%98%95_%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D&quot;&gt;위키백과 명령형 프로그래밍&lt;/a&gt; 참고) 방식입니다. 보통은 명령형 프로그래밍인 &lt;code&gt;watch&lt;/code&gt; 보다는 선언형 프로그래밍인 &lt;code&gt;computed&lt;/code&gt;를 사용하는 것이 더 좋습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;demo&amp;quot;&amp;gt;{{ fullName }}&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#demo&amp;#39;,
  data: {
    firstName: &amp;#39;Foo&amp;#39;,
    lastName: &amp;#39;Bar&amp;#39;,
    fullName: &amp;#39;Foo Bar&amp;#39;
  },
  watch: {
    firstName: function (val) {
      this.fullName = val + &amp;#39; &amp;#39; + this.lastName
    },
    lastName: function (val) {
      this.fullName = this.firstName + &amp;#39; &amp;#39; + val
    }
  }
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위 예제는 &lt;code&gt;watch&lt;/code&gt;를 사용한 예제입니다. &lt;a href=&quot;https://codepen.io/beomy/pen/VEOGKB?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 위 예제 결과를 확인 할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;demo&amp;quot;&amp;gt;{{ fullName }}&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#demo&amp;#39;,
  data: {
    firstName: &amp;#39;Foo&amp;#39;,
    lastName: &amp;#39;Bar&amp;#39;
  },
  computed: {
    fullName: function () {
      return this.firstName + &amp;#39; &amp;#39; + this.lastName
    }
  }
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위 예제는 &lt;code&gt;computed&lt;/code&gt;를 사용한 예제입니다. &lt;code&gt;watch&lt;/code&gt;를 사용했을 때 보다 코드가 더 간결해진 것을 볼 수 있습니다. &lt;a href=&quot;https://codepen.io/beomy/pen/GYaXre?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 위 예제 결과를 확인 할 수 있습니다.&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;computed&lt;/code&gt;의 setter 함수&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;computed&lt;/code&gt;는 기본적으로 getter 함수입니다. 필요한 경우 setter 함수를 정의하여 사용 할 수 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;demo&amp;quot;&amp;gt;{{ fullName }}&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code&gt;new Vue({
  el: &amp;#39;#demo&amp;#39;,
  data: {
    firstName: &amp;#39;Foo&amp;#39;,
    lastName: &amp;#39;Bar&amp;#39;
  },
  computed: {
  fullName: {
    // getter
    get: function () {
      return this.firstName + &amp;#39; &amp;#39; + this.lastName
    },
    // setter
    set: function (newValue) {
      var names = newValue.split(&amp;#39; &amp;#39;)
      this.firstName = names[0]
      this.lastName = names[names.length - 1]
    }
  }
})&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제는 &lt;code&gt;computed&lt;/code&gt;의 setter 함수를 정의한 예제입니다. &lt;a href=&quot;https://codepen.io/beomy/pen/JmqaNp?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 예제 결과를 확인 할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;2. &lt;code&gt;watch&lt;/code&gt; 속성&lt;/h1&gt;
&lt;p&gt;대부분의 경우 &lt;code&gt;computed&lt;/code&gt; 속성을 사용하는 것이 좋습니다. 하지만 데이터 변경의 응답으로 비동기식 계산이 필요한 경우나 시간이 많이 소요되는 계산을 해야 할 때 &lt;code&gt;watch&lt;/code&gt;를 사용하는 것이 좋습니다. (저는 개인적으로 데이터가 변경되어 API를 호출해야 할 때, &lt;code&gt;watch&lt;/code&gt;를 사용합니다.)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;watch-example&amp;quot;&amp;gt;
  &amp;lt;p&amp;gt;
    yes/no 질문을 물어보세요:
    &amp;lt;input v-model=&amp;quot;question&amp;quot;&amp;gt;
  &amp;lt;/p&amp;gt;
  &amp;lt;p&amp;gt;{{ answer }}&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;

&amp;lt;!-- 이미 Ajax 라이브러리의 풍부한 생태계와 범용 유틸리티 메소드 컬렉션이 있기 때문에, --&amp;gt;
&amp;lt;!-- Vue 코어는 다시 만들지 않아 작게 유지됩니다. --&amp;gt;
&amp;lt;!-- 이것은 이미 익숙한 것을 선택할 수 있는 자유를 줍니다. --&amp;gt;
&amp;lt;script src=&amp;quot;https://unpkg.com/axios@0.12.0/dist/axios.min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&amp;quot;https://unpkg.com/lodash@4.13.1/lodash.min.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script&amp;gt;
var watchExampleVM = new Vue({
  el: &amp;#39;#watch-example&amp;#39;,
  data: {
    question: &amp;#39;&amp;#39;,
    answer: &amp;#39;질문을 하기 전까지는 대답할 수 없습니다.&amp;#39;
  },
  watch: {
    // 질문이 변경될 때 마다 이 기능이 실행됩니다.
    question: function (newQuestion) {
      this.answer = &amp;#39;입력을 기다리는 중...&amp;#39;
      this.getAnswer()
    }
  },
  methods: {
    // _.debounce는 lodash가 제공하는 기능으로
    // 특히 시간이 많이 소요되는 작업을 실행할 수 있는 빈도를 제한합니다.
    // 이 경우, 우리는 yesno.wtf/api 에 액세스 하는 빈도를 제한하고,
    // 사용자가 ajax요청을 하기 전에 타이핑을 완전히 마칠 때까지 기다리길 바랍니다.
    // _.debounce 함수(또는 이와 유사한 _.throttle)에 대한
    // 자세한 내용을 보려면 https://lodash.com/docs#debounce 를 방문하세요.
    getAnswer: _.debounce(
      function () {
        if (this.question.indexOf(&amp;#39;?&amp;#39;) === -1) {
          this.answer = &amp;#39;질문에는 일반적으로 물음표가 포함 됩니다. ;-)&amp;#39;
          return
        }
        this.answer = &amp;#39;생각중...&amp;#39;
        var vm = this
        axios.get(&amp;#39;https://yesno.wtf/api&amp;#39;)
          .then(function (response) {
            vm.answer = _.capitalize(response.data.answer)
          })
          .catch(function (error) {
            vm.answer = &amp;#39;에러! API 요청에 오류가 있습니다. &amp;#39; + error
          })
      },
      // 사용자가 입력을 기다리는 시간(밀리세컨드) 입니다.
      500
    )
  }
})
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위에 예제는 Vue.JS 공식문서에서 &lt;code&gt;watch&lt;/code&gt;의 적절한 사용 방법을 나타낸 코드입니다. &lt;a href=&quot;https://codepen.io/beomy/pen/OBYBXX?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 위의 예제 결과를 확인 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;watch&lt;/code&gt;를 사용하면, API롤 호출하고 그 결과에 대한 응답을 받기 전까지 중간 상태를 설정할 수있습니다. &lt;code&gt;computed&lt;/code&gt;를 사용하면 API 호출 결과를 기다리는 동안의 중간 상태을 설정할 수 없습니다.&lt;/p&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://kr.vuejs.org/v2/guide/computed.html&quot;&gt;https://kr.vuejs.org/v2/guide/computed.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>computed</category>
      <category>computed setter</category>
      <category>computed 캐싱</category>
      <category>Methods</category>
      <category>Vue.js</category>
      <category>Watch</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/49</guid>
      <comments>https://beomy.tistory.com/49#entry49comment</comments>
      <pubDate>Wed, 31 Oct 2018 00:45:27 +0900</pubDate>
    </item>
    <item>
      <title>[Vue.JS] 템플릿 문법</title>
      <link>https://beomy.tistory.com/48</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;820&quot; height=&quot;289&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/990CBA395BCC4B0C2F?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/990CBA395BCC4B0C2F?original&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/990CBA395BCC4B0C2F&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F990CBA395BCC4B0C2F&quot; width=&quot;820&quot; height=&quot;289&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;Vue.JS는 렌더링 된 DOM과 Vue 인스턴스의 데이터에 바인딩 할 수 있는 HTML 기반의 문법을 제공합니다. 이 문법을 템플릿 문법이라고 하겠습니다. Vue.JS는 내부적으로 템플릿 문법을 가상 DOM을 리턴하는 &lt;code&gt;render&lt;/code&gt; 함수로 컴파일 합니다.&lt;/p&gt;
&lt;p&gt;Vue.JS는 가상 DOM을 사용하여 DOM을 변경할 때 최소한의 DOM을 조작하고 적용이 가능하도록 하였습니다. 템플릿 문법을 사용하지는 대신 &lt;code&gt;render&lt;/code&gt; 함수(JSX도 지원합니다.)를 사용할 수도 있습니다. (&lt;code&gt;render&lt;/code&gt; 함수의 사용법은 다음에 설명하도록 하겠습니다...)&lt;/p&gt;
&lt;p&gt;Vue.JS의 템플릿 문법에 대해 알아 보도록 하겠습니다.&lt;/p&gt;
&lt;h1&gt;1. 보간법 (Interpolation)&lt;/h1&gt;
&lt;p&gt;DOM에 데이터를 바인딩 하는 방법 중에 보간법, DOM에 데이터를 나타내는 방법을 이야기 하도록 하겠습니다.&lt;/p&gt;
&lt;h2&gt;Text&lt;/h2&gt;
&lt;p&gt;Mustache 문법(이중 중괄호)을 사용해 텍스트를 나타내는 방법입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;p&amp;gt;메시지: {{ msg }}&amp;lt;/p&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Mustache 문법을 사용한 &lt;code&gt;{{ msg }}&lt;/code&gt; 부분은 &lt;code&gt;msg&lt;/code&gt;의 값으로 대체됩니다. 또한 Vue 인스턴스 &lt;code&gt;data&lt;/code&gt;의 &lt;code&gt;msg&lt;/code&gt; 속성이 변경될 때 마다 화면이 갱신됩니다. &lt;code&gt;v-once&lt;/code&gt; 디렉티브를 사용하면 데이터가 변경 될 때 화면을 갱신하지 않게 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;p v-once&amp;gt;다시는 변경하지 않습니다: {{ msg }}&amp;lt;/p&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&quot;https://codepen.io/beomy/pen/aRKrba?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 위의 Mustache 문법을 사용한 예제와 &lt;code&gt;v-once&lt;/code&gt; 디렉티브를 사용한 예제를 확인 할 수 있습니다.&lt;/p&gt;
&lt;h2&gt;Raw HTML&lt;/h2&gt;
&lt;p&gt;Mustache 문법을 사용하여 DOM을 나타내면 HTML이 아닌 일반 텍스트로 DOM에 나타납니다. HTML로 출력하려면 &lt;code&gt;v-html&lt;/code&gt; 디렉티브를 사용해야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;p&amp;gt;Mustache: {{ rawHtml }}&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;v-html: &amp;lt;span v-html=&amp;quot;rawHtml&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/p&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;span&lt;/code&gt;의 내용은 &lt;code&gt;rawHtml&lt;/code&gt;에 저장된 내용으로 대체됩니다. 이 때 &lt;code&gt;rawHtml&lt;/code&gt;에 저장된 HTML에 바인딩된 데이터(보간법, 디렉티브로 바인딩 된 데이터들..)는 단순 문자열로 인식됩니다. 즉 &lt;code&gt;v-html&lt;/code&gt;을 이용한 HTML들은 템플릿 문법을 사용할 수 없습니다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://codepen.io/beomy/pen/NOzQPZ?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 &lt;code&gt;v-html&lt;/code&gt;을 사용한 예제를 확인 할 수 있습니다.&lt;/p&gt;
&lt;h2&gt;Attribute&lt;/h2&gt;
&lt;p&gt;Mustache 문법은 HTML 속성에 사용할 수 없습니다. HTML 속성에 Vue 데이터를 사용하기 위해서는 &lt;code&gt;v-bind&lt;/code&gt;를 사용해야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div v-bind:id=&amp;quot;dynamicId&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;&amp;lt;div id=&amp;quot;{{ dynamicId }}&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt;를 사용한다면 Syntax Error가 발생합니다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://codepen.io/beomy/pen/WaKeKX?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 v-bind를 사용한 예제를 확인 할 수 있습니다.&lt;/p&gt;
&lt;h2&gt;JavaScript 표현식 사용&lt;/h2&gt;
&lt;p&gt;Vue.JS의 모든 데이터 바인딩(템플릿 문법)에서는 JavaScript 표현식 사용이 가능합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{{ number + 1 }}

{{ ok ? &amp;#39;YES&amp;#39; : &amp;#39;NO&amp;#39; }}

{{ message.split(&amp;#39;&amp;#39;).reverse().join(&amp;#39;&amp;#39;) }}

&amp;lt;div v-bind:id=&amp;quot;&amp;#39;list-&amp;#39; + id&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위와 같은 JavaScript 표현식 사용이 가능합니다. 하지만 하나의 표현식만 가능하기 때문에,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- 아래는 구문입니다, 표현식이 아닙니다. --&amp;gt;
{{ var a = 1 }}

&amp;lt;!-- 조건문은 작동하지 않습니다. 삼항 연산자를 사용해야 합니다. --&amp;gt;
{{ if (ok) { return message } }}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위와 같은 JavaScript는 사용 할 수 없습니다. 또한 &lt;code&gt;Math&lt;/code&gt;나 &lt;code&gt;Date&lt;/code&gt; 같은 전역 객체는 사용 가능하지만, 사용자가 정의한 전역 객체는 사용 할 수 없습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div&amp;gt;{{ Math.round(1.2) }}&amp;lt;/div&amp;gt;

&amp;lt;!-- 사용 불가능 합니다 --&amp;gt;
&amp;lt;div&amp;gt;{{ CustomGlobals.round(1.2) }}&amp;lt;/div&amp;gt;
&amp;lt;script&amp;gt;
var CustomGlobals = {
  round(value) {
    return Math.round(value)
  }
}
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;사용자가 정의한 전역 객체는 사용 할 수 없습니다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://codepen.io/beomy/pen/pxZogQ?editors=1010&quot;&gt;CodePen&lt;/a&gt;에서 JavaScript 사용 예제를 확인 할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;2. 디렉티브 (Directive)&lt;/h1&gt;
&lt;p&gt;디렉티브는 &lt;code&gt;v-&lt;/code&gt; 접두어가 있는 특수한 속성입니다. 디렉티브는 디렉티브의 속성 값이 변경 될 때, DOM과 바인딩 하여 DOM을 변경하는 역할을 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;p v-if=&amp;quot;seen&amp;quot;&amp;gt;이제 나를 볼 수 있어요&amp;lt;/p&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제에서 &lt;code&gt;seen&lt;/code&gt;은 &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt;와 바인딩됩니다. &lt;code&gt;seen&lt;/code&gt;이 변경 될 때, &lt;code&gt;v-if&lt;/code&gt; 디렉티브로 인해 DOM에 삽입 또는 제거 되어 보이게 됩니다.&lt;/p&gt;
&lt;h2&gt;전달인자 (Argument)&lt;/h2&gt;
&lt;p&gt;일부 디렉티브(&lt;code&gt;v-bind&lt;/code&gt;, &lt;code&gt;v-on&lt;/code&gt;)는 콜론(&lt;code&gt;:&lt;/code&gt;)을 사용하여 전달인자를 사용할 수 있습니다.&lt;/p&gt;
&lt;h3&gt;v-bind&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;v-bind&lt;/code&gt;는 HTML 속성을 갱신하는데 사용됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;a v-bind:href=&amp;quot;url&amp;quot;&amp;gt;LINK&amp;lt;/a&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제에서 &lt;code&gt;href&lt;/code&gt;는 전달인자입니다. &lt;code&gt;href&lt;/code&gt; 속성을 &lt;code&gt;url&lt;/code&gt; 값에 바인드하는 &lt;code&gt;v-bind&lt;/code&gt; 디렉티브에게 알려줍니다.&lt;/p&gt;
&lt;h3&gt;v-on&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;v-on&lt;/code&gt;은 이벤트 핸들링을 하는데 사용됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;a v-on:click=&amp;quot;doSomething&amp;quot;&amp;gt;LINK&amp;lt;/a&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제에서 &lt;code&gt;click&lt;/code&gt;은 전달인자입니다. &lt;code&gt;click&lt;/code&gt; 이벤트가 발생했을 때 호출되는 &lt;code&gt;doSomething&lt;/code&gt; 함수를 &lt;code&gt;v-on&lt;/code&gt; 디렉티브에게 알려줍니다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://codepen.io/beomy/pen/YJjMjO?editors=1011&quot;&gt;CodePen&lt;/a&gt;에서 전달인자 예제를 확인 할 수 있습니다.&lt;/p&gt;
&lt;h2&gt;수식어 (Modifiers)&lt;/h2&gt;
&lt;p&gt;수식어는 점(&lt;code&gt;.&lt;/code&gt;)으로 표시되는 특수 접미사 입니다. 디렉티브를 특별한 방법으로 바인딩 해야 함을 나타냅니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;form v-on:submit.prevent=&amp;quot;onSubmit&amp;quot;&amp;gt;
  &amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;submit&amp;quot; /&amp;gt;
&amp;lt;/form&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위 예제의 &lt;code&gt;.prevent&lt;/code&gt; 수식어는 실행된 이벤트 핸들러(&lt;code&gt;onSubmit&lt;/code&gt;)에서 &lt;code&gt;event.preventDefault()&lt;/code&gt;를 호출하도록 &lt;code&gt;v-on&lt;/code&gt; 디렉티브에 알려줍니다.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://codepen.io/beomy/pen/EdpzLd?editors=1011&quot;&gt;CodePen&lt;/a&gt;에서 수식어 예제를 확인 할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;3. 약어&lt;/h1&gt;
&lt;p&gt;가장 자주 사용되는 &lt;code&gt;v-bind&lt;/code&gt;와 &lt;code&gt;v-on&lt;/code&gt;은 약어가 제공됩니다.&lt;/p&gt;
&lt;h2&gt;v-bind 약어&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- 전체 문법 --&amp;gt;
&amp;lt;a v-bind:href=&amp;quot;url&amp;quot;&amp;gt; ... &amp;lt;/a&amp;gt;

&amp;lt;!-- 약어 --&amp;gt;
&amp;lt;a :href=&amp;quot;url&amp;quot;&amp;gt; ... &amp;lt;/a&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;v-on 약어&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- 전체 문법 --&amp;gt;
&amp;lt;a v-on:click=&amp;quot;doSomething&amp;quot;&amp;gt; ... &amp;lt;/a&amp;gt;

&amp;lt;!-- 약어 --&amp;gt;
&amp;lt;a @click=&amp;quot;doSomething&amp;quot;&amp;gt; ... &amp;lt;/a&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://kr.vuejs.org/v2/guide/syntax.html&quot;&gt;https://kr.vuejs.org/v2/guide/syntax.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>Directive</category>
      <category>Interpolation</category>
      <category>v-bind</category>
      <category>V-ON</category>
      <category>Vue.js</category>
      <category>디렉티브</category>
      <category>보간법</category>
      <category>약어</category>
      <category>템플릿 문법</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/48</guid>
      <comments>https://beomy.tistory.com/48#entry48comment</comments>
      <pubDate>Tue, 23 Oct 2018 00:35:16 +0900</pubDate>
    </item>
    <item>
      <title>[Vue.JS] 라이프 사이클</title>
      <link>https://beomy.tistory.com/47</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;820&quot; height=&quot;289&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/998B063B5BC491F415?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/998B063B5BC491F415?original&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/998B063B5BC491F415&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F998B063B5BC491F415&quot; width=&quot;820&quot; height=&quot;289&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;Vue.JS의 라이프 사이클은 Vue.JS 어플리케이션을 구현할 때 빼 놓을 수 없는, 빼 놓고는 Vue.JS 어플리케이션을 개발하기 힘든, 매우 중요한 개념입니다. Vue.JS의 라이프 사이클은 크게 Create, Mount, Update, Destory로 나눌 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;600&quot; height=&quot;1520&quot; alt=&quot;Vue.JS의 라이프 사이클&quot; style=&quot;width: 600px; height: 1520px;&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/99E0014A5BC4942D18?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/99E0014A5BC4942D18?original&quot; data-alt=&quot;Vue.JS의 라이프 사이클&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99E0014A5BC4942D18&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99E0014A5BC4942D18&quot; width=&quot;600&quot; height=&quot;1520&quot; alt=&quot;Vue.JS의 라이프 사이클&quot; style=&quot;width: 600px; height: 1520px;&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;Vue.JS의 라이프 사이클&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;1. Create&lt;/h1&gt;
&lt;p&gt;Vue.JS의 라이프 사이클들 중에 가장 먼저 실행됩니다. create 단계에서 실행되는 라이프 사이클 훅(hook)들은 컴포넌트가 DOM에 추가 되기 전이기 때문에, DOM에 접근하거나 &lt;code&gt;this.$el&lt;/code&gt;을 사용할 수 없습니다. 컴포넌트가 DOM에 추가 되기 전에 호출 되기 때문에 서버 사이드 렌더링에서도 지원되는 훅입니다. Create 단계에서 호출되는 라이프 사이클 훅들은 &lt;code&gt;beforeCreate&lt;/code&gt;와 &lt;code&gt;created&lt;/code&gt;가 있습니다.&lt;/p&gt;
&lt;h2&gt;beforeCreate&lt;/h2&gt;
&lt;p&gt;Vue.JS의 라이프 사이클 훅 중에서 가장 먼저 실행 되는 훅입니다. data와 이벤트(&lt;code&gt;$on&lt;/code&gt;, &lt;code&gt;$once&lt;/code&gt;, &lt;code&gt;$off&lt;/code&gt;, &lt;code&gt;$emit&lt;/code&gt;), 감시자(&lt;code&gt;$watch&lt;/code&gt;)등이 설정 되기 전에 호출되는 라이프 사이클 훅입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export default {
  data() {
    return {
      title: &amp;#39;Vue.JS&amp;#39;
    };
  }
  beforeCreate() {
    // data(this.title)와 이벤트($on ...) 감시자($watch)를 사용할 수 없습니다.
    console.log(&amp;#39;beforeCreate&amp;#39;);
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&quot;https://codepen.io/beomy/pen/ReyErv?editors=0012&quot;&gt;CodePen&lt;/a&gt;에서 &lt;code&gt;beforeCreate&lt;/code&gt; 라이프 사이클 훅 결과를 확인 할 수 있습니다.&lt;/p&gt;
&lt;h2&gt;created&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;data&lt;/code&gt;, &lt;code&gt;computed&lt;/code&gt;, &lt;code&gt;methods&lt;/code&gt;, &lt;code&gt;watch&lt;/code&gt; 등과 같은 옵션 설정이 완료된 시점이기 때문에, &lt;code&gt;data&lt;/code&gt; 등을 사용할 수 있습니다. 하지만 아직 DOM에 컴포넌트가 마운트 되지 않았기 때문에 &lt;code&gt;$el&lt;/code&gt;은 사용할 수 없습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export default {
  data() {
    return {
      title: &amp;#39;Vue.JS&amp;#39;
    };
  },
  computed: {
    titleComputed() {
      return &amp;#39;After Computed : &amp;#39; + this.title;
    }
  },
  created() {
    // data(this.title), computed(this.titleComputed), methods... 등을 사용할 수 있지만,
    // DOM이 마운팅 되기 전이기 때문에 $el은 사용할 수 없습니다.
    console.log(this.title, this.titleComputed);
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&quot;https://codepen.io/beomy/pen/NOMevN?editors=0012&quot;&gt;CodePen&lt;/a&gt;에서 &lt;code&gt;created&lt;/code&gt; 라이프 사이클 훅 결과를 확인 할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;2. Mount&lt;/h1&gt;
&lt;p&gt;컴포넌트가 DOM에 추가될 때, 실행되는 라이프 사이클 훅입니다. 서버 사이드 렌더링을 지원하지 않습니다. 렌더링이 될 때 DOM을 변경하고 싶다면 이 라이프 사이클 훅을 사용할 수 있습니다. 하지만 컴포넌트 초기에 &lt;code&gt;data&lt;/code&gt;가 세팅되어야 한다면, &lt;code&gt;created&lt;/code&gt; 라이프 사이클 훅을 사용하는 것이 좋습니다. Mount 단계에서 호출되는 라이프 사이클 훅들은 &lt;code&gt;beforeMount&lt;/code&gt;와 &lt;code&gt;mounted&lt;/code&gt;가 있습니다.&lt;/p&gt;
&lt;h2&gt;beforeMount&lt;/h2&gt;
&lt;p&gt;컴포넌트가 DOM에 추가 되기 직전에 실행되는 훅입니다. 서버 사이드 렌더링을 지원하지 않습니다. 컴포넌트 초기에 &lt;code&gt;data&lt;/code&gt;가 세팅되어야 한다면 &lt;code&gt;created&lt;/code&gt; 라이프 사이클 훅을, 렌더링 되고 DOM을 변경해야 한다면 &lt;code&gt;mounted&lt;/code&gt; 라이프 사이클 훅을 사용하면 되기 때문에, 거의 사용하지 않는 라이프 사이클 훅입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export default  {
  beforeMount() {
    // $el은 아직 사용할 수 없습니다.
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&quot;https://codepen.io/beomy/pen/QZrzrd?editors=0012&quot;&gt;CodePen&lt;/a&gt;에서 &lt;code&gt;beforeMount&lt;/code&gt; 라이프 사이클 훅 결과를 확인 할 수 있습니다.&lt;/p&gt;
&lt;h2&gt;mounted&lt;/h2&gt;
&lt;p&gt;컴포넌트가 DOM에 추가 된 후 호출되는 라이프 사이클 훅입니다. 서버 사이드 렌더링은 지원하지 않습니다. &lt;code&gt;$el&lt;/code&gt;을 사용하여 DOM에 접근 할 수 있습니다. &lt;code&gt;mounted&lt;/code&gt; 훅이 호출되었다고 모든 컴포넌트가 마운트 되었다고 보장할 수는 없습니다. 전체가 렌더링 보장된 상태에서 작업을 하기 위해서는 &lt;code&gt;$nextTick&lt;/code&gt;을 사용해야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export default {
  mounted() {
    // $el 을 사용할 수 있습니다.
    console.log(&amp;#39;mounted&amp;#39;, this.$el);
    this.$nextTick(() =&amp;gt; {
      // 모든 화면이 렌더링된 후 호출됩니다.
    });
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&quot;https://codepen.io/beomy/pen/KGRbGq?editors=0012&quot;&gt;CodePen&lt;/a&gt;에서 &lt;code&gt;mounted&lt;/code&gt; 라이프 사이클 훅 결과를 확인 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;자식 컴포넌트의 &lt;code&gt;mounted&lt;/code&gt; 훅이 부모 컴포넌트의 &lt;code&gt;mounted&lt;/code&gt; 훅 보다 먼저 실행됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;537&quot; height=&quot;629&quot; alt=&quot;Mounted 훅 호출 순서&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/999F79345BC7579416?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/999F79345BC7579416?original&quot; data-alt=&quot;자식 컴포넌트의 Mounted 훅이 부모의 Mounted 훅 보다 먼저 실행됩니다.&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/999F79345BC7579416&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F999F79345BC7579416&quot; width=&quot;537&quot; height=&quot;629&quot; alt=&quot;Mounted 훅 호출 순서&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;자식 컴포넌트의 Mounted 훅이 부모의 Mounted 훅 보다 먼저 실행됩니다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;위의 그림을 보면 쉽게 이해 하실 수 있습니다. &lt;code&gt;created&lt;/code&gt; 훅은 부모 -&amp;gt; 자식 순으로 호출 되지만, &lt;code&gt;mounted&lt;/code&gt; 훅은 자식 -&amp;gt; 부모 순으로 호출됩니다. 즉 부모 컴포넌트는 자식 컴포넌트가 모두 DOM에 추가 된 후에야 &lt;code&gt;mounted&lt;/code&gt; 훅이 실행됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const Child = {
  template: &amp;#39;&amp;lt;div&amp;gt;Child&amp;lt;/div&amp;gt;&amp;#39;,
  mounted() {
    console.log(&amp;#39;Child Component Mounted&amp;#39;);
  }
}

new Vue({
  el: &amp;#39;#app&amp;#39;,
  mounted() {
    console.log(&amp;#39;Parent Component Mounted&amp;#39;);
  },
  components: {
    Child
  }
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&quot;https://codepen.io/beomy/pen/jexXjL?editors=0012&quot;&gt;CodePen&lt;/a&gt;에서 위 예제의 실행 결과를 확인 할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;3. Update&lt;/h1&gt;
&lt;p&gt;컴포넌트에서 사용되는 속성들이 변경되는 등의.. 컴포넌트가 재 랜더링 되면 실행되는 라이프 사이클 훅입니다. 컴포넌트가 재 렌더링 될 때, 변경 된 값으로 어떠한 작업을 해야 할 때 유용하게 사용 되는 훅입니다. 서버 사이드 렌더링은 지원하지 않습니다.&lt;/p&gt;
&lt;h2&gt;beforeUpdate&lt;/h2&gt;
&lt;p&gt;DOM이 재 렌더링 되기 직전에 호출되는 라이프 사이클 훅입니다. 업데이트 된 값들을 가지고 있는 상태이기 때문에, 업데이트 된 값으로 다른 값들을 업데이트 할 수 있습니다. 이 훅에서 값이 변경되더라도 다시 &lt;code&gt;beforeUpdate&lt;/code&gt; 훅이 호출 되지 않기 때문에, 무한 루프에 빠질 걱정은 하지 않으셔도 됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export default {
  beforeUpdate() {
    console.log(&amp;#39;beforeUpdate&amp;#39;);
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&quot;https://codepen.io/beomy/pen/wYXoee?editors=1012&quot;&gt;CodePen&lt;/a&gt;에서 &lt;code&gt;beforeUpdate&lt;/code&gt; 훅 예제를 확인 할 수 있습니다.&lt;/p&gt;
&lt;h2&gt;updated&lt;/h2&gt;
&lt;p&gt;DOM이 재 렌더링 된 후 호출되는 라이프 사이클 훅입니다. DOM이 업데이트 된 후 호출 되는 훅이기 때문에 변경 된 후의 DOM을 이용해야 하는 처리를 할 때 사용하기 유용한 훅입니다. &lt;code&gt;mounted&lt;/code&gt; 훅과 마찬가지로 재 렌더링이 끝났다는 것이 보장된 상태에서 작업하기 위해서는 &lt;code&gt;$nextTick&lt;/code&gt;을 사용해야 합니다. &lt;code&gt;beforeUpdate&lt;/code&gt; 훅과 다르게 &lt;code&gt;updated&lt;/code&gt; 훅에서 &lt;code&gt;data&lt;/code&gt;를 수정하게 되면 &lt;code&gt;update&lt;/code&gt; 훅이 호출 되기 때문에 무한 루프에 빠질 수 있으니 유의해야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export default {
  updated() {
    console.log(&amp;#39;updated&amp;#39;);
    this.$nextTick(function () {
      // 모든 화면이 렌더링된 후 실행합니다.
    });
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&quot;https://codepen.io/beomy/pen/aRKVVZ?editors=1012&quot;&gt;CodePen&lt;/a&gt;에서 &lt;code&gt;updated&lt;/code&gt; 훅 예제를 확인 할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;4. Destroy&lt;/h1&gt;
&lt;p&gt;컴포넌트가 제거 될 때 실행되는 라이프 사이클 훅입니다.&lt;/p&gt;
&lt;h2&gt;beforeDestroy&lt;/h2&gt;
&lt;p&gt;컴포넌트가 제거 되기 직전에 호출되는 라이프 사이클 훅입니다. 이 훅에서 컴포넌트는 본래의 기능들을 가지고 있는 온전한 상태입니다. 이 훅에서 이벤트 리스너를 해제하거나 컴포넌트에서 동작으로 할당 받은 자원들은 해제해야 할 때 사용하기 적합한 훅입니다. 서버 사이드 렌더링을 지원하지 않습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export default {
  beforeDestroy() {
    console.log(&amp;#39;beforeDestory&amp;#39;);
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&quot;https://codepen.io/beomy/pen/oaypYy?editors=0012&quot;&gt;CodePen&lt;/a&gt;에서 &lt;code&gt;beforeDestroy&lt;/code&gt; 훅 예제를 확인 할 수 있습니다.&lt;/p&gt;
&lt;h2&gt;destroyed&lt;/h2&gt;
&lt;p&gt;컴포넌트가 제거 된 후 호출되는 라이프 사이클 훅입니다. 컴포넌트의 모든 이벤트 리스너(&lt;code&gt;@click&lt;/code&gt;, &lt;code&gt;@change&lt;/code&gt; 등..)와 디렉티브(&lt;code&gt;v-model&lt;/code&gt;, &lt;code&gt;v-show&lt;/code&gt; 등..)의 바인딩이 해제 되고, 하위 컴포넌트도 모두 제거됩니다. 서버 사이드 렌더링을 지원하지 않습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export default {
  destroyed() {
    console.log(&amp;#39;destroyed&amp;#39;);
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href=&quot;https://codepen.io/beomy/pen/jeKYKM?editors=0012&quot;&gt;CodePen&lt;/a&gt;에서 &lt;code&gt;destroyed&lt;/code&gt; 훅 예제를 확인 할 수 있습니다.&lt;/p&gt;
&lt;h1&gt;5. GitHub 소스 확인&lt;/h1&gt;
&lt;p&gt;Vue.JS 라이프 사이클 예제는 &lt;a href=&quot;https://github.com/beomy/vue-life-cycle&quot;&gt;https://github.com/beomy/vue-life-cycle&lt;/a&gt;에서 확인 할 수 있습니다.&lt;/p&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/witinweb/vue-js-%EB%9D%BC%EC%9D%B4%ED%94%84%EC%82%AC%EC%9D%B4%ED%81%B4-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-7780cdd97dd4&quot;&gt;https://medium.com/witinweb/vue-js-%EB%9D%BC%EC%9D%B4%ED%94%84%EC%82%AC%EC%9D%B4%ED%81%B4-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-7780cdd97dd4&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>beforeCreate</category>
      <category>beforeDestroy</category>
      <category>beforeMount</category>
      <category>beforeUpdate</category>
      <category>created</category>
      <category>destroyed</category>
      <category>life cycle</category>
      <category>mounted</category>
      <category>Updated</category>
      <category>Vue.js</category>
      <category>라이프 사이클</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/47</guid>
      <comments>https://beomy.tistory.com/47#entry47comment</comments>
      <pubDate>Sun, 21 Oct 2018 02:30:31 +0900</pubDate>
    </item>
    <item>
      <title>[Vue.JS] Vue 인스턴스</title>
      <link>https://beomy.tistory.com/46</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;820&quot; height=&quot;289&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/9976AE505BB4E0621C?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/9976AE505BB4E0621C?original&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/9976AE505BB4E0621C&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F9976AE505BB4E0621C&quot; width=&quot;820&quot; height=&quot;289&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;Vue.JS 공식 문서를 따르면, Vue는 엄격하게 MVVM 패턴과 관계되어 있지 않지만, MVVM 패턴에 영감을 받았다고 합니다.(&lt;a href=&quot;http://beomy.tistory.com/43&quot;&gt;[디자인패턴] MVC, MVP, MVVM 비교&lt;/a&gt; 참고) MVVM 패턴의 특징 중 하나가 데이터 바인딩입니다. Model이 업데이트 되면, 데이터 바인딩으로 인해 View가 업데이트 됩니다.&lt;/p&gt;
&lt;h1&gt;Vue 인스턴스 만들기&lt;/h1&gt;
&lt;p&gt;Vue 어플리케이션을 만드는 것은 Vue 인스턴스를 만드는 것에서 시작됩니다. Vue 어플리케이션의 구조는 루트 Vue 인스턴트 밑에 하위 컴포넌트가 붙는 컴포넌트 트리 구조입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;Root Instance
└─ TodoList
   ├─ TodoItem
   │  ├─ DeleteTodoButton
   │  └─ EditTodoButton
   └─ TodoListFooter
      ├─ ClearTodosButton
      └─ TodoListStatistics&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Vue 인스턴스를 생성 할 때, &lt;code&gt;data&lt;/code&gt;, &lt;code&gt;method&lt;/code&gt;, 라이프사이클 콜백(&lt;code&gt;created&lt;/code&gt;, &lt;code&gt;mounted&lt;/code&gt;, &lt;code&gt;destoryed&lt;/code&gt; 등의..) 옵션들을 포함 하는 옵션 객체를 전달 해야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;new Vue({
  data() {
    // ... 화면을 그리는데 사용되는 데이터들
  },
  methods: {
    // ... 화면을 그리는데 사용되는 메소드들
  },
  created() {
    // ... 컴포넌트가 생성된 후 호출되는 라이프사이클 콜백
  },
  mounted() {
    // ... 컴포넌트가 마운트 된 후 호출되는 라이프사이클 콜백
  },
  destoryed() {
    // ... 컴포넌트가 언마운트 된 후 호출되는 라이프사이클 콜백
  },
  components: {
    // ... 화면을 그리는데 사용되는 하위 컴포넌트들
  }
});&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Vue 인스턴스를 생성할 때 넘겨줘야 하는 옵션 중 몇가지를 살펴보도록 하겠습니다. 생명주기 콜백함수들도 옵션들 중에 하나인데, 생명주기 콜백함수들은 많기도하고, 중요하기 때문에 다음 포스팅에 이야기 하려고 합니다. 각각의 옵션들의 사용방법도 이후 포스팅에서 더 자세히 이야기 하도록 하고, 이번에는 간단한 선언 방법만 이야기 하겠습니다.&lt;/p&gt;
&lt;h2&gt;1) data (Object | Function)&lt;/h2&gt;
&lt;p&gt;화면을 그리는데 사용하는 데이터 객체를 리턴하는 함수이거나 혹은 데이터 객체 그 자체가 될 수 있습니다. Vue 인스턴스가 아닌 하위 컴포넌트에서 사용될 때는 반드시 함수형이여야 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  value1: &amp;#39;this is value 1&amp;#39;, // value1, value2를 객체의 속성이라고 이야기 하겠습니다.
  value2: &amp;#39;this is value 2&amp;#39;
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;data&lt;/code&gt; 객체의 속성들이 변경 되면(함수일 경우 리턴하는 데이터 객체의 속성), 화면이 자동으로 갱신됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;new Vue({
  data() {
    return {
      // 이 값들이 변경되면 화면이 자동으로 갱신됩니다.
      msg: &amp;#39;함수형 data 입니다.&amp;#39;,
      desc: &amp;#39;하위 컴포넌트에서 data는 반드시 함수형이여 합니다.&amp;#39;
    };
  }
  /* 혹은
  data: {
    msg: &amp;#39;객체형 data 입니다.&amp;#39;
    desc: &amp;#39;Vue 인스턴스를 생성 할 때는 객체를 사용해도 됩니다.&amp;#39;
  }
  */
});&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;2) props (Array&lt;string&gt; | Object)&lt;/h2&gt;
&lt;p&gt;부모 컴포넌트로부터 전달 받은 property들의 Array 혹은 Object입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div&amp;gt;
  &amp;lt;HellowWord title=&amp;quot;Hello Vue&amp;quot; desc=&amp;quot;Vue Instance&amp;quot; /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;부모 컴포넌트에서 하위 컴포넌트로 &lt;code&gt;props&lt;/code&gt; 넘겨주기&lt;/p&gt;
&lt;p&gt;위의 코드와 같이 하위 컴포넌트를 호출하면 &lt;code&gt;title&lt;/code&gt;와 &lt;code&gt;desc&lt;/code&gt;를 &lt;code&gt;props&lt;/code&gt;의 Array에 추가해 주어야 하위 컴포넌트에서 &lt;code&gt;title&lt;/code&gt;과 &lt;code&gt;desc&lt;/code&gt;를 사용할 수 있게 됩니다. 유효성 검사 및 기본값과 같은 고급 옵션을 사용하기 위해 Object를 사용할 수도 있습니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  props:[&amp;#39;title&amp;#39;, &amp;#39;desc&amp;#39;],
  /* 혹은 고급 옵션을 사용하기 위한 Object
  props: {
    title: String, // 타입 설정
    desc: {
      required: true, // 필수값 설정
      default: &amp;#39;설명이 없습니다.&amp;#39; // 기본 값 설정
    }
  } */
}&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;3) computed ({ [key: string]: Function | { get: Function, set: Function } })&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;data&lt;/code&gt;를 미리 계산한 값들을 나타내는 함수들을 모아둔 객체입니다. setter을 만들지 않았다면 기본적으로 getter 기능만 제공합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  date() {
    return() {
      msg: &amp;#39;함수형 data 입니다.&amp;#39;,
      desc: &amp;#39;하위 컴포넌트에서 data는 반드시 함수형이여 합니다.&amp;#39;
    }
  }
  computed: {
    print() {
      return this.msg + &amp;#39; &amp;#39; + this.desc
    }
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;4) watch ({ [key: string]: sting | Function | Object | Array })&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;watch&lt;/code&gt;는 &lt;code&gt;data&lt;/code&gt;와 한쌍을 이루게 됩니다. 즉, &lt;code&gt;data&lt;/code&gt;에 표현된 key와 &lt;code&gt;watch&lt;/code&gt;에 표현된 key의 이름은 동일해야 합니다. &lt;code&gt;data&lt;/code&gt;의 value 값이 변경이 되면, &lt;code&gt;watch&lt;/code&gt;의 key의 value에 할당된 콜백이 실행됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  data() {
    return {
      msg: &amp;#39;메시지입니다&amp;#39;
    }
  }
  watch: {
    msg() {
      console.log(&amp;#39;data의 msg가 수정되면 호출됩니다.&amp;#39;);
    }
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;5) methods ({ [key: string]: Function })&lt;/h2&gt;
&lt;p&gt;화면을 표현하는데 사용하는 모든 메소드들이 모여 있는 곳입니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  data() {
    return {
      a: 1
    };
  }
  methods: {
    plus() {
      this.a++;
    }
  }
}&lt;/code&gt;&lt;/pre&gt;&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://kr.vuejs.org/v2/guide/instance.html&quot;&gt;https://kr.vuejs.org/v2/guide/instance.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Vue.JS</category>
      <category>computed</category>
      <category>data</category>
      <category>Methods</category>
      <category>props</category>
      <category>Vue.js</category>
      <category>Watch</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/46</guid>
      <comments>https://beomy.tistory.com/46#entry46comment</comments>
      <pubDate>Thu, 11 Oct 2018 00:41:23 +0900</pubDate>
    </item>
    <item>
      <title>[자바스크립트] ES8(ECMA Script 8) - async, await</title>
      <link>https://beomy.tistory.com/45</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;820&quot; height=&quot;289&quot; alt=&quot;ES8&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;289&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/99C1243E5BABA8F310?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/99C1243E5BABA8F310?original&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99C1243E5BABA8F310&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99C1243E5BABA8F310&quot; width=&quot;820&quot; height=&quot;289&quot; alt=&quot;ES8&quot; data-origin-width=&quot;820&quot; data-origin-height=&quot;289&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;비동기 프로그래밍으로 발생하는 콜백지옥을 해결하는 방법으로 ES6에서 추가된 Promise를 사용하는 방법이 있습니다. (&lt;a href=&quot;http://beomy.tistory.com/10&quot;&gt;[자바스크립트] 비동기프로그래밍 - 콜백함수(Callback function)&lt;/a&gt; 참고) 이번 포스트에서는 비동기 프로그래밍을 처리할 수 있는 방법으로 &lt;code&gt;async&lt;/code&gt;와 &lt;code&gt;await&lt;/code&gt;에 대해 이야기 하려 합니다.&lt;/p&gt;
&lt;h1&gt;1. 문법&lt;/h1&gt;
&lt;pre&gt;&lt;code&gt;async function name([param[, param[, ... param]]]) { 
    statements 
}&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;name&lt;/strong&gt;: 함수 이름&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;param&lt;/strong&gt;: 함수에 전달되는 인자들의 이름&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;statements&lt;/strong&gt;:  함수 본문&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;리턴 값&lt;/strong&gt;: async 함수의 &lt;code&gt;return&lt;/code&gt; 값으로 &lt;code&gt;resolve&lt;/code&gt; 된 &lt;code&gt;Promise&lt;/code&gt; 객체 혹은 async 함수에서 예외가 발생하였다면 발생 된 예외로 &lt;code&gt;reject&lt;/code&gt; 된 &lt;code&gt;Promise&lt;/code&gt; 객체&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;async 함수를 사용하는 방법은 기본 함수를 생성하는 것과 큰 차이가 없습니다. 그렇기 때문에 &lt;code&gt;Promise&lt;/code&gt;를 이용하는 것보다 &lt;code&gt;async&lt;/code&gt;, &lt;code&gt;await&lt;/code&gt;를 사용할 때 코드가 더 간결해 질 수 있습니다. async 함수의 리턴 값은 &lt;code&gt;resolve&lt;/code&gt; 된 &lt;code&gt;Promise&lt;/code&gt; 객체, 혹은 &lt;code&gt;reject&lt;/code&gt; 된 &lt;code&gt;Promise&lt;/code&gt; 객체를 암묵적으로 리턴합니다. 암묵적으로 리턴한다는 말은, &lt;code&gt;new Promise&lt;/code&gt; 를 사용하여 &lt;code&gt;Promise&lt;/code&gt; 객체를 리턴하지 않아도 async 함수의 리턴 값으로 &lt;code&gt;Promise&lt;/code&gt; 객체를 받게 된다는 뜻으로 사용하였습니다.&lt;/p&gt;
&lt;h4&gt;* 참고 - 리턴 값 확인하기&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;230&quot; height=&quot;146&quot; alt=&quot;return 값 확인하기 - resolve&quot; style=&quot;width: 230px; height: 146px;&quot; data-origin-width=&quot;281&quot; data-origin-height=&quot;179&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/999DE33A5BACFCE152?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/999DE33A5BACFCE152?original&quot; data-alt=&quot;async 함수 안에서 리턴 한 값은 resolve 된 Promise 객체로 리턴 되어 집니다.&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/999DE33A5BACFCE152&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F999DE33A5BACFCE152&quot; width=&quot;230&quot; height=&quot;146&quot; alt=&quot;return 값 확인하기 - resolve&quot; style=&quot;width: 230px; height: 146px;&quot; data-origin-width=&quot;281&quot; data-origin-height=&quot;179&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;async 함수 안에서 리턴 한 값은 resolve 된 Promise 객체로 리턴 되어 집니다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;230&quot; height=&quot;160&quot; alt=&quot;return 값 확인하기 - reject&quot; style=&quot;width: 230px; height: 160px;&quot; data-origin-width=&quot;280&quot; data-origin-height=&quot;195&quot;&gt;&lt;span data-url=&quot;https://t1.daumcdn.net/cfile/tistory/99FF5C415BACFE5E1A?original&quot; data-phocus=&quot;https://t1.daumcdn.net/cfile/tistory/99FF5C415BACFE5E1A?original&quot; data-alt=&quot;async 함수 안에서 발생한 예외는 reject 된 Promise 객체로 리턴 되어 집니다.&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/cfile/tistory/99FF5C415BACFE5E1A&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99FF5C415BACFE5E1A&quot; width=&quot;230&quot; height=&quot;160&quot; alt=&quot;return 값 확인하기 - reject&quot; style=&quot;width: 230px; height: 160px;&quot; data-origin-width=&quot;280&quot; data-origin-height=&quot;195&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;async 함수 안에서 발생한 예외는 reject 된 Promise 객체로 리턴 되어 집니다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h1&gt;2. 데모&lt;/h1&gt;
&lt;pre&gt;&lt;code&gt;function resolveAfter2Seconds() {
  return new Promise(resolve =&amp;gt; {
    setTimeout(() =&amp;gt; {
      resolve(&amp;#39;resolved&amp;#39;);
    }, 2000);
  });
}

async function asyncCall() {
  console.log(&amp;#39;calling&amp;#39;);
  var result = await resolveAfter2Seconds();
  console.log(result);
  // expected output: &amp;#39;resolved&amp;#39;
}

asyncCall();&lt;/code&gt;&lt;/pre&gt;&lt;div id=&quot;async-result&quot; class=&quot;txc-textbox&quot; style=&quot;background-color: #ffffff; padding: 10px; height: 80px; overflow-y: auto; border: 1px solid #cbcbcb;&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;div style=&quot;display: inline-block; margin-top: 1em; border: 1px solid #ddd; border-radius: 3px; vertical-align: top;&quot;&gt;&lt;button onclick=&quot;asyncCall()&quot; style=&quot;height: 30px; width: 70px;&quot;&gt;Run&lt;/button&gt;&lt;/div&gt;
&lt;div style=&quot;display: inline-block; margin-top: 1em; border: 1px solid #ddd; border-radius: 3px; vertical-align: top;&quot;&gt;&lt;button onclick=&quot;asyncReset()&quot; style=&quot;height: 30px; width: 70px;&quot;&gt;Reset&lt;/button&gt;&lt;/div&gt;
&lt;script&gt;
function resolveAfter2Seconds() {
  return new Promise(resolve =&gt; {
    setTimeout(() =&gt; {
      resolve('resolved');
    }, 2000);
  });
}
async function asyncCall() {
  document.querySelector('#async-result').innerText += '&gt; calling\n';
  var result = await resolveAfter2Seconds();
  document.querySelector('#async-result').innerText += '&gt; ' + result + '\n';
}
function asyncReset() {
  document.querySelector('#async-result').innerText = '';
}
&lt;/script&gt;
&lt;/div&gt;

&lt;h1&gt;3. 설명&lt;/h1&gt;
&lt;p&gt;async 함수에서는 &lt;code&gt;await&lt;/code&gt;를 사용할 수 있습니다. &lt;code&gt;await&lt;/code&gt;는 async 함수에서만 사용 가능합니다. 일반 함수에서 &lt;code&gt;await&lt;/code&gt;를 사용하게 되면 syntax error가 발생됩니다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;await&lt;/code&gt;는 &lt;code&gt;Promise&lt;/code&gt;와 함께 사용되어야 합니다. &lt;code&gt;await&lt;/code&gt;를 사용하면 &lt;code&gt;Promise&lt;/code&gt;가 종료 될 때까지 함수 실행이 일시 정지 됩니다. 그후 &lt;code&gt;Promise&lt;/code&gt;가 종료 되면 함수 실행이 다시 진행 됩니다. &lt;code&gt;await&lt;/code&gt; 사용하면 &lt;code&gt;Promise&lt;/code&gt;에서 &lt;code&gt;resolve&lt;/code&gt; 된 값을 반환 받게 됩니다. &lt;code&gt;await&lt;/code&gt;의 &lt;code&gt;Promise&lt;/code&gt;가 &lt;code&gt;reject&lt;/code&gt; 되면, 예외가 발생됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function awaitFunction() {
  return new Promise((resolve, reject) =&amp;gt; {
    setTimeout(() =&amp;gt; resolve(&amp;#39;success&amp;#39;), 1000);
    // setTimeout(() =&amp;gt; reject(&amp;#39;fail&amp;#39;), 1000);
  });
}

async function asyncFunction() {
  try {
    const msg = await awaitFunction();
    console.log(msg); // awaitFunction에서 resolve가 호출 될 때 resolve의 인자값 &amp;#39;success&amp;#39;
  } catch (e) {
    console.log(e); // awaitFunction에서 reject가 호출 될 때 reject의 인자값 &amp;#39;fail&amp;#39;
  }
}

asyncFunction();&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;Promise&lt;/code&gt;의 사용 방법을 단순히 하기 위해서(&lt;code&gt;Promise&lt;/code&gt;의 콜백 함수 동작을 단순히 보이기 위해서) &lt;code&gt;async&lt;/code&gt;와 &lt;code&gt;await&lt;/code&gt;가 사용됩니다.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var resolveAfter2Seconds = function() {
  console.log(&amp;quot;starting slow promise&amp;quot;);
  return new Promise(resolve =&amp;gt; {
    setTimeout(function() {
      resolve(20);
      console.log(&amp;quot;slow promise is done&amp;quot;);
    }, 2000);
  });
};

var resolveAfter1Second = function() {
  console.log(&amp;quot;starting fast promise&amp;quot;);
  return new Promise(resolve =&amp;gt; {
    setTimeout(function() {
      resolve(10);
      console.log(&amp;quot;fast promise is done&amp;quot;);
    }, 1000);
  });
};

var sequentialStart = async function() {
  console.log(&amp;quot;==SEQUENTIAL START==&amp;quot;);

  // If the value of the expression following the await operator is not a Promise, it&amp;#39;s converted to a resolved Promise.
  const slow = await resolveAfter2Seconds();

  const fast = await resolveAfter1Second();
  console.log(slow);
  console.log(fast);
}

var concurrentStart = async function() {
  console.log(&amp;quot;==CONCURRENT START with await==&amp;quot;);
  const slow = resolveAfter2Seconds(); // starts timer immediately
  const fast = resolveAfter1Second();

  console.log(await slow);
  console.log(await fast); // waits for slow to finish, even though fast is already done!
}

var stillSerial = function() {
  console.log(&amp;quot;==CONCURRENT START with Promise.all==&amp;quot;);
  Promise.all([resolveAfter2Seconds(), resolveAfter1Second()]).then(([slow, fast]) =&amp;gt; {
    console.log(slow);
    console.log(fast);
  });
}

var parallel = function() {
  console.log(&amp;quot;==PARALLEL with Promise.then==&amp;quot;);
  resolveAfter2Seconds().then((message)=&amp;gt;console.log(message)); // in this case could be simply written as console.log(resolveAfter2Seconds());
  resolveAfter1Second().then((message)=&amp;gt;console.log(message));
}

sequentialStart(); // takes 2+1 seconds in total
// wait above to finish
setTimeout(concurrentStart, 4000); // takes 2 seconds in total
// wait again
setTimeout(stillSerial, 7000); // same as before
// wait again
setTimeout(parallel, 10000); // trully parallel&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;위의 예제는 MDN에 나와 있는 &lt;code&gt;async&lt;/code&gt;, &lt;code&gt;await&lt;/code&gt; 예제입니다. 코드가 길고 복잡해 보이지만 뜯어 보면 어렵지 않은 코드입니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;resolveAfter2Seconds&lt;/code&gt; : 2초후 &lt;code&gt;Promise.resolve&lt;/code&gt;로 20을 내보내는 함수입니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;resolveAfter1Second&lt;/code&gt; : 1초후 &lt;code&gt;Promise.resolve&lt;/code&gt;로 10을 내보내는 함수입니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sequentialStart&lt;/code&gt; : &lt;code&gt;resolveAfter2Seconds&lt;/code&gt;와 &lt;code&gt;resolveAfter1Second&lt;/code&gt;를 &lt;code&gt;await&lt;/code&gt;를 이용하여 순차적으로 결과를 받는 함수입니다. 순차적으로 결과를 받기 때문에, 2초(&lt;code&gt;resolveAfter2Seconds&lt;/code&gt;) + 1초(&lt;code&gt;resolveAfter1Second&lt;/code&gt;) = 3초 후 &lt;code&gt;console.log&lt;/code&gt; 출력 값을 확인 할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;concurrentStart&lt;/code&gt; : &lt;code&gt;resolveAfter2Seconds&lt;/code&gt;와 &lt;code&gt;resolveAfter1Second&lt;/code&gt;를 동시(엄밀히 말하면 동시는 아니지만..) 실행하고 그 후 &lt;code&gt;await&lt;/code&gt;로 결과를 받는 함수입니다. 동시에 함수들이 실행 되기 때문에 가장 마지막으로 종료 되는 2초(&lt;code&gt;resolveAfter2Seconds&lt;/code&gt;) 후 &lt;code&gt;console.log&lt;/code&gt; 출력 값을 확인 할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;stillSerial&lt;/code&gt; : &lt;code&gt;Promise.all&lt;/code&gt;을 사용하여 &lt;code&gt;concurrentStart&lt;/code&gt;와 동일한 기능을 구현한 함수입니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;parallel&lt;/code&gt; : 병렬로 &lt;code&gt;resolveAfter2Seconds&lt;/code&gt;와 &lt;code&gt;resolveAfter1Second&lt;/code&gt;를 실행하는 함수입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;참고&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>JavaScript</category>
      <category>Async</category>
      <category>await</category>
      <category>ECMA Script 2017</category>
      <category>ECMA Script 8</category>
      <category>ES8</category>
      <category>javascript</category>
      <category>Promise</category>
      <category>promise.all</category>
      <category>자바스크립트</category>
      <author>버미노트</author>
      <guid isPermaLink="true">https://beomy.tistory.com/45</guid>
      <comments>https://beomy.tistory.com/45#entry45comment</comments>
      <pubDate>Sat, 29 Sep 2018 01:53:11 +0900</pubDate>
    </item>
  </channel>
</rss>