<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>growing-hyo.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Tue, 16 Dec 2025 02:56:05 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>growing-hyo.log</title>
            <url>https://velog.velcdn.com/images/growing-hyo/profile/24a9a29a-3038-4998-9b93-deaa72dddf46/image.PNG</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. growing-hyo.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/growing-hyo" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[모든 Side Effect가 useEffect를 필요로 하진 않는다]]></title>
            <link>https://velog.io/@growing-hyo/%EB%AA%A8%EB%93%A0-Side-Effect%EA%B0%80-useEffect%EB%A5%BC-%ED%95%84%EC%9A%94%EB%A1%9C-%ED%95%98%EC%A7%84-%EC%95%8A%EB%8A%94%EB%8B%A4</link>
            <guid>https://velog.io/@growing-hyo/%EB%AA%A8%EB%93%A0-Side-Effect%EA%B0%80-useEffect%EB%A5%BC-%ED%95%84%EC%9A%94%EB%A1%9C-%ED%95%98%EC%A7%84-%EC%95%8A%EB%8A%94%EB%8B%A4</guid>
            <pubDate>Tue, 16 Dec 2025 02:56:05 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>React Udemy 강의 179번 정리</p>
</blockquote>
<p>Side effect면 무조건 useEffect를 써야 한다고 생각했다면 틀렸다.
useEffect는 남용하면 안 된다.
이번 강의의 핵심은 이거다.</p>
<h2 id="1-side-effect는-맞지만-useeffect가-필요-없는-경우">1. Side Effect는 맞지만, useEffect가 필요 없는 경우</h2>
<h3 id="예시-상황">예시 상황</h3>
<ul>
<li>사용자가 place를 클릭함</li>
<li>선택한 장소를 state에 추가 → UI 업데이트</li>
<li>동시에 localStorage에 선택한 장소 목록 저장 (새로고침해도 유지되게 하기 위함)</li>
</ul>
<pre><code class="language-js">function handleSelectPlace(id) {
  setSelectedPlaces((prev) =&gt; [...prev, id]);

  const storedIds =
    JSON.parse(localStorage.getItem(&#39;selectedPlaces&#39;)) || [];

  if (storedIds.indexOf(id) === -1) {
    localStorage.setItem(
      &#39;selectedPlaces&#39;,
      JSON.stringify([id, ...storedIds])
    );
  }
}</code></pre>
<p>이 코드는 side effect가 맞음.
하지만 useEffect는 필요 없음.</p>
<h2 id="2-왜-side-effect인데도-useeffect를-안-써도-될까">2. 왜 side effect인데도 useEffect를 안 써도 될까?</h2>
<p>이 코드는 렌더링 때문에 실행되는 코드가 아니기 때문임.
이 코드는 컴포넌트 렌더링 중에 실행되지 않음.
오직 사용자 클릭 이벤트가 발생했을 때만 실행됨.</p>
<p>즉, 사용자 행동이 있어야 코드가 실행되기 때문에 state를 업데이트해도 무한 루프가 생기지 않음.</p>
<h2 id="3-useeffect가-필요한-경우-vs-필요-없는-경우">3. useEffect가 필요한 경우 vs 필요 없는 경우</h2>
<h3 id="useeffect가-꼭-필요한-경우">useEffect가 꼭 필요한 경우</h3>
<ul>
<li>컴포넌트가 렌더링될 때 자동으로 실행되어야 하는 코드</li>
<li>렌더링 중 state 변경 → 무한 루프 위험 있는 코드</li>
</ul>
<p>예를 들면, 위치 정보 가져오는 코드(<code>navigator.geolocation</code>), 서버 데이터 fetch, 렌더 이후 DOM에 접근해야 하는 코드 등등.</p>
<p>즉, 렌더링 → 자동 실행
이 구조일 때 useEffect 필요.</p>
<h3 id="useeffect가-필요-없는-경우">useEffect가 필요 없는 경우</h3>
<ul>
<li>사용자 이벤트로만 실행되는 코드</li>
<li>버튼 클릭, 리스트 클릭 등</li>
</ul>
<p>예를 들면, localStorage 저장, 로그 남기기 등등..</p>
<p>즉, 사용자 액션 → 실행
이 구조일 때는 useEffect가 굳이 필요 없음.</p>
<h2 id="4-중요한-규칙--hooks는-어디서나-쓸-수-없다">4. 중요한 규칙 : Hooks는 어디서나 쓸 수 없다</h2>
<pre><code class="language-js">function handleSelectPlace() {
  useEffect(() =&gt; {}); // 불가능
}</code></pre>
<ul>
<li>Hooks는 반드시 <strong>컴포넌트 최상단</strong>에서만 사용 가능</li>
<li>조건문, 반복문, 이벤트 핸들러 안에서는 사용 불가!!</li>
</ul>
<p>아무튼 이번 예제에서는 애초에 useEffect를 쓸 필요가 없음. 그래서 규칙 위반 문제도 발생하지 않음.</p>
<hr>
<p>useEffect는 side effect를 관리하기 위한 도구이지
side effect를 무조건 감싸는 도구가 아니다.</p>
<p>useEffect는 렌더링 이후 자동으로 실행되어야 하는 로직에만 사용해야 하며, 사용자 이벤트로만 실행되는 side effect는 이벤트 핸들러 안에서 처리하는 것이 더 바람직하다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[useEffect로 Side Effect 다루기]]></title>
            <link>https://velog.io/@growing-hyo/useEffect%EB%A1%9C-Side-Effect-%EB%8B%A4%EB%A3%A8%EA%B8%B0</link>
            <guid>https://velog.io/@growing-hyo/useEffect%EB%A1%9C-Side-Effect-%EB%8B%A4%EB%A3%A8%EA%B8%B0</guid>
            <pubDate>Tue, 16 Dec 2025 01:42:11 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>React Udemy 강의 178번 정리</p>
</blockquote>
<p>이번 강의의 핵심은 한 가지,
&quot;<em>렌더링 중에 state를 바꾸면 왜 문제가 생기고, 그걸 useEffect로 어떻게 해결할까?</em>&quot;</p>
<h2 id="1-문제가-뭐였을까-무한-루프">1. 문제가 뭐였을까? (무한 루프)</h2>
<p>사용자 위치를 가져와서 그 위치를 기준으로 장소 목록을 정렬. 그리고 그 결과를 state에 저장.</p>
<h3 id="문제-코드의-핵심-구조">문제 코드의 핵심 구조</h3>
<pre><code class="language-js">// 컴포넌트 함수 안
setAvailablePlaces(sortedPlaces);</code></pre>
<p>이 코드가 <strong>컴포넌트 실행 중에 바로 실행</strong>됨.</p>
<h3 id="왜-문제일까">왜 문제일까?</h3>
<ol>
<li>컴포넌트 실행함</li>
<li>state 업데이트</li>
<li>state가 바뀌어서 컴포넌트 재실행</li>
<li>다시 state 업데이트</li>
<li>무한 반복..</li>
</ol>
<p>즉, 렌더링 중에 side effect(state 변경)를 실행함.</p>
<h2 id="2-side-effect란">2. Side Effect란?</h2>
<p>렌더링과 직접적인 관련이 없는 작업을 말한다.</p>
<p>예를 들면, 데이터 fetch, 브라우저 API 사용(위치, localStorage 등), 타이머, interval 등등.</p>
<p>이런 작업은 <strong>렌더링 도중에 하면 안 됨</strong>.</p>
<h2 id="3-useeffect는-언제-실행될까">3. useEffect는 언제 실행될까?</h2>
<pre><code class="language-js">useEffect(() =&gt; {
  // side effect 코드
}, []);</code></pre>
<p>useEffect 안의 코드는 컴포넌트가 렌더링된 <strong>이후에</strong> 실행됨.</p>
<p>즉, 렌더링 완료 후.
그래서 렌더링 중에 state를 바꾸는 문제가 사라짐.</p>
<h2 id="4-왜-의존성-배열이-중요할까">4. 왜 의존성 배열(<code>[]</code>)이 중요할까?</h2>
<pre><code class="language-js">useEffect(() =&gt; {
  // 위치 가져오고 state 업데이트
}, []);</code></pre>
<h3 id="의존성-배열의-의미">의존성 배열의 의미</h3>
<table>
  <thead>
    <tr>
      <th>의존성 배열</th>
      <th>실행 시점</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>생략</td>
      <td>모든 렌더링 후마다 실행</td>
    </tr>
    <tr>
      <td>[ ]</td>
      <td>최초 렌더링 후 1번만 실행</td>
    </tr>
    <tr>
      <td>[value]</td>
      <td>value가 바뀔 때만 실행</td>
    </tr>
  </tbody>
</table>

<p>이번 예제에서는 사용자 위치를 앱 시작 시 한 번만 필요로 하기 때문에 <code>[]</code> 사용함.
결과적으로 무한 루프에서 벗어나 최초 1번만 실행함.</p>
<h2 id="5-왜-빈-배열이면-한-번만-실행될까">5. 왜 빈 배열이면 한 번만 실행될까?</h2>
<p>React는 의존성 배열을 보고 판단함.
배열 안에 아무 값도 없으면 비교할 값이 없음.
즉, 바뀐 게 없음.
그렇다면, 다시 실행할 이유가 없음.</p>
<p>그래서 첫 렌더링 이후 딱 한 번만 실행하는 것.</p>
<h2 id="6-fallback-ui의-역할">6. fallback UI의 역할</h2>
<pre><code class="language-jsx">&lt;Places fallbackText=&quot;Sorting places by distance...&quot; /&gt;</code></pre>
<p>위치 정보 요청은 시간이 걸릴 수 있기 때문에 그동안 보여줄 UI가 필요함.</p>
<p>useEffect + fallback UI는 자연스러운 비동기 흐름.</p>
<hr>
<p>useEffect는 렌더링과 무관한 작업(side effect)을 렌더링 이후 안전한 시점에 실행하도록 도와주는 훅이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[useReducer 훅]]></title>
            <link>https://velog.io/@growing-hyo/useReducer-%ED%9B%85</link>
            <guid>https://velog.io/@growing-hyo/useReducer-%ED%9B%85</guid>
            <pubDate>Mon, 15 Dec 2025 03:08:32 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>Udemy React 강의 173번 정리</p>
</blockquote>
<h2 id="왜-usereducer가-필요할까">왜 useReducer가 필요할까?</h2>
<h3 id="현재-상황-context--usestate">현재 상황 (Context + useState)</h3>
<ul>
<li>CartContextProvider 컴포넌트 안에서 장바구니 상태를 관리 중임</li>
<li>상태는 객체 / 배열 형태</li>
<li>상태 업데이트 로직이 점점 복잡해짐</li>
</ul>
<h3 id="문제점">문제점</h3>
<ul>
<li><strong>항상 함수형 업데이트</strong>를 써야 함</li>
<li>로직이 길어질수록 가독성이 떨어짐</li>
<li>상태 변경 규칙이 컴포넌트 안에 흩어져 있음</li>
</ul>
<p>이런 경우에 등장하는 게 <code>useReducer</code></p>
<h2 id="reducer란-무엇인가">reducer란 무엇인가?</h2>
<h3 id="reduce의-기본-개념-react-이전부터-있던-개념">reduce의 기본 개념 (React 이전부터 있던 개념)</h3>
<p>여러 값을 하나의 값으로 줄이는 함수임.</p>
<p>대표적인 예가 JavaScript의 reduce.</p>
<pre><code class="language-js">const total = items.reduce((sum, item) =&gt; {
  return sum + item.price * item.quantity;
}, 0);</code></pre>
<p>이전값 + 규칙 → 새값
이 패턴을 상태 관리에 적용한 게 useReducer임.</p>
<h2 id="usereducer의-핵심">useReducer의 핵심</h2>
<p>상태 변경 로직을 한 곳(reducer)에 모으자!</p>
<h3 id="usereducer는-두-가지를-반환함">useReducer는 두 가지를 반환함</h3>
<pre><code class="language-jsx">const [state, dispatch] = useReducer(reducer, initialState);</code></pre>
<ul>
<li>state : 현재 상태</li>
<li>dispatch : action(행동)을 보내는 함수</li>
</ul>
<p>useState의 setState와 다름!
직접 값을 넣는 게 아니라 action을 전달</p>
<h2 id="reducer-함수의-구조">reducer 함수의 구조</h2>
<p>reducer는 <strong>반드시 이 형태</strong>를 가짐 :</p>
<pre><code class="language-jsx">function shoppingCartReducer(state, action) {
  return newState;
}</code></pre>
<h3 id="매개변수의-의미">매개변수의 의미</h3>
<table>
  <thead>
    <tr>
      <td>매개변수</td>
      <td>설명</td>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>state</td>
      <td>항상 최신 상태 스냅샷</td>
    </tr>
    <tr>
      <td>action</td>
      <td>dispatch로 전달한 정보</td>
    </tr>
  </tbody>
