<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>About Web</title>
    <link>https://abcdqbbq.tistory.com/</link>
    <description>Javascript , Vue.js, Jquery, ajax, HTML, CSS, Sass 등 이론 정리</description>
    <language>ko</language>
    <pubDate>Tue, 14 Jul 2026 07:21:55 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>갠디</managingEditor>
    <image>
      <title>About Web</title>
      <url>https://tistory1.daumcdn.net/tistory/3657005/attach/13aa7c08a2d14f8798727db21c62bddc</url>
      <link>https://abcdqbbq.tistory.com</link>
    </image>
    <item>
      <title>REST API란?</title>
      <link>https://abcdqbbq.tistory.com/117</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;프론트 개발을 하며 서버단과 소통을 하다보면 REST API라는 말을 자주 듣게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;REST API는 웹 기반의 시스템에서 클라이언트와 서버가 원활하게 통신할 수 있게 해주는 일종의 지침이자 설계 원칙이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. REST&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;REST는 Representational State Transfer의 약자로&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP URI를 통해 자원(Resource)를 명시하고, HTTP 메소드 (GET, POST, PUT, PATCH, DELETE)를 통해 해당 자원에 대한 CRUD(Create, Read, Update, Delete)를 적용하는 것을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://abcdqbbq.tistory.com/43&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;2020.04.10 - [Frontend/Web] - URI vs URL 비교&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자원 즉, 문서, 사진, 그림, 데이터 등의 정보를 URI를 통해 명시한다 (이 때 URI는 동사가 아닌 명사로만 이뤄진다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) 특정 예술가들의 정보가 자원일 경우 /artists 라고 URI에 자원을 명시한다. (명사, 복수형으로 작성한다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;GET&lt;/b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; /artists&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;-&amp;gt; 아티스트 목록을 가져온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;GET&lt;/b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; /artists/musician&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; -&amp;gt; 아티스트 중 세부 카테고리 뮤지션 목록을 가져온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;POST&lt;/b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; /artists&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; -&amp;gt; 아티스트 목록에 새로운 아티스트 정보를 추가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;PUT&lt;/b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;/artists&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; -&amp;gt; 아티스트 목록을 수정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;DELETE&lt;/b&gt;&amp;nbsp; /artists&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; -&amp;gt; 아티스트 목록을 제거한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 artists 라는 복수형 명사로 URI에 자원을 명시한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 GET, POST, PUT, DELETE 와 같은 HTTP 메소드를 통해서 서버에 요청을 하기 때문에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;REST API 메시지만으로도 어떤 자원을 어떤식으로 처리할 것인지를 명시적으로 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 클라이언트에서 URI에 자원을 적어서 HTTP 메소드로 요청을 하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버에서는 적절한 응답을 보내게 되는데 JSON, XML, TEXT, RSS 등 여러 형태로 응답을 받을 수 있지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주로 JSON이나 XML을 통해 데이터를 주고 받는 것이 일반적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ex.png&quot; data-origin-width=&quot;1210&quot; data-origin-height=&quot;848&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/47SYw/btsQvQMBU9U/aLXYZkziotTWkXDsZzBe0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/47SYw/btsQvQMBU9U/aLXYZkziotTWkXDsZzBe0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/47SYw/btsQvQMBU9U/aLXYZkziotTWkXDsZzBe0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F47SYw%2FbtsQvQMBU9U%2FaLXYZkziotTWkXDsZzBe0k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;628&quot; height=&quot;848&quot; data-filename=&quot;ex.png&quot; data-origin-width=&quot;1210&quot; data-origin-height=&quot;848&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. REST API 특징&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. Uniform Interface(인터페이스 일관성)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;표준화된 인터페이스로 다양한 클라이언트에서 서버로 접근이 가능하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;URI로 지정한 자원에 대해 요청을 통일되게 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 HTTP 표준 프로토콜에 따르는 모든 플랫폼에서 사용이 가능하기 때문에 특정 언어나 기술에 종속되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. Stateless(무상태성)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버가 클라이언트 요청에 대한 상태를 저장하지 않는다. 그렇기 때문에 API 서버는 클라이언트의 요청만 단순 처리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트의 이전 요청이나 세션, 쿠키 정보를 별도로 저장 및 관리하지 않기 때문에 서비스의 자유도가 높아진다. (단순 처리만 하면 돼서 구현이 단순해진다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. Cacheble(캐시가능)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP 캐싱을 활용할 수 있어서 성능이 향상되고 대량의 요청을 효율적으로 처리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;*무상태성과의 차이라면 무상태성은 서버가 &lt;b&gt;클라이언트의 '상태'를 저장하지 않고&lt;/b&gt; 요청마다 독립적으로 처리하는 것이고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐시 가능은 &lt;b&gt;서버의 '응답' 데이터&lt;/b&gt;를 클라이언트나 중간 프록시 서버에서 저장해서 재사용 할 수 있는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. Server-Client(서버-클라이언트 구조)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자원이 있는 쪽이 서버, 요청하는 쪽이 클라이언트가 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버는 API를 제공하고 비즈니스 로직 처리 및 저장을 담당하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트는 사용자 인증이나 세션, 로그인 등을 직접 관리하고 책임진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런식으로 역할을 확실하게 분리해서 &lt;b&gt;서로간의 의존성을 줄인다&lt;/b&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5. Self-descriptiveness(자체 표현 구조)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;REST API 메시지만 보고도 어떤 자원에 어떤 요청을 하는지 쉽게 이해할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://aws.amazon.com/ko/what-is/restful-api/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://aws.amazon.com/ko/what-is/restful-api/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://junvelee.tistory.com/107&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://junvelee.tistory.com/107&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend/Vue.js, React</category>
      <category>API</category>
      <category>Rest</category>
      <category>REST API</category>
      <category>rest api http</category>
      <category>rest api 자원</category>
      <category>rest api 특징</category>
      <category>rest api란</category>
      <category>restful api</category>
      <author>갠디</author>
      <guid isPermaLink="true">https://abcdqbbq.tistory.com/117</guid>
      <comments>https://abcdqbbq.tistory.com/117#entry117comment</comments>
      <pubDate>Wed, 10 Sep 2025 23:32:26 +0900</pubDate>
    </item>
    <item>
      <title>Javascript (자바스크립트) - FormData() 객체</title>
      <link>https://abcdqbbq.tistory.com/116</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;FormData() 는 자바스크립트에서 HTML 폼 데이터를 쉽게 서버로 전송할 수 있게 해주는 내장 객체이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주로 파일 업로드 이미지 전송 등 multipart/form-data 방식이 필요한 상황에서 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 기본 문법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;new FormData()로 form 객체를 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 몇 가지 메소드를 사용해서 FormData를 다룰 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;append(key, value) : 데이터 추가&lt;/li&gt;
&lt;li&gt;set(key, value) : 기존 키가 있으면 덮어쓰고 없으면 새로 추가&lt;/li&gt;
&lt;li&gt;get(key) : key에 해당하는 첫 번째 값을 가져옴&lt;/li&gt;
&lt;li&gt;getAll(key) : key에 해당하는 모든 값을 배열로 가져옴&lt;/li&gt;
&lt;li&gt;delete(key) : 해당 key값의 필드를 삭제 (키, 밸류 모두 제거)&lt;/li&gt;
&lt;li&gt;has(key) : 해당 key값이 존재하는지를 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1757501075453&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const form = new FormData();

// append
form.append('file', testData);	// file 이라는 key를 가지고 testData를 가진 데이터를 추가

// set
form.set('file', testData2);	// file 이라는 key가 있으므로 file에 testData2 밸류로 덮어씀

// get
form.get('file');	// file 이라는 key의 value를 반환

// getAll
form.append('username', 'Chris');
form.append('username', 'Bob');
form.getAll('username'); // Returns [&quot;Chris&quot;, &quot;Bob&quot;] - username이라는 key를 가진 모든 value를 배열로 반환

//delete
form.get('file');	// file 이라는 key를 가진 필드를 제거

