<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>frontweb_tk.log</title>
        <link>https://velog.io/</link>
        <description>쉬운게 좋은 FE개발자😺</description>
        <lastBuildDate>Mon, 13 May 2024 13:27:16 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>frontweb_tk.log</title>
            <url>https://velog.velcdn.com/images/forntweb_tk/profile/b6ae7f0c-6d18-4fde-b263-8ea61e5004f6/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. frontweb_tk.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/forntweb_tk" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[React.js] 우당탕탕 카운트업 기능 개발하기]]></title>
            <link>https://velog.io/@forntweb_tk/React.js-%EC%B9%B4%EC%9A%B4%ED%8A%B8-%EC%97%85-%EA%B8%B0%EB%8A%A5-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@forntweb_tk/React.js-%EC%B9%B4%EC%9A%B4%ED%8A%B8-%EC%97%85-%EA%B8%B0%EB%8A%A5-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Mon, 13 May 2024 13:27:16 GMT</pubDate>
            <description><![CDATA[<h2 id="기능구현-목표">기능구현 목표</h2>
<ul>
<li><p>다음과 같이 스크롤을 내리면 나타나는 카운트업 기능을 만들고자 한다.
<img src="https://velog.velcdn.com/images/forntweb_tk/post/28a6a374-bc81-48f5-a4ff-cd85abf1dc52/image.gif" alt=""></p>
</li>
<li><p>부분적으로 보기
<img src="https://velog.velcdn.com/images/forntweb_tk/post/fbd5f099-90a8-424c-b5ba-4d5f8335faf8/image.gif" alt=""></p>
</li>
</ul>
<h2 id="📝첫번째-구현">📝첫번째 구현</h2>
<ul>
<li><p>여러 레퍼런스들을 참고하여 아래와 같이 코드를 작성하였다.</p>
<ul>
<li>useCountUp.jsx<pre><code class="language-js">import { useEffect, useState } from &quot;react&quot;;
</code></pre>
</li>
</ul>
<p>/* eslint-disable no-unused-vars */
const useCountUp = (end, start = 0, duration = 2000) =&gt; {
  const [count, setCount] = useState(start);
  useEffect(() =&gt; {</p>
<pre><code>let currentNum = start;
const delay = duration / end;
const countUp = setInterval(() =&gt; {
  currentNum++;
  setCount(currentNum);
  if (currentNum === end) {
    clearInterval(countUp);
  }
}, delay);</code></pre><p>  }, [end, start, duration]);
  return count.toFixed(0);
};
export default useCountUp;</p>
<pre><code>- Counting.jsx
```js

import styled from &quot;styled-components&quot;;
import useCountUp from &quot;../Hooks/useCountUp&quot;;

const CountingWrapper = styled.div`
  display: flex;
  flex-direction: column;
  gap: 2rem;
  align-items: center;
  width: 100%;
`;
const Count = styled.div`
  font-size: ${(props) =&gt; props.theme.fontSize.title_2};
  font-weight: bold;
  color: ${(props) =&gt; props.theme.color.darkGreen};
`;
const Name = styled.div`
  font-size: ${(props) =&gt; props.theme.fontSize.content_14};
  font-weight: bold;

  color: ${(props) =&gt; props.theme.color.orange};
`;

export default function Counting({ ...props }) {
  console.log(props.number);
  console.log(Number(props.number));

  return (
    &lt;CountingWrapper&gt;
      &lt;Count&gt;{useCountUp(Number(props.number), 0, 2000)}&lt;/Count&gt;
      &lt;Name&gt;{props.name}&lt;/Name&gt;
    &lt;/CountingWrapper&gt;
  );
}</code></pre><blockquote>
<p>참고로 Counting에서 전달받는 <code>props.number</code>은 문자이다. 그래서 Number함수로 처리했을때 처음엔 다음과 같은 오류가 발생했었다. 
<code>uncaught typeerror: number is not a function</code> 
검색해보니 내가 스타일을 위해 Number를 선언해 사용하고 있어서 그런 것이었다.(예약어) 그래서 중복되는 이름이 없도록 number라고 사용한 것을 모두 제거하고 Count로 스타일 이름을 바꾸니 오류가 사라지게 되었다.</p>
</blockquote>
</li>
<li><p>그러자 다음과 같이 작동하였다.</p>
</li>
</ul>
</br>

<h3 id="💻첫번째-구현-작동화면">💻첫번째 구현 작동화면</h3>
<blockquote>
<p>참고: 맨처음 숫자인 800k+는 우선 800을 먼저 넣어보았다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/forntweb_tk/post/8ec74349-029f-4145-a1b5-9805e9f67d92/image.gif" alt=""></p>
<ul>
<li><p>문제점</p>
<table>
<thead>
<tr>
<th align="center">구분</th>
<th align="center">원본</th>
<th align="center">내가 만든 것</th>
</tr>
</thead>
<tbody><tr>
<td align="center">렌더링 시기</td>
<td align="center">화면에서는 보이지 않지만  원본은 스크롤해서 해당 구역이 비춰질때부터 카운팅 시작함.</td>
<td align="center">처음 전체 화면 렌더링 되자마자 시작됨.</td>
</tr>
<tr>
<td align="center">카운팅 방식</td>
<td align="center">원본은 3초 정도의 시간안에 동시에 끝남.</td>
<td align="center">내가 만든건 숫자가 높을수록 늦게까지 혼자 카운팅 되고있음.</td>
</tr>
<tr>
<td align="center">화면 효과</td>
<td align="center">자연스럽게 숫자가 나타나는 페이드인? 이 있음</td>
<td align="center"></td>
</tr>
</tbody></table>
</li>
<li><p>카운팅 방식을 먼저 수정해보자.</p>
</li>
</ul>
</br>

<h2 id="📝두번째-구현">📝두번째 구현</h2>
<h3 id="모두-같은-시간에-종료-되도록-먼저-만들자">모두 같은 시간에 종료 되도록 먼저 만들자.</h3>
<p>Q. 처음 작성한 코드에서 <code>const delay = duration / end;</code>에 의해 이론적으로 <code>end</code> 숫자가 커질수록 <code>delay</code> 속도가 빨라지게 하여 모두 동시에 끝나는게 맞지만 왜 그렇게 동작하지 않을까?</p>
<p>A. mdn공식문서를 확인해보니 최소 <code>4ms</code>까지만 가능 ⇒ 그럼 최소 4ms로 맞추고 나머지는 숫자를 띄엄띄엄 세는 식으로 가야겠따.</p>
<blockquote>
<h4 id="지연-제한"><a href="https://developer.mozilla.org/ko/docs/Web/API/setInterval#%EC%A7%80%EC%97%B0_%EC%A0%9C%ED%95%9C">지연 제한</a></h4>
<p>간격(interval)은 중첩될 수 있습니다. 즉, <code>setInterval()</code>에 대한 콜백이 <code>setInterval()</code>을 호출하여 첫 번째 간격이 계속 진행 중일지라도 다른 간격의 실행을 시작할 수 있습니다. 이것이 성능에 미칠 수 있는 잠재적인 영향을 완화하기 위해 간격이 5개 수준 이상으로 중첩되면 브라우저는 자동으로 간격에 대해 4 ms 최소 값을 적용합니다. 중첩 호출이 심화된 <code>setInterval()</code>의 호출에서 4 ms 미만의 값을 지정하면 4 ms로 고정됩니다.</p>
</blockquote>
<ul>
<li><p>각 숫자에 대해서 다음 표와 같이 실험(?)을 진행했다.</p>
<table>
<thead>
<tr>
<th>목표숫자</th>
<th>800k+</th>
<th>89</th>
<th>12</th>
<th>250</th>
</tr>
</thead>
<tbody><tr>
<td>지속시간(가정)</td>
<td>2000ms</td>
<td>2000ms</td>
<td>2000ms</td>
<td>2000ms</td>
</tr>
<tr>
<td>시작숫자</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
</tr>
<tr>
<td>예상 지연시간</td>
<td>0.0025ms</td>
<td>22.5ms</td>
<td>166ms</td>
<td>8ms</td>
</tr>
<tr>
<td>4ms이상?</td>
<td>X</td>
<td>O</td>
<td>O</td>
<td>O</td>
</tr>
<tr>
<td>예상 순위</td>
<td>4</td>
<td>1</td>
<td>1</td>
<td>1</td>
</tr>
<tr>
<td>실제 순위</td>
<td>4</td>
<td>1</td>
<td>1</td>
<td>3</td>
</tr>
</tbody></table>
</li>
</ul>
<p>⇒ 250을 200으로 고쳤을때도 진짜 아주 살짝 늦게 끝나고(10ms) 150으로 고쳐보니 동일하게 끝나는 것을 확인했다. (13ms) 그래서 안전하게 20ms 로 맞추기로 한다. (<strong>100번 나누기 이하일 경우</strong>)</p>
<ul>
<li><p>작성코드</p>
<pre><code class="language-js">// useCountUp.jsx
import { useEffect, useState } from &quot;react&quot;;

/* eslint-disable no-unused-vars */
const useCountUp = (end, start = 0, duration = 2000) =&gt; {
  const [count, setCount] = useState(start);
  useEffect(() =&gt; {
    let currentNum = start;
    if (Math.abs(end) &gt; 99) {
      const delay = 20;
      const countUp = setInterval(() =&gt; {
        currentNum = currentNum + end / (duration / delay);
        setCount(currentNum);
        if (currentNum === end) {
          clearInterval(countUp);
        }
      }, delay);
    } else {
      const delay = Math.abs(Math.floor(duration / end));
      const countUp = setInterval(() =&gt; {
        currentNum++;
        setCount(currentNum);
        if (currentNum === end) {
          clearInterval(countUp);
        }
      }, delay);
    }
  }, [end, start, duration]);
  return count.toFixed(0);
};
export default useCountUp;</code></pre>
</li>
</ul>
</br>


<h3 id="💻두번째-구현-작동화면">💻두번째 구현 작동화면</h3>
<blockquote>
<p>맨처음 숫자를 8000으로 바꾸었을 때 화면이다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/forntweb_tk/post/7966aee0-098a-4513-8285-3c643e933a21/image.gif" alt=""></p>
<ul>
<li>문제점<ul>
<li>8000에서는1의자리 숫자가 전혀 변하지 않는다.</li>
<li>또한 8000이라는 숫자까지는 도달하지만 실제로는 원본과 같이 숫자와 문자가 조합된 800k+라는 숫자를 써야 한다.</li>
</ul>
</li>
</ul>
</br>  

<h2 id="📝세번째-구현">📝세번째 구현</h2>
<h3 id="800k-가-800000팔십만-으로-읽을-수-있도록-변환시켜주자">800k+ 가 800000(팔십만) 으로 읽을 수 있도록 변환시켜주자.</h3>
<ul>
<li>레퍼런스를 참고하여 다음 단위변환 커스텀 훅을 작성하였다.<pre><code class="language-js">//useConvert.jsx
const useConvert = (num, digits) =&gt; {
const si = [
  { value: 1, symbol: &quot;&quot; },
  { value: 1e3, symbol: &quot;k&quot; },
  { value: 1e6, symbol: &quot;M&quot; },
  { value: 1e9, symbol: &quot;G&quot; },
  { value: 1e12, symbol: &quot;T&quot; },
  { value: 1e15, symbol: &quot;P&quot; },
  { value: 1e18, symbol: &quot;E&quot; },
];
const rx = /\.0+$|(\.[0-9]*[1-9])0+$/;
let i;
for (i = si.length - 1; i &gt; 0; i--) {
  if (num &gt;= si[i].value) {
    break;
  }
}
return (num / si[i].value).toFixed(digits).replace(rx, &quot;$1&quot;) + si[i].symbol;
};
</code></pre>
</li>
</ul>
<p>export default useConvert;</p>
<pre><code>
- 그리고 이 훅을 useCounting.jsx에 다음 코드와 같이 적용시켰는데 에러가 발생하였다.
```js
//useCounting.jsx
//생략
  if (count&gt;1000){
    const convertedNum = useConvert(count,0) // 이 부분 에러
  }
  return count.toFixed(0);
};
export default useCountUp;</code></pre><blockquote>
<p><code>React Hook &quot;useConvert&quot; is called conditionally. React Hooks must be called in the exact same order in every component render.</code></p>
</blockquote>
<p>⇒ 리액트 훅은 조건식 안에 사용될 수 없다고 한다. 간과한 리액트 훅의 규칙은 다음과 같다.</p>
<blockquote>
<h4 id="최상위at-the-top-level에서만-hook을-호출해야-합니다">최상위(at the Top Level)에서만 Hook을 호출해야 합니다</h4>
<p><strong>반복문, 조건문 혹은 중첩된 함수 내에서 Hook을 호출하지 마세요.</strong> 대신 early return이 실행되기 전에 항상 React 함수의 최상위(at the top level)에서 Hook을 호출해야 합니다. 이 규칙을 따르면 컴포넌트가 렌더링 될 때마다 항상 동일한 순서로 Hook이 호출되는 것이 보장됩니다. 이러한 점은 React가 <code>useState</code> 와 <code>useEffect</code> 가 여러 번 호출되는 중에도 Hook의 상태를 올바르게 유지할 수 있도록 해줍니다. 이 점에 대해서 궁금하다면 <a href="https://ko.legacy.reactjs.org/docs/hooks-rules.html#explanation">아래</a>에서 자세히 설명해 드리겠습니다.</p>
</blockquote>
<h4 id="오직-react-함수-내에서-hook을-호출해야-합니다">오직 React 함수 내에서 Hook을 호출해야 합니다</h4>
<p><strong>Hook을 일반적인 JavaScript 함수에서 호출하지 마세요.</strong> 대신 아래와 같이 호출할 수 있습니다.</p>
<ul>
<li><p>✅ React 함수 컴포넌트에서 Hook을 호출하세요.</p>
</li>
<li><p>✅ Custom Hook에서 Hook을 호출하세요. (<a href="https://ko.legacy.reactjs.org/docs/hooks-custom.html">다음 페이지</a>에서 이 부분을 살펴볼 예정입니다)
이 규칙을 지키면 컴포넌트의 모든 상태 관련 로직을 소스코드에서 명확하게 보이도록 할 수 있습니다.</p>
</li>
<li><p>수정코드 ⇒ 리액트 훅을 조건문 밖으로 빼냈다.</p>
<pre><code class="language-js">//useCounting.jsx
//생략
const convertedNum = useConvert(count, 0); // 조건문 밖으로 뺌

if (count &gt; 1000) {
  return convertedNum;
} else {
  return count.toFixed(0);
}
};
export default useCountUp</code></pre>
</li>
</ul>
</br>  

<h3 id="💻세번째-구현-작동화면">💻세번째 구현 작동화면</h3>
<p><img src="https://velog.velcdn.com/images/forntweb_tk/post/1d34efee-e727-4e5f-a5b8-0aebfca35d4d/image.gif" alt=""></p>
<ul>
<li>문제점
에러 발생은 사라졌으나 숫자 카운팅이 다음과 같이 일어났다. → <strong>1000단위 위부터는 xk로 세기 시작한다.</strong></li>
</ul>
</br>  

<h2 id="📝네번째-구현">📝네번째 구현</h2>
<h3 id="최종-숫자만-단위변환에-적용되도록-한다">최종 숫자만 단위변환에 적용되도록 한다.</h3>
<pre><code class="language-js">import { useEffect, useState } from &quot;react&quot;;
import useConvert from &quot;./useConvert&quot;;