</table>

<p>React가 <strong>최신 state를 보장</strong>하기 때문에 <strong>이전 상태 기반 업데이트를 자동으로 처리</strong>함. 그래서 <code>setState(prev =&gt; ...)</code> 이런 패턴이 필요 없는 거임.</p>
<h2 id="reducer는-왜-컴포넌트-밖에다가-두는-걸까">reducer는 왜 컴포넌트 밖에다가 두는 걸까?</h2>
<pre><code class="language-jsx">function shoppingCartReducer(state, action) {
  return state;
}</code></pre>
<h3 id="이유">이유</h3>
<ul>
<li>props / state에 직접 접근할 필요 없음</li>
<li>컴포넌트 리렌더링 시 재생성될 필요 없음</li>
<li>순수 함수 유지</li>
</ul>
<p>즉, <strong>상태 변경 규칙만 정의하는 함수</strong>이기 때문임.</p>
<p>지금 단계에서 이 강의가 말하고자 하는 핵심은 useReducer는 복잡한 상태 변경 로직을 구조적으로 관리하기 위한 도구라는 것.</p>
<ul>
<li>Context + 복잡한 state → useReducer 궁합 좋음</li>
<li>무슨 일이 일어났는지(action) 중심 사고</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[Context Outsourcing(아웃소싱) & Provider 컴포넌트 분리 정리]]></title>
            <link>https://velog.io/@growing-hyo/Context-Outsourcing%EC%95%84%EC%9B%83%EC%86%8C%EC%8B%B1-Provider-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EB%B6%84%EB%A6%AC-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@growing-hyo/Context-Outsourcing%EC%95%84%EC%9B%83%EC%86%8C%EC%8B%B1-Provider-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EB%B6%84%EB%A6%AC-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Mon, 15 Dec 2025 02:22:30 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>Udemy React 강의 172번 정리</p>
</blockquote>
<h2 id="문제-상황--app-컴포넌트가-너무-비대해짐">문제 상황 : App 컴포넌트가 너무 비대해짐</h2>
<p>지금까지 App 컴포넌트에서 :</p>
<ul>
<li>장바구니 state 관리 (useState)</li>
<li>아이템 추가 / 수량 변경 함수들</li>
<li>Context에 전달할 value 객체 생성</li>
<li><code>&lt;CartContext.Provider value={...}&gt;</code> 설정</li>
</ul>
<p>즉, Context + State + 로직이 전부 App에 몰려 있음.
이렇게 되면 App 컴포넌트가 점점 거대해지고, Context가 여러 개 생기면 App이 더 복잡해진다.
즉, 루트 컴포넌트는 깔끔해야 한다는 원칙에 어긋남.</p>
<h2 id="해결--context-전용-provider-컴포넌트-만들기">해결 : Context 전용 Provider 컴포넌트 만들기</h2>
<p><strong>Context + State + 관련 로직을 App이 아니라 Context Provider 컴포넌트로 옮기는 것</strong>이다.</p>
<p>즉, Context를 사용만 하는 쪽(App)과 Context를 관리하는 쪽(Provider 컴포넌트)를 분리하는 것.</p>
<h2 id="shopping-cart-context의-역할"><code>shopping-cart-context</code>의 역할</h2>
<p>이제 Context 파일은 단순히 createContext만 하는 것이 아니라 (1) 장바구니 state, (2) 아이템 추가 / 수정 로직, (3) Context에 전달할 value 구성, (4) Provider 컴포넌트 렌더링 이 모든 걸 담당한다.</p>
<pre><code class="language-jsx">// src/store/shopping-cart-context.jsx

export default function CartContextProvider({ children }) {
  const [shoppingCart, setShoppingCart] = useState({
    items: [],
  });

  function handleAddItemToCart(id) {
    setShoppingCart(prevShoppingCart =&gt; {
      const updatedItems = [...prevShoppingCart.items];

      const existingCartItemIndex = updatedItems.findIndex(
        cartItem =&gt; cartItem.id === id
      );
      const existingCartItem = updatedItems[existingCartItemIndex];

      if (existingCartItem) {
        const updatedItem = {
          ...existingCartItem,
          quantity: existingCartItem.quantity + 1,
        };
        updatedItems[existingCartItemIndex] = updatedItem;
      } else {
        const product = DUMMY_PRODUCTS.find(product =&gt; product.id === id);
        updatedItems.push({
          id: id,
          name: product.title,
          price: product.price,
          quantity: 1,
        });
      }

      return {
        items: updatedItems,
      };
    });
  }

  function handleUpdateCartItemQuantity(productId, amount) {
    setShoppingCart(prevShoppingCart =&gt; {
      const updatedItems = [...prevShoppingCart.items];
      const updatedItemIndex = updatedItems.findIndex(
        item =&gt; item.id === productId
      );

      const updatedItem = {
        ...updatedItems[updatedItemIndex],
      };

      updatedItem.quantity += amount;

      if (updatedItem.quantity &lt;= 0) {
        updatedItems.splice(updatedItemIndex, 1);
      } else {
        updatedItems[updatedItemIndex] = updatedItem;
      }

      return {
        items: updatedItems,
      };
    });
  }

  const ctxValue = {
    items: shoppingCart.items,
    addItemToCart: handleAddItemToCart,
    updateItemQuantity: handleUpdateCartItemQuantity,
  };

  return (
    &lt;CartContext.Provider value={ctxValue}&gt;{children}&lt;/CartContext.Provider&gt;
  );
}</code></pre>
<p>이 컴포넌트는 데이터와 로직을 감싸서 제공하는 역할을 하는 거지, UI를 만들기 위한 게 아님. 그래서 children을 받아서 그대로 감싸줌.</p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[전체 데모 앱에 Context API 적용하기]]></title>
            <link>https://velog.io/@growing-hyo/%EC%A0%84%EC%B2%B4-%EB%8D%B0%EB%AA%A8-%EC%95%B1%EC%97%90-Context-API-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@growing-hyo/%EC%A0%84%EC%B2%B4-%EB%8D%B0%EB%AA%A8-%EC%95%B1%EC%97%90-Context-API-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 15 Dec 2025 01:51:49 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>Udemy React 강의 171번 정리</p>
</blockquote>
<p>이번 강의의 목표는 <strong>Cart 관련 state + 로직을 전부 Context로 옮겨서 prop drilling을 완전히 제거</strong>하는 것이다.</p>
<h2 id="1-시작-상태--prop-drilling이-남아-있던-구조">1. 시작 상태 : prop drilling이 남아 있던 구조</h2>
<p>이전까지 구조는 대략 이러했다.</p>
<pre><code class="language-text">App
 ├─ Header (cart items 개수 필요)
 ├─ Product (add to cart 함수 필요)
 └─ CartModal
     └─ Cart (items + update 함수 필요)</code></pre>
<p>그래서 App에서 onAddToCart, cartItems, onUpdateCartItemQuantity 이런 props들이 계속 내려갔다.</p>
<p>문제는 중간 컴포넌트들은 이 props를 사용하지도 않는데 단지 전달만 하기 위해 존재하게 되는 것이었다. 전형적인 prop drilling.</p>
<h2 id="2-app-컴포넌트--context-provider-정리">2. App 컴포넌트 : Context Provider 정리</h2>
<h3 id="context에서-제공하는-값들">Context에서 제공하는 값들</h3>
<pre><code class="language-jsx">  const ctxValue = {
    items: shoppingCart.items,
    addItemToCart: handleAddItemToCart,
    updateItemQuantity: handleUpdateCartItemQuantity,
  };</code></pre>
<pre><code class="language-jsx">&lt;CartContext.Provider value={ctxValue}&gt;
  {children}
&lt;/CartContext.Provider&gt;</code></pre>
<p>이제 Context는 단순한 값 공유를 넘어서 cart items를 읽고, cart 업데이트 함수를 호출하는 것까지 모두 가능해짐.</p>
<h2 id="4-product-컴포넌트--props-제거-→-context-사용">4. Product 컴포넌트 : props 제거 → Context 사용</h2>
<h3 id="before">Before</h3>
<pre><code class="language-jsx">&lt;Product onAddToCart={handleAddItemToCart} /&gt;</code></pre>
<p>After</p>
<pre><code class="language-jsx">const { addItemToCart } = useContext(CartContext);

&lt;button onClick={() =&gt; addItemToCart(id)}&gt;Add&lt;/button&gt;</code></pre>
<ul>
<li>Product는 이제 <strong>props를 하나도 받지 않음</strong></li>
<li>필요한 건 Context에서 직접 가져옴</li>
</ul>
<h2 id="5-header-컴포넌트--cart-개수도-context로">5. Header 컴포넌트 : cart 개수도 Context로</h2>
<p>Header는 장바구니 개수만 필요함.</p>
<pre><code class="language-jsx">const { items } = useContext(CartContext);
const totalItems = items.length;</code></pre>
<ul>
<li>이제 App에서 Header로 cart 관련 props를 넘길 필요 없음</li>
</ul>
<h2 id="6-cartmodal--cart-컴포넌트-정리">6. CartModal &amp; Cart 컴포넌트 정리</h2>
<ul>
<li>CartModal은 단순히 Cart를 감싸는 wrapper이기 때문에 Context 필요 없음</li>
<li>Cart 컴포넌트는 items와 update 함수가 필요하기 때문에 Context 사용</li>
</ul>
<pre><code class="language-jsx">const { items, updateItemQuantity } = useContext(CartContext);</code></pre>
<h2 id="7-최종-결과-구조">7. 최종 결과 구조</h2>
<pre><code class="language-text">App
 ├─ Header (Context 사용)
 ├─ Product (Context 사용)
 └─ CartModal
     └─ Cart (Context 사용)</code></pre>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[Context 값이 바뀌면 컴포넌트는 어떻게 될까?]]></title>
            <link>https://velog.io/@growing-hyo/Context-%EA%B0%92%EC%9D%B4-%EB%B0%94%EB%80%8C%EB%A9%B4-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%90%A0%EA%B9%8C</link>
            <guid>https://velog.io/@growing-hyo/Context-%EA%B0%92%EC%9D%B4-%EB%B0%94%EB%80%8C%EB%A9%B4-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%90%A0%EA%B9%8C</guid>
            <pubDate>Mon, 15 Dec 2025 01:20:38 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>Udemy React 강의 170번 정리</p>
</blockquote>
<p>이번 강의에서는 Context 값을 사용하는 컴포넌트가 언제, 왜 다시 실행(리렌더링)되는지를 다뤘다.</p>
<p>핵심은 생각보다 단순하다. <strong>useContext로 Context 값을 사용하는 컴포넌트는 그 Context 값이 변경되면 자동으로 다시 실행된다.</strong></p>
<h2 id="react-컴포넌트는-언제-다시-실행될까">React 컴포넌트는 언제 다시 실행될까?</h2>
<p>컴포넌트 함수는 다음 세 가지 경우에 다시 실행된다.</p>
<ol>
<li>자기 자신의 state가 변경될 때</li>
<li>부모 컴포넌트가 다시 실행될 때</li>
<li>사용 중인 Context 값이 변경될 때 (이번 강의의 핵심)</li>
</ol>
<h2 id="context를-사용한다는-게-무슨-뜻일까">Context를 &quot;사용한다&quot;는 게 무슨 뜻일까?</h2>
<pre><code class="language-jsx">function Cart() {
  const cartCtx = useContext(CartContext);

  return &lt;p&gt;{cartCtx.items.length}&lt;/p&gt;;
}</code></pre>
<p>이 코드를 작성하는 순간, <strong>Cart 컴포넌트는 CartContext의 값(items)에 의존</strong>하게 된다.</p>
<p>즉, <strong>이 컴포넌트는 CartContext 값이 바뀌면 다시 실행되어야 한다</strong>고 React는 이해한다.</p>
<h2 id="context-값이-바뀌면-실제로-무슨-일이-일어날까">Context 값이 바뀌면 실제로 무슨 일이 일어날까?</h2>
<p>예를 들어 App 컴포넌트에서</p>
<pre><code class="language-jsx">setShoppingCart(prev =&gt; ({
  items: [...prev.items, newItem],
}));</code></pre>
<p>이 코드가 실행되면 다음 순서로 일이 벌어진다.</p>
<ol>
<li>shoppingCart state 변경</li>
<li>CartContext.Provider의 value 변경</li>
<li>Context 값이 변경됨</li>
<li>이 Context를 사용하는 모든 컴포넌트가 다시 실행됨</li>
</ol>
<p>이렇게 보면 state와 Context는 리렌더링 관점에서 비슷하게 느껴지는데..
강의에서 강조하는 중요한 포인트는 이거다.</p>
<ul>
<li>state가 변경되면 컴포넌트가 다시 실행되듯이 <strong>Context 값이 변경되어도 컴포넌트는 다시 실행</strong>된다.</li>
</ul>
<p>하지만 Context 값이 바뀌면 앱 전체가 다 리렌더링되는 건 아니다.
<strong>해당 Context를 실제로 사용하는 컴포넌트만 다시 실행</strong>된다.
Context를 사용하지 않는 컴포넌트는 영향을 받지 않는다.</p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[Context와 State 연결하기]]></title>
            <link>https://velog.io/@growing-hyo/Context%EC%99%80-State-%EC%97%B0%EA%B2%B0%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@growing-hyo/Context%EC%99%80-State-%EC%97%B0%EA%B2%B0%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 14 Dec 2025 13:05:53 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>Udemy React 강의 168번 정리</p>
