<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>aroo_ming.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Thu, 16 Jan 2025 10:34:56 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>aroo_ming.log</title>
            <url>https://velog.velcdn.com/images/aroo_ming/profile/1e2b5ad9-5999-4b7a-8c30-b9fa58dff863/image.gif</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. aroo_ming.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/aroo_ming" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Lighthouse 성능 분석 (FCP / LCP / TBT)]]></title>
            <link>https://velog.io/@aroo_ming/Lighthouse-%EC%84%B1%EB%8A%A5-%EB%B6%84%EC%84%9D-LCP-FCP-TBT</link>
            <guid>https://velog.io/@aroo_ming/Lighthouse-%EC%84%B1%EB%8A%A5-%EB%B6%84%EC%84%9D-LCP-FCP-TBT</guid>
            <pubDate>Thu, 16 Jan 2025 10:34:56 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/aroo_ming/post/171c64a0-29d8-419c-95c6-661aa32ca5d8/image.png" alt="thumbnail"></p>
<br />

<h2 id="0️⃣-로컬-환경과-배포된-서비스-간의-성능-차이가-발생하는-이유">0️⃣ 로컬 환경과 배포된 서비스 간의 성능 차이가 발생하는 이유</h2>
<p>Lighthouse로 성능을 체크해보면 로컬 환경과 배포된 서비스에서 성능 차이가 꽤 많이 발생한다. </p>
<p>보통은 배포 서비스에서 성능이 더 좋게 나오는데 그 이유는 <code>번들러가 코드를 최적화</code>하기 때문이다.
번들링 과정에서 코드 압축, Tree Shaking 같은 최적화 작업이 일어난다. 이를 통해 불필요한 코드가 제거되고, 번들 크기가 줄어들며, 브라우저가 더 효율적으로 로딩되도록 환경이 개선된다.</p>
<p>반면 로컬 환경에서는 디버깅 편의성을 위해 코드 최적화가 생략되거나 제한적으로 적용되기 때문에, 같은 코드로 구현되어 있더라도 로컬 환경과 배포 서비스 사이에 성능 차이가 발생할 수 있다.</p>
<br />

<h2 id="1️⃣-fcp--lcp">1️⃣ FCP / LCP</h2>
<h3 id="✏️-측정-시점">✏️ 측정 시점</h3>
<ul>
<li><p><strong>FCP (First Contentful Paint)</strong>
: 브라우저가 요청을 보낸 시점부터 DOM의 첫 번째 콘텐츠가 화면에 렌더링되는 시점</p>
</li>
<li><p><strong>LCP (Largest Contentful Paint)</strong>
: 브라우저가 요청을 보낸 시점부터 가장 큰 콘텐츠 요소가 화면에 렌더링되는 시점</p>
</li>
</ul>
<br />

<h3 id="✏️-fcp가-줄어들면-lcp가-줄어들까-">✏️ FCP가 줄어들면 LCP가 줄어들까 ?</h3>
<p>DOM의 첫 번째 콘텐츠가 화면에 렌더링되는 시점(FCP)이 개선된다면 가장 큰 콘텐츠 요소가 화면에 렌더링되는 시점(LCP) 자체를 개선하지 않더라도, 초반 렌더링 속도가 개선됨에 따라 가장 큰 콘텐츠 요소가 렌더링되는 시점에도 영향을 미칠 것이다.</p>
<blockquote>
<p>아래 그림을 보면 이해하기 쉽다.</p>
</blockquote>
<p>핵심은 FCP와 LCP를 따로 생각하는 것이 아니라, 아래와 같이 연결되어 있는 모습으로 생각해보자.</p>
<p><img src="https://velog.velcdn.com/images/aroo_ming/post/718aa88e-6bf9-4963-a90b-052594565015/image.png" alt="FCP와 LCP"></p>
<p>FCP가 줄면, LCP가 줄어들 수 밖에 없다.</p>
<p>그럼에도 예외는 있다.
FCP와 관계없이 LCP 자체에만 문제가 있는 경우라면, FCP를 최적화해도 LCP에 큰 영향을 미치지는 않을 것이다. <del>(그렇지만 이런 경우는 많지 않다고 생각한다.)</del></p>
<br />


<h2 id="2️⃣-tbt">2️⃣ TBT</h2>
<h3 id="✏️-측정-시점-1">✏️ 측정 시점</h3>
<ul>
<li><strong>TBT (Total Blocking Time)</strong>
: 마우스 클릭, 화면 탭, 또는 키보드 누르기와 같은 사용자 입력에 페이지가 응답하지 못하는 총 시간
: DOM의 첫 번째 콘텐츠가 화면에 렌더링되는 시점(FCP)부터 상호작용 시작 시점(TTI/ Time To Interective) </li>
</ul>
<br />


<h2 id="3️⃣-react와-nextjs-프로젝트의-성능-지표-차이">3️⃣ React와 Next.js 프로젝트의 성능 지표 차이</h2>
<h3 id="✏️-react와-nextjs의-fcplcp와-tbt-비교">✏️ React와 Next.js의 FCP/LCP와 TBT 비교</h3>
<ul>
<li>React로 구현한 프로젝트는 FCP, LCP가 느리고 TBT가 빠르다. </li>
<li>Next.js로 구현한 프로젝트는 FCP, LCP가 빠르고 TBT가 느리다. </li>
</ul>
<blockquote>
<p>위와 같은 현상이 나타나는 이유가 뭘까?</p>
</blockquote>
<p>React와 Next.js의 초기 렌더링 방식에 대해 생각해보자.
<code>React는 CSR</code> 방식, <code>Next.js는 SSR</code> 방식으로 초기 렌더링이 진행된다.</p>
<br />

<h3 id="✏️-csr-vs-ssr">✏️ CSR vs SSR</h3>
<h4 id="▶️-csr-client-side-rendering">▶️ <strong>CSR (Client Side Rendering)</strong></h4>
<p>: 첫 화면 렌더링 시, 클라이언트 단에서 모든 JS 번들을 받아오고 이를 처리하기 전까지 아무런 화면도 보여주지 않는다.</p>
<p> =&gt; 초기 렌더링까지 비교적 오랜 시간이 걸리며, <strong>FCP와 LCP가 느리다.</strong>
 =&gt; 하지만 초기에 모든 JS 번들을 불러오기 때문에, <strong>TBT는 빠르다.</strong></p>
<hr />

<h4 id="▶️-ssr-server-side-rendering">▶️ <strong>SSR (Server Side Rendering)</strong></h4>
<p>: JS 번들을 처리하기 전에, 첫 화면에 필요한 HTML을 먼저 화면에 띄워준다.
이 시점에서 HTML은 웹 화면을 보여주는 HTML일 뿐, 이벤트 리스너를 포함하고 있지 않다.
이후, 하이드레이션 과정을 통해 JS(이벤트 리스너 포함)를 연결하여 HTML을 상호작용 가능한 상태로 전환한다.</p>
<p> =&gt; 초기 렌더링까지 비교적 시간이 적게 걸리며, <strong>FCP와 LCP가 빠르다.</strong>
 =&gt; 하지만 초기에 모든 JS 번들을 불러오지 않기 때문에, <strong>TBT와 화면 간 이동 속도가 느리다.</strong></p>
<br />


<h3 id="✏️-nextjs-tbt-개선">✏️ Next.js TBT 개선</h3>
<p>Next.js에서는 동적인 화면을 만들기 위해 실행되는 하이드레이션 시간이 TBT 시간이 된다. 
결국, TBT를 개선하기 위해서는 하이드레이션 시간을 줄이면 된다.</p>
<p>정적인 화면에서는 하이드레이션 과정이 불필요하기 때문에 모든 컴포넌트에서 하이드레이션이 수행될 필요는 없다. 이를 위해 하이드레이션을 수행하지 않는 <code>서버 컴포넌트</code>가 등장했다.
인터랙티브한 화면을 구현해야 한다면 <code>use client</code>를 활용하여 클라이언트 컴포넌트로 만들어주면 된다.</p>
<p>빠른 초기 렌더링이라는 강점은 살리면서, <code>서버 컴포넌트를 적극적으로 활용</code>하여 페이지 간 이동이 자연스럽고 TBT와 같은 성능 저하가 느껴지지 않는다면 Next.js를 효과적으로 활용했다고 할 수 있다.</p>
<blockquote>
<h4 id="▶️-인터렉션이-많은-서비스라면-nextjs를-사용하는게-큰-의미가-있을까">▶️ 인터렉션이 많은 서비스라면 Next.js를 사용하는게 큰 의미가 있을까?</h4>
<p>인터랙션이 많다는 것은 클라이언트 컴포넌트의 비중이 높아지고 하이드레이션이 빈번하게 발생한다는 뜻이다. 이러한 경우, 무거운 React를 사용하는 것과 큰 차이가 없어진다.</p>
</blockquote>
<blockquote>
<h4 id="▶️-인터렉션을-구현하려면-클라이언트-컴포넌트가-필요한-이유">▶️ 인터렉션을 구현하려면 클라이언트 컴포넌트가 필요한 이유</h4>
<p>이벤트 핸들러는 윈도우 객체에 속한다. 이 때 윈도우 객체는 브라우저에만 있기 때문에, 서버에서 처리할 수 없다.
따라서 인터렉션을 구현하기 위해서는 클라이언트 컴포넌트가 필요하다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[리렌더링 최적화 (feat. memoization)]]></title>
            <link>https://velog.io/@aroo_ming/%EB%A6%AC%EB%A0%8C%EB%8D%94%EB%A7%81-%EC%B5%9C%EC%A0%81%ED%99%94-feat.-memoization</link>
            <guid>https://velog.io/@aroo_ming/%EB%A6%AC%EB%A0%8C%EB%8D%94%EB%A7%81-%EC%B5%9C%EC%A0%81%ED%99%94-feat.-memoization</guid>
            <pubDate>Fri, 03 Jan 2025 08:09:59 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/eassy/post/6752e1fc-d7db-4239-8c1b-37ba0eb041a5/jjv1Rj.gif" alt=""></p>
<blockquote>
<p>리렌더링 최적화를 하기 위해서는 먼저 리렌더링이 발생하는 조건을 알아야 한다.</p>
</blockquote>
<h2 id="1️⃣-리렌더링-발생-조건">1️⃣ 리렌더링 발생 조건</h2>
<h4 id="1-state가-정의된-컴포넌트에서-state가-변경되었을-때">1. state가 정의된 컴포넌트에서 state가 변경되었을 때</h4>
<ul>
<li>값이 변화했을 때 리렌더링을 발생시키기 위해 state를 사용하는 것이기 때문에, 이는 너무나 당연한 이야기이다.</li>
<li>만약 리렌더링을 의도하지 않았다면, useRef를 사용하면 된다.</li>
</ul>
<h4 id="2-컴포넌트에서-받는-props의-값이-변경되었을-때">2. 컴포넌트에서 받는 props의 값이 변경되었을 때</h4>
<ul>
<li>props로 받는 값을 항상 최신 상태로 유지해야 하기 때문에, 이 또한 너무나도 당연한 이야기이다.</li>
</ul>
<h4 id="3-컴포넌트에서-받는-props의-부모-컴포넌트가-렌더링되었을-때">3. 컴포넌트에서 받는 props의 부모 컴포넌트가 렌더링되었을 때</h4>
<ul>
<li><em>이 상황에서도 당연하게 리렌더링이 발생한다고 할 수 있을까?</em></li>
</ul>
<br />    


<h2 id="2️⃣-메모이제이션">2️⃣ 메모이제이션</h2>
<ul>
<li><p>계산 결과를 메모리에 저장해두고 동일한 계산이 요청될 때, <strong>계산을 다시 요청하지 않고 저장된 값을 반환하는 최적화 기법</strong>이다.</p>
</li>
<li><p>메모이제이션을 하는 대상에 따라 크게 3가지 방법으로 나눌 수 있다.</p>
<ul>
<li><code>React.memo</code>: 컴포넌트를 메모이제이션</li>
<li><code>useMemo</code>: 연산의 결과 값을 메모이제이션</li>
<li><code>useCallback</code>: 연산을 수행하는 함수를 메모이제이션</li>
</ul>
</li>
</ul>
<br /> 

<h3 id="✏️-reactmemo">✏️ React.memo</h3>
<ul>
<li><p><code>컴포넌트</code>를 메모이제이션하는 고차 함수</p>
</li>
<li><p>React에서 제공하는 고차 컴포넌트(HOC)로, <strong>컴포넌트 렌더링 결과를 메모이징</strong>하여 성능을 최적화한다.</p>
</li>
<li><p>해당 컴포넌트가 받는 props가 동일한지 <code>얕은 비교</code>를 통해 검사를 진행한다.</p>
<ul>
<li>props 값이 이전 props 값과 동일하다면 &gt; 부모 컴포넌트가 리렌더링 되어도 자식 컴포넌트는 리렌더링되지 않는다.</li>
<li>props 값이 이전 props 값과 다르다면 &gt; 부모 컴포넌트가 리렌더링 되면, 자식 컴포넌트도 리렌더링 된다. (이 때, 부모 컴포넌트에서 관리하는 state 값이 변경되어도 리렌더링이 발생한다.)<pre><code class="language-javascript">const SomeComponent = React.memo(function
 SomeComponent(props) {
    // …
 });</code></pre>
</li>
</ul>
</li>
</ul>
<br /> 


<ul>
<li>두 번째 인자로 커스텀 비교 함수를 받아 <code>깊은 비교</code>를 통해 검사를 진행할 수 있다.<ul>
<li>이전 props(prevProps)와 새로운 props(nextProps)를 인자로 받는다.</li>
<li>비교 함수가 true를 반환할 경우 &gt; 리렌더링이 발생한다.</li>
<li>비교 함수가 false를 반환할 경우 &gt; 리렌더링이 발생하지 않는다.<pre><code class="language-javascript">function areEqual(prevProps, nextProps) {
/*
 nextProp가 prevProps와 동일한 값을 가지면 true를 반환하고, 그렇지 않다면 false를 반환
*/
}
export default React.memo(MyComponent, areEqual);</code></pre>
</li>
</ul>
</li>
</ul>
<br /> 


<h4 id="🚀-주의-사항-참조-동일성-유지의-필요성">🚀 주의 사항 (참조 동일성 유지의 필요성)</h4>
<ul>
<li>컴포넌트의 props가 이전 props 값과 동일한지 <code>얕은 비교</code>를 통해 검사하기 때문에, 객체나 배열, 함수 같은 참조 타입의 props를 검사할 때 문제가 발생할 수 있다.</li>
<li>예를 들어, 객체와 함수를 props로 받는 경우 객체와 함수는 참조 타입이기 때문에, 매 리렌더링마다 내용은 같지만 참조가 달라져 props가 변경되었다고 판단 &gt; 리렌더링이 발생한다.</li>
</ul>
<br /> 

<h3 id="✏️-usememo">✏️ useMemo</h3>
<ul>
<li><p>연산의 <code>결과 값</code>을 메모이제이션하여 성능을 최적화하기 위한 훅</p>
</li>
<li><p>useMemo 사용 시, 값의 참조 동일성을 유지할 수 있다.</p>
</li>
<li><p>첫 번째 인자로 메모이제이션할 연산, 두 번째 인자로 의존성 배열을 받는다.
의존성 배열에 포함된 값이 변경되지 않으면 이전 결과 값을 재사용한다. (useEffect와 유사한 구조)</p>
<pre><code class="language-javascript"> function School (props) {
    const name = useMemo(() ⇒ {
       return {
          lastname: ‘서’,
          firstname: ‘아름’
       };
 }. []);</code></pre>
</li>
<li><p>useMemo를 활용하여 값을 할당하면 <strong>의존성 배열에 포함된 값이 변경될 경우에만 재할당</strong>이 일어나기 때문에, 값의 참조 동일성을 보장한다.</p>
</li>
<li><p>의존성 배열이 비어있는 경우, 초기 렌더링 시에만 첫 번째 인자에 해당하는 연산을 수행한다.</p>
</li>
</ul>
<br /> 

<h3 id="✏️-usecallback">✏️ useCallback</h3>
<ul>
<li><p><code>함수</code>를 메모이제이션하여 성능을 최적화하기 위한 훅</p>
</li>
<li><p>useCallbck 사용 시, 함수의 참조 동일성을 유지할 수 있다.</p>
</li>
<li><p>첫 번째 인자로 메모이제이션할 연산, 두 번째 인자로 의존성 배열을 받는다.
의존성 배열에 포함된 값이 변경되지 않으면 이전 함수를 재사용한다. (useMemo와 동일한 구조)</p>
</li>
</ul>
<br /> 

<h4 id="🚀-흔한-오해">🚀 흔한 오해</h4>
<ul>
<li><p>useCallback으로 메모이제이션한 함수는 의존성 배열에 포함된 값이 변하지 않을 경우, 리렌더링 시 새롭게 생성되지 않는다고 생각하기 쉽다.</p>
</li>
<li><p>정말 그럴까??  ❌❌</p>
</li>
<li><p>useCallback의 <strong>인자로 넘겨주는 함수는 매 렌더링마다 반드시 다시 생성</strong>된다.</p>
</li>
<li><p>이후, 의존성 배열을 검사하여 이전 함수와 새롭게 생성된 함수가 다르다면 생성한 함수를 리턴해주고,
변하지 않았다면 기존에 메모이징했던 함수를 재사용한다 !!</p>
</li>
</ul>
<br />


<h2 id="❓-usememo-usecallback은-reactmemo-없이는-렌더링-최적화에-아무런-효과가-없을까-">❓ useMemo, useCallback은 React.memo 없이는 렌더링 최적화에 아무런 효과가 없을까 ?</h2>
<blockquote>
<p>React.memo로 컴포넌트 리렌더링을 최적화한 뒤, 내부적으로 useCallback과 useMemo를 활용하여 연산된 값이나 함수를 메모이징할 수 있다.
여기서 React.memo만 제거해보자.</p>
</blockquote>
<blockquote>
<p>기존에 리렌더링 최적화를 해둔 부분에서 다시 리렌더링이 발생하는 것을 확인할 수 있다.
그렇다면 useMemo, useCallback은 React.memo 없이는 렌더링 최적화에 아무런 효과가 없는걸까?</p>
</blockquote>
<ul>
<li><p>React.memo는 컴포넌트 메모이제이션을 수행한다.
이러한 역할이 사라지니 더이상 컴포넌트가 메모이제이션 되지 않아 기존에 최적화해두었던 부분에서 모두 다시 리렌더링이 발생하는 것이다.</p>
</li>
<li><p>해당 질문에 대한 답은 React의 리렌더링 과정을 이해하면 알 수 있다.</p>
</li>
</ul>
<br />


<h3 id="✏️-react의-리렌더링-과정">✏️ React의 리렌더링 과정</h3>
<h4 id="▶️-render-phase">▶️ <strong>Render Phase</strong></h4>
<ul>
<li>재조정 과정을 거쳐 Virtual DOM과 Real DOM을 비교, 변경이 필요한 부분을 확인한다.</li>
</ul>
<h4 id="▶️-commit-phase">▶️ <strong>Commit Phase</strong></h4>
<ul>
<li><p>Render Phase에서 체크해뒀던 변경이 필요한 부분들을 Real DOM에 반영하는 단계이다.</p>
</li>
<li><p>변경이 필요한 부분이 없다면, Commit Phase는 스킵한다.</p>
</li>
</ul>
<h4 id="▶️-usememo-usecallback은-유지-reactmemo를-삭제한-후-react의-리렌더링-과정">▶️ <strong>useMemo, useCallback은 유지/ React.memo를 삭제한 후, React의 리렌더링 과정</strong></h4>
<ol>
<li><p>부모 컴포넌트가 리렌더링 되고, 이후 자식 컴포넌트도 리렌더링 되면서 각각의 Render Phase가 실행된다.</p>
</li>
<li><p>useMemo와 useCallback을 통해 자식 컴포넌트들의 props 값(참조)을 이전 props 값(참조)와 동일하게 유지한다.</p>
</li>
<li><p>Rendar Phase내의 재조정 과정에서 Real DOM에 변경이 필요한 부분이 없다고 판단한다.
따라서, Commit Phase는 실행되지 않는다.</p>
</li>
<li><p>Commit Phase는 막았지만 Render Phase가 여전히 실행된다.</p>
</li>
<li><p>최적화를 하긴 했는데, 눈으로 보기에는 최적화를 진행하기 전과 거의 동일하게 동작한다. 😱</p>
</li>
</ol>
<h4 id="▶️-결론-render-phase까지-막기-위해-사용하는-것이-reactmemo이다-">▶️ 결론: Render Phase까지 막기 위해 사용하는 것이 <code>React.memo</code>이다 !!</h4>
<br />


<h2 id="❓-usememo-usecallback-reactmemo를-많이-사용하면-좋을까">❓ useMemo, useCallback, React.memo를 많이 사용하면 좋을까?</h2>
<ul>
<li><p>React.memo는 컴포넌트 변경 여부를 검사하기 위해 매 렌더링마다 얕은 비교를 수행한다.</p>
</li>
<li><p>useMemo, useCallback 역시 매 렌더링 Phase마다 의존성 배열 검사 및 캐시 관리를 수행한다.</p>
</li>
<li><p>이런 모든 것들이 비용이기 때문에, 리렌더링을 최적화한 효과보다 다른 부분에서 오히려 성능 저하를 일으킬 수 있다.</p>
</li>
<li><p>결국.. 참조 동일성을 반드시 유지해야 하는 상황이더라도, 메모이제이션을 적용하는 것이 항상 정답이라고 말할 수는 없다.</p>
</li>
</ul>
<br />


<h2 id="3️⃣-마무리">3️⃣ 마무리</h2>
<p>최적화에는 정답이 없다 ..! 최적화 기준을 잘 세우는 것, 그리고 단면만 보지 말고 전체적인 성능을 고려하여 최적의 선택을 하는 것이 중요하다.</p>
<p>어디선가, 아래와 같은 말을 들은 적이 있다. <del>정말 띵언</del></p>
<blockquote>
<p>&quot;최적화를 언제 하면 좋을까요??&quot;</p>
</blockquote>
<ol>
<li>하면 좋을 것 같아요. &gt; <strong>하지 마세요.</strong></li>
<li>쓰읍.. 해야할 것 같은데 .. &gt;  <strong>한 번 더 생각해보세요.</strong></li>
<li>진짜 진짜 진짜 무조건 해야된다 !!! &gt; <strong>프로파일링(측정)부터 진행해보세요.</strong></li>
</ol>
<p>항상 모든 것에는 trade-off가 있기 때문에, 최적화를 해서 특정 시간을 줄였다면 결국 어떤 것 <del>(메모리, 코드의 양, 개발자의 시간 등등)</del>을 희생했다는 이야기가 된다.</p>
<p>결국 어떤 것을 희생할지 잘 생각해보고, 최적화로 인해 발생하는 불필요한 비용을 최소화하는 것이 중요하다.</p>
<br />

<hr />

<h4 id="📚-참고자료">📚 참고자료</h4>
<p><a href="https://www.youtube.com/watch?v=WTY0QTv27dk">메모이제이션을 활용한 리렌더링 최적화</a>
<a href="https://velog.io/@fltxld3/React-React.memo%EB%A1%9C-Rendering-%EC%B5%9C%EC%A0%81%ED%99%942-feat.%EC%9D%BC%EA%B8%B0%EC%9E%A5#%EC%82%AC%EC%9A%A9%EB%B0%A9%EB%B2%95%EC%96%95%EC%9D%80%EB%B9%84%EA%B5%90">[React] React.memo로 Rendering 최적화2 (feat.예제1,2)</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[🚨 번들러 에러 해결하기 (React/Vite) 🚨]]></title>
            <link>https://velog.io/@aroo_ming/Refused-to-apply-style-from-.css-because-its-MIME-type-texthtml-is-not-a-supported-stylesheet-MIME-type-and-strict-MIME-checking-is-enabled</link>
            <guid>https://velog.io/@aroo_ming/Refused-to-apply-style-from-.css-because-its-MIME-type-texthtml-is-not-a-supported-stylesheet-MIME-type-and-strict-MIME-checking-is-enabled</guid>
            <pubDate>Thu, 12 Sep 2024 08:57:01 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/aroo_ming/post/4d5b8601-be93-4e85-875f-6ead1da88988/image.png" alt="충격"></p>
<blockquote>
<p>Refused to apply style from &#39;<del>~</del>.css&#39; because its MIME type (&#39;text/html&#39;) is not a supported stylesheet MIME type, and strict MIME checking is enabled. 
로컬에선 잘만 돌아가던 코드가 배포 사이트에서 에러를 뱉기 시작 . .</p>
</blockquote>
<p>언젠가 다시 발생할 수도 있는 에러이기에 또 이런 에러를 만났을 미래의 나를 위한..
이런 에러와 싸우고 있을 누군가를 위한.. 트러블 슈팅 🔥</p>
<br />

