<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>끄적끄적</title>
    <link>https://pyolog.tistory.com/</link>
    <description>머리에넣어놓기</description>
    <language>ko</language>
    <pubDate>Sun, 12 Apr 2026 16:45:33 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>수</managingEditor>
    <image>
      <title>끄적끄적</title>
      <url>https://tistory1.daumcdn.net/tistory/4180163/attach/ed009b9bf8be4384ab263112c5da3390</url>
      <link>https://pyolog.tistory.com</link>
    </image>
    <item>
      <title>NPM 패키지 사이즈 확인하는 법</title>
      <link>https://pyolog.tistory.com/48</link>
      <description>&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;npm에 나와있는 unpackedSize가 단순히 사용하게 되면 쓸 용량이라고 생각이 될 수가 있지만,&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;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;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 번들포비아 (&lt;a href=&quot;https://bundlephobia.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://bundlephobia.com/&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;606&quot; data-origin-height=&quot;421&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NtARt/btsIZj9SdM9/XZ1gAJgbMSfXK7KGRUyQwK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NtARt/btsIZj9SdM9/XZ1gAJgbMSfXK7KGRUyQwK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NtARt/btsIZj9SdM9/XZ1gAJgbMSfXK7KGRUyQwK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNtARt%2FbtsIZj9SdM9%2FXZ1gAJgbMSfXK7KGRUyQwK%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;606&quot; height=&quot;421&quot; data-origin-width=&quot;606&quot; data-origin-height=&quot;421&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;853&quot; data-origin-height=&quot;239&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Q6LMX/btsIX6qa8Bt/2UxJDOIsrntyXaxkrvs5I0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Q6LMX/btsIX6qa8Bt/2UxJDOIsrntyXaxkrvs5I0/img.png&quot; data-alt=&quot;https://bundlephobia.com/package/react-dnd@16.0.1&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Q6LMX/btsIX6qa8Bt/2UxJDOIsrntyXaxkrvs5I0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQ6LMX%2FbtsIX6qa8Bt%2F2UxJDOIsrntyXaxkrvs5I0%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;199&quot; data-origin-width=&quot;853&quot; data-origin-height=&quot;239&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://bundlephobia.com/package/react-dnd@16.0.1&lt;/figcaption&gt;
&lt;/figure&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;여기서 번들사이즈란 최종적으로 사용자에게 제공될 자바스크립트 파일의 크기이며, 웹 애플리케이션 성능과 직접적인 관련이 있습니다.&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;2. 패키지포비아 (&lt;a href=&quot;https://packagephobia.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://packagephobia.com/&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;674&quot; data-origin-height=&quot;706&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6TsDv/btsIZtYLtMV/5jLjGzYHjewkRgX1m9ml0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6TsDv/btsIZtYLtMV/5jLjGzYHjewkRgX1m9ml0K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6TsDv/btsIZtYLtMV/5jLjGzYHjewkRgX1m9ml0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6TsDv%2FbtsIZtYLtMV%2F5jLjGzYHjewkRgX1m9ml0K%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;472&quot; height=&quot;494&quot; data-origin-width=&quot;674&quot; data-origin-height=&quot;706&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위으 publish Size는 압축된 크기로 npm 레지스트리에 크기이며, 네트워크를 통해 다운로드될 때 크기입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;installSize는 패키지를 설치했을 때 로컬 시스템에서 차지하는 전체 크기이며 패키지 자체와 모든 의존성 패키지를 포함합니다.&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;3. npmtrends (&lt;a href=&quot;https://npmtrends.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://npmtrends.com/&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;980&quot; data-origin-height=&quot;822&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HvqD9/btsIX5Y8RqZ/YzqL4vFqouX0PRRZrCn281/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HvqD9/btsIX5Y8RqZ/YzqL4vFqouX0PRRZrCn281/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HvqD9/btsIX5Y8RqZ/YzqL4vFqouX0PRRZrCn281/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHvqD9%2FbtsIX5Y8RqZ%2FYzqL4vFqouX0PRRZrCn281%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;683&quot; height=&quot;573&quot; data-origin-width=&quot;980&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;npmtrends는 몇 가지의 라이브러리를 비교할 때 자주 쓰는 서비스 중 하나입니다. 실제 설치 회수, 크기 등을 비교할 수 있으며 패키지의 인지도를 분석할 때 많이 사용하게 됩니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. npm graph (&lt;a href=&quot;https://npmgraph.js.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://npmgraph.js.org/&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1199&quot; data-origin-height=&quot;380&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdMEAi/btsIYwPuD4u/bcx4d7iC9lDiGPD6uACjBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdMEAi/btsIYwPuD4u/bcx4d7iC9lDiGPD6uACjBK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdMEAi/btsIYwPuD4u/bcx4d7iC9lDiGPD6uACjBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdMEAi%2FbtsIYwPuD4u%2Fbcx4d7iC9lDiGPD6uACjBK%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;718&quot; height=&quot;228&quot; data-origin-width=&quot;1199&quot; data-origin-height=&quot;380&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 npm graph 입니다.&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;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>ETC</category>
      <author>수</author>
      <guid isPermaLink="true">https://pyolog.tistory.com/48</guid>
      <comments>https://pyolog.tistory.com/48#entry48comment</comments>
      <pubDate>Fri, 9 Aug 2024 00:54:53 +0900</pubDate>
    </item>
    <item>
      <title>HTTP Request 메소드의 종류와 기능</title>
      <link>https://pyolog.tistory.com/47</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;354&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxlNif/btsICGZsmjK/0R7U8VkpQfk0cmoZY3ZWt1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxlNif/btsICGZsmjK/0R7U8VkpQfk0cmoZY3ZWt1/img.png&quot; data-alt=&quot;https://res.cloudinary.com/practicaldev/image/fetch/s--9G_NLci7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1uapyaa2ob49xctta1j7.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxlNif/btsICGZsmjK/0R7U8VkpQfk0cmoZY3ZWt1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxlNif%2FbtsICGZsmjK%2F0R7U8VkpQfk0cmoZY3ZWt1%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;880&quot; height=&quot;354&quot; data-origin-width=&quot;880&quot; data-origin-height=&quot;354&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://res.cloudinary.com/practicaldev/image/fetch/s--9G_NLci7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1uapyaa2ob49xctta1j7.png&lt;/figcaption&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP 요청은 클라이언트 &amp;harr; 서버 간의 통신을 위한 &lt;span style=&quot;color: #6164c6;&quot;&gt;프로토콜&lt;/span&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;HTTP 요청의 구성으로는 Request Line, Headers, Body로 구성됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Request Line은 메서드, URL, HTTP버전으로 구성됩니다.&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;메서드의 종류로는&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - GET : 서버에서 데이터를 요청&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - POST : 서버에서 데이터를 제출&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - PUT : 서버에서 데이터를 업데이트&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - DELETE : 서버에서 데이터를 삭제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - PATCH : 서버에 데이터를 부분적으로 업데이트&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;각 메서드의 예시를 보겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1721227417777&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// GET
fetch('https://test.com/api/data', {
  method: 'GET',
  headers: {
    'Content-Type': 'application/json'
  }
})
  .then(response =&amp;gt; response.json())
  .then(data =&amp;gt; console.log(data))
  .catch(error =&amp;gt; console.error('Error:', error));



//POST
fetch('https://test.com/api/data', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
   ...somedata
  })
})
  .then(response =&amp;gt; response.json())
  .then(data =&amp;gt; console.log(data))
  .catch(error =&amp;gt; console.error('Error:', error));



