<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>yo_onms.log</title>
        <link>https://velog.io/</link>
        <description>프론트엔드 주니어 개발자</description>
        <lastBuildDate>Tue, 31 Oct 2023 11:03:56 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>yo_onms.log</title>
            <url>https://images.velog.io/images/minseok_yun/profile/fd8dd08b-45e4-41af-b740-85797f5797e2/1564741916682.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. yo_onms.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/minseok_yun" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[npm에서 pnpm으로 마이그레이션하기]]></title>
            <link>https://velog.io/@minseok_yun/npm%EC%97%90%EC%84%9C-pnpm%EC%9C%BC%EB%A1%9C-%EB%A7%88%EC%9D%B4%EA%B7%B8%EB%A0%88%EC%9D%B4%EC%85%98%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@minseok_yun/npm%EC%97%90%EC%84%9C-pnpm%EC%9C%BC%EB%A1%9C-%EB%A7%88%EC%9D%B4%EA%B7%B8%EB%A0%88%EC%9D%B4%EC%85%98%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 31 Oct 2023 11:03:56 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>회사에서 모노레포를 사용하면서 pnpm이라는 패키지 관리자를 사용하고 있어 공부 겸 좋은점들이 많아 개인프로젝트에도 적용해본 경험을 씁니다.</p>
</blockquote>
<h3 id="1-pnpm이란-무엇인가">1. pnpm이란 무엇인가?</h3>
<p>pnpm을 쉽게 설명 하자면 npm의 단점을 보완한 패키지 매니저다.
npm을 사용하여 개발하다 보면 여러 프로젝트에서 중복되는 의존성이 node_modules마다 중복으로 설치됩니다. 해결방안으로 프로젝트에 설치하는것이 아니라 별도의 모듈 디렉토리에 1개에만 설치하고 사용할 때는 모듈 디렉에 링크하여 사용합니다.
<a href="https://pnpm.io/motivation">- pnpm 공식사이트</a></p>
<h3 id="2-pnpm-설치">2. pnpm 설치</h3>
<pre><code class="language-javascript"> npm install -g pnpm

 *의존성 문제가 걸릴경우
 sudo npm install -g pnpm</code></pre>
