<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>choi95의 TIL </title>
    <link>https://choi95.tistory.com/</link>
    <description>기억보다 기록을</description>
    <language>ko</language>
    <pubDate>Mon, 13 Apr 2026 02:51:14 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>choi95</managingEditor>
    <image>
      <title>choi95의 TIL </title>
      <url>https://tistory1.daumcdn.net/tistory/4594054/attach/50ac12c7f08d4b2385573ae06c0fe0ba</url>
      <link>https://choi95.tistory.com</link>
    </image>
    <item>
      <title>심심해서 만들어 본 Toggle Switch </title>
      <link>https://choi95.tistory.com/207</link>
      <description>&lt;h3 style=&quot;box-sizing: border-box; border-width: 0px 0px 2px 10px; border-bottom-style: solid; border-bottom-color: #6bacce; padding: 3px 5px; border-left-style: solid; border-left-color: #6bacce; margin: 5px 0px; letter-spacing: 1px; line-height: 1.5; font-weight: 800;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;시작한 계기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;머리를 식힐 겸(?) 원티드에서 채용 공고를 보다가, 2기 때 수료 했던 원티드 프리온 보딩 공고가 떠 있어 들어가 봤다.&lt;/span&gt;&lt;span&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&gt;해당 문제는 Toggle Switch 를 구현하는 것인데, 이전의 Toggle Switch와는 다르게 Slider 영역에 두 개의 글자가 각 사이드에 위치하였고 Toggle이 해당 글자 부분으로 이동하게 되면 동적으로 스타일링이 변하는 흥미로운 문제였다.&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&gt;한산한 밤, 오랜만에 바닐라 JS   를 사용하고 싶어 해당 문제를 다음과 같이 간단히 코딩해보았다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-width: 0px 0px 2px 10px; border-bottom-style: solid; border-bottom-color: #6bacce; padding: 3px 5px; border-left-style: solid; border-left-color: #6bacce; margin: 5px 0px; letter-spacing: 1px; line-height: 1.5; font-weight: 800;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;Code&lt;/span&gt;&lt;/h3&gt;
&lt;p class=&quot;codepen&quot; style=&quot;height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;&quot; data-height=&quot;300&quot; data-theme-id=&quot;dark&quot; data-default-tab=&quot;html,result&quot; data-slug-hash=&quot;qBpQEBJ&quot; data-user=&quot;leulsiqf&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;See the Pen &lt;a href=&quot;https://codepen.io/leulsiqf/pen/qBpQEBJ&quot;&gt; Untitled&lt;/a&gt; by 최병현 (&lt;a href=&quot;https://codepen.io/leulsiqf&quot;&gt;@leulsiqf&lt;/a&gt;) on &lt;a href=&quot;https://codepen.io&quot;&gt;CodePen&lt;/a&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;script src=&quot;https://cpwebassets.codepen.io/assets/embed/ei.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>JS</category>
      <author>choi95</author>
      <guid isPermaLink="true">https://choi95.tistory.com/207</guid>
      <comments>https://choi95.tistory.com/207#entry207comment</comments>
      <pubDate>Sat, 16 Apr 2022 00:06:47 +0900</pubDate>
    </item>
    <item>
      <title>타입스크립트 상에서 svg 파일을 컴포넌트로서 import할 때 문제</title>
      <link>https://choi95.tistory.com/206</link>
      <description>&lt;h3 style=&quot;box-sizing: border-box; border-width: 0px 0px 2px 10px; border-bottom-style: solid; border-bottom-color: #6bacce; padding: 3px 5px; border-left-style: solid; border-left-color: #6bacce; margin: 5px 0px; letter-spacing: 1px; line-height: 1.5; font-weight: 800;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;&quot;&gt;문제&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;&quot;&gt;타입스크립트를 사용한 프로젝트 내에서 svg 파일을 컴포넌트로서 사용하기 위해 다음과 같이 코드를 작성하였다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1649678495135&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { ReactComponent as ProfileIcon } from '/images/icon-profile.svg';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 해당 코드에서 다음과 같은 에러가 발생하였다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;&quot;*.svg&quot;' 모듈에 내보낸 멤버 'ReactComponent'이(가) 없습니다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-width: 0px 0px 2px 10px; border-bottom-style: solid; border-bottom-color: #6bacce; padding: 3px 5px; border-left-style: solid; border-left-color: #6bacce; margin: 5px 0px; letter-spacing: 1px; line-height: 1.5; font-weight: 800;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;&quot;&gt;문제 해결&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;&quot;&gt;스택오버플로우에서 나와 같은 문제를 발견하였고 해당 문제를 해결할 수 있었다.&lt;/span&gt;&lt;span style=&quot;&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/54121536/typescript-module-svg-has-no-exported-member-reactcomponent&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://stackoverflow.com/questions/54121536/typescript-module-svg-has-no-exported-member-reactcomponent&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1649678649335&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;TypeScript - Module '&amp;quot;*.svg&amp;quot;' has no exported member 'ReactComponent&quot; data-og-description=&quot;I'm trying to import an .svg file as a React component with TypeScript. According to the React docs, this is done like so: import { ReactComponent as Icon } from './Icon.svg'; Following the&quot; data-og-host=&quot;stackoverflow.com&quot; data-og-source-url=&quot;https://stackoverflow.com/questions/54121536/typescript-module-svg-has-no-exported-member-reactcomponent&quot; data-og-url=&quot;https://stackoverflow.com/questions/54121536/typescript-module-svg-has-no-exported-member-reactcomponent&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/iofbd/hyN0g1n6RK/IDlsJdOPGhQ7QtjzUikqW1/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/54121536/typescript-module-svg-has-no-exported-member-reactcomponent&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://stackoverflow.com/questions/54121536/typescript-module-svg-has-no-exported-member-reactcomponent&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/iofbd/hyN0g1n6RK/IDlsJdOPGhQ7QtjzUikqW1/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316');&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;TypeScript - Module '&quot;*.svg&quot;' has no exported member 'ReactComponent&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;I'm trying to import an .svg file as a React component with TypeScript. According to the React docs, this is done like so: import { ReactComponent as Icon } from './Icon.svg'; Following the&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;stackoverflow.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;custom.d.ts 파일을 생성해주고 해당 파일 내에서 모든 svg 파일에 대해 다음과 같이 속성을 설정해 주었다.&lt;/p&gt;
