<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>개발자 기록.zip</title>
        <link>https://velog.io/</link>
        <description>풀스택 개발자 기록집 📁</description>
        <lastBuildDate>Wed, 19 Feb 2025 01:43:38 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>개발자 기록.zip</title>
            <url>https://velog.velcdn.com/images/suhyun_zip/profile/9a20c5f6-9991-4b54-b2db-26989b04eac9/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. 개발자 기록.zip. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/suhyun_zip" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Svelte 4 vs Svelte 5]]></title>
            <link>https://velog.io/@suhyun_zip/Svelte4-vs-Svelte5</link>
            <guid>https://velog.io/@suhyun_zip/Svelte4-vs-Svelte5</guid>
            <pubDate>Wed, 19 Feb 2025 01:43:38 GMT</pubDate>
            <description><![CDATA[<h1 id="💡-들어가며">💡 들어가며</h1>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/440995a0-d269-48d0-ba51-1469e711b976/image.png" alt=""></p>
<p>2024년 10월 20일 Svelte 5가 출시되었다. 기존 회사에서 Svelte 4를 사용하여 프로젝트를 진행 중이었는데 이번에 Svelte 5로 마이그레이션을 하게 되었다. 이에 따라 공부를 하면서 한글로 정리된 정보는 많이 없는 것 같아 벨로그에 <code>Svelte5가 되며 달라진 점</code>을 정리해 보려고 한다.</p>
<hr>
<h1 id="🔥-svelte5는-뭐가-다를까">🔥 Svelte5는 뭐가 다를까?</h1>
<blockquote>
<p><strong>&quot;Svelte 5의 핵심 변화&quot;</strong>
Svelte 5에서는 보다 직관적인 API와 성능 최적화를 목표로 큰 변화를 가져왔다.</p>
</blockquote>
<ul>
<li>반응성 모델 변경: $state(), $derived(), $effect() 도입</li>
<li>Props &amp; 이벤트 개선: $props(), $bindable() 지원</li>
<li>컴포넌트 구조 최적화: slot 대신 children props 사용</li>
<li>렌더링 최적화: Fine-Grained Reactivity 도입</li>
</ul>
<p>이제 하나씩 상세히 알아보도록 하자.</p>
<hr>
<h2 id="🌿-state">🌿 State</h2>
<h3 id="state">$state()</h3>
<blockquote>
<p>반응형 변수일 경우 <code>$state()</code>를 사용하여 반응형 상태임을 명시한다.</p>
</blockquote>
<h4 id="🏃♀️➡️-svelte4">🏃‍♀️‍➡️ Svelte4</h4>
<pre><code class="language-js">&lt;script&gt;
    let count = 0;
&lt;/script&gt;</code></pre>
<h4 id="🏃♂️➡️-svelte5">🏃‍♂️‍➡️ Svelte5</h4>
<pre><code class="language-js">&lt;script&gt;
    let count = $state(0);
&lt;/script&gt;</code></pre>
<h3 id="svelte-4-vs-svelte-5">Svelte 4 vs Svelte 5</h3>
<table>
<thead>
<tr>
<th>비교 항목</th>
<th>Svelte 4 (기존 방식)</th>
<th>Svelte 5 (새로운 방식)</th>
</tr>
</thead>
<tbody><tr>
<td><strong>반응성 선언 방식</strong></td>
<td><code>let count = 0;</code> (자동 감지)</td>
<td><code>let count = $state(0);</code></td>
</tr>
<tr>
<td><strong>변수의 명확성</strong></td>
<td>반응형인지 불분명함</td>
<td><code>$state()</code>를 사용하여 명확히 반응형 변수임을 표시</td>
</tr>
<tr>
<td><strong>렌더링 최적화</strong></td>
<td>전체 블록이 다시 실행될 가능성 있음</td>
<td>변경된 부분만 업데이트</td>
</tr>
</tbody></table>
<p>즉, Svelte 5에서는 <code>$state()</code>를 통해 더 직관적인 상태 관리를 제공하면서, 렌더링 성능도 최적화되었다.</p>
<hr>
<h2 id="🌿-computed-state">🌿 Computed State</h2>
<h3 id="derived">$derived()</h3>
<blockquote>
<p>반응형 계산일 경우 <code>$derived()</code>를 사용하여 명확히 선언한다.
복잡한 연산(여러 개의 반응형 상태를 조합하여 새로운 값을 만들 때)는 <code>$derived.by()</code>를 사용한다.</p>
</blockquote>
<h4 id="🏃♀️➡️-svelte4-1">🏃‍♀️‍➡️ Svelte4</h4>
<pre><code class="language-js">&lt;script&gt;
    let count = 10;
    $: doubleCount = count * 2;
&lt;/script&gt;

&lt;div&gt;{doubleCount}&lt;/div&gt;</code></pre>
<h4 id="🏃♂️➡️-svelte5-1">🏃‍♂️‍➡️ Svelte5</h4>
<pre><code class="language-js">&lt;script&gt;
    let count = $state(10);
    const doubleCount = $derived(count * 2);
    let total = $derived.by(() =&gt; {
           // code...
    });
&lt;/script&gt;

&lt;div&gt;{doubleCount}&lt;/div&gt;</code></pre>
<h3 id="svelte-4-vs-svelte-5-1">Svelte 4 vs Svelte 5</h3>
<table>
<thead>
<tr>
<th>비교 항목</th>
<th>Svelte 4 (기존 방식)</th>
<th>Svelte 5 (새로운 방식)</th>
</tr>
</thead>
<tbody><tr>
<td><strong>반응형 계산 방식</strong></td>
<td><code>$:</code>를 사용한 반응형 선언</td>
<td><code>$derived()</code>를 사용하여 명확히 선언</td>
</tr>
<tr>
<td><strong>실행 방식</strong></td>
<td>모든 반응형 값 변경 시 실행됨</td>
<td>필요한 값이 변경될 때만 실행됨</td>
</tr>
<tr>
<td><strong>렌더링 성능</strong></td>
<td>전체 블록이 다시 실행될 가능성 있음</td>
<td>변경된 부분만 업데이트</td>
</tr>
</tbody></table>
<p>Svelte 5에서는 <code>$derived()</code>를 통해 불필요한 재계산을 줄이고, 더 최적화된 반응형 상태 관리를 제공하게 되었다.</p>
<hr>
<h2 id="🌿-life-cycle-methods">🌿 Life Cycle methods</h2>
<h3 id="effect">$effect()</h3>
<blockquote>
<p><code>$effect()</code>를 활용해 라이프사이클과 반응성을 더욱 직관적으로 관리할 수 있다.</p>
</blockquote>
<h4 id="🏃♀️➡️-svelte4-2">🏃‍♀️‍➡️ Svelte4</h4>
<pre><code class="language-js">&lt;script lang=&quot;ts&quot;&gt;
    import { onMount } from &quot;svelte&quot;

    onMount(() =&gt; {
        // code...
    });
&lt;/script&gt;</code></pre>
<h4 id="🏃♂️➡️-svelte5-2">🏃‍♂️‍➡️ Svelte5</h4>
<pre><code class="language-js">&lt;script lang=&quot;ts&quot;&gt;
    $effect(() =&gt; {
        // code...
    });
&lt;/script&gt;</code></pre>
<p><code>Effect Rune</code>은 Svelte5에서 반응성을 처리하는 새로운 방식 중 하나다.
$effect는 참조된 반응형 변수(State)가 변경될 때마다 실행된다. (<code>Untrack</code>을 사용하지 않는 이상 😥) 이러한 특성으로 인해 API 호출, DOM 조작 등 사이드 이펙트에만 사용해야 하고, 반응형 변수를 기반으로 새로운 값을 계산하는 용도로 쓰면 안 된다.</p>
<h4 id="🏃♀️➡️🏃♂️➡️-effect를-사용해-반응형-값을-계산하는-예시">🏃‍♀️‍➡️🏃‍♂️‍➡️ $effect를 사용해 반응형 값을 계산하는 예시</h4>
<pre><code class="language-js">&lt;script lang=&#39;ts&#39;&gt;
    let total = 100;
    let spent = $state(0);
    let left = $state(total);

    $effect(() =&gt; {
        left = total - spent;
    });

    $effect(() =&gt; {
        spent = total - left;
    });
&lt;/script&gt;</code></pre>
<p>이처럼 작성할 경우 두 $effect가 서로 값을 갱신하면서 무한 루프가 발생할 위험이 있다. Svelte5에서는 이를 방지하기 위해 $derived를 사용하거나 <strong>직접 함수로 변경</strong>하는 것이 더 적절하다고 말한다.
위 코드를 함수로 변경해 보자.</p>
<h4 id="🏃♀️➡️🏃♂️➡️-함수-기반으로-값을-계산하는-예시">🏃‍♀️‍➡️🏃‍♂️‍➡️ 함수 기반으로 값을 계산하는 예시</h4>
<pre><code class="language-js">&lt;script lang=&#39;ts&#39;&gt;
    let total = 100;
    let spent = $state(0);
    let left = $state(total);

    function updateSpent(e) {
        spent = +e.target.value;
        left = total - spent;
    }

    function updateLeft(e) {
        left = +e.target.value;
        spent = total - left;
    }
&lt;/script&gt;</code></pre>
<p>변수 spent과 left를 updateSpent()와 updateLeft() 함수에서 직접 갱신하도록 수정해 보았다. 불필요한 반응형 트리거를 없애고 더 직관적인 코드가 되었다.</p>
<blockquote>
<p><code>$effect.pre()</code>를 사용하면 상태 변경 전에 특정 작업을 수행할 수 있다.</p>
</blockquote>
<pre><code class="language-js">&lt;script lang=&quot;ts&quot;&gt;
    $effect.pre(() =&gt; {
        // code...
    });
&lt;/script&gt;</code></pre>
<h3 id="effectpre">$effect.pre()</h3>
<p><code>$effect.pre()</code>는 Pre-Effect로, 일반적인 $effect보다 앞서 실행되는 반응형 효과를 정의하는 기능이다. 기존 값을 보존해야 하거나 선제적인 처리가 필요한 경우 유용하게 쓸 수 있다.</p>
<h3 id="effect와-effectpre의-차이점">$effect와 $effect.pre()의 차이점</h3>
<table>
<thead>
<tr>
<th align="center">기능</th>
<th>$effect</th>
<th>$effect.pre()</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><strong>실행 시점</strong></td>
<td>상태가 변경된 후 실행됨</td>
<td>상태가 변경되기 직전에 실행됨</td>
</tr>
<tr>
<td align="center"><strong>용도</strong></td>
<td>상태 변경 후 후속 작업 (예: API 호출, 로깅)</td>
<td>상태 변경 전에 선제적으로 실행해야 하는 작업 (예: 기존 값 백업)</td>
</tr>
</tbody></table>
<h4 id="🏃♀️➡️🏃♂️➡️-일반적인-effect">🏃‍♀️‍➡️🏃‍♂️‍➡️ 일반적인 $effect</h4>
<pre><code class="language-js">&lt;script lang=&quot;ts&quot;&gt;
    let count = $state(0);

    $effect(() =&gt; {
        console.log(`Count changed to: ${count}`);
    });

    function increment() {
        count++;
    }
&lt;/script&gt;</code></pre>
<p>이 경우 count가 변경된 이후에 $effect가 실행된다.</p>
<h4 id="🏃♀️➡️🏃♂️➡️-effectpre를-활용한-예시">🏃‍♀️‍➡️🏃‍♂️‍➡️ $effect.pre()를 활용한 예시</h4>
<pre><code class="language-js">&lt;script lang=&quot;ts&quot;&gt;
    let count = $state(0);
    let previousCount = $state(0);

    $effect.pre(() =&gt; {
        previousCount = count;  // count 변경 전에 기존 값 저장
    });

    $effect(() =&gt; {
        console.log(`Count changed from ${previousCount} to ${count}`);
    });

    function increment() {
        count++;
    }
&lt;/script&gt;</code></pre>
<p>이번에는 count가 변경되기 전에 previousCount를 저장하도록 $effect.pre()를 사용해 보았다. 변경 후 실행되는 $effect에서 이전 값과 새로운 값을 비교 가능하다.</p>
<hr>
<h2 id="🌿-props">🌿 Props</h2>
<h3 id="props">$props()</h3>
<blockquote>
<p><code>$props()</code>를 사용해 기존 export let으로 가져오던 props를 한 번에 쉽게 가져올 수 있다.</p>
</blockquote>
<h4 id="🏃♀️➡️-svelte4-3">🏃‍♀️‍➡️ Svelte4</h4>
<pre><code class="language-js">&lt;script&gt;
    export let name = &quot;&quot;;
    export let age = null;
    export let favouriteColors = [];
    export let isAvailable = false;
&lt;/script&gt;

&lt;p&gt;My name is {name}!&lt;/p&gt;
&lt;p&gt;My age is {age}!&lt;/p&gt;
&lt;p&gt;My favourite colors are {favouriteColors.join(&quot;, &quot;)}!&lt;/p&gt;
&lt;p&gt;I am {isAvailable ? &quot;available&quot; : &quot;not available&quot;}&lt;/p&gt;</code></pre>
<h4 id="🏃♂️➡️-svelte5-3">🏃‍♂️‍➡️ Svelte5</h4>
<pre><code class="language-js">&lt;script lang=&quot;ts&quot;&gt;
    const {
        name = &#39;&#39;,
        age = null,
        favouriteColors = [],
        isAvailable = false,
    } = $props();
&lt;/script&gt;

&lt;p&gt;My name is {name}!&lt;/p&gt;
&lt;p&gt;My age is {age}!&lt;/p&gt;
&lt;p&gt;My favourite colors are {favouriteColors.join(&quot;, &quot;)}!&lt;/p&gt;
&lt;p&gt;I am {isAvailable ? &quot;available&quot; : &quot;not available&quot;}&lt;/p&gt;</code></pre>
<p>기존 export let으로 하나하나 가져오던 props를 $props()를 사용하여 한 번에 가져올 수 있다.</p>
<h3 id="bindable">$bindable()</h3>
<blockquote>
<p><code>$bindable()</code>을 사용하여 데이터 양방향 바인딩을 할 수 있다.</p>
</blockquote>
<h4 id="🏃♀️➡️-svelte4-4">🏃‍♀️‍➡️ Svelte4</h4>
<pre><code class="language-js">&lt;!-- Parent.svelte --&gt;
&lt;UserProfile bind:optionalProp={someValue} /&gt;

&lt;!-- UserProfile.svelte --&gt;
&lt;script&gt;
    export let optionalProp;
&lt;/script&gt;
&lt;input bind:value={optionalProp} /&gt;</code></pre>
<p>기존에는 양방향 바인딩을 하기 위해서는 부모 컴포넌트에서 bind:를 붙이고 자식 컴포넌트에서 export let을 붙여야만 했다.</p>
<h4 id="🏃♂️➡️-svelte5-4">🏃‍♂️‍➡️ Svelte5</h4>
<pre><code class="language-js">&lt;script&gt;
    const { optionalProp = $bindable() } = $props();
&lt;/script&gt;</code></pre>
<p><code>$bindable()</code>을 사용하면 bind: 없이도 양방향 바인딩이 가능하다. optionalProp은 부모로부터 값을 받을 수 있고, 자식에서도 변경할 수 있다.</p>
<hr>
<h2 id="🌿-debugging">🌿 Debugging</h2>
<h3 id="inspect">$inspect</h3>
<blockquote>
<p><code>$inspect</code>는 지정한 변수나 상태가 변경될 때마다 해당 값을 콘솔에 출력한다.</p>
</blockquote>
<h4 id="🏃♀️➡️-svelte4-5">🏃‍♀️‍➡️ Svelte4</h4>
<pre><code class="language-js">&lt;script&gt;
    let count = $state(0);
    let message = $state(&#39;hello&#39;);

    $: {
        console.log(count);
          console.log(message);
    }
&lt;/script&gt;

&lt;button onclick={() =&gt; count++}&gt;Increment&lt;/button&gt;
&lt;input bind:value={message} /&gt;</code></pre>
<h4 id="🏃♂️➡️-svelte5-5">🏃‍♂️‍➡️ Svelte5</h4>
<pre><code class="language-js">&lt;script&gt;
    let count = $state(0);
    let message = $state(&#39;hello&#39;);

    $inspect(count, message); // count 또는 message가 변경될 때마다 콘솔에 출력
&lt;/script&gt;

&lt;button onclick={() =&gt; count++}&gt;Increment&lt;/button&gt;
&lt;input bind:value={message} /&gt;</code></pre>
<p>위 코드에서 count나 message가 변경될 때마다 $inspect가 자동으로 해당 값을 콘솔에 출력한다. 이는 개발 중에 상태 변화를 추적하는 데 유용하며, 프로덕션 빌드에서는 자동으로 제거되어 성능에 영향을 주지 않는다.</p>
<h3 id="inspectwith">$inspect().with()</h3>
<blockquote>
<p>$inspect는 기본적으로 console.log를 사용하여 값을 출력하지만, <code>.with()</code> 메서드를 통해 커스텀 콜백 함수를 지정할 수 있다.</p>
</blockquote>
<h4 id="🏃♂️➡️-svelte5-6">🏃‍♂️‍➡️ Svelte5</h4>
<pre><code class="language-js">&lt;script&gt;
    let count = $state(0);

    $inspect(count).with((type, count) =&gt; {
        if (type === &#39;update&#39;) {
            console.trace(&#39;Count updated:&#39;, count); // 상태 변경 시 스택 트레이스 출력
        }
    });
&lt;/script&gt;

&lt;button onclick={() =&gt; count++}&gt;Increment&lt;/button&gt;</code></pre>
<p>위 예시에서 count가 변경될 때마다 스택 트레이스를 포함한 커스텀 메시지가 콘솔에 출력된다. 이를 통해 상태 변경의 출처를 쉽게 추적할 수 있다.</p>
<h3 id="inspecttrace">$inspect.trace()</h3>
<blockquote>
<p><code>$inspect.trace()</code>는 함수 내부에서 사용되어, 해당 함수가 실행될 때마다 어떤 반응형 상태의 변경으로 인해 실행되었는지를 콘솔에 출력한다.</p>
</blockquote>
<h4 id="🏃♂️➡️-svelte5-7">🏃‍♂️‍➡️ Svelte5</h4>
<pre><code class="language-js">&lt;script&gt;
    let count = $state(0);

    function increment() {
        $inspect.trace();
        count += 1;
    }
&lt;/script&gt;

&lt;button onclick={increment}&gt;Increment&lt;/button&gt;</code></pre>
<p>increment 함수 내에 $inspect.trace()를 추가하면, count의 변경으로 인해 함수가 실행될 때마다 콘솔에 어떤 상태 변경이 함수 실행을 트리거했는지에 대한 정보가 출력된다.</p>
<hr>
<h2 id="🌿-parent-access">🌿 Parent Access</h2>
<h3 id="host">$host()</h3>
<blockquote>
<p>부모 컴포넌트의 상태나 메서드에 접근할 때 <code>$host()</code>를 사용하여 더 직관적으로 데이터를 공유할 수 있다.</p>
</blockquote>
<h4 id="🏃♀️➡️-svelte4-6">🏃‍♀️‍➡️ Svelte4</h4>
<pre><code class="language-js">&lt;!-- Parent.svelte --&gt;
&lt;script&gt;
    import Child from &#39;./Child.svelte&#39;;

    let message = &quot;Hello from Parent!&quot;;
&lt;/script&gt;

&lt;Child message={message} /&gt;

&lt;!-- Child.svelte --&gt;
&lt;script&gt;
    export let message;
&lt;/script&gt;

&lt;p&gt;부모 메시지: {message}&lt;/p&gt;</code></pre>
<h4 id="🏃♂️➡️-svelte5-8">🏃‍♂️‍➡️ Svelte5</h4>
<pre><code class="language-js">&lt;!-- Parent.svelte --&gt;
&lt;script&gt;
    import Child from &#39;./Child.svelte&#39;;

    let message = &quot;Hello from Parent!&quot;;
&lt;/script&gt;

&lt;Child /&gt;

&lt;!-- Child.svelte --&gt;
&lt;script&gt;
    let { message } = $host; // 부모 컴포넌트의 message 가져오기
&lt;/script&gt;

&lt;p&gt;부모 메시지: {message}&lt;/p&gt;</code></pre>
<h3 id="svelte-4-vs-svelte-5-2">Svelte 4 vs Svelte 5</h3>
<table>
<thead>
<tr>
<th>비교 항목</th>
<th>Svelte 4 (기존 방식)</th>
<th>Svelte 5 (새로운 방식)</th>
</tr>
</thead>
<tbody><tr>
<td><strong>부모 상태 접근</strong></td>
<td>Props(<code>export let</code>) 사용</td>
<td><code>$host()</code> 사용</td>
</tr>
<tr>
<td><strong>데이터 전달 방식</strong></td>
<td>부모 → 자식으로만 가능</td>
<td>부모의 데이터를 직접 참조 가능</td>
</tr>
<tr>
<td><strong>렌더링 최적화</strong></td>
<td>Props 변경 시 전체 업데이트 가능</td>
<td>변경된 부분만 반영</td>
</tr>
<tr>
<td><strong>사용 편의성</strong></td>
<td><code>export let</code>을 매번 선언해야 함</td>
<td><code>$host()</code>로 간편하게 접근 가능</td>
</tr>
</tbody></table>
<p>즉, Svelte 5에서는 <code>$host()</code>를 통해 부모 상태 및 메서드 접근이 더 쉬워지고, Props 선언 없이도 데이터를 공유할 수 있다.</p>
<hr>
<h2 id="🌿-slots">🌿 Slots</h2>
<h3 id="children과-render-children">children과 @render children()</h3>
<blockquote>
<p>슬롯이 children이라는 prop으로 전달된다.
<code>@render children()</code>을 사용해 이를 렌더링한다.</p>
</blockquote>
<h4 id="🏃♀️➡️-svelte4-7">🏃‍♀️‍➡️ Svelte4</h4>
<pre><code class="language-js">&lt;button&gt;
  &lt;slot&gt;&lt;/slot&gt; &lt;!-- 슬롯을 여기에 삽입 --&gt;
&lt;/button&gt;</code></pre>
<p>Svelte 4에서는 <slot/> 태그를 사용해서 슬롯 콘텐츠를 렌더링했다.</p>
<h4 id="🏃♂️➡️-svelte5-9">🏃‍♂️‍➡️ Svelte5</h4>
<pre><code class="language-js">&lt;script&gt;
    let { children } = $props(); // 슬롯을 props로 가져옴
&lt;/script&gt;

&lt;button&gt;
    {@render children()}  &lt;!-- 슬롯 내용을 렌더링 --&gt;
&lt;/button&gt;</code></pre>
<p>Svelte 5에서는 슬롯이 children이라는 prop으로 전달된다. 그리고 <code>@render children()</code>을 사용해 렌더링한다. 기존 <slot/> 방식보다 더 명확하고 <strong>함수형 컴포넌트</strong> 스타일에 가까운 구조다.</p>
<hr>
<h2 id="🌿-snippets">🌿 Snippets</h2>
<h3 id="render-snippetname">@render snippetName()</h3>
<blockquote>
<p><code>#snippet</code>은 Svelte 5에서 기존 slot을 대체하는 기능으로, 반복적으로 사용될 수 있는 UI 조각을 정의하는 역할을 한다.
<code>@render snippetName()</code>을 사용하면 스니펫을 원하는 위치에서 쉽게 렌더링 가능하다.</p>
</blockquote>
<h4 id="🏃♀️➡️-svelte4-8">🏃‍♀️‍➡️ Svelte4</h4>
<pre><code class="language-js">{#each images as image}
    {#if image.href}
        &lt;a href={image.href}&gt;
            &lt;img src={image.src} /&gt;
        &lt;/a&gt;
    {:else}
        &lt;img src={image.src} /&gt;
    {/if}
{/each}
</code></pre>
<p>이 코드에서는 images 배열을 순회하면서 각각의 image를 처리하고 있다. image.href가 존재하면 이미지를 a 태그 안에 넣고, 존재하지 않으면 그냥 img 태그만 렌더링된다. 이렇게 하면 중복되는 img 태그가 여러 번 반복된다.</p>
<h4 id="🏃♂️➡️-svelte5-10">🏃‍♂️‍➡️ Svelte5</h4>
<pre><code class="language-js">{#snippet figure(image)}
    &lt;img src={image.src} /&gt;
{/snippet}

{#each images as image}
    {#if image.href}
        &lt;a href={image.href}&gt;
            {@render figure(image)}
        &lt;/a&gt;
    {:else}
        {@render figure(image)}
    {/if}
{/each}</code></pre>
<p>figure(image)라는 스니펫을 정의하고, image.src를 사용해 img 태그를 렌더링하도록 했다. 이 스니펫을 사용하면, 반복적인 img 태그를 직접 작성할 필요 없이 <code>@render figure(image)</code>를 호출하여 재사용할 수 있다. 따라서 중복되는 img 태그를 스니펫으로 묶어 가독성이 좋아지고 유지보수가 쉬워진다!</p>
<h3 id="특징">특징</h3>
<ul>
<li>스니펫은 컴포넌트 내부 어디에서든 선언할 수 있다.</li>
<li>같은 lexical scope 내에서 접근 가능하다. 즉, 형제 요소(siblings)와 그 형제의 자식(children)에서도 접근할 수 있다.</li>
<li>스니펫은 컴포넌트의 props로 전달할 수 있다.</li>
<li>타입을 지정할 수 있다. 즉, TypeScript와 결합 가능!</li>
</ul>
<hr>
<h2 id="🌿-event-handlers">🌿 Event Handlers</h2>
<h3 id="onclick">onclick</h3>
<blockquote>
<p>on:click이 아니라 HTML의 기본 <code>onclick</code> 속성을 그대로 사용한다. </p>
</blockquote>
<h4 id="🏃♀️➡️-svelte4-9">🏃‍♀️‍➡️ Svelte4</h4>
<pre><code class="language-js">&lt;script&gt;
    let count = 0;

    function handleClick() {
        count++;
    }
&lt;/script&gt;

&lt;button on:click={handleClick}&gt;
    clicks: {count}
&lt;/button&gt;</code></pre>
<p>기존에는 on <strong>&quot;:&quot;</strong> click을 사용했다.</p>
<h4 id="🏃♂️➡️-svelte5-11">🏃‍♂️‍➡️ Svelte5</h4>
<pre><code class="language-js">&lt;script&gt;
    let count = $state(0);

    function onclick() {
        count++;
    }
&lt;/script&gt;

&lt;button {onclick}&gt;
    clicks: {count}
&lt;/button&gt;
</code></pre>
<p>Svelte 5에서는 on:click이 아니라 HTML의 기본 <code>onclick</code> 속성을 그대로 사용한다. 또한 이벤트 핸들러를 함수로 분리한 뒤 {}를 사용해 속성으로 바로 전달이 가능하다.</p>
<hr>
<h2 id="🌿-fine-grained-reactivity">🌿 Fine-grained reactivity</h2>
<blockquote>
<p><strong>&quot;세밀한 반응성을 제공한다.&quot;</strong>
불필요한 렌더링을 최소화하면서 필요한 부분만 업데이트하는 방식으로 성능을 최적화한다.</p>
</blockquote>
<h3 id="svelte-4-vs-svelte-5-3">Svelte 4 vs Svelte 5</h3>
<table>
<thead>
<tr>
<th>비교 항목</th>
<th>Svelte 4 (기존 방식)</th>
<th>Svelte 5 (새로운 방식)</th>
</tr>
</thead>
<tbody><tr>
<td><strong>반응형 상태 선언</strong></td>
<td><code>$:</code>를 사용한 반응형 선언</td>
<td><code>$state()</code> 를 사용한 세밀한 상태 관리</td>
</tr>
<tr>
<td><strong>렌더링 범위</strong></td>
<td>상태 변경 시 전체 블록 실행</td>
<td>변경된 부분만 업데이트</td>
</tr>
<tr>
<td><strong>최적화 수준</strong></td>
<td>비교적 전체적으로 반응성 적용</td>
<td>최소한의 변경만 감지하여 업데이트</td>
</tr>
<tr>
<td><strong>렌더링 성능</strong></td>
<td>컴포넌트 전체 리렌더링 가능</td>
<td>필요한 부분만 재렌더링</td>
</tr>
</tbody></table>
<h4 id="🏃♀️➡️-svelte4-10">🏃‍♀️‍➡️ Svelte4</h4>
<pre><code class="language-js">&lt;script&gt;
    let todos = [];

    function remaining(todos) {
        console.log(&#39;recalculating&#39;);
        return todos.filter((todo) =&gt; !todo.done).length;
    }

    function addTodo(event) {
        if (event.key !== &#39;Enter&#39;) return;

        todos = [
            ...todos,
            {
                done: false,
                text: event.target.value
            }
        ];

        event.target.value = &#39;&#39;;
    }
&lt;/script&gt;</code></pre>
<p>Svelte 4의 코드를 살펴보자. remaining() 함수는 todos가 변경될 때마다 실행된다. todos 배열이 새로운 값으로 할당되면서 컴포넌트 전체가 다시 렌더링될 가능성이 높다. 할 일 목록에서 특정 항목만 변경되더라도 전체 todos 배열을 다시 검사해야 되기 때문에 성능 저하가 발생한다.</p>
<h4 id="🏃♂️➡️-svelte5-12">🏃‍♂️‍➡️ Svelte5</h4>
<pre><code class="language-js">&lt;script&gt;
    let todos = $state([]);

    let remaining = $derived(() =&gt; {
        console.log(&#39;recalculating&#39;);
        return todos.filter((todo) =&gt; !todo.done).length;
    });

    function addTodo(event) {
        if (event.key !== &#39;Enter&#39;) return;

        todos = [
            ...todos,
            {
                done: false,
                text: event.target.value
            }
        ];

        event.target.value = &#39;&#39;;
    }
&lt;/script&gt;
</code></pre>
<p>todos를 <code>$state([])</code>로 변경하여 배열 전체가 아닌 <strong>개별 항목</strong>이 변경될 때만 반응형 업데이트가 발생한다. 또한 remaining을 <code>$derived()</code>로 변경하여, todos가 변경될 때마다 remaining이 전체를 다시 계산하지 않고 필요한 부분만 업데이트 한다. 전체 렌더링이 아닌 최소한의 업데이트만 수행하는 방식으로 성능이 개선된 것이다.</p>
<hr>
<h1 id="💡-마치며">💡 마치며</h1>
<p>지금까지 Svelte 5가 되면서 Svelte 4와 달라진 점들에 대해 살펴보았다. 여러 변화들이 있지만 이전보다 세밀하게 상태 관리를 할 수 있다는 것이 장점이지 않을까 싶다. 애증의 스벨트.. 기는 하지만 파면 팔수록 재미있는 건 사실이다. 앞으로도 화이팅해보자고. 😎</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Svelte 데이터 바인딩]]></title>
            <link>https://velog.io/@suhyun_zip/Svelte-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B0%94%EC%9D%B8%EB%94%A9</link>
            <guid>https://velog.io/@suhyun_zip/Svelte-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B0%94%EC%9D%B8%EB%94%A9</guid>
            <pubDate>Sun, 20 Oct 2024 05:28:28 GMT</pubDate>
            <description><![CDATA[<h1 id="🥨-들어가며">🥨 들어가며</h1>
<p>일반적으로 Svelte는 부모 컴포넌트에서 자식 컴포넌트로 데이터가 전달되는 <strong>단방향 데이터 흐름</strong>을 가진다. 단방향 데이터 흐름으로 인해 <code>부모 컴포넌트</code>에서 <code>자식 컴포넌트</code>로 전달되는 데이터가 업데이트될 때 자식 컴포넌트는 <strong>영향을 받아</strong> 반응형 동작을 하게 되지만, 반대로 <code>자식 컴포넌트</code>에서 <code>부모 컴포넌트</code>로 전달받은 데이터가 업데이트될 때 부모 컴포넌트는 <strong>영향을 받지 않는다.</strong></p>
<p>이런 규칙을 깨고 자식 컴포넌트에서 부모 컴포넌트로 전달받은 데이터가 업데이트될 때 부모 컴포넌트가 반응형 동작을 할 수 있게 하는 양방향 데이터 흐름을 가질 수 있도록 하는 것이 <strong>데이터 바인딩</strong>이다. 🙂</p>
<hr>
<h1 id="🥨-데이터-바인딩">🥨 데이터 바인딩</h1>
<h2 id="🧀-input-태그">🧀 Input 태그</h2>
<h3 id="typetext">type=&quot;text&quot;</h3>
<pre><code class="language-js">&lt;script&gt;
    let name = &#39;world&#39;;
&lt;/script&gt;

&lt;input bind:value={name}&gt;
&lt;h1&gt;Hello {name}!&lt;/h1&gt;</code></pre>
<h4 id="완성-화면">완성 화면</h4>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/c72e62f0-31e8-4c5a-a0e0-5314a195db24/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/59e63223-0083-47f2-9b2f-9619d0978e86/image.png" alt=""></p>
<p>이처럼 input에 입력된 값이 변경될 때마다 자동으로 name의 값 또한 변경되는 것을 볼 수 있다.</p>
<h3 id="typenumber-typerange">type=&quot;number&quot;, type=&quot;range&quot;</h3>
<pre><code class="language-js">&lt;script&gt;
    let a = 1;
    let b = 2;
&lt;/script&gt;

&lt;label&gt;
    &lt;input type=&quot;number&quot; bind:value={a} min=&quot;0&quot; max=&quot;10&quot;&gt;
    &lt;input type=&quot;range&quot; bind:value={a} min=&quot;0&quot; max=&quot;10&quot;&gt;
&lt;/label&gt;

&lt;label&gt;
    &lt;input type=&quot;number&quot; bind:value={b} min=&quot;0&quot; max=&quot;10&quot;&gt;
    &lt;input type=&quot;range&quot; bind:value={b} min=&quot;0&quot; max=&quot;10&quot;&gt;
&lt;/label&gt;

&lt;p&gt; {a} + {b} = {a+b}&lt;/p&gt;</code></pre>
<h4 id="완성-화면-1">완성 화면</h4>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/faf270fa-0053-45fe-bd09-5c710c2c7c2e/image.png" alt=""></p>
<p>input 태그의 type 속성이 number 혹은 range일 경우 바인딩되는 값은 자동으로 Number 타입이 된다.</p>
<h3 id="typecheckbox">type=&quot;checkbox&quot;</h3>
<pre><code class="language-js">&lt;script&gt;
    let yes = false;
&lt;/script&gt;

&lt;label&gt;
    &lt;input type=&quot;checkbox&quot; bind:checked={yes}&gt;
    CheckBox
&lt;/label&gt;</code></pre>
<h4 id="완성-화면-2">완성 화면</h4>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/8f24a269-babe-4d93-8ddc-c39295d659c5/image.png" alt=""></p>
<p>type 속성이 checkbox인 경우, bind:checked를 사용해서 checked 속성을 바인딩해야 한다.</p>
<h3 id="typeradio">type=&quot;radio&quot;</h3>
<pre><code class="language-js">&lt;script&gt;
    let picked = null;
&lt;/script&gt;

&lt;label&gt;
    &lt;input type=&quot;radio&quot; value=&quot;One&quot; bind:group={picked}&gt;
    One
&lt;/label&gt;
&lt;label&gt;
    &lt;input type=&quot;radio&quot; value=&quot;Two&quot; bind:group={picked}&gt;
    Two
&lt;/label&gt;
&lt;br&gt;
&lt;span&gt;선택: {picked}&lt;/span&gt;</code></pre>
<h4 id="완성-화면-3">완성 화면</h4>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/4101cfd1-51e8-4512-b03d-1060d4b6d995/image.png" alt=""></p>
<p>radio의 경우 여러 개의 input 태그들이 동일한 데이터에 바인딩되어야 한다. 이때는 <code>bind:group</code>을 사용한다. value 속성에서는 각각 input 태그들이 선택되었을 때 지정되어야 하는 데이터를 지정한다. bind:group은 type이 radio에서뿐만 아니라 동일한 데이터를 바인딩해야 할 경우에도 사용 가능하다.</p>
<hr>
<h2 id="🧀-textarea-태그">🧀 Textarea 태그</h2>
<pre><code class="language-js">&lt;script&gt;
    let value = &#39;Some Text&#39;;
&lt;/script&gt;

&lt;style&gt;
    textarea { width: 100%; height: 200px;}
&lt;/style&gt;

&lt;!-- &lt;textarea bind:value={value}&gt;&lt;/textarea&gt; --&gt;
&lt;textarea bind:value&gt;&lt;/textarea&gt; // 약어 사용 가능</code></pre>
<hr>
<h2 id="🧀-select-태그">🧀 Select 태그</h2>
<pre><code class="language-js">&lt;script&gt;
    const list = [
        {id: 1, text: &#39;A&#39;},
        {id: 2, text: &#39;B&#39;},
        {id: 3, text: &#39;C&#39;}
    ]
    let selected;
&lt;/script&gt;

&lt;select bind:value={selected}&gt;
    {#each list as item (item.id)}
        &lt;option value={item}&gt;{item.text}&lt;/option&gt;
    {/each}
&lt;/select&gt;
&lt;span&gt;선택함: {selected? selected.id : &#39;기다리는 중...&#39;}&lt;/span&gt;</code></pre>
<h4 id="완성-화면-4">완성 화면</h4>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/166af58a-7129-4f1d-a085-97c28114cc65/image.png" alt=""></p>
<p>bind:value로 데이터 바인딩되는 값의 타입은 Object, String 등 모든 타입이 가능하다. 바인딩 데이터의 초기값이 정의되어 있지 않다면 리스트이 첫 번째 값이 기본값으로 저장된다.</p>
<h3 id="multiple-속성">multiple 속성</h3>
<pre><code class="language-js">&lt;script&gt;
    const list = [
        {id: 1, text: &#39;A&#39;},
        {id: 2, text: &#39;B&#39;},
        {id: 3, text: &#39;C&#39;}
    ];
    let selected = [];
&lt;/script&gt;

&lt;select multiple bind:value={selected}&gt;
    {#each list as item (item.id)}
        &lt;option value={item}&gt;{item.text}&lt;/option&gt;
    {/each}
&lt;/select&gt;
&lt;span&gt;선택함: {selected? selected.map(x =&gt; x.id).join(&#39;,&#39;) : &#39;기다리는 중...&#39;}&lt;/span&gt;</code></pre>
<h4 id="완성-화면-5">완성 화면</h4>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/26a18655-5a79-492e-a336-7491b90e6d96/image.png" alt=""></p>
<hr>
<h2 id="🧀-contenteditable-속성">🧀 contenteditable 속성</h2>
<pre><code class="language-js">&lt;script&gt;
    let html = &#39;&lt;p&gt;텍스트를 입력해 주세요.&lt;/p&gt;&#39;
&lt;/script&gt;

&lt;div
    contenteditable=&quot;true&quot;
    bind:innerHTML={html}
&gt;&lt;/div&gt;

&lt;div
    contenteditable=&quot;true&quot;
    bind:innerHTML={html}
&gt;&lt;/div&gt;

&lt;pre&gt;{html}&lt;/pre&gt;

&lt;style&gt;
    [contenteditable] {
        padding: 0.5em;
        border: 1px solid #eee;
        border-radius: 4px;
    }
&lt;/style&gt;</code></pre>
<h4 id="완성-화면-6">완성 화면</h4>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/223de278-8db3-4c82-a81c-be5219e36867/image.png" alt=""></p>
<p>contenteditable를 true로 설정하면 textContent와 innerHTML 속성을 바인딩하여 사용할 수 있다.</p>
<hr>
<h2 id="🧀-each-블록-바인딩">🧀 Each 블록 바인딩</h2>
<pre><code class="language-js">&lt;script&gt;
    import { each } from &quot;svelte/internal&quot;;

    let todos = [
        {done: false, text: &#39;독서하기&#39;},
        {done: false, text: &#39;Svelte 공부하기&#39;},
        {done: false, text: &#39;일기 쓰기&#39;}
    ]
&lt;/script&gt;

{#each todos as todo, index (index)}
    &lt;label class:done={todo.done}&gt;
        &lt;input
            type=&quot;checkbox&quot;
            bind:checked={todo.done}&gt;
            {todo.text}
    &lt;/label&gt;
{/each}</code></pre>
<h4 id="완성-화면-7">완성 화면</h4>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/8a20b633-c809-4321-b453-4bd3bfd80e35/image.png" alt=""></p>
<hr>
<h2 id="🧀-this-바인딩">🧀 This 바인딩</h2>
<p>DOM 요소를 가져오고 싶거나 컴포넌트 인스턴스를 가져올 때 사용하는 것이 This 바인딩이다.</p>
<h3 id="html-태그에서-this-바인딩">HTML 태그에서 This 바인딩</h3>
<pre><code class="language-js">&lt;script&gt;
    let value;
    let input;
&lt;/script&gt;

&lt;input bind:value bind:this={input}&gt;
&lt;button on:click={() =&gt; input.focus()}&gt;focus&lt;/button&gt;</code></pre>
<h4 id="완성-화면-8">완성 화면</h4>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/410880d5-64f5-4c3f-8894-473153f0bf7d/image.png" alt=""></p>
<p>버튼을 클릭하면 input 태그가 포커스되고, bind:this={input}와 같이 작성하면 input 변수에 input 태그가 바인딩된다. 이벤트 핸들러 등의 다른 함수에서 바인딩된 DOM 요소를 접근해서 사용할 수 있다.</p>
<h3 id="컴포넌트에서-this-바인딩">컴포넌트에서 This 바인딩</h3>
<h4 id="innersvelte">Inner.svelte</h4>
<pre><code class="language-js">&lt;!-- src/Inner.svelte --&gt;
&lt;script&gt;
    let value, input;
&lt;/script&gt;

&lt;input bind:value bind:this={input}&gt;</code></pre>
<h4 id="appsvelte">App.svelte</h4>
<pre><code class="language-js">&lt;script&gt;
    import Inner from &quot;./Inner.svelte&quot;;
    let inner;
&lt;/script&gt;

&lt;Inner bind:this={inner} /&gt;
&lt;button on:click={() =&gt; inner.$capture_state().input.focus()}&gt;Focus&lt;/button&gt;</code></pre>
<p>This를 바인딩하여 컴포넌트 인스턴스를 가져온 경우, 바인딩된 컴포넌트 인스턴스의 $capture_state()를 호출하면 해당 컴포넌트의 데이터(State) 값을 가져올 수 있다.</p>
<hr>
<h2 id="🧀-컴포넌트-props-바인딩">🧀 컴포넌트 Props 바인딩</h2>
<p>HTML 속성을 바인딩한 것처럼 bind:Props를 사용하여 컴포넌트의 Props를 바인딩할 수도 있다.</p>
<h4 id="innersvelte-1">Inner.svelte</h4>
<pre><code class="language-js">&lt;!-- src/Inner.svelte --&gt;
&lt;script&gt;
    export let value;
&lt;/script&gt;

&lt;input bind:value&gt;</code></pre>
<h4 id="appsvelte-1">App.svelte</h4>
<pre><code class="language-js">&lt;script&gt;
    import Inner from &quot;./Inner.svelte&quot;;
    let value;
    function handleClick() {
        alert(value);
    }
&lt;/script&gt;

&lt;Inner bind:value /&gt;
&lt;button on:click={handleClick}&gt;출력&lt;/button&gt;</code></pre>
<p>Inner 컴포넌트는 value라는 Props를 전달받고, App 컴포넌트는 bind:value로 value라는 Props를 바인딩했다. 이렇게 컴포넌트의 Props를 바인딩하면 Inner 컴포넌트에서도 App 컴포넌트의 value 값을 업데이트할 수 있는 양방향 데이터 흐름을 가지게 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Svelte 이벤트 처리]]></title>
            <link>https://velog.io/@suhyun_zip/Svelte-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@suhyun_zip/Svelte-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Sun, 20 Oct 2024 04:34:48 GMT</pubDate>
            <description><![CDATA[<h1 id="🧚♂️-들어가며">🧚‍♂️ 들어가며</h1>
<p>이번 포스팅에서는 Svelte에서 이벤트를 처리하는 방법에 대해 정리해 보았다. 특히 <code>dispatch</code>를 눈여겨 보자!</p>
<hr>
<h1 id="🧚♂️-이벤트-처리">🧚‍♂️ 이벤트 처리</h1>
<h2 id="💫-이벤트-수식어">💫 이벤트 수식어</h2>
<p>스벨트는 DOM 이벤트를 처리할 때 이벤트 수식어 기능을 제공한다.</p>
<pre><code class="language-js">&lt;script&gt;
    function handleClick() {
        alert(&#39;no more alert&#39;);
    }
&lt;/script&gt;

&lt;button on:click|once={handleClick}&gt;Click me!&lt;/button&gt;</code></pre>
<p>위는 once 이벤트 수식어를 사용한 코드이다. once 이벤트 수식어는 이벤트가 발생했을 때 이벤트 핸들러를 단 한 번만 호출한다.</p>
<h3 id="종류">종류</h3>
<blockquote>
<h4 id="preventdefault">preventDefault</h4>
<p>태그의 기본 동작을 막는 이벤트 수식어</p>
</blockquote>
<h4 id="stoppropagation">stopPropagation</h4>
<p>이벤트가 다음 요소로 흐르는 것을 막는 이벤트 수식어</p>
<h4 id="passive">passive</h4>
<p>터치 혹은 휠 이벤트로 발생하는 스크롤의 성능을 향상시키는 이벤트 수식어
-&gt; 스벨트는 안전한 곳에 자동으로 추가한다!</p>
<h4 id="nonpassive">nonpassive</h4>
<p><code>passive: false</code>를 지정해 주는 수식어</p>
<h4 id="capture">capture</h4>
<p>캡처 방식으로 이벤트 핸들러를 실행함</p>
<h4 id="once">once</h4>
<p>이벤트 핸들러를 단 한번만 실행하도록 하는 이벤트 수식어</p>
<h4 id="self">self</h4>
<p><code>event.target</code>과 이벤트 핸들러를 정의한 요소가 같을 때 이벤트 핸들러를 실행하도록 하는 이벤트 수식어</p>
<pre><code class="language-js">on:click|once|capture={...}</code></pre>
<p>위 코드처럼 체인으로 연결해 수식어 여러 개를 사용할 수도 있다.</p>
<hr>
<h2 id="💫-컴포넌트-이벤트">💫 컴포넌트 이벤트</h2>
<p>컴포넌트에서 이벤트를 발생시켜 상위 컴포넌트로 데이터를 전달할 수 있다.</p>
<h3 id="createeventdispatcher-함수">createEventDispatcher 함수</h3>
<pre><code class="language-js">dispatch: ((name: string, detail?: any) =&gt; void) = createEventDispatcher();</code></pre>
<p>createEventDispatcher 함수는 이벤트를 발생시키는 dispatch 함수를 반환한다. dispatch 함수는 <code>name</code>과 <code>detail</code> 두 개의 파라미터를 전달받는다.</p>
<blockquote>
<h4 id="name">name</h4>
<p>이벤트 이름</p>
</blockquote>
<h4 id="detail">detail</h4>
<p>이벤트 객체의 detail 필드에 담을 데이터</p>
<p>여기서 주의할 점은 createEventDispatcher()는 컴포넌트가 <strong>처음 생성</strong>될 때 호출되어야 한다. 함수 안에서 createEventDispatcher()를 호출하여 dispatch를 사용하면 이벤트 전달이 되지 않는다. 아래에서 코드와 함께 dispatch 사용법을 연습해 보자!</p>
<h4 id="innersvelte">Inner.svelte</h4>
<pre><code class="language-js">&lt;!-- src/Inner.svelte --&gt;
&lt;script&gt;
    import { createEventDispatcher } from &quot;svelte&quot;;

    const dispatch = createEventDispatcher();

    function sayHello() {
        dispatch(&#39;message&#39;, {
            text: &#39;안녕!&#39;
        })
    }
&lt;/script&gt;

&lt;button on:click={sayHello}&gt;인사하기&lt;/button&gt;</code></pre>
<h4 id="appsvelte">App.svelte</h4>
<pre><code class="language-js">&lt;script&gt;
    import Inner from &quot;./Inner.svelte&quot;;

    function handleMessage(event) {
        debugger;
        alert(event.detail.text);
    }
&lt;/script&gt;

&lt;Inner on:message={handleMessage} /&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/80c14c44-4619-4f03-b8b7-b7f196728894/image.png" alt=""></p>
<p>디버깅을 해 보면 event.detail에 하위 컴포넌트에서 전달한 text라는 데이터를 확인할 수 있다. 이처럼 하위에서 상위로 데이터를 보낼 일이 있을 때 사용하면 아주 편리하다. 😎</p>
<h3 id="customevent">CustomEvent</h3>
<p>CreateEventDispatcher로 생성된 컴포넌트 이벤트는 CustomEvent를 말한다. 개발자가 직접 정의하여 사용할 수 있는 이벤트다.</p>
<hr>
<h2 id="💫-이벤트-포워딩">💫 이벤트 포워딩</h2>
<p>DOM 이벤트와 달리 컴포넌트 이벤트는 버블링되지 않는다. 자동으로 상위 요소로 이벤트가 전달되지 않기 때문에 의도적으로 부모 컴포넌트로 이벤트를 전달해야 한다.</p>
<h4 id="innersvelte-1">Inner.svelte</h4>
<pre><code class="language-js">&lt;!-- src/Inner.svelte --&gt;
&lt;script&gt;
    import { createEventDispatcher } from &quot;svelte&quot;;

    const dispatch = createEventDispatcher();

    function sayHello() {
        dispatch(&#39;message&#39;, {
            text: &#39;안녕!&#39;
        })
    }
&lt;/script&gt;

&lt;button on:click={sayHello}&gt;인사하기&lt;/button&gt;</code></pre>
<h4 id="outersvelte">Outer.svelte</h4>
<pre><code class="language-js">&lt;!-- src/Outer.svelte --&gt;
&lt;script&gt;
    import Inner from &quot;./Inner.svelte&quot;;
    import { createEventDispatcher } from &quot;svelte&quot;;

    const dispatch = createEventDispatcher();

    function forward(event) {
        dispatch(&#39;message&#39;, event.detail);
    }
&lt;/script&gt;

&lt;Inner on:message={forward}/&gt;</code></pre>
<h4 id="appsvelte-1">App.svelte</h4>
<pre><code class="language-js">&lt;script&gt;
    import Outer from &quot;./Outer.svelte&quot;;

    function handleMessage(event) {
        alert(event.detail.text);
    }
&lt;/script&gt;

&lt;Outer on:message={handleMessage} /&gt;</code></pre>
<h4 id="완성-화면">완성 화면</h4>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/9dfe8433-27ce-48be-8430-56e39d19c7f4/image.png" alt=""></p>
<p>스벨트에서는 약어를 제공하기 때문에 Outer.svelte의 코드를 좀 더 간결하게 작성할 수도 있다.</p>
<h4 id="outersvelte-1">Outer.svelte</h4>
<pre><code class="language-js">&lt;!-- src/Outer.svelte --&gt;
&lt;script&gt;
    import Inner from &quot;./Inner.svelte&quot;;
&lt;/script&gt;

&lt;Inner on:message/&gt;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Svelte 논리 블록]]></title>
            <link>https://velog.io/@suhyun_zip/Svelte-%EB%85%BC%EB%A6%AC-%EB%B8%94%EB%A1%9D</link>
            <guid>https://velog.io/@suhyun_zip/Svelte-%EB%85%BC%EB%A6%AC-%EB%B8%94%EB%A1%9D</guid>
            <pubDate>Sun, 20 Oct 2024 03:53:31 GMT</pubDate>
            <description><![CDATA[<h1 id="🎵-들어가며">🎵 들어가며</h1>
<p>이번 포스팅에서는 스벨트에서 사용하는 논리 블록에 대해 알아보자! 😀</p>
<hr>
<h1 id="🎵-논리-블록">🎵 논리 블록</h1>
<h2 id="🔮-조건문-블록">🔮 조건문 블록</h2>
<p>스벨트는 조건에 따라 DOM을 화면에 나타낼지 나타내지 않을지 선택할 수 있는 조건문 블록을 제공한다. js와 동일하게 If, Else, Else-if 블록이 있다.</p>
<h3 id="if-블록">If 블록</h3>
<pre><code class="language-js">&lt;script&gt;
    let user = {loggedIn: false};

    function toggle() {
        user.loggedIn = !user.loggedIn;
    }
&lt;/script&gt;

{#if user.loggedIn}
    &lt;button on:click={toggle}&gt;
        Log out
    &lt;/button&gt;
{/if}

{#if !user.loggedIn}
    &lt;button on:click={toggle}&gt;
        Log in
    &lt;/button&gt;
{/if}</code></pre>
<h3 id="else-블록">Else 블록</h3>
<p>Else 블록을 사용하여 좀 더 간단히 작성해 보자.</p>
<pre><code class="language-js">&lt;script&gt;
    let user = {loggedIn: false};

    function toggle() {
        user.loggedIn = !user.loggedIn;
    }
&lt;/script&gt;

{#if user.loggedIn}
    &lt;button on:click={toggle}&gt;
        Log out
    &lt;/button&gt;
{:else}
    &lt;button on:click={toggle}&gt;
        Log in
    &lt;/button&gt;
{/if}</code></pre>
<h3 id="else-if-블록">Else-if 블록</h3>
<pre><code class="language-js">&lt;script&gt;
    let x = 7;
&lt;/script&gt;

{#if x &gt; 10}
    &lt;p&gt;{x} is greater than 10&lt;/p&gt;
{:else if 5 &gt; x}
    &lt;p&gt;{x} is less than 5&lt;/p&gt;
{:else}
    &lt;p&gt;{x} is between 5 and 10&lt;/p&gt;
{/if}
</code></pre>
<hr>
<h2 id="🔮-반복문-블록">🔮 반복문 블록</h2>
<h3 id="each-블록">Each 블록</h3>
<h4 id="배열">배열</h4>
<pre><code class="language-js">&lt;script&gt;
    const arr = [&#39;H&#39;, &#39;e&#39;, &#39;l&#39;, &#39;l&#39;, &#39;o&#39;];
&lt;/script&gt;

&lt;ul&gt;
    {#each arr as item}
        &lt;li&gt;{item}&lt;/li&gt;
    {/each}
&lt;/ul&gt;</code></pre>
<h4 id="유사-배열">유사 배열</h4>
<pre><code class="language-js">&lt;script&gt;
    const arrLike = {
        0: &#39;H&#39;,
        1: &#39;e&#39;,
        2: &#39;l&#39;,
        3: &#39;l&#39;,
        4: &#39;o&#39;,
        length: 5
    };
&lt;/script&gt;

&lt;ul&gt;
    {#each arrLike as item}
        &lt;li&gt;{item}&lt;/li&gt;
    {/each}
&lt;/ul&gt;</code></pre>
<h4 id="iterable-객체">iterable 객체</h4>
<p>반복 가능한 객체는 아래와 같이 Each 블록을 사용할 수 있다.</p>
<blockquote>
<ul>
<li><strong>iterator</strong>
value와 done이라는 두 개의 속성을 가진 객체를 반환하는 <code>next</code> 함수를 포함한 객체</li>
</ul>
</blockquote>
<ul>
<li><strong>iterable</strong>
이터레이터를 반환하는 <code>[Symbol.Iterator]</code> 함수를 가진 객체</li>
</ul>
<pre><code class="language-js">&lt;script&gt;
    const iterable = {
        [Symbol.iterator]: function*() {
            yield &#39;H&#39;;
            yield &#39;e&#39;;
            yield &#39;l&#39;;
            yield &#39;l&#39;;
            yield &#39;o&#39;;
        }
    };
&lt;/script&gt;

&lt;ul&gt;
    {#each [...iterable] as item}
        &lt;li&gt;{item}&lt;/li&gt;
    {/each}
&lt;/ul&gt;</code></pre>
<h3 id="else-블록-1">Else 블록</h3>
<p>Each 블록의 배열이 비어 있을 경우, 예외 처리 표현을 위해 Else 블록을 사용할 수 있다.</p>
<pre><code class="language-js">&lt;script&gt;
    let arr = [];
    setTimeout(()=&gt;{
        arr = [&#39;H&#39;, &#39;e&#39;, &#39;l&#39;, &#39;l&#39;, &#39;o&#39;];
    }, 1000)
&lt;/script&gt;

&lt;ul&gt;
    {#each arr as item}
        &lt;li&gt;{item}&lt;/li&gt;
    {:else}
        &lt;li&gt;빈 배열입니다.&lt;/li&gt;
    {/each}
&lt;/ul&gt;</code></pre>
<h3 id="index-사용">index 사용</h3>
<p>Each 블록의 두 번째 파라미터로 현재의 index를 알 수 있다.</p>
<pre><code class="language-js">&lt;script&gt;
    const arr = [&#39;H&#39;, &#39;e&#39;, &#39;l&#39;, &#39;l&#39;, &#39;o&#39;];
&lt;/script&gt;

&lt;ul&gt;
    {#each arr as item, i}
        &lt;li&gt;{i+1}: {item}&lt;/li&gt;
    {/each}
&lt;/ul&gt;</code></pre>
<h3 id="구조-분해-사용">구조 분해 사용</h3>
<pre><code class="language-js">&lt;script&gt;
    const arr = [
        {id: 1, value: &#39;H&#39;},
        {id: 2, value: &#39;e&#39;},
        {id: 3, value: &#39;l&#39;},
        {id: 4, value: &#39;l&#39;},
        {id: 5, value: &#39;o&#39;}
    ];
&lt;/script&gt;

&lt;ul&gt;
    {#each arr as {id, value}}
        &lt;li&gt;{id}: {value}&lt;/li&gt;
    {/each}
&lt;/ul&gt;</code></pre>
<h3 id="key-지정">Key 지정</h3>
<p>Key는 Each 블록의 업데이트를 최적화하기 위해 사용된다. Key를 지정하면 업데이트되었을 때, 업데이트된 위치를 정확히 인지해 해당 DOM만 업데이트할 수 있다.</p>
<pre><code class="language-js">&lt;script&gt;
    let arr = [
        {id: 1, value: &#39;H&#39;},
        {id: 2, value: &#39;e&#39;},
        {id: 3, value: &#39;l&#39;},
        {id: 4, value: &#39;l&#39;},
        {id: 5, value: &#39;o&#39;}
    ];
&lt;/script&gt;

&lt;ul&gt;
    {#each arr as item}
        &lt;li&gt;{item.id}: {item.value}&lt;/li&gt;
    {/each}
&lt;/ul&gt;

&lt;button on:click={() =&gt; {arr = arr.slice(1)}}&gt;
    첫 번째 아이템 제거
&lt;/button&gt;</code></pre>
<p>위와 같이 작성했을 경우, 첫 번째 아이템이 제거되면 위에서부터 순차적으로 업데이트가 되고 마지막 아이템이 삭제되어 총 5번의 업데이트가 발생한다.</p>
<pre><code class="language-js">&lt;script&gt;
    let arr = [
        {id: 1, value: &#39;H&#39;},
        {id: 2, value: &#39;e&#39;},
        {id: 3, value: &#39;l&#39;},
        {id: 4, value: &#39;l&#39;},
        {id: 5, value: &#39;o&#39;}
    ];
&lt;/script&gt;

&lt;ul&gt;
    {#each arr as item (item.id)}
        &lt;li&gt;{item.id}: {item.value}&lt;/li&gt;
    {/each}
&lt;/ul&gt;

&lt;button on:click={() =&gt; {arr = arr.slice(1)}}&gt;
    첫 번째 아이템 제거
&lt;/button&gt;</code></pre>
<p>두 번째 코드처럼 Key를 지정하면 실제로 변경된 아이템만 업데이트되어 최적화된다. Key를 지정하는 방법은 <code>{#each arr as item (item.id)}</code>와 같이 소괄호({...}) 안에 Key를 지정하면 된다.</p>
<p>스벨트는 내부적으로 Map 객체를 사용하기 때문에 Key에 숫자나 문자열뿐만 아니라 객체를 사용할 수도 있다. 즉, <code>(item.id)</code> 대신 <code>(item)</code>으로 변경할 수도 있다!</p>
<hr>
<h2 id="🔮-비동기-블록">🔮 비동기 블록</h2>
<h3 id="await-블록">Await 블록</h3>
<pre><code class="language-js">&lt;script&gt;
    let promise = getRandomNumber();

    function getRandomNumber() {
        return new Promise((resolve, reject) =&gt; {
            setTimeout(() =&gt; {
                resolve(Math.random());
            }, 2000);
        });
    }

    function handleClick() {
        promise = getRandomNumber();
    }
&lt;/script&gt;

{#await promise}
    &lt;p&gt;기다리는 중...&lt;/p&gt;
{:then number} 
    &lt;p&gt;생성된 숫자는 {number}입니다.&lt;/p&gt;
{:catch error}
    &lt;p style=&quot;color: red&quot;&gt;{error}&lt;/p&gt;
{/await}

&lt;button on:click={handleClick}&gt;생성하기&lt;/button&gt;</code></pre>
<blockquote>
<p> {#await promise}...{:then number}</p>
</blockquote>
<p>비동기 작업이 응답하기 전 화면에 노출되는 블록이다. 스켈레톤 UI를 사용하기 적당한 블록이다.</p>
<blockquote>
<p>{:then number}...{:catch error}</p>
</blockquote>
<p>비동기 작업이 응답하여 응답된 결과가 화면에 노출되는 블록이다. number는 Promise의 resolve 함수에 전달된 파라미터 값이다. Async 함수의 경우에는 반환값이다.</p>
<blockquote>
<p>{:catch error}...{/await}</p>
</blockquote>
<p>비동기 작업에서 에러가 발생할 때 화면에 노출되는 블록이다. error는 Promise의 reject 함수에 전달된 파라미터 값이다. Async 함수의 경우에는 throw로 반환된 값이다.</p>
<h3 id="간략한-await-블록">간략한 Await 블록</h3>
<pre><code class="language-js">{#await promise then number}
    &lt;p&gt;생성된 숫자는 {number}입니다.&lt;/p&gt;
{/await}</code></pre>
<p>비동기 작업에서 에러가 발생하지 않을 것을 확신할 수 있다면 Catch 블록을 생략할 수 있다.</p>
<hr>
<h2 id="🔮-key-블록">🔮 Key 블록</h2>
<p>Key 블록을 사용하면 블록에 사용한 표현식이 업데이트되면서 Key 블록의 내용들이 제거된 후 다시 추가된다.</p>
<pre><code class="language-js">&lt;script&gt;
    import KeyComp from &#39;./KeyComp.svelte&#39;;
    let value = 0;
&lt;/script&gt;

{#key value}
    &lt;KeyComp {value} /&gt;
{/key}
&lt;button on:click={() =&gt; value++}&gt;Add Value&lt;/button&gt;</code></pre>
<h4 id="완성-화면">완성 화면</h4>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/cf69bbc1-1dec-4b5a-9fb7-e2ba23d72c33/image.png" alt=""></p>
<p>개발자 도구로 살펴보면 첫 렌더링 시 컴포넌트가 생성되어 create 로그가 출력된 것을 볼 수 있다. Key 블록의 조건이 업데이트될 때마다 컴포넌트가 제거된 후 다시 생성된다. 때문에 Add Value 버튼이 클릭된 횟수만큼 로그가 출력된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Svelte Props]]></title>
            <link>https://velog.io/@suhyun_zip/Svelte-Props</link>
            <guid>https://velog.io/@suhyun_zip/Svelte-Props</guid>
            <pubDate>Sat, 19 Oct 2024 12:31:14 GMT</pubDate>
            <description><![CDATA[<h1 id="🧚♀️-들어가며">🧚‍♀️ 들어가며</h1>
<p>이번 포스팅에서는 Props에 대해 알아보려고 한다.</p>
<hr>
<h1 id="🧚♀️-props">🧚‍♀️ Props</h1>
<h2 id="🧙♂️-개념">🧙‍♂️ 개념</h2>
<blockquote>
<p>부모 컴포넌트에서 자식 컴포넌트로 전달되는 데이터들을 <code>Properties(이하 Props)</code>라고 한다. </p>
</blockquote>
<p>앞선 포스팅에서 속성(Attribute)에 대해 살펴보았다. 속성과 Props의 개념은 동일하다. 다만 HTML 태그에서 사용하면 <code>속성</code>, 컴포넌트에서 사용하면 <code>Props</code>라고 한다. 용어 차이만 있을 뿐!</p>
<pre><code class="language-js">&lt;script&gt;
    import Component from &#39;./Component.svelte&#39;;
&lt;/script&gt;

&lt;div class=&quot;this-is-attribute&quot;&gt;&lt;/div&gt; &lt;!-- class는 속성이다. --&gt;
&lt;Component text=&quot;this-is-property&quot; /&gt; &lt;!-- text는 Props이다. --&gt;</code></pre>
<hr>
<h2 id="🧙♂️-특징">🧙‍♂️ 특징</h2>
<h3 id="단방향-데이터-흐름">단방향 데이터 흐름</h3>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/b2100df3-3f25-4ba7-bf90-c42a16061280/image.png" alt="">
(출처 - Joy of Code)</p>
<p>Props는 기본적으로 단방향 데이터 흐름으로 이루어진다. 단방향 데이터 흐름은 부모에서 자식 컴포넌트로 데이터가 전달되는 것을 말한다. 반대로 자식에서 부모 컴포넌트로 데이터를 전달하기 위해서는 이벤트를 통해야 한다.</p>
<p>자식 컴포넌트에서 Props의 값을 변경하더라도 부모 컴포넌트에서는 Props로 전달한 값은 업데이트되지 않는다. 단방향 데이터 흐름은 데이터 흐름을 단순하게 만들고, 흐름이 이해하기 쉽기 때문에 개발과 유지보수가 편리하다.</p>
<p>만약 Props를 양방향 데이터 흐름으로 사용하려면 데이터 바인딩을 하면 되는데, 이는 나중에 다시 알아보도록 하자. 🤔</p>
<hr>
<h2 id="🧙♂️-props-정의">🧙‍♂️ Props 정의</h2>
<h4 id="appsvelte">App.svelte</h4>
<pre><code class="language-js">&lt;script&gt;
    import Child from &#39;./Child.svelte&#39;;
&lt;/script&gt;

&lt;Child text=&quot;hello, world!&quot;/&gt;</code></pre>
<h4 id="childsvelte">Child.svelte</h4>
<pre><code class="language-js">&lt;!-- src/Child.svelte --&gt;
&lt;script&gt;
    export let text;
&lt;/script&gt;

&lt;p&gt;{text}&lt;/p&gt;</code></pre>
<h4 id="완성-화면">완성 화면</h4>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/eacd3cc9-1814-4bad-b32d-42565573b210/image.png" alt=""></p>
<p>위처럼 export를 사용하여 Props를 전달받을 수 있다.</p>
<hr>
<h2 id="🧙♂️-기본값-설정">🧙‍♂️ 기본값 설정</h2>
<h4 id="childsvelte-1">Child.svelte</h4>
<pre><code class="language-js">&lt;!-- src/Child.svelte --&gt;
&lt;script&gt;
    export let text = &#39;전달받은 값이 없습니다.&#39;;
&lt;/script&gt;

&lt;p&gt;{text}&lt;/p&gt;</code></pre>
<p>자식 컴포넌트에 Props 값이 전달되지 않았을 경우를 대비하여 Props의 기본값을 설정할 수 있다.</p>
<hr>
<h2 id="🧙♂️-전개-연산자-사용">🧙‍♂️ 전개 연산자 사용</h2>
<p>전개 연산자(Spread Operator)는 많은 양의 Props를 자식 컴포넌트에 전달해야 할 때 코드 양을 줄이는 역할을 한다.</p>
<h4 id="infosvelte">Info.svelte</h4>
<pre><code class="language-js">&lt;!-- src/Info.svelte --&gt;
&lt;script&gt;
    export let name;
    export let version;
    export let speed;
    export let website;
&lt;/script&gt;

&lt;p&gt;
    The &lt;code&gt;{name}&lt;/code&gt; package is {speed} fast.
    Download version {version} from &lt;a href=&quot;https://www.npmjs.com/package/{name}&quot;&gt;npm&lt;/a&gt;
    and &lt;a href={website}&gt;learn more here&lt;/a&gt;
&lt;/p&gt;</code></pre>
<h4 id="appsvelte-1">App.svelte</h4>
<pre><code class="language-js">&lt;script&gt;
    import Info from &#39;./Info.svelte&#39;;
    const pkg = {
        name: &#39;svelte&#39;,
        version: 3,
        speed: &#39;blazing&#39;,
        website: &#39;https://svelte.dev&#39;
    };
&lt;/script&gt;

&lt;Info 
    name={pkg.name}
    version={pkg.version}
    speed={pkg.speed}
    website={pkg.website}
/&gt;</code></pre>
<h4 id="완성-화면-1">완성 화면</h4>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/8c6044b3-b59b-4836-889c-19430e00b839/image.png" alt=""></p>
<p>코드가 너무 길다.... 전개 연산자를 사용하면 아래와 같이 코드 양을 줄일 수 있다.</p>
<h4 id="appsvelte-2">App.svelte</h4>
<pre><code class="language-js">&lt;script&gt;
    import Info from &#39;./Info.svelte&#39;;
    const pkg = {
        name: &#39;svelte&#39;,
        version: 3,
        speed: &#39;blazing&#39;,
        website: &#39;https://svelte.dev&#39;
    };
&lt;/script&gt;

&lt;Info {...pkg}/&gt;</code></pre>
<p>아주 코드가 간단해졌다. 😎</p>
<hr>
<h2 id="🧙♂️--props와--restprops">🧙‍♂️ $$ props와 $$ restProps</h2>
<p>스벨트는 Props를 전달받은 컴포넌트에서 export로 Props를 선언하지 않아도 Props를 사용할 수 있는 방법을 제공한다. 최적화하기 어렵기 때문에 이 방법은 추천하지 않지만, 어떤 Props가 컴포넌트로 전달될지 모르는 경우에는 유용하게 사용 가능하다.</p>
<h3 id="props">$$props</h3>
<h4 id="infosvelte-1">Info.svelte</h4>
<pre><code class="language-js">&lt;!-- src/Info.svelte --&gt;
&lt;p&gt;
    The &lt;code&gt;{$$props.name}&lt;/code&gt; package is {$$props.speed} fast.
    Download version {$$props.version} from &lt;a href=&quot;https://www.npmjs.com/package/{$$props.name}&quot;&gt;npm&lt;/a&gt;
    and &lt;a href={$$props.website}&gt;learn more here&lt;/a&gt;
&lt;/p&gt;</code></pre>
<p><code>$$props</code> 변수는 컴포넌트로 전달된 모든 Props를 담고 있다.</p>
<h3 id="restprops">$$restProps</h3>
<h4 id="infosvelte-2">Info.svelte</h4>
<pre><code class="language-js">&lt;!-- src/Info.svelte --&gt;
&lt;script&gt;
    export let name;
    export let version;
&lt;/script&gt;

&lt;p&gt;
    The &lt;code&gt;{name}&lt;/code&gt; package is {$$restProps.speed} fast.
    Download version {version} from &lt;a href=&quot;https://www.npmjs.com/package/{$$restProps.name}&quot;&gt;npm&lt;/a&gt;
    and &lt;a href={$$restProps.website}&gt;learn more here&lt;/a&gt;
&lt;/p&gt;</code></pre>
<p><code>$$restProps</code> 변수는 export로 정의되지 않은 Props만 담고 있다. name과 version Props는 export로 선언되었기 때문에 <code>$$restProps.name</code>과 <code>$$restProps.version</code>의 값은 undefined다.</p>
<hr>
<h2 id="🧙♂️---style-props">🧙‍♂️ --style-props</h2>
<p>사용자 지정 CSS 속성을 컴포넌트 Props로 전달할 수 있다. ---style-props는 문법적 설탕(Syntactic sugar)이다.</p>
<h4 id="globalcss">global.css</h4>
<pre><code class="language-js">&lt;!-- public/global.css --&gt;
html {
  ---theme-color: black;
}</code></pre>
<h4 id="stylepropssvelte">StyleProps.svelte</h4>
<pre><code class="language-js">&lt;!-- src/StyleProps.svelte --&gt;
&lt;div&gt;테마 적용&lt;/div&gt;

&lt;style&gt;
    div {
        color: var(--theme-color);
    }
&lt;/style&gt;</code></pre>
<h4 id="appsvelte-3">App.svelte</h4>
<pre><code class="language-js">&lt;!-- src/App.svelte --&gt;
&lt;script&gt;
  import StyleProps from &#39;./StyleProps.svelte&#39;;
&lt;/script&gt;

&lt;StyleProps --theme-color=&quot;red&quot; /&gt;
&lt;StyleProps /&gt;</code></pre>
<p>&quot;--theme-color=&#39;red&#39;&quot;를 전달하고 나머지 하나에는 아무것도 전달하지 않았다. --theme-color를 전달한 StyleProps 컴포넌트의 글씨는 빨간색, 전달하지 않은 StyleProps는 검은색으로 나타난다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Svelte 반응형 문법]]></title>
            <link>https://velog.io/@suhyun_zip/%EB%B0%98%EC%9D%91%ED%98%95-%EB%AC%B8%EB%B2%95</link>
            <guid>https://velog.io/@suhyun_zip/%EB%B0%98%EC%9D%91%ED%98%95-%EB%AC%B8%EB%B2%95</guid>
            <pubDate>Sat, 19 Oct 2024 11:46:23 GMT</pubDate>
            <description><![CDATA[<h1 id="💬-들어가며">💬 들어가며</h1>
<p>이번 포스팅에서는 스벨트에서 사용하는 반응형 문법에 대해 알아보자. 특히 스벨트의 $ 문법을 눈여겨 보자!</p>
<hr>
<h1 id="💬-반응형-문법">💬 반응형 문법</h1>
<h2 id="👏-데이터-할당">👏 데이터 할당</h2>
<h3 id="반응형-동작">반응형 동작</h3>
<blockquote>
<p>사용자 행동(클릭 등) 또는 데이터 변화에 따라 자동으로 데이터 혹은 화면이 업데이트되는 것</p>
</blockquote>
<pre><code class="language-js">&lt;script&gt;
    let count = 0;
    setInterval(() =&gt; count += 1, 1000);
&lt;/script&gt;

&lt;p&gt;count: {count}&lt;/p&gt;</code></pre>
<h4 id="완성-화면">완성 화면</h4>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/7ed98faf-a4b7-409f-94ff-683092c35205/image.png" alt=""></p>
<p>count 값이 업데이트되면 반응형 동작으로 화면이 업데이트된다. 데이터가 할당되면 반응형 동작을 하게 되는데, 데이터가 할당되는 대표적인 경우는 이벤트가 발생하여 이벤트 핸들러에서 데이터를 업데이트하는 경우다.</p>
<h3 id="이벤트-리스너">이벤트 리스너</h3>
<blockquote>
<pre><code class="language-js">{button on=이벤트 이름=&quot;{이벤트 핸들러}&quot;&gt;Click&lt;/button&gt;</code></pre>
</blockquote>
<pre><code>
이벤트 리스너는 HTML 태그에 이벤트가 발생하는지 감시하고 있다가 이벤트가 발생했을 때 이벤트 핸들러를 호출한다. 이벤트 이름에는 click, change, mousedown 등이 온다.

### 이벤트 핸들러

```js
&lt;script&gt;
    let count = 0;
    function handleClick(event) {
        count += 1;
    }
&lt;/script&gt;

&lt;button on:click={handleClick}&gt;{count}&lt;/button&gt;</code></pre><h3 id="이벤트-인라인-핸들러">이벤트 인라인 핸들러</h3>
<pre><code class="language-js">&lt;script&gt;
    let count = 0;
&lt;/script&gt;

&lt;button on:click={(event) =&gt; count += 1}&gt;{count}&lt;/button&gt;</code></pre>
<p>일부 프레임워크에서는 성능상의 이유로 인라인 이벤트 핸들러 사용을 지양하고 있다. 그러나 스벨트는 컴파일되면서 최적화되기 때문에 인라인 핸들러를 성능 고민 없이 사용할 수 있다.</p>
<hr>
<h2 id="👏-데이터-할당-시-주의사항">👏 데이터 할당 시 주의사항</h2>
<p>스벨트의 반응형은 데이터가 할당되었을 때 동작한다. Number, String 타입 등의 기본형 변수가 아닌 Array, Object 타입의 참조형 변수는 데이터 할당 없이 참조된 데이터 업데이트가 가능하다.</p>
<pre><code class="language-js">    const arr = [];
    arr.push(&#39;Hello World!&#39;);
    arr[0] = &#39;helloWorld&#39;;

    const obj = { foo: &#39;bar&#39; };
    obj.foor = &#39;baz&#39;;</code></pre>
<p>참조형 변수의 값을 데이터 할당 없이 업데이트할 수 있더라도, 참조형 변수 역시 데이터 할당이 되어야 반응형 동작을 하게 된다.</p>
<h3 id="배열-데이터-할당">배열 데이터 할당</h3>
<p>배열의 push, splice 등의 메서드를 사용하여 배열의 참조 값을 업데이트한 경우에는 자동으로 화면을 업데이트하지 않는다. 아래와 같이 할당이 되어야 반응형으로 동작한다.</p>
<pre><code class="language-js">&lt;script&gt;
    let numbers = [];

    function addNumber(){
        // numbers.push(numbers.length + 1);
        // numbers = numbers;
        numbers = [...numbers, numbers.length + 1];
    };
&lt;/script&gt;

&lt;p&gt;{numbers}&lt;/p&gt;
&lt;button on:click={addNumber}&gt;추가&lt;/button&gt;</code></pre>
<h3 id="객체-데이터-할당">객체 데이터 할당</h3>
<p>객체 데이터를 할당할 때 할당되는 변수가 할당 식의 가장 왼쪽에 나타나야 한다. 다음 코드와 같이 할당할 경우 반응형 동작을 하지 않는다.</p>
<pre><code class="language-js">&lt;script&gt;
    let obj = {
        foo: {
            bar: &#39;bar&#39;
        }
    };

    function handleClick() {
        const foo = obj.foo;
        foo.bar = &#39;baz&#39;; // 반응형으로 동작하지 않음.
    }
&lt;/script&gt;

&lt;p&gt;{obj.foo.bar}&lt;/p&gt;
&lt;button on:click={handleClick}&gt;변경&lt;/button&gt;</code></pre>
<p>다음과 같이 수정해야 반응형으로 동작한다.</p>
<pre><code class="language-js">&lt;script&gt;
    let obj = {
        foo: {
            bar: &#39;bar&#39;
        }
    };

    function handleClick() {
        obj.foo.bar = &#39;baz&#39;;
    };
&lt;/script&gt;

&lt;p&gt;{obj.foo.bar}&lt;/p&gt;
&lt;button on:click={handleClick}&gt;변경&lt;/button&gt;</code></pre>
<hr>
<h2 id="👏--문법">👏 $ 문법</h2>
<p><code>$</code> 문법은 특정 데이터를 감시한다. $ 문법을 사용하면 특정 데이터가 업데이트되었을 때 필요한 동작을 수행하는 코드를 작성할 수 있다. $ 문법을 사용하는 방식에 따라 반응형 선언, 반응형 실행으로 구분할 수 있다.</p>
<h3 id="반응형-선언">반응형 선언</h3>
<blockquote>
<p>데이터가 업데이트되었을 때 자동으로 값을 선언하는 방법</p>
</blockquote>
<pre><code class="language-js">&lt;script&gt;
    let count = 0;

    $: doubled = count + 2; // count 업데이트 시 재계산 된다.
&lt;/script&gt;

&lt;button on:click={(event) =&gt; count += 1}&gt;count: {count}&lt;/button&gt;
&lt;p&gt;doubled: {doubled}&lt;/p&gt;</code></pre>
<h4 id="완성-화면-1">완성 화면</h4>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/d3c2f404-089e-4b74-bbeb-a319885c92f9/image.png" alt=""></p>
<p>count 값이 업데이트되면 $: 블록이 반응형으로 실행되어 doubled 값을 업데이트한다. 이처럼 let doubled와 같은 변수 선언이 필요하지 않다.</p>
<h3 id="반응형-실행">반응형 실행</h3>
<pre><code class="language-js">&lt;script&gt;
    let count = 0;
    $: console.log(`count: ${count}`);
&lt;/script&gt;

&lt;button on:click={(event) =&gt; count += 1}&gt;count: {count}&lt;/button&gt;</code></pre>
<h4 id="완성-화면-2">완성 화면</h4>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/12a7a277-13c0-4f01-90c7-de4558da0a8e/image.png" alt=""></p>
<p>count 값이 업데이트될 때마다 값을 console.log로 출력한다.</p>
<pre><code class="language-js">&lt;script&gt;
    let count = 0;
    $: {
        console.log(`count: ${count}`);
        if(count &gt;= 10){
            alert(&#39;count가 10보다 큽니다.&#39;);
        }
    } 
&lt;/script&gt;

&lt;button on:click={(event) =&gt; count += 1}&gt;count: {count}&lt;/button&gt;</code></pre>
<h4 id="완성-화면-3">완성 화면</h4>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/d2f62715-6ff1-42b7-8ca1-d561cfca8998/image.png" alt=""></p>
<p>이렇게 블록으로 작성도 가능하다.</p>
<pre><code class="language-js">&lt;script&gt;
    let count = 0;
    let isEnter = false;
    $: {
        // count 또는 isEnter 값이 변경될 때 실행된다.
        console.log(`count: ${count}, isEnter: ${isEnter}`);
    } 
&lt;/script&gt;

&lt;p&gt;isEnter: {isEnter}&lt;/p&gt;
&lt;button
    on:click={(e) =&gt; count += 1}
    on:mouseenter={() =&gt; isEnter = true}
    on:mouseleave={() =&gt; isEnter = false}
&gt;
    count: {count}
&lt;/button&gt;</code></pre>
<p>$: 블록에서 두 개 이상의 변수를 사용한다면 두 개의 변수를 모두 감시한다. count 또는 isEnter 값이 변경될 경우 $: 블록이 실행된다.</p>
<hr>
<h2 id="👏--문법-사용-시-주의사항">👏 $ 문법 사용 시 주의사항</h2>
<h3 id="첫-렌더링-시-실행">첫 렌더링 시 실행</h3>
<p>$: 블록은 첫 렌더링 시에도 실행된다.</p>
<h3 id="최상위-레벨에서-선언">최상위 레벨에서 선언</h3>
<p>$ 문법은 최상위 레벨에서 선언되어야 한다. 함수 안이나 블록문 안에서 사용되면 데이터를 감시할 수 없다.</p>
<pre><code class="language-js">&lt;script&gt;
    let count = 0;
    {
        // 블록 안에서 사용한 $는 데이터를 감시하지 못함
        $: console.log(`count: ${count}`)
    }
    function init() {
        // 함수 안에서 사용한 $는 데이터를 감시하지 못함
        $: console.log(`count: ${count}`)
    }
    init();
&lt;/script&gt;

&lt;button on:click={(e) =&gt; count += 1}&gt;count: {count}&lt;/button&gt;</code></pre>
<h3 id="감시-데이터-최적화">감시 데이터 최적화</h3>
<p>$ 문법은 $: 블록에서 사용된 데이터를 감시한다. 사용되네이터를 모두 감시하기 때문에 사용된 데이터 중 하나라도 업데이트되면 $: 블록이 실행된다. 하나의 $: 블록이 많은 양의 데이터를 감시하게 되면, 데이터가 업데이트되었을 때마다 불필요한 코드가 실행될 수 있어 성능 저하가 발생하게 된다. <strong>감시하는 데이터를 분리할 수 있다면 여러 개의 $: 블록으로 분리하여 필요한 코드만 동작하게 하는 것</strong>이 좋다.</p>
<h3 id="감시-데이터-명시">감시 데이터 명시</h3>
<p>$: 블록에서 직접 사용되는 값들만 감시하여 반응형으로 동작한다.</p>
<pre><code class="language-js">&lt;script&gt;
    let x = 0;
    let y = 0;

    function yPlusAValue(value) {
        return value + y;
    }

    $: total = yPlusAValue(x);
&lt;/script&gt;

total: {total}
&lt;button on:click={() =&gt; x++}&gt;
    Increment X
&lt;/button&gt;

&lt;button on:click={() =&gt; y++}&gt;
    Increment Y
&lt;/button&gt;</code></pre>
<p>$: 블록에서 yPlusAValue 함수의 파라미터로 x 값을 전달해 주면 x와 y의 값이 합쳐져 total 값이 업데이트된다. x나 y 값이 변경되면 $: 블록이 실행되어 total 값을 업데이트해야 할 것 같지만, 실제로는 $: 블록에 직접 사용된 값은 x뿐이기 때문에 x 값이 업데이트될 때만 반응형으로 동작한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Svelte 기초 문법]]></title>
            <link>https://velog.io/@suhyun_zip/%EC%8A%A4%EB%B2%A8%ED%8A%B8-%EA%B8%B0%EC%B4%88-%EB%AC%B8%EB%B2%95-%EC%95%8C%EA%B8%B0</link>
            <guid>https://velog.io/@suhyun_zip/%EC%8A%A4%EB%B2%A8%ED%8A%B8-%EA%B8%B0%EC%B4%88-%EB%AC%B8%EB%B2%95-%EC%95%8C%EA%B8%B0</guid>
            <pubDate>Sat, 19 Oct 2024 09:52:22 GMT</pubDate>
            <description><![CDATA[<h1 id="📚-들어가며">📚 들어가며</h1>
<p>이번 포스팅에서는 스벨트의 기초 문법에 대해 정리해 보려고 한다.</p>
<hr>
<h1 id="📚-기초-문법">📚 기초 문법</h1>
<h2 id="📖-데이터-정의">📖 데이터 정의</h2>
<pre><code class="language-js">&lt;script&gt;
    let name = &#39;world&#39;;
&lt;/script&gt;

&lt;h1&gt;Hello, {name.toUpperCase()}!&lt;/h1&gt;</code></pre>
<p>스벨트에서 데이터를 정의하는 방법은 위와 같다. 평범한 js 변수를 선언하듯이 변수를 선언해 주고, HTML 태그에서 중괄호({...}) 안에 변수를 사용하면 된다. 이렇게 만들어진 변수는 값이 변경되면 화면도 자동으로 업데이트된다. 또한 중괄호 안에서 js 문법을 사용할 수도 있다.</p>
<hr>
<h2 id="📖-속성-정의">📖 속성 정의</h2>
<h3 id="속성-약어">속성 약어</h3>
<pre><code class="language-js">&lt;script&gt;
    let src = &#39;https://bemoy.github.io/assets/img/logo.png&#39;;
    let name = &#39;Beomy&#39;;
&lt;/script&gt;

&lt;img {src} alt=&quot;{name}&quot;&gt;</code></pre>
<p>스벨트에서는 코드 양을 좀 더 줄일 수 있도록 속성 약어 기능을 제공한다. HTML 태그의 속성 이름과 속성에 할당된 변수 이름이 동일할 때 속성 약어로 작성할 수 있다. 위와 같이 img 태그의 속성 이름과 변수 이름이 src로 동일하기 때문에 속성 약어 사용이 가능하다.</p>
<h3 id="클래스-속성-약어">클래스 속성 약어</h3>
<pre><code class="language-js">&lt;button&gt;
    class:active
    on:click={() =&gt; active = true}
&lt;/button&gt;</code></pre>
<p>HTML 태그에 정의할 클래스 이름과 변수 이름이 동일할 경우 약어로 작성할 수 있다. 클래스 약어를 사용하면 코드를 좀 더 직관적으로 작성할 수 있다.</p>
<hr>
<h2 id="📖-컴포넌트-정의">📖 컴포넌트 정의</h2>
<p>레고 블록들을 조합해서 하나의 완성품을 만들어내는 것처럼 스벨트에서는 컴포넌트를 조합해서 프로젝트를 완성해야 한다.</p>
<h4 id="childsvelte">Child.svelte</h4>
<pre><code class="language-js">&lt;!-- src/Child.svelte --&gt;
 &lt;p&gt;Child 컴포넌트입니다.&lt;/p&gt;</code></pre>
<h4 id="appsvelte">App.svelte</h4>
<pre><code class="language-js">&lt;script&gt;
    import Child from &#39;./Child.svelte&#39;;
&lt;/script&gt;

 &lt;p&gt;App 컴포넌트입니다.&lt;/p&gt;
 &lt;Child /&gt;</code></pre>
<h4 id="완성-화면">완성 화면</h4>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/99760dfb-37fd-48c3-95c1-87df58985815/image.png" alt=""></p>
<p>이렇게 필요한 컴포넌트를 import 해서 블록처럼 조립한다. 컴포넌트를 사용하면 한 파일에 작성해야 할 코드를 여러 파일로 나눠서 작성할 수 있다. 목적에 맞게 컴포넌트 파일을 나눠 작성하면 가독성이 좋아지고 유지보수를 쉽게 만들 수 있다.</p>
<hr>
<h2 id="📖-스타일-정의">📖 스타일 정의</h2>
<p>스타일은 스타일 태그와 인라인 스타일 속성 모두 사용 가능하다. 스벨트는 컴포넌트를 조립하는 방식으로 구성되기 때문에 각 컴포넌트마다 원하는 스타일을 다르게 적용할 수 있다.</p>
<h4 id="firstsvelte">First.svelte</h4>
<pre><code class="language-js">&lt;!-- src/First.svelte --&gt;
 &lt;p&gt;큰 글씨&lt;/p&gt;

 &lt;style&gt;
    p {
        font-size: 30px;
    }
 &lt;/style&gt;</code></pre>
<h4 id="secondsvelte">Second.svelte</h4>
<pre><code class="language-js">&lt;!-- src/First.svelte --&gt;
&lt;p&gt;빨간 글씨&lt;/p&gt;

&lt;style&gt;
   p {
       color: red;
   }
&lt;/style&gt;</code></pre>
<h4 id="appsvelte-1">App.svelte</h4>
<pre><code class="language-js">&lt;script&gt;
    import First from &#39;./First.svelte&#39;;
    import Second from &#39;./Second.svelte&#39;;
&lt;/script&gt;

&lt;First /&gt;
&lt;Second /&gt;</code></pre>
<h4 id="완성-화면-1">완성 화면</h4>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/800eaa57-08fc-4a26-9602-5c6ec0f93e28/image.png" alt=""></p>
<p>이렇게 동작할 수 있는 이유는 스타일 태그 안에 작성된 스타일에 고유의 값을 가진 클래스 속성이 추가되기 때문이다. 추가되는 클래스는 컴포넌트마다 고유한 값을 가지고 있다.</p>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/852ac28e-4630-4e85-9d09-56b0f94d8790/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/d0ed1088-866d-40e7-8a1d-27863d0679f6/image.png" alt=""></p>
<p>개발자 도구로 살펴보면 각각 클래스 속성에 <code>svelte-1xbl3aw</code>, <code>svelte-qehlz6</code> 고유한 클래스가 추가된 것을 볼 수 있다.</p>
<h3 id="global-수식어">:global 수식어</h3>
<p>위에서 알아본 것처럼 A 컴포넌트의 스타일 태그에서 작성한 스타일은 B 컴포넌트의 스타일에 영향을 주지 않는다. 대부분의 경우에서는 컴포넌트 내에서 정의된 스타일만 적용되는 것이 바람직하다. 그러다 다음 코드와 같은 경우가 필요할 때도 있다.</p>
<h4 id="childsvelte-1">Child.svelte</h4>
<pre><code class="language-js">&lt;!-- src/Child.svelte --&gt;
&lt;p&gt;Active 되면 글자 색을 바꾸고 싶어요.&lt;/p&gt;</code></pre>
<h4 id="appsvelte-2">App.svelte</h4>
<pre><code class="language-js">&lt;script&gt;
    import Child from &#39;./Child.svelte&#39;;
    let active = false;
&lt;/script&gt;

&lt;div class:active&gt;
    &lt;Child /&gt;
    &lt;button on:click={() =&gt; active = !active}&gt;Toggle&lt;/button&gt;
&lt;/div&gt;

&lt;style&gt;
    div.active :global(p) {
        color: red;
    }
&lt;/style&gt;</code></pre>
<h4 id="완성-화면-2">완성 화면</h4>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/5406b436-38df-4c9b-8123-d83a29e7c7ff/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/a7c15946-8465-43dc-8b7c-73ce898f69eb/image.png" alt=""></p>
<p><code>:global</code> 수식어의 동작 원리는 :global 수식어가 사용된 HTML 태그에는 고유한 클래스가 추가되지 않는다는 것에 있다. 따라서 :global 수식어를 사용한 선택자는 고유한 클래스가 붙지 않아, 전역 선택자 역할을 하게 된다.</p>
<hr>
<h2 id="📖-html-문자열-표현">📖 HTML 문자열 표현</h2>
<pre><code class="language-js">&lt;script&gt;
    let string = &#39;Hello &lt;strong&gt;world&lt;/strong&gt;!&#39;
&lt;/script&gt;

&lt;p&gt;{string}&lt;/p&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/89360e58-afcd-4b49-909c-b6835f7cf324/image.png" alt=""></p>
<p>문자열에 HTML 태그가 포함되어 있지만 단순 문자열로 인식되기 때문에 화면에 태그가 그대로 보인다.</p>
<pre><code class="language-js">&lt;script&gt;
    let string = &#39;Hello &lt;strong&gt;world&lt;/strong&gt;!&#39;
&lt;/script&gt;

&lt;p&gt;{@html string}&lt;/p&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/9fa5e6af-9571-4fde-bfb1-9ae63ec036d0/image.png" alt=""></p>
<p><code>@html</code> 어노테이션을 사용하면 문자열로 표현된 HTML 태그를 화면에 나타낼 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Git 자주 쓰는 명령어]]></title>
            <link>https://velog.io/@suhyun_zip/Git-%EC%9E%90%EC%A3%BC-%EC%93%B0%EB%8A%94-%EB%AA%85%EB%A0%B9%EC%96%B4</link>
            <guid>https://velog.io/@suhyun_zip/Git-%EC%9E%90%EC%A3%BC-%EC%93%B0%EB%8A%94-%EB%AA%85%EB%A0%B9%EC%96%B4</guid>
            <pubDate>Wed, 25 Sep 2024 09:06:54 GMT</pubDate>
            <description><![CDATA[<h1 id="👀-들어가며">👀 들어가며</h1>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/f9270490-f764-4422-bf87-b62cae09d925/image.png" alt=""></p>
<p>이제는 필수가 되어 버린 Git에서 자주 쓰이는 명령어에 대해 알아보자.</p>
<hr>
<h1 id="💡-git-주요-명령어-정리">💡 Git 주요 명령어 정리</h1>
<h2 id="git-status">git status</h2>
<blockquote>
<p>현재 상태 확인 (추가된 파일, 수정된 파일 등)</p>
</blockquote>
<pre><code class="language-java">git status</code></pre>
<h2 id="git-log">git log</h2>
<blockquote>
<p>커밋 기록 조회</p>
</blockquote>
<pre><code class="language-java">git log</code></pre>
<h2 id="git-add">git add</h2>
<blockquote>
<p>변경 내역이 있는 파일을 스테이징 영역에 추가</p>
</blockquote>
<pre><code class="language-java">git add 변경된파일명
git add .   # 모든 변경된 파일 추가</code></pre>
<h2 id="git-restore">git restore</h2>
<blockquote>
<p>스테이징에서 파일을 내림</p>
</blockquote>
<pre><code class="language-java">git restore --staged 파일명
git restore --staged . # 모든 파일을 스테이징에서 내림</code></pre>
<h2 id="git-commit">git commit</h2>
<blockquote>
<p>스테이징된 파일을 로컬 저장소에 커밋</p>
</blockquote>
<pre><code class="language-java">git commit -m &quot;커밋 메시지&quot;</code></pre>
<h3 id="이미-커밋한-내용을-변경하고-싶다면">이미 커밋한 내용을 변경하고 싶다면?</h3>
<p>간혹 이미 커밋한 내용을 변경해야 할 때가 있는데 이때는 에디터를 열어 수정 후 저장해 주면 된다.</p>
<pre><code class="language-java">git commit --amend

필요한 내용 수정
esc 키 입력
:wq 입력 (변경 내용 저장 후 나가기) / :q 입력 (변경 내용을 저장하지 않고 나가기)</code></pre>
<h2 id="git-push">git push</h2>
<blockquote>
<p>로컬 커밋을 원격 저장소에 푸시</p>
</blockquote>
<pre><code class="language-java">git push origin 브랜치명</code></pre>
<h2 id="git-pull">git pull</h2>
<blockquote>
<p>원격 저장소의 변경 사항을 로컬로 가져오기 위해 풀</p>
</blockquote>
<pre><code class="language-java">git pull origin 브랜치명</code></pre>
<h2 id="git-merge">git merge</h2>
<blockquote>
<p>다른 브랜치와 병합</p>
</blockquote>
<pre><code class="language-java">git merge 브랜치명</code></pre>
<h2 id="git-branch--d">git branch -d</h2>
<blockquote>
<p>브랜치 삭제</p>
</blockquote>
<pre><code class="language-java">git branch -d 브랜치명 # 로컬 브랜치 삭제
git branch -D 브랜치명 # 로컬 브랜치 강제 삭제
git push origin --delete 브랜치명 # 원격 브랜치 삭제</code></pre>
<h2 id="git-branch--m">git branch -m</h2>
<blockquote>
<p>브랜치 이름 변경</p>
</blockquote>
<pre><code class="language-java">git branch -m 새_브랜치명 # 현재 체크아웃된 로컬 브랜치 이름 변경
git branch -m 기존_브랜치명 새_브랜치명 # 체크아웃된 브랜치가 아닌 다른 브랜치 이름 변경</code></pre>
<h2 id="git-fetch">git fetch</h2>
<blockquote>
<p>원격 저장소의 최신 정보 가져옴</p>
</blockquote>
<pre><code class="language-java">git fetch origin</code></pre>
<h2 id="git-checkout">git checkout</h2>
<blockquote>
<p>브랜치 체크아웃 (git fetch 후 사용하기!)</p>
</blockquote>
<pre><code class="language-java">git checkout -b 로컬_브랜치명 origin/원격_브랜치명</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Svelte 알아보기 : 세팅 및 프로젝트 생성까지]]></title>
            <link>https://velog.io/@suhyun_zip/Svelte-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0-%EC%84%B8%ED%8C%85-%EB%B0%8F-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1%EA%B9%8C%EC%A7%80</link>
            <guid>https://velog.io/@suhyun_zip/Svelte-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0-%EC%84%B8%ED%8C%85-%EB%B0%8F-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%83%9D%EC%84%B1%EA%B9%8C%EC%A7%80</guid>
            <pubDate>Wed, 21 Feb 2024 13:41:48 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/413d1c75-4176-4316-baa0-570fd2e0612e/image.png" alt=""></p>
<h1 id="👋-들어가며">👋 들어가며</h1>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/9c86d5db-3cf1-45ec-a4e3-07f9e94e4dd3/image.png" alt=""></p>
<p>사적으로 Svelte를 공부해 보고 싶은 마음 + 공적으로 실무에서 Svelte를 사용하게 됨으로 인해 오늘부터 Svelte에 관련된 글을 작성해 보려고 한다. 공부를 위해 <code>Svelte로 맛보는 웹 애플리케이션 개발</code>이라는 서적을 구매하였기 때문에 본 시리즈는 이 책과 함께 진행될 예정이다. 가 보자고 😎</p>
<hr>
<h1 id="🤔-svelte란-무엇인가">🤔 Svelte란 무엇인가</h1>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/967374ef-100b-4087-bfb2-42c068d8fe86/image.png" alt=""></p>
<p><code>Svelte</code>란 자바스크립트 라이브러리 중 하나로, 리액트보다 가볍고 뷰보다 쉽다는 장점을 내세운 비교적 <strong>최신 프론트엔드 기술</strong>이다.</p>
<h2 id="no-virtual-dom">No Virtual Dom</h2>
<p>스벨트의 가장 큰 특징은 가상 돔이 없다는 것이다. 스벨트는 실행 시점이 아닌 빌드 시점에 모든 코드들을 바닐라 js로 변경시킨다. 컴파일러의 개념인 것이다. 따라서 배포 시 프레임워크, 라이브러리처럼 함께 배포되는 개념이 아니기 때문에 더욱 가볍다. 결과적으로 속도가 더 빨라지게 된다.</p>
<hr>
<h1 id="💬-초기-세팅을-해-보자">💬 초기 세팅을 해 보자</h1>
<h2 id="1-svelte-개발자-도구-설치">#1 Svelte 개발자 도구 설치</h2>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/59320e8c-f200-4d7d-b468-968263aaf98e/image.png" alt=""></p>
<p>크롬 앱 중 Svelte Devtools를 사용하면 컴포넌트의 데이터를 확인할 수 있어 좀 더 편한 디버깅이 가능하다. <a href="https://chromewebstore.google.com/detail/svelte-devtools/kfidecgcdjjfpeckbblhmfkhmlgecoff">Svelte Devtools</a>로 가서 확장 프로그램을 크롬에 추가해 주자.</p>
<h2 id="2-vs-code-nodejs-설치">#2 VS Code, Node.js 설치</h2>
<p>이 부분의 설명은 생략하도록 하겠다.</p>
<hr>
<h1 id="😎-첫-프로젝트-생성하기">😎 첫 프로젝트 생성하기</h1>
<p>스벨트는 롤업과 웹팩을 사용하는 2개의 템플릿 프로젝트를 제공한다.</p>
<blockquote>
<p><strong>- 롤업과 웹팩</strong>
대표적으로 여러 개 자바스크립트 파일을 하나 또는 설정에 따라 그 이상의 자바스크립트 파일로 뭉친 후, 다른 사람들이 보기 어렵게 난독화하는 역할을 한다. 롤업과 웹팩을 <code>번들러</code>라고 한다.</p>
</blockquote>
<h2 id="1-롤업-프로젝트-생성">#1 롤업 프로젝트 생성</h2>
<p>VS Code 터미널에 아래 코드를 입력하여 롤업 번들러를 사용하는 Svelte 프로젝트를 생성한다.</p>
<pre><code>$ npx degit sveltejs/template svelte-app # svelte-app 폴더에 프로젝트를 생성
$ cd svelte-app # svelte-app 폴더로 이동
$ npm install # package 설치
$ npm run dev # svelte-app 프로젝트 실행</code></pre><p><img src="https://velog.velcdn.com/images/suhyun_zip/post/3adb180e-67af-4c2c-bbde-beb65fd85270/image.png" alt=""></p>
<p>터미널에 이런 문구가 뜬다면 프로젝트 생성 및 실행에 성공한 것이다. 주소 창에 로컬 주소를 입력하면 다음과 같은 웹 페이지를 확인할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/f1d96920-13ce-4813-a951-9f4194a6648b/image.png" alt=""></p>
<h2 id="2-웹팩-프로젝트-생성">#2 웹팩 프로젝트 생성</h2>
<p>이번에는 웹팩 번들러를 사용하는 Svelte 프로젝트를 생성해 보자. 둘 다 생성할 필요는 없고, 롤업 또는 웹팩 중 내게 알맞은 번들러를 사용하면 된다.</p>
<pre><code>$ npx degit sveltejs/template-webpack svelte-app # svelte-app 폴더에 프로젝트를 생성
$ cd svelte-app # svelte-app 폴더로 이동
$ npm install # package 설치
$ npm run dev # svelte-app 프로젝트 실행</code></pre><hr>
<h1 id="🔎-프로젝트-구조-살펴보기">🔎 프로젝트 구조 살펴보기</h1>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/50ffccc1-fb25-4d82-8a21-a66d9263775d/image.png" alt=""></p>
<p>스벨트 프로젝트의 디렉터리 구조는 위와 같다. 각 폴더 및 파일의 용도는 아래에 정리해 보았다.</p>
<ul>
<li><p><strong>public/ : public 폴더가 프로덕션 환경에 배포되어야 한다.</strong></p>
<ul>
<li>build/ : 빌드되어 생성된 번들 파일이 모이게 되는 폴더
  -&gt; 웹팩 프로젝트의 경우 build 폴더 없이 public 폴더 아래에 번들 파일이 생성됨!</li>
<li>bundle.css : <code>.svelte</code> 파일 안에서 사용한 style 태그들이 번들되어 생성된 스타일 번들 파일</li>
<li>bundle.js : <code>.svelte</code> 파일이 빌드되어 생성된 js 번들 파일</li>
<li>bundle.js.map : 오류가 발생할 경우 오류 위치를 표시하기 위한 매핑 파일 (디버깅)</li>
<li>global.css : 전역 스타일을 정의하기 위한 스타일 파일
  -&gt; <code>bundle.css</code> 파일에 정의한 스타일이 우선 순위가 더 높음</li>
<li>index.html : 웹 페이지의 인덱스 파일</li>
</ul>
</li>
<li><p><strong>src/ : 대부분의 코딩이 이루어지는 폴더이다.</strong></p>
<ul>
<li>App.svelte : 프로젝트의 루트 컴포넌트
  -&gt; src/main.js에서 App 컴포넌트를 불러와 사용함</li>
<li>main.js : 프로젝트의 첫 시작 파일</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[PWA에 대해 알고 사용해 보기]]></title>
            <link>https://velog.io/@suhyun_zip/PWA%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EA%B3%A0-%EC%82%AC%EC%9A%A9%ED%95%B4-%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@suhyun_zip/PWA%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EA%B3%A0-%EC%82%AC%EC%9A%A9%ED%95%B4-%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Mon, 29 Jan 2024 03:02:44 GMT</pubDate>
            <description><![CDATA[<h1 id="💡-오늘의-목표">💡 오늘의 목표</h1>
<blockquote>
<p>PWA 만드는 방법에 대해 알고, 나의 PWA 커스터마이징 해 보기!</p>
</blockquote>
<hr>
<h1 id="🔎-pwa">🔎 PWA</h1>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/56c9690b-03dc-4e4d-a5b2-e602a087d9b9/image.png" alt=""></p>
<p>PWA란 <code>Progressive Web Apps</code>의 줄임말로, 모바일 기기에서 네이티브 앱과 같은 사용자 경험을 제공하는 웹 앱이다. 기존 전통적인 웹 앱과 는 달리 네이티브 앱과 유사한 기능을 제공할 수 있다는 것이 큰 장점이다. PWA를 사용하면 사용자가 앱을 다운로드하거나, 업데이트할 필요 없이 웹 브라우저를 통해 앱을 바로 사용할 수 있다!</p>
<h2 id="장점">장점</h2>
<ul>
<li>스마트폰, 태블릿 바탕화면에 내 웹 사이트를 설치할 수 있음</li>
<li>오프라인에서도 동작할 수 있음</li>
<li>설치 유도 비용이 적음</li>
</ul>
<h2 id="필수-조건">필수 조건</h2>
<p><code>manifest.json</code>과 <code>service-worker.js</code> 파일이 있어야 한다. 아래 방법을 사용하여 PWA 프로젝트를 만든다면 이 파일들이 자동으로 생성된다.</p>
<h3 id="manifestjson">manifest.json</h3>
<blockquote>
<p>웹 앱의 아이콘, 이름, 테마 색 등을 설정할 수 있는 파일</p>
</blockquote>
<pre><code class="language-js">{
  &quot;short_name&quot;: &quot;React App&quot;, // 설치 후 앱 런처나 바탕화면에 표시할 이름 12자
  &quot;name&quot;: &quot;Create React App Sample&quot;, // 기본 이름
  &quot;icons&quot;: [ // 여러 가지 사이즈별 아이콘 이미지 경로
    {
      &quot;src&quot;: &quot;favicon.ico&quot;,
      &quot;sizes&quot;: &quot;64x64 32x32 24x24 16x16&quot;,
      &quot;type&quot;: &quot;image/x-icon&quot;
    },
    {
      &quot;src&quot;: &quot;logo192.png&quot;,
      &quot;type&quot;: &quot;image/png&quot;,
      &quot;sizes&quot;: &quot;192x192&quot;
    },
    {
      &quot;src&quot;: &quot;logo512.png&quot;,
      &quot;type&quot;: &quot;image/png&quot;,
      &quot;sizes&quot;: &quot;512x512&quot;
    }
  ],
  &quot;start_url&quot;: &quot;.&quot;, // 앱 아이콘 클릭 시 보여 줄 메인 페이지 경로
  &quot;display&quot;: &quot;standalone&quot;, // standalone 또는 fullscreen
  &quot;theme_color&quot;: &quot;#000000&quot;, // 상단 탭 색상 등 원하는 테마 색상
  &quot;background_color&quot;: &quot;#ffffff&quot; // 앱 처음 실행 시 잠깐 뜨는 splashscreen의 배경 색
}</code></pre>
<h3 id="service-workerjs">service-worker.js</h3>
<blockquote>
<p>웹 앱을 설치했을 때 어떤 CSS, JS, HTML, 이미지 파일이 하드에 설치될지 결정할 수 있는 파일
-&gt; <code>Cache Storage</code>에 저장하여 오프라인에서도 사이트를 열 수 있게 도와줌</p>
</blockquote>
<hr>
<h1 id="😎-pwa-사용해-보기">😎 PWA 사용해 보기</h1>
<h2 id="1-pwa-프로젝트-생성">1. PWA 프로젝트 생성</h2>
<pre><code>npx create-react-app 프로젝트명 --template cra-template-pwa</code></pre><p>프로젝트 폴더를 VS Code로 연 다음, 터미널에 위 명령어를 입력하여 새로운 리액트 앱을 만든다. PWA를 만들기 위해서는 꼭 이와 같이 터미널에 명령어를 입력해야 하는데, 만약 기존 프로젝트를 PWA로 만들고 싶을 때는 어떻게 하면 될까? 그냥... 새 PWA 프로젝트를 만들고 기존 코드를 복사 붙여넣기 하면 된다. 👼</p>
<h2 id="2-indexjs-파일-수정">2. index.js 파일 수정</h2>
<h4 id="indexjs">index.js</h4>
<pre><code class="language-js">...

serviceWorkerRegistration.register(); // 기존에 작성되어 있는 unregister를 register로 변경

...</code></pre>
<p>위 파일을 수정한 뒤 빌드하고 배포하면 끝!</p>
<h2 id="3-개발자-도구로-디버깅">3. 개발자 도구로 디버깅</h2>
<p>크롬 개발자 도구로 PWA에 관련된 모든 것을 살펴볼 수 있다. PWA로 만들어진 사이트 <code>flipkart</code>로 확인해 보자.</p>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/c6a4ec01-a3dd-42fa-b3df-cf0b2c82c69f/image.png" alt=""></p>
<p>개발자 도구 &gt; Application 탭에 들어가면 <code>Manifest</code>, <code>Service Workers</code>, <code>Cache storage</code> 항목이 보이는데 각각 하나씩 알아보자.</p>
<h4 id="manifest">Manifest</h4>
<blockquote>
<p><code>manifest.json</code> 내용을 확인할 수 있음</p>
</blockquote>
<h4 id="service-workers">Service Workers</h4>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/e5feb24d-e65b-4198-bfb8-037937c67efa/image.png" alt=""></p>
<blockquote>
<p><code>service-worker</code> 파일이 잘 있는지, 오프라인에서 잘 동작하는지, 푸시 알림이 잘 전송되는지 등을 테스트할 수 있음</p>
</blockquote>
<h4 id="cache-storage">Cache Storage</h4>
<blockquote>
<p><code>service-worker</code> 덕분에 하드에 설치된 CSS, JS, HTML 파일들을 확인하고, 캐시된 파일을 제거할 수 있음</p>
</blockquote>
<hr>
<h1 id="👋-마치며">👋 마치며</h1>
<p>평소에 모바일로 쇼핑몰에 들어갔을 때, 앱 설치를 하지 않았지만 내 폰 배경화면에 앱 형태로 설치되는 것을 보고 신기했던 경험이 있는데 이게 바로 PWA였다니! 여러 장점을 가지고 있는 만큼 편리한 기능인 것 같다. 👍</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[성능 개선 (2) - memo, useMemo, useTransition, useDeferredValue]]></title>
            <link>https://velog.io/@suhyun_zip/%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0-2-memo-useMemo-useTransition-useDeferredValue</link>
            <guid>https://velog.io/@suhyun_zip/%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0-2-memo-useMemo-useTransition-useDeferredValue</guid>
            <pubDate>Wed, 24 Jan 2024 06:38:12 GMT</pubDate>
            <description><![CDATA[<h1 id="👍-성능-개선-두-번째">👍 성능 개선 두 번째!</h1>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/973002f9-e102-43a8-bba3-4d58eb5a48ce/image.gif" alt=""></p>
<p>이번 포스팅에서는 지난 포스팅에 이어 프로젝트 성능 개선 시 유용한 기능 몇 가지를 담아 보려고 한다.</p>
<hr>
<h1 id="📝-memo">📝 memo</h1>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/6b224473-c353-4ef5-a118-f344755570b7/image.png" alt=""></p>
<p>컴포넌트가 재렌더링되면 그 안에 있는 자식 컴포넌트 또한 항상 함께 재렌더링이 된다. 평소에는 큰 문제가 없겠지만, 만약 자식 컴포넌트가 렌더링 시간이 1초 넘게 걸리는 무거운 컴포넌트면 어떻게 할까? 부모 컴포넌트에 있는 버튼을 누를 때마다 1초가 지연되는 문제가 발생하고 만다. 😐</p>
<p>이럴 때 사용하는 것이 <code>memo</code>다.</p>
<h2 id="사용하기">사용하기</h2>
<p>간단한 예시와 함께 불필요한 재렌더링을 막아 보자.</p>
<pre><code class="language-js">import { memo, useState } from &#39;react&#39;

let Child = memo( function(){
  console.log(&#39;재렌더링됨&#39;)
  return &lt;div&gt;자식 컴포넌트&lt;/div&gt;
})

function Cart(){ 

  let [count, setCount] = useState(0)

  return (
    &lt;Child/&gt;
    &lt;button onClick={()=&gt;{ setCount(count+1) }}&gt; + &lt;/button&gt;
  )
}</code></pre>
<p>Child로 전송되는 props가 변하는 경우에만 재렌더링이 되는 것을 콘솔창으로 확인할 수 있다. </p>
<p>이렇게만 보면 <code>memo</code>는 세상 편리해 보이는데.. 그렇다고 막 쓰는 건 안 된다. memo로 감싼 컴포넌트는 불필요한 재렌더링을 막기 위해 기존 props와 바뀐 props를 비교하는 연산이 추가로 진행되는데, 만약 props가 크고 복잡하다면 이 자체로도 큰 부담이 될 수 있다.</p>
<p>따라서 결론 : &quot;<strong>꼭 필요한 곳에만 사용하자</strong>&quot; 😶</p>
<hr>
<h1 id="📋-usememo">📋 useMemo</h1>
<p>위 memo와 유사한 <code>useMemo</code>라는 문법도 있다. 이것은 쉽게 말하자면 useEffect와 유사한 용도이다. 컴포넌트 로드 시 1회만 실행하고 싶은 코드가 있다면 useMemo에 담으면 된다.</p>
<h2 id="사용하기-1">사용하기</h2>
<pre><code class="language-js">import { useMemo, useState } from &#39;react&#39;;

function 함수(){
    return 반복문 10억 번 돌린 결과
}

...

function Cart(){

    let result = useMemo(()=&gt;{return 함수()}, [state])
    /* 컴포넌트 렌더링 시 1회만 실행해 줌 */

    return (
        &lt;Child /&gt;
        &lt;button onClick={()=&gt;{ setCount(count+1) }}&gt; + &lt;/button&gt;
      )
...
</code></pre>
<p>이렇게 작성한다면 useMemo에 담긴 &#39;반복문을 10억 번 돌리는&#39; 함수가 컴포넌트 로드 시 1회만 실행된다. 그렇다면 재렌더링 될 때마다 동작을 하지 않으니 좀 더 성능을 개선할 수 있다! useEffect처럼 특정 state, props가 변할 때만 실행하도록 코드를 짤 수도 있다.</p>
<hr>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/26d33edf-3a09-41d5-9ef2-5943a0663a65/image.jpg" alt=""></p>
<h1 id="🧚♀️-usetransition">🧚‍♀️ useTransition</h1>
<p>재렌더링이 느린 컴포넌트의 성능을 개선할 수 있는 <code>useTransition</code>이라는 훅이 존재한다. 간단한 예시로 알아보도록 하자.</p>
<h2 id="사용하기-2">사용하기</h2>
<pre><code class="language-js">import {useState, useTransition} from &#39;react&#39;

/* 데이터가 10000개 있는 array 자료 만들기 */
let a = new Array(10000).fill(0)

function App(){
  let [name, setName] = useState(&#39;&#39;)
  let [isPending, startTransition] = useTransition()

  return (
    &lt;div&gt;
      &lt;input onChange={ (e)=&gt;{ 
        /* 다른 코드들보다 나중에 처리하기 */
        startTransition(()=&gt;{
          setName(e.target.value) 
        })
      }}/&gt;

      {
        /* array에 담긴 개수만큼 div 생성하기 */
        a.map(()=&gt;{
          return &lt;div&gt;{name}&lt;/div&gt;
        })
      }
    &lt;/div&gt;
  )
}</code></pre>
<p>의도적으로 재렌더링이 느린 컴포넌트를 만들고, useTransition을 사용해 보았다. 이렇게 작성했을 때 성능이 개선되는 이유는 <code>startTransition()</code> 함수로 묶은 state 변경 함수가 다른 코드들보다 나중에 처리되기 때문인데. 따라서 <code>&lt;input&gt;</code> 타이핑처럼 즉각적으로 반응해야 하는 코드가 먼저 실행되고, 사용자가 느끼는 반응 속도도 훨씬 빠르게 느껴진다.</p>
<p>물론 근본적인 성능 개선이라기보다는 특정 코드의 실행 시점을 뒤로 옮겨 주는 것일 뿐이다. 😅</p>
<h3 id="ispending은-뭐지">isPending은 뭐지?</h3>
<blockquote>
<p><code>startTransition()</code>으로 감싼 코드가 처리 중일 때 true로 변하는 변수</p>
</blockquote>
<pre><code class="language-js">{
  isPending ? &quot;로딩 중..💭&quot; :
  a.map(()=&gt;{
    return &lt;div&gt;{name}&lt;/div&gt;
  })
} </code></pre>
<p>이 변수를 활용하여 이런 식으로 로딩 화면을 만들어 보는 것도 가능하다!</p>
<hr>
<h1 id="🧚♂️-usedeferredvalue">🧚‍♂️ useDeferredValue</h1>
<p><code>useDeferredValue</code>는 startTransition()과 용도가 같다. 그러나 useDeferredValue는 state 또는 변수 하나를 집어 넣을 수 있고, 이 state나 변수에 변동사항이 생기면 나중에 처리해 준다.</p>
<h2 id="사용하기-3">사용하기</h2>
<pre><code class="language-js">import {useState, useTransition, useDeferredValue} from &#39;react&#39;

let a = new Array(10000).fill(0)

function App(){
  let [name, setName] = useState(&#39;&#39;)

  /* state에 변동사항이 생겼을 때 나중에 처리해 줌 */
  /* 처리 결과를 state1에 저장함 */
  let state1 = useDeferredValue(name)

  return (
    &lt;div&gt;
      &lt;input onChange={ (e)=&gt;{ 
          setName(e.target.value) 
      }}/&gt;

      {
        a.map(()=&gt;{
          return &lt;div&gt;{state1}&lt;/div&gt;
        })
      }
    &lt;/div&gt;
  )
}</code></pre>
<p>이렇게 작성하면 위와 같은 기능을 개발할 수 있다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[성능 개선 (1) - React DevTools, Redux DevTools, lazy import]]></title>
            <link>https://velog.io/@suhyun_zip/%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0-React-DevTools-Redux-DevTools-lazy-import</link>
            <guid>https://velog.io/@suhyun_zip/%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0-React-DevTools-Redux-DevTools-lazy-import</guid>
            <pubDate>Wed, 24 Jan 2024 05:47:47 GMT</pubDate>
            <description><![CDATA[<h1 id="🎉-프로젝트의-성능을-개선해-보자">🎉 프로젝트의 성능을 개선해 보자!</h1>
<p>이번 포스팅에서는 프로젝트의 성능을 개선할 수 있는 몇 가지 방법들을 담아 보려고 한다. 🙂</p>
<hr>
<h1 id="🍧-react-devtools">🍧 React DevTools</h1>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/bf1c8b98-706f-43fc-8338-4b715ab23d64/image.png" alt="">
<code>출처🔎 GeeksforGeeks</code></p>
<blockquote>
<p>React의 컴포넌트를 검사하고 props와 state를 편집할 수 있으며, 성능 문제를 식별할 수 있도록 도와주는 Chrome 확장 프로그램</p>
</blockquote>
<h2 id="설치하기">설치하기</h2>
<p>Chrome 웹 스토어에서 <a href="https://chromewebstore.google.com/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi">React Developer Tools</a>를 검색하여 설치하면 된다.</p>
<h2 id="사용하기">사용하기</h2>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/addfdd4f-dfdc-4ef1-83dd-c0ad306f0f38/image.png" alt=""></p>
<p>프로젝트를 실행한 뒤 개발자 도구를 열어 보면 상단 탭 영역에 <code>Components</code>와 <code>Profiler</code>가 추가된 모습을 볼 수 있다.</p>
<h3 id="components">Components</h3>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/90ddb8ab-51b3-48d6-bace-fc9b55626558/image.png" alt=""></p>
<p>이처럼 왼쪽에서 컴포넌트 구조 파악이 가능하다. 왼쪽 상단 아이콘을 눌러 컴포넌트를 클릭하면 컴포넌트 안에 있는 state와 props 등이 조회 가능하다. 값을 수정해 볼 수도 있다!</p>
<h3 id="profiler">Profiler</h3>
<p>Profiler 탭에서는 컴포넌트의 렌더링 시간을 측정할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/406bab1e-5a38-495e-9fa7-bfdc8630a0f3/image.png" alt=""></p>
<p>상단의 녹화 버튼을 누른 뒤 페이지 이동이나 버튼 조작을 해 보고, 다시 녹화 버튼을 눌러 녹화를 종료한다. 그러면 방금 렌더링된 모든 컴포넌트의 렌더링 시간을 측정해 준다. 만약 이상하게 느린 컴포넌트가 있다면 여기서 범인을 찾아 수정해 줄 수 있다.</p>
<hr>
<h1 id="🍨-redux-devtools">🍨 Redux DevTools</h1>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/229305d9-3bd9-4247-ab60-3a3bd934b24b/image.png" alt=""></p>
<blockquote>
<p>Redux Store에 있던 state를 개발자 도구에서 조회할 수 있게 해 주는 Chrome 확장 프로그램</p>
</blockquote>
<p>Redux DevTools를 사용하면 Redux store에 있던 state를 전부 확인 가능하다. 또한 dispatch를 날릴 때마다 생기는 변화들에 대해 로그를 작성해 준다. store가 복잡해졌을 때 사용하면 유용하다.</p>
<h2 id="설치하기-1">설치하기</h2>
<p>Chrome 웹 스토어에서 <a href="https://chromewebstore.google.com/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd">Redux DevTools</a>를 검색하여 설치하면 된다.</p>
<h2 id="사용하기-1">사용하기</h2>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/73c2f457-4f50-40ac-b2b6-d91f48db8cfc/image.png" alt=""></p>
<p>프로젝트를 실행한 뒤 개발자 도구를 열어 보면 상단 탭 영역에 <code>Redux</code>가 추가된 모습을 볼 수 있다.</p>
<h4 id="storejs의-일부">store.js의 일부</h4>
<pre><code class="language-js">...

let cart = createSlice({
    name : &#39;cart&#39;,
    initialState : [
        {id : 0, name : &#39;White and Black&#39;, count : 2},
        {id : 2, name : &#39;Grey Yordan&#39;, count : 1}
    ],
    reducers : {
        ...

        addCart(state, action){
            state.push(action.payload)
        }
    }
})

export let {addCount, addCart} = cart.actions

export default configureStore({
  reducer: {
    user : user.reducer,
    stock : stock.reducer,
    cart : cart.reducer
  }
}) </code></pre>
<p>내가 Redux store에 담아 놓았던 <code>addCart</code>를 개발자 도구로 확인할 수 있는 모습!</p>
<hr>
<h1 id="🥱-lazy-import">🥱 lazy import</h1>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/78e9a867-8811-4490-8508-88b7bf7ca5dd/image.gif" alt=""></p>
<p>리액트 코드를 다 작성했다면 <code>npm run build</code>를 입력해서 그 코드들을 html, css, js 파일로 변환해야 한다. 리액트로 만드는 <code>Single Page Application</code>은 html, js 파일이 하나만 생성된다는 특징이 있는데, 그 안에는 지금까지 만든 파일들의 모든 내용이 들어 있어서 파일 사이즈가 크다. 따라서 리액트 사이트들은 첫 페이지 로딩 속도가 매우 느릴 수 있다는 단점이 있는데.. 😯</p>
<p>이러한 단점을 보완하려면 js 파일을 잘게 쪼개면 된다. 첫 페이지 로딩 시 굳이 필요 없는 컴포넌트들을 다른 js 파일로 쪼개 놓고, 필요 시에만 로딩할 수 있도록 한다면 첫 페이지의 로딩 속도를 향상시킬 수 있다. 이때 사용하는 것이 바로 <code>lazy import</code>이다.</p>
<h2 id="사용하기-2">사용하기</h2>
<p>기존 진행 중인 쇼핑몰 프로젝트로 예시를 들어 보려고 한다.</p>
<h4 id="appjs의-일부">App.js의 일부</h4>
<pre><code class="language-js">...
import Detail from &#39;./routes/Detail.js&#39;
import Cart from &#39;./routes/Cart.js&#39;
...</code></pre>
<p>기존 이렇게 import 되어 있던 코드들. Detail은 사용자가 상세 페이지로 이동했을 때 필요한 컴포넌트고, Cart는 해당 상품을 장바구니에 넣었을 때 필요한 컴포넌트다. 따라서 첫 페이지 로딩 시에는 필요가 없다.</p>
<pre><code class="language-js">...
import { lazy } from &#39;react&#39;

const Detail = lazy( () =&gt; import(&#39;./routes/Detail.js&#39;) )
const Cart = lazy( () =&gt; import(&#39;./routes/Cart.js&#39;) )
...</code></pre>
<p>이렇게 <code>lazy</code> 문법을 사용하면 &quot;Detail, Cart 컴포넌트가 필요해지면 import 해 주세요!&quot;라는 뜻이 된다.</p>
<h3 id="만약-컴포넌트-로드까지-지연이-발생하면">만약 컴포넌트 로드까지 지연이 발생하면?</h3>
<p>lazy를 사용하면 당연히 Detail, Cart 컴포넌트 로드까지 지연 시간이 발생할 수 있다. 이럴 때는 <code>Suspense</code>를 사용하면 된다.</p>
<pre><code class="language-js">...
import { Suspense } from &#39;react&#39;;
...
      &lt;Suspense fallback={&lt;div&gt;로딩 중..💭&lt;/div&gt;}&gt;
        &lt;Routes&gt;
            ...
        &lt;/Routes&gt;
      &lt;/Suspense&gt;
...</code></pre>
<p>이렇게 작성해 주면 lazy로 쪼개 놓은 컴포넌트가 로드 될 때 로딩 화면을 보여 줄 수 있다! 😎</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[쇼핑몰 - React Query로 사용자 이름 조회]]></title>
            <link>https://velog.io/@suhyun_zip/%EC%87%BC%ED%95%91%EB%AA%B0-React-Query%EB%A1%9C-%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%9D%B4%EB%A6%84-%EC%A1%B0%ED%9A%8C</link>
            <guid>https://velog.io/@suhyun_zip/%EC%87%BC%ED%95%91%EB%AA%B0-React-Query%EB%A1%9C-%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%9D%B4%EB%A6%84-%EC%A1%B0%ED%9A%8C</guid>
            <pubDate>Tue, 23 Jan 2024 03:54:15 GMT</pubDate>
            <description><![CDATA[<h1 id="오늘의-목표">오늘의 목표</h1>
<blockquote>
<p>React Query란 무엇인지 알아보고, 이를 사용하여 사용자 이름 조회하기</p>
</blockquote>
<hr>
<h1 id="💡-react-query란">💡 React Query란?</h1>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/d33587f5-d362-4bc1-b455-7f06e9dc10a3/image.png" alt=""></p>
<p>리액트 쿼리란 React Application에서 서버 상태를 불러 오고, 캐싱하며, 지속적으로 동기화하고 업데이트하는 작업을 도와주는 라이브러리이다.</p>
<h4 id="고민-네-가지-🤔">고민 네 가지 🤔</h4>
<blockquote>
<p>💭 ajax 성공 또는 실패 시 html을 보여 주려면...
💭 몇 초마다 자동으로 ajax를 요청하려면...
💭 실패 시 몇 초 후 요청을 재시도하려면...
💭 prefetch를 하려면...</p>
</blockquote>
<p>리액트 쿼리는 위 기능들을 쉽게 구현할 수 있도록 도와준다. 간단히 말하면 실시간 데이터를 계속 가져와 사용해야 하는 사이트 개발 시 유용하다.</p>
<h2 id="장점">장점</h2>
<ul>
<li>성공/실패/로딩 중 여부를 쉽게 파악할 수 있음<pre><code class="language-js">변수명.data // 성공 시 true 반환
변수명.isLoading // 로딩 중일 시 true 반환
변수명.error // 실패 시 true 반환</code></pre>
</li>
<li>틈만 나면 자동으로 refetch를 해 줌</li>
<li>실패 시 자동으로 재시도를 해 줌</li>
<li>ajax로 가져온 결과는 state 공유가 필요 없음</li>
<li>ajax 결과를 캐싱해 줌</li>
</ul>
<h2 id="설치하기">설치하기</h2>
<pre><code>npm install react-query</code></pre><p>위 코드를 터미널에 입력하여 설치한다.</p>
<h2 id="세팅하기">세팅하기</h2>
<h4 id="indexjs">index.js</h4>
<pre><code class="language-js">...
import { QueryClient, QueryClientProvider } from &#39;react-query&#39;;

const queryClient = new QueryClient();

...

root.render(
  &lt;QueryClientProvider client={queryClient}&gt;
    ...
  &lt;/QueryClientProvider&gt;
);</code></pre>
<p><code>index.js</code>에 위와 같이 작성해 주면 세팅 끝!</p>
<h2 id="예시로-사용-방법-알기">예시로 사용 방법 알기</h2>
<p>이제 리액트 쿼리를 사용해서 진행 중인 쇼핑몰 프로젝트에서 사용자의 이름을 조회해 볼 것이다.</p>
<h3 id="appjs">App.js</h3>
<pre><code class="language-js">...
import { useQuery } from &#39;react-query&#39;;
...

function App() {

  ...

  let result = useQuery(&#39;작명&#39;, ()=&gt;{
    return axios.get(&#39;https://codingapple1.github.io/userdata.json&#39;).then((a)=&gt;{
      console.log(&#39;요청됨&#39;)
      return a.data
    })
  })
  ...


  return (
    &lt;div className=&quot;App&quot;&gt;

    ...
          &lt;Nav className=&#39;ms-auto&#39;&gt;
            { result.isLoading &amp;&amp; &#39;로딩 중..💭&#39; } {/* 로딩 시 출력 */}
            { result.error &amp;&amp; &#39;에러 😯&#39; } {/* 실패 시 출력 */}
            { result.data &amp;&amp; (&#39;반가워요, &#39; + result.data.name + &#39; 님!&#39;) } {/* 성공 시 출력 */}
          &lt;/Nav&gt;
    ...</code></pre>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/ab10bb35-b2e1-4961-b0c4-64ac02c4e7d2/image.png" alt=""></p>
<p>사용자 이름 조회가 성공하여 해당 html이 출력되는 모습! 😎</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[쇼핑몰 - localStorage로 최근 본 상품 기능 구현]]></title>
            <link>https://velog.io/@suhyun_zip/%EC%87%BC%ED%95%91%EB%AA%B0-localStorage%EB%A1%9C-%EC%B5%9C%EA%B7%BC-%EB%B3%B8-%EC%83%81%ED%92%88-%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84</link>
            <guid>https://velog.io/@suhyun_zip/%EC%87%BC%ED%95%91%EB%AA%B0-localStorage%EB%A1%9C-%EC%B5%9C%EA%B7%BC-%EB%B3%B8-%EC%83%81%ED%92%88-%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84</guid>
            <pubDate>Mon, 22 Jan 2024 07:47:54 GMT</pubDate>
            <description><![CDATA[<h1 id="🌈-오늘의-목표">🌈 오늘의 목표</h1>
<blockquote>
<p><code>localStorage</code>에 대해 알고, 이를 사용하여 최근 본 상품 기능 구현하기!</p>
</blockquote>
<hr>
<h1 id="🌵-web-storage">🌵 Web Storage</h1>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/c5480a46-de6c-45a0-b4d1-40459cc20569/image.png" alt=""><code>이미지 출처💬 LoginRadius</code></p>
<br>
웹 스토리지란 쿠키와 비슷하게 해당 도메인과 관련된 특정 데이터를 서버가 아니라 클라이언트 쪽에 저장할 수 있도록 하는 기능이다. 웹 스토리지는 쿠키와 유사하지만 몇 가지 차이점을 가지고 있다.

<h2 id="특징">특징</h2>
<ul>
<li>key-value의 쌍 형태로 데이터를 저장함</li>
<li>쿠키와 달리 서버에 전송되지 않아 서버에 부담이 가지 않음</li>
<li>쿠키와 달리 필요할 때만 꺼내 쓰기 때문에 자동 전송의 위험이 없음</li>
<li>도매인 단위로 접근이 제한됨</li>
<li>쿠키는 4KB까지 데이터를 저장할 수 있지만, 웹 스토리지는 5KB까지 저장 가능함</li>
<li>유효 기간이 존재하지 않음</li>
<li>String 데이터 타입만 지원함</li>
</ul>
<h2 id="종류">종류</h2>
<blockquote>
<h3 id="local-storage">Local Storage</h3>
<p><code>window.localStorage</code> 객체로 브라우저를 종료해도 유지되는 데이터
(브라우저를 정리해야만 삭제됨)</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/ce254750-2568-4d3c-a9f7-66a2bfc7105d/image.png" alt=""></p>
<p>개발자 도구의 <code>Application-Local Storage</code> 탭에서 확인할 수 있다.</p>
<blockquote>
<h3 id="session-storage">Session Storage</h3>
<p><code>window.sessionStorage</code> 객체로 탭/윈도우를 닫기 전까지 유지되는 데이터</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/8d6bfb79-bd98-4402-a22c-1eabb5f43c8b/image.png" alt=""></p>
<p>개발자 도구의 <code>Application-Session Storage</code> 탭에서 확인할 수 있다.</p>
<h2 id="사용-방법">사용 방법</h2>
<h3 id="데이터-저장">데이터 저장</h3>
<pre><code class="language-js">localStorage.setItem(&#39;이름&#39;,&#39;값&#39;) // Local Storage
sessionStorage.setItem(&#39;이름&#39;,&#39;값&#39;) // Session Storage</code></pre>
<h4 id="만약-object-자료를-저장하고-싶다면">만약 object 자료를 저장하고 싶다면?</h4>
<pre><code class="language-js">let obj = { 이름 : &#39;값&#39;};
localStorage.setItem(&#39;data&#39;, JSON.stringify(obj));</code></pre>
<p>이럴 때는 <code>JSON</code>을 사용하면 된다. 🙂</p>
<h3 id="데이터-출력">데이터 출력</h3>
<pre><code class="language-js">localStorage.getItem(&#39;이름&#39;) // Local Storage
sessionStorage.getItem(&#39;이름&#39;) // Session Storage</code></pre>
<h4 id="object-자료-출력도-json을-쓰자-👍">object 자료 출력도 JSON을 쓰자 👍</h4>
<pre><code class="language-js">JSON.parse(localStorage.getItem(&#39;data&#39;))</code></pre>
<h3 id="데이터-삭제">데이터 삭제</h3>
<pre><code class="language-js">localStorage.removeItem(&#39;이름&#39;) // Local Storage
sessionStorage.removeItem(&#39;이름&#39;) // Session Storage</code></pre>
<h3 id="데이터-수정">데이터 수정</h3>
<p>데이터를 수정하는 문법은 따로 없다. 따라서 데이터를 꺼낸 뒤 수정하고 다시 저장하면 된다.</p>
<p>문법까지 알았으니 이제 본격적으로 기능을 구현해 보자!</p>
<hr>
<h1 id="👏-최근-본-상품-ui-만들기">👏 최근 본 상품 UI 만들기</h1>
<h2 id="appjs">App.js</h2>
<pre><code class="language-js">...
  useEffect(()=&gt;{
    let arr = [];

    /* 아직 본 상품이 없을 경우에만 배열 생성 (home으로 이동 시 배열 초기화 방지) */
    if(arr.length === 0){
      localStorage.setItem(&#39;watched&#39;, JSON.stringify(arr)) /* array 생성 */
    }
  },[])
...</code></pre>
<h2 id="detailjs">Detail.js</h2>
<pre><code class="language-js">...
    /* 페이지에 접속 시 최근 본 상품 추가하기 */
    useEffect(()=&gt;{
        let watchedList = JSON.parse(localStorage.getItem(&#39;watched&#39;));
        watchedList.push(id);

          /* Array를 Set으로 바꾸기 (중복 방지) */
        watchedList = new Set(watchedList); 
        /* Set을 Array로 바꾸기 */
        watchedList = Array.from(watchedList); 

          /* 상품이 추가된 배열을 다시 localStorage에 추가하기 */
        localStorage.setItem(&#39;watched&#39;, JSON.stringify(watchedList));
    },[])
...

    return (

      ...
            &lt;div&gt;
                &lt;h5&gt;최근 본 항목&lt;/h5&gt;
                &lt;Row&gt;
                    {
                        watchedList.map((id, i)=&gt;{
                            let shoe = shoes[id];
                            return(
                                &lt;div key={i}&gt;
                                    &lt;Col&gt;
                                        &lt;Link to={&#39;/detail/&#39;+shoe.id}&gt;
                                            {shoe.title}
                                        &lt;/Link&gt;
                                    &lt;/Col&gt;
                                &lt;/div&gt;
                            )
                        })
                    }
                &lt;/Row&gt;
            &lt;/div&gt;
    ...</code></pre>
<p>이제 상품 상세 페이지로 이동할 때마다 조회한 상품의 id가 localStorage에 저장된다. 개발자 도구로 확인해 보자.</p>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/b822f333-25db-4f21-90c1-0be9fcfc2d0f/image.png" alt=""></p>
<p>정말로 상품 id가 추가되어 있는 모습!</p>
<p><code>set</code>으로 중복을 방지해 주었기 때문에 같은 상품을 여러 번 조회하여도 값이 하나만 저장된다.</p>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/e66a140e-5ee3-4730-8ced-3d3ed8d70ff3/image.png" alt=""></p>
<p>저장된 상품 id로 최근 본 상품의 이름을 출력하고, 이름 클릭 시 해당하는 상품의 상세 페이지로 이동할 수 있게 링크도 걸어 보았다. (css는 생략.. 😅)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[리액트에서 자주 쓰는 if문 알기]]></title>
            <link>https://velog.io/@suhyun_zip/%EB%A6%AC%EC%95%A1%ED%8A%B8%EC%97%90%EC%84%9C-%EC%9E%90%EC%A3%BC-%EC%93%B0%EB%8A%94-if%EB%AC%B8-%EC%95%8C%EA%B8%B0</link>
            <guid>https://velog.io/@suhyun_zip/%EB%A6%AC%EC%95%A1%ED%8A%B8%EC%97%90%EC%84%9C-%EC%9E%90%EC%A3%BC-%EC%93%B0%EB%8A%94-if%EB%AC%B8-%EC%95%8C%EA%B8%B0</guid>
            <pubDate>Mon, 22 Jan 2024 05:47:56 GMT</pubDate>
            <description><![CDATA[<h1 id="🧙♂️-들어가며">🧙‍♂️ 들어가며</h1>
<p>이번 포스팅에서는 리액트에서 자주 사용하는 if문 작성 패턴 5개를 정리해 보았다.</p>
<hr>
<h1 id="🔮-자주-쓰는-if문">🔮 자주 쓰는 if문</h1>
<h2 id="컴포넌트-안에서-쓰는-if-else">컴포넌트 안에서 쓰는 if-else</h2>
<pre><code class="language-js">function Component() {
  if ( true ) {
    return &lt;p&gt;true일 때의 HTML&lt;/p&gt;;
  } else {
    return null;
  }
}</code></pre>
<h4 id="else-생략-가능">else 생략 가능</h4>
<pre><code class="language-js">function Component() {
  if ( true ) {
    return &lt;p&gt;true일 때의 HTML&lt;/p&gt;;
  } 
  return null;
} </code></pre>
<p>else와 중괄호를 하나 없애도 위 코드와 같은 기능을 한다. JS function 안에서는 <code>return</code>이라는 키워드를 만나면 return 아래에 있는 코드는 더 이상 실행되지 않기 때문이다.</p>
<h2 id="jsx-안에서-쓰는-삼항-연산자">JSX 안에서 쓰는 삼항 연산자</h2>
<pre><code class="language-js">function Component() {
  return (
    &lt;div&gt;
      {
        1 === 1
        ? &lt;p&gt;true일 때의 HTML&lt;/p&gt;
        : null
      }
    &lt;/div&gt;
  )
} </code></pre>
<p>기존 삼항 연산자와 동일하게 <code>조건문 ? 조건문 true 시 실행할 코드 : false 시 실행할 코드</code> 형식으로 작성하면 된다.</p>
<h2 id="연산자로-if-역할-대신하기">&amp;&amp; 연산자로 if 역할 대신하기</h2>
<pre><code class="language-js">function Component() {
  return (
    &lt;div&gt;
      {
        1 === 1
        ? &lt;p&gt;true일 때의 HTML&lt;/p&gt;
        : null
      }
    &lt;/div&gt;
  )
} 

function Component() {
  return (
    &lt;div&gt;
      { 1 === 1 &amp;&amp; &lt;p&gt;true일 때의 HTML&lt;/p&gt; }
    &lt;/div&gt;
  )
}</code></pre>
<p>위 코드 두 개는 동일한 역할을 한다. 왼쪽 조건식이 true이면 오른쪽 JSX가 그 자리에 남고, 왼쪽 조건식이 false면 false가 남는다.</p>
<p>JS는 <code>&amp;&amp;</code>로 연결된 값들 중 처음 등장하는 false 값을 찾아주고, 만약 그게 아니라면 마지막 값을 남겨 주기 때문!</p>
<h2 id="switch-case-조건문">switch-case 조건문</h2>
<pre><code class="language-js">function Component2(){
  var user = &#39;seller&#39;;
  switch (user){
    case &#39;seller&#39; :
      return &lt;h4&gt;판매자 로그인&lt;/h4&gt;
    case &#39;customer&#39; :
      return &lt;h4&gt;구매자 로그인&lt;/h4&gt;
    default : 
      return &lt;h4&gt;일반 로그인&lt;/h4&gt;
  }
}</code></pre>
<p>기존 <code>switch문</code> 문법과 동일하게 작성해 주면 된다. <code>default :</code>는 맨 마지막에 쓰는 else문과 동일하다고 생각하면 쉽다. if문을 연달아 쓸 때 코드가 짧아진다는 장점이 있지만, 조건식 란에서 변수 하나만 검사할 수 있다는 것이 단점이다. 😯</p>
<h2 id="objectarray-자료형-응용">object/array 자료형 응용</h2>
<p>만약 경우에 따라서 다른 html 태그들을 보여 주고 싶을 때는 어떻게 코드를 작성하면 좋을까? 이럴 때는 JS의 <code>object/array</code> 자료형을 사용하면 편리하다.</p>
<pre><code class="language-js">function Component() {
  var state = &#39;info&#39;;
  return (
    &lt;div&gt;
      {
        { 
           info : &lt;p&gt;상품 정보&lt;/p&gt;,
           shipping : &lt;p&gt;배송 관련&lt;/p&gt;,
           refund : &lt;p&gt;환불 약관&lt;/p&gt;
        }[state]
      }

    &lt;/div&gt;
  )
}

// 또는

var tabUI = { 
  info : &lt;p&gt;상품 정보&lt;/p&gt;,
  shipping : &lt;p&gt;배송 관련&lt;/p&gt;,
  refund : &lt;p&gt;환불 약관&lt;/p&gt;
}

function Component() {
  var state = &#39;info&#39;;
  return (
    &lt;div&gt;
      {
        tabUI[state]
      }
    &lt;/div&gt;
  )
} </code></pre>
<p>이렇게 작성하면 state가 &#39;info&#39;일 때는 info 항목에 저장된 <code>&lt;p&gt;</code> 태그가 보여지고, state가 &#39;refund&#39;라면 refund 항목에 저장된 <code>&lt;p&gt;</code> 태그가 보여질 것이다.</p>
<p>이런 식으로 간단하고 직관적인 if문을 작성할 수 있다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[쇼핑몰 - Redux로 장바구니 만들기]]></title>
            <link>https://velog.io/@suhyun_zip/%EC%87%BC%ED%95%91%EB%AA%B0-Redux%EB%A1%9C-%EC%9E%A5%EB%B0%94%EA%B5%AC%EB%8B%88-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@suhyun_zip/%EC%87%BC%ED%95%91%EB%AA%B0-Redux%EB%A1%9C-%EC%9E%A5%EB%B0%94%EA%B5%AC%EB%8B%88-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Sat, 20 Jan 2024 07:20:02 GMT</pubDate>
            <description><![CDATA[<h1 id="💎-오늘의-목표">💎 오늘의 목표</h1>
<blockquote>
<p><strong>Redux</strong>를 사용하여 장바구니 기능 구현하기 🛒</p>
</blockquote>
<hr>
<h1 id="😯-redux란">😯 Redux란?</h1>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/624ca9f2-6561-48db-b4de-9b9595c37131/image.webp" alt=""></p>
<p><code>Redux</code>는 상태 관리를 도와주는 라이브러리이다. 리액트에서 컴포넌트가 많아지면 상위에서 하위로 데이터를 보내 줄 때 수많은 props를 거쳐야 하는데, 이렇게 되면 상태 관리가 복잡해지게 된다. (이것이 바로 Props drilling)</p>
<p>이러한 문제점을 해결하기 위해 사용하는 것이 바로 Redux이다. 리덕스는 전역으로 상태를 관리해 주는 저장소를 제공해 준다. 따라서 컴포넌트들이 props 없이 state 공유가 가능해진다.</p>
<h2 id="설치하기">설치하기</h2>
<pre><code>npm install @reduxjs/toolkit react-redux</code></pre><p>터미널에 위 코드를 입력하여 Redux Toolkit을 설치해 준다.</p>
<hr>
<h2 id="세팅하기">세팅하기</h2>
<p>먼저 src 폴더에 <code>store.js</code> 파일을 생성한 뒤 아래 코드를 작성한다.</p>
<h4 id="storejs">store.js</h4>
<pre><code class="language-js">import { configureStore } from &#39;@reduxjs/toolkit&#39;

export default configureStore({
  reducer: {

  }
}) </code></pre>
<p>그리고 <code>index.js</code>로 가서 아래 코드를 작성해 준다.</p>
<h4 id="indexjs">index.js</h4>
<pre><code class="language-js">...

import { Provider } from &#39;react-redux&#39;;
import store from &#39;./store.js&#39;;

const root = ReactDOM.createRoot(document.getElementById(&#39;root&#39;));
root.render(
  // &lt;React.StrictMode&gt;
  &lt;Provider store={store}&gt; // 이 부분이 추가됨!
    &lt;BrowserRouter&gt;
      &lt;App /&gt;
    &lt;/BrowserRouter&gt;
  &lt;/Provider&gt;
  // &lt;/React.StrictMode&gt;
);

reportWebVitals();
</code></pre>
<p>이렇게 하면 세팅까지 끝!</p>
<hr>
<h2 id="문법-알기">문법 알기</h2>
<h3 id="store에-state-보관하기">store에 state 보관하기</h3>
<p>이제 <code>store.js</code>, 즉 Redux store에 여러 개의 state들을 보관할 수가 있는데, state를 보관하는 문법은 아래와 같다.</p>
<pre><code class="language-js">import { configureStore, createSlice } from &#39;@reduxjs/toolkit&#39;

let 변수명 = createSlice({ /* state 하나를 slice라고 부름 */
    name : &#39;state이름&#39;,
    initialState : &#39;state값&#39;
})

export default configureStore({
  reducer: {
    작명 : state이름.reducer
  }
}) </code></pre>
<h3 id="store에-보관한-state-가져오기">store에 보관한 state 가져오기</h3>
<pre><code class="language-js">useSelector((state)=&gt;{return state})</code></pre>
<p>이렇게 작성하면 저장해 놓은 state를 꺼내 쓸 수 있다. 😀</p>
<h3 id="store의-state-변경하기">store의 state 변경하기</h3>
<blockquote>
<p><strong>Step1💡</strong> state 변경 함수 만들기</p>
</blockquote>
<h4 id="storejs-1">store.js</h4>
<pre><code class="language-js">let 변수명 = createSlice({ /* state 하나를 slice라고 부름 */
    name : &#39;state이름&#39;,
    initialState : &#39;state값&#39;
    reducers : {
        함수명(state){
            return 함수 실행 시 수행될 코드
        }
    }
})</code></pre>
<blockquote>
<p><strong>Step2💡</strong> export 하기</p>
</blockquote>
<h4 id="storejs-2">store.js</h4>
<pre><code class="language-js">export let { 함수명 } = 변수명.actions</code></pre>
<blockquote>
<p><strong>Step3💡</strong> <code>dispatch( state변경함수() )</code> 작성하기</p>
</blockquote>
<p>버튼 클릭 시 함수가 실행되는 예시로 설명해 보았다.</p>
<h4 id="cartjs">Cart.js</h4>
<pre><code class="language-js">import { useDispatch } from &#39;react-redux&#39;;
import { 함수명 } from &#39;./../store.js&#39;;

function 컴포넌트명(){
    let dispatch = useDispatch()

    return(
        &lt;button onClick={()=&gt;{
            dispatch(함수명())
        }}&gt;&lt;/button&gt;
    )
}

...
</code></pre>
<hr>
<h1 id="🛒-이제-진짜-장바구니-만들기">🛒 이제 진짜 장바구니 만들기</h1>
<h2 id="storejs-3">store.js</h2>
<pre><code class="language-js">import { configureStore, createSlice } from &#39;@reduxjs/toolkit&#39;
import user from &#39;./store/userSlice&#39;

let stock = createSlice({
    name : &#39;stock&#39;,
    initialState : [10,11,12]
})

let cart = createSlice({
    name : &#39;cart&#39;,
    initialState : [
        {id : 0, name : &#39;White and Black&#39;, count : 2},
        {id : 2, name : &#39;Grey Yordan&#39;, count : 1}
    ],
    reducers : {
        addCount(state, action){
            let i = state.findIndex((a)=&gt;{ return a.id == action.payload })
            state[i].count++
        },
        addCart(state, action){
            state.push(action.payload)
        }
    }
})

export let {addCount, addCart} = cart.actions

export default configureStore({
  reducer: {
    user : user.reducer,
    stock : stock.reducer,
    cart : cart.reducer
  }
}) </code></pre>
<h2 id="cartjs-1">Cart.js</h2>
<pre><code class="language-js">import { Table } from &#39;react-bootstrap&#39;
import { useDispatch, useSelector } from &#39;react-redux&#39;;
import { changeName, increase } from &#39;./../store/userSlice.js&#39;;
import store from &#39;../store.js&#39;;
import { addCount } from &#39;../store.js&#39;;

function Cart(){

    let state = useSelector((state)=&gt;{return state})
            /* Redux store에 있는 state 가져다 씀 */
    let cart = state.cart
    let dispatch = useDispatch()

    return(
        &lt;&gt;
            &lt;h6&gt;{state.user.age}살 {state.user.name} 님의 장바구니&lt;/h6&gt;
            &lt;button onClick={()=&gt;{
                dispatch(increase(10))
            }}&gt;이름 변경&lt;/button&gt;
            &lt;button onClick={()=&gt;{
                dispatch(changeName())
            }}&gt;나이 변경&lt;/button&gt;

            &lt;Table&gt;
            &lt;thead&gt;
                &lt;tr&gt;
                    &lt;th&gt;#&lt;/th&gt;
                    &lt;th&gt;상품명&lt;/th&gt;
                    &lt;th&gt;수량&lt;/th&gt;
                    &lt;th&gt;변경하기&lt;/th&gt;
                &lt;/tr&gt;
            &lt;/thead&gt;
            &lt;tbody&gt;
                {
                    cart.map((a,i)=&gt;
                        &lt;tr key={i}&gt;
                            &lt;td&gt;{cart[i].id}&lt;/td&gt;
                            &lt;td&gt;{cart[i].name}&lt;/td&gt;
                            &lt;td&gt;{cart[i].count}&lt;/td&gt;
                            &lt;td&gt;&lt;button onClick={()=&gt;{dispatch(addCount(cart[i].id))}}&gt;+&lt;/button&gt;&lt;/td&gt;
                        &lt;/tr&gt;
                    )
                }
            &lt;/tbody&gt;
            &lt;/Table&gt; 
        &lt;/&gt;
    )
}

export default Cart;</code></pre>
<h2 id="detailjs">Detail.js</h2>
<pre><code class="language-js">...

import { addCart } from &quot;./../store.js&quot;
import { useDispatch } from &quot;react-redux&quot;;
...

function Detail(props) {

  ...

    let dispatch = useDispatch()
    ...
                    &lt;button className=&quot;btn btn-danger&quot; onClick={()=&gt;{
                        dispatch(addCart({id:item.id, name:item.title, count: 1}))
                    }}&gt;주문하기&lt;/button&gt;
    ...
</code></pre>
<p>구현 화면은 아래와 같다.</p>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/76d04a40-4b56-4868-bc89-d2bbc42d2c86/image.png" alt=""></p>
<p>장바구니에서 이름, 나이, 수량을 간단하게 변경해 볼 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/894a5a94-2b66-4ce1-afe0-089d9d96623f/image.png" alt=""></p>
<p>또한 상세 페이지에서 &#39;주문하기&#39; 버튼 클릭 시 장바구니에 추가되는 기능 또한 구현해 보았다. 👍</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[쇼핑몰 - transition과 Automatic Batching]]></title>
            <link>https://velog.io/@suhyun_zip/%EC%87%BC%ED%95%91%EB%AA%B0-transition%EA%B3%BC-Automatic-Batching</link>
            <guid>https://velog.io/@suhyun_zip/%EC%87%BC%ED%95%91%EB%AA%B0-transition%EA%B3%BC-Automatic-Batching</guid>
            <pubDate>Fri, 19 Jan 2024 08:18:24 GMT</pubDate>
            <description><![CDATA[<h1 id="📚-오늘의-목표">📚 오늘의 목표</h1>
<p>transition으로 컴포넌트 전환 애니메이션을 주자! 😎</p>
<hr>
<h1 id="💬-전환-애니메이션-주는-방법">💬 전환 애니메이션 주는 방법</h1>
<blockquote>
<p><strong>Step1</strong> 💡 애니메이션 동작 전 className 만들기
<strong>Step2</strong> 💡 애니메이션 동작 후 className 만들기
<strong>Step3</strong> 💡 className에 transition 속성 추가하기
<strong>Step4</strong> 💡 원할 때 2번 className 부착하기</p>
</blockquote>
<hr>
<h1 id="👀-코드로-살펴보기">👀 코드로 살펴보기</h1>
<h2 id="appcss">App.css</h2>
<pre><code class="language-css">.start{
  opacity: 0;
}

.end{
  opacity: 1;
  transition: opacity 0.5s; /* opacity가 변경될 때 0.5초에 걸쳐서 변경 */
}
...</code></pre>
<h2 id="detailjs">Detail.js</h2>
<pre><code class="language-js">...
function TabContent({tab}){
    let [fade, setFade] = useState(&#39;&#39;)
    useEffect(()=&gt;{
        let a = setTimeout(()=&gt;{setFade(&#39;end&#39;)}, 100)
        return()=&gt;{
            clearTimeout(a)
            setFade(&#39;&#39;)
        }
    },[tab])

    return(
        &lt;div className={&#39;start &#39; + fade}&gt;
            { [&lt;div&gt;내용0&lt;/div&gt;, &lt;div&gt;내용1&lt;/div&gt;, &lt;div&gt;내용2&lt;/div&gt;][tab] }
        &lt;/div&gt;
    )
}
...</code></pre>
<h3 id="여기서-잠깐">여기서 잠깐!</h3>
<p><code>setTimeout</code>을 쓴 이유는 무엇일까?</p>
<p>바로 React 18부터 생긴 <code>Automatic Batching</code> 기능 때문이다. 이 기능은 내부 소스, 외부 소스 상관 없이 state 업데이트를 일괄 처리하여 렌더링이 한 번에 이루어질 수 있도록 해 준다. 따라서 내가 return 문에 <code>setFade(&#39;&#39;)</code>를 적었다고 해도, 렌더링 시 <code>setFade(&#39;&#39;)</code>와 <code>setFade(&#39;end&#39;)</code>가 동시에 이루어지기 때문에 fade 기능이 제대로 구현되지 않은 것이다.</p>
<p>따라서 시간 차이를 두기 위해 setTimeout 함수를 사용한 것이다!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[쇼핑몰 - 탭 UI 만들기]]></title>
            <link>https://velog.io/@suhyun_zip/%EC%87%BC%ED%95%91%EB%AA%B0-%ED%83%AD-UI-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@suhyun_zip/%EC%87%BC%ED%95%91%EB%AA%B0-%ED%83%AD-UI-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Fri, 19 Jan 2024 07:48:03 GMT</pubDate>
            <description><![CDATA[<h1 id="🧚♂️-오늘의-목표">🧚‍♂️ 오늘의 목표</h1>
<blockquote>
<p>쇼핑몰 프로젝트에 <strong>탭 UI</strong>를 만들어 보자!</p>
</blockquote>
<hr>
<h1 id="💫-탭-ui-만들어-보기">💫 탭 UI 만들어 보기</h1>
<p>탭 UI를 만들기 위해 <a href="https://react-bootstrap.netlify.app/docs/components/navs/">React Bootstrap - nav</a>에 들어가 원하는 레이아웃을 선택한 뒤 진행하였다.</p>
<h2 id="detailjs">Detail.js</h2>
<pre><code class="language-js">...

import { Nav } from &quot;react-bootstrap&quot;;

function Detail(props) {
    ...
    let [tab, setTab] = useState(0); /* 숫자에 따라 n번째 내용이 보이는 형태 */

    ...

    return (
    ...

            &lt;Nav variant=&quot;tabs&quot;  defaultActiveKey=&quot;link0&quot;&gt;
                &lt;Nav.Item&gt;
                &lt;Nav.Link eventKey=&quot;link0&quot; onClick={()=&gt;{setTab(0)}}&gt;버튼0&lt;/Nav.Link&gt;
                &lt;/Nav.Item&gt;
                &lt;Nav.Item&gt;
                &lt;Nav.Link eventKey=&quot;link1&quot; onClick={()=&gt;{setTab(1)}}&gt;버튼1&lt;/Nav.Link&gt;
                &lt;/Nav.Item&gt;
                &lt;Nav.Item&gt;
                &lt;Nav.Link eventKey=&quot;link2&quot; onClick={()=&gt;{setTab(2)}}&gt;버튼2&lt;/Nav.Link&gt;
                &lt;/Nav.Item&gt;
            &lt;/Nav&gt;

            &lt;TabContent tab={tab}/&gt;

        &lt;/div&gt;
    )
}

function TabContent({tab}){
    return(
        /* 첫 번째 방법 (if문 사용)*/
        // if(tab == 0){
        //     return &lt;div&gt;내용0&lt;/div&gt;
        // }
        // if(tab == 1){
        //     return &lt;div&gt;내용1&lt;/div&gt;
        // }
        // if(tab == 2){
        //     return &lt;div&gt;내용2&lt;/div&gt;
        // }

          /* 두 번째 방법 (배열 사용)*/
        [&lt;div&gt;내용0&lt;/div&gt;, &lt;div&gt;내용1&lt;/div&gt;, &lt;div&gt;내용2&lt;/div&gt;][tab]
    )
}

export default Detail;</code></pre>
<p>구현한 화면은 아래와 같다.</p>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/55840b52-656a-4263-ab6e-cdc749855809/image.png" alt=""></p>
<p>선택한 Tab에 따라 해당하는 내용이 보여진다. 🙂</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[쇼핑몰 - Axios로 더보기 버튼 만들기]]></title>
            <link>https://velog.io/@suhyun_zip/%EC%87%BC%ED%95%91%EB%AA%B0-Axios%EB%A1%9C-%EB%8D%94%EB%B3%B4%EA%B8%B0-%EB%B2%84%ED%8A%BC-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@suhyun_zip/%EC%87%BC%ED%95%91%EB%AA%B0-Axios%EB%A1%9C-%EB%8D%94%EB%B3%B4%EA%B8%B0-%EB%B2%84%ED%8A%BC-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Wed, 17 Jan 2024 07:40:55 GMT</pubDate>
            <description><![CDATA[<h1 id="😯-들어가며">😯 들어가며</h1>
<p>이번 포스팅에서는 리액트에서 외부 라이브러리인 <code>axios</code>로 Ajax를 요청하는 방법에 대해 알아보고, 이를 활용하여 상품 더보기 버튼을 만들어 볼 것이다. ajax에 대한 자세한 설명은 아래 글을 참고하자.</p>
<p>✨ <a href="https://velog.io/@suhyun_zip/Ajax-23.07.13">Ajax 자세히 알기</a> ✨</p>
<hr>
<h1 id="👀-axios란">👀 Axios란?</h1>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/25880156-534a-4270-ab4a-d6d9cf4df32d/image.webp" alt=""></p>
<blockquote>
<p>Axios는 브라우저, Node.js를 위한 Promise API를 활용하는 HTTP 비동기 통신 라이브러리이다.</p>
</blockquote>
<h2 id="설치-방법">설치 방법</h2>
<pre><code>npm install axios</code></pre><p>터미널에 위 코드를 작성하면 설치 끝이다. 이제 프로젝트 상단에 import 해 주자.</p>
<pre><code class="language-js">import axios from &#39;axios&#39;;</code></pre>
<h2 id="사용-방법">사용 방법</h2>
<h3 id="get">GET</h3>
<pre><code class="language-js">axios.get(&#39;url&#39;) // 요청 한 개

Promise.all([axios.get(&#39;url1&#39;), axios.get(&#39;url2&#39;)]).then(()=&gt;{}) // 동시에 ajax 요청 여러 개 할 때</code></pre>
<h3 id="post">POST</h3>
<pre><code class="language-js">axios.post(&#39;url&#39;, [전송할 데이터])</code></pre>
<hr>
<h1 id="🙆♀️-더보기-버튼-만들기">🙆‍♀️ 더보기 버튼 만들기</h1>
<h2 id="appjs">App.js</h2>
<pre><code class="language-js">...

import axios from &#39;axios&#39;;

function App() {

  let [shoes, setShoes] = useState(data);

  return (
    &lt;div className=&quot;App&quot;&gt;

    ...

      &lt;button onClick={()=&gt;{
        axios.get(&#39;https://codingapple1.github.io/shop/data2.json&#39;)
        .then((result)=&gt;{

          console.log(result.data)
          console.log(shoes) // 받아 오는 데이터의 형태가 어떤지 콘솔창에 출력해 보기

          let copy = [...shoes, ...result.data]; // 기존 배열과 새로 받아 온 데이터의 배열을 합침!
          setShoes(copy);
        })
      }}&gt;더보기&lt;/button&gt;

    &lt;/div&gt;
  );
}

...
</code></pre>
<p>구현 화면은 아래와 같다.</p>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/d8777533-65ea-4693-9b92-7c9e47f3af1f/image.png" alt=""></p>
<p>더보기 버튼 클릭 시 axios로 받아 온 새 신발들이 목록에 나타난다. 😎</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[쇼핑몰 - Lifecycle과 useEffect]]></title>
            <link>https://velog.io/@suhyun_zip/%EC%87%BC%ED%95%91%EB%AA%B0-Lifecycle%EA%B3%BC-useEffect</link>
            <guid>https://velog.io/@suhyun_zip/%EC%87%BC%ED%95%91%EB%AA%B0-Lifecycle%EA%B3%BC-useEffect</guid>
            <pubDate>Wed, 17 Jan 2024 06:31:08 GMT</pubDate>
            <description><![CDATA[<h1 id="🍄-들어가며">🍄 들어가며</h1>
<p>이번 포스팅에서는 리액트의 Lifecycle과 useEffect에 대해 알아보고, 이것을 활용하여 쇼핑몰 알림창을 구현해 볼 것이다.</p>
<hr>
<h1 id="🥑-lifecycle">🥑 Lifecycle</h1>
<p>먼저 <code>Lifecycle</code>이란 무엇일까?</p>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/6c093496-3ea6-4c6a-bde3-849a98c7936d/image.png" alt=""></p>
<p>컴포넌트는 생성이 될 수도 있고, 재렌더링(업데이트)이 될 수도 있으며, 필요가 없을 때는 삭제가 되기도 한다. 이처럼 컴포넌트는 생명 주기, 즉 Lifecycle을 가지고 있다. 이때 생성되는 것을 <code>mount</code>, 재렌더링 되는 것을 <code>update</code>, 제거되는 것을 <code>unmount</code>라고 한다. </p>
<h2 id="장점">장점</h2>
<p>이 Lifecycle을 알고 있으면 각각의 상황에서 원하는 기능을 개발하는 것이 수월해진다. 컴포넌트의 생명 주기에 따라 필요한 코드를 hook 해 주면 되기 때문이다.</p>
<p>또한 Lifecycle은 코드의 실행 시점을 조절할 수 있기 때문에 반복 연산, 서버에서 데이터를 가져오는 작업, 타이머 등 시간이 오래 걸리는 작업 수행 시 <code>useEffect</code>를 사용하면 조금이라도 html 렌더링 <strong>속도를 높일 수 있다</strong>는 장점이 있다! 🙂</p>
<h2 id="사용-방법">사용 방법</h2>
<pre><code class="language-js">import {useState, useEffect} from &#39;react&#39;;

function Detail(){

  useEffect(()=&gt;{
    // 컴포넌트 로드 &amp; 업데이트될 때마다 실행할 코드
  });

  return (생략)
}</code></pre>
<p>Lifecycle hook 기본 문법은 이와 같다. 컴포넌트가 로드 또는 업데이트 될 때마다 실행할 코드를 <code>useEffect</code>안에 작성해 준다. 그러면 html 렌더링 이후에 이 코드가 동작하게 된다.</p>
<p>useEffect에 넣을 수 있는 실행 조건들이 있는데. 아래에 간단히 정리해 보았다.</p>
<blockquote>
<h4 id="1-재렌더링마다-코드-실행">1. 재렌더링마다 코드 실행</h4>
</blockquote>
<pre><code class="language-js">useEffect(()=&gt;{실행할 코드})</code></pre>
<blockquote>
<h4 id="2-mount-시-1회-코드-실행">2. mount 시 1회 코드 실행</h4>
</blockquote>
<pre><code class="language-js">useEffect(()=&gt;{실행할 코드}, [])</code></pre>
<blockquote>
<h4 id="3-unmount-시-1회-코드-실행">3. unmount 시 1회 코드 실행</h4>
</blockquote>
<pre><code class="language-js">useEffect(()=&gt;{ 
  return ()=&gt;{
    실행할 코드
  }
}, [])</code></pre>
<blockquote>
<h4 id="4-useeffect-실행-전-실행">4. useEffect 실행 전 실행</h4>
</blockquote>
<pre><code class="language-js">useEffect(()=&gt;{ 
  return ()=&gt;{
    실행할 코드
  }
})</code></pre>
<blockquote>
<h4 id="5-특정-state-변경-시에만-실행">5. 특정 state 변경 시에만 실행</h4>
</blockquote>
<pre><code class="language-js">useEffect(()=&gt;{ 
  실행할 코드
}, [state1])</code></pre>
<p>이 개념들을 머릿속에 넣고 이제 프로젝트로 돌아가 보자!</p>
<hr>
<h1 id="🍇-코드로-살펴보기">🍇 코드로 살펴보기</h1>
<h2 id="detailjs">detail.js</h2>
<pre><code class="language-js">import { useEffect, useState } from &quot;react&quot;;
import { useParams } from &quot;react-router-dom&quot;;

function Detail(props) {

  ...

    let [alert, setAlert] = useState(true);

    useEffect(()=&gt;{
        let a = setTimeout(()=&gt;{setAlert(false)}, 2000)
        return ()=&gt;{
            clearTimeout(a)
        }
    },[])

    return (
        &lt;div className=&quot;container&quot;&gt;
            {
                alert == true ?
                &lt;div className=&quot;alert alert-warning&quot;&gt;
                        2초 이내 구매 시 할인!
                &lt;/div&gt;
                : null
            }

            ...

        &lt;/div&gt;
    )
}

export default Detail;</code></pre>
<p>구현 화면은 아래와 같다.</p>
<p><img src="https://velog.velcdn.com/images/suhyun_zip/post/70b71d96-5fc6-42c1-9707-e0b350a6027f/image.png" alt=""></p>
<p>처음 페이지에 접속하면 타이머 시간으로 설정해 놓은 2초 동안 알림창이 나타났다가 시간이 지나면 사라진다. 이처럼 Lifecycle과 useEffect를 활용하여 다양한 기능을 구현해 볼 수 있다. 👏</p>
]]></description>
        </item>
    </channel>
</rss>