<p>이렇게 전역에 설치를 하고 <code>pnpm -v</code> 를 하였을때 버전이 뜨게 된다면 제대로 설치가 된겁니다.</p>
<h3 id="3-기존-node_modules-삭제하기">3. 기존 node_modules 삭제하기</h3>
<pre><code class="language-javascript">npx npkill</code></pre>
<p>ok to proceed? 라는 문구가 나오면 <strong>y</strong>를 눌러서 제거는 하시면 됩니다.
스페이스바를 누르면 삭제가 진행이되고 complete가 되면 <strong>q</strong>를 누르셔서 현재 상태에서 나오시면 됩니다.</p>
<blockquote>
<p>이제껏 쌓여 있던 노드 모듈을 제거하는 과정입니다.
그리고 node_moducles를 삭제합니다.</p>
</blockquote>
<h3 id="4-packagejson-수정">4. package.json 수정</h3>
<pre><code class="language-javascript">&quot;scripts&quot;: {
  &quot;preinstall&quot;: &quot;npx only-allow pnpm&quot;, 
  ...
}</code></pre>
<p>프로젝트에서 pnpm을 사용하는 경우, 다른 패키지 매니저를 사용하는 것을 막기 위해선 다음 preinstall 스크립트를 package.json에 추가합니다.</p>
<h3 id="5-모노레포일-경우pnpm-workspaceyaml-만들기">5. (모노레포일 경우)pnpm-workspace.yaml 만들기</h3>
<pre><code class="language-javascript">packages:
  # include packages in subfolders (e.g. apps/ and packages/)
  - &quot;apps/**&quot;
  - &#39;packages/**&#39;
  # if required, exclude some directories
  - &#39;!**/test/**&#39;</code></pre>
<p>모노레포라면 workspace 파일을 작성한다.
추가적인 설명은 <a href="https://pnpm.io/workspaces?ref=blog.joe-brothers.com">pnpm workspace </a>문서를 참고하시면 됩니다.</p>
<h3 id="6-pnpm-import-lock파일-import">6. pnpm import (lock파일 import)</h3>
<pre><code class="language-javascript">pnpm import</code></pre>
<p>위 명령어로 yarn.lock 또는 package-lock.json으로 pnpm-lock.yaml 파일을 생성한다.</p>
<h3 id="7-프로젝트에-node_modules-삭제하기">7. 프로젝트에 node_modules 삭제하기</h3>
<pre><code class="language-javascript">rm ./package-lock.json</code></pre>
<p>현재 프로젝트에 있던 node_moducles를 삭제합니다.
혹시 yarn을 사용하셨다면 yarn-lock.json등 다른 lock파일이 있으면 삭제해줍니다.
위의 npx npkill을 하여 node_moduels가 없다면 생략하셔도 됩니다.</p>
<h3 id="8-의존성-설치">8. 의존성 설치</h3>
<pre><code class="language-javascript">pnpm install</code></pre>
<p>마지막으로 의존성을 설치해주면 됩니다. 
이렇게 하시면 node_modules가 생성됩니다.</p>
<br />
<br />

<p>이렇게 npm -&gt; pnpm 마이그레이션 작업이 끝났습니다!</p>
<p>추가적으로 pnpm CLI 명령어는 <a href="https://pnpm.io/pnpm-cli">pnpm 홈페이지</a>에 잘 나와 있습니다.
많이 쓰는 명령어는 아래에 기재합니다.</p>
<blockquote>
</blockquote>
<p>pnpm add <pkg>: 패키지를 설치한다.
pnpm add -D <pkg>: devDependencies에 추가한다.
pnpm install: 프로젝트의 모든 디펜던시를 설치한다.
pnpm remove <pkg>: 패키지를 제거한다.
pnpm prune: 사용되지 않는 패키지를 의존성에서 제거한다.</p>
<h3 id="참고">참고</h3>
<p>  <a href="https://pnpm.io/installation">https://pnpm.io/installation</a>
  <a href="https://dev.to/andreychernykh/yarn-npm-to-pnpm-migration-guide-2n04">https://dev.to/andreychernykh/yarn-npm-to-pnpm-migration-guide-2n04</a>
  <a href="https://blog.joe-brothers.com/pnpm-migration-guide/">https://blog.joe-brothers.com/pnpm-migration-guide/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[
NextJs next/image Sharp vs  Squoosh]]></title>
            <link>https://velog.io/@minseok_yun/Nexts-nextimage-Sharp-vs-Squoosh</link>
            <guid>https://velog.io/@minseok_yun/Nexts-nextimage-Sharp-vs-Squoosh</guid>
            <pubDate>Sun, 17 Sep 2023 05:53:27 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>회사 프로젝트를 진행중에 서버 트래픽이 너무 뛰게되어 이유를 찾게되었고 그중 가장 큰 이유가 이미지를 로딩할때 가장 크게 뛴다는걸 알게되었다.
이미지 최적화를 하다가 next/image에 sharp 라이브러리를 알게된 일을 적으려 합니다.</p>
</blockquote>
<p>우선 next/image를 사용하는 이유를 간략하게 설명하겠습니다.
img 태그와 next/image의 성능차이등 상세한 내용은 이번 주제와 맞지 않기 때문에 넘어가겠습니다!</p>
<h3 id="1-nextimage-태그에서-제공하는-기능">1. Next/Image 태그에서 제공하는 기능</h3>
<ul>
<li>장치의 크기에 맞춘 적절한 이미지 사이즈와 최신 이미지 포맷 지원</li>
<li>Web Vitals의 CLS 발생을 방지</li>
<li>레이지 로드를 기본적으로 사용하고 있기 때문에, 뷰포트에 노출됐을 때 이미지 로드. 선택적으로 블러링 처리한 이미지를 먼저 노출하는 기능</li>
<li>이미지 리사이징. 외부 이미지도 리사이징 가능</li>
</ul>
<p>조금 더 깊이 알고싶으신분은 아래 공식문서에 들어가보세요!
<a href="https://nextjs.org/docs/app/building-your-application/optimizing/images">Next/Image 공식 문서</a></p>
<h3 id="2-sharp-vs-squoosh">2. Sharp vs Squoosh</h3>
<p>Sharp와 Squoosh는 이미지 최적화 라이브러리이며 next/image에서 둘다 사용 가능하다.
Sharp의 경우 따로 설치해서 사용가능하고, Squoosh는 설치하지 않아도 바로 사용 가능하다.
<img src="https://velog.velcdn.com/images/minseok_yun/post/d4b6e404-c034-47cf-ab75-9c3f8332a312/image.png" alt="nextjs sharp 권장 이미지"></p>
<p>위의 사진과 같이 next/image에서는 sharp 사용을 추천하고 있다.</p>
<p>그럼에도 sharp를 바로 사용하지 못하는 이유는 저작권 문제 때문이라고 알고있다. 
(아니라면 피드백 주세요!)</p>
<p>동일한 이미지 기준으로 압축된 용량도 비슷하지만 Sharp를 사용했을때 응답 속도가 6배 정도가 빠르다.</p>
<p>각각 장단점</p>
<ul>
<li><p>Sharp
  장점: Squoosh보다 6배 빠름
  단점: 설치할떄 호환성 이슈가 있을수 있음</p>
</li>
<li><p>Squoosh
  장점: 설치 하지 않아도 바로 사용가능(호환성 이슈 x) 
  단점: Sharp보다 6배 느림</p>
</li>
</ul>
<h3 id="3-왜-sharp가-빠른것인가">3. 왜 Sharp가 빠른것인가?</h3>
<ul>
<li><p>성능 최적화: sharp는 이미지 처리에 특화된 라이브러리로, 이미지를 효율적으로 처리하고 압축할 수 있습니다. squoosh보다 더 최적화된 이미지 처리를 제공할 수 있어서 응답 속도가 빨라집니다.</p>
</li>
<li><p>이미지 압축: sharp는 이미지 압축을 효율적으로 수행할 수 있는 기능을 제공합니다. 이미지 압축은 이미지 파일의 크기를 줄이고 전송 대역폭을 절약하는 데 도움을 줍니다. 더 작은 이미지 파일은 더 빠른 다운로드 시간을 제공하므로 응답 속도가 향상됩니다.</p>
</li>
</ul>
<ul>
<li><p>이미지 형식 변환: sharp는 이미지를 다양한 형식으로 변환할 수 있습니다. 웹 페이지에서 사용되는 이미지 형식에 맞게 이미지를 변환하면 불필요한 데이터를 제거하고 응답 속도를 향상시킬 수 있습니다.</p>
<ul>
<li>캐싱 및 프리렌더링: next/image와 함께 sharp를 사용하면 이미지 캐싱 및 프리렌더링과 같은 기능을 효과적으로 활용할 수 있습니다. 이로 인해 웹 페이지의 성능이 향상되고 응답 속도가 빨라집니다.</li>
</ul>
</li>
</ul>
<p>위의 글은 chat GPT한테 물은 결과입니다... 
다른곳에서도 찾기가 힘들어 chat GPT의 도움을 받았습니다...ㅎ</p>
<h4 id="느낀점">느낀점</h4>
<blockquote>
<p>내장된 라이브러리 하나를 사용하는되도 이렇게 공부가 많이 필요한걸 느끼고 백엔드(서버)쪽을 생각하면서 코드를 짜야겠다는 생각이 더 들었다.</p>
</blockquote>
<h4 id="참고">참고</h4>
<p><a href="https://oliveyoung.tech/blog/2023-06-09/nextjs-image-optimization/">https://oliveyoung.tech/blog/2023-06-09/nextjs-image-optimization/</a>
<a href="https://nextjs.org/docs/app/building-your-application/deploying#nodejs-server">https://nextjs.org/docs/app/building-your-application/deploying#nodejs-server</a>
<a href="https://bsnn.tistory.com/148">https://bsnn.tistory.com/148</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[NextJs에서 react-query SSR 사용시 dehydratedstate error]]></title>
            <link>https://velog.io/@minseok_yun/NextJs%EC%97%90%EC%84%9C-react-query-SSR-%EC%82%AC%EC%9A%A9%EC%8B%9C-dehydratedstate-error</link>
            <guid>https://velog.io/@minseok_yun/NextJs%EC%97%90%EC%84%9C-react-query-SSR-%EC%82%AC%EC%9A%A9%EC%8B%9C-dehydratedstate-error</guid>
            <pubDate>Thu, 07 Sep 2023 05:05:26 GMT</pubDate>
            <description><![CDATA[<p>이전 글에 이어서 react-query를 도입하던중 ssr에서 데이터를 props로 내리던중 </p>
<blockquote>
<p><img src="https://velog.velcdn.com/images/minseok_yun/post/38d93fe0-4acd-41d0-a6ae-38322559c3bc/image.png" alt="">Server Error
Error: Error serializing <code>.products.headers</code> returned from <code>getServerSideProps</code> in &quot;/products&quot;.
Reason: <code>object</code> (&quot;[object AxiosHeaders]&quot;) cannot be serialized as JSON. Please only return JSON serializable data types.</p>
</blockquote>
<p>라는 오류가 떳다....
이러한 에러의 이유를 찾을려고 구글링을 하던중 github에서 열띈 토론이 일어나는곳을 찾았다.
<a href="https://github.com/TanStack/query/issues/1458">https://github.com/TanStack/query/issues/1458</a></p>
<p>위의 글과 여러 블로그들을 참고 하여 원인은 2가지를 밝혀냈다.</p>
<h3 id="1-원인">1. 원인</h3>
<ol>
<li><p>getServerSideProps를 사용해 서버에서 클라이언트로 데이터를 props로 넘겨줄 때, props로 전달되는 값에 JSON으로 변환이 불가능한 것이 포함되어 있을 경우 해당 에러가 발생한다.</p>
</li>
<li><p>props로 넘겨주는 것은 AxiosResponse 타입 or Promise 객체가 아니라 해당 객체에서 data를 추출한 형태여야 한다.</p>
</li>
</ol>
<br/>
그럼 해결 방법은 뭐가 있을까??

<h3 id="2-해결-방법">2. 해결 방법</h3>
<p>개인적으로는 2가지 방법이 떠올랐다.</p>
<ol>
<li>Api 통신후 data를 추출한 형태를 return 해주자.</li>
<li>JSON 형태로 강제 형변환을 해주자</li>
</ol>
<p>위의 2가지 방법을 가지고 코드를 짜보자</p>
<h4 id="1-api-통신후-data를-추출한-형태를-return-해주자">1. Api 통신후 data를 추출한 형태를 return 해주자.</h4>
<pre><code class="language-javascript">// 상품 조회하기 예시 코드
// pages/api.ts
export const getProducts = async () =&gt; {
  const res = await axios.get(&quot;http://api.test.com/products&quot;);

  return res.data; // data객체를 리턴
};
</code></pre>
<p>개인적으로는 이 방식이 제일 깔끔해서 대부분 에러를 해당 방식으로 해결했던거 같다.</p>
<h4 id="2-json형태로-강제-형변환을-해주자">2. JSON형태로 강제 형변환을 해주자</h4>
<pre><code class="language-javascript">// 상품 조회하기 예시 코드
// pages/api.ts
export const getProducts = async () =&gt; {
  const res = await axios.get(&quot;http://api.test.com/products&quot;);

  return res;
};

// pages/products
export async function getServerSideProps() {
  const products = await getProducts();
  return { 
    props: {
        products: products,
        dehydratedState: JSON.parse(JSON.stringify(dehydrate(queryClient))),
      }, 
  };
}
</code></pre>
<p>위에 queryClient를 선언한 부분은 생략했습니다.
이렇게 queryClient자체를 형변환해서 보내주는 방식이 있는거 같다.
개인적으로 위의 방식이 안통했을때 아래의 방식을 이용하는게 좋을꺼 같다.
항상 내가 원하는 방식으로 데이터를 받을수 없으니 알아두는게 좋을꺼 같다.</p>
<p>이렇게 NextJs에서 SSR + react-query 조합에서의 serializing 에러를 해결하는 방법에 대해 알아봤습니다.</p>
<p>감사합니다.</p>
<h4 id="reference">Reference</h4>
<p><a href="https://careerly.co.kr/qnas/1911">https://careerly.co.kr/qnas/1911</a>
<a href="https://github.com/TanStack/query/issues/1458">https://github.com/TanStack/query/issues/1458</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[NextJs에서 react-query SSR 대응하기]]></title>
            <link>https://velog.io/@minseok_yun/NextJs%EC%97%90%EC%84%9C-react-query-SSR-%EB%8C%80%EC%9D%91%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@minseok_yun/NextJs%EC%97%90%EC%84%9C-react-query-SSR-%EB%8C%80%EC%9D%91%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 05 Sep 2023 08:36:48 GMT</pubDate>
            <description><![CDATA[<p>react-query는 react를 사용하는 사람이라면 한번쯤은 들어본 라이브러리이다.</p>
<blockquote>
</blockquote>
<p>개인적으로 장점은 크게 2가지라 생각하는데
<strong>1. 데이터 캐싱을 손쉽게 할수있다. (캐싱 방법이나 등은 설명하지 않겠습니다.)
2. 페이징처리, 지연 로딩 데이터와 같은 성능 최적화해주며 로딩 상태 등과 같은 기능을 제공을 해준다.</strong></p>
<p>위와 같은 이유로 NextJs + react-query를 많이 사용하는거 같다.</p>
<p>NextJs를 사용하는 가장 큰 이유 중 하나는 SSR이라고 생각하는데 react-query는 아쉽게도 CSR로 데이터를 가져온다.</p>
<p>그래서 react-query의 데이터를 SSR에서 사용할수 있는 대표적인 *<em>2가지 *</em>방법을 알아볼려고한다.</p>
<h3 id="1-initial-data">1. Initial Data</h3>
<p>개인적으로 가장 직관적인 방법이라고 생각한다.
프로젝트에 SSR이 적은경우 자주 사용하는거 같다.</p>
<pre><code class="language-javascript">// apis.ts
export const getProducts = () =&gt; axios.get(&#39;https://api.test.com/products&#39;);

// pages/product.tsx

const Product = (props) =&gt; {
  const { data } = useQuery(&#39;feeds&#39;, getProducts, { initialData: props.products });
  ...
}

export async function getServerSideProps() {
  const products = await getProducts();
  return { props: { products } };
}</code></pre>
<p>위와 같은 방식으로 많이 쓰는거 같다.
간략하게 설명 하자면 getServerSideProps에서 getProducts를 통해 product 데이터를 받아오고 받아온 데이터를 useQuery가 선언되어있는곳에 props로 넘겨 initialData: product 이런식으로 적용을 하면 된다.</p>
<blockquote>
<p>이해가 안된다면 아래 링크로 들어가서 추가적으로 보세요!
<a href="https://tanstack.com/query/v4/docs/react/guides/initial-query-data">https://tanstack.com/query/v4/docs/react/guides/initial-query-data</a></p>
</blockquote>
<h3 id="2-hydration">2. Hydration</h3>
<p>프로젝트에 SSR이 많이 사용이 되면 적용하는거 같다.
개인적으로는 3개 이상의 SSR대응이 필요하면 적용하는거 같다.
한번 초기설정 적용후에는 편하게 사용이 가능하다.</p>
<pre><code class="language-javascript">
// pages/_app.tsx
function MyApp({ Component, pageProps }) {
  const [client] = useState(() =&gt; new QueryClient());

  return (
    &lt;QueryClientProvider client={client}&gt;
       &lt;Hydrate state={pageProps.dehydratedState}&gt;
         &lt;Component {...pageProps} /&gt;
       &lt;/Hydrate&gt;
     &lt;/QueryClientProvider&gt;
  );
};

// apis.ts
export const getProducts = () =&gt; axios.get(&#39;https://api.test.com/products&#39;);

// pages/product.tsx
const Product = (props) =&gt; {
  const { data } = useQuery(&#39;feeds&#39;, getProducts);
  ...
}

export async function getServerSideProps() {
  const queryClient = new QueryClient();
  const products = await queryClient.fetchQuery(&#39;feeds&#39;, getProducts);

  return {
     props: {
       dehydratedState: dehydrate(queryClient),
     },
   }
 }</code></pre>
<p>_app.tsx에 SSR 설정을 한 번 해두면 다른 모든 페이지에서 위와 같은 방법으로 사용할 수 있다.</p>
<blockquote>
<p>이해가 안된다면 아래 링크로 들어가서 추가적으로 보세요!
<a href="https://tanstack.com/query/v4/docs/react/reference/hydration">https://tanstack.com/query/v4/docs/react/reference/hydration</a></p>
</blockquote>
<p>이렇게 NextJs + react-query SSR에서 데이터 쓰기에 대해 알아봤다.
뭔가 SSR대응을 할려고 추가설정이 점점 늘어나는거 같은 기분이다.</p>
<p>다음 포스트는 hydration을 하면서 겪었던 serializing error에 대해 포스팅 예정입니다.</p>
<p>읽어주셔서 감사합니다.</p>
<h3 id="reference">Reference</h3>
<p><a href="https://tanstack.com/query/v4/docs/react/overview">https://tanstack.com/query/v4/docs/react/overview</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] useState + useEffect => useQuery 로 대체하기]]></title>
            <link>https://velog.io/@minseok_yun/React-useState-useEffect-useQuery-%EB%A1%9C-%EB%8C%80%EC%B2%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@minseok_yun/React-useState-useEffect-useQuery-%EB%A1%9C-%EB%8C%80%EC%B2%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 09 Apr 2023 03:56:59 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p> Vue에서 React(NextJs)로 마이그레이션하는 과정에서 코드를 좀더 직관적이게 관리하면서 캐싱도 용이 하게 하고 싶다는 생각이 들었습니다. 
그러한 이유로 react-query를 도입하게 되었습니다.</p>
</blockquote>
<p><em><del>마이그레이션 한다고 너무 바빳네요...</del></em></p>
<br/>

<p><strong>🤚 들어가기에 앞서!</strong>  <strong>해당 코드는 가상의 코드입니다.</strong></p>
<h3 id="1-이전-코드">1. 이전 코드</h3>
<pre><code class="language-javascript">import { useEffect, useState } from &quot;react&quot;;
import { getItems } from &quot;../../../api/ItemApi&quot;;
import Item from &quot;../../../common/Item/Item&quot;;
import ItemListStyle from &quot;./ItemListStyle&quot;;
import ItemType from &quot;../../constants/Item&quot;

const ItemList = () =&gt; {
  const [items, setItems] = useState&lt;ItemType&gt;([]);
  useEffect(() =&gt; {
    const fetchData = async () =&gt; {
      const res = await getItems(&quot;/items&quot;);
      setItems(res.data);
    };
    fetchData();
  }, []);
  return (
    &lt;ItemListStyle&gt;
      {items.map((item, i) =&gt; {
        return (
            &lt;Item
              key={item.id}
              item={item}
              tabIndex={i}
            /&gt;
        );
      })}
    &lt;/ItemListStyle&gt;
  );
};

export default ItemList;
</code></pre>
<p>useState, useEffect를 사용해서 구현하였습니다.
처음 한번만 데이터를 가져옵니다.
해당 코드에 적용한 것을 보겠습니다.</p>
<h3 id="2-변경-코드">2. 변경 코드</h3>
<pre><code class="language-javascript">import { useQuery } from &quot;react-query&quot;;
import { getItems } from &quot;../../../api/ItemApi&quot;;
import Item from &quot;../../../common/Item/Item&quot;;
import ItemListStyle from &quot;./ItemListStyle&quot;;

const ItemList = () =&gt; {
  const fetchData = async () =&gt; {
      const res = await getItems(&quot;/items&quot;);
      return(res.data);
  };

  const { data, isLoading, isError } = useQuery(&quot;item&quot;, fetchData);

  if (isLoading) {
    return &lt;div&gt;isLoding...&lt;/div&gt;;
  }
  if (isError) {
    return &lt;div&gt;error&lt;/div&gt;;
  }

  return (
    &lt;ItemListStyle&gt;
      {data.map((item, i) =&gt; {
        return (
            &lt;Item
              key={item.id}
              item={item}
              tabIndex={i}
            /&gt;
        );
      })}
    &lt;/ItemListStyle&gt;
  );
};

export default ItemList;
</code></pre>
<p>코드 자체가 깔끔하게 변경된것을 볼수 있습니다.
또한 useEffect,useState 등을 쓰지 않고도 상태 관리 및 fetch를 컨트롤 할수 있어서 좋았던거 같습니다.</p>
<br/>

<blockquote>
<p>해당 코드를 작성하면서 useEffect에 useQuery도 넣는 실수도 하고 refetch도 쓰고 많은 실수를 해보면서 많은 것을 배운거 같습니다...</p>
</blockquote>
<h4 id="참조">참조</h4>
<p><a href="https://choar816.tistory.com/167">https://choar816.tistory.com/167</a>
<a href="https://tanstack.com/query/latest/docs/react/overview!%5B%5D(https://velog.velcdn.com/images/minseok_yun/post/c6879462-afea-4782-b567-1b170ee40265/image.png)">https://tanstack.com/query/latest/docs/react/overview![](https://velog.velcdn.com/images/minseok_yun/post/c6879462-afea-4782-b567-1b170ee40265/image.png)</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[URL / URI / URN 한방에 이해하기!]]></title>
            <link>https://velog.io/@minseok_yun/URL-URI-URN-%ED%95%9C%EB%B0%A9%EC%97%90-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@minseok_yun/URL-URI-URN-%ED%95%9C%EB%B0%A9%EC%97%90-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 12 Feb 2023 08:09:29 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>웹 개발을 하다가 문득 URL.URI의 차이점을 잘 모르는거 같아 정리한 글입니다.</p>
</blockquote>
<h3 id="1-urluriurn의-차이점">1. URL,URI,URN의 차이점</h3>
<p>우리가 웹 개발을 하다보면 URL이라는 단어를 정말 많이 듣는다.
하지만 URI,URN은 조금 생소할수 있다.
아래의 사진이 차이점을 잘 보여주는 사진이다.</p>
<p><img src="https://velog.velcdn.com/images/minseok_yun/post/94e9e52b-ab0b-4dea-a1bc-a02b5ecb9c80/image.png" alt="차이점 사진"></p>
<p>위의 사진을 정리하자면</p>
<h4 id="1-자원의-식별자uri">1. 자원의 식별자(URI)</h4>
<h4 id="2-위치url">2. 위치(URL)</h4>
<h4 id="3-이름urn">3. 이름(URN)</h4>
<br>

<h3 id="2-urluriurn의-정의">2. URL,URI,URN의 정의</h3>
<ul>
<li>URI: <strong>통합 자원 식별자</strong>(Uniform Resource Identifier)는 인터넷에 있는 자원을 어디에 있는지 자원 자체를 식별하는 방법이다. <em>즉 URL,URN은 URI의 하위 개념이다.</em></li>
<li>URL: 파일식별자(Uniform Resource Locator)는 네트워크 상에서 리소스에 대한 <strong>구체적인 위치</strong>를 서술한다.</li>
<li>URN: 통합 자원 이름(Uniform Resource Name)은 urn:scheme 을 사용하는 URI를 위한 역사적인 이름이다.<em>URN은 리소스를 여기저기로 옮기더라도 문제없이 동작한다.</em></li>
</ul>
<h3 id="3-urluriurn-구분하기">3. URL,URI,URN 구분하기</h3>
<p><img src="https://velog.velcdn.com/images/minseok_yun/post/a90e3405-2eee-4efc-b991-b5eca3e580b5/image.png" alt="구분하는 사진"></p>
<p>하지만 설명만으로는 이해하기 힘들어 예시를 든 사진으로 다시 설명을 하자면</p>
<p>예를들어 다음과 같은 홈페이지 링크가 있다고 하자.</p>
<p><a href="https://www.google.com/search?q=question">https://www.google.com/search?q=question</a></p>
<p><a href="https://www.google.com/">https://www.google.com/</a> 서버에 위치한 search 페이지는 query string인 page의 값에 따라 여러가지 화면 결과를 나타나게 된다.</p>
<p>이때 여기서 URL은 search의 위치를 표기한 <a href="https://www.google.com/search">https://www.google.com/search</a> 까지이다.</p>
<p>하지만 사용자가 원하는 정보에 도달 하기위해서는 ?q=question라는 식별자(Identifier)가 필요한 것이다.</p>
<p>따라서 엄격히 구분하자면 위의 <a href="https://www.google.com/search?q=question">https://www.google.com/search?q=question</a> 주소는 URI이고, 식별자가 빠진 <a href="https://www.google.com/search%EB%A5%BC">https://www.google.com/search를</a> URL이라고 하는 것이다. </p>
<p>URL,URL을 조금 더 쉽게 표현하자면 아래의 두 주소는 같은 URL이고 다른 URI라고 할 수 있다.</p>
<p><a href="https://www.google.com/search?q=question">https://www.google.com/search?q=question</a></p>
<p><a href="https://www.google.com/search?q=question2">https://www.google.com/search?q=question2</a></p>
<h3 id="4-느낀점">4. 느낀점</h3>
<blockquote>
<p>다른 사람들이 url,uri 주세요 했을때 아무렇지 않게 말했던 단어들도 한번 더 짚어 보고 사용하자..</p>
</blockquote>
<h3 id="참조">참조</h3>
<p><a href="https://inpa.tistory.com/entry/WEB-%F0%9F%8C%90-URL-URI-%EC%B0%A8%EC%9D%B4">https://inpa.tistory.com/entry/WEB-%F0%9F%8C%90-URL-URI-%EC%B0%A8%EC%9D%B4</a></p>
<p><a href="https://programming119.tistory.com/194">https://programming119.tistory.com/194</a></p>
<p><a href="https://kotlinworld.com/96">https://kotlinworld.com/96</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Preload, Prefetch, Preconnect 차이점이 뭘까?]]></title>
            <link>https://velog.io/@minseok_yun/Preload-Prefetch-Preconnect-%EC%B0%A8%EC%9D%B4%EC%A0%90%EC%9D%B4-%EB%AD%98%EA%B9%8C</link>
            <guid>https://velog.io/@minseok_yun/Preload-Prefetch-Preconnect-%EC%B0%A8%EC%9D%B4%EC%A0%90%EC%9D%B4-%EB%AD%98%EA%B9%8C</guid>
            <pubDate>Sun, 05 Feb 2023 06:17:16 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>성능 최적화를 하다가 preload, prefetch 등을 잘모르고 쓰는거 같아 이것을 알아보기로 했다.</p>
</blockquote>
<h3 id="1-preload">1. Preload</h3>
<p>현재 페이지에서 사용할 리소스(resource)를 <strong>우선순위</strong>를 높게하여 빠르게 가져오게 한다. 주로 웹 폰트, 비지니스적으로 필요한 이미지 같이 빠르게 로드(load)되어야하는 곳에서 사용한다.</p>
<pre><code class="language-javascript">&lt;link rel=&quot;preload&quot; href={&#39;/static/images/myImage.png&#39;} as=&quot;image&quot; /&gt;</code></pre>
<p>_* 주의사항
 Preload는 전체적인 웹 성능을 빠르게 하기 위해 사용되는 속성이 아니다.
초기 로드 시 사용되지 않는 리소스에 PreLoad를 설정하지 않게 주의해야 한다.
_</p>
<br>


<h3 id="2-prefetch">2. Prefetch</h3>
<p>낮은 순위의 리소스에 주로 사용하고 <strong>유휴상태</strong>일 때 background에서 리소스를 fetch해온다. 그리고 브라우저에 캐시로 잡아두는 방식이다. 캐싱을 해두기 때문에 렌더링 과정에서 큰 리소스도 빠르게 렌더링 할 수 있다.</p>
<pre><code class="language-javascript">&lt;link rel=&quot;prefetch&quot; href=&quot;page-2.html&quot;&gt;</code></pre>
<p> _* 주의사항
 Preload는 재귀적으로 동작하지 않습니다.
위의 코드와 같이 prefetch를 사용한다면, page-2.html이라는 HTML 리소스를 가져올 수 있지만 page-2.html에서 사용되는 CSS 등의 리소스들은 가져오지 않습니다.
_</p>
<br>

<h3 id="3-preconnect">3. Preconnect</h3>
<p>현재 페이지에서 외부 도메인의 리소스를 참고하는 것을 브라우저에게 알려 미리 외부 도메인과 연결을 설정할 수 있게 합니다</p>
<pre><code class="language-javascript">&lt;link rel=&quot;preconnect&quot; href=&quot;https://example.com&quot;&gt;</code></pre>
<p> _* 주의사항
preconnect는 외부 도메인과 연결을 구축하기 때문에 많은 CPU 시간을 차지할 수 있습니다.
10초 이내로 브라우저가 닫힌다면, 이전의 모든 연결 작업은 낭비되는 것이기 때문에 브라우저가 빨리 닫힐 수 있는 페이지에서는 preconnect를 사용하지 않는 것이 좋습니다.
_</p>
<br>

<h3 id="느낀점">느낀점</h3>
<blockquote>
<p>생각보다 상황에 따른 사용방법이 많았다 최적화를 더욱 관심 가질수록 cs, js 의 기초의 중요성이 많이 필요한거같다.</p>
</blockquote>
<h3 id="참고자료">참고자료</h3>
<p><a href="https://beomy.github.io/tech/browser/preload-preconnect-prefetch/">참고자료1</a></p>
<p><a href="https://velog.io/@fepanbr/HTML-preload-preconnect-prefetch">참고자료2</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[n을 활용한 node.js 버전 관리]]></title>
            <link>https://velog.io/@minseok_yun/n%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-node.js-%EB%B2%84%EC%A0%84-%EA%B4%80%EB%A6%AC</link>
            <guid>https://velog.io/@minseok_yun/n%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-node.js-%EB%B2%84%EC%A0%84-%EA%B4%80%EB%A6%AC</guid>
            <pubDate>Sat, 21 Jan 2023 05:36:28 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>최근 바쁜 일상 때문에 블로그를 뒤로 했었는데 다시 힘을 내 작성을 시작하도록 하자!</p>
</blockquote>
<p>node.js 버전을 관리 해야하는 일이 생겼다.
기존 프로젝트라 vue2로 이루어져 있어서 node 버전을 낮은것을 사용해야 했는데 마이그레이션을 진행함으로써 node 버전을 변경하며 적용을 시켜야 했었다.</p>
<p>전에는 nvm이라는 것을 사용했지만 최근 n이라는 툴을 사용하면 더 쉽게 관리 할수 있다고 하여 사용해 보았습니다.</p>
<br>

<p><em><strong>저자는 맥을 사용하고 있습니다.</strong></em></p>
<p>해당 툴을 사용할땐 sudo를 이용하여 관리자 권한모드로 사용하도록 합니다.</p>
<ul>
<li><p>n 설치 방법</p>
<p>npm</p>
<pre><code>sudo npm install -g n</code></pre><p>yarn</p>
<pre><code>sudo yarn install -g n</code></pre><br>
</li>
<li><p>n의 이용법</p>
<pre><code>n -h</code></pre><br>
</li>
<li><p>현재 설치된 node 버전 확인</p>
<pre><code>n 
 o node/14.16.1
   node/18.12.1</code></pre><p><em>여기서 방향키로 선택하게되면 해당 Node 버전으로 변경된다.</em></p>
</li>
</ul>
<ul>
<li><p>특정 버전 node 설치하기</p>
<pre><code>n latest
n stable
n lts</code></pre><p><em>latest는 가장 최신버전, stable은 가장 안정적인 방법, lst는 LTS 버전을 설치합니다.</em></p>
<ul>
<li>특정 버전 node 삭제하기<pre><code>n rm [버전]</code></pre></li>
</ul>
</li>
</ul>
<h4 id="후기">후기</h4>
<blockquote>
<p>최근 회사들의 공고를 보면 node.js을 요구하는데 이유를 조금씩 느끼는 중이다. 공부할게 더 늘었다... ㅜㅜ</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[useEffect 덜어내기]]></title>
            <link>https://velog.io/@minseok_yun/useEffect-%EB%8D%9C%EC%96%B4%EB%82%B4%EA%B8%B0</link>
            <guid>https://velog.io/@minseok_yun/useEffect-%EB%8D%9C%EC%96%B4%EB%82%B4%EA%B8%B0</guid>
            <pubDate>Fri, 26 Aug 2022 03:24:50 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>최근 리팩토링 도중 useEffect가 남발하는 코드를 보여 이걸 해결해 나가아가는 과정을 글로 적어 보려고 합니다.</p>
</blockquote>
<br/>

<h2 id="1-먼저-예제를-보자">1. 먼저 예제를 보자</h2>
<p>아래 예제에서 item, filter 둘 중 하나의 데이터만 변경되더라도 ui가 업데이트 되어야 하기 때문에 visibleItems가 변경되어야 한다. 그래서 useEffect를 통해 ui 관련 state를 업데이트 시켰었다.</p>
<pre><code class="language-javascript">function ItemList({ item, filter }) {
  const [newItem, setNewItem] = useState(&#39;&#39;);
  const [visibleItems, setVisibleItems] = useState([]);

  useEffect(() =&gt; {
    setVisibleItems(getFilteredItems(item, filter));
  }, [item, filter]);

}</code></pre>
<p>하지만 이렇게 되면 불필요한 리렌더링을 발생시킨다. 굳이 setVisibleItems을 useEffect에 호출 할 필요가 없습니다.</p>
<h2 id="2-리팩토링을-해보자">2. 리팩토링을 해보자</h2>
<pre><code class="language-javascript">function ItemList({ item, filter }) {
  const [newItem, setNewItem] = useState(&#39;&#39;);

  const visibleItems = getFilteredItems(item, filter));
}</code></pre>
<p>이렇게 리팩토링이 가능하다.
최신 데이터가 반영이 안되는게 아니라는 의문을 가질수도 있지만,
컴포넌트가 리렌더링 될 때마다 ui가 의존하고 있는 visibleItems가 업데이트 되기 때문에 ui는 최신 데이터 반영을 보장할 수 있습니다.</p>
<h2 id="3-렌더링-성능을-높여보자">3. 렌더링 성능을 높여보자</h2>
<p>만약 getFilteredItems가 비싼 연산이면 어떨까요?</p>
<pre><code class="language-javascript">import { useMemo,useState } from &quot;react&quot;

function ItemList({ item, filter }) {
  const [newItem, setNewItem] = useState(&#39;&#39;);

  const visibleItems = useMemo(()=&gt; getFilteredItems(item, filter),[item,filter]);
}</code></pre>
<p>이럴때는 useMemo를 사용해 연산한 값을 재사용하여 성능 최적화를 할수 있습니다.</p>
<br/>

<h3 id="후기">후기</h3>
<blockquote>
<p>최근 성능 최적화에 대한 중요성을 느끼게 되었고 작은것 하나하나부터 습관을 들여야겠다.</p>
</blockquote>
<h3 id="reference">Reference</h3>
<p><a href="https://ko.reactjs.org/docs/hooks-reference.html#usememo">https://ko.reactjs.org/docs/hooks-reference.html#usememo</a>
<a href="https://gusrb3164.github.io/web/2021/12/29/less-use-useeffect/">https://gusrb3164.github.io/web/2021/12/29/less-use-useeffect/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Build: Cannot use JSX unless the '--jsx' flag is provided.   이게 뭔데...]]></title>
            <link>https://velog.io/@minseok_yun/Build-Cannot-use-JSX-unless-the-jsx-flag-is-provided.-%EC%9D%B4%EA%B2%8C-%EB%AD%94%EB%8D%B0</link>
            <guid>https://velog.io/@minseok_yun/Build-Cannot-use-JSX-unless-the-jsx-flag-is-provided.-%EC%9D%B4%EA%B2%8C-%EB%AD%94%EB%8D%B0</guid>
            <pubDate>Mon, 15 Aug 2022 09:59:29 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>Typescript 로 클립보드 관련 오픈소스를 만들어 중 다른 프로젝트에 적용하다 마주한 에러였다...
정말 처음보는 에러들은 항상 주구장창 나오는거같다 ㅎㅎ</p>
</blockquote>
<h3 id="1-원인-찾기">1. 원인 찾기</h3>
<br/>

<p>처음 의심을 해본곳은 적용시킨 프로젝트의 셋팅이었다.</p>
<p>그렇게 구글링을 해서 찾아본 결과 vsc 세팅 문제라고 하여 시도해 보았다...</p>
<ol>
<li><p>vsc 맨밑에 버전을 찾아보고
<img src="https://velog.velcdn.com/images/minseok_yun/post/2d579ca9-8f78-4b66-af97-3be515e58438/image.png" alt="vsc 셋팅 1"></p>
</li>
<li><p>명령 팔레트를 열어서 ctrl + shift+ p (MAC = command + shift + p)
타입스크립트 버전 선택을 할수 있는곳으로가서
<img src="https://velog.velcdn.com/images/minseok_yun/post/10707b96-1409-4b79-a3b3-0b6d07ac8b48/image.png" alt="vsc 셋팅 2"></p>
</li>
<li><p>&quot;작업 공간 버전 사용&quot; 을 선택해보았다
<img src="https://velog.velcdn.com/images/minseok_yun/post/868a56b0-3c9c-41a4-b0a5-bbd922947938/image.png" alt="vsc 셋팅 3"></p>
</li>
</ol>
<p>과연... 첫번째 시도만에 해결될 일이 없었습니다...
그렇게 찾은 2번째 원인은 바로 babel설정 문제였습니다.
Typescript로 만든 오픈 소스 였기에 js로 빌드 후 배포를 했어야 했는데...
이런 초보적인 실수를 했더군요..</p>
<br/>

<h3 id="2-원인-해결">2. 원인 해결</h3>
<p>오픈소스의 package.json을 보기로 하였다.</p>
<pre><code class="language-javascript">&quot;main&quot;: &quot;src/index.ts&quot;</code></pre>
<p>이런식으로 오픈소스의 메인이 index.ts 로 잡혀있었다.
이렇게되면 오픈소스를 사용하는 사용자는 ts파일을 받게되어
ts 파싱? 에러를 마주하게 된다.</p>
<p>그러하여 나는 뭘해야하나 찾아보니
npm run build 에 이러한 코드를 추가하여 원인이 해결되었다.</p>
<pre><code class="language-javascript">&quot;scripts&quot;: {
    &quot;build&quot;: &quot;rm -rf dist &amp;&amp; tsc&quot;,
  }</code></pre>
<p>위의 코드를 해석해 보면</p>
<blockquote>
<p>기존 dist폴더를 삭제 (없으면 생성) 하고 tsc로 코드 컴파일 및 빌드를 한다
빌드한 파일들은 dist 폴더에 저장</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/minseok_yun/post/bbe13707-e45b-400e-a61f-026ffdfa204f/image.png" alt="tsc 빌드 결과"></p>
<p>그렇게 되면 이렇게 index.ts 가아닌 index.js 파일이 생성이 되어있다.
안에 내용을 보면 Typescript언어가 js로 컴파일 되어있는것을 볼수 있다.</p>
<p>이 작업을 진행한후 </p>
<pre><code class="language-javascript">&quot;main&quot;: &quot;src/index.ts&quot; -&gt; &quot;main&quot;: &quot;dist/index.js&quot; </code></pre>
<p>이런식으로 오픈소스의 메인을 바꾸면 다른 사용자들이 사용을 할때 에러가 뜨지 않는다.</p>
<h3 id="3-후기">3. 후기..</h3>
<blockquote>
<p>정말 언어만 안다고 해결되지 않는것들이 너무 많은거 같다..
컴파일, 빌드 환경 이런쪽을 더 공부해 봐야 겠다.</p>
</blockquote>
<br/>

<h4 id="reference">Reference</h4>
<p><a href="https://stackoverflow.com/questions/50432556/cannot-use-jsx-unless-the-jsx-flag-is-provided">https://stackoverflow.com/questions/50432556/cannot-use-jsx-unless-the-jsx-flag-is-provided</a></p>
<p><a href="https://www.faqcode4u.com/faq/244570/build-cannot-use-jsx-unless-the-jsx-flag-is-provided">https://www.faqcode4u.com/faq/244570/build-cannot-use-jsx-unless-the-jsx-flag-is-provided</a></p>
<p><a href="https://docs.microsoft.com/ko-kr/visualstudio/javascript/compile-typescript-code-npm?view=vs-2022">https://docs.microsoft.com/ko-kr/visualstudio/javascript/compile-typescript-code-npm?view=vs-2022</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[useEffect?? useLayoutEffect?? 차이점을 알아보자!]]></title>
            <link>https://velog.io/@minseok_yun/useEffect-useLayoutEffect-%EC%B0%A8%EC%9D%B4%EC%A0%90%EC%9D%84-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90</link>
            <guid>https://velog.io/@minseok_yun/useEffect-useLayoutEffect-%EC%B0%A8%EC%9D%B4%EC%A0%90%EC%9D%84-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90</guid>
            <pubDate>Sat, 09 Jul 2022 04:06:18 GMT</pubDate>
            <description><![CDATA[<p>최근 바쁜 일상을 지내던 중 동료 개발자분이 useLayoutEffect을 쓰는것을 보고 요놈의 쓰임새가 궁금해서 정리하기로 하였다.</p>
<br/>

<blockquote>
<p>설명에 들어가기전 중요 개념 2가지를 알아야 한다.</p>
</blockquote>
<ul>
<li><strong>Render</strong>: DOM Tree 를 구성하기 위해 각 엘리먼트의 스타일 속성을 계산하는 과정</li>
<li><strong>Paint</strong>: 실제 스크린에 Layout을 표시하고 업데이트하는 과정</li>
</ul>
<br/>

<h3 id="1-useeffect-란">1. useEffect 란?</h3>
<p>useEffect 는 컴포넌트들이 render 와 paint 된 후 실행된다. 비동기적으로 실행됩니다. paint 된 후 실행되기 때문에, useEffect 내부에 dom 에 영향을 주는 코드가 있을 경우 사용자 입장에서는 화면의 깜빡임을 보게됩니다.</p>
<ul>
<li>useEffect의 수명 주기
<img src="https://velog.velcdn.com/images/minseok_yun/post/8f8c9b62-8d42-4a8a-9592-e3c1d86221ca/image.png" alt="useEffect"> </li>
</ul>
<br/>

<h3 id="2-uselayouteffect-란">2. useLayoutEffect 란?</h3>
<p>useLayoutEffect 는 컴포넌트들이 render 된 후 실행되며, 그 이후에 paint 가 됩니다. 이 작업은 동기적으로 실행됩니다. paint 가 되기전에 실행되기 때문에 dom 을 조작하는 코드가 존재하더라도 사용자는 깜빡임을 경험하지 않습니다.</p>
<ul>
<li>useLayoutEffect의 생명주기
<img src="https://velog.velcdn.com/images/minseok_yun/post/6e108429-c603-4e35-ab4b-ed62d0374a3b/image.png" alt="useLayoutEffect"></li>
</ul>
<br/>

<h3 id="3-결론">3. 결론</h3>
<p>useLayoutEffect 는 동기적으로 실행되고 내부의 코드가 모두 실행된 후 painting 작업을 거칩니다. 따라서 로직이 복잡할 경우 사용자가 레이아웃을 보는데까지 시간이 오래걸린다는 단점과 성능 저하가 우려되어, 기본적으로는 항상 useEffect 만을 사용하는 것을 권장드립니다. </p>
<p>구체적인 예시로는</p>
<blockquote>
</blockquote>
<ul>
<li>Data fetch</li>
<li>Event Handler</li>
<li>State reset</li>
</ul>
<p>등의 작업은 useEffect를 사용하여야 하고</p>
<pre><code class="language-javascript">const Layout = () =&gt; {
  const [count, setCount] = useState(0);

  useLayoutEffect(() =&gt; {
    if (value === 0) {
      setValue(10 + Math.random() * 200);
    }
  }, [count]);

  return (
    &lt;button onClick={() =&gt; setCount(0)}&gt;
      count: {count}
    &lt;/button&gt;
  );
};
</code></pre>
<p>위의 코드와 같이 초기 값과 paint된후의 값이 다를때 깜빡임이 일어날수 있어
이럴경우 useLayoutEffect를 사용하는게 바람직하다.</p>
<h3 id="출처">출처</h3>
<ul>
<li><a href="https://pubudu2013101.medium.com/what-is-the-real-difference-between-react-useeffect-and-uselayouteffect-51723096dc19">https://pubudu2013101.medium.com/what-is-the-real-difference-between-react-useeffect-and-uselayouteffect-51723096dc19</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Debounce로 성능 향상 시키기]]></title>
            <link>https://velog.io/@minseok_yun/Debounce%EB%A1%9C-%EC%84%B1%EB%8A%A5-%ED%96%A5%EC%83%81-%EC%8B%9C%ED%82%A4%EA%B8%B0</link>
            <guid>https://velog.io/@minseok_yun/Debounce%EB%A1%9C-%EC%84%B1%EB%8A%A5-%ED%96%A5%EC%83%81-%EC%8B%9C%ED%82%A4%EA%B8%B0</guid>
            <pubDate>Sat, 11 Jun 2022 06:29:17 GMT</pubDate>
            <description><![CDATA[<p>키업으로 하게되면 api 호출을 자주 하게 되어 성능 저하의 문제가 있습니다.
그래서 debounce 이용하여 검색 기능을 구현해 보았습니다.</p>
<h3 id="1-debounce-란">1. Debounce 란?</h3>
<blockquote>
<ul>
<li>디바운싱(Debouncing)은 시간이 많이 걸리는 작업이 자주 실행되지 않도록 사용되는 프로그래밍 방법입니다.</li>
</ul>
</blockquote>
<ul>
<li>디바운싱은 함수가 실행되기 전에 일정 시간을 대기하도록 합니다. 즉, 함수가 호출되는 속도를 제한합니다.</li>
<li>함수가 여러 번 호출되면, 일정 시간이 지난 후 마지막에 호출된 함수만 실행되고 이전의 호출된 함수는 무시됩니다.</li>
</ul>
<br>

<h3 id="2-일반-input-change">2. 일반 Input Change</h3>
<p>키업을 기준으로 코드를 짯습니다.</p>
<pre><code class="language-js">
const Debounce = () =&gt; {

  //일반 input state
  const [normalValue, setNormalValue] = useState&lt;string&gt;(&#39;&#39;);

  //일반 input change
  const normalChange = (e: React.FormEvent&lt;HTMLInputElement&gt;) =&gt; {
    setNormalValue(e.currentTarget.value);
  };

  return (
    &lt;&gt;
      &lt;Styled.Wrap&gt;
        &lt;Styled.InputWrap&gt;
          &lt;Styled.SearchInput
            placeholder=&quot;Nomal 입력&quot;
            type=&quot;text&quot;
            value={normalValue}
            onChange={normalChange}
          /&gt;
          &lt;p&gt; Normal: {normalValue}&lt;/p&gt;
        &lt;/Styled.InputWrap&gt;
      &lt;/Styled.Wrap&gt;
    &lt;/&gt;
  );
};
</code></pre>
<p>이런식으로 하게 되면 input 박스에 글자를 입력 할때마다 state에 값을 실시간으로 업데이트 하게 됩니다.
만약 api호출을 하게된다면 글자 하나하나 당 api를 호출하기 때문에 성능 저하의 원인이 될수 있습니다.</p>
<p>그래서 <strong>debounce</strong> 를 이용하여 일정 시간이 지난 후 마지막에 호출된 함수만 실행시키게 만들어 보았습니다.</p>
<br>

<h3 id="3-debounce-input-change">3. debounce Input change</h3>
<pre><code class="language-js">
const Debounce = () =&gt; {

  //input state
  const [debounceValue, setDebounceValue] = useState&lt;string&gt;(&#39;&#39;);
  //debounce input state
  const [debounceResultValue, setDebounceResulValue] = useState&lt;string&gt;(&#39;&#39;);

  //input에 들어오는 글자
  const debounceChange = (e: React.FormEvent&lt;HTMLInputElement&gt;) =&gt; {
    setDebounceValue(e.currentTarget.value);
  };
  //debounce 처리된 input 값
  const debounceValueChange = (searchValue: string) =&gt; {
    setDebounceResulValue(searchValue);
  };

  //디바운스 시간 처리 및 함수 호출
  const debouncedSearchTerm = useDebounce(debounceValue, 500);

  //debounce 일정 시간 호출 되는지 확인
  function useDebounce(value: string, delay: number) {
    const [debouncedValue, setDebouncedValue] = useState(value);
    useEffect(() =&gt; {
      const handler = setTimeout(() =&gt; {
        setDebouncedValue(value);
      }, delay);

      return () =&gt; {
        clearTimeout(handler);
      };
    }, [value, delay]);

    return debouncedValue;
  }

  //debounce 실시간 처리
  useEffect(() =&gt; {
    if (debouncedSearchTerm) {
      debounceValueChange(debouncedSearchTerm);
    } else {
      setDebounceResulValue(&#39;&#39;);
    }
  }, [debouncedSearchTerm]);

  return (
    &lt;&gt;
      &lt;Styled.Wrap&gt;
        &lt;Styled.InputWrap&gt;
          &lt;Styled.SearchInput
            placeholder=&quot;Debounce 입력&quot;
            type=&quot;text&quot;
            value={debounceValue}
            onChange={debounceChange}
          /&gt;
          &lt;p&gt;Debounce: {debounceResultValue}&lt;/p&gt;
        &lt;/Styled.InputWrap&gt;
      &lt;/Styled.Wrap&gt;
    &lt;/&gt;
  );
};
</code></pre>
<p>위에서 부터 코드를 설명 하겟습니다.</p>
<ul>
<li>debounceChange를 통해 input에 입력되는 값을 받게 됩니다.</li>
<li>debounceValue의 값이 바뀌며 의존성이 걸린 debouncedSearchTerm가 실행이됩니다.</li>
<li>debouncedSearchTerm이 실행되면 useDebounce 함수가 실행이되는데 여기서 input 값이랑 delay(시간)을 기준으로 일정 시간 동안 input값이 추가 적으로 입력되는지 보게됩니다.</li>
<li>만약 delay시간동안 추가적인 입력이 없게되면 useEffect를 통해 debounceResultValue 의 값이 바뀌면서 결과값이 노출되게 됩니다.</li>
</ul>
<br>

<h3 id="4-차이점">4. 차이점</h3>
<p>사진으로 키업과 debounce의 차이점을 보여드리겠습니다 
<img src="https://velog.velcdn.com/images/minseok_yun/post/c8674250-0e08-4b6d-bc77-1ae1673ae69f/image.gif" alt="debounce와 키업의 차이점"></p>
<h3 id="5-느낀점">5. 느낀점</h3>
<p>키업으로 항상 검색 같은 기능을 구현 하였는데 이렇게 debounce 같이 웹성능 최적화 작업을 알게되어 기뻣다. 오늘도 성장하는 개발자가 되어가도록 하자!!</p>
<br>

<h3 id="출처">출처</h3>
<ul>
<li><a href="https://developer-talk.tistory.com/263">https://developer-talk.tistory.com/263</a></li>
<li><a href="https://developer-talk.tistory.com/248">https://developer-talk.tistory.com/248</a></li>
<li><a href="https://flamingotiger.github.io/javascript/throttle-debounce/">https://flamingotiger.github.io/javascript/throttle-debounce/</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[IntersectionObserver로 무한 스크롤 만들기]]></title>
            <link>https://velog.io/@minseok_yun/IntersectionObserver%EB%A1%9C-%EB%AC%B4%ED%95%9C-%EC%8A%A4%ED%81%AC%EB%A1%A4-%EB%A7%8C%EB%93%A4%EA%B8%B0</link>
            <guid>https://velog.io/@minseok_yun/IntersectionObserver%EB%A1%9C-%EB%AC%B4%ED%95%9C-%EC%8A%A4%ED%81%AC%EB%A1%A4-%EB%A7%8C%EB%93%A4%EA%B8%B0</guid>
            <pubDate>Wed, 08 Jun 2022 13:48:48 GMT</pubDate>
            <description><![CDATA[<p>최근 무한 스크롤 구현을 하다가 scroll이벤트보다 좋은 방법으로 무한 스크롤 구현을 알아보다 Intersection Observer API를 알게되었고 제가 한번 해보도록 하겠습니다!</p>
<h3 id="1-설치">1. 설치</h3>
<pre><code class="language-js"> yarn add react-intersection-observer</code></pre>
<p>또는</p>
<pre><code class="language-js"> npm i react-intersection-observer</code></pre>
<br/>

<h3 id="2-설명">2. 설명</h3>
<p><em>Intersection Observer 예제</em></p>
<pre><code class="language-js"> let observer = new IntersectionObserver(callback, options);</code></pre>
<p>Intersection Observer를 생성할 때는 3가지 옵션 설정이 가능하다.
옵션에는 root, rootMargin, threshold 이 있다.</p>
<p><img src="https://velog.velcdn.com/images/minseok_yun/post/11a5584e-277d-4f6d-b777-da0fe3a5de11/image.png" alt=""></p>
<ul>
<li><strong>root</strong> : 이 요소를 기준으로 관찰하고자하는 요소의 가시성을 결정한다. (default: 브라우저 viewport)</li>
<li><strong>rootMargin</strong>: 단어의 뜻 그대로 root가 갖는 margin 값이다. threshold를 계산할 때 rootMargin 만큼 더 계산합니다. (default: 0)</li>
<li><strong>threshold</strong> : root를 기준으로 대상요소가 얼마만큼 보일때 콜백함수를 실행할지 0~1 사이의 값으로 나타낸다. 만약 요소가 25%씩 보일때마다 실행하고 싶다면 [0, 0.25, 0.5, 0.75, 1] 와 같이 배열로 작성하면 된다. (default: 1)</li>
</ul>
<br/>

<h3 id="3-구현">3. 구현</h3>
<p><img src="https://velog.velcdn.com/images/minseok_yun/post/3e9e269c-ed8c-41db-a006-9ada0d15b0b6/image.gif" alt="무한스크롤"></p>
<p>우선 Style 부분의 설명은 제외하고 설명을 하겠습니다.</p>
<p>전체코드 </p>
<pre><code class="language-js">const InfiniteScroll = () =&gt; {
  const [itemList, setItemList] = useState&lt;number[]&gt;([
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
  ]);
  const [target, setTarget] = useState&lt;HTMLElement | null | undefined&gt;(null);
  const [isLoading, setIsLoading] = useState&lt;boolean&gt;(false);

  const onIntersect: IntersectionObserverCallback = async (
    [entry],
    observer,
  ) =&gt; {
    if (entry.isIntersecting &amp;&amp; !isLoading) {
      //관찰대상 끊기
      observer.unobserve(entry.target);
      //로딩컴포넌트 on
      setIsLoading(true);
      // 데이터를 가져오는 부분
      await new Promise((resolve) =&gt; setTimeout(resolve, 1000));
      let Items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
      setItemList((itemLists) =&gt; itemLists.concat(Items));
      setIsLoading(false);
      //새로운 관찰대상 target
      observer.observe(entry.target);
    }
  };
 return (
    &lt;&gt;
      &lt;ItemWrap&gt;
        {itemList.map((item: number, index: number) =&gt; {
          return &lt;Styled.Item&gt;{index + 1}&lt;/Styled.Item&gt;;
        })}
      &lt;/ItemWrap&gt;
      {isLoading ? (
        &lt;LoaderWrap&gt;
          &lt;ReactLoading type=&quot;spin&quot; color=&quot;#A593E0&quot; /&gt;
        &lt;/LoaderWrap&gt;
      ) : (
        &#39;&#39;
      )}
      &lt;div ref={setTarget} /&gt;
    &lt;/&gt;
  );
};

  useEffect(() =&gt; {
    if (!target) return;

    let observer: IntersectionObserver;

    if (target) {
      observer = new IntersectionObserver(onIntersect, {
        threshold: 0.4,//margin이라고 생각하면 편합니다.
      });
      observer.observe(target);
    }

    return () =&gt; observer &amp;&amp; observer.disconnect();
  }, [target]);

  return (
    &lt;&gt;
      &lt;ItemWrap&gt;
        //이미지 렌더링
        {itemList.map((item: number, index: number) =&gt; {
          return &lt;Item&gt;{index + 1}&lt;/Item&gt;;
        })}
      &lt;/ItemWrap&gt;
      //로딩시 보이는 컴포넌트
      {isLoading ? (
        &lt;LoaderWrap&gt;
          &lt;ReactLoading type=&quot;spin&quot; color=&quot;#A593E0&quot; /&gt;
        &lt;/LoaderWrap&gt;
      ) : (
        &#39;&#39;
      )}
      //감시하는곳
      &lt;div ref={setTarget} /&gt;
    &lt;/&gt;
  );
};</code></pre>
<h4 id="설명">설명</h4>
<ul>
<li>ref를 통해 대상요소를 observe 한다.</li>
<li>useEffect를 통해 마지막 대상을 관찰하다 마지막 부분에 닿으면 통신을 통해 새로운 데이터 붙이기 + 기존 관찰 대상 -&gt; 마지막 데이터로 관찰대상 변경</li>
</ul>
<br/>

<h3 id="4-느낀점">4. 느낀점</h3>
<blockquote>
<p>생각보다 개념이 어렵게 느껴져서 애먹은거 같은데 막상 구현하고 보니 이해가 되었던거 같다. 이런식으로 웹 성능과 관련된 것들을 더 공부해 나아 가야 겠다.</p>
</blockquote>
<h3 id="참조">참조</h3>
<ul>
<li><a href="https://developer.mozilla.org/ko/docs/Web/API/Intersection_Observer_API">https://developer.mozilla.org/ko/docs/Web/API/Intersection_Observer_API</a></li>
<li><a href="https://velog.io/@jce1407/React-%EB%AC%B4%ED%95%9C-%EC%8A%A4%ED%81%AC%EB%A1%A4-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-with-Intersection-Observer">https://velog.io/@jce1407/React-%EB%AC%B4%ED%95%9C-%EC%8A%A4%ED%81%AC%EB%A1%A4-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-with-Intersection-Observer</a></li>
<li><a href="https://velog.io/@euneun/%EC%A1%B8%EC%97%85%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-k162xd7y">https://velog.io/@euneun/%EC%A1%B8%EC%97%85%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-k162xd7y</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Cookie , Session 의 차이점]]></title>
            <link>https://velog.io/@minseok_yun/LocalStorage-SessionStorage-Cookie%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90</link>
            <guid>https://velog.io/@minseok_yun/LocalStorage-SessionStorage-Cookie%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90</guid>
            <pubDate>Sun, 29 May 2022 06:37:18 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>최근 로그인 관련 기능을 구현 하다가 Cookie와 Session에 차이점을 잘 모른다고 생각하여 공부하게 되었습니다.</p>
</blockquote>
<p><strong>쿠키, 세션, 웹 스토리지</strong>를 설명하기에 앞서 사용 이유를 쉽게 이해하기 위해 <strong>HTTP 프로토콜</strong>의 특징을 알아보자! </p>
<h3 id="1-http-프로토콜란">1. HTTP 프로토콜란?</h3>
<p>-비연결지향(Connectionless)</p>
<p>HTTP는 클라이언트가 요청(Request)을 서버에 보내고, 서버는 클라이언트에게 적절한 응답(Response)을 주고 연결(Connection)을 끊는 특성이 있다.</p>
<p>-상태없음(Stateless)</p>
<p>커넥션을 끊는 순간 클라이언트와 서버의 통신이 끝나며 상태 정보는 유지하지 않는 특성이 있다.</p>
<br/>

<p>HTTP는 이 두가지 특성을 보완하기 위해서 <strong>쿠키, 세션, 웹 스토리지</strong>를 사용하게 되었다.</p>
<h3 id="2-쿠키cookie">2. 쿠키(Cookie)</h3>
<p><strong>쿠키</strong>는 클라이언트 로컬에 저장되는 키와 값 형태의 작은 파일로 <strong>이름,값,만료시간,경로정보등</strong>이 들어있습니다.</p>
<p>쿠키는 주로 세가지 목적을 위해 사용됩니다.</p>
<ul>
<li>세션관리 : 서버에 저장해야할 로그인, 장바구니 등의 정보 관리</li>
<li>사용자 맞춤: 사용자가 선호하는 옵션이나 테마 등의 세팅</li>
<li>사용자 추적: 사용자의 행동을 기록하고 분석하는 용도</li>
</ul>
<p>쿠키는 2가지로 나뉘는데 바로 <strong>세션 쿠키</strong>와 <strong>지속 쿠키</strong>로 나뉜다.
만료 날짜/시간을 지정하지 않으면, &#39;메모리에 있는 동안&#39; 계속 유효하다고 판단하도록 <strong>세션 쿠키</strong>에 저장되고, 만료 날짜/시간을 지정하면 프로세스가 종료되더라도(메모리에서 사라지더라도) 특정 만료날짜/시간까지 유효하므로 <strong>지속 쿠키</strong>에 저장된다.
쉽게말해</p>
<blockquote>
<p><strong>세션 쿠키</strong>는 브라우저 메모리에 저장되므로 브라우저가 종료되면 쿠키는 사라지게 된다.
<strong>지속 쿠키</strong>는 파일로 저장되므로 브라우저가 종료되어도 쿠키는 남아있게 된다.</p>
</blockquote>
<h4 id="쿠키-프로세스">쿠키 프로세스</h4>
<ul>
<li>브라우저에서 웹페이지 접속</li>
<li>클라이언트가 요청한 웹페이지를 받으면서 쿠키를 클라이언트의 로컬(하드)에 저장</li>
<li>클라이언트가 재요청시 웹페이지 요청과 함꼐 쿠키값도 전송</li>
<li>지속적으로 로그인 정보를 가지고 있는것처럼 사용</li>
</ul>
<h4 id="쿠키의-한계">쿠키의 한계</h4>
<ul>
<li>300개까지 만 저장가능, 하나의 도메인에 20개의 값만 보유가능, 하나의 쿠키값은 4KB까지만 저장</li>
<li>사용자의 요청없이도 브라우저가 request시에 request header을 넣어 자동으로 서버에 전달</li>
</ul>
<h3 id="3-세션session">3. 세션(Session)</h3>
<p>세션은 쿠키를 기반으로 하고 있지만, 사용자 정보 파일을 브라우저에 저장하는 쿠키와 달리 세션은 서버 측에서 관리합니다.
일정 시간동안 같은 브라우저로 부터 들어오는 요구를 하나의 상태로 보고 그 상태를 유지하는 것</p>
<p>다시말해 웹 브라우저를 통해 웹서버에 접속한 이후부터 그 브라우저를 종료 할떄까지 상태 유지</p>
<p>클라이언트가 request를 보내면, 해당 서버의 엔진이 클라이언트에게 유일한 세션ID를 부여합니다</p>
<p>세션은 주로 이렇게 사용됩니다.</p>
<ul>
<li>세션사용 : 로그인 정보유지</li>
</ul>
<h4 id="세션-프로세스">세션 프로세스</h4>
<ul>
<li>클라이언트는 서버접속시 세션 ID를 발급</li>
<li>클라이언트는 세션ID에 대해 쿠키를 사용해서 저장</li>
<li>서버요청시에 이 쿠키의 세션ID를 서버에 전달하여 사용</li>
<li>서버는 받은 세션ID로 세션의 클라이언트정보를 가져오고 서버요청을 처리하여 클라이언트에게    응답</li>
</ul>
<h3 id="4-쿠키과-세션의-차이">4. 쿠키과 세션의 차이</h3>
<h4 id="저장위치">저장위치</h4>
<ul>
<li>쿠키 : 클라이언트에 파일로 저장</li>
<li>세션 : 서버에 저장</li>
</ul>
<h4 id="보안">보안</h4>
<ul>
<li>쿠키 : 클라이언트 로컬에 저장되기 떄문에 변질되거나 request에서 스나이핑당할 우려가 있            어서 보안 취약</li>
<li>세션 : 쿠키를 이용해서 세션id만 저장하고 그것으로 구분해서 서버에서 처리하기 떄문에 비          교적 안전 (보안 면에서 쿠키보다 우수)</li>
</ul>
<h4 id="라이프-사이클">라이프 사이클</h4>
<ul>
<li>쿠키 : 지속쿠키에 경우 브라우저를 종료하더라도 저장 될수 있음</li>
<li>세션 : 만료기간을 정할수 있지만 브라우저 종료시 세션아이디 삭제될수 있음 </li>
</ul>
<h4 id="속도">속도</h4>
<ul>
<li>쿠키 : 쿠키에 정보가 있기 떄문에 서버에 요청시 속도가 빠름</li>
<li>세션 : 정보가 서버에 있기 떄문에 처리가 요구되어 비교적으로 느림</li>
</ul>
<h3 id="5-그럼-굳이-쿠키를-사용하는-이유">5. 그럼 굳이 쿠키를 사용하는 이유</h3>
<p>세션은 서버에 데이터를 저장 즉, 서버의 자원을 사용하기 때문에 서버 자원에 한계가 있고 메모리를 사용하다보면 속도 저하도 올 수 있기 때문이다.</p>
<h3 id="reference">Reference</h3>
<p>-<a href="https://velog.io/@hellozin/%EC%BF%A0%ED%82%A4-%EC%84%B8%EC%85%98-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EC%9B%B9-%EC%8A%A4%ED%86%A0%EB%A6%AC%EC%A7%80">쿠키-세션-그리고-웹-스토리지</a>
-<a href="https://jeong-pro.tistory.com/80">Web - 쿠키와 세션의 차이, 용도, 사용법(cookie,session)</a>
-<a href="https://velog.io/@stay136/%EA%B8%B0%EC%88%A0-%EC%BF%A0%ED%82%A4%EC%99%80-%EC%84%B8%EC%85%98%EC%9D%98-%EC%B0%A8%EC%9D%B4">기술-쿠키와-세션의-차이</a></p>
<h3 id="느낀점">느낀점</h3>
<blockquote>
<p>이제까지 쿠키와 세션에 대해 잘 모른체 마구잡이로 사용하였는데 보안과 속도 적인 측면을 생각하면 코딩하는 사람이 되도록 하여야 겠다!!</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Github Actions으로 오픈소스 Test 자동화]]></title>
            <link>https://velog.io/@minseok_yun/Github-Actions%EC%9C%BC%EB%A1%9C-%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4-Test-%EC%9E%90%EB%8F%99%ED%99%94</link>
            <guid>https://velog.io/@minseok_yun/Github-Actions%EC%9C%BC%EB%A1%9C-%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4-Test-%EC%9E%90%EB%8F%99%ED%99%94</guid>
            <pubDate>Sun, 22 May 2022 09:18:58 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>자동화에 관심이 많은 요즘 오픈소스 Test도 자동화하면 좋겠다는 생각에 만들게 되었습니다.</p>
</blockquote>
<h3 id="1-github에-최신상태-유지하기">1. Github에 최신상태 유지하기!!</h3>
<p>Github에 소스코드를 push하여 최신 상태로 유지한다.</p>
<br/>

<h3 id="2-테스트-자동화-설정">2. 테스트 자동화 설정!!</h3>
<p>Github repository에서 Action 메뉴를 클릭하고 New workflow 버튼을 클릭한다.
다음 set up a workflow yourself를 클릭한다.</p>
<p><img src="https://velog.velcdn.com/images/minseok_yun/post/d3a146c4-3492-464f-bc85-e7ddfa66c3dd/image.png" alt=""></p>
<h3 id="3-testyml-만들기">3. Test.yml 만들기!!</h3>
<p>파일에 대한 내용은 
Node 10, 12, 14, 15와 ubuntu, macOS, windows에서 테스트를 한다는 내용이다.</p>
<pre><code class="language-javascript">name: Test

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build:
    strategy:
      matrix:
        node-version: [10.x, 12.x, 14.x, 15.x]
        os: [ubuntu-latest, macOS-latest, windows-latest]
    runs-on: $

    steps:
      - uses: actions/checkout@v2
      - name: Use Node.js $
        uses: actions/setup-node@v1
        with:
          node-version: $
      # 테스트를 수행하기 위한 명령어를 작성한다.
      - run: npm install
      - run: npm test
</code></pre>
<p>파일 이름을 test로 변경하고 커밋을 한다.</p>
<p>이렇게 되면 main 브랜치에 push 및 pull_request를 할 때 마다 테스트를 수행한다.</p>
<p>수행 결과 및 내역은 Actions 메뉴를 통해 확인할 수 있습니다.</p>
<p><img src="blob:https://velog.io/19ab89ff-e4de-42a0-8975-7888e994dafa" alt="업로드중.."></p>
<p>(하필 다 빨강이네요...)</p>
<h3 id="😀-느낀점">😀 느낀점</h3>
<p>최근 자동화에 대한 중요성을 느끼게 된다.
자동화를 함으로써 시간을 절약하게되고 그게 코드의 퀄리티와 직결 된다는걸 새삼느끼는중이다.
자동화에 조금 더 빠져든거 같다 ㅎㅎ</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[GitHub Action으로 Next.js + Typescript 배포하기]]></title>
            <link>https://velog.io/@minseok_yun/GitHub-Action%EC%9C%BC%EB%A1%9C-Next.js-Typescript-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@minseok_yun/GitHub-Action%EC%9C%BC%EB%A1%9C-Next.js-Typescript-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 10 May 2022 02:37:22 GMT</pubDate>
            <description><![CDATA[<p>NextJs + Typescript 프로젝트를 gh-pages로 배포하던중 배포화를 자동화 시키면 좋겠다라는 생각이 들어 시도를 하면서 겪은 에러 및 진행 방법을 공유드립니다!!</p>
<h2 id="1-배포-방법">1. 배포 방법</h2>
<p>package.json 파일 안의 “script” 속성에 “deploy”하는 명령어를 추가한다.</p>
<pre><code class="language-js">//package.json

 &quot;scripts&quot;: {
    &quot;dev&quot;: &quot;next&quot;,
    &quot;build&quot;: &quot;next build&quot;,
    &quot;start&quot;: &quot;next start&quot;,
    &quot;deploy&quot;: &quot;rm -rf node_modules/.cache &amp;&amp; next build &amp;&amp; next export &amp;&amp; touch out/.nojekyll &amp;&amp; git add out/ &amp;&amp; git commit -m \&quot;Deploy Next.js to gh-pages\&quot; &amp;&amp; git subtree push --prefix out origin gh-pages&quot;
  },</code></pre>
<p>위의 명령어들을 정리해 보면</p>
<pre><code class="language-js">rm -rf node_modules/.cache  : node_modules 캐시제거

next build : next 프로젝트 빌드

next export : next 프로젝트를 static html앱으로 컴파일한 out/ 폴더를 생성해 줌

touch out/.nojekyll : Github page의 jekyll 처리과정에서 _next 이러한 파일을 특수 리소스로 간주하고 최종 사이트에 복사하지 않는데 .nojekyll 파일을 만들면 이를 막을 수 있다.

git add out/ : 상의 변경 내용을 스테이징 영역에 추가

git commit -m &quot;Deploy Next.js to gh-pages&quot; : 커밋메시지 작성 및 로컬 레파지에 추가

git subtree push --prefix out origin gh-pages : github 저장소 gh-pages브랜치에 push</code></pre>
<p>이 명령어 들을 가지고 있는 deploy를 실행시키면 배포가 자동으로 됩니다</p>
<pre><code class="language-js">npm run deploy</code></pre>
<br/>

<h2 id="2-nextconfigjs">2. next.config.js</h2>
<p>next.config.js 파일이 루트 폴더에 없다면 파일을 생성하고 다음과 같은 코드를 넣어준다. name은 자신의 깃허브 저장소 이름을 넣어준다.</p>
<pre><code class="language-js">//next.config.js

module.exports = {
  assetPrefix:
    process.env.NODE_ENV === &#39;production&#39;
      ? &#39;https://yunminseok.github.io/Mlog&#39;
      : &#39;&#39;,
};</code></pre>
<p>개발모드에서는 requestURL=localhost:3000/ 에 리소스를 요청하지만,</p>
<p>배포모드에는 requestURL = yunminseok.github.io/ 에 요청한다.</p>
<p>assetPrefix 속성을 통해 리소스 요청 주소를 따로 넣어주어야 한다.</p>
<br/>

<h2 id="2-env-configjs-babelrcjs">2. env-config.js, babelrc.js</h2>
<p>env-config.js 파일을 루트 폴더에 만들어주고 다음과 같은 코드를 친다.</p>
<pre><code class="language-js">// env-config.js

module.exports = {
  &#39;process.env.BACKEND_URL&#39;:
    process.env.NODE_ENV === &#39;production&#39;
      ? &#39;https://yoonminseok.github.io/Mlog&#39;
      : &#39;&#39;,
};</code></pre>
<p>이제부터 process.env.BACKEND_URL 라는 환경변수를 사용할 수 있다. 이 환경변수는 이미지, 동영상 등의 리소스에 접근하거나 사용자에게 보여지는 URL을 설정할 때 쓰인다. </p>
<br/>

<p>.babelrc.js 파일 안의 플러그인에 등록해준다.</p>
<pre><code class="language-js">// .babelrc.js

const env = require(&#39;./env-config&#39;);

module.exports = {
  presets: [&#39;next/babel&#39;],
  plugins: [
    [
      &#39;styled-components&#39;,
      {
        ssr: true,
        displayName: true,
        preprocess: false,
      },
    ],
    [&#39;transform-define&#39;, env],
  ],
};</code></pre>
<h2 id="3-이미지-리소스-접근">3. 이미지 리소스 접근</h2>
<p>위에서 만든 환경변수를 사용해서 process.env.BACKEND_URL 리소스에 접근해야 되지만 next/image를 사용해서인지 오류가 떠서 다른 방식으로 접근을 하였다.</p>
<pre><code class="language-js">//next.config.js

module.exports = {
  assetPrefix:
    process.env.NODE_ENV === &#39;production&#39;
      ? &#39;https://yoonminseok.github.io/Mlog&#39;
      : &#39;&#39;,
  images: {
    loader: &#39;imgix&#39;,
    path: &#39;https://yoonminseok.github.io/Mlog&#39;,
  },
};
</code></pre>
<p>이런식 으로 loader와 path값을 지정을 해주면 밑의 코드와 같이 사용가능하다</p>
<pre><code class="language-js">&lt;Image
  src={&#39;/Introduce.png&#39;}
  alt=&quot;Introduce_Logo&quot;
  width={400}
  height={400}
/&gt;
</code></pre>
<br/>

<h2 id="4-느낀점">4. 느낀점</h2>
<p>이렇게 Nextjs + Typescript + Gihub-action을 통해 Github pages로 배포하는 법을 알아봤다. 막상 시작할때는 설정부분이 너무 어려웠는데 구현 하고나니깐 npm run deploy명령 하나로 배포가 되는걸 보니 왜 개발자들이 자동화를 좋아하는지 알수 있는 작업이었다.</p>
<p>이제부터 자동화를 좋아하는 개발자가 되도록 노력해야겠다는 생각이 들었다</p>
<h3 id="reference">Reference</h3>
<p><a href="https://taeny.dev/javascript/nextjs-with-deployment-platform/">nextJS 뭘로 배포할까? (Netlify, Vercel, Github page)</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Next 배포시 Image Optimization 에러]]></title>
            <link>https://velog.io/@minseok_yun/Next-%EB%B0%B0%ED%8F%AC%EC%8B%9C-Image-Optimization-%EC%97%90%EB%9F%AC</link>
            <guid>https://velog.io/@minseok_yun/Next-%EB%B0%B0%ED%8F%AC%EC%8B%9C-Image-Optimization-%EC%97%90%EB%9F%AC</guid>
            <pubDate>Sat, 30 Apr 2022 07:39:26 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>최근에 NextJs + Typescript 를 gh-pages를 통해 배포 진행 중에 생긴 이미지 로더 에러를 파헤쳐 보았습니다.</p>
</blockquote>
<br/>

<h3 id="우선-에러-메시지를-살펴보자">우선 에러 메시지를 살펴보자</h3>
<pre><code class="language-js">Error: Image Optimization using Next.js&#39; default loader is not compatible with next export.
Possible solutions:
Use next start to run a server, which includes the Image Optimization API.
Use any provider which supports Image Optimization (like Vercel).
Configure a third-party loader in next.config.js.
Use the loader prop for next/image.</code></pre>
<p>해당 에러가 무슨 이유로 뜨는지 알아 보자</p>
<pre><code>next/Image에서 임포트하는 &lt;Image /&gt; 컴포넌트의 default loader가 
next export에서는 지원되지 않기 때문에 발생하는 에러다. </code></pre><br/>

<p> <a href="https://nextjs.org/docs/api-reference/next/image#built-in-loaders">공식문서</a>에 따르면 next.config.js 파일에 다음과 같은 설정을 추가하면 된다.</p>
<pre><code class="language-js"> module.exports = {
  images: {
    loader: &#39;imgix&#39;,
    path: [도메인 주소],
  },
};</code></pre>
<p>이런식으로 설정을 해주면 이미지의 기본 주소값이 설정이 되어 이미지가 잘 로딩이 된다.</p>
<h3 id="reference">Reference</h3>
<p><a href="https://nextjs.org/docs/api-reference/next/image">공식문서 - Vercel</a>
<a href="https://github.com/vercel/next.js/issues/21079">Github - Issue</a></p>
<h3 id="느낀점">느낀점</h3>
<blockquote>
<p>배포 환경을 구현중인데 생각했던것보다 설정 해줘야 할것들이 많고 신경 써줘야 할 부분들이 많은거 같아 힘들다 😂</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Default export vs Named export ]]></title>
            <link>https://velog.io/@minseok_yun/Default-export-vs-Named-export</link>
            <guid>https://velog.io/@minseok_yun/Default-export-vs-Named-export</guid>
            <pubDate>Sun, 24 Apr 2022 08:37:50 GMT</pubDate>
            <description><![CDATA[<h3 id="작성을-하기전">작성을 하기전</h3>
<blockquote>
<p>문득 코드를 보는데 default export와 named export가 엄청 혼용되어서 사용되고 있었다.
뭔가 코드가 지저분한 생각이 들어서 하나의 방식으로 통일하려고 했는데, 혹시나 성능차가 있을까 싶어서 둘의 특징과 차이점을 찾아봤다.</p>
</blockquote>
<br/>

<h3 id="1-default-export">1. Default Export</h3>
<p><a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/export">참고자료 MDN</a>
default 로 선언된 모듈은 하나의 파일에서 단 하나의 변수 또는 클래스 등등만 export 할 수 있습니다.</p>
<pre><code class="language-js">const Test = () =&gt; {
     ...
}
export default default Test</code></pre>
<p>! import 할 때는 아무 이름으로나 import 가능합니다.</p>
<h4 id="example">example</h4>
<pre><code class="language-js">import Test from &#39;../Test&#39;

or

import TestPage from &#39;../Test&#39;</code></pre>
<p>이런식으로 이름을 자유롭게 붙일수 있습니다.</p>
<h4 id="but"><strong>BUT!!</strong></h4>
<p>단, var, let, const 를 바로 export default 할 수 없습니다.</p>
<pre><code class="language-js">export default const MY_Test = &quot;This is Test&quot;</code></pre>
<br/>

<h3 id="2-named-export">2. Named Export</h3>
<p>한 파일 내에서 여러 변수/클래스 등등을 export 하는 것이 가능합니다.</p>
<p><a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/export">참고자료 MDN</a></p>
<pre><code class="language-js">export class FirstTestClass { /* blabla */ }
export class SecondTestClass { /* blabla */ }</code></pre>
<p>! 다만, import 시 {} 안에다가 export 된 이름과 동일하게 설정해야 한다.</p>
<h4 id="example-1">Example</h4>
<pre><code class="language-js">import { FirstTestClass, SecondTestClass } from &#39;./TestClass&#39;</code></pre>
<p>다른 이름으로 import 할 수 있으나 아래처럼 as를 사용해야 한다.</p>
<h4 id="example2">Example2</h4>
<ul>
<li>as 를 사용하면 한 파일에 있는 클래스/변수들을 한 번에 import 할 수 있다.</li>
</ul>
<pre><code class="language-js">import * as Hi from &#39;./TestClass&#39;</code></pre>
<h4 id="성능">성능</h4>
<pre><code>성능에는 차이가 없는 것으로 보입니다.</code></pre><br/>