/* eslint-disable no-unused-vars */
const useCountUp = (end, start = 0, duration = 5000) =&gt; {
  const [count, setCount] = useState(start);

  useEffect(() =&gt; {
        // 아래는 1000 이상인 경우에 대해 새로 작성한 코드
    let currentNum = start;
    if (Math.abs(end) &gt; 1000) {
      const delay = 20;
      const countUp = setInterval(() =&gt; {
        currentNum = currentNum + 950 / (duration / delay);
        setCount(currentNum);
        if (currentNum === 950) {
          clearInterval(countUp);
        }
      }, delay);

    } else if (Math.abs(end) &gt; 99) {
      // (중략)

  const convertedNum = useConvert(end, 0);
  if (count &gt; 949) {
    return convertedNum;
  }

  return count.toFixed(0);
};
export default useCountUp;</code></pre>
</br>  

<h3 id="💻네번째-구현-작동화면최종">💻네번째 구현 작동화면(최종)</h3>
<blockquote>
<p>참고: 페이드인 스타일 적용함</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/forntweb_tk/post/7d5b0ac2-3474-4eb7-b4ac-90fe5524d90f/image.gif" alt=""></p>
</br>

<h2 id="📝구현-완료된-기능">📝구현 완료된 기능</h2>
<ul>
<li>숫자 카운트 업</li>
<li>숫자 크기와 상관없이 1의 자리 숫자 변화가 눈에 보이도록 함</li>
<li>숫자 크기와 상관없이 같은 시각에 종료되도록 함</li>
<li>1000단위 이상의 숫자를 문자로 변환하여 표기</li>
<li>페이드 인 (fade-in) 애니메이션</br>




</li>
</ul>
<h2 id="📝번외--추가-구현해야할-기능">📝번외 : 추가 구현해야할 기능</h2>
<ul>
<li>스크롤이벤트를 이용하여 해당 컴포넌트 도달시 카운트업이 수행되도록 한다. (useScrollCount 구현)</li>
</ul>
</br>



<h2 id="📝마치며">📝마치며</h2>
<ul>
<li>기능을 구현하면서 <code>setInterval</code>을 사용하여 카운트 업 기능을 구현할 때는 <strong>지연간격의 제한</strong>이 있기 때문에 숫자가 얼만큼 크냐에 따라 조건별로 나눠 적용시켜줘야 한다는 것을 알게되었다.</li>
<li>기능을 구현하기 전엔 단순히 숫자가 카운트업 되기만 하면 되어 굉장히 쉬울 것이라고 예상했지만 의외로 한 단계의 과정을 거치면 또 손봐줘야 할 다음 단계가 있었고, 그것을 풀어나가는 과정이 흥미로웠다.</li>
<li>스크롤이벤트를 기능을 추가할때 추가 포스팅을 할 예정이다.</li>
</ul>
</br>


<h4 id="참고-레퍼런스-출처">참고 레퍼런스 출처</h4>
<ul>
<li><a href="https://ko.legacy.reactjs.org/docs/hooks-rules.html">Hook의 규칙</a></li>
<li><a href="https://velog.io/@g_c0916/%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-%EC%97%86%EC%9D%B4-Count-Up-%EA%B8%B0%EB%8A%A5-%EB%A7%8C%EB%93%A4%EA%B8%B0">라이브러리 없이 Count Up 기능 만들기</a></li>
<li><a href="https://shylog.com/react-custom-hooks-scroll-animation-countup/">React Custom Hooks로 scroll animation 만들기 CountUp편</a></li>
<li><a href="https://gist.github.com/peterkimzz/199de0935a66f3506f815a01f25747be">숫자를 k, M 등으로 축약해서 반환하기</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js 마스터리] Next.js페이지 렌더링 -ISR]]></title>
            <link>https://velog.io/@forntweb_tk/Next.js-%EB%A7%88%EC%8A%A4%ED%84%B0%EB%A6%AC-Next.js%ED%8E%98%EC%9D%B4%EC%A7%80-%EB%A0%8C%EB%8D%94%EB%A7%81-ISR</link>
            <guid>https://velog.io/@forntweb_tk/Next.js-%EB%A7%88%EC%8A%A4%ED%84%B0%EB%A6%AC-Next.js%ED%8E%98%EC%9D%B4%EC%A7%80-%EB%A0%8C%EB%8D%94%EB%A7%81-ISR</guid>
            <pubDate>Mon, 22 Jan 2024 12:06:40 GMT</pubDate>
            <description><![CDATA[<h2 id="isr">ISR</h2>
<p>(Incremental Static Regeneration, 증분 정적 재생성)</p>
<ul>
<li><p>일정시간을 주기로 정적페이지를 다시 생성하는 기술</p>
</li>
<li><p>기존 SSG 방식 동작
<img src="https://velog.velcdn.com/images/forntweb_tk/post/b67184e0-6090-48ba-89be-9943122ef006/image.png" alt=""></p>
</li>
<li><p>ISR: 기존 SSG 방식 동작 + SSR 방식의 업데이트
<img src="https://velog.velcdn.com/images/forntweb_tk/post/898a5b49-09bd-4810-ba08-fc4e4d2e9d75/image.png" alt="">
<img src="https://velog.velcdn.com/images/forntweb_tk/post/73dc91f8-8386-4913-b998-00005d377e15/image.png" alt=""></p>
<ul>
<li>이미 만들어져 있는 페이지를 반환하여 매우 빠른 속도로 렌더링 된다. (SSG의 장점)</li>
<li>세팅된 시간 마다 주기적으로 업데이트 되어 최신 데이터를 반영할 수 있다 (SSR의 장점)</li>
</ul>
</li>
<li><p>(다이나믹페이지)<code>[code].js</code>에만 적용가능</p>
<pre><code class="language-js">import { fetchCountry } from &quot;@/api&quot;;
import SubLayout from &quot;@/components/SubLayout&quot;;
import { useRouter } from &quot;next/router&quot;;
</code></pre>
</li>
</ul>
<p>export default function Country({ country }) {
  const router = useRouter();
  const { code } = router.query;</p>
<p>  if (router.isFallback) {
    return <div>Laoding...</div>;
  }</p>
<p>  if (!country) {
    return <div>존재하지 않는 국가입니다.</div>;
  }
  return (
    <div>
      {country.commonName}
      {country.officialName}
    </div>
  );
}</p>
<p>Country.Layout = SubLayout;</p>
<p>export const getStaticPaths = async () =&gt; {
  return {
    paths: [
      { params: { code: &quot;ABW&quot; } },
      { params: { code: &quot;KOR&quot; } },
    ],
    fallback: true,
  };
};</p>
<p>export const getStaticProps = async (context) =&gt; {
  const { code } = context.params;
  console.log(<code>${code}페이지 생성!</code>); // 👈🏻</p>
<p>  let country = null;
  if (code) {
    country = await fetchCountry(code);
  }
  return {
    props: { country },
    revalidate: 3, // 👈🏻 기존SSG페이지에서 이 부분만 추가해주면 된다.
  };
};</p>
<pre><code>
![](https://velog.velcdn.com/images/forntweb_tk/post/a5f33d32-4aa4-4e03-824a-687353f952ca/image.png)

위 페이지에서 설정한 시간(3초)이 지난 후 마다 새로고침을 하는 경우 아래와 같이 서버에서 페이지가 새로 생성되는 것을 알 수 있다.
![](https://velog.velcdn.com/images/forntweb_tk/post/6a64aba6-bfe9-4f5f-95c7-ce2df59a5f7a/image.png)

&gt; 렌더링 전략 (각각페이지마다 다르게 설정 가능)
- 반드시 실시간 데이터만 보장되어야 할 때 → SSR 혹은 클라이언트에서 fetch
- 빠른 사용자 응답이 필요한 경우 → SSG 
- SSG + 최신데이터 반영 → ISR</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js 마스터리] Next.js페이지 렌더링 -SSG]]></title>
            <link>https://velog.io/@forntweb_tk/Next.js-%EB%A7%88%EC%8A%A4%ED%84%B0%EB%A6%AC-Next.js%ED%8E%98%EC%9D%B4%EC%A7%80-%EB%A0%8C%EB%8D%94%EB%A7%81-SSG</link>
            <guid>https://velog.io/@forntweb_tk/Next.js-%EB%A7%88%EC%8A%A4%ED%84%B0%EB%A6%AC-Next.js%ED%8E%98%EC%9D%B4%EC%A7%80-%EB%A0%8C%EB%8D%94%EB%A7%81-SSG</guid>
            <pubDate>Thu, 18 Jan 2024 13:32:00 GMT</pubDate>
            <description><![CDATA[<h2 id="ssg">SSG</h2>
<p>(Static Site Generation, 정적 사이트 생성)</p>
<p>서버측에서 페이지를 <strong>빌드 타임에 한번만</strong> 렌더링 하는 것
<img src="https://velog.velcdn.com/images/forntweb_tk/post/dcf74d5d-ea96-4207-9158-7519551ad983/image.png" alt="">
... 이후의 hydration과정은 SSR과 같음
<img src="https://velog.velcdn.com/images/forntweb_tk/post/eb1dd1b4-d960-4440-9afc-f8f039ee797f/image.png" alt=""></p>
<h3 id="정리">정리</h3>
<ul>
<li>빠른 페이지 응답을 보장할 수 있지만</li>
<li>최신 데이터를 반영하기는 어렵기 때문에</li>
<li>페이지 내부 데이터가 변경되지 않는 페이지에 적절한 렌더링 전략</li>
</ul>
<br/>



<h2 id="실습">실습</h2>
<blockquote>
<p>SSR에서 SSG로 바꾸기</p>
</blockquote>
<h3 id="pagesindexjs">pages/index.js</h3>
<ul>
<li>이전에 사용했던 <code>getServerSideProps</code>에서 <code>getStaticProps</code>로만 바꾸면 된다.<pre><code class="language-js">import { fetchCountries } from &quot;@/api&quot;;
</code></pre>
</li>
</ul>
<p>export default function Home({ countries }) {
  return (
    <div>
      {countries.map((country) =&gt; {
        return <div key={country.code}>{country.commonName}</div>;
      })}
    </div>
  );
}</p>
<p>export const getStaticProps = async () =&gt; { //👈🏻 
  console.log(&quot;countires 데이터 불러옴&quot;); // 👈🏻참고
  const countries = await fetchCountries();
  return {
    props: {
      countries,
    },
  };
};</p>
<pre><code>&gt; **참고: `getStaticProps` 내부에 있는 `console.log` 찍힘**
- 빌드 전 npm run dev에서 작동시킬때는 새로고침을 할때마다 서버측에 내용이 나타나지만, 
- 빌드 이후 npm run start로 작동시키면 서버에 나타나지 않는다.


&lt;br/&gt;


### pages/country/[code].js
- 동적경로는 이 페이지는 위와 같이 단순하게 `getStaticProps`를 사용해서 만들 수 없다.
- 존재하는 경로를 찾아내는 함수가 필요하다.
![](https://velog.velcdn.com/images/forntweb_tk/post/c0a138f9-a274-4429-a6a6-ffb467b3733e/image.png)
![](https://velog.velcdn.com/images/forntweb_tk/post/2a27dc8a-ae3a-4cbe-9d79-8495f6b6a2ee/image.png)

```js
import { fetchCountry } from &quot;@/api&quot;;
import SubLayout from &quot;@/components/SubLayout&quot;;
import { useRouter } from &quot;next/router&quot;;

export default function Country({ country }) {
  // 클라이언트에서 사용
  const router = useRouter();
  const { code } = router.query;

  if (!country) {
    return &lt;div&gt;존재하지 않는 국가입니다.&lt;/div&gt;;
  }
  return (
    &lt;div&gt;
      {country.commonName}
      {country.officialName}
    &lt;/div&gt;
  );
}

Country.Layout = SubLayout;

export const getStaticPaths = async () =&gt; {
  return {
    paths: [
      //어떤경로가 필요한지 명시
      { params: { code: &quot;ABW&quot; } },
      { params: { code: &quot;KOR&quot; } },
    ],
    fallback: true, // 👈🏻paths에 없는 경로 요청이 오는 경우 처리 옵션 (하단설명참고)
  };
};

export const getStaticProps = async (context) =&gt; {
  // 서버측에서 불러옴
  const { code } = context.params;
  let country = null;
  if (code) {
    country = await fetchCountry(code);
  }
  return {
    props: { country },
  };
};
</code></pre><ul>
<li><p>존재하지 않는 경로를 요청하는 경우</p>
<ol>
<li><p><code>fallback: false;</code> : 404페이지
<img src="https://velog.velcdn.com/images/forntweb_tk/post/2760d17a-e5d8-43f8-992e-7a7f4117d37f/image.png" alt=""></p>
<br/>
</li>
<li><p><code>fallback: &quot;blocking&quot;;</code> : 빌드타임에 생성하지 않은 페이지도 실시간으로 생성해서 제공할 수 있다.</p>
</li>
</ol>
<ul>
<li>존재하지 않는 경로를 요청하면 서버에서 해당 페이지를 생성해서 반환 및 저장해둔다.
<img src="https://velog.velcdn.com/images/forntweb_tk/post/3a233a76-e954-4df3-be9c-dcb35f916680/image.png" alt=""></li>
<li>그 이후 같은 페이지를 요청하면 저장해 둔 페이지를 곧바로 제공한다.
<img src="https://velog.velcdn.com/images/forntweb_tk/post/16aabf75-c50e-4b5c-9af8-d33b6bf1fa50/image.png" alt=""><blockquote>
<ul>
<li>특징: (짧은) 로딩 시간이 생김</li>
</ul>
</blockquote>
</li>
</ul>
<br/>

<ol start="3">
<li><code>fallback: true;</code> : 사용자에게 데이터 없는 상태의 페이지를 먼저 보여준 후 데이터를 보냄
<img src="https://velog.velcdn.com/images/forntweb_tk/post/9c0c73ab-d928-48de-8db3-8cb7e7aee195/image.png" alt=""></li>
</ol>
<ul>
<li>지정값 외에 경로 요청시 빈 페이지 (<code>존재하지 않는 국가입니다.</code>)를 먼저 내보냈음을 확인할 수 있다.
<img src="https://velog.velcdn.com/images/forntweb_tk/post/4214debb-6fa9-4583-8a10-e75147a70e6d/image.png" alt=""></li>
<li>그 이후 데이터 값을 보내주었다.
<img src="https://velog.velcdn.com/images/forntweb_tk/post/858185e1-93f7-4300-92b6-70c56bf5273c/image.png" alt=""><blockquote>
<ul>
<li>특징: blocking 방식에 비해 페이지 자체는 빠르게 렌더링되어 사용자 입장에서는 쾌적한 환경이라고 느낄 수 있다.</li>
</ul>
</blockquote>
</li>
</ul>
<blockquote>
<p><strong>참고: router객체를 이용해 현재 페이지가 fallback상태인지 구분 가능</strong></p>
<ul>
<li>컴포넌트 내에 다음 코드 추가 </li>
</ul>
</blockquote>
<pre><code class="language-js">export default function Country({ country }) {
  const router = useRouter();
  const { code } = router.query;
  if (router.isFallback) {    // 👈🏻
    return &lt;div&gt;Laoding...&lt;/div&gt;;
  }
  // ...후략...
}</code></pre>
<p><img src="https://velog.velcdn.com/images/forntweb_tk/post/b6430c25-e40e-4929-9a24-996d5cb97443/image.png" alt=""></p>
</li>
</ul>
<h3 id="pagessearchindexjs">pages/search/index.js</h3>
<ul>
<li><code>pages/country/[code].js</code>처럼 동적경로를 갖는다고 생각하고 <code>getStaticPaths</code>를 사용할 수 없음 
→ 이유1. url parameter 활용하는 페이지에서만 <code>getStaticPaths</code>사용 가능
→ 이유2. <code>getStaticProps</code>함수 내부에서는 url parameter에 접근가능하지만, qeury string에는 접근 불가
(query stirng 값은 너무 랜덤하다. url parameter는 경우의 수가 한정되어 있다.)</li>
</ul>
<blockquote>
<p>※ 클라이언트 사이드에서 직접 불러오기
<strong>fetchSearchResults의 값(검색결과)이 굳이 서버측에서 불러올 필요가 없다면</strong> 컴포넌트 내부에서 useRouter훅을 통해 query stirng만 가져오고, 컴포넌트가 페이지에 마운트 되었을 때 쿼리값을 가지고 컴포넌트 내부에서 api를 호출하여 state에 보관하는 방법이 있다.</p>
</blockquote>
<pre><code class="language-js">import { fetchSearchResults } from &quot;@/api&quot;;
import { useRouter } from &quot;next/router&quot;;
import { useEffect, useState } from &quot;react&quot;;

export default function Search() {
  const router = useRouter();
  const { q } = router.query;
  const [countries, setCountries] = useState([]);

  const setData = async () =&gt; {
    const data = await fetchSearchResults(q); // 👈🏻검색결과 불러오기
    setCountries(data);
  };

  useEffect(() =&gt; {
    if (q) {
      setData();
    }
  }, [q]);

  return (
    &lt;div&gt;
      {countries.map((country) =&gt; {
        return &lt;div key={country.code}&gt;{country.commonName}&lt;/div&gt;;
      })}
    &lt;/div&gt;
  );
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js 마스터리] Next.js페이지 렌더링 -SSR]]></title>
            <link>https://velog.io/@forntweb_tk/Next.js-%EB%A7%88%EC%8A%A4%ED%84%B0%EB%A6%AC-Next.js%ED%8E%98%EC%9D%B4%EC%A7%80-%EB%A0%8C%EB%8D%94%EB%A7%81-SSR</link>
            <guid>https://velog.io/@forntweb_tk/Next.js-%EB%A7%88%EC%8A%A4%ED%84%B0%EB%A6%AC-Next.js%ED%8E%98%EC%9D%B4%EC%A7%80-%EB%A0%8C%EB%8D%94%EB%A7%81-SSR</guid>
            <pubDate>Thu, 18 Jan 2024 10:31:07 GMT</pubDate>
            <description><![CDATA[<h2 id="ssr">SSR</h2>
<p>(Server Side Rendering, 서버사이드렌더링)</p>
<ul>
<li>서버 측에서 페이지를 렌더링 하는 것
<img src="https://velog.velcdn.com/images/forntweb_tk/post/2083dc8d-3a13-4724-b6e0-adbf5aaa5814/image.png" alt=""></li>
</ul>
<ol>
<li><p>브라우저에서 <code>/mypage</code> 페이지 요청</p>
</li>
<li><p>서버에서 페이지에 필요한 데이터를 불러와 완성된 <code>mypage.html</code> 생성</p>
</li>
<li><p>서버에서 클라이언트로 <code>mypage.html</code> 반환</p>
</li>
<li><p>클라이언트는 ㉠렌더링이 완료된 페이지를 화면에 ㉡렌더링 시킴</p>
<blockquote>
<p>㉠렌더링 : 리액트 컴포넌트와 같이 자바스크립트로 작성되어 있는 페이지를 실제 HTML코드로 변환하는 과정을 말함
<img src="https://velog.velcdn.com/images/forntweb_tk/post/b2288d1b-d6aa-4025-9e36-019d827907d6/image.png" alt="">
㉡렌더링 : HTML을 화면(브라우저)에 실제로 그려내는 것을 말함
<img src="https://velog.velcdn.com/images/forntweb_tk/post/8f2fd2ac-a0ea-44d3-b1f9-e6408c2fbb41/image.png" alt=""></p>
</blockquote>
</li>
<li><p>4번 이후 화면 상태는 모든 컨텐츠가 다 그려진 상태이지만 인터렉션은 불가능함 (아직 JS파일을 못 받았음) </p>
</li>
<li><p>서버가 JavaScript Bundle을 보내줌</p>
</li>
<li><p>클라이언트가 JavaScript코드와 HTML요소들을 서로 연결함 (Hydration, 수화)</p>
</li>
<li><p>완성된 웹페이지가 화면에 렌더링</p>
</li>
</ol>
<ul>
<li>전체과정
<img src="https://velog.velcdn.com/images/forntweb_tk/post/a8c49d04-3b35-4064-8bef-df1610a1aaef/image.png" alt=""></li>
</ul>
<br/>



<h3 id="ssr의-특징">SSR의 특징</h3>
<ul>
<li>요청시 서버가 즉각적으로 페이지를 생성해 반환하는 렌더링 전략이다.</li>
<li>CRS에 비해 SEO 측면에서 월등하다.</li>
<li>첫 페이지 로딩속도가 평균적으로 CRS보다 빠르다.</li>
</ul>
<br/>



<h3 id="getserversideprops">getServerSideProps</h3>
<ul>
<li>Next앱에서 특정 페이지를 SSR방식으로 작동시키는 함수 이름</li>
<li>SSR을 위해 서버측에서 페이지 컴포넌트에게 전달할 데이터를 설정하는 함수</li>
<li>반드시 객체를 반환해야함</li>
<li>이 객체는 반드시 <code>props: {}</code>를 포함해야 함</li>
<li><strong>오직 서버에서만 실행됨</strong></li>
<li>이 함수 내에서는 <code>window.location</code> 과 같이 window객체에 접근 불가 (존재하지 않는 값)<pre><code class="language-js">export default function Home({ name }) {
return (
  &lt;div&gt;{ name }&lt;/div&gt;
);
}
</code></pre>
</li>
</ul>
<p>export const getServerSideProps = async () =&gt; {  // 👈🏻SSR 함수
  return {
    props: {
      name: &quot;KOREA&quot;,
    },
  };
};</p>
<pre><code>

- 출력화면
![](https://velog.velcdn.com/images/forntweb_tk/post/debb9d06-86d7-4dc8-8ced-b4e58d703a9d/image.png)


- 개발자도구 Components 확인
![](https://velog.velcdn.com/images/forntweb_tk/post/b0de48ff-4ae1-4311-be69-31a8759e226a/image.png)


- 서버측에서 미리 렌더링 되었음을 확인하는 방법
  1. (브라우저 화면)우 클릭 - 페이지소스보기
![](https://velog.velcdn.com/images/forntweb_tk/post/8ae324ef-8c98-4e08-9fad-1c59c7e19653/image.png)
  2. 개발자도구 - Network - localhost클릭 (요청시 처음 반환한 html파일)
  ![](https://velog.velcdn.com/images/forntweb_tk/post/357d4afa-90f6-4d60-a58e-91ca2d583b4a/image.png)



&lt;br/&gt;



### 서버측 출력 / 클라이언트측 출력
```js
export default function Page() {
  console.log(&quot;2&quot;);   
  useEffect(() =&gt; {   // 브라우저에서만 출력 (컴포넌트 마운트는 서버측에서 발생하지 않음 )
    console.log(&quot;3&quot;); 
  }, []);
  return &lt;div&gt;...&lt;/div&gt;;
}

export const getServerSideProps = async () =&gt; {
  console.log(&quot;1&quot;);   // 서버에서만 출력 (새로고침할때마다)
  return {
    props: {},
  };
};</code></pre><ul>
<li>서버측 콘솔에서 출력되는 콘솔 메세지 : 1, 2</li>
<li>클라이언트측 콘솔에서 출력되는 콘솔 메세지 : 2, 3</li>
</ul>
<blockquote>
<p>2번의 경우 : 서버에서 렌더링하기 위해 한번 실행되고, 그 후 브라우저에서 Hydration과정 끝난 후 한번더 마운트 되어 실행되기 때문에 양쪽에서 출력 된다. (서버 → 브라우저)</p>
</blockquote>
<br/>



<h3 id="query-string-동적-경로">Query String 동적 경로</h3>
<ul>
<li><code>~/search?q={검색어}</code><pre><code class="language-js">export default function Search() {
return &lt;div&gt;...&lt;/div&gt;;
}
</code></pre>
</li>
</ul>
<p>export const getServerSideProps = async (context) =&gt; {
  const { q } = context.query; // 👈🏻Query String으로 전달된 값 q에 접근
  return {
    props: {},
  };
};</p>
<pre><code>

## 실습
![](https://velog.velcdn.com/images/forntweb_tk/post/68dd7664-cb2c-4b41-b3e3-3bb9356ffcb2/image.png)



&lt;br/&gt;



### api.js (기존 사용 코드)
```js
import axios from &quot;axios&quot;;  // ※ axios 라이브러리 설치하기

// 전체 국가 데이터
export async function fetchCountries() {
  try {
    const response = await axios.get(&quot;https://naras-api.vercel.app/all&quot;);
    return response.data;
  } catch (e) {
    return [];
  }
}

// 검색 결과 데이터
export async function fetchSearchResults(q) {
  try {
    const response = await axios.get(`
  https://naras-api.vercel.app/search?q=${q}
  `);

    return response.data;
  } catch (e) {
    return [];
  }
}

// 특정 국가 데이터
export async function fetchCountry(code) {
  try {
    const response = await axios.get(
      `https://naras-api.vercel.app/code/${code}`
    );
    return response.data;
  } catch (e) {
    return null;
  }
}</code></pre><br/>



<h3 id="pagesindexjs">pages/index.js</h3>
<pre><code class="language-js">import { fetchCountries } from &quot;@/api&quot;;

export default function Home({ countries }) {
  return (
    &lt;div&gt;
      {countries.map((country) =&gt; {
        return &lt;div key={country.code}&gt;{country.commonName}&lt;/div&gt;;
      })}
    &lt;/div&gt;
  );
}

export const getServerSideProps = async () =&gt; {
  // API호출 코드가 필요함
  const countries = await fetchCountries();
  return {
    props: {
      countries,
    },
  };
};
</code></pre>
<ul>
<li>출력 화면
<img src="https://velog.velcdn.com/images/forntweb_tk/post/fe956326-2fdd-47f4-9602-6fc7304078f3/image.png" alt=""></li>
<li>컴포넌트 확인
<img src="https://velog.velcdn.com/images/forntweb_tk/post/1e331873-5be7-4a67-a819-65079084bfc9/image.png" alt=""></li>
<li>네트워크 확인
<img src="https://velog.velcdn.com/images/forntweb_tk/post/c90bf03e-f407-4eed-bf51-db7183268575/image.png" alt=""></li>
<li>클라이언트측 네트워크 확인 : 아무것도 없음
<img src="https://velog.velcdn.com/images/forntweb_tk/post/13af0709-b11c-47a3-bb17-de797a3f4a95/image.png" alt=""></li>
</ul>
<br/>




<h3 id="pagessearchindexjs-동적경로-사용">pages/search/index.js (동적경로 사용)</h3>
<pre><code class="language-js">import { fetchSearchResults } from &quot;@/api&quot;;
import SubLayout from &quot;@/components/SubLayout&quot;;

export default function Search({ countries }) {
  return (
    &lt;div&gt;
      {countries.map((country) =&gt; {
        return &lt;div key={country.code}&gt;{country.commonName}&lt;/div&gt;;
      })}
    &lt;/div&gt;
  );
}

Search.Layout = SubLayout;

export const getServerSideProps = async (context) =&gt; { // 👈🏻 매개변수 이용
  //1.검색결과 api호출
  //2.props리턴
  const { q } = context.query; // 쿼리스트링 꺼내오기
  let countries = [];
  if (q) {
    countries = await fetchSearchResults(q);
  }
  return {
    props: { countries },
  };
};
</code></pre>
<ul>
<li>컴포넌트 확인
<img src="https://velog.velcdn.com/images/forntweb_tk/post/b4581e47-4f57-4d27-a5ec-168395a8f691/image.png" alt=""></li>
<li>네트워크 확인
<img src="https://velog.velcdn.com/images/forntweb_tk/post/2858a57f-1504-4e9b-8420-f72b59201dac/image.png" alt=""></li>
</ul>
<br/>



<h3 id="pagescountrycodejs">pages/country/[code].js</h3>
<pre><code class="language-js">import { fetchCountry } from &quot;@/api&quot;;
import SubLayout from &quot;@/components/SubLayout&quot;;
import { useRouter } from &quot;next/router&quot;;

export default function Country({ country }) {
  // 클라이언트에서 사용
  const router = useRouter();
  const { code } = router.query;
  return (
    &lt;div&gt;
      {country.commonName}
      {country.officialName}
    &lt;/div&gt;
  );
}

Country.Layout = SubLayout;

export const getServerSideProps = async (context) =&gt; {
  // 서버측에서 불러옴
  const { code } = context.params; //url parameter
  let country = null;
  if (code) {
    country = await fetchCountry(code);
  }
  return {
    props: { country },
  };
};
</code></pre>
<ul>
<li>출력 화면
<img src="https://velog.velcdn.com/images/forntweb_tk/post/32ebbac3-cc82-4737-b2f4-cf2c951f5dfb/image.png" alt=""></li>
</ul>
<blockquote>
<p><strong>URL parameter 불러오는 방식 차이 이해하기</strong></p>
</blockquote>
<ul>
<li>클라이언트측에서 이용하는 경우 : 컴포넌트 내부에서 불러옴
(브라우저에서 hydration과정이 일어난 뒤에 동작)<pre><code class="language-js">const router = useRouter(); // 리액트훅을 사용
const { code } = router.query;</code></pre>
</li>
<li>서버측에서 이용하는 경우 : context객체에서 불러옴<pre><code class="language-js">const { code } = context.params;</code></pre>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js 마스터리] 
Next.js페이지 라우터]]></title>
            <link>https://velog.io/@forntweb_tk/Next.js-%EB%A7%88%EC%8A%A4%ED%84%B0%EB%A6%AC-Next.js%ED%8E%98%EC%9D%B4%EC%A7%80-%EB%9D%BC%EC%9A%B0%ED%84%B0</link>
            <guid>https://velog.io/@forntweb_tk/Next.js-%EB%A7%88%EC%8A%A4%ED%84%B0%EB%A6%AC-Next.js%ED%8E%98%EC%9D%B4%EC%A7%80-%EB%9D%BC%EC%9A%B0%ED%84%B0</guid>
            <pubDate>Thu, 18 Jan 2024 09:17:03 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/forntweb_tk/post/54df9a3b-faed-4c3e-bddb-dd338e548dde/image.png" alt=""></p>