&lt;pre id=&quot;code_1649678746597&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;declare module '*.svg' {
    import React = require('react');
    export const ReactComponent: React.FC&amp;lt;React.SVGProps&amp;lt;SVGSVGElement&amp;gt;&amp;gt;;
    const src: string;
    export default src;
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 ts 컴파일러가 ts|tsx 확장을 처리하도록 tsconfig.json에 custom.d.ts를 호출하도록 해주어 문제를 해결하였다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1649679148022&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;compilerOptions&quot;: {
  
  ( ... )
   
  &quot;include&quot;: [
    &quot;next-env.d.ts&quot;,
    &quot;**/*.ts&quot;,
    &quot;**/*.tsx&quot;,
    &quot;next.config.js&quot;,
    &quot;custom.d.ts&quot;,
    &quot;styles&quot;,
    &quot;pages&quot;,
    &quot;public&quot;
  ],
  
 ( ... )
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>TS</category>
      <author>choi95</author>
      <guid isPermaLink="true">https://choi95.tistory.com/206</guid>
      <comments>https://choi95.tistory.com/206#entry206comment</comments>
      <pubDate>Mon, 11 Apr 2022 21:13:03 +0900</pubDate>
    </item>
    <item>
      <title>로컬 PC에서 이미지 가져오고 저장하기_타입스크립트 내에서 비동기 작업을 통한 FileReader 인스턴스 사용기</title>
      <link>https://choi95.tistory.com/205</link>
      <description>&lt;h3 style=&quot;box-sizing: border-box; border-width: 0px 0px 2px 10px; border-bottom-style: solid; border-bottom-color: #6bacce; padding: 3px 5px; border-left-style: solid; border-left-color: #6bacce; margin: 5px 0px; letter-spacing: 1px; line-height: 1.5; font-weight: 800;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;Issue&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;프로젝트 중, 로컬 PC에서 가져 온 이미지를 preview로 보여주며 저장하는 기능 구현이 필요했다.&lt;/span&gt;&lt;span&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-width: 0px 0px 2px 10px; border-bottom-style: solid; border-bottom-color: #6bacce; padding: 3px 5px; border-left-style: solid; border-left-color: #6bacce; margin: 5px 0px; letter-spacing: 1px; line-height: 1.5; font-weight: 800;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;&quot;&gt;Code&lt;/span&gt;&lt;/h3&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;span style=&quot;&quot;&gt;1) Input 스타일 커스터마이징&lt;/span&gt;&lt;/b&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;우선, Input 태그 내에 'type=file' 속성을 지정해주면 로컬 PC에서 이미지 소스를 가져올 수 있지만 기본 지정 스타일이 원하던 방향이 아니기에 다음과 같이 JSX를 작성하였다.&lt;/p&gt;
&lt;pre id=&quot;code_1649674788874&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;         &amp;lt;&amp;gt;
	  &amp;lt;S.FileBox&amp;gt;
            &amp;lt;S.FileLabel htmlFor=&quot;file&quot;&amp;gt;내 PC에서 불러오기&amp;lt;/S.FileLabel&amp;gt;
            &amp;lt;S.FetchLocalPC type=&quot;file&quot; id=&quot;file&quot; onChange={storeImage}/&amp;gt;
          &amp;lt;/S.FileBox&amp;gt;
          &amp;lt;&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 로컬 PC에서 이미지를 가져오는 기능은 FetchLocalPC 컴포넌트이지만 스타일을 커스터마이징하기 위해서 FileLabel 컴포넌트를 따로 만들어 두 컴포넌트에 다음과 같이 스타일을 지정해주었다.&lt;/p&gt;
&lt;pre id=&quot;code_1649674934815&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export const FileLabel = styled.label`
  padding: 20px 50px;
  border: 1px solid #f0f0f0;
  border-radius: 2px;
  font-size: 20px;
  line-height: 3;
  background-color: #f0f0f0;
  color: #616161;
  cursor: pointer;
`;

export const FetchLocalPC = styled.input`
  position: absolute;
  width: 0;
  height: 0;
  padding: 0;
  overflow: hidden;
  border: 0;
`;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기능을 담당하는 FetchLocalPC 컴포넌트의 위치값과 스타일을 모두 초기화해주고 실제 스타일을 담당할 FileLabel 컴포넌트에 원하는 CSS 속성을 지정해줌으로써 스타일을 커스터마이징 해주었다.&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) 이벤트가 발생할 때 File 인터페이스 참조하기&lt;/b&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;해당 input 태그에서 click 이벤트가 발생하였을 때 로컬 PC에서 가져온 이미지에 대한 정보를 가져오기 위해서 files 참조하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/API/File&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.mozilla.org/ko/docs/Web/API/File&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1649675346239&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;File - Web API | MDN&quot; data-og-description=&quot;File 인터페이스는 파일에 대한 정보를 제공하고, 웹 페이지의 JavaScript가 해당 내용에 접근할 수 있는 방법을 제공합니다.&quot; data-og-host=&quot;developer.mozilla.org&quot; data-og-source-url=&quot;https://developer.mozilla.org/ko/docs/Web/API/File&quot; data-og-url=&quot;https://developer.mozilla.org/ko/docs/Web/API/File&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/befcSz/hyN0d4B7f3/pndqD77jedWB11zwAqNoo1/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/API/File&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.mozilla.org/ko/docs/Web/API/File&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/befcSz/hyN0d4B7f3/pndqD77jedWB11zwAqNoo1/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080');&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;File - Web API | MDN&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;File 인터페이스는 파일에 대한 정보를 제공하고, 웹 페이지의 JavaScript가 해당 내용에 접근할 수 있는 방법을 제공합니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.mozilla.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: #8cb3be;&quot;&gt;File 인터페이스는 파일에 대한 정보를 제공&lt;/span&gt;하고, 웹 페이지의 JavaScript가 해당 내용에 접근할 수 있는 방법을 제공합니다. &lt;span style=&quot;color: #8cb3be;&quot;&gt;File&amp;nbsp;객체는 보통&amp;nbsp;FileList&amp;nbsp;객체에서 가져올 수 있습니다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 fileList를 참조하고 있는 files에 프로퍼티에 대한 콘솔 결과이다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;FileList&amp;nbsp;{0: File, length: 1}0:&amp;nbsp;FilelastModified:&amp;nbsp;1648878872817lastModifiedDate:&amp;nbsp;Sat Apr 02 2022 14:54:32 GMT+0900 (한국 표준시)&amp;nbsp;{}name:&amp;nbsp;&quot;image4.jpeg&quot;size:&amp;nbsp;79784type:&amp;nbsp;&quot;image/jpeg&quot;webkitRelativePath:&amp;nbsp;&quot;&quot;[[Prototype]]:&amp;nbsp;Filelength:&amp;nbsp;1[[Prototype]]:&amp;nbsp;FileList&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 프로젝트에선 단일 이미지 선택 기능 밖에 필요없기 때문에 다음과 같이 fileList 내에서 0번째 인덱스에 적재된 file 인터페이스 값을 참조하였다.&lt;/p&gt;
&lt;pre id=&quot;code_1649675633139&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const storeImage = async (e: React.ChangeEvent&amp;lt;HTMLInputElement&amp;gt;) =&amp;gt; {
	// 해당 async/await 구문이 왜 필요한지 뒤에 단계에서 설명
    const target = e.target as HTMLInputElement;
    
    const result = target.files &amp;amp;&amp;amp; await parseFile(target.files[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;b&gt;3) 비동기 작업과 함께 FileReader 인스턴스 사용&lt;/b&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;해당 file 인스턴스만을 이용해서는 클라이언트 측에서 이미지 소스를 정상적으로 가져올 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지 소스를 사용하기 위해서는 &lt;span style=&quot;color: #0593d3;&quot;&gt;FileReader&lt;/span&gt; 객체를 사용해야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/API/FileReader&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.mozilla.org/ko/docs/Web/API/FileReader&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1649675878026&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;FileReader - Web API | MDN&quot; data-og-description=&quot;FileReader 객체는 웹 애플리케이션이 비동기적으로 데이터를 읽기 위하여 읽을 파일을 가리키는File 혹은 Blob 객체를 이용해 파일의 내용을(혹은 raw data버퍼로) 읽고 사용자의 컴퓨터에 저장하는 &quot; data-og-host=&quot;developer.mozilla.org&quot; data-og-source-url=&quot;https://developer.mozilla.org/ko/docs/Web/API/FileReader&quot; data-og-url=&quot;https://developer.mozilla.org/ko/docs/Web/API/FileReader&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/sgYQW/hyN0lhirmE/K4k8R0RLo1uILQ99kvGKh0/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080&quot;&gt;&lt;a href=&quot;https://developer.mozilla.org/ko/docs/Web/API/FileReader&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.mozilla.org/ko/docs/Web/API/FileReader&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/sgYQW/hyN0lhirmE/K4k8R0RLo1uILQ99kvGKh0/img.png?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080');&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;FileReader - Web API | MDN&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;FileReader 객체는 웹 애플리케이션이 비동기적으로 데이터를 읽기 위하여 읽을 파일을 가리키는File 혹은 Blob 객체를 이용해 파일의 내용을(혹은 raw data버퍼로) 읽고 사용자의 컴퓨터에 저장하는&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.mozilla.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;FileReader 객체는 웹 애플리케이션이 &lt;span style=&quot;color: #8cb3be;&quot;&gt;비동기적으로&lt;/span&gt; 데이터를 읽기 위하여 &lt;span style=&quot;color: #8cb3be;&quot;&gt;읽을 파일을 가리키는 File 혹은&amp;nbsp;Blob 객체를 이용해 파일의 내용을(혹은 raw data버퍼로) 읽고 사용자의 컴퓨터에 저장하는 것을 가능하게 해줍니다.&lt;/span&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1649675967489&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const processFile = (currentFile: File): Promise&amp;lt;string | ArrayBuffer | null&amp;gt;  =&amp;gt; {
    return new Promise(resolve =&amp;gt; {
        const reader = new FileReader();
        reader.readAsDataURL(currentFile);
        reader.onload = () =&amp;gt; {
            const result = reader.result;
            resolve(result);
        }
    })
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FileReader 객체 중 readAsDataURL 메서드는 File 객체를 웹 어플리케이션에서 읽을 수 있도록 Buffer 형식으로 변환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중요한 점은 onload 메서드이다.&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;onload 메서드는 FileReader 객체가 읽기 작업을 마치고 어떠한 작업을 할지 콜백 함수를 인자로 넘기는데, 이때 &lt;span style=&quot;color: #0593d3;&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;color: #000000;&quot;&gt;비동기 작업(pending 상태)이 제대로 이뤄지기 위해서 다음과 같이 async/await 함수를 사용하여 Promise 객체를 반환받도록 코드를 작성하였다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1649676616162&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export const parseFile = async (currentFile: File) =&amp;gt; {
    const parsedFile: string | ArrayBuffer | null = await processFile(currentFile);

    return parsedFile;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-width: 0px 0px 2px 10px; border-bottom-style: solid; border-bottom-color: #6bacce; padding: 3px 5px; border-left-style: solid; border-left-color: #6bacce; margin: 5px 0px; letter-spacing: 1px; line-height: 1.5; font-weight: 800;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;&quot;&gt;ArrayBuffer Type?&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;&quot;&gt;타입스크립트로 프로젝트를 하다보니 모든 값에 대한 타입을 지정해줘야 됬는데, FileReader 객체에서 반환한 값의 타입 중ArrayBuffer 라는 타입이 있어, Buffer에 대한 의문이 들었다.&lt;/span&gt;&lt;span style=&quot;&quot;&gt;&lt;a href=&quot;https://curryyou.tistory.com/441&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://curryyou.tistory.com/441&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1649677058198&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;자바스크립트 버퍼(Buffer): ArrayBuffer, TypedArray 파헤치기!&quot; data-og-description=&quot;자바스크립트는 바이너리 데이터를 다루기 위한 방법으로 버퍼(Buffer)를 구현한 ArrayBuffer, ArrayBufferView를 제공한다. 이번 글에서 다룰 내용을 요약하면 아래와 같다. 1. &amp;nbsp;버퍼(Buffer)란? - 임시로 바&quot; data-og-host=&quot;curryyou.tistory.com&quot; data-og-source-url=&quot;https://curryyou.tistory.com/441&quot; data-og-url=&quot;https://curryyou.tistory.com/441&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/1Y87h/hyN0lBCleO/Wc3j0nIfk6Iu91kRaTGWYk/img.png?width=666&amp;amp;height=278&amp;amp;face=0_0_666_278,https://scrap.kakaocdn.net/dn/mkzM7/hyN0gG4wHo/qJ2x136uPwUnfKzvQmVQ20/img.png?width=666&amp;amp;height=278&amp;amp;face=0_0_666_278,https://scrap.kakaocdn.net/dn/bdITHU/hyN0hsrVhz/b51GZ7jHLLXi4142kMuJJK/img.png?width=2344&amp;amp;height=854&amp;amp;face=0_0_2344_854&quot;&gt;&lt;a href=&quot;https://curryyou.tistory.com/441&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://curryyou.tistory.com/441&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/1Y87h/hyN0lBCleO/Wc3j0nIfk6Iu91kRaTGWYk/img.png?width=666&amp;amp;height=278&amp;amp;face=0_0_666_278,https://scrap.kakaocdn.net/dn/mkzM7/hyN0gG4wHo/qJ2x136uPwUnfKzvQmVQ20/img.png?width=666&amp;amp;height=278&amp;amp;face=0_0_666_278,https://scrap.kakaocdn.net/dn/bdITHU/hyN0hsrVhz/b51GZ7jHLLXi4142kMuJJK/img.png?width=2344&amp;amp;height=854&amp;amp;face=0_0_2344_854');&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;자바스크립트 버퍼(Buffer): ArrayBuffer, TypedArray 파헤치기!&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;자바스크립트는 바이너리 데이터를 다루기 위한 방법으로 버퍼(Buffer)를 구현한 ArrayBuffer, ArrayBufferView를 제공한다. 이번 글에서 다룰 내용을 요약하면 아래와 같다. 1. &amp;nbsp;버퍼(Buffer)란? - 임시로 바&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;curryyou.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 포스팅에서 Buffer 개념에 대해서 확인할 수 있었다.&lt;span style=&quot;color: #555555;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;자바스크립트의 버퍼란 &lt;span style=&quot;color: #8cb3be;&quot;&gt;특정 크기의 메모리 공간&lt;/span&gt;에 &lt;span style=&quot;color: #8cb3be;&quot;&gt;바이너리 데이터&lt;/span&gt;를 저장해두는 객체이다.&lt;/blockquote&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;하지만 자바스크립트의 용도가 다양해지면서 오디오, 비디오 및 웹소켓 통신에서 사용하는 Raw Binary Data를 직접 다룰 필요가 생겼기 때문에 메모리의 효율성을 높이기 위해 Buffer를 제공하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(이 중 ArrayBuffer는 개발자가 지정한 메모리 크기만큼의 바이너리 데이터를 저장하는 객체이다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-width: 0px 0px 2px 10px; border-bottom-style: solid; border-bottom-color: #6bacce; padding: 3px 5px; border-left-style: solid; border-left-color: #6bacce; margin: 5px 0px; letter-spacing: 1px; line-height: 1.5; font-weight: 800;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;&quot;&gt;구현 영상&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;무제.gif&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/LPd1F/btry2mQBRHO/kHchUEutbVtJPj2RJEOU41/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/LPd1F/btry2mQBRHO/kHchUEutbVtJPj2RJEOU41/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/LPd1F/btry2mQBRHO/kHchUEutbVtJPj2RJEOU41/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/LPd1F/btry2mQBRHO/kHchUEutbVtJPj2RJEOU41/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;1920&quot; height=&quot;1080&quot; data-filename=&quot;무제.gif&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>TS</category>
      <author>choi95</author>
      <guid isPermaLink="true">https://choi95.tistory.com/205</guid>
      <comments>https://choi95.tistory.com/205#entry205comment</comments>
      <pubDate>Mon, 11 Apr 2022 20:50:21 +0900</pubDate>
    </item>
    <item>
      <title>react-redux에 typescript 활용하기_typesafe-actions 라이브러리 사용기</title>
      <link>https://choi95.tistory.com/204</link>
      <description>&lt;h3 style=&quot;box-sizing: border-box; border-width: 0px 0px 2px 10px; border-bottom-style: solid; border-bottom-color: #6bacce; padding: 3px 5px; border-left-style: solid; border-left-color: #6bacce; margin: 5px 0px; letter-spacing: 1px; line-height: 1.5; font-weight: 800;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;Issue&lt;/span&gt;&lt;/h3&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&gt;프로젝트 도중 typescript 기반의 react-redux 전역 상태를 세팅해야 되는 상황이 있었다.&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&gt;redux 내 action, 액션 생성 함수, 리듀서에 대한 적절한 타입을 지정해주기 까다로운 상황 속에서 &lt;span style=&quot;color: #0593d3;&quot;&gt;typesafe-actions&lt;/span&gt; 라는 라이브러리를 알게 되었다.&lt;/span&gt;&lt;span&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;a href=&quot;https://github.com/piotrwitek/typesafe-actions&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/piotrwitek/typesafe-actions&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1649331782433&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - piotrwitek/typesafe-actions: Typesafe utilities for &amp;quot;action-creators&amp;quot; in Redux / Flux Architecture&quot; data-og-description=&quot;Typesafe utilities for &amp;quot;action-creators&amp;quot; in Redux / Flux Architecture - GitHub - piotrwitek/typesafe-actions: Typesafe utilities for &amp;quot;action-creators&amp;quot; in Redux / Flux Architecture&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/piotrwitek/typesafe-actions&quot; data-og-url=&quot;https://github.com/piotrwitek/typesafe-actions&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/NuUiq/hyNXhTrhyH/8iRuUwN1NVbs3BSgYrCzA1/img.png?width=1200&amp;amp;height=600&amp;amp;face=969_150_1072_262&quot;&gt;&lt;a href=&quot;https://github.com/piotrwitek/typesafe-actions&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/piotrwitek/typesafe-actions&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/NuUiq/hyNXhTrhyH/8iRuUwN1NVbs3BSgYrCzA1/img.png?width=1200&amp;amp;height=600&amp;amp;face=969_150_1072_262');&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;GitHub - piotrwitek/typesafe-actions: Typesafe utilities for &quot;action-creators&quot; in Redux / Flux Architecture&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Typesafe utilities for &quot;action-creators&quot; in Redux / Flux Architecture - GitHub - piotrwitek/typesafe-actions: Typesafe utilities for &quot;action-creators&quot; in Redux / Flux Architecture&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.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;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이에 typesafe-actions 라이브러리를 활용하여 채팅 로그를 저장하는 전역 store을 구현하였고 이에 대해 회고해 보고자 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-width: 0px 0px 2px 10px; border-bottom-style: solid; border-bottom-color: #6bacce; padding: 3px 5px; border-left-style: solid; border-left-color: #6bacce; margin: 5px 0px; letter-spacing: 1px; line-height: 1.5; font-weight: 800;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;Code&lt;/span&gt;&lt;b&gt;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h3&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) redux 상태 관리 코드 분립&lt;/b&gt;&lt;b&gt;&lt;/b&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;flux 기반의 데이터 흐름을 위해 redux와 관련된 코드를 modules 폴더에 분립하여 주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(module이란 키워드는 프로그래밍 상에서 광범위하게 쓰이기 때문에, 지금 보니 modules 폴더명은 그닥 좋지 못한 것 같다)&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;채팅 관련 정보를 저장하고 있는 comments 폴더 내에 크게 actions, reducer, types 하위 폴더로 분립해줌으로써, 각 기능에 대한 코드를 간략 및 명목화 시켰다.&lt;/p&gt;
&lt;pre id=&quot;code_1649332234017&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; modules
 ┣  comments
 ┃ ┣  actions.ts
 ┃ ┣  reducer.ts
 ┃ ┗  types.ts
 ┗  index.ts&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;b&gt;2) 액션 생성 함수에 대해 createAction 함수 사용&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1649333000766&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { createAction } from 'typesafe-actions';
import type { CommentInfo } from './types';

export const ADD_COMMENT = 'comments/ADD_COMMENT' as const;
export const DELETE_COMMENT = 'comments/DELETE_COMMENT';
export const RESPONSE_COMMENT = 'response/RESPONSE_COMMENT';

let nextId = 5;

export const addComment = (commentInfo: CommentInfo) =&amp;gt; ({
  type: ADD_COMMENT,
  payload: {
    ...commentInfo,
    messageId: ++nextId,
  },
});

export const deleteComment = createAction(DELETE_COMMENT)&amp;lt;number&amp;gt;();
export const responseComment = createAction(RESPONSE_COMMENT)&amp;lt;number | null&amp;gt;();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래 타입스크립트 내에서 액션 값에 뒤에 &lt;span style=&quot;color: #0593d3;&quot;&gt;as const&amp;nbsp;&lt;span style=&quot;color: #000000;&quot;&gt;라고 const assertion을 해줘야, string literal type이 아닌 상수 그 자체 값으로 인식된다.&lt;/span&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;color: #0593d3;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;typesafe-acions 내 createAction 함수를 사용하여 액션을 지정하면 자동으로 이에 대한 타입 지정 과정을 처리해주기 때문에 생략이 가능하다.&lt;/span&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;color: #0593d3;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;하지만 이 경우에도 as const를 써야 될 경우와 쓰지 않을 경우로 나눠지는데, action.payload 값을 그대로 쓰지 않고 정제해야 될 경우(파라미터로 받아 온 데이터와 리턴되는 action.payload 값이 다를 경우)에는 createAction 함수를 쓰지 않고 const assertion을 한 뒤 일반 액션 생성 함수를 작성하는 것이 좋다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1649333555075&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 예시 코드

// const assertion 사용
export const addTodo = (text: string) =&amp;gt; ({
    type: ADD_TODO,
    payload: {
        id: nextId++,
        text
    }
})

// createAction 함수 사용 =&amp;gt; 코드가 더 복잡해 질 수 있음
export const addTodo = createAction(ADD_TODO, action =&amp;gt; (text: string) =&amp;gt; action({
     id: nextId++,
     text
}))&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;b&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;3) InitialState와 Action에 대한 타입 지정&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1649333757674&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { ActionType } from 'typesafe-actions';
import * as actions from './actions';

export type CommentsAction = ActionType&amp;lt;typeof actions&amp;gt;;
export type ResponseAction = ActionType&amp;lt;typeof actions&amp;gt;;

export type CommentInfo = {
  userid: number;
  messageId: number;
  userName: string;
  profileImage: string;
  content: string;
  date: string;
  responseId: number | null;
};

export type CommentsInfoState = CommentInfo[];

export type ResponseState = {
  responseId: null | number;
  responseActive: boolean;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;액션에 대한 타입을 지정해 주기 위해 acions 모듈에서 각 action들을 한 번에 import 해 온 뒤, typeof 연산자를 사용하여 해당 acions 객체에 대한 타입 추론을 해주었다.&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;그리고 해당 typeof 연산을 typesafe-actions 내 ActionType 함수의 제너릭으로 지정해주어, action에 대한 타입 값을 손 쉽게 지정하여 주었다.&lt;/p&gt;
&lt;pre id=&quot;code_1649334164782&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 예시 코드

// ActionType을 쓰지 않을 경우(모든 액션에 대해 ReturnType 연산을 사용)
type TodosAction = ReturnType&amp;lt;typeof addTodo&amp;gt; | ReturnType&amp;lt;typeof toggleTodo&amp;gt; | ReturnType&amp;lt;typeof removeTodo&amp;gt;

// ActionType을 쓸 경우
const actions = { addTodo, toggleTodo, removeTodo };
type TodosAction = ActionType&amp;lt;typeof actions&amp;gt;;&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;b&gt;4) createReducer 함수를 사용하여 리듀서 정의&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1649334281544&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { createReducer } from 'typesafe-actions';
import { CommentsAction, CommentsInfoState } from './types';
import { ADD_COMMENT, DELETE_COMMENT, RESPONSE_COMMENT } from './actions';

const initialState: CommentsInfoState = [
  {
    userid: 1,
    messageId: 1,
    userName: '춘식',
    profileImage: './img/profile_yongwoo.png',
    content: '네이버로 보내줘~',
    date: '2021-02-10 23:26:30',
    responseId: null,
  },
  ( ... )
];

const comments = createReducer&amp;lt;CommentsInfoState, CommentsAction&amp;gt;(initialState, {
  [ADD_COMMENT]: (state, action) =&amp;gt; state.concat(action.payload),
  [DELETE_COMMENT]: (state, action) =&amp;gt; state.filter((info) =&amp;gt; info.messageId !== action.payload),
});

export default comments;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;types 모듈에서 정의 한 type 값들을 import하여 initialState에 타입을 지정해주며 createReducer 함수에 제너릭으로 사용하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1649334530460&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 예시 코드

// createReducer 함수를 사용하지 않은 경우
function todos(state: TodosState = initialState, action: TodosAction): TodosState {
     switch(action.type) {
         case ADD_TODO:
             return state.concat({
                 id: action.payload.id,
                 text: action.payload.text,
                 done: false,
             })
         case TOGGLE_TODO:
             return state.map(todo =&amp;gt;
                 todo.id === action.payload ? { ...todo, done: !todo.done } : todo
                 )
         case REMOVE_TODO:
             return state.filter(todo =&amp;gt; todo.id !== action.payload)
         default:
             return state
     }
 }
 
 // createReducer 함수를 사용한 경우
 const todos = createReducer&amp;lt;TodosState, TodosAction&amp;gt;(initialState, {
    [ADD_TODO]: (state, action) =&amp;gt; state.concat({
        ...action.payload,
        done: false
    }),
    [TOGGLE_TODO]: (state, action) =&amp;gt; state.map(
        todo =&amp;gt; todo.id === action.payload ? { ...todo, done: !todo.done } : todo
    ),
    [REMOVE_TODO]: (state, action) =&amp;gt; state.filter(todo =&amp;gt; todo.id !== action.payload)
})&lt;/code&gt;&lt;/pre&gt;</description>
      <category>TS</category>
      <author>choi95</author>
      <guid isPermaLink="true">https://choi95.tistory.com/204</guid>
      <comments>https://choi95.tistory.com/204#entry204comment</comments>
      <pubDate>Thu, 7 Apr 2022 21:31:07 +0900</pubDate>
    </item>
    <item>
      <title>그래프 문제를 풀다가 어이 없었던 실수_배열 객체에 대한 고찰</title>
      <link>https://choi95.tistory.com/203</link>
      <description>&lt;h3 style=&quot;box-sizing: border-box; border-width: 0px 0px 2px 10px; border-bottom-style: solid; border-bottom-color: #6bacce; padding: 3px 5px; border-left-style: solid; border-left-color: #6bacce; margin: 5px 0px; letter-spacing: 1px; line-height: 1.5; font-weight: 800;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;&quot;&gt;문제&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;&quot;&gt;&lt;a href=&quot;https://github.com/choi2601/LeetCode-ProblemSolving/tree/main/797-all-paths-from-source-to-target&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/choi2601/LeetCode-ProblemSolving/tree/main/797-all-paths-from-source-to-target&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1648869267554&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - choi2601/LeetCode-ProblemSolving:  &amp;zwj;   LeetCode 알고리즘 문제풀이 코드 저장소&quot; data-og-description=&quot; &amp;zwj;  LeetCode 알고리즘 문제풀이 코드 저장소. Contribute to choi2601/LeetCode-ProblemSolving development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/choi2601/LeetCode-ProblemSolving/tree/main/797-all-paths-from-source-to-target&quot; data-og-url=&quot;https://github.com/choi2601/LeetCode-ProblemSolving&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cFfZaE/hyNTsVa6UV/fytnHIAf0sQ8HpcRkIlb40/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/choi2601/LeetCode-ProblemSolving/tree/main/797-all-paths-from-source-to-target&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/choi2601/LeetCode-ProblemSolving/tree/main/797-all-paths-from-source-to-target&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cFfZaE/hyNTsVa6UV/fytnHIAf0sQ8HpcRkIlb40/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;GitHub - choi2601/LeetCode-ProblemSolving:  &amp;zwj;  LeetCode 알고리즘 문제풀이 코드 저장소&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt; &amp;zwj;  LeetCode 알고리즘 문제풀이 코드 저장소. Contribute to choi2601/LeetCode-ProblemSolving development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-width: 0px 0px 2px 10px; border-bottom-style: solid; border-bottom-color: #6bacce; padding: 3px 5px; border-left-style: solid; border-left-color: #6bacce; margin: 5px 0px; letter-spacing: 1px; line-height: 1.5; font-weight: 800;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;&quot;&gt;배열 객체&lt;/span&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1648869366339&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const answer = [];
    const n = graph.length;
    const nodes = Array.from(Array(n), () =&amp;gt; Array());
    const ch = Array.from({length: n}, () =&amp;gt; 0);
    
    for(let i = 0; i &amp;lt; graph.length; i++) {
        const route = graph[i];
        for(let j = 0; j &amp;lt; route.length; j++) {
            const path = route[j];
            nodes[i].push(path);
        }
    }
    
    const tmp = [0];
    
    function DFS(v) {
        if(v === n - 1) {
            answer.push(tmp);
            return;
        }
        else {
            for(let i = 0; i &amp;lt; nodes[v].length; i++) {
                if(ch[nodes[v][i]] === 0) {
                    ch[nodes[v][i]] = 1;
                    tmp.push(nodes[v][i]);
                    DFS(nodes[v][i]);
                    ch[nodes[v][i]] = 0;
                    tmp.pop();
                }
            }
        }
    }
    
    ch[0] = 1;
    DFS(0);
    
    return answer;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 코드는 처음에 작성한 코드로서, 다음과 같이 계속 정답이 틀리게 나왔다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;[[0],[0]]&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지나온 경로를 임시 저장하는 변수인 tmp를 콘솔창에 찍어봤을 때는 올바른 경로 행적이 나오는데, 경로가 담긴 정답 변수를 반환하면 위와 같이 초기 경로인 0만을 담은 배열이 나왔다.&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;color: #0593d3;&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;배열은 객체로서, 참조값에 의해 값(value)가 공유되기 때문에, 다시 경로를 되돌아왔을 때 이전 행적을 없애기 위한 tmp.pop() 연산이 최종 결과에 영향을 미친 것 이었다.&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;이에 원본 배열을 정답 변수에 바로 push()하는 것이 아닌, Array.prototype.slice()를 사용하여 원본 배열을 깊은 복사한 새로운 참조값을 지닌 배열 객체를 정답 변수에 push() 해줌으로써 문제를 해결할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>JS</category>
      <author>choi95</author>
      <guid isPermaLink="true">https://choi95.tistory.com/203</guid>
      <comments>https://choi95.tistory.com/203#entry203comment</comments>
      <pubDate>Sat, 2 Apr 2022 12:23:41 +0900</pubDate>
    </item>
    <item>
      <title>Next.js로 정적생성(Static Generation) 구현_getStaticProps와 getStaticPaths 사용기</title>
      <link>https://choi95.tistory.com/202</link>
      <description>&lt;h3 style=&quot;box-sizing: border-box; border-width: 0px 0px 2px 10px; border-bottom-style: solid; border-bottom-color: #6bacce; padding: 3px 5px; border-left-style: solid; border-left-color: #6bacce; margin: 5px 0px; letter-spacing: 1px; line-height: 1.5; font-weight: 800;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;Issue&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Next.js 프로젝트 도중 상품 상세 페이지를 정적생성(Static Generation)을 하기 위해서 &lt;span style=&quot;color: #0593d3;&quot;&gt;getStaticProps&lt;/span&gt;와 &lt;span style=&quot;color: #0593d3;&quot;&gt;getStaticPaths&lt;/span&gt;를 사용해 보기로 하였다.&lt;/span&gt;&lt;span&gt;해당 기능을 구현하기 이전에 정적생성, getStaticProps, getStaticPaths가 어떤 의미를 지니고 있는지 간략하게 메모해 두고자 한다.&lt;/span&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;정적생성(Static Generation)은 &lt;span style=&quot;color: #8cb3be;&quot;&gt;빌드 시점에 온전한 페이지의 HTML이 생성되어 서버에서 물리적으로 HTML 파일을 모두 갖고 있는 상태&lt;/span&gt;이다. 한 번 응답한 후에는 &lt;span style=&quot;color: #8cb3be;&quot;&gt;CDN(content delivery network)이 파일을 기억(캐쉬, cache)하여 응답&lt;/span&gt;하기 때문에 랜더링 속도가 빠르고 불필요한 서버 요청을 줄일 수 있다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;getStaticProps는&lt;span style=&quot;color: #8cb3be;&quot;&gt; 빌드 시점에 api를 호출하고 데이터를 응답&lt;/span&gt; 받아서 HTML을 완성하는 정적생성을 위한 메서드이다.&amp;nbsp;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1648696838369&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export async function getStaticProps(context) {
  return {
    props: {}, // will be passed to the page component as props
  }
}&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;getStaticPaths는 &lt;span style=&quot;color: #8cb3be;&quot;&gt;동적 라우트인 페이지이면서 정적으로 페이지를 생성&lt;/span&gt;하고 싶을 때 사용되는 메서드이다.&amp;nbsp;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1648696854895&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export async function getStaticPaths() {
  return {
    paths: [
      { params: { ... } }
    ],
    fallback: true // false or 'blocking'
  };
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-width: 0px 0px 2px 10px; border-bottom-style: solid; border-bottom-color: #6bacce; padding: 3px 5px; border-left-style: solid; border-left-color: #6bacce; margin: 5px 0px; letter-spacing: 1px; line-height: 1.5; font-weight: 800;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;Code&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;1) api 비동기 함수 분립&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;api fetch는 서버에서 데이터를 가져올 때마다 매번 로직을 짜줘야 하기 때문에 각각의 컴포넌트에 관련 로직을 정의해준 대신에 별도로 분립하였다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1648697318921&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  api
 ┣   contacts.api.ts
 ┗   items.api.ts // 해당 컴포넌트 내에서 API 통신을 위한 비동기 함수 정의&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 컴포넌트 내에서 async/await 함수를 통해 비동기 로직을 처리해주고 서버에서 받아 온 데이터의 타입은 인터페이스을 통해 정의해주었다.&lt;/p&gt;
