<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>bongbong velog</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Sun, 07 Jul 2024 06:14:25 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. bongbong velog. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/hi_young" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Next.js] 렌더링 성능 개선하기 (2)]]></title>
            <link>https://velog.io/@hi_young/Next.js-%EB%A0%8C%EB%8D%94%EB%A7%81-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0%ED%95%98%EA%B8%B0-2</link>
            <guid>https://velog.io/@hi_young/Next.js-%EB%A0%8C%EB%8D%94%EB%A7%81-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0%ED%95%98%EA%B8%B0-2</guid>
            <pubDate>Sun, 07 Jul 2024 06:14:25 GMT</pubDate>
            <description><![CDATA[<p>이전 편에서는 렌더링 방식을 변경하고 시각적으로 보이는 이슈들부터 개선하였는데요.
최적화가 필요한 부분은 크게 2가지가 더 있었습니다.</p>
<h1 id="문제점">문제점</h1>
<h2 id="tbt-지표를-높이는-blocking-요소들">TBT 지표를 높이는 Blocking 요소들</h2>
<p>TBT(Total Blocking Time)는 FCP(First Contentful Paint)와 TTI(Time to Interactive) 사이에서 50ms 이상이 걸리는 긴 작업들을 blocking time으로 간주해 더한 지표입니다. 즉, 사용자가 처음 페이지 콘텐츠를 본 후 마우스 클릭, 터치, 키보드 입력 등이 가능해지기까지의 시간을 측정한 값으로, Lighthouse에서 TBT에 가장 큰 가중치를 두고 있어 매우 중요한 지표 중 하나입니다.
<img src="https://velog.velcdn.com/images/hi_young/post/0615b5f5-2f57-4034-a4a3-9316d2b22059/image.png" alt=""></p>
<p>해당 지표가 500ms 이상 나오면서 어떤 부분에서 blocking 되고 있는지 분석이 필요하였습니다.</p>
<h2 id="느린-초기-페이지-렌더링">느린 초기 페이지 렌더링</h2>
<p>SSR(Server-Side Rendering)로 전환하면서 초기 HTML을 요청하고 받아오는 시간이 길어졌고, 네트워크의 서버 응답 대기 시간(network waiting for server response)이 많을 때는 7초까지 걸리는 문제가 있었습니다. 이로 인해 처음 페이지를 보는 것조차 오랜 시간이 걸렸습니다. 따라서 SSR 로직 최적화가 필요했습니다.</p>
<h1 id="해결-방법">해결 방법</h1>
<h2 id="bundle-analyzer를-사용한-번들-최적화">bundle-analyzer를 사용한 번들 최적화</h2>
<p>lightHouse에서는 TBT 지표를 향상시키기 위해서 Javascript 번들의 code splitting, 사용하지 않는 코드 제거, 효율적인 third-party script loading을 제안하고 있습니다.</p>
<p><a href="https://www.npmjs.com/package/@next/bundle-analyzer">next/bundle-analyzer</a>를 사용해서 js의 초기 번들 파일의 상태를 분석하였고 next.js의 dynamic import를 사용해서 code split 및 lazy load를 적용하였습니다.</p>
<h3 id="공통-번들-파일-최적화">공통 번들 파일 최적화</h3>
<p>First Load JS는 페이지가 처음 로드될 때 모든 페이지가 공통으로 로드하는 번들인데 _app.js의 사이즈가 378kB로 매우 큽니다.</p>
<pre><code>+ First Load JS shared by all               464 kB
  ├ chunks/framework-ce84985cd166733a.js    45.2 kB
  ├ chunks/main-e191f7fcdcea027b.js         35.7 kB
  ├ chunks/pages/_app-38596a77fcf1fc39.js   378 kB
  ├ chunks/webpack-8b927309e233a998.js      2.56 kB
  └ css/202272ed9e27e959.css                2.47 kB</code></pre><p><img src="https://velog.velcdn.com/images/hi_young/post/ae51f667-f887-415c-bc94-63a24647948a/image.png" alt=""></p>
<p>_app 파일의 내부를 들여다보면 lottie.js와 sentry가 큰 부분을 차지하고 있었습니다.</p>
<p>sentry 하나가 288kB로 매우 거대한 사이즈를 차지하고 있었고 추가 기능(integrations)으로 사용했던 reply와 breadbreaums를 제거해도 169kB였습니다. sentry가 사용되는 페이지는 초기 번들의 50% 이상을 sentry가 차지하고 있었습니다.</p>
<p>sentry issue에 해결방법을 찾아보았으나 lazy load, tree shaking을 지원하고 있지 않아 개발자들의 민심이 매우 좋지 않은 상황...
<img src="https://velog.velcdn.com/images/hi_young/post/6ac77af9-733e-4cad-8707-cb4f0313f544/image.png" alt=""></p>
<p><a href="https://github.com/getsentry/sentry-javascript/issues/7680">https://github.com/getsentry/sentry-javascript/issues/7680</a>
<a href="https://github.com/getsentry/sentry-javascript/issues/2707">https://github.com/getsentry/sentry-javascript/issues/2707</a></p>
<p>그래서 현재 회사 이슈로 웹프로젝트에는 sentry를 꼭 사용하지 않아도 되는 상황이었고 굳이 방대한 패키지로 성능에 지장을 줄 필요가 없다고 판단하여 잠시 sentry/nextjs를 내려두었습니다. 추후 에러 트래킹을 위해 성능감소를 안고 sentry를 사용해야하는지 다른 대체방안이 있을지 고민해봐야할 부분이었습니다.</p>
<p>그리고 react-lottie-player는 298kB로 거대한 사이즈를 보여주고 있었는데요. lottie-light 버전을 tree shaking으로 지원하는 lottie-web으로 대체했고 163kB 줄여 약 45% 감소시킬 수 있었습니다.</p>
<h3 id="페이지-번들-최적화">페이지 번들 최적화</h3>
<p><img src="https://velog.velcdn.com/images/hi_young/post/7aae135c-ca4b-4326-aaf3-2001ccff796d/image.png" alt="">
<strong>Filter to intial chunks</strong>에 원하는 페이지를 필터해서 초기 번들에 어떤 파일들이 포함되는지 확인할 수 있습니다. 이를 통해 초기에 필요하지 않은 컴포넌트나 라이브러리를 dynamic import를 적용하였습니다. </p>
<p>152kB의 ProfileOverView.tsx 내부에는 많은 컴포넌트들이 존재하는데 탭 전환을 통해 보여지는 영역들이 많아 첫번째 탭을 제외한 컴포넌트에 lazy load를 적용하기 적합하였습니다. 152kB -&gt; 13kB로 감소</p>
<p>lodash는 debounce 메서드만 사용하고 있었기 때문에 cherry-picking을 통해 처리하였습니다.</p>
<h2 id="third-party-script-비동기-로드">third party script 비동기 로드</h2>
<p>그리고 GA, GTM, Google ADS, 영상 광고 SDK 등 많은 thrid party script가 사용되고 있었기 때문에 defer를 추가하여 렌더링이 blocking되지 않도록 수정하였습니다.</p>
<h2 id="ssr-최적화">SSR 최적화</h2>
<p>Warning: data for page &quot;/profile/[platform]/[path]&quot; (path &quot;...&quot;) is 502 kB which exceeds the threshold of 128 kB, this amount of data can reduce performance.</p>
<p>해당 페이지에서는 일정 부분을 SSR로 서빙하기 위해 getServerSideProps 내에서 6개의 API를 호출하고 있습니다. promise.all을 사용해서 병렬적으로 처리되도록 개선했고, 사이즈가 큰 API에서는 필요한 데이터만 return 하도록 수정하였습니다.</p>
<h2 id="이미지-최적화">이미지 최적화</h2>
<p>index 페이지에는 lottie 및 큰 이미지들이 많이 사용되고 있었고 cdn 및 lazy load를 처리하여 최적화 하였습니다.</p>
<h1 id="결과">결과</h1>
<h3 id="lighthouse-desktop-performance-측정">lightHouse Desktop Performance 측정</h3>
<p><img src="https://velog.velcdn.com/images/hi_young/post/409f6cc7-f8fe-4e93-9041-795125c2bcce/image.png" alt=""></p>
<h3 id="lighthouse-mobile-performance-측정">lightHouse Mobile Performance 측정</h3>
<p><img src="https://velog.velcdn.com/images/hi_young/post/a2b34693-15f4-4e62-8e2d-3ac305619446/image.png" alt=""></p>
<p>SEO 개선을 위해 mobile performance 개선도 함께 진행했는데 점수 올리기가 힘들었습니다. 찾아보니 lightHouse는 모바일 성능 측정시 3G 환경으로 세팅해버리고 Desktop 환경보다 CPU, 네트워크 속도를 4배 느리게 throttling하여 측정하고 있어서 높은 점수를 얻기 힘든 것이었습니다. </p>
<p>해외 SEO 및 마케팅 회사의 블로그를 찾아보니 SEO로 유명한 웹사이트도 60대 중반대를 기록하고 있기에 일반적으로 40~50점대를 기록하는것도 굉장한 개선이라고 말해주고 있습니다.
<a href="https://www.seocomponent.com/blog/why-lighthouse-mobile-score-is-bad/">https://www.seocomponent.com/blog/why-lighthouse-mobile-score-is-bad/</a></p>
<h3 id="번들-사이즈">번들 사이즈</h3>
<ul>
<li><p>index page : 4.68MB -&gt; 289kB (약 94% 감소)</p>
</li>
<li><p>profile page : 717kB -&gt; 313kB (약 56% 감소)</p>
</li>
<li><p>First Load JS : 464kB -&gt; 232kB (약 50% 감소)</p>
<pre><code>// 개선 전
┌ ○ / (359 ms)                              4.13 MB        4.68 MB
├ λ /profile/[platform]/[path]              2.42 kB         717 kB
...</code></pre></li>
<li><p>First Load JS shared by all               464 kB
├ chunks/framework-ce84985cd166733a.js    45.2 kB
├ chunks/main-e191f7fcdcea027b.js         35.7 kB
├ chunks/pages/_app-38596a77fcf1fc39.js   378 kB
├ chunks/webpack-8b927309e233a998.js      2.56 kB</p>
</li>
</ul>
<p>// 개선 후
┌ ● / (ISR: 600 Seconds) (435 ms)           20 kB           289 kB
├ λ /profile/[platform]/[path]              11.1 kB         313 kB
...</p>
<ul>
<li>First Load JS shared by all               232 kB
├ chunks/framework-ce84985cd166733a.js    45.2 kB
├ chunks/main-e191f7fcdcea027b.js         35.7 kB
├ chunks/pages/_app-49bf831d0475e453.js   147 kB
└ chunks/webpack-c771521b231214a1.js      3.76 kB<pre><code></code></pre></li>
</ul>
<p>성능개선을 하면서 점수가 올라갈수록 빨라지는 페이지 전환과 깔끔한 렌더링을 보면서 뿌듯했고 재밌었습니다. 블로그로 정리하다보니 좀더 개선할 수 있는 부분이 있는데 놓친것 같아 출근하면 좀더 개선을 해봐야겠습니다.
불필요한 번들을 제거하는 것도 중요하지만 SSR로 많은 로직을 처리하면서 코드의 속도도 중요하다는걸 깨달았는데요. 코테 어려워서 손놓고 있었는데 다시 시작해야겠다는 생각이 듭니다.
앞으로는 주기적으로 성능테스트를 하며 관리를 해야겠습니다.</p>
<p>마지막으로 틀린 부분이 있거나 궁금한 것이 있다면 편하게 코멘트 부탁드려요!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Next.js Data Fetching]]></title>
            <link>https://velog.io/@hi_young/Next.js-Rendering</link>
            <guid>https://velog.io/@hi_young/Next.js-Rendering</guid>
            <pubDate>Mon, 24 Jun 2024 14:05:31 GMT</pubDate>
            <description><![CDATA[<p>| 이 포스팅은 Next.js 문서 번역 및 정리한 내용입니다.</p>
<p>기본적으로 Next.js는 모든 페이지를 pre-render한다. React를 단독으로 사용할 때 클라이언트측에서 JavaScript로 렌더링하는 대신 각 페이지에 대해 미리 HTML을 생성한다. pre-rendering을 사용하면 성능과 SEO가 향상될 수 있다.</p>
<p>생성된 각 HTML은 페이지를 위해 필요한 최소한의 JavaScript로 결합하게 되는데 이것을 Hydration이라고 부른다. 브라우저에서 페이지가 로드될 때, JavaScript가 실행되고 페이지를 인터렉티브하게 만든다. Hydration이 완료되어야 이벤트나 로직이 실행될 수 있다.</p>
<h3 id="pre-rendering">Pre-rendering</h3>
<p>Next.js에는 정적 생성과 서버 측 렌더링이라는 두 가지 형태의 pre-rendering이 있다. 차이점은 페이지의 HTML을 생성하는 시점에 있다. </p>
<ul>
<li>Static Generation : HTML은 빌드 시 생성되며 각 요청에서 재사용된다.</li>
<li>Server-side Rendering : 각 요청마다 HTML이 생성된다.</li>
</ul>
<p>Next.js는 각 페이지마다 사용할 pre-rendering 방식을 선택할 수 있다. 그래서 대부분의 페이지에서 Static Generation을 사용하고 다른 페이지는 Server-side Rendering을 사용해서 하이브리드 Next.js 앱을 만들 수 있다.</p>
<p>성능상의 이유로 Server-side Rendering 보다 Static Generation을 사용하는 것이 좋으며 성능을 높이기 위해 정적으로 생성된 페이지를 CDN으로 캐시할 수 있다. 하지만 몇몇 케이스에서 Server-side Rendering이 유리한 선택일 수 있다.</p>
<p>📎Next.js는 useEffect나 서버사이드 함수를 사용하지 않는 이상 기본적으로 SSG로 렌더링한다. </p>
<p>📎next.js는 기본적으로 pre-rendering을 지원한다. 프리렌더링은 정적 생성, 동적 생성 두 가지로 나뉘는데 정적 생성은 빌드 타임에 HTML을 생성하는 것이고 후자는 서버단에서 HTML을 생성하는 것이다. 브라우저에서 페이지를 로드할 때 js 파일을 받아와 실행시키고 pre-rendering된 HTML과 js 파일을 결합해서 사용자와 상호작용가능하도록 만드는 작업을 Hydration이라고 부르는데 마치 마른 대지에 물을 뿔려주는것같은 단어이다.</p>
<h1 id="data-fetching">Data Fetching</h1>
<h2 id="getstaticprops"><code>getStaticProps</code></h2>
<p>페이지에서 <code>getStaticProps</code> 함수를 반환하면 빌드 타임에 pre-rendering 한다.</p>
<h3 id="언제-getstaticprops을-사용해야-하나">언제 <code>getStaticProps</code>을 사용해야 하나?</h3>
<ul>
<li>페이지를 렌더링하는 데 필요한 데이터가 빌드 타임에 제공된다.</li>
<li>CMS로부터 데이터를 받는다.</li>
<li>페이지는 사전 렌더링 되어야 하고 매우 빠르게 서빙되어야 한다. 성능을 위해 CDN에 의해 캐시될 수 있는 HTML, JSON 파일을 생성한다.</li>
<li>특정 상황에서는 미들웨어를 사용하여 경로를 우회하여 사용자별로 다른 페이지를 보여줄수도 있다.</li>
</ul>
<h3 id="getstaticprops는-언제-실행되나요"><code>getStaticProps</code>는 언제 실행되나요?</h3>
<p><code>getStaticProps</code>는 항상 서버에서 실행되고 클라이언트에서는 실행되지 않는다. getStaticProps로 작성한 코드는 클라이언트측 번들에서 제거된다.</p>
<ul>
<li><code>getStaticProps</code>는 항상 <code>next build</code>하는 동안 실행된다.</li>
<li><code>getStaticProps</code>는 <code>fallback:true</code>를 사용했을 때 백그라운드에서 실행된다.</li>
<li><code>getStaticProps</code>는 <code>fallback:blocking</code>을 사용할 때 초기 렌더링 전에 호출된다.</li>
<li><code>getStaticProps</code>는 revalidate를 사용할 때 백그라운드에서 실행된다.</li>
</ul>
<p>ISR과 결합해서 사용할 때, <code>getStaticProps</code>는 stale page가 revalidate되는 동안 백그라운드에서 실행되고 새로 생성된 페이지를 브라우저에 제공한다.</p>
<p><code>getStaticProps</code>는 정적 HTML을 생성하기 때문에 클라이언트의 쿼리 파라미터, HTTP Header에 접근할 수 없다. 이러한 데이터에 접근하려면 미들웨어 사용을 고려해야한다.</p>
<h3 id="server-side-code-직접-작성하기">server-side code 직접 작성하기</h3>
<p><code>getStaticProps</code>는 서버측에서 실행되므로 클라이언측에서 실행되지 않고 직접 데이터베이스 쿼리를 작성할 수 있다.</p>
<p>즉 <code>getStaticProps</code>에서 API route를 가져오는 대신 서버측 코드를 직접 작성할 수 있다. </p>
<p><strong>그리고 <code>getStaticProps</code>에서 API Routes로 직접 호출하는 것은 서버 내에서 추가 호출이 발생하기 때문에 성능이 저하된다. API Routes 없이 직접 데이터를 가져오는 것이 더 효율적이다.</strong></p>
<h3 id="html--json-정적-생성">HTML &amp; JSON 정적 생성</h3>
<p><code>getStaticProps</code>로 pre-rendering된 페이지는 HTML 외에 JSON 파일도 생성한다.</p>
<p>이 JSON 파일은 <code>next/link</code> 또는 <code>next/router</code>를 통한 클라이언트 측 라우팅에 사용된다. <code>getStaticProps</code>를 사용해서 프리렌더링된 페이지로 이동할 때, Next.js는 JSON 파일을 fetch하고 page component의 props로 받아 사용한다. 이는 export한 JSON만 오직 사용하기 때문에 getStaticProps를 호출하지 않는다는 것을 의미한다.</p>
<p>ISR을 사용하는 경우, <code>getStaticProps</code>는 클라이언트측 navigation을 위해 필요한 JSON을 백그라운드에서 생성한다.</p>
<p>✍️ <code>getStaticProps</code>는 빌드 타임에 실행되는 함수이며 이때 JSON으로 반환된다. 그래서 블로그나 랜딩 페이지처럼 정적인 데이터를 표시하는 페이지에서 사용하면 빌드 시점에 이미 HTML로 생성되어 있기 때문에 매우 빠르게 사용자에게 렌더링할 수 있다. 그런데 ISR로 생성하는 경우 fallback 옵션에 따라 서버사이드에서 다시 렌더링되기도 한다. </p>
<h2 id="getstaticpath">getStaticPath</h2>
<p>페이지에 동적 경로가 있고 <code>getStaticProps</code>를 사용하는 경우 정적으로 생성될 경로 목록들을 정의해줘야 한다.</p>
<p>동적 경로를 사용하는 페이지에서 <code>getStaticPaths</code> 함수를 내보내면 지정된 모든 경로를 pre-rendering 한다.</p>
<h3 id="언제-getstaticpaths를-사용하나">언제 <code>getStaticPaths</code>를 사용하나?</h3>
<p>동적 라우팅을 사용하면서 정적으로 pre-rendering을 하고 싶을 때 사용한다.</p>
<ul>
<li>headless CMS로부터 제공되는 데이터</li>
<li>database로부터 제공되는 데이터</li>
<li>file system에서 제공하는 데이터</li>
<li>캐시될 수 있는 데이터</li>
<li>페이지가 pre-rendering(for SEO) 되어야 하며 빨라야한다.</li>
</ul>
<h3 id="언제-getstaticpaths가-실행되나">언제 <code>getStaticPaths</code>가 실행되나?</h3>
<p>빌드 타임에 실행되기 때문에 runtime는 호출되지 않는다. 즉 client-side 번들에는 해당 소스가 포함되지 않는다.</p>
<h3 id="getstaticpaths는-어디에서-사용하나"><code>getStaticPaths</code>는 어디에서 사용하나?</h3>
<ul>
<li><code>getStaticProps</code>와 반드시 함께 사용해야한다.</li>
<li><code>getServerSideProps</code>에는 사용할 수 없다.</li>
<li>page file에서만 사용할 수 있다.</li>
</ul>
<h3 id="fallback-옵션">fallback 옵션</h3>
<p>빌드 시에 미리 생성되지 않은 경로에 사용자가 접근했을 때 어떻게 동작할 것인지 결정하는 옵션</p>
<ul>
<li><code>fallback: false</code> : 빌드 시 지정한 경로 외의 모든 경로는 404 페이지로 처리</li>
<li><code>fallback: true</code> : 백그라운드에서 동적으로 HTML을 생성하고 이 동안 사용자는 로딩 상태를 보게 된다.</li>
<li><code>fallback: blocking</code> : 백그라운드에서 동적으로 HTML을 생성하고, 이 동안 사용자가 기다렸다가 페이지를 서빙받는다.</li>
</ul>
<p>✍️ <code>getStaticPaths</code>는 빌드 시점에 렌더링을 할 건데 동적인 경로들을 가질 때 사용하는 함수이다. fallback 옵션을 통해 지정한 경로만 허용할 것인지, 동적으로 다시 생성할 것인지, 이 과정에서 로딩을 보여줄 것인지 말것인지 정할 수 있다. 그리고 많은 양의 동적 경로를 생성하게 되면 빌드 시간이 지연되게 되는데 이는 아래처럼 path를 대괄호 처리해서 요청 시점에 생성하도록 해결할수도 있다.</p>
<pre><code class="language-jsx">export async function getStaticPaths() {
  // When this is true (in preview environments) don&#39;t
  // prerender any static pages
  // (faster builds, but slower initial page load)
  if (process.env.SKIP_BUILD_STATIC_GENERATION) {
    return {
      paths: [],
      fallback: &#39;blocking&#39;,
    }
  }</code></pre>
<h2 id="getserversideprops">getServerSideProps</h2>
<p>요청 시점에 데이터를 가져오고 페이지의 내용을 렌더링하는 데 사용할 수 있는 Next.js 함수</p>
<pre><code class="language-tsx">import type { InferGetServerSidePropsType, GetServerSideProps } from &#39;next&#39;

type Repo = {
  name: string
  stargazers_count: number
}

export const getServerSideProps = (async () =&gt; {
  // Fetch data from external API
  const res = await fetch(&#39;https://api.github.com/repos/vercel/next.js&#39;)
  const repo: Repo = await res.json()
  // Pass data to the page via props
  return { props: { repo } }
}) satisfies GetServerSideProps&lt;{ repo: Repo }&gt;

export default function Page({
  repo,
}: InferGetServerSidePropsType&lt;typeof getServerSideProps&gt;) {
  return (
    &lt;main&gt;
      &lt;p&gt;{repo.stargazers_count}&lt;/p&gt;
    &lt;/main&gt;
  )
}</code></pre>
<h3 id="언제-getserversideprops를-사용하면-좋을까">언제 <code>getServerSideProps</code>를 사용하면 좋을까?</h3>
<p>개인화된 사용자 요청 데이터, 요청 시점에 최신 데이터를 보여줘야하는 경우</p>
<h3 id="behavior">Behavior</h3>
<ul>
<li><code>getServerSideProps</code>는 서버에서 실행된다.</li>
<li><code>getServerSideProps</code>는 오직 페이지에서만 사용할 수 있다.</li>
<li><code>getServerSideProps</code>는 JSON을 반환한다.</li>
<li>사용자가 페이지를 방문하면 <code>getServerSideProps</code>가 요청 시점에 데이터를 가져와 초기 HTML을 렌더링한다.</li>
<li>페이지 컴포넌트에 전달된 props는 초기 HTML 일부로 클라이언트에서 볼 수 있기때문에 민감한 정보는 props에 포함하지 않도록 주의해야한다.</li>
<li>사용자가 &#39;next/link&#39;나 &#39;next/router&#39;를 통해 페이지를 방문하면, Next.js는 서버에 API 요청을 보내고 이 요청에서 <code>getServerSideProps</code>가 실행된다. -&gt; 해당 객체를 통해 서버에서 페이지를 렌더링해서 다음 페이지를 보여주는 것이기 때문에 <code>&lt;a&gt;</code> 링크나 window.location 사용시 화면 전환시 깜빡거리는 현상이 발생한다. </li>
<li><code>getServerSideProps</code>를 사용할 때 CMS, DB를 직접 호출할 수 있다.</li>
</ul>
<h3 id="error-handling">Error handling</h3>
<p><code>getServerSideProps</code> 내부에서 오류 발생시 <code>pages/500.js</code> 파일이 표시된다.</p>
<h3 id="caching-with-server-side-rendering">Caching with Server-Side Rendering</h3>
<p>caching header 설정을 통해 캐싱할 수 있다. cache-control을 직접 제어하기 전에 ISR이 더 적합한 방법인지 고려해보는 것을 추천한다.</p>
<pre><code class="language-jsx">export async function getServerSideProps({ req, res }) {
  res.setHeader(
    &#39;Cache-Control&#39;,
    &#39;public, s-maxage=10, stale-while-revalidate=59&#39;
  )

  return {
    props: {},
  }
}</code></pre>
<h1 id="csrclient-side-rendering">CSR(Client side Rendering)</h1>
<p>브라우저가 로드된 후 Javascript 파일을 실행시켜서 HTML을 만드는 방식이다. 초기 실행시 사용자가 페이지를 보기까지 시간이 걸릴 수 있다는 단점이 있으나 그 뒤부터는 페이지 이동시 깜빡임 없는 부드러운 화면전환을 제공한다는 장점이 있다. </p>
<p>next.js에서 CSR을 사용하는 방법은</p>
<ol>
<li><code>useEffect()</code></li>
<li><code>SWR</code> or <code>TanStack Query</code>와 같은 라이브러리</li>
</ol>
<p>CSR로만 렌더링을 하게 되면 SEO에 불리하다는 단점이 있고 초기 사용자가 페이지를 보기까지 시간이 걸리기 때문에 CSR과 SSR을 결합하여 하이브리드로 사용하기를 장려하고 있다.</p>
<h1 id="isrincremental-static-regeneration">ISR(Incremental Static Regeneration)</h1>
<p>사이트를 빌드한 후에도 정적 페이지를 생성하거나 업데이트할 수 있게 해준다. </p>
<ul>
<li>Good to know : edge runtime은 ISR과 호환되지 않는다. 캐싱이 필요하면 직접 cache-control 헤더를 설정하여 stale-while-revalidate를 사용해야 한다.</li>
</ul>
<p>ISR을 사용하려면 <code>getStaticProps</code>에 revalidate props를 추가한다.</p>
<pre><code class="language-jsx">function Blog({ posts }) {
  return (
    &lt;ul&gt;
      {posts.map((post) =&gt; (
        &lt;li key={post.id}&gt;{post.title}&lt;/li&gt;
      ))}
    &lt;/ul&gt;
  )
}

// 이 함수는 서버 측에서 빌드 시에 호출.
// 새 요청이 들어오면 revalidate가 활성화된 경우 서버리스 함수에서 다시 호출될 수 있다.
export async function getStaticProps() {
  const res = await fetch(&#39;https://.../posts&#39;)
  const posts = await res.json()

  return {
    props: {
      posts,
    },
    revalidate: 10,
  }
}

export async function getStaticPaths() {
  const res = await fetch(&#39;https://.../posts&#39;)
  const posts = await res.json()

  // 포스트를 기반으로 사전 렌더링할 경로를 가져온다.
  const paths = posts.map((post) =&gt; ({
    params: { id: post.id },
  }))

  // 이 경로들만 빌드 시에 사전 렌더링.
  // { fallback: &#39;blocking&#39; }은 경로가 존재하지 않는 경우 요청 시에 페이지를 서버 렌더링한다.
  return { paths, fallback: &#39;blocking&#39; }
}

export default Blog</code></pre>
<ul>
<li>초기 요청 후 10초 이내의 모든 요청은 즉시 캐시된다.</li>
<li>10초가 지난 후 다음 요청도 캐시된 페이지가 표시된다.</li>
<li>Next.js는 백그라운드에서 페이지를 다시 생성한다.</li>
<li>페이지 생성에 성공하면 캐시를 무효화하고 업데이트된 페이지를 표시한다.</li>
<li>백그라운드에서 재생성에 실패하면 여전히 이전 페이지를 보여준다.</li>
</ul>
<p>Good to know : CDN 캐싱이 활성화되어있는지 확인해야한다.  </p>
<h1 id="reference">Reference</h1>
<ul>
<li><a href="https://nextjs.org/docs/pages/building-your-application/data-fetching">https://nextjs.org/docs/pages/building-your-application/data-fetching</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Next.js API Routes 어떻게 활용하면 좋을까?]]></title>
            <link>https://velog.io/@hi_young/Next.js-API-Routes-%EC%96%B4%EB%96%BB%EA%B2%8C-%ED%99%9C%EC%9A%A9%ED%95%98%EB%A9%B4-%EC%A2%8B%EC%9D%84%EA%B9%8C</link>
            <guid>https://velog.io/@hi_young/Next.js-API-Routes-%EC%96%B4%EB%96%BB%EA%B2%8C-%ED%99%9C%EC%9A%A9%ED%95%98%EB%A9%B4-%EC%A2%8B%EC%9D%84%EA%B9%8C</guid>
            <pubDate>Sat, 22 Jun 2024 06:35:43 GMT</pubDate>
            <description><![CDATA[<h1 id="api-routes">API Routes</h1>
<p>Next.js로 public API를 구축할 수 있는 솔루션을 제공</p>
<p><code>pages/api</code> 폴더 내의 모든 파일은 <code>/api/*</code>에 매핍되어 페이지 대신 API 엔드포인트로 처리되며 이는 서버 측 전용 번들이며 클라이언트 측 번들 크기를 늘리지 않는다.</p>
<pre><code class="language-tsx">import type { NextApiRequest, NextApiResponse } from &#39;next&#39;

type ResponseData = {
  message: string
}

export default function handler(
  req: NextApiRequest,
  res: NextApiResponse&lt;ResponseData&gt;
) {
  res.status(200).json({ message: &#39;Hello from Next.js!&#39; })
}</code></pre>
<h3 id="특징">특징</h3>
<ul>
<li>기본적으로 Next.js의 API는 같은 웹사이트에서만 접근할 수 있다.(same-origin only)</li>
<li>다른 웹사이트에서도 접근 가능하게 하려면 CORS 설정을 해줘야 한다.(CORS request helper로 가능)</li>
<li>통합된 프로젝트 구조로 프론트엔드와 백엔드 코드가 동일한 프로젝트 내에 있어 코드 관리가 용이하다.</li>
<li>별도의 서버 설정 없이 API 구축이 가능하다</li>
<li>데이터베이스, 외부 API 등 다양한 소스에서 데이터를 가져오거나 처리할 수 있다.</li>
<li>간단한 데이터 처리에는 적합하지만 복잡한 비즈니스 로직이나 대규모 애플리케이션에 한계가 있다.</li>
<li>높은 트래픽을 처리할 때 서버 성능 저하가 발생할 수 있다.</li>
</ul>
<h2 id="http-methods">HTTP Methods</h2>
<p>API 경로에서 다양한 HTTP 메소드를 처리하려면 다음과 같이 요청 핸들러에서 req.method를 사용할 수 있다.</p>
<pre><code class="language-tsx">import type { NextApiRequest, NextApiResponse } from &#39;next&#39;

export default function handler(req: NextApiRequest, res: NextApiResponse) {
  if (req.method === &#39;POST&#39;) {
    // Process a POST request
  } else {
    // Handle any other HTTP method
  }
}</code></pre>
<h2 id="request-helpers">Request Helpers</h2>
<p>API Routes는 들어오는 요청(req)을 파싱하는 request helpers를 제공한다.</p>
<ul>
<li>req.cookies : reqeust에 의해 전송된 쿠기를 포함하는 객체</li>
<li>req.query : url의 query string</li>
<li>req.body : <code>content-type</code>에 따라 파싱된 body</li>
</ul>
<h2 id="response-helpers">Response Helpers</h2>
<p>Server Response object는 개발자 경험을 개선하고 새로운 API 엔드포인트를 더 빨리 생성할 수 있도록 하기 위해 express.js와 유사한 여러 helper methods를 포함한다.</p>
<ul>
<li>res.status(code) : HTTP status code를 설정한다. code는 유효한 HTTP 상태 코드여야한다.</li>
<li>res.json(body) : JSON 응답을 보낸다. body는 직렬화 가능한 객체여야 한다.</li>
<li>res.send(body) : HTTP 응답을 보낸다. body는 문자열, 객체 or buffer일 수 있다.</li>
<li>res.redirect([status,] path) : 지정된 경로 또는 URL로 리디렉션한다. </li>
<li>res.revalidate(urlPath) : <code>getStaticProps</code>를 사용하여 페이지를 다시 검증한다. </li>
</ul>
<h1 id="api-routes-어떻게-활용하면-좋을까">API Routes 어떻게 활용하면 좋을까?</h1>
<h3 id="1-form-validation">1. Form validation</h3>
<p>API Routes는 클라이언트와 서버 간의 데이터를 주고받는 데 유용. 예를 들어, 사용자로부터 폼 데이터를 받아 서버에서 처리할 때 사용할 수 있다.</p>
<p>데이터 무결성 보장: 서버 측 검증은 클라이언트가 어떤 환경에서든지 잘못된 데이터를 서버에 전송하지 못하도록 합니다. 이는 데이터베이스에 저장되는 데이터의 품질과 무결성을 보장하는 데 중요합니다</p>
<pre><code class="language-js">// pages/api/submit.js
import type { NextApiRequest, NextApiResponse } from &#39;next&#39;
import { z } from &#39;zod&#39;

const schema = z.object({
  // ...
})

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  const parsed = schema.parse(req.body)
  // ...
}
</code></pre>
<h3 id="2-외부-서비스-url-숨기기">2. 외부 서비스 URL 숨기기</h3>
<p>API 라우트를 사용하면 클라이언트 사이드에서 바로 외부 API를 호출하지 않고, 내 서버를 거치게 하여 외부 서비스의 URL이나 키 같은 민감한 정보를 숨길 수 있습니다. 이는 보안상의 이점을 갖습니다.</p>
<p>외부 API를 숨기는 것은 보안과 데이터 보호 측면에서 여러 가지 이점을 제공합니다. Next.js API Routes를 사용하여 외부 API 호출을 서버 측에서 처리함으로써 다음과 같은 상황에서 이점을 얻을 수 있습니다:</p>
<p>API 키 보호:</p>
<p>상황: 여러분의 애플리케이션이 외부 서비스(API)를 사용해야 하고, 이 서비스에 접근하기 위해 API 키가 필요하다면, 이 API 키를 클라이언트 코드에 직접 포함시키는 것은 위험할 수 있습니다. 사용자가 API 키를 쉽게 추출할 수 있기 때문입니다.
이점: Next.js API Routes에서 외부 API를 호출함으로써 API 키를 서버 측에 안전하게 보관할 수 있습니다. 클라이언트는 API 키를 전혀 알 필요가 없습니다.
요청 및 응답 변환:</p>
<p>상황: 외부 API의 응답 형식이 클라이언트 애플리케이션에 적합하지 않을 수 있습니다. 예를 들어, 외부 API가 너무 많은 데이터를 반환하거나 필요한 형식으로 데이터를 반환하지 않는 경우가 있습니다.
이점: Next.js API Routes에서 외부 API의 응답을 변환하여 클라이언트가 필요한 데이터만 전달할 수 있습니다. 이를 통해 데이터의 크기를 줄이고 클라이언트의 처리 부담을 줄일 수 있습니다.
보안 및 접근 제어:</p>
<p>상황: 특정 사용자만 외부 API에 접근할 수 있도록 제한해야 하는 경우가 있습니다.
이점: Next.js API Routes에서 사용자 인증 및 권한 검사를 수행한 후에 외부 API를 호출할 수 있습니다. 이를 통해 인증되지 않은 사용자가 외부 API에 접근하는 것을 방지할 수 있습니다.</p>
<h3 id="3-데이터-필터링-및-가공">3. 데이터 필터링 및 가공</h3>
<p>서버에서 데이터 변환이나 처리가 필요한 경우 API Routes를 사용할 수 있습니다. 예를 들어, 외부 API로부터 데이터를 가져와서 클라이언트에 맞게 변환한 후 제공할 수 있습니다.</p>
<p>클라이언트에서 직접 외부 API를 호출하는 대신, 서버에서 API 요청을 중계하고 응답 데이터를 필터링하거나 가공하여 클라이언트에 전달할 수 있습니다. 이는 네트워크 트래픽을 줄이고, 클라이언트 애플리케이션의 성능을 향상시킬 수 있습니다.</p>
<pre><code class="language-js">// pages/api/external-data.js
export default async (req, res) =&gt; {
  const response = await fetch(&#39;https://api.example.com/data&#39;);
  const data = await response.json();

  // 데이터 변환
  const transformedData = data.map(item =&gt; ({
    id: item.id,
    name: item.name.toUpperCase(),
  }));

  res.status(200).json(transformedData);
};</code></pre>
<h3 id="4-인증-및-권한-관리">4. 인증 및 권한 관리</h3>
<p>API Routes를 통해 사용자 인증 및 권한 관리를 구현할 수 있습니다. 예를 들어, 토큰을 검증하여 사용자가 인증된 상태인지 확인할 수 있습니다.
사용자 인증: 사용자의 로그인 요청을 처리하기 위해 API 라우트를 사용하여 서버측에서 사용자의 인증을 처리하고, 인증 토큰을 클라이언트에 전달할 수 있습니다.</p>
<pre><code class="language-js">// pages/api/protected.js
import jwt from &#39;jsonwebtoken&#39;;

export default (req, res) =&gt; {
  const { authorization } = req.headers;

  if (!authorization) {
    return res.status(401).json({ message: &#39;No token provided&#39; });
  }

  try {
    const decoded = jwt.verify(authorization.split(&#39; &#39;)[1], &#39;secret-key&#39;);
    res.status(200).json({ message: &#39;Authenticated&#39;, user: decoded });
  } catch (error) {
    res.status(401).json({ message: &#39;Invalid token&#39; });
  }
};
</code></pre>
<h3 id="5-서버-측-기능-제공">5. 서버 측 기능 제공</h3>
<p>서버 측에서만 사용할 수 있는 특정 기능(예: 파일 시스템 접근, 데이터베이스 쿼리 등)을 API Routes를 통해 제공할 수 있습니다.</p>
<pre><code class="language-js">// pages/api/upload.js
import formidable from &#39;formidable&#39;;

export default (req, res) =&gt; {
  if (req.method === &#39;POST&#39;) {
    const form = new formidable.IncomingForm();
    form.parse(req, (err, fields, files) =&gt; {
      if (err) {
        return res.status(500).json({ message: &#39;File upload error&#39; });
      }

      // 예: 파일 처리
      // const file = files.file;
      // fs.renameSync(file.path, `/uploads/${file.name}`);

      res.status(200).json({ message: &#39;File uploaded successfully&#39; });
    });
  } else {
    res.status(405).json({ message: &#39;Method not allowed&#39; });
  }
};</code></pre>
<h3 id="6-cors-우회">6. CORS 우회</h3>
<h3 id="api-routes를-serverside에서-호출하는-것을-성능상-비효율적">API Routes를 serverside에서 호출하는 것을 성능상 비효율적.</h3>
<h1 id="reference">Reference</h1>
<ul>
<li><a href="https://velog.io/@rkd028/Next-js-API-Route-%EC%8B%AD%EB%B6%84-%ED%99%9C%EC%9A%A9%ED%95%98%EA%B8%B0">https://velog.io/@rkd028/Next-js-API-Route-%EC%8B%AD%EB%B6%84-%ED%99%9C%EC%9A%A9%ED%95%98%EA%B8%B0</a></li>
<li><a href="https://rocketengine.tistory.com/entry/NextJS-13-Routing-Route-Handlers-%ED%99%9C%EC%9A%A9%EB%B0%A9%EC%95%88">https://rocketengine.tistory.com/entry/NextJS-13-Routing-Route-Handlers-%ED%99%9C%EC%9A%A9%EB%B0%A9%EC%95%88</a></li>
<li><a href="https://nextjs.org/docs/pages/building-your-application/data-fetching/forms-and-mutations">https://nextjs.org/docs/pages/building-your-application/data-fetching/forms-and-mutations</a></li>
<li><a href="https://www.dhiwise.com/post/getting-started-with-next-js-api-routes-a-comprehensive-guide">https://www.dhiwise.com/post/getting-started-with-next-js-api-routes-a-comprehensive-guide</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Next.js Rendering]]></title>
            <link>https://velog.io/@hi_young/Next.js-ISR</link>
            <guid>https://velog.io/@hi_young/Next.js-ISR</guid>
            <pubDate>Sun, 16 Jun 2024 13:48:16 GMT</pubDate>
            <description><![CDATA[<p>이 포스팅은 Next.js 문서 번역 및 정리한 내용입니다.</p>
<p>기본적으로 Next.js는 모든 페이지를 pre-render한다. React를 단독으로 사용할 때 클라이언트측에서 JavaScript로 렌더링하는 대신 각 페이지에 대해 미리 HTML을 생성한다. pre-rendering을 사용하면 성능과 SEO가 향상될 수 있다.</p>
<p>생성된 각 HTML은 페이지를 위해 필요한 최소한의 JavaScript로 결합하게 되는데 이것을 Hydration이라고 부른다. 브라우저에서 페이지가 로드될 때, JavaScript가 실행되고 페이지를 인터렉티브하게 만든다. Hydration이 완료되어야 이벤트나 로직이 실행될 수 있다.</p>
<h3 id="pre-rendering">Pre-rendering</h3>
<p>Next.js에는 정적 생성과 서버 측 렌더링이라는 두 가지 형태의 pre-rendering이 있다. 차이점은 페이지의 HTML을 생성하는 시점에 있다. </p>
<ul>
<li>Static Generation : HTML은 빌드 시 생성되며 각 요청에서 재사용된다.</li>
<li>Server-side Rendering : 각 요청마다 HTML이 생성된다.</li>
</ul>
<p>Next.js는 각 페이지마다 사용할 pre-rendering 방식을 선택할 수 있다. 그래서 대부분의 페이지에서 Static Generation을 사용하고 다른 페이지는 Server-side Rendering을 사용해서 하이브리드 Next.js 앱을 만들 수 있다.</p>
<p>성능상의 이유로 Server-side Rendering 보다 Static Generation을 사용하는 것이 좋으며 성능을 높이기 위해 정적으로 생성된 페이지를 CDN으로 캐시할 수 있다. 하지만 몇몇 케이스에서 Server-side Rendering이 유리한 선택일 수 있다.</p>
<p>📎Next.js는 useEffect나 서버사이드 함수를 사용하지 않는 이상 기본적으로 SSG로 렌더링한다. </p>
<p>📎next.js는 기본적으로 pre-rendering을 지원한다. 프리렌더링은 정적 생성, 동적 생성 두 가지로 나뉘는데 정적 생성은 빌드 타임에 HTML을 생성하는 것이고 후자는 서버단에서 HTML을 생성하는 것이다. 브라우저에서 페이지를 로드할 때 js 파일을 받아와 실행시키고 pre-rendering된 HTML과 js 파일을 결합해서 사용자와 상호작용가능하도록 만드는 작업을 Hydration이라고 부르는데 마치 마른 대지에 물을 뿔려주는것같은 단어이다.</p>
<h1 id="ssrserver-side-rendering">SSR(Server Side Rendering)</h1>
<p>사용자가 요청할 때마다 서버에서 HTML을 생성하여 반환하는 방식이다. 사용자가 페이지를 요청할 때마다 최신 데이터를 기반으로 HTML을 생성하여 클라이언트에 보내기 때문에, 초기 페이지 로드 시점에 최신 콘텐츠를 제공할 수 있다.</p>
<h1 id="ssgstatic-site-generation">SSG(Static Site Generation)</h1>
<h3 id="static-generation-without-data">Static Generation without data</h3>
<pre><code class="language-jsx">function About() {
  return &lt;div&gt;About&lt;/div&gt;
}

export default About</code></pre>
<p>위와 같은 외부 데이터를 가져올 필요없는 페이지는 빌드 타임에 HTML을 생성한다.</p>
<h3 id="static-generation-with-data">Static Generation with data</h3>
<p>외부 데이터를 fetching 해야하는 경우 두 가지 방법이 있다.</p>
<ol>
<li>data에 따라 페이지 컨텐츠가 달라진다 -&gt; <code>getStaticProps</code></li>
<li>data에 따라 페이지 경로가 달리진다. -&gt; <code>getStaticPaths</code> + <code>getStaticProps</code></li>
</ol>
<h3 id="언제-static-generation을-사용하나">언제 Static Generation을 사용하나?</h3>
<p>빌드 시점에 pre-rendering을 해도 되는 페이지라면 SSG를 사용하는 것이 성능적으로 좋다.</p>
<h1 id="csrclient-side-rendering">CSR(Client side Rendering)</h1>
<p>브라우저가 로드된 후 Javascript 파일을 실행시켜서 HTML을 만드는 방식이다. 초기 실행시 사용자가 페이지를 보기까지 시간이 걸릴 수 있다는 단점이 있으나 그 뒤부터는 페이지 이동시 깜빡임 없는 부드러운 화면전환을 제공한다는 장점이 있다. </p>
<p>next.js에서 CSR을 사용하는 방법은</p>
<ol>
<li><code>useEffect()</code></li>
<li><code>SWR</code> or <code>TanStack Query</code>와 같은 라이브러리</li>
</ol>
<p>CSR로만 렌더링을 하게 되면 SEO에 불리하다는 단점이 있고 초기 사용자가 페이지를 보기까지 시간이 걸리기 때문에 CSR과 SSR을 결합하여 하이브리드로 사용하기를 장려하고 있다.</p>
<h1 id="isrincremental-static-regeneration">ISR(Incremental Static Regeneration)</h1>
<p>사이트를 빌드한 후에도 정적 페이지를 생성하거나 업데이트할 수 있게 해준다. </p>
<ul>
<li>Good to know : edge runtime은 ISR과 호환되지 않는다. 캐싱이 필요하면 직접 cache-control 헤더를 설정하여 stale-while-revalidate를 사용해야 한다.</li>
</ul>
<p>ISR을 사용하려면 <code>getStaticProps</code>에 revalidate props를 추가한다.</p>
<pre><code class="language-jsx">function Blog({ posts }) {
  return (
    &lt;ul&gt;
      {posts.map((post) =&gt; (
        &lt;li key={post.id}&gt;{post.title}&lt;/li&gt;
      ))}
    &lt;/ul&gt;
  )
}

// 이 함수는 서버 측에서 빌드 시에 호출.
// 새 요청이 들어오면 revalidate가 활성화된 경우 서버리스 함수에서 다시 호출될 수 있다.
export async function getStaticProps() {
  const res = await fetch(&#39;https://.../posts&#39;)
  const posts = await res.json()

  return {
    props: {
      posts,
    },
    revalidate: 10,
  }
}

export async function getStaticPaths() {
  const res = await fetch(&#39;https://.../posts&#39;)
  const posts = await res.json()

  // 포스트를 기반으로 사전 렌더링할 경로를 가져온다.
  const paths = posts.map((post) =&gt; ({
    params: { id: post.id },
  }))

  // 이 경로들만 빌드 시에 사전 렌더링.
  // { fallback: &#39;blocking&#39; }은 경로가 존재하지 않는 경우 요청 시에 페이지를 서버 렌더링한다.
  return { paths, fallback: &#39;blocking&#39; }
}

export default Blog</code></pre>
<ul>
<li>초기 요청 후 10초 이내의 모든 요청은 즉시 캐시된다.</li>
<li>10초가 지난 후 다음 요청도 캐시된 페이지가 표시된다.</li>
<li>Next.js는 백그라운드에서 페이지를 다시 생성한다.</li>
<li>페이지 생성에 성공하면 캐시를 무효화하고 업데이트된 페이지를 표시한다.</li>
<li>백그라운드에서 재생성에 실패하면 여전히 이전 페이지를 보여준다.</li>
</ul>
<p>Good to know : CDN 캐싱이 활성화되어있는지 확인해야한다.  </p>
<h1 id="reference">Reference</h1>
<ul>
<li><a href="https://nextjs.org/docs/app/building-your-application/rendering">https://nextjs.org/docs/app/building-your-application/rendering</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js] 렌더링 성능 개선하기 (1)]]></title>
            <link>https://velog.io/@hi_young/Next.js-%EB%A0%8C%EB%8D%94%EB%A7%81-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0%ED%95%98%EA%B8%B0-1</link>
            <guid>https://velog.io/@hi_young/Next.js-%EB%A0%8C%EB%8D%94%EB%A7%81-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0%ED%95%98%EA%B8%B0-1</guid>
            <pubDate>Fri, 07 Jun 2024 10:05:54 GMT</pubDate>
            <description><![CDATA[<p>빽빽한 업데이트 플랜으로 피처 개발만 하다 드디어 성능 개선을 할 수 있는 일정이 잡혔다. 유저가 가장 많이 유입되는 페이지인 &#39;검색 상세 페이지&#39;부터 성능 개선을 시작해보려 한다.</p>
<h2 id="문제점">문제점</h2>
<p>light house 성능 측면에서도 점수가 낮으며 실제로 렌더링되는 모습만 봐도 문제가 꽤 많다.</p>
<h3 id="light-house-report">Light house report</h3>
<p><img src="https://velog.velcdn.com/images/hi_young/post/aa569b7a-407a-4295-b9e5-cef2c525f1be/image.png" alt=""></p>
<h3 id="눈으로-확인되는-렌더링-이슈들">눈으로 확인되는 렌더링 이슈들</h3>
<ul>
<li>타 경쟁사 웹사이트 대비 렌더링 성능이 떨어짐.</li>
<li>검색 후 페이지가 전환될 때마다 전체 로딩 화면을 보여준다.</li>
<li>로딩이 끝난 후에도 UI가 생성되며 펼쳐지는 모습이 그대로 노출된다. (layout shift)</li>
<li>이로 인해 해당 페이지를 서빙받는 속도가 느릴 뿐 아니라 렌더링 과정이 깔끔하지 못함.</li>
</ul>
<p>바쁜 일정으로 한 페이지 내에서 팀원들끼리 컴포넌트를 나눠서 작업해야했고, 한 컴포넌트를 여러 명이 수정하기도 했던 상황이라 렌더링 이슈가 많다. 그리고 Next.js 프레임워크에서 개발하고 있었으나 서버사이드 렌더링을 거의 사용하지 않고 있었다.</p>
<br/>
<br/>

<h2 id="개선-1-세로-스크롤-영역-고정하기">(개선 1) 세로 스크롤 영역 고정하기</h2>
<p>화면이 렌더링되면서 스크롤이 없는 상태에서 컨텐츠가 길어지면 세로 스크롤 영역이 오른쪽에 생기는데 이때 화면폭이 줄어들면서 화면 전체가 움직인다. 특히 컨텐츠를 검색할 때 실시간으로 결과가 보이기 때문에 결과가 짧았다 길어졌다 반복되면 이 화면폭 움직임은 더 크게 느껴진다. </p>
<p>그래서 <code>overflow-y:scroll</code> 속성을 통해 세로 스크롤 영역을 고정으로 유지하고 스크롤바 디자인을 커스텀하였다. </p>
<pre><code class="language-css">body {
  overflow-y: scroll;
}
body::-webkit-scrollbar {
  width: 7px;
}
body::-webkit-scrollbar-thumb {
  background-color: #555555;
  border-radius: 10px;
}</code></pre>
<p>간단한 css로 화면이 렌더링될 때, 검색할 때 화면폭이 움직이지 않고 고정돼서 훨씬 깔끔한 느낌이 들었다.</p>
<h2 id="개선-2-ssr-csr-영역-정하기">(개선 2) SSR CSR 영역 정하기</h2>
<p>검색 상세 페이지의 경우 보여주는 데이터가 매우 많다. 부르는 API만 메타성 8개, 컨텐츠 API 7개로 API 조합 및 재가공해서 보여주는 영역도 많다. 그렇다보니 이 모든 API를 서버사이드에서 작업하기에는 부하가 클 것이고 그만큼 속도도 느려질 것이다.</p>
<p>보여주는 데이터가 많은 타 서비스들의 페이지 렌더링을 참고해보니 모든 영역을 SSR로 보여주는게 아닌 페이지의 일정 부분만 SSR으로 보여주고, 나머지는 스켈레톤 UI로 처리하는 것을 알 수 있었다. 그래서 개선할 페이지에서도 아래와 같이 영역을 나누어 보았다.</p>
<p><img src="https://velog.velcdn.com/images/hi_young/post/eddb0cdb-d522-4a70-b7a3-774fcdd3cc15/image.png" alt=""></p>
<h2 id="개선-3-csr-→-ssr로-전환">(개선 3) CSR → SSR로 전환</h2>
<h3 id="불필요한-로딩-제거">불필요한 로딩 제거</h3>
<p>CSR로 구현시 데이터를 가져오는 동안 보여줘야했던 전체 화면 로딩이 있었다. 노란 컴포넌트 영역은 서버사이드에서 완성된 상태로 가져올 것이기 때문에 불필요한 전체 로딩 UI를 제거했다. </p>
<h3 id="getserversideprops-함수-사용">getServerSideProps 함수 사용</h3>
<p>검색 결과에 따라 다른 정보를 보여줘야하며, 최신 데이터를 제공해야하기 때문에 SSR, SSG, ISR 중 SSR을 선택했다. 노란 컴포넌트의 데이터를 가져올 수 있는 API를 getServerSideProps에서 처리해서 initialData로 가져와서 사용했다.</p>
<h3 id="csr로-처리되고-있는-ui-수정">CSR로 처리되고 있는 UI 수정</h3>
<p>서버사이드에서 initialData를 미리 가져왔으나 여전히 깜빡거리는 UI들이 있었다. 바로 노란 컴포넌트 내에서 추가 API 호출 후 생성되는 UI들이었다.</p>
<p>이러한 UI들은 렌더링되는 영역을 css로 정확하게 잡아주어야 감싸고 있는 레이아웃이 움직이는 문제가 안생기며 스켈레톤 UI로 대체해주었다. </p>
<p><img src="https://velog.velcdn.com/images/hi_young/post/307905f8-f80d-4756-ac53-55c0008328bb/image.png" alt=""></p>
<h3 id="이미지-렌더링-개선">이미지 렌더링 개선</h3>
<p>기존에는 이미지 cdn 주소를 가져오기 위해 API를 2개 사용하고 있었다.</p>
<ul>
<li>이미지 key 값을 호출하는 API</li>
<li>key 값에 따른 cdn 주소가 맵핑되어있는 메타성 API</li>
</ul>
<p>url 하나만 있으면 되는데 이 때문에 렌더링 속도가 특히 느렸다. 그래서 백엔드팀과 함께 의논하여 공통 cdn 주소와 key값만으로 이미지 url을 생성할 수 있도록 수정하였다.</p>
<p>이렇게 개선하고 나니 한가지 눈에 띄는 문제가 있었다. 페이지가 전환될 때 잠깐의 딜레이가 있는데 유저 입장에서는 페이지가 잠깐 멈춘 느낌을 받을 수도 있다. 그래서 고민하던 중 타 서비스들을 참고해보니 사이트 상단에 작은 페이지 로딩 프로그레스 바를 넣어서 “페이지가 이동중이다”라는 느낌을 받게 하고 있었다.</p>
<h2 id="개선-4-페이지-로딩-추가">(개선 4) 페이지 로딩 추가</h2>
<p><img src="https://velog.velcdn.com/images/hi_young/post/3068b956-6bf2-402d-a243-25b1305412cd/image.jpg" alt=""></p>
<p>progress에 따라 로딩바 애니메이션을 만들기 위해 <code>react-top-loading-bar</code> 라는 라이브러리를 사용했다.</p>
<pre><code class="language-jsx">
const [progress, setProgress] = useState(0);

useEffect(() =&gt; {
  const start = () =&gt; {
    setProgress(40);
  };
  const end = () =&gt; {
    setProgress(100);
  };
  Router.events.on(&#39;routeChangeStart&#39;, start);
  Router.events.on(&#39;routeChangeComplete&#39;, end);
  Router.events.on(&#39;routeChangeError&#39;, end);
  return () =&gt; {
    Router.events.off(&#39;routeChangeStart&#39;, start);
    Router.events.off(&#39;routeChangeComplete&#39;, end);
    Router.events.off(&#39;routeChangeError&#39;, end);
  };
}, []);

return (
  &lt;LoadingBar color={COLOR.POINT.MAIN} progress={progress} /&gt;
)</code></pre>
<h2 id="1차-개선-결과">1차 개선 결과</h2>
<p>시각적으로 보이는 이슈들부터 해결했는데 점수가 20점 가량 올랐다.
CSR로 렌더링되고 있던 부분들을 SSR로 바꾸면서 LCP, TBT 시간이 50%이상 감소했으며
CLS은 기존에도 시간은 적게 걸렸으나 눈으로 확인되던 Layout shift를 제거했으며 시간도 80%이상 감소되었다. </p>
<p><img src="https://velog.velcdn.com/images/hi_young/post/7bb2fffe-13ef-4333-88d6-75df0963c88d/image.png" alt=""></p>
<p>2차 개선 계속…</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CS 스터디] 혼자 공부하는 컴퓨터구조+운영체제 Chapter 6~8]]></title>
            <link>https://velog.io/@hi_young/CS-%EC%8A%A4%ED%84%B0%EB%94%94-%ED%98%BC%EC%9E%90-%EA%B3%B5%EB%B6%80%ED%95%98%EB%8A%94-%EC%BB%B4%ED%93%A8%ED%84%B0%EA%B5%AC%EC%A1%B0%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-Chapter-68</link>
            <guid>https://velog.io/@hi_young/CS-%EC%8A%A4%ED%84%B0%EB%94%94-%ED%98%BC%EC%9E%90-%EA%B3%B5%EB%B6%80%ED%95%98%EB%8A%94-%EC%BB%B4%ED%93%A8%ED%84%B0%EA%B5%AC%EC%A1%B0%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-Chapter-68</guid>
            <pubDate>Sun, 19 May 2024 05:56:41 GMT</pubDate>
            <description><![CDATA[<h1 id="06-메모리와-캐시-메모리">06 메모리와 캐시 메모리</h1>
<h2 id="06-1-ram의-특징과-종류">06-1 RAM의 특징과 종류</h2>
<h3 id="ram의-특징">RAM의 특징</h3>
<ul>
<li>휘발성 메모리 : 전원을 끄면 저장된 내용이 사라지는 저장 장치</li>
<li>하드 디스크, SSD, CD-ROM, USB 메모리는 전원을 꺼도 저장된 내용이 유지되며 비휘발성 메모리라고 한다.</li>
<li>그래서 비휘발성 메모리에는 보관할 대상을 저장하고, 휘발성 메모리에는 실행할 대상을 저장한다.</li>
</ul>
<h3 id="ram의-용량과-성능">RAM의 용량과 성능</h3>
<ul>
<li>RAM 용량이 크면 어떤 점이 좋을까?</li>
<li>RAM의 용량이 크면 많은 프로그램들을 동시에 빠르게 실행하는데 유리하다. RAM 용량이 작으면 CPU에서 실행해야할 데이터를 보조기억장치에서 가져와야하므로 시간이 오래걸린다. 그러나 RAM 용량이 커서 미리 실행할 프로그램들이 들어가 있으면 그만큼 빠르게 CPU가 가져와 실행할 수 있다.</li>
<li>RAM 용량만 늘린다고 해서 실행 속도가 그에 비례해서 증가하지 않는다.</li>
</ul>
<h3 id="ram의-종류">RAM의 종류</h3>
<p><strong>DRAM</strong></p>
<ul>
<li>Dynamic RAM의 준말</li>
<li>시간이 지나면 데이터가 점차 사라지는 RAM</li>
<li>데이터 소멸을 막기 위해 일정 주기로 데이터를 재활성화해야한다.</li>
<li>일반적으로 많이 사용하는 메모리<ul>
<li>소비 전력이 낮고</li>
<li>저렴</li>
<li>집적도가 높아 대용량으로 설계하기 용이  </li>
</ul>
</li>
</ul>
<p><strong>SRAM</strong></p>
<ul>
<li>Static RAM</li>
<li>저장된 데이터가 변하지 않는 RAM</li>
<li>속도가 빠름<ul>
<li>소비 전력이 크고</li>
<li>비싸고</li>
<li>집적도가 낮음.</li>
</ul>
</li>
<li>그래서 대용량으로 만들어질 필요는 없지만 속도가 빨라야하는 저장 장치인 캐시 메모리에 사용</li>
</ul>
<p><strong>SDRAM</strong></p>
<ul>
<li>Synchronous Dynamic RAM</li>
<li>클럭 타이밍에 맞춰 CPU와 정보를 주고 받을 수 있는 메모리</li>
</ul>
<p><strong>DDR SDRAM</strong></p>
<ul>
<li>Double Data Rate SDRAM</li>
<li>대역폭을 넓혀 속도를 빠르게 만든 메모리</li>
<li>대역폭 : 데이터를 주고받는 길의 너비</li>
</ul>
<p>📝
RAM은 실행중인 프로그램의 데이터와 명령어가 저장된 장치이다. RAM의 특징은 휘발성메모리로 전원이 꺼지면 저장된 내용도 사라진다. 그와 반대로 하드 디스크, SSD, USB는 비휘발성 메모리로 전원을 꺼도 저장된 내용이 유지된다. 그래서 실행할 대상은 휘발성 메모리에, 보관할 대상은 비휘발성 메모리에 저장하게 된다.
RAM의 용량이 크면 CPU가 실행할 프로그램을 빠르게 가져올 수 있으므로 실행 속도가 빨라진다. 그러나 RAM의 용량에 비례해서 실행 속도가 증가하지는 않는다. 용량이 작으면 보조프로그램에서 들고와야하니 당연히 느려지겠지만 CPU가 RAM에서 들고오는 건 똑같기 때문이다.
RAM의 종류는 DRAM, SRAM 등이 있으며 DRAM은 시간이 지나면 데이터를 지우는 특징이 있으며 RAM이 여기에 해당된다. SRAM은 데이터 변화가 없으며 속도가 빨라서 캐시 메모리가 해당된다.</p>
<h2 id="06-2-메모리의-주소-공간">06-2 메모리의 주소 공간</h2>
<h3 id="물리-주소와-논리-주소">물리 주소와 논리 주소</h3>
<ul>
<li><p>물리 주소
메모리가 사용하는 주소로 하드웨어상의 실제 주소</p>
</li>
<li><p>논리 주소
CPU와 실행 중인 프로그램이 사용하는 각각의 프로그램에 부여된 주소</p>
</li>
<li><p>MMU(Memory Management Unit)</p>
<ul>
<li>논리 주소를 물리 주소로 변환해주는 장치</li>
<li>CPU가 발생시킨 논리 주소에 베이스 레지스터 값을 더하여 논리 주소를 물리 주소로 변환</li>
</ul>
</li>
<li><p>베이스 레지스터 : 프로그램의 가장 작은 물리 주소, 즉 프로그램의 첫 물리 주소를 저장하는 셈이고, 논리 주소는 프로그램의 시작점으로부터 떨어진 거리인 셈이다.</p>
</li>
</ul>
<h3 id="메모리-보호-기법">메모리 보호 기법</h3>
<ul>
<li>한계 레지스터 : 다른 프로그램의 영역을 침벌할 수 있는 명령어는 위험하기 때문에 논리 주소를 벗어나는 명령어 실행을 방지하고 실행 중인 프로그램이 다른 프로그램에 영향을 받지 않도록 보호할 방법을 담당</li>
<li>CPU가 접근하려는 논리 주소는 한계 레지스터가 저장한 값보다 커서는 안됨<ul>
<li>인터럽트를 발생시켜 실행을 중단시킴</li>
</ul>
</li>
</ul>
<p>📝
물리 주소는 메모리 하드웨어상의 실제 주소를 말하고, 논리 주소는 프로그램의 시작 주소부터 떨어진 거리를 의미하며 프로그램들끼리 중복될 수 있다. 예를 들면 A라는 프로그램의 논리주소가 100이고, B라는 프로그램의 논리주소가 100이다. A를 물리 주소로 변환하면 베이스 레지스터(1000)이라 1100일거고, B는 베이스 레지스터(2000)이라 2100이 되는거다. 논리 주소를 물리 주소로 변환해주는 장치가 MMU로 CPU가 요청한 논리주소값에 베이스 레지스터의 값을 더해서 구한다. 그리고 다른 프로그램이 서로 영향 주는 것을 막기 위해 한계 레지스터가 존재하며, 이는 프로그램의 시작점부터의 크기를 나타낸다. 만약 CPU가 접근하려는 주소가 한계 레지스터보다 크면 인터럽트가 발생한다.</p>
<h2 id="06-3-캐시-메모리">06-3 캐시 메모리</h2>
<ul>
<li>CPU가 메모리에 접근하는 시간은 CPU의 연산 속도보다 느림</li>
<li>CPU가 연산을 빨리한다 해도 메모리에 접근하는 시간이 느리면 CPU의 빠른 연산 속도는 아무런 쓸모가 없다.</li>
<li>이를 극복하기 위한 저장 장치가 바로 캐시 메모리</li>
</ul>
<h3 id="저장-장치-계층-구조">저장 장치 계층 구조</h3>
<ul>
<li>CPU와 가까운 저장 장치는 빠르고, 멀리 있는 저장 장치는 느리다.</li>
<li>속도가 빠른 저장 장치는 저장 용량이 작고, 가격이 빘다.</li>
<li>CPU에 얼마나 가까운가를 기준으로 계층적으로 나타낼 수 있다.</li>
</ul>
<h3 id="캐시-메모리">캐시 메모리</h3>
<ul>
<li>CPU와 메모리 사이에 위치하고, 레지스터보다 용량이 크고 메모리보다 빠른 SRAM 기반의 저장 장치</li>
<li>메모리에서 CPU가 사용할 일부 데이터를 미리 캐시 메모리로 가지고 와서 활용하는 것</li>
<li>여러 개의 캐시 메모리가 있음.</li>
<li>CPU와 가까운 순서대로 계층을 구성.<ul>
<li>L1 캐시</li>
<li>L2 캐시</li>
<li>L3 캐시</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hi_young/post/a5737e8a-c8d5-4f2e-b96a-b34965812fe5/image.png" alt=""></p>
<h3 id="참조-지역성-원리">참조 지역성 원리</h3>
<ul>
<li><p>CPU가 사용할 법한 대상을 예측하여 저장</p>
</li>
<li><p>캐시 히트 : 자주 사용할 것으로 예측한 데이터가 실제로 들어맞아 캐시 메모리 내 데이터가 CPU에서 활용될 경우</p>
</li>
<li><p>캐시 미스 : 예측이 틀려 메모리에서 필요한 데이터를 직접 가져와야 하는 경우</p>
</li>
<li><p>캐시 적중률 : 캐시가 히트되는 비율</p>
</li>
<li><p>참조 지역성의 원리 : CPU가 메모리에 접근할 때의 주된 경향을 바탕으로 만들어진 원리</p>
<ul>
<li>시간 지역성 : CPU는 최근에 접근했던 메모리 공간에 다시 접근하려는 경향이 있음.</li>
<li>공간 지역성 : 접근한 메모리 공간 근처를 접근하려는 경향이 있음.</li>
</ul>
</li>
</ul>
<p>📝 캐시 메모리란 CPU와 메모리 사이의 속도 차이를 줄일 수 있는 저장 장치이다. CPU가 일을 처리하는 속도보다 메모리에서 데이터를 가져오는 속도가 느리기 때문에 중간 캐시 메모리가 있는데 CPU가 사용할법한 데이터를 미리 가져와서 저장을 한다. 아 그리고 캐시도 CPU에 가까운 순으로 L1, L2, L3가 존재한다. CPU가 사용할 법한 대상을 예측하여 저장하는데 참조 지역성 원리에 따른다. 최근에 실행했던 프로그램의 메모리, 실행했던 프로그램 메모리 근처에 있는 데이터들을 미리 들고와서 저장해놓는데 만약 캐시 메모리에 저장된 데이터가 CPU에서 활용될 경우를 캐시 히트, 없어서 메모리에서 들고오면 캐시 미스, 캐시가 되는 비율을 캐시 적중률이라고 한다.</p>
<h1 id="07-보조기억장치">07 보조기억장치</h1>
<h2 id="07-1-다양한-보조기억장치">07-1 다양한 보조기억장치</h2>
<h3 id="하드-디스크">하드 디스크</h3>
<ul>
<li>자기적인 방식으로 데이터를 저장하는 보조기억장치</li>
<li>하드 디스크가 데이터에 접근하는 시간은 크게 탐색시간, 회전 지연, 전송 시간으로 나뉘며 데이터를 탐색하고 읽어들이는 시간이 오래 걸린다.</li>
</ul>
<h3 id="플래시-메모리">플래시 메모리</h3>
<ul>
<li>USB메모리, SSD, SD카드 모두 플래시 메모리 기반의 보조기억장치</li>
<li>전기적으로 데이터를 읽고 쓸 수 있는 반도체 기반의 저장 장치</li>
<li>셀 : 플래시 메모리에서 데이터를 저장하는 가장 작은 단위</li>
<li>같은 용량의 플래시 메모리 저장 장치라 할지라도 셀의 타입에 따라 수명, 가격, 성능이 다르다.</li>
<li>SLC, MLC, TLC</li>
</ul>
<p>📝 하드 디스크는 자기적인 방식으로 데이터를 저장, 플래시 메모리는 전기적으로 데이터를 읽고 쓸 수 있는 반도체 기반의 저장 장치이다.</p>
<h2 id="07-2-raid의-정의와-종류">07-2 RAID의 정의와 종류</h2>
<ul>
<li>1TB 하드 * 4 개 구성이 4TB 하드 1개 구성보다 성능과 안전성이 더 높다.</li>
</ul>
<h3 id="raid의-정의">RAID의 정의</h3>
<ul>
<li>RAID(Redundant Array of Independent Disks)</li>
<li>하드 디스크와 SSD를 사용하는 기술로, 데이터의 안전성 혹은 높은 성능을 위해 여러 개의 물리적 보조기억장치를 마치 하나의 논리적 보조기억장치처럼 사용하는 기술을 의미</li>
</ul>
<h3 id="raid의-종류">RAID의 종류</h3>
<h4 id="raid-0">RAID 0</h4>
<ul>
<li>여러 개의 보조기억장치에 데이터를 단순히 나누어서 저장하는 구성 방식</li>
<li>스트라입 : 줄무늬처럼 분석되어 저장된 데이터</li>
<li>스트라이핑 : 분산하여 저장하는 것</li>
<li>장점 : 동시에 데이터를 읽고 쓸 수 있기 때문에 속도가 빠름</li>
<li>단점 : 저장된 정보가 안전하지 않다.<h4 id="raid-1">RAID 1</h4>
</li>
<li>완전한 복사본을 만드는 방식</li>
<li>장점 : 복구가 간단하다</li>
<li>단점 : 사용 가능한 용량이 작아지고 느리다.<h4 id="raid-4">RAID 4</h4>
</li>
<li>오류를 검출하고 복구하기 위한 정보를 저장한 장치를 두는 구성 방식</li>
<li>패리티 비트 : 오류를 검출하고 복구하기 위한 정보</li>
<li>단점 : 새로운 데이터 저장할 때마다 패리티를 저장하기 때문에 병목 현상 발생.<h4 id="raid-5">RAID 5</h4>
</li>
<li>패리티 정보를 분산하여 저장하는 방식</li>
<li>장점 : RAID 4의 문제인 병목 현상 해소<h4 id="raid-6">RAID 6</h4>
</li>
<li>서로 다른 두 개의 패리티를 두는 방식</li>
<li>데이터를 더욱 안전하게 보관하고 싶을 때 사용</li>
</ul>
<p>📝 RAID는 데이터의 안전성과 성능을 위한 방법 중 하나로 여러 개의 보조기억장치를 사용하는 전략이다.</p>
<h1 id="08-입출력장치">08 입출력장치</h1>
<h2 id="08-1-장치-컨트롤러와-장치-드라이버">08-1 장치 컨트롤러와 장치 드라이버</h2>
<h3 id="장치-컨트롤러">장치 컨트롤러</h3>
<ul>
<li><p>입출력장치는 다루기 까다롭다.</p>
<ul>
<li>입출력장치 종류가 많아서 정보를 주고받는 방식을 규격화하기 어렵다.</li>
<li>일반적으로 CPU와 메모리의 데이터 전송률은 높지만 입출력장치의 데이터 전송률은 낮다.<ul>
<li>전송률 : 데이터를 얼마나 빨리 교환할 수 있는지를 나타내는 지표</li>
</ul>
</li>
</ul>
</li>
<li><p>그래서 장치 컨트롤러 이러한 문제들을 해결해 주는데</p>
<ul>
<li>CPU와 입출력장치 간의 통신 중개</li>
<li>오류 검출</li>
<li>데이터 버퍼링<ul>
<li>버퍼링 : 전송률이 높은 장치와 낮은 장치 사이에 주고받는 데이터를 버퍼라는 임시 저장 공간에 저장하여 전송률을 비슷하게 맞추는 방법</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>-&gt; 장치 컨트롤러는 전송률은 높은 CPU와 전송률이 낮은 입출력장치와의 차이를 데이터 버퍼링으로 완화한다.</p>
<h3 id="장치-드라이버">장치 드라이버</h3>
<ul>
<li>장치 컨트롤러의 동작을 감지하고 제어함으로서 장치 컨트롤러가 컴퓨터 내부와 정보를 주고받을 수 있게 하는 프로그램</li>
<li>장치 컨트롤러 : 입출력장치를 연결하기 위한 하드웨어적인 통로</li>
<li>장치 드라이버 : 입출력장치를 연결하기 위한 소프트웨어적인 통로</li>
</ul>
<p>📝
입출력장치는 종류가 많아 규격화가 어렵고 전송률도 낮다. 그래서 CPU와의 통신을 도와주는 장치 컨트롤러가 데이터 버퍼링을 통해 전송률을 비슷하게 맞춰준다.
그리고 소프트웨어적으로도 장치 드라이버가 함께 필요하다.</p>
<h2 id="08-2-다양한-입출력-방법">08-2 다양한 입출력 방법</h2>
<h3 id="프로그램-입출력">프로그램 입출력</h3>
<ul>
<li>기본적으로 프로그램 속 명령어로 입출력장치를 제어하는 방법</li>
</ul>
<h3 id="인터럽트-기반-입출력">인터럽트 기반 입출력</h3>
<ul>
<li>장치 컨트롤러가 입출력 작업을 끈낸 뒤 CPU로 인터럽트 요청을 보내는 방법</li>
</ul>
<h3 id="dma-입출력">DMA 입출력</h3>
<ul>
<li>입출력장치와 메모리가 CPU를 거치지 않고 상호작용할 수 있는 입출력 방식</li>
<li>DMA 컨트롤러 : 직접 시스템 버스로 메모리에 접근하며 장치 컨트롤러와 통신한다. 작업이 끝나면 CPU에 인터럽트를 걸어 작업이 끝났음을 알린다.</li>
<li>시스템 버스는 동시 사용이 불가능하기 때문에 DMA 컨트롤러는 입출력 버스를 이용한다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Next.js FCM을 이용한 Push 알림 구현(1)]]></title>
            <link>https://velog.io/@hi_young/Firebase-Cloud-MessagingFCM</link>
            <guid>https://velog.io/@hi_young/Firebase-Cloud-MessagingFCM</guid>
            <pubDate>Thu, 16 May 2024 22:34:09 GMT</pubDate>
            <description><![CDATA[<h1 id="push-api">Push API</h1>
<ul>
<li>서버로부터 푸시 메시지를 수신할 수 있는 기능을 제공</li>
<li>이를 통해 동의한 사용자에게 비동기식 알림 및 업데이트를 전달</li>
<li>2023년 3월부터 최신 브라우저도 지원되기 시작했으며 이전 브라우저에서는 동작하지 않을 수 있다. 대부분의 브라우저에서 동작하는 듯.
<img src="https://velog.velcdn.com/images/hi_young/post/d74a5075-111a-443a-9524-f8bcf14b6265/image.png" alt=""></li>
</ul>
<p>이러한 푸시 알림이 가능한 서버를 만드는 것은 매우 복잡하고 많은 기술적인 과제들이 있기 때문에 구글의 FCM이라는 서비스를 사용하면 좀더 쉽게 사용할 수 있다. </p>
<h1 id="fcmfirebase-cloud-messaging">FCM(Firebase Cloud Messaging)</h1>
<p>Google에서 제공하는 무료 푸시 알림 및 메시징 서비스로 모바일 및 웹 애플리케이션에 푸시 알림을 보낼 수 있다.</p>
<h2 id="주요-기능">주요 기능</h2>
<ol>
<li><p>알림 메시지 또는 데이터 메시지 전송
사용자에게 표시되는 알림 메시지를 전송하거나 데이터 메시지를 전송해서 임의로 처리할 수 있다.</p>
</li>
<li><p>다양한 메시지 타켓팅
단일 기기, 기기 그룹, 주제를 구독한 기기 등 3가지 방식으로 클라이언트 앱에 메시지를 보낼 수 있다.</p>
</li>
<li><p>FCM 콘솔에서 메시지 전송
코드 작성 없이 FCM 콘솔에서 직접 전송 가능하다.</p>
</li>
</ol>
<h1 id="fcm으로-메시지를-보내는-과정">FCM으로 메시지를 보내는 과정</h1>
<p><img src="https://velog.velcdn.com/images/hi_young/post/cdf69295-26ee-4f3d-b4e1-61196461c9c0/image.png" alt=""></p>
<p>(1) 브라우저에서 푸시 서비스 구독 + VAPID 공개 키 전달
(2) 구독 정보 수신
(3) 구독 정보 서버로 전달
(4) 구독 정보 + 메시지 + VAPID 비공개 키로 암호화된 JWT
(5) VAPID 공개 키로 유효성 검증 &amp; 검증된 경우 구독 정보에 해당하는 브라우저로 푸시 메시지 발송</p>
<h2 id="vapid">VAPID</h2>
<p><img src="https://velog.velcdn.com/images/hi_young/post/6bfa63f2-5a04-4754-8e97-ead3c532e1c4/image.png" alt=""></p>
<ul>
<li>Voluntary Application Server Idenrification</li>
<li>Web Push API의 안전한 푸시 메시지 전송을 위한 인증 방식</li>
<li>공개키와 비공개키 쌍을 갖는 암호화 방식</li>
</ul>
<h1 id="구현-순서">구현 순서</h1>
<ol>
<li><p>FCM SDK 설정
firebase에서 프로젝트 생성 후 FCM 설정</p>
</li>
<li><p>클라이언트 개발</p>
</li>
</ol>
<ul>
<li>알림 허용 팝업</li>
<li>유저 기기 구독 후 식별 가능한 토큰 서버로 전달</li>
<li>메시지 처리</li>
</ul>
<ol start="3">
<li>서버 개발</li>
</ol>
<ul>
<li><p>인증 및 메시지를 보내기 위한 Firebase Admin SDK Or 서버 프로토콜 중 선택</p>
</li>
<li><p>서버 환경
<a href="https://firebase.google.com/docs/cloud-messaging/server?hl=ko&amp;_gl=1*133xdz2*_up*MQ..*_ga*MTE0Nzc5ODI5My4xNzE1OTAxMzQ2*_ga_CW55HF8NVT*MTcxNTkwMTM0NS4xLjAuMTcxNTkwMTM0NS4wLjAuMA">https://firebase.google.com/docs/cloud-messaging/server?hl=ko&amp;_gl=1*133xdz2*_up*MQ..*_ga*MTE0Nzc5ODI5My4xNzE1OTAxMzQ2*_ga_CW55HF8NVT*MTcxNTkwMTM0NS4xLjAuMTcxNTkwMTM0NS4wLjAuMA</a>..</p>
</li>
<li><p>FCM 등록 토큰 관리를 위한 권장사항
<a href="https://firebase.google.com/docs/cloud-messaging/manage-tokens?hl=ko&amp;_gl=1*15ik23n*_up*MQ..*_ga*MTE0Nzc5ODI5My4xNzE1OTAxMzQ2*_ga_CW55HF8NVT*MTcxNTkwMTM0NS4xLjAuMTcxNTkwMTM0NS4wLjAuMA">https://firebase.google.com/docs/cloud-messaging/manage-tokens?hl=ko&amp;_gl=1*15ik23n*_up*MQ..*_ga*MTE0Nzc5ODI5My4xNzE1OTAxMzQ2*_ga_CW55HF8NVT*MTcxNTkwMTM0NS4xLjAuMTcxNTkwMTM0NS4wLjAuMA</a>..</p>
</li>
</ul>
<h1 id="fcm-메시지-정보">FCM 메시지 정보</h1>
<h3 id="알림-메시지">알림 메시지</h3>
<ul>
<li>백그라운드에서 사용자 기기에 메시지를 표시.</li>
<li>브라우저 시스템 알림</li>
<li>정의된 interface를 사용해야함.<pre><code>{
&quot;message&quot;:{
  &quot;token&quot;:&quot;bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...&quot;,
  &quot;notification&quot;:{
    &quot;title&quot;:&quot;Portugal vs. Denmark&quot;,
    &quot;body&quot;:&quot;great match!&quot;
  }
}
}</code></pre><h3 id="데이터-메시지">데이터 메시지</h3>
</li>
<li>클라이언트에서 해당 데이터 처리</li>
<li>정해진 interface 없음</li>
<li>브라우저 내 커스텀 알림 및 업데이트용으로 사용하면 좋을 듯</li>
<li>?? 백그라운드 상황에서도 오는지 확인 필요<pre><code>{
&quot;message&quot;:{
  &quot;token&quot;:&quot;bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...&quot;,
  &quot;data&quot;:{
    &quot;Nick&quot; : &quot;Mario&quot;,
    &quot;body&quot; : &quot;great match!&quot;,
    &quot;Room&quot; : &quot;PortugalVSDenmark&quot;
  }
}
}</code></pre></li>
</ul>
<p>마지막으로 의문이 들었던 것들을 좀 더 찾아봤다.
FCM 운영에 드는 비용이 꽤나 들텐데 왜 무료로 제공하는걸까? 구글의 인프라 자체가 워낙 크게 잘 구축되어있어서 효율적으로 관리할 수 있으며 FCM을 무료로 제공함으로서 구글의 다른 유료 클라우드 서비스 연계로 수익을 얻을 수 있다. 그리고 푸시 알림에 대한 데이터들을 수집할 수 있고 그 데이터로 또 다른 비지니스로 활용할 수 있다.</p>
<p>그리고 하루에 몇건까지 메시지 전송이 가능할까? FCM 문서에는 하루에 전송할 수 있는 메세지에 대한 명확한 제한이 명시되어 있지 않다. 그래서 대규모로 푸시 알림 기능을 사용하게 된다면 구글측에 문의를 통해 추가적인 지원을 받는 것이 좋을 것 같다.</p>
<p>확인된 포그라운드 상태는 &quot;화면에 포커스 되어있을 때&quot;, &quot;화면에 보이지만 포커스 안되어있을 떄&quot; 두 가지였고
확인된 백그라운드 상태는 &quot;브라우저 실행 후 포커스되지 않을 탭에 있을 떄&quot;, &quot;브라우저 실행 후 탭을 껐을 때&quot; 였다.</p>
<p>다음은 구현 포스팅 예정.</p>
<h1 id="reference">Reference</h1>
<p><a href="https://geundung.dev/114">https://geundung.dev/114</a>
<a href="https://web.dev/articles/push-notifications-web-push-protocol?hl=ko">https://web.dev/articles/push-notifications-web-push-protocol?hl=ko</a>
<a href="https://firebase.google.com/docs/cloud-messaging?hl=ko&amp;_gl=1*1qwvmnz*_up*MQ..*_ga*MTMwOTkzMzM4OC4xNzE1OTEzMzYy*_ga_CW55HF8NVT*MTcxNTkxMzM2Mi4xLjAuMTcxNTkxMzM2Mi4wLjAuMA">https://firebase.google.com/docs/cloud-messaging?hl=ko&amp;_gl=1*1qwvmnz*_up*MQ..*_ga*MTMwOTkzMzM4OC4xNzE1OTEzMzYy*_ga_CW55HF8NVT*MTcxNTkxMzM2Mi4xLjAuMTcxNTkxMzM2Mi4wLjAuMA</a>..</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[0516 TIL]]></title>
            <link>https://velog.io/@hi_young/0516-TIL</link>
            <guid>https://velog.io/@hi_young/0516-TIL</guid>
            <pubDate>Wed, 15 May 2024 22:37:51 GMT</pubDate>
            <description><![CDATA[<h1 id="push-api">Push API</h1>
<ul>
<li>브라우저를 통해 푸시 알림을 보낼 수 있게 해주는 API</li>
<li>service worker와 함께 사용되어야 한다.<ul>
<li>백그라운드에서 실행되는 스크립트로, 웹 애플리케이션과 브라우저 간의 중간에 위치하여 푸시 알림 및 오프라인 콘텐츠 캐싱과 같은 기능을 제공.</li>
</ul>
</li>
</ul>
<h1 id="notification-api">Notification API</h1>
<h1 id="push-api를-사용하기-위해서-pwa-환경-세팅이-필수인가">Push API를 사용하기 위해서 PWA 환경 세팅이 필수인가?</h1>
<ul>
<li>Push API를 사용하기 위해서는 서비스 워커 세팅 및 등록이 필요하다.</li>
<li>next-pwa는 서비스 워커 등록 기능이 있을 뿐, 기본적으로 브라우저가 지원하는 Service Worker API를 활용하여 구현할 수 있다.<pre><code class="language-jsx">// pages/_app.js
</code></pre>
</li>
</ul>
<p>import { useEffect } from &#39;react&#39;;</p>
<p>function App({ Component, pageProps }) {
  useEffect(() =&gt; {
    if (&#39;serviceWorker&#39; in navigator) {
      window.addEventListener(&#39;load&#39;, () =&gt; {
        navigator.serviceWorker.register(&#39;/service-worker.js&#39;)
          .then(registration =&gt; {
            console.log(&#39;ServiceWorker registration successful with scope: &#39;, registration.scope);
          })
          .catch(error =&gt; {
            console.log(&#39;ServiceWorker registration failed: &#39;, error);
          });
      });
    }
  }, []);</p>
<p>  return &lt;Component {...pageProps} /&gt;;
}</p>
<p>export default App;</p>
<pre><code>
# PWA(Progressive Web App)
## PWA란
모바일 앱과 웹 앱의 장점을 결합하여 사용자 경험을 향상시키는 웹 애플리케이션 형태

## 특징
### 1. 오프라인 지원
PWA는 서비스 워커를 사용하여 오프라인 상태에서도 동작할 수 있다. 사용자가 인터넷에 연결되어 있지 않더라도 앱에 접속하여 콘텐츠를 탐색하고 작업할 수 있다.

### 2. 홈 화면에 아이콘 추가
사용자가 웹 앱을 홈 화면에 추가할 수 있으며, 이렇게 추가된 PWA 아이콘을 클릭하여 앱을 쉽게 시작할 수 있다.

### 3. 네이티브 앱과 유사한 사용자 경험
PWA는 네이티브 앱과 유사한 사용자 경험을 제공한다. 전체 화면 모드, 푸시 알림, 디바이스 기능 접근 등의 기능을 활용하여 사용자에게 편리하고 직관적인 앱 경험을 제공한다.

### 4. 빠른 로딩 속도
PWA는 캐싱과 prefetch를 통해 로딩 속도를 향상시킨다. 사용자가 웹 앱을 방문할 때 필요한 자원을 미리 캐싱하고, 콘텐츠를 더 빨리 로드하여 사용자 경험을 개선한다.

### 5. 안전성과 보안
HTTPS를 사용하여 통신하고, 서비스 워커를 통해 캐싱된 콘텐츠를 안전하게 제공함으로써 데이터 보안과 개인정보 보호를 강화한다.


# Service Worker
## 서비스 워커란
브라우저와 웹 애플리케이션 사이 중간에 위치하여 다양한 기능을 수행하는 백그라운드 스크립트로 주로 웹 애플리케이션의 오프라인 지원, 푸시 알림, 백그라운드 동기화 및 네트워크 요청 중간 처리 등의 기능을 수행한다.

## 서비스 워커 특징
- 서비스 워커는 웹 애플리케이션이 오프라인 상태에서도 작동할 수 있도록 해야한다.
- 사용자가 오프라인 상태일 때도 앱을 로드하고, 이전에 캐시된 콘텐츠를 제공하여 사용자 경험을 향상시킬 수 있다.
- 백그라운드에서 동작하기 때문에 사용자의 주요 스레드를 차단하지 않고 비동기적으로 작업을 처리할 수 있다.

## 주요 기능
### 1. 네트워크 요청 중간 처리
서비스 워커는 브라우저의 HTTP 요청을 가로채고, 네트워크 요청을 캐시에서 가져오거나 온라인 상태에서 요청을 보낼 수 있다. 캐시된 리소스를 사용하여 웹 애플리케이션의 성능을 향상시킬 수 있다.
- MSW(Mock Service Worker)는 실제로 서비스 워커로 동작하지는 않으며, 테스트 목적으로 사용되는 가짜 서버를 제공하는 라이브러리.

### 2. 오프라인 지원
서비스 워커는 웹 애플리케이션이 오프라인에서도 동작할 수 있도록 한다. 사용자가 오프라인 상태일 때도 캐시된 콘텐츠를 표시하여 사용성을 유지한다.
- 오프라인에서도 도서 검색 및 읽기 기능
- 오프라인에서 가능한 게임

### 3. 푸시 알림
서비스 워커는 브라우저로부터 푸시 알림을 받고 사용자에게 알림을 표시할 수 있다. 이를 통해 백그라운드에서도 사용자에게 중요한 정보를 전달할 수 있다.

### 4. 백그라운드 동기화
서비스 워커는 백그라운드에서 주기적으로 실행되며 데이터를 동기화하거나 백그라운드 작업을 수행할 수 있다. 이를 통해 애플리케이션이 온라인 상태일 때 백그라운드에서 데이터를 업데이트하거나 작업을 처리할 수 있다.

### 질문
- 파이어베이스에서만 등록하면 되는건지? 아니면 웹 자체에도 세팅이 필요한건지?
파이어베이스의 세팅값으로 서비스 워커 파일을 생성 후, 웹 자체에서도 파일 추가 및 등록이 필요하다.

- 백그라운드 스크립트


# FCM(Firebase Cloud Messaging)
- 푸시 알림을 쉽게 구현하고 관리할 수 있는 서비스 워커 기능을 제공.
- 서비스 워커 파일을 생성
- 서비스 워커 등록 : 서비스 워커 파일을 웹 루트 경로에 추가하고, 해당 서비스 워커를 등록해줘야한다.
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[CS 스터디] 혼자 공부하는 컴퓨터구조+운영체제 Chapter 4~5]]></title>
            <link>https://velog.io/@hi_young/CS-%EC%8A%A4%ED%84%B0%EB%94%94-%ED%98%BC%EC%9E%90-%EA%B3%B5%EB%B6%80%ED%95%98%EB%8A%94-%EC%BB%B4%ED%93%A8%ED%84%B0%EA%B5%AC%EC%A1%B0%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-Chapter-47</link>
            <guid>https://velog.io/@hi_young/CS-%EC%8A%A4%ED%84%B0%EB%94%94-%ED%98%BC%EC%9E%90-%EA%B3%B5%EB%B6%80%ED%95%98%EB%8A%94-%EC%BB%B4%ED%93%A8%ED%84%B0%EA%B5%AC%EC%A1%B0%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-Chapter-47</guid>
            <pubDate>Wed, 15 May 2024 07:43:49 GMT</pubDate>
            <description><![CDATA[<h1 id="chapter-04-cpu의-작동-원리">Chapter 04 CPU의 작동 원리</h1>
<h2 id="04-1-alu와-제어장치">04-1 ALU와 제어장치</h2>
<h3 id="aluarithmetic-and-logical-unit">ALU(Arithmetic and Logical Unit)</h3>
<ul>
<li>산술논리연산장치</li>
<li>CPU에서 계산하는 부품</li>
<li>레지스터를 통해 피연산자를 받아들이고, 제어장치로부터 수행할 연산을 알려주는 제어 신호를 받아들인다.</li>
<li>레지스터와 제어장치로부터 받아들인 피연산자와 제어 신호로 산술 연산, 논리 연산 등 다양한 연산을 수행.</li>
<li>플래그<ul>
<li>연산 결과에 대한 추가적인 상태 정보</li>
<li>CPU가 프로그램을 실행하는 도중 반드시 기억해야하는 참고 정보</li>
<li>플래그 레지스터에 따로 저장됨.</li>
<li>플래그 종류<ul>
<li>부호 플래그: 연산 결과의 부호</li>
<li>제로 플래그 : 연산 결과가 0인지</li>
<li>캐리 플래그 : 연산 결과가 올림수나 빌림수가 발생하는지</li>
<li>오버플로우 플래그 : 오버플로우가 발생했는지</li>
<li>인터럽트 플래그 : 인터럽트가 가능한지</li>
<li>슈퍼바이저 플래그 : 커널 모드로 실행 중인지, 사용자 모드로 실행 중인지</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>📝 CPU의 ALU란 연산장치이며, 레지스터와 제어장치로부터 정보를 받아들여서 연산을 수행한다. 그 중 플래그라는 정보도 함께 내보내는데 연산 결과에 대한 추가적인 정보를 가지고 있으며 플래그 레지스터에 따로 저장되는 정보이다.</p>
<h3 id="제어장치">제어장치</h3>
<ul>
<li>제어 신호를 내보내고, 명령어를 해석하는 부품</li>
<li>제어 신호 : 컴퓨터 부품들을 관리하고 작동시키기 위한 일종의 전기 신호.</li>
</ul>
<p><strong>제어장치가 받아들이는 정보</strong></p>
<ul>
<li>클럭 신호</li>
</ul>
<p>클럭 : 컴퓨터의 시간 단위</p>
<ul>
<li>해석해야 할 명령어</li>
</ul>
<p>CPU가 해석해야할 명령어는 명령어 레지스터에 저장되며 제어장치는 이곳에서 명령어를 받아들이고 해석한다.</p>
<ul>
<li>플래그 레지스터 속 플래그</li>
</ul>
<p>플래그는 ALU 연산에 대한 추가적인 상태 정보이기 때문에 이를 참고하여 제어 신호를 발생시킨다.</p>
<ul>
<li>시스템 버스(제어 버스)로 전달된 제어 신호</li>
</ul>
<p><strong>제어장치가 내보내는 정보</strong></p>
<ul>
<li>CPU 외부에 전달하는 제어 신호<ul>
<li>메모리</li>
<li>입출력장치</li>
</ul>
</li>
<li>CPU 내부에 전달하는 제어 신호<ul>
<li>ALU에 전달 : 수행할 연산을 지시하기 위해</li>
<li>레지스터에 전달 : 레지스터 간에 데이터를 이동 or 레지스터에 저장된 명령어를 해석하기 위해</li>
</ul>
</li>
</ul>
<p>📝 CPU의 주요 장치는 ALU, 제어장치, 레지스터 총 3가지이며 이 중 제어장치가 가장 정교하게 설계된 부품이다. 사실상 명령을 내리는 부품이 바로 이친구. 이 제어장치가 받아들이는 정보는 클럭, 명령어 레지스터, 플래그 레지스터, 제어 신호. 그리고 제어장치가 내보내는 정보는 CPU 내부와 외부로 나뉜다. CPU 외부는 메모리, 입출력장치로 내부는 ALU, 레지스터다.</p>
<h2 id="04-2-레지스터">04-2 레지스터</h2>
<p>레지스터의 종류와 역할, 명령어가 처리되는 과정 이해해보기</p>
<h3 id="반드시-알아야-할-레지스터">반드시 알아야 할 레지스터</h3>
<ul>
<li>프로그램 카운터 : 메모리에서 읽어 들일 명령어의 주소를 저장</li>
<li>명령어 레지스터 : 방금 메모리에서 읽어들인 명령어를 저장하는 레지스터</li>
<li>메모리 주소 레지스터 : 메모리의 주소를 저장하는 레지스터</li>
<li>메모리 버퍼 레지스터 : 데이터와 명령어를 저장하는 레지스터</li>
</ul>
<p>프로그램 카운터에 읽어들일 명령어의 주소가 입력되면 → 메모리 주소 레지스터에 해당 주소가 입력되고 → 해당 주소에 있는 데이터와 명령어어를 읽어들인 다음 → 명령어 레지스터에 저장해서 제어신호를 발생시킴.</p>
<p>→ CPU가 메모리 속 프로그램을 순차적으로 읽어 들이고 실행해 나갈 수 있는 이유는 CPU 속 프로그램 카운터가 꾸준히 증가하기 때문.</p>
<h3 id="특정-레지스터를-이용한-주소-지정-방식1--스택-주소-지정-방식">특정 레지스터를 이용한 주소 지정 방식(1) : 스택 주소 지정 방식</h3>
<ul>
<li>스택 포인터 : 스택의 꼭대기를 가리키는 레지스터</li>
<li>메모리의 스택 영역에 마지막에 저장한 값의 위치를 저장하는 레지스터</li>
</ul>
<h3 id="특정-레지스터를-이용한-주소-지정-방식2--변위-주소-지정-방식">특정 레지스터를 이용한 주소 지정 방식(2) : 변위 주소 지정 방식</h3>
<ul>
<li>오퍼랜드 필드의 값과 특정 레지스터의 값을 더하여 유효 주소를 얻어내는 주소 지정 방식</li>
</ul>
<p><strong>상대 주소 지정 방식</strong></p>
<ul>
<li>오퍼랜드와 프로그램 카운터의 값을 더하여 유효 소를 얻는 방식.</li>
<li>만약 프로그램 카운터의 값이 +3이면 메모리에서 3번째 주소를 가져오고, -3이면 메모리에서 뒤로 3번째 주소값을 가져오는 방식</li>
</ul>
<p><strong>베이스 레지스터 주소 지정 방식</strong></p>
<ul>
<li>오퍼랜드와 베이스 레지스터의 값을 더하여 유효 주소를 얻는 방식</li>
<li>베이스 레지스터가 200이고, 오퍼랜드가 50이면 250번지에 있는 메모리 주소를 가져옴.</li>
</ul>
<p>📝 레지스터는 CPU 내에 있는 작은 임시저장장치이다. 꼭 알아야 할 레지스터가 프로그램 카운터, 메모리 주소 저장, 메모리 버퍼, 명령어 레지스터 등이 있다. CPU에 명령어가 순차적으로 실행되는 것은 프로그램 카운터가 증가하기 때문이며 프로그램 카운터가 증가하면 해당 주소의 메모리값을 읽어들이면서 실행하게 된다.</p>
<p>특정 레지스터를 이용해서 주소를 지정하는 방식도 있는데 스택영역에서 들고오는 방식, 변위 주소를 사용하는 방식이 있다. 변위 주소는 상대 주소로 하는 방법, 베이스 레지스터를 사용해서 계산하는 방식도 있다.</p>
<h2 id="04-3-명령어-사이클과-인터럽트">04-3 명령어 사이클과 인터럽트</h2>
<h3 id="명령어-사이클">명령어 사이클</h3>
<ul>
<li>프로그램 속 각각의 명령어들은 일정한 주기가 반복되며 실행되는데 이 주기</li>
<li>인출 사이클 : 메모리에 있는 명령어를 CPU로 가지고 오는 단계</li>
<li>실행 사이클 : CPU로 가져온 명령어를 실행하는 단계<ul>
<li>제어장치가 명령어 레지스터에 담긴 값을 해석하고 제어 신호를 발생시키는 단계</li>
</ul>
</li>
<li>간접 사이클 : 명령어를 실행하기 위해서는 메모리 접근을 한 번 더 해야하는 단계</li>
</ul>
<p>→ CPU가 명령어를 실행하는 단계를 표현하면 인출 사이클(메모리에 있는 명령어를 CPU로 가져옴), 실행 사이클(CPU로 가져온 명령어를 실행하는 단계), 실행하다 메모리에 한번 더 접근해야하면 이를 간접 사이클이라고 한다.</p>
<h3 id="인터럽트">인터럽트</h3>
<ul>
<li>CPU의 작업을 방해하는 신호</li>
<li>인터럽트 종류는 크게 동기 인터럽트와 비동기 인터럽트가 있다.</li>
</ul>
<p><strong>동기 인터럽트(예외)</strong></p>
<ul>
<li>프로그래밍상의 오류와 같은 예외적인 상황에 마추졌을 때 발생하는 인터럽트</li>
</ul>
<p><strong>비동기 인터럽트(하드웨어 인터럽트)</strong></p>
<ul>
<li>입출력장치에 의해 발생하는 인터럽트</li>
<li>하드웨어 인터럽트</li>
</ul>
<p>CPU가 만약 프린트에 출력을 명령했다고 가정했을 때 프린트는 CPU다 속도가 느리기 때문에 완료되었는지 알려면 계속 polling하면 물어봐야한다. CPU 사이클 낭비다.</p>
<p>하드웨어 인터럽트를 사용하면 CPU는 프론터로부터 완료 인터럽트를 받을 때까지 다른 작업을 처리할 수 있다.</p>
<ul>
<li>하드웨어 인터럽트 처리 순서</li>
</ul>
<ol>
<li>입출력장치는 CPU에 인터럽트 요청 신호를 보낸다.</li>
<li>CPU는 실행 사이클이 끝나고 명령어를 인출하기 전 항상 인터럽트 여부를 확인</li>
<li>CPU는 인터럽트 요청을 확인하고 인터럽트 플래그를 통해 현재 인터럽트를 받아들일 수 있는지 여부를 확인</li>
<li>인터럽트를 받아들일 수 있으면 CPU는 지금까지의 작업을 백업</li>
<li>CPU는 인터럽트 벡터를 참조하여 인터럽트 서비스 루틴을 실행</li>
<li>인터럽트 서비스 루틴 실행이 끝나면 백업해둔 작업을 복구하여 실행을 재개</li>
</ol>
<p><strong>인터럽트 서비스 루틴</strong></p>
<ul>
<li>인터럽트가 발생했을 때 해당 인터럽트를 어떻게 처리하고 작동해야할지에 대한 정보로 이루어진 프로그램</li>
</ul>
<p>CPU가 인터럽트를 처리한다 → 인터럽트 서비스 루틴을 실행하고 본래 수행하던 작업으로 다시 되돌아온다라는 말과 같다. CPU가 인터럽트 서비스 루틴을 실행하려면 인터럽트 서비스 루틴의 시작 주소를 알아야 하는데, 이는 인터럽트 벡터를 통해 알 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/hi_young/post/01036bcc-7caa-4fbf-8e3d-d2d8a3ccf330/image.png" alt=""></p>
<p>📝
명령어 사이클은 인출, 실행, 간접, 인터럽트 사이클로 이루어져 있다.
인터럽트는 CPU의 작업을 방해하는 신호로 예외 인터럽트, 하드웨어 인터럽트 두 가지로 나늰다.
예외 인터럽트는 오류로 인한 작업 방해
하드웨어 인터럽트는 입출력장치에 의한 신호이다.
하드웨어 인터럽트는 CPU와 입출력장치간의 속도 차이가 나기 때문에 이를 효율적으로 실행하기 위한 방법
인터럽트 요청 신호 : CPU의 작업을 방해하는 신호
인터럽트 플래그 : CPU가 인터럽트 실행이 가능한지에 대한 정보가 담겨 있고
인터럽트 벡터 : 인터럽트 서비스 루틴의 메모리 주소가 들어있으며
인터럽트 서비스 루틴 : 인터럽트를 처리하는 프로그램</p>
<h1 id="chapter-05-cpu-성능-향상-기법">Chapter 05 CPU 성능 향상 기법</h1>
<h2 id="05-1-빠른-cpu를-위한-설계-기법">05-1 빠른 CPU를 위한 설계 기법</h2>
<h3 id="클럭">클럭</h3>
<ul>
<li>컴퓨터 부품들은 클럭 신호에 맞춰 일사불란하게 움직인다.</li>
<li>CPU는 명령어 사이클이라는 정해진 흐름에 맞춰 명령어들을 실행한다.</li>
<li>클럭 속도가 높아지면 CPU는 명령어 사이클을 더 빠르게 반복할 것이고, 다른 부품들도 그에 발맞춰 더 빠르게 작동할 것이다.</li>
<li>클럭 속도를 무작정 높이면 발열 문제가 심각해지기 때문에 클럭 속도만으로 CPU의 성능을 올리는 것에는 한계가 있다.</li>
</ul>
<h3 id="코어와-멀티코어">코어와 멀티코어</h3>
<ul>
<li>코어 : 명령어를 실행하는 부품</li>
<li>CPU : 명령어를 실행하는 부품을 여러 개 포함하는 부품</li>
<li>멀티코어 CPU/멀티코어 프로세서 : 코어를 여러 개 포함하고 있는 CPU</li>
<li>코어를 무작정 늘린다고 CPU의 성능이 올라가는 것은 아니다.</li>
<li>코어마다 처리할 연산이 적절히 분배되지 않는다면 코어 수에 비례하여 연산 속도가 증가하지 않는다.</li>
<li>코어마다 처리할 명령어들을 얼마나 적절하게 분배하느냐이고 그에 따라서 연산 속도는 크게 달라진다.</li>
</ul>
<h3 id="스레드와-멀티-스레드">스레드와 멀티 스레드</h3>
<ul>
<li>스레드 : 실행 흐름의 단위</li>
<li>스레드에는 하드웨어적 스레드와 소프트웨어적 스레드가 있다.</li>
</ul>
<p><strong>하드웨어적 스레드</strong></p>
<ul>
<li>하나의 코어가 동시에 처리하는 명령어 단위</li>
<li>멀티스레드 프로세스/멀티스레드 CPU : 하나의 코어로 여러 명령어를 동시에 처리하는 CPU</li>
</ul>
<p><strong>소프트웨어적 스레드</strong></p>
<ul>
<li>하나의 프로그램에서 독립적으로 실행되는 단위</li>
</ul>
<p><strong>멀티스레드 프로세서</strong></p>
<ul>
<li>멀리스레드 프로세서는 실제로 설계하는 일은 매우 복잡하지만, 가장 큰 핵심은 레지스터다.</li>
<li>하나의 코어로 여러 명령어를 동시에 처리하도록 만들려면 프로그램 카운터, 스택 포인터, 메모리 버퍼 레지스터, 메모리 주소 레지스터와 같이 하나의 명령어를 처리하기 위해 꼭 필요한 레지스터를 여러 개 가지고 있으면 된다.</li>
<li>논리 프로세스 : 프로그램 입장에서는 4코어 8스레드 CPU는 마치 8개의 CPU가 있는 것처럼 느껴지기 때문에 논리 프로세스라고 부르기도 한다.</li>
</ul>
<p>📝
CPU의 성능을 결정하는 것들을 배웠다.
클럭이란 마치 cpu의 심장박동 소리 같달까. 클럭의 신호에 맞춰 CPU가 동작하기 때문에 클럭이 빠를수록 CPU도 더 빠르게 동작할 것이다. 그런데 클럭이 높으면 발열 이슈가 있기 떄문에 한계가 있다.
코어는 명령어를 처리하는 부품이다. CPU를 명령어를 실행하는 부품이라고 하는데 사실상 코어를 의미하는 것이며 CPU는 코어의 집합이라고 볼 수 있다. 2코어라고 하면 명령어를 실행하는 부품이 2개인것이다. 그런데 코어의 수를 늘린다고 성능이 마냥 좋아지진 않는다.
스레드는 하드웨어적 스레드(CPU), 소프트웨어적 스레드 두 가지로 나뉜다. 하드웨어적 스레드는 코어에서 동시에 처리할 수 있는 명령어 단위를 의미하는데, 하나의 코어로 여러 명령어를 처리할 수 있는 CPU를 멀티스레드 프로세스라고 한다.
코어는 명령어를 실행하는 부품인거고, 그 안에서 처리하는 인력의 수를 스레드라고 한다.
소프트웨어적 스레드는 프로그램 내에서 실행되는 단위로, javascript 같은 경우는 싱글스레드로 명령어를 실행하는 단위가 1개라 한번에 하나의 명령어만 실행 가능하다.
멀티스레드 프로세스의 핵심은 레지스터라 스레드 수만큼 레지스터를 가지고 있다.</p>
<h2 id="05-2-명령어-병렬-처리-기법">05-2 명령어 병렬 처리 기법</h2>
<h3 id="명령어-파이프라인">명령어 파이프라인</h3>
<ul>
<li>명령어 처리 과정을 클럭 단위로 나누면<ul>
<li>명령어 인출</li>
<li>명령어 해석</li>
<li>명령어 실행</li>
<li>결과 저장</li>
</ul>
</li>
<li>같은 단계가 겹치지 않으면 CPU는 각 단계를 동시에 실행할 수 있다.
<img src="https://velog.velcdn.com/images/hi_young/post/fd80e7ce-5f16-4df3-9de3-0808001be5be/image.png" alt=""></li>
<li>명령어 파이프라이닝 : 공장 생산 라인과 같이 명령어들을 명령어 파이프라인에 넣고 동시에 처리하는 기법</li>
</ul>
<p><strong>파이프라인 위험</strong></p>
<ul>
<li>파이프라이닝은 높은 성능을 가져오기는 하지만, 특정 상황에서는 성능 향상에 실패한다.</li>
<li>파이프라인 위험에는 데이터 위험, 제어 위험, 구조적 위험 3가지가 있다.</li>
<li>데이터 위험 : 데이터 의존적인 두 명령어를 무작정 동시에 실행하려고 하면 파이프라인이 제대로 작동하지 않는 것</li>
<li>제어 위험 : 분기 등으로 인한 프로그램 카운터의 갑잡스러운 변화에 의해 발생한다.</li>
<li>구조적 위험(자원 위험) : 서로 다른 명령어가 동시에 ALU, 레지스터 등과 같은 CPU 부품을 사용하려고 할 때 발생</li>
</ul>
<h3 id="슈퍼스칼라">슈퍼스칼라</h3>
<ul>
<li>CPU 내부에 여러 개의 명령어 파이프라인을 포함한 구조</li>
<li>명령어 파이프라인을 하나만 두는 것이 마치 공장 생산 라인을 한 개 두는 것과 같다면, 슈퍼스칼라는 공장 생산 라인을 여러 개 두는 것과 같다.</li>
<li>이론적으로는 파이프라인 개수에 비례하여 프로그램 처리 속도가 빨라야져야 하는게 맞으나 하나의 파이프라인을 사용할 때 보다 데이터 위험, 제어 위험, 자원 위험을 피하기가 더욱 까다롭다.</li>
<li>그래서 더욱 더 고도로 설계되어야함.</li>
</ul>
<h3 id="비순차적-명령어-처리">비순차적 명령어 처리</h3>
<ul>
<li>대부분의 CPU가 차용하는 기법</li>
<li>명령어를 순차적으로만 실행하지 않고 순서를 바꿔 실행하도 무방한 명령어를 먼저 실행하여 명령어 파이프라인이 멈추는 것을 방지하는 기법</li>
<li>명령어들이 어떤 명령어와 데이터 의존성을 가지고 있는지, 순서를 바꿔 실행할 수 있는 명령어에는 어떤 것들이 있는지를 판단할 수 있어야 한다.</li>
</ul>
<p><strong>👩🏻‍💻</strong>
코어, 스레드도 중요하지만 명령어를 병렬적으로 잘 처리하는 것도 중요하다. 명령어를 처리할 때 인출, 해석, 실행, 저장 4가지 단계가 있으며 이 단계는 겹치지 않으면 동시에 실행이 가능하다. 그래서 공장 생산 라인처럼 명령어들을 파이프라인에 넣어서 동시에 처리하는 기법을 명령어 파이프라이닝이라고 한다.
파이프라인이 여러개인것을 슈퍼스칼라라고 하며
대부분의 CPU가 차용하는 기법인 비순차적 명령어 처리는 명령어 파이프라인이 멈추지 않도록 의존성이 없는 명령어를 먼저 처리하는 기법이다.</p>
<h2 id="05-3-cisc와-risc">05-3 CISC와 RISC</h2>
<h3 id="명령어-집합">명령어 집합</h3>
<ul>
<li>CPU제조사마다 명령어의 세세한 생김세, 명령어로 할 수 있는 연산, 주소 지정 방식 등이 차이가 있다.</li>
<li>명령어 집합(ISA) : CPU가 이해할 수 있는 명령어들의 모음, CPU의 언어이자 하드웨어가 소프트웨어를 어떻게 이해할지에 대한 약속.</li>
<li>CPU마다 ISA가 다를 수 있다.</li>
<li>같은 소스코드로 만들어진 같은 프로그램이라 할지라도 ISA가 다르면 CPU가 이해할 수 있는 명령어도 어셈블리어도 달라진다.</li>
</ul>
<h3 id="cisccomplex-instruction-set-computer">CISC(Complex Instruction Set Computer)</h3>
<ul>
<li>x86, x86-64는 대표적인 CISC 기반의 ISA</li>
<li>복잡하고 다양한 수의 가변 길이 명령어 집합을 활용</li>
<li>그래서 적은 수의 명령어로도 프로그램을 실행할 수 있다.</li>
<li>프로그램을 실행하는 명령어 수가 적다는 말은 &#39;컴파일된 프로그램의 크기가 작다&#39;는 것을 의미</li>
<li>같은 소스를 컴파일해도 CPU마다 생성되는 실행 파일의 크기가 다를 수 있다.</li>
<li>명령어 수행 시간이 길고 가지각색이기 때문에 파이프라인이 효율적으로 명령어를 처리할 수 없음.</li>
<li>대다수의 복잡한 명령어는 사용 빈도가 낮음</li>
</ul>
<h3 id="riscreduced-instruction-set-computer">RISC(Reduced Instruction Set Computer)</h3>
<ul>
<li>고정 길이 명령어를 사용</li>
<li>명령어 파이프라이닝에 최적화</li>
</ul>
<p>👩‍💻
CPU 아키텍처에 따라 이해할 수 있는 언어가 다르다.
CPU가 이해할 수 있는 명령어들의 모음을 ISA.
명령어는 고정 길이여야 명령어 파이프라인에 유리하며 그 예시로 RISC가 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 0509]]></title>
            <link>https://velog.io/@hi_young/TIL-0509</link>
            <guid>https://velog.io/@hi_young/TIL-0509</guid>
            <pubDate>Wed, 08 May 2024 23:53:18 GMT</pubDate>
            <description><![CDATA[<h2 id="query-parameter">query parameter</h2>
<ul>
<li>URL의 물음표 뒤에 key=value 형식의 파라미터.</li>
<li>여러개 사용할 때는 &amp;로 이어 붙여주면 된다.</li>
</ul>
<h3 id="사용-용도">사용 용도</h3>
<ul>
<li>Tracking</li>
</ul>
<p><code>?utm_source=social</code></p>
<ul>
<li>Reordering</li>
</ul>
<p><code>?sort=lowest-price</code></p>
<ul>
<li>Filtering</li>
</ul>
<p><code>?colur=blue</code></p>
<ul>
<li>Identifying</li>
</ul>
<p><code>?categoryId=123</code></p>
<ul>
<li>Paginating</li>
</ul>
<p><code>?page=2</code></p>
<ul>
<li>Searching</li>
</ul>
<p><code>?search=hihi</code></p>
<ul>
<li>Translating</li>
</ul>
<p><code>?lang=kr</code></p>
<ul>
<li>쿼리 파라미터로 제어하는 데이터들은 페이지내에서 큰 변화를 일으키기 보다는 값에 따라 컨텐츠의 정렬이나 표시형태가 달라질 뿐이다.</li>
</ul>
<h3 id="path">path</h3>
<ul>
<li>어떤 자원의 위치를 특정해서 보여줘야 할 때 사용.</li>
</ul>
<h2 id="nextjs-dynamic-routes">Next.js Dynamic Routes</h2>
<ul>
<li>path에 들어올 이름이 동적으로 바뀌어야할 때 사용.</li>
</ul>
<h3 id="convention">Convention</h3>
<ul>
<li>파일 또는 폴더 이름을 브라켓으로 감싸서 사용할 수 있다.</li>
</ul>
<p><code>[id]</code></p>
<h3 id="example">Example</h3>
<p>블로그는 상세페이지에 따라 slug가 달라지기 때문에 아래 폴더구로조 dynamic routing 처리를 할 수 있다.</p>
<p><code>pages/blog/[slug].js</code></p>
<h3 id="catch-all-segments">Catch-all Segments</h3>
<p><code>[...segment]</code> </p>
<p>path에 변수가 여러개 일 때 브라켓 안에서 …을 통해 생략해서 사용할 수 있다.</p>
<h3 id="optional-catch-all-segment">Optional Catch-all Segment</h3>
<p>더블 브라켓을 사용해서 segment를 옵션적으로 처리할 수도 있다.</p>
<p><code>[[...segment]]</code></p>
<h3 id="reference">Reference</h3>
<p><a href="https://nextjs.org/docs/pages/building-your-application/routing/dynamic-routes">https://nextjs.org/docs/pages/building-your-application/routing/dynamic-routes</a></p>
<p><a href="https://velog.io/@timosean/Web-Path-variable-Query-String-%EA%B7%B8%EB%A6%AC%EA%B3%A0-SEO">https://velog.io/@timosean/Web-Path-variable-Query-String-그리고-SEO</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[TIL] 0508]]></title>
            <link>https://velog.io/@hi_young/TIL-0508</link>
            <guid>https://velog.io/@hi_young/TIL-0508</guid>
            <pubDate>Tue, 07 May 2024 23:39:10 GMT</pubDate>
            <description><![CDATA[<h3 id="version-naming-활용">version naming 활용</h3>
<ul>
<li>계속해서 업데이트 될 가능성이 있는 네이밍에는 version으로 명명하는 것이 편리하다.</li>
<li>sign in 프로세스가 업데이트되면서 기존 sign in url과 신규 버전의 url이 함께 공존해야하는 상황이라 <code>sign-in/v1</code>, <code>sign-in/v2</code> 형식으로 웹 url을 활용하는 것이 네이밍 고민할 필요도 없고 명확하다.</li>
</ul>
<h3 id="powershell-명령어-해석">PowerShell 명령어 해석</h3>
<p>Get-CimInstance -Query &quot;SELECT * from Win32_Process WHERE name LIKE &#39;**.exe&#39;&quot; | Select-Object -ExpandProperty CommandLine</p>
<ul>
<li><p>PowerShell
ms에서 개발한 CLI, 텍스트 명령어로 운영체제와 상호작용할 수 있는 프로그램.</p>
</li>
<li><p>electron shell
<a href="https://www.electronjs.org/docs/latest/api/shell">https://www.electronjs.org/docs/latest/api/shell</a></p>
</li>
<li><p>Get-CimInstance
powershell에서 사용되는 cmdlet 중 하나로 CIM(Common Information Model) 클래스의 인터섵스를 쿼리하고 해당 인스턴스의 정보를 가져올 수 있다.
현재 실행 중인 프로세스의 목록을 가져오는 경우 아래와 같으 명령할 수 있다.</p>
<pre><code>Get-CimInstance -ClassName Win32_Process</code></pre></li>
<li><p>Select-Object
powershell에서 사용되는 cmdlet 및 옵션으로 입력된 개체의 속성을 선택하거나 변환하는데 사용. </p>
</li>
<li><p>-ExpandProperty
Select-Object와 함께 사용되는 옵션으로 하위 속성을 확장하여 출력할 수 있다. 그래서 Command Line을 가져오는 경우 해당 옵션을 추가 후 가져올 수 있음</p>
<pre><code>Get-CimInstance -ClassName Win32_Process | Select-Object -ExpandProperty CommandLine</code></pre></li>
<li><p>cmdlet
PowerShell에서 사용할 수 있는 명령어로 동사-명사 조합으로 되어있음.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CS 스터디] 혼자 공부하는 컴퓨터구조+운영체제 Chapter 1~3]]></title>
            <link>https://velog.io/@hi_young/CS-%EC%8A%A4%ED%84%B0%EB%94%94-%ED%98%BC%EC%9E%90-%EA%B3%B5%EB%B6%80%ED%95%98%EB%8A%94-%EC%BB%B4%ED%93%A8%ED%84%B0%EA%B5%AC%EC%A1%B0%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-Chapter-14</link>
            <guid>https://velog.io/@hi_young/CS-%EC%8A%A4%ED%84%B0%EB%94%94-%ED%98%BC%EC%9E%90-%EA%B3%B5%EB%B6%80%ED%95%98%EB%8A%94-%EC%BB%B4%ED%93%A8%ED%84%B0%EA%B5%AC%EC%A1%B0%EC%9A%B4%EC%98%81%EC%B2%B4%EC%A0%9C-Chapter-14</guid>
            <pubDate>Mon, 06 May 2024 07:03:13 GMT</pubDate>
            <description><![CDATA[<h1 id="chapter-01-컴퓨터-구조-시작하기">Chapter 01 컴퓨터 구조 시작하기</h1>
<h2 id="01-1-컴퓨터-구조를-알아야-하는-이유">01-1 컴퓨터 구조를 알아야 하는 이유</h2>
<ul>
<li>컴퓨터 구조를 이해하고 있다면 문제 상황을 빠르게 진단할 수 있고, 문제 해결의 실마리를 다양하게 찾을 수 있다.</li>
<li>컴퓨터 구조를 이해하면 문법만으로 알기 어려운 성능/용량/비용을 고려하며 개발할 수 있다.</li>
</ul>
<p>📝 웹 개발을 할 때는 브라우저에서 많은 것들을 처리해주지만 앱 개발 같은 경우 OS 위에서 돌아가기 때문에 컴퓨터구조, 운영체제는 필수로 알고 있어야 한다. 웹 개발 또한 운영체제 - 브라우저 위에서 동작하기 때문에 문제 발생시 더 좋은 해결책을 고민할 수 있을 것이다.</p>
<h2 id="01-2-컴퓨터-구조의-큰-그림">01-2 컴퓨터 구조의 큰 그림</h2>
<h3 id="컴퓨터가-이해하는-정보">컴퓨터가 이해하는 정보</h3>
<ul>
<li>컴퓨터는 0과 1로 표현된 정보만 이해</li>
<li>데이터 : 컴퓨터가 이해하는 숫자, 문자, 이미지, 동영상 같은 정적인 정보</li>
<li>명령어 : 데이터를 움직이고 컴퓨터를 작동시키는 정보</li>
</ul>
<h3 id="컴퓨터의-4가지-핵심-부품">컴퓨터의 4가지 핵심 부품</h3>
<ul>
<li><p>CPU : 컴퓨터의 두뇌로 CPU는 메모리에 저장된 명령어를 읽어 들이고, 읽어 들인 명령어를 해석하고, 실행하는 부품.</p>
<ul>
<li>산술논리연산장치(ALU) : 계산기</li>
<li>레지스터 : CPU 내부의 임시 저장 장치</li>
<li>제어장치 : 제어 신호라는 전기 신호를 내보내고 명령어를 해석하는 장치</li>
</ul>
</li>
<li><p>주기억장치(메모리) : 현재 실행되는 프로그램의 명령어와 데이터를 저장하는 부품.</p>
<ul>
<li>프로그램이 실행되기 위해서는 반드시 메모리에 저장되어 있어야 한다.</li>
<li>메모리는 현재 실행되는 프로그램의 명령어와 데이터를 저장한다.</li>
<li>메모리에 저장된 값의 위치는 주소로 알 수 있다.</li>
</ul>
</li>
<li><p>보조기억장치 : 전원이 꺼져도 보관될 프로그램을 저장하는 부품.</p>
<ul>
<li>하드 디스크, SSD, USB, DVD, CD-ROM</li>
</ul>
</li>
<li><p>입출력장치 : 마이크, 스피커, 프린터, 마우스, 키보드처럼 컴퓨터 외부에 연결되어 컴퓨터 내부와 정보를 교환하는 장치.</p>
</li>
<li><p>메인보드와 시스템 버스 : 네 가지 핵심 부품은 메인보드에 연결되어 시스템 버스를 통해 서로 정보 또는 데이터를 주고 받는다.</p>
<ul>
<li>메인보드 : 여러 컴퓨터 부품을 부착할 수 있는 슬롯과 연결 단자가 있는 판.</li>
<li>시스템 버스 : 메인보드에 연결된 부품들이 서로 정보를 교환할 수 있는 통로.</li>
</ul>
</li>
</ul>
<p>📝 컴퓨터가 이해하는 정보는 0과 1 binary data 뿐이다. 정적인 정보들을 담는 데이터와 이 데이터들을 움직이고 컴퓨터를 동작시키는 명령어로 두개로 나뉜다. 컴퓨터의 핵심 4가지 부품은 CPU, 메모리, 보조기억장치, 입출력장치 4가지이다.</p>
<h1 id="chapter-02-데이터">Chapter 02 데이터</h1>
<h2 id="02-1-0과-1로-숫자를-표현하는-방법">02-1 0과 1로 숫자를 표현하는 방법</h2>
<h3 id="정보-단위">정보 단위</h3>
<ul>
<li><p>비트(bit) : 0과 1을 나타내는 가장 작은 정보 단위</p>
</li>
<li><p>단위</p>
<ul>
<li>1byte = 8bit</li>
<li>1kB = 1000byte</li>
<li>1MB = 1000kB</li>
<li>1GB = 1000MB</li>
<li>1TB = 1000GB</li>
</ul>
</li>
<li><p>워드(word) : CPU가 한 번에 처리할 수 있는 데이터의 크기</p>
<ul>
<li>인텔의 x86 CPU는 32비트 워드, x64 CPU는 64비트 워드</li>
<li>✔️ 32비트 64비트 프로그램 차이는?<h3 id="이진법">이진법</h3>
</li>
</ul>
</li>
<li><p>0과 1만으로 모든 숫자를 표현하는 방법</p>
</li>
<li><p>양수인지 음수인지 구분하기 위해 플래그를 사용.</p>
<h3 id="십육진법">십육진법</h3>
</li>
<li><p>15를 넘어가는 시점에 자리 올림을 하는 숫자 표현 방식</p>
</li>
<li><p>0<del>9, A</del>F 정보로 표현</p>
</li>
<li><p>이진수 -&gt; 십육진수, 십육진수 -&gt; 이진수로 변환하기 쉽기 때문에 사용.</p>
</li>
</ul>
<p>📝 bit는 가장 작은 정보 표현단위로 0과 1을 나타낸다. bit -&gt; byte -&gt; kB -&gt; MB -&gt; GB -&gt; TB 순으로 정보 단위가 올라간다. 워드는 CPU가 한번에 처리할 수 있느 데이터의 크기로 보통 32비트, 64비트 두가지로 나누어지며 64비트 성능이 더 좋다.</p>
<h2 id="02-2-0과-1로-문자를-표현하는-방법">02-2 0과 1로 문자를 표현하는 방법</h2>
<h3 id="문자-집합과-인코딩">문자 집합과 인코딩</h3>
<ul>
<li>문자 집합<ul>
<li>컴퓨터가 인식하고 표현할 수 있는 문자의 모음</li>
<li>문자 집합에 없는 문자는 이해할 수 없음.</li>
</ul>
</li>
<li>인코딩 : 문자를 0과 1로 변환하는 과정</li>
<li>디코딩 : 0과 1로 표현된 문자 코드를 사람이 읽을 수 있는 문자로 변환하는 과정</li>
</ul>
<h3 id="아스키-코드">아스키 코드</h3>
<ul>
<li>초창기 문자 집합 중 하나</li>
<li>총 128개의 문자를 표현할 수 있음. 문자에 1:1로 맵핍된 십진수로 변환 후 이진수로 변환되어 인코딩 됨.</li>
<li>한글을 표현할 수 없음.</li>
</ul>
<h3 id="euc-kr">EUC-KR</h3>
<ul>
<li>알파벳을 쭉 이어 쓰면 단어가 되는 영어와 달리, 한글은 음절 하나하나가 초성, 중성, 종성으로 이루어져 있음.</li>
<li>한글같은 경우는 완성형과 조합형이 존재<ul>
<li>완성형 : 완성된 하나의 글자에 고유한 코드를 부여하는 인코딩 방식</li>
<li>조합형 : 초성, 중성, 종성 각각에 코드를 부여해서 합하는 방식</li>
</ul>
</li>
<li>EUC-KR은 완성형 인코딩 방식이라 문자 집합에 정의되지 않은 쀍 이런 문자는 깨지는 문제가 발생.</li>
</ul>
<h3 id="유니코드와-utf-8">유니코드와 UTF-8</h3>
<ul>
<li>또 하나 문제는 각 나라별로 인코딩 방식이 다르다면, 다국어 지원시 각 나라별로 다른 인코딩 방식을 적용해야 하는 번거로움이 있다.</li>
<li>유니코드는 다양한 한글을 초함하며, 대부분 나라의 문자, 특수문자, 이모티콘까지도 코드로 표현할 수 있는 통일된 문자 집합이다.</li>
<li>유니코드는 다양한 방법으로 인코딩할 수 있는데 UTF-8, UTF-16, UTF-32 등이 있다.</li>
</ul>
<p>📝 컴퓨터가 인식할 수 있는 문자 모음을 문자 집합이라고 하며, 문자를 0과 1로 변환시키는 과정을 인코딩, 사람이 읽을 수 있는 문자로 다시 변환시키는 작업을 디코딩이라고 한다. 초창기 문자 집합으로 아스키코드가 있었으나 128가지만 인식할 수 있다는 한계가 있었고, 그 다음 한글에 맞춰서 나온 문자집합인 EUC-KR은 완성형 인코딩 방식이라 모든 한글을 인식할 수 없다는 한계가 있었다. 게다가 각 나라별로 다른 인코딩 방식은 다국어 지원시 매우 번거로울수밖에 없는 작업이다. 그래서 나온게 유니코드이며 여러 나라의 문자를 광범위하게 통일된 형태로 표현할 수 있는 문자 집합이다. 이 유니코드를 인코딩하는 방식은 여러가지가 있다.</p>
<h1 id="chapter-03-명령어">Chapter 03 명령어</h1>
<h2 id="03-1-소스-코드와-명령어">03-1 소스 코드와 명령어</h2>
<h3 id="고급-언어와-저급-언어">고급 언어와 저급 언어</h3>
<ul>
<li>고급 언어 : 사람을 위한 언어(프로그래밍 언어)</li>
<li>저급 언어 : 컴퓨터가 이해하고 실행할 수 있는 언어<ul>
<li>기계어 : 0과 1 명령어 비트로 이루어진 언어</li>
<li>어셈블리어 : 기계어를 읽기 편한 형태로 번역한 저급 언어</li>
</ul>
</li>
</ul>
<h3 id="컴파일-언어와-인터프리터-언어">컴파일 언어와 인터프리터 언어</h3>
<ul>
<li><p>고급언어가 저급언어로 변환되는 방법</p>
</li>
<li><p>컴파일 방식
컴파일 언어 : 컴파일러에 의해 소스 코드 전체가 저급 언어로 변환된어 실행되는 고급 언어
컴파일 : 소스 코드 전체가 저급 언어로 변환되는 과정
컴파일러 : 컴파일을 수행해주는 도구
목적 언어 : 컴파일를 통해 저급언어로 변환된 코드</p>
</li>
<li><p>인터프리터 방식
인터프리터에 의해 소스코드가 한줄씩 실행되는 고급 언어
인터프리터 언어는 소스 코드를 한줄씩 실행하기 때문에 저급언어로 변환하는 시간을 기다릴 필요가 없다.</p>
</li>
<li><p>컴파일 언어와 인터프리터 언어 흑과 백 방식으로 구분할 수 없다.
하나의 프로그래밍 언어가 반드시 둘 중 하나의 방식만으로 작동하지 않고, 두 가지 방식이 합께 사용되는 경우가 많기 때문.</p>
</li>
<li><p>목적 파일 VS 실행 파일
컴파일러로 저급 언어로 변환된 파일을 목적 파일이라고 하며 링킹 과정을 거쳐야지 실행 파일이 될 수 있음.</p>
</li>
</ul>
<p>📝 프로그래밍 언어를 고급언어, binary data를 저급언어라고 한다. 고급언어에서 저급언어로 변환되는 방법은 2가지가 있으며 컴파일 방식, 인터프리터 방식이 있다. 컴파일 방식은 고급 언어를 컴파일러로 한번에 저급 언어로 변환하는 방식이며 중간에 오류가 있으면 도중에 중단되며 컴파일을 완료할 수 없다. 인터프리터 방식은 인터프리터가 한줄씩 읽으면서 실행하기 때문에 소스코드 중간에 오류가 있어도 그 전까지는 실행할 수 있으며 오류를 읽게되는 순간 중지된다.</p>
<h2 id="03-2-명령어의-구조">03-2 명령어의 구조</h2>
<h3 id="연산-코드와-오퍼랜드">연산 코드와 오퍼랜드</h3>
<ul>
<li>명령어는 연산 코드와 오퍼랜드로 구성</li>
<li>연산 코드(연산자)<ul>
<li>명령어가 수행할 연산</li>
</ul>
</li>
<li>오퍼랜드(피연산자)<ul>
<li>연산에 사용할 데이터가 저장된 위치</li>
<li>메모리 주소나 레지스터 이름이 담긴다.</li>
<li>주소 필드라고 부름.</li>
</ul>
</li>
</ul>
<h3 id="주소-지정-방식">주소 지정 방식</h3>
<ul>
<li>오퍼랜드 필드 안에 데이터 자체를 넣지 않고 주소를 넣는 이유는 오퍼랜드의 크기가 한정적이기 때문이다. 그래서 메모리 주소로 표현하게 되면 표현할 수 있는 데이터의 크기가 하나의 메모리 주소에 저장할 수 있는 공간만큼 커지게 된다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[개발 서적] 우아한 타입스크립트 with 리액트]]></title>
            <link>https://velog.io/@hi_young/%EA%B0%9C%EB%B0%9C-%EC%84%9C%EC%A0%81-%EC%9A%B0%EC%95%84%ED%95%9C-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-with-%EB%A6%AC%EC%95%A1%ED%8A%B8</link>
            <guid>https://velog.io/@hi_young/%EA%B0%9C%EB%B0%9C-%EC%84%9C%EC%A0%81-%EC%9A%B0%EC%95%84%ED%95%9C-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-with-%EB%A6%AC%EC%95%A1%ED%8A%B8</guid>
            <pubDate>Thu, 18 Apr 2024 14:16:08 GMT</pubDate>
            <description><![CDATA[<p>읽고 안까먹기 위해 정리 중</p>
<p>04.18</p>
<h1 id="1장">1장</h1>
<p>1.1 웹 개발의 역사</p>
<ul>
<li>90년대에는 마이크로소프트 ie, 넷스케이프 두 브라우저 양대산맥</li>
<li>초기 웹사이트는 단순하게 링크로 정보를 확인할 수 있는 수단이었음.</li>
<li>그러다 웹에서 다양한 콘텐츠를 표현하고 싶어서 10일만에 javascript라는 새로운 언어를 만들어서 도입</li>
<li>javascript는 계속 기능 추가되면서 업데이트하는데 막상 브라우저는 그 속도를 따라가지못해서 브라우저마다 js 호환성 문제가 많았음.</li>
<li>그래서 ECMA라는 국제 표준화 기구에다가 JS좀 표준화 시켜달라해서 만들어진게 ECMAScript.</li>
<li>웹사이트랑 웹앱플리케이션 차이<ul>
<li>웹사이트는 웹사이트 제공자가 일방적으로 정보 제공만 가능.</li>
<li>웹 앱플리케이션은 사용자와 쌍방향으로 상호작용 가능(채팅, 메일, 검색 등등)</li>
</ul>
</li>
<li>웹 앱플리케이션의 시발점이 구글 맵</li>
<li>구글 맵을 시작으로 웹 개발이 성장하고 프로젝트 규모가 점점 커지고 복잡해지기 시작</li>
<li>그러면서 컴포넌트 단위로 개발하기 시작하고, AJAX가 등장함.</li>
</ul>
<p>1.2 자바스크립트의 한계</p>
<ul>
<li>자바스크립트는 동적 언어임. 런타임 시점 즉, 프로그램이 실행되고 있을 때 타입이 정해지는데 개발자는 분명 number가 파라미터로 들어가는 함수를 만들었는데 누가 실수로 string 넣어두면 실행할 때 runtime error가 발생하게 되는 거임.</li>
<li>그러다 typescript 두둥장.</li>
<li>정적언어라 컴파일 할 때 타입에러 다 잡아줌.</li>
<li>에디터에서 인터페이스에 따른 자동완성 지원해줘서 짱편함.</li>
<li>코드 읽을 때 가독성이 좋아짐.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Next.js 배포 환경에 따른 여러 환경변수(.env) 설정]]></title>
            <link>https://velog.io/@hi_young/Next.js-%EB%B0%B0%ED%8F%AC-%ED%99%98%EA%B2%BD%EC%97%90-%EB%94%B0%EB%A5%B8-%EC%97%AC%EB%9F%AC-%ED%99%98%EA%B2%BD%EB%B3%80%EC%88%98.env-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@hi_young/Next.js-%EB%B0%B0%ED%8F%AC-%ED%99%98%EA%B2%BD%EC%97%90-%EB%94%B0%EB%A5%B8-%EC%97%AC%EB%9F%AC-%ED%99%98%EA%B2%BD%EB%B3%80%EC%88%98.env-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Tue, 14 Nov 2023 15:49:24 GMT</pubDate>
            <description><![CDATA[<h3 id="문제-상황">문제 상황</h3>
<ul>
<li>현재 프로젝트에서 배포 환경이 3가지(dev, stg, prod)</li>
<li>환경별로 다른 env로 중요 키값들을 설정하려고 함.<ul>
<li>dev -&gt; <code>.env.development</code></li>
<li>stg -&gt; <code>.env.staging</code></li>
<li>prod -&gt; <code>.env.produnction</code></li>
</ul>
</li>
<li>next.js에서는 환경 변수 설정이 되어 있음.<ul>
<li>개발 환경 -&gt; <code>.env.development</code></li>
<li>배포 환경 -&gt; <code>.env.production</code></li>
</ul>
</li>
<li>이미 설정되어있는 환경변수를 어떻게 오버라이드 할 수 있을까?</li>
</ul>
<h3 id="시도-1--amazon-ecs-task-definition에-environment-정의">시도 1 : Amazon ECS task definition에 environment 정의</h3>
<ul>
<li>이미 CICD가 설정되어있는 환경이다보니 백엔드 레포지토리를 참고해봤는데 env 값들을 task-definition에 정의하고 있었다.</li>
<li>해당 값들은 docker(가상환경)에 값이 설정되어 process.env로 접근 가능해진다고 함.</li>
<li>테스트로 dev 서버에 배포해서 콘솔 찍어봤더니 undefined만 찍힘</li>
</ul>
<p>-&gt; 백엔드는 node.js 환경이라 process.env로 키값에 바로 접근이 가능함.
-&gt; 그치만 클라이언트에서는 빌드될 때 번들링되면서 process.env의 값이 주입되기 때문에 가상환경에 있는 환경변수 값을 읽을수가 없음.</p>
<h3 id="시도-2--nextconfigjs의-env-옵션">시도 2 : <code>next.config.js</code>의 env 옵션</h3>
<ul>
<li>배포 환경별 스트립트 실행시 cross-env로 환경변수를 설정<pre><code>&quot;build:DEV&quot; : &quot;cross-env NEXT_PUBLIC_ENV=DEV next build&quot;
&quot;build:STG&quot; : &quot;cross-env NEXT_PUBLIC_ENV=STG next build&quot;
&quot;build:PROD&quot; : &quot;cross-env NEXT_PUBLIC_ENV=PROD next build&quot;</code></pre></li>
<li><code>next.config.js</code>에서 <code>process.env.NEXT_PUBLIC_ENV</code>로 배포 환경을 구분해서 env를 다시 설정.</li>
</ul>
<p>-&gt; 배포 환경별로 키값을 다르게 주입은 가능.
-&gt; 그러나 config 파일에 3가지 환경의 변수들이 들어가면서 코드가 복잡해짐.</p>
<h3 id="시도-3--env-cmd로-env-우선순위-수정">시도 3 : env-cmd로 env 우선순위 수정</h3>
<ul>
<li>env-cmd를 설치해서 스크립트 실행시 아래와 같이 사용<pre><code>&quot;build:DEV&quot; : &quot;env-cmd -f .env.development next build&quot;
&quot;build:STG&quot; : &quot;env-cmd -f .env.staging next build&quot;
&quot;build:PROD&quot; : &quot;env-cmd -f .env.production next build&quot;</code></pre></li>
<li>env-cmd -f로 실행된 .env파일이 최우선순위를 가지고 주입됨.</li>
</ul>
<h3 id="개념-정리">개념 정리</h3>
<ul>
<li>왜 프레임워크마다 환경변수시 붙는 prefix가 다르지? 그리고 왜 특정 prefix만 인식을 하는거지?</li>
<li>환경 변수는 무조건 .env?</li>
<li>왜 프레임워크마다 env 우선순위가 다른거지?</li>
<li>process.env의 값이 번들에 주입되는거면, 브라우저에서 js 파일 업로드 까보면 키값이 노출되는건가?</li>
<li>NODE_ENV는 왜 자동으로 개발 배포환경이 분리되는거지?</li>
<li>현재 private 레포에 env 파일을 push해서 사용하고 있는데 올바른 방법일까?</li>
<li>next.js에서 제공하는 환경변수 </li>
</ul>
<h3 id="reference">Reference</h3>
<ul>
<li><a href="https://jayprogram.tistory.com/89">https://jayprogram.tistory.com/89</a></li>
<li><a href="https://velog.io/@henrynoowah/Next.js-NEXT-JS-environment-variables">https://velog.io/@henrynoowah/Next.js-NEXT-JS-environment-variables</a></li>
<li><a href="https://www.daleseo.com/js-dotenv/">https://www.daleseo.com/js-dotenv/</a></li>
<li><a href="https://han-py.tistory.com/507">https://han-py.tistory.com/507</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[러닝 타입스크립트_Part 1 개념]]></title>
            <link>https://velog.io/@hi_young/%EB%9F%AC%EB%8B%9D-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8Part-1-%EA%B0%9C%EB%85%90</link>
            <guid>https://velog.io/@hi_young/%EB%9F%AC%EB%8B%9D-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8Part-1-%EA%B0%9C%EB%85%90</guid>
            <pubDate>Wed, 07 Jun 2023 05:50:04 GMT</pubDate>
            <description><![CDATA[<h1 id="chapter-1-자바스크립트에서-타입스크립트로">Chapter 1 자바스크립트에서 타입스크립트로</h1>
<h2 id="11-자바스크립트의-역사">1.1 자바스크립트의 역사</h2>
<ul>
<li>자바스크립트는 넷스케이프에서 10일만에 완성된 스크립트 언어</li>
</ul>
<h2 id="12-바닐라-자바스크립트의-함정">1.2 바닐라 자바스크립트의 함정</h2>
<p>보조적인 기능을 목적으로 만들었기 때문에 태생적 한계가 존재.</p>
<ul>
<li>동적 분석으로 인한 자유로움 : 런타임시 타입이 정해졌고 이로 인해 예상치 못한 버그 발생</li>
<li>문서(주석) 관리의 어려움 : JSDoc으로 변수와 함수를 설명하는 주석을 다는 방법이 있었으나 프로젝트 크기가 커질수록 관리가 어려워짐.</li>
<li>부족한 개발자 도구</li>
</ul>
<h2 id="13-타입스크립트">1.3 타입스크립트</h2>
<ul>
<li>프로그래밍 언어 : 자바스크립트의 기능은 가지고 있으면서 type과 annotation 기능을 추가로 제공해주는 상위 언어</li>
<li>타입 검사기 : 타입 시스템을 통해 잘못 구성된 타입에 대해 알려주는 프로그램</li>
<li>컴파일러 : 타입스크립트로 작성된 코드를 자바스크립트로 컴파일 해주는 프로그램</li>
<li>언어 서비스 : 타입 검사기를 사용해 IDE에서 오류를 표시하고 자동완성 기능을 도와줌.</li>
</ul>
<h2 id="14-타입스크립트-특징">1.4 타입스크립트 특징</h2>
<ul>
<li>정적 분석 : 런타임 이전에 코드를 분석하여 IDE에서 타입 에러 및 힌트를 실시간으로 제공 받을 수 있다.</li>
<li>타입 지정 : 코드를 지정한 방법으로만 사용하도록 제한할 수 있다.</li>
<li>정확한 문서화 : type과 annotation을 통해 정확한 타입 정보를 제공한다.</li>
<li>강력한 개발자 도구 : IDE에 타입스크립트를 지원하여 자동완성, 타입 힌트, 타입 에러를 실시간으로 제공해주어 개발자가 더 편리하게 코드 작성이 가능해진다.</li>
</ul>
<h2 id="15-로컬에서-시작하기">1.5 로컬에서 시작하기</h2>
<ul>
<li>전역에 설치<ul>
<li>tsc(타입스크립트 컴파일러) 사용 가능해짐</li>
</ul>
</li>
</ul>
<pre><code class="language-tsx">npm i -g typescript</code></pre>
<ul>
<li>설치 및 버전 확인</li>
</ul>
<pre><code class="language-tsx">tsc --version</code></pre>
<ul>
<li>로컬에서 실행 및 세팅<ul>
<li><code>tsconfig.json</code> 파일 생</li>
</ul>
</li>
</ul>
<pre><code class="language-tsx">tsc --init</code></pre>
<h2 id="16-타입스크립트에-대한-오해">1.6 타입스크립트에 대한 오해</h2>
<ul>
<li>코드 스타일 의견을 강요하지 않으며, 특정 앱 프레임워크와도 연관되어 있지 않는다.</li>
<li>런타임 시에는 코드에 영향을 주지 않고, 개발시에만 사용되는 주석 형태로 작동한다.</li>
<li>타입스크립트는 빌드시 자바스크립트로 컴파일되며, 자바스크립트 런타임에는 영향을 끼치지 않는다.</li>
</ul>
<h1 id="chapter-2-타입-시스템">Chapter 2 타입 시스템</h1>
<h2 id="21-타입의-종류">2.1 타입의 종류</h2>
<ul>
<li>타입 : 값의 형태</li>
<li>null, undefined, boolean, string, number, bigint, symbol</li>
<li>초깃값을 갖는 변수의 타입을 유추한다.</li>
</ul>
<h3 id="211-타입-시스템">2.1.1 타입 시스템</h3>
<ol>
<li>코드를 읽고 존재하는 모든 타입과 값을 이해</li>
<li>각 값이 초기 선언에서 가질 수 있는 타입을 확인</li>
<li>각 값이 추후 코드에서 어떻게 사용될 수 있는지 모든 방법을 확인</li>
<li>값의 사용법이 타입과 일치하지 않으면 사용자에게 오류를 표</li>
</ol>
<h3 id="212-오류-종류">2.1.2 오류 종류</h3>
<ul>
<li>구문 오류 : 잘못된 문법으로 인해 javascript로 컴파일 되지 못하도록 차단한 경우</li>
<li>타입 오류 : 타입 검사기에 따라 일치하지 않는 것이 감지된 경우</li>
</ul>
<h2 id="22-할당-가능성">2.2 할당 가능성</h2>
<ul>
<li>할당된 값이 예상 가능한 타입인지 확인하는 것.</li>
</ul>
<h3 id="221-할당-가능성-오류-이해하기">2.2.1 할당 가능성 오류 이해하기</h3>
<ul>
<li><code>Type X is not assignable to type Y</code></li>
<li>할당하려는 X 타입이 이미 할당되어 있는 Y 타입과 일치하지 않아서 생기는 오류로 가장 일반적인 오류</li>
</ul>
<h2 id="23-타입-애너테이션">2.3 타입 애너테이션</h2>
<ul>
<li>변수에 초깃값을 설정하지 않으면 any 타입으로 지정</li>
<li>any는 모든 타입이 들어올 수 있기 때문에 타입스크립트를 쓰는 이유가 없어진다.</li>
<li>초깃값이 없는 변수에는 타입 애너테이션 설정으로 타입 선언이 가능</li>
</ul>
<pre><code class="language-tsx">let rocker :string;
rocker = &quot;seyoung&quot;;</code></pre>
<h3 id="231-불필요한-타입-애너테이션">2.3.1 불필요한 타입 애너테이션</h3>
<ul>
<li>타입스크립트는 원시타입에 대해 자동 추론이 가능하다.<ul>
<li>undefined, null, boolean, string, number, symbol, bigint</li>
</ul>
</li>
<li>변수가 명확한 타입일 때는 자동으로 유추하기 때문에 annotation을 추가할 필요 없다.</li>
</ul>
<pre><code class="language-jsx">let name : string = &quot;seyoung&quot;;
let name = &quot;seyoung&quot;; // string type임을 자동으로 추론한다.</code></pre>
<h1 id="chapter-3-유니언과-리터럴">Chapter 3 유니언과 리터럴</h1>
<h2 id="31-유니언-타입">3.1 유니언 타입</h2>
<ul>
<li>상황에 따라 두 가지 이상의 타입이 쓰일 때 사용</li>
</ul>
<pre><code class="language-jsx">let score = 20;
let result : string | number = scroe &lt; 80 : &quot;불합격&quot; : score; </code></pre>
<h2 id="32-내로잉">3.2 내로잉</h2>
<ul>
<li><p>유니언 타입 설정으로 타입이 2가지가 되어버린 상황에서 타입을 명확하게 좁힐 때 사용</p>
</li>
<li><p>내로잉 방법</p>
<ul>
<li><p>값 할당을 통한 내로잉</p>
<pre><code class="language-jsx">let inventory : string | number = &quot;water&quot;;

inventory.toUppercase(); //WATER

invertory.toFixed();
// Error : Property &#39;toFixed; does not exist on type &#39;string&#39;</code></pre>
</li>
<li><p>조건 검사를 통한 내로잉</p>
</li>
<li><p>typeof 검사를 통한 내로잉</p>
</li>
</ul>
</li>
</ul>
<h2 id="33-리터럴-타입">3.3 리터럴 타입</h2>
<ul>
<li><p>값 자체를 타입으로 사용하는 것</p>
</li>
<li><p>변수가 제한된 값들을 가질 때 안전하게 제한하여 사용할 수 있다.</p>
</li>
<li><p>유니온 타입으로 지정</p>
</li>
</ul>
<pre><code class="language-jsx">type btnType = &quot;default&quot; | &quot;error&quot;;
type btnColor = &quot;red&quot; | &quot;black&quot; | &quot;green&quot;;
type btnStyele = `${btnType}-${btnColor}`</code></pre>
<ul>
<li>const 로 변수 선언</li>
</ul>
<p>const는 변경이 불가능typescript에서 자동으로 literal type으로 추론한다.</p>
<ul>
<li>const assertion(const 단언, 주장)</li>
</ul>
<p><code>as const</code> 를 변수 선언 후 뒤에 추가하면 literal type으로 추론해준다.</p>
<pre><code class="language-jsx">const Color = {
    red : &quot;FF2031&quot;,
    black : &quot;000000&quot;
} as const
</code></pre>
<h2 id="34-엄격한-null-검사">3.4 엄격한 null 검사</h2>
<ul>
<li>null 또는 undefined가 될 수 있는 경우를 타입스크립트에서는 엄격하게 검사하고 있으며 이를 해결하기 위해서는 참 검사를 통한 내로잉을 할 수 있다..</li>
</ul>
<h2 id="35-타입-별칭">3.5 타입 별칭</h2>
<pre><code class="language-jsx">type RawData = boolean | number | string | null</code></pre>
<ul>
<li>type도 javascript 변수처럼 별칭을 만들어서 선언할 수 있다.<ul>
<li>그러나 javascript 가 아니라는 점을 주의.</li>
<li>개발시에만 사용되는 코드이며 실제 런타임 코드에서는 참조할 수 없다.</li>
</ul>
</li>
<li>선언한 type은 재사용이 가능하다.</li>
<li>파스칼케이스로 이름을 지정한다.</li>
<li>타입 별칭은 결합이 가능하다</li>
</ul>
<pre><code class="language-jsx">type Id = number | string;
type IdMaybe = Id | undefined | null;</code></pre>
<h1 id="chapter-4-객체">Chapter 4 객체</h1>
<ul>
<li>복잡한 객체 형태를 설명하는 방법</li>
<li>타입스크립트가 객체의 할당 가능성을 확인하는 방법</li>
</ul>
<h2 id="41-객체-타입">4.1 객체 타입</h2>
<h3 id="411-객체-타입-선언">4.1.1 객체 타입 선언</h3>
<ul>
<li>객체 타입을 명시적으로 선언하기</li>
</ul>
<pre><code class="language-jsx">let poetLate :{
    born:number;
    name:string;
} = {
    born:1935,
    name:&quot;seyoung&quot;
}</code></pre>
<h3 id="412-별칭-객체-타입">4.1.2 별칭 객체 타입</h3>
<ul>
<li>객체 타입 선언 방법은 코드가 길어지면서 가독성이 떨어지며 재사용성도 떨어지기 때문에 보통 타입 별칭을 할당해 사용하는 방법이 일반적.</li>
</ul>
<pre><code class="language-tsx">type Poet = {
    born:number;
    name:string;
}

let poetLate :Poet = {
    born:1995,
    name:&quot;seyoung&quot;
}</code></pre>
<h2 id="42-구조적-타이핑">4.2 구조적 타이핑</h2>
<ul>
<li>구조적 타이핑 : 런타임 이전에 타입체크를 하는 것.</li>
<li>덕 타이핑 : 런타임 때 타입체크를 하는 것.</li>
</ul>
<h3 id="421-사용-검사">4.2.1 사용 검사</h3>
<ul>
<li>type에 설정된 프로퍼티를 잘 사용했는지 검사한다.<ul>
<li>type에 설정한 property를 모두 사용했는지</li>
<li>설정한 type이 맞는지</li>
</ul>
</li>
</ul>
<pre><code class="language-tsx">type FirstAndLastNames = {
    first: string;
    last : string;
}

const hasBoth : FirstAndLastNames = {
    first:&quot;Sarojini&quot;,
    last:&quot;Naidu&quot;
}

const hasOnlyOne : FirstAndLastNames = {
    first:&quot;Sappho&quot;
    // Error : 
        // Property &#39;last&#39; is missing in type &#39;{ first: string; }&#39; but required in type &#39;FirstAndLastNames&#39;.
}</code></pre>
<h3 id="422-초과-속성-검사">4.2.2 초과 속성 검사</h3>
<ul>
<li>예상되는 타입 이외에 초과 속성을 설정하면 타입 오류가 발생한다.</li>
<li>기존 객체 리터럴을 제공하면 초과 속성 검사를 우회한다.</li>
</ul>
<pre><code class="language-tsx">type Poet = {
    born : number;
    name : string;
}

const existingObject = {
    activity : &quot;walking&quot;,
    born:1233,
    name:&quot;seyoung&quot;
}

const extraPropertyButOk: Poet = existingObject;</code></pre>
<h3 id="423-중첩된-객체-타입">4.2.3 중첩된 객체 타입</h3>
<ul>
<li>중첩된 객체의 type을 따로 추출하면 에러 메세지를 읽을 때 가독성 더 좋아진다.</li>
</ul>
<pre><code class="language-tsx">type Author = {
    firstName : string;
    lastName: string;
}

type Poem = {
    author : Author;
    name: string;
}

const PoemMismatch : Poem = {
    author : {
        name:&quot;seyoung&quot;
        // Error : Type &#39;{ name: string; }&#39; is not assignable to type &#39;Author&#39;.
        // Object literal may only specify known properties, and &#39;name&#39; does not exist in type &#39;Author&#39;.
    },
    name:&quot;young&quot;
}</code></pre>
<h3 id="424-선택적-속성">4.2.4 선택적 속성</h3>
<ul>
<li>선택적 속성으로 : 앞에 <code>?</code> 를 추가하면 해당 속성은 생략가능해진다.</li>
</ul>
<h2 id="43-객체-타입-유니언">4.3 객체 타입 유니언</h2>
<h3 id="431-유추된-객체-타입-유니언">4.3.1 유추된 객체 타입 유니언</h3>
<ul>
<li>변수에 여러 객체 타입 중 하나가 될 수 있는 초깃값이 할당되면 타입스크립트는 객체 타입 유니언으로 유추한다.</li>
</ul>
<pre><code class="language-tsx">const poem = Math.random() &gt; 0.5 ? { name:&quot;크다&quot;, pages:7} : { name:&quot;작다&quot;, rhymes:true}

// 타입스크립트가 자동으로 추론한 타입
// {
//     name: string;
//     pages: number;
//     rhymes?: undefined;
// } | {
//     name: string;
//     rhymes: boolean;
//     pages?: undefined;
// }</code></pre>
<h3 id="432-명시된-객체-타입-유니언">4.3.2 명시된 객체 타입 유니언</h3>
<ul>
<li>위의 자동 추론된 타입에서는 rhymes와 pages가 존재하지 않을 때도 접근이 가능하다.</li>
<li>객체 타입 유니언을 명시해줌으로써 존재하지 않는 속성에 대한 접근을 막아 코드의 안전을 지킬 수 있다.</li>
</ul>
<pre><code class="language-tsx">type PoemWithPages = {
    name:string;
    pages:number;
}

type PoemWithRhymes = {
    name:string;
    rhymes:boolean;
}
type Poem = PoemWithPages | PoemWithRhymes;
const poem: Poem = Math.random() &gt; 0.5 ? { name:&quot;크다&quot;, pages:7} : { name:&quot;작다&quot;, rhymes:true}</code></pre>
<h3 id="433-객체-타입-내로잉">4.3.3 객체 타입 내로잉</h3>
<ul>
<li>객체 타입 유니온도 내로잉을 통해 타입을 좁혀야줘야 한다.</li>
</ul>
<pre><code class="language-tsx">type PoemWithPages = {
    name:string;
    pages:number;
}

type PoemWithRhymes = {
    name:string;
    rhymes:boolean;
}
type Poem = PoemWithPages | PoemWithRhymes;
const poem: Poem = Math.random() &gt; 0.5 ? { name:&quot;크다&quot;, pages:7} : { name:&quot;작다&quot;, rhymes:true}

if(&quot;pages&quot; in poem){
    console.log(poem.pages) // PoemWithPages로 좁혀짐
} else {
    console.log(poem.rhymes) // PoemWithRhymes로 좁혀짐
}</code></pre>
<h3 id="434-판별된-유니언">4.3.4 판별된 유니언</h3>
<ul>
<li>type의 속성을 객체의 형태로 나타낼 수 있다.</li>
</ul>
<pre><code class="language-tsx">type PoemWithPages = {
    name:string;
    pages:number;
    type:&#39;pages&#39;;
}

type PoemWithRhymes = {
    name:string;
    rhymes:boolean;
    type:&#39;rhymes&#39;;
}

type Poem = PoemWithPages | PoemWithRhymes;
const poem: Poem = Math.random() &gt; 0.5 
    ? { name:&quot;크다&quot;, pages:7, type:&#39;pages&#39;} 
    : { name:&quot;작다&quot;, rhymes:true, type:&#39;rhymes&#39;}

if(poem.type === &quot;pages&quot;){
    console.log(&quot;pages&quot;)
} else {
    console.log(&quot;rhymes&quot;)
}</code></pre>
<h2 id="44-교차-타입">4.4 교차 타입</h2>
<ul>
<li>유니온 타입이 <code>|</code> 연산자로 둘 중 하나의 타입을 나타낸다면</li>
<li><code>&amp;</code> 연산자로 여러 타입을 묶어서 교차시키는 것도 가능하다.</li>
<li><code>&amp;</code> , <code>|</code> 연산자를 결합하여 타입을 표현할 수도 있다.</li>
</ul>
<h3 id="441-교차-타입의-위험성">4.4.1 교차 타입의 위험성</h3>
<ul>
<li>assignable error(할당 가능성 오류) 메세지가 길어지면서 읽기 어려워 질 수 있다.</li>
<li>type을 분리하여 별칭 이름으로 오류 메세지가 뜨게 하면 읽기가 훨씬 쉬워진다.</li>
</ul>
<pre><code class="language-tsx">type ShortPoemBase = {author : string};
type Haiku = ShortPoemBase &amp; {kiho : string; type :&#39;haiku&#39;}
type Villanelle = ShortPoemBase &amp; {meter : number; type :&#39;villanelle&#39;}
type ShortPoem = Haiku | Villanelle;</code></pre>
<ul>
<li>원시타입에서 교차타입을 지정하면 never 타입이 된다.<ul>
<li>never타입은 어떠한 값도 제공할 수 없다는 의미</li>
<li>코드에서 불가능한 상태를 나타낼 때 제공</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[NEXTSTEP] TDD, 클린 코드 with React 강의 회고]]></title>
            <link>https://velog.io/@hi_young/NEXTSTEP-TDD-%ED%81%B4%EB%A6%B0-%EC%BD%94%EB%93%9C-with-React-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@hi_young/NEXTSTEP-TDD-%ED%81%B4%EB%A6%B0-%EC%BD%94%EB%93%9C-with-React-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Mon, 01 May 2023 08:09:46 GMT</pubDate>
            <description><![CDATA[<h1 id="이-강의를-선택하게-된-이유">이 강의를 선택하게 된 이유</h1>
<p>이 강의를 듣기 전까지는 사실 output을 내는 것에만 집중했다. 내 손으로 뚝딱뚝딱 만들어지는게 재밌어서 뭐든 만들어보고 싶었다. 그래서 회사 프로젝트가 조금 여유로워질때면 사이드 프로젝트를 진행했는데, 부족한 실력이었기에 input과 output 두 마리 토끼를 모두 잡으면서 결과물을 내는 것이 쉽지 않았다. 기술적인 개념들의 정확한 이해와 코드 품질에 대한 개선은 신경쓰지 못했고 우선 어떻게든 돌아가게 만드는 것에 집중했다.</p>
<p>그런데 문제는 열심히 만든 결과물이 보기에는 그럴싸했는데 코드를 보여주는 순간 한 없이 작아지는 나를 발견했다. 회사에서는 내가 작업한 파일을 동료가 수정해야할 때 &quot;아... 그거 좀 복잡할거에요&quot;라는 말이 우선 나오며, 사이드 프로젝트의 코드도 중구난방 복잡했다.</p>
<p>다른 사람들이 읽기 쉬운 좋은 코드란 무엇인가? 내가 짜는 코드가 과연 안전한 것일까? 이런 궁금증들을 가지게 되었다. 그러다 친한 지인의 추천으로 넥스트스텝의 클린코드 강의를 알게 되었고 내 고민들에 대한 정답을 찾을 수 있을 것 같아 신청하게 되었다.</p>
<h1 id="강의-진행-방식">강의 진행 방식</h1>
<p><a href="https://edu.nextstep.camp/c/QoTvUh4y/">넥스트 스텝 링크</a></p>
<p>일주일에 한번씩 강의가 있고, 코드리뷰를 해주는 미션이 3가지(온보딩 미션 1개 + 공식 미션 2개) 있고 이에 따라 리뷰어 분도 바뀐다. 그래서 여러 시각으로 리뷰를 받을 수 있었고 빅테크 개발자들은 어떤 생각을 가지고 코드를 짜는지 직접 물어보고 답변을 들을 수 있었기에 코멘트 하나하나가 정말 값지다고 느꼈다.</p>
<p>그리고 일주일에 한 번씩 있는 강의도 정말 좋았는데 리액트에 대한 중요한 개념 + 프론트엔드 개발자가 현실적으로 고민해야하는 부분들을 많이 일깨워주셨다.</p>
<p>아래는 진행된 미션 PR 링크들이며 각 미션별로 step3까지 있다. </p>
<h3 id="페이먼츠-미션">페이먼츠 미션</h3>
<p><a href="https://github.com/next-step/react-payments/pull/55">https://github.com/next-step/react-payments/pull/55</a></p>
<p><a href="https://github.com/next-step/react-payments/pull/88">https://github.com/next-step/react-payments/pull/88</a></p>
<p><a href="https://github.com/next-step/react-payments/pull/97">https://github.com/next-step/react-payments/pull/97</a></p>
<h3 id="쇼핑카트-미션">쇼핑카트 미션</h3>
<p><a href="https://github.com/next-step/react-shopping-cart/pull/3">https://github.com/next-step/react-shopping-cart/pull/3</a></p>
<p><a href="https://github.com/next-step/react-shopping-cart/pull/25">https://github.com/next-step/react-shopping-cart/pull/25</a></p>
<h1 id="나는-무엇을-알게-되었나">나는 무엇을 알게 되었나?</h1>
<h3 id="가독성-좋은-코드를-짜는-방법">가독성 좋은 코드를 짜는 방법</h3>
<p>좋은 개발자의 조건 중 하나가 글을 잘 쓰는 것이다. 글을 잘 쓴다는 것은 읽는 사람의 입장에서 술술 잘 읽히고 이해가 잘 된다는 것이며 개발에서도 비슷한 맥락으로 이해할 수 있다. 코드를 처음 보는 사람도 어떤 코드인지 이해가 잘 되는 코드가 클린코드, 즉 가독성이 좋은 코드이다.
그래서 미션을 할 때 리뷰어님에게 편지를 보낸다는 느낌으로 많은 고민을 하며 코드를 짰다. </p>
<ul>
<li>네이밍을 어떻게 하면 한눈에 내 의도를 파악할 수 있을까?<ul>
<li>무조건 짧은 것이 좋은 건 아니다. 길더라도 파악하기 좋은 네이밍으로!</li>
<li>매직넘버는 상수로 그 숫자의 의미 알 수 있도록 하기.</li>
</ul>
</li>
<li>코드를 모듈화 시키면 리뷰할 때 더 보기 편하지 않을까?<ul>
<li>constant, util, type 등 파일들은 같은 관심사 묶기</li>
</ul>
</li>
<li>관리하기 쉽도록 하려면 어떻게 해야하지?<ul>
<li>관리포인트가 여러군데라면 상수, 모듈화를 통해 관리 포인트 줄이기!</li>
<li>ex) router path를 상수로 만들어두면 수정시 매우 편함. 하나만 수정하면 되니깐!</li>
</ul>
</li>
</ul>
<p>그리고 이런 고민들이 보여서 재밌었다는 리뷰어님의 코멘트에… 한글로 쓴 것도 아닌데 코드에 녹아져있는 생각이 보였다는게 정말 신기했다. 코딩은 글쓰기와 비슷하다는 것을 정말 많이 깨달았다.</p>
<h3 id="context-api-reducer-잘-활용하기">Context API, reducer 잘 활용하기</h3>
<p>Context API는 일정 구간의 컴포넌트 트리에서 상태를 간편하게 공유할 수 있도록 도와주는 API이다. props drilling이나, 동일한 props를 많은 컴포넌트에 공유해야 할 때의 불편함을 해결할 수 있다.</p>
<p>reducer는 상태 로직을 업데이트하는 함수들을 하나로 관리할 수 있게 도와준다. 하나의 상태를 업데이트하는 로직이 다양할 때 reducer로 로직의 type을 정해서 관리할 수 있다.</p>
<p>그리고 이러한 두 가지 도구는 궁합이 좋아서 함께 사용하면 상태 관리가 명료하고 간단해지는 장점이 있는데 명심할 점은 두 가지가 꼭 세트가 아니라는 것..!  </p>
<h3 id="cdd를-위해-스토리북은-어떻게-잘-활용할-수-있을까">CDD를 위해 스토리북은 어떻게 잘 활용할 수 있을까?</h3>
<p>CDD란 Component Driven Development로 UI 컴포넌트를 중심으로 개발하는 방법이다. 스토리북은 CDD 개발의 최적화된 도구이며 실제 프로젝트와 독립된 환경에서 컴포넌트 단위로 UI를 만들어 문서화하고 테스트 할 수 있다.</p>
<p>CDD는 디자인 시스템에는 거의 필수이나 현실적으로 일정이 촉박한 일반 프로젝트에서는 CDD를 완벽하게 따라가기에는 어려움이 있다. 그래도 디자인 시안에서 보이는 공통된 input, button 등은 재사용할 수 있도록 같이 작업하는 것이 좋을 것 같다는 생각이 든다.</p>
<h3 id="controlled--uncontrolled-components">Controlled &amp; uncontrolled components</h3>
<p>controlled, uncontrolled component는 각자의 장단점이 있기 때문에 상황에 따라 선택하는 것이 좋다. </p>
<p>controlled component는 react의 useState를 사용해서 상태를 제어하는 것이다. 그렇기 때문에 React의 렌더링 주기에 들어가 있어 예측 가능한 코드 작성이 가능하며, 사용자가 입력하는 값을 UI에 즉시 업데이트할 수 있기 때문에 UX적인 측면에서도 좋다. 그러나 복잡한 UI에서는 리렌더링으로 인해 성능이슈가 있을 수 있기 때문에 주의해야한다.</p>
<p>uncontrolled component는 브라우저에 있는 form 요소의 value값을 직접 사용자가 변경하고 그 값을 가져오도록 구현하는 방식이다. react의 상태를 사용하지 않기 때문에 리렌더링이 없고 빠르다.</p>
<h3 id="컴포넌트를-컴포넌트-답게-사용하기">컴포넌트를 컴포넌트 답게 사용하기</h3>
<p>컴포넌트를 재사용가능하게 만들기 위해서는 사이드이펙트가 없어야한다. 즉 순수함수로 만들어서 어디서든 재사용가능하도록 만드는 것이다.
그리고 컴포넌트의 목적에 대해서도 생각해보아야 한다. 그 목적에 따라 사이드 이펙트를 어디까지 허용할 것인지 정하는 것도 필요하다.</p>
<h3 id="프론트엔드-개발자에게-필요한-테스트">프론트엔드 개발자에게 필요한 테스트</h3>
<p>기능, 디자인 수정요청이 쏟아져도, 앞의 일정이 늘어나도 개발 일정은 그대로인 상황이 나만 그런게 아닌 프론트엔드 개발자의 숙명이라는 걸 알게 되었고, 이에 대응하기 위해서 프론트엔드 개발자는 가만히 있어선 안되고 개발 일정을 맞추기 위한 테스트를 수행해야한다. 프론트엔드에서 현실적인 테스트는 스토리북을 사용해 재사용가능한 컴포넌트를 만들어 UI 테스트를 해볼 수 있으며, 백엔드 API가 완성될떄까지 가만히 있는 것이 아니라 MSW로 직접 API 테스트를 해볼 수도 있다. </p>
<h1 id="느낀점">느낀점</h1>
<p>이 과정을 통해 평소 실무에서 본능적으로 하고 있었던 것들에 대해 구체화할 수 있었고 몰랐던 것들도 많이 깨달을 수 있었다. 그리고 넥스트 스텝의 교육 퀄리티도 정말 좋았는데 강사님, 리뷰어 분들께 얻는 인사이트도 크고, 같은 미션을 하는 참가자들의 코드를 함께 볼 수 있는데 거기서 얻는 꿀팁들도 꽤 많았다.</p>
<p>그리고 &quot;왜?&quot;의 중요성을 새삼 다시 한번 깨달을 수 있었는데 이러한 질문을 통해 깊이 있는 공부가 가능해지고, 궁금증을 통해 흥미가 더 생기는 나 자신을 볼 수 있었다.</p>
<h1 id="아쉬운-부분">아쉬운 부분</h1>
<p>2달이라는 시간동안 좀 더 집중해서 했어야하는데 회사와 병행하는 것이 시간적으로 부족했고 체력적으로도 힘들었다. 그래서 마지막 미션을 제대로 마무리하지 못한 상태라 아쉬움이 크다... 미루지 말고 제때 해야한다는 것을 또 한번 깨닫는다.</p>
<h1 id="앞으로의-계획">앞으로의 계획</h1>
<p>클린코드 리액트에서 배운 것을 토대로, 혼자서 프로젝트를 하나 해보려 한다. 부족한 react 개념과 깊이 있는 학습을 통해 프로젝트 하나를 제대로 완성해보고 싶어졌다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[사이드 프로젝트 회고 - 댕메이트]]></title>
            <link>https://velog.io/@hi_young/%EB%8C%95%EB%A9%94%EC%9D%B4%ED%8A%B8</link>
            <guid>https://velog.io/@hi_young/%EB%8C%95%EB%A9%94%EC%9D%B4%ED%8A%B8</guid>
            <pubDate>Thu, 01 Dec 2022 14:57:12 GMT</pubDate>
            <description><![CDATA[<h1 id="1-시작">1. 시작</h1>
<p>한달전 저는 react에 대해 각잡고 공부해보려는 시점이었습니다. 그리고 선택지는 2가지.</p>
<ol>
<li>인터넷 강의로 공부.</li>
<li>사이드 프로젝트로 직접 만들면서 공부.</li>
</ol>
<p>같이 일했던 동료분이 사이드 프로젝트를 하자는 제안을 주셨고 고민끝에 &#39;역시 공부는 실전이지!&#39; 라는 생각으로 넘블에서 진행하는 <a href="https://www.numble.it/e3d67139-f040-47cd-80a7-12e063ef1f36">지역 기반 커뮤니티 만들기 프로젝트</a>에 참여하게 되었습니다.</p>
<h1 id="2-우리-댕메이트는요">2. 우리 댕메이트는요!</h1>
<center><img src="https://velog.velcdn.com/images/hi_young/post/70f10f05-48cd-48f2-979e-59fe5d716364/image.png" width="300"></center>

<p>강아지를 키우는 견주들을 위한 지역 기반 커뮤니티 입니다. 같은 동네에서 우리 댕댕이와 산책할 친구를 구할 수 있고 정보를 공유할 수도 있어요. 서비스 컨셉이 더 궁금하다면 아래 링크를 참고해주세요😀
<a href="https://velog.velcdn.com/images/hi_young/post/4d6450e9-f21f-4b18-a0a2-375ac02517f4/image.pdf">댕메이트 소개</a></p>
<h1 id="3-구현-기능">3. 구현 기능</h1>
<h2 id="✨-지역-인증을-통한-회원가입">✨ 지역 인증을 통한 회원가입</h2>
<p align="center"><img src="https://velog.velcdn.com/images/hi_young/post/d56af67e-f482-4892-9585-645295a35eaf/image.gif" width="300"></p>

<p>현재 위치에서 6km 이내에 있는 동,읍,면을 자동으로 검색하여 간편하게 자신의 동네를 선택해 회원가입을 할 수 있습니다.</p>
<p>모든 버튼은 사용자가 input창에 입력한 정보의 유효성 검사를 통과했을 때 활성화 되도록 처리했구요.</p>
<p>이메일은 중복 검사가 필요하기 때문에 이메일 형식 유효성 체크가 통과된 뒤부터 중복 체크 api를 호출하여 검사할 수 있도록 만들었습니다.</p>
<p>닉네임 또한 중복 체크를 통해 동일한 닉네임으로 가입할 수 없도록 제한했습니다.</p>
<h2 id="✨-로그인">✨ 로그인</h2>
<p align="center"><img src="https://velog.velcdn.com/images/hi_young/post/2c1c4e41-2834-4131-8606-a86593af4174/image.gif" width="300px"></p>

<p>로그인은 이메일, 비밀번호 형식 유효성 체크뿐만 아니라, 존재하지 않는 아이디일때, 비밀번호만 틀렸을 때 각각에 대해 에러 핸들링을 추가 했습니다.</p>
<p>로그인에 성공하면 귀여운 스플래시화면이 환영해줘요🥰</p>
<h2 id="✨-댕메이트-메인">✨ 댕메이트 메인</h2>
<p align="center"><img src="https://velog.velcdn.com/images/hi_young/post/739e1386-dbf5-47f7-a864-767729b9eda8/image.gif" width="300px"></p>

<p>메인에는 인피니트 스크롤과 스켈레톤 ui를 넣어 부드러운 ux로 게시글 리스트를 볼 수 있도록 했습니다.</p>
<h2 id="✨-게시물-업로드수정삭제">✨ 게시물 업로드/수정/삭제</h2>
<p align="center"><img src="https://velog.velcdn.com/images/hi_young/post/02a968db-6e03-42db-a601-f2b1a26e0b15/image.gif" width="300px"></p>

<p>원하는 카테고리칩을 선택하고 이미지와 본문을 작성하여 업로드할 수 있습니다.</p>
<p>본인의 게시물은 자유롭게 수정하고 삭제할 수 있습니다.</p>
<h2 id="✨-댓글대댓글-작성수정삭제">✨ 댓글/대댓글 작성/수정/삭제</h2>
<p align="center"><img src="https://velog.velcdn.com/images/hi_young/post/2ff545e2-614a-40a9-a5ee-8e78e5da9dcb/image.gif" width="300px"></p>

<p>댓글, 대댓글을 작성하고 본인의 댓글은 자유롭게 수정하고 삭제할 수 있습니다.</p>
<h2 id="✨-좋아요-기능">✨ 좋아요 기능</h2>
<p align="center"><img src="https://velog.velcdn.com/images/hi_young/post/933d50de-6240-4ff1-9ffc-5900a2ed8ea5/image.gif" width="300px"></p>

<p>내가 관심있는 게시물에 좋아요를 누르고 관심 목록에서 확인할 수 있습니다.</p>
<h2 id="✨-내가-쓴글">✨ 내가 쓴글</h2>
<p align="center"><img src="https://velog.velcdn.com/images/hi_young/post/30798d66-ef5b-49dd-9971-df75b7405907/image.gif" width="300px"></p>

<p>내가 쓴 글과 댓글들을 확인할 수 있습니다.</p>
<h2 id="✨-프로필">✨ 프로필</h2>
<p align="center"><img src="https://velog.velcdn.com/images/hi_young/post/fa6fe838-d830-425d-a841-14bfe7c5356e/image.gif" width="300px"></p>

<p>내 프로필사진과 닉네임을 변경할 수 있습니다. 그리고 프로필 정보 오용 및 남용을 막기 위해 
프로필 정보는 수정 후 7일 이내에 다시 변경할 수 없도록 했습니다.</p>
<h1 id="4-댕메이트-제작-상세">4. 댕메이트 제작 상세</h1>
<ul>
<li><p>도메인 : <del><a href="https://dangmate.com">https://dangmate.com</a></del>(현재는 서비스 종료상태입니다😭)</p>
</li>
<li><p>제작 기간 : 2022.10.21~2022.12.1 <strong>(6주)</strong></p>
</li>
<li><p>팀원</p>
<ul>
<li>프론트엔드 : 세영</li>
<li>백엔드 : 제우스</li>
<li>디자인 : 제민규</li>
</ul>
</li>
<li><p>프론트엔드 기술 스택 : Vite, React, Typescript, Recoil, Emotion</p>
</li>
<li><p>사용 서비스 및 협업 툴</p>
<ul>
<li>이용 서비스 : Kakao map api, 주소 오픈 api</li>
<li>배포 : AWS S3</li>
<li>협업툴 : Figma, Github, Notion, Slack</li>
</ul>
</li>
<li><p>개발 깃허브 : <a href="https://github.com/dangmate">https://github.com/dangmate</a></p>
</li>
</ul>
<h1 id="6-회고">6. 회고</h1>
<h2 id="배운-점">배운 점</h2>
<ul>
<li><p>서비스의 기본 기능들을 구현해보면서 유의해야하는 점들을 깨달을 수 있었습니다.</p>
<ul>
<li>비용적 측면에서 api 제어(throttling/debouncing, 댓글/대댓글 data fetch 없이 리렌더링 시키기)</li>
</ul>
</li>
<li><p>라이브러리는 잘 알고 써야 잘 쓸 수 있습니다.</p>
<ul>
<li>form 라이브러리를 초반에 사용했다가 커스텀이 안돼서 삽질하다 결국 삭제하고 스스로 구현했습니다. </li>
</ul>
</li>
<li><p>모바일 반응형에서 주의해야될 점과 ios에서 발생하는  크로스브라우징 문제들을 파악하고 개선할 수 있었습니다.</p>
<ul>
<li>키패드가 올라왔을 때 컨텐츠 가림 문제 및 버튼 처리</li>
<li>ios input 그림자 및 zoom 이슈/ 사파리 주소창 이슈</li>
</ul>
</li>
<li><p>react 숙련도를 올릴 수 있었습니다. <small>(물론... 현재 코드는 리팩토링이 시급합니다🫠)</small></p>
</li>
</ul>
<h2 id="아쉬웠던-점">아쉬웠던 점</h2>
<p>시간은 부족하고 혼자하기에 작업량이 정말 많았기에... 그 과정에서 생긴 개발 이슈들은 해결하면 넘어가기 급급했고 제대로 정리를 못한 점들이 너무 아쉬웠습니다. 천천히 곱씹으며 이해를 할 시간이 없었달까...🥲</p>
<p>그리고 충분히 컴포넌트로 묶어서 사용할 수 있던 부분들을 하드코딩으로 하게 된것도 있고, react로 렌더링 최적화 시킬 수 있는 훅들도 많이 사용하지 못했습니다. 코드에 대한 고민을 해볼 수 있는 시간이 적었던 점이 많이 아쉽습니다...</p>
<h2 id="마지막으로">마지막으로...</h2>
<p>짧은 시간이었지만 한 서비스의 기본 기능들을 혼자서 모두 구현해볼 수 있었고 그 과정에서 어떤 점들을 유의해야하는지 깨달을 수 있었기에 좋았습니다. </p>
<p>그리고 모바일 반응형, 크로스 브라우징 등을 신경썼고 깔끔하게 구현된 것 같아 뿌듯했습니다.</p>
<p>친절하고 맡은 일을 잘 해냈던 팀원들 덕분에 힘들어도 재밌게 작업할 수 있었고, &quot;나중에 함께 일해보고 싶다!&quot;라는 생각이 들었습니다. 그리고 내가 느끼는 것처럼 과연 나는 같이 일하고 싶은 동료였을까?라는 생각을 하며 스스로를 되돌아보는 시간도 가질 수 있었습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[javascript input 제어(날짜 자동 하이픈, 숫자만 입력)]]></title>
            <link>https://velog.io/@hi_young/input-%EB%82%A0%EC%A7%9C%EC%9E%90%EB%8F%99-%ED%95%98%EC%9D%B4%ED%94%88-%EC%88%AB%EC%9E%90%EB%A7%8C-%EC%9E%85%EB%A0%A5</link>
            <guid>https://velog.io/@hi_young/input-%EB%82%A0%EC%A7%9C%EC%9E%90%EB%8F%99-%ED%95%98%EC%9D%B4%ED%94%88-%EC%88%AB%EC%9E%90%EB%A7%8C-%EC%9E%85%EB%A0%A5</guid>
            <pubDate>Fri, 07 Oct 2022 16:37:31 GMT</pubDate>
            <description><![CDATA[<h3 id="1-숫자만-입력받기">(1) 숫자만 입력받기</h3>
<p>일반적으로 input type에 number를 입력할 수 있지만 단점이 있다.
입력한 문자가 value에 들어가진 않는데 화면에 입력한 값이 보이며 특수문자 또한 입력이 가능하다.</p>
<img src="https://velog.velcdn.com/images/hi_young/post/e2e7905a-d52d-43f3-a4db-94f245a7810a/image.gif" width="50%">

<p>그래서 number type을 사용하지 않고 정규식을 이용해 사용자가 입력한 문자값은 아예 볼 수 없도록 설정한다.</p>
<p>이때 input event를 사용하는데 일반적으로 key를 입력받으면
(1)keydown -&gt; (2)keypress -&gt; (3)input -&gt; (4)keyup
이 순서로 이벤트가 일어나며 event.target.value에 3번부터 값이 들어온다.
그리고 실제로 사용자 화면에 출력되는 이벤트는 4번부터다.
즉, input 이벤트에서 value에 문자열이 입력되는 순간 사용자 눈에 보이기도 전에
정규식으로 삭제해버릴 수 있다.</p>
<pre><code class="language-html">&lt;label for=&quot;date&quot;&gt;날짜 : &lt;/label&gt;
&lt;input type=&quot;text&quot; id=&quot;date&quot; oninput=&quot;onlyNumber()&quot; &gt;</code></pre>
<pre><code class="language-jsx">function onlyNumber(){
    const reg = /\D/g;
      event.target.value = event.target.value.replace(reg, &quot;&quot;);
}</code></pre>
<h3 id="2-자동-하이픈-추가">(2) 자동 하이픈 추가</h3>
<p>타사이트에서 숫자만 입력받아 하이픈은 자동으로 추가 삭제되는 기능이 멋져보여서 이것까지 구현해보려 한다.</p>
<p>구현 목표</p>
<ul>
<li>사용자는 숫자만 입력 가능(문자열, 하이픈 입력 불가)</li>
<li>숫자 길이에 따라 하이픈(-)은 자동으로 추가/삭제</li>
</ul>
<pre><code class="language-jsx">
let date = document.querySelector(&quot;#date&quot;);

// 문자열, 하이픈을 막기 위해 input event 사용
date.addEventListener(&quot;input&quot;, () =&gt; {

  // 사용자 입력값은 모두 숫자만 받는다.(나머지는 &quot;&quot;처리)
  let val = date.value.replace(/\D/g, &quot;&quot;);
  let leng = val.length;

  // 출력할 결과 변수
  let result = &#39;&#39;;

  // 5개일때 - 20221 : 바로 출력
  if(leng &lt; 6) result = val;
  // 6~7일 때 - 202210 : 2022-101으로 출력
  else if(leng &lt; 8){
      result += val.substring(0,4);
    result += &quot;-&quot;;
    result += val.substring(4);
  // 8개 일 때 - 2022-1010 : 2022-10-10으로 출력
  } else{
      result += val.substring(0,4);
    result += &quot;-&quot;;
    result += val.substring(4,6);
    result += &quot;-&quot;;
    result += val.substring(6);
  }
  date.value = result;

})
</code></pre>
<p>코드펜에 유효성 체크와 함께 정리해보았다.</p>
<p>!codepen[seyoungjoy/embed/XWqxJWy?default-tab=html%2Cresult]</p>
<h2 id="reference">Reference</h2>
<ul>
<li><a href="https://youtu.be/31RM_tDUJTs">https://youtu.be/31RM_tDUJTs</a></li>
<li><a href="https://hoony-devblog.tistory.com/15">https://hoony-devblog.tistory.com/15</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Vue] textarea 줄바꿈이 안되는 이유]]></title>
            <link>https://velog.io/@hi_young/Vue-textarea-%EC%A4%84%EB%B0%94%EA%BF%88%EC%9D%B4-%EC%95%88%EB%90%98%EB%8A%94-%EC%9D%B4%EC%9C%A0</link>
            <guid>https://velog.io/@hi_young/Vue-textarea-%EC%A4%84%EB%B0%94%EA%BF%88%EC%9D%B4-%EC%95%88%EB%90%98%EB%8A%94-%EC%9D%B4%EC%9C%A0</guid>
            <pubDate>Sat, 23 Jul 2022 07:50:40 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<ul>
<li>textarea를 통해 글자를 입력하고, 서버로 보낸 뒤 다시 들고 왔을 때 줄바꿈이 안먹히는 현상이 발생했다.
<img src="https://velog.velcdn.com/images/hi_young/post/b5ab4b54-bd13-424c-953e-99d84b416ada/image.png" alt=""><h2 id="원인">원인</h2>
</li>
<li>textarea에서 개행문자는 <code>\r\n</code>,  <code>\n</code> 으로 되어있기 때문에 웹에서는 줄바꿈이 먹히지 않는 것이었다.</li>
<li>replace 함수를 통해 해당 개행문자들을 <code>&lt;br&gt;</code> 로 처리해주면 된다.</li>
</ul>
<h2 id="해결과정">해결과정</h2>
<ol>
<li>replaceAll 메서드를 통해 개행문자들을 <code>&lt;br&gt;</code>로 치환시켜줬다.<pre><code class="language-jsx">// script
 this.babsangDescription =
   this.babsangDetailData.dining_description.replaceAll(/(\n|\r\n)/g,&#39;&lt;br&gt;&#39;);
</code></pre>
</li>
</ol>
<p>// template</p>
<div>{{ babsangDescription }}</div>
```


<p>❗ 이렇게 들고와서 웹에 띄우니 <code>&lt;br&gt;</code>이 그대로 보임...
<img src="https://velog.velcdn.com/images/hi_young/post/d3f47dbe-2421-440d-8c97-9b92ef868a25/image.png" alt=""></p>
<ol start="2">
<li>v-html 디텍티브를 통해 태그 문자열을 파싱해서 화면에 나타내도록 했다.<pre><code class="language-jsx">// template
&lt;div v-html=&quot;babsangDescription&quot;&gt;&lt;/div&gt;</code></pre>
<img src="https://velog.velcdn.com/images/hi_young/post/b5979fa3-1ada-4c97-8aeb-985d4cd7c925/image.png" alt=""></li>
</ol>
<p>성공~</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CSS] mix-blend-mode]]></title>
            <link>https://velog.io/@hi_young/TIL-mix-blend-modenuxt-routing</link>
            <guid>https://velog.io/@hi_young/TIL-mix-blend-modenuxt-routing</guid>
            <pubDate>Tue, 19 Jul 2022 15:14:35 GMT</pubDate>
            <description><![CDATA[<h2 id="mix-blend-mode">mix-blend-mode</h2>
<p>awwards나 웹을 보면 글자가 다른 요소랑 겹칠때 색상이 대비되는 효과를 자주 보았다.
난 대단한 스크립트가 들어가는건가 했는데 css 속성 하나만 주면 끝나는 거였다.</p>
<pre><code>/* 키워드 값 */
mix-blend-mode: normal;
mix-blend-mode: multiply;
mix-blend-mode: screen;
mix-blend-mode: overlay;
mix-blend-mode: darken;
mix-blend-mode: lighten;
mix-blend-mode: color-dodge;
mix-blend-mode: color-burn;
mix-blend-mode: hard-light;
mix-blend-mode: soft-light;
mix-blend-mode: difference;
mix-blend-mode: exclusion;
mix-blend-mode: hue;
mix-blend-mode: saturation;
mix-blend-mode: color;
mix-blend-mode: luminosity;

/* 전역 값 */
mix-blend-mode: initial;
mix-blend-mode: inherit;
mix-blend-mode: unset;</code></pre><p>폰트나 요소에는 background, color로 적용할 수 있고
svg는 가장 최하단에 있는 태그의 fill에 색상을 적용하고 mix-blend-mode 속성을 줘야 한다.</p>
<p>간단하게 트렌디한 요소의 디자인을 만들 수 있어 좋은 속성인 것 같다. </p>
<h2 id="reference">Reference</h2>
<ul>
<li><a href="https://developer.mozilla.org/ko/docs/Web/CSS/mix-blend-mode">https://developer.mozilla.org/ko/docs/Web/CSS/mix-blend-mode</a></li>
</ul>
]]></description>
        </item>
    </channel>
</rss>