<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>jungo_0</title>
        <link>https://velog.io/</link>
        <description>열심히 기록할 예정🙃</description>
        <lastBuildDate>Thu, 16 Apr 2026 01:19:23 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>jungo_0</title>
            <url>https://velog.velcdn.com/images/jungo_0/profile/7ec3a889-5948-40c6-b3d3-3af69edf771d/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. jungo_0. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/jungo_0" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Next.js] Server Actions, formData, useFormState]]></title>
            <link>https://velog.io/@jungo_0/Next.js-Server-Actions-formData-useFormState</link>
            <guid>https://velog.io/@jungo_0/Next.js-Server-Actions-formData-useFormState</guid>
            <pubDate>Thu, 16 Apr 2026 01:19:23 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/jungo_0/post/0b366307-f3d7-4564-81bc-bb7b8b679bed/image.png" alt=""></p>
<h1 id="nextjs-app-router-핵심-정리-server-actions-formdata-useformstate">[Next.js] App Router 핵심 정리: Server Actions, formData, useFormState</h1>
<p>Next.js의 App Router 환경에서 데이터를 주고받는 방식은 기존 리액트 방식과는 완전히 다릅니다. 핵심이 되는 세 가지 개념을 실제 코드 예시와 함께 정리합니다.</p>
<hr>
<h2 id="1-server-actions-서버-액션">1. Server Actions (서버 액션)</h2>
<p><strong>&quot;백엔드 로직을 프론트엔드 함수처럼 직접 호출하는 기술&quot;</strong></p>
<p>기존에는 데이터를 전송하기 위해 API 엔드포인트를 따로 만들었지만, 서버 액션은 함수 하나로 서버측 로직을 실행합니다.</p>
<ul>
<li><strong>구현:</strong> 함수 내부에 <code>&#39;use server&#39;</code> 지시어를 선언합니다.</li>
<li><strong>장점:</strong> * API URL을 일일이 관리할 필요가 없습니다.<ul>
<li>민감한 DB 로직이 클라이언트에 노출되지 않아 보안에 강력합니다.</li>
<li>자바스크립트가 로드되지 않은 환경에서도 HTML Form 표준에 따라 동작합니다.</li>
</ul>
</li>
</ul>
<hr>
<h2 id="2-formdata-폼-데이터">2. formData (폼 데이터)</h2>
<p><strong>&quot;폼에 담긴 모든 데이터를 한데 묶은 보따리&quot;</strong></p>
<p>서버 액션이 호출될 때 전달되는 인자로, 폼 내부의 모든 입력값을 가지고 있습니다.</p>
<ul>
<li><strong>자동 수집:</strong> 각 <code>input</code> 태그에 <code>name</code> 속성만 적어주면 제출 시점에 데이터를 자동으로 싹 긁어모읍니다.</li>
<li><strong>데이터 추출:</strong> <code>formData.get(&#39;name값&#39;)</code> 메서드로 원하는 데이터를 꺼낼 수 있습니다.</li>
<li><strong>💡 useRef와 차이점:</strong><ul>
<li><strong>useRef (리모컨):</strong> 특정 입력창에 포커스를 주거나 직접 클릭을 발생시키는 등 &#39;조작&#39;이 목적입니다.</li>
<li><strong>formData (보따리):</strong> 입력된 데이터들을 서버로 &#39;전송&#39;하기 위해 묶어주는 것이 목적입니다.</li>
</ul>
</li>
</ul>
<hr>
<h2 id="3-useformstate-폼-상태-관리">3. useFormState (폼 상태 관리)</h2>
<p><strong>&quot;서버의 응답을 UI에 실시간으로 연결하는 통로&quot;</strong></p>
<p>서버 액션이 실행된 후, 서버에서 보내온 결과(성공/실패 메시지 등)를 클라이언트 화면에 보여주기 위해 사용하는 Hook입니다.</p>
<h3 id="실제-구현-예시">실제 구현 예시</h3>
<pre><code class="language-javascript">// 1. 서버 액션 정의 (actions.js)
&#39;use server&#39;;

export async function shareMeal(prevState, formData) {
  const title = formData.get(&#39;title&#39;);

  if (!title) {
    return { message: &#39;제목을 입력해주세요!&#39; }; // 에러 메시지 리턴
  }

  // DB 저장 로직 수행...
  return { message: &#39;성공적으로 저장되었습니다.&#39; };
}

// 2. 클라이언트 컴포넌트 (MealForm.js)
&#39;use client&#39;;
import { useFormState } from &#39;react-dom&#39;;
import { shareMeal } from &#39;./actions&#39;;

export default function MealForm() {
  const [state, formAction] = useFormState(shareMeal, { message: null });

  return (
    &lt;form action={formAction}&gt;
      &lt;input type=&quot;text&quot; name=&quot;title&quot; /&gt;
      {/* 서버에서 보낸 메시지가 state에 담겨 출력됨 */}
      {state.message &amp;&amp; &lt;p&gt;{state.message}&lt;/p&gt;}
      &lt;button type=&quot;submit&quot;&gt;제출&lt;/button&gt;
    &lt;/form&gt;
  );
}</code></pre>
<hr>
<h3 id="4-revalidatepath-데이터-최신화-캐싱">4. revalidatePath: 데이터 최신화 (캐싱)</h3>
<p>Next.js는 성능을 위해 페이지를 캐싱합니다. 데이터가 추가되었는데 화면이 바뀌지 않는다면 캐시 때문입니다.</p>
<ul>
<li><p>역할: revalidatePath(&#39;/meals&#39;)를 호출하면 해당 경로의 기존 캐시를 버리고 새로운 데이터를 다시 가져오도록 명령합니다.</p>
</li>
<li><p>시점: 보통 서버 액션 안에서 DB 업데이트가 성공적으로 끝난 직후에 사용합니다.</p>
</li>
</ul>
<h2 id="5-전체-데이터-흐름-요약">5. 전체 데이터 흐름 요약</h2>
<ol>
<li><p>사용자: 폼 작성 후 제출 클릭.</p>
</li>
<li><p>클라이언트: 모든 데이터가 formData 보따리에 담김.</p>
</li>
<li><p>서버: Server Action 실행, DB 작업 수행.</p>
</li>
<li><p>서버: 작업 결과(메시지 등)를 반환하고 revalidatePath로 캐시 갱신.</p>
</li>
<li><p>클라이언트: useFormState가 리턴받은 결과를 화면에 표시.</p>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js] 데이터 로딩 방식]]></title>
            <link>https://velog.io/@jungo_0/Next.js-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%A1%9C%EB%94%A9-%EB%B0%A9%EC%8B%9D</link>
            <guid>https://velog.io/@jungo_0/Next.js-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%A1%9C%EB%94%A9-%EB%B0%A9%EC%8B%9D</guid>
            <pubDate>Wed, 15 Apr 2026 09:17:29 GMT</pubDate>
            <description><![CDATA[<h2 id="nextjs-서버-컴포넌트에서-데이터베이스-직접-연결하기">[Next.js] 서버 컴포넌트에서 데이터베이스 직접 연결하기</h2>
<blockquote>
<p><code>useEffect</code>와 <code>fetch</code> 없이 서버에서 직접 데이터를 꺼내오는 현대적인 풀스택 로딩 전략을 분석함</p>
</blockquote>
<p><strong>키워드:</strong> Direct DB Access, RSC async/await, lib 폴더 구조, SQLite</p>
<hr>
<h3 id="1-데이터-로딩-방식의-혁신">1. 데이터 로딩 방식의 혁신</h3>
<p>기존 리액트(CSR)는 브라우저가 API 서버를 호출하고 기다리는 복잡한 과정을 거쳤지만, Next.js 서버 컴포넌트는 서버 내부에서 <strong>DB에 직접 접근</strong>합니다.</p>
<table>
<thead>
<tr>
<th align="left">비교 항목</th>
<th align="left">기존 리액트 (CSR)</th>
<th align="left">Next.js 서버 컴포넌트 (RSC)</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>통신 방식</strong></td>
<td align="left">브라우저 → API 서버 → DB</td>
<td align="left"><strong>서버 컴포넌트 → DB (직접)</strong></td>
</tr>
<tr>
<td align="left"><strong>필요한 훅</strong></td>
<td align="left"><code>useState</code>, <code>useEffect</code> 필수</td>
<td align="left"><strong>필요 없음 (async/await 사용)</strong></td>
</tr>
<tr>
<td align="left"><strong>보안</strong></td>
<td align="left">API 엔드포인트 노출 위험</td>
<td align="left"><strong>서버 내부 실행으로 안전함</strong></td>
</tr>
</tbody></table>
<hr>
<h3 id="2-db-로직-분리-libmealsjs">2. DB 로직 분리 (<code>lib/meals.js</code>)</h3>
<p>데이터베이스 연결과 쿼리 로직은 별도의 <code>lib</code> 폴더에서 관리하여 코드의 가독성과 재사용성을 높입니다.</p>
<pre><code class="language-javascript">import sql from &#39;better-sqlite3&#39;;

const db = sql(&#39;meals.db&#39;);

export async function getMeals() {
  // 로딩 상태 확인을 위해 의도적인 지연(Delay) 추가
  await new Promise((resolve) =&gt; setTimeout(resolve, 2000));

  // .all()은 모든 행을 배열로 가져옴
  return db.prepare(&#39;SELECT * FROM meals&#39;).all();
}</code></pre>
<hr>
<h3 id="3-컴포넌트에-asyncawait를-직접-쓰는-이유">3. 컴포넌트에 async/await를 직접 쓰는 이유</h3>
<p>가장 많은 입문자가 헷갈리는 부분입니다. 왜 일반 리액트 컴포넌트는 async가 안 되고, 서버 컴포넌트는 될까요?</p>
<h3 id="①-일반-리액트csr의-한계">① 일반 리액트(CSR)의 한계</h3>
<p>일반적인 클라이언트 컴포넌트는 <strong>&quot;즉시 UI(JSX)를 반환해야 하는 함수&quot;</strong>입니다</p>
<ul>
<li><p>문제점: async를 붙이면 함수가 JSX가 아닌 Promise를 반환합니다.</p>
</li>
<li><p>결과: 리액트 엔진은 화면에 그릴 HTML이 필요한데 &#39;나중에 줄게&#39;라는 약속(Promise)을 받으면 에러를 냅니다. 그래서 예전엔 useEffect 안에서 데이터를 따로 관리했습니다.</p>
</li>
</ul>
<h3 id="②-서버-컴포넌트rsc의-마법">② 서버 컴포넌트(RSC)의 마법</h3>
<p>서버 컴포넌트는 브라우저가 아닌 <strong>서버(백엔드 환경)</strong>에서 실행됩니다.</p>
<ul>
<li>기다림의 미학: 서버는 &quot;데이터 가져오는 데 2초 걸려? 오케이, 다 가져올 때까지 내가 기다렸다가(await) 완성된 HTML을 통째로 구워서 보내줄게&quot;라고 할 수 있는 능력이 있습니다.</li>
</ul>
<h3 id="데이터-로딩-프로세스-변화">데이터 로딩 프로세스 변화</h3>
<ol>
<li>서버 진입: 요청이 오면 서버 컴포넌트 함수가 실행됨.</li>
<li>비동기 대기: await getMeals() 라인에서 DB 응답이 올 때까지 함수 실행을 잠시 멈춤.</li>
<li>HTML 생성: 데이터가 도착하면 그 데이터를 버무려 최종 HTML을 완성함.</li>
<li>전송: 브라우저는 자바스크립트가 실행되기 전부터 이미 데이터가 채워진 HTML을 받게 됨.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js] 서버 컴포넌트, 클라이언트 컴포넌트]]></title>
            <link>https://velog.io/@jungo_0/Next.js-%EC%84%9C%EB%B2%84-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8</link>
            <guid>https://velog.io/@jungo_0/Next.js-%EC%84%9C%EB%B2%84-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8</guid>
            <pubDate>Wed, 15 Apr 2026 07:29:27 GMT</pubDate>
            <description><![CDATA[<h2 id="nextjs-레이아웃layoutjs과-메타데이터metadata의-이해">[Next.js] 레이아웃(layout.js)과 메타데이터(Metadata)의 이해</h2>
<blockquote>
<p><code>page.js</code>가 각 페이지의 구체적인 내용이라면, <code>layout.js</code>는 이를 감싸는 공통의 뼈대와 환경 설정을 담당함</p>
</blockquote>
<p><strong>키워드:</strong> Root Layout, Children Props, Nested Layout, Metadata SEO</p>
<hr>
<h3 id="1-layoutjs의-정의와-역할">1. layout.js의 정의와 역할</h3>
<p><code>layout.js</code>는 하나 이상의 페이지를 감싸는 공통의 <strong>껍데기(Shell)</strong>입니다. 헤더, 푸터, 내비게이션 바처럼 모든 페이지에서 반복되는 요소들을 한 곳에서 관리합니다.</p>
<h3 id="2-루트-레이아웃root-layout의-필수성">2. 루트 레이아웃(Root Layout)의 필수성</h3>
<p><code>app</code> 폴더 최상위의 <code>layout.js</code>는 애플리케이션의 근본입니다. 리액트 컴포넌트임에도 불구하고 <code>&lt;html&gt;</code>과 <code>&lt;body&gt;</code> 태그를 직접 렌더링해야 하는 필수 파일입니다.</p>
<h3 id="3-children-속성과-페이지-삽입">3. children 속성과 페이지 삽입</h3>
<p>레이아웃은 리액트의 표준 속성인 <strong><code>children</code></strong>을 통해 내용을 전달받습니다.</p>
<ul>
<li><strong>동작</strong>: 사용자가 접속한 경로의 <code>page.js</code>가 레이아웃의 <code>{children}</code> 자리로 자동 조립됩니다.</li>
</ul>
<hr>
<h3 id="4-중첩-레이아웃-nested-layout">4. 중첩 레이아웃 (Nested Layout)</h3>
<p>특정 하위 폴더에도 레이아웃을 추가할 수 있으며, 이는 해당 섹션(예: 관리자 페이지, 마이페이지)에만 특화된 디자인을 적용할 때 유용합니다.</p>
<h3 id="5-메타데이터metadata와-seo">5. 메타데이터(Metadata)와 SEO</h3>
<p>Next.js에서는 <code>&lt;head&gt;</code> 태그를 직접 쓰지 않고 약속된 <strong><code>metadata</code></strong> 객체를 사용합니다.</p>
<ul>
<li><strong>자동 처리</strong>: <code>export const metadata = { title: &#39;...&#39;, description: &#39;...&#39; }</code>라고 정의하면 Next.js가 이면에서 메타 태그를 생성합니다.</li>
<li><strong>상속</strong>: 상위 레이아웃의 메타데이터는 하위 페이지로 자연스럽게 이어집니다.</li>
</ul>
<hr>
<h1 id="19-nextjs-서버-컴포넌트rsc-vs-클라이언트-컴포넌트rcc">19. [Next.js] 서버 컴포넌트(RSC) vs 클라이언트 컴포넌트(RCC)</h1>
<blockquote>
<p>코드가 서버(Node.js)에서 실행되는지, 브라우저(클라이언트)에서 실행되는지에 따라 성능과 기능이 결정됨</p>
</blockquote>
<hr>
<h3 id="1-리액트-서버-컴포넌트-react-server-components-rsc">1. 리액트 서버 컴포넌트 (React Server Components, RSC)</h3>
<p>Next.js의 모든 컴포넌트는 별도 설정이 없다면 <strong>기본적으로 서버 컴포넌트</strong>입니다.</p>
<ul>
<li><strong>실행 위치</strong>: 브라우저가 아닌 <strong>백엔드 서버</strong>에서 실행됩니다.</li>
<li><strong>장점</strong>: <ul>
<li>자바스크립트 번들 크기가 줄어들어 성능 향상.</li>
<li>완성된 HTML을 보내주므로 <strong>SEO(검색 엔진 최적화)</strong>에 매우 유리.</li>
</ul>
</li>
<li><strong>특징</strong>: <code>console.log</code>가 브라우저가 아닌 <strong>터미널</strong>에 찍힙니다.</li>
</ul>
<hr>
<h3 id="2-클라이언트-컴포넌트-client-components-rcc">2. 클라이언트 컴포넌트 (Client Components, RCC)</h3>
<p>브라우저 전용 기능이나 사용자와의 실시간 상호작용이 필요한 경우 사용합니다.</p>
<ul>
<li><strong>사용법</strong>: 파일 최상단에 <strong><code>&#39;use client&#39;;</code></strong> 지시어를 추가합니다.</li>
<li><strong>언제 사용하나?</strong><ol>
<li><strong>상태 및 생명주기</strong>: <code>useState</code>, <code>useEffect</code> 사용 시.</li>
<li><strong>이벤트</strong>: <code>onClick</code>, <code>onChange</code> 핸들러 사용 시.</li>
<li><strong>브라우저 API</strong>: <code>window</code>, <code>localStorage</code> 접근 시.</li>
</ol>
</li>
</ul>
<hr>
<h3 id="3-왜-기본이-서버-컴포넌트일까-비교">3. 왜 기본이 서버 컴포넌트일까? (비교)</h3>
<table>
<thead>
<tr>
<th align="left">구분</th>
<th align="left">일반 리액트 (CSR)</th>
<th align="left">Next.js 서버 컴포넌트 (RSC)</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>초기 HTML</strong></td>
<td align="left">빈 껍데기 (<code>div id=&quot;root&quot;</code>)</td>
<td align="left"><strong>모든 컨텐츠가 채워진 HTML</strong></td>
</tr>
<tr>
<td align="left"><strong>JS 로드량</strong></td>
<td align="left">전체 로직 다운로드 (무거움)</td>
<td align="left">필요한 최소 코드만 전송 (가벼움)</td>
</tr>
<tr>
<td align="left"><strong>SEO</strong></td>
<td align="left">불리함</td>
<td align="left"><strong>매우 유리함</strong></td>
</tr>
</tbody></table>
<hr>
<h3 id="4-실전-예시-imageslideshow-컴포넌트">4. 실전 예시: ImageSlideshow 컴포넌트</h3>
<p>슬라이드쇼처럼 <code>setInterval</code>이나 <code>useState</code>를 써서 실시간으로 화면을 바꿔야 하는 경우, 서버는 브라우저의 미래 상황을 예측할 수 없으므로 반드시 <strong>클라이언트 컴포넌트</strong>로 작성해야 합니다.</p>
<p>```javascript
&#39;use client&#39;; // 이 컴포넌트는 이제 브라우저에서 생동감 있게 움직입니다.</p>
<p>import { useEffect, useState } from &#39;react&#39;;
// ... 5초마다 이미지를 바꾸는 로직 (브라우저 메모리와 타이머 사용)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js] Next.js 기초]]></title>
            <link>https://velog.io/@jungo_0/Next.js-%EA%B8%B0%EC%B4%88</link>
            <guid>https://velog.io/@jungo_0/Next.js-%EA%B8%B0%EC%B4%88</guid>
            <pubDate>Wed, 15 Apr 2026 07:15:52 GMT</pubDate>
            <description><![CDATA[<h2 id="nextjs-풀스택-프레임워크의-정의와-서버-컴포넌트의-동작-원리">[Next.js] 풀스택 프레임워크의 정의와 서버 컴포넌트의 동작 원리</h2>
<blockquote>
<p>리액트를 넘어선 표준 프레임워크 Next.js의 핵심인 &#39;보호된 파일명&#39;과 서버 중심의 렌더링 설계를 분석함</p>
</blockquote>
<p><strong>키워드:</strong> Full-stack Framework, File-based Routing, RSC(React Server Components), Prefetching</p>
<hr>
<h3 id="1-nextjs의-정의와-특징">1. Next.js의 정의와 특징</h3>
<p>Next.js는 리액트(React)를 기반으로 하는 <strong>풀스택 프레임워크</strong>입니다. 리액트가 UI 라이브러리로서 기능을 제공한다면, Next.js는 라우팅, 서버 사이드 렌더링, API 구축 등 웹 서비스에 필요한 모든 도구를 종합적으로 제공합니다.</p>
<p>❖ <strong>핵심 특징:</strong></p>
<ul>
<li><strong>파일 기반 라우팅</strong>: 특정 폴더 구조와 파일명만으로 자동으로 URL 경로를 생성함.</li>
<li><strong>서버 컴포넌트 기본값</strong>: 모든 컴포넌트가 별도 설정 없이도 서버에서 실행되는 구조를 가짐.</li>
<li><strong>성능 자동 최적화</strong>: 이미지, 폰트 최적화 및 코드 분할(Code Splitting)을 자동으로 처리함.</li>
</ul>
<hr>
<h3 id="2-보호된-파일명-pagejs">2. 보호된 파일명: <code>page.js</code></h3>
<p>Next.js 프로젝트에서는 파일명이 곧 기능이 되는 <strong>약속된 규칙</strong>이 존재합니다. 그중 가장 핵심이 되는 것이 <code>page.js</code>입니다.</p>
<ul>
<li><strong>역할</strong>: 해당 경로(URL)에서 실제로 브라우저에 렌더링될 리액트 컴포넌트를 정의함.</li>
<li><strong>규칙</strong>: 반드시 해당 파일 내에서 컴포넌트를 <code>export</code> 해야만 Next.js 엔진이 인지하고 화면을 구성함.</li>
</ul>
<hr>
<h3 id="3-리액트-서버-컴포넌트-react-server-components">3. 리액트 서버 컴포넌트 (React Server Components)</h3>
<p>Next.js 환경의 컴포넌트는 기본적으로 <strong>서버 컴포넌트</strong>로 취급됩니다. 이는 컴포넌트가 브라우저가 아닌 백엔드 서버에서 실행됨을 의미합니다.</p>
<h4 id="-서버-컴포넌트-실행-프로세스"><strong>### 서버 컴포넌트 실행 프로세스</strong></h4>
<blockquote>
<ol>
<li><strong>서버 실행</strong>: 사용자가 페이지를 요청하면 서버에서 컴포넌트 함수를 실행함.</li>
<li><strong>로그 출력</strong>: 컴포넌트 내부의 <code>console.log</code>는 브라우저 콘솔이 아닌 <strong>서버 터미널</strong>에 출력됨.</li>
<li><strong>HTML 전송</strong>: 계산이 끝난 완성된 HTML 구조만 브라우저로 전송함.</li>
<li><strong>클라이언트 부담 감소</strong>: 브라우저가 내려받아야 할 자바스크립트 번들 크기가 획기적으로 줄어듦.</li>
</ol>
</blockquote>
<hr>
<h3 id="4-link-컴포넌트-spa의-부드러움과-seo의-결합">4. Link 컴포넌트: SPA의 부드러움과 SEO의 결합</h3>
<p>Next.js는 초기 방문 시에는 서버에서 완성된 페이지를 보여주고, 이후 이동 시에는 필요한 데이터만 교체하는 하이브리드 방식을 취합니다.</p>
<ul>
<li><strong><code>&lt;a&gt;</code> 태그의 한계</strong>: 전체 페이지를 다시 다운로드하여 새로고침(깜빡임)이 발생하고 SPA의 장점이 사라짐.</li>
<li><strong><code>&lt;Link&gt;</code> 컴포넌트</strong>: 브라우저의 기본 이동 동작을 막고 자바스크립트로 화면 내용만 교체함.</li>
</ul>
<h4 id="-성능의-핵심-프리페칭prefetching"><strong>### 성능의 핵심: 프리페칭(Prefetching)</strong></h4>
<p>Next.js의 <code>&lt;Link&gt;</code>는 단순히 이동만 돕지 않습니다. 링크가 사용자의 화면(뷰포트)에 들어오는 순간, Next.js는 해당 페이지의 데이터를 <strong>미리 로드</strong>해둡니다.</p>
<p><span style="background-color:#5A5AFF">사용자가 링크를 클릭하기도 전에 데이터를 준비해두기 때문에, 클릭하는 순간 즉각적인 전환이 일어나는 압도적인 반응성을 제공합니다.</span></p>
<hr>
<h3 id="요약-및-결론">요약 및 결론</h3>
<table>
<thead>
<tr>
<th align="left">구분</th>
<th align="left">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>서버 컴포넌트</strong></td>
<td align="left">백엔드에서 실행되어 초기 로딩 속도와 보안 향상</td>
</tr>
<tr>
<td align="left"><strong>page.js</strong></td>
<td align="left">경로를 결정짓는 핵심 파일 (보호된 파일명)</td>
</tr>
<tr>
<td align="left"><strong>Link 컴포넌트</strong></td>
<td align="left">새로고침 없는 SPA 이동과 프리페칭을 통한 초고속 전환</td>
</tr>
</tbody></table>
<hr>
<h3 id="결론"><strong>결론</strong></h3>
<p>Next.js를 사용한다는 것은 단순히 리액트를 서버에서 돌리는 것을 넘어, <strong>최적의 사용자 경험을 위한 설계 시스템</strong>을 도입하는 것입니다. 서버 컴포넌트와 Link 컴포넌트의 조화는 현대 웹이 추구하는 속도와 SEO 두 마리 토끼를 모두 잡는 가장 우아한 방법입니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 테스팅 심화]]></title>
            <link>https://velog.io/@jungo_0/React-%ED%85%8C%EC%8A%A4%ED%8C%85-%EC%8B%AC%ED%99%94</link>
            <guid>https://velog.io/@jungo_0/React-%ED%85%8C%EC%8A%A4%ED%8C%85-%EC%8B%AC%ED%99%94</guid>
            <pubDate>Tue, 14 Apr 2026 10:58:00 GMT</pubDate>
            <description><![CDATA[<h2 id="리액트-테스팅-완벽-가이드-기초부터-비동기-mocking까지">리액트 테스팅 완벽 가이드: 기초부터 비동기 Mocking까지</h2>
<blockquote>
<p>&quot;사용자가 소프트웨어를 사용하는 방식과 유사하게 테스트하라&quot;는 철학 아래, 컴포넌트의 렌더링부터 복잡한 비동기 로직까지 검증하는 실전 테스팅 기법임</p>
</blockquote>
<p><strong>키워드:</strong> RTL(React Testing Library), AAA 패턴, User Event, Mocking(jest.fn), Async Testing</p>
<hr>
<h3 id="1-리액트-테스팅의-핵심-철학">1. 리액트 테스팅의 핵심 철학</h3>
<p>리액트 테스팅 라이브러리(RTL)의 최우선 가치는 내부 구현(State, Props 명칭 등)이 아닌 <strong>사용자의 관점</strong>을 검증하는 것입니다. </p>
<p>❖ <strong>핵심 원칙:</strong></p>
<ul>
<li>내부 상태값이 무엇인지보다 <strong>화면에 무엇이 보이고, 어떤 상호작용이 일어나는지</strong>가 중요함.</li>
<li>코드를 리팩토링해도 사용자 경험이 같다면 테스트는 통과해야 함.</li>
</ul>
<hr>
<h3 id="2-테스트의-기본-구조-aaa-패턴">2. 테스트의 기본 구조: AAA 패턴</h3>
<p>모든 테스트 코드는 가독성과 유지보수를 위해 3단계 구조를 따릅니다.</p>
<ol>
<li><strong>Arrange (준비)</strong>: 테스트할 컴포넌트를 <code>render()</code>로 가상 DOM에 띄움.</li>
<li><strong>Act (실행)</strong>: 클릭, 타이핑 등 사용자의 행동을 수행 (<code>userEvent</code> 활용).</li>
<li><strong>Assert (단언)</strong>: 결과가 예상과 일치하는지 <code>expect()</code>로 확인.</li>
</ol>
<hr>
<h3 id="3-단계별-실습-및-메서드-분석">3. 단계별 실습 및 메서드 분석</h3>
<h4 id="①-기초-정적-컴포넌트-테스트-getbytext"><strong>① 기초: 정적 컴포넌트 테스트 (<code>getByText</code>)</strong></h4>
<p>가장 기본적인 텍스트 렌더링 확인 단계입니다. <code>describe</code>로 관련 테스트를 그룹화합니다.</p>
<pre><code class="language-javascript">describe(&#39;Greeting component&#39;, () =&gt; {
  test(&#39;renders &quot;Hello World&quot; as a text&#39;, () =&gt; {
    render(&lt;Greeting /&gt;); // Arrange
    const helloWorldElement = screen.getByText(&#39;Hello World!&#39;); // Act &amp; Assert
    expect(helloWorldElement).toBeInTheDocument();
  });
});
</code></pre>
<h4 id="②-중급-이벤트와-상태-변경-getbyrole--userevent"><strong>② 중급: 이벤트와 상태 변경 (<code>getByRole</code> &amp; <code>userEvent</code>)</strong></h4>
<p>버튼 클릭 시 UI가 변하는지 테스트합니다. <code>userEvent</code>는 실제 브라우저의 상호작용(포커스, 클릭 등)을 더 정밀하게 흉내 냅니다.</p>
<pre><code class="language-javascript">test(&#39;버튼 클릭 시 &quot;Changed!&quot; 문구가 나타나는지 확인&#39;, () =&gt; {
  render(&lt;Greeting /&gt;);

  // 1. 역할(Role)로 버튼 찾기
  const buttonElement = screen.getByRole(&#39;button&#39;);

  // 2. 사용자의 실제 클릭 동작 수행
  userEvent.click(buttonElement); 

  // 3. 변경된 UI 검증
  const outputElement = screen.getByText(&#39;Changed!&#39;);
  expect(outputElement).toBeInTheDocument();
});</code></pre>
<h4 id="③-고급-요소의-부재-확인-queryby"><strong>③ 고급: 요소의 부재 확인 (<code>queryBy</code>)</strong></h4>
<p>특정 요소가 <strong>화면에 없어야 함</strong>을 검증할 때는 <code>getBy</code>를 쓰면 안 됩니다. 요소를 찾지 못하는 순간 에러를 던져 테스트가 중단되기 때문입니다.</p>
<ul>
<li><strong><code>queryByText</code></strong>: 요소를 찾지 못하면 에러 대신 <code>null</code>을 반환합니다.</li>
<li><strong>활용</strong>: <code>expect(...).toBeNull()</code>과 함께 사용하여 특정 UI가 사라졌음을 안전하게 확인합니다.</li>
</ul>
<hr>
<h4 id="④-심화-비동기-데이터와-모킹-mocking"><strong>④ 심화: 비동기 데이터와 모킹 (<code>Mocking</code>)</strong></h4>
<p>API 호출처럼 시간이 걸리는 작업은 실제 네트워크를 타지 않도록 <code>jest.fn()</code>으로 가짜 함수를 만들어 대체(Mocking)합니다.</p>
<pre><code class="language-js">test(&#39;성공 시 포스트 목록을 렌더링함&#39;, async () =&gt; {
  // 1. Mocking: 실제 fetch를 가짜 함수로 대체하여 외부 의존성 제거
  window.fetch = jest.fn();
  window.fetch.mockResolvedValueOnce({
    json: async () =&gt; [{ id: &#39;p1&#39;, title: &#39;First post&#39; }],
  });

  render(&lt;Async /&gt;);

  // 2. 비동기 대기: findAllByRole
  // find로 시작하는 메서드는 요소가 나타날 때까지 기다리며 Promise를 반환함
  const listItemElements = await screen.findAllByRole(&#39;listitem&#39;);

  // 3. 결과 검증
  expect(listItemElements).not.toHaveLength(0);
});
</code></pre>
<hr>
<h3 id="-테스팅-메서드-선정-가이드"><strong>### 테스팅 메서드 선정 가이드</strong></h3>
<p>상황에 맞는 쿼리(Query)를 선택하는 것이 테스트의 견고함을 결정합니다. 단순히 요소를 찾는 것을 넘어, 테스트의 의도에 맞는 접두사를 사용하는 것이 중요합니다.</p>
<table>
<thead>
<tr>
<th align="left">메서드 접두사</th>
<th align="left">특징</th>
<th align="left">용도</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>getBy...</strong></td>
<td align="left">요소를 즉시 찾음. 없으면 즉시 에러 발생</td>
<td align="left">일반적인 요소 존재 확인 (가장 기본적이고 많이 쓰임)</td>
</tr>
<tr>
<td align="left"><strong>queryBy...</strong></td>
<td align="left">요소를 찾지 못하면 에러 대신 <code>null</code> 반환</td>
<td align="left">특정 요소가 <strong>존재하지 않음</strong>을 안전하게 확인할 때</td>
</tr>
<tr>
<td align="left"><strong>findBy...</strong></td>
<td align="left">요소를 찾을 때까지 <strong>기다림</strong> (Promise 반환)</td>
<td align="left"><strong>비동기</strong> 데이터 로딩 후 화면에 나타나는 요소를 확인할 때</td>
</tr>
</tbody></table>
<hr>
<h4 id="💡-쿼리-선택의-팁"><strong>💡 쿼리 선택의 팁</strong></h4>
<ol>
<li><strong>우선순위</strong>: 가능하면 <code>getByRole</code>을 최우선으로 사용하세요. 이는 시각 장애인이 스크린 리더를 사용하는 방식과 유사하여 접근성까지 함께 테스트할 수 있습니다.</li>
<li><strong>에러 메시지</strong>: <code>getBy</code>는 요소를 찾지 못할 때 전체 DOM 구조를 에러 메시지로 보여주기 때문에 디버깅에 매우 유리합니다.</li>
<li><strong>비동기 처리</strong>: <code>findBy</code>는 기본적으로 1000ms(1초) 동안 요소를 기다립니다. API 응답 속도에 따라 이 시간은 조절 가능합니다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 리액트 테스팅 ]]></title>
            <link>https://velog.io/@jungo_0/React-%ED%85%8C%EC%8A%A4%ED%8C%85</link>
            <guid>https://velog.io/@jungo_0/React-%ED%85%8C%EC%8A%A4%ED%8C%85</guid>
            <pubDate>Tue, 14 Apr 2026 10:24:02 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/jungo_0/post/701539d0-bf04-4f1b-85f8-eb0eef3c0739/image.png" alt=""></p>
<h2 id="리액트-테스팅-기초-코드가-코드를-검증하는-법">리액트 테스팅 기초: 코드가 코드를 검증하는 법</h2>
<blockquote>
<p>수동 테스트의 한계를 극복하고, 앱이 커져도 안정성을 유지할 수 있도록 테스트 코드를 작성하는 자동화 테스팅의 핵심 원리임</p>
</blockquote>
<p><strong>키워드:</strong> Unit/Integration/E2E Test, Jest, React Testing Library(RTL), AAA 패턴</p>
<hr>
<h3 id="1-수동-테스트의-한계와-자동화의-필요성">1. 수동 테스트의 한계와 자동화의 필요성</h3>
<p>우리는 보통 브라우저에서 직접 클릭하며 기능을 확인하는 <strong>수동 테스트</strong>를 진행합니다. 하지만 서비스가 커지면 다음과 같은 치명적인 문제가 발생합니다.</p>
<p>❖ <strong>수동 테스트의 문제점:</strong></p>
<ul>
<li><strong>회귀 버그(Regression Bug)</strong>: 새 기능을 넣었을 때 기존 기능이 망가지는 것을 놓침.</li>
<li><strong>심리적 부담</strong>: &quot;이거 수정하면 다른 데 터지는 거 아냐?&quot;라는 두려움 발생.</li>
<li><strong>비효율성</strong>: 복잡한 앱을 매번 전수 조사하는 것은 불가능함.</li>
</ul>
<p><span style="background-color:#5A5AFF">자동화 테스팅은 수동 테스트를 대신하는 것이 아니라, 코드가 코드를 검사하게 함으로써 개발자의 확신을 더해주는 도구입니다.</span></p>
<hr>
<h3 id="2-테스팅의-3가지-유형">2. 테스팅의 3가지 유형</h3>
<p>자동화 테스트는 검사하는 범위에 따라 세 단계로 구분됩니다.</p>
<table>
<thead>
<tr>
<th align="left">유형</th>
<th align="left">테스트 범위</th>
<th align="left">특징</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>단위 테스트 (Unit)</strong></td>
<td align="left">함수, 개별 컴포넌트</td>
<td align="left">가장 작고 빠르며, 로직의 정확성을 검증함</td>
</tr>
<tr>
<td align="left"><strong>통합 테스트 (Integration)</strong></td>
<td align="left">여러 컴포넌트의 협력</td>
<td align="left">컴포넌트끼리 데이터를 잘 주고받는지 확인함</td>
</tr>
<tr>
<td align="left"><strong>E2E 테스트</strong></td>
<td align="left">앱 전체 흐름 (End-to-End)</td>
<td align="left">실제 사용자처럼 로그인부터 결제까지 시뮬레이션함</td>
</tr>
</tbody></table>
<hr>
<h3 id="3-테스팅-도구의-역할-jest--rtl">3. 테스팅 도구의 역할: Jest &amp; RTL</h3>
<p>리액트 앱은 보통 두 가지 도구의 조합으로 테스트를 진행합니다.</p>
<ul>
<li><strong>Jest (심판)</strong>: 테스트 코드를 실행하고 결과가 성공인지 실패인지 판단하는 테스트 러너입니다.</li>
<li><strong>React Testing Library (시뮬레이터)</strong>: 가상의 브라우저(JSDOM)에 컴포넌트를 렌더링하고 사용자의 행동을 흉내 냅니다.</li>
</ul>
<hr>
<h3 id="4-실전-코드-분석-및-aaa-패턴">4. 실전 코드 분석 및 AAA 패턴</h3>
<p>테스트 코드는 <strong>Arrange(준비), Act(행동), Assert(단언)</strong>라는 3단계 법칙을 따를 때 가장 명확합니다.</p>
<pre><code class="language-js">import { render, screen } from &#39;@testing-library/react&#39;;
import App from &#39;./App&#39;;

test(&#39;리액트 학습 링크가 렌더링되는지 확인&#39;, () =&gt; {
  // 1. Arrange (준비): 테스트할 컴포넌트를 가상 DOM에 띄움
  render(&lt;App /&gt;);

  // 2. Act (행동): 필요한 요소를 찾음 (/i는 대소문자 무시)
  const linkElement = screen.getByText(/learn react/i);

  // 3. Assert (단언): 결과가 맞는지 확인 (문서에 존재하는가?)
  expect(linkElement).toBeInTheDocument();
});
</code></pre>
<h3 id="테스트-실행-흐름-요약">테스트 실행 흐름 요약</h3>
<p>가상 렌더링: render() 함수가 실제 브라우저 없이도 메모리 상에 HTML 구조를 생성함.</p>
<p>요소 탐색: screen.getByText를 통해 사용자가 눈으로 보는 텍스트를 기준으로 요소를 찾음.</p>
<p>검증 실행: expect(...).toBeInTheDocument()가 심판 역할을 하며 테스트 통과 여부를 결정함.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React]데이터 변경 Action과 데이터 관리  ]]></title>
            <link>https://velog.io/@jungo_0/%EB%B0%B1%EC%97%94%EB%93%9C%EB%A1%9C-%EB%B3%B4%EB%82%B4%EA%B8%B0</link>
            <guid>https://velog.io/@jungo_0/%EB%B0%B1%EC%97%94%EB%93%9C%EB%A1%9C-%EB%B3%B4%EB%82%B4%EA%B8%B0</guid>
            <pubDate>Mon, 13 Apr 2026 07:25:38 GMT</pubDate>
            <description><![CDATA[<h2 id="데이터-변경의-핵심-action과-데이터-관리-전략">데이터 변경의 핵심: Action과 데이터 관리 전략</h2>
<blockquote>
<p><code>loader</code>가 데이터를 가져오는(Read) 역할이었다면, <code>action</code>은 데이터를 보내고 조작하는(Create, Update, Delete) 핵심 도구임</p>
</blockquote>
<p><strong>키워드:</strong> <code>action</code>, <code>useLoaderData</code>, <code>useRouteLoaderData</code>, <code>throw json()</code>, <code>useRouteError</code></p>
<hr>
<h3 id="1-loader-화면이-뜨기-전에-데이터를-가져온다">1. loader(): 화면이 뜨기 전에 데이터를 가져온다</h3>
<p>기본 리액트 방식(<code>useEffect</code>)은 컴포넌트가 렌더링된 후에 데이터를 가져오지만, <code>loader</code>는 페이지 이동이 완료되기 전에 데이터를 미리 가져옵니다.</p>
<p>❖ <strong>장점 및 특징:</strong></p>
<ul>
<li><strong>UX 향상</strong>: 빈 화면이나 로딩 스피너를 보는 시간을 줄이고, 완성된 페이지를 바로 보여줍니다.</li>
<li><strong>자동 처리</strong>: <code>loader</code>가 <code>response</code> 객체를 그대로 리턴하면, 리액트 라우터가 내부적으로 <code>.json()</code> 처리를 수행해줍니다.</li>
</ul>
<hr>
<h3 id="2-useloaderdata--userouteloaderdata">2. useLoaderData &amp; useRouteLoaderData</h3>
<p>가져온 데이터를 컴포넌트에서 꺼내 쓰는 방법입니다.</p>
<ul>
<li><strong><code>useLoaderData</code></strong>: 현재 라우트에 설정된 <code>loader</code> 데이터를 직접 가져옵니다.</li>
<li><strong><code>useRouteLoaderData</code></strong>: 부모 라우트의 데이터를 공유받을 때 사용합니다. 라우트 설정에 <code>id</code>를 부여하고, 그 <code>id</code>를 열쇠 삼아 자식 페이지(상세, 수정 등)들이 데이터를 공유합니다.</li>
</ul>
<hr>
<h3 id="3-동적-라우트와-파라미터-params">3. 동적 라우트와 파라미터 (params)</h3>
<p>URL에 <code>:eventId</code>와 같은 변수가 포함된 경우, <code>loader</code> 함수는 자동으로 <code>params</code> 객체를 인자로 받습니다.</p>
<p>❖ <strong>차이점:</strong></p>
<ul>
<li>컴포넌트 외부에서 실행되므로 <code>useParams</code> 훅을 사용할 수 없습니다.</li>
<li>대신 <code>loader({ params })</code> 형태를 활용하여 어떤 데이터를 가져올지 API 주소를 동적으로 생성합니다.</li>
</ul>
<hr>
<h3 id="4-throw-json을-이용한-우아한-에러-처리">4. throw json()을 이용한 우아한 에러 처리</h3>
<p>데이터 로딩 중 404나 500 에러가 발생하면 리액트 라우터의 방식으로 에러를 &quot;던집니다(throw)&quot;.</p>
<ul>
<li><strong><code>json()</code> 유틸리티</strong>: <code>JSON.stringify</code>를 직접 쓸 필요 없이 객체와 상태 코드를 한 번에 던질 수 있게 해줍니다.</li>
<li><strong><code>useRouteError</code></strong>: 던져진 에러는 공통 <code>ErrorPage</code>에서 이 훅으로 낚아채서 상황에 맞는 메시지(404: 페이지 없음, 500: 서버 에러 등)를 보여줍니다.</li>
</ul>
<hr>
<h3 id="5-데이터-재사용을-위한-중첩-라우트-설계">5. 데이터 재사용을 위한 중첩 라우트 설계</h3>
<p>동일한 데이터가 필요한 여러 페이지(상세 보기, 수정 페이지)를 하나의 부모 라우트로 묶어 관리합니다.</p>
<p>❖ <strong>최적화 원리:</strong></p>
<ul>
<li>부모 라우트에 <code>id</code>와 <code>loader</code>를 설정합니다.</li>
<li>하위의 모든 자식들은 추가적인 API 호출 없이 부모의 데이터를 재사용할 수 있어 성능과 유지보수에 매우 유리합니다.</li>
</ul>
<hr>
<h4 id="-action-작동-프로세스-cud"><strong>### Action 작동 프로세스 (CUD)</strong></h4>
<blockquote>
<ol>
<li><strong>양식 제출</strong>: 사용자가 <code>&lt;Form&gt;</code>을 제출하면 브라우저의 기본 동작을 막고 라우터의 <code>action</code>이 가동됨.</li>
<li><strong>데이터 처리</strong>: <code>action</code> 함수가 <code>request</code> 객체에서 폼 데이터를 뽑아내 서버로 <code>POST/PATCH/DELETE</code> 요청을 보냄.</li>
<li><strong>자동 갱신</strong>: 서버 작업이 끝나면 리액트 라우터가 현재 페이지의 <code>loader</code>를 다시 실행하여 화면을 최신 상태로 업데이트함.</li>
<li><strong>이동</strong>: 작업 완료 후 <code>redirect()</code>를 통해 목록 페이지 등으로 사용자를 안내함.</li>
</ol>
</blockquote>
<hr>
<h3 id="요약-및-결론">요약 및 결론</h3>
<p>리액트 라우터의 <code>loader</code>와 <code>action</code>을 활용하면 컴포넌트 내부 로직이 획기적으로 줄어듭니다.</p>
<p><span style="background-color:#5A5AFF">&quot;읽기는 loader, 쓰기는 action&quot;. 이 두 축을 중심으로 라우트를 설계하면 데이터의 흐름이 명확해지고 앱의 반응 속도가 비약적으로 향상됩니다.</span></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 리액트 라우터 loader]]></title>
            <link>https://velog.io/@jungo_0/React-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%9D%BC%EC%9A%B0%ED%84%B0-loader</link>
            <guid>https://velog.io/@jungo_0/React-%EB%A6%AC%EC%95%A1%ED%8A%B8-%EB%9D%BC%EC%9A%B0%ED%84%B0-loader</guid>
            <pubDate>Mon, 13 Apr 2026 05:25:58 GMT</pubDate>
            <description><![CDATA[<h2 id="리액트-라우터의-혁신-loader를-활용한-선-로딩-전략">리액트 라우터의 혁신: Loader를 활용한 선 로딩 전략</h2>
<blockquote>
<p>컴포넌트가 렌더링된 후 데이터를 가져오는 기존 방식(Waterfall)을 탈피하여, 페이지 이동과 동시에 데이터를 준비하는 효율적인 데이터 패칭 기법임</p>
</blockquote>
<p><strong>키워드:</strong> <code>createBrowserRouter</code>, <code>loader</code>, <code>useLoaderData</code>, <code>Outlet</code>, <code>NavLink (end)</code></p>
<hr>
<h3 id="중첩-라우팅과-레이아웃-구성">중첩 라우팅과 레이아웃 구성</h3>
<p>리액트 라우터 6.4 버전부터는 객체 기반의 라우터 설정을 통해 복잡한 계층 구조를 직관적으로 관리할 수 있습니다.</p>
<p>❖ <strong>구조적 특징:</strong></p>
<ul>
<li><strong><code>RootLayout</code></strong>: 전체 앱의 공통 뼈대 (네비게이션 등).</li>
<li><strong><code>EventsRootLayout</code></strong>: <code>/events</code> 하위 페이지들의 공통 레이아웃.</li>
<li><strong><code>Outlet</code></strong>: 부모 라우트 컴포넌트 내에서 자식 라우트 컴포넌트가 렌더링될 위치를 지정함.</li>
<li><strong><code>NavLink (end)</code></strong>: 경로가 정확히 일치할 때만 활성 상태(active)를 표시하도록 제한함.</li>
</ul>
<hr>
<h3 id="loader-선-로딩-후-렌더링의-마법">loader: &quot;선 로딩 후 렌더링&quot;의 마법</h3>
<p>기존의 <code>useEffect</code> 방식은 컴포넌트가 화면에 나타난 뒤에야 데이터를 가져왔지만, <code>loader</code>는 페이지에 발을 들이기 전에 미리 장을 봐오는 것과 같습니다.</p>
<table>
<thead>
<tr>
<th align="left">방식</th>
<th align="left">흐름 (Process)</th>
<th align="left">사용자 경험 (UX)</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>기존 (useEffect)</strong></td>
<td align="left">컴포넌트 렌더링 → fetch 실행 → 상태 업데이트 → 재렌더링</td>
<td align="left">빈 화면이나 로딩 스피너를 먼저 보게 됨</td>
</tr>
<tr>
<td align="left"><strong>혁신 (loader)</strong></td>
<td align="left"><strong>데이터 로딩 완료(await)</strong> → 완성된 데이터와 함께 컴포넌트 등장</td>
<td align="left">데이터가 채워진 완성된 페이지를 즉시 보게 됨</td>
</tr>
</tbody></table>
<p><span style="background-color:#5A5AFF"><code>loader</code>는 라우트 설정 단계에서 함수를 연결하여, 해당 경로로 이동하기 직전에 데이터를 미리 가져오는 &#39;사전 준비 요원&#39;입니다.</span></p>
<hr>
<h3 id="실전-코드-분석-loader-적용하기">실전 코드 분석: loader 적용하기</h3>
<pre><code class="language-js">{
  path: &#39;events&#39;,
  element: &lt;EventsRootLayout /&gt;,
  children: [
    {
      index: true,
      element: &lt;EventsPage /&gt;,
      // 페이지 이동 전 실행되는 사전 로딩 함수
      loader: async () =&gt; {
        const response = await fetch(&#39;http://localhost:8080/events&#39;);

        if (!response.ok) {
          // 에러 처리 로직
        } else {
          const resData = await response.json();
          return resData.events; // 리턴된 데이터는 컴포넌트에서 useLoaderData로 사용 가능
        }
      },
    },
    { path: &#39;:eventId&#39;, element: &lt;EventDetailPage /&gt; },
  ],
}</code></pre>
<hr>
<h4 id="데이터-패칭-프로세스의-변화">데이터 패칭 프로세스의 변화</h4>
<blockquote>
<ol>
<li><strong>사용자 클릭</strong>: 사용자가 <code>/events</code> 링크를 클릭함.</li>
<li><strong>Loader 가동</strong>: 리액트 라우터가 해당 경로의 <code>loader</code> 함수를 감지하고 데이터를 가져오기 시작함.</li>
<li><strong>컴포넌트 렌더링</strong>: 데이터 로딩이 끝날 때까지 이동을 잠시 유보했다가, 데이터가 준비되면 <code>EventsPage</code>를 화면에 뿌림.</li>
<li><strong>데이터 사용</strong>: 컴포넌트 내부에서 <code>useLoaderData()</code> 훅을 통해 <code>useState</code> 없이도 데이터를 바로 사용함.</li>
</ol>
</blockquote>
<hr>
<h3 id="커스텀-레이아웃과-outlet의-조화">커스텀 레이아웃과 Outlet의 조화</h3>
<p>중첩된 라우트 구조에서 레이아웃 컴포넌트는 UI의 일관성을 유지하는 핵심 역할을 합니다.</p>
<p>❖ <strong>레이아웃 관리:</strong></p>
<ul>
<li><code>EventsRootLayout</code> 내부에 <code>&lt;EventsNavigation /&gt;</code>을 배치하고 그 아래에 <code>&lt;Outlet /&gt;</code>을 둡니다.</li>
<li>이렇게 하면 <code>/events</code>, <code>/events/new</code>, <code>/events/1</code> 등 어떤 하위 페이지로 가더라도 서브 네비게이션은 고정된 채 알맹이(<code>Outlet</code>)만 바뀌게 됩니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React]리듀서와 비동기의 분리 Redux ,  비동기]]></title>
            <link>https://velog.io/@jungo_0/React%EB%A6%AC%EB%93%80%EC%84%9C%EC%99%80-%EB%B9%84%EB%8F%99%EA%B8%B0%EC%9D%98-%EB%B6%84%EB%A6%AC-Redux-%EB%B9%84%EB%8F%99%EA%B8%B0</link>
            <guid>https://velog.io/@jungo_0/React%EB%A6%AC%EB%93%80%EC%84%9C%EC%99%80-%EB%B9%84%EB%8F%99%EA%B8%B0%EC%9D%98-%EB%B6%84%EB%A6%AC-Redux-%EB%B9%84%EB%8F%99%EA%B8%B0</guid>
            <pubDate>Sun, 12 Apr 2026 12:20:03 GMT</pubDate>
            <description><![CDATA[<h2 id="리덕스-설계의-황금률---리듀서와-비동기의-분리">리덕스 설계의 황금률:   리듀서와 비동기의 분리</h2>
<blockquote>
<p>리듀서 내부의 금지된 규칙을 이해하고, &quot;멍청한 백엔드&quot;를 보완하는 프론트엔드 설계 전략을 마스터하는 단계임</p>
</blockquote>
<p><strong>키워드:</strong> Reducer Purity(리듀서의 순수성), Side Effects(부수 효과), Fat Reducer vs Lean Component, Thunk(썽크)</p>
<hr>
<h3 id="리듀서-안에서-fetch를-쓰면-안-되는-이유">리듀서 안에서 fetch()를 쓰면 안 되는 이유</h3>
<p>리덕스의 리듀서는 반드시 <strong>순수 함수(Pure Function)</strong>여야 합니다. 즉, 외부 세계를 건드리지 않고 입력값에 대해서만 결과를 내놓아야 합니다.</p>
<p>❖ <strong>설계 원칙 위반:</strong></p>
<ul>
<li><strong>예측 가능성 파괴</strong>: 비동기 요청은 언제 끝날지 알 수 없습니다. 리덕스는 현재 상태를 즉각적으로 파악해야 하는데, 비동기가 끼어들면 상태 추적이 불가능해집니다.</li>
<li><strong>순수성 위반</strong>: 서버 데이터를 수정하는 행위는 &#39;부수 효과&#39;입니다. 리듀서는 오직 &#39;상태 계산&#39;만 해야 하며 외부 환경(서버)을 바꿔서는 안 됩니다.</li>
</ul>
<p><span style="background-color:#5A5AFF">리듀서는 &quot;입력(현재 상태+액션) -&gt; 출력(새 상태)&quot;이 즉각적이고 명확한 &#39;계산기&#39; 역할만 수행해야 합니다.</span></p>
<hr>
<h3 id="전략적-선택-멍청한-백엔드-대응법">전략적 선택: &quot;멍청한 백엔드&quot; 대응법</h3>
<p>파이어베이스와 같이 단순히 저장만 하는 백엔드를 사용할 때는 프론트엔드가 더 똑똑해져야 합니다.</p>
<table>
<thead>
<tr>
<th align="left">시나리오</th>
<th align="left">백엔드 특징</th>
<th align="left">프론트엔드(리덕스) 역할</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>똑똑한 백엔드</strong></td>
<td align="left">자체 로직(수량 계산 등) 있음</td>
<td align="left">상품 ID만 전송 후 결과값 반영</td>
</tr>
<tr>
<td align="left"><strong>멍청한 백엔드</strong></td>
<td align="left">시키는 대로 저장만 함</td>
<td align="left"><strong>리듀서에서 모든 계산</strong> 후 전체 데이터 전송</td>
</tr>
</tbody></table>
<h4 id="-데이터-동기화-프로세스"><strong>### 데이터 동기화 프로세스</strong></h4>
<blockquote>
<ol>
<li><strong>리덕스 선제 업데이트</strong>: 리듀서에서 복잡한 계산(수량, 합계)을 완벽하게 끝냄</li>
<li><strong>상태 감지 (App.js)</strong>: <code>useSelector</code>로 바뀐 상태를 감지함</li>
<li><strong>서버 복사 (PUT)</strong>: <code>useEffect</code>를 통해 최신 상태를 서버에 통째로 덮어쓰기(PUT)하여 동기화함</li>
</ol>
</blockquote>
<hr>
<h3 id="해결책-썽크thunk와-액션-생성자">해결책: 썽크(Thunk)와 액션 생성자</h3>
<p>리듀서에 넣을 수 없는 비동기 로직은 <strong>리덕스 썽크(Thunk)</strong>를 통해 우아하게 해결할 수 있습니다.</p>
<p>❖ <strong>로직의 거처 선정:</strong></p>
<ul>
<li><strong>리듀서(Reducer)</strong>: &quot;데이터 계산의 중심&quot;. 절대 비동기 금지.</li>
<li><strong>썽크(Thunk)</strong>: &quot;비즈니스 로직의 해결사&quot;. HTTP 요청, 에러 처리 등 &#39;지저분한&#39; 일을 전담.</li>
<li><strong>컴포넌트(Component)</strong>: &quot;화면의 전달자&quot;. 복잡한 건 몰라도 됨. 오직 <code>dispatch</code>만 수행 (Lean Component).</li>
</ul>
<pre><code class="language-js">// App.js에서의 자동 동기화 예시
useEffect(() =&gt; {
  if (isInitial) { // 초기 렌더링 시 전송 방지
    isInitial = false;
    return;
  }

  // 리덕스 상태(cart)가 바뀔 때마다 서버에 PUT 요청
  sendCartData(cart); 
}, [cart]);</code></pre>
<h4 id="리덕스-데브툴devtools-활용">리덕스 데브툴(DevTools) 활용</h4>
<p>단순한 디버깅을 넘어, 애플리케이션의 흐름을 지배하는 도구입니다.</p>
<p>❖ 핵심 기능:</p>
<ul>
<li><p>상태 추적: 액션 전후의 상태 변화를 한눈에 비교.</p>
</li>
<li><p>시간 여행(Time Travel): 과거의 특정 액션 시점으로 상태를 되돌려 버그 재현.</p>
</li>
<li><p><span style='color:yellowgreen'>복잡한 상태 변화를 시각화하여 유지보수 시간을 획기적으로 단축함.</span></p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Map Set]]></title>
            <link>https://velog.io/@jungo_0/Map-Set</link>
            <guid>https://velog.io/@jungo_0/Map-Set</guid>
            <pubDate>Thu, 12 Feb 2026 08:04:54 GMT</pubDate>
            <description><![CDATA[<h2 id="map--set-완벽-정리">Map &amp; Set 완벽 정리</h2>
<blockquote>
<p>배열(<code>Array</code>)만 쓰다가 특정 상황에서 막힐 때가 있는데, 그때 꺼내 쓰는 게 <code>Map</code>이랑 <code>Set</code>임. 중복 제거랑 탐색 속도 면에서 효율이 압도적이라 정리</p>
</blockquote>
<h3 id="1-set-세트">1. Set (세트)</h3>
<p><code>Set</code>은 <strong>중복을 허용하지 않는</strong> 데이터 집합임. 값만 저장하는데, 똑같은 걸 두 번 넣어도 하나만 남음.</p>
<h4 id="특징">특징</h4>
<ul>
<li>중복된 값은 알아서 걸러짐.</li>
<li>특정 값이 있는지 확인할 때 <code>has()</code>를 쓰는데, 배열의 <code>includes()</code>보다 훨씬 빠름 ($O(1)$).</li>
</ul>
<pre><code class="language-javascript">const mySet = new Set();

mySet.add(1);
mySet.add(2);
mySet.add(2); // 중복이라 안 들어감

console.log(mySet.has(1)); // true
console.log(mySet.has(3)); // false
console.log(mySet.size);   // 2 (개수 확인)

// ✅ 꿀팁: 배열 중복 제거 한 줄 컷
const arr = [1, 2, 2, 3, 3];
const uniqueArr = [...new Set(arr)]; // [1, 2, 3]

2. Map (맵)Map은 Key(키) - Value(값) 쌍으로 저장하는 구조임. 객체({})랑 비슷하지만 코테에선 Map이 훨씬 편함.특징키값으로 숫자, 객체 등 아무거나 다 쓸 수 있음.size로 데이터가 몇 개인지 바로 확인 가능.데이터가 들어온 순서가 유지됨.JavaScriptconst myMap = new Map();

myMap.set(&#39;바키&#39;, &#39;격투가&#39;);
myMap.set(&#39;죠죠&#39;, &#39;스탠드사&#39;);
myMap.set(123, &#39;숫자 키 가능&#39;);

console.log(myMap.get(&#39;바키&#39;)); // &#39;격투가&#39;
console.log(myMap.has(&#39;죠죠&#39;)); // true (키 있는지 확인)
console.log(myMap.size);        // 3
</code></pre>
<p>구분,Array (배열),Set / Map
속도,includes() 쓰면 O(N) (느림),has() 쓰면 O(1) (개빠름)
중복,상관없음 (다 들어감),중복 불가 (특히 Set)
용도,순서대로 저장하거나 인덱스 접근할 때,빠른 탐색이나 빈도수 셀 때</p>
<p>실전 예시: 숫자 개수 세기
배열에 숫자가 각각 몇 개씩 있는지 셀 때 Map 쓰면 깔끔함.</p>
<pre><code class="language-js">const arr = [1, 1, 2, 3, 1, 2];
const counts = new Map();

arr.forEach(num =&gt; {
  // 있으면 가져와서 +1, 없으면 0에서 +1
  counts.set(num, (counts.get(num) || 0) + 1);
});

console.log(counts.get(1)); // 3</code></pre>
<p>✅ 정리: 단순히 들어있는지 체크만 할 거면 Set, 이름표 붙여서 관리하거나 개수 세야 하면 Map 쓰면 됨. 성능 차이 때문에 큰 데이터 다룰 땐 필수임.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React]Redux와 불변성(Immutability)]]></title>
            <link>https://velog.io/@jungo_0/Redux%EC%99%80-%EB%B6%88%EB%B3%80%EC%84%B1Immutability</link>
            <guid>https://velog.io/@jungo_0/Redux%EC%99%80-%EB%B6%88%EB%B3%80%EC%84%B1Immutability</guid>
            <pubDate>Mon, 26 Jan 2026 06:52:38 GMT</pubDate>
            <description><![CDATA[<h2 id="커스텀-훅custom-hooks으로-로직-분리하기">커스텀 훅(Custom Hooks)으로 로직 분리하기</h2>
<blockquote>
<p>복잡한 비동기 통신이나 상태 관리 로직을 하나의 함수(Hook)로 묶어 여러 컴포넌트에서 재사용할 수 있게 만드는 고급 기법임</p>
</blockquote>
<p>리액트의 내장 훅(<code>useState</code>, <code>useEffect</code> 등)을 조합하여 나만의 도구를 만드는 과정이다.</p>
<hr>
<h3 id="커스텀-훅의-핵심-규칙">커스텀 훅의 핵심 규칙</h3>
<p>커스텀 훅을 만들 때는 리액트가 이를 일반 함수와 구분할 수 있도록 약속된 형식을 지켜야 한다.</p>
<p>❖ <strong>작성 가이드:</strong></p>
<ul>
<li><strong><code>use</code> 접두사</strong>: 반드시 함수 이름은 <code>use</code>로 시작해야 함 (예: <code>useFetch</code>, <code>useInput</code>)</li>
<li><strong>상태의 독립성</strong>: 같은 훅을 여러 컴포넌트에서 사용해도 각각의 컴포넌트는 자신만의 독립된 상태를 가짐</li>
<li><strong>로직 공유</strong>: UI가 아닌 <strong>동작(Logic)</strong>을 공유하는 것이 목적임</li>
</ul>
<p><span style="background-color:#5A5AFF">커스텀 훅은 컴포넌트를 가볍게 만들고, 코드의 중복을 획기적으로 줄여주는 &quot;로직 주머니&quot;와 같다.</span></p>
<hr>
<h3 id="실전-예시-usefetch-커스텀-훅">실전 예시: useFetch 커스텀 훅</h3>
<p>데이터를 가져오는 공통 로직을 <code>useFetch.js</code> 파일로 분리한 모습이다.</p>
<pre><code class="language-js">import { useEffect, useState } from &#39;react&#39;;

export function useFetch(fetchFn, initialValue) {
  const [isFetching, setIsFetching] = useState(false);
  const [error, setError] = useState();
  const [fetchedData, setFetchedData] = useState(initialValue);

  useEffect(() =&gt; {
    async function fetchData() {
      setIsFetching(true);
      try {
        const data = await fetchFn();
        setFetchedData(data);
      } catch (error) {
        setError({ message: error.message || &#39;데이터를 불러오지 못했습니다.&#39; });
      }
      setIsFetching(false);
    }
    fetchData();
  }, [fetchFn]);

  // 컴포넌트에서 필요한 값들을 객체 형태로 반환
  return { isFetching, fetchedData, setFetchedData, error };
}</code></pre>
<h4 id="커스텀-훅-작동-프로세스">커스텀 훅 작동 프로세스</h4>
<blockquote>
<ol>
<li><strong>매개변수 전달</strong>: 컴포넌트에서 실행할 비동기 함수(<code>fetchFn</code>)와 초기값(<code>initialValue</code>)을 훅에 넘겨줌</li>
<li><strong>내부 상태 관리</strong>: 훅 내부에서 로딩, 에러, 데이터 상태를 독립적으로 관리함</li>
<li><strong>자동 실행</strong>: <code>useEffect</code>를 통해 컴포넌트가 마운트될 때 데이터를 자동으로 가져옴</li>
<li><strong>UI 연결</strong>: 컴포넌트는 훅이 리턴한 값들을 구조 분해 할당으로 받아 화면을 그리는 데만 집중함</li>
</ol>
</blockquote>
<hr>
<h3 id="커스텀-훅의-이점-관심사의-분리">커스텀 훅의 이점: 관심사의 분리</h3>
<p>컴포넌트 파일 안에 수십 줄의 <code>useEffect</code>와 <code>useState</code>가 섞여 있으면 가독성이 떨어진다.</p>
<p>❖ <strong>최적화 효과:</strong></p>
<ul>
<li><strong>가독성 향상</strong>: 컴포넌트는 &quot;어떤 UI를 보여줄지&quot;에만 집중하고, 훅은 &quot;어떻게 데이터를 가져올지&quot;만 고민함</li>
<li><strong>유지보수 용이</strong>: 데이터 패칭 로직에 수정이 필요할 때, 여러 파일을 고칠 필요 없이 커스텀 훅 파일 하나만 수정하면 됨</li>
<li><span style='color:yellowgreen'>함수 이름(<code>useFetch</code>)만 봐도 이 로직이 무엇을 하는지 한눈에 파악할 수 있음</span></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 효율적인 폼(Form) 관리: FormData API와 preventDefault]]></title>
            <link>https://velog.io/@jungo_0/React-%ED%9A%A8%EC%9C%A8%EC%A0%81%EC%9D%B8-%ED%8F%BCForm-%EA%B4%80%EB%A6%AC</link>
            <guid>https://velog.io/@jungo_0/React-%ED%9A%A8%EC%9C%A8%EC%A0%81%EC%9D%B8-%ED%8F%BCForm-%EA%B4%80%EB%A6%AC</guid>
            <pubDate>Sun, 19 Oct 2025 05:34:13 GMT</pubDate>
            <description><![CDATA[<h2 id="데이터-삭제와-낙관적-업데이트-optimistic-updates">데이터 삭제와 낙관적 업데이트 (Optimistic Updates)</h2>
<blockquote>
<p>사용자가 삭제 버튼을 눌렀을 때, 서버의 응답을 기다리지 않고 화면에서 먼저 지워버려 앱이 즉각적으로 반응하게 만드는 기법임</p>
</blockquote>
<p>비동기 통신의 지연 시간(Latency)을 사용자 경험(UX)으로 극복하는 고급 기술이다.</p>
<hr>
<h3 id="삭제-로직의-3단계-흐름">삭제 로직의 3단계 흐름</h3>
<p>데이터 삭제는 단순히 명령을 보내는 것이 아니라, UI와 서버 데이터를 동기화하는 섬세한 과정이 필요하다.</p>
<p>❖ <strong>구현 프로세스:</strong></p>
<ol>
<li><strong>화면 선제 업데이트</strong>: <code>setUserPlaces</code>를 호출하여 클릭한 항목을 리스트에서 즉시 제거한다.</li>
<li><strong>서버 통신 수행</strong>: <code>fetch</code> 요청을 통해 서버에 삭제 명령을 보낸다.</li>
<li><strong>에러 핸들링 (복구)</strong>: 만약 서버 삭제가 실패하면, 화면에서 지웠던 데이터를 다시 복구시킨다.</li>
</ol>
<p><span style="background-color:#5A5AFF">사용자는 &quot;내 명령이 즉시 실행되었다&quot;고 느끼며, 앱이 매우 빠르다는 인상을 받게 된다.</span></p>
<hr>
<h3 id="실전-코드-분석">실전 코드 분석</h3>
<pre><code class="language-js">const handleRemovePlace = useCallback(async function handleRemovePlace() {
  // 1. 낙관적 업데이트: UI에서 먼저 제거
  setUserPlaces((prevPickedPlaces) =&gt;
    prevPickedPlaces.filter((place) =&gt; place.id !== selectedPlace.current.id)
  );

  try {
    // 2. 서버에 실제 삭제 요청
    await updateUserPlaces(
      userPlaces.filter((place) =&gt; place.id !== selectedPlace.current.id)
    );
  } catch (error) {
    // 3. 실패 시 복구 로직: 이전 상태로 되돌림
    setUserPlaces(userPlaces);
    setErrorUpdatingPlaces({
      message: error.message || &#39;삭제에 실패했습니다.&#39;,
    });
  }
}, [userPlaces]);</code></pre>
<hr>
<h4 id="-삭제-및-복구-프로세스"><strong>### 삭제 및 복구 프로세스</strong></h4>
<blockquote>
<ol>
<li><strong>배열 필터링</strong>: <code>filter</code> 메서드를 사용하여 선택된 ID를 제외한 새로운 배열을 생성하고 상태를 업데이트함</li>
<li><strong>비동기 요청</strong>: <code>await</code>를 사용하여 서버의 응답을 기다리지만, 사용자는 이미 지워진 화면을 보고 있음</li>
<li><strong>롤백(Rollback)</strong>: 네트워크 단절 등으로 서버 작업이 실패하면 <code>catch</code> 문에서 기존 <code>userPlaces</code>를 다시 세팅하여 항목을 화면에 다시 나타나게 함</li>
</ol>
</blockquote>
<hr>
<h3 id="-왜-usecallback으로-감싸나"><strong>### 왜 useCallback으로 감싸나</strong></h3>
<p>이 삭제 함수는 보통 자식 컴포넌트(모달이나 버튼)로 전달되는 경우가 많다.</p>
<p>❖ <strong>최적화 이유:</strong></p>
<ul>
<li><strong>함수 주소 고정</strong>: <code>useCallback</code>을 쓰지 않으면 부모가 리렌더링될 때마다 삭제 함수가 새로 만들어진다.</li>
<li><strong>불필요한 자식 렌더링 방지</strong>: 함수 주소가 바뀌면 이 함수를 전달받은 자식 컴포넌트(<code>memo</code> 처리된 경우)까지 다시 그려지게 되므로, 이를 방지하기 위해 주소를 박제한다.</li>
<li><span style='color:yellowgreen'>의존성 배열에 <code>userPlaces</code>를 넣어 최신 데이터 상태를 유지하도록 설계한다.</span></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 가상 DOM과 렌더링 원리]]></title>
            <link>https://velog.io/@jungo_0/%EA%B0%80%EC%83%81-DOM%EA%B3%BC-%EB%A0%8C%EB%8D%94%EB%A7%81-%EC%9B%90%EB%A6%AC</link>
            <guid>https://velog.io/@jungo_0/%EA%B0%80%EC%83%81-DOM%EA%B3%BC-%EB%A0%8C%EB%8D%94%EB%A7%81-%EC%9B%90%EB%A6%AC</guid>
            <pubDate>Fri, 19 Sep 2025 13:48:35 GMT</pubDate>
            <description><![CDATA[<h2 id="가상-dom과-렌더링-원리">가상 DOM과 렌더링 원리</h2>
<blockquote>
<p>리액트가 왜 빠른지, 그리고 우리가 개발자 도구에서 목격하는 &quot;깜빡임&quot;의 정체가 무엇인지 파헤쳐 봅니다.</p>
</blockquote>
<hr>
<h3 id="1-초기-렌더링-the-initial-render">1. 초기 렌더링 (The Initial Render)</h3>
<p>처음 웹사이트에 접속하면 리액트는 전체 컴포넌트 트리를 훑으며 <strong>가상 DOM 스냅샷</strong>을 찍습니다. 그리고 이 설계도대로 실제 빈 HTML의 <code>&lt;div&gt;</code> 안에 내용을 꽉 채워 넣습니다.</p>
<p>❖ <strong>초기 단계:</strong></p>
<ul>
<li><strong>실제 DOM</strong>: 텅 비어 있음 (<code>&lt;div id=&quot;root&quot;&gt;&lt;/div&gt;</code>)</li>
<li><strong>가상 DOM</strong>: 컴포넌트 트리 전체 구조를 메모리에 생성</li>
<li><strong>결과</strong>: 가상 DOM 전체가 실제 DOM으로 뿅! 하고 나타남</li>
</ul>
<hr>
<h3 id="2-컴포넌트-재실행과-가상-dom의-비교">2. 컴포넌트 재실행과 가상 DOM의 비교</h3>
<p>사용자가 버튼을 클릭해서 <code>counter</code> 값이 바뀌면 리액트가 다시 움직이기 시작합니다. 단순히 화면을 바꾸는 게 아니라 내부적으로 정교한 비교 과정을 거칩니다.</p>
<p>❖ <strong>비교 프로세스:</strong></p>
<ul>
<li><strong>새 스냅샷 생성</strong>: 상태가 변한 컴포넌트를 다시 실행해서 새로운 가상 DOM 스냅샷을 생성</li>
<li><strong>Diffing (비교)</strong>: 리액트가 <strong>&quot;이전 스냅샷&quot;</strong>과 <strong>&quot;새 스냅샷&quot;</strong>을 1:1로 비교 (마치 &#39;틀린 그림 찾기&#39;와 같음)</li>
<li><strong>최소 변경 사항 도출</strong>: &quot;다른 건 그대로인데 <code>&lt;span&gt;</code> 안의 숫자만 5에서 6으로 바뀌었네?&quot;라고 딱 그 부분만 찾아냄</li>
</ul>
<hr>
<h3 id="3-실제-dom-업데이트-reconciliation">3. 실제 DOM 업데이트 (Reconciliation)</h3>
<p>리액트가 영리한 이유는 바로 이 지점입니다. 전체 페이지를 다시 그리는 대신, 찾아낸 그 <strong>&#39;차이점&#39;</strong>만 실제 DOM에 적용합니다.</p>
<ul>
<li><strong>비효율적 방식</strong>: 전체 HTML을 싹 지우고 새로 쓰기 (브라우저 부하 가중)</li>
<li><strong>리액트 방식</strong>: 딱 <code>&lt;span&gt;</code> 요소의 텍스트 노드만 교체 (사용자가 느끼지 못할 정도로 빠름)</li>
</ul>
<p><span style="background-color:#5A5AFF">개발자 도구에서 <strong>&quot;해당 요소만 깜빡이는 현상&quot;</strong>이 바로 리액트가 최소한의 일만 하고 있다는 증거입니다!</span></p>
<hr>
<h3 id="4-왜-실제-dom을-직접-안-쓰나요">4. 왜 실제 DOM을 직접 안 쓰나요?</h3>
<p>실제 DOM은 브라우저 화면을 그리는 복잡한 정보(스타일, 레이아웃 등)를 다 가지고 있어서, 하나만 건드려도 브라우저가 계산을 다시 해야 합니다.</p>
<p>❖ <strong>가상 DOM의 장점:</strong></p>
<ul>
<li><strong>속도</strong>: 자바스크립트 객체일 뿐이라 메모리 안에서 수만 번 비교해도 엄청나게 빠름</li>
<li><strong>전략</strong>: &quot;메모리에서 신나게 비교하고, <strong>결론만 실제 DOM에 딱 한 번</strong> 알려주는&quot; 효율적인 방식</li>
</ul>
<hr>
<h3 id="💡-요약-정리"><strong>💡 요약 정리</strong></h3>
<table>
<thead>
<tr>
<th align="left">단계</th>
<th align="left">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>함수 실행 $\neq$ 업데이트</strong></td>
<td align="left">함수가 실행된다고 해서 무조건 화면이 바뀌는 건 아님</td>
</tr>
<tr>
<td align="left"><strong>가상 DOM</strong></td>
<td align="left">메모리에 존재하는 가벼운 복사본 설계도</td>
</tr>
<tr>
<td align="left"><strong>Diffing</strong></td>
<td align="left">이전 설계도와 새 설계도의 차이점 찾기</td>
</tr>
<tr>
<td align="left"><strong>업데이트</strong></td>
<td align="left">바뀐 부분만 실제 DOM에 &#39;수술&#39;하듯 집어넣기</td>
</tr>
</tbody></table>
<hr>
<h3 id="결론"><strong>결론</strong></h3>
<p>리액트는 단순히 UI를 그리는 라이브러리가 아니라, <strong>최소한의 변경으로 최적의 경험을 제공하는 전략가</strong>입니다. 가상 DOM이라는 완충 지대를 두어 브라우저의 부담을 덜어주는 것이 그 핵심입니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JavaScript] JSON]]></title>
            <link>https://velog.io/@jungo_0/JavaScript-JSON</link>
            <guid>https://velog.io/@jungo_0/JavaScript-JSON</guid>
            <pubDate>Thu, 28 Aug 2025 06:16:49 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/jungo_0/post/e4443ae8-4b12-422c-a394-6ef2c8ed17b5/image.png" alt=""></p>
<h3 id="json-기본-이해-jsonstringify와-jsonparse-활용-및-json-형식과-문자열의-차이">JSON 기본 이해: JSON.stringify와 JSON.parse 활용 및 JSON 형식과 문자열의 차이</h3>
<h2 id="1-json이란-무엇인가">1. JSON이란 무엇인가</h2>
<blockquote>
<p>JSON은 &quot;JavaScript Object Notation&quot;의 약자입니다. 이는 데이터를 구조화하여 표현하기 위한 개방형 표준 형식이며, 사람이 읽고 쓰기 용이하며 기계가 파싱하고 생성하기 쉬운 형태로 설계되었습니다. 웹 서비스에서 서버와 클라이언트 간의 데이터 교환에 주로 사용됩니다.</p>
</blockquote>
<h4 id="json의-주요-특징">JSON의 주요 특징</h4>
<p><strong>가독성</strong>: key-value 쌍으로 구성되어 데이터의 의미를 쉽게 파악할 수 있습니다.
<strong>경량성</strong>: 다른 데이터 교환 형식(예: XML)에 비해 파일 크기가 작아 네트워크 전송 효율이 높습니다.
<strong>범용성</strong>: 대부분의 프로그래밍 언어에서 JSON을 처리하는 라이브러리 및 기능이 내장되어 있습니다.</p>
<h4 id="json-데이터의-일반적인-구조">JSON 데이터의 일반적인 구조</h4>
<pre><code class="language-js">{
  &quot;department&quot;: &quot;IT 개발&quot;,
  &quot;employeeId&quot;: &quot;mjh&quot;,
  &quot;isActive&quot;: true,
  &quot;skills&quot;: [&quot;JavaScript&quot;, &quot;React&quot;, &quot;Spring&quot;],
  &quot;contact&quot;: {
    &quot;email&quot;: &quot;contact@example.com&quot;,
    &quot;phone&quot;: &quot;010-xxxx-xxxx&quot;
  }
}</code></pre>
<h2 id="2-jsonstringify와-jsonparse의-기능-및-형태">2. JSON.stringify()와 JSON.parse()의 기능 및 형태</h2>
<blockquote>
<p>JSON 데이터는 주로 문자열 형태로 통신되거나 저장됩니다. 자바스크립트에서는 이러한 변환을 위해 JSON.stringify()와 JSON.parse() 두 가지 내장 함수를 제공합니다.</p>
</blockquote>
<p>JSON.stringify(): 자바스크립트 객체를 JSON 문자열로 변환</p>
<ul>
<li>자바스크립트 객체 또는 값을 JSON 형식의 문자열로 직렬화(serialize)합니다. 이 함수는 주로 객체를 웹 서버로 전송하거나 localStorage와 같은 웹 스토리지에 저장하기 전에 사용됩니다.<pre><code class="language-js">// 변환 전: 자바스크립트 객체
const userDataObject = {
name: &quot;김철수&quot;,
age: 30,
isAdmin: false,
roles: [&quot;user&quot;, &quot;guest&quot;]
};
</code></pre>
</li>
</ul>
<p>console.log(&quot;변환 전 타입:&quot;, typeof userDataObject);
console.log(&quot;변환 전 값:&quot;, userDataObject);
/* 출력:
변환 전 타입: object
변환 전 값: { name: &#39;김철수&#39;, age: 30, isAdmin: false, roles: [ &#39;user&#39;, &#39;guest&#39; ] }
*/</p>
<p>// JSON.stringify() 적용: 객체를 JSON 문자열로 변환
const jsonStringifiedData = JSON.stringify(userDataObject);</p>
<p>console.log(&quot;변환 후 타입:&quot;, typeof jsonStringifiedData);
console.log(&quot;변환 후 값:&quot;, jsonStringifiedData);
/* 출력:
변환 후 타입: string
변환 후 값: {&quot;name&quot;:&quot;김철수&quot;,&quot;age&quot;:30,&quot;isAdmin&quot;:false,&quot;roles&quot;:[&quot;user&quot;,&quot;guest&quot;]}
*/</p>
<pre><code>변환 후 jsonStringifiedData 변수는 JavaScript의 object 타입이 아닌 string 타입의 값을 가지게 되며, 모든 키와 문자열 값은 이중 따옴표(&quot;)로 감싸집니다.



 JSON.parse(): JSON 문자열을 자바스크립트 객체로 변환

- J JSON 형식의 문자열을 파싱(parse)하여 자바스크립트 객체로 역직렬화(deserialize)합니다. 이 함수는 주로 웹 서버로부터 JSON 응답을 수신하거나 localStorage에서 JSON 데이터를 읽어올 때 사용됩니다.
```js
// 변환 전: JSON 형식의 문자열 (예: 서버로부터 수신된 데이터)
const receivedJsonString = &#39;{&quot;name&quot;:&quot;박영희&quot;,&quot;age&quot;:25,&quot;isAdmin&quot;:true,&quot;roles&quot;:[&quot;admin&quot;,&quot;editor&quot;]}&#39;;

console.log(&quot;변환 전 타입:&quot;, typeof receivedJsonString);
console.log(&quot;변환 전 값:&quot;, receivedJsonString);
/* 출력:
변환 전 타입: string
변환 전 값: {&quot;name&quot;:&quot;박영희&quot;,&quot;age&quot;:25,&quot;isAdmin&quot;:true,&quot;roles&quot;:[&quot;admin&quot;,&quot;editor&quot;]}
*/

// JSON.parse() 적용: JSON 문자열을 자바스크립트 객체로 변환
const parsedDataObject = JSON.parse(receivedJsonString);

console.log(&quot;변환 후 타입:&quot;, typeof parsedDataObject);
console.log(&quot;변환 후 값:&quot;, parsedDataObject);
/* 출력:
변환 후 타입: object
변환 후 값: { name: &#39;박영희&#39;, age: 25, isAdmin: true, roles: [ &#39;admin&#39;, &#39;editor&#39; ] }
*/</code></pre><p>변환 후 parsedDataObject 변수는 JavaScript의 object 타입이 되며, 해당 객체의 속성에 직접 접근하여 데이터를 활용할 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JavaScript] 화살표함수 / this]]></title>
            <link>https://velog.io/@jungo_0/JavaScript-%ED%99%94%EC%82%B4%ED%91%9C%ED%95%A8%EC%88%98</link>
            <guid>https://velog.io/@jungo_0/JavaScript-%ED%99%94%EC%82%B4%ED%91%9C%ED%95%A8%EC%88%98</guid>
            <pubDate>Sun, 15 Dec 2024 09:14:53 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/jungo_0/post/e4443ae8-4b12-422c-a394-6ef2c8ed17b5/image.png" alt=""></p>
<p><em>function키워드를 사용해서 만든 함수는 동적인 <code>this</code>때문에 이리저리 바인딩하느라 혼동을 줄 수 있음 화살표 함수를 사용하면 어떤방법으로 호출하던 <code>this</code>의 값이 변하지않음
화살표함수의 <code>this</code>값은 그 함수를 감싸는 스코프의 <code>this</code>를 그대로 가져와서 사용 
전통적인 함수의 <code>this</code>는 함수를 호출하는 방법에 따라 동적으로 바뀜</em></p>
<p><a href="https://velog.io/@jungo_0/df-7jqff55x">this 복습</a></p>
<hr>
<h3 id="일반함수-this">일반함수 this</h3>
<pre><code class="language-js">const object = {
  name:&#39;jungo&#39;,
  main: function () {
    console.log(this);
  },
};
object.main();
// {name:&#39;jungo&#39;,main:f}
</code></pre>
<p>함수를 어떤 객체가 호출했냐에 따라 <code>this</code>가 달라짐
-&gt; object 객체</p>
<ul>
<li>일반함수가 가지는 <code>this</code>값은 함수가 선언된 위치랑은 관련 x</li>
<li>함수가 호출된 방법에 따라 달라짐</li>
</ul>
<h3 id="화살표함수-this">화살표함수 this</h3>
<blockquote>
<p>화살표함수는 자신만의 <code>this</code>를 가지고 있지 않는다.
화살표함수 내부에서 this에 접근하면 외부에서 가져와서 사용</p>
</blockquote>
<pre><code class="language-js">const object = {
  name:&#39;jungo&#39;,
  main: function () {
    console.log(this);
  },
  mainArrow:()=&gt;{
    console.log(this)
  }
};

object.mainArrow();
</code></pre>
<p>결과
<img src="https://velog.velcdn.com/images/jungo_0/post/075bbe5f-7f43-4db9-b223-dea523a789be/image.png" alt="">
window가 this값으로 나온다.
화살표함수는 자신만의 this를 가지지 않기 때문에 자신을 감싸고있는 스코프로 올라가서 this를 찾는다.</p>
<ul>
<li>선언된 위치에서 <code>this</code>가 결정된다. === 함수가 호출된 방법과는 무관하다. </li>
</ul>
<p>따라서</p>
<pre><code class="language-js">const object = {
  name:&#39;jungo&#39;,
  main: function () {
    console.log(this);
  },
  mainArrow:()=&gt;{
    console.log(this.name)    //아무것도 출력 x
  }
};

object.mainArrow();
</code></pre>
<p><span style="background-color:#5A5AFF">화살표함수는 this가 더이상 object가 되지않기 때문에  object속성에 접근할 수 없음</span></p>
<blockquote>
<p>객체의 메서드로는 화살표함수보다 일반함수를 사용하는 것이 적절</p>
</blockquote>
<hr>
<h2 id="예제">예제</h2>
<pre><code class="language-js">const object = {
  name:&#39;jungo&#39;,
  main: function () {
    const innerFunction = function (){
      console.log(this)
    };
    innerFunction()
  },
};

object.main();
</code></pre>
<h4 id="결과---window">결과 - window</h4>
<p><img src="https://velog.velcdn.com/images/jungo_0/post/b1a8953e-ba9f-4343-9fb7-087044dfb9ba/image.png" alt=""></p>
<p>this값이 object가 아닌 window가 출력되는 이유</p>
<ul>
<li>innerFunction은 어떤 객체로부터 직접적으로 호출된게 아님 따라서 innerFunction안에 this는 window</li>
</ul>
<h3 id="수정-예제화살표함수">수정 예제(화살표함수)</h3>
<pre><code class="language-js">const object = {
  name:&#39;jungo&#39;,
  main: function () {
    const innerFunction = () =&gt; {
      console.log(this)
    };
    innerFunction()
  },
};

object.main();
</code></pre>
<h4 id="결과">결과</h4>
<pre><code class="language-js">//object 객체
{name:&#39;jungo&#39;, main:f}</code></pre>
<p>화살표 함수의 this는 감싸는 외부 스코프(main함수)의 this를 그대로 사용 </p>
<ul>
<li>main함수 안에있는 this값은 object객체 </li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JavaScript] 클로저]]></title>
            <link>https://velog.io/@jungo_0/JavaScript-%ED%81%B4%EB%A1%9C%EC%A0%80</link>
            <guid>https://velog.io/@jungo_0/JavaScript-%ED%81%B4%EB%A1%9C%EC%A0%80</guid>
            <pubDate>Mon, 28 Oct 2024 14:34:30 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/jungo_0/post/e4443ae8-4b12-422c-a394-6ef2c8ed17b5/image.png" alt=""></p>
<h3 id="렉시컬-스코프">렉시컬 스코프</h3>
<blockquote>
<p>스코프란 변수나 함수의 접근가능한 유효 범위를 의미하며, 렉시컬 스코프(정적 스코프) 규칙을 따름
JavaScript 스코프는 전역/로컬 스코프로 나뉘며 렉시컬 스코프 규칙에 따라 결정된다.</p>
</blockquote>
<p>❖렉시컬 스코프 규칙:변수가 선언된 위치에 따라 접근 가능 범위가 결정</p>
<h2 id="자바스크립트-변수-참조-과정">자바스크립트 변수 참조 과정</h2>
<h4 id="함수가-실행될때마다-메모리상에-실행컨텍스트-생성">함수가 실행될때마다 메모리상에 실행컨텍스트 생성</h4>
<ul>
<li>실행컨텍스트 안에는 렉시컬 환경 존재</li>
<li>여기에 현재 스코프에서 선언한 모든 변수와 함수들이 저장된 환경 레코드 존재</li>
<li>현재 환경에서 찾지 못한 변수 상위 스코프에서 찾기위해 상위 스코프를 가리키는 외부 렉시컬 환경 참조도 갖고있음</li>
</ul>
<h4 id="코드-상에서-변수나-함수를-참조하면">코드 상에서 변수나 함수를 참조하면</h4>
<ol>
<li>현재 환경 레코드에서 찾기</li>
<li>외부 렉시컬환경 참조를 통해 상위 스코프 참조</li>
<li>없을경우 전역 스코프까지 올라가며, 없으면 <code>Reference Error</code></li>
</ol>
<hr>
<p><span style="background-color:#5A5AFF">클로저 개념을 이해해도 언제 어떻게 쓰이는지 모르겠음</span></p>
<pre><code class="language-js">function countWithoutClosure {
  let count = 0;
    return count;
}
console. log(countWithoutClosure()); // &#39;0&#39;
console. log(count); // ReferenceError: count is not defined</code></pre>
<p>렉시컬 스코프 규칙에 따라 함수안에서 선언한 변수는 함수 안에서만 접근 가능</p>
<blockquote>
<ol>
<li>함수가 실행되면 함수 내부 변수(count)는 메모리에 올라감</li>
<li>return -&gt; 메모리 상 제거</li>
<li>함수바깥에서는 변수가 제거된 상태이므로 참조 불가</li>
</ol>
</blockquote>
<h4 id="기본적인-closure-형태">기본적인 closure 형태</h4>
<blockquote>
<p>클로저는 함수가 생성될 때의 스코프(외부 변수)에 접근할 수 있는 성질을 의미
함수 선언문 바깥에서도 함수 스코프를 참조할 수 있음
<span style='color:yellowgreen'>React useState에서 클로저 개념 사용</span></p>
</blockquote>
<pre><code class="language-js">function countWithClosure() {
let count = 0;
return {
  increase: function () {
    count++;
    return count;
    },
  decrease: function () {
    count--；
    return count;
  },
  getCount: function () {
    return count;
  },
 };
}</code></pre>
<p>함수 return문에서 함수 내부의 변수를 사용하는 함수를 다시 return하면 함수 선언문 바깥에서도 스코프에 접근 가능</p>
<pre><code class="language-js">const counter = countWithClosure;
console. log(counter getCount()); // 0
counter. increase();
console. log(counter.getCount()); // 1
counter. decrease();
console. log(counter.getCount)); // 0</code></pre>
<blockquote>
<p>counter 변수: <code>function increase</code>,<code>function decrease</code>,<code>function getCount</code> 클로저에 의해 return될때 함수 실행에 필요한 상위 스코프도 메모리로 가져옴(<code>counter</code>)
--&gt; <code>counter</code>가 클로저에 의해 메모리에 캡쳐되고있음</p>
</blockquote>
<hr>
<h2 id="정리">정리</h2>
<blockquote>
<p>Closre
자바스크립트는 렉시컬 스코프 규칙을 따름
따라서, 일반적인 방식으로는 함수 선언문 밖에서 함수 스코프에 접근할 수 없음 이를 보완하기 위한 방법이 클로저 함수임
클로저 수는 스코프가 상위 함수의 스코프를 참조하는 항수를 return 하는 방식 React의 useState, moment 라이브러리에서 사용하는 방식</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CSS] Grid, Flex, Position]]></title>
            <link>https://velog.io/@jungo_0/fd</link>
            <guid>https://velog.io/@jungo_0/fd</guid>
            <pubDate>Mon, 14 Oct 2024 13:40:48 GMT</pubDate>
            <description><![CDATA[<p>Flex</p>
<blockquote>
<p>1차원 요소 구성에 용이
수평, 수직 구조</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/jungo_0/post/108f7f87-d0ca-415a-979f-546cd2cf88f2/image.png" alt=""></p>
<p>Grid</p>
<blockquote>
<p>2차원 요소 구성에 용이</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/jungo_0/post/da8f4b76-61e4-40fa-8e46-e4cd148708f9/image.png" alt=""></p>
<pre><code class="language-js">&lt;div class =&quot;container&quot;&gt;
  &lt;/div&gt;</code></pre>
<pre><code class="language-js">.container {
  display:grid;
}</code></pre>
<p>grid영역안에 있는 모든컨텐츠는 gird cell로 분류됨</p>
<pre><code class="language-js">grid-template-columns: 100px 100px 100px
grid-template-rows: 100px 100px 100px
grid-template-rows: repeat(3,1fr)
</code></pre>
<h3 id="행열-합치기">행열 합치기</h3>
<p><img src="https://velog.velcdn.com/images/jungo_0/post/50146c2d-65ce-4d56-8ba1-1fcc7fd8fb73/image.png" alt=""></p>
<p>열번호 1,2
행번호 1,2,3</p>
<pre><code class="language-js">grid-column:1/2;
grid-row:1/3;</code></pre>
<hr>
<h2 id="position">Position</h2>
<blockquote>
<p>position은 아무값도 정해주지않으면 <code>static</code></p>
</blockquote>
<h4 id="relative">relative</h4>
<blockquote>
<p>현재 위치에서 상하좌우 이동 (본인기준)
<img src="https://velog.velcdn.com/images/jungo_0/post/6b6d8066-054c-4b81-9e00-d463c8ec7947/image.png" alt=""></p>
</blockquote>
<h4 id="absolute">absolute</h4>
<blockquote>
<p>2번에 absolute를 부여하면 다른아이템들이 해당아이템의 크기를 무시한체 배치됨 (본인 relative로 선언된 부모기준)
<img src="https://velog.velcdn.com/images/jungo_0/post/cd2d9d2f-f1db-4c14-b1c3-4cf90db10833/image.png" alt="">
body기준으로 top 30px 영향
-&gt;absolute는 해당아이템을 다른 레이아웃으로 보내버림 따라서 1,3은 2번을 무시하고 기존 레이아웃으르 따름</p>
</blockquote>
<h4 id="fixed">fixed</h4>
<blockquote>
<p>사용자의 화면을 기준으로 고정
<img src="https://velog.velcdn.com/images/jungo_0/post/b9f018f8-11c2-4351-b3a2-ebeff4ca6e8f/image.png" alt=""></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 리액트 훅의 규칙과 커스텀 훅(Custom Hooks)]]></title>
            <link>https://velog.io/@jungo_0/React-%EB%A6%AC%EC%95%A1%ED%8A%B8-%ED%9B%85%EC%9D%98-%EA%B7%9C%EC%B9%99%EA%B3%BC-%EC%BB%A4%EC%8A%A4%ED%85%80-%ED%9B%85</link>
            <guid>https://velog.io/@jungo_0/React-%EB%A6%AC%EC%95%A1%ED%8A%B8-%ED%9B%85%EC%9D%98-%EA%B7%9C%EC%B9%99%EA%B3%BC-%EC%BB%A4%EC%8A%A4%ED%85%80-%ED%9B%85</guid>
            <pubDate>Sun, 13 Oct 2024 09:33:59 GMT</pubDate>
            <description><![CDATA[<h3 id="8-성능-최적화의-함정-언제-최적화를-멈춰야-할까">8. 성능 최적화의 함정: 언제 최적화를 멈춰야 할까?</h3>
<blockquote>
<p>모든 코드에 memo와 useMemo를 바르는 것은 오히려 앱을 더 느리게 만들고 코드를 복잡하게 만드는 &#39;과잉 최적화(Over-optimization)&#39;가 될 수 있음</p>
</blockquote>
<p>최적화 도구들은 공짜가 아니다. 값을 비교하고 메모리에 저장하는 과정 자체가 메모리와 CPU 자원을 소모하기 때문이다.</p>
<hr>
<h3 id="최적화가-오히려-독이-되는-순간">최적화가 오히려 독이 되는 순간</h3>
<p>리액트의 기본 렌더링 속도는 매우 빠르다. 굳이 최적화가 필요 없는 곳에 도구를 남발하면 다음과 같은 부작용이 생긴다.</p>
<p>❖ <strong>과잉 최적화의 부작용:</strong></p>
<ul>
<li><strong>비교 비용 발생</strong>: Props를 매번 비교하는 연산이 실제 컴포넌트를 그리는 비용보다 커질 수 있음</li>
<li><strong>가독성 저하</strong>: 단순한 코드들이 <code>useCallback</code>과 의존성 배열로 뒤덮여 유지보수가 어려워짐</li>
<li><strong>메모리 낭비</strong>: 쓰지 않아도 될 값을 캐싱하기 위해 메모리 점유율이 올라감</li>
</ul>
<p><span style="background-color:#5A5AFF">&quot;성능 문제가 체감될 때&quot; 혹은 &quot;자식이 너무 무거워서 부모의 변화에 민감하게 반응할 때&quot;만 최적화를 적용하는 것이 원칙이다.</span></p>
<hr>
<h3 id="최적화-도구-사용-가이드라인">최적화 도구 사용 가이드라인</h3>
<p>성능 최적화 도구를 언제 꺼내 들어야 할지 결정하는 기준은 다음과 같다.</p>
<h4 id="-최적화-결정-프로세스"><strong>### 최적화 결정 프로세스</strong></h4>
<blockquote>
<ol>
<li><strong>기본 렌더링 확인</strong>: 먼저 최적화 없이 코드를 작성하고, 실제 앱의 동작이 끊기는지 확인한다.</li>
<li><strong>병목 지점 발견</strong>: React DevTools의 Profiler 탭을 사용해 어떤 컴포넌트가 불필요하게 자주 그려지는지 찾는다.</li>
<li><strong>단계적 적용</strong>: 가장 무거운 자식 컴포넌트에 <code>memo</code>를 적용해보고, 그 효과를 유지하기 위해 <code>useCallback</code>으로 함수 주소를 고정한다.</li>
</ol>
</blockquote>
<hr>
<h3 id="실전-팁-값비싼-연산의-기준">실전 팁: 값비싼 연산의 기준</h3>
<p><code>useMemo</code>를 쓸지 말지 고민된다면, 해당 연산이 수백 번의 루프를 돌거나 복잡한 객체를 매번 새로 생성하는지 확인하라.</p>
<p>❖ <strong>사용 권장 상황:</strong></p>
<ul>
<li><strong>참조 동일성 유지</strong>: 값이 바뀌지 않았는데 자식의 <code>memo</code>가 깨지는 것을 막아야 할 때</li>
<li><strong>무거운 계산</strong>: 필터링, 정렬, 데이터 가공 등 CPU 소모가 큰 로직이 포함될 때</li>
<li><span style='color:yellowgreen'>단순히 변수 하나를 선언하거나 짧은 문자열을 합치는 정도라면 useMemo 없이 생으로 두는 것이 훨씬 낫다.</span></li>
</ul>
<hr>
<h3 id="요약-및-결론">요약 및 결론</h3>
<p>최적화는 <strong>필요할 때만</strong> 하는 것이 최고의 최적화다. 섣부른 최적화는 코드의 유연성을 해치고 버그를 유발할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JavaScript] this / bind]]></title>
            <link>https://velog.io/@jungo_0/df-7jqff55x</link>
            <guid>https://velog.io/@jungo_0/df-7jqff55x</guid>
            <pubDate>Mon, 02 Sep 2024 10:30:21 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/jungo_0/post/e4443ae8-4b12-422c-a394-6ef2c8ed17b5/image.png" alt=""></p>