</blockquote>
<p>이전까지 Context는 항상 빈 배열을 가진 정적인 값이었음.</p>
<pre><code class="language-jsx">&lt;CartContext.Provider value={{ items: [] }}&gt;</code></pre>
<p>이제 목표는 : Context를 실제 state와 연결해서 값을 읽는 것뿐 아니라 <strong>값을 변경하는 것까지 Context로 처리</strong>하기.</p>
<h2 id="1-context를-state에-연결하는-가장-기본적인-방법">1. Context를 State에 연결하는 가장 기본적인 방법</h2>
<h3 id="기존-상태">기존 상태</h3>
<pre><code class="language-jsx">const [shoppingCart, setShoppingCart] = useState({
  items: [],
});</code></pre>
<p>이 state의 구조는 이미 Context value와 동일함.</p>
<pre><code class="language-jsx">{
  items: []
}</code></pre>
<p>그래서 가장 단순한 연결 방법은 이거 :</p>
<pre><code class="language-jsx">&lt;CartContext.Provider value={shoppingCart}&gt;</code></pre>
<h3 id="결과">결과</h3>
<ul>
<li>Cart 컴포넌트에서 cartCtx.items 읽기 가능</li>
<li>state가 변경되면 Context도 자동으로 갱신됨</li>
</ul>
<p>즉, Context = state의 읽기 창구 역할</p>
<p>하지만 지금 상태에서는 Context로 값 읽기는 가능하지만 Context로 값 수정은 불가능함. 여전히 addItemToCart 같은 함수는 props로 전달 중..</p>
<p>즉, Context를 쓰고 있지만 prop drilling이 완전히 사라진 건 아님.</p>
<h2 id="2-해결책--context에-함수도-함께-넣기">2. 해결책 : Context에 함수도 함께 넣기</h2>
<p>Context에는 데이터뿐 아니라 <strong>함수</strong>도 담을 수 있다.</p>
<h3 id="context-value를-직접-구성하기">Context value를 직접 구성하기</h3>
<pre><code class="language-jsx">const ctxValue = {
  items: shoppingCart.items,
  addItemToCart: handleAddItemToCart,
};</code></pre>
<p>그리고 Provider에 전달 :</p>
<pre><code class="language-jsx">&lt;CartContext.Provider value={ctxValue}&gt;</code></pre>
<h3 id="핵심-개념">핵심 개념</h3>
<ul>
<li>items → 상태 값</li>
<li>addItemToCart → 상태를 변경하는 함수</li>
<li>Context는 이제 읽기 및 쓰기 모두 가능해짐</li>
</ul>
<p>Context 기본값에 함수를 넣는 이유는 딱 하나, 자동 완성 때문.</p>
<h2 id="3-context를-소비해서-state를-변경하기-product-컴포넌트">3. Context를 소비해서 state를 변경하기 (Product 컴포넌트)</h2>
<p>이제 Product 컴포넌트에서는 더 이상 props가 필요 없다.</p>
<h3 id="이전-방식">이전 방식</h3>
<pre><code class="language-jsx">&lt;Product onAddToCart={handleAddItemToCart /&gt;</code></pre>
<h3 id="context-사용-방식">Context 사용 방식</h3>
<pre><code class="language-jsx">import { useContext } from &#39;react&#39;;
import { CartContext } from &#39;../store/shopping-cart-context.jsx&#39;;

export default function Product({ id }) {
  const { addItemToCart } = useContext(CartContext);

  return (
    &lt;button onClick={() =&gt; addItemToCart(id)}&gt;Add to Cart &lt;/button&gt;
    );
}</code></pre>
<h3 id="결과-1">결과</h3>
<ul>
<li>Product → Context → App state</li>
<li>중간 컴포넌트에 props 전달하지 않아도 됨</li>
<li>prop drilling 제거</li>
</ul>
<p>이제 구조는 이렇게 바뀜.</p>
<pre><code class="language-code">Product
  ↓ (Context)
CartContext
  ↓
App (state + 로직)</code></pre>
<ul>
<li>상태는 App에 있음</li>
<li>접근은 어디서든 Context로</li>
<li>props는 UI 구성용으로만 사용</li>
</ul>
<p>Context는 state 자체가 아님!
Context는 state와 state 변경 함수에 접근하는 통로 역할임.
Context에 함수를 넣으면 값을 읽는 것뿐 아니라 그 값을 변경하는 것도 가능함.
그 결과, prop drilling이 제거되고, 컴포넌트 결합도 감소되며, 구조가 훨씬 깔끔해짐.</p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[Context 소비하기 (useContext / use 훅)]]></title>
            <link>https://velog.io/@growing-hyo/Context-%EC%86%8C%EB%B9%84%ED%95%98%EA%B8%B0-useContext-use-%ED%9B%85</link>
            <guid>https://velog.io/@growing-hyo/Context-%EC%86%8C%EB%B9%84%ED%95%98%EA%B8%B0-useContext-use-%ED%9B%85</guid>
            <pubDate>Sat, 13 Dec 2025 11:41:05 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>Udemy React 강의 167번 정리</p>
</blockquote>
<p>이번 강의에서는 Context를 제공한 뒤, 실제 컴포넌트에서 소비(consuming)하는 방법을 배웠다.</p>
<p>핵심은 <strong>props 대신 Context에서 값을 꺼내 쓰는 것</strong>이다.</p>
<h2 id="1-context를-왜-소비해야-할까">1. Context를 왜 소비해야 할까?</h2>
<p>현재 Cart 컴포넌트는 이런 역할을 한다.</p>
<ul>
<li>장바구니 아이템 목록 출력</li>
<li>아이템이 없으면 fallback 메시지 출력</li>
<li>총 가격 계산</li>
</ul>
<p>이전까지는 이 모든 데이터를 props로 전달받았다.
하지만 Context를 도입했으므로 이제는 props 없이 CartContext에서 직접 데이터를 가져와야 한다.</p>
<h2 id="2-context를-소비하는-방법---usecontext">2. Context를 소비하는 방법 - <code>useContext</code></h2>
<h3 id="기본-사용법">기본 사용법</h3>
<pre><code class="language-jsx">import { useContext } from &#39;react&#39;;
import { CartContext } from &#39;../store/shopping-cart-context.jsx&#39;;

export default function Cart() {
  const cartCtx = useContext(CartContext);

  return (
    &lt;&gt;
      {cartCtx.items.length === 0 &amp;&amp; &lt;p&gt;No items in cart!&lt;/p&gt;}
      {cartCtx.items.map(item =&gt; (
        &lt;li key={item.id}&gt;{item.name}&lt;/li&gt;
      ))}
    &lt;/&gt;
  );
}</code></pre>
<h3 id="핵심">핵심</h3>
<ul>
<li><code>useContext(Context객체)</code> 형태로 사용</li>
<li>반환값은 <code>Provider</code>에서 제공한 value</li>
<li>더 이상 props.items 같은 건 필요 없음</li>
</ul>
<h2 id="3-context를-소비하는-방법---use-훅-react-19-or-react-19">3. Context를 소비하는 방법 - use 훅 (React 19 or React 19+)</h2>
<pre><code class="language-jsx">import { use } from &#39;react&#39;;

const cartCtx = use(CartContext);</code></pre>
<h3 id="usecontext와-use의-차이">useContext와 use의 차이</h3>
<p>일반적인 React 훅(<code>useState</code>, <code>useContext</code> 등)은 if 문이나 반복문 안에서 사용할 수 없다.
하지만 <strong><code>use</code> 훅은 예외적으로 if 블록 안에서도 사용할 수 있어 조금 더 유연한 사용이 가능</strong>하다.
다만 <code>use</code> 훅은 React 19 혹은 그 이상에서만 지원되기 때문에 현재 기준으로는 <strong><code>useContext</code>가 더 안전하고 범용적인 방식으로 사용</strong>된다.</p>
<h2 id="4-provider에-value를-꼭-설정해야-하는-이유">4. Provider에 value를 꼭 설정해야 하는 이유</h2>
<p>Context를 소비하려면 <strong>반드시 Provider에서 value를 제공</strong>해야 한다.</p>
<pre><code class="language-jsx">&lt;CartContext.Provider value={{ items: [] }}&gt;
  &lt;App /&gt;
&lt;/CartContext.Provider&gt;</code></pre>
<h3 id="만약-value를-안-주면">만약 value를 안 주면?</h3>
<ul>
<li>에러 발생</li>
<li>Context를 읽을 수 없음</li>
<li>UI가 비어 보이거나 앱이 깨짐</li>
</ul>
<h2 id="context-기본값default-value은-왜-중요할까">Context 기본값(default value)은 왜 중요할까?</h2>
<p>Context 생성 시 이런 코드를 썼다.</p>
<pre><code class="language-jsx">const CartContext = createContext({
  items: [],
});</code></pre>
<h3 id="이-기본값의-역할은">이 기본값의 역할은</h3>
<ul>
<li>자동 완성(IntelliSense) 지원!</li>
<li>cartCtx.items 같은 접근이 IDE에서 바로 보임</li>
<li>실제 앱 실행 시 같은 Provider의 value가 덮어씀</li>
</ul>
<p>즉, 개발자 경험을 향상시키기 위한 설정임.</p>
<h2 id="6-구조-분해-할당으로-더-깔끔하게-쓰기">6. 구조 분해 할당으로 더 깔끔하게 쓰기</h2>
<pre><code class="language-jsx">export default Cart() {
  const { items } = useContext(CartContext);

  return (
    &lt;div&gt;
      {items.length === 0 &amp;&amp; &lt;p&gt;No items in cart!&lt;/p&gt;}
    &lt;/div&gt;
  )
}</code></pre>
<hr>
<p>지금은 Context에 하드코딩된 값을 넣고 있음.
다음 강의에서는 Context를 state와 연결해서 실제로 장바구니 아이템이 추가되고 삭제되도록 만들 예정.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Prop Drilling 해결 방법 1 : Component Composition]]></title>
            <link>https://velog.io/@growing-hyo/Prop-Drilling-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95-1-Component-Composition</link>
            <guid>https://velog.io/@growing-hyo/Prop-Drilling-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95-1-Component-Composition</guid>
            <pubDate>Fri, 12 Dec 2025 11:37:27 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>Udemy React 강의 164번 정리</p>
</blockquote>
<p>이번 강의에서는 prop drilling을 줄이는 한 가지 방법, 바로 컴포넌트 합성(Component Composition)에 대해 배웠다.</p>
<p>핵심은 상태를 어디에 두느냐보다 함수를 <strong>누구에게까지 전달해야 하느냐</strong>에 있다.</p>
<h2 id="1-prop-drilling이-생기는-이유">1. Prop Drilling이 생기는 이유</h2>
<h3 id="기존-구조">기존 구조</h3>
<pre><code class="language-code">App
 └─ Shop
     └─ Product</code></pre>
<ul>
<li>handleAddItemToCart 함수는 App에 있음</li>
<li>버튼 클릭은 Product에서 발생</li>
<li>그래서 App → Shop → Product로 함수가 계속 내려감</li>
</ul>
<pre><code class="language-jsx">&lt;App&gt;
  &lt;Shop onAddItemToCart={handleAddItemToCart}&gt;
    &lt;Product onAddToCart={handleAddItemToCart} /&gt;
  &lt;/Shop&gt;