<h3 id="🤔-그래서-어떤걸-써야할까">🤔 그래서 어떤걸 써야할까?</h3>
<blockquote>
<p>제 생각으로는 default export를 우선적으로 사용하고, 한 파일에서 여러개를 export 해야하는 경우는 named export를 사용하고 있다.
보통 한개의 파일이 하나의 함수형 컴포넌트나 클래스를 담고있기 때문에 default export를 디폴트로 사용해도 문제가 생기지 않았기 때문이다.
named export는 상수들을 담은 constants 등의 파일에 사용하는 경우가 많다.</p>
</blockquote>
<br/>

<h2 id="references">References</h2>
<p><a href="https://stackoverflow.com/questions/33305954/typescript-export-vs-default-export">StackOverflow</a></p>
<br/>

<h3 id="느낀점">느낀점</h3>
<p>개발은 이런식으로 하나씩 궁금점을 해결해 나가는 재미가 있는거 같다.
오늘보다 내일 더 나은 개발자가 되기 위해 달리자!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React.lazy와 Suspense 컴포넌트를 활용한 코드 스플리팅]]></title>
            <link>https://velog.io/@minseok_yun/React.lazy%EC%99%80-Suspense-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%EC%BD%94%EB%93%9C-%EC%8A%A4%ED%94%8C%EB%A6%AC%ED%8C%85</link>
            <guid>https://velog.io/@minseok_yun/React.lazy%EC%99%80-Suspense-%EC%BB%B4%ED%8F%AC%EB%84%8C%ED%8A%B8%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%EC%BD%94%EB%93%9C-%EC%8A%A4%ED%94%8C%EB%A6%AC%ED%8C%85</guid>
            <pubDate>Sun, 13 Mar 2022 07:39:24 GMT</pubDate>
            <description><![CDATA[<p>react v16.6 이후부터는 코드 스플리팅을 위한 내장 함수인 React.lazy와 Suspense 컴포넌트가 생겼습니다.</p>
<blockquote>
<h4 id="잠깐-코드-스플리팅이-뭘까">잠깐 코드 스플리팅이 뭘까?</h4>
<p>다른 곳에 여러 설명이 있지만 내가 이해한 대로 설명을 하자면 
<em>지금 당장 필요한 코드가 아니라면 따로 분리시켜서, 나중에 필요할때 불러와서 사용 할 수 있습니다. 이를 통하여, 페이지의 로딩 속도를 개선 할 수 있죠.</em></p>
</blockquote>
<p>React.lazy와 Suspense는 아직 서버 사이드 렌더링을 할 수 없다고 한다. 
서버에서 렌더링 된 앱에서 코드 분할을 하기 원한다면 Loadable Components를 사용하면 된다고 한다!!
자세한 설명은 <a href="https://ko.reactjs.org/docs/code-splitting.html">공식문서</a>를 참고하시면 됩니다!!</p>
<h3 id="☝-코드-스플리팅-하기-전">☝ 코드 스플리팅 하기 전</h3>
<h4 id="srcsplitjs">src/split.js</h4>
<pre><code class="language-javascript">function split() {
  &lt;div&gt;버튼을 눌러야 나타난다!!&lt;/div&gt;;
}

export default split;</code></pre>
<h4 id="srcappjs">src/App.js</h4>
<pre><code class="language-javascript">import React from &#39;react&#39;;
import split from &#39;./split&#39;;

function App() {
    const [visible, setVisible] = useState(false);
    return (
      &lt;div&gt;
        &lt;button
          onClick={() =&gt; {
            setVisible(true);
          }}
        &gt;
          버튼
        &lt;/button&gt;
        {visible ? &lt;split /&gt; : null}
      &lt;/div&gt;
    );
}

export default App;</code></pre>
<p>코드를 잘 보면 버튼을 누르게 되기 전까지 split 컴포넌트는 필요가 없습니다.
필요없는 코드들을 한번에 작성하게되면 초기화 로딩에 필요한 비용이 많이 들기 때문에 코드 스플리팅을 하는것입니다.</p>
<h3 id="✌-코드-스플리팅-후">✌ 코드 스플리팅 후</h3>
<pre><code class="language-javascript">import React, { Suspense, useState } from &quot;react&quot;;

const split = React.lazy(() =&gt; import(&quot;./split&quot;));

function App() {
  const [visible, setVisible] = useState(false);
  return (
    &lt;div&gt;
      &lt;button
        onClick={() =&gt; {
          setVisible(true);
        }}
      &gt;
        버튼
      &lt;/button&gt;
      &lt;Suspense fallback={&lt;div&gt;Loading...&lt;/div&gt;}&gt;
        {visible ? &lt;split /&gt; : null}
      &lt;/Suspense&gt;
    &lt;/div&gt;
  );
}

export default App;</code></pre>
<p>React.lazy를 통해 다이나믹 import로 사용할 컴포넌트를 가져옵니다.</p>
<p>그 후, Suspense 내부에서 해당 컴포넌트를 호출하면 됩니다.</p>
<p>Suspense의 fallback 속성은 해당 컴포넌트를 가져오는 데 걸리는 시간 동안 렌더할 일종의 로더를 지정할 수 있습니다.</p>
<h3 id="😎-후기">😎 후기</h3>
<p>Lazy를 적용해볼 일이 많이 없어 혼자서 예제를 만들어보니 정말 쓸만한 친구 인거 같다. 이렇게 최신 기술들이 나오면 시도 해보는 개발자가 되리라 다시 한번 다짐하는 하루였다...</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[React] Canvas에서 CORS 에러가 왠 말이야!!]]></title>
            <link>https://velog.io/@minseok_yun/Canvas%EC%97%90%EC%84%9C-CORS-%EC%97%90%EB%9F%AC%EA%B0%80-%EC%99%A0-%EB%A7%90%EC%9D%B4%EC%95%BC</link>
            <guid>https://velog.io/@minseok_yun/Canvas%EC%97%90%EC%84%9C-CORS-%EC%97%90%EB%9F%AC%EA%B0%80-%EC%99%A0-%EB%A7%90%EC%9D%B4%EC%95%BC</guid>
            <pubDate>Sun, 13 Feb 2022 08:08:28 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>최근 개발을 하면서 사진에 태그를 거는 기능을 구현 할 일이 있었다.
