<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>이채원</title>
        <link>https://velog.io/</link>
        <description>의문은 '삶의 수준'을 결정하고, 질문은 '삶 자체'를 바꾼다.</description>
        <lastBuildDate>Thu, 29 Jan 2026 02:33:13 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>이채원</title>
            <url>https://velog.velcdn.com/images/l__chwon/profile/a5e8ba6d-049c-494f-b847-ad5cef594e2a/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. 이채원. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/l__chwon" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[독서]2026(05.05) -권]]></title>
            <link>https://velog.io/@l__chwon/%EB%8F%85%EC%84%9C202601.20-%EA%B6%8C</link>
            <guid>https://velog.io/@l__chwon/%EB%8F%85%EC%84%9C202601.20-%EA%B6%8C</guid>
            <pubDate>Thu, 29 Jan 2026 02:33:13 GMT</pubDate>
            <description><![CDATA[<p>2026년은 시작 올해도 열심히
최종수정 : <span style="color:#fa1148;"><del>2026.05.05</del></span></p>
<hr>
<h1 id="소설">소설</h1>
<p><img src="https://velog.velcdn.com/images/l__chwon/post/4a3ec656-c87c-4e1b-adb9-a1b111d013bb/image.jpg" alt=""></p>
<h3 id="러브레터--이와이-슌지-지음">러브레터 | 이와이 슌지 지음</h3>
<p>완독 : 2026.01.12 ~ 2025.01.28</p>
<blockquote>
<p>멜로 영화는 당연 일본이라고 생각한다. 워낙 유명한 영화지만 항상 생각만 했지 찾아 볼 생각은 못했다. 영화와 소설, 보는거와 읽는건 차이가 있다고 생각했고 항상 소설을 보고 영화를 보면 감동과 여운이 덜 하다고 생각한다. 그래서 먼저 소설을 읽었다. 눈, 편지 깨끗한 느낌의 멜로의 영화 글로써 읽어보길 잘 했다고 생각이 든다. 꼭 영화도 찾아봐야지😀</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/l__chwon/post/db10b861-1cdc-4d00-ae38-f529bbab1041/image.jpg" alt=""></p>
<h3 id="노인과-바다--어니스트-헤밍웨이-지음">노인과 바다 | 어니스트 헤밍웨이 지음</h3>
<p>완독 : 2026.03.30 ~ 2025.04.09</p>
<blockquote>
<p>아무 것도 없었다.. 허탈하고 허망한 느낌? 노인 스스로 바다에서의 독백(?)이 가장 기억에 남는다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/l__chwon/post/5de86b92-daac-4e39-aca8-f5bb6870f7b6/image.jpg" alt=""></p>
<h3 id="혼모노--성해나-지음">혼모노 | 성해나 지음</h3>
<p>완독 : 2026.04.12 ~ 2026.04.24</p>
<blockquote>
<p>재미있게 읽었다. 소설의 마지막 끝맺음에서의 항상 여운이 남았다고 해야하나?? 정리가 됐다는 느낌보다 뭔가를 두고왔다?라는 느낌이 더 많이 들었다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/l__chwon/post/617349e1-ca35-4985-9e9d-d38d2eeb58cc/image.jpg" alt=""></p>
<h3 id="세상의-마지막-기차역--무라세-다케시-지음">세상의 마지막 기차역 | 무라세 다케시 지음</h3>
<p>완독 : 2026.04.24 ~ 2026.05.05</p>
<blockquote>
<p>탈선사고로 인해 사랑하는 사람을 잃은 사람들의 이야기 4개의 이야기, 상황이 있지만 조금씩 서로가 연결되어 있어 이야기에 흥미가 더욱 더 생겼던 거 같다. 어렵지 않은 단어들로 편하게 읽을 수 있었고 현실과 판타지를 잘 섞어 만든 소설 같다.</p>
</blockquote>
<h1 id="인문">인문</h1>
<p><img src="https://velog.velcdn.com/images/l__chwon/post/40c76da2-334c-4939-a48f-8bd3ab4be2f8/image.jpg" alt=""></p>
<h3 id="죽음이란-무엇인가--셸리-케이건-지음">죽음이란 무엇인가 | 셸리 케이건 지음</h3>
<p>완독 : 2026.04.12 ~ 🏃🏻‍♂️</p>
<blockquote>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[css] 이미지 object-fit]]></title>
            <link>https://velog.io/@l__chwon/css-%EC%9D%B4%EB%AF%B8%EC%A7%80-object-fit</link>
            <guid>https://velog.io/@l__chwon/css-%EC%9D%B4%EB%AF%B8%EC%A7%80-object-fit</guid>
            <pubDate>Thu, 06 Mar 2025 06:33:36 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>다양한 이미지 사이즈를 비율에 맞게 센터 정렬하며 화면을 줄였을 때 비율에 맞게 줄어들게 만든다</p>
</blockquote>
<pre><code class="language-html">&lt;div class=&quot;card&quot;&gt;
  &lt;div class=&quot;imgbox&quot;&gt;
      &lt;img src=&quot;&quot; alt=&quot;사진&quot;&gt;
  &lt;/div&gt;
  &lt;div class=&quot;txtbox&quot;&gt;
    &lt;strong class=&quot;title&quot;&gt;&lt;/strong&gt;
    &lt;p class=&quot;date&quot;&gt;2025.01.01&lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;</code></pre>
<pre><code class="language-css">.imgbox{padding-top:54%; position: relative; overflow: hidden;}
.imgbox img{width:100%; height:100%; object-fit: cover; position: absolute; top:0;}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[css] 모바일 햄버거 리스트 로고 상단 고정]]></title>
            <link>https://velog.io/@l__chwon/css-%EB%AA%A8%EB%B0%94%EC%9D%BC-%ED%96%84%EB%B2%84%EA%B1%B0-%EB%A6%AC%EC%8A%A4%ED%8A%B8-%EB%A1%9C%EA%B3%A0-%EC%83%81%EB%8B%A8-%EA%B3%A0%EC%A0%95</link>
            <guid>https://velog.io/@l__chwon/css-%EB%AA%A8%EB%B0%94%EC%9D%BC-%ED%96%84%EB%B2%84%EA%B1%B0-%EB%A6%AC%EC%8A%A4%ED%8A%B8-%EB%A1%9C%EA%B3%A0-%EC%83%81%EB%8B%A8-%EA%B3%A0%EC%A0%95</guid>
            <pubDate>Thu, 06 Mar 2025 03:56:22 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/l__chwon/post/659cbefb-53db-409b-bedb-0b538ca84b55/image.gif" alt=""></p>