&lt;pre id=&quot;code_1648697555824&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import axios from 'axios';

export interface ItemInfoType {
    id:              number;
    name:            string;
    originalPrice:   number;
    minSellingPrice: number;
    ncSellingPrice:  number;
    warning:         string;
    discountRate:    number;
    imageUrl:        string;
    options:         Option[];
    brand:           string;
}

export interface Option {
    expireAt: Date;
    count: number;
    sellingPrice: number;
}

export async function getItemInfo(paramsId: string | string[] | undefined) {
    const request = await axios.get(`https://(..URL)/${paramsId}`);
    const { data: { conItem } } = request;
    
    // 원하는 데이터를 랩핑하기 위해 서버에서 받아 온 데이터를 디스트럭쳐링 할당
    const { id, name, originalPrice, minSellingPrice, ncSellingPrice, warning, discountRate, imageUrl, options, } = conItem;
    
    const itemInfo = { id, name, originalPrice, minSellingPrice, ncSellingPrice, warning, discountRate, imageUrl, options, brand: conItem[&quot;conCategory2&quot;].name };
    
    return {
        itemInfo
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2) pages 폴더 내에서 getStaticProps 사용&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pages 폴더에선 Next.js의 파일 기반 라우팅 기능을 담당하는 디렉토리이다. 폴더 혹은 파일 이름을 지정하여 컴포넌트를 만들면 이에 해당하는 router가 자동으로 지정된다.&lt;/p&gt;
&lt;pre id=&quot;code_1648697948538&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; items
 ┗  [id] // query parameter
 ┃ ┗  index.tsx&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 컴포넌트 내에서 api 함수를 호출하여 미리 각 상품들에 대한 상세 정보 리스트를 서버에서 불러와 HTML을 구성함으로써, 정적생성이 구현되도록 하였다.&lt;/p&gt;
&lt;pre id=&quot;code_1648698367079&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export const getStaticProps: GetStaticProps = async ({ params }) =&amp;gt; {
  const id = params &amp;amp;&amp;amp; params.id;
  const { itemInfo } = await getItemInfo(id);

  if (!itemInfo) {
    return {
      notFound: true,
    };
  }

  return {
    props: itemInfo,
  };
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 서버에서 가져 올 상품 데이터들은 query parameter id값으로 동적 라우팅이 되기 때문에 상품 리스트에 해당하는 모든 상품들의 상세 페이지를 가져오기 위해서 getStaticPaths도 같이 사용해야 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(getStaticProps에 인자로 받는 params가 query parameter id값에 해당한다)&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;이를 위해 다음과 같이 상품 카테고리에 대한 상품 리스트 데이터를 서버에서 받아오고, 해당 상품 리스트 데이터에서 각 상품의 상세 페이지에 해당하는 고유 id값을 추출하여 paths에 적재하여 반환하였다.&lt;/p&gt;
&lt;pre id=&quot;code_1648699050034&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export const getStaticPaths: GetStaticPaths = async () =&amp;gt; {
  const {
    data: { conCategory1s },
  } = await axios.get('https://(...URL)');
  const categoriesID = conCategory1s.map(({ id }: any) =&amp;gt; id);
  let paths: any = [];

  for (const id of categoriesID) {
    const { data } = await axios.get(`https://(...URL)`);
    const {
      conCategory1: { conCategory2s },
    } = data;
    let conItems = [];
    for (let i = 0; i &amp;lt; conCategory2s.length; i++) {
      conItems = conCategory2s[i].conItems;

      const conItemsID = conItems.map(({ id }: any) =&amp;gt; ({
        params: { id: id.toString() },
      }));
      paths = paths.concat(conItemsID);
    }
  }

  return { paths, fallback: false };
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 getStaticProps에서 반환한 값을 랜더링하고자 하는 컴포넌트 내에서 다음과 같이 props로 전달 받아 정적생성 기능을 마무리하였다.&lt;/p&gt;
&lt;pre id=&quot;code_1648699734804&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const Items: React.FC&amp;lt;ItemInfoType&amp;gt; = (itemInfo: ItemInfoType) =&amp;gt; {
  return (
    &amp;lt;ItemsTemplate&amp;gt;
      &amp;lt;ItemsForm itemInfo={itemInfo} /&amp;gt;
    &amp;lt;/ItemsTemplate&amp;gt;
  );
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-width: 0px 0px 2px 10px; border-bottom-style: solid; border-bottom-color: #6bacce; padding: 3px 5px; border-left-style: solid; border-left-color: #6bacce; margin: 5px 0px; letter-spacing: 1px; line-height: 1.5; font-weight: 800;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;회고&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;결과적으로 각 상품들에 대한 상세 페이지를 정적생성 방식을 통해 클라이언트 측에서 랜더링하는 데에 기능 상으로 성공하였다.&lt;/span&gt;&lt;span&gt;하지만 CSR 방식으로 랜더링하였을 때보다 랜더링 속도가 현저히 느린 성능 상의 문제가 있었다.&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&gt;랜더링 성능을 높이기 위해서 단순히 서버 측에서 페이지를 구성하는 SSR 방식이, 정답만은 아니라는 것을 해당 프로젝트에서 확인해 볼 수 있었다.&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&gt;또한 쇼핑물과 같이 커머스 기반의 서비스 내에서는 매일 새로운 상품을 보여주기 위하여 상품 목록 api를 호출해서 상품 데이터를 받아와야 하는데, 만약 빌드 시점에 정적으로 HTML을 생성해버리면 유동적으로 데이터를 받아오지 못하는 문제가 있기 때문에, 관련 메서드들을 사용할 경우에는 신중히 사용해야 함을 재확인할 수 있었다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Next.js</category>
      <author>choi95</author>
      <guid isPermaLink="true">https://choi95.tistory.com/202</guid>
      <comments>https://choi95.tistory.com/202#entry202comment</comments>
      <pubDate>Thu, 31 Mar 2022 13:06:18 +0900</pubDate>
    </item>
    <item>
      <title>event.target property 접근_as를 통한 타입 단언(Type Assertion)</title>
      <link>https://choi95.tistory.com/201</link>
      <description>&lt;h3 style=&quot;box-sizing: border-box; border-width: 0px 0px 2px 10px; border-bottom-style: solid; border-bottom-color: #6bacce; padding: 3px 5px; border-left-style: solid; border-left-color: #6bacce; margin: 5px 0px; letter-spacing: 1px; line-height: 1.5; font-weight: 800;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;&quot;&gt;문제&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;&quot;&gt;화면에 랜더링 된 각 리스트를 누르면 해당 리스트의 상세 페이지로 이동하는 기능을 구현하기 위해 다음과 같이 각 리스트에 고유 아이디 값을 dataset에 할당해주었고 해당 id값을 path query parameter에 할당해주었다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1648300300053&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const LinkPage: FC&amp;lt;LinkPageProps&amp;gt; = ({ itemInfoList }: LinkPageProps) =&amp;gt; {
  const navigate = useNavigate();

  const moveToDetailPage = (
    e: React.MouseEvent&amp;lt;HTMLParagraphElement, MouseEvent&amp;gt;
  ) =&amp;gt; {
    const id = (e.target as HTMLParagraphElement).dataset.id;
    navigate(`/detail?id=${id}`);
  };

	( ... )

  return (
             &amp;lt;LinkTitle
               data-id={itemInfo.key}
               onClick={moveToDetailPage}
               &amp;gt;
               {itemInfo?.sent ? itemInfo.sent.subject : &quot;무제&quot;}
              &amp;lt;/LinkTitle&amp;gt;
  );
};&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;blockquote data-ke-style=&quot;style3&quot;&gt;'EventTarget' 형식에 'dataset' 속성이 없습니다. ts(2339)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-width: 0px 0px 2px 10px; border-bottom-style: solid; border-bottom-color: #6bacce; padding: 3px 5px; border-left-style: solid; border-left-color: #6bacce; margin: 5px 0px; letter-spacing: 1px; line-height: 1.5; font-weight: 800;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;&quot;&gt;문제해결&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TypeScript 컴파일러가 타입을 실제 런타임에 존재할 변수의 타입과 다르게 추론하거나 너무 보수적으로 추론하는 경우에 위와 같은 문제가 발생한 것인데, 이를 해결하기 위해 as 문법을 사용하였다.&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;다음과 같이 e.target는 p 태그 기반인 엘리먼트이기 때문에 type 값이 HTMLParagraphElement이며, 안정적으로 event.target의 특정 property에 접근하기 위해 as 구문을 사용하여 해당 type으로 &lt;span style=&quot;color: #0593d3;&quot;&gt;타입 단언(Type Assertion)&lt;span style=&quot;color: #000000;&quot;&gt; 을 선언해 주었다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1648300809546&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt; const moveToDetailPage = (
    e: React.MouseEvent&amp;lt;HTMLParagraphElement, MouseEvent&amp;gt;
  ) =&amp;gt; {
    const id = (e.target as HTMLParagraphElement).dataset.id;
    navigate(`/detail?id=${id}`);
  };&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-width: 0px 0px 2px 10px; border-bottom-style: solid; border-bottom-color: #6bacce; padding: 3px 5px; border-left-style: solid; border-left-color: #6bacce; margin: 5px 0px; letter-spacing: 1px; line-height: 1.5; font-weight: 800;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;&quot;&gt;Reference&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/42066421/property-value-does-not-exist-on-type-eventtarget&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://stackoverflow.com/questions/42066421/property-value-does-not-exist-on-type-eventtarget&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1648300854902&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;Property 'value' does not exist on type 'EventTarget'&quot; data-og-description=&quot;I am using TypeScript Version 2 for an Angular 2 component code. I am getting error &amp;quot;Property 'value' does not exist on type 'EventTarget'&amp;quot; for below code, what could be the solution. Tha...&quot; data-og-host=&quot;stackoverflow.com&quot; data-og-source-url=&quot;https://stackoverflow.com/questions/42066421/property-value-does-not-exist-on-type-eventtarget&quot; data-og-url=&quot;https://stackoverflow.com/questions/42066421/property-value-does-not-exist-on-type-eventtarget&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cr3ozS/hyNOj5Ielg/3QnhdI9ZI83kikyfScNVQ1/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316&quot;&gt;&lt;a href=&quot;https://stackoverflow.com/questions/42066421/property-value-does-not-exist-on-type-eventtarget&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://stackoverflow.com/questions/42066421/property-value-does-not-exist-on-type-eventtarget&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cr3ozS/hyNOj5Ielg/3QnhdI9ZI83kikyfScNVQ1/img.png?width=316&amp;amp;height=316&amp;amp;face=0_0_316_316');&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;Property 'value' does not exist on type 'EventTarget'&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;I am using TypeScript Version 2 for an Angular 2 component code. I am getting error &quot;Property 'value' does not exist on type 'EventTarget'&quot; for below code, what could be the solution. Tha...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;stackoverflow.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>TS</category>
      <author>choi95</author>
      <guid isPermaLink="true">https://choi95.tistory.com/201</guid>
      <comments>https://choi95.tistory.com/201#entry201comment</comments>
      <pubDate>Sat, 26 Mar 2022 22:20:59 +0900</pubDate>
    </item>
    <item>
      <title>Promise 리턴 타입 오류</title>
      <link>https://choi95.tistory.com/200</link>
      <description>&lt;h3 style=&quot;box-sizing: border-box; border-width: 0px 0px 2px 10px; border-bottom-style: solid; border-bottom-color: #6bacce; padding: 3px 5px; border-left-style: solid; border-left-color: #6bacce; margin: 5px 0px; letter-spacing: 1px; line-height: 1.5; font-weight: 800;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;&quot;&gt;문제&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;&quot;&gt;async/await 비동기 처리 함수 내에서&amp;nbsp;&lt;span&gt;특정 정보들이 담긴 아이템 리스트를&lt;span&gt; 서버로부터 전달 받아 반환하도록 다음과 같이 코드를 작성하였다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1648296118183&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export interface ItemType {
    created_at:     number;
    key:            string;
    expires_at:     number;
    download_count: number;
    count:          number;
    size:           number;
    summary:        string;
    thumbnailUrl:   string;
    files:          File[];
    sent?:          Sent;
}

export const getItemData = async (): ItemType[] =&amp;gt; {
    const { data } = await axios.get('https://romantic-hopper-546d5e.netlify.app/data/itemInfoList.json');
    
    return data;
}&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;blockquote data-ke-style=&quot;style3&quot;&gt;'ItemType[]' 형식은 Promise 호환 생성자 값을 참조하지 않으므로 ES5/ES3에서 유효한 비동기 함수 반환 형식이 아닙니다.ts(1055)&lt;br /&gt;&lt;span style=&quot;&quot;&gt;&lt;/span&gt;&lt;span style=&quot;&quot;&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-width: 0px 0px 2px 10px; border-bottom-style: solid; border-bottom-color: #6bacce; padding: 3px 5px; border-left-style: solid; border-left-color: #6bacce; margin: 5px 0px; letter-spacing: 1px; line-height: 1.5; font-weight: 800;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;&quot;&gt;문제해결&lt;/span&gt;&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;async function은 항상 &lt;span style=&quot;color: #8cb3be;&quot;&gt;Promise 객체를 리턴&lt;/span&gt;하고 await에서 코드 실행을 일시 중지하고 함수에서 반환된 Promise 객체의 결과를 기다린다.&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 function은 항상 비동기 연산 과정을 거치기 때문에 Promise 객체를 리턴하기 때문에 위 코드에서도 서버에서 전달 받은 데이터 타입 그 자체를 리턴 타입으로 명시해주는 것이 아니라 Promise&amp;lt;F&amp;gt; 타입 형식을 지정해줘야 되었다.&lt;/p&gt;
&lt;pre id=&quot;code_1648296634910&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export const getItemData = async (): Promise&amp;lt;ItemType[]&amp;gt; =&amp;gt; {
    const { data } = await axios.get('https://romantic-hopper-546d5e.netlify.app/data/itemInfoList.json');
    
    return data;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-width: 0px 0px 2px 10px; border-bottom-style: solid; border-bottom-color: #6bacce; padding: 3px 5px; border-left-style: solid; border-left-color: #6bacce; margin: 5px 0px; letter-spacing: 1px; line-height: 1.5; font-weight: 800;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;&quot;&gt;Reference&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;&quot;&gt;&lt;a href=&quot;https://radlohead.gitbook.io/typescript-deep-dive/future-javascript/async-await&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://radlohead.gitbook.io/typescript-deep-dive/future-javascript/async-await&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1648296685375&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;Async Await - TypeScript Deep Dive&quot; data-og-description=&quot;wrapToReturnPromise는 제네레이터 함수를 사용하여 generator 객체를 반환받은 다음, generator.next()를 사용합니다. 만약 값이 promise 라면 then+catch하고 결과값을 generator.next(result)또는 generator.thorw(error) 로 &quot; data-og-host=&quot;radlohead.gitbook.io&quot; data-og-source-url=&quot;https://radlohead.gitbook.io/typescript-deep-dive/future-javascript/async-await&quot; data-og-url=&quot;https://radlohead.gitbook.io/typescript-deep-dive/future-javascript/async-await&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://radlohead.gitbook.io/typescript-deep-dive/future-javascript/async-await&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://radlohead.gitbook.io/typescript-deep-dive/future-javascript/async-await&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;Async Await - TypeScript Deep Dive&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;wrapToReturnPromise는 제네레이터 함수를 사용하여 generator 객체를 반환받은 다음, generator.next()를 사용합니다. 만약 값이 promise 라면 then+catch하고 결과값을 generator.next(result)또는 generator.thorw(error) 로&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;radlohead.gitbook.io&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>TS</category>
      <author>choi95</author>
      <guid isPermaLink="true">https://choi95.tistory.com/200</guid>
      <comments>https://choi95.tistory.com/200#entry200comment</comments>
      <pubDate>Sat, 26 Mar 2022 21:11:28 +0900</pubDate>
    </item>
    <item>
      <title>React 컴포넌트 간에 props 데이터 전달 과정에서의 type error</title>
      <link>https://choi95.tistory.com/199</link>
      <description>&lt;h3 style=&quot;box-sizing: border-box; border-width: 0px 0px 2px 10px; border-bottom-style: solid; border-bottom-color: #6bacce; padding: 3px 5px; border-left-style: solid; border-left-color: #6bacce; margin: 5px 0px; letter-spacing: 1px; line-height: 1.5; font-weight: 800;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;문제&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;상위 컴포넌트 내에서 하위 컴포넌트로 props를 전달하는 과정에서 type 에러가 발생하였다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1648293164945&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export interface InfoType {
  [key: string]: any;
  id: number;
  title: string;
  client: string;
  due: string;
  count: number;
  amount: number;
  method: string[];
  material: string[];
  status: string;
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1648292790602&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const InfoListForm: FC&amp;lt;OptionalProps&amp;gt; = ({ method, material, status }) =&amp;gt; {
 
 	( ... )
 
  return (
    &amp;lt;Container isEmpty={isEmpty}&amp;gt;
      {isEmpty ? (
        &amp;lt;GuideLine&amp;gt;
          &amp;lt;FrontInformation&amp;gt;조건에 맞는 견적 요청이 없습니다.&amp;lt;/FrontInformation&amp;gt;
        &amp;lt;/GuideLine&amp;gt;
      ) : (
        currentInfoList.map((info: InfoType, index) =&amp;gt; {
          return &amp;lt;CardBoard key={info.id} info={info} index={index} /&amp;gt;;
        })
      )}
    &amp;lt;/Container&amp;gt;
  );
};&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;'{ key: number; info: InfoType; index: number; }' 형식에 'InfoType' 형식의 id, title, client, due 외 5개 속성이 없습니다.&lt;br /&gt;ts(2740)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-width: 0px 0px 2px 10px; border-bottom-style: solid; border-bottom-color: #6bacce; padding: 3px 5px; border-left-style: solid; border-left-color: #6bacce; margin: 5px 0px; letter-spacing: 1px; line-height: 1.5; font-weight: 800;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;문제해결&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;interface InfoType 자체를 인터페이스로 사용하려고 발생 한 문제였다.&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&gt;이를 해결하기 위해 props로 넘겨주는 데이터를 스프레드 문법을 사용하여 개별적인 값들의 목록으로 만든 뒤 하위 컴포넌트 인자 내에서 하나의 객체 값으로 받아 디스트럭쳐링 할당을 통해 각 값들을 사용하였다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1648293567384&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const InfoListForm: FC&amp;lt;OptionalProps&amp;gt; = ({ method, material, status }) =&amp;gt; {

	( ... )
    
  return (
    &amp;lt;Container isEmpty={isEmpty}&amp;gt;
      {isEmpty ? (
        &amp;lt;GuideLine&amp;gt;
          &amp;lt;FrontInformation&amp;gt;조건에 맞는 견적 요청이 없습니다.&amp;lt;/FrontInformation&amp;gt;
        &amp;lt;/GuideLine&amp;gt;
      ) : (
        currentInfoList.map((info: InfoType, index) =&amp;gt; {
          return &amp;lt;CardBoard key={info.id} {...info} index={index} /&amp;gt;;
        })
      )}
    &amp;lt;/Container&amp;gt;
  );
};&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1648293657539&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const CardBoard: FC&amp;lt;InfoType&amp;gt; = (info: InfoType) =&amp;gt; {
  const { title, client, due, count, amount, method, material, status, index } = info;

  ( ... )
  
};&lt;/code&gt;&lt;/pre&gt;</description>
      <category>TS</category>
      <author>choi95</author>
      <guid isPermaLink="true">https://choi95.tistory.com/199</guid>
      <comments>https://choi95.tistory.com/199#entry199comment</comments>
      <pubDate>Sat, 26 Mar 2022 20:21:39 +0900</pubDate>
    </item>
    <item>
      <title>인덱스 시그니쳐(index signature)를 사용하여 객체 접근</title>
      <link>https://choi95.tistory.com/198</link>
      <description>&lt;h3 style=&quot;box-sizing: border-box; border-width: 0px 0px 2px 10px; border-bottom-style: solid; border-bottom-color: #6bacce; padding: 3px 5px; border-left-style: solid; border-left-color: #6bacce; margin: 5px 0px; letter-spacing: 1px; line-height: 1.5; font-weight: 800;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;문제&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;유저가 선택한 아이템의 정보를 담고 있는 리스트를 순회하여 해당 아이템이 존재하는지의 여부를 확인하기 위해서 다음과 같이 문자열로 객체의 프로퍼티에 접근하는 코드를 작성하였다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1648288254648&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;infoList.forEach((info: InfoType) =&amp;gt; {
        let isSorted = false;
        for (let i = 0; i &amp;lt; currentSelectedSortNum; i++) {
          if (info[currentSortName].includes(currentSort[i])) {
            isSorted = true;
            break;
          }
        }

        ( ... )
        
 });&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;blockquote data-ke-style=&quot;style3&quot;&gt;Element implicitly has an 'any' type because type ( ... ) has &lt;span style=&quot;color: #ef6f53;&quot;&gt;no index signature.&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-width: 0px 0px 2px 10px; border-bottom-style: solid; border-bottom-color: #6bacce; padding: 3px 5px; border-left-style: solid; border-left-color: #6bacce; margin: 5px 0px; letter-spacing: 1px; line-height: 1.5; font-weight: 800;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;문제해결&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;해당 문제는 indexable 타입과 자바스크립트 색인의 동작방식에 관해 몰라서 생긴 문제였다.&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&gt;자바스크립트는 객체의 프로퍼티에 접근을 할 때 문자열로 접근할 수 있으며 이는 &lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;자바스크립트 색인의 동작방식에 의해 객체의 색인에 접근할 때 내부적으로 toString() 메서드를 호출하여 문자열로 변형된 값을 통해 접근&lt;/span&gt;&lt;/b&gt;한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1648289083458&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ES6