<h2 id="this">this</h2>
<p>JavaScript에서의 <code>this</code>는 다른 언어랑 좀 다르다.</p>
<blockquote>
<p>어떤 객체를 가르키는 키워드
&quot;<code>this</code>는 함수를 호출한 객체이다&quot;
-&gt; 함수를 호출한 객체가 누구인지만 중요</p>
</blockquote>
<h3 id="전역-문맥">전역 문맥</h3>
<p>전역적인 문맥에서 접근하면 <code>this</code>는 윈도우 객체가 된다.</p>
<pre><code class="language-js">console.log(this)</code></pre>
<p><img src="https://velog.velcdn.com/images/jungo_0/post/867d953d-8962-4562-837a-a35e964457ba/image.png" alt=""></p>
<h3 id="함수-내부">함수 내부</h3>
<pre><code class="language-js">function main(){
  console.log(this)
}
main()</code></pre>
<p>&quot;<code>this</code>는 함수를 호출한 객체이다&quot;</p>
<ol>
<li>function으로 함수를 정의하면 함수는 <code>window</code>객체에 등록됨
<img src="https://velog.velcdn.com/images/jungo_0/post/4490ece8-ec38-4bee-b13b-e49cf21b2843/image.png" alt=""></li>
<li>main()호출하면 window객체 불러짐</li>
</ol>
<ul>
<li>main() === window.main()</li>
<li>main함수를 직접적으로 호출한 객체는 window 따라서 함수안에 있는 this값은 윈도우 객체</li>
<li>window를 생략하고 main함수를 호출해도 똑같이 윈도우 객체안에 있는 main함수를 호출한거임</li>
</ul>
<h3 id="object">Object</h3>
<pre><code class="language-js">const object = {
  name:&#39;jungo&#39;,
  main: function () {
    console.log(this);
  },
};
object.main();
// {name:&#39;jungo&#39;,main:f}
const main2 = object.main;
main2();</code></pre>
<p>함수안에 있는 this는 함수를 호출한 객체가 된다.</p>
<ul>
<li><p><code>object</code>라는 객체가 main을 호출 (object의 메서드로 main호출)
따라서<code>this</code>는 <code>object</code>가 된다. </p>
</li>
<li><p>객체의 다른 속성에 접근할때 유용
  -this.name // jungo</p>