&lt;/App&gt;</code></pre>
<h3 id="문제점">문제점</h3>
<ul>
<li>Shop 컴포넌트는 이 함수를 사용하지 않음</li>
<li>그럼에도 불구하고 &quot;전달&quot;만 하기 위해 props를 받음</li>
<li>이런 구조가 반복되면 컴포넌트가 점점 지저분해짐</li>
</ul>
<p>이게 prop drilling</p>
<h2 id="2-핵심-질문-하나로-정리하기">2. 핵심 질문 하나로 정리하기</h2>
<p><em>이 컴포넌트 안에서 이 함수가 실제로 실행되는가?</em></p>
<ul>
<li>YES → 함수 전달 필요</li>
<li>NO → 함수 전달 불필요 (prop drilling 가능성)</li>
</ul>
<h2 id="3-해결-방법-1--component-composition">3. 해결 방법 1 : Component Composition</h2>
<h3 id="접근-방식">접근 방식</h3>
<ul>
<li>Shop이 제품을 직접 렌더링하지 않게 한다</li>
<li>Shop은 단순히 &quot;감싸는 역할&quot;만 하도록 변경</li>
<li>실제 로직은 App에서 처리</li>
</ul>
<h2 id="4-변경-전-그리고-변경-후-비교">4. 변경 전 그리고 변경 후 비교</h2>
<h3 id="변경-전--shop이-모든-걸-관리">변경 전 : Shop이 모든 걸 관리</h3>
<pre><code class="language-jsx">function Shop({ onAddItemToCart }) {
  return (
    &lt;ul&gt;
      {products.map(product =&gt; (
        &lt;Product
          key={product.id}
          onAddToCart={onAddItemToCart}
        /&gt;
      ))}
    &lt;/ul&gt;
  );
}</code></pre>
<ul>
<li>제품 렌더링</li>
<li>함수 전달</li>
<li>중간 관리자 역할</li>
</ul>
<h3 id="변경-후--shop은-wrapper-역할만">변경 후 : Shop은 wrapper 역할만!</h3>
<pre><code class="language-jsx">function Shop({ children }) {
  return &lt;ul&gt;{children}&lt;/ul&gt;;
}</code></pre>
<p>App에서 직접 Product 렌더링:</p>
<pre><code class="language-jsx">&lt;Shop&gt;
  {products.map(product =&gt; (
    &lt;Product
      key={product.id}
      onAddToCart={handleAddItemToCart}
    /&gt;
  ))}
&lt;/Shop&gt;</code></pre>
<h2 id="5-무엇이-달라졌을까">5. 무엇이 달라졌을까?</h2>
<h3 id="함수-전달-구조-변화">함수 전달 구조 변화</h3>
<pre><code class="language-code">App → Product</code></pre>
<ul>
<li>Product는 버튼 클릭 시 함수를 <strong>직접 실행</strong></li>
<li>Shop은 children만 감쌈 → 함수 몰라도 됨</li>
</ul>
<p>즉, <strong>함수가 필요한 컴포넌트에게만 전달됨</strong>.</p>
<hr>
<p>하지만 이 방법에도 한계가 있다.
모든 로직을 App으로 끌어올리면 App 컴포넌트가 점점 비대해짐.
즉, 모든 prop drilling을 component composition으로 해결할 수는 없음.</p>
<p>그래서 다음 강의에서는 <strong>Context API / 전역 상태 관리</strong>를 배우게 될 예정.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Task 관리 & Prop Drilling 이해하기]]></title>
            <link>https://velog.io/@growing-hyo/Task-%EA%B4%80%EB%A6%AC-Prop-Drilling-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@growing-hyo/Task-%EA%B4%80%EB%A6%AC-Prop-Drilling-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Fri, 12 Dec 2025 05:01:26 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>Udemy React 강의 160번 정리</p>
</blockquote>
<p>이번 강의의 핵심은 크게 두 가지.</p>
<ol>
<li>Task를 어떻게 상태로 관리할 것인가</li>
<li>왜 prop drilling이 문제가 되는가 (그리고 왜 다음에 다른 방법을 배우는지)</li>
</ol>
<h2 id="1-task-입력-ref가-아니라-state를-쓰는-이유">1. Task 입력: ref가 아니라 state를 쓰는 이유</h2>
<h3 id="상황">상황</h3>
<ul>
<li>NewTask 컴포넌트에 input + Add Task 버튼이 있음</li>
<li>버튼을 누르면 1) 입력된 task를 저장해야 하고, 2) input을 비워야 함</li>
</ul>
<h3 id="ref로-하면-안-될까">ref로 하면 안 될까?</h3>
<p>가능은 함.
하지만 여기서는 React가 DOM을 제어하도록 두는 연습을 하기 위해 state를 사용함.</p>
<h3 id="그래서-선택한-방식-controlled-input-state">그래서 선택한 방식: controlled input (state)</h3>
<pre><code class="language-jsx">import { useState } from &#39;react&#39;;

export default function NewTask({ onAdd }) {
  const [enteredTask, setEnteredTask] = useState(&#39;&#39;);

  function handleChange(event) {
    setEnteredTask(event.target.value);
  }

  function handleClick() {
    // 1. 먼저 입력된 값을 App 컴포넌트로 보내기
    // 2. 이 입력란을 빈 칸으로 리셋하기
    onAdd(enteredTask);
    setEnteredTask(&#39;&#39;);
  }

  return (
    &lt;div className=&quot;flex items-center gap-4&quot;&gt;
      &lt;input
        type=&quot;text&quot;
        className=&quot;w-64 px-2 py-1 rounded-sm bg-stone-200&quot;
        onChange={handleChange}
        value={enteredTask}
      /&gt;
      &lt;button
        className=&quot;text-stone-700 hover:text-stone-950&quot;
        onClick={handleClick}
      &gt;
        Add Task
      &lt;/button&gt;
    &lt;/div&gt;
  );
}</code></pre>
<ul>
<li>onChange → 입력값을 state에 저장</li>
<li>value={enteredTask} → state를 다시 input에 반영</li>
<li>버튼 클릭 시: (1) task 전달 (2) setEnteredTask(&#39;&#39;) → input 초기화</li>
</ul>
<p>즉, 입력값을 직접 DOM에서 조작하지 않고, state로 관리.</p>
<h2 id="2-task는-어디에-저장해야-할까">2. Task는 어디에 저장해야 할까?</h2>
<h3 id="중요한-설계-포인트">중요한 설계 포인트</h3>
<ul>
<li>Task는 특정 Project에 속함</li>
<li>Project와 Task를 모두 알고 있는 곳은? App 컴포넌트</li>
<li>그래서 1) Task 배열 상태는 App에 둔다 2) Task 추가 및 삭제 함수도 App에 둔다</li>
</ul>
<h2 id="3-task-추가-흐름-전체-데이터-흐름">3. Task 추가 흐름 (전체 데이터 흐름)</h2>
<h3 id="데이터-흐름-요약">데이터 흐름 요약</h3>
<pre><code class="language-code">NewTask
  ↓ (onAdd)
Tasks
  ↓
SelectedProject
  ↓
App (handleAddTask)</code></pre>
<p>이게 바로 prop drilling임.</p>
<h2 id="4-prop-drilling이란">4. Prop Drilling이란?</h2>
<h3 id="정의">정의</h3>
<p>필요한 컴포넌트가 아닌데도 단지 <strong>전달만 하기 위해 props를 계속 내려보내는 것</strong></p>
<h3 id="실제로-여기서-발생한-일">실제로 여기서 발생한 일</h3>
<ul>
<li>handleAddTask 함수는 App에 있음</li>
<li>하지만 버튼은 NewTask에 있음</li>
<li>중간 컴포넌트들: SelectedProject | Tasks</li>
<li>이 컴포넌트들은 함수를 쓰지도 않으면서 전달만 함</li>
</ul>
<pre><code class="language-code">App
 └─ SelectedProject
     └─ Tasks
         └─ NewTask (여기서 실제로 사용)</code></pre>
<p>중간 컴포넌트들이 점점 지저분해짐</p>
<hr>
<p>이 강의에서는 Task 기능을 구현하는 것보다 state를 어디에 두어야 하는지와 prop drilling이 왜 불편해지는지를 직접 체감하는 게 핵심이었다.</p>
<p>다음 강의에서는 이 문제를 더 깔끔하게 해결하는 방법을 배운다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Refs와 State를 함께 사용해야 하는 경우 (타이머 예시)]]></title>
            <link>https://velog.io/@growing-hyo/Refs%EC%99%80-State%EB%A5%BC-%ED%95%A8%EA%BB%98-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%95%BC-%ED%95%98%EB%8A%94-%EA%B2%BD%EC%9A%B0-%ED%83%80%EC%9D%B4%EB%A8%B8-%EC%98%88%EC%8B%9C</link>
            <guid>https://velog.io/@growing-hyo/Refs%EC%99%80-State%EB%A5%BC-%ED%95%A8%EA%BB%98-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%95%BC-%ED%95%98%EB%8A%94-%EA%B2%BD%EC%9A%B0-%ED%83%80%EC%9D%B4%EB%A8%B8-%EC%98%88%EC%8B%9C</guid>
            <pubDate>Thu, 04 Dec 2025 12:02:45 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>Udemy React 강의 141번 정리</p>
</blockquote>
<h3 id="문제-상황">문제 상황</h3>
<ul>
<li><code>setTimeout()</code>은 한 번만 실행됨 → 경과 시간 추적 불가</li>
<li>목표 :<ul>
<li>시간이 지속적으로 얼마나 남았는지 추적</li>
<li>시간이 끝나면 자동으로 모달 열기</li>
<li>사용자가 중간에 STOP 눌러도 멈추기</li>
<li>성공과 실패를 구분하여 표시</li>
</ul>
</li>
</ul>
<p>그래서 <code>setTimeout</code> 대신 <code>setInterval</code>을 사용하여 계속 시간 감소시키는 방식 필요</p>
<h3 id="setinterval-vs-settimeout-차이">setInterval vs setTimeout 차이</h3>
<table>
  <thead>
    <tr>
      <th>기능</th>
      <th>setTimeout</th>
      <th>setInterval</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>실행 방식</td>
      <td>일정 시간 후 1회 실행</td>
      <td>일정 시간마다 반복 실행</td>
    </tr>
    <tr>
      <td>경과 시간 추적</td>
      <td>불가능</td>
      <td>가능</td>
    </tr>
    <tr>
      <td>타이머 종료 시 별도 처리</td>
      <td>필요 없음</td>
      <td>직접 clearInterval 해야 함</td>
    </tr>
  </tbody>
</table>

<h3 id="왜-ref로-interval-id를-저장해야-할까">왜 ref로 interval ID를 저장해야 할까?</h3>
<table>
  <thead>
    <tr>
      <th>저장 방식</th>
      <th>문제점</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>let timer 일반 변수</td>
      <td>리렌더링 때 사라짐 or 컴포넌트 인스턴스 공유됨</td>
    </tr>
    <tr>
      <td>state</td>
      <td>state 변경 → 리렌더링 발생 → 오히려 timer 초기화 문제</td>
    </tr>
    <tr>
      <td>ref</td>
      <td>값 유지 + UI 업데이트 없음 + 컴포넌트마다 독립</td>
    </tr>
  </tbody>
</table>


<p>핵심 : UI와 상관없는 값이지만 컴포넌트가 재실행되어도 유지되어야 하는 값 → ref에 저장!</p>
<h3 id="setinterval--ref--state-조합이-필요한-이유">setInterval + ref + state 조합이 필요한 이유</h3>
<table>
  <thead>
    <tr>
      <th>역할</th>
      <th>담당</th>
      <th>설명</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>state (timeRemaining)</td>
      <td>화면 업데이트</td>
      <td>화면에 남은 시간을 보여주기 위해 필요</td>
    </tr>
    <tr>
      <td>ref (intervalId)</td>
      <td>반복 실행 제어</td>
      <td>더 이상 실행되면 안 되므로 중단 시 필요</td>
    </tr>
    <tr>
      <td>state vs ref</td>
      <td>영향도</td>
      <td>state는 UI 렌더링, ref는 내부 값 저장</td>
    </tr>
  </tbody>
</table>

