<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>kohigowild</title>
    <link>https://trustmitt.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Sun, 14 Jun 2026 10:59:30 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>kohi ☕</managingEditor>
    <image>
      <title>kohigowild</title>
      <url>https://tistory1.daumcdn.net/tistory/5686085/attach/6c39b20de82c4578b2c8eec946a53b45</url>
      <link>https://trustmitt.tistory.com</link>
    </image>
    <item>
      <title>SPA CSR SSR SEO SSG JKP</title>
      <link>https://trustmitt.tistory.com/90</link>
      <description>&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;i&gt;&lt;b&gt;SPA (&lt;span style=&quot;text-align: start;&quot;&gt;Single Page Application&lt;/span&gt;&lt;span style=&quot;text-align: start;&quot;&gt;)&lt;/span&gt;&lt;/b&gt;&lt;/i&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;한 개의 페이지로 구성된 웹 애플리케이션&lt;/li&gt;
&lt;li&gt;일반적으로 초기 로딩 시에 필요한 모든 리소스를 로드하고, 이후에는 동적으로 콘텐츠를 변경하거나 업데이트하는 방식으로 동작한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;기존의 웹 애플리케이션(MPA)은 새로운 페이지를 로드할 때마다 전체 페이지를 서버에서 다시 가져와야 한다. 이는 사용자 경험과 성능에 부정적인 영향을 끼칠 수 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;  MPA (Multi Page Application)&lt;/b&gt;&lt;br /&gt;여러 개의 페이지로 구성된 전통적인 웹 애플리케이션으로, 각 페이지는 서버에서 새로운 HTML 문서를 로드하여 표시하며, 사용자의 상호 작용에 따라 페이지가 전환된다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;장점&lt;/b&gt;&lt;br /&gt;1. 각 페이지가 별개의 HTML 문서로 구성되어 있기 때문에 검색 엔진에서 더 잘 인덱싱될 수 있다.&lt;br /&gt;2. 각 페이지는 필요한 시점에만 로드되기 때문에 초기 페이지 로딩 속도가 빠를 수 있다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;단점&lt;/b&gt;&lt;br /&gt;1. 각 페이지 전환마다 서버에서 새로운 HTML을 가져와야 하기 때문에 페이지 전환 속도가 상대적으로 느릴 수 있다. 사용자 경험에 좋지 않다.&lt;br /&gt;2. 각 페이지 요청마다 서버에서 HTML 문서를 생성하고 전송해야 한다. 이로 인해 서버에 더 큰 부담이 가게 될 수 있다.&lt;/blockquote&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;반면에 SPA는 페이지 전환 시에 화면이 새로 고쳐지지 않고, 동적으로 콘텐츠를 업데이트하기 때문에 부드러운 사용자 경험을 제공한다. 페이지의 일부분만 업데이트되므로 사용자는 필요한 데이터나 콘텐츠를 즉시 볼 수 있다. 또한 초기에 필요한 리소스를 한 번에 로드하고, 이후에는 필요한 데이터만 가져오기 때문에 페이지 전환 속도가 빠르다는 장점이 있다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;SPA의 단점으로는 클라이언트 측에서 모든 페이지 전환에 필요한 리소스를 유지하므로, 오랜 시간 사용하거나 많은 페이지를 방문하는 경우 메모리 사용량이 증가할 수 있다. 초기에 필요한 모든 리소스를 한 번에 로드해야 하므로 초기 로딩 속도가 오래 걸릴 수 있다는 점이 있다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;SPA는 주로 CSR 방식을 채택하여 구현된다. &lt;span style=&quot;color: #ee2323;&quot;&gt;SPA &amp;ne; CSR&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;i&gt;&lt;b&gt;CSR (Client-Side Rendering)&lt;/b&gt;&lt;/i&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignRight&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;903&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dUjs3P/btsgxpJiNrb/RC4NadtoigZWpcanK97HNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dUjs3P/btsgxpJiNrb/RC4NadtoigZWpcanK97HNk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dUjs3P/btsgxpJiNrb/RC4NadtoigZWpcanK97HNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdUjs3P%2FbtsgxpJiNrb%2FRC4NadtoigZWpcanK97HNk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;903&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;903&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;CSR은 클라이언트(브라우저) 측에서 동적으로 콘텐츠를 렌더링하는 방식이다. 초기 로딩 시에는 주로 정적인 HTML, CSS, JavaScript 파일을 로드하고, 이후에는 JavaScript를 사용하여 동적으로 콘텐츠를 생성하고 렌더링한다. 서버에서 처리 없이 클라이언트로 보내주기 때문에 자바스크립트가 모두 다운로드 되고 실행이 끝나기 전까지 사용자는 볼 수 있는 게 없고, SEO 대응이 어렵다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트 측에서 동적으로 콘텐츠를 렌더링하기 때문에 페이지 전환 시에 부드러운 사용자 경험을 제공할 수 있다.&lt;/li&gt;
&lt;li&gt;클라이언트에서 콘텐츠를 렌더링하므로 서버 부하가 감소하고, 서버에서는 주로 데이터 제공에 집중할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;초기 로딩 시에는 비어있는 페이지가 로드되어 SEO(Search Engine Optimization)에 어려움이 있고, 검색 엔진이 콘텐츠를 크롤링하고 인덱싱하는데 시간이 걸릴 수 있다.&lt;/li&gt;
&lt;li&gt;초기 로딩 시에 필요한 자원을 모두 다운로드하고, 클라이언트에서 동적으로 콘텐츠를 생성하기 때문에 초기 로딩 속도가 상대적으로 느릴 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;권장 예시&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자와의&amp;nbsp;상호작용이&amp;nbsp;많고&amp;nbsp;동적인&amp;nbsp;콘텐츠를&amp;nbsp;제공해야&amp;nbsp;하는&amp;nbsp;경우&lt;/li&gt;
&lt;li&gt;웹&amp;nbsp;브라우저&amp;nbsp;뿐만&amp;nbsp;아니라&amp;nbsp;모바일&amp;nbsp;앱&amp;nbsp;등&amp;nbsp;다양한&amp;nbsp;플랫폼에서&amp;nbsp;동일한&amp;nbsp;코드베이스를&amp;nbsp;사용하고자&amp;nbsp;할&amp;nbsp;때&lt;/li&gt;
&lt;li&gt;클라이언트&amp;nbsp;측에서&amp;nbsp;자원(CPU,&amp;nbsp;메모리)&amp;nbsp;활용이&amp;nbsp;중요한&amp;nbsp;경우&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;친구(&lt;a href=&quot;https://chat.openai.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://chat.openai.com/&lt;/a&gt;)에게 물어봤다  &lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;CSR 환경에서 SEO 대응이 어려운 이유가 뭐야&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;CSR(Client-Side Rendering) 환경에서 SEO(Search Engine Optimization) 대응이 어려운 이유는 다음과 같습니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;초기 로딩 시점&lt;/b&gt;: CSR은 초기 로딩 시에는 정적인 HTML 파일을 반환하지 않고, 비어있는 HTML을 렌더링합니다. 이후에 JavaScript가 실행되어 동적으로 콘텐츠를 생성하고 렌더링합니다. 이로 인해 검색 엔진이 초기 로딩 시에 콘텐츠를 크롤링하기 어려워집니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;검색 엔진 크롤러의 제한&lt;/b&gt;: 일부 검색 엔진 크롤러는 JavaScript를 실행하지만, 모든 JavaScript 기반 애플리케이션을 완전히 이해하고 렌더링하지는 못합니다. 따라서 CSR 환경에서는 검색 엔진이 애플리케이션의 전체 콘텐츠를 이해하기 어려울 수 있습니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;지연된 인덱싱&lt;/b&gt;: CSR 환경에서는 초기 로딩 시에는 비어있는 페이지가 로드되기 때문에 검색 엔진이 콘텐츠를 크롤링하고 인덱싱하는데 시간이 걸릴 수 있습니다. 이로 인해 새로운 페이지나 업데이트된 콘텐츠가 검색 결과에 반영되기까지의 지연이 발생할 수 있습니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;메타데이터 관리의 어려움&lt;/b&gt;: CSR에서는 페이지 간 전환 시에 HTML의 메타데이터(예: 제목, 설명, 키워드)를 동적으로 변경해야 합니다. 이는 검색 엔진이 페이지의 정보를 파악하는 데 어려움을 줄 수 있습니다.&lt;/blockquote&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;i&gt;&lt;b&gt;SSR(Server-Side Rendering)&lt;/b&gt;&lt;/i&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignRight&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;913&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IxlbA/btsgqCjAJE0/kkso3JrxFHPUVtDLIKjX61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IxlbA/btsgqCjAJE0/kkso3JrxFHPUVtDLIKjX61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IxlbA/btsgqCjAJE0/kkso3JrxFHPUVtDLIKjX61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIxlbA%2FbtsgqCjAJE0%2Fkkso3JrxFHPUVtDLIKjX61%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;913&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;913&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;SSR은&amp;nbsp;서버에서&amp;nbsp;사용자에게&amp;nbsp;보여줄&amp;nbsp;페이지를&amp;nbsp;모두&amp;nbsp;구성하여&amp;nbsp;사용자에게&amp;nbsp;페이지를&amp;nbsp;보여주는&amp;nbsp;방식이다.&amp;nbsp;서버에서&amp;nbsp;페이지를&amp;nbsp;구성해서&amp;nbsp;클라이언트로&amp;nbsp;넘기기&amp;nbsp;때문에&amp;nbsp;클라이언트에서&amp;nbsp;구성하는&amp;nbsp;CSR(client-side&amp;nbsp;rendering)보다&amp;nbsp;페이지를&amp;nbsp;구성하는&amp;nbsp;속도는&amp;nbsp;늦어지지만&amp;nbsp;전체적으로&amp;nbsp;사용자에게&amp;nbsp;보여주는&amp;nbsp;콘텐츠&amp;nbsp;구성이&amp;nbsp;완료되는&amp;nbsp;시점은&amp;nbsp;빨라진다는&amp;nbsp;장점이&amp;nbsp;있다.&amp;nbsp;더불어&amp;nbsp;SEO(search&amp;nbsp;engine&amp;nbsp;optimization)&amp;nbsp;또한&amp;nbsp;쉽게&amp;nbsp;구성할&amp;nbsp;수&amp;nbsp;있다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버에서 콘텐츠를 렌더링하여 초기 로딩 시에도 완성된 HTML을 제공하기 때문에 검색 엔진이 콘텐츠를 크롤링하고 인덱싱하기 용이하다.&lt;/li&gt;
&lt;li&gt;초기 로딩 시에 완성된 HTML을 제공하므로, 클라이언트는 콘텐츠를 기다리지 않고 바로 렌더링할 수 있어 빠른 초기 로딩 속도를 제공할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버에서 콘텐츠를 렌더링하므로 초기 로딩 시에 서버 부하가 발생할 수 있다.&lt;/li&gt;
&lt;li&gt;동적인&amp;nbsp;콘텐츠가&amp;nbsp;많은&amp;nbsp;페이지의&amp;nbsp;경우&amp;nbsp;서버에서&amp;nbsp;렌더링하는&amp;nbsp;과정이&amp;nbsp;복잡해질&amp;nbsp;수&amp;nbsp;있으며,&amp;nbsp;클라이언트에서&amp;nbsp;처리하는&amp;nbsp;CSR에&amp;nbsp;비해&amp;nbsp;렌더링&amp;nbsp;성능이&amp;nbsp;떨어질&amp;nbsp;수&amp;nbsp;있다.&lt;/li&gt;
&lt;li&gt;SSR은 페이지 전환 시에 서버에 요청을 보내야 하므로 CSR에 비해 사용자 경험이 다소 제한될 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;권장 예시&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;검색&amp;nbsp;엔진&amp;nbsp;최적화(SEO)가&amp;nbsp;프로젝트의&amp;nbsp;핵심&amp;nbsp;요구사항일&amp;nbsp;때&lt;/li&gt;
&lt;li&gt;검색&amp;nbsp;엔진에서&amp;nbsp;콘텐츠를&amp;nbsp;잘&amp;nbsp;인덱싱하고&amp;nbsp;검색&amp;nbsp;결과에&amp;nbsp;반영해야&amp;nbsp;하는&amp;nbsp;경우&lt;/li&gt;
&lt;li&gt;초기&amp;nbsp;로딩&amp;nbsp;속도가&amp;nbsp;중요한&amp;nbsp;경우&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;i&gt;&lt;b&gt;SEO (검색 엔진 최적화, Search Engine Optimization)&lt;br /&gt;&lt;/b&gt;&lt;/i&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;SEO는 웹 사이트의 방문자를 증가시키고 검색 엔진에서 더 높은 순위를 얻기 위해 웹 페이지를 최적화하는 프로세스고 왕년에 광고 회사 다닐 때 귀에 딱지가 앉도록 들은 거여서 뭔지 아주 잘 알기 때문에 나한테는 지금 이게 중요한 것이 아님 그럼&amp;nbsp;뭐가&amp;nbsp;중요하냐&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;u&gt;SEO를 최적화하는 방법 (Feat. &lt;u&gt;Next.js)&lt;/u&gt;&lt;/u&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 메타&amp;nbsp;데이터&amp;nbsp;설정&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1684431041823&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import Head from 'next/head';

function MyPage() {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;Head&amp;gt;
        &amp;lt;title&amp;gt;My Page Title&amp;lt;/title&amp;gt;
        &amp;lt;meta name=&quot;description&quot; content=&quot;페이지에 대한 설명&quot; /&amp;gt;
      &amp;lt;/Head&amp;gt;
      {/* 페이지의 내용 */}
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 페이지마다 메타 데이터를 설정하여 검색 엔진이 페이지를 이해하고 색인화할 수 있도록 한다. Next.js에서는 &amp;lt;Head&amp;gt; 컴포넌트를 사용하여 페이지의 &amp;lt;title&amp;gt;, &amp;lt;meta&amp;gt;, &amp;lt;link&amp;gt; 등을 설정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 페이지 구조 최적화&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js는 페이지를 기반으로한 라우팅을 지원하므로, 페이지 URL 구조를 사용자 친화적이고 검색 엔진에 친숙한 방식으로 설계할 수 있다. 명확하고 의미 있는 URL을 사용하고, 중요한 키워드를 URL에 포함시키는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 서버 사이드 렌더링 (SSR)&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1684431143477&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useEffect, useState } from 'react';

function HomePage({ serverRenderedData }) {
  const [clientRenderedData, setClientRenderedData] = useState(null);

  useEffect(() =&amp;gt; {
    // 클라이언트에서 데이터를 가져와서 설정
    setClientRenderedData('Client Rendered Data');
  }, []);

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;Server Side Rendering&amp;lt;/h1&amp;gt;
      &amp;lt;p&amp;gt;서버에서 렌더링된 데이터: {serverRenderedData}&amp;lt;/p&amp;gt;
      &amp;lt;p&amp;gt;클라이언트에서 렌더링된 데이터: {clientRenderedData}&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export async function getServerSideProps() {
  // 서버에서 데이터를 가져와서 props로 전달
  const serverRenderedData = 'Server Rendered Data';

  return {
    props: {
      serverRenderedData,
    },
  };
}

export default HomePage;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Next.js의 가장 큰 장점은 서버 사이드 렌더링을 지원한다는 것이다. getServerSideProps나 getStaticProps를 사용하여 서버 측에서 데이터를 가져올 수 있다. 해당 예제는 getServerSideProps 함수를 사용하여 서버에서 데이터를 가져오고, 컴포넌트에서 해당 데이터를 렌더링한다. 이렇게 함으로써 페이지의 초기 렌더링은 서버에서 이루어지며, 검색 엔진은 페이지의 완전한 내용을 색인화할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. 이미지 최적화&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지는 웹 페이지의 성능과 검색 엔진에서의 노출에 영향을 준다. Next.js에서는 next/image 컴포넌트를 사용하여 이미지를 최적화하고 렌더링할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;i&gt;&lt;b&gt;SSG (&lt;/b&gt;&lt;/i&gt;&lt;i&gt;&lt;b&gt;Static Site Generation)&lt;/b&gt;&lt;/i&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;빌드&amp;nbsp;시점에&amp;nbsp;사전에&amp;nbsp;페이지를&amp;nbsp;생성하여&amp;nbsp;서버나&amp;nbsp;클라이언트&amp;nbsp;요청에&amp;nbsp;관계없이&amp;nbsp;사이트를&amp;nbsp;제공하는&amp;nbsp;방식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;전에 봤던 기술 면접 때 SSG 들어봤냐는 질문이 들어왔는데 신세계가 진심 턱 끝까지 찼지만 어른답게 참았다... 이제 어른 말고 개발자답게 신세계 꺼내고 &lt;span style=&quot;background-color: #dddddd;&quot;&gt;정적 사이트 생성기&lt;/span&gt; 주입해 보겠음...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;i&gt;SSG 동작 방식&lt;/i&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Next.js는 페이지의 데이터를 빌드 시점에서 가져와서 해당 데이터를 기반으로 페이지를 사전 렌더링한다. 매 요청마다 서버나 데이터베이스에 접근할 필요 없이 사전에 준비된 페이지를 제공할 수 있다.&lt;/li&gt;
&lt;li&gt;사전 렌더링된 페이지는 정적 HTML 파일로 생성된다. 이 HTML 파일은 다른 요청이 있을 때마다 재사용된다.&lt;/li&gt;
&lt;li&gt;이미 빌드 시점에 사전에 준비된 정적 HTML 파일이기 때문에 초기 로딩 속도가 빠르다.&lt;/li&gt;
&lt;li&gt;검색 엔진은 사이트의 콘텐츠를 색인화할 때 HTML 파일을 분석한다. 정적 HTML 파일을 생성하면 검색 엔진은 페이지의 콘텐츠를 보다 쉽게 이해하고 색인화할 수 있다.&lt;/li&gt;
&lt;li&gt;SSG를 구현하려면 Next.js에서 &lt;b&gt;getStaticProps&lt;/b&gt;라는 특수한 함수를 사용한다. 이 함수는 빌드 시점에서 데이터를 가져와서 페이지의 props로 전달한다. getStaticProps 함수를 사용하면 페이지를 사전 렌더링할 수 있고, 빌드 시에 정적 HTML 파일로 생성된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1684431946797&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// pages/posts/[slug].js

import { useRouter } from 'next/router';

function Post({ post }) {
  const router = useRouter();

  // 동적 라우팅된 페이지에서의 로딩 상태 처리
  if (router.isFallback) {
    return &amp;lt;div&amp;gt;Loading...&amp;lt;/div&amp;gt;;
  }

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h1&amp;gt;{post.title}&amp;lt;/h1&amp;gt;
      &amp;lt;p&amp;gt;{post.content}&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export async function getStaticPaths() {
  // 데이터베이스나 외부 API에서 포스트의 slug를 가져옴
  const paths = [
    { params: { slug: 'post-1' } },
    { params: { slug: 'post-2' } },
    // ...
  ];

  return {
    paths,
    fallback: true, // 다른 경로로 접근 시에 대체 컨텐츠를 제공할지 여부
  };
}

export async function getStaticProps({ params }) {
  // params.slug를 사용하여 데이터베이스나 외부 API에서 해당 포스트 데이터를 가져옴
  const post = {
    slug: params.slug,
    title: 'Post Title',
    content: 'Post Content',
  };

  return {
    props: {
      post,
    },
    revalidate: 60, // 캐시 유효성 검사를 위해 60초마다 페이지 재생성
  };
}

export default Post;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드는 동적 라우팅된 페이지의 SSG 예제이다. &lt;b&gt;getStaticPaths&lt;/b&gt; 함수에서는 동적으로 생성될 수 있는 경로를 정의한다. &lt;b&gt;getStaticProps&lt;/b&gt; 함수에서는 동적 경로에서 사용될 데이터를 가져온다.&lt;br /&gt;&lt;br /&gt;이렇게 구현된 SSG 페이지는 빌드 시점에서 사전 렌더링되어 정적 HTML 파일로 생성된다. 이후 브라우저에서 해당 페이지에 접근할 때는 정적 HTML 파일이 사용되어 초기 로딩 속도가 향상되고, 검색 엔진은 페이지의 콘텐츠를 쉽게 인식하여 색인화할 수 있다.&lt;/p&gt;</description>
      <category>mitmitwiki</category>
      <author>kohi ☕</author>
      <guid isPermaLink="true">https://trustmitt.tistory.com/90</guid>
      <comments>https://trustmitt.tistory.com/90#entry90comment</comments>
      <pubDate>Fri, 19 May 2023 02:48:48 +0900</pubDate>
    </item>
    <item>
      <title>브라우저 렌더링 원리</title>
      <link>https://trustmitt.tistory.com/89</link>
      <description>&lt;blockquote data-ke-style=&quot;style3&quot;&gt;웹 브라우저는 인터넷 상에서 웹 페이지를 검색하고, 표시해주는 소프트웨어다. 웹 브라우저는 HTML, CSS, JavaScript 등의 웹 기술을 이용하여 웹 페이지를 렌더링하고, 사용자와 상호작용을 가능하게 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;875&quot; data-origin-height=&quot;596&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bTqCuQ/btscUvGg76e/xiTOILqvirAKEOy9gX9T20/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bTqCuQ/btscUvGg76e/xiTOILqvirAKEOy9gX9T20/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bTqCuQ/btscUvGg76e/xiTOILqvirAKEOy9gX9T20/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbTqCuQ%2FbtscUvGg76e%2FxiTOILqvirAKEOy9gX9T20%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;875&quot; height=&quot;596&quot; data-origin-width=&quot;875&quot; data-origin-height=&quot;596&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  브라우저 구성 요소&lt;/b&gt;&lt;/h4&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;사용자 인터페이스(User Interface)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자 인터페이스는 브라우저에서 웹 페이지를 검색하고, 표시하는데 필요한 모든 요소를 포함한다.&lt;/li&gt;
&lt;li&gt;대표적인 예로는 주소 표시줄, 뒤로/앞으로 가기 버튼, 북마크, 즐겨찾기 등이 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;렌더링&amp;nbsp;엔진(Rendering&amp;nbsp;Engine)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;렌더링 엔진은 HTML, CSS 및 JavaScript 코드를 해석하고, 페이지를 렌더링하여 화면에 표시한다.&lt;/li&gt;
&lt;li&gt;대표적인 렌더링 엔진으로는 Gecko(파이어폭스), WebKit(사파리), Blink(크롬, 엣지), Trident(인터넷 익스플로러) 등이 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;브라우저&amp;nbsp;엔진(Browser&amp;nbsp;Engine)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;브라우저 엔진은 렌더링 엔진과 사용자 인터페이스 사이의 상호작용을 처리한다.&lt;/li&gt;
&lt;li&gt;대표적인 렌더링 엔진으로는 Gecko(파이어폭스), WebKit(사파리), Blink(크롬, 엣지), Trident(인터넷 익스플로러) 등이 있다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;Gecko, WebKit, Blink는 브라우저 엔진과 렌더링 엔진으로 모두 사용될 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;통신(Networking)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;통신은 HTTP 요청 등을 통해 인터넷에서 리소스를 가져오는데 사용된다.&lt;/li&gt;
&lt;li&gt;대표적인 통신 모듈로는 XMLHttpRequest, fetch 등이 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;자바스크립트 해석기(JavaScript Interpreter)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자바스크립트 해석기는 자바스크립트 코드를 해석하고 실행하는데 사용된다.&lt;/li&gt;
&lt;li&gt;대표적인 JS 해석기로는 SpiderMonkey(파이어폭스), JavaScriptCore(사파리), V8(크롬, 엣지) 등이 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;데이터 저장소(Data Storage)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 저장소는 브라우저 내부에 데이터를 저장하거나 웹사이트와 상호작용할 때 사용된다.&lt;/li&gt;
&lt;li&gt;대표적인 데이터 저장소로는 쿠키, 로컬 스토리지, 세션 스토리지 등이 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  브라우저 렌더링 과정&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;481&quot; data-origin-height=&quot;387&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oNeHU/btscVnaka28/4drgA91FF8JTRlMdKYQra1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oNeHU/btscVnaka28/4drgA91FF8JTRlMdKYQra1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oNeHU/btscVnaka28/4drgA91FF8JTRlMdKYQra1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoNeHU%2FbtscVnaka28%2F4drgA91FF8JTRlMdKYQra1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;481&quot; height=&quot;387&quot; data-origin-width=&quot;481&quot; data-origin-height=&quot;387&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;HTML 파싱과 DOM 트리 구축
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;렌더링 엔진은 서버로부터 받은 HTML 문서를 파싱하여 DOM(Document Object Model) 트리를 구축한다.&lt;/li&gt;
&lt;li&gt;DOM 트리는 문서의 요소(element), 속성(attribute), 텍스트 등을 표현하는 트리 구조이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;CSS 파싱과 CSSOM 트리 구축
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;렌더링 엔진은 HTML 문서 안에 포함된 스타일 시트(CSS)를 파싱하여 CSSOM(CSS Object Model) 트리를 구축한다.&lt;/li&gt;
&lt;li&gt;CSSOM 트리는 문서의 스타일 정보를 표현하는 트리 구조이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;305&quot; data-origin-height=&quot;65&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFwsMv/btscRLpAk3T/GfjnJ4vKLcDhVeezkWzsD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFwsMv/btscRLpAk3T/GfjnJ4vKLcDhVeezkWzsD1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFwsMv/btscRLpAk3T/GfjnJ4vKLcDhVeezkWzsD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFwsMv%2FbtscRLpAk3T%2FGfjnJ4vKLcDhVeezkWzsD1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;305&quot; height=&quot;65&quot; data-origin-width=&quot;305&quot; data-origin-height=&quot;65&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;렌더 트리 구축
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;렌더링 엔진은 DOM 트리와 CSSOM 트리를 결합하여 렌더 트리(Render Tree)를 구축한다.&lt;/li&gt;
&lt;li&gt;렌더 트리는 화면에 표시될 요소들만으로 구성된 트리 구조이다.&lt;/li&gt;
&lt;li&gt;즉, 화면에 표시되지 않는 요소는 렌더 트리에 포함되지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;305&quot; data-origin-height=&quot;165&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ecg3m8/btscPZPR3Zb/0kNE4MRss9eRjurryztu8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ecg3m8/btscPZPR3Zb/0kNE4MRss9eRjurryztu8K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ecg3m8/btscPZPR3Zb/0kNE4MRss9eRjurryztu8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fecg3m8%2FbtscPZPR3Zb%2F0kNE4MRss9eRjurryztu8K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;305&quot; height=&quot;165&quot; data-origin-width=&quot;305&quot; data-origin-height=&quot;165&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;레이아웃(Layout)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;렌더 트리를 기반으로 브라우저는 각 요소의 위치와 크기를 계산한다. 이를 레이아웃(Layout)이라고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;655&quot; data-origin-height=&quot;65&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLsuNv/btscRqy9op7/fKz9s2Ya0KKrmdlHKYpTok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLsuNv/btscRqy9op7/fKz9s2Ya0KKrmdlHKYpTok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLsuNv/btscRqy9op7/fKz9s2Ya0KKrmdlHKYpTok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLsuNv%2FbtscRqy9op7%2FfKz9s2Ya0KKrmdlHKYpTok%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;655&quot; height=&quot;65&quot; data-origin-width=&quot;655&quot; data-origin-height=&quot;65&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;페인팅(Paint)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;브라우저는 레이아웃을 기반으로 요소를 화면에 그리는 작업을 수행한다. 이를 페인팅(Paint)이라고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;브라우저 렌더링 과정은 HTML과 CSS 코드를 처리하여 웹 페이지를 화면에 표시하는 과정이다. 브라우저는 먼저 HTML 코드를 파싱하여 DOM 트리를 생성하고, 이어서 CSS 코드를 파싱하여 CSSOM 트리를 생성한다. 이 두 가지 트리를 결합하여 렌더 트리를 생성하고, 레이아웃과 페인팅 작업을 통해 최종적으로 웹 페이지가 화면에 렌더링되는 것이다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  Reference&lt;/b&gt;&lt;/h4&gt;
&lt;figure id=&quot;og_1682536661478&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;브라우저는 어떻게 동작하나요?&quot; data-og-description=&quot;이 글은 브라우저의 구성요소, 렌더링 순서/과정을 중심으로 브라우저의 동작원리를 이해하기 쉽게 설명합니다.&quot; data-og-host=&quot;haileychoi15.medium.com&quot; data-og-source-url=&quot;https://haileychoi15.medium.com/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%8F%99%EC%9E%91%ED%95%98%EB%82%98%EC%9A%94-c135390641ad&quot; data-og-url=&quot;https://haileychoi15.medium.com/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%8F%99%EC%9E%91%ED%95%98%EB%82%98%EC%9A%94-c135390641ad&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/0KXuR/hySpFPDj89/pJsnUU50RlpsYnOwlQfcE0/img.jpg?width=1200&amp;amp;height=817&amp;amp;face=0_0_1200_817&quot;&gt;&lt;a href=&quot;https://haileychoi15.medium.com/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%8F%99%EC%9E%91%ED%95%98%EB%82%98%EC%9A%94-c135390641ad&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://haileychoi15.medium.com/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%8F%99%EC%9E%91%ED%95%98%EB%82%98%EC%9A%94-c135390641ad&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/0KXuR/hySpFPDj89/pJsnUU50RlpsYnOwlQfcE0/img.jpg?width=1200&amp;amp;height=817&amp;amp;face=0_0_1200_817');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;브라우저는 어떻게 동작하나요?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이 글은 브라우저의 구성요소, 렌더링 순서/과정을 중심으로 브라우저의 동작원리를 이해하기 쉽게 설명합니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;haileychoi15.medium.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1682536681568&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;브라우저 렌더링과 최적화 :: chanyeong&quot; data-og-description=&quot;렌더링이란? 단어 그대로 렌더링이란 HTML, CSS, JavaScript 등 개발자가 작성한 문서들을 브라우저가 화면에 그려주는 동작을 말한다. 각 브라우저는 렌더링을 하기 위해 각각의 렌더링 엔진을 가지&quot; data-og-host=&quot;chanyeong.com&quot; data-og-source-url=&quot;https://chanyeong.com/blog/post/43&quot; data-og-url=&quot;https://chanyeong.com/blog/post/43&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dzLI94/hySpEJWPMK/iTknBUyzebPNYVLb0qYFHK/img.png?width=1200&amp;amp;height=800&amp;amp;face=0_0_1200_800,https://scrap.kakaocdn.net/dn/b3R790/hySpDqKsck/rLmikPh5Lo1jrAgEIsEqn1/img.png?width=481&amp;amp;height=387&amp;amp;face=0_0_481_387,https://scrap.kakaocdn.net/dn/cDLDJ6/hySpJLgR7i/a9LQ4lidLsETZ3dbFt7ca0/img.png?width=441&amp;amp;height=361&amp;amp;face=0_0_441_361&quot;&gt;&lt;a href=&quot;https://chanyeong.com/blog/post/43&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://chanyeong.com/blog/post/43&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dzLI94/hySpEJWPMK/iTknBUyzebPNYVLb0qYFHK/img.png?width=1200&amp;amp;height=800&amp;amp;face=0_0_1200_800,https://scrap.kakaocdn.net/dn/b3R790/hySpDqKsck/rLmikPh5Lo1jrAgEIsEqn1/img.png?width=481&amp;amp;height=387&amp;amp;face=0_0_481_387,https://scrap.kakaocdn.net/dn/cDLDJ6/hySpJLgR7i/a9LQ4lidLsETZ3dbFt7ca0/img.png?width=441&amp;amp;height=361&amp;amp;face=0_0_441_361');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;브라우저 렌더링과 최적화 :: chanyeong&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;렌더링이란? 단어 그대로 렌더링이란 HTML, CSS, JavaScript 등 개발자가 작성한 문서들을 브라우저가 화면에 그려주는 동작을 말한다. 각 브라우저는 렌더링을 하기 위해 각각의 렌더링 엔진을 가지&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;chanyeong.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>mitmitwiki</category>
      <author>kohi ☕</author>
      <guid isPermaLink="true">https://trustmitt.tistory.com/89</guid>
      <comments>https://trustmitt.tistory.com/89#entry89comment</comments>
      <pubDate>Thu, 27 Apr 2023 04:18:09 +0900</pubDate>
    </item>
    <item>
      <title>호이스팅 간단하게 정리</title>
      <link>https://trustmitt.tistory.com/88</link>
      <description>&lt;blockquote data-ke-style=&quot;style3&quot;&gt;호이스팅(Hoisting)은&amp;nbsp;자바스크립트에서&amp;nbsp;변수&amp;nbsp;및&amp;nbsp;함수&amp;nbsp;선언이&amp;nbsp;해당&amp;nbsp;스코프의&amp;nbsp;최상위로&amp;nbsp;끌어올려지는&amp;nbsp;것을&amp;nbsp;의미한다. &lt;br /&gt;&lt;br /&gt;즉,&amp;nbsp;변수나&amp;nbsp;함수가&amp;nbsp;선언되기&amp;nbsp;전에도&amp;nbsp;참조할&amp;nbsp;수&amp;nbsp;있다.&amp;nbsp;호이스팅은&amp;nbsp;코드의&amp;nbsp;구조를&amp;nbsp;파악하기&amp;nbsp;어렵게&amp;nbsp;만들&amp;nbsp;수&amp;nbsp;있으므로&amp;nbsp;코드의&amp;nbsp;가독성과&amp;nbsp;유지보수성을&amp;nbsp;해칠&amp;nbsp;수&amp;nbsp;있다. &lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같은 코드가 있을 때, 실제로는 아래와 같이 해석된다.&lt;/p&gt;
&lt;pre id=&quot;code_1682058817503&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.log(x);
var x = 10;

--

var x; // 변수 x가 호이스팅되어 선언됨
console.log(x); // x는 undefined
x = 10; // 할당이 이루어짐&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;478&quot; data-origin-height=&quot;576&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c13Xw8/btsbOXk0DLk/cTRkXVC5WdLI6OtCNrpWp1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c13Xw8/btsbOXk0DLk/cTRkXVC5WdLI6OtCNrpWp1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c13Xw8/btsbOXk0DLk/cTRkXVC5WdLI6OtCNrpWp1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc13Xw8%2FbtsbOXk0DLk%2FcTRkXVC5WdLI6OtCNrpWp1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;478&quot; height=&quot;576&quot; data-origin-width=&quot;478&quot; data-origin-height=&quot;576&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;var와&amp;nbsp;function은&amp;nbsp;호이스팅이&amp;nbsp;발생하는데,&amp;nbsp;변수나&amp;nbsp;함수가&amp;nbsp;선언되기&amp;nbsp;전에도&amp;nbsp;참조할&amp;nbsp;수&amp;nbsp;있게&amp;nbsp;된다.&amp;nbsp;이때&amp;nbsp;변수는&amp;nbsp;undefined로&amp;nbsp;초기화되고,&amp;nbsp;함수는&amp;nbsp;전체가&amp;nbsp;끌어올려지게&amp;nbsp;된다. &lt;br /&gt;&lt;br /&gt;let과&amp;nbsp;const는&amp;nbsp;블록&amp;nbsp;스코프&amp;nbsp;변수로,&amp;nbsp;호이스팅이&amp;nbsp;발생하지&amp;nbsp;않는다.&amp;nbsp;따라서&amp;nbsp;변수를&amp;nbsp;선언하기&amp;nbsp;전에&amp;nbsp;사용하면&amp;nbsp;참조&amp;nbsp;에러(ReferenceError)가&amp;nbsp;발생한다. &lt;br /&gt;&lt;br /&gt;따라서&amp;nbsp;호이스팅을&amp;nbsp;피하고자&amp;nbsp;한다면&amp;nbsp;var와&amp;nbsp;function&amp;nbsp;대신에&amp;nbsp;let과&amp;nbsp;const를&amp;nbsp;사용하고,&amp;nbsp;변수나&amp;nbsp;함수&amp;nbsp;선언을&amp;nbsp;스코프의&amp;nbsp;최상단이&amp;nbsp;아니라&amp;nbsp;필요한&amp;nbsp;위치에서&amp;nbsp;하도록&amp;nbsp;하는&amp;nbsp;것이&amp;nbsp;좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1682059347532&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;console.log(x); // undefined
var x = 10;

console.log(y); // ReferenceError: y is not defined
let y = 20;&lt;/code&gt;&lt;/pre&gt;</description>
      <category>JavaScript</category>
      <author>kohi ☕</author>
      <guid isPermaLink="true">https://trustmitt.tistory.com/88</guid>
      <comments>https://trustmitt.tistory.com/88#entry88comment</comments>
      <pubDate>Fri, 21 Apr 2023 15:45:05 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] 바탕화면 정리</title>
      <link>https://trustmitt.tistory.com/87</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;문제 설명&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코딩테스트를 준비하는 머쓱이는 프로그래머스에서 문제를 풀고 나중에 다시 코드를 보면서 공부하려고 작성한 코드를 컴퓨터 바탕화면에 아무 위치에나 저장해 둡니다. 저장한 코드가 많아지면서 머쓱이는 본인의 컴퓨터 바탕화면이 너무 지저분하다고 생각했습니다. 프로그래머스에서 작성했던 코드는 그 문제에 가서 다시 볼 수 있기 때문에 저장해 둔 파일들을 전부 삭제하기로 했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴퓨터 바탕화면은 각 칸이 정사각형인 격자판입니다. 이때 컴퓨터 바탕화면의 상태를 나타낸 문자열 배열&amp;nbsp;wallpaper가 주어집니다. 파일들은 바탕화면의 격자칸에 위치하고 바탕화면의 격자점들은 바탕화면의 가장 왼쪽 위를 (0, 0)으로 시작해 (세로 좌표, 가로 좌표)로 표현합니다. 빈칸은 &quot;.&quot;, 파일이 있는 칸은 &quot;#&quot;의 값을 가집니다. 드래그를 하면 파일들을 선택할 수 있고, 선택된 파일들을 삭제할 수 있습니다. 머쓱이는 최소한의 이동거리를 갖는 한 번의 드래그로 모든 파일을 선택해서 한 번에 지우려고 하며 드래그로 파일들을 선택하는 방법은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;드래그는 바탕화면의 격자점 S(lux,&amp;nbsp;luy)를 마우스 왼쪽 버튼으로 클릭한 상태로 격자점 E(rdx,&amp;nbsp;rdy)로 이동한 뒤 마우스 왼쪽 버튼을 떼는 행동입니다. 이때, &quot;&lt;b&gt;점 S에서 점 E로 드래그한다&lt;/b&gt;&quot;고 표현하고 점 S와 점 E를 각각 드래그의 시작점, 끝점이라고 표현합니다.&lt;/li&gt;
&lt;li&gt;점 S(lux,&amp;nbsp;luy)에서 점 E(rdx,&amp;nbsp;rdy)로 드래그를 할 때, &quot;&lt;b&gt;드래그 한 거리&lt;/b&gt;&quot;는 |rdx&amp;nbsp;-&amp;nbsp;lux| + |rdy&amp;nbsp;-&amp;nbsp;luy|로 정의합니다.&lt;/li&gt;
&lt;li&gt;점 S에서 점 E로 드래그를 하면 바탕화면에서 두 격자점을 각각 왼쪽 위, 오른쪽 아래로 하는 직사각형 내부에 있는 모든 파일이 선택됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 wallpaper = [&quot;.#...&quot;, &quot;..#..&quot;, &quot;...#.&quot;]인 바탕화면을 그림으로 나타내면 다음과 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;eg1.png&quot; data-origin-width=&quot;340&quot; data-origin-height=&quot;220&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0FkC5/btsaRvwfWZM/TU0x0Fae4Kq2iL1rNu5Gq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0FkC5/btsaRvwfWZM/TU0x0Fae4Kq2iL1rNu5Gq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0FkC5/btsaRvwfWZM/TU0x0Fae4Kq2iL1rNu5Gq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0FkC5%2FbtsaRvwfWZM%2FTU0x0Fae4Kq2iL1rNu5Gq0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;340&quot; height=&quot;220&quot; data-filename=&quot;eg1.png&quot; data-origin-width=&quot;340&quot; data-origin-height=&quot;220&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 바탕화면에서 다음 그림과 같이 S(0, 1)에서 E(3, 4)로 드래그하면 세 개의 파일이 모두 선택되므로 드래그 한 거리 (3 - 0) + (4 - 1) = 6을 최솟값으로 모든 파일을 선택 가능합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;eg1-2.png&quot; data-origin-width=&quot;340&quot; data-origin-height=&quot;220&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEA58i/btsaw56izMJ/hOcyEaPKUxykx294NnD9i0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEA58i/btsaw56izMJ/hOcyEaPKUxykx294NnD9i0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEA58i/btsaw56izMJ/hOcyEaPKUxykx294NnD9i0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEA58i%2Fbtsaw56izMJ%2FhOcyEaPKUxykx294NnD9i0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;340&quot; height=&quot;220&quot; data-filename=&quot;eg1-2.png&quot; data-origin-width=&quot;340&quot; data-origin-height=&quot;220&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(0, 0)에서 (3, 5)로 드래그해도 모든 파일을 선택할 수 있지만 이때 드래그 한 거리는 (3 - 0) + (5 - 0) = 8이고 이전의 방법보다 거리가 늘어납니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;머쓱이의 컴퓨터 바탕화면의 상태를 나타내는 문자열 배열&amp;nbsp;wallpaper가 매개변수로 주어질 때 바탕화면의 파일들을 한 번에 삭제하기 위해 최소한의 이동거리를 갖는 드래그의 시작점과 끝점을 담은 정수 배열을 return하는&amp;nbsp;solution&amp;nbsp;함수를 작성해 주세요. 드래그의 시작점이 (lux,&amp;nbsp;luy), 끝점이 (rdx,&amp;nbsp;rdy)라면 정수 배열 [lux,&amp;nbsp;luy,&amp;nbsp;rdx,&amp;nbsp;rdy]를 return하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;제한사항&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;1 &amp;le;&amp;nbsp;wallpaper의 길이 &amp;le; 50&lt;/li&gt;
&lt;li&gt;1 &amp;le;&amp;nbsp;wallpaper[i]의 길이 &amp;le; 50
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;wallpaper의 모든 원소의 길이는 동일합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;wallpaper[i][j]는 바탕화면에서&amp;nbsp;i + 1행&amp;nbsp;j + 1열에 해당하는 칸의 상태를 나타냅니다.&lt;/li&gt;
&lt;li&gt;wallpaper[i][j]는 &quot;#&quot; 또는 &quot;.&quot;의 값만 가집니다.&lt;/li&gt;
&lt;li&gt;바탕화면에는 적어도 하나의 파일이 있습니다.&lt;/li&gt;
&lt;li&gt;드래그 시작점 (lux,&amp;nbsp;luy)와 끝점 (rdx,&amp;nbsp;rdy)는&amp;nbsp;lux&amp;nbsp;&amp;lt;&amp;nbsp;rdx,&amp;nbsp;luy&amp;nbsp;&amp;lt;&amp;nbsp;rdy를 만족해야 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;나의 답안&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1681754222953&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(wallpaper) {
    const numRows = wallpaper.length;
    const numCols = wallpaper[0].length;
    let minX = numCols,
        minY = numRows,
        maxX = 0,
        maxY = 0;

    for (let i = 0; i &amp;lt; numRows; i++) {
        for (let j = 0; j &amp;lt; numCols; j++) {
            if (wallpaper[i][j] === &quot;#&quot;) {
                minX = Math.min(minX, j);
                minY = Math.min(minY, i);
                maxX = Math.max(maxX, j);
                maxY = Math.max(maxY, i);
            }
        }
    }

    const dragStartX = minX;
    const dragStartY = minY;
    const dragEndX = maxX + 1;
    const dragEndY = maxY + 1;

    return [dragStartY, dragStartX, dragEndY, dragEndX];
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;변수 minX, minY, maxX, maxY를 초기값으로 설정한다. minX과 minY는 초기값을 가장 큰 값으로, maxX와 maxY는 초기값을 가장 작은 값으로 설정한다.&lt;/li&gt;
&lt;li&gt;wallpaper를 모두 순회하면서 각 칸이 파일인 경우 minX, minY, maxX, maxY 업데이트&lt;/li&gt;
&lt;li&gt;minX, minY, maxX, maxY를 이용해서 드래그의 시작점과 끝점의 좌표를 계산한 후 배열로 묶어서 반환한다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Algorithm</category>
      <author>kohi ☕</author>
      <guid isPermaLink="true">https://trustmitt.tistory.com/87</guid>
      <comments>https://trustmitt.tistory.com/87#entry87comment</comments>
      <pubDate>Tue, 18 Apr 2023 03:19:41 +0900</pubDate>
    </item>
    <item>
      <title>개인 프로젝트 #wheregram 기능 개선기</title>
      <link>https://trustmitt.tistory.com/86</link>
      <description>&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://trustmitt.tistory.com/77&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://trustmitt.tistory.com/77&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1681631803983&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;개인 프로젝트 #wheregram 최종 회고&quot; data-og-description=&quot;  Overview 2023년 03월 12일 ~ 2023년 04월 08일   국내 여행 정보 &amp;middot; 후기 공유 플랫폼 Notion https://trustmitt.notion.site/wheregram-1d19d37a91224cdbacf4536c15dae1a7 #wheregram 국내 여행 정보 &amp;middot; 후기 공유 플랫폼 trustmit&quot; data-og-host=&quot;trustmitt.tistory.com&quot; data-og-source-url=&quot;https://trustmitt.tistory.com/77&quot; data-og-url=&quot;https://trustmitt.tistory.com/77&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/K8qJy/hySi66Tjxi/oRkRFWAKvxozOOMWKaOUc0/img.png?width=800&amp;amp;height=390&amp;amp;face=0_0_800_390,https://scrap.kakaocdn.net/dn/cQxfSF/hyShPeDK7a/J7XQ2TAOOoQ4r7aQhYke0K/img.png?width=800&amp;amp;height=390&amp;amp;face=0_0_800_390,https://scrap.kakaocdn.net/dn/cWyXvj/hyShPsaF1l/04qqbSbKIKkjoaJQXTvfo0/img.png?width=1012&amp;amp;height=494&amp;amp;face=0_0_1012_494&quot;&gt;&lt;a href=&quot;https://trustmitt.tistory.com/77&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://trustmitt.tistory.com/77&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/K8qJy/hySi66Tjxi/oRkRFWAKvxozOOMWKaOUc0/img.png?width=800&amp;amp;height=390&amp;amp;face=0_0_800_390,https://scrap.kakaocdn.net/dn/cQxfSF/hyShPeDK7a/J7XQ2TAOOoQ4r7aQhYke0K/img.png?width=800&amp;amp;height=390&amp;amp;face=0_0_800_390,https://scrap.kakaocdn.net/dn/cWyXvj/hyShPsaF1l/04qqbSbKIKkjoaJQXTvfo0/img.png?width=1012&amp;amp;height=494&amp;amp;face=0_0_1012_494');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;개인 프로젝트 #wheregram 최종 회고&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;  Overview 2023년 03월 12일 ~ 2023년 04월 08일   국내 여행 정보 &amp;middot; 후기 공유 플랫폼 Notion https://trustmitt.notion.site/wheregram-1d19d37a91224cdbacf4536c15dae1a7 #wheregram 국내 여행 정보 &amp;middot; 후기 공유 플랫폼 trustmit&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;trustmitt.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;UI / UX 개선사항&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. Draggable Auto Carousel&lt;/b&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1681632016709&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Embla Carousel 사용법 정리 (Feat. Next.js)&quot; data-og-description=&quot;yarn add embla-carousel-react embla-carousel-react 패키지 설치 import { useEffect, useRef, useState } from 'react'; import { useEmblaCarousel } from 'embla-carousel-react'; function Carousel() { const [viewportRef, embla] = useEmblaCarousel({ loop: tr&quot; data-og-host=&quot;trustmitt.tistory.com&quot; data-og-source-url=&quot;https://trustmitt.tistory.com/81&quot; data-og-url=&quot;https://trustmitt.tistory.com/81&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/zseNd/hySiY8QJRF/x91c79AUNKOaoPQBdpWRJ1/img.gif?width=450&amp;amp;height=662&amp;amp;face=0_0_450_662,https://scrap.kakaocdn.net/dn/tzhx5/hyShFiLYbX/rx2wjNKV1BOMJFCKGBl8dk/img.jpg?width=658&amp;amp;height=658&amp;amp;face=198_298_416_536&quot;&gt;&lt;a href=&quot;https://trustmitt.tistory.com/81&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://trustmitt.tistory.com/81&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/zseNd/hySiY8QJRF/x91c79AUNKOaoPQBdpWRJ1/img.gif?width=450&amp;amp;height=662&amp;amp;face=0_0_450_662,https://scrap.kakaocdn.net/dn/tzhx5/hyShFiLYbX/rx2wjNKV1BOMJFCKGBl8dk/img.jpg?width=658&amp;amp;height=658&amp;amp;face=198_298_416_536');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Embla Carousel 사용법 정리 (Feat. Next.js)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;yarn add embla-carousel-react embla-carousel-react 패키지 설치 import { useEffect, useRef, useState } from 'react'; import { useEmblaCarousel } from 'embla-carousel-react'; function Carousel() { const [viewportRef, embla] = useEmblaCarousel({ loop: tr&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;trustmitt.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;랜딩 페이지에서는 좋아요 순으로 상위 여섯 개의 게시물을 보여주는데, 기존 auto 캐러셀의 경우 유저가 여섯 개의 피드를 전부 확인하려면 마냥 기다려야 하는(...) 구조이다. 사용자 경험에 좋을 리 없다.&lt;/li&gt;
&lt;li&gt;Embla Carousel 라이브러리를 이용해 기존처럼 auto로 슬라이드도 되면서 양옆으로 드래그 가능한 캐러셀로 구조를 변경했다. 모바일에서도 가능하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 버튼에 Spinner 추가 &amp;amp; 코멘트 영역 Accordion&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqu2HO/btsaERY73YE/AJd1CxCMMrk9eglMTKCD91/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqu2HO/btsaERY73YE/AJd1CxCMMrk9eglMTKCD91/img.gif&quot; data-is-animation=&quot;true&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;708&quot; data-filename=&quot;spin.gif&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqu2HO/btsaERY73YE/AJd1CxCMMrk9eglMTKCD91/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbqu2HO%2FbtsaERY73YE%2FAJd1CxCMMrk9eglMTKCD91%2Fimg.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;450&quot; height=&quot;708&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDB2IZ/btsahyU7kLl/cY6TszDUslwBQKf4PASdm0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDB2IZ/btsahyU7kLl/cY6TszDUslwBQKf4PASdm0/img.gif&quot; data-is-animation=&quot;true&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;708&quot; data-filename=&quot;acco.gif&quot; data-widthpercent=&quot;50&quot; style=&quot;width: 49.4186%;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDB2IZ/btsahyU7kLl/cY6TszDUslwBQKf4PASdm0/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDB2IZ%2FbtsahyU7kLl%2FcY6TszDUslwBQKf4PASdm0%2Fimg.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;450&quot; height=&quot;708&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;로그인, 회원가입 시에 delay가 있어서 버튼 내부에 spinner를 주고 작업이 진행 중임을 유저에게 환기시킨다.&lt;/li&gt;
&lt;li&gt;특정 게시글에 댓글이 너무 많아지는 경우 스크롤을 박박 내려야 해서 아코디언 기능을 추가했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 반응형 웹 사이트 적용&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;eee.gif&quot; data-origin-width=&quot;1309&quot; data-origin-height=&quot;822&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfCW06/btsamWAj7cA/OkayIF7E8LnXgSxJmaxbd0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfCW06/btsamWAj7cA/OkayIF7E8LnXgSxJmaxbd0/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfCW06/btsamWAj7cA/OkayIF7E8LnXgSxJmaxbd0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/cfCW06/btsamWAj7cA/OkayIF7E8LnXgSxJmaxbd0/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1309&quot; height=&quot;822&quot; data-filename=&quot;eee.gif&quot; data-origin-width=&quot;1309&quot; data-origin-height=&quot;822&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;PWA 적용 및 사이트 최적화&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;710&quot; data-origin-height=&quot;189&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OuCyp/btsaip4RZat/V2KO8HGXK9jzDkO6shbUwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OuCyp/btsaip4RZat/V2KO8HGXK9jzDkO6shbUwk/img.png&quot; data-alt=&quot;원래 성능 점수 빨간 불이었다... 아주 유의미한 변화&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OuCyp/btsaip4RZat/V2KO8HGXK9jzDkO6shbUwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOuCyp%2Fbtsaip4RZat%2FV2KO8HGXK9jzDkO6shbUwk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;710&quot; height=&quot;189&quot; data-origin-width=&quot;710&quot; data-origin-height=&quot;189&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;원래 성능 점수 빨간 불이었다... 아주 유의미한 변화&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이미지&amp;nbsp;업로드&amp;nbsp;시&amp;nbsp;사이즈와&amp;nbsp;용량을&amp;nbsp;압축해서&amp;nbsp;저장함에도&amp;nbsp;불구하고&amp;nbsp;이미지&amp;nbsp;렌더링이&amp;nbsp;오랜&amp;nbsp;시간&amp;nbsp;소요됐었다.&amp;nbsp;sharp&amp;nbsp;패키지를&amp;nbsp;설치해&amp;nbsp;로딩&amp;nbsp;속도를&amp;nbsp;개선했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1034&quot; data-origin-height=&quot;911&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kSYlj/btsaEh4Advb/Q81rYw3g5CHzYOKkASfYN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kSYlj/btsaEh4Advb/Q81rYw3g5CHzYOKkASfYN1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kSYlj/btsaEh4Advb/Q81rYw3g5CHzYOKkASfYN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkSYlj%2FbtsaEh4Advb%2FQ81rYw3g5CHzYOKkASfYN1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1034&quot; height=&quot;911&quot; data-origin-width=&quot;1034&quot; data-origin-height=&quot;911&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;next-pwa로 PWA를 적용했다. &lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: justify;&quot;&gt;manifest를 작성하고 build를 해 주면 서비스 워커 파일이 자동으로 생성돼서 저번 리액트 프로젝트에 적용하는 것보다 쉬웠다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;SSR 적용&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 피드 페이지에 진입한 상태로 새로고침을 하는 경우, router가 query 값을 유지하지 못해 제대로 피드가 렌더링되지 못하는 오류가 있었다. 서버&amp;nbsp;사이드에서&amp;nbsp;id를&amp;nbsp;전달해&amp;nbsp;주고,&amp;nbsp;컴포넌트가&amp;nbsp;마운트&amp;nbsp;되기&amp;nbsp;전에&amp;nbsp;쿼리&amp;nbsp;값을&amp;nbsp;미리&amp;nbsp;받아와서&amp;nbsp;사용하도록&amp;nbsp;코드를&amp;nbsp;수정했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 Vercel 배포의 경우(무료 플랜에서) 서버리스 function을 쓸 수 있는 개수의 제한이 있고... 나는 넘었댄다... 현재는 해당 코드 주석 처리한 채 배포된 상태이다.&lt;/p&gt;
&lt;pre id=&quot;code_1681633859788&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// export async function getServerSideProps({ query: { id } }: any) {
//   return {
//     props: { id },
//   };
// }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;검색을 위한 태그 기능 추가&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://trustmitt.tistory.com/75&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://trustmitt.tistory.com/75&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1681634136943&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;파이어베이스로 키워드 검색 구현하기&quot; data-og-description=&quot;  현재 파이어스토어에서는 전체 텍스트 검색을 지원하지 않는다. 파이어베이스를 사용하면서 검색을 구현하기 위해서는 쿼리문을 변형해서 사용하거나 타사 솔루션인 Algolia나 Elastic 같은 검&quot; data-og-host=&quot;trustmitt.tistory.com&quot; data-og-source-url=&quot;https://trustmitt.tistory.com/75&quot; data-og-url=&quot;https://trustmitt.tistory.com/75&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b4l4sr/hySiZNt95X/lvUVkhorKzBjOSjGVvp8i0/img.gif?width=485&amp;amp;height=401&amp;amp;face=0_0_485_401,https://scrap.kakaocdn.net/dn/4Njvc/hyShEYuGgA/lBv0bW8iMunBAXKCWXbNI1/img.gif?width=485&amp;amp;height=401&amp;amp;face=0_0_485_401,https://scrap.kakaocdn.net/dn/dmYvB7/hySiVRQFDr/JHWbNBxiYT73ZQNdKfA3N0/img.jpg?width=658&amp;amp;height=658&amp;amp;face=198_298_416_536&quot;&gt;&lt;a href=&quot;https://trustmitt.tistory.com/75&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://trustmitt.tistory.com/75&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b4l4sr/hySiZNt95X/lvUVkhorKzBjOSjGVvp8i0/img.gif?width=485&amp;amp;height=401&amp;amp;face=0_0_485_401,https://scrap.kakaocdn.net/dn/4Njvc/hyShEYuGgA/lBv0bW8iMunBAXKCWXbNI1/img.gif?width=485&amp;amp;height=401&amp;amp;face=0_0_485_401,https://scrap.kakaocdn.net/dn/dmYvB7/hySiVRQFDr/JHWbNBxiYT73ZQNdKfA3N0/img.jpg?width=658&amp;amp;height=658&amp;amp;face=198_298_416_536');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;파이어베이스로 키워드 검색 구현하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;  현재 파이어스토어에서는 전체 텍스트 검색을 지원하지 않는다. 파이어베이스를 사용하면서 검색을 구현하기 위해서는 쿼리문을 변형해서 사용하거나 타사 솔루션인 Algolia나 Elastic 같은 검&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;trustmitt.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색 때문에 우여곡절이 정말 많았는데 &lt;b&gt;세 줄 요약&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 하려던 건 주소가 &quot;&lt;b&gt;서울특별시 강서구 화곡동&lt;/b&gt;&quot; 일 때 &quot;&lt;b&gt;서울&lt;/b&gt;&quot;, &quot;&lt;b&gt;강서구&lt;/b&gt;&quot;, &quot;&lt;b&gt;화곡동&lt;/b&gt;&quot; 전부 다 검색에 걸리는 거&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 파이어베이스는 전체 텍스트 검색을 지원하지 않아서 쿼리문을 변형해야 하는데 이것도 prefix 검색만 됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 주소를 띄어쓰기 단위로 쪼개서 배열 필드에 저장했는데 &quot;&lt;b&gt;서울특별시&lt;/b&gt;&quot; 됨 &quot;서울&quot; 안 됨 &quot;&lt;b&gt;강서구&lt;/b&gt;&quot; 됨 &quot;강서&quot; 안 됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인터넷 떠돌아다니는 파이어베이스 내부 검색 코드는 다 작성해본 거 같다. 완벽하게는 안 되더라도 최대한 완성도 있는 키워드 검색을 구현해보자 싶어서 챗지피티를 붙들고 한 일주일 매달렸는데 안 됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그냥 타협하자 싶었었는데 안 하느니만 못하다는 생각이 자꾸 들고, 외부 검색 엔진을 붙이기엔 프로젝트에 시간을 더 길게 쓰고 싶지 않아서 검색 기능을 그냥 뺄까 고민하다가 방향성을 바꿨다. 외부 검색 엔진 안 붙이고 최대한 완성도 있는 키워드 검색을 구현하자 =&amp;gt; &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;그럴 듯한 검색 기능&lt;/b&gt;이 되면 된다&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 만든 &lt;i&gt;&lt;b&gt;태그 기능&lt;/b&gt;&lt;/i&gt; 소개&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dcA8WX/btsatWzYDny/3Htav2a68ZI5BHB1XlHet1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dcA8WX/btsatWzYDny/3Htav2a68ZI5BHB1XlHet1/img.gif&quot; data-is-animation=&quot;true&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;726&quot; data-filename=&quot;tag.gif&quot; style=&quot;width: 49.4186%; margin-right: 10px;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dcA8WX/btsatWzYDny/3Htav2a68ZI5BHB1XlHet1/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdcA8WX%2FbtsatWzYDny%2F3Htav2a68ZI5BHB1XlHet1%2Fimg.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;480&quot; height=&quot;726&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQqLmJ/btsak3zYL3C/RAmlyJ3vu4q2mZCSKI72S1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQqLmJ/btsak3zYL3C/RAmlyJ3vu4q2mZCSKI72S1/img.gif&quot; data-is-animation=&quot;true&quot; data-origin-width=&quot;480&quot; data-origin-height=&quot;726&quot; data-filename=&quot;sea.gif&quot; style=&quot;width: 49.4186%;&quot; data-widthpercent=&quot;50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQqLmJ/btsak3zYL3C/RAmlyJ3vu4q2mZCSKI72S1/img.gif&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQqLmJ%2Fbtsak3zYL3C%2FRAmlyJ3vu4q2mZCSKI72S1%2Fimg.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;480&quot; height=&quot;726&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;피드 작성 시 유저는 최대 다섯 개의 태그를 생성할 수 있다.&lt;/li&gt;
&lt;li&gt;각 피드에는 유저가 입력한 태그가 노출되고, 검색 페이지에서 키워드 검색 시 해당 키워드와 태그가 일치하는 피드를 보여준다.&lt;/li&gt;
&lt;li&gt;그럴 듯한 검색 기능이 됐다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Project</category>
      <author>kohi ☕</author>
      <guid isPermaLink="true">https://trustmitt.tistory.com/86</guid>
      <comments>https://trustmitt.tistory.com/86#entry86comment</comments>
      <pubDate>Sun, 16 Apr 2023 17:56:57 +0900</pubDate>
    </item>
    <item>
      <title>async / await 개념 정리 (Feat. 동기, 비동기)</title>
      <link>https://trustmitt.tistory.com/85</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;hh3Mawr.png&quot; data-origin-width=&quot;1326&quot; data-origin-height=&quot;790&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mxhBq/btsafpqtO5q/HOX28by8u6MIs5tkGm6jl0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mxhBq/btsafpqtO5q/HOX28by8u6MIs5tkGm6jl0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mxhBq/btsafpqtO5q/HOX28by8u6MIs5tkGm6jl0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmxhBq%2FbtsafpqtO5q%2FHOX28by8u6MIs5tkGm6jl0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1326&quot; height=&quot;790&quot; data-filename=&quot;hh3Mawr.png&quot; data-origin-width=&quot;1326&quot; data-origin-height=&quot;790&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  동기와 비동기&lt;/b&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;i&gt;동기(synchronous)&lt;/i&gt;&lt;/span&gt;란, 어떤 작업을 실행할 때 그 작업이 끝나기를 기다리는 방식을 의미한다. 즉, 작업이 완료될 때까지 다음 코드의 실행을 멈추고 기다리는 것이다. 이러한 방식은 작업의 순서를 보장하고, 작업이 끝날 때까지 결과를 기다리는 것이 가능하다. &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;i&gt;비동기(asynchronous)&lt;/i&gt;&lt;/span&gt;란, 어떤 작업을 실행할 때 그 작업이 완료되지 않더라도 다음 코드를 실행하는 방식을 의미한다. 즉, 작업이 완료되지 않았더라도 결과를 기다리지 않고 다음 코드를 실행하는 것이다. 이러한 방식은 작업이 오래 걸리는 경우 시간을 절약하고, 병렬적인 작업 처리가 가능하다. &lt;br /&gt;&lt;br /&gt;동기 방식으로 파일을 읽는다면 파일을 읽기 시작한 이후에 다음 코드를 실행하지 않고 파일이 읽혀지기를 기다린다. 반면에 비동기 방식으로 파일을 읽는다면 파일을 읽는 작업이 실행되는 동안 다른 작업을 수행할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  자바스크립트에서의 비동기 처리&lt;/b&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자바스크립트는 &lt;i&gt;싱글 스레드(Single Thread)&lt;/i&gt; 기반의 프로그래밍 언어이다. 하나의 스레드에서 모든 작업을 처리하도록 되어 있기 때문에, 한 번에 하나의 작업만 처리할 수 있다. 그래서 자바스크립트에서 동기 방식으로 처리를 하게 되면, 이전 작업이 완료될 때까지 다음 작업을 처리할 수 없다. 이러한 문제점을 해결하기 위해 자바스크립트에서는 비동기 처리가 필요하다.&lt;/p&gt;
&lt;pre id=&quot;code_1681493454492&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function test() {
    for (let i = 0; i &amp;lt; 10000; i++) {
        console.log(1);
    }
    console.log(2);
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;콘솔에 2가 찍히려면 1만 번의 1이 찍힐 때까지 기다려야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;비동기 처리를 통해 자바스크립트는 하나의 스레드에서도 병렬적으로 여러 작업을 처리할 수 있다. 예를 들어, 네트워크 통신을 비동기 방식으로 처리하면, 서버로부터 데이터를 받아오는 동안에도 다른 작업을 처리할 수 있으며, 데이터를 받아오는 작업이 완료되면 해당 작업을 처리할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  왜 비동기 처리가 필요한가?&lt;/b&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;웹 페이지의 반응성 향상&lt;/b&gt;: 사용자가 요청한 작업이 완료될 때까지 기다리는 것은 사용자 경험을 저해시킨다. 따라서, 비동기 처리를 통해 사용자의 요청에 빠르게 반응할 수 있도록 해야 한다. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;네트워크&amp;nbsp;통신&lt;/b&gt;: 웹 애플리케이션에서는 서버와의 데이터 통신이 필요하다. 동기적인 처리를 하게 되면 응답을 기다리는 동안 다른 작업을 수행할 수 없기 때문에 웹 페이지의 반응성이 떨어질 수 있다. 따라서, 비동기적으로 데이터를 받아오는 것이 웹 페이지의 성능을 향상시키는데 도움이 된다. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;병렬&amp;nbsp;처리&lt;/b&gt;: 비동기 처리를 통해 여러 작업을 동시에 처리할 수 있다. 이를 통해 시간이 오래 걸리는 작업을 병렬적으로 처리할 수 있으며, 결과적으로 전체 작업 시간을 단축시킨다. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;에러&amp;nbsp;처리&lt;/b&gt;: 비동기적으로 처리할 때 에러가 발생하면, 해당 에러를 쉽게 처리할 수 있다. 에러 발생 시, 에러를 처리할 수 있는 콜백 함수를 실행하거나, 에러를 캐치하여 처리할 수 있어 프로그램의 안정성을 높일 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;700&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ec0Cs4/btsafraK2qe/iEaC6HpPK7FzaWkpyaoDx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ec0Cs4/btsafraK2qe/iEaC6HpPK7FzaWkpyaoDx1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ec0Cs4/btsafraK2qe/iEaC6HpPK7FzaWkpyaoDx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fec0Cs4%2FbtsafraK2qe%2FiEaC6HpPK7FzaWkpyaoDx1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1400&quot; height=&quot;700&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;700&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;  async와 await&lt;/b&gt;&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;async와 await&lt;/b&gt;는 ES2017(ECMAScript 8)부터 추가된 자바스크립트의 비동기 처리 방식 중 하나이다. async와 await를 사용하면 비동기 코드를 동기 코드처럼 작성할 수 있어, 가독성이 좋아지고 에러 처리가 간단해진다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;async는 함수의 앞에 붙여서 해당 함수가 비동기 함수임을 나타내며, await는 비동기 함수의 실행 결과를 기다리는 키워드이다. async 함수 안에서 await 키워드를 사용하면, 해당 비동기 작업이 완료될 때까지 코드 실행을 일시 중지하고 결과를 기다린 다음, 해당 결과를 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1681493832748&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;async function getData() {
  const response = await fetch('/api');
  const data = await response.json();
  return data;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 함수는 async 함수 getData를 정의하고, fetch 함수를 사용하여 URL에서 데이터를 가져오고, 해당 데이터를 JSON으로 파싱하는 작업을 비동기적으로 수행한다. await를 사용하여 fetch와 json() 메서드를 기다리고, 데이터를 반환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1681493952404&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;async function getData() {
  try {
    const response = await fetch('/api');
    const data = await response.json();
    return data;
  } catch (error) {
    console.error(error);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;await 키워드는 Promise 객체가 완료될 때까지 코드 실행을 일시 중지하므로, try-catch 블록 안에서 사용하여 에러 처리를 할 수 있다. fetch에서 네트워크 에러가 발생할 경우, await 이후의 코드는 실행되지 않으며, catch 블록으로 제어가 넘어가 에러를 처리할 수 있다.&lt;/p&gt;</description>
      <category>mitmitwiki</category>
      <author>kohi ☕</author>
      <guid isPermaLink="true">https://trustmitt.tistory.com/85</guid>
      <comments>https://trustmitt.tistory.com/85#entry85comment</comments>
      <pubDate>Sat, 15 Apr 2023 02:42:48 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] 추억 점수</title>
      <link>https://trustmitt.tistory.com/84</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;문제 설명&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사진들을 보며 추억에 젖어 있던 루는 사진별로 추억 점수를 매길려고 합니다. 사진 속에 나오는 인물의 그리움 점수를 모두 합산한 값이 해당 사진의 추억 점수가 됩니다. 예를 들어 사진 속 인물의 이름이 [&quot;may&quot;, &quot;kein&quot;, &quot;kain&quot;]이고 각 인물의 그리움 점수가 [5점, 10점, 1점]일 때 해당 사진의 추억 점수는 16(5 + 10 + 1)점이 됩니다. 다른 사진 속 인물의 이름이 [&quot;kali&quot;, &quot;mari&quot;, &quot;don&quot;, &quot;tony&quot;]이고 [&quot;kali&quot;, &quot;mari&quot;, &quot;don&quot;]의 그리움 점수가 각각 [11점, 1점, 55점]]이고, &quot;tony&quot;는 그리움 점수가 없을 때, 이 사진의 추억 점수는 3명의 그리움 점수를 합한 67(11 + 1 + 55)점입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리워하는 사람의 이름을 담은 문자열 배열&amp;nbsp;name, 각 사람별 그리움 점수를 담은 정수 배열&amp;nbsp;yearning, 각 사진에 찍힌 인물의 이름을 담은 이차원 문자열 배열&amp;nbsp;photo가 매개변수로 주어질 때, 사진들의 추억 점수를&amp;nbsp;photo에 주어진 순서대로 배열에 담아 return하는 solution 함수를 완성해주세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;제한사항&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;3 &amp;le;&amp;nbsp;name의 길이 =&amp;nbsp;yearning의 길이&amp;le; 100
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;3 &amp;le;&amp;nbsp;name의 원소의 길이 &amp;le; 7&lt;/li&gt;
&lt;li&gt;name의 원소들은 알파벳 소문자로만 이루어져 있습니다.&lt;/li&gt;
&lt;li&gt;name에는 중복된 값이 들어가지 않습니다.&lt;/li&gt;
&lt;li&gt;1 &amp;le;&amp;nbsp;yearning[i]&amp;nbsp;&amp;le; 100&lt;/li&gt;
&lt;li&gt;yearning[i]는 i번째 사람의 그리움 점수입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;3 &amp;le;&amp;nbsp;photo의 길이 &amp;le; 100
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;1 &amp;le;&amp;nbsp;photo[i]의 길이 &amp;le; 100&lt;/li&gt;
&lt;li&gt;3 &amp;le;&amp;nbsp;photo[i]의 원소(문자열)의 길이 &amp;le; 7&lt;/li&gt;
&lt;li&gt;photo[i]의 원소들은 알파벳 소문자로만 이루어져 있습니다.&lt;/li&gt;
&lt;li&gt;photo[i]의 원소들은 중복된 값이 들어가지 않습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;나의 답안&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1681485752204&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(name, yearning, photo) {
    let scores = [];
    for (let i = 0; i &amp;lt; photo.length; i++) {
        let photoScore = 0;
        for (let j = 0; j &amp;lt; photo[i].length; j++) {
            let idx = name.indexOf(photo[i][j]);
            if (idx !== -1) photoScore += yearning[idx];
        }
        scores.push(photoScore);
    }
    return scores;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 사진의 인물을 순회하며, 해당 인물의 이름이 name 배열에 있는 경우 그리움 점수를 가져와 photoScore에 더한다.&lt;/li&gt;
&lt;li&gt;모든 인물에 대한 점수를 계산한 후 scores 배열에 push&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Algorithm</category>
      <author>kohi ☕</author>
      <guid isPermaLink="true">https://trustmitt.tistory.com/84</guid>
      <comments>https://trustmitt.tistory.com/84#entry84comment</comments>
      <pubDate>Sat, 15 Apr 2023 01:31:10 +0900</pubDate>
    </item>
    <item>
      <title>[프로그래머스] 달리기 경주</title>
      <link>https://trustmitt.tistory.com/83</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;문제 설명&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;얀에서는 매년 달리기 경주가 열립니다. 해설진들은 선수들이 자기 바로 앞의 선수를 추월할 때 추월한 선수의 이름을 부릅니다. 예를 들어 1등부터 3등까지 &quot;mumu&quot;, &quot;soe&quot;, &quot;poe&quot; 선수들이 순서대로 달리고 있을 때, 해설진이 &quot;soe&quot;선수를 불렀다면 2등인 &quot;soe&quot; 선수가 1등인 &quot;mumu&quot; 선수를 추월했다는 것입니다. 즉 &quot;soe&quot; 선수가 1등, &quot;mumu&quot; 선수가 2등으로 바뀝니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선수들의 이름이 1등부터 현재 등수 순서대로 담긴 문자열 배열&amp;nbsp;players와 해설진이 부른 이름을 담은 문자열 배열&amp;nbsp;callings가 매개변수로 주어질 때, 경주가 끝났을 때 선수들의 이름을 1등부터 등수 순서대로 배열에 담아 return 하는 solution 함수를 완성해주세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;제한사항&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;5 &amp;le;&amp;nbsp;players의 길이 &amp;le; 50,000
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;players[i]는 i번째 선수의 이름을 의미합니다.&lt;/li&gt;
&lt;li&gt;players의 원소들은 알파벳 소문자로만 이루어져 있습니다.&lt;/li&gt;
&lt;li&gt;players에는 중복된 값이 들어가 있지 않습니다.&lt;/li&gt;
&lt;li&gt;3 &amp;le;&amp;nbsp;players[i]의 길이 &amp;le; 10&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;2 &amp;le;&amp;nbsp;callings의 길이 &amp;le; 1,000,000
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;callings는&amp;nbsp;players의 원소들로만 이루어져 있습니다.&lt;/li&gt;
&lt;li&gt;경주 진행중 1등인 선수의 이름은 불리지 않습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;첫 번째 답안 (오답)&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1681485222468&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(players, callings) {
    let rank = [...players];
    for (let i = 0; i &amp;lt; callings.length; i++) {
        let idx = rank.indexOf(callings[i]);
        if (idx === 0) continue;
        [rank[idx], rank[idx - 1]] = [rank[idx - 1], rank[idx]];
    }
    return rank;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;등수 배열을 선수 배열로 초기화&lt;/li&gt;
&lt;li&gt;기준 선수(idx)가 선두에 있을 때는 건너뛰고 그렇지 않으면 한 단계 앞으로 이동&lt;/li&gt;
&lt;li&gt;테스트 케이스 4개에서 시간 초과&lt;br /&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;indexOf 함수 호출과 선수 이동 작업으로 인한 배열 복사가 많이 발생하여 성능 저하 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;두 번째 답안&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1681485398984&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(players, callings) {
    let rank = new Map();
    for (let i = 0; i &amp;lt; players.length; i++) {
        rank.set(players[i], i);
    }
    for (let i = 0; i &amp;lt; callings.length; i++) {
        let idx = rank.get(callings[i]);
        if (idx === 0) continue;
        let temp = players[idx - 1];
        players[idx - 1] = callings[i];
        players[idx] = temp;
        rank.set(players[idx], idx);
        rank.set(players[idx - 1], idx - 1);
    }
    return players;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;성능 개선
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;선수와 등수의 쌍으로 매핑&lt;/li&gt;
&lt;li&gt;배열 복사 대신에 선수 이동을 직접 수행&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Algorithm</category>
      <author>kohi ☕</author>
      <guid isPermaLink="true">https://trustmitt.tistory.com/83</guid>
      <comments>https://trustmitt.tistory.com/83#entry83comment</comments>
      <pubDate>Sat, 15 Apr 2023 00:18:06 +0900</pubDate>
    </item>
    <item>
      <title>230413 :: [프로그래머스] 공 던지기, 피보나치 수</title>
      <link>https://trustmitt.tistory.com/82</link>
      <description>&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;공&amp;nbsp;던지기&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;문제 설명&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;머쓱이는 친구들과 동그랗게 서서 공 던지기 게임을 하고 있습니다. 공은 1번부터 던지며 오른쪽으로 한 명을 건너뛰고 그다음 사람에게만 던질 수 있습니다. 친구들의 번호가 들어있는 정수 배열&amp;nbsp;numbers와 정수&amp;nbsp;K가 주어질 때,&amp;nbsp;k번째로 공을 던지는 사람의 번호는 무엇인지 return 하도록 solution 함수를 완성해보세요.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;제한사항&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;2 &amp;lt;&amp;nbsp;numbers의 길이 &amp;lt; 100&lt;/li&gt;
&lt;li&gt;0 &amp;lt;&amp;nbsp;k&amp;nbsp;&amp;lt; 1,000&lt;/li&gt;
&lt;li&gt;numbers의 첫 번째와 마지막 번호는 실제로 바로 옆에 있습니다.&lt;/li&gt;
&lt;li&gt;numbers는 1부터 시작하며 번호는 순서대로 올라갑니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;나의 코드&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1681413289571&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(numbers, k) {
  let idx = 0;
  while (--k &amp;gt; 0) {
    idx = (idx + 1) % numbers.length;
    idx = (idx + 1) % numbers.length;
  }
  return numbers[idx];
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;매번 idx를 1씩 증가시키고, 다음 사람에게 공을 던지기 위해 다시 idx를 1 증가&lt;/li&gt;
&lt;li&gt;%&amp;nbsp;연산자를&amp;nbsp;사용하여&amp;nbsp;idx가&amp;nbsp;배열&amp;nbsp;numbers의&amp;nbsp;범위를&amp;nbsp;벗어나지&amp;nbsp;않도록&amp;nbsp;한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: center;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;피보나치 수&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;문제 설명&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;피보나치 수는 F(0) = 0, F(1) = 1일 때, 1 이상의 n에 대하여 F(n) = F(n-1) + F(n-2) 가 적용되는 수 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를들어&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;F(2) = F(0) + F(1) = 0 + 1 = 1&lt;/li&gt;
&lt;li&gt;F(3) = F(1) + F(2) = 1 + 1 = 2&lt;/li&gt;
&lt;li&gt;F(4) = F(2) + F(3) = 1 + 2 = 3&lt;/li&gt;
&lt;li&gt;F(5) = F(3) + F(4) = 2 + 3 = 5&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;와 같이 이어집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2 이상의 n이 입력되었을 때, n번째 피보나치 수를 1234567으로 나눈 나머지를 리턴하는 함수, solution을 완성해 주세요.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;제한 사항&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;n은 2 이상 100,000 이하인 자연수입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;나의 코드&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1681414968323&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(n) {
    let a = 0, b = 1, c;
    for (let i = 2; i &amp;lt;= n; i++) {
        c = (a + b) % 1234567;
        a = b;
        b = c;
    }
    return b;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;초기값 a와 b를 0과 1로 설정하고, n번째 항까지 피보나치 수열 계산&lt;/li&gt;
&lt;li&gt;각 항을 계산할 때마다 1234567로 나눈 나머지를 구해주고 마지막으로 계산된 항을 반환&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Algorithm</category>
      <author>kohi ☕</author>
      <guid isPermaLink="true">https://trustmitt.tistory.com/82</guid>
      <comments>https://trustmitt.tistory.com/82#entry82comment</comments>
      <pubDate>Fri, 14 Apr 2023 04:43:52 +0900</pubDate>
    </item>
    <item>
      <title>Embla Carousel 사용법 정리 (Feat. Next.js)</title>
      <link>https://trustmitt.tistory.com/81</link>
      <description>&lt;pre id=&quot;code_1681408572726&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;yarn add embla-carousel-react&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;embla-carousel-react 패키지 설치&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1681408614450&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { useEffect, useRef, useState } from 'react';
import { useEmblaCarousel } from 'embla-carousel-react';

function Carousel() {
  const [viewportRef, embla] = useEmblaCarousel({ loop: true });
  const [cardInfo, setCardInfo] = useState&amp;lt;FeedListType[]&amp;gt;([]);

  useEffect(() =&amp;gt; {
 	// 카드인포데이터패칭
  }, []);

  return (
    &amp;lt;div className=&quot;overflow-hidden&quot; ref={viewportRef}&amp;gt;
      &amp;lt;div className=&quot;flex&quot;&amp;gt;
        {cardInfo.map((card) =&amp;gt; {
          return (
            &amp;lt;div key={card.docId} style={{ display: 'flex', position: 'relative' }}&amp;gt;
              &amp;lt;FeedCard card={card} /&amp;gt;
            &amp;lt;/div&amp;gt;
          );
        })}
      &amp;lt;/div&amp;gt;
  );
}

export default Carousel;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;Carousel 컴포넌트 코드&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;b&gt;여기까지 했다가 만난 문제 상황&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;i&gt;페이지가 로드될 때 Embla Carousel이 작동하지 않고 창을 리사이징하면 작동하는 경우&lt;/i&gt;&lt;/u&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Embla Carousel의 초기화 시점이 올바르지 않기 때문&lt;/li&gt;
&lt;li&gt;Next.js는 페이지의 초기 렌더링 시 서버와 클라이언트에서 모두 실행되기 때문에, 이를 고려하여 Embla Carousel을 초기화해야 한다.&lt;/li&gt;
&lt;li&gt;useEffect 훅과 조건문을 사용하여 Embla Carousel을 클라이언트 측에서 초기화하도록 만들어야 한다.&lt;/li&gt;
&lt;li&gt;Embla Carousel이 초기화될 때 데이터가 로드되지 않아서도 발생할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;수정한 코드&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1681409115533&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  const [cardInfo, setCardInfo] = useState&amp;lt;FeedListType[]&amp;gt;([]);
  const [viewportRef, embla] = useEmblaCarousel(
    {
      loop: true,
    }
  );

  useEffect(() =&amp;gt; {
    if (embla &amp;amp;&amp;amp; cardInfo.length &amp;gt; 0) {
      embla.reInit();
    }
  }, [embla, cardInfo]);&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;페이지가 로드됐고 카드인포가 있을 때 Embla Carousel을 초기화한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에 오토 기능 추가&lt;/p&gt;
&lt;pre id=&quot;code_1681409300263&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;yarn add embla-carousel-autoplay&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;autoplay 패키지 설치&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1681409343306&quot; class=&quot;typescript&quot; data-ke-language=&quot;typescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  const [cardInfo, setCardInfo] = useState&amp;lt;FeedListType[]&amp;gt;([]);
  const autoplayOptions = {
    delay: 3000,
  };
  const [viewportRef, embla] = useEmblaCarousel(
    {
      loop: true,
    },
    [Autoplay(autoplayOptions)],
  );

  useEffect(() =&amp;gt; {
    if (embla &amp;amp;&amp;amp; cardInfo.length &amp;gt; 0) {
      embla.reInit();
    }
  }, [embla, cardInfo]);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;이틀걸렷다.gif&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;662&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8tOmn/btr92qvkRgN/sPp2GmeIPiDbjkpdX09uwK/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8tOmn/btr92qvkRgN/sPp2GmeIPiDbjkpdX09uwK/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8tOmn/btr92qvkRgN/sPp2GmeIPiDbjkpdX09uwK/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/8tOmn/btr92qvkRgN/sPp2GmeIPiDbjkpdX09uwK/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;450&quot; height=&quot;662&quot; data-filename=&quot;이틀걸렷다.gif&quot; data-origin-width=&quot;450&quot; data-origin-height=&quot;662&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;이거 만드는데 뻥 안 치고 이틀 걸렸다...&lt;/p&gt;
&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignLeft&quot; data-emoticon-type=&quot;friends1&quot; data-emoticon-name=&quot;016&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/016.gif&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/016.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스크롤링 유연하게 술술 잘 넘어간다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐러셀 라이브러리 중에 용량 작은 편이라는데 그래서 그런지 성능 측면에서도 좋은 거 같음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이거 모바일에서도 잘 됨!!!&lt;/p&gt;</description>
      <category>React/NextJS</category>
      <author>kohi ☕</author>
      <guid isPermaLink="true">https://trustmitt.tistory.com/81</guid>
      <comments>https://trustmitt.tistory.com/81#entry81comment</comments>
      <pubDate>Fri, 14 Apr 2023 03:17:52 +0900</pubDate>
    </item>
  </channel>
</rss>