<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>binilog.log</title>
        <link>https://velog.io/</link>
        <description>비니로그 쳌킨</description>
        <lastBuildDate>Mon, 02 Jan 2023 13:23:22 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>binilog.log</title>
            <url>https://images.velog.io/images/dabin-lee/profile/9cfe2c8d-6b69-49d1-af0e-dc53a02e12f4/social.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. binilog.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/dabin-lee" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[브라우저 렌더링 최적화]]></title>
            <link>https://velog.io/@dabin-lee/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EB%A0%8C%EB%8D%94%EB%A7%81-%EC%B5%9C%EC%A0%81%ED%99%94</link>
            <guid>https://velog.io/@dabin-lee/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EB%A0%8C%EB%8D%94%EB%A7%81-%EC%B5%9C%EC%A0%81%ED%99%94</guid>
            <pubDate>Mon, 02 Jan 2023 13:23:22 GMT</pubDate>
            <description><![CDATA[<h1 id="browser-rendering-optimization">Browser rendering optimization</h1>
<p><img src="https://velog.velcdn.com/images/dabin-lee/post/d84b8aaf-be47-4a82-9098-004f962ebd2d/image.png" alt=""></p>
<p>javascript: JavaScript로 시각적 변화가 트리거되는 작업을 처리하는데 사용
style: css rule을 어떤 요소에 적용할지 계산하는 프로세스
layout: 요소가 화면에서 얼마의 공간을 차지하고 배치되는지 계산하는 작업
paint: 픽셀을 채우는 프로세스로, 다수의 layer에서 수행
composite: 그려진 여러 layer를 가지고 page를 그리는데 정확한 순서로 화면에 그리는 합성 과정</p>
<h3 id="60fps-60-tiems-a-second란">60fps (60 tiems a second)란?</h3>
<p>각 기계마다 1초에 몇 프레임이 그려지는지를 표현하는 단위</p>
<p>한장을 그릴 때 1초 (1000ms/60) = 16.6ms안에 브라우저가 표현되어야 한다. 
브라우저에서 그리지 못할 경우 애니메이션이 끊겨서 보여지는 현상이 나타난다.</p>
<p>위 모든 단계를 16.6ms안에 그래서 불필요한 과정은 생략하고 압축해서 수행해야 하는것이 기본적인 최적화이다. </p>
<pre><code>[크롬 브라우저 Rendering Path]
- Recalculate Style
- height 속성 변경으로 좌표 계산이 필요한지?
- 필요하다면 layout발생(reflow)
- 효과적인 처리를 위해 update layer tree
- 계산된 영역 정보를 비트맵으로 저장하기 위해 paint 발생
- composite layer 작업에서 레이어를 병합 후 화면을 출력</code></pre><p>이러한 애니메이션 동작을 위해서는 16.6ms내에 처리되어야 한다. </p>
<h3 id="layoer-모델">[layoer 모델]</h3>
<p>웹 페이지를 렌더링하기 위해 필요한 이미지 단위 요소이다.</p>
<ul>
<li>레이어들을 배치/합성하여 최종적인 웹 페이지를 표현한다.</li>
<li>모든 페이지를 root레이어를 가진다.</li>
<li>레이어의 이미지는 텍스처로서 paint작업시 CPU에 의해서 Video Memmory에 로드된다. </li>
<li>결국 레이어 생성 비용이 크고 추가 memory가 필요하게 된다. </li>
</ul>
<p>마치 층 처럼 움직이는데 기존의 process와 별개로 하나의 레이어가 특정한 레이어 층과 만났을 때 생성되는 것이다.
레이어 트리거: 어떤 특정 프로퍼티</p>
<p>transform이라는 css property를 만들게 되면 레이어를 새롭게 만들라는 트리거로 작용하게 된다. </p>
<p>결국은 웹 브라우저화면에서 기존의 렌더링 프로세스와 별개로 또 하나의 화면이라 부를수 있다. 어떠한 화면을 렌더링 할 것인지 병합하는 과정이 composite layer이다. </p>
<p>브라우저 렌더링 프로세스들이 어떤 장치가 수행하는 것일까?
더 빠른 렌더링을 위해 렌더링 최적화 작업이 필요하다.</p>
<p>script가 들어왔을 때 CPU에서는 Recalculate style을 수행한다. 
레이어를 업데이트 -&gt; 메인 메모리 저장 -&gt; 페인트 
GPU안에서 연산을 한다.
Composite Layer </p>
<p>연산 능력보다 GPU가 더 빨리 연산을 할 수 있다. 
Composite layer가 연산 비용이 더 적다. 
같은 dom을 조작시키는 상태에서 cpu조작보다 GPU조작을 하는 것이 비용적으로 더 적게 드는 것이다. </p>
<p>어떻게 하면 성능을 개선할 수 잇는지에 대한 대답은
RAYPER와 PAINT를 최</p>
<p>✔️ 참고</p>
<p><a href="https://youtu.be/G4eQziVzCTE">https://youtu.be/G4eQziVzCTE</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[vue router(2)_404, 중첩 라우팅]]></title>
            <link>https://velog.io/@dabin-lee/vue-router2404-%EC%A4%91%EC%B2%A9-%EB%9D%BC%EC%9A%B0%ED%8C%85</link>
            <guid>https://velog.io/@dabin-lee/vue-router2404-%EC%A4%91%EC%B2%A9-%EB%9D%BC%EC%9A%B0%ED%8C%85</guid>
            <pubDate>Mon, 02 Jan 2023 13:19:58 GMT</pubDate>
            <description><![CDATA[<h3 id="notfound-route-설정하기">NotFound route 설정하기</h3>
<p>동적으로 라우트를 설정할 때 잘못된 경로로 중첩이 들어갈 경우 정규식을 사용해서 무엇이든 일치시킬 수 있다. </p>
<pre><code>{
    path: &quot;/:catchAll(.*)&quot;,
    // 함수로 보이지만 정규식 패턴임
    // 잡히지 않는 모든 경로를 포함한다는
    name: &quot;NotFoundPage&quot;,
    component: NotFoundPage,
},</code></pre><pre><code>{ 
  path: &#39;/:pathMatch(.*)*&#39;, 
  name: &#39;NotFound&#39;, 
  component: NotFoundView 
},
</code></pre><hr>
<h3 id="nested-사용-방법">Nested 사용 방법</h3>
<hr>
<h3 id="routerreplace">router.replace</h3>
<pre><code>예시)

&lt;li class=&quot;nav-item&quot;&gt;
  &lt;router-link
    :to=&quot;{ name: &#39;NestedOne&#39;, replace: true }&quot;
    &gt;Nested One&lt;/router-link
  &gt;
&lt;/li&gt;</code></pre><p><code>router.push메소드에 replace: true 속성을 추가하여 동작한다.</code>
replace: true로 설정할 경우 해당 route는 현재 항목을 대체하는 용도로 쓰인다.</p>
<p>[router.replace &amp; router.push와 차이점]
서로 같은 역할을 하지만 router.replace는 새로운 히스토리 항목에 추가하지 않고 탐색한다. </p>
<p><a href="https://v3.router.vuejs.org/kr/api/#router-link">https://v3.router.vuejs.org/kr/api/#router-link</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[브라우저 렌더링 과정_(2)]]></title>
            <link>https://velog.io/@dabin-lee/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EB%A0%8C%EB%8D%94%EB%A7%81-%EA%B3%BC%EC%A0%952</link>
            <guid>https://velog.io/@dabin-lee/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EB%A0%8C%EB%8D%94%EB%A7%81-%EA%B3%BC%EC%A0%952</guid>
            <pubDate>Sun, 01 Jan 2023 17:40:06 GMT</pubDate>
            <description><![CDATA[<p>[html 파싱 DOM 생성]
브라우저의 요청에 의해서 서버가 응답한 HTML문서는 <strong>문자열로 이루어진 텍스트</strong>이다. 
해당 텍스트 문서를 브라우저에 시각적으로 렌더링을 하기 위해서는 브라우저가 이해할 수 있는 자료구조로 변환을 해야 한다. </p>
<p>브라우저 렌더링 엔진은 HTML문서를 파싱하여 브라우저가 이해할 수 있는 자료구조 (DOM)을 생성한다. </p>
<h2 id="rendering-engine-working-process">rendering engine working process</h2>
<p>하나의 렌더링 엔진 과정을 알아보자
렌더링 엔진은 URI를 통해 요청을 받아 해당하는 데이터를 렌더링 하는 역할을 한다.
크롬의 경우 webkit이라는 렌더링 엔진을 사용한다.</p>
<p>[render tree]
렌더링을 위한 트리 구조의 자료구조이다. 
브라우저 화면에 렌더링 되지 않는 NODE(script, meta tag등)와 display:none등 node들은 포함되지 않는다. 렌더 트리는 브라우저 화면에 렌더링 되는 노드로만 구성된다. 
완성된 렌더트리는 페인팅 처리에 의해 입력되게 된다. </p>
<p>[rendering engine 동작 과정]
렌더 트리를 구축 -&gt; 배치 -&gt; 그리기
파싱 된 HTML는 결과적으로 render tree를 구축하게 된다. render tree는 배치를 하고 최종적으로 브라우저에 표시되는 글자와 이미지 등으로 그려지게 된다. </p>
<ol>
<li>DOM tree 구축
HTML, css, javascript 파싱한다. 
html문서 컨텐츠 내부의 태그들을 DOM node(Document Object Model) 이라는 하나의 요소로 변환시킨다.
css파일도 함께 모든 스타일 요소들을 parcing하여 스타일 규칙이 적용된 DOM Node는 최종적으로 렌더 트리(render object)를 생성하게 된다.</li>
</ol>
<p>render object로 바꾼다는 표현이 더 정확한다. </p>
<ol start="2">
<li><p>render tree 구축
html,css를 파싱해서 만들어진 DOM tree는 render tree로 바뀌게 된다.
색상, 면적, 시각적 속성을 갖는 사각형을 포함한다.</p>
</li>
<li><p>render tree 배치
render object는 DOM object가 변환된 것이다. DOM Node가 배치 정보를 갖고 있기에 각각 그려질 위치 정보를 갖고 있다. 그래서 각 요소들은 브라우저의 화면의 배치 정보에 배치되게 된다. </p>
</li>
<li><p>render tree 그리기
각 node를 돌아다니면서 paint작업을 진행하게 된다. </p>
</li>
</ol>
<hr>
<p><img src="https://velog.velcdn.com/images/dabin-lee/post/2c57d965-eaf2-44aa-92d4-4f029c818022/image.png" alt=""></p>
<p>DOM tree는 Dom Node라는 자식 요소들로 구성되어 있다. 
DOM tree가 다 구성된 후 렌더 트리가 생성되는 것이 아닌 각각의 요소들이 새로 생성되고 업데이트 될 때마다 render object로 전달된다. 바로 바로 변환되게 되는 것이다. </p>
<p><img src="https://velog.velcdn.com/images/dabin-lee/post/c462cd8a-5bbd-43d4-ab3f-48433252cc12/image.png" alt=""></p>
<p>즉, DOM tree가 완벽히 완성될 때까지 화면이 안보이는 것이 아닌 DOM tree가 완성되는 중간중간 render tree 구축, 배치, 그려지게 되는것이다. </p>
<hr>
<h2 id="webkit-동작-과정">webkit 동작 과정</h2>
<p>DOM tree =&gt;  render tree =&gt; 그리기</p>
<ol>
<li>html 파싱하여 DOM tree 생성
html 태그를 파싱을 하면 DOM tree(DOM node가 tree 형태로 바뀐 모습)가 생성되게 된다. 
html은 마크업 랭귀지로써 정보를 표시하는 역할만 할 수 있기에 정보가 동적으로 움직이는 프로그래밍 언어로 동작은 불가능하다. 그래서 html을 Dom tree로 변환하게 되면 javascript와 상호작용하여 해당 정보들은 동적으로 움직일 수 있게 된다.</li>
</ol>
<p>HTML을 DOM tree로 변경하게 되면? 버튼을 누르면 버튼기능 등의 사용자와 상호작용이 가능하게 된다. </p>
<p>[script태그는 왜 하단에 위치하는 것일까?]</p>
<pre><code>1. &lt;script&gt; tag 를 parsing 한다
2. &lt;script&gt; tag 를 실행한다
3. 실행이 완료된 후 다음 tag 를 파싱한다</code></pre><p>렌더링 엔진은 Dom tree를 parsing하는 동시에 실행을 한다. 
script를 만나게 된다면 script태그의 파싱이 완료될 때까지 다음 html 태그는 parsing 할 수가 없다. 추가적으로 파싱 할 html가 남아있지 않은 상태에서 script태그 파싱이 되는것이다. 그러므로 script태그는 body태그 안 마지막에 위치하게 된다. </p>
<p>[html5의 script태그는 비동기이다.]
defer와 async를 사용하게 되면 스크립트가 비동기적으로 동작하게 된다. </p>
<ol start="2">
<li>CSS(style sheets)를 parsing하여 스타일 규칙을 얻기
css또한 html과 마찬가지로 object model을 가지고 있다 (CSSOM)
CSSOM은 CSS parser가 parsing을 하면 스타일 규칙을 얻게 된다.
DOM tree의 어떠한 Dom Node에 대한 스타일 규칙의 정보를 갖고 있다.
돔 트리의 Dom Node와 스타일 규칙을 만나게 되면 render tree가 만들어지게 된다. </li>
</ol>
<ol start="3">
<li>DOM tree를 생성하는 동시 이미 생성된 DOM tree와 CSSOM을 Attachment
생성된 DOM node는 obejct이기에 attach라는 method를 가진다. 
DOM node가 추가되면 attach가 호출되어서 render tree가 생성된다. 
DOM node에 CSS규칙이 붙어서 최종적으로 DOM tree의 구성요소인 DOM node가 
호출되서 실행하게 되면 render obejct로 바로 바로 변환이 된다. </li>
</ol>
<p>html태그 중 head는 브라우저에서 그려지는 태그가 아니다. 
render object에서 렌더링 할 오브젝트이기에 바뀌지 않는다. </p>
<ul>
<li><code>&lt;html&gt;</code> 과 <code>&lt;body&gt;</code> DOM node 또한 render object 로 구성되는데 이들은 render tree root로써 render view 라고 부른다. 
render tree root에 나머지 Dom node가 render object로 생성되어 추가된다. </li>
</ul>
<ol start="4">
<li>구축한 render tree는 layout한다.
render tree root를 시작으로 최상층 요소로 부터 화면의 왼쪽 위부터 Dom node(html태그)를 그려나가며 배치를 완료 시킨다. </li>
</ol>
<ol start="5">
<li>render tree그리기 
배치가 끝난 렌더 트리는 탐색 후에 해당하는 render object는 paint method를 호출하여 그리게 된다. 해당 역할은 ui백엔드가 담당한다.  </li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[브라우저 렌더링 과정_(1)]]></title>
            <link>https://velog.io/@dabin-lee/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EB%A0%8C%EB%8D%94%EB%A7%81-%EA%B3%BC%EC%A0%951</link>
            <guid>https://velog.io/@dabin-lee/%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EB%A0%8C%EB%8D%94%EB%A7%81-%EA%B3%BC%EC%A0%951</guid>
            <pubDate>Sun, 01 Jan 2023 16:21:00 GMT</pubDate>
            <description><![CDATA[<h1 id="browser-rendering-process">browser rendering process</h1>
<p>브라우저를 구성하는 각각의 elements는 어떤 기능을 하는 것일까?</p>
<p>html, css: 화면을 렌더링을 하는 주된 자료
javascript: html태그를 동적으로 만드는 역할을 하는 프로그래밍 언어</p>
<h2 id="browser-elements">[browser elements]</h2>
<p><img src="https://velog.velcdn.com/images/dabin-lee/post/63d7dd49-3e32-4de2-be8b-9ee7826490ab/image.png" alt=""></p>
<blockquote>
<ol>
<li>사용자 인터페이스 <blockquote>
<p>브라우저창에서 보이는 주소창, 홈, 새로고침, 이전 버튼 등을 말한다. 
사용자 인터페이스는 사용자가 컨트롤 할 수 있는 보여지는 페이지를 말한다. </p>
</blockquote>
</li>
<li>브라우저 엔진<blockquote>
<p>사용자가 주소 표시줄에 url을 입력했을 때 uri를 렌더링 엔진에게 전달해주는 역할을 한다. 
홈을 눌렀을 때 홈페이지로 이동 시켜줄 수 있도록 렌더링 엔진에게 전달하는 역할을 한다. 
자료 저장소로 자료를 찾기위해 전달을 한다. </p>
</blockquote>
</li>
<li>렌더링 엔진<blockquote>
<p>가장 중요함
uri에 해당하는 데이터를 통신, 자바스크립트 해석기, ui백엔드에게 퍼서를 이용해서 html, css를 파싱해서 전달한다. </p>
</blockquote>
</li>
<li>통신<blockquote>
<p>렌더링 엔진으로 부터 서버에게 http요청을 서버에게 응답 받은 데이터를 렌더링 엔진에게 전달한다. </p>
</blockquote>
</li>
<li>자바스크립트 해석기<blockquote>
<p>크롬에서는 v8이라는 자바스크립트 엔진을 사용한다. 
이 엔진을 통해서 html태그를 동적으로 움직일 수 있도록 하는 역할을 하는 자바스크립트를 parsing한다.  </p>
</blockquote>
</li>
<li>ui백엔드<blockquote>
<p>최종적으로 렌더링엔진에서 생성된 렌더트리를 브라우저에 그리는 역할을 담당한다. </p>
</blockquote>
</li>
</ol>
</blockquote>
<hr>
<h2 id="browser-rendering-process-1">[browser rendering process]</h2>
<p>사용자가 주소표시줄에 uri를 입력한 후에 어떻게 브라우저에서 일이 발생하는지?</p>
<p>먼저 사용자가 주소표시줄에 url을 입력하는 순간 브라우저 엔진에게 uri를 전달하고 해당하는 데이터를 자료저장소에서 먼저 찾는다. </p>
<p>서버로 가서 해당하는 데이터를 받아오게 되면 똑같은 데이터를 서버로 부터 계속 받아오는 낭비가 발생한다. 
이러한 낭비를 방지하기 위해서 자료저장소에 자주 받아오는 데이터를 저장(캐싱)한다. 통신까지 굳이 가지 않아도 캐싱 기법을 사용하여 효율적인 렌더링을 사용할 수 있다. </p>
<p>만약 데이터를 처음 받아오는 경우라면 서버로 가서 자료를 받아와야 한다. 
렌더링 엔진에게 사용자가 uri입력값 자체를 전달한다. </p>
<p>[순서]</p>
<p>1) 통신에게 URI입력값을 전달함
2) 통신 레이어는 서버에 요청을 보낸다. 해당하는 주소의 데이터(html, css) 해당하는 데이터를 렌더링 엔진에게 response한다.
3) 데이터를 받은 렌더링 엔진은 자바스크립트 해석기에 보내 parsing을 한다. 
4) 최종적으로 완성된 DOM node는 render object(render tree 구성요소)로 변한다. 
5) render object는 ui백엔드로 전달되어 브라우저 화면을 그리게 된다.</p>
<p><em>Point</em>
통신레이어를 통해 응답받은 데이터(html, css)를 렌더링 엔진이 파싱하는 동시에
javascript해석기는 파싱한 결과인 DOM tree를 조작하게 된다. </p>
<p>자바스크립트에서 해석한 결과 html 태그와 css가 화면을 렌더링을 하는 주된 자료이다. javascript는 고정 된 태그에게 동적인 움직임 조작한다. 
javascript의 조작까지 완료된 후에 render object를 통해 DOM노드가 render tree 구성 요소로 변화된다. UI백엔드를 통해 마지막으로 paint가 실행되고 화면이 그려지게 된다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Composition API]]></title>
            <link>https://velog.io/@dabin-lee/Composition-API</link>
            <guid>https://velog.io/@dabin-lee/Composition-API</guid>
            <pubDate>Sun, 01 Jan 2023 15:00:16 GMT</pubDate>
            <description><![CDATA[<h2 id="반응성">반응성</h2>
<p>setup 함수를 이용하여 데이터를 전달하는 상황일때 만일 반응성을 이용하지 않는다면? 
어떠한 값의 변경이 일어났을 때 해당 데이터를 사용하고 있는 기능, 화면에서 변화를 감지하지 않기에 상태를 유지할 수가 없게 된다.</p>
<hr>
<h3 id="torefs">toRefs()</h3>
<p>만약에 state를 반복해서 사용하는 상황일 경우?
애초에 보낼 때 반응성을 가진 데이터를 속성값을 써서 보내면 된다고 생각할 수 있다. 
하지만 return에 속성 값을 직접 입력하게 된다면 반응성을 잃게 된다.
이런 상황에선 toRefs를 이용할 수 있다. </p>
<pre><code>&lt;template&gt;
    &lt;p&gt;{{ username.name }}&lt;/p&gt;
&lt;/template&gt;

&lt;script&gt;
import { reactive } from &quot;@vue/reactivity&quot;;
export default {
    setup() {
        const username = reactive({
            name: &quot;ninja&quot;,
            age: 3,
        });
        return { username };
    },
};
&lt;/script&gt;</code></pre><ul>
<li>username.name을 여러개 사용해야 한다면?</li>
</ul>
<pre><code>&lt;template&gt;
    &lt;p&gt;{{ username }}&lt;/p&gt;
&lt;/template&gt;

&lt;script&gt;
import { reactive } from &quot;@vue/reactivity&quot;;
export default {
    setup() {
        const username = reactive({
            name: &quot;ninja&quot;,
            age: 3,
        });
        return { username.name };
    },
};
&lt;/script&gt;</code></pre><ul>
<li>username.name을 직접 return 시키면 되지 않나?
정답은 No! 직접 속성값을 전달하게 되면 반응성을 잃게 된다. 
(상태 변화에 대한 감지가 불가능하게 된다는 말)</li>
</ul>
<pre><code>&lt;template&gt;
    &lt;p&gt;{{ name }}&lt;/p&gt;
    &lt;p&gt;{{ age }}&lt;/p&gt;
&lt;/template&gt;

&lt;script&gt;
import { reactive, toRefs } from &quot;@vue/reactivity&quot;;
export default {
    props: [&quot;post&quot;],
    setup() {
        const username = reactive({
            name: &quot;ninja&quot;,
            age: 3,
        });
        return toRefs(username);
    },
};
&lt;/script&gt;</code></pre><hr>
<h3 id="ref">ref</h3>
<p>데이터의 형태를 object가 아닌 단일 값으로 보내고 싶을 때</p>
<pre><code>&lt;template&gt;
        &lt;p&gt;{{ isNotUgly }}&lt;/p&gt;
&lt;/template&gt;

&lt;script&gt;
import { ref } from &quot;@vue/reactivity&quot;;
export default {
    setup() {
        const isHandsome = false;
        const isNotUgly = isHandsome;
        isHandsome = true;
        return { isNotUgly };
    },
};
&lt;/script&gt;</code></pre><p>일반적인 javascript 형태일 경우의 형태로 입력을 했을 때 isHansome의 값이 바뀌어도 isNotUgly값은 바뀌지 않는다. 
vue에서는 isNotUgly값 또한 변화를 기대하기에 반응성을 주입을 시켜줘야한다. </p>
<pre><code>&lt;template&gt;
        &lt;p&gt;{{ isNotUgly }}&lt;/p&gt;
&lt;/template&gt;

&lt;script&gt;
import { ref } from &quot;@vue/reactivity&quot;;
export default {
    setup() {
        const isHandsome = ref(false);
        const isNotUgly = isHandsome;
        isHandsome.value = true;
        return { isNotUgly };
    },
};
&lt;/script&gt;</code></pre><p>단일의 값인 경우는 ref를 이용할 수 있다. 
더 다양한 데이터는 reative를 사용해서 보낼 수 있다. </p>
<hr>
<h3 id="reactive">reactive({})</h3>
<pre><code>&lt;template&gt;
        &lt;p&gt;{{ isHandsome }}&lt;/p&gt;
        &lt;p&gt;{{ username.name }}&lt;/p&gt;
&lt;/template&gt;

&lt;script&gt;
import { reactive, ref, toRefs } from &quot;@vue/reactivity&quot;;
export default {
    setup() {
        const isHandsome = ref(false);
        const username = reactive({
            name: &quot;ninja&quot;,
            age: 3,
        });
        return { isHandsome, username };
    },
};
&lt;/script&gt;</code></pre><h3 id="torefs-1">toRefs()</h3>
<p>object형태로 값을 보내기때문에 스프레드 연산자를 이용해서 전달해야한다. </p>
<pre><code>&lt;template&gt;
        &lt;p&gt;{{ name }}&lt;/p&gt;
&lt;/template&gt;

&lt;script&gt;
import { reactive, toRefs } from &quot;@vue/reactivity&quot;;
export default {
    setup() {
        const username = reactive({
            name: &quot;ninja&quot;,
            age: 3,
        });
        return { ...toRefs(username) };
    },
};
&lt;/script&gt;</code></pre><hr>
<pre><code>&lt;template&gt;

    &lt;div&gt;{{ username }}&lt;/div&gt;
    &lt;input type=&quot;text&quot; v-model=&quot;username&quot; /&gt;
    &lt;button @click=&quot;changeName&quot;&gt;이름 변경&lt;/button&gt;

    &lt;div&gt;제품명: {{ name }}, 가격: {{ price }}&lt;/div&gt;
    &lt;button @click=&quot;changeProduct&quot;&gt;제품 변경&lt;/button&gt;

&lt;/template&gt;

&lt;script&gt;
import { reactive, ref, toRefs } from &quot;@vue/reactivity&quot;;
export default {
    setup() {
        const username = ref(&quot;scalp&quot;);
        function changeName() {
            username.value = &quot;messi&quot;;
        }

        const productList = reactive({
            name: &quot;tv&quot;,
            price: 1000,
        });
        function changeProduct() {
            (productList.name = &quot;세탁기&quot;), (productList.price = 5000);
        }

        return {
            username,
            changeName,
            ...toRefs(productList),
            changeProduct,
        };
    },
};
&lt;/script&gt;</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[vue beiginner]]></title>
            <link>https://velog.io/@dabin-lee/vue-beiginner</link>
            <guid>https://velog.io/@dabin-lee/vue-beiginner</guid>
            <pubDate>Sat, 24 Dec 2022 16:06:25 GMT</pubDate>
            <description><![CDATA[<h4 id="template-refs">template refs</h4>
<p>javascript로 dom을 핸들링 할 경우</p>
<pre><code>&lt;template&gt;
    &lt;h1&gt;{{ title }}&lt;/h1&gt;
    &lt;input type=&quot;text&quot; ref=&quot;boo&quot; /&gt;
    &lt;button @click=&quot;handleClick&quot;&gt;click me&lt;/button&gt;
    &lt;Modal /&gt;
&lt;/template&gt;

export default {
    name: &quot;App&quot;,
    components: { Modal },
    data() {
        return {
            title: &quot;My First Vue App&quot;,
        };
    },
    methods: {
        handleClick() {
            console.log(this.$refs.boo);
            this.$refs.boo.classList.add(&quot;active&quot;);
            this.$refs.boo.focus();
        },
    },
};
&lt;/script&gt;
</code></pre><hr>
<h3 id="style의-scoped--global">style의 scoped &amp; global</h3>
<pre><code>&lt;style scoped&gt;</code></pre><ul>
<li><code>&lt;style&gt;</code> 태그가 scoped 속성을 가지고있을 때, CSS는 현재 컴포넌트의 엘리먼트에만 적용된다.</li>
</ul>
<p>하위 컴포넌트는 상위 css와 하위 css 영향을 받는다. 
상, 하위가 동일시 되어있는 지정된 style을 사용하게 되면 성능저하를 가져올 수 있다. 그렇기 때문에 클래스 또는 id선택자를 사용하여 느려지지 않게 이슈를 해결할 수 있다. </p>
<p>만일 공통되는 속성이 있다면 global style파일을 만들어서 index에 import사용하는 것을 추천!</p>
<h3 id="components-props">components Props</h3>
<h3 id="event-활용">event 활용</h3>
<h4 id="emitting-custom-event">emitting custom event</h4>
<ul>
<li>부모에게 이벤트를 전달한다. <pre><code>//App.vue
</code></pre></li>
</ul>
<div v-if="showModal">
    <Modal
        :header="header"
        :text="text"
        theme="sale"
        @close="toggleModal"
    />
</div>


<p>//Modal.vue
<template>
    &lt;div class=&quot;backdrop&quot; @click.self=&quot;closeModal&quot;&gt;
        <div class="modal" :class="{ sale: theme === 'sale' }">
            <h1>{{ header }}</h1>
            <p>{{ text }}</p>
        </div>
    </div>
</template></p>
<script>
export default {
    props: ["header", "text", "theme"],
    methods: {
        closeModal() {
            this.$emit("close");
        },
    },
};
</script>
<pre><code>- modal.js안에서 만들어진 클릭이벤트는 this.$emit(&quot;close&quot;)를 부모로 전달한다. 
- 전달받은 close emit은 @close=&quot;toggleModal&quot;로 받는다. 


#### click event modifiers</code></pre><p>//app.vue (부모)</p>
<!-- click event modifiers -->
<!-- <button @click="toggleModal">open Modal</button> -->
<!-- <button @click.alt="toggleModal">open Modal</button> -->
<p>&lt;button @click.shift=&quot;toggleModal&quot;&gt;open Modal</button></p>
<p>//Modal.vue
<template>
    <!-- 내부에는 이벤트 먹히지 않게하기 -->
    &lt;div class=&quot;backdrop&quot; @click.self=&quot;closeModal&quot;&gt;
    <!-- <div class="backdrop" @click="closeModal"> -->
        ...
    </div>
</template></p>
<pre><code>
### slots
</code></pre><p>//app.vue
<template></p>
<div v-if="showModal">
    <Modal theme="sale" @close="toggleModal">
        <template v-slot:links>
              <a href="#">sign up now</a>
              <a href="#">more info</a>
        </template>
        <h1>Ninja Givaway</h1>
        <p>Grab yout ninja swag for half price!</p>
    </Modal>
</div>
</template>



<p>//modal.vue
<template>
  &lt;div class=&quot;backdrop&quot; @click.self=&quot;closeModal&quot;&gt;
    <div class="modal" :class="{ sale: theme === 'sale' }">
          <slot> default slot</slot></p>
<pre><code>      &lt;div class=&quot;action&quot;&gt;
          &lt;slot name=&quot;links&quot;&gt;&lt;/slot&gt;
      &lt;/div&gt;
  &lt;/div&gt;</code></pre>  </div>
</template>
```



<h3 id="using-teleport">using teleport</h3>
]]></description>
        </item>
        <item>
            <title><![CDATA[vue3 기초]]></title>
            <link>https://velog.io/@dabin-lee/vue3-%EA%B8%B0%EC%B4%88</link>
            <guid>https://velog.io/@dabin-lee/vue3-%EA%B8%B0%EC%B4%88</guid>
            <pubDate>Thu, 22 Dec 2022 13:15:43 GMT</pubDate>
            <description><![CDATA[<p>vue components를 통해 여러 화면에서 재사용이 가능하다.</p>
<p>해당 위젯을 뷰 구성 요소 자체에 있다.</p>
<p>웹사이트를 누르면 서버에 요청을 처리하고 httml페이지를 response받는다.
해당 페이지 안에서 또다른 요청이 있을 때
vue는 전체 웹 사이트의 으흐름을 제어하지 않는다. 
<img src="https://velog.velcdn.com/images/dabin-lee/post/ac799bbf-6ccc-4725-9b78-bff129341839/image.png" alt=""></p>
<p>v-model</p>
<h3 id="기본-활용">기본 활용</h3>
<pre><code>//index.html
&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
    &lt;head&gt;
        &lt;meta charset=&quot;UTF-8&quot; /&gt;
        &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot; /&gt;
        &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&gt;
        &lt;title&gt;Document&lt;/title&gt;
        &lt;!-- vue 3 --&gt;
        &lt;script src=&quot;https://unpkg.com/vue@3.0.2&quot;&gt;&lt;/script&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;h1&gt;Hello vue&lt;/h1&gt;
        &lt;div id=&quot;app&quot;&gt;
            &lt;p&gt;{{title}} - {{author}} - {{age}}&lt;/p&gt;
            &lt;button v-on:click=&quot;age++&quot;&gt;Increase&lt;/button&gt;
            &lt;button v-on:click=&quot;age--&quot;&gt;Decrease&lt;/button&gt;
            &lt;!-- &lt;div @click=&quot;title =&#39;something else&#39;&quot;&gt;change book title&lt;/div&gt; --&gt;
            &lt;div @click=&quot;changeTitle&quot;&gt;change book title&lt;/div&gt;
        &lt;/div&gt;
        &lt;script src=&quot;./app.js&quot;&gt;&lt;/script&gt;
    &lt;/body&gt;
&lt;/html&gt;
</code></pre><pre><code>//app.js
const app = Vue.createApp({
    data() {
        return {
            title: &quot;the final empire&quot;,
            author: &quot;brandon sanderson&quot;,
            age: 45,
        };
    },
    methods: {
        changeTitle() {
            this.title = &quot;words of Randiance&quot;;
        },
    },
});

app.mount(&quot;#app&quot;);
</code></pre><h3 id="조건부-렌더링">조건부 렌더링</h3>
<pre><code>//index.html

    &lt;body&gt;
        &lt;h1&gt;Hello vue&lt;/h1&gt;
        &lt;div id=&quot;app&quot;&gt;
            &lt;div v-if=&quot;showBooks&quot;&gt;
                &lt;p&gt;{{title}} - {{author}}&lt;/p&gt;
                &lt;div&gt;{{age}}&lt;/div&gt;
            &lt;/div&gt;

            &lt;!-- &lt;button @click=&quot;changeHideBooks&quot;&gt;Control book&lt;/button&gt; --&gt;
            &lt;button @click=&quot;changeHideBooks&quot;&gt;
                &lt;span v-if=&quot;showBooks&quot;&gt;Hide Books&lt;/span&gt;
                &lt;!-- &lt;span v-if=&quot;!showBooks&quot;&gt;Show Books&lt;/span&gt; --&gt;
                &lt;span v-else&gt;Show Books&lt;/span&gt;
            &lt;/button&gt;

            &lt;button v-show=&quot;showBooks&quot;&gt;currently show books&lt;/button&gt;
        &lt;/div&gt;
        &lt;script src=&quot;./app.js&quot;&gt;&lt;/script&gt;
    &lt;/body&gt;</code></pre><pre><code>//app.js
const app = Vue.createApp({
    data() {
        return {
            title: &quot;the final empire&quot;,
            author: &quot;brandon sanderson&quot;,
            age: 45,
            showBooks: true,
        };
    },
    methods: {
        changeTitle() {
            this.title = &quot;words of Randiance&quot;;
        },
        changeHideBooks() {
            this.showBooks = !this.showBooks;
        },
    },
});

app.mount(&quot;#app&quot;);
</code></pre><ul>
<li><p>v-if는 DOM 영역에 실제로 그려주지 않는 방법을 사용하고 이와 달리 v-show는 단지 css style의 display: none을 사용하여 보여주지만 않는다는 차이가 있다</p>
</li>
<li><p>dom에서 무언가를 제거하는 것은 css를 사용하여 숨기고 표시하는 것보다 성능 측면에서 더 많은 시간과 비용이 소모된다.</p>
</li>
</ul>
<hr>
<h3 id="other-mouse-event">other mouse event</h3>
<pre><code>//index.html
&lt;style&gt;
    .box {
        display: inline-block;
        margin: 20px;
        padding: 100px 0;
        width: 400px;
        text-align: center;
        background-color: #ddd;
    }
&lt;/style&gt;

    &lt;body&gt;
        &lt;h1&gt;Hello vue&lt;/h1&gt;
            &lt;div class=&quot;box&quot; @mouseover=&quot;handleEvent($event, 5)&quot;&gt;mouseOver&lt;/div&gt;
            &lt;div class=&quot;box&quot; @mouseleave=&quot;handleEvent&quot;&gt;mouseLeave&lt;/div&gt;
            &lt;div class=&quot;box&quot; @dblclick=&quot;handleEvent&quot;&gt;double click&lt;/div&gt;
            &lt;div class=&quot;box&quot; @mousemove=&quot;handleMouseMove&quot;&gt;
                position - {{x}} {{y}}
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;script src=&quot;./app.js&quot;&gt;&lt;/script&gt;
    &lt;/body&gt;</code></pre><pre><code>//app.js
const app = Vue.createApp({
    data() {
        return {
            x: 0,
            y: 0,
        };
    },
    methods: {
        handleEvent(event, data) {
            console.log(event, event.type);
            if (data) {
                console.log(data);
            }
        },
        handleMouseMove(e) {
            this.x = e.offsetX;
            this.y = e.offsetY;
        },
    },
});

app.mount(&quot;#app&quot;);
</code></pre><h3 id="v-for">v-for</h3>
<pre><code>//index.html
&lt;div v-if=&quot;showBooks&quot;&gt;
  &lt;ul&gt;
      &lt;li v-for=&quot;book in books&quot;&gt;
          &lt;h3&gt;{{book.title}}&lt;/h3&gt;
          &lt;p&gt;{{book.author}}&lt;/p&gt;
      &lt;/li&gt;
  &lt;/ul&gt;
&lt;/div&gt;</code></pre><pre><code>
//app.js
const app = Vue.createApp({
    data() {
        return {
            showBooks: true,
            books: [
                {
                    userId: 1,
                    title: &quot;AAAAA&quot;,
                    author: &quot;as23&quot;,
                    age: &quot;123456&quot;,
                },
                {
                    userId: 2,
                    title: &quot;BBBB&quot;,
                    author: &quot;h5jdd&quot;,
                    age: &quot;123456&quot;,
                },
                {
                    userId: 3,
                    title: &quot;CCCCC&quot;,
                    author: &quot;2dhbs&quot;,
                    age: &quot;33333333&quot;,
                },
                {
                    userId: 4,
                    title: &quot;DDDDD&quot;,
                    author: &quot;bacasd&quot;,
                    age: &quot;222222222&quot;,
                },
                {
                    userId: 5,
                    title: &quot;EEEEE&quot;,
                    author: &quot;asdfasdf&quot;,
                    age: &quot;111111111&quot;,
               }
            ],
        };
    },
    methods: {
        toggleShowBooks() {
            this.showBooks = !this.showBooks;
        },
    },
});

app.mount(&quot;#app&quot;);
</code></pre><h3 id="속성-데이터-바인딩">속성 데이터 바인딩</h3>
<h3 id="dynamic-class">Dynamic class</h3>
<ul>
<li>미리 클래스 이름에 스타일을 지정해 놓고 조건에 맞추어 해당 클래스 적용을 시킬 때 사용한다. </li>
</ul>
<p>방법1. 객체</p>
<pre><code>v-bind: calss  = &quot;{클래스 이름: 조건}&quot;
or
:calss = &quot;{클래스 이름: 조건}&quot;</code></pre><pre><code>//index.html
 &lt;div v-if=&quot;showBooks&quot;&gt;
  &lt;ul&gt;
    &lt;li
        v-for=&quot;book in books&quot;
        :class=&quot;{fav : book.isFav}&quot;
        @click=&quot;toggleFev(book)&quot;
    &gt;
        &lt;img :src=&quot;book.img&quot; :alt=&quot;book.title&quot; /&gt;
        &lt;h3&gt;{{book.title}}&lt;/h3&gt;
        &lt;p&gt;{{book.author}}&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
&lt;/div&gt;
</code></pre><h3 id="computed-property">computed property</h3>
<pre><code>//index.html
&lt;li v-for=&quot;book in filterdBooks&quot;
  :class=&quot;{fav : book.isFav}&quot;
  @click=&quot;toggleFev(book)&quot;
&gt;
  &lt;img :src=&quot;book.img&quot; :alt=&quot;book.title&quot; /&gt;
  &lt;h3&gt;{{book.title}}&lt;/h3&gt;
  &lt;p&gt;{{book.author}}&lt;/p&gt;
&lt;/li&gt;


//app.js
computed: {
    filterdBooks() {
        return this.books.filter((book) =&gt; book.isFav);
    },
},</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[연산자를 이용한 타입 정의]]></title>
            <link>https://velog.io/@dabin-lee/%EC%97%B0%EC%82%B0%EC%9E%90%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%ED%83%80%EC%9E%85-%EC%A0%95%EC%9D%98</link>
            <guid>https://velog.io/@dabin-lee/%EC%97%B0%EC%82%B0%EC%9E%90%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%ED%83%80%EC%9E%85-%EC%A0%95%EC%9D%98</guid>
            <pubDate>Mon, 05 Dec 2022 18:45:28 GMT</pubDate>
            <description><![CDATA[<h2 id="union-type">union type</h2>
<p><code>|를 사용한다.</code></p>
<pre><code>function logMessage( value:any) {
    console.log(value)
}

logMessage(&#39;hello&#39;)
logMessage(100)
logMessage(false)</code></pre><p>any를 사용하면 어떤 타입이건 넣을 수 있다.
하지만 이렇게 사용하면 타입을 사용하는 의미를 잃게 된다. </p>
<pre><code>function logMessage( value:string | number) {
    console.log(value)
}

logMessage(&#39;hello&#39;)
logMessage(100)</code></pre><p><code>||</code> or연산자를 썼을 때의 파이프를 하나 추가해준다. 
문자열과 숫자 모두 동일하게 받아서 사용할 수 있게 된다.</p>
<h3 id="장점">장점</h3>
<p>타입 가드: 특정 타입으로 타입의 범위를 좁혀나가는 과정(필터링시켜줌)</p>
<ul>
<li>타입 구분 후 api를 쉽게 접근할 수 있다. </li>
</ul>
<pre><code>var soso: string | number | boolean
const logMessage2 = (value: string | number) =&gt; {
    if( typeof value === &#39;number&#39;){
        value.toLocaleString()
        // number에 대한 속성을 바로 쓸 수 있다.
    }
    if(typeof value ==== &#39;string&#39;){
        // string에 대한 속성을 쓸 수 있다. 
    }
    //string과 number가 아닌 타입에 대해 에러를 만들 수 있다.
    throw new TypeError(&#39;value must be string or number&#39;)
}
logMessage(&#39;hello&#39;)
logMessage(222)
</code></pre><p>특정 변수나 파라미터에 한가지 이상의 타입을 사용하고 싶을 때 활용한다. 
위 코드처럼 매개변수로 string과 number를 받고 있다. </p>
<p><img src="https://velog.velcdn.com/images/dabin-lee/post/083494a9-81c1-4791-8527-361f3b5f6c62/image.png" alt=""></p>
<p>if문안의 value는 해당 타입에 적용되는 메서드들이 표시가 된다. 
any를 사용할 경우는 (타입스크립트가 추론하는 시점에) 타입을 정의할 수 없어서 api나 속성들을 볼 수가 없다. </p>
<p>그 외에 string과 number가 아닌 타입에 대해서는 에러를 발생시킬 수 있다. </p>
<h3 id="특징">특징</h3>
<p>유니온 타입은 인터페이스를 여러개 뒀을 때 공통된 속성만 접근할 수 있다. </p>
<pre><code>interface Developer{
    name: string,
    skill: string
}
interface Person{
    name: string,
    age: number
}
const who = (someone:Developer | Person) =&gt; {
    someone.name
    someone.skill //사용불가
    someone.age //사용불가
    }</code></pre><p><img src="https://velog.velcdn.com/images/dabin-lee/post/735e5155-1176-4ab9-84d1-bae55a6e6d53/image.png" alt=""></p>
<p>someone.을 치면 name만 표시가 된다.
왜일까?
타입스크립트 관점에서는 someone이 어떤 값이 올지 모른다.
타입 검증도 없이 사용하게 된다면 코드상의 에러가 날 수 있다고 추측하게 된다.
그래서 interface나 type처럼 여러개의 구조체를 사용할 경우에는 보장된 공통된 속성만을 사용할 수 있게 해놨다. </p>
<p>그렇다면, age와 skill을 사용하고 싶을 땐 어떻게 해야할까?
유니온 타입의 장점에서 살펴봤던 타입가드를 활용해서 특정 타입으로 제한을 둔 후의 로직을 활용해야 한다. </p>
<hr>
<h2 id="intersection-type">Intersection type</h2>
<p>&#39;&amp;&#39; 연산자 인터섹션 타입</p>
<ul>
<li>타입을 모두 만족하는 타입</li>
</ul>
<p><img src="https://velog.velcdn.com/images/dabin-lee/post/51fd3044-7a80-4215-b302-c87cd13ffa40/image.png" alt="">
사진처럼 타입이 never로 표시된다.
string이면서 number이면서 boolean인 하나의 타입을 말한다.</p>
<p>[함수 내부에서 사용할 때]
<img src="https://velog.velcdn.com/images/dabin-lee/post/e7a091d3-ffbb-4f65-88a4-c3d8522a8d47/image.png" alt="">
유니온 타입을 사용했을 때 구조체를 여러개 사용했을 경우에는 공통 속성외 속성을 사용하기 위해선 타입에 대한 추가적인 처리가 필요했었다. 
하지만, 인터섹션타입을 활용한 함수안에서 someone.을 치게되면 name, skill, age 모두 사용할 수 있게 표시가 된다. </p>
<h3 id="인터섹션과-유니온타입의-차이점">인터섹션과 유니온타입의 차이점</h3>
<p>함수를 호출할 때 두가지의 차이점을 확인하자.</p>
<pre><code>//union

interface Developer{
    name: string,
    skill: string
}
interface Person{
    name: string,
    age: number
}

function askSomeone(value: Developer | Person){

}

askSomeone({name:&#39;빈&#39;, skill: &#39;typescript&#39;}) //Developer타입
askSomeone({name:&#39;므앙고&#39;, age:  100}) //Person타입</code></pre><p>유니온 타입을 사용한 함수는 호출 시에 인자 값으로 Developer 이거나 Person을 주면 된다. </p>
<pre><code>//intersection

interface Developer{
    name: string,
    skill: string
}
interface Person{
    name: string,
    age: number
}


function askSomeone(value: Developer &amp; Person){

}
askSomeone({name:&#39;빈&#39;, skill: &#39;typescript&#39;}) //에러
askSomeone({name:&#39;빈&#39;, skill: &#39;typescript&#39;, age:  100})</code></pre><p>인터셉션을 사용한 함수의 호출 관점으로 보면
Developer &amp; Person 속성을 모두 합한 모습이 인자로 들어와야 한다. 
Developer | Person의 속성을 모두 합친 하나의 타입이라고 보면 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[ts 함수 매개변수의 속성 타입]]></title>
            <link>https://velog.io/@dabin-lee/ts-%ED%95%A8%EC%88%98-%EB%A7%A4%EA%B0%9C%EB%B3%80%EC%88%98%EC%9D%98-%EC%86%8D%EC%84%B1-%ED%83%80%EC%9E%85</link>
            <guid>https://velog.io/@dabin-lee/ts-%ED%95%A8%EC%88%98-%EB%A7%A4%EA%B0%9C%EB%B3%80%EC%88%98%EC%9D%98-%EC%86%8D%EC%84%B1-%ED%83%80%EC%9E%85</guid>
            <pubDate>Sat, 03 Dec 2022 16:54:16 GMT</pubDate>
            <description><![CDATA[<p>아래 예문에서 함수는 object[]를 타입이다. 
매개변수도 타입을 정의해줬는데, 함수 내부에서 파라미터의 속성의 타입이 없다는 에러를 발견했을 때의 오류는 어떻게 해결해야 할까?</p>
<p>parameter의 타입이 object일 때</p>
<pre><code>let todoItems: object[];

function completeTodo(index:number, todo:object):void {
  todo.done = true;
  todoItems.splice(index, 1, todo);
}
</code></pre><p><img src="https://velog.velcdn.com/images/dabin-lee/post/0c9a4e0b-07c0-4d9c-8348-17858d173376/image.png" alt=""></p>
<p>todo.done의 부분 done이 빨간 밑줄이 생기는데 속성의 타입이 없다고 한다. 
이럴때는 객체의 형태를 구체적으로 타입을 정의해줘야 한다. </p>
<pre><code>let todoItems: {{id:number, title: string, done: boolean}}[];

function completeTodo(index:number, todo?:{id:number, title: string, done: boolean}):void {
  todo.done = true;
  todoItems.splice(index, 1, todo);
}
</code></pre><p>이제 중복되는 코드 부분을 정리해보자 </p>
<pre><code>let todoItems: {
  id:number
  title: string
  done: boolean
}[];</code></pre><p>todoItems의 object부분은 다른 함수에서도 매개변수나 반환값으로도 쓰이고 있다. 
이럴때는 타입별칭을 사용해서 타입을 먼저 선언해 준다. </p>
<pre><code>type Todo = {
  id:number
  title: string
  done: boolean
}


let todoItems: Todo[];</code></pre><p>Todo를 선언하고 사용하는 곳에 Todo라고 타입을 지정해주면 된다. 
또는 interface를 사용한다. </p>
<p>interface는 타입스크립트에서 가장 핵심이다. 
이제 Todo라는 값을 이용해서 중복되는 부분을 줄여나갈 수 있다. </p>
<hr>
<h2 id="interface란">interface란?</h2>
<p>인터페이스는 반복되는 타입에 대해서 하나의 interface를 만들고 사용한다. 동일한 규칙을 쓰겠다는 상호간의 약속을 의미한다. </p>
<ul>
<li>객체의 스펙(속성과 속성의 타입)</li>
<li>함수의 파라미터</li>
<li>함수의 스펙(파라미터, 반환 타입)</li>
<li>배열과 객체를 접근하는 방식</li>
<li>클래스</li>
</ul>
<p><strong>[객체 속성과 속성의 타입]</strong></p>
<pre><code>// bin라는 변수는 User라는 interface를 받는다. 
// interface User는 객체의 스펙이 정의됨

interface User{
    name: string
    age: number
}

const bin : User = {
    age: 33,
    name: &#39;비니&#39;
}</code></pre><ul>
<li>함수의 파라미터에 인터페이스 활용
함수의 인터페이스를 활용할 때 파라미터에 정의를 확인하고
함수 호출시 인자가 파라미터에 정의한 인터페이스의 규칙에도 맞는지 확인한다.</li>
</ul>
<p>아래 getUser()함수는 User의 인터페이스를 가져야한다.</p>
<pre><code>function getUser(user:User) {
    console.log(user)
}


// const capt = {
//     name: &#39;bin&#39;
// } error
const capt = {
    name: &#39;bin&#39;,
    age: 33
}
getUser(capt)</code></pre><p><strong>[함수 구조에 인터페이스 활용]</strong></p>
<p>함수의 전체적인 모습도 인터페이스로 정의할 수 있다. </p>
<pre><code>const sumFun ={
    (a: number, a:number):number
}


//화살표 함수로 사용했을 때
const sum = (a, b):sumFun =&gt; {
    return a + b
}

//or
let sum: sumFun
sum = function(a, b){
    return a + b
}

// 위 함수는 이런 모습이다.
sum = function(a:number, b:number):number{
    return a + b
}</code></pre><p><strong>[배열에 인터페이스 활용]</strong></p>
<ul>
<li>인덱싱 방식을 정의하는 인터페이스<pre><code>interface stringArr {
  [index: number]: string
  // 배열에 접근하는 방식
}
</code></pre></li>
</ul>
<p>const arr:stringArr = [&#39;a&#39;, &#39;b&#39;]
arr[0] = 10 //stringArr에 맞지 않음</p>
<pre><code>stringArr는속성은 이름이 정해져 있지 않다. 
(위에서는 임의로 index라고 넣어줌)


배열에 대한 정의를 []괄호 안에 넣고, 해당 속성의 타입을 지정해 줄 수 있다. 


- 인터페이스 딕셔너리 패턴
인덱싱과 유사하다. 해당 key에 대한 타입과 속성에 대한 타입을 interface에 정의해준다. </code></pre><p>interface StringRegexDictionary{
    [key: string]: RegExp
    // [속성의 이름: 속성 타입]: 
}</p>
<p>// 정규식을 갖고 있는 객체 생성
const obj: StringRegexDictionary = {
    // cssfile: &#39;css&#39; 정규식이 아니기에 error발생 </p>
<pre><code>cssfile: /\.css$/,
jsfile: /\.js$/</code></pre><p>}</p>
<pre><code>
장점?  
obj에 있는 key만 forEach로 돌렸다. value는 모두 string 타입이다. 타입스크립트가 자동으로 추론을 해준다. </code></pre><p>Object.keys(obj).forEach((value) =&gt; {
    console.log(value) //string
})</p>
<pre><code>
만일 객체에 접근했을 때 정해놓은 인터페이스에 어긋날 경우를 바로 캐치할 수 있다.</code></pre><p>obj[&#39;cssfile&#39;] = &#39;a&#39; //RegExp타입이 아니라 오류가 나옴</p>
<pre><code>

**[인터페이스 확장]**

oop, js prototype 마찬가지로 기존의 interface를 확장해서 사용한다는 개념
</code></pre><p>interface Person2 {
    name: string
    age: number
}</p>
<p>// interface Developer {
//     name: string //중복
//     age: number //중복
//     language: string
// }</p>
<p>interface Developer extends Person2{
    language: string
}</p>
<pre><code>- extends 키워드를 사용
확장을 하려고 할 때 이미 다른 인터페이스에 있는 속성이 중복될 경우에는 
갖고 있는 속성과 타입에 대해 상속을 받아서 사용할 수 있다. 



</code></pre><p>const newPerson: Developer = {
    name: &#39;lili&#39;
} //에러 발생</p>
<p>const newPerson: Developer = {
    name: &#39;lili&#39;,
    age: 33,
    language: &#39;ts&#39;
}</p>
<p>```</p>
<ul>
<li>확장한 인터페이스로 객체를 선언
만약 하나의 속성만 사용하게 되면 인터페이스 오류가 나게 된다. 
Person과 Developer의 타입들을 모두 정의 해줘야 하는것이 인터페이스 확장의 규칙이다. </li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[타입스크립트_class]]></title>
            <link>https://velog.io/@dabin-lee/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8class</link>
            <guid>https://velog.io/@dabin-lee/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8class</guid>
            <pubDate>Fri, 18 Nov 2022 07:33:51 GMT</pubDate>
            <description><![CDATA[<p>//class 다루기</p>
<pre><code>class Car {
   color:string
   constructor(color:string){
      this.color = color
   }
   start(){
      console.log(&quot;start&quot;)
   }
}

const bmw = new Car(&quot;red&quot;)</code></pre><ul>
<li>타입스크립트에서 클래스를 작성할때 멤버변수는 미리 선언해줘야한다. </li>
</ul>
<pre><code>class Car {
   //color:string
   constructor(public color:string){
      this.color = color
   }
   start(){
      console.log(&quot;start&quot;)
   }
}

const bmw = new Car(&quot;red&quot;)</code></pre><ul>
<li>멤버변수를 사용하지 않을 때는 readonly나 public 키워드를 적어준다.</li>
</ul>
<h3 id="접근-제한자access-modifier">접근 제한자(access modifier)</h3>
<p>[public, private, protected]</p>
<p>ES6의 클래스는 다른 객체지향언어처럼 접근 제한자를 지원하지 않았다. 
하지만, 타입스크립트는 접근 제한자를 지원한다. </p>
<blockquote>
<p>[public]: 자식 클래스나 클래스 인스턴스 모두 접근 가능하다.
아무것도 표기하지 않고 작성하면 모두 public이다.
[private]: 해당 클래스 내부에서만 접근 가능.
[protected]: 자식 클래스에서 접근 가능, 클래스 인스턴스는 참조 불가</p>
</blockquote>
<p>[public]</p>
<pre><code>//class 다루기

class Car {
   public name: string =&quot;car&quot; //public 명시 안해줘도 됨
   color:string
   constructor(color:string){
      this.color = color
   }
   start(){
      console.log(&quot;start&quot;)
   }
}


//Car 클래스를 상속받은 Bmw
class Bmw extends Car{
   constructor(color:string){
      super(color)
      //super를 호출하지 않으면 에러가 발생한다.
   }
   showName(){
      console.log(super.name) //Car의 name이 public이기에 자식클래스에서 사용해도 문제가 없다.
   }
}


const z4 = new Bmw(&quot;black&quot;)</code></pre><p>[private]</p>
<pre><code>class Car {
   private name: string =&quot;car&quot;
   color:string
   constructor(color:string){
      this.color = color
   }
   start(){
      console.log(&quot;start&quot;)
      console.log(this.name)
   }
}


class Bmw extends Car{
   constructor(color:string){
      super(color)
   }
   showName(){
      console.log(super.name) //에러발생
   }
}

const z4 = new Bmw(&quot;black&quot;)</code></pre><p>private일 경우 자식 클래스에서 name을 사용할 수 없다.</p>
<pre><code>class Car {
   #name: string =&quot;car&quot;
   color:string
   constructor(color:string){
      this.color = color
   }
   start(){
      console.log(&quot;start&quot;)
      console.log(this.#name)
   }
}


class Bmw extends Car{
   constructor(color:string){
      super(color)
   }
   showName(){
      console.log(super.#name) //에러발생
   }
}

const z4 = new Bmw(&quot;black&quot;)</code></pre><p>private대신 #으로 사용할 수 있다. </p>
<p>[protected]</p>
<pre><code>//class 다루기

class Car {
   protected name: string =&quot;car&quot;
   color:string
   constructor(color:string){
      this.color = color
   }
   start(){
      console.log(&quot;start&quot;)
      console.log(this.name)
   }
}


//Car 클래스를 상속받은 Bmw
class Bmw extends Car{
   constructor(color:string){
      super(color)
      //super를 호출하지 않으면 에러가 발생한다.
   }
   showName(){
      console.log(super.name) 
   }
}


const z4 = new Bmw(&quot;black&quot;)
console.log(z4.name) //error발생</code></pre><p>protected는 자식 클래스 내부에서는 참조할 수 있다. 
하지만 클래스 인스턴스에는 참조할 수 없다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[타입스크립트_리터럴, 유니온/교차 타입]]></title>
            <link>https://velog.io/@dabin-lee/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EB%A6%AC%ED%84%B0%EB%9F%B4-%EC%9C%A0%EB%8B%88%EC%98%A8%EA%B5%90%EC%B0%A8-%ED%83%80%EC%9E%85</link>
            <guid>https://velog.io/@dabin-lee/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EB%A6%AC%ED%84%B0%EB%9F%B4-%EC%9C%A0%EB%8B%88%EC%98%A8%EA%B5%90%EC%B0%A8-%ED%83%80%EC%9E%85</guid>
            <pubDate>Wed, 16 Nov 2022 12:56:11 GMT</pubDate>
            <description><![CDATA[<h3 id="literal-types">literal types</h3>
<pre><code>const userName1 = &quot;Bob&quot;
let userName2 = &quot;Tom&quot;</code></pre><p>let은 변할 수 있기에 마우스를 올리면 type이 string으로 표시된다.
const는 변할 수 없는 값이기에 Bob 이외의 값을 가질 수 없다. </p>
<pre><code>let userName2 = &quot;Tom&quot;
let userName2: string | number = &quot;Tom&quot;
userName2 = 3</code></pre><p>userName2은 최초 할당된 값이 string이 였지만 타입이 다른 값을 추가하고 싶다면 type을 선언해준다. </p>
<p>[type을 통해서 enum 비슷한 형태를 만들 수 있다. ]</p>
<ul>
<li>enum: 열거형으로서 비슷한 값들끼리 묶어준다.<pre><code>type Job= &quot;police&quot;  | &quot;developer&quot; | &quot;teacher&quot;
</code></pre></li>
</ul>
<p>interface User{
   name:string,
   job: Job
}</p>
<p>const user: User = {
   name: &quot;Bob&quot;,
   job: &#39;police&#39;
}</p>
<pre><code>---
### 유니온타입
||을 의미한다.
동일한 속성을 타입을 다르게해서 구분할 수 있게 도와주는것이 식별 가능한 유니온 타입이라고 한다. </code></pre><p>interface Car{
   name: &quot;Car&quot;
   color: string
   start(): void
}
interface Mobile{
   name: &quot;mobile&quot;
   color: string
   call(): void
}</p>
<p>function getGift(gift: Car | Mobile){
   console.log(gift.color) //둘다 갖고있기에 color는 나옴
   if(gift.name === &quot;Car&quot;){ //먼저 확인
      gift.start() 
   }else{
     gift.call()
   }
}</p>
<pre><code>gift는 union타입을 사용하여 car 또는 mobile을 받는다. 
동일한 속성의 name을 줬지만 타입을 다르게 줌으로써 두 개의 인터페이스를 구분할 수 있게 된다. 

![](https://velog.velcdn.com/images/dabin-lee/post/a3b33209-f12d-4558-86b6-36e379597c74/image.png)

gift에 마우스를 오버했더니 parameter를 알 수 있다. 

예문에는 if를 사용했지만, 검사해야할 항목이 많아지면 swich를 사용하는게 가속성에 좋다. 



---

### 교차타입
교차타입은 &amp;&amp;를 의미한다.
여러개 타입을 하나로 합쳐주는 역할을 한다.
그래서 필요한 모든 기능을 가진 하나의 타입이 만들어 진다. </code></pre><p>interface Car{
   name: string
   start(): void
}
interface Toy{ 
   name: string
   color: string
   price: number
}</p>
<p>const toyCar: Toy &amp; Car = {
   name: &quot;타요&quot;,
   start(){},
   color: &quot;blue&quot;,
   price: 1000
}</p>
<p>```
toyCar의 속성은 Toy와 Var의 interface를 모두 가지고 있다. 
이럴 경우 두 interface속성을 모두 가지고 있어야한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[타입스크립트_함수]]></title>
            <link>https://velog.io/@dabin-lee/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%ED%95%A8%EC%88%98</link>
            <guid>https://velog.io/@dabin-lee/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%ED%95%A8%EC%88%98</guid>
            <pubDate>Wed, 16 Nov 2022 11:31:33 GMT</pubDate>
            <description><![CDATA[<h3 id="함수">함수</h3>
<pre><code>function add(num1:number, num2:number): void{
    // return num1 + num2 
    console.log(num1 - num2)
}</code></pre><p>return이 없을 땐 void를 입력해준다. </p>
<pre><code>function IsAdult(age:number): boolean{
    return age &gt; 19 
}</code></pre><h4 id="선택적-매개변수">[선택적 매개변수]</h4>
<pre><code>function hello(name:string){
    return `Hello, ${name || &quot;world&quot;}`
}
// name이 있으면 name을 쓰고 없으면 world

const result = hello() //error</code></pre><p>${name || &quot;world&quot;}로 대비한 코드가 있지만 error가 발생한다.
typescript에선 보다 명시적으로 알려줘야 한다. </p>
<p>▼</p>
<pre><code>function hello(name?:string){
    return `Hello, ${name || &quot;world&quot;}`
}
const result = hello() 
const result2 = hellow(&quot;sam&quot;)</code></pre><p>name에 물음표를 붙여서 있어도 되고 없어도 되는 파라미터를 표시한다.
옵셔널이라고 하더라도 타입은 항상 지켜줘야 한다. 
undefined이거나 만약에 있다면 string이여야 한다. </p>
<p>사실 javascript에서 매개변수로 default값을 줄 수가 있다. 
<img src="https://velog.velcdn.com/images/dabin-lee/post/1261f24f-734e-437c-a4c6-843380d2df5f/image.png" alt=""></p>
<p>위에 있는 코드와 같은 모양인것을 확인할 수 있다. </p>
<ul>
<li>이름과 나이를 받아서 문자열을 출력해준다. 
여기서 나이는 optional이다. </li>
</ul>
<pre><code>function hello(name:string, age?:number): string{
   if(age !== undefined){
    return `hello ${name}. you are ${age}`
   }else{
    return `hello ${name}`
   }
}</code></pre><p><em>주의</em>
name앞에 age가 오면 안된다. 
필수 매개변수보다 선택적 매개변수가 앞으로 오게 되면 성립할 수 없는 코드이다. </p>
<pre><code>function hello( age?:number, name:string): string{
   if(age !== undefined){
    return `hello ${name}. you are ${age}`
   }else{
    return `hello ${name}`
   }
}</code></pre><p>만약 age를 앞에두고 옵셔널하게 사용하고 싶다면?</p>
<pre><code>function hello(age:number | undefined, name:string): string{
   if(age !== undefined){
    return `hello ${name}. you are ${age}`
   }else{
    return `hello ${name}`
   }
}

console.log(hello(undefined, &quot;sam&quot;))</code></pre><p>age를 number와  undefined를 받을 수 있게 한다. 
사용시에는 명시적으로 undefined를 나타내줘야 한다.</p>
<hr>
<h4 id="restparameter">[restparameter]</h4>
<p>restparameter는 개수가 매번 바뀔 수 있다. 
전달받은 매개변수를 배열로 나타낼 수 있다. </p>
<pre><code>function add(...num: number[]){
   return num.reduce((result, num) =&gt; result + num, 0)
}

add(1, 2, 3)
add(1, 2, 3,4,5,6,7,8,9,10)</code></pre><hr>
<h4 id="함수에서-this">[함수에서 this]</h4>
<pre><code>interface User {
   name: string
}

const Sam: User = {
   name: &#39;sam&#39;
}

function showName(){
   console.log(this.name)
}

const aa = showName.bind(Sam)
aa()</code></pre><p><code>bind</code> 메소드로 this를 묶어준다. </p>
<p>Sam이라는 객체는 User의 타입을 갖고 있는 상태 
this는 사용하는 방법에 따라 달라지지만, this를 명시해주고 싶을 때는 
bind를 통해서 해당 객체와 묶어준다. </p>
<ul>
<li><p>[만약 this의 type을 정의해주고 싶을 땐?]</p>
<pre><code>function showName2(this:User){
 console.log(this.name)
}</code></pre><p>매개변수 자리에 this와 타입을 같이 입력해주면 된다.</p>
</li>
<li><p>[만일 매개변수가 있을 경우?]</p>
<pre><code>function showName3(this:User, age:number, gender: &#39;m&#39;|&#39;f&#39;){
  console.log(this.name, age, gender)
}
const a = showName3.bind(Sam)
a(30, &#39;f&#39;) // &quot;sam&quot;,  30,  &quot;f&quot; </code></pre></li>
</ul>
<ul>
<li>[함수 오버로드]
전달받은 매개변수의 개수나 타입에 따라 다른 동작을 하도록 하는 것을 의미한다. 
함수위에 똑같은 모습으로 함수를 작성한다. </li>
</ul>
<pre><code>interface User {
   name: string,
   age: number
}


// number 또는 string을 받을 수 있다. 
// 나이가 숫자가 아닐때 string을 반환
// User혹은 string이 반환됨


function join(name:string, age:string):string
function join(name:string, age:number):User
function join(name:string, age:number | string): User | string{
   if(typeof age === &#39;number&#39;){
      return{
         name,
         age
      }
   }else{
      return &quot;나이는 숫자로 입력해주세요.&quot;
   }
}


// age를 number로 받았을때 User로 받을 수 있다. 
const sam: User = join(&quot;sam&quot;, 30)

// age가 string일때
const jane: string = join(&quot;jane&quot;, &quot;31&quot;)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[useRef를 이용한 이벤트리스너]]></title>
            <link>https://velog.io/@dabin-lee/useRef%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%9D%B4%EB%B2%A4%ED%8A%B8%EB%A6%AC%EC%8A%A4%EB%84%88</link>
            <guid>https://velog.io/@dabin-lee/useRef%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%9D%B4%EB%B2%A4%ED%8A%B8%EB%A6%AC%EC%8A%A4%EB%84%88</guid>
            <pubDate>Wed, 16 Nov 2022 07:17:33 GMT</pubDate>
            <description><![CDATA[<p>dom을 직접 선택해야 하는 경우들이 있다.</p>
<ol>
<li>엘리먼트의 크기를 가져와야 할 때</li>
<li>스크롤바 위치를 가져와야 할 때</li>
<li>엘리먼트에 포커스를 설정 해줘야 할 때 
등등</li>
</ol>
<p>모달 창 외의 부분을 클릭하게 되면 창이 닫히는 이벤트를 만들어 줄것이다. 
(먼저 modal div에 ref를 설정해준 상태이다)</p>
<pre><code>//main.js

const ref = useRef()
useOnClickOutside(ref, () =&gt; { setModalOpen(false) })</code></pre><p>함수를 하나 만들자. 
useOnclickOutside()함수는 ref와 콜백함수를 매개변수로 받고 있다. </p>
<pre><code>import React, { useEffect } from &#39;react&#39;

export const useOnClickOutside = (ref, handler) =&gt; {
    useEffect(() =&gt; {
        const listener = (event) =&gt; {
            if (!ref.current || ref.current.contains(event.target)) {
                return
            } else {
                handler()
            }
        }
        document.addEventListener(&quot;mousedown&quot;, listener) 
        document.addEventListener(&quot;touchstart&quot;, listener)

        return () =&gt; {
            document.addEventListener(&quot;mousedown&quot;, listener)
            document.addEventListener(&quot;touchstart&quot;, listener)

        };
    }, [ref, handler])
}</code></pre><p>왜 useEffect를 했을까?</p>
<ul>
<li>ref.current의 변화(모달 창 의 안인지? 밖인지?)가 있는것을 감지하고 이벤트를 실행해야 한다.</li>
<li>모달 창의 밖을 클릭한게 맞다면? 콜백 함수를 실행해 줄 것이다. </li>
</ul>
<p>listener의 함수를 살펴보자.
!ref.current : event가 발생했는데 ref가 없을 때
ref.current.contains(event.target): 이벤트의 타겟을 ref가 포함하고 있을 때</p>
<p>(모달 창 안의 내용을 클릭할 때) 이럴 때는 그냥 return을 해준다. </p>
<p>그렇지 않을 경우 handler함수로 이용해 setModatl(false)로 모달 창을 닫아 준다. </p>
<hr>
<p>[useEffect에서 mount, unMount]</p>
<p>useEffect안에서 마운트가 됐던 컴포넌트의 listner는 
return을 통해서 unMount(clear)를 해줘야 한다. </p>
<pre><code>document.addEventListener(&quot;mousedown&quot;, listener) 
document.addEventListener(&quot;touchstart&quot;, listener)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[타입스크립트_기본타입, interface]]></title>
            <link>https://velog.io/@dabin-lee/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8</link>
            <guid>https://velog.io/@dabin-lee/%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8</guid>
            <pubDate>Tue, 15 Nov 2022 18:23:01 GMT</pubDate>
            <description><![CDATA[<h3 id="typescript">Typescript</h3>
<pre><code>function showTimes(arr) {
    arr.forEach(element =&gt; {
        console.log(element)
    });
}

showTimes([1,2,3]) //1, 2, 3
showTimes(1,2,3) //error 발생</code></pre><ul>
<li>Javascript(동적언어): 
실행되는 시점 (런타임)에 타입이 결정되고 오류가 있을 시 그때 처리가 가능하다.</li>
</ul>
<pre><code>function showTimes(arr:number[]) {
    arr.forEach(element =&gt; {
        console.log(element)
    });
}

showTimes([1,2,3]) //1, 2, 3


function showTimes(arr:string[]) {
    arr.forEach(element =&gt; {
        console.log(element)
    });
}

showTimes([1,2,3]) //error</code></pre><ul>
<li>Java, TypeScript(정적언어)
컴파일시에 타입이 결정된다. 
코드 작성때 시간이 길어지지만, 생각하고 코드를 짠다면 안정적인 로직 작성이 가능하다. </li>
</ul>
<p>정리하자면, 타입스크립트를 쓰는 이유?
javascript에 타입이 추가 된 것이다. 그로인해 코드가 올바르게 작동하는지 확인할 수 있다. </p>
<pre><code>[javascript]
function showTime(arr){
    arr.forEach((item) =&gt; {
        console.log(item)
    })
}
showTime([1, 2, 3])
showTime(1, 2, 3)

///1
///2
///3
/// error...

//런타임에서 타입이 결정 됨</code></pre><hr>
<h4 id="첫번째-예제-확인">[첫번째 예제 확인]</h4>
<pre><code>function add(num1:number, num2:number){
    console.log(num1 + num2)
}

//add() 
//add(1)
add(1,2)
//add(&#39;hello&#39;, &#39;world&#39;)</code></pre><p>any타입이였던 인수가 number로 고정이 됐다. 
인수의 개수가 맞지 않은 부분과 string의 함수 호출부분이 에러가 표시가 됐다. </p>
<p>함수를 만들 때 의도했던 방식 외에 다른 방법은 모두 에러라고 나온다. 
몇개의 인수를 어떤 타입으로 전달해야하는지 일일이 코드를 뒤져볼 필요가 없다. </p>
<p>[두번째 예제 확인]</p>
<p><img src="https://velog.velcdn.com/images/dabin-lee/post/2121d4d1-ef59-45dd-ac43-9914e4b1c7af/image.png" alt="">
any: 아무 타입이나 가능하다. 도저히 어떤 타입인지 모를때 사용한다. 하지만 가급적이면 사용을 지양한다. </p>
<p><img src="https://velog.velcdn.com/images/dabin-lee/post/8b9756d3-5cdd-4f6f-b56c-139ec8b1f473/image.png" alt="">
Expected 1 arguments, but got 3
한개의 인수만 필요한데 3개를 받았다는 에러 메세지이다. </p>
<pre><code>function showTime(arr:number[]){
    // arr: number의 배열을 받음
    arr.forEach((item) =&gt; {
        console.log(item)
    })
}
showTime([1, 2, 3])</code></pre><hr>
<h3 id="타입스크립트-기본-타입">타입스크립트 기본 타입</h3>
<h4 id="원시타입">원시타입</h4>
<pre><code>let car:string = &#39;bmw&#39;

let age:number = 30
let isAdult:boolean = true
let a:number[] = [1,2,3]
let a2:Array&lt;number&gt; = [1,2,3]

let week1:string[] = [&#39;mon&#39;, &#39;tue&#39;, &#39;wed&#39;]
let week2:Array&lt;string&gt; = [&#39;mon&#39;, &#39;tue&#39;, &#39;wed&#39;]

week1.push(3)//error

let u:undefined = undefined
let n:null = null</code></pre><ul>
<li>null과 undefined
모든 타입의 하위 타입으로 number 같은 타입에 할당할 수 있다. 
strickNullChecks를 사용하게 되면 null과 undefined는 오직 any타입과 각자 자신타입에만 할당 가능하다. </li>
</ul>
<h4 id="참조타입">참조타입</h4>
<ul>
<li>object, array, tupple</li>
</ul>
<h5 id="object">object</h5>
<p>타입을 지정해주면 지정된 값들만 넣을 수 있게 만들어야 한다. 
타입이 길어질경우 가독성이 많이 떨어지기에 보통 타입을 따로 지정해서 사용한다. </p>
<ul>
<li>각 타입 지정 방법: Type Alias &amp; Interface, index signature <ul>
<li>type alias: type을 따로 만들어서 사용한다.</li>
<li>index signature: [key: string] key값에 어떤 타입이 명세될줄 모르나 전부 string으로 타입을 지정하는 방식이다.</li>
<li>union type: string | number 처럼 여러 타입을 같이 선언한다</li>
</ul>
</li>
</ul>
<pre><code>[기본형]
let obj: {
    name: string;
    age: number;
} = {
    name: &#39;dabin&#39;,
    age: 22
}</code></pre><pre><code>[type alias]
type myType = {
    name: string;
    age: number;
}
let obj: myTpye = {
    name: &#39;dabin&#39;,
    age: 22
}</code></pre><pre><code>[Type Alias + Index Signature + Union Type]
type myType = {
    [key: string]: string | number
}
let obj: myTpye = {
    name: &#39;dabin&#39;,
    age: 22
}</code></pre><h5 id="array">Array</h5>
<p>javascript처럼 자유롭지 못하다. </p>
<pre><code>[기본형]
let a:number[] = [1, 3, 5]
let b:Array&lt;number&gt; = [4, 6, 7]
let week1:string[] = [&#39;mon&#39;, &#39;tue&#39;, &#39;wed&#39; ]
let week2:Array&lt;string&gt; = [&#39;mon&#39;, &#39;tue&#39;, &#39;wed&#39; ]

week1.push(&#39;4&#39;)</code></pre><p>[배열 다중 타입]</p>
<pre><code>let arr: (string|number)[] = [1, &#39;1&#39;]</code></pre><h5 id="튜플tuple">튜플(Tuple)</h5>
<p>배열과 비슷하다. 인덱스별로 타입이 다를 때 사용한다. </p>
<pre><code>//배열의 첫번째 요소에는 string 두번째 요소에는 number을 입력할 수 있음]
let ab:[string, number];
ab = [&#39;z&#39;, 1]
ab = [1, &#39;z&#39;] //error

// 문자 =&gt; 숫자 toLowerCase
b[0].toLocaleLowerCase()
b[1].toLocaleLowerCase() //number에는 toLocaleLowerCase가 없음


// void, never</code></pre><h5 id="void-never">void, never</h5>
<pre><code>function sayHello():void{
    console.log(&#39;hello&#39;)
}</code></pre><ul>
<li>void는 함수에서 아무것도 반환하지 않을 때 주로 사용한다. </li>
</ul>
<pre><code>function showError():never{
    throw new Error()
}

function infLoop():never{
   while (true){
    //do something...
   }
}</code></pre><ul>
<li>never는 영원히 끝나지 않는 함수나, 항상 에러를 반환하는 함수에서 사용할 수 있다. </li>
</ul>
<h5 id="enum">enum</h5>
<p>열거형으로서 비슷한 값들끼리 묶어준다.
특정값만 입력하게 강조하고 싶을 때 또한 그 값들이 공통점이 있을 때 사용한다.</p>
<pre><code>enum Os{
    Window,
    Ios,
    Android
}</code></pre><p>각각 수동으로 값을 할당하지 않으면 자동으로 0부터 1씩 증가되며 값이 할당된다.</p>
<pre><code>enum Os{
    Window,
    Ios = 3,
    Android
}</code></pre><p><img src="https://velog.velcdn.com/images/dabin-lee/post/f0be1985-92ee-4fd8-8b00-2cf582fd88b5/image.png" alt=""></p>
<p>값을 할당하게 되면 해당 값부터 차례대로 값이 출력되는걸 알 수 있다. 
window는 건너뛰고 값을주면 window는 0이된다. </p>
<pre><code>console.log(Os[3]) // [LOG]: &quot;Ios&quot;
console.log(Os[&#39;Ios&#39;]) // [LOG]: 10</code></pre><p>컴파일된 결과를 보면 OS라는 객체가 생성되고 양방향으로 내편되어 있다. 
enum에는 숫자가 아닌 물자도 입력이 가능한데, 이럴때는 양방향이 아닌 단방향 맵핑만된다. </p>
<pre><code>enum Os{
    Window = &#39;win&#39;,
    Ios = &#39;ios&#39;,
    Android = &#39;and&#39;
}

// 숫자가 아닌 문자열도 입력할 수 있다. 이럴 경우 단방향 맵핑만 된다. 

 let myOs : Os;
 //myOs에는 Os에 있는 window, ios, Android만 사용할 수 있게 된다. 

 myOs = Os.Window //특정값만 입력할 수 있게 할 수 있다. </code></pre><hr>
<h3 id="인터페이스interface">인터페이스(interface)</h3>
<pre><code>let user:object;

user = {
    name: &#39;xx&#39;,
    age: 30
}

console.log(user.name)
//콘솔에 있는 name부분이 빨간줄이 그어진다. </code></pre><p>object에는 특정 속성값에 대한 정보가 없기때문에 에러가 발생한다. </p>
<pre><code>interface User{
    name: string,
    age: number,
}

let user2: User={
    name: &#39;xx&#39;,
    age: 30
}

user2.age = 10 

console.log(user2.age)</code></pre><p>interface를 통해서 객체의 property를 정의해서 표현해준다. </p>
<h5 id="property-추가">[property 추가]</h5>
<pre><code>interface User{
    name: string,
    age: number,
    gender? : string,
}
</code></pre><p>있어도되고 없어도 되는 속성을 추가하고 싶을 때는 optional chaining 사용한다.
(* optional chaining: 프로퍼티가 없는 중첩 객체를 에러 없이 안전하게 접근할 수 있다. )키 값뒤에 <code>?</code> 를 넣어준다. </p>
<pre><code>interface User{
    name:string,
    age:number,
    gender?: string // 입력을 해도 되고 안해도 되는 속성은 optional을 사용한다.
}

let user: User = {
    name: &#39;xxx&#39;,
    age: 30
}

user.age = 10
user.gender = &quot;male&quot; //error

console.log(user.name) </code></pre><p>만약 gender라는 property가 있을 때는 무조건 string이여야 한다. </p>
<h5 id="readonly">[readOnly]</h5>
<p>값에 접근할 수 는 있지만 수정은 불가능하다. </p>
<pre><code>interface User{
    gender? : string,
    readonly birthYear: number
}

let user2: User={
    age: 30,
    birthYear: 2000
}

user2.birthYear = 1999
console.log(user2.birthYear)</code></pre><p><img src="https://velog.velcdn.com/images/dabin-lee/post/35048831-672e-45ad-85d9-7efaf5506bce/image.png" alt="">
<code>user2.birthYear = 1999</code>를 입력하면 에러가 뜨게된다.
읽기 전용 속성이라 수정할 수 없다. </p>
<h5 id="문자열-index-속성">[문자열 index 속성]</h5>
<pre><code>interface User{
    gender? : string,
    age: number,
    readonly birthYear: number
    1? : string;
    2? : string;
    3? : string;
    4? : string;
}

let user2: User={
    age: 30,
    birthYear: 2000,
    1: &#39;A&#39;
}
</code></pre><p>학년별로 interface를 정의해줄 수 있다. 
하지만, 1학년만 쓰고 싶을 때는 optional chaining 쓸 수 바께 없다.</p>
<p>이럴 경우에 문자열 index 속성을 사용하면 된다. </p>
<pre><code>type Score = &#39;A&#39; | &#39;B&#39; | &#39;C&#39; | &#39;F&#39;

interface User{
    gender? : string,
    age: number,
    readonly birthYear: number
    //[grade: number] : string
    [grade: number] : Score
}

let user2: User={
    age: 30,
    birthYear: 2000,
    1: &#39;s&#39;,  //에러발생 
    2: &#39;B&#39;
}


// 학년별로 interface를 정의해줄 수 있다. 
</code></pre><p><code>[grade: number] : string</code> 학년은 숫자로 받음 : 학점은 string
number를 key로 하고 string value로 받는 property를 여러개 만들 수 있다.  </p>
<p>만약 학점을 String으로 받기에는 범위가 너무 넓은데 이럴 때 사용할 수 있는게
문자 리터럴이다. </p>
<p><code>type Score = &#39;A&#39; | &#39;B&#39; | &#39;C&#39; | &#39;F&#39;</code></p>
<p>이렇게 추가하게 되면 Score의 입력값만을 사용할 수 있다. </p>
<h5 id="interface로-함수-정의">[interface로 함수 정의]</h5>
<pre><code>interface Add {
    // (num1: number, num2: number): void
    (num1: number, num2: number): number
}

const add : Add = function(x, y){
    return x + y
}
add(10, 20)


interface IsAdult {
    (age:number): boolean
}

const a:IsAdult = (age) =&gt; {
    return age &gt; 10
}

console.log(a(7)) //false
console.log(a(11)) //true </code></pre><h5 id="interface로-클래스-정의">[interface로 클래스 정의]</h5>
<ul>
<li>[implements]<pre><code>interface Car{
  color: string
  wheels: number
  start(): void
}
</code></pre></li>
</ul>
<p>class Bmw implements Car {
    color = &quot;red&quot;
    wheels = 4
}</p>
<pre><code>- error가 발생한다. 속성값을 모두 입력해야 하기때문에 start()의 함수도 같이 정의를 해줘야 한다. 

</code></pre><p>interface Car{
    color: string
    wheels: number
    start(): void
}</p>
<p>class Bmw implements Car {
    color = &quot;red&quot;
    wheels = 4
    start(){
        console.log(&#39;gogogo&#39;)
    }
}</p>
<pre><code>
- [constructor 이용]</code></pre><p>interface Car{
    color: string
    wheels: number
    start(): void
}</p>
<p>class Bmw implements Car {
    color
    wheels = 4
    // 생성될 때 색상을 입력 받기
    constructor(c:string){
        this.color = c
    }
    start(){
        console.log(&#39;gogogo&#39;)
    }
}</p>
<p>const b = new Bmw(&#39;blue&#39;)
console.log(b)
//[LOG]: Bmw: {
  &quot;wheels&quot;: 4,
  &quot;color&quot;: &quot;blue&quot;
} </p>
<pre><code>
- [extends]
인터페이스는 확장이 가능하다. 그럴 때 extends 키워드를 사용한다. </code></pre><p>interface Car{
    color: string
    wheels: number
    start(): void
}</p>
<p>interface Benz extends Car{
    door:number
    stop(): void
}</p>
<p>const benz : Benz = {
    door: 5,
    stop(){
        console.log(&#39;benz!&#39;)
    }
}
console.log(benz.stop())
//에러가 발생된다.</p>
<pre><code>
왜 에러가 발생될까?
interface확장 전 Car의 속성들도 모두 입력을 해야한다.</code></pre><p>interface Car{
    color: string
    wheels: number
    start(): void
}</p>
<p>interface Benz extends Car{
    door:number
    stop(): void
}</p>
<p>const benz : Benz = {
    door: 5,
    stop(){
        console.log(&#39;benz!&#39;)
    },
    color:&quot;blank&quot;,
    wheels: 4,
    start(){
        console.log(&#39;gogo&#39;)
    }
}
console.log(benz.stop)</p>
<pre><code>
참고로 확장은 여러개를 할 수 있다. 
동시 확장이 가능하다.
</code></pre><p>interface Car{
    color: string
    wheels: number
    start(): void
}</p>
<p>interface Toy {
    name: string
}</p>
<p>interface ToyCar extends Car, Toy{
    price: number
}</p>
<pre><code></code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[useLocation 쿼리 파라미터 얻기]]></title>
            <link>https://velog.io/@dabin-lee/useLocation-%EC%BF%BC%EB%A6%AC-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0-%EC%96%BB%EA%B8%B0</link>
            <guid>https://velog.io/@dabin-lee/useLocation-%EC%BF%BC%EB%A6%AC-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0-%EC%96%BB%EA%B8%B0</guid>
            <pubDate>Tue, 15 Nov 2022 16:34:34 GMT</pubDate>
            <description><![CDATA[<p>검색페이지 구현 등에서 유용하게 쓰일 수 있는 react router Hook이다. </p>
<p>get 방식 중 하나인 <code>https://경로?키=값</code> 형태로 접속할 때,
react에서 쿼리 스트링을 얻는 방법</p>
<ul>
<li>먼저 주소 문자열 요청 파라미터는
protocol/ host/ query string으로 구분된다. </li>
</ul>
<p><a href="https://ooeunz.tistory.com/43">참고</a>
<img src="https://velog.velcdn.com/images/dabin-lee/post/dbf479ea-a3f1-4fe0-b2be-abc7b16fe0c0/image.png" alt=""></p>
<ol>
<li>useNavigate()로 파라미터를 전달하자<pre><code>const navigate = useNavigate()
const [inputValue, setInputValue] = useState(&#39;&#39;)
const [handleShow, setHandleShow] = useState(false)
</code></pre></li>
</ol>
<p>const handleInput = (e) =&gt; {
    setInputValue(e.target.value)
    navigate(<code>/search?q=${e.target.value}</code>)
}</p>
<pre><code>- navigate로 함수를 얻는다. 
`navigate(&#39;/이동경로, {state: 키: 값, ...}&#39;)`

2. useLocation()로 이동한 페이지에서 파마리터를 취득하자
useLocation()을 콘솔에 찍어보면 search속성이 있음.
해당 부분을 가지고 객체를 만든다. 
`hash
: 
&quot;&quot;
key
: 
&quot;4ch1eqpl&quot;
pathname
: 
&quot;/search&quot;
search
: 
&quot;?q=&quot;
state
: 
null`

3. URLSearchParams 객체 생성
</code></pre><p>const useQuery = () =&gt; {
    return new URLSearchParams(useLocation().search)
}
let query = useQuery()
const searchTerm = query.get(&#39;q&#39;)</p>
<pre><code>- URLSearchParams객체.get(&quot;키&quot;) 로 원하는 쿼리를 취득할 수 있다. 
( 내가 만든 location의 search 키 값은 q로 되어있음 )
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[useDebounce custom Hooks]]></title>
            <link>https://velog.io/@dabin-lee/useDebounce-custom-Hooks</link>
            <guid>https://velog.io/@dabin-lee/useDebounce-custom-Hooks</guid>
            <pubDate>Tue, 15 Nov 2022 14:42:46 GMT</pubDate>
            <description><![CDATA[<p>useDebounce가 왜 필요한지?</p>
<p>debounce function은 사용자가 미리 결정된 시간 동안 타이핑을 멈출 때까지 keyup 이벤트의 처리를 지연시킨다. </p>
<p>입력된 모든 문자를 처리할 경우 성능이 저하되고 불필요한 로드가 추가될 수 있기에 이를 방지할 수 있다. </p>
<p>useDebounce를 사용하게 된다면 ui 코드가 모든 이벤트를 처리할 필요가 없고 서버로 전송되는 api 호출 수도 크게 줄어들게 된다. </p>
<p>즉, input창에서 검색을 입력할 때 글자 하나하나 마다 이벤트가 처리되는 것을 방지 할 수 있다. 계속 요청이 들어가는 것은 퍼포먼스에 안좋은 영향을 끼치게 된다. </p>
<p>debounce를 이용해서 타이핑시에는 요청이 가지 않도록 하고, 타이핑이 모두 끝났을 때 요청을 하게 한다. 
타이핑 시간에 대한 이벤트 동작을 지연을 시킴으로써 성능에 도움을 줄 수 있다. </p>
<pre><code>useDebounce.js

import { useState, useEffect } from &#39;react&#39;

export const useDebounce = (value, delay) =&gt; {
    const [debounceValue, setDebounceValue] = useState(value)
    useEffect(() =&gt; {
        const handler = setTimeout(() =&gt; {
            setDebounceValue(value)
        }, delay)
        return () =&gt; {
            clearTimeout(handler)
        }
    }, [value, delay])
    return debounceValue
}</code></pre><p>setDebounceValue(value)로 검색어를 치게 되면 delay시간이 지나고 난 후에 debounceValue로 들어가게 된다. </p>
<p>delay시간이 지나가기 전에 타이핑을 더 하게 된다면, 
setTimeout이 없어지게 되고 다시 setTimeout이 실행하게 된다. </p>
<p>1을 입력을 했을 때 0.5초가 지나면 setDebounceValue에 들어가지만
0.5초가 되기 전에 <code>2</code>를 더 타이핑 하면 clearTimeout이 되어서 
기존에 1만 입력했던 setTimeout은 사라지게 된다.</p>
<p>즉 1이 .5초가 지나기 전 연달아 2가 입력되면 다시 0.5초가 지나고 나서  setDebounceValue에 들어가진다. </p>
<p>기존에는 1이 입력되면 .5초가 지나고 나서 호출이 바로 되지만
.5초가 지나기 전에 2를 입력하면 delay가 다시 없어지고 2가 넣어진 후 다시 .5초가 호출이 된다. </p>
<pre><code>index.js

const searchTerm = query.get(&#39;q&#39;)
const debounceSearchTerm = useDebounce(searchTerm, 500)

useEffect(() =&gt; {
    if (debounceSearchTerm) {
        fetchSearchMovie(debounceSearchTerm)
    }
}, [debounceSearchTerm]) //처음 렌더링 후 searchTerm이 변경될 때마다 렌더링
const fetchSearchMovie = async (searchTerm) =&gt; {
    try {
        const request = await axios.get(
            `/search/multi?include_adult=false&amp;query=${searchTerm}`
        )
        setSearchResults(request.data.results)
    } catch (err) {
        console.log(&#39;err&#39;, err)
    }
}</code></pre><p>query.get(&#39;q&#39;)는 검색하고자 하는 value를 넣어준다. 
500의 시간이 지나고 나면 호출이 될 수 있게 함.</p>
<p>이제 검색창에 입력을 하게 되면, 
해당 검색에 대한 입력이 0.5초가 지나고 나서 통신이 호출되는 것을 확인할 수 있다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[callback, promise, async/await]]></title>
            <link>https://velog.io/@dabin-lee/Temp-Title</link>
            <guid>https://velog.io/@dabin-lee/Temp-Title</guid>
            <pubDate>Tue, 01 Nov 2022 16:46:51 GMT</pubDate>
            <description><![CDATA[<p><strong>콜백함수는 다른 코드의 인자로 넘겨주는 함수이다.</strong>
js에서는 나중에 실행하는 함수이기에 콜백함수라고 불린다.</p>
<blockquote>
<p>다른 함수의 인자로 넘길 수 있고 
리턴 값으로 함수를 쓸 수 있고
변수에 함수를 넣을 수도 있다.
그래서 js에서는 함수를 일급객체라고 한다.</p>
</blockquote>
<p>콜백을 받는 함수가 어떻게 동작하느냐에 따라서 동기적, 비동기적일 수 도 있다.
콜백은 나중에 실행하라고 인자를 다른 함수에게 넘겨주고 제어권도 함께 위임한다.
그래서 콜백을 받은 함수는 역할에 따라 받은 콜백을 실행한다.
콜백을 큐에 넣는지 바로 콜 스택에서 실행하는 역할인지에 따라 동기인지 비동기인지 구별이 된다. </p>
<p>[콜백은 &#39;함수&#39;이다.]
콜백함수는 함수이기 때문에 콜백함수로 어떤 객체의 메서드를 전달하더라도 그 메서드는 메서드가 아닌 함수로서 호출된다.</p>
<p>[콜백함수의 특징]
다른함수(A)의 인자로 콜백함수(B)를 전달하면 A가 B의 제어권을 갖게된다.</p>
<ul>
<li>특별한 요청(bind)이 없는 한 A에 미리 정해놓은 방식에 따라 B를 호출한다. </li>
</ul>
<p>미리 정해놓은 방식?</p>
<p>1) 어떤 시점에 콜백을 호출할지
2) 인자에는 어떤 값들을 지정할 지
3) this에는 무엇을 바인딩할지 등이다. </p>
<p>규칙에 따라 정확히 호출하지 않으면 원하는 결과를 얻을 수 없게 된다.</p>
<pre><code>var arr = [1, 2, 3, 4, 5]
var obj = {
    vals: [1, 2, 3],
    logvalues: function (v, i) {
        if (this.vals) {
            console.log(this.vals, v, i)
        } else {
            console.log(this, v, i)
        }
    }
}

obj.logvalues(1, 2) //객체 메소드로 넘겨짐
console.log(arr.forEach(obj.logvalues)) //콜백함수로 넘겨짐

호출은 forEach가 한다. 
obj.logvalues가 가리키고 있는 함수만 떼어서 전달을 한것이다.
그걸 받은 forEach역시 함수로써 호출을 하게 된것.
그렇다면 this는 obj가 아닌 전역객체가 담기게 된다. 

//window {...} 1 0
//window {...} 2 1
//window {...} 3 2
//window {...} 4 3

그렇다면 obj를 this로 사용하고 싶으면?
arr.forEach(obj.logvalues.bind(obj)) //bind
arr.forEach(obj.logvalues, obj) //thisArg</code></pre><hr>
<p><strong>비동기 연산을 다루는 promise</strong> 
=&gt; 콜백지옥을 벗어날 수 있다.
(비동기처리는 &#39;특정 코드의 실행이 완료될 때까지 기다리지 않고 다음 코드를 먼저 수행)</p>
<p><strong>[Producer]</strong></p>
<pre><code>const f1 = () =&gt; {
    return new Promise((res, rej) =&gt; {
        setTimeout(() =&gt; {
            res(&#39;1번 주문 완료&#39;)
        }, 1000);
    })
}
</code></pre><p>promise는 class이기에 new키워드를 이용해서 object를 생성한다.
프라미스의 생성자는 excutor라는 콜백함수를 전달해준다. 
이때 콜백함수는 콜백함수reject, resolve를 받는다. </p>
<p>실행이 잘 성공했는지 혹은 실패했는지 그럼 성공(resolve) or 실패(reject)의 결과 값이 무엇인지 위 세가지 내용을 미래(비동기 작업이 종료된 후)에 then과 catch를 통해 반환해주겠다고 약속해주는 객체이다.</p>
<p>resolve와 reject는 자바스크립트가 자체적으로 제공하는 콜백이기 때문에 개발자는 이 콜백 함수들을 신경 쓰지 않고 excutor 안의 코드만 작성하면 된다.</p>
<p><strong>[Promise 상태]</strong>
프라미스가 만들어지고 종료 될때까지의 상태를 말한다. 
Promise의 비동기 작업이 종료된 후, 비동기 처리가 완료 되지 않았다면 Pending, 완료 되었다면 Fulfilled, 실패하거나 오류가 발생하였다면 Rejected 상태를 갖는다.</p>
<p><strong>[consumer]</strong>
producer가 제공하는 정보를 기다렸다가 then, catch, finally를 이용해서 값을 받아온다. </p>
<ul>
<li>resolve는 then을 통해 결과값을 받아서 처리가 가능하고,</li>
<li>reject는 catch를 통해 결과값을 받아서 처리가 가능하다. </li>
<li>finally는 Promise가 resolve를 사용하거나 reject를 사용해도 상관없이 무조건 실행된다.</li>
</ul>
<p><strong>[promise와 콜백 함수 방식 다시 살펴보기]</strong>
callback은 콜백은 비동기 로직의 처리 결과를 위해서 callback안에서만 처리를 해야한다. callback 밖에서는 비동기에서 온 값을 알 수가 없다.
callback의 문제점은 함수의 중첩이 많아질 수록 가독성이 나빠진다. </p>
<p>promise는 그 자체로 비동기 처리를 위한 객체이다. 
excutor 로직이 실행이 된 후 then과 catch로 값을 받아오는게 잘 정의가 되어있지만, template이 존재하지 않기때문에 코드가 복잡해지고 에러 처리같은 작업들이 어렵다는 단점이 있다. 
promise도 콜백헬과 마찬가지로 then()지옥이 있다. </p>
<hr>
<p><code>Synchronous(동기) Vs Asynchronous(비동기)</code></p>
<p><strong>async / await</strong></p>
<p>callback과 promise의 단점을 해소하고자 탄생된 async await
asyn-await은 함수형 컴포넌트에 문법적으로 더 잘맞게 되어있다.
프로미스를 조금 더 간결하고 간편하고 그러고 동기적으로 실행되는 것처럼 보이게 만들어준다. </p>
<p>[어떻게 쓰는건지 이해해보자]
서버와의 통신은 요청과 응답의 순서가 보장되지 않기 때문에 비동기 통신이다.
서버에 요청을하면 순차적으로 처리가 완료됨에 따라 응답이 제공된다.</p>
<p>서버에 요청을 한 후 기다리지 않고 다음 행동을 하게 된다.
ex) 서버에게 db를 가져와 tem_data에 저장을 요청 =&gt;  해당 값을 보여달라고 요청</p>
<p>이 경우에 서버는 요청만 하고 클라이언트는 서버에게 해당 값을 요청했지만, 받지 못하고 바로 다음 코드가 실행이 되게 된다.</p>
<p>이럴 때 async를 사용한다. </p>
<p>async 함수는 서버에 요청하는 경우 서버로부터 실행 계획을 Promise를 통해 답을 받는 구조로 진행이 된다. 
하지만 Promise를 받아도 다음 코드가 실행이 되게 되는데, 이때 await을 사용해서 promise의 답을 받고 다음실행을 한다. </p>
<p>async함수 =&gt; promise반환 =&gt; await로 promise를 기다림 =&gt; 완료</p>
<p>이처럼 서버 응답을 기다린 후 코드를 순차적으로 진행할 수 있게된다. </p>
<p><a href="https://velog.io/@fxoco/%EC%84%9C%EB%B2%84-%EA%B8%B0%EB%8B%A4%EB%A0%A4%EC%A3%BC%EA%B8%B0-async-await">참고 벨로그</a></p>
<p>[중요 사용 방법]</p>
<blockquote>
<p>await 는 async 함수 안에서만 동작한다.
async/await 은 Promise 와는 다르게 에러를 핸들링 할 수 있는 기능이 없다. 따라서 try-catch() 문을 활용하여 에러를 핸들링 하여 주어야 한다.</p>
</blockquote>
<p>1) 비동기 작업 앞에 await 키워드를 붙여준다.
그러면 메인작업들은 멈추지 않고 await을 가지고 있는 비동기 작업이 결과를 낼 때까지 기다린다.</p>
<pre><code>await 서버에요청
완료후 실행 할 함수</code></pre><p>2) try-catch로 예외를 처리</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React.memo, useCallback(), useMemo()]]></title>
            <link>https://velog.io/@dabin-lee/React.memo-useCallback-useMemo</link>
            <guid>https://velog.io/@dabin-lee/React.memo-useCallback-useMemo</guid>
            <pubDate>Tue, 25 Oct 2022 15:37:29 GMT</pubDate>
            <description><![CDATA[<p>리액트는 컴포넌트를 렌더링 한 뒤에 이전 결과와 비교하여 DOM을 업데이트 한다. </p>
<p>컴포넌트를 나누는것은 재사용성을 위해서 이지만, 컴포넌트의 렌더링 최적화를 위해서 이기도 하다. </p>
<p>결국 렌더링이 최대한 덜 될 수 있있게 해야하는데, 이때 리 렌더링이 필요없는 컴포넌트의 렌더링을 방지하여 성능을 높일 수 있는 방법들이 있다. </p>
<hr>
<h3 id="reactmemo">React.memo</h3>
<p><strong>컴포넌트 렌더링 최적화</strong></p>
<p>리 렌더링을 방지하여 컴포넌트의 리렌더링 성능 최적화를 해줄 수 있다.</p>
<p>예를들어 todoList 리액트 앱에서
todo list form영역에서 input에 입력을 하게 된다면,</p>
<p>value를 가지고 있는 부분만 렌더링이 아닌 연관이 없는 컴포넌트까지 같이 렌더링이 되는것을 확인할 수 있다.</p>
<p>이러한 부분은 react.memo를 적용해서
컴포넌트에서 리렌더링이 필요한 상황에서만 리렌더링을 하도록 설정해 문제를 해결할 수 있다.
사용방법:  적용을 해야하는 컴포넌트를 memo로 감싸주면 된다.</p>
<pre><code>import React from &#39;react&#39;
import List from &#39;./List&#39;

const Lists = React.memo(({ todoData, setTodoData }) =&gt; {

    return (
        &lt;div&gt;
            {todoData.map((data) =&gt; (
                &lt;div key={data.id}&gt;
                    &lt;List
                        key={data.id}
                        id={data.id}
                        title={data.title}
                        completed={data.completed}
                        todoData={todoData}
                        setTodoData={setTodoData}
                    /&gt;
                &lt;/div&gt;
            ))}
        &lt;/div&gt;
    )
})

export default Lists</code></pre><p>React.memo는 HOC이다. 
HOC(Higher-Order Components) HOC란 컴포넌트를 인자로 받아서 새로운 컴포넌트를 return해주는 구조의 함수</p>
<p>HOC는 리액트의 API가 아닌, 리액트 컴포넌트를 구성하는데 일종의 패턴이다.</p>
<p>리액트는 참조 값만 비교하는 Shallow Copy(얕은 복사)를 실행한다. 
즉 state가 변경될 때 shallow copy를 통해 같은 값인지 판단하고 렌더링 여부를 결정한다. </p>
<hr>
<h3 id="usecallback">useCallback()</h3>
<p><strong>함수 최적화</strong></p>
<p>컴포넌트가 렌더링 될 때 그 안에 있는 함수도 다시 그리게 된다.
  컴포넌트가 리 렌더링 될때마다 똑같은 함수를 계속 만드는 것은 좋은 현상이 아니다.</p>
<p>  만약 함수가 자식 컴포넌트에 props로 내려 줄 경우 함수를 포함하고 있는 컴포넌트가 리 렌더링 될 때마다 자식 컴포넌트의 props로 받은 함수도 새롭게 만들어지기 때문에 계속 리 렌더링을 하게 된다. </p>
<p>이러한 문제를 해결하기 위해 useCallback()을 사용한다.</p>
<pre><code>const handleClick = useCallback((id=&gt; {
  let newTodoData = todoData.filter((data) =&gt; data.id !== id)
  setTodoData(newTodoData)
  localStorage.setItem(&quot;todoData&quot;, JSON.stringify(newTodoData))
},[todoData])</code></pre><p>useCallback안에 콜백함수와 의존성배열을 순서대로 입력한다.
함수 내에서 참조하는 state, props가 있다면 의존성 배열에 추가한다.</p>
<p>todoData가 useCallback으로 인해 새로 생성되지 않기에 메모리에 새로 할당되지 않고 동일한 참조 값을 사용하게 된다.</p>
<p>의존성 배열에 아무것도 없다면 컴포넌트가 최초 렌더링 시에만 함수가 생성되고, 그 이후에는 동일한 참조값을 사용하는 함수가 된다. </p>
<hr>
<h3 id="usememo">useMemo()</h3>
<p><strong>결과 값 최적화하기</strong></p>
<blockquote>
<p>Memoization이란?
비용이 많이 드는 함수 호출의 결과를 저장하고 동일한 입력이 발생할때 캐시된 결과를 반환한다. 컴퓨터 속도를 높이는데 주로 사용되는 최적화 기술이다. </p>
</blockquote>
<pre><code>function Component({a, b}){
  const result = compute(a, b)
  return &lt;div&gt;{result}&lt;/div&gt;
}</code></pre><p>component내의 compute함수가 만일 복잡한 연산을 하게 된다면 결과 값을 리턴하는데 오랜시간이 걸리게 된다. </p>
<p>이런 상황에서 컴포넌트가 계속 리 렌더링이 될 경우라면, 성능에 좋지 않은 영향을 미치게 되며 UI지연 현상이 발생하게 된다.</p>
<p>이러한 현상을 해결해주기 위해 useMemo를 사용한다.</p>
<p>compute함수에 넘겨주는 값이 이전과 동일하다면, 컴포넌트가 리 렌더링이 되더라도 연산을 다시 하지 않고 저장해두었던 값을 재활용하게 된다. </p>
<pre><code>function Component({a, b}){
  const result = useMemo(() =&gt; compute(a, b), [a, b])
  return &lt;div&gt;{result}&lt;/div&gt;
}</code></pre><p>useMemo로 감싸 준 후 첫번째 인수에는 의존성 배열 compute 함수에서 사용하는 값을 넣어준다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SSR, CSR]]></title>
            <link>https://velog.io/@dabin-lee/SSR-CSR</link>
            <guid>https://velog.io/@dabin-lee/SSR-CSR</guid>
            <pubDate>Mon, 24 Oct 2022 13:12:14 GMT</pubDate>
            <description><![CDATA[<h3 id="브라우저-렌더링">브라우저 렌더링</h3>
<p>가져온 페이지 정보를 브라우저가 어떻게 출력할까?
브라우저 종류: 익스플로러, 파이어폭스, 사파리, 크롬, 오페라 등
브라우저 주요기능: HTML 페이지, PDF, 이미지등 서버에게 요청해서 보여주는 것 </p>
<p>브라우저가 화면에 나타나는 요소를 렌더링 할 때 렌더링 엔진을 사용한다.
렌더링 엔진을 사용해서 유저가 요청한 컨텐츠를 표시해준다.
EX) 크롬: 블링크, 사파리: 웹킷</p>
<p><img src="https://velog.velcdn.com/images/dabin-lee/post/b896ba5b-c837-494e-998e-93bbdfa45412/image.png" alt=""></p>
<p>HTML회면 요청 =&gt; HTML, CSS 파싱 =&gt; 화면에 표시</p>
<p>파싱이란? 브라우저가 코드를 이해하고 사용하기 쉬운 구조로 변환하는 것이다.
파싱의 결과: DOM트리, NODE 트리로 표현된다. </p>
<p>DOM트리: HTML페이지를 구조화해서 계층으로 표현한 개념으로 최상단<code>&lt;HTML&gt;</code> 루트 태그를 시작으로 페이지에 대한 각 요소가 노드로 만들어진다. 
NODE트리: 트리를 구성하는 객체 하나하나를 뜻한다.</p>
<p>파싱 중 <code>&lt;script&gt;</code>태그를 만나게 되면 파싱이 중단된다.
파일의 크기가 크거나 인터넷이 느리다면 페이지를 보는데 오랜 시간이 오래걸린다.
js에 의존적인 웹 사이트라면 가장 하단에 놓는것이 좋지 않을 수 있다. 
이러한 부분도 고려해봐야 한다. (async, defer에 대해서 알아보는 것을 추천)</p>
<p>DOM + CCOM =&gt; 렌더링 트리 생성 =&gt; view포트 크기에 맞게 레이아웃이 결정됨 =&gt; 마지막 페인팅 단계에서는 눈에 보이는 내용을 픽셀로 변환해서 브라우저에 표시해준다.</p>
<hr>
<h3 id="ssr-csr">SSR, CSR</h3>
<p><strong>SSR(Server Side Rendering)</strong>
<img src="https://velog.velcdn.com/images/dabin-lee/post/71ef2d3c-dce3-4ed7-89de-b27e8e22031c/image.png" alt=""></p>
<blockquote>
<p>서버에서 페이지를 구성하여 반환
불필요한 부분까지 리 렌더링으로 성능 저하 서버 자원 낭비</p>
</blockquote>
<p>서버에서 렌더링 준비를 끝마친 상태로 클라이언트에게 전달하는 방식이다.
서버에서 이미 렌더링 가능한 상태임으로 클라이언트는 JS가 다운되는 동안에도 다른 것을 볼 수 있다.</p>
<p>데이터베이스에서 데이터를 가져온 후 다시 브라우저에 데이터가 그려진다. 
이 방식은 서버에서 데이터까지 모두 포함하여 페이지를 구성한 후 브라우저에 전달한다. </p>
<p>클라이언트가 페이지를 이동하거나 다른 요청이 생길 때마다 이 과정을 반복하기 때문에
화면에서 바뀌지 않은 부분도 계속해서 리 렌더링이 된다는 문제점이 있다. </p>
<p><strong>CSR(Client Side Rendering)</strong>
<img src="https://velog.velcdn.com/images/dabin-lee/post/2be21ede-e37a-41ca-83ff-f91c99de51d4/image.png" alt=""></p>
<blockquote>
<p>서버에서 필요한 부분만 전달 받고 클라이언트에서 렌더링하기 때문에 서버에 부담이 적다.
사용자가 첫 화면을 보기까지 시간이 오래 걸릴 수 있다.
LOW SEO(Search Engine Optimization)에 유용하지 않다.</p>
</blockquote>
<p>react, vue등 SPA에서 쓰이는 방법으로 서버에서 구성했던 SSR과 달리 클라이언트에서 화면을 렌더링 한다. 서버는 요청을 받으면 클라이언트에 HTML, JS를 보내주고 클라이언트는 응답받은 HTML, JS를 렌더링한다.</p>
<p>CRS은 첫 HTML을 빈 화면으로 받고 (데이터를 제외한) 화면을 그리는 코드들이 프론트에서 받아진다. 이때 데이터들은 JS파일에 한번에 번들되어 들어오기 때문에 로딩 속도가 걸린다. 
초기 진입 속도가 SSR에 비해서 느리다. </p>
<p>하지만 초기 진입 후론 필요한 데이터만 갱신하면 되기때문에 SSR에 비해 서버 부하거 덜하다.
첫 화면이 빈HTML이기 때문에 검색엔진 최적화에 취약하다. </p>
<p>( * 초기 진입속도에 대한부분은  Code Splitting 기능으로 해결 할 수 있다. ) </p>
<h4 id="nextjs">Next.js</h4>
<p>SSR, CSR의 문제점을 해결한 방식이다. 
즉 SSR의 불필요한 부분까지 렌더링 되는 점, CSR의 초기 진입속도, SEO에 취약 하다는 점을 보완된 프레임워크이다. </p>
<p>빈 HTML =&gt; 첫 페이지는 서버에서 렌더링을 하여 데이터가 채워진 HTML을 받아 검색엔진 최적화 문제를 해결한다. 
그 이후로 CSR의 방식으로 필요한 부분만 갱신하도록 하여 서버에 부담을 줄이도록 했다. </p>
<hr>
<h3 id="브라우저-저장소">브라우저 저장소</h3>
<p>쿠키, 웹 스토리지, 로컬 스토리지, 세션 스토리지</p>
<p>HTTP통신을 통해서 
클라이언트가 서버에게 requeset를 보내고
서버는 클라이언트에게 requeset에 대한 response를 보내고 접속을 종료한다. </p>
<p>통신이 끝나면 상태 인증에 쓰이는 상태 정보를 유지하지 않는다는 특징이 있다.
서버측에선 자원 낭비를 하지 않는 장점이 있지만,
통신을 할 때마다 새로 연결해줘야 하기 때문에 클라이언트는 그 때마다 인증을 해줘야하는 단점을 갖고있다. </p>
<p>이러한 문제에 사용하는 것이 브라우저의 스토리지이다.
브라우저의 저장공간인데, 쿠키, 웹 스토리지(로컬 스토리지, 세션 스토리지)가 있다.</p>
<p>웹 스토리지는 html5부터 제공되는 저장소이다.
쿠키와 웹 스토리지는 모두 해당 도메인에 대한 데이터를 브라우저에 저장한다.</p>
<p><strong>[쿠키]</strong>
쿠키는 서버가 클라이언트에게 전송하는 작은 데이터 파일이다.
이름, 값, 도메인정보, 경로 정보, 만료 일자와 시간 등이 있다.
모든 브라우저에 전송이 되지만, 매번 서버에 전송이 되고 저장 용량이 작다. 
또 하나 보안에 취약하다는 단점이 있다. </p>
<p><strong>[웹 스토리지]</strong>
HTML5부터는 쿠키의 단점을 보완한 웹 스토리지를 사용한다.
쿠키와 기능은 유사하지만, 클라이언트에 저장만 할 뿐 서버로 전송하지 않는다.
키와 벨류의 값의 형태로 데이터를 저장한다.</p>
<p>로컬 스토리지: 브라우저 자체에 반 영구적으로 저장하고 브라우저를 종료해도 해당 데이터가 유지된다. 
세션 스토리지: 탭 윈도우 단위로 생성이되고 탭을 닫을 때 데이터가 삭제된다.</p>
<p>*로컬 스토리지 사용방법: <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage">https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage</a></p>
<p>*세션 스토리지 사용방법: <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage">https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage</a></p>
<p><a href="https://hianna.tistory.com/697">참고 블로그</a></p>
<p><strong>[어떤 유형의 데이터를 어디에 저장할까?]</strong>
로컬 스토리지: 자동 로그인
세션 스토리지: 입력 폼, 비로그인 장바구니 기능 
쿠키: 다시보지않기 팝업 창</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React Hook Form 유효성 체크]]></title>
            <link>https://velog.io/@dabin-lee/React-Hook-Form-%EC%9C%A0%ED%9A%A8%EC%84%B1-%EC%B2%B4%ED%81%AC</link>
            <guid>https://velog.io/@dabin-lee/React-Hook-Form-%EC%9C%A0%ED%9A%A8%EC%84%B1-%EC%B2%B4%ED%81%AC</guid>
            <pubDate>Sun, 23 Oct 2022 14:08:30 GMT</pubDate>
            <description><![CDATA[<h1 id="📌-react-hook-form">📌 React Hook Form</h1>
<p>모듈 다운 받기
<code>npm install react-hook-form --save</code></p>
<p>useForm import하기
<code>import { UseForm } from &#39;react-hook-form&#39;</code></p>
<h4 id="register">register()</h4>
<pre><code>const { register } = useForm();
{...register(&quot;email&quot;)}</code></pre><p>input 태그 안에 register 함수를 spread해서 넣어줄 수 있다 (ES6 문법)</p>
<h4 id="watch">watch()</h4>
<pre><code>const { watch } = useForm();
console.log(watch(&#39;email&#39;))`</code></pre><p>watch(): 지정된 input을 관찰하고 해당 값을 반환한다. 
관찰 하려는 input 요소에 register를 등록해준다.</p>
<p>*버전 7부터 변경됨 : 
ref={register} =&gt; {...register(&quot;email&quot;)}
콘솔을 확인하면 입력시킨 input에 타이핑되는 값이 watch로 표기가 되는것을 확인할 수 있다. </p>
<h4 id="handlesubmit">handleSubmit()</h4>
<pre><code>const { handleSubmit } = useForm();
const onValid = (data) =&gt; {
        console.log(data);
    }
&lt;form onSubmit={handleSubmit(onValid)}&gt;
...</code></pre><p>handleSubmit은 React Hook Form에서 Submit을 관리하기 위해 만든 함수이다.
react hook form이 모든 validation을 마쳤을때 호출된다.</p>
<p>handleSubmit은 함수를 인자로 받으며 그 함수에 data라는 인자를 넘겨준다. 
한번에 관리할 수 있어서 편리하다.</p>
<hr>
<h3 id="validation-체크하기">validation 체크하기</h3>
<p>required는 input태그에서 사용하는 속성이다.
필수입력사항에는 required속성을 사용한다.</p>
<h4 id="유효성-체크의-조건-넣기">유효성 체크의 조건 넣기</h4>
<pre><code>&lt;input
type=&quot;text&quot;
id=&quot;email&quot;
{...register(&quot;email&quot;,
    {
    required: true,
    pattern: {
            value: /\S+@\S+\.\S+/,
            message: &quot;이메일 형식에 맞지 않습니다.&quot;}
        })
      } 
 /&gt;</code></pre><p>[유효성 검사 속성]
required : 반드시 입력해야 하는지 체크
min : 입력에 허용되는 최소 값
max : 입력에 허용되는 최대 값
minLength : 입력에 허용되는 최소 길이
maxLength : 입력에 허용되는 최대 길이
pattern : 입력에 대한 정규식 패턴
validate: 콜백 함수를 인수로 전달하거나 콜백 함수의 객체를 전달하여 검증</p>
<h4 id="유효성-체크-시-에러문구-설정">유효성 체크 시 에러문구 설정</h4>
<pre><code>const { formState: { errors } } = useForm();
return (
    &lt;div&gt;
        &lt;form onSubmit={handleSubmit(onSubmit)}&gt;
            &lt;label htmlFor=&quot;email&quot;&gt;이메일&lt;/label&gt;
            &lt;input
                ...
            {errors.email &amp;&amp; errors.email.type === &quot;required&quot; &amp;&amp; &lt;p&gt;this email field is required&lt;/p&gt;}
            {errors.email &amp;&amp; errors.email.type === &quot;pattern&quot; &amp;&amp; &lt;p&gt;올바른 이메일을 입력하세요&lt;/p&gt;}</code></pre><p><code>errors.email</code>: error.email의 유효성 체크에 걸렸다면? erros 렌더링 해주기
<code>rrors.email.type === &quot;required&quot;</code>: type이 required인 error.email</p>
<p><code>&lt;span&gt;{errors?.Email?.message}&lt;/span&gt;</code>
? : undefined여도 실행하기 (not required)</p>
<h4 id="errormessage-컴포넌트">ErrorMessage 컴포넌트</h4>
<p><code>npm install @hookform/error-message</code>
유효성 검사에 ErrorMessage를 추가할 수 있다.</p>
<pre><code>  &lt;label&gt;gender&lt;/label&gt;
  &lt;select name=&quot;gender&quot; id=&quot;gender&quot;
      {...register(&quot;gender&quot;, {
          required: true
      })}
  &gt;
      &lt;option value=&quot;&quot;&gt;select...&lt;/option&gt;
      &lt;option value=&quot;male&quot;&gt;male&lt;/option&gt;
      &lt;option value=&quot;female&quot;&gt;female&lt;/option&gt;
  &lt;/select&gt;

  &lt;ErrorMessage
      errors={errors}
      name=&quot;gender&quot;
      render={({ message }) =&gt; &lt;p&gt;{message} 성별을 선택하세요&lt;/p&gt;}
  /&gt;</code></pre><hr>
<h4 id="전체-코드-확인하기">전체 코드 확인하기</h4>
<pre><code>
import { useForm } from &#39;react-hook-form&#39;
import { ErrorMessage } from &#39;@hookform/error-message&#39;

export default function Signup2() {


    const { register, formState: { errors }, handleSubmit } = useForm();
    const onSubmit = data =&gt; console.log(data.required);

    // 필수입력사항에는 required속성을 사용
    return (
        &lt;div&gt;
            &lt;form onSubmit={handleSubmit(onSubmit)}&gt;
                &lt;label htmlFor=&quot;email&quot;&gt;이메일&lt;/label&gt;
                &lt;input
                    type=&quot;text&quot;
                    id=&quot;email&quot;
                    {...register(&quot;email&quot;,
                        {
                            required: true,
                            pattern: /\S+@\S+\.\S+/
                            // pattern: {
                            //     value: /\S+@\S+\.\S+/,
                            //     message: &quot;이메일 형식에 맞지 않습니다.&quot;,
                            // },
                        })
                    } /&gt;
                {errors.email &amp;&amp; errors.email.type === &quot;required&quot; &amp;&amp; &lt;p&gt;this email field is required&lt;/p&gt;}
                {errors.email &amp;&amp; errors.email.type === &quot;pattern&quot; &amp;&amp; &lt;p&gt;올바른 이메일을 입력하세요&lt;/p&gt;}
                {/* email의 유효성 체크에 걸렸다면? erros 렌더링 해주기 
                    유효성 type이 require라면 
                */}

                &lt;label htmlFor=&quot;name&quot;&gt;이름&lt;/label&gt;
                &lt;input type=&quot;text&quot;
                    id=&quot;name&quot;
                    {...register(&quot;name&quot;,
                        {
                            required: true,
                            maxLength: 10
                        })
                    } /&gt;
                {errors.name &amp;&amp; errors.name.type === &quot;required&quot; &amp;&amp; &lt;p&gt;this name field is required&lt;/p&gt;}
                {errors.name &amp;&amp; errors.name.type === &quot;maxLength&quot; &amp;&amp; &lt;p&gt;your input exceed maximun length&lt;/p&gt;}

                &lt;label htmlFor=&quot;pw&quot;&gt;비밀번호&lt;/label&gt;
                &lt;input type=&quot;password&quot;
                    id=&quot;pw&quot;
                    {...register(&quot;pw&quot;,
                        {
                            required: true,
                            minLength: 8
                        })} /&gt;
                {errors.pw &amp;&amp; errors.pw.type === &quot;required&quot; &amp;&amp; &lt;p&gt;this email field is required&lt;/p&gt;}
                {errors.pw &amp;&amp; errors.pw.type === &quot;minLength&quot; &amp;&amp; &lt;p&gt;your input exceed minLength length&lt;/p&gt;}



                &lt;label htmlFor=&quot;pwComfirm&quot;&gt;비밀번호 확인&lt;/label&gt;
                &lt;input type=&quot;password&quot;
                    id=&quot;pwComfirm&quot;
                    {...register(&quot;pwComfirm&quot;)} /&gt;



                &lt;label&gt;gender&lt;/label&gt;
                &lt;select name=&quot;gender&quot; id=&quot;gender&quot;
                    {...register(&quot;gender&quot;, {
                        required: true
                    })}
                &gt;
                    &lt;option value=&quot;&quot;&gt;select...&lt;/option&gt;
                    &lt;option value=&quot;male&quot;&gt;male&lt;/option&gt;
                    &lt;option value=&quot;female&quot;&gt;female&lt;/option&gt;
                &lt;/select&gt;

                &lt;ErrorMessage
                    errors={errors}
                    name=&quot;gender&quot;
                    render={({ message }) =&gt; &lt;p&gt;{message} 성별을 선택하세요&lt;/p&gt;}
                /&gt;


                &lt;button type=&quot;submit&quot;&gt;계정생성&lt;/button&gt;
            &lt;/form&gt;

        &lt;/div&gt;
    )
}
</code></pre>]]></description>
        </item>
    </channel>
</rss>