<ul>
<li><p>요청 : <code>/</code> 
👉 폴더: <code>pages/index.js</code></p>
</li>
<li><p>요청 : <code>/search</code> 
👉 폴더: <code>pages/search.js</code></p>
<ul>
<li>라우트별로 폴더 분리가능</li>
</ul>
<p><img src="https://velog.velcdn.com/images/forntweb_tk/post/63762235-ea13-48be-86bf-ec53ef7d8427/image.png" alt=""></p>
</li>
<li><p>요청 : 동적인 경로1 <code>/country/{code}</code> 
👉 폴더: <code>pages/country/[code].js</code></p>
</li>
<li><p>요청 : 동적인 경로2 <code>/country/{code}/{year}</code> 
👉 폴더: <code>pages/country/[...code].js</code> (Catch-All)
<img src="https://velog.velcdn.com/images/forntweb_tk/post/72f49adc-68a3-4662-ad36-75b386405088/image.png" alt=""></p>
</li>
<li><p>요청 : 동적인 경로3 <code>/country/{code?}</code>(url파라미터가 없는 경우) 
👉 폴더: <code>pages/country/[[...code]].js</code> (Optional Catch-All)
<img src="https://velog.velcdn.com/images/forntweb_tk/post/94570ad6-5275-4591-9056-6a2b66affbc0/image.png" alt=""> </p>
</li>
</ul>
<br/>




<h3 id="nextjs의-페이지-라우터-특징">Next.js의 페이지 라우터 특징</h3>
<ul>
<li>페이지 라우터는 pages 폴더의 구조를 기반으로 페이지 라우팅을 제공한다.</li>
<li>페이지 라우터는 동적 경로 또한 쉽게 대응이 가능하다.</li>
<li>페이지 라우터를 이용하면 pages폴더의 구조 배치 이외에 별다른 라우팅 코드를 작성하지 않아도 된다.</li>
<li>페이지 라우터는 Next13버전(App 라우터)에서도 기본적으로 제공된다.</li>
</ul>
<br/>




<h3 id="동적경로-생성-방법">동적경로 생성 방법</h3>
<ul>
<li>&quot;~/post/{포스트 아이디}&quot; → <code>/pages/post/[post_id].js</code></li>
</ul>
<p>-<code>[post_id].js</code>페이지</p>
<pre><code class="language-js">import { useRouter } from &quot;next/router&quot;;

export function PostDetail() {
  const router = useRouter();

  const { code } = router.query; // 👈🏻URL Parameter로 전달된 post_id에 접근

  return &lt;div&gt;&lt;/div&gt;;
}</code></pre>
<br/>




<h3 id="csr-방식-페이지-이동">CSR 방식 페이지 이동</h3>
<ul>
<li><code>&lt;Link href=&quot;경로&quot; /&gt;</code> (※ href ※)</li>
</ul>
<pre><code class="language-js">//index.js
import Link from &quot;next/link&quot;;

