<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>kyu.log</title>
        <link>https://velog.io/</link>
        <description>dev kyu</description>
        <lastBuildDate>Wed, 19 Feb 2025 06:42:46 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. kyu.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/k_kyu" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[A project ] study(13) Props 타입정의]]></title>
            <link>https://velog.io/@k_kyu/A-project-study13-Props-%ED%83%80%EC%9E%85%EC%A0%95%EC%9D%98</link>
            <guid>https://velog.io/@k_kyu/A-project-study13-Props-%ED%83%80%EC%9E%85%EC%A0%95%EC%9D%98</guid>
            <pubDate>Wed, 19 Feb 2025 06:42:46 GMT</pubDate>
            <description><![CDATA[<h1 id="-span-stylecolorcornflowerblueprops타입-정의란-span">** <span style="color:CornflowerBlue">Props**타입 정의란, </span></h1>
<p>💡 <strong>Props(프롭스)</strong>는 <strong>컴포넌트에 전달하는 값(데이터)</strong>을 의미해.
즉, 부모 컴포넌트에서 자식 컴포넌트로 값을 전달할 때 사용하는 것!</p>
<h2 id="🔍-1️⃣-props-타입-정의란">🔍 1️⃣ Props 타입 정의란?</h2>
<p>📌 Props 타입 정의를 하는 이유</p>
<p>코드를 더 명확하게 작성할 수 있음 (어떤 값이 들어와야 하는지 예측 가능)
잘못된 데이터가 전달되는 걸 방지할 수 있음 (예: 숫자여야 하는데 문자열이 들어오는 경우 방지)
예제: Props 타입을 정의하지 않은 경우</p>
<pre><code class="language-ts">const Button = (props) =&gt; {
  return &lt;button&gt;{props.label}&lt;/button&gt;;
};

export default Button;</code></pre>
<p>🚨 문제점:</p>
<p>props.label이 문자열일지, 숫자일지, 객체일지 명확하지 않음
label이 필수인지, 선택인지 불분명</p>
<h2 id="🔍-2️⃣-props-타입을-정의하는-방법">🔍 2️⃣ Props 타입을 정의하는 방법</h2>
<p>✅ TypeScript에서는 interface 또는 type을 사용해 Props의 타입을 정의할 수 있어!</p>
<p>📌 interface를 사용한 Props 타입 정의</p>
<pre><code class="language-ts">interface ButtonProps {
  label: string; // ✅ label은 문자열이어야 함
  onClick: () =&gt; void; // ✅ onClick은 함수여야 함
}</code></pre>
<p> 이제 위에서 만든 Button 컴포넌트에 적용해보자!</p>
<pre><code class="language-ts">const Button = ({ label, onClick }: ButtonProps) =&gt; {
  return &lt;button onClick={onClick}&gt;{label}&lt;/button&gt;;
};

export default Button;</code></pre>
<p>✅ 이렇게 하면, label이 string이어야 하고, onClick이 함수여야 함을 보장할 수 있어!
💡 이제 onClick에 숫자를 넣거나 label을 number로 설정하면 TypeScript가 오류를 잡아줌.</p>
<h2 id="🔍-3️⃣-bottomsheetprops-이해하기">🔍 3️⃣ BottomSheetProps 이해하기</h2>
<p>아까 본 BottomSheet.tsx에서도 Props 타입을 정의했었지? 다시 보면서 이해해보자!</p>
<pre><code class="language-ts">interface ButtomSheetProps {
  onClose: () =&gt; void;  // ✅ 모달을 닫는 함수 (반환값 없음)
  options: {            // ✅ 버튼 목록 (배열)
    title: string;      // 버튼에 표시될 텍스트
    onClick: () =&gt; void; // 버튼을 클릭했을 때 실행할 함수
    type?: string;      // (선택적) 버튼 스타일 (&#39;primary&#39;, &#39;danger&#39; 등)
  }[];
}</code></pre>
<p>✅ BottomSheetProps를 사용한 컴포넌트</p>
<pre><code class="language-ts">const ButtomSheet = ({ onClose, options }: ButtomSheetProps) =&gt; {
  return (
    &lt;div&gt;
      &lt;button onClick={onClose}&gt;닫기&lt;/button&gt;
      {options.map((option, index) =&gt; (
        &lt;button key={index} onClick={option.onClick}&gt;
          {option.title}
        &lt;/button&gt;
      ))}
    &lt;/div&gt;
  );
};</code></pre>
<p>🚀 이제 onClose는 반드시 함수여야 하고, options는 배열 형태의 데이터여야 함을 보장할 수 있어!</p>
<h3 id="📌-최종-정리">📌 최종 정리</h3>
<table>
<thead>
<tr>
<th>개념</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Props</strong></td>
<td>컴포넌트에 전달하는 값</td>
</tr>
<tr>
<td><strong>Props 타입 정의</strong></td>
<td>Props가 어떤 형태인지 지정하는 것 (string, number, function 등)</td>
</tr>
<tr>
<td><strong><code>interface</code> 사용</strong></td>
<td>객체 형태의 Props 타입을 정의할 때 주로 사용</td>
</tr>
<tr>
<td><strong>타입을 정의하는 이유</strong></td>
<td>코드의 안정성 증가 + 오류 방지</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[[A project ] study(12)  NextAuth 소셜 로그인]]></title>
            <link>https://velog.io/@k_kyu/A-project-study12-NextAuth-%EC%86%8C%EC%85%9C-%EB%A1%9C%EA%B7%B8%EC%9D%B8</link>
            <guid>https://velog.io/@k_kyu/A-project-study12-NextAuth-%EC%86%8C%EC%85%9C-%EB%A1%9C%EA%B7%B8%EC%9D%B8</guid>
            <pubDate>Thu, 13 Feb 2025 08:06:20 GMT</pubDate>
            <description><![CDATA[<h1 id="-span-stylecolorcornflowerbluenextauthspan-를-사용한-소셜-로그인-기능-구현-순서">** <span style="color:CornflowerBlue">NextAuth**</span>, 를 사용한 소셜 로그인 기능 구현 순서</h1>
<h3 id="먼저-span-stylecolorcornflowerblue-nextauth-설치span">먼저, <span style="color:CornflowerBlue"> NextAuth 설치</span></h3>
<pre><code>pnpm add next-auth
</code></pre><h3 id="span-stylecolorcornflowerblue1️⃣-nextauth-설정-파일-appapiauthnextauthts-span"><span style="color:CornflowerBlue">1️⃣ NextAuth 설정 파일 (app/api/auth/[...nextauth].ts) </span></h3>
<pre><code>import NextAuth from &quot;next-auth&quot;;
import GoogleProvider from &quot;next-auth/providers/google&quot;;
import FacebookProvider from &quot;next-auth/providers/facebook&quot;;

export const authOptions = {
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    }),
    FacebookProvider({
      clientId: process.env.FACEBOOK_CLIENT_ID!,
      clientSecret: process.env.FACEBOOK_CLIENT_SECRET!,
    }),
  ],
  callbacks: {
    async session({ session, token }) {
      session.user.id = token.sub;
      return session;
    },
  },
  secret: process.env.NEXTAUTH_SECRET,
};

const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };
</code></pre><p>✔️ GoogleProvider, FacebookProvider → 소셜 로그인 Provider 추가
✔️ callbacks.session → 로그인 후 세션 정보를 설정
✔️ NEXTAUTH_SECRET → 보안을 위한 암호화 키</p>
<h3 id="span-stylecolorcornflowerblue2️⃣-nextauth-설정-파일-appapiauthnextauthts-span"><span style="color:CornflowerBlue">2️⃣ NextAuth 설정 파일 (app/api/auth/[...nextauth].ts) </span></h3>
<pre><code>&quot;use client&quot;;

import { signIn } from &quot;next-auth/react&quot;;

const LoginButton = () =&gt; {
  return (
    &lt;div className=&quot;flex flex-col gap-2&quot;&gt;
      &lt;button
        onClick={() =&gt; signIn(&quot;google&quot;)}
        className=&quot;bg-red-500 text-white px-4 py-2 rounded&quot;
      &gt;
        Google 로그인
      &lt;/button&gt;
      &lt;button
        onClick={() =&gt; signIn(&quot;facebook&quot;)}
        className=&quot;bg-blue-500 text-white px-4 py-2 rounded&quot;
      &gt;
        Facebook 로그인
      &lt;/button&gt;
    &lt;/div&gt;
  );
};

export default LoginButton;
</code></pre><p>✔️ signIn(&quot;google&quot;) → 구글 로그인 실행
✔️ signIn(&quot;facebook&quot;) → 페이스북 로그인 실행</p>
<h3 id="span-stylecolorcornflowerblue-3️⃣-로그인-페이지-appauthloginpagetsxspan"><span style="color:CornflowerBlue"> 3️⃣ 로그인 페이지 (app/(auth)/login/page.tsx)</span></h3>
<pre><code>&quot;use client&quot;;

import LoginButton from &quot;@src/components/LoginButton&quot;;

const LoginPage = () =&gt; {
  return (
    &lt;div className=&quot;flex h-screen flex-col items-center justify-center&quot;&gt;
      &lt;h1 className=&quot;text-2xl font-bold mb-4&quot;&gt;로그인&lt;/h1&gt;
      &lt;LoginButton /&gt;
    &lt;/div&gt;
  );
};

export default LoginPage;
</code></pre><h3 id="span-stylecolorcornflowerblue-4️⃣-로그인-후-대시보드-페이지-appdashboardpagetsxspan"><span style="color:CornflowerBlue"> 4️⃣ 로그인 후 대시보드 페이지 (app/dashboard/page.tsx)</span></h3>
<pre><code>&quot;use client&quot;;

import { useSession, signOut } from &quot;next-auth/react&quot;;

const Dashboard = () =&gt; {
  const { data: session } = useSession();

  if (!session) {
    return &lt;p&gt;로그인이 필요합니다.&lt;/p&gt;;
  }

  return (
    &lt;div className=&quot;flex h-screen flex-col items-center justify-center&quot;&gt;
      &lt;h1 className=&quot;text-2xl font-bold&quot;&gt;환영합니다, {session.user?.name}님!&lt;/h1&gt;
      &lt;button
        onClick={() =&gt; signOut()}
        className=&quot;mt-4 bg-gray-500 text-white px-4 py-2 rounded&quot;
      &gt;
        로그아웃
      &lt;/button&gt;
    &lt;/div&gt;
  );
};

