<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>seank-log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Wed, 14 May 2025 09:09:45 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>seank-log</title>
            <url>https://velog.velcdn.com/images/seank-log/profile/b0e86a77-74bd-495f-b4da-34912708b845/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. seank-log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/seank-log" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Vue 3 변수 정의 완전 정복 – ref, reactive, toRef, toRefs 언제 어떻게 쓸까?]]></title>
            <link>https://velog.io/@seank-log/Vue-3-%EB%B3%80%EC%88%98-%EC%A0%95%EC%9D%98-%EC%99%84%EC%A0%84-%EC%A0%95%EB%B3%B5-ref-reactive-toRef-toRefs-%EC%96%B8%EC%A0%9C-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%93%B8%EA%B9%8C</link>
            <guid>https://velog.io/@seank-log/Vue-3-%EB%B3%80%EC%88%98-%EC%A0%95%EC%9D%98-%EC%99%84%EC%A0%84-%EC%A0%95%EB%B3%B5-ref-reactive-toRef-toRefs-%EC%96%B8%EC%A0%9C-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%93%B8%EA%B9%8C</guid>
            <pubDate>Wed, 14 May 2025 09:09:45 GMT</pubDate>
            <description><![CDATA[<p>Vue 3에서는 <code>ref</code>, <code>reactive</code>, <code>toRef</code>, <code>toRefs</code> 등 다양한 방식으로 변수를 선언한다.<br>이름도 비슷하고 용도도 겹쳐 보이지만, 구조와 목적을 정확히 이해하면 상황에 맞게 효율적으로 사용할 수 있다.<br>이 글에서는 각 방식의 차이점과 실전 사용법, 그리고 상황별 추천까지 한 번에 정리한다.</p>
<hr>
<h2 id="✅-변수-선언-방식-비교">✅ 변수 선언 방식 비교</h2>
<table>
<thead>
<tr>
<th>방식</th>
<th>대상</th>
<th><code>.value</code> 필요</th>
<th>구조분해 가능</th>
<th>사용 예시</th>
<th>추천 상황</th>
</tr>
</thead>
<tbody><tr>
<td>🧩 <code>ref</code></td>
<td>원시값, DOM 참조</td>
<td>✅ 필요</td>
<td>❌ 불가</td>
<td><code>const count = ref(0)</code></td>
<td>단일 숫자, 문자열, 불린 값 관리에 적합</td>
</tr>
<tr>
<td>🧱 <code>reactive</code></td>
<td>객체, 배열</td>
<td>❌ 불필요</td>
<td>❌ 직접 불가</td>
<td><code>const state = reactive({ a: 1 })</code></td>
<td>여러 속성을 가진 복합 객체 전체 상태 관리에 적합</td>
</tr>
<tr>
<td>🎯 <code>toRef</code></td>
<td><code>reactive</code> 객체의 속성</td>
<td>✅ 필요</td>
<td>✅ 가능</td>
<td><code>const title = toRef(state, &#39;title&#39;)</code></td>
<td>특정 속성만 독립적으로 <code>ref</code>처럼 다뤄야 할 때</td>
</tr>
<tr>
<td>🧷 <code>toRefs</code></td>
<td><code>reactive</code> 객체 전체</td>
<td>✅ 필요</td>
<td>✅ 가능</td>
<td><code>const { a, b } = toRefs(state)</code></td>
<td>구조분해하면서도 반응형 유지가 필요한 경우</td>
</tr>
</tbody></table>
<hr>
<h2 id="✅-사용법">✅ 사용법</h2>
<h4 id="ref--단일-상태값을-다룰-때-사용한다"><code>ref</code> – 단일 상태값을 다룰 때 사용한다.</h4>
<p><code>ref</code>는 숫자, 문자열, 불린과 같은 원시값을 반응형으로 만들 때 사용한다.<br><code>.value</code>를 통해 접근하며, 가장 기본적인 상태 저장 방법이다.</p>
<pre><code class="language-jsx">import { ref } from &#39;vue&#39;

const count = ref(0)
count.value++</code></pre>
<h4 id="reactive--객체-전체를-반응형으로-관리할-때-사용한다"><code>reactive</code> – 객체 전체를 반응형으로 관리할 때 사용한다.</h4>
<p>reactive는 객체나 배열 전체를 반응형으로 만들고 싶을 때 사용한다.
.value 없이 속성에 직접 접근할 수 있고, 구조가 있는 데이터를 다루기에 적합하다.
import { reactive } from &#39;vue&#39;</p>
<pre><code class="language-jsx">const user = reactive({
  id: null,
  name: &#39;&#39;,
  email: &#39;&#39;
})

user.name = &#39;Alice&#39;</code></pre>
<h4 id="toref--객체의-특정-속성만-분리해서-다뤄야-할-때-사용한다">toRef – 객체의 특정 속성만 분리해서 다뤄야 할 때 사용한다.</h4>
<p>toRef는 reactive 객체에서 특정 속성 하나만 ref처럼 따로 뽑아 사용할 수 있게 한다.
이 방식은 해당 속성을 개별로 넘겨야 하거나, watch나 props로 연결할 때 유용하다.</p>
<pre><code class="language-jsx">import { reactive, toRef } from &#39;vue&#39;

const user = reactive({ name: &#39;Vue&#39; })
const name = toRef(user, &#39;name&#39;)

name.value = &#39;React&#39;  // user.name 도 함께 바뀜</code></pre>
<h4 id="torefs--구조분해하면서도-반응형을-유지하고-싶을-때-사용한다">toRefs – 구조분해하면서도 반응형을 유지하고 싶을 때 사용한다.</h4>
<p>toRefs는 reactive 객체를 구조분해할 때 반응형 연결을 유지해준다.
템플릿에서 개별 속성처럼 사용할 수 있어 컴포넌트 가독성이 좋아진다.</p>
<pre><code class="language-jsx">import { reactive, toRefs } from &#39;vue&#39;

const user = reactive({ name: &#39;Vue&#39;, age: 3 })
const { name, age } = toRefs(user)

name.value = &#39;Nuxt&#39;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Vue 3 플러그인, 컴포저블, Provide/Inject, 커스텀 디렉티브]]></title>
            <link>https://velog.io/@seank-log/Vue-3-%EC%99%84%EC%A0%84-%EC%A0%95%EB%B3%B5-%ED%94%8C%EB%9F%AC%EA%B7%B8%EC%9D%B8-%EC%BB%B4%ED%8F%AC%EC%A0%80%EB%B8%94-ProvideInject-%EC%BB%A4%EC%8A%A4%ED%85%80-%EB%94%94%EB%A0%89%ED%8B%B0%EB%B8%8C</link>
            <guid>https://velog.io/@seank-log/Vue-3-%EC%99%84%EC%A0%84-%EC%A0%95%EB%B3%B5-%ED%94%8C%EB%9F%AC%EA%B7%B8%EC%9D%B8-%EC%BB%B4%ED%8F%AC%EC%A0%80%EB%B8%94-ProvideInject-%EC%BB%A4%EC%8A%A4%ED%85%80-%EB%94%94%EB%A0%89%ED%8B%B0%EB%B8%8C</guid>
            <pubDate>Sat, 22 Mar 2025 10:15:00 GMT</pubDate>
            <description><![CDATA[<p>🚀 Vue 3에서 자주 사용하는 핵심 기능들 — <strong>플러그인</strong>, <strong>컴포저블</strong>, <strong>provide/inject</strong>, <strong>커스텀 디렉티브</strong> — 각각의 역할과 차이점을 한 눈에 정리해봅니다.</p>
<table>
<thead>
<tr>
<th>기능</th>
<th>한 줄 요약 설명</th>
<th>언제 써야 할까?</th>
</tr>
</thead>
<tbody><tr>
<td>📦 <strong>플러그인</strong></td>
<td>앱 전체에서 공통 기능을 설정할 때 사용</td>
<td><code>$api</code>, <code>$log</code> 등 전역 기능이 필요할 때</td>
</tr>
<tr>
<td>🧩 <strong>컴포저블</strong></td>
<td>컴포넌트 내부 로직을 재사용할 때 적합</td>
<td>여러 컴포넌트에서 동일한 상태 관리나 로직이 반복될 때</td>
</tr>
<tr>
<td>🔗 <strong>provide / inject</strong></td>
<td>조상 컴포넌트에서 자식 컴포넌트로 데이터 전달</td>
<td>설정값이나 공통 데이터를 자식 컴포넌트에 내려줄 때</td>
</tr>
<tr>
<td>🎯 <strong>커스텀 디렉티브</strong></td>
<td>엘리먼트에 직접 동작을 붙일 때 사용</td>
<td>포커스 자동 부여, 외부 클릭 감지 등 DOM 조작이 필요할 때</td>
</tr>
</tbody></table>
<hr>
<h2 id="🧩-vue-3-주요-기능-비교표">🧩 Vue 3 주요 기능 비교표</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>📦 <strong>플러그인</strong></th>
<th>🧩 <strong>컴포저블</strong></th>
<th>🔗 <strong>provide / inject</strong></th>
<th>🎯 <strong>커스텀 디렉티브</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>주요 목적</strong></td>
<td>앱 전체 설정, 전역 기능 추가</td>
<td>재사용 가능한 로직 분리</td>
<td>조상 → 자식 간 데이터 전달</td>
<td>DOM 직접 제어</td>
</tr>
<tr>
<td><strong>사용 위치</strong></td>
<td><code>main.js</code> → <code>app.use()</code></td>
<td>컴포넌트 내부 <code>setup()</code></td>
<td><code>setup()</code> 내 <code>provide</code>, <code>inject</code></td>
<td>엘리먼트에 디렉티브로 사용 (<code>v-*</code>)</td>
</tr>
<tr>
<td><strong>전역 설정 가능</strong></td>
<td>✅ (<code>app.config</code>, <code>app.component</code> 등)</td>
<td>❌</td>
<td>❌</td>
<td>❌</td>
</tr>
<tr>
<td><strong>전역 속성 추가</strong></td>
<td>✅ (<code>$log</code>, <code>$api</code> 등)</td>
<td>❌</td>
<td>❌</td>
<td>❌</td>
</tr>
<tr>
<td><strong>재사용 가능</strong></td>
<td>✅ (라이브러리처럼 배포 가능)</td>
<td>✅ (로직 중심 재사용)</td>
<td>✅ (여러 컴포넌트에서 공유 가능)</td>
<td>✅ (디렉티브 재등록 가능)</td>
</tr>
<tr>
<td><strong>DOM 직접 접근</strong></td>
<td>❌</td>
<td>제한적 (<code>ref</code> 등으로 가능)</td>
<td>❌</td>
<td>✅ (<code>focus</code>, <code>scroll</code> 등)</td>
</tr>
<tr>
<td><strong>예시</strong></td>
<td><code>Vue Router</code>, <code>i18n</code>, <code>Toast</code></td>
<td><code>useAuth()</code>, <code>useDarkMode()</code></td>
<td>테마 설정, 글로벌 서비스 공유</td>
<td><code>v-focus</code>, <code>v-scroll</code> 등</td>
</tr>
<tr>
<td><strong>Vue 내장 기능 의존</strong></td>
<td>많이 사용 (<code>app.*</code>)</td>
<td>Composition API만 사용</td>
<td>Composition API만 사용</td>
<td>Directive API 사용</td>
</tr>
</tbody></table>
<hr>
<h2 id="🔍-각-기능-예시-코드">🔍 각 기능 예시 코드</h2>
<h3 id="📦-플러그인-예시">📦 플러그인 예시</h3>
<pre><code class="language-js">// myPlugin.js
export default {
  install(app, options) {
    app.config.globalProperties.$sayHello = () =&gt; console.log(&#39;Hello!&#39;);
    app.provide(&#39;pluginData&#39;, options?.message || &#39;Default Message&#39;);
  }
}

// main.js
import myPlugin from &#39;./myPlugin&#39;;
app.use(myPlugin, { message: &#39;Hello from plugin!&#39; });</code></pre>
<h3 id="🧩-컴포저블-예시">🧩 컴포저블 예시</h3>
<pre><code class="language-js">// useCounter.js
import { ref } from &#39;vue&#39;;

export function useCounter() {
  const count = ref(0);
  const increment = () =&gt; count.value++;
  return { count, increment };
}

// 컴포넌트 내부
const { count, increment } = useCounter();</code></pre>
<h3 id="🔗-provide--inject-예시">🔗 Provide / Inject 예시</h3>
<pre><code class="language-js">&lt;!-- ParentComponent.vue --&gt;
&lt;script setup&gt;
import { provide } from &#39;vue&#39;;
provide(&#39;message&#39;, &#39;Hello from parent!&#39;);
&lt;/script&gt;

&lt;!-- ChildComponent.vue --&gt;
&lt;script setup&gt;
import { inject } from &#39;vue&#39;;
const message = inject(&#39;message&#39;);
&lt;/script&gt;

&lt;template&gt;
  &lt;div&gt;{{ message }}&lt;/div&gt;
&lt;/template&gt;</code></pre>
<h3 id="🎯-커스텀-디렉티브-예시">🎯 커스텀 디렉티브 예시</h3>
<pre><code class="language-js">// directives/v-focus.js
export default {
  mounted(el) {
    el.focus();
  }
}

// main.js
import vFocus from &#39;./directives/v-focus&#39;;
app.directive(&#39;focus&#39;, vFocus);

// inputComponent.vue
&lt;!-- 사용 --&gt;
&lt;input v-focus /&gt;</code></pre>
<h2 id="🔌-실무에서-자주-쓰는-플러그인-활용-예">🔌 실무에서 자주 쓰는 플러그인 활용 예</h2>
<p>Vue 플러그인은 전역 기능을 설정하거나 앱 전체에서 반복되는 로직을 쉽게 공유할 수 있어요.<br>아래는 실무에서 <strong>provide/inject 방식</strong>으로 많이 사용하는 전형적인 예시입니다:</p>
<table>
<thead>
<tr>
<th>사용 예</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>✅ <strong>$api</strong></td>
<td>HTTP 요청 관련 함수들을 묶어서 전역에서 사용 (<code>getUser()</code>, <code>getPosts()</code> 등)</td>
</tr>
<tr>
<td>✅ <strong>$auth</strong></td>
<td>로그인 상태, 사용자 정보, 토큰 처리 등 인증 관련 로직 공유</td>
</tr>
<tr>
<td>✅ <strong>$logger</strong></td>
<td>개발 중 콘솔 로그 또는 에러 로깅 기능 전역 제공</td>
</tr>
<tr>
<td>✅ <strong>$toast</strong></td>
<td>알림/토스트 메시지 전역 호출 (<code>$toast.success(&#39;완료!&#39;)</code>)</td>
</tr>
</tbody></table>
<hr>
<h3 id="📦-api-플러그인">📦 <code>$api</code> 플러그인</h3>
<pre><code class="language-js">// plugins/api.js
export default {
  install(app, options) {
    const api = {
      getUser() {
        return fetch(`${options.baseUrl}/user`).then(res =&gt; res.json())
      },
      getPosts() {
        return fetch(`${options.baseUrl}/posts`).then(res =&gt; res.json())
      }
    }

    app.provide(&#39;$api&#39;, api)
  }
}</code></pre>
<hr>
<h3 id="🔐-auth-플러그인">🔐 <code>$auth</code> 플러그인</h3>
<pre><code class="language-js">// plugins/auth.js
export default {
  install(app) {
    const auth = {
      isLoggedIn: () =&gt; !!localStorage.getItem(&#39;token&#39;),
      getToken: () =&gt; localStorage.getItem(&#39;token&#39;),
      login(token) {
        localStorage.setItem(&#39;token&#39;, token)
      },
      logout() {
        localStorage.removeItem(&#39;token&#39;)
      }
    }

    app.provide(&#39;$auth&#39;, auth)
  }
}</code></pre>
<hr>
<h3 id="🧾-logger-플러그인">🧾 <code>$logger</code> 플러그인</h3>
<pre><code class="language-js">// plugins/logger.js
export default {
  install(app) {
    const logger = {
      log: (...args) =&gt; console.log(&#39;[LOG]&#39;, ...args),
      warn: (...args) =&gt; console.warn(&#39;[WARN]&#39;, ...args),
      error: (...args) =&gt; console.error(&#39;[ERROR]&#39;, ...args)
    }

    app.provide(&#39;$logger&#39;, logger)
  }
}</code></pre>
<hr>
<h3 id="🛎️-toast-플러그인-예-간단한-alert-기반">🛎️ <code>$toast</code> 플러그인 (예: 간단한 alert 기반)</h3>
<pre><code class="language-js">// plugins/toast.js
export default {
  install(app) {
    const toast = {
      success: (msg) =&gt; alert(`✅ Success: ${msg}`),
      error: (msg) =&gt; alert(`❌ Error: ${msg}`)
    }

    app.provide(&#39;$toast&#39;, toast)
  }
}</code></pre>
<hr>
<h3 id="🧩-mainjs에서-등록하기">🧩 main.js에서 등록하기</h3>
<pre><code class="language-js">import { createApp } from &#39;vue&#39;
import App from &#39;./App.vue&#39;

import apiPlugin from &#39;./plugins/api&#39;
import authPlugin from &#39;./plugins/auth&#39;
import loggerPlugin from &#39;./plugins/logger&#39;
import toastPlugin from &#39;./plugins/toast&#39;

const app = createApp(App)

app.use(apiPlugin, { baseUrl: &#39;https://jsonplaceholder.typicode.com&#39; })
app.use(authPlugin)
app.use(loggerPlugin)
app.use(toastPlugin)

app.mount(&#39;#app&#39;)</code></pre>
<hr>
<h3 id="✅-컴포넌트에서-사용-예시">✅ 컴포넌트에서 사용 예시</h3>
<pre><code class="language-js">&lt;script setup&gt;
import { inject } from &#39;vue&#39;

const $api = inject(&#39;$api&#39;)
const $auth = inject(&#39;$auth&#39;)
const $logger = inject(&#39;$logger&#39;)
const $toast = inject(&#39;$toast&#39;)

// 예시 사용
onMounted(async () =&gt; {
  if ($auth.isLoggedIn()) {
    const user = await $api.getUser()
    $logger.log(&#39;User loaded:&#39;, user)
    $toast.success(&#39;사용자 정보를 불러왔습니다!&#39;)
  } else {
    $toast.error(&#39;로그인이 필요합니다.&#39;)
  }
})
&lt;/script&gt;</code></pre>
<hr>
<blockquote>
<p>💡 <code>app.provide()</code>를 활용한 플러그인은 Composition API와 궁합이 좋고,<br><code>inject()</code>로 필요한 곳에서만 선택적으로 사용할 수 있어 유지보수가 편해요.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Javascript Proxy 가이드]]></title>
            <link>https://velog.io/@seank-log/Javascript-Proxy-%EA%B0%80%EC%9D%B4%EB%93%9C</link>
            <guid>https://velog.io/@seank-log/Javascript-Proxy-%EA%B0%80%EC%9D%B4%EB%93%9C</guid>
            <pubDate>Sat, 15 Feb 2025 10:22:32 GMT</pubDate>
            <description><![CDATA[<p>JavaScript의 <code>Proxy</code> 객체는 객체의 동작을 가로채어 원하는 방식으로 제어할 수 있도록 해주는 강력한 기능입니다. 이를 활용하면 데이터 검증, 로깅, 기본값 설정 등의 다양한 기능을 손쉽게 구현할 수 있습니다.</p>
<h2 id="1-proxy란">1. Proxy란?</h2>
<p><code>Proxy</code>는 객체를 감싸(<code>wrap</code>) 특정 동작을 가로채고 수정할 수 있도록 해주는 기능입니다.</p>
<pre><code class="language-javascript">const proxy = new Proxy(target, handler);</code></pre>
<ul>
<li><strong>target</strong>: 감싸고자 하는 원본 객체</li>
<li><strong>handler</strong>: 가로채고 싶은 동작을 정의하는 객체 (트랩(trap) 메서드 포함)</li>
</ul>
<p><code>handler</code> 객체에는 다양한 트랩(trap)이 존재하며, 이를 활용하여 객체의 동작을 제어할 수 있습니다.</p>
<h2 id="2-기본-예제-객체-속성-가로채기">2. 기본 예제: 객체 속성 가로채기</h2>
<pre><code class="language-javascript">const person = { name: &quot;Alice&quot;, age: 25 };

const handler = {
  get(target, property) {
    console.log(`&#39;${property}&#39; 속성을 읽으려 합니다.`);
    return target[property];
  },
};

const proxyPerson = new Proxy(person, handler);

console.log(proxyPerson.name);  // &#39;name&#39; 속성을 읽으려 합니다. → Alice
console.log(proxyPerson.age);   // &#39;age&#39; 속성을 읽으려 합니다. → 25</code></pre>
<h2 id="3-예제-값-검증-유효성-검사">3. 예제: 값 검증 (유효성 검사)</h2>
<pre><code class="language-javascript">const validator = {
  set(target, property, value) {
    if (property === &quot;age&quot; &amp;&amp; (typeof value !== &quot;number&quot; || value &lt; 0)) {
      throw new Error(&quot;나이는 0 이상의 숫자여야 합니다.&quot;);
    }
    target[property] = value;
    return true;
  }
};

const user = new Proxy({}, validator);
user.age = 30;  // 정상 동작
console.log(user.age); // 30

user.age = -5; // Error: 나이는 0 이상의 숫자여야 합니다.</code></pre>
<h2 id="4-예제-동적-속성-기본값-설정">4. 예제: 동적 속성 기본값 설정</h2>
<pre><code class="language-javascript">const defaultHandler = {
  get(target, property) {
    return property in target ? target[property] : &quot;값이 없습니다&quot;;
  }
};

const user = new Proxy({}, defaultHandler);
console.log(user.name); // &quot;값이 없습니다&quot;
user.name = &quot;Charlie&quot;;
console.log(user.name); // &quot;Charlie&quot;</code></pre>
<h2 id="5-예제-함수-호출-가로채기">5. 예제: 함수 호출 가로채기</h2>
<pre><code class="language-javascript">const logger = {
  apply(target, thisArg, args) {
    console.log(`함수 실행: ${target.name}(${args})`);
    return target.apply(thisArg, args);
  }
};

function sum(a, b) {
  return a + b;
}

const proxySum = new Proxy(sum, logger);
console.log(proxySum(5, 3));  // &quot;함수 실행: sum(5,3)&quot; → 8</code></pre>
<h2 id="6-proxy의-주요-트랩trap-목록">6. Proxy의 주요 트랩(trap) 목록</h2>
<table>
<thead>
<tr>
<th>트랩</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>get(target, property, receiver)</code></td>
<td>객체 프로퍼티 읽기 감지</td>
</tr>
<tr>
<td><code>set(target, property, value, receiver)</code></td>
<td>객체 프로퍼티 변경 감지</td>
</tr>
<tr>
<td><code>deleteProperty(target, property)</code></td>
<td>속성 삭제 감지</td>
</tr>
<tr>
<td><code>has(target, property)</code></td>
<td><code>in</code> 연산자 사용 감지 (<code>property in obj</code>)</td>
</tr>
<tr>
<td><code>apply(target, thisArg, args)</code></td>
<td>함수 호출 감지</td>
</tr>
<tr>
<td><code>construct(target, args, newTarget)</code></td>
<td><code>new</code> 키워드 사용 감지</td>
</tr>
</tbody></table>
<h2 id="결론">결론</h2>
<p><code>Proxy</code>는 객체의 동작을 가로채어 유효성 검사, 기본값 제공, 함수 호출 로깅 등 다양한 활용이 가능합니다. 이를 이용하면 객체를 더욱 강력하고 유연하게 제어할 수 있습니다. 🚀</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Vite: 차세대 프론트엔드 빌드 툴]]></title>
            <link>https://velog.io/@seank-log/Vite-%EC%B0%A8%EC%84%B8%EB%8C%80-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EB%B9%8C%EB%93%9C-%ED%88%B4</link>
            <guid>https://velog.io/@seank-log/Vite-%EC%B0%A8%EC%84%B8%EB%8C%80-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EB%B9%8C%EB%93%9C-%ED%88%B4</guid>
            <pubDate>Mon, 10 Feb 2025 14:47:12 GMT</pubDate>
            <description><![CDATA[<h2 id="1-vite란">1. Vite란?</h2>
<p>Vite는 빠른 개발 서버와 최적화된 빌드 시스템을 제공하는 <strong>차세대 프론트엔드 빌드 도구</strong>입니다. 기존의 Webpack과 같은 번들러의 단점을 개선하며, 특히 Vue, React 같은 최신 프레임워크와 잘 어울립니다.</p>
<p>Vite는 <code>esbuild</code>를 활용하여 개발 서버 속도를 극대화하고, <code>Rollup</code>을 이용한 최적화된 빌드를 제공합니다.</p>
<hr>
<h2 id="2-vite의-주요-기능">2. Vite의 주요 기능</h2>
<h3 id="✅-빠른-개발-서버">✅ 빠른 개발 서버</h3>
<p>Vite는 기존 번들링 방식이 아닌 <strong>ES 모듈(ESM) 기반의 즉시 제공(Serve on Demand) 방식</strong>을 사용하여 개발 서버를 빠르게 실행합니다.</p>
<h3 id="✅-hmrhot-module-replacement-성능-향상">✅ HMR(Hot Module Replacement) 성능 향상</h3>
<p>Vite는 모듈 단위로 변경 사항을 반영하여 HMR 속도를 극대화하며, 큰 프로젝트에서도 빠르게 적용됩니다.</p>
<h3 id="✅-빌드-최적화-rollup-기반-번들링">✅ 빌드 최적화 (Rollup 기반 번들링)</h3>
<p>Vite는 <code>Rollup</code>을 사용하여 프로덕션 빌드를 진행하며, 트리 쉐이킹(Tree Shaking)과 코드 스플리팅(Code Splitting) 같은 최적화를 자동으로 수행합니다.</p>
<h3 id="✅-플러그인-시스템-지원">✅ 플러그인 시스템 지원</h3>
<p>Vite는 플러그인 API를 제공하며, 기존의 Rollup 플러그인을 그대로 사용할 수 있습니다.</p>
<h3 id="✅-다양한-프레임워크-지원">✅ 다양한 프레임워크 지원</h3>
<p>Vue, React, Svelte 등 다양한 프레임워크를 공식적으로 지원하며, Vue의 경우 <code>vue-plugin</code>을 제공하여 편리한 개발 환경을 제공합니다.</p>
<hr>
<h2 id="3-vite와-webpack-비교">3. Vite와 Webpack 비교</h2>
<table>
<thead>
<tr>
<th>비교 항목</th>
<th>Vite</th>
<th>Webpack</th>
</tr>
</thead>
<tbody><tr>
<td>번들링 방식</td>
<td>ESM 기반</td>
<td>번들링 후 제공</td>
</tr>
<tr>
<td>개발 서버 속도</td>
<td>매우 빠름</td>
<td>느림 (전체 번들 필요)</td>
</tr>
<tr>
<td>HMR 성능</td>
<td>빠름 (모듈 단위 갱신)</td>
<td>느림 (전체 리로딩 발생 가능)</td>
</tr>
<tr>
<td>빌드 최적화</td>
<td>Rollup 사용, 자동 최적화</td>
<td>추가 설정 필요</td>
</tr>
<tr>
<td>플러그인 지원</td>
<td>Rollup 플러그인 사용 가능</td>
<td>Webpack 플러그인 사용</td>
</tr>
<tr>
<td>사용 편의성</td>
<td>설정이 간단</td>
<td>설정이 복잡</td>
</tr>
</tbody></table>
<hr>
<h2 id="4-vite의-동작-방식">4. Vite의 동작 방식</h2>
<h3 id="🚀-기존-번들러webpack의-문제점">🚀 기존 번들러(Webpack)의 문제점</h3>
<p>Webpack과 같은 기존 번들러는 <strong>모든 파일을 번들링한 후 브라우저에 제공</strong>하는 방식으로 동작합니다. 프로젝트가 커질수록 초기 빌드 시간이 증가하며, 개발 서버 속도가 느려지는 단점이 있습니다.</p>
<h3 id="⚡-vite의-즉시-제공-방식-esm-기반">⚡ Vite의 즉시 제공 방식 (ESM 기반)</h3>
<p>Vite는 개발 서버 실행 시 <strong>파일을 미리 번들링하지 않고, 요청이 들어오면 즉시 변환하여 제공</strong>하는 방식을 사용합니다.</p>
<ol>
<li><strong>JS 모듈을 직접 브라우저에서 로드</strong><ul>
<li><code>import</code> 문을 통해 개별 파일을 불러오고, 필요할 때만 변환하여 제공</li>
</ul>
</li>
<li><strong>변경된 모듈만 갱신</strong><ul>
<li>전체 애플리케이션을 다시 빌드하지 않고, 수정된 부분만 즉시 반영</li>
</ul>
</li>
</ol>
<p>이 방식 덕분에 <strong>서버 시작이 빠르고, 코드 변경 시 즉각적인 반응이 가능</strong>합니다.</p>
<hr>
<h2 id="5-rollup이란">5. Rollup이란?</h2>
<p>Rollup은 JavaScript 모듈을 번들링하는 도구로, Vite의 프로덕션 빌드에서 사용됩니다. 트리 쉐이킹(Tree Shaking)과 코드 스플리팅(Code Splitting) 등의 기능을 제공하여 최적화된 번들 파일을 생성합니다.</p>
<p>Rollup은 단독으로 사용할 수도 있으며, Vite의 내부 빌드 시스템으로 동작합니다.</p>
<h2 id="6-vite가-필요한-이유">6. Vite가 필요한 이유</h2>
<h3 id="1️⃣-빠른-개발-환경-구축">1️⃣ 빠른 개발 환경 구축</h3>
<p>Vite는 개발 서버 실행 속도가 빠르고, 코드 변경 시 실시간 반영(HMR)이 원활하여 개발 경험을 향상시킵니다.</p>
<h3 id="2️⃣-대규모-프로젝트에서도-뛰어난-성능">2️⃣ 대규모 프로젝트에서도 뛰어난 성능</h3>
<p>Webpack 기반 프로젝트에서는 코드가 많아질수록 빌드 시간이 길어지지만, Vite는 ES 모듈을 활용한 방식으로 이러한 성능 저하를 방지합니다.</p>
<h3 id="3️⃣-최적화된-프로덕션-빌드">3️⃣ 최적화된 프로덕션 빌드</h3>
<p>Rollup을 기반으로 빌드를 수행하며, 코드 스플리팅 및 트리 쉐이킹 등의 최적화 기능이 기본적으로 적용됩니다.</p>
<h3 id="4️⃣-vue-3와의-강력한-호환성">4️⃣ Vue 3와의 강력한 호환성</h3>
<p>Vite는 Vue 3의 공식 개발 환경으로 권장되며, <code>@vitejs/plugin-vue</code> 플러그인을 사용하면 SFC(Single File Component)도 원활하게 처리할 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JavaScript 모듈 시스템: 동기적 로딩과 비동기적 로딩의 차이]]></title>
            <link>https://velog.io/@seank-log/JavaScript-%EB%AA%A8%EB%93%88-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EB%8F%99%EA%B8%B0%EC%A0%81-%EB%A1%9C%EB%94%A9%EA%B3%BC-%EB%B9%84%EB%8F%99%EA%B8%B0%EC%A0%81-%EB%A1%9C%EB%94%A9%EC%9D%98-%EC%B0%A8%EC%9D%B4</link>
            <guid>https://velog.io/@seank-log/JavaScript-%EB%AA%A8%EB%93%88-%EC%8B%9C%EC%8A%A4%ED%85%9C-%EB%8F%99%EA%B8%B0%EC%A0%81-%EB%A1%9C%EB%94%A9%EA%B3%BC-%EB%B9%84%EB%8F%99%EA%B8%B0%EC%A0%81-%EB%A1%9C%EB%94%A9%EC%9D%98-%EC%B0%A8%EC%9D%B4</guid>
            <pubDate>Mon, 10 Feb 2025 14:25:19 GMT</pubDate>
            <description><![CDATA[<p>JavaScript의 모듈 시스템은 크게 <strong>CommonJS(require)</strong>와 <strong>ESM(import)</strong> 방식으로 나뉩니다. 이 두 방식의 핵심적인 차이점 중 하나는 <strong>모듈 로딩 방식</strong>이며, <code>require()</code>는 동기적(Synchronous)이고, <code>import</code>는 비동기적(Asynchronous)으로 동작합니다.</p>
<p>이번 글에서는 두 방식의 실행 모델을 심층적으로 분석하여, <strong>왜 require()는 동기적이고 import는 비동기적인지</strong>를 설명하겠습니다.</p>
<hr>
<h2 id="📌-commonjsrequire와-동기적-로딩">📌 CommonJS(require)와 동기적 로딩</h2>
<p><strong>CommonJS의 <code>require()</code>는 호출 시점에서 해당 모듈을 즉시 로드하고 평가를 완료할 때까지 실행을 블로킹합니다.</strong> 따라서 코드 실행 흐름을 멈추고 모듈의 모든 내용을 읽고 실행한 후에야 다음 코드가 실행됩니다.</p>
<h3 id="✅-require-실행-모델-예제">✅ require() 실행 모델 예제</h3>
<pre><code class="language-js">console.log(&quot;Before require&quot;);

const message = require(&quot;./module.js&quot;); // &lt;- 파일을 읽고 실행이 끝날 때까지 대기 (Blocking)

console.log(&quot;After require&quot;);</code></pre>
<h3 id="📌-실행-흐름">📌 실행 흐름</h3>
<pre><code>Before require
(module.js 실행 내용)
After require</code></pre><p>✅ <code>&quot;Before require&quot;</code>가 출력된 후, <code>require(&quot;./module.js&quot;)</code>가 실행됩니다.
✅ <code>module.js</code>의 로딩과 실행이 <strong>완료될 때까지 블로킹</strong>되며, 그 이후 <code>&quot;After require&quot;</code>가 출력됩니다.</p>
<p>📌 <strong>require()는 동기적 실행 모델을 따르며, 모듈 로드가 완료될 때까지 실행 흐름을 멈춘다.</strong></p>
<hr>
<h2 id="📌-ecmascript-modulesesm과-비동기적-로딩">📌 ECMAScript Modules(ESM)과 비동기적 로딩</h2>
<p><strong>ESM(ECMAScript Modules)의 <code>import</code>는 비동기적으로 실행되며, 모듈을 병렬로 가져옵니다.</strong> 즉, <code>import</code>는 블로킹 없이 모듈을 로드하고, 이후 실행 흐름을 유지하면서 로드 완료 후 평가됩니다.</p>
<h3 id="✅-import-실행-모델-예제">✅ import 실행 모델 예제</h3>
<pre><code class="language-js">console.log(&quot;Before import&quot;);

import(&quot;./module.js&quot;).then(() =&gt; {
  console.log(&quot;Module loaded&quot;);
});

console.log(&quot;After import&quot;);</code></pre>
<h3 id="📌-실행-흐름-1">📌 실행 흐름</h3>
<pre><code>Before import
After import
(module.js 실행 내용)
Module loaded</code></pre><p>✅ <code>&quot;Before import&quot;</code>가 출력됨
✅ <code>import(&quot;./module.js&quot;)</code>는 <strong>비동기적으로 실행되므로 즉시 다음 코드(&quot;After import&quot;)가 실행됨</strong>
✅ <code>module.js</code>가 로드된 후 <code>&quot;Module loaded&quot;</code>가 출력됨</p>
<p>📌 <strong>import는 실행 흐름을 블로킹하지 않고, 모듈을 병렬로 가져오면서 필요할 때 평가된다.</strong></p>
<hr>
<h2 id="🎯-require-vs-import-비교-분석">🎯 require() vs import 비교 분석</h2>
<table>
<thead>
<tr>
<th>비교 항목</th>
<th>CommonJS (<code>require()</code>)</th>
<th>ESM (<code>import</code>)</th>
</tr>
</thead>
<tbody><tr>
<td>실행 방식</td>
<td>동기적 (Blocking)</td>
<td>비동기적 (Non-blocking)</td>
</tr>
<tr>
<td>모듈 로딩 시점</td>
<td>즉시 로드</td>
<td>필요할 때 로드 가능 (지연 로딩)</td>
</tr>
<tr>
<td>브라우저 지원</td>
<td>❌ (Node.js 전용)</td>
<td>✅ (브라우저 네이티브 지원)</td>
</tr>
<tr>
<td>Vite에서 사용</td>
<td>❌ 느리므로 비효율적</td>
<td>✅ 빠른 로딩 가능</td>
</tr>
</tbody></table>
<p>✅ <strong>Vite가 ESM을 활용하는 이유</strong>
Vite는 <code>require()</code>처럼 모든 파일을 한 번에 로드하는 방식이 아니라, <strong>브라우저의 ESM 기능을 활용하여 필요한 모듈만 비동기적으로 가져오기 때문에 빠른 개발 환경을 제공한다.</strong> 🚀</p>
<hr>
<h2 id="💡-결론">💡 결론</h2>
<ul>
<li><code>require()</code>는 <strong>동기적(Synchronous) 실행 모델</strong>을 따르며, 모듈이 로드될 때까지 <strong>실행 흐름을 멈춘다</strong>.</li>
<li><code>import</code>는 <strong>비동기적(Asynchronous) 실행 모델</strong>을 따르며, <strong>모듈이 로드되는 동안 다른 코드가 실행될 수 있다</strong>.</li>
<li>Vite와 같은 현대적인 빌드 도구는 <strong>ESM을 활용하여 개발 속도를 최적화</strong>한다.</li>
</ul>
<p>고급 개발자로서 효율적인 모듈 로딩을 고려할 때, <strong>어떤 환경에서 어떤 모듈 시스템을 사용할지 깊이 이해하는 것이 필수적</strong>입니다. 😊</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Frontend 로컬 환경에서 외부 서버의 API 호출하기: Webpack DevServer 설정 방법]]></title>
            <link>https://velog.io/@seank-log/Frontend-%EB%A1%9C%EC%BB%AC-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-%EC%99%B8%EB%B6%80-%EC%84%9C%EB%B2%84%EC%9D%98-API-%ED%98%B8%EC%B6%9C%ED%95%98%EA%B8%B0-Webpack-DevServer-%EC%84%A4%EC%A0%95-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@seank-log/Frontend-%EB%A1%9C%EC%BB%AC-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-%EC%99%B8%EB%B6%80-%EC%84%9C%EB%B2%84%EC%9D%98-API-%ED%98%B8%EC%B6%9C%ED%95%98%EA%B8%B0-Webpack-DevServer-%EC%84%A4%EC%A0%95-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Fri, 07 Feb 2025 09:08:46 GMT</pubDate>
            <description><![CDATA[<h1 id="webpack-devserver에서-302-error-해결-allowedhosts-옵션의-역할">Webpack DevServer에서 302 Error 해결: <code>allowedHosts</code> 옵션의 역할</h1>
<h2 id="문제-상황">문제 상황</h2>
<p>Webpack 4.0의 <code>devServer</code>를 사용하여 로컬 개발 환경에서 프록시를 설정하고 외부 API(예: <code>https://alpha.com</code>)를 호출하려 할 때, <strong>302 Redirect Error</strong>가 발생하는 문제가 있었습니다.</p>
<h3 id="webpack-devserver-설정">Webpack DevServer 설정</h3>
<pre><code class="language-js">configs.devServer = {
    host: &#39;0.0.0.0&#39;,
    port: 3000,
    hot: true,
    historyApiFallback: true,
    allowedHosts: [&#39;all&#39;],
    proxy: {
      &#39;/api&#39;: {
        target: &#39;https://alpha.com&#39;,
        changeOrigin: true,
        secure: false,
        headers: {
          Cookie: &#39;cid=123&#39;
        }
      }
    }
}</code></pre>
<p>이 설정을 적용하기 전에는 API 요청 시 <strong>302 에러</strong>가 발생하여 정상적으로 데이터를 가져올 수 없었습니다. 하지만 <code>allowedHosts: [&#39;all&#39;]</code> 옵션을 추가한 후 문제없이 API 호출이 이루어졌습니다.</p>
<h2 id="allowedhosts-옵션이-해결책이-된-이유"><code>allowedHosts</code> 옵션이 해결책이 된 이유</h2>
<h3 id="webpack-devserver의-allowedhosts란">Webpack DevServer의 <code>allowedHosts</code>란?</h3>
<p><code>allowedHosts</code>는 Webpack DevServer의 <strong>보안 정책(CORS, Same-Origin Policy)</strong>과 관련된 설정입니다.</p>
<p>기본적으로 Webpack DevServer는 <strong>Host Header 검사를 수행</strong>하여, 예상하지 못한 도메인에서 접근하는 것을 방지합니다. 예를 들어, <code>localhost:3000</code>에서 실행 중인데 <code>alpha.com</code>의 API를 호출하면 Webpack DevServer가 이를 차단할 수 있습니다.</p>
<h3 id="allowedhosts의-동작-방식"><code>allowedHosts</code>의 동작 방식</h3>
<ul>
<li><p>기본값: <code>undefined</code></p>
<ul>
<li>Webpack DevServer는 <code>localhost</code> 또는 <code>127.0.0.1</code> 같은 기본적인 호스트에서만 요청을 허용합니다.</li>
</ul>
</li>
<li><p>특정 도메인을 허용하는 경우:</p>
<pre><code class="language-js">allowedHosts: [&#39;alpha.com&#39;, &#39;beta.com&#39;]</code></pre>
<ul>
<li><code>alpha.com</code>, <code>beta.com</code>에서의 요청만 허용합니다.</li>
</ul>
</li>
<li><p>모든 호스트를 허용하는 경우:</p>
<pre><code class="language-js">allowedHosts: [&#39;all&#39;]</code></pre>
<ul>
<li>어떠한 도메인에서 접근하더라도 요청을 허용합니다.</li>
</ul>
</li>
</ul>
<h2 id="302-redirect-오류의-원인">302 Redirect 오류의 원인</h2>
<p>302 오류는 <strong>리디렉션 발생</strong>을 의미하며, 여러 원인이 있을 수 있지만 이번 경우에는 Webpack DevServer가 <code>Host Header</code>를 검증하는 과정에서 문제가 발생한 것으로 보입니다.</p>
<ul>
<li><code>allowedHosts</code>가 설정되지 않으면 Webpack DevServer가 Host Header를 검사하고, 프록시 요청을 정상적으로 처리하지 못할 수 있습니다.</li>
<li>이때, 브라우저가 <strong>302 Redirect</strong>를 수행하여 다른 경로로 리디렉션하거나 요청을 차단할 수 있습니다.</li>
</ul>
<p><code>allowedHosts: [&#39;all&#39;]</code>을 설정하면 Webpack DevServer가 모든 도메인을 신뢰하도록 설정되므로, 프록시 요청이 정상적으로 이루어지고 API 호출이 성공하게 됩니다.</p>
<h2 id="쿠키-설정-방법">쿠키 설정 방법</h2>
<p>Alpha 서버에 정상적으로 로그인한 후, 개발자 도구(F12)를 열고 <strong>네트워크 탭</strong>에서 요청에 포함된 <code>Cookie</code> 값을 확인합니다. 해당 쿠키 값을 복사한 후, Webpack DevServer의 프록시 설정에서 <code>headers</code> 옵션에 추가하면 됩니다.</p>
<p>예를 들어, <code>cid=12345</code>라는 쿠키 값이 있다면 다음과 같이 설정할 수 있습니다:</p>
<pre><code class="language-js">headers: {
  Cookie: &#39;cid=12345&#39;
}</code></pre>
<p>이 방법을 사용하면 Webpack DevServer의 프록시를 통해 Alpha 서버에 인증된 상태로 API를 요청할 수 있습니다.</p>
<h2 id="결론">결론</h2>
<p>Webpack DevServer에서 외부 API 호출 시 302 Redirect Error가 발생한다면 <code>allowedHosts: [&#39;all&#39;]</code> 설정을 추가하여 문제를 해결할 수 있습니다. 하지만, <strong>보안상 위험할 수 있으므로 로컬환경에서 개발 및 테스트를 하는 경우에만 사용하는 것이 좋습니다.</strong></p>
]]></description>
        </item>
    </channel>
</rss>