<h4 id="main2">main2</h4>
<p>main2는 window 출력
<img src="https://velog.velcdn.com/images/jungo_0/post/bcda7d18-3a9c-4dfb-aebd-6338602867e6/image.png" alt=""></p>
</li>
<li><p>main2함수를 호출한 객체는 object가 아니고 전역적으로 호출된 함수</p>
</li>
</ul>
<h4 id="main함수를-먼저-정의하고-객체의-구성원으로-포함시킨-경우">main함수를 먼저 정의하고 객체의 구성원으로 포함시킨 경우</h4>
<pre><code class="language-js">function main(){
  console.log(this)
}

const object = {
  name:&#39;jungo&#39;,
  main,
};
object.main();
//{name:&#39;jungo&#39;,main:f}</code></pre>
<p>결과 동일</p>
<ul>
<li>함수가 object 바깥에서 정의되든 안에서 정의되든 main함수를 호출한 객체가 object라면 this는 object</li>
</ul>
<p><span style="background-color:#5A5AFF">함수의 <code>this</code>값이 궁금하면 .바로 왼쪽객체를 보면된다!</span></p>
<hr>
<h2 id="bind">bind</h2>
<blockquote>
<p>함수에 우리가 원하는 객체를 binding 해준다.
우리가 원하는 객체를 this값으로 설정해주고 싶음</p>
</blockquote>
<pre><code class="language-js">function main(){
  console.log(this)
}