export default Dashboard;
</code></pre><p>✔️ useSession() → 현재 로그인 상태 확인
✔️ signOut() → 로그아웃 기능</p>
<h3 id="span-stylecolorcornflowerblue-5️⃣-환경변수-설정-envlocalspan"><span style="color:CornflowerBlue"> 5️⃣ 환경변수 설정 (.env.local)</span></h3>
<pre><code>GOOGLE_CLIENT_ID=구글에서_발급받은_client_id
GOOGLE_CLIENT_SECRET=구글에서_발급받은_client_secret
FACEBOOK_CLIENT_ID=페이스북에서_발급받은_client_id
FACEBOOK_CLIENT_SECRET=페이스북에서_발급받은_client_secret
NEXTAUTH_SECRET=임의의_랜덤_문자열
NEXTAUTH_URL=http://localhost:3000
</code></pre><p>✔️ 환경변수를 설정해 보안 강화
✔️ NEXTAUTH_SECRET → 보안 강화를 위해 필수</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[A project ] study(11) 소셜로그인 Provider]]></title>
            <link>https://velog.io/@k_kyu/A-project-study11-%EC%86%8C%EC%85%9C%EB%A1%9C%EA%B7%B8%EC%9D%B8-Provider</link>
            <guid>https://velog.io/@k_kyu/A-project-study11-%EC%86%8C%EC%85%9C%EB%A1%9C%EA%B7%B8%EC%9D%B8-Provider</guid>
            <pubDate>Thu, 13 Feb 2025 07:57:28 GMT</pubDate>
            <description><![CDATA[<h1 id="-소셜-로그인span-stylecolorcornflowerblueprovider에-대해span">** 소셜 로그인<span style="color:CornflowerBlue">Provider**에 대해,</span></h1>
<p>웹사이트나 앱에서 별도의 회원가입 없이 기존의 소셜 계정을 사용하여 로그인할 수 있게 하는 기능이야.</p>
<h3 id="span-stylecolorcornflowerblue🔍-소셜-로그인-provider의-개념-span"><span style="color:CornflowerBlue">🔍 소셜 로그인 Provider의 개념 </span></h3>
<ol>
<li>사용자(User) → 로그인 버튼 클릭</li>
<li>웹사이트(Client) → 해당 소셜 로그인 Provider로 인증 요청</li>
<li>소셜 로그인 Provider(Google, Facebook 등) → 사용자의 정보를 확인한 후, 액세스 토큰(access token) 발급</li>
<li>웹사이트(Client) → 발급된 토큰을 받아 사용자 인증을 완료하고, 로그인 처리</li>
</ol>
<p>👉 소셜 로그인 Provider는 패스트푸드점의 키오스크 같은 역할이야!</p>
<ul>
<li>직접 주문(회원가입)하지 않고, 기존 계정(카드)로 바로 결제(로그인) 가능</li>
<li>여러 브랜드(구글, 페이스북 등)를 지원</li>
<li>인증이 끝나면 음식(로그인 정보)이 제공됨</li>
</ul>
<h3 id="span-stylecolorcornflowerblue🔍-소셜-로그인-provider의-핵심-개념-span"><span style="color:CornflowerBlue">🔍 소셜 로그인 Provider의 핵심 개념 </span></h3>
<ul>
<li>OAuth 2.0 → 로그인 과정에서 토큰(Token) 을 이용한 인증 방식</li>
<li>Access Token → 로그인 후 발급되는 사용자 인증 정보</li>
<li>Refresh Token → Access Token이 만료되면 재발급하는 역할</li>
<li>Redirect URI → 로그인 후 사용자를 다시 보낼 웹사이트 주소</li>
<li>Scope(권한 요청) → 로그인 시, 사용자의 어떤 정보를 가져올지 결정</li>
</ul>
<h3 id="span-stylecolorcornflowerblue🔍-소셜-로그인-provider의-동작-방식-span"><span style="color:CornflowerBlue">🔍 소셜 로그인 Provider의 동작 방식 </span></h3>
<p>(예) 구글 로그인, NextAuth 사용</p>
<ol>
<li>구글 로그인 버튼 클릭</li>
<li>OAuth 2.0 인증 요청 → 사용자가 로그인 허용하면, Google이 Access Token을 발급</li>
<li>발급된 Access Token을 서버로 전송</li>
<li>서버에서 토큰을 검증 후, 사용자 로그인 처리</li>
<li>로그인 완료 후, 홈페이지로 리디렉트</li>
</ol>
<h3 id="span-stylecolorcornflowerblue🔍-nextauth에서-provider-설정-예제-span"><span style="color:CornflowerBlue">🔍 NextAuth에서 Provider 설정 예제 </span></h3>
<pre><code>import NextAuth from &#39;next-auth&#39;;
import GoogleProvider from &#39;next-auth/providers/google&#39;;

export default NextAuth({
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    }),
  ],
  callbacks: {
    async session({ session, token }) {
      session.user.id = token.sub;
      return session;
    },
  },
});</code></pre><p>✔️ clientId, clientSecret → 구글 콘솔에서 발급받은 키
✔️ callbacks.session → 로그인 후 세션 정보를 설정</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[A project ] study(10) DBMS(데이터베이스 관리 시스템)을 알아보자!]]></title>
            <link>https://velog.io/@k_kyu/A-project-study10-DBMS%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EA%B4%80%EB%A6%AC-%EC%8B%9C%EC%8A%A4%ED%85%9C%EC%9D%84-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90</link>
            <guid>https://velog.io/@k_kyu/A-project-study10-DBMS%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4-%EA%B4%80%EB%A6%AC-%EC%8B%9C%EC%8A%A4%ED%85%9C%EC%9D%84-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90</guid>
            <pubDate>Thu, 13 Feb 2025 07:37:41 GMT</pubDate>
            <description><![CDATA[<h1 id="-span-stylecolorcornflowerbluedbms데이터베이스-관리-시스템span">** <span style="color:CornflowerBlue">DBMS(데이터베이스 관리 시스템)**</span></h1>
<p>대표적인 DBMS 6개를 쉽고 빠르게 알아보자!</p>
<h2 id="1️⃣-mysql-마이sql">1️⃣ MySQL (마이SQL)</h2>
<p>✔️ 가장 많이 쓰이는 무료 데이터베이스
✔️ 속도가 빠르고, 웹사이트, 앱에서 많이 사용됨
✔️ 유명한 사용처: 네이버, 카카오, 유튜브, 페이스북 일부</p>
<p>📌 예시:
네이버에 가입하면 <strong>내 정보(이름, 이메일, 비밀번호 등)</strong>를 저장할 곳이 필요해.
👉 이런 사용자 정보를 저장하는 데 MySQL이 사용될 수 있어!</p>
<h2 id="2️⃣-mariadb-마리아db">2️⃣ MariaDB (마리아DB)</h2>
<p>✔️ MySQL과 거의 똑같지만, 더 자유롭게 쓸 수 있음
✔️ MySQL이 오라클(Oracle)에 인수된 후, 오픈소스로 만든 DB
✔️ 기업에서도 많이 사용함</p>
<p>📌 예시:
카카오톡 같은 서비스가 있고, MySQL을 쓰던 회사가 비용 부담 없이 더 자유롭게 운영하고 싶다면?
👉 MariaDB로 바꾸면 됨! 기능이 거의 동일해.</p>
<h2 id="3️⃣-postgresql-포스트그레sql">3️⃣ PostgreSQL (포스트그레SQL)</h2>
<p>✔️ 데이터를 정밀하게 관리해야 할 때 사용
✔️ 데이터를 안전하게 보호하는 기능이 뛰어남
✔️ 은행, 금융, 연구소에서 많이 사용
✔️ 무료인데도 강력한 기능 제공</p>
<p>📌 예시:
은행에서 고객의 계좌 잔액을 정확하게 관리해야 해.
👉 PostgreSQL은 실수 없이 데이터를 저장하고 보호하는 데 강점이 있어!</p>
<h2 id="4️⃣-microsoft-sql-server-ms-sql">4️⃣ Microsoft SQL Server (MS SQL)</h2>
<p>✔️ 마이크로소프트(Microsoft)에서 만든 DB
✔️ 윈도우 환경에서 최적화되어 있음
✔️ 기업에서 많이 사용하며, 보안이 강력함</p>
<p>📌 예시:
회사 내부에서 직원 정보, 매출 데이터를 관리하려면?
👉 MS SQL이 많이 사용됨. 특히 MS Office(엑셀, 워드)와도 연동이 쉬움!</p>
<h2 id="5️⃣-mongodb-몽고db">5️⃣ MongoDB (몽고DB)</h2>
<p>✔️ 엑셀처럼 칸(표)으로 데이터를 저장하지 않고, JSON 형태로 저장
✔️ 빠르게 변경할 수 있는 데이터에 적합
✔️ SNS, 쇼핑몰, 게임 데이터에 많이 사용됨</p>
<p>📌 예시:
인스타그램 같은 SNS에서 게시글, 댓글, 좋아요 수가 계속 바뀌지?
👉 이런 데이터를 MongoDB가 빠르게 저장하고 관리할 수 있어.</p>
<h2 id="6️⃣-sqlite-에스큐라이트">6️⃣ SQLite (에스큐라이트)</h2>
<p>✔️ 가장 가벼운 DB, 설치 없이 파일 하나로 동작
✔️ 스마트폰, 작은 앱, 소규모 프로젝트에서 사용됨
✔️ 네트워크 연결 없이 단독으로 동작</p>
<p>📌 예시:
휴대폰 앱에서 메모 앱 같은 간단한 기능을 만들 때, 서버가 없어도 저장이 필요할 때!
👉 SQLite는 별도 서버 없이 파일 하나로 DB를 쓸 수 있음.</p>
<br/>

<h2 id="-span-stylecolorbrown🚀-진짜-쉬운-설명span">** <span style="color:Brown">🚀 진짜 쉬운 설명**</span></h2>
<p>🗂 엑셀처럼 칸으로 정리할 거면? → MySQL, PostgreSQL, MS SQL
📄 자유롭게 저장하고 싶다면? → MongoDB
📱 작은 앱에서 쓴다면? → SQLite</p>
<ul>
<li>웹사이트, 앱 → MySQL, MariaDB</li>
<li>기업용 데이터, 금융 서비스 → PostgreSQL, MS SQL</li>
<li>SNS, 쇼핑몰, 게임 → MongoDB</li>
<li>모바일 앱, 간단한 데이터 저장 → SQLite</li>
</ul>
<h4 id="데이터베이스-한눈에-비교">데이터베이스 한눈에 비교!</h4>
<table>
<thead>
<tr>
<th>DB 종류</th>
<th>특징</th>
<th>대표 사용처</th>
</tr>
</thead>
<tbody><tr>
<td><strong>MySQL</strong></td>
<td>빠르고 많이 사용됨</td>
<td>웹사이트, 앱 (네이버, 유튜브)</td>
</tr>
<tr>
<td><strong>MariaDB</strong></td>
<td>MySQL 대체, 무료</td>
<td>오픈소스 기반 서비스</td>
</tr>
<tr>
<td><strong>PostgreSQL</strong></td>
<td>정밀하고 안전</td>
<td>은행, 금융, 연구소</td>
</tr>
<tr>
<td><strong>MS SQL Server</strong></td>
<td>기업용, MS 제품과 호환</td>
<td>기업 내부 시스템</td>
</tr>
<tr>
<td><strong>MongoDB</strong></td>
<td>유연하고 빠름</td>
<td>SNS, 쇼핑몰, 게임</td>
</tr>
<tr>
<td><strong>SQLite</strong></td>
<td>가벼움, 파일 하나로 동작</td>
<td>모바일 앱, 간단한 프로젝트</td>
</tr>
</tbody></table>
<hr>
<h3 id="-span-stylecolorlightsteelblue✏️-더-알아가기-span">** <span style="color:LightSteelBlue">✏️ 더 알아가기 **</span></h3>
<h3 id="1️⃣-dbms가-필요한-이유는">1️⃣ DBMS가 필요한 이유는?</h3>
<p>✅ 데이터 정리 &amp; 관리 → 파일로 저장하면 관리하기 어려움
✅ 빠른 검색 &amp; 수정 → 필요한 정보를 빠르게 찾고, 수정 가능
✅ 동시 접속 &amp; 보안 → 여러 사람이 동시에 사용할 수 있고, 보안 설정 가능
✅ 데이터 무결성 보장 → 잘못된 데이터가 들어가는 걸 방지</p>
<p>예를 들어,
📌 웹사이트 회원 정보를 파일에 저장하면? → 파일이 커지고, 검색이 느려짐
📌 DBMS를 사용하면? → 데이터가 체계적으로 정리되고, 빠르게 찾을 수 있음!</p>
<h3 id="2️⃣-dbms의-기본-개념">2️⃣ DBMS의 기본 개념</h3>
<p>DBMS를 이해하려면, 데이터가 어떻게 저장되는지 먼저 알아야 해.</p>
<p>📌 1. 데이터베이스 (Database)
✔️ 여러 개의 테이블(표)로 구성된 데이터 저장 공간
✔️ 하나의 웹사이트(서비스)는 하나의 데이터베이스를 가짐</p>
<p>💡 예시:</p>
<p>KINS_DB (KiNS 서비스의 데이터베이스)
E_COMMERCE_DB (쇼핑몰 데이터베이스)</p>
<h3 id="📌-2-테이블-table">📌 2. 테이블 (Table)</h3>
<p>✔️ 데이터가 저장되는 표 형태의 구조
✔️ 행(Row) = 데이터 한 줄, 열(Column) = 속성(이름, 나이, 이메일 등)</p>
<p>💡 예시: 회원 테이블(User Table)</p>
<table>
<thead>
<tr>
<th>ID</th>
<th>이름</th>
<th>나이</th>
<th>이메일</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>홍길동</td>
<td>30</td>
<td><a href="mailto:hong@example.com">hong@example.com</a></td>
</tr>
<tr>
<td>2</td>
<td>김영희</td>
<td>25</td>
<td><a href="mailto:kim@example.com">kim@example.com</a></td>
</tr>
</tbody></table>
<h3 id="📌-3-sql-structured-query-language">📌 3. SQL (Structured Query Language)</h3>
<p>✔️ 데이터베이스와 소통하는 언어
✔️ 데이터를 추가, 수정, 삭제, 조회하는 데 사용됨</p>
<p>💡 예시</p>
<pre><code>-- 회원 테이블에서 모든 데이터 조회
SELECT * FROM users;

-- 회원 추가
INSERT INTO users (이름, 나이, 이메일) VALUES (&#39;박철수&#39;, 28, &#39;park@example.com&#39;);

-- 회원 정보 수정
UPDATE users SET 나이 = 29 WHERE ID = 1;

-- 회원 삭제
DELETE FROM users WHERE ID = 2;
</code></pre><h3 id="3️⃣-dbms의-주요-기능">3️⃣ DBMS의 주요 기능</h3>
<table>
<thead>
<tr>
<th>기능</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>데이터 저장</td>
<td>데이터를 테이블에 저장하고 구조적으로 관리</td>
</tr>
<tr>
<td>데이터 조회</td>
<td>필요한 데이터를 빠르게 찾고 검색</td>
</tr>
<tr>
<td>데이터 수정</td>
<td>기존 데이터를 업데이트</td>
</tr>
<tr>
<td>데이터 삭제</td>
<td>불필요한 데이터를 제거</td>
</tr>
<tr>
<td>보안 &amp; 권한 관리</td>
<td>사용자별 데이터 접근 권한 설정</td>
</tr>
<tr>
<td>트랜잭션 관리</td>
<td>데이터가 정확하고 안정적으로 저장되도록 보장</td>
</tr>
</tbody></table>
<p>💡 트랜잭션(Transaction)이란?
✅ 데이터의 일관성을 유지하기 위해 작업 단위를 묶어서 처리하는 것
✅ 중간에 오류가 나면 모두 취소(ROLLBACK) 되거나,
✅ 성공하면 완전히 저장(COMMIT) 됨.</p>
<p>예시: 은행 송금</p>
<p>A의 계좌에서 100만 원을 출금
B의 계좌에 100만 원을 입금
✔️ 둘 다 성공해야 완료! (트랜잭션이 보장됨)
✔️ 하나라도 실패하면? 다시 원상복구(Rollback)</p>
<h3 id="-span-stylecolorbrown🚀-진짜-쉬운-설명span-1">** <span style="color:Brown">🚀 진짜 쉬운 설명**</span></h3>
<p>🔹 RDBMS(MySQL, PostgreSQL 등) = 엑셀
➡️ 칸(테이블) 안에 데이터를 정리해서 저장
➡️ 모든 데이터가 체계적이고 명확한 관계를 가짐</p>
<p>🔹 NoSQL(MongoDB, Redis 등) = 포스트잇
➡️ 자유롭게 데이터를 붙이고 수정 가능
➡️ 빠르게 저장하고 변경해야 할 때 유용</p>
<p>데이터베이스는 우리가 사용하는 모든 웹 서비스의 핵심이야!
🚀 DBMS를 이해하면, 데이터를 더 효율적으로 다룰 수 있어! 💡</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[A project ] study(9) use-funnel]]></title>
            <link>https://velog.io/@k_kyu/A-project-study9-use-funnel</link>
            <guid>https://velog.io/@k_kyu/A-project-study9-use-funnel</guid>
            <pubDate>Tue, 11 Feb 2025 07:30:28 GMT</pubDate>
            <description><![CDATA[<h1 id="-span-stylecolorcornflowerblueuse-funnel이란-span">** <span style="color:CornflowerBlue">use-funnel**이란, </span></h1>
<p>모바일 네비게이션에 최적화된 단계별 상태 관리 라이브러리야.
<a href="https://use-funnel.slash.page/ko">@use-funnel</a></p>
<h2 id="-span-stylecolorbrown📍핵심-설명span">** <span style="color:Brown">📍핵심 설명**</span></h2>
<ul>
<li><strong>useFunnel은 **엘리베이터 버튼</strong>이라고 생각해봐!
단계를 배열(steps)로 관리하고, nextStep()과 previousStep()을 사용하여 손쉽게 단계 이동을 할 수 있도록 도와주는 훅이야.</br>
- **useFunnel의 핵심 기능**
- 여러 단계를 배열로 저장 (steps)
- nextStep()을 호출하면 다음 단계로 이동
- previousStep()을 호출하면 이전 단계로 이동
- 현재 단계를 step으로 반환 (step === steps[currentIndex])
- 마지막 단계(isLastStep), 첫 번째 단계(isFirstStep) 여부 확인 가능


</li>
</ul>
 <br/>


<h3 id="span-stylecolorcornflowerblue🔍--use-funnel의-사용-예제span"><span style="color:CornflowerBlue">🔍  use-funnel의 사용 예제</span></h3>
<p><strong>1️⃣  use-funnel 설치</strong></p>
<table>
<thead>
<tr>
<th>패키지 매니저</th>
<th>설치 명령어</th>
</tr>
</thead>
<tbody><tr>
<td><strong>pnpm</strong></td>
<td><code>pnpm add @use-funnel/react-router-dom</code></td>
</tr>
<tr>
<td></td>
<td><code>pnpm add @use-funnel/next</code></td>
</tr>
<tr>
<td></td>
<td><code>pnpm add @use-funnel/react-navigation-native</code></td>
</tr>
<tr>
<td></td>
<td><code>pnpm add @use-funnel/browser</code></td>
</tr>
<tr>
<td><strong>yarn</strong></td>
<td><code>yarn add @use-funnel/react-router-dom</code></td>
</tr>
<tr>
<td></td>
<td><code>yarn add @use-funnel/next</code></td>
</tr>
<tr>
<td></td>
<td><code>yarn add @use-funnel/react-navigation-native</code></td>
</tr>
<tr>
<td></td>
<td><code>yarn add @use-funnel/browser</code></td>
</tr>
<tr>
<td>❗ 하지만, use-funnel 패키지는 공식 라이브러리가 아니므로, 직접 구현해야 함!</td>
<td></td>
</tr>
</tbody></table>
<p><strong>2️⃣ useFunnel.ts 생성 (hooks/useFunnel.ts)</strong></p>
<pre><code>import { useState } from &#39;react&#39;;

const useFunnel = (steps: string[]) =&gt; {
  const [currentIndex, setCurrentIndex] = useState(0);

  const nextStep = () =&gt; {
    setCurrentIndex((prev) =&gt; (prev &lt; steps.length - 1 ? prev + 1 : prev));
  };

  const previousStep = () =&gt; {
    setCurrentIndex((prev) =&gt; (prev &gt; 0 ? prev - 1 : prev));
  };

  return {
    step: steps[currentIndex],
    nextStep,
    previousStep,
    isLastStep: currentIndex === steps.length - 1,
    isFirstStep: currentIndex === 0,
  };
};

export default useFunnel;
</code></pre><h4 id="🙌-단계를-배열steps로-관리-nextstep-previousstep을-이용하여-이동">🙌 단계를 배열(steps)로 관리, nextStep(), previousStep()을 이용하여 이동</h4>
<p><strong>3️⃣ SignupPage.tsx에서 useFunnel 적용</strong></p>
<pre><code>import useFunnel from &#39;@src/hooks/useFunnel&#39;;
import AgreementList from &#39;@src/components/AgreementList&#39;;
import NameInputStep from &#39;@src/components/NameInputStep&#39;;
import PhoneInputStep from &#39;@src/components/PhoneInputStep&#39;;
import SignupSuccessPage from &#39;@src/components/SignupSuccessPage&#39;;

const SignupPage = () =&gt; {
  const { step, nextStep, previousStep } = useFunnel([
    &#39;agreement&#39;,
    &#39;name-input&#39;,
    &#39;phone-input&#39;,
    &#39;success&#39;
  ]);

  return (
    &lt;div&gt;
      {step === &#39;agreement&#39; &amp;&amp; &lt;AgreementList onNext={nextStep} /&gt;}
      {step === &#39;name-input&#39; &amp;&amp; &lt;NameInputStep onNext={nextStep} /&gt;}
      {step === &#39;phone-input&#39; &amp;&amp; &lt;PhoneInputStep onNext={nextStep} /&gt;}
      {step === &#39;success&#39; &amp;&amp; &lt;SignupSuccessPage onNext={nextStep} /&gt;}

      {/* 이전 단계 버튼 */}
      {step !== &#39;agreement&#39; &amp;&amp; (
        &lt;button onClick={previousStep} className=&quot;mt-4 px-4 py-2 bg-gray-300 rounded&quot;&gt;
          이전 단계
        &lt;/button&gt;
      )}
    &lt;/div&gt;
  );
};

export default SignupPage;
</code></pre> <br/>

<h2 id="-span-stylecolorbrown🚀-진짜-쉬운-설명span">** <span style="color:Brown">🚀 진짜 쉬운 설명**</span></h2>
<p><strong>useFunnel 적용 순서</strong>
1️⃣ 프로젝트 구조 설정 → hooks/, components/, pages/ 폴더 구성
2️⃣ 커스텀 훅 useFunnel.ts 생성 → 단계별 관리 (nextStep(), previousStep())
3️⃣ SignupPage.tsx에서 useFunnel 사용 → 단계별 UI 적용
4️⃣ 단계 이동 로직 추가 → 버튼 클릭 시 nextStep() &amp; previousStep() 실행</p>
<hr>
<h3 id="-span-stylecolorlightsteelblue✏️-더-알아가기-span">** <span style="color:LightSteelBlue">✏️ 더 알아가기 **</span></h3>
<ul>
<li>어떤 경우에 어떤 방식이 좋을까?</li>
</ul>
<p><strong>1️⃣ useState가 더 적합한 경우</strong>
모든 체크박스가 한 화면에서 한 번에 보여야 하는 경우
필수/선택 항목이 단계별로 구분될 필요가 없는 경우
체크박스를 사용자가 자유롭게 조작할 수 있어야 하는 경우</p>
<p><strong>2️⃣ useFunnel이 더 적합한 경우</strong>
필수 항목을 먼저 체크하고, 이후 선택 항목을 보여줘야 하는 경우
체크박스를 단계를 나눠서 점진적으로 사용자에게 보여주고 싶은 경우
폼(회원가입, 동의 과정)이 여러 단계로 나뉘어 있는 경우</p>
<blockquote>
</blockquote>
<p>✔️ <strong>useState</strong> : 체크박스를 항상 한 화면에 보이게 하고, 개별적으로 체크할 때 적합
✔️ <strong>useFunnel</strong> : 체크 단계를 진행하면서 필요한 체크박스만 보이게 하고 싶을 때 적합</p>
<p>체크박스가 한 번에 다 보여야 하면 useState, 단계별로 나눠서 진행해야 하면 useFunnel을 선택하면 돼!</p>
<ul>
<li>한눈에 비교하기</li>
</ul>
<table>
<thead>
<tr>
<th>비교 항목</th>
<th><code>useState</code> 사용</th>
<th><code>useFunnel</code> 사용</th>
</tr>
</thead>
<tbody><tr>
<td><strong>체크박스 상태 관리</strong></td>
<td>개별 <code>true/false</code> 값을 직접 조작</td>
<td><code>step</code> 상태를 변경하여 관리</td>
</tr>
<tr>
<td><strong>사용 방식</strong></td>
<td><code>setState</code>로 개별 상태 업데이트</td>
<td><code>nextStep()</code>으로 단계 이동</td>
</tr>
<tr>
<td><strong>체크박스 표시 방식</strong></td>
<td>모든 체크박스가 한 번에 보임</td>
<td>단계별로 필요한 체크박스만 표시</td>
</tr>
<tr>
<td><strong>제어 방식</strong></td>
<td>모든 체크박스를 개별적으로 조작 가능</td>
<td>특정 단계에서 특정 체크박스만 활성화 가능</td>
</tr>
<tr>
<td><strong>단계 이동</strong></td>
<td>없음 (모든 체크박스가 항상 표시됨)</td>
<td><code>nextStep()</code>, <code>previousStep()</code> 사용 가능</td>
</tr>
<tr>
<td><strong>가독성</strong></td>
<td>상태가 많아질수록 관리가 복잡해짐</td>
<td>논리적으로 단계를 나눠서 깔끔하게 관리 가능</td>
</tr>
<tr>
<td><strong>유지보수</strong></td>
<td>새로운 체크박스 추가 시 <code>useState</code> 구조 수정 필요</td>
<td>단계 배열(<code>steps</code>)에 추가하면 쉽게 관리 가능</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[[A project ] study(8) 피그마 아이콘 설정하기]]></title>
            <link>https://velog.io/@k_kyu/A-project-study8-%ED%94%BC%EA%B7%B8%EB%A7%88-%EC%95%84%EC%9D%B4%EC%BD%98-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@k_kyu/A-project-study8-%ED%94%BC%EA%B7%B8%EB%A7%88-%EC%95%84%EC%9D%B4%EC%BD%98-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0</guid>
            <pubDate>Thu, 16 Jan 2025 11:50:57 GMT</pubDate>
            <description><![CDATA[<h1 id="피그마span-stylecolorcornflowerblue-아이콘-설정-방법span">피그마<strong><span style="color:CornflowerBlue"> 아이콘 설정</strong> 방법,</span></h1>
<p>프로젝트에 들어가기 앞서, 아이콘을 먼저 공통화 할일이 생길거야.</p>
<h3 id="span-stylecolorcornflowerblue🔍-아이콘-설정-순서-span"><span style="color:CornflowerBlue">🔍 아이콘 설정 순서 </span></h3>
<p><strong>1️⃣ 폴더 구조 설정</strong>
피그마에서 내보낸 아이콘과 로고 파일을 재사용 가능하고 유지보수하기 쉽게 관리하려면 
다음과 같은 폴더 구조를 추천해.</p>
<pre><code class="language-text">src/
├── assets/
│   ├── icons/
│   │   ├── search.svg
│   │   ├── user.svg
│   │   └── home.svg
│   ├── logos/
│   │   ├── main-logo.svg
│   │   └── secondary-logo.svg
│   └── index.ts</code></pre>
<ul>
<li>icons/: 작은 크기의 아이콘들(예: 검색, 사용자, 홈 등)을 저장.</li>
<li>logos/: 회사 로고 또는 브랜드를 대표하는 큰 이미지를 저장.</li>
<li>index.ts: 각 파일을 import/export하는 코드로 작성해 아이콘과 로고를 한 곳에서 관리.</li>
</ul>
<br/>

<p><strong>2️⃣ 피그마에서 내보내기</strong></p>
<ul>
<li><p><strong>아이콘 및 로고 정리</strong></p>
<ol>
<li><p>피그마에서 아이콘 및 로고를 선택:
사용하는 모든 아이콘과 로고를 한 곳에 모아 <strong>프레임(Frame)</strong>으로 그룹화.
각 아이콘/로고에 의미 있는 이름을 부여하세요(예: search, home, main-logo).</p>
</li>
<li><p>SVG 형식으로 내보내기:
아이콘과 로고를 <strong>벡터 형식(SVG)</strong>으로 내보내면 화면 크기에 상관없이 선명하게 유지해.</p>
</li>
</ol>
</li>
<li><p><strong>피그마 내보내기 설정</strong></p>
</li>
</ul>
<ol>
<li><p>아이콘과 로고를 선택:
내보낼 아이콘 또는 로고 선택.</p>
</li>
<li><p>Export 설정:
오른쪽 패널에서 Export 섹션 클릭.
SVG 형식으로 설정.</p>
</li>
<li><p>내보내기 실행:
내보내기를 실행하면, 선택한 아이콘/로고를 .svg 파일로 저장할 수 있어.</p>
</li>
<li><p>SVG 최적화 (선택 사항):
내보낸 SVG 파일이 불필요하게 큰 경우 SVGO와 같은 도구로 최적화할 수 있어.</p>
</li>
</ol>
<br/>

<p><strong>3️⃣ VSCode에 아이콘 및 로고 추가</strong></p>
<ol>
<li>내보낸 파일 이동
피그마에서 내보낸 .svg 파일을 다음 폴더로 옮겨.</li>
</ol>
<pre><code>아이콘: src/assets/icons/
로고: src/assets/logos/</code></pre><ol start="2">
<li>index.ts 파일로 관리
index.ts 파일을 생성하여 아이콘과 로고를 모듈화해. 
이렇게 하면 파일 경로를 단축하고, 쉽게 가져다 쓸 수 있어!</li>
</ol>
<pre><code class="language-ts">// src/assets/index.ts

// 아이콘
export { default as SearchIcon } from &#39;./icons/search.svg&#39;;
export { default as UserIcon } from &#39;./icons/user.svg&#39;;
export { default as HomeIcon } from &#39;./icons/home.svg&#39;;

// 로고
export { default as MainLogo } from &#39;./logos/main-logo.svg&#39;;
export { default as SecondaryLogo } from &#39;./logos/secondary-logo.svg&#39;;</code></pre>
<ol start="3">
<li>컴포넌트에서 사용하기
SVG 파일을 컴포넌트에서 직접 사용하려면 @svgr/webpack 또는 next/image를 사용해.</li>
</ol>
<ul>
<li>SVG를 컴포넌트로 사용:
Next.js에서는 SVG 파일을 컴포넌트처럼 사용할 수 있습니다<pre><code class="language-ts">import { SearchIcon, MainLogo } from &#39;../assets&#39;;
</code></pre>
</li>
</ul>
<p>const Header = () =&gt; {
  return (
    <header className="flex items-center p-4">
      <MainLogo className="w-32 h-auto" />
      <SearchIcon className="w-6 h-6 ml-auto" />
    </header>
  );
};</p>
<p>export default Header;</p>
<pre><code>
 - 이미지 파일로 처리하기 (Next.js next/image):
SVG 파일 대신 PNG/JPG를 사용하는 경우
```ts
import Image from &#39;next/image&#39;;
import MainLogo from &#39;../assets/logos/main-logo.png&#39;;

const Header = () =&gt; {
  return (
    &lt;header className=&quot;flex items-center p-4&quot;&gt;
      &lt;Image src={MainLogo} alt=&quot;Main Logo&quot; width={128} height={64} /&gt;
    &lt;/header&gt;
  );
};

export default Header;</code></pre><br/>

<h2 id="-span-stylecolorbrown🚀-진짜-쉬운-설명span">** <span style="color:Brown">🚀 진짜 쉬운 설명**</span></h2>
<ul>
<li><strong>파일 이름 규칙:</strong> 파일 이름은 소문자-케밥-케이스(search-icon.svg)를 사용하여 명확하게 구분.</li>
<li><strong>아이콘 시스템 구축:</strong> 같은 크기(예: 24x24px)로 내보내 아이콘 크기 일관성 유지.</li>
<li><strong>피그마 업데이트와 동기화:</strong> 아이콘/로고가 변경되었을 때, 같은 방식으로 업데이트하고 VSCode에 반영.</li>
</ul>
<blockquote>
<ol>
<li>피그마에서 SVG 형식으로 아이콘과 로고를 내보내기.</li>
<li>VSCode에서 assets/icons와 assets/logos 폴더로 관리.</li>
<li>index.ts를 활용해 한 곳에서 import/export 관리.</li>
<li>컴포넌트에서 쉽게 가져다 사용.</li>
</ol>
</blockquote>
<p>이 방식으로 작업하면 아이콘과 로고를 체계적으로 관리하고, 프로젝트의 일관성을 유지할 수 있어!</p>
<hr>
<h3 id="-span-stylecolorlightsteelblue✏️-더-알아가기-span">** <span style="color:LightSteelBlue">✏️ 더 알아가기 **</span></h3>
<ul>
<li><strong>index.ts로 모듈화란?</strong>
로고 파일을 여러 곳에서 사용하려면, 각 파일을 직접 import할 수도 있지만 index.ts로 관리하면 더 편리해! index.ts는 폴더의 모든 파일을 한 곳에서 모아 가져올 수 있는 역할을 하는거지.</li>
</ul>
<pre><code class="language-ts">//src/assets/logos/index.ts
import MainLogo from &#39;./main-logo.svg&#39;;
import SecondaryLogo from &#39;./secondary-logo.svg&#39;;

export { MainLogo, SecondaryLogo };
//index.ts 파일에서 로고 파일들을 import하고, 다시 export하여, 다른 파일에서는 index.ts를 통해 로고를 가져올 수 있어.</code></pre>
<ul>
<li>왜 편리한가?<ul>
<li>모듈화하지 않은 경우<pre><code class="language-ts">import MainLogo from &#39;@/assets/logos/main-logo.svg&#39;;
import SecondaryLogo from &#39;@/assets/logos/secondary-logo.svg&#39;;</code></pre>
</li>
</ul>
</li>
<li>모듈화한 경우<pre><code class="language-ts">import { MainLogo, SecondaryLogo } from &#39;@/assets/logos&#39;;</code></pre>
🙌 경로가 짧아지고 관리하기 쉬워져!</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[구글링 잘~ 하는 법]]></title>
            <link>https://velog.io/@k_kyu/%EA%B5%AC%EA%B8%80%EB%A7%81-%EC%9E%98-%ED%95%98%EB%8A%94-%EB%B2%95</link>
            <guid>https://velog.io/@k_kyu/%EA%B5%AC%EA%B8%80%EB%A7%81-%EC%9E%98-%ED%95%98%EB%8A%94-%EB%B2%95</guid>
            <pubDate>Tue, 14 Jan 2025 07:51:27 GMT</pubDate>
            <description><![CDATA[<h3 id="span-stylecolorcornflowerblue🔍-google-검색span"><span style="color:CornflowerBlue">🔍 google 검색</span></h3>
<ul>
<li><p>질문의 요점을 잘 정리해서  검색해 봐.</p>
<ul>
<li>ex) express.js 라우터 xxx 에러 문제</li>
</ul>
</li>
<li><p>그냥 검색하는 것보다 구글 검색연산자를 활용해봐.</p>
<ul>
<li>ex) typeorm site:stackoverflow.com</li>
</ul>
</li>
<li><p>한글로만 검색하지말고 영어로 검색해 봐.</p>
<ul>
<li>ex) nest.js 프리즈마 연동(x) → nest.js prisma example(o)</li>
</ul>
</li>
</ul>
<br/>

<h3 id="span-stylecolorcornflowerblue🔍-github-검색span"><span style="color:CornflowerBlue">🔍 github 검색</span></h3>
<ul>
<li>All github 으로 검색하여 reference 확인</li>
<li>우리가 고민하는 건 100% 남이 고민한 것.(all gitHub으로 변경)</li>
</ul>
<br/>

<h3 id="span-stylecolorcornflowerblue🔍-예시-찾는-키워드span"><span style="color:CornflowerBlue">🔍 예시 찾는 키워드</span></h3>
<p>사용하고자 하는 라이브러리나 프레임워크가 있을 때 아래 키워드를 사용하면 예시를 쉽게 찾을 수 있어.</p>
<ul>
<li>OOO sample</li>
<li>OOO tutorial</li>
<li>OOO example</li>
<li>OOO reference</li>
<li><strong>OOO best practices</strong></li>
<li><strong>OOO not working</strong></li>
<li>OOO documentation</li>
<li>how to OOO</li>
</ul>
<p>ex) nestjs google OAuth2.0 example</p>
<br/>

<h3 id="span-stylecolorcornflowerblue🔍-참고-documentspan"><span style="color:CornflowerBlue">🔍 참고 Document</span></h3>
<ul>
<li><p>[Error | javascript MDN] (<a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Error">https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Error</a>)</p>
</li>
<li><p>[연산자를 이용하여 검색 범위 좁히기] (<a href="https://support.google.com/cloudsearch/answer/6172299?hl=ko&amp;co=GENIE.Platform%3DDesktop">https://support.google.com/cloudsearch/answer/6172299?hl=ko&amp;co=GENIE.Platform%3DDesktop</a>)</p>
</li>
<li><p>[6개월간의 TIL회고] (<a href="https://wayhome25.github.io/til/2017/08/14/TIL-for-6-months/">https://wayhome25.github.io/til/2017/08/14/TIL-for-6-months/</a>)</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[VSCode, 컴퓨터 단축키(맥과 윈도우)]]></title>
            <link>https://velog.io/@k_kyu/VSCode-%EC%BB%B4%ED%93%A8%ED%84%B0-%EB%8B%A8%EC%B6%95%ED%82%A4%EB%A7%A5%EA%B3%BC-%EC%9C%88%EB%8F%84%EC%9A%B0</link>
            <guid>https://velog.io/@k_kyu/VSCode-%EC%BB%B4%ED%93%A8%ED%84%B0-%EB%8B%A8%EC%B6%95%ED%82%A4%EB%A7%A5%EA%B3%BC-%EC%9C%88%EB%8F%84%EC%9A%B0</guid>
            <pubDate>Tue, 14 Jan 2025 07:41:58 GMT</pubDate>
            <description><![CDATA[<h3 id="span-stylecolorcornflowerblue-vscode-단축키-정리span"><span style="color:CornflowerBlue"> VSCode 단축키 정리</span></h3>
<table>
<thead>
<tr>
<th>단축키</th>
<th>설명</th>
<th>중요도</th>
<th>비고</th>
</tr>
</thead>
<tbody><tr>
<td>Ctrl + Space</td>
<td>자동 완성을 트리거 합니다</td>
<td>매우 중요</td>
<td>전 mac 사용 중이라 Shift + Space로 바꿔서 사용.</td>
</tr>
<tr>
<td>Ctrl + /</td>
<td>현재 줄의 주석을 토글합니다</td>
<td>매우 중요</td>
<td></td>
</tr>
<tr>
<td>Ctrl + S</td>
<td>현재 파일을 저장합니다</td>
<td>매우 중요</td>
<td></td>
</tr>
<tr>
<td>Ctrl + C</td>
<td>현재 선택한 텍스트를 복사합니다</td>
<td>매우 중요</td>
<td></td>
</tr>
<tr>
<td>Ctrl + X</td>
<td>현재 선택한 텍스트를 잘라냅니다</td>
<td>매우 중요</td>
<td></td>
</tr>
<tr>
<td>Ctrl + V</td>
<td>클립보드에 있는 텍스트를 붙여넣습니다</td>
<td>매우 중요</td>
<td></td>
</tr>
<tr>
<td>Ctrl + Z</td>
<td>마지막 작업을 되돌립니다</td>
<td>매우 중요</td>
<td></td>
</tr>
<tr>
<td>Ctrl + Shift + Z</td>
<td>되돌린 작업을 다시 실행합니다</td>
<td>매우 중요</td>
<td></td>
</tr>
<tr>
<td>Ctrl + F</td>
<td>현재 파일에서 텍스트를 찾습니다</td>
<td>매우 중요</td>
<td></td>
</tr>
<tr>
<td>Ctrl + Shift + F</td>
<td>전체 파일에서 텍스트를 찾습니다</td>
<td>매우 중요</td>
<td></td>
</tr>
<tr>
<td>Ctrl + P</td>
<td>파일을 빠르게 열거나 검색합니다</td>
<td>매우 중요</td>
<td></td>
</tr>
<tr>
<td>Ctrl + Shift + P</td>
<td>명령 팔레트를 엽니다</td>
<td>매우 중요</td>
<td>F1 도 가능</td>
</tr>
<tr>
<td>Ctrl + B</td>
<td>사이드바를 토글합니다</td>
<td>중요</td>
<td></td>
</tr>
<tr>
<td>Ctrl + `</td>
<td>터미널을 토글합니다</td>
<td>중요</td>
<td>전 Ctrl + J 로 사용.</td>
</tr>
<tr>
<td>Ctrl + K + [</td>
<td>선택한 영역 접기</td>
<td>중요</td>
<td></td>
</tr>
<tr>
<td>Ctrl + K + ]</td>
<td>선택한 영역 열기</td>
<td>중요</td>
<td></td>
</tr>
<tr>
<td>Ctrl + K + 0</td>
<td>모든 영역 접기</td>
<td>중요</td>
<td></td>
</tr>
<tr>
<td>Ctrl + K + J</td>
<td>모든 영역 열기</td>
<td>중요</td>
<td></td>
</tr>
<tr>
<td>F12</td>
<td>정의로 이동합니다</td>
<td>중요</td>
<td>함수 정의 등</td>
</tr>
<tr>
<td>Alt + Shift + ↑</td>
<td>현재 줄의 코드를 복사합니다</td>
<td>중요</td>
<td></td>
</tr>
<tr>
<td>Alt + ↑ or ↓</td>
<td>현재 줄의 코드를 아래 / 위로 이동시킵니다</td>
<td>중요</td>
<td></td>
</tr>
</tbody></table>
<h3 id="span-stylecolorcornflowerblue-컴퓨터-기본-단축키-정리span"><span style="color:CornflowerBlue"> 컴퓨터 기본 단축키 정리</span></h3>
<p>Mac 사용자는 아래 표에 보이는것 처럼, 
Ctrl을 Command로 Alt를 Option으로 알아들으면 돼!</p>
<table>
<thead>
<tr>
<th>단축키</th>
<th>설명</th>
<th>중요도</th>
<th>비고</th>
</tr>
</thead>
<tbody><tr>
<td>Ctrl + C</td>
<td>선택한 항목을 복사합니다</td>
<td>매우 중요</td>
<td></td>
</tr>
<tr>
<td>Ctrl + V</td>
<td>복사하거나 잘라낸 항목을 붙여넣습니다</td>
<td>매우 중요</td>
<td></td>
</tr>
<tr>
<td>Ctrl + X</td>
<td>선택한 항목을 잘라냅니다 (복사와 다름)</td>
<td>매우 중요</td>
<td></td>
</tr>
<tr>
<td>Ctrl + Z</td>
<td>마지막 작업을 취소합니다</td>
<td>매우 중요</td>
<td></td>
</tr>
<tr>
<td>Ctrl + Y</td>
<td>취소한 마지막 작업을 다시 실행합니다</td>
<td>매우 중요</td>
<td></td>
</tr>
<tr>
<td>Ctrl + F</td>
<td>현재 문서에서 검색합니다</td>
<td>매우 중요</td>
<td></td>
</tr>
<tr>
<td>Ctrl + S</td>
<td>현재 문서를 저장합니다</td>
<td>매우 중요</td>
<td></td>
</tr>
<tr>
<td>Ctrl + A</td>
<td>모든 항목을 선택합니다</td>
<td>매우 중요</td>
<td></td>
</tr>
<tr>
<td>Alt + Tab</td>
<td>열린 프로그램 간 전환합니다</td>
<td>중요</td>
<td>맥은 Command + Tab 사용</td>
</tr>
<tr>
<td>F5 또는 Ctrl + R</td>
<td>웹페이지를 새로 고칩니다</td>
<td>중요</td>
<td></td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[[A project ] study(7) React Query의 UX향상 옵션]]></title>
            <link>https://velog.io/@k_kyu/A-project-study7-React-Query%EC%9D%98-UX%ED%96%A5%EC%83%81-%EC%98%B5%EC%85%98</link>
            <guid>https://velog.io/@k_kyu/A-project-study7-React-Query%EC%9D%98-UX%ED%96%A5%EC%83%81-%EC%98%B5%EC%85%98</guid>
            <pubDate>Tue, 14 Jan 2025 07:32:47 GMT</pubDate>
            <description><![CDATA[<h1 id="react-query의--span-stylecolorcornflowerblueux향상-옵션span">React Query의 ** <span style="color:CornflowerBlue">UX향상 옵션**,</span></h1>
<p>여기에서 소개할 기능들은 사용자 경험을 향상시키기 위해 꼭 알아야 하는 핵심 옵션이야!</p>
<h3 id="span-stylecolorcornflowerblue🔍-optimistic-updates-낙관적-업데이트span"><span style="color:CornflowerBlue">🔍 Optimistic Updates (낙관적 업데이트)</span></h3>
<ul>
<li>서버 응답 전에 화면을 먼저 업데이트하는 기능.</li>
<li>사용자가 변경 사항을 즉시 확인할 수 있어 빠르고 부드러운 경험을 제공.</li>
<li>(예제)<pre><code class="language-js">const mutation = useMutation(
(newLike) =&gt; axios.post(&#39;/api/like&#39;, newLike),
{
  onMutate: (newLike) =&gt; {
    queryClient.setQueryData([&#39;likes&#39;], (oldLikes) =&gt; [...oldLikes, newLike]);
  },
  onError: () =&gt; {
    queryClient.invalidateQueries([&#39;likes&#39;]); // 실패 시 상태 복구
  },
}
);</code></pre>
🙌 쉽게 비유하자면: 좋아요 버튼을 누르자마자 숫자가 올라가는 것처럼, 결과를 믿고 미리 보여주는 경험이야!</li>
</ul>
<br/>

<h3 id="span-stylecolorcornflowerblue🔍--prefetching-미리-가져오기span"><span style="color:CornflowerBlue">🔍  Prefetching (미리 가져오기)</span></h3>
<ul>
<li>사용자가 요청하기 전에 데이터를 미리 가져오는 기술.</li>
<li>사용자가 특정 페이지나 데이터를 클릭하기 전에 데이터를 준비해 두어 로딩 없이 즉시 표시.</li>
<li>(예제)<pre><code class="language-js">queryClient.prefetchQuery([&#39;posts&#39;, postId], () =&gt;
axios.get(`/api/posts/${postId}`).then((res) =&gt; res.data)
);</code></pre>
🙌 쉽게 비유하자면: <strong>&quot;미리 데운 음식&quot;</strong>과 같아요. 손님이 주문하기 전에 음식을 준비해 두면, 빠르게 제공할 수 있어!</li>
</ul>
<br/>

<h3 id="span-stylecolorcornflowerblue🔍-keeppreviousdataspan"><span style="color:CornflowerBlue">🔍 keepPreviousData</span></h3>
<ul>
<li>데이터가 변경되더라도 이전 데이터를 유지해 깜빡임을 방지하는 기능.</li>
<li>사용자 경험을 매끄럽게 만들기 위해 중요한 옵션.</li>
<li>(예제)<pre><code class="language-js">const { data, isFetching } = useQuery(
[&#39;posts&#39;, page],
() =&gt; fetchPosts(page),
{ keepPreviousData: true }
);</code></pre>
🙌 쉽게 비유하자면: <strong>&quot;대기열 유지&quot;</strong>와 같아요. 새로운 데이터를 가져오는 동안 기존 데이터를 유지해 화면이 깜빡이지 않아.</li>
</ul>
<br/>

<h3 id="span-stylecolorcornflowerblue🔍-useinfinitequery-무한-스크롤-구현span"><span style="color:CornflowerBlue">🔍 useInfiniteQuery (무한 스크롤 구현)</span></h3>
<ul>
<li>데이터를 페이지 단위로 가져오면서, 사용자가 스크롤할 때마다 데이터를 추가로 로드하는 기능.</li>
<li>무한 스크롤을 쉽게 구현할 수 있어.</li>
<li>(예제)<pre><code class="language-js">const { data, fetchNextPage, hasNextPage } = useInfiniteQuery(
[&#39;posts&#39;],
({ pageParam = 1 }) =&gt; fetchPosts(pageParam),
{
  getNextPageParam: (lastPage) =&gt; lastPage.nextPage || undefined,
}
);</code></pre>
🙌 쉽게 비유하자면: <strong>&quot;자동 밥 리필&quot;</strong>과 같아요. 밥그릇이 비면 자동으로 밥을 채워주는 것처럼 데이터를 제공해.</li>
</ul>
<br/>

<h2 id="-span-stylecolorbrown🚀-진짜-쉬운-설명span">** <span style="color:Brown">🚀 진짜 쉬운 설명**</span></h2>
<ul>
<li><strong>Optimistic Updates:</strong> 서버 요청 전에 결과를 먼저 화면에 표시.</li>
<li><strong>Prefetching:</strong> 필요한 데이터를 미리 가져와 빠른 로딩 경험 제공.</li>
<li><strong>keepPreviousData:</strong> 데이터 변경 시에도 기존 데이터를 유지.</li>
<li><strong>useInfiniteQuery:</strong> 무한 스크롤로 데이터를 동적으로 로드.</li>
</ul>
<p>React Query의 이 옵션들을 활용하면 사용자 경험을 대폭 향상시킬 수 있어!</p>
<hr>
<h3 id="-span-stylecolorlightsteelblue✏️-더-알아가기-span">** <span style="color:LightSteelBlue">✏️ 더 알아가기 **</span></h3>
<ul>
<li><strong>페이지네이션 vs 무한 스크롤</strong>
페이지네이션과 무한 스크롤은 사용자가 데이터를 탐색하는 방법을 제공하는 두 가지 주요 패턴이야.</li>
</ul>
<table>
<thead>
<tr>
<th>구분</th>
<th>페이지네이션</th>
<th>무한 스크롤</th>
</tr>
</thead>
<tbody><tr>
<td>정의</td>
<td>데이터를 <strong>페이지 단위</strong>로 나누어 사용자가 이동할 수 있게 제공.</td>
<td>사용자가 <strong>스크롤을 내릴 때마다 데이터를 로드</strong>하는 방식.</td>
</tr>
<tr>
<td>UX 특성</td>
<td>명확하게 페이지를 구분하여, 사용자가 특정 페이지로 쉽게 이동 가능.</td>
<td>끊김 없는 사용자 경험 제공. 자연스럽게 데이터를 추가.</td>
</tr>
<tr>
<td>장점</td>
<td>- 사용자가 원하는 페이지를 쉽게 선택 가능.</td>
<td>- 페이지를 기억하기 쉬움.</td>
</tr>
<tr>
<td>단점</td>
<td>- 사용자가 페이지를 전환할 때 약간의 로딩이 발생.</td>
<td>- 클릭/탐색 작업 필요.</td>
</tr>
<tr>
<td>사용 사례</td>
<td>검색 결과 페이지 (예: 구글 검색, 쇼핑몰)</td>
<td>SNS 피드, 뉴스 앱 (예: 인스타그램, 페이스북)</td>
</tr>
<tr>
<td>적합한 데이터 양</td>
<td>큰 데이터 양에 적합. 사용자가 원하는 위치로 점프 가능.</td>
<td>비교적 적은 양의 데이터를 자연스럽게 소비할 때 적합.</td>
</tr>
</tbody></table>
<ul>
<li><strong>페이지네이션</strong>은 명확한 데이터 구분이 필요한 경우, 특히 검색 결과나 쇼핑몰에서 적합.</li>
<li><strong>무한 스크롤</strong>은 사용자가 데이터를 자연스럽게 소비해야 하는 SNS나 뉴스 앱에 유리.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[A project ] study(6) React Query의 Must-Know Options]]></title>
            <link>https://velog.io/@k_kyu/A-project-study6-React-Query%EC%9D%98-Must-Know-Options</link>
            <guid>https://velog.io/@k_kyu/A-project-study6-React-Query%EC%9D%98-Must-Know-Options</guid>
            <pubDate>Tue, 14 Jan 2025 03:16:29 GMT</pubDate>
            <description><![CDATA[<h1 id="-span-stylecolorcornflowerbluemust-know-options란-span">** <span style="color:CornflowerBlue">Must-Know Options**란, </span></h1>
<p>React Query를 사용할 때 꼭 알아야 하는 주요 옵션들이야! 이 옵션들은 데이터를 어떻게 가져오고, 캐싱하며, 새로고침할지 등을 설정하는 데 사용돼. 
React Query의 똑똑한 데이터 관리를 위해 꼭 이해하고 활용해야 하는 옵션이지.</p>
<h3 id="span-stylecolorcornflowerblue🔍-staletimespan"><span style="color:CornflowerBlue">🔍 staleTime</span></h3>
<ul>
<li><p><strong>&quot;데이터가 신선하다고 간주되는 시간&quot;</strong>을 설정.</p>
</li>
<li><p>기본값: 0 (항상 오래된 데이터로 간주).</p>
</li>
<li><p>설정된 시간 동안 캐시 데이터를 &quot;신선한 상태(fresh)&quot;로 간주하므로, 서버 요청 없이 캐시 데이터를 바로 사용해.</p>
</li>
<li><p>(예제)</p>
<pre><code class="language-js">const { data } = useQuery([&#39;todos&#39;], fetchTodos, { staleTime: 1000 * 60 }); // 1분</code></pre>
<p>🙌 쉽게 비유하자면: &quot;음식이 상하지 않는 기간&quot;과 같아, staleTime 동안은 데이터를 새로 가져오지 않고, 기존 데이터를 재사용해.</p>
</li>
</ul>
<br/>

<h3 id="span-stylecolorcornflowerblue🔍-cachetimespan"><span style="color:CornflowerBlue">🔍 cacheTime</span></h3>
<ul>
<li><strong>&quot;캐시 데이터를 유지하는 시간&quot;</strong>을 설정.</li>
<li>기본값: 5분(inactive 상태에서 5분간 캐시 유지).</li>
<li>이 시간이 지나면 캐시삭제.</li>
<li>(예제)<pre><code class="language-js">const { data } = useQuery([&#39;todos&#39;], fetchTodos, { cacheTime: 1000 * 60 * 10 }); // 10분</code></pre>
🙌 쉽게 비유하자면: &quot;냉장고에 음식을 얼마나 오래 보관할지&quot;와 같아. cacheTime이 지나면 음식(데이터)이 삭제돼.</li>
</ul>
<br/>

<h3 id="span-stylecolorcornflowerblue🔍-refetchonmountspan"><span style="color:CornflowerBlue">🔍 refetchOnMount</span></h3>
<ul>
<li>컴포넌트가 마운트될 때 데이터를 다시 가져올지 설정.</li>
<li>기본값: true(마운트 시 stale 데이터는 자동으로 새로고침).</li>
<li>(예제)<pre><code class="language-js">const { data } = useQuery([&#39;todos&#39;], fetchTodos, { refetchOnMount: false });</code></pre>
🙌 쉽게 비유하자면: 집에 돌아올 때마다 냉장고 문을 열어 음식을 확인할지&quot;와 같아. false로 설정하면 마운트할 때 데이터를 다시 가져오지 않아.</li>
</ul>
<br/>

<h3 id="span-stylecolorcornflowerblue🔍-refetchonwindowfocusspan"><span style="color:CornflowerBlue">🔍 refetchOnWindowFocus</span></h3>
<ul>
<li>브라우저가 포커스를 받을 때 데이터를 새로고침할지 설정.</li>
<li>기본값: true (브라우저로 돌아올 때 stale 데이터 새로고침).</li>
<li>(예제)<pre><code class="language-js">const { data } = useQuery([&#39;todos&#39;], fetchTodos, { refetchOnWindowFocus: false });</code></pre>
🙌 쉽게 비유하자면: &quot;다시 방으로 돌아왔을 때 냉장고 문을 열어 음식을 확인할지&quot;와 같아. false로 설정하면 브라우저로 돌아와도 데이터를 다시 가져오지 않아.</li>
</ul>
<br/>

<h3 id="span-stylecolorcornflowerblue🔍-refetchonreconnectspan"><span style="color:CornflowerBlue">🔍 refetchOnReconnect</span></h3>
<ul>
<li><p>네트워크가 다시 연결되었을 때 데이터를 새로고침할지 설정.</p>
</li>
<li><p>기본값: true(재연결 시 자동 새로고침).</p>
</li>
<li><p>(예제)</p>
<pre><code class="language-js">const { data } = useQuery([&#39;todos&#39;], fetchTodos, { refetchOnReconnect: false });</code></pre>
<p>🙌 쉽게 비유하자면: &quot;인터넷이 끊겼다가 다시 연결되었을 때 데이터를 새로 확인할지&quot;와 같아.</p>
<br/>

</li>
</ul>
<h3 id="span-stylecolorcornflowerblue🔍-retryspan"><span style="color:CornflowerBlue">🔍 retry</span></h3>
<ul>
<li>데이터 요청이 실패했을 때 재시도 횟수를 설정.</li>
<li>기본값: 3 (최대 3번 재요청).</li>
<li>(예제)<pre><code class="language-js">const { data } = useQuery([&#39;todos&#39;], fetchTodos, { retry: 2 }); // 2번만 재요청</code></pre>
🙌 쉽게 비유하자면: &quot;음식을 배달했는데 실패하면 몇 번까지 다시 배달 시도할지&quot;와 같아. 요청이 실패했을 때, 자동으로 재시도해서 데이터를 가져오려고 노력.</li>
</ul>
<br/>

<h2 id="-span-stylecolorbrown🚀-진짜-쉬운-설명span">** <span style="color:Brown">🚀 진짜 쉬운 설명**</span></h2>
<hr>
<h3 id="-span-stylecolorlightsteelblue✏️-더-알아가기-span">** <span style="color:LightSteelBlue">✏️ 더 알아가기 **</span></h3>
<p><strong>enabled와 select란?</strong>
React Query의 옵션 중 하나로, 데이터를 가져오는 방식이나 쿼리의 동작을 세밀하게 제어하는 데 사용해.</p>
<ul>
<li><p><strong>enabled란?</strong></p>
</li>
<li><p><em>쿼리를 활성화하거나 비활성화*</em>하는 옵션이야.
마치, <strong>&quot;스위치&quot;</strong>와 같아! enabled: false는 쿼리를 꺼두는 것이고, 특정 조건에서만 켜는거지.</p>
<ul>
<li><p>enabled: <code>false</code>로 설정하면 쿼리가 자동으로 실행되지 않아.</p>
</li>
<li><p>데이터 요청을 조건부로 실행하고 싶을 때 사용해.
(예) 특정 상태나 변수가 준비된 이후에 데이터를 가져오고 싶을 때.</p>
</li>
<li><p>(예시)</p>
<pre><code class="language-js">import { useQuery } from &#39;@tanstack/react-query&#39;;
import axios from &#39;axios&#39;;
</code></pre>
</li>
</ul>
</li>
</ul>
<p>function UserProfile({ userId }) {
  const { data, isLoading } = useQuery(
    [&#39;user&#39;, userId],
    () =&gt; axios.get(<code>/api/users/${userId}</code>).then((res) =&gt; res.data),
    {
      enabled: !!userId, // userId가 있을 때만 쿼리 실행
    }
  );</p>
<p>  if (!userId) return <p>사용자 ID가 필요합니다.</p>;
  if (isLoading) return <p>로딩 중...</p>;</p>
<p>  return <p>사용자 이름: {data.name}</p>;
}
//enabled: !!userId는 userId가 존재할 때만 쿼리 실행을 허용.
//사용자가 userId를 제공하지 않으면 서버 요청이 발생하지 않아.</p>
<pre><code>

- **select란?**
**쿼리 응답 데이터를 변환**할 수 있는 옵션이야.
마치, **필터링 작업**과 같아! 서버에서 가져온 데이터를 전부 사용하지 않고, 필요한 부분만 골라서 사용하지.
  -  서버에서 가져온 원본 데이터를 사용자가 원하는 형태로 가공할 때 유용해.
  - 서버에서 받은 데이터가 필요 이상으로 많을 때, 필요한 정보만 골라내고 싶을 때 사용해.
(예) 특정 상태나 변수가 준비된 이후에 데이터를 가져오고 싶을 때.

  - (예시)
```js
import { useQuery } from &#39;@tanstack/react-query&#39;;
import axios from &#39;axios&#39;;

function UserProfile({ userId }) {
  const { data, isLoading } = useQuery(
    [&#39;user&#39;, userId],
    () =&gt; axios.get(`/api/users/${userId}`).then((res) =&gt; res.data),
    {
      select: (data) =&gt; ({
        name: data.name,
        email: data.email,
      }), // 필요한 데이터만 추출
    }
  );

  if (isLoading) return &lt;p&gt;로딩 중...&lt;/p&gt;;

  return (
    &lt;div&gt;
      &lt;p&gt;이름: {data.name}&lt;/p&gt;
      &lt;p&gt;이메일: {data.email}&lt;/p&gt;
    &lt;/div&gt;
  );
}
//서버에서 가져온 데이터에 사용자가 원하는 가공 로직(select)을 적용.
//데이터 가공은 클라이언트에서 이루어지므로 서버 로직에 영향을 주지 않음.</code></pre><p><strong>🎯 enabled와 select 차이</strong></p>
<table>
<thead>
<tr>
<th>옵션</th>
<th>설명</th>
<th>주된 역할</th>
</tr>
</thead>
<tbody><tr>
<td><code>enabled</code></td>
<td>쿼리가 실행될 조건을 설정. 조건이 충족되지 않으면 쿼리 실행 안 됨.</td>
<td>쿼리 실행 여부 제어</td>
</tr>
<tr>
<td><code>select</code></td>
<td>서버에서 가져온 데이터를 원하는 형태로 변환.</td>
<td>데이터 가공 및 필터링</td>
</tr>
</tbody></table>
<ul>
<li><strong>enabled:</strong> 쿼리 실행 여부를 조건부로 제어.</li>
<li><strong>select:</strong> 서버에서 가져온 데이터를 가공해서 필요한 데이터만 반환.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[A project ] study(5) React Query 상세]]></title>
            <link>https://velog.io/@k_kyu/A-project-study5-React-Query-%EC%83%81%EC%84%B8</link>
            <guid>https://velog.io/@k_kyu/A-project-study5-React-Query-%EC%83%81%EC%84%B8</guid>
            <pubDate>Tue, 14 Jan 2025 03:01:20 GMT</pubDate>
            <description><![CDATA[<h1 id="-span-stylecolorcornflowerbluereact-query란-span">** <span style="color:CornflowerBlue">React Query**란, </span></h1>
<p>React Query(=TanStack Query)는 서버 상태 관리를 간편하게 만들어주는 도구야.
서버에서 데이터를 가져오고, 캐시에 저장하며, 이를 다시 사용할 수 있도록 관리하는 것을 도와줘. 특히, 비동기 작업(데이터 요청, 응답 처리, 상태 관리)을 자동화해서 개발자 경험을 크게 향상시켜주지!</p>
<h2 id="-span-stylecolorbrown📍핵심-설명span">** <span style="color:Brown">📍핵심 설명**</span></h2>
<ul>
<li><p><strong>데이터 패칭 (Fetching):</strong> 서버에서 데이터를 가져오는 작업. (예) API 호출로 게시글 목록을 가져오기.</p>
</li>
<li><p><strong>캐싱 (Caching):</strong> 한 번 가져온 데이터를 메모리에 저장해서, 같은 요청을 다시 보낼 필요가 없게 만듦.
빠른 성능과 효율적인 데이터 관리를 제공.</p>
</li>
<li><p><strong>자동 리프레시 (Refetching):</strong> 데이터가 오래되면 자동으로 새로고침하거나, 수동으로 요청할 수 있음.</p>
</li>
<li><p><strong>요청 상태 관리:</strong> 로딩 중, 에러 발생, 성공 등 요청의 상태를 자동으로 관리.</p>
</li>
<li><p><strong>SWR 전략:</strong> &quot;오래된 데이터를 사용하면서 새 데이터를 가져오는(Stale-While-Revalidate)&quot; 방식으로, 화면을 빠르게 렌더링하고 최신 데이터를 유지.</p>
<br/>

</li>
</ul>
<h3 id="span-stylecolorcornflowerblue🔍-react-query-사용-예제span"><span style="color:CornflowerBlue">🔍 React Query 사용 예제</span></h3>
<p><strong>1️⃣ React Query설치</strong></p>
<table>
<thead>
<tr>
<th>패키지 매니저</th>
<th>설치 명령어</th>
</tr>
</thead>
<tbody><tr>
<td>yarn</td>
<td><code>yarn add @tanstack/react-query</code></td>
</tr>
<tr>
<td>pnpm</td>
<td><code>pnpm add @tanstack/react-query</code></td>
</tr>
</tbody></table>
<p><strong>2️⃣ 기본 설정 (QueryClientProvider)</strong>
React Query는 QueryClient라는 관리자를 추가해야해.</p>
<pre><code class="language-js">import { QueryClient, QueryClientProvider } from &#39;@tanstack/react-query&#39;;

const queryClient = new QueryClient();

function App() {
  return (
    &lt;QueryClientProvider client={queryClient}&gt;
      &lt;MyComponent /&gt;
    &lt;/QueryClientProvider&gt;
  );
}</code></pre>
<p><strong>3️⃣ 데이터를 가져오기 (useQuery)</strong>
<strong>useQuery</strong>는 데이터를 가져오는 데 사용해.</p>
<pre><code class="language-js">import { useQuery } from &#39;@tanstack/react-query&#39;;
import axios from &#39;axios&#39;;

function MyComponent() {
  const { data, isLoading, error } = useQuery([&#39;posts&#39;], async () =&gt; {
    const response = await axios.get(&#39;https://jsonplaceholder.typicode.com/posts&#39;);
    return response.data;
  });

  if (isLoading) return &lt;p&gt;로딩 중...&lt;/p&gt;;
  if (error) return &lt;p&gt;에러 발생: {error.message}&lt;/p&gt;;

  return (
    &lt;div&gt;
      &lt;h1&gt;게시글 목록&lt;/h1&gt;
      &lt;ul&gt;
        {data.map((post) =&gt; (
          &lt;li key={post.id}&gt;{post.title}&lt;/li&gt;
        ))}
      &lt;/ul&gt;
    &lt;/div&gt;
  );
}</code></pre>
<br/>

<h3 id="span-stylecolorcornflowerblue-🔍--mutation이란span"><span style="color:CornflowerBlue"> 🔍  Mutation이란?</span></h3>
<p><strong>데이터 변경 작업(예: POST, PUT, DELETE 요청)</strong>을 처리할 때 사용하는 훅이야.
데이터를 가져오는 useQuery와는 반대로, 데이터를 변경하는 데 사용해!</p>
<pre><code class="language-js">import { useMutation, useQueryClient } from &#39;@tanstack/react-query&#39;;
import axios from &#39;axios&#39;;

function AddPost() {
  const queryClient = useQueryClient();

  const mutation = useMutation(
    (newPost) =&gt; axios.post(&#39;https://jsonplaceholder.typicode.com/posts&#39;, newPost),
    {
      onSuccess: () =&gt; {
        // 성공 후 기존 데이터를 다시 가져오게 함
        queryClient.invalidateQueries([&#39;posts&#39;]);
      },
    }
  );

  const addNewPost = () =&gt; {
    mutation.mutate({ title: &#39;새 게시글&#39;, body: &#39;내용입니다.&#39; });
  };

  return (
    &lt;div&gt;
      &lt;button onClick={addNewPost} disabled={mutation.isLoading}&gt;
        게시글 추가
      &lt;/button&gt;
      {mutation.isLoading &amp;&amp; &lt;p&gt;추가 중...&lt;/p&gt;}
      {mutation.isError &amp;&amp; &lt;p&gt;에러 발생: {mutation.error.message}&lt;/p&gt;}
      {mutation.isSuccess &amp;&amp; &lt;p&gt;추가 완료!&lt;/p&gt;}
    &lt;/div&gt;
  );
}
</code></pre>
<br/>

<h3 id="span-stylecolorcornflowerblue-🔍-invalidatequeries란span"><span style="color:CornflowerBlue"> 🔍 invalidateQueries란?</span></h3>
<p>invalidateQueries는 React Query의 캐시를 무효화(Invalidate)하는 기능이야.
예를 들어, 새로운 데이터를 추가하거나 삭제한 후 이전에 가져온 데이터를 새로고침할 때 사용해.</p>
<pre><code class="language-js">queryClient.invalidateQueries([&#39;posts&#39;]); // &#39;posts&#39; 키로 캐시된 데이터를 새로고침</code></pre>
<br/>

<h3 id="span-stylecolorcornflowerblue-🔍-react-query의-swr-전략span"><span style="color:CornflowerBlue"> 🔍 React Query의 SWR 전략</span></h3>
<p>SWR은 <strong>Stale-While-Revalidate(오래된 데이터를 사용하면서 새 데이터를 가져옴)</strong>의 약자야.</p>
<ul>
<li><p>캐시 데이터 제공 (빠르게 화면 표시)
먼저, React Query는 캐시에 저장된 데이터를 화면에 표시해.
화면이 빨리 보여서 사용자 경험이 좋아져.</p>
</li>
<li><p>새 데이터 가져오기 (최신 데이터 유지)
동시에 서버에서 새 데이터를 가져와 캐시를 업데이트해.
화면에 최신 데이터가 반영돼.</p>
</li>
</ul>
<p><strong>React Query:</strong> 서버 상태 관리에 특화된 강력한 도구.
<strong>SWR:</strong> 데이터 페칭과 캐싱을 단순하게 처리하는 도구.</p>
<p><strong>React Query는 SWR 전략을 기본으로 제공</strong>해, 
먼저 캐시에서 데이터를 보여주고, 서버에서 새 데이터를 가져와 화면을 업데이트함.</p>
<br/>

<h2 id="-span-stylecolorbrown🚀-진짜-쉬운-설명span">** <span style="color:Brown">🚀 진짜 쉬운 설명**</span></h2>
<p>React Query는 서버 데이터를 효율적으로 관리하고, 캐싱과 상태 관리를 자동화해 개발을 더 빠르고 쉽게 만들어주는 도구야!</p>
<ul>
<li><strong>자동화:</strong> 데이터를 가져오고, 상태를 관리하며, 새로고침까지 자동으로 처리.</li>
<li><strong>효율성:</strong> 캐싱으로 동일한 요청을 반복하지 않아 성능이 좋아짐.</li>
<li><strong>간결한 코드:</strong> useQuery와 useMutation으로 간단하게 API를 처리.</li>
</ul>
<p><strong>🎯 쉽게 비유하기</strong>
React Query를 <strong>자동 음식 배달 서비스</strong>라고 생각해봐!</p>
<ul>
<li><p>음식 주문(데이터 요청): 사용자가 음식(데이터)을 주문.
React Query가 자동으로 서버에서 데이터를 가져와 제공.</p>
</li>
<li><p>배달 상태 관리(요청 상태 관리):  음식이 배달 중인지, 배달 완료되었는지, 실패했는지 상태를 
자동으로 관리. &quot;데이터를 가져오는 중...&quot; 같은 로딩 메시지를 쉽게 표시할 수 있어.</p>
</li>
<li><p>음식 보관소(캐싱): 가져온 음식(데이터)을 보관소(캐시)에 저장.
같은 데이터가 다시 필요하면 서버에 요청하지 않고, 보관소에서 꺼내줘.</p>
</li>
<li><p>유통기한 관리(자동 리프레시): 데이터가 오래되면 자동으로 새로 가져오거나, 사용자가 요청했을 때 데이터를 새로고침해.</p>
</li>
</ul>
<hr>
<h3 id="-span-stylecolorlightsteelblue✏️-더-알아가기-span">** <span style="color:LightSteelBlue">✏️ 더 알아가기 **</span></h3>
<ul>
<li>React Query에서 쿼리 데이터의 <strong>여러 상태(state)</strong></li>
</ul>
<table>
<thead>
<tr>
<th>상태</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>fresh</td>
<td>데이터를 새로 패칭할 필요가 없는 상태. <code>staleTime</code>이 지나지 않은 상태로, 캐시 데이터를 그대로 사용가능.</td>
</tr>
<tr>
<td>stale</td>
<td>데이터를 새로 패칭해야 하는 상태. <code>staleTime</code>이 지난 후, 새로운 데이터를 가져오기 위해 쿼리가 실행.</td>
</tr>
<tr>
<td>active</td>
<td>현재 컴포넌트에서 사용 중인 쿼리 상태. 컴포넌트가 마운트되어 쿼리를 사용하고 있을 때.</td>
</tr>
<tr>
<td>inactive</td>
<td>더 이상 사용되지 않는 쿼리 상태. 컴포넌트가 언마운트되거나 쿼리가 더 이상 필요하지 않을 때.</td>
</tr>
<tr>
<td>deleted</td>
<td>캐시에서 제거된 쿼리 상태. <code>gcTime</code>이 지나면 쿼리가 캐시에서 삭제되어 이 상태가 됨.</td>
</tr>
<tr>
<td>fetching</td>
<td>데이터를 서버에서 가져오고 있는 상태. 이 상태에서는 <code>isFetching</code>이 <code>true</code>로 설정.</td>
</tr>
</tbody></table>
<ul>
<li>React Query에서 상태 사용하는 이유.
React Query는 서버 상태(server state)를 자동으로 관리하면서도 다음과 같은 세부 상태를 제공해.<ul>
<li>데이터를 가져올지 결정(stale vs fresh).</li>
<li>데이터를 캐시에 유지할지, 삭제할지 결정(inactive vs deleted).</li>
<li>사용자가 요청 중인지, 결과를 기다리는 중인지 파악(fetching).
이 상태들을 이해하면, 데이터 요청, 캐싱, 새로고침 등 React Query의 핵심 기능을 효율적으로 사용 가능해!</li>
</ul>
</li>
</ul>
<ul>
<li>React Query의 <strong>기본 설정 옵션</strong></li>
</ul>
<table>
<thead>
<tr>
<th>기본 설정</th>
<th>의미</th>
</tr>
</thead>
<tbody><tr>
<td><code>staleTime: 0</code></td>
<td><code>useQuery</code> 또는 <code>useInfiniteQuery</code>에 등록된 <code>queryFn</code>을 통해 fetch 받은 데이터는 항상 stale 상태로 취급.</td>
</tr>
<tr>
<td><code>refetchOnMount: true</code></td>
<td><code>useQuery</code> 또는 <code>useInfiniteQuery</code>가 있는 컴포넌트가 마운트 시 stale 데이터를 refetch 자동 실행.</td>
</tr>
<tr>
<td><code>refetchOnWindowFocus: true</code></td>
<td>실행 중인 브라우저 화면을 focus 할 때마다 stale 데이터를 refetch 자동 실행.</td>
</tr>
<tr>
<td><code>refetchOnReconnect: true</code></td>
<td>네트워크가 끊겼다가 재연결되었을 때 stale 데이터를 refetch 자동 실행.</td>
</tr>
<tr>
<td><code>gcTime (cacheTime): 5분</code></td>
<td><code>useQuery</code> 또는 <code>useInfiniteQuery</code>가 있는 컴포넌트가 언마운트된 후 inactive 상태가 5분 경과 시 캐시 데이터 삭제 처리.</td>
</tr>
<tr>
<td><code>retry: 3</code></td>
<td><code>useQuery</code> 또는 <code>useInfiniteQuery</code>에 등록된 <code>queryFn</code>이 API 요청 실패 시 총 3번까지 재요청 자동 시도.</td>
</tr>
</tbody></table>
<ul>
<li>React Query의 기본 설정 옵션 설명<ul>
<li>staleTime: 데이터가 <strong>stale(오래된 상태)</strong>로 간주되기 전까지의 시간을 설정.</li>
<li>refetchOnMount: 컴포넌트가 다시 마운트될 때 데이터를 자동으로 새로 가져올지 결정.</li>
<li>refetchOnWindowFocus: 사용자가 브라우저로 돌아왔을 때 데이터를 새로고침할지 설정.</li>
<li>refetchOnReconnect: 네트워크가 복구되었을 때 데이터를 새로고침할지 설정.</li>
<li>gcTime (cacheTime): 데이터가 캐시에서 삭제되기 전까지의 시간.</li>
<li>retry: 데이터 요청이 실패했을 때 자동으로 재요청을 몇 번 시도할지 설정.</li>
</ul>
</li>
</ul>
<br/>

<p><strong>🎯 헷갈리는 개념 정리</strong></p>
<p><strong>1. staleTime vs gcTime</strong></p>
<ul>
<li><p><strong>staleTime</strong>: 얼마의 시간이 흐른 뒤에 stale 취급할 건지 (default: 0)</p>
</li>
<li><p><strong>gcTime</strong>: inactive 된 이후로 메모리에 얼마만큼 있을건지 (default: 5분, gcTime 0되면 삭제처리)</p>
</li>
<li><p><strong>staleTime 과 stale/fresh 의 관계</strong></p>
<ul>
<li>staleTime &gt; 0 이면, fresh data</li>
<li>staleTime = 0 이면, stale data</li>
</ul>
</li>
</ul>
<p><strong>2. isPending vs. isFetching</strong></p>
<ul>
<li><p><strong>isPending</strong> : 새로운 캐시 데이터를 서버에서 받고 있는 지 여부.</p>
<ul>
<li>캐시 데이터가 있는 경우 isPending은 false, isFetching은 true</li>
</ul>
</li>
<li><p><strong>isFetching</strong>: 서버에서 데이터를 받고 있는 지 여부.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React ] study(7) 인증과 인가 - 쿠키, 세션, 토큰, JWT]]></title>
            <link>https://velog.io/@k_kyu/React-study7-%EC%9D%B8%EC%A6%9D%EA%B3%BC-%EC%9D%B8%EA%B0%80-%EC%BF%A0%ED%82%A4-%EC%84%B8%EC%85%98-%ED%86%A0%ED%81%B0-JWT</link>
            <guid>https://velog.io/@k_kyu/React-study7-%EC%9D%B8%EC%A6%9D%EA%B3%BC-%EC%9D%B8%EA%B0%80-%EC%BF%A0%ED%82%A4-%EC%84%B8%EC%85%98-%ED%86%A0%ED%81%B0-JWT</guid>
            <pubDate>Mon, 13 Jan 2025 13:19:28 GMT</pubDate>
            <description><![CDATA[<h1 id="-span-stylecolorcornflowerblue인증과-인가란-span">** <span style="color:CornflowerBlue">인증과 인가**란, </span></h1>
<p><strong>1️⃣ 인증 (Authentication):</strong> <strong>누구인지 확인하는 과정</strong>이야.
(예) &quot;이 사용자가 정말 로그인하려는 계정의 주인인지 확인.&quot; 로그인: ID와 비밀번호 입력.
<strong>결과:</strong> &quot;당신은 누구인지 확인했습니다.&quot;</p>
<p><strong>2️⃣ 인가 (Authorization):</strong> <strong>권한을 확인하는 과정</strong>이야.
(예) &quot;이 사용자가 특정 작업(예: 관리자 페이지 접근)을 할 수 있는 권한이 있는지 확인.&quot;
<strong>결과:</strong> &quot;이 작업을 할 수 있는 사람입니다/아닙니다.&quot;</p>
<h2 id="-span-stylecolorbrown📍핵심-설명span">** <span style="color:Brown">📍핵심 설명**</span></h2>
<p>인증과 인가를 구현할 때 사용하는 기술은  <strong>쿠키(Cookie), 세션(Session), 토큰(Token), JWT</strong>이 있어.</p>
<blockquote>
<ul>
<li>** 쿠키 (Cookie)**
웹 브라우저에 저장하는 작은 정보 조각이야. 서버가 브라우저에게 데이터를 저장하도록 요청하고, 
브라우저는 이 데이터를 이후 요청마다 서버에 다시 보여줘.</li>
</ul>
</blockquote>
<ul>
<li>마치 <strong>극장 입장권</strong>과 같아!
입장권(쿠키)은 극장(브라우저)에 저장해.
입장권을 보여주면 서버가 &quot;아, 이 사람은 이미 인증받았군!&quot; 하고 신원을 확인해.</li>
<li>특징: 클라이언트(브라우저)에 데이터 저장.
로그인 정보, 장바구니 상태 등을 유지할 때 사용.
만료 시간이 설정되어 있어, 시간이 지나면 쿠키가 삭제.</li>
</ul>
<blockquote>
<ul>
<li><strong>세션 (Session)</strong>
서버에 저장되는 사용자 상태 정보야. 사용자가 서버에 접속하면 세션 ID를 생성하고, 이를 쿠키에 저장해.</li>
</ul>
</blockquote>
<ul>
<li>마치 <strong>호텔 키카드</strong>와 같아!
키카드(세션 ID)가 발급되고, 이를 가지고 있을 때만 방(서버 데이터)에 접근할 수 있고, 키카드는 호텔(서버)에 상태 정보를 저장하므로, 잃어버려도 큰 문제가 없어.</li>
<li>특징: 데이터가 서버 저장.
서버가 사용자의 상태를 기억할 수 있어.
서버 리소스를 사용하기 때문에 많은 사용자가 접속하면 부담이 될 수 있어.</li>
</ul>
<blockquote>
<ul>
<li><strong>토큰 (Token)</strong>
사용자 정보를 포함한 인증 데이터야. 서버는 사용자가 로그인하면 토큰을 발급하고, 이후 요청마다 이 토큰을 사용해 인증해.</li>
</ul>
</blockquote>
<ul>
<li>마치 <strong>회원증</strong>과 같아!
회원증(토큰)을 가지고 있으면, &quot;이 사람이 인증된 회원이구나&quot;라고 확인할 수 있고, 회원증에는 사용자의 정보가 포함되어 있어.</li>
<li>특징: 클라이언트에 저장.(쿠키, 로컬 스토리지 등)
상태를 서버에 저장하지 않으므로 서버 부하가 적어.</li>
</ul>
<blockquote>
<ul>
<li><strong>JWT (JSON Web Token)</strong>
JSON 형식으로 된 토큰이야. 토큰 안에 인증 정보(예: 사용자 ID, 권한)를 담아서 서버가 이를 해석해 인증해.</li>
</ul>
</blockquote>
<ul>
<li>마치 <strong>비행기 탑승권</strong>과 같아!
탑승권(JWT)에는 승객 이름, 좌석 번호 등 정보가 포함되어 있고, 항공사는 탑승권만 보고 신원을 확인하고, 서버에 추가 저장이 필요 없어.</li>
<li>JWT 구조는
<strong>헤더(Header):</strong> 어떤 종류의 토큰인지와 어떤 알고리즘으로 서명되었는지에 대한 정보가 들어있어.
<strong>본문(Payload):</strong> 실제로 중요한 데이터가 들어있는 부분이야. 예를 들어, 사용자 ID, 토큰의 만료 시간 등이 여기에 포함.(민감한 정보는 넣지 않아야해!)
<strong>서명(Signature):</strong> 토큰이 위조되지 않았는지 확인하는 역할을 해. 서버만이 알 수 있는 비밀 키로 서명되어 있어, 이 서명 때문에 토큰의 무결성이 보장돼.</li>
<li>특징: 데이터를 직접 토큰에 포함하여 서버는 상태를 기억할 필요 없고, 자체 인증이 가능하지만, 민감한 정보를 담으면 안돼. 암호화 서명(Signature)으로 토큰 위조를 방지하지.</li>
</ul>
<br/>

<h2 id="-span-stylecolorbrown🚀-진짜-쉬운-설명span">** <span style="color:Brown">🚀 진짜 쉬운 설명**</span></h2>
<ul>
<li><strong>쿠키:</strong> 로그인 상태 유지, 장바구니 저장 등 간단한 상태 유지가 필요할 때.</li>
<li><strong>세션:</strong> 사용자 데이터를 서버에서 안전하게 관리하고 싶을 때.</li>
<li><strong>토큰:</strong> 서버 부하를 줄이고, 상태 정보를 클라이언트에서 관리하고 싶을 때.</li>
<li><strong>JWT:</strong> RESTful API와 같은 무상태(Stateless) 시스템에서 인증을 구현할 때.</li>
</ul>
<table>
<thead>
<tr>
<th>특징</th>
<th>쿠키</th>
<th>세션</th>
<th>토큰</th>
<th>JWT</th>
</tr>
</thead>
<tbody><tr>
<td>데이터 저장 위치</td>
<td>브라우저</td>
<td>서버</td>
<td>클라이언트</td>
<td>클라이언트</td>
</tr>
<tr>
<td>인증 데이터 포함</td>
<td>보통 포함하지 않음 (세션 ID 저장)</td>
<td>포함하지 않음 (세션 ID만 저장)</td>
<td>포함 가능</td>
<td>포함 (JSON 형식)</td>
</tr>
<tr>
<td>만료 관리</td>
<td>쿠키 만료 시간으로 설정</td>
<td>세션 만료 시간으로 설정</td>
<td>토큰 만료 시간 설정 가능</td>
<td>토큰 만료 시간 설정 가능</td>
</tr>
<tr>
<td>보안</td>
<td>민감 정보 저장 시 주의 필요</td>
<td>비교적 안전</td>
<td>보안에 신경 써야 함</td>
<td>서명(Signature)으로 위조 방지</td>
</tr>
<tr>
<td>서버 부하</td>
<td>적음</td>
<td>서버 리소스 필요</td>
<td>없음</td>
<td>없음</td>
</tr>
</tbody></table>
<hr>
<h3 id="-span-stylecolorlightsteelblue✏️-더-알아가기-span">** <span style="color:LightSteelBlue">✏️ 더 알아가기 **</span></h3>
<ul>
<li><strong>토큰으로 으로 보안을 강화하는 법!</strong>
(이를 위해서는 각종 토큰을 발급해주는 영역의 백엔드 개발자와의 협업이 필요해)</li>
</ul>
<p>리소스 접근 인가를 받기 위해 사용되는 토큰을 <code>Access Token</code> 이라고 불러.
<code>Access Token</code>의 만료기간을 길게 잡고 인증상태를 오래 가져갈 경우 서버 부담은 줄어드나 보안성(탈취 당할 경우)에 허점이 있어.</p>
<p>인증 보안이 중요한 서비스의 경우 인증 시 2개의 토큰 (<code>Access Token, Refresh Token</code>)을 발급해. <code>Access Token</code>의 기간은 30분 정도로 짧게 가져 가고, <code>Refresh Token</code>은 1~2주 정도로 길게 잡는 경우가 많아!
이 경우 서버에서는 <code>Access Token</code>이 만료되었을 때 <code>Refresh Token</code> 이 유효한 상태면 새로운 <code>Access Token</code>을 클라이언트에 발급해주고 인증상태를 유지할 수 있도록 하고, <code>Refresh Token</code> 만료 시 다시 로그인하라는 메시지를 응답해.</p>
<ul>
<li><p><strong>Thunder Client툴</strong>
HTTP 요청을 보내고 응답을 받는데 필요한 모든 기능을 가지고 있는 툴이야, 이를 이용하면 손쉽게 API를 테스트하고 디버깅 할 수 있어.</p>
<ul>
<li>설치방법:
VS Code Extensions에서 [thunder Client] 검색 후 설치 → 사이드바의 아이콘 선택</li>
<li>기본 사용 방법:</li>
</ul>
<ol>
<li>New Request 클릭</li>
<li>원하는 HTTP Method 선택 및 URL 입력(Payload가 필요할 경우 Body에 추가)</li>
<li>Send를 눌러 응답 확인</li>
</ol>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React ] study(6) Axios Custom Instance와 Interceptors]]></title>
            <link>https://velog.io/@k_kyu/React-study6-Axios-Custom-Instance%EC%99%80-Interceptors</link>
            <guid>https://velog.io/@k_kyu/React-study6-Axios-Custom-Instance%EC%99%80-Interceptors</guid>
            <pubDate>Mon, 13 Jan 2025 12:19:15 GMT</pubDate>
            <description><![CDATA[<h1 id="-span-stylecolorcornflowerbluecustom-instance란-span">** <span style="color:CornflowerBlue">Custom Instance**란, </span></h1>
<p>맞춤형 인스턴스로 Axios를 사용할 때 기본 설정(base configuration)을 미리 정해두는 방법이야.
반복적으로 작성해야 할 옵션(예: 기본 URL, 공통 헤더)을 한 번만 정의하고 재사용 가능해!</p>
<h3 id="span-stylecolorcornflowerblue🔍--custom-instance-사용-예제span"><span style="color:CornflowerBlue">🔍  Custom Instance 사용 예제</span></h3>
<pre><code class="language-js">// src &gt; axios &gt; api.js
import axios from &#39;axios&#39;;

// Axios Custom Instance 생성
const apiClient = axios.create({
  baseURL: &#39;https://api.example.com&#39;, // 기본 URL
  timeout: 5000, // 요청 제한 시간
  headers: {
    &#39;Content-Type&#39;: &#39;application/json&#39;, // 공통 헤더
    Authorization: &#39;Bearer YOUR_TOKEN_HERE&#39;, // 토큰 등 인증 정보
  },
});

// Custom Instance 사용
apiClient.get(&#39;/users&#39;)
  .then((response) =&gt; {
    console.log(&#39;사용자 데이터:&#39;, response.data);
  })
  .catch((error) =&gt; {
    console.error(&#39;오류 발생:&#39;, error);
  });</code></pre>
<h3 id="🙌-custom-instance의-장점">🙌 Custom Instance의 장점</h3>
<ul>
<li>코드 간결화: 반복적인 설정을 줄이고, 코드 가독성을 높힘.</li>
<li>일관된 설정 관리: 모든 요청에 대해 동일한 설정을 적용 가능.</li>
<li>확장성: 다양한 API 클라이언트를 각각의 Custom Instance로 관리 가능.</li>
</ul>
<br/>

<h1 id="-span-stylecolorcornflowerblueinterceptors란-span">** <span style="color:CornflowerBlue">Interceptors**란, </span></h1>
<p>Axios의 요청(Request)과 응답(Response)을 중간에 가로채 추가 작업을 수행하는 기능이야.
예를 들어, 요청 전에 인증 토큰을 추가하거나, 응답 데이터를 가공할 수 있어!</p>
<h3 id="span-stylecolorcornflowerblue🔍--interceptors-사용-예제span"><span style="color:CornflowerBlue">🔍  Interceptors 사용 예제</span></h3>
<p><strong>1️⃣ 요청 인터셉터 (Request Interceptor)</strong></p>
<pre><code class="language-js">apiClient.interceptors.request.use(
  (config) =&gt; {
    // 요청 전에 실행되는 로직
    console.log(&#39;요청 정보:&#39;, config);
    config.headers.Authorization = `Bearer NEW_TOKEN_HERE`; // 토큰 추가
    return config; // 수정된 요청 설정 반환
  },
  (error) =&gt; {
    // 요청 오류 처리
    console.error(&#39;요청 오류:&#39;, error);
    return Promise.reject(error);
  }
);</code></pre>
<p><strong>2️⃣ 응답 인터셉터 (Response Interceptor)</strong></p>
<pre><code class="language-js">apiClient.interceptors.response.use(
  (response) =&gt; {
    // 응답 성공 시 실행되는 로직
    console.log(&#39;응답 데이터:&#39;, response.data);
    return response; // 수정된 응답 반환
  },
  (error) =&gt; {
    // 응답 오류 처리
    console.error(&#39;응답 오류:&#39;, error);
    if (error.response &amp;&amp; error.response.status === 401) {
      console.log(&#39;인증 오류 발생. 로그인 페이지로 이동합니다.&#39;);
    }
    return Promise.reject(error);
  }
);</code></pre>
<h3 id="🙌-custom-instance의-장점-1">🙌 Custom Instance의 장점</h3>
<ul>
<li>공통 작업 처리: 모든 요청에 공통적으로 적용할 작업(예: 인증 토큰 추가)을 처리.</li>
<li>에러 처리 간소화: 응답 에러를 중앙에서 처리가능.</li>
<li>데이터 가공: 서버로 보내기 전에 요청 데이터를 가공하거나, 응답 데이터를 재구성.</li>
</ul>
<br/>

<h3 id="span-stylecolorcornflowerblue🔍--custom-instance와-interceptors를-함께-사용하는-예제span"><span style="color:CornflowerBlue">🔍  Custom Instance와 Interceptors를 함께 사용하는 예제</span></h3>
<pre><code class="language-js">import axios from &#39;axios&#39;;

// Custom Instance 생성
const apiClient = axios.create({
  baseURL: &#39;https://api.example.com&#39;,
  timeout: 5000,
});

// 요청 인터셉터 추가
apiClient.interceptors.request.use(
  (config) =&gt; {
    config.headers.Authorization = `Bearer YOUR_TOKEN_HERE`;
    console.log(&#39;요청 정보:&#39;, config);
    return config;
  },
  (error) =&gt; {
    console.error(&#39;요청 오류:&#39;, error);
    return Promise.reject(error);
  }
);

// 응답 인터셉터 추가
apiClient.interceptors.response.use(
  (response) =&gt; {
    console.log(&#39;응답 데이터:&#39;, response.data);
    return response;
  },
  (error) =&gt; {
    console.error(&#39;응답 오류:&#39;, error);
    if (error.response &amp;&amp; error.response.status === 401) {
      alert(&#39;로그인이 필요합니다!&#39;);
    }
    return Promise.reject(error);
  }
);

// Custom Instance 사용
apiClient.get(&#39;/users&#39;)
  .then((response) =&gt; {
    console.log(&#39;사용자 데이터:&#39;, response.data);
  })
  .catch((error) =&gt; {
    console.error(&#39;오류 발생:&#39;, error);
  });</code></pre>
<br/>

<h2 id="-span-stylecolorbrown🚀-진짜-쉬운-설명span">** <span style="color:Brown">🚀 진짜 쉬운 설명**</span></h2>
<ul>
<li><strong>Custom Instance:</strong> Axios 설정을 미리 정의해 반복 코드를 줄이고 일관성을 높이는 도구.</li>
<li><strong>Interceptors:</strong> 요청과 응답을 가로채 공통 작업(예: 토큰 추가, 에러 처리)을 처리하는 강력한 기능.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React ] study(5) Axios]]></title>
            <link>https://velog.io/@k_kyu/React-study5-Axios</link>
            <guid>https://velog.io/@k_kyu/React-study5-Axios</guid>
            <pubDate>Mon, 13 Jan 2025 08:23:43 GMT</pubDate>
            <description><![CDATA[<h1 id="-span-stylecolorcornflowerblueaxios란-span">** <span style="color:CornflowerBlue">Axios**란, </span></h1>
<p>웹에서 데이터를 가져오거나 서버로 데이터를 보내기 위한 도구야.
쉽게 말해, 웹 브라우저와 서버가 대화할 수 있도록 도와주는 메신저라고 생각하면 돼!</p>
<h2 id="-span-stylecolorbrown📍핵심-설명span">** <span style="color:Brown">📍핵심 설명**</span></h2>
<p>Axios는 <strong>배달 앱</strong>처럼 동작해!</p>
<ul>
<li><p>클라이언트(<strong>사용자</strong>): &quot;피자를 주문할게요!&quot; 하고 요청을 보냄.</p>
</li>
<li><p>Axios(<strong>배달 앱</strong>): 주문 요청을 가게(서버)로 전달하고, 결과를 받아옴.</p>
</li>
<li><p>서버(<strong>가게</strong>): 요청을 처리하고, 피자(데이터)를 배달 앱(Axios)에게 전달.</p>
</li>
<li><p>클라이언트(<strong>사용자</strong>): 배달 앱(Axios)을 통해 데이터를 받아 결과를 확인.</p>
</li>
</ul>
<br/>

<h3 id="span-stylecolorcornflowerblue🔍--axios의-주요-역할span"><span style="color:CornflowerBlue">🔍  Axios의 주요 역할</span></h3>
<ul>
<li><strong>데이터 요청 (GET)</strong>: 서버에서 데이터를 가져옴.
(예) 게시글 목록을 가져오기.</li>
<li><strong>데이터 전송 (POST)</strong>: 서버에 데이터를 보냄.
(예) 회원가입 정보 전송.</li>
<li><strong>서버와 통신 오류 처리</strong>: 요청이 실패하면 에러를 쉽게 처리.</li>
</ul>
<br/>

<h3 id="span-stylecolorcornflowerblue🔍--axios의-사용-예제span"><span style="color:CornflowerBlue">🔍  Axios의 사용 예제</span></h3>
<p><strong>1️⃣  Axios 설치</strong></p>
<table>
<thead>
<tr>
<th>패키지 매니저</th>
<th>설치 명령어</th>
</tr>
</thead>
<tbody><tr>
<td>yarn</td>
<td><code>yarn add axios</code></td>
</tr>
<tr>
<td>pnpm</td>
<td><code>pnpm add axios</code></td>
</tr>
</tbody></table>
<p><strong>2️⃣ GET 요청 예제 (서버에서 데이터 가져오기)</strong></p>
<pre><code class="language-js">import axios from &#39;axios&#39;;

axios.get(&#39;https://jsonplaceholder.typicode.com/posts&#39;)
  .then((response) =&gt; {
    console.log(response.data); // 가져온 데이터를 출력
  })
  .catch((error) =&gt; {
    console.error(&#39;데이터 가져오기 실패:&#39;, error);
  });
// axios.get()은 서버에 데이터를 요청하는 코드야. 요청이 성공하면 response.data로 데이터를 받아오고, 실패하면 catch에서 에러를 처리해.</code></pre>
<p><strong>3️⃣ POST 요청 예제 (서버로 데이터 보내기)</strong></p>
<pre><code class="language-js">import axios from &#39;axios&#39;;

axios.post(&#39;https://jsonplaceholder.typicode.com/posts&#39;, {
  title: &#39;새 게시글&#39;,
  body: &#39;내용입니다.&#39;,
  userId: 1
})
  .then((response) =&gt; {
    console.log(&#39;성공적으로 추가됨:&#39;, response.data);
  })
  .catch((error) =&gt; {
    console.error(&#39;추가 실패:&#39;, error);
  });
//axios.post()는 데이터를 서버로 보내는 코드야, 여기서는 새로운 게시글 정보를 서버로 전송해.</code></pre>
<br/> 

<h3 id="-span-stylecolorcornflowerblue🔍-섬세한-설명span">** <span style="color:CornflowerBlue">🔍 섬세한 설명**</span></h3>
<blockquote>
<ul>
<li><h3 id="axiosget"><strong>axios.get</strong></h3>
<code>get</code>은 서버의 데이터를 <strong>조회할 때 사용</strong>.</li>
</ul>
</blockquote>
<pre><code class="language-js">// url에는 서버의 url이 들어가고, config에는 기타 여러가지 설정 추가가능.
// config는 axios 공식문서에서 확인해봐!
axios.get(url[, config]) // GET(url은 매개변수, 대괄호([]) 안의 값은 선택 입력사항.</code></pre>
<p>   <a href="https://axios-http.com/kr/docs/req_config">axios 공식문서</a></p>
<ul>
<li><strong>예시코드</strong><pre><code class="language-js">// src/App.js
</code></pre>
</li>
</ul>
<p>import React, { useEffect, useState } from &quot;react&quot;;
import axios from &quot;axios&quot;; // axios import</p>
<p>const App = () =&gt; {
  const [todos, setTodos] = useState(null);</p>
<pre><code>// axios를 통해서 get 요청을 하는 함수를 생성.
// 비동기처리를 해야하므로 async/await 구문을 통해서 처리.</code></pre><p>  const fetchTodos = async () =&gt; {
    const { data } = await axios.get(&quot;<a href="http://localhost:4000/todos&quot;">http://localhost:4000/todos&quot;</a>);
    setTodos(data); // 서버로부터 fetching한 데이터를 useState의 state로 set.
  };</p>
<pre><code>// 생성한 함수를 컴포넌트가 mount된 후 실행하기 위해 useEffect를 사용.</code></pre><p>  useEffect(() =&gt; {
        // effect 구문에 생성한 함수를 넣어 실행.
    fetchTodos();
  }, []);</p>
<pre><code>// data fetching이 정상적으로 되었는지 콘솔을 통해 확인.</code></pre><p>  console.log(todos);
  return <div>App</div>;
};</p>
<p>export default App;</p>
<pre><code>

&gt;- ### **axios.post**
```post```는 보통 서버에 데이터를 **추가할 때 사용.**
```js
// 보통은 클라이언트의 데이터를 body형태로 서버에 보내고자 할 때 사용.
axios.post(url[, data[, config]])   // POST</code></pre><ul>
<li><strong>예시코드</strong><pre><code class="language-js">// src/App.jsx
</code></pre>
</li>
</ul>
<p>import React, { useEffect, useState } from &quot;react&quot;;
import axios from &quot;axios&quot;; // axios import</p>
<p>const App = () =&gt; {
  // 새롭게 생성하는 todo를 관리하는 state
  const [todo, setTodo] = useState({
    title: &quot;&quot;,
  });</p>
<p>  const [todos, setTodos] = useState(null);</p>
<p>  const fetchTodos = async () =&gt; {
    const { data } = await axios.get(&quot;<a href="http://localhost:4000/todos&quot;">http://localhost:4000/todos&quot;</a>);
    setTodos(data);
  };</p>
<pre><code>// (원리를 꼭 이해!!!)
// HTTP에서는 body에 javascript 객체를 direct로 넣을 수 없어!
// axios는 내부적으로 JSON.stringify를 적용하기 때문에 이처럼 편리하게 사용하는 것 뿐이야.</code></pre><p>  const onSubmitHandler = async(todo) =&gt; {
    await axios.post(&quot;<a href="http://localhost:4000/todos&quot;">http://localhost:4000/todos&quot;</a>, todo);
  };</p>
<p>  // 만일 fetch를 사용했다면, 이렇게 JSON.stringify를 &#39;직접&#39; 해줘야 해.
  // await fetch(&quot;<a href="http://localhost:4000/todos&quot;">http://localhost:4000/todos&quot;</a>, {
  //   method: &quot;POST&quot;,
  //   headers: {
  //     &quot;Content-Type&quot;: &quot;application/json&quot;,
  //   },
  //   body: JSON.stringify(todo),
  // });</p>
<p>  useEffect(() =&gt; {
    fetchTodos();
  }, []);</p>
<p>  return (
    &lt;&gt;
      &lt;form
        onSubmit={(e) =&gt; {
                    // 👇 submit했을 때 브라우저의 새로고침을 방지. 
          e.preventDefault();
          onSubmitHandler(todo);
        }}
      &gt;
        &lt;input
          type=&quot;text&quot;
          onChange={(ev) =&gt; {
            const { value } = ev.target;
            setTodo({
              ...todo,
              title: value,
            });
          }}
        /&gt;
        <button>추가하기</button>
      </form>
      <div>
        {todos?.map((todo) =&gt; (
          <div key={todo.id}>{todo.title}</div>
        ))}
      </div>
    &lt;/&gt;
  );
};</p>
<p>export default App;</p>
<pre><code>
&gt;- ### **axios.delete**
 ```delete```는 저장되어 있는 데이터를 **삭제요청 할때 사용.**
```js
axios.delete(url[, config])  // delete</code></pre><ul>
<li><strong>예시코드</strong><pre><code class="language-js">// src/App.jsx
</code></pre>
</li>
</ul>
<p>import React, { useEffect, useState } from &quot;react&quot;;
import axios from &quot;axios&quot;; </p>
<p>const App = () =&gt; {
  const [todo, setTodo] = useState({
    title: &quot;&quot;,
  });</p>
<p>  const [todos, setTodos] = useState(null);</p>
<p>  const fetchTodos = async () =&gt; {
    const { data } = await axios.get(&quot;<a href="http://localhost:4000/todos&quot;">http://localhost:4000/todos&quot;</a>);
    setTodos(data); 
  };</p>
<p>  const onSubmitHandler = (todo) =&gt; {
    axios.post(&quot;<a href="http://localhost:4000/todos&quot;">http://localhost:4000/todos&quot;</a>, todo);
  };</p>
<pre><code>// 새롭게 추가한 삭제 버튼 이벤트 핸들러 </code></pre><p>  const onClickDeleteButtonHandler = (todoId) =&gt; {
    axios.delete(<code>http://localhost:4000/todos/${todoId}</code>);
  };</p>
<p>  useEffect(() =&gt; {
    fetchTodos();
  }, []);</p>
<p>  return (
    &lt;&gt;
      &lt;form
        onSubmit={(e) =&gt; {
          e.preventDefault();
          onSubmitHandler(todo);
        }}
      &gt;
        &lt;input
          type=&quot;text&quot;
          onChange={(ev) =&gt; {
            const { value } = ev.target;
            setTodo({
              ...todo,
              title: value,
            });
          }}
        /&gt;
        <button>추가하기</button>
      </form>
      <div>
        {todos?.map((todo) =&gt; (
          <div key={todo.id}>
            {todo.title}
            &lt;button
              type=&quot;button&quot;
              onClick={() =&gt; onClickDeleteButtonHandler(todo.id)}
            &gt;
              삭제하기
            </button>
          </div>
        ))}
      </div>
    &lt;/&gt;
  );
};</p>
<p>export default App;</p>
<pre><code>
&gt;- ### **axios.patch**
 ```patch```는 어떤 데이터를 **수정요청 할때 사용.**
```js
axios.patch(url[, data[, config]])  // patch</code></pre><ul>
<li><strong>예시코드</strong><pre><code class="language-js">// src/App.jsx
</code></pre>
</li>
</ul>
<p>import React, { useEffect, useState } from &quot;react&quot;;
import axios from &quot;axios&quot;;</p>
<p>const App = () =&gt; {
  const [todo, setTodo] = useState({
    title: &quot;&quot;,
  });
  const [todos, setTodos] = useState(null);</p>
<p>  // patch에서 사용할 id, 수정값의 state를 추가
  const [targetId, setTargetId] = useState(null);
  const [editTodo, setEditTodo] = useState({
    title: &quot;&quot;,
  });</p>
<p>  const fetchTodos = async () =&gt; {
    const { data } = await axios.get(&quot;<a href="http://localhost:4000/todos&quot;">http://localhost:4000/todos&quot;</a>);
    setTodos(data);
  };</p>
<p>  const onSubmitHandler = (todo) =&gt; {
    axios.post(&quot;<a href="http://localhost:4000/todos&quot;">http://localhost:4000/todos&quot;</a>, todo);
  };</p>
<p>  const onClickDeleteButtonHandler = (todoId) =&gt; {
    axios.delete(<code>http://localhost:4000/todos/${todoId}</code>);
  };</p>
<p>  // 수정버튼 이벤트 핸들러 추가 👇
  const onClickEditButtonHandler = (todoId, edit) =&gt; {
    axios.patch(<code>http://localhost:4000/todos/${todoId}</code>, edit);
  };</p>
<p>  useEffect(() =&gt; {
    fetchTodos();
  }, []);</p>
<p>  return (
    &lt;&gt;
      &lt;form
        onSubmit={(e) =&gt; {
          e.preventDefault();
          onSubmitHandler(todo);
        }}
      &gt;
        {/* 👇 수정기능에 필요한 id, 수정값 input2개와 수정하기 버튼을 추가 <em>/}
        <div>
          &lt;input
            type=&quot;text&quot;
            placeholder=&quot;수정하고싶은 Todo ID&quot;
            onChange={(ev) =&gt; {
              setTargetId(ev.target.value);
            }}
          /&gt;
          &lt;input
            type=&quot;text&quot;
            placeholder=&quot;수정값 입력&quot;
            onChange={(ev) =&gt; {
              setEditTodo({
                ...editTodo,
                title: ev.target.value,
              });
            }}
          /&gt;
          &lt;button
                        // type=&#39;button&#39; 을 추가해야 form의 영향에서 벗어남
            type=&quot;button&quot;
            onClick={() =&gt; onClickEditButtonHandler(targetId, editTodo)}
          &gt;
            수정하기
          </button>
        </div>
        &lt;input
          type=&quot;text&quot;
          onChange={(ev) =&gt; {
            const { value } = ev.target;
            setTodo({
              ...todo,
              title: value,
            });
          }}
        /&gt;
        <button>추가하기</button>
      </form>
      <div>
        {todos?.map((todo) =&gt; (
          <div key={todo.id}>
                        {/</em> todo의 아이디를 화면에 표시 */}
            {todo.id} :{todo.title}
            &lt;button
              type=&quot;button&quot;
              onClick={() =&gt; onClickDeleteButtonHandler(todo.id)}
            &gt;
              삭제하기
            </button>
          </div>
        ))}
      </div>
    &lt;/&gt;
  );
};</p>
<p>export default App;</p>
<pre><code>#### 🙌 네트워크 쪽 개발을 할 때는 항상 브라우저에 있는 ```네트워크 탭```을 확인하면서 개발을 진행해야 해, 어떤 문제가 생겼을 때 이정보를 통해 디버깅을 할 수 있기 때문이야.

&lt;br/&gt;

## ** &lt;span style=&quot;color:Brown&quot;&gt;🚀 진짜 쉬운 설명**&lt;/span&gt;
Axios는 웹에서 데이터를 주고받는 작업을 간단하고 효율적으로 처리할 수 있도록 도와주는 도구야. &quot;서버와 대화할 때 쓰는 편리한 메신저&quot;로 생각하면 쉽다!

------
### ** &lt;span style=&quot;color:LightSteelBlue&quot;&gt;✏️ 더 알아가기 **&lt;/span&gt;


- **axios와 fetch 차이점**

| 항목                | Axios                                    | Fetch                                   |
|---------------------|------------------------------------------|-----------------------------------------|
| 사용 편의성         | 간단한 문법과 추가 기능 제공             | 기본적으로 약간의 추가 코드 필요         |
| JSON 자동 처리      | 요청 및 응답에서 JSON 자동 처리           | JSON 변환을 수동으로 처리해야 함         |
| 요청 취소 기능      | 기본적으로 요청 취소 기능 제공            | 추가 라이브러리(`AbortController`) 필요 |
| 오류 처리           | HTTP 오류를 자동으로 `.catch`에서 처리    | HTTP 오류를 직접 확인 후 처리 필요       |
| 브라우저 지원       | 모든 브라우저에서 동작                   | 최신 브라우저만 지원 (구형 브라우저 제한)|
| 추가 기능           | 타임아웃 설정, 헤더 관리 등 추가 기능 제공 | 기본 기능만 제공                        |


- **json-server API 명세서 확인방법**

Axios를 사용해서 GET 요청 코드를 작성하기에 앞서, 어떤 방식으로 요청 해야할지는 우리가 사용하는 json-server의 방식을 알아봐야 해. 

다시 말해, **Axios는 `GET` 요청을 할 수 있도록 도와주는 패키지일뿐이지, “어떻게 요청을 해야하지?” 와 같은 방식에 대한 확인은 우리가 사용할 API 명세서를 보아야 한다는 뜻**이야. 예를 들어 `GET` 요청을 할 때 **path variable**로 해야할지, **query**로 보내야할지는 API를 만든 사람이 하라는대로 해야 하기때문이지.

- **.env와 .gitignore**란?
&gt; **.env(환경 변수 파일)**
.env 파일은 중요한 설정 값이나 비밀 정보를 저장하는 파일이야.
(예) API 키, 데이터베이스 비밀번호, 서버 URL 등.
코드에 직접 노출하지 않고, 안전하게 관리하기 위해 사용해.

 - **.env 사용법**
 1. 프로젝트에 .env 파일 생성: 파일 이름: ```.env```
 2. 환경 변수 저장: 키=값 형태로 작성.
```planitext
API_KEY=123456789
DATABASE_URL=https://example.com
SECRET_KEY=mysecret</code></pre><ol start="3">
<li>코드에서 불러오기.</li>
</ol>
<pre><code class="language-js">require(&#39;dotenv&#39;).config();

console.log(process.env.API_KEY); // &quot;123456789&quot;</code></pre>
<blockquote>
<p> <strong>.gitignore(Git 제외 파일 설정)</strong>
.gitignore 파일은 Git에 포함시키지 않을 파일이나 폴더를 지정하는 파일야.
(예) node_modules, .env, 빌드 파일 등.
민감한 정보나 불필요한 파일이 저장소에 올라가지 않도록 방지해.</p>
</blockquote>
<ul>
<li><strong>.gitignore 사용법</strong></li>
</ul>
<ol>
<li>프로젝트에 .gitignore 파일 생성: 파일 이름: <code>.gitignore</code></li>
<li>Git에 포함하지 않을 파일 추가: 무시할 파일이나 폴더를 작성.<pre><code class="language-planitext"># node_modules 폴더 제외
node_modules/
</code></pre>
</li>
</ol>
<h1 id="환경-변수-파일-제외">환경 변수 파일 제외</h1>
<p>.env</p>
<h1 id="로그-파일-제외">로그 파일 제외</h1>
<p>*.log</p>
<h1 id="운영-시스템별-파일-제외-예-macos">운영 시스템별 파일 제외 (예: macOS)</h1>
<p>.DS_Store</p>
<pre><code>3. 확인하기.
.gitignore에 추가된 파일은 Git 상태에 나타나지 않아.
이미 커밋된 파일은 제외되지 않으므로, 이를 삭제하려면 아래 명령어를 사용해야해.

```bash
git rm --cached 파일이름</code></pre><blockquote>
<ul>
<li><strong>.env가 중요한 이유:</strong></li>
</ul>
</blockquote>
<ul>
<li>보안: 중요한 정보를 코드에 직접 노출하지 않음.</li>
<li>환경별 설정 관리: 로컬 개발, 스테이징, 프로덕션 환경별로 다른 설정을 쉽게 관리.<ul>
<li><strong>.gitignore가 중요한 이유:</strong></li>
<li>저장소 정리: 프로젝트와 관련 없는 파일을 Git에 올리지 않아, 저장소가 깔끔해.</li>
<li>보안: 민감한 정보(.env 파일 등)가 저장소에 올라가지 않도록 방지.</li>
<li><strong>한줄요약</strong></li>
</ul>
</li>
<li><code>.env</code> 민감한 정보(예: API 키, 비밀번호)를 안전하게 저장.</li>
<li><code>.gitignore</code> Git에 올리지 않을 파일을 지정해 저장소를 깨끗하고 안전하게 관리.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React ] study(4) json-server]]></title>
            <link>https://velog.io/@k_kyu/React-study4-json-server</link>
            <guid>https://velog.io/@k_kyu/React-study4-json-server</guid>
            <pubDate>Mon, 13 Jan 2025 06:32:43 GMT</pubDate>
            <description><![CDATA[<h1 id="-span-stylecolorcornflowerbluejson-server란-span">** <span style="color:CornflowerBlue">json-server**란, </span></h1>
<p>가짜 서버(fake server)를 간단히 만들 수 있는 도구! 
JSON 파일 하나만 있으면, 데이터를 읽고 쓰는 간단한 API 서버를 빠르게 만들 수있고 보다 실무적으로 데이터를 가져오는 연습을 할 수 있다.</p>
<h2 id="-span-stylecolorbrown📍핵심-설명span">** <span style="color:Brown">📍핵심 설명**</span></h2>
<ul>
<li><p>json-server는 <strong>&quot;가상의 음식 주문 시스템&quot;</strong>처럼 동작해!</p>
</li>
<li><p>메뉴판(<strong>JSON 파일</strong>): &quot;메뉴판&quot;에 음식(데이터)을 적어 놓는다. (예) 피자, 치킨, 버거.</p>
</li>
<li><p>주문(<strong>API 요청</strong>): &quot;피자 주세요!&quot;라고 주문(GET 요청).
&quot;새로운 음식을 추가하고 싶어요!&quot;라고 요청(POST 요청).</p>
</li>
<li><p>주방(<strong>json-server</strong>): 메뉴판(JSON 파일)을 읽고, 요청대로 처리.</p>
</li>
<li><p>어떤 상황에서 쓰냐면?
프론트엔드 개발자가 백엔드가 준비되지 않았을 때 임시 서버로 사용할 수 있고, 실제 서버처럼 동작하므로, REST API를 테스트할 때 유용해!</p>
</li>
</ul>
<br/>

<h3 id="span-stylecolorcornflowerblue🔍--json-server-사용-예제span"><span style="color:CornflowerBlue">🔍  json-server 사용 예제</span></h3>
<p><strong>1️⃣  json-server 설치</strong></p>
<table>
<thead>
<tr>
<th>패키지 매니저</th>
<th>설치 명령어</th>
</tr>
</thead>
<tbody><tr>
<td>yarn</td>
<td><code>yarn global add json-server</code></td>
</tr>
<tr>
<td>pnpm</td>
<td><code>pnpm add -g json-server</code></td>
</tr>
</tbody></table>
<p><strong>2️⃣ JSON 파일 만들기</strong>
데이터를 담은 JSON 파일(db.json)을 준비.</p>
<pre><code class="language-json">{
  &quot;posts&quot;: [
    { &quot;id&quot;: 1, &quot;title&quot;: &quot;Hello World&quot; },
    { &quot;id&quot;: 2, &quot;title&quot;: &quot;json-server is awesome!&quot; }
  ]
}</code></pre>
<p><strong>3️⃣ 서버 실행</strong></p>
<table>
<thead>
<tr>
<th>작업</th>
<th>실행 명령어</th>
</tr>
</thead>
<tbody><tr>
<td>서버 실행</td>
<td><code>json-server --watch db.json</code></td>
</tr>
</tbody></table>
<p><strong>4️⃣ 테스트</strong>
<a href="http://localhost:3000/posts%EB%A1%9C">http://localhost:3000/posts로</a> API 요청을 테스트 가능!</p>
<ul>
<li><strong>GET 요청</strong>: 모든 게시글 가져오기<ul>
<li>URL: <a href="http://localhost:3000/posts">http://localhost:3000/posts</a></li>
</ul>
</li>
<li><strong>POST 요청</strong>: 새로운 게시글 추가<ul>
<li>URL: <a href="http://localhost:3000/posts">http://localhost:3000/posts</a></li>
<li>데이터: { &quot;id&quot;: 3, &quot;title&quot;: &quot;New Post&quot; }</li>
</ul>
</li>
<li><strong>PUT 요청</strong>: 특정 게시글 수정<ul>
<li>URL: <a href="http://localhost:3000/posts/1">http://localhost:3000/posts/1</a></li>
<li>데이터: { &quot;title&quot;: &quot;Updated Title&quot; }</li>
</ul>
</li>
<li><strong>DELETE 요청</strong>: 특정 게시글 삭제<ul>
<li>URL: <a href="http://localhost:3000/posts/1">http://localhost:3000/posts/1</a></li>
</ul>
</li>
</ul>
<br/>

<h2 id="-span-stylecolorbrown🚀-진짜-쉬운-설명span">** <span style="color:Brown">🚀 진짜 쉬운 설명**</span></h2>
<p>json-server는 JSON 파일 하나로 간단한 REST API 서버를 빠르게 만들고, 데이터를 읽고 쓰는 테스트를 할 수 있는 도구라고 생각하면 돼!</p>
<hr>
<h3 id="-span-stylecolorlightsteelblue✏️-더-알아가기-span">** <span style="color:LightSteelBlue">✏️ 더 알아가기 **</span></h3>
<ul>
<li><strong>json-server 더 알아보기!</strong></li>
</ul>
<blockquote>
<p>** 1️⃣ 기본 포트 변경하기**
기본적으로 포트 3000에서 실행되지만, 다른 포트로 변경도 가능해!
(<code>--port</code> 옵션을 사용하면 포트를 5000, 8080 등 원하는 값으로 변경가능)</p>
</blockquote>
<pre><code>json-server --watch db.json --port 5000</code></pre><blockquote>
<p><strong>2️⃣ 라우팅 설정 (Custom Routes)</strong>
RESTful API 외에 특정 URL로 요청을 라우팅할 수 있습니다.
routes.json 파일을 만들어 라우팅을 정의 가능.</p>
</blockquote>
<ul>
<li>(예) routes.json<pre><code class="language-json">{
&quot;/api/posts&quot;: &quot;/posts&quot;,
&quot;/api/users&quot;: &quot;/users&quot;
}</code></pre>
<ul>
<li>실행<pre><code>json-server --watch db.json --routes routes.json</code></pre></li>
<li>결과
<code>/api/posts</code> → <code>/posts</code>
<code>/api/users</code> → <code>/users</code></li>
</ul>
</li>
</ul>
<blockquote>
<p><strong>3️⃣ 쿼리 파라미터 지원</strong>
JSON 데이터를 필터링, 정렬, 페이지네이션 등으로 조작가능.
<strong>1. 필터링</strong>
db.json의 posts 중에서 author가 &quot;John&quot;인 데이터만 가져옴.</p>
</blockquote>
<pre><code>GET /posts?author=John</code></pre><p><strong>2. 정렬</strong>
id를 기준으로 내림차순 정렬.</p>
<pre><code>GET /posts?_sort=id&amp;_order=desc</code></pre><p><strong>3. 페이지네이션</strong>
한 페이지에 5개씩 데이터를 나누고, 2페이지를 가져옴.</p>
<pre><code>GET /posts?_page=2&amp;_limit=5</code></pre><blockquote>
<p><strong>4️⃣ 데이터 관계 설정 (Nested Routes)</strong>
db.json에서 데이터를 중첩된 형태로 저장하면, 관계형 데이터를 사용가능.</p>
</blockquote>
<pre><code>{
  &quot;posts&quot;: [
    { &quot;id&quot;: 1, &quot;title&quot;: &quot;First Post&quot;, &quot;author&quot;: &quot;John&quot; }
  ],
  &quot;comments&quot;: [
    { &quot;id&quot;: 1, &quot;postId&quot;: 1, &quot;content&quot;: &quot;Great post!&quot; }
  ]
}</code></pre><p>요청 예시 <code>GET /posts/1/comments</code>(postId가 1인 댓글만 가져옴.)</p>
<ul>
<li><strong>json-server를 더 잘 활용하는 팁</strong><ul>
<li>빠른 프로토타입 제작: 백엔드가 준비되지 않았을 때 임시로 사용.</li>
<li>API 테스트: 프론트엔드 개발자들이 실제 서버를 기다리지 않고 작업 가능.</li>
<li>연습 환경: RESTful API 요청을 배우거나 테스트할 때 사용.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React ] study(3) HTTP]]></title>
            <link>https://velog.io/@k_kyu/React-study2-HTTP</link>
            <guid>https://velog.io/@k_kyu/React-study2-HTTP</guid>
            <pubDate>Mon, 13 Jan 2025 05:21:37 GMT</pubDate>
            <description><![CDATA[<h1 id="-span-stylecolorcornflowerbluehttp란-span">** <span style="color:CornflowerBlue">HTTP**란, </span></h1>
<p>HTTP(HyperText Transfer Protocol)는 <strong>웹에서 데이터를 주고받는 약속(규칙)</strong>이야.
쉽게 말해, 인터넷에서 컴퓨터끼리 대화하는 언어라고 생각하며 돼.</p>
<h2 id="-span-stylecolorbrown📍핵심쉬운-설명span">** <span style="color:Brown">📍핵심(쉬운) 설명**</span></h2>
<ul>
<li>웹 브라우저와 서버의 대화<ul>
<li><strong>웹 브라우저</strong>: &quot;안녕하세요! 저 구글 홈페이지 주세요~&quot; (요청, Request)<ul>
<li><strong>서버</strong>: &quot;여기 있어요! 구글 홈페이지 데이터입니다~&quot; (응답, Response)
HTTP는 이렇게 <strong>요청(Request)과 응답(Response)</strong>을 주고받는 방식!</li>
</ul>
</li>
</ul>
</li>
</ul>
<br/>

<h3 id="span-stylecolorcornflowerblue🔍-http-요청응답의-구조span"><span style="color:CornflowerBlue">🔍 HTTP 요청/응답의 구조</span></h3>
<p>1️⃣ <strong>HTTP 요청</strong>
클라이언트가 서버에게 데이터를 요청.</p>
<ul>
<li>주요 구성:<ul>
<li>메서드: 어떤 작업을 할지 지정 (예: GET, POST).</li>
<li>URL: 요청할 리소스 주소.</li>
<li>헤더(Header): 요청 정보 (예: 인증, 언어 설정).</li>
<li>본문(Body): 서버로 보낼 데이터 (POST 요청 시).</li>
</ul>
</li>
</ul>
<p><strong>2️⃣ HTTP 응답</strong>
서버가 클라이언트에게 데이터를 응답.</p>
<ul>
<li>주요 구성:<ul>
<li>상태 코드(Status Code): 요청 처리 결과 (예: 200, 404, 500).</li>
<li>헤더(Header): 응답 정보 (예: 콘텐츠 타입).</li>
<li>본문(Body): 클라이언트에게 보내는 실제 데이터.</li>
</ul>
</li>
</ul>
<p>3️⃣ <strong>HTTP 상태 코드</strong></p>
<ul>
<li><p><strong>1xx: 정보</strong></p>
<ul>
<li><strong><code>100</code> Continue</strong> 요청의 일부를 서버가 받았으며, 나머지를 계속 보내라는 의미.</li>
</ul>
</li>
<li><p><strong>2xx: 성공</strong></p>
<ul>
<li><strong><code>200</code> OK</strong> 요청이 성공적으로 처리되었음.</li>
<li><strong><code>201</code> Created</strong> 요청이 성공적이었으며, 새로운 자원이 생성되었음.</li>
</ul>
</li>
<li><p><strong>3xx: 리다이렉션</strong></p>
<ul>
<li><strong><code>301</code> Moved Permanently</strong> 요청한 리소스가 영구적으로 새로운 URL로 이동.</li>
<li><strong><code>302</code> Found</strong> 요청한 리소스가 임시로 다른 URL로 이동.</li>
</ul>
</li>
<li><p><strong>4xx: 클라이언트 오류</strong></p>
<ul>
<li><strong><code>400</code> Bad Request</strong> 잘못된 요청임.</li>
<li><strong><code>401</code> Unauthorized</strong> 인증이 필요함.<ul>
<li><strong><code>404</code> Not Found</strong> 요청한 리소스를 찾을 수 없음.</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>5xx: 서버 오류</strong></p>
<ul>
<li><strong><code>500</code> Internal Server Error</strong> 서버가 요청을 처리하는 동안 오류가 발생.</li>
<li><strong><code>502</code> Bad Gateway</strong> 서버가 게이트웨이 또는 프록시 역할을 하는 서버로부터 유효하지 않은 응답을 받음.</li>
</ul>
<br/>

</li>
</ul>
<h3 id="span-stylecolorcornflowerblue🔍-http-메서드span"><span style="color:CornflowerBlue">🔍 HTTP 메서드</span></h3>
<p>HTTP 메서드는 클라이언트가 서버에게 요청의 성격을 알리는 데 사용됨.</p>
<ul>
<li><p><strong>GET</strong></p>
<p>  서버로부터 <code>데이터를 요청할 때</code> <strong>*<em>사용해요. 요청 데이터가 *</em>URL에 포함</strong>되어 전송되며, 주로 데이터를 조회할 때 사용.</p>
<ul>
<li><strong>REST API에서의 사용</strong>: 특정 리소스를 조회할 때 사용.</li>
</ul>
</li>
<li><p><strong>POST</strong></p>
<ul>
<li><strong>정의</strong>: 서버에 <code>데이터를 제출할 때</code> 사용.</li>
<li><strong>특징</strong>: 요청 데이터가 요청 본문에 포함되어 전송되며, 주로 데이터를 생성하거나 제출할 때 사용.</li>
<li><strong>REST API에서의 사용</strong>: 새로운 리소스를 생성할 때 사용.</li>
</ul>
</li>
<li><p><strong>PUT, PATCH</strong></p>
<ul>
<li><strong>정의</strong>: 서버의 <code>데이터를 업데이트할 때</code> 사용.</li>
<li><strong>특징</strong>: 요청 데이터가 요청 본문에 포함되어 전송되며, 주로 기존 데이터를 수정할 때 사용.</li>
<li><strong>REST API에서의 사용</strong>: 기존 리소스를 수정할 때 사용.</li>
</ul>
</li>
<li><p><strong>DELETE</strong></p>
<ul>
<li><strong>정의</strong>: 서버의 <code>데이터를 삭제할 때</code> 사용.</li>
<li><strong>특징</strong>: 주로 특정 데이터를 삭제할 때 사용.</li>
<li><strong>REST API에서의 사용</strong>: 특정 리소스를 삭제할 때 사용.</li>
</ul>
<br/>

</li>
</ul>
<h3 id="span-stylecolorcornflowerblue🔍-restful-원칙에-따라-작성된-api-명세서span"><span style="color:CornflowerBlue">🔍 RESTful 원칙에 따라 작성된 API 명세서</span></h3>
<ul>
<li> 기본 URL <code>https://api.example.com</code></li>
<li>엔드포인트</li>
</ul>
<table>
<thead>
<tr>
<th>순번</th>
<th>요청 내용</th>
<th>method</th>
<th>url</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>게시글을 추가</td>
<td>POST</td>
<td>/posts</td>
</tr>
<tr>
<td>2</td>
<td>모든 게시글 조회</td>
<td>GET</td>
<td>/posts</td>
</tr>
<tr>
<td>3</td>
<td>특정 게시글 조회</td>
<td>GET</td>
<td>/posts/:id</td>
</tr>
<tr>
<td>4</td>
<td>특정 게시글 정보 업데이트</td>
<td>PUT</td>
<td>/posts/:id</td>
</tr>
<tr>
<td>5</td>
<td>특정 게시글 정보 일부 수정</td>
<td>PATCH</td>
<td>/posts/:id</td>
</tr>
<tr>
<td>6</td>
<td>특정 게시글 정보 삭제</td>
<td>DELETE</td>
<td>/posts/:id</td>
</tr>
</tbody></table>
  <br/>

<h3 id="span-stylecolorcornflowerblue🔍-응답요청-예시span"><span style="color:CornflowerBlue">🔍 응답/요청 예시</span></h3>
<blockquote>
<p><strong>Content-Type: application/json</strong></p>
</blockquote>
<ul>
<li><p><strong>Content-Type</strong> 헤더는 서버와 클라이언트가 주고받는 데이터의 형식(타입)을 알려주는 정보야.</p>
</li>
<li><p><strong>application/json</strong>은 이 데이터가 JSON 형식임을 나타내.</p>
</li>
<li><p>쉽게 비유하기, 이걸 <strong>택배 포장</strong>에 비유해볼게!
택배를 보내기 전에 상자에 &quot;옷&quot;, &quot;책&quot;, &quot;전자제품&quot; 등 포장된 물건의 종류를 적어둬야겠지?
마찬가지로, HTTP에서 Content-Type은 본문 데이터가 어떤 형식인지 알려주는 <strong>&quot;라벨&quot;</strong> 역할을 하는 거야.</p>
</li>
<li><p>JSON 형식이란?(다음에 더 자세히 다룰거야!)
JSON (JavaScript Object Notation)은 데이터를 표현하는 가볍고 읽기 쉬운 형식.</p>
<pre><code class="language-json">{
&quot;name&quot;: &quot;Alice&quot;,
&quot;age&quot;: 25,
&quot;isStudent&quot;: true
}</code></pre>
</li>
<li><p><strong>Content-Type: application/json의 역할</strong></p>
<ul>
<li>클라이언트 → 서버로 요청: 클라이언트는 요청 본문이 JSON 형식임을 서버에게 알려줘.<pre><code class="language-http">POST /api/users HTTP/1.1
Content-Type: application/json
</code></pre>
</li>
</ul>
</li>
</ul>
<p>{
  &quot;name&quot;: &quot;Alice&quot;,
  &quot;age&quot;: 25
}</p>
<pre><code>
- 서버 → 클라이언트로 응답: 서버는 응답 본문이 JSON 형식임을 클라이언트에게 알려줘.
```http
HTTP/1.1 200 OK
Content-Type: application/json

{
  &quot;message&quot;: &quot;Success&quot;,
  &quot;data&quot;: {
    &quot;id&quot;: 1,
    &quot;name&quot;: &quot;Alice&quot;
  }
}</code></pre><h4 id="🙌-요게-왜-중요하냐면">🙌 요게 왜 중요하냐면!</h4>
<p>서버와 클라이언트는 서로 다른 시스템이기 때문에, 데이터 형식을 명확히 알려줘야 문제가 생기지 않아. 서버와 클라이언트가 데이터를 올바르게 해석하고 처리할 수 있게 도와주는거지! <strong>Content-Type: application/json</strong>은 데이터를 JSON 형식으로 처리하도록 약속하는 거야.</p>
<br/>

<h2 id="-span-stylecolorbrown🚀-진짜-쉬운-설명span">** <span style="color:Brown">🚀 진짜 쉬운 설명**</span></h2>
<p>HTTP는 웹 브라우저와 서버가 데이터를 주고받기 위해 사용하는 약속이야.
&quot;클라이언트가 요청(Request), 서버가 응답(Response)&quot;의 원리로 동작해!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React ] study(2) Async / Await]]></title>
            <link>https://velog.io/@k_kyu/React-study2-Async-Await</link>
            <guid>https://velog.io/@k_kyu/React-study2-Async-Await</guid>
            <pubDate>Mon, 13 Jan 2025 04:32:43 GMT</pubDate>
            <description><![CDATA[<h1 id="-span-stylecolorcornflowerblueasync--await란-span">** <span style="color:CornflowerBlue">Async / Await**란, </span></h1>
<p>Promise 기반 비동기 작업을 더 읽기 쉽게 작성할 수 있도록 도와주는 문법이야.
마치 비동기 작업을 동기 코드처럼 작성할 수 있게 해주는 도구,
결과가 나올 때까지 잠깐 기다렸다가 다음 일을 한다고 생각하면 돼!</p>
<h2 id="-span-stylecolorbrown📍핵심쉬운-설명span">** <span style="color:Brown">📍핵심(쉬운) 설명**</span></h2>
<p>Async / Await은 음식 주문 후 기다리는 과정과 비슷해.</p>
<ul>
<li>음식 주문 (Promise 생성): 식당에서 음식을 주문. 이 작업은 시간이 걸리므로 비동기로 처리.</li>
<li><strong>음식을 기다림(await)</strong>: 음식을 기다리는 동안 다른 일을 하지 않고, 멈춰서 결과를 기다려.</li>
<li>음식이 나옴 (Promise 완료): 음식이 나오면, 그때부터 다음 일을 진행.
Async / Await은 이렇게 <strong>&quot;주문 → 기다림 → 음식 도착 후 작업&quot;</strong>을 코드로 표현.</li>
</ul>
<br/>

<h3 id="span-stylecolorcornflowerblue🔍-async--await의-핵심-개념span"><span style="color:CornflowerBlue">🔍 Async / Await의 핵심 개념</span></h3>
<ul>
<li><strong>async</strong> 함수: 비동기 작업을 처리하기 위한 함수로, 항상 Promise를 반환.
함수 앞에 async를 붙이면, 그 안에서 await사용 가능.</li>
</ul>
<ul>
<li><strong>await</strong> 키워드: Promise가 완료될 때까지 기다렸다가 결과를 반환. 
await은 async 함수 안에서만 사용 가능.</li>
</ul>
<br/>

<h3 id="-span-stylecolorcornflowerblue🔍-자주-사용되는-패턴예시span">** <span style="color:CornflowerBlue">🔍 자주 사용되는 패턴(예시)**</span></h3>
<blockquote>
<p><strong>1️⃣ 기본 사용법</strong></p>
</blockquote>
<pre><code class="language-js">const fetchData = async () =&gt; {
  const response = await fetch(&quot;https://jsonplaceholder.typicode.com/posts/1&quot;); // 데이터를 가져올 때까지 기다림
  const data = await response.json(); // JSON 파싱이 완료될 때까지 기다림
  console.log(data); // 데이터 출력
};
fetchData();</code></pre>
<ul>
<li>동작 과정: fetch 함수가 데이터를 가져올 때까지 await로 기다림. -&gt; 가져온 데이터를 JSON으로 변환할 때도 await로 기다림. -&gt; 최종 데이터를 출력.</li>
</ul>
<blockquote>
<p><strong>2️⃣ Async / Await로 에러 처리</strong></p>
</blockquote>
<pre><code class="language-js">const fetchData = async () =&gt; {
  try {
    const response = await fetch(&quot;https://jsonplaceholder.typicode.com/posts/1&quot;);
    if (!response.ok) {
      throw new Error(&quot;데이터를 가져오는데 실패했습니다.&quot;);
    }
    const data = await response.json();
    console.log(data);
  } catch (error) {
    console.error(&quot;에러 발생:&quot;, error.message);
  }
};
fetchData();</code></pre>
<ul>
<li>특징: <code>try...catch</code> 블록을 사용해 에러를 처리.
Promise의 <code>.catch()</code> 대신 동기 코드처럼 작성 가능.</li>
</ul>
<h4 id="🙌-promise를-왜-사용할까">🙌 Promise를 왜 사용할까?</h4>
<ul>
<li>코드 가독성 증가: <code>.then()</code> 체인이 없는 깔끔한 코드 작성 가능.
동기 코드처럼 읽히므로, 쉬운 유지보수.</li>
<li>에러 처리 간단: <code>try...catch</code> 블록으로 에러를 다룰 수 있어, 코드가 더 직관적.</li>
<li>Promise보다 간결: 여러 비동기 작업이 연결될 때도 쉽게 작성 가능.</li>
</ul>
<br/>

<h2 id="-span-stylecolorbrown🚀-진짜-쉬운-설명span">** <span style="color:Brown">🚀 진짜 쉬운 설명**</span></h2>
<p>Async / Await은 Promise 기반의 비동기 작업을 더 동기적으로 보이게 작성하는 방법으로, 코드 가독성과 유지보수를 크게 향상시킴.</p>
<p>(더 쉬운 예시)</p>
<ul>
<li><strong>비동기 작업 없이 코드를 작성</strong><pre><code class="language-js">console.log(&quot;1. 피자를 주문합니다.&quot;);
// 피자가 나올 때까지 기다리지 않고, 그냥 다음 일을 해버림
console.log(&quot;2. 음료수를 주문합니다.&quot;);</code></pre>
</li>
<li><strong>Async / Await를 사용</strong><pre><code class="language-js">const orderPizza = async () =&gt; {
console.log(&quot;1. 피자를 주문합니다.&quot;);
await new Promise((resolve) =&gt; setTimeout(resolve, 3000)); // 3초 기다림
console.log(&quot;2. 피자가 도착했습니다!&quot;);
};
</code></pre>
</li>
</ul>
<p>orderPizza();</p>
<pre><code></code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[React ] study(1) Promise]]></title>
            <link>https://velog.io/@k_kyu/React-study1-Promise</link>
            <guid>https://velog.io/@k_kyu/React-study1-Promise</guid>
            <pubDate>Mon, 13 Jan 2025 03:44:11 GMT</pubDate>
            <description><![CDATA[<h1 id="-span-stylecolorcornflowerbluepromise란-span">** <span style="color:CornflowerBlue">Promise**란, </span></h1>
<p>비동기 작업(조금 시간이 걸리는 작업)의 결과를 미래에 제공하겠다고 약속하는 것이야.
완료 또는 실패를 처리하기 위해 사용되는 개념이기도 해.</p>
<h2 id="-span-stylecolorbrown📍핵심쉬운-설명span">** <span style="color:Brown">📍핵심(쉬운) 설명**</span></h2>
<ul>
<li><p>음식 주문 (<strong>Promise</strong>): 배달 앱에서 피자 주문.
이때, 앱은 &quot;피자가 곧 도착할 거야&quot;라고 약속해. (Promise 생성.)</p>
</li>
<li><p>피자가 오기 전 (<strong>Pending</strong>): 피자가 만들어지는 중이거나, 배달 중인 상태.
이 상태를 Pending 상태.(결과가 아직 준비되지 않음!)</p>
</li>
<li><p>피자가 도착 (<strong>Fulfilled</strong>): 배달원이 피자를 가져오면 약속이행.
이 상태를 Fulfilled 상태. (약속이 성공적으로 완료!)</p>
</li>
<li><p>문제가 생길 경우 (<strong>Rejected</strong>): 가게가 문을 닫았거나, 배달이 취소되면 약속을 지킬 수 없음. 이 상태를 Rejected 상태.(약속 실패!)</p>
</li>
</ul>
<br/>

<h3 id="span-stylecolorcornflowerblue🔍-promise-상태span"><span style="color:CornflowerBlue">🔍 Promise 상태</span></h3>
<ul>
<li><strong>Pending</strong>: 결과를 기다리는 중.(초기 상태) <code>resolve</code>나 <code>reject</code>로 인해 다른 상태로 변경되기 전까지의 상태.</li>
<li><strong>Fulfilled</strong>: 작업이 성공적으로 완료됨. <code>resolve</code>로 인해 <code>pending</code> 상태에서 fulfilled 상태로 변경.</li>
<li><strong>Rejected</strong>: 작업이 실패함. <code>reject</code>로 인해 <code>pending</code> 상태에서 <code>rejected</code> 상태로 변경.<h4 id="🙌-promise-객체는-then-catch-finally-메서드를-통해-이행되거나-거부된-이후의-동작을-정의할-수-있다">🙌 Promise 객체는 then, catch, finally 메서드를 통해 이행되거나 거부된 이후의 동작을 정의할 수 있다!</h4>
</li>
</ul>
<br/>

<h3 id="-span-stylecolorcornflowerblue🔍-자주-사용되는-패턴예시span">** <span style="color:CornflowerBlue">🔍 자주 사용되는 패턴(예시)**</span></h3>
<blockquote>
<p>1️⃣ <strong>Promise 생성</strong>(음식 주문하기)</p>
</blockquote>
<pre><code class="language-js">const orderPizza = new Promise((resolve, reject) =&gt; {
  const isStoreOpen = true;
  if (isStoreOpen) {
    resolve(&quot;피자가 도착했습니다! 🍕&quot;);
  } else {
    reject(&quot;가게가 문을 닫았습니다. 😢&quot;);
  }
});</code></pre>
<blockquote>
<p>2️⃣ <strong>Promise 처리</strong>(배달 결과 확인하기)</p>
</blockquote>
<pre><code class="language-js">orderPizza
  .then((message) =&gt; {
    console.log(message); // &quot;피자가 도착했습니다! 🍕&quot;
  })
  .catch((error) =&gt; {
    console.error(error); // &quot;가게가 문을 닫았습니다. 😢&quot;
  });</code></pre>
<h4 id="🙌-promise를-왜-사용할까">🙌 Promise를 왜 사용할까?</h4>
<ul>
<li><p>기다림 관리: 네트워크 요청(예: 데이터 가져오기), 파일 읽기 등 시간이 걸리는 작업의 결과를 효율적으로 처리.</p>
</li>
<li><p>코드 정리: 비동기 작업을 순서대로 처리할 때 코드가 더 읽기 쉽게 정리됩니다.</p>
</li>
</ul>
<br/>

<h2 id="-span-stylecolorbrown🚀-진짜-쉬운-설명span">** <span style="color:Brown">🚀 진짜 쉬운 설명**</span></h2>
<p>Promise는 결과가 나중에 제공되는 작업을 처리하기 위한 도구로, 성공(Fulfilled) 또는 실패(Rejected) 상태에 따라 원하는 동작을 정의할 수 있다.</p>
<hr>
<h3 id="-span-stylecolorlightsteelblue✏️-더-알아가기-span">** <span style="color:LightSteelBlue">✏️ 더 알아가기 **</span></h3>
<ul>
<li><p><strong>Promise.all이란?</strong>
여러 개의 Promise를 동시에 실행하고, 모두 완료될 때까지 기다린 후 결과를 반환하는 메서드.
즉, &quot;여러 작업을 한꺼번에 처리하고, 결과를 한 번에 받아오겠다&quot;는 의미.</p>
</li>
<li><p><strong>Promise.all을 쉽게 비유하기</strong>
Promise.all은 여러 음식을 한꺼번에 배달 주문하는 것과 비슷해.</p>
<ul>
<li><p>여러 음식을 주문 (Promise 생성): 피자, 치킨, 그리고 버거를 각각 주문.(여러 개의 Promise를 생성.)</p>
</li>
<li><p>모든 음식이 도착해야 완료 (모든 Promise가 Fulfilled): 모든 배달원이 음식을 가져와야 완료.(Promise.all은 모든 Promise가 성공해야 결과를 반환.)</p>
</li>
<li><p>한 음식이라도 실패하면 전체 실패 (Rejected): 예를 들어, 피자 가게가 문을 닫았으면 전체 주문이 실패. (Promise.all은 하나라도 Rejected 상태면 전체가 실패로 간주.)</p>
</li>
</ul>
</li>
</ul>
<ul>
<li><p><strong>Promise.all의 특징</strong></p>
<ul>
<li>모든 Promise가 Fulfilled: 모든 Promise가 성공적으로 완료되면, 결과를 배열로 반환.</li>
<li>하나라도 Rejected: Promise 중 하나라도 실패하면 즉시 에러를 반환하고 멈춤.</li>
<li>병렬 실행: Promise를 동시에 실행하므로 작업 시간을 줄일 수 있음.</li>
</ul>
</li>
<li><p>** Promise.all 예제**</p>
</li>
</ul>
<blockquote>
<p><strong>1️⃣ 기본 사용법</strong></p>
</blockquote>
<pre><code class="language-js">const pizza = new Promise((resolve) =&gt; setTimeout(() =&gt; resolve(&quot;피자 도착! 🍕&quot;), 3000));
const chicken = new Promise((resolve) =&gt; setTimeout(() =&gt; resolve(&quot;치킨 도착! 🍗&quot;), 2000));
const burger = new Promise((resolve) =&gt; setTimeout(() =&gt; resolve(&quot;버거 도착! 🍔&quot;), 1000));
Promise.all([pizza, chicken, burger])
  .then((results) =&gt; {
    console.log(&quot;모든 음식 도착:&quot;, results);
    // 결과: [&quot;피자 도착! 🍕&quot;, &quot;치킨 도착! 🍗&quot;, &quot;버거 도착! 🍔&quot;]
  })
  .catch((error) =&gt; {
    console.error(&quot;배달 실패:&quot;, error);
  });</code></pre>
<p><strong>2️⃣ 하나의 Promise라도 실패하는 경우</strong></p>
<pre><code class="language-js">const pizza = new Promise((resolve) =&gt; setTimeout(() =&gt; resolve(&quot;피자 도착! 🍕&quot;), 3000));
const chicken = new Promise((resolve, reject) =&gt; setTimeout(() =&gt; reject(&quot;치킨 배달 실패!&quot;), 2000));
const burger = new Promise((resolve) =&gt; setTimeout(() =&gt; resolve(&quot;버거 도착! 🍔&quot;), 1000));
Promise.all([pizza, chicken, burger])
  .then((results) =&gt; {
    console.log(&quot;모든 음식 도착:&quot;, results);
  })
  .catch((error) =&gt; {
    console.error(&quot;배달 실패:&quot;, error);
    // 결과: &quot;배달 실패: 치킨 배달 실패!&quot;
  });</code></pre>
<ul>
<li><p><strong>Promise.all을 사용하는 이유</strong></p>
<ul>
<li>동시에 작업 처리: 여러 작업을 병렬로 처리해 시간을 절약할 수 있어요.</li>
<li>결과를 한꺼번에 처리: 모든 작업이 완료된 후, 결과를 한 번에 가져올 수 있습니다.</li>
<li>실패 처리 용이: 작업 중 하나라도 실패하면 에러를 쉽게 감지하고 처리할 수 있습니다.</li>
</ul>
</li>
<li><p>*<em>한줄 요약 *</em>
Promise.all은 여러 Promise를 병렬로 실행하고, 모든 작업이 완료되면 결과를 배열로 반환하는 메서드로, 동시에 여러 작업을 효율적으로 처리할 때 사용.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[A project ] study(4) React Hooks와 React Hook Form]]></title>
            <link>https://velog.io/@k_kyu/A-project-study4-React-Hooks%EC%99%80-React-Hook-Form%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90</link>
            <guid>https://velog.io/@k_kyu/A-project-study4-React-Hooks%EC%99%80-React-Hook-Form%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90</guid>
            <pubDate>Fri, 10 Jan 2025 13:31:55 GMT</pubDate>
            <description><![CDATA[<h1 id="-span-stylecolorcornflowerbluereact-hooks란-span">** <span style="color:CornflowerBlue">React Hooks**란, </span></h1>
<p>React Hooks는 React에서 상태(state)와 생명주기(lifecycle)를 관리할 수 있도록 해주는 기능이야.
즉, 클래스 컴포넌트가 아닌 함수형 컴포넌트에서도 상태를 사용할 수 있게 해주는 도구지.</p>
<ul>
<li><p>React Hooks는 컴포넌트의 &quot;수납공간&quot;
React 컴포넌트가 단순히 UI만 보여주는 게 아니라, 데이터를 기억하거나, 어떤 동작을 처리해야 할 때 사용해.</p>
</li>
<li><p>Hooks는 컴포넌트 안에 상태를 저장할 공간을 만들어줘.</p>
</li>
<li><p>주요 React Hooks</p>
<ul>
<li><strong>useState:</strong> 상태를 추가하고 관리.
(예) 카운터 숫자를 기억하거나, 다크 모드 상태를 저장.<ul>
<li><strong>useEffect:</strong> 컴포넌트가 화면에 나타날 때나 사라질 때 실행할 동작을 정의.
(예) API 호출, 구독(subscribe) 등.</li>
<li><strong>useContext:</strong> 전역 데이터를 공유.
(예) 로그인 상태나 테마 설정을 여러 컴포넌트에서 공유.</li>
<li><strong>useReducer:</strong> 복잡한 상태 업데이트 로직을 관리.
(예) 상태를 여러 단계로 변경해야 하는 경우.</li>
</ul>
</li>
</ul>
</li>
<li><p>React Hooks 간단 예제</p>
<pre><code class="language-js">import React, { useState } from &#39;react&#39;;
</code></pre>
</li>
</ul>
<p>const Counter = () =&gt; {
  const [count, setCount] = useState(0); // 상태 선언</p>
<p>  return (
    <div>
      <p>현재 카운트: {count}</p>
      &lt;button onClick={() =&gt; setCount(count + 1)}&gt;증가</button>
      &lt;button onClick={() =&gt; setCount(count - 1)}&gt;감소</button>
    </div>
  );
};</p>
<p>export default Counter;</p>
<p>//useState를 사용해 숫자 상태를 저장. 버튼을 누를 때마다 상태가 업데이트되고, 화면이 변경.</p>
<pre><code>
&lt;br/&gt;

# ** &lt;span style=&quot;color:CornflowerBlue&quot;&gt;React Hook Form**이란, &lt;/span&gt;

React Hook Form은 React에서 폼 데이터를 쉽게 관리할 수 있도록 도와주는 라이브러리야.
폼(input, checkbox 등)에서 입력된 값을 저장하고 검증하는 과정을 간단하게 만들어.

- React Hook Form은 &quot;폼 관리자&quot;
입력값을 관리하고, 올바르게 입력되지 않았을 때 경고를 띄워주는 관리자 역할을 해.
- 기본 HTML 폼을 사용하면 데이터 관리가 번거롭지만, React Hook Form을 사용하면 훨씬 간단져.

- React Hook Form의 주요 특징
   - 폼 상태 관리: 입력값(예: 이름, 이메일 등)을 쉽게 저장하고 업데이트.
   - 유효성 검증: 값이 비어 있거나, 형식이 맞지 않을 때 에러 메시지 표시.
   - 가벼움: React의 기본 기능을 활용해 성능에 부담을 주지 않음.
   - 외부 라이브러리와 통합 가능: Zod, Yup 같은 검증 라이브러리와 함께 사용해 더 강력한 유효성 검증 가능.

- React Hook Form 간단 예제
```js
import React from &#39;react&#39;;
import { useForm } from &#39;react-hook-form&#39;;

const SignupForm = () =&gt; {
  const {
    register, // 입력값을 관리하는 함수
    handleSubmit, // 폼 제출 처리 함수
    formState: { errors }, // 유효성 검사 에러
  } = useForm();

  const onSubmit = (data) =&gt; {
    console.log(&quot;입력 데이터:&quot;, data);
  };

  return (
    &lt;form onSubmit={handleSubmit(onSubmit)}&gt;
      &lt;div&gt;
        &lt;label&gt;이름&lt;/label&gt;
        &lt;input {...register(&quot;name&quot;, { required: &quot;이름은 필수입니다.&quot; })} /&gt;
        {errors.name &amp;&amp; &lt;p&gt;{errors.name.message}&lt;/p&gt;}
      &lt;/div&gt;

      &lt;div&gt;
        &lt;label&gt;이메일&lt;/label&gt;
        &lt;input
          {...register(&quot;email&quot;, {
            required: &quot;이메일은 필수입니다.&quot;,
            pattern: {
              value: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/,
              message: &quot;유효한 이메일 주소를 입력해주세요.&quot;,
            },
          })}
        /&gt;
        {errors.email &amp;&amp; &lt;p&gt;{errors.email.message}&lt;/p&gt;}
      &lt;/div&gt;

      &lt;button type=&quot;submit&quot;&gt;제출&lt;/button&gt;
    &lt;/form&gt;
  );
};

export default SignupForm;

//register로 입력값을 React Hook Form에 등록. 폼 제출 시, handleSubmit으로 데이터 처리. 유효성 검사 실패 시 errors에 메시지를 표시.</code></pre><br/>

<h2 id="-span-stylecolorbrown🚀-진짜-쉬운-설명span">** <span style="color:Brown">🚀 진짜 쉬운 설명**</span></h2>
<ul>
<li><strong>React Hooks와 React Hook Form의 차이점</strong></li>
</ul>
<table>
<thead>
<tr>
<th><strong>구분</strong></th>
<th><strong>React Hooks</strong></th>
<th><strong>React Hook Form</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>역할</strong></td>
<td>상태와 생명주기를 관리</td>
<td>폼 데이터를 관리하고 유효성을 검증</td>
</tr>
<tr>
<td><strong>주요 기능</strong></td>
<td><code>useState</code>, <code>useEffect</code> 등 다양한 Hook 제공</td>
<td><code>register</code>, <code>handleSubmit</code>으로 폼 데이터를 관리</td>
</tr>
<tr>
<td><strong>사용 대상</strong></td>
<td>전반적인 상태 관리 (예: 다크 모드, 카운터 등)</td>
<td>폼 데이터 입력 및 검증 (예: 회원가입, 로그인 폼 등)</td>
</tr>
<tr>
<td><strong>라이브러리 여부</strong></td>
<td>React에 내장된 기능</td>
<td>외부 라이브러리 (React Hook Form)</td>
</tr>
</tbody></table>
<ul>
<li><strong>React Hooks</strong>: React 컴포넌트의 상태와 생명주기를 관리하기 위한 내장 도구.</li>
<li><strong>React Hook Form</strong>: 폼 데이터 입력 및 검증을 간단하게 만들어주는 외부 라이브러리.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[A project ] study(3) Zustand]]></title>
            <link>https://velog.io/@k_kyu/A-project-study3-Zustand</link>
            <guid>https://velog.io/@k_kyu/A-project-study3-Zustand</guid>
            <pubDate>Fri, 10 Jan 2025 12:51:01 GMT</pubDate>
            <description><![CDATA[<h1 id="-span-stylecolorcornflowerbluezustand란-span">** <span style="color:CornflowerBlue">Zustand**란, </span></h1>
<p>React 애플리케이션에서 데이터를 효율적으로 관리하기 위한 상태 관리 라이브러리야.</p>
<h2 id="-span-stylecolorbrown📍핵심-설명span">** <span style="color:Brown">📍핵심 설명**</span></h2>
<ul>
<li><p>Zustand는 <strong>중앙 데이터 저장소</strong>
여러 컴포넌트가 같은 데이터를 필요로 할 때, 중앙에 데이터를 저장하고 필요할 때 꺼내 쓸 수 있도록 해줘. (예시)</p>
<ul>
<li>로그인 여부: 헤더, 사이드바, 프로필 페이지 등에서 공유.</li>
<li>다크 모드 상태: 모든 페이지에서 같은 테마를 사용.</li>
<li>장바구니: 상품 상세 페이지에서 추가한 데이터를 장바구니 페이지에서도 보여줌.</li>
</ul>
<br/>

</li>
</ul>
<h3 id="span-stylecolorcornflowerblue🔍-zustand-사용-예제span"><span style="color:CornflowerBlue">🔍 Zustand 사용 예제</span></h3>
<p><strong>1️⃣ Zustand 설치</strong></p>
<table>
<thead>
<tr>
<th><strong>패키지 매니저</strong></th>
<th><strong>설치 명령어</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>Yarn</strong></td>
<td><code>yarn add zustand</code></td>
</tr>
<tr>
<td><strong>pnpm</strong></td>
<td><code>pnpm add zustand</code></td>
</tr>
</tbody></table>
<p><strong>2️⃣ Zustand 저장소 생성</strong></p>
<pre><code class="language-js">import create from &#39;zustand&#39;;

// Zustand 저장소 생성
const useStore = create((set) =&gt; ({
  isLoggedIn: false, // 초기 로그인 상태
  login: () =&gt; set({ isLoggedIn: true }), // 로그인 상태 업데이트
  logout: () =&gt; set({ isLoggedIn: false }), // 로그아웃 상태 업데이트
}));</code></pre>
<p><strong>3️⃣ Zustand 상태 사용</strong></p>
<pre><code class="language-js">import React from &#39;react&#39;;
import { useStore } from &#39;./store&#39;;

const App = () =&gt; {
  const isLoggedIn = useStore((state) =&gt; state.isLoggedIn);
  const login = useStore((state) =&gt; state.login);
  const logout = useStore((state) =&gt; state.logout);

  return (
    &lt;div&gt;
      &lt;h1&gt;{isLoggedIn ? &quot;Logged In&quot; : &quot;Logged Out&quot;}&lt;/h1&gt;
      &lt;button onClick={isLoggedIn ? logout : login}&gt;
        {isLoggedIn ? &quot;Logout&quot; : &quot;Login&quot;}
      &lt;/button&gt;
    &lt;/div&gt;
  );
};

export default App;</code></pre>
<br/>

<h3 id="-span-stylecolorcornflowerblue🔍-자주-사용되는-패턴예시span">** <span style="color:CornflowerBlue">🔍 자주 사용되는 패턴(예시)**</span></h3>
<ul>
<li>다크 모드 토글 만들기</li>
</ul>
<p><strong>1️⃣ 상태 관리 설정</strong>
Zustand로 다크 모드 상태를 중앙에서 관리</p>
<pre><code class="language-js">import create from &#39;zustand&#39;;

// Zustand 저장소 생성
const useStore = create((set) =&gt; ({
  isDarkMode: false, // 다크 모드 상태
  toggleDarkMode: () =&gt; set((state) =&gt; ({ isDarkMode: !state.isDarkMode })), // 상태 변경 함수
}));</code></pre>
<p><strong>2️⃣ 상태를 사용하는 컴포넌트</strong>
React 컴포넌트에서 다크 모드 상태를 가져와 UI를 변경</p>
<pre><code class="language-js">import React from &#39;react&#39;;
import { useStore } from &#39;./store&#39;;

const App = () =&gt; {
  const isDarkMode = useStore((state) =&gt; state.isDarkMode); // 다크 모드 상태 가져오기
  const toggleDarkMode = useStore((state) =&gt; state.toggleDarkMode); // 상태 변경 함수 가져오기

  return (
    &lt;div style={{ backgroundColor: isDarkMode ? &#39;#333&#39; : &#39;#fff&#39;, color: isDarkMode ? &#39;#fff&#39; : &#39;#000&#39;, height: &#39;100vh&#39; }}&gt;
      &lt;h1&gt;{isDarkMode ? &#39;다크 모드&#39; : &#39;라이트 모드&#39;}&lt;/h1&gt;
      &lt;button onClick={toggleDarkMode}&gt;모드 전환&lt;/button&gt;
    &lt;/div&gt;
  );
};

export default App;</code></pre>
<h4 id="🙌-위의-코드를-더-이해해보기">🙌 위의 코드를 더 이해해보기.</h4>
<ul>
<li>Zustand로 중앙 저장소 생성<ul>
<li>isDarkMode는 현재 다크 모드인지 여부를 저장.</li>
<li>toggleDarkMode는 다크 모드를 켜고 끄는 함수.</li>
</ul>
</li>
<li>React 컴포넌트에서 상태를 가져옴<ul>
<li>useStore를 사용해 다크 모드 상태(isDarkMode)와 토글 함수(toggleDarkMode)를 가져옴.</li>
</ul>
</li>
<li>UI와 상태를 연결<ul>
<li>isDarkMode 값에 따라 배경색과 텍스트 색상을 변경.</li>
<li>버튼을 클릭하면 toggleDarkMode가 실행되어 다크 모드 상태가 바뀜.</li>
</ul>
</li>
</ul>
<br/>

<h2 id="-span-stylecolorbrown🚀-진짜-쉬운-설명span">** <span style="color:Brown">🚀 진짜 쉬운 설명**</span></h2>
<p>json-server는 JSON 파일 하나로 간단한 REST API 서버를 빠르게 만들고, 데이터를 읽고 쓰는 테스트를 할 수 있는 도구라고 생각하면 돼!</p>
]]></description>
        </item>
    </channel>
</rss>