<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>dev_log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Mon, 29 Dec 2025 06:31:14 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>dev_log</title>
            <url>https://velog.velcdn.com/images/jeun_ios/profile/2b13b2d5-f0c7-4f1c-9732-010aa498aa47/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. dev_log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/jeun_ios" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[HTTP, HTTPS, WebSocket 차이]]></title>
            <link>https://velog.io/@jeun_ios/HTTP-HTTPS-WebSocket-%EC%B0%A8%EC%9D%B4</link>
            <guid>https://velog.io/@jeun_ios/HTTP-HTTPS-WebSocket-%EC%B0%A8%EC%9D%B4</guid>
            <pubDate>Mon, 29 Dec 2025 06:31:14 GMT</pubDate>
            <description><![CDATA[<h2 id="http와-https의-관계">HTTP와 HTTPS의 관계</h2>
<h3 id="http">HTTP</h3>
<ul>
<li><code>Request</code> → <code>Response</code> → <code>연결 종료</code></li>
<li>Stateless (이전 클라에서 보냈던 요청을 서버에서 기억 X)</li>
<li>통신 내용 암호화 X</li>
</ul>
<h3 id="https">HTTPS</h3>
<ul>
<li>HTTP + TLS(SSL)</li>
<li>통신 내용 암호화 O</li>
<li>동작 방식은 HTTP와 동일</li>
</ul>
<br>

<hr>
<h2 id="websocket">WebSocket</h2>
<ul>
<li>한 번 연결하면 계속 유지</li>
<li>서버 ↔ 클라이언트 양방향 통신</li>
<li>실시간 데이터 전송에 적합</li>
</ul>
<h3 id="websocket-연결-과정">WebSocket 연결 과정</h3>
<pre><code>1. HTTP 요청 (Upgrade: websocket)
2. 서버 승인
3. 이후부터는 WebSocket 프로토콜</code></pre><p>✔ 처음에만 HTTP를 사용할 뿐 연결 이후에는 HTTP가 아님</p>
<hr>
<p>보안 버전 정리</p>
<pre><code>| 통신 목적     | 비보안  | 보안 |
| ---------- - | ------ | ------- |
| 요청-응답     | HTTP   | HTTPS   |
| 실시간 양방향 | WS     | WSS     |
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[Vue] emit과 slot의 차이, React와 비교]]></title>
            <link>https://velog.io/@jeun_ios/Vue-emit%EA%B3%BC-slot%EC%9D%98-%EC%B0%A8%EC%9D%B4-React%EC%99%80-%EB%B9%84%EA%B5%90</link>
            <guid>https://velog.io/@jeun_ios/Vue-emit%EA%B3%BC-slot%EC%9D%98-%EC%B0%A8%EC%9D%B4-React%EC%99%80-%EB%B9%84%EA%B5%90</guid>
            <pubDate>Tue, 11 Nov 2025 09:09:02 GMT</pubDate>
            <description><![CDATA[<h2 id="vue의-emit-vs-slot">Vue의 emit vs slot</h2>
<h3 id="emit--자식-→-부모로-이벤트-전달"><code>emit</code>  자식 → 부모로 이벤트 전달</h3>
<p>자식 컴포넌트가 부모에게 이벤트나 데이터를 전달할 때 사용</p>
<pre><code class="language-vue">&lt;!-- Parent.vue --&gt;
&lt;ChildComponent @clicked=&quot;handleClick&quot; /&gt;

&lt;script setup&gt;
import ChildComponent from &#39;./ChildComponent.vue&#39;

const handleClick = (msg) =&gt; {
  console.log(&#39;테스트:&#39;, msg)
}
&lt;/script&gt;

&lt;!-- ChildComponent.vue --&gt;
&lt;template&gt;
  &lt;button @click=&quot;sendMessage&quot;&gt;클릭&lt;/button&gt;
&lt;/template&gt;

&lt;script setup&gt;
const emit = defineEmits([&#39;clicked&#39;])

const sendMessage = () =&gt; {
  emit(&#39;clicked&#39;, &#39;클릭 이벤트 실행&#39;)
}
&lt;/script&gt;</code></pre>
<p>결과: 버튼을 클릭하면 부모 콘솔에 &quot;클릭 이벤트 실행&quot; 출력</p>
<h3 id="그럼-리액트에서는">그럼 리액트에서는?</h3>
<p>React에는 emit이 없고 부모가 콜백 함수를 props로 내려주고 자식이 해당 함수를 호출</p>
<pre><code class="language-tsx">// Parent.tsx
import Child from &#39;./Child&#39;

export default function Parent() {
  const handleClick = (msg) =&gt; {
    console.log(&#39;테스트:&#39;, msg)
  }

  return &lt;Child onClickButton={handleClick} /&gt;
}

// Child.tsx
export default function Child({ onClickButton }) {
  return &lt;button onClick={() =&gt; onClickButton(&#39;클릭 이벤트 실행&#39;)}&gt;클릭&lt;/button&gt;
}</code></pre>
<hr>
<h3 id="slot-부모-→-자식으로-콘텐츠-전달"><code>slot</code> 부모 → 자식으로 콘텐츠 전달</h3>
<p>slot은 부모가 자식 컴포넌트 내부에 콘텐츠를 꽂아 넣는 개념 (React의 children)</p>
<pre><code class="language-vue">&lt;!-- Parent.vue --&gt;
&lt;Layout&gt;
  &lt;p&gt;slot 테스트&lt;/p&gt;
&lt;/Layout&gt;

&lt;!-- Layout.vue --&gt;
&lt;template&gt;
  &lt;div class=&quot;layout&quot;&gt;
    &lt;header&gt;header&lt;/header&gt;
    &lt;slot /&gt; &lt;!-- 부모의 콘텐츠가 들어오는 자리 --&gt;
    &lt;footer&gt;footer&lt;/footer&gt;
  &lt;/div&gt;
&lt;/template&gt;</code></pre>
<p>결과:
header
slot 테스트
footer</p>
<pre><code class="language-tsx">// Parent.tsx
import Layout from &#39;./Layout&#39;

export default function Parent() {
  return (
    &lt;Layout&gt;
      &lt;p&gt;slot 테스트&lt;/p&gt;
    &lt;/Layout&gt;
  )
}

// Layout.tsx
export default function Layout({ children }) {
  return (
    &lt;div className=&quot;layout&quot;&gt;
      &lt;header&gt;header&lt;/header&gt;
      &lt;main&gt;{children}&lt;/main&gt;
      &lt;footer&gt;footer&lt;/footer&gt;
    &lt;/div&gt;
  )
}</code></pre>
<h3 id="정리표">정리표</h3>
<p>emit: 자식 → 부모에게 신호를 보냄
slot: 부모가 자식 내부를 채우는 공간 부모 → 자식에게 값을 채움</p>
<ul>
<li>React
  ◆ emit → props 콜백 함수 호출
  ◆ slot → children</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Network] WebSocket 통신과 Soket.io]]></title>
            <link>https://velog.io/@jeun_ios/c7ubb0zr</link>
            <guid>https://velog.io/@jeun_ios/c7ubb0zr</guid>
            <pubDate>Wed, 02 Jul 2025 17:44:01 GMT</pubDate>
            <description><![CDATA[<h3 id="websocket이란-">WebSocket이란 ?</h3>
<blockquote>
<p>WebSocket은 클라이언트와 서버 간에 지속적인 양방향 통신을 가능하게 하는 프로토콜
HTTP와 달리 연결이 한 번 수립되면 지속적으로 유지되어 실시간 데이터 교환이 가능</p>
</blockquote>
<h4 id="사용하는-이유">사용하는 이유</h4>
<ul>
<li>기존 HTTP는 요청-응답 방식으로만 통신 가능</li>
<li>서버에서 클라이언트로 먼저 데이터 전송 불가</li>
<li>매 요청마다 새로운 연결 생성으로 오버헤드 발생</li>
</ul>
<h3 id="soketio란-">Soket.io란 ?</h3>
<blockquote>
<p>Socket.IO는 WebSocket을 기반으로 한 JavaScript 라이브러리로, 실시간 양방향 통신을 더 쉽고 안정적으로 구현할 수 있도록 도와준다</p>
</blockquote>
<h4 id="주요-특징">주요 특징</h4>
<ul>
<li>자동 fallback 지원</li>
<li>WebSocket이 지원되지 않는 환경에서 자동으로 다른 방식으로 전환</li>
<li>Polling, Long-polling 등의 대안 기술 활용</li>
</ul>
<h4 id="추가-기능">추가 기능</h4>
<ul>
<li>Room 기능으로 특정 그룹 내 통신 가능</li>
<li>네임스페이스로 논리적 연결 분리</li>
<li>자동 재연결 기능</li>
<li>브로드캐스팅 지원</li>
</ul>
<hr>
<h3 id="차이점">차이점</h3>
<ol>
<li><p>기술적 성격</p>
<p>WebSocket: 브라우저 내장 표준 프로토콜
Socket.IO: 외부 라이브러리 (설치 필요)</p>
</li>
<li><p>브라우저 호환성 처리</p>
<pre><code class="language-js">// WebSocket - 직접 호환성 체크 필요
if (typeof WebSocket !== &#39;undefined&#39;) {
 const ws = new WebSocket(&#39;ws://localhost:3000&#39;);
} else {
 // 대안 방법 직접 구현해야 함
}
</code></pre>
</li>
</ol>
<p>// Socket.IO - 자동 처리
const socket = io(&#39;<a href="http://localhost:3000&#39;">http://localhost:3000&#39;</a>); // 알아서 최적 방법 선택</p>
<pre><code>
3. 연결 실패 시 대응

  WebSocket: 연결 끊어지면 수동으로 재연결 코드 작성
  Socket.IO: 자동 재연결 기능 내장

4. 구현 복잡성
```js
// WebSocket - 모든 것을 직접 구현
const ws = new WebSocket(&#39;ws://localhost:3000&#39;);
ws.onopen = function() { /* 연결 처리 */ };
ws.onmessage = function(event) { /* 메시지 처리 */ };
ws.onerror = function() { /* 에러 처리 */ };
ws.onclose = function() { /* 재연결 로직 직접 구현 */ };

// Socket.IO - 간단한 API
const socket = io(&#39;http://localhost:3000&#39;);
socket.on(&#39;connect&#39;, () =&gt; { /* 연결 처리 */ });
socket.on(&#39;message&#39;, (data) =&gt; { /* 메시지 처리 */ });
// 에러, 재연결 등은 자동 처리</code></pre><ol start="5">
<li><p>성능과 용량</p>
<p>WebSocket: 가볍고 빠름
Socket.IO: 기능이 많아 상대적으로 무거움</p>
</li>
</ol>
<hr>
<h4 id="사용-사례">사용 사례</h4>
<ul>
<li>간단한 실시간 통신: WebSocket</li>
<li>복잡한 실시간 애플리케이션: Socket.IO</li>
<li>최대 성능 필요: WebSocket</li>
<li>개발 편의성 우선: Socket.IO</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Tailwind 3, 4 비교]]></title>
            <link>https://velog.io/@jeun_ios/Tailwind-3-4-%EB%B9%84%EA%B5%90</link>
            <guid>https://velog.io/@jeun_ios/Tailwind-3-4-%EB%B9%84%EA%B5%90</guid>
            <pubDate>Mon, 23 Jun 2025 18:52:45 GMT</pubDate>
            <description><![CDATA[<p><code>@theme</code>이란?</p>
<blockquote>
<p><code>@theme</code>은 <strong>Tailwind CSS 4에서 새로 도입된 CSS 지시어(directive)</strong></p>
</blockquote>
<h4 id="tailwind-3-vs-tailwind-4-비교">Tailwind 3 vs Tailwind 4 비교</h4>
<p>Tailwind 3 (이전 방식):</p>
<pre><code class="language-js">// tailwind.config.js
module.exports = {
  theme: {
    extend: {
      colors: {
        brand: &#39;#3b82f6&#39;
      }
    }
  }
}</code></pre>
<p>Tailwind 4 (새로운 방식):</p>
<pre><code class="language-js">css/* CSS 파일에서 직접 */
@theme inline {
  --color-brand: #3b82f6;
}</code></pre>
<p>왜 <code>@theme</code> 안에서 써야 하나?</p>
<h3 id="1-tailwind의-테마-시스템-통합">1. Tailwind의 테마 시스템 통합</h3>
<pre><code class="language-css">css@theme inline {
  --color-brand: #3b82f6;  /* 이것이 text-brand 클래스가 됨 */
}</code></pre>
<p><code>--color-brand</code>는 자동으로 <code>text-brand</code>, <code>bg-brand</code>, <code>border-brand</code> 등의 클래스를 생성</p>
<h3 id="2-일반-css-변수와의-차이점">2. 일반 CSS 변수와의 차이점</h3>
<pre><code class="language-css">/* 이렇게 하면 Tailwind 클래스가 생성되지 않음 */
:root {
  --my-color: #3b82f6;  /* text-my-color 클래스 생성 안됨 */
}

/* 이렇게 해야 Tailwind 클래스가 생성됨 */
@theme inline {
  --color-my-color: #3b82f6;  /* text-my-color 클래스 생성됨 */
}</code></pre>
<h3 id="3-tailwind의-컴파일-과정">3. Tailwind의 컴파일 과정</h3>
<ol>
<li><code>@theme</code> 블록을 스캔</li>
<li>CSS 변수를 Tailwind 유틸리티 클래스로 변환</li>
<li><code>text-brand</code>, <code>bg-brand</code> 등 생성</li>
</ol>
<p>정리</p>
<ul>
<li><code>@theme</code>은 Tailwind 4의 새로운 테마 정의 방법</li>
<li>CSS 변수를 Tailwind 클래스로 자동 변환해주는 특별한 지시어</li>
<li><code>@theme</code> 밖에서 정의한 CSS 변수는 Tailwind 클래스가 되지 않음</li>
</ul>
<p>그래서 커스텀 클래스를 사용하려면 반드시 <code>@theme</code> 안에서 정의해야 한다..</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next] 다국어 처리]]></title>
            <link>https://velog.io/@jeun_ios/Next-%EB%8B%A4%EA%B5%AD%EC%96%B4-%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@jeun_ios/Next-%EB%8B%A4%EA%B5%AD%EC%96%B4-%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Mon, 23 Jun 2025 15:13:35 GMT</pubDate>
            <description><![CDATA[<h4 id="다국어-처리-시-사용할-수-있는-라이브러리--솔루션-정리">다국어 처리 시 사용할 수 있는 라이브러리 &amp; 솔루션 정리</h4>
<table>
<thead>
<tr>
<th>항목</th>
<th><code>next-intl</code></th>
<th><code>i18next</code></th>
<th><code>Lokalise</code></th>
<th><code>Crowdin</code></th>
</tr>
</thead>
<tbody><tr>
<td><strong>역할</strong></td>
<td>Next.js 전용 다국어 렌더링</td>
<td>범용 i18n 라이브러리 (React, Vue 등 호환)</td>
<td>번역 협업/관리 플랫폼</td>
<td>번역 협업/관리 플랫폼</td>
</tr>
<tr>
<td><strong>형태</strong></td>
<td><strong>라이브러리</strong></td>
<td><strong>라이브러리</strong></td>
<td><strong>SaaS 플랫폼</strong></td>
<td><strong>SaaS 플랫폼</strong></td>
</tr>
<tr>
<td><strong>Next.js 적합성</strong></td>
<td>✅ 최적화됨 (app router 완전 대응)</td>
<td>❌ 직접 설정 필요 / 예전 방식 위주</td>
<td>◯ JSON 변환으로 연동</td>
<td>◯ JSON 변환으로 연동</td>
</tr>
<tr>
<td><strong>타입 지원</strong></td>
<td>✅ TypeScript 완전 대응 (제네릭 기반)</td>
<td>◯ 약한 편</td>
<td>-</td>
<td>-</td>
</tr>
<tr>
<td><strong>ICU 메시지 포맷 지원</strong></td>
<td>✅</td>
<td>◯ (추가 패키지 필요)</td>
<td>◯</td>
<td>◯</td>
</tr>
<tr>
<td><strong>번역 관리 UI</strong></td>
<td>❌ 없음</td>
<td>❌ 없음</td>
<td>✅ 웹 UI + 워크플로</td>
<td>✅ 웹 UI + 번역가 마켓 포함</td>
</tr>
<tr>
<td><strong>협업 번역</strong></td>
<td>❌ 수동 처리</td>
<td>❌ 수동 처리</td>
<td>✅ 번역가, 리뷰어 초대 가능</td>
<td>✅ AI 보조 번역, 맥락 제공</td>
</tr>
<tr>
<td><strong>자동화 (CI/CD)</strong></td>
<td>◯ JSON 수동 배포</td>
<td>◯ JSON 수동 배포</td>
<td>✅ GitHub, CLI, Figma 연동</td>
<td>✅ GitHub, CLI, Notion 연동</td>
</tr>
<tr>
<td><strong>무료 사용 가능 여부</strong></td>
<td>✅</td>
<td>✅</td>
<td>◯ 500 keys까지 무료</td>
<td>◯ OSS나 소규모에 무료 티어</td>
</tr>
</tbody></table>
<br>

<h3 id="추천-사용-시점-요약">추천 사용 시점 요약</h3>
<table>
<thead>
<tr>
<th>상황</th>
<th>추천 조합</th>
</tr>
</thead>
<tbody><tr>
<td><strong>개인/스타트업 + 빠른 개발</strong></td>
<td><code>next-intl</code> + 수동 JSON 관리</td>
</tr>
<tr>
<td><strong>기존에 i18next를 써본 적 있다면</strong></td>
<td><code>i18next</code> + <code>next-i18next</code> 플러그인 활용</td>
</tr>
<tr>
<td><strong>번역가와 협업, 자동화 필요</strong></td>
<td><code>next-intl</code> + <code>Lokalise</code> or <code>Crowdin</code></td>
</tr>
<tr>
<td><strong>오픈소스 프로젝트 운영</strong></td>
<td><code>next-intl</code> + <code>Crowdin</code> (무료 티어 제공)</td>
</tr>
<tr>
<td><strong>번역 프로세스까지 전부 관리하고 싶을 때</strong></td>
<td><code>i18next</code> + <code>Lokalise</code> or <code>Crowdin</code></td>
</tr>
</tbody></table>
<br>

<h3 id="예시-구조도">예시 구조도</h3>
<pre><code>📁 app/
   └─ [locale]/
       └─ page.tsx         ← Next.js 다국어 라우팅
📁 messages/
   └─ en.json              ← 번역 사전 (next-intl 또는 i18next 용)
   └─ ko.json
📁 tools/
   └─ i18n.ts              ← next-intl 또는 i18next 초기화</code></pre><br>

<h3 id="간단-요약">간단 요약</h3>
<p>next-intl vs i18next</p>
<blockquote>
<p>next-intl은 Next.js 앱 전용
i18next는 범용이지만 설정 복잡</p>
</blockquote>
<p>Lokalise vs Crowdin
비슷하지만, Lokalise는 워크플로 자동화 강점 / Crowdin은 AI 번역·시장 연결 강점</p>
<p>함께 쓰는 구조는
next-intl or i18next로 앱 구성 + Lokalise/Crowdin으로 번역 관리 자동화</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] useMemo, React.memo, useCallback, useRef 차이점]]></title>
            <link>https://velog.io/@jeun_ios/useMemo-React.memo-useCallback-useRef-%EC%B0%A8%EC%9D%B4%EC%A0%90</link>
            <guid>https://velog.io/@jeun_ios/useMemo-React.memo-useCallback-useRef-%EC%B0%A8%EC%9D%B4%EC%A0%90</guid>
            <pubDate>Sat, 21 Jun 2025 16:22:47 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><code>useMemo</code>: 값 계산 최적화
<code>React.memo</code>: 컴포넌트 렌더링 최적화
<code>useCallback</code>: 함수 재생성 방지
<code>useRef</code>: DOM 접근, 값 저장
→ 성능 최적화 &amp; 렌더링 제어에 쓰이는 핵심 훅과 컴포넌트</p>
</blockquote>
<h3 id="usememo">useMemo</h3>
<ul>
<li>복잡한 계산을 렌더링마다 반복하지 않도록 캐싱
→ 의존값이 바뀌지 않으면 이전 값 재사용</li>
</ul>
<pre><code class="language-tsx">&quot;use client&quot;;

import { useState, useMemo } from &quot;react&quot;;

type User = {
  id: number;
  name: string;
  email: string;
};

const dummyUsers: User[] = Array.from({ length: 10000 }, (_, i) =&gt; ({
  id: i,
  name: `User ${i}`,
  email: `user${i}@example.com`,
}));

export default function UserTable() {
  const [search, setSearch] = useState(&quot;&quot;);

  const filteredUsers = useMemo(() =&gt; {
    console.log(&quot;🔍 사용자 목록 필터링 실행&quot;);
    return dummyUsers.filter((user) =&gt;
      user.name.toLowerCase().includes(search.toLowerCase())
    );
  }, [search]);

  return (
    &lt;div className=&quot;p-6 space-y-4&quot;&gt;
      &lt;h2 className=&quot;text-xl font-bold&quot;&gt;📋 사용자 목록&lt;/h2&gt;
      &lt;input
        type=&quot;text&quot;
        placeholder=&quot;이름으로 검색...&quot;
        value={search}
        onChange={(e) =&gt; setSearch(e.target.value)}
        className=&quot;border px-3 py-1 w-full&quot;
      /&gt;

      &lt;ul className=&quot;h-[300px] overflow-y-scroll border rounded p-2 space-y-1&quot;&gt;
        {filteredUsers.slice(0, 50).map((user) =&gt; (
          &lt;li key={user.id} className=&quot;text-sm text-gray-700 dark:text-gray-300&quot;&gt;
            {user.name} ({user.email})
          &lt;/li&gt;
        ))}
      &lt;/ul&gt;
    &lt;/div&gt;
  );
}</code></pre>
<blockquote>
<p>코드 설명: </p>
</blockquote>
<ol>
<li>10,000명의 유저 데이터를 메모리에 저장</li>
<li>사용자가 입력한 search 키워드에 따라 <code>useMemo</code>를 통해 결과를 캐싱해서 재사용</li>
<li>필터된 유저 목록은 최대 50명만 보여줌 <code>filteredUsers.slice(0, 50)</code></li>
<li>search 값이 바뀔때만 <code>filteredUsers</code>이 실행됨, 외에는 캐싱된 <code>filteredUsers</code> 사용</li>
</ol>
<p>🤔 <strong>근데 검색할때마다 필터 함수 돌릴거면 캐싱해서 재사용하는 의미가 없지 않나?</strong>
→ react는 state 값이 변경될 때 마다 컴포넌트 함수 전체를 다시 실행하기 때문에 <code>useMemo</code>가 없으면 필터링 연산도 매번 다시 실행된다 그렇기 때문에 큰 계산이 있거나 결과가 자주 변하지 않을때는 <code>useMemo</code>를 사용하는 것이 좋다</p>
<hr>
<h3 id="reactmemo">React.memo</h3>
<ul>
<li>props가 바뀌지 않으면 재렌더링을 막는 고차 컴포넌트 (HOC)</li>
<li>HOC: 컴포넌트를 인자로 받아 새로운 컴포넌트로 반환하는 함수 </li>
</ul>
<pre><code class="language-tsx">&quot;use client&quot;;

import { useState } from &quot;react&quot;;
import React from &quot;react&quot;;

export default function MemoExample() {
  const [count, setCount] = useState(0);
  const [input, setInput] = useState(&quot;&quot;);

  return (
    &lt;div className=&quot;p-6 space-y-4&quot;&gt;
      &lt;h2 className=&quot;text-xl font-bold&quot;&gt;React.memo 사용 비교&lt;/h2&gt;

      &lt;button
        onClick={() =&gt; setCount((prev) =&gt; prev + 1)}
        className=&quot;px-4 py-2 bg-blue-500 text-white rounded&quot;
      &gt;
        숫자 증가 (count: {count})
      &lt;/button&gt;

      &lt;input
        type=&quot;text&quot;
        value={input}
        onChange={(e) =&gt; setInput(e.target.value)}
        placeholder=&quot;입력해보세요&quot;
        className=&quot;border px-2 py-1 rounded&quot;
      /&gt;

      &lt;div className=&quot;flex gap-10 mt-6&quot;&gt;
        &lt;NormalChild /&gt;
        &lt;MemoizedChild /&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}

// 일반 컴포넌트
function NormalChild() {
  console.log(&quot;❌ NormalChild 렌더링&quot;);
  return &lt;div className=&quot;p-4 bg-red-100 rounded&quot;&gt;일반 자식 컴포넌트&lt;/div&gt;;
}

// memo로 감싼 컴포넌트
const MemoizedChild = React.memo(function MemoChild() {
  console.log(&quot;✅ MemoizedChild 렌더링&quot;);
  return &lt;div className=&quot;p-4 bg-green-100 rounded&quot;&gt;React.memo 자식 컴포넌트&lt;/div&gt;;
});</code></pre>
<p>→ 부모가 리렌더링돼도 props가 같으면 해당 컴포넌트는 리렌더링되지 않음
<br></p>
<ul>
<li><p>실행해보면 초기 렌더링 console
<img src="https://velog.velcdn.com/images/jeun_ios/post/163182bd-8ca1-41a4-a251-2fa9c60e019f/image.png" alt=""></p>
</li>
<li><p>숫자 증가 버튼 한번 클릭 시
<img src="https://velog.velcdn.com/images/jeun_ios/post/7606017c-3de7-4538-b42a-be198c77e757/image.png" alt=""></p>
</li>
<li><p>input에 텍스트 입력 시
<img src="https://velog.velcdn.com/images/jeun_ios/post/6214faa7-05df-4aa5-8237-814f18bfa156/image.png" alt=""></p>
</li>
</ul>
<p>💡 초기 렌더링 이후 자식 컴포넌트 리렌더링이 되지 않는 것을 볼수 있다!</p>
<hr>
<h3 id="usecallback">useCallback</h3>
<ul>
<li>함수를 의존성 배열 기준으로 메모이제이션<pre><code class="language-tsx">&quot;use client&quot;;
</code></pre>
</li>
</ul>
<p>import React, { useCallback, useState } from &quot;react&quot;;</p>
<p>const ChildButton = ({ label, onClick }: { label: string; onClick: () =&gt; void }) =&gt; {
  console.log(<code>🔄 ${label} 렌더링됨</code>);
  return (
    &lt;button
      onClick={onClick}
      className=&quot;px-4 py-2 rounded text-white font-medium&quot;
      style={{
        backgroundColor: label.includes(&quot;useCallback&quot;) ? &quot;#10b981&quot; : &quot;#3b82f6&quot;,
      }}
    &gt;
      {label}
    </button>
  );
};</p>
<p>const MemoizedButton = React.memo(ChildButton);</p>
<p>export default function CallbackComparison() {
  const [count, setCount] = useState(0);</p>
<p>  // ❌ useCallback 없이: 매 렌더마다 함수 새로 생성됨
  const wthoutCallback = () =&gt; {
    console.log(&quot;클릭 (without useCallback)&quot;);
  };</p>
<p>  // ✅ useCallback 사용: 함수 메모이제이션됨
  const withCallback = useCallback(() =&gt; {
    console.log(&quot;클릭 (with useCallback)&quot;);
  }, []);</p>
<p>  return (
    <div className="p-6 space-y-6">
      <h2 className="text-xl font-bold">🔁 useCallback 사용 / 미사용 비교</h2></p>
<pre><code>  &lt;p className=&quot;text-gray-700&quot;&gt;count: {count}&lt;/p&gt;
  &lt;button
    onClick={() =&gt; setCount((prev) =&gt; prev + 1)}
    className=&quot;px-3 py-1 bg-gray-800 text-white rounded&quot;
  &gt;
    count 증가 (부모 컴포넌트 리렌더)
  &lt;/button&gt;

  &lt;div className=&quot;flex gap-4 mt-4&quot;&gt;
    &lt;MemoizedButton label=&quot;❌ useCallback 미사용&quot; onClick={wthoutCallback} /&gt;
    &lt;MemoizedButton label=&quot;✅ useCallback 사용&quot; onClick={withCallback} /&gt;
  &lt;/div&gt;
&lt;/div&gt;</code></pre><p>  );
}</p>
<pre><code>
→ 매번 새로운 함수 객체를 생성하지 않도록 방지 
(특히 React.memo 자식에게 props로 넘길 때 유용)

- 실행해보면 초기 렌더링 console
![](https://velog.velcdn.com/images/jeun_ios/post/4ac79032-d2c5-4f19-82fd-b6d6b3527176/image.png)

- `count 증가 (부모 컴포넌트 리렌더)` 버튼 한번 클릭 시
![](https://velog.velcdn.com/images/jeun_ios/post/ea886922-159b-45a0-8a98-2cac32b9dac1/image.png)

💡 useCallback 함수 사용 시 메모이제이션 된 값을 사용하는걸 볼 수 있다!

---

### useRef
- 렌더링과 무관한 값을 저장하거나 DOM 요소를 참조할 때 사용

```tsx
&quot;use client&quot;;

import { useRef, useState } from &quot;react&quot;;

export default function RefExample() {
  const countRef = useRef(0); // 리렌더링 없이 값 저장
  const [renderCount, setRenderCount] = useState(0); // 렌더링 확인용

  const handleClick = () =&gt; {
    countRef.current += 1; // 화면에는 안 보임
  };

  const triggerRender = () =&gt; {
    setRenderCount((prev) =&gt; prev + 1); // 강제 리렌더링
  };

  return (
    &lt;div className=&quot;space-y-4 p-6&quot;&gt;
      &lt;h2 className=&quot;text-lg font-semibold&quot;&gt;🔄 useRef 리렌더링 테스트&lt;/h2&gt;

      &lt;button
        onClick={handleClick}
        className=&quot;px-4 py-2 bg-blue-500 text-white rounded&quot;
      &gt;
        useRef 값 +1 (렌더링 없음)
      &lt;/button&gt;

      &lt;button
        onClick={triggerRender}
        className=&quot;px-4 py-2 bg-gray-700 text-white rounded&quot;
      &gt;
        강제로 리렌더링
      &lt;/button&gt;

      &lt;div className=&quot;mt-4 text-gray-800&quot;&gt;
        &lt;p&gt;useRef 값: {countRef.current}&lt;/p&gt;
        &lt;p&gt;렌더링 횟수: {renderCount}&lt;/p&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}</code></pre><ul>
<li>초기 렌더링 결과</li>
</ul>
<p><img src="https://velog.velcdn.com/images/jeun_ios/post/b1eea140-0694-4fa7-9e0d-ffb1b93daf85/image.png" alt=""></p>
<ul>
<li><code>useRef값 +1</code> 버튼을 3번 클릭했을 때</li>
</ul>
<p><img src="https://velog.velcdn.com/images/jeun_ios/post/69a5d1d2-c465-4206-908e-78e8ca2acdac/image.png" alt=""></p>
<ul>
<li><code>강제로 리렌더링</code> 버튼을 눌렀을 때</li>
</ul>
<p><img src="https://velog.velcdn.com/images/jeun_ios/post/bcca3695-096b-4a35-b759-4dc54c74a0d7/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next] SSR vs SSG(+ ISR)]]></title>
            <link>https://velog.io/@jeun_ios/Next-SSR-vs-SSG-ISR</link>
            <guid>https://velog.io/@jeun_ios/Next-SSR-vs-SSG-ISR</guid>
            <pubDate>Thu, 19 Jun 2025 17:17:33 GMT</pubDate>
            <description><![CDATA[<p>오늘 적어볼것은 <strong>SSR과 SSG를 어떤 상황에 사용하는 것이 좋을까?</strong> 이다</p>
<h2 id="ssr">SSR</h2>
<table>
<thead>
<tr>
<th>사용 추천</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>데이터가 자주 바뀌고 최신 정보가 중요</strong></td>
<td>예: 실시간 재고, 할인율, 주문 상태 등<br>→ 요청할 때마다 최신 데이터를 포함해야 함</td>
</tr>
<tr>
<td><strong>유저 맞춤형 정보가 포함될 때</strong></td>
<td>예: 로그인 사용자별 상품 추천, 장바구니 상태 등</td>
</tr>
<tr>
<td><strong>검색엔진도 최신 정보를 봐야 하는 경우</strong></td>
<td>뉴스, 공지사항 등 인덱싱이 실시간성에 따라 달라질 때</td>
</tr>
</tbody></table>
<br>

<h2 id="ssg--isr">SSG + ISR</h2>
<table>
<thead>
<tr>
<th>사용 추천</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>데이터가 자주 바뀌지 않거나, 초 단위까지 실시간일 필요는 없을 때</strong></td>
<td>예: 하루 몇 번 갱신되는 상품 리스트, 블로그, 마케팅 페이지</td>
</tr>
<tr>
<td><strong>검색엔진에 중요한 정보가 미리 포함되어 있으면 되는 경우</strong></td>
<td>즉시 인덱싱보다 콘텐츠 노출이 우선일 때</td>
</tr>
<tr>
<td><strong>빠른 로딩 속도가 중요한 랜딩/메인 페이지</strong></td>
<td>SSG는 정적 HTML이기 때문에 TTFB/LCP가 매우 빠름</td>
</tr>
<tr>
<td><strong>페이지 수가 많지만 모두 자주 변경되진 않을 때</strong></td>
<td>ISR을 통해 인기 페이지만 갱신하고 나머진 캐시 유지</td>
</tr>
</tbody></table>
<br>

<h3 id="실무에서-사용한다면-">실무에서 사용한다면 ?</h3>
<p>쇼핑몰 웹사이트를 만든다고 가정했을 때,</p>
<p><strong>SSR</strong></p>
<pre><code>1. 로그인 후 메인/마이페이지: 유저별 맞춤 콘텐츠 필요하므로 서버에서 매 요청 처리
2. 메인 페이지 인기상품: 실시간 인기 상품 노출 필요
3. 검색 결과 페이지: 검색 결과도 SEO에 노출되게 하고싶으면 SSR 처리, 필요없으면 CSR 처리</code></pre><p><strong>SSG</strong></p>
<pre><code>1. 메인 페이지 상품 리스트: 쇼핑몰의 전체 상품 리스트는 실시간성으로 바뀌기보다 하루에 n번 갱신하는것이 낫다 생각
2. 제품 상세 페이지: 많이 보는 상품은 SSG + ISR로 캐싱, 실시간 재고 필요하면 SSR
3. 이벤트 및 프로모션: 고정 콘텐츠, 실시간성 필요 없지만 SEO 적합</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[프로세스와 스레드 정리]]></title>
            <link>https://velog.io/@jeun_ios/%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EC%99%80-%EC%8A%A4%EB%A0%88%EB%93%9C-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@jeun_ios/%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EC%99%80-%EC%8A%A4%EB%A0%88%EB%93%9C-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Wed, 18 Jun 2025 23:59:07 GMT</pubDate>
            <description><![CDATA[<h3 id="프로세스">프로세스</h3>
<ul>
<li>운영체제로부터 자원을 할당받는 단위</li>
<li><code>code</code>, <code>data</code>, <code>heap</code>, <code>stack</code> 형식으로 할당</li>
</ul>
<h3 id="스레드">스레드</h3>
<ul>
<li>프로세스가 할당받은 자원을 이용하는 실행의 단위</li>
<li>메모리 영역 내에서 <code>stack</code>은 각자 값을 가지고 있음</li>
<li>나머지 <code>code</code>, <code>data</code>, <code>heap</code>, 형식으로 할당된 영역 공유</li>
</ul>
<br>

<p><strong>프로세스를 실행하다가 오류가 나서 강제종료되면 ?</strong>
→ 공유하고 있는 파일을 손상시키는 것이 아니면 영향 X</p>
<p><strong>스레드 하나에서 오류가 발생하면 ?</strong>
→ 오류 난 스레드가 속한 프로세스가 종료 될 수 있다</p>
<hr>
<h3 id="싱글-스레드">싱글 스레드</h3>
<ul>
<li>하나의 프로세스에서 하나의 스레드가 실행</li>
<li>하나의 스택과 레지스터로 표현</li>
</ul>
<blockquote>
<p>장점) 자원 접근에 대한 동기화 신경을 쓰지 않아도 됨
단점) 여러개의 CPU를 활용하지 못함</p>
</blockquote>
<h3 id="멀티-스레드">멀티 스레드</h3>
<ul>
<li>각 프로세스 내에서 여러개의 스레드가 동시 작업 수행</li>
<li>자원을 공유하여 생성/관리 중복 최소화</li>
</ul>
<blockquote>
<p>장점) 자원을 공유하므로 가볍고 통신이 빠르다
단점) 동기화 이슈나 하나의 스레드 오류로 프로세스에 영향이 갈 수 있다</p>
</blockquote>
<h3 id="멀티-프로세스">멀티 프로세스</h3>
<ul>
<li>하나의 프로그램이 여러개의 독립된 프로세스로 나누어 실행됨</li>
</ul>
<blockquote>
<p>장점) 각 프로세스가 메모리를 독립적으로 사용해 안전성이 높다
단점) 자원 소모가 크고, 프로세스 간 통신이 복잡하다</p>
</blockquote>
<br>

<hr>
<p><strong>🤷‍♀️ 그러면 멀티 프로세스와 멀티 스레드는 어떻게 구분하여 언제 사용하는 것이 좋을까?</strong></p>
<p><code>멀티 프로세스</code> : 작업간의 안전성이 중요할 때 사용 
<code>멀티 스레드</code> : 가볍고 빠른 통신이 중요할 때 사용</p>
<p>ex) 크롬 브라우저는 각 탭을 멀티 프로세스로 분리해서 한 탭이 멈춰도 다른 탭에는 영향이 없도록 설계되어 있다. 반면 서버나 게임에서는 멀티 스레드를 활용해 가볍고 빠름을 추구한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] TanStack Query 정리 (1)]]></title>
            <link>https://velog.io/@jeun_ios/React-TanStack-Query-%EC%A0%95%EB%A6%AC-1</link>
            <guid>https://velog.io/@jeun_ios/React-TanStack-Query-%EC%A0%95%EB%A6%AC-1</guid>
            <pubDate>Tue, 17 Jun 2025 20:55:46 GMT</pubDate>
            <description><![CDATA[<h3 id="tanstack-query란">TanStack Query란?</h3>
<blockquote>
<p><code>서버로부터 데이터 가져오기</code>, <code>데이터 캐싱</code>, <code>캐시 제어</code> 등 데이터를 쉽고 효율적으로 관리할 수 있는 라이브러리</p>
</blockquote>
<br>

<h3 id="기능-정리">기능 정리</h3>
<ul>
<li>데이터 가져오기 및 캐싱</li>
<li>동일 요청의 중복 제거 (단일 요청으로 통합)</li>
<li>신선한 데이터 유지 및 빠른 데이터 업데이트</li>
<li>페이지네이션 및 데이터 지연로드 등의 성능 최적화</li>
<li>네트워크 재연결, 요청 실패 등의 자동 갱신</li>
</ul>
<br>

<h3 id="기본-설정">기본 설정</h3>
<pre><code class="language-tsx">import { QueryClient, QueryClientProvider } from &quot;@tanstack/react-query&quot;;

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 1000 * 10
    },
  },
});

function App() {
  return (
   &lt;QueryClientProvider client={queryClient}&gt;
      &lt;div&gt;기본 설정 세팅&lt;/div&gt;
   &lt;/QueryClientProvider&gt;;
  );
}</code></pre>
<pre><code class="language-jsx">1. QueryClient를 사용하여 캐시와 상호 작용 할 수 있다.
2. QueryClientProvider를 최상단에서 감싸주고 QueryClient 인스턴스를 client props로 넣어 연결해야 함.
3. 해당 context는 비동기 요청을 알아서 처리하는 background 계층이 된다.</code></pre>
<br>

<h3 id="데이터-캐싱">데이터 캐싱</h3>
<pre><code class="language-tsx">const result = useQuery({
  queryKey: [&quot;A&quot;],
  queryFn,
  staleTime: 0, // (기본값 0초)
  gcTime: 1000 * 60 * 5, // (기본값 5분)
});</code></pre>
<pre><code class="language-jsx">1. A라는 queryKey를 가진 쿼리 인스턴스가 mount
2. 네트워크 요청을 통해 데이터를 fetch(queryFn)하고, 데이터 A라는 queryKey로 캐싱
3. 기본값인 staleTime이 0이므로, 데이터를 가져오자마자 바로 stale(상함) 상태로 전환
4. 쿼리 인스턴스가 unmount 되면, Tanstack Query는 해당 캐시를 gcTime 동안 유지
5. 설정해놓은 5분 이내에 A 쿼리가 다시 mount 되면, 캐시된 데이터를 즉시 반환 
   (동시에 queryFn은 background에서 실행)
6. 5분 이내에 A 쿼리가 다시 mount 되지 않으면, 해당 캐시는 가비지 컬렉션에 의해 삭제

// gcTime: 해당 시간이 지나면 &quot;데이터 캐시에서 제거&quot; (메모리에서도 사라짐)
// staleTime: &quot;데이터가 상하는데까지 걸리는 시간&quot; 설정 옵션 (위 코드는 0으로 설정했기 때문에 바로상함)</code></pre>
<br>

<h3 id="usequery-기본-문법">useQuery 기본 문법</h3>
<ul>
<li>useQuery는 <code>v5</code>부터 인자로 단 하나의 객체만 받는다. 그 중 첫번째 인자 <code>queryKey</code>, <code>queryFn</code> 가 필수 값이다.</li>
</ul>
<pre><code class="language-tsx">const result = useQuery({
  queryKey, // 쿼리 식별자 (필수)
  queryFn, // 데이터 fetch하는 비동기 함수 (필수)
  // 옵션 예시) gcTime, staleTime, select 등
});

result.data; // queryFn 결과값 or 캐시된 데이터
result.isStale; // 값이 상했는지 여부 (staleTime 지나면 true)
result.refetch; // 재요청 함수 (queryFn을 다시 실행시키는 수동 트리거)
result.isLoading; // 로딩 상태 (queryFn 진행 상태 확인)</code></pre>
<br>

<h3 id="usequery---queryfn">useQuery - queryFn</h3>
<ul>
<li>쿼리 함수는 데이터를 가져오는 비동기 함수로, 꼭 데이터를 반환하거나 오류를 던져야 함</li>
<li>던져진 오류는 반환되는 <code>error</code> 객체로 확인 가능</li>
<li><code>error</code>는 기본적으로 <code>null</code></li>
</ul>
<pre><code class="language-tsx">import { useQuery } from &#39;@tanstack/react-query&#39;

type ResponseValue = {
  message: string
  time: string
}

export default function DelayedData() {
  const { data, error } = useQuery&lt;ResponseValue&gt;({
    queryKey: [&#39;delay&#39;],
    queryFn: async () =&gt; {
      const res = await fetch(&#39;https://api.heropy.dev/v0/delay?t=1000&#39;)
      const data = await res.json()
      if (!data.time) {
        throw new Error(&#39;문제가 발생했습니다!&#39;)
      }
      return data
    },
    staleTime: 1000 * 10,
    retry: 1
  })
  return (
    &lt;&gt;
      {data &amp;&amp; &lt;div&gt;{JSON.stringify(data)}&lt;/div&gt;}
      {error &amp;&amp; &lt;div&gt;{error.message}&lt;/div&gt;}
    &lt;/&gt;
  )
}</code></pre>
<br>

<h3 id="usequery-주요-리턴-데이터">useQuery 주요 리턴 데이터</h3>
<pre><code class="language-tsx">const {
  data,
  error,
  status,
  isPending,
  fetchStatus,
  isLoading,
  isFetching,
  isError,
  refetch,
  // ...
} = useQuery({
  queryKey: [&quot;all-items&quot;],
  queryFn: getAllItems,
});</code></pre>
<blockquote>
<ul>
<li>data: 쿼리 함수가 리턴한 Promise에서 resolved된 데이터</li>
</ul>
</blockquote>
<ul>
<li>error: 쿼리 함수에 오류가 발생한 경우, 쿼리에 대한 오류 객체</li>
<li>status: data, 쿼리 결과값에 대한 상태를 표현하는 status는 문자열 형태로 3가지의 값이 존재한다.<ul>
<li>pending: 쿼리 데이터가 없고, 쿼리 시도가 아직 완료되지 않은 상태
ㅤ- { enabled: false } 상태로 쿼리가 호출되면 이 상태로 시작된다</li>
<li>error: 에러 발생했을 때 상태</li>
<li>success: 쿼리 함수가 오류 없이 요청 성공하고 데이터를 표시할 준비가 된 상태</li>
</ul>
</li>
<li>isPending: 쿼리가 아직 수행되지 않았고, 캐싱된 데이터가 없을 때 true를 반환한다<ul>
<li>status(pending)에 파생된 boolean 값이다</li>
</ul>
</li>
<li>fetchStatus: queryFn에 대한 정보를 나타냄<ul>
<li>fetching: 쿼리가 현재 실행 중인 상태</li>
<li>paused: 쿼리를 요청했지만, 잠시 중단된 상태 (network mode와 연관)</li>
<li>idle: 쿼리가 현재 아무 작업도 수행하지 않는 상태</li>
</ul>
</li>
<li>isLoading: 캐싱 된 데이터가 없을 때 즉, 처음 실행된 쿼리일 때 로딩 여부에 따라 true/false로 반환된다.<ul>
<li>이는 캐싱 된 데이터가 있다면 로딩 여부에 상관없이 false를 반환한다.</li>
<li>status(pending)와 fetchStatus(fetching) 결합된 boolean이다. 즉, isFetching &amp;&amp; isPending 와 동일하다.</li>
</ul>
</li>
<li>isFetching: 캐싱된 데이터가 있더라도 서버에 요청을 보내면 true를 반환한다<ul>
<li>fetchStatus(fetching)에 파생된 boolean 값이다</li>
</ul>
</li>
<li>isSuccess: 쿼리 요청이 성공하면 true</li>
<li>isError: 쿼리 요청 중에 에러가 발생한 경우 true</li>
<li>refetch: 쿼리를 수동으로 다시 가져오는 함수
그 외 반환 데이터들을 자세히 알고 싶으면 <a href="https://tanstack.com/query/v5/docs/framework/react/reference/useQuery">useQuery 공식 문서 참고</a></li>
</ul>
<br>







<p>솔직히 옵션이 너무 많아서 엄청 복잡해보인다. 하지만 평소 state 관리하던 loading이나 성공 결과들을
API 요청 시 한번에 처리 할 수 있다는것이 tanStack Query의 장점인 것 같다.
<br>
<del>옵션을 하나씩 정리하기에는 너무 많은 양으로 아래 참조 링크를 보며 학습하도록 하자... 🙀</del></p>
<hr>
<p><a href="https://www.heropy.dev/p/HZaKIE">https://www.heropy.dev/p/HZaKIE</a>
<a href="https://github.com/ssi02014/react-query-tutorial?tab=readme-ov-file#%EA%B0%9C%EC%9A%94">https://github.com/ssi02014/react-query-tutorial?tab=readme-ov-file#%EA%B0%9C%EC%9A%94</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TS] 유틸리티 타입 정리]]></title>
            <link>https://velog.io/@jeun_ios/TS-%EC%9C%A0%ED%8B%B8%EB%A6%AC%ED%8B%B0-%ED%83%80%EC%9E%85-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@jeun_ios/TS-%EC%9C%A0%ED%8B%B8%EB%A6%AC%ED%8B%B0-%ED%83%80%EC%9E%85-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Mon, 16 Jun 2025 05:50:05 GMT</pubDate>
            <description><![CDATA[<h2 id="목차">목차</h2>
<blockquote>
<ol start="0">
<li>유틸리티 타입이란</li>
</ol>
</blockquote>
<ol>
<li><code>Partial&lt;T&gt;</code></li>
<li><code>Required&lt;T&gt;</code></li>
<li><code>Readonly&lt;T&gt;</code></li>
<li><code>Pick&lt;T, K&gt;</code></li>
<li><code>Omit&lt;T, K&gt;</code></li>
<li><code>Record&lt;K, V&gt;</code></li>
<li><code>Exclude&lt;T, K&gt;</code></li>
<li><code>Extract&lt;T, K&gt;</code></li>
<li><code>ReturnType&lt;T&gt;</code></li>
</ol>
<h3 id="유틸리티-타입-">유틸리티 타입 ?</h3>
<h4 id="타입스크립트가-자체적으로-제공하는-특수한-타입">타입스크립트가 자체적으로 제공하는 특수한 타입</h4>
<p><code>맵드 타입 기반</code>: 목차 1~6번 
(key를 기준으로 새로운 타입을 만든다. 키의 반복을 통해 속성 자체를 바꾸는 것에 초점)</p>
<p><code>조건부 타입 기반</code>: 목차 7~9번
(타입이 어떤 조건에 따라 바뀌는 &quot;삼항 연산자 스타일&quot; 분기)</p>
<h3 id="partialt"><code>Partial&lt;T&gt;</code></h3>
<p>특정 객체 타입의 모든 프로퍼티를 선택적 프로퍼티로 변환
👉 기존 객체 타입에 정의된 프로퍼티 중 일부분만 사용할 수 있도록 도와주는 타입</p>
<pre><code class="language-ts">interface Post {
  title: string;
  tags: string[];
  content: string;
  thumbnailURL?: string;
}

const draft: Partial&lt;Post&gt; = {
  title: &quot;제목&quot;,
  content: &quot;내용&quot;,
};</code></pre>
<h3 id="requiredt"><code>Required&lt;T&gt;</code></h3>
<p>특정 객체 타입의 모든 프로퍼티를 필수 프로퍼티로 변환 (partial 반대)
👉 사용 시 옵셔널 프로퍼티 생략 불가능 (생략 시 오류 발생)</p>
<pre><code class="language-ts">interface Post {
  title: string;
  tags: string[];
  content: string;
  thumbnailURL?: string;
}

const withThumbnailPost: Required&lt;Post&gt; = {
  title: &quot;제목&quot;,
  tags: [&quot;ts&quot;],
  content: &quot;&quot;,
  // thumbnailURL: &quot;&quot;,
};</code></pre>
<h3 id="readonlyt"><code>Readonly&lt;T&gt;</code></h3>
<p>특정 객체 타입의 모든 프로퍼티를 읽기 전용 프로퍼티로 변환
👉 사용 시 수정 불가능 (수정 시 오류 발생)</p>
<pre><code class="language-ts">interface Post {
  title: string;
  tags: string[];
  content: string;
  thumbnailURL?: string;
}

const readonlyPost: Readonly&lt;Post&gt; = {
  title: &quot;수정 불가&quot;,
  tags: [],
  content: &quot;&quot;,
};

readonlyPost.content = &#39;수정 시도&#39;;</code></pre>
<h3 id="pickt-k"><code>Pick&lt;T, K&gt;</code></h3>
<p>특정 객체 타입으로부터 특정 프로퍼티만 골라내는 타입</p>
<pre><code class="language-ts">interface Post {
  title: string;
  tags: string[];
  content: string;
  thumbnailURL?: string;
}

const legacyPost: Pick&lt;Post, &quot;title&quot; | &quot;content&quot;&gt; = {
  title: &quot;&quot;,
  content: &quot;&quot;,
};
// 추출된 타입 : { title : string; content : string }</code></pre>
<h3 id="omitt-k"><code>Omit&lt;T, K&gt;</code></h3>
<p>특정 객체 타입으로부터 특정 프로퍼티만을 제거하는 타입</p>
<pre><code class="language-ts">interface Post {
  title: string;
  tags: string[];
  content: string;
  thumbnailURL?: string;
}

const noTitlePost: Omit&lt;Post, &quot;title&quot;&gt; = {
  content: &quot;&quot;,
  tags: [],
  thumbnailURL: &quot;&quot;,
};</code></pre>
<h3 id="recordk-v"><code>Record&lt;K, V&gt;</code></h3>
<p>type 중복 프로퍼티 추가 시 이용하는 타입
👉 K에는 어떤 프로퍼티들이 있을지 String Literal Union 타입을 할당, V에는 프로퍼티의 값 타입을 할당</p>
<pre><code class="language-ts">type Thumbnail = {
  large: {
    url: string;
  };
  medium: {
    url: string;
  };
  small: {
    url: string;
  };
};

// 위 코드를 아래처럼 사용 가능

type Thumbnail = Record&lt;
  &quot;large&quot; | &quot;medium&quot; | &quot;small&quot;,
  { url: string }
&gt;;</code></pre>
<hr>
<p>아래는 조건부 타입 기반 타입 정리 !</p>
<h3 id="excludet-k"><code>Exclude&lt;T, K&gt;</code></h3>
<p>Exclude 타입은 T로부터 U를 제거하는 타입
👉 여러 개가 합쳐진 유니언 타입에서 특정 케이스만 제거하고 싶을 때 사용</p>
<pre><code class="language-ts">type Status = &quot;loading&quot; | &quot;success&quot; | &quot;error&quot; | &quot;idle&quot;;

// Status 타입에서 &quot;loading&quot; 상태만 제외하고 싶어요.
type NonLoadingStatus = Exclude&lt;Status, &quot;loading&quot;&gt;;
// 결과 타입 NonLoadingStatus는 &quot;success&quot; | &quot;error&quot; | &quot;idle&quot; 가 돼요!

let currentStatus: NonLoadingStatus = &quot;success&quot;;
// currentStatus = &quot;loading&quot;; // 에러! NonLoadingStatus 타입에는 &quot;loading&quot;이 없어요.</code></pre>
<h3 id="extractt-k"><code>Extract&lt;T, K&gt;</code></h3>
<p>Extract 타입은 T로 부터 U를 추출하는 타입입니다
👉 유니언 타입 중에서 특정 타입들만 따로 뽑아내고 싶을 때 사용</p>
<pre><code class="language-ts">type EventType = &quot;click&quot; | &quot;mouseover&quot; | &quot;keydown&quot; | &quot;keyup&quot;;

// EventType 중에서 마우스 관련 이벤트(&quot;click&quot;, &quot;mouseover&quot;)만 뽑아내고 싶어요.
type MouseEventType = Extract&lt;EventType, &quot;click&quot; | &quot;mouseover&quot;&gt;;
// 결과 타입 MouseEventType는 &quot;click&quot; | &quot;mouseover&quot; 가 돼요!

let mouseEvent: MouseEventType = &quot;click&quot;;
// mouseEvent = &quot;keydown&quot;; // 에러! MouseEventType 타입에는 &quot;keydown&quot;이 없어요.</code></pre>
<h3 id="returntypet"><code>ReturnType&lt;T&gt;</code></h3>
<p>타입변수 T에 할당된 함수 타입의 반환값 타입을 추출하는 타입</p>
<pre><code class="language-ts">type ReturnType&lt;T extends (...args: any) =&gt; any&gt; = T extends (
  ...agrs: any
) =&gt; infer R
  ? R
  : never;

function funcA() {
  return &quot;hello&quot;;
}

function funcB() {
  return 10;
}

type ReturnA = ReturnType&lt;typeof funcA&gt;;
// string

type ReturnB = ReturnType&lt;typeof funcB&gt;;
// number</code></pre>
<hr>
<p>코드 예시 출처
<a href="https://www.inflearn.com/course/%ED%95%9C%EC%9E%85-%ED%81%AC%EA%B8%B0-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8">한 입 크기로 잘라먹는 타입스크립트</a>
<a href="https://velog.io/@outofearth/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%9D%98-%EB%8C%80%ED%91%9C%EC%A0%81%EC%9D%B8-%EC%9C%A0%ED%8B%B8%EB%A6%AC%ED%8B%B0-%ED%83%80%EC%9E%85-10%EA%B0%80%EC%A7%80#7-%EB%84%88%EB%8A%94-%EB%B9%BC%EA%B3%A0-excludet-u">https://velog.io/@outofearth/posts</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React & Next] Hydration과 Bundling]]></title>
            <link>https://velog.io/@jeun_ios/m24dgldm</link>
            <guid>https://velog.io/@jeun_ios/m24dgldm</guid>
            <pubDate>Sat, 14 Jun 2025 13:58:19 GMT</pubDate>
            <description><![CDATA[<h3 id="hydration">Hydration</h3>
<blockquote>
<p>서버가 미리 만든 HTML에 브라우저에서 React의 JS 기능을 붙이는 과정
이미 렌더링 된 HTML에 <code>useState</code>, <code>useEffect</code>, <code>onClick</code> 등의 기능을 살리는 것</p>
</blockquote>
<h4 id="💡-발생">💡 발생</h4>
<ul>
<li>SSR이나 SSG처럼 HTML이 먼저 도착한 경우</li>
<li>브라우저는 해당 HTML과 React의 가상 DOM을 동기화함</li>
</ul>
<h4 id="🤸♀️-예시">🤸‍♀️ 예시</h4>
<pre><code class="language-tsx">// 서버 렌더링된 HTML
&lt;h1&gt;타이틀&lt;/h1&gt;
&lt;button&gt;버튼&lt;/button&gt;

// 브라우저에서 JS 번들이 로드됨
→ React가 해당 HTML과 가상 DOM을 연결 (hydration)
→ onClick 이벤트, useState 등 기능이 활성화됨</code></pre>
<br>

<hr>
<h3 id="bundling">Bundling</h3>
<blockquote>
<p>여러 JS 파일, 모듈들을 하나로 묶어서 단일 JS 파일로 만드는 작업
JS 파일을 최소화하여 최적화 <code>Webpack</code>, <code>Vite</code>, <code>Turbopack</code> 등으로 처리</p>
</blockquote>
<h4 id="💡-발생-1">💡 발생</h4>
<ul>
<li>앱 빌드 시 <code>main.js</code> 번들 파일 생성</li>
</ul>
<h4 id="🤸♀️-예시-1">🤸‍♀️ 예시</h4>
<pre><code class="language-tsx">// src/components/A.tsx
// src/components/B.tsx

→ 번들링 후 여러 JS 파일(예: main.js, chunk.js 등)로 묶임</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[FE 개념정리] 상태 및 보안, DOM]]></title>
            <link>https://velog.io/@jeun_ios/FE-%EA%B0%9C%EB%85%90%EC%A0%95%EB%A6%AC-%EC%83%81%ED%83%9C-%EB%B0%8F-%EB%B3%B4%EC%95%88-DOM</link>
            <guid>https://velog.io/@jeun_ios/FE-%EA%B0%9C%EB%85%90%EC%A0%95%EB%A6%AC-%EC%83%81%ED%83%9C-%EB%B0%8F-%EB%B3%B4%EC%95%88-DOM</guid>
            <pubDate>Fri, 13 Jun 2025 05:01:23 GMT</pubDate>
            <description><![CDATA[<h3 id="http-상태-401과-403의-차이">HTTP 상태 401과 403의 차이</h3>
<ul>
<li><code>401</code> (Unauthorized): 인증이 필요하거나, 인증이 실패한 경우</li>
<li><code>403</code> (Forbidden): 인증은 되었지만 해당 리소스 권한 없음</li>
</ul>
<br>

<h3 id="쿠키와-세션-보안-측면">쿠키와 세션 보안 측면</h3>
<ul>
<li><p>쿠키보다 세션이 안전</p>
<ul>
<li>인증 정보는 서버에만 저장됨</li>
<li>클라이언트는 세션 ID만 전달</li>
<li>서버에서 강제 로그아웃, 만료 처리 통제 가능</li>
</ul>
</li>
<li><p>JWT 사용 시 HTTP 헤더에 담아 전송</p>
<ul>
<li>서버는 토큰의 유효성과 서명만 검증</li>
</ul>
</li>
</ul>
<ul>
<li>JWT 사용 전략
```</li>
</ul>
<ol>
<li>Access Token 헤더 전송</li>
<li>Access Token 만료 시 Refresh Token 발급</li>
<li>refresh 요청 시 이전 토큰 폐기 및 새로운 refresh token 발급</li>
</ol>
<ul>
<li>Refresh Token은 <code>HttpOnly + Secure + SameSite=Strict</code> 쿠키에 저장<pre><code></code></pre></li>
</ul>
<br>

<h3 id="react에서의-dom-컨트롤-방법">React에서의 DOM 컨트롤 방법</h3>
<p>1️⃣ <code>useRef()</code> + <code>.current</code>: 특정 DOM 요소에 직접 접근 시 사용</p>
<pre><code>import { useRef, useEffect } from &#39;react&#39;;

function MyComponent() {
  const inputRef = useRef&lt;HTMLInputElement&gt;(null);

  useEffect(() =&gt; {
    if (inputRef.current) {
      inputRef.current.focus();
    }
  }, []);

  return &lt;input ref={inputRef} /&gt;;
}
</code></pre><br>

<p>2️⃣ <code>useEffect()</code>: DOM이 렌더링 된 후 동작을 수행할 때 사용</p>
<pre><code>useEffect(() =&gt; {
  console.log(document.getElementById(&#39;myDiv&#39;)?.innerText);
}, []);
</code></pre><br>

<p>3️⃣ <code>className</code>/<code>style</code> props: DOM 속성을 선언적으로 제어</p>
<pre><code>&lt;div className={isActive ? &#39;active&#39; : &#39;&#39;} style={{ color: &#39;red&#39; }}&gt;
  Hello
&lt;/div&gt;
</code></pre><br>

<p>4️⃣ <code>dangerouslySetInnerHTML</code>: HTML 문자열을 DOM 삽입 시 사용 (XSS 위험)</p>
<pre><code>&lt;div dangerouslySetInnerHTML={{ __html: &#39;&lt;b&gt;Hello&lt;/b&gt;&#39; }} /&gt;
</code></pre><p><strong>✏️ 직접 접근이 필요한 경우에만 <code>ref</code>로 제한적으로 사용하는 것이 베스트</strong></p>
<br>


<h3 id="xss와-csrf">XSS와 CSRF</h3>
<h4 id="xss-악성-스크립트를-웹사이트에-삽입해서-다른-사용자의-브라우저에서-실행되게-하는-공격">XSS: 악성 스크립트를 웹사이트에 삽입해서 다른 사용자의 브라우저에서 실행되게 하는 공격</h4>
<ul>
<li>공격 대상: 사용자</li>
<li><code>&lt;input&gt;</code>, <code>&lt;textarea&gt;</code>, 댓글, 게시판 등 사용자가 입력을 표시하는 곳에서 발생</li>
<li>쿠키 탈취 및 세션 하이재킹, 사용자 입력 조작 등의 목적</li>
</ul>
<h4 id="csrf-사용자가-로그인된-상태에서-공격자가-만든-요청을-사용자-의지와-무관하게-실행되게-하는-공격">CSRF: 사용자가 로그인된 상태에서, 공격자가 만든 요청을 사용자 의지와 무관하게 실행되게 하는 공격</h4>
<ul>
<li>공격대상: 서버</li>
<li>예시: <code>&lt;img src=&quot;http://bank.com/transfer?amount=10000&amp;to=hacker&quot;&gt;</code></li>
<li>로그인된 사용자의 권한으로 요청. 무단 송금, 비밀번호 변경 등 서버 상태 변경</li>
</ul>
<h4 id="🔒-해결방법">🔒 해결방법</h4>
<pre><code>XSS : 출력 시 반드시 이스케이프 처리 및 입력값 필터링, 
      CSP(악성 스크립트 차단) 적용, 쿠키에 HttpOnly 속성 설정

CSRF : CSRF 토큰 사용 및 SameSite 쿠키 옵션 설정, 
       origin 헤더 검증, 로그아웃 시 세션 무효화, 세션 timeout 설정</code></pre><br>

<hr>
<p>그리고.. 실제 서비스에서의 Next.js의 단점/제약 정리 (App Router 기준)</p>
<h4 id="😬-서버-컴포넌트--클라이언트-컴포넌트-분리의-복잡성">😬 서버 컴포넌트 + 클라이언트 컴포넌트 분리의 복잡성</h4>
<ul>
<li>컴포넌트 내부에서 useState, useEffect, event listener 사용 시 use client 필요</li>
<li>이로 인해 모든 버튼 컴포넌트마다 use client를 붙이게 되거나, SSR 의도가 무너질때도 있다.. (경험담 ... ^^)</li>
</ul>
<h4 id="🐝-app-router-구조와-page-router-구조의-충돌">🐝 App Router 구조와 Page Router 구조의 충돌</h4>
<ul>
<li>기존 page router 기반 프로젝트에서 app router로 마이그레이션 시 혼합 사용 불가</li>
<li>구조/라이프사이클/라우팅 방식이 완전히 다름 
ex) <code>getServerSideProps</code> → <code>server component</code> or <code>fetch</code>로 대체</li>
<li>그렇기 때문에 일부만 마이그레이션 불가. 기존 라이브러리와 호환 이슈 발생</li>
</ul>
<h4 id="👾-레이아웃-구조-복잡화">👾 레이아웃 구조 복잡화</h4>
<ul>
<li>페이지마다 <code>layout.tsx</code>, <code>loading.tsx</code>, <code>error.tsx</code> 등 여러 파일 관리 필요</li>
<li>유지보수 시 새로 들어온 팀원이 구조파악 힘들때가 있다.. 🥲</li>
</ul>
<h4 id="❄️-미들웨어-제약">❄️ 미들웨어 제약</h4>
<ul>
<li>리디렉션, 쿠키 읽기, 헤더 검사, JWT 토큰 단순검사 등 가벼운 조건 체크는 적합!</li>
<li>하지만 고급 인증/인가 처리는 API Route 또는 서버에서 따로 처리를 해야하는 이슈가 있다</li>
</ul>
<br>

<p>Next의 장점은 충분히 알고 있지만, 단점에 대한 질문이 나오면 바로 답변이 나오지 않아서 정리해봤다 ㅎㅎ..</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next] Server Action, API Route, Server Component]]></title>
            <link>https://velog.io/@jeun_ios/wsej3q01</link>
            <guid>https://velog.io/@jeun_ios/wsej3q01</guid>
            <pubDate>Tue, 27 May 2025 16:42:14 GMT</pubDate>
            <description><![CDATA[<h3 id="server-action-use-server">Server Action [<code>use server</code>]</h3>
<ul>
<li><code>폼 제출</code>이나 <code>API 호출</code>처럼 특정 이벤트 처리</li>
<li>함수로 구현되고, <code>use server</code>를 붙여서 서버에서 실행됨 <br> (클라이언트 컴포넌트에서 호출 가능)</li>
</ul>
<h3 id="api-route">API Route</h3>
<ul>
<li><code>/app/api/</code>에 정의된 엔드 포인트 (App Router 기준)</li>
<li>프록시 서버, Webhook, 외부 API 호출 처리 등 범용적으로 사용 가능</li>
</ul>
<h3 id="server-component">Server Component</h3>
<ul>
<li>서버에서 렌더링되는 컴포넌트 (서버에서만 실행)</li>
<li>클라이언트에는 <code>HTML</code>만 전송 (번들 사이즈 줄고 초기 로딩 속도 ↑)</li>
<li>인터랙티브는 클라이언트 컴포넌트로 내려줘야됨</li>
</ul>
<hr>
<h4 id="🤔-그래서-server-action과-api-route의-차이점은">🤔 그래서 Server Action과 API Route의 차이점은?</h4>
<p>Server Action</p>
<pre><code>1. 버튼 클릭 이벤트 같은 UI 액션을 서버에서 처리
2. FormData 파싱 및 CSRF 방어를 자동으로 처리
3. GET 요청에 직접 대응 불가능 (폼 action 방식이라 기본적으로 POST)</code></pre><p>API Route</p>
<pre><code>1. GET, POST, PUT, DELETE 등 다양한 HTTP 메서드 지원
2. fetch, axios 같은 클라이언트 코드에서 호출 필요
3. FormData 파싱 및 CSRF 방어 직접 구현</code></pre><br>

<p>여기까지 읽었을 때 평소에 API 연동 시 GET 요청만 아니면 Server Action으로 처리해도 되지 않나? 싶었는데...</p>
<ul>
<li>CORS 프록시 서버 역할</li>
<li>외부 서비스에서 호출(Webhook, Slack 등)</li>
<li>헤더/쿠키 제어</li>
<li>실시간 데이터 처리(WebSocket)</li>
<li>여러 메서드 분기처리</li>
</ul>
<p>위에 해당되는 케이스일 때 API Route를 사용하면 된다!</p>
<hr>
<h4 id="🙆♀️-한줄정리">🙆‍♀️ 한줄정리</h4>
<p>Server Component = UI
Server Action = 컴포넌트 안에서 일어나는 액션 처리
API Route = HTTP 메서드와 API를 처리하는 곳</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next] SSG fallback option]]></title>
            <link>https://velog.io/@jeun_ios/Next-SSG-fallback-option</link>
            <guid>https://velog.io/@jeun_ios/Next-SSG-fallback-option</guid>
            <pubDate>Tue, 27 May 2025 11:51:26 GMT</pubDate>
            <description><![CDATA[<p>Next.js에서 <code>[id].tsx</code> 같은 <strong>동적 경로 페이지</strong>에 <code>getStaticProps</code>를 사용할 경우,
필요한 fallback 옵션 정리</p>
<h3 id="fallback-옵션-비교">fallback 옵션 비교</h3>
<table>
<thead>
<tr>
<th>fallback 옵션</th>
<th>undefined path 접근 시</th>
<th>최초 요청</th>
<th>이후 요청</th>
<th>SEO</th>
</tr>
</thead>
<tbody><tr>
<td><code>false</code></td>
<td>404 반환</td>
<td>빠름 (SSG)</td>
<td>SSG</td>
<td>좋음</td>
</tr>
<tr>
<td><code>blocking</code></td>
<td>서버에서 생성 후 반환</td>
<td>느림 (1회 SSR)</td>
<td>SSG</td>
<td>좋음</td>
</tr>
<tr>
<td><code>true</code></td>
<td>빈 페이지 + 로딩</td>
<td>빠름 (레이아웃만)</td>
<td>SSG</td>
<td>낮음</td>
</tr>
</tbody></table>
<hr>
<h3 id="fallback-false">fallback: false</h3>
<ul>
<li>정의된 path만 HTML로 사전 생성</li>
<li>정의되지 않은 path는 404 Not Found 반환</li>
<li>사용 예시: 고정된 블로그 글, 정책 페이지</li>
</ul>
<h3 id="fallback-blocking">fallback: blocking</h3>
<ul>
<li>정의되지 않은 path 접근 시, 빌드 타임 이후 서버에서 HTML을 실시간 생성</li>
<li>생성된 페이지는 캐시에 저장 → 이후부터 SSG처럼 동작</li>
<li>사용 예시: SEO가 중요한 상세 페이지 (뉴스, 상품)</li>
</ul>
<h3 id="fallback-true">fallback: true</h3>
<ul>
<li>정의되지 않은 path는 빈 페이지 반환 (레이아웃만 표시 / 로딩 UI 필요)</li>
<li>클라이언트는 props 없이 먼저 렌더링 → 이후 서버에서 데이터를 받아 채움</li>
<li>사용 예시: 페이지 수가 많은 곳 (커뮤니티, Q&amp;A 게시판)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next] Router]]></title>
            <link>https://velog.io/@jeun_ios/Next-Router</link>
            <guid>https://velog.io/@jeun_ios/Next-Router</guid>
            <pubDate>Thu, 22 May 2025 10:16:36 GMT</pubDate>
            <description><![CDATA[<h3 id="page-router">Page Router</h3>
<p>Next 초창기부터 제공되어 오던 구 버전의 라우터</p>
<h4 id="장점">장점</h4>
<ul>
<li>파일 시스템 기반의 간편한 페이지 라우팅 제공</li>
<li>다양한 방식의 사전 렌더링 제공 <code>(SSR, SSG, ISR)</code></li>
</ul>
<table>
<thead>
<tr>
<th></th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><strong>SSR</strong></td>
<td>요청이 들어올 때 마다 서버에서 사전 렌더링 진행</td>
</tr>
<tr>
<td><strong>SSG</strong></td>
<td>빌드 타임에 미리 페이지 사전 렌더링하여 정적 HTMl 생성</td>
</tr>
<tr>
<td><strong>ISR</strong></td>
<td>SSG 페이지 일정 시간마다 재생성 (revalidate)</td>
</tr>
</tbody></table>
<h4 id="단점">단점</h4>
<ul>
<li>페이지별 레이아웃 설정이 번거로움 (코드 중복 ↑)</li>
<li>data fetching이 페이지 컴포넌트에 집중됨</li>
<li>불필요한 컴포넌트들도 JS Bundle에 포함됨</li>
</ul>
<hr>
<h3 id="app-router">App Router</h3>
<p>Next 13버전과 함께 처음으로 공개된 신규 라우터 </p>
<h4 id="장점-1">장점</h4>
<ul>
<li>유연한 레이아웃 시스템 제공 (중첩 레이아웃, Parallel Routes)</li>
<li>Server Component 지원 → JS Bundle 크기 절감 가능</li>
<li>서버 액션으로 API Route 대체 가능</li>
</ul>
<h4 id="단점-1">단점</h4>
<ul>
<li>서버 / 클라이언트 각 컴포넌트 분리 필요</li>
<li>Server Component에서는 브라우저 API 사용 불가 (window, document 등)</li>
<li>hydration mismatch, suspense 오류 시 디버깅 복잡성 증가</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[ESLint]]></title>
            <link>https://velog.io/@jeun_ios/jxgqq30k</link>
            <guid>https://velog.io/@jeun_ios/jxgqq30k</guid>
            <pubDate>Wed, 21 May 2025 15:14:25 GMT</pubDate>
            <description><![CDATA[<h3 id="eslint-">ESLint ?</h3>
<pre><code>코드를 실행하지 않고 문법 오류, 버그 가능성, 스타일 문제를 자동으로 찾아주는 도구</code></pre><h3 id="사용-이유-">사용 이유 ?</h3>
<ul>
<li>버그 예방: 변수 선언을 하지 않았는데 사용하거나 오타 같은 실수를 잡아줌</li>
<li>코드 일관성 유지: 포맷을 통일해줌 (세미콜론이나 탭/스페이스 등)</li>
<li>리팩터링 용이: 이상한 코드 패턴을 미리 알려주기 때문에 유지보수에 도움이 됨</li>
<li>팀 협업 필수: 규칙 기반으로 자동 리뷰를 도와줌</li>
</ul>
<h3 id="eslint가-잡아주는-구체적-예시">ESLint가 잡아주는 구체적 예시</h3>
<pre><code class="language-js">async function fetchData() {
  const res = fetch(&quot;https://api.com/data&quot;);
  const data = await res.json();
  return data;
}
// ESLint: Expected &#39;await&#39; before calling &#39;fetch&#39; because the function is async.</code></pre>
<pre><code class="language-json">// .eslintrc
{
  &quot;rules&quot;: {
    &quot;require-await&quot;: &quot;warn&quot; // async 함수에서 await 없으면 경고
  }
}</code></pre>
<ul>
<li>ESLint 플러그인에서 eslint-plugin-promise 사용</li>
<li>규칙 중 <code>require-await</code>, <code>no-return-await</code>, <code>no-async-promise-executor</code>, <code>prefer-await-to-callbacks</code> 같은 async 관련 규칙이 켜져 있을 때</li>
</ul>
<br>

<pre><code class="language-js">const age = &quot;30&quot;;

if (age == 30) {
  console.log(&quot;30살입니다&quot;);
}
// ESLint: Expected &#39;===&#39; and instead saw &#39;==&#39;</code></pre>
<pre><code class="language-json">{
  &quot;rules&quot;: {
    &quot;eqeqeq&quot;: [&quot;error&quot;, &quot;always&quot;]
  }
}
</code></pre>
<ul>
<li>항상 <code>===</code>과 <code>!==</code>만 쓰고, <code>==</code>과 <code>!=</code>은 쓰지 말라는 설정</li>
<li><code>==</code> 는 타입이 다르더라도 자동 형변환 후 값이 같으면 true</li>
<li><code>===</code>는 타입과 값이 모두 같아야 true, 타입이 다르면 false</li>
</ul>
<br>

<hr>
<p>설정 파일은 항상 프로젝트 루트에 존재하도록 한다.</p>
<p>자세한 활용방법: <a href="https://tech.kakao.com/posts/375">https://tech.kakao.com/posts/375</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] 상태관리 총정리]]></title>
            <link>https://velog.io/@jeun_ios/qcp5w7e1</link>
            <guid>https://velog.io/@jeun_ios/qcp5w7e1</guid>
            <pubDate>Wed, 21 May 2025 11:29:04 GMT</pubDate>
            <description><![CDATA[<h3 id="🥕-react-내장">🥕 React 내장</h3>
<p><code>useState</code>: 컴포넌트 레벨의 로컬 상태 관리
<code>useReducer</code>: 복잡한 상태 로직 관리 (Redux와 유사한 패턴으로 상태 업데이트)
<code>useContext</code>: 전역 상태 공유 (prop drilling 해결)</p>
<h3 id="🥐-라이브러리">🥐 라이브러리</h3>
<p><code>Redux</code>: Redux Toolkit으로 사용하는 대규모 상태 관리 라이브러리
<code>Zustand</code>: 심플하고 가벼운 전역 상태 관리, Provider 없이 사용
<code>Jotai</code>: Context보다 빠르고 직관적인 상태 관리
<code>Recoil</code>: 상태 간 의존성과 Selector로 유연한 상태 관리
<code>Valtio</code>: 객체처럼 다루며 간단하게 사용하는 상태 관리
<code>Mobx</code>: 객체지향적 상태 관리, 반응성 높은 UI</p>
<h3 id="🥨-서버-상태관리">🥨 서버 상태관리</h3>
<p><code>TanStack Query</code>: 서버 상태 관리 특화, API 데이터 관리 시 사용
<code>SWR</code>: 간단한 데이터 페칭과 캐싱 필요 시 사용
<code>Apollo Client</code>: 로컬 상태와 원격 상태 통합 관리 (GraphQL 전용)</p>
<h3 id="🍞-폼-상태관리">🍞 폼 상태관리</h3>
<p><code>React Hook Form</code>: 성능 최적화 폼 라이브러리, 최소 리렌더링
<code>Formik</code>: 폼 상태와 유효성 검사 필요시 사용</p>
<br>

<hr>
<h4 id="🤖-상태관리의-아키텍처-패턴-이나-구현방식-분류-개념">🤖 상태관리의 &quot;아키텍처 패턴&quot; 이나 &quot;구현방식&quot; 분류 개념</h4>
<br>

<h3 id="1️⃣-atomic-store">1️⃣ Atomic Store</h3>
<p>개념:</p>
<ul>
<li>상태를 작은 단위(원자, atom)로 나누어 관리</li>
<li>각 원자는 독립적이고 필요할 때만 구독/업데이트</li>
<li>Bottom-up 방식 (작은 것부터 조합해서 큰 것을 만듦)</li>
</ul>
<p>라이브러리:</p>
<ul>
<li><strong>Jotai</strong>: <code>atom()</code>함수로 원자 생성</li>
<li><strong>Recoil</strong>: <code>atom()</code>과 <code>selector()</code> 사용</li>
</ul>
<p>장점: </p>
<ul>
<li>필요한 부분만 리렌더링</li>
<li>컴포넌트별 필요한 상태만 구독</li>
</ul>
<br>

<h3 id="2️⃣-flux-pattern">2️⃣ Flux Pattern</h3>
<p>개념:</p>
<ul>
<li>단방향 데이터 플로우 아키텍처</li>
<li>Action → Dispatcher → Store → View → Action 순환<blockquote>
<p>Action → Dispatcher → Store → View
↑ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ↓
←←←←←←←←←←←←←←←←←</p>
</blockquote>
</li>
</ul>
<p>라이브러리:</p>
<ul>
<li><strong>Redux</strong>: Flux의 단순화된 구현</li>
<li><strong>Zustand</strong>: Flux 패턴을 간소화</li>
</ul>
<p>장점:</p>
<ul>
<li>예측 가능한 상태변화</li>
<li>디버깅 용이 및 액션을 통한 상태 변경만 허용</li>
</ul>
<br>

<h3 id="3️⃣-proxy-store">3️⃣ Proxy Store</h3>
<p>개념:</p>
<ul>
<li>객체에 대한 접근과 수정을 가로채서 반응성 구현</li>
<li>불변성 관리 없이 직접 뮤테이션 가능</li>
</ul>
<p>라이브러리:</p>
<ul>
<li><strong>Valtio</strong>: 가장 대표적인 Proxy 기반 라이브러리</li>
<li><strong>MobX</strong>: Proxy + 데코레이터 패턴 (관찰 가능한 상태)</li>
</ul>
<p>장점:</p>
<ul>
<li>직관적인 상태 변경 (일반 객체처럼 사용)</li>
<li>자동 최적화</li>
</ul>
<p>예시 (Valtio):</p>
<pre><code class="language-javascript">const state = proxy({ count: 0 })

// 직접 변경 가능
state.count += 1</code></pre>
<br>

<hr>
<h4 id="🥹-그래서-언제-무엇을-선택하면-될까">🥹 그래서 언제 무엇을 선택하면 될까?</h4>
<ul>
<li><p>Atomic Store (Jotai, Recoil)</p>
<blockquote>
<p>상태 간 복잡한 의존성 있을 때
세밀한 성능 최적화가 필요할 때
상태를 작은 단위로 나누어 관리하고 싶을 때</p>
</blockquote>
</li>
<li><p>Flux Pattern (Redux, zustand)</p>
<blockquote>
<p>예측 가능한 상태 변화가 중요할 때
팀 개발에서 일관성이 필요할 때
디버깅과 테스트가 중요할 때</p>
</blockquote>
</li>
<li><p>Proxy Store (Valtio, Mobx)</p>
<blockquote>
<p>빠른 프로토타이핑이 필요할 때
불변성 관리가 번거로울 때
직관적인 코드 작성을 원할 때</p>
</blockquote>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Swfit] 타입 정리 및 학습 로드맵]]></title>
            <link>https://velog.io/@jeun_ios/Swfit-%ED%83%80%EC%9E%85-%EC%B4%88%EB%B0%98%EB%B6%80-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@jeun_ios/Swfit-%ED%83%80%EC%9E%85-%EC%B4%88%EB%B0%98%EB%B6%80-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Wed, 18 Jan 2023 16:42:45 GMT</pubDate>
            <description><![CDATA[<h3 id="swift의-주요-타입">Swift의 주요 타입</h3>
<ul>
<li><p>Basic Type
Int, Double, String, Bool 등</p>
</li>
<li><p>Custom Type (사용자 정의 타입)
Enum, Class, Struct</p>
</li>
<li><p>Tuple
데이터들을 소괄호로 묶어서 간단하게 사용 할 수 있음..
(String, Int, String)</p>
</li>
<li><p>Collection
아이템들이 들어가는 것
(Array, Dictionary, Set)</p>
</li>
</ul>
<br>

<p><strong>모든 데이터 타입은 옵셔널 처리가 가능하다</strong></p>
<hr>
<h4 id="💡-메모리-구조-이해-cs-관점-동작원리">💡 메모리 구조 이해 (CS 관점 동작원리)</h4>
<ul>
<li>코드 영역: 함수 및 명령어 저장</li>
<li>데이터 영역: 전역 변수 저장</li>
<li>스택 영역: 함수 실행 시 생성되는 지역 변수, 파라미터</li>
<li>힙 영역: 클래스 인스턴스 등 동적 메모리 할당</li>
</ul>
<h4 id="swift-기초-문법-흐름">Swift 기초 문법 흐름</h4>
<ul>
<li>변수/상수 </li>
<li>기본연산자 (% 주의) </li>
<li>조건문(if/switch) </li>
<li>튜플 </li>
<li>삼항/범위 연산자(...) </li>
<li>반복문(for/while) </li>
</ul>
<h4 id="함수">함수</h4>
<ul>
<li>파라미터 → 스택에 저장</li>
<li>호출 시 → 스택 프레임에 쌓이며 실행</li>
</ul>
<h4 id="옵셔널">옵셔널</h4>
<ul>
<li>값이 있을 수도, 없을 수도 있는 타입</li>
<li>선언 예: <code>var name: String?</code></li>
</ul>
<blockquote>
<p>처리 방법</p>
</blockquote>
<ul>
<li><code>!</code> 강제 언래핑</li>
<li><code>if let</code> 바인딩</li>
<li><code>guard let</code> 바인딩</li>
<li><code>??</code> 닐 병합 (기본값 제공)</li>
</ul>
<h4 id="컬렉션">컬렉션</h4>
<ul>
<li>Array, Dictionary, Set</li>
<li>각각 순서/키-값/중복제거 등의 목적</li>
</ul>
<h4 id="열거형">열거형</h4>
<pre><code class="language-swift">enum Weekday {
  case monday, tuesday, wednesday
}
</code></pre>
<p>한정된 사례를 통하여 개발자가 직접 만들 수 있는 Type</p>
<hr>
<p>이후 학습 주제 정리</p>
<br>


<table>
<thead>
<tr>
<th>주제</th>
<th>핵심 개념</th>
</tr>
</thead>
<tbody><tr>
<td>클래스 / 구조체</td>
<td>참조 vs 값 타입, 초기화, 메서드</td>
</tr>
<tr>
<td>상속 / 확장</td>
<td>코드 재사용 및 기능 확장</td>
</tr>
<tr>
<td>프로토콜</td>
<td>공통 인터페이스 추상화</td>
</tr>
<tr>
<td>클로저</td>
<td>함수형 프로그래밍 핵심</td>
</tr>
<tr>
<td>메모리 관리</td>
<td>ARC, strong/weak/unowned</td>
</tr>
<tr>
<td>제네릭</td>
<td>타입에 구애받지 않는 재사용 코드</td>
</tr>
<tr>
<td>에러 처리</td>
<td><code>try</code>, <code>catch</code>, <code>throws</code></td>
</tr>
<tr>
<td>Result 타입</td>
<td>성공/실패 값을 타입으로 관리</td>
</tr>
<tr>
<td>접근 제어</td>
<td><code>private</code>, <code>fileprivate</code>, <code>public</code> 등</td>
</tr>
<tr>
<td>문자열 / 문자 처리</td>
<td>문자열 조작, 유니코드</td>
</tr>
<tr>
<td>고급 연산자</td>
<td>사용자 정의 연산자 등</td>
</tr>
<tr>
<td>날짜/시간</td>
<td><code>Date</code>, <code>DateFormatter</code></td>
</tr>
<tr>
<td>메모리 안전 / 스레드</td>
<td>비동기 처리, <code>DispatchQueue</code>, <code>async/await</code></td>
</tr>
<tr>
<td>아키텍처 / 네트워킹</td>
<td>MVC 패턴, URLSession, API 통신 등</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Swift] Closure]]></title>
            <link>https://velog.io/@jeun_ios/c2cr7exn</link>
            <guid>https://velog.io/@jeun_ios/c2cr7exn</guid>
            <pubDate>Thu, 12 Jan 2023 08:08:17 GMT</pubDate>
            <description><![CDATA[<h3 id="클로저란">클로저란?</h3>
<ul>
<li>func 키워드를 이용해 이름을 붙여주는 함수</li>
</ul>
<br>

<p>클로저에는 두가지 종류가 있다.</p>
<ol>
<li><strong>Named Closure</strong></li>
<li><strong>Unnamed Closure</strong></li>
</ol>
<br>

<p>우리가 여태 써왔던 이름이 있는 함수는</p>
<pre><code class="language-swift">func doSomething() {
        print(&quot;Named Closure&quot;)
}</code></pre>
<p>이런 <strong>Named Closure</strong>이다. 하지만 이것을 클로저라고 부르지 않고 그냥 <strong>함수</strong>라고 불렀던 것이다.</p>
<br>

<p>그리고 이름을 붙이지 않고 사용하는 함수를</p>
<pre><code class="language-swift">let closure = { print(&quot;Unnamed Closure&quot;) }</code></pre>
<p><strong>익명함수</strong>, 즉 <strong>Unnamed Closure</strong> 라고 부르는 것이다.</p>
<br>

<p>정리하자면</p>
<br>

<p><strong>클로저는 Named Closure &amp; Unnamed Closure 둘다 포함하지만,</strong> </p>
<p><strong>보통 Unnamed Closure 를 말한다.</strong></p>
<br>

<hr>
<br>

<h3 id="클로저-표현식">클로저 표현식</h3>
<pre><code class="language-swift">{ (Parameters) -&gt; Return Type in
     실행 구문
}</code></pre>
<p>이렇게 표현식을 사용하는데, 익명 함수인 만큼 <code>func</code> 이란 키워드를 사용하지 않는다.</p>
<p>그리고 클로저는 <strong>Closure Head</strong>와 <strong>Closure Body</strong>로 이루어져 있는데</p>
<br>

<pre><code class="language-swift">{ (Parameters) -&gt; Return Type in
     실행구문
}

// Closure Head : (Parameters) -&gt; Return
// Closure Body : 실행구문</code></pre>
<p>이 둘을 구분지어주는게 바로 <code>in</code> 이라는 키워드이다.</p>
<br>

<h3 id="☝-parameter와-return-type이-둘-다-없는-클로저">☝ Parameter와 Return Type이 둘 다 없는 클로저</h3>
<br>


<p>클로저는 익명이긴 하지만 <strong>함수</strong>이다.</p>
<p>따라서 Swift에서 1급 객체이기 때문에 상수에 클로저를 대입할 수 있다.</p>
<p>특히 <strong>Parameter</strong> 와 <strong>Return Type</strong>이 둘 다 없는 경우는 다음과 같이 사용함</p>
<br>

<pre><code class="language-swift">let closure = { () -&gt; () in
        print(&quot;Closure&quot;)
}</code></pre>
<p><strong>Return Type</strong> 이 <strong>있어도, 없어도 생략 가능</strong>하고 <strong>Parameter</strong> 조차 생략 가능하다.</p>
<br>

<h3 id="✌-parameter와-return-type이-있는-클로저">✌ Parameter와 Return Type이 있는 클로저</h3>
<br>

<pre><code class="language-swift">let driving = { (place: String) -&gt; String in
    return &quot;저는 차를타고 \(place)에 가고 있습니다.&quot;
}</code></pre>
<p><strong>Parameter</strong>와 <strong>Return Type</strong>이 있는 경우는 이렇게 표시한다.</p>
<br>

<p><strong>함수 때 배운 대로 Parameter의</strong> <code>place</code><strong>는 단독으로 쓰였으니, 
Argument Label이자 Parameter Name</strong>이라고 생각할 수 있지만,</p>
<br>

<p>⭐ <strong>클로저에선 Argument Label을 사용하지 않는다</strong> ⭐</p>
<br>

<p>따라서, name은 <strong>Argument Label이 아니고</strong>, 오직 <strong>Parameter Name</strong> 이다. </p>
<p>클로저를 호출할 때는 <strong>Argument Label을 사용하지 않는다 !!</strong></p>
<br>

<pre><code class="language-swift">driving(&quot;회사&quot;) // 저는 차를 타고 회사에 가고 있습니다.
driving(place: &quot;회사&quot;) // Error!</code></pre>
<p>사용할 시 에러가 난다….!</p>
<br>

<hr>
<br>

<h3 id="1급-객체로서-클로저">1급 객체로서 클로저</h3>
<ol>
<li><p><strong>클로저를 변수나 상수에 대입할 수 있다.</strong></p>
<p>   ⇒ 클로저를 변수나 상수에 대입할 수 있고, 대입된 변수나 상수를 실행 시키는 것이 가능하다.</p>
</li>
</ol>
<pre><code class="language-swift">let closure = { () -&gt; () in
        print(&quot;Closure&quot;)
}

let closure2 = closure</code></pre>
<br>

<ol start="2">
<li><strong>함수의 파라미터 타입으로 클로저를 전달할 수 있다.</strong></li>
</ol>
<pre><code class="language-swift">func doSomething(closure: () -&gt; ()) {
        closure()
}</code></pre>
<p>이런 식으로 <strong>함수를 파라미터로 전달받는</strong> <strong>doSomething</strong> 이라는 함수가 있는데 </p>
<p>파라미터로 <strong>함수</strong>를 넘겨줘도 되지만</p>
<pre><code class="language-swift">doSomething(closure: { () -&gt; () in
        print(&quot;Hello!&quot;)
})

// 클로저로 작성된 부분 : { () -&gt; () in print(&quot;Hello!&quot;) }</code></pre>
<p>이렇게 <strong>클로저</strong>를 넘겨줘도 된다.</p>
<p>doSomething이란 함수에서 파라미터로 전달받은 함수를 실행시키면 </p>
<p>작성한 클로저가 실행된다.</p>
<br>

<ol start="3">
<li><strong>함수의 반환 타입으로 클로저를 사용할 수 있다.</strong></li>
</ol>
<pre><code class="language-swift">func doSomething() -&gt; () -&gt; () {

        return { () -&gt; () in
                print(&quot;Hello JE!&quot;)
        }
}</code></pre>
<p>선언부는 기존 함수와 똑같지만 <code>return</code>할 때 실제 값을 함수가 아닌 <strong>클로저를 리턴</strong> 할 수 있다.</p>
<br>

<pre><code class="language-swift">let closure = doSomething()
closure() // Hello JE!</code></pre>
<p>또한 호출하는 곳에서 클로저를 받아서 이렇게 실행시킬 수 있다.</p>
<br>

<hr>
<br>

<h3 id="클로저-실행">클로저 실행</h3>
<ul>
<li>클로저가 대입된 변수나 상수로 호출</li>
</ul>
<pre><code class="language-swift">let closure = { () -&gt; String in
        return &quot;Hello JE!&quot;
}

closure()  // Hello JE!</code></pre>
<p>이런 식으로 클로저가 대입된 상수 closure를 호출 구문인 ()를 이용해서 실행시킬 수 있다.</p>
<br>

<ul>
<li>클로저를 직접 실행하기</li>
</ul>
<pre><code class="language-swift">({ () -&gt; () in
        print(&quot;Hello JE!&quot;)
})()  // Hello JE!</code></pre>
<p>클로저를 변수나 상수에 대입하지 않고 실행시키고 싶다면, (완벽한 일회성)</p>
<p>그땐 <strong>클로저를 ( ) 소괄호로 감싸고</strong>, <strong>마지막에 호출 구문인 ()</strong>를 추가해주면 된다.</p>
<p><br><br><br><br><br></p>
<h3 id="📖-참고">📖 <strong>참고</strong></h3>
<hr>
<p><a href="https://babbab2.tistory.com/81">https://babbab2.tistory.com/81</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Swift] Optional]]></title>
            <link>https://velog.io/@jeun_ios/foj7akny</link>
            <guid>https://velog.io/@jeun_ios/foj7akny</guid>
            <pubDate>Thu, 12 Jan 2023 08:08:12 GMT</pubDate>
            <description><![CDATA[<h3 id="optional이란">Optional이란?</h3>
<ul>
<li>nil을 사용할 수 있는 타입과 없는 타입을 구분하기 위함이며, 
nil을 사용할 수 있는 Type을 <strong>Optional Type</strong>이라 부른다.</li>
</ul>
<br>

<p>쉽게 설명하자면</p>
<br>

<p><code>nil</code> 이라는 값을 가질 수 있으면 <strong>Optional Type</strong>이고, </p>
<p><strong>Optional Type</strong>을 선언할 때 타입 옆에 <code>?</code> 를 붙인다.</p>
<pre><code class="language-swift">let name: String? // String? = Optional Type
let age: Int? // Int? = Optional Type</code></pre>
<br>

<hr>
<br>

<h3 id="nil이란">nil이란?</h3>
<ul>
<li><p>변수에 객체가 할당되지 않은 상태 <strong>“값이 없음”</strong>을 뜻한다.<br></p>
<p>  ⇒ <code>nil</code>과 <code>null</code>은 다르다!</p>
<blockquote>
<p>Nil : 오류가 났지만, 앱 중단시키는 것 대신 nil을 돌려줄 테니 오류 난 것을 알려줄 때 사용 
Null : 어떠한 값도 가지지 않고 있다는 뜻</p>
</blockquote>
</li>
</ul>
<br>

<pre><code class="language-swift">let dictHuman = [&quot;name&quot;: &quot;je&quot;, &quot;gender&quot;: &quot;female&quot;]

let name = dictHuman[&quot;name&quot;] // &quot;je&quot;
let gender = dictHuman[&quot;je&quot;] // nil</code></pre>
<br>

<p><code>name</code>이라는 <strong>key</strong>에 접근을 하면 <code>je</code> 이라는 <strong>value</strong>가 잘 뜨지만,</p>
<p><code>je</code>이라는 <strong>key</strong>에 접근을 했을 때 <strong>dictHuman</strong>에는 <code>je</code>이라는 <strong>key</strong>가 존재하지 않기 때문에 <code>nil</code>이 뜸</p>
<br>

<hr>
<br>

<h3 id="non-optional-type과-optional-type">Non-Optional Type과 Optional Type</h3>
<ul>
<li><p>nil은 오류가 발생했을 때 오류 대신 뱉는 값이라고 하였다.</p>
<blockquote>
<p>어떤 자료형이든 nil을 반환하고 저장 할 수 있을까?
  → NO !! 절대 불가능하다</p>
</blockquote>
</li>
</ul>
<br>

<p><strong>nil</strong>을 <strong>저장</strong>할 수 있는 건 오로지 <strong>Optional</strong>로 <strong>선언된 자료형</strong> 뿐이다.</p>
<br>

<p><strong>Non-Optional Type</strong>은 선언과 동시에 초기화 해주던가 Type Annotation을 사용해야 된다.</p>
<p>⇒ 이 친구는 무조건 값을 가져야 한다 !! 값이 없으면 오류 발생 !!!!</p>
<br>

<p><strong>Optional Type</strong></p>
<ol>
<li>Type Annotation 이용</li>
<li>Type Inference 이용
→ 타입 추론, ⭐<strong>초기화 되는 값이 무조건 Optional 자료형</strong>⭐ 이여야 된다</li>
</ol>
<pre><code class="language-swift">// Type Annotation
var name: String?
name = nil // ?로 선언된 name에는 nil 저장 가능

// Type Inference
let a: Int? = nil
let b = a
print(b) // nil</code></pre>
<br>

<p>아까 nil이란? 을 설명했을 때</p>
<pre><code class="language-swift">let gender = dictHuman[&quot;je&quot;] // nil</code></pre>
<br>

<p>위 구문이 가능했던 이유는 dictHuman에서 value값을 가져오는 <a href="subScript"></a> 의 원형을 보면</p>
<pre><code class="language-swift">@inlinable public subscript(key: Key) -&gt; Value?</code></pre>
<br>

<p>subScript 리턴 값이 옵셔널로 선언되어 있다.</p>
<p>따라서 dictHuman[”je”]의 리턴 값은 옵셔널 자료형인거고, </p>
<p>상수 gender는 타입 추론에 의해 옵셔널 자료형을 갖게 된 것이다.</p>
<br>

<hr>
<br>

<h3 id="type">Type</h3>
<pre><code class="language-swift">var today: String
var weather: String?

print(type(of: today))    // String
print(type(of: weather))  // Optional&lt;String&gt;</code></pre>
<br>

<ul>
<li><p><strong>Non-Optional Type</strong>과 <strong>Optional Type</strong>의 자료형 결과값은 다르다</p>
<blockquote>
<p>Non-Optional Type = String Type
  Optional Type = Optional<String> Type</p>
</blockquote>
</li>
</ul>
<p><br><br><br><br><br></p>
<h3 id="📖-참고">📖 <strong>참고</strong></h3>
<hr>
<p><a href="https://babbab2.tistory.com/15">https://babbab2.tistory.com/15</a></p>
]]></description>
        </item>
    </channel>
</rss>