const mainBind = main.bind({name: &#39;hi&#39;})
mainBind();
</code></pre>
<ul>
<li><code>this</code>값이 <code>window</code>가 아니라 {name: &#39;hi&#39;}
<span style="background-color:#5A5AFF">이미 bind된걸 또 bind할수는 없음</span></li>
</ul>
<pre><code class="language-js">const object = {
  name:&#39;jungo&#39;,
  main: function () {
    console.log(this);
  }.bind({name: &#39;객체&#39;}),
};
object.main();</code></pre>
<ul>
<li>this값이 {name:&#39;객체&#39;}로 바뀐다. </li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] usecallback]]></title>
            <link>https://velog.io/@jungo_0/usecallback</link>
            <guid>https://velog.io/@jungo_0/usecallback</guid>
            <pubDate>Tue, 30 Jul 2024 12:23:20 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/jungo_0/post/c294b3d4-4d4c-4168-8f30-a8294f7d1a34/image.png" alt=""></p>
<h2 id="useeffect-사용시-문제점">useEffect 사용시 문제점</h2>
<blockquote>
<p><code>Effect</code>함수에 속성이나 상태값을 사용한다면 <code>dependency</code>로 추가해야한다. 이 코드에서 <code>onConfirm</code>속성을 의존성으로 추가하려하는데 <strong><em><code>onConfirm</code>은 함수</em></strong></p>
</blockquote>
<pre><code class="language-js">export default function DeleteConfirmation({ onConfirm, onCancel }) {
  useEffect(() =&gt; {
    console.log(&quot;TIMER SET&quot;);
    const timer = setTimeout(() =&gt; {
      onConfirm();
    }, 3000);

    return () =&gt; {
      console.log(&quot;Cleaning up timer&quot;);
      clearTimeout(timer);
    };
  }, [onConfirm]);</code></pre>
<ul>
<li>종속성으로서 함수를 추가할 때는 무한 루프를 생성할 위험이 있음</li>
</ul>
<p><code>open</code>과 같은 의존성을이 배열에 추가할 때, 이 Modal 컴포넌트에서 리액트가 이 Effect 함수를 다시 실행해야 하기 때문    </p>
<pre><code class="language-js">function Modal({ open, children, onClose }) {
  const dialog = useRef();

  useEffect(() =&gt; {
    if (open) {
      dialog.current.showModal();
    } else {
      dialog.current.close();
    }
  }, [open]);</code></pre>
<h4 id="onconfirm-함수">onConfirm 함수</h4>
<pre><code class="language-js">//App.jsx
  function handleRemovePlace() {
    setPickedPlaces((prevPickedPlaces) =&gt;
      prevPickedPlaces.filter((place) =&gt; place.id !== selectedPlace.current)
    );
    setModalIsOpen(false);

    const storedIds = JSON.parse(localStorage.getItem(&quot;selectedPlaces&quot;)) || [];
    localStorage.setItem(
      &quot;selectedPlaces&quot;,
      JSON.stringify(storedIds.filter((id) =&gt; id !== selectedPlace.current))
    );
  }

 &lt;Modal open={modalIsOpen}&gt;
        {modalIsOpen &amp;&amp; (
          &lt;DeleteConfirmation
            onCancel={handleStopRemovePlace}
            onConfirm={handleRemovePlace}
          /&gt;
        )}
      &lt;/Modal&gt;</code></pre>
<p>js에서 함수는 객체이므로 이 <code>handleRomovePlace</code> 함수 객체로 앱 컴포넌트 함수가 실행될 때마다 재생성</p>
<ul>
<li>리액트는 새로운 값, 새로운 함수를 보고
이전의 값 또는 함수와 비교하게 되고
그리고는 이 둘이 다르다고 판단을 내린다.</li>
</ul>
<blockquote>
<p>그 이유는 앱 컴포넌트가 다시 렌더링될 때 함수가 다시 실행되면 완전히 새로운 <code>handleRemovePlace</code> 함수가 생성
이 함수를 <code>DeleteConfirmation</code>에서 받은 후에는 리액트는 이 새로운 값, 새로운 함수를 보고 이전의 값 또는 함수와 비교하게되고  둘이 다르다고 판단
<strong>무한루프 발생 위험</strong></p>
</blockquote>
<hr>
<h3 id="usecallback">usecallback</h3>
<blockquote>
<p>React에 함수가 항상 재생성되지 않도록 하기 위해 사용할 수 있는 hook이 제공 함수를 안에 넣어주기만 하면됨(첫번째인자) , 종속성(두번째인자)</p>
</blockquote>
<ul>
<li>둘러싼 함수를 return, 안쪽에 함수가 재생성되지 않고 내부에 메모리로서 저장</li>
<li>두번째인자:함수 안에 사용되는 prop또는 state value값 추가 (useEffect 유사)</li>
</ul>
<pre><code class="language-js">  const handleRemovePlace = useCallback(function handleRemovePlace() {
    setPickedPlaces((prevPickedPlaces) =&gt;
      prevPickedPlaces.filter((place) =&gt; place.id !== selectedPlace.current)
    );
    setModalIsOpen(false);

    const storedIds = JSON.parse(localStorage.getItem(&quot;selectedPlaces&quot;)) || [];
    localStorage.setItem(
      &quot;selectedPlaces&quot;,
      JSON.stringify(storedIds.filter((id) =&gt; id !== selectedPlace.current))
    );
  }, []);</code></pre>
]]></description>
        </item>
    </channel>
</rss>