<h2 id="💬-혹시-깃허브-로그인이-문제-">💬 혹시 깃허브 로그인이 문제 ?</h2>
<p>우리 서비스에는 깃허브 로그인 기능이 구현되어있는데, 깃허브 보안때문에 배포 사이트에서 문제가 생기는 건가 의심을 했다.
현재 서비스 배포가 Cloudflare로 이뤄지고 있었기 때문에 정확한 문제 확인을 위해 서비스 레포를 포그 떠서 개인 레포로 가져온 뒤, 버셀로 배포를 해서 버셀 배포본으로 서비스에 접근해보았다.</p>
<p>그러나 여전히 같은 에러 발생 ..!!! 🤬</p>
<br />

<h2 id="🧐-에러-메시지로-다시-돌아가보자">🧐 에러 메시지로 다시 돌아가보자</h2>
<p><code>Refused to apply style from &#39;~~~.css&#39; because its MIME type (&#39;text/html&#39;) is not a supported stylesheet MIME type, and strict MIME checking is enabled.</code></p>
<h4 id="메시지를-보면-어떤-파일을-못-찾고-있다고-하는데-이걸-자세히-보면-번들-파일인-걸-알-수-있다">메시지를 보면 어떤 파일을 못 찾고 있다고 하는데.. 이걸 자세히 보면! 번들 파일인 걸 알 수 있다</h4>
<p>➡️ 에러 메시지에 찾을 수 없다고 나온 파일 명이 dist &gt; asset &gt; js, css 파일 명과 동일
➡️ js 번들 파일 하나, css 번들 파일 하나
➡️ 아..! 지금 번들 파일을 못 찾고 있는 것이구나 !! (깨달음)</p>
<br />

<h3 id="💥-문제를-찾은-것만-같다-">💥 문제를 찾은 것만 같다 ..!</h3>
<p>앞서 적은 것처럼 로컬에선 잘 동작하던 코드가 배포본에서만 제대로 동작하지 않는 이유에 대해 궁금했었는데, 번들러 관련 에러라면 설명이 가능했다 !</p>
<p>로컬에선 빌드를 하지 않기 때문에 오류가 발생하지 않았고, 배포 환경에선 빌드 후 번들링 과정을 수행하기 때문에 오류가 발생할 수 밖에 없던 것이다..!!</p>
<hr />

<p>dist를 기준으로 배포를 하니까 번들 파일 경로는 dist를 root로 생각한다. 
정상적인 하위 주소는 <strong>/asset/번들파일</strong>이 되고, 번들 파일 요청은 <strong>배포주소/asset/번들파일</strong>로 들어가야 정상적으로 파일을 찾을 수 있다.</p>
<br />

<h2 id="👀-그렇다면-왜-특정-주소에서만-에러가-발생했을까-">👀 그렇다면 왜 특정 주소에서만 에러가 발생했을까 !?</h2>
<p>우리의 배포 사이트 기준, 홈 화면에서는 위에서 말한 정상적인 주소(배포주소/asset/번들파일)로 접근하여 번들 파일을 잘 불러오고 있었다.</p>
<p>그러나 특정 주소로 넘어갔을 경우에는 &#39;RequestURL: 배포주소 + 특정 주소/asset/번들파일&#39;에 접근하여 번들 파일을 요청했다.</p>
<p><strong>결국, 비정상적인 경로에서 번들 파일을 찾으려고 해서 에러가 발생했던 것 ..!!! 🌟🌟</strong></p>
<br /> 


<h2 id="🫵-이런-사단이-난-이유-">🫵 이런 사단이 난 이유 !</h2>
<p>➡️ <strong>vite.cofig.js의 base 필드</strong>를 주목하자 🌟</p>
<p>vite.cofig.js의 base 필드를 지정하면 상대 경로로 번들 위치를 잡게 되는데, 홈으로 접근했을 때는 서브주소가 없기 때문에 <strong>&quot;배포주소 + /&quot;를 루트로</strong> 보고 정상적인 번들러 위치에 접근한다.</p>
<p>하지만, <strong>서브 주소</strong>로 리다이렉트 된 경우에는 <strong>&quot;배포주소 + /서브 주소&quot;를 루트로</strong> 보고 해당 주소를 기준으로 번들을 찾으려고 하기 때문에, 에러가 발생한 것이다.</p>
<br /> 

<h2 id="😻-해결-방법">😻 해결 방법</h2>
<h4 id="➡️-vitecofigjs의-base-필드-자체를-지워버리기-">➡️ vite.cofig.js의 base 필드 자체를 지워버리기 !</h4>
<p>base 필드를 지우면 무조건 루트 경로를 절대 경로로 생각하고 번들 위치를 잡기 때문에, 오류를 해결할 수 있다 !!
(루트 경로를 절대 경로로 생각하고 번들 위치를 잡게 되면, 어떠한 서브 URL에 접근하더라도 <strong>항상 동일한 위치에 번들이 있음을 보장</strong> 가능 🌟🌟)</p>
<p><img src="https://velog.velcdn.com/images/aroo_ming/post/52650d07-3ce1-4de8-b7d5-c880a4ac4d23/image.png" alt="게비스콘"></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[GraphQL을 알아보자]]></title>
            <link>https://velog.io/@aroo_ming/GraphQL%EC%9D%84-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90</link>
            <guid>https://velog.io/@aroo_ming/GraphQL%EC%9D%84-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90</guid>
            <pubDate>Wed, 24 Apr 2024 10:04:22 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-graphql">📌 GraphQL</h1>
<ul>
<li>기존 REST API 한계점을 극복하기 위해 Meta(전 facebok)가 고안한 쿼리 언어</li>
</ul>
<h2 id="☑️-rest-api와-차이점">☑️ REST API와 차이점</h2>
<h3 id="▶️-overfetching">▶️ Overfetching</h3>
<ul>
<li><p><strong>REST API</strong></p>
<ul>
<li>일부 속성만 필요한 경우에도 전체 데이터를 불러온다.</li>
<li>서비스 크기가 커질수록 다루는 양의 크기가 커질 경우 서버 부하, 데이터 전송 속도 및 크기 등 Overfetching으로 인한 이슈가 발생할 수 있다. <del>(클라이언트 입장에서는 필요한 데이터만 골라서 사용하면 되긴 하지만)</del></li>
</ul>
</li>
<li><p><strong>Graph QL</strong></p>
<ul>
<li><p>필요한 필드만을 명시적으로 요청하여 필요한 정보들만 선택하여 받아올 수 있다.</p>
<pre><code class="language-graphql">  query {
      teams {
          id
          manager
          mascot
      }
  }</code></pre>
</li>
<li><p>전체 데이터를 받아오지 않고 필요에 따라 원하는 데이터만 받아오기 때문에, 데이터 전송량이 감소한다. 이로써 REST API에서 언급했던 Overfetching 이슈를 해결할 수 있다.</p>
</li>
</ul>
</li>
</ul>
<h3 id="▶️-underfetching">▶️ Underfetching</h3>
<ul>
<li><p><strong>REST API</strong></p>
<ul>
<li>특정 데이터를 받아오기 위해 여러 개의 endpoint를 사용해야 한다. 하나의 데이터를 가져오기 위해 n개의 endpoint를 거치기도 한다. 이는 서버 부하, 네트워크 오버헤드 등을 유발할 수 있다.</li>
</ul>
</li>
<li><p><strong>Graph QL</strong></p>
<ul>
<li><p>중첩된 필드를통해 여러 객체와 관련된 데이터를 한 번에 요청할 수 있다. 이를 통해 하나의 endpoint에서 여러 계층의 정보들을 한 번에 받아올 수 있다.</p>
<pre><code class="language-graphql">  query {
      team(id: 2) {
          id
          manager
          mascot
          members {
              first_name
              last_name
          }
      }

      rolse {
          id
          requirement
      }
  }</code></pre>
</li>
<li><p>하나의 endpoint에서 모든 요청을 해결할 수 있기 때문에, 요청 횟수가 감소한다. 즉, 여러 번 네트워크를 호출할 필요가 없기 때문에 REST API의 Underfetching 이슈를 해결할 수 있다.</p>
</li>
</ul>
</li>
</ul>
<hr>
<h1 id="📌graphql-구조">📌GraphQL 구조</h1>
<h2 id="☑️-쿼리-뮤테이션query-mutation">☑️ 쿼리/ 뮤테이션(Query/ Mutation)</h2>
<h3 id="▶️-query">▶️ Query</h3>
<p>: <strong>정보를 읽어오기 위해</strong> 사용하는 메소드 - CRUD의 <strong>R</strong></p>
<ul>
<li><p><code>쿼리문</code> (요청): 원하는 정보를 쿼리로 요청</p>
<pre><code class="language-graphql">  query {
      user {
          name
      }
  }</code></pre>
</li>
<li><p><code>응답</code>: 쿼리문에 대한 응답이 데이터로 반환</p>
<pre><code class="language-json">  {
      &quot;data&quot; : {
          &quot;user&quot; : {
              &quot;name&quot; : &quot;abcde&quot;
          }
      }
  }
</code></pre>
</li>
</ul>
<h3 id="▶️-mutation">▶️ Mutation</h3>
<p>: 받아온 <strong>정보를 변형하기 위해</strong> 사용하는 메서드 - CRUD의 <strong>CUD</strong></p>
<ul>
<li><p><code>요청 예시</code> - addMovie라는 뮤테이션에 새로운 영화 이름을 추가하는 예시</p>
<pre><code class="language-graphql">  mutation {
      addMovie(name:&quot;Rocks&quot;){
          name
      }
  }</code></pre>
</li>
</ul>
<h3 id="▶️-subscription">▶️ Subscription</h3>
<p>: 실시간으로 변경된 데이터를 가져오기 위한 요청 방식</p>
<p>  웹 소켓을 통해 소켓 통신을 열어두고, 데이터 업데이트 시 알리는 방식으로 이뤄진다.</p>
<h3 id="▶️-스키마와-타입시스템">▶️ 스키마와 타입시스템</h3>
<p>: GraphQL 타입 시스템과 데이터를 표현하는 방법</p>
<hr>
<h1 id="📌-query에-대해-더-자세히-알아보자">📌 Query에 대해 더 자세히 알아보자</h1>
<h2 id="☑️-aliases">☑️ Aliases</h2>
<ul>
<li>필드의 결과를 원하는 이름으로 변경할 수 있다. 중복된 필드를 다른 이름으로 받아올 때 사용할 수 있다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/aroo_ming/post/2af3a246-33e2-4074-bcce-e7d3a1f1b936/image.png" alt=""></p>
<h2 id="☑️-fragments">☑️ Fragments</h2>
<ul>
<li>반복되는 필드의 집합을 하나의 요소로 정의하여 이를 재사용할 수 있다.</li>
<li>같은 필드를 순서대로 요청한다고 가정해보자. 필드를 최소 2번은 반복해야 하기 때문에, 중복되는 코드의 양이 늘어나고 쿼리가 복잡해진다. 이러한 경우 fragment를 사용하면 필드셋을 구성한 다음 필요한 쿼리에 포함시킬 수 있다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/aroo_ming/post/45175c55-a26c-4527-8da2-68e26c14ec0e/image.png" alt=""></p>
<ul>
<li>fragment는 복잡한 응용 프로그램 내에서 데이터 요구사항을 작은 단위로 분할하는데 사용된다.</li>
<li>특히, 청크가 다른 여러 UI 구성 요소를 하나의 초기 데이터 fetch로 통합해야 하는 경우 많이 사용한다.</li>
</ul>
<h2 id="☑️-operation-name">☑️ Operation name</h2>
<ul>
<li><p>Operation type: Query(읽기), Mutation(데이터를 변경하고 가져옴), Subscription(실시간으로 변경된 데이터를 가져옴)</p>
</li>
<li><p>Operation name: 의미있고 명시적인 이름으로 하는 것이, 디버깅이나 서버측에서 로깅하는데 유용하다.</p>
<pre><code class="language-graphql">  # Operation type: query
  # Opertation name: HeroNameAndFriends
  query HeroNameAndFriends {
    hero {
      name
      friends {
        name
      }
    }
  }</code></pre>
