<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>boyeon_jeong.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Sat, 07 Jun 2025 17:40:39 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>boyeon_jeong.log</title>
            <url>https://velog.velcdn.com/images/boyeon_jeong/profile/28832175-6449-4a50-a494-acc577671fa4/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. boyeon_jeong.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/boyeon_jeong" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[객체지향 디자인 패턴 – 상태 패턴 (with javascript)]]></title>
            <link>https://velog.io/@boyeon_jeong/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%EC%83%81%ED%83%9C-%ED%8C%A8%ED%84%B4-with-javascript</link>
            <guid>https://velog.io/@boyeon_jeong/%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%EC%83%81%ED%83%9C-%ED%8C%A8%ED%84%B4-with-javascript</guid>
            <pubDate>Sat, 07 Jun 2025 17:40:39 GMT</pubDate>
            <description><![CDATA[<h2 id="상태-패턴행위-패턴">상태 패턴(행위 패턴)</h2>
<p>행위 패턴(Behavioral Pattern) 중 하나로, 객체가 내부 상태(state) 변경에 따라 <strong>행동(behavior)과 상태 전이</strong>를 <code>각각의 상태 객체</code>에 위임하는 패턴입니다.</p>
<h3 id="등장-배경">등장 배경</h3>
<p>예를 들어 뽑기 기계를 개발할때, “동전 있음”, “동전 없음”, “상품 판매”, “상품 매진” 등의 <code>상태</code>는“동전 투입”, “동전 반환”, “손잡이 돌림”, “알맹이 내보냄” 등의 <code>행동</code> 에 따라 변경됩니다. </p>
<p>각각의 행동에 대한 함수를 구현해봅시다. 아래와 같은 고려사항이 있을 수 있습니다.</p>
<ol>
<li>현재 상태에 따라 행동을 다르게 구현한다.</li>
<li>행동을 모두 마친 후의 상태에 따라 다음 상태를 정한다.</li>
</ol>
<p>위와 같은 상황을 고려하여 구현하려면 행동 함수 내부의 많은 if-else가 필요합니다. 또한, 변경 사항이 발생(재고 보충, 특별 보너스 상품 지급 등등)하면 내부 코드를 전체적으로 고려하여 변경해야 합니다. (버그 발생 위험 높아짐)</p>
<p>이러한 문제를 해결하고자 각각의 상태에 대한 객체를 생성하고(<code>NoCoinState</code>, <code>HasCoinState</code>, <code>SoldState</code>, <code>SoldOutState</code>) 내부에서는 현재 상태에서 실행할 행동(<code>insertCoin()</code>, <code>ejectCoin() ...</code>)과 상태전이를 처리하도록 캡슐화 하여 각각의 객체에게 책임을 위임하는 패턴이 상태 패턴이 등장하였습니다. </p>
<blockquote>
<p>이렇게 각각의 객체에게 위임하게 되면 OCP를 준수하게 되어 확장이 용이하고, SRP를 준수하게 되어 테스트가 용이해집니다.  </p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/boyeon_jeong/post/c84c6daf-8deb-4b3c-8228-66ebc5fb19f9/image.png" alt=""></p>
<h3 id="핵심-아이디어">핵심 아이디어</h3>
<ol>
<li><code>State Context</code>의 뼈대를 만듭니다. <ol>
<li>아래에서 만들 <code>State Object</code>를 보유하고 상태 전의를 관리하는 <strong>상태의 주인</strong></li>
</ol>
</li>
<li><code>State Interface</code>를 생성합니다.<ol>
<li>모든 행동 메서드 포함</li>
</ol>
</li>
<li><code>State Interface</code>로 각각의 <code>State Object</code> 생성합니다.<ol>
<li>모든 행동 매서드 구현</li>
<li>State Context를 참조<ol>
<li>constructor parameter로 주입 받음</li>
<li>상태 내부로 의존성 주입(DI)</li>
<li>State Context 객체를 참조 = 상태 객체가 <strong>자신을 보유한</strong> 컨텍스트 객체를 <strong>조작할</strong> 수 있도록 주입된 Context 레퍼런스</li>
<li>상속, 구성 x</li>
</ol>
</li>
</ol>
</li>
<li><code>State Context</code>의 내부를 다시 구현합니다.<ol>
<li>각각의 상태를 생성 및 소유합니다. (new 사용, 구성 관계)</li>
<li>상태 전의를 위한 세터/케터를 구현합니다.</li>
<li>실제 외부에서 호출할 동작을 구현합니다.</li>
</ol>
</li>
<li>외부에서는 각각의 상태 객체를 바라볼 필요 없이, State Context만 바라봅니다.</li>
</ol>
<ul>
<li>Context → State<ul>
<li>구성 관계 - Composition</li>
<li>Context가 State객체를 생성하여 소유</li>
<li>Context가 제거되면 State들도 같이 소멸(생명주기를 책임짐)</li>
</ul>
</li>
<li>State → Context<ul>
<li>연관/참조  관계 - Association/Reference</li>
<li>State는 외부의 Context를 참조</li>
<li>State는 Context의 생명주기를 책임지지 x</li>
</ul>
</li>
<li>결국 서로 양방향으로 연관이 되어있지만 서로 다른 성격의 관계를 가집니다.</li>
</ul>
<pre><code>💡 구성 관계 vs 참조 관계

(1) 구성 관계
구성 관계는 소스 코드 내에서 new를 써서 내부에서 직접 인스턴스를 생성하고 그 객체의 생명주기를 자신이 책임집니다.
⇒ 부분(part)이 전체(whole)에 강하게 종속되어, 전체가 생성·파괴될 때 부분도 함께 생성·파괴되어야 할 때 사용

(2) 참조 관계
참조관계는 외부에서 만들어진 인스턴스를 단순히 constructor 파라미터로 받아와서 참조만 할당하여 사용만 할뿐, 생성/파괴 책임은 없습니다.
⇒ 한 객체가 다른 객체를 사용만 해야 할때 사용</code></pre><h3 id="실제-활용">실제 활용</h3>
<p><img src="https://velog.velcdn.com/images/boyeon_jeong/post/f819e856-51d0-413e-8d15-46f4a07902cc/image.png" alt=""></p>
<p>State interface</p>
<pre><code class="language-jsx">export interface State {
  insertCoin(): void;
  ejectCoin(): void;
  turnCrank(): void;
  dispense(): void;
}</code></pre>
<p>Context class</p>
<pre><code class="language-jsx">import { State } from &#39;./State&#39;;
import { NoCoinState } from &#39;./NoCoinState&#39;;
import { HasCoinState } from &#39;./HasCoinState&#39;;
import { SoldState } from &#39;./SoldState&#39;;
import { SoldOutState } from &#39;./SoldOutState&#39;;

export class GumballMachine {
  private noCoinState: State;
  private hasCoinState: State;
  private soldState:   State;
  private soldOutState: State;

  private state: State;
  private count: number;

  constructor(count: number) {
    this.count = count;
    this.noCoinState   = new NoCoinState(this);
    this.hasCoinState  = new HasCoinState(this);
    this.soldState     = new SoldState(this);
    this.soldOutState  = new SoldOutState(this);

    this.state = count &gt; 0 ? this.noCoinState : this.soldOutState;
  }

  // 상태 전이를 위한 세터/게터
  setState(state: State) {
    this.state = state;
  }
  getNoCoinState()   { return this.noCoinState; }
  getHasCoinState()  { return this.hasCoinState; }
  getSoldState()     { return this.soldState; }
  getSoldOutState()  { return this.soldOutState; }

  // 알맹이 재고 차감
  releaseBall(): void {
    if (this.count &gt; 0) {
      console.log(&#39;알맹이 나가는 중...&#39;);
      this.count--;
    }
  }

  // 외부에서 호출할 동작
  insertCoin()  { this.state.insertCoin();  }
  ejectCoin()   { this.state.ejectCoin();   }
  turnCrank()   {
    this.state.turnCrank();
    this.state.dispense();
  }

  getCount() { return this.count; }
}</code></pre>
<p>각각의 State Object</p>
<pre><code class="language-jsx">import { State } from &#39;./State&#39;;
import { GumballMachine } from &#39;./GumballMachine&#39;;

export class NoCoinState implements State {
  constructor(private machine: GumballMachine) {}

  insertCoin(): void {
    console.log(&#39;동전이 투입되었습니다.&#39;);
    this.machine.setState(this.machine.getHasCoinState());
  }
  ejectCoin(): void {
    console.log(&#39;동전이 없습니다. 반환할 수 없습니다.&#39;);
  }
  turnCrank(): void {
    console.log(&#39;먼저 동전을 넣어주세요.&#39;);
  }
  dispense(): void {
    console.log(&#39;알맹이를 받을 수 없습니다.&#39;);
  }
}

// HasCoinState.ts
import { State } from &#39;./State&#39;;
import { GumballMachine } from &#39;./GumballMachine&#39;;

export class HasCoinState implements State {
  constructor(private machine: GumballMachine) {}

  insertCoin(): void {
    console.log(&#39;이미 동전이 있습니다. 추가로 넣을 수 없습니다.&#39;);
  }
  ejectCoin(): void {
    console.log(&#39;동전을 반환합니다.&#39;);
    this.machine.setState(this.machine.getNoCoinState());
  }
  turnCrank(): void {
    console.log(&#39;손잡이를 돌리셨습니다...&#39;);
    this.machine.setState(this.machine.getSoldState());
  }
  dispense(): void {
    console.log(&#39;알맹이를 받을 수 없습니다.&#39;);
  }
}

// SoldState.ts
import { State } from &#39;./State&#39;;
import { GumballMachine } from &#39;./GumballMachine&#39;;

export class SoldState implements State {
  constructor(private machine: GumballMachine) {}

  insertCoin(): void {
    console.log(&#39;잠시만 기다려주세요. 알맹이가 나가는 중입니다.&#39;);
  }
  ejectCoin(): void {
    console.log(&#39;이미 손잡이를 돌리셨습니다. 반환 불가합니다.&#39;);
  }
  turnCrank(): void {
    console.log(&#39;손잡이는 한 번만 돌려주세요.&#39;);
  }
  dispense(): void {
    this.machine.releaseBall();
    if (this.machine.getCount() &gt; 0) {
      this.machine.setState(this.machine.getNoCoinState());
    } else {
      console.log(&#39;알맹이가 모두 소진되었습니다.&#39;);
      this.machine.setState(this.machine.getSoldOutState());
    }
  }
}

// SoldOutState.ts
import { State } from &#39;./State&#39;;
import { GumballMachine } from &#39;./GumballMachine&#39;;

export class SoldOutState implements State {
  constructor(private machine: GumballMachine) {}

  insertCoin(): void {
    console.log(&#39;더 이상 판매할 알맹이가 없습니다. 동전을 반환합니다.&#39;);
  }
  ejectCoin(): void {
    console.log(&#39;동전이 없습니다.&#39;);
  }
  turnCrank(): void {
    console.log(&#39;알맹이가 없어서 손잡이를 돌릴 수 없습니다.&#39;);
  }
  dispense(): void {
    console.log(&#39;알맹이가 없습니다.&#39;);
  }
}</code></pre>
<p>실제 사용하는 부분</p>
<pre><code class="language-jsx">// main.ts (테스트)
import { GumballMachine } from &#39;./GumballMachine&#39;;

const machine = new GumballMachine(2);

machine.insertCoin();
machine.turnCrank();
// 동전이 투입되었습니다.
// 손잡이를 돌리셨습니다...
// 알맹이 나가는 중...
// 상태가 NoCoinState로 전이

machine.insertCoin();
machine.ejectCoin();
// 동전이 투입되었습니다.
// 동전을 반환합니다.

machine.insertCoin();
machine.turnCrank();
machine.insertCoin();
machine.turnCrank();
// 알맹이 나가는 중...
// 알맹이가 모두 소진되었습니다.
// 더 이상 판매할 알맹이가 없습니다. 동전을 반환합니다.
// 알맹이가 없습니다.</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Next.js app router 점진적 migration]]></title>
            <link>https://velog.io/@boyeon_jeong/Next.js-app-router-%EC%A0%90%EC%A7%84%EC%A0%81-migration</link>
            <guid>https://velog.io/@boyeon_jeong/Next.js-app-router-%EC%A0%90%EC%A7%84%EC%A0%81-migration</guid>
            <pubDate>Fri, 15 Mar 2024 03:31:48 GMT</pubDate>
            <description><![CDATA[<p>app router는 내부적으로 pages router와 <strong>동시에 동작</strong>하도록 구현되어 있기 때문에 page router에서 app router로 <strong>점진적 migration</strong>을 할 수 있습니다. toy project 마이그레이션 경험을 바탕으로 마이그레이션 할는 방법에 대해 순서대로 정리해보았습니다.</p>
<h1 id="1-경로">1. 경로</h1>
<h2 id="app-폴더-생성">app 폴더 생성</h2>
<p>root에 app폴더를 생성합니다. </p>
<p>app/ 디렉토리에 있는 폴더들이 각각 라우팅 경로를 의미합니다. 각각의 폴더는 반드시 page.js 파일을 가지고 있어야 합니다.</p>
<pre><code>app/
  ├── page.js
  └── contact/
      └── page.js
  └── About/
      └── page.js</code></pre><h1 id="2-layout">2. layout</h1>
<h2 id="layout를-정의">layout를 정의</h2>
<p>layout를 정의해줍니다. layout 파일은 하위 모든 경로에 <strong>공통적인 UI</strong>를 제공해줍니다.</p>
<h2 id="rootlayout">RootLayout</h2>
<p>pages 폴더의 layout인 RootLayout component는 <strong>필수</strong>입니다. 이 컴포넌트에 기존 _document.js와 app.js의 내용을 migration 해줍니다.</p>
<h4 id="head">head</h4>
<p>기존 next/head를 새로운 built-in SEO support인 <a href="https://nextjs.org/docs/app/building-your-application/upgrading/app-router-migration#step-3-migrating-nexthead">Metadata</a>로 변경해줍니다. </p>
<pre><code class="language-jsx">//before
import Head from &#39;next/head&#39;

export default function Page() {
  return (
    &lt;&gt;
      &lt;Head&gt;
        &lt;title&gt;My page title&lt;/title&gt;
      &lt;/Head&gt;
    &lt;/&gt;
  )
}

//after
import { Metadata } from &#39;next&#39;

export const metadata: Metadata = {
  title: &#39;My Page Title&#39;,
}

export default function Page() {
  return &#39;...&#39;
}</code></pre>
<h1 id="data-fetching">Data fetching</h1>
<h2 id="getserversideprosgetstaticprops">getServerSidePros/getStaticProps</h2>
<p>기존 getServerSidePros와 getStaticProps는 fetch 함수로 대체해줍니다. </p>
<pre><code class="language-jsx">export default async function Page() {
  // Similar to `getStaticProps`.
  const staticData = await fetch(`https://...`, { cache: &#39;force-cache&#39; })

  // Similar to `getServerSideProps`.
  const dynamicData = await fetch(`https://...`, { cache: &#39;no-store&#39; })

  // Similar to `getStaticProps` with the `revalidate` option.
  const revalidatedData = await fetch(`https://...`, {
    next: { revalidate: 10 },
  })

  return &lt;div&gt;...&lt;/div&gt;
}</code></pre>
<h2 id="getstaticpaths">getStaticPaths</h2>
<p>그리고 getStaticPaths는 generateStaticParmas로 대체해줍니다.</p>
<pre><code class="language-jsx">//before
import PostLayout from &#39;@/components/post-layout&#39;

export async function getStaticPaths() {
  return {
    paths: [{ params: { id: &#39;1&#39; } }, { params: { id: &#39;2&#39; } }],
  }
}

export async function getStaticProps({ params }) {
  const res = await fetch(`https://.../posts/${params.id}`)
  const post = await res.json()

  return { props: { post } }
}

export default function Post({ post }) {
  return &lt;PostLayout post={post} /&gt;
}

//after
import PostLayout from &#39;@/components/post-layout&#39;

export async function generateStaticParams() {
  return [{ id: &#39;1&#39; }, { id: &#39;2&#39; }]
}

async function getPost(params) {
  const res = await fetch(`https://.../posts/${params.id}`)
  const post = await res.json()

  return post
}

export default async function Post({ params }) {
  const post = await getPost(params)

  return &lt;PostLayout post={post} /&gt;
}</code></pre>
<h1 id="error">Error</h1>
<h2 id="404js">404.js</h2>
<p>404.js는 not-found.js로 대체해줍니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[gatsby blog 개발 트러블 슈팅 : google icons 추가]]></title>
            <link>https://velog.io/@boyeon_jeong/gatsby-blog-%EA%B0%9C%EB%B0%9C-%ED%8A%B8%EB%9F%AC%EB%B8%94-%EC%8A%88%ED%8C%85-google-icons-%EC%B6%94%EA%B0%80</link>
            <guid>https://velog.io/@boyeon_jeong/gatsby-blog-%EA%B0%9C%EB%B0%9C-%ED%8A%B8%EB%9F%AC%EB%B8%94-%EC%8A%88%ED%8C%85-google-icons-%EC%B6%94%EA%B0%80</guid>
            <pubDate>Fri, 12 Jan 2024 07:00:37 GMT</pubDate>
            <description><![CDATA[<h2 id="gatsby-plugin-fontawesome-css-사용-안하기">gatsby-plugin-fontawesome-css 사용 안하기</h2>
<p>공식 홈페이지에서 추천하는것 처럼 보이는 방식은 version이 안 맞는다는 경고가 뜬다. 이 플러그인은 업데이트가 안되어 있어서 버전이 안맞는다는 경고가 뜨며 어떤 글에서는 해당 플러그인이 아이콘 크기 버그 해결만을 위한 플러그인인이라고 하니 사용하지 않기로 결정했다.</p>
<p><code>yarn add gatsby-plugin-fontawesome-css</code></p>
<pre><code>module.exports = {
  plugins: [`gatsby-plugin-fontawesome-css`],
}</code></pre><h2 id="react-fontawesome-패키지를-설치">react-fontawesome 패키지를 설치</h2>
<p>Gatsby는 React 기반이기 때문에 React 전용 react-fontawesome 패키지를 설치</p>
<pre><code>{
    &quot;dependencies&quot;: {
        &quot;@fortawesome/fontawesome-svg-core&quot;: &quot;^6.4.0&quot;,
        &quot;@fortawesome/free-brands-svg-icons&quot;: &quot;^6.4.0&quot;,
        &quot;@fortawesome/free-regular-svg-icons&quot;: &quot;^6.4.0&quot;,
        &quot;@fortawesome/free-solid-svg-icons&quot;: &quot;^6.4.0&quot;,
        &quot;@fortawesome/react-fontawesome&quot;: &quot;^0.2.0&quot;
    }
}</code></pre><h2 id="dynamic-방법의-문제점">dynamic 방법의 문제점</h2>
<p>폰트를 사용하는 방법은 여러개가 있는데, 나는 공통 컴포넌트를 만들어 사용할것이기 때문에 string으로 props를 넘겨서 사용할 수 있는 <a href="https://fontawesome.com/docs/web/use-with/react/add-icons#dynamic-icon-importing">dynamic 방법</a>을 사용했다.</p>
<p>이 방법은 <strong>Babel Macros</strong> 플러그인 설정을 통해 사용할 아이콘을 일일이 import할 필요가 없으면서도 사용한 아이콘만 번들에 포함하도록 자동으로 <strong>번들 최적화</strong>하는 방법이다. <code>이 방법은 공식 홈페이지에서도 추천하는 방법!</code></p>
<p>대신 몇가지 설정을 해주어야 한다.
<code>yarn add babel-plugin-macros</code></p>
<p><code>babel.config.js</code></p>
<pre><code>module.exports = function (api) {
     //여기에서 캐시 오류가 났기 때문에
    api.cache(true);
    return {
      plugins: [&#39;macros&#39;],
    }
}</code></pre><p><code>babel-plugin-macros.config.js</code></p>
<pre><code>module.exports = {
  &#39;fontawesome-svg-core&#39;: {
    &#39;license&#39;: &#39;free&#39;
  }
}</code></pre><p>그런데 여기서 <code>충격적인 오류</code>가 또 발생했다.</p>
<pre><code class="language-jsx">import { FontAwesomeIcon } from &#39;@fortawesome/react-fontawesome&#39;
import { icon } from &#39;@fortawesome/fontawesome-svg-core/import.macro&#39;
import { Color, FoundationProps } from &quot;../../models/types&quot;;
import { SizeProp } from &#39;@fortawesome/fontawesome-svg-core&#39;;
import colors from &#39;../../constants/colors&#39;;

export type IconProps = {
  name:
  | &quot;moon&quot; | &quot;bars&quot;
  size?: SizeProp
  color?: Color;
};

const Icon = ({
  className,
  name,
  size = &quot;4x&quot;,
  color = &quot;primary3&quot;,
}: IconProps &amp; FoundationProps) =&gt; {
  console.log(name)
  return (
    &lt;span className={className}&gt;
      &lt;FontAwesomeIcon icon={icon({ name, style: &#39;solid&#39; })} size={size} color={colors[color]} /&gt;
    &lt;/span &gt;
  );
};

export default Icon;</code></pre>
<p>@fortawesome/fontawesome-svg-core/import.macro는 icon 매크로를 호출할때, name 속성 지정할 수 있다. 내가 놓친 부분은 icon 매크로가 반환하는 <code>아이콘 정보는 build time에 고정</code>되게 된다는 것이였다.</p>
<p>내가 수행했던 방식은 <strong>build time이 아닌 runtime 중에만 동적으로 실제 name을 가져올 수 있었기 때문에, icon 매크로를 수행하는 build time에는 referenced icon을 알 수 있기 때문에 해당 오류가 발생했다.</strong> </p>
<p><code>Only string literals are supported for the name property (use a string here instead)</code></p>
<p><img src="https://velog.velcdn.com/images/boyeon_jeong/post/16e82c57-ea1b-4130-aeb3-64a5695f1011/image.png" alt=""></p>
<p>그래서 다시 다른 방법으로 고고뤼! </p>
<h2 id="두가지-해결-방법">두가지 해결 방법</h2>
<p>내가 생각한 해결방법은 아래 2가지 이다. </p>
<h3 id="1-각각-import-해주는-방식">1. 각각 import 해주는 방식</h3>
<pre><code class="language-jsx">import { FontAwesomeIcon } from &#39;@fortawesome/react-fontawesome&#39;;
import { faMoon, faBars } from &#39;@fortawesome/free-solid-svg-icons&#39;;

...

let selectedIcon = &#39;&#39;;
if(props.name === &quot;moon&quot;) selectedIcon= faMoon; 

...</code></pre>
<h3 id="2-그대로-babel-macro를-사용하면서-미리-icon-매크로로-name-지정하여-referenced-icon을-가져온-후-객체에-저장해놓는-방법">2. 그대로 babel macro를 사용하면서 미리 icon 매크로로 name 지정하여 referenced icon을 가져온 후 객체에 저장해놓는 방법</h3>
<pre><code class="language-jsx">import { FontAwesomeIcon } from &#39;@fortawesome/react-fontawesome&#39;
import { icon } from &#39;@fortawesome/fontawesome-svg-core/import.macro&#39;

const icons = {
  moon: icon({ name: &quot;moon&quot;, style: &#39;solid&#39; }),
  bars: icon({ name: &quot;bars&quot;, style: &#39;solid&#39; }),
}

...

const selectedIcon = props.name[name];

...</code></pre>
<h2 id="최종-나의-선택은">최종 나의 선택은!</h2>
<p>나는 추가로 여러가지의 아이콘을 가져올 예정이였기 때문에 하나하나 icon을 import 해주지 않으면서도 번들 최적화를 수행해주는 아래의 방식을 선택하였다.</p>
<pre><code class="language-jsx">import { FontAwesomeIcon } from &#39;@fortawesome/react-fontawesome&#39;
import { icon } from &#39;@fortawesome/fontawesome-svg-core/import.macro&#39;
import { Color, FoundationProps } from &quot;../../models/types&quot;;
import { SizeProp } from &#39;@fortawesome/fontawesome-svg-core&#39;;
import colors from &#39;../../constants/colors&#39;;

const icons = {
  moon: icon({ name: &quot;moon&quot;, style: &#39;solid&#39; }),
  bars: icon({ name: &quot;bars&quot;, style: &#39;solid&#39; }),
}

export type IconProps = {
  name:
  | &quot;moon&quot; | &quot;bars&quot;
  size?: SizeProp
  color?: Color;
};

const Icon = ({
  className,
  name,
  size = &quot;4x&quot;,
  color = &quot;primary3&quot;,
}: IconProps &amp; FoundationProps) =&gt; {
  return (
    &lt;span className={className}&gt;
      &lt;FontAwesomeIcon icon={icons[name]} size={size} color={colors[color]} /&gt;
    &lt;/span &gt;
  );
};

export default Icon;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Emotion 더 잘 사용하기!]]></title>
            <link>https://velog.io/@boyeon_jeong/Emotion-%EB%8D%94-%EC%9E%98-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@boyeon_jeong/Emotion-%EB%8D%94-%EC%9E%98-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 29 Dec 2023 22:33:55 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>📌 이 글은 emotion 공식문서의 Best Practices 참고하여 작성되었습니다.</p>
</blockquote>
<h2 id="스타일-재사용-하기">스타일 재사용 하기</h2>
<p>어떤 애플리케이션을 개발하는데 에러메세지 스타일이 여러 컴포넌트에서 반복적으로 사용되며, 해당 에러메세지는 폰트사이즈가 다른 여러버전이 존재한다고 가정해봅시다. </p>
<p>이러한 스타일을 재사용하기 위해 스타일을 공유하는 방법은 두가지가 있습니다.</p>
<h3 id="1-css-objects를-export-해주기">1. css objects를 export 해주기</h3>
<pre><code class="language-jsx">export const errorCss = css({
  color: &#39;red&#39;,
  fontWeight: &#39;bold&#39;
})

// Use arrays to compose styles
export const largeErrorCss = css([errorCss, { fontSize: &#39;3rem&#39; }])
export const mediumErrorCss = css([errorCss, { fontSize: &#39;2rem&#39; }])
export const smallErrorCss = css([errorCss, { fontSize: &#39;0.5rem&#39; }])</code></pre>
<pre><code class="language-jsx">import { errorCss, largeErrorCss, mediumErrorCss , smallErrorCss} from &#39;...&#39;

return (
    &lt;p css=[errorCss, {fontSize: &#39;5rem&#39;}]&gt;Failed!!!!!!!&lt;/p&gt;
    &lt;p css={largeErrorCss}&gt;Failed!!!&lt;/p&gt;
    &lt;p css={mediumErrorCss}&gt;Failed!!&lt;/p&gt;
    &lt;p css={smallErrorCss}&gt;Failed!&lt;/p&gt;
)</code></pre>
<h3 id="2-컴포넌트-재사용-해주기">2. 컴포넌트 재사용 해주기</h3>
<p>아래처럼 컴포넌트 자체를 재사용 해줄 경우 좀 더 반복적이 코드를 줄일 수 있습니다.</p>
<pre><code class="language-jsx">export function ErrorMessage({ className, children }) {
  return (
    &lt;p css={{ color: &#39;red&#39;, fontWeight: &#39;bold&#39; }} className={className}&gt;
      {children}
    &lt;/p&gt;
  )
}

export function LargeErrorMessage({ className, children }) {
  return (
    &lt;ErrorMessage css={{ fontSize: &#39;3rem&#39; }} className={className}&gt;
      {children}
    &lt;/ErrorMessage&gt;
  )
}</code></pre>
<blockquote>
<p>📌 css={}로 넘긴 css object는 하위컴포넌트의 <code>className</code>으로 넘어감</p>
</blockquote>
<pre><code class="language-jsx">import { ErrorMessage, LargeErrorMessage} from &#39;...&#39;

return (
    &lt;ErrorMessage css={{ fontSize: &#39;5rem&#39; }}&gt;Failed!!!!!!!&lt;/ErrorMessage&gt;
    &lt;LargeErrorMessage&gt;Failed!!!&lt;/LargeErrorMessage&gt;</code></pre>
<h2 id="동적으로-스타일-주기">동적으로 스타일 주기</h2>
<p>만약 다른 스타일은 동일하지만 backgroud-style만 달라지는 경우에 어떻게 구현할 수 있을까요? </p>
<p>만약 동적으로 스타일을 줄 수 없다면 아래와 같이 중복되는 코드를 계속해서 작성해야 할 것입니다.</p>
<pre><code class="language-jsx">&lt;style&gt;
  .css-1udhswa {
    border-radius: 50%;
    width: 40px;
    height: 40px;
    background-style: url(https://i.pravatar.cc/150?u=0);
  }

  .css-1cpwmbr {
    border-radius: 50%;
    width: 40px;
    height: 40px;
    background-style: url(https://i.pravatar.cc/150?u=1);
  }

  .css-am987o {
    border-radius: 50%;
    width: 40px;
    height: 40px;
    background-style: url(https://i.pravatar.cc/150?u=2);
  }
&lt;/style&gt;</code></pre>
<p>이를 style props를 사용한다면 쉽게 해결 할 수 있습니다. </p>
<ol>
<li><code>style</code> 코드에서는 <code>var(변수명)</code> 형태로 사용</li>
<li><code>element</code>에서는 <code>style={{&#39;변수명&#39; : 값}}</code> 으로 넘겨주면 됩니다.<pre><code class="language-jsx">.avatar {
border-radius: 50%;
width: 40px;
height: 40px;
background-style: var(--background-style);
}
</code></pre>
</li>
</ol>
<p>function Avatar({ imageUrl }) {
  return &lt;div className=&quot;avatar&quot; style={{ &#39;--background-style&#39;: imageUrl }} /&gt;
}</p>
<pre><code>&gt; 만약 typescript를 사용한다면 style={{ [&#39;--background-style&#39; as any]: imageUrl }} 이렇게 사용해주면 된다.



## Theming 사용하기
dark mode, light mode를 개발하는 방법중에 emotion의 ThemeProvider을 사용하는 방법이 있습니다.

#### 1. App top level에 ThemeProvider 추가해주기
```jsx
import { ThemeProvider } from &#39;@emotion/react&#39;

const theme = {
  colors: {
    primary: &#39;hotpink&#39;
  }
}

render(
  &lt;ThemeProvider theme={theme}&gt;
    &lt;App.js&gt;
  &lt;/ThemeProvider&gt;
)</code></pre><h4 id="2-1-usetheme-사용하기">2-1. useTheme 사용하기</h4>
<pre><code class="language-jsx">import {useTheme } from &#39;@emotion/react&#39;

const theme = {
  colors: {
    primary: &#39;hotpink&#39;
  }
}

function SomeText(props) {
  const theme = useTheme()
  return &lt;div css={{ color: theme.colors.primary }} {...props} /&gt;
}</code></pre>
<h4 id="2-2-css-에서-theme-활용해주기">2-2. css 에서 theme 활용해주기</h4>
<pre><code class="language-jsx">import {useTheme } from &#39;@emotion/react&#39;

const theme = {
  colors: {
    primary: &#39;hotpink&#39;
  }
}

function SomeText(props) {
  return &lt;div css={theme =&gt; ({ color: theme.colors.primary })}&gt;some other text&lt;/div&gt;
}</code></pre>
<blockquote>
<p>ThemeProvider는 Context API로 전체 테마를 바꾸는 것입니다. 그런데 이는 CSS 변수 접근 방식과 비교하면 아래의 두가지 단점이 존재합니다.</p>
</blockquote>
<ol>
<li>DX 하락: 코드가 복잡해짐</li>
<li>성능 하락: ThemeProvider 접근 방식을 사용하면 모든 구성 요소의 스타일을 업데이트해야 하며 브라우저는 해당 업데이트를 페인트함, 그러나 CSS 변수 접근 방식을 사용하면 스타일을 단일 구성 요소(본문)로 업데이트한 다음 브라우저에서 해당 업데이트를 페인트함</li>
</ol>
<blockquote>
<p><a href="https://frontdev.tistory.com/entry/%EB%8B%A4%ED%81%AC%EB%AA%A8%EB%93%9C%EB%A5%BC-%EC%9C%84%ED%95%B4%EC%84%9C-Context-API%EB%B3%B4%EB%8B%A4-CSS-%EB%B3%80%EC%88%98%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%98%EC%84%B8%EC%9A%94">참고</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[이진탐색 알고리즘]]></title>
            <link>https://velog.io/@boyeon_jeong/%EC%9D%B4%EC%A7%84%ED%83%90%EC%83%89-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98</link>
            <guid>https://velog.io/@boyeon_jeong/%EC%9D%B4%EC%A7%84%ED%83%90%EC%83%89-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98</guid>
            <pubDate>Wed, 20 Dec 2023 07:04:03 GMT</pubDate>
            <description><![CDATA[<h3 id="키워드">키워드</h3>
<ul>
<li>반씩 쪼개는 탐색</li>
<li><code>정렬</code> 된후에 탐색</li>
<li>1억이면 30번 정도 탐색하면 됨</li>
</ul>
<h3 id="시간-복잡도">시간 복잡도</h3>
<p>순차탐색이 O(N)이라면 이진탐색은 O(logN)이다.</p>
<h3 id="그래서-언제-이진탐색을-풀어야-할까">그래서 언제 이진탐색을 풀어야 할까?</h3>
<ul>
<li>만약 범위가 억이 넘어가는게 존재할 경우</li>
<li>데이터 정렬 뒤에 다수의 쿼리를 날려야 할 경우</li>
</ul>
<h3 id="풀기1--원하는게-arr에-있니">풀기1 : 원하는게 arr에 있니?</h3>
<pre><code class="language-javascript">const binarySearch = (arr, target, start, end) =&gt; {
    //탐색 실패(값 없음)
      if(start &gt; end) return -1;
      let mid = parseInt((start+end)/2);
      //만약 찾았다면 현재 index return
      if(arr[mid] === target) return mid;
      //start target mid end
      else if(arr[mid] &gt; target) return binarySearch(arr, target, start, mid-1);
      //start mid target end
      else if(arr[mid] &lt; target) return binarySearch(arr, target, mid+1, end);
}</code></pre>
<h3 id="풀기2-정렬된-배열에서-특정-원소의-개수-구하기">풀기2: 정렬된 배열에서 특정 원소의 개수 구하기</h3>
<p>추후에 세그먼트 트리와 같은 복잡한 알고리즘에서 활용됨</p>
<p>c나 python에서는 lowerBound(), upperBound()라는 함수를 사용해서 해결할 수 있지만, javascript는 직접 구현해야함</p>
<pre><code class="language-javascript">cont lowerBound = (arr, target, strat, end) =&gt; {
    while(start &lt; end){
        let mid = parseInt((start+end)/2);
          if(arr[mid] &gt;= target) end = mid;
          else start = mid + 1;
    }
      return end;
}

const upperBound = (arr,taret, start, end) =&gt; {
    while(start &lt; end){
        let mid = parseInt((start+end)/2);
          //start target arr[mid] end
          //start target mid mid mid mid arr[mid] end
          if(arr[mid] &gt; target) end = mid;
          //start arr[mid] target1 target2 end =&gt; start: target1
          //start target1 arr[mid] target2 end =&gt; start: target2
          else start = mid + 1;
    }

      return end;
}</code></pre>
<h3 id="풀기3-파라메트릭-서치">풀기3: 파라메트릭 서치</h3>
<p>다른 고급 알고리즘 문제랑도 연결됨</p>
<h4 id="단조증가함수">단조증가함수</h4>
<ul>
<li>단조 증가 함수란 <code>x &lt;= y 이면 f(x) &lt;=f(y)</code>인 함수를 의미합니다.</li>
<li>이진탐색 함수는 <code>정렬된 배열</code>을 다루기 때문에 단조 증가 함수입니다.</li>
</ul>
<h4 id="파라메트릭-서치">파라메트릭 서치</h4>
<ul>
<li>최적화 문제를 여러개의 결정문제로 바꾸어 해결하는 기법<blockquote>
<p>최적화 문제: 최댓값, 최솟값을 찾는것 저럼 특정한 조건을 만족하는 알맞은 값을 빠르게 찾는 문제</p>
</blockquote>
</li>
</ul>
<h4 id="실제-문제">실제 문제</h4>
<ul>
<li>2512</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[React query 완전 정복, Custom hook으로 관심사 분리하여 사용하기 ]]></title>
            <link>https://velog.io/@boyeon_jeong/React-query-%EC%99%84%EC%A0%84-%EC%A0%95%EB%B3%B5-Custom-hook%EC%9C%BC%EB%A1%9C-%EA%B4%80%EC%8B%AC%EC%82%AC-%EB%B6%84%EB%A6%AC%ED%95%98%EC%97%AC-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@boyeon_jeong/React-query-%EC%99%84%EC%A0%84-%EC%A0%95%EB%B3%B5-Custom-hook%EC%9C%BC%EB%A1%9C-%EA%B4%80%EC%8B%AC%EC%82%AC-%EB%B6%84%EB%A6%AC%ED%95%98%EC%97%AC-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 15 Dec 2023 02:57:11 GMT</pubDate>
            <description><![CDATA[<h2 id="react-query-완전-정복">React query 완전 정복</h2>
<p><a href="https://betterprogramming.pub/7-tips-for-using-react-query-in-large-projects-22ccc49d61c2">본문</a></p>
<h3 id="1-react-query-key-enum으로-관리하기">1. React query key Enum으로 관리하기</h3>
<pre><code class="language-jsx">// hooks/items.hooks.ts
import { useQuery } from &quot;react-query&quot;;

export enum ServerStateKeysEnum {
  Items = &#39;items&#39;
} </code></pre>
<h3 id="2-react-query-cutom-hook으로-감싸기">2. React query cutom hook으로 감싸기</h3>
<p>데이터 계층과 프레젠테이션 계층을 분리하여 관심사를 명확히 구분할 수 있습니다. 
이 말은 즉, API fetch function을 React 컴포넌트에서 가져올 필요가 없다는것을 의미합니다.</p>
<pre><code class="language-jsx">// hooks/items.hooks.ts
import { useQuery } from &quot;react-query&quot;;

export enum ServerStateKeysEnum {
  Items = &#39;items&#39;
} 

export const useGetItems = () =&gt;
  useQuery(
    ServerStateKeysEnum.Items,
    () =&gt; fetch(&#39;https://example.com/feedbacks&#39;),  //Simple fetch function
  );

// components/SomeComponent.tsx

import React from &quot;react&quot;;
import { useGetItems } from &quot;hooks/items.hooks.ts&quot;;

export const SomeComponent: React.FC = () =&gt; {
  const { data: items } = useGetItems();
  return &lt;&gt;{ items }&lt;/&gt;
}</code></pre>
<p>그리고 이렇게 custom hook으로 만들면 해당 데이터를 props drilling 할 필요가 없어집니다. 자식 컴포넌트에 props로 데이터를 넘길 필요 없이 각 컴포넌트에서 custom hook을 불러오면 됩니다.(stale time을 잘 활용하면 불필요한 api 호출 막을 수 있음)</p>
<pre><code class="language-jsx">// components/SomeComponent.tsx

import React from &quot;react&quot;;
import { useGetItems } from &quot;hooks/items.hooks.ts&quot;;

export const SomeComponent: React.FC = () =&gt; {
  const { data: items } = useGetItems();
  return (&lt;&gt;
    &lt;h1&gt;You have {items.length} items&lt;/h1&gt;
    {/* 이것은 item에 관심이 없는 일부 중간 구성 요소이며
        item에 관심이 있는 손자 구성 요소에 도달하기 위해 데이터를 전달할 필요가 없습니다.
    */}
    &lt;SomeRandomChildComponent /&gt; 
  &lt;/&gt;);
}

// components/SomeGreatGrandChildrenComponen.tsx

import React from &quot;react&quot;;
import { useGetItems } from &quot;hooks/items.hooks.ts&quot;;

export const SomeGreatGrandChildrenComponen: React.FC = () =&gt; {
  const { data: items } = useGetItems();
  return (&lt;&gt;{items}&lt;/&gt;);
}</code></pre>
<h3 id="3-stale-time-option을-통해-네트워크-비용-감소">3. stale time option을 통해 네트워크 비용 감소</h3>
<p>위에서 잠깐 언급한 <code>stale time을 잘 활용하면 불필요한 api 호출 막을 수 있음</code>은 아래의 config로 지정할 수 있습니다. 해당 옵션은 프로젝트 전체의 option, 각 useQuery의 option으로 모두 지정할 수 있습니다.</p>
<pre><code class="language-jsx">// app.tsx
import { QueryClient, QueryClientProvider } from &#39;react-query&#39;;

const queryCache = new QueryClient({
  defaultOptions: {
    queries: {
      refetchOnWindowFocus: false,
      retry: false,
      staleTime: 30000,
    },
  },
});

const App = () =&gt; (
    &lt;QueryClientProvider client={queryCache}&gt;
      &lt;FirstSiblingComponent /&gt;
      &lt;SecondSiblingComponent /&gt;
    &lt;/QueryClientProvider&gt;
);</code></pre>
<h3 id="4-수동으로-캐시-무효화">4. 수동으로 캐시 무효화</h3>
<p>만약 products list를 수정할 수 있는 어드민 페이지가 있다고 가정해봅시다. </p>
<p>처음 api에서 products를 불러온 후 staleTime을 infinity로 지정한 후 수정 및 추가 작업을 한 후에만 다시 가져오도록 하고 싶을땐 어떻게 해야 할까요? 바로 react query의 invalidateQueries 매서드를 활용하면 됩니다.</p>
<pre><code class="language-jsx">// hooks/items.hooks.ts
import { useMutation, useQueryClient } from &quot;react-query&quot;;
import {postCreateNewItem} from &#39;./items.api&#39;;
import { useQuery } from &quot;react-query&quot;;

export enum ServerStateKeysEnum {
  Items = &#39;items&#39;
} 

export const useGetItems = () =&gt;
  useQuery(
    ServerStateKeysEnum.Items,
    () =&gt; fetch(&#39;https://example.com/feedbacks&#39;),  //Simple fetch function
  );

export const useCreateItem = () =&gt; {
  const cache = useQueryClient();
  return useMutation(postCreateNewItem, {
    onSuccess: () =&gt; {
      cache.invalidateQueries(ServerStateKeysEnum.Items);
    }
  });
};</code></pre>
<h3 id="5-custom-hook에서-option-custom-하기">5. custom hook에서 option custom 하기</h3>
<p>custom hook으로 useQury, useMutation을 작성하여 활용하려고 하는데 각 컴포넌트마다 옵션이 다른경우가 존재합니다.</p>
<h4 id="5-1-usequery의-enabled-옵션">5-1. useQuery의 enabled 옵션</h4>
<p>만약 useQuery의 경우, 특정 상황에서는 해당 api를 요청하지 않아도 되는 경우 enabled 옵션을 컴포넌트에서 정해야 할 수 있습니다.</p>
<pre><code class="language-jsx">// hooks/items.hooks.ts
import { useQuery, UseQueryOptions } from &quot;react-query&quot;;

export enum ServerStateKeysEnum {
  Items = &#39;items&#39;
} 

export const useGetItems = (options?: UseQueryOptions) =&gt;
  useQuery(
    ServerStateKeysEnum.Items,
    () =&gt; fetch(&#39;https://example.com/feedbacks&#39;),  //Simple fetch function
    {
      ...options
    }
  );

// components/SomeComponent.tsx
export const SomeComponent: React.FC&lt;{ hasValidSubscription: boolean }&gt; = ({
  hasValidSubscription
}) =&gt; {
  const { data: items } = useGetItems({
    enabled: hasValidSubscription //If hasValidSubscription === false, query won&#39;t be executed
  });
  return (&lt;&gt;{items}&lt;/&gt;);
}</code></pre>
<h4 id="5-2-usemutation-onerror-onsuccess">5-2. useMutation onError, onSuccess</h4>
<p>useMutation의 경우 만약 에러처리, 성공처리가 모든 컴포넌트에서 같다면 custom option이 불 필요하지만 만약 컴포넌트마다 에러처리, 성공처리를 다르게 해주어야 한다면 custom option으로 지정해줄 수 있습니다.</p>
<pre><code class="language-jsx">// hooks/items.hooks.ts
import { useMutation } from &quot;react-query&quot;;
import {patchItem} from &#39;./items.api&#39;;
import { useRenderToastMessage } from &#39;../some-helper-hooks/useRenderToastMessage&#39;;

export const useMutateItem = (options?: UseMutationOptions) =&gt; {
  const toast = useRenderToastMessage();
  return useMutation(patchItem, {
    onSuccess: () =&gt; {
      toast.render({
        theme: &#39;success&#39;,
        message: &#39;Item successfully modified&#39;
      });
    },
    onError: () =&gt; {
      toast.notify({
        theme: &#39;error&#39;,
        children: &#39;Could not modify item&#39;
      });
    },
  });
};


// components/SomeComponent.tsx
export const SomeComponent: React.FC = () =&gt; {
  const { mutate } = useMutateItem({
   onSuccess: () =&gt; {
      toast.render({
        theme: &#39;success&#39;,
        message: &#39;Item successfully modified&#39;
      });
    },
    onError: () =&gt; {
      toast.notify({
        theme: &#39;error&#39;,
        children: &#39;Could not modify item&#39;
      });
    },
  });

  const { data } = mutate();
  return (&lt;&gt;{data}&lt;/&gt;);
}</code></pre>
<h3 id="-실시간">(+) 실시간..</h3>
<p><a href="https://junvelee.tistory.com/129">실시간 업데이트</a></p>
<pre><code class="language-jsx">staleTime: 30000, // 30초 동안 캐시된 데이터를 사용
queryInvalidationInterval: 60000, // 1분마다 서버에 재요청하여 캐시 갱신</code></pre>
<blockquote>
<p>vs 소켓</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[React query 캐시와 캐시 상태 활용하기]]></title>
            <link>https://velog.io/@boyeon_jeong/React-query-%EC%BA%90%EC%8B%9C</link>
            <guid>https://velog.io/@boyeon_jeong/React-query-%EC%BA%90%EC%8B%9C</guid>
            <pubDate>Thu, 14 Dec 2023 03:15:24 GMT</pubDate>
            <description><![CDATA[<h1 id="react-query의-캐시-상태fresh-stale-inactive">React Query의 캐시 상태(fresh, stale, inactive)</h1>
<p>react query deevtool을 보면 다음과 같이 <strong>fresh, stale, fetching, inactive</strong>라는 상태가 존재합니다.
<img src="https://velog.velcdn.com/images/boyeon_jeong/post/0bf44066-16bd-46e6-9b31-f8319aaa0697/image.png" alt=""></p>
<blockquote>
<p><strong>fresh</strong>(:신선한) &gt; 데이터 그대로 사용 가능(현재 DB 저장된 데이터와 같은 데이터)
<strong>stale</strong>(:신선하지 않은) &gt; 데이터 새로 fetching 필요(현재 DB 저장된 데이터와 다른 데이터)
<strong>inactive</strong> &gt; 현재 컴포넌트에서 사용 x</p>
</blockquote>
<p>가장 먼저 react query에서 <code>캐시가 유효하다</code>라는 말은 QueryCache 객체의 <strong>queries 배열과 queriesInMap 객체</strong>에 데이터가 <code>저장되어 있는 상태</code>를 의미합니다. </p>
<p>기본적으로 위의 3가지 상태(fresh, stale, inactive)는 <code>모두 저장은 되어있는 데이터(캐싱 데이터)</code>이며, 아래 2가지의 상황에 따라 <code>상태를 구별</code>한 것입니다.</p>
<ol>
<li>현재 컴포넌트 사용되는지 유무</li>
<li>refectcing이 필요한지
<img src="https://velog.velcdn.com/images/boyeon_jeong/post/a5aa06d7-cd7e-4964-8cff-175d6bb2c9b1/image.png" alt=""></li>
</ol>
<p>만약 이 캐시가 현재 DB 저장된 데이터와 같아서 그대로 사용 가능한 경우라면 api를 새로 refectcing하지 않아도 되는 <code>fresh</code> 상태를 의미하고, DB 데이터가 새로 업데이트 되어 refectcing이 필요한 <code>stale</code> 상태라고 합니다.</p>
<p>따라서 stale일 경우에 refectcing이 되는데요. 여기서 <strong>무조건 stale라고 해서 실시간으로 api가 refectcing 되는 것이 아니라</strong> 아래와 같은 상황일 경우 refectcing이 진행됩니다.</p>
<ul>
<li>새로운 Query Instance가 마운트 될 때 (페이지 이동 후 등의 상황)</li>
<li>브라우저 화면을 다시 focus 할 때</li>
<li>인터넷이 재연결되었을 때</li>
<li>refetchInterval이 설정되어있을 때</li>
</ul>
<hr>
<h1 id="why">Why</h1>
<p>여기서 왜 cache에 더해 여러가지 캐시의 상태들이 필요한지에 대한 의문이 들수 있습니다.</p>
<h3 id="1-ux개선">1. UX개선</h3>
<p>잘 정리해주신 <a href="https://www.timegambit.com/blog/digging/react-query/03">블로그</a> 덕분에 의문점이 해결되었습니다. 블로그의 내용을 정리하자면 stale상태를 활용한다면 cache를 다시 불러오는 그 <code>비어있는 타임</code>에 loading 페에지가 아닌 stale상태 데이터를 보여줌으로서 사용성을 높이는 목적을 달성할 수 있습니다. (마치 배포할때 블루, 그린과 같은 너낌!)</p>
<p><img src="https://velog.velcdn.com/images/boyeon_jeong/post/b76bffdc-dd3a-4109-a84b-d9695ac807c5/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/boyeon_jeong/post/2558ffe4-eba1-4d9d-babf-31d57088b5e1/image.png" alt=""></p>
<blockquote>
<p>이미지 출처: <a href="https://www.timegambit.com/blog/digging/react-query/03">https://www.timegambit.com/blog/digging/react-query/03</a></p>
</blockquote>
<h3 id="2-네트워크-비용-감소">2. 네트워크 비용 감소</h3>
<p>만약 아래와 같은 경우에 react query를 사용하지 않는다면 불필요한 네트워크 비용이 발생할 수 있습니다.</p>
<ul>
<li>api에서 불러온 후 바뀌지 않는 데이터</li>
<li>post, put, delete가 발생한 경우에만 refetcing 필요</li>
<li>변경사항을 네트워크 요청 없이 직접 수정해줄 수 있는 경우</li>
</ul>
<p>따라서 이러한 경우 react query의 여러가지 설정을 적절히 사용해준다면 네트워크 비용을 크게 감소시킬수 있습니다.</p>
<hr>
<h1 id="how">How</h1>
<h3 id="1-time-설정">1. Time 설정</h3>
<h4 id="📌-staletime">📌 staleTime</h4>
<p>fresh에서 stale로 가는 시간(api 호출을 n초 있다가 다시 해)</p>
<h4 id="📌-cachetime">📌 cacheTime</h4>
<p>inactive가 된 후 메모리에 남아있는 시간(이 시간이 지나면 가비지 컬렉터에 의해 제거됨)</p>
<h3 id="2-default-설정">2. default 설정</h3>
<p>QueryClient의 defaultOptions에 staleTime과 cacheTime 지정해줄수 있습니다.</p>
<pre><code class="language-jsx">const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 1000 * 60,
      cacheTime: 1000 * 60 * 5,
    }
  },
});

const App = ({ Component, pageProps }: AppProps) =&gt; {
  return (
    &lt;QueryClientProvider client={queryClient}&gt;
      &lt;Component {...pageProps} /&gt;
    &lt;/QueryClientProvider&gt;
  );
};</code></pre>
<h3 id="3-상황별-설정">3. 상황별 설정</h3>
<h4 id="📌-api에-호출이-필요하지만-최초-호출-후-바뀌지-않는-정적-데이터">📌 api에 호출이 필요하지만 최초 호출 후 바뀌지 않는 정적 데이터</h4>
<p><code>staleTime</code>, <code>cacheTime</code>을 <code>Infinity</code>로 지정</p>
<h4 id="📌-post-put-delete-할때만-다시-호출이-필요한-경우">📌 post, put, delete 할때만 다시 호출이 필요한 경우</h4>
<p>invalidateQueries() 메서드 활용
invalidateQueries 메서드는 캐싱된 쿼리를 무효화하는 메서드입니다.</p>
<p>어떤 상태의 데이터를 무효화 할것인지는 refetchType(active, inactive, all, none)으로 지정해줄수 있습니다. 기본값은 active입니다.</p>
<pre><code class="language-jsx">useMutation(api.commonService.postLikeData, {
  onSuccess: (data) =&gt; queryClient.invalidateQueries([&#39;getVideoData&#39;, data.newsId]);
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[react query 프로젝트에 적용하기 ]]></title>
            <link>https://velog.io/@boyeon_jeong/react-query-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%97%90-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@boyeon_jeong/react-query-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%97%90-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 12 Dec 2023 08:59:25 GMT</pubDate>
            <description><![CDATA[<h2 id="how">How</h2>
<h3 id="install">install</h3>
<pre><code class="language-shell">npm i react-query</code></pre>
<h3 id="queryclient생성-후-queryclientprovider로-감싸기">queryClient생성 후 QueryClientProvider로 감싸기</h3>
<pre><code class="language-jsx">import &quot;./App.css&quot;;
import Products from &quot;./pages/Products&quot;;
import {
  QueryClient,
  QueryClientProvider,
} from &#39;react-query&#39;


function App() {
  const queryClient = new QueryClient()

  return  &lt;QueryClientProvider client={queryClient}&gt;&lt;Products /&gt;&lt;/QueryClientProvider&gt;;
}

export default App;</code></pre>
<h3 id="component에서-사용하기">Component에서 사용하기</h3>
<p>기존 useEffect와 단순 await, async와 비교해봅시다!</p>
<h4 id="1-get-usequery">1. get: useQuery</h4>
<pre><code class="language-jsx">const Products = () =&gt; {
  const [products, setProducts] = useState&lt;Product[]&gt;([]);

  const query = useQuery(&#39;products&#39;, getProduct);
  console.log(query);

  //기존 코드
  // const fetchPosts = async () =&gt; {
  //   const res: AxiosRequestConfig&lt;{ products: Product[] }&gt; = await getProduct();

  //   setProducts(res?.data?.products ?? []);
  // };

  // useEffect(() =&gt; {
  //   fetchPosts();
  // }, []);</code></pre>
<p><img src="https://velog.velcdn.com/images/boyeon_jeong/post/33c11e12-1613-430f-9a5f-1f261fefb59c/image.png" alt=""></p>
<h3 id="devtools-사용하기">Devtools 사용하기</h3>
<p>App.tsx에 ReactQueryDevtools을 import해주어 사용합니다.</p>
<pre><code class="language-tsx">import &quot;./App.css&quot;;
import Products from &quot;./pages/Products&quot;;
import { QueryClient, QueryClientProvider } from &quot;react-query&quot;;
import { ReactQueryDevtools } from &#39;react-query/devtools&#39;

function App() {
  const queryClient = new QueryClient();

  return (
    &lt;QueryClientProvider client={queryClient}&gt;
      &lt;ReactQueryDevtools initialIsOpen={false} /&gt;
      &lt;Products /&gt;
    &lt;/QueryClientProvider&gt;
  );
}

export default App;</code></pre>
<p><img src="https://velog.velcdn.com/images/boyeon_jeong/post/7fe54fe7-8dbf-4ee7-ab33-e5465da79f1c/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[axios 모듈화와 Type 지정]]></title>
            <link>https://velog.io/@boyeon_jeong/axios-%EB%AA%A8%EB%93%88%ED%99%94%EC%99%80-Type-%EC%A7%80%EC%A0%95</link>
            <guid>https://velog.io/@boyeon_jeong/axios-%EB%AA%A8%EB%93%88%ED%99%94%EC%99%80-Type-%EC%A7%80%EC%A0%95</guid>
            <pubDate>Tue, 12 Dec 2023 07:39:45 GMT</pubDate>
            <description><![CDATA[<h2 id="why">Why</h2>
<p>서버에서 데이터를 많이 다루어야 하는 어플리케이션에서는 axios를 많이 활용해야 합니다. 만약 axios를 <strong>모듈화</strong> 하지 않는다면 <code>반복적인 코드</code>를 작성해야 하고 <code>일관되지 않는 코드</code>를 작성할 수 있습니다.</p>
<p>또한, axios의 <strong>타입</strong>을 잘 활용한다면 <code>자동 완성 기능</code>과 <code>사이드 이펙트</code>를 줄일 수 있습니다.</p>
<h2 id="how">How</h2>
<h3 id="1-폴더-구조">1. 폴더 구조</h3>
<p>저는 기본적인 보일러 플레이트 코드와 공통적인 타입 지정 부분은 <code>config.ts</code>에 작성해주었습니다. 그리고 실제 api는 <code>api 폴더</code> 하위에 <code>도메인 별로 파일</code>을 만들어서 작성했습니다.
<img src="https://velog.velcdn.com/images/boyeon_jeong/post/f7179671-dccd-4688-8556-76f921d4fcf1/image.png" alt=""></p>
<h3 id="2-configts">2. config.ts</h3>
<p>저는 axios의 baseurl, timeout, header등을 활용하여 create하고 get, post, put, delete 등의 타입을 지정해주는 부분을 <code>config.ts</code>에 작성했습니다.</p>
<pre><code class="language-ts">import axios, { Axios } from &quot;axios&quot;;
import { AxiosRequestConfig, AxiosResponse } from &#39;axios&#39;;

const server: Axios = axios.create({
    // baseURL: &#39;&#39;,
    // headers: {},
    // timeout: 1000,
});

const http = {
    get : &lt;T = any, R = AxiosResponse&lt;T&gt;, D = any&gt;(url: string, config?: AxiosRequestConfig&lt;D&gt;): Promise&lt;R&gt; =&gt; {
        return server.get(url, config);
    },
};
export default http;</code></pre>
<p>타입은 <code>/node_modules/axios/index.d.ts</code>를 참고하였습니다.
<img src="https://velog.velcdn.com/images/boyeon_jeong/post/6e43809a-1827-4b0e-80dc-5d00996835cb/image.png" alt=""></p>
<h3 id="3-api-요청">3. api 요청</h3>
<p>그리고 실제 api를 요청하는 url, config 등은 도메인 별로 파일을 나누어 작성해주었습니다. </p>
<pre><code class="language-ts">import http from &quot;../config&quot;

export const getProduct = () =&gt; {
    return http.get(&#39;/products&#39;);
}</code></pre>
<h3 id="4-component-활용">4. Component 활용</h3>
<p>그리고 실제 Compoenet에서는 아래와 같이 활용하였습니다.</p>
<pre><code class="language-tsx">const Products = () =&gt; {
  const [products, setProducts] = useState&lt;Product[]&gt;([]);

  const fetchPosts = async () =&gt; {
    const res: AxiosRequestConfig&lt;{ products: Product[] }&gt; = await getProduct();

    setProducts(res?.data?.products ?? []);
  };

  useEffect(() =&gt; {
    fetchPosts();
  }, []);
...</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Front단에서 API 만들기 - Mock Service Worker (Version 2)]]></title>
            <link>https://velog.io/@boyeon_jeong/Mock-Service-Worker</link>
            <guid>https://velog.io/@boyeon_jeong/Mock-Service-Worker</guid>
            <pubDate>Mon, 11 Dec 2023 02:34:14 GMT</pubDate>
            <description><![CDATA[<h2 id="mock-service-worker">Mock Service Worker</h2>
<p>mock service worker는 Service Worker를 사용하여 네트워크 호출을 가로채는 API 모킹(mocking) 라이브러리입니다. 한마디로 브라우저를 속여 <code>마치 백엔드 API인척 가짜 데이터</code>를 제공해줄 수 있는 라이브러리입니다.</p>
<p><img src="https://velog.velcdn.com/images/boyeon_jeong/post/da52f626-2b79-4a0a-9c7c-56e551f05264/image.png" alt=""></p>
<blockquote>
<p>이미지 <a href="https://www.codit.eu/blog/how-to-mock-api-requests-in-front-end-development/">출처</a></p>
</blockquote>
<h2 id="why">Why</h2>
<h3 id="1-빠른-개발">1. 빠른 개발</h3>
<p>프론트엔드 개발은 프로세스 순서의 <code>가장 마지막</code>에 위치해 있습니다. 그로인하여 실제 개발 프로젝트에서 기획, 백엔드 개발이 완료될때까지 대기하다가 개발이 뒤로 밀려 완성도가 낮아지는 경우가 존재합니다.</p>
<p><code>그런데 만약 프론트엔드 개발자가 미리 api를 개발하고 테스트 할 수 있는 환경이 제공된다면 어떨까요?</code> 
만약 백엔드 개발과 프론트 개발이 병렬적으로 협업하게 되어 의존성을 떨어트리게 된다면 빠른 개발이 가능한 환경이 제공될 것입니다. 이는 개발의 완성도를 높일 수 있는 발판이 될 것입니다.</p>
<h3 id="2-테스트-신뢰도">2. 테스트 신뢰도</h3>
<p>테스트를 할때 발생하는 <code>외부 요인</code>(네트워크, 서버)들이 존재합니다. 이는 테스트의 <code>일관성과 신뢰도</code>를 떨어트릴수 있습니다.</p>
<p>그에 반해 msw는 외부 요인의 영향을 받지 않기 때문에 일관된 테스트를 진행할 수 있습니다. </p>
<h2 id="how">How</h2>
<h3 id="1-install">1. Install</h3>
<pre><code class="language-shell">npm install msw --save-dev</code></pre>
<h3 id="2-요청-가로채기">2. 요청 가로채기</h3>
<p>request에 따른 mock response를 보내기 위해서는 먼저 요청을 intercept 해야 합니다. 여기서 필요한 것은 <code>predicate, resolver 함수</code> 입니다. 이 두가지를 msw에서 제공하는 <a href="https://mswjs.io/docs/api/http">http 함수</a>에 알맞게 보내면 됩니다. </p>
<h4 id="2-1-하나씩-보내기">2-1. 하나씩 보내기</h4>
<pre><code class="language-javascript">import { http, HttpResponse } from &#39;msw&#39;

http.get(
  &#39;/pets&#39;,
  ({ request, params, cookies }) =&gt; {
    return HttpResponse.json([&#39;Tom&#39;, &#39;Jerry&#39;, &#39;Spike&#39;])
  }
)</code></pre>
<h4 id="2-2-한번에-여러개-보내기">2-2. 한번에 여러개 보내기</h4>
<pre><code class="language-javascript">export const handlers = [
  http.get(&#39;/pets&#39;, petsResolver),
  http.post(&#39;https://api.github.com/repo&#39;, repoResolver),
]</code></pre>
<blockquote>
<p>path 관련된 <a href="https://github.com/pillarjs/path-to-regexp">정규식</a> 사용 가능합니다. </p>
</blockquote>
<pre><code class="language-javascript">// Intercept all GET requests to &quot;/user&quot;:
// - GET /user
// - GET /user/abc-123
// - GET /user/abc-123/settings
http.get(&#39;/user/*&#39;, userResolver)</code></pre>
<h3 id="3-응답-보내기">3. 응답 보내기</h3>
<h4 id="3-1-staus-code--text">3-1. staus code + text</h4>
<pre><code class="language-javascript">import { http, HttpResponse } from &#39;msw&#39;

export const handlers = [
  http.get(&#39;/apples&#39;, () =&gt; {
    return new HttpResponse(null, {
      status: 404,
      statusText: &#39;Out Of Apples&#39;,
    })
  }),
]</code></pre>
<h4 id="3-2-mocking-headers">3-2. Mocking headers</h4>
<pre><code class="language-javascript">import { http, HttpResponse } from &#39;msw&#39;

export const handlers = [
  http.post(&#39;/auth&#39;, () =&gt; {
    return new HttpResponse(null, {
      headers: {
        &#39;Set-Cookie&#39;: &#39;mySecret=abc-123&#39;,
        &#39;X-Custom-Header&#39;: &#39;yes&#39;,
      },
    })
  }),
]</code></pre>
<h4 id="3-3-text">3-3. text</h4>
<pre><code class="language-javascript">import { http, HttpResponse } from &#39;msw&#39;

export const handler = [
  http.get(&#39;/name&#39;, () =&gt; {
    return new HttpResponse(&#39;John&#39;)
  }),
]</code></pre>
<h4 id="3-4-json">3-4. json</h4>
<pre><code class="language-javascript">import { http, HttpResponse } from &#39;msw&#39;

export const handlers = [
  http.post(&#39;/auth&#39;, () =&gt; {
    // Note that you DON&#39;T have to stringify the JSON!
    return HttpResponse.json({
      user: {
        id: &#39;abc-123&#39;,
        name: &#39;John Maverick&#39;,
      },
    })
  }),
]
</code></pre>
<h2 id="실제-적용">실제 적용</h2>
<h3 id="1-설치와-init">1. 설치와 init</h3>
<p>msw를 설치한 후 브라우저 service worker를 등록해주기 위해서는 <a href="https://mswjs.io/docs/cli/init/">init</a>이 필요합니다.</p>
<pre><code class="language-shell">npm install msw --save-dev
npx msw init public/ --save</code></pre>
<p>init 명령어를 실행하면 public 폴더 하위에 mockServiceWorker.js 파일이 자동 생성됩니다.
<img src="https://velog.velcdn.com/images/boyeon_jeong/post/3164f260-48a0-4b77-a54c-eebad12b06ba/image.png" alt=""></p>
<h3 id="2-필요한-파일-만들기">2. 필요한 파일 만들기</h3>
<p>msw는 hanlders를 만들고 browser에 setup 해주고 setup한 server를 start 해주는 과정이 필요합니다. 이를 위해서 server(mocks)폴더에 browser, handlers 파일을 만들어줍니다.
<img src="https://velog.velcdn.com/images/boyeon_jeong/post/0a6923ca-05e5-492a-9b10-7e3884386825/image.png" alt=""></p>
<h3 id="3-handlers-작성">3. handlers 작성</h3>
<p>아주 간단하게 products를 가져오는 handler를 생성하고 handlers에 포함시켜줍니다.(모듈화 안한 코드)</p>
<pre><code class="language-tsx">import { http, HttpResponse } from &quot;msw&quot;;

interface Product {
  id: number;
  brand: string;
  name: string;
  price: number;
  rate: number;
  review: number;
}

const products: Product[] = [
  {
    id: 1,
    brand: &quot;LG전자&quot;,
    name: &quot;LG스타일러 5벌+1벌 S5MBAUE 블랙미러+실내제습&quot;,
    price: 1235440,
    rate: 19,
    review: 344,
  },
  {
    id: 2,
    brand: &quot;아엠홈&quot;,
    name: &quot;비침없는 도톰 레이스/쉬폰커튼(나비주름/핀형/봉집)&quot;,
    price: 41600,
    rate: 40,
    review: 31258,
  },
  {
    id: 3,
    brand: &quot;동원&quot;,
    name: &quot;동원참치 85g*12캔 3종 (라이트스탠다드/고추/콘참치)&quot;,
    price: 17980,
    rate: 25,
    review: 4475,
  },
  {
    id: 4,
    brand: &quot;LG전자&quot;,
    name: &quot;LG 디오스 식기세척기 오브제컬렉션 DUBJ2EAL&quot;,
    price: 929900,
    rate: 26,
    review: 250,
  },
  {
    id: 5,
    brand: &quot;LG전자&quot;,
    name: &quot;비스포크 WF24B9600KE+DV20B9760CE 오토오픈도어&quot;,
    price: 2242719,
    rate: 7,
    review: 22,
  },
];

const productsResolver = () =&gt; {
  return HttpResponse.json({ products });
};

export const handlers = [http.get(&quot;/products&quot;, productsResolver)];</code></pre>
<h3 id="4-borwers에-setup-해주기">4. borwers에 setUp 해주기</h3>
<p>위에서 작성한 handlers를 setup 해줍니다.</p>
<pre><code class="language-tsx">import { setupWorker } from &#39;msw/browser&#39;
import { handlers } from &#39;./handlers&#39;;

export const server = setupWorker(...handlers);</code></pre>
<blockquote>
<p>Service Worker는 브라우저에만 사용할 수 있습니다. 따라서 jest와 같이 node에서 사용하려면 따로 server setupServer함수를 작성하여 setup해주어야 합니다.</p>
</blockquote>
<h3 id="5-start해주기">5. start해주기</h3>
<p>main.tsx에서 setUp한 Worker를 불러온 후 start 해줍니다.</p>
<pre><code class="language-tsx">import React from &quot;react&quot;;
import ReactDOM from &quot;react-dom/client&quot;;
import App from &quot;./App.tsx&quot;;
import &quot;./index.css&quot;;

const root = ReactDOM.createRoot(document.getElementById(&quot;root&quot;)!);

const mock = async () =&gt; {
  const { server } = await import(&quot;./server/browser&quot;);
  server.start();
};

mock().then(() =&gt; {
  root.render(
    &lt;React.StrictMode&gt;
      &lt;App /&gt;
    &lt;/React.StrictMode&gt;
  );
});
</code></pre>
<p>정상적으로 mockServer가 실행되었다면 크롬 개발자도구에 Mocking enabled. 라는 글자를 확인 할 수 있습니다.
<img src="https://velog.velcdn.com/images/boyeon_jeong/post/442e34e5-b4f6-4d7e-bcc7-c3144f326ea8/image.png" alt=""></p>
<h3 id="6-axios를-통해-mock-api-호출하기">6. axios를 통해 mock api 호출하기</h3>
<p><img src="https://velog.velcdn.com/images/boyeon_jeong/post/1ce68036-c65b-40ea-ab14-776accbe8f14/image.png" alt="">
<img src="https://velog.velcdn.com/images/boyeon_jeong/post/8e6b336d-c3be-42cc-a4a1-2bde185431ab/image.png" alt=""></p>
<pre><code class="language-jsx">import Typography from &quot;../atoms/Typography&quot;;
import colors from &quot;../atoms/Color&quot;;
import { useEffect, useState } from &quot;react&quot;;
import axios from &quot;axios&quot;;

interface Product {
  id: number;
  brand: string;
  name: string;
  price: number;
  rate: number;
  review: number;
}

const Products = () =&gt; {
  const [products, setProducts] = useState&lt;Product[]&gt;([]);

  const fetchPosts = async () =&gt; {
    const res = await axios({
      method: &quot;get&quot;,
      url: &quot;/products&quot;,
    });

    setProducts(res.data.products);
    console.log(res);
  };

  useEffect(() =&gt; {
    fetchPosts();
  }, []);

  return (
    &lt;section
      css={{
        border: `1px solid ${colors[&quot;black&quot;]}`,
        display: &quot;grid&quot;,
        gridTemplateColumns: &quot;repeat(4, 23%)&quot;,
        justifyContent: &quot;space-between&quot;,
      }}
    &gt;
      {products.map((product: Product) =&gt; (
        &lt;div
          key={product.id}
          css={{
            display: &quot;flex&quot;,
            flexDirection: &quot;column&quot;,
            alignItems: &quot;start&quot;,
          }}
        &gt;
          &lt;Typography variant=&quot;h4&quot; color=&quot;gray1&quot;&gt;
            {product.brand}
          &lt;/Typography&gt;
          &lt;Typography variant=&quot;h2&quot;&gt;{product.name}&lt;/Typography&gt;
          &lt;div&gt;
            &lt;Typography variant=&quot;h1B&quot; color=&quot;primary&quot;&gt;
              {product.rate}
            &lt;/Typography&gt;
            &lt;Typography variant=&quot;h1B&quot;&gt;{product.price}&lt;/Typography&gt;
          &lt;/div&gt;
          &lt;Typography variant=&quot;h4B&quot; color=&quot;gray2&quot;&gt;
            {`리뷰 ${product.review}`}
          &lt;/Typography&gt;
        &lt;/div&gt;
      ))}
    &lt;/section&gt;
  );
};

export default Products;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[CSS-in-CSS, CSS-in-JS 비교(+ emotion 사용법)]]></title>
            <link>https://velog.io/@boyeon_jeong/CSS-in-CSS-CSS-in-JS-%EB%B9%84%EA%B5%90-emotion-%EC%82%AC%EC%9A%A9%EB%B2%95</link>
            <guid>https://velog.io/@boyeon_jeong/CSS-in-CSS-CSS-in-JS-%EB%B9%84%EA%B5%90-emotion-%EC%82%AC%EC%9A%A9%EB%B2%95</guid>
            <pubDate>Thu, 07 Dec 2023 03:25:55 GMT</pubDate>
            <description><![CDATA[<h2 id="css-in-css">CSS-in-CSS</h2>
<p>css를 <code>모듈화</code> 하는 방법</p>
<ul>
<li>장점: 이름이 중복되어도 scope를 지역적으로 제한하여 번들링 한 후에는 겹치지 않음</li>
<li>단점: 별도로 많은 <code>CSS 파일</code> 필요<blockquote>
<p>CSS 전처리기 활용(SCSS)</p>
</blockquote>
</li>
</ul>
<h2 id="css-in-js">CSS-in-JS</h2>
<p>javascript 안에서 css를 정의하는 방법입니다. 코드를 구성하는 더 나은 방법으로는 <code>코로케이션</code> 이 있습니다. 이는 <code>단일 컴포넌트에 관련된 모든 것을 같은 위치</code>에 두는 것입니다. CSS-in-JS는 이러한 코로케이션 방법을 사용할 수 있습니다. 가장 유명한 CSS-in-JS 라이브러리에는 styled-components, emotion 가 있습니다.</p>
<blockquote>
<p>코로케이션에 대해 자세히 알아보기: <a href="https://kentcdodds.com/blog/colocation">https://kentcdodds.com/blog/colocation</a></p>
</blockquote>
<hr>
<h2 id="emotion">emotion</h2>
<p>emotion의 사용 방법을 간단하게 살펴보려고 합니다. </p>
<h3 id="css함수">css함수</h3>
<p>emotion은 css함수를 사용합니다. import를 해주고 css Prop(css 함수)를 사용하기 위한 준비를 해줍니다.</p>
<pre><code class="language-jsx">// JSX Pragma
/** @jsxImportSource @emotion/react */
//css 함수
import { css } from &#39;@emotion/react&#39;;</code></pre>
<h3 id="css-prop을-사용하는-방법">css Prop을 사용하는 방법</h3>
<ol>
<li>babel preset<pre><code>{
&quot;presets&quot;: [&quot;@emotion/babel-preset-css-prop&quot;]
}</code></pre></li>
<li>jsx pragma<pre><code>/** @jsx jsx */
import { jsx } from &#39;@emotion/react&#39;</code></pre></li>
<li>플러그인</li>
</ol>
<p>위 세가지의 방법중 하나를 선택해서 css prop을 사용할 준비를 합니다. 두가지 모두 React.createElemet 대신 emoion&#39;s jsx 함수로 컴파일 되도록 해줍니다.</p>
<p><img src="https://velog.velcdn.com/images/boyeon_jeong/post/36de0bfe-712a-4cd6-a1af-8f930cd536de/image.png" alt=""></p>
<blockquote>
<p><a href="https://emotion.sh/docs/css-prop#babel-preset">https://emotion.sh/docs/css-prop#babel-preset</a></p>
</blockquote>
<h3 id="기본-사용법">기본 사용법</h3>
<p>html에 태그에 css 속성을 넘기고 해당 속성에 css 함수의 결과값을 넘깁니다.</p>
<blockquote>
<p>html에 태그에는 css 속성이 없지만 위에서 React.createElemet 대신 emoion&#39;s jsx 함수로 컴파일 되도록 해주었기 때문에 사용 가능</p>
</blockquote>
<p>css 함수의 결과값을 넘기는 방법은 2가지가 있습니다.</p>
<h4 id="1-css-함수에-문자열-넘기기">1. css 함수에 문자열 넘기기</h4>
<pre><code class="language-jsx">function App() {
    return &lt;div css={css`
        backgroud: yellow;
    `}&gt;노란색 영역&lt;/div&gt;
}</code></pre>
<h4 id="2-css-함수에-객체-넘기기css-함수-생략-가능">2. css 함수에 객체 넘기기(css 함수 생략 가능)</h4>
<pre><code class="language-jsx">function App() {
    return &lt;div css={css({
          backgroud: &quot;yello&quot;;
      })}&gt;노란색 영역&lt;/div&gt;
}
//css 함수 생략
function App() {
    return &lt;div css={{
          backgroud: &quot;yello&quot;;
      }&gt;노란색 영역&lt;/div&gt;
}</code></pre>
<blockquote>
<p>스타일을 지정할때 보통 css에서 사용하는 케밥 케이스(rocket-launch-duration)가 아닌 자바스크립트에서 사용하는 카멜 케이스(rocketLaunchDuration) 사용</p>
</blockquote>
<h3 id="가변-스타일링1--하나의-속성값">가변 스타일링1 : 하나의 속성값</h3>
<ol>
<li>기본적인 버튼 스타일<pre><code class="language-jsx">const Button({children}) =&gt; {
 return (
     &lt;button css={
       다른 스타일...
     }&gt;
         {children}
     &lt;/button&gt;
 );
}
</code></pre>
</li>
</ol>
<p>function App() {
    return <Button>Button</Button>;
}</p>
<p>export default App;</p>
<pre><code>
2. 컬러가 정의되어 있는 객체 추가(하나의 속성값)
```jsx
const colors = {
    default = &quot;rgb(36, 41, 47)&quot;,
    danger = &quot;rgb(207, 34, 46)&quot;,
    outline = &quot;rgb(9, 105, 218)&quot;,
}

const Button({children}) =&gt; {
    return (
        &lt;button css={
          다른 스타일...
        }&gt;
            {children}
        &lt;/button&gt;
    );
}

function App() {
    return &lt;Button&gt;Button&lt;/Button&gt;;
}

export default App;</code></pre><ol start="3">
<li>Button 컴포넌트의 props 추가(아무 이름)<pre><code class="language-jsx">const colors = {
 default = &quot;rgb(36, 41, 47)&quot;,
 danger = &quot;rgb(207, 34, 46)&quot;,
 outline = &quot;rgb(9, 105, 218)&quot;,
}
</code></pre>
</li>
</ol>
<p>const Button({children, variant=&quot;defualt&quot;}) =&gt; {
    return (
        &lt;button css={
          다른 스타일...
        }&gt;
            {children}
        </button>
    );
}</p>
<p>function App() {
    return (
      <Button variant="defualt">Button</Button>;
      <Button variant="danger">Button</Button>;
      <Button variant="outline">Button</Button>;
    );
}</p>
<p>export default App;</p>
<pre><code>
4. 받은 props 속성을 스타일에 적용
```jsx
const colors = {
    default = &quot;rgb(36, 41, 47)&quot;,
    danger = &quot;rgb(207, 34, 46)&quot;,
    outline = &quot;rgb(9, 105, 218)&quot;,
}

const Button({children, variant=&quot;defualt&quot;}) =&gt; {
    return (
        &lt;button css={
          다른 스타일...,
          color: colrs[variant],
        }&gt;
            {children}
        &lt;/button&gt;
    );
}

function App() {
    return (
      &lt;Button variant=&quot;defualt&quot;&gt;Button&lt;/Button&gt;;
      &lt;Button variant=&quot;danger&quot;&gt;Button&lt;/Button&gt;;
      &lt;Button variant=&quot;outline&quot;&gt;Button&lt;/Button&gt;;
    );
}

export default App;</code></pre><h3 id="가변-스타일링2--여러개의-속성값">가변 스타일링2 : 여러개의 속성값</h3>
<ol>
<li>기본적인 버튼 스타일<pre><code class="language-jsx">const Button({children}) =&gt; {
 return (
     &lt;button css={
       다른 스타일...
     }&gt;
         {children}
     &lt;/button&gt;
 );
}
</code></pre>
</li>
</ol>
<p>function App() {
    return <Button>Button</Button>;
}</p>
<p>export default App;</p>
<pre><code>
2. 사이즈 정의되어 있는 객체 추가(여러개의 속성)
```jsx
const sizeStyles = {
  sm: {
      fontSize: &#39;12px&#39;,
    padding: &#39;3px 12px&#39;
  },
  md: {
      fontSize: &#39;14px&#39;,
    padding: &#39;5px 16px&#39;
  },
  lg: {
      fontSize: &#39;16px&#39;,
    padding: &#39;9px 20px&#39;
  }
}

const Button({children}) =&gt; {
    return (
        &lt;button css={
          다른 스타일...
        }&gt;
            {children}
        &lt;/button&gt;
    );
}

function App() {
    return &lt;Button&gt;Button&lt;/Button&gt;;
}

export default App;</code></pre><ol start="3">
<li>Button 컴포넌트의 props 추가(아무 이름)<pre><code class="language-jsx">const sizeStyles = {
sm: {
   fontSize: &#39;12px&#39;,
 padding: &#39;3px 12px&#39;
},
md: {
   fontSize: &#39;14px&#39;,
 padding: &#39;5px 16px&#39;
},
lg: {
   fontSize: &#39;16px&#39;,
 padding: &#39;9px 20px&#39;
}
}
</code></pre>
</li>
</ol>
<p>const Button({children, size=&#39;sm&#39;}) =&gt; {
    return (
        &lt;button css={
          다른 스타일...
        }&gt;
            {children}
        </button>
    );
}</p>
<p>function App() {
    return (
      <Button size="sm">Button</Button>;
      <Button size="md">Button</Button>;
      <Button size="lg">Button</Button>;
    );
}</p>
<p>export default App;</p>
<pre><code>
4. 받은 props 속성을 스타일에 적용(여러개의 속성이 객체로 담겨있기 때문에 `스프레드 연산자 사용`)
```jsx
const sizeStyles = {
  sm: {
      fontSize: &#39;12px&#39;,
    padding: &#39;3px 12px&#39;
  },
  md: {
      fontSize: &#39;14px&#39;,
    padding: &#39;5px 16px&#39;
  },
  lg: {
      fontSize: &#39;16px&#39;,
    padding: &#39;9px 20px&#39;
  }
}

const Button({children, size=&quot;sm&quot;}) =&gt; {
    return (
        &lt;button css={
          다른 스타일...,
          ...sizeStyles[size]
        }&gt;
            {children}
        &lt;/button&gt;
    );
}

function App() {
    return (
      &lt;Button size=&quot;sm&quot;&gt;Button&lt;/Button&gt;;
      &lt;Button size=&quot;md&quot;&gt;Button&lt;/Button&gt;;
      &lt;Button size=&quot;lg&quot;&gt;Button&lt;/Button&gt;;
    );
}

export default App;</code></pre><hr>
<blockquote>
<p>Bootstrap, Material, Tailwind</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[재사용성을 높이는 컴포넌트 설계에 대한 생각(Atomic Design 패턴과 Headless component 사용하기)]]></title>
            <link>https://velog.io/@boyeon_jeong/%EC%9E%AC%EC%82%AC%EC%9A%A9%EC%84%B1%EC%9D%84-%EB%86%92%EC%9D%B4%EB%8A%94-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%84%A4%EA%B3%84%EC%97%90-%EB%8C%80%ED%95%9C-%EC%83%9D%EA%B0%81</link>
            <guid>https://velog.io/@boyeon_jeong/%EC%9E%AC%EC%82%AC%EC%9A%A9%EC%84%B1%EC%9D%84-%EB%86%92%EC%9D%B4%EB%8A%94-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%84%A4%EA%B3%84%EC%97%90-%EB%8C%80%ED%95%9C-%EC%83%9D%EA%B0%81</guid>
            <pubDate>Tue, 05 Dec 2023 03:50:53 GMT</pubDate>
            <description><![CDATA[<p>요즘 컴포넌트 설계에 대해 관심이 많아졌다. <code>설계를 잘 하는 개발자</code>가 되고 싶다는 생각이 든다. 그 이유는 다음과 같다.</p>
<ol>
<li>초기 구축 서비스에서 <code>설계를 잘 하고 싶다</code>.</li>
<li>설계가 잘 된 프로젝트에 투입 되었을때 <code>잘 흡수하고 스며들고</code> 싶다.</li>
</ol>
<p>내가 하고 싶은 설계는 재사용성을 높여 협업에 도움을 주어서 <code>개발 속도를 향상</code>시키고, 추후에 <code>유지보수 비용도 줄일 수</code> 있는 설계를 하고 싶다.</p>
<h2 id="why">Why</h2>
<p>내가 이런 생각을 하게 된 계기는 다음과 같다.</p>
<ol>
<li>처음 내가 작성한 코드를</li>
<li>추후에 팀원이 비슷하다며 <strong>로직과 디자인을 추가</strong></li>
<li>다른 팀원도 비슷하다며 <strong>로직과 디자인을 추가</strong></li>
<li>갑자기 내 코드가 작동하지 않음</li>
<li>내가 작성했지만 현재는 알아볼 수 없는 <code>복잡한 컴포넌트</code>가 됨</li>
<li>작성한 팀원에서 가서 물어보고 다시 해석해야 해서 간단한 로직이나 디자인 추가에도 <code>많은 시간이 듬</code></li>
</ol>
<p><img src="https://velog.velcdn.com/images/boyeon_jeong/post/cc05dafb-3a14-443d-b85b-7d14e50935fc/image.png" alt=""></p>
<p>이런 경험을 하면서 만약 <code>설계를 처음부터 잘 했다면 정말 좋았겠다</code> 라는 생각을 하게 되었고, 설계가 정말 중요하다는 사실을 깨달았다. </p>
<h2 id="how">How</h2>
<h3 id="설계-기법의-종류디자인-패턴-아키텍쳐-등등">설계 기법의 종류(디자인 패턴, 아키텍쳐 등등)</h3>
<p>설계 기법은 정말 다양하다. 심지어 유명한 디자인 패턴이나 설계 아키텍쳐도 사용하는 기업이나 개인마다 조금씩 의미를 다르게 두어 사용하거나 프로젝트의 성향에 따라 다르게 사용한다. </p>
<blockquote>
<p>MVC, MVVM, Container-Presenter, Props Drilling과 FLUX 패턴, Atomic, VAC 패턴
Headless Component, Compound Component, Custom Hook</p>
</blockquote>
<p>정말 다양하지만 이러한 패턴들은<code>재사용성을 높이</code>고 <code>기능과 디자인을 분리</code>하는 목적을 추구하고 있다는 것이 <code>공통점</code>이다. </p>
<p>따라서 우선 특정 디자인 패턴, 아키텍쳐 패턴을 사용하겠다는 생각보다는 실제 예시에서 어떻게 재사용성을 높이고 디자인과 기능을 분리할지에 대해 먼저 생각해보았다.</p>
<h3 id="1-재사용성을-높이자">1. 재사용성을 높이자!</h3>
<p><img src="https://velog.velcdn.com/images/boyeon_jeong/post/9d14c2a5-1b57-4852-a863-350d7dff1b50/image.png" alt=""></p>
<p>예를 들어 위와 같은 디자인을 받았다고 가정해보자! 가장 먼저 해당 디자인의 <code>기본 요소(Atoms)</code>를 생각해보면 Input, Button, Text이다. 이렇게 기본 요소를 분리하고 이들을 <code>조합</code>하여 사용한다면 재사용성을 높일 수 있을것이다. </p>
<p><img src="https://velog.velcdn.com/images/boyeon_jeong/post/4bf4fcbd-39e5-49c4-a329-314e4223ca4f/image.png" alt=""></p>
<p>따라서 재사용성을 높이기 위해서는 <code>Atomic Design 패턴</code>을 선택했다.</p>
<h3 id="2-조합방식에-대해-고민해보자">2. 조합방식에 대해 고민해보자!</h3>
<h4 id="공통되는-기능과-디자인">공통되는 기능과 디자인</h4>
<ul>
<li>input의 기본 디자인과 기능</li>
</ul>
<h4 id="다른-디자인">다른 디자인</h4>
<ul>
<li>하단 text(validation)</li>
<li>오른쪽 button</li>
<li>input내부에 value옆에 붙은 문자열(원)</li>
</ul>
<h4 id="다른-기능">다른 기능</h4>
<ul>
<li>버튼 클릭 기능</li>
<li>validation 체크 기능</li>
</ul>
<p>이처럼 공통되는 기능이 있으면서 조금 다른 디자인과 기능을 가진 컴포넌트들의 재사용성을 높이기 위해서는 <code>molecules으로 잘 조합</code>하고 <code>프로젝트 특징</code>에 따라 cutom hook이나 compound component를 사용하면 좋을꺼 같다.
<img src="https://velog.velcdn.com/images/boyeon_jeong/post/096b78a2-6024-415c-93a3-4157c5335f4a/image.png" alt=""></p>
<h3 id="3-정리해보자면">3. 정리해보자면</h3>
<pre><code class="language-javascript">Atomic으로 나누기();
customHook으로 기능 분리();

if(조합해야 하는 경우의 수가 많음 || 반복적으로 쓰이는 횟수가 애매) {
    compound component로 조합 준비();
}

molecules 만들기();
</code></pre>
<p><img src="https://velog.velcdn.com/images/boyeon_jeong/post/9374828c-5291-437d-8b54-831b37887383/image.png" alt=""></p>
<h2 id="result">Result</h2>
<p>위의 방법으로 설계를 한다면 전에 구현했던 방식보다 더 좋은 프로젝트 유지보수가 될꺼 같다. </p>
<p>아직 완벽한 설계방식은 아니지만 <code>내가 추구하는 목적</code>에 한발짝씩 다가가야 겠다. </p>
<blockquote>
<p>추구하는 목적: 재사용이 높으면서 다양한 비즈니스 요구에도 쉽게 적용할 수 있는 구조</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[기업들은 디자인 시스템을 왜 디자인 시스템을 구축할까?]]></title>
            <link>https://velog.io/@boyeon_jeong/%EA%B8%B0%EC%97%85%EB%93%A4%EC%9D%80-%EB%94%94%EC%9E%90%EC%9D%B8-%EC%8B%9C%EC%8A%A4%ED%85%9C%EC%9D%84-%EC%99%9C-%EB%94%94%EC%9E%90%EC%9D%B8-%EC%8B%9C%EC%8A%A4%ED%85%9C%EC%9D%84-%EA%B5%AC%EC%B6%95%ED%95%A0%EA%B9%8C</link>
            <guid>https://velog.io/@boyeon_jeong/%EA%B8%B0%EC%97%85%EB%93%A4%EC%9D%80-%EB%94%94%EC%9E%90%EC%9D%B8-%EC%8B%9C%EC%8A%A4%ED%85%9C%EC%9D%84-%EC%99%9C-%EB%94%94%EC%9E%90%EC%9D%B8-%EC%8B%9C%EC%8A%A4%ED%85%9C%EC%9D%84-%EA%B5%AC%EC%B6%95%ED%95%A0%EA%B9%8C</guid>
            <pubDate>Tue, 05 Dec 2023 02:02:45 GMT</pubDate>
            <description><![CDATA[<p>디자인 시스템은 요즘 여러 기업에서 구축하여 사용하고 있습니다. 대부분 <code>자사의 서비스</code>를 보유하고 있으며 <code>비슷한 디자인 컨셉</code>으로 <code>여러 프로젝트</code>를 진행하는 기업일 수록 더욱 더 디자인 시스템을 활용합니다. </p>
<h2 id="why">Why</h2>
<p>디자인 시스템이 가지는 이점은 다음과 같습니다.</p>
<ol>
<li>재사용성</li>
<li>일관성</li>
<li>확장성, 빠른 신규 개발 가능(<code>빠른 성장</code> 가능)</li>
</ol>
<h2 id="how">How</h2>
<h4 id="1-디자인-원칙-정하기">1. 디자인 원칙 정하기</h4>
<p>디자인 시스템을 구축하기 위해서는 전체적인 디자인 원칙을 잡아야 합니다. 이 원칙을 기반으로 전체적인 서비스 디자인의 <code>일관성을 유지</code>하여 디자인 시스템의 <code>재사용성</code>이 높아질 수 있는 방향으로 나아가야 합니다.</p>
<p>여기서 중요한점은 <code>모든 도메인이나 상황에 쉽게 활용</code> 할 수 있는 <code>디자인 에셋</code>을 만드는 것이 중요합니다. 뿌리가 깊은 나무가 흔들리지 않는 것처럼 <strong>잘 만든 디자인 에셋이 효율적인 디자인 시스템을 만들 수 있습니다</strong>.</p>
<h4 id="2-순서대로-디자인-시스템-구축하기">2. 순서대로 디자인 시스템 구축하기</h4>
<p>위에서 컨셉을 정했다면 하나씩 디자인 시스템을 구축합니다.</p>
<ol>
<li>UI 기본 요소 : 컬러 팔레트, 텍스트 스타일, 크기, 아이콘 등등</li>
<li>UI 구성 요소 : 버튼, 칩, 컨트롤, 인풋 등등</li>
</ol>
<blockquote>
<p>UI 구성 요소를 활용하여 모든 도메인이나 상황에서 자주 사용되는 더 큰 요소도 만들어 재사용하면 좋다. atomic 디자인 패턴으로 생각해봤을때 atomics, molecules, organisms, template 중에서 어디까지 디자인 시스템으로 만들지에 대해 고민해봐도 좋을꺼 같다.</p>
</blockquote>
<h2 id="result">Result</h2>
<p>빠르게 변화하고 빠르게 성장하는 시대에서 <code>빠른 개발</code>은 <code>큰 무기</code>가 될 수 있습니다. 탄탄한 기반이 다져진 디자인 시스템으로 다양한 프로젝트를 쉽게 만들 수 있다면 <code>무한한 성장</code>이 가능할 것입니다.</p>
<blockquote>
<p>참고 자료 : <a href="https://medium.com/coupang-engineering/introducing-coupangs-design-system-baeb117949f1">https://medium.com/coupang-engineering/introducing-coupangs-design-system-baeb117949f1</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[vite로 project 생성하기]]></title>
            <link>https://velog.io/@boyeon_jeong/vite%EB%A1%9C-project-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@boyeon_jeong/vite%EB%A1%9C-project-%EC%83%9D%EC%84%B1%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 04 Dec 2023 07:33:11 GMT</pubDate>
            <description><![CDATA[<p>vite <a href="https://vitejs.dev/guide/">공식 홈페이지</a></p>
<h3 id="npm-create-vite-명령어-실행">npm create vite 명령어 실행</h3>
<pre><code class="language-shell">npm create vite@latest headless-component-app -- --template react</code></pre>
<h3 id="npm-install-및-실행">npm install 및 실행</h3>
<pre><code class="language-shell">npm install
npm run dev</code></pre>
<h3 id="vite의-다른점">vite의 다른점</h3>
<ul>
<li>무조건 <code>.jsx 확장자</code>를 사용해야 한다.</li>
<li>index.html이 public폴더에 없고, root로 빠져있다.</li>
<li>index.js가 아닌 <code>main.jsx</code>를 사용</li>
<li>server를 실행하기 위해서는 <code>npm run dev</code>를 사용한다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/boyeon_jeong/post/3c9208fa-c772-4054-bc99-b2404e7d1061/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Vue.js 상태관리 라이브러리 도입기(Pinia와 VueX를 비교해봅시다!)]]></title>
            <link>https://velog.io/@boyeon_jeong/Vue.js-%EC%83%81%ED%83%9C%EA%B4%80%EB%A6%AC-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-%EB%8F%84%EC%9E%85%EA%B8%B0Pinia%EC%99%80-VueX%EB%A5%BC-%EB%B9%84%EA%B5%90%ED%95%B4%EB%B4%85%EC%8B%9C%EB%8B%A4</link>
            <guid>https://velog.io/@boyeon_jeong/Vue.js-%EC%83%81%ED%83%9C%EA%B4%80%EB%A6%AC-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-%EB%8F%84%EC%9E%85%EA%B8%B0Pinia%EC%99%80-VueX%EB%A5%BC-%EB%B9%84%EA%B5%90%ED%95%B4%EB%B4%85%EC%8B%9C%EB%8B%A4</guid>
            <pubDate>Thu, 30 Nov 2023 10:50:42 GMT</pubDate>
            <description><![CDATA[<h1 id="vuejs-상태관리-라이브러리-도입-배경">Vue.js 상태관리 라이브러리 도입 배경</h1>
<p><img src="https://velog.velcdn.com/images/boyeon_jeong/post/3131a795-3780-4d7c-8f08-05603ec28a07/image.png" alt=""></p>
<p>Vue.js는 하나의 화면에는 실제 수많은 컴포넌트로 설계 되어 구성되어져 있기 때문에 컴포넌트들 간에는 부모 컴포넌트와 자식 컴포넌트의 관계가 존재합니다.</p>
<p>부모-자식 컴포넌트의 관계가 많아질 경우 최상위에 부모 컴포넌트의 state의 값을 변경하기 위해 하위의 있는 자식들은 계속해서 인자로 넘겨 줘야 한다는 꼬리 물기 식의 불필요한 코드가 반복적으로 생길 수 있습니다.</p>
<p>이러한 단점을 보안하기 위해 VUEX, PINIA와 같은 <strong>상태 관리 라이브러리가 등장</strong>했습니다. 이러한 라이브러리들은 모든 컴포넌트에 대한 중 앙 집중식 저장소 역할을 합니다.</p>
<h1 id="상태관리-라이브러리-비교">상태관리 라이브러리 비교</h1>
<p>vue.js의 상태관리 라이브러리로는 VUEX를 흔히 사용하지만, VueConf Toronto 2021 에서 Vue 의 창시자 Evan You 가 직접 등장해 추천 하는 상태 관리 플러그인을 Vuex 가 아닌 Pinia 로 공표하여 PINIA가 주목받고 있는 추세입니다.</p>
<p><img src="https://velog.velcdn.com/images/boyeon_jeong/post/bcc2fd83-a289-4783-a071-3890f6d01c29/image.png" alt=""></p>
<blockquote>
<p><a href="https://npmtrends.com/pinia-vs-vuex">https://npmtrends.com/pinia-vs-vuex</a></p>
</blockquote>
<h2 id="이-둘의-차이점은-크게-3가지로-나눌-수-있습니다">이 둘의 차이점은 크게 3가지로 나눌 수 있습니다.</h2>
<p><img src="https://velog.velcdn.com/images/boyeon_jeong/post/318ad15b-d2c3-4d75-82d7-45335528b9e7/image.png" alt=""></p>
<h3 id="1-composition-api">1. Composition API</h3>
<p>첫번째로 pinia에서는 Vue3의 Composition API에 코드 작성 방식을 제공하고 있습니다. 따라서 기능별로 나누어 개발할 수 있기 때문에 좀 더 논리적 관점에서의 개발이 가능하게 됩니다.
<img src="https://velog.velcdn.com/images/boyeon_jeong/post/5eddf0c0-14d7-4ff4-8411-421dbf5d4fda/image.png" alt=""></p>
<h4 id="1-1-optional">1-1. Optional</h4>
<pre><code class="language-js">// stores/list.js
import { defineStore } from &quot;pinia&quot;;
export const useListStore = defineStore(&quot;list&quot;,{ 
  state:()=&gt;({list:[]}),
  actions: {
    addList(param){
      this.list.push(param); 
    }
    //addList: (param) =&gt; this.list.push(param);
  }, 
  getters: {
      getAllList(state){ 
        return state.list;
      }
      //getAllList: (state) =&gt; state.list
  } 
});</code></pre>
<h4 id="1-2-composition">1-2. Composition</h4>
<pre><code class="language-js">// stores/list.js
import { defineStore } from &quot;pinia&quot;; 
import { ref, computed } from &quot;vue&quot;;
export const useListStore = defineStore(&quot;list&quot;, () =&gt; { 
  const list = ref([]);
  function addList(param) {
      list.value.push(param);
  }
  const getDataAll = computed(() =&gt; list.value);
  return { list, addList, getDataAll };
});</code></pre>
<h3 id="2-생성-가능한-store의-갯수">2. 생성 가능한 Store의 갯수</h3>
<h4 id="2-1-vuex">2-1. VueX</h4>
<p>어플리케이션의 규모가 커지면 관리해야 할 요소의 성격이 완전히 다른 경우가 종종 생깁니다. VueX는 하나의 Store만 가질 수 있기 때문에 이처럼 성격이 다른 경우에도 하나의 파일에서 관리해야 합니다.</p>
<p>이때 VueX에서는 namespaced modules를 이용하여 폴더를 구분한 후 성격에 따른 state와 method를 분리하여 구현할 수 있습니다. 아래와 같 이 폴더를 구분할 경우 폴더 이름이 곧 Vuex 네임스페이스가 됩니다.</p>
<p><img src="https://velog.velcdn.com/images/boyeon_jeong/post/01a7e241-7c53-4639-955a-102e5a71baf6/image.png" alt=""></p>
<p>namespace를 생성한후에 실제 컴포넌트에서는 다음과 같이 사용합니다.</p>
<pre><code class="language-js">&lt;template&gt;
  &lt;Home /&gt;
&lt;/template&gt;
&lt;script&gt;
    import { mapMutations, mapState } from &quot;vuex&quot;;
    export default { 
      name: &quot;MainPage&quot;, 
      components: {
          Home: () =&gt; import(&quot;@/components/pages/home&quot;), 
      },
      methods: {
          //첫번째 인자에 namespace 이름을 넘겨준다. 
        ...mapMutations(&quot;ansData&quot;, [&quot;mutateANSData&quot;]),
      }, 
    };
&lt;/script&gt;</code></pre>
<h4 id="2-2-pinia">2-2. pinia</h4>
<p>pinia는 이와 비교하여 여러 개의 store를 가질 수 있기 때문에 namespaced modules 역시 사용하지 않습니다. 이처럼 Vuex와 비교했을 때 Pinia는 간결한 구조를 가지지만 namespaced 되어 독립된 store 객체를 가진다는 컨셉은 유효합니다</p>
<pre><code class="language-js">import Vue from &quot;vue&quot;;
import { useCartStore } from &quot;./stores/cart&quot;; 
import { useUserStore } from &quot;./stores/user&quot;;

export default defineComponent({ 
  name: &quot;App&quot;,
  setup() {
    const user = useUserStore(); 
    const cart = useCartStore();

    function addItemToCart() { 
      cart.addItem(user.name);
    } 
    ...</code></pre>
<h3 id="3-mutation-유무">3. Mutation 유무</h3>
<h4 id="3-1-vuex">3-1. VueX</h4>
<p>VueX에서는 Store라는 저장소에 컴포넌트 간 공유할 data 속성인 state를 정의합니다. 이렇게 정의한 state 데이터를 변경할 수 있는 로직을 각각 Component에서 정의할 수도 있지만, 공통되는 로직을 매번 따로 정의할 경우, 가독성이 떨어지고 성능이 떨어질 수 있습니다. 따라서 Mutations, Actions, Getters 를 사용하여 공통으로 사용되는 로직을 한곳에서 관리합니다.</p>
<p>이 3가지는 공통으로 사용되는 로직을 한곳에서 관리한다는 공통점이 가지지만, 아래와 같은 차이점을 가지고 있습니다.</p>
<p><img src="https://velog.velcdn.com/images/boyeon_jeong/post/07f0fb77-3d0c-4640-8e73-c944c9a4ef65/image.png" alt=""></p>
<p>Mutations 의 성격상 안에 정의한 로직들이 순차적으로 일어나야 각 컴포넌트의 반영 여부를 제대로 추적할 수가 있기 때문에 동기적인 작업 만 수행할 수 있습니다. 따라서 비동기적인 로직을 수행하기 위해서는 Action 내부에 Mutations 을 commit하는 등의 작업을 추가로 진행해야 한다.
<img src="https://velog.velcdn.com/images/boyeon_jeong/post/1b143916-9554-411e-a553-763488a372fd/image.png" alt=""></p>
<pre><code class="language-js">/* store.js */
import Vue from &#39;vue&#39; import Vuex from &#39;vuex&#39;
Vue.use(Vuex);
export default new Vuex.Store({ state: {
count: 0, },
    mutations: {
        increment(state) {
            state.count++;
        },
        decrement(state) {
            state.count--;
} },
    actions: {
      incrementAsync ({ commit }) {
        setTimeout(() =&gt; {
          commit(&#39;increment&#39;)
}, 1000) }
} })</code></pre>
<h4 id="3-2-pinia">3-2. pinia</h4>
<p>pinia에서는 비동기적, 동기적 로직 모두 action에서 수행할 수 있기 때문에 mutations이 없습니다. 따라서 불필요한 코드가 줄고, 좀 더 간결한 코드를 작성할 수 있습니다.</p>
<pre><code class="language-js">// stores/list.js
import { defineStore } from &quot;pinia&quot;;
export const useListStore = defineStore(&quot;list&quot;,{ state:()=&gt;({count:0}),
actions: {
    incrementAsync () {
        setTimeout(() =&gt; {
          state.count++;
        }, 1000)
} }
});</code></pre>
<hr>
<h3 id="우리-프로젝트에는-pinia를-도입하기로-결정">우리 프로젝트에는 Pinia를 도입하기로 결정..!</h3>
<p>내가 진행했던 프로젝트에서 Pinia를 도입한 이유를 다시 정리해보면 다음과 같습니다!</p>
<ol>
<li><p><strong>Composition API 활용</strong>: Pinia는 Vue 3의 Composition API를 적극적으로 활용하여 코드를 작성할 수 있게 해줍니다. 이는 기능을 논리적으로 나누어 개발할 수 있게 하며, 더 직관적이고 효율적인 코드 작성이 가능하게 합니다.</p>
</li>
<li><p><strong>다중 Store 지원</strong>: Pinia는 여러 개의 Store를 가질 수 있어서 애플리케이션 규모가 크고 성격이 다른 부분을 더 효과적으로 관리할 수 있습니다. 이는 Vuex에서는 namespaced modules를 사용해야 하는데, Pinia에서는 별도의 네임스페이스 없이 여러 개의 독립된 Store를 사용할 수 있습니다.</p>
</li>
<li><p><strong>간결하고 명확한 코드</strong>: Pinia는 비동기 및 동기적 로직을 모두 처리할 수 있는 actions를 제공하며, mutations가 없어서 코드가 더 간결해지고 가독성이 향상됩니다. 이는 특히 비동기 작업을 처리할 때 불필요한 코드를 줄일 수 있게 도와줍니다.</p>
</li>
</ol>
<p>이로 인해 Pinia를 도입함으로써 프로젝트의 유지보수성과 확장성을 향상시키며, 개발 생산성을 높일 수 있었습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[useEffect 활용하기]]></title>
            <link>https://velog.io/@boyeon_jeong/use-Effect</link>
            <guid>https://velog.io/@boyeon_jeong/use-Effect</guid>
            <pubDate>Tue, 28 Nov 2023 08:58:08 GMT</pubDate>
            <description><![CDATA[<h2 id="useeffect-조건부-랜더링">useEffect 조건부 랜더링</h2>
<pre><code class="language-jsx">const FetchDate = () =&gt; {
    const [isLoading, setIsLoading] = useState(true);
      const [isError, setIsError] = useState(false);
      const [user, setUser] = useState(null);


      //당연한 소리겠지만 민약에 return을 하게 되면 더이상 밑의 코드가 실행되지 않기 때문에 아래 useEffect는 실행되지 않습니다!
      if(isLoading){
        return &lt;h2&gt;Loding...&lt;/h2&gt;;
    }

      //최초 랜더링때 fectch data 불러오기
      useEffect(()=&gt;{
        const fetchUser = async() =&gt; {
            try{
                const resp = await fetch(url);
                  //fetch에서는 404를 에러로 판단하지 않음..
                  if(!resp.ok) {
                    setIsError(true);
                      setIsLoading(false);
                      return;
                }
                  const user = await resp.json();
                  setUser(user);
            } catch (error) {
                setIsError(true);
            }
            setIsLoading(false);
        }
        fetchUser();
    }, []);

      //조건부 랜더링
      if(isLoading){
        return &lt;h2&gt;Loding...&lt;/h2&gt;;
    }
      if(isError){
        return &lt;h2&gt;There was an error...&lt;/h2&gt;;
    }

      //일반적인 return(조건에 걸리지 않을때!)
      return (
        &lt;section&gt;
            &lt;h2&gt;{user.name}&lt;/h2&gt;
          &lt;/section&gt;
    )
}
</code></pre>
<h2 id="mounted-unmounted-일때-함수-실행">mounted, unmounted 일때 함수 실행</h2>
<h4 id="기본-예제">기본 예제</h4>
<pre><code class="language-jsx">const ToggleButton = () =&gt; {
    const [toggle, setToggle] = useState(false);

      return (
        &lt;div&gt;
            &lt;button onClick={()=&gt;setToggle(!toggle)}&gt;
              toggle component
            &lt;/button&gt;
            {toggle &amp;&amp; &lt;RandomComponent /&gt;}
          &lt;/div&gt;
    );
}

const RandomComponent = () =&gt; {
      //useEffect에 의존성 배열은 빈 배열로 주면 최초 랜더링될때만 함수가 실행된다.
    useEffect(()=&gt;{
          const intId = setInterval(()=&gt;{
              console.log(&#39;hello from interval&#39;);
        }, 1000);

          //useEffect 내부에서 리턴한 콜백은 언마운트시 실행된다.
          return () =&gt; {
            clearInterval(intId);
        }
    }, []);

      return &lt;h1&gt;Random Component&lt;/h1&gt;;
}

export default ToggleButton;</code></pre>
<h4 id="window-eventlistner">window eventListner</h4>
<pre><code class="language-jsx">const RandomComponent = () =&gt; {
    useEffect((()=&gt;{
        const someFunc = () =&gt; {
            ...
        }
        //만약 이벤트 등록만 하게 되면 해당 함수가 계속해서 쌓기게 된다.
        window.addEventListner(&#39;scroll&#39;, someFunc);
        //그렇기 떄문에 unmounted 할때 반드시 remove 해줘야 한다.
        window.removeEventListner(&#39;scroll&#39;, someFunc);
    }), []);

    return &lt;h1&gt;hello&lt;/h1&gt;;
}</code></pre>
<h2 id="docs-읽어보기">Docs 읽어보기</h2>
<p><a href="https://react.dev/learn/you-might-not-need-an-effect">https://react.dev/learn/you-might-not-need-an-effect</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React Docs Deep Dive] useState]]></title>
            <link>https://velog.io/@boyeon_jeong/React-Docs-Deep-Dive-useState</link>
            <guid>https://velog.io/@boyeon_jeong/React-Docs-Deep-Dive-useState</guid>
            <pubDate>Tue, 28 Nov 2023 01:48:03 GMT</pubDate>
            <description><![CDATA[<p>react를 실제 프로젝트에서 사용하면서 여러 hook을 사용했었습니다. 보다 hook에 대해 정확히 이해하며 react에 대해서도 좀 더 deep하게 알아보는 시간을 가지려고 합니다.(트러블 슈팅과 효율적인 프로젝트 수행을 위해서)</p>
<p>저는 react docs의 reference를 참조하였습니다.</p>
<h1 id="hook">Hook</h1>
<p>먼저 전체적인 hook의 공통점에 대해 살펴보겠습니다.</p>
<ul>
<li>hook은 반복문이나 조건절에서 call 할 수 없습니다. 반드시 컴포넌트의 top level에서 call 해야 합니다.</li>
</ul>
<h1 id="usestate">useState</h1>
<p>useState는 컴포넌트의 <a href="https://react.dev/learn/state-a-components-memory">state</a>를 추가할 수 있게 해주는 React의 Hook입니다. 기본 형태는 아래와 같습니다.</p>
<pre><code class="language-jsx">const [something, setSomething] = useState(initialState);</code></pre>
<blockquote>
<p>set function이란 위의 예시에서 setSomthing처럼 useState의 return값을 의미합니다. 이 함수는 state를 업데이트 시켜주고 리랜더링을 트리거 합니다. </p>
</blockquote>
<h2 id="📌-usestate-deep-dive">📌 useState Deep Dive</h2>
<h3 id="1️⃣-set-function은-반드시-다음-랜더에-state를-업데이트-해줍니다">1️⃣ set function은 반드시! 다음 랜더에 state를 업데이트 해줍니다.</h3>
<p>만약에 같은 set function에서 state를 읽으려고 한다면 방금 업데이트 한 값이 아닌 이전 값이 읽어지게 됩니다. 그 이유는 react는 set function을 모든 이벤트 핸들러가 끝난후 <strong>일괄 수행(batch)</strong> 해주기 때문에 <strong>이미 실행된 코드(실행 컨테스트에서 push, pop)된</strong> 부분에서는 set 함수가 적용되지 않습니다.</p>
<pre><code class="language-jsx">function handleClick() {
  console.log(count);  // 0

  setCount(count + 1); // Request a re-render with 1
  console.log(count);  // Still 0!

  setTimeout(() =&gt; {
    console.log(count); // Also 0!
  }, 5000);
}</code></pre>
<h3 id="2️⃣-set-function-내부에서-updater-funtion을-지정해주면-이전값을-반영하여-계산한다">2️⃣ set function 내부에서 updater funtion을 지정해주면 이전값을 반영하여 계산한다.</h3>
<pre><code class="language-jsx">function handleClick() {
  setAge(age + 1); // setAge(42 + 1)
  setAge(age + 1); // setAge(42 + 1)
  setAge(age + 1); // setAge(42 + 1)
}

function handleClick() {
  setAge(a =&gt; a + 1); // setAge(42 =&gt; 43)
  setAge(a =&gt; a + 1); // setAge(43 =&gt; 44)
  setAge(a =&gt; a + 1); // setAge(44 =&gt; 45)
}</code></pre>
<p>만약 이전 state를 기반으로 set function을 업데이트 해주고 싶다면 set function <strong>내부에 updater function을 넘겨주면 됩니다.</strong> 보통 만약 state가 age라면 첫글자인 a를 argument 이름으로 짓는데 prevAge, prevValue 등등 맘대로 지어도 됩니다..!</p>
<blockquote>
<p><a href="https://fe-j.tistory.com/entry/setState-%ED%95%A8%EC%88%98%ED%98%95-%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8%EB%A1%9C">내부 동작</a></p>
</blockquote>
<h3 id="3️⃣-만약-같은-값을-update해주면-리랜딩-되지-않습니다">3️⃣ 만약 같은 값을 update해주면 리랜딩 되지 않습니다.</h3>
<p>만약 Object.is 비교로 같은 값을 업데이트 해주게 되면 리액트는 최적화를 위해서 리 랜더링을 스킵합니다.</p>
<h3 id="4️⃣-react는-state-업데이트를-일괄처리-합니다batch">4️⃣ react는 state 업데이트를 일괄처리 합니다.(<a href="https://react.dev/learn/queueing-a-series-of-state-updates">batch</a>)</h3>
<p>리액트는 중복적인 리랜더링을 방지하기 위해서 모든 event handler가 모두 수행된 후에 set function들을 한번에 수행합니다. (만약 강제로 일찍 업데이트 해야 한다면 <a href="https://react.dev/reference/react-dom/flushSync">flushSync</a>를 사용)</p>
<h3 id="5️⃣-렌더링-중에-상태를-업데이트-해야-하는-경우">5️⃣ 렌더링 중에 상태를 업데이트 해야 하는 경우!</h3>
<p>일반적으로 리액트에서 상태는 이벤트 핸들러나 라이프사이클 메소드를 통해 이루어집니다. 그런데 어떠한 상황에서는 랜더링 중에 상태를 업데이트 해야 하는 경우가 존재합니다.</p>
<p>그 대표적인 예시로 props에 넘어온 값을 상태로 업데이트 해주어야 하는 경우입니다. 이러한 경우 현재 넘어온 props와 현재의 state값을 비교하여 다른 경우(상태를 추척)에 상태를 업데이트 해줍니다. </p>
<pre><code class="language-jsx">import { useState } from &#39;react&#39;;

function CounterDisplay({ count }) {
  const [prevCount, setPrevCount] = useState(0);

  if (prevCount !== count) {
    setPrevCount(count);
    console.log(`Count changed from ${prevCount} to ${count}`);
  }

  return &lt;div&gt;{count}&lt;/div&gt;;
}
</code></pre>
<p>이렇게 동작할때에는 React는 해당 컴포넌트의 랜더링을 즉시 취소하고 새로운 상태로 다시 랜더링을 합니다. 그래서 두 번의 완전한 렌더링이 발생하는 것이 아니라, 렌더링이 중간에 중단되고 다시 시작되는 형태입니다.</p>
<p>여기서 주의해야 할 점은 이 패턴이 오동작하거나 무한 루프에 빠질 수 있는 상황이 발생할 수 있으므로, 사용 시에는 신중해야 합니다. 이 패턴은 특별한 상황에서만 필요하며, 일반적인 경우에는 피하는 것이 좋습니다.</p>
<h3 id="6️⃣-무한-로딩-될-수-있는-코드">6️⃣ 무한 로딩 될 수 있는 코드</h3>
<pre><code class="language-jsx">const Example = () =&gt; {
    const [value, setValue] = useState(0);

      const sayHello = () =&gt; {
        console.log(&#39;hello&#39;);
          setValue(value+1);
    }

    sayHello();
      return (
      &lt;div&gt;
        &lt;h1&gt;value: {value}&lt;/h1&gt;
      &lt;/div&gt;
    ); 
};

export default Example;</code></pre>
<p>example component내부에서 sayHello함수가 실행되는데 이 함수는 리랜더링 될때마다 실행이 됩니다. 그리고 setState는 리랜더링을 트리거합니다. 따라서 랜더링 될때마다 호출되는 sayHello 내부에 랜더링을 트리거 하는 set function이 존재하면 무한 루프에 빠지게 됩니다.</p>
<blockquote>
<p>결론적으로 useState를 사용할때 신경써야할 부분을 정리해보면 아래와 같다</p>
</blockquote>
<ol>
<li>Object.is 비교시 같으면 리랜더링이 안된다.</li>
<li>set function 내부에서 즉각적으로 상태를 반영해야 한다면 콜백 함수로 작성한다.</li>
<li>무한 루프에 빠지지 않도록 주의한다</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[이벤트 루프와 태스크 큐]]></title>
            <link>https://velog.io/@boyeon_jeong/%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A3%A8%ED%94%84%EC%99%80-%ED%83%9C%EC%8A%A4%ED%81%AC-%ED%81%90</link>
            <guid>https://velog.io/@boyeon_jeong/%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A3%A8%ED%94%84%EC%99%80-%ED%83%9C%EC%8A%A4%ED%81%AC-%ED%81%90</guid>
            <pubDate>Mon, 27 Nov 2023 08:45:58 GMT</pubDate>
            <description><![CDATA[<h2 id="자바스크립트-엔진-동작비동기-❌">자바스크립트 엔진 동작(비동기 ❌)</h2>
<p>자바스크립트 엔진은 싱글 스레드로 동작합니다. 자바스크립트 엔진은 <strong>콜스택과 힙으로 구성</strong>되어 있습니다. 이렇게 이루어진 엔진은 콜 스택을 통해 요청된 작업을 순차적으로 실행합니다. 결론적으로 자바스크립트 엔진만으로는 비동기 작업을 수행할 수 없습니다.</p>
<blockquote>
<ul>
<li>콜 스택: 실행 컨텍스트 스택</li>
</ul>
</blockquote>
<ul>
<li>힙: 객체가 저장되는 메모리 공간(실행 컨테스트에서 참조)</li>
</ul>
<p><img src="https://velog.velcdn.com/images/boyeon_jeong/post/f77b3bd7-0f1f-4c4e-b499-60e75c7e2e08/image.png" alt=""></p>
<h2 id="자바스크립트-엔진-구동하는-환경-동작비동기-🅾️">자바스크립트 엔진 구동하는 환경 동작(비동기 🅾️)</h2>
<p>따라서 비동기 동작은 자바스크립트 엔진이 구동하는 환경인 브라우저 또는 node.js에서 담당합니다. </p>
<p>브라우저로 예를 들어보면 브라우저는 <strong>태스크 큐와 이벤트 루프</strong>를 제공합니다. </p>
<blockquote>
<ul>
<li>태스크 큐: 비동기 함수가 일시적으로 보관되는 영역</li>
</ul>
</blockquote>
<ul>
<li>이벤트 루프: 콜 스택과 태스크 큐를 반복적으로 확인하는 작업을 함. 콜 스텍이 비었고, 태스크 큐에 함수가 있으면 태스큐에 있는 함수를 콜 스택으로 이동시킨다.</li>
</ul>
<h2 id="동작-순서-예시">동작 순서 예시</h2>
<p><img src="https://velog.velcdn.com/images/boyeon_jeong/post/37fadadf-f499-4023-8e4d-2fad8605812f/image.gif" alt=""></p>
<p>gif:  <a href="https://talkwithcode.tistory.com/89">https://talkwithcode.tistory.com/89</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[vite가 서버가동과 피드백(개발 속도)이 빠른 이유]]></title>
            <link>https://velog.io/@boyeon_jeong/vite%EA%B0%80-%EC%84%9C%EB%B2%84%EA%B0%80%EB%8F%99%EA%B3%BC-%ED%94%BC%EB%93%9C%EB%B0%B1%EA%B0%9C%EB%B0%9C-%EC%86%8D%EB%8F%84%EC%9D%B4-%EB%B9%A0%EB%A5%B8-%EC%9D%B4%EC%9C%A0</link>
            <guid>https://velog.io/@boyeon_jeong/vite%EA%B0%80-%EC%84%9C%EB%B2%84%EA%B0%80%EB%8F%99%EA%B3%BC-%ED%94%BC%EB%93%9C%EB%B0%B1%EA%B0%9C%EB%B0%9C-%EC%86%8D%EB%8F%84%EC%9D%B4-%EB%B9%A0%EB%A5%B8-%EC%9D%B4%EC%9C%A0</guid>
            <pubDate>Mon, 20 Nov 2023 04:01:43 GMT</pubDate>
            <description><![CDATA[<p>javascript 프로젝트의 규모가 커짐으로써 모듈의 개수가 늘어나고 있습니다. 현재 javascript기반의 도구는 성능 병목 현상이 발생되었습니다. 이로인하여 아래와 같은 문제가 발생합니다.</p>
<ol>
<li>서버 가동하는데 오랜 시간이 걸림</li>
<li>느린 피드백(HMR을 사용해도)</li>
</ol>
<p><strong>그 결과 개발자의 생산성이 현저히 떨어지게 됩니다.</strong></p>
<h2 id="vite가-이전-도구들과-다른점">vite가 이전 도구들과 다른점</h2>
<h3 id="1-사전-번들링">1. 사전 번들링</h3>
<p>기존도구들은 먼저 번들링 된 후에야 서버를 구동할 수 있었습니다. 그로인해서 모듈이 커질수록 낮은 서버 구동 속도를 가지게 됩니다. 이를 개선할 수 있도록 *<em>vite는 사전 번들링을 제공합니다. *</em></p>
<p>vite는** plain javascript(라이브러리 소스, dependencies)는 사전 번들링 ** 합니다. 이때 <strong>esbuild</strong> 도구를 사용하는데 이 도구는 Webpack, Parcel과 같은 기존의 번들러 대비 <strong>10-100배 빠른 속도를 제공합니다.</strong></p>
<p><img src="https://velog.velcdn.com/images/boyeon_jeong/post/2669a556-093e-48d7-8ac1-d0da8ab9eb93/image.png" alt=""></p>
<blockquote>
<p>출처: <a href="https://ko.vitejs.dev/guide/why.html">https://ko.vitejs.dev/guide/why.html</a></p>
</blockquote>
<h3 id="2-브라우저에서-지원하는-es-modulesesm-사용">2. 브라우저에서 지원하는 ES Modules(ESM) 사용</h3>
<p>vite는 non-plain javascript(Source code)는 native ESM를 사용한다. 이것은 본질적으로 브라우저가 번들러의 작업의 일부를 차지할 수 있도록 합니다. </p>
<blockquote>
<p>native-esm은 중복 import로 인한 중복 네트워크 통신이 발생할 수 있습니다. 이로인하여 여전히 비 효율적인 부분이 있습니다. 이러한 비효율성을 개선하려면 트리 세이킹, 지연 로딩 및 파일 분할 등의 작업이 필요합니다. 이를 위해서는 번들링 과정이 여전히 필요합니다. vite는 이때 rollup을 사용합니다.</p>
</blockquote>
<h2 id="vite의-개발환경과-프로덕션-환경에서의-번들링">vite의 개발환경과 프로덕션 환경에서의 번들링</h2>
<p>Vite는 개발과 프로덕션 환경에서 다르게 동작하는데, 이를 통해 <strong>개발 중에는 빠른 개발 속도를 유지하면서 프로덕션 환경에서는 최적화된 번들을 생성</strong>할 수 있습니다.</p>
<ol>
<li><p><strong>개발 환경:</strong></p>
<ul>
<li>개발 서버에서는 Hot Module Replacement (HMR)을 활용하여 빠른 반복 개발을 지원합니다.</li>
<li>코드는 필요할 때마다 브라우저에서 동적으로 컴파일되고 제공되므로 빠른 개발 환경을 제공합니다.</li>
<li>브라우저가 요청하는 파일이나 모듈이 변경될 때, 필요한 부분만 다시 컴파일되고 갱신됩니다.</li>
</ul>
</li>
<li><p><strong>프로덕션 환경:</strong></p>
<ul>
<li>프로덕션 빌드에서는 Vite는 트리쉐이킹(Tree-Shaking)과 같은 최적화 기술을 사용하여 불필요한 코드를 제거하고 최종 번들을 최적화합니다.</li>
<li>코드 분할(Splitting)을 통해 사용되지 않는 코드는 번들에 포함되지 않도록 합니다.</li>
<li>최종 결과물은 프로덕션 환경에 최적화된 작은 번들로 생성됩니다.</li>
</ul>
</li>
</ol>
<blockquote>
<p>개발 환경에서도 Vite는 코드 분할(Code Splitting)을 적극적으로 활용하고, 트리 쉐이킹(Tree-Shaking)과 같은 최적화 기술을 일부 적용합니다. 하지만 <strong>이러한 최적화는 주로 프로덕션 빌드에서 더 강력하게 작동</strong>합니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[모듈번들러를 사용하면 왜 어플리케이션의 성능이 향상될까?]]></title>
            <link>https://velog.io/@boyeon_jeong/%EB%AA%A8%EB%93%88%EB%B2%88%EB%93%A4%EB%9F%AC%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%A9%B4-%EC%99%9C-%EC%96%B4%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EC%9D%98-%EC%84%B1%EB%8A%A5%EC%9D%B4-%ED%96%A5%EC%83%81%EB%90%A0%EA%B9%8C</link>
            <guid>https://velog.io/@boyeon_jeong/%EB%AA%A8%EB%93%88%EB%B2%88%EB%93%A4%EB%9F%AC%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%A9%B4-%EC%99%9C-%EC%96%B4%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98%EC%9D%98-%EC%84%B1%EB%8A%A5%EC%9D%B4-%ED%96%A5%EC%83%81%EB%90%A0%EA%B9%8C</guid>
            <pubDate>Sun, 19 Nov 2023 04:32:58 GMT</pubDate>
            <description><![CDATA[<h2 id="모듈-번들러란">모듈 번들러란?</h2>
<p>프론트단에서는 webpack, rollup 등의 모듈 번들러를 사용합니다. 모듈 번들러는 <strong>여러 가지 모듈들을 하나의 번들로 묶어서 최적화된 형태</strong>로 전송하고, 웹 애플리케이션의 성능을 향상시키는데에 사용됩니다. 모듈 번들러와 관련된 <a href="https://npmtrends.com/esbuild-vs-parcel-vs-rollup-vs-snowpack-vs-vite-vs-webpack">트렌드</a>는 아래와 같습니다. </p>
<p><img src="https://velog.velcdn.com/images/boyeon_jeong/post/228057f5-2690-474a-b3d1-aa35b36cb171/image.png" alt=""></p>
<blockquote>
<p>Esbuild, Rollup 및 Webpack은 모듈 번들러이자 빌드 도구로 사용되며, Parcel, Snowpack, Vite는 주로 빠른 개발 환경을 제공하면서 번들링도 수행하는 도구입니다.</p>
</blockquote>
<h2 id="모듈번들러를-사용하면-왜-어플리케이션의-성능이-향상될까">모듈번들러를 사용하면 왜 어플리케이션의 성능이 향상될까?</h2>
<p>그렇다면 모듈번들러를 사용하면 왜 어플리케이션의 성능이 향상될까요? 그 이유에 대해 알아봅시다!</p>
<h4 id="1-네트워크-성능-개선">1. 네트워크 성능 개선</h4>
<p>여러 개의 모듈이나 파일을 <strong>별도로 로드하면 각각의 HTTP 요청</strong>이 발생하게 되어 <strong>네트워크 부하가</strong> 발생할 수 있습니다. 번들링을 통해 모듈들을 하나로 묶어 전송하면, <strong>HTTP 요청의 수를 줄이고 네트워크 성능을 개선</strong>할 수 있습니다.</p>
<h4 id="2-리소스-최소화">2. 리소스 최소화</h4>
<p>번들러는 <strong>불필요한 코드를 제거하고 코드를 압축하여 번들의 크기를 최소화</strong>합니다. 이는 브라우저가 <strong>더 빨리 번들을 다운로드하고 해석</strong>할 수 있도록 도움을 줍니다.</p>
<blockquote>
<p>트리 쉐이킹, 압축(UglifyJS, Terser 등과 같은 도구)</p>
</blockquote>
<h4 id="3-모듈-간의-의존성을-파악하여-최적화된-순서-적용">3. 모듈 간의 의존성을 파악하여 최적화된 순서 적용</h4>
<p>번들러는 <strong>모듈 간의 의존성을 파악</strong>하고, 이를 고려하여 <strong>최적화된 순서</strong>로 모듈을 번들로 묶습니다. 이는 브라우저에서 모듈을 효율적으로 로드하고 실행할 수 있도록 돕습니다.</p>
<blockquote>
</blockquote>
<p><strong>모듈 A:</strong></p>
<pre><code class="language-javascript">// 모듈 A
import { foo } from &#39;./moduleB&#39;;
export function myFunction() {
  return foo();
}</code></pre>
<p><strong>모듈 B:</strong></p>
<pre><code class="language-javascript">// 모듈 B
export function foo() {
  return &#39;Hello from module B&#39;;
}</code></pre>
<p><strong>모듈 C:</strong></p>
<pre><code class="language-javascript">// 모듈 C
import { myFunction } from &#39;./moduleA&#39;;
console.log(myFunction());</code></pre>
<p>이 상황에서 번들러는 다음과 같은 순서로 모듈을 번들로 묶을 수 있습니다.<br/></p>
<ol>
<li><strong>모듈 B 번들링:</strong> 모듈 B는 의존성이 없으므로 먼저 번들링됩니다.</li>
<li><strong>모듈 A 번들링:</strong> 모듈 A는 모듈 B에 의존하고 있으므로, 번들러는 모듈 B의 번들이 먼저 로드되도록 순서를 조정하여 모듈 A를 번들링합니다.</li>
<li><strong>모듈 C 번들링:</strong> 모듈 C는 모듈 A에 의존하고 있습니다. 번들러는 모듈 A의 번들이 먼저 로드되도록 순서를 조정하여 모듈 C를 번들링합니다.</li>
</ol>
<ol start="3">
<li><strong>브라우저 캐싱 활용:</strong>
번들링된 파일은 하나의 파일로 묶여 있기 때문에 브라우저에서 캐싱을 효과적으로 활용할 수 있습니다. 변경된 모듈만 새로 다운로드하고, 나머지는 캐시를 활용하여 로드할 수 있어 초기 로딩 속도를 개선합니다.</li>
</ol>
<blockquote>
<p>TODO: <a href="https://sinnerr0.medium.com/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EB%B2%88%EB%93%A4%EB%A7%81%EC%9D%98-%EC%A7%84%ED%99%94-c951444e5fdf">https://sinnerr0.medium.com/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C-%EB%B2%88%EB%93%A4%EB%A7%81%EC%9D%98-%EC%A7%84%ED%99%94-c951444e5fdf</a></p>
</blockquote>
]]></description>
        </item>
    </channel>
</rss>