<p>즉, UI에 반영되는 값(남은 시간)은 state, UI와 무관한 실행 상태(interval ID)는 ref.</p>
<h3 id="성공--실패-처리-흐름">성공 / 실패 처리 흐름</h3>
<p>실패 → [조건] timeReamining &lt;= 0 → &quot;You lost!&quot;
성공 → [조건] STOP 버튼 클릭 → &quot;You win!&quot;</p>
<p>둘다 동일하게 <code>dialog.current.open()</code> 호출.
즉, 내부 dialog가 어떻게 구현되었는지 TimerChallenge는 몰라도 됨. open이라는 컴포넌트 API만 호출하면 됨.</p>
<h3 id="이-강의의-핵심-요약">이 강의의 핵심 요약</h3>
<p>setInterval로 남은 시간을 지속적으로 추적해야 하는 경우, ref와 state는 서로 다른 목적을 가진다.
state는 UI를 업데이트하기 위한 값이며, ref는 UI와 무관하지만 유지되어야 하는 데이터를 저장하는 데 적합하다.
React 컴포넌트에서 <strong>UI 반영 여부가 ref와 state를 구분하는 기준</strong>이다.</p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[useImperativeHandle — 컴포넌트의 내부 기능(API)을 외부에 노출하는 방법]]></title>
            <link>https://velog.io/@growing-hyo/useImperativeHandle-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EC%9D%98-%EB%82%B4%EB%B6%80-%EA%B8%B0%EB%8A%A5API%EC%9D%84-%EC%99%B8%EB%B6%80%EC%97%90-%EB%85%B8%EC%B6%9C%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@growing-hyo/useImperativeHandle-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EC%9D%98-%EB%82%B4%EB%B6%80-%EA%B8%B0%EB%8A%A5API%EC%9D%84-%EC%99%B8%EB%B6%80%EC%97%90-%EB%85%B8%EC%B6%9C%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Thu, 04 Dec 2025 11:33:28 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>Udemy React 강의 140번 정리</p>
</blockquote>
<pre><code class="language-jsx">&lt;ResultModal ref={dialog} /&gt;

// 그리고 외부에서
dialog.current.showModal()</code></pre>
<p>이전 방식은 이러하다. TimerChallenge 컴포넌트는 ResultModal 내부가 dialog라는 사실을 알고 있어야 하고, 만약 내부 DOM이 <code>&lt;dialog&gt;</code>에서 <code>&lt;div&gt;</code>로 바뀌면? <code>showModal()</code> 메서드는 존재하지 않고, 호출에 실패하여 결국 에러를 발생시킨다.</p>
<p>즉, 내부 구현을 외부가 알고 있어야 하는 구조, 즉, 매우 취약한 API가 된다.</p>
<p>큰 규모의 팀 프로젝트에서는 컴포넌트가 내부 DOM 구조를 바꿔도 외부 기능은 유지되는 게 중요하다.</p>
<h2 id="해결-방법--useimperativehandle">해결 방법 — <code>useImperativeHandle</code></h2>
<p><code>ref.current</code>에 내가 정의한 메서드를 저장해서 외부에서 호출할 수 있게 해준다.</p>
<p>예시 :</p>
<pre><code class="language-jsx">useImperativeHandle(ref, () =&gt; ({
  open() {
    dialog.current.showModal();
  }
}));</code></pre>
<p>이제 외부에서는 다음만 호출하면 됨 :</p>
<pre><code class="language-jsx">dialog.current.open();</code></pre>
<ul>
<li>이제 외부는 <code>dialog</code>가 뭔지 몰라도 됨.</li>
<li>내부에서 DOM 구조가 바뀌어도 <code>open()</code>만 유지하면 되는 API 구조가 탄생!</li>
</ul>
<h2 id="코드-구조">코드 구조</h2>
<p>ResultModal.jsx (구버전 방식 : forwardRef 사용)</p>
<pre><code class="language-jsx">import { forwardRef, useRef, useImperativeHandle } from &quot;react&quot;;

const ResultModal = forwardRef(function ResultModal(props, ref) {
  const dialog = useRef();

  useImperativeHandle(ref, () =&gt; ({
    open() {
      dialog.current.showModal();
    }
  }));

  return &lt;dialog ref={dialog}&gt;...&lt;/dialog&gt;;
});

export default ResultModal;</code></pre>
<p>TimerChallenge.jsx</p>
<pre><code class="language-jsx">const dialog = useRef();

&lt;ResultModal ref={dialog} /&gt;

dialog.current.open();</code></pre>
<ul>
<li>외부는 실제 HTML 요소의 메서드를 몰라도 되고, <code>open()</code>이라는 API만 알면 됨. 그리고 이 open이라는 이름은 내 마음대로 지을 수 있음. <code>openSesame()</code>여도 됨. 다만 팀 협업 시 이름은 의미가 명확해야 한다는 것.</li>
</ul>
<h2 id="useimperativehandle-정리">useImperativeHandle 정리</h2>
<table>
  <thead>
    <tr>
      <th>개념</th>
      <th>설명</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>사용 목적</td>
      <td>컴포넌트 내부 기능을 외부에서 제어할 수 있도록 API 형태로 노출</td>
    </tr>
    <tr>
      <td>왜 필요한가?</td>
      <td>외부가 내부 DOM 구조(showModal 등)에 의존하지 않도록 하기 위함</td>
    </tr>
    <tr>
      <td>전달되는 내용</td>
      <td>ref.current에 커스텀 메서드(open, close 등)를 가진 객체가 저장됨</td>
    </tr>
    <tr>
      <td>내부 DOM 변경 영향</td>
      <td>DOM이 바뀌어도 API(open 등)만 유지하면 외부는 영향 없음</td>
    </tr>
    <tr>
      <td>forwardRef 필요?</td>
      <td>React 최신 버전에서는 선택사항, 구버전에서는 필수</td>
    </tr>
    <tr>
      <td>사용하면 안 되는 경우</td>
      <td>props로 UI 제어가 가능한 경우 (예: isOpen, toggle 상태 전달)</td>
    </tr>
    <tr>
      <td>적합한 사용 예</td>
      <td>Modal, toast, scroll, focus 등 '명령 기반(imperative)' UI 제어</td>
    </tr>
  </tbody>
</table>

<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[커스텀 컴포넌트로 ref 전달하기]]></title>
            <link>https://velog.io/@growing-hyo/%EC%BB%A4%EC%8A%A4%ED%85%80-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EB%A1%9C-ref-%EC%A0%84%EB%8B%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@growing-hyo/%EC%BB%A4%EC%8A%A4%ED%85%80-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EB%A1%9C-ref-%EC%A0%84%EB%8B%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 04 Dec 2025 11:02:30 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>Udemy React 강의 139번 정리</p>
</blockquote>
<p>리액트에서 ref는 원래 DOM 요소에 연결할 때 사용한다.</p>
<pre><code class="language-jsx">&lt;input ref={inputRef} /&gt;</code></pre>
<p>하지만 커스텀 컴포넌트 내부의 DOM 요소에 ref를 연결하고 싶을 때는 상황이 조금 까다롭다.</p>
<p>강의 예제에서는 TimerChallenge → ResultModal 모달을 띄우기 위해 dialog 요소에 ref를 연결해야 한다.</p>
<h2 id="1-기본-개념--ref는-dom에만-직접-붙는다">1. 기본 개념 : ref는 DOM에만 직접 붙는다</h2>
<p>다음 코드는 가능하다.</p>
<pre><code class="language-jsx">&lt;dialog ref={dialog} /&gt;</code></pre>
<p>하지만 다음은 기본적으로 동작하지 않는다.</p>
<pre><code class="language-jsx">&lt;ResultModal ref={dialog} /&gt;   // 커스텀 컴포넌트라서 직접 ref 연결 불가</code></pre>
<p>그래서 <strong>커스텀 컴포넌트가 ref를 전달받도록 처리</strong>해줘야 한다.</p>
<h2 id="2-최신-react-react-19--183에서는-그냥-ref-props-받아서-하면-됨">2. 최신 React (React 19 / 18.3~)에서는 그냥 ref props 받아서 하면 됨!</h2>
<p>React 19에서는 아래 코드가 동작한다.</p>
<p>TimerChallenge.jsx</p>
<pre><code class="language-jsx">const dialog = useRef();

&lt;ResultModal ref={dialog} /&gt;</code></pre>
<p>ResultModal.jsx</p>
<pre><code class="language-jsx">export default function ResultModal({ ref }) {
  return &lt;dialog ref={ref}&gt; ... &lt;/dialog&gt;;
}</code></pre>
<p>그리고 TimerChallenge 안에서는 <code>dialog.current.showModal();</code> 이렇게 하면 바로 dialog 요소를 조작할 수 있다.</p>
<p>최신 리액트는 ref를 일반 prop처럼 받을 수 있게 API가 바뀜 (그래서 forwardRef 없어도 됨)</p>
<p>하지만 구버전 React에서는 위 방식이 안 됨. 즉, ref를 커스텀 컴포넌트에 직접 잔달할 수 없었음. 그래서 구버전에서는 forwardRef를 사용해야 한다.</p>
<p>ResultModal.jsx</p>
<pre><code class="language-jsx">import { forwardRef } from &#39;react&#39;;

const ResultModal = forwardRef(function ResultModal(props, ref) {
  return (
    &lt;dialog ref={ref}&gt;{props.children}&lt;/dialog&gt;
    );
});

export default ResultModal;</code></pre>
<p>여기서 forwardRef가 컴포넌트를 특별한 형태로 감싸줌. 그래서 컴포넌트는 <strong>두 번째 인자</strong>로 ref를 전달받는다. (props, ref)</p>
<h2 id="3-왜-이러한-ref-전달이-필요한가">3. 왜 이러한 ref 전달이 필요한가?</h2>
<p>모달을 열기 위해 <code>showModal()</code>을 호출해야 하기 때문임.</p>
<pre><code class="language-jsx">dialog.current.showModal();</code></pre>
<ul>
<li>dialog는 HTML <code>&lt;dialog&gt;</code> 요소</li>
<li><code>showModal()</code>은 브라우저 내장 메서드</li>
<li>→ React와 무관</li>
<li>하지만 DOM 요소를 직접 조작해야 함</li>
</ul>
<p>따라서 ref로 DOM에 접근해야 한다는 것.</p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[DOM 연결 외 Refs 활용법]]></title>
            <link>https://velog.io/@growing-hyo/DOM-%EC%97%B0%EA%B2%B0-%EC%99%B8-Refs-%ED%99%9C%EC%9A%A9%EB%B2%95</link>
            <guid>https://velog.io/@growing-hyo/DOM-%EC%97%B0%EA%B2%B0-%EC%99%B8-Refs-%ED%99%9C%EC%9A%A9%EB%B2%95</guid>
            <pubDate>Tue, 02 Dec 2025 07:40:55 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>Udemy React 강의 137번 정리</p>
</blockquote>
<p>이 강의에서는 ref의 두 번째 핵심 용도를 다룬다. DOM 요소 말고도 UI에 직접 영향 없는 값을 저장할 때 ref를 쓸 수 있다는 점.
특히 타이머 예제를 통해 왜 변수나 useState로는 안 되고, 왜 ref가 정답인지 설명한다.</p>
<h2 id="1-문제--타이머를-정지해야-하는데">1. 문제 : 타이머를 정지해야 하는데,,,</h2>
<p>setTimeout이 반환하는 타이머는 아래와 같이 사용할 수 있다.</p>
<pre><code class="language-js">const timer = setTimeout(() =&gt; {
  setTimerExpired(true)
}, targetTime * 1000)

clearTimeout(timer)</code></pre>
<p>즉, 타이머를 멈추려면 타이머 ID(pointer)가 필요함.</p>
<p>그래서 처음엔 이렇게 생각할 수 있다.</p>
<pre><code class="language-js">let timer // 타이머 ID 저장

function handleStart() {
  timer = setTimeout(...)
}