//PUT
fetch('https://test.com/api/data/1', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    ...somedata
  })
})
  .then(response =&amp;gt; response.json())
  .then(data =&amp;gt; console.log(data))
  .catch(error =&amp;gt; console.error('Error:', error));



//DELETE
fetch('https://test.com/api/data/1', { 
  method: 'DELETE',
  headers: {
    'Content-Type': 'application/json'
  }
})
  .then(response =&amp;gt; response.json())
  .then(data =&amp;gt; console.log(data))
  .catch(error =&amp;gt; console.error('Error:', error));



//PATCH
fetch('https://test.com/api/data/1', { 
  method: 'PATCH',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
   ...somedata
  })
})
  .then(response =&amp;gt; response.json())
  .then(data =&amp;gt; console.log(data))
  .catch(error =&amp;gt; console.error('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;&amp;nbsp;&lt;/p&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;'GET', 'PUT', 'DELETE' 은 여러번 요청을 수행하더라도 멱등한 속성을 갖습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'POST'는 새로운 리소스를 생성하므로 여러번 반복하면 계속 서버 상태가 변경될 수 있으니 멱등하지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'PATCH'는 요청에 따라 다릅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 patch의 목적이 기존값을 1씩 증가시는 요청 이라하면 멱등성을 갖지 않고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;username을 newname으로 바꾸는 것 같은 요청은 멱등성을 갖습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTTP 응답 상태 코드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 1xx: 요청이 수신되어 처리중&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 2xx: 요청이 성공적으로 처리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 3xx: 요청완료를 위해 추가작업이 필요함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 4xx: 클라이언트 오휴&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 5xx: 서버가 요청 처리 중 오류발생&lt;/p&gt;</description>
      <category>CS</category>
      <author>수</author>
      <guid isPermaLink="true">https://pyolog.tistory.com/47</guid>
      <comments>https://pyolog.tistory.com/47#entry47comment</comments>
      <pubDate>Wed, 17 Jul 2024 23:57:08 +0900</pubDate>
    </item>
    <item>
      <title>게시판 만들기 (4) 가상 테이블 (react-window vs react-virtualized)</title>
      <link>https://pyolog.tistory.com/46</link>
      <description>&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;1360&quot; data-origin-height=&quot;530&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dQSZfj/btsHeaArJHV/OlnkOeU07qnjsWvX1m6z1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dQSZfj/btsHeaArJHV/OlnkOeU07qnjsWvX1m6z1k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dQSZfj/btsHeaArJHV/OlnkOeU07qnjsWvX1m6z1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdQSZfj%2FbtsHeaArJHV%2FOlnkOeU07qnjsWvX1m6z1k%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;1360&quot; height=&quot;530&quot; data-origin-width=&quot;1360&quot; data-origin-height=&quot;530&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;게시판을 만들 때 테이블 리스트, 알람 리스트 를 만들 때 가상 테이블을 사용하여 효율을 향상시킬 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&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;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;가장 대표적인 가상화 테이블의 라이브러리로는 react-window와 react-virtualized가 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;한동안 react-virtualized가 react-window 보다 사용자수가 많았는데 갑자기 사용자가 역전이 되어있네요.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;react-virtualized가 용량이 약2~3배 가량 높고, 그만큼 좀 더 많은 기능을 제공하는 것 같지만,&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Sans Light';&quot;&gt;저는 단순히 테이블 요소만 리스팅하기만 하면 되므로 비교적 용량이 적은 react-window를 사용하겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;pre id=&quot;code_1715088866504&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Yarn
yarn add react-window

# NPM
npm install --save react-window&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1715106046578&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const Page = () =&amp;gt; {
  ... code

  const Row = ({ index, style, data }) =&amp;gt; (
    &amp;lt;div style={{ ...style, margin: '5px 0' }}&amp;gt;  // 마진을 추가로 설정
      &amp;lt;AlarmRow key={index} alarm={data} /&amp;gt;
    &amp;lt;/div&amp;gt;
  );

  if (alarmListRC &amp;amp;&amp;amp; alarmListRC.length &amp;gt; 0) {
    return (
    	//실제 아이템의 사이즈는 45이지만 위에서 마진을 5씩 위아래로 더했기에 10 추가
      &amp;lt;List height={150} itemCount={alarmListRC.length} itemSize={55} width='85%' itemData='Additional Data'&amp;gt;
        {({ index, style }) =&amp;gt; Row({ index, style, data: alarmListRC[index] })}
      &amp;lt;/List&amp;gt;
    );
  } else return &amp;lt;div&amp;gt; 활동 알람이 없습니다. &amp;lt;/div&amp;gt;;
};

export default Page;&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;815&quot; data-origin-height=&quot;170&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mv1xc/btsHde4fK8a/DNJ9ZGZOuokysAroypHka1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mv1xc/btsHde4fK8a/DNJ9ZGZOuokysAroypHka1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mv1xc/btsHde4fK8a/DNJ9ZGZOuokysAroypHka1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fmv1xc%2FbtsHde4fK8a%2FDNJ9ZGZOuokysAroypHka1%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;815&quot; height=&quot;170&quot; data-origin-width=&quot;815&quot; data-origin-height=&quot;170&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;다음과 같이 아이템이 테이블로 잘나왔습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아이템의 개수가 현재 3개밖에안되지만, 많아졌을 때의 결과물들을 나중에 한번 더 보겠습니다.&lt;/p&gt;</description>
      <category>React</category>
      <author>수</author>
      <guid isPermaLink="true">https://pyolog.tistory.com/46</guid>
      <comments>https://pyolog.tistory.com/46#entry46comment</comments>
      <pubDate>Wed, 8 May 2024 03:21:48 +0900</pubDate>
    </item>
    <item>
      <title>게시판 만들기 (3) 세션관리 훅 만들기(useSession)</title>
      <link>https://pyolog.tistory.com/45</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;304&quot; data-origin-height=&quot;166&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pEA3P/btsGBYtQP2n/Btu5n5IrxZdnDdJKVfSDDk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pEA3P/btsGBYtQP2n/Btu5n5IrxZdnDdJKVfSDDk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pEA3P/btsGBYtQP2n/Btu5n5IrxZdnDdJKVfSDDk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpEA3P%2FbtsGBYtQP2n%2FBtu5n5IrxZdnDdJKVfSDDk%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;304&quot; height=&quot;166&quot; data-origin-width=&quot;304&quot; data-origin-height=&quot;166&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React에서 hook이란?&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;&lt;b&gt; Hook&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;은 React 16.8에 새로 추가된 기능입니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Hook&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;은 class를 작성하지 않고도 state와 다른 React의 기능들을 사용할 수 있게 해줍니다.&lt;/span&gt; &lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;React 공식문서에서의 hook의 &lt;a href=&quot;https://ko.legacy.reactjs.org/docs/hooks-intro.html#motivation&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;motivation&lt;/a&gt;의 결론은 다음과 같습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;Hook을 통해 서로 비슷한 것을 하는 작은 함수의 묶음으로 컴포넌트를 나누는 방법을 사용할 수 있습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저의 게시판에서 사용할 훅은 세션관리에 이용할 custom hook입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매 페이지마다 login session을 관리하여, token의 생명을 검사하고, refresh와 logout 등의 관리를 하는 hook입니다.&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;현재 로그인을 진행하면 cookie를 통해 accessToken과 refreshToken등을 관리하며, 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;custom hook인 useSession 안에서는 쿠키를 불러와 accessToken의 값들을 살펴보고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;토큰의 만기시간을 비교해, refresh가 필요하면 진행, 토큰이 없거나 유효하지 않으면, 다시 로그인을 하는 페이지로 이동을 시킬겁니다.&lt;/p&gt;
&lt;pre id=&quot;code_1713123022185&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// useSession.ts

import { useEffect } from 'react';
import { useRouter } from 'next/router';
import { refreshToken } from 'src/apis/userApi';
import Cookies from 'js-cookie';
import { jwtDecode } from 'jwt-decode';

interface atInterface {
  mobile: string;
  exp: string;
  iat: string;
  nickName: string;
  email: string;
}

const useSession = () =&amp;gt; {
  const router = useRouter();
  const at = Cookies.get('WMS_accessToken');
  const rt = Cookies.get('WMS_refreshToken');

  useEffect(() =&amp;gt; {
    if (!at || !rt) {
      router.push('/login');
    }
    if (at) {
      const atDecoded: atInterface = jwtDecode(at);
      const cur = new Date().getTime();
      const sessionExpTime = parseInt(atDecoded.exp + '000');

      const refreshSession = async () =&amp;gt; {
        const res = await refreshToken(at, rt);
        if (res &amp;amp;&amp;amp; res.success) {
          Cookies.set('WMS_accessToken', res.data.accessToken);
          Cookies.set('WMS_refreshToken', res.data.refreshToken);
        } else {
          Cookies.remove('WMS_accessToken');
          Cookies.remove('WMS_refreshToken');
          router.push('/login');
        }
      };

      if (cur &amp;gt; sessionExpTime) {
        console.log('refreshToken!');
        refreshSession();
      } else {
        console.log('Token is alive.');
      }
    }
  }, [at, rt, router]);
};

export default useSession;&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;이를 session 관리가 필요한 page에 적용하여 실행시켜주기만 하면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1713123204733&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//pages/somepage/index.tsx

...

const SomePage = () =&amp;gt; {
	...
    
    useSession();
    
    ...
    
    return (...)
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>React</category>
      <category>Hook</category>
      <category>NextJS</category>
      <category>React</category>
      <author>수</author>
      <guid isPermaLink="true">https://pyolog.tistory.com/45</guid>
      <comments>https://pyolog.tistory.com/45#entry45comment</comments>
      <pubDate>Mon, 15 Apr 2024 04:32:32 +0900</pubDate>
    </item>
    <item>
      <title>게시판 만들기 (2) (Recoil 설정하기)</title>
      <link>https://pyolog.tistory.com/44</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;500&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sBgW2/btsGbUEfvlW/knqyKl0kgKH5VYpuQjlpKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sBgW2/btsGbUEfvlW/knqyKl0kgKH5VYpuQjlpKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sBgW2/btsGbUEfvlW/knqyKl0kgKH5VYpuQjlpKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsBgW2%2FbtsGbUEfvlW%2FknqyKl0kgKH5VYpuQjlpKK%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;800&quot; height=&quot;500&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;500&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 프로젝트에서 사용하기로 한 상태관리 라이브러리는 recoil 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그동안은 redux만을 사용해보았는데, 이번엔 recoil을 사용해보고 싶어서 사용해보기로 했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자주 하지 않은 업데이트, 메모리 누수.. 등 다양한 단점들이 존재하여 현재 Redux에 비해 매우 사용률이&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저조하긴 하지만 그래도 react서 만든 것이기 때문에 한번쯤은 사용해볼 가치가 있다고 생각합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(devtools가 없어 개발자도구에서 확인이 불가능하여 매우 불편한점은.... 매우 불편...)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 초기 세팅 및 사용법이 엄-청 간편하기 때문에 빠르게 작성하여 진행할 수 있다는 장점이 있습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;pre id=&quot;code_1711617120415&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// src/recoil/user.ts

import { atom } from 'recoil'

export const sessionState = atom({
  key: 'session',
  default: {
    state: false,
    email: '',
    nickname: '',
    mobile: '',
    sessionToken: '',
    refreshToken: ''
  }
})

export const userState = atom({
  key: 'user',
  default: {
    email: '',
    nickname: '',
    profileImage: ''
  }
})

export const registerInfoState = atom({
  key: 'registerInfo',
  default: {
    email: '',
    password: '',
    nickname: '',
    mobile: ''
  }
})&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;우선 회원가입 및 유저정보를 저장하는 간단한 상태저장 코드를 만들었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게만 atom으로 state를 설정하면 이제 사용준비가 거의 끝났다는게 사실입니까!?&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;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 전에! Devtools가 없는 recoil의 상태확인을 수시로 확인해보고 싶은 답답함을 해결하기 위해,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;custom hook을 하나만들어 필요한 페이지에 붙여다 써서 확인해보겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1711617368403&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// src/hooks/useRecoilLogger.ts


import { useEffect } from 'react'
import { useRecoilValue } from 'recoil'
import { sessionState, userState, registerInfoState } from 'src/recoil/user'

export function useRecoilLogger() {
  const session = useRecoilValue(sessionState)
  const user = useRecoilValue(userState)
  const registerInfo = useRecoilValue(registerInfoState)

  useEffect(() =&amp;gt; {
    console.log('Recoil State:', {
      session,
      user,
      registerInfo
    })
  }, [session, user, registerInfo])
}&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;자 그럼 이제 이걸 로그인 페이지에 작성하여 확인해보겠습니다.&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;useRecoilLogger() 를 사용하시면 됩니다.&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;670&quot; data-origin-height=&quot;404&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l57bO/btsF95GZjBC/tzdjGHm5lMDWMkvJNdaiy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l57bO/btsF95GZjBC/tzdjGHm5lMDWMkvJNdaiy0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l57bO/btsF95GZjBC/tzdjGHm5lMDWMkvJNdaiy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl57bO%2FbtsF95GZjBC%2FtzdjGHm5lMDWMkvJNdaiy0%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;670&quot; height=&quot;404&quot; data-origin-width=&quot;670&quot; data-origin-height=&quot;404&quot;/&gt;&lt;/span&gt;&lt;/figure&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 로그인화면에서의 아이디 비밀번호를 어떻게 recoil state에 저장하는 지만 알아보겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1711621823258&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { sessionState } from 'src/recoil/user';
import { useRecoilState } from 'recoil';

... 

const [session, setSessionState] = useRecoilState(sessionState);

...


return (
	&amp;lt;input
    	value={session.email}
        onChange={(event: ChangeEvent&amp;lt;HTMLTextAreaElement | HTMLInputElement&amp;gt;) =&amp;gt;
            setSessionState(prev =&amp;gt; ({
              ...prev,
              email: event.target.value
            }))
          }
    /&amp;gt;
)

...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;643&quot; data-origin-height=&quot;162&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l1uR2/btsF94hbytQ/D11vSctxNEstmkNyJDjlF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l1uR2/btsF94hbytQ/D11vSctxNEstmkNyJDjlF0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l1uR2/btsF94hbytQ/D11vSctxNEstmkNyJDjlF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl1uR2%2FbtsF94hbytQ%2FD11vSctxNEstmkNyJDjlF0%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;643&quot; height=&quot;162&quot; data-origin-width=&quot;643&quot; data-origin-height=&quot;162&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;&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;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에는 이제 API 연결을하여 필요한 정보들을 저장하고 사용하는 블로그로 뵙겠습니다.&lt;/p&gt;</description>
      <category>React</category>
      <category>NextJS</category>
      <category>Recoil</category>
      <author>수</author>
      <guid isPermaLink="true">https://pyolog.tistory.com/44</guid>
      <comments>https://pyolog.tistory.com/44#entry44comment</comments>
      <pubDate>Thu, 28 Mar 2024 20:07:37 +0900</pubDate>
    </item>
    <item>
      <title>게시판 만들기 (1) (NextJS MaterialUI Template)</title>
      <link>https://pyolog.tistory.com/43</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;문득이자 예전부터 해보고 싶었던, 관련 분야의 게시판을 만들어 보기로 했습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 게시판이라고 하면 단순히 CRUD만 있는 간단한 프로젝트 아니냐 라고 할 수 있지만,&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;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;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;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;디자인구성(MUI) &amp;rarr; 상태관리 라이브러리 세팅(Recoil) &amp;rarr; API 연동 ( 로그인 부터 순차적으로 )&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;각 진행단계에서 제가 사용한 stack이나 그 stack을 사용한 방법등을 공유 및 조언을 받고 싶은 마음에 작성된 블로그 입니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style3&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;● MUI 템플릿&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에 Material UI에서 다양한 component들을 가져와 사용해 보았지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Template 자체를 제공하는건 기억 저 어디 콩알만큼 있었습니다.&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;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://mui.com/material-ui/getting-started/templates/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://mui.com/material-ui/getting-started/templates/&lt;/a&gt;&amp;nbsp; &amp;nbsp;&lt;b&gt;&amp;larr; MUI template&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 중에서 저는 typescript를 사용할 거기 때문에 typescript로 지원된 template을 하나 가져왔습니다.&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;그리고 사용안할 page routing 들은 다 삭제를 하고, 언젠간 쓸일이 있을 거 같은 것들은 냄겨두고 정리를 진행했습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;614&quot; data-origin-height=&quot;539&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKaN9H/btsGawxmxVL/mwRPiB2gABbhKITocKcBMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKaN9H/btsGawxmxVL/mwRPiB2gABbhKITocKcBMk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKaN9H/btsGawxmxVL/mwRPiB2gABbhKITocKcBMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKaN9H%2FbtsGawxmxVL%2FmwRPiB2gABbhKITocKcBMk%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;614&quot; height=&quot;539&quot; data-origin-width=&quot;614&quot; data-origin-height=&quot;539&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1900&quot; data-origin-height=&quot;906&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnaOjn/btsF8otLTFr/Wa5vIcGW4lOemZizUiJGmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnaOjn/btsF8otLTFr/Wa5vIcGW4lOemZizUiJGmk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnaOjn/btsF8otLTFr/Wa5vIcGW4lOemZizUiJGmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnaOjn%2FbtsF8otLTFr%2FWa5vIcGW4lOemZizUiJGmk%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;1900&quot; height=&quot;906&quot; data-origin-width=&quot;1900&quot; data-origin-height=&quot;906&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;우선 이런식으로 틀은 잡았으니,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2탄에서 상태관리 라이브러리 Recoil을 연결시켜 보겠습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>React</category>
      <category>materialui</category>
      <category>NextJS</category>
      <author>수</author>
      <guid isPermaLink="true">https://pyolog.tistory.com/43</guid>
      <comments>https://pyolog.tistory.com/43#entry43comment</comments>
      <pubDate>Thu, 28 Mar 2024 15:28:48 +0900</pubDate>
    </item>
    <item>
      <title>[Programmers] 완주하지 못한 선수 (JS)</title>
      <link>https://pyolog.tistory.com/42</link>
      <description>&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;participant를 한번 돌면서 completion을 보고 포함이 안되어있는 사람을 찾으면 되는 것이다.&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;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#잘못된 풀이&lt;/p&gt;
&lt;pre id=&quot;code_1705943543971&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//잘못된 풀이 방식 (시간 초과)

function solution(participant, completion) {
    return participant.filter(d =&amp;gt; {
        if(completion.includes(d)){
            let i = completion.indexOf(d);
            completion.splice(i,1);
            return false;
        } else return true;
    })[0]
}&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;그러므로 최대한 배열을 사용안하는 방법을 생각해 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 먼저 p라는 객체에 participant를 모두 key값으로 넣고 값을 0으로 초기화 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. participant를 다시 읽으며 p의 해당 key 값을 증가시킨다. (동명이인이 있으므로 1이상이 될수도있다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 마지막으로 completion을 읽으며 또다시 p에서 해당 key값을 감소 시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 문제에서 탈락자는 1명이라고 특정했으므로 0인 사람의 이름을 찾는다.&lt;/p&gt;
&lt;pre id=&quot;code_1705944311163&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(participant, completion) {
    let p = {};
    participant.forEach(d =&amp;gt; p[d] = 0);
    participant.forEach(d =&amp;gt; p[d]++);
    completion.forEach(d =&amp;gt; p[d]--);
    return Object.keys(p).filter(d =&amp;gt; p[d] &amp;gt;= 1)[0]
}&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;또 한번 배열의 무서움에 대해 느낀다..&lt;/p&gt;</description>
      <category>Programmers</category>
      <category>programmers</category>
      <author>수</author>
      <guid isPermaLink="true">https://pyolog.tistory.com/42</guid>
      <comments>https://pyolog.tistory.com/42#entry42comment</comments>
      <pubDate>Tue, 23 Jan 2024 02:25:13 +0900</pubDate>
    </item>
    <item>
      <title>[Programmers] 신고 결과 받기 (JS)</title>
      <link>https://pyolog.tistory.com/41</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&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;a가 [b,c,...]를 신고했을때 신고당한 사람들 중 정지를 당한 사람의 수의 배열이다.&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;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;1&lt;span style=&quot;color: #006dd7;&quot;&gt;.&lt;/span&gt; p라는 객체에 누가, 누구를 신고했는지 저장한다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;2. 신고내역을 하나씩 보며 p를 작성한다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;&lt;b&gt;3. p를 보고 각 id당 신고당한 회수를 살펴보고 조건보다 많이 되어있는 정지당할 사람을 추출한다.&lt;/b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;4. 정지당한 id를 신고한 id를 추출한다&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1705918427457&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;p = {
 'id1': {
        'id2': 0,
        'id3': 0,
        'id4': 0
  },
  'id2': {...}
  ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1705913595560&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;//solution

function solution(id_list, report, k) {
    var answer = [];
    let p = {}; 
    let reported = {}; // 각 id들이 신고당한 횟수를 기록하는 변수
    let reportedNames = []; // 정지당한 id를 기록하는 변수
    
    // p는 현재 유저들이 신고한 기록을 담은 object
    id_list.forEach(d =&amp;gt; {
        p[d] = {};
        reported[d] = 0;
        answer[d] = 0;
        id_list.forEach(d2 =&amp;gt; d!==d2 ? p[d][d2] = 0 : null)
    });
    
    // --------------------&amp;gt;
    // p = {
    //     'muzi': { frodo: 0, apeach: 0, frodo: 0, ...},
    //     'frodo': {...}
    // } 
    // 이런식으로 key 값의 사람이 각 사람을 신고한 횟수이다.
    
    
    // 신고내역을 하나씩 보며, 신고한 내역을 기록한다.
    report.forEach(d =&amp;gt; {
        let a = d.split(' ')[0];
        let b = d.split(' ')[1];
        p[a][b] = 1;
    })
    
    // 기록된 신고내역으로 각 id들이 신고당한 횟수를 기록한다.
    id_list.forEach(d =&amp;gt; {
        Object.keys(p[d]).forEach(d2 =&amp;gt; {
            if(p[d][d2]){
                reported[d2]++;
                // 만약 k보다 신고회수가 많아지고 reportedNames에 현재 기록되어있지 않다면 추가한다.
                if(reported[d2] &amp;gt;= k &amp;amp;&amp;amp; !reportedNames.includes(d2)) reportedNames.push(d2);
            }
        })
    })
    
    // 우리가 원하는 것은 누가 어떤이들을 신고했을때 그 사람이 정지당한 개수이다.
    for(let key in p){
        for(let key2 in p[key]){
            // key 가 key2를 신고한 회수가 1이상이고, 정지당한 목록에 포함되어있다면
            if(p[key][key2] &amp;gt;= 1 &amp;amp;&amp;amp; reportedNames.includes(key2)){
                answer[key]++;
            }
        }
    }
    answer = Object.values(answer)
    
    return answer;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Programmers</category>
      <category>programmers</category>
      <author>수</author>
      <guid isPermaLink="true">https://pyolog.tistory.com/41</guid>
      <comments>https://pyolog.tistory.com/41#entry41comment</comments>
      <pubDate>Mon, 22 Jan 2024 19:34:49 +0900</pubDate>
    </item>
    <item>
      <title>[Programmers] 햄버거 만들기 (JS)</title>
      <link>https://pyolog.tistory.com/40</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에 문제를 봤을 땐 stack이 바로 떠오르지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ingredient의 길이가 1백만 이기에, 만일 for문을 2번 돈다 해도, 시간초과의 늪에 걸릴줄 몰랐다..&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;pre id=&quot;code_1705595801971&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 잘못된 접근

let str = ingredient.join('');

while(str.indexOf('1231') !== -1){
	if(str.indexOf('1231') !== -1){
    	answer++;
        str.replace('1231', '')
    }
 }
 ...&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;이렇게 되면 만약 최악의 경우를 생각해본다면 n^2의 시간복잡도가 나올 수 있었을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 이걸 알았음에도 불구하고 1백만이라는 너무 약하게 본것도 있다.&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;다시 돌아와 배열을 이용한 stack 의 풀이 방식을 작성했다.&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;방식은 배열에 한개씩 집어넣으면서, 배열속 마지막 4개가 1231을 만들 수 있는지 확인하여, 만들 수 있다면 배열에서 다시 제거하는 방법이다. 이는 n의 시간복잡도를 갖는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1705596097796&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 올바른 풀이
function solution(ingredient) {
    var answer = 0;
    let stack = [];
    
    for(let i=0; i&amp;lt;ingredient.length; i++){
        stack.push(ingredient[i]);
        if(stack.slice(-4).join('') === '1231'){
            answer++;
            stack.splice(-4);
        }
    }

    return answer;
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Programmers</category>
      <category>programmers</category>
      <author>수</author>
      <guid isPermaLink="true">https://pyolog.tistory.com/40</guid>
      <comments>https://pyolog.tistory.com/40#entry40comment</comments>
      <pubDate>Fri, 19 Jan 2024 01:41:36 +0900</pubDate>
    </item>
    <item>
      <title>[Programmers] 둘만의 암호 (JS)</title>
      <link>https://pyolog.tistory.com/39</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 문자열 S 의 각 문자를 Index 만큼 알파벳상의 순서 뒤로 미는데, Skip에 포함된 스펠링은 건너 뛰라는 것이다.&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;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 배열로 먼저 a부터 z까지 저장을 해놓은뒤&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. skip에 포함되어있는 스펠링을 배열에서 지우고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 문자열 s를 index만큼 배열에 기반하여 밀어주기만 하면된다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 여기서 문자열이 배열을 넘어가면 다시 앞에서부터 순서를 해야하기 때문에 길이만큼 모듈러연산? 즉 나눠주기만 하면된다.&lt;/p&gt;
&lt;pre id=&quot;code_1705585523324&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function solution(s, skip, index) {
    var answer = '';
    let alpha = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'];
    
    alpha = alpha.filter(d =&amp;gt; !skip.includes(d));
    
    answer = Array.from(s).map(s =&amp;gt; alpha[(alpha.indexOf(s)+index)%alpha.length]).join('')
    
    return answer;
}&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;매일 for문을 작성하며 길게하여 가독성 좋고 이해하기 편하게 작성하려고 노력했지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 느낌 오는대로 한줄로 작성을 해보았다.&lt;/p&gt;</description>
      <category>Programmers</category>
      <category>programmers</category>
      <author>수</author>
      <guid isPermaLink="true">https://pyolog.tistory.com/39</guid>
      <comments>https://pyolog.tistory.com/39#entry39comment</comments>
      <pubDate>Thu, 18 Jan 2024 22:45:59 +0900</pubDate>
    </item>
  </channel>
</rss>