<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>dd_dev.log</title>
        <link>https://velog.io/</link>
        <description>developer</description>
        <lastBuildDate>Mon, 13 Feb 2023 05:00:12 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>dd_dev.log</title>
            <url>https://velog.velcdn.com/images/dd_dev/profile/dd756c62-3c92-4294-a723-4ef252ba28db/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. dd_dev.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/dd_dev" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[DOM 의 position ?]]></title>
            <link>https://velog.io/@dd_dev/DOM-%EC%9D%98-position</link>
            <guid>https://velog.io/@dd_dev/DOM-%EC%9D%98-position</guid>
            <pubDate>Mon, 13 Feb 2023 05:00:12 GMT</pubDate>
            <description><![CDATA[<p>브라우저에서 dom 관련 상하좌우 판단 기준에 대한 정확한 이해가 부족해서, 확실하게 짚고 넘어가고자 확인해본다.</p>
<h1 id="offset">offset</h1>
<p>현 element 의 outer border 에서부터 <code>offsetParent</code> 의 inner border 까지의 상대적인 거리  </p>
<h2 id="offsetparent">offsetParent</h2>
<p>현 element 에서 가장 근접한 <code>positioned 조상 element</code></p>
<h3 id="positioned-조상-element">positioned 조상 element</h3>
<ul>
<li>non-static position element</li>
<li>td, th, table (자체 static positioned element 인 경우의)  </li>
</ul>
<blockquote>
<p>offsetTop / offsetLeft 는 offsetParent 의 상대적인 padding edge 에 따라 결정된다.</p>
</blockquote>
<h1 id="elementgetboundingclientrect">Element.getBoundingClientRect()</h1>
<p>DOMRect object 를 리턴하는 함수.<br>element 의 size 관련 정보와, viewport 에 상대적인 postiion 정보를 return</p>
<p><img src="https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect/element-box-diagram.png" alt="viewport"></p>
<h2 id="domrect-padding-과-border-width-포함하는-가장작은-직사각형"><code>DOMRect</code>: padding 과 border-width 포함하는 가장작은 직사각형</h2>
<ul>
<li>left, top, right, bottom, x, y, width, height  </li>
<li>width &amp; height = <code>element + padding + border-width</code><ul>
<li><code>box-sizing: border-box;</code> 일 경우, element의 width &amp; height 과 같아진다. (기본 content-box;)</li>
</ul>
</li>
</ul>
<blockquote>
<p>document 의 top-left 코너로부터의 상대값을 원한다면, <code>window.scrollY / window.scrollX</code> 를 더해줘라</p>
</blockquote>
<h1 id="refererence">refererence</h1>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetTop">https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetTop</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect">https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[MDN Javascript - Dynamic client-side scripting] Javascript 가 뭔가요 ?]]></title>
            <link>https://velog.io/@dd_dev/MDN-Javascript-Dynamic-client-side-scripting-Javascript-%EA%B0%80-%EB%AD%94%EA%B0%80%EC%9A%94</link>
            <guid>https://velog.io/@dd_dev/MDN-Javascript-Dynamic-client-side-scripting-Javascript-%EA%B0%80-%EB%AD%94%EA%B0%80%EC%9A%94</guid>
            <pubDate>Thu, 27 Oct 2022 07:26:42 GMT</pubDate>
            <description><![CDATA[<h1 id="둘러보기">둘러보기</h1>
<p>JavaScript는 표준 웹 기술이라는 케이크의 세 번째 층입니다.  </p>
<ul>
<li><code>HTML</code>은 웹 콘텐츠의 구조를 짜고 의미를 부여하는 마크업 언어입니다. 예를 들어 페이지의 어디가 문단이고, 헤딩이고, 데이터 표와 외부 이미지/비디오인지 정의합니다.</li>
<li><code>CSS</code>는 HTML 콘텐츠에 스타일을 적용할 수 있는 스타일 규칙 언어입니다. 배경색을 추가하고, 글꼴을 바꾸고, 콘텐츠를 신문처럼 다열 레이아웃으로 배치할 수 있습니다.</li>
<li><code>JavaScript</code>는 동적으로 콘텐츠를 바꾸고, 멀티미디어를 제어하고, 애니메이션을 추가하는 등 거의 모든 것을 만들 수 있는 스크립팅 언어입니다. (정말 모든게 가능하지는 않겠지만, JavaScript 코드 몇 줄만으로도 놀라운 결과를 이룰 수 있습니다)</li>
</ul>
<h2 id="애플리케이션-프로그래밍-인터페이스api라고-부르는-이-기능들은-여러분의-javascript-코드에서-사용할-수-있는-강력한-마법을-추가로-제공합니다"><code>애플리케이션 프로그래밍 인터페이스(API)</code>라고 부르는 이 기능들은 여러분의 JavaScript 코드에서 사용할 수 있는 강력한 마법을 추가로 제공합니다</h2>
<ul>
<li><code>DOM (Document Object Model) API</code>로 HTML 콘텐츠를 추가, 제거, 변경하고, 동적으로 페이지에 스타일을 추가하는 등 HTML/CSS를 조작할 수 있습니다. 페이지 위에 뜨는 팝업창이나, (위쪽의 간단한 예제처럼) 새로운 콘텐츠가 나타나는 것을 본 적이 있으면 DOM이 동작한 겁니다.</li>
<li><code>Geolocation API</code>로 지리정보를 가져올 수 있습니다. Google 지도에서 여러분의 위치를 찾아 지도에 그릴 수 있는 이유가 바로 이 API입니다.</li>
<li><code>Canvas와 WebGL API</code>로 애니메이션을 적용한 2D와 3D 그래픽을 만들 수 있습니다. 두 웹 기술을 사용해서 만들 수 있는 놀라운 결과를 엿보려면 Chrome Experiments와 webglsamples를 방문하세요.</li>
<li><code>HTMLMediaElement와 WebRTC를 포함하는 오디오와 비디오 API</code>로는 멀티미디어를 사용한 흥미로운 일을 할 수 있습니다. 예를 들어 오디오나 비디오를 웹 페이지에서 바로 재생하거나, 여러분의 웹캠으로 비디오를 찍어 다른 사람의 화면에 보여줄 수 있습니다. (간단한 스냅샷 데모를 방문해서 감을 잡아보세요)</li>
</ul>
<blockquote>
<p>서드파티 API는 브라우저에 탑재되지 않은 API로, 웹의 어딘가에서 직접 코드와 정보를 찾아야 합니다.  </p>
</blockquote>
<h1 id="웹-페이지에서-javascript는-어떤-일을-하나요">웹 페이지에서 JavaScript는 어떤 일을 하나요?</h1>
<p>웹 페이지를 브라우저로 불러오면, 브라우저는 여러분의 코드(HTML, CSS, JavaScript)를 실행 환경(브라우저 탭)에서 실행합니다.<br>JavaScript는 DOM (Document Object Model) API를 통해 HTML과 CSS를 동적으로 수정, 사용자 인터페이스를 업데이트하는 일에 가장 많이 쓰입니다.  </p>
<h2 id="브라우저-보안">브라우저 보안</h2>
<p>각각의 브라우저 탭은 코드를 실행하기 위한 독립적인 그릇(기술 용어로 &quot;실행 환경&quot;이라고 부릅니다)입니다.<br>독립적이라는 것은 대부분의 탭이 서로에게서 완전히 분리되어 한 탭의 코드가 다른 탭의 코드, 또는 다른 사이트에 직접적인 영향을 줄 수 없다는 뜻입니다.   </p>
<h2 id="javascript-실행-순서">JavaScript 실행 순서</h2>
<p>JavaScript 블록을 마주치면, 일반적으로는 순서대로 위에서 아래로 실행합니다  </p>
<h2 id="인터프리터와-컴파일러">인터프리터와 컴파일러</h2>
<p>인터프리터를 사용하는 언어에서는 코드를 위에서 아래로 실행하고, 코드 구동 결과는 즉시 반환됩니다. <code>브라우저에서 JavaScript 코드를 실행하기 전에 다른 형태로 변환할 필요가 없다는 점</code>을 기억하세요.<br>반면, 컴파일러를 사용하는 컴파일 언어에서는 컴퓨터가 코드를 실행하기 전에 다른 형태로 변환(컴파일)해야 합니다.<br>JavaScript는 가볍고, 인터프리터를 사용하는 프로그래밍 언어입니다. 대부분의 모던 JavaScript 인터프리터들은 사실 <code>JIT 컴파일(just-in-time 컴파일)</code>이라는 기술을 사용해 성능을 향상하기는 합니다.<br>하지만 JavaScript는 여전히 인터프리터 언어로 분류됩니다. 컴파일을 먼저 해놔야 하는 것이 아니라 런타임에 일어나기 때문입니다.  </p>
<h2 id="서버-사이드와-클라이언트-사이드-코드">서버 사이드와 클라이언트 사이드 코드</h2>
<p>클라이언트 사이드 코드는 사용자의 컴퓨터에서 동작하는 코드입니다. 정확히는 클라이언트 사이드 JavaScript입니다.<br>반대로 서버 사이드 코드는 서버에서 동작하는 코드로, 그 실행 결과를 브라우저가 다운로드해서 화면에 띄우게 됩니다.<br>JavaScript는 브라우저 뿐만 아니라, 많은 사람들이 사용하는 Node.js 환경처럼 서버 사이드 언어로도 사용할 수 있습니다.  </p>
<h2 id="동적-코드와-정적-코드">동적 코드와 정적 코드</h2>
<p>클라이언트 사이드 JavaScript와 서버 사이드 언어들 모두 동적이라는 단어로 설명할 수 있습니다. 동적인 이유는 웹 페이지/웹 앱의 서로 다른 상황에 서로 다른 화면을 보여줄 수 있고, 필요하면 새로운 콘텐츠를 생성할 수 있기 때문입니다.<br>정적인 페이지는 항상 같은 콘텐츠만 보여줍니다.  </p>
<h1 id="웹-페이지에-javascript를-어떻게-넣나요">웹 페이지에 JavaScript를 어떻게 넣나요?</h1>
<p>CSS가 <code>&lt;link&gt;</code> 요소로 외부 스타일 시트를 적용하고, <code>&lt;style&gt;</code> 요소로 내부 스타일 시트를 적용했다면, JavaScript는 한 종류의 HTML 친구만 요구합니다. 바로 <code>&lt;script&gt;</code> 요소입니다.  </p>
<h2 id="인라인-javascript-처리기">인라인 JavaScript 처리기</h2>
<p> 이 방법을 사용하지 말아주세요. JavaScript로 HTML 코드를 물들이는 것은 나쁜 방법일 뿐더러 비효율적입니다. JavaScript를 적용하려는 모든 버튼마다 일일히 onclick=&quot;createParagraph()&quot;를 추가해야 하니까요.  </p>
<blockquote>
<p> 대신 addEventListener 사용하기  </p>
</blockquote>
<h2 id="스크립트-로딩-전략">스크립트 로딩 전략</h2>
<p>해당 요소를 포함한 HTML 코드보다 JavaScript를 먼저 불러와버리면 코드가 올바르게 동작하지 못할 것입니다.  </p>
<ul>
<li><code>document.addEventListener(&#39;DOMContentLoaded&#39;, ()</code></li>
<li><code>&lt;script src=&quot;script.js&quot; defer&gt;&lt;/script&gt;</code></li>
</ul>
<blockquote>
<p>이렇게 하면 스크립트와 HTML을 동시에 불러오므로 오류가 발생하지 않습니다.  </p>
</blockquote>
<ul>
<li>고전적인 방법은 스크립트 요소를 본문의 맨 마지막(<code>&lt;/body&gt; 태그 바로 앞</code>)에 배치하는 것입니다.<blockquote>
<p>이 방법의 문제는 HTML DOM을 모두 불러오기 전에는 스크립트의 로딩과 분석이 완전히 중단된다는 점입니다. 그래서 많은 스크립트를 포함하는 대형 사이트에서는 성능이 저하될 수 있습니다.  </p>
</blockquote>
</li>
</ul>
<h2 id="async와-defer">async와 defer</h2>
<h3 id="async">async</h3>
<p>async 특성을 지정하면 스크립트를 가져오는 동안 페이지 로딩을 중단하지 않습니다. 그러나 스크립트 다운로드가 끝나면 바로 실행되는데, 실행 도중에는 페이지 렌더링이 중단됩니다. <code>스크립트의 실행 순서를 보장할 방법은 없습니다</code>. 따라서 async는 다른 스크립트에 의존하지 않는 독립 스크립트에 사용해야 합니다.  </p>
<h3 id="defer">defer</h3>
<p>defer 특성을 지정한 스크립트는 페이지 내에 <code>배치한 순서대로 불러오게 됩니다</code>. 또한 페이지 콘텐츠를 모두 불러오기 전까지는 실행하지 않으므로, 페이지 요소를 수정하거나 추가하는 등 DOM 작업을 기대하는 스크립트에 유용합니다.  </p>
<p><img src="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/First_steps/What_is_JavaScript/async-defer.jpg" alt="async vs defer"></p>
<h1 id="주석">주석</h1>
<p>브라우저는 무시하는 주석을 작성해서 다른 개발자들(아니면 무슨 일을 했었는지 기억하지 못하는 미래의 여러분)에게 안내를 제공할 수 있습니다.  </p>
<h1 id="ref">Ref</h1>
<p><a href="https://developer.mozilla.org/ko/docs/Learn/JavaScript/First_steps/What_is_JavaScript">[Javascript - Dynamic client-side scripting] What is Javascript ?</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[AWS freetier, Docker (npm, nginx) 로 빠르게 웹 서비스 띄우기]]></title>
            <link>https://velog.io/@dd_dev/AWS-freetier-Docker-npm-nginx-%EB%A1%9C-%EB%B9%A0%EB%A5%B4%EA%B2%8C-%EC%9B%B9-%EC%84%9C%EB%B9%84%EC%8A%A4-%EB%9D%84%EC%9A%B0%EA%B8%B0</link>
            <guid>https://velog.io/@dd_dev/AWS-freetier-Docker-npm-nginx-%EB%A1%9C-%EB%B9%A0%EB%A5%B4%EA%B2%8C-%EC%9B%B9-%EC%84%9C%EB%B9%84%EC%8A%A4-%EB%9D%84%EC%9A%B0%EA%B8%B0</guid>
            <pubDate>Fri, 21 Oct 2022 08:48:29 GMT</pubDate>
            <description><![CDATA[<h1 id="aws-instance-생성">AWS instance 생성</h1>
<h2 id="instance">instance</h2>
<p><img src="https://velog.velcdn.com/images/dd_dev/post/71e113ee-eb97-44d8-95d7-6fef4feb97ed/image.png" alt=""></p>
<p>default 로 yum 이 설치되어있기 때문에, <code>Amazon Linux</code> 로 세팅  </p>
<h2 id="security-group">security group</h2>
<p><img src="https://velog.velcdn.com/images/dd_dev/post/4df517f4-4b8e-4d86-9247-56c352f4c77e/image.png" alt=""></p>
<p>default ssh 22 를 제외하고, <code>Custom TCP 8080 포트의 inbound</code> 를 허용한다. (웹 서비스의 포트)  </p>
<h1 id="aws-instance-connect--setting">AWS instance connect &amp; setting</h1>
<h2 id="install-nodejs">install nodejs</h2>
<p>자습서의 node.js 설정을 참고하여 nvm, nodejs install  </p>
<pre><code class="language-bash">$ sudo su
$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash
$ . ~/.nvm/nvm.sh
$ nvm install --lts</code></pre>
<h2 id="install-docker">install docker</h2>
<p>docker 설치 및 실행  </p>
<pre><code class="language-bash">$ yum install docker -y
$ systemctl start docker</code></pre>
<h1 id="proejct-setting">Proejct Setting</h1>
<h2 id="pull-clone-github">pull clone github</h2>
<p>NPM으로 웹 서비스를 구현한 github project clone  </p>
<pre><code class="language-bash">$ yum install git -y
$ git clone [REPO]</code></pre>
<h3 id="git-credential-store-git-idpw-기억">git credential store (git id/pw 기억)</h3>
<pre><code class="language-bash">$ git config --global credential.helper store</code></pre>
<h2 id="proejct-root-에-dockerfile-생성">Proejct root 에 Dockerfile 생성</h2>
<pre><code class="language-Dockerfile"># build stage
FROM node:lts-alpine as build-stage
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

# production stage
FROM nginx:stable-alpine as production-stage
COPY --from=build-stage /app/dist /usr/share/nginx/html
EXPOSE 80
CMD [&quot;nginx&quot;, &quot;-g&quot;, &quot;daemon off;&quot;]</code></pre>
<h1 id="serve">serve</h1>
<p>nginx 에 80으로 serving 하도록 설정했으므로, aws security group 에 설정한 포트를 80으로 노출시킨다.  </p>
<pre><code class="language-bash">$ cd [GIT_PROJECT_ROOT]
$ docker build -t [IMAGE_NAME] .
$ docker run -d -it -p 8080:80 --name [CONTAINER_NAME] [IMAGE_NAME]</code></pre>
<h1 id="check">check</h1>
<p>aws instance 의 <code>[Public IPv4 address]:8080</code> 으로 접속 확인  </p>
<h1 id="reference">reference</h1>
<ul>
<li><a href="https://docs.aws.amazon.com/ko_kr/sdk-for-javascript/v2/developer-guide/setting-up-node-on-ec2-instance.html">자습서: Amazon EC2 인스턴스에서 Node.js 설정</a></li>
<li><a href="https://v2.vuejs.org/v2/cookbook/dockerize-vuejs-app.html">Dockerize Vue.js App</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Typescript 5분 개요]]></title>
            <link>https://velog.io/@dd_dev/Typescript-5%EB%B6%84-%EA%B0%9C%EC%9A%94</link>
            <guid>https://velog.io/@dd_dev/Typescript-5%EB%B6%84-%EA%B0%9C%EC%9A%94</guid>
            <pubDate>Mon, 03 Oct 2022 08:10:36 GMT</pubDate>
            <description><![CDATA[<h1 id="typescript-5분-개요"><a href="https://www.typescriptlang.org/ko/docs/handbook/typescript-in-5-minutes.html">Typescript 5분 개요</a></h1>
<h2 id="타입-추론-types-by-inference">타입 추론 (Types by Inference)</h2>
<p>변수를 생성하면서 동시에 특정 값에 할당하는 경우, TypeScript는 그 값을 해당 변수의 타입으로 사용할 것입니다.  </p>
<h2 id="타입-정의하기-defining-types">타입 정의하기 (Defining Types)</h2>
<p>몇몇 디자인 패턴은 자동으로 타입을 제공하기 힘들 수 있는데 (동적 프로그래밍을 사용하고 있을 것이기 때문에) 이러한 경우에 TypeScript는 TypeScript에게 타입이 무엇이 되어야 하는지 명시 가능한 JavaScript 언어의 확장을 지원합니다.  </p>
<p>이 객체의 형태를 명시적으로 나타내기 위해서는 interface 로 선언합니다.  </p>
<pre><code class="language-ts">interface User {
  name: string;
  id: number;
}
// ---cut---
const user: User = {
  name: &quot;Hayes&quot;,
  id: 0,
};</code></pre>
<p>인터페이스는 클래스로도 선언할 수 있습니다.  </p>
<pre><code class="language-ts">class UserAccount {
  name: string;
  id: number;
  constructor(name: string, id: number) {
    this.name = name;
    this.id = id;
  }
}
const user: User = new UserAccount(&quot;Murphy&quot;, 1);</code></pre>
<p>인터페이스는 함수에서 매개변수와 리턴 값을 명시하는데 사용되기도 합니다.  </p>
<pre><code class="language-ts">// ---cut---
function getAdminUser(): User {
  //...
}
function deleteUser(user: User) {
  // ...
}</code></pre>
<p><code>boolean, bigint, null, number, string, symbol, object와 undefined</code>는 인터페이스에서 사용할 수 있습니다. TypeScript는 몇 가지를 추가해 목록을 확장합니다.  </p>
<ul>
<li><code>any</code> (무엇이든 허용합니다)</li>
<li><code>unknown</code> (이 타입을 사용하는 사람이 타입이 무엇인지 선언했는가를 확인하십시오)</li>
<li><code>never</code> (이 타입은 발생될 수 없습니다) </li>
<li><code>void</code> (undefined를 리턴하거나 리턴 값이 없는 함수).  </li>
</ul>
<p>타입을 구축하기 위한 두 가지 구문이 있다는 것을 꽤 빠르게 알 수 있을 것입니다.: <code>Interfaces and Types</code> - interface를 우선적으로 사용하고 특정 기능이 필요할 때 type을 사용해야 합니다.  </p>
<h2 id="타입-구성-composing-types">타입 구성 (Composing Types)</h2>
<h3 id="유니언-unions">유니언 (Unions)</h3>
<p>타입이 여러 타입 중 하나일 수 있음을 선언하는 방법입니다  </p>
<pre><code class="language-ts">type MyBool = true | false;
type WindowStates = &quot;open&quot; | &quot;closed&quot; | &quot;minimized&quot;;
type LockStates = &quot;locked&quot; | &quot;unlocked&quot;;
type OddNumbersUnderTen = 1 | 3 | 5 | 7 | 9;
function getLength(obj: string | string[]) {
  return obj.length;
}</code></pre>
<table>
<thead>
<tr>
<th>Type</th>
<th>Predicate</th>
</tr>
</thead>
<tbody><tr>
<td>string</td>
<td>typeof s === &quot;string&quot;</td>
</tr>
<tr>
<td>number</td>
<td>typeof n === &quot;number&quot;</td>
</tr>
<tr>
<td>boolean</td>
<td>typeof b === &quot;boolean&quot;</td>
</tr>
<tr>
<td>undefined</td>
<td>typeof undefined === &quot;undefined&quot;</td>
</tr>
<tr>
<td>function</td>
<td>typeof f === &quot;function&quot;</td>
</tr>
<tr>
<td>array</td>
<td>Array.isArray(a)</td>
</tr>
</tbody></table>
<h3 id="제네릭-generics">제네릭 (Generics)</h3>
<p>타입에 변수를 제공하는 방법입니다.  </p>
<pre><code class="language-ts">type StringArray = Array&lt;string&gt;;
type NumberArray = Array&lt;number&gt;;
type ObjectWithNameArray = Array&lt;{ name: string }&gt;;</code></pre>
<h2 id="구조적-타입-시스템-structural-type-system">구조적 타입 시스템 (Structural Type System)</h2>
<p>구조적 타입 시스템에서 두 객체가 같은 형태를 가지면 같은 것으로 간주됩니다.  </p>
<pre><code class="language-ts">interface Point {
  x: number;
  y: number;
}

function printPoint(p: Point) {
  console.log(`${p.x}, ${p.y}`);
}

// &quot;12, 26&quot;를 출력합니다
const point = { x: 12, y: 26 };
printPoint(point);</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Vue3 가이드 정리]]></title>
            <link>https://velog.io/@dd_dev/Vue3-%EA%B0%80%EC%9D%B4%EB%93%9C-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@dd_dev/Vue3-%EA%B0%80%EC%9D%B4%EB%93%9C-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Wed, 28 Sep 2022 08:26:25 GMT</pubDate>
            <description><![CDATA[<h1 id="-vue는-무엇일까요"><a href="https://v3-docs.vuejs-korea.org/"># Vue는 무엇일까요?</a></h1>
<h1 id="핵심-가이드"><a href="https://v3-docs.vuejs-korea.org/guide/essentials/application.html">핵심 가이드</a></h1>
<h2 id="프로그레시브-프레임워크">프로그레시브 프레임워크</h2>
<p>JAM stack 은 Javascript, Api, Markup Stack 의 약자이다.<br>SPA 과는 비슷하지만 다르다.  </p>
<h2 id="싱글-파일-컴포넌트">싱글 파일 컴포넌트</h2>
<p>Vue SFC는 이름에서 알 수 있듯이 컴포넌트의 논리(JavaScript), 템플릿(HTML) 및 스타일(CSS)을 하나의 파일에 캡슐화</p>
<h2 id="api-스타일">API 스타일</h2>
<ul>
<li><p>옵션 API  </p>
<blockquote>
<p>옵션의 data, methods 및 mounted 같은 객체를 사용하여 컴포넌트의 로직를 정의<br>컴포넌트 인스턴스&quot;(예제에서 볼 수 있는 this)의 개념을 중심으로 합니다</p>
</blockquote>
</li>
<li><p>컴포지션 API  </p>
<blockquote>
<p>import해서 가져온 API 함수들을 사용하여 컴포넌트의 로직를 정의<br>컴포지션 API는 일반적으로 <code>&lt;script setup&gt;</code> 과 함께 사용<br>함수 범위에서 직접 반응형 변수를 선언하고 복잡성을 처리하기 위해 여러 함수의 상태를 함께 구성하는데 중점을 둡니다</p>
</blockquote>
</li>
<li><p>무엇을 선택해야 할까요?</p>
</li>
</ul>
<h1 id="vue-앱-만들기">Vue 앱 만들기</h1>
<h2 id="앱-인스턴스">앱 인스턴스</h2>
<p>모든 Vue 앱은 createApp 함수를 사용하여 새로운 앱 인스턴스를 생성하는 것으로 시작</p>
<h2 id="앱-환경설정">앱 환경설정</h2>
<pre><code class="language-js">app.config.errorHandler = (err) =&gt; {
/* 에러 처리 */
}</code></pre>
<h1 id="템플릿-문법">템플릿 문법</h1>
<p><a href="https://v3-docs.vuejs-korea.org/guide/essentials/template-syntax.html">https://v3-docs.vuejs-korea.org/guide/essentials/template-syntax.html</a><br>렌더링된 DOM에 바인딩할 수 있는 HTML 기반 템플릿 문법을 사용<br>가상 DOM 개념에 익숙하고 JavaScript의 원시적인 작동법을 선호하는 경우, JSX 지원 옵션을 사용하여 템플릿 대신 렌더링 함수를 직접 작성할 수도 있습니다  </p>
<h2 id="제한된-전역-접근">제한된 전역 접근</h2>
<pre><code class="language-js">const GLOBALS_WHITE_LISTED =
&#39;Infinity,undefined,NaN,isFinite,isNaN,parseFloat,parseInt,decodeURI,&#39; +
&#39;decodeURIComponent,encodeURI,encodeURIComponent,Math,Number,Date,Array,&#39; +
&#39;Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt&#39;</code></pre>
<p><code>app.config.globalProperties</code>에 추가하여, Vue 내부의 모든 표현식에서 전역 속성에 접근 할 수 있도록 명시적으로 정의할 수 있습니다  </p>
<h2 id="동적인-인자">동적인 인자</h2>
<p>디렉티브의 인자를 대괄호로 감싸서 JavaScript 표현식으로 사용할 수도 있습니다:  </p>
<pre><code class="language-html">&lt;a :[attributeName]=&quot;url&quot;&gt; ... &lt;/a&gt;
&lt;a @[eventName]=&quot;doSomething&quot;&gt;
&lt;!-- 이렇게 하면 컴파일러 경고가 트리거됩니다. --&gt;
&lt;a :[&#39;foo&#39; + bar]=&quot;value&quot;&gt; ... &lt;/a&gt;
&lt;!-- computed 속성을 사용하는 것이 더 나을 것입니다 --&gt;</code></pre>
<h2 id="수식어">수식어</h2>
<p><code>.prevent</code> 수식어는 트리거된 이벤트에서 <code>event.preventDefault()</code>를 호출하도록 <code>v-on</code> 디렉티브에 지시  </p>
<h1 id="반응형-기초">반응형 기초</h1>
<p>data에 포함하지 않고 this에 직접 새 속성을 추가할 수는 있습니다. 그러나 이러한 방식으로 추가된 속성은 이후 반응형 업데이트 동작이 이루어지지 않습니다.  </p>
<blockquote>
<p>기본 제공되는 API를 노출할 때 <code>$</code> 접두사를 사용<br>내부 속성에 대해서는 <code>_</code> 접두사를 사용  </p>
</blockquote>
<h2 id="반응형-재정의-vs-원본">반응형 재정의 vs 원본</h2>
<p>Vue 3에서는 JavaScript Proxy를 활용하여 데이터를 반응형으로 만듭니다<br>원본을 반응형으로 재정의한 프락시 객체<br>Vue 2와 달리 원본 newObject 객체는 그대로 유지, 반응형으로 변하지 않습니다  </p>
<h2 id="dom-업데이트-시기">DOM 업데이트 시기</h2>
<p>DOM 업데이트는 동기적으로 적용되지 않는다<br>DOM 업데이트가 완료될 때까지 기다리려면 nextTick() 전역 API를 사용할 수 있습니다:  </p>
<h2 id="깊은-반응형">깊은 반응형</h2>
<p>중첩된 객체나 배열을 변경할 때에도 변경 사항이 감지됩니다:  </p>
<h1 id="계산된-속성">계산된 속성</h1>
<h2 id="계산된-캐싱-vs-메서드">계산된 캐싱 vs 메서드</h2>
<p>표현식에서 메서드를 호출하여 동일한 결과를 얻을 수도 있습니다:<br><strong>차이점은 계산된 속성은 의존된 반응형을 기반으로 캐시된다는 점</strong>
메서드 호출은 리렌더링이 발생할 때마다 항상 함수를 실행합니다.  </p>
<h2 id="수정-가능한-계산된-속성">수정 가능한 계산된 속성</h2>
<p>드물게 &quot;수정 가능한&quot; 계산된 속성이 필요한 경우, getter와 setter를 모두 제공하여 속성을 만들 수 있습니다.  </p>
<h2 id="모범-사례">모범 사례</h2>
<ul>
<li><p>getter에서 사이드 이펙트는 금물</p>
<blockquote>
<p>getter에서 비동기 요청을 하거나 DOM을 변경하면 안됩니다!  </p>
</blockquote>
</li>
<li><p>계산된 값을 변경하지 마십시오</p>
<blockquote>
<p>임시 스냅샷으로 생각하십시오.  </p>
</blockquote>
</li>
</ul>
<h1 id="클래스와-스타일-바인딩">클래스와 스타일 바인딩</h1>
<p>Vue는 v-bind가 class 및 style과 함께 사용될 때 특별한 향상을 제공  </p>
<h2 id="html-클래스-바인딩">HTML 클래스 바인딩</h2>
<ul>
<li><p>객체로 바인딩 하기</p>
<pre><code class="language-html">  &lt;div :class=&quot;{ active: isActive }&quot;&gt;&lt;/div&gt;</code></pre>
<blockquote>
<p>객체를 반환하는 계산된 속성으로 바인딩할 수도 있습니다.  </p>
</blockquote>
</li>
<li><p>배열로 바인딩 하기</p>
<pre><code class="language-html">  &lt;div :class=&quot;[isActive ? activeClass : &#39;&#39;, errorClass]&quot;&gt;&lt;/div&gt;
  &lt;div :class=&quot;[{ active: isActive }, errorClass]&quot;&gt;&lt;/div&gt;</code></pre>
</li>
<li><p>컴포넌트에서 사용하기</p>
<blockquote>
<p>최상위(root) 엘리먼트가 하나로 구성된 컴포넌트에서 class 속성을 사용하면, 해당 클래스가 컴포넌트의 루트 엘리먼트에 이미 정의된 기존 클래스와 병합되어 추가됩니다.  </p>
</blockquote>
<blockquote>
<p>여러 개의 최상위 엘리먼트로 컴포넌트가 구성되어 있는 경우, 클래스를 적용할 엘리먼트를 정의해야 합니다. $attrs 컴포넌트 속성을 사용하여 이 작업을 수행할 수 있습니다.  </p>
</blockquote>
<pre><code class="language-html">  &lt;!-- MyComponent 템플릿에서 $attrs 속성을 사용 --&gt;
  &lt;p :class=&quot;$attrs.class&quot;&gt;안녕!&lt;/p&gt;
  &lt;span&gt;반가워!&lt;/span&gt;</code></pre>
</li>
</ul>
<h2 id="인라인-스타일-바인딩">인라인 스타일 바인딩</h2>
<p>:style에 사용될 CSS 속성에 해당하는 키 문자열은 camelCase가 권장되지만, kebab-cased(실제 CSS에서 사용되는 방식)도 지원  </p>
<ul>
<li><p>배열로 바인딩 하기</p>
<pre><code class="language-html">  &lt;div :style=&quot;[baseStyles, overridingStyles]&quot;&gt;&lt;/div&gt;</code></pre>
</li>
<li><p>접두사 자동완성</p>
<blockquote>
<p>CSS 속성이 :style에 사용되면, 자동으로 해당 속성과 <code>벤더 접두사</code>가 조합된 여러 개의 특수한 속성을 테스트하고 지원되는 속성을 찾아서 추가합니다.  </p>
</blockquote>
</li>
<li><p>다중 값</p>
<blockquote>
<p>스타일 속성에 다중 값을 배열로 제공할 수 있습니다. 예를 들면 다음과 같습니다:  </p>
</blockquote>
<pre><code class="language-html">  &lt;div :style=&quot;{ display: [&#39;flex&#39;, &#39;-webkit-box&#39;, &#39;-ms-flexbox&#39;] }&quot;&gt;&lt;/div&gt;</code></pre>
<blockquote>
<p>브라우저가 지원하는 배열 내 <code>마지막 값</code>을 렌더링합니다.   </p>
</blockquote>
</li>
</ul>
<h1 id="조건부-렌더링">조건부 렌더링</h1>
<h2 id="v-if-vs-v-show">v-if vs v-show</h2>
<p>v-if는 &quot;실제&quot; 조건부 렌더링입니다. 왜냐하면 조건부 블록이 전환될 경우, 블록 내 이벤트 리스너와 자식 컴포넌트가 제대로 제거되거나 재생성되기 때문입니다.  </p>
<p>게으르므로(lazy), 초기 조건이 false면 아무 작업도 수행하지 않습니다. 조건부 블록은 조건이 true가 될 때까지 렌더링되지 않습니다.  </p>
<p>v-show는 훨씬 간단합니다. 엘리먼트는 CSS 기반으로 전환 되므로, 초기 조건과 관계없이 항상 렌더링 됩니다.  </p>
<p>v-if는 전환 비용이 더 높고, v-show는 초기 렌더링 비용이 더 높습니다.  </p>
<h2 id="v-if-with-v-for">v-if with v-for</h2>
<p>v-if와 v-for를 함께 사용하는 것은 권장되지 않습니다.<br>v-if와 v-for를 함께 사용하면 v-if가 먼저 평가  </p>
<h1 id="리스트-렌더링">리스트 렌더링</h1>
<p>in 대신 of를 구분 기호로 사용하여 JavaScript 반복문 문법처럼 사용할 수도 있습니다:  </p>
<pre><code class="language-html">&lt;div v-for=&quot;item of items&quot;&gt;&lt;/div&gt;</code></pre>
<p>v-for의 아이템도 분해 할당해 사용할 수 있습니다:</p>
<pre><code class="language-html">&lt;!-- index 에일리어스도 사용 --&gt;
&lt;li v-for=&quot;({ message }, index) in items&quot;&gt;
  {{ message }} {{ index }}
&lt;/li&gt;</code></pre>
<h2 id="객체에-v-for-사용하기">객체에 v-for 사용하기</h2>
<p>Object.keys()를 호출한 결과에 기반  </p>
<pre><code class="language-html">&lt;li v-for=&quot;(value, key, index) in myObject&quot;&gt;
  {{ index }}. {{ key }}: {{ value }}
&lt;/li&gt;</code></pre>
<h2 id="숫자-범위에-v-for-사용하기">숫자 범위에 v-for 사용하기</h2>
<p>이 경우 1...n 범위를 기준으로 템플릿을 여러 번 반복합니다.<br>여기서 n의 값은 0이 아니라 1부터 시작합니다.  </p>
<h2 id="v-if에-v-for-사용하기">v-if에 v-for 사용하기</h2>
<p>이것들이 같은 노드에 존재할 때 v-if가 v-for보다 우선순위가 높기 때문에 v-if 조건문에서 v-for 변수에 접근할 수 없습니다:  </p>
<h2 id="key를-통한-상태유지">key를 통한 상태유지</h2>
<p>기본적으로 &quot;in-place patch&quot; 전략<br>리스트 렌더링 출력이 자식 컴포넌트 상태 또는 임시 DOM 상태(예: 양식 입력 값)에 의존하지 않는 경우에만 유효합니다.<br><code>&lt;template v-for&gt;</code>를 사용할 때 key는 <code>&lt;template&gt;</code> 컨테이너에 있어야 합니다.<br>가능한 한 언제나 v-for는 key 속성과 함께 사용하는 것을 권장합니다.<br>key에는 문자열, 숫자, 심볼 형식의 값만 바인딩해야 합니다  </p>
<h2 id="배열-변경-감지">배열 변경 감지</h2>
<ul>
<li>push()</li>
<li>pop()</li>
<li>shift()</li>
<li>unshift()</li>
<li>splice()</li>
<li>sort()</li>
<li>reverse()  <blockquote>
<p>계산된 속성에서 reverse()와 sort() 사용에 주의하십시오!  </p>
</blockquote>
</li>
</ul>
<h2 id="배열-교체">배열 교체</h2>
<p>filter(), concat() 및 slice()<br>이전 배열을 다른 배열로 바꾸는 과정에서 서로 중복되는 객체를 가지는 부분을 매우 효율적으로 처리  </p>
<h1 id="이벤트-핸들링">이벤트 핸들링</h1>
<h2 id="인라인-핸들러에서-이벤트-객체-접근하기">인라인 핸들러에서 이벤트 객체 접근하기</h2>
<pre><code class="language-html">&lt;!-- 특수한 키워드인 $event 사용 --&gt;
&lt;button @click=&quot;warn(&#39;아직 양식을 제출할 수 없습니다.&#39;, $event)&quot;&gt;
  제출하기
&lt;/button&gt;

&lt;!-- 인라인 화살표 함수 사용 --&gt;
&lt;button @click=&quot;(event) =&gt; warn(&#39;아직 양식을 제출할 수 없습니다.&#39;, event)&quot;&gt;
  제출하기
&lt;/button&gt;</code></pre>
<h2 id="이벤트-수식어">이벤트 수식어</h2>
<ul>
<li>.stop</li>
<li>.prevent</li>
<li>.self</li>
<li>.capture</li>
<li>.once</li>
<li>.passive  </li>
</ul>
<pre><code class="language-html">&lt;!-- 수식어를 연결할 수 있습니다. --&gt;
&lt;a @click.stop.prevent=&quot;doThat&quot;&gt;&lt;/a&gt;

&lt;!-- 이벤트에 핸들러 없이 수식어만 사용할 수 있습니다. --&gt;
&lt;form @submit.prevent&gt;&lt;/form&gt;

&lt;!-- event.target이 엘리먼트 자신일 경우에만 핸들러가 실행됩니다. --&gt;
&lt;!-- 예를 들어 자식 엘리먼트에서 클릭 액션이 있으면 핸들러가 실행되지 않습니다. --&gt;
&lt;div @click.self=&quot;doThat&quot;&gt;...&lt;/div&gt;</code></pre>
<blockquote>
<p>수식어를 명시한 순서대로 관련 코드가 생성되기 때문에 수식어를 사용할 때 순서가 중요합니다.<br>따라서 <code>@click.prevent.self</code>를 사용하면 해당 엘리먼트 및 자식 엘리먼트가 클릭 되는 경우 실행되는 기본 동작을 방지합니다.<br>반면, <code>@click.self.prevent</code>는 해당 엘리먼트가 클릭 되는 경우에만 실행되는 기본 동작을 방지합니다.  </p>
</blockquote>
<p><code>.capture, .once 및 .passive</code> 수식어는 네이티브 addEventListener 메서드의 옵션을 반영합니다:  </p>
<pre><code class="language-html">&lt;!-- 이벤트 리스너를 추가할 때 캡처 모드 사용 --&gt;
&lt;!-- 내부 엘리먼트에서 클릭 이벤트 핸들러가 실행되기 전에, 여기에서 먼저 핸들러가 실행됩니다. --&gt;
&lt;div @click.capture=&quot;doThis&quot;&gt;...&lt;/div&gt;

&lt;!-- 클릭 이벤트는 단 한 번만 실행됩니다. --&gt;
&lt;a @click.once=&quot;doThis&quot;&gt;&lt;/a&gt;

&lt;!-- 핸들러 내 `event.preventDefault()`가 포함되었더라도 --&gt;
&lt;!-- 스크롤 이벤트의 기본 동작(스크롤)이 발생합니다.        --&gt;
&lt;div @scroll.passive=&quot;onScroll&quot;&gt;...&lt;/div&gt;</code></pre>
<blockquote>
<p>.passive 수식어는 일반적으로 모바일 장치의 성능 향상을 위해 터치 이벤트 리스너와 함께 사용됩니다.  </p>
</blockquote>
<h2 id="입력키-수식어">입력키 수식어</h2>
<p>KeyboardEvent.key를 통해 유효한 입력키 이름을 kebab-case로 변환하여 수식어로 사용할 수 있습니다:  </p>
<ul>
<li>.enter</li>
<li>.tab</li>
<li>.delete (&quot;Delete&quot; 및 &quot;Backspace&quot; 키 모두 캡처)</li>
<li>.esc</li>
<li>.space</li>
<li>.up</li>
<li>.down</li>
<li>.left</li>
<li>.right  </li>
</ul>
<h2 id="시스템-입력키-수식어">시스템 입력키 수식어</h2>
<ul>
<li>.ctrl</li>
<li>.alt</li>
<li>.shift</li>
<li>.meta  <blockquote>
<p>eyup 이벤트와 함께 사용되는 경우, 키가 눌려저 있어야 이벤트가 발생합니다  </p>
</blockquote>
</li>
</ul>
<h2 id="exact-수식어">.exact 수식어</h2>
<p>정확한 조합을 제어할 수 있습니다.  </p>
<h2 id="마우스-버튼-수식어">마우스 버튼 수식어</h2>
<ul>
<li>.left</li>
<li>.right</li>
<li>.middle  </li>
</ul>
<h1 id="form-입력-바인딩">Form 입력 바인딩</h1>
<p>checkedNames 배열은 항상 현재 체크된 순서대로 값을 포함합니다.<br>select option 처리시, iOS에서는 이 경우 변경 이벤트를 발생시키지 않기 때문에 비활성화된 옵션에 빈 값을 제공하는 것이 좋습니다. <code>value=&quot;&quot;</code><br>다중 선택(배열로 바인딩 됨)<br>v-model은 문자열이 아닌 값의 바인딩도 지원합니다!  </p>
<h2 id="수식어-1">수식어</h2>
<p>v-model은 각 input 이벤트 이후 데이터와 입력을 동기화합니다<br>대신 change 이벤트 이후에 동기화하기 위해 <code>.lazy</code> 수식어를 추가할 수 있습니다.<br>사용자 입력이 자동으로 숫자로 유형 변환되도록 하려면, v-model 수식어로 <code>.number</code>를 추가하면 됩니다:<br>값을 parseFloat()로 파싱할 수 없으면 원래 값이 대신 사용됩니다.<br>자동으로 트리밍되도록 하려면 v-model 수식어로 <code>.trim</code>을 추가  </p>
<h1 id="수명-주기-훅">수명 주기 훅</h1>
<p><img src="https://v3-docs.vuejs-korea.org/assets/lifecycle.d3fe54ca.png" alt="hook"></p>
<h1 id="감시자">감시자</h1>
<p><code>옵션 API</code>를 사용하는 경우, <code>watch</code> 옵션을 사용하여 반응형 속성이 변경될 때마다 함수를 실행<br><code>Composition API</code>를 사용하는 경우, <code>watch 함수</code>를 사용하여 반응형 속성이 변경될 때마다 함수를 실행할 수 있습니다:  </p>
<h2 id="깊은-감시자">깊은 감시자</h2>
<p>watch는 기본적으로 얕습니다.  </p>
<pre><code class="language-js">  watch: {
    someObject: {
      handler(newValue, oldValue) {
        // 참고:
        // someObject가 다른 객체로 교체되지 않는 한,
        // newValue와 oldValue는 같습니다.
        // 둘 다 동일한 객체를 참고하고 있기 때문입니다!
      },
      deep: true
    }
  }</code></pre>
<h2 id="열성적인-감시자">열성적인 감시자</h2>
<p>handler 함수와 <code>immediate: true</code> 옵션으로 구성된 객체를 사용해 감시자를 선언함으로써 콜백이 즉시 실행되도록 할 수 있습니다:  </p>
<h2 id="콜백-실행-타이밍">콜백 실행 타이밍</h2>
<p>개발자가 생성한 감시자 콜백은 Vue 컴포넌트가 업데이트되기 전에 실행<br>Vue에 의해 업데이트된 후 의 DOM을 감시자 콜백에서 접근하려면, <code>flush: &#39;post&#39;</code> 옵션을 지정해야 합니다:<br>동일한 틱 내에 여러 번 상태 변경 시 마다 동기적으로 콜백을 호출해야 하는 경우, <code>flush: &#39;sync&#39;</code> 옵션을 사용해야 합니다.  </p>
<h2 id="thiswatch">this.$watch()</h2>
<pre><code class="language-js">  created() {
    this.$watch(&#39;question&#39;, (newQuestion) =&gt; {
      // ...
    })
  }</code></pre>
<h2 id="감시자-중지하기">감시자 중지하기</h2>
<p>컴포넌트가 마운트 해제될 때 자동으로 중지<br>마운트 해제되기 전에 감시자를 중지해야 하는 경우  </p>
<pre><code class="language-js">const unwatch = this.$watch(&#39;foo&#39;, callback)

// ...나중에 감시자가 더 이상 필요하지 않을 때:
unwatch()</code></pre>
<h1 id="템플릿-참조">템플릿 참조</h1>
<p>ref 값은 this.$refs에 노출됩니다:<br>ref가 v-for 내부에서 사용되면 ref 값은 해당 엘리먼트를 포함하는 배열이 됩니다:  </p>
<h2 id="함수로-참조하기">함수로 참조하기</h2>
<pre><code class="language-html">&lt;input :ref=&quot;(el) =&gt; { /* el을 속성이나 ref에 할당 */ }&quot;&gt;</code></pre>
<p>엘리먼트가 마운트 해제되는 경우 인자는 null입니다.  </p>
<h2 id="컴포넌트에-ref-사용하기">컴포넌트에 ref 사용하기</h2>
<p>ref는 자식 컴포넌트에 사용할 수도 있습니다. 이 경우 ref는 컴포넌트 인스턴스를 참조합니다:<br>자식 컴포넌트의 <code>this</code>와 동일<br><code>expose</code> 옵션을 사용하여 하위 인스턴스에 대한 접근을 제한할 수 있습니다:  </p>
<h1 id="컴포넌트-기초">컴포넌트 기초</h1>
<p>앱이 중첩된 컴포넌트의 트리로 구성되는 것은 일반적입니다:  </p>
<ul>
<li>Props 전달하기</li>
<li>emits</li>
</ul>
<h2 id="슬롯이-있는-컨텐츠-배포">슬롯이 있는 컨텐츠 배포</h2>
<pre><code class="language-html">&lt;template&gt;
  &lt;div class=&quot;alert-box&quot;&gt;
    &lt;strong&gt;이것은 데모용 에러입니다.&lt;/strong&gt;
    &lt;slot /&gt;
  &lt;/div&gt;
&lt;/template&gt;</code></pre>
<h2 id="동적-컴포넌트">동적 컴포넌트</h2>
<pre><code class="language-html">&lt;!-- currentTab이 변경되면 컴포넌트가 변경됩니다 --&gt;
&lt;component :is=&quot;currentTab&quot;&gt;&lt;/component&gt;</code></pre>
<ul>
<li>등록된 컴포넌트의 이름 문자열</li>
<li>실제 가져온 컴포넌트 객체  <blockquote>
<p>내장된 <code>&lt;KeepAlive&gt;</code> 컴포넌트를 사용하여 비활성 컴포넌트를 &quot;활성&quot; 상태로 유지하도록 강제할 수 있습니다.  </p>
</blockquote>
</li>
</ul>
<h2 id="dom-템플릿-파싱-주의-사항">DOM 템플릿 파싱 주의 사항</h2>
<p>다음 소스의 문자열 템플릿을 사용하는 경우에는 적용되지 않습니다:  </p>
<ul>
<li>싱글 파일 컴포넌트(SFC)</li>
<li>인라인 템플릿 문자열(예: template: &#39;...&#39;)</li>
<li><code>&lt;script type=&quot;text/x-template&quot;&gt;</code>  </li>
</ul>
<h1 id="-컴포넌트-심화"><a href="https://v3-docs.vuejs-korea.org/guide/components/registration.html"># 컴포넌트 심화</a></h1>
<h1 id="컴포넌트-등록">컴포넌트 등록</h1>
<h2 id="전역-등록">전역 등록</h2>
<pre><code class="language-js">pp
  .component(&#39;ComponentA&#39;, ComponentA)
  .component(&#39;ComponentB&#39;, ComponentB)
  .component(&#39;ComponentC&#39;, ComponentC)</code></pre>
<ul>
<li>컴포넌트를 제거하는 것(일명 &quot;tree-shaking&quot;)을 방지합니다</li>
<li>대규모 앱에서 의존 관계를 덜 명확하게 만듭니다</li>
</ul>
<h2 id="로컬-등록">로컬 등록</h2>
<pre><code class="language-js">export default {
  components: {
    ComponentA
  }
}</code></pre>
<h2 id="컴포넌트-이름-표기법">컴포넌트 이름 표기법</h2>
<p>PascalCase 이름은 유효한 JavaScript 식별자입니다. 이렇게 하면 JavaScript에서 컴포넌트를 더 쉽게 가져오고 등록할 수 있습니다. IDE의 자동 완성 기능도 지원합니다.<br><code>&lt;PascalCase /&gt;</code>는 기본 HTML의 템플릿 엘리먼트가 아닌 Vue 컴포넌트임을 더 명확하게 합니다. 또한 Vue 컴포넌트를 사용자 정의 엘리먼트(웹 컴포넌트)와 구별합니다.<br>PascalCase를 사용하여 등록된 컴포넌트에 대한 kebab-case 태그 해석을 지원<br>MyComponent로 등록된 컴포넌트가 <code>&lt;MyComponent&gt;</code> 또는 <code>&lt;my-component&gt;</code>를 통해 템플릿에서 참조될 수 있음  </p>
<h1 id="props">Props</h1>
<h2 id="props-이름-케이싱">Props 이름 케이싱</h2>
<p>obj.camelCase와 같이 camelCase를 사용<br>camelCase로 선언된 props 속성일지라도 관례적으로 HTML 속성 표기법과 동일하게 kebab-case로 표기해서 사용하도록 해야 합니다<br><code>&lt;MyComponent greeting-message=&quot;안녕!&quot; /&gt;</code><br>어떠한 타입의 값도 prop로 전달할 수 있습니다  </p>
<h2 id="객체로-여러-속성-바인딩하기">객체로 여러 속성 바인딩하기</h2>
<p>객체의 모든 속성을 props로 전달하려면 <code>인자 없이 v-bind</code>를 사용할 수 있습니다  </p>
<pre><code class="language-html">&lt;BlogPost v-bind=&quot;post&quot; /&gt;
&lt;BlogPost :id=&quot;post.id&quot; :title=&quot;post.title&quot; /&gt;</code></pre>
<h2 id="단방향-데이터-흐름">단방향 데이터 흐름</h2>
<p>모든 props는 자식 속성과 부모 속성 사이에 <code>하향식 단방향 바인딩</code>을 형성<br>부모 컴포넌트가 업데이트될 때마다 자식 컴포넌트의 모든 props가 최신 값으로 업데이트  </p>
<h2 id="객체배열-props-변경에-관하여">객체/배열 props 변경에 관하여</h2>
<p>자식 컴포넌트는 바인딩된 prop을 변경할 수는 없지만, <code>객체 또는 배열의 중첩 속성을 변경할 수는 있습니다</code><br>가장 좋은 방법은 부모와 자식이 의도적으로 밀접하게 연결되어 있지 않는 한 이러한 변경을 피하는 것이며, 필요 시 자식은 부모가 변경을 수행할 수 있도록 <code>emit 이벤트</code>를 호출하는 방식으로 구현해야 합니다  </p>
<h2 id="prop-유효성-검사">Prop 유효성 검사</h2>
<pre><code class="language-js">    propE: {
      type: Object,
      // 객체 또는 배열 기본값은 팩토리 함수에서 반환되어야 합니다.
      // 함수는 컴포넌트에서 받은 rawProps를 인자로 받습니다.
      // (rawProps: 부모 컴포넌트에게 받은 props 전체 객체)
      default(rawProps) {
        return { message: &#39;안녕!&#39; }
      }
    },
    // 사용자 정의 유효성 검사 함수
    propF: {
      validator(value) {
        // 값은 다음 문자열 중 하나와 일치해야 합니다.
        return [&#39;성공&#39;, &#39;경고&#39;, &#39;위험&#39;].includes(value)
      }
    },</code></pre>
<blockquote>
<p>prop의 타입이 Boolean이고 누락된 경우, false가 기본값이 됩니다. 의도에 따라서 default 값 정의가 필요할 수 있습니다.  </p>
</blockquote>
<h2 id="실행-간-타입-체크">실행 간 타입 체크</h2>
<ul>
<li>String</li>
<li>Number</li>
<li>Boolean</li>
<li>Array</li>
<li>Object</li>
<li>Date</li>
<li>Function</li>
<li>Symbol  </li>
</ul>
<pre><code class="language-js">export default {
  props: {
    author: Person
  }
}
class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName
    this.lastName = lastName
  }
}</code></pre>
<blockquote>
<p>author prop 값이 <code>new Person</code>으로 생성되었는지 확인합니다.  </p>
</blockquote>
<h2 id="불리언-캐스팅">불리언 캐스팅</h2>
<pre><code class="language-html">&lt;!-- :disabled=&quot;true&quot; 같음 --&gt;
&lt;MyComponent disabled /&gt;

&lt;!-- :disabled=&quot;false&quot; 같음 --&gt;
&lt;MyComponent /&gt;</code></pre>
<p>예를 들어 prop이 여러 타입을 가질 수 있게 선언되었다면:<br>Boolean에 대한 캐스팅 규칙은 타입 선언 순서에 관계없이 적용됩니다.  </p>
<h1 id="컴포넌트-이벤트">컴포넌트 이벤트</h1>
<p>.once 수식어는 컴포넌트 이벤트 리스너에서도 지원됩니다.  </p>
<pre><code class="language-html">&lt;MyComponent @some-event.once=&quot;callback&quot; /&gt;</code></pre>
<p>camelCase 형식으로 이벤트를 발신했지만, 부모에서 kebab-case 표기로 리스너를 사용하여 이를 수신할 수 있습니다  </p>
<h2 id="이벤트-인자">이벤트 인자</h2>
<p>$emit()에서 이벤트 이름 뒤에 전달된 모든 추가 인자는 리스너로 전달됩니다. 예를 들어, <code>$emit(&#39;foo&#39;, 1, 2, 3)</code>을 사용하면 리스너 함수는 <code>세 개의 인자</code>를 받습니다.  \</p>
<h2 id="발신되는-이벤트-선언하기">발신되는 이벤트 선언하기</h2>
<pre><code class="language-js">export default {
  emits: [&#39;inFocus&#39;, &#39;submit&#39;]
}
export default {
  emits: [&#39;inFocus&#39;, &#39;submit&#39;],
  setup(props, { emit }) {
    emit(&#39;submit&#39;)
  }
}
export default {
  emits: {
    submit(payload) {
      // `true` 또는 `false` 값을 반환하여
      // 유효성 검사 통과/실패 여부를 알려줌

      // 페이로드는 전달되는 인자를 나타내는 것으로
      // `emit(&#39;submit&#39;, &#39;a&#39;, &#39;b&#39;, &#39;c&#39;)`와 같이 3개의 인자를 전달하는 경우,
      // `submit(pl1, pl2, pl3) { /* 유효성 검사 반환 로직 */ }`과 같이
      // 유효성 검사에 페이로드를 사용할 수 있습니다.
    }
  }
}</code></pre>
<blockquote>
<p>네이티브 이벤트(예: click)가 emits 옵션에 정의된 경우 리스너는 이제 컴포넌트에서 발생하는 click 이벤트만 수신 대기하고 네이티브 click 이벤트에 더 이상 응답하지 않습니다.  </p>
</blockquote>
<h2 id="v-model과-함께-사용하기">v-model과 함께 사용하기</h2>
<p>컴포넌트에서 사용될 때 v-model은 대신 다음을 수행합니다:  </p>
<pre><code class="language-html">&lt;CustomInput
  :modelValue=&quot;searchText&quot;
  @update:modelValue=&quot;newValue =&gt; searchText = newValue&quot;
/&gt;
&lt;CustomInput v-model=&quot;searchText&quot; /&gt;

&lt;!-- child --&gt;
&lt;input
:value=&quot;modelValue&quot;
@input=&quot;$emit(&#39;update:modelValue&#39;, $event.target.value)&quot;
/&gt;</code></pre>
<p>computed 속성을 사용  </p>
<pre><code class="language-html">&lt;script&gt;
export default {
  props: [&#39;modelValue&#39;],
  emits: [&#39;update:modelValue&#39;],
  computed: {
    value: {
      get() {
        return this.modelValue
      },
      set(value) {
        this.$emit(&#39;update:modelValue&#39;, value)
      }
    }
  }
}
&lt;/script&gt;

&lt;template&gt;
  &lt;input v-model=&quot;value&quot; /&gt;
&lt;/template&gt;</code></pre>
<h2 id="v-model-인자-변경">v-model 인자 변경</h2>
<pre><code class="language-html">&lt;MyComponent v-model:title=&quot;bookTitle&quot; /&gt;

&lt;script&gt;
export default {
  props: [&#39;title&#39;],
  emits: [&#39;update:title&#39;]
}
&lt;/script&gt;</code></pre>
<h2 id="v-model-다중-바인딩">v-model 다중 바인딩</h2>
<pre><code class="language-html">&lt;UserName
  v-model:first-name=&quot;first&quot;
  v-model:last-name=&quot;last&quot;
/&gt;</code></pre>
<h2 id="v-model-수식어-핸들링">v-model 수식어 핸들링</h2>
<pre><code class="language-html">&lt;MyComponent v-model:title.capitalize=&quot;myText&quot;&gt;

&lt;script&gt;
export default {
  props: {
    title: String,
    titleModifiers: {
      default: () =&gt; ({})
    }
  },
  emits: [&#39;update:title&#39;],
  created() {
    console.log(this.titleModifiers) // { capitalize: true }
  },
  methods: {
    emitValue(e) {
      let value = e.target.value
      if (this.titleModifiers.capitalize) {
        value = value.charAt(0).toUpperCase() + value.slice(1)
      }
      this.$emit(&#39;update:title&#39;, value)
    }
  }
}
&lt;/script&gt;</code></pre>
<h1 id="폴스루-속성">폴스루 속성</h1>
<h2 id="속성-상속">속성 상속</h2>
<p>속성 또는 v-on 이벤트 리스너 이지만, 이것을 받는 컴포넌트의 props 또는 emits에서 명시적으로 선언되지 않은 속성  </p>
<blockquote>
<p>class, style, id  </p>
</blockquote>
<h2 id="v-on-리스너-상속">v-on 리스너 상속</h2>
<p><code>&lt;button&gt;</code>에 이미 v-on으로 바인딩된 click 리스너가 있는 경우 부모/자식 두 리스너가 모두 트리거됩니다</p>
<h2 id="중첩된-컴포넌트-상속">중첩된 컴포넌트 상속</h2>
<p>전달되는 속성이 <code>&lt;MyButton&gt;</code>에서 props로 선언되었거나 emit으로 등록된 핸들러일 경우, <code>&lt;BaseButton&gt;</code>으로 전달되지 않습니다. <code>&lt;MyButton&gt;</code> 에서 명시적으로 선언되어 &quot;사로잡혔기(consumed)&quot; 때문입니다.<br>그러나 전달되는 속성이 <code>&lt;MyButton&gt;</code>의 템플릿에서 다시 선언된 경우, props나 핸들러로 허용될 수 있습니다.  </p>
<h2 id="속성-상속-비활성화">속성 상속 비활성화</h2>
<p><code>inheritAttrs: false</code>  </p>
<pre><code class="language-html">&lt;span&gt;폴스루 속성: {{ $attrs }}&lt;/span&gt;</code></pre>
<p>$attrs 객체에는 컴포넌트의 props 또는 emits 옵션으로 선언되지 않은 모든 속성(예: class, style, v-on 리스너 등)이 포함됩니다.  </p>
<p>class 및 v-on 리스너와 같은 폴스루 속성이 외부 <code>&lt;div&gt;</code>가 아닌 내부 <code>&lt;button&gt;</code>에 적용되기를 원할 수 있습니다. inheritAttrs: false로 설정하고, v-bind=&quot;$attrs&quot;로 이를 구현할 수 있습니다:  </p>
<pre><code class="language-html">&lt;div class=&quot;btn-wrapper&quot;&gt;
  &lt;button class=&quot;btn&quot; v-bind=&quot;$attrs&quot;&gt;클릭하기&lt;/button&gt;
&lt;/div&gt;</code></pre>
<h2 id="다중-루트-노드에서-속성-상속">다중 루트 노드에서 속성 상속</h2>
<p>$attrs가 명시적으로 바인딩된 경우, 경고가 표시되지 않습니다:</p>
<pre><code class="language-html">&lt;header&gt;...&lt;/header&gt;
&lt;main v-bind=&quot;$attrs&quot;&gt;...&lt;/main&gt;
&lt;footer&gt;...&lt;/footer&gt;</code></pre>
<h1 id="슬롯">슬롯</h1>
<p>슬롯 컨텐츠는 자식 컴포넌트의 데이터에 접근할 수 없습니다. 일반적으로 다음을 기억하십시오:  </p>
<blockquote>
<p>상위 템플릿의 모든 항목은 상위 범위에서 컴파일됩니다. 자식 템플릿의 모든 것은 자식 범위에서 컴파일됩니다.  </p>
</blockquote>
<h2 id="대체-컨텐츠">대체 컨텐츠</h2>
<pre><code class="language-html">&lt;button type=&quot;submit&quot;&gt;
  &lt;slot&gt;
    제출 &lt;!-- 대체 컨텐츠 --&gt;
  &lt;/slot&gt;
&lt;/button&gt;</code></pre>
<h2 id="이름이-있는-슬롯">이름이 있는 슬롯</h2>
<pre><code class="language-html">&lt;BaseLayout&gt;
  &lt;template #header&gt;
    &lt;h1&gt;다음은 페이지 제목일 수 있습니다.&lt;/h1&gt;
  &lt;/template&gt;

  &lt;!-- implicit default slot --&gt;
  &lt;p&gt;주요 내용에 대한 단락입니다.&lt;/p&gt;
  &lt;p&gt;그리고 또 하나.&lt;/p&gt;

  &lt;template #footer&gt;
    &lt;p&gt;다음은 연락처 정보입니다.&lt;/p&gt;
  &lt;/template&gt;

  &lt;!-- 동적 슬롯, 단축 문법 사용 --&gt;
  &lt;template #[dynamicSlotName]&gt;
&lt;/BaseLayout&gt;</code></pre>
<h2 id="범위가-지정된-슬롯">범위가 지정된 슬롯</h2>
<p> props를 컴포넌트에 전달하는 것처럼 속성을 슬롯 아울렛에 전달할 수 있습니다:  </p>
<pre><code class="language-html"> &lt;!-- &lt;MyComponent&gt; 템플릿 --&gt;
&lt;div&gt;
  &lt;slot :text=&quot;greetingMessage&quot; :count=&quot;1&quot;&gt;&lt;/slot&gt;
&lt;/div&gt;

&lt;MyComponent v-slot=&quot;{ text, count }&quot;&gt;
  {{ text }} {{ count }}
&lt;/MyComponent&gt;</code></pre>
<p>슬롯의 name은 예약되어 있기 때문에 props에 포함되지 않습니다.  </p>
<h2 id="렌더리스-컴포넌트">렌더리스 컴포넌트</h2>
<p>로직을 포함하기만 하고 자체적으로 아무 것도 렌더링하지 않는 컴포넌트를 생각해낼 수 있습니다. 시각적 출력은 범위가 지정된 슬롯인 사용될 컴포넌트에 완전히 위임됩니다. 이러한 유형의 컴포넌트를 <code>렌더리스 컴포넌트</code>라고 합니다.</p>
<h1 id="provide제공--inject주입">Provide(제공) / Inject(주입)</h1>
<p>먼 조상 컴포넌트의 무언가가 필요한 경우<br>하위 트리의 모든 컴포넌트는 깊이에 관계없이 상위 체인의 컴포넌트에서 제공(provide)하는 의존성을 주입(inject)할 수 있습니다.  </p>
<h2 id="provide">Provide</h2>
<pre><code class="language-js">export default {
  data() {
    return {
      message: &#39;안녕!&#39;
    }
  },
  provide() {
    // 함수 구문을 사용하여 `this`에 접근할 수 있습니다.
    return {
      message: this.message
    }
  }
}</code></pre>
<p>이것이 주입된 값을 반응형으로 만들지 않습니다.  </p>
<h2 id="앱-수준의-provide">앱 수준의 provide</h2>
<pre><code class="language-js">import { createApp } from &#39;vue&#39;

const app = createApp({})

app.provide(/* 키 */ &#39;message&#39;, /* 값 */ &#39;안녕!&#39;)</code></pre>
<h2 id="inject">Inject</h2>
<pre><code class="language-js">export default {
  inject: [&#39;message&#39;],
  inject: {
    /* 로컬 키 */ localMessage: {
      from: /* 주입된 키 */ &#39;message&#39;,
      default: &#39;이것은 기본 값 문자열 입니다.&#39;
    },
    user: {
      // 생성하는 데 비용이 많이 드는 기본이 아닌 값 또는 컴포넌트 인스턴스마다
      // 고유해야 하는 값에 대해 팩토리 함수를 사용합니다.
      default: () =&gt; ({ name: &#39;철수&#39; })
    }
  }
  data() {
    return {
      // 주입된 값을 기반으로 하는 초기 데이터
      fullMessage: this.message
    }
  }
}</code></pre>
<h2 id="반응형으로-만들기">반응형으로 만들기</h2>
<pre><code class="language-js">import { computed } from &#39;vue&#39;

export default {
  data() {
    return {
      message: &#39;안녕!&#39;
    }
  },
  provide() {
    return {
      // 계산된 속성을 명시적으로 제공
      message: computed(() =&gt; this.message)
    }
  }
}</code></pre>
<h1 id="비동기-컴포넌트">비동기 컴포넌트</h1>
<p>필요할 때만 서버에서 컴포넌트를 로드  </p>
<pre><code class="language-js">import { defineAsyncComponent } from &#39;vue&#39;

const AsyncComp = defineAsyncComponent(() =&gt; {
  return new Promise((resolve, reject) =&gt; {
    // ...서버에서 컴포넌트를 로드하는 로직
    resolve(/* 로드 된 컴포넌트 */)
  })
})
// ... 일반 컴포넌트처럼 `AsyncComp`를 사용</code></pre>
<pre><code class="language-js">import { defineAsyncComponent } from &#39;vue&#39;

const AsyncComp = defineAsyncComponent(() =&gt;
  import(&#39;./components/MyComponent.vue&#39;)
)
/* 컴포넌트 */
app.component(&#39;MyComponent&#39;, defineAsyncComponent(() =&gt;
  import(&#39;./components/MyComponent.vue&#39;)
))</code></pre>
<h2 id="로딩-및-에러-상태">로딩 및 에러 상태</h2>
<pre><code class="language-js">const AsyncComp = defineAsyncComponent({
  // 로더 함수
  loader: () =&gt; import(&#39;./Foo.vue&#39;),

  // 비동기 컴포넌트가 로드되는 동안 사용할 로딩 컴포넌트입니다.
  loadingComponent: LoadingComponent,
  // 로딩 컴포넌트를 표시하기 전에 지연할 시간. 기본값: 200ms
  delay: 200,

  // 로드 실패 시 사용할 에러 컴포넌트
  errorComponent: ErrorComponent,
  // 시간 초과 시, 에러 컴포넌트가 표시됩니다. 기본값: 무한대
  timeout: 3000
})</code></pre>
<h2 id="지연suspense-사용하기">지연(suspense) 사용하기</h2>
<p>비동기 컴포넌트는 내장 컴포넌트인 <code>&lt;Suspense&gt;</code>와 함께 사용할 수 있습니다.  </p>
<h1 id="재사용성"><a href="https://v3-docs.vuejs-korea.org/guide/reusability/composables.html">재사용성</a></h1>
<h1 id="컴포저블">컴포저블</h1>
<p>Vue 앱의 컨텍스트에서 컴포저블은 Vue <code>컴포지션 API를 활용하여 상태 저장 로직를 캡슐화하고 재사용하는 함수</code>입니다.  </p>
<h2 id="마우스-위치-추적기-예제">마우스 위치 추적기 예제</h2>
<pre><code class="language-js">// mouse.js
import { ref, onMounted, onUnmounted } from &#39;vue&#39;

// 관례상, 컴포저블 함수 이름은 &quot;use&quot;로 시작합니다.
export function useMouse() {
  // 컴포저블로 캡슐화된 내부에서 관리되는 상태
  const x = ref(0)
  const y = ref(0)

  // 컴포저블은 시간이 지남에 따라 관리되는 상태를 업데이트할 수 있습니다.
  function update(event) {
    x.value = event.pageX
    y.value = event.pageY
  }

  // 컴포저블은 또한 이것을 사용하는 컴포넌트의 수명주기에 연결되어
  // 사이드 이펙트를 설정 및 해제할 수 있습니다.
  onMounted(() =&gt; window.addEventListener(&#39;mousemove&#39;, update))
  onUnmounted(() =&gt; window.removeEventListener(&#39;mousemove&#39;, update))

  // 관리 상태를 반환 값으로 노출
  return { x, y }
}</code></pre>
<pre><code class="language-html">&lt;script setup&gt;
import { useMouse } from &#39;./mouse.js&#39;

const { x, y } = useMouse()
&lt;/script&gt;

&lt;template&gt;마우스 위치: {{ x }}, {{ y }}&lt;/template&gt;</code></pre>
<p>컴포넌트 내부처럼 컴포저블에서는 <code>컴포지션 API 함수</code>의 전체를 사용할 수 있습니다<br>하지만 컴포저블의 멋진 부분은 중첩할 수도 있다는 것입니다. <code>하나의 컴포저블 함수는 하나 이상의 &quot;다른 컴포저블 함수&quot;를 호출할 수 있습니다</code>.  
각 컴포넌트 인스턴스는 서로 간섭하지 않도록 x와 y 상태의 자체 복사본을 생성합니다  </p>
<pre><code class="language-js">// fetch.js
import { ref, isRef, unref, watchEffect } from &#39;vue&#39;

export function useFetch(url) {
  const data = ref(null)
  const error = ref(null)

  function doFetch() {
    // 가져오기 전에 상태 재설정..
    data.value = null
    error.value = null
    // unref()는 ref의 래핑을 해제합니다.
    fetch(unref(url))
      .then((res) =&gt; res.json())
      .then((json) =&gt; (data.value = json))
      .catch((err) =&gt; (error.value = err))
  }

  if (isRef(url)) {
    // 설정하기: 입력 URL이 ref인 경우 반응적 다시 가져오기
    watchEffect(doFetch)
  } else {
    // 그렇지 않으면 한 번만 가져 와서
    // 감시자의 오버 헤드를 피하합니다.
    doFetch()
  }

  return { data, error }
}
</code></pre>
<h2 id="관례와-모범-사례">관례와 모범 사례</h2>
<p>&quot;use&quot;로 시작하는 camelCase 이름으로 컴포저블 함수의 이름을 지정하는 것이 관례입니다.  </p>
<h3 id="입력-인자">입력 인자</h3>
<pre><code class="language-js">import { unref } from &#39;vue&#39;

function useFeature(maybeRef) {
  // 만약 mayRef가 실제로 ref라면,
  // mayRef.value가 반환될 것이고,
  // 그렇지 않을 경우, mayRef는 있는 그대로 반환될 것입니다.
  const value = unref(maybeRef)
}</code></pre>
<p>입력이 ref일 때 컴포저블이 반응 효과를 생성하는 경우, <code>watch()</code>를 사용하여 ref를 명시적으로 감시하거나, <code>watchEffect()</code> 내부에서 <code>unref()</code>를 호출하여 올바르게 추적되도록 하십시오.</p>
<h3 id="반환-값">반환 값</h3>
<p>컴포넌트에서 항상 ref 객체를 반환하여 반응성을 유지하면서 컴포넌트에서 구조화할 수 있도록 하는 것이 좋습니다<br>컴포저블 함수로 반환된 ref 상태를 감싸는 객체를 .value 접두사 없이 객체 속성처럼 사용하고 싶다면, 반환된 일반 객체를 <code>reactive()</code>로 래핑하여 ref가 래핑되지 않도록 할 수 있습니다:  </p>
<pre><code class="language-js">const mouse = reactive(useMouse())
// mouse.x는 원본 ref에 연결되어 있습니다.
console.log(mouse.x)</code></pre>
<h3 id="사이드-이펙트">사이드 이펙트</h3>
<blockquote>
<p>서버 사이드 렌더링(SSR)을 사용하는 앱에서 작업하는 경우, 마운트 후 수명주기 훅인 <code>onMounted()에서 DOM 관련 사이드 이펙트를 수행해야 합니다.</code> 이 훅은 브라우저에서만 호출되므로 내부 코드가 DOM에 접근할 수 있는지 알 수 있습니다.  </p>
</blockquote>
<blockquote>
<p><code>onUnmounted()에서 사이드 이펙트를 마무리 지었는지 확인하십시오</code>. 예를 들어, 컴포저블 코드에서 DOM 이벤트 리스너를 사용하는 경우, onUnmounted()에서 해당 리스너를 제거해야 합니다(useMouse() 예제에서 본 것처럼). useEventListener() 예제와 같이 자동으로 이를 수행하는 컴포저블 코드를 구성하는 것도 좋은 아이디어일 수 있습니다.  </p>
</blockquote>
<h3 id="제한사항">제한사항</h3>
<p>컴포저블은 <code>&lt;script setup&gt;</code> 또는 <code>setup()</code> 훅에서만 동기적으로 호출되어야 합니다. 어떤 경우에는 <code>onMounted()</code>와 같은 수명주기 훅에서 호출할 수도 있습니다.<br><code>&lt;script setup&gt;</code>은 await를 사용한 후 컴포저블 함수를 호출할 수 있는 유일한 곳입니다.  </p>
<h2 id="체계적인-코드를-위해-컴포저블로-추출하기">체계적인 코드를 위해 컴포저블로 추출하기</h2>
<p>컴포지션 API는 논리적 문제를 기반으로 컴포넌트 코드를 더 작은 기능으로 구성할 수 있는 완전한 유연성을 제공  </p>
<h2 id="옵션-api에서-컴포저블-적용">옵션 API에서 컴포저블 적용</h2>
<p>컴포저블 함수는 setup() 내에서 호출되어야 하고 반환된 바인딩은 this와 템플릿에 노출되도록 setup()에서 반환되어야 합니다:  </p>
<pre><code class="language-js">import { useMouse } from &#39;./mouse.js&#39;
import { useFetch } from &#39;./fetch.js&#39;

export default {
  setup() {
    const { x, y } = useMouse()
    const { data, error } = useFetch(&#39;...&#39;)
    return { x, y, data, error }
  },
  mounted() {
    // setup()에서 노출된 속성은 `this`에서 접근할 수 있습니다.
    console.log(this.x)
  }
  // ...다른 옵션 코드 로직
}</code></pre>
<h2 id="다른-기술과의-비교">다른 기술과의 비교</h2>
<h3 id="vs-mixins">vs. Mixins</h3>
<ol>
<li>어떤 인스턴스 속성이 어떤 mixins에 의해 주입되는지 명확하지 않아 구현을 추적하고 컴포넌트의 동작을 이해하기 어렵습니다</li>
<li>여러 mixins이 잠재적으로 동일한 속성 키를 등록하여 네임스페이스 충돌을 일으킬 수 있습니다.</li>
<li>여러 mixins은 공유 속성 키에 의존해야 하므로 암시적으로 결합됩니다.  <blockquote>
<p>Vue 3에서는 더 이상 mixins를 사용하지 않는 것이 좋습니다  </p>
</blockquote>
</li>
</ol>
<h3 id="vs-렌더리스-컴포넌트">vs. 렌더리스 컴포넌트</h3>
<p>렌더리스 컴포넌트에 비해 컴포저블의 주요 이점은 컴포저블이 <code>추가적인 컴포넌트 인스턴스 오버헤드를 발생시키지 않는다</code>는 것입니다.  </p>
<blockquote>
<p>권장 사항은 순수 로직을 재사용할 때 컴포저블을 사용하고 로직과 시각적 레이아웃을 모두 재사용할 때 컴포넌트를 사용하는 것입니다.  </p>
</blockquote>
<h3 id="vs-react-훅">vs. React 훅</h3>
<p>컴포지션 API는 부분적으로 React 훅에서 영감을 얻었고 Vue 컴포저블은 실제로 로직 구성 기능 측면에서 React 훅와 유사합니다.  </p>
<h2 id="추가적인-읽을거리">추가적인 읽을거리</h2>
<ul>
<li><a href="https://v3-docs.vuejs-korea.org/guide/extras/reactivity-in-depth.html">반응형 심화</a>: Vue의 반응형 시스템이 어떻게 작동하는지에 대한 심화 수준의 이해를 위해.</li>
<li><a href="https://v3-docs.vuejs-korea.org/guide/scaling-up/state-management.html">상태 관리</a>: 여러 컴포넌트가 공유하는 상태를 관리하는 패턴입니다.</li>
<li><a href="https://v3-docs.vuejs-korea.org/guide/scaling-up/testing.html#testing-composables">테스팅 컴포저블</a>: 단위 테스트 컴포저블에 대한 팁.</li>
<li><a href="https://vueuse.org/">VueUse</a>: 계속 증가하는 Vue 컴포저블 컬렉션입니다. 또한 소스 코드는 훌륭한 학습 자료입니다.</li>
</ul>
<h1 id="커스텀-디렉티브">커스텀 디렉티브</h1>
<p>커스텀 디렉티브는 원하는 기능을 직접 DOM 조작을 통해서만 달성할 수 있는 경우에만 사용해야 합니다.  </p>
<p>우리는 Vue에서 컴포넌트 기초와 컴포저블이라는 두 가지 형태의 코드 재사용을 도입했습니다. <code>컴포넌트는 주요 구성-요소(building-block)</code>이고, <code>컴포저블은 상태 저장 로직을 재사용하는 데 중점</code>을 둡니다<br><code>커스텀 디렉티브는 주로 일반 엘리먼트에 대한 저수준(low-level) DOM 접근과 관련된 로직을 재사용</code>하기 위한 것입니다.  </p>
<p><code>&lt;script setup&gt;에서 v 접두사로 시작하는 모든 camelCase 변수를 커스텀 디렉티브로 사용할 수 있습니다.</code> 위의 예에서 vFocus는 템플릿에서 v-focus로 사용할 수 있습니다.</p>
<p><code>&lt;script setup&gt;을 사용하지 않는 경우, directives 옵션</code>을 사용하여 커스텀 디렉티브를 등록할 수 있습니다:</p>
<p>앱 수준에서 커스텀 디렉티브를 전역적으로 등록하는 것도 일반적입니다:  </p>
<pre><code class="language-js">const app = createApp({})

// 모든 컴포넌트에서 v-focus를 사용할 수 있도록 합니다.
app.directive(&#39;focus&#39;, {
  /* ... */
})</code></pre>
<h2 id="디렉티브-훅">디렉티브 훅</h2>
<pre><code class="language-js">const myDirective = {
  // 바인딩된 엘리먼트의 속성 또는
  // 이벤트 리스너가 적용되기 전에 호출됩니다.
  created(el, binding, vnode, prevVnode) {
    // 인자에 대한 자세한 내용은 아래를 참고.
  },
  // 엘리먼트가 DOM에 삽입되기 직전에 호출됩니다.
  beforeMount(el, binding, vnode, prevVnode) {},
  // 바인딩된 엘리먼트의 부모 컴포넌트 및
  // 모든 자식 컴포넌트의 mounted 이후에 호출됩니다.
  mounted(el, binding, vnode, prevVnode) {},
  // 부모 컴포넌트의 updated 전에 호출됩니다.
  beforeUpdate(el, binding, vnode, prevVnode) {},
  // 바인딩된 엘리먼트의 부모 컴포넌트 및
  // 모든 자식 컴포넌트의 updated 이후에 호출됩니다.
  updated(el, binding, vnode, prevVnode) {},
  // 부모 컴포넌트의 beforeUnmount 이후에 호출됩니다.
  beforeUnmount(el, binding, vnode, prevVnode) {},
  // 부모 컴포넌트의 unmounted 전에 호출됩니다.
  unmounted(el, binding, vnode, prevVnode) {}
}</code></pre>
<h3 id="훅-인자">훅 인자</h3>
<ul>
<li>el: 디렉티브가 바인딩된 엘리먼트입니다. DOM을 직접 조작하는 데 사용할 수 있습니다.</li>
<li>binding: 다음 속성을 포함하는 객체입니다.<ul>
<li>value: 디렉티브에 전달된 값입니다. 예를 들어 v-my-directive=&quot;1 + 1&quot;에서 value는 2입니다.</li>
<li>oldValue: 이것은 beforeUpdate 및 updated에서만 사용할 수 있습니다. 값이 변경되었는지 여부에 관계없이 사용 가능합니다.</li>
<li>arg: 디렉티브에 전달된 인자(있는 경우). 예를 들어 v-my-directive:foo에서 인자는 &quot;foo&quot;입니다.</li>
<li>modifiers: 수식어가 있는 경우 수식어를 포함하는 객체입니다. 예를 들어 v-my-directive.foo.bar에서 수식어 객체는 { foo: true, bar: true }입니다.</li>
<li>instance: 디렉티브가 사용되는 컴포넌트의 인스턴스입니다.</li>
<li>dir: 디렉티브를 정의하는 객체</li>
</ul>
</li>
<li>vnode: 바인딩된 엘리먼트를 나타내는 기본 VNode.</li>
<li>prevNode: 이전 렌더링에서 바인딩된 엘리먼트를 나타내는 VNode입니다. beforeUpdate 및 updated 훅에서만 사용할 수 있습니다.  </li>
</ul>
<p><code>&lt;div v-example:[arg]=&quot;value&quot;&gt;&lt;/div&gt;</code><br>내장 디렉티브와 유사하게 커스텀 디렉티브 인자는 동적일 수 있습니다  </p>
<h2 id="간단하게-함수로-사용하기">간단하게 함수로 사용하기</h2>
<p>커스텀 디렉티브가 mounted 및 updated에 대해 동일한 동작을 갖는 것이 일반적이며, 다른 훅은 필요하지 않습니다. 이러한 경우 디렉티브를 객체가 아닌 함수로 정의할 수 있습니다:  </p>
<pre><code class="language-js">app.directive(&#39;demo&#39;, (el, binding) =&gt; {
  console.log(binding.value.color) // =&gt; &quot;white&quot;
  console.log(binding.value.text) // =&gt; &quot;안녕!&quot;
})</code></pre>
<h2 id="컴포넌트에서-사용">컴포넌트에서 사용</h2>
<p>커스텀 디렉티브는 <code>폴스루 속성</code>과 유사하게 항상 컴포넌트의 루트 노드에 적용됩니다.<br>속성과 달리 디렉티브는 v-bind=&quot;$attrs&quot;를 사용하여 다른 엘리먼트에 전달할 수 없습니다.<br>일반적으로 컴포넌트에 커스텀 디렉티브를 사용하는 것은 <strong>권장되지 않습니다</strong>.  </p>
<h1 id="플러그인">플러그인</h1>
<p>Vue에 앱 레벨 기능을 추가하는 자체 코드입니다.  </p>
<pre><code class="language-js">import { createApp } from &#39;vue&#39;
const app = createApp({})
app.use(myPlugin, {
  /* 선택적인 옵션 */
})

/* myPlugin.js */
const myPlugin = {
  install(app, options) {
    // 앱 환경설정
  }
}</code></pre>
<p>일반적인 시나리오  </p>
<ol>
<li><code>app.component()</code> 및 <code>app.directive()</code>를 사용하여 하나 이상의 전역 컴포넌트 또는 커스텀 디렉티브를 등록합니다.</li>
<li><code>app.provide()</code>를 호출하여 앱 전체에 리소스를 주입 가능하게 만듭니다.</li>
<li>일부 전역 인스턴스 속성 또는 메서드를 <code>app.config.globalProperties</code>에 첨부하여 추가합니다.</li>
<li>위 목록의 몇 가지를 조합해 무언가를 수행해야 하는 라이브러리(예: <code>vue-router</code>).</li>
</ol>
<h2 id="플러그인-작성하기-provide--inject-활용하기">플러그인 작성하기 (Provide / Inject 활용하기)</h2>
<pre><code class="language-js">// plugins/i18n.js
export default {
  install: (app, options) =&gt; {
    // 전역적으로 사용 가능한 $translate() 메서드 주입
    app.config.globalProperties.$translate = (key) =&gt; {
      // `key`를 경로로 사용하여
      // `options`에서 중첩 속성을 검색합니다.
      return key.split(&#39;.&#39;).reduce((o, i) =&gt; {
        if (o) return o[i]
      }, options)
    }

    app.provide(&#39;i18n&#39;, options)
  }
}</code></pre>
<pre><code class="language-html">&lt;script setup&gt;
import { inject } from &#39;vue&#39;

const i18n = inject(&#39;i18n&#39;)

console.log(i18n.greetings.hello)
&lt;/script&gt;</code></pre>
<h1 id="빌트인-컴포넌트"><a href="https://v3-docs.vuejs-korea.org/guide/built-ins/transition.html">빌트인 컴포넌트</a></h1>
<h1 id="트랜지션">트랜지션</h1>
<p>Vue는 상태 변화에 대응하기 위해 트랜지션 및 애니메이션 작업에 도움이 되는 두 가지 빌트인 컴포넌트를 제공합니다:  </p>
<h2 id="transition-컴포넌트"><Transition> 컴포넌트</h2>
<p>해당 애니메이션은 다음 중 하나의 조건에 충족하면 발생합니다:  </p>
<ul>
<li>v-if를 통한 조건부 렌더링</li>
<li>v-show를 통한 조건부 표시</li>
<li>스페셜 엘리먼트 <code>&lt;component&gt;</code>를 통해 전환되는 동적 컴포넌트  </li>
</ul>
<pre><code class="language-html">&lt;button @click=&quot;show = !show&quot;&gt;토글&lt;/button&gt;
&lt;Transition&gt;
  &lt;p v-if=&quot;show&quot;&gt;안녕&lt;/p&gt;
&lt;/Transition&gt;</code></pre>
<pre><code class="language-css">.v-enter-active,
.v-leave-active {
  transition: opacity 0.5s ease;
}

.v-enter-from,
.v-leave-to {
  opacity: 0;
}</code></pre>
<blockquote>
<p><code>&lt;Transition&gt;</code>은 슬롯 컨텐츠로 단일 엘리먼트 또는 컴포넌트만 지원합니다. 컨텐츠가 컴포넌트인 경우, 컴포넌트에는 단일 루트 엘리먼트만 있어야 합니  </p>
</blockquote>
<h2 id="css-기반-트랜지션">CSS 기반 트랜지션</h2>
<ol>
<li><code>v-enter-from</code>: 진입 시작 상태. 엘리먼트가 삽입되기 전에 추가되고, 엘리먼트가 삽입되고 1 프레임 후 제거됩니다.</li>
<li><code>v-enter-active</code>: 진입 활성 상태. 모든 진입 상태에 적용됩니다. 엘리먼트가 삽입되기 전에 추가되고, 트랜지션/애니메이션이 완료되면 제거됩니다. 이 클래스는 진입 트랜지션에 대한 지속 시간, 딜레이 및 이징(easing) 곡선을 정의하는 데 사용할 수 있습니다.</li>
<li><code>v-enter-to</code>: 진입 종료 상태. 엘리먼트가 삽입된 후 1 프레임 후 추가되고(동시에 v-enter-from이 제거됨), 트랜지션/애니메이션이 완료되면 제거됩니다.</li>
<li><code>v-leave-from</code>: 진출 시작 상태. 진출 트랜지션이 트리거되면 즉시 추가되고 1 프레임 후 제거됩니다.</li>
<li><code>v-leave-active</code>: 진출 활성 상태. 모든 진출 상태에 적용됩니다. 진출 트랜지션이 트리거되면 즉시 추가되고, 트랜지션/애니메이션이 완료되면 제거됩니다. 이 클래스는 진출 트랜지션에 대한 지속 시간, 딜레이 및 이징 곡선을 정의하는 데 사용할 수 있습니다.</li>
<li><code>v-leave-to</code>: 진출 종료 상태. 진출 트랜지션이 트리거된 후 1 프레임이 추가되고(동시에 v-leave-from이 제거됨), 트랜지션/애니메이션이 완료되면 제거됩니다.  </li>
</ol>
<h3 id="트랜지션-이름-지정하기">트랜지션 이름 지정하기</h3>
<p><code>&lt;Transition name=&quot;fade&quot;&gt;</code><br><code>.fade-enter-active,</code>  </p>
<h3 id="css-애니메이션">CSS 애니메이션</h3>
<p>네이티브 CSS 애니메이션은 CSS 트랜지션과 동일한 방식으로 적용되지만, <code>엘리먼트가 삽입된 직후에 *-enter-from이 제거되지 않고, animationend 이벤트에서 제거된다는 차이점</code>이 있습니다.  </p>
<h3 id="커스텀-트랜지션-클래스">커스텀 트랜지션 클래스</h3>
<pre><code class="language-html">&lt;!-- Animate.css가 페이지에 포함되어 있다고 가정합니다. --&gt;
&lt;Transition
  name=&quot;custom-classes&quot;
  enter-active-class=&quot;animate__animated animate__tada&quot;
  leave-active-class=&quot;animate__animated animate__bounceOutRight&quot;
&gt;
  &lt;p v-if=&quot;show&quot;&gt;안녕&lt;/p&gt;
&lt;/Transition&gt;</code></pre>
<h3 id="트랜지션과-애니메이션을-같이-사용하기">트랜지션과 애니메이션을 같이 사용하기</h3>
<p>transitionend 또는 animationend가 될 수 있습니다. 둘 중 하나만 사용하는 경우<br><code>&lt;Transition type=&quot;animation&quot;&gt;...&lt;/Transition&gt;</code>  </p>
<h3 id="성능-고려사항">성능 고려사항</h3>
<ul>
<li>애니메이션 진행중에 문서 레이아웃에 영향을 미치지 않으므로 <code>모든 애니메이션 프레임에서 값비싼 CSS 레이아웃 계산을 트리거하지 않습니다.</code> (height 또는 margin)</li>
<li>대부분의 최신 브라우저는 <code>transform</code>에 애니메이션을 적용할 때 <code>GPU 하드웨어 가속을 활용</code>할 수 있습니다.  </li>
</ul>
<h2 id="javascript-훅">JavaScript 훅</h2>
<pre><code class="language-html">&lt;Transition
  :css=&quot;false&quot;
  @before-enter=&quot;onBeforeEnter&quot;
  @enter=&quot;onEnter&quot;
  @after-enter=&quot;onAfterEnter&quot;
  @enter-cancelled=&quot;onEnterCancelled&quot;
  @before-leave=&quot;onBeforeLeave&quot;
  @leave=&quot;onLeave&quot;
  @after-leave=&quot;onAfterLeave&quot;
  @leave-cancelled=&quot;onLeaveCancelled&quot;
&gt;</code></pre>
<p>JavaScript 전용 트랜지션을 사용할 때 일반적으로 <code>:css=&quot;false&quot; prop</code>을 추가하는 것이 좋습니다.  </p>
<h2 id="재사용-가능한-트랜지션">재사용 가능한 트랜지션</h2>
<pre><code class="language-html">  &lt;Transition
    name=&quot;my-transition&quot;
    @enter=&quot;onEnter&quot;
    @leave=&quot;onLeave&quot;&gt;
    &lt;slot&gt;&lt;/slot&gt; &lt;!-- 슬롯 컨텐츠 전달 --&gt;
  &lt;/Transition&gt;</code></pre>
<h2 id="등장-트랜지션">등장 트랜지션</h2>
<p>노드의 초기 렌더링에도 트랜지션을 적용 <code>&lt;Transition appear&gt;</code>  </p>
<h2 id="엘리먼트-간-트랜지션">엘리먼트 간 트랜지션</h2>
<pre><code class="language-html">&lt;Transition&gt;
  &lt;button v-if=&quot;docState === &#39;saved&#39;&quot;&gt;수정&lt;/button&gt;
  &lt;button v-else-if=&quot;docState === &#39;edited&#39;&quot;&gt;저장&lt;/button&gt;
  &lt;button v-else-if=&quot;docState === &#39;editing&#39;&quot;&gt;취소&lt;/button&gt;
&lt;/Transition&gt;</code></pre>
<h2 id="트랜지션-모드">트랜지션 모드</h2>
<p>진출 시 엘리먼트가 먼저 애니메이션 처리되고 진입 시 엘리먼트가 진출 애니메이션이 완료된 이후에 삽입되기를 원할 수 있습니다. <code>&lt;Transition mode=&quot;out-in&quot;&gt;</code><br><code>&lt;Transition&gt;</code>은 자주 사용되지는 않지만 <code>mode=&quot;in-out&quot;</code>도 지원합니다.  </p>
<h2 id="컴포넌트-간-트랜지션">컴포넌트 간 트랜지션</h2>
<p><code>&lt;Transition&gt;</code>은 동적 컴포넌트에서도 사용할 수 있습니다:  </p>
<pre><code class="language-html">&lt;Transition name=&quot;fade&quot; mode=&quot;out-in&quot;&gt;
  &lt;component :is=&quot;activeComponent&quot;&gt;&lt;/component&gt;
&lt;/Transition&gt;</code></pre>
<h2 id="동적-트랜지션">동적 트랜지션</h2>
<p><code>&lt;Transition :name=&quot;transitionName&quot;&gt;</code>  </p>
<h1 id="트랜지션-그룹">트랜지션 그룹</h1>
<p>목록에서 렌더링되는 엘리먼트 또는 컴포넌트의 삽입, 제거 및 순서 변경을 애니메이션으로 만들기 위해 설계된 빌트인 컴포넌트  </p>
<h2 id="transition과의-차이점"><code>&lt;Transition&gt;</code>과의 차이점</h2>
<ul>
<li>기본적으로 래퍼 엘리먼트를 렌더링하지 않습니다. 그러나 tag prop으로 렌더링할 엘리먼트를 지정할 수 있습니다.</li>
<li>트랜지션 모드는 더 이상 상호 배타적인 엘리먼트를 사용하지 않기 때문에 사용할 수 없습니다.</li>
<li>내부 엘리먼트는 고유한 key 속성을 필수로 가져야 합니다.</li>
<li>CSS 트랜지션 클래스는 그룹/컨테이너 자체가 아닌 목록의 개별 엘리먼트에 적용됩니다.  </li>
</ul>
<pre><code class="language-html">&lt;TransitionGroup name=&quot;list&quot; tag=&quot;ul&quot;&gt;
  &lt;li v-for=&quot;item in items&quot; :key=&quot;item&quot;&gt;
    {{ item }}
  &lt;/li&gt;
&lt;/TransitionGroup&gt;</code></pre>
<h2 id="이동-트랜지션">이동 트랜지션</h2>
<pre><code class="language-css">.list-move, /* 움직이는 엘리먼트에 트랜지션 적용 */
.list-enter-active,
.list-leave-active {
  transition: all 0.5s ease;
}

.list-enter-from,
.list-leave-to {
  opacity: 0;
  transform: translateX(30px);
}

/* 이동 애니메이션을 올바르게 계산할 수 있도록
   레이아웃 흐름에서 나머지 항목을 꺼내기. */
.list-leave-active {
  position: absolute;
}</code></pre>
<h2 id="시차가-있는-목록-트랜지션">시차가 있는 목록 트랜지션</h2>
<p>GreenSock 라이브러리를 사용  </p>
<pre><code class="language-js">function onEnter(el, done) {
  gsap.to(el, {
    opacity: 1,
    height: &#39;1.6em&#39;,
    delay: el.dataset.index * 0.15,
    onComplete: done
  })
}</code></pre>
<h1 id="keepalive">KeepAlive</h1>
<p><code>&lt;KeepAlive&gt;</code>는 여러 컴포넌트 간에 동적으로 전환될 때, 컴포넌트 인스턴스를 조건부로 캐시할 수 있는 빌트인 컴포넌트입니다.  </p>
<h2 id="기본-사용법">기본 사용법</h2>
<p>인스턴스가 비활성 상태인 경우에도 상태가 보존되기를 원합니다  </p>
<pre><code class="language-html">&lt;!-- 비활성 컴포넌트가 캐시됩니다! --&gt;
&lt;KeepAlive&gt;
  &lt;component :is=&quot;activeComponent&quot; /&gt;
&lt;/KeepAlive&gt;</code></pre>
<h2 id="include--exclude">Include / Exclude</h2>
<p>내부의 모든 컴포넌트 인스턴스를 캐시, include 및 exclude props를 통해 이 동작을 사용자 정의할 수 있습니다.<br><code>props 모두 쉼표로 구분된 문자열, RegExp(정규식) 또는 이 두 유형 중 하나를 포함하는 배열</code>이 될 수 있습니다:  </p>
<h2 id="최대-캐시-인스턴스">최대 캐시 인스턴스</h2>
<p><code>max</code> props를 통해 캐시할 수 있는 컴포넌트 인스턴스의 최대 수를 제한할 수 있습니다.<br>max가 지정되면 <code>&lt;KeepAlive&gt;</code>는 LRU(Least recently used) 캐시처럼 작동합니다.  </p>
<h2 id="캐시된-인스턴스의-수명주기">캐시된 인스턴스의 수명주기</h2>
<p>마운트 해제되는 대신 비활성화됨 상태가 됩니다<br>kept-alive 컴포넌트는 <code>onActivated()</code> 및 <code>onDeactivated()</code>를 사용하여 이 두 가지 상태에 대한 수명 주기 훅을 등록할 수 있습니다:</p>
<h1 id="텔레포트">텔레포트</h1>
<p>컴포넌트 템플릿의 일부를 해당 컴포넌트의 DOM 계층 외부의 DOM 노드로 &quot;이동&quot;할 수 있게 해주는 빌트인 컴포넌트입니다.  </p>
<p>템플릿의 일부가 논리적으로 여기에 속하지만, 시각적인 관점에서 Vue 앱 외부의 다른 DOM 어딘가에 표시되어야 합니다.  </p>
<pre><code class="language-html">&lt;button @click=&quot;open = true&quot;&gt;모달 열기&lt;/button&gt;

&lt;Teleport to=&quot;body&quot;&gt;
  &lt;div v-if=&quot;open&quot; class=&quot;modal&quot;&gt;
    &lt;p&gt;짜자잔~ 모달입니다!&lt;/p&gt;
    &lt;button @click=&quot;open = false&quot;&gt;닫기&lt;/button&gt;
  &lt;/div&gt;
&lt;/Teleport&gt;</code></pre>
<h2 id="컴포넌트와-함께-사용하기">컴포넌트와 함께 사용하기</h2>
<p><code>&lt;Teleport&gt;</code>에 컴포넌트가 포함되어 있으면, 해당 컴포넌트는 논리적으로 <code>&lt;Teleport&gt;</code>를 포함하는 부모 컴포넌트의 자식으로 유지됩니다. Props 전달() 및 이벤트 발신(emit)은 계속 동일한 방식으로 작동합니다.  </p>
<h2 id="텔레포트-비활성화">텔레포트 비활성화</h2>
<p><code>&lt;Teleport :disabled=&quot;isMobile&quot;&gt;</code>  </p>
<h2 id="같은-대상에-여러-텔레포트-사용">같은 대상에 여러 텔레포트 사용</h2>
<pre><code class="language-html">&lt;Teleport to=&quot;#modals&quot;&gt;
  &lt;div&gt;A&lt;/div&gt;
&lt;/Teleport&gt;
&lt;Teleport to=&quot;#modals&quot;&gt;
  &lt;div&gt;B&lt;/div&gt;
&lt;/Teleport&gt;</code></pre>
<h1 id="suspense">Suspense</h1>
<blockquote>
<p><code>&lt;Suspense&gt;</code> 는 실험적인 기능입니다. 아직 개발이 완료된 안정된 상태가 아니며 추후 AP가 변경 될 수 있습니다.  </p>
</blockquote>
<p><code>&lt;Suspense&gt;</code> 컴포넌트는 이러한 중첩된 비동기 의존성이 해결될 때까지 최상위에서 로드/에러 상태를 표시할 수 있는 기능을 제공합니다.  </p>
<h2 id="비동기-setup">비동기 setup()</h2>
<pre><code class="language-js">export default {
  async setup() {
    const res = await fetch(...)
    const posts = await res.json()
    return {
      posts
    }
  }
}</code></pre>
<h2 id="로딩-상태">로딩 상태</h2>
<p><code>#default</code> 와 <code>#fallback</code> 이라는 두 개의 슬롯이 있습니다.   </p>
<pre><code class="language-html">&lt;Suspense&gt;
  &lt;!-- 컴포넌트와 중첩된 비동기 의존성 --&gt;
  &lt;Dashboard /&gt;

  &lt;!-- #fallback 슬롯을 통한 로딩 상태 --&gt;
  &lt;template #fallback&gt;
    로딩중...
  &lt;/template&gt;
&lt;/Suspense&gt;</code></pre>
<h2 id="이벤트">이벤트</h2>
<p><code>pending</code> 이벤트는 보류 상태에 들어갈 때 발생합니다. <code>resolve</code> 이벤트는 새 컨텐츠가 default 슬롯에서 해결되면 발생합니다. <code>fallback</code> 슬롯의 컨텐츠가 표시될 때 fallback 이벤트가 시작됩니다.  </p>
<h2 id="에러-처리">에러 처리</h2>
<p><code>&lt;Suspense&gt;</code> 는 현재 컴포넌트 자체를 통해 에러를 처리하지 않습니다. 그러나 errorCaptured 옵션 또는 onErrorCaptured() 훅을 사용하여 <code>&lt;Suspense&gt;</code> 의 부모 컴포넌트에서 비동기 에러를 캡처하고 처리할 수 있습니다.  </p>
<h2 id="다른-컴포넌트와-결합">다른 컴포넌트와 결합</h2>
<pre><code class="language-html">&lt;RouterView v-slot=&quot;{ Component }&quot;&gt;
  &lt;template v-if=&quot;Component&quot;&gt;
    &lt;Transition mode=&quot;out-in&quot;&gt;
      &lt;KeepAlive&gt;
        &lt;Suspense&gt;
          &lt;!-- 메인 컨텐츠 --&gt;
          &lt;component :is=&quot;Component&quot;&gt;&lt;/component&gt;

          &lt;!-- 로딩 상태 --&gt;
          &lt;template #fallback&gt;
            로딩중...
          &lt;/template&gt;
        &lt;/Suspense&gt;
      &lt;/KeepAlive&gt;
    &lt;/Transition&gt;
  &lt;/template&gt;
&lt;/RouterView&gt;</code></pre>
<h1 id="확장하기"><a href="https://v3-docs.vuejs-korea.org/guide/scaling-up/sfc.html">확장하기</a></h1>
<h1 id="싱글-파일-컴포넌트-1">싱글 파일 컴포넌트</h1>
<p>하나의 파일에서 컴포넌트의 뷰, 로직 및 스타일을 캡슐화하고 배치합니다  </p>
<h2 id="vue-cli">Vue CLI</h2>
<p>Vue CLI는 웹팩 기반의 Vue 공식 툴체인입니다<br>특정 웹팩 전용 기능에 의존하는 유지보수 상태가 아니라면, Vite로 새 프로젝트를 시작하는 것이 좋습니다.   </p>
<h3 id="브라우저-내-템플릿-편집에-대한-참고-사항">브라우저 내 템플릿 편집에 대한 참고 사항</h3>
<p>클라이언트 번들 크기를 줄이기 위해 Vue는 다양한 사용 사례에 최적화된 다른 &quot;빌드&quot;를 제공합니다.  </p>
<ul>
<li><code>vue.runtime.*</code>으로 시작하는 빌드 파일은 런타임 전용 빌드입니다. 컴파일러를 포함하지 않습니다. 이러한 빌드를 사용할 때 모든 템플릿은 빌드 과정을 통해 사전 컴파일되어야 합니다.</li>
<li><code>.runtime</code>을 포함하지 않는 빌드 파일은 전체 빌드입니다. 컴파일러를 포함하고 브라우저에서 직접 템플릿 컴파일을 지원합니다. 그러나 페이로드가 ~14kb 증가합니다.  </li>
</ul>
<h2 id="ide-지원">IDE 지원</h2>
<p>권장 IDE 설정은 VSCode + Volar입니다.  </p>
<h2 id="타입스크립트">타입스크립트</h2>
<p>Volar는 템플릿 표현식 및 교차 컴포넌트 props 유효성 검사를 포함하여 <code>&lt;script lang=&quot;ts&quot;&gt;</code> 블록을 사용하여 SFC에 대한 유형 검사를 제공합니다.  </p>
<h1 id="라우팅">라우팅</h1>
<p>대부분의 싱글 페이지 앱(SPA)의 경우 공식적으로 지원되는 <code>vue-router</code> 라이브러리를 사용하는 것이 좋습니다.<br>브라우저 <code>hashchange 이벤트</code>를 수신하거나 <code>History API</code>를 사용하여 현재 컴포넌트 상태를 업데이트할 수 있습니다.  </p>
<pre><code class="language-html">&lt;script setup&gt;
import { ref, computed } from &#39;vue&#39;
import Home from &#39;./Home.vue&#39;
import About from &#39;./About.vue&#39;
import NotFound from &#39;./NotFound.vue&#39;

const routes = {
  &#39;/&#39;: Home,
  &#39;/about&#39;: About
}

const currentPath = ref(window.location.hash)

window.addEventListener(&#39;hashchange&#39;, () =&gt; {
  currentPath.value = window.location.hash
})

const currentView = computed(() =&gt; {
  return routes[currentPath.value.slice(1) || &#39;/&#39;] || NotFound
})
&lt;/script&gt;

&lt;template&gt;
  &lt;a href=&quot;#/&quot;&gt;Home&lt;/a&gt; |
  &lt;a href=&quot;#/about&quot;&gt;About&lt;/a&gt; |
  &lt;a href=&quot;#/non-existent-path&quot;&gt;잘못된 링크&lt;/a&gt;
  &lt;component :is=&quot;currentView&quot; /&gt;
&lt;/template&gt;</code></pre>
<h1 id="상태-관리">상태 관리</h1>
<h2 id="반응형-api를-통한-간단한-상태-관리">반응형 API를 통한 간단한 상태 관리</h2>
<p>여러 인스턴스에서 공유해야 하는 상태가 있는 경우, <code>reactive()</code>를 사용하여 반응형 객체를 만든 다음 여러 컴포넌트에서 가져갈 수 있습니다.  </p>
<pre><code class="language-js">// store.js
import { reactive } from &#39;vue&#39;

export const store = reactive({
  count: 0,
  increment() {
    this.count++
  }
})</code></pre>
<pre><code class="language-html">&lt;!-- ComponentA.vue --&gt;
&lt;script setup&gt;
import { store } from &#39;./store.js&#39;
&lt;/script&gt;

&lt;template&gt;
  &lt;button @click=&quot;store.increment()&quot;&gt;
    B 컴포넌트에서: {{ store.count }}
  &lt;/button&gt;
&lt;/template&gt;</code></pre>
<h2 id="ssr-고려-사항">SSR 고려 사항</h2>
<p><code>서버 사이드 렌더링(SSR)</code>을 활용하는 앱을 구축하는 경우, 위 패턴은 store가 여러 요청에서 공유되는 싱글톤이기 때문에 문제를 일으킬 수 있습니다. 이것에 대한 자세한 내용은 SSR 가이드에서 설명합니다.  </p>
<h2 id="pinia피니아-공식-상태관리-라이브러리">Pinia(피니아: 공식 상태관리 라이브러리)</h2>
<p>Vuex와 비해 Pinia는 더 간단한 API를 제공하고, Composition-API 스타일의 API를 제공하며, 가장 중요한 것은 TypeScript와 함께 사용할 때 견고한 유형 추론을 지원합니다.  </p>
<h1 id="테스트">테스트</h1>
<h2 id="테스트-유형">테스트 유형</h2>
<ul>
<li><code>단위</code>: 주어진 함수, 클래스 또는 컴포저블에 제공된 입력 정보가 의도하는 출력 또는 사이드 이팩트를 생성하는지 확인합니다.</li>
<li><code>컴포넌트</code>: 컴포넌트가 마운트, 렌더링, 상호 작용이 의도대로 작동하는지 확인합니다. 이러한 테스트는 단위 테스트보다 더 많은 코드를 가져오고 더 복잡하며 실행하는 데 더 많은 시간이 필요합니다.</li>
<li><code>End-to-end</code>: 여러 페이지에 걸쳐 있는 기능을 확인하고, 프로덕션으로 빌드되는 Vue 앱처럼 실제 네트워크 요청을 합니다. 이러한 테스트에는 종종 데이터베이스 또는 기타 백엔드를 구축하는 작업이 포함됩니다.  </li>
</ul>
<h2 id="단위-테스트">단위 테스트</h2>
<p>단위 테스트는 일반적으로 단일 함수, 클래스, 컴포저블 또는 모듈을 다룹니다.<br>단위 테스트는 일반적으로 UI 렌더링, 네트워크 요청 또는 기타 환경 문제를 포함하지 않는 자체적으로 해야 할 일에 대한 논리, 컴포넌트, 클래스, 모듈 또는 함수에 적용됩니다.<br>Vue 관련 기능을 단위 테스트하는 두 가지 경우가 있습니다.  </p>
<ol>
<li>컴포저블</li>
<li>컴포넌트<br>예를 들어, 컴포넌트 테스트는 프로그래밍 방식으로 컴포넌트와 상호 작용하는 대신 사용자가 엘리먼트를 클릭하는 것과 같아야 합니다.  </li>
</ol>
<p><code>Vitest</code>와 브라우저 기반 러너의 주요 차이점은 <code>속도와 실행 컨텍스트</code>입니다. 간단히 말해서, <code>Cypress</code>와 같은 브라우저 기반 러너는 Vitest와 같은 노드 기반 러너가 포착할 수 없는 문제(예: 스타일 문제, 실제 네이티브 DOM 이벤트, 쿠키, 로컬 스토리지 및 네트워크 에러)를 포착할 수 있지만, 브라우저 기반 러너는 브라우저를 열고 스타일시트를 컴파일하는 등의 작업을 수행하기 때문에 Vitest보다 훨씬 느립니다. Cypress는 컴포넌트 테스트를 지원하는 브라우저 기반 러너입니다.  </p>
<h3 id="마운팅-라이브러리">마운팅 라이브러리</h3>
<p>앱의 테스트 우선 순위와 초점이 더 잘 맞기 때문에 앱의 컴포넌트를 테스트할 때 <code>@testing-library/vue</code>를 사용하는 것이 좋습니다. Vue 전용 내부 테스트가 필요한 고급 컴포넌트를 빌드하는 경우에만 @vue/test-utils를 사용하세요.  </p>
<h2 id="e2e-테스트">E2E 테스트</h2>
<p>E2E 테스트는 프로덕션으로 빌드된 Vue 앱에 대해 네트워크 요청을 필요로하는 다중 페이지 앱 동작에 중점을 둡니다.  </p>
<h3 id="브라우저-간-테스트">브라우저 간 테스트</h3>
<p>E2E 테스트의 주요 이점 중 하나는 여러 브라우저에서 앱을 테스트할 수 있다는 것입니다.<br>전반적으로 우리는 <code>Cypress</code>가 유익한 UI, 뛰어난 디버깅 가능성, 빌트인 검증 및 stubs, 내결함성, 병렬화 및 스냅샷과 같은 기능을 갖춘 가장 완벽한 E2E 솔루션을 제공한다고 믿습니다.  </p>
<h1 id="서버-사이드-렌더링-ssr">서버 사이드 렌더링 (SSR)</h1>
<h2 id="왜-ssr-일까요">왜 SSR 일까요?</h2>
<ul>
<li><p><code>컨텐츠에 도달하는 시간 단축</code>: 인터넷 속도가 느리거나 기기가 느린 경우 더 두드러집니다. 서버 렌더링 마크업은 모든 JavaScript가 다운로드 및 실행되어 표시될 때까지 기다릴 필요가 없으므로 사용자가 완전히 렌더링된 페이지를 더 빨리 볼 수 있습니다. 또한 데이터 가져오기는 초기 방문을 위해 서버 측에서 수행되므로 클라이언트보다 데이터베이스에 더 빠르게 연결할 수 있습니다. 이는 일반적으로 개선된 Core Web Vitals 측정항목과 더 나은 UX를 가져오며, 컨텐츠에 도달하는 시간이 전환율과 직접적으로 관련된 앱에 중요할 수 있습니다.</p>
</li>
<li><p><code>통합 유지보수 모델</code>: 백엔드 템플릿 시스템과 프론트엔드 프레임워크 사이를 왔다 갔다 하는 대신, 전체 앱을 개발하기 위해 동일한 언어와 선언적 컴포넌트 지향의 유지보수 모델을 사용할 수 있습니다.</p>
</li>
<li><p><code>더 나은 SEO</code>: 검색 엔진 크롤러는 완전히 렌더링된 페이지를 직접 볼 수 있습니다.  </p>
</li>
</ul>
<h3 id="제약사항">제약사항</h3>
<ul>
<li><p><code>개발 제약 사항</code>. 브라우저별 코드는 특정 수명주기 훅 내에서만 사용할 수 있습니다. 일부 외부 라이브러리는 서버 렌더링 앱에서 실행할 수 있도록 특별한 처리가 필요할 수 있습니다.</p>
</li>
<li><p><code>더 복잡한 빌드 설정 및 배포 요구 사항</code>. 모든 정적 파일 서버에 배포할 수 있는 완전 정적 SPA와 달리 서버 렌더링 앱에는 Node.js 서버를 실행할 수 있는 환경이 필요합니다.</p>
</li>
<li><p><code>더 많은 서버 측 부하</code>. Node.js에서 전체 앱을 렌더링하는 것은 정적 파일을 제공하는 것보다 CPU를 더 많이 사용하므로, 트래픽이 많을 것으로 예상되는 경우, 해당 서버 로드에 대비하고 캐싱 전략을 현명하게 사용하세요.</p>
</li>
</ul>
<h3 id="ssr-vs-ssg-ssg-static-site-generation">SSR vs. SSG (SSG: Static-Site Generation)</h3>
<p>미리 렌더링된 페이지가 생성되어 정적 HTML 파일로 제공됩니다.<br>SSG는 정적 데이터, 즉 빌드 시 결정되어 배포 간에 변경되지 않는 데이터를 소비하는 페이지에만 적용할 수 있습니다. 하지만 데이터가 변경될 때마다 새로운 배포가 필요합니다.<br>소수의 마케팅 페이지(예: /, /about, /contact 등)의 SEO를 개선하기 위해 SSR만 고려하고 있다면, SSG를 대안으로 추천합니다.  </p>
<h3 id="클라이언트-하이드레이트">클라이언트 하이드레이트</h3>
<p>클라이언트 측 앱을 대화형으로 만들기 위해 Vue는 하이드레이트 단계를 수행해야 합니다.  </p>
<h3 id="교차-요청-상태-오염cross-request-state-pollution">교차 요청 상태 오염(Cross-Request State Pollution)</h3>
<p>SSR 컨텍스트에서 앱 모듈은 일반적으로 서버가 부팅될 때 서버에서 한 번만 초기화됩니다. 동일한 모듈 인스턴스가 여러 서버 요청에서 재사용되고 싱글톤 상태 객체도 재사용됩니다.  </p>
]]></description>
        </item>
    </channel>
</rss>