// has
form.has('file'); 	// file 이라는 key를 가졌는지 여부 확인 (file을 delete 했기 때문에 false 반환)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 전송&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fetch나 XMLHttpRequest 의 요청 바디에 FormData 객체를 넣으면 브라우저는 자동으로 적절한&amp;nbsp;Content-Type&amp;nbsp;헤더(multipart/form-data)를&amp;nbsp;지정하여&amp;nbsp;전송한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일이나 이미지 업로드 등을 바이너리 데이터 전송한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;바이너리 데이터&lt;/b&gt;&lt;br /&gt;0과 1로 이루어진 이진수 데이터로 이미지, 오디오, 동영상 등 데이터를 바이너리 형태로 저장. 전송시 base64로 인코딩해서 사용&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ko.javascript.info/formdata&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://ko.javascript.info/formdata&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-FormData-%EC%A0%95%EB%A6%AC-fetch-api&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-FormData-%EC%A0%95%EB%A6%AC-fetch-api&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend/Javascript</category>
      <author>갠디</author>
      <guid isPermaLink="true">https://abcdqbbq.tistory.com/116</guid>
      <comments>https://abcdqbbq.tistory.com/116#entry116comment</comments>
      <pubDate>Wed, 10 Sep 2025 19:56:48 +0900</pubDate>
    </item>
    <item>
      <title>CSR, SSR, 유니버셜 렌더링</title>
      <link>https://abcdqbbq.tistory.com/115</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;프론트엔드 프레임워크인 Vue나 React를 공부하다보면 많이 보게되는 개념이 CSR, SSR이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 용어는 웹 페이지가 어떻게 렌더링 되는지에 대한 방식을 얘기한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. CSR&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSR은 Client Side Rendering으로 가장 큰 특징으로 웹 페이지를 &lt;b&gt;클라이언트(브라우저)에서 렌더링&lt;/b&gt; 하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 웹 페이지를 이용할 때 클라이언트(브라우저)가 서버에 웹페이지 요청을 하면 서버는 HTML, CSS, Javascript 파일을 전달해주고 클라이언트에서는 서버에서 받은 해당 리소스로 직접 페이지를 그린다.(렌더링한다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트 --(웹 페이지 요청)---&amp;gt;&amp;nbsp; 서버&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;클라이언트&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; &amp;lt;---(html, css, js 리소스 전달)---&amp;nbsp; 서버&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 서버에서 내려준 response 값을 봐도 소스는 &amp;lt;div id=&quot;app&quot;&amp;gt; 과 같이 간단한 소스만 받아오는 것을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 SPA 프레임워크가 CSR에 해당한다. (Vue.js, React...)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 장점&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 초기에 페이지에 필요한 소스를 한 번에 렌더링 하기 때문에 &lt;b&gt;페이지 전환할 때 빠른 속도&lt;/b&gt;와 &lt;b&gt;뛰어난 사용자 경험&lt;/b&gt;을 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 대부분의 연산을 클라이언트에서 처리하기 때문에 &lt;b&gt;서버 부하가 감소&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 단점&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 장점 1에서 말했 듯이 초기에 페이지에 필요한 소스를 한 번에 렌더링 하기 때문에 &lt;b&gt;초기 로딩 시간이 느려질 수 있다&lt;/b&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 최초 HTML에 실제 페이지에 노출되는 소스가 아닌 &amp;lt;div id=&quot;app&quot;&amp;gt; 과 같이 렌더링을 위한 소스만 보여지기 때문에 &lt;b&gt;SEO에 불리&lt;/b&gt;하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. SSR&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSR은 Server Side Rendering으로 CSR과 다르게 웹 페이지를 &lt;b&gt;서버에서 렌더링&lt;/b&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 웹 페이지를 이용할 때 클라이언트를 통해 요청을 하면 서버에서 HTML을 다 렌더링한 소스를 클라이언트로 전달한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;클라이언트 --(웹 페이지 요청)---&amp;gt;&amp;nbsp; 서버&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;클라이언트&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;lt;---(서버에서 그린 html 소스 전달)---&amp;nbsp; 서버&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 서버에서 내려준 response 값에는 내가 요청한 html 소스가 담겨져서 받아온다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;CSR과 다르게 사용자가 페이지를 요청하면 요청을 할 때마다 HTML을 서버에서 그려서 클라이언트로 내려준다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;(CSR은 한 번에 내려준다.)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 장점&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1. 서버에서 최초 화면에 대한 HTML을 먼저 제공해주기 때문에 &lt;b&gt;초기 로딩 속도가 빠르다.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2. 최초 HTML에 실제 페이지에 노출되는 소스가 적용되어있기 때문에 &lt;b&gt;SEO에 유리&lt;/b&gt;하다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 단점&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1. CSR과 다르게 페이지를 한 번에 로드하는 것이 아닌 사용자의 &lt;b&gt;요청에 따라&lt;/b&gt; 필요한 데이터를 서버에서 가져오기 때문에 &lt;b&gt;사용자 경험이 떨어질 수&lt;/b&gt; 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2. 서버에 매번 요청을 하기 때문에 &lt;b&gt;서버 부하가 증가&lt;/b&gt;할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;3. 유니버셜 렌더링(Universal Rendering)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유니버셜 렌더링은 CSR과 SSR의 장점을 결합해서 사용하는 개념으로 Nuxt.js, Next.js를 통해 사용하는 것이 일반적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유니버셜 렌더링은 초기 화면을 서버에서 SSR로 렌더링 한 후에 사용자들의 페이지 상호작용은 클라이언트에서 CSR로 처리하는 하이브리드형 방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버에서 생성된 &lt;b&gt;정적인 HTML을&lt;/b&gt; 클라이언트에서 &lt;b&gt;동적이고 상호작용 가능한 애플리케이션으로 변환&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 사용자는 먼저 뿌려진 HTML을 보기 때문에 &lt;b&gt;초기 로딩 시간이 빠르다.&lt;/b&gt;&amp;nbsp;(자바스크립트가 로드되기 전에 컨텐츠를 본다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 검색엔진이 서버에서 완전히 뿌려진 HTML을 크롤링해서&lt;b&gt; SEO가 향상&lt;/b&gt; 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 클라이언트에서는 &lt;b&gt;하이드레이션&lt;/b&gt; 과정을 거쳐서 서버가 내려준 HTML을 스크립트로 제어할 수 있도록 처리해준다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend/Vue.js, React</category>
      <category>csr</category>
      <category>SSR</category>
      <category>vue 렌더링</category>
      <category>Vue.js</category>
      <category>서버사이드렌더링</category>
      <category>유니버셜 렌더링</category>
      <category>유니버셜렌더링</category>
      <category>클라이언트사이드렌더링</category>
      <author>갠디</author>
      <guid isPermaLink="true">https://abcdqbbq.tistory.com/115</guid>
      <comments>https://abcdqbbq.tistory.com/115#entry115comment</comments>
      <pubDate>Wed, 10 Sep 2025 09:59:04 +0900</pubDate>
    </item>
    <item>
      <title>Javascript (자바스크립트) 활용 - 바닐라 스크립트로 애니메이션 그리기(requestAnimationFrame)</title>
      <link>https://abcdqbbq.tistory.com/114</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;제이쿼리에서는 js로 애니메이션 효과를 구현하려면 편하게 .animate 메소드를 사용할 수 있었지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바닐라 js로 애니메이션을 구현하려고 하면 비교적 복잡한 코드로 구현이 필요하다는걸 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애니메이션 작동 원리를 생각해보면 일정한 시간 간격으로 특정 위치로 이동하게 되는 소스를 구현해야 하는데 일정 시간을 반복하는걸 생각해보면 setInterval이나 setTimeout 를 생각할 수 있는데 두 가지 방법 보다는 requestAnimationFrame이 브라우저 측면에서는 훨씬 효율적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. requestAnimationFrame 란?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;requestAnimationFrame는 (줄여서 RAF라고 부른다.) 브라우저에서 제공하는 메소드로 애니메이션과 같이 반복적인 작업을 수행할 때 사용한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;requestAnimationFrame의 특징을 설명하기 위해서는 setInterval, setTimeout 함수와 비교를 해보면 쉽게 알 수 있다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 98.6048%; height: 363px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px; text-align: center;&quot;&gt;setInterval / setTimeout&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px; text-align: center;&quot;&gt;requestAnimationFrame (RAF)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;타이머의 주기가 정확하지 않기 때문에 애니메이션이 &lt;b&gt;일정하지 않게 보이거나 끊겨&lt;/b&gt; 보일 수 있다.&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;브라우저의 &lt;b&gt;리페인트 주기&lt;/b&gt;에 맞게 콜백 함수를 예약하기 때문에 브라우저가 화면을 갱신할 때에 맞춰 애니메이션을 실행한다. 따라서 &lt;b&gt;부드러운 애니메이션&lt;/b&gt;을 볼 수 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 40px;&quot;&gt;&lt;span style=&quot;background-color: #efefef; color: #333333; text-align: start;&quot;&gt;고정된 타이머 주기를 사용해서 타이머 기능이 필요하지 않더라도 불필요하게 작업이 되거나&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;배터리 소모&lt;/b&gt;&lt;span style=&quot;background-color: #efefef; color: #333333; text-align: start;&quot;&gt;가 된다.&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 40px;&quot;&gt;브라우저에 최적화하여 애니메이션을 처리하기 때문에 사용자 디바이스의 &lt;b&gt;사양이나 배터리 수명 등을 최적화&lt;/b&gt; 하여 실행한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 60px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 60px;&quot;&gt;&lt;span style=&quot;background-color: #efefef; color: #333333; text-align: start;&quot;&gt;브라우저가 비활성된 상태에서 혹은 백그라운드에서 실행을 멈추지 않고 계속해서 작업을 실행하기 때문에&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;불필요한 리소스&lt;/b&gt;&lt;span style=&quot;background-color: #efefef; color: #333333; text-align: start;&quot;&gt;를 소비하게 된다.&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 60px;&quot;&gt;브라우저가 비활성된 상태이거나 백그라운드에서 실행을 할 때 &lt;b&gt;애니메이션 처리를 조정&lt;/b&gt;하여 &lt;b&gt;성능을 최적화&lt;/b&gt;한다.&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 60px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 60px;&quot;&gt;&lt;span style=&quot;background-color: #efefef; color: #333333; text-align: start;&quot;&gt;정해진 시간 간격에 따라 비동기적으로 작업을 예약하고 실행하기 때문에 다른 브라우저 작업과 &lt;b&gt;동기화가 보장되지 않을&lt;/b&gt; 수 있다.&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 60px;&quot;&gt;브라우저의 리페인트 주기에 맞게 콜백 함수를 실행하기 때문에 &lt;b&gt;다른 브라우저 작업과 동기화&lt;/b&gt; 되어 실행한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;requestAnimationFrame은 초가 60프레임 정도를 구현한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(브라우저 상황에 따라 다를 수 있다. 하지만 최대한 구현하려한다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. requestAnimationFrame&lt;span&gt;&amp;nbsp;사용&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;기본적인 사용은 아래와 같다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1711786006583&quot; class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;const callBackFnc = (timeStamp) =&amp;gt; {
	console.log(timeStamp);  // timeStamp 값이 시간에 따라 계속 증가한다.
}

