<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>m.log</title>
        <link>https://velog.io/</link>
        <description>FE moon</description>
        <lastBuildDate>Wed, 12 Mar 2025 16:49:39 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>m.log</title>
            <url>https://velog.velcdn.com/images/river-m/profile/98603f36-4460-4e9a-b967-64d123248451/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. m.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/river-m" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Tailwind CSS v4] @apply 인식 오류 해결하기]]></title>
            <link>https://velog.io/@river-m/Tailwind-CSS-v4-apply-%EC%9D%B8%EC%8B%9D-%EC%98%A4%EB%A5%98-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@river-m/Tailwind-CSS-v4-apply-%EC%9D%B8%EC%8B%9D-%EC%98%A4%EB%A5%98-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 12 Mar 2025 16:49:39 GMT</pubDate>
            <description><![CDATA[<h2 id="🔥-문제-개요">🔥 문제 개요</h2>
<p>오늘 Tailwind CSS v4를 처음 적용한 프로젝트에서 아래와 같은 오류가 발생했습니다.</p>
<h3 id="❌-오류-메시지-예시"><strong>❌ 오류 메시지 예시</strong></h3>
<pre><code class="language-bash">Error: Cannot apply unknown utility class: bg-primary-500
</code></pre>
<p>이 오류는 Tailwind CSS v4에서 <code>@apply</code>가 글로벌 범위에서 자동으로 적용되지 않기 때문에 발생합니다.</p>
<hr>
<p>** ✅ Tailwind CSS v4에서 <code>@theme</code>을 활용한 변수 정의
**
Tailwind CSS v4에서는 <code>@theme</code>을 사용하여 CSS 변수 형태로 색상, 폰트, 패딩 등을 정의할 수 있습니다.</p>
<h3 id="🔹-globalscss에서-theme-정의-예시"><strong>🔹 <code>globals.css</code>에서 <code>@theme</code> 정의 예시</strong></h3>
<pre><code class="language-css">@theme {
  --color-primary-50: oklch(0.982 0.018 155.826);
  --color-primary-100: oklch(0.962 0.044 156.743);
  --color-primary-200: oklch(0.925 0.084 155.995);
  --color-primary-300: oklch(0.871 0.15 154.449);
  --color-primary-400: oklch(0.792 0.209 151.711);
  --color-primary-500: oklch(0.723 0.219 149.579);
  --color-primary-600: oklch(0.627 0.194 149.214);
  --color-primary-700: oklch(0.527 0.154 150.069);
  --color-primary-800: oklch(0.448 0.119 151.328);
  --color-primary-900: oklch(0.393 0.095 152.535);
  --color-primary-950: oklch(0.266 0.065 152.934);
}
</code></pre>
<hr>
<p><code>@theme</code>에서 정의한 변수를 다른 CSS 파일에서 <code>@apply</code>로 사용하려면 <strong>단순히 <code>@apply bg-primary-500</code>을 추가하는 것만으로는 동작하지 않습니다.</strong></p>
<p>Tailwind v4에서는 <code>@apply</code>가 글로벌 적용되지 않으므로, 다른 CSS 파일에서 <code>@apply</code>를 사용하려면 <code>@reference</code>를 추가해야 합니다.</p>
<h3 id="🔹-componentscss에서-reference-사용-예시"><strong>🔹 <code>components.css</code>에서 <code>@reference</code> 사용 예시</strong></h3>
<pre><code class="language-css">@reference &quot;./globals.css&quot;;  /* Tailwind가 정의된 파일을 참조 */

.btn-primary {
  @apply bg-primary-500 text-white font-bold py-2 px-4 rounded;
}
/* bg-primary-500 같은 @theme에서 정의된 변수들은 @reference를 추가해야 정상적으로 적용됩니다. */
</code></pre>
<p>✅ <strong>이제 Tailwind가 <code>@apply bg-primary-500</code>을 정상적으로 인식할 수 있습니다!</strong></p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[코딩테스트: 문자열을 n번 반복하는 다양한 방법]]></title>
            <link>https://velog.io/@river-m/%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EB%AC%B8%EC%9E%90%EC%97%B4%EC%9D%84-n%EB%B2%88-%EB%B0%98%EB%B3%B5%ED%95%98%EB%8A%94-%EB%8B%A4%EC%96%91%ED%95%9C-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@river-m/%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EB%AC%B8%EC%9E%90%EC%97%B4%EC%9D%84-n%EB%B2%88-%EB%B0%98%EB%B3%B5%ED%95%98%EB%8A%94-%EB%8B%A4%EC%96%91%ED%95%9C-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Tue, 03 Dec 2024 13:18:17 GMT</pubDate>
            <description><![CDATA[<p>자바스크립트에서 문자열을 여러 번 반복하는 작업은 간단하면서도 다양한 방법으로 구현할 수 있다. 아래는 문자열 string을 3번 반복하여 stringstringstring을 만드는 다양한 방법을 소개한다.</p>
<hr>
<h2 id="1-repeat-메서드-사용">1. repeat() 메서드 사용</h2>
<p>ES6부터 제공되는 repeat() 메서드는 가장 간단하고 직관적인 방법이다.</p>
<pre><code class="language-js">function solution(my_string, k) {
    return my_string.repeat(k);
}

// 예시
console.log(solution(&quot;string&quot;, 3)); // stringstringstring</code></pre>
<hr>
<h2 id="2-for-반복문-사용">2. for 반복문 사용</h2>
<p>for문을 활용해 반복적으로 문자열을 누적하는 방법이다.</p>
<pre><code class="language-js">function solution(my_string, k) {
    let result = &quot;&quot;;
    for (let i = 0; i &lt; k; i++) {
        result += my_string;
    }
    return result;
}

// 예시
console.log(solution(&quot;string&quot;, 3)); // stringstringstring</code></pre>
<hr>
<h2 id="3-array와-join-사용">3. Array와 join() 사용</h2>
<p>배열과 join() 메서드를 활용해 반복 문자열을 생성하는 방법이다.</p>
<pre><code class="language-js">function solution(my_string, k) {
    return Array(k).fill(my_string).join(&quot;&quot;);
}

// 예시
console.log(solution(&quot;string&quot;, 3)); // stringstringstring</code></pre>
<hr>
<h2 id="4-재귀-함수-사용">4. 재귀 함수 사용</h2>
<p>재귀를 활용한 방식으로 문자열을 반복적으로 더해가는 풀이다.</p>
<pre><code class="language-js">function solution(my_string, k) {
    if (k &lt;= 0) return &quot;&quot;;
    return my_string + solution(my_string, k - 1);
}

// 예시
console.log(solution(&quot;string&quot;, 3)); // stringstringstring</code></pre>
<hr>
<h2 id="5-reduce-사용">5. reduce() 사용</h2>
<p>Array와 reduce()를 활용해 문자열을 누적하는 방식이다.</p>
<pre><code class="language-js">function solution(my_string, k) {
    return Array(k).fill(my_string).reduce((acc, curr) =&gt; acc + curr, &quot;&quot;);
}

// 예시
console.log(solution(&quot;string&quot;, 3)); // stringstringstring</code></pre>
<hr>
<h2 id="6-while-반복문-사용">6. while 반복문 사용</h2>
<p>while문을 이용해 반복 횟수만큼 문자열을 더해가는 방법이다.</p>
<pre><code class="language-js">function solution(my_string, k) {
    let result = &quot;&quot;;
    while (k &gt; 0) {
        result += my_string;
        k--;
    }
    return result;
}

// 예시
console.log(solution(&quot;string&quot;, 3)); // stringstringstring</code></pre>
<hr>
<p>상황별 선택 가이드</p>
<blockquote>
<pre><code>•    가장 간단한 방법: repeat()
•    추가 조건이나 조작 필요: for, while
•    배열과 함수형 스타일 선호: reduce()나 Array와 join()
•    재귀 연습이 필요한 경우: 재귀 함수 사용</code></pre></blockquote>
<p>코딩테스트에서는 문제의 요구사항과 실행 시간에 따라 적합한 방법을 선택하자. repeat() 메서드가 가장 간단하고 빠르지만, 다른 방법도 상황에 따라 유용하다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리액트 라우터 V6.4] errorElement]]></title>
            <link>https://velog.io/@river-m/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%9D%BC%EC%9A%B0%ED%84%B0-V6.4-errorElement</link>
            <guid>https://velog.io/@river-m/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%9D%BC%EC%9A%B0%ED%84%B0-V6.4-errorElement</guid>
            <pubDate>Thu, 14 Sep 2023 16:12:40 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>이 포스트는 리액트 라우터의 최신 버전인 V6.16을 기준으로 작성되었다. 그러나 V6.4 버전 이후로 크게 변한 사항은 없고, v6.4 버전이 그전의 버전들와 많이 달라졌기 때문에 지금 보고계신 이포스트는 v6.4 이후 버전의 리액트 라우터 라고 생각하면 된다.</p>
</blockquote>
<p>이 포스트는 전편과 연결되어 있지만, 전편을 보지 않아도 충분히 이해할 수 있는 내용을 담고 있다. </p>
<h2 id="오류페이지-처리">오류페이지 처리</h2>
<p>웹사이트를 개발하면서 특정 에러 페이지를 처리해야 할 상황이 종종 발생한다. 예를 들어, 사용자가 잘못된 URL로 접근했을 때, 존재하지 않는 페이지를 보여주고 홈페이지로 리다이렉트할 수 있어야 한다. 리액트 라우터 v6.4에서는 이를 <strong><code>errorElement</code></strong>로 처리할 수 있다.</p>
<pre><code class="language-jsx">const router = createBrowserRouter(
  createRoutesFromElements(
    &lt;Route path=&quot;/&quot; element={&lt;Layout /&gt;} errorElement={&lt;ErrorBoundary /&gt;}&gt;
      &lt;Route index element={&lt;Index /&gt;} /&gt;
      &lt;Route path=&quot;about&quot; element={&lt;About /&gt;}&gt;&lt;/Route&gt;
      &lt;Route path=&quot;products&quot; element={&lt;Products /&gt;}&gt;
        &lt;Route index element={&lt;All /&gt;} /&gt;
        &lt;Route path=&quot;men&quot; element={&lt;Men /&gt;} /&gt;
        &lt;Route path=&quot;women&quot; element={&lt;Women /&gt;} /&gt;
      &lt;/Route&gt;
    &lt;/Route&gt;
  )
);

function App() {
  return &lt;RouterProvider router={router} /&gt;;
}</code></pre>
<p>맨 외부 레이아웃의 <strong><code>errorElement</code></strong>에는 에러 처리를 위한 컴포넌트를 삽입하면 된다.</p>
<p><img src="https://velog.velcdn.com/images/river-m/post/d3579169-aa5b-4a6e-8563-6126fecef478/image.png" alt=""></p>
<p>간단하게 에러 화면을 구성해봤는데</p>
<pre><code class="language-jsx">//ErrorBoundary
import { useNavigate, useRouteError } from &quot;react-router-dom&quot;;

function ErrorBoundary() {
  const error = useRouteError();
  const navigate = useNavigate();

  console.error(error);

  function goToHome() {
    navigate(&quot;/&quot;, { replace: true });
  }

  return (
    &lt;div className=&quot;error&quot;&gt;
      &lt;h2&gt;Error&lt;/h2&gt;
      &lt;p&gt;
        &lt;i&gt;{error.status} &lt;/i&gt;
        &lt;i&gt;{error.statusText || error.message}&lt;/i&gt;
      &lt;/p&gt;
      &lt;button type=&quot;button&quot; onClick={goToHome}&gt;
        home
      &lt;/button&gt;
    &lt;/div&gt;
  );
}

export default ErrorBoundary;</code></pre>
<p><strong><code>useRouteError</code></strong>는 에러 정보를 반환하고, <strong><code>useNavigate</code></strong>는 페이지 리디렉션을 위한 함수이다.</p>
<p><strong><code>ErrorBoundary</code></strong> 컴포넌트에서는 error.status와 error.statusText를 화면에 표시한다. 또한 버튼을 클릭하면 홈페이지로 리디렉션된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리액트 라우터 V6.4] 중첩라우트, Layout, outlet]]></title>
            <link>https://velog.io/@river-m/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%9D%BC%EC%9A%B0%ED%84%B0-V6.4-%EC%A4%91%EC%B2%A9%EB%9D%BC%EC%9A%B0%ED%8A%B8-Layout-outlet</link>
            <guid>https://velog.io/@river-m/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%9D%BC%EC%9A%B0%ED%84%B0-V6.4-%EC%A4%91%EC%B2%A9%EB%9D%BC%EC%9A%B0%ED%8A%B8-Layout-outlet</guid>
            <pubDate>Thu, 14 Sep 2023 14:37:57 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>이 포스트는 리액트 라우터의 최신 버전인 V6.16을 기준으로 작성되었다. 그러나 V6.4 버전 이후로 크게 변한 사항은 없고, v6.4 버전이 그전의 버전들와 많이 달라졌기 때문에 지금 보고계신 이포스트는 v6.4 이후 버전의 리액트 라우터 라고 생각하면 된다.</p>
</blockquote>
<h2 id="중첩라우트-구성---레이아웃">중첩라우트 구성 - 레이아웃</h2>
<p>레이아웃은 웹 페이지의 기본적인 구조를 나타내며, 헤더나 푸터 같은 일반적으로 변하지 않는 영역이 종종 있다. 이런 고정된 영역들을 중첩 라우트를 활용해 효율적으로 관리할 수 있다. 중첩 라우트를 사용하면 여러 종류의 레이아웃을 손쉽게 적용하고 변경할 수 있다.</p>
<h2 id="기본적인-레이아웃-구성"><strong>기본적인 레이아웃 구성</strong></h2>
<p>먼저 layout 만든다.</p>
<pre><code class="language-jsx">//App.jsx
const router = createBrowserRouter(
  createRoutesFromElements(&lt;Route path=&quot;/&quot; element={&lt;Layout /&gt;}&gt;&lt;/Route&gt;)
);</code></pre>
<h3 id="outlet">outlet</h3>
<pre><code class="language-jsx">//layout.jsx
import { NavLink, Outlet } from &quot;react-router-dom&quot;;
function Layout() {
  return (
    &lt;div&gt;
      &lt;header&gt;
        &lt;ul&gt;
          &lt;li&gt;
            &lt;NavLink to={&quot;/&quot;}&gt;HOME&lt;/NavLink&gt;
          &lt;/li&gt;
          &lt;li&gt;
            &lt;NavLink to={&quot;/ABOUT&quot;}&gt;ABOUT&lt;/NavLink&gt;
          &lt;/li&gt;
          &lt;li&gt;
            &lt;NavLink to={&quot;/PRODUCTS&quot;}&gt;PRODUCTS&lt;/NavLink&gt;
          &lt;/li&gt;
        &lt;/ul&gt;
      &lt;/header&gt;
      &lt;main&gt;
        &lt;Outlet /&gt;
      &lt;/main&gt;
      &lt;footer&gt;footer 영역&lt;/footer&gt;
    &lt;/div&gt;
  );
}

export default Layout;</code></pre>
<p>레이아웃 컴포넌트 안에는 헤더와 푸터가 구성되어 있고, 메인 영역에는 <strong><code>Outlet</code></strong> 컴포넌트가 위치한다. 레이아웃 내에서 <strong><code>Outlet</code></strong> 컴포넌트를 사용하면 자식 라우트를 렌더링할 수 있다. 이 영역이 중첩 라우트가 표시될 구역이다.</p>
<blockquote>
<p><strong><code>NavLink</code></strong>는 현재 라우트와 일치할 때 특별한 스타일이나 클래스를 적용할 수 있는 React Router의 컴포넌트다.</p>
</blockquote>
<p>레이아웃의 자식 컴포넌트로 이런 설정을 추가하면 구성이 완료된다.</p>
<pre><code class="language-jsx">
const router = createBrowserRouter(
  createRoutesFromElements(
    &lt;Route path=&quot;/&quot; element={&lt;Layout /&gt;}&gt;
      &lt;Route index element={&lt;Index /&gt;} /&gt;
      &lt;Route path=&quot;/about&quot; element={&lt;About /&gt;}&gt;&lt;/Route&gt;
      &lt;Route path=&quot;/products&quot; element={&lt;Products /&gt;}&gt;&lt;/Route&gt;
    &lt;/Route&gt;
  )
);

function App() {
  return &lt;RouterProvider router={router} /&gt;;
}