function handleStop() {
  clearTimeout(timer)
}</code></pre>
<p>하지만 이렇게 하면 제대로 작동하지 않는다.</p>
<h2 id="2-왜-단순-변수let로는-안-될까">2. 왜 단순 변수(let)로는 안 될까?</h2>
<p>문제를 일으키는 핵심 이유 2가지 :</p>
<h3 id="1-컴포넌트-렌더링마다-변수-재생성">1) 컴포넌트 렌더링마다 변수 재생성</h3>
<p>React는 state가 바뀌면 컴포넌트를 재실행(re-run)한다.
그리고 컴포넌트 안의 <code>let timer</code>는 렌더링이 다시 일어날 때마다 새로 선언되고, 이전 값은 사라진다.
즉, handleStart에 저장한 타이머 ID가 handleStop 실행 시점에는 <strong>사라져 버린 상태</strong>가 된다.
그래서 clearTimeout()이 제대로 호출되지 않음.</p>
<h3 id="2-여러-컴포넌트-인스턴스-사이에-공유됨">2) 여러 컴포넌트 인스턴스 사이에 공유됨</h3>
<p>이 문제는 더 심각하다.
강의 예제처럼 5초 타이머 / 1초 타이머 두 개가 따로 렌더링된다면 어떻게 될까?</p>
<pre><code class="language-js">let timer</code></pre>
<p>이 변수가 컴포넌트 함수 밖에 선언된다면, 두 컴포넌트 인스턴스가 같은 timer 변수를 공유하게 된다.</p>
<p>결과는,</p>
<ol>
<li>5초 타이머 시작 → timer = 5초 타이머 ID</li>
<li>1초 타이머 시작 → timer = 1초 타이머 ID로 덮어씀</li>
<li>5초 타이머 stop 누르면? → 이미 ID가 덮었음 → 5초 타이머는 안 멈춰짐</li>
<li>결국 You lost! 뜸</li>
</ol>
<p>즉, 컴포넌트 별로 독립된 타이머가 필요한데, 변수는 전부 공유된다.</p>
<h2 id="3-그렇다면-state로-저장하면-되지-않을까">3. 그렇다면 state로 저장하면 되지 않을까?</h2>
<p>state를 사용하면, 또 다른 문제가 발생한다.</p>
<p>state로 타이머 ID를 저장하면,</p>
<pre><code class="language-js">const [timerId, setTimerId] = useState()</code></pre>
<p>문제는,</p>
<ul>
<li>state 변경 → 리렌더 발생</li>
<li>리렌더 → setTimeout 사이드 이펙트 재실행 위험</li>
<li>리렌더는 타이머 관리 목적과 맞지 않음</li>
<li>타이머 ID 자체는 UI에 전혀 영향 없음</li>
</ul>
<p>즉, UI를 바꿀 필요 없는 값 때문에 리렌더를 유발하면 불필요한 성능 소모가 발생한다.</p>
<h2 id="4-그래서-ref가-정답이다">4. 그래서 ref가 정답이다</h2>
<p>ref는 다음과 같은 특징을 가지기 때문.</p>
<h3 id="1-리렌더-사이에서도-값이-유지됨-state처럼-유지됨">1) 리렌더 사이에서도 값이 유지됨 (state처럼 유지됨)</h3>
<p><code>ref.current</code>는 컴포넌트가 여러 번 렌더링되어도 값이 보존됨.
즉, handleStart에서 저장한 ID → handleStop에서도 그대로 살아 있음.</p>
<h3 id="2-컴포넌트마다-ref가-독립됨">2) 컴포넌트마다 ref가 독립됨</h3>
<p>각 TimerChallenge 컴포넌트마다 샌드박스처럼 독립된 ref 생성됨.
즉, 5초/1초 타이머가 서로 타이머 ID를 덮어쓰지 않음.</p>
<h3 id="3-ref-변경은-리렌더를-유발하지-않음">3) ref 변경은 리렌더를 유발하지 않음</h3>
<p>state와 반대로 <code>ref.current</code> 변경은 리렌더를 유발하지 않고, UI에 영향을 주지 않는 값 관리에 최적함.</p>
<p>코드로 보자.</p>
<pre><code class="language-jsx">const timer = useRef(); 

function handleStart() {
  timer.current = setTimeout(() =&gt; {
    setTimerExpired(true);
  }, targetTime * 1000);
}

function handleStop() {
  clearTimeout(timer.current);
}</code></pre>
<ul>
<li>컴포넌트가 리렌더 되어도 <code>timer.current</code> 값 유지</li>
<li>여러 컴포넌트가 있어도 ID 충돌 없음</li>
<li>UI 영향 받지 않음 (불필요한 리렌더 없음)</li>
</ul>
<h2 id="5-최종-요약">5. 최종 요약</h2>
<h3 id="변수let">변수(let)</h3>
<ul>
<li>렌더마다 초기화됨 → 값 보존 불가</li>
<li>여러 컴포넌트 인스턴스에서 공유됨 → 충돌 발생</li>
</ul>
<h3 id="state">state</h3>
<ul>
<li>값 유지됨</li>
<li>하지만 변경 시 리렌더 → UI에 필요 없는 렌더 발생</li>
</ul>
<h3 id="ref">ref</h3>
<ul>
<li>값 유지됨</li>
<li>컴포넌트마다 독립됨</li>
<li>변경해도 리렌더 없음 (UI 영향 없는 값에 최적)</li>
</ul>
<p>그래서 타이머 ID 같은 값은 ref로 관리하는 것이 정답이다.</p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[Refs vs State]]></title>
            <link>https://velog.io/@growing-hyo/Refs-vs-State</link>
            <guid>https://velog.io/@growing-hyo/Refs-vs-State</guid>
            <pubDate>Tue, 02 Dec 2025 02:16:29 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><em>Udemy React 강의 134번 정리</em></p>
</blockquote>
<p>React에는 UI를 구성하는 두 개의 중요한 값이 있다.</p>
<ul>
<li><strong>state</strong> : UI를 업데이트하기 위한 값</li>
<li><strong>ref</strong> : DOM 요소 또는 저장하고 싶은 값을 직접 참조하기 위한 값</li>
</ul>
<p>둘은 &quot;언제 사용하는가&quot;와 &quot;리렌더링이 발생하는가&quot;에서 큰 차이를 가진다.</p>
<p>이번 글에서는 왜 ref만으로 UI를 업데이트할 수 없는지, state와 ref의 정확한 역할 차이를 살펴본다.</p>
<h2 id="1-왜-ref-값만-사용해서-ui를-업데이트하면-안-될까">1. 왜 ref 값만 사용해서 UI를 업데이트하면 안 될까?</h2>
<p>아래 코드가 왜 안 되는지부터 보자.</p>
<pre><code class="language-jsx">&lt;p&gt;{playerName.current.value}&lt;/p&gt;</code></pre>
<p>첫 번째 렌더에서 에러가 발생한다. → <code>ref.current</code>가 undefined</p>
<p>처음 컴포넌트가 렌더될 때는 아직 <code>&lt;input ref={playerName}&gt;</code>와 연결되기 전이다. → 따라서 <code>playerName.current === undefined</code></p>
<p>그래서 첫 렌더 때 &quot;Cannot read property &#39;value&#39; of undefined&quot; 같은 오류가 난다.</p>
<h2 id="2-해결처럼-보이는-코드도-결국-안-됨">2. 해결처럼 보이는 코드도 결국 안 됨</h2>
<p>강의에서 소개된 우회 코드 :</p>
<pre><code class="language-jsx">&lt;p&gt;{playerName.current ? playerName.current.value : &quot;Unknown entity&quot;}&lt;/p&gt;</code></pre>
<p>이렇게 하면 에러는 사라지지만, 버튼을 클릭해도 UI가 업데이트되지 않는다.</p>
<p>왜일까?</p>
<h2 id="3-핵심-차이--ref는-변경되어도-리렌더링을-발생시키지-않음">3. 핵심 차이 : ref는 변경되어도 리렌더링을 발생시키지 않음</h2>
<p>이게 state와 ref의 가장 중요한 차이다.</p>
<table>
  <thead>
    <tr>
      <th>구분</th>
      <th>ref</th>
      <th>state</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>값이 변해도 UI가 리렌더링 되나?</td>
      <td>❌ 리렌더링 안 됨</td>
      <td>⭕️ 리렌더링 됨</td>
    </tr>
    <tr>
      <td>DOM 요소 접근 용도</td>
      <td>⭕️</td>
      <td>❌</td>
    </tr>
    <tr>
      <td>값이 자주 바뀌지만 UI에 반영할 필요 없을 때?</td>
      <td>⭕️ 매우 효율적</td>
      <td>❌ 매번 리렌더링 됨</td>
    </tr>
    <tr>
      <td>UI 업데이트 필요할 때?</td>
      <td>❌ 사용하면 안 됨</td>
      <td>⭕️ 필수</td>
    </tr>
  </tbody>
</table>