<pre><code class="language-html">&lt;div class=&quot;mobile-menu&quot;&gt;
  &lt;div class=&quot;inner&quot;&gt;
    &lt;div class=&quot;logobox&quot;&gt;
        &lt;img class=&quot;logo&quot; src=&quot;&quot; alt=&quot;&quot;&gt;
          &lt;button type=&quot;button&quot; class=&quot;close&quot;&gt;&lt;/button&gt;
    &lt;/div&gt;
    &lt;div class=&quot;listbox&quot;&gt;
        .......
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;</code></pre>
<pre><code class="language-css">.mobile-menu{display: block; position: absolute; top:0; left:0; background:#fff;}
.logobox{ /* 로고박스에 맞는 css 입력*/}
.listbox{ position: fixed; top: /* 로고 박스만큼 */; left: 0; width: 100%; height: 100%; overflow-y: auto; -ms-overflow-style: none; -ms-overflow-style: none; scrollbar-width: none; box-sizing: border-box;}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[독서]2025(11.25) / -권]]></title>
            <link>https://velog.io/@l__chwon/%EB%8F%85%EC%84%9C202501.11</link>
            <guid>https://velog.io/@l__chwon/%EB%8F%85%EC%84%9C202501.11</guid>
            <pubDate>Sat, 11 Jan 2025 01:27:47 GMT</pubDate>
            <description><![CDATA[<p>2025년은 시작 올해도 열심히
최종수정 : <span style="color:#fa1148;"><del>2025.11.25</del></span></p>
<hr>
<h1 id="소설">소설</h1>
<p><img src="https://velog.velcdn.com/images/l__chwon/post/d551e190-15bb-4ba2-9500-162c2960c172/image.jpg" alt=""></p>
<h3 id="브람스를-좋아하세요--프랑수아즈-사강-지음">브람스를 좋아하세요... | 프랑수아즈 사강 지음</h3>
<p>완독 : 2025.01.06 ~ 2025.01.11</p>
<blockquote>
<p>고전문학을 그리 좋아하지 않지만<strong>(재미있게 읽히지 않지만)</strong> 오랜만에 재미있게 읽을 수 있었던 소설이었다. 남녀간의 사랑에 대한 내용을 풀어 쓴 소설이지만 소설에 많은 함축적인 내용이 많았다. 사람의 감정과 행동에 대한 세밀한 부분들이 보였고 남자는 단순한, 여자는 안정적인 그런 부분들이 돋보이지 않았나 생각이 든다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/l__chwon/post/aaed90ca-ac56-48af-84c4-ea04dac396a7/image.jpg" alt=""></p>
<h3 id="참을-수-없는-존재의-가벼움--밀란-쿤데라-지음">참을 수 없는 존재의 가벼움 | 밀란 쿤데라 지음</h3>
<p>완독 : 2025.01.12 ~ 2025.02.05</p>
<blockquote>
<p>정치적 억압과 네명의 주인공의 사랑 이야기를 담은 작품으로써 정서적(?)인 분의 심리 묘사가 어려우면서 최고라고 할 수 있다. 읽으면서 모든 내용을 이해할 수 없었지만 확실한건 작가의 심리적 묘사가 정말 뛰어나다는 걸 알 수 있었다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/l__chwon/post/85ef36e9-0209-4ba3-8de7-42c1f55e3d76/image.jpg" alt=""></p>
<h3 id="이별하는-방법을-가르쳐줘--이치조-미사키-지음">이별하는 방법을 가르쳐줘 | 이치조 미사키 지음</h3>
<p>완독 : 2025.02.06 ~ 2025.02.13</p>
<blockquote>
<p>항상 나올때마다 읽게되는 이치조 미사키 최근 소설보다 아쉽긴 하지만 너무 잘 읽히는 소설</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/l__chwon/post/f03f13d7-4f00-4cca-8716-6c7b16634bee/image.jpg" alt=""></p>
<h3 id="소년--가와바타-야스나리-지음">소년 | 가와바타 야스나리 지음</h3>
<p>완독 : 2025.03.17 ~ 🏃🏻‍♂️</p>
<blockquote>
<p>한 소년의 깨끗한 사랑(?) 일기 형식의 글들이 읽기에 참 신박하고 재미있었다. 나도 나중에 내가 쓴 읽기를 복기한다면 이런 기분일까?</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/l__chwon/post/da36ce69-aab5-4d7d-8a4a-6221dfa5410e/image.jpg" alt=""></p>
<h3 id="백야행1--히가시노-게이고-지음">백야행1 | 히가시노 게이고 지음</h3>
<p>완독 : 2025.09.24 ~ 2025.10.17</p>
<blockquote>
<p>초반에 여러 사건을 보여주며 이야기의 흐름을 겹겹이 쌓아가는 느낌? 2권이 기대되는 느낌 과연 결말은 어떨까?</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/l__chwon/post/175dfeeb-5f44-4907-b2d1-0f30992bcd7d/image.jpg" alt=""></p>
<h3 id="백야행2--히가시노-게이고-지음">백야행2 | 히가시노 게이고 지음</h3>
<p>완독 : 2025.10.20 ~ 2025.11.05</p>
<blockquote>
<p>마지막 결말은 좀 어이없다. 이야기의 흐름으로 볼 때 좀 더 큰 사건이 있거나 마무리에 대한 기대감이 컸는데 생각한 만큼의 크기가 아니라 아쉬웠던 거 같다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/l__chwon/post/33ee0ada-6491-42c7-bb69-eaf4e83ebe51/image.jpg" alt=""></p>
<h3 id="어둠-뚫기--박선우-지음">어둠 뚫기 | 박선우 지음</h3>
<p>완독 : 2025.11.13 ~ 2025.11.25</p>
<blockquote>
<p>중고 서점 알라딘에서 처음 구매 후 읽어본 국내 소설이다. 퀴어 가족사 소설인지 몰랐고 어머니에 대한, 따듯한(?) 소설인 줄 알았다. 어려운 내용 없이 술술 읽혔고 정말 어느 가정에 있을 법한 내용을 가지고 있는 소설이었다.</p>
</blockquote>
<h1 id="에세이">에세이</h1>
<p><img src="https://velog.velcdn.com/images/l__chwon/post/881716f7-83b1-4414-9056-0c54365abd79/image.jpg" alt=""></p>
<h3 id="당신은-결국-무엇이든-해내는-사람--김상현-지음">당신은 결국 무엇이든 해내는 사람 | 김상현 지음</h3>
<p>완독 : 2025.02.14 ~ 2025.03.10</p>
<blockquote>
<p>상황에따라 읽었을 때 느끼는 점이 달라지 듯 마음에 와닿는 부분들이 있었다.</p>
</blockquote>
<h1 id="인문">인문</h1>
<p><img src="https://velog.velcdn.com/images/l__chwon/post/589f4080-d725-4f6c-884f-b369d1f3e89e/image.jpg" alt=""></p>
<h3 id="고독에-관하여--요한-g-치머만-지음">고독에 관하여 | 요한 G. 치머만 지음</h3>
<p>완독 : 2025.04.01 ~ 🏃🏻‍♂️</p>
<blockquote>
</blockquote>
<p> <img src="https://velog.velcdn.com/images/l__chwon/post/ee5a3950-1732-4620-a8c1-5c2644169558/image.jpg" alt=""></p>
<h3 id="태도의-철학--샤를-페팽-지음">태도의 철학 | 샤를 페팽 지음</h3>
<p>완독 : 2025.06.01 ~ 2025.07.19</p>
<blockquote>
<p>실패에 대해 두려워하지말자 실패 또한 성공이다.</p>
</blockquote>
<h1 id="경제경영">경제/경영</h1>
<p><img src="https://velog.velcdn.com/images/l__chwon/post/9e493eee-0499-4a3e-b648-a633d09d2f45/image.jpg" alt=""></p>
<h3 id="1를-읽는-힘--메르-지음">1%를 읽는 힘 | 메르 지음</h3>
<p>완독 : 2025.07.22 ~ 🏃🏻‍♂️</p>
<blockquote>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Vue3] API 서버 통신]]></title>
            <link>https://velog.io/@l__chwon/Vue3-API-%EC%84%9C%EB%B2%84-%ED%86%B5%EC%8B%A0</link>
            <guid>https://velog.io/@l__chwon/Vue3-API-%EC%84%9C%EB%B2%84-%ED%86%B5%EC%8B%A0</guid>
            <pubDate>Tue, 13 Aug 2024 04:58:05 GMT</pubDate>
            <description><![CDATA[<h1 id="💻-api-서버-통신">💻 API 서버 통신</h1>
<h2 id="json">json?</h2>
<blockquote>
<p>json은 테스트를 하거나 프로토타이핑을 해야할 때 <code>REST API</code> 서버를 빠르게 생성해주는 프론트엔드 개발자를 위한 라이브러리다. 명령어로 쉽게 API 서버를 생성 할 수 있다. <a href="https://github.com/typicode/json-server">json다운로드</a></p>
</blockquote>
<h3 id="💡-rest-api">💡 REST API?</h3>
<ul>
<li>API의 리소스 즉 URL이 HTTP 메서드와 함께 표현되도록 정한 API 규격을 말한다.</li>
</ul>
<br>

<pre><code>//터미널 json 설치
npm install json-serve 

//api 서버 실행 -&gt; root 디렉토리에 `db.json` 파일이 설치됨
npx json-server --watch db.json

//localhost port 변경 (5000번으로 port를 변경했다.)
npx json-server --watch db.json --port 5000</code></pre><p><code>package.json</code>파일에 <code>&quot;db&quot;: &quot;json-server --watch db.json --port 5000&quot;</code>를 저장하여 필요할 때 명령어를 따로 길게 적지 않아도 사용할 수 있다. 다음에 사용할 때 <code>npm run db</code>명령어를 사용하면 된다.</p>
<pre><code class="language-javascript">// package.json 파일
{
      &quot;scripts&quot;: {
        &quot;dev&quot;: &quot;vite&quot;,
          &quot;db&quot;: &quot;json-server --watch db.json --port 5000&quot;,
          ...
      }
      },</code></pre>
<h2 id="axios">Axios</h2>
<blockquote>
<p>API 서버와 통신을 하기 위한 비동기 통신 모듈 <a href="https://github.com/axios/axios">Axios 설치</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Vue3] History 모드]]></title>
            <link>https://velog.io/@l__chwon/Vue3-History-%EB%AA%A8%EB%93%9C</link>
            <guid>https://velog.io/@l__chwon/Vue3-History-%EB%AA%A8%EB%93%9C</guid>
            <pubDate>Mon, 12 Aug 2024 06:04:13 GMT</pubDate>
            <description><![CDATA[<h1 id="💻-history-모드">💻 History 모드</h1>
<blockquote>
<p>Router 이스턴스를 생성할 때 <code>history</code> 옵션을 사용하면 다양한 history mode 중에서 선택할 수 있다. </p>
</blockquote>
<ul>
<li>Hash - createWebHashHistory()</li>
<li>History - createWebHistory()</li>
<li>Memory - createMemoryHistory()</li>
</ul>
<br>

<h2 id="1-1-hash-모드">1-1. Hash 모드</h2>
<blockquote>
<p>Vue Router를 통해 URL로 페이지를 전환할 때 히스토리 관리 기법을 해시(<code>#</code>)형으로 쓸 수 있게 해준다. 해시모드는 <code>createWebHashHistory()</code>를 사용하여 생성</p>
</blockquote>
<pre><code class="language-javascript">import { createRouter, createWebHashHistory } from &#39;vue-router&#39;

const router = createRouter({
    history: createWebHashHistory(),
      router: [
        ....  
    ]
})</code></pre>
<p>내부적으로 전달되는 실제 URL 앞에 해시 문자(<code>#</code>)를 사용한다. URL의 이 섹션은 서버로 전송되지 않으므로 서버 수준에서 특별한 처리가 필요하지 않는다. ** 그러나 그것은 SEO에 나쁜 영향을 미친다.** Hash는 지양하며 HTML5모드(<code>createWebHistory()</code>)를 사용하자</p>
<br>

<h2 id="1-2-history-모드html5-모드">1-2. History 모드(html5 모드)</h2>
<blockquote>
<p>Vue Router를 통해 URL로 페이지를 전환할 때 히스토리 관리 기법을 해시(<code>#</code>)없이 쓸 수 있게 해준다. Web API인 <code>history.pushState()</code>를 활용하여 페이지를 다시 로드하지 않고도 URL 탐색을 할 수 있다. HTML5 모드는 <code>createWebHistory()</code>로 생성되며 권장 모드이다.</p>
</blockquote>
<pre><code class="language-javascript">import { createRouter, createWebHistory } from &#39;vue-router&#39;

const router = createRouter({
    history: createWebHistory(),
      routes: [
      ...
    ]
})</code></pre>
<p><code>createWebHistory()</code>를 사용하면 URL은 &quot;정상&quot;으로 보인다. 하지만 여기에 문제가 있다. 우리의 앱이 적절한 서버 설정이 없는 단일 페이지 클라이언트 앱이기 때문에 사용자가 직접 <code>http://oursite.com/user/id</code>에 접속하면 404 오류가 발생한다. 이 문제를 해결하려면 서버에 간단하게 포괄적인 대체 경로를 추가하면 된다. URL이 정적 에셋과 일치하지 않으면 앱이 있는 동일한 <code>index.html</code>페이지를 제공해야 한다.</p>
<br>

<blockquote>
<p>💡 hash와 history의 차이는 <code>#</code>이 붙고 안 붙고의 차이다. 정확한 차이는 운영 서버에 배포할 때 일어난다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Vue3] Vue Router2]]></title>
            <link>https://velog.io/@l__chwon/Vue3-Vue-Router2</link>
            <guid>https://velog.io/@l__chwon/Vue3-Vue-Router2</guid>
            <pubDate>Thu, 08 Aug 2024 08:00:02 GMT</pubDate>
            <description><![CDATA[<h1 id="💻-vue-router2">💻 Vue Router2</h1>
<h2 id="동적-라우트">동적 라우트</h2>
<blockquote>
<p>주어진 패턴을 가진 라우트를 동일한 컴포넌트에 매핑해야하는 경우가 자주 있다. 예를 들어 **  사용자 목록(User List) ** 는 <code>/users</code>와 같은 경로에 매핑되면 되지만 ** 사용자 상세(User Detail) ** 는 ** 사용자 식별자 별로 같은 컴포넌트에 매핑 ** 되어야 한다. 이럴때 Vue Router에서는 경로에서 동적 세그먼트를 사용하여 해결할 수 있다. 이를 <code>param</code>라고 한다.<br><br> ex) /user/alice, /user/alice2, /user/alice3 .... -&gt; UserComponent.vue</p>
</blockquote>
<p><strong>index.js</strong></p>
<pre><code class="language-javascript">// router/index.js

    // &#39;:&#39; &lt;- 동적 라우팅이라도하며 여러 개의 URL을 하나의 페이지 컴포넌트에 맵핑하고 싶을때
    // 표현하는 표현 방식이다.
    // 이 세미콜론은 파라미터라고해서 라우트 객체에 params로 받을 수 있다. -&gt; {{ $route.params }}
    {
        path: &#39;/posts/:id&#39;,
        name: &#39;PostDetail&#39;,
        component: PostDetailView,
    },
    {
        path: &#39;/posts/:id/edit&#39;,
        name: &#39;PostEdit&#39;,
        component: PostEditView,
    },</code></pre>
<ul>
<li>동적 세그먼트는 콜론(<code>:</code>)으로 표시한다.</li>
<li>그리고 컴포넌트에서 동적 세그먼트의 값은 <code>$route.params</code> 필드로 접근할 수 있다.</li>
</ul>
<br>

<pre><code class="language-html">&lt;template&gt;
    &lt;div&gt;
        &lt;h2&gt;제목&lt;/h2&gt;
        &lt;p&gt;{{ $route.params }}&lt;/p&gt;
        .....
&lt;/template&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/l__chwon/post/ab8953b3-6175-4db7-84b9-178098dc3007/image.png" alt=""></p>
<p>이렇게 <code>$route.params</code> 사용하여 파라미터를 받을 수 있다.</p>
<br>

<h3 id="1-1-query-hash">1-1. query, hash</h3>
<blockquote>
<p><code>$route.params</code>외에도 <code>$route</code> 객체는 <code>$route.query(쿼리스트링)</code>, <code>$route.hash(해시태그)</code> 등과 같은 다른 유용한 정보도 노출한다.</p>
</blockquote>
<p>** query **</p>
<pre><code class="language-html">&lt;template&gt;
    &lt;div&gt;
        &lt;h2&gt;제목&lt;/h2&gt;
        &lt;p&gt;params: {{ $route.params }}&lt;/p&gt;
        &lt;p&gt;query: {{ $route.query }}&lt;/p&gt;
        &lt;p&gt;query value: {{ $route.query.searchText }}&lt;/p&gt;
      .....</code></pre>
<p><img src="https://velog.velcdn.com/images/l__chwon/post/4b4ac4ba-50a1-4f78-a93b-3e43fa04f1c2/image.png" alt=""></p>
<p>** hash **</p>
<pre><code class="language-html">&lt;template&gt;
    &lt;div&gt;
        &lt;h2&gt;제목&lt;/h2&gt;
        &lt;p&gt;params: {{ $route.params }}&lt;/p&gt;
        &lt;p&gt;query: {{ $route.query }}&lt;/p&gt;
        &lt;p&gt;query value: {{ $route.query.searchText }}&lt;/p&gt;
        &lt;p&gt;hash: {{ $route.hash }}&lt;/p&gt;
      .....</code></pre>
<p><img src="https://velog.velcdn.com/images/l__chwon/post/cd6fd18c-d1be-4be4-99e1-63e35cd60bbf/image.png" alt=""></p>
<br>

<h2 id="프로그래밍-방식-네비게이션">프로그래밍 방식 네비게이션</h2>
<blockquote>
<p><code>&lt;RouterLink&gt;</code>를 사용하여 선언적 네비게이션용 anchor 태그를 사용하는 것 외에도 라우터 인스턴스 메소드를 사용하여 프로그래밍 방식으로 수행할 수 있다.</p>
</blockquote>
<h3 id="routerpush">router.push</h3>
<blockquote>
<p>다른 URL로 이동하려면 <code>router.push</code>를 사용할 수 있다. 이 메소드는 새로운 항목을 히스토리스택에 넣기 때문에 사용자가 브라우저의 뒤로 가기 버튼을 클릭하면 이전 URL로 이동하게 된다. 이 메소드는 <code>&lt;RouterLink&gt;</code>를 클릭 할 때 내부적으로 호출되는 메소드이므로 <code>&lt;RouterLink :to=&quot;..&quot;&gt;</code>를 클릭하면 <code>router.push(..)</code>를 호출하는 것과 같다.</p>
</blockquote>
<br>

<h2 id="404-not-found-route">404 Not Found Route</h2>
<blockquote>
<p>일반 파라미터(<code>:id</code>)는 슬래쉬(<code>/</code>)로 구분된 URL 사이의 문자만 일치 시킨다. 무엇이든 일치시키려면 param바로 뒤에 괄호 안에 정규식(<code>regexp</code>)을 사용할 수 있다.</p>
</blockquote>
<pre><code class="language-javascript">//404 Not found
    {
        path: &#39;/:pathMatch(.*)*&#39;,
        name: &#39;NotFound&#39;,
        component: NotFoundView,
    },</code></pre>
<br>

<h2 id="중첩-라우트nested-routes">중첩 라우트(Nested Routes)</h2>
<blockquote>
<p>실제 앱 UI는 일반적으로 여러 단계로 중첩 된 컴포넌트로 이루어져 있다. URL의 세그먼트가 중첩 된 컴포넌트의 특정 구조와 일치한다는 것은 매우 일반적이다.</p>
</blockquote>
<pre><code class="language-javascript">//중첩 Router
    {
        path: &#39;/nested&#39;,
        name: &#39;NestedView&#39;,
        component: NestedView,
        children: [
            {
               //기본 nested를 클릭 했을 때 처음 보여지는 페이지 설정
                path: &#39;&#39;,
                name: &#39;NestedHome&#39;,
                component: NestedHomeView,
            },
            {
                path: &#39;one&#39;, // /nested/one
                name: &#39;NestedOne&#39;,
                component: NestedOneView,
            },
            {
                path: &#39;two&#39;, // /nested/two
                name: &#39;NestedTwo&#39;,
                component: NestedTwoView,
            },
        ],
    },</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Vue3] Vue Router]]></title>
            <link>https://velog.io/@l__chwon/Vue-Vue-Router</link>
            <guid>https://velog.io/@l__chwon/Vue-Vue-Router</guid>
            <pubDate>Wed, 07 Aug 2024 05:32:03 GMT</pubDate>
            <description><![CDATA[<h1 id="💻vue-router">💻Vue Router</h1>
<blockquote>
<p>뷰 라우터는 Vue.js를 이용하여 싱글 페이지 애플리케이션(SPA)을 구현할 때 사용하는 Vue.js의 공식 라우터다. <a href="https://router.vuejs.kr/guide/">Vue Router 공식문서</a></p>
</blockquote>
<br>

<h2 id="💡-라우터router란">💡 라우터(Router)란?</h2>
<blockquote>
<p>라우터는 일반적으로 네트워크간에 데이터를 전송하는 장치를 말한다. 뷰에서 말하는 라우터는 쉽게 말해서 URL에 따라 어떤 페이지를 보여줄지 매핑해주는 라이브러리라고 보면 된다. 예를 들어 <strong>&quot; <code>/home</code> 경로로 요청이 왔을 때 <code>Home.vue</code> 컴포넌트를 화면에 렌더링 해라&quot;</strong> 라는 역할을 수행하는 라이브러리라고 생각하면 된다. 그리고 이때 <code>/home</code> -&gt; <code>Home.vue</code> 이러한 매핑정보를 라우트(Route)라고도 한다.</p>
</blockquote>
<h3 id="라우트route란">라우트(Route)란?</h3>
<ul>
<li>어떤 URL에 대해 어떤 페이지를 표시해야 하는지에 대한 정보</li>
</ul>
<br>

<h2 id="설치">설치</h2>
<pre><code>npm install vue-router</code></pre><br>

<h3 id="1-1-페이지-컴포넌트-생성">1-1. 페이지 컴포넌트 생성</h3>
<blockquote>
<p><code>HomeView.vue</code>와 <code>AboutView.vue</code> 페이지(컴포넌트)를 생성한다. 경로는 root에 폴더를 만들어 넣어준다 <code>src/views/</code></p>
</blockquote>
<p>** HomeView.vue **</p>
<pre><code class="language-html">src/views/HomeView.vue

&lt;template&gt;
    &lt;div&gt;Home view&lt;/div&gt;

&lt;/template&gt;

&lt;script setup&gt;
&lt;/script&gt;

&lt;style lang=&quot;scss&quot; scoped&gt;&lt;/style&gt;
</code></pre>
<p>** AboutView.vue **</p>
<pre><code class="language-html">src/views/AboutView.vue

&lt;template&gt;
    &lt;div&gt;About View&lt;/div&gt;
&lt;/template&gt;

&lt;script setup&gt;
&lt;/script&gt;

&lt;style lang=&quot;scss&quot; scoped&gt;&lt;/style&gt;
</code></pre>
<br>

<h3 id="1-2-라우터router-설정">1-2. 라우터(router) 설정</h3>
<p>** index.js **</p>
<pre><code class="language-javascript">// src/router/index.js

//라우트 정보를 담고 있는 라우터 객체를 생성
import { createRouter, createWebHistory } from &#39;vue-router&#39;;
import HomeView from &#39;@/views/HomeView.vue&#39;;
import AboutView from &#39;@/views/AboutView.vue&#39;;

//router설정
const routes = [
    {
        path: &#39;/&#39;, //root로 들어왔을 때
        name: &#39;Home&#39;,
        component: HomeView,
    },
    {
        path: &#39;/about&#39;, //about로 들어왔을 때
        name: &#39;About&#39;,
        component: AboutView,
    },
];

//라우터 객체 생성
const router = createRouter({
    history: createWebHistory(&#39;/&#39;),
    routes, // 키와 값이 같으면 값을 빼고 적어도 됨
});

//생선된 router 객체를 외부에서 사용할 수 있도록 export
export default router;</code></pre>
<p>설정한 라우터 객체를 Vue 인스턴스에 추가해보자</p>
<p>** main.js **</p>
<pre><code class="language-javascript">// src/main.js

//bootstrap 설치 후 import
import &#39;bootstrap/dist/css/bootstrap.min.css&#39;;
import &#39;bootstrap-icons/font/bootstrap-icons.css&#39;;

import { createApp } from &#39;vue&#39;;
import App from &#39;./App.vue&#39;;

//router/index.js에서 설정한 router 객체를 가져옴
import router from &#39;@/router&#39;;

//use 메서드를 통해서 설정한다.
createApp(App).use(router).mount(&#39;#app&#39;);
import &#39;bootstrap/dist/js/bootstrap.js&#39;;</code></pre>
<p><code>app.use(router)</code>를 호출 함으로써 컴포넌트 내부에서 <code>$router</code>, <code>$route</code> 객체에 접근할 수 있다.</p>
<br>

<h2 id="routerlink-routerview">RouterLink, RouterView</h2>
<h3 id="routerlink">RouterLink</h3>
<blockquote>
<p>Vue Router 에서는 페이지를 이동할 때는 일반 <code>a</code>태그를 사용하는 대신 ** 커스텀 컴포넌트인 <code>&lt;RouterLink&gt;</code>를 사용하여 다른 페이지 링크를 만들어야 한다. ** 이를 통해 ** Vue Router는 페이지를 리로딩 하지 않고 URL에 매핑된 페이지를 렌더링 ** 할 수 있다.</p>
</blockquote>
<h3 id="routerview">RouterView</h3>
<blockquote>
<p><code>&lt;RouterView&gt;</code>는 URL에 매핑된 컴포넌트를 화면에 표시한다.</p>
</blockquote>
<br>

<h2 id="push-replace">push, replace</h2>
<blockquote>
<p><code>router.push</code>와 같은 역할을 하지만 유일한 차이는 새로운 히스토리 항목에 추가하지 않고 탐색한다는 것이다. 이름에서 알 수 있듯이 현재 항목을 대체한다.</p>
</blockquote>
<pre><code class="language-javascript">// router.push 메소드에 replace: true 속성을 추가하여 동일하게 동작 시킬 수 있다.
router.push({ path: &#39;/home&#39;, replace: true})
// equivalent to
router.replace({ path: &#39;/home&#39;})</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Vue] 동적 컴포넌트]]></title>
            <link>https://velog.io/@l__chwon/Vue-%EB%8F%99%EC%A0%81-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8</link>
            <guid>https://velog.io/@l__chwon/Vue-%EB%8F%99%EC%A0%81-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8</guid>
            <pubDate>Tue, 06 Aug 2024 05:51:48 GMT</pubDate>
            <description><![CDATA[<h1 id="💻-동적-컴포넌트dynamic-component">💻 동적 컴포넌트(Dynamic Component)</h1>
<blockquote>
<p>컴포넌트를 동적으로 변경하고 싶을 때 <code>v-bind:is</code> 속성을 사용해서 변경할 수 있다. 동적 컴포넌트는 탭 인터페이스와 같이 컴포넌트간에 동적으로 전환해야 할 때 유용하다.</p>
</blockquote>
<pre><code class="language-html">&lt;component&gt; :is=&quot;currentTabComponent&quot;&gt;&lt;/component&gt;</code></pre>
<p>위 예시에서 <code>:is</code> 속성에 전달된 값은 다음 중 하나를 포함할 수 있다.</p>
<ul>
<li>등록된 컴포넌트의 문자열 이름 <code>string</code></li>
<li>실제 가져온 컴포넌트 객체 <code>object</code></li>
</ul>
<blockquote>
<p>💡 <code>&lt;component :is=&quot;...&quot;</code>를 사용하여 여러 컴포넌트간 전환하면 컴포넌트의 마운트가 매번 해제된다. 이때 <code>&lt;KeepAlive&gt; 내장 컴포넌트</code>를 사용하여 &quot;비활성 컴포넌트&quot;들의 &quot;활성&quot; 상태를 유지할 수 있도록 강제할 수 있다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Vue] Script Setup]]></title>
            <link>https://velog.io/@l__chwon/Vue-Script-Setup</link>
            <guid>https://velog.io/@l__chwon/Vue-Script-Setup</guid>
            <pubDate>Tue, 30 Jul 2024 05:11:30 GMT</pubDate>
            <description><![CDATA[<h1 id="💻--script-setup">💻 &lt; script setup &gt;</h1>
<blockquote>
<p><code>&lt;script setup&gt;</code>은 Single-File-Component 내에서 Composition API를 사용하기 위한 ** syntactic-sugar(문법적 설탕) ** 입니다. SFC와 CompositionAPI를 사용하는 경우 <code>&lt;script setup&gt;</code>을 사용하는 것을 권장한다. 왜냐면 일반 <code>&lt;script&gt;</code> 구문보다 많은 장점을 제공해준다.</p>
</blockquote>
<ul>
<li>간결한 문법으로 사용구(boilerplate)를 줄일 수 있다.</li>
<li>타입스크립트를 사용한 props와 emits 선언 가능</li>
<li>런타임 성능의 향상(템플릿이 setup 스크립트와 같은 스코프(scope)에 있는 render 함수로 컴파일되므로 프록시가 필요없음)</li>
<li>더 뛰어난 IDE 타입 추론 성능 (language 서버가 코드로부터 타입을 추론해내는데 비용이 덜 든다)</li>
</ul>
<blockquote>
<p>📌 syntactic-sugar는 기능은 그대로인데 읽는 사람을 위해 직관적으로 쉽게 코드를 읽을 수 있게 만든다는 것이다.</p>
</blockquote>
<pre><code class="language-html">&lt;script setup&gt;
  import { ref } from &quot;vue&quot;;

  // script에 setup을 선언하면 된다.
  console.log(&#39;Hi!&#39;);

  //반응형 데이터 또한 잘 작동한다.
  const message = ref(&quot;&quot;);

  //따로 return을 하지 않더라도 사용할 수 있다. (상용구를 줄일 수 있다)
  const msg = &#39;hi&#39;;
&lt;/script&gt;


&lt;script&gt;
  //위의 setup을 사용했을 때 아래처럼 컴파일된다.
  export default {
      import { ref } from &quot;vue&quot;;

      setup() {
          console.log(&#39;Hi!&#39;);

          const message = ref(&quot;&quot;);

          const msg = &#39;hi&#39;;
          return{ msg, message };
      },
  }
&lt;/script&gt;</code></pre>
<br>

<h2 id="1-1-defineprops--defineemits">1-1 defineProps() &amp; defineEmits()</h2>
<blockquote>
<p><code>defineProps()</code>와 <code>defineEmits()</code> API를 <code>&lt;script setup&gt;</code>내에 선언하여 <code>props</code>와 <code>emits</code>을 사용할 수 있다.</p>
</blockquote>
<pre><code class="language-html">&lt;script setup&gt;
  const props = defineProps({
      foo: String
  })

  const emit = defineEmits([&#39;change&#39;, &#39;delete&#39;])
&lt;/script&gt;</code></pre>
<ul>
<li><code>defineProps</code>와 <code>defineEmits</code>는 <code>&lt;script setup&gt;</code> 내부에서만 사용할 수 있는 컴파일러 매크로다. 그렇기에 <code>import</code>할 필요가 없으면 <code>&lt;script setup&gt;</code>이 처리될 때 컴파일 된다.</li>
<li><code>defineProps</code>는 <code>props</code>옵션과 동일한 값을 허용한다. 그리고 <code>defineEmits</code>는 <code>emits</code> 옵션과 동일한 값을 허용한다.</li>
<li><code>defineProps</code>와 <code>defineEmits</code>는 전달된 옵션을 기반으로 타입 추론을 제공한다.</li>
<li><code>defineProps</code>와 <code>defineEmits</code>에 전달된 옵션은 <code>setup()</code>에서 <code>모듈 영역(module scope)</code>으로 호스팅된다. 따라서 옵션은 <code>setup()</code> 영역에 선언된 지역 변수를 참조할 수 없다. 만약 그렇게 하면 컴파일 오류가 발생한다. 하지만 <code>import</code>된 옵션은 사용할 수 있다. 왜냐면 <code>import</code>도 모듈 영역으로 호스트가 되기 때문이다.</li>
</ul>
<h2 id="1-2-defineexpose">1-2 defineExpose()</h2>
<blockquote>
<p><code>&lt;script setup&gt;</code>을 사용하는 컴포넌트는 기본적으로 <strong>Template Refs</strong>나 $parent와 같이 컴포넌트간 통신이 닫혀 있다. <code>&lt;script setup&gt;</code>을 사용하는 컴포넌트의 내부 데이터나 메서드를 명시적으로 노출하려면 <code>defineExpose()</code>컴파일러 매크로를 사용할 수 있다.</p>
</blockquote>
<pre><code class="language-html">&lt;script setup&gt;
  import { ref } from &#39;vue&#39;;

  const a = 1
  const b = ref(2)

  defineExpose({
      a,
      b
  })
&lt;/script&gt;</code></pre>
<p>expose는 일반 &lt; script &gt;에서도 사용할 수 있다.</p>
<pre><code class="language-html">&lt;script&gt;
  export default{
      setup(props, context){
      //Expose public properties (Function)
      console.log(context.expose)
      }
  }
&lt;/script&gt;</code></pre>
<h2 id="1-3-useslots--useattrs">1-3 useSlots() &amp; useAttrs()</h2>
<blockquote>
<p><code>slots</code>과 <code>attrs</code>는 <code>&lt;template&gt;</code> 내부에서 <code>$slots</code>과 <code>$attrs</code>로 직접 접근해서 사용할 수 있다. 만약 <code>&lt;script setup&gt;</code> 내부에서 slots과 attrs를 사용하고 싶다면 각각 <code>useSlots()</code>, <code>useAttrs()</code> helper 메서드를 사용할 수 있다.</p>
</blockquote>
<pre><code class="language-html">&lt;script setup&gt;
  import { useSlots, useAttrs } from &#39;vue&#39;

  const slots = useSlots()
  const attrs = useAttrs() // fallthrough 속성 접근
&lt;/script&gt;</code></pre>
<p><code>slots</code>과 <code>attrs</code>는 일반 <code>&lt;script&gt;</code>에서도 사용할 수 있다.</p>
<pre><code class="language-html">&lt;script&gt;
  export default{
      setup(props, context){
          //Attributes (Non-reactive object, equivalent to $attrs)
          console.log(context.attrs)

          //Slots (Non-reactive object, equivalent to $slots)
          console.log(context.slots)
      }
  }
&lt;/script&gt;
</code></pre>
<h2 id="1-4--script-와--script-setup함께-사용">1-4 &lt; script &gt;와 &lt; script setup&gt;함께 사용</h2>
<blockquote>
<p><code>&lt;script setup&gt;</code>은 <code>nomal &lt;script&gt;</code>와 함께 사용할 수 있다. 예를 들면 다음과 같은 경우에 <code>nomal &lt;script&gt;</code>가 필요할 수 있다.</p>
</blockquote>
<ul>
<li>예를 들어 <code>&lt;script setup&gt;</code>에서 표현할 수 없는 inheritAttrs옵션이나 Plugin을 통해 활성화된 Custom 옵션을 사용하고자 할때 <code>nomal &lt;script&gt;</code>를 함께 선언합니다.</li>
<li><code>named export</code>를 선언 했을 때(예: <code>export const data</code>)</li>
<li>한 번만 실행되어야 하는 로직이 있을 때</li>
</ul>
<pre><code class="language-html">&lt;script&gt;
  //일반 스크립트, 모듈 범위에서 한 번만 실행
  runSideEffectOnce()

  //옵션 선언
  export default {
      inheritAttrs : false,
      customOptions: {}
  }
&lt;/script&gt;

&lt;script setup&gt;
  //각 인스턴스 생성시 setup() 범위에서 실행
&lt;/script&gt;</code></pre>
<h2 id="1-5-top-level-await">1-5 Top-level await</h2>
<blockquote>
<p><code>&lt;script setup&gt;</code>내의 Top-level에서 <code>await</code>을 사용할 수 있습니다. 그리고 코드는 <code>async setup()</code>이렇게 컴파일 된다.</p>
</blockquote>
<pre><code class="language-html">&lt;script setup&gt;
  const post = await fetch(`/api/post/1`).then((r) =&gt; r.json())
&lt;/script&gt;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Vue] Template Refs]]></title>
            <link>https://velog.io/@l__chwon/Vue-Template-Refs</link>
            <guid>https://velog.io/@l__chwon/Vue-Template-Refs</guid>
            <pubDate>Fri, 26 Jul 2024 07:30:55 GMT</pubDate>
            <description><![CDATA[<h1 id="💻-template-refs">💻 Template Refs</h1>
<blockquote>
<p>Vue의 선언적 렌더링 모델은 대부분의 직접적인 DOM의 작업을 대신 수행한다. 하지만 때론 기본 DOM요소에 직접 접근해야 하는 경우가 있을 수 있다. 이때 <code>ref</code> 특수 속성을 사용해서 쉽게 접근할 수 있다.</p>
</blockquote>
<pre><code class="language-html">&lt;input type=&quot;text&quot; ref=&quot;input&quot; /&gt;</code></pre>
<p><code>ref</code>는 특수 속성이다. <strong>이 <code>ref</code>특수 속성을 통해 마운트된 DOM 요소 또는 자식 컴포넌트에 대한 참조를 얻을 수 있다.</strong></p>
<br>

<h2 id="1-refs-접근하기">1. Refs 접근하기</h2>
<blockquote>
<p>Composition API로 참조를 얻으려면 동일한 이름의 참조를 선언해야 한다.</p>
</blockquote>
<ul>
<li>컴포넌트가 마운트된 후에 접근할 수 있다.</li>
<li><code>&lt;template&gt;</code> 안에서 <code>input</code>으로 <code>Refs</code> 참조에 접근하려는 경우 렌더링되기 전에는 참조가 <code>null</code>일 수 있다.</li>
<li><code>&lt;template&gt;</code> 안에서 <code>$refs</code> 내장 객체로 <code>Refs</code> 참조에 접근할 수 있다.</li>
</ul>
<br>

<h2 id="2-v-for-내부-참조">2. V-for 내부 참조</h2>
<blockquote>
<p>v3.2.25 이상에서 동작
<code>v-for</code>내부에서 <code>ref</code>가 사용될 때 <code>ref</code>는 마운트 후 요소 배열로 채워진다.</p>
</blockquote>
<pre><code class="language-html">&lt;script&gt;
  import { ref, onMounted } from &#39;vue&#39;

  export default {
      setup() {
          const list = ref([1, 2, 3]);
          const itemRefs = ref([]);

          onMounted(() =&gt; console.log(itemRefs.value))

          return{ list, itemRefs }
      }
 }
&lt;/script&gt;</code></pre>
<br>

<h2 id="3-컴포넌트-refs">3. 컴포넌트 Refs</h2>
<blockquote>
<p><code>ref</code>를 자식 컴포넌트에도 사용할 수 있다. <code>ref</code>로 자식 컴포넌트에 참조값을 얻게 되면 자식 컴포넌트의 모든 속성과 메서드에 대한 전체를 접근할 수 있다.
이러한 경우 부모/자식 컴포넌트간 의존도가 생기기 때문에 이러한 방법은 반드시 필요한 경우에만 사용해야 한다. 그리고 일반적으로 <code>ref</code>보다 표준 props를 사용하여 부모/자식간 상호작용을 구현해야 한다.</p>
</blockquote>
<p><strong>child.vue</strong></p>
<pre><code class="language-html">&lt;template&gt;
  &lt;div&gt;Child Component&lt;/div&gt;
&lt;/template&gt;

&lt;script&gt;
  import { ref } from `vue`;

  export default {
      setup() {
          const message = ref(&#39;Hello Child!&#39;);
          const sayHello = () =&gt; {
              alert(message.value);
          };
          return { message, sayHello };
      },
  };
&lt;/script&gt;</code></pre>
<p>부모 컴포넌트에서 자식 컴포넌트의 상태나 메서드에 접근할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Vue] Lifecycle Hooks]]></title>
            <link>https://velog.io/@l__chwon/Vue-Lifecycle-Hooks</link>
            <guid>https://velog.io/@l__chwon/Vue-Lifecycle-Hooks</guid>
            <pubDate>Fri, 26 Jul 2024 02:02:03 GMT</pubDate>
            <description><![CDATA[<h1 id="💻-lifecycle-hooks">💻 Lifecycle Hooks</h1>
<blockquote>
<p>다음은 인스턴스 수명 주기에 대한 다이어그램이다. 지금 진행 중인 모든 것을 완전히 이해할 필요는 없지만 더 많이 배우고 구축함에 따라 유용한 참고 자료가 된다. 자세한 내용은 <a href="https://ko.vuejs.org/guide/essentials/lifecycle">Vue.js 생명주기 훅</a>을 참고하자</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/l__chwon/post/18dad506-d3c0-42ad-bc8b-b8991cbdf5d0/image.png" alt=""></p>
<br>

<h2 id="1-lifecycle-hooks-단계">1. Lifecycle Hooks 단계</h2>
<blockquote>
<p>Lifecycle Hooks은 크게 4단계로 나눠진다.
<code>Creation(생성)</code> &gt; <code>Mounting(장착)</code> &gt; <code>Updating(수정)</code> &gt; <code>Destruction(소멸)</code></p>
</blockquote>
<h3 id="1-1-creation">1-1. Creation</h3>
<blockquote>
<p>컴포넌트 초기화 단계이며 <code>Creation Hooks</code>은 라이플사이클 단계에서 가장 먼저 실행된다.</p>
</blockquote>
<ul>
<li>아직 컴포넌트가 DOM에 추가되기 전이므로 DOM에 접근할 수 없다.</li>
<li>서버렌더링에서 지원되는 단계</li>
<li>클라이언트나 서버 렌더 단에서 처리해야 할 일이 있으면 이단계에서 진행</li>
</ul>
<h3 id="🔨-beforecreate">🔨 beforeCreate</h3>
<blockquote>
<p>컴포넌트 인스턴스가 초기화 될 때 실행된다. <code>data()</code> 또는 <code>computed</code>와 같은 다른 옵션을 처리하기 전에 즉시 호출된다.</p>
</blockquote>
<h3 id="🔨-created">🔨 created</h3>
<blockquote>
<p>컴포넌트 인스턴스가 초기화를 완료한 후 호출되는 훅이다.</p>
</blockquote>
<h3 id="🔨-setup">🔨 setup</h3>
<blockquote>
<p>Composition API의 <code>setup()</code> 훅은 Options API 훅 보다 먼저 호출된다. <code>beforeCreate</code>와 <code>created</code> 라이프사이클 훅은 Options API에서 사용하는 라이프사이클 훅으로 Vue3 Composition API를 활용하여 개발을 진행할 때는 setup()함수로 대체할 수 있습니다.</p>
</blockquote>
<pre><code class="language-html">&lt;script&gt;
  export default {
      beforeCreate(){

      },
      created(){

      },
      setup(){
          //coding....
      }
  }
&lt;/script&gt;  </code></pre>
<br>

<h3 id="1-2-mounting">1-2. Mounting</h3>
<blockquote>
<p>DOM에 컴포넌트를 삽입하는 단계이다. <code>onBeforeMount</code>와 <code>onMounted</code>가 있다.</p>
</blockquote>
<ul>
<li>서버렌더링에서 지원되지 않는다.</li>
<li>초기 렌더링 직전에 돔을 변경하고자 한다면 이 단계에서 활용할 수 있다.</li>
</ul>
<h3 id="🔨-onbeforemount">🔨 onBeforeMount</h3>
<blockquote>
<p>컴포넌트가 마운트되기 직전에 호출된다.</p>
</blockquote>
<ul>
<li>대부분의 경우 사용을 권장하지 않는다.</li>
</ul>
<h3 id="🔨-onmounted">🔨 onMounted</h3>
<blockquote>
<p>컴포넌트가 마운트된 후에 호출된다. DOM에 접근할 수 있다.</p>
</blockquote>
<ul>
<li>모든 자식 컴포넌트가 마운트되었음을 의미한다.</li>
<li>자체 DOM 트리가 생성되어 상위 컴포넌트에 삽입되었음을 의미한다.</li>
</ul>
<br>

<pre><code class="language-html">&lt;template&gt;
    &lt;div class=&quot;container py-4&quot;&gt;
        &lt;input ref=&quot;inputRef&quot; type=&quot;text&quot; value=&quot;안녕하세요. &quot; /&gt;
    &lt;/div&gt;
&lt;/template&gt;

&lt;script&gt;
import { onBeforeMount, onMounted, ref } from &quot;vue&quot;;

export default {
    setup() {
        console.log(&quot;setup&quot;);
        const inputRef = ref(null);
        //onBeforeMount는 mount되기 전이라 출력X
        onBeforeMount(() =&gt; {
            console.log(&quot;onBeforeMount&quot;, inputRef.value);
        });
        //onMounted는 mount된 이후이기 때문에 출력
        onMounted(() =&gt; {
            console.log(&quot;onMounted&quot;, inputRef.value.value);
        });

        return { inputRef };
    },
    // beforeCreate() {
    //     console.log(&quot;beforeCreate&quot;);
    // },
    // created() {
    //     console.log(&quot;created&quot;);
    // },
};
&lt;/script&gt;

&lt;style lang=&quot;scss&quot; scoped&gt;&lt;/style&gt;</code></pre>
<br>

<p><strong>출력화면</strong>
<img src="https://velog.velcdn.com/images/l__chwon/post/a51fbd2e-190d-454e-b53a-0703acc1b532/image.png" alt=""></p>
<p>부모 컴포넌트 아래에 자식 컴포넌트가 있을경우 어떻게 처리가 되는지 확인해보자. 
<img src="https://velog.velcdn.com/images/l__chwon/post/cc86066d-9df6-4883-9543-b25279d833bb/image.png" alt=""></p>
<p>부모 컴포넌트의 <code>setup</code>과 <code>onBeforeMount</code>가 먼저 처리가 된 후 자식 컴포넌트가 처리가 된다. 자식 컴포넌트의 처리가 끝나면 부모 컴포넌트의 <code>onMount</code>가 마지막으로 처리된다.</p>
<br>

<h3 id="1-3-updating">1-3. Updating</h3>
<blockquote>
<p>컴포넌트에서 사용되는 반응형 데이터가 변경되거나 어떠한 이유로 재렌더링이 발생될 때 호출된다.</p>
</blockquote>
<ul>
<li>디버깅이나 프로파일링 등을 위해 컴포넌트 재 렌더링 시점을 알고 싶을 때 사용하면 된다.</li>
</ul>
<h3 id="🔨-onbeforeupdate">🔨 onBeforeUpdate</h3>
<blockquote>
<p>반응형 상태 변경으로 인해 컴포넌트가 DOM 트리를 업데이트하기 직전에 호출된다.<br>컴포넌트에서 사용되는 반응형 상태 값이 변해서, DOM에도 그 변화를 적용시켜야 할 때가 있다. 이 때, 변화 직전에 호출되는 것이 바로 onBeforeUpdate 훅이다.</p>
</blockquote>
<h3 id="🔨-onupdated">🔨 onUpdated</h3>
<blockquote>
<p>반응 상태 변경으로 인해 컴포넌트가 DOM 트리를 업데이트한 후에 호출된다. <br> 상위 컴포넌트의 <code>onUpdated</code> 훅은 하위 컴포넌트의 훅 이후에 호출된다. (<code>Child</code> &gt; <code>Parent</code>) 이 훅은 다른 상태 변경으로 인해 발생할 수 있는 컴포넌트의 DOM 업데이트 후에 호출된다. 특정 상태 변경 후에 업데이트된 DOM에 엑세스해야 하는 경우 대신 <code>nextTick()</code>을 사용하자</p>
</blockquote>
<pre><code class="language-html">&lt;template&gt;
    &lt;div class=&quot;container py-4&quot;&gt;
        &lt;p id=&quot;message&quot;&gt;{{ message }}&lt;/p&gt;
    &lt;/div&gt;
&lt;/template&gt;

&lt;script&gt;
import { onBeforeUpdate, onUpdated, ref } from &quot;vue&quot;;

export default {
    setup() {
        const inputRef = ref(null);
        const message = ref(&quot;&quot;);

        onBeforeUpdate(() =&gt; {
            console.log(&quot;onBefroeUpdate&quot;, message.value);
            console.log(
                &quot;DOM Content:&quot;,
                document.querySelector(&quot;#message&quot;).textContent,
            );
        });
        onUpdated(() =&gt; {
            console.log(&quot;onUpdated&quot;, message.value);
            console.log(
                &quot;DOM Content:&quot;,
                document.querySelector(&quot;#message&quot;).textContent,
            );
        });

        return { inputRef, message };
    },
};
&lt;/script&gt;

&lt;style lang=&quot;scss&quot; scoped&gt;&lt;/style&gt;
</code></pre>
<p><strong>출력화면</strong>
<img src="https://velog.velcdn.com/images/l__chwon/post/a9933634-6aa2-4ab1-9153-6d6ebf523416/image.png" alt=""></p>
<p>반응형 데이터 <code>message</code>를 수정하게되면 콘솔에서 값을 확인할 수 있다. <code>onBeforeUpdate</code>는 DOM 트리를 업데이트하기 이전이기 때문에 DOM 컨텐츠를 가져왔을 때 아직 이전 데이터인 빈값인 걸 확인할 수 있다. <code>onUpdated</code>은 DOM 트리가 변경된 이후의 호출되는 HOOK이기 때문에 DOM 컨텐츠를 가져오는 것을 확인할 수 있다. 다시 한번 변경해보자.</p>
<p><img src="https://velog.velcdn.com/images/l__chwon/post/3b43114b-e407-4a5d-9e27-61e2f5a5499a/image.png" alt=""></p>
<p>반응형 데이터는 변경된 걸 확인할 수 있지만 DOM 컨텐츠는 변경되기 이전의 데이터를 가져오는 것을 확인할 수 있다. <code>onUpdated</code>은 변경된 후의 데이터를 가져오는 것을 확인할 수 있다.</p>
<br>

<p><strong>!!<span style="color: red">WARNING</span>!!</strong>
<code>onUpdated</code> 훅에서 컴포넌트 상태를 변경하지 말자. 그러면 무한 업데이트 루프가 발생 할 수 있다.!</p>
<br>

<h3 id="1-4-destruction">1-4. Destruction</h3>
<blockquote>
<p>해체(소멸)단계 이며 <code>onBeforeUnmount</code>와 <code>onUnmounted</code>가 있다.</p>
</blockquote>
<h3 id="🔨-onbeforeunmount">🔨 onBeforeUnmount</h3>
<blockquote>
<p>컴포넌트가 마운트 해제되기 직전에 호출</p>
</blockquote>
<h3 id="🔨-onunmounted">🔨 onUnmounted</h3>
<blockquote>
<p>컴포넌트가 마운트 해제된 후 호출</p>
</blockquote>
<p><strong>부모</strong></p>
<pre><code class="language-html">&lt;template&gt;
    &lt;div class=&quot;container py-4&quot;&gt;
        &lt;button @click=&quot;visible = !visible&quot;&gt;Toggle Child&lt;/button&gt;
        &lt;LifeCycleChild v-if=&quot;visible&quot;&gt;&lt;/LifeCycleChild&gt;
        &lt;p id=&quot;message&quot;&gt;{{ message }}&lt;/p&gt;
    &lt;/div&gt;
&lt;/template&gt;

&lt;script&gt;
import { onBeforeMount, onBeforeUpdate, onMounted, onUpdated, ref } from &quot;vue&quot;;
import LifeCycleChild from &quot;./LifeCycleChild.vue&quot;;

export default {
    components: {
        LifeCycleChild,
    },
    setup() {
        const inputRef = ref(null);
        const message = ref(&quot;&quot;);
        const visible = ref(false);

        return { inputRef, message, visible };
    },

};
&lt;/script&gt;

&lt;style lang=&quot;scss&quot; scoped&gt;&lt;/style&gt;
</code></pre>
<p><strong>자식</strong></p>
<pre><code class="language-html">&lt;template&gt;
    &lt;div class=&quot;card&quot;&gt;
        &lt;div class=&quot;card-body&quot;&gt;Lifecycle Child&lt;/div&gt;
        &lt;input id=&quot;input&quot; type=&quot;text&quot; /&gt;
    &lt;/div&gt;
&lt;/template&gt;

&lt;script&gt;
import { onBeforeMount, onBeforeUnmount, onMounted, onUnmounted } from &quot;vue&quot;;
export default {
    setup() {
        onBeforeMount(() =&gt; {
            console.log(&quot;[child] onBeforeMount&quot;);
        });
        onMounted(() =&gt; {
            console.log(&quot;[child] onMount&quot;);
        });
        onBeforeUnmount(() =&gt; {
            console.log(&quot;[Child] onBeforeUnmount&quot;);
            console.log(document.querySelector(&quot;#input&quot;));
        });
        onUnmounted(() =&gt; {
            console.log(&quot;[Child] onUnmounted&quot;);
            console.log(document.querySelector(&quot;#input&quot;));
        });
        return {};
    },
};
&lt;/script&gt;

&lt;style lang=&quot;scss&quot; scoped&gt;&lt;/style&gt;
</code></pre>
<br>

<p><strong>출력화면</strong>
<img src="https://velog.velcdn.com/images/l__chwon/post/ffae5a2a-8764-47d8-8289-a94fbe714665/image.png" alt=""></p>
<p>Toggle Child 클릭 시 <code>LifeCycleChild.vue</code> 컴포넌트가 생성되며 마운트 되었기 때문에   <code>[Child] onBeforeMount</code>와 <code>[Child] onMount</code>가 실행되는 걸 볼 수 있다.
<img src="https://velog.velcdn.com/images/l__chwon/post/e84add06-8938-422f-9696-4df29137927a/image.png" alt=""></p>
<p>Toggle child 를 다시 클릭해서 마운트 해제, 소멸시키면
<img src="https://velog.velcdn.com/images/l__chwon/post/e1230c96-a260-4ea9-8c91-20de71c6b754/image.png" alt="">\</p>
<p><code>[Child] onBeforeUnmount</code>과 <code>[Child] onUnmounted</code>이 호출되는 걸 볼 수 있다. <code>[Child] onBeforeUnmount</code>은 마운트를 해제하기 직전에 호출하는 HOOK이기 때문에 아직 DOM을 가져올 수 있지만 <code>[Child] onUnmounted</code>는 마운트를 해제한 HOOK이기 때문에 DOM을 가져왔을 때 없는 걸 확인할 수 있다.</p>
<br>

<h3 id="1-5-etc">1-5. ETC</h3>
<ul>
<li><a href="https://vuejs.org/api/composition-api-lifecycle.html#onerrorcaptured">onErrorCaptured()</a></li>
<li><a href="https://vuejs.org/api/composition-api-lifecycle.html#onrendertracked">onRenderTracked()</a></li>
<li><a href="https://vuejs.org/api/composition-api-lifecycle.html#onrendertriggered">onRenderTriggered()</a></li>
<li><a href="https://vuejs.org/api/composition-api-lifecycle.html#onactivated">onActivated()</a></li>
<li><a href="https://vuejs.org/api/composition-api-lifecycle.html#ondeactivated">onDeactivated()</a></li>
<li><a href="https://vuejs.org/api/composition-api-lifecycle.html#onserverprefetch">onServerPrefetch()</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Vue]Provide / Inject]]></title>
            <link>https://velog.io/@l__chwon/VueProvide-Inject</link>
            <guid>https://velog.io/@l__chwon/VueProvide-Inject</guid>
            <pubDate>Wed, 24 Jul 2024 05:12:47 GMT</pubDate>
            <description><![CDATA[<h1 id="💻-provide">💻 Provide</h1>
<blockquote>
<p>하위 컴포넌트 항목에 데이터를 제공하려면 <strong>provider</strong> 역할을 하는 상위 컴포넌트 <code>setup()</code> 함수 내부에 쓸 수 있다.</p>
</blockquote>
<pre><code class="language-html">&lt;script&gt;
  import { provide } from &#39;vue&#39;;

  export default {
      setup(){
          provide(&#39;message&#39;, &#39;hello!&#39;)
      },
  };
&lt;/script&gt;</code></pre>
<p><code>provide()</code> 함수는 두 개의 파라미터를 받는다.</p>
<ul>
<li>첫 번째 파라미터는 <strong>주입 키</strong>:<code>문자열</code> 또는 <code>Symbol</code>이 될 수 있다. <strong>주입 키</strong>는 하위 컴포넌트에서 주입된 값을 조회하는데 사용된다.</li>
<li>두 번째 파라미터는 <strong>제공된 값</strong>: 값은 refs와 같은 반응성 데이터를 포함하여 모든 유형이 될 수 있다.</li>
</ul>
<br>

<h2 id="1-1-reactivity">1-1. Reactivity</h2>
<blockquote>
<p>Provide / Inject를 반응성 데이터로 제공할 때 가<strong>능한 모든 변경을 provider 내부에서 하는 것이 좋다.</strong> 이렇게 Provider 내부에 배치되면 향후 유지관리가 용이하다. <br>만약에 Injector 내부 컴포넌트에서 반응성 데이터를 변경해야 하는 경우 데이터 변경을 제공하는 함수를 함께 제공하는 것이 좋다.</p>
</blockquote>
<pre><code class="language-html">&lt;script&gt;
  // Provider
  const message = ref(&#39;Hi two&#39;);
  const updateMessage = () =&gt; {
      message.value = &#39;world!&#39;;
  };
  provide(&#39;message&#39;, {message, updateMessage});
&lt;/script&gt;</code></pre>
<pre><code class="language-html">&lt;script&gt;
  // Injector
  const { message, updateMessage } = inject(&#39;message&#39;);</code></pre>
<p>그리고 주입된 컴포넌트에서 제공된 값을 변경할 수 없도록 하려면 <code>readonly()</code> 함수를 사용할 수 있다.</p>
<pre><code class="language-html">&lt;script&gt;
  import { provide, readonly, ref } from &#39;vue&#39;;
  provide(&#39;count&#39;, readonly(count));
&lt;/script&gt;</code></pre>
<br>

<h2 id="1-2-symbol-키-사용">1-2. Symbol 키 사용</h2>
<blockquote>
<p>대규모 애플리케이션에서 다른 개발자와 함께 작업할 때 잠재적 충돌을 피하기 위해 Symbol 주입 키를 사용하는 것이 가장 좋다.</p>
</blockquote>
<pre><code class="language-html">&lt;script&gt;
  // keys.js
  export const myInjectionKey = Symbol()
&lt;/script&gt;</code></pre>
<pre><code class="language-html">&lt;script&gt;
  // in provider component
  import { provide } from &#39;vue&#39;
  import { myInjectionKey } from &#39;./keys.js&#39;

  provide(myInjectionKey, {
   /* data to provide */
  })
&lt;/script&gt;</code></pre>
<pre><code class="language-html">&lt;script&gt;
  // in injector component
  import { inject } from &#39;vue&#39;
  import { myInjectionKey } from &#39;./keys.js&#39;

  const injected = inject(myInjectionKey)
&lt;/script&gt;</code></pre>
<br>

<h2 id="1-3-app-level-provide">1-3. App-level Provide</h2>
<blockquote>
<p>컴포넌트에서 데이터를 제공하는 것 외에 App-level에서 제공할 수도 있다.</p>
</blockquote>
<pre><code class="language-html">&lt;script&gt;
  import { createApp } from &#39;vue&#39;;
  import App from &#39;./App.vue&#39;
  const app = createApp(App);
  app.provide(&#39;appMessage&#39;, &#39;hello app message&#39;)
  app.mount(&#39;#app&#39;);
&lt;/script&gt;</code></pre>
<br>

<h3 id="🔨-provide--inject-사용-예">🔨 Provide / Inject 사용 예</h3>
<p>App-level에서의 Provide는 앱에서 렌더링되는 모든 컴포넌트에서 사용할 수 있다. 이것은 Plugin을 작성할 때 유용합니다. Vue2에서 컴포넌트 인스턴스 객체를 추가할 때 global property에 추가 했으나, Vue3에서 Composition API setup 함수에서는 컴포넌트 인스턴스에 접근할 수 없다. 이때 대신하여 Provide / Inject를 사용할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Vue]Slots]]></title>
            <link>https://velog.io/@l__chwon/VueSlots</link>
            <guid>https://velog.io/@l__chwon/VueSlots</guid>
            <pubDate>Tue, 23 Jul 2024 07:20:17 GMT</pubDate>
            <description><![CDATA[<h1 id="💻-slots">💻 Slots</h1>
<blockquote>
<p>HTML 요소와 마찬가지로 우리가 만든 컴포넌트에 콘텐츠를 전달할 수 있으면 유용하다. <code>&lt;FancyButton&gt;</code>컴포넌트를 만든 후 콘텐츠를 전달해보자.</p>
</blockquote>
<pre><code class="language-html">&lt;!-- FancyButton.vue --&gt;
&lt;template&gt;
  &lt;button class=&quot;fancy-btn&quot;&gt;
      &lt;slot&gt;&lt;/slot&gt;
  &lt;/button&gt;
&lt;/template&gt;</code></pre>
<p>-&gt; Style
위에 정의한 컴포넌트를 부모 컴포넌트에 사용해보자</p>
<pre><code class="language-html">&lt;FancyButton&gt;
    &lt;!-- 슬롯 콘텐츠 --&gt;
      Click!!
&lt;/FancyButton&gt;</code></pre>
<p><code>slot</code> <strong>요소는 부모 컴포넌트에서 제공하는 콘텐츠를 나타내는 슬롯 콘텐츠다.</strong> 그리고 슬롯은 텍스트 뿐만아니라 HTML요소, 컴포넌트 등 다양한 모든 콘텐츠가 될 수 있다.</p>
<h2 id="named-slots">Named Slots</h2>
<blockquote>
<p><code>&lt;slot&gt;</code> 요소에 이름을 부여하여 여러개의 <code>&lt;slot&gt;</code>을 정의할 수 있다.</p>
</blockquote>
<pre><code class="language-html">&lt;!-- BaseCard.vue --&gt;
&lt;template&gt;
  &lt;article&gt;
    &lt;div&gt;
      &lt;slot name=&quot;header&quot;&gt;&lt;/slot&gt;
    &lt;/div&gt;
    &lt;div&gt;
      &lt;slot&gt;&lt;/slot&gt;
    &lt;/div&gt;
    &lt;div&gt;
      &lt;slot name=&quot;footer&quot;&gt;&lt;/slot&gt;
    &lt;/div&gt;
  &lt;/article&gt;
&lt;/template&gt;</code></pre>
<ul>
<li><code>slot</code>에 <code>name</code> 속성을 부여하여 특정 슬롯 콘텐츠가 렌더링 되어야 할 위치를 설정할 수 있다.</li>
<li><code>name</code>이 없는 <code>&lt;slot&gt;</code>의 이름은 암시적으로 <code>default</code>다.<pre><code class="language-html">&lt;!-- 부모 컴포넌트 사용 예시 --&gt;
&lt;template&gt;
&lt;BaseCard&gt;
  &lt;template v-slot:hedaer&gt;제목&lt;/template&gt;
  &lt;template v-slot:default&gt;내용&lt;/template&gt;
  &lt;template v-slot:footer&gt;푸터&lt;/template&gt;
&lt;/BaseCard&gt;
&lt;/template&gt;</code></pre>
위 예시처럼 <code>name</code>이 부여된 <code>&lt;slot&gt;</code>에 콘텐츠를 전달하려면 <code>v-slot</code> 디렉티브를 사용하여 전달할 수 있다. 그리고 <code>v-slot:전달인자</code>를 사용하여 지정한 슬롯 콘텐츠에 전달할 수 있다.<br><br><code>v-slot</code>은 <code>#</code> 단축키로 표현할 수 이있다.<pre><code class="language-html">&lt;!-- 부모 컴포넌트 사용 예시 --&gt;
&lt;template&gt;
&lt;BaseCard&gt;
  &lt;template #hedaer&gt;제목&lt;/template&gt;
  &lt;template #default&gt;내용&lt;/template&gt;
  &lt;template #footer&gt;푸터&lt;/template&gt;
&lt;/BaseCard&gt;
&lt;/template&gt;</code></pre>
그리고 default 슬롯은 암시적으로 처리할 수 있다.<pre><code class="language-html">&lt;!-- 부모 컴포넌트 사용 예시 --&gt;
&lt;template&gt;
&lt;BaseCard&gt;
  &lt;template #hedaer&gt;제목&lt;/template&gt;
  &lt;!-- 암시적으로 default slot --&gt;
  내용
  &lt;template #footer&gt;푸터&lt;/template&gt;
&lt;/BaseCard&gt;
&lt;/template&gt;</code></pre>
</li>
</ul>
<h2 id="render-scope">Render Scope</h2>
<blockquote>
<p>슬롯 콘텐츠는 상위 컴포넌트에 정의되어 있으므로 상위 컴포넌트의 데이터 영역에 접근은 가능하지만 <strong>하위 컴포넌트의 영역에는 접근할 수 없다.</strong></p>
</blockquote>
<h2 id="scoped-slots">Scoped Slots</h2>
<blockquote>
<p><strong>Render Scope</strong>에서 언급했던 것처럼 슬롯 콘텐츠는 자식 컴포넌트의 데이터에 접근할 수 없다. 하지만 <strong>슬롯 콘텐츠</strong>에서 <strong>상위 컴포넌트와 하위 컴포넌트 데이터를 모두 사용</strong>할 수 있다면 우리는 개발할 때 매우 유용하다. 이러한 방법으로 우리는 자식 컴포넌트에서 <code>&lt;slot&gt;</code> 요소를 사용할 때 props를 전달하는 것처럼 속성을 슬롯 콘텐츠에 전달할 수 있다.</p>
</blockquote>
<pre><code class="language-html">&lt;!-- MyComponent.vue --&gt;
&lt;template&gt;
  &lt;div&gt;
    &lt;slot :text=&quot;greetingMessage&quot; :count=&quot;count&quot;&gt;&lt;/slot&gt;
  &lt;/div&gt;
&lt;/template&gt;

&lt;script&gt;
  import { ref } from &#39;vue&#39;;

  export default {
      setup() {
          const greetingMessage = ref(&#39;Hello!!&#39;);
          const count = ref(1);
          return{ greetingMessage, count};
      },
  };
&lt;/script&gt;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Vue]Non-Prop 속성]]></title>
            <link>https://velog.io/@l__chwon/VueNon-Prop-%EC%86%8D%EC%84%B1</link>
            <guid>https://velog.io/@l__chwon/VueNon-Prop-%EC%86%8D%EC%84%B1</guid>
            <pubDate>Tue, 23 Jul 2024 04:33:34 GMT</pubDate>
            <description><![CDATA[<h1 id="non-prop속성-fallthrough-속성">Non-Prop속성 (fallthrough 속성)</h1>
<blockquote>
<p><strong>Non-Prop 속성</strong>은 <code>props</code> 또는 <code>event</code>에 명시적으로 선언되지 않은 속성 또는 이벤트다. 예를 들어 <code>class</code>, <code>style</code>, <code>id</code>와 같은 것들이 있다.</p>
</blockquote>
<h2 id="1-속성-상속">1. 속성 상속</h2>
<blockquote>
<p>컴포넌트가 단일 루트 요소로 구성되어 있으면 <strong>fallthough 속성</strong>은 루트 요소의 속성에 자동으로 추가된다. 예를들어 <code>&lt;MyButton&gt;</code>이라는 컴포넌트가 있다고 가정해보자.</p>
</blockquote>
<pre><code class="language-html">&lt;!-- template of &lt;MyButton&gt; --&gt;
&lt;button&gt;click me&lt;/button&gt;</code></pre>
<p>그리고 이 컴포넌트를 사용하는 부모 컴포는트는 다음과 같다.</p>
<pre><code class="language-html">&lt;MyButton class=&quot;large&quot;&gt;</code></pre>
<p>최종 렌더링된  DOM은 다음과 같다.</p>
<pre><code class="language-html">&lt;button class=&quot;larg&quot;&gt;click me&lt;/button&gt;</code></pre>
<br>

<h3 id="1-1-class-style-속성-병합">1-1. class, style 속성 병합</h3>
<ul>
<li>만약 자식 컴포넌트 루트요소에 이미 <code>calss</code>와 <code>style</code>속성이 정의되어 있으면, 부모로 받은 <code>class</code>와 <code>style</code>속성과 병합한다.<pre><code class="language-html">&lt;!-- template of &lt;MyButton&gt; --&gt;
&lt;button class=&quot;btn&quot;&gt;click me&lt;/button&gt;</code></pre>
최종 병합된 DOM은 다음과 같다.<pre><code class="language-html">&lt;button class=&quot;btn larg&quot;&gt;click me&lt;/button&gt;</code></pre>
</li>
</ul>
<h3 id="1-2-v-on-이벤트-리스너-상속">1-2. v-on 이벤트 리스너 상속</h3>
<ul>
<li><code>v-on</code> 이벤트 리스너도 동일하게 상속된다.<pre><code class="language-html">&lt;MyButton @click=&quot;onClick&quot;&gt;</code></pre>
<code>@click</code> 리스너는 <code>&lt;MyButton&gt;</code>의 컴포넌트 루트요소인 <code>&lt;button&gt;</code>요소에 추가된다.<br>만약 <code>&lt;button&gt;</code>요소에 이미 바인딩된 이벤트가 있다면 이벤트가 추가되어 두 리스너 모두 트리거 된다.</li>
</ul>
<h3 id="1-3-속성-상속-비활성화">1-3. 속성 상속 비활성화</h3>
<ul>
<li>컴포넌트가 자동으로 Non-Prop 속성을 상속하지 않도록 하려면 컴포넌트의 <code>inheritAttrs: false</code> 옵션을 설정할 수 있다.<pre><code class="language-html">&lt;template&gt;
&lt;button class=&quot;btn&quot; data-link=&quot;hello&quot;&gt;Click me&lt;/button&gt;
&lt;/template&gt;
&lt;script&gt;
export default{
    inheritAttrs: false,
}
&lt;/script&gt;</code></pre>
컴포넌트에 Non-Prop 속성을 비활성화 하는 일반적인 경우는 자식 컴포넌트의 루트요소에 이외의 다른 요소에 Non-Prop 속성을 적용하고 싶을 때다.<br><br>그리고 적용해야 하는 요소에 <code>&lt;template&gt;</code>에서 Non-Prop속성에 접근할 수 있는 내장 객체 <code>$attrs</code>로 직접 접근할 수 있다.<pre><code class="language-html">&lt;template&gt;
&lt;p&gt;fallthrough 속성: {{ $ attrs }}&lt;/p&gt;
&lt;/template&gt;</code></pre>
<code>$attrs</code>객체에는 컴포넌트에 선언되지 않은 모든 속석 <code>props</code>, <code>emits</code>(예:<code>class</code>, <code>style</code>, <code>v-on</code>등)을 포함하고 있습니다.<blockquote>
<p>몇가지 참고 사항:</p>
</blockquote>
</li>
<li><code>props</code>와 달리 <strong>Non-prop 속성</strong>은 JavaScript에서 원래 대소문자를 유지하므로 <code>foo-bar</code>와 같은 속성은 <code>$attrs[&#39;foo-bar&#39;]</code>로 접근해야한다.</li>
<li><code>@click</code>과 같은 <code>v-on</code>리스너는 <code>$attrs.onClick</code>과 같이 함수로 접근할 수 있다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Vue]Events]]></title>
            <link>https://velog.io/@l__chwon/VueEvents</link>
            <guid>https://velog.io/@l__chwon/VueEvents</guid>
            <pubDate>Mon, 22 Jul 2024 08:18:32 GMT</pubDate>
            <description><![CDATA[<h1 id="💻-events">💻 Events</h1>
<blockquote>
<p><code>props</code>를 활용하여 부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달하고 자식 컴포넌트에서는 부모 컴포넌트로 데이터를 전달하거나 또는 트리거의 목적으로 이벤트를 발생 시킬 수 있다. 그리고 이벤트는 컴포넌트의 <code>emit</code> 메서드를 통하여 발생 시킬 수 있다.</p>
</blockquote>
<p>상위 컴포넌트에서 하위 컴포넌트로 전달할 때 props로 전달이 가능하다. 그리고 하위 컴포넌트에서 상위 컴포넌트로 이벤트를 올릴 때는 emit을 활용하여 올릴 수 있다.<br><br> 템플릿 안에서 사용할 때 컴포넌트 인스턴스의 메서드인 <code>$emit</code>을 사용하였고</p>
<pre><code class="language-html">&lt;template&gt;
  &lt;button @click=&quot;$emit(&#39;someEvent&#39;)&quot;&gt;버튼&lt;/button&gt;
&lt;/template&gt;</code></pre>
<p>그러면 부모 컴포넌트에서 <code>v-on</code>(또는 <code>@</code>)을 사용하여 이벤트를 수신할 수 있다.</p>
<pre><code class="language-html">&lt;MyComponent @some-event=&quot;callFunction&quot;&gt;</code></pre>
<p><code>.once</code> 수식어는 컴포넌트 커스텀 이벤트에서도 지원됩니다.</p>
<pre><code class="language-html">&lt;MyComponent @some-event.once=&quot;callFunction&quot;&gt;</code></pre>
<br>

<p>이벤트와 함께 특정 값을 내보낼 수 있다. <code>$emit</code>함수 이벤트명에 추가로 파라미터를 넘길 수 있다.</p>
<pre><code class="language-html">&lt;template&gt;
  &lt;button @click=&quot;$emit(&#39;someEvent&#39;, &#39;Hello&#39;, &#39;Hi&#39;, &#39;~&#39;)&quot;&gt;버튼&lt;/button&gt;
&lt;/template&gt;</code></pre>
<p>그런다음 부모 컴포넌트에서 이벤트와 함께 파라미터를 받을 수 있다.</p>
<pre><code class="language-html">&lt;template&gt;
    &lt;MyComponent @some-event=&quot;callFunction&quot; /&gt;
&lt;/template&gt;

&lt;script setup&gt;
  export default{
      setup(){
          const callFunction = (word1, word2, word3) =&gt; {
              alert(word1, word2, word3);
          };
          return {
              callFinction
          }
      }
  }
&lt;/script&gt;</code></pre>
<p><br><br></p>
<h2 id="이벤트-선언하기">이벤트 선언하기</h2>
<blockquote>
<p><code>emits</code> 옵션을 사용하여 이벤트를 선언할 수 있다. 이때 이벤트 선언하는 방법은 두가지 형식으로 선언할 수 있다.</p>
</blockquote>
<ul>
<li>문자열 배열 선언</li>
<li>객체문법 선언</li>
</ul>
<p>그리고 JavaScript 코드에서 이벤트를 내보낼 때는 <code>setup()</code> 함수의 파라미터로 넘어온 <code>context.emit()</code>메서드를 사용할 수 있다.
<br></p>
<h3 id="문자열-배열-선언">문자열 배열 선언</h3>
<pre><code class="language-html">&lt;script&gt;
    export default{
          emits: [&#39;someEvent&#39;],
          setup(props, context){
              context.emit(&#39;someEvent&#39;, &#39;Hello World!&#39;)
          }
      }
&lt;/script&gt;</code></pre>
<pre><code class="language-html">&lt;script&gt;
    export default{
          emits: [&#39;someEvent&#39;],
          setup(props, { emit }){
              emit(&#39;someEvent&#39;, &#39;Hello World!&#39;)
          }
      }
&lt;/script&gt;</code></pre>
<br>

<h3 id="객체문법-선언-유효성-검사가-필요할-때">객체문법 선언 (유효성 검사가 필요할 때)</h3>
<ul>
<li><p>객체문법으로 선언할 경우 <code>validation</code>로직을 추가할 수 있다. 만약 <code>validation</code>이 없다면 <code>null</code>로 설정하면 된다.</p>
<pre><code class="language-html">&lt;script&gt;
  export default{
        emits: {
            //유효성 검사가 없는 이벤트 선언
            someEvent: null,

            //유효성 검사가 있는 이벤트 선언
            someSubmit: (result) =&gt; {
                if (email &amp;&amp; password) {
                    return true
                } else {
                    console.warn(&#39;result 값이 비어있습니다!&#39;)
                    return false
                }

            }
        },
        setup(props, context){
            context.emit(&#39;someEvent&#39;, &#39;Hello World!&#39;)
        }
    }
&lt;/script&gt;</code></pre>
<p>선택 사항이지만 컴포넌트가 작동하는 방식을 더 잘 문서화하기 위해 모든 이벤트를 정의하는 것이 좋다. 또한 Vue가 <strong>fallthrough</strong>속성에서 알려진 리스너를 제외할 수 있다.
<br><br></p>
<h2 id="v-model-만들기">v-model 만들기</h2>
<blockquote>
<p>컴포넌트를 만든 후 해당 컴포넌트에 <code>v-model</code>을 적용하려면 <code>@update:modelValue</code>이벤트를 사용하여 <code>v-model</code>을 만들 수 있다. 일반적으로 기본 HTML 요소인 <code>&lt;input&gt;</code> 태그에 <code>v-model</code>은 아래와 같이 사용한다.</p>
</blockquote>
</li>
</ul>
<pre><code class="language-html">&lt;input v-model=&quot;username&quot;&gt;</code></pre>
<p>위에 선언된 <code>v-model</code>은 아래와 같이 동작한다.</p>
<pre><code class="language-html">&lt;input :value=&quot;username&quot; @input=&quot;username = $event.target.value&quot;&gt;</code></pre>
<p>위에 기본 동작 대신 우리가 만든 컴포넌트는 아래와 같이 수행한다.</p>
<pre><code class="language-html">&lt;LabelInput :modelValue=&quot;username&quot; @update:modelValue=&quot;newValue =&gt; username = newValue&quot;&gt;
&lt;!-- props로는 :modelValue 이벤트로는 @update:modelValue를 구현하면 된다.--&gt;</code></pre>
<p>이 <code>&lt;LabelInput&gt;</code>을 실제로 동작하게 하려면 아래와 같이 컴포넌트를 정의해야한다.</p>
<ul>
<li><code>modelValue</code> <code>props</code>를 <code>:value</code>속성에 바인딩</li>
<li><code>@input</code> 이벤트에서 새 <code>@update:modelValue</code> 이벤트로 내보낸다.<pre><code class="language-html">&lt;template&gt;
&lt;label&gt;
  {{ label }}
  &lt;input type=&quot;text&quot; :value=&quot;modelValue&quot; @input=&quot;$emit(&#39;update:modelValue&#39;, $event.target.value)&quot;&gt;
&lt;/label&gt;
&lt;/template&gt;
</code></pre>
</li>
</ul>
<script>
    export default{
          props: ['modelValue', 'label'],
          emits: ['update:modelValue'],
      };
</script>
<pre><code>그리고 아래와 같이 우리가 만든 컴포넌트에 `v-model`을 적용할 수 있다.
```html
&lt;LabelInput label=&quot;이름&quot; v-model=&quot;username&quot;&gt;</code></pre><br>

<h3 id="computed-이용하기">Computed 이용하기</h3>
<ul>
<li>컴포넌트 안에서 computed를 사용하여 v-model을 구현할 수 있다.<pre><code class="language-html">&lt;template&gt;
&lt;label&gt;
  {{ label }}
  &lt;input type=&quot;text&quot; :value=&quot;value&quot;&gt;
&lt;/label&gt;
&lt;/template&gt;
</code></pre>
</li>
</ul>
<script>
      import { computed } from 'vue';

    export default{
          props: ['modelValue', 'label'],
          emits: ['update:modelValue'],
          setup(props, context){
              const value = computed({
                  get() {
                      return props.modelValue;
                  },
                  set() {
                      contex.emit('update:modelValue', value);
                  },
              });
              return { value };
          }
      };
</script>
<pre><code>전달 할 수 있다. 이럴때 `emit`를 사용할 수 있다.

&lt;br&gt;

### v-model 전달인자
- 기본적으로 `v-model`은 컴포넌트에서 `modelValue` `props`와 `update:modelValue`이벤트로 사용한다. 하지만 `전달인자(Arguments)`를 사용하여 이러한 이름을 수정할 수 있다.
```html
&lt;BookComponent v-model:title=&quot;bookTitle&quot;&gt;</code></pre><p>이 경우 자식 컴포넌트에서는 <code>:title</code>을 속성으로 정의하고 <code>update:title</code>로 이벤트를 내보내야 한다.</p>
<pre><code class="language-html">&lt;template&gt;
  &lt;article&gt;
    &lt;strong&gt;책 이름&lt;/strong&gt; :
    &lt;input type=&quot;text&quot; :value=&quot;title&quot; @input=&quot;$emit(&#39;update:title&#39;, $event.target.calue)&quot; &gt;
  &lt;/article&gt;
&lt;/template&gt;

&lt;script&gt;
    export default {
          props: [&#39;title&#39;],
          emits: [&#39;update: title&#39;],
      }
&lt;/script&gt;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Vue]Props]]></title>
            <link>https://velog.io/@l__chwon/VueProps</link>
            <guid>https://velog.io/@l__chwon/VueProps</guid>
            <pubDate>Fri, 19 Jul 2024 03:24:12 GMT</pubDate>
            <description><![CDATA[<h1 id="💻props">💻Props</h1>
<blockquote>
<p>블로그를 구축하는 경우 블로그 게시글을 나타내는 컴포넌트가 있다고 가정해보자 이때 모든 블로그 게시글의 UI나 레이아웃은 동일하지만 게시글의 제목, 내용과 같은 데이터는 각각 다르다. 그러면 <strong>컴포넌트에 각각 제목이나 내용과 같은 데이터를 전달해야 하는데</strong> 이때 <code>props</code>를 사용하여 컴포넌트로 데이터(속성)을 전달할 수 있다.</p>
</blockquote>
<h2 id="props-란">Props 란?</h2>
<ul>
<li><code>Props</code>란? <strong>컴포넌트에 등록할 수 있는 <span style="color:#666">사용자 정의 속성</span></strong>입니다. 블로그 게시글 컴포넌트에 사용자 정의 속성을 선언하면 이 컴포넌트를 사용하는 부모 컴포넌트에서 데이터(속성)을 전달 할 수 있습니다.</li>
</ul>
<h2 id="1-props-선언">1. Props 선언</h2>
<ul>
<li>Vue 컴포넌트에는 명시적 <code>Props</code> 선언이 필요합니다. 왜냐하면 컴포넌트에 전달된 외부 props가 fallthrough 속성으로 처리되어야 함을 알 수 있습니다.<blockquote>
<p>🔧 <strong>fallthrough 속성</strong>
props 또는 emits에 명시적으로 선언되지 않은 속성 또는 이벤트</p>
</blockquote>
</li>
</ul>
<h3 id="1-1-문자열-배열-선언">1-1. 문자열 배열 선언</h3>
<ul>
<li>컴포넌트에 <code>props</code> 옵션을 사용하여 선언할 수 있다.</li>
</ul>
<pre><code class="language-html">&lt;script&gt;
export default{
    props: [title],
      setup(props){
          console.log(props.title)
      }
}
&lt;/script&gt;</code></pre>
<h3 id="1-2-객체문법-선언">1-2. 객체문법 선언</h3>
<ul>
<li>문자열 배열을 사용하여 <code>props</code>를 선언하는 것 외에도 객체 문법을 사용하여 속성 타입과 함께 선언 할 수 있다.</li>
</ul>
<pre><code class="language-html">&lt;script&gt;
export default{
    props: {
          //key: value
          title: String,
          likes: Number
      },
      setup(props){
          console.log(props.title)
          console.log(props.likes)
      }
}
&lt;/script&gt;</code></pre>
<p><code>porps</code> 선언시 <code>key</code>는 속성명이고 <code>value</code>는 속성 타입이다. 더 자세히 선언하고 싶으면 <code>value</code>에 고급 옵션인 객체를 선언할 수 있다. <strong><span style="color:orange">vue 스타일 가이드에서는 문자열로 간단히 선언하는 것 보다 객체 타입으로 디테일하게 선언하는 것을 권장하고 있다.</span></strong></p>
<blockquote>
<ul>
<li><code>type</code>: <code>String</code>, <code>Number</code>, <code>Boolean</code>, <code>Array</code>, <code>Object</code>, <code>Date</code>, <code>Function</code>, <code>Symbol</code> 모든 기본 생성자 또는 모든 사용자 정의 타입이 올 수 있다.(예: <code>Person</code>, <code>Animal</code>)
그리고 <code>[Number, String]</code> 배열을 이용하여 여러개의 타입을 선언할 수 있다.</li>
</ul>
</blockquote>
<ul>
<li><code>default</code>: 속성값이 비어 있거나 <code>undefined</code>를 전달 받는 경우 기본값을 선언할 수 있다. 그리고 객체 또는 배열 타입인 경우 기본값을 <strong>팩토리 함수</strong>를 사용하여 반환해야 한다.</li>
<li><code>required</code>: 속성이 필수값이라면 <code>true</code>로 해서 설정할 수 있다.</li>
<li><code>validator</code>: 속성값의 유효성 검사가 필요할 때 사용할 수 있다.</li>
</ul>
<p>✨ <strong>컴포넌트 사용 시 <code>type</code>, <code>required</code>, <code>validator</code> 명시된 사항을 위반할 때 개발모드에서 콘솔 경고가 발생한다.</strong></p>
<br>
<br>
`props`를 이용한 예시를 들어보자

<p><strong>AppCard.vue</strong></p>
<pre><code class="language-html">
//AppCard.vue

&lt;template&gt;
    &lt;div class=&quot;card&quot;&gt;
        &lt;div class=&quot;card-body&quot;&gt;
              &lt;!-- 항목에 맞게 넣어준다. --&gt;
            &lt;h5 class=&quot;card-title red&quot;&gt;{{ title }}&lt;/h5&gt;
            &lt;p class=&quot;card-text&quot;&gt;{{ contents }}&lt;/p&gt;
            &lt;a href=&quot;#&quot; class=&quot;btn btn-primary&quot;&gt;Go somewhere&lt;/a&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/template&gt;

&lt;script&gt;
export default {
      //props라는 속성을 선언한다.
    props: [&quot;title&quot;, &quot;contents&quot;],
    setup() {
        return {};
    },
};
&lt;/script&gt;
&lt;style&gt;&lt;/style&gt;</code></pre>
<p><strong>TheView.vue</strong></p>
<pre><code class="language-html">&lt;script&gt;

//TheView.vue

&lt;template&gt;
    &lt;main&gt;
        &lt;div class=&quot;container text-center py-4&quot;&gt;
            &lt;div class=&quot;row g-3&quot;&gt;
                &lt;div class=&quot;col col-4&quot;&gt;
                      //AppCard.vue 파일에서 props 선언한 것을 가져와 정적인 값을 줄 수 있다.
                    &lt;AppCard title=&quot;제목1&quot; contents=&quot;내용1&quot;&gt;&lt;/AppCard&gt;
                &lt;/div&gt;
                &lt;div class=&quot;col col-4&quot;&gt;
                      //반응형 데이터를 선언하여서도 넣을 수 있다.(바인딩하여 사용)
                    &lt;AppCard :title=&quot;post.title&quot; :contents=&quot;post.contents&quot;&gt;&lt;/AppCard&gt;
                &lt;/div&gt;
                &lt;div class=&quot;col col-4&quot;&gt;
                    &lt;AppCard&gt;&lt;/AppCard&gt;
                &lt;/div&gt;
                &lt;div class=&quot;col col-4&quot;&gt;
                    &lt;AppCard&gt;&lt;/AppCard&gt;
                &lt;/div&gt;
                &lt;div class=&quot;col col-4&quot;&gt;
                    &lt;AppCard&gt;&lt;/AppCard&gt;
                &lt;/div&gt;
                &lt;div class=&quot;col col-4&quot;&gt;
                    &lt;AppCard&gt;&lt;/AppCard&gt;
                &lt;/div&gt;
                &lt;div class=&quot;col col-4&quot;&gt;
                    &lt;AppCard&gt;&lt;/AppCard&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/main&gt;
&lt;/template&gt;

&lt;script&gt;
import AppCard from &quot;@/components/AppCard.vue&quot;;
import { reactive } from &quot;vue&quot;;
export default {
    components: {
        AppCard,
    },
    setup() {
          //바인딩하여 사용할 수 있다.
        const post = reactive({
            title: &quot;제목2&quot;,
            contents: &quot;내용2&quot;,
        });
        return { post };
    },
};
&lt;/script&gt;

&lt;style&gt;&lt;/style&gt;</code></pre>
<br>

<p><code>v-for</code>을 이용해서 루프를 돌릴 수 있다.</p>
<p><strong>TheView.vue</strong></p>
<pre><code class="language-html">&lt;template&gt;
    &lt;main&gt;
        &lt;div class=&quot;container text-center py-4&quot;&gt;
            &lt;div class=&quot;row g-3&quot;&gt;
                &lt;div v-for=&quot;post in posts&quot; :key=&quot;post.id&quot; class=&quot;col col-4&quot;&gt;
                    &lt;AppCard :title=&quot;post.title&quot; :contents=&quot;post.contents&quot;&gt;&lt;/AppCard&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    &lt;/main&gt;
&lt;/template&gt;

&lt;script&gt;
import AppCard from &quot;@/components/AppCard.vue&quot;;
import { reactive } from &quot;vue&quot;;
export default {
    components: {
        AppCard,
    },
    setup() {
        //v-for을 이용한 루프
        const posts = reactive([
            { id: 1, title: &quot;제목1&quot;, contents: &quot;내용1&quot; },
            { id: 2, title: &quot;제목2&quot;, contents: &quot;내용2&quot; },
            { id: 3, title: &quot;제목3&quot;, contents: &quot;내용3&quot; },
            { id: 4, title: &quot;제목4&quot;, contents: &quot;내용4&quot; },
            { id: 5, title: &quot;제목5&quot;, contents: &quot;내용5&quot; },
        ]);

        return { posts };
    },
};
&lt;/script&gt;

&lt;style lang=&quot;scss&quot; scoped&gt;&lt;/style&gt;
</code></pre>
<br>

<p>** v-for을 이용한 결과화면**</p>
<p><img src="https://velog.velcdn.com/images/l__chwon/post/3504af8d-e242-46ad-a584-fa40deb486b9/image.png" alt=""></p>
<br>

<p>객체문법을 이용한 방법
<strong>AppCard.vue</strong></p>
<pre><code class="language-html">&lt;template&gt;
    &lt;div class=&quot;card&quot;&gt;
        &lt;div class=&quot;card-body&quot;&gt;
            &lt;!-- type : new, notice --&gt;
            &lt;!-- &lt;span class=&quot;badge text-bg-secondary&quot;&gt;{{
                type == &quot;news&quot; ? &quot;뉴스&quot; : &quot;공지사항&quot;
            }}&lt;/span&gt; --&gt;
            &lt;span class=&quot;badge text-bg-secondary&quot;&gt; {{ typeName }}&lt;/span&gt;
            &lt;h5 class=&quot;card-title red mt-2&quot;&gt;{{ title }}&lt;/h5&gt;
            &lt;p class=&quot;card-text&quot;&gt;{{ contents }}&lt;/p&gt;

            &lt;!-- v-if를 통해서 isLike가 true일 경우 위에 아닐경우 밑에 태그가 노출된다.--&gt;
            &lt;!-- &lt;a href=&quot;#&quot; v-if=&quot;isLike&quot; class=&quot;btn btn-danger&quot;&gt;좋아요&lt;/a&gt;
            &lt;a href=&quot;#&quot; v-else class=&quot;btn btn-outline-danger&quot;&gt;좋아요&lt;/a&gt; --&gt;

            &lt;!-- computed를 이용한 태그 묶기 --&gt;
            &lt;a href=&quot;#&quot; class=&quot;btn&quot; :class=&quot;isLikeClass&quot;&gt;좋아요&lt;/a&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/template&gt;

&lt;script&gt;
import { computed } from &quot;vue&quot;;

export default {
    //객체 타입으로 선언
    props: {
        //속성을 정의
        type: {
            type: String,
            default: &quot;news&quot;,
            //타입이 명확히 들어가 있는지 유효성 검사를 진행한다. 만약 타입이 잘못 들어가 있으면 콘솔에 &#39;Invalid prop&#39;라는 오류를 볼 수 있다.
            validator: value =&gt; {
                return [&quot;news&quot;, &quot;notice&quot;].includes(value);
            },
        },
        title: {
            type: String,
            required: true,
        },
        contents: {
            type: String,
            required: true,
        },
        isLike: {
            type: Boolean,
            default: false,
        },
        //레퍼런스타입(객체, 배열 등) 디폴트를 설정할 때는 기본값을 반환하는 팩토리 함수를 선언해야 한다.
        obj: {
            type: Object,
            default: () =&gt; {
                return;
            },
        },
    },
    // setup() 함수의 첫 번째 매개변수로 porps 객체를 받아 사용할 수 있다.
    setup(props) {
        // computed를 이용해 좋아요를 하나로 묶어보자
        const isLikeClass = computed(() =&gt;
            props.isLike ? &quot;btn-danger&quot; : &quot;btn-outline-danger&quot;,
        );

        const typeName = computed(() =&gt;
            props.type === &quot;news&quot; ? &quot;뉴스&quot; : &quot;공지사항&quot;,
        );
        return { isLikeClass, typeName };
    },
};
&lt;/script&gt;
&lt;style&gt;&lt;/style&gt;
</code></pre>
<br>

<h2 id="2-단방향-데이터-흐름">2. 단방향 데이터 흐름</h2>
<blockquote>
<p>모든 <code>props</code>는 상위 속성과 하위 속성간에 단방향 바인딩으로 형성되어 있다. 만약 상위 속성이 업데이트되면 하위 속성도 업데이트되지만 그 반대는 아니다. 이러한 성질은 하위 속성 변경 실수로 상위 속성을 변경하여 앱의 데이터 흐름을 이해하기 어렵게 만드는 것을 방지할 수 있다. <br><br> 또한 상위 컴포넌트가 업데이트될 때마다 하위 컴포넌트의 모든 <code>props</code>는 최신 상태도 최기화 된다. 그렇기 때문에 <strong>자식 컴포넌트 내부에서 props</strong>를 변경하지 않아야 한다. </p>
</blockquote>
<br>

<h2 id="3-boolean-casting">3. Boolean Casting</h2>
<blockquote>
<p><code>Boolean</code>타입의 Props는 특별한 캐스팅 규칙이 있습니다. <code>&lt;MyComponet&gt;</code>가 다음과 같이 선언되어 있다.</p>
</blockquote>
<pre><code class="language-html">&lt;script&gt;
    export default{
          props: {
              disabled: Boolean
          }
      }
&lt;/script&gt;</code></pre>
<p><code>MyComponent</code>는 다음과 같이 사용할 수 있다.</p>
<pre><code class="language-html">&lt;!-- :disabled=&quot;true&quot; 전달하는 것과 동일하다. --&gt;
&lt;MyComonent disabled&gt;
&lt;!-- :disabled=&quot;false&quot; 전달하는 것과 동일하다. --&gt;
&lt;MyComonent&gt;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Vue]Single File Component(SFC)]]></title>
            <link>https://velog.io/@l__chwon/VueSingle-File-ComponentSFC</link>
            <guid>https://velog.io/@l__chwon/VueSingle-File-ComponentSFC</guid>
            <pubDate>Thu, 18 Jul 2024 08:29:17 GMT</pubDate>
            <description><![CDATA[<h1 id="💻single-file-component">💻Single File Component</h1>
<blockquote>
<p>Vue에서 Single File Component(SFC, <code>*.vue</code>)는 Vue 컴포넌트의 템플릿(template), 로직(script), 스타일(style)을 하나의 파일로 캡슐화하는 특수파일 형식이다. 확장자는 <code>*.vue</code>이다.</p>
</blockquote>
<br>

<h2 id="언어-블록">언어 블록</h2>
<hr>
<h3 id="1-template">1. <code>&lt;template&gt;</code></h3>
<ol>
<li>각 <code>*.vue</code>파일은 한 번에 최대 하나의 <code>top-level &lt;template&gt;</code>블록을 포함할 수 있다.</li>
<li>콘텐츠는 추출되어 <code>@vue/compiler-dom</code>으로 전달되고, JavaScript 렌더 기능으로 사전 컴파일되고, <code>render</code>옵션으로 내보내어 컴포넌트에 첨부된다.</li>
</ol>
<h3 id="2-script">2. <code>&lt;script&gt;</code></h3>
<ol>
<li>각 <code>*.vue</code>파일은 한 번에 최대 하나의 <code>&lt;script&gt;</code>블록을 포함할 수 있다.(<strong>&lt; script  setup &gt;</strong>제외)</li>
<li>스크립트는 ES 모듈로 실행된다.</li>
<li><code>defaulte export</code>는 일반 객체 또는 <code>defineComponent</code>의 반환 값으로 Vue 컴포넌트 옵션 객체여야 한다.</li>
</ol>
<h3 id="3-script-setup">3. <code>&lt;script setup&gt;</code></h3>
<ol>
<li>각 <code>*.vue</code> 파일은 한 번에 최대 하나의 <code>&lt;script setup&gt;</code>블록을 포함할 수 있다.</li>
<li><code>&lt;script setup&gt;</code>은 사전에 처리되어 컴포넌트의 <code>setup()</code>함수로 사용된다. 즉 컴포넌트의 각 인스턴스에 대해 실행된다. <code>&lt;script setup&gt;</code>의 최상위 바인딩은 템플릿에 자동으로 노출된다.</li>
</ol>
<h3 id="4-style">4. <code>&lt;style&gt;</code></h3>
<ol>
<li>단일 <code>*.vue</code>파일에는 여러 <code>&lt;style&gt;</code>태그가 포함될 수 있다.</li>
<li><code>&lt;style&gt;</code> 태그는 현재 컴포넌트에 스타일을 캡슐화하는데 도움이 되도록 <code>scoped</code> 또는 <code>module</code> 속성을 가질 수 있다. 캡슐화 모드가 다른 여러 <code>&lt;style&gt;</code> 태그를 동일한 구성 요소에서 혼합할 수 있다.</li>
</ol>
<br>

<h2 id="custom-blocks">Custom Blocks</h2>
<hr>
<blockquote>
<p>프로젝트별 요구사항에 따라 <code>*.vue</code>파일에 <strong>사용자 정의 블록</strong>을 추가할 수 있다. 예를 들면 다음과 같은 사용자 정의 블록 예가 있다.</p>
</blockquote>
<ul>
<li>Gridsome: <code>&lt;page-query&gt;</code></li>
<li>vite-plugin-vue-gql: <code>&lt;gql&gt;</code></li>
<li>vue-i18n: <code>&lt;i18n&gt;</code></li>
</ul>
<br>

<h2 id="전처리기">전처리기</h2>
<hr>
<blockquote>
<p><code>&lt;script&gt;</code>의 <code>lang</code>속성을 사용하여 전처리기 언어를 선언할 수 있다. 일반적인 경우에는 TypeScript를 사용하는 것이다.</p>
</blockquote>
<pre><code class="language-javascript">&lt;script lang=&quot;ts&quot;&gt;
   //use TypeScript
&lt;/script&gt;</code></pre>
<p><code>lang</code>속성은 모든 블록에 적용할 수 있다. 예를 들어 <strong>SASS</strong>와 <strong>Pug</strong>를 <code>&lt;style&gt;</code>과 <code>&lt;template&gt;</code>에 적용할 수 있다.</p>
<pre><code class="language-html">&lt;template lang=&quot;pug&quot;&gt;
  p {{ msg }}
&lt;/template&gt;

&lt;style lang=&quot;scss&quot;&gt;
    $primary-color:#333;
      body{
          color: $primary-color;
      }
&lt;/style&gt;</code></pre>
<br>

<h2 id="src-가져오기">Src 가져오기</h2>
<hr>
<blockquote>
<p><code>*.vue</code> 컴포넌트를 여러 파일로 분할하려는 경우 <code>src</code>속성을 사용하여 <code>language block</code>에 대한 외부 파일을 가져올 수 있다.</p>
</blockquote>
<pre><code class="language-html">&lt;template src=&quot;./templatye.html&quot;&gt;&lt;/template&gt;
&lt;style src=&quot;./style.css&quot;&gt;&lt;/style&gt;
&lt;script src=&quot;./script.js&quot;&gt;&lt;/script&gt;</code></pre>
<p><code>src</code>로 가져오는 것은 webpack 모듈 요청과 동일한 경로 확인 규칙을 따릅니다. 즉, 다음을 의미한다. 상대 경로는 <strong>./</strong> 로 시작해야 한다.
<br>
npm 종속성에서 리소스를 가져올 수 있습니다.</p>
<pre><code class="language-html">&lt;!-- import a file from the installed &quot;todomvc-app-css&quot; npm package --&gt;
&lt;style src=&quot;./style.css&quot;&gt;&lt;/style&gt;</code></pre>
<p>src 가져오기는 사용자 정의 블록에서도 작동합니다.</p>
<pre><code class="language-html">&lt;unit-test src=&quot;./unit-test.js&quot;&gt;
&lt;/unit-test&gt;</code></pre>
<br>

<h2 id="css-기능">CSS 기능</h2>
<hr>
<blockquote>
<p>Scoped CSS
<code>&lt;style&gt;</code>태그에 <code>scoped</code>속성이 있는 경우 해당 CSS는 <strong>현재 컴포넌트의 요소에만</strong> 적용된다.</p>
</blockquote>
<pre><code class="language-html">&lt;template&gt;
  &lt;p class=&quot;greeting&quot;&gt;greeting&lt;/p&gt;
&lt;/template&gt;
&lt;style scoped&gt;
  .greeting{ color:red; font-weight:bold;}
&lt;/style&gt;</code></pre>
<p>원리는 PostCSS 사용하여 아래와 같이 변환 된다.</p>
<pre><code class="language-html">&lt;template&gt;
  &lt;p class=&quot;greeting&quot; data-v-7ba5bd90&gt;greeting&lt;/p&gt;
&lt;/template&gt;
&lt;style scoped&gt;
  .greeting[data-v-7ba5bd90]{ color:red; font-weight:bold;}
&lt;/style&gt;</code></pre>
<br>

<blockquote>
<p>CSS 모듈
<code>&lt;style module&gt;</code>은 CSS 모듈로 컴파일되고, CSS 클래스 <code>$style</code>객체의 속성으로 노출한다.</p>
</blockquote>
<pre><code class="language-html">&lt;template&gt;
  &lt;p :class=&quot;$style.red&quot;&gt;This should be red&lt;/p&gt;
&lt;/template&gt;
&lt;style module&gt;
  .red{ color: red;}
&lt;/style&gt;</code></pre>
<p>결과 클래스는 충돌을 피하기 위해 해시되어 CSS 범위를 현재 컴포넌트로만 지정하는 것과 동일한 효과를 얻습니다. 전역 예외 및 구성과 같은 자세한 내용은 CSS 모듈 사양을 참조
<strong>Custom Inject Name</strong>
모듈 속성에 값을 제공하여 주입된 클래스 객체의 속성 키를 변경할 수 있다.</p>
<pre><code class="language-html">&lt;template&gt;
  &lt;p :class=&quot;classes.red&quot;&gt;red&lt;/p&gt;
&lt;/template&gt;
&lt;style module=&quot;classes&quot;&gt;
  .red{ color: red;}
&lt;/style&gt;</code></pre>
<br>

<blockquote>
<p><code>v-bind()</code> in CSS
SFC <code>&lt;style&gt;</code> 태그는 <code>v-bind</code> CSS 기능을 사용하여 CSS 값을 동적 구성 요소 상태에 연결하는 것을 지원한다.</p>
</blockquote>
<pre><code class="language-html">&lt;template&gt;
  &lt;div class=&quot;text&quot;&gt;hello&lt;/div&gt;
&lt;/template&gt;
&lt;script&gt;
    export default {
          data(){
              return{
                  color: &#39;red&#39;
              }
          }
      }
&lt;/script&gt;

&lt;style&gt;
.text{ color: v-bind(color);}
&lt;/style&gt;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Vue]컴포넌트 기초]]></title>
            <link>https://velog.io/@l__chwon/Vue%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EA%B8%B0%EC%B4%88-0piaoh64</link>
            <guid>https://velog.io/@l__chwon/Vue%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EA%B8%B0%EC%B4%88-0piaoh64</guid>
            <pubDate>Thu, 18 Jul 2024 06:18:45 GMT</pubDate>
            <description><![CDATA[<h1 id="💻component">💻Component</h1>
<ul>
<li>컴포넌트를 정의하는 방법은 <code>Single-File Component(SFC)</code>를 사용하는 방법과 <code>문자열 템플릿(String template)</code>으로 정의하는 방법이 있다.
<br><br></li>
</ul>
<h2 id="1-single-file-componentsfc">1. Single-File Component(SFC)</h2>
<blockquote>
<p><strong>빌드 도구를 사용할 때</strong> 컴포넌트는 일반적으로 Single-File Component(SFC)로 정의할 수 있다. SFC는 확장 <code>*.vue</code>를 가진 단일 파일이다.</p>
</blockquote>
<pre><code class="language-html">&lt;template&gt;
&lt;/template&gt;

&lt;script&gt;
&lt;/script&gt;

&lt;style&gt;&lt;/style&gt;</code></pre>
<p>이렇게 <code>template</code>, <code>script</code>, <code>style</code>로 나눠져 있다.
<br></p>
<h3 id="1-1-컴포넌트-등록">1-1. 컴포넌트 등록</h3>
<ul>
<li>Vue 컴포넌트는 <code>&lt;template&gt;</code>안에서 발견 되었을 때 Vue가 구현 위치를 알 수 있도록 <code>등록</code>해야합니다. 그리고 컴포넌트를 등록하는 방법은 <code>전역(Global)</code> 및 <code>지역(Local)</code> 두 가지가 있다.</li>
</ul>
<h4 id="1-전역-등록">1) 전역 등록</h4>
<ul>
<li>우리는 <code>app.componet()</code>메서드를 사용하여 <strong>현재 Vue 애플리케이션에서 전역적으로 사용</strong>할 수 있도록 할 수 있다.</li>
</ul>
<h4 id="2-지역-등록">2) 지역 등록</h4>
<ul>
<li>전역 등록은 편리하지만 다음과 같은 몇가지 단점이 있다.</li>
</ul>
<ol>
<li><p>webpack(또는 Vite)과 같은 빌드 시스템을 사용하는 경우 컴포넌트를 전역 등록하는 것은 컴포넌트를 사용하지 않더라도 계속해서 최종 빌드에 해당 컴포넌트가 포함되는 것을 의미한다. 이는 사용자가 다운로드하는 자바스크립트 파일의 크기를 불필요하게 증가시킨다.</p>
</li>
<li><p>전역 등록을 계속 하게 되면 애플리케이션의 컴포넌트간 종속 관계를 확인하기 힘듭니다. 상위 컴포넌트, 하위 컴포넌트 구분이 힘들면 유지보수를 하기에 매우 어려워지게 된다.</p>
</li>
</ol>
<h2 id="2-문자열-템플릿string-template">2. 문자열 템플릿(String Template)</h2>
<blockquote>
<p><strong>빌드 도구를 사용하지 않을 때</strong> 컴포넌트는 Vue 옵션인을 포함하는 일반 JavaScript 객체로 정의할 수 있다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Vue]Bootstrap5 설치]]></title>
            <link>https://velog.io/@l__chwon/VueBootstrap5-%EC%84%A4%EC%B9%98</link>
            <guid>https://velog.io/@l__chwon/VueBootstrap5-%EC%84%A4%EC%B9%98</guid>
            <pubDate>Thu, 30 May 2024 05:43:34 GMT</pubDate>
            <description><![CDATA[<h2 id="✍🏻bootstrap5">✍🏻Bootstrap5</h2>
<ul>
<li>Bootstrap은 UI Framesork다. 사전에 정의된 CSS를 사용하여 최소한의 노력으로 쉽게 HTML Markup을 도와준다. </li>
</ul>
<p>터미널을 통해 설치해준다.</p>
<pre><code>npm install bootstrap</code></pre><p><code>main.js</code>에 import를 한다.</p>
<pre><code class="language-javascript">import &quot;bootstrap/dist/css/bootstrap.min.css&quot;;

createApp(App).mount(&quot;#app&quot;);
//아래 코드는 마운트 바로 아래에 넣어준다.
import &quot;bootstrap/dist/js/bootstrap.js&quot;;</code></pre>
<p>Bootstrap와 같이 Vuerify를 활용해도 괜찮다.
<a href="https://vuetifyjs.com/en/">https://vuetifyjs.com/en/</a></p>
]]></description>
        </item>
    </channel>
</rss>