</li>
</ul>
<hr>
<h1 id="📌--instrospection">📌  Instrospection</h1>
<ul>
<li>API 명세서 대신 사용할 수 있는 GraphQL 기능</li>
<li>서버 자체에서 현재 서버에 정의된 스키마의 실시간 정보를 공유할 수 있다.</li>
<li>인트로스펙션용 쿼리가 따로 존재하며, 일반 gql문을 작성하듯이 작성하면 된다. 다만, 대부분 서버용 gql 라이브러리에는 쿼리용 IDE를 제공하기  때문에, 실제로는 스키마 인트로스펙션을 위해 ql 쿼리 문을 작성할 필요는 없다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/aroo_ming/post/6b9540dd-2d97-4ffc-82e7-832f0dc5fc99/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React 최적화]]></title>
            <link>https://velog.io/@aroo_ming/React-%EC%B5%9C%EC%A0%81%ED%99%94</link>
            <guid>https://velog.io/@aroo_ming/React-%EC%B5%9C%EC%A0%81%ED%99%94</guid>
            <pubDate>Fri, 15 Dec 2023 04:42:47 GMT</pubDate>
            <description><![CDATA[<h2 id="🧶-컴포넌트-수준의-최적화">🧶 컴포넌트 수준의 최적화</h2>
<h3 id="🔗-reactmemo">🔗 React.memo</h3>
<ul>
<li>함수 컴포넌트의 결과를 <code>Memoization</code>하는 메소드<ul>
<li><strong>Memoization</strong><ul>
<li>비용이 많이 드는 함수 호출의 결과를 저장</li>
<li>동일한 입력 발생 시, 재사용 → 성능 향상에 사용됨.</li>
</ul>
</li>
</ul>
</li>
<li>props가 변경되지 않은 경우에만 재사용하는 고차 컴포넌트</li>
</ul>
<pre><code class="language-jsx">// 최적화 전
function My(props) {
    return (
        &lt;div&gt;
            {props.data}
        &lt;/div&gt;
    )
}
function App() {
    const [state, setState] = useState(0)
    return (
        &lt;&gt;
            &lt;button onClick={()=&gt; setState(0)}&gt;Click&lt;/button&gt;
            &lt;My data={state} /&gt;
        &lt;/&gt;
    )
}</code></pre>
<ul>
<li>버튼을 계속 누를 경우, state 값이 동일함에도 불구하고 My 컴포넌트는 계속해서 리렌더링 됨.<ul>
<li>만약 App과 My 컴포넌트 하위로 수천개의 컴포넌트들이 존재한다면 …? → 엄청난 성능이슈 발생 ; ;</li>
</ul>
</li>
<li>리렌더링을 줄이기 위해 React.memo(My 컴포넌트를 memoization된 버전으로 리턴)를 활용해보자.</li>
</ul>
<br />


<pre><code class="language-jsx">// React.memo()를 활용한 최적화 코드
function My(props) {
    return (
        &lt;div&gt;
            {props.data}
        &lt;/div&gt;
    )
}

// React.memo() 활용
const MemoedMy = React.memo(My)
function App() {
    const [state, setState] = useState(0)
    return (
        &lt;&gt;
            &lt;button onClick={()=&gt; setState(0)}&gt;Click&lt;/button&gt;
            &lt;MemeodMy data={state} /&gt;
        &lt;/&gt;
    )
}</code></pre>
<ul>
<li>버튼을 연속적으로 클릭하더라도 My 컴포넌트는 한 번만 렌더링된 후, 다시 렌더링 ❌!<ul>
<li>React.memo()가 props 값을 memoization한 후, 캐싱된 결과를 리턴하기 때문에 동일한 입력 값에 대해 My 컴포넌트를 실행하지 않음.</li>
</ul>
</li>
</ul>
   <br />

<h3 id="🔗-usememo">🔗 useMemo</h3>
<ul>
<li>React에서 CPU 소모가 심한 함수들을 캐싱하기 위해 사용됨.</li>
<li>계산 비용이 많이 드는 작업에 유용함.</li>
</ul>
<pre><code class="language-jsx">// 최적화 전
function App() {
    const [count, setCount] = useState(0)

    const expFunc = (count)=&gt; {
        waitSync(3000);
        return count * 90;
    }
    const resCount = expFunc(count)
    return (
        &lt;&gt;
            Count: {resCount}
            &lt;input type=&quot;text&quot; onChange={(e)=&gt; setCount(e.target.value)} placeholder=&quot;Set Count&quot; /&gt;
        &lt;/&gt;
    )
}</code></pre>
<ul>
<li>어떠한 입력값이 들어올 때마다 컴포넌트가 다시 렌더링되어 expFunc이 호출됨.<ul>
<li>대규모 성능 <code>병목 현상</code> 유발<ul>
<li><strong>병목 현상</strong>: 전체 시스템의 성능이나 용량이 하나의 구성요소로 인해 제한받는 현상</li>
</ul>
</li>
</ul>
</li>
</ul>
 <br />


<pre><code class="language-tsx">// useMemo()를 활용한 최적화 코드
function App() {
    const [count, setCount] = useState(0)

    const expFunc = (count)=&gt; {
        waitSync(3000);
        return count * 90;
    }

    // useMemo() 활용
    const resCount = useMemo(()=&gt; {
        return expFunc(count)
    }, [count])
    return (
        &lt;&gt;
            Count: {resCount}
            &lt;input type=&quot;text&quot; onChange={(e)=&gt; setCount(e.target.value)} placeholder=&quot;Set Count&quot; /&gt;
        &lt;/&gt;
    )
}</code></pre>
<ul>
<li>expFunc은 입력값 캐싱<ul>
<li>동일한 입력 발생 시, useMemo는 expFunc를 호출하지 않고 입력된 값에 대해 캐싱된 결과 값을 리턴함!</li>
</ul>
</li>
</ul>
<br />


<h3 id="🔗-usecallback">🔗 useCallback</h3>
<ul>
<li>useMemo와 비슷하지만, 함수 선언 자체를 memoization하는데 사용된다는 차이가 있음.</li>
</ul>
<pre><code class="language-jsx">function App() {
    const check = 90
    const [count, setCount] = useState(0)

    // useCallback() 활용
    const clickHndlr = useCallback(()=&gt; { setCount(check) }, [check]);
    return (
        &lt;&gt;
            &lt;button onClick={()=&gt; setCount(count + 1)}&gt;Set Count&lt;/button&gt;
            &lt;TestComp func={clickHndlr} /&gt;
        &lt;/&gt;
    )
}</code></pre>
<ul>
<li>clickHndlr는 dependenccy인 check의 값이 변하지 않는 한, App 컴포넌트가 리렌더링 되어도 새로 생성되지 않음.<ul>
<li>즉, setCount 버튼을 반복해서 클릭하더라도 TestComp는 리렌더링 되지 않음.</li>
</ul>
</li>
<li>useCallbacck이 check 값을 확인<ul>
<li>이전 값과 다른 경우: 새로운 함수를 리턴 + 새로운 참조가 되었으므로 리렌더링됨.</li>
<li>이전 값과 동일한 경우: 아무 것도 리턴 ❌ + 함수 참조가 이전과 같다고 판단하여 리렌더링 ❌</li>
</ul>
</li>
</ul>
<br />


<h2 id="🧶-react-내장-성능-기능-활용">🧶 React 내장 성능 기능 활용</h2>
<h3 id="🔗-lazy-loading">🔗 Lazy Loading</h3>
<ul>
<li><p>부하를 단축하기 위해 자주 사용되는 최적화 기법</p>
</li>
<li><p>React에서는 컴포넌트의 Lazy Lodading을 위해 <code>React.lazy()</code>를 사용함.</p>
<ul>
<li><strong>React.lazy()</strong><ul>
<li>React v16.6에 새로 추가된 기능으로, React.lazy()를 사용하면 동적 import를 사용하여 컴포넌트를 렌더링 할 수 있음.</li>
<li>즉, React.lazy()를 사용하면 동적 가져오기를 통해 구성 요소 수준에서 React 애플리케이션을 쉽게 코드 분할할 수 있음.</li>
</ul>
</li>
</ul>
</li>
<li><p>사용하는 이유!</p>
<ul>
<li><p>일반적으로 규모가 큰 React 애플리케이션은 많은 요소, 라이브러리 등으로 구성됨.</p>
</li>
<li><p>애플리케이션의 다른 부분을 로드하려고 노력하지 않을 경우, 사용자가 첫 페이지를 로드하는 즉시 대규모 단일 Javascript 번들이 사용자에게 전송됨 → 페이지 성능에 상당한 영향을 미침.</p>
</li>
<li><p>React.lazy()를 통해 손쉽게 개별 Javascript 청크로 분리해보자!</p>
<br />


</li>
</ul>
</li>
</ul>
<pre><code>1. `React.lazy()`

    ```jsx
    const About = React.lazy(() =&gt; import(&#39;./About&#39;));
    ```

    - import() 구문을 반환하는 콜백 함수를 인자로 받음.
    - 동적으로 불러오는 컴포넌트 파일은 두 가지 규칙을 지켜야 함!
        - React 컴포넌트를 포함해야 함.
        - default export를 가진 컴포넌트여야 함.

 2. `&lt;Suspense /&gt;`

```jsx
&lt;Suspense fallback={&lt;div&gt;Loading...&lt;/div&gt;}&gt;
  &lt;Routes&gt;
    &lt;Route path=&quot;/&quot; element=&lt;About/&gt;} /&gt;
  &lt;/Routes&gt;
&lt;/Suspense&gt;
```

- 컴포넌트가 로드될 때까지 fallback() props에 있는 React 엘리먼트를 띄워줌.
- 비동기 데이터 가져오기와 코드 분할을 더 간단하게 처리하고, 사용자 경험을 개선하기 위한 목적으로 도입된 기능
- React v16.6 이상부터 사용 가능
- 사용 목적
    - `비동기 데이터 가져오기`
    Suspense 사용 시, 컴포넌트에서 비동기 작업을 수행하거나 외부 데이터를 가져오는 동안 화면에 로딩 상태를 표시하여 사용자 경험을 향상시킬 수 있음.
    - `코드 분할`

        Suspense를 사용하면 코드 분할과 함께 앱 번들의 크기를 줄이고 초기 로딩시간을 최적화할 수 있음. 필요한 컴포넌트만 로드하고 사용자가 해당 컴포넌트를 요청할 때까지 다른 컴포넌트를 로드하지 않도록 해야 함.

3. `React Router와 함께 사용`

```jsx
return (
   &lt;Router&gt;
    &lt;Suspense fallback={&lt;div&gt;Loading...&lt;/div&gt;}&gt;
      &lt;Routes&gt;
        &lt;Route path=&quot;/&quot; element=&lt;About/&gt;} /&gt;
      &lt;/Routes&gt;
    &lt;/Suspense&gt;
  &lt;/Router&gt;
);
```

- React 공식문서에 따르면,  Router 바로 아래 Suspense를 위치시키고 React.lazy()는Route로 보여줄 컴포넌트를 불러오는 것을 권장하고 있음.</code></pre><br />


<h2 id="🧶-이외에도-">🧶 이외에도 ..!</h2>
<h3 id="🔗-list-가상화">🔗 List 가상화</h3>
<ul>
<li>React 공식문서를 참고하면, 애플리케이션에서 거대한 List(수백 또는 수천 행)를 렌더링하는 경우 <code>windowing</code>이라는 기법을 사용하는 것을 추천한다고 함.<ul>
<li><strong>windowing</strong><ul>
<li>거대한 List 렌더링 시, 브라우저의 viewport에 보여지는 부분만 렌더링하고 나머지는 스크롤할 때 보여지도록 하는 기법</li>
<li>이를 활용하면 컴포넌트를 리렌더링하는데 걸리는 시간과 생성된 DOM 노드의 수를 크게 줄일 수 있음.</li>
<li>대표적인 windowing 라이브러리: react-window, react-virtualized</li>
</ul>
</li>
</ul>
</li>
</ul>
<br />


<h3 id="🔗-불변성의-중요성">🔗 불변성의 중요성</h3>
<ul>
<li><p>불변성을 지킬 수 있는 가장 간단한 방법은 props와 state로 사용 중인 값을 변경하지 않는 것</p>
</li>
<li><p>concat, spread 문법(shallow copy) 등 활용하기</p>
<ul>
<li><p>concat()</p>
<pre><code class="language-jsx">  handleClick() {
    this.setState(state =&gt; ({
      words: state.words.concat([&#39;marklar&#39;])
    }));
  }</code></pre>
</li>
<li><p>spread syntax</p>
<pre><code class="language-jsx">  handleClick() {
    this.setState(state =&gt; ({
      words: [...state.words, &#39;marklar&#39;],
    }));
  };</code></pre>
</li>
<li><p>Object.assign(), object spread properties</p>
<pre><code class="language-jsx">  // 불변성을 지키지 않은 코드
  function updateColorMap(colormap) {
    colormap.right = &#39;blue&#39;;
  }</code></pre>
<ul>
<li><p>이런 코드를 작성하고 싶다면, 위와 같이 객체 원본을 변경시키지 말고 Object.assign()이나 object spread properties를 활용해보자 !</p>
<pre><code class="language-jsx">// Object.assign() 활용 코드
function updateColorMap(colormap) {
return Object.assign({}, colormap, {right: &#39;blue&#39;});
}</code></pre>
<pre><code class="language-jsx">// object spread properties 활용 코드
function updateColorMap(colormap) {
return {...colormap, right: &#39;blue&#39;};
}</code></pre>
</li>
</ul>
</li>
</ul>
</li>
<li><p>깊게 중첩된 객체를 처리할 때, 불변성을 지키는 방식(얕은 복사)으로 객체를 업데이트하다 보면 굉장히 복잡하고 헷갈린다고 느낄 수 있음.</p>
</li>
<li><p>이런 경우, <code>immer</code>를 활용하면 불변성이 가져다주는 이득을 잃지 않고 가독성있는 코드를 작성할 수 있음.</p>
<ul>
<li><strong>immer</strong>: 상태를 업데이트할 때, 불변성을 관리해주는 라이브러리</li>
</ul>
</li>
</ul>
<br />


<h2 id="🧶-마무리">🧶 마무리</h2>
<ul>
<li>앞서 정리한 방법 외에도 다양한 최적화 방법이 있음.</li>
<li>기능구현 단계에서부터 최적화를 미리 수행하기 보다, 프로젝트를 모두 개발한 후 필요한 곳에 최적화를 적용시키는 것이 중요함 !</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[react-query & asynchronous recoil로 데이터 관리하기]]></title>
            <link>https://velog.io/@aroo_ming/react-query-asynchronous-recoil%EB%A1%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EA%B4%80%EB%A6%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@aroo_ming/react-query-asynchronous-recoil%EB%A1%9C-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EA%B4%80%EB%A6%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 21 Nov 2023 14:59:55 GMT</pubDate>
            <description><![CDATA[<h2 id="🪵-react-query란">🪵 react-query란?</h2>
<p><img src="https://velog.velcdn.com/images/aroo_ming/post/3525e79f-d4e8-487f-a1bd-401f3b626df2/image.jpeg" alt=""></p>
<ul>
<li><code>데이터 fetching</code>, <code>캐싱</code>, <code>동기화</code> , <code>데이터 업데이트</code>를 보다 쉽게 다룰 수 있도록 하는 상태관리 라이브러리</li>
<li>Redux나 MobX와 같은 라이브러이에 비해 러닝커브가 상당히 낮고 보일러플레이트가 적어 편하게 사용 가능함.</li>
<li>react-query를 사용함으로써 클라이언트와 서버 데이터를 명확히 분리할 수 있음.</li>
</ul>
<br />

<h2 id="🪵-react-query--왜-사용할까">🪵 react-query  왜 사용할까?</h2>
<ol>
<li><p><strong>간편한 데이터 관리</strong></p>
<ul>
<li>react 애플리케이션에서 서버의 상태를 불러오고, 캐싱하고, 지속적으로 동기화 및 업데이트를 간편하게 처리할 수 있도록 도와줌.</li>
</ul>
</li>
<li><p><strong>간단하고 직관적으로 API 사용 가능</strong></p>
<ul>
<li>복잡하고 장황한 코드가 필요한 다른 데이터 패칭 방식과 달리, react component 내부에서 간단하고 직관적으로 API를 사용할 수 있음.</li>
</ul>
</li>
<li><p><strong><code>핵심로직</code> 에 집중 가능</strong></p>
<ul>
<li><p>Redux에는 기본 원칙이 존재하기 때문에, 이 원칙을 충족하기 위해 장황한 양의 보일러플레이트 코드가 요구됨.</p>
<ul>
<li><p>간단한 로직을 짜는데도 갖춰야 할 코드의 양이 너무 많아 핵심 로직에 집중하기 힘듦.</p>
<p>  <del>(실제로 보면 그 양이 정말… 어마무시 …)</del></p>
</li>
</ul>
</li>
<li><p>캐싱, Window Focus Refetching 등 다양한 기능을 제공함.</p>
<ul>
<li>이를 활용하여 API 요청과 관련된 번잡한 작업없이 핵심로직 자체에 집중할 수 있음.</li>
</ul>
</li>
</ul>
</li>
</ol>
<br />


<h2 id="🪵-react-query-알아보기">🪵 react-query 알아보기</h2>
<h3 id="1️⃣-간단한-코드로-비교해보기">1️⃣ 간단한 코드로 비교해보기</h3>
<p>🌱 <strong>react-query를 사용하지 않은 기존 데이터 패칭 코드</strong></p>
<ul>
<li>하나의 데이터를 패칭할 뿐인데 꽤 많은 양의 코드를 작성해야 함.</li>
<li>useEffect를 사용하기 때문에, 이로 인해 side effect 발생 → 코드 흐름 파악이 어려워질 수 있음.</li>
</ul>
<pre><code class="language-jsx">// 1. 데이터 패칭 코드 작성
const getNikaData = async () =&gt; {
  const data = await fetch(&quot;https://musma.net/user/nika&quot;)
        .then((response) =&gt; response.json());
  return data;
};

// 2. 데이터를 담을 상태 생성
const [nika, setNika] = useState();

// 3. useEffecct를 이용하여 컴포넌트 마운트 시, 데이터 패칭 후 상태 저장
useEffect(() =&gt; {
    getNikaData()
      .then((res) =&gt; setState(res))
      .catch((err) =&gt; {
                console.log(err.response.data)
            });
  }, []);</code></pre>
<br />

<p><strong>🌱 react-query를 사용한 코드</strong></p>
<ul>
<li>라이브러리를 사용하기 전보다 훨씬 코드의 양이 적어짐.<ul>
<li>여기서 데이터 패칭 로직까지 분리하게 된다면, 컴포넌트에 포함된 코드의 양은 더 적어지게 됨.</li>
</ul>
</li>
<li>useEffect를 사용하지 않으면서 코드 흐름을 파악하기 비교적 쉬워짐.</li>
</ul>
<pre><code class="language-jsx">// 1. 데이터 패칭 코드 작성
const getNikaData = async () =&gt; {
  const data = await fetch(&quot;https://musma.net/user/nika&quot;)
        .then((response) =&gt; response.json());
  return data;
};

// 데이터 패칭 및 상태 저장
const { data } = useQuery([&quot;nika&quot;], getNikaData);</code></pre>
<br />

<h3 id="2️⃣-query--mutation">2️⃣ Query &amp; Mutation</h3>
<ul>
<li>react-query는 API 요청을 Query와 Mutation으로 처리함.</li>
</ul>
<p>🌱 <strong>Query - useQuery</strong></p>
<ul>
<li>query 요청은 <code>useQuery</code> 훅으로 진행됨.</li>
<li>query 함수를 사용해서 데이터를 가져오고 캐싱할 수 있음.<ul>
<li>query 함수는 대부분 데이터 패칭용으로 사용되며, <code>GET</code>으로 받아올 때 주로 <code>useQuery</code> 사용.</li>
</ul>
</li>
<li>HTTP Method GET 요청과 같이 서버에 저장되어 있는 <code>상태</code> 를 불러와 사용할 때 용이</li>
<li>useQuery는 <code>비동기</code>로 작동함.<ul>
<li>하나의 컴포넌트에 여러 개의 useQuery 존재 → 두 개의 useQuery가 동시에 실행 !<ul>
<li>하나가 끝날 때까지 기다렸다가 다음 useQuery가 실행되는 것 ❌!!</li>
</ul>
</li>
<li><code>enabled</code> 옵션을 사용할 경우, 동기적으로 사용 가능<ul>
<li>useQuery에 3번째 인자로 enabled 옵션을 넣어줄 경우, enabled 값이 true일 때 해당 useQuery를 실행 → 동기적으로 함수 실행 가능</li>
</ul>
</li>
</ul>
</li>
</ul>
<pre><code class="language-jsx">// 가장 기본적인 형태의 React Query useQuery Hook 사용 예시
const { data } = useQuery(
  queryKey, // 이 Query 요청에 대한 응답 데이터를 캐시할 때 사용할 Unique Key (required)
  fetchFn, // 이 Query 요청을 수행하기 위한 Promise를 Return 하는 함수 (required)
  options, // useQuery에서 사용되는 Option 객체 (optional)
);</code></pre>
<ul>
<li>요청마다 구분되는 <code>Unique Key</code> 를 반드시 필요로 함.</li>
<li>Unique Key로 api response를 로컬에 캐시하고 관리함.</li>
</ul>
<pre><code class="language-jsx">function Users() {
  const user = useQuery(
    [&#39;userInfo&#39;], // &#39;userInfo&#39;를 Key로 사용하여 데이터 캐싱
    // 다른 컴포넌트에서 &#39;userInfo&#39;를 QueryKey로 사용한 useQuery Hook이 있다면 캐시된 데이터를 우선 사용
    () =&gt; axios.get(&#39;/users&#39;).then(({ data }) =&gt; data),
  );

  if (user.isLoading) return &lt;div&gt; 로딩중... &lt;/div&gt;;
  // 이렇게 해두면, n번의 로딩 반복 → 에러 상태로 변함
  // 에러 상태로 변하기 전, 로딩이 반복될 때 isError를 찍어보면 false로 나옴.
  if (user.isError) return &lt;div&gt; 에러: {user.error.message} &lt;/div&gt;;

  return (
    &lt;div&gt;
      {&#39; &#39;}
      {data?.map(({ id, name }) =&gt; (
        &lt;span key={id}&gt; {name} &lt;/span&gt;
      ))}{&#39; &#39;}
    &lt;/div&gt;
  );
}</code></pre>
<pre><code class="language-jsx">function UserInfo({ userId }) {
  const userDetail = useQuery(
    // &#39;userInfo&#39;, userId를 Key로 사용하여 데이터 캐싱
    [&#39;userInfo&#39;, userId],
    () =&gt; axios.get(`/users/${userId}`)
  );

  if (userDetail.isLoading) return &lt;div&gt; 로딩중... &lt;/div&gt;;
  if (userDetail.isError) return &lt;div&gt; 에러: {userDetail.error.message} &lt;/div&gt;;
  return &lt;div&gt; {...} &lt;/div&gt;;
}</code></pre>
<p>🌱 <strong>Mutation - useMutation</strong></p>
<ul>
<li>mutation 요청은 <code>useMutation</code> 훅으로 진행됨.</li>
<li>서버에 <code>side effect</code>를 발생시켜 서버 상태를 변경시킬 때 사용<ul>
<li>HTTP Method <code>POST</code>, <code>PUT</code>, <code>DELETE</code> 등에 활용</li>
<li>즉, 값을 바꿀 때 사용하는 훅 !</li>
</ul>
</li>
</ul>
<pre><code class="language-jsx">// 가장 기본적인 형태의 React Query useMutation Hook 사용 예시
const { mutate } = useMutation(
  mutationFn, // 해당 Mutation 요청을 수행하기 위해 Promise를 반환하는 함수 (required)
  options, // useMutation에서 사용되는 Option 객체 (optional)
);</code></pre>
<ul>
<li>useMutation의 <code>첫 번째 파라미터</code>는 useMutation의 return 값 중 mutate(또는 mutateAsync) 함수를 호출하여 서버에 side effect 발생시킬 수 있음.<ul>
<li>useMutation의 첫 번째 파라미터:  Mutation 요청을 수행하기 위한 Promise를 반환하는 함수</li>
</ul>
</li>
</ul>
<pre><code class="language-jsx">function NotificationSwitch({ value }) {
  // mutate 함수를 호출하여 mutationFn 실행
  const { mutate, isLoading } = useMutation(
    (value) =&gt; axios.post(URL, { value }), // mutationFn
  );

  return (
    &lt;Switch
      checked={value}
      disabled={isLoading}
      onChange={(checked) =&gt; {
        // mutationFn의 파라미터 &#39;value&#39;로 checked 값 전달
        mutate(checked);
      }}
    /&gt;
  );
}</code></pre>
<hr>
<h2 id="🪵-recoil에-대해-가볍게-알아보기">🪵 recoil에 대해 가볍게 알아보기</h2>
<p><img src="https://velog.velcdn.com/images/aroo_ming/post/bb545f0f-7c5a-4057-a47b-24b8e109138b/image.png" alt=""></p>
<h3 id="1️⃣-recoil">1️⃣ recoil</h3>
<ul>
<li>react에 최적화된 전역상태 관리 라이브러리</li>
</ul>
<br />

<h3 id="2️⃣-atom">2️⃣ atom</h3>
<ul>
<li><p>하나의 상태로, 컴포넌트가 구독할 수 있는 react state</p>
</li>
<li><p>atom 변경 시, 이를 구독하고 있는 컴포넌트들이 모두 다시 렌더링됨.</p>
</li>
<li><p>atom 생성을 위해서는 어플리케이션의 고유한 키, 디폴트 값을 설정해야 함.</p>
<pre><code class="language-jsx">  import { atom } from &quot;recoil&quot;;

  export const aroomingInfo = atom({
    key: &quot;aroomingInfo&quot;,
    default: &#39;&#39;,
  });</code></pre>
</li>
</ul>
<br />    

<h3 id="3️⃣-selector">3️⃣ selector</h3>
<ul>
<li><p>여러 상태(atom)를 조합한 새로운 상태</p>
<ul>
<li>get, set 메소드 사용 가능</li>
<li>즉, 다른 atom에 의존하는 동적인 데이터를 만들 수 있게 해줌!</li>
</ul>
</li>
<li><p>recoil의 selector 사용 시, 동일한 API 요청에 대해 값 캐싱 가능 → 불필요한 API 요청을 줄일 수 있음!</p>
<pre><code class="language-jsx">  // selector 생성
  import { selector } from &quot;recoil&quot;;
  import { aroomingInfo } from &quot;../atom/AroomingInfo&quot;;

  export const aroomingMessage = selector({
    key: &quot;aroomingMessage/get&quot;,
  // ✨ 메세지 목록만 가져오기
    get: ({ get }) =&gt; {
      return get(aroomingInfo).messageList;
    },
  // ✨ 메세지 목록 업데이트
    set: ({ get, set }, newMessage) =&gt; {
      const newAroomingInfo = {
        ...get(aroomingInfo),
        messageList: [...get(aroomingInfo).messageList, newMessage],
      };
      set(aroomingInfo, newAroomingInfo);
    },
  });</code></pre>
<pre><code class="language-jsx">  // selector를 컴포넌트에서 활용하는 코드
  import { aroomingeMessage } from &quot;../selector/AroomingInfo&quot;;
  import { useRecoilState } from &quot;recoil&quot;;
  import styled from &quot;styled-components&quot;;
  import { useRef } from &quot;react&quot;;

  const AtomArooming = () =&gt; {
    const [aroomingInfo, setAroomingInfo] = useRecoilState(aroomingMessage);
    const inputRef = useRef();

    return (
      &lt;&gt;
        &lt;St.MessageWrapper&gt;
          // ✅ 메세지 보여주기
          {aroomingInfo.map((message) =&gt; (
            &lt;div key={message}&gt;{message}&lt;/div&gt;
          ))}
        &lt;/St.MessageWrapper&gt;
        &lt;St.InputWrapper&gt;
          &lt;input type=&quot;text&quot; ref={inputRef} /&gt;
          // ✅ 메세지 추가 
          &lt;button onClick={() =&gt; setAroomingInfo(inputRef.current.value)}&gt;
            메세지 추가
          &lt;/button&gt;
        &lt;/St.InputWrapper&gt;
      &lt;/&gt;
    );
  };

  export default AtomArooming;</code></pre>
</li>
</ul>
<br />


<h3 id="4️⃣-selectorfamily-options">4️⃣ selectorFamily (options)</h3>
<ul>
<li><p>매개변수를 넘겨주면서 selector를 사용하고 싶을 때 사용</p>
<pre><code class="language-jsx">  export const aroomingSelecteMessage = selectorFamily({
    key: &quot;aroomingSelecteMessage/get&quot;,
    get:
      (selectIdx) =&gt;
      ({ get }) =&gt; {
        return get(aroomignInfo).messageList.filter(
          (_, idx) =&gt; idx === selectIdx
        )[0];
      },
  });

</code></pre>
</li>
</ul>
<pre><code>const aroomingLastMessage = useRecoilValue(aroomingSelecteMessage(1));
```</code></pre><br />

<h2 id="🪵-asynchronous-recoil">🪵 asynchronous recoil</h2>
<ul>
<li>아까 위에서 정의한 selector 함수를 그대로 가져와보자.</li>
<li>똑같은 코드에 단순히 <code>async</code>만 붙여주면 비동기로 사용할 수 있다 !</li>
<li>단, selector는 <code>비동기 set을 지원하지 않는다 !</code><ul>
<li><strong>만약 비동기로 데이터를 가져와서 그 값을 기준으로 값을 세팅해야 하는 경우에는?!</strong><br>  → custom hook을 활용할 수 있음. (중간에 custom hook을 배치하여 hook 내부에 비동기 작업을 수행하고 그 값을 set 함수에 넘기는 방법)</li>
</ul>
</li>
</ul>
<pre><code>```jsx
// 기존의 selector 코드에 async-await 추가
import { selector } from &quot;recoil&quot;;
import { aroomingInfo } from &quot;../atom/AroomingInfo&quot;;

export const aroomingMessage = selector({
  key: &quot;aroomingMessage/get&quot;,
// ✨ async-await 추가
  get: async({ get }) =&gt; {
    return await get(aroomingInfo).messageList;
  },
  set: ({ get, set }, newMessage) =&gt; {
    const newAroomingInfo = {
      ...get(aroomingInfo),
      messageList: [...get(aroomingInfo).messageList, newMessage],
    };
    set(aroomingInfo, newAroomingInfo);
  },
});
```</code></pre><ul>
<li>이때 비동기 recoil state를 사용하게 되면 Suspense를 사용하라는 오류를 볼 수 있음.</li>
</ul>
<br />

<h3 id="1️⃣-suspense">1️⃣ Suspense</h3>
<ul>
<li><p>컴포넌트가 읽어 들이고 있는 데이터가 아직 준비되지 않았다고 React에 알려주는 일종의 메커니즘</p>
</li>
<li><p>데이터 호출 로직에서 <code>호출 완료 여부</code> 인지 → <strong>데이터 불러오기를 완료할 때까지</strong> <code>fallback</code> 속성 값으로 넣어준 컴포넌트를 표시해줌.</p>
<ul>
<li><p>아래 코드처럼 fallback 함수로 Loading 문구를 반환하게 해줬다면, <strong>데이터가 불러와지기 전까지</strong> <code>Loading…</code>을 띄워줌!</p>
<pre><code class="language-jsx">// App.tsx
function App() {
return (
  &lt;RecoilRoot&gt;
    {/* 여기 */}
    &lt;React.Suspense fallback={&lt;div&gt;Loading...&lt;/div&gt;}&gt;
      &lt;CurrentUserInfo /&gt;
    &lt;/React.Suspense&gt;
  &lt;/RecoilRoot&gt;
);
}</code></pre>
</li>
</ul>
</li>
</ul>
<br />


<h3 id="2️⃣-error-handling-에러-처리">2️⃣ Error Handling (에러 처리)</h3>
<ul>
<li>Recoil selector가 발생할 에러에 대해 던져주면 React <ErrorBoundary>로 잡을 수 있음.</li>
</ul>
<pre><code class="language-jsx">// App.tsx
function App() {
  return (
    &lt;RecoilRoot&gt;
      {/* 여기 */}
      &lt;ErrorBoundary&gt;
        &lt;React.Suspense fallback={&lt;div&gt;Loading...&lt;/div&gt;}&gt;
          &lt;CurrentUserInfo /&gt;
        &lt;/React.Suspense&gt;
      &lt;/ErrorBoundary&gt;
    &lt;/RecoilRoot&gt;
  );
}</code></pre>
<br />

<h3 id="3️⃣-recoil과-선언적-프로그래밍">3️⃣ recoil과 선언적 프로그래밍</h3>
<ul>
<li>recoil을 통해 비동기 데이터를 처리하는 방식은 recoil 없이 사용했던 방식과 차이가 있음.</li>
</ul>
<br />

<p><strong>🌱 기존 코드</strong></p>
<ul>
<li>loading일 때는 <code>&lt;Spinner /&gt;</code>를, error 발생 시에는 <code>&lt;ErrorMessage /&gt;</code>를, 그게 아니라면 <code>&lt;DataView /&gt;</code>를 보여주는 …! UI를 어떻게 보여줄지에 집중한 <code>명령형 프로그래밍</code> 방식으로 처리</li>
</ul>
<pre><code class="language-jsx">// 기본 App.tsx
function App() {
    const {loading, data, error} = useSelector(somethingSelector);

if (loading) {
    return &lt;Spinner/&gt;
}

if (error) {
    return &lt;ErrorMessage error={error}/&gt;
}

return &lt;DataView data={data}/&gt;;
}</code></pre>
<p><strong>🌱 recoil 활용 코드</strong></p>
<ul>
<li><code>&lt;Suspense /&gt;</code>와 <code>&lt;ErrorBoundary /&gt;</code>를 사용하여 무엇 보여줄지에 집중한 <code>선언형 프로그래</code>밍 방식으로 처리</li>
</ul>
<pre><code class="language-jsx">function App() {
    const data = useRecoilValue(somethingSelector)
  return (
      &lt;ErrorBoundary FallbackComponent={ErrorMessage}&gt;
        &lt;Suspense fallback={Spinner}&gt;
           &lt;DataView data={data}/&gt;
        &lt;/Suspense&gt;
      &lt;/ErrorBoundary&gt;
  );
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[로드 밸런싱(Load Balancing): 서버 부하 분산 기술]]></title>
            <link>https://velog.io/@aroo_ming/%EB%A1%9C%EB%93%9C-%EB%B0%B8%EB%9F%B0%EC%8B%B1Load-Balancing-%EC%84%9C%EB%B2%84-%EB%B6%80%ED%95%98-%EB%B6%84%EC%82%B0-%EA%B8%B0%EC%88%A0</link>
            <guid>https://velog.io/@aroo_ming/%EB%A1%9C%EB%93%9C-%EB%B0%B8%EB%9F%B0%EC%8B%B1Load-Balancing-%EC%84%9C%EB%B2%84-%EB%B6%80%ED%95%98-%EB%B6%84%EC%82%B0-%EA%B8%B0%EC%88%A0</guid>
            <pubDate>Fri, 03 Nov 2023 15:07:29 GMT</pubDate>
            <description><![CDATA[<h2 id="👀-부하-분산load-balancing">👀 부하 분산(Load Balancing)</h2>
<ul>
<li>애플리케이션을 지원하는 리소스 풀 전체에 네트워크 트래픽을 균등하게 배포하는 방식.</li>
</ul>
<blockquote>
<p><strong>컴퓨터공학에서의 로드 밸런싱</strong></p>
<ul>
<li>부하분산 또는 로드 밸런싱(Load balancing)은 컴퓨터 네트워크 기술의 일종으로 둘 혹은 셋 이상의 중앙처리장치 혹은 저장장치와 같은 컴퓨터 자원들에게 작업을 나누는 것을 의미한다. 이로써 가용성 및 답 시간을 최적화시킬 수 있다.</li>
</ul>
</blockquote>
<br />

<h2 id="👀-서버-부하-분산server-load-balancing">👀 서버 부하 분산(Server Load Balancing)</h2>
<p><img src="https://velog.velcdn.com/images/aroo_ming/post/573d58a6-014e-48da-8696-22d1c157646f/image.png" alt=""></p>
<ul>
<li>외부의 사용자로부터 들어오는 다수의 요청을 서버들에 적절히 배분하여 각각의 서버들이 요청을 처리하게 하는 것.</li>
<li>즉, 서버가 처리해야 할 업무 혹은 요청(load)을 여러 대의 서버로 나눠(balancing) 처리하는 것을 의미함.</li>
<li>한 대의 서버로 부하가 집중되지 않도록 트래픽 관리 → 각각의 서버가 최적의 성능을 내게 하는 목적 !</li>
</ul>
<blockquote>
<p><strong>Load Balancer</strong>: cloud 상에서 서버 부하 분산을 담당하는 Network Switch를 부르는 말.
<strong>LB, Load Balancing</strong>: 현업에서 서버 부하 분산을 칭하는 말.</p>
</blockquote>
<br />

<h2 id="👀-서버-부하-분산-방법load-balancing-method">👀 서버 부하 분산 방법(Load Balancing Method)</h2>
<p>: 서버의 능력을 고려하여 분배해야 서버가 죽지 않기 때문에, 서버의 상황에 맞춰 적절한 방법을 선택해야 함.</p>
<h3 id="🥽-round-robin">🥽 Round Robin</h3>
<p><img src="https://velog.velcdn.com/images/aroo_ming/post/1dfd463b-17a3-4550-addb-7b2624ab24aa/image.png" alt=""></p>
<ul>
<li>로드 밸런서가 다수의 서버에게 순서대로 요청을 할당하는 방법.</li>
<li>가장 단순한 방법으로, 서버군에 차례대로 요청을 할당하여 분산함.</li>
</ul>
<br />

<h3 id="🥽-least-connection">🥽 Least Connection</h3>
<p><img src="https://velog.velcdn.com/images/aroo_ming/post/7ed6f699-c327-4874-b10a-c436cb3a3bde/image.png" alt=""></p>
<ul>
<li>로드 밸런서가 서버에게 요청을 전달한 뒤, 사용자와 서버가 정상적인 연결을 맺으면 connection 생성</li>
<li>로드 밸런서 또한 중간자로서 connection 정보를 갖고 있음</li>
<li>connection 수 정보를 기반으로 서버에게 요청 전달.</li>
<li>이때, connection이 가장 적은 서버 === 부하가 가장 덜한 서버를 의미, 부하가 가장 덜한 서버에 요청을 전달함 !</li>
</ul>
<br />

<h3 id="🥽-ratio가중치">🥽 Ratio(가중치)</h3>
<p><img src="https://velog.velcdn.com/images/aroo_ming/post/d80e65fb-5720-4170-926a-b8ab113a185b/image.png" alt=""></p>
<ul>
<li>서버의 처리 능력을 고려하여 할당 가능한 connection 비율을 미리 정해둠.</li>
<li>서버 부하 분산 비율이 100%라고 가정했을 때, 성능이 가장 떨어지는 서버에게 10%를 나머지 서버에게 각각 30%를 할당함.</li>
</ul>
<br />

<h3 id="🥽-fastestresponse-time">🥽 Fastest(Response Time)</h3>
<ul>
<li>응답속도가 가장 빠른 서버에게 우선적으로 할당하는 방식.<ul>
<li>서버에 할당된 connection이 5개, 서버의 response도 5개인 경우<ul>
<li>connection에 대해 모두 응답 → 성능이 충분하다고 판단 → 서버에 추가 요청 보냄.</li>
</ul>
</li>
<li>서버에 할당된 connection이 10개, 서버의 response는 5개인 경우<ul>
<li>현재 성능이 충분하지 않아 제대로 답변하지 못한다고 판단 → 서버에 추가 요청 보내지 않음.</li>
</ul>
</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[HTTP & HTTPS: 웹 통신의 기초와 보안 버전의 차이]]></title>
            <link>https://velog.io/@aroo_ming/HTTP-HTTPS-%EC%9B%B9-%ED%86%B5%EC%8B%A0%EC%9D%98-%EA%B8%B0%EC%B4%88%EC%99%80-%EB%B3%B4%EC%95%88-%EB%B2%84%EC%A0%84%EC%9D%98-%EC%B0%A8%EC%9D%B4</link>
            <guid>https://velog.io/@aroo_ming/HTTP-HTTPS-%EC%9B%B9-%ED%86%B5%EC%8B%A0%EC%9D%98-%EA%B8%B0%EC%B4%88%EC%99%80-%EB%B3%B4%EC%95%88-%EB%B2%84%EC%A0%84%EC%9D%98-%EC%B0%A8%EC%9D%B4</guid>
            <pubDate>Fri, 03 Nov 2023 15:03:26 GMT</pubDate>
            <description><![CDATA[<h2 id="📌-http">📌 HTTP</h2>
<ul>
<li><p><code>Hyper Text Transfer Protocol</code></p>
</li>
<li><p>서버/ 클라이언트 모델을 따라 데이터를 주고 받기 위한 프로토콜</p>
</li>
<li><p>인터넷에서 <strong>하이퍼텍스트</strong>를 교환하기 위한 통신규약, <code>80번 포트</code> 사용</p>
<blockquote>
<p><strong>하이퍼텍스트</strong>(Hypertext): 참조(하이퍼링크)를 통해 독자가 한 문서에서 다른 문서로 즉시 접근할 수 있는 텍스트</p>
</blockquote>
</li>
<li><p>HTTP 서버 → 80번 포트에서 요청 기다림 → 클라이언트 → 80번 포트로 요청 보냄.</p>
</li>
</ul>
<br />

<h3 id="📐-http-구조">📐 HTTP 구조</h3>
<ul>
<li>HTTP는 애플리케이션 레벨의 프로토콜로 TCP/IP 위에서 작동함.</li>
<li>상태를 가지고 있지 않는 stateless 프로토콜</li>
<li>Method, Path, Version, Headers, Body 등으로 구성</li>
<li>암호화되지 않은 <code>평문 데이터를 전송</code>하는 프로토콜이기 때문에, HTTP로 비밀번호나 주민등록번호 등을 주고 받을 시, 제 3자가 정보 조회를 할 수 있었음 → <code>보안 취약성</code><ul>
<li><strong>이를 개선하기 위해 등장한게 HTTPS !</strong></li>
</ul>
</li>
</ul>
<br />

<h2 id="📌-https">📌 HTTPS</h2>
<ul>
<li><code>Hyper Text Transfer Protocol **Secure**</code></li>
<li>HTTP + 데이터 암호화 → HTTP 요청 및 응답을 SSL/ TLS 기술에 결합함.</li>
<li><code>443번 포트</code> 사용</li>
<li>네트워크 상에서 중간에 제 3자가 정보를 조회할 수 없도록 <code>암호화 지원</code></li>
</ul>
<br />

<h3 id="📐-https-동작-방식">📐 HTTPS 동작 방식</h3>
<ol>
<li>사용자 브라우저의 주소 표시줄에 <code>https://</code> URL 형식을 입력하여 HTTPS 웹 사이트를 방문.</li>
<li>브라우저: 서버의 SSL 인증서를 요청 → 사이트의 신뢰성을 검증하려고 시도.</li>
<li>서버: 공개 키가 포함된 SSL 인증서를 회신으로 전송.</li>
<li>웹 사이트의 SSL 인증서 → 서버 아이덴티티 증명.<ul>
<li>브라우저에서 인증 성공 시, 브라우저: 퍼블릭 키를 사용 → 비밀 세션 키가 포함된 메시지를 암호화 → 전송.</li>
</ul>
</li>
<li>웹 서버: 개인 키를 사용 → 메시지를 해독, 세션 키를 검색 → 암호화 → 브라우저에 승인 메시지 전송.</li>
<li>브라우저와 웹 서버 모두 동일한 세션 키를 사용 → 메시지를 안전하게 교환하도록 전환.</li>
</ol>
<br />


<h2 id="🙋♀️-http2-http3-https의-차이점">🙋‍♀️ HTTP/2, HTTP/3, HTTPS의 차이점</h2>
<ul>
<li><strong>HTTP/1.1</strong>: 출시된 최초의 HTTP 버전</li>
<li><strong>HTTP/2, HTTP/3</strong>: 프로토콜 자체를 업데이트한 버전<ul>
<li>데이터 전송 시스템 수정 → 효율성 개선</li>
</ul>
</li>
</ul>
<h3 id="🔍-http2">🔍 HTTP/2</h3>
<ul>
<li><p>텍스트 형식 대신, <strong>바이너리</strong>로 데이터 교환</p>
<blockquote>
<p><strong>바이너리</strong>(Binary):1과 0만을 사용하여 수를 나타내는 진법(2진법)/ 컴퓨터 언어</p>
</blockquote>
</li>
<li><p>서버가 새 HTTP 요청을 기다리는 대신, 클라이언트 캐시에 응답을 사전에 전송 가능</p>
</li>
</ul>
<h3 id="🔍-http3">🔍 HTTP/3</h3>
<ul>
<li>비교적 최신에 나온 버전</li>
<li>HTTP/2를 한 단계 더 발전시킨 버전</li>
<li>실시간 스트리밍 및 기타 최신 데이터 전송 요구 사항을 보다 효율적으로 지원하는 것을 목표로 함.</li>
</ul>
<h3 id="🔍-https">🔍 HTTPS</h3>
<ul>
<li>HTTP의 보안 문제를 우선시 함.</li>
<li>최신 시스템에서는 SSL/ TLS 와 함께 HTTP/2를 HTTPS로 사용.</li>
<li>HTTP/3이 발전하면 브라우저 및 서버 기술도 HTTPS에 통합될 예정.</li>
</ul>
<br />


<h2 id="🙋♀️-http보다-https를-선택하는-이유">🙋‍♀️ HTTP보다 HTTPS를 선택하는 이유</h2>
<h3 id="🔍-보안">🔍 보안</h3>
<ul>
<li><code>HTTP</code> 메시지는 일반 텍스트이기 때문에 권한이 없는 당사자가 인터넷을 통해 쉽게 엑세스하고 읽을 수 있음.</li>
<li><code>HTTPS</code>는 모든 데이터를 암호화된 데이터 형태로 전송함.<ul>
<li>사용자의 민감한 데이터를 제 3자가 네트워크를 통해 가로챌 수 없음.</li>
<li>신용카드 세부정보, 고객 개인 정보 등 잠재적으로 민감한 정보를 보호하려면 HTTPS를 선택하는 것이 좋음.</li>
</ul>
</li>
</ul>
<h3 id="🔍-인증">🔍 인증</h3>
<ul>
<li><code>HTTP</code>의 신뢰성이 더 낮기 때문에, 검색 엔진은 <code>HTTPS</code> 웹 페이지보다 웹 사이트 컨텐츠 순위를 낮게 지정함.</li>
<li>브라우저는 주소 표시줄에서 웹 사이트 URL 옆에 있는 자물쇠 아이콘 배치 → 사용자에게 HTTPS 연결을 표시<ul>
<li>추가 보안 및 신뢰 요소 때문에 사용자 역시 HTTPS 웹 사이트 및 애플리케이션을 선호 !</li>
</ul>
</li>
</ul>
<h3 id="🔍-성능-및-분석">🔍 성능 및 분석</h3>
<ul>
<li><p><code>HTTPS</code> 웹 애플리케이션은 <code>HTTP</code> 애플리케이션보다 로드 속도가 더 빠름.</p>
</li>
<li><p><code>HTTPS</code>는 참조 링크 역시 더 잘 추적함.</p>
</li>
<li><p>따라서, 분석 소프트웨어가 신뢰할 수 있는 <strong>트래픽 소스</strong>를 정확하게 식별하게 하려면 <code>HTTPS</code>를 활성화하는 것이 좋음 !</p>
<blockquote>
<p><strong>트래픽 소스</strong>(Traffic Source)</p>
<ul>
<li>웹 사이트나 애플리케이션에 방문하는 사용자가 어떤 경로나 출처로부터 왔는지 나타내는 정보</li>
<li>웹 분석에서 중요한 역할을 함.</li>
<li>트래픽 소스 분석 시, 웹 사이트/ 애플리케이션의 성과를 이해하고 개선하기 위한 인사이트를 얻을 수 있음.</li>
</ul>
</blockquote>
</li>
</ul>
<br />

<h2 id="🧩-결론">🧩 결론</h2>
<table>
<thead>
<tr>
<th></th>
<th>HTTP</th>
<th>HTTPS</th>
</tr>
</thead>
<tbody><tr>
<td>의미</td>
<td>Hypertext Transfer Protocol</td>
<td>Hypertext Transfer Protocol Secure</td>
</tr>
<tr>
<td>기본 프로토콜</td>
<td>HTTP/1과 HTTP/2는 TCP/IP를 사용합니다. HTTP/3은 QUIC 프로토콜을 사용합니다.</td>
<td>HTTP 요청 및 응답을 추가로 암호화하기 위해 SSL/TLS와 함께 HTTP/2 사용</td>
</tr>
<tr>
<td>포트</td>
<td>기본 포트 80</td>
<td>기본 포트 443</td>
</tr>
<tr>
<td>용도</td>
<td>이전 텍스트 기반 웹 사이트</td>
<td>모든 최신 웹 사이트</td>
</tr>
<tr>
<td>보안</td>
<td>추가 보안 기능 없음</td>
<td>퍼블릭 키 암호화에 SSL 인증서 사용</td>
</tr>
<tr>
<td>이점</td>
<td>인터넷을 통한 통신 지원</td>
<td>웹 사이트에 대한 권위, 신뢰성 및 검색 엔진 순위 개선</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[TLS/SSL 프로토콜의 역할과 handshake: 암호화된 통신 설정 과정]]></title>
            <link>https://velog.io/@aroo_ming/TLSSSL-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C%EC%9D%98-%EC%97%AD%ED%95%A0%EA%B3%BC-handshake-%EC%95%94%ED%98%B8%ED%99%94%EB%90%9C-%ED%86%B5%EC%8B%A0-%EC%84%A4%EC%A0%95-%EA%B3%BC%EC%A0%95</link>
            <guid>https://velog.io/@aroo_ming/TLSSSL-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C%EC%9D%98-%EC%97%AD%ED%95%A0%EA%B3%BC-handshake-%EC%95%94%ED%98%B8%ED%99%94%EB%90%9C-%ED%86%B5%EC%8B%A0-%EC%84%A4%EC%A0%95-%EA%B3%BC%EC%A0%95</guid>
            <pubDate>Fri, 03 Nov 2023 15:02:50 GMT</pubDate>
            <description><![CDATA[<h2 id="💻-tls-transport-layer-security">💻 TLS (Transport Layer Security)</h2>
<ul>
<li>전송 계층 보안으로, 인터넷 상의 커뮤니케이션을 위한 개인 정보와 데이터 보안을 용이하게 하기 위해 설계된 보안 프로토콜</li>
<li>네트워크 상에서 보안을 제공하는 암호화 프로토콜을 사용해 네트워크에 보안을 제공함으로써, 개인정보 보호를 보장하는 클라이언트/ 서버 프로토콜</li>
<li>전송 중인 정보를 보호함으로써 안전하지 않은 인프라를 통해 통신을 보호함.<ul>
<li>웹 사이트를 로드하는 웹 브라우저와 같이 웹 응용 프로그램과 서버 간의 커뮤니케이션을 암호화에 사용됨.</li>
<li>이메일, 메시지, 보이스오버 IP(VoIP) 등 다른 커뮤니케이션을 암호화하기 위해 사용</li>
</ul>
</li>
<li>TLS 버전 1.3은 최근에 활발히 사용되는 암호화 프로토콜 버전으로, 이제는 사용하지 않는 SSL 버전 3.0 프로토콜보다 향상된 보안 기능과 성능을 다수 제공함.</li>
</ul>
<br />

<h3 id="🌟-tls-프로토콜-역할">🌟 TLS 프로토콜 역할</h3>
<ul>
<li>TLS 프로토콜은 주로 암호화, 인증, 무결성이라는 세 가지 역할을 수행함.<ul>
<li><code>암호화</code>: 제 3자로부터 전송되는 데이터를 숨김.</li>
<li><code>인증</code>: 정보를 교환하는 당사자가 요청된 당사자임을 보장함.</li>
<li><code>무결성</code>: 데이터가 위조되거나 변조되지 않았는지 확인함.</li>
</ul>
</li>
<li>서버와 클라이언트가 TLS로 통신을 할 때, 어떠한 제 3자도 메시지를 변형하거나 감청할 수 없도록 함.</li>
</ul>
<hr>
<br />

<h2 id="💻-ssl-secure-sockets-layer">💻 SSL (Secure Sockets Layer)</h2>
<ul>
<li>클라이언트와 서버  간의 안전한 링크를 통해 송수신되는 모든 데이터를 안전하게 보장하는 과거의 보안 표준 기술이었음.</li>
<li>보안 취약점으로 인해 현재는 사용하지 않는 암호화 프로토콜</li>
</ul>
<br />

<h3 id="🌟-ssl-프로토콜-역할">🌟 SSL 프로토콜 역할</h3>
<ul>
<li>SSL이 적용되지 않은 통신의 경우, 평문(plain-text)이 그대로 전송됨.<ul>
<li>만약 제 3자가 통신 패킷을 탈취할 경우, 그 내용을 쉽게 확인할 수 있음.</li>
</ul>
</li>
<li>SSL을 적용한다면 요청을 암호화해서 보내기 때문에, 통신 패킷이 탈취되어도 복호화 키가 없으면 원래 내용을 알 수 없게되어 평문이 노출되는 문제를 기술적으로 해결 가능함.</li>
</ul>
<hr>
<br />

<h2 id="💻-tlsssl-handshake">💻 TLS/SSL HandShake</h2>
<blockquote>
<p>handshake: 클라이언트와 서버 간의 요청/ 응답을 반복하며 통신에 필요한 사전 작업을 하는게 마치 악수 같아 이러한 용어가 붙여졌다고 한다.</p>
</blockquote>
<blockquote>
<p>HTTPS에서 클라이언트와 서버 간 통신 전, SSL 인증서로 신뢰성 여부를 판단하기 위해 연결하는 방식</p>
</blockquote>
<ol>
<li><p><strong><code>ClientHello 요청</code></strong></p>
<ul>
<li>클라이언트가 특정 주소에 접근하면, 해당하는 서버에 요청을 보냄.<ul>
<li>ex. NAVER에 접근 시, 네이버 서버에 요청 전송</li>
</ul>
</li>
<li>클라이언트의 주요 정보를 서버에 전송.<ul>
<li>난수 데이터</li>
<li>암호화 프로토콜 정보</li>
<li>클라이언트가 사용 가능한 암호화 기법</li>
<li>세션 ID</li>
<li>기타 확장 정보</li>
</ul>
</li>
<li>해당 클라이언트를 식별, 어떤 암호화를 사용할 수 있는지 등의 정보를 서버가 인지하도록 함.</li>
</ul>
</li>
<li><p><strong><code>ServerHello 응답</code></strong></p>
<ul>
<li>서버가 ClientHello 요청을 받으면, 아래의 정보를 담아 클라이언트에게 응답을 보냄.<ul>
<li>난수 데이터(ClientHello의 난수 데이터와 다름)</li>
<li>서버가 사용할 암호화 기법</li>
<li>인증서<ul>
<li>CA, 도메인, 공개 키</li>
</ul>
</li>
</ul>
</li>
<li>클라이언트에서 사용 가능한 암호화 기법 중 서버에서 활용할 암호화 기법을 전달하여 동일한 암호화 기법으로 송수신할 수 있도록 선언.</li>
<li>인증서 정보와 함께, 서버와의 암호화 통신을 위한 서버 공개키가 전달됨.</li>
<li>서버의 공개키로 데이터를 암호화 → 서버는 개인 키로 복호화 → 요청 분석</li>
</ul>
</li>
<li><p><strong><code>인증서 검토</code></strong></p>
<ul>
<li>서버가 전달한 인증서가 실제 해당 서버의 인증서인지, 신뢰할 수 있는 CA에서 발급된 것인지, 실제 해당 CA에서 발급받았는지 등 인증서 검토<ul>
<li>인증서에 이상이 없다면, <code>연결이 안전</code>하다는 창을 볼 수 있음.</li>
<li>인증서에 문제가 있다면, <code>비공개 연결이 아닙니다.</code> 와 같은 경고문을 볼 수 있음.<ul>
<li>이는 브라우저가 사용자에게 보내는 경고문으로, 해당 사이트의 인증서가 올바르지 않음을 의미.</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><p><strong><code>Premaster Secret 송수신</code></strong></p>
<ul>
<li>ClientHello, ServerHello에서 송수신한 난수 데이터를 조합하여 Premaster Secret 생성.</li>
<li>Premaster Secret을 ServerHello에서 전달받았던 공개 키로 암호화.</li>
<li>해당 데이터는 서버가 가진 개인 키로만 복호화 가능 → 데이터 탈취 시에도 내용 보호 가능.</li>
<li>서버: 수신된 데이터 복호화 → 클라이언트와 동일한 Premaster Secret 저장 가능.</li>
</ul>
</li>
<li><p><strong><code>통신 키 생성</code></strong></p>
<ul>
<li>보유한 Premaster Secret을 기반으로 Master Secret, Session Key 생성.</li>
<li>이를 통해, 클라이언트와 서버가 동일한 키를 보유하게됨.</li>
<li>클-서 암호화 통신 가능.</li>
</ul>
</li>
<li><p><strong><code>데이터 송수신</code></strong></p>
<ul>
<li>저장된 Session Key를 통한 대칭 키 암호화 방식으로 암/복호화하여 통신.</li>
</ul>
</li>
<li><p><strong><code>세션 종료</code></strong></p>
<ul>
<li>클라이언트와의 연결이 끝났을 경우, Session Key 폐기.</li>
</ul>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[대칭키 & 공개키 암호화: 데이터 보호를 위한 키 관리 방법]]></title>
            <link>https://velog.io/@aroo_ming/%EB%8C%80%EC%B9%AD%ED%82%A4-%EA%B3%B5%EA%B0%9C%ED%82%A4-%EC%95%94%ED%98%B8%ED%99%94-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B3%B4%ED%98%B8%EB%A5%BC-%EC%9C%84%ED%95%9C-%ED%82%A4-%EA%B4%80%EB%A6%AC-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@aroo_ming/%EB%8C%80%EC%B9%AD%ED%82%A4-%EA%B3%B5%EA%B0%9C%ED%82%A4-%EC%95%94%ED%98%B8%ED%99%94-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%B3%B4%ED%98%B8%EB%A5%BC-%EC%9C%84%ED%95%9C-%ED%82%A4-%EA%B4%80%EB%A6%AC-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Sun, 29 Oct 2023 12:27:02 GMT</pubDate>
            <description><![CDATA[<h2 id="🔑-대칭-키symmentric-key">🔑 대칭 키(Symmentric Key)</h2>
<p><img src="https://velog.velcdn.com/images/aroo_ming/post/9db60d15-da0f-4e91-9fd1-1c747b57b7f0/image.png" alt=""></p>
<ul>
<li>암호화와 복호화에 <code>같은 암호 키</code>(대칭 키)를 사용하는 알고리즘</li>
<li>해당 키를 아는 사람만 문서를 복호화할 수 있음.</li>
<li>Session Key, Secret Key, Shared Key, 대칭키, 단용키라고도 함</li>
<li>대표적인 알고리즘: DES, 3DES, AES, SEED, ARIA, 최근 주목받고 있는 암호인 ChaCha20</li>
<li><strong>장점</strong>: 기밀성 제공, 공개 키 암호화 방식에 비해 속도가 빠름.</li>
<li><strong>단점</strong>: 무결성/ 인증/ 부인방지를 보장하지 않음.<ul>
<li>부인방지: 메시지의 송수신이나 교환 후, 또는 통신이나 처리가 실행된 후에 그 사실을 사후에 증명함으로써 사실 부인을 방지하는 보안기술</li>
<li>키 교환 중 <code>탈취 문제</code> 발생 가능성</li>
<li><code>사람마다 키 교환을 따로</code> 해야하기 때문에, <strong>사람의 수 증가 → 관리해야 할 키의 수가 방대</strong>해짐</li>
</ul>
</li>
</ul>
<br />

<h3 id="🔐-대칭-키-암호">🔐 대칭 키 암호</h3>
<ul>
<li>하나의 비밀키를 client와 server가 함께 사용</li>
<li><code>암호화 키 === 복호화 키</code></li>
<li>공개 키와 비밀 키를 별도로 가지는 것에 비하면 계산 속도가 빠름.</li>
<li>비밀 키 하나만 알아내면 암호화된 내용을 해독 가능 → <code>해커로부터 안전하지 않음.</code></li>
<li>대칭 키 암호는 <code>암호화하는 단위</code>에 따라 <strong>스트림 암호</strong>와 <strong>블록 암호</strong>로 나눌 수 있음.<ul>
<li><code>스트림 암호</code>: 연속적인 비트/ 바이트를 계속해서 입력받아서, 그에 대응하는 암호화 비트/ 바이트를 생성하는 방식.</li>
<li><code>블록 암호</code>: 정해진 한 단위(블록)을 입력받아 그에 대응하는 암호화 블록을 생성하는 방식</li>
</ul>
</li>
</ul>
<br />

<hr>
<h2 id="🔑-공개-키public-key-비대칭-키asymmetric-key">🔑 공개 키(Public Key)/ 비대칭 키(Asymmetric Key)</h2>
<p><img src="https://velog.velcdn.com/images/aroo_ming/post/a185df53-2ddd-4c4e-b575-21e398d19c9b/image.png" alt=""></p>
<ul>
<li><p>암호화와 복호화에 사용하는 <code>암호키를 분리</code>한 알고리즘</p>
</li>
<li><p><strong>대칭키의 키 교환 문제를 해결</strong>하기 위해 등장</p>
<ul>
<li><code>키가 공개</code>되어 있기 때문에, 키 교환 필요성이 사라짐.</li>
<li>대칭키의 경우, 송수신자의 키를 알아야하기 때문에 분배가 어렵고 복잡하지만, 공개 키와 비밀 키로 분리할 경우 남들이 알아도 되는 <code>공개 키만 공개</code>하면 됨.</li>
</ul>
</li>
<li><p>대표적인 알고리즘: <code>Diffie Hellman</code>, <code>RSA</code>, <code>DSA</code>, <code>ECC</code></p>
<ul>
<li><strong>Diffie Hellman</strong> : 최초의 공개키 알고리즘, 위조에 취약</li>
<li><strong>RSA</strong> : 대표적 공개키 알고리즘</li>
<li><strong>DSA</strong> : 전자서명 알고리즘 표준</li>
<li><strong>ECC</strong> : 짧은 키로 높은 암호 강도, 빠른 구현 가능 PDA, 스마트폰등에 사용</li>
</ul>
</li>
<li><p>장점: 키 분배 필요 X, 기밀성/ 인증/ 부인방지 기능 제공</p>
</li>
<li><p>단점: 느린 속도</p>
<ul>
<li>긴 문서를 암호화하는데 사용하기 보단, 대칭 키 알고리즘의 키 값에 대한 암호에 사용됨.</li>
</ul>
</li>
<li><p>방식</p>
<ul>
<li><p><code>암호모드</code>: 송신자 공개 키로 암호화 → 송신자 사설 키로 복호화</p>
<p>  소량의 메시지 암호화 목적, 주로 키 교환의 용도로 사용</p>
</li>
<li><p><code>인증모드</code>: 송신자 사설 키로 암호화 → 송신자 공개키로 복호화</p>
<p>  메시지를 인증 <code>부인방지</code>하는 것이 목적</p>
</li>
</ul>
</li>
</ul>
<br />

<h3 id="🔐-공개-키-암호">🔐 공개 키 암호</h3>
<ul>
<li>암호화와 복호화에 사용하는 키가 다름.<ul>
<li><code>공개 키</code>: <strong>모든 사람</strong>이 접근 가능한 키/ <code>암호화</code>에 사용하는 키</li>
<li><code>비밀 키</code>: <strong>사용자만</strong> 가지고 있는 키/ <code>복호화</code>에 사용하는 키</li>
</ul>
</li>
<li>공개 키는 누구나 알 수 있지만, 비밀 키는 키의 소유자만이 알 수 있음.<ul>
<li>특정 비밀키를 가지는 <strong>사용자만 내용을 열어볼 수 있음.</strong></li>
<li>안전한 메시지 전달 가능</li>
</ul>
</li>
<li>공개 키 암호화 방식은 <strong>우체통</strong>에 비유할 수 있음.<ul>
<li><strong>우체통의 투입구</strong> <code>공개 키</code>를 통해 누구나 편지를 넣을 수 있음.</li>
<li>편지함은 <strong>열쇠</strong> <code>비밀 키</code>를 가진 사람만 열 수 있음.</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/aroo_ming/post/d1e93127-8d0c-49ba-a7e0-c87635b371bd/image.png" alt=""></p>
<ul>
<li>공개 키 암호화/ 복호화 방식<ul>
<li>A: B의 공개 키로 암호화한 데이터를 B에 보냄.</li>
<li>B: 본인의 비밀 키로 복호화한 평문 확인 → A의 공개키로 응답을 암호화 → A에 보냄.</li>
<li>A: 자신의 비밀 키로 암호화된 응답문 복호화</li>
<li>따라서, B의 공개 키에 대응되는 비밀 키를 가지고 있는 B만 해당 데이터를 볼 수 있게 됨.</li>
</ul>
</li>
<li>단, 대칭 키에 비해 암호화/ 복호화  과정이 매우 복잡함.<ul>
<li>암호화/ 복호화하는 키가 서로 다르기 때문!</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[useEffect, useMemo, useLayoutEffect 알아보기]]></title>
            <link>https://velog.io/@aroo_ming/useEffect-useMemo-useLayoutEffect-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@aroo_ming/useEffect-useMemo-useLayoutEffect-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Thu, 26 Oct 2023 15:33:05 GMT</pubDate>
            <description><![CDATA[<h1 id="📑-react-life-cycle">📑 React Life Cycle</h1>
<p>들어가며, 리액트의 생명 주기에 대해 아주 간단히 알아보도록 하자 !</p>
<p><img src="https://velog.velcdn.com/images/aroo_ming/post/f054feaa-f4e4-4964-9ce7-2caeba20f63c/image.png" alt=""></p>
<ul>
<li><code>Mount</code>: 컴포넌트가 <strong>최초 실행</strong>될 때</li>
<li><code>Render</code><ul>
<li>컴포넌트 내의 <strong>엘리먼트 요소</strong>들(HTML, React 사용자 정의 태그 등)을 <strong>화면 상에 그리는</strong> 동작</li>
<li>컴포넌트가 <strong>마운트된 후</strong>, 컴포넌트가 호출될 때</li>
</ul>
</li>
<li><code>re-Render</code>: <strong>렌더링</strong> 동작을 <strong>다시 수행</strong>하는것</li>
<li><code>UnMount</code>: 컴포넌트가 <strong>사라질 때</strong></li>
</ul>
<p>즉, <strong>컴포넌트가 <code>처음 나타날 때</code>를 <code>마운트</code></strong>, 그 이후 값이 변경되어 <strong><code>컴포넌트가 변경된 상태에서 호출될 때</code>를 <code>렌더링</code></strong> 이라고 부른다.</p>
<br />

<h2 id="🔰-class-component-생명주기">🔰 Class Component 생명주기</h2>
<h3 id="👀-mount-마운트">👀 Mount (마운트)</h3>
<p>: 컴포넌트가 생성 시, 발생하는 생명주기</p>
<p> ✔︎ <strong>constructor</strong></p>
<ul>
<li>컴포넌트 생성자 메서드, 컴포넌트가 생성되면 가장 먼저 실행되는 메소드</li>
</ul>
<p>✔︎ <strong>getDerivedStateFromProps</strong></p>
<ul>
<li>props로부터 파생된 state를 가져오는 메소드</li>
</ul>
<p>✔︎ <strong>render</strong></p>
<ul>
<li>컴포넌트를 렌더링하는 메소드</li>
</ul>
<p>✔︎ <strong>componentDidMount</strong></p>
<ul>
<li>컴포넌트가 마운트되면 호출되는 메소드</li>
</ul>
<br />

<h3 id="👀-updating-업데이트">👀 Updating (업데이트)</h3>
<p>: 컴포넌트 업데이트 시점에 호출되는 메소드</p>
<p>✔︎ <strong>getDerivedStateFromProps</strong></p>
<ul>
<li>컴포넌트의 props나 state  변경 시 호출되는 메소드</li>
</ul>
<p>✔︎ <strong>shouldComponentUpdate</strong></p>
<ul>
<li>컴포넌트의 리렌더링 여부를 결정하는 메소드<ul>
<li><strong>React.memo</strong>와 유사</li>
<li>boolean 반환</li>
</ul>
</li>
</ul>
<p>✔︎ <strong>componentDidUpdate</strong></p>
<ul>
<li>컴포넌트 업데이트 후 발생하는 메소드<ul>
<li>의존성 배열이 변할 때만 useEffect가 실행되는 것과 같은 맥락 !</li>
</ul>
</li>
</ul>
<br />

<h3 id="👀-unmount-언마운트">👀 UnMount (언마운트)</h3>
<p>: 컴포넌트가 화면에서 사라지는 것과 관련된 메소드</p>
<p>✔︎ <strong>componentWillUnmount</strong></p>
<ul>
<li>컴포넌트 생성자 메서드, 컴포넌트가 생성되면 가장 먼저 실행되는 메소드</li>
</ul>
<br />

<hr>
<h1 id="📑-useeffect">📑 useEffect()</h1>
<ul>
<li>컴포넌트가 렌더링될 때 특정 작업(side effect)을 실행할 수 있도록 하는 Hook<ul>
<li><code>side effect</code>: 컴포넌트가 렌더링 된 이후, 비동기로 처리되어야 하는 부수적인 효과<ul>
<li>ex.  함수 내부에서 함수 외부의 변수를 변하게 하는 액션</li>
</ul>
</li>
</ul>
</li>
<li><code>componentDidMount</code>와 <code>componentDidUpdate</code>, <code>componentWillUnmount</code>가 합쳐진 것 !</li>
</ul>
<br />

<h2 id="🔰-useeffect-언제-작업을-처리하는가">🔰 useEffect, 언제 작업을 처리하는가!</h2>
<ol>
<li>component가 <code>mount</code> 됐을 때</li>
<li>component가 <code>unmount</code> 됐을 때</li>
<li>component가 <code>update</code> 됐을 때</li>
</ol>
<br />

<p><img src="https://velog.velcdn.com/images/aroo_ming/post/a4083043-8a3c-4298-b07d-bb524e97e6a6/image.webp" alt=""></p>
<p>⇒ 즉, 클래스형 컴포넌트에서 사용할 수 있었던 생명주기 메소드(<code>componentDidMount</code>, <code>componentDidUpdate</code>, <code>componentWillUnmount</code>)를 함수형 컴포넌트에서도 사용 가능해짐!</p>
<br />

<h2 id="🔰-useeffect--구조">🔰 useEffect  구조</h2>
<pre><code class="language-jsx">useEffect (function, deps)</code></pre>
<ul>
<li><p><code>function</code>: 수행하고자 하는 작업</p>
<ul>
<li>리액트는 이 함수를 기억했다가, DOM 업데이트 이후 불러냄</li>
<li>function이 함수(a)를 return할 경우, 컴포넌트 unmount 시 함수(a) 다시 실행</li>
</ul>
</li>
<li><p><code>deps</code> : 의존성, 이 값에 의존하여 function 실행</p>
<ul>
<li>배열 형태/ 검사하고자하는 특정 값이나 빈 배열이 들어감.</li>
<li><code>특정 값</code>이 들어가는 경우: 컴포넌트가 mount될 때, 지정한 값이 업데이트될 때 useEffect 실행</li>
<li><code>빈 배열</code>이 들어가는 경우: 따로 의존성을 갖지 않게 되어, 컴포넌트 mount 시 한 번만 실행됨.</li>
</ul>
</li>
</ul>
<br />


<h2 id="🔰-cleanup-함수">🔰 cleanup 함수</h2>
<h3 id="👀-기본-형태">👀 <strong>기본 형태</strong></h3>
<pre><code class="language-jsx">useEffect(() =&gt; {
    ... // 실행할 내용

    return () =&gt; {
        ... // clenup
    }
}</code></pre>
<ul>
<li><code>cleanup 함수</code>: 컴포넌트 unmount 시, 실행되는 함수<ul>
<li>컴포넌트가 사라질 때 호출되는 함수</li>
<li>메모리 누수를 방지하여 메모리를 관리하거나 컴포넌트가 사라질 때, 수행할 작업들을 추가하기 위해 사용</li>
<li>단, react 18 버전부터는 메모리 누수에 대한 경고가 사라짐</li>
</ul>
</li>
</ul>
<h3 id="👀-예제">👀 <strong>예제</strong></h3>
<pre><code class="language-jsx">useEffect(() =&gt; {
    // 실행 함수
  const timer = setTimeout(() =&gt; {
  }, 3000);

    // clean-up 함수
  return () =&gt; { lock = true };

}, []);</code></pre>
<ul>
<li>리렌더링 시, useEffect의 return 함수 실행</li>
<li>실행된 함수가 useEffect의 내부 기능의 작동을 막음</li>
<li>setState나 setTimeout, API 요청과 같은 비동기 함수가 작동할 때 조건문을 걸어 unmount되지 않았을때만 실행할 수 있도록 함</li>
</ul>
<br />

<h2 id="🔰-effect-타이밍">🔰 effect 타이밍</h2>
<ul>
<li>useEffect로 전달된 함수는 <code>컴포넌트 렌더링 - 화면 업데이트 - useEffect 실행</code> 순서로 실행됨.<ul>
<li>즉, <strong>useEffect 실행은 최초 렌더링 이후</strong>에 일어남!!</li>
</ul>
</li>
<li>만약 화면을 다 그리기 전에 동기화를 시키고 싶은 경우?<ul>
<li><strong><code>useLayoutEffect()</code></strong>를 활용하자!</li>
<li>useLayoutEffect로 전달된 함수는 <code>컴포넌트 렌더링 - useLayoutEffect 실행 - 화면 업데이트</code> 순서로 실행되기 때문에, 화면 업데이트 전 동기화가 가능해짐.</li>
</ul>
</li>
</ul>
<br />

<hr>
<h1 id="📑-uselayouteffect">📑 useLayoutEffect()</h1>
<p><img src="https://velog.velcdn.com/images/aroo_ming/post/f32f6e61-b62b-4425-925f-4d2eec13565c/image.webp" alt=""></p>
<ul>
<li>사실상 useEffect()와 거의 동일한 역할을 하는 훅</li>
<li>아래에 명시한 useLayoutEffect()의 선언/ 정의 방식 역시, useEffect()와 거의 동일한 모습을 확인할 수 있다.</li>
</ul>
<br />

<h2 id="🔰-uselayouteffect--구조">🔰 useLayoutEffect  구조</h2>
<pre><code class="language-jsx">useLayoutEffect (function, deps)

useLayoutEffect(() =&gt; {
    ... // 실행할 내용

    return () =&gt; {
        ... // clean-up
    }
}</code></pre>
<ul>
<li>수행하고자 하는 작업인 <code>function</code>과 함수 실행 시점에 영향을 미치는 의존성 배열인 <code>deps</code> 를 매개 변수로 가짐.</li>
<li><code>clean-up</code> 함수 활용 가능.</li>
</ul>
<br />

<h2 id="🔰-uselayouteffect-vs-useeffect">🔰 useLayoutEffect() vs useEffect()</h2>
<h3 id="👀-이벤트-호출-시기">👀 이벤트 호출 시기</h3>
<p>🐝 <strong><code>useEffect</code></strong>: 컴포넌트 마운트 실행 → 브라우저가 화면에 DOM 그리기(화면 업데이트) → effect 함수가 실행됨</p>
<p>🐿️ <strong><code>useLayoutEffect</code></strong>: 컴포넌트 마운트실행 → effect 함수 실행 → 브라우저 화면에 DOM 그리기(화면 업데이트)</p>
<br />

<h3 id="👀-화면-깜빡임">👀 화면 깜빡임</h3>
<p>🐝 <strong><code>useEffect</code></strong>: 컴포넌트들이 render, <strong>paint</strong>된 후 실행되기 때문에, 비동기적으로 실행이 이루어짐</p>
<ul>
<li>함수 내부 DOM에 영향을 주는 코드가 있을 경우, 화면 깜빡임 발생 ⭕️ !<ul>
<li><code>paint</code>: 실제 스크린에 Layout을 표시하고 업데이트하는 과정</li>
</ul>
</li>
</ul>
<p>🐿️ <strong><code>useLayoutEffect</code></strong>: 컴포넌트들이 render된 후 실행되고, 후에 pain가 되기 때문에 동기적으로 실행이 이루어짐</p>
<ul>
<li>함수 내부 DOM을 조작하는 코드가 존재하더라도, 화면 깜빡임 발생 ❌ !</li>
</ul>
<br />

<h3 id="👀-결론">👀 결론</h3>
<ul>
<li>useLayoutEffect()는 동기적으로 실행되며, 내부 코드가 모두 실행된 후 painting 작업을 거치기 때문에 로직이 복잡할 경우 사용자가 레이아웃을 보기까지 오랜 시간이 걸린다.</li>
<li>data fetch, event handler, state reset 등 <code>**기본적인 작업 시에는 useEffect()를 사용**</code>하는 것이 좋다.</li>
<li>단, DOM을 조작하거나 state들이 조건에 따라 첫 painting 시 다르게 렌더링되어야 하는 경우에는 useLayoutEffect()를 사용하는 것이 좋다.<ul>
<li>useEffect 사용 시, re-rendering으로 인해 화면 깜빡임 발생</li>
</ul>
</li>
</ul>
<hr>
<h1 id="📑-usememo">📑 useMemo()</h1>
<ul>
<li>리액트에서 컴포넌트 성능을 최적화하는데 사용되는 훅<ul>
<li>부모 컴포넌트의 값이 변했을 때, 자식 컴포넌트에 발생할 불필요한 리렌더링을 방지</li>
</ul>
</li>
<li>useMemo에서 ‘memo’는 <strong>memoization</strong>을 의미<ul>
<li>‘메모리에 넣는다’는 의미</li>
<li>컴퓨터 프로그램이 동일한 계산을 반복해야할 때, <code>이전에 계산한 값을 메모리에 저장</code>함으로써 동일한 계산의 <code>반복 수행을 제거</code>하여 프로그램 실행 속도를 빠르게 하는 기술</li>
</ul>
</li>
</ul>
<p>즉, 동일한 값을 반환하는 함수를 반복적으로 호출해야 한다면, 처음 값을 계산할 때 해당 값을 메모리에 저장하여 필요할 때마다 다시 계산하지 않고 <code>메모리에서 꺼내서 사용</code></p>
<br />

<h2 id="🔰-usememo--구조">🔰 useMemo  구조</h2>
<pre><code class="language-jsx">useMemo(calculateValue, dependencies)</code></pre>
<ul>
<li><code>calculateValue</code>: 캐시하려는 값을 계산하는 함수<ul>
<li>인수를 취하지 않는 순수 함수여야 함!</li>
</ul>
</li>
<li><code>dependencies</code>: 의존성 배열<ul>
<li><code>특정 값</code>이 들어가는 경우: 콜백 함수 재 호출 → memoization된 값 업데이트 → 다시 memoization 실행</li>
<li><code>빈 배열</code>이 들어가는 경우: 따로 의존성을 갖지 않게 되어, 컴포넌트 mount 시 한 번만 값 계산 → 이후 항상 memoization된 값 꺼내와서 사용</li>
</ul>
</li>
</ul>
<br />

<h2 id="🔰-usememo--주의사항">🔰 useMemo  주의사항</h2>
<ul>
<li>useMemo()를 활용하여 값을 캐싱하려면 <code>구성 요소의 최상위 레벨에서 호출</code>해야 함!</li>
<li><code>restrict mode</code>에서는 계산 함수를 <code>2번 호출</code> 함<ul>
<li>이는 개발 전용 동작이며, <strong>프로덕션에는 영향을 주지 않음</strong>!</li>
<li>계산 함수가 순수함수로 잘 구현되어 있다면, 프로덕션에 영향을 주지 않고 호출 중 하나의 결과는 무시됨.</li>
</ul>
</li>
<li>값을 재활용하기 위해 메모리를 써서 값을 저장하는 훅이기 때문에, <strong>불필요하게 모든 값을 memoization</strong> 해버리면 <code>성능 저하</code>가 발생하게 됨</li>
<li>따라서, <strong><code>꼭 필요한 경우에만 사용할 것</code></strong>을 권장한다 !</li>
</ul>
<br />

<h2 id="🚨-usememo-vs-reactmemo">🚨 useMemo vs React.memo</h2>
<h3 id="🎃-reactmemo">🎃 <strong>React.memo</strong></h3>
<pre><code class="language-jsx">const MyComponent = React.memo((props) =&gt; {
    return (/*컴포넌트 렌더링 코드*/)}
);</code></pre>
<ul>
<li><strong>Higher-Order Components(HOC)</strong><ul>
<li>컴포넌트를 인자로 받아 새로운 컴포넌트를 다시 return 해주는 함수.</li>
<li>인자로 받은 컴포넌트로 새로운 별도의 컴포넌트를 만듦.</li>
</ul>
</li>
<li>불필요한 컴포넌트 렌더링 방지.<ul>
<li>컴포넌트가 같은 props를 받고 같은 결과를 렌더링하는 경우, React.memo를 활용하여 불필요한 컴포넌트 렌더링 방지.</li>
</ul>
</li>
<li>오직 props 변경 여부만을 체크.<ul>
<li>함수 내부에서 useState와 같은 훅을 사용하고있다면, state가 변경될 때마다 렌더링 발생.</li>
<li>props로 들어온 값 비교. (num, str → <strong>실제 값</strong>이 동일한지 비교, object → <strong>참조 <code>reference</code></strong> 여부 비교)</li>
</ul>
</li>
<li>메모이제이션을 하고자 하는 컴포넌트 자체를 React.memo로 감싸줌.</li>
<li>상위 컴포넌트가 리렌더링되더라도 React.memo로 감싸준 컴포넌트는 리렌더링되지 않음.</li>
</ul>
<br />

<h3 id="🎃-usememo">🎃 <strong>useMemo</strong></h3>
<ul>
<li>복잡한 결과 값을 memoization 해서 최척화하기 위한 Hook.</li>
<li>Hook이기 때문에 함수형 컴포넌트 내에서만 사용 가능.</li>
<li>값을 계산하는 과정을 최적화 → 값 반환.</li>
</ul>
<br />

<h3 id="🎃-결론">🎃 <strong>결론</strong></h3>
<p><strong><code>공통점</code></strong></p>
<ul>
<li>props가 변하지 않으면(이전 props와 동일하면) 인자로 넘긴 함수는 재실행되지 않고, 이전 memoized된 결과를 반환함.</li>
</ul>
<p><strong><code>차이점</code></strong></p>
<ul>
<li>React.memo는 HOC, useMemo는 hook.</li>
<li><code>React.memo</code>: 함수이기 때문에 클래스형 컴포넌트와 함수형 컴포넌트 모두 사용 가능.</li>
<li><code>useMemo</code>: hook이기 때문에 오직 함수형 컴포넌트 내에서만 사용 가능.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[TCP/IP 흐름제어와 혼잡제어: 데이터 전송 관리]]></title>
            <link>https://velog.io/@aroo_ming/TCPIP-%ED%9D%90%EB%A6%84%EC%A0%9C%EC%96%B4%EC%99%80-%ED%98%BC%EC%9E%A1%EC%A0%9C%EC%96%B4-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%84%EC%86%A1-%EA%B4%80%EB%A6%AC</link>
            <guid>https://velog.io/@aroo_ming/TCPIP-%ED%9D%90%EB%A6%84%EC%A0%9C%EC%96%B4%EC%99%80-%ED%98%BC%EC%9E%A1%EC%A0%9C%EC%96%B4-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%84%EC%86%A1-%EA%B4%80%EB%A6%AC</guid>
            <pubDate>Thu, 19 Oct 2023 19:26:16 GMT</pubDate>
            <description><![CDATA[<h2 id="🌟-tcpip-란">🌟 TCP/IP 란?</h2>
<p>컴퓨터와 컴퓨터 간의 LAN 또는 WAN에서 원활한 통신을 가능하게 하기 위한 통신 규약이다.</p>
<br />

<p><code>TCP 통신</code>은 클라이언트와 서버가 연결된 상태에서 데이터를 주고 받는 연결 지향적 프로토콜로, 가장 큰 특징은 <code>신뢰성</code>이다.</p>
<p>신뢰성이란, 통신 중간에 데이터가 유실되지 않는 것을 뜻한다. </p>
<p>신뢰성을 구성하는 대표적인 방법으로는 <code>흐름제어</code>와 <code>혼잡제어</code>가 있다.</p>
<br />

<hr>
<h2 id="🌟-흐름-제어-end-to-end">🌟 흐름 제어 (End to End)</h2>
<p>흐름 제어는 송신 측과 수신 측의 <code>데이터 처리 속도를 일치</code>시키는 기법이다.</p>
<p>수신 측이 송신 측보다 데이터 처리 속도가 느릴 경우, 데이터를 손실할 위험이 존재하게 된다. 흐름 제어는 송신 측에서 전송하는 데이터의 양을 제어하는 방식으로 데이터 처리속도를 일치시킨다. </p>
<p>즉, 흐름 제어는 <code>송신 측과 수신 측 사이의 **패킷** 수를 제어</code>하는 기능이라고 할 수 있다.</p>
<p>→ <code>패킷</code> (Packet): 인터넷에서 라우팅을 효과적으로 하기 위해 나눈 데이터 조각</p>
<br />

<p><strong>[흐름 제어가 필요한 상황]</strong></p>
<ul>
<li>송신 측 데이터를 전송량 &gt; 수신 측 데이터 처리량<ul>
<li>수신자의 버퍼에 미처 처리하지 못한 데이터가 쌓임</li>
<li>수신자의 버퍼 가득 참 → 버퍼가 가득 차있는 동안 전달된 데이터 손실 위험</li>
</ul>
</li>
</ul>
<br />

<p><strong>[흐름 제어의 해결 방식]</strong></p>
<ul>
<li>송신자의 데이터 전송 속도 조절<ul>
<li>수신자가 데이터를 처리하는 속도만큼 송신자가 데이터를 천천히 보내게 하여 문제 해결</li>
</ul>
</li>
</ul>
<br />

<h3 id="👣-stop-and-wait-→-비효율적">👣 Stop and Wait (→ 비효율적)</h3>
<ul>
<li>매번 전송한 패킷에 대해 확인 응답(ACK)을 받으면 다음 패킷을 전송하는 방법.</li>
<li>패킷을 하나씩 보내기 때문에, <code>비효율적인 방식</code>!</li>
</ul>
<p><img src="https://velog.velcdn.com/images/aroo_ming/post/6826249c-04d8-4a9d-b61f-7eace2f9d4d9/image.png" alt=""></p>
<p>💦 <strong>데이터 보낼 때마다 아래 과정 반복</strong> 💦</p>
<ol>
<li>송신자 → [데이터] → 수신자</li>
<li>수신자 → [남은 버퍼 공간] → 송신자 (데이터 받을 때마다 알려줌)</li>
<li>송신자 → [수신자가 보낸 정보] → 자신의 데이터 전송 속도 조절</li>
</ol>
<p>⇒ 매번 전송한 패킷에 대한 확인 응답을 받아야 다음 패킷 전송 가능한 구조.</p>
<p>⇒ 구현은 간단하지만 매우 비효율적 !!</p>
<br />

<h3 id="👣-sliding-window-방식-→-stop-and-wait-방식-개선">👣 Sliding Window 방식 (→ Stop and Wait 방식 개선)</h3>
<ul>
<li>데이터를 하나만 보내고 기다리는 것이 아닌, 한 번에 여러 개의 데이터 전송 → 수신자의 응답 기다림</li>
<li>송신 측이 수신 측에서 받은 <strong>윈도우 크기</strong>를 참고해서 데이터의 흐름을 제어하는 방식<ul>
<li><code>윈도우 크기</code>: 단위 시간 내에 보내는 패킷 수</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/aroo_ming/post/8486af38-b720-4596-b2b4-73e1f0831d8d/image.png" alt=""></p>
<p>💦 <strong>데이터를 다 받을 때까지 아래 과정 반복</strong> 💦</p>
<ol>
<li><p>수신자 → 최초 윈도우 크기를 7로 설정</p>
</li>
<li><p>송신자 → 수신자의 확인 응답(ACK)을 받기 전까지 데이터 전송</p>
<ul>
<li>재전송<ul>
<li>일정 시간동안 수신 측으로부터 확인 응답(ACK)을 받지 못하면 패킷 재전송</li>
<li>송신 측 재전송 → 수신 측 버퍼의 남은 공간 없는 경우 → 문제 발생<ul>
<li>이를 해결하기 위해, 확인 응답(ACK)을 보내면서 남은 버퍼의 크기(윈도우 크기)도 함께 전송</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li><p>수신자 → 확인 응답(ACK) → 송신자에게 전송 → 윈도우 크기를 충족할 수 있도록 윈도우를 옆으로 이동 시킴.</p>
</li>
</ol>
<br />

<hr>
<h2 id="🌟-혼잡-제어-end-to-network">🌟 혼잡 제어 (End to Network)</h2>
<p>혼잡 제어는 송신 측의 데이터 전달과 <code>네트워크의 데이터 처리 속도 차이를 해결</code>하기 위한 기법이다.</p>
<p>데이터의 양이 라우터가 처리할 수 있는 양을 초과할 경우, 라우터는 데이터를 처리하지 못하고 송신 측에서는 해당 데이터를 손실 데이터로 간주하게 된다. 혼잡 제어는 송신 측의 데이터 전송 속도를 적절히 조절하여 데이터 처리 속도 차이를 해결한다.</p>
<p>즉, 혼잡 제어는 <code>네트워크 내의 패킷 수를 조절</code>하여 네트워크의 오버 플로우를 방지하는 기능이라고 할 수 있다.</p>
<br />

<p><strong>[혼잡 제어가 필요한 상황]</strong></p>
<ul>
<li>송신자가 사용하는 네트워크에 엄청나게 많은 인원이 데이터를 전송하는 경우<ul>
<li>라우터가 데이터를 처리하는 속도보다 많은 양의 데이터가 들어오면 문제 발생</li>
<li>송신자 → <strong>라우터</strong>가 처리하지 못한 데이터를 잃어버린 것으로 간주 → 계속해서 데이터 재전송<ul>
<li><code>라우터</code>(router): 네트워크에서 송신자가 보낸 데이터를 알맞은 수신자에게 보내주는 장치/ 데이터 처리 속도와 처리할 데이터를 보관해두는 버퍼 존재</li>
</ul>
</li>
<li>이러한 과정이 반복되면 네트워크는 점점 혼잡해짐.</li>
</ul>
</li>
</ul>
<br />

<p><strong>[혼잡 제어 해결 방식]</strong></p>
<ul>
<li>송신자의 데이터 전송 속도 조절<ul>
<li>네트워크에 전송되는 데이터가 과도하게 증가하는 현상 방지</li>
</ul>
</li>
</ul>
<br />

<h3 id="👣-aimdadditive-increase-multiplicative-decrease">👣 AIMD(Additive Increase/ Multiplicative Decrease)</h3>
<ul>
<li>패킷을 하나씩 전송하다가 문제없이 도착 시, 윈도우 크기 1씩 증가시켜가며 전송하는 방법</li>
<li>패킷 전송에 실패 시, 네트워크가 혼잡하다 판단 → 패킷 전송 속도 절반으로 줄임.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/aroo_ming/post/11ae571d-a22b-42bf-abbe-894eebec3585/image.png" alt=""></p>
<p>💦 <strong>특징 💦</strong></p>
<ul>
<li>공평한 방식</li>
<li>여러 호스트가 한 네트워크를 공유하고 있으면 나중에 진입하는 쪽이 불리하지만, 시간이지나면 평형상태로 수렴.</li>
</ul>
<p>💦 <strong>문제점 💦</strong></p>
<ul>
<li>초기 네트워크의 높은 대역폭을 사용하지 못함 → 오랜 시간 소요 → 네트워크가 혼잡해지는 상황을 미리 감지하지 못함.</li>
<li>즉, 네트워크가 혼잡해지고 나서야 대역폭을 줄이는 방식</li>
</ul>
<br />

<h3 id="👣-slow-start-느린-시작">👣 Slow Start (느린 시작)</h3>
<ul>
<li>AIMD 방식: 네트워크의 수용량 주변에서는 효율적으로 작동하지만, 초기 전송 속도를 올리는데 오랜 시간이 소요되는 단점 존재.</li>
<li>패킷을 하나씩 전송하다가 문제없이 도착 시, 각각의 ACK 패킷마다 윈도우 크기 1씩 증가시킴.<ul>
<li>한 주기가 지나면 윈도우 크기는 2배가 됨.</li>
</ul>
</li>
<li>전송 속도는 AIMD에 반해 지수 함수 꼴로 증가<ul>
<li>단, 혼잡 현상 발생 시, 윈도우 크기를 1로 떨어뜨림.</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/aroo_ming/post/a3cba096-073b-44eb-bf3e-a6e70e628bfa/image.png" alt=""></p>
<p>💦 <strong>특징 💦</strong></p>
<ul>
<li>초기 네트워크의 수용량을 예상할 수 있는 정보 없음.</li>
<li>그러나 한번 혼잡 현상이 발생하고 나면 네트워크의 수용량을 어느정도 예상 가능</li>
<li>ACK가 도착할 때마다 윈도우 크기를 증가 시킴<ul>
<li>초기 윈도우 크기가 느리게 증가할 지 라도, 시간이 지날 수록 윈도우 크기가 점점 빠르게 증가!</li>
<li>혼잡 현상이 발생했던 윈도우 크기의 절반까지는 지수 함수 꼴로 창 크기 증가 → 완만하게 1씩 증가</li>
</ul>
</li>
</ul>
<br />

<h3 id="👣-fast-retransmit-빠른-재전송">👣 Fast Retransmit (빠른 재전송)</h3>
<ul>
<li>TCP의 혼합 조절에 추가된 정책.</li>
<li>패킷을 받는 쪽에서 먼저 도착 해야할 패킷이 도착하기 전, 다음 패킷이 도착한 경우에도 ACK 패킷 보냄.</li>
<li>순서대로 잘 도착한 마지막 패킷의 다음 패킷 순번을 ACK 패킷에 실어서 보냄.<ul>
<li>중간에 하나의 패킷 손실 → 송신 측: 순번이 중복된 ACK 패킷 받음.</li>
<li>이를 감지하는 순간, 문제가 되는 순번의 패킷 재전송.</li>
</ul>
</li>
<li>중복된 순번의 패킷을 3개 받으면 재전송.<ul>
<li>혼잡한 상황 감지 → 윈도우 크기 줄임.</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/aroo_ming/post/857562eb-ffa7-4a91-b242-2657880eb878/image.png" alt=""></p>
<p>💦 <strong>특징 💦</strong></p>
<ul>
<li>송신 측은 자신이 설정한 타임아웃 시간이 지나지 않았어도 바로 손실 패킷 재전송 가능<ul>
<li>보다 빠른 재전송률 유지 가능</li>
</ul>
</li>
</ul>
<br />

<h3 id="👣-fast-recovery-빠른-회복">👣 Fast Recovery (빠른 회복)</h3>
<ul>
<li>혼잡한 상태 → 윈도우 크기를 1이 아니라 반으로 줄이고, 선형 증가시킴.</li>
<li>혼잡 상황을 한 번 겪은 이후로는 AIMD 방식으로 동작.</li>
</ul>
<br />



<hr>
<h2 id="🌟-마무리">🌟 마무리</h2>
<p>흐름 제어와 혼잡 제어는 모두 <code>TCP의 신뢰성있는 데이터 전송</code>을 위한 기능이다.</p>
<p>두 기능의 공통점은 송신자의 전송 속도를 제어함으로써 데이터 전송 시의 오류를 줄이는 기능이라는 것이다.</p>
<p>두 기능의 차이점은 <strong>흐름 제어</strong>는 <strong>송신 측과 수신 측</strong>의 패킷 수를 제어하는 기능이고, <strong>혼잡 제어</strong>는 <strong>네트워크 내</strong>의 패킷 수를 조절하는 기능이라는 것이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[TCP 3-way handshake와 4-way handshake: 연결 설정과 해제 과정]]></title>
            <link>https://velog.io/@aroo_ming/TCP-3-way-handshake%EC%99%80-4-way-handshake-%EC%97%B0%EA%B2%B0-%EC%84%A4%EC%A0%95%EA%B3%BC-%ED%95%B4%EC%A0%9C-%EA%B3%BC%EC%A0%95</link>
            <guid>https://velog.io/@aroo_ming/TCP-3-way-handshake%EC%99%80-4-way-handshake-%EC%97%B0%EA%B2%B0-%EC%84%A4%EC%A0%95%EA%B3%BC-%ED%95%B4%EC%A0%9C-%EA%B3%BC%EC%A0%95</guid>
            <pubDate>Thu, 19 Oct 2023 16:14:43 GMT</pubDate>
            <description><![CDATA[<h2 id="🌟-tcp란-transmission-control-protocol">🌟 <strong>TCP란? (Transmission Control Protocol)</strong></h2>
<p>인터넷 상에서 데이터를 메시지 형태로 보내기 위해 <strong>IP와 함께 사용하는 프로토콜</strong>을 말한다.</p>
<p>즉, 클라이언트와 서버가 연결된 상태에서 데이터를 주고 받는 <code>연결 지향적 프로토콜</code>이라고 할 수 있다.</p>
<p>일반적으로 TCP는 IP와 함께 사용되며 <strong>IP</strong>는 <code>배달</code>을, <strong>TCP</strong>는 <code>패킷의 추적 및 관리</code>를 하게 된다. </p>
<p>TCP는 연결형 서비스로, <code>신뢰적인 전송을 보장</code>하기 때문에 데이터의 흐름제어와 혼잡제어를 수행한다.</p>
<br />

<h3 id="👣-tcp-특징">👣 TCP 특징</h3>
<ul>
<li><code>3-way handshake</code> 과정을 통해 연결을 <code>수립</code>하고, <code>4-way handshake</code>을 통해 연결을 <code>해제</code>한다.</li>
<li>연결 지향적 → 상대방이 내 신호를 받을 수 있는지 확인하고 전송</li>
<li>높은 신뢰성 보장</li>
<li>흐름 제어 및 혼잡 제어 수행</li>
</ul>
<br />


<h3 id="👣-tcp-작동-방식-큰-틀">👣 TCP 작동 방식 (큰 틀)</h3>
<ul>
<li>데이터 전송 전, 3-way handshake → 논리적 접속 성립<ul>
<li>모든 데이터는 고정된 통신 선로를 통해 전달 됨</li>
</ul>
</li>
<li>데이터 전송이 끝나면  4-way handshake → 데이터가 모두 전달됐는지 확인 → 연결 해제</li>
</ul>
<p>따라서, TCP는 서버와 클라이언트 간의 데이터를 신뢰성 있게 전달하기 위해 만들어진 프로토콜이다.</p>
<p>데이터를 정확하고 안정적으로 전달할 수 있기 때문에 높은 신뢰성을 보장한다.</p>
<br />

<hr>
<h2 id="🌟-tcp-3-way-handshake-란">🌟 <strong>TCP 3-way handshake 란?</strong></h2>
<ul>
<li>전송제어 프로토콜(TCP)에서 통신하는 장치 간 연결이 잘 되었는지 확인하는 과정/ 방식.</li>
<li>TCP/IP 4계층을 통과, 데이터를 보낼 준비가 되면 수신 측이 받을 준비가 되었는지 확인하는 과정.</li>
<li>3-way handshake를 수행하기 위해서는 TCP 헤더에 표시한 <code>SYN</code>과 <code>ACK</code> 플래그들이 사용됨.</li>
</ul>
<br />

<h3 id="👣-3-way-handshake-과정">👣 3-way handshake 과정</h3>
<p><img src="https://velog.velcdn.com/images/aroo_ming/post/b55c419c-1a12-432d-8f12-28f6bcfb73f6/image.png" alt=""></p>
<ol>
<li><p>클라이언트 → 서버에 접속 요청 메시지  SYN(x) 패킷 전송.</p>
<ul>
<li><code>SYN</code>(Synchronize Sequence Number):  임의의 랜덤 숫자</li>
</ul>
</li>
<li><p>서버 → SYN(x) 받음 → 클라이언트에게 연결요청 메시지 ACK(x+1) + SYN(y) 패킷 전송.</p>
<ul>
<li><code>ACK</code>(Acknowledgement number: SYN+1의 값</li>
</ul>
</li>
<li><p>클라이언트 → ACK(x+1) + SYN(y) 받음 → 서버에 ACK(y+1) 전송.</p>
</li>
</ol>
<p>⇒ 3️⃣번의 통신 완료 후, <code>연결 성립(Established)</code></p>
<br />

<hr>
<h2 id="🌟-tcp-4-way-handshake-란">🌟 <strong>TCP 4-way handshake 란?</strong></h2>
<ul>
<li>연결 성립 후, 모든 통신이 끝났다면 이를 해제해야 함.</li>
<li>전송제어 프로토콜(TCP)에서 <code>통신을 중단</code>할 때 사용하는 과정/ 방식<ul>
<li>연결 중단 시, 한 번 더 확인 후 해제</li>
</ul>
</li>
<li>데이터 송수신이 완료되면 <code>TCP 연결을 해제</code>하는 과정</li>
<li>4-way handshake를 수행하기 위해서는 TCP 헤더에 표시한 <code>ACK</code>와 <code>FIN</code> 플래그들이 사용됨.</li>
</ul>
<br />

<h3 id="👣-tcp-4-way-handshake-과정">👣 TCP 4-way handshake 과정</h3>
<p><img src="https://velog.velcdn.com/images/aroo_ming/post/44c7baf5-77f1-4c5a-8a4e-8a1b6640d594/image.png" alt=""></p>
<ol>
<li><p>클라이언트 → 서버에게 연결 종료를 알리는 FIN 플래그가 설정된 패킷 전송.</p>
<ul>
<li><code>FIN</code> (finish): 세션을 종료시키는데 사용됨, 더이상 보낸 데이터가 없음을 의미</li>
</ul>
</li>
<li><p>서버 → FIN 신호 확인 → 확인했다고 알려주기 위해 ACK 신호 전송.</p>
<ul>
<li>이때, 모든 데이터를 보내기 위해 CLOSE_WAIT 상태 돌입.</li>
</ul>
</li>
<li><p>서버 통신 끝 → 연결 종료 요청 합의의 의미로 클라이언트에게 FIN 플래그가 설정된 패킷 전송.</p>
</li>
<li><p>클라이언트 → FIN 받음 → 확인의 의미로 ACK 플래그가 설정된 패킷 보냄</p>
<ul>
<li>아직 서버로부터 받지 못한 데이터가 있을 수 있으므로 TIME_WAIT을 통해 기다림</li>
<li>TIME_WAIT: 의도치 않은 에러로 인해 연결이 데드락으로 빠지는 것 방지 목적<ul>
<li>에러 발생 → 종료 지연 → 타임 초과 → CLOSED 상태로 변경</li>
</ul>
</li>
<li>서버 → ACK 받은 이후 소켓 닫음 (Closed)</li>
<li>클라이언트 → TIME_WAIT 시간이 끝나면 클라이언트 닫음(Closed)</li>
</ul>
</li>
</ol>
<p>⇒ 4️⃣번의 통신 이후, <code>연결 해제</code></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SWR과 ReactQuery 비교하기]]></title>
            <link>https://velog.io/@aroo_ming/SWR%EA%B3%BC-ReactQuery-%EB%B9%84%EA%B5%90%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@aroo_ming/SWR%EA%B3%BC-ReactQuery-%EB%B9%84%EA%B5%90%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 20 Aug 2023 09:48:12 GMT</pubDate>
            <description><![CDATA[<h2 id="💎-swr">💎 SWR</h2>
<ul>
<li>데이터 가져오기를 위한 react hook</li>
<li>캐시(stale)로부터 데이터 반환 → fetch 요청 → 최신화된 데이터 가져오기</li>
<li>즉, 다른 컴포넌트에서 동일한 상태를 사용하고자 하는 경우 이전에 캐시했던 상태를 그대로 사용<ul>
<li>서버로 재요청 하는 방식 ❌❌</li>
<li>서로 다른 컴포넌트에서 동일한 상태 공유 가능!</li>
</ul>
</li>
<li>한 줄의 코드로 데이터 가져오기 가능</li>
</ul>
<br />

<h2 id="💎-swr--react-query와-비교하여-자세히-알아보기">💎 SWR ! React Query와 비교하여 자세히 알아보기</h2>
<p>🔥 <code>React Query</code></p>
<p>리액트 어플리케이션에서 서버 상태를 가져오고, 캐싱, 동기화, 업데이트하는 것을 쉽게 해줌.</p>
<pre><code class="language-jsx">import { QueryClient, QueryClientProvider, useQuery } from &quot;react-query&quot;;

const queryClient = new QueryClient();
const url = &quot;https://61b88c9d64e4a10017d19053.mockapi.io/user&quot;;

const App = () =&gt; (
  &lt;div&gt;
    &lt;QueryClientProvider client={queryClient}&gt;
      &lt;ReactQueryProfile /&gt;
    &lt;/QueryClientProvider&gt;
  &lt;/div&gt;
);

const ReactQueryProfile = () =&gt; {
  const {isLoading, error, data, isFetching} = useQuery(&quot;users&quot;, () =&gt;
    fetch(&quot;https://61b88c9d64e4a10017d19053.mockapi.io/user&quot;).then(res =&gt; res.json())
  );

  if (error) return &lt;div&gt;failed to load&lt;/div&gt;;
  if (isLoading) return &lt;div&gt;loading...&lt;/div&gt;;

  return &lt;Profile library=&quot;React Query&quot; data={data} /&gt;;
}

const Profile = ({library, data}) =&gt; (
  &lt;div&gt;
    &lt;h1&gt;Users from {library}&lt;/h1&gt;
    {data.map(user =&gt; &lt;p&gt;{user.level} developer &lt;strong&gt;{user.name}&lt;/strong&gt;&lt;/p&gt;)}
  &lt;/div&gt;
)

export default App;</code></pre>
<p>🌊 <code>SWR</code></p>
<p>캐시에서 데이터를 반환한 후, 서버에 데이터를 가져오는 요청을 보내고, 마지막으로 최신 데이터를 제공함.</p>
<pre><code class="language-jsx">import useSWR from &quot;swr&quot;;

const App = () =&gt; (
  &lt;div&gt;
    &lt;SWRProfile /&gt;
  &lt;/div&gt;
);

const SWRProfile = () =&gt; {
  const {data, error} = useSWR(&quot;https://61b88c9d64e4a10017d19053.mockapi.io/user&quot;, url =&gt;
    fetch(url).then(res =&gt; res.json())
  );

  if (error) return &lt;div&gt;failed to load&lt;/div&gt;;
  if (!data) return &lt;div&gt;loading...&lt;/div&gt;;

  return &lt;Profile library=&quot;SWR&quot; data={data} /&gt;;
}

const Profile = ({library, data}) =&gt; (
  &lt;div&gt;
    &lt;h1&gt;Users from {library}&lt;/h1&gt;
    {data.map(user =&gt; &lt;p&gt;{user.level} developer &lt;strong&gt;{user.name}&lt;/strong&gt;&lt;/p&gt;)}
  &lt;/div&gt;
)

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

<h3 id="1-provider">1. Provider</h3>
<p>🔥 <code>React Query</code></p>
<p>Provider로 컴포넌트를 감싸지 않을 경우, 에러 발생함.</p>
<p>🌊 <code>SWR</code></p>
<p>별도의 Provider 없이 컴포넌트에서 바로 사용 가능함.</p>
<br />

<h3 id="2-fetcher">2. Fetcher</h3>
<blockquote>
<p>모두 두 번째 인자로 fetcher를 받음.</p>
</blockquote>
<p>🔥 <code>React Query</code></p>
<p>fetcher에 url 직접 전달해야 함.</p>
<p>꼭! 두 번째 인자에 fetcher를 넘겨줘야 함.</p>
<p>🌊 <code>SWR</code></p>
<p>fetcher의 인자로 useSWR의 첫 번째 인자를 넘겨줌.</p>
<p>전역 성정을 통해 fetcher 정해둘 수 있음.</p>
<br />

<h3 id="3-mutation">3. Mutation</h3>
<p>🔥 <code>React Query</code></p>
<p>post/ patch/ put/ delete를 통해 서버의 상태를 변형시키는 것.</p>
<p>🌊 <code>SWR</code></p>
<p>useSWR()을 통해 받아온 데이터를 클라이언트 사이드에서 변형시켜 업데이트 해주는 것.</p>
<br />

<h3 id="4-selectors">4. Selectors</h3>
<p>🔥 <code>React Query</code></p>
<p>select를 이용해 데이터를 가공할 수 있음.</p>
<p>🌊 <code>SWR</code></p>
<p>데이터 가공은 불가능함.</p>
<br />

<h3 id="5-offline-mutation">5. Offline Mutation</h3>
<p>🔥 <code>React Query</code></p>
<p>오프라인 상태에서 뮤테이션을 시도했을 때, 해당 요청을 잠시 멈췄다가 온라인 상태가 되면 요청 재시도함.</p>
<p>🌊 <code>SWR</code></p>
<p>API를 멈췄다가 다시 시도하기 때문에, 서버 데이터를 변경하는 것은 불가능함.</p>
<br />

<h3 id="6-auto-garbage-collection">6. Auto Garbage Collection</h3>
<p>🔥 <code>React Query</code></p>
<p>지정된 시간(기본 5분)동안 쿼리가 사용되지 않을 경우, 자동으로 가비지 컬렉션 지원.</p>
<p>🌊 <code>SWR</code></p>
<p>SWR에서는 별도로 제공하고 있지 않음.</p>
<br />

<hr/>

<h2 id="📚-참고-자료">📚 참고 자료</h2>
<p><a href="https://swr.vercel.app/ko">https://swr.vercel.app/ko</a></p>
<p><a href="https://tech.madup.com/react-query-vs-swr/">https://tech.madup.com/react-query-vs-swr/</a></p>
<p><a href="https://velog.io/@seohee0112/React-Query-vs-SWR">https://velog.io/@seohee0112/React-Query-vs-SWR</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[카카오 로그인 구현하기 (REST API)]]></title>
            <link>https://velog.io/@aroo_ming/%EC%B9%B4%EC%B9%B4%EC%98%A4-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-REST-API</link>
            <guid>https://velog.io/@aroo_ming/%EC%B9%B4%EC%B9%B4%EC%98%A4-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-REST-API</guid>
            <pubDate>Thu, 03 Aug 2023 08:18:00 GMT</pubDate>
            <description><![CDATA[<h2 id="🪄-카카오-로그인-구현-방식">🪄 카카오 로그인 구현 방식</h2>
<p>카카오 로그인 구현 방식은 크게 <code>2가지</code>로 나뉜다.</p>
<ol>
<li><strong><code>JavaScript SDK</code></strong> 활용</li>
<li><strong><code>REST API</code></strong> 활용</li>
</ol>
<p>Kakao Developers 문서에 들어가보면, <code>REST API</code>를 활용한 카카오 로그인은 <code>PC 및 모바일 웹</code>에서 구현시 적합한 방식이라고 명시되어있다.
나는 모바일 웹에서 카카오 로그인을 구현할 예정이었기에, REST API를 활용하여 해당 기능을 구현하게 되었다.</p>
<p>실제 코드를 구현하기 전, 먼저 postman에서 로직을 실행해보았다.</p>
<hr />


<h2 id="📜-핵심-플로우">📜 핵심 플로우</h2>
<ol>
<li>카카오 서버에 인가코드를 요청한다.
→ 본인 인증 성공 시, 인가코드가 발급된다.</li>
<li>인가코드를 활용하여 개인 token을 발급받는다.
→ 로그인 성공 시, token이 발급된다.</li>
<li>token으로 로그인을 유지한다.</li>
</ol>
<hr />


<h2 id="📮-인가코드-및-token-발급받기">📮 인가코드 및 token 발급받기</h2>
<p><a href="https://developers.kakao.com/console/app">KakaoDevelopers</a></p>
<p>1️⃣ 위의 사이트에 들어가서 새로운 어플리케이션을 만들어준다.
(개인적으로 테스트해보는 용도라면, 사업자 명도 자유롭게 적으면 된다!)
<img src="https://velog.velcdn.com/images/aroo_ming/post/d5ad242d-d452-480f-9f68-56d6790c0264/image.png" alt=""></p>
<br />


<p>2️⃣ <code>내 애플리케이션 &gt; 제품 설정 &gt; 카카오 로그인</code>에 들어간다.
<img src="https://velog.velcdn.com/images/aroo_ming/post/0a3b0b02-8f98-4499-b761-f3ccc729a1e9/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/aroo_ming/post/ceea3c54-2cb8-44cb-bc07-85875802821c/image.png" alt=""> </p>
<p>→ 카카오 로그인을 활성화 해주고, Redirect URI도 추가한다.</p>
<pre><code>https://getpostman.com/oauth2/callback</code></pre><br />


<p>3️⃣ <code>내 애플리케이션 &gt; 앱 설정 &gt; 요약 정보</code>에 들어가서 <code>REST API 키</code>를 확인한다.
<img src="https://velog.velcdn.com/images/aroo_ming/post/22c45b0c-9b20-4c06-9dee-d7246b36e238/image.png" alt=""></p>
<br />


<p>4️⃣ postman에서 oauth2.0 인증을 해본다.
🚨 이 때, <strong>postman</strong> 웹 사이트가 아닌 <strong>앱</strong>에서 진행을 해야 제대로 oauth2.0 인증 테스트를 할 수 있다! 🚨
<img src="https://velog.velcdn.com/images/aroo_ming/post/c88c6fbf-ff05-481b-9e25-beef9baf7140/image.png" alt=""></p>
<br />



<p>5️⃣ 아래의 사진을 참고하여 정보를 채워준 후, <code>Get New Access Token</code>을 눌러준다.
<img src="https://velog.velcdn.com/images/aroo_ming/post/2004e416-ad37-4b89-afd5-0b5806a2c2a0/image.png" alt=""></p>
<ul>
<li><code>Token Name</code> : kakao rest API</li>
<li><code>Callback URL</code> : Authorization Code 방식으로 토큰 발급을 받는 url/  Redirect URI 부분에 추가한 주소 적어준다. 안되면 006 Error 뜬다. (postman에서 실습하기 때문에, postman 주소로 적어줌.)</li>
<li><code>Auth URL</code> : OAUTH를 지원해 주는 서버에 로그인을 하기 위한 주소</li>
<li><code>Access Token URL</code> : 발급 받은 인증 코드를 포함하여 req 하여 res로 token을 받기 위한 URL</li>
<li><code>Client ID</code> : REST API 키</li>
<li><code>Client Secret</code> : 안 적어도 되지만, Client Secret을 따로 설정해줬다면 적어줘야 한다. 
(내 애플리케이션 &gt; 제품 설정 &gt; 카카오 로그인 &gt; 보안)</li>
</ul>
<br />

<p>6️⃣ <code>Get New Access Token</code> 버튼을 클릭하면 아래와 같은 창이 뜬다.
<img src="https://velog.velcdn.com/images/aroo_ming/post/df28d274-401d-45eb-a264-e5dce9904c0b/image.png" alt=""></p>
<br />

<p>7️⃣ 로그인에 성공하면 <code>토큰</code>이 발급되고, Use Token 버튼을 클릭하면 postman에서 자동으로 토큰을 저장한다.
<img src="https://velog.velcdn.com/images/aroo_ming/post/0e5f454d-3512-49ad-ab49-220b972ccfc9/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/aroo_ming/post/3e40758a-b0bc-4298-adee-24c7cce87773/image.png" alt=""></p>
<hr />


<h2 id="☑️-코드에-활용하기">☑️ 코드에 활용하기</h2>
<p>1️⃣ Redirect URI에 콜백 주소를 추가하고, 해당 주소와 REST API 값을 env 파일에 저장한다.
<img src="https://velog.velcdn.com/images/aroo_ming/post/031d1ea5-0092-46ed-958e-f4a425da3eb1/image.png" alt=""></p>
<br />

<p>2️⃣ oAuth.ts 파일을 만들어서 해당 정보를 상수파일에 저장해둔다.
이때, <code>vite</code>로 react를 시작한 경우 env에 저장된 값을 불러오고 싶다면 <code>import.meta.env.VITE_APP_~~</code>를 사용해야 한다!!</p>
<pre><code>export const CLIENT_ID = import.meta.env.VITE_APP_REST_API_KEY;
export const REDIRECT_URI = import.meta.env.VITE_APP_REDIRECT_URI;

// prompt=login: 기존 사용자 인증 여부와 상관없이, 로그인 화면 출력 (추가 설정/ 필수 아님!)
export const KAKAO_AUTH_URL = `https://kauth.kakao.com/oauth/authorize?client_id=${CLIENT_ID}&amp;redirect_uri=${REDIRECT_URI}&amp;response_type=code&amp;prompt=login`;
</code></pre><br />

<p>3️⃣ 카카오 서버에 요청보내기</p>
<pre><code>import { KAKAO_AUTH_URL } from &quot;../constant/OAuth&quot;;
import loginLogo from &quot;../assets/loginLogo.png&quot;;

function KakaoLogin() {
  return (
    &lt;div&gt;
      &lt;a href={KAKAO_AUTH_URL}&gt;
        &lt;img
          src={loginLogo}
          alt=&quot;카카오로 계속하기&quot;
        /&gt;
      &lt;/a&gt;
    &lt;/div&gt;
  );
}

export default KakaoLogin;
</code></pre><p>→ 이미지 클릭 시, 콜백주소로 이동</p>
<pre><code>function KakaoCallback() {
  // code: 카카오로부터 받은 인가코드
  const code = new URL(window.location.href).searchParams.get(&quot;code&quot;);
  const grantType = &quot;authorization_code&quot;;

  useEffect(() =&gt; {
    if (code) {
      // 토큰을 받기 위한 코드 (access_token(6시간), refresh_token(2달) 받아올 수 있음)
      axios
        .post(
          `https://kauth.kakao.com/oauth/token?grant_type=${grantType}&amp;client_id=${CLIENT_ID}&amp;redirect_uri=${REDIRECT_URI}&amp;code=${code}`,
          {},
          {
            headers: {
              &quot;Content-type&quot;: &quot;application/x-www-form-urlencoded;charset=utf-8&quot;,
            },
          }
        )
        .then((res: resInfo) =&gt; {
          console.log(res);
          const { access_token } = res.data;
          // 유저 개인정보 받아오기 위한 호출 코드
          axios
            .post(
              `https://kapi.kakao.com/v2/user/me`,
              // post body (post 통신 시, 데이터가 없다면 빈 객체로 보냄)
              {},
              // request headers
              {
                headers: {
                  // Authorization: 사용자 인증 수단, 엑세스 토큰 값
                  Authorization: `Bearer ${access_token}`,
                  &quot;Content-type&quot;:
                    &quot;application/x-www-form-urlencoded;charset=utf-8&quot;,
                },
              }
            )
            .then((res: resInfo) =&gt; {
              // 유저 id도 발급해줌
              console.log(res);
              // 유저 닉네임 가져오기
              console.log(res.data.kakao_account.profile);
            });
        })
        .catch((Error: object) =&gt; {
          console.log(Error);
        });
    }
  }, [code]);
  return &lt;div&gt;&lt;/div&gt;;
}</code></pre><p>→ 콜백 주소에 등록해놓은 컴포넌트
→ 이렇게 하면 토큰 및 유저 정보를 가져올 수 있음!</p>
<hr />

<h2 id="💪-마무리">💪 마무리</h2>
<ul>
<li>처음 소셜 로그인을 구현했을 때, 여러 자료를 참고해도 잘 감이 오지 않아 많이 헤맸던 기억이 있다.</li>
<li>개인적으로는, 무작정 코드로 구현하지 않고 postman을 활용하여 pre-task를 진행한 후에 코드 구현을 했던 것이 소셜 로그인에 대한 이해를 높이는데 많은 도움이 됐다!</li>
<li>소셜 로그인을 구현하고자 하는 많은 사람들에게 이 글이 도움이 되길 바라며..!!</li>
</ul>
<br />
]]></description>
        </item>
        <item>
            <title><![CDATA[React query]]></title>
            <link>https://velog.io/@aroo_ming/React-query</link>
            <guid>https://velog.io/@aroo_ming/React-query</guid>
            <pubDate>Fri, 21 Jul 2023 12:16:48 GMT</pubDate>
            <description><![CDATA[<h1 id="💎-react-query">💎 React query</h1>
<ul>
<li>fetching, caching, 서버 데이터와의 동기화를 지원해주는 라이브러리</li>
<li><code>비동기 처리를</code> 쉽게 관리할 수 있는 라이브러리</li>
<li><code>Client state</code>에 적합한 라이브러리</li>
<li>react component 내부에서 간단하고 직관적으로 api 사용 가능</li>
<li>api 요청 관련 번잡한 작업 없이 “핵심 로직”에 집중 가능</li>
</ul>
<br />

<h2 id="0️⃣-상태">0️⃣ 상태</h2>
<ul>
<li><p><code>fresh</code></p>
<ul>
<li><p>요청이 <strong>만료되지 않은</strong> 쿼리</p>
</li>
<li><p>새롭게 <strong>추가</strong>된 쿼리</p>
</li>
<li><p><strong>최신</strong> 상태의 쿼리</p>
<br />
</li>
</ul>
</li>
<li><p><code>fetching</code></p>
<ul>
<li><strong>요청 중</strong>인 쿼리</li>
</ul>
</li>
</ul>
<br />

<ul>
<li><code>stale</code><ul>
<li>요청이 <strong>만료</strong>된 쿼리</li>
<li><strong>기존</strong> 상태의 쿼리</li>
</ul>
</li>
</ul>
<br />

<ul>
<li><code>inactive</code><ul>
<li><strong>비활성화</strong>된 쿼리</li>
</ul>
</li>
</ul>
<br />

<h2 id="1️⃣-캐싱">1️⃣ 캐싱</h2>
<ul>
<li>데이터 캐싱 (특정 데이터의 복사본 저장 → 동일한 데이터에 재접근 속도 높이는 것)</li>
<li>반복적인 비동기 데이터 호출 방지 → 불필요한 api 콜 줄임 → 서버에 대한 부하 줄일 수 있음.</li>
<li>데이터를 캐싱했는데 해당 데이터의 상태가 변경되기 전의 내용을 가지고 있는 상황이 발생하지 않도록, 필요한 상황에 적절히 <code>데이터를 갱신</code>해야 함.<ol>
<li>화면을 보고 있을 때</li>
<li>페이지 전환이 일어났을 때</li>
<li>페이지 전환없이 이벤트가 발생해 데이터 요청할 때</li>
</ol>
</li>
<li>react query에서는 <code>어떤 시점</code>에 데이터를 <code>refetching</code> 하는지 알 수 있는 <code>옵션</code>을 제공함.<ul>
<li><code>refetchOnWindowFocus</code>: 브라우저에 포커스가 들어온 경우</li>
<li><code>refetchOnMount</code>: 새로운 컴포넌트 마운트가 발생한 경우</li>
<li><code>refetchOnReconnect</code>: 네트워크 재연결이 발생한 경우</li>
</ul>
</li>
</ul>
<br />

<h3 id="➡️-staletime--cachetime">➡️ staleTime &amp; cacheTime</h3>
<ul>
<li><code>staleTime</code><ul>
<li>데이터가 fresh → stale 상태로 변경되는 데 걸리는 시간</li>
<li>fresh 상태 → refetch 트리거 (위의 3가지 상태) 발생해도 refetch 일어나지 X!!</li>
<li>기본 값: 0</li>
<li>따로 설정해주지 않는다면, refetch 트리거 발생 → 무조건 refetch</li>
</ul>
</li>
<li><code>cacheTime</code><ul>
<li>데이터가 inactive한 상태일 때 캐싱된 상태로 남아있는 시간</li>
<li>특정 컴포넌트 unmount → 사용된 데이터는 inactive 상태로 전환 → cacheTime만큼 데이터 유지</li>
<li>cacheTime 이후 데이터 → 가비지 콜렉터로 수집 → 메모리에서 해제</li>
<li>cacheTime이 모두 지나기 전, 컴포넌트가 다시 mount → 새로운 데이터를 fetch 해오는 동안 캐싱된 데이터 보여줌</li>
<li>즉, <code>fetch 하는 동안만</code>! 캐싱된 데이터를 <code>임시로</code> 보여주는 것!</li>
</ul>
</li>
</ul>
<p>⇒ 사용자가 <code>특정 이벤트</code>가 발생했을 때 <code>refetching</code> 하도록 설정할 수 있음!</p>
<br />

<h2 id="2️⃣-client-data--sever-data">2️⃣ Client Data &amp; Sever Data</h2>
<ul>
<li><code>Client Data</code><ul>
<li>상태 관리 라이브러리가 관리</li>
<li>세션 간 지속되지 않는 데이터</li>
<li>클라이언트가 소유하는 데이터</li>
<li>ex) 컴포넌트의 state, 동기적으로 저장되는 redux store의 데이터</li>
</ul>
</li>
<li><code>Sever Data</code><ul>
<li>react query가 관리</li>
<li>세션 간 지속되는 데이터</li>
<li>비동기적이며, 여러 클라이언트에 의해 수정, 공유되는 데이터</li>
<li>ex) 비동기 요청으로 받아올 수 있는 백엔드 DB에 저장되어 있는 데이터</li>
</ul>
</li>
<li>onError, onSuccess 등의 함수를 통해 데이터 fetch 성공, 실패에 대한 분기를 간단히 구현 가능</li>
</ul>
<pre><code class="language-jsx">const { data, isLoading } = useQueries(
    [&#39;unique-key&#39;],
    () =&gt; {
        return api({
            url: URL,
            method: &#39;GET&#39;,
        });
    },
    {
        onSuccess: (data) =&gt; {
            // data로 이것저것 하는 로직
        }
    },
    {
        onError: (error) =&gt; {
            // error로 이것저것 하는 로직
        }
    }
)</code></pre>
<br />

<h2 id="3️⃣-대표적인-기능들">3️⃣ 대표적인 기능들</h2>
<blockquote>
<p>💡 기본적으로 <code>GET - useQuery</code> / <code>PUT, UPDATE, DELETE - useMutation</code> 사용</p>
</blockquote>
<br />

<h3 id="❇️-usequery">❇️ useQuery</h3>
<ul>
<li><strong>첫 번째 파라미터</strong>: unique key를 포함한 배열 (동일한 쿼리를 불러올 때 유용하게 사용 가능)<ul>
<li>배열의 첫 요소: unique key</li>
<li>배열의 두 번째 요소 ~ : query 함수 내부의 파라미터</li>
</ul>
</li>
<li><strong>두 번째 파라미터</strong>: 실제 호출하고자 하는 비동기 함수 (함수는 Promise를 반환하는 형태)</li>
<li><strong>최종 반환 값</strong>: API 성공/ 실패 여부, 반환 값을 포함한 객체</li>
</ul>
<pre><code class="language-jsx">// useQuery 활용 코드
const { isLoading, error, data } = useQuery({
    queryKey: [&#39;repoData&#39;],
    queryFn: () =&gt;
      fetch(&#39;https://api.github.com/repos/tannerlinsley/react-query&#39;).then(
        (res) =&gt; res.json(),
      ),
  })</code></pre>
<pre><code class="language-jsx">// 전체 예시 코드
import {
  QueryClient,
  QueryClientProvider,
  useQuery,
} from &#39;@tanstack/react-query&#39;

const queryClient = new QueryClient()

export default function App() {
  return (
    &lt;QueryClientProvider client={queryClient}&gt;
      &lt;Example /&gt;
    &lt;/QueryClientProvider&gt;
  )
}

function Example() {
    // data를 통해, fetch 성공 시 데이터 반환 가능
  const { isLoading, error, data } = useQuery({
    queryKey: [&#39;repoData&#39;],
    queryFn: () =&gt;
      fetch(&#39;https://api.github.com/repos/tannerlinsley/react-query&#39;).then(
        (res) =&gt; res.json(),
      ),
  })

    // isLoading을 통해 로딩 여부를 알 수 있음
  if (isLoading) return &#39;Loading...&#39;

    // error를 통해 에러 발생 여부를 알 수 있음
  if (error) return &#39;An error has occurred: &#39; + error.message

  return (
    &lt;div&gt;
      &lt;h1&gt;{data.name}&lt;/h1&gt;
      &lt;p&gt;{data.description}&lt;/p&gt;
      &lt;strong&gt;👀 {data.subscribers_count}&lt;/strong&gt;{&#39; &#39;}
      &lt;strong&gt;✨ {data.stargazers_count}&lt;/strong&gt;{&#39; &#39;}
      &lt;strong&gt;🍴 {data.forks_count}&lt;/strong&gt;
    &lt;/div&gt;
  )
}</code></pre>
<br />

<h3 id="❇️-usemutation">❇️ useMutation</h3>
<ul>
<li>PUT, UPDATE, DELETE와 같이 <code>값을 변경할 때</code> 사용하는 api</li>
<li><strong>첫 번째 파라미터</strong>: 비동기 함수</li>
<li><strong>두 번째 파라미터</strong>: 상황 별 분기 설정</li>
<li><strong>최종 반환 값</strong>: API 성공/ 실패 여부, 반환 값을 포함한 객체 (useQuery와 동일)</li>
</ul>
<pre><code class="language-jsx">// useMutation 활용 코드
const mutation = useMutation({
    mutationFn: (newTodo) =&gt; {
      return axios.post(&#39;/todos&#39;, newTodo)
    },
  })</code></pre>
<pre><code class="language-jsx">// 전체 예시 코드
function App() {
  const mutation = useMutation({
    mutationFn: (newTodo) =&gt; {
            // 처음에 post 비동기 함수를 넣어줌
      return axios.post(&#39;/todos&#39;, newTodo)
    },
  })

  return (
    &lt;div&gt;
      {mutation.isLoading ? (
        &#39;Adding todo...&#39;
      ) : (
        &lt;&gt;
          {mutation.isError ? (
            &lt;div&gt;An error occurred: {mutation.error.message}&lt;/div&gt;
          ) : null}

          {mutation.isSuccess ? &lt;div&gt;Todo added!&lt;/div&gt; : null}

          &lt;button
            onClick={() =&gt; {
                            // 첫 번째 인자로 api 호출 시 전달해야 할 데이터 넣어주기!
              mutation.mutate({ id: new Date(), title: &#39;Do Laundry&#39; })
            }}
          &gt;
            Create Todo
          &lt;/button&gt;
        &lt;/&gt;
      )}
    &lt;/div&gt;
  )
}</code></pre>
<br />

<h3 id="✅-추가-내용">✅ 추가 내용</h3>
<p><strong>📚 useQuery 동기적 실행</strong></p>
<ul>
<li><p><code>enabled</code> 옵션 사용</p>
<ul>
<li><p>enabled에 값 대입 → 해당 값이 true일 때 → useQuery 비동기 실행 가능!</p>
<pre><code class="language-jsx">const { data: todoList, error, isFetching } = useQuery(&quot;todos&quot;, fetchTodoList);
const { data: nextTodo, error, isFetching } = useQuery(
&quot;nextTodos&quot;,
fetchNextTodoList,
{
  enabled: !!todoList // true가 되면 fetchNextTodoList를 실행한다
}
);</code></pre>
</li>
</ul>
</li>
</ul>
<p><strong>📚 useQueries</strong></p>
<ul>
<li>여러 개의 useQuery를 한 번에 실행 가능</li>
<li><code>Promise.all()</code> 처럼 묶어서 실행할 수 있도록 도와주는 역할!</li>
<li>여러 query에 대한 반환 값이 배열로 묶여 반환됨</li>
</ul>
<pre><code class="language-jsx">const results = useQueries({
  queries: [
    { queryKey: [&#39;post&#39;, 1], queryFn: fetchPost, staleTime: Infinity},
    { queryKey: [&#39;post&#39;, 2], queryFn: fetchPost, staleTime: Infinity}
  ]
})</code></pre>
<p><strong>📚 retry</strong></p>
<ul>
<li>요청이 실패한 경우, 지정한 최대 요청 횟수까지 재요청 보냄 (기본: 3회)</li>
<li><code>retryDelay</code>: 다음 재요청까지의 딜레이 시간</li>
</ul>
<pre><code class="language-jsx">const userQuery = useQuery([&#39;users&#39;,1], fetchUsers,
                           { retry : 10, retryDelay : 400});</code></pre>
<br />

<h2 id="4️⃣-장점">4️⃣ 장점</h2>
<ol>
<li><strong>Devtools</strong><ul>
<li><code>react-query/devtools</code> 를 통해 Devtool 지원 → 확실한 데이터 흐름 파악 가능</li>
<li>개발 모드에서만 사용</li>
<li><code>swr</code> : devtools 사용 가능하지만, 서드 파티 라이브러리를 이용해야 함!</li>
</ul>
</li>
</ol>
<br />

<ol>
<li><strong>무한 스크롤 구현</strong><ul>
<li><code>getPreviousPageParam</code>, <code>fetchPreviousPage</code>, <code>hasPreviousPage</code> 등의 기능을 활용하여 쉽게 무한 스크롤 구현 가능</li>
<li>swr을 활용해도 무한 스크롤을 구현할 수 있긴 하지만, 유저가 부가적인 코드를 작성해야 한다는 단점있음</li>
</ul>
</li>
</ol>
<br />

<ol start="2">
<li><p><strong>Selectors</strong></p>
<ul>
<li><p><code>select</code> 를 통해 raw data로 부터 원하는 <code>데이터 추출, 반환</code> 가능</p>
<pre><code class="language-jsx">import { useQuery } from &#39;react-query&#39;

function User() {
const { data } = useQuery(&#39;user&#39;, fetchUser, {

      // select 활용
  select: user =&gt; user.username,
})
return &lt;div&gt;Username: {data}&lt;/div&gt;
}</code></pre>
<br />
</li>
</ul>
</li>
<li><p><strong>Data Optimization</strong></p>
<ul>
<li><p>swr과 달리, <code>쿼리가 업데이트</code> 될 때만 <code>refetch</code> 진행</p>
</li>
<li><p><code>여러 컴포넌트</code>에서 <strong>동일한 쿼리</strong> 사용 시, <code>한 번에 묶어</code> 업데이트 진행</p>
</li>
<li><p>이를 통해 렌더링 퍼포먼스 개선 가능!</p>
<br />
</li>
</ul>
</li>
<li><p><strong>Garbage Collection</strong></p>
<ul>
<li>지정 시간(기본 5분)동안 쿼리가 사용되지 않을 경우, 메모리 해제 → <code>Auto Garbage Collection</code> → 메모리 관리</li>
</ul>
</li>
</ol>
<br />

<hr/>

<h2 id="📚-reference">📚 Reference</h2>
<p><a href="https://tanstack.com/query/latest/docs/react/overview?from=reactQueryV3&amp;original=https%3A%2F%2Ftanstack.com%2Fquery%2Fv3%2Fdocs%2Foverview">https://tanstack.com/query/latest/docs/react/overview?from=reactQueryV3&amp;original=https%3A%2F%2Ftanstack.com%2Fquery%2Fv3%2Fdocs%2Foverview</a></p>
<p><a href="https://velog.io/@seohee0112/React-Query">https://velog.io/@seohee0112/React-Query</a></p>
<p><a href="https://velog.io/@kandy1002/React-Query-%ED%91%B9-%EC%B0%8D%EC%96%B4%EB%A8%B9%EA%B8%B0">https://velog.io/@kandy1002/React-Query-푹-찍어먹기</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[📍Redux, Recoil 개념잡기]]></title>
            <link>https://velog.io/@aroo_ming/Redux-Recoil-%EA%B0%9C%EB%85%90%EC%9E%A1%EA%B8%B0</link>
            <guid>https://velog.io/@aroo_ming/Redux-Recoil-%EA%B0%9C%EB%85%90%EC%9E%A1%EA%B8%B0</guid>
            <pubDate>Tue, 27 Jun 2023 07:57:35 GMT</pubDate>
            <description><![CDATA[<h1 id="🪢-redux">🪢 Redux</h1>
<p><img src="https://velog.velcdn.com/images/aroo_ming/post/aa9449ff-df18-49b4-85a7-3cddd0e830c9/image.png" alt=""></p>
<ul>
<li><code>JS 기반</code> 상태 관리 라이브러리</li>
<li>사용량이 압도적으로 높은 만큼, <code>안전성</code> 보장</li>
<li><code>보일러 플레이트 코드</code> 양이 너무 많음.<ul>
<li>작은 기능 하나를 추가하더라도 여러 줄의 코드 필요</li>
</ul>
</li>
<li><code>러닝 커브</code> 높음.</li>
<li>하나의 파일(store)에서 상태를 관리하기 때문에, <strong>버그 발생 시 추적이 쉽고 관리가 용이함</strong></li>
<li>Redux DevTools를 활용하면 <code>디버깅</code>이 쉽다.<ul>
<li>앱의 상태가 언제, 어디서, 왜, 어떻게 바뀌었는지 쉽게 추적 가능</li>
</ul>
</li>
<li><code>대규모 프로젝트</code>에서 많이 쓰임</li>
</ul>
<br/>

<h2 id="💡store-action-reducer">💡Store, Action, Reducer</h2>
<h3 id="🏠-store">🏠 Store</h3>
<ul>
<li><code>상태가 관리</code>되는 하나의 공간<ul>
<li><strong>컴포넌트와 별개</strong>로 Store라는 공간에 상태를 담아두고, 관리한다.</li>
<li>컴포넌트에서는 상태가 필요할 때, 스토어에 접근한다.</li>
</ul>
</li>
</ul>
<br/>

<h3 id="📜-action">📜 Action</h3>
<ul>
<li><p>앱에서 Store에 운반할 <code>데이터 (주문서)</code></p>
</li>
<li><p>JS 객체 형식</p>
<pre><code class="language-tsx">  {
    type: &#39;ACTION_CHANGE_USER&#39;, // 필수
    payload: { // 옵션
      name: &#39;하나몬&#39;,
      age: 100
    }
  }</code></pre>
</li>
</ul>
<br/>

<h3 id="💌-reducer">💌 Reducer</h3>
<ul>
<li>Action을 <code>Store에 전달하기 위해</code> 거쳐야 하는 곳</li>
<li><strong>Action → Reducer에 전달 → Store에 상태 업데이트</strong></li>
<li>Action → Reducer에 전달하기 위해서는 <code>dispatch()</code> 메소드 사용해야 함.</li>
</ul>
<br/>

<h2 id="✅-결론">✅ 결론</h2>
<ol>
<li>Action 객체 → dispatch() 메소드에 전달됨</li>
<li>dispatch(Action) 통해 Reducer 호출</li>
<li>Reducer → 새로운 Store 생성</li>
</ol>
<p><img src="https://s3-us-west-2.amazonaws.com/secure.notion-static.com/0998fa57-e63b-455a-b2fb-3f890a80d77d/Untitled.png" alt="Untitled"></p>
<hr>
<h1 id="🪢-recoil">🪢 Recoil</h1>
<p><img src="https://velog.velcdn.com/images/aroo_ming/post/d2bc2377-680b-4077-b5b1-4946191d4f2c/image.png" alt=""></p>
<ul>
<li><code>React 상태 관리 라이브러리</code></li>
<li><code>직관적</code>이기 때문에, 비교적 사용자 친화적임.</li>
<li><strong>get/ set</strong>  인터페이스 사용 → <code>boilerplate-free API</code> 제공</li>
<li><code>러닝 커브</code> 낮음.</li>
<li><code>data-flow graph</code><strong><strong>:</strong></strong> 상태 데이터가 <strong>atom → selector → 컴포넌트</strong> 순서로 흐름</li>
<li><strong>컴포넌트 수정 없이</strong> 동기/ 비동기 식 전환 가능</li>
</ul>
<br/>

<h2 id="💡-atoms-selectors">💡 Atoms, Selectors</h2>
<h3 id="🫧-atoms">🫧 Atoms</h3>
<ul>
<li>상태의 단위</li>
<li>업데이트/ 참조 가능</li>
<li><strong>atom 업데이트 → 구독된 컴포넌트는 새로운 값을 반영 → 리렌더링</strong></li>
<li><code>런타임</code>에서도 생성 가능</li>
<li><code>React의 state 대신</code> 사용 가능</li>
<li><strong>동일한 atom</strong>이 여러 컴포넌트에서 사용 → <code>모든 컴포넌트</code>는 <code>상태를 공유</code>함.</li>
<li>Atoms는 atom 함수를 사용해 생성</li>
</ul>
<pre><code class="language-tsx">const fontSizeState = atom({
  key: &#39;fontSizeState&#39;,
  default: 14,
});</code></pre>
<br/>

<ul>
<li><p><code>고유한 키</code> 값 필요</p>
</li>
<li><p><code>useRecoilState</code> 훅을 통해 atom 읽고 쓰기 가능</p>
<ul>
<li><p>useState와 비슷하지만, <strong>컴포넌트 간의 상태가 공유</strong>될 수 있다는 차이.</p>
<pre><code class="language-tsx">function FontButton() {
const [fontSize, setFontSize] = useRecoilState(fontSizeState);
return (
  &lt;button onClick={() =&gt; setFontSize((size) =&gt; size + 1)} style={{fontSize}}&gt;
    Click to Enlarge
  &lt;/button&gt;
);
}</code></pre>
</li>
</ul>
</li>
</ul>
<br/>

<h3 id="👀-selectors">👀 Selectors</h3>
<ul>
<li>atoms나 다른 selectors를 입력으로 받아들이는 순수 함수</li>
<li><code>atom 상태 값</code>을 동기/ 비동기 방식을 통해 <code>변환</code></li>
<li><strong>상위 atoms/ selectors 업데이트 → 하위의 selector도 다시 실행됨</strong></li>
<li>참조 가능</li>
<li><strong>selectors 변경 → 컴포넌트 리렌더링</strong></li>
<li>최소한의 상태 집합만 atoms에 저장 → 다른 모든 파생 데이터 → selectors에 명시한 함수를 통해 효율적 계산 가능</li>
<li>어떤 컴포넌트가 <code>자신을 필요</code>로 하는지, 어떤 <code>상태에 의존</code>하는지 <code>추적</code></li>
<li>컴포넌트의 관점)) selectors, atoms는 동일한 인터페이스 가짐 → 서로 대체 가능</li>
<li>Selectors는 selector 함수를 사용해 생성</li>
</ul>
<pre><code class="language-tsx">const fontSizeLabelState = selector({
  key: &#39;fontSizeLabelState&#39;,
  get: ({get}) =&gt; {
        // fontSizestate라는 하나의 atom에 의존성을 가짐
    const fontSize = get(fontSizeState);
    const unit = &#39;px&#39;;

    return `${fontSize}${unit}`;
  },
});</code></pre>
<pre><code class="language-tsx">function FontButton() {
  const [fontSize, setFontSize] = useRecoilState(fontSizeState);
    // Selectors는 useRecoilValue() 통해 읽을 수 있음
    // fontSizeLableState는 writable 하지 않기 때문에 useRecoilState()를 이용하지 않음!
  const fontSizeLabel = useRecoilValue(fontSizeLabelState);

  return (
    &lt;&gt;
      &lt;div&gt;Current font size: ${fontSizeLabel}&lt;/div&gt;

      &lt;button onClick={setFontSize(fontSize + 1)} style={{fontSize}}&gt;
        Click to Enlarge
      &lt;/button&gt;
    &lt;/&gt;
  );
}</code></pre>
<br />

<hr/>

<h2 id="📚-reference">📚 Reference</h2>
<p><a href="https://ko.redux.js.org/">Redux - 자바스크립트 앱을 위한 예측 가능한 상태 컨테이너. | Redux</a></p>
<p><a href="https://hanamon.kr/redux%EB%9E%80-%EB%A6%AC%EB%8D%95%EC%8A%A4-%EC%83%81%ED%83%9C-%EA%B4%80%EB%A6%AC-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC/">Redux(리덕스)란? (상태 관리 라이브러리) - 하나몬</a></p>
<p><a href="https://recoiljs.org/ko/docs/introduction/core-concepts">주요 개념 | Recoil</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Next.js, 개념에 대해 자세히 알아보자!]]></title>
            <link>https://velog.io/@aroo_ming/Next.js-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</link>
            <guid>https://velog.io/@aroo_ming/Next.js-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0</guid>
            <pubDate>Sun, 14 May 2023 17:33:51 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/aroo_ming/post/6a2a4f33-da6c-48aa-bd98-81ce74efce01/image.png" alt=""></p>
<h2 id="📌nextjs란">📌Next.js란?</h2>
<blockquote>
<p>웹 어플리케이션을 구축하기 위한 React 프레임워크</p>
</blockquote>
<br />

<p>👇 그래서.. React랑 뭐가 다른건데!! 
👇 React와의 차이를 알아보기 위해 CSR과 SSR에 대해 짚고 넘어가보자! </p>
<br />

<h2 id="📌csr-vs-ssr">📌CSR vs SSR</h2>
<h3 id="✅-csr-client-side-rendering">✅ CSR (Client Side Rendering)</h3>
<p><img src="https://velog.velcdn.com/images/aroo_ming/post/f1afd8a3-b5c2-44cf-8c67-ac13ca1a6a63/image.png" alt=""></p>
<ul>
<li>클라이언트 쪽에서 렌더링을 진행하는 방식이다.</li>
<li>서버는 요청을 받으면 클라이언트에 HTML과 JS를 보내주고 클라이언트에서는 이것을 받아서 렌더링을 시작한다. </li>
<li><code>서버에서의 처리없이</code> 클라이언트로 보내주기 때문에, 모든 JavaScript 파일을 다운로드하는 동안 제대로 된 화면을 볼 수 없다.</li>
<li>브라우저가 JS를 다운로드하고 동적 DOM을 생성하기 위해 초기 로드 시간이 오래 걸린다.</li>
<li>이후 렌더링은 서버에서 해당 데이터만 response 받아 사용하면 되기 대문에 첫 렌더링 이후 구동 속도는 빠른 편이다.</li>
<li>검색 엔진 최적화(SEO)가 필요없거나, 데이터의 사전 렌더링이 필요없거나, 페이지의 내용이 실시간으로 자주 업데이트되어야 할 경우에는 유용하게 쓰이는 방식이다.</li>
</ul>
<br />

<h3 id="✅-ssr-server-side-rendering">✅ SSR (Server Side Rendering)</h3>
<p><img src="https://velog.velcdn.com/images/aroo_ming/post/dbfae895-0d0b-4a2c-acc0-a06df2281e97/image.png" alt=""></p>
<ul>
<li>서버 쪽에서 렌더링 준비를 마친 상태로 클라이언트에 전달하는 방식이다.</li>
<li>이미 <code>렌더 가능한</code> 상태로 서버에서 클라이언트로 전달되기 때문에, JavaScript 파일이 다운로드 되는 동안 사용자는 로딩 화면만 보고 있지 않아도 된다.</li>
<li>검색 엔진은 자동화된 로봇인 &#39;크롤러&#39;로 웹 사이트를 읽게 되는데,서버에서 렌더링된 HTML 파일을 보내주는 SSR은 검색 엔진 최적화(SEO)에 유리하다.</li>
<li>JavaScript 파일 다운로드를 기다리지 않아도 되기 때문에 초기 구동 속도가 빠르다.</li>
<li>그러나 서버 호출 시 페이지가 새로고침 되며 깜빡임이 발생하고, 요청이 생길 때마다 서버에서 렌더링되지 않아도 될 부분까지 준비하기 때문에 서버 부하를 일으키기도 한다.</li>
</ul>
<br />

<hr>
<br />

<h2 id="📌react와-nextjs">📌React와 Next.js</h2>
<blockquote>
<p>React와 Next.js의 가장 큰 차이점 중 하나는 렌더링하는 방식에 있다.
React는 CSR, Next.js는 SSR 방식으로 렌더링한다.</p>
</blockquote>
<br />

<h3 id="✅-react-feat-csr">✅ React (feat. CSR)</h3>
<p><img src="https://velog.velcdn.com/images/aroo_ming/post/662c827a-8ad0-4baf-bfca-01e3f97e0474/image.png" alt=""></p>
<ul>
<li>대표적인 CSR 라이브러리인 React는 웹 사이트를 요청했을 때 빈 HTML을 가져와서 JS를 로딩하는 구조이다.</li>
<li>로딩 시간이 SSR에 비해 오래 걸리고, 만약 JS를 다룰 줄 모르는 검색 엔진이 사이트를 크롤링한다면 텅 빈 HTML만 인식하게 된다.</li>
<li>즉, CSR 방식을 사용하는 React는 로딩 시간도 비교적 오래걸리고 SEO에 취약하다는 단점이 있다.</li>
</ul>
<br />

<hr>
<br />

<h3 id="✅-nextjs-feat-ssr">✅ Next.js (feat. SSR)</h3>
<p>반면, Next.js는 웹에 사이트를 요청했을 때 서버 측 렌더링을 거친 후 HTML과 JS를 따로 reponse한다.</p>
<blockquote>
<p>💡 Next.js의 작동원리는 다음과 같다. <br />
    1. 초기에 사용자가 서버에 접속을 요청하면, SSR 방식으로 렌더링될 HTML을 보낸다.
    2. 브라우저에서 JavaScript를 다운로드하고 React를 실행한다.
    3. 사용자와 페이지가 서로 상호작용하며 다른 페이지로 이동하게 될 경우에는 CSR 방식으로 브라우저에서 처리한다.</p>
</blockquote>
<p> <img src="https://velog.velcdn.com/images/aroo_ming/post/88d4c5b0-84c1-4515-89c5-7c6652ddb103/image.png" alt=""></p>
<ul>
<li>Next.js는 모든 페이지를 <code>pre-render(HTML을 JS 처리하기 전, 사전에 생성)</code> 하기 때문에, 검색엔진 최적화가 가능해진다. </li>
<li>사전 렌더링에 의해 만들어진 HTML은 해당 페이지에서 필요한 최소한의 JavaScript 코드와 연결된다. </li>
<li>어떤 페이지의 HTML이 브라우저에 의해 로드되면 그 HTML에 연결된 JavaScript 코드가 실행되어 해당 페이지 상호작용이 가능해지며, 이런 과정을 <code>Hydration</code>이라고 부른다.</li>
<li>즉, Next.js는 <code>리액트 문법을 쓰는 프론트 문법을 쓸 수 있게 도와주 백엔드 프레임워크</code> 라고 할 수 있다.</li>
</ul>
<br />

<hr>
<br />

<h2 id="📌nextjs-어떤-특징-기능을-가지고-있을까">📌Next.js, 어떤 특징/ 기능을 가지고 있을까?</h2>
<blockquote>
<p>다음은 공식 문서에서 가져온 Next.js 특징 중 일부이다. <br />
     - 직관적인 페이지 기반 라우팅 시스템
    - 사전 렌더링, 정적 생성(SSG) 및 서버 측 렌더링(SSR)은 페이지 별로 지원
    - 더 빠른 페이지 로드를 위한 자동 코드 분할
    - 최적화된 프리패치를 사용한 클라이언트 측 라우팅(CSR)
    - 내장 CSS 및 Sass 지원 및 모든 CSS-in-JS 라이브러리 지원</p>
</blockquote>
<br />

<p>👇 좀 더 자세히 알아보자!</p>
<br />

<h3 id="1-automatic-routing">1. Automatic Routing</h3>
<p>React와 달리, 별도의 세팅없이 페이지를 기반으로 한 라우팅을 지원한다. React에서는 별도의 라우터를 제공하지 않기 때문에, <code>react-router-dom</code>이라는 패키지를 설치하여 사용했다. </p>
<p>Next.js 13은 app 폴더 하위에<code>page</code> 파일을 추가하고 그 안에 레이아웃을 넣는 방식으로, 추가한 파일을 export하게 되면 폴더명이 페이지 route가 되어 해당 파일이 라우터 역할을 하게 된다!</p>
<p>ex1. <code>app/blog/page.js</code> → <code>/blog</code></p>
<p>ex2. <code>app/blog/first-blog/page.js</code> → <code>/blog/first-blog</code></p>
<br />

<h3 id="2-dynamic-routes">2. Dynamic Routes</h3>
<p>항상 미리 정의된 경로를 사용할 수 있는 것은 아니다. 예를 들어 블로그 게시물의 경로는 /blog1, /blog2, /blog3 … 과 같은데, 모든 경로를 미리 정의하거나 페이지 컴포넌트를 생성하는 일은 굉장히 비효율적이다.</p>
<p>이때 사용하는 것이 <code>동적 라우팅</code>이다. <code>[folderName]</code>과 같이 폴더 이름을 대괄호로 묶어주면 해당 페이지 컴포넌트가 동적으로 라우팅 된다.</p>
<p>클라이언트 사이드에서 동적 경로에 접근하고 싶다면 <code>next/link</code>를 사용해주면 되고, <code>router.push</code> 같은 메소드를 사용할 수 도 있다!</p>
<br />

<h4 id="📍--foldername">📍  [folderName]</h4>
<blockquote>
<p>app/category/[slug]/page.js</p>
</blockquote>
<p>Next.js 13 Automatic Routing에 따라 app 폴더 하위에 category 폴더를 만들고, 그 안에 Dynamic Routes를 위한 [slug] 폴더를 만든 것이다.
<code>/category/food</code>, <code>/category/study</code> 등의 주소는 모두 <code>/post/[pid].js</code>로 매칭된다. 
[id]나 [slug]처럼 동적인 부분은 Layout과 Page나 Head 컴포넌트들에게 params prop으로 전달된다.</p>
<p><code>/category/food</code> → <code>{ slug: &#39;food&#39; }</code></p>
<p><code>/category/study</code> → <code>{ slug: &#39;study&#39; }</code></p>
<br />

<h4 id="📍-catch-all-segment--foldername">📍 Catch-all Segment : [...folderName]</h4>
<blockquote>
<p>app/shop/[...slug]/page.js</p>
</blockquote>
<p>[slug]에 <code>...</code>을 추가하여 shop 하위의 모든 경로를 잡아낼 수 있다.</p>
<p><code>/shop/clothes</code> → <code>{ slug: [&#39;clothes&#39;] }</code></p>
<p><code>/shop/clothes/pants</code> → <code>{ slug: [&#39;clothes&#39;, &#39;pants&#39;] }</code></p>
<p><code>/shop/clothes/tops/t-shirts</code> → <code>{ slug: [&#39;clothes&#39;, &#39;tops&#39;, &#39;t-shirts&#39;] }</code></p>
<br />


<h3 id="3-code-splitting">3. Code Splitting</h3>
<p>코드 분할은 다른 페이지에 대한 코드는 처음에 제공하지 않고, 원하는 페이지에 접근하는데 필요한 chunk(작은 코드 조각)만을 로드한다.</p>
<p>여러 개의 페이지가 있는 웹 사이트일지라도 필요한 페이지만을 빠르게 로드할 수 있으며, 이는 JavaScript 페이로드를 최소화하고 애플리케이션 로드 시간을 개선할 수 있다.</p>
<p>초기 페이지 로드에 필요하지 않은 구성 요소가 있는 경우 다음(👇)과 같이 동적으로 가져올 수 있다. 이는 필요할 때만 Puppy를 로드하도록 지시하고 있다.</p>
<pre><code>
// import Puppy from &quot;../components/Puppy&quot;;
import dynamic from &quot;next/dynamic&quot;;

// ...

const Puppy = dynamic(import(&quot;../components/Puppy&quot;));</code></pre><br />


<h3 id="4-client-side-navigation">4. Client-Side Navigation</h3>
<p><code>&lt;Link /&gt;</code> 컴포넌트를 통한 페이지 이동이 빠르고 매끄럽다. HTML의 a 태그와 달리, 페이지를 리로딩하지 않는다. <code>&lt;Link /&gt;</code>가  뷰포트에 보이면 관련 페이지를 백그라운드에서 미리 가져오기 때문에, 사용자가 링크를 클릭했을 때 매우 빠르게 해당 페이지로 이동할 수 있게 해준다.</p>
<br />

<h3 id="5-typescript">5. TypeScript</h3>
<p>TypeScript를 활용하기 위해서 웹팩이나 바벨을 만지지 않아도 된다. TypeScript를 설치하고 yarn run dev를 해주면 자동으로 tsconfig, next-end.d.ts가 생성되어 TypeScript를 사용할 수 있다.</p>
<pre><code>yarn add typescript @types/node @types/react

yarn run dev</code></pre><br />

<h3 id="6-ssr--csr">6. SSR + CSR</h3>
<p>초기 페이지 로딩에서는 SSR 방식을 이용하여 처리하고, 이후의 요청에 대해서는 CSR 방식으로 처리하기 때문에 SSR 방식의 장점(SEO에 유리)과 CSR의 이점을 모두 활용할 수 있다.</p>
<br />

<h3 id="7-pre-rendering">7. Pre-rendering</h3>
<p>Next.js는 모든 페이지를 사전 렌더링한다.</p>
<br />

<hr>
<br />

<h2 id="📌nextjs-13">📌Next.js 13</h2>
<p><img src="https://velog.velcdn.com/images/aroo_ming/post/d5079023-5323-4a99-877b-5e0f60b58ad2/image.png" alt=""></p>
<h3 id="👉-app-directory">👉 app/ Directory</h3>
<ul>
<li>기존 버전에서는 Automatic Routing 사용 시, pages 안에 파일 생성하여 별도의 라우터 없이도 애플리케이션 내부에서 즉시 경로 생성이 가능했다. 하지만 Next.js 13 버전부터는 <code>app/</code> 디렉토리가 등장하여 <code>라우팅 및 레이아웃 기능을 개선</code>했다.</li>
<li>현재는 pages 디렉토리와 app 디렉토리가 공존할 수 있는 베타 버전으로 제공되고 있다.</li>
</ul>
<br />

<h3 id="👉-streaming">👉 Streaming</h3>
<ul>
<li>app/ 디렉토리에서 loading.js 예약 파일을 생성하면 렌더링이 되기 전에 로딩 중인 화면을 표시할 수 있는 기능이 추가됐다.</li>
<li>아래 코드처럼 엄청 간단하게 만들어볼 수 있다!<pre><code>export default function Loading() {
  return(
      &lt;div&gt;Loading...&lt;/div&gt;
  )
}</code></pre></li>
</ul>
<br />

<h3 id="👉-data-fetching">👉 Data Fetching</h3>
<ul>
<li><p>기존 버전에서는 데이터를 가져올 때 getServerSideProps()나 getStaticProps()를 사용했지만, Next.js 13 버전부터는 이 문법들을 사용하지 않는다.</p>
</li>
<li><p><a href="https://nextjs.org/docs/app/building-your-application/data-fetching/fetching#dynamic-data-fetching">공식문서</a>에 따르면, 현재 Client Component에서의<code>use()</code> 사용은 권장되지 않으며, 이는 무수한 리렌더를 유발할 것이라고 언급하고 있다. Client Component에서 fetch 데이터가 필요하다면, SWR과 React Query와 같은 라이브러리 사용을 추천한다고 한다.</p>
</li>
</ul>
<br />


<ul>
<li><p>정적 데이터 가져오기</p>
<pre><code>fetch(&#39;https://...&#39;); // cache: &#39;force-cache&#39; is the default</code></pre></li>
<li><p><code>캐시된 데이터의 유효성</code>을 주기적으로 다시 검사하려면 <code>next.revalidate</code> 옵션을 사용해서 리소스의 캐시 유효시간(초)을 설정할 수 있다.</p>
</li>
</ul>
<pre><code>fetch(&#39;https://...&#39;, { next: { revalidate: 10 } });</code></pre><ul>
<li>동적 데이터 가져오기<pre><code>fetch(&#39;https://...&#39;, { cache: &#39;no-store&#39; });</code></pre></li>
</ul>
<br />

<ul>
<li>fetch 구문의 URL 뒤에 <code>{ cache: &#39;&#39; }</code> 를 넣어줄 수 있다. cache에 들어가는 내용에 따라 기존의 getServerSideProps(), getStaticProps()의 기능을 구현할 수 있다.</li>
</ul>
<pre><code>
// Similar to `getStaticProps`.
fetch(URL, { cache: &#39;force-cache&#39; });

// Similar to `getServerSideProps`.
fetch(URL, { cache: &#39;no-store&#39; });

// Similar to `getStaticProps` with the `revalidate` option.
fetch(URL, { next: { revalidate: 10 } });</code></pre><br />

<h3 id="👉-etc">👉 etc..</h3>
<p>이 외에도 Next.js 13에서 변경된 부분이 다수 있다. 자세한 내용은 <a href="https://nextjs.org/blog/next-13">nextjs.org</a>를 참고해보자!</p>
<br />

<hr>
<p>📘참고자료
<a href="https://nextjs.org/learn/foundations/how-nextjs-works">How Next.js Works</a>
<a href="https://nextjs.org/docs">What is Next.js?</a>
<a href="https://www.reason-to-code.com/blog/why-we-couldn&#39;t-feel-the-difference-of-nextjs/">Next.js를 사용하면서 리액트랑 별반 다르지 않다고 느꼈던 이유</a>
<a href="https://velog.io/@syoung125/Next.js-%EA%B8%B0%EB%B3%B8-%EA%B0%9C%EB%85%90-1-Next.js-%EB%9E%80-Next.js%EB%A5%BC-%EC%99%9C-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C-Next.js%EC%9D%98-%EC%9E%A5%EC%A0%90%EC%9D%80">[Next.js] 기본 개념 : Next.js 란? Next.js를 왜 사용할까? Next.js의 장점은?</a>
<a href="https://nextjs.org/blog/next-13">Next.js 13</a>
<a href="https://www.youtube.com/watch?v=5BRFGMs1v_o">Next.js 13 신버전 발표회 요약</a>
<a href="https://hjk329.github.io/next.js/Next.js%EC%9D%98-Routing/">Next.js의 Routing</a>
<a href="https://pusha.tistory.com/entry/Nextjs-Dynamic-Import-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C">Next.js Dynamic Import(동적 가져오기): 성능 및 사용자 경험 개선</a>
<a href="https://nextjs.org/docs/pages/building-your-application/routing/dynamic-routes">dynamic-routes</a></p>
]]></description>
        </item>
    </channel>
</rss>