<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>yhc-key.log</title>
        <link>https://velog.io/</link>
        <description>practice react, javascript</description>
        <lastBuildDate>Mon, 06 May 2024 08:43:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>yhc-key.log</title>
            <url>https://velog.velcdn.com/images/yhc-key/profile/36dae5c6-09f0-4c5a-bf90-0bf17ce2f668/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. yhc-key.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/yhc-key" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[TypeScript - chapter12 / 러닝 타입스크립트]]></title>
            <link>https://velog.io/@yhc-key/TypeScript-chapter12-%EB%9F%AC%EB%8B%9D-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8</link>
            <guid>https://velog.io/@yhc-key/TypeScript-chapter12-%EB%9F%AC%EB%8B%9D-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8</guid>
            <pubDate>Mon, 06 May 2024 08:43:00 GMT</pubDate>
            <description><![CDATA[<h1 id="ide-기능-사용">IDE 기능 사용</h1>
<p>VSCode 기준 설명</p>
<h2 id="코드-탐색">코드 탐색</h2>
<h3 id="정의-찾기">정의 찾기</h3>
<ul>
<li>Go to Definition(F12)는 요청된 이름이 원래 정의된 위치로 즉시 이동</li>
<li>Cmd / Ctrl + 이름을 클릭하면 정의된 곳으로 이동</li>
<li>Peek &gt; Peek Definition(Option(맥) / Alt(윈도우) + F12)는 정의를 보여주는 Peek 상자를 불러옴.</li>
</ul>
<h3 id="참조-찾기">참조 찾기</h3>
<ul>
<li><p>Go to Reference(Shift + F12)를 선택하면 마우스 오른쪽으로 클릭한 이름 바로 아래에 확장 가능한 Peek 상자가 나타나고, 해당 타입 정의 또는 값의 참조 목록을 보여줌. </p>
</li>
<li><p>Find All References( Option / Alt + shift + F12)도 참조 목록을 보여주지만, 코드 탐색 후에도 사이드바 뷰에서 확인 가능. 한 번에 둘 이상의 참조를 열거나 수행에 유용.</p>
</li>
</ul>
<h3 id="구현-찾기">구현 찾기</h3>
<ul>
<li>go to Implementations( Cmd / Ctrl + F12) 와 Find All Implementations 는 인터페이스와 추상 클래스 메서드를 위해 만들어진 Go to Refereces와 Find All References의 특수 버전. 코드에서 인터페이스 또는 추상 메서드의 모든 구현을 찾음.</li>
</ul>
<h2 id="코드-작성">코드 작성</h2>
<p>IDE는 변경 내용을 파일에 저장하기도 전에 사용자가 입력한 파일의 편집 내용을 볼 수 있음.</p>
<h3 id="이름-완성하기">이름 완성하기</h3>
<p>타입스크립트 API 이용하여 동일한 파일에 존재하는 이름을 자동 완성할 수 있음. 
패키지 의존성에 대해서도 자동 가져오기를 제공.</p>
<h3 id="자동-가져오기-업데이트">자동 가져오기 업데이트</h3>
<p>VS Code 파일 탐색기를 사용해 파일을 drag &amp; drop 하거나 중첩된 폴더 경로로 이름을 바꾸면, VS Code에서 타입스크립트를 사용해 파일 경로를 업데이트하도록 제안.</p>
<h3 id="코드-액션">코드 액션</h3>
<p>많은 IDE 유틸리티는 직접 실행할 수 있는 액션으로 제공됨.
편집기에서 코드 액션을 사용할 수 잇다면 VS Code에서는 텍스트 커서 위에 클릭 가능한 전구 아이콘이 나타남.
코드 액션 매뉴를 여는 VS Code의 기본 단축키는 Cmd / Ctrl .</p>
<h4 id="이름-바꾸기">이름 바꾸기</h4>
<ul>
<li>함수, 인터페이스 또는 변수의 이름철머 이미 존재하는 이름을 수동을 변경하는 작업은 상당히 번거롭다.  메뉴의 Rename Symbol(F2) 옵션을 선택하면 새 이름을 입력할 수 있는 텍스트 상자가 나타남. </li>
<li>새 이름 적용 전 Shift + Enter 를 눌러서 발생할 모든 텍스트 ㅂ녀경 사항을 나열하는 리팩토링 미리보기 창을 열 수 있다.</li>
</ul>
<h4 id="사용하지-않는-코드-제거">사용하지 않는 코드 제거</h4>
<p>VS Code는 사용하지 않는 코드의 불투명도를 1/3으로 줄여서 보여줌.</p>
<h4 id="기타-빠른-수정">기타 빠른 수정</h4>
<p>Quick Fix 목록을 확인하는 것이 좋음.</p>
<h4 id="리팩토링">리팩토링</h4>
<p>타입스크립트 언어 서비스는 다양한 코드 구조를 지원하는 여러 편리한 코드 변경 기능을 제공 함. 일부는 코드 줄을 이용하는 것처럼 간단하지만, 새로운 함수를 생성하는 것처럼 복잡한 기능도 있음.
코드 영역을 선택하면 VS Code는 선택된 코드 옆에 전구 아이콘을 보여줌. 리팩토링 가능 목록을 보려면 전구 아이콘을 클릭하면 됨.</p>
<h2 id="오류를-효과적으로-처리하기">오류를 효과적으로 처리하기</h2>
<h3 id="언어-서비스-오류">언어 서비스 오류.</h3>
<p>모든 오류를 코드 아래 빨간 물결선으로 표시함
해당 문자 옆에 오류 텍스트를 담은 호버 상자가 나타남.
동일한 소스 파일에 여러 문제가 잇는 경우에는 View Problem 디스플레이 우측에 있는 위, 아래쪽 화살표로 오류 전환 가능. 오류 이동 단축키는 F8, Shift + F8 사용</p>
<h4 id="problems-탭">PROBLEMS 탭</h4>
<ul>
<li>모든 문제를 나타내는 PROBLEMS 탭이 있음. 탭에서 오류를 클릭하면 텍스트 커서가 문제가 되는 행과 열로 이동함.</li>
<li>VS Code는 현재 열려 있는 파일에 대한 오류만 나열함. 모든 오류를 실시간 업데이트하고 싶다면 터미널에서 타입스크립트 컴파일러를 실행해야 함.</li>
</ul>
<h4 id="터미널-컴파일러-실행">터미널 컴파일러 실행</h4>
<ul>
<li><p>터미널에서 타입스크립트 컴파일러 watch 모드로 실행하는 것이 좋음. 그렇게 하면 모든 오류에 대한 실시간 업데이트 목록이 제공됨</p>
</li>
<li><p>VS Code에서 이 작업을 수행하려면, 터미널 패널을 열고 tsc-w를 실행 or 프로젝트 참조를 사용한다면 tsc -b -w를 실행</p>
</li>
<li><p>Cmd / Ctrl 을 누른 상태에서 파일 이름 클릭시 텍스트 커서가 해당 파일의 잘못된 행과 열로 이동.</p>
</li>
</ul>
<h4 id="타입-이해">타입 이해</h4>
<p>Ctrl 을 누른 상태로 마우스를 가져가면 변수에 대한 호버 정보를 보여줌.
호버 상자는 타입 별칭 같은 타입에도 사용 가능.
복잡한 타입의 구성 요소를 이해할 때 유용한 한 가지 전략은 타입의 한 구성 요소를 나타내는 타입 별칭을 만드는 것.</p>
<pre><code class="language-typescript">ex)
const fruits = {
    apple : 1,
    broccoli: 2,
    cherry: 3,
    };

export type FruitName = keyof typeof fruits;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[TypeScript - chapter11 / 러닝 타입스크립트 ]]></title>
            <link>https://velog.io/@yhc-key/TypeScript-chapter11-%EB%9F%AC%EB%8B%9D-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8</link>
            <guid>https://velog.io/@yhc-key/TypeScript-chapter11-%EB%9F%AC%EB%8B%9D-%ED%83%80%EC%9E%85%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8</guid>
            <pubDate>Wed, 01 May 2024 08:56:43 GMT</pubDate>
            <description><![CDATA[<h2 id="선언-파일">선언 파일</h2>
<p>타입스크립트는 구현과 별도로 타입 형태를 선언할 수 있음. 
타입 선언은 파일 이름이 .d.ts 확장자로 끝나는 <strong>선언 파일</strong>에 작성됩니다.</p>
<h3 id="선언-파일-1">선언 파일</h3>
<p>.d.ts 선언 파일은 런타임 코드 포함 불가 하다는 제약 사항 외에는 거의 .ts와 같음
ex) 사용법</p>
<pre><code class="language-typescript">// types.d.ts
export interface Character {
  catchphrase?: string;
  name: string;
}</code></pre>
<pre><code class="language-typescript">// index.ts
import {Character} from &quot;./types&quot;;

export const character: Character = {
  catchphrase: &quot;Yee-haw!&quot;,
  nae: &quot;Sandy cheeks&quot;,</code></pre>
<p>선언 파일은 값이 아닌 타입만 선언할 수 있는 코드 영역을 의미하는 앰비언트 컨텍스트를 생성합니다.
<em>앰비언트 컨텍스트 : 자바스크립트에서 일반적으로 사용되는 실행 컨텍스트의 하위 개념</em></p>
<h3 id="런타임-값-선언">런타임 값 선언</h3>
<p>선언 파일은 함수 또는 변수 같은 런타임 값 생성 x 이지만, declare 키워드를 사용해 이러한 구조체가 존재한다고 선언할 수 있음
초기값 허용되지 않는다는 점 제외 일반적인 변수 선언과 동일한 구문</p>
<pre><code class="language-typescript">// ex)
// types.d.ts
declare let declared: string; //OK

declare let initializer: string =&quot;Wanda&quot;;
//Error : initializers are not allowed in abient contexts.</code></pre>
<p>함수와 클래스도 비슷하지만 본문이 없음.</p>
<p>declare 키워드를 사용한 타입 선언은 .d.ts 선언 파일에서 사용하는 게 가장 일반적 하지만 선언 파일 외부에서도 사용 가능
전역으로 사용가능한 변수가 해당 파일에서만 사용되어야 하는 경우 declare 키워드가 유용.</p>
<h4 id="전역-변수">전역 변수</h4>
<p>import 또는 export 문이 없는 타입스크립트 파일은 모듈이 아닌 스크립트로 취급.따라서 이를 이용해 타입을 전역으로 선언할 수 있음
다음 globals.d.ts는 전역으로 존재하는 const version: string을 선언.</p>
<pre><code class="language-typescript">// globals.d.ts
declare const version: string;</code></pre>
<pre><code class="language-typescript">//version.ts
export function logVersion() {
  console.log(&#39;Version: ${version}&#39;);
}</code></pre>
<h4 id="전역-인터페이스-병합">전역 인터페이스 병합</h4>
<p>변수는 유일한 전역은 아니며 전역 API와 값에 대한 많은 타입 선언이 전역으로 존재. 인터페이스는 동일한 이름의 다른 인터페이스와 병합되기 때문에 import와 export 문이 없는 .d.ts 선언 파일 같은 전역 스크립트 컨텍스트에서 인터페이스를 선언하면 해당 인터페이스가 전역으로 확장됨.</p>
<pre><code class="language-typescript">// File1.ts
interface Car {
    brand: string;
}

// File2.ts
interface Car {
    model: string;
}

// Main.ts
const myCar: Car = {
    brand: &quot;Toyota&quot;,
    model: &quot;Camry&quot;
};</code></pre>
<h4 id="전역-확장">전역 확장</h4>
<p>declare global 코드 블록 구문을 사용해 해당 블록 내용이 전역 컨텍스트에 있다고 표시.</p>
<h3 id="내장된-선언">내장된 선언</h3>
<p>Array, Function, Map, Set과 같은 전역 객체는 타입 시스템이 알아야 하지만 코드에서 선언되지 않는 구문. 이와 같은 전역 객체는 디노, Node.js, 웹 브라우저 등에서 실행되는 런타임 코드에 의해 제공</p>
<h4 id="라이브러리-선언">라이브러리 선언</h4>
<p>Array, Function 같은 내장된 전역 객체는 lib, [target].d.ts 파일 이름으로 선언.</p>
<h4 id="라이브러리-target">라이브러리 target</h4>
<p>타입스크립트는 기본적으로 tsc CLI 또는 프로젝트의 tsconfig.json(기본값은 (es5)에서 제공된 target 설정에 따라 적절한 lib 파일을 포함 인터페이스 병합을 사용해 서로 빌드.</p>
<h4 id="dom-선언">DOM 선언</h4>
<p>DOM 타입이라고 하는 웹 브라우저 타입은 local Storage와 같은 API와 웹 브라우저에서 주로 사용하는 HTMLElement와 같은 타입 형태를 다룸. DOM 타입은 lib.dom.d.ts 파일과 다른 lib.*.d.ts 선언 파일에도 저장</p>
<p>lib 컴파일러 옵션을 재정위하지 않는 타입스크립트 프로젝트는 DOM 타입을 기본으로 포함.
Node.js와 같은 브라우저가 아닌 환경에서 실행되는 프로젝트는 타입 시스템이 존재한다고 주장하는 document와 localStorage 같은 전역 API에 접근할 수 없기 때문에 개발자가 혼란스러울 수 있음.</p>
<h3 id="모듈-선언">모듈 선언</h3>
<p>선언 파일의 또 다른 중요한 기능은 모듈의 상태를 설명하는 기능.
모듈 문자열 이름 앞에 declare 시 모듈 내용을 타입 시스템에 알릴 수 있음.</p>
<pre><code class="language-typescript">// module.d.ts
declare module &quot;my-example-lib&quot; {
  export const value: string;
}</code></pre>
<pre><code class="language-typescript">// index.ts
import {value } from &quot;my-example-lib&quot;;

console.log(value);</code></pre>
<p>코드에서 declare module을 자주 사용하면 안됨. 와일드 카드 모듈 선언 or 후반부 다루는 패키지 타입과 함께 사용함.</p>
<h4 id="와일드카드-모듈-선언">와일드카드 모듈 선언</h4>
<p>모듈 선언은 js와 ts 파일 확장자가 아닌 특정 파일의 내용을 코드로 가져올 수 있음을 웹 애플리케이션에 알리기 위해 사용. 모듈 선언으로 하나의 * 와일드카드를 포함해 해당 패턴과 일치하는 모든 모듈을 나타낼 수 있음.</p>
<p>CSS 모듈을 지원하면 CSS 파일에서 런타임에 사용할 수 있는 객체로 스타일을 가져옴.
기본적으로 { [i: string]: string } 타입의 객체를 내보내는 &quot;*.module.css&quot;와 같은 패턴으로 모듈을 정의함.</p>
<pre><code class="language-typescript">// styles.d.ts
declare module &quot;*.module.css&quot; {
  const styles: { [i: string]: string} ;
  export default styles;
}</code></pre>
<pre><code class="language-typescript">import styles from &quot;./styles.module.css&quot;;

styles.anyClassName; // 타입 : string</code></pre>
<ul>
<li>와일드카드 모듈을 사용해 로컬 파일을 나타내는 방식이 타입 안정성을 완벽히 보장하지는 않음. 일부 프로젝트는 웹팩 같은 빌드 시스템을 사용하거나 로컬 파일에서 .d.ts 파일을 생성해 가져오기가 가능한지 확인합니다.<h3 id="패키지-타입">패키지 타입</h3>
<h4 id="선언">선언</h4>
ts는 입력된 파일에 대한 .d.ts 출력 파일과 js 출력 파일을 함께 생성하는 옵션을 제공함.
다음 index.ts 소스 파일이 주어졌다고 가정해보자.<pre><code class="language-typescript">// index.ts
export const greet = ( text: string) =&gt; {
console.log(&quot;Hello, ${text}!&quot;);
};</code></pre>
module은 es2015, target은 es2015인 선언 옵션을 사용해 다음 출력 파일을 생성<pre><code class="language-typescript">// index.d.ts
export declare const greet: (text:string) =&gt; void;</code></pre>
</li>
</ul>
<pre><code class="language-typescript">//index.js
export const greet = (text) =&gt; {
  console.log(&#39;Hello, ${text}!&#39;);
};</code></pre>
<p>자동으로 생성된 .d.ts파일은 프로젝트에서 사용자가 사용할 타입 정의를 생성하는 가장 좋은 방법.</p>
<h4 id="패키지-타입-의존성">패키지 타입 의존성</h4>
<p>자체 .d.ts 선언 파일고 함께 제공되는 npm 모듈은 대부분 다음과 같은 파일 구조를 가짐.</p>
<pre><code>lib/
    index.js
    index.d.ts
package.json</code></pre><p>예를 들어 꾸준히 인기 있는 테스트 프레임워크인 제스트는 타입스크립트로 작성되었으며 jest 패키지 내에 자체 번들 .d.ts 파일을 제공함. describe와 it 같은 함수를 제공하는 @jest/globals 패키지에 대한 의존성을 가지며 jest는 전역으로 사용할 수 있음.</p>
<pre><code>// package.json
{
    &quot;devDependencies&quot;: {
        &quot;jest&quot;: &quot;^32.1.0&quot;
    }
}</code></pre><pre><code class="language-typescript">// using-globals.d.ts
describe(&quot;MyAPI&quot;, () =&gt; {
    it(&quot;works&quot;, () =&gt; { /* ... */ });
});</code></pre>
<pre><code class="language-typescript">//using-imported.d.ts
import {describe, it} from &quot;@jest/globals&quot;;

describe(&quot;MyAPI&quot;, () =&gt; {
    it(&quot;works&quot;, () =&gt; { /* ... */});
});</code></pre>
<h4 id="패키지-타입-노출">패키지 타입 노출</h4>
<p>프로젝트가 npm에 배포되고 사용자를 위한 타입을 제공하려면 패키지의 package.json 파일에 types 필드를 추가해 루트 선언 파일을 가리킵니다. types 필드는 main 필드와 유사하게 작동하고 종종 동일한 것처럼 보이지만, .js 확장자 대신에 .d.ts 확장자를 사용함.</p>
<p>예를 들어 다음 가상의 package 파일에서 main 런타임 파일인 ./lib/index.js는 types 파일인 ./lib/index.d.ts와 병렬 처리됨.</p>
<pre><code>{
    &quot;author&quot; : &quot;Pendant Publishing&quot;,
    &quot;main&quot; : &quot;./lib/index.js&quot;,
    &quot;name&quot; : &quot;coffeetable&quot;,
    &quot;types&quot; : &quot;./lib/index.d.ts&quot;,
    &quot;version&quot;: &quot;0.5.22&quot;,
}</code></pre><p>그런 다음 타입스크립트는 유틸리지 패키지에서 가져온 파일을 사용하기 위해 제공해야 하는 것으로 ./lib/index.d.ts의 내용을 사용함.</p>
<ul>
<li>타입 필드가 패키지의 package.json에 존재하지 않으면 타입스크립트는 ./index.d.ts를 기본값으로 가정함.</li>
</ul>
<h3 id="definitelytyped">DefinitelyTyped</h3>
<ul>
<li>TS 프로젝트는 여전히 해당 패키지에서 모듈의 타입 형태를 알려줘야 함.</li>
<li>타입스크립트 팀과 커뮤니티는 커뮤니티에서 작성된 패키지 정의를 수용하기 위해 DefinitelyTyped 라는 거대한 저장소를 만들었음. 짧게줄여서 DT는 깃허브에서 가장 활발한 저장소 중 하나. 저장소에는 변경 제안 검토 밑 업데이트 게시와 관련된 자동화 부분과 수천 개의 .d.ts 정의 패키지가 포함되어 있음.</li>
<li>DT 패키지는 타입을 제공하는 패키지와 동일한 이름으로 npm에 @types 범위로 게시됨.
예를 들어 2022년 집필 시점 @types/react는 리액트 패키지에 대한 타입 정의를 제공함.</li>
</ul>
<p>시멘틱 버저닝인 &quot;semver&quot; 번호는 @types/ 패키지와 패키지가 나타내는 패키지가 서로 반드시 일치하지는 않음. 초기의 리액트 같은 패치 버전, 심지어 메이저 버전에서 일치하지 않는 부분을 종종 발견가능.</p>
<p><strong>WARING</strong> : 커뮤니티에서 작성되기 때문에 상위 프로젝트보다 뒤쳐지거나 약간 부정확할 수 있음. 프로젝트가 성공적으로 컴파일되었지만 라이브러리 호출 시 런타임 오류 발생하면 접근하고 있는 API의 서명이 변경되었는지 확인할 것.</p>
<h4 id="타입-사용-가능성">타입 사용 가능성</h4>
<p>아직 사용 가능한 타입이 없는 패지키에서 타입을 얻는 일반적인 세 가지 옵션은 다음과 같음</p>
<ul>
<li>@types/ 패키지를 생성하기 위해 DefniteTyped에 풀 리퀘를 보내기.</li>
<li>declare module 구문을 사용해 프로젝트 내에서 타입 작성</li>
<li>13장 구성 옵션에서 다루게 될 noImplicitAny 옵션을 비활성하고 강력하게 경고</li>
</ul>
<p>시간이 있다면 DT에 타입을 제공하는 것이 좋음.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React test 작성 -2]]></title>
            <link>https://velog.io/@yhc-key/React-test-%EC%9E%91%EC%84%B1-2</link>
            <guid>https://velog.io/@yhc-key/React-test-%EC%9E%91%EC%84%B1-2</guid>
            <pubDate>Mon, 29 Apr 2024 07:37:06 GMT</pubDate>
            <description><![CDATA[<h2 id="userevent-vs-fireevent">userEvent vs fireEvent</h2>
<p>userEvent : fireEvent를 사용해서 만들어짐. 
userEvent 내부 코드를 보면 fireEvent를 사용하면서 엘리먼트 타입에 따라서 Label을 클릭했을 때, 더욱 적절한 반응을 보여줌 (보다 유저의 진짜 반응처럼 보임)</p>
<p>userEvent.click(button) 시 버튼이 focus되지만, fireEvent로 버튼 클릭 시 focus 되지 않음.</p>
<p>즉 <strong>userEvent를 쓰는 것을</strong> 보다 권장함.</p>
<h2 id="eslint를-통한-디버깅">Eslint를 통한 디버깅</h2>
<p>내부 설정해주기</p>
<p>extends 항목 : 플러그인을 추가 한 후에 규칙을 정해줘야 사용가능.
그래서 extends 항목에 사용하고자 하는 규칙을 설정.
recommended는 추천이 되는 걸 사용
규칙 변경 원할 시 rule 항목 추가</p>
<pre><code class="language-js">{
    &quot;plugins&quot;: [
      &quot;testing-library&quot;,
      &quot;jest-dom&quot;
      ],
      &quot;extends&quot;: [
        &quot;react-app&quot;,
        &quot;react-app/jest&quot;,
        &quot;plugin:testing-library/react&quot;,
        &quot;plugin:jest-dom/recommended&quot;
      ]
}</code></pre>
<h2 id="msw">MSW</h2>
<p>MSW : Mock Service Worker
브라우저에 서비스 워커를 등록하여 외부로 나가는 네트워크 리퀘스트를 감지. 실제 서버로 갈 때 중간에 가로채서(intercept) MSW 클라이언트 사이드 라이브러리로 보내고, 등록된 핸들러에서 요청 처리 후 모의 응답을 브라우저에게 보냄.
API 응답 포함 테스트시 유용.
Jest 사용시 노드와 통합 방식 사용해야 함 (브라우저 X)</p>
<h3 id="msw-세팅">MSW 세팅</h3>
<ol>
<li><p>MSW 설치</p>
<pre><code class="language-js">npm install msw --save</code></pre>
</li>
<li><p>handler 생성</p>
<pre><code class="language-javascript">// src/mocks/handlers.js
// 핸들러들 넣어주는 파일 만듬
import { rest } from &quot;msw&quot;;
</code></pre>
</li>
</ol>
<p>export const handlers = [
  rest.get(&quot;<a href="http://localhost:5000/products&quot;">http://localhost:5000/products&quot;</a>, (req, res, ctx) =&gt; {
    return res(
      ctx.json([
        {
          name: &quot;America&quot;,
          imagePath: &quot;/images/america.jpeg&quot;,
        },
        {
          name: &quot;England&quot;,
          imagePath: &quot;/images/england.jpeg&quot;,
        },
      ])
    );
  }),
  rest.get(&quot;<a href="http://localhost:5000/options&quot;">http://localhost:5000/options&quot;</a>, (req, res, ctx) =&gt; {
    return res(
      ctx.json([
        {
          name: &quot;Insurance&quot;,
        },
        {
          name: &quot;Dinner&quot;,
        },
      ])
    );
  }),
];</p>
<pre><code>
3. server 생성
```js
//src/mocks/server.js
import { setupServer} from &#39;msw/node&#39;;
import {handlers} from &quot;./handlers&quot;;

export const server = setupServer(...handlers);
</code></pre><ol start="4">
<li>setupTests 생성<pre><code class="language-js">//src/setupTests.js
</code></pre>
</li>
</ol>
<p>import &quot;@testing-library/jest-dom&quot;;</p>
<p>import { server } from &quot;./mocks/server&quot;;</p>
<p>beforeAll(() =&gt; {
  server.listen();
});
// 서버 켜주고
afterEach(() =&gt; server.resetHandlers());
// 다른 핸들러에 영향 안미치도록 리셋해주고
afterAll(() =&gt; server.close());
// 서버 닫기</p>
<p>```</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[kprintf 컨퍼런스 - 프론트앤드 후기]]></title>
            <link>https://velog.io/@yhc-key/kprintf-%EC%BB%A8%ED%8D%BC%EB%9F%B0%EC%8A%A4-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%95%A4%EB%93%9C-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@yhc-key/kprintf-%EC%BB%A8%ED%8D%BC%EB%9F%B0%EC%8A%A4-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%95%A4%EB%93%9C-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Sun, 28 Apr 2024 08:38:30 GMT</pubDate>
            <description><![CDATA[<h1 id="react-19-메이저-업데이트-무엇이-바뀌는가">React 19 메이저 업데이트 무엇이 바뀌는가?</h1>
<h2 id="react-19-이전">React 19 이전</h2>
<p>React의 기본 컨셉은 Code Layer -&gt; React Layer -&gt; Browser Layer</p>
<p>16.8에서 Functional 컴포넌트는 항상 렌더링된 값을 UI에 보여줌
-&gt; 사용자는 더 이상 UI와 데이터 간 동기화를 생각하지 않아도 됨
-&gt; 데이터 업데이트를 UI에 알려야함 -&gt; State = User Interaction으로 발생한 데이터 변화</p>
<p>Thinking about state : state도 생명주기를 가지고, 독립적인 생명 주기를 보장</p>
<p>독립적인 state 데이터 조작 API = React Hooks</p>
<p>useSuspense 는 독립적인 데이터로 관리가 안됨 
useCatch, useProvider, useShouldComponentUpdate 역시 독립적으로 데이터를 관리하는 것이 아니므로 리액트 훅이 아님</p>
<p>16.8에서는 무엇을을 해결함 (render()내부에 모두 넣는 것으로)</p>
<p>18에서는 언제와 어디서를 해결하고자 했음. 
모든 데이터 update가 즉시 이루어져야 하는가?
-&gt; 언제 알릴 지 React가 선택권을 쥐고 있으면, 필요한 UI 먼저 업데이트 할 수 있다.</p>
<p>리액트 18부터는 덜 중요한 부분 잠시 끊고, 중요한거부터 처리하고 후속작업을 해줌
-&gt; React 18의 의의 = React Layer는 독립된 스케쥴링을 한다.</p>
<p>Suspense : 
렌더링 중 Stop 이 걸려 가까운 Suspense가 처리함 (마치 try-catch-throwd의 관계) 
실제 React 내부에서는 Ping을 통해 렌더링을 시도해달라 요청함.</p>
<p>Automatic Batching도 같은 원리 -&gt; setState를 한번에 처리
18 미만에서는 따로따로 setState를 처리했는데, 
18부터는 setState를 잠깐 기다려서 모아놓은 다음에 한번에 렌더링</p>
<ol>
<li>마이크로 태스크 큐가 끝날때</li>
<li>혹은 이벤트 루프가 다음 스테이트로 넘어갈 때 </li>
</ol>
<h3 id="two-react-패러다임의-부상">Two React 패러다임의 부상</h3>
<p>UI = f(state, data)
각 컴포넌트가 실행되어야 하는 곳은 다름 (클라이언트 or 서버)
-&gt; state가 없는 컴포넌트는 client에서 다룰 필요가 없다. -&gt; 보다 최적화 가능 -&gt; 코드가 줄어듬
-&gt; data가 없는 컴포넌트는 server에서 다룰 필요가 없다.</p>
<h2 id="react-19는-어떤-점이-달라질까">React 19는 어떤 점이 달라질까</h2>
<p>2-1. Server Component에 대한 본격적 지원</p>
<ol>
<li>SC는 서버에서만 실행되는 C로 실제 client에 코드가 전달되지 않는다.</li>
<li>Server Component는 DB, 파일, 다른 서버 등 Server-side recourse에 접근할 수 있다.</li>
<li>SC는 Server-side resource에 따라 변경 된다.</li>
<li>server상태가 바귀어도 client의 상태를 보존할 수 있어야 한다.</li>
</ol>
<p>즉 Sc는 db와 통신하고, Suspense와 합쳐지면 최적화 + 최적의 UI를 보여줄 수 있다.
Suspense로 감싸면 서로의 동작이 서로에게 영향을 주지 못한다.</p>
<p>Client -&gt; Server로 소통하는 Server Action(Form)도 준비중.</p>
<p>이에 따라 새로운 React Hooks도 도입
useFormState, useFormStatus, useOptimistic 등…</p>
<p>2-3. Asset Loading
유저의 관점에서, css, 폰트, 이미지도 모두 UI다
18이하에서는 React 렌더링과 Asset을 가져오는 차이로 UI 불균형이 생김</p>
<p> React-Helmet으로 처리 가능하지만, 이제 react자체로 처리가능</p>
<p>실제 스태틱하게 빌드시 index.html의 head에 들어감 (nextjs는 자동으로 해줌)</p>
<p>2-4. Resource Hints도 이제 React내부에서 사용가능</p>
<p>Resource Hints : 어떤 걸 미리 다운받을지 연결해놓는거. -&gt; 사용성과 UI 개선시키는 기능
Dns-prefetch를 react와 통합하여 할 수 있음.</p>
<p>prefetchDNS 사용가능 (import {prefetchDNS} from ‘react-dom’)
preconnect는 미리 서버와 연결까지 함 -&gt;더 빠르게 resource를 들고 올 수 있음</p>
<p>tcp가 그렇게 짧지 않음 적으면 50ms 길면 1초 
Tcp, tls handshake 시 약 200ms ~ 300ms 더나아가 몇초까지도 가능</p>
<p>Preload = Preconnect + 리소스까지
특정 Resource가 쓰일 것이 확실하면 preload는 좋은 선택지</p>
<p>2-5. 웹 컴포넌트에 대한 지원은 공지되어 있지만, 아직 명확한 건 없음
React나 다른 프레임워크 없이도 Component를 만들 수 있음.
실제로 Github와 같은 서비스에선 Web Components를 적극 활용 중. 다만 브라우저 적으로 뭔가 지원이 있지는 않음 그래서 소극적</p>
<p>2-6. The ‘use’ hook
서버 컴포넌트는 hooks를 사용하지 못하고, 사용할 일도 없음.
Client component에서는 async한 컴포넌트를 사용하지 않음.</p>
<p>Use hook을 사용하면 비동기 함수도 동기적으로 활용할 수 있음.</p>
<p>Use 문법은 await과 비슷
Use 문법은 Promise, Context면 모두 활용가능</p>
<p>useSuspenseQuery 는 react-query와 굉장히 유사</p>
<p>2-7. Goodbye, Forward Ref!
React는 렌더링과 상관없는 값을 가리키기 위한 ref(reference)란 개념이 있음.
React 18까지는 ForwardRef로 특수한 형태로 ref로 접근해야 했음.</p>
<p>Class Componet 내 ref의 값은 prop 내 고유한 값이 있기 때문
정확히는 Class의 instance인지 여부를 담고 있음
근데 이제 Functional component 쓰고, function은 instance란 개념이 없으므로 필요가 없음.
그래서, react 19는 forwardRef란 개념이 없음.</p>
<p>2-8. react-Compiler(React Forget) 
알아서 Auto-Memoization을 지원할 계획이었음. ( react memo, useMemo, useCallback)
하지만 아직 불안정한지 잘 안쓰고 잇음
하지만 Instagram 웹페이지에서는 시험 적용되어있음</p>
<ol start="3">
<li>우리는 다가오는 업데이트를 어떻게 준비해야할까?<br>직접 써보기 &amp; 공식 블로그와 공식 채널로 정보 수집 &amp; Blog Driven Knowledge는 하지 말자 (velog, tistory 너무 좋아하지 마라!)</li>
</ol>
<p>철학은 변하지 않는다.
코드 레이어 -&gt; 리액트 레이어 -&gt; 리액트 내부 스케줄러가 결정 -&gt;   브라우저 레이어로 가는 흐름은 바뀌지 않을것</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[kprintf 컨퍼런스 - 백앤드 부분 정리]]></title>
            <link>https://velog.io/@yhc-key/%EC%BB%A8%ED%8D%BC%EB%9F%B0%EC%8A%A4-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@yhc-key/%EC%BB%A8%ED%8D%BC%EB%9F%B0%EC%8A%A4-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Sun, 28 Apr 2024 08:36:27 GMT</pubDate>
            <description><![CDATA[<h3 id="서론">서론</h3>
<p>CS는 개발자의 포텐셜
서버는 구조를 바꾸기 어렵다.
TPS(Transaction Per Second) :서버가 초당 처리한 사용자 요청의 수
처리 가능한 최대 TPS -&gt; 서버 성능 판단 가능 (동시 사용자와는 다름)
동시 사용자(Concurrent Users : 접속 사용자 중 현재 시스템을 사용하고 있는 사용. (동시 접속자)
부하 사용자(Action Users) : 동시사용자 중 트랜잭션을 발생하여 결과를 기다리고 있는 사용자. (TPS)
Saturation Point (서버의 TPS는 계속 늘어나지만, 특정 시점 이후로는 TPS가 더 이상 증가하지 않음(즉 대기시간이 길어짐))</p>
<p>허나 서버를 늘린다고 무조건 TPS가 증가하지 않음. 
또한 서버는 24시간 돌아가기 때문에 안정성이 중요. RAM 사용량 등 다방면 고려해야함.</p>
<h3 id="서버는-어떻게-요청을-처리할까">서버는 어떻게 요청을 처리할까?</h3>
<p>원격 프로세스는 직접 통신이 불가능해서 소켓을 이용해 진행함.
Port : 통신에서 애플리케이션이 상호고분을 위해 사용하는 번호
Socket : 네트워크 상에서 돌아가는 두 프로그램 간 양방향 통신의 앤드포인트
(똑같은 서버로 서로 다른 요청이 들어올 때, 어떻게 구분할 것인가?)
기본적으로, 대부분의 서버는 Request가 올 시 이에 대응하는 프로세스/스레드가 필요 -&gt; 일일이 만들면 Context Switching이 과도하게 발생되어 성능 저하 발생.
톰캣 -&gt; 사용자의 오청을 처리할 스레드를 미리 만들어 놓고, 요청이 들어올 때 마다 스레드를 통해 처리하는 방식을 사용함</p>
<h3 id="c10k-problem">c10k problem</h3>
<p>한 번에 10000개의 요청을 동시에 처리할 수 있냐?
ulimit : file descriptors : 256 -&gt; 소켓도 최대 256개만 열 수 있음
프로세스의 필요에 따라 Hard Limit까지 올릴 수도 있음(81920개 (맥북 특정 버전기준)</p>
<p>MySQL 또한, 커넥션 별로 소켓을 열어서 관리하고 있음 (214개 + a)
즉, 최대 연결 수가 변경되지 않으면, soft limit 값을 확인해야 함.</p>
<h3 id="http">HTTP</h3>
<p>HTTP 는 W3 상에서 정보를 주고 받을 수 있는 프로토콜. 주로 TCP를 사용
HTTP/3부터는 UDP 사용</p>
<p>TCP - 더 빠르게 할 수 없을까?</p>
<p>HTTP/1.0 시절에는 HTML, JS파일, CSS 파일 이기 때문에 각각 3way Handshake를 함. 즉 3번 이 과정을 진행
HTTP/1.1 시절에는 HTML, JS파일, CSS 파일 을 한번에 TCP Handshake를 해서 빨라짐.
옛날에는 새로고침을 3번하면 3번 HandShake
지금은 Web Cache를 통해 TCP Handshake 총 1회(캐시 정책에 따라 0회일 때도 있음)
하지만
같은 서버에서의 다른 주소에 대해서 3번 호출 시 TCP Handshake를 총 3회 함</p>
<p>한 번 신뢰성 획득하면 그 이후로는 안해도 되는 것을 활용한 것이 TCP Fast Open</p>
<h3 id="dhcp에서-udp-사용-이유">DHCP에서 UDP 사용 이유</h3>
<p>DHCP : IP가 없을때 IP를 달라고하는 것
클라이언트는 DHCP가 서버가 어딨는지 모르므로 연결된 모든 곳에 쏨 -&gt; 모든 DHCP가 다 응답을 줌. 클라이언트가 가장 빠른 거 받음
TCP -&gt; Hanshake 과정 필요 -&gt; 모든 통신이 1:1로 이루어져야 함.(Unicast)
즉, Broadcast(로컬 네트워크에 연결된 모든 곳에 요청) -&gt; TCP 사용 불가
&amp; IP 는 3계층 TCP 는 4계층</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React test 작성 -1]]></title>
            <link>https://velog.io/@yhc-key/React-test-%EC%9E%91%EC%84%B1</link>
            <guid>https://velog.io/@yhc-key/React-test-%EC%9E%91%EC%84%B1</guid>
            <pubDate>Sun, 28 Apr 2024 08:16:53 GMT</pubDate>
            <description><![CDATA[<h1 id="셋팅">셋팅</h1>
<h2 id="react-testing-library">React Testing Library</h2>
<p>create-react-app을 통해 생성된 프로젝트는 즉시 React Testing Library를 지원해서 별도 작업이 필요없음. 
그렇지 않은 경우 npm을 통해 추가 가능</p>
<pre><code class="language-javscript">npm install --save-dev @testing-library/react</code></pre>
<p>RTL은 에어비엔비에서 만든 Enzyme을 대처하는 솔루션
Enzyme : 구현 주도 테스트(implementation Driven Test)
React Testing Library : 행위 주도 테스트(Behavior Driven Test)</p>
<p>create-react-app 의 경우 babel, webpack 설정이 되어 있음</p>
<h2 id="jest">Jest</h2>
<p>FaceBook에 의해서 만들어진 테스팅 프레임 워크
Test case를 만들어 app 초드가 잘 돌아가는지 확인해줌
create-react-app에는 기본 설정이 되어 있음. 없는 경우는</p>
<ol>
<li><pre><code class="language-javascript">npm install jest --save-dev</code></pre>
</li>
<li>Test 스크립트 변경</li>
<li>테스트 작성할 폴더 및 파일 기본 구조 생성</li>
</ol>
<p>파일 이름을 test, spec 혹은 폴더 이름을 test로 하면 자동으로 Jest가 찾아서 실행해줌.</p>
<h3 id="jest-사용법">Jest 사용법</h3>
<p>describe: argument(name, fn) 여러 관련 테스트를 그룹화하는 블록을 만듬
it same as test : argument(name, fn, timeout) 개별 테스트를 수행하는 곳. 각 테스트를 작은 문장처럼 설명</p>
<p>expect : expect함수는 값을 테스트할 때마다 사용. matcher와 함께 사용하는 것이 일반적
matcher : 다른 방법으로 값을 테스트 하도록 &quot;매처&quot;를 사용합니다.</p>
<h4 id="matcher의-종류">matcher의 종류</h4>
<p>toBe : 예상 값 테스트
not.toBe : 나와서는 안되는 값 테스트
이 외 추가 예정</p>
<h2 id="test-실행">Test 실행</h2>
<p>npm test시 여러 옵션 실행 가능
Watch Usage에 자세히 설명 나와있으니 필요한 부분 찾아서 사용하면 됨.</p>
<h3 id="렌더-함수">렌더 함수</h3>
<p>&#39;render&#39; 함수 : 인자로 렌더링할 React 컴포넌트가 들어감
RTL에서 제공하는 쿼리 함수와 기타 유틸리티 함수를 담고 있는 객체를 리턴
screen을 이용해 쿼리함수 사용하는 것이 나중에 더 복잡함이 덜함.</p>
<h3 id="쿼리-함수">쿼리 함수</h3>
<p>쿼리는 페이지에서 요소를 찾기 위해 테스트 라이브러리가 제공하는 방법
여러 유형의 쿼리(&quot;get&quot;, &quot;find&quot;, &quot;query&quot;)가 있음. </p>
<p>getBy : 쿼리에 대해 일치하는 노드를 반환하고, 일치하는 요소가 없거나 둘 이상의 일치가 발견되면 설명 오류 발생(둘 이상 요소 예상 시 getAllBy 사용)
queryBy : 쿼리에 대해 일치하는 노드 반환, 요소 없으면 null 반환. 둘 이상 일치 발견 시 오류 발생 (queryAllBy 사용 시 피할 수 있음)
findBy : 주어진 쿼리와 일치하는 요소가 발견되면 해결되는 Promise 반환. 요소 발견 x or 기본 제한 시간인 1000ms 후에 둘 이상 요소 발견 시 둘 이상의 요소 발견시 약속 거부 (findAllBy 사용 시 회피 가능)</p>
<p>getBy + waitFor = findBy</p>
<p><img src="https://velog.velcdn.com/images/yhc-key/post/0fcd8103-f82a-4a25-a6ef-fb572e19fd38/image.png" alt=""></p>
<h2 id="tdd의-장점">TDD의 장점</h2>
<ol>
<li>TDD를 하므로 인해 많은 기능 테스트로 소스 코드에 안정감이 부여된다.</li>
<li>실제 개발하면서 시간이 많이 소요된느 부분인 디버깅 시간이 줄어들고 실제 개발 시간도 줄어듬</li>
<li>소스 코드 하나하나 신중하게 짜기 때문에 클린 코드 나올 확률이 높다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[TDD를 위한 툴 JEST 사용법 - 설정과 분해]]></title>
            <link>https://velog.io/@yhc-key/TDD%EB%A5%BC-%EC%9C%84%ED%95%9C-%ED%88%B4-JEST-%EC%82%AC%EC%9A%A9%EB%B2%95-</link>
            <guid>https://velog.io/@yhc-key/TDD%EB%A5%BC-%EC%9C%84%ED%95%9C-%ED%88%B4-JEST-%EC%82%AC%EC%9A%A9%EB%B2%95-</guid>
            <pubDate>Thu, 18 Apr 2024 04:52:10 GMT</pubDate>
            <description><![CDATA[<h2 id="설정과-분해">설정과 분해</h2>
<p>수행 되기전 설정 작업과 수행 된 후 마무리 작업을 위한 헬퍼 함수를 JEST는 제공함.</p>
<h3 id="많은-테스트를-위한-설정-반복">많은 테스트를 위한 설정 반복</h3>
<p>많은 테스트를 위해 반복적 수행 필요한 작업이 있다면, beforeEach, afterEach 사용 가능</p>
<pre><code class="language-js">beforeEach(() =&gt; {
  initializeCityDatabase();
});

afterEach(() =&gt; {
  clearCityDatabase();
});

test(&#39;city database has Vienna&#39;, () =&gt; {
  expect(isCity(&#39;Vienna&#39;)).toBeTruthy();
});

test(&#39;city database has San Juan&#39;, () =&gt; {
  expect(isCity(&#39;San Juan&#39;)).toBeTruthy();
});</code></pre>
<h3 id="일회성-설정">일회성 설정</h3>
<p>한 번만 필요한 설정이 있을 수 있음. 비동기인 경우 이 것이 특히 귀찮음. 이를 위한 beforeAll, afterAll 을 제공</p>
<pre><code class="language-js">beforeAll(() =&gt; {
  return initializeCityDatabase();
});

afterAll(() =&gt; {
  return clearCityDatabase();
});

test(&#39;city database has Vienna&#39;, () =&gt; {
  expect(isCity(&#39;Vienna&#39;)).toBeTruthy();
});

test(&#39;city database has San Juan&#39;, () =&gt; {
  expect(isCity(&#39;San Juan&#39;)).toBeTruthy();
});</code></pre>
<h3 id="살펴보기--특정-그룹만-before-after-사용하기">살펴보기 ( 특정 그룹만, before, after 사용하기)</h3>
<p>describe 사용해서 테스트들 그룹핑 가능</p>
<pre><code class="language-js">// 이 파이의 모든 테스트에 적용됩니다
beforeEach(() =&gt; {
  return initializeCityDatabase();
});

test(&#39;city database has Vienna&#39;, () =&gt; {
  expect(isCity(&#39;Vienna&#39;)).toBeTruthy();
});

test(&#39;city database has San Juan&#39;, () =&gt; {
  expect(isCity(&#39;San Juan&#39;)).toBeTruthy();
});

describe(&#39;matching cities to foods&#39;, () =&gt; {
  // 이 describe 블럭의 테스트에만 적용됩니다
  beforeEach(() =&gt; {
    return initializeFoodDatabase();
  });

  test(&#39;Vienna &lt;3 sausage&#39;, () =&gt; {
    expect(isValidCityFoodPair(&#39;Vienna&#39;, &#39;Wiener Schnitzel&#39;)).toBe(true);
  });

  test(&#39;San Juan &lt;3 plantains&#39;, () =&gt; {
    expect(isValidCityFoodPair(&#39;San Juan&#39;, &#39;Mofongo&#39;)).toBe(true);
  });
});</code></pre>
<pre><code class="language-js">//결과물을 통한 흐름체크하기
beforeAll(() =&gt; console.log(&#39;1 - beforeAll&#39;));
afterAll(() =&gt; console.log(&#39;1 - afterAll&#39;));
beforeEach(() =&gt; console.log(&#39;1 - beforeEach&#39;));
afterEach(() =&gt; console.log(&#39;1 - afterEach&#39;));
test(&#39;&#39;, () =&gt; console.log(&#39;1 - test&#39;));
describe(&#39;Scoped / Nested block&#39;, () =&gt; {
  beforeAll(() =&gt; console.log(&#39;2 - beforeAll&#39;));
  afterAll(() =&gt; console.log(&#39;2 - afterAll&#39;));
  beforeEach(() =&gt; console.log(&#39;2 - beforeEach&#39;));
  afterEach(() =&gt; console.log(&#39;2 - afterEach&#39;));
  test(&#39;&#39;, () =&gt; console.log(&#39;2 - test&#39;));
});

// 1 - beforeAll
// 1 - beforeEach
// 1 - test
// 1 - afterEach
// 2 - beforeAll
// 1 - beforeEach
// 2 - beforeEach
// 2 - test
// 2 - afterEach
// 1 - afterEach
// 2 - afterAll
// 1 - afterAll</code></pre>
<h3 id="describe의-실행-순서와-테스트-블럭">describe의 실행 순서와 테스트 블럭</h3>
<p>Jest는 실제 테스트 실행 전 테스트 파일의 모든 describe 처리기 실행
describe 블럭 내부보다 before, after에서 해야 하는 이유임.</p>
<pre><code class="language-js">describe(&#39;outer&#39;, () =&gt; {
  console.log(&#39;describe outer-a&#39;);

  describe(&#39;describe inner 1&#39;, () =&gt; {
    console.log(&#39;describe inner 1&#39;);
    test(&#39;test 1&#39;, () =&gt; {
      console.log(&#39;test for describe inner 1&#39;);
      expect(true).toEqual(true);
    });
  });

  console.log(&#39;describe outer-b&#39;);

  test(&#39;test 1&#39;, () =&gt; {
    console.log(&#39;test for describe outer&#39;);
    expect(true).toEqual(true);
  });

  describe(&#39;describe inner 2&#39;, () =&gt; {
    console.log(&#39;describe inner 2&#39;);
    test(&#39;test for describe inner 2&#39;, () =&gt; {
      console.log(&#39;test for describe inner 2&#39;);
      expect(false).toEqual(false);
    });
  });

  console.log(&#39;describe outer-c&#39;);
});

// describe outer-a
// describe inner 1
// describe outer-b
// describe inner 2
// describe outer-c
// test for describe inner 1
// test for describe outer
// test for describe inner 2</code></pre>
<h3 id="testonly를-통한-특정성-부여">test.only를 통한 특정성 부여</h3>
<p>수행할 테스트가 유일할 경우 테스트가 실패하는가의 여부 체크는 중요하다.
test -&gt; test.only를 통해 특정성을 부여해 테스트 실패 여부 원인을 찾을 수 있다.</p>
<pre><code class="language-js">test.only(&#39;이 테스트는 수행할 유일한 테스트가 될 것입니다&#39;, () =&gt; {
  expect(true).toBe(false);
});

test(&#39;이 테스트는 수행되지 않을 것입니다&#39;, () =&gt; {
  expect(&#39;A&#39;).toBe(&#39;A&#39;);
});</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[TDD를 위한 툴 JEST 사용법 - 비동기 코드 테스트]]></title>
            <link>https://velog.io/@yhc-key/TDD%EB%A5%BC-%EC%9C%84%ED%95%9C-%ED%88%B4-JEST-%EC%82%AC%EC%9A%A9%EB%B2%95-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%BD%94%EB%93%9C-%ED%85%8C%EC%8A%A4%ED%8A%B8</link>
            <guid>https://velog.io/@yhc-key/TDD%EB%A5%BC-%EC%9C%84%ED%95%9C-%ED%88%B4-JEST-%EC%82%AC%EC%9A%A9%EB%B2%95-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%BD%94%EB%93%9C-%ED%85%8C%EC%8A%A4%ED%8A%B8</guid>
            <pubDate>Thu, 18 Apr 2024 04:20:52 GMT</pubDate>
            <description><![CDATA[<h2 id="비동기-코드-테스트">비동기 코드 테스트</h2>
<h3 id="콜백">콜백</h3>
<p>가장 일반적인 비동기 패턴이다.
아래와 같이 해서는 안된다.</p>
<pre><code class="language-js">// 잘못된 예시
test(&#39;the data is peanut butter&#39;, () =&gt; {
  function callback(data) {
    expect(data).toBe(&#39;peanut butter&#39;);
  }

  fetchData(callback);
});</code></pre>
<p>done이라는 단일 인자 사용시 Jest는 done 콜백이 호출될 때까지 기다림</p>
<pre><code class="language-js">test(&#39;the data is peanut butter&#39;, done =&gt; {
  function callback(data) {
    try {
      expect(data).toBe(&#39;peanut butter&#39;);
      done();
    } catch (error) {
      done(error);
    }
  }

  fetchData(callback);
});</code></pre>
<p>expect 구문이 실패하는 경우 오류 발생 &amp; done()호출 x. 
실패한 이유를 보려면 expect를 try 블럭으로 감싸고, catch 블럭의 오류를 done에게 전달해야 함.</p>
<h3 id="프로미스">프로미스</h3>
<p>코드가 프로미스 사용 경우 더 간단한 방법이 있음</p>
<pre><code class="language-js">test(&#39;the data is peanut butter&#39;, () =&gt; {
  return fetchData().then(data =&gt; {
    expect(data).toBe(&#39;peanut butter&#39;);
  });
});</code></pre>
<p>return을 이용해 프로미스를 반드시 반환해야 함. 거절이 예상될 경우 then대신 catch 메서드 사용</p>
<h3 id="asyncawait">Async/Await</h3>
<p>test에 전달된 함수의 앞에 async 사용</p>
<pre><code class="language-js">test(&#39;the data is peanut butter&#39;, async () =&gt; {
  const data = await fetchData();
  expect(data).toBe(&#39;peanut butter&#39;);
});

test(&#39;the fetch fails with an error&#39;, async () =&gt; {
  expect.assertions(1);
  try {
    await fetchData();
  } catch (e) {
    expect(e).toMatch(&#39;error&#39;);
  }
});</code></pre>
<p>.resolves와 .rejects와 함께 조합도 가능</p>
<pre><code class="language-js">test(&#39;the data is peanut butter&#39;, async () =&gt; {
  await expect(fetchData()).resolves.toBe(&#39;peanut butter&#39;);
});

test(&#39;the fetch fails with an error&#39;, async () =&gt; {
  await expect(fetchData()).rejects.toThrow(&#39;error&#39;);
});</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[TDD를 위한 툴 JEST 사용법 - introduction, matcher]]></title>
            <link>https://velog.io/@yhc-key/TDD%EB%A5%BC-%EC%9C%84%ED%95%9C-%ED%88%B4-JEST-%EC%82%AC%EC%9A%A9%EB%B2%95</link>
            <guid>https://velog.io/@yhc-key/TDD%EB%A5%BC-%EC%9C%84%ED%95%9C-%ED%88%B4-JEST-%EC%82%AC%EC%9A%A9%EB%B2%95</guid>
            <pubDate>Thu, 18 Apr 2024 04:12:28 GMT</pubDate>
            <description><![CDATA[<h2 id="시작">시작</h2>
<p>typescript 에 적용시킬거면 jest + babel or ts-jest 
jest + babel 은 type check는 해주지 않는다. 이를 원한다면 ts-jest 사용</p>
<h2 id="matcher-사용">matcher 사용</h2>
<pre><code class="language-js">test(&#39;two plus two is four&#39;, () =&gt; {
  expect(2 + 2).toBe(4);
});</code></pre>
<p>expect 는 예상 객체 반환 -&gt; .tobBe(4)가 여기서의 매처. 실패한 매처 추적하여 에러 메시지 출력
객체 값을 확인하기 위해서는 toEqual 사용권장</p>
<h3 id="undefined-null-false-처리">undefined, null, false 처리</h3>
<ul>
<li>tobeNull : null에만 일치</li>
<li>toBeUndefined : undefnied에만 일치</li>
<li>toBeDefined : toBeUndefined의 반대</li>
<li>toBeTruthy : if 구문이 true로 취급하는 모든 것과 일치</li>
<li>toBeFalse : if 구문이 false로 취급하는 모든 것과 일치</li>
</ul>
<h3 id="숫자">숫자</h3>
<p>부동 소수점 등가의 경우, 테스트가 사소한 반올림 오류에 따라 달라지지 않도록 toEqual 대신 toBeCloseTo 사용권장</p>
<h3 id="문자열">문자열</h3>
<p>toMatch로 정규식과 비교하여 문자열 검사 가능</p>
<pre><code class="language-js">test(&#39;there is no I in team&#39;, () =&gt; {
  expect(&#39;team&#39;).not.toMatch(/I/);
});

test(&#39;but there is a &quot;stop&quot; in Christoph&#39;, () =&gt; {
  expect(&#39;Christoph&#39;).toMatch(/stop/);
});</code></pre>
<h3 id="배열과-이터러블">배열과 이터러블</h3>
<p>toContain을 사용하여 배열이나 이터러블 특정 항목 포함 여부 확인 가능</p>
<pre><code class="language-js">const shoppingList = [
  &#39;diapers&#39;,
  &#39;kleenex&#39;,
  &#39;trash bags&#39;,
  &#39;paper towels&#39;,
  &#39;beer&#39;,
];

test(&#39;the shopping list has beer on it&#39;, () =&gt; {
  expect(shoppingList).toContain(&#39;beer&#39;);
  expect(new Set(shoppingList)).toContain(&#39;beer&#39;);
});</code></pre>
<h3 id="예외">예외</h3>
<p>특정 함수 호출될 때 오류 발생여부 테스트 시 toThrow 사용</p>
<h3 id="그-외">그 외</h3>
<p>추가 정보는 참조 문서(<a href="https://mulder21c.github.%E3%85%87io/jest/docs/en/next/expect">https://mulder21c.github.ㅇio/jest/docs/en/next/expect</a>) 참고
학습 후 비동기 코드 검사 방법 체크함이 좋음</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[onKeydown event는 기본 event가 없다.]]></title>
            <link>https://velog.io/@yhc-key/onKeydown-event%EB%8A%94-%EA%B8%B0%EB%B3%B8-event%EA%B0%80-%EC%97%86%EB%8B%A4</link>
            <guid>https://velog.io/@yhc-key/onKeydown-event%EB%8A%94-%EA%B8%B0%EB%B3%B8-event%EA%B0%80-%EC%97%86%EB%8B%A4</guid>
            <pubDate>Thu, 04 Apr 2024 07:29:09 GMT</pubDate>
            <description><![CDATA[<p>모달에서 onKeydown 이벤트를 설정할 일이 있었다. 
처음에는 onKeyDown에 submit처럼 기본이벤트가 있을거라 생각해서 커스텀모달을 만들때 onKeydown 이벤트에 event.preventDefault()를 걸어주었는데, 이를 모달 내부에 추가되는 input창에서 입력시 외부 onKeydown이벤트가 이벤트 버블링에 의해 실행되기 때문에 event.stopPropagation()을 써줘야하는 문제가 있었다. 
하지만 기본 이벤트가 없기 때문에 event.preventDefault() 및 event.stopPropagation()이 필요없다는 것을 깨닫고 삭제 해주어 코드를 간편하게 만들어주었다.</p>
<pre><code class="language-js">// components/modalCustom.tsx
&quot;use client&quot;;
import React, {
  Fragment,
  KeyboardEvent,
  ReactNode,
  useEffect,
  useState,
} from &quot;react&quot;;
import ReactDOM from &quot;react-dom&quot;;
import { ModalCustomProps } from &quot;@/type/interface&quot;;

// name: 필수
export default function ModalCustom({
  name,
  buttonString = { cancel: &quot;취소하기&quot;, add: &quot;등록하기&quot; },
  design = &quot;&quot;,
  changeButton = false,
  open,
  onClose,
  onClickEvent,
  children,
}: ModalCustomProps) {
  const [prevScrollY, setPrevScrollY] = useState&lt;number | undefined&gt;(undefined);
  const [hasScrollbar, setHasScrollbar] = useState&lt;boolean&gt;(false);
  // 스크롤이 있는지 확인하는 함수
  const checkScrollbar = (): void =&gt; {
    setHasScrollbar(document.body.scrollHeight &gt; window.innerHeight);
  };

  const keyDownHandler = (event: KeyboardEvent&lt;HTMLInputElement&gt;) =&gt; {
    if (!open) {
      return;
    }
    //event.preventDefault(); 
    // 이것을 막음으로써 이벤트 버블링이 일어나 디버깅에 시간이 소요되었다.
    if (event.key === &quot;Enter&quot;) {
      document.getElementById(`${name}AddButton`)?.click();
    } else if (event.key === &quot;Escape&quot;) {
      document.getElementById(`${name}CancelButton`)?.click();
    }
  };

  useEffect((): void =&gt; {
    checkScrollbar();
    // 스크롤을 방지하고 현재 위치를 반환
    const preventScroll = (): void =&gt; {
      const currentScrollY = window.scrollY;
      document.body.style.position = &quot;fixed&quot;;
      document.body.style.width = &quot;100%&quot;;
      document.body.style.top = `-${currentScrollY}px`; // 현재 스크롤 위치
      document.body.style.overflowY = hasScrollbar ? &quot;scroll&quot; : &quot;hidden&quot;;
      setPrevScrollY(currentScrollY);
    };

    // 스크롤을 허용하고, 스크롤 방지 함수에서 반환된 위치로 이동
    const allowScroll = (): void =&gt; {
      document.body.style.position = &quot;&quot;;
      document.body.style.width = &quot;&quot;;
      document.body.style.top = &quot;&quot;;
      document.body.style.overflowY = &quot;&quot;;
      if (prevScrollY !== undefined) {
        window.scrollTo(0, prevScrollY);
      }
    };
    // 모달이 열릴 때마다 스크롤바 여부를 확인하고, 스크롤 방지/허용 함수 호출
    if (open) {
      checkScrollbar();
      preventScroll();
    } else {
      allowScroll();
    }
  }, [open, hasScrollbar, prevScrollY]);

  if (!open) return null;

  return ReactDOM.createPortal(
    &lt;Fragment&gt;
      &lt;div
        className=&quot;fixed top-0 bottom-0 left-0 right-0 z-50 bg-black bg-opacity-40&quot;
        onClick={onClose}
      /&gt;
      &lt;div
        tabIndex={0}
        onKeyDown={keyDownHandler}
        className={`fixed z-50 px-20 py-12 transform -translate-x-1/2 -translate-y-1/2 bg-white top-1/2 left-1/2 rounded-xl outline-none ${design}`}
      &gt;
        {children}
        &lt;div className=&quot;flex justify-center mt-5 &quot;&gt;
          &lt;button
            id={`${name}CancelButton`}
            onClick={onClose}
            className={`px-12 py-2 mr-6 text-sm font-semibold rounded-md text-neutral-100  ring-1 ring-inset ring-f5red-700/10 ${changeButton ? &quot;bg-f5green-350 hover:bg-f5green-300&quot; : &quot;bg-f5red-350 hover:bg-f5red-300&quot;}`}
          &gt;
            {buttonString.cancel}
          &lt;/button&gt;
          &lt;button
            id={`${name}AddButton`}
            onClick={async () =&gt; {
              if (await onClickEvent()) {
                onClose();
              }
            }}
            type=&quot;button&quot;
            className={`px-12 py-2 text-sm font-semibold rounded-md text-neutral-100  ring-1 ring-inset ring-f5green-700/10 ${changeButton ? &quot;bg-f5red-350 hover:bg-f5red-300&quot; : &quot;bg-f5green-350 hover:bg-f5green-300&quot;}`}
          &gt;
            {buttonString.add}
          &lt;/button&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/Fragment&gt;,
    document.getElementById(&quot;globalModal&quot;) as HTMLElement
  );
}
</code></pre>
<pre><code class="language-js">// myEssay.tsx
const handleKeyDown = (
    e: React.KeyboardEvent&lt;HTMLInputElement | HTMLTextAreaElement&gt;
  ) =&gt; {
    if (e.key !== &quot;Enter&quot; &amp;&amp; e.key !== &quot;Escape&quot;) {
       e.stopPropagation();
    }
 }; // 불필요한 함수


//그외
&lt;ModalCustom
        open={isEssayChangeModalOpen}
        name=&quot;changeEssay&quot;
        onClose={() =&gt; setIsEssayChangeModalOpen(false)}
        onClickEvent={changeEssay}
        buttonString={{ cancel: &quot;취소하기&quot;, add: &quot;변경하기&quot; }}
      &gt;
        &lt;div className=&quot;flex flex-col h-min-[800px] text-xl font-medium &quot;&gt;
          &lt;span className=&quot;text-center&quot;&gt;🖋 자소서 변경하기 🖋&lt;/span&gt;
          &lt;label htmlFor=&quot;inputTitle&quot; className=&quot;font-bold&quot;&gt;
            자기소개서 항목
          &lt;/label&gt;
          &lt;input
            type=&quot;text&quot;
            placeholder=&quot;빌 경우 원래 항목이 들어갑니다&quot;
            className=&quot;w-[700px] min-h-16 p-1 h-auto text-sm focus:outline-f5green-300 my-3 &quot;
            ref={essayTitleChangeRef}
            id=&quot;inputTitle&quot;
            onKeyDown={(e) =&gt; handleKeyDown(e)}
            // 이 onKeyDown 처리는 필요 없는 것이였다.
          /&gt;
          &lt;label htmlFor=&quot;inputEssay&quot; className=&quot;font-bold &quot;&gt;
            자기소개서 내용
          &lt;/label&gt;
          &lt;textarea
            placeholder=&quot;빌 경우 원래 내용이 들어갑니다.&quot;
            className=&quot;w-[700px] max-w-[100%] p-1 h-auto mt-3 min-h-40 text-sm my-3 focus:outline-f5green-300 text-start&quot;
            ref={essayChangeRef}
            id=&quot;inputEssay&quot;
          /&gt;
        &lt;/div&gt;
      &lt;/ModalCustom&gt;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[React deep dive -12_Zustand]]></title>
            <link>https://velog.io/@yhc-key/React-deep-dive-12Zustand</link>
            <guid>https://velog.io/@yhc-key/React-deep-dive-12Zustand</guid>
            <pubDate>Wed, 27 Mar 2024 14:54:53 GMT</pubDate>
            <description><![CDATA[<h1 id="zustand">Zustand</h1>
<p>Zustand 리덕스에 영감을 받아 만들어졌다. 하나의 스토어를 중앙 집중형으로 활용해 이 스토어 내부에서 상태를 관리하고 있다. 
(2022년 9월 기준 Zustand 최신 버전인 4.1.1을 기준으로 한다.)</p>
<h2 id="zustand의-바닐라-코드">Zustand의 바닐라 코드</h2>
<pre><code class="language-js">// Zustand store 코드
const createStoreImpl: CreateStoreImpl = (createState) =&gt; {
  type TState = ReturnType&lt;typeof createState&gt;
  type Listener = (state: TState, prevState: TState) =&gt; void
  let state: TState
  const listeners: Set,Listener&gt; = new Set()

  const setState: SetStateInternal&lt;TState&gt; = (partial, replace) =&gt; {
    // ...
    const nextState =
       typeof partial === &#39;function&#39; ? (partial as(state:TState) =&gt; Tstate(state) : partial
    if (nextState !== state) {
      const previousState =state
      state =
        replace ?? typeof nextState !== &#39;object&#39; ? (nextState as TState) : Object.assign({}, state, nextState)
      listeners.forEac((listener) =&gt; listener(state, previousState))
    }
  }

  const getState: () =&gt; TState = () =&gt; state

  const subscribe: (listener: Listener) =&gt; () =&gt; void = (listener) =&gt; {
    listeners.add(listener)
    //Unsubscribe
    return () =&gt; listeners.delete(listener)
  }

  const destroy: () =&gt; void = () =&gt; listeners.clear()
  cosnt api = { setState, getState, subscribe, destroy }
  state = ( createState as PopArgument&lt;typeof createState&gt;)(setState, getState, api,
                                                            )
  return api as any
}</code></pre>
<ul>
<li>스토어의 구조가 state값을 useState 외부에서 관리하는 것을 볼 수 있다. </li>
<li>state라고 하는 변수가 스토어의 상태값을 담아두는 곳이며, setState는 이 state 값을 변경하는 용도로 만들어졌다. </li>
<li>특이한 점은 partial, replace인데 partial은 state 일부분 변경, replace는 전체 변경 시 사용할 수 있다.</li>
<li>getState는 클로저 최신 값 가져올 때 쓰고, subscribe는 listener를 등록하는 데, Set 형태로 선언되어 추가, 삭제 중복 관리가 용이하게끔 설계돼 있다.</li>
<li><strong>상태값 변경 시 리렌더링이 필요한 컴포넌트에 전파될 목적으로 만들어졌음을 알 수 있다.</strong></li>
<li>destroy는 listeners를 초기화하는 역할을 한다.</li>
<li>createStore는 이렇게 만들어진 getState, setState, subscribe, destroy를 반환하고 있다. </li>
<li>import 하는게 없는 바닐라 js로 이루어져 있다.<pre><code class="language-js">//./src/vanilla.ts
type CounterStore = {
count: number
increase: (num: number) =&gt; void
}
</code></pre>
</li>
</ul>
<p>const store = createStore<CounterStore>((set) =&gt; ({
  count: 0,
  increase: (num: number) =&gt; set((state) =&gt; ({ count: state.count + num })), }))</p>
<p>store.subscribe((state, prev) =&gt; {
  if (state.count !== prev.count) {
    console.log(&#39;count has been changed&#39;, state.count)
  }
})</p>
<p>store.setState((state) =&gt; ({ count : state.count +1 }))</p>
<p>store.getState().increase()</p>
<pre><code>
## Zustand의 리액트 코드
Zustand를 리액트에서 사용하기 위해서는 어디선가 store를 읽고 리렌더링을 해야 한다. Zustand 스토어를 리액트에서 사용할 수 있도록 도와주는 함수들은 ./src/react/ts에서 관리되고 있다. 타입을 제외하고 여기에서 export하는 함수는 바로 useStore와 create다. 먼저 useStore를 살펴보자.

```js
// Zustand의 useStore 구현
export function useStore&lt;TState, StateSlice&gt;(
  api: WithReact&lt;StoreApi&lt;TState&gt;&gt;,
  selector : (state: TState) =&gt; StateSlice = api.getState as any,
  equalityFn?: (a: StateSlice, b: StateSlice) =&gt; boolean,
) {
  const slice = useSyncExternalStoreWithSelector(
    api.subscribe,
    api.getState,
    api.getSrverState || api.getState,
    selector,
    equalityFn,
  )
  useDebugValue(slice)
  return slice
}</code></pre><ul>
<li>useSyncExternalStoreWithSelector를 사용해서 앞선 useStore의 subscribe, getState를 넘겨주고, 스토어에서 선택을 원하는 state를 고르는 함수인 selector를 넘겨주고 끝난다.</li>
<li>useSyncExternalStoreWithSelector는 useSyncExternalStore와 완전히 동일하지만 원하는 값을 가져올 수 있는 selector와 동등 비교를 할 수 있는 equalityFn 함수를 받는다는 차이가 있다.</li>
<li>즉 객체가 예상되는 외부 상태값에서 일부 값을 꺼내올 수 있도록 useSyncExternalStoreWithSeletor를 사용했다.</li>
</ul>
<p>또 한 가지, ./src/react.ts에서 export하는 변수는 바로 create인데, 이는 리액트에서 사용할 수 있는 스토어를 만들어주는 변수다.</p>
<pre><code class="language-js">const createImpl = &lt;T&gt;(createState: StateCreator&lt;T, [], []&gt;) =&gt; {
  const api =
        typeof createState === &#39;function&#39; ? createStore(createState) : createState

  const useBoundStore: any = (selector?: any, equalityFn?: any) =&gt; useStore(api, selector, equalityFn)

  Object.assign(useBoundStore, api)

  return useBoundStore
}

const create = (&lt;T&gt;(createState: StateCreator&lt;T, [], []&gt; | undefined) =&gt; createState ? createImpl(createState) : createImpl) as Create

export default create</code></pre>
<p>리액트의 create는 바닐라 createStore를 기반으로 만들어졌기 때문에 거의 유사하다. 또한 간결한 구조 덕분에 리액트 환경에서도 스토어를 생성하고 사용하기가 매우 쉽다.</p>
<pre><code class="language-js">interface Store {
  count: number
  text: string
  increase: (count: number) =&gt; void
  setText: (text: string) =&gt; void
}

const store = createStore&lt;Store&gt;((set) =&gt; ({
  count: 0,
  text: &#39;&#39;,
  increase: (num) =&gt; set((state) =&gt; ({ count: state.count + num })),
  setText: (text) =&gt; set({ text}),
}))

const counterSelector = ({ count, increase }: Store) =&gt; ({
  count, increase,
})

function Counter() {
  const{ count, increase} = useStore(store, counterSelector)

  function handleClick() {
    increase(1)
  }

  return (
    &lt;&gt;
    &lt;h3&gt;{count}&lt;/h3&gt;
    &lt;button onClick={handleClick}&gt;+&lt;/button&gt;
      &lt;/&gt;
  )
}

const inputSelector = ({ text, setText}: Store) =&gt; ({
  text,
  setText,
})

function Input() {
  const {text, setText} = useStore(store, inputSelector)

  useEffect(() =&gt; {
    console.log(&#39;Input Changed&#39;)
  })

  function handleChange(e: ChangeEvent&lt;HTMLInputElement&gt;) {
    setText(e.target.value)
  }

  return (
    &lt;div&gt;
      &lt;input value={text} onChange={handleChange} /&gt;
    &lt;/div&gt;
  )
}</code></pre>
<p>스토어 생성 자체는 앞선 예제와 동일하며, useStore를 사용하면 이 스토어를 리액트에서 사용할 수 있게 된다.
create를 사용해 스토어를 만들면 useStore를 굳이 사용하지 않더라도 바로 사용 가능하다.</p>
<h2 id="간단한-사용법">간단한 사용법</h2>
<pre><code class="language-js">import {create} from &#39;zustand&#39;

const useCounterStore = create((set) =&gt; ({
  count: 1, 
  inc: () =&gt; set((state) =&gt; ({count:state,count +1})),
  dec: () =&gt; set((state) =&gt; ({count: state.count -1})),
}))

function Counter() {
  const { count, inc, dec } = useCounterStore()
  return (
    &lt;div class=&quot;counter&quot;&gt;
      &lt;span&gt;{count}&lt;/span&gt;
      &lt;button onClick={inc}&gt;up&lt;/button&gt;
      &lt;button onClick={dec}&gt;down&lt;/button&gt;
    &lt;/div&gt;
  )
}</code></pre>
<p>Zustand의 create를 통해 스토어를 만들고 반환 값으로 이 스토어를 컴포넌트 내부에서 사용할 수 있는 훅을 받았다. 이 훅으로 getter, setter 모두 접근해 사용 가능하게 된다.</p>
<h2 id="특징">특징</h2>
<p>Zustand는 특별히 많은 코드를 작성하지 않아도 빠르게 스토어를 만들고 사용할 수 있다는 큰 장점이 있다. 라이브러리 크기 역시 Bundlephobia 기준 79.1kB인 Recoil, 13.1kB인 Jotai와 다르게 Zustand는 고작 2.9kB이다.
또한 미들웨어 역시 지원하는데, create의 두 번째 인수로 원하는 미들웨어를 추가하면 된다. 스토어 데이터 영구 보존할 수 있는 persist, 복잡한 객체 관리 도와주는 immer, 리덕스 미들웨어 등의 사용이 가능하다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React deep dive -11_Jotai]]></title>
            <link>https://velog.io/@yhc-key/React-deep-dive-11Jotai</link>
            <guid>https://velog.io/@yhc-key/React-deep-dive-11Jotai</guid>
            <pubDate>Mon, 11 Mar 2024 07:42:02 GMT</pubDate>
            <description><![CDATA[<h1 id="jotai">Jotai</h1>
<p>React의 atom 모델에 영감을 받아 만들어진 상태 관리 라이브러리. 상향식(bottom-up) 접근법을 취하고 있음. 리덕스와 같이 하나의 큰 상태를 app에 내려주는 방식이 아니라, 작은 단위의 상태를 위로 전파할 수 있는 구조를 취하고 있음을 의미.  </p>
<p>리액트 Context의 문제점인 불필요한 리렌더링이 일어난다는 문제를 해결하고자 설계돼 있으며, 추가적으로 개발자들이 메모이제이션이나 최적화를 거치지 않아도 리렌더링이 발생되지 않도록 설계
(책은 2022년 9월 기준 최신 버전인 1.8.3을 기준으로 설명)</p>
<h2 id="atom">atom</h2>
<p>최소 상태를 의미. atom 하나로 파생된 상태까지 만들 수 있다는 점에서 차이가 있음</p>
<pre><code class="language-js">const counterAtom = atom(0)</code></pre>
<pre><code class="language-js">// 담기는 정보 예시
console.log(counterAtom)
// ...
// {
//  init 0,
//  read: (get) =&gt; get(config),
//  write: (get, set, update) =&gt;
//     set(config, typeof update === &#39;function&#39; ? update(get(config)) : update)
// }</code></pre>
<p>고유 키가 필요했던 Recoil과 다르게 Jotai는 별도 key가 필요없다. 
상태는 useAtomValue에서 찾아볼 수 있다.</p>
<h2 id="useatomvalue">useAtomValue</h2>
<p>다음은 Jotai의 useAtomValue 구현이다.</p>
<pre><code class="language-js">export function useAtomValue&lt;Value&gt;(
  atom: Atom&lt;Value&gt;,
  scope?: Scope,
): Awaited&lt;Value&gt; {
  const ScopeConteext = getScopeContext(scope)
  const scopeContainer = useContext(ScopeContext)
  const { s: store, v: versionFromProvider } = scopeContainer
  const getAtomValue = (version?: VersionObject) =&gt; {
    const atomState = store[READ_ATOM](atom, version)
    // ...    
}

  const [[version, valueFromReducer, atomFromReducer], rerenderIfChanged] = ((prev, nextVersion) =&gt; {
    const nextValue = getAtomValue(nextVersion)
    if (Object.is(prev[1], nextValue) &amp;&amp; prev[2] === atom) {
      return prev //bail out
    }
    return [nextVersion, nextValue, atom]
  },                                                         versionFromProvider,
   (initialVersion) =&gt; {
    const initialValue = getAtomValue(initialVersion)
    return [initialVersion, initialValue, atom]
  })

  let value =valueFromReducer
  if (atomFromReducer !== atom) {
    rerenderIfChanged(version)
    value = getAtomValue(version)
  }

useEffect(() =&gt; {
  const { v: versionFromProvider  = scopeContainer}
  if (versionFromProvider) {
    store[COMMIT_ATOM](atom, versionFromProvider)
  }

  const unsubscribe = store[SUBSCRIBE_ATOM](
    atom,
    rerenderIfChanged,
    versionFromProvider,
  )
  rerenderIfChanged(versionFromProvider)
  return unsubscribe
}, [store, atom, scopeContainer])
  //...
return value
}</code></pre>
<ul>
<li>여기서 눈여겨 봐야할 것은 usereducer로 반환하는 상태값은 3가지 (version, valueFromReducer, atomFromReducer)이다. </li>
<li>첫 번째는 store의 버전, 두 번째는 atom에서 get을 수행했을 때 반환되는 값, 세 번째는 atom 그 자체를 의미한다. Recoil과는 다르게, 컴포넌트 루트 레벨에서 Context가 존재하지 않아도 된다.</li>
<li>자바스크립트에서 객체만을 키로 가지는 WeakMap을 사용해 store에 atom 객체 그 자체를 키로 활용해 값을 저장한다.</li>
<li>rerenderIfChanged를 통해 최신 값으로 업데이트 해준다.<h2 id="useatom">useAtom</h2>
useState와 동일한 형태의 배열을 반환한다.
첫 번째로는 atom의 현재 값을 나타내는 useAtomValue 훅의 결과를 반환하며, 두 번째로는 useSetAtom 훅을 반환하는데, 이 훅은 atom을 수정할 수 있는 기능을 제공한다.</li>
</ul>
<h2 id="간단한-사용법">간단한 사용법</h2>
<p>다음 코드는 Jotai에서 간단한 상태를 선언하고, 만들어진 상태로부터 파생된 상태를 사용하는 예제다.</p>
<pre><code class="language-js">import { atom, useAtom, useAtomValue } from &#39;Jotai&#39;

const counterState =atom(0)

function Counter() {
const [, setCount] = useAtom(counterState)

function handleButtonClick() {
    setCount((count) =&gt; count +1 )
 }

  return (
    &lt;&gt;
        &lt;button onClick={handleButtonClick}&gt;+&lt;/button&gt;
    &lt;/&gt;
  )
}

const biggerThan10 = useAtomValue(isBiggerThan10)

return (
  &lt;&gt;
      &lt;h3&gt;{count}&lt;/h3&gt;
      &lt;p&gt;count is bigger than 10: {JSON.stringify(biggerThan10)}&lt;/p&gt;
  &lt;/&gt;
  )
}

export default function App() {
  return (
    &lt;&gt;
        &lt;Counter /&gt;
        &lt;Count /&gt;
    &lt;/&gt;
  )
}</code></pre>
<h2 id="특징">특징</h2>
<p>Recoil에 영감을 받았기에 유사하면서 한계점을 극복하기 위한 노력이 보인다.</p>
<ul>
<li>Recoil의 atom 개념을 도입하면서도 API가 간결하다.</li>
<li>Recoil에서는 atom에서 파생된 값을 만들기 위해서 seletor가 필요했지만, Jotai에서는 selector가 없이도 atom만으로 atom 값에서 또 다른 파생된 상태를 만들 수 있어 간결하다.</li>
<li>Jotai 자체도 타입스크립트로 작성돼 있어 타입 지원이 잘 되어 있으며, Flow로 작성되어 별도로 d.ts를 제공하는 Recoil 대비 장점으로 볼 수 있다.</li>
<li>리액트 18의 변경된 API 지원하며, 현재 v2.x 버전가지 정식으로 출시돼있어 실제 서비스하는 app에도 무리 없이 사용 가능하다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS_Deep_Dive_chapter48 : 모듈]]></title>
            <link>https://velog.io/@yhc-key/JSDeepDivechapter48-%EB%AA%A8%EB%93%88</link>
            <guid>https://velog.io/@yhc-key/JSDeepDivechapter48-%EB%AA%A8%EB%93%88</guid>
            <pubDate>Wed, 06 Mar 2024 00:09:08 GMT</pubDate>
            <description><![CDATA[<h1 id="chapter-48--모듈">chapter 48 : 모듈</h1>
<h2 id="모듈의-일반적-의미">모듈의 일반적 의미</h2>
<ul>
<li>모듈이란 애플리케이션을 구성하는 개별적 요소로서 재사용 가능한 코드 조각을 말한다. 일반적으로 모듈은 기능을 기준으로 파일 단위로 분리한다.   </li>
<li>이 때 모듈이 성립하려면 모듈은 자신만의 파일 스코프(모듈 스코프)를 가질 수 있어야 한다.  </li>
<li>모듈의 모든 자산은 캡슐화되어 다른 모듈에서 접근할 수 없다. 즉 개별적 존재로서 애플리케이션과 분리되어 존재한다.</li>
<li>하지만 쓰지 못하면 의미가 없으므로, <strong>모듈은 공개가 필요한 자산에 한정하여 명시적으로 선택적 공개가 가능하다. 이를 export 라 한다.</strong></li>
<li>공개된 모듈의 자산은 다른 모듈에서 재사용할 수 있다. 모듈 자산을 사용하는 모듈을 모듈 사용자라 한다.</li>
<li><strong>모듈 사용자는 모듈이 공개한 자산 중 일부 또는 전체를 선택해 자신의 스코프 내로 불러들여 재사용할 수 있다. 이를 import라 한다.</strong></li>
</ul>
<h2 id="js와-모듈">JS와 모듈</h2>
<p>자바스크릅티를 클라이언트 사이드, 즉 브라우저 환경에 국한하지 않고 범용적으로 사용하려는 움직임이 생기면서 모듈 시스템은 반드시 해결해야 하는 핵심 과제가 되었다. 이런 상황에서 제안된 것이 CommonJS와 AMD다.<br>JS 런타임 환경인 Node.js는 모듈 시스템의 사실상 표준인 CommonJS를 채택했고 독자적인 진화를 거쳐, 현재는 CommonJS 사양과 100% 동일하지는 않지만 기본적으로 CommonJS 사양을 따르고 있다.<br>즉 Node.js는 ECMAScript 표준 사양은 아니지만 모듈 시스템을 지원한다. 따라서 Node.js 환경에서는 파일별로 독립적인 파일 스코프(모듈 스코프)를 갖는다.</p>
<h2 id="es6-모듈esm">ES6 모듈(ESM)</h2>
<ul>
<li>IE를 제외한 브라우저에서 ES6 모듈을 사용할 수 있다.  </li>
<li>script 태그에 type=&quot;module&quot; 어트리뷰트를 추가하면 로드된 js 파일은 모듈로서 동작한다.  </li>
<li>ESM을 명확히 하기 위해 ESM의 파일 확장자는 mjs를 사용할 것을 권장한다.<pre><code class="language-js">&lt;script type=&quot;module&quot; src=&quot;app.mjs&quot;&gt;&lt;/script&gt;</code></pre>
</li>
</ul>
<h3 id="모듈-스코프">모듈 스코프</h3>
<p><strong>ESM은 파일 자체의 독자적인 모듈 스코프를 제공한다. 따라서 모듈 내에서 var 키워드로 선언한 변수는 더는 전역 변수가 아니며 window 객체의 프로퍼티도 아니다.</strong></p>
<pre><code class="language-js">// foo.mjs
// x 변수는 전역 변수가 아니며 window 객체의 프로퍼티도 아니다.
var x = &#39;foo&#39;;
console.log(x); // foo
console.log(window.x); // undeifined</code></pre>
<h3 id="export-키워드">export 키워드</h3>
<p>export 키워드는 선언문 앞에 사용한다. 이로써 변수, 함수, 클래스 등 모든 식별자를 export할 수 있다.</p>
<h3 id="import-키워드">import 키워드</h3>
<p>다른 모듈에서 공개한 식벼자를 자신의 모듈 스코프 내부로 로드하려면 import 키워드를 사용한다.</p>
<pre><code class="language-js">// app.mjs
// 같은 폴더 내의 lib.mjs 모듈이 export한 식별자 이름으로 import한다.
// ESM의 경우 파일 확장자를 생략할 수 없다.
// alias 가능
import { pi as PI, square, Person } from &#39;./lib.mjs&#39;;

console.log(PI);</code></pre>
<pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;body&gt;
        &lt;script type=&quot;module&quot; src=&quot;app.mjs&quot;&gt;&lt;/script&gt;
    &lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>위 예제의 app.mjs는 애플리케이션의 진입점이므로 반드시 script 태그로 로드해야 한다. 하지만 lib.mjs는 app.mjs의 import 문에 의해 로드되는 의존성이다. 따라서 lib.mjs는 script 태그로 로드하지 않아도 된다.   </p>
<p>1개만 export 할 때는 default를 사용할 수 있고, 이름 없이 하나의 값을 export한다. 이 경우 var, let, const 키워드는 사용할 수 없다.<br>default 키워드와 함께 export한 모듈은 {} 없이 임의의 이름으로 import한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS_Deep_Dive_chapter47 : 에러_처리]]></title>
            <link>https://velog.io/@yhc-key/JSDeepDivechapter-47-%EC%97%90%EB%9F%AC%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@yhc-key/JSDeepDivechapter-47-%EC%97%90%EB%9F%AC%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Wed, 06 Mar 2024 00:08:19 GMT</pubDate>
            <description><![CDATA[<h1 id="chapter-47--에러-처리">chapter 47 : 에러 처리</h1>
<h2 id="에러-처리의-필요성">에러 처리의 필요성</h2>
<p>에러가 발생하지 않는 코드 작성은 불가능하다. 발생한 에러에 대해 대처하지 않고 방치하면 프로그램은 강제 종료된다.</p>
<p>try...catch 문을 사용해 발생한 에러에 적절하게 대응하면 프로그램이 강제 종료되지 않고 계속햇거 코드 실행이 가능하다.</p>
<h2 id="trycatchfinally-문">try...catch...finally 문</h2>
<p>에러 처리 방법은 크게 두 가지가 있다.  </p>
<ul>
<li>querySelector나 Array#find 메서드처럼 예외적인 상황이 발생하면 반환하는 값(null 또는 -1)을 if 문이나 단축 평가 또는 옵셔널 체이닝 연산자를 통해 확인해서 처리하는 방법</li>
<li>에러 처리 코드를 미리 등록해 두고 에러가 발생하면 에러 처리 코드로 점프하도록 하는 방법  </li>
</ul>
<p>try...catch...finally 문은 두 번째 방법으로 이 방법을 에러 처리라고 한다.</p>
<pre><code class="language-js">try {
    // 실행할 코드
} catch (err) {
    // try 코드 블록에서 에러가 발생하면 이 코드 블록의 코드가 시행된다.
    // err에는 try 코드 블록에서 발생한 Error 객체가 전달
} finally {
    // 에러 발생과 상관없이 한 번 실행
}</code></pre>
<h2 id="error-객체">Error 객체</h2>
<p>Error 생성자 함수는 에러 객체를 생성. Error 생성자 함수에는 에러를 상세히 설명하는 에러 메시지를 인수로 전달할 수 있다.</p>
<pre><code class="language-js">const error = new Error(&#39;invalid&#39;);</code></pre>
<p>Error 생성자 함수가 생성한 에러 객체는 message 프로퍼티와 stack 프로퍼티를 가짐.<br>message : Error 생성자 함수에 인수로 전달한 에러 메시지<br>stack : 에러를 발생시킨 콜스택의 호출 정보를 나타내는 문자열. (디버깅 목적)</p>
<p>JS의 7가지 에러는 아래와 같다. 모두 Error.prototype을 상속받는다.</p>
<table>
<thead>
<tr>
<th align="center">생성자 함수</th>
<th align="center">인스턴스</th>
</tr>
</thead>
<tbody><tr>
<td align="center">Error</td>
<td align="center">일반적 에러 객체</td>
</tr>
<tr>
<td align="center">SyntaxError</td>
<td align="center">자바스크립트 문법에 맞지 않는 문을 해석할 때 발생하는 에러 객체</td>
</tr>
<tr>
<td align="center">ReferenceError</td>
<td align="center">참조할 수 없는 식별자를 참조했을 때 발생하는 에러 객체</td>
</tr>
<tr>
<td align="center">TypeError</td>
<td align="center">피연산자 또는 인수의 데이터 타입이 유효하지 않을 때 발생하는 에러 객체</td>
</tr>
<tr>
<td align="center">RangeError</td>
<td align="center">숫자값의 허용 범위를 벗어났을 때 발생하는 에러객체</td>
</tr>
<tr>
<td align="center">URIError</td>
<td align="center">encodeURI 또는 decodeURI 함수에 부적절한 인수를 전달했을 때 발생하는 에러 객체</td>
</tr>
<tr>
<td align="center">EvalError</td>
<td align="center">eval 함수에서 발생하는 에러 객체</td>
</tr>
</tbody></table>
<h2 id="throw-문">throw 문</h2>
<p>Error 생성자 함수로 에러 객체를 생성한다고 에러가 발생하는 것은 아니다. 즉 에러 객체 생성과 에러 발생은 의미가 다르다.</p>
<pre><code class="language-js">try {
    // 에러 객체를 생성한다고 에러가 발생하는 것은 아니다.
    new Error(&#39;something wrong&#39;);
} catch (error) {
    console.log(error);
}</code></pre>
<p>에러를 발생시키려면 try 코드 블록에서 throw 문으로 에러 객체를 던져야 한다.</p>
<pre><code class="language-js">// 외부에서 전달받은 콜백 함수를 n번만큼 반복 호출하는 코드의 에러 처리
const repeat = (n, f) =&gt; {
    // 매개변수 f에 전달된 인수가 함수가 아니면 TypeError를 발생
    if (typeof f !== &#39;function&#39;) throw new TypeError(&#39;f must be a function&#39;);

    for (var i = 0 ; i &lt; n; i++) {
        f(i); //i를 전달하면서 f를 호출
    }
};

try {
    repeat(2, 1);
} catch (err) {
    console.error(err);
}</code></pre>
<h2 id="에러의-전파">에러의 전파</h2>
<p>에러는 호출자 방향으로 전파된다. 
즉, 콜 스택의 아래 방향(실행 중인 실행 컨택스트가 푸시되기 직전에 푸시된 실행 컨텍스트 방향)으로 전파된다. 다음 예제를 살펴보자. </p>
<pre><code class="language-js">// ex)
coinst foo = () =&gt; {
    throw Error(&#39;foo에서 발생한 에러&#39;); // 4
}

const bar = () =&gt; {
    foo(); // 3
}

const baz = () =&gt; {
    bar(); // 2
}

try {
    baz();
} catch(err) {
    console.error(err);
}</code></pre>
<p>1에서 baz 함수를 호출하면 2에서 bar함수가 호출되고 3에서 foo 함수가 호출되고 foo 함수는 4에서 에러를 throw한다. 이때 foo 함수가 throw한 에러는 다음과 같이 호출자에게 전파되어 전역에서 캐치된다.<br><img src="https://velog.velcdn.com/images/yhc-key/post/2a540e6f-7826-437f-88fe-eb812d10dd65/image.png" alt=""></p>
<ul>
<li>이 때 throw된 에러를 캐치하여 적절히 대응하면 프로그램을 강제 종료시키지 않고 코드의 실행 흐름을 복구할 수 있다. throw된 에러를 어디에서도 캐치하지 않으면 프로그램은 강제 종료된다.  </li>
<li>주의할 것은 비동기 함수인 setTimeout, 프로미스 후속 처리 메서드의 콜백 함수는 호출자가 없다는 것이다.  </li>
<li>settTimeout or 프로미스 후속 처리 메서드의 콜백 함수는 태스크 큐나 마이크로태스크 큐에 일시 저장되었다가 콜 스택이 비면 이벤트 루프에 의해 콜 스택으로 푸시되어 실행. 이 때 콜 스택에 푸시된 콜백 함수의 실행 컨텍스트는 <strong>콜 스택의 가장 하부</strong>에 존재하게 된다. </li>
<li>따라서 에러를 전파할 호출자가 존재하지 않는다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS_Deep_Dive_chapter46 : 제너레이터와_async/await]]></title>
            <link>https://velog.io/@yhc-key/JSDeepDivechapter46-%EC%A0%9C%EB%84%88%EB%A0%88%EC%9D%B4%ED%84%B0%EC%99%80asyncawait</link>
            <guid>https://velog.io/@yhc-key/JSDeepDivechapter46-%EC%A0%9C%EB%84%88%EB%A0%88%EC%9D%B4%ED%84%B0%EC%99%80asyncawait</guid>
            <pubDate>Tue, 05 Mar 2024 00:49:12 GMT</pubDate>
            <description><![CDATA[<h1 id="chapter46--제너레이터와-asyncawait">chapter46 : 제너레이터와 async/await</h1>
<h2 id="제너레이터란">제너레이터란?</h2>
<p>ES6에서 도입된 제너레이터는 코드 블록의 실행을 일시 중지했다가 필요한 시점에 재개할 수 있는 특수한 함수다. 제너리에터와 일반 함수의 차이는 다음과 같다.</p>
<ul>
<li>제너레이터 함수는 함수 호출자에게 함수 실행의 제어권을 양도할 수 있다.</li>
<li>제너레이터 함수는 함수 호출자와 함수의 상태를 주고받을 수 있다.</li>
<li>제너레이터 함술르 호출하면 제너레이터 객체를 반환한다.</li>
</ul>
<h2 id="제너레이터-함수의-정의">제너레이터 함수의 정의</h2>
<p>제너레이터 함수는 function* 키워드로 선언한다. 그리고 하나 이상의 yield 표현식을 포함한다. 이것을 제외하면 일반 함수를 정의하는 방법과 같다.</p>
<pre><code class="language-js">// 제너레이터 함수 선언문
function* genDecFun() {
    yield 1;
}
// 제너레이터 함수 표현식
const genExpFunc = function* {
    yield 1;
};

// 제너레이터 메서드
const obj = {
    * genObjMethod() {
        yield 1;
    }
}

// 제너레이터 클래스 메서드
class MyClass {
    * genClsMethod() {
        yield 1;
    }
}</code></pre>
<p>에스터리스크(*)의 위치는 function 키워드와 함수 이름 사이라면 어디든지 상관없다. 하지만 일관성 유지를 위해 fucntion 키워드 바로 뒤에 붙이는 것을 권장한다.<br>제너레이터 화살표 함수 정의 x 이며 new 연산자와 함께 생성자 함수로 호출 할 수 없다.</p>
<h2 id="제너레이터-객체">제너레이터 객체</h2>
<p><strong>제너레이터 함수를 호출하면 일반 함수처럼 함수 코드 블록을 실행하는 것이 아니라 제너레이터 객체를 생성해 반환한다. 제너레이터 함수가 반환한 제너레이터 객체는 이터러블이면서 동시에 이터레이터다.</strong><br>다시 말해, 제너레이터 객체는 Symbol.iterator 메서드를 상속받는 이터러블이면서 value, done 프로퍼티를 갖는 이터레이터 리절트 객체를 반환하는 next 메서드를 소유하는 이터레이터다. </p>
<pre><code class="language-js">// 제너레이터 함수
function* genFunc() {
    yield 1;
    yield 2;
    yield 3;
}

//제너레이터 함수를 호출하면 제너레이터 객체를 반환
const generator = genFunc();</code></pre>
<p>제너레이터 객체는 next 메서드를 갖는 이터레이터지만 이터레이터에는 없는 return, throw 메서드를 갖는다. 제너레이터 객체의 세 개의 메서들르 호출하면 다음과 같이 동작한다.</p>
<ul>
<li>next 메서드를 호출하면 제너레이터 함수의 yield 표현식까지 코드 블록을 실행하고 yield된 값을 value 프로퍼티 값으로, false를 done 프로퍼티 값으로 갖는 이터레이터 리절트 객체를 반환한다.</li>
</ul>
<h2 id="제너레이터의-일시-중지와-재개">제너레이터의 일시 중지와 재개</h2>
<ul>
<li>일반 함수는 호출 이후 제어구너을 함수가 독점하지만 제너레이터는 함수 호출자에게 제어권을 양도(yield)하여 필요한 시점에 함수 실행을 재개할 수 있다.  </li>
<li><strong>yield 키워드는 제너레이터 함수의 실행을 일시 중지시키거나 yield 키워드 뒤에 오는 표현식의 평가 결과를 제너레이터 함수 호출자에게 반환한다.</strong></li>
<li><strong>제너레이터 객체의 next 메서드를 호출하면 yield 표현식까지 실행되고 일시 중지(suspend)된다. 이때 함수의 제어권이 호출자로 양도된다.</strong></li>
<li>이 때 제너레이터 객체의 next 메서드는 value, done 프로퍼티를 갖는 이터레이터 리절트 객체를 반환한다.</li>
<li>next 메서드가 반환한 이터레이러 리절트 객체의 value 프로퍼티에는 yield 표현식에서 yield된 값이 할당되고 done 프로퍼티에는 제너레이터 함수가 끝까지 실행되었음을 나타내는 true 가 할당된다.</li>
<li>이터레이터 next 메서드와 달리 제너레이터 객체의 next 메서드에는 인수를 전달할 수 있다.</li>
<li>제너레이터 객체의 next 메서드에 전달한 인수는 제너레이터 함수의 yield 표현식을 할당받는 변수에 할당된다.</li>
<li>제너레이터를 활용하면 비동기 처리를 동기 처리처럼 구현할 수 있다.</li>
</ul>
<h2 id="제너레이터의-활용">제너레이터의 활용</h2>
<h3 id="이터러블의-구현">이터러블의 구현</h3>
<p>제너레이터 함수를 사용하면 이터레이션 프로토콜을 준수해 이터러블을 생성하는 방식보다 간단히 이터러블을 구현할 수 있다.</p>
<pre><code class="language-js">// ex)
// 무한 이터러블 생성 함수
const infiniteFibonacci = ( function () {
    let [pre, cur] = [0, 1];

    return {
        [Symbol.iterator]() { reteurn this; },
        next() {
            [pre, cur] = [cur, pre + cur]; 

            return { value : cur}
        }
    };
}());

// infiniteFibonacci는 무한 이터러블이다.
for (const num of infiniteFibonacci) {
    if (num &gt; 10000) break;
    console.log(num);
}</code></pre>
<pre><code class="language-js">// 제너레이터 사용 ex)
// 무한 이터러블 생성 제너레이터 함수
const infiniteFibonacci = (function* () {
    let [pre, cur] = [0, 1];

    while(true) {
        [pre, cur] = [cur, pre + cur];
        yield cur;
    }
}());

for (const num of infiniteFibonacci) {
    if (num &gt; 10000) break;
    console.log(num);
}</code></pre>
<h3 id="비동기-처리">비동기 처리</h3>
<p>이런 특성을 활용하면 프로미스를 사용한 비동기 처리를 동기처럼 구현할 수 있다.</p>
<pre><code class="language-js">const fetch = require(&#39;node-fetch&#39;);

const async = generatorFunc =&gt; {
    const generator = generatorFunc();

    const onResolved = arg =&gt; {
        const result = generator.next(arg);

        return result.done
            ? result.value : result.value.then(res =&gt; onResolved(res));
    };
    return onResolved;
};

(async(function* fetchTodo() {
    const url = &#39;https://~&#39;;

    const response = yield fetch(url);
    const todo = uield response.json();
    console.log(tood);
    // {userId : 1, id : 1, title : &#39;delectus aut autem&#39;, completed: false}
})());</code></pre>
<p>혹시 제너레이터 실행기가 필요하다면 직접 구현하는 것보다 co 라이브러리를 사용하도록 하자.</p>
<h2 id="asyncawait">async/await</h2>
<p>제너레이터보다 간단하고 가독성 좋게 비동기 처리를 동기 처리처럼 동작하도록 구현할 수 있는 async/awiat가 도입되었다.<br>이를 이용하면 프로미스의 후속 처리 메서드 없이 마치 동기 처리처럼 프로미스가 처리 결과를 반환하도록 구현할 수 있다. </p>
<h3 id="async-함수">async 함수</h3>
<p>await 함수는 반드시 async 함수 내부에서 사용해야 한다. async 함수는 async 키워드를 사용해 정의하며 언제나 프로미스를 반환한다. </p>
<h3 id="await-키워드">await 키워드</h3>
<ul>
<li>await 키워드는 프로미스가 settled 상태가 될 때까지 대기하다가 settled 상태가 되면 프로미스가 resolve한 처리 결과를 반환한다. </li>
<li>await 키워드는 반드시 프로미스 앞에서 사용해야 함  </li>
<li>비동기 처리가 완료될 때까지 순차적으로 기다릴 필요가 없음 -&gt; 1개의 await + Promise.all등을 사용</li>
<li>비동기 처리가 완료될 때까지 순차적으로 기다려야 함 -&gt; 여러 개의 await으로 순차적 처리</li>
</ul>
<h3 id="에러-처리">에러 처리</h3>
<ul>
<li>비동기 함수의 콜백 함수를 호출한 것은 비동기 함수가 아니기 때문에 try...catch 문을 사용해 에러 캐치를 할 수 없다.</li>
<li>async/await 에러 처리는 try...catch 사용가능. 프로미스 반환 비동기 함수는 명시적으로 호출할 수 있기 때문</li>
<li>**async 함수 내에서 catch 문을 사용해서 에러 처리를 하지 않으면 async 함수는 발생한 에러를 reject하는 프로미스를 반환한다. 따라서 async 함수를 호출하고 Promise.prototype.catch 후속 처리 메서드를 사용해 에러를 캐치할 수도 있다.<pre><code class="language-js">const fetch = require(&#39;node-fetch&#39;);
</code></pre>
</li>
</ul>
<p>const foo = async () =&gt; {
    const wrongUrl = &quot;https://~&quot;;</p>
<pre><code>const response = await fetch(wrongUrl);
const data = await response.json();
return data;</code></pre><p>};</p>
<p>foo().then(console.log).catch(console.error); // TypeError: Failed to fetch
```</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS_Deep_Dive_chapter34 : 이터러블
]]></title>
            <link>https://velog.io/@yhc-key/JSDeepDivechapter34-%EC%9D%B4%ED%84%B0%EB%9F%AC%EB%B8%94</link>
            <guid>https://velog.io/@yhc-key/JSDeepDivechapter34-%EC%9D%B4%ED%84%B0%EB%9F%AC%EB%B8%94</guid>
            <pubDate>Tue, 05 Mar 2024 00:27:07 GMT</pubDate>
            <description><![CDATA[<h1 id="chapter-34--이터러블">chapter 34 : 이터러블</h1>
<h2 id="이터레이션-프로토콜">이터레이션 프로토콜</h2>
<p>ES6에서 도입된 이터레이션 프로토콜은 순회 가능한 데이터 컬렉션(자료구조)을 만들기 위해 ECMAScript 사양에 정의하여 미리 약속한 규칙이다.</p>
<p>ES6 이전의 순회 가능한 데이터 컬렉션, 즉 배열, 문자열, 유사 배열 객체, DOM 컬렉션 등은 통일된 규약없이 각자 나름의 구조를 가지고 for 문, for ...in 문, forEach 메서드 등 다양한 방법으로 순회할 수 있었다.<br>ES6에서는 순회 가능한 데이터 컬렉션을 이터레이션 프로토콜을 준수하는 이터러블로 통일하여 for...of 문, 스프레드 문법, 배열 디스트럭처링 할당의 대상으로 사용할 수 있도록 일원화했다.</p>
<ul>
<li>이터러블 프로토콜 : Well-known Symbol.iterator를 프로퍼티 키로 사용한 메서드를 직접 구현하거나 프로토타입 체인을 통해 상속받은 Symbol.iterator 메서드를 호출하면 이터레이터 프로토콜을 준수한 이터레이터를 반환한다. 이러한 규약을 이터러블 프로토콜이라 하며, 이터러블 프로토콜을 준수한 객체를 이터러블이라 한다. <strong>이터러블은 for...of 문으로 순회할 수 있으며 스프레드 문법과 배열 디스트럭처링 할당의 대상으로 사용할 수 있다.</strong></li>
<li>이터레이터 프로토콜 : 이터러블의 Symbol.iterator 메서드를 호출하면 이터레이터 프로토콜을 준수한 이터레이터를 반환한다. 이터레이터는 next 메서드를 소유하며 next 메서드를 호출하면 이터러블을 순회하며 <strong>value와 done 프로퍼티를 갖는 이터레이터 리절트 객체를 반환</strong>한다. 이러한 규약을 이터레이터 프로토콜이라 하며, <strong>이터레이터 프로토콜을 준수한 객체를 이터레이터라 한다.</strong> 이터레이터는 이터러블의 요소를 탐색하기 위한 포인터 역할을 한다.</li>
</ul>
<h3 id="이터러블">이터러블</h3>
<p>이터러블 프로토콜을 준수한 객체를 이터러블이라 한다. 즉, 이터러블은 Symbol.iterator를 프로퍼티 키로 사용한 메서드를 직접 구현하거나 프로토타입 체인을 통해 상속받은 객체를 말함.
Symbol.iterator 메서드를 직접구현하지 않거나 상속받지 않은 일반 객체는 for...of 문으로 순회할 수 없으며 스프레드 문법과 배열 디스트럭처링 할당의 대상으로 사용할 수 없다.<br>TC39 프로세스의 stage 4(Finished) 단계에서 스프레드 프로퍼티 제안은 일반 객체에 스프레드 문법의 사용을 허용한다.</p>
<h3 id="이터레이터">이터레이터</h3>
<ul>
<li>이터러블의 Symbol.iterator 메서드를 호출하면 이터레이터 프로토콜을 준수한 이터레이터를 반환. <strong>이터러블의 Symbol.iterator 메서드가 반환한 이터레이터는 next 메서드를 갖는다.</strong></li>
<li>next 메서드는 이터러블의 각 요소를 순회하기 위한 포인터의 역할을 함.</li>
<li>즉 next 메서드를 호출하면 이터러블을 순차적으로 한 단계씩 순회하며 순회 결과를 나타내는 이터레이터 리절트 객체를 반환</li>
</ul>
<pre><code class="language-js">const array = [1, 2, 3];

const iterator = aray[Symbol.iterator]();

// next 메서드를 호출하면 이터러블을 순회하며 순회 결과를 나타내는 이터레이터 리절트 객체를 반환
// 이터레이터 리절트 객체는 value와 done 프로퍼티를 갖는 객체
console.log(iterator.next()); // {value: 1, done: false}
console.log(iterator.next()); // {value: 2, done: false}
console.log(iterator.next()); // {value: 3, done: false}
console.log(iterator.next()); // {value: undefined, done: true}</code></pre>
<p>done 프로퍼티는 이터러블의 순회 완료 여부를 나타냄</p>
<h2 id="빌트인-이터러블">빌트인 이터러블</h2>
<p>js는 이터레이션 프로토콜을 준수한 객체인 빌트인 이터러블을 제공<br>다음의 표준 빌트인 객체들은 빌트인 이터러블이다.
| 빌트인 이터러블 | Symbol.iterator 메서드 |
|:-------:|:--------:|
| Array | Array.prototype[Symbol.iterator] |
| Map | Map.prototype[Symbol.iterator] |
| Set | Set.prototype[Symbol.iterator] |
| TypedArray | TypedArray.prototype[Symbol.iterator] |
| arguments| arguments[Symbol.iterator] |
| DOM 컬렉션 | NodeList.prototype[Symbol.iterator] |
| DOM 컬렉션 | HTMLCollection.prototype[Symbol.iterator] |</p>
<h2 id="for--of-문">for ... of 문</h2>
<p>이터러블을 순회하면서 이터러블의 요소를 변수에 할당<br>for ... of 문은 for ... in 문의 형식과 매우 유사<br>for... in : 프로퍼티 어트리뷰트 [[Enumerable]] 값이 true 인 프로퍼티를 순회하며 열거 키가 심벌은 열거 x
for... of : 내부적으로 이터레이터의 next 메서드를 호출하여 이터러블을 순회하며 next 메서드가 반환한 이터레이터 리절트 객체의 value 프로퍼티 값을 for...of 문의 변수에 할당.</p>
<h2 id="이터러블과-유사-배열-객체">이터러블과 유사 배열 객체</h2>
<ul>
<li>length, for 문 순회, 배열처럼 인덱스로 프로퍼티 값에 접근가능</li>
<li>유사 배열 객체는 이터럽르이 아닌 일반 객체 =&gt; Symbol.iterator 메서드가 없기 때문에 for ... of문으로 순회 불가</li>
<li>arguments, NodeList, HTMLCollection은 유사 배열 객체이면서 이터러블</li>
<li>모든 유사 배열 객체가 이터러블인 것은 아니지만 Array.from을 이용해 유사 배열 객체 or 이터러블을 인수로 전달받아 배열로 변환하여 반환할 수 있다.</li>
</ul>
<h2 id="이터레이션-프로토콜의-필요성">이터레이션 프로토콜의 필요성</h2>
<p>이터레이션 프로토콜은 데이터 소비자와 데이터 공급자를 연결하는 인터페이스 역할을 함</p>
<h2 id="사용자-정의-이터러블">사용자 정의 이터러블</h2>
<h3 id="사용자-정의-이터러블-구현">사용자 정의 이터러블 구현</h3>
<p>피보나치 수열을 구현한 사용자 정의 이터러블 ex)</p>
<pre><code class="language-js">const fibonacci = {
  [Symbol.iterator]() {
    let [pre, cur] = [0, 1];
    const max = 10; //수열 최대값

    // Symbol.iterator 메서드는 next 메서드를 소유한 이터레이터를 반환해야 하고
    // next  메서드는 이터레이터 리절트 객체를 반환해야 한다.

    return {
      next() {
        [pre, cur] = [cur, pre + cur];
        // 이터레이터 리절트 객체 반환
        return { value: cur, done: cur &gt;= max };
      },
    };
  },
};

// 이터러블인 객체를 순회할 때마다 next 메서드가 호출
for (const num of fibonacci) {
  console.log(num); // 1 2 3 5 8
}</code></pre>
<h3 id="이터러블을-생성하는-함수">이터러블을 생성하는 함수</h3>
<pre><code class="language-js">const fibonacci = function (max) {
  let [pre, cur] = [0, 1];

  // Symbol.iterator 메서드는 next 메서드를 소유한 이터레이터를 반환

  return {
    [Symbol.iterator]() {
      return {
        next() {
          [pre, cur] = [cur, pre + cur];
          // 이터레이터 리절트 객체 반환
          return { value: cur, done: cur &gt;= max };
        },
      };
    },
  };
};

// 수열 최대값을 인수로 전달하면서 호출
for (const num of fibonacci(10)) {
  console.log(num); // 1 2 3 5 8
}</code></pre>
<h3 id="무한-이터러블과-지연-평가">무한 이터러블과 지연 평가</h3>
<p>무한 수열 구현 ex)</p>
<pre><code class="language-js">const fibonacciFunc = function () {
  let [pre, cur] = [0, 1];

  return {
    [Symbol.iterator]() {
      return this;
    },
    next() {
      [pre, cur] = [cur, pre + cur];
      return { value: cur };
    },
  };
};

for ( const num of fibonacciFunc()) {
    if (num &gt; 10000) break;
    console.log(num);
}

// 배열 디스트럭처링 할당을 통해 무한 이러터블에서 3개의 요소만 취득한다.
const [f1, f2, f3] = fibonacciFunc();
console.log(f1, f2, f3); // 1 2 3</code></pre>
<p>위 예제의 이터러블은 <strong>지연 평가(lazy evaluation)</strong> 를 통해 데이터를 생성<br>이처럼 지연 평가를 사용하면 불필요한 데이털르 미리 생성하지 않고 필요한 데이터를 필요한 순간에 생성하므로 빠른 실행 속도를 기대할 수 있고 불필요한 메모리를 소비하지 않으며 무한도 표현할 수 있다는 장점이 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS_Deep_Dive_chapter26 : ES6_함수의_추가_기능]]></title>
            <link>https://velog.io/@yhc-key/JSDeepDivechapter26-ES6%ED%95%A8%EC%88%98%EC%9D%98%EC%B6%94%EA%B0%80%EA%B8%B0%EB%8A%A5</link>
            <guid>https://velog.io/@yhc-key/JSDeepDivechapter26-ES6%ED%95%A8%EC%88%98%EC%9D%98%EC%B6%94%EA%B0%80%EA%B8%B0%EB%8A%A5</guid>
            <pubDate>Tue, 05 Mar 2024 00:25:56 GMT</pubDate>
            <description><![CDATA[<h1 id="chapter-26--es6-함수의-추가-기능">chapter 26 : ES6 함수의 추가 기능</h1>
<h2 id="함수의-구분">함수의 구분</h2>
<p>ES6 이전까지 JS의 함수는 별다른 구분 없이 사용했음.<br>JS 함수는 </p>
<ul>
<li>일반적인 함수로 호출</li>
<li>new 연산자와 함께 호출하여 인스턴스 생성할 수 있는 생성자 함수로서 호출</li>
<li>객체 바인딩되어 메서드로서 호출</li>
<li>객체 바인딩되어 메서드로서 호출</li>
</ul>
<p>용도가 많아 실수 유발 및 성능 면에서도 손해이다.<br>이러한 문제를 해결하기 위해 ES6에서는 함수를 사용 먹적에 따라 세 가지 종류로 명확히 구분했다.  </p>
<table>
<thead>
<tr>
<th>ES6함수 의 구분</th>
<th align="center">CONSTRUCTOR</th>
<th align="center">prototype</th>
<th align="center">super</th>
<th align="center">arugments</th>
</tr>
</thead>
<tbody><tr>
<td>일반 함수(normal)</td>
<td align="center">O</td>
<td align="center">O</td>
<td align="center">X</td>
<td align="center">O</td>
</tr>
<tr>
<td>메서드(Method)</td>
<td align="center">X</td>
<td align="center">X</td>
<td align="center">O</td>
<td align="center">O</td>
</tr>
<tr>
<td>화살표 함수(normal)</td>
<td align="center">X</td>
<td align="center">X</td>
<td align="center">X</td>
<td align="center">X</td>
</tr>
</tbody></table>
<p>일반 함수는 constructor 이지만 ES6의 메서드와 화살표 함수는 non-constructor다. </p>
<h2 id="메서드">메서드</h2>
<p>ES6 사양에서 메서드는 메서드 축약 표현으로 정의된 함수만을 의미한다.</p>
<pre><code class="language-js">const obj = {
    x: 1,
    // foo는 메서드다.
    foo() { return this.x; },
    // bar에 바인딩된 함수는 메서드가 아닌 일반 함수다.
    bar: function() { return this.x; }
};

console.log(obj.foo()); // 1
console.log(ojb.foo()); // 1</code></pre>
<p>ES6 사양에서 정의한 메서드는 인스턴스를 생성할 수 없는 non-constructor이다. 따라서 생성자 함수로서 호출이 불가하다.<br>ES6 메서드는 자신을 바인딩한 객체를 가리키는 내부 슬롯 [[HomeObject]]를 갖는다. super 참조는 내부 슬롯 [[HomeObejct]]를 사용하여 수퍼클래스의 메서드를 참조하므로 내부 슬롯 [[HomeObject]]를 갖는 ES6 메서드는 super 키워드를 사용할 수 있다.</p>
<h2 id="화살표-함수">화살표 함수</h2>
<p>화살표 함수(arrow function)은 function 키워드 대신 화살표(=&gt;, fat arrow)를 사용하여 기존의 함수 정의 방식보다 간략하게 함수를 정의할 수 있다.<br>특히 화살표 함수는 콜백 함수 내부에서 this가 전역 객체를 가리키는 문제를 해결하기 위한 대안으로 유용하다.</p>
<h3 id="화살표-함수-정의">화살표 함수 정의</h3>
<h4 id="함수-정의">함수 정의</h4>
<p>함수 선언문 x, 함수 표현식만 가능</p>
<pre><code class="language-js">const multiply = (x, y) =&gt; x * y;
multiply(2, 3); // -&gt; 6</code></pre>
<h4 id="매개변수-선언">매개변수 선언</h4>
<p>매개변수가 여러 개인 경우 소괄호 ()안에 매개변수를 선언</p>
<pre><code class="language-js">const arrow = (x, y) =&gt; { ... };
// 1개인 경우 소괄호 생략 가능
const arrow = x =&gt; { ... };
// 매개변수가 없는 경우 소괄호 () 생략 x</code></pre>
<h4 id="함수-몸체-정의">함수 몸체 정의</h4>
<p>하나의 문으로 구성된다면 함수 몸체를 감싸는 중괄호 {}를 생략할 수 있다.<br>함수 몸체를 감싸는 중괄호 {}를 생략한 경우 함수 몸체 내부의 문이 표현식이 아닌 문이라면 에러가 발생한다. 표현식이 아닌 문은 반환할 수 없기 때문  </p>
<pre><code class="language-js">const arrow = () =&gt; const x =1; // SyntaxError: Unexpected toekn &#39;const&#39;

//위 표현은 다음과 같이 해석됨
const arrow = () =&gt; { return const x = 1;};</code></pre>
<p>따라서 함수 몸체가 하나의 문으로 구성된다 해도 함수 몸체의 문이 표현식이 아닌 문이라면 중괄호 생략 불가  </p>
<ul>
<li>객체 리터럴 반환의 경우 소괄호 ()로 감싸주어야 함. 감싸 주지 않으면 {}를 함수 몸체를 감싸는 중괄호 {}로 해석함.  </li>
<li>화살표 함수도 즉시 실행함수로 사용할 수 있음</li>
<li>일급객체이므로 고차함수에 인수 전달할 수 있으며 표현이 간결하고 가독성이 좋다. 따라서 콜백 함수 정의 시 유용</li>
</ul>
<h3 id="화살표-함수와-일반-함수의-차이">화살표 함수와 일반 함수의 차이</h3>
<ol>
<li>화살표 함수는 인스턴스를 생성할 수 없는 non-constructor다  </li>
</ol>
<p>화살표 함수는 인스턴스를 생성할 수 없으므로 prototype 프러피타가 없고 프로토타입도 생성하지 않는다.</p>
<ol start="2">
<li>중복된 매개변수 이름을 선언할 수 없다.</li>
<li>화살표 함수는 함수 자체의 this, arguments, super, new.target 바인딩을 갖지 않는다.  </li>
</ol>
<p>스코프 체인을 통해 상위 스코프를 참조. 만약 화살표 함수 중첩시 스코프 체인상 가장 가까운 상위 함수중에서 화살표 함수가 아닌 함수를 참조한다.</p>
<h3 id="this">this</h3>
<p>화살표 함수의 this는 일반 함수의 this와 다르게 동작. 이는 &quot;콜백 함수 내부의 this 문제&quot;, 즉 콜백 함수 내부의this가 외부 함수의 this와 다르기 때문에 발생하는 문제를 해결하기 위해 의도적으로 설계된 것이다.</p>
<p>과거 콜백 함수 내부의 this 문제는 다음과 같이 해결했다.</p>
<ol>
<li>add 메서드를 호출한 prefixer 객체를 가리키는 this를 일단 회피시킨 후에 콜백 함수 내부에서 사용</li>
<li>객체를 가리키는 this를 명확하게 지정</li>
<li>bind 메서드를 사용하여 this 바인딩</li>
</ol>
<ul>
<li>ES6 부터는 화살표 함수를 사용하여 콜백 함수 내부의 this 문제를 해결할 수 있다.  </li>
<li><strong>화살표 함수는 함수 자체의 this 바인딩을 갖지않는다. 따라서 화살표 함수 내부에서 this를 참조하면 상위 스코프의 this를 그대로 참조하는데, 이를 lexical this라 한다.</strong>  </li>
<li>ES6 메서드가 아닌 일반적인 의미의 메서드를 화살표 함수로 정의하는 것은 피해야 한다. 내부 this 가 아닌 상위 스코프의 this를 찾게 되고, 이는 전역 등 잘못된 곳을 참조할 수 있다.</li>
</ul>
<h3 id="super">super</h3>
<p>화살표 함수는 함수 자체의 super 바인딩을 갖지 않는다. 따라서 똑같이 상위 스코프의 super를 참조한다.  </p>
<h3 id="arguments">arguments</h3>
<p>함수 자체의 arguments 바인딩을 갖지 않음. 따라서 this와 마찬가지로 상위 스코프의 arguments를 참조</p>
<h2 id="rest-파라미터">Rest 파라미터</h2>
<h3 id="기본-문법">기본 문법</h3>
<p>Rest 파라미터(나머지 매개변수)는 매개변수 이름 앞에 세개의 점 ...을 붙여서 정의한 매개변수를 의미한다.<br>Rest 파라미터는 함수에 전달된 인수들의 목록을 배열로 전달받는다. </p>
<pre><code class="language-js">function foo(...rest) {
    console.log(rest);
}

foo(1, 2, 3, 4, 5);</code></pre>
<p>Rest 파라미터는 이름 그대로 먼저 선언된 매개변수에 할당된 인수를 제외한 나머지 인수들로 구성된 배열이 할당된다. 따라서 Rest 파라미터는 반드시 마지막 파라미터이어야 한다. 또한 한 개만 선언할 수 있다.</p>
<h3 id="rest-파라미터와-arguments-객체">Rest 파라미터와 arguments 객체</h3>
<ul>
<li>ES5에서는 arguments 객체를 활용하여 인수를 전달받았다. 하지만 유사 배열 객체이기 때문에 call, apply 메서드를 사용해 객체를 배열로 반환해야 하는 번거로움이 있었다.  </li>
<li>ES6에서는 rest 파라미터의 사용으로 arguments 객체를 배열로 변환하는 번거로움을 피할 수 있다.</li>
<li>화살표 함수는 함수 자체의 arguments 객체를 갖지 않으므로 반드시 Rest 파라미터를 사용해야 한다.</li>
</ul>
<h2 id="매개변수-기본값">매개변수 기본값</h2>
<ul>
<li>ES6에서 도입된 매개변수 기본값을 사용하면 함수 내에서 수행하던 인수 체크 및 초기화를 간소화할 수 있다.</li>
<li>인수를 전달하지 않은 경우와 undefined를 전달한 경우에만 유효하다. </li>
<li>Rest에는 기본값을 지정할 수 없다.</li>
<li>객체의 length 프로퍼티와 arguments 객체에는 아무런 영향을 주지 않는다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS_Deep_Dive_chapter25 : 클래스]]></title>
            <link>https://velog.io/@yhc-key/JSDeepDivechapter25-%ED%81%B4%EB%9E%98%EC%8A%A4</link>
            <guid>https://velog.io/@yhc-key/JSDeepDivechapter25-%ED%81%B4%EB%9E%98%EC%8A%A4</guid>
            <pubDate>Tue, 05 Mar 2024 00:24:55 GMT</pubDate>
            <description><![CDATA[<h1 id="chapter-25--클래스">chapter 25 : 클래스</h1>
<h2 id="클래스는-프로토타입의-문법적-설탕인가">클래스는 프로토타입의 문법적 설탕인가?</h2>
<p>자바스크립트는 프로토타입 기반 객체지향 언어다.<br>ES5에서는 클래스 없이도 다음과 같이 생성자 함수와 프로토 타입을 통해 객체지향 언어의 상속을 구현할 수 있다.<br>단, 클래스와 생성자 함수는 모두 프로토타입 기반의 인스턴스를 생성하지만 정확히 동일하게 동작하지 않는다.<br>다음과 같이 몇 가지 차이가 있다.  </p>
<ol>
<li>클래스를 new 연산자 없이 호출하면 에러가 발생한다. 하지만 생성자 함수를 new 연산자 없이 호출하면 일반 함수로서 호출된다.</li>
<li>클래스는 상속을 지원하는 extends와 super 키워드를 제공한다. 하지만 생성자 함수는 지원하지 않는다.</li>
<li>클래스는 호이스팅이 발생하지 않는 것처럼 동작한다. 하지만 함수 선언문으로 정의된 생성자 함수는 함수 호이스팅이 함수 표현식으로 정의한 생성자 함수는 변수 호이스팅이 발생한다.</li>
<li>클래스 내의 모든 코드에는 암묵적으로 strict mode가 지정되어 실행되며 strict mode 해제가 불가능하다.</li>
<li>클래스의 constructor, 프로토타입 메서드, 정적 메서든느 모두 프로퍼티 어트리뷰트 [[Enumerable]]의 값이 false다. 다시 말해 열겨되지 않는다.</li>
</ol>
<p>따라서 클래스를 단순한 문법적 설탕이라고 보기보다는 새로운 객체 생성 메커니즘으로 보는것이 좀 더 합당하다.</p>
<h2 id="클래스-정의">클래스 정의</h2>
<p>클래스는 class 키워드를 사용하여 정의한다. 파스칼 케이스가 일반적이지만 사용하지 않아도 에러가 발생하지는 않는다.<br>ex)</p>
<pre><code class="language-js">class Person {}</code></pre>
<p>일반적 이지는 않지만 함수와 마찬가지로 표현식으로 클래스를 정의할 수도 있다.</p>
<pre><code class="language-js">//익명 클래스 표현식
const Person = class {};
//기명 클래스 표현식
const Person = class MyClass {};</code></pre>
<p>클래스를 표현식으로 정의할 수 있다는 것은 곧 일급 객체라는 것을 의미한다. 즉 다음과 같은 특징을 갖는다.</p>
<ul>
<li>무명의 리터럴로 생성할 수 있다. 즉 런타임에 생성 가능하다.</li>
<li>변수나 자료구조에 저장할 수 있다.</li>
<li>함수의 매개변수에게 전달할 수 있다.</li>
<li>함수의 반환값으로 사용할 수 있다.</li>
<li>즉 클래스는 함수다.</li>
</ul>
<p>클래스 몸체에는 0개 이상의 메서드만 정의할 수 있다. constructor, 프로토타입 메서드, 정적 메서드 세 가지가 가능하다.  </p>
<pre><code class="language-js">class Person {

    constructor(name) {
        this.name = name;
    }

    sayHi() {
        console.log(`Hi! My name is ${this.name}`)
    }

    static sayHello() {
        console.log(&quot;Hello&quot;)
    }
}

const me = new Person(&#39;Lee&#39;);

console.log(me.name);

me.sayHi();

Person.sayHello();</code></pre>
<p><img src="https://velog.velcdn.com/images/yhc-key/post/06c9f0fb-0ef3-4df5-8ad2-de4adcc5ce00/image.png" alt=""></p>
<h2 id="클래스-호이스팅">클래스 호이스팅</h2>
<ul>
<li>클래스는 함수로 평가된다. </li>
<li>클래스 선언문으로 정의한 클래스는 함수 선언문과 같이 소스코드 평가 과정, 즉 런타임 이전에 먼저 평가되어 함수 객체를 생성 (constructor)</li>
<li>함수 객체를 생성하는 시점에 프로토타입도 더불어 생성(프로토타입과 생성자 함수는 쌍으로 존재)</li>
<li>클래스는 클래스 정의 이전에 참조 불가</li>
</ul>
<pre><code class="language-js">const Person = &#39;&#39;;

{
    // 호이스팅 발생 x 시 &#39;&#39;이 출력되어야 한다.
    console.log(Person);
    // ReferenceError : Cannot access &#39;Person&#39; before initialization

    class Person {}
}</code></pre>
<p>이와 같이 호이스팅은 발생하나 let, const 처럼 TDZ에 빠지기 때문에 호이스팀이 발생하지 않는 것처럼 동작한다.</p>
<h2 id="인스턴스-생성">인스턴스 생성</h2>
<p>클래스는 생성자 함수이며 new 연산자와 함께 호출되어 인스턴스를 생성</p>
<pre><code class="language-js">class Person {}

const me = new Person();
console.log(me); // Person {}</code></pre>
<p>함수는 new 연산자 사용 여부에 따라 일반 함수 or 생성자 함수이나, 클래스는 인스턴스 생성이 유일한 존재 이유이므로 반드시 new 연산자와 함께 호출해야 함.</p>
<h2 id="메서드">메서드</h2>
<p>클래스 몸체에서 정의할 수 있는 메서드는 constructor, 프로토타입 메서드, 정적 메서드 세 가지 이다.</p>
<h3 id="constructor">constructor</h3>
<ul>
<li><p>인스턴스 생성 및 초기화 하기 위한 특수 메서드다. 이름 변경이 불가능하다.</p>
<pre><code class="language-js">class Person {

  constructor(name) {
      this.name = name;
  }
}</code></pre>
</li>
<li><p>constructor 내부의 this는 생성자 함수와 마찬가지로 클래스가 생성한 인스턴스를 가리킨다.</p>
</li>
<li><p>constructor는 메서드로 해석되는 것이 아니라 클래스가 평가되어 생성한 함수 객체 코드의 일부가 된다.</p>
<pre><code>클래스의 constructor 메서드와 프로토타입의 constructor 프로퍼티는 이름은 같지만 관련이 없다.  
프로토타입의 constructor 프로퍼티는 모든 프로토타입이 가지고 잇는 프로퍼티이며, 생성자 함수를 가리킨다.</code></pre></li>
<li><p>constructor는 클래스 내 최대 한 개만 존재할 수 있으며, 생략 가능하다. 생략시 빈 constructor에 의해 빈 객체를 생성한다.</p>
</li>
<li><p>인스턴스 초기화시 constructor 내부의 this를 통해 초기화 하기 대문에 constructor는 필수이다.</p>
</li>
<li><p>constructor는 별도의 반환문을 가지지 않아야 한다.</p>
</li>
<li><p>constructor 내부에서 명시적으로 this가 아닌 값을 반환하면 안된다. 만일 반환시 원시값 -&gt; 무시후 this 반환, this가 아닌 객체시 return 문 명시한 객체가 반환된다.</p>
</li>
</ul>
<h3 id="프로토타입-메서드">프로토타입 메서드</h3>
<p>생성자 함수 사용하여 인스턴스 생성 후 프로토타입 메서드 생성을 위해서는 다음과 같이 해야한다.</p>
<pre><code class="language-js">function Person(name) {
    this.name = name;
}

Person.prototype.sayHi = function () {
    console.log(`Hi! My name is ${this.name}`);
};

const me = new Person(&#39;Lee&#39;);
me.sayHi();</code></pre>
<ul>
<li>클래스 몸체에서 정의한 메서드는 자동으로 프로토타입 메서드가 된다.</li>
<li>생성자 함수와 마찬기졸 클래스가 생성한 인스턴스는 프로토타입 체인의 일원이 된다.
<img src="https://velog.velcdn.com/images/yhc-key/post/893a7fe8-04fd-43a8-9600-b0b07ffdf523/image.png" alt=""></li>
<li>위와 같은 이유로 클래스는 생성자 함수와 같이 인스턴스를 생성하는 생성자 함수라고 볼 수 있다.</li>
<li>즉 클래스는 생성자 함수와 마찬가지로 프로토타입 기반의 객체 생성 메커니즘이다.</li>
</ul>
<h3 id="정적-메서드">정적 메서드</h3>
<p>생성자 함수의 경우는 다음과 같이 정적 메서드를 만들었다.</p>
<pre><code class="language-js">function Person(name) {
    this.name =name;
}
Person.sayHi = function() {
    console.log(&#39;Hi!&#39;);
}

Person.sayHi();</code></pre>
<p>클래스에서는 메서드에 static 키워드를 붙이면 정적 메서드(클래스 메서드)가 된다.</p>
<pre><code class="language-js">class Person {

    constructor(name) {
        this.name = name;
    }

    static sayHi() {
        console.log(&#39;Hi&#39;);
    }
}</code></pre>
<p>위 예제는 다음과 같이 프로토타입 체인을 생성한다.
<img src="https://velog.velcdn.com/images/yhc-key/post/38792a26-f8a5-4567-b7b7-82371f48302f/image.png" alt="">
즉 정적 메서드는 클래스에 바인딩된 메서드가 된다.<br>정적 메서드는 인스턴스로 호출할 수 없다(인스턴스 프로토타입 체인상에 존재 x). 따라서 클래스로 호출해야한다.</p>
<h3 id="정적-메서드와-프로토타입-메서드의-차이">정적 메서드와 프로토타입 메서드의 차이</h3>
<p>차이는 다음과 같다.</p>
<ul>
<li><ol>
<li>정적 메서드와 프로토타입 메서드는 자신이 속해 있는 프로토타입 체인이 다르다.</li>
</ol>
</li>
<li><ol start="2">
<li>정적 메서드는 클래스로 호출하고 프로토타입 메서드는 인스턴스로 호출한다.</li>
</ol>
</li>
<li><ol start="3">
<li>정적 메서드는 인스턴스 프로퍼티 참조 x 반대는 가능 </li>
</ol>
</li>
</ul>
<p>표준 빌트인 객체인 Math, Number, JSON, Object, Reflect 등은 다양한 정적 메서드를 가지고 있다.<br>이렇게 하면 이름 충돌 가능성을 줄여 주고 관련 함수들을 구조화할 수 있는 효과가 있다. 이런 이유로 애플리케이션 전역에서 사용할 유틸리티 함수를 전역 함수로 정의하지 않고 메서드로 구조화할 때 유용하다.</p>
<h3 id="클래스에서-정의한-메서드의-특징">클래스에서 정의한 메서드의 특징</h3>
<ol>
<li>function 키워드를 생략한 메서드 축약 표현을 사용</li>
<li>객체 리터럴과는 다르게 클래스에 메서드를 정의할 때는 콤마가 필요 없다.</li>
<li>암묵적으로 strict mode로 실행</li>
<li>for ... in문이나 Object.keys 메서드 등으로 열거 불가능.</li>
<li>내부 메서드 [[Construct]]를 갖지 않는 non-construtor다. 따라서 new 연산자와 함께 호출할 수 없다.</li>
</ol>
<h2 id="클래스의-인스턴스-생성-과정">클래스의 인스턴스 생성 과정</h2>
<p>new 연산자와 함께 클래스를 호출하면 생서자 함수와 마찬가지로 클래스의 내부 메서드 [[Construct]]가 호출된다. &quot;생성자 함수의 인스턴스 생성 과정&quot;에서 살펴본 바와 유사하게 다음과 같은 과정을 거쳐 인스턴스가 생성된다.</p>
<h4 id="1-인스턴스-생성과-this-바인딩">1. 인스턴스 생성과 this 바인딩</h4>
<p>new 연산자와 함께 클래스를 호출하면 constructor 내부 코드가 실행되기에 앞서 암묵적으로 빈 객체가 생성된다. 인스턴스의 프로토타입으로 클래스의 prototype 프로퍼티가 가리키는 객체가 설정된다. 그리고 인스턴스는 this에 바인딩된다.</p>
<h4 id="2-인스턴스-초기화">2. 인스턴스 초기화</h4>
<p>constructor의 내부 코드가 실행되어 this에 바인딩되어 있는 인스턴스를 초기화한다.</p>
<h4 id="3-인스턴스-반환">3. 인스턴스 반환</h4>
<p>클래스의 모든 처리가 끝나면 완성된 인스턴스가 바인딩된 this가 암묵적으로 반환된다.</p>
<h2 id="프로퍼티">프로퍼티</h2>
<h3 id="인스턴스-프로퍼티">인스턴스 프로퍼티</h3>
<p>인스턴스 프로퍼티는 constructor 내부에서 정의해야 한다.</p>
<h3 id="접근자-프로퍼티">접근자 프로퍼티</h3>
<ul>
<li>접근자 프로퍼티는 자체적으로는 값 [[Value]]을 갖지 않고 다른 데이터 프로퍼티의 값을 읽거나 저장할 때 사용하는 접근자 함수로 구성된 프로퍼티다.  </li>
<li>getter 함수와 setter 함수로 구성되어 있다.  </li>
<li>getter는 메서드 이름 앞에 get 키워드를 사용해 정의 (반드시 무언가 반환해야 함)</li>
<li>setter는 메서드 이름 앞에 set 키워드를 사용해 정의 (반드시 매개변수가 있어야 함)</li>
<li>getter, setter이름은 인스턴스 프로퍼티처럼 사용</li>
<li>즉 getter는 호출하는 것이아니라 프로퍼티처럼 참조하는 형식으로 사용</li>
<li>클래스의 메서드는 기본적으로 프로토타입 메서드이므로, 클래스의 접근자 프로퍼티 또한 인스턴스 프로퍼티가 아닌 프로토타입의 프로퍼티가 된다.</li>
</ul>
<h3 id="클래스-필드-정의-제안">클래스 필드 정의 제안</h3>
<p>클래스 기반 객체지향 언어에서 클래스가 생성할 인스턴스의 프로퍼티를 가리키는 용어다.<br>자바스크립트의 클래스 몸체에는 메서드만 선언할 수 있다. 따라서</p>
<pre><code class="language-js">class Person {
    // 클래스 필드 정의
    name = &#39;Lee&#39;;
}

const me = new Person(&#39;Lee&#39;);</code></pre>
<p>따라서 문법 에러가 발생해야 하나 최신 브라우저(Chrome 72 이상), Node.js(버전 12이상)에서 실행하면 정상 작동한다.<br>왜냐하면 JS도 인스턴스 프로퍼티를 마치 클래스 기반 객체지향 언어의 클래스 필드처럼 정의할 수 잇는 새로운 표준 사양인 &quot;Class field declarations&quot;가 2021년 1월 TC39 프로세스의 stage3(candidate)에 현재 제안되어 있기 때문이다.
함수는 일급 객체이므로 함수를 클래스 필드에 할당할 수 있다. 
ex</p>
<pre><code class="language-js">class Person {
    name = &#39;Lee&#39;;

    getName = function () {
        return this.name;
    }
    // 화살표 함수도 가능
}

const me = new Person();
console.log(me); // Person { name: &quot;Lee&quot;, getName: f}
console.log(me.getName()); //Lee</code></pre>
<p>다만 모든 클래스 필드는 인스턴스 프로퍼티가 되기 때문에 이 함수는 프로토타입 메서드가 아닌 인스턴스 메서드가 된다.<br>따라서 클래스 필드에 함수를 할당하는 것은 권장하지 않는다.  </p>
<h3 id="private-필드-정의-제안">private 필드 정의 제안</h3>
<p>js에서 아직 prviate, public, protected 키워드와 같은 접근 제한자를 지원하지 않기에 언제나 public이다.<br>다만 위에서 말한 TC39 프로세스의 stage3에는 private 필드를 정의할 수 있는 새로운 표준 사양이 제안되어 있다.<br>private 필드의 선두에는 #, 참조할 때도 #을 붙여주어야 한다.</p>
<pre><code class="language-js">class Person {

    #name =&#39;&#39;;

    constructor(name) {

        this.#name = name;
    }
}

const me = new Person(&#39;Lee&#39;);

// private 필드 #name은 클래스 외부에서 참조할 수 없다.
console.log(me.#name)
// SyntaxError: Private field &#39;#name&#39; must be declared in an enclosing class</code></pre>
<p>직접 접근은 불가하지만 접근자 프로퍼티를 통해 간접적으로 접근하는 방법은 유효하다.<br>private 필드를 직접 constructor에 정의하면 에러가 발생하므로 반드시 클래스 몸체에 정의해야 한다.</p>
<h3 id="static-필드-정의-제안">static 필드 정의 제안</h3>
<p>정적 메서드에서는 static 키워드로 만들 수 있었으나 static 키워드를 사용하여 정적 필드를 정의할 수는 없었다.<br>하지만 TC39프로세스의 stage 3에 의해 제안되어 있고, Chrome 72 이상, Node.js 버전 12이상에 이미 구현되어 있다.  </p>
<h2 id="상속에-의한-클래스-확장">상속에 의한 클래스 확장</h2>
<h3 id="클래스-상속과-생성자-함수-상속">클래스 상속과 생성자 함수 상속</h3>
<p>상속에 의한 클래스 확장은 지금까지 살펴본 프로토타입 기반 상속과는 다른 개념이다.<br>프로토타입 기반 상속은 프로토타입 체인을 통해 다른 객체의 자산을 상속받는 개념이고,<br><strong>상속에 의한 클래스 확장</strong>은 기존 클래스를 상속받아 새로운 클래스를 확장하여 정의하는 것이다.<br><img src="https://velog.velcdn.com/images/yhc-key/post/fc77ff38-10d6-4cc1-90a5-d8499a7b744d/image.png" alt="">
클래스는 상속을 통해 다른 클래스를 확장할 수 있는 문법인 extends 키워드가 기본적으로 제공되지만, 생성자 함수는 그와 같은 게 없다.</p>
<h3 id="extends-키워드">extends 키워드</h3>
<p>extends 키워드를 사용하여 상속받을 클래스를 정의할 수 있다.
확장된 클래스를 서브 클래스(파생 클래스 ,자식 클래스), 상속된 클래스를 수퍼 클래스(베이스 클래스, 부모 클래스)라고 부른다.<br>인스턴스의 프로토타입 체인분 아니라 클래스 간의 프로토타입 체인도 생성한다.<br><img src="https://velog.velcdn.com/images/yhc-key/post/3d9ab4ef-feff-45a6-984a-14a2bed5fcee/image.png" alt=""></p>
<h3 id="동적-상속">동적 상속</h3>
<p>생성자 함수를 상속받아 클래스 확장도 가능하다.  단 extends 앞에는 반드시 클래스가 와야 한다.<br>extends 뒤에는 클래스뿐만이 아니라 [[Construct]] 내부 메서드를 갖는 함수 객체로 평가될 수 있는 모든 표현식을 사용할 수 있다.
ex)</p>
<pre><code class="language-js">function Base1() {}

class Base2() {}

let condition = true;

//조건에 따라 동적으로 상속 대상을 결정하는 서브클래스
class Derived extends (condition ? Base1 : Base2) {}

const derived = new Derived();
console.log(derived);

console.log(derived instanceof Base1); // true
console.log(derived instanceof Base1); // false</code></pre>
<h3 id="서브클래스의-constructor">서브클래스의 constructor</h3>
<p>서브클래스에서 constructor를 생략하면 암묵적으로 다음과 같이 정의된다.</p>
<pre><code class="language-js">constructor(...args) { super(...args);}</code></pre>
<p>super()는 수퍼클래스의 constructor(super-constructor)를 호출하여 인스턴스를 생성한다.  </p>
<h3 id="super-키워드">super 키워드</h3>
<p>함수처럼 호출할 수도 있고 this와 같이 식별자처럼 참조할 수 있는 특수한 키워드. super 동작은 다음과 같다.</p>
<ul>
<li>super를 호출하면 수퍼클래스의 constructor(super-constructor)를 호출</li>
<li>super를 참조하면 수퍼클래스의 메서드를 호출할 수 있다.</li>
</ul>
<h4 id="super-호출">super 호출</h4>
<p>super를 호출할 때 주의사항은 다음과 같다.</p>
<ul>
<li>서브클래스에서 constructor를 생략하지 않는 경우 반드시 super를 호출해야 한다.</li>
<li>서브클래스의 constructor에서 super를 호출하기 전에는 this를 참조할 수 없다.</li>
<li>super는 반드시 서브클래스의 constructor에서만 호출. 다른 곳에서 호출시 에러 발생</li>
</ul>
<h4 id="super-참조">super 참조</h4>
<p>메서드 내에서 super를 참조하면 수퍼클래스의 메서드를 호출할 수 있다.</p>
<ul>
<li><p>서브클래스의 프로토타입 메서드 내에서 super.sayHi는 수퍼클래스의 프로토타입 메서드 sayHi를 가리킨다.</p>
<pre><code class="language-js">// 수퍼클래스
class Base {
  constructor(name) {
      this.name =name;
  }

  sayHi() {
      return `Hi! ${this.name}`;
  }
}
</code></pre>
</li>
</ul>
<p>// 서브캘릇
class Derived extends Base {
    sayHi() {</p>
<pre><code>    return `${super.sayHi()}. how are you doing?`;
}</code></pre><p>}</p>
<p>const derived = new Derived(&#39;Lee&#39;);
console.log(derived.sayHi()); //Hi! Lee. how are you doing?</p>
<pre><code>- 서브클래스이 정적 메서드 내에서 super.sayHi는 수퍼클래스의 정적 메서드 sayHi를 가리킨다. 
```js
class Base {
    static sayHi() {
        return &#39;Hi!&#39;;
    }
}

// 서브클래스
class Derived extends Base {
    static sayHi() {

        return `${super.sayHi()} how are you doing?`;
    }
}
console.log(Derived.sayHi()); //Hi! how are you doing?</code></pre><h3 id="상속-클래스의-인스턴스-생성-과정">상속 클래스의 인스턴스 생성 과정</h3>
<ul>
<li><ol>
<li>서브 클래스의 super호출 : 다른 클래스 상속받지 않는 클래스(and 생성자 함수)s는 내부 슬롯 [[ConstructorKind]]의 값이 &quot;base&quot;, 상속받으면 &quot;derived&quot;.<br>하지만 서브클래스는 직접 인스턴스 생성 x 수퍼클래스에게 인스턴스 생성 위임(따라서 서브 constructor 내부 super를 반드시 호출해야함!)</li>
</ol>
</li>
<li><ol start="2">
<li>수퍼클래스의 인스턴스 생성과 this 바인딩</li>
</ol>
</li>
<li><ol start="3">
<li>수퍼클래스의 인스턴스 초기화</li>
</ol>
</li>
<li><ol start="4">
<li>서브클래스 constructor로의 복귀와 this 바인딩 : super가 반환한 인스턴스가 this에 바인딩. 서브클래스는 별도의 인스턴스를 생성하지 않고 super가 반환한 인스턴스를 this에 바인딩하여 그대로 사용</li>
</ol>
</li>
<li><ol start="5">
<li>서브클래스의 인스턴스 초기화</li>
</ol>
</li>
<li><ol start="6">
<li>인스턴스 반환</li>
</ol>
</li>
</ul>
<h3 id="표준-빌트인-생성자-함수-확장">표준 빌트인 생성자 함수 확장</h3>
<p>동적 상속에서 살펴보았듯이 extends 키워드 다음에는 [[Construct]] 내부 메서드를 갖는 함수 객체로 평가될 수 있는 모든 표현식을 사용가능하다. 따라서 String, Number, Array 같은 표준 빌트인 객체도 extends 키워드를 사용하여 확장할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS_Deep_Dive_chapter24 : 클로저]]></title>
            <link>https://velog.io/@yhc-key/JSDeepDivechapter24-%ED%81%B4%EB%A1%9C%EC%A0%80</link>
            <guid>https://velog.io/@yhc-key/JSDeepDivechapter24-%ED%81%B4%EB%A1%9C%EC%A0%80</guid>
            <pubDate>Tue, 05 Mar 2024 00:22:40 GMT</pubDate>
            <description><![CDATA[<h1 id="chapter-24--클로저">chapter 24 : 클로저</h1>
<p>JS 고유 개념이 아닌, 함수를 일급 객체로 취급하는 함수형 프로그래밍 언어(예: 하스켈, 리스프, 얼랭, 스칼라 등)에서 사용되는 중요한 특성이다.</p>
<p>클로저 정의 : 클로저는 함수와 그 함수가 선언된 렉시컬 환경과의 조합</p>
<h2 id="렉시컬-스코프">렉시컬 스코프</h2>
<p>렉시컬 스코프 : <strong>JS엔진은 함수를 얻이서 호출했는지가 아니라 함수를 어디에 정의했는지에 따라 상위 스코프를 결정</strong></p>
<h2 id="함수-객체의-내부-슬롯-environment">함수 객체의 내부 슬롯 [[Environment]]</h2>
<p>함수는 자신의 내부 슬롯 [[Environment]]에 자신이 정의 된 환경, 즉 상위 스코프의 참조를 저장한다.
함수 객체의 내부 슬롯 [[Environment]]에 저장된 현재 실행 중인 실행 컨텍스트의 렉시컬 환경의 참조가 바로 상위 스코프<br>또한 자신이 호출되었을 때 생성될 함수 렉시컬 환경의 &quot;외부 렉시컬 환경에 대한 참조&quot;에 저장될 참조값이다.<br>함수 객체는 내부 슬롯 [[Environment]]에 저장한 렉시컬 환경의 참조, 즉 상위 스코프를 자신 이 존재하는 한 기억한다.</p>
<h2 id="클로저와-렉시컬-환경">클로저와 렉시컬 환경</h2>
<p><strong>외부 함수 보다 중첩 함수가 더 오래 유지되는 경우 중첩 함수는 이미 생명 주기가 종료한 외부 함수의 변수를 참조할 수 있다.</strong><br><strong>이러한 중첩 함수를 클로저</strong>라고 부른다.  </p>
<ul>
<li><p>함수의 실행 컨텍스트는 그 종료 시 실행 컨텍스트 스택에서 제거되지만 outer 함수의 렉시컬 환경까지 소멸하는 것은 아니다.</p>
</li>
<li><p>JS의 모든 함수는 상위 스코프를 기억하므로 이론적으로 모든 함수는 클로저이다. 다만 상위 스코프의 식별자를 차조하지 않는 함수는 클로저가 아니다.</p>
</li>
<li><p>상위 스코프의 식별자를 참조하더라도, 외부 함수보다 중첩 함수가 일찍 소멸되는경우에는 일반적으로 클로저라고 하지 않는다.</p>
<pre><code class="language-js">function foo() {
  const x =1;

  function bar() {
      debugger;

      console.log(x);
  }
  bar();
}
foo();</code></pre>
<p>이런식이라면 bar()가 바깥에서 호출하지 않았으므로 클로저 x  </p>
<pre><code class="language-js">function foo() {
  const x =1;
  const y =2;
  function bar() {
      debugger;

      console.log(x);
  }
  return bar;
}
const bar = foo();
bar();</code></pre>
<p>여기서는 bar는 상위 스코프의 식별자를 참조하며 더 오래 살아 남으며 클로저이다.  </p>
</li>
<li><p><em>클로저는 중첩 함수가 상위 스코프의 식별자를 참조하고 있고 중첩 함수가 외부 함수보다 더 오래 유지되는 경우에 한정하는 것이 일반적이다.*</em>  </p>
</li>
<li><p>위의 예시에서는 y는 참조하고 있지 않기 때문에 클로저에 저장하지 않는다. 이렇 듯 참조하는 변수(여기서는 x)를 자유 변수라 한다.</p>
</li>
<li><p>클로저란 &quot;함수가 자유 변수에 대해 닫혀있다&quot;라는 의미이다.</p>
</li>
</ul>
<h2 id="클로저의-활용">클로저의 활용</h2>
<p>클로저는 상태를 안전하게 변경하고 유지하기 위해 사용한다.<br>상태를 안전하게 은닉하고 특정 함수에게만 상태 변경을 허용하기 위해 사용한다.<br>ex)</p>
<pre><code class="language-js">const increase = (function () {
    let num = 0;

    return function () {
        return ++num;
    }
}());

console.log(increase());
console.log(increase());
console.log(increase());</code></pre>
<ul>
<li>이처럼 클로저는 상태가 의도치 않게 변경되지 않도록 안전하게 은닉하고 특정 함수에게만 상태 변경을 허용하여 상태를 안전하게 변경하고 유지하기 위해 사용한다.</li>
</ul>
<h2 id="캡슐화와-정보-은닉">캡슐화와 정보 은닉</h2>
<p>캡슐화는 객체의 상태를 나타내는 프로퍼티와 프로퍼티를 참조하고 조작할 수 있는 동작인 메서드를 하나로 묶는 것을 말한다.<br>이를 정보 은닉이라 한다.<br>대부분의 객체지향 프로그래밍 언어는 클래스를 정의하고 그 클래스를 구성하는 멤버(프로퍼티와 메서드)에 대하여 public, private, protected 같은 접근 제한자를 선언하여 공개 범위를 한정할 수 있다.
JS는 접근 제한자를 지원하지 않는다. 즉 항상 public 하다.</p>
<h2 id="자주-발생하는-실수">자주 발생하는 실수</h2>
<p>다음은 그 예시이다.</p>
<pre><code class="language-js">var funcs = [];

for (var i = 0; i &lt;3; i++) {
    funcs[i] = function () {return i};
}

for (var j = 0 ; j &lt;funcs.length ; j++) {
    console.log(funcs[j]());
}</code></pre>
<ul>
<li>var는 함수 스코프이기 때문에 뒤의 return i부분은 반복문이 끝나고 평가 됨. 따라서 그 때의 i값은 3이므로 funcs[0], funcs[1], funcs[2]의 값은 3임</li>
<li>funcs[i]에 있는 i는 그 즉시 평가되기 때문에 0, 1, 2 값이 제대로 들어감  </li>
</ul>
<p>바르게 고치면 다음과 같다.</p>
<pre><code class="language-js">var funcs = [];

for (var i = 0 ; i &lt;3 ; i++) {
    funcs[i] = (function (id) {
        return function () {
            return id;
        };
    }(i));

}

for (var j = 0 ; j&lt;funcs.length; j++) {
    console.log(funcs[j]());
}</code></pre>
<p>let을 사용하면 이렇게 할 필요 없이 깔끔하게 해결된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS_Deep_Dive_chapter23 : 실행_컨텍스트]]></title>
            <link>https://velog.io/@yhc-key/JSDeepDivechapter23-%EC%8B%A4%ED%96%89%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8</link>
            <guid>https://velog.io/@yhc-key/JSDeepDivechapter23-%EC%8B%A4%ED%96%89%EC%BB%A8%ED%85%8D%EC%8A%A4%ED%8A%B8</guid>
            <pubDate>Tue, 05 Mar 2024 00:21:51 GMT</pubDate>
            <description><![CDATA[<h1 id="chapter23--실행-컨텍스트">chapter23 : 실행 컨텍스트</h1>
<p>execution context(실행 컨텍스트)는 JS 동작 원리를 담고 있는 핵심 개념이다.</p>
<h2 id="소스코드의-타입">소스코드의 타입</h2>
<p>ECMAScript 사양은 소스코드를 4가지 타입으로 구분. 이들은 실행 컨텍스트 생성</p>
<ol>
<li>global code : 전역 존재 소스코드</li>
<li>function code : 함수 내부 존재 소스코드</li>
<li>eval code : : 빌트인 전역 함수eval 함수에 인수로 전달되어 실행되는 소스코드</li>
<li>module code : 모듈 내부 존재 소스코드. 모듈 내부 함수, 클래스 등 내부 코드 포함 x</li>
</ol>
<h4 id="전역-코드">전역 코드</h4>
<p>전역 변수 관리 위해 최상위 스코프인 전역 스코프 생성. var 키워드로 선언된 전역 변수와 함수 선언문으로 정의된 전역 함수를 전역 객체의 프로퍼티와 메서드로 바인딩하고 참조하기 위해 전역 객체와 연결되어야 함. 이를 위해 전역 코드가 평가되면 전역 실행 컨텍스트 생성</p>
<h3 id="함수-코드">함수 코드</h3>
<p>지역 변수, 매개변수 arguments 객체 관리하는 지역 스코프 생성. 함수 코드 평가시 실행 컨텍스트 생성</p>
<h3 id="eval-코드">eval 코드</h3>
<p>strict mode에서 자신만의 독자적 스코프 생성. eval 코드 평가 시 eval 실행 컨텍스트 생성</p>
<h3 id="모듈-코드">모듈 코드</h3>
<p>모듈 코드는 모듈별로 독립적 모듈 스코프 생성. 평가 시 모듈 실행 컨텍스트 생성</p>
<h2 id="소스코드의-평가와-실행">소스코드의 평가와 실행</h2>
<ul>
<li>JS 엔진은 소스코드의 평가와 소스코드의 실행 과정으로 나누어 처리</li>
<li>평가 과정에서는 실행 컨텍스트 생성, 변수, 함수 등의 선언문만 먼저 실행하여 생성된 변수나 함수 식별자를 키로 실행 컨텍스트가 관리하는 스코프(렉시컬 환경의 환경 레코드)에 등록</li>
<li>평가 과정 이후 순차적으로 실행 (런타임 시작) </li>
<li>변수나 함수 참조는 실행 컨텍스트 관리 스코프에서 검색해서 취득</li>
<li>변수 값의 변경 등 소스코드 실행 결과는 실행 컨텍스트가 관리하는 스코프에 등록
<img src="https://velog.velcdn.com/images/yhc-key/post/7c9f735d-2813-45a6-bd33-ae523ace8fae/image.png" alt=""></li>
</ul>
<p>var x는 먼저 실행하여 실행 컨텍스트가 관리하는 스코에 등록되고 undefined로 초기화<br>소스코드 평가 후 실행. 선언문은 평가 시 실행되었으므로 할당문 x =1만 실행. </p>
<h2 id="실행-컨텍스트의-역할">실행 컨텍스트의 역할</h2>
<pre><code class="language-js">const x = 1;
const y = 2;

function foo(a) {
    const x = 10;
    const y = 20;

    console.log(a + x+ y) //130
}

foo(100);

console.log(x + y);</code></pre>
<p>코드 실행은 다음과 같다<br>전역 코드 평가 -&gt; 전역 코드 실행 -&gt; 함수 코드 평가 -&gt; 함수 코드 실행</p>
<h3 id="전역코드-평가">전역코드 평가</h3>
<ul>
<li>전역 코드 실행 준비를 함. </li>
<li>소스코드 평가 과정에서 선언문만 먼저 실행</li>
<li>전역 코드 변수 선언문과 함수 선언문이 먼저 실행되고, 그 결과 생성된 전역 변수와 전역 함수가 실행 컨텍스트 관리하는 전역 스코프에 등록</li>
<li>이 때 var 키워드 선언 전역 변수, 함수 선언문으로 정의된 전역 함수는 전역 객체의 프로퍼티와 메서드가 된다.</li>
</ul>
<h3 id="전역-코드-실행">전역 코드 실행</h3>
<p>순차적으로 코드 실행하며 값 할당, 함수 호출 등 진행 하며 함수 호출시, 전역 코드 실행이 일시 중단되고 코드 실행 순서 변경 함수 내부로 진입한다.</p>
<h3 id="함수-코드-평가">함수 코드 평가</h3>
<ul>
<li>전역코드와 비슷한 평가과정을 함수 내부에 대해서 똑같이 진행</li>
<li>이 때 전역 스코프가 아닌 지역 스코프에 등록. </li>
<li>arguments 객체 생성되어 지역 스코프에 등록되고 this 바인딩 결정</li>
</ul>
<h3 id="함수-코드-실행">함수 코드 실행</h3>
<p>차례대로 함수 코드 실행. 이 경우에는 console.log메서드를 호출하는데, console을 스코프 체인을 통해 검색. 그 후 log 프로퍼티를 console 객체의 프로토타입 체인을 통해 검색.<br>-&gt; 스코프 구분하여 식별자와 바인딩된 값이 관리되어야 함.</p>
<p>이 모든 것을 관리하는 것이 실행 컨텍스트.<br>실행 컨텍스트 : 식별자(변수, 함수, 클래스 등의 이름)을 등록하고 관리하는 스코프와 코드 실행 순서 관리를 구현한 내부 매커니즘으로, 모든 코드는 실행 컨텍스트를 통해 실행되고 관리된다.</p>
<h2 id="실행-컨텍스트-스택">실행 컨텍스트 스택</h2>
<p>전역 진행 중 함수 호출 시 함수 실행 컨텍스트 생성<br>ex)</p>
<pre><code class="language-js">const x = 1;

function foo () {
    const y = 2;

    function bar () {
        const z = 3;
        console.log(x + y+ z);
    }
    bar();
}

foo(); //6</code></pre>
<p>이 때 실행 컨텍스트는 스택 자료구조로 관리. 이를 실행 컨텍스트 스택이라고 부름
<img src="https://velog.velcdn.com/images/yhc-key/post/79910527-fb70-4292-86b9-b0f9d6f2ac6a/image.png" alt="">
진행은 다음과 같다.  </p>
<ol>
<li>전역 코드의 평가와 실행</li>
<li>foo 함수 코드의 평가와 실행</li>
<li>bar 함수 코드의 평가와 실행</li>
<li>foo 함수 코드 복귀</li>
<li>전역 코드로 복귀  </li>
</ol>
<p>실행 컨텍스트 스택 최상위 실행 컨텍스트는 언제나 현재 실행 중인 코드의 실행 컨텍스트</p>
<h3 id="렉시컬-환경">렉시컬 환경</h3>
<p>렉시컬 환경 : 식별자와 식별자에 바인딩된 값, 그리고 상위 스코프에 대한 참조를 기록하는 자료구조로 실행 컨텏스트를 구성하는 컴포넌트<br>실행 컨텍스트 스택 -&gt; 코드 실행 순서 관리, 렉시컬 환경 -&gt; 스코프와 식별자 관리  </p>
<ul>
<li>렉시컬 환경은 키와 값을 갖는 객체 형태의 스코프(전역, 함수, 블록 스코프)를 생성하여 식별자를 키로 등록하고 식별자에 바인딩된 값을 관리. 즉 저장소라 볼 수 있다.</li>
<li>실행 컨텍스틑 LexicalEnvironment 컴포넌트와 VariableEnvironment 컴포넌트로 구성.
<img src="https://velog.velcdn.com/images/yhc-key/post/6563d9cf-1b42-49bb-a9a3-d5f00a74700d/image.png" alt=""></li>
<li>처음에는 동일한 렉시컬 환경을 참조. 몇몇 경우에 떨어지기도 하지만, 여기서는 구분하지 않고 (strict mode, eval 코드, try/catch 문과 같은 특수 상황 제외) 렉시컬 환경으로 통일해 설명<h4 id="렉시컬-환경의-구성요소">렉시컬 환경의 구성요소</h4>
</li>
</ul>
<ol>
<li><p>환경 레코드 : 스코프에 포함된 식별자를 등록하고 등록된 식별자에 바인딩된 값을 관리하는 저장소</p>
</li>
<li><p>외부 렉시컬 환경에 대한 참조 : 상위 스코프를 가리킴. 실행 컨텍스트 생성 소스코드 포함 상위 코드의 렉시컬 환경. 단방향 링크드 리스트인 스코프 체인을 구현</p>
</li>
</ol>
<h2 id="실행-컨텍스트의-생성과-식별자-검색-과정">실행 컨텍스트의 생성과 식별자 검색 과정</h2>
<h3 id="전역-객체-생성">전역 객체 생성</h3>
<ul>
<li>전역 코드 평가 전 생성. 빌트인 전역 프로퍼티와 빌트인 전역 함수 등이 포함됨.  </li>
<li>전역 객체도 Object.prototype 상속<h3 id="전역-코드-평가">전역 코드 평가</h3>
생성 후 JS 엔진이 전역 코드 평가. 평가는 다음 순서로</li>
</ul>
<ol>
<li>전역 실행 컨텍스트 생성</li>
<li>전역 렉시컬 환경 생성<ul>
<li>전역 환경 레코드 생성<ul>
<li>객체 환경 레코드 생성</li>
<li>선언적 환경 레코드 생성</li>
</ul>
</li>
<li>this 바인딩</li>
<li>외부 렉시컬 환경에 대한 참조 결정
<img src="https://velog.velcdn.com/images/yhc-key/post/bbda7772-d45d-4e7a-b1ab-d128233e40ac/image.png" alt=""></li>
</ul>
</li>
</ol>
<ul>
<li>전역 실행 컨텍스트 생성 : 실행 컨텍스트 스택에 푸쉬</li>
<li>전역 렉시컬 환경 생성 : 전역 실행 컨텍스트에 바인딩</li>
<li>전역 환경 레코드 생성 : 객체 환경 레코드(var, 함수 선언문으로 정의한 전역 변수, 빌트인 전역 프로퍼티와 빌트인 전역 함수, 표준 빌트인 객체 관리)와 선언적 환경 레코드(let, const로 선언한 전역 변수)로 구성되어 있음.</li>
<li>객체 환경 레코드 생성 : var 키워드로 선언한 변수는 코드 실행 단계에서 변수 선언문 이전에도 참조 가능. 다만 항상 undefined.  </li>
<li>선언적 환경 레코드 생성 : const는 선언 단계와 초기화 단계가 분리ㅗ디어 진행. 따라서 초기화 단계, 즉 런타임에 실행 흐름이 변수 선언문에 도달하기 전까지 일시적 사각지대(Temporal Dead Zone)에 빠지게 된다.</li>
<li>this 바인딩 : this바인딩은 객체 환경 레코드와 선언적 환경 레코드에는 없고, 전역 환경 레코드와 함수 환경 레코드에만 존재한다.</li>
<li>외부 렉시컬 환경에 대한 참조 결정 : 상위 스코프를 가리키며 단방향 링크드 리스트인 스코프 체인을 구현 (전역 코드시는 null)<h3 id="전역-코드-실행-1">전역 코드 실행</h3>
</li>
<li>식별자는 스코프가 다르면 같은 이름을 갈질 수 있으므로, 식별자 결정이 필요  </li>
<li>실행 중인 실행 컨테슥트에서 식별자를 검색하기 시작.  </li>
<li>전역까지 해당 식별자가 없다면 참조 에러 발생</li>
</ul>
<h2 id="실행-컨텍스트와-블록-레벨-스코프">실행 컨텍스트와 블록 레벨 스코프</h2>
<p>var : 함수의 코드 블록만 지역 스코프 (함수 레벨 스코프)
let, const : 모든 코드 블록을 지역 스코프로 인정 (블록 레벨 스코프)</p>
]]></description>
        </item>
    </channel>
</rss>