export default App;</code></pre>
<p>이제 레이아웃 안에서 라우트 페이지가 제대로 표시되는 것을 확인할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/river-m/post/77b441ef-f982-464b-8fb5-a71c801304a4/image.gif" alt=""></p>
<h2 id="중첩-라우트를-활용한-레이아웃"><strong>중첩 라우트를 활용한 레이아웃</strong></h2>
<p>외부 레이아웃 안에 서브 레이아웃이 필요한 상황은 자주 발생한다. 예를 들면, PRODUCTS 페이지에서는 카테고리를 선택할 수 있는 하위 메뉴가 있고, 해당 하위 메뉴를 선택하면 아래에 그 카테고리에 맞는 상품을 보여주는 구조가 필요할 수 있다. 이럴 때 중첩 라우팅을 통해 페이지를 유연하게 구성할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/river-m/post/3dbedd2c-ea9d-4c39-b9a3-35e4309114a6/image.png" alt=""></p>
<p>products 자식컴포넌트에 이렇게 또 중첩으로 레이아웃을 구성할수 있다.</p>
<pre><code class="language-jsx">
const router = createBrowserRouter(
  createRoutesFromElements(
    &lt;Route path=&quot;/&quot; element={&lt;Layout /&gt;}&gt;
      &lt;Route index element={&lt;Index /&gt;} /&gt;
      &lt;Route path=&quot;about&quot; element={&lt;About /&gt;}&gt;&lt;/Route&gt;
      &lt;Route path=&quot;products&quot; element={&lt;Products /&gt;}&gt;
        &lt;Route index element={&lt;All /&gt;} /&gt;
        &lt;Route path=&quot;men&quot; element={&lt;Men /&gt;} /&gt;
        &lt;Route path=&quot;women&quot; element={&lt;Women /&gt;} /&gt;
      &lt;/Route&gt;
    &lt;/Route&gt;
  )
);</code></pre>
<p>Product 컴포넌트:</p>
<pre><code class="language-jsx">//porducts
import { Link, NavLink, Outlet, useMatch } from &quot;react-router-dom&quot;;