let obj = {
    toString() {
        console.log('toString() called');
    }
};
let foo = {};
foo[obj] = 'Key is obj'; // toString() called
console.log(foo[obj]);
// toString() called
// Key is obj&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;이유인 즉슨, 프로퍼티에 접근할 때 어떤 타입인지 확인할 수 없어 암묵적으로 any 타입을 사용하기 때문이다.&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;이는 tsconfig의 &quot;noImplicitAny&quot;: true 이기 때문에 발생하는 에러이다.&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;해당 문제를 해결하기 위해선 인덱스 시그니쳐(index signature)를 사용하면 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(아래 인터페이스 선언 된 인덱스 시그니쳐는 'key 값은 string이고 반환값은 any' 이라는 의미를 담고 있다)&lt;/p&gt;
&lt;pre id=&quot;code_1648289295713&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;export interface InfoType {
  [key: string]: any;
  id: number;
  title: string;
  client: string;
  due: string;
  count: number;
  amount: number;
  method: string[];
  material: string[];
  status: string;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;box-sizing: border-box; border-width: 0px 0px 2px 10px; border-bottom-style: solid; border-bottom-color: #6bacce; padding: 3px 5px; border-left-style: solid; border-left-color: #6bacce; margin: 5px 0px; letter-spacing: 1px; line-height: 1.5; font-weight: 800;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;Reference&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;a href=&quot;https://heecheolman.tistory.com/64#%EC%A4%91%EC%B2%A9%EB%90%9C-index-signature&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://heecheolman.tistory.com/64#%EC%A4%91%EC%B2%A9%EB%90%9C-index-signature&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1648289492292&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;[typescript] 타입스크립트 인터페이스&quot; data-og-description=&quot;typescript03 타입스크립트 인터페이스 기존에 자바스크립트에는 인터페이스라는 개념이 없었습니다. 하지만 타입스크립트를 이용해 인터페이스를 사용할 수 있게 됐습니다. 인터페이스라는 용어&quot; data-og-host=&quot;heecheolman.tistory.com&quot; data-og-source-url=&quot;https://heecheolman.tistory.com/64#%EC%A4%91%EC%B2%A9%EB%90%9C-index-signature&quot; data-og-url=&quot;https://heecheolman.tistory.com/64&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bDGxom/hyNOyIvR4a/PDv0iDUs09KurzJoi2kmo0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/cgEeXf/hyNOjR5ECW/ckY7LK7WuMC0NU7wkz8MK1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://heecheolman.tistory.com/64#%EC%A4%91%EC%B2%A9%EB%90%9C-index-signature&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://heecheolman.tistory.com/64#%EC%A4%91%EC%B2%A9%EB%90%9C-index-signature&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bDGxom/hyNOyIvR4a/PDv0iDUs09KurzJoi2kmo0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/cgEeXf/hyNOjR5ECW/ckY7LK7WuMC0NU7wkz8MK1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&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;[typescript] 타입스크립트 인터페이스&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;typescript03 타입스크립트 인터페이스 기존에 자바스크립트에는 인터페이스라는 개념이 없었습니다. 하지만 타입스크립트를 이용해 인터페이스를 사용할 수 있게 됐습니다. 인터페이스라는 용어&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;heecheolman.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;</description>
      <category>TS</category>
      <author>choi95</author>
      <guid isPermaLink="true">https://choi95.tistory.com/198</guid>
      <comments>https://choi95.tistory.com/198#entry198comment</comments>
      <pubDate>Sat, 26 Mar 2022 19:57:26 +0900</pubDate>
    </item>
  </channel>
</rss>