<p>React는 <strong>state가 변경될 때만</strong> 컴포넌트가 다시 실행된다.
ref는 아무리 바뀌어도 컴포넌트를 다시 실행하지 않는다.</p>
<p>그래서 ref만으로는 UI가 업데이트 될 수 없다.</p>
<h2 id="4-왜-state가-필요한가">4. 왜 state가 필요한가?</h2>
<p>state는 UI와 1:1로 연결되어 있기 때문에</p>
<ul>
<li>값이 바뀐다 → 컴포넌트 재실행 → UI 업데이트</li>
</ul>
<p>즉, UI에 즉시 반영되어야 하는 값은 반드시 state로 관리해야 한다.</p>
<p>반면 ref는 값이 바뀌어도 컴포넌트가 리렌더링 되지 않는다. 따라서 UI에 반영되지 않는다.</p>
<p>❗️ ref는 DOM에 접근하거나 <strong>렌더링과 무관한 값 저장용</strong>일 뿐, UI를 업데이트하려면 무조건 state를 사용해야 한다.</p>
<h2 id="5-ref와-state는-서로-대체-관계가-아님">5. ref와 state는 서로 대체 관계가 아님!</h2>
<p>React에서 두 값은 목적 자체가 다르다.</p>
<h3 id="state를-써야-하는-경우">state를 써야 하는 경우</h3>
<ul>
<li>화면에 값이 보여야 할 때</li>
<li>값이 바뀌면 UI도 바뀌어야 할 때</li>
<li>입력값을 실시간으로 화면에 반영해야 할 때</li>
</ul>
<h3 id="ref를-써야-하는-경우">ref를 써야 하는 경우</h3>
<ul>
<li>DOM 요소에 직접 접근해야 할 때</li>
<li>입력값을 나중에 한 번에 가져올 때</li>
<li>값이 변경되어도 UI 업데이트가 필요 없는 경우</li>
<li>리렌더링을 피하고 싶을 때</li>
</ul>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[Refs (참조)]]></title>
            <link>https://velog.io/@growing-hyo/Refs-%EC%B0%B8%EC%A1%B0</link>
            <guid>https://velog.io/@growing-hyo/Refs-%EC%B0%B8%EC%A1%B0</guid>
            <pubDate>Tue, 02 Dec 2025 01:45:14 GMT</pubDate>
            <description><![CDATA[<p>React는 기본적으로 state(상태)를 통해 UI를 업데이트하는 구조지만, 때로는 state로 굳이 관리하지 않아도 되는 값이나 DOM 요소 자체에 접근해야 하는 상황이 생긴다.</p>
<p>이때 사용하는 것이 바로 Ref(Reference, 참조)이다.</p>
<h2 id="1-ref란-무엇인가">1. Ref란 무엇인가?</h2>
<p>Ref는 말 그대로 <strong>특정 요소를 가리키는 참조값</strong>.</p>
<ul>
<li><code>useState</code>처럼 React가 관리하는 특별한 값</li>
<li>하지만 UI 업데이트를 위한 값이 아니라 <strong>DOM 요소(HTML 요소)에 직접 접근할 때</strong> 사용하는 값</li>
</ul>
<p>Ref를 만들기 위한 Hook :</p>
<pre><code class="language-jsx">import { useRef } from &#39;react&#39;;

const playerNameRef = useRef();</code></pre>
<h2 id="2-ref를-jsx-요소에-연결하기">2. Ref를 JSX 요소에 연결하기</h2>
<p>모든 JSX 요소(내장 DOM 요소)는 ref라는 특별한 prop을 지원한다.</p>
<pre><code class="language-jsx">&lt;input ref={playerNameRef} /&gt;</code></pre>
<p>이렇게 연결하면 :</p>
<pre><code class="language-code">playerNameRef.current → 해당 input DOM 요소</code></pre>
<p>여기서 핵심은 :</p>
<p>useRef()로 만든 ref 객체는 항상 <code>{ current: ... }</code> 형태다.
진짜 값(여기서는 input 요소)은 current에 들어간다.</p>
<h2 id="3-왜-ref가-필요할까-state와의-차이">3. 왜 Ref가 필요할까? (State와의 차이)</h2>
<p>보통 input 값 관리할 때는 state로 관리한다.</p>
<pre><code class="language-jsx">value={input}
onChange={(e) =&gt; setInput(e.target.value)}</code></pre>
<p>하지만 이 방식은 타이핑할 때마다 <strong>re-render</strong>가 일어난다.</p>
<p><strong>입력값이 단순히 저장만 필요할 때는</strong> 굳이 state로 관리할 필요가 없다. 이럴 때 <strong>Ref로 읽어오는 방식</strong>이 훨씬 단순하다.</p>
<h2 id="4-ref로-값-읽어오기-onclick-시점에만-가져오기">4. Ref로 값 읽어오기 (onClick 시점에만 가져오기)</h2>
<pre><code class="language-jsx">function clickHandler() {
  const entered = playerNameRef.current.value;
  setPlayerName(entered)l
}</code></pre>
<p>이렇게 하면 (1)매 타이핑마다 렌더되지 않고, (2)필요할 때(버튼 클릭 시)에만 값 읽어오고, (3)컴포넌트 코드가 더 간결해진다.</p>
<h2 id="5-사용-예">5. 사용 예</h2>
<p>기존 방식 : state로 모든 입력 변화 추적했음.</p>
<ul>
<li>onChange로 매 입력마다 state 갱신</li>
<li>state로 value도 관리</li>
<li>submitted state도 별도로 관리</li>
</ul>
<h3 id="💫-ref-방식">💫 Ref 방식</h3>
<pre><code class="language-jsx">const playerNameRef = useRef();
const [playerName, setPlayerName] = useState(&#39;&#39;);

function handleClick() {
  setPlayerName(playerNameRef.current.value); // DOM에서 직접 가져옴
}

return (
  &lt;&gt;
    &lt;input ref={playerNameRef} /&gt;
    &lt;button onClick={handleClick}&gt;Set Name&lt;/button&gt;
    &lt;p&gt;{playerName ?? &#39;Unknown entity&#39;}&lt;/p&gt;
  &lt;/&gt;
);</code></pre>
<ul>
<li>타이핑할 때 UI가 리렌더링되지 않음</li>
<li>버튼 누른 순간에만 이름이 반영됨</li>
<li>코드가 더 간단함 (onChange, value props 필요 없음)</li>
</ul>
<h2 id="6-ref는-언제-쓰는-게-맞을까">6. Ref는 언제 쓰는 게 맞을까?</h2>
<h3 id="ref가-적합한-경우">Ref가 적합한 경우</h3>
<ul>
<li>input 값이 <strong>즉시 UI 업데이트</strong>가 필요 <strong>없을</strong> 때</li>
<li>입력값을 <strong>한 번에 모아서</strong> 가져오고 싶을 때 (예: 폼 제출)</li>
<li>특정 DOM 메서드(<code>focus()</code>, <code>scrollIntoView()</code> 등)를 호출해야 할 때</li>
<li>애니메이션, 외부 라이브러리, Canvas 조작 등 DOM 직접 접근이 필요할 때</li>
</ul>
<h3 id="ref가-적합하지-않은-경우">Ref가 적합하지 않은 경우</h3>
<ul>
<li>화면에 입력값을 실시간 반영해야 하는 경우 → 반드시 state로 관리!</li>
<li>리렌더링이 UI에 필요한 경우</li>
</ul>
<p>즉, Ref는 React 안에서 <strong>DOM을 직접 다뤄야 할 때 가장 중요한 도구</strong>다.</p>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[리액트 컴포넌트 – Styled Components 재사용 가능한 컴포넌트 생성 및 컴포넌트 조합]]></title>
            <link>https://velog.io/@growing-hyo/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-Styled-Components-%EC%9E%AC%EC%82%AC%EC%9A%A9-%EA%B0%80%EB%8A%A5%ED%95%9C-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%83%9D%EC%84%B1-%EB%B0%8F-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%A1%B0%ED%95%A9</link>
            <guid>https://velog.io/@growing-hyo/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-Styled-Components-%EC%9E%AC%EC%82%AC%EC%9A%A9-%EA%B0%80%EB%8A%A5%ED%95%9C-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%83%9D%EC%84%B1-%EB%B0%8F-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%A1%B0%ED%95%A9</guid>
            <pubDate>Sat, 29 Nov 2025 13:36:55 GMT</pubDate>
            <description><![CDATA[<p>Styled Components로 여러 UI 요소(div, label, input, button 등)를 만들다 보면, 처음에는 컴포넌트 파일 안에 작은 styled component들을 계속 만들어 붙이게 된다. 하지만 규모가 커질수록 어떤 컴포넌트를 재사용할지, 파일을 어떻게 나눌지가 매우 중요해짐.</p>
<h2 id="1-파일-안에-그대로-둘-것-vs-분리할-것">1. 파일 안에 그대로 둘 것 vs. 분리할 것</h2>
<p>Styled Components를 여러 개 만들다 보면 어떤 컴포넌트는 <strong>해당 파일에만 필요한 것</strong>이고, 어떤 컴포넌트는 <strong>여러 페이지 또는 컴포넌트에서 재사용될 가능성이 있는데</strong>, 이 두 가지를 구분하는 것이 핵심이다.</p>
<p>예시 : ControlContainer (div)</p>
<ul>
<li>AuthInput.jsx 안에서만 쓰임</li>
<li>다른 곳에서 재사용될 가능성 거의 없음
➡️ 파일 안에 그대로 두는 것이 정답</li>
</ul>
<p>예시 : Button, Input, Label</p>
<ul>
<li>다른 페이지/폼에서도 반복해서 사용 가능</li>
<li>스타일을 통일하는 것이 UX적으로도 중요
➡️ 별도 파일로 분리하는 것이 정답</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[리액트 컴포넌트 스타일링 — Styled Components [심화] 동적 스타일링 · 가상 선택자 · 중첩 규칙 · 미디어 쿼리]]></title>
            <link>https://velog.io/@growing-hyo/%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-%EC%8B%AC%ED%99%94-%EB%8F%99%EC%A0%81-%EC%8A%A4%ED%83%80%EC%9D%BC%EB%A7%81-%EA%B0%80%EC%83%81-%EC%84%A0%ED%83%9D%EC%9E%90-%EC%A4%91%EC%B2%A9-%EA%B7%9C%EC%B9%99-%EB%AF%B8%EB%94%94%EC%96%B4-%EC%BF%BC%EB%A6%AC</link>
            <guid>https://velog.io/@growing-hyo/%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-%EC%8B%AC%ED%99%94-%EB%8F%99%EC%A0%81-%EC%8A%A4%ED%83%80%EC%9D%BC%EB%A7%81-%EA%B0%80%EC%83%81-%EC%84%A0%ED%83%9D%EC%9E%90-%EC%A4%91%EC%B2%A9-%EA%B7%9C%EC%B9%99-%EB%AF%B8%EB%94%94%EC%96%B4-%EC%BF%BC%EB%A6%AC</guid>
            <pubDate>Sat, 29 Nov 2025 13:36:09 GMT</pubDate>
            <description><![CDATA[<p>Styled Components는 동적 스타일링, 상태 기반 조건부 CSS, 중첩 셀렉터, hover 스타일, Media Query까지 모두 처리할 수 있다.</p>
<p>이 글에서는 <strong>props로 스타일을 동적으로 변경하는 방법</strong>, <strong>$ prefix를 사용하는 이유</strong>, <strong>가상 선택자(:hover) 적용</strong>, <strong>중첩 규칙 적용</strong>, <strong>미디어 쿼리를 Styled Components 안에서 작성하는 방법</strong>을 정리한다.</p>
<h2 id="1-styled-components로-조건부-스타일링하기">1. Styled Components로 조건부 스타일링하기</h2>
<p>Styled Components의 진짜 강점은 <strong>props에 따라 동적으로 CSS를 바꿀 수 있다</strong>는 것이다.</p>
<p>예 : label이 invalid일 때만 빨간색으로 스타일 바꾸기</p>
<pre><code class="language-jsx">const Label = styled.label`
  color: ${(props) =&gt; (props.$invalid ? &#39;#ff6b6b&#39; : &#39;#333&#39;)};
`;</code></pre>
<p>jsx에서는 아래와 같이 작성한다.</p>
<pre><code class="language-jsx">&lt;Label $invalid={!emailIsValid}&gt;Email&lt;/Label&gt;</code></pre>
<p>어떻게 동작하는 걸까?
props.$invalid는 true/false 값을 받고, Styled Components는 <strong>CSS 템플릿 리터럴 안에서 자바스크립트 함수를 실행</strong>해 그 반환값을 CSS에 삽입한다.</p>
<p>즉, 조건부 className 없이 깨끗하게 스타일을 토글할 수 있다.</p>
<h2 id="2-왜-invalid처럼--prefix를-붙일까">2. 왜 $invalid처럼 $ prefix를 붙일까?</h2>
<p>Styled Components는 모든 props를 실제 DOM 요소로 전달한다.</p>
<p>예를 들어 :</p>
<pre><code class="language-jsx">&lt;label invalid=&quot;false&quot;&gt;</code></pre>
<p>이런 식으로 렌더링되면 브라우저에서 &quot;invalid&quot;는 HTML built-in attribute라고 판단해서 콘솔 경고가 뜬다.</p>
<p>그래서 Styled Components는 스타일링용 props에 $invalid, $error, $active처럼 $ prefix를 붙이는 패턴을 권장한다.</p>
<p>참고로 $로 시작하는 props는 DOM으로 전달되지 않음! (Styled Components 내부에서만 사용됨)</p>
<h2 id="3-input에도-조건부-스타일-적용하기">3. Input에도 조건부 스타일 적용하기</h2>
<p>입력값이 invalid일 때만 배경, 테두리, 글자색을 바꾸고 싶다면?</p>
<pre><code class="language-jsx">const Input = styled.input`
  background-color: ${(p) =&gt; (p.$invalid ? &#39;#fddddd&#39; : &#39;#f3f3f3&#39;)};
  border-color: ${(p) =&gt; (p.$invalid ? &#39;#ff6b6b&#39; : &#39;transparent&#39;)};
  color: ${(p) =&gt; (p.$invalid ? &#39;#ff6b6b&#39; : &#39;#333&#39;)};
`;</code></pre>
<p>jsx에서는 아래와 같이 작성한다.</p>
<pre><code class="language-jsx">&lt;Input $invalid={!emailIsValid} /&gt;</code></pre>
<p>이 방식은 클래스를 토글하는 방식보다 <strong>더 선언적</strong>이고, <strong>JSX가 더 깔끔</strong>해지고, <strong>CSS가 컴포넌트 근처에 있어서 관리하기 쉽다</strong>.</p>
<h2 id="4-중첩-규칙-nested-selectors">4. 중첩 규칙 (Nested Selectors)</h2>
<p>Styled Components 안에서는 CSS에서 사용하던 중첩 구조를 그대로 사용할 수 있다.</p>
<p>예 : 부모 header 안의 img, h1 등을 한 번에 스타일링하기</p>
<pre><code class="language-jsx">const Header = styled.header`
  display: flex;
  flex-direction: column;

  &amp; img {
    width: 120px;
  }

  &amp; h1 {
    font-size: 2rem;
  }
`;</code></pre>
<p>&amp;의 의미</p>
<ul>
<li>&amp; = 현재 styled component 자신</li>
<li>&amp; img = Header 내부의 img 요소</li>
<li>&amp; h1 = Header 내부의 h1 요소</li>
</ul>
<p>이 방식 덕분에 각각을 따로 Styled Component로 만들지 않아도 된다.</p>
<h2 id="5-pseudo-selector-가상-선택자-사용하기-hover">5. Pseudo Selector (가상 선택자) 사용하기 [:hover]</h2>
<p>Hover 스타일도 매우 간단하게 적용할 수 있다.</p>
<pre><code class="language-jsx">const Button = styled.button`
  background-color: #222;
  padding: 1rem;

  &amp;:hover {
    background-color: #333;
  }
`;</code></pre>
<h2 id="6-media-query도-똑같이-사용-가능">6. Media Query도 똑같이 사용 가능</h2>
<p>CSS에서 하던 방식 그대로 작성하면 된다.</p>
<pre><code class="language-jsx">const Header = styled.header`
  margin-bottom: 2rem;

  @media (min-width: 768px) {
    margin-bottom: 4rem;
  }
`;</code></pre>
<p>CSS와 99% 동일하다는 점이 Styled Components의 큰 장점 중 하나.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[리액트 컴포넌트 스타일링 — [심화] 동적 스타일링 · 가상 선택자 · 중첩 규칙 · 미디어 쿼리]]></title>
            <link>https://velog.io/@growing-hyo/%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-%EC%8B%AC%ED%99%94-%EB%8F%99%EC%A0%81-%EC%8A%A4%ED%83%80%EC%9D%BC%EB%A7%81-%EA%B0%80%EC%83%81-%EC%84%A0%ED%83%9D%EC%9E%90-%EC%A4%91%EC%B2%A9-%EA%B7%9C%EC%B9%99-%EB%AF%B8%EB%94%94%EC%96%B4-%EC%BF%BC%EB%A6%AC</link>
            <guid>https://velog.io/@growing-hyo/%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-%EC%8B%AC%ED%99%94-%EB%8F%99%EC%A0%81-%EC%8A%A4%ED%83%80%EC%9D%BC%EB%A7%81-%EA%B0%80%EC%83%81-%EC%84%A0%ED%83%9D%EC%9E%90-%EC%A4%91%EC%B2%A9-%EA%B7%9C%EC%B9%99-%EB%AF%B8%EB%94%94%EC%96%B4-%EC%BF%BC%EB%A6%AC</guid>
            <pubDate>Fri, 28 Nov 2025 01:34:39 GMT</pubDate>
            <description><![CDATA[<p>Styled Components는 동적 스타일링, 상태 기반 조건부 CSS, 중첩 셀렉터, hover 스타일, Media Query까지 모두 처리할 수 있다.</p>
<p>이 글에서는 <strong>props로 스타일을 동적으로 변경하는 방법</strong>, <strong>$ prefix를 사용하는 이유</strong>, <strong>가상 선택자(:hover) 적용</strong>, <strong>중첩 규칙 적용</strong>, <strong>미디어 쿼리를 Styled Components 안에서 작성하는 방법</strong>을 정리한다.</p>
<h2 id="1-styled-components로-조건부-스타일링하기">1. Styled Components로 조건부 스타일링하기</h2>
<p>Styled Components의 진짜 강점은 <strong>props에 따라 동적으로 CSS를 바꿀 수 있다</strong>는 것이다.</p>
<p>예 : label이 invalid일 때만 빨간색으로 스타일 바꾸기</p>
<pre><code class="language-jsx">const Label = styled.label`
  color: ${(props) =&gt; (props.$invalid ? &#39;#ff6b6b&#39; : &#39;#333&#39;)};
`;</code></pre>
<p>jsx에서는 아래와 같이 작성한다.</p>
<pre><code class="language-jsx">&lt;Label $invalid={!emailIsValid}&gt;Email&lt;/Label&gt;</code></pre>
<p>어떻게 동작하는 걸까?
props.$invalid는 true/false 값을 받고, Styled Components는 <strong>CSS 템플릿 리터럴 안에서 자바스크립트 함수를 실행</strong>해 그 반환값을 CSS에 삽입한다.</p>
<p>즉, 조건부 className 없이 깨끗하게 스타일을 토글할 수 있다.</p>
<h2 id="2-왜-invalid처럼--prefix를-붙일까">2. 왜 $invalid처럼 $ prefix를 붙일까?</h2>
<p>Styled Components는 모든 props를 실제 DOM 요소로 전달한다.</p>
<p>예를 들어 :</p>
<pre><code class="language-jsx">&lt;label invalid=&quot;false&quot;&gt;</code></pre>
<p>이런 식으로 렌더링되면 브라우저에서 &quot;invalid&quot;는 HTML built-in attribute라고 판단해서 콘솔 경고가 뜬다.</p>
<p>그래서 Styled Components는 스타일링용 props에 $invalid, $error, $active처럼 $ prefix를 붙이는 패턴을 권장한다.</p>
<p>참고로 $로 시작하는 props는 DOM으로 전달되지 않음! (Styled Components 내부에서만 사용됨)</p>
<h2 id="3-input에도-조건부-스타일-적용하기">3. Input에도 조건부 스타일 적용하기</h2>
<p>입력값이 invalid일 때만 배경, 테두리, 글자색을 바꾸고 싶다면?</p>
<pre><code class="language-jsx">const Input = styled.input`
  background-color: ${(p) =&gt; (p.$invalid ? &#39;#fddddd&#39; : &#39;#f3f3f3&#39;)};
  border-color: ${(p) =&gt; (p.$invalid ? &#39;#ff6b6b&#39; : &#39;transparent&#39;)};
  color: ${(p) =&gt; (p.$invalid ? &#39;#ff6b6b&#39; : &#39;#333&#39;)};
`;</code></pre>
<p>jsx에서는 아래와 같이 작성한다.</p>
<pre><code class="language-jsx">&lt;Input $invalid={!emailIsValid} /&gt;</code></pre>
<p>이 방식은 클래스를 토글하는 방식보다 <strong>더 선언적</strong>이고, <strong>JSX가 더 깔끔</strong>해지고, <strong>CSS가 컴포넌트 근처에 있어서 관리하기 쉽다</strong>.</p>
<h2 id="4-중첩-규칙-nested-selectors">4. 중첩 규칙 (Nested Selectors)</h2>
<p>Styled Components 안에서는 CSS에서 사용하던 중첩 구조를 그대로 사용할 수 있다.</p>
<p>예 : 부모 header 안의 img, h1 등을 한 번에 스타일링하기</p>
<pre><code class="language-jsx">const Header = styled.header`
  display: flex;
  flex-direction: column;

  &amp; img {
    width: 120px;
  }

  &amp; h1 {
    font-size: 2rem;
  }
`;</code></pre>
<p>&amp;의 의미</p>
<ul>
<li>&amp; = 현재 styled component 자신</li>
<li>&amp; img = Header 내부의 img 요소</li>
<li>&amp; h1 = Header 내부의 h1 요소</li>
</ul>
<p>이 방식 덕분에 각각을 따로 Styled Component로 만들지 않아도 된다.</p>
<h2 id="5-pseudo-selector-가상-선택자-사용하기-hover">5. Pseudo Selector (가상 선택자) 사용하기 [:hover]</h2>
<p>Hover 스타일도 매우 간단하게 적용할 수 있다.</p>
<pre><code class="language-jsx">const Button = styled.button`
  background-color: #222;
  padding: 1rem;

  &amp;:hover {
    background-color: #333;
  }
`;</code></pre>
<h2 id="6-media-query도-똑같이-사용-가능">6. Media Query도 똑같이 사용 가능</h2>
<p>CSS에서 하던 방식 그대로 작성하면 된다.</p>
<pre><code class="language-jsx">const Header = styled.header`
  margin-bottom: 2rem;

  @media (min-width: 768px) {
    margin-bottom: 4rem;
  }
`;</code></pre>
<p>CSS와 99% 동일하다는 점이 Styled Components의 큰 장점 중 하나.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[리액트 컴포넌트 스타일링 — Styled Components (기본 개념 및 컴포넌트 생성 방법)]]></title>
            <link>https://velog.io/@growing-hyo/%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-Styled-Components-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90-%EB%B0%8F-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%83%9D%EC%84%B1-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@growing-hyo/%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-Styled-Components-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90-%EB%B0%8F-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%EC%83%9D%EC%84%B1-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Thu, 27 Nov 2025 14:02:07 GMT</pubDate>
            <description><![CDATA[<p>React에는 다양한 스타일링 방식이 존재한다.
Vanilla CSS → Inline Styles → CSS Modules까지 모두 React에서 널리 쓰이는 방식이지만 이번에는 완전히 다른 철학을 가진 스타일링 방식인 Styled Components를 살펴본다.</p>
<p>Styled Components는 CSS 파일도, inline 스타일도 아닌 <strong>스타일이 적용된 React 컴포넌트를 직접 생성하는 방식</strong>이다. 즉, 스타일과 컴포넌트를 하나의 단위로 묶어 UI를 구성하게 해준다.</p>
<h2 id="1-styled-components란">1. Styled Components란?</h2>
<p>Styled Components는 서드 파티 라이브러리로 JS 파일 안에서 스타일을 작성하고 그 스타일이 적용된 <strong>커스텀 컴포넌트</strong>를 만들어 쓸 수 있는 방식이다.</p>
<h3 id="설치">설치</h3>
<pre><code class="language-bash">npm install styled-components</code></pre>
<p>설치 후 프로젝트를 새로 실행해야 할 수도 있다.</p>
<h2 id="2-기본-사용법--styleddiv-생성하기">2. 기본 사용법 : <code>styled.div</code> 생성하기</h2>
<p>styled-components를 사용하려면 먼저 styled를 import한다.</p>
<pre><code class="language-jsx">import styled from &#39;styled-components&#39;;</code></pre>
<p>그리고 다음처럼 <code>styled.div</code> 형태로 새로운 컴포넌트를 만들 수 있다.</p>
<pre><code class="language-jsx">const ControlContainer = styled.div`
  margin-bottom: 1rem;
  display: flex;
  flex-direction: column;
`;</code></pre>
<p>여기서 중요한 점,</p>
<ul>
<li>backtick(`) 안에는 순수한 CSS 코드를 그대로 작성한다.</li>
<li>PascalCase로 작성.</li>
<li>멀티 라인 작성 가능.</li>
</ul>
<p>결과적으로 스타일이 적용된 div 컴포넌트가 만들어진다.</p>
<p>이제 기존 div 대신 아래와 같이 사용한다.</p>
<pre><code class="language-jsx">&lt;ControlContainer&gt;
  ...
&lt;/ControlContainer&gt;</code></pre>
<h2 id="3-styled-components가-동작하는-방식">3. Styled Components가 동작하는 방식</h2>
<p>Styled Components는 내부적으로,</p>
<ol>
<li>우리가 작성한 CSS를 빌드 시 고유한 클래스명으로 변환한다.</li>
<li>이 고유한 클래스를 <code>&lt;head&gt;</code>에 삽입한다.</li>
<li>해당 클래스를 우리가 만든 컴포넌트에 자동으로 붙인다</li>
</ol>
<p>즉, <strong>CSS Modules처럼 스코프가 보장</strong>되며, 우리는 className을 일일이 관리할 필요 없이 <strong>스타일이 적용된 컴포넌트만 사용</strong>하면 된다.</p>
<h2 id="4-styled-components로-기본-요소label-input도-재정의하기">4. Styled Components로 기본 요소(label, input)도 재정의하기</h2>
<p>Styled Components는 div 뿐만 아니라 모든 HTML 태그를 대상으로 만들 수 있다.</p>
<h3 id="예--label-스타일링">예 : label 스타일링</h3>
<pre><code class="language-jsx">const Label = styled.label`
  font-weight: bold;
  margin: 0.5rem 0;
  color: #333;
`;</code></pre>
<p>컴포넌트에서 사용할 때는 아래와 같이 사용한다.</p>
<pre><code class="language-jsx">&lt;Label&gt;Email&lt;/Label&gt;</code></pre>
<h3 id="예--input-스타일링">예 : input 스타일링</h3>
<pre><code class="language-jsx">const Input = styled.input`
  padding: 0.5rem;
  border-radius: 6px;
  border: 1px solid #ccc;
`;</code></pre>
<p>컴포넌트에서 사용할 때는 아래와 같이 사용한다.</p>
<pre><code class="language-jsx">&lt;Input type=&quot;email&quot; onChange={...} /&gt;</code></pre>
<h2 id="5-⭐️-styled-components만의-핵심-특징--props-자동-전달">5. ⭐️ Styled Components만의 핵심 특징 : props 자동 전달</h2>
<p>Styled Components는 내부에서 기본 HTML 요소(label, input 등)을 생성한다.
그리고 그 요소에게 우리가 전달한 props를 그대로 forwarding한다.</p>
<p>예 :</p>
<pre><code class="language-jsx">&lt;Input
  type=&quot;password&quot;
  onChange={passwordChangeHandler}
  className={!passwordIsValid ? &#39;invalid&#39; : &#39;&#39;}
/&gt;</code></pre>
<p>이 모든 props는 내부의 실제 <code>&lt;input&gt;</code>에게 그대로 전달된다.</p>
<p>즉, 이벤트 리스너(onChange, onBlur), HTML 속성(type, value), className, style, aria-* 전부 문제 없이 동작한다.</p>
<p>이 덕분에 styled input이냐, 기본 input이냐의 차이만 있을 뿐 나머지 로직은 그대로 유지된다.</p>
<h2 id="6-styled-components의-장단점">6. Styled Components의 장단점</h2>
<h3 id="장점">장점</h3>
<p>1) <strong>컴포넌트 단위 스타일 관리</strong></p>
<ul>
<li>스타일과 구조가 하나의 파일, 한 덩어리로 묶여 유지보수가 쉬움.</li>
</ul>
<p>2) <strong>스코프 자동 보장</strong></p>
<ul>
<li>클래스 이름은 자동으로 유니크하게 변환되므로 충돌 없음.</li>
</ul>
<p>3) <strong>순수 CSS 그대로 사용</strong></p>
<ul>
<li>CSS 문법 변형 없이 그대로 사용 가능.</li>
</ul>
<p>4) <strong>조건부 스타일링 / props 기반 스타일링 강력</strong>
(이 부분은 다음 강의에서 더 깊이 다룸!)</p>
<p>5) <strong>className 안 써도 됨</strong></p>
<ul>
<li>&quot;이 컴포넌트는 이 스타일이다&quot;라는 개념이 명확해짐.</li>
</ul>
<h3 id="단점">단점</h3>
<p>1) <strong>서트 파티 라이브러리 설치 필요</strong></p>
<ul>
<li>번들 크기가 증가할 수 있음.</li>
</ul>
<p>2) <strong>JS 안에 스타일이 들어오므로 파일이 길어질 수 있음</strong></p>
<ul>
<li>취향에 따라 가독성이 떨어진다고 느끼는 사람도 있음.</li>
</ul>
<p>3) <strong>모든 컴포넌트가 styled 컴포넌트가 되면 요소 구조 파악이 어려울 수 있음</strong></p>
<ul>
<li>개발자 도구에서 <code>&lt;label class=&quot;sc-hvigdm eEJnQf&quot;&gt;&lt;/label&gt;</code> 같은 형태로 보임.</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>