서버는 AWS S3를 쓰고 프론트 단은 localhost:3000 포트로 개발을 하고있었습니다. 
태그를 걸기위해 사진의 위치값을 찾을려고 getImageData()를 사용했으나 이러한 오류가 떳다.</p>
</blockquote>
<p><img src="https://images.velog.io/images/minseok_yun/post/027456b4-4934-4d80-994c-651f522de60b/image.png" alt=""></p>
<p>초보 개발자들을 지독하게 괴롭히는 CORS에러를 마주하게 되었다.
위의 에러는 CORS의 정책을 위반해서 나오는 에러라고 한다.
그렇다면 CORS 정책은 무엇일까?</p>
<h3 id="🤔-cors-관련-이슈는-왜-발생하는-걸까">🤔 CORS 관련 이슈는 왜 발생하는 걸까?</h3>
<p><img src="https://images.velog.io/images/minseok_yun/post/36f4afa9-0706-4f28-b9b1-8f77baa61fbc/image.png" alt=""></p>
<h4 id="관련-자료">관련 자료</h4>
<p><a href="https://developer.mozilla.org/ko/docs/Web/HTTP/CORS">교차 출처 리소스 공유(CORS)</a></p>
<h4 id="mdn의-내용을-요약하자면">MDN의 내용을 요약하자면</h4>
<ul>
<li>CORS는 서로 다른 출처(Origin)간에 리소스를 전달하는 방식을 제어하는 체제이다.</li>
<li>CORS 요청이 가능하려면 서버에서 특정 헤더인 Access-Control-Allow-Origin과 함께 응답할 필요가 있다.</li>
</ul>
<p>CORS 정책을 위반하여 서로 다른 출처를 가진 상태에서 무언가를 요청하게 되면 <strong><em>브라우저가 보안상의 이유로 차단</em></strong> 을 해버린다!
예를들어, 클라이언트 포트가 3000번이고 서버의 포트가 8080 일 때,
클라이언트에서 서버로 리소스를 요청했을 때 CORS 에러 메시지가 클라이언트 콘솔에 빨갛게 뜨고 데이터를 주지 않게 된다.</p>
<p>CORS문제를 해결하려면 동일한 출처에서 리소스를 요청하면 된다!!
<del><em>참 쉽죠?</em></del></p>
<blockquote>
<p>나는 여기에 한스푼 더 얹어서 Canvas에서 CORS에러가 왜 뜨는지 조금 더 보게 되었다.</p>
</blockquote>
<p>참고자료
<a href="https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image">Allowing cross-origin use of images and canvas</a></p>
<pre><code>Canvas를 사용할 때 CORS 승인 없이 이미지를 사용할 수는 있지만, 
이미지를 변형한 상태에서 데이터를 캔버스에서 꺼내려 한다면 
“canvas tainted” 관련 오류가 발생할 것이다.</code></pre><p>결국 같은 맥락의 글이었다.</p>
<p>따라서 CORS의 해결법은 찾았는데 2가지 정도를 대체적으로 많이 쓰는것 같아서 이를 정리 해보았다.</p>
<h3 id="1-이미지에-cross-origin-속성-설정">1. 이미지에 Cross Origin 속성 설정</h3>
<p>JavaScript 에서 이미지 자체에 Cross Origin 속성을 설정해야한다.</p>
<pre><code class="language-javascript">var img = new Image();
img.src = &quot;http://other-domain.com/image.jpg&quot;;
img.crossOrigin = &quot;Anonymous&quot;;</code></pre>
<p>딱히 좋은 방법은 아닌거 같았다.</p>
<h3 id="2-서버-헤더에-cross-origin-속성-설정">2. 서버 헤더에 Cross Origin 속성 설정</h3>
<p>가장 기본적인 방법으로는 서버에서 헤더를 설정해주는 것이다.</p>
<pre><code class="language-javascript">app.use((req, res, next) =&gt; {
  res.header(&quot;Access-Control-Allow-Origin&quot;, &quot;*&quot;); // 모든 도메인
  res.header(&quot;Access-Control-Allow-Origin&quot;, &quot;https://example.com&quot;); // 특정 도메인
});</code></pre>
<h3 id="3-프록시-설정해주기">3. 프록시 설정해주기</h3>
<p><a href="https://create-react-app.dev/docs/proxying-api-requests-in-development/">리액트 공식 문서</a> 에 프록시 설정방법이 나와있다.</p>
<p>package.json에 proxy 한 줄을 추가하면 된다!</p>
<pre><code class="language-javascript">//package.json
{
  ...,
  &quot;proxy&quot;: &quot;http://apiserver.com:5000&quot;,
  ...,
}</code></pre>
<p>그러면 다음과 같이 request가 처리된다.</p>
<p><img src="https://images.velog.io/images/minseok_yun/post/a46dba49-7069-431f-9957-5fbc57c65a8e/image.png" alt=""></p>
<ol>
<li>browser에서 React dev server(<a href="http://localhost:3000)%EC%9C%BC%EB%A1%9C">http://localhost:3000)으로</a> 요청을 보낸다.</li>
<li>React dev server가 해당 요청을 api server(<a href="http://apiserver.com:5000)%EC%97%90">http://apiserver.com:5000)에</a> 보낸다.</li>
<li>api server가 response를 react dev server에게 전달한다.</li>
<li>React dev server는 이 response를 그대로 browser에게 전달해준다.
이때, browser 입장에서는 api server가 아닌, React dev server가 응답한 것 처럼 보이게 된다.</li>
</ol>
<br/>

<blockquote>
<p>이렇게 해결 4가지 정도를 찾아 보았다. 
해결법은 내가 찾은것 보다 많지만 대체적으로 많이 쓰는것으로 작성을 하였다.  </p>
</blockquote>
<h3 id="느낀점">느낀점</h3>
<p>CORS에러가 뜨지 않았으면 놓치고 갔을 웹에서 가장 중요한 문제를 알게 되어서 기뻣다.
그리고 언어에 대한 기본 문법을 제외하고 웹에서는 공부해야 할게 너무 많다는것도 느끼게 되었다...😂</p>
<p>감사합니다🙏</p>
]]></description>
        </item>
    </channel>
</rss>