// requestAnimationFrame(콜백함수);
requestAnimationFrame(callBackFnc);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;requestAnimationFrame(콜백함수) 로 사용하는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 사용하는 콜백함수는 첫 번째 파라미터 값으로 &lt;b&gt;타임 스탬프&lt;/b&gt;를 가져온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 그 타임 스탬프는 시간의 경과에 따라 증가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;requestAnimationFrame의 간단한 사용 예시를 보자면&lt;/p&gt;
&lt;pre id=&quot;code_1711783065868&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const testBtn = document.querySelector('.btn_test');
let testDuration = 0;

const scrollEvt = () =&amp;gt; {
  testDuration += 50;
  const rafScroll = requestAnimationFrame(scrollEvt);
	
  window.scrollTo(0, testDuration);
  if(testDuration &amp;gt; 500){
    cancelAnimationFrame(rafScroll);
    testDuration = 0;
  }
}
testBtn.addEventListener('click', scrollEvt);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드는 실무에서 쓰일 일은 없는 소스지만 간단하게 보자면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;testBtn 버튼을 누르면 브라우저의 스크롤이 내가 지정한 위치만큼 이동하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;window.scrollTo(0, 원하는 위치);&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로 지정을 하는데 testDuration이라는 반복되는 값을 만들어서 requestAnimationFrame 함수가 실행 될 때 마다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;50씩 증가를 시켜 스크롤 위치에 변화를 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 증가하는 testDuration 변수 값이 500이 넘어가게 되면 requestAnimationFrame 함수의 반대 성격의&amp;nbsp; &lt;b&gt;cancelAnimationframe()&lt;/b&gt; 함수를 사용해서 애니메이션을 멈출 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(버튼 클릭 -&amp;gt; 50씩 스크롤 위치 증가 -&amp;gt; 증가된 값이 500이 넘어가면 멈춤)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. requestAnimationFrame&lt;span&gt;&amp;nbsp;활용&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;실무에서 쓸법한 내용으로 예시를 들자면&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1711783637073&quot; class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;const duration = 500;  // 애니메이션 속도 조정
const moveEtc = (target) =&amp;gt; {
  const targetEl = document.querySelector(target);	// 함수를 실행할 때 목적지 엘리먼트를 받아옴

  if(targetEl){
    let start;
    const targetPosition = targetEl.getBoundingClientRect().top;  // 파라미터로 받아온 target의 위치값 구하기
    const startPosition = window.scrollY;	// 애니메이션 이벤트가 발생했을 때의 현재 위치 값
    const distance = targetPosition;
    const easings = {	// 애니메이션의 easing 효과
      linear(t) {
        return t;
      },
      easeInQuad(t) {
        return t * t;
      },
      easeOutQuad(t) {
        return t * (2 - t);
      },
      easeInOutQuad(t) {
        return t &amp;lt; 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
      },
      easeInCubic(t) {
        return t * t * t;
      },
      easeOutCubic(t) {
        return (--t) * t * t + 1;
      },
      easeInOutCubic(t) {
        return t &amp;lt; 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
      },
      easeInQuart(t) {
        return t * t * t * t;
      },
      easeOutQuart(t) {
        return 1 - (--t) * t * t * t;
      },
      easeInOutQuart(t) {
        return t &amp;lt; 0.5 ? 8 * t * t * t * t : 1 - 8 * (--t) * t * t * t;
      },
      easeInQuint(t) {
        return t * t * t * t * t;
      },
      easeOutQuint(t) {
        return 1 + (--t) * t * t * t * t;
      },
      easeInOutQuint(t) {
        return t &amp;lt; 0.5 ? 16 * t * t * t * t * t : 1 + 16 * (--t) * t * t * t * t;
      }
    };
    const moveScroll = function(timeStamp){  // requestAnimationFrame를 적용할 함수, 첫 번째 파라미터로 타임 스탬프 받아오기
      if(start === undefined){
        start = timeStamp;  // requestAnimationFrame 함수의 타임스탬프를 기준으로 시작 시간을 지정
      }

      const elapsed = timeStamp - start;  // 시작 시간, 타임 스탬프를 이용해서 애니메이션 경과 시간을 구한다.
      const targetPos = startPosition + distance * easings.linear(elapsed / duration);  // 현재 위치, 목표 위치, easing 등으로 최종 목적지까지의 애니메이션 형태를 구성
      window.scrollTo(0, targetPos);

      if(elapsed &amp;lt;= duration){
        requestAnimationFrame(moveScroll);
      }else if(timeStamp &amp;gt;= elapsed){
        // 애니메이션 종료 후
        const targetPosFix = window.scrollY + targetEl.getBoundingClientRect().top;
        window.scrollTo(0, targetPosFix);
      }
    };
    requestAnimationFrame(moveScroll);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 작성할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;requestAnimationFrame(콜백함수)의 콜백함수는 첫 번째 파라미터로 받아온 타임 스탬프 값(timeStamp)을 이용해서 애니메이션 시작점, 목적지, 애니메이션의 경과 시간 등을 계산한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/API/window/requestAnimationFrame&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://developer.mozilla.org/ko/docs/Web/API/window/requestAnimationFrame&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://codepen.io/pawelgrzybek/pen/ZeomJB&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://codepen.io/pawelgrzybek/pen/ZeomJB&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@y_jem/react-%EC%8A%AC%EB%A1%AF-%EC%B9%B4%EC%9A%B4%ED%8A%B8-%EA%B8%B0%EB%8A%A5&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@y_jem/react-%EC%8A%AC%EB%A1%AF-%EC%B9%B4%EC%9A%B4%ED%8A%B8-%EA%B8%B0%EB%8A%A5&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend/Javascript</category>
      <category>javascript scroll animate</category>
      <category>requestAnimationFrame</category>
      <category>requestAnimationFrame setInterval</category>
      <category>requestAnimationFrame 애니메이션</category>
      <category>requestAnimationFrame 예제</category>
      <category>requestAnimationFrame 특징</category>
      <category>vanila js scroll</category>
      <category>바닐라 js 스크롤</category>
      <category>바닐라 스크립트 애니메이션</category>
      <category>자바스크립트 스크롤 애니메이션</category>
      <author>갠디</author>
      <guid isPermaLink="true">https://abcdqbbq.tistory.com/114</guid>
      <comments>https://abcdqbbq.tistory.com/114#entry114comment</comments>
      <pubDate>Sat, 30 Mar 2024 17:11:07 +0900</pubDate>
    </item>
    <item>
      <title>Javascript (자바스크립트) - script defer, async (스크립트 파일 가져오기)</title>
      <link>https://abcdqbbq.tistory.com/113</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;브라우저는 html 파일을 상단 head 태그의 내용부터 순차적으로 파싱을 하게된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 자바스크립트를 css를 추가하는 것 처럼 head 태그 안에서 사용하게 되면 문제가 발생하게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1708354715622&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!doctype html&amp;gt;
&amp;lt;html lang=&quot;ko&quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;script&amp;gt;
      const btn = document.querySelector('#btn');
      btn.addEventListener('click', function(){
        console.log('Hello World');  // undefined btn 에러 출력
      })
    &amp;lt;/script&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;button id=&quot;btn&quot;&amp;gt;버튼&amp;lt;/button&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 소스로 보았을 때 브라우저는 head태그에 있는 script를 먼저 파싱하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;body 태그 안에 있는 DOM 요소를 파싱하기 때문에&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;id가 btn인 button 태그를 불러오기 전에 스크립트로 해당 태그를 불러오려고 했으니 undefined 에러가 출력이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 이슈를 해결하기 위해서 몇 가지 방법이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. &amp;lt;/body&amp;gt; 태그 상단에 스크립트 작성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 스크립트 태그는 &amp;lt;/body&amp;gt; 태그 위에 위치해서 사용하는 경우가 많은데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;습관적으로 사용해서 의식을 못할 수도 있지만 DOM 파싱 순서 이슈 때문에 사용하게 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1708355026650&quot; class=&quot;html xml&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;&amp;lt;!doctype html&amp;gt;
&amp;lt;html lang=&quot;ko&quot;&amp;gt;
  &amp;lt;head&amp;gt;&amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;button id=&quot;btn&quot;&amp;gt;버튼&amp;lt;/button&amp;gt;
    
    &amp;lt;script&amp;gt;
      const btn = document.querySelector('#btn');
      btn.addEventListener('click', function(){
        console.log('Hello World');  // 콘솔창에 'Hello World' 정상 출력
      })
    &amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. load 이벤트 리스너 사용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;window.onload나 DOMContentLoaded 이벤트 리스너를 사용해서 HTML이 파싱된 후에 스크립트를 실행할 수 있도록 처리할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;window.onload : HTML 파싱으로 DOM 생성, 외부 콘텐츠(이미지, css, script 등) 모든 요소가 로드된 후 발생하는 이벤트&lt;/li&gt;
&lt;li&gt;DOMContentLoaded : HTML 파싱으로 DOM 생성만 한 후 발생하는 이벤트&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;*window.onload&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 요소를 다 로드한 후 스크립트를 뿌려주다보니 페이지 내에 요소가 많을 경우 스크립트를 실행하기까지 초기 로딩 시간이 길어질 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1708355362885&quot; class=&quot;html xml&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;&amp;lt;!doctype html&amp;gt;
&amp;lt;html lang=&quot;ko&quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;script&amp;gt;
      window.onload = function(){
        const btn = document.querySelector('#btn');
        btn.addEventListener('click', function(){
          console.log('Hello World');
        })
      }
    &amp;lt;/script&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;button id=&quot;btn&quot;&amp;gt;버튼&amp;lt;/button&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;DOMContentLoaded&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span&gt;HTML DOM만 생성되고 스크립트를 실행하다보니 window.onload 보다 빠르게 실행된다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1708355466531&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!doctype html&amp;gt;
&amp;lt;html lang=&quot;ko&quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;script&amp;gt;
      documnet.addEventListener('DOMContentLoaded', function(){
        const btn = document.querySelector('#btn');
        btn.addEventListener('click', function(){
          console.log('Hello World');
        })
      });
    &amp;lt;/script&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;button id=&quot;btn&quot;&amp;gt;버튼&amp;lt;/button&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. defer, async 속성으로 script 로드&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML5에 추가된 속성으로 script태그에 defer, async 속성을 추가해서 사용한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;defer : HTML 파싱과 함께 비동기로 script를 불러온다.(script 파일은 가져오지만 &lt;b&gt;바로 실행하지 않는다&lt;/b&gt;.)&lt;/li&gt;
&lt;li&gt;async : HTML 파싱과 함께 비동기로 script를 불러온다.(script 파일을 가져오면서 &lt;b&gt;바로 실행한다&lt;/b&gt;.)&lt;/li&gt;
&lt;/ul&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666; text-align: start;&quot;&gt;*비동기: 브라우저가 HTML 파싱을 하면서 스크립트 태그를 만났을 때하던 작업 (HTML 파싱)을 멈추지 않고 script 파일을 병렬적으로 가져온다.&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;defer 속성은 HTML을 파싱하면서 script 파일만 불러오고 실행은 나중에 하지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;async 속성은 HTML을 파싱하면서 script 파일을 불러옴과 동시에 실행을 같이 하고 실행하는 순간에는 HTML 파싱을 일시 정지하기 때문에 경우에 따라 에러가 발생할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기 때문에 대부분의 경우에는 defer 속성을 사용하고 async는 필요에 따라 사용하면 좋을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;wfL82.png&quot; data-origin-width=&quot;801&quot; data-origin-height=&quot;814&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4POpO/btsE0HN9Il4/pdvbzXOg1StCcUikAGZRD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4POpO/btsE0HN9Il4/pdvbzXOg1StCcUikAGZRD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4POpO/btsE0HN9Il4/pdvbzXOg1StCcUikAGZRD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4POpO%2FbtsE0HN9Il4%2FpdvbzXOg1StCcUikAGZRD0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;801&quot; height=&quot;814&quot; data-filename=&quot;wfL82.png&quot; data-origin-width=&quot;801&quot; data-origin-height=&quot;814&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=7qVc4Ez0fnY&amp;amp;list=PLlaP-jSd-nK9LiA2n07uBhzOn9wI53xGV&amp;amp;index=4&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.youtube.com/watch?v=7qVc4Ez0fnY&amp;amp;list=PLlaP-jSd-nK9LiA2n07uBhzOn9wI53xGV&amp;amp;index=4&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend/Javascript</category>
      <category>defer async</category>
      <category>html undefined 에러</category>
      <category>script async</category>
      <category>script defer</category>
      <category>script defer async</category>
      <category>script defer 속성</category>
      <category>script undefined</category>
      <category>script태그</category>
      <category>script태그 async</category>
      <category>script태그 defer</category>
      <author>갠디</author>
      <guid isPermaLink="true">https://abcdqbbq.tistory.com/113</guid>
      <comments>https://abcdqbbq.tistory.com/113#entry113comment</comments>
      <pubDate>Tue, 20 Feb 2024 00:25:44 +0900</pubDate>
    </item>
    <item>
      <title>Javascript (자바스크립트) 활용 - 타이팅 효과(텍스트 입력)</title>
      <link>https://abcdqbbq.tistory.com/112</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;특정한 영역에서 텍스트 효과를 눈에 띄게 보이게 하기 위해서 텍스트가 입력되는 형태의 타이핑 효과를 자바스크립트를 이용해서 구현할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 스크립트 작성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구글링을 해보면 한 줄/여러 줄일 경우를 나눠서 타이핑 효과 스크립트를 다르게 작성하는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업을 하다보면 한 줄 혹은 여러 줄로 수정이 많이 발생할 수 있기 때문에 여러 줄 기준으로 스크립트를 작성해놓고 그 때 그 때 맞춰서 텍스트만 수정해주는게 좋아 여러 줄 기준으로 스크립트를 작성했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;아래 소스의 변수들을 간략히 설명하면&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- txtString : 내가 작성할 문자열 (두 줄로 입력할 경우 문자열에 \n 기호 입력)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- txtSpeed : 타이핑 속도를 조절할 숫자&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- txtDelay : 타이핑 완료 후 새로 입력되는데까지 대기 시간&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- txtIndex : 타이핑 되는 현재의 문자열 index&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- *typeControl&lt;/b&gt; : 타이핑 완료 후에 타이핑을 멈추게 하기 위해 on/off 기능을 위한 변수&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;- *txtNow&lt;/b&gt; : txtIndex를 setInterval을 돌리면서 하나씩 증가시켜&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;txtString에 작성했던 문자열을&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;하나씩 받아온다. 추가로 받아오는 문자열에 \n (줄바꿈) 특수기호가 있으면 &amp;lt;br&amp;gt; 태그를 입력해준다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1699866890784&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const txtWrap = document.querySelector('.typing');
const txtString = '안녕하세요! \n Hello World!';
const txtSpeed = 300;
const txtDelay = 2000;
let txtIndex = 0;
let typeCotrol = true;

function typingEvent(){
    if(typeCotrol === true){  // true일 경우 타이핑 진행, false일 경우 타이핑 중지
        let txtNow = txtString[txtIndex++];
        txtWrap.innerHTML += txtNow === &quot;\n&quot; ? &quot;&amp;lt;br&amp;gt;&quot;: txtNow;
        if(txtIndex &amp;gt;= txtString.length){  // 문자열 index와 문자열 length를 비교해 문자열 마지막을 판별
            txtIndex = 0;  // 문자 입력을 다 하면 index를 0으로 초기화
            typeCotrol = false;  // 입력 완료시 typeControl을 false로 바꿔 입력 막기
        }
    }else{
        // typeControl이 false일 경우
        clearInterval(setTyping);  // setInterval 제거 (타이핑 멈춤)
        setTimeout(function(){
            txtWrap.innerHTML = '';
            typeCotrol = true;
            setTyping = setInterval(typingEvent, txtSpeed);  // txtDelay만큼 시간 후 재 입력
        }, txtDelay)
    }
}

let setTyping = setInterval(typingEvent, txtSpeed);  // setInterval을 통한 텍스트 입력&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 setInterval을 통해 인덱스를 증가시켜 변수로 담아놓을 문자열을 하나씩 받아온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 주의해야할 점은&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;txtWrap.innerHTML = 문자열&lt;/b&gt;이 아니라&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;b&gt;txtWrap.innerHTML&lt;/b&gt;&lt;span&gt;&lt;b&gt; += 문자열&lt;/b&gt; 로 작성해야 문자열이 하나씩 추가가 된다. (+=)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. CSS 작성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순 텍스트 입력만 구현하는 것 보다 실제 텍스트를 입력하는 것 같이 깜빡이는 커서 효과도 줄 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1699868617986&quot; class=&quot;css&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;css&quot;&gt;&lt;code&gt;.typing:after  {
  display:inline-block;
  margin-left:2px;
  vertical-align:middle;
  content:'';
  background:#000;
  width:2px;
  height:22px;
  animation:blink infinite 1s ease;
}

@keyframes blink {
  0%	{opacity:0;}
  100%	{opacity:1;}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 가상 선택자 after를 사용해서 임의의 바(bar)를 만들고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;animation을 사용해서 깜빡임 효과를 주면 보다 리얼하게 텍스트 입력 효과를 만들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시 화면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;codepen&quot; data-height=&quot;300&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;wvNeYGr&quot; data-user=&quot;hyunjin-k&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot;&gt;
  &lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/hyunjin-k/pen/wvNeYGr&quot;&gt;
  Typing&lt;/a&gt; by KK (&lt;a href=&quot;https://codepen.io/hyunjin-k&quot;&gt;@hyunjin-k&lt;/a&gt;)
  on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;
&lt;/p&gt;
&lt;script async src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend/Javascript</category>
      <category>javascript typing</category>
      <category>javascript 타이핑</category>
      <category>javascript 텍스트 입력 효과</category>
      <category>자바스크립트 타이핑</category>
      <category>자바스크립트 타이핑 텍스트</category>
      <category>자바스크립트 텍스트 입력 효과</category>
      <category>자바스크립트 텍스트 타이핑</category>
      <category>자바스크립트 텍스트 효과</category>
      <category>타이핑 텍스트</category>
      <category>텍스트 타이핑</category>
      <author>갠디</author>
      <guid isPermaLink="true">https://abcdqbbq.tistory.com/112</guid>
      <comments>https://abcdqbbq.tistory.com/112#entry112comment</comments>
      <pubDate>Mon, 13 Nov 2023 18:48:15 +0900</pubDate>
    </item>
    <item>
      <title>[vue 2 기초] Axios (액시오스)</title>
      <link>https://abcdqbbq.tistory.com/111</link>
      <description>&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Axios(액시오스)는 뷰에서 권고하고 있는 HTTP 통신 라이브러리 이고&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Promise 기반이다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;*Promise : 자바스크립트 비동기 처리 패턴&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP 통신 라이브러리인 Axios를 알아보기 전에 간단하게 HTTP 통신 구조를 알아보려한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. HTTP 통신 구조 (클라이언트 &amp;lt;-&amp;gt; 서버)&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;http.jpg&quot; data-origin-width=&quot;771&quot; data-origin-height=&quot;476&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbsY5p/btszTz2cwfr/iCeHTfNegOtIU8QMAGLRI1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbsY5p/btszTz2cwfr/iCeHTfNegOtIU8QMAGLRI1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbsY5p/btszTz2cwfr/iCeHTfNegOtIU8QMAGLRI1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbsY5p%2FbtszTz2cwfr%2FiCeHTfNegOtIU8QMAGLRI1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;771&quot; height=&quot;476&quot; data-filename=&quot;http.jpg&quot; data-origin-width=&quot;771&quot; data-origin-height=&quot;476&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot;&gt;브라우저 (클라이언트)와 서버에서 HTTP 통신 방법을 간단하게 정리하자면&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;브라우저(클라이언트)에서 HTTP Requset를 통해 서버의 특정 백엔드 로직을 실행하고&lt;/li&gt;
&lt;li&gt;백엔드 로직을 통해 DB에서 로직에 맞는 데이터를 가져온다.&lt;/li&gt;
&lt;li&gt;가져온 데이터를 또 다시 특정 백엔드 로직을 통해 HTTP Response로 브라우저(클라이언트)에 응답을 한다.&lt;/li&gt;
&lt;li&gt;HTTP Response를 통해 가져온 데이터를 브라우저에 뿌려준다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot;&gt;위와 같이 HTTP 통신 구조를 간략하게 정리할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Axios 적용&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;CDN 방식 or NPM 방식으로 axios 라이브러리를 설치한 뒤에&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-macro-name=&quot;code&quot; data-hasbody=&quot;true&quot;&gt;
&lt;div style=&quot;background-color: #ffffff; color: #333333; text-align: left;&quot;&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1699344452013&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;div id=&quot;app&quot;&amp;gt;
    &amp;lt;button v-on:click=&quot;getData&quot;&amp;gt;get user&amp;lt;/button&amp;gt;&amp;lt;!-- 버튼 클릭시 vue 인스턴스에 저장해놓은 getData 메소드를 호출한다. --&amp;gt;
    &amp;lt;div&amp;gt; {{ users }} &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
 
&amp;lt;script src=&quot;https://cdn.jsdelivr.net/npm/vue/dist/vue.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;https://unpkg.com/axios/dist/axios.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script&amp;gt;
    new Vue({
        el: '#app',
        data: {
            users: []
        },
        methods: {
            getData: function(){
                var vm = this;
                axios.get('https://jsonplaceholder.typicode.com/users/')    // https://jsonplaceholder.typicode.com/users/ 에서 데이터 받아오기
                    .then(function(response){
                        console.log(response.data);     // response에서 data 속성 값 출력
                        // this.users = response.data;  // 함수 안에 this로 해당 this는 vue 인스턴스에 대한 this가 window를 가르킨다.
                        vm = response.data;             // getData 함수 내부에 this를 따로 변수에 담아서 사용
                    }.catch(function(){
                        console.log(error);
                    });
            }
        }
    });
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;vue 인스턴스 methods에 axios를 사용하기 위한 함수를 등록해준다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;axios.get() : 데이터 가져오기&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;axios.then() : 비동기 통신이 성공했을 경우 처리&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;axios.catch() : 통신시 오류가 발생했을 때의 처리&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;위의 소스와 같이 작성할 때 주의해야 할 점은 vue 인스턴스 내부에 있는 데이터에 접근하기 위해 this를 사용하게 되는데&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;함수 내에서 this를 사용하게되면 함수 내부에서 this를 가르키게 되어 this는 윈도우를 가르키게 된다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그렇게 때문에 getData 메소드 내부에 this를 별도 변수에 담아서 사용하거나&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;then에서 function(){} 이 아닌 =&amp;gt; 화살표 함수 방식으로 작성하여 this가 원하는 방향으로 지정되도록 처리할 필요가 있다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;출처&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;인프런 'Vue.js 시작하기 - Age of Vue.js' 강좌&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend/Vue.js, React</category>
      <category>axios</category>
      <category>axios 특징</category>
      <category>vue axios</category>
      <category>vue http</category>
      <category>vue 통신</category>
      <category>Vue.js</category>
      <category>vue.js axios</category>
      <category>vue.js http 통신</category>
      <category>뷰 axios</category>
      <category>뷰js axois</category>
      <author>갠디</author>
      <guid isPermaLink="true">https://abcdqbbq.tistory.com/111</guid>
      <comments>https://abcdqbbq.tistory.com/111#entry111comment</comments>
      <pubDate>Tue, 7 Nov 2023 19:09:19 +0900</pubDate>
    </item>
    <item>
      <title>[vue 2 기초] 라우터</title>
      <link>https://abcdqbbq.tistory.com/110</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;라우터는 뷰 라이브러리를 이용하여 SPA(Single Page Appliaction) 구현을 할 때 사용하는 라이브러리이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 라우터 연결&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;라우터를 사용하기 위해서는 CDN 방식 혹은 NPM 방식으로 설치할 수 있다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;CDN 방식은 script 태그에 CDN 링크를 추가해서 연결을 하는 방식이고&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;NPM 방식은 Vue CLI로 프로젝트를 생성하거나 NPM 기반으로 프로젝트를 생성해서 라우터 설치 명령어 입력을 통해 설치한다.&lt;/p&gt;
&lt;pre id=&quot;code_1699259178751&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!-- CDN 방식 --&amp;gt;
&amp;lt;script src=&quot;https://unpkg.com/vue@2&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;https://unpkg.com/vue-router@3&quot;&amp;gt;&amp;lt;/script&amp;gt;
  
&amp;lt;!-- NPM 방식(아래 명령어 입력) --&amp;gt;
npm install vue-router@3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot;&gt;위와 같이 설치한 뒤 아래 소스와 같이 뷰 라우터를 등록할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1699259327478&quot; class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;var router = new VueRouter({
    // 라우터 옵션
});
 
// 인스턴스에 라우터 인스턴스를 등록
new Vue({
    router: router,     //  라우터 인스턴스의 변수명으로 등록
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 라우터 옵션&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;라우터를 연결 및 등록한 뒤 라우터를 사용할 때 라우터 옵션을 지정한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;2가지의 필수 옵션이 있다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- routes:&amp;nbsp;라우팅&amp;nbsp;할&amp;nbsp;URL과&amp;nbsp;컴포넌트&amp;nbsp;값&amp;nbsp;지정(페이지의&amp;nbsp;라우팅&amp;nbsp;정보)&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- mode:&amp;nbsp;URL의&amp;nbsp;해쉬&amp;nbsp;값&amp;nbsp;제거&amp;nbsp;속성&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1699259382854&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var LoginComponent = {
    template: '&amp;lt;div&amp;gt;Login&amp;lt;/div&amp;gt;'
}
var MainComponent = {
    template: '&amp;lt;div&amp;gt;Main&amp;lt;/div&amp;gt;'
}
 
var router = new VueRouter({
    mode: 'history',
    routes: [   // 배열 형식으로 지정
        {
            // 페이지의  url 이름
            path:'/login',
            // 해당 url에서 표시될 컴포넌트
            component: LoginComponent,
        },
        {
            path: '/main',
            component: MainComponent,
        }
    ]
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot;&gt;위와 같이 라우터 옵션들을 입력해서 특정 컴포넌트들을 세부 페이지처럼 사용할 수 있게 url을 지정하고 연결해준다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. router-view&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot;&gt;라우터 옵션으로 라우터를 컴포넌트와 연결하고 화면에 해당 컴포넌트를 뿌릴 때 router-view를 사용한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1699259411920&quot; class=&quot;html xml&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;&amp;lt;div id=&quot;app&quot;&amp;gt;
    &amp;lt;router-view&amp;gt;&amp;lt;/router-view&amp;gt;&amp;lt;!-- 라우터에 등록한 LoginComponent 혹은 MainComponent를 화면에 보여준다 --&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;localhost:0000/router.html&lt;b&gt;/login&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;으로 url을 입력시 LoginComponent 내용이,&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;localhost:0000/router.html&lt;b&gt;/main&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;으로 url을 입력시 MainComponent 내용이 &amp;lt;router-view&amp;gt; 영역에 뿌려진다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;4. router-link&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;사용자들이 일반적인 웹 페이지에서 a태그를 통해 링크 이동을 하여 페이지를 탐색하듯&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;vue 화면에서 특정 링크를 클릭해서 화면을 이동할 수 있게 해주는 것이 router-link 이다. (실제 html 소스상에 &amp;lt;router-link&amp;gt; 는 a태그로 출력된다.)&lt;/p&gt;
&lt;pre id=&quot;code_1699259455085&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;div id=&quot;app&quot;&amp;gt;
    &amp;lt;!-- &amp;lt;router-link to=&quot;이동할 URL&quot;&amp;gt;&amp;lt;/router-link&amp;gt; --&amp;gt;
    &amp;lt;router-link to=&quot;/login&quot;&amp;gt;로그인&amp;lt;/router-link&amp;gt;
    &amp;lt;router-link to=&quot;/main&quot;&amp;gt;메인&amp;lt;/router-link&amp;gt;
 
    &amp;lt;router-view&amp;gt;&amp;lt;/router-view&amp;gt; &amp;lt;!-- routerlink를 클릭할 때마다 to에 연결된 url의 화면이 출력된다. --&amp;gt;
&amp;lt;/div&amp;gt;
 
&amp;lt;!-- 출력
&amp;lt;a href=&quot;/login&quot;&amp;gt;로그인&amp;lt;/a&amp;gt;
&amp;lt;a href=&quot;/main&quot;&amp;gt;메인&amp;lt;/a&amp;gt;
--&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출처&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot;&gt;인프런 'Vue.js 시작하기 - Age of Vue.js' 강좌&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend/Vue.js, React</category>
      <category>router</category>
      <category>router-link</category>
      <category>router-view</category>
      <category>vue 라우터</category>
      <category>vue.js router-link</category>
      <category>vue.js router-view</category>
      <category>vue.js 라우터</category>
      <category>라우터</category>
      <category>뷰 라우터</category>
      <category>뷰 라우터 옵션</category>
      <author>갠디</author>
      <guid isPermaLink="true">https://abcdqbbq.tistory.com/110</guid>
      <comments>https://abcdqbbq.tistory.com/110#entry110comment</comments>
      <pubDate>Mon, 6 Nov 2023 18:50:51 +0900</pubDate>
    </item>
    <item>
      <title>[vue 2 기초] 컴포넌트 통신(props, event emit)</title>
      <link>https://abcdqbbq.tistory.com/109</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;컴포넌트의 데이터는 각 컴포넌트에서 관리하기 때문에 각 컴포넌트는 고유한 데이터 유효범위를 갖는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 컴포넌트간 데이터를 주고 받기 위해서는 특별한 규칙을 따라야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;component-communication.2bb1d838.png&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;266&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSp0WC/btszHG1FIjE/v7RIesyjv4eKZSN6aw52Sk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSp0WC/btszHG1FIjE/v7RIesyjv4eKZSN6aw52Sk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSp0WC/btszHG1FIjE/v7RIesyjv4eKZSN6aw52Sk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSp0WC%2FbtszHG1FIjE%2Fv7RIesyjv4eKZSN6aw52Sk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;266&quot; data-filename=&quot;component-communication.2bb1d838.png&quot; data-origin-width=&quot;300&quot; data-origin-height=&quot;266&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;위와 같이 상&amp;rarr;하로 props를 통해 데이터를 전달하고&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하&amp;rarr;상은 이벤트를 올려서 전달한다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;*컴포넌트 통신 규칙 : 직접 연관이 되어있는 상, 하위 컴포넌트 간의 데이터 전달이 가능하다. (데이터 오류 발생시 추적에 용이)&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;component.jpg&quot; data-origin-width=&quot;798&quot; data-origin-height=&quot;371&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1mVOf/btszKIEgM9R/ahdQ0BaHlKGLP8GoWvgKM1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1mVOf/btszKIEgM9R/ahdQ0BaHlKGLP8GoWvgKM1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1mVOf/btszKIEgM9R/ahdQ0BaHlKGLP8GoWvgKM1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1mVOf%2FbtszKIEgM9R%2FahdQ0BaHlKGLP8GoWvgKM1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;798&quot; height=&quot;371&quot; data-filename=&quot;component.jpg&quot; data-origin-width=&quot;798&quot; data-origin-height=&quot;371&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. props (상 -&amp;gt; 하)&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;props는 위에서 설명한 것과 같이 상위 컴포넌트에서 하위 컴포넌트로 데이터를 전달하는 방법이다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;부모 컴포넌트에 등록되어 있는 데이터를 가져오는 기본적인 사용 방법은&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1. 자식(하위) 컴포넌트에 props 속성을 사용해 프롭스 속성명을 등록해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1699065324315&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;var appHeader = {
    template: '&amp;lt;h1&amp;gt;Title&amp;lt;/h1&amp;gt;',
    props: ['propsdata']    // 프롭스 속성 명은 임의로 지정이 가능하다.
}
 
new Vue({
    el: '#app',
    component: {
        'app-header' : appHeader,
    },
    data: {
        message: 'hi',
    }
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot;&gt;2. 자식 컴포넌트에서 v-bind를 사용해서 상위 컴포넌트의 데이터 속성을 해당하는 자식 컴포넌트로 전달한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1699065336123&quot; class=&quot;html xml&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;&amp;lt;!-- v-bind:프롭스 명=&quot;상위 컴포넌트의 data 속성&quot; --&amp;gt;
&amp;lt;app-header v-bind:propsdata=&quot;message&quot;&amp;gt;&amp;lt;/app-header&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot;&gt;크게 위 1, 2번의 방법으로 적용할 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1699065397314&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;div id=&quot;app&quot;&amp;gt;
    &amp;lt;app-header v-bind:propsdata=&quot;message&quot;&amp;gt;&amp;lt;/app-header&amp;gt;
    &amp;lt;app-content v-bind:propsnum=&quot;num&quot;&amp;gt;&amp;lt;/app-content&amp;gt;
&amp;lt;/div&amp;gt;
 
&amp;lt;script src=&quot;https://cdn.jsdelivr.net/npm/vue/dist/vue.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script&amp;gt;
    var appHeader = {
        template: '&amp;lt;h1&amp;gt;{{ propsdata }}&amp;lt;/h1&amp;gt;',   // {{ 데이터 }} 이중 괄호를 통해 부모 컴포넌트로부터 받은 props 값을 그대로 뿌려줄 수 있다.
        props: ['propsdata'],
    }
    var appContent = {
        // 각 영역 안에 데이터는 고유의 데이터로 가진다. 따라서 appHeader와 마찬가지로 propsdata라는 같은 이름으로 사용해도 된다.
        template: '&amp;lt;div&amp;gt;{{ propsnum }}&amp;lt;/div&amp;gt;',
        props: ['propsnum'],
    }
    new Vue({
        el: '#app',
        components: {
            'app-header' : appHeader,
            'app-content' : appContent,
        },
        data: {
            message: 'hi',
            num: 10
        }
    });
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Event Emit (하 -&amp;gt; 상)&lt;/h3&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;부모 컴포넌트의 데이터를 가져올 때 props 속성을 사용한다면&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;자식 컴포넌트에서는 이벤트 발생(emit)을 통해 상위로 이벤트를 전달해준다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;방법은&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1. 자식 컴포넌트에 event emit 메소드를 등록해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1699065441437&quot; class=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;// appHeader에 있는 button을 클릭하면 passEvent 함수가 실행되고 그 함수는 'pass'라는 이벤트명을 가진 event emit용 함수이다.
var appHeader = {
    template: '&amp;lt;button v-on:click=&quot;passEvent&quot;&amp;gt;click me&amp;lt;/button&amp;gt;',
    methods: {
        passEvent: function(){  //passEvent라는 메소드를 통해 해당 자식 컴포넌트에 $emit 이벤트명은 정의한다.
            this.$emit('pass'); // 이벤트 이름 등록 : $emit('이벤트명')
        }
    }
}
 
new Vue({
    el: '#app',
    components: {
        'app-header': appHeader,
    },
    methods: {
        logText: function(){
            console.log('hi');
        },
    }
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot;&gt;2. 자식 컴포넌트에 $emit으로 등록한 이벤트 명을 통해 부모 컴포넌트의 메소드를 가져온다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1699065458938&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!-- v:on:하위 컴포넌트에서 발생한 이벤트 이름=&quot;상위 컴포넌트의 메소드 이름&quot; --&amp;gt;
&amp;lt;app-header v-on:pass=&quot;logText&quot;&amp;gt;&amp;lt;/app-header&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;위와 같이 작업하면 app-header 영역에 버튼이 생기고&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;해당 버튼을 클릭하면 자식 요소의 버튼을 클릭하지만 부모 요소에 logText() 메소드가 실행된다.&lt;/p&gt;
&lt;pre id=&quot;code_1699065486960&quot; class=&quot;html xml&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;&amp;lt;div id=&quot;app&quot;&amp;gt;
    &amp;lt;!-- v:on:하위 컴포넌트에서 발생한 이벤트 이름=&quot;상위 컴포넌트의 메소드 이름&quot; --&amp;gt;
    &amp;lt;app-header v-on:pass=&quot;logText&quot;&amp;gt;&amp;lt;/app-header&amp;gt;
    &amp;lt;app-content v-on:number=&quot;addNum&quot;&amp;gt;&amp;lt;/app-content&amp;gt;
&amp;lt;/div&amp;gt;
 
&amp;lt;script src=&quot;https://cdn.jsdelivr.net/npm/vue/dist/vue.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script&amp;gt;
    var appHeader = {
        template: '&amp;lt;button v-on:click=&quot;passEvent&quot;&amp;gt;click me&amp;lt;/button&amp;gt;',
        methods: {
            passEvent: function(){
                this.$emit('pass');
            }
        }
    }
    var appContent = {
        template: '&amp;lt;button v-on:click=&quot;addNumber&quot;&amp;gt;add&amp;lt;/button&amp;gt;',
        methods: {
            addNumber: function(){
                this.$emit('number');
            }
        }
    }
    new Vue({
        el: '#app',
        components: {
            'app-header': appHeader,
            'app-content': appContent,
        },
        methods: {
            logText: function(){
                console.log('hi');
            },
            addNum: function(){
                this.num++
                console.log(this.num);
            }
        },
        data: {
            num: 10,
        }
    })
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot;&gt;위와 같은 소스로 자식 컴포넌트에서의 이벤트로 부모 컴포넌트의 data 값을 변경할 수도 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 같은 레벨에서의 컴포넌트 통신 방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot;&gt;같은 레벨의 컴포넌트에서는 다이렉트로 데이터를 통신할 수 없고 연결되어 있는 부모 컴포넌트를 통해서 데이터를 주고받을 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;same-level.jpg&quot; data-origin-width=&quot;563&quot; data-origin-height=&quot;260&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uUCDD/btszLuS0Sw3/FbK9bJdguGJWaCBgQGJ5SK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uUCDD/btszLuS0Sw3/FbK9bJdguGJWaCBgQGJ5SK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uUCDD/btszLuS0Sw3/FbK9bJdguGJWaCBgQGJ5SK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuUCDD%2FbtszLuS0Sw3%2FFbK9bJdguGJWaCBgQGJ5SK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;563&quot; height=&quot;260&quot; data-filename=&quot;same-level.jpg&quot; data-origin-width=&quot;563&quot; data-origin-height=&quot;260&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;AppContent에서 버튼을 클릭해서 10이라는 숫자를 AppHeader로 보내기 위해서는 다음과 같이 적용할 수 있다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;1. AppContent에서 Root 컴포넌트로 event emit을 이용해서 이벤트를 전달한다.&lt;/p&gt;
&lt;pre id=&quot;code_1699065536293&quot; class=&quot;html xml&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;&amp;lt;div id=&quot;app&quot;&amp;gt;
    &amp;lt;app-header&amp;gt;&amp;lt;/app-header&amp;gt;
    &amp;lt;app-content v-on:pass=&quot;deliverNum&quot;&amp;gt;&amp;lt;/app-content&amp;gt;
&amp;lt;/div&amp;gt;
 
&amp;lt;script src=&quot;https://cdn.jsdelivr.net/npm/vue/dist/vue.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script&amp;gt;
    var appHeader = {
        template: '&amp;lt;div&amp;gt;header&amp;lt;/div&amp;gt;',
    }
    var appContent = {
        template: '&amp;lt;div&amp;gt;Content &amp;lt;button v-on:click=&quot;passNum&quot;&amp;gt;pass&amp;lt;/button&amp;gt;&amp;lt;/div&amp;gt;',
        methods: {
            passNum: function(){
                this.$emit('pass', 10); // 10이라는 숫자를 pass 이벤트에 담아 보낸다.
            },
        }
    }
    new Vue({
        el: '#app',
        components: {
            'app-header' : appHeader,
            'app-content' : appContent,
        },
        data: {
            num: 0,
        },
        methods: {
            deliverNum: function(value){    // value는 $emit으로 보낸 10이 담겨온다.
                this.num = value;
            }
        }
    });
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot;&gt;2. 부모 컴포넌트에서 AppContent로부터 받아온 이벤트를 AppHeader로 props를 통해 전달한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1699065553398&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;div id=&quot;app&quot;&amp;gt;
    &amp;lt;app-header v-bind:propsdata=&quot;num&quot;&amp;gt;&amp;lt;/app-header&amp;gt;
    &amp;lt;app-content v-on:pass=&quot;deliverNum&quot;&amp;gt;&amp;lt;/app-content&amp;gt;
&amp;lt;/div&amp;gt;
 
&amp;lt;script src=&quot;https://cdn.jsdelivr.net/npm/vue/dist/vue.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script&amp;gt;
    var appHeader = {
        template: '&amp;lt;div&amp;gt;header&amp;lt;/div&amp;gt;',
        props: ['propsdata']    // props 등록
    }
    var appContent = {
        template: '&amp;lt;div&amp;gt;Content &amp;lt;button v-on:click=&quot;passNum&quot;&amp;gt;pass&amp;lt;/button&amp;gt;&amp;lt;/div&amp;gt;',
        methods: {
            passNum: function(){
                this.$emit('pass', 10);
            },
        }
    }
    new Vue({
        el: '#app',
        components: {
            'app-header' : appHeader,
            'app-content' : appContent,
        },
        data: {
            num: 0,
        },
        methods: {
            deliverNum: function(value){
                this.num = value;
            }
        }
    });
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;위 처럼 작업하게 되면 AppContent에 있는 버튼을 클릭할 때&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;AppHeader에 propsdata:10 값이 전달된다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;참고&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot;&gt;인프런 'Vue.js 시작하기 - Age of Vue.js' 강좌&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend/Vue.js, React</category>
      <category>vue event emit</category>
      <category>vue props</category>
      <category>vue props emit</category>
      <category>vue 데이터 이동</category>
      <category>vue 데이터 전달</category>
      <category>Vue.js</category>
      <category>vue.js emit</category>
      <category>vue.js event emit</category>
      <category>vue.js props</category>
      <category>vue.js 이벤트</category>
      <author>갠디</author>
      <guid isPermaLink="true">https://abcdqbbq.tistory.com/109</guid>
      <comments>https://abcdqbbq.tistory.com/109#entry109comment</comments>
      <pubDate>Sat, 4 Nov 2023 11:41:02 +0900</pubDate>
    </item>
    <item>
      <title>[Vue 2 기초] 컴포넌트</title>
      <link>https://abcdqbbq.tistory.com/108</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;component.png&quot; data-origin-width=&quot;1406&quot; data-origin-height=&quot;544&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/X6H7k/btszK3ODyjI/YsxeY5TC6NiqsMK39qVdg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/X6H7k/btszK3ODyjI/YsxeY5TC6NiqsMK39qVdg1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/X6H7k/btszK3ODyjI/YsxeY5TC6NiqsMK39qVdg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FX6H7k%2FbtszK3ODyjI%2FYsxeY5TC6NiqsMK39qVdg1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1406&quot; height=&quot;544&quot; data-filename=&quot;component.png&quot; data-origin-width=&quot;1406&quot; data-origin-height=&quot;544&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;컴포넌트란 화면을 영역별로 &lt;b&gt;구분&lt;/b&gt;하고 개발할 수 있는 뷰의 기능이다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;컴포넌트 단위로 화면을 개발하게 되면 특정 영역을 여러 페이지에서 사용하게 될 때 특정한 컴포넌트를 가져와서 사용하면 되기 때문에&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;재사용성&lt;/b&gt;이 올라가고 효율이 올라간다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;1. 전역 컴포넌트&lt;/h3&gt;
&lt;pre id=&quot;code_1699008834890&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;div id=&quot;app&quot;&amp;gt;
    &amp;lt;app-header&amp;gt;&amp;lt;/app-header&amp;gt;   &amp;lt;!-- 컴포넌트를 생성하면 컴포넌트 태그를 써서 적용한다. --&amp;gt;
    &amp;lt;app-content&amp;gt;&amp;lt;/app-content&amp;gt;
&amp;lt;/div&amp;gt;
 
&amp;lt;!-- 적용 후
&amp;lt;div id=&quot;app&quot;&amp;gt;
    &amp;lt;h1&amp;gt;Header&amp;lt;/h1&amp;gt;
    &amp;lt;div&amp;gt;Content&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
--&amp;gt;
 
&amp;lt;script src=&quot;https://cdn.jsdelivr.net/npm/vue/dist/vue.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script&amp;gt;
    // Vue.component('컴포넌트 이름', 컴포넌트 내용);
    // 전역 컴포넌트
    Vue.component('app-header', {
        template: '&amp;lt;h1&amp;gt;Header&amp;lt;/h1&amp;gt;',
    });
    Vue.component('app-content', {
        template: '&amp;lt;div&amp;gt;Content&amp;lt;/div&amp;gt;',
    });
 
    new Vue({
        el: '#app',
    });
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 소스를 Vue 개발자 도구에서 확인해보면&lt;/p&gt;
&lt;pre id=&quot;code_1699008889538&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;Root&amp;gt;
    &amp;lt;app-header&amp;gt;
    &amp;lt;app-content&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;위와 같이 생성된다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;(인스턴스를 생성하면 기본적으로 &amp;lt;Root&amp;gt; 컴포넌트가 만들어진다.)&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;위 방법은 전역 컴포넌트를 등록하는 방법이고 실 서비스에서는 주로 지역 컴포넌트를 사용하게 된다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 지역 컴포넌트&lt;/h3&gt;
&lt;pre id=&quot;code_1699008934023&quot; class=&quot;html xml&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;html&quot;&gt;&lt;code&gt;&amp;lt;div id=&quot;app&quot;&amp;gt;
    &amp;lt;app-footer&amp;gt;&amp;lt;/app-footer&amp;gt;
&amp;lt;/div&amp;gt;
 
&amp;lt;!-- 적용 후
&amp;lt;div id=&quot;app&quot;&amp;gt;
    &amp;lt;footer&amp;gt;Footer&amp;lt;/footer&amp;gt;
&amp;lt;/div&amp;gt;
--&amp;gt;
 
&amp;lt;script src=&quot;https://cdn.jsdelivr.net/npm/vue/dist/vue.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script&amp;gt;
    new Vue({
        el: '#app',
        // 지역 컴포넌트
        components: {
            // '키(컴포넌트 이름)' : 값(컴포넌트 내용)
            'app-footer' : {
                template: '&amp;lt;footer&amp;gt;Footer&amp;lt;/footer&amp;gt;'
            }
        },
    });
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 전역&amp;nbsp;컴포넌트,&amp;nbsp;지역&amp;nbsp;컴포넌트의&amp;nbsp;차이&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 지역&amp;nbsp;컴포넌트는&amp;nbsp;components&amp;nbsp;속성에&amp;nbsp;있는&amp;nbsp;컴포넌트들을&amp;nbsp;통해&amp;nbsp;해당&amp;nbsp;페이지를&amp;nbsp;봤을&amp;nbsp;때&amp;nbsp;어떤&amp;nbsp;컴포넌트들이&amp;nbsp;연결이&amp;nbsp;되어있는지&amp;nbsp;명시적으로&amp;nbsp;확인이&amp;nbsp;수월하다.&amp;nbsp;(&lt;u&gt;일반적으로는&amp;nbsp;지역&amp;nbsp;컴포넌트&amp;nbsp;사용&lt;/u&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 전역&amp;nbsp;컴포넌트는&amp;nbsp;플러그인이나&amp;nbsp;라이브러리와&amp;nbsp;같이&amp;nbsp;전역으로&amp;nbsp;사용해야하는&amp;nbsp;컴포넌트만&amp;nbsp;등록해서&amp;nbsp;사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #172b4d; text-align: start;&quot;&gt;인프런 'Vue.js 시작하기 - Age of Vue.js' 강좌&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Frontend/Vue.js, React</category>
      <category>vue 2</category>
      <category>vue js 컴포넌트</category>
      <category>Vue 컴포넌트</category>
      <category>vue.js 전역 컴포넌트</category>
      <category>vue.js 지역 컴포넌트</category>
      <category>vue.js 지역컴포넌트 전역컴포넌트</category>
      <category>vue.js 컴포넌트</category>
      <category>전역 컴포넌트</category>
      <category>지역 컴포넌트</category>
      <category>지역 컴포넌트 전역 컴포넌트 차이</category>
      <author>갠디</author>
      <guid isPermaLink="true">https://abcdqbbq.tistory.com/108</guid>
      <comments>https://abcdqbbq.tistory.com/108#entry108comment</comments>
      <pubDate>Fri, 3 Nov 2023 21:08:26 +0900</pubDate>
    </item>
  </channel>
</rss>