export default function Home() {
  const code = &quot;KOR&quot;;
  return (
    &lt;div&gt;
      Home Page
      &lt;div&gt;
        &lt;Link href={&quot;/search&quot;}&gt;Search Page 이동&lt;/Link&gt; // 👈🏻 일반문자 형태
      &lt;/div&gt;
      &lt;div&gt;
        &lt;Link
          href={{  // 👈🏻 url오브젝트 형태 (pathname, query)
            pathname: &quot;/country/[code]&quot;, 
            query: { code: code },
          }}
        &gt;
          {code}페이지로 이동
        &lt;/Link&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}
</code></pre>
<blockquote>
<p><strong>※ react router와 헷갈리지 않기</strong>
<code>&lt;Link to=&quot;&quot;&gt;</code></p>
</blockquote>
<br/>




<h3 id="버튼-클릭-페이지-이동">버튼 클릭 페이지 이동</h3>
<pre><code class="language-js">export function PageA() {
  const router = useRouter();
  const onClick = () =&gt; {
    router.push(&quot;/&quot;) // 👈🏻 push 함수사용
  };
  return (
    &lt;button onClick={onClick}&gt;인덱스 페이지로 이동&lt;/button&gt;
  );
}</code></pre>
<blockquote>
<p>참고</p>
</blockquote>
<ul>
<li><code>router.replace()</code> : 뒤로가기 방지하면서 페이지 이동</li>
<li><code>router.back()</code> : 뒤로가기</li>
<li><code>router.reload()</code> : 새로고침</li>
</ul>
<br/>




<h3 id="페이지-별-레이아웃-적용하기">페이지 별 레이아웃 적용하기</h3>
<ul>
<li><p><code>./pages/_app.js</code></p>
<pre><code class="language-js">export default function App({ Component, pageProps }) {
const EmptyLayout = ({ children }) =&gt; &lt;&gt;{children}&lt;/&gt;;
const Layout = Component.Layout || EmptyLayout;

return (
  &lt;Layout&gt;
    &lt;Component {...pageProps} /&gt;
  &lt;/Layout&gt;
);
}</code></pre>
</li>
<li><p><code>./pages/post.js</code></p>
<pre><code class="language-js">export function Post() {
return &lt;div&gt;...&lt;/div&gt;;
}
Post.Layout = ({ children }) =&gt; &lt;main&gt;{children}&lt;/main&gt;; // 👈🏻</code></pre>
</li>
<li><p><code>./pages/about.js</code></p>
<pre><code class="language-js">export function About() {
return &lt;div&gt;...&lt;/div&gt;;
}</code></pre>
</li>
<li><ul>
<li><code>/post</code>페이지는 <code>&lt;main&gt;</code>태그로 감싸져 렌더링된다.</li>
<li><code>/about</code>페이지는 그대로 렌더링 된다.</li>
<li>App컴포넌트의 Layout변수에는 <code>/post</code>페이지일 경우 <code>&lt;main&gt;</code>태그로 페이지를 감싸는 레이아웃이 적용되고 <code>/about</code>페이지일 경우 아무런 레이아웃도 적용되지 않는다.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js마스터리] Next.js 소개]]></title>
            <link>https://velog.io/@forntweb_tk/Next.js%EB%A7%88%EC%8A%A4%ED%84%B0%EB%A6%AC-Next.js-%EC%86%8C%EA%B0%9C</link>
            <guid>https://velog.io/@forntweb_tk/Next.js%EB%A7%88%EC%8A%A4%ED%84%B0%EB%A6%AC-Next.js-%EC%86%8C%EA%B0%9C</guid>
            <pubDate>Wed, 17 Jan 2024 09:55:21 GMT</pubDate>
            <description><![CDATA[<h2 id="nextjs">Next.js</h2>
<ul>
<li>Vercel이 개발한 오픈소스 자바스크립트 <em>프레임워크</em></li>
<li>React.js에 다양한 기능을 제공하는 확장판</li>
</ul>
<h3 id="프레임워크-vs-라이브러리">프레임워크 vs 라이브러리</h3>
<table>
<thead>
<tr>
<th></th>
<th>프레임워크</th>
<th>라이브러리</th>
</tr>
</thead>
<tbody><tr>
<td>기능 구현의 주도권</td>
<td>프레임워크가 주도권을 가짐</td>
<td>프로그래머가 주도권을 가짐</td>
</tr>
<tr>
<td>ex)페이지 라우팅을 구현해야한다면...</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>톱, 망치 (도구)</td>
</tr>
</tbody></table>
<h3 id="서버사이드렌더링">서버사이드렌더링</h3>
<p><img src="https://velog.velcdn.com/images/forntweb_tk/post/7a21630b-2185-4dcc-89cb-9a673d02d09b/image.png" alt=""></p>
<ul>
<li>CSR방식으로 동장하는 리액트 앱의 단점
<img src="https://velog.velcdn.com/images/forntweb_tk/post/1a5cffef-8648-4db2-8b6b-d4dca0948168/image.png" alt="">
<img src="https://velog.velcdn.com/images/forntweb_tk/post/51a77604-6662-4e73-b239-99785fd4c522/image.png" alt="">
<img src="https://velog.velcdn.com/images/forntweb_tk/post/3cf937c3-16b6-4db5-ad14-bf13b55a5b77/image.png" alt=""></li>
</ul>
<ul>
<li>CSR은 검색 엔진 최적화(SEO)에 불리함
: 검색엔진이 사이트에서 정보를 수집할 때 가장 먼저 받는 html파일을 저장해둠 → 리액트로 개발한 서비스에서는 <strong>빈껍데기 html파일을 받음</strong></li>
</ul>
<blockquote>
<p><strong>검색 엔진 최적화(SEO)</strong>
(Search Engine Optimization)
구글, 네이버, 다음 등의 검색 포털 서비스의 검색 결과에 더 잘 노출되게 하는 것</p>
</blockquote>
<ul>
<li>SSR과 CRS를 동시에 제공하는 NEXT.js
<img src="https://velog.velcdn.com/images/forntweb_tk/post/56b1d91c-4613-4a20-aabc-1acada42f3c9/image.png" alt="">
<img src="https://velog.velcdn.com/images/forntweb_tk/post/ef01180e-c736-4d32-8e6c-a10e1679e40c/image.png" alt=""></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js마스터리] React 소개]]></title>
            <link>https://velog.io/@forntweb_tk/Next.js%EB%A7%88%EC%8A%A4%ED%84%B0%EB%A6%AC-React-%EC%86%8C%EA%B0%9C</link>
            <guid>https://velog.io/@forntweb_tk/Next.js%EB%A7%88%EC%8A%A4%ED%84%B0%EB%A6%AC-React-%EC%86%8C%EA%B0%9C</guid>
            <pubDate>Tue, 09 Jan 2024 12:22:03 GMT</pubDate>
            <description><![CDATA[<h2 id="reactjs">React.js</h2>
<p><img src="https://velog.velcdn.com/images/forntweb_tk/post/cf3fb53f-3793-4509-919d-250d78a5f93c/image.png" alt=""></p>
<ul>
<li>Meta에서 만든 오픈소스 자바스크립트 라이브러리</li>
</ul>
<h3 id="1-컴포넌트-기반의-ui-표현">1. 컴포넌트 기반의 UI 표현</h3>
<p>페이지에 공통적으로 사용되는 (자주 중복되는) UI요소들은 컴포넌트 단위로 모듈화를 시킨다.
<img src="https://velog.velcdn.com/images/forntweb_tk/post/b64949ab-7285-4775-8b46-20622338a7b9/image.png" alt="">
<img src="https://velog.velcdn.com/images/forntweb_tk/post/f7b6f2ab-41e1-4c54-93e3-85d9823dcaca/image.png" alt=""></p>
<h3 id="2-빠르고-편리한-업데이트-구현">2. 빠르고 편리한 업데이트 구현</h3>
<p>웹에서의 업데이트는 곧 상호작용(interaction)을 의미함
→ 사용자의 행동(버튼클릭, 메세지 입력 등)에 웹이 실시간으로 반응하는 것</p>
<p>한 페이지 내에서도 여러 정보를 얻을 수 있게 됨</p>
<h2 id="virtual-dom">Virtual DOM</h2>
<h3 id="웹-브라우저의-렌더링-과정">웹 브라우저의 렌더링 과정</h3>
<p>(Critical Rendering Path)
<img src="https://velog.velcdn.com/images/forntweb_tk/post/994588b9-0053-442a-bf75-3fc1cb6c5c02/image.png" alt=""></p>
<ol>
<li>계층화
<img src="https://velog.velcdn.com/images/forntweb_tk/post/6e7c9960-9c07-4ad7-96d5-98117b594d26/image.png" alt=""></li>
</ol>
<ul>
<li>브라우저가 오른쪽과 같이 해석하기 편한 형태로 계층화 시킨다.
<img src="https://velog.velcdn.com/images/forntweb_tk/post/b49b0403-db7d-467c-9a6b-0e0b7e48452e/image.png" alt=""></li>
</ul>
<ol start="2">
<li><p>Render Tree
<img src="https://velog.velcdn.com/images/forntweb_tk/post/3eb42205-2108-4fb5-b761-58dc67d3f250/image.png" alt=""></p>
</li>
<li><p>Layout
<img src="https://velog.velcdn.com/images/forntweb_tk/post/3835f8fb-44a3-441c-8ddc-70b49d9167e0/image.png" alt=""></p>
</li>
<li><p>Painting
<img src="https://velog.velcdn.com/images/forntweb_tk/post/d3daf338-a1c1-49c3-9384-d35bf66281c3/image.png" alt=""></p>
</li>
</ol>
<h3 id="웹-브라우저가-화면을-업데이트-하는-과정">웹 브라우저가 화면을 업데이트 하는 과정</h3>
<ul>
<li><p>JavaScript의 DOM 수정
<img src="https://velog.velcdn.com/images/forntweb_tk/post/d30c177e-fb01-461e-8f7b-5139f4933f6f/image.png" alt=""></p>
</li>
<li><p>업데이트 시 주의사항 : Reflow, Repaint 최소화 필수
<img src="https://velog.velcdn.com/images/forntweb_tk/post/435719c0-75eb-48e9-98b6-253e78aa0a54/image.png" alt=""></p>
<ul>
<li><p>나쁜코드
<img src="https://velog.velcdn.com/images/forntweb_tk/post/2f0c3bb3-5d00-4342-9a15-11afbf39dbd8/image.png" alt=""></p>
</li>
<li><p>좋은코드
<img src="https://velog.velcdn.com/images/forntweb_tk/post/71be4e5c-a79b-48c0-b53e-ee058c63c61b/image.png" alt=""></p>
</li>
</ul>
</li>
<li><p>따라서 동시에 일어나는 업데이트를 최대한 모아서 시켜줘야하는데, 이것은 서비스 규모가 커질수록 점점 어려워진다. → 해결방안: Virtual DOM
<img src="https://velog.velcdn.com/images/forntweb_tk/post/9b3bcd59-7435-4d29-8c26-e128d290cbc3/image.png" alt=""></p>
</li>
</ul>
<h2 id="vite로-react-시작하기">Vite로 react 시작하기</h2>
<ul>
<li>react를 사용하기 위해서는 다음과 같은 단계를 거쳐야한다.</li>
</ul>
<ol>
<li>Node.js 패키지 생성하기 : <code>npm init</code></li>
<li>리액트 설치하기 : <code>npm install react</code></li>
<li>기타 도구들 설치하고 설정하기 (입문자에게 권장하기 어려움)</li>
</ol>
<p>따라서 위의 복잡한 단계들을 한번에 처리해주는 도구 Vite를 사용한다.
vite: 세팅된 react앱을 자동으로 생성해준다.</p>
<ol>
<li>vite 명령어<pre><code>npm create vite@latest</code></pre></li>
<li>프로젝트 이름 설정</li>
<li>프레임워크 설정</li>
<li>언어 설정
<img src="https://velog.velcdn.com/images/forntweb_tk/post/9fdc6c7b-8a84-464f-b3fe-e7bbc5500ac0/image.png" alt=""></li>
</ol>
<h3 id="packagejson-살펴보기">package.json 살펴보기</h3>
<p><img src="https://velog.velcdn.com/images/forntweb_tk/post/d886205b-bbae-4aa5-87d1-906cb6b85de3/image.png" alt=""></p>
<ul>
<li><p><code>dependencies</code> : 패키지 안에 어떤 라이브러리들이 설치되어 있는가
※ <code>npm install</code>로 라이브러리 설치</p>
</li>
<li><p><code>devDependencies</code> : <code>dependencies</code>와 같이 어떤 라이브러리들이 필요한지 명시하지만, 실제로 프로젝트를 build해서 서버에 올릴 필요가 없는 않은 개발용 라이브러리</p>
</li>
</ul>
<h3 id="node_modules-살펴보기">node_modules 살펴보기</h3>
<p>react앱을 만들기 위해 많은 라이브러리들이 필요함.
<img src="https://velog.velcdn.com/images/forntweb_tk/post/3ff91aa9-0098-4705-bf4e-231b009ee974/image.png" alt=""></p>
<h3 id="public폴더-살펴보기">public폴더 살펴보기</h3>
<p>이미지, 폰트, 동영상 등 코드가 아닌 정적인 파일 보관
<img src="https://velog.velcdn.com/images/forntweb_tk/post/b78f1e1e-ab02-4381-8ff4-cbfdc12691f3/image.png" alt=""></p>
<h3 id="src폴더-살펴보기">src폴더 살펴보기</h3>
<pre><code>src
ㄴ assets
   ㄴ react.svg
ㄴ App.css
ㄴ App.jsx
ㄴ index.css
ㄴ main.jsx</code></pre><ul>
<li><code>assets</code>폴더 : 소스코드에서 직접 접근해서 사용하는 이미지나 폰트를 보관하는 폴더
<img src="https://velog.velcdn.com/images/forntweb_tk/post/61dc270c-d60d-4d2f-8a20-55ba1907b7a8/image.png" alt=""></li>
</ul>
<h3 id="그-외">그 외</h3>
<ul>
<li><code>.eslintrc.cjs</code> : eslint도구의 옵션 설정 (개발자들간의 코드스타일 통일)</li>
<li><code>.gitignore</code> : github에 올리지 않을 내용</li>
<li><code>index.html</code> : react파일의 틀 역할 (여기에 직접 코드를 입력하지 않음, id=&quot;root&quot;의 div아래 추가)</li>
<li><code>vite.config.js</code> : vite도구의 옵션 설정
<img src="https://velog.velcdn.com/images/forntweb_tk/post/3bb68d72-249f-4abb-bb2b-c2c5e0f16635/image.png" alt=""></li>
</ul>
<h3 id="vite-실행">vite 실행</h3>
<pre><code>npm run dev</code></pre><p><img src="https://velog.velcdn.com/images/forntweb_tk/post/ac41c1db-a086-4aef-bb0e-ae646d88f184/image.png" alt=""></p>
<h2 id="리액트-앱-동작원리">리액트 앱 동작원리</h2>
<blockquote>
<p>리액트 앱 실행시 나오는 localhost 주소는 어떻게 생성된걸까?</p>
</blockquote>
<ul>
<li>도메인 : 고유한 주소 (ex) <a href="https://naver.com">https://naver.com</a>)
<img src="https://velog.velcdn.com/images/forntweb_tk/post/972726dd-79c6-4dee-ac24-f4b92cf80f5c/image.png" alt="">
<img src="https://velog.velcdn.com/images/forntweb_tk/post/bfae7784-de38-4864-88e9-ea90331febf9/image.png" alt=""></li>
</ul>
<h3 id="httpslocalhost5173"><code>https://localhost:5173</code></h3>
<p><img src="https://velog.velcdn.com/images/forntweb_tk/post/e968be32-ad44-42d6-b9ae-db5eea95de4f/image.png" alt=""></p>
<ul>
<li><code>localhost</code> : 우리의 컴퓨터를 지칭하는 주소</li>
<li><code>5173</code> : 포트번호</li>
<li>여러개의 서버 동작 예시
<img src="https://velog.velcdn.com/images/forntweb_tk/post/c2d779c9-c8ec-422e-b65c-9e22f4b5806b/image.png" alt=""></li>
</ul>
<p>※ 내 컴퓨터가 아닌 다른 컴퓨터에서는 접속하지 못함</p>
<h3 id="리액트가-화면-요소를-렌더링-하는-방법">리액트가 화면 요소를 렌더링 하는 방법</h3>
<ol>
<li>웹 서버에서 특정 포트번호(5173)에 요청</li>
<li>html파일을 브라우저에 보내줌
<img src="https://velog.velcdn.com/images/forntweb_tk/post/cfce94ca-494b-440b-a2ab-a16a1325206e/image.png" alt=""></li>
</ol>
<ul>
<li>하지만 <code>&lt;body&gt;</code>태그 안에는 특별한 요소가 없음</li>
<li>사실 <code>&lt;body&gt;</code>태그 안의<code>&lt;script&gt;</code> 태그가 실행한 자스 코드가 동적으로 생성한 요소들이다.</li>
</ul>
<ol start="3">
<li>src/main.jsx 파일
<img src="https://velog.velcdn.com/images/forntweb_tk/post/bf3a2454-255b-4ec1-9d5f-ea1c8e1ddd66/image.png" alt=""></li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js마스터리] Node.js 소개]]></title>
            <link>https://velog.io/@forntweb_tk/Next.js%EB%A7%88%EC%8A%A4%ED%84%B0%EB%A6%AC-Node.js-%EC%86%8C%EA%B0%9C</link>
            <guid>https://velog.io/@forntweb_tk/Next.js%EB%A7%88%EC%8A%A4%ED%84%B0%EB%A6%AC-Node.js-%EC%86%8C%EA%B0%9C</guid>
            <pubDate>Mon, 08 Jan 2024 13:01:22 GMT</pubDate>
            <description><![CDATA[<h2 id="nodejs-소개">Node.js 소개</h2>
<ul>
<li><p>React, Next는 모두 Node.js를 기반으로 구동되는 라이브러리(프레임워크)이다.</p>
</li>
<li><p>Node.js는 JavaScript의 실행환경(Run Time) = JavaScript의 구동기
<img src="https://velog.velcdn.com/images/forntweb_tk/post/d8cedd2c-bea9-41bc-ad1b-dfc5f77af9f5/image.png" alt=""></p>
<ul>
<li>JavaScript은 생산성을 중심에 두고 만들어진 언어이다. 따라서 매우 유연하게 설계되었다.</li>
<li>원래는 웹 내에서만 작동되던 언어인데 웹 밖에서도 프로그램을 만들고 싶어져서 Node.js를 사용해 가능해졌다.</li>
</ul>
</li>
</ul>
<blockquote>
<h3 id="npm">npm</h3>
<p>(node package manager)</p>
</blockquote>
<ul>
<li><p>npm은 Node.js와 함께 설치되는 Node.js의 패키지를 관리해주는 도구이다.</p>
</li>
<li><p><code>npm init</code>
<img src="https://velog.velcdn.com/images/forntweb_tk/post/92d20f18-f003-49a9-a219-00a55998c8f0/image.png" alt=""></p>
</li>
</ul>
<ul>
<li><code>npm run start</code>
<img src="https://velog.velcdn.com/images/forntweb_tk/post/c48178bf-b30b-481f-874a-c5cd324a9fdb/image.png" alt=""></li>
</ul>
<h3 id="모듈시스템">모듈시스템</h3>
<p><img src="https://velog.velcdn.com/images/forntweb_tk/post/e9f2ef9b-f8a0-46eb-95d6-4c2793e82b68/image.png" alt=""></p>
<ul>
<li><p>파일을 두개 이상으로 분리할때 모듈시스템을 사용한다.</p>
</li>
<li><p><code>CJS</code> :모듈내장객체 이용
<img src="https://velog.velcdn.com/images/forntweb_tk/post/cef9b305-b4bc-4b6f-a29e-05fc27d0c17d/image.png" alt=""></p>
</li>
</ul>
<ul>
<li>index.js<pre><code class="language-js">const moduleData = require(&quot;./math&quot;); // 👈내장함수 사용
</code></pre>
</li>
</ul>
<p>console.log(moduleData.add(1, 2)); // 3
console.log(moduleData.sub(1, 2)); // -1</p>
<pre><code>
- math.js
```js
function add(a,b) {
  return a + b;
}

function sub(a,b) {
  return a - b;
}

module.exports = { // 👈내장객체 사용
  add,
  sub,
};</code></pre><p>객체가 저장되므로 구조분해 할당으로 사용 가능</p>
<ul>
<li>index.js<pre><code class="language-js">const {add, sub} = require(&quot;./math&quot;);
</code></pre>
</li>
</ul>
<p>console.log(add(1, 2)); // 3
console.log(sub(1, 2)); // -1</p>
<pre><code>

- `ESM` : package.json에서 다음과 같이 추가해야 함 → CJS는 더이상 사용하지 못함
![](https://velog.velcdn.com/images/forntweb_tk/post/0680e731-4e83-49e7-9d88-00bf04337f4c/image.png)

- index.js
```js
import { add, sub } from &quot;./math.js&quot;; // 👈

console.log(add(1, 2)); // 3
console.log(sub(1, 2)); // -1</code></pre><blockquote>
<p>주의 : 확장자를 꼭 입력해주어야 한다.</p>
</blockquote>
<ul>
<li>math.js<pre><code class="language-js">function add(a,b) {
return a + b;
}
</code></pre>
</li>
</ul>
<p>function sub(a,b) {
  return a - b;
}</p>
<p>export = { add, sub }; // 👈</p>
<pre><code>
다음과 같이 함수선언문 앞에 export 가능
- math.js
```js
export function add(a,b) {
  return a + b;
}

export function sub(a,b) {
  return a - b;
}</code></pre><ul>
<li><p><code>defalut</code> 명령어를 사용하여 대표하는 기본값을 설정할 수 있다.</p>
</li>
<li><p>index.js</p>
<pre><code class="language-js">import mul, { add, sub } from &quot;./math.js&quot;; // 👈대표값은 이름변경 가능
</code></pre>
</li>
</ul>
<p>console.log(add(1, 2)); // 3
console.log(sub(1, 2)); // -1
console.log(mul(10, 20)); // 200</p>
<pre><code>
- math.js
```js
export function add(a,b) {
  return a + b;
}

export function sub(a,b) {
  return a - b;
}

export default function multiply(a,b) {
  return a * b;
}</code></pre><h2 id="nodejs-라이브러리">Node.js 라이브러리</h2>
<ul>
<li>라이브러리 : 프로그램을 개발할 때 필요한 다양한 기능을 미리 만들어 모듈화 해놓은 것</li>
</ul>
<p><img src="https://velog.velcdn.com/images/forntweb_tk/post/6fbdf918-70dc-4325-ba29-f6d5c5c87498/image.png" alt=""></p>
<ul>
<li><a href="https://npmjs.com">라이브러리 다운로드</a></li>
</ul>
<p><img src="https://velog.velcdn.com/images/forntweb_tk/post/976f55cf-442d-47d1-955f-5b96c63c3b3d/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[한입] 맵드타입 기반 유틸리티 타입들2]]></title>
            <link>https://velog.io/@forntweb_tk/%ED%95%9C%EC%9E%85-%EB%A7%B5%EB%93%9C%ED%83%80%EC%9E%85-%EA%B8%B0%EB%B0%98-%EC%9C%A0%ED%8B%B8%EB%A6%AC%ED%8B%B0-%ED%83%80%EC%9E%85%EB%93%A42</link>
            <guid>https://velog.io/@forntweb_tk/%ED%95%9C%EC%9E%85-%EB%A7%B5%EB%93%9C%ED%83%80%EC%9E%85-%EA%B8%B0%EB%B0%98-%EC%9C%A0%ED%8B%B8%EB%A6%AC%ED%8B%B0-%ED%83%80%EC%9E%85%EB%93%A42</guid>
            <pubDate>Fri, 22 Dec 2023 08:36:56 GMT</pubDate>
            <description><![CDATA[<h3 id="✏️pickt-k">✏️<code>Pick&lt;T, K&gt;</code></h3>
<ul>
<li>객체타입으로부터 특정 프로퍼티만 딱 골라내는 타입<pre><code class="language-ts">interface Post {
title: string;
tags: string[];
content: string;
thumbnailURL?: string;
}
</code></pre>
</li>
</ul>
<p>// 직접 구현
type Pick&lt;T, K extends keyof T&gt; = {
    // K extends &#39;title&#39;|&#39;tags&#39;|&#39;content&#39;|&#39;thumbnailURL&#39; 
    // number extends &#39;title&#39;|&#39;tags&#39;|&#39;content&#39;|&#39;thumbnailURL&#39; ❌
  <a href="V;">key in K</a>: T[key];
};</p>
<p>const legacyPost: Pick&lt;Post, &quot;title&quot; | &quot;content&quot;&gt; = {
  title: &quot;옛날 글&quot;,
  content: &quot;옛날 컨텐츠&quot;,
};</p>
<pre><code>


&lt;br/&gt;



### ✏️`Pick&lt;T, K&gt;`
- 객체타입으로부터 특정 프로퍼티만 딱 골라내는 타입
```ts
interface Post {
  title: string;
  tags: string[];
  content: string;
  thumbnailURL?: string;
}

// 직접구현
type Omit&lt;T, K extends keyof T&gt; = Pick&lt;T, Exclude&lt;keyof T, K&gt;&gt;;
// T = Post, K = &#39;title&#39;
// Pick&lt;Post, Exclude&lt;keyof Post, &#39;title&#39;&gt;&gt;
// Pick&lt;Post, Exclude&lt;&#39;title&#39;|&#39;tags&#39;|&#39;content&#39;|&#39;thumbnailURL&#39;, &#39;title&#39;&gt;&gt;
// Pick&lt;Post, &#39;tags&#39;|&#39;content&#39;|&#39;thumbnailURL&#39;&gt;


// 제목이 없는 글
const noTitlePost: Omit&lt;Post, &quot;title&quot;&gt; = {
  content: &quot;&quot;,
  tags: [],
  thumbnailURL: &quot;&quot;,
};</code></pre><br/>



<h3 id="✏️recordk-v">✏️<code>Record&lt;K, V&gt;</code></h3>
<ul>
<li><p>다음과 같이 버전마다 같은 프로퍼티객체를 갖는다면, 그 버전이 추가되거나 프로퍼티 이름이 바뀔때 일일히 수정하기 번거롭다. (+코드중복)</p>
<pre><code class="language-ts">type ThumbnailLegacy = {
large: {
  url: string;
};
medium: {
  url: string;
};
small: {
  url: string;
};
watch: {
  url: string;
};
...
};</code></pre>
</li>
<li><p>다음의 유틸리티 타입을 사용할 수 있다.</p>
<pre><code class="language-ts">// 직접구현
type Record&lt;K extends keyof any, V&gt; = {</code></pre>
</li>
</ul>
<p>};</p>
<p>type Thumbnail = Record&lt;
  &quot;large&quot; | &quot;medium&quot; | &quot;small&quot; | &quot;watch&quot;,
  { url: string }</p>
<blockquote>
<p>;</p>
</blockquote>
<pre><code>![](https://velog.velcdn.com/images/forntweb_tk/post/94b3c467-8add-4810-9801-42d720b3763a/image.png)

→ 실무에서 많이 사용된다.
→ 동일한 패턴을 갖는 객체 타입을 쉽게 정의할 수 있다.
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[한입] 맵드타입 기반 유틸리티 타입들]]></title>
            <link>https://velog.io/@forntweb_tk/%ED%95%9C%EC%9E%85-%EB%A7%B5%EB%93%9C%ED%83%80%EC%9E%85-%EA%B8%B0%EB%B0%98-%EC%9C%A0%ED%8B%B8%EB%A6%AC%ED%8B%B0-%ED%83%80%EC%9E%85%EB%93%A4</link>
            <guid>https://velog.io/@forntweb_tk/%ED%95%9C%EC%9E%85-%EB%A7%B5%EB%93%9C%ED%83%80%EC%9E%85-%EA%B8%B0%EB%B0%98-%EC%9C%A0%ED%8B%B8%EB%A6%AC%ED%8B%B0-%ED%83%80%EC%9E%85%EB%93%A4</guid>
            <pubDate>Fri, 22 Dec 2023 08:35:21 GMT</pubDate>
            <description><![CDATA[<h2 id="맵드타입-기반-유틸리티-타입들">맵드타입 기반 유틸리티 타입들</h2>
<br/>


<h3 id="✏️partialt">✏️<code>Partial&lt;T&gt;</code></h3>
<ul>
<li>특정 객체 타입의 모든 프로퍼티를 선택적 프로퍼티로 바꿔주는 타입<pre><code class="language-ts">interface Post {
title: string;
tags: string[];
content: string;
thumbnailURL?: string;
}
</code></pre>
</li>
</ul>
<p>// 직접 구현
type Partial<T> = {
  [key in keyof T]?: T[key]; // 키(맵드타입) : 밸류(인덱스드엑세스타입)
};</p>
<p>const draft: Partial<Post> = {
  title: &quot;제목 나중에&quot;,
  content: &quot;초안...&quot;,
};</p>
<pre><code>


&lt;br/&gt;


### ✏️`Required&lt;T&gt;`
- 특정 객체 타입의 모든 프로퍼티를 필수 프로퍼티로 바꿔주는 타입
```ts
interface Post {
  title: string;
  tags: string[];
  content: string;
  thumbnailURL?: string;
}

// 직접 구현
type Required&lt;T&gt; = {
  [key in keyof T]-?: T[key]; // 물음표(선택적)을 제거한다.
};

const withThumbnailPost: Required&lt;Post&gt; = {
  title: &quot;한입 타스 후기&quot;,
  tags: [&quot;ts&quot;],
  content: &quot;&quot;,
  thumbnailURL: &quot;https://...&quot;,
};</code></pre><br/>


<h3 id="✏️readonlyt">✏️<code>Readonly&lt;T&gt;</code></h3>
<ul>
<li>특정 객체 타입의 모든 프로퍼티를 읽기 전용 프로퍼티로 바꿔주는 타입<pre><code class="language-ts">interface Post {
title: string;
tags: string[];
content: string;
thumbnailURL?: string;
}
</code></pre>
</li>
</ul>
<p>// 직접 구현
type Readonly<T> = {
  readonly [key in keyof T]: T[key];
};</p>
<p>const readonlyPost: Readonly<Post> = {
  title: &quot;보호된 게시글 입니다.&quot;,
  tags: [],
  content: &quot;&quot;,
};</p>
<p>readonlyPost.content = &quot;&quot;; // ❌오류
```</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[한입] 유틸리티 타입]]></title>
            <link>https://velog.io/@forntweb_tk/%ED%95%9C%EC%9E%85-%EC%9C%A0%ED%8B%B8%EB%A6%AC%ED%8B%B0-%ED%83%80%EC%9E%85</link>
            <guid>https://velog.io/@forntweb_tk/%ED%95%9C%EC%9E%85-%EC%9C%A0%ED%8B%B8%EB%A6%AC%ED%8B%B0-%ED%83%80%EC%9E%85</guid>
            <pubDate>Fri, 22 Dec 2023 07:19:31 GMT</pubDate>
            <description><![CDATA[<h2 id="유틸리티-타입">유틸리티 타입</h2>
<p>(Utility Types)</p>
<ul>
<li>제네릭, 맵드 타입, 조건부 타입 등의 타입 조작 기능을 통해 실무에서 자주 사용되는 타입을 미리 만들어 놓은 것</li>
</ul>
<br/>


<h3 id="✏️예제1-readonlyt">✏️예제1: <code>Readonly&lt;T&gt;</code></h3>
<ul>
<li>모든 프로퍼티를 읽기전용으로 바꿈<pre><code class="language-ts">interface Person {
name: string;
age: number;
}
</code></pre>
</li>
</ul>
<p>const person: Readonly<Person> = { //👈
  name: &quot;이정환&quot;,
  age: 27,
};</p>
<p>person name = &quot;&quot;; // ❌오류</p>
<pre><code>

&lt;br/&gt;


### ✏️예제2: `Partial&lt;T&gt;`
- 모든 프로퍼티를 선택적 프로퍼티로 바꿈
```ts
interface Person {
  name: string;
  age: number;
}

const person: Partial&lt;Person&gt; = { //👈
  name: &quot;이정환&quot;,
};
</code></pre><br/>

<h3 id="✏️그외">✏️그외...</h3>
<ul>
<li>너무 많아서 다 볼 수 없다.
<img src="https://velog.velcdn.com/images/forntweb_tk/post/abe57535-2ace-4995-8b8d-02e6807d2b89/image.png" alt=""></li>
</ul>
<br/>

<h3 id="✏️우리가-알아볼-타입">✏️우리가 알아볼 타입</h3>
<p><img src="https://velog.velcdn.com/images/forntweb_tk/post/cc3d02ec-1e3e-45c2-8cce-03f802e777d1/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[한입] 📒Day14 Quiz]]></title>
            <link>https://velog.io/@forntweb_tk/%ED%95%9C%EC%9E%85-Day14-Quiz</link>
            <guid>https://velog.io/@forntweb_tk/%ED%95%9C%EC%9E%85-Day14-Quiz</guid>
            <pubDate>Fri, 22 Dec 2023 06:52:46 GMT</pubDate>
            <description><![CDATA[<h2 id="quiz-1">Quiz 1.</h2>
<pre><code class="language-ts">/*
[ 문제 소개 ]
다음 요구사항을 만족하는 IsProductKey&lt;T&gt; 타입을 완성하세요
- IsProductKey 타입은 조건부 타입으로 다음 조건에 따라 각각 다른 타입으로 결정됩니다.
  - T가 Product의 key(프로퍼티 이름)중 하나일 경우 결과는 true 타입이 됩니다.
  - T가 Product의 key(프로퍼티 이름)중 하나가 아닐 경우 결과는 false 타입이 됩니다.
  - 예를 들면 다음과 같습니다.
    - ex) IsProductKey&lt;&quot;id&quot;&gt; 타입은 true 타입이 됩니다.
    - ex) IsProductKey&lt;&quot;author&quot;&gt; 타입은 false 타입이 됩니다.
*/

/* [Quiz] 아래의 코드를 수정하세요 */
interface Product {
  id: number;
  name: string;
  price: number;
  seller: {
    id: number;
    name: string;
    company: string;
  };
}

type IsProductKey&lt;T&gt; = any;

/* [Test] 여기부터는 정답을 체크하기 위한 용도로 수정하지 않습니다 */
import { Equal, Expect } from &quot;@type-challenges/utils&quot;;

type TestCases = [
  Expect&lt;Equal&lt;IsProductKey&lt;&quot;id&quot;&gt;, true&gt;&gt;,
  Expect&lt;Equal&lt;IsProductKey&lt;&quot;name&quot;&gt;, true&gt;&gt;,
  Expect&lt;Equal&lt;IsProductKey&lt;&quot;author&quot;&gt;, false&gt;&gt;,
  Expect&lt;Equal&lt;IsProductKey&lt;&quot;winterlood&quot;&gt;, false&gt;&gt;
];</code></pre>
<br/>


<h3 id="✏️답안">✏️답안</h3>
<pre><code class="language-ts">type IsProductKey&lt;T&gt; = T extends keyof Product ? true : false;
</code></pre>
<p><a href="https://www.typescriptlang.org/play?strict=false&amp;noImplicitAny=false&amp;ts=5.1.6#code/PQKgUA2gBIHN2DgTVAxg4HBqoF0yBOmwMuNUCljgNrWA1A4LargIuNSAznYI4TgGquApTVAJIDOACgE4D2AJgK4BjAC4BpAKYBPADwAVAHxRAAwuBQ8YqARMcCMg7UAcg3jABaZu279h4iUuWABcaiAHCcAuNYADey4B5xwDodUHHfuAF0aiAGVsAfdqhAQBqQz0APTtcPQAaawFQJwEoWwAmmzAA6MCgoQxlAABqoTl5BIUANcagAa0kACkAQVbdAH2XAB6WoQBdxwBDOgEpAREmoWkAMhsAfcahAXprABrGoGMAfmsYhDj4xS2aoZLSMrKhc-JMi0oqJGvqmtq6e3rzAEVHABabhscnGADMAQwAbFnmVReX0zMNACDHAH06oIATlsALl2ebATKCATBrAK1DKS+mTWYgAHu0jAVTKJJFIAEQASx42IUKhsMzmCyWcNW3ygyNRrHRRXMOIefCEAAsuBxCZYbI8Xm9lB9KSBgGAwKAoBAAIp8XEALzQUDOgAz20qAFXnADstAMAGENxHR4KAisC4gB2QjEHEeAnmDOEUAA3qt8QAuKAmvgAWwARhaANyrE0PD1iV0sGamgDm-syAAcOLjra73d6-atXk8nhbXY6EVAXW7PT6ONGEYHg6Hwyao1SoAIuB6Yw8TRIK-GqyWAL7+jtioQSGPzelbMxY+RQAC86xpSPNJp4LHKki4d02hTtAH4oKT5q6+a9-eKQJKZGIw4rADejgAY6xyABkXGHFAK9NFEALTOAGwXaJeoIAQccAOqtQQCVY4AI80eLqtCAAOTUCAFKjsKYIaoq4g2nJCA6UAAKIAI58M8AA0aFIgOdodlAdzcB6UDYgAAn2A76AIbLPJmVansArK4i82IHtR8wnmGADCDyvAuk4QKsqH4WIwhSBhWFPFIQ5rpi0h4gSci4duciqaJ4mSdJzxycYClMtiZZiISamzGIGnYVpBFCFJmF6fJGJGSy7KcmZxHPK8Vk2RJdm6bJTmMli2IAO6muaHBPFwvAeXullyGAaD+kAA">코드링크</a></p>
<h2 id="quiz-2">Quiz 2.</h2>
<pre><code class="language-ts">/*
[ 문제 소개 ]
다음 조건을 만족하는 Extract&lt;T, U&gt; 타입을 구현하세요
- Extract&lt;T, U&gt; 타입은 T로부터 U만 추출하는 타입입니다.
  - ex) Extract&lt;string | boolean, boolean&gt;은 boolean 타입이 됩니다.
  - ex) Extract&lt;number | string, string&gt;은 string 타입이 됩니다.

(힌트💡 강의 중 직접 구현하는 Exclude 타입의 반대라고 생각해보세요!)
*/

/* [Quiz] 아래의 코드를 수정하세요 */
type Extract&lt;T, U&gt; = any;

/* [Test] 여기부터는 정답을 체크하기 위한 용도로 수정하지 않습니다 */
import { Equal, Expect } from &quot;@type-challenges/utils&quot;;

type TestCases = [
  Expect&lt;Equal&lt;Extract&lt;string | number, string&gt;, string&gt;&gt;,
  Expect&lt;Equal&lt;Extract&lt;string | number, number&gt;, number&gt;&gt;
];</code></pre>
<br/>


<h3 id="✏️답안-1">✏️답안</h3>
<pre><code class="language-ts">type Extract&lt;T, U&gt; = T extends U? never : U;</code></pre>
<p><a href="https://www.typescriptlang.org/play?strict=false&amp;noImplicitAny=false&amp;ts=5.1.6&amp;ssl=12&amp;ssc=1&amp;pln=13&amp;pc=1#code/PQKgUA2gBIHN2DgTVAxg4HBqoF0yBOmwMuNUA4TgLjWAi41IDOdgjhOAaq4ClNUAogB4AuATgIYDGTAPACoA0UAKoA+KIAGFwKHjJQDa1gEDXKgDkHAKWNgAtPWbsufQaImTAAuNRegHQ7AAb2AGReGkogFNnAObM0DkwBNNmAHRgoUDQFMGAEpNVk4eAGdWAEsAOwBzKAAfKAAjAHs0gBs-NhjBdKycmJFjAuzcg0AXcahAShb3Lx9-IJDtHhiAVwBbFL8WJKhIlli4wUHhkoHo+Kra+rAwAApAGPXADqXAXg3AQp2oQFQawA1xqEBESahAQcnARAmoeWdGDkz2gBM-A33ADB7AAGbAH3bAA5qoQF2BwEAawAuq4AXnpUAEJAmAQMB5qAoBAAIrtKIALzQUEAIqOADPb9oAVecAOy2AH06oIAMIcAqBNKZRQKFgJgATwADo9GKEdAJhGIALwmKABJh+GJ3cLCAD8UBifgAbr0oAAuYQAbhhIDhvD8kTRgBvRwAMdVZaKTAK9NJEALTOAGwXKFqoIAQccAOqtQQCVY4AR5tMJPJgAHJqCAKVHAK1D7ip0KinXpaRYTCgAG96ABHdpsTKCRiMrhQAC+UAAZiw0p0oAAiAACdMZag4AAtY9l4mrgO0mFFMuEc4qaQzHqrIgBhNjhNVQLkQbyaRM8OjR2PcZmtbhjabJDrdXqjKZxESLobxEQrgcJvw6EcxzLjrRhKdL-pznosQTn3orsVdC8bsBoRVAA">코드링크</a></p>
<br/>



<h2 id="quiz-3">Quiz 3.</h2>
<pre><code class="language-ts">/*
[ 문제 소개 ]
배열 타입의 요소를 추출하는 InferArrayType&lt;T&gt; 타입을 구현하세요
*/

/* [Quiz] 아래의 코드를 수정하세요 */
type InferArrayType&lt;T&gt; = any;

/* [Test] 여기부터는 정답을 체크하기 위한 용도로 수정하지 않습니다 */
import { Expect, Equal } from &quot;@type-challenges/utils&quot;;

const arr1 = [1, 2, 3];
const arr2 = [&quot;hello&quot;, &quot;myname&quot;, &quot;winterlood&quot;];
const arr3 = [1, true, &quot;hi&quot;];

type TestCases = [
  Expect&lt;Equal&lt;InferArrayType&lt;typeof arr1&gt;, number&gt;&gt;,
  Expect&lt;Equal&lt;InferArrayType&lt;typeof arr2&gt;, string&gt;&gt;,
  Expect&lt;Equal&lt;InferArrayType&lt;typeof arr3&gt;, number | string | boolean&gt;&gt;
];</code></pre>
<br/>


<h3 id="✏️답안-2">✏️답안</h3>
<pre><code class="language-ts">type InferArrayType&lt;T&gt; = T extends (infer R)[]? R :never;</code></pre>
<p><a href="https://www.typescriptlang.org/play?strict=false&amp;noImplicitAny=false&amp;ts=5.1.6#code/PQKgUA2gBIHN2DgTVAxg4HBqoF0yAYewL6NUAMLgoeOAa41ICljigPp1SAps4DmzgGquApTVAJIB2AZgKYBOAgjx4BDAJ4AVEQAcuAHjEA+fAUAi41EA2tYBA1+oA5B0mBDAwYUFAgBFAK4BLAF5oogEVHAGe0lAKvOAdlqqAMIcCoEztJQBmAALlJcrJy8AsLiYXKKALxQYlBcAB7BXGwAJgDOUAAUVpE8UABKAJQQaAD85VAAXGxcAG68ANxGJhBiXLnB9oA3o4AMdYABvYAMi8w+gK9NqoAtM4A2C-TDUIAg44A6q1CAlWOAI82AOh1QvvSAA5NQgFKjgK1DgBNNgCdNgYZWALaSAPY8wVAA3lAAomnSAGNggAaX4ARwsQgANlAAL5QDg8F6PKAAIgAAqFpABaAEAC2hUKyAHM+sALMErFDcqjOmAAS82P0oEJBABGKBJCBs0EAJlBAGY0J0GUyPqyeLzOaZUXiuFCoS9UaDUY8RGwhI8uMq0QB3YqZHiKl7ZVHC+mM5kSgXS7mg4I8CxcFV4qxmulY8K9foAYSEuT6trAUF+-y4QJkPwh0Jk7G4-EEogk0hknpeHBZ7PkoLYFkeACNePJs8HQ4DgpHo1DYyVokm4mmMxLedmoP0eMVicXgaW-uXK5Dq3GoonYinG5meALW7mC7woAAfNsOzuLqD5l4vIlCNjFsDmoA">코드링크</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[한입] infer(추론)]]></title>
            <link>https://velog.io/@forntweb_tk/%ED%95%9C%EC%9E%85-infer%EC%B6%94%EB%A1%A0</link>
            <guid>https://velog.io/@forntweb_tk/%ED%95%9C%EC%9E%85-infer%EC%B6%94%EB%A1%A0</guid>
            <pubDate>Thu, 21 Dec 2023 10:50:32 GMT</pubDate>
            <description><![CDATA[<p>(Inference)</p>
<h3 id="✏️함수-타입에서-리턴-타입만-가져오기">✏️함수 타입에서 리턴 타입만 가져오기</h3>
<pre><code class="language-ts">type Func = () =&gt; string;

type ReturnType&lt;T&gt; = T extends () =&gt; string ? string : never;

type A = ReturnType&lt;Func&gt;; // string</code></pre>
<p><img src="https://velog.velcdn.com/images/forntweb_tk/post/3b41e4a0-4a6c-439b-8606-3b2014867bc2/image.png" alt=""></p>
<ul>
<li>하지만 다음 <code>type B</code>는 <code>never</code>타입이 나온다 (원하는 결과는 <code>number</code>타입)<pre><code class="language-ts">type FuncA = () =&gt; string;
type FuncB = () =&gt; number;
</code></pre>
</li>
</ul>
<p>type ReturnType<T> = T extends () =&gt; string ? string : never;</p>
<p>type A = ReturnType<FuncA>; // string
type B = ReturnType<FuncB>; // never</p>
<pre><code>
- 다음과 같이 코드를 작성하면 참이 되게 하는 타입을 추론해서 R에 들어간다.
```ts
type FuncA = () =&gt; string;
type FuncB = () =&gt; number;

type ReturnType&lt;T&gt; = T extends () =&gt; infer R ? R : never;

type A = ReturnType&lt;FuncA&gt;; // string
type B = ReturnType&lt;FuncB&gt;; // number
type C = ReturnType&lt;number&gt;; // never ※추론불가※</code></pre><p><img src="https://velog.velcdn.com/images/forntweb_tk/post/164002a8-02c9-41c4-b734-47ee4cf15bc0/image.png" alt=""></p>
<br/>


<h3 id="✏️promise의-결과타입만-가져오기">✏️promise의 결과타입만 가져오기</h3>
<pre><code class="language-ts">type PromiseUnpack&lt;T&gt; = any;
// 요구사항
// 1. T는 프로미스 타입이어야 함
// 2. 프로미스 타입의 결과값 타입을 반환하여야 함

type PromiseA = PromiseUnpack&lt;Promise&lt;number&gt;&gt;;
// number타입원함

type PromiseB = PromiseUnpack&lt;Promise&lt;string&gt;&gt;;
// string타입원함</code></pre>
<ul>
<li>정답코드<pre><code class="language-ts">type PromiseUnpack&lt;T&gt; = T extends Promise&lt;infer R&gt; ? R :never;
</code></pre>
</li>
</ul>
<p>type PromiseA = PromiseUnpack&lt;Promise<number>&gt;; // number</p>
<p>type PromiseB = PromiseUnpack&lt;Promise<string>&gt;; // string
```</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[한입] 분산적인 조건부 타입]]></title>
            <link>https://velog.io/@forntweb_tk/%ED%95%9C%EC%9E%85-%EB%B6%84%EC%82%B0%EC%A0%81%EC%9D%B8-%EC%A1%B0%EA%B1%B4%EB%B6%80-%ED%83%80%EC%9E%85</link>
            <guid>https://velog.io/@forntweb_tk/%ED%95%9C%EC%9E%85-%EB%B6%84%EC%82%B0%EC%A0%81%EC%9D%B8-%EC%A1%B0%EA%B1%B4%EB%B6%80-%ED%83%80%EC%9E%85</guid>
            <pubDate>Thu, 21 Dec 2023 09:52:20 GMT</pubDate>
            <description><![CDATA[<p>(Distributive Conditional Types)</p>
<ul>
<li>유니온타입은 일반적인 조건부 타입이 아닌 분산적인 조건부 타입으로 업그레이드 되어 각각 분리되어 들어간다.<pre><code class="language-ts">type StringNumberSwitch&lt;T&gt; = T extends number ? string : number;
</code></pre>
</li>
</ul>
<p>let varA: StringNumberSwitch<number>; // string
let varB: StringNumberSwitch<string>; // number
let varC: StringNumberSwitch&lt;number | string&gt;; // number | string 유니온타입
// 1. let varC: StringNumberSwitch<number>; → string
// 2. let varC: StringNumberSwitch<string>; → number</p>
<pre><code>

&lt;br/&gt;


### ✏️예제1 : 유니온에서 특정 타입만 제거하기
```ts
type Exclude&lt;T, U&gt; = T extends U ? never : T;

type A = Exclude&lt;number | string | boolean, string&gt;; // number | boolean 유니온 타입
// type A = Exclude&lt;number , string&gt;; → number
// type A = Exclude&lt;string , string&gt;; → never
// type A = Exclude&lt;boolean , string&gt;; → boolean
// 결과: number | never | boolean 합집합 → number | boolean 유니온 타입</code></pre><br/>



<h3 id="✏️예제2--유니온에서-특정-타입만-가져오기">✏️예제2 : 유니온에서 특정 타입만 가져오기</h3>
<pre><code class="language-ts">type Extract&lt;T, U&gt; = T extends U ? T : never;

type B = Extract&lt;number | string | boolean, string&gt;; // string 타입
// type A = Exclude&lt;number , string&gt;; → never
// type A = Exclude&lt;string , string&gt;; → string
// type A = Exclude&lt;boolean , string&gt;; → never
// 결과: never | string | never 합집합 → string 타입</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[한입] 조건부 타입]]></title>
            <link>https://velog.io/@forntweb_tk/%ED%95%9C%EC%9E%85-%EC%A1%B0%EA%B1%B4%EB%B6%80-%ED%83%80%EC%9E%85</link>
            <guid>https://velog.io/@forntweb_tk/%ED%95%9C%EC%9E%85-%EC%A1%B0%EA%B1%B4%EB%B6%80-%ED%83%80%EC%9E%85</guid>
            <pubDate>Thu, 21 Dec 2023 09:28:12 GMT</pubDate>
            <description><![CDATA[<h2 id="조건부-타입">조건부 타입</h2>
<p>삼항연산자를 이용해서 조건에 따라 타입을 결정한다.</p>
<pre><code class="language-ts">type A = number extends string ? string : number; // number타입</code></pre>
<br/>



<h3 id="✏️객체타입">✏️객체타입</h3>
<pre><code class="language-ts">type ObjA = {
  a: number;
};

type ObjB = {
  a: number;
  b: number;
};

type B = ObjB extends ObjA ? string : number; // number타입</code></pre>
<br/>



<h3 id="✏️제네릭타입">✏️제네릭타입</h3>
<blockquote>
<h4 id="참고-제네릭타입-형태">참고: 제네릭타입 형태</h4>
</blockquote>
<pre><code class="language-ts">type StringNumberSwitch&lt;T&gt; = T extends number ? string : number;
let varA: StringNumberSwitch&lt;number&gt;; // string
let varB: StringNumberSwitch&lt;string&gt;; // number</code></pre>
<br/>



<h4 id="예제">예제</h4>
<ul>
<li>다음과 같은 코드가 있다.<pre><code class="language-ts">function removeSpaces(text: string) {
return text.replaceAll(&quot; &quot;, &quot;&quot;);
}
</code></pre>
</li>
</ul>
<p>let result = removeSpaces(&quot;hi im winterlood&quot;); //&quot;hiimwinterlood&quot;
result.toUpperCase();</p>
<pre><code>

&lt;br/&gt;



- 여기서 매개변수 `text`의 타입에 `undefined`와 `null`값을 추가하면 다음과 같은 오류가 발생한다.
```ts
function removeSpaces(text: string | undefined | null) {
  return text.replaceAll(&quot; &quot;, &quot;&quot;); //❌오류 →string메서드 사용불가
}

let result = removeSpaces(&quot;hi im winterlood&quot;); //&quot;hiimwinterlood&quot;
result.toUpperCase();</code></pre><br/>



<ul>
<li>조건문을 이용해서 타입 좁히기한다.
→ 함수 내부 오류는 사라지지만 <code>result</code>변수 타입이 <code>string|undefined</code>로, 문자열 메서드 사용이 불가해 오류 발생</li>
</ul>
<pre><code class="language-ts">function removeSpaces(text: string | undefined | null) {
  if (typeof text === &quot;string&quot;) {
    return text.replaceAll(&quot; &quot;, &quot;&quot;);
  } else {
    return undefined;
  }
}

let result = removeSpaces(&quot;hi im winterlood&quot;);
result.toUpperCase(); //❌오류 →string메서드 사용불가</code></pre>
<br/>



<ul>
<li>조건부 타입을 사용하여 다음과 같이 해결한다.<pre><code class="language-ts">function removeSpaces&lt;T&gt;(text: T): T extends string ? string : undefined {
if (typeof text === &quot;string&quot;) {
  return text.replaceAll(&quot; &quot;, &quot;&quot;); //❌오류
} else {
  return undefined; //❌오류
}
}
</code></pre>
</li>
</ul>
<p>let result = removeSpaces(&quot;hi im winterlood&quot;); //✅
result.toUpperCase(); //✅
let result2 = removeSpaces(undefined); //✅</p>
<pre><code>

&lt;br/&gt;



- 함수내부에 발생한 오류는 다음과 같이 변경한다.(함수내부에서는 조건부타입의 결과가 어떻게 될지 알수 없기 때문에 발생한 오류)

1. any타입으로 단언 → 숫자를 전달해도 검사불가
```ts
type StringNumberSwitch&lt;T&gt; = T extends number ? string : number;

let varA: StringNumberSwitch&lt;number&gt;;
let varB: StringNumberSwitch&lt;string&gt;;

function removeSpaces&lt;T&gt;(text: T): T extends string ? string : undefined {
  if (typeof text === &quot;string&quot;) {
    return text.replaceAll(&quot; &quot;, &quot;&quot;) as any; //👈
  } else {
    return undefined as any; //👈
  }
}

let result = removeSpaces(&quot;hi im winterlood&quot;);
result.toUpperCase();
let result2 = removeSpaces(undefined);</code></pre><br/>



<ol start="2">
<li>함수 오버로딩 → 오버로드 시그니처 만듦<pre><code class="language-ts">function removeSpaces&lt;T&gt;(text: T): T extends string ? string : undefined; //👈
function removeSpaces&lt;T&gt;(text: any) {
if (typeof text === &quot;string&quot;) {
 return text.replaceAll(&quot; &quot;, &quot;&quot;);
} else {
 return undefined;
}
}
</code></pre>
</li>
</ol>
<p>let result = removeSpaces(&quot;hi im winterlood&quot;);
result.toUpperCase();
let result2 = removeSpaces(undefined);</p>
<pre><code>- - `string`타입이 아닌 타입을 반환할 때 문제 감지
![](https://velog.velcdn.com/images/forntweb_tk/post/58bd6904-2090-483a-88c1-12f636125f5f/image.png)
- - `undefined`이 아닌 값을 반환할 때 문제 감지
![](https://velog.velcdn.com/images/forntweb_tk/post/3ee9075b-579e-4948-bde2-ac7fc39811a3/image.png)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[한입] 📒Day13 Quiz]]></title>
            <link>https://velog.io/@forntweb_tk/%ED%95%9C%EC%9E%85-Day13</link>
            <guid>https://velog.io/@forntweb_tk/%ED%95%9C%EC%9E%85-Day13</guid>
            <pubDate>Mon, 18 Dec 2023 13:23:27 GMT</pubDate>
            <description><![CDATA[<h2 id="quiz-1">Quiz 1.</h2>
<pre><code class="language-ts">/*
[ 문제 소개 ]
다음 요구사항을 만족하도록 getSellersFromProducts 함수의 매개변수와 반환값 타입을 정의하세요
- getSellersFromProducts 함수는 매개변수로 받은 Product 배열로부터, seller 객체만 추출해 새로운 배열로 반환하는 함수입니다.
- 반환값을 명시적으로 설정해야 합니다 (인덱스드 엑세스 타입)
*/

/* [Quiz] 아래의 코드를 수정하세요 */

interface Product {
  id: number;
  name: string;
  price: number;
  seller: {
    id: number;
    name: string;
    company: string;
  };
}

function getSellersFromProducts(products: any): any {
  return products.map((product) =&gt; product.seller);
}

/* [Test] 여기부터는 정답을 체크하기 위한 용도로 수정하지 않습니다 */
import { Equal, Expect } from &quot;@type-challenges/utils&quot;;

type TestCases = [
  Expect&lt;Equal&lt;Parameters&lt;typeof getSellersFromProducts&gt;[0], Product[]&gt;&gt;,
  Expect&lt;Equal&lt;ReturnType&lt;typeof getSellersFromProducts&gt;, Product[&quot;seller&quot;][]&gt;&gt;
];
</code></pre>
<br/>


<h3 id="✏️답안틀림">✏️답안(틀림)</h3>
<pre><code class="language-ts">function getSellersFromProducts(products: Product[]): Product[&quot;seller&quot;] {
  return products.map((product) =&gt; product.seller);
}</code></pre>
<h3 id="정답">정답</h3>
<pre><code class="language-ts">function getSellersFromProducts(products: Product[]): Product[&quot;seller&quot;][] {
  return products.map((product) =&gt; product.seller);
}</code></pre>
<h3 id="해설">해설</h3>
<p><img src="https://velog.velcdn.com/images/forntweb_tk/post/208c40f4-8fec-41b1-a240-97e38e3f502b/image.png" alt=""></p>
<h2 id="quiz-2">Quiz 2.</h2>
<pre><code class="language-ts">
/*
[ 문제 소개 ]
다음 조건을 만족하는 3개의 타입을 추가로 정의하세요
- PartialProduct 타입은 Product 타입의 모든 프로퍼티를 다 선택적 프로퍼티로 바꾼 타입입니다.
- ReadonlyProduct 타입은 Product 타입의 모든 프로퍼티를 다 읽기 전용 프로퍼티로 바꾼 타입입니다.
- ReadonlyPartialProduct 타입은 Product 타입의 모든 프로퍼티를 다 선택적, 읽기 전용 프로퍼티로 바꾼 타입입니다.
*/

/* [Quiz] 아래의 코드를 수정하세요 */
interface Product {
  id: number;
  name: string;
  price: number;
  seller: {
    id: number;
    name: string;
    company: string;
  };
}

type PartialProduct = any;

type ReadonlyProduct = any;

type ReadonlyPartialProduct = any;

/* [Test] 여기부터는 정답을 체크하기 위한 용도로 수정하지 않습니다 */
import { Equal, Expect, NotEqual } from &quot;@type-challenges/utils&quot;;

type TestCases = [
  Expect&lt;Equal&lt;PartialProduct, Partial&lt;Product&gt;&gt;&gt;,
  Expect&lt;Equal&lt;ReadonlyProduct, Readonly&lt;Product&gt;&gt;&gt;,
  Expect&lt;Equal&lt;ReadonlyPartialProduct, Readonly&lt;Partial&lt;Product&gt;&gt;&gt;&gt;
];
</code></pre>
<br/>


<h3 id="✏️답안">✏️답안</h3>
<pre><code class="language-ts">type PartialProduct = {
  [key in keyof Product]?: Product[key];
};

type ReadonlyProduct = {
  readonly [key in keyof Product]: Product[key];
};

type ReadonlyPartialProduct = {
  readonly [key in keyof Product]?: Product[key];
};</code></pre>
<p><a href="https://www.typescriptlang.org/play?strict=false&amp;noImplicitAny=false&amp;ts=5.1.6&amp;ssl=31&amp;ssc=3&amp;pln=21&amp;pc=1#code/PQKgUA2gBIHN2DgTVAxg4HBqoF0yBOmwMuNUA4TgLjWAi41IDOdgjhOAaq4ClNUAzMoBrjUgAwuCh4yYCmzgADWA6HVIFQJxpUAcg4BSxsAFooABQCGAJwAuASzkAbGQoD2AEwCuAYyWs2gAXHZOg8dPNAFV2ADlqiAQVd6AfZcAPS4B9OqJiiACQcBdhcBACZd3D35AFB7AHvrTNkAJpswAOikoACUAUzldbQA7dQBPLT0jE3YLYusytnsnV08fP0BfccAGOqhAEAnASrGwzyjY9gTk1MzsvML5ZTVNK1LTCpmbdlqe718AkIAaKFaO7vqIqBi4xJSQYDAwUCgIAEV9FQAvNChAEVHADPbmQBV5wB2Wn0AMIYEojEUDOYBUuSUGQUADM5IYMpYSjYAN5gKBQFS6ABcUFy+gAtgAjKEAbjRuLk+IyOIAzkoFOCAOZk9EABwZ8JxeKJpPJNIy6nUUJxqPR6MxXIJxIULLFuUp1KgdIZuWZ5PRhm0+NZclyBVp9KZsoAvmTjRclAVWQiJqoNJVZgBeKCi64AawyBQxuSgHoK2mhiKqaAA-DiHcYIH60GayWBLdb0lkcvkigsTM7XQpk2MvVHPd7fZ6A0HSmhw+n8wUY2BTRarQiRinxoo7dMkRmXeTs6NU+6C+Ci-7AxGlKGKx2qzW65cQNcACoZOnPQA3oy1AAG9gAZF2gCQCvTSRAC0zgBsFyhtQAg44AdVagnUAI838AGUQADk1BAFKjgFahxIg84qbXaZQuqAAFEAEd9A0LZAIAD2tYwtgAOW0JQQLA9QoGNKBoR0fEoAAIgAAQTDJJEMAALDQhVVJdgH0VR1BpHC40IqBFzpABhOR+RpKBnQgckoJgpQAB5kI0QTbSmUctnE0TRwAPnkjY+OgjJjGE0DRKbXNJKTXtCjE9N5NkxT0X4lShJE9RBM01NpPbKotmsvTbP0jtDNksAayAA">코드링크</a></p>
<br/>



<h2 id="quiz-3">Quiz 3.</h2>
<pre><code class="language-ts">/*
[ 문제 소개 ]
다음 조건을 만족하는 Score 타입을 구현하세요
- Score 타입은 점수를 나타내기 위한 문자열 타입으로 &#39;${0부터 10까지의 정수}-${0부터 10까지의 정수}&#39;형식을 갖습니다&#39;
  - ex) &quot;1-0&quot;, &quot;3-2&quot;, &quot;0-4&quot; 이런 형태의 문자열 타입입니다.
  - ex) 어느쪽의 점수에도 두 자리수는 올 수 없습니다.
*/

/* [Quiz] 아래의 코드를 수정하세요 */
type Score = any;

/* [Test] 여기부터는 정답을 체크하기 위한 용도로 수정하지 않습니다 */
import { Equal, Expect, ExpectExtends, ExpectFalse } from &quot;@type-challenges/utils&quot;;

const tc1 = &quot;19-0&quot;;
const tc2 = &quot;5-11&quot;;
const tc3 = &quot;9-1&quot;;
const tc4 = &quot;2-8&quot;;
const tc5 = &quot;7-2&quot;;

type TestCases = [
  ExpectFalse&lt;ExpectExtends&lt;Score, typeof tc1&gt;&gt;,
  ExpectFalse&lt;ExpectExtends&lt;Score, typeof tc2&gt;&gt;,
  Expect&lt;ExpectExtends&lt;Score, typeof tc3&gt;&gt;,
  Expect&lt;ExpectExtends&lt;Score, typeof tc4&gt;&gt;,
  Expect&lt;ExpectExtends&lt;Score, typeof tc5&gt;&gt;
];</code></pre>
<br/>


<h3 id="✏️답안-1">✏️답안</h3>
<pre><code class="language-ts">type Score = `${Int}-${Int}`;

type Int = &#39;1&#39;|&#39;2&#39;|&#39;3&#39;|&#39;4&#39;|&#39;5&#39;|&#39;6&#39;|&#39;7&#39;|&#39;8&#39;|&#39;9&#39;|&#39;10&#39;;</code></pre>
<p><a href="https://www.typescriptlang.org/play?ts=5.1.6#code/PQKgUA2gBIHN2DgTVAxg4HBqoF0yBOmwMuNUA4TgLjWAi41IDOdgjhOAaq4ClNUAygMYD2ATgKZSADC4KHjJgNrWAQNcqAOQcApY2AC09Zuy7dAAuNRACBOAMIcA+nVEAZDZ0AtDYAY6qIBBxwDqrsQAnjgF9G5gHnHAOh1QA5ABIA3gAZAAb2AGRagBGN4AydYADk4Aa41CAqBMqAL4Srp4+-sFhkVEOgKhrgLtDJIBoNYCtQ4ATTZgOYFBQUmwAHgCUUABEvhJutQA0dQDMEgBMLXVuEgAstVCALuOAD+1QaYA7C2EwlnLchQB0JWVQlTWALaOACU2AvVNhqoALo4AjzVCACi1QZoA1nSq0gDRjUCpQgGOjBZjLIMBgYKBQEACKAFcAJYALzQUEAIqOADPawoAVecAOy0aFThESiKAfMAAFwAngAHDiMVgcAC8UAABq4AJIAO0xMSptKiZIA3F8cfioDTMVBSQ5fA4AD4OTqChxtUX9UUAVlFADZRQB2UUADlFAE5Rf4HKzviBfgAVNgAZ0x4MAN6N6by0cKAV6aSIAWmcANguUAwmKCASrGjvZkZQglBAFKjr3RnyBAFtcaxuS4oABRACOAIAhgAbVoxir4hiYtMZthZ9OYtjUgAmRpzmcxADEU0aOFEoAAzFhMUN1AAC7LYEgYAAsU8miwBzY3AAGYoHJo21HXMakmqCYhi+Hl1XxqxrTsCz+eLzor2pSiS+Xyb7fcxdtffrk+ss8Lhj9fedCTK09MOfnhhS-cKrqbrF4hwhomgAwomtZGiuEArOmFbVpObAADywXmmIFkWpaIYS7CtJ2TANvevgAHxEc0MG5lm8G1shFFoRUhYlkaWEyGwuGAfh96dCRZGlChWY0RW6GMcxRJsfiHEXtx5EVgJqFCZh2GsQu7EEYu-RSbxtGyfm9EYUxiliWwElfiRYBoKyQA">코드링크</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[한입] 템플릿 리터럴 타입]]></title>
            <link>https://velog.io/@forntweb_tk/%ED%95%9C%EC%9E%85-%ED%85%9C%ED%94%8C%EB%A6%BF-%EB%A6%AC%ED%84%B0%EB%9F%B4-%ED%83%80%EC%9E%85</link>
            <guid>https://velog.io/@forntweb_tk/%ED%95%9C%EC%9E%85-%ED%85%9C%ED%94%8C%EB%A6%BF-%EB%A6%AC%ED%84%B0%EB%9F%B4-%ED%83%80%EC%9E%85</guid>
            <pubDate>Mon, 18 Dec 2023 13:19:59 GMT</pubDate>
            <description><![CDATA[<h2 id="템플릿-리터럴-타입">템플릿 리터럴 타입</h2>
<p>(Template Literal Type)</p>
<blockquote>
<p>스트링 리터럴 타입을 기반으로 정해진 패턴의 문자열만 포함하는 타입</p>
</blockquote>
<ul>
<li>다음과 같이 템플릿 리터럴 타입으로 원하는 형태의 조합을 작성한다.<pre><code class="language-ts">type Color = &quot;red&quot; | &quot;balck&quot; | &quot;green&quot;;
</code></pre>
</li>
</ul>
<p>type Animal = &quot;dog&quot; | &quot;cat&quot; | &quot;chicken&quot;;</p>
<p>type ColoredAnimal = <code>${Color}-${Animal}</code>; // 👈</p>
<p>const coloredAnimal: ColoredAnimal = &quot;balck-cat&quot;;</p>
<pre><code>![](https://velog.velcdn.com/images/forntweb_tk/post/e82b6192-9483-49e5-bf9e-f2287bb5bab2/image.png)

- 아래와 같이 모든 조합이 나타남
![](https://velog.velcdn.com/images/forntweb_tk/post/50b36ca3-d32b-4f4e-88c7-2f9ffad7e2b8/image.png)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[한입] Mapped(맵드)타입]]></title>
            <link>https://velog.io/@forntweb_tk/%ED%95%9C%EC%9E%85-Mapped%EB%A7%B5%EB%93%9C%ED%83%80%EC%9E%85</link>
            <guid>https://velog.io/@forntweb_tk/%ED%95%9C%EC%9E%85-Mapped%EB%A7%B5%EB%93%9C%ED%83%80%EC%9E%85</guid>
            <pubDate>Mon, 18 Dec 2023 13:13:54 GMT</pubDate>
            <description><![CDATA[<h2 id="mapped맵드타입">Mapped(맵드)타입</h2>
<blockquote>
<p>기존의 객체 타입으로부터 새로운 객체 타입을 만드는 타입</p>
</blockquote>
<pre><code class="language-ts">interface User {
  id: number;
  name: string;
  age: number;
}

// 한명의 유저 정보 불러오는 기능
function fetchUser() {
  // ...어쩌구 기능 ..
  return {
    id: 1,
    name: &quot;이정환&quot;,
    age: 27,
  };
}

// 한명의 유저 정보 수정 ㅓ기능
function updateUser(user: User) {
  // ...어쩌구 수정기능 ..
}

updateUser({
  id: 1,         // 👈안보내고 싶은 값
  name: &quot;이정환&quot;, // 👈안보내고 싶은 값
  age: 25,
});</code></pre>
<ul>
<li><code>age</code>값만 업데이트 하고 싶을 때, 다른 프로퍼티들은 안보내고 싶으면 선택적 프로퍼티를 다음과 같이 생성함 (<code>interface PatialUser</code>)<pre><code class="language-ts">interface User {
id: number;
name: string;
age: number;
}
</code></pre>
</li>
</ul>
<p>interface PatialUser { // 👈
  id?: number;
  name?: string;
  age?: number;
}</p>
<p>// 한명의 유저 정보 불러오는 기능
function fetchUser() {
  // ...어쩌구 기능 ..
  return {
    id: 1,
    name: &quot;이정환&quot;,
    age: 27,
  };
}</p>
<p>// 한명의 유저 정보 수정 기능
function updateUser(user: PatialUser) { // 👈
  // ...어쩌구 수정기능 ..
}</p>
<p>updateUser({
  age: 25,  // 👈
});</p>
<pre><code>- 하지만 이 방식은 기존 `User`타입과 너무 중복임 → *맵드타입으로 해결*



&lt;br/&gt;


- 맵드타입으로 나타낸 코드
&gt; **타입별칭으로만 생성 가능** (인터페이스 불가)
```ts
interface User {
  id: number;
  name: string;
  age: number;
}

type PatialUser = { // 👈타입별칭
  [key in &quot;id&quot; | &quot;name&quot; | &quot;age&quot;]?: User[key]; // 👈맵드타입 형태
};</code></pre><ul>
<li>마우스 오버시 다음과 같이 타입이 나타난다.
<img src="https://velog.velcdn.com/images/forntweb_tk/post/e92091c4-bb87-458e-9976-d6ac961f4fba/image.png" alt=""></li>
</ul>
<br/>

<ul>
<li>이는 다음과 같은 코드이다.(<code>keyof</code>연산자 사용)<pre><code class="language-ts">interface User {
id: number;
name: string;
age: number;
}
</code></pre>
</li>
</ul>
<p>type PatialUser = {
  [key in keyof User]?: User[key];
};</p>
<pre><code>
&lt;br/&gt;


#### ※ 참고: 맵드타입 형태 예시 ※
```ts
interface User {
  id: number;
  name: string;
  age: number;
}

type BooleanUser = {
  [key in keyof User]: boolean;
};

// readonly 사용
type ReadonlyUser = {
  readonly [key in keyof User]?: User[key];
};</code></pre><p><img src="https://velog.velcdn.com/images/forntweb_tk/post/4864bbbc-cdc5-43cb-b7a8-686dba4fdced/image.png" alt=""></p>
<blockquote>
<p>활용도가 높고 실무에서 자주 사용됨</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[한입] keyof연산자]]></title>
            <link>https://velog.io/@forntweb_tk/%ED%95%9C%EC%9E%85-keyof%EC%97%B0%EC%82%B0</link>
            <guid>https://velog.io/@forntweb_tk/%ED%95%9C%EC%9E%85-keyof%EC%97%B0%EC%82%B0</guid>
            <pubDate>Mon, 18 Dec 2023 06:13:32 GMT</pubDate>
            <description><![CDATA[<h2 id="keyof연산자">keyof연산자</h2>
<blockquote>
<p>특정 객체 타입으로부터 프로퍼티 키들을 모두 스트링 리터럴 유니온 타입으로 추출하는 연산자</p>
</blockquote>
<ul>
<li>일반적으로 작성된 코드<pre><code class="language-ts">interface Person {
name: string;
age: number;
}
</code></pre>
</li>
</ul>
<p>function getPropertyKey(person: Person, key: &quot;name&quot; | &quot;age&quot;) {
  return person[key];
}</p>
<p>const person: Person = {
  name: &quot;이정환&quot;,
  age: 27,
};</p>
<p>getPropertyKey(person, &quot;name&quot;); // 이정환</p>
<pre><code>- 하지만 `Person`의 프로퍼티 타입이 바뀔때마다 매번 `key`를 재정의 해주기 어렵다. → keyof연산을 사용
```ts

interface Person {
  name: string;
  age: number;
}

function getPropertyKey(person: Person, key: keyof Person) {
  return person[key];
}

const person: Person = {
  name: &quot;이정환&quot;,
  age: 27,
};

getPropertyKey(person, &quot;name&quot;); // 이정환</code></pre><ul>
<li><code>keyof Person</code>은 Person의 키 값들이 유니온으로 추출됨 (<code>&quot;name&quot; | &quot;age&quot; | ...</code>)</li>
</ul>
<br/>


<ul>
<li>주의사항: 무조건 타입에만 사용할 수 있다.
<img src="https://velog.velcdn.com/images/forntweb_tk/post/10467093-59c0-46eb-a396-015dab91ff5a/image.png" alt=""></li>
</ul>
<br/>

<h3 id="✏️typeof연산자와-같이-사용">✏️typeof연산자와 같이 사용</h3>
<pre><code class="language-ts">type Person = typeof person; // 👈

function getPropertyKey(person: Person, key: keyof typeof person) { // 👈
  return person[key];
}

const person = { 
  name: &quot;이정환&quot;,
  age: 27,
};

getPropertyKey(person, &quot;name&quot;); // 이정환</code></pre>
<ul>
<li><p>위와 같이 코드를 작성하면 타입스크립트가 변수 <code>person</code>에서 타입을 추론함
<img src="https://velog.velcdn.com/images/forntweb_tk/post/a90e5b16-e097-4c37-96e7-9b6aa64dc9d3/image.png" alt=""></p>
</li>
<li><p>다음과 같은 형식으로 적용됨
<img src="https://velog.velcdn.com/images/forntweb_tk/post/6a2b0987-2cb4-4bce-a82b-a8807d158449/image.png" alt=""></p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[한입] 인덱스드 엑세스 타입]]></title>
            <link>https://velog.io/@forntweb_tk/%ED%95%9C%EC%9E%85-%EC%9D%B8%EB%8D%B1%EC%8A%A4%EB%93%9C-%EC%97%91%EC%84%B8%EC%8A%A4-%ED%83%80%EC%9E%85</link>
            <guid>https://velog.io/@forntweb_tk/%ED%95%9C%EC%9E%85-%EC%9D%B8%EB%8D%B1%EC%8A%A4%EB%93%9C-%EC%97%91%EC%84%B8%EC%8A%A4-%ED%83%80%EC%9E%85</guid>
            <pubDate>Mon, 18 Dec 2023 05:31:39 GMT</pubDate>
            <description><![CDATA[<h2 id="인덱스드-엑세스-타입">인덱스드 엑세스 타입</h2>
<p>(Indexed Access Type)</p>
<blockquote>
<p>객체, 배열, 튜플 타입에서 특정 프로퍼티 혹은 요소의 타입을 추출하는 타입</p>
</blockquote>
<br/>


<h3 id="✏️객체-타입">✏️객체 타입</h3>
<ul>
<li>일반적으로 작성된 코드<pre><code class="language-ts">interface Post {
title: string;
content: string;
author: {
  id: number;
  name: string;
};
}
</code></pre>
</li>
</ul>
<p>function printAuthorInfo(author: { id: number; name: string }) {
  console.log(<code>${author.name} - ${author.id}</code>);
}</p>
<p>const post: Post = {
  title: &quot;게시글 제목&quot;,
  content: &quot;게시글 컨텐츠&quot;,
  author: {
    id: 1,
    name: &quot;이정환&quot;,
  },
};</p>
<pre><code>
&lt;br/&gt;

- 여기서 만약 `author`의 프로퍼티값 `location`이 추가 되면 (예를들면) `author`의 값을 받는 모든 함수들에 그 값을 일일히 추가해주어야 한다. (번거로움)
![](https://velog.velcdn.com/images/forntweb_tk/post/570cc357-af26-4c9f-8b9d-367d675e98e3/image.png)

&lt;br/&gt;

- 인덱스드 엑세스를 사용하면 다음 코드와 같다.
```ts
interface Post {
  title: string;
  content: string;
  author: {
    id: number;
    name: string;
  };
}

function printAuthorInfo(author: Post[&quot;author&quot;]) { // 👈
  console.log(`${author.name} - ${author.id}`);
}

const post: Post = {
  title: &quot;게시글 제목&quot;,
  content: &quot;게시글 컨텐츠&quot;,
  author: {
    id: 1,
    name: &quot;이정환&quot;,
  },
};</code></pre><p><img src="https://velog.velcdn.com/images/forntweb_tk/post/65135377-f110-4b4e-b827-f91e03d65335/image.png" alt=""></p>
<ul>
<li>프로퍼티의 타입이 추가되거나 수정되어도 즉시 반영된다.</li>
</ul>
<blockquote>
<h4 id="-용어-인덱스index-">※ 용어: 인덱스(index) ※</h4>
<p><img src="https://velog.velcdn.com/images/forntweb_tk/post/76e5cd4c-037b-4c66-8ece-337c26a90dfc/image.png" alt=""></p>
</blockquote>
<br/>

<ul>
<li>주의사항1: 인덱스에 들어가는 문자열은 값이 아니라 타입이다.<ul>
<li>다음과 같이 작성하면 오류가 발생한다.<pre><code class="language-ts">// ...생략
</code></pre>
</li>
</ul>
</li>
</ul>
<p>const key = &quot;author&quot;;</p>
<p>function printAuthorInfo(author: Post[key]) { // ❌오류 (변수이자 값 →불가)
  console.log(<code>${author.name} - ${author.id}</code>);
}</p>
<pre><code>
&lt;br/&gt;

- 주의사항2: 프로퍼티에 없는 값이 들어가면 오류가 발생한다.
```ts
interface Post {
  title: string;
  content: string;
  author: {
    id: number;
    name: string;
  };
}

function printAuthorInfo(author: Post[&quot;what&quot;]) { // ❌오류
  console.log(`${author.name} - ${author.id}`);
}</code></pre><br/>

<blockquote>
<h4 id="참고-author에서-id-값만-가져오고-싶은-경우">참고: <code>author</code>에서 <code>id</code> 값만 가져오고 싶은 경우</h4>
</blockquote>
<ul>
<li>이중 대괄호<code>[]</code>를 사용한다.<pre><code class="language-ts">interface Post {
title: string;
content: string;
author: {
  id: number;
  name: string;
};
}
function printAuthorInfo(author: Post[&quot;author&quot;][&quot;id&quot;]) { // 👈
console.log(`${author.name} - ${author.id}`);
}</code></pre>
</li>
<li>아래와 같이 <code>author</code>는 <code>number</code>타입으로 지정됨
<img src="https://velog.velcdn.com/images/forntweb_tk/post/fb6ef830-a695-47dc-a208-d7a213637f8e/image.png" alt=""></li>
</ul>
<br/>


<h3 id="✏️배열-타입">✏️배열 타입</h3>
<ul>
<li>배열 타입은 인터페이스가 아닌 타입별칭으로 한다.<pre><code class="language-ts">type PostList = { // 👈1.타입별칭
title: string;
content: string;
author: {
  id: number;
  name: string;
};
}[]; // 👈2.배열
</code></pre>
</li>
</ul>
<p>function printAuthorInfo(author: PostList[number][&quot;author&quot;]) { // 👈3.별첨
  console.log(<code>${author.name} - ${author.id}</code>);
}</p>
<p>const post: PostList[number] = { // 👈4.별첨
  title: &quot;게시글 제목&quot;,
  content: &quot;게시글 컨텐츠&quot;,
  author: {
    id: 1,
    name: &quot;이정환&quot;,
  },
};</p>
<pre><code>- 별첨3. 요소의 타입을 먼저 뽑고 그 객체타입으로부터 author프로퍼티 타입을 뽑아온다.
- 별첨4. `number`를 넣으면 배열타입으로부터 하나의 배열타입만 가져온다
  - 숫자를 넣어도 된다
```ts
const post: PostList[0] = { // 👈
  title: &quot;게시글 제목&quot;,
  content: &quot;게시글 컨텐츠&quot;,
  author: {
    id: 1,
    name: &quot;이정환&quot;,
  },
};</code></pre><br/>


<h3 id="✏️튜플-타입">✏️튜플 타입</h3>
<ul>
<li>튜플타입은 다음과 같이 사용한다.<pre><code class="language-ts">type Tup = [number, string, boolean];
</code></pre>
</li>
</ul>
<p>type Tup0 = Tup[0];
type Tup1 = Tup[1];
type Tup2 = Tup[2];
type Tup3 = Tup[3]; // ❌오류 → 길이고정
type TupNum = Tup[number]; // 👈number
// → 튜플타입 안에 있는 모든 타입의 최적의 공통타입을 추론</p>
<pre><code></code></pre>]]></description>
        </item>
    </channel>
</rss>