function Products() {
  const match = useMatch(&quot;/products&quot;);

  return (
    &lt;div&gt;
      &lt;nav&gt;
        &lt;ul&gt;
          &lt;li&gt;
            &lt;Link to={&quot;&quot;} className={match &amp;&amp; &quot;active&quot;}&gt;
              All
            &lt;/Link&gt;
          &lt;/li&gt;
          &lt;li&gt;
            &lt;NavLink to={&quot;men&quot;}&gt;men&lt;/NavLink&gt;
          &lt;/li&gt;
          &lt;li&gt;
            &lt;NavLink to={&quot;women&quot;}&gt;women&lt;/NavLink&gt;
          &lt;/li&gt;
        &lt;/ul&gt;
      &lt;/nav&gt;
      &lt;div&gt;
        &lt;Outlet /&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}

export default Products;</code></pre>
<p><strong>서브 레이아웃에서 <code>NavLink</code>가 원하는 대로 작동하지 않는 문제가 있다. 정확한 해결책은 아직 찾지 않았지만, 나는 그냥 <code>Link</code> 컴포넌트를 사용하고 <code>useMatch</code>를 통해 URL을 판단한 뒤, <code>active</code> 클래스를 추가했다. 더 효율적인 해결 방법을 알고 있다면 댓글로 공유해주시면 감사하겠다!ㅜ</strong></p>
<p> 이제 화면에서 중첩 레이아웃을 구성한 것을 확인할 수 있다.
<img src="https://velog.velcdn.com/images/river-m/post/344cdb12-076e-4476-a7a6-8450c5157ca7/image.gif" alt=""></p>
<h2 id="동적-세그먼트">동적 세그먼트</h2>
<p>지금은 카테고리가 몇 개 없어서 문제가 없지만, 만약 카테고리가 많아진다면 일일이 컴포넌트를 만들어 <strong><code>Products</code></strong>의 자식 컴포넌트로 추가하는 작업은 굉장히 번거로울 수 있다. 그런 상황에서 UI는 거의 동일하고 데이터만 다르다면, 동적 세그먼트를 사용할 수 있다. 여기서  path 안에 콜론(:)은 경로에서 &quot;동적 세그먼트&quot;로 변환하는 특별한 의미를 가진다.</p>
<pre><code class="language-jsx">  &lt;Route path=&quot;/&quot; element={&lt;Layout /&gt;}&gt;
      &lt;Route index element={&lt;Index /&gt;} /&gt;
      &lt;Route path=&quot;about&quot; element={&lt;About /&gt;}&gt;&lt;/Route&gt;
      &lt;Route path=&quot;products&quot; element={&lt;Products /&gt;}&gt;
        &lt;Route index element={&lt;All /&gt;} /&gt;
        &lt;Route path=&quot;:categoriId&quot; element={&lt;categori /&gt;} /&gt;
      &lt;/Route&gt;
    &lt;/Route&gt;</code></pre>
<p>여기서는 따로 동적 세그먼트와 동적으로 컴포넌트를 생성하는 방법에 대해서는 다루지 않을 것이다. 궁금한 분들은 공식 문서에서 자세한 정보를 찾아보는 것을 추천한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리액트 라우터 v6.4] 기본 사용법]]></title>
            <link>https://velog.io/@river-m/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%9D%BC%EC%9A%B0%ED%84%B0-v6.4-%EA%B8%B0%EB%B3%B8-%EC%82%AC%EC%9A%A9%EB%B2%95</link>
            <guid>https://velog.io/@river-m/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%9D%BC%EC%9A%B0%ED%84%B0-v6.4-%EA%B8%B0%EB%B3%B8-%EC%82%AC%EC%9A%A9%EB%B2%95</guid>
            <pubDate>Thu, 14 Sep 2023 09:49:07 GMT</pubDate>
            <description><![CDATA[<h1 id="리액트-라우터-v64">리액트 라우터 v6.4</h1>
<blockquote>
<p>이 포스트는 리액트 라우터의 최신 버전인 V6.16을 기준으로 작성되었다. 그러나 V6.4 버전 이후로 크게 변한 사항은 없고, v6.4 버전이 그전의 버전들와 많이 달라졌기 때문에 지금 보고계신 이포스트는 v6.4 이후 버전의 리액트 라우터 라고 생각하면 된다. </p>
</blockquote>
<h2 id="소개글">소개글</h2>
<p>이 글은 오로지 리액트 라우터의 최신 버전에 대한 기초적인 사용법에 간략하게 소개한다. 이전 버전과의 차이나 마이그레이션 방법은 다루지 않으므로, 이미 이전 버전을 사용하고 계신 분은 v6.4 이상의 버전에 어떤 변화가 있는지만 간략히 알고 싶다면 이 글을 그냥 참고만 하면 된다 . 마이그레이션에 필요한 자세한 정보는 리액트 라우터의 공식 문서를 보는걸 추천한다.</p>
<h2 id="설치와-사용"><strong>설치와 사용</strong></h2>
<p>리액트 라우터를 사용하려면 먼저 설치해야 한다.</p>
<pre><code class="language-jsx">npm install react-router-dom</code></pre>
<h2 id="브라우저-라우터-생성과-첫-라우트-구성"><strong>브라우저 라우터 생성과 첫 라우트 구성</strong></h2>
<p>먼저 해야 할 일은 브라우저 라우터를 생성하고, 첫 번째 라우트를 설정하는 것이다. v6.4 버전에서는 새로운 <strong><a href="https://reactrouter.com/en/6.16.0/routers/picking-a-router#data-apis">데이터 API</a></strong>를 지원하는 라우터가 도입되었다.</p>
<p><strong><code>createBrowserRouter</code></strong>는 리액트 라우터 v6.4 이후로 브라우저 라우터를 생성하는 권장되는 방법이다. 이와 달리 <strong><code>&lt;BrowserRouter&gt;</code></strong> 라우터는 새로운 데이터 API를 지원하지 않는다.</p>
<p><strong>v6.4 이후 코드 예시:</strong></p>
<pre><code class="language-jsx">//App.jsx
import { RouterProvider, createBrowserRouter } from &quot;react-router-dom&quot;;

//객체형 라우트 구성방식
const router = createBrowserRouter([
  {
    path: &quot;/&quot;,
    element: &lt;div&gt;Hello world!&lt;/div&gt;,
  },
]);

function App() {
  return &lt;RouterProvider router={router} /&gt;;
}

export default App;
</code></pre>
<p><strong><code>createBrowserRouter</code></strong>를 사용해서 브라우저 라우터를 생성한 후, 이를 <strong><code>RouterProvider</code></strong>에 전달한다. 이렇게 하면 <strong><code>RouterProvider</code></strong>가 생성한 라우터를 사용해서 라우팅을 관리하게 된다.</p>
<p>예전의 방식을 간단하게 살펴 보겠다.</p>
<pre><code class="language-jsx">//v6.4 이전 버전 방식 
import {
  BrowserRouter,
  Route,
  Routes,
} from &quot;react-router-dom&quot;;

function App() {
  return (
    &lt;BrowserRouter&gt;
      &lt;Routes&gt;
        &lt;Route path=&quot;/&quot; element={&lt;div&gt;Hello world!&lt;/div&gt;} /&gt;
      &lt;/Routes&gt;
    &lt;/BrowserRouter&gt;
  );
}
</code></pre>
<p>이제는 <code>BrowserRouter</code> 와  <code>Routes</code> 가 필요없게 됐다.</p>
<h2 id="객체-대신-jsx로-사용하여-라우트를-구성하기">객체 대신 JSX로 사용하여 라우트를 구성하기</h2>
<p>대부분, 저 포함,은 JSX를 이용해 라우트를 구성하는 것을 선호한다. 리액트 라우터 공식 문서의 튜토리얼에서는 객체 방식을 사용한 코드 예시가 나와서 조금 헷갈렸던 경험이 있다.</p>
<h3 id="createroutesfromelements"><code>createRoutesFromElements</code></h3>
<p>JSX로 라우트를 설정하려면 <strong><code>createRoutesFromElements</code></strong> 함수가 필요하다. 이 함수는 <strong><code>&lt;Route&gt;</code></strong> 요소를 바탕으로 라우트 객체를 생성해준다.</p>
<p>JSX 라우트 구성 사용:</p>
<pre><code class="language-jsx">import {
  Route,
  RouterProvider,
  createBrowserRouter,
  createRoutesFromElements,
} from &quot;react-router-dom&quot;;

const router = createBrowserRouter(
  createRoutesFromElements(
    &lt;Route path=&quot;/&quot; element={&lt;div&gt;Hello world!&lt;/div&gt;}&gt;&lt;/Route&gt;
  )
);

function App() {
  return &lt;RouterProvider router={router} /&gt;;
}

export default App;</code></pre>
<h2 id="jsx-vs-객체-형식의-라우트-구성"><strong>JSX vs 객체 형식의 라우트 구성</strong></h2>
<blockquote>
<p>리액트 라우터 공식 문서의 튜토리얼에서는 마지막 부분에서 JSX로 라우트를 구성하는 방식도 언급하고 있다. JSX와 객체 형식 사이에는 기능적인 차이가 없다. 이 둘 사이의 차이는 오직 개발자의 스타일과 선호도에 따른 것이다. 따라서 특별히 더 권장하는 방식은 없으며, 자기가 선호하는 방식을 선택해서 사용하면 된다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리액트] React에서 Memoization]]></title>
            <link>https://velog.io/@river-m/%EB%A6%AC%EC%95%A1%ED%8A%B8-React%EC%97%90%EC%84%9C-Memoization</link>
            <guid>https://velog.io/@river-m/%EB%A6%AC%EC%95%A1%ED%8A%B8-React%EC%97%90%EC%84%9C-Memoization</guid>
            <pubDate>Thu, 14 Sep 2023 08:10:44 GMT</pubDate>
            <description><![CDATA[<p>React 개발을 하다 보면 애플리케이션의 성능을 최적화해야 할 상황이 종종 생긴다. 이 때 사용할 수 있는 기법 중 하나가 Memoization이다. </p>
<h2 id="memoization이란"><strong>Memoization이란?</strong></h2>
<p>Memoization은 계산 결과를 메모리에 저장해 두고 동일한 계산이 요청될 때 다시 계산하지 않고 저장된 값을 반환하는 최적화 기법이다. 이를 통해 애플리케이션의 속도를 높일 수 있다.</p>
<hr>
<h2 id="reactmemo의-이해와-활용"><strong>React.memo의 이해와 활용</strong></h2>
<p>React.memo는 React 라이브러리에서 제공하는 고차 컴포넌트(HOC)로, 함수형 컴포넌트의 렌더 결과를 메모이징해 성능을 최적화할 수 있다. </p>
<h3 id="작동-원리"><strong>작동 원리</strong></h3>
<p>React.memo는 props의 얕은 비교(shallow comparison)를 통해 이전과 현재의 props가 같은지 확인한다. 만약 props가 변경되지 않았다면, 이전에 렌더링된 결과를 재사용한다.</p>
<h3 id="사용법"><strong>사용법</strong></h3>
<p>기본적인 사용법은 아래와 같다.</p>
<pre><code class="language-jsx">const MyComponent = React.memo(function MyComponent({ name }) {
  return &lt;div&gt;{`안녕, ${name}`}&lt;/div&gt;;
});</code></pre>
<p>여기서 <strong><code>MyComponent</code></strong>는 <strong><code>name</code></strong> props가 변경되지 않는 한 이전 렌더링 결과를 재사용한다.</p>
<h3 id="커스텀-비교-함수"><strong>커스텀 비교 함수</strong></h3>
<p>React.memo는 두 번째 인자로 커스텀 비교 함수를 받을 수 있다. 이 함수는 이전 props와 새로운 props를 인자로 받고, 두 값이 같으면 <strong><code>true</code></strong>, 다르면 <strong><code>false</code></strong>를 반환한다.</p>
<pre><code class="language-jsx">const areEqual = (prevProps, nextProps) =&gt; {
  return prevProps.name === nextProps.name;
};

const MyComponent = React.memo(function MyComponent({ name }) {
  return &lt;div&gt;{`안녕, ${name}`}&lt;/div&gt;;
}, areEqual);</code></pre>
<h3 id="주의점"><strong>주의점</strong></h3>
<ol>
<li><strong>얕은 비교</strong>: React.memo는 얕은 비교만 수행하므로, 객체나 배열 같은 참조 타입의 props가 변경될 때 문제가 생길 수 있다.</li>
<li><strong>메모리 사용량</strong>: 메모이징은 메모리에 렌더링 결과를 저장하기 때문에 메모리 사용량이 늘어날 수 있다.</li>
<li><strong>불필요한 최적화</strong>: 모든 컴포넌트에 React.memo를 사용하는 것은 권장하지 않는다. 성능 이슈가 있는 경우에만 사용하자.</li>
</ol>
<blockquote>
<p>React.memo는 함수형 컴포넌트의 성능을 최적화하기 위한 유용한 도구다. 하지만 사용 시에는 주의점을 고려해야 한다. 성능 이슈가 실제로 발생하는 경우에만 적절히 활용하도록 하자.</p>
</blockquote>
<hr>
<h2 id="react에서-usememo의-이해와-활용"><strong>React에서 useMemo의 이해와 활용</strong></h2>
<p>React 애플리케이션에서 성능 최적화를 고려한다면 <strong><code>useMemo</code></strong>는 중요한 훅 중 하나다. 복잡한 연산이나 빈번한 렌더링이 일어나는 경우, 이 훅을 사용해 성능을 개선할 수 있다. </p>
<h3 id="작동-원리-1"><strong>작동 원리</strong></h3>
<p><strong><code>useMemo</code></strong>는 메모이제이션(memoization)을 통해 연산의 결과값을 저장해 둔다. 이 훅은 의존성 배열이 변경되지 않으면 이전에 계산한 값을 재사용한다.</p>
<h3 id="사용법-1"><strong>사용법</strong></h3>
<p>기본 사용법은 아래와 같다.</p>
<pre><code class="language-jsx">import React, { useMemo } from &#39;react&#39;;

const MyComponent = ({ list }) =&gt; {
  const sortedList = useMemo(() =&gt; {
    return list.sort((a, b) =&gt; a - b);
  }, [list]);

  return (
    &lt;div&gt;
      {sortedList.map((item) =&gt; (
        &lt;p&gt;{item}&lt;/p&gt;
      ))}
    &lt;/div&gt;
  );
};</code></pre>
<p>여기에서 <strong><code>sortedList</code></strong>는 <strong><code>list</code></strong> 배열이 변경될 때만 다시 정렬된다. 그 외의 경우에는 이전에 정렬된 배열을 재사용한다.</p>
<h3 id="의존성-배열"><strong>의존성 배열</strong></h3>
<p><strong><code>useMemo</code></strong>의 두 번째 인자는 의존성 배열이다. 이 배열 내의 값이 변경되면 <strong><code>useMemo</code></strong> 내부의 코드가 다시 실행된다. 의존성 배열을 잘 관리하는 것이 중요하다.</p>
<h3 id="주의점-1"><strong>주의점</strong></h3>
<ol>
<li><strong>복잡한 연산만 최적화</strong>: <strong><code>useMemo</code></strong>는 복잡한 연산이나 자주 변경되는 값에 대한 최적화에 적합하다.</li>
<li><strong>메모리 사용량 증가</strong>: 메모이제이션은 메모리에 값을 저장하기 때문에 메모리 사용량이 증가할 수 있다.</li>
<li><strong>의존성 배열 관리</strong>: 의존성 배열이 잘못 설정되면 예상하지 못한 버그가 발생할 수 있다.</li>
</ol>
<blockquote>
<p><strong><code>useMemo</code></strong>는 React에서 성능 최적화에 큰 도움을 줄 수 있다. 하지만 언제나 그렇듯, 이 훅도 적절한 상황과 조건에서 사용해야 최대의 효과를 볼 수 있다. 성능 문제가 발생할 때만 고려해서 사용하는게 바람직하다.</p>
</blockquote>
<hr>
<h2 id="react에서-usecallback의-깊은-이해와-활용"><strong>React에서 useCallback의 깊은 이해와 활용</strong></h2>
<p><strong><code>useCallback</code></strong>은 React에서 제공하는 훅 중 하나로, 함수를 메모이징해서 성능을 최적화할 수 있는 기능을 제공한다. </p>
<h3 id="작동-원리-2"><strong>작동 원리</strong></h3>
<p><strong><code>useCallback</code></strong> 훅은 주어진 함수와 의존성 배열을 인자로 받아, 메모이징된 함수를 반환한다. 의존성 배열의 값이 변경되지 않는다면 이전에 생성된 함수를 재사용한다.</p>
<h3 id="기본-사용법"><strong>기본 사용법</strong></h3>
<p><strong><code>useCallback</code></strong>의 기본 사용법은 다음과 같다.</p>
<pre><code class="language-jsx">import React, { useCallback } from &#39;react&#39;;

const MyButton = ({ onClick, label }) =&gt; {
  return &lt;button onClick={onClick}&gt;{label}&lt;/button&gt;;
};

const App = () =&gt; {
  const handleClick = useCallback(() =&gt; {
    console.log(&#39;Button clicked!&#39;);
  }, []);

  return &lt;MyButton onClick={handleClick} label=&quot;클릭해줘!&quot; /&gt;;
};</code></pre>
<h3 id="의존성-배열-1"><strong>의존성 배열</strong></h3>
<p><strong><code>useCallback</code></strong>의 두 번째 인자는 의존성 배열이다. 이 배열 내의 값이 변경되면 새로운 함수를 생성하고 메모이징한다. 배열이 비어 있다면 컴포넌트가 처음 마운트될 때 생성한 함수를 계속해서 재사용한다.</p>
<pre><code class="language-jsx">const handleClick = useCallback(() =&gt; {
  console.log(`Button clicked ${count} times`);
}, [count]);</code></pre>
<h3 id="주의점-2"><strong>주의점</strong></h3>
<ol>
<li><strong>오버헤드</strong>: <strong><code>useCallback</code></strong>을 무분별하게 사용하면 오히려 성능 문제가 생길 수 있다.</li>
<li><strong>의존성 배열 관리</strong>: 의존성 배열을 잘못 관리하면 예상치 못한 동작이나 버그를 유발할 수 있다.</li>
<li><strong>함수 내부 상태</strong>: <strong><code>useCallback</code></strong>으로 메모이징된 함수가 클로저를 형성하므로 함수 내부에서 사용하는 외부 변수에 주의해야 한다.</li>
</ol>
<h3 id="사용-시나리오"><strong>사용 시나리오</strong></h3>
<ul>
<li>이벤트 핸들러 최적화</li>
<li>자주 발생하지만 연산이 복잡하지 않은 함수 메모이징</li>
<li>자식 컴포넌트에 함수를 props로 전달할 때 불필요한 렌더링 방지</li>
</ul>
<blockquote>
<p><strong><code>useCallback</code></strong>은 React 애플리케이션에서 성능 최적화를 위해 유용하게 사용할 수 있는 훅이다. 하지만 잘못 사용하면 오히려 성능 문제를 유발할 수 있으므로 적절한 상황에서만 사용해야 헌다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리액트] Redux-thunk]]></title>
            <link>https://velog.io/@river-m/%EB%A6%AC%EC%95%A1%ED%8A%B8-Redux-thunk</link>
            <guid>https://velog.io/@river-m/%EB%A6%AC%EC%95%A1%ED%8A%B8-Redux-thunk</guid>
            <pubDate>Tue, 05 Sep 2023 14:32:33 GMT</pubDate>
            <description><![CDATA[<h1 id="redux-thunk">Redux-thunk</h1>
<p>비동기 통신을 위한 redux-middleware</p>
<p><img src="https://velog.velcdn.com/images/river-m/post/121993fb-d2ea-44a3-bb15-3443fba753ff/image.png" alt=""></p>
<h2 id="redux-thunk를-이해하고-사용하기">Redux-Thunk를 이해하고 사용하기</h2>
<p>Redux는 자바스크립트 앱에서 상태 관리를 위한 라이브러리다. 하지만 Redux 자체는 비동기 작업을 처리하기에는 불편한 점이 있다. 이럴 때 도움이 되는 것이 Redux-Thunk 미들웨어다. 이 포스트에서는 Redux-Thunk가 무엇인지, 왜 필요한지, 어떻게 사용하는지에 대해 알아보자.</p>
<h3 id="redux-thunk란">Redux-Thunk란?</h3>
<p>Redux-Thunk는 Redux의 미들웨어 중 하나로, 비동기 작업을 쉽게 처리할 수 있게 해준다. 기본적으로 액션 생성자가 객체 대신 함수를 반환할 수 있게 한다. 이 함수는 <code>dispatch</code>와 <code>getState</code> 두 개의 인자를 받아 비동기 작업을 수행할 수 있다.</p>
<pre><code class="language-jsx">const fetchData = () =&gt; {
  return (dispatch, getState) =&gt; {
    fetch(&#39;some-api&#39;)
      .then(response =&gt; response.json())
      .then(data =&gt; dispatch({ type: &#39;FETCH_SUCCESS&#39;, payload: data }));
  };
};
</code></pre>
<h2 id="왜-redux-thunk가-필요한가">왜 Redux-Thunk가 필요한가?</h2>
<h3 id="순수-함수의-제한">순수 함수의 제한</h3>
<p>Redux의 액션 생성자와 리듀서는 순수 함수여야 한다. 순수 함수는 같은 인자에 대해 항상 같은 값을 반환해야 하고, 외부 상태에 의존하거나 변경하지 않아야 한다. 따라서 API 호출 같은 비동기 작업을 하기 어렵다.</p>
<h3 id="미들웨어의-도입">미들웨어의 도입</h3>
<p>Redux-Thunk와 같은 미들웨어를 사용하면 액션 생성자 내에서 비동기 작업을 할 수 있다. 미들웨어는 액션이 디스패치되어 리듀서에서 처리되기 전에 중간에 실행되는 코드다.</p>
<h2 id="redux-thunk를-활용한-비동기-작업-상세-예제"><strong>Redux-Thunk를 활용한 비동기 작업: 상세 예제</strong></h2>
<h2 id="프로젝트-구조">프로젝트 구조</h2>
<pre><code>my-redux-thunk-app/
├── src/
│   ├── store/
│   │   ├── actions/
│   │   │   └── fetchData.js
│   │   ├── reducers/
│   │   │   ├── dataReducer.js
│   │   │   └── index.js  
│   │   └── index.js
│   ├── App.js
│   └── index.js
└── package.json</code></pre><h3 id="필요한-패키지-설치">필요한 패키지 설치</h3>
<p>프로젝트 폴더에서 터미널을 열고 다음 명령어를 실행해 필요한 패키지를 설치한다.</p>
<pre><code class="language-bash">npm install redux redux-thunk react-redux
</code></pre>
<h2 id="storereducersdatareducerjs-리듀서-설정">store/reducers/dataReducer.js: 리듀서 설정</h2>
<p><code>src/store/reducers/dataReducer.js</code> 파일을 생성하고 다음과 같이 작성한다.</p>
<pre><code class="language-jsx">const initialState = {
  loading: false,
  data: null,
  error: null,
};

// dataReducer는 액션에 따라 state를 변경하는 함수다.
const dataReducer = (state = initialState, action) =&gt; {
  switch (action.type) {
    case &#39;FETCH_START&#39;:
      // 로딩 시작할 때
      return { ...state, loading: true };
    case &#39;FETCH_SUCCESS&#39;:
      // 데이터 가져오는 것이 성공했을 때
      return { ...state, loading: false, data: action.payload };
    case &#39;FETCH_ERROR&#39;:
      // 에러 발생했을 때
      return { ...state, loading: false, error: action.payload };
    default:
      return state;
  }
};

export default dataReducer;
</code></pre>
<h2 id="storereducersindexjs-rootreducer-작성"><strong>store/reducers/index.js: rootReducer 작성</strong></h2>
<p><strong><code>src/store/reducers/index.js</code></strong> 파일을 만들고 다음과 같이 작성해.</p>
<pre><code class="language-jsx">import { combineReducers } from &#39;redux&#39;;
import dataReducer from &#39;./dataReducer&#39;;
// 여러 개의 리듀서를 하나로 합친다.
const rootReducer = combineReducers({
  data: dataReducer,
});

export default rootReducer;</code></pre>
<h2 id="storeindexjs-스토어-설정">store/index.js: 스토어 설정</h2>
<p><code>src/store/index.js</code> 파일을 생성하고 아래와 같이 작성한다.</p>
<pre><code class="language-jsx">// 스토어 생성. 미들웨어로 thunk 사용.
import { createStore, applyMiddleware } from &#39;redux&#39;;
import thunk from &#39;redux-thunk&#39;;
import rootReducer from &#39;./reducers&#39;;

const store = createStore(rootReducer, applyMiddleware(thunk));

export default store;
</code></pre>
<h2 id="storeactionsfetchdatajs-액션-생성자">store/actions/fetchData.js: 액션 생성자</h2>
<p><code>src/store/actions/fetchData.js</code> 파일을 생성하고 다음과 같이 작성한다.</p>
<pre><code class="language-jsx">// 비동기 작업을 위한 액션 생성자다.
export const fetchData = () =&gt; {
  return (dispatch) =&gt; {
    // 데이터를 가져오기 시작하면 &#39;FETCH_START&#39; 액션을 디스패치한다.
    dispatch({ type: &#39;FETCH_START&#39; });

    fetch(&#39;&lt;https://jsonplaceholder.typicode.com/todos/1&gt;&#39;)
     .then((response) =&gt; {
        if (!response.ok) {
          // 에러가 발생하면 &#39;FETCH_ERROR&#39; 액션을 디스패치한다.
          throw new Error(`${response.status}`);
        }
        return response.json();
      })
      .then(data =&gt; {
        // 성공하면 &#39;FETCH_SUCCESS&#39; 액션을 디스패치한다.
        dispatch({ type: &#39;FETCH_SUCCESS&#39;, payload: data });
      })
      .catch(error =&gt; {
        // 에러 발생시 &#39;FETCH_ERROR&#39; 액션을 디스패치한다.
        dispatch({ type: &#39;FETCH_ERROR&#39;, payload: error });
      });
  };
};</code></pre>
<h2 id="appjs-앱의-메인-코드">App.js: 앱의 메인 코드</h2>
<p><code>src/App.js</code> 파일에서는 다음과 같이 작성한다.</p>
<pre><code class="language-jsx">// useDispatch와 useSelector로 리덕스 스토어와 상호작용한다.
import { useDispatch, useSelector } from &quot;react-redux&quot;;
import { fetchData } from &quot;./store/actions/fetchData&quot;;

const App = () =&gt; {
  const dispatch = useDispatch();
  // 스토어의 data 상태를 가져온다.
  const { loading, data, error } = useSelector((state) =&gt; state.data);

  const handleClick = () =&gt; {
    // 버튼 클릭하면 fetchData 액션을 디스패치한다.
    dispatch(fetchData());
  };

  return (
    &lt;div&gt;
      &lt;button onClick={handleClick}&gt;데이터 가져오기&lt;/button&gt;
      {/* 상태에 따른 UI 렌더링 */}
      {loading ? (
        &lt;h1&gt;Loading...&lt;/h1&gt;
      ) : error ? (
        &lt;h1&gt;Error: {error.message}&lt;/h1&gt;
      ) : (
        data &amp;&amp; &lt;h1&gt;{data.title}&lt;/h1&gt;
      )}
    &lt;/div&gt;
  );
};</code></pre>
<h2 id="indexjs-앱-실행">index.js: 앱 실행</h2>
<p><code>src/index.js</code> 파일에서는 다음과 같이 작성한다.</p>
<pre><code class="language-jsx">//Provider으로 감싸서 리덕스 스토어를 제공한다.
import React from &quot;react&quot;;
import ReactDOM from &quot;react-dom/client&quot;;
import App from &quot;./App&quot;;
import { Provider } from &quot;react-redux&quot;;
import store from &quot;./store&quot;;

ReactDOM.createRoot(document.getElementById(&quot;root&quot;)).render(
  &lt;React.StrictMode&gt;
    &lt;Provider store={store}&gt;
      &lt;App /&gt;
    &lt;/Provider&gt;
  &lt;/React.StrictMode&gt;
);</code></pre>
<p>이제 <code>store</code> 폴더 안에서 액션 생성자와 리듀서, 그리고 스토어 설정까지 모두 관리하고 있다. 이 구조를 따르면 프로젝트 관리가 더 쉬워질 것이다.</p>
<h2 id="결론">결론</h2>
<p>Redux-Thunk는 Redux와 함께 사용되어 비동기 작업을 간단하게 처리할 수 있게 해주는 미들웨어다. 이를 통해 액션 생성자는 비동기 로직을 캡슐화하며, 리듀서는 순수 함수를 유지할 수 있다. 비동기 처리가 필요한 경우 Redux-Thunk를 활용해보면 좋을 것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리액트] 미들웨어 이해하기]]></title>
            <link>https://velog.io/@river-m/%EB%A6%AC%EC%95%A1%ED%8A%B8%EB%A6%AC%EB%8D%95%EC%8A%A4-%EB%AF%B8%EB%93%A4%EC%9B%A8%EC%96%B4-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@river-m/%EB%A6%AC%EC%95%A1%ED%8A%B8%EB%A6%AC%EB%8D%95%EC%8A%A4-%EB%AF%B8%EB%93%A4%EC%9B%A8%EC%96%B4-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 05 Sep 2023 09:41:28 GMT</pubDate>
            <description><![CDATA[<h2 id="미들웨어란-무엇인가">미들웨어란 무엇인가?</h2>
<p>리덕스 미들웨어는 액션을 디스패치했을 때 리듀서에서 처리되기 전에 특정 작업을 수행할 수 있게 해주는 기능이다. 예를 들어 API 호출, 로깅, 액션 취소 등 다양한 작업을 할 수 있다. </p>
<p><img src="https://velog.velcdn.com/images/river-m/post/df5aee98-d805-457c-b389-82027ddc905b/image.png" alt=""></p>
<p>미들웨어가 필요한 이유는 주로 비동기 작업이나 부가적인 처리를 해야 할 때 나타난다. 리듀서는 순수 함수여야 하기 때문에, 그 자체로는 API 호출이나 로깅 등의 부수 효과(side-effects)를 가지는 작업을 할 수 없다.</p>
<h2 id="미들웨어의-주요-역할">미들웨어의 주요 역할</h2>
<ol>
<li>액션 객체 검증: 액션 객체의 유효성을 검사해 잘못된 액션을 걸러낼 수 있다.</li>
<li>액션 로깅: 액션의 발생을 로깅해 디버깅을 쉽게 할 수 있다.</li>
<li>비동기 작업 처리: <code>thunk</code>나 <code>saga</code> 같은 미들웨어를 사용해 비동기 로직을 쉽게 관리할 수 있다.</li>
</ol>
<h2 id="미들웨어-만드는-방법">미들웨어 만드는 방법</h2>
<p>미들웨어는 보통 아래와 같은 형태로 만든다.</p>
<pre><code class="language-jsx">const myMiddleware = store =&gt; next =&gt; action =&gt; {
  // 미들웨어 로직
  return next(action);
};
</code></pre>
<h2 id="미들웨어-적용하기">미들웨어 적용하기</h2>
<p>미들웨어를 적용하기 위해선 <code>applyMiddleware</code> 함수를 사용한다.</p>
<pre><code class="language-jsx">import { createStore, applyMiddleware } from &#39;redux&#39;;
import myMiddleware from &#39;./myMiddleware&#39;;

const store = createStore(
  rootReducer,
  applyMiddleware(myMiddleware)
);
</code></pre>
<h2 id="미들웨어-예시-로깅-미들웨어">미들웨어 예시: 로깅 미들웨어</h2>
<p>간단한 로깅 미들웨어를 만들어 볼까? 코드는 아래와 같다.</p>
<pre><code class="language-jsx">const logger = store =&gt; next =&gt; action =&gt; {
  console.log(&#39;dispatching&#39;, action);
  let result = next(action);
  console.log(&#39;next state&#39;, store.getState());
  return result;
};
</code></pre>
<h2 id="비동기-미들웨어-thunk">비동기 미들웨어: Thunk</h2>
<p>Thunk 미들웨어는 함수 형태의 액션을 디스패치할 수 있게 해준다.</p>
<pre><code class="language-jsx">const thunk = store =&gt; next =&gt; action =&gt; {
  if (typeof action === &#39;function&#39;) {
    return action(store.dispatch, store.getState);
  }
  return next(action);
};
</code></pre>
<h2 id="결론">결론</h2>
<p>리덕스 미들웨어는 액션을 디스패치했을 때 추가적인 작업을 할 수 있게 해주는 강력한 기능이다. 미들웨어를 통해 코드의 유지보수성과 확장성을 높일 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리액트] Redux]]></title>
            <link>https://velog.io/@river-m/%EB%A6%AC%EC%95%A1%ED%8A%B8-Redux</link>
            <guid>https://velog.io/@river-m/%EB%A6%AC%EC%95%A1%ED%8A%B8-Redux</guid>
            <pubDate>Tue, 05 Sep 2023 09:14:24 GMT</pubDate>
            <description><![CDATA[<hr>
<h2 id="redux란-무엇인가">Redux란 무엇인가?</h2>
<p>Redux는 자바스크립트 앱에서 상태를 효율적으로 관리하기 위한 라이브러리다. 주로 React와 함께 사용되지만, 다른 라이브러리나 프레임워크에서도 사용 가능하다.</p>
<h3 id="왜-필요한가">왜 필요한가?</h3>
<ul>
<li>중앙 집중적인 상태 관리: 앱의 크기가 커지면 상태 관리가 복잡해진다. Redux는 이를 중앙에서 관리해 해결한다.</li>
<li>시간 여행 디버깅: 이전 상태로 쉽게 되돌릴 수 있다.</li>
<li>테스트 용이: 상태를 예측 가능하게 관리할 수 있으므로 테스트가 용이하다.</li>
</ul>
<hr>
<h2 id="redux를-이용한-카운터-앱-예시">Redux를 이용한 카운터 앱 예시</h2>
<h3 id="1-설치">1. 설치</h3>
<p>먼저 프로젝트에 Redux를 설치한다.</p>
<pre><code class="language-bash">npm install redux react-redux
</code></pre>
<h3 id="2-action-정의">2. Action 정의</h3>
<p>Action 객체를 반환하는 함수를 정의한다.</p>
<pre><code class="language-jsx">// src/store/actions/counterActions.js

export const increment = () =&gt; {
  return {
    type: &quot;INCREMENT&quot;,
  };
};</code></pre>
<h3 id="3-reducer-생성">3. Reducer 생성</h3>
<p>Reducer는 상태와 액션을 받아 새로운 상태를 반환한다.</p>
<pre><code class="language-jsx">// src/store/reducers/counterReducer.js

const initialState = 0;

const counterReducer = (state = initialState, action) =&gt; {
  switch (action.type) {
    case &#39;INCREMENT&#39;:
      return state + 1;
    default:
      return state;
  }
};

export default counterReducer;
</code></pre>
<h3 id="4-root-reducer-만들기">4. Root Reducer 만들기</h3>
<p>여러 개의 리듀서를 하나로 합치는 Root Reducer를 만든다.</p>
<pre><code class="language-jsx">// src/store/reducers/index.js

import { combineReducers } from &quot;redux&quot;;
import counterReducer from &quot;./counterReducer&quot;;

const rootReducer = combineReducers({
  counter: counterReducer,
});

export default rootReducer;</code></pre>
<h3 id="5-store-만들기">5. Store 만들기</h3>
<p>Store를 생성하고, React 앱에 연결한다.</p>
<pre><code class="language-jsx">// src/main.js

import React from &quot;react&quot;;
import ReactDOM from &quot;react-dom/client&quot;;
import { createStore } from &quot;redux&quot;;
import { Provider } from &quot;react-redux&quot;;
import App from &quot;./App&quot;;
import rootReducer from &quot;./store/reducers&quot;;

const store = createStore(rootReducer);

ReactDOM.createRoot(document.getElementById(&quot;root&quot;)).render(
  &lt;React.StrictMode&gt;
    &lt;Provider store={store}&gt;
      &lt;App /&gt;
    &lt;/Provider&gt;
  &lt;/React.StrictMode&gt;
);
</code></pre>
<h3 id="6-react-컴포넌트에-연결">6. React 컴포넌트에 연결</h3>
<p><code>react-redux</code>의 <code>useSelector</code>와 <code>useDispatch</code> 훅을 사용한다.</p>
<pre><code class="language-jsx">// src/App.js

import React from &quot;react&quot;;
import { useDispatch, useSelector } from &quot;react-redux&quot;;
import { increment } from &quot;./store/actions/counterActions&quot;;

function App() {
  const count = useSelector((state) =&gt; state.counter);
  const dispatch = useDispatch();

  return &lt;button onClick={() =&gt; dispatch(increment())}&gt;Count: {count}&lt;/button&gt;;
}

export default App;</code></pre>
<p>이제 리덕스를 통해 상태 관리가 가능하다. <code>useSelector</code>로 상태에 접근할 때 <code>state.counter</code>로 접근하는 것에 주의하자. 이렇게 되는 이유는 <code>combineReducers</code>에서 <code>counter</code>로 설정했기 때문이다.</p>
<hr>
<h2 id="미들웨어와-비동기-작업">미들웨어와 비동기 작업</h2>
<p>미들웨어를 통해 액션을 디스패치하기 전후로 로직을 넣을 수 있다.</p>
<h2 id="best-practices">Best Practices</h2>
<ul>
<li>액션 타입은 상수로 관리한다.</li>
<li>리듀서는 순수 함수여야 한다.</li>
<li>복잡한 로직은 미들웨어에서 처리한다.</li>
</ul>
<hr>
<p>이것은 Redux의 기본적인 개념과 사용 방법에 대한 소개다. 더 자세한 내용은 공식 문서에서 확인할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리액트] useReducer]]></title>
            <link>https://velog.io/@river-m/%EB%A6%AC%EC%95%A1%ED%8A%B8-useReducer</link>
            <guid>https://velog.io/@river-m/%EB%A6%AC%EC%95%A1%ED%8A%B8-useReducer</guid>
            <pubDate>Mon, 04 Sep 2023 16:49:24 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong><code>useState</code></strong>가 React에서 가장 기본적인 상태 관리 도구라면, <strong><code>useReducer</code></strong>는 좀 더 복잡한 상태 로직을 관리할 때 유용하다. 전역 상태 관리에는 <strong><code>Context API</code></strong>나 <strong><code>Redux</code></strong>도 많이 쓰이지만, <strong><code>useReducer</code></strong>도 그에 못지않게 중요한 역할을 한다.</p>
</blockquote>
<h2 id="usereducer란"><strong>useReducer란?</strong></h2>
<p><strong><code>useReducer</code></strong>는 <strong><code>React</code></strong>에서 제공하는 하나의 훅으로, 액션 객체를 디스패치해 상태를 업데이트하는 방식으로 동작한다. 기본 형태는 다음과 같다.</p>
<pre><code class="language-jsx">const [state, dispatch] = useReducer(reducer, initialState);</code></pre>
<ul>
<li><strong><code>state</code></strong>: 현재 상태</li>
<li><strong><code>dispatch</code></strong>: 액션을 발생시키는 함수</li>
<li><strong><code>reducer</code></strong>: reducer 는 현재 상태와 액션 객체를 파라미터로 받아와서 새로운 상태를 반환해주는 함수이다.</li>
<li><strong><code>initialState</code></strong>: 초기 상태</li>
</ul>
<h2 id="왜-usereducer를-쓰는가"><strong>왜 useReducer를 쓰는가?</strong></h2>
<ol>
<li>복잡한 상태 로직 관리</li>
<li>읽기 쉬운 코드 구조</li>
<li>재사용 가능한 로직</li>
</ol>
<p><strong>예시</strong></p>
<pre><code class="language-jsx">import { useReducer } from &quot;react&quot;;

function reducer(state, action) {
  switch (action.type) {
    case &quot;INCREMENT&quot;:
      return state + 1 ;
    default:
      return state;
  }
}

export default function App() {
  const [state, dispatch] = useReducer(reducer, 0 );

  return (
    &lt;div&gt;
      &lt;p&gt;{state.count}&lt;/p&gt;
      &lt;button onClick={() =&gt; dispatch({ type: &quot;INCREMENT&quot; })}&gt;+&lt;/button&gt;
    &lt;/div&gt;
  );
}</code></pre>
<h2 id="usereducer와-context-api를-활용한-전역-상태-관리"><strong>useReducer와 Context API를 활용한 전역 상태 관리</strong></h2>
<p><strong>1. Context와 Reducer 생성</strong></p>
<pre><code class="language-jsx">import { createContext, useReducer } from &quot;react&quot;;

export const CountContext = createContext();

const initialState = 0;

function countReducer(state, action) {
  if (action.type === &quot;INCREMENT&quot;) {
    return action.payload;
  }
  return state;
}

export function CountProvider({ children }) {
  const [count, dispatch] = useReducer(countReducer, initialState);

  return (
    &lt;CountContext.Provider value={{ count, dispatch }}&gt;
      {children}
    &lt;/CountContext.Provider&gt;
  );
}</code></pre>
<p><strong>2. 컴포넌트에서 사용하기</strong></p>
<pre><code class="language-jsx">import { useContext } from &quot;react&quot;;
import { CountProvider, CountContext } from &quot;./store/CountContext&quot;;

export default function App() {
  return (
    &lt;CountProvider&gt;
      &lt;Counter /&gt;
    &lt;/CountProvider&gt;
  );
}

function Counter() {
  const { count, dispatch } = useContext(CountContext);

  return (
    &lt;div&gt;
      &lt;p&gt;{count}&lt;/p&gt;
      &lt;button
        onClick={() =&gt; dispatch({ type: &quot;INCREMENT&quot;, payload: count + 1 })}
      &gt;
        증가
      &lt;/button&gt;
    &lt;/div&gt;
  );
}</code></pre>
<p>여기서 <strong><code>CountProvider</code></strong>는 전역 상태를 제공하고, <strong><code>Counter</code></strong> 컴포넌트에서는 그 상태를 사용하고 있다. </p>
<p>이렇게 하면 <strong><code>INCREMENT</code></strong> 액션을 디스패치할 때 <strong><code>payload</code></strong>를 통해 새로운 <strong><code>count</code></strong> 값을 전달할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리액트] Context API]]></title>
            <link>https://velog.io/@river-m/%EB%A6%AC%EC%95%A1%ED%8A%B8-Context-API</link>
            <guid>https://velog.io/@river-m/%EB%A6%AC%EC%95%A1%ED%8A%B8-Context-API</guid>
            <pubDate>Mon, 04 Sep 2023 09:28:23 GMT</pubDate>
            <description><![CDATA[<p>웹 어플리케이션을 개발 하다보면 전역 상태로 관리해야 하는 값들이 꽤 많이 생긴다.</p>
<blockquote>
<p>리액트에서 전역 상태를 관리하는 방법 중 하나는 Context API를 사용하는 것이다. 이 API를 이해하려면 먼저 &#39;상태&#39;라는 개념을 이해해야 한다. 상태는 데이터의 현재 상태를 나타내고, 리액트 애플리케이션에서 여러 컴포넌트 간에 상태를 공유하기 위해 Context API를 사용한다.</p>
</blockquote>
<h2 id="context-api의-장점">Context API의 장점</h2>
<ul>
<li>Prop drilling 문제를 해결한다.</li>
<li>전역 상태 관리가 쉽다.</li>
<li>코드의 재사용성이 높아진다.</li>
</ul>
<p>Context API는 리액트에서 전역 상태를 관리하기 위해 사용되어. 기본적으로는 <strong><code>createContext</code></strong>, <strong><code>Context.Provider</code></strong>, 그리고 <strong><code>useContext</code></strong> 훅을 이용해서 사용하게 된다.</p>
<h2 id="createcontext"><strong>createContext</strong></h2>
<p>React에서 새로운 컨텍스트를 생성할 때 사용한다. <strong><code>React.createContext()</code></strong>를 호출하면 Provider와 Consumer 컴포넌트가 생성되며, 이를 통해 상태를 공유할 수 있다.</p>
<p>먼저, context를 만든다:</p>
<pre><code class="language-jsx">import React, { createContext, useContext, useState } from &#39;react&#39;;

const MyContext = createContext(null);</code></pre>
<h2 id="provider"><strong>Provider</strong></h2>
<p>생성된 컨텍스트에 포함된 Provider 컴포넌트는 상태를 공급하는 역할을 한다. <strong><code>value</code></strong> 속성을 통해 전달하고자 하는 상태를 지정할 수 있다.</p>
<p><strong><code>Provider</code></strong>를 이용해서 상태를 공유하고 싶은 컴포넌트 트리를 감싼다:</p>
<pre><code class="language-jsx">function App() {
  const [state, setState] = useState(&quot;I&#39;m a shared state!&quot;);

  return (
    &lt;MyContext.Provider value={{ state, setState }}&gt;
      &lt;MyComponent /&gt;
    &lt;/MyContext.Provider&gt;
  );
}</code></pre>
<h2 id="usecontext-훅"><strong>useContext 훅</strong></h2>
<p>함수형 컴포넌트 내에서 <strong><code>useContext</code></strong> 훅을 사용하면 해당 컨텍스트의 현재 값을 쉽게 사용할 수 있다. 이 훅은 Provider에서 전달한 <strong><code>value</code></strong> 값을 반환한다.</p>
<p>이제 <strong><code>useContext</code></strong> 훅을 이용해서 하위 컴포넌트에서 이 상태를 사용할 수 있다:</p>
<pre><code class="language-jsx">function MyComponent() {
  const { state, setState } = useContext(MyContext);

  return (
    &lt;div&gt;
      &lt;p&gt;{state}&lt;/p&gt;
      &lt;button onClick={() =&gt; setState(&quot;New State&quot;)}&gt;Change State&lt;/button&gt;
    &lt;/div&gt;
  );
}</code></pre>
<p>이게 기본적인 Context API의 사용 방법이다. 상태를 공유할 범위에 따라 여러 개의 Context를 만들 수도 있고, 복잡한 로직을 처리하기 위해 여러 Context를 중첩해서 사용할 수도 있다.</p>
<h2 id="결론">결론</h2>
<p>Context API는 리액트에서 전역 상태를 효과적으로 관리할 수 있는 강력한 도구이다. 적절히 사용하면 컴포넌트 간의 상태 공유가 더욱 쉬워진다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리액트] Redux toolkit]]></title>
            <link>https://velog.io/@river-m/%EB%A6%AC%EC%95%A1%ED%8A%B8-redux-toolkit</link>
            <guid>https://velog.io/@river-m/%EB%A6%AC%EC%95%A1%ED%8A%B8-redux-toolkit</guid>
            <pubDate>Mon, 04 Sep 2023 06:13:22 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><strong>Redux Toolkit: Redux Toolkit</strong>은 Redux의 공식 도구세트로, 상태 관리를 좀 더 간단하고 효율적으로 할 수 있도록 도와준다. 기본 Redux 설정은 코드가 많고 반복적일 수 있는데, Redux Toolkit은 이런 부분을 최소화해준다.</p>
</blockquote>
<hr>
<p>먼저 <strong><code>store.js</code></strong> 파일을 생성한다.  Redux Toolkit의 <strong><code>configureStore</code></strong> API를 가져온다.</p>
<p>아래는 초기 설정된 Redux Store의 예시 코드이다.</p>
<p><strong>store.js</strong></p>
<pre><code class="language-jsx">import { configureStore } from &#39;@reduxjs/toolkit&#39;

export const store = configureStore({
  reducer: {},
})
</code></pre>
<p>이렇게 하면 Redux Store가 생성된다.</p>
<ul>
<li><strong><code>configureStore</code></strong>는 Redux Toolkit에서 제공하는 API로, Redux store를 쉽게 설정할 수 있게 해준다. 이 함수는 여러 가지 Redux 미들웨어와 DevTools 설정 같은 것들을 자동으로 처리해줘서, 원래 Redux에서는 꽤 번거로웠던 설정을 간단하게 해줄 수 있다.</li>
<li><strong><code>reducer: {}</code></strong> 부분은 Redux의 리듀서를 등록하는 곳이다. 리듀서는 액션에 따라 상태를 어떻게 변경할지 정의하는 함수인데, 여기에 <strong><code>{}</code></strong> 이렇게 비어있으면 아무 리듀서도 등록되지 않은 상태라고 보면 된다.</li>
</ul>
<p><strong>store</strong>가 생성되면, React-Redux의 <strong><code>&lt;Provider&gt;</code></strong>를 애플리케이션 주위에 배치하여 React 구성 요소에서 Redux 스토어를 사용할 수 있게 설정할 수 있다. 이를 위해 <strong><code>src/main.js</code></strong> 파일에서 방금 생성한 Redux 스토어를 가져와 <strong><code>&lt;Provider&gt;</code></strong>를 <strong><code>&lt;App&gt;</code></strong> 주위에 배치하고, 스토어를 props로 전달한다. 이렇게 하면 React 구성 요소에서 Redux 스토어에 접근할 수 있게 된다. </p>
<p><strong>main.jsx</strong></p>
<pre><code class="language-jsx">import React from &#39;react&#39;
import ReactDOM from &#39;react-dom&#39;
import &#39;./index.css&#39;
import App from &#39;./App&#39;
import { store } from &#39;./store/store&#39;
import { Provider } from &#39;react-redux&#39;

ReactDOM.render(
  &lt;Provider store={store}&gt;
    &lt;App /&gt;
  &lt;/Provider&gt;,
  document.getElementById(&#39;root&#39;)
)</code></pre>
<p><code>counterSlice.js</code> 이름의 새파일을 추가한다. 해당 파일에서 <code>createSlice</code>Redux Toolkit에서 API를 가져온다.</p>
<p><strong>counterSlice.js</strong></p>
<pre><code class="language-jsx">import { createSlice } from &#39;@reduxjs/toolkit&#39;;

const counterSlice = createSlice({
  name: &#39;counter&#39;,
  initialState: 0,
  reducers: {
    increment: (state) =&gt; state + 1,
    decrement: (state) =&gt; state - 1,
  },
});

export const { increment, decrement } = counterSlice.actions;
export default counterSlice.reducer;</code></pre>
<p><strong><code>createSlice</code></strong>는 Redux Toolkit의 API 중 하나로, 액션 생성자와 리듀서를 쉽게 만들어주는 함수다. 일반적으로 Redux에서 액션 타입, 액션 생성자, 리듀서를 각각 따로 작성해야 하는데, <strong><code>createSlice</code></strong>를 사용하면 이 모든 것을 한 번에 처리할 수 있다.</p>
<p><strong><code>createSlice</code></strong> 함수는 객체를 인자로 받는데, 이 객체에는 <strong><code>name</code></strong>, <strong><code>initialState</code></strong>, 그리고 <strong><code>reducers</code></strong> 필드가 필요하다. </p>
<p><strong><code>name</code></strong>은 Slice의 이름, <strong><code>initialState</code></strong>는 초기 상태, <strong><code>reducers</code></strong>는 리듀서 함수들을 객체 형태로 정의한다.</p>
<p>이렇게 하면 <strong><code>counterSlice.actions</code></strong>에는 <strong><code>increment</code></strong>와 <strong><code>decrement</code></strong> 액션 생성자가 자동으로 생성되고, <strong><code>counterSlice.reducer</code></strong>에는 리듀서 함수가 생성된다. 이를 통해 코드를 훨씬 간결하고 관리하기 쉽게 만들 수 있다.</p>
<p> <strong><code>counterSlice</code></strong>라는 이름의 slice를 만들었다면, 이제 Redux Store 애는 이렇게 설정할 수 있다.</p>
<p><strong>Store</strong>.<strong>js</strong></p>
<pre><code class="language-jsx">import { configureStore } from &#39;@reduxjs/toolkit&#39;
import counterSlice from &#39;./counterSlice&#39;

export const store = configureStore({
  reducer: {
    counter: counterSlice.reducer
  }
})</code></pre>
<p>이렇게 하면 <strong><code>counterSlice.reducer</code></strong>가 <strong><code>counter</code></strong>라는 이름으로 store에 등록된다. 이제 이 리듀서는 <strong><code>state.counter</code></strong>로 접근할 수 있게 될 것이다. 이 slice에서 정의한 액션 생성자들로 상태를 변경할 수 있게 될 것이다.</p>
<p>이제 React-Redux의 훅을 사용해 React 컴포넌트가 Redux Store와 상호작용할 수 있다. <strong><code>useSelector</code></strong>를 통해 저장소의 데이터를 가져오고, <strong><code>useDispatch</code></strong>를 통해 액션을 디스패치할 수 있다.</p>
<p><strong>App.jsx</strong></p>
<pre><code class="language-jsx">import { useDispatch, useSelector } from &#39;react-redux&#39;;
import { increment, decrement } from &#39;./counterSlice&#39;;

function App() {
  const dispatch = useDispatch();
  const counter = useSelector((state) =&gt; state.counter);

  return (
    &lt;div className=&quot;App&quot;&gt;
      &lt;h1&gt;Counter: {counter}&lt;/h1&gt;
      &lt;button onClick={() =&gt; dispatch(increment())}&gt;Increment&lt;/button&gt;
      &lt;button onClick={() =&gt; dispatch(decrement())}&gt;Decrement&lt;/button&gt;
    &lt;/div&gt;
  );
}

export default App;</code></pre>
<ol>
<li><strong><code>useDispatch</code></strong>와 <strong><code>useSelector</code></strong> 훅을 <strong><code>react-redux</code></strong>에서 가져온다. 이 훅들은 Redux store와 상호작용하기 위해 사용된다.</li>
<li><strong><code>increment</code></strong>와 <strong><code>decrement</code></strong> 액션 생성자를 <strong><code>counterSlice</code></strong>에서 가져온다.</li>
<li><strong><code>useDispatch</code></strong> 훅을 사용해 Redux store에 dispatch할 수 있는 함수를 가져온다.</li>
<li><strong><code>useSelector</code></strong> 훅을 사용해 Redux store의 <strong><code>counter</code></strong> 상태를 가져온다.</li>
<li><strong><code>Increment</code></strong>와 <strong><code>Decrement</code></strong> 버튼을 렌더링하고, 각 버튼이 클릭되면 <strong><code>increment</code></strong> 또는 <strong><code>decrement</code></strong> 액션을 dispatch한다.</li>
</ol>
<p>이상으로 React와 Redux Toolkit을 설정하고 사용하는 방법에 대한 간략한 개요를 마무리하겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리액트] 전역 상태 관리]]></title>
            <link>https://velog.io/@river-m/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%A0%84%EC%97%AD-%EC%83%81%ED%83%9C-%EA%B4%80%EB%A6%AC</link>
            <guid>https://velog.io/@river-m/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%A0%84%EC%97%AD-%EC%83%81%ED%83%9C-%EA%B4%80%EB%A6%AC</guid>
            <pubDate>Mon, 04 Sep 2023 06:04:56 GMT</pubDate>
            <description><![CDATA[<h2 id="전역-상태-관리란-무엇인가">전<strong>역 상태 관리란 무엇인가?</strong></h2>
<p>전역 상태 관리는 여러 컴포넌트에서 공유해야 하는 상태를 한곳에서 중앙 집중적으로 관리하는 것을 의미한다. 예를 들어, 로그인한 사용자의 정보, 설정 값, 테마 등이 전역 상태에 해당한다. 이러한 전역 상태를 각각의 컴포넌트에서 일일이 관리하는 것은 효율적이지 않다. 따라서 전역 상태 관리는 상태를 효율적으로 관리하기 위한 필수적인 도구다.</p>
<h2 id="왜-전역-상태-관리를-사용해야-하는가"><strong>왜 전역 상태 관리를 사용해야 하는가?</strong></h2>
<ol>
<li><strong>데이터 일관성</strong>: 상태가 여러 곳에서 관리될 경우 데이터 불일치 문제가 발생할 수 있다. 중앙에서 관리하면 이런 문제를 피할 수 있다.</li>
<li><strong>코드 재사용성</strong>: 상태 관리 로직을 한 곳에서 관리하므로, 필요한 로직을 재사용하기 쉽다.</li>
<li><strong>유지보수</strong>: 상태가 어떻게 변경되고 전파되는지 명확하기 때문에, 디버깅이나 추가 기능 구현이 간단해진다.</li>
</ol>
<h3 id="전역-상태-관리를-사용하지-않을-경우"><strong>전역 상태 관리를 사용하지 않을 경우</strong></h3>
<p>전역 상태 관리를 사용하지 않을 경우, 주로 부모-자식 컴포넌트 간에 <strong><code>props</code></strong>를 통해 상태를 전달한다. 아니면, 커스텀 이벤트를 사용해서 상태를 관리할 수도 있다.</p>
<h4 id="어떻게-상태를-전달하나">어떻게 상태를 전달하나?</h4>
<ol>
<li><strong>Props 전달</strong>: 상위 컴포넌트에서 하위 컴포넌트로 <strong><code>props</code></strong>를 통해 상태를 내려보낸다.</li>
<li><strong>Callback 함수</strong>: 하위 컴포넌트에서 상태를 변경할 필요가 있을 경우, 상위 컴포넌트에서 변경을 위한 함수를 <strong><code>props</code></strong>로 전달한다.</li>
</ol>
<h4 id="불편한-점은">불편한 점은?</h4>
<ol>
<li><strong>Props 드릴링</strong>: 상태를 가장 하위 컴포넌트까지 전달해야 하는 경우, 중간에 있는 모든 컴포넌트들을 거쳐야 한다.</li>
<li><strong>데이터 일관성</strong>: 여러 컴포넌트에서 같은 상태를 관리할 경우, 일관성을 유지하기 어렵다.</li>
<li><strong>재사용성</strong>: 같은 상태 로직을 여러 곳에서 사용할 경우, 코드를 중복으로 작성해야 할 수 있다.</li>
</ol>
<h4 id="일반적으로-전역-상태-관리로-관리하는-상태들"><strong>일반적으로 전역 상태 관리로 관리하는 상태들</strong></h4>
<ol>
<li><strong>사용자 인증 정보</strong>: 로그인한 사용자의 정보나 토큰 등</li>
<li><strong>앱 설정</strong>: 사용자가 설정한 테마, 언어, 레이아웃 등</li>
<li><strong>캐시된 데이터</strong>: 서버에서 가져온 데이터를 재사용할 경우</li>
<li><strong>알림 및 메시지</strong>: 알림이나 오류 메시지 등의 전역적인 UI 상태</li>
<li><strong>쇼핑 카트</strong>: 온라인 쇼핑 사이트에서 사용자가 담은 상품 목록 등</li>
</ol>
<blockquote>
<p>이렇게 보면 전역 상태 관리가 필요한 상황과 그렇지 않은 상황이 명확히 구분된다. 따라서 상황에 따라 가장 적절한 상태 관리 전략을 선택하는 것이 중요하다.</p>
</blockquote>
<h2 id="리액트에서의-전역-상태-관리-도구"><strong>리액트에서의 전역 상태 관리 도구</strong></h2>
<ol>
<li><strong>Context API</strong>: 리액트 팀이 공식적으로 지원하는 상태 관리 도구다. <strong><code>useState</code></strong>나 <strong><code>useReducer</code></strong>와 함께 사용하여 상태를 관리한다.</li>
<li><strong>Redux</strong>: 가장 널리 알려진 상태 관리 라이브러리 중 하나다. 미들웨어 지원, 타임 트래블 디버깅, 커뮤니티와의 호환성 등 많은 이점이 있다.<ul>
<li><strong>Redux Toolkit: Redux Toolkit</strong>은 Redux의 공식 도구세트로, 상태 관리를 좀 더 간단하고 효율적으로 할 수 있도록 도와준다. 기본 Redux 설정은 코드가 많고 반복적일 수 있는데, Redux Toolkit은 이런 부분을 최소화해준다.</li>
</ul>
</li>
</ol>
<h3 id="다른-전역-상태-관리-라이브러리들"><strong>다른 전역 상태 관리 라이브러리들</strong></h3>
<ol>
<li><strong>MobX</strong>: 객체 지향적인 접근으로 상태 관리를 할 수 있다. 데코레이터를 활용하여 코드를 더 깔끔하게 유지할 수 있다.</li>
<li><strong>Recoil</strong>: 페이스북에서 개발한 상태 관리 라이브러리로, 아토믹 디자인을 통한 세밀한 상태 관리가 가능하다.</li>
<li><strong>jotai</strong>: 아토믹 디자인 원칙에 기반한 상태 관리 라이브러리다. Recoil과 유사하나 더 경량화되어 있다.</li>
<li><strong>zustand</strong>: 함수형 프로그래밍 패러다임을 적용한 작은 라이브러리로, 쉽게 상태 관리를 할 수 있다.</li>
</ol>
<h3 id="각-도구와-라이브러리의-차이-장단점"><strong>각 도구와 라이브러리의 차이, 장단점</strong></h3>
<ol>
<li><strong>Context API</strong><ul>
<li><strong>장점</strong>: 별도의 라이브러리 설치가 필요 없고, 리액트의 내장 기능이므로 학습 곡선이 낮다.</li>
<li><strong>단점</strong>: 상태 로직이 복잡해지면 관리하기 어렵다. 또한, 리렌더링 효율이 떨어질 수 있다.</li>
</ul>
</li>
<li><strong>Redux</strong><ul>
<li><strong>장점</strong>: 강력한 미들웨어 지원, 커뮤니티가 활발하므로 다양한 자료와 플러그인이 있다.</li>
<li><strong>단점</strong>: 초기 설정과 학습 곡선이 높다. 또한, 간단한 상태 관리에는 오버킬이 될 수 있다.</li>
</ul>
</li>
<li><strong>Redux Toolkit</strong><ul>
<li><strong>장점:</strong><ol>
<li><strong>보일러플레이트 코드 최소화</strong>: 액션 생성 함수, 리듀서, 슬라이스 등을 쉽게 만들 수 있다.</li>
<li><strong>Immer 라이브러리 포함</strong>: 불변성을 쉽게 관리할 수 있다.</li>
<li><strong>미들웨어 기본 설정</strong>: Redux Thunk가 기본적으로 내장되어 있다.</li>
</ol>
</li>
<li><strong>단점</strong>:  기본 Redux를 모르는 상태에서 Toolkit을 시작하면, 내부가 어떻게 동작하는지 이해하기 어려울 수 있다.</li>
</ul>
</li>
<li><strong>MobX</strong><ul>
<li><strong>장점</strong>: 자유도가 높고, 객체 지향 프로그래밍에 친화적이다.</li>
<li><strong>단점</strong>: 리액트와의 통합이 쉽지 않을 수 있다. 디버깅이 어려울 수 있다.</li>
</ul>
</li>
<li><strong>Recoil</strong><ul>
<li><strong>장점</strong>: 아토믹 디자인 원칙을 따라 세밀한 상태 관리가 가능하다.</li>
<li><strong>단점</strong>: 아직 실험적이고, 커뮤니티 지원이 부족하다.</li>
</ul>
</li>
<li><strong>jotai</strong><ul>
<li><strong>장점</strong>: Recoil과 유사하게 아토믹 디자인을 지원하지만 더 경량화되어 있다.</li>
<li><strong>단점</strong>: 아직 새로운 라이브러리로, 문서와 커뮤니티 지원이 부족할 수 있다.</li>
</ul>
</li>
<li><strong>zustand</strong><ul>
<li><strong>장점</strong>: 함수형 프로그래밍 패러다임을 적용하여 간단하게 상태를 관리할 수 있다.</li>
<li><strong>단점</strong>: 대규모 애플리케이션에서는 다른 라이브러리에 비해 관리가 어려울 수 있다.</li>
</ul>
</li>
</ol>
<h2 id="결론"><strong>결론</strong></h2>
<p>전역 상태 관리는 복잡한 애플리케이션에서 상태를 일관되게 관리할 수 있게 해준다. 리액트에서는 다양한 도구와 라이브러리가 있어서, 프로젝트의 요구사항과 팀의 선호에 따라 선택할 수 있다. Redux나 MobX, Recoil처럼 잘 알려진 라이브러리부터, jotai나 zustand처럼 더 새롭고 경량화된 라이브러리까지 다양한 옵션이 있다. 이 중 Redux Toolkit은 Redux를 더 쉽고 간결하게 사용할 수 있게 도와주는 유용한 도구세트다.</p>
<blockquote>
<p>전역 상태 관리는 복잡한 앱을 구축할 때 필수적인 부분이다. 여러 전역 상태 관리 방법의 장단점을 이해하고, 프로젝트에 가장 적합한 방법을 선택하는 것이 중요하다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리액트] SPA와 React Router]]></title>
            <link>https://velog.io/@river-m/%EB%A6%AC%EC%95%A1%ED%8A%B8-SPA%EC%99%80-React-Router</link>
            <guid>https://velog.io/@river-m/%EB%A6%AC%EC%95%A1%ED%8A%B8-SPA%EC%99%80-React-Router</guid>
            <pubDate>Tue, 15 Aug 2023 12:52:59 GMT</pubDate>
            <description><![CDATA[<h2 id="spa-single-page-application란">SPA (Single Page Application)란</h2>
<p>SPA는 Single Page Application의 약자로, 웹 페이지가 단 하나의 HTML 파일로 이루어진 애플리케이션이다. 전통적인 웹 페이지와는 달리, 사용자와 상호작용할 때마다 새로운 페이지를 로드하는 대신, 필요한 데이터만 서버에서 불러와 동적으로 현재 페이지를 갱신한다.</p>
<h3 id="장점">장점</h3>
<ol>
<li><strong>빠른 반응성</strong>: 전체 페이지를 새로고침하지 않으므로 사용자 경험이 부드럽다.</li>
<li><strong>서버 부하 감소</strong>: 필요한 부분만 업데이트하기 때문에 서버에 부담이 적다.</li>
<li><strong>개발 편리성</strong>: 프론트엔드와 백엔드를 분리하여 개발이 용이하다.</li>
</ol>
<h3 id="단점">단점</h3>
<ol>
<li><strong>SEO 문제</strong>: Cilent Side Rendering 방식으로 동작해서 검색 엔진 최적화가 어려울 수 있다.</li>
<li><strong>초기 로딩 시간</strong>: 앱의 모든 자원을 처음에 로드해야 하므로 초기 로딩 시간이 길 수 있다.<h2 id="리액트-라우터"><a href="https://reactrouter.com/en/main">리액트 라우터</a></h2>
</li>
</ol>
<p>리액트 라우터는 SPA를 만드는 리액트 애플리케이션에서 라우팅을 관리하는 라이브러리다. 라우팅은 사용자의 요청에 따라 다른 뷰를 보여주는 역할을 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리액트] 컴포넌트 스타일링]]></title>
            <link>https://velog.io/@river-m/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%8A%A4%ED%83%80%EC%9D%BC%EB%A7%81</link>
            <guid>https://velog.io/@river-m/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%8A%A4%ED%83%80%EC%9D%BC%EB%A7%81</guid>
            <pubDate>Mon, 14 Aug 2023 15:06:40 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>리액트에서 컴포넌트의 스타일링은 중요한 작업 중 하나이다. 다양한 방법으로 스타일을 적용할 수 있으며, 각 방법은 다음과 같다.</p>
</blockquote>
<h2 id="1-inline-style">1. Inline style</h2>
<p>Inline style은 객체 형태로 스타일을 정의하고, 컴포넌트의 <strong><code>style</code></strong> 속성에 전달하는 방법이다. 이 방법은 간편하게 스타일을 적용할 수 있으나, 복잡한 스타일링에는 제한적일 수 있다.  그리고 inline style은 보기 안좋다..</p>
<pre><code class="language-jsx">const style = {
  color: &#39;blue&#39;,
  fontSize: &#39;16px&#39;
};

function MyComponent() {
  return &lt;div style={style}&gt;Hello, World!&lt;/div&gt;;
}
</code></pre>
<h2 id="2-classname">2. className</h2>
<p>전통적인 CSS 파일을 사용하고 싶다면 <code>className</code> 속성을 활용해야 한다. CSS 클래스를 정의하고, 해당 클래스 이름을 컴포넌트에 할당하면 된다. className만 CamelCase 뿐이지 일반 HTML class를 작성하는 방법은 똑같다.</p>
<pre><code class="language-css">.my-class {
  color: red;
}

// JS
function MyComponent() {
  return &lt;div className=&quot;my-class&quot;&gt;Hello, World!&lt;/div&gt;;
}
</code></pre>
<h2 id="3-css-modules">3. CSS Modules</h2>
<p>CSS Modules는 클래스 이름의 중복을 막기 위한 기술이다. 웹팩이나 기타 빌드 도구와 함께 사용되는 CSS 처리 방식 중 하나이다. 이 기술은 전역 범위의 CSS 클래스 이름 충돌을 방지하며, 각 컴포넌트별로 독립적인 스타일을 정의할 수 있게 해준다. CSS 파일을 모듈처럼 불러와 컴포넌트에 적용할 수 있다. </p>
<h3 id="1-작성-방법">1<strong>. 작성 방법</strong></h3>
<p>CSS Modules를 사용하려면 <strong><code>.module.css</code></strong> 확장자를 사용해야 한다. 클래스 이름을 작성할 때는 일반 CSS와 동일하나, 해당 CSS 파일을 자바스크립트로 불러올 때 모듈처럼 사용한다.</p>
<pre><code class="language-css">/* style.module.css */
.myClass {
  color: green;
}

// JS
import styles from &#39;./style.module.css&#39;;

function MyComponent() {
  return &lt;div className={styles.myClass}&gt;Hello, World!&lt;/div&gt;;
}
</code></pre>
<h3 id="2-성능과-유지-보수">2<strong>. 성능과 유지 보수</strong></h3>
<p>CSS Modules는 개발 시에만 로컬 스코핑이 적용되므로 런타임 성능에 영향을 주지 않는다. 또한 컴포넌트별로 독립적인 스타일을 관리할 수 있어 유지 보수가 용이하다.</p>
<h3 id="3-로컬-스코핑-local-scoping">3<strong>. 로컬 스코핑 (Local Scoping)</strong></h3>
<p>전통적인 CSS에서는 클래스 이름이 전역 범위에 정의되어, 이름 충돌이 일어날 수 있다. CSS Modules는 이 문제를 해결하기 위해 클래스 이름을 로컬 스코핑하며, 각 컴포넌트가 고유한 스타일을 가질 수 있게 해준다. 그래서 클래스네임을 신경써서 짓지 않아도 된다. </p>
<blockquote>
<p>CSS에서 로컬 스코핑이란, 특정 컴포넌트 또는 모듈 내에서만 유효한 스타일 클래스 이름을 정의하는 것을 의미한다. 이로 인해 같은 클래스 이름을 여러 컴포넌트에서 사용해도 서로 영향을 주지 않는다.
<strong>예를 들어:</strong></p>
</blockquote>
<pre><code class="language-jsx">// 컴포넌트 A의 스타일
.button {
  color: red;
}

// 컴포넌트 B의 스타일
.button {
  color: blue;
}</code></pre>
<p>정상적으로 일반 css인 경우 같은 페이지에서 두 컴포넌트가 공존 한다면 클래스 네임 충돌하여 예상치 못한 결과를 초래할 수 있다.</p>
<p>CSS Modules를 사용하면 작성할 땐<code>.button</code>이지만 개발자 도구로 해당 컴포넌트의 요소를 보면</p>
<p>컴포넌트 A 클래스 네임은 <code>_button_7oxvs_33</code> 컴포넌트 B는 <code>_button_7oxvs_53</code> 이런 식으로  유니크한 클래스 네임을 새롭게 만들어줘서 중복되지 못하도록 한다.</p>
<blockquote>
<p>CSS Modules는 SCSS 도 사용할수 있다. 같이 사용하면 엄청 편해지는걸 채감할수 있다.</p>
</blockquote>
<h2 id="4-css-in-js">4. CSS in JS</h2>
<p>CSS in JS는 스타일을 자바스크립트 내에서 작성하는 패러다임이다. 여러 라이브러리가 이 방식을 지원하며, JS와 CSS의 경계를 허문다.</p>
<h3 id="styled-components-라이브러리">styled-components (라이브러리)</h3>
<pre><code class="language-jsx">import styled from &#39;styled-components&#39;;

const MyDiv = styled.div`
  color: orange;
`;

function MyComponent() {
  return &lt;MyDiv&gt;Hello, World!&lt;/MyDiv&gt;;
}</code></pre>
<h2 id="결론">결론</h2>
<p>리액트에서 컴포넌트 스타일링은 다양한 방법으로 할 수 있다. 프로젝트의 요구사항과 팀의 코딩 스타일에 맞게 적절한 방법을 선택하는 것이 중요하다. 각 방법은 그 자체의 장단점이 있으니, 상황에 맞게 잘 고려해 사용하면 될 것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리액트] 합성(Composition)]]></title>
            <link>https://velog.io/@river-m/%EB%A6%AC%EC%95%A1%ED%8A%B8-%ED%95%A9%EC%84%B1Composition</link>
            <guid>https://velog.io/@river-m/%EB%A6%AC%EC%95%A1%ED%8A%B8-%ED%95%A9%EC%84%B1Composition</guid>
            <pubDate>Mon, 14 Aug 2023 11:30:16 GMT</pubDate>
            <description><![CDATA[<h2 id="1-합성의-의미">1. 합성의 의미</h2>
<p>합성은 리액트에서 컴포넌트를 작성하는 기본 패턴 중 하나로, 하나 이상의 컴포넌트를 결합하여 새로운 컴포넌트를 생성하는 방식이다. 이를 통해 코드의 재사용성을 높이고 유지 보수를 간소화한다.</p>
<h2 id="2-props로-컴포넌트-전달하기">2. Props로 컴포넌트 전달하기</h2>
<p>리액트에서 합성을 구현하는 기본적인 방법은 props를 통해 컴포넌트를 전달하는 것이다.</p>
<pre><code class="language-jsx">function Dialog(props) {
  return (
    &lt;div className=&quot;dialog&quot;&gt;
      {props.title}
      {props.children}
    &lt;/div&gt;
  );
}

function WelcomeDialog() {
  return (
    &lt;Dialog title=&quot;Welcome!&quot;&gt;
      &lt;p&gt;Hello, User!&lt;/p&gt;
    &lt;/Dialog&gt;
  );
}
</code></pre>
<h2 id="3-container-컴포넌트와-presentational-컴포넌트">3. Container 컴포넌트와 Presentational 컴포넌트</h2>
<p>합성을 이용하면 로직을 처리하는 Container 컴포넌트와 UI를 렌더링하는 Presentational 컴포넌트로 나눌 수 있어, 관심사의 분리가 이루어진다.</p>
<h2 id="4-higher-order-component-hoc">4. Higher-Order Component (HOC)</h2>
<p>HOC는 컴포넌트를 인자로 받아 새로운 컴포넌트를 반환하는 함수로, 로직을 공유하고자 할 때 유용하다.</p>
<h2 id="5-render-props">5. Render Props</h2>
<p>Render Props는 children props로 함수를 전달받아 컴포넌트의 렌더링 방식을 사용자가 결정하게 한다. 이를 통해 더 유연한 컴포넌트 작성이 가능하다.</p>
<h2 id=""></h2>
<h2 id="결론">결론</h2>
<p>리액트의 합성은 컴포넌트 구조를 깔끔하고 유지 보수가 쉽게 만드는 데 있어 중요한 역할을 한다. 컴포넌트의 재사용성을 높이고, 관심사의 분리를 통해 가독성과 유지 보수성을 향상시킨다. 다양한 패턴과 함께 적절히 활용하면 리액트 애플리케이션의 효율과 품질을 높일 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리액트] Ref]]></title>
            <link>https://velog.io/@river-m/%EB%A6%AC%EC%95%A1%ED%8A%B8-Ref</link>
            <guid>https://velog.io/@river-m/%EB%A6%AC%EC%95%A1%ED%8A%B8-Ref</guid>
            <pubDate>Sun, 13 Aug 2023 11:29:38 GMT</pubDate>
            <description><![CDATA[<p>리액트에서는 Ref라는 기능을 사용해 컴포넌트 내부의 특정 DOM에 직접 접근할 수 있다.</p>
<h2 id="ref-란">Ref 란</h2>
<p>Ref는 &quot;reference&quot;의 약자로, 리액트에서 특정 DOM 엘리먼트나 컴포넌트 인스턴스에 접근하는 방법을 제공한다. Ref를 이용하면 직접적인 DOM 조작이 가능해져 특정 요소에 포커스를 주거나, 크기 또는 위치를 계산 하는 일이나, 미디어의 재생을 제어하는 등의 작업을 할 수 있다. </p>
<p>함수형 컴포넌트에서는 <strong><code>useRef</code></strong> 훅을 사용해 Ref를 생성하고 관리한다. 이 훅은 렌더링 사이에서 Ref 객체의 <strong><code>current</code></strong> 값을 유지해주기 때문에, 렌더링이 일어나더라도 Ref 값은 변하지 않는다. </p>
<p>예를 들어, 입력 필드에 사용자가 클릭한 시점에 자동으로 포커스를 주고 싶다면 다음과 같이 <strong><code>useRef</code></strong>를 사용할 수 있다:</p>
<h3 id="사용-예시">사용 예시</h3>
<pre><code class="language-jsx">import React, { useRef } from &#39;react&#39;;

function MyComponent() {
  const myInput = useRef();

  const handleClick = () =&gt; {
    myInput.current.focus();
  };

  return (
    &lt;div&gt;
      &lt;input ref={myInput} /&gt;
      &lt;button onClick={handleClick}&gt;Focus the input&lt;/button&gt;
    &lt;/div&gt;
  );
}</code></pre>
<p>위 예제에서 <strong><code>useRef</code></strong> 훅은 입력 엘리먼트에 대한 참조를 저장하고, 버튼이 클릭되면 해당 참조를 통해 입력 엘리먼트에 직접 포커스를 준다. Ref는 사용하기 쉬운 동시에 강력한 기능을 제공하므로, 적절한 상황과 케이스에서 활용해야 하며 그렇지 않으면 복잡도를 높일 수 있다는 점을 유념해야 한다.</p>
<h2 id="forward-ref">forward Ref</h2>
<p>forward Ref는 리액트에서 Ref를 직접 다루기 보다, 자식 컴포넌트로 Ref를 &quot;전달&quot;하기 위한 기능이다. 즉, 부모 컴포넌트가 자식 컴포넌트의 내부 DOM 엘리먼트에 직접 접근할 필요가 있을 때 사용할 수 있다.</p>
<p>forward Ref를 사용하는 것은 큰 컴포넌트 트리에서 특정 자식 컴포넌트의 DOM 엘리먼트에 접근해야 할 때 유용하다. 이를 통해 부모 컴포넌트가 자식 컴포넌트의 내부 구조나 동작 방식을 알 필요 없이 필요한 작업을 수행할 수 있다.</p>
<p>리액트의 <code>React.forwardRef</code> 함수를 사용하면 forward Ref를 생성할 수 있다. 아래 예시는 <code>MyInput</code> 컴포넌트가 <code>ref</code>를 받아서 내부의 <code>input</code> 엘리먼트로 전달하는 모습을 보여준다.</p>
<pre><code class="language-jsx">const MyInput = forwardRef((props, ref) =&gt; (
  &lt;input ref={ref} type=&quot;text&quot; {...props} /&gt;
));

function ParentComponent() {
  const inputRef = useRef();

  return (
    &lt;div&gt;
      &lt;MyInput ref={inputRef} /&gt;
     &lt;button
        onClick={() =&gt; {
          alert(inputRef.current.value);
          inputRef.current.value = &quot;&quot;;
          inputRef.current.focus();
        }}
      &gt;포커스&lt;/button&gt;
    &lt;/div&gt;
  );
}
</code></pre>
<p>이 예제에서 <code>React.forwardRef</code>를 사용해 <code>MyInput</code> 컴포넌트는 받은 <code>ref</code>를 내부 <code>input</code> 엘리먼트에 전달한다. 부모 컴포넌트는 <code>MyInput</code> 컴포넌트의 내부 구조나 동작을 알 필요 없이 <code>input</code> 엘리먼트에 직접 접근할 수 있다.</p>
<p>forward Ref는 컴포넌트 설계에서 중요한 역할을 하며, 컴포넌트 간의 느슨한 결합을 유지하면서도 필요한 경우 특정 DOM 조작을 할 수 있게 해준다.</p>
<h2 id="mutable-object-ref">mutable object Ref</h2>
<p>mutable object Ref는 React의 <code>useRef</code> 훅을 사용하여 만들 수 있으며, 이 객체는 <code>.current</code> 프로퍼티를 가지고 있다. 이 프로퍼티는 변경 가능하며, 컴포넌트 렌더링 사이에서 값을 유지한다. 이는 값을 저장하고 관리하고 싶을 때 유용하다.</p>
<p>다음은 mutable object Ref를 사용하는 간단한 예시다.</p>
<pre><code class="language-jsx">import React, { useRef } from &#39;react&#39;;

function MyComponent() {
  const myRef = useRef(0); // 초기값 설정

  const handleClick = () =&gt; {
    myRef.current += 1; // current 값 변경
    console.log(myRef.current); // 변경된 값 로깅
  };

  return (
    &lt;button onClick={handleClick}&gt;
      Click me
    &lt;/button&gt;
  );
}
</code></pre>
<p>여기서 <code>useRef</code>를 사용하여 <code>myRef</code>라는 mutable object Ref를 생성하고, <code>.current</code> 프로퍼티를 통해 값에 접근하고 수정한다. <code>myRef.current</code>의 값은 컴포넌트가 다시 렌더링되어도 유지되므로, 컴포넌트의 상태나 생명 주기와 독립적인 값을 관리하고 싶을 때 사용할 수 있다.</p>
<p>이런 성질 때문에 mutable object Ref는 DOM 요소의 참조뿐만 아니라, 타이머 ID, 외부 라이브러리의 인스턴스 등과 같이 컴포넌트 렌더링과 무관한 값을 관리하는 데 사용될 수 있다.</p>
<p>mutable object Ref를 사용하면 값의 변경이 컴포넌트의 재렌더링을 유발하지 않기 때문에, 렌더링과 무관한 값을 효율적으로 관리할 수 있다는 장점이 있다.</p>
<p>이 방법은 리액트에서 값을 다루는 또 다른 유연한 방식으로, 상황에 맞게 적절하게 활용할 수 있어야 한다.</p>
<h2 id="callback-ref">callback Ref</h2>
<p>callback Ref는 리액트에서 Ref를 다루는 또 다른 방법으로, 함수를 사용하여 컴포넌트의 특정 DOM 요소에 대한 참조를 얻는다. 이 방식은 리액트의 생명 주기와 연동되어, 특정 작업을 더욱 세밀하게 제어할 수 있다.</p>
<p>callback Ref는 함수형 컴포넌트 뿐만 아니라 클래스 컴포넌트에서도 사용할 수 있다. 아래 예시처럼 <code>ref</code> 속성에 함수를 전달하여 사용한다.</p>
<pre><code class="language-jsx">function MyComponent() {
  const myInputRef = useCallback(node =&gt; {
    if (node !== null) {
      node.focus();
    }
  }, []);

  return &lt;input ref={myInputRef} /&gt;;
}
</code></pre>
<p>위 예제에서 <code>useCallback</code>을 사용하여 Ref 함수를 만들고, <code>input</code> 요소의 <code>ref</code> 속성에 할당한다. 이 함수는 컴포넌트가 마운트되거나 언마운트될 때 호출되며, 인자로 해당 DOM 요소 또는 <code>null</code>을 받는다.</p>
<p>이 방식은 더 많은 제어를 원할 때 유용하다. 예를 들어, 컴포넌트가 언마운트될 때 특정 작업을 수행해야 한다면 callback Ref가 적합하다.</p>
<p>callback Ref의 장점 중 하나는 Ref 객체를 직접 다루지 않아도 된다는 것이다. 이는 코드의 간결성과 유지보수의 편리성을 증가시킨다.</p>
<p>또한, callback Ref는 특정 조건이 충족될 때만 Ref를 설정하고 싶을 때도 유용하다. 함수 내에서 로직을 추가하면, 특정 조건에 따라 Ref를 설정하거나 무시할 수 있다.</p>
<p>callback Ref는 리액트의 Ref 시스템 중 매우 유연한 부분이며, 특별한 상황에서 Ref를 다룰 필요가 있을 때 효과적인 해결책을 제공한다.</p>
<h2 id="마무리">마무리</h2>
<p>Ref는 리액트에서 DOM 엘리먼트나 컴포넌트 인스턴스에 직접 접근할 수 있는 매우 유용한 기능이다. 다양한 사용 방법과 케이스에 따라 선택할 수 있는 ref 유형이 있으므로, 상황에 맞게 적절한 방법을 선택하면 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리액트]  폼, 제어 컴포넌트, 비제어 컴포넌트]]></title>
            <link>https://velog.io/@river-m/%EB%A6%AC%EC%95%A1%ED%8A%B8-%ED%8F%BC-%EC%A0%9C%EC%96%B4-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EB%B9%84%EC%A0%9C%EC%96%B4-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8</link>
            <guid>https://velog.io/@river-m/%EB%A6%AC%EC%95%A1%ED%8A%B8-%ED%8F%BC-%EC%A0%9C%EC%96%B4-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EB%B9%84%EC%A0%9C%EC%96%B4-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8</guid>
            <pubDate>Sat, 12 Aug 2023 15:06:13 GMT</pubDate>
            <description><![CDATA[<h2 id="폼">폼</h2>
<blockquote>
<p>HTML 폼 element는 폼 element 자체가 내부 상태를 가지기 때문에, React의 다른 DOM element와 다르게 동작한다.  웹 애플리케이션에서 폼은 사용자로부터 정보를 입력받는 중요한 요소이다.  리액트에서는 폼을 더 쉽게 다루고 제어할 수 있는 도구를 제공한다.</p>
</blockquote>
<p>리액트에서 폼을 다룰 때, 주로 두 가지 방법이 사용되는데, 제어 컴포넌트와 비제어 컴포넌트이다. 두 방법 모두 각자의 장단점이 있다.</p>
<h2 id="제어-컴포넌트">제어 컴포넌트</h2>
<p>리액트에서 폼 요소를 제어 컴포넌트로 만들면 값이 리액트의 상태에 의해 제어된다. 이렇게 하면 값의 변경을 감지하고 다루기가 훨씬 수월하다. HTML에서 <code>&lt;input&gt;</code>, <code>&lt;textarea&gt;</code>, <code>&lt;select&gt;</code>와 같은 폼 요소는 일반적으로 사용자의 입력을 기반으로 자신의 state를 관리하고 업데이트한다. React에서는 변경할 수 있는 state가 일반적으로 컴포넌트의 state 속성에 유지되며 <a href="https://ko.legacy.reactjs.org/docs/react-component.html#setstate"><code>setState()</code></a>에 의해 업데이트된다.</p>
<h3 id="제어-컴포넌트-예시">제어 컴포넌트 예시</h3>
<pre><code class="language-jsx">import React, { useState } from &#39;react&#39;;

function ControlledForm() {
  const [name, setName] = useState(&#39;&#39;);

  const handleChange = (e) =&gt; {
    setName(e.target.value);
  };

  return (
    &lt;form&gt;
      &lt;label&gt;
        이름:
        &lt;input type=&quot;text&quot; value={name} onChange={handleChange} /&gt;
      &lt;/label&gt;
    &lt;/form&gt;
  );
}
</code></pre>
<p><code>setName</code>을 사용하여 입력값을 변경하면 리액트가 컴포넌트를 다시 렌더링하고 최신 값이 입력 필드에 반영된다.</p>
<h3 id="폼-제출">폼 제출</h3>
<p>폼의 제출은 사용자가 입력한 데이터를 서버에 보내거나 다른 컴포넌트로 전달할 때 주로 사용된다.</p>
<pre><code class="language-jsx">function FormSubmit() {
  const [value, setValue] = useState(&#39;&#39;);

  const handleSubmit = (e) =&gt; {
    e.preventDefault();
    alert(`입력값: ${value}`);
  };

  return (
    &lt;form onSubmit={handleSubmit}&gt;
      &lt;input type=&quot;text&quot; value={value} onChange={(e) =&gt; setValue(e.target.value)} /&gt;
      &lt;button type=&quot;submit&quot;&gt;제출&lt;/button&gt;
    &lt;/form&gt;
  );
}
</code></pre>
<p><code>onSubmit</code> 이벤트 핸들러를 사용하면 폼 제출을 캡처할 수 있다.</p>
<h3 id="textarea-태그"><strong>textarea 태그</strong></h3>
<p>HTML에서 <code>&lt;textarea&gt;</code> 요소는 텍스트를 자식으로 정의한다.</p>
<pre><code class="language-jsx">&lt;textarea&gt;
  Hello there, this is some text in a text area
&lt;/textarea&gt;</code></pre>
<p>React에서 <code>&lt;textarea&gt;</code>는 <code>value</code> 어트리뷰트를 대신 사용한다.</p>
<pre><code class="language-jsx"> const [value, setValue] = useState(&#39;&#39;);
&lt;textarea value={value} onChange={handleChange} /&gt;</code></pre>
<h3 id="select-태그"><strong>select 태그</strong></h3>
<p>HTML에서 <code>&lt;select&gt;</code>는 드롭 다운 목록을 만든다. 예를 들어, 이 HTML은 과일 드롭 다운 목록을 만들고 있다.</p>
<pre><code class="language-jsx">&lt;select&gt;
  &lt;option value=&quot;grapefruit&quot;&gt;Grapefruit&lt;/option&gt;
  &lt;option value=&quot;lime&quot;&gt;Lime&lt;/option&gt;
  &lt;option selected value=&quot;coconut&quot;&gt;Coconut&lt;/option&gt;
  &lt;option value=&quot;mango&quot;&gt;Mango&lt;/option&gt;
&lt;/select&gt;</code></pre>
<p>React에서는 <code>selected</code> 어트리뷰트를 사용하는 대신 최상단 <code>select</code>태그에 <code>value</code> 어트리뷰트를 사용한다. 한 곳에서 업데이트만 하면되기 때문에 제어 컴포넌트에서 사용하기 더 편하다.</p>
<pre><code class="language-jsx">&lt;select value={value} onChange={handleChange}&gt;
            &lt;option value=&quot;grapefruit&quot;&gt;Grapefruit&lt;/option&gt;
            &lt;option value=&quot;lime&quot;&gt;Lime&lt;/option&gt;
            &lt;option value=&quot;coconut&quot;&gt;Coconut&lt;/option&gt;
            &lt;option value=&quot;mango&quot;&gt;Mango&lt;/option&gt;
          &lt;/select&gt;</code></pre>
<p>전반적으로 <code>&lt;input type=&quot;text&quot;&gt;</code>, <code>&lt;textarea&gt;</code> 및 <code>&lt;select&gt;</code> 모두 매우 비슷하게 동작한다. 모두 제어 컴포넌트를 구현하는데 <code>value</code> 어트리뷰트를 허용한다.</p>
<blockquote>
<p>리액트는 웹 폼을 다루는 강력한 도구를 제공한다. 상태 관리와 이벤트 핸들링을 통해 폼 입력과 제어, 유효성 검사를 쉽게 수행할 수 있다. 제대로된 구조와 패턴을 사용하면 사용자 경험을 향상시키고 개발 효율성을 높일 수 있다.</p>
</blockquote>
<h2 id="리액트에서-다중-입력-제어하기">리액트에서 다중 입력 제어하기</h2>
<p>다중 입력 필드를 가진 폼은 사용자로부터 여러 정보를 동시에 수집해야 할 때 흔히 사용된다. 리액트에서는 여러 입력 필드를 효율적으로 제어하는 방법을 제공하며, 상태를 사용해 여러 입력을 관리하는 방법을 살펴보자.</p>
<h3 id="다중-입력-상태-사용하기">다중 입력 상태 사용하기</h3>
<p>여러 입력 필드를 가진 폼의 경우, 각 입력 필드에 대한 상태를 따로 관리할 수도 있지만, 객체를 사용해 한 번에 관리하는 것이 더 효율적일 수 있다.</p>
<h3 id="다중-입력-예시">다중 입력 예시</h3>
<pre><code class="language-jsx">import React, { useState } from &#39;react&#39;;

function MultiInputForm() {
  const [form, setForm] = useState({
    name: &#39;&#39;,
    age: &#39;&#39;,
    email: &#39;&#39;
  });

  const handleChange = (e) =&gt; {
    const { name, value } = e.target;
    setForm({
      ...form,
      [name]: value
    });
  };

  return (
    &lt;form&gt;
      &lt;label&gt;
        이름:
        &lt;input type=&quot;text&quot; name=&quot;name&quot; value={form.name} onChange={handleChange} /&gt;
      &lt;/label&gt;
      &lt;label&gt;
        나이:
        &lt;input type=&quot;number&quot; name=&quot;age&quot; value={form.age} onChange={handleChange} /&gt;
      &lt;/label&gt;
      &lt;label&gt;
        이메일:
        &lt;input type=&quot;email&quot; name=&quot;email&quot; value={form.email} onChange={handleChange} /&gt;
      &lt;/label&gt;
    &lt;/form&gt;
  );
}
</code></pre>
<p><code>handleChange</code> 함수에서 이벤트 객체의 <code>name</code>과 <code>value</code>를 가져와서 상태를 업데이트한다. 이렇게 하면 코드를 간결하게 유지하면서 여러 입력 필드를 효과적으로 관리할 수 있다.</p>
<blockquote>
<p>리액트에서 다중 입력 제어는 상태를 통해 간단하게 구현할 수 있다. 한 객체 내에서 여러 입력 값을 관리함으로써 코드의 재사용성과 유지보수성을 높일 수 있다. 이를 통해 사용자로부터 여러 정보를 효율적으로 수집하고 처리할 수 있게 된다.</p>
</blockquote>
<hr>
<h2 id="비제어-컴포넌트">비제어 컴포넌트</h2>
<p>리액트에서는 폼을 다루는 방식 중 하나로 비제어 컴포넌트를 사용할 수 있다. 비제어 컴포넌트는 리액트 컴포넌트가 폼의 상태를 관리하지 않고, 폼 입력을 DOM 자체에서 처리한다.</p>
<blockquote>
<p>대부분 경우에 폼을 구현하는데 제어 컴포넌트를 사용하는것이 좋다.</p>
</blockquote>
<h3 id="특징">특징</h3>
<p>비제어 컴포넌트는 <code>ref</code>를 사용하여 폼 요소에 직접 접근할수 있다. 따라서, 리액트의 상태를 사용하지 않고, 폼 요소의 값을 직접 읽을 수 있다. 리액트에서는 <strong><code>useRef</code></strong>와 <strong><code>createRef</code></strong>라는 두 가지 방법으로 <strong><code>ref</code></strong>를 만들 수 있다. 함수형 컴포넌트에서는 <strong><code>useRef</code></strong>를, 클래스 컴포넌트에서는 <strong><code>createRef</code></strong>를 사용한다.</p>
<h3 id="useref-사용-예시"><strong>useRef 사용 예시</strong></h3>
<pre><code class="language-jsx">import React, { useRef } from &#39;react&#39;;

function UncontrolledForm() {
  const inputRef = useRef();

  const handleSubmit = (e) =&gt; {
    e.preventDefault();
    alert(&#39;A name was submitted: &#39; + inputRef.current.value);
  };

  return (
    &lt;form onSubmit={handleSubmit}&gt;
      &lt;label&gt;
        이름:
        &lt;input type=&quot;text&quot; ref={inputRef} /&gt;
      &lt;/label&gt;
      &lt;button type=&quot;submit&quot;&gt;제출&lt;/button&gt;
    &lt;/form&gt;
  );
}
</code></pre>
<p>이 코드에서 <code>inputRef</code>를 사용하여 입력 요소에 직접 접근하고 있다. <code>handleSubmit</code> ****이벤트 핸들러에서 <code>inputRef.current.value</code> 으로 input 폼요소의 값을 읽고 있다.</p>
<h2 id="제어-컴포넌트-과-비제어-컴포넌트-비교">제어 컴포넌트 과 비제어 컴포넌트 비교</h2>
<h3 id="제어-컴포넌트-1"><strong>제어 컴포넌트</strong></h3>
<p>제어 컴포넌트는 폼의 입력값이 리액트 컴포넌트의 상태에 의해 제어되는 컴포넌트를 말한다. 입력값이 변할 때마다 상태를 변경하고, 상태가 변경될 때마다 컴포넌트가 다시 렌더링된다.</p>
<h3 id="비제어-컴포넌트-1"><strong>비제어 컴포넌트</strong></h3>
<p>비제어 컴포넌트는 폼의 상태를 직접 관리하지 않고, DOM에서 값을 직접 가져오는 방식이다. <strong><code>ref</code></strong>를 사용해서 DOM 노드에 직접 접근한다.</p>
<table>
<thead>
<tr>
<th>제어 컴포넌트</th>
<th>비제어 컴포넌트</th>
</tr>
</thead>
<tbody><tr>
<td><strong>장점:</strong></td>
<td><strong>장점:</strong></td>
</tr>
<tr>
<td>• 상태에 따른 로직을 쉽게 구현할 수 있다.</td>
<td>• 코드가 더 간결하고 단순하다.</td>
</tr>
<tr>
<td>• 상태를 중앙에서 관리할 수 있다.</td>
<td>• 리액트의 상태 관리 없이도 폼 값을 관리할 수 있다.</td>
</tr>
<tr>
<td><strong>단점:</strong></td>
<td><strong>단점:</strong></td>
</tr>
<tr>
<td>• 코드가 다소 복잡해질 수 있다.</td>
<td>• 상태에 따른 로직을 구현하기 어렵다.</td>
</tr>
</tbody></table>
<blockquote>
<p>비제어 컴포넌트는 폼을 다룰 때 코드를 간결하게 유지하려는 경우나 리액트 외부와의 통합이 필요한 경우에 효과적이다. 하지만, 복잡한 상태 관리나 로직 구현이 필요한 경우에는 제어 컴포넌트를 사용하는 것이 더 적절할 수 있다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리액트] 이벤트 제어하기]]></title>
            <link>https://velog.io/@river-m/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%A0%9C%EC%96%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@river-m/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%A0%9C%EC%96%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 12 Aug 2023 10:04:58 GMT</pubDate>
            <description><![CDATA[<h2 id="리액트의-이벤트">리액트의 이벤트</h2>
<ul>
<li>React의 이벤트는 소문자 대신 캐멀케이스(camelCase)를 사용한다.</li>
</ul>
<p>이벤트 핸들러를 인라인으로 정의하려면 다음과 같이 익명 함수로 작성하면 된다.</p>
<pre><code class="language-jsx">&lt;button onClick={() =&gt; alert(&#39;You clicked me!&#39;)}&gt;</code></pre>
<h2 id="컴포넌트에서-이벤트-처리">컴포넌트에서 이벤트 처리</h2>
<h3 id="이벤트-핸들러-정의하기">이벤트 핸들러 정의하기</h3>
<p>리액트에서 이벤트를 처리하려면 이벤트 핸들러 함수를 작성하고, 해당 함수를 특정 이벤트 핸들러 속성(예: onClick, onSubmit)에 전달할 수 있다.</p>
<pre><code class="language-jsx">function handleInputChange(e) {
  console.log(e.target.value);
}
</code></pre>
<h3 id="이벤트-전달하기">이벤트 전달하기</h3>
<ul>
<li>JSX를 사용하여 이벤트에 전달되는 함수는 호출되는 것이 아니라 전달되어야 한다.</li>
</ul>
<table>
<thead>
<tr>
<th>함수 전달(올바른)</th>
<th>함수 호출(잘못됨)</th>
</tr>
</thead>
<tbody><tr>
<td><code>&lt;button onClick={handleClick}&gt;</code></td>
<td><code>&lt;button onClick={handleClick()}&gt;</code></td>
</tr>
</tbody></table>
<blockquote>
<p>함수 전달(올바른 방식)에서는 <strong><code>handleClick</code></strong> 함수가 이벤트 핸들러로 <strong><code>onClick</code></strong>에 전달된다. 이는 React에게 해당 함수를 기억하고, 사용자가 버튼을 클릭했을 때만 호출하도록 지시한다. <a href="https://react.dev/learn/responding-to-events">링크</a></p>
</blockquote>
<p>다른예시:</p>
<ul>
<li>이벤트를 원하는 요소에 연결.</li>
</ul>
<pre><code class="language-jsx">&lt;input type=&quot;text&quot; onChange={handleInputChange} /&gt;
</code></pre>
<p>이제 사용자가 input에 입력을 하면, <code>handleInputChange</code> 함수가 호출된다.</p>
<h3 id="기본-동작-방지"><strong>기본 동작 방지</strong></h3>
<p>일부 브라우저 이벤트에는 기본 동작들이 있다. 예를 들어 <code>&lt;form&gt;</code>제출 이벤트는 버튼을 클릭할 때 발생하며 기본 동작으로 전체 페이지를 다시 로드한다.</p>
<p><code>e.preventDefault()</code>이벤트 개체를 호출하여 브라우저 기본동작을  발생하지 않도록 할 수 있다.</p>
<pre><code class="language-jsx">function Signup() {

    function handleSubmit(e){
   e.preventDefault();
      alert(&#39;Submitting!&#39;);
}

  return (
    &lt;form onSubmit={handleSubmit}&gt;
      &lt;input /&gt;
      &lt;button&gt;Send&lt;/button&gt;
    &lt;/form&gt;
  );
}</code></pre>
<h3 id="이벤트-전파"><strong>이벤트 전파</strong></h3>
<p>이벤트 전파는 자바스크립트 포스트에 다뤘기 때문에 상세하게 다루지 않을꺼다. <a href="https://velog.io/@river-m/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%8B%A4%EB%A3%A8%EA%B8%B0">링크</a> </p>
<p><a href="https://react.dev/learn/responding-to-events#event-propagation">리액트 공식문서</a>를 보면 이벤트가 부모 요소에 전파하는 것을 방지하려면, 전파 중지를 <strong><code>e.stopPropagation();</code></strong> 을 해당 이벤트 핸들러에 작성하면 된다.</p>
<blockquote>
<p>리액트에서 이벤트 핸들링은 웹 앱의 사용자 인터랙션을 다루는 중요한 부분이다. 이 포스트에서는 리액트에서 이벤트 핸들러를 정의하고 사용하는 방법, 이벤트 객체의 사용, 기본 동작 방지 및 이벤트 전파와 같은 주제를 다뤘다. 이러한 기초를 통해 사용자와의 상호작용을 원활하게 만들 수 있으며, 더욱 풍부하고 반응적인 경험을 제공할 수 있다.</p>
</blockquote>
<p><strong><a href="https://react.dev/learn/responding-to-events">리액트 공식 문서</a></strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[리액트] 리스트와 키]]></title>
            <link>https://velog.io/@river-m/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%A6%AC%EC%8A%A4%ED%8A%B8%EC%99%80-%ED%82%A4</link>
            <guid>https://velog.io/@river-m/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%A6%AC%EC%8A%A4%ED%8A%B8%EC%99%80-%ED%82%A4</guid>
            <pubDate>Sat, 12 Aug 2023 07:38:40 GMT</pubDate>
            <description><![CDATA[<p>리액트에서 데이터의 배열을 컴포넌트 리스트로 변환하는 작업은 매우 일반적이다. 이 과정에서 &quot;key&quot;는 중요한 역할을 한다. </p>
<h2 id="컴포넌트-반복하기">컴포넌트 반복하기</h2>
<p>리액트에서 배열의 데이터를 컴포넌트로 변환하는 작업은 자주 발생하는 일이다. 이렇게 여러 개의 컴포넌트를 반복적으로 렌더링하려면 배열의 <strong><code>map</code></strong> 메서드를 활용할 수 있다.</p>
<h3 id="map--메서드-사용하기"><strong><code>map</code>  메서드 사용하기</strong></h3>
<p>자바스크립트의 배열 메서드인 <strong><code>map</code></strong>은 배열의 각 요소에 대해 함수를 실행하고, 그 결과로 새로운 배열을 생성한다. 리액트에서는 이를 활용해 배열 데이터를 컴포넌트로 변환할 수 있다.</p>
<p><strong>예시:</strong></p>
<pre><code class="language-jsx">const fruits = [&#39;Apple&#39;, &#39;Banana&#39;, &#39;Cherry&#39;];
const fruitItems = fruits.map((fruit) =&gt; &lt;li&gt;{fruit}&lt;/li&gt;);

return (
  &lt;ul&gt;{fruitItems}&lt;/ul&gt;
);</code></pre>
<h3 id="filter-메서드-사용하기"><strong><code>filter</code> 메서드 사용하기</strong></h3>
<p>리액트에서 리스트를 다룰 때, <strong><code>map</code></strong>사용하는 것이 일반적이지만, 조건에 따라 특정 컴포넌트만 렌더링하고 싶다면 <strong><code>filter</code></strong> 메서드도 함께 사용할 수 있다.</p>
<ol>
<li><strong>렌더링할 리스트 데이터</strong></li>
</ol>
<pre><code class="language-jsx">const users = [
  { id: 1, name: &#39;Alice&#39;, isActive: true },
  { id: 2, name: &#39;Bob&#39;, isActive: false },
  { id: 3, name: &#39;Charlie&#39;, isActive: true },
];
</code></pre>
<ol>
<li><strong>Filter 메서드로 조건에 맞는 데이터 선택</strong></li>
</ol>
<p><strong><code>isActive</code></strong> 값이 <strong><code>true</code></strong>인 사용자만을 렌더링하고 싶다면, <strong><code>filter</code></strong> 메서드로 해당 조건의 데이터만 선택할 수 있다.</p>
<pre><code class="language-jsx">const activeUsers = users.filter(user =&gt; user.isActive);</code></pre>
<ol>
<li><strong>Map 메서드로 컴포넌트 생성하기</strong></li>
</ol>
<p><strong><code>map</code></strong> 메서드를 사용해 선택된 데이터로 컴포넌트를 생성한다.</p>
<pre><code class="language-jsx">const userList = activeUsers.map(user =&gt; &lt;User key={user.id} user={user} /&gt;);</code></pre>
<p><strong>4. 렌더링</strong></p>
<p>마지막으로 생성된 컴포넌트 리스트를 렌더링하면 된다.</p>
<pre><code class="language-jsx">return (
  &lt;div&gt;{userList}&lt;/div&gt;
);</code></pre>
<h2 id="컴포넌트로-분리하기"><strong>컴포넌트로 분리하기</strong></h2>
<p>코드의 재사용과 관리를 더 쉽게 하기 위해, 리스트 아이템을 별도의 컴포넌트로 분리할 수 있다.</p>
<p><strong>예시:</strong></p>
<pre><code class="language-jsx">function FruitItem({ fruit }) {
  return &lt;li&gt;{fruit}&lt;/li&gt;;
}

const fruits = [&#39;Apple&#39;, &#39;Banana&#39;, &#39;Cherry&#39;];
const fruitItems = fruits.map((fruit) =&gt; &lt;FruitItem fruit={fruit} /&gt;);

return (
  &lt;ul&gt;{fruitItems}&lt;/ul&gt;
);</code></pre>
<h2 id="key">Key</h2>
<p>리액트에서 리스트를 렌더링할 때 <strong><code>key</code></strong>는 매우 중요한 역할을 한다. <strong><code>key</code></strong>는 리액트가 어떤 항목을 변경, 추가 또는 삭제할지 식별하는 데 도움을 준다. key는 엘리먼트에 안정적인 고유성을 부여하기 위해 배열 내부의 엘리먼트에 지정해야 한다.</p>
<h3 id="key의-필요성"><strong><code>key</code>의 필요성</strong></h3>
<p>리액트는 렌더링 성능을 최적화하기 위해 가상 DOM을 사용한다. 배열을 렌더링할 때 각 요소에 고유한 <strong><code>key</code></strong>를 할당하면 리액트는 변화가 있는 요소만 효과적으로 식별하고 업데이트할 수 있다.</p>
<blockquote>
<p><strong><code>key</code></strong>가 없다면 배열의 모든 요소를 다시 렌더링해야 하므로 성능이 저하될 수 있다.</p>
</blockquote>
<h3 id="key-사용하기"><strong><code>key</code> 사용하기</strong></h3>
<p><strong><code>key</code></strong>는 각 리스트 항목의 고유한 문자열로 할당되어야 한다. 가능한 한 항목의 고유한 값을 사용하는 것이 좋다.</p>
<p><strong>예시:</strong></p>
<pre><code class="language-jsx">const fruits = [
  { id: 1, name: &#39;Apple&#39; },
  { id: 2, name: &#39;Banana&#39; },
  { id: 3, name: &#39;Cherry&#39; }
];

const fruitItems = fruits.map((fruit) =&gt; &lt;li key={fruit.id}&gt;{fruit.name}&lt;/li&gt;);</code></pre>
<h3 id="인덱스를-key로-사용하는-문제점"><strong>인덱스를 <code>key</code>로 사용하는 문제점</strong></h3>
<p>배열의 인덱스를 <strong><code>key</code></strong>로 사용하는 것은 쉽고 간편할 수 있지만, 배열의 순서가 변경될 가능성이 있다면 문제가 발생할 수 있다. 이는 리액트의 성능 최적화에 방해가 될 수 있으므로, 가능한 한 고유한 값을 사용해야 한다.</p>
<h3 id="고유한-값을-사용하는-방법">고유한 값을 사용하는 방법</h3>
<ol>
<li><strong>데이터의 고유한 값을 사용하기</strong></li>
</ol>
<p>가장 간단하고 안전한 방법은 이미 고유한 값을 갖고 있는 데이터의 특정 필드를 키로 사용하는 것이다. 예를 들어, 데이터베이스에서 가져온 객체가 <strong><code>id</code></strong> 필드를 갖고 있다면 이를 키로 사용할 수 있다.</p>
<pre><code class="language-jsx">const listItems = items.map(item =&gt; &lt;li key={item.id}&gt;{item.name}&lt;/li&gt;);</code></pre>
<ol>
<li><strong>라이브러리 사용하기</strong></li>
</ol>
<p>JavaScript에는 고유한 키를 생성할 수 있는 라이브러리들이 있다. 예를 들어, <strong><code>uuid</code></strong> 라이브러리를 사용하면 매우 고유한 문자열을 생성할 수 있다.</p>
<pre><code class="language-jsx">import { v4 as uuidv4 } from &#39;uuid&#39;;

const myKey = uuidv4(); // &#39;6c84fb90-12c4-11e1-840d-7b25c5ee775a&#39;</code></pre>
<ol>
<li><strong>키 생성 함수 만들기</strong></li>
</ol>
<p>필요에 따라 고유한 키를 생성하는 함수를 직접 만들 수도 있다. 예를 들어, 타임스탬프와 랜덤 값을 조합하는 방법이 있다.</p>
<pre><code class="language-jsx">function createUniqueKey() {
  return `${new Date().getTime()}-${Math.random()}`;
}</code></pre>
<h3 id="key와-컴포넌트"><strong><code>key</code>와 컴포넌트</strong></h3>
<p>리스트를 렌더링하는 컴포넌트 안에서 <strong><code>key</code></strong>를 정의해야 한다. <strong><code>key</code></strong>는 특별한 속성으로, 컴포넌트 내부에서 <strong><code>props.key</code></strong>로 접근할 수 없다.</p>
<blockquote>
<p>리액트에서 <strong><code>key</code></strong>는 컴포넌트의 배열 렌더링을 최적화하는 데 중요하다. 고유한 값을 사용하고, 적절한 위치에서 정의함으로써 리액트의 렌더링 성능을 향상시킬 수 있다.</p>
</blockquote>
]]></description>
        </item>
    </channel>
</rss>