<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>lee-yun-pyo.log</title>
        <link>https://velog.io/</link>
        <description>프론트엔드 개발자 지망생</description>
        <lastBuildDate>Sat, 10 Aug 2024 08:04:07 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. lee-yun-pyo.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/lee-yun-pyo" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Blob 객체]]></title>
            <link>https://velog.io/@lee-yun-pyo/Blob-%EA%B0%9D%EC%B2%B4</link>
            <guid>https://velog.io/@lee-yun-pyo/Blob-%EA%B0%9D%EC%B2%B4</guid>
            <pubDate>Sat, 10 Aug 2024 08:04:07 GMT</pubDate>
            <description><![CDATA[<h1 id="blob">Blob</h1>
<p>JavaScript에서 대용량의 이진 데이터를 다루기 위해 사용된다. 파일, 이미지, 텍스트 등의 데이터를 이진 형식으로 표현할 수 있으며, 특히 파일 업로드나 다운로드, 그리고 데이터 송수신 작업 등에 사용된다. </p>
<h3 id="주요-특징">주요 특징:</h3>
<ul>
<li><strong>데이터의 크기와 형식</strong>: <code>Blob</code> 객체는 임의의 바이트 배열로 데이터를 저장한다. 텍스트, 이미지, 비디오, 오디오 등 다양한 데이터를 포함할 수 있다.</li>
<li><strong>MIME 타입</strong>: <code>Blob</code> 객체를 생성할 때 MIME 타입을 지정할 수 있다. 이 MIME 타입은 해당 데이터가 어떤 형식인지를 나타내며, 예를 들어 <code>application/json</code>, <code>image/png</code>, <code>text/plain</code> 등이 있다.</li>
</ul>
<h3 id="blob-생성"><strong>Blob 생성</strong></h3>
<p><strong>Blob 생성자는 새로운 Blob 객체를 반환한다.</strong></p>
<p>생성 시 인수로 <code>array</code>와 <code>options</code>을 받습니다.</p>
<pre><code class="language-jsx">const newBlob = new Blob(array, options?)</code></pre>
<p><code>array</code> : <code>ArrayBuffer</code>, <code>ArrayBufferView</code>, <code>Blob</code>, <code>USVString</code> 또는 이 값들을 혼합해 구성한 <code>Array</code> 이다. <code>Blob</code>의 데이터를 구성하게 된다</p>
<p><code>option</code></p>
<ul>
<li><code>type</code> : Blob에 저장할 데이터의 <a href="https://developer.mozilla.org/ko/docs/Glossary/MIME_type">MIME 유형</a>. 기본 값은 빈 문자열(<code>&#39;&#39;</code>).</li>
</ul>
<pre><code class="language-jsx">const array = [&#39;&lt;a id=&quot;a&quot;&gt;&lt;b id=&quot;b&quot;&gt;hey!&lt;/b&gt;&lt;/a&gt;&#39;]; // 하나의 DOMString을 포함한 배열
const blob = new Blob(array, { type: &quot;text/html&quot; }); // 생성한 블롭</code></pre>
<h3 id="프로젝트에-적용한-사례">프로젝트에 적용한 사례</h3>
<p><img src="https://velog.velcdn.com/images/lee-yun-pyo/post/0c08e5b8-ffc7-4dde-a841-6f10728bcb64/image.JPG" alt="postman"></p>
<p>위와 같은 형식으로 POST 요청을 보낼 때 이미지를 담는 <code>photos</code> 와 사용자 입력값을 받는 JSON 형태의 <code>postRequest</code> 를 body 로 보내야했다. </p>
<p>formData에 <code>postRequest</code> 키 값을 가진 데이터를 담아서 보냈다. </p>
<pre><code class="language-jsx">const postRequestData = new Blob(
  [
    JSON.stringify({
      kakaoId: &quot;12345&quot;,
      title: &quot;제목&quot;,
      content: &quot;설명&quot;,
      category: &quot;카테고리&quot;,
      hashtags: [&quot;태그&quot;],
    }),
  ],
  { type: &quot;application/json&quot; }
);

const response = await fetch(API_PATH.createPost(), {
  method: &quot;POST&quot;,
  headers: {
    Authorization: `Bearer ${accessToken}`,
  },
  body: formData,
});</code></pre>
<h3 id="요약">요약</h3>
<p><code>Blob</code>은 JavaScript에서 대용량의 이진 데이터를 효율적으로 처리하고 다루기 위한 객체다. 이 객체는 데이터를 전송하거나 저장할 때 사용되며, 특히 파일 처리와 네트워크 통신에서 유용하다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS 공부 - var, let, const 과 호이스팅]]></title>
            <link>https://velog.io/@lee-yun-pyo/JS-%EA%B3%B5%EB%B6%80-var-let-const-%EA%B3%BC-%ED%98%B8%EC%9D%B4%EC%8A%A4%ED%8C%85</link>
            <guid>https://velog.io/@lee-yun-pyo/JS-%EA%B3%B5%EB%B6%80-var-let-const-%EA%B3%BC-%ED%98%B8%EC%9D%B4%EC%8A%A4%ED%8C%85</guid>
            <pubDate>Fri, 09 Aug 2024 11:55:39 GMT</pubDate>
            <description><![CDATA[<h2 id="스코프">스코프</h2>
<ul>
<li>모든 식별자는 자신이 선언된 위치에 따라 다른 코드가 식별자 자신을 참조할 수 있는 유효 범위가 결정된다. (식별자가 유효한 범위)</li>
</ul>
<h3 id="스코프-체인">스코프 체인</h3>
<ul>
<li>스코프가 계층적으로 연결된 것</li>
<li>변수를 참조할 때 스코프 체인을 통해 변수를 참조하는 코드의 스코프에서 시작하여 상위 스코프 방향으로 이동하여 선언된 변수를 검색한다.</li>
</ul>
<h3 id="함수-레벨-스코프와-블록-레벨-스코프">함수 레벨 스코프와 블록 레벨 스코프</h3>
<ul>
<li>블록 레벨 스코프<ul>
<li>함수 몸체만이 아니라 모든 코드 블록이 지역 스코프를 만든다</li>
</ul>
</li>
<li>함수 레벨 스코프<ul>
<li>함수의 코드 블록만이 지역 스코프를 만든다.<br />    
### 렉시컬 스코프

</li>
</ul>
</li>
</ul>
<pre><code class="language-jsx">var x = 1;

function foo() {
    var x = 10;
    bar();
}

function bar() {
    console.log(x);
}

foo(); // ?
bar(); // ?</code></pre>
<p>위 코드의 실행 결과는 bar 함수의 상위 스코프가 무엇인지에 따라 결정된다. </p>
<blockquote>
</blockquote>
<ol>
<li>함수를 어디서 호출했는지에 따라 함수의 상위 스코프를 결정<ol start="2">
<li>함수를 어디서 정의했는지에 따라 함수의 상위 스코프를 결정</li>
</ol>
</li>
</ol>
<p>1번 방식은 동적 스코프라고 한다. 함수가 호출되는 시점에 동적으로 상위 스코프를 결정한다. </p>
<p>동적 스코프를 따를 경우 bar 함수의 상위 스코프는 foo 함수의 지역 스코프와 전역 스코프가 된다. </p>
<pre><code class="language-jsx">// 동적 스코프를 따를 경우 결과
10
1</code></pre>
<p>2번 방식은 렉시컬 스코프 또는 정적 스코프라 한다. 함수 정의가 평가되는 시점에 상위 스코프를 결정한다. </p>
<p>렉시컬 스코프를 따를 경우 bar 함수의 상위 스코프는 전역 스코프가 된다. </p>
<pre><code class="language-jsx">// 렉시컬 스코프를 따를 경우 결과
1
1</code></pre>
<p><strong>자바스크립트는 렉시컬 스코프를 따른다. 따라서 함수가 호출된 위치는 상위 스코프 결정에 어떠한 영향을 주지 않으며 함수의 상위 스코프는 언제나 자신이 정의된 스코프이다.</strong></p>
<h2 id="var-키워드의-문제점">var 키워드의 문제점</h2>
<ol>
<li><p>같은 스코프 내 중복 선언이 허용된다.</p>
<pre><code class="language-jsx"> function foo() {
     var x = 1;
     var x = 2;
     console.log(x); // 2
 }
 foo();

 function bar() {
     let x = 1;
     let y = 2; // SyntaxError
 }
 bar();</code></pre>
</li>
<li><p>함수 레벨 스코프를 따른다.</p>
<p> var 키워드로 선언한 변수는 함수 코드 불록만을 지역스코프로 인정한다. 따라서 함수 외부에서 선언한 변수는 코드 블록 내 선언해도 모두 전역 변수가 된다.</p>
<pre><code class="language-jsx"> var x = 1;

 if (true) {
     // var 은 함수 레벨 스코프를 따르므로 코드 블록 내에서 선언되었다해도 전역변수다.
     // 따라서 x 는 전역 변수다. 이미 선언된 전역 변수 x가 있으므로 x 변수는 중복 선언된다. 
     var x = 10;
 }

 console.log(x); // 10</code></pre>
</li>
<li><p>변수 호이스팅</p>
<p> var 키워드로 변수를 선언하면 변수 호이스팅에 의해 스코프 선두로 끌어 올려진 것 처럼 동작한다. 따라서, 변수 호이스팅에 의해 var 키워드로 선언한 변수는 변수 선언문 이전에 참조할 수 있다. </p>
<pre><code class="language-jsx"> // 이 시점에 변수 호이스팅에 의해 이미 x 변수가 선언되었다. (1. 선언단계)
 // 변수 x 는 undefined로 초기화된다 (2. 초기화 단계)
 console.log(x); // undefined

 // 변수의 값 할당 (3. 할당 단계)
 x = 100;

 console.log(x); // 100

 var x;</code></pre>
</li>
</ol>
<ol start="4">
<li><p>var 와 전역객체</p>
<p> var 키워드로 선언한 전역 변수와 전역함수는 전역 개체 <code>window</code> 의 프로퍼티가 된다.</p>
<pre><code class="language-jsx"> var x = 1;
 console.log(window.x); // 1

 let y = 1; // let 으로 선언한 전역 변수는 전역 객체 프로퍼티가 아니다.
 console.log(window.y); // undefined</code></pre>
</li>
</ol>
<h2 id="let-키워드-특징">let 키워드 특징</h2>
<ol>
<li>변수 중복 선언 금지</li>
<li>블록 레벨 스코프</li>
<li><a href="https://velog.io/@lee-yun-pyo/JS-%EA%B3%B5%EB%B6%80-var-let-const-%EA%B3%BC-%ED%98%B8%EC%9D%B4%EC%8A%A4%ED%8C%85#let-%EB%B3%80%EC%88%98-%ED%98%B8%EC%9D%B4%EC%8A%A4%ED%8C%85">변수 호이스팅</a></li>
</ol>
<h2 id="const-키워드-특징">const 키워드 특징</h2>
<ol>
<li><p>선언과 초기화
 const 로 선언한 변수는 반드시 선언과 동시에 초기화해야 한다.</p>
<pre><code class="language-jsx"> const x; // SyntaxError</code></pre>
</li>
<li><p>블록 레벨 스코프</p>
</li>
<li><p>호이스팅이 발생하지 않는 것처럼 동작</p>
</li>
<li><p>재할당 금지</p>
</li>
<li><p>상수</p>
<ul>
<li><p>const 로 선언된 변수에 원시 값을 할당한 경우 할당된 값을 변경할 수 없다.
단, 객체를 할당한 경우 값을 변경할 수 있다. 객체는 재할당 없이도 직접 변경이 가능하기 때문이다.</p>
</li>
<li><p><em>const 키워드는 재할당을 금지할 뿐 불변을 의미하지 않는다.*</em></p>
<pre><code class="language-js">const person = {
     name: &#39;lee&#39;
 }
// 재할당 없이도 변경 가능
person.name = &#39;kim&#39;;</code></pre>
</li>
</ul>
</li>
</ol>
<br/> 

<h2 id="호이스팅">호이스팅</h2>
<pre><code class="language-jsx">console.log(score); // undefined
var score;</code></pre>
<p>JS 는 인터프리터에 의해 위에서 부터 순차적으로 코드를 실행하기 때문에 <code>console.log</code> 시점에 score 변수가 선언되기 전이므로 참조 에러가 발생할 것 처럼 보인다. 하지만 참조 에러가 발생하지 않고 <code>undefined</code> 가 출력된다. </p>
<p>그 이유는 <strong>변수 선언이 소스코드가 한 줄씩 실행되는 시점, 즉 런타임이 아니라 그 이전 단계에서 먼저 실행되기 때문이다.</strong></p>
<p>자바스크립트 엔진을 코드를 실행시킬 때 다음과 같은 과정을 거친다. </p>
<blockquote>
<ol>
<li>소스코드 평가 과정<ul>
<li>변수 선언을 포함한 모든 선언문(변수 및 함수 선언문 등)을 찾아내 먼저 실행한다.</li>
</ul>
</li>
<li>소스 코드 평가 과정이 끝나면 모든 선언문을 제외하고 코드를 한 줄씩 실행한다. </li>
</ol>
</blockquote>
<p>즉, JS 엔진은 변수 선언이 코드 어디에 있든 상관없이 다른 코드보다 먼저 실행된다. 따라서 변수 선언이 코드 어디에 있든지 어디서든 변수를 참조할 수 있다. </p>
<p>이처럼 <strong>변수 선언문이 코드 선두로 끌어 올려진 것처럼 동작하는 특징을 호이스팅</strong>이라 한다. 변수 선언 뿐만 아니라 <code>var</code> <code>let</code> <code>const</code> <code>function</code> <code>class</code> 키워드를 사용해서 선언하는 모든 식별자는 호이스팅 된다.</p>
<h3 id="var-변수-호이스팅"><code>var</code> 변수 호이스팅</h3>
<pre><code class="language-jsx">// 코드 이 시점에는 변수 호이스팅에 의해 foo 변수가 선언된다. (1. 선언단계)
// 변수 foo 가 undefined 로 초기화 (2. 초기화 단계)
console.log(foo); // undefined

// 값 할당 (3. 할당 단계)
foo = 123;

console.log(foo); // 123

// 변수 선언
var foo;</code></pre>
<ul>
<li><code>var</code> 키워드로 선언한 변수는 <strong>런타임 이전에 암묵적으로 선언단계와 초기화 단계가 함께 진행</strong>된다. 이후 할당문을 만나면 <strong>할당 단계가 진행</strong>된다.
<img src="https://velog.velcdn.com/images/lee-yun-pyo/post/e3532b27-a324-47b7-ad3a-71d72f9923fc/image.png" alt="var 호이스팅"></li>
<li>가독성 저하 및 오류 발생 가능성</li>
</ul>
<h3 id="let-변수-호이스팅"><code>let</code> 변수 호이스팅</h3>
<pre><code class="language-jsx">console.log(foo); // ReferenceError
let foo;</code></pre>
<p><code>let</code> 키워드로 선언한 변수는 호이스팅이 발생하지 않는 것처럼 보인다. </p>
<p><code>let</code> 키워드로 선언한 변수는 <strong>선언단계와 초기화 단계가 분리되어 진행</strong>된다.  즉, 런타임 이전에 JS 엔진에 의해 암묵적으로 선언단계가 먼저 실행되고 초기화 단계는 변수 선언문에 도달했을 때 실행된다. </p>
<p><code>let</code> 키워드로 선언한 변수는 스코프 시작 지점 부터 변수 선언문까지 변수를 참조할 수 없는데 변수를 참조할 수 없는 구간을 <strong>일시적 사각지대</strong>라고 부른다.</p>
<pre><code class="language-js">// 선언 단계 실행
// 초기화 이전 일시적 사각지대
console.log(foo); // ReferenceError

let foo; // 변수 선언문: 초기화 단계 실행
console.log(foo);

foo = 1; // 할당 단계
console.log(foo); // 1 </code></pre>
<p><img src="https://velog.velcdn.com/images/lee-yun-pyo/post/6716e8a1-393d-4f22-832b-a66df501a263/image.png" alt="let 호이스팅"></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[비동기 처리 - 1. 콜백 패턴]]></title>
            <link>https://velog.io/@lee-yun-pyo/%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B2%98%EB%A6%AC-1.-%EC%BD%9C%EB%B0%B1-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@lee-yun-pyo/%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B2%98%EB%A6%AC-1.-%EC%BD%9C%EB%B0%B1-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Mon, 29 Jul 2024 11:37:33 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/lee-yun-pyo/post/36f877a8-a1fc-43b1-a11a-c7f4fa080646/image.jpeg" alt="JS 표지"></p>
<blockquote>
<p> <strong>동기 vs 비동기</strong>
동기는 현재 실행 중인 작업이 끝날 때까지 다음 작업은 대기하고 작업이 끝나면 다음 작업이 실행되는 것을 의미한다. 반대로 비동기는 현재 실행 중인 작업을 기다리지 않고 다음 작업을 수행하는 것을 의미한다.<br>동기는 작업 실행의 순서를 보장한다는 장점이 있지만 블로킹이 발생한다는 단점이 있다. 
비동기는 작업의 블로킹이 발생하지 않는다는 장점이 있지만 작업 실행의 순서를 보장하지 못한다는 단점이 있다.</p>
</blockquote>
<h1 id="비동기-처리의-필요성">비동기 처리의 필요성</h1>
<pre><code class="language-jsx">function plus() {
  let a = 1;
  setTimeout(()=&gt;console.log(++a), 1000);
  return a;
}

const result = plus();
console.log(&#39;result :&#39;, result);</code></pre>
<pre><code>result: 1
// 1초 뒤
2</code></pre><p>위 코드에서 <code>plus</code> 함수는 비동기 함수다. 비동기 함수란 함수 내부에 비동기로 동작하는 코드를 포함한 함수를 말한다. </p>
<p><strong>비동기 함수를 호출하면 함수 내부의 비동기로 동작하는 코드가 완료되지 않았다 해도 기다리지 않고 즉시 종료된다. 즉, 비동기 함수 내부의 비동기로 동작하는 코드는 비동기 함수가 종료된 이후에 왼료된다. 따라서 비동기 함수 내부의 비동기로 동작하는 코드에서 처리결과를 외부로 반환하거나 상위 스코프의 변수에 할당하면 기대대로 동작하지 않는다.</strong> </p>
<p><code>plus</code> 함수가 비동기 함수인 이유는 <code>plus</code> 함수 내부의 <strong><code>setTimeout</code> 함수가 비동기로 동작</strong>하기 때문이다. <code>plus</code> 함수를 호출하면 <code>a</code> 를 반환하고 즉시 종료된다. 즉, <code>plus</code> 함수 내부의 <code>setTimeout</code> 의 콜백 함수 <code>() =&gt; console.log(++a)</code> 는 <code>plus</code> 함수가 종료된 이후에 실행된다. </p>
<p><code>plus</code> 함수의 기대하는 기능(1초 뒤 1 더해진 값 출력) 을 수행하기 위해 상위 스코프에 변수를 할당하면 어떨까?</p>
<pre><code class="language-jsx">let a = 1;
function plus() {
  setTimeout(()=&gt;console.log(++a), 1000);
    return a;
}

const result = plus();
console.log(&#39;result :&#39;, result); 1️⃣
console.log(&quot;a:&quot;, a); // 2️⃣ </code></pre>
<pre><code>result : 1
a: 1
// 1초 뒤
2</code></pre><p>여전히 <code>plus</code> 함수의 반환값으로 여전히 <code>1</code> 이 출력되는 문제가 발생한다. <code>setTimeout</code> 함수의 콜백함수는 언제나 2️⃣ 의 console.log 가 종료된 이후에 호출된다. 따라서 2️⃣ 의 시점에는 아직 전역변수 <code>a</code> 에 +1 한 결과가 할당되기 이전이다. </p>
<p>이런 현상이 발생하는 이유는 함수 실행 과정에 의해 설명된다. </p>
<ol>
<li><p><code>plus</code> 함수가 호출되면 <code>plus</code> 실행 컨텍스트가 콜스택에 푸시된다.</p>
<ol start="2">
<li>이후 함수 코드 실행과정에서 <code>setTimeout</code> 함수의 실행 컨텍스트가 콜 스택에 push 되어 실행되고 즉시 pop 된다. </li>
</ol>
</li>
<li><p>이후 브라우저는 타이머 설정 및  만료를 기다리고 타이머가 만료되면 <code>setTimout</code> 함수의 콜백 함수를 태스크 큐에 푸시한다. </p>
</li>
<li><p><code>plus</code> 함수가 종료하면 <code>plus</code> 실행 컨텍스트가 콜 스택에서 팝 되고, 곧바로 1️⃣과 2️⃣의 console.log 가 호출된다. </p>
</li>
<li><p>이후 1️⃣ 과 2️⃣ 가 종료되어 콜스택에서 제거되면 이벤트 루프에 의해 태스크 큐에 있던 <code>setTimeout</code> 의 콜백함수를 콜스택에 푸시되어 실행된다. </p>
</li>
</ol>
<p><strong>이처럼 비동기 함수는 비동기 처리 결과를 외부에 반환할 수 없고, 상위 스코프 변수에 할당할 수도 없다. 따라서 비동기 함수의 처리 결과에 대한 후속 처리는 비동기 함수 내부에서 수행해야 한다. 이 때, 비동기 함수를 범용적으로 사용하기 위해 비동기 함수에 비동기 처리 결과에 대한 후속처리를 수행하는 콜백함수를 전달하는 것이 일반적이다.</strong> </p>
<h1 id="비동기-처리-방법-1-콜백-패턴">비동기 처리 방법 1: 콜백 패턴</h1>
<h2 id="콜백-패턴-사용">콜백 패턴 사용</h2>
<pre><code class="language-jsx">function plus(callback) {
  let a = 1;
  setTimeout(() =&gt; callback(++a), 1000);
}

plus((result) =&gt; {
  console.log(&#39;Async result:&#39;, result);
});</code></pre>
<pre><code>// 1초 뒤
Async result: 2</code></pre><p>콜백 패턴을 사용하여 비동기 함수의 문제를 해결하려면, 함수가 비동기 작업을 완료한 후 콜백 함수를 호출하도록 수정해야 합니다. 위 코드는 <code>plus</code> 함수가 콜백을 인수로 받아서 비동기 작업이 완료된 후 콜백을 호출하는 방식으로 수정했다.</p>
<h2 id="콜백-패턴의-단점">콜백 패턴의 단점</h2>
<h3 id="콜백-지옥-발생">콜백 지옥 발생</h3>
<p>이처럼 비동기 함수가 콜백 함수를 통해 후속 처리를 수행하고, 그 결과로 또 다른 비동기 함수를 호출해야 할 때 콜백 호출이 중첩되어 복잡도가 높아지는 현상을 <strong>콜백 지옥</strong>이라고 한다.</p>
<pre><code class="language-jsx">function plus(num, callback) {
  setTimeout(() =&gt; callback(++num), 1000);
}

// 콜백 지옥 예시
plus(1, (result1) =&gt; {
  plus(result1, (result2) =&gt; {
    plus(result2, (result3) =&gt; {
      plus(result3, (result4) =&gt; {
          console.log(&#39;result 4&#39;, result4);
      });
    });
  });
});

// 4초 후
// result 4: 5 출력</code></pre>
<p>각 <code>plus</code> 함수 호출은 이전 호출의 결과를 받아서 다시 <code>plus</code> 함수를 호출한다. </p>
<p>이와 같이 콜백 패턴을 이용하면 코드의 가독성과 유지보수성이 떨어진다. 이를 해결하기 위해 <code>Promise</code>나 <code>async/await</code>를 사용한다.</p>
<pre><code class="language-jsx">function plus(num) {
  return new Promise((resolve) =&gt; {
    setTimeout(() =&gt; resolve(++num), 1000);
  });
}

// 프로미스 패턴을 사용한 비동기 처리
plus(1)
  .then((result1) =&gt; {
    return plus(result1);
  })
  .then((result2) =&gt; {
    return plus(result2);
  })
  .then((result3) =&gt; {
    return plus(result3);
  })
  .then((result4) =&gt; {
    console.log(&#39;result 4&#39;, result4);
  })
  .catch((error) =&gt; {
    console.error(&#39;Error:&#39;, error);
  });
</code></pre>
<h3 id="에러-처리-어려움">에러 처리 어려움</h3>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS 공부 - 생성자 함수에 의한 객체 생성]]></title>
            <link>https://velog.io/@lee-yun-pyo/JS-%EA%B3%B5%EB%B6%80-%EC%83%9D%EC%84%B1%EC%9E%90-%ED%95%A8%EC%88%98%EC%97%90-%EC%9D%98%ED%95%9C-%EA%B0%9D%EC%B2%B4-%EC%83%9D%EC%84%B1</link>
            <guid>https://velog.io/@lee-yun-pyo/JS-%EA%B3%B5%EB%B6%80-%EC%83%9D%EC%84%B1%EC%9E%90-%ED%95%A8%EC%88%98%EC%97%90-%EC%9D%98%ED%95%9C-%EA%B0%9D%EC%B2%B4-%EC%83%9D%EC%84%B1</guid>
            <pubDate>Mon, 22 Jul 2024 11:50:48 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/lee-yun-pyo/post/d06ea754-b627-4ac6-aaed-a82adf15ae3b/image.jpeg" alt="표지"></p>
<h1 id="생성자-함수란"><strong>생성자 함수란?</strong></h1>
<p><code>new</code> 연산자와 함께 호출하여 객체(인스턴스)를 생성하는 함수</p>
<p>자바스크립트는 여러 빌트인 생성자 함수를 제공한다.
<code>Object</code> <code>String</code> <code>Number</code> <code>Fuction</code> <code>Array</code> 등등</p>
<h2 id="객체-리터럴-사용-vs-생성자-함수-사용">객체 리터럴 사용 vs 생성자 함수 사용</h2>
<h3 id="객체-리터럴-사용의-문제점">객체 리터럴 사용의 문제점</h3>
<p>단 하나의 객체만 생성한다. </p>
<pre><code class="language-jsx">const circle1 = {
    r: 5,
    getDiameter() {
        return 2 * r;
    }
};

console.log(circle1.getDiameter()); // 10

const circle2 = {
    r: 10,
    getDiameter() {
        return 2 * r;
    }
};

console.log(circle2.getDiameter()); // 20</code></pre>
<p>보통 상태를 표현하는 프로퍼티는 객체마다 값이 다를 수 있지만 메서드는 내용이 동일하다. 하지만 객체 리터럴에 의해 객체를 생성하는 경우 프로퍼티 구조가 동일함에도 불구하고 매번 같은 프로퍼티와 메서드를 기술해야 한다. </p>
<h3 id="생성자-함수-사용의-장점">생성자 함수 사용의 장점</h3>
<p>생성자 함수에 의한 객체 생성 방식은 생성자 함수를 사용하여 프로퍼티 구조가 동일한 여러 객체를 간편하게 생성할 수 있다. </p>
<pre><code class="language-jsx">function Circle(r) {
    this.r = r;
    this.getDiameter = function () {
        return 2 * r;
    }
}

// 객체 생성
const circle1 = new Circle(5);

console.log(circle1.getDiameter()); // 10</code></pre>
<h2 id="생성자-함수는-객체를-반환하는데-왜-생성자함수에는-객체를-생성하고-반환하는-코드가-없는걸까">생성자 함수는 객체를 반환하는데 왜 생성자함수에는 객체를 생성하고 반환하는 코드가 없는걸까?</h2>
<p>→ 암묵적 처리를 통해 인스턴스를 생성하고 반환하기 때문!</p>
<h3 id="생성자-함수-인스턴스-생성-과정">생성자 함수 인스턴스 생성 과정</h3>
<p><strong>1. 인스턴스 생성과 this 바인딩</strong>
   암묵적으로 빈 객체가 생성되고 this에 바인딩 된다.</p>
<blockquote>
<p>💡 이것이 생성자 함수 내에 <code>this</code> 가 생성자 함수가 생성할 인스턴스를 가리키는 이유이다.
<a href="https://velog.io/@lee-yun-pyo/JS-%EA%B3%B5%EB%B6%80-this">this 블로그 참고</a></p>
</blockquote>
<pre><code class="language-jsx">    function Circle(r) {
        // 1. 암묵적으로 인스턴스 생성 후 this 바인딩
        console.log(this); // Circle {}

        this.r = r;
        this.getDiameter = function () {
            return 2 * r;
        }
    }</code></pre>
<p><strong>2. 인스턴스 초기화</strong>
생성자 함수의 코드가 한 줄씩 실행되어 <code>this</code> 에 바인딩되어있는 인스턴스를 초기화 한다.</p>
<pre><code class="language-jsx">    function Circle(r) {
        // 1. 암묵적으로 인스턴스 생성 후 this 바인딩

        // 2. this에 바인딩되어 있는 인스턴스 초기화
        this.r = r;
        this.getDiameter = function () {
            return 2 * r;
        }
    }</code></pre>
<p><strong>3. 인스턴스 반환</strong>
생성자 함수 내부에서 모든 처리가 끝나면 암묵적으로 인스턴스가 바인된된 <code>this</code> 를 반환한다.</p>
<pre><code class="language-jsx">    function Circle(r) {
        // 1. 암묵적으로 인스턴스 생성 후 this 바인딩

        // 2. this에 바인딩되어 있는 인스턴스 초기화
        this.r = r;
        this.getDiameter = function () {
            return 2 * r;
        }
        // 3. 완성된 인스턴스가 바인딩된 this가 암묵적으로 반환
    }

    // 인스턴스 생성
    const circle = new Circle(5); </code></pre>
<h3 id="만약-this-아닌-다른-객체를-명시적으로-반환한다면">만약 this 아닌 다른 객체를 명시적으로 반환한다면?</h3>
<p>→ this가 반환되지 못하고 <code>return</code> 문에 명시된 객체가 반환된다. </p>
<pre><code class="language-jsx">function Circle(r) {
    this.r = r;
    this.getDiameter = function () {
        return 2 * r;
    }

    // 명시적으로 객체 반환
    return {};
}

// 인스턴스 생성. 명시적으로 객체를 반환하면 암묵적인 this 반환이 무시되고
// 명시적으로 반환한 객체를 반환한다.
const circle = new Circle(5); // {}</code></pre>
<p>하지만 명시적으로 원시값을 반환하면 원시 값 반환은 무시되고 암묵적으로 this가 반환된다. </p>
<h2 id="내부-메서드-call-과-constructor">내부 메서드 [[Call]] 과 [[Constructor]]</h2>
<p>함수는 객체다. 즉, 일반 객체가 가지고 있는 내부 슬롯과 내부 메서드를 모두 가지고 있다. </p>
<p>함수는 일반 객체와 다른 점은 호출할 수 있다는 것이다. 따라서 함수는 일반 객체가 가지는 내부 슬롯과 내부 메서드는 물론 함수로서 함수객체를 위한 내부 슬롯과 내부 메서드 <code>[[Call]], [[Constructor]]</code> 도 가지고 있다. </p>
<p>함수를 생성자함수로서 사용되기 위해서는 <code>new</code> 연산자와 함께 호출되어야 한다. 하지만 모든 함수가 <code>new</code> 연산자를 사용하여 생성자 함수로 사용될 수 있는 것은 아니다. </p>
<blockquote>
<p> 내부 메서드 <code>[[Call]]</code> 을 갖는 함수 객체를 <strong>callable</strong> 이라 하며,
 내부 메서드 <code>[[Construct]]</code>를 갖는 함수 객체를 <strong>constructor</strong>,
 <code>[[Constructor]]</code> 를 갖지 않는 함수 객체를 <strong>non-constructor</strong> 라고 한다.</p>
</blockquote>
<p>모든 함수는 <code>callable</code> 이자만 <code>callable</code> 이면서 생성자 함수로서 호출될 수 있는 함수인 <code>constructor</code> 와 생성자 함수로서 호출될 수 없는 함수인 <code>non-constructor</code> 로 구분된다. </p>
<p>이에 자바스크립트 엔진은 함수 정의 방식에 따라서 이 둘을 구분한다. </p>
<blockquote>
<p><code>constructor</code>
    - 함수 표현식, 함수 선언문, 클래스
<code>non-constructor</code> 
    - 메서드(축약 표현), 화살표 함수</p>
</blockquote>
<pre><code class="language-jsx">// 일반 함수 정의: 함수 선언문, 함수 표현식
function foo() {}
const bar = function () {};

// 일반함수로 정의된 함수만이 constructor 이다. 
new foo(); // {}
new bar(); // {}

// 화살표 함수 정의
const arrow = () =&gt; {};

new arrow(); // TypeError</code></pre>
<h3 id="new-연산자">new 연산자</h3>
<p><code>new</code> 연산자와 함수를 호출하면 해당 함수는 생성자 함수로 동작한다. 즉, 함수 내부 객체 메서드가 <code>[[Call]]</code> 이 아닌 <code>[[Constructor]]</code> 가 호출된다. </p>
<p>반대로 <code>new</code> 연산자 없이 함수를 호출하면 해당함수는 일반함수로 동작한다. 즉, 함수 내부 객체 메서드가 <code>[[Constructor]]</code> 이 아닌 <code>[[Call]]</code> 이 호출된다. </p>
<p>두 호출 방식에 따라 함수 내부의 <code>this</code> 에 바인딩된 값이 다르다.</p>
<p><code>new</code> 연산자로 호출한 함수의 <code>this</code> 는 생성자 함수가 생성할 인스턴스를 가리킨다. </p>
<p>일반 함수로서 호출한 함수의 <code>this</code> 는 전역객체를 가리킨다. </p>
<blockquote>
<p>일반 함수와 생성자 함수의 형식적 차이는 없다. 따라서 이를 구분하기 위해 생성자 함수를 선언할 때 일반적으로 파스칼 케이스(첫문자를 대문자로 기술)로 명명한다.</p>
</blockquote>
<h2 id="참고">참고</h2>
<ul>
<li><a href="https://ebook-product.kyobobook.co.kr/dig/epd/ebook/E000002928532">모던 자바스크립트 Deep Dive</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[History API 로 SPA 구현]]></title>
            <link>https://velog.io/@lee-yun-pyo/History-API-%EB%A1%9C-SPA-%EA%B5%AC%ED%98%84</link>
            <guid>https://velog.io/@lee-yun-pyo/History-API-%EB%A1%9C-SPA-%EA%B5%AC%ED%98%84</guid>
            <pubDate>Fri, 28 Jun 2024 17:39:22 GMT</pubDate>
            <description><![CDATA[<p>자바스크립트로 URL 라우팅을 구현하려고 한다. 바닐라 자바스크립트를 사용중이고 클라이언트 단 라우팅을 지원하는 라이브러리를 사용할 수 없기 때문에 자바스크립트의 <code>Histroy API</code> 를 사용하였다. </p>
<h3 id="요구사항">요구사항</h3>
<p>로그인 버튼이나 회원가입 버튼, 홈 버튼 등을 클릭할 때 URL를 변경시키고 동시에 UI도 변경시킨다.</p>
<h2 id="js로-클라이언트-단에서-라우팅-처리">JS로 클라이언트 단에서 라우팅 처리</h2>
<h3 id="navigate-함수-생성">navigate() 함수 생성</h3>
<p>이 <code>navigate()</code> 함수는 상태 객체를 인자로 받고, 상태 객체 안에 담겨있는 <code>url</code> 속성을 읽어서 <code>url</code> 에 맞는 UI를 화면에 보여준다. 따라서 이 함수를 호출하면 HTML에서 페이지의 내용이 바뀐다.</p>
<pre><code class="language-jsx">/**
 * 주어진 상태에 따라 해당 URL에 맞는 뷰를 표시.
 * @param {Object} state - 상태 객체.
 */
function navigate(state) {
  const { url } = state;
  const views = {
    chatting: document.querySelector(&quot;#chattingView&quot;),
    login: document.querySelector(&quot;#loginView&quot;),
  };

  switch (url) {
    case ROUTES.HOME:
      updateView(views, views.chatting);
      break;
    case ROUTES.LOGIN:
      updateView(views, views.login);
      break;
    default:
      break;
  }
}

/**
 * 업데이트할 뷰를 표시하고 다른 뷰를 숨긴다.
 * @param {Object} views - 모든 뷰를 담고 있는 객체.
 * @param {HTMLElement} viewToShow - 표시할 뷰의 DOM 요소.
 */
function updateView(views, viewToShow) {
  Object.values(views).forEach((view) =&gt; view.classList.add(&quot;hidden&quot;));
  viewToShow.classList.remove(&quot;hidden&quot;);
}</code></pre>
<h3 id="updateroute-핸들러-생성">updateRoute 핸들러 생성</h3>
<p>이제 헤더 버튼에 클릭 이벤트 핸들러를 설정한다.</p>
<p>사용자가 각 버튼을 클릭하면 브라우저의 주소 표시줄의 URL이 바뀔 수 있도록 <code>history.pushState()</code> 메서드를 호출한다. 첫 번째 인자에는 경로 정보가 담겨있는 상태 객체를 넘기고, 세 번째 인자로는 브라우저의 주소 표시줄에 표시할 경로를 문자열로 넘김니다. 마지막으로 위에서 작성한 <code>navigate()</code> 함수에 동일한 상태 객체를 넘겨서 페이지 내용이 바뀌게한다.</p>
<pre><code class="language-jsx">/**
 * 주어진 URL로 브라우저의 히스토리 상태를 업데이트 및 해당 상태로 navigate.
 * @param {string} url - 새로 업데이트할 대상 URL
 */
export function updateRoute(url) {
  const state = { url };
  if (url === window.location.pathname) {
    history.replaceState(state, &quot;&quot;, url);
  } else {
    history.pushState(state, &quot;&quot;, url);
  }
  navigate(state);
}

// 핸들러 등록
$headerTitle.addEventListener(&quot;click&quot;, () =&gt; updateRoute(ROUTES.HOME));</code></pre>
<p>이 때, 동일한 버튼을 연속적으로 클릭하면 동일한 history가 history stack 에 쌓이기 때문에 이를 방지하기 위해 <code>replaceState()</code> 메서드를 사용한다. </p>
<h3 id="popstateevent-이벤트-핸들러-등록"><strong>PopStateEvent 이벤트 핸들러 등록</strong></h3>
<p>여기까지만 하면 URL 변경에 따라 UI가 적절하게 변경된다. 하지만 ‘뒤로가기’ 또는 ‘앞으로 가기’를 클릭하면 주소표시창에 URL만 변경될 뿐 페이지의 UI는 그대로다.</p>
<p>이 문제를 해결하기 위해 윈도우에서 발생하는 <code>popstate</code> 이벤트를 감지해서 라우팅 처리해준다. 위에서 <code>pushState()</code> 와 <code>replaceState()</code> 메서드의 첫 번째 인자로 상태 객체를 넘겨주었기 때문에, 해당 페이지로 뒤로 가기나 앞으로 가기가 되면, 이벤트 핸들러에 매개 변수로 해당 상태 객체가 넘어오게 된다. 따라서 이 상태 객체를 그대로 <code>navigate()</code> 함수에 인자로 넘겨준다.</p>
<pre><code class="language-jsx">/**
 * popstate 이벤트가 발생할 때 호출되어, 이벤트 상태를 가지고 navigate 한다.
 * @param {PopStateEvent} event - popstate 이벤트 객체.
 */
function popStateHandler(event) {
  const { state } = event;
  navigate(state);
}

window.addEventListener(&quot;popstate&quot;, popStateHandler);</code></pre>
<h2 id="데모">데모</h2>
<p><img src="https://velog.velcdn.com/images/lee-yun-pyo/post/c64b34c4-0c76-422c-a4cf-ff3da42b894d/image.gif" alt="histroy API 데모"></p>
<p>해당 코드 깃허브 주소 <a href="https://github.com/lee-yun-pyo/Login-Practice">https://github.com/lee-yun-pyo/Login-Practice</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Histroy API]]></title>
            <link>https://velog.io/@lee-yun-pyo/Histroy-API</link>
            <guid>https://velog.io/@lee-yun-pyo/Histroy-API</guid>
            <pubDate>Fri, 28 Jun 2024 16:48:14 GMT</pubDate>
            <description><![CDATA[<p><code>history</code> 전역 객체를 통해 브라우저 세션 히스토리에 대한 접근할 수 있다. 사용자의 방문 기록을 앞뒤로 탐색하고, 방문 기록 스택의 내용을 조작할 수 있는 유용한 메서드와 속성을 제공한다.</p>
<h3 id="세션-히스토리란">세션 히스토리란?</h3>
<p><img src="https://velog.velcdn.com/images/lee-yun-pyo/post/d63dcb05-2d4b-4621-b8cd-704cd17d42ea/image.png" alt="History API"></p>
<p>브라우저에서 페이지를 이동하면 위 사진처럼 페이지 이동 이력이 나타나는 데 이것을 <strong>history 스택</strong>이라고 한다. History API 를 이용하면 이 history 스택을 조작할 수 있다. </p>
<p>History 객체는 브라우저의 세션 기록(현재 페이지를 불러온 탭 혹은 프레임이 방문했던 페이지)을 조작할 때 사용합니다.</p>
<h2 id="사용법">사용법</h2>
<h3 id="사용자-방문-기록을-앞-뒤로-이동하기">사용자 방문 기록을 앞 뒤로 이동하기</h3>
<p><code>back()</code> , <code>forward()</code> , <code>go()</code> 사용</p>
<pre><code class="language-jsx">histroy.back(); // 뒤로 이동
histroy.forward(); // 앞으로 이동

// 특정 지점으로 이동 
histroy.go(-1); // 뒤로 가기
history.go(1); // 한 페이지 앞으로 이동
history.go(2); // 두 페이지 앞으로 이동

// 새로고침
history.go();
history.go(0);

console.log(histroy.length); // 방문 기록 스택 페이지 수</code></pre>
<p>이 세 메서드를 호출하면 브라우저는 해당 페이지를 reload 하기 때문에 SPA 에서 잘 사용되지 않는다.</p>
<h2 id="주소창-url-조작">주소창 URL 조작</h2>
<p>History API는 브라우저의 현재 URL 조작할 수 있도록 <code>pushState()</code>와 <code>replaceState()</code> 메서드를 제공한다. 위에서 다룬 실제로 페이지가 이동하는 메서드와 다르게 이 2개의 메서드는  브라우저 주소 표시줄의 URL만 갱신되고 실제로 해당 페이지가 다시 불러오지는 않는다.</p>
<pre><code class="language-jsx">pushState(state, unused, url)
replaceState(state, unused, url)</code></pre>
<p>두 메서드 모두 3개의 매개변수를 받는다. </p>
<p>첫 번째 인자는 이 URL에 연관된 <strong>상태 객체</strong>를 의미한다. <code>pushState()</code>와 <code>replaceState()</code> 가 호출되면 필수적으로 histrory 스택이 변경이 된다. 이 때, 스택에 들어가는 아이템의 정보로 이 <strong>상태 객체</strong>가 들어간다. url 정보 뿐만 아니라 다른 정보를 넣고 싶을 때 이 매개변수를 이용하면 된다. </p>
<p>두 번째 인자는 하위 호환성 때문에 존재하는 것으로 그냥 빈 문자열 <code>&quot;&quot;</code> 을 넘긴다.</p>
<p>세 번째 인자는 <strong>새로 변경할 URL</strong> 을 넘긴다. (상대 경로, 절대 경로 둘 다 가능). 새 URL은 현재 URL과 같은 출처여야 한다. </p>
<p><code>pushState()</code> 메서드와 <code>replaceState()</code> 메서드 모두 <strong>현재 세션 히스토리를 변경하여 url을 변경</strong>시킨다. 하지만 세션 히스토리를 변경시키는 방식에 차이가 있다. </p>
<h3 id="pushstate"><code>pushState()</code></h3>
<p>인자로 넘어온 URL을 현재 페이지의 바로 다음 방문 기록으로 추가하고, 그 이후의 방문 이력을 있다면 모두 지운다.</p>
<pre><code class="language-jsx">// a -&gt; b -&gt; c -&gt; d -&gt; e
//      ⬆ 현재 페이지 
history.pushState({}, &quot;&quot;, &quot;x&quot;);
// a -&gt; b -&gt; x
//           ⬆ 현재 페이지
history.pushState({}, &quot;&quot;, &quot;y&quot;);
// a -&gt; b -&gt; x -&gt; y
//                ⬆ 현재 페이지
history.back();
// a -&gt; b -&gt; x -&gt; y
//           ⬆ 현재 페이지</code></pre>
<p>따라서 <code>pushState()</code> 메서드를 호출한 이후 브라우저에서 ‘뒤로가기’ 버튼을 클릭해 원래 페이지로 돌아가는 것이 가능하지만 ‘앞으로 가기’ 버튼은 비활성화 된다. </p>
<h3 id="replacestate"><code>replaceState()</code></h3>
<p>인자로 넘어온 URL이 현재 페이지를 대체한다.</p>
<p>현재 페이지 다음에 있는 방문이력은 그대로 둔다. </p>
<pre><code class="language-jsx">// a -&gt; b -&gt; c -&gt; d -&gt; e
//      ⬆ 현재 페이지
history.replaceState({}, &quot;&quot;, &quot;x&quot;);
// a -&gt; x -&gt; c -&gt; d -&gt; e
//      ⬆ 현재 페이지
history.back();
// a -&gt; x -&gt; c -&gt; d -&gt; e
// ⬆ 현재 페이지</code></pre>
<p>따라서 <code>replaceState()</code> 메서드를 호출한 이후 ‘뒤로 가기’ 버튼을 누르면 원래 페이지의 이전 페이지로 이동하고 ‘앞으로 가기’ 버튼을 누르면 다음 페이지로 이동할 수 있다. </p>
<h2 id="popstateevent"><code>PopStateEvent</code></h2>
<p><code>PopStateEvent</code> 는 브라우저에서 뒤로 가기나 앞으로 가기를 할 때 <code>window</code> 전역 객체에서 발생한다. 그러므로 이벤트 핸들러로 해당 URL에 부합하는 내용을 클라이언트에 보여줄 수 있다. </p>
<p>이 때 사용하는 것이 <code>pushState()</code> 메서드와 <code>replaceState()</code> 메서드의 첫 번째 매개변수인 상태 객체이다. 상태 객체를 읽어서 적절한 처리를 할 수 있다. </p>
<pre><code class="language-jsx">window.addEventListener(&quot;popstate&quot;, (event) =&gt; {
    const { state } = event;
});</code></pre>
<p>이 <code>PopStateEvent</code> 는 SPA 구성할 때 페이지 이동 처리를 하기 위해 필수적으로 사용하는 이벤트이다. 실제 웹사이트 자체가 바뀌지 않는데 클라이언트 화면을 변경해주려면 해당 히스토리 이벤트를 프로그래머가 알아야 하는 데 <code>PopStateEvent</code> 가 이를 해결한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[package.json 의존성 관리]]></title>
            <link>https://velog.io/@lee-yun-pyo/package.json-%EC%9D%98%EC%A1%B4%EC%84%B1-%EA%B4%80%EB%A6%AC</link>
            <guid>https://velog.io/@lee-yun-pyo/package.json-%EC%9D%98%EC%A1%B4%EC%84%B1-%EA%B4%80%EB%A6%AC</guid>
            <pubDate>Tue, 25 Jun 2024 12:23:10 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/lee-yun-pyo/post/38527925-7f0e-4c40-86a9-cba27f1f26e9/image.png" alt=""></p>
<h1 id="packagejson">package.json</h1>
<p><code>package.json</code>은 Node.js 프로젝트에서 사용되는 설정 파일로 프로젝트의 메타데이터와 함께 프로젝트에 필요한 의존성 패키지들을 정의하는 역할을 한다. 주로 프로젝트 루트 디렉토리에 위치하며, JSON 형식으로 작성된다.</p>
<h2 id="dependencies">dependencies</h2>
<p><code>package.json</code> 파일에서 <code>dependencies</code>는 프로젝트가 정상적으로 동작하는 데 필요한 패키지와 모듈들을 정의하는 부분이다. 이 섹션에 나열된 패키지들은 <code>npm install</code> 명령어를 실행하면 자동으로 설치된다. 각 패키지는 이름과 버전으로 지정된다.</p>
<p><code>dependencies</code> 에 존재하는 패키지들은 프로젝트가 실행될 때 반드시 필요한 패키지들이다. </p>
<pre><code class="language-json">&quot;dependencies&quot;: {
  &quot;express&quot;: &quot;^4.17.1&quot;,
  &quot;mongoose&quot;: &quot;^5.10.7&quot;
}.</code></pre>
<blockquote>
</blockquote>
<p>💡 <strong>Please do not put test harnesses or transpilers or other &quot;development&quot; time tools in your <code>dependencies</code> object. <br/>
<code>dependencies</code></strong> 에 테스트 하네스, 트랜스필러 또는 기타 &quot;개발&quot; 시간 도구를 넣지 마세요. <br/>
<a href="https://docs.npmjs.com/cli/v7/configuring-npm/package-json#dependencies">npm 공식 문서</a></p>
<h2 id="devdependencies">devDependencies</h2>
<p><code>devDependencies</code>는 <code>package.json</code> 파일에서 프로젝트의 개발 환경에서만 필요한 패키지와 모듈들을 정의하는 섹션이다. 이 패키지들은 애플리케이션의 실제 실행에는 필요하지 않지만, 개발 및 테스트, 빌드 과정에서 필요하다. </p>
<p>보통 <strong>테스팅 프레임워크(<code>jest</code>), 빌드 도구(<code>webpack</code>), 린팅 도구(<code>eslint</code>), 개발 서버(<code>nodemon</code>)</strong> 등에 사용된다. </p>
<pre><code class="language-json">&quot;devDependencies&quot;: {
  &quot;jest&quot;: &quot;^26.6.3&quot;,
  &quot;eslint&quot;: &quot;^7.10.0&quot;
}</code></pre>
<p>이 섹션에 있는 <code>jest</code>와 <code>eslint</code> 패키지는 개발 중에만 필요하고 실제 애플리케이션이 배포되고 실행될 때는 이 패키지들이 필요하지 않다. </p>
<p><code>devDependencies</code>에 있는 패키지들이 설치되지 않기 위해서 다음 명령어를 실행하면 된다. </p>
<pre><code class="language-jsx">npm install --production</code></pre>
<h2 id="버전-범위-기호">버전 범위 기호</h2>
<p><code>package.json</code> 파일에서 버전 범위를 나타내는 기호들이 존재한다. 주로 semver(Semantic Versioning)를 따르는 패키지의 버전을 지정할 때 사용된다. </p>
<blockquote>
</blockquote>
<p>💡 <strong>Semantic Versioning (semver)</strong>
버전을  <code>MAJOR.MINOR.PATCH</code> 형식으로 나타낸다. (<code>주 버전.부 버전.수정 버전</code>) <br/></p>
<ol>
<li>기존 버전과 호환되지 않게 API가 바뀌면 <code>MAJOR</code> 을 올리고,</li>
<li>기존 버전과 호환되면서 새로운 기능을 추가할 때는 <code>MINOR</code> 을 올리고,</li>
<li>기존 버전과 호환되면서 버그를 수정한 것이라면 <code>PATCH</code> 을 올린다.</li>
</ol>
<h3 id="-기호"><code>^</code> 기호</h3>
<p><code>^</code> 기호는 주어진 버전과 호환되는 모든 새로운 버전을 허용한다는 의미이다. 구체적으로는 <code>MAJOR</code> 가 변경되지 않는 한, 가능한 최신 버전을 설치할 수 있게 한다.</p>
<blockquote>
</blockquote>
<p>💡 ex) <code>^4.17.1</code>는 다음과 같은 의미를 갖는다.</p>
<ul>
<li><code>4.17.1</code> 이상이면서 <code>4.x.x</code> 버전의 범위 내에서</li>
<li>가능한 최신 버전 (즉, 주 버전 4가 유지되는 한 최신 버전)</li>
</ul>
<hr>
<p> <code>~4.17.1</code> 이 허용하는 버전과 아닌 버전 다음과 같다. </p>
<ul>
<li>✅ <code>4.17.1</code> <code>4.17.2</code> <code>4.18.0</code> <code>4.19.3</code></li>
<li>❌ <code>5.0.0</code> 이상</li>
</ul>
<h3 id="-기호-1"><code>~</code> 기호</h3>
<p><code>~</code> 기호는 주어진 버전과 호환되는 수정 버전(<code>PATCH</code>) 업데이트만 허용합니다. 부 버전(<code>MINOR</code>)과 주 버전(<code>MAJOR</code>)은 고정된다.</p>
<blockquote>
</blockquote>
<p>💡 ex) <code>~4.17.1</code> 은 다음과 같은 의미를 갖는다.</p>
<ul>
<li><code>4.17.1</code> 이상이면서 <code>4.17.x</code> 버전의 범위 내에서</li>
<li>가능한 최신 버전 (즉, 부 버전 17이 유지되는 한 수정 버전은 최신 버전 가능)</li>
</ul>
<hr>
<p> <code>~4.17.1</code> 이 허용하는 버전과 아닌 버전 다음과 같다. </p>
<ul>
<li>✅ <code>4.17.1</code> <code>4.17.2</code> <code>4.17.3</code></li>
<li>❌ <code>4.18.0</code> 이상</li>
</ul>
<h3 id="그-외-기호들">그 외 기호들</h3>
<p><code>&gt;</code> 기호: 지정된 버전보다 높은 모든 버전을 허용한다.</p>
<blockquote>
<p>💡 <code>&gt;4.17.1</code>는 다음과 같은 의미를 갖는다.</p>
</blockquote>
<ul>
<li><code>4.17.1</code> 보다 높은 버전 (즉, <code>4.17.1</code> 제외)</li>
</ul>
<hr>
<p>따라서, <code>&gt;4.17.1</code> 이 허용하는 버전과 아닌 버전 다음과 같다. </p>
<ul>
<li>✅ <code>4.17.2</code> <code>4.18.0</code></li>
<li>❌ <code>5.0.0</code> <code>4.17.1</code> , 그 이상</aside>

</li>
</ul>
<p>이 외에도 이와 비슷한  <code>&gt;=</code>, <code>&lt;</code> <code>&lt;=</code> 가 있다. </p>
<h3 id="의존성-범위">의존성 범위</h3>
<p>일반적으로 의존성의 범위는 가능한 한 좁게 설정하여 안정성과 예측 가능성을 높이는 것이 좋다. 그러나 특정한 경우에는 유연성을 제공하기 위해 넓은 범위를 사용할 수 있다.</p>
<pre><code class="language-jsx">&quot;dependencies&quot;: {
  &quot;express&quot;: &quot;4.17.1&quot; // 좁은 범위 예시
  &quot;express&quot;: &quot;^4.17.1&quot; // 넓은 범위 예시
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[프론트에서 MVC 패턴과 Flux 패턴]]></title>
            <link>https://velog.io/@lee-yun-pyo/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%90%EC%84%9C-MVC-%ED%8C%A8%ED%84%B4%EA%B3%BC-Flux-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@lee-yun-pyo/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%90%EC%84%9C-MVC-%ED%8C%A8%ED%84%B4%EA%B3%BC-Flux-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Mon, 24 Jun 2024 02:59:51 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/lee-yun-pyo/post/a8334e47-dea8-45eb-b9d0-eb053c672559/image.png" alt=""></p>
<p>리액트 등장 전 Angular JS와 같은 프레임워크는 MVC 패턴을 유지하고 있었다. 이후 MVC 패턴의 문제점을 해결하기 위해 Flux 패턴이 등장했다. </p>
<p>먼저 MVC 패턴에 대해 알아보고 Flux 패턴에 대해 알아보자.</p>
<h1 id="mvc-패턴이란">MVC 패턴이란?</h1>
<p>Model, View, Controller 의 약자로 <strong>Model에 상태를 저장</strong>하고 <strong>Controller가 Model를 변경</strong>시킨다. 또한 <strong>Model이 변경되면 View를 업데이트</strong> 시킨다. 또한 View 에서도 Model를 변경시킬 수 있다. </p>
<h2 id="프론트에서-mvc-패턴">프론트에서 MVC 패턴</h2>
<h3 id="frontend-view-에서-일어나는-일들">[Frontend] View 에서 일어나는 일들</h3>
<p>사용자가 입력을 하거나, 화면을 선택 및 클릭, 서버와 통신 등 View에서 많은 일들이 일어난다. 이는 View와 Model이 서로 영향을 줄 수 있음을 의미한다. </p>
<blockquote>
</blockquote>
<p>💡 <strong>1. View → Model</strong>
ex) 사용자 입력으로 Model 변경
<strong>2. View ← Model</strong>
ex) 서버로부터 데이터를 받아서 View 에 데이터 출력
결국, <strong>View ↔ Model</strong> 이 <strong>양방향</strong>으로 데이터가 흐르는 것을 알 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/lee-yun-pyo/post/16b1f645-09f5-481a-9fd9-d599bde2b6e9/image.png" alt="양방향 흐름"></p>
<h3 id="많은-view를-가지는-프론트">많은 View를 가지는 프론트</h3>
<p>하지만 프론트엔드에서는 View 개수가 매우 많다 (물론 1~2개 있을 수 있지만 View 규모가 커질 것이다. 따라서 우리는 View를 분리하여 여러 개의 작은 뷰를 만든다). 그리고 Model 도 다수 존재할 수 있다. </p>
<p>이는 View 와 Model 간 의존성과 복잡도가 증가시킨다. 그리고 이를 보안하기 위해 많은 View 와 Model 를 상호 연결하는 매우 큰 Controller 가 필요하다.  </p>
<p><img src="https://velog.velcdn.com/images/lee-yun-pyo/post/8eae082a-1d89-411c-99fc-7c1cabb3bb60/image.png" alt=""></p>
<h3 id="mvc-패턴의-한계">MVC 패턴의 한계</h3>
<p>이렇게 되면 다수의 View와 Model이 존재하고 한 View에서 여러 Model를 변경시키고 한 Model에서 여러 View를 변경시키는 문제가 발생한다. 이는 어떤 View에서 상태를 변경시키면 다른 View에도 상태가 업데이트가 표시되는 등 추적하기 어려워 진다. </p>
<p>결국, 복잡도가 증가하고 데이터의 흐름을 파악하고 예측하기 힘들어진다.</p>
<p>이를 해결하기 위해 고안된 것이 Flux 패턴이다.</p>
<hr>
<h1 id="flux-패턴">Flux 패턴</h1>
<p>Flux 패턴은 Facebook에서 개발된 애플리케이션 아키텍처로, <strong>단방향 데이터 흐름</strong>을 통해 애플리케이션의 상태 관리를 단순화하고 <strong>예측 가능</strong>하게 한다. Flux 패턴은 주로 대규모 애플리케이션에서 상태 관리의 복잡성을 줄이기 위해 사용된다.</p>
<p><img src="https://velog.velcdn.com/images/lee-yun-pyo/post/db916767-a2a0-4d84-ba38-fc0e369a6863/image.png" alt="Flux 패턴의 흐름과 구성 요소"></p>
<h2 id="구성-요소">구성 요소</h2>
<ul>
<li><strong>Action (액션)</strong>:<ul>
<li>사용자나 다른 소스로부터 발생하는 이벤트이다.</li>
<li>액션 객체는 <code>type</code> 필드와 함께 데이터를 포함하여 디스패처에게 전달한다.</li>
<li>ex) <code>{ type: &#39;ADD_TODO&#39;, payload: { text: &#39;Learn Flux&#39; } }</code>.</li>
</ul>
</li>
<li><strong>Dispatcher (디스패처)</strong>:<ul>
<li>모든 액션이 중앙 디스패처를 통해 전달된다.</li>
<li>디스패처는 액션을 받아서 등록된 모든 스토어에 전달하여 스토어 상태를 조작한다.</li>
<li>단일 디스패처를 사용하여 데이터 흐름을 중앙에서 관리한다.</li>
</ul>
</li>
<li><strong>Store (스토어)</strong>:<ul>
<li>애플리케이션의 상태와 상태를 변경하는 로직을 가진다.</li>
<li>스토어는 액션에 따라 상태를 업데이트한다.</li>
<li>디스패처에 의해 상태가 변경되면 스토어는 뷰에게 변경사항을 알린다.</li>
<li>스토어는 상태를 보유하고 있으므로 컨트롤러 역할을 한다.</li>
</ul>
</li>
<li><strong>View (뷰):</strong> 사용자에게 데이터를 표시하는 UI 컴포넌트.<ul>
<li>뷰는 스토어로부터 상태를 받아와서 렌더링한다.</li>
<li>뷰에서 이벤트(사용자 입력 등)를 발생시키면 액션을 생성하여 디스패처에 전달한다.</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/lee-yun-pyo/post/e76d1745-1d3f-4130-b34a-40bf0d5fee1c/image.png" alt=""></p>
<p>각 요소들은 단방향 흐름에 따라 순서대로 역할을 수행하고 Action 이 View(사용자 입력 등)나 다른 소스(서버로부터 데이터 응답 등)로 부터 발생하면 처음부터 다시 순서대로 실행된다.</p>
<p>이로 인해 단방향 데이터 흐름으로 인해 상태 관리가 쉬워지고, 디버깅이 용이해지며, 애플리케이션의 복잡성이 낮아진다. </p>
<h2 id="데이터-흐름-도식화">데이터 흐름 도식화</h2>
<p>ex) To Do List에서 flux 패턴 적용</p>
<p><img src="https://velog.velcdn.com/images/lee-yun-pyo/post/80667dde-44b5-4b72-b85a-ce704e29d281/image.gif" alt="데이터 흐름"></p>
<h2 id="참고">참고</h2>
<ul>
<li><a href="https://redux.js.org/understanding/history-and-design/history-of-redux#2015-the-birth-of-redux">리덕스 공식 문서 - A (Brief) History of Redux
</a></li>
<li><a href="https://www.youtube.com/watch?v=Y5vOfv67h8A">유튜브</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS 공부 - this]]></title>
            <link>https://velog.io/@lee-yun-pyo/JS-%EA%B3%B5%EB%B6%80-this</link>
            <guid>https://velog.io/@lee-yun-pyo/JS-%EA%B3%B5%EB%B6%80-this</guid>
            <pubDate>Thu, 06 Jun 2024 12:27:29 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/lee-yun-pyo/post/d15c3b8c-c267-46ed-8a6a-e23aeed2b351/image.jpeg" alt=""></p>
<h1 id="this-란">this 란?</h1>
<ul>
<li>자신이 속한 <strong>객체</strong> 또는 자신이 생성할 <strong>인스턴스</strong>를 가리키는 <strong>자기 참조 변수</strong></li>
</ul>
<h2 id="this-의-목적">this 의 목적</h2>
<ul>
<li>생성자 함수 내부에서 프로퍼티나 메서드를 추가하기 위해 자신이 생성할 인스턴스를 참조할 수 있어야 한다. 하지만, 생성자 함수로 인스턴스를 생성하려면 먼저 생성자 함수가 존재하여야 한다.</li>
<li>생성자 함수를 정의하는 시점에는 아직 인스턴스 생성하기 전이므로 생성자 함수가 생성할 인스턴스를 가리키는 식별자를 알 수 없다. 따라서 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 특수한 식별자가 필요하다.</li>
</ul>
<h2 id="this-바인딩">this 바인딩</h2>
<ul>
<li><code>this</code> 는 함수가 호출되는 방식에 따라 <code>this</code> 에 바인딩될 값이 결정된다. (동적)</li>
<li>렉시컬 스코프와 반대</li>
</ul>
<h1 id="함수-호출-방식에-따른-this-바인딩">함수 호출 방식에 따른 <code>this</code> 바인딩</h1>
<h2 id="1-일반-함수-호출">1. 일반 함수 호출</h2>
<ul>
<li>일반 함수로 호출하면 함수 내부 <code>this</code> 에는 <strong>전역 객체가 바인딩</strong> 된다.</li>
<li>하지만 <code>this</code> 는 객체 프로퍼티나 메서드를 참조하기 위한 자기 참조 변수이므로 <strong>객체를 생성하지 않는 일반 함수에서는 의미가 없다</strong>.</li>
</ul>
<pre><code class="language-jsx">var value = 1;

const obj = {
    value: 100,
    foo() {
        console.log(this); // { value: 100, foo: *f* }
        console.log(this.value); // 100

        function bar() {
            console.log(this); // window
            console.log(this.value); // 1
        }

        bar(); // 📌 일반 함수 호출
    }
};

obj.foo()</code></pre>
<p><small>메서드의 중첩함수도 일반 함수로 호출되면 this에는 전역객체 바인딩</small></p>
<p>일반 함수로 호출된 모든함수(중첩함수, 콜백함수 등) 내부의 <code>this</code> 에는 전역 객체가 바인딩된다. </p>
<blockquote>
<p>💡  (메서드 내부 중첩함수 또는 콜백함수의 <code>this</code> 바인딩 <strong>≠</strong> 메서드 내 <code>this</code> 바인딩<strong>)</strong></p>
</blockquote>
<blockquote>
<p>💡 <strong>메서드 내의 중첩 함수 또는 콜백함수</strong>
중첩 함수 또는 콜백함수는 외부 함수를 돕는 헬퍼 역할을 하므로 외부 함수 로직을 대신하는 경우가 대부분이다. 하지만, 외부 함수인 메서드와 중첩함수 또는 콜백함수의 <code>this</code> 가 일치하지 않는다는 것은 헬퍼로 동작하기 힘들게 만든다.<br/>
따라서 메서드 내부 중첩함수나 콜백 함수의 <code>this</code> 바인딩을 메서드 내 <code>this</code> 바인딩과 일치 시켜야 한다. <br/>
방법 1.
메서드 내의 <code>this</code> 를 새 변수에 할당하고 중첩함수(또는 콜백함수) 내부에서 <code>this</code> 대신 변수를 할당한다. <br/>
방법2.
명시적으로 바인딩한다. (<code>apply</code> , <code>call</code> , <code>bind</code> 메서드 사용) <br/>
방법3.
화살표 함수 사용. 화살표 함수 내부의 <code>this</code> 는 상위 스코프 <code>this</code> 를 가리킴.</p>
</blockquote>
<p>예시 코드</p>
<pre><code class="language-jsx">var value = 1;

const obj = {
    value: 100,
    foo() {
        console.log(this); // { value: 100, foo: *f* }
        // ⬇ 콜백 함수 내부의 this 에는 전역객체가 바인딩된다. 
        // 📌 콜백 함수의 this 바인딩과 객체 메서드(*foo()*) 내 this 바인딩을 일치 시켜야 한다. 
        setTimeout(function () {
            console.log(this); // window
            console.log(this.value); // 1
        }, 100);
    }
};

obj.foo(); </code></pre>
<h3 id="방법-1-this-를-새-변수에-할당">방법 1: <code>this</code> 를 새 변수에 할당</h3>
<pre><code class="language-jsx">var value = 1;

const obj = {
    value: 100,
    foo() {
        const that = this; // 메서드 내 this를 변수에 할당한다. 

        // 콜백 함수에서 this 대신 that를 참조한다. 
        setTimeout(function () {
            console.log(that.value); // *100*
        }, 100);
    }
};

obj.foo(); </code></pre>
<h3 id="방법-2-bind-이용">방법 2: <code>bind</code> 이용</h3>
<pre><code class="language-jsx">var value = 1;

const obj = {
    value: 100,
    foo() {
        // bind 이용
        setTimeout(function () {
            console.log(this.value); // *100*
        }.bind(this), 100);
    }
};

obj.foo(); </code></pre>
<h3 id="방법-3-화살표-함수-이용">방법 3: 화살표 함수 이용</h3>
<pre><code class="language-jsx">var value = 1;

const obj = {
    value: 100,
    foo() {
        // 화살표 함수 내부의 this는 상위 스코프의 this를 가리킨다. 
        setTimeout(() =&gt; console.log(this.value), 100); // 100
    }
};

obj.foo(); </code></pre>
<br /> 

<h2 id="2-메서드-호출">2. 메서드 호출</h2>
<p>메서드 내부의 <code>this</code> 에는 <strong>메서드를 호출한 객체</strong>, 즉 메서드를 호출할 때 <strong>메서드 이름 앞 마침표(.) 연산자 앞에 기술된 객체가 바인딩</strong>된다. </p>
<p><img src="https://velog.velcdn.com/images/lee-yun-pyo/post/bdcd3db4-7d79-49f6-b2de-0c75b62fb5db/image.JPG" alt=""></p>
<blockquote>
<p>💡 메서드는 객체에 포함된 것이 아니라 독립적으로 존재하는 별도의 객체다</p>
</blockquote>
<pre><code class="language-jsx">const person = {
    name: &#39;Lee&#39;,
    getName() {
        return this.name;
    }
};

const anotherPerson = {
    name: &#39;Kim&#39;
};

anotherPerson.getName = person.getName;

// getName 메서드를 호출한 객체는 anotherPerson 이다
console.log(anotherPerson.getName()); // Kim

const getName = person.getName;

// getName 메서드를 일반함수로 호출
console.log(getName()); // &#39;&#39;
// window.name </code></pre>
<br /> 

<h2 id="3-생성자-함수-호출">3. 생성자 함수 호출</h2>
<p>생성자 함수 내부 <code>this</code> 에는 <strong>생성자 함수가 생성할 인스턴스</strong>가 바인딩 된다. </p>
<blockquote>
<p>💡 생성자 함수 호출 시 주의할 점은 호출할 때 <code>new</code> 연산자와 함께 호출해야 생성자 함수로 동작한다. <code>new</code> 연산자를 사용하지 않고 호출하면 일반 함수로 동작한다.</p>
</blockquote>
<pre><code class="language-jsx">function Circle(radius) {
    this.radius = radius;
    this.getDiameter = function () {
        return 2 * this.radius;
    };
}

// 객체 생성
const circle1 = new Circle(5);
console.log(circle1.getDiameter()); // 10

// new 연산자와 함께 호출하지 않으면 생성자 함수로 동작하지 않고 일반 함수 호출이다.
const circle2 = Circle(15);

// 일반 함수로 호출된 Circle에는 반환문이 없으로 undefined 반환
console.log(circle2); // undefined

// 일반 함수로 호출된 Circle 내부 this는 전역객체를 가리킴.
console.log(radius) // 15 (*window.radius*)</code></pre>
<br /> 

<h2 id="4-apply--call--bind-메서드에-의한-간접-호출">4. <code>apply</code> / <code>call</code> / <code>bind</code> 메서드에 의한 간접 호출</h2>
<h3 id="41-apply--call"><strong>4.1. <code>apply</code> , <code>call</code></strong></h3>
<p><code>apply</code> , <code>call</code> 는 <strong>함수를 호출</strong>하고 호출하면서 <strong>첫번째 인수로 전달한 특정 객체</strong>를 호출한 함수의 <code>this</code> 에 바인딩한다.</p>
<blockquote>
<p>💡 <strong>두 메서드의 차이</strong>
    - <code>apply</code> 는 함수의 인수를 <strong>배열</strong>로 묶어 전달. 
    - <code>call</code> 은 함수의 인수를 쉼표로 구분한 <strong>리스트 형식</strong>으로 전달.</p>
</blockquote>
<p>두 메서드는 호출할 함수에 인수를 전달하는 방식만 다를 뿐 모두 동일한 기능(<strong>this로 사용할 객체를 전달하면서 함수 호출</strong>)을 수행한다. </p>
<p><strong>4.1.1. <code>apply</code> , <code>call</code> 의 활용</strong></p>
<p>유사 배열 객체에 배열 메서드를 사용하는 경우</p>
<pre><code class="language-jsx">function convertArgsToArray() {
    console.log(arguments);

    // arguments 객체를 배열로 변환
    const arr = Array.prototype.slice.call(arguments);
    console.log(arr);

    return arr;
}

convertArgsToArray(1, 2, 3); // [1, 2, 3]</code></pre>
<br />

<h3 id="42-bind"><strong>4.2. <code>bind</code></strong></h3>
<p><code>bind</code> 메서드는 함수를 호출하지 않고 <strong>첫 번째 인수로 전달한 값으로 this 바인딩이 교체된 함수를 새롭게 생성해 반환</strong>한다. </p>
<p><strong>4.2.1. <code>bind</code> 메서드의 활용</strong></p>
<p>메서드의 <code>this</code> 와 메서드 내부 중첩함수 또는 콜백함수의 <code>this</code> 불일치 문제 해결에 활용.</p>
<pre><code class="language-jsx">const person = {
    name: &#39;Lee&#39;,
    foo(callback) {
        // 위치1. 콜백 함수가 호출되기 이전에 this는 메서드를 호출한 객체(person)를 가리킨다.
        setTimeout(callback, 1000); // 🚨

        // bind로 callback 함수 내부의 this 바인딩을 전달
        setTimeout(callback.bind(this), 1000); // 💡
    }
};

person.foo(function () {
    console.log(this.name); // 위치2. 일반함수로 호출되어 this는 전역객체를 가리킨다.
});</code></pre>
<h1 id="정리">정리</h1>
<p><code>this</code> 는 함수 호출 방식에 따라 동적으로 결정된다. </p>
<table>
<thead>
<tr>
<th>함수 호출 방식</th>
<th>this 바인딩</th>
</tr>
</thead>
<tbody><tr>
<td>일반 함수 호출</td>
<td>전역 객체</td>
</tr>
<tr>
<td>메서드 호출</td>
<td>메서드를 호출한 객체</td>
</tr>
<tr>
<td>생성자 함수 호출</td>
<td>생성자 함수가 생성할 인스턴스</td>
</tr>
<tr>
<td>apply, call, bind 메서드에 의한 간접 호출</td>
<td>apply, call, bind 메서드 첫 번째 인수로 전달한 객체</td>
</tr>
</tbody></table>
<h2 id="참고">참고</h2>
<ul>
<li><a href="https://ebook-product.kyobobook.co.kr/dig/epd/ebook/E000002928532">모던 자바스크립트 Deep Dive</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS 공부 - 원시값과 객체]]></title>
            <link>https://velog.io/@lee-yun-pyo/JS-%EA%B3%B5%EB%B6%80-%EC%9B%90%EC%8B%9C%EA%B0%92%EA%B3%BC-%EA%B0%9D%EC%B2%B4</link>
            <guid>https://velog.io/@lee-yun-pyo/JS-%EA%B3%B5%EB%B6%80-%EC%9B%90%EC%8B%9C%EA%B0%92%EA%B3%BC-%EA%B0%9D%EC%B2%B4</guid>
            <pubDate>Tue, 07 May 2024 16:45:19 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/lee-yun-pyo/post/39275e01-4d77-4ad6-b330-e082e99bf552/image.jpeg" alt="JS 표지"></p>
<blockquote>
<p>💡 <strong>자바스크립트 데이터 타입</strong></p>
</blockquote>
<ol>
<li>숫자</li>
<li>문자열</li>
<li>불리언</li>
<li>null</li>
<li>undefined</li>
<li>심벌</li>
<li>객체</li>
</ol>
<h1 id="원시값과-객체">원시값과 객체</h1>
<h3 id="차이">차이</h3>
<ol>
<li>원시값은 변경 불가능한 값 (immutable) , 객체는 변경 가능한 값 (mutable)</li>
<li>변수에 값 할당 시 원시값은 실제 값이 저장, 객체는 참조값이 저장</li>
<li>원시 값은 값에 의한 전달 방식, 객체는 참조의 의한 전달 방식</li>
</ol>
<p>위 자바스크립트 데이터 타입 중 <code>7. 객체</code> 를 제외한 데이터 타입은 모두 원시값이다. </p>
<br />

<h2 id="1-변수">1. 변수</h2>
<p>이번 주제 대해 이해하기 위해 변수 개념에 대해 알 필요가 있다. </p>
<blockquote>
<p>변수는 하나의 값을 저장하기 위해 확보한 메모리 공간 자체 또는 그 메모리 공간을 식별하기 위해 붙인 이름이다.</p>
</blockquote>
<br />

<h2 id="2-원시-값-변경-불가능한-값">2. 원시 값 (변경 불가능한 값)</h2>
<h3 id="2-1-변수가-원시-값을-저장하는-방법">2-1. 변수가 원시 값을 저장하는 방법</h3>
<p><img src="https://velog.velcdn.com/images/lee-yun-pyo/post/d66e0833-b9f9-4456-bd24-16c0149986e6/image.png" alt=""></p>
<p>원시 값을 할당한 변수에 새로운 원시 값을 재할당 시, <strong>이전의 값을 변경하는 것이 아니라 새로운 메모리에 값을 저장하고 변수가 그 값을 가리키게 한다</strong>.</p>
<p>이 때 변수가 참조하는 메모리 주소가 변경되는 이유는 변수에 할당된 값이 변경 불가능하기 때문이다. 만약 값이 변경 가능하다면 변수가 가리키는 메모리 주소는 유지되며 원시 값 자체가 변경되면 그만이다.</p>
<p>이러한 특성을 <strong>불변성</strong>이라 한다. <strong>불변성을 갖는 원시 값을 할당한 변수는 재할당 이외에 변수 값을 변경할 수 있는 방법이 없다.</strong> (데이터 신뢰성을 보장)</p>
<h3 id="2-2-원시-값-변수에-변수를-할당했을-때-무엇이-어떻게-전달되는가">2-2. 원시 값: 변수에 변수를 할당했을 때 무엇이 어떻게 전달되는가?</h3>
<pre><code class="language-jsx">var score = 80;
var copy = score;

console.log(score); // 80
console.log(copy); // 80</code></pre>
<p><img src="https://velog.velcdn.com/images/lee-yun-pyo/post/b55e3662-db62-4fdb-a8c0-5a8ac573b14d/image.png" alt=""></p>
<p>변수에 원시값을 갖는 변수를 할당하면 할당받는 copy 변수 에는 할당되는 score 변수의 원시 값이 복사되어 전달된다. (값에 의한 전달).
이때, score 변수와 copy변수의 값 80은 <strong>다른 메모리 공간에 저장된 별개의 값</strong>이다. </p>
<pre><code class="language-jsx">score = 100;

console.log(score); // 100
console.log(copy); // 80</code></pre>
<p><img src="https://velog.velcdn.com/images/lee-yun-pyo/post/2ca175b6-e63b-48b3-ad19-99650ee69dc3/image.png" alt=""></p>
<p>두 변수의 원시 값은 각각 다른 메모리에 저장되어 있어 어느 한쪽에서 재할당을 통해 값을 변경해도  서로 간섭할 수 없다.</p>
<br />

<h2 id="3-객체-변경-가능한-값">3. 객체 (변경 가능한 값)</h2>
<h3 id="3-1-변수가-객체를-저장하는-방법">3-1. 변수가 객체를 저장하는 방법</h3>
<pre><code class="language-jsx">var person = {
    name: &quot;Lee&quot;
}</code></pre>
<p>객체를 할당한 변수에는 생성된 객체가 <strong>실제로 저장된 메모리 공간의 주소가 저장</strong>되어 있다. 이 값을 <strong>참조 값</strong>이라고 하고 변수는 이 값을 통해 객체에 접근할 수 있다. </p>
<p align="center"><img src="https://velog.velcdn.com/images/lee-yun-pyo/post/362d913f-86b0-4575-a20e-c48127b522fb/image.png" alt="Image" width="400" height="100"></p>

<p>원시 값은 원시값을 갖는 변수 값을 변경하려면 재할당 외에 방법은 없다. </p>
<p>하지만 객체는 변경 가능한 값이 때문에 재할당 없이 객체를 직접 변경할 수 있다. 이때, 객체를 할당한 변수에 재할당을 하지 않았으므로 객체를 할당한 변수의 참조값은 변경되지 않는다.</p>
<br />

<pre><code class="language-jsx">var person = {
    name: &#39;Lee&#39;
}

person.name = &#39;kim&#39;;

console.log(person); // { name: &quot;Kim&quot; }</code></pre>
<p align="center"><img src="https://velog.velcdn.com/images/lee-yun-pyo/post/2075b47c-06b6-430b-be69-0216667e2648/image.png" alt="Image" width="400" height="100"></p>

<h3 id="3-2-객체-변수에-변수를-할당했을-때-무엇이-어떻게-전달되는가">3-2. 객체: 변수에 변수를 할당했을 때 무엇이 어떻게 전달되는가?</h3>
<pre><code class="language-jsx">var person = {
    name: &#39;Lee&#39;
}

// 참조 값 복사
var copy = person;</code></pre>
<p>객체를 가리키는 변수를 다른 변수에 할당하면 원본의 <strong>참조 값이 복사되어 전달</strong>된다. (참조에 의한 전달)
<img src="https://velog.velcdn.com/images/lee-yun-pyo/post/e8424bfc-f7ba-4b2d-912a-92d8c0e5f69d/image.png" alt=""></p>
<blockquote>
<p>💡 부작용: 객체는 원시값과 다르게 <strong>여러개의 식별자가 하나의 객체를 공유할 수 있다</strong></p>
</blockquote>
<p>위 그림처럼 원본 <code>person</code> 을 사본 <code>copy</code> 에 할당하면 원본의 참조 값을 복사해서 사본에 저장한다. 이때, 원본과 사본은 동일한 참조값을 갖는다. 즉, 원본과 사본 모두 동일한 객체를 가리킨다.  이것은 하나의 객체를 공유하며 어느 하나에서 객체를 변경하면 다른 변수에도 영향을 미친다.</p>
<p>이처럼 객체는 변경이 가능하기 때문에 데이터 신뢰성 보장이 어렵다. 따라서 변경 가능한 데이터가 불변성을 유지하기 위한 방법으로 <strong>카피 온 라이트</strong>가 있다.</p>
<h2 id="결론">결론</h2>
<p>원시 값을 할당한 변수를 참조하면 메모리에 저장되어 있는 원시 값에 접근한다. 
하지만 객체를 할당한 변수를 참조하면 메모리에 저장되어 있는 참조 값을 통해 실제 객체 접근한다.</p>
<h3 id="더하여">더하여</h3>
<p>*<em>Q : <code>const</code> 는 상수를 의미하는 데 왜 값이 변경되나요?
*</em></p>
<pre><code class="language-jsx">const o = {};
var score;
o.a = 1;
console.log(o); // { a: 1 }</code></pre>
<p>위 코드를 보고 위와 같은 질문을 할 수도 있다. 이것은 <code>const</code> 키워드와 <code>변경 가능한 값</code>의 의미에 대해 오해할 때 생기는 질문이다. </p>
<p><strong>A</strong>: <code>const</code> 는 <strong>재할당이 금지</strong>된 변수(상수)를 나타내는 키워드일 뿐이다. 위 코드에서 상수 <code>o</code> 을 재할당하지는 않았다.
<code>{}</code> 의 데이터 타입은 객체이므로 변경 가능한 값이다.
변경 불가능한 값과 변경 가능한 값이라는 말은 값 자체를 변경할 있나 없나의 차이지 변수 값을 변경할 수 있거나 없다는 것이 아니다.</p>
<h2 id="참고">참고</h2>
<ul>
<li><a href="https://ebook-product.kyobobook.co.kr/dig/epd/ebook/E000002928532">모던 자바스크립트 Deep Dive</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Set 를 이용한 방문처리 (G4 샘터)]]></title>
            <link>https://velog.io/@lee-yun-pyo/Set-%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EB%B0%A9%EB%AC%B8%EC%B2%98%EB%A6%AC-G4-%EC%83%98%ED%84%B0</link>
            <guid>https://velog.io/@lee-yun-pyo/Set-%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EB%B0%A9%EB%AC%B8%EC%B2%98%EB%A6%AC-G4-%EC%83%98%ED%84%B0</guid>
            <pubDate>Tue, 07 May 2024 15:51:23 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/lee-yun-pyo/post/4217f420-6fc9-4195-a4e9-62042b5faff1/image.png" alt=""></p>
<h2 id="문제-18513-샘터">문제 18513 샘터</h2>
<p><a href="https://www.acmicpc.net/problem/18513">18513번: 샘터</a></p>
<blockquote>
<p>💡 <strong>입력</strong>
첫째 줄에 자연수 <em>N</em>과 <em>K</em>가 공백을 기준으로 구분되어 주어진다. (<code>1 ≤ N, K ≤ 100,000</code>) 둘째 줄에 <em>N</em>개의 샘터의 위치가 공백을 기준으로 구분되어 정수 형태로 주어진다. (<code>-100,000,000 ≤ 샘터의 위치 ≤ 100,000,000</code>) 단, 모든 <em>N</em>개의 샘터의 위치들은 서로 다르게 주어진다.</p>
</blockquote>
<h3 id="풀이">풀이</h3>
<p>샘물 위치를 큐에 넣고 1만큼 떨어진 주변을 탐색하여 값을 도출한다. 너비우선탐색을 사용하여 쉽게 풀이법을 찾을 수 있었다.</p>
<h3 id="문제">문제</h3>
<p>하지만 방문처리를 하기 위해 배열을 길이를 선언해야 했다. 그러나, 샘터의 위치가 -1억부터 +1억까지의 총 2억 범위에 있기 때문에 메모리 초과 문제가 발생했다.</p>
<pre><code class="language-jsx">const visited = Array.from({ length: 200_000_000 }, () =&gt; false); // 🚨 메모리 초과</code></pre>
<p>또한 집을 지을 수 있는지 위치 범위가 샘터의 위치 범위와 같다고 문제에서 볼 수 없기 때문에 방문 배열의 길이를 미리 선언하는 것은 옳지 않았다.</p>
<h3 id="해결">해결</h3>
<p>따라서 배열대신 <code>Set</code> 자료구조를 사용하여 문제를 해결하였다. </p>
<p><code>set.add()</code> 메서드를 사용하여 방문처리를 하고 <code>set.has()</code> 메서드를 통해 방문여부를 확인할 수 있다. </p>
<p><code>set.has()</code> 의 시간 복잡도는 평균적으로 <strong>O(1)</strong> 이다. 이는 <code>Set</code>이 해시 테이블을 기반으로 구현되어 있기 때문에  해시 테이블을 사용하면 값의 존재 여부를 빠르게 확인할 수 있다.</p>
<h3 id="코드">코드</h3>
<pre><code class="language-jsx">const dx = [-1, 1];

const [N, K] = inputArr.shift().split(&quot; &quot;).map(Number);
const arr = inputArr.shift().split(&quot; &quot;).map(Number);

const set = new Set(); // Set 선언

let house = 0;
let answer = 0;
const dequeue = new Dequeue();
arr.forEach((v) =&gt; dequeue.push([v, 0]));
arr.forEach((v) =&gt; set.add(v)); // 방문 처리

let isBreak = false;

while (dequeue.getLength()) {
  const [curX, count] = dequeue.shift();

  for (let i = 0; i &lt; 2; i++) {
    const nx = curX + dx[i];

    if (set.has(nx)) continue; // 방문 여부 확인

    dequeue.push([nx, count + 1]);
    set.add(nx); // 방문 처리
    house += 1;
    answer += count + 1;
    if (house === K) {
      isBreak = true;
      break;
    }
  }

  if (isBreak) break;
}

console.log(answer);</code></pre>
<h3 id="정리">정리</h3>
<p>그동안 배열을 선언해서만 방문처리를 하였는데 <code>Set</code> 으로도 방문처리 방법이 있다는 것을 알게 되었다. 좋은 해결법인 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS 공부 - 브라우저 렌더링]]></title>
            <link>https://velog.io/@lee-yun-pyo/JS-%EA%B3%B5%EB%B6%80-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EB%A0%8C%EB%8D%94%EB%A7%81</link>
            <guid>https://velog.io/@lee-yun-pyo/JS-%EA%B3%B5%EB%B6%80-%EB%B8%8C%EB%9D%BC%EC%9A%B0%EC%A0%80-%EB%A0%8C%EB%8D%94%EB%A7%81</guid>
            <pubDate>Thu, 11 Apr 2024 08:04:59 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/lee-yun-pyo/post/8f741c21-6ed0-4a31-9be3-9b8accc57e1f/image.jpeg" alt=""></p>
<h2 id="브라우저-렌더링이란">브라우저 렌더링이란?</h2>
<ul>
<li>렌더링: HTML, CSS, Javascript로 작성된 문서를 해석하여 브라우저에 시각적으로 출력하는 것</li>
</ul>
<h2 id="렌더링할-문서-가져오기">렌더링할 문서 가져오기</h2>
<p>브라우저 렌더링을 하기 위해서는 렌더링할 리소스가 필요하다. 렌더링할 리소스들은 서버에 있기 때문에 브라우저는 서버에 요청을 보내고 서버는 브라우저에게 요청에 대한 응답을 보낸다. </p>
<p>이에 브라우저는 주소창을 통해 서버에게 요청을 보낼 수 있다. </p>
<p>브라우저 url 주소창에 <code>https://www.naver.com/</code> 을 입력하면 naver.com 서버로 요청을 보낸다. 이 경우 루트 요청(<code>/</code>) 을 보낸다. 서버는 루트 요청에 대해 암묵적으로 <code>index.html</code> 을 설정한다. 즉,  <code>https://www.naver.com/</code> 는 <code>https://www.naver.com/index.html</code> 을 같은 요청이다. )</p>
<p><img src="https://velog.velcdn.com/images/lee-yun-pyo/post/54eeab43-e3e4-46fc-a73e-ea4642f6aa18/image.png" alt=""></p>
<p><code>https://www.naver.com/index.html</code> 을 요청하면 <code>index.html</code> 뿐만 아니라 js파일, css파일, 이미지 파일도 요청한 것을 볼 수 있다. 이것은 HTML 문서를 해석면서 <code>script</code> 태그, <code>link</code> 태그, <code>img</code> 태그를 만나면 HTML 코드 해석을 중단하고 각 리소스를 요청하기 떄문이다. </p>
<h2 id="문서-파싱하기">문서 파싱하기</h2>
<h3 id="html-코드-파싱-및-dom-생성">HTML 코드 파싱 및 DOM 생성</h3>
<ul>
<li>HTML  문서는 순수 텍스트로 이루어진 문서이기 때문에 브라우저가 해석할 수 있도록 변환한다.</li>
</ul>
<pre><code class="language-jsx"> &lt;!DOCTYPE html&gt;
 &lt;html lang=&quot;en&quot;&gt;
 &lt;head&gt;
   &lt;meta charset=&quot;UTF-8&quot;&gt;
   &lt;link rel=&quot;stylesheet&quot; href=&quot;sytle.css&quot;&gt; // 📌 HTML 파싱 중단, css 파일 로드
   &lt;title&gt;Document&lt;/title&gt;
 &lt;/head&gt;
 &lt;body&gt;
   &lt;ul&gt;
       &lt;li&gt;apple&lt;/li&gt;
       &lt;li&gt;banana&lt;/li&gt;
   &lt;/ul&gt;
   &lt;script src=&quot;main.js&quot; /&gt; // 📌 HTML 파싱 중단, JS 코드 파싱
 &lt;/body&gt;
 &lt;/html&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/lee-yun-pyo/post/a1d02c84-d739-4130-ba71-e66144f80a05/image.png" alt=""></p>
<p>HTML 파싱 과정</p>
<ol>
<li>브라우저는 HTML 문서를 2진수의 <strong>바이트 코드</strong>로 입력을 받는다. </li>
<li>바이트 코드는 <code>&lt;meta charset=&quot;UTF-8&quot;&gt;</code> 에 의해 <strong>문자열</strong> 형식으로 변환된다. </li>
<li>문자열 형식은 문법적 의미를 가지는 <strong>토큰</strong>으로 분해된다.</li>
<li>각 토큰은 객체로 변환되어 <strong>노드</strong>를 생성한다. </li>
<li>노드들로 구성된 트리 자료구조인 <strong>DOM</strong> 을 생성한다.</li>
</ol>
<h3 id="css-코드-파싱-및-cssom-생성">CSS 코드 파싱 및 CSSOM 생성</h3>
<ul>
<li>HTML 문서를 해석하다가 css 파일를 로드하는 <code>link</code> 태그나 <code>style</code> 태그를 만나면 HTML 파싱을 중단하고 CSS 코드를 파싱한다.</li>
<li>CSS 파싱 과정은 HTML과 동일(<strong>바이트 → 문자열 → 토큰 → 노드 → CSSOM</strong>) 하고  <strong>CSSOM</strong> 을 생성한다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/lee-yun-pyo/post/3dfa01bc-f28e-474e-b339-d0af7bfeedcf/image.png" alt=""></p>
<h3 id="렌더-트리-생성">렌더 트리 생성</h3>
<ul>
<li>생성된 DOM과 CSSOM을 결합하여 <strong>렌더 트리</strong>를 생성한다.</li>
<li>렌더 트리는 화면에 렌더링 되는 요소들의 노드들만 포함한다. (<code>meta</code> 태그, <code>script</code> 태그, css <code>display:none</code> 노드 포함 X)</li>
</ul>
<h3 id="레이아웃과-페인트">레이아웃과 페인트</h3>
<ul>
<li>레이아웃: 노드들의 크기나 위치를 계산</li>
<li>페인트: 화면에 노드를 렌더링</li>
</ul>
<h3 id="자바스크립트-코드-파싱">자바스크립트 코드 파싱</h3>
<ul>
<li>HTML 문서 파싱 중 <code>script</code> 태그를 만나면 HTML 파싱을 중단하고 제어권을 자바스크립트 엔진에 넘긴다.</li>
<li>자바스크립트 엔진은 자바스크립트 코드를 해석하여 <strong>추상적 구문 트리</strong>를 생성한다.</li>
</ul>
<h2 id="dom-또는-cssom-변경">DOM 또는 CSSOM 변경</h2>
<ul>
<li>자바스크립트 코드에 의해 DOM이나 CSSOM이 변경이 될 수 있다. 변경에 의해 DOM과 CSSOM은 다시 렌더 트리를 생성하고 변경된 렌더 트리를 가지고 다시 레이아웃과 페인트 작업을 해야한다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/lee-yun-pyo/post/9b2f2615-2a83-47f7-84c5-379731503fde/image.png" alt=""></p>
<h3 id="리플로우와-리페인트">리플로우와 리페인트</h3>
<ul>
<li>리플로우: 다시 레이아웃 하는 것</li>
<li>리페인트: 변경된 렌더트리를 가지고 페인트 하는 것</li>
</ul>
<h2 id="참고">참고</h2>
<ul>
<li><a href="https://ebook-product.kyobobook.co.kr/dig/epd/ebook/E000002928532">모던 자바스크립트 Deep Dive</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS 공부 - 비동기 프로그래밍]]></title>
            <link>https://velog.io/@lee-yun-pyo/JS-%EA%B3%B5%EB%B6%80-%EB%B9%84%EB%8F%99%EA%B8%B0-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</link>
            <guid>https://velog.io/@lee-yun-pyo/JS-%EA%B3%B5%EB%B6%80-%EB%B9%84%EB%8F%99%EA%B8%B0-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</guid>
            <pubDate>Thu, 04 Apr 2024 14:30:18 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/lee-yun-pyo/post/eddea0d8-0059-44a5-982e-8e8dd9485634/image.jpeg" alt=""></p>
<h1 id="비동기-프로그래밍">비동기 프로그래밍</h1>
<hr>
<h3 id="개요">개요</h3>
<ul>
<li>비동기란 작업이 완료될 때까지 기다리지 않고 다른 작업을 수행하는 과정을 말한다. 이는 작업 순서가 보장되지 않는다는 단점이 있지만 작업이 완료될 때까지 다른 작업이 기다리지 않고 동시에 실행될 수 있으므로 블로킹이 발생하지 않는다는 장점이 있다. 이를 고려하여 프로그래밍 하는 것을 비동기 프로그래밍이라고 한다.</li>
</ul>
<h3 id="프론트엔드에서-비동기-작업을-하는-이유">프론트엔드에서 비동기 작업을 하는 이유</h3>
<ul>
<li>비동기 프로그래밍은 일반적으로 웹에서 네트워크 요청을 할 때 수행된다. 브라우저가 서버에 네트워크 요청을 보내고 응답을 기다리는 동안 다른 작업을 수행할 때 사용된다. 그 외 다른 작업에서도 비동기 프로그래밍을 수행한다.</li>
</ul>
<br />

<h1 id="자바스크립트에서-함수-실행-방식">자바스크립트에서 함수 실행 방식</h1>
<hr>
<h2 id="자바스크립트는-싱글-스레드-방식">자바스크립트는 싱글 스레드 방식!</h2>
<p>싱글 스레드 방식은 한번에 단 하나의 작업만 처리할 수 있음을 의미한다. </p>
<p>자바스크립트는 단 하나의 콜 스택을 갖는다. 콜 스택은 함수의 실행 순서를 관리하는 데 함수가 순차적으로 실행되는 원리를 따른다.</p>
<p>함수를 호출하면 함수 실행 컨텍스트가 생성되고 실행 컨텍스트는 콜 스택에 푸쉬되고 함수가 실행된다. 콜 스택의 최상위 요소는 현재 실행 중인 함수이고 콜 스택에서 제거되는 것은 함수의 종료를 의미한다. 콜 스택의 최상위 요소를 제외한 요소들은 대기 중인 작업들이며 최상위 요소가 제거되면 대기 중인 작업이 실행된다.</p>
<p>이처럼 자바스크립트는 단 하나의 콜스택을 갖기 때문에 한 번에 하나의 작업을 할 수 있다. 따라서 작업이 수행되는 동안 다른 작업이 대기하기 하게 되어 블로킹이 발생하고 동기 처리 방식으로 수행된다. </p>
<h2 id="그렇다면-비동기는-어떻게-수행된다는-것인가">그렇다면 비동기는 어떻게 수행된다는 것인가?</h2>
<p>자바스크립트는 싱글 스레드 방식이다. 하지만 웹 브라우저를 보면 한번에 여러 작업이 수행되는 것을 볼 수 있다. (ex. 웹 요청으로 데이터를 가져오는 동안 렌더링 수행). </p>
<p>그 이유는 브라우저(or nodejs)가 이벤트 루프와 태스크 큐를 통해 비동기 처리를 수행하기 때문이다. 이벤트 루프는 자바스크립트 엔진의 콜 스택을 감시하며 콜 스택이 비어있고 태스크 큐에 작업이 있으면 태스크 큐의 작업을 콜 스택에 푸시한다. </p>
<p>예를 들어, <code>setTimeout(fn, delay)</code> 을 호출하면 콜 스택에 <code>setTimeout</code> 함수가 푸시된다. 이후 지정시간 (<code>delay</code>) 시간 이후 <code>setTimeout</code> 의 콜백 함수가 태스크 큐에 푸시된다. 그리고  <code>setTimeout</code> 함수 자체는 그 즉시 콜 스택에서 제거된다. 이 후, 나머지 코드가 실행된 후 콜스택이 비어있으면 이벤트 루프가 이를 감지하고 태스크 큐에 있는 콜백함수를 콜스택에 푸시하여 실행한다. </p>
<p>브라우저는 <code>setTimeout</code> 함수 뿐만 아니라 HTTP 요청과 DOM API와 같은 Web API 를 제공한다. </p>
<p><img src="https://velog.velcdn.com/images/lee-yun-pyo/post/a9c92571-a366-4bf0-9200-4e5902405413/image.png" alt=""></p>
<blockquote>
<p>💡 <strong>비동기 처리 과정에서 브라우저와 자바스크립트 엔진의 역할 구분</strong>
브라우저: 타이머 설정, 태스크 큐에 콜백함수 등록
자바스크립트 엔진: 나머지 코드 실행 컨텍스트 생성 및 콜 스택에 푸시 </p>
</blockquote>
<p><strong>이와 같이 브라우저와 자바스크립트 엔진의 협력으로 비동기 처리를 수행할 수 있다.</strong></p>
<br />

<h2 id="참고">참고</h2>
<ul>
<li><a href="https://ebook-product.kyobobook.co.kr/dig/epd/ebook/E000002928532">모던 자바스크립트 Deep Dive</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[npm vs npx]]></title>
            <link>https://velog.io/@lee-yun-pyo/npm-vs-npx</link>
            <guid>https://velog.io/@lee-yun-pyo/npm-vs-npx</guid>
            <pubDate>Sun, 31 Mar 2024 13:38:38 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/lee-yun-pyo/post/0d59e06c-1644-4b8a-99d6-5ccedc3621a4/image.png" alt=""></p>
<h1 id="npm">npm</h1>
<ul>
<li><code>Node Package Manager</code></li>
<li>Node.js 프로젝트의 종속성을 관리하여 패키지 추가, 업데이트 및 공유 과정을 간소화한다.</li>
</ul>
<h3 id="종속성-관리">종속성 관리</h3>
<ul>
<li><code>npm install package-name</code> 을 사용하여 <code>node_modules</code> 폴더 안에 프로젝트 특정 종속을 설치한다.</li>
</ul>
<h3 id="packagejson">package.json</h3>
<ul>
<li>프로젝트 메타데이터와 종속성을 추적하여 버전 일관성을 보장한다.</li>
</ul>
<h1 id="npx">npx</h1>
<ul>
<li><code>Node Package eXecute</code></li>
<li>Node.js 패키지를 실행하여 전역 설치 없이 바이너리 또는 명령의 실행을 간소화한다.</li>
</ul>
<h3 id="바이너리-실행">바이너리 실행</h3>
<ul>
<li>전역 설치 없이 도구를 실행하여 깨끗한 개발 환경을 보장한다.</li>
</ul>
<h3 id="명령어-실행">명령어 실행</h3>
<ul>
<li>패키지에서 명령어를 즉시 실행하여 가끔 사용하거나 일회성 스크립트에 이상적이다.</li>
</ul>
<h1 id="리액트에서-npm-vs-npx">리액트에서 npm vs npx</h1>
<h3 id="npm-1">npm</h3>
<ul>
<li><code>npm</code> 을 사용하여 <code>create-react-app</code> 을 사용하려면 먼저 전역적으로 설치한 다음 실행해야 한다. 이 경우 <code>npm</code> 사용이 중복된다.</li>
</ul>
<h3 id="npx-1">npx</h3>
<ul>
<li><code>npx</code> 의 가장 일반적인 응용 프로그램은 <code>create-react-app</code> 명령어이다. 프로젝트를 초기화하는 동안 한 번만 사용하면 되기 때문에 설치하지 않는다.</li>
</ul>
<h1 id="정리">정리</h1>
<blockquote>
<ul>
<li><code>npm</code> 은 컴퓨터에 JS 패키지르를 설치, 삭제 및 업데이트하는 데 사용되는 패키지 관리자이다.</li>
</ul>
</blockquote>
<ul>
<li><code>npx</code> 는 패키지 실행기이며, JS 패키지를 설치하지 않고 직접 실행하는 데 사용된다.</li>
</ul>
<blockquote>
<ul>
<li><code>npm</code> 은 전역적으로 패키지를 설치한다. 즉, 장기적으로 더 이상 필요하지 않은 패키지로 인해 컴퓨터가 오염될 수 있다.</li>
</ul>
</blockquote>
<ul>
<li><code>npx</code> 는 패키지를 설치하지 않으므로 기계의 패키지 오염 문제가 발생하지 않는다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS 공부 - 이벤트 전파와 위임]]></title>
            <link>https://velog.io/@lee-yun-pyo/JS-%EA%B3%B5%EB%B6%80-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%A0%84%ED%8C%8C%EC%99%80-%EC%9C%84%EC%9E%84</link>
            <guid>https://velog.io/@lee-yun-pyo/JS-%EA%B3%B5%EB%B6%80-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%A0%84%ED%8C%8C%EC%99%80-%EC%9C%84%EC%9E%84</guid>
            <pubDate>Mon, 25 Mar 2024 12:19:27 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/lee-yun-pyo/post/740775c2-a633-4c69-9ec1-0bda7b6b006e/image.jpeg" alt=""></p>
<h1 id="이벤트-전파">이벤트 전파</h1>
<h3 id="이벤트-전파-1">이벤트 전파</h3>
<ul>
<li>DOM 요소 노드에서 발생한 이벤트는 DOM트리를 통해 전파된다.</li>
<li>이벤트 전파는 이벤트 객체가 전파되는 방향에 따라 3단계로 구분한다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/lee-yun-pyo/post/1be89e53-f8b0-4c1b-9037-b8ee22029d26/image.png" alt=""></p>
<blockquote>
<p>💡 <strong>이벤트 전파 3단계</strong></p>
</blockquote>
<ul>
<li>캡처링 단계: 이벤트가 상위에서 하위 요소로 전파</li>
<li>타깃 단계: 이벤트가 이벤트 타깃에 도달</li>
<li>버블링 단계: 이벤트가 하위에서 상위 요소로 전파
이벤트는 <strong>캡처링 - 타깃 - 버블링</strong> 순으로 전판한다.</li>
</ul>
<pre><code class="language-html">&lt;body&gt;
    &lt;ul id=&quot;fruits&quot;&gt;
        &lt;li id=&quot;apple&quot;&gt;apple&lt;/li&gt;
        &lt;li id=&quot;banana&quot;&gt;banana&lt;/li&gt;
        &lt;li id=&quot;kiwi&quot;&gt;kiwi&lt;/li&gt;
    &lt;/ul&gt;
    &lt;script&gt;
        const $fruits = document.getElementById(&quot;fruits&quot;);
        const $banana = document.getElementById(&quot;banana&quot;);

        // ul태그 하위 요소인 li 요소를 클릭했을 때
        $fruits.addEventListener(&quot;click&quot;, e =&gt; {
            console.log(e.eventPhase); // 이벤트 단계 3 (버블링단계)
            console.log(e.target); // 이벤트 타깃 *[object HTMLLIElement]*
            console.log(e.currentTarget); // 커렌트 타깃 *[object HTMLULElement]*
        });

        $banana.addEventListener(&quot;click&quot;, e =&gt; {
            console.log(e.eventPhase); // 이벤트 단계 2 (타깃 단계)
            console.log(e.target); // 이벤트 타깃 *[object HTMLLIElement]*
            console.log(e.currentTarget); // 커렌트 타깃 *[object HTMLLIElement]*
        });

        $fruits.addEventListener(&quot;click&quot;, e =&gt; {
            console.log(e.eventPhase); // 이벤트 단계 1 (캡처링단계)
            console.log(e.target); // 이벤트 타깃 *[object HTMLLIElement]*
            console.log(e.currentTarget); // 커렌트 타깃 *[object HTMLULElement]*
        }, true);
    &lt;/script&gt;
&lt;/body&gt;</code></pre>
<p>이와 같이 이벤트는 이벤트를 발생시킨 이벤트 타깃은 물론 상위 DOM요소에서도 캐치할 수 있다. </p>
<blockquote>
<p>💡 <strong>event.target 와 event.currentTarget</strong></p>
</blockquote>
<ul>
<li><code>event.target</code>
실제로 이벤트를 발생시킨 DOM 요소</li>
<li><code>event.currentTarget</code>
이벤트 핸들러가 바인딩된 DOM 요소<pre><code class="language-jsx">EventTarget.addEventListener(&#39;eventType&#39;, functionName [, useCapture])</code></pre>
<code>addEventListener</code> 방식으로 이벤트를 등록할 때 <em><code>EventTarget</code></em> 이 곧 <code>event.currentTarget</code> 이 된다.</li>
<li><code>event.target</code> 와 <code>event.currentTarget</code> 은 버블링/캡처링 과정에 따라 서로 다를 수 있다.</li>
</ul>
<h3 id="이벤트-캡처링-캐치-방법">이벤트 캡처링 캐치 방법</h3>
<ul>
<li>이벤트 핸들러 등록 방식 중 어트리뷰트/프로퍼티 방식은 타깃 단계와 버블링 단계만 캐치할 수 있다.</li>
<li><code>addEventListener</code> 메서드 방식은 <strong>3번째 인자로 <code>true</code> 를 전달</strong>하면 캡처링 단계도 캐치할 수 있다. 3번째 인자로 <code>false</code> 를 전달하거나 생략하면 타깃 단계와 버블링 단계만 캐치할 수 있다.</li>
</ul>
<br />

<h1 id="이벤트-위임">이벤트 위임</h1>
<pre><code class="language-jsx">function handleClick({ target }) {
    [...$fruits.children].forEach($fruit =&gt; {
        // 로직 처리
    });
}

document.getElementById(&quot;apple&quot;).onclick = handleClick;
document.getElementById(&quot;banana&quot;).onclick = handleClick;
document.getElementById(&quot;kiwi&quot;).onclick = handleClick;</code></pre>
<h3 id="코드-문제-위임-x">코드 문제 (위임 X)</h3>
<ol>
<li>list가 많아진다면 그만큼 브라우저가 기억해야할 이벤트 리스너도 많아진다. (비효율)</li>
<li>list가 동적으로 추가해야 할 때 추가로 이벤트를 등록해야한다.</li>
</ol>
<br /> 

<pre><code class="language-jsx">function handleClick({ target }) {
        *// 이벤트를 발생시킨 요소가 #fruits &gt; li 자식 요소가 아니면 무시*
        if (!target.matches(&#39;#fruits &gt; li&#39;)) return;
    [...$fruits.children].forEach($fruit =&gt; {
        // 로직 처리
    });
}
$fruits.onclick = handleClick;</code></pre>
<h3 id="이벤트-위임-효과">이벤트 위임 효과</h3>
<ul>
<li>브라우저가 너무 많은 이벤트 핸들러를 기억하지 않게 하고, DOM 이 추가/삭제 될때 Event등록을 매번 해줘야 하는 수고를 없앨 수 있다.</li>
</ul>
<h3 id="이벤트-전파-막기">이벤트 전파 막기</h3>
<ul>
<li><code>stopPropagation</code> 메서드 이용</li>
<li>하위 DOM 요소의 이벤트를 개별적으로 처리하기 위해 이벤트 전파를 막는다.</li>
</ul>
<h2 id="참고">참고</h2>
<ul>
<li><a href="https://ebook-product.kyobobook.co.kr/dig/epd/ebook/E000002928532">모던 자바스크립트 Deep Dive</a></li>
<li><a href="https://developer.mescius.com/blogs/html-and-wijmo-events/">이벤트 전파 사진 출처</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[JS 공부 - 이벤트]]></title>
            <link>https://velog.io/@lee-yun-pyo/%EC%9D%B4%EB%B2%A4%ED%8A%B8</link>
            <guid>https://velog.io/@lee-yun-pyo/%EC%9D%B4%EB%B2%A4%ED%8A%B8</guid>
            <pubDate>Sun, 24 Mar 2024 13:49:22 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/lee-yun-pyo/post/0ff48ba2-05f5-4b32-94a1-56987110eb4f/image.jpeg" alt=""></p>
<h2 id="개요">개요</h2>
<h3 id="이벤트-핸들러">이벤트 핸들러</h3>
<ul>
<li>이벤트가 발생했을 때 브라우저에 의해 호출될 함수</li>
</ul>
<h3 id="이벤트-핸들러-등록">이벤트 핸들러 등록</h3>
<ul>
<li>이벤트 발생 시 브라우저에게 이벤트 핸들러의 호출을 위임하는 것</li>
<li>3가지 방법이 있다.</li>
</ul>
<blockquote>
<p>💡 <strong>브라우저에게 함수 호출을 위임하는 이유</strong>
사용자가 언제 이벤트를 발생 (ex. 버튼 클릭)시킬지 모르기 때문에 개발자가 명시적으로 함수를 호출하지 않고 브라우저에게 함수 호출을 위임한다. 
브라우저는 처리해야 할 특정 사건이 발생하면 이를 감지(ex. 버튼 클릭 감지)하여 이벤트를 발생 시킬 수 있다.</p>
</blockquote>
<pre><code class="language-jsx">const $button = document.querySelector(&#39;button&#39;);

$button.onclick = () =&gt; { console.log(&#39;button click&#39;); };</code></pre>
<p>이벤트 핸들러 프로퍼티에 함수를 할당하면 해당 이벤트가 발생했을 때 (버튼 클릭했을 때) 할당한 함수가 <strong>브라우저에 의해 호출</strong>된다. </p>
<br />

<h2 id="이벤트-핸들러-등록-1">이벤트 핸들러 등록</h2>
<h3 id="1-어트리뷰트-방식">1. 어트리뷰트 방식</h3>
<ul>
<li>등록방법<ul>
<li>HTML 요소 어트리뷰트 중에는 이벤트 핸들러 어트리뷰트가 있는 데 값으로 <strong>함수 호출문</strong>을 할당한다.</li>
</ul>
</li>
<li>이벤트 핸들러 어트리뷰트 이름: <code>on</code> + <code>이벤트 타입</code> (ex. <code>onclick</code> )</li>
</ul>
<pre><code class="language-html">&lt;button onclick=&quot;sayHi(&#39;lee&#39;)&quot;&gt;버튼&lt;/button&gt;
&lt;script&gt;
    function sayHi(name) {
        console.log(name);
    }
&lt;/script&gt;</code></pre>
<blockquote>
<p>🚨 <strong>함수 참조가 아닌 함수 호출문을 할당?</strong>
 이벤트 핸들러를 등록할 때 콜백 함수처럼 함수 참조를 등록해야 브라우저가 이벤트 핸들러를 호출할 수 있다. 만약 함수 호출문을 등록하면 함수 평가 결과가 이벤트 핸들러로 등록된다. 이 때, 함수를 반환하는 고차함수 호출문을 등록하면 문제가 없지만 함수가 아닌 값을 반환하는 함수 호출문을 등록하면 브라우저가 이벤트 핸들러를 호출할 수 없다.</p>
</blockquote>
<p>이벤트 핸들러 어트리뷰트 값은 사실 <strong>암묵적으로 생성될 이벤트 핸들러의 함수 몸체</strong>를 의미한다.</p>
<p>즉, <code>onclick=&quot;sayHi(&#39;lee&#39;)&quot;</code> 어트리뷰트는 파싱되어 다음과 같은 함수를 암묵적으로 생성한다.</p>
<pre><code class="language-jsx">function onClick(event) {
    sayHi(&#39;lee&#39;);
}</code></pre>
<p>이후 생성된 함수를 <code>onclick</code> 이벤트 핸들러 프로퍼티에 할당한다. (결국 이벤트 핸들러 프로퍼티 방식과 동일)</p>
<blockquote>
<p>💡 <strong>왜 이렇게 동작하는 지?</strong>
 이벤트 핸들러에 인수를 전달하기 위해! 어트리뷰트 값으로 함수 참조를 할당하려면 인수를 전달하기 곤란하다.</p>
</blockquote>
<p>React 나 Vue.js 같은 프레임워크에서는 이벤트 핸들러 어트리뷰트 방식으로 이벤트를 처리한다. </p>
<h3 id="2-프로퍼티-방식">2. 프로퍼티 방식</h3>
<ul>
<li>등록방법:<ul>
<li><code>window</code> 객체, DOM 노드 객체는 이벤트에 대응하는 이벤트 핸들러 프로퍼티를 가지고 있다. 이 이벤트 핸들러 프로퍼티에 함수를 바인딩한다.</li>
</ul>
</li>
<li>이벤트 핸들러 프로퍼티 키 이름: <code>on</code> + <code>이벤트 타입</code> (ex. <code>onclick</code> )</li>
</ul>
<pre><code class="language-html">&lt;button&gt;버튼&lt;/button&gt;
&lt;script&gt;
    const $button = document.querySelector(&#39;button&#39;);

    // 이벤트 핸들러 프로퍼티에 이벤트 핸들러 바인딩
    $button.onclick = function () {
        console.log(&#39;클릭&#39;)
    }
&lt;/script&gt;</code></pre>
<p>이벤트 핸들러를 등록하려면 아래 3가지를 지정해야 한다. </p>
<blockquote>
</blockquote>
<ul>
<li>이벤트 타깃 (event target): 이벤트를 발생시킬 객체 (ex. <code>$button</code> )</li>
<li>이벤트 타입 (event type): 이벤트 종류 (ex. <code>click</code> )</li>
<li>이벤트 핸들러: (ex. <code>function () { console.log() }</code> )</li>
</ul>
<p>이벤트 핸들러 프로퍼티에는 하나의 이벤트 핸들러만 바인딩할 수 있다. </p>
<h3 id="3-addeventlistener-메서드-방식">3. addEventListener 메서드 방식</h3>
<ul>
<li>등록 방법</li>
</ul>
<pre><code class="language-jsx">EventTarget.addEventListener(&#39;eventType&#39;, functionName [, useCapture])</code></pre>
<pre><code class="language-html">&lt;button&gt;버튼&lt;/button&gt;
&lt;script&gt;
    const $button = document.querySelector(&#39;button&#39;);

    $button.addEventListener(&#39;click&#39;, function () {
        console.log(&#39;클릭&#39;);
    });
&lt;/script&gt;</code></pre>
<p>addEventListener 메서드는 이벤트 핸들러 프로퍼티 방식과 달리 하나 이상의 이벤트 핸들러를 등록할 수 있다. (등록된 순서대로 호출)</p>
<p>단, 동일한 이벤트를 중복 등록하면 하나만 등록된다. </p>
<pre><code class="language-jsx">const $button = document.querySelector(&#39;button&#39;);

const handleClick = () =&gt; console.log(&#39;클릭&#39;);

// 중복 등록 시 핸들러 하나만 등록
$button.addEventListener(&#39;click&#39;, handleClick);
$button.addEventListener(&#39;click&#39;, handleClick);</code></pre>
<br />

<h2 id="이벤트-핸들러-제거">이벤트 핸들러 제거</h2>
<h3 id="이벤트-핸들러-프로퍼티-방식에서">이벤트 핸들러 프로퍼티 방식에서</h3>
<ul>
<li>제거 방법<ul>
<li>이벤트 핸들러 프로퍼티에 <code>null</code> 를 할당한다. (<code>removeEventListenr</code> 메서드 ❌)</li>
</ul>
</li>
</ul>
<pre><code class="language-jsx">// 이벤트 핸들러 등록
$button.onclick = handleClick;

// 이벤트 핸들러 제거
$button.onclick = null;</code></pre>
<h3 id="addeventlistener-메서드-방식에서">addEventListener 메서드 방식에서</h3>
<ul>
<li>제거 방법<ul>
<li><code>removeEventListenr</code> 메서드 사용 (인수는 등록과 동일)</li>
<li><code>addEventListener</code> 메서드에 전달한 인수와 <code>removeEventListener</code> 메서드에 전달한 인수가 일치해야 한다.</li>
</ul>
</li>
</ul>
<pre><code class="language-jsx">// 이벤트 핸들러 등록
$button.addEventListener(&#39;click&#39;, handleClick);

// 이벤트 핸들러 제거
$button.removeEventListener(&#39;click&#39;, handleClick);</code></pre>
<blockquote>
<p>💡 <code>removeEventListener</code> 의 인수로 전달한 이벤트 핸들러와 <code>addEventListener</code> 인수로 전달한 이벤트 핸들러는 동일해야 하기 때문에 만약 익명함수로 이벤트 핸들러를 등록했다면 이벤트를 제거할 수 없다.</p>
</blockquote>
<p>단, 기명 이벤트 핸들러 내부에서 <code>removeEventListener</code> 메서드를 호출하여 이벤트 핸들러를 제거할 수 있다. 따라서 이 경우 이벤트 핸들러는 단 한번만 호출된다.</p>
<pre><code class="language-jsx">// 버튼을 여러번 클릭해도 한 번만 이벤트 핸들러 호출
$button.addEventListener(&#39;click&#39;, function foo() {
    console.log(&#39;클릭&#39;);

    // 이벤트 핸들러를 제거한다.    
    $button.removeEventListener(&#39;click&#39;, foo);
}</code></pre>
<br />

<h2 id="참고">참고</h2>
<ul>
<li><a href="https://ebook-product.kyobobook.co.kr/dig/epd/ebook/E000002928532">모던 자바스크립트 Deep Dive</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[그래프 탐색 문제 (SV2 연결 요소의 개수)]]></title>
            <link>https://velog.io/@lee-yun-pyo/%EA%B7%B8%EB%9E%98%ED%94%84-%ED%83%90%EC%83%89-%EB%AC%B8%EC%A0%9C-SV2-%EC%97%B0%EA%B2%B0-%EC%9A%94%EC%86%8C%EC%9D%98-%EA%B0%9C%EC%88%98</link>
            <guid>https://velog.io/@lee-yun-pyo/%EA%B7%B8%EB%9E%98%ED%94%84-%ED%83%90%EC%83%89-%EB%AC%B8%EC%A0%9C-SV2-%EC%97%B0%EA%B2%B0-%EC%9A%94%EC%86%8C%EC%9D%98-%EA%B0%9C%EC%88%98</guid>
            <pubDate>Sun, 28 May 2023 17:25:54 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<p><strong>11724 연결 요소의 개수</strong>
<a href="https://www.acmicpc.net/problem/11724">https://www.acmicpc.net/problem/11724</a>
알고리즘 분류: <strong>그래프 탐색, DFS, BFS</strong></p>
<h2 id="풀이">풀이</h2>
<p>이 문제 삽질을 많이 했다. 왜냐하면 문제의 내용을 정확히 파악하지 못했다.</p>
<p><strong>입력값</strong></p>
<pre><code>6 2
3 4
4 2</code></pre><p><img src="https://velog.velcdn.com/images/lee-yun-pyo/post/b4dcc39e-b214-4c77-88fe-239e8ade91d0/image.png" alt="">
입력값이 위와 같을 때 그래프는 위 그림처럼 표현할 수 있다. </p>
<p>이 때, 주의할 것이 두가지가 있다. </p>
<ul>
<li>간선이 무방향인 그래프이다.</li>
<li>연결 요소의 개수를 구할 때 <code>2-4-3</code> 처럼 간선으로 이루어진 그래프 뿐만 아니라 1, 5, 6 간선도 연결 요소이다.
즉, 위 그래프의 연결 요소의 개수는 1이 아닌 4이다. </li>
</ul>
<p><strong>구현 코드</strong></p>
<pre><code class="language-js">const filePath = process.platform === &quot;linux&quot; ? &quot;/dev/stdin&quot; : &quot;./input.txt&quot;;

const [[N, M], ...arr] = require(&quot;fs&quot;)
  .readFileSync(filePath)
  .toString()
  .trim()
  .split(&quot;\n&quot;)
  .map((i) =&gt; i.split(&quot; &quot;).map(Number));

// 각 노드가 연결된 정보를 배열로 표현 (2차원 배열)
const graph = Array.from({ length: N + 1 }, () =&gt; []);
for (let i = 0; i &lt; M; i++) {
  // 무방향 그래프 특징을 감안하여 배열을 표현한다.
  graph[arr[i][1]].push(arr[i][0]);
  graph[arr[i][0]].push(arr[i][1]);
}

// 각 노드가 방문된 정보를 배열로 표현 (방문한것은 1, 그렇지 않은 것은 0)
const visited = Array.from({ length: N + 1 }, () =&gt; 0);

// DFS 함수
function dfs(graph, start, visited) {
  // 현재 노드 방문처리
  visited[start] = 1;
  for (const i of graph[start]) {
    // 현재 노드와 연결된 다른 노드 재귀적으로 방문
    if (!visited[i]) dfs(graph, i, visited);
  }
  // 간선이 서로 연결된 노드를 모두 검사했으면 1을 반환
  return 1;
}

function solution(M, arr) {
  let result = 0;

  // 간선이 없는 경우 노드의 개수가 연결 요소 개수
  if (M === 0) return N;

  for (let i = 0; i &lt; M; i++) {
    // 노드가 방문된 상태가 아니면 DFS 함수 호출
    if (visited[arr[i][0]] === 0) {
      result += dfs(graph, arr[i][0], visited);
    }
  }
  // 연결된 노드 없이 홀로 있는 노드의 수
  const alone = visited.filter((i) =&gt; i === 0).length - 1;
  return result + alone;
}

const answer = solution(M, arr);
console.log(answer);</code></pre>
<h2 id="today-i-learned">Today I Learned</h2>
<p><strong>JS 문법</strong></p>
<p>빈 배열 5개 요소를 가지는 배열을 생성할 때,
<code>Array.from()</code> 과 <code>new Array().fill()</code> 차이</p>
<pre><code class="language-js">const array = Array.from({ length: 5 }, () =&gt; []);</code></pre>
<pre><code class="language-js">const array = new Array(5).fill([]);</code></pre>
<p>위 두 문장 모두 array을 출력하면 <code>[ [], [], [], [], [] ]</code> 같은 출력값이 나온다. </p>
<p>그러나, </p>
<pre><code class="language-js">array[0].push(1);</code></pre>
<p>위 코드를 실행한다면
<code>Array.from()</code> 로 선언한 배열의 출력값은 다음과 같고,</p>
<pre><code>[ [ 1 ], [ 0 ], [ 0 ], [ 0 ], [ 0 ] ]</code></pre><p><code>new Array().fill()</code> 로 선언한 배열의 출력값은 다음과 같다. </p>
<pre><code>[ [ 1 ], [ 1 ], [ 1 ], [ 1 ], [ 1 ] ]</code></pre><p><strong>왜 이런 원치 않는 동작이 일어나는 것일까?</strong></p>
<ul>
<li><code>new Array()</code>로 생성된 배열은 실제로 빈 배열을 가리키는 참조를 가지고 있다. <code>fill([])</code> 메소드는 주어진 배열(<code>[]</code>)을 참조하여 해당 배열로 모든 요소를 채우는 것이 아니라, 배열이 가리키고 있는 참조를 공유하는 것이다.</li>
<li>따라서 <code>fill([])</code>로 요소를 채우면, 모든 요소가 동일한 빈 배열을 참조하게 된다.</li>
</ul>
<p>이로 인해 하나의 요소를 수정하면 다른 요소에도 동일한 변경이 반영된다.</p>
<blockquote>
<p>💡 따라서 배열을 생성할 때는 각 요소가 독립적인 빈 배열을 가지도록 하기 위해 다른 방법을 사용해야 한다. 
 <code>Array.from()</code>을 사용하면 각 요소가 서로 다른 빈 배열을 가지게 된다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[슬라이딩 윈도우 (SV2 소가 길을 건너간 이유 5)]]></title>
            <link>https://velog.io/@lee-yun-pyo/%EC%8A%AC%EB%9D%BC%EC%9D%B4%EB%94%A9-%EC%9C%88%EB%8F%84%EC%9A%B0-SV2-%EC%86%8C%EA%B0%80-%EA%B8%B8%EC%9D%84-%EA%B1%B4%EB%84%88%EA%B0%84-%EC%9D%B4%EC%9C%A0-5</link>
            <guid>https://velog.io/@lee-yun-pyo/%EC%8A%AC%EB%9D%BC%EC%9D%B4%EB%94%A9-%EC%9C%88%EB%8F%84%EC%9A%B0-SV2-%EC%86%8C%EA%B0%80-%EA%B8%B8%EC%9D%84-%EA%B1%B4%EB%84%88%EA%B0%84-%EC%9D%B4%EC%9C%A0-5</guid>
            <pubDate>Sat, 27 May 2023 17:44:57 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<p>14465 소가 길을 건너간 이유 5 (SV2)
<a href="https://www.acmicpc.net/problem/14465">https://www.acmicpc.net/problem/14465</a></p>
<p>알고리즘 분류: <strong>슬라이딩 윈도우, 누적합</strong> </p>
<h2 id="풀이">풀이</h2>
<p><strong>입력값</strong></p>
<pre><code>10 6 5
2
10
1
5
9</code></pre><p>입력값 N의 길이의 배열 하나를 생성하여 배열을 고장난 신호등은 0을 정상인 신호등은 1로 한다. </p>
<pre><code>[ 0, 0, 1, 1, 0, 1, 1, 1, 0, 0 ]</code></pre><p>여기서 <strong>슬라이딩 윈도우</strong> 알고리즘을 사용한다. 
검사할 윈도우의 크기는 입력값 <code>K</code> 이며 각 윈도우의 내부 요소의 합과 K의 차를 구해 최솟값을 구한다. </p>
<p><strong>코드</strong></p>
<pre><code class="language-js">const filePath = process.platform === &quot;linux&quot; ? &quot;/dev/stdin&quot; : &quot;./input.txt&quot;;

const [input, ...inputArr] = require(&quot;fs&quot;)
  .readFileSync(filePath)
  .toString()
  .trim()
  .split(&quot;\n&quot;);

const [N, K, B] = input.split(&quot; &quot;).map(Number);
const repaired = inputArr.map(Number);

const array = new Array(N).fill(1);
for (const element of repaired) {
  array[element - 1] = 0;
}

let window = array.slice(0, K).reduce((ac, cv) =&gt; ac + cv, 0);
let answer = K - window;
for (let i = K; i &lt; N; i++) {
  window += array[i] - array[i - K];
  if (K - window &lt; answer) answer = K - window;
}

console.log(answer);</code></pre>
<h2 id="today-i-learned">Today I Learned</h2>
<p>슬라이딩 윈도우 기법이란?
<img src="https://velog.velcdn.com/images/lee-yun-pyo/post/21eda951-15f4-4ba4-b4c5-8dd9051964ed/image.png" alt=""></p>
<ul>
<li>고정 사이즈의 윈도우가 이동하면서 윈도우 내에 있는 데이터를 이용해 문제를 풀이하는 알고리즘을 말한다.</li>
</ul>
<p>위 예시의 윈도우 내의 요소들의 합을 차례로 구한다면 다음과 같다. </p>
<ul>
<li>첫번째 윈도우 요소의 합: <code>1 + 15 + 1 + 2</code></li>
<li>두번째 윈도우 요소의 합: <code>1 - (1) + 15 + 1 + 2 + (+6)</code></li>
</ul>
<p>이 때, 다음 윈도우의 요소의 합을 구할 때 다음 윈도우의 시작과 끝 인덱스의 요소를 하나씩 합하는 게 아니라 
이전의 윈도우에서 첫번째 윈도우 요소를 빼고 끝 인덱스의 다음 인덱스의 요소를 더하면 된다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[누적합 문제 (GD5 개똥벌레)]]></title>
            <link>https://velog.io/@lee-yun-pyo/%EB%88%84%EC%A0%81%ED%95%A9-%EB%AC%B8%EC%A0%9C-GD5-%EA%B0%9C%EB%98%A5%EB%B2%8C%EB%A0%88</link>
            <guid>https://velog.io/@lee-yun-pyo/%EB%88%84%EC%A0%81%ED%95%A9-%EB%AC%B8%EC%A0%9C-GD5-%EA%B0%9C%EB%98%A5%EB%B2%8C%EB%A0%88</guid>
            <pubDate>Fri, 26 May 2023 17:41:47 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<p><a href="https://www.acmicpc.net/problem/3020">https://www.acmicpc.net/problem/3020</a></p>
<p>알고리즘 분류: <strong>누적합</strong></p>
<h2 id="풀이">풀이</h2>
<pre><code>입력값
6 7
1
5
3
3
5
1</code></pre><p><img src="https://velog.velcdn.com/images/lee-yun-pyo/post/3863136c-f3aa-4869-b460-e327d1e2b4d2/image.png" alt=""></p>
<p>입력값이 위와 같이 주어졌을 때, 각 높이마다 장애물의 수를 구하면 된다. 즉, 위 입력값에서 <code>[3, 2, 3, 2, 3, 2, 3]</code> 을 도출하고 <code>Math.min()</code> 을 통해 최소값과 최소값의 개수를 구하면 된다. </p>
<h2 id="today-i-learned">Today I Learned</h2>
<h4 id="처음-코드">처음 코드</h4>
<p>처음 구현할 때 <code>[3, 2, 3, 2, 3, 2, 3]</code> 을 도출하는 과정에서 시간 초과가 발생했다. </p>
<p>입력값으로 받은 석순과 종유석의 높이값만으로 결과를 도출했기 때문에 누적합을 구할 때 2중 <code>for</code> 문을 사용하였다. </p>
<pre><code class="language-js">/* 처음 작성한 코드 */

const sucksun = array.filter(
  (v, index) =&gt; index % 2 === 0
); // sucksun: [ 1, 3, 5]
const jongyusuck = array.filter(
  (v, index) =&gt; index % 2 !== 0
); // jongyusuck: [5, 3, 1]

const arr = new Array(H).fill(0);
for (let i = 0; i &lt; sucksun.length; i++) {
  for (let j = 0; j &lt; sucksun[i]; j++) arr[j]++;
} 
// arr: [ 3, 2, 2, 1, 1, 0, 0 ]


for (let i = 0; i &lt; jongyusuck.length; i++) {
  for (let j = 0; j &lt; jongyusuck[i]; j++) arr[arr.length - 1 - j]++;
} 
// arr: [ 3, 2, 3, 2, 3, 2, 3 ]</code></pre>
<h4 id="수정-코드">수정 코드</h4>
<ol>
<li>석순과 종유석의 높이값에 대응되는 <strong><em>인덱스</em></strong>를 저장한다. </li>
<li>석순은 배열의 뒤에서부터 누적합을 계산하고 종유석은 배열의 앞에서부터 누적합을 계산한다. <pre><code class="language-js">/* 수정한 코드 */
let sucksun = new Array(H + 1).fill(0);
let jongyusuck = new Array(H + 1).fill(0);
array.filter((v, index) =&gt;
index % 2 === 0 ? jongyusuck[H - v + 1]++ : sucksun[v]++
);
</code></pre>
</li>
</ol>
<p>// sucksun: [ 0, 1, 0, 1, 0, 1, 0, 0 ]
// jongyusuck: [ 0, 0, 0, 1, 0, 1, 0, 1 ]</p>
<p>for (let i = 1; i &lt;= H; i++) {
  sucksun[H - i] += sucksun[H - i + 1];
  jongyusuck[i] += jongyusuck[i - 1];
}</p>
<p>// sucksun: [ 3, 3, 2, 2, 1, 1, 0, 0 ]
// jongyusuck: [ 0, 0, 0, 1, 1, 2, 2, 3 ]</p>
<p>let newArr = [];
for (let i = 1; i &lt;= H; i++) {
  newArr[i - 1] = sucksun[i] + jongyusuck[i];
}</p>
<p>// newArr: [ 3, 2, 3, 2, 3, 2, 3 ]</p>
<pre><code>
</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[이진탐색 문제 (SV2 과자 나눠주기)]]></title>
            <link>https://velog.io/@lee-yun-pyo/%EA%B3%BC%EC%9E%90-%EB%82%98%EB%88%A0%EC%A3%BC%EA%B8%B0-JS-%ED%92%80%EC%9D%B4</link>
            <guid>https://velog.io/@lee-yun-pyo/%EA%B3%BC%EC%9E%90-%EB%82%98%EB%88%A0%EC%A3%BC%EA%B8%B0-JS-%ED%92%80%EC%9D%B4</guid>
            <pubDate>Fri, 26 May 2023 06:48:47 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<p><strong>16401 과자 나눠주기 (SV2)</strong></p>
<p><a href="https://www.acmicpc.net/problem/16401">https://www.acmicpc.net/problem/16401</a></p>
<p>알고리즘 분류: <strong>이진 탐색</strong></p>
<h2 id="풀이">풀이</h2>
<p>입력으로 주어진 과자의 길이 중 최대 값을 기준으로 한다. </p>
<pre><code class="language-js">const start = 1;
const end = Math.max(...array);</code></pre>
<p>이후 이진탐색으로 계산하여 매 중간값으로 주어진 과자의 길이들을 나눈 몫을 다 더해 더한 값이 조카의 수보다 작거나 혹은 크거나 같을 때를 비교하여 <code>start</code>와 <code>end</code> 값을 조정한다.  </p>
<p>처음 코드를 작성할 때는 주어진 과자를 <code>mid</code>로 모두 계산을 하고 새로운 배열에 저장했다. 그리고 이 새로운 배열의 원소들의 합을 구했다. </p>
<pre><code class="language-js">function binary_search(array, start, end) {
  let result = 0;
  while (start &lt;= end) {
    const mid = Math.floor((start + end) / 2);
    const newArr = array.map((element) =&gt; Math.floor(element / mid));
    const sum = newArr.reduce((ac, cv) =&gt; ac + cv, 0);
    if (sum &gt;= M) {
      result = mid;
      start = mid + 1;
    } else {
      end = mid - 1;
    }
  }
  return result;
}</code></pre>
<p>그러나 이 방법은 나눈 몫이 0일 때도 계산을 해야하고 새로운 배열 또한 생성해야 한다. 
따라서 이 방법은 시간복잡도나 공간 복잡도 측면에서 좋지 않다. </p>
<h4 id="보완한-코드-작성">보완한 코드 작성</h4>
<pre><code class="language-js">function binary_search(array, start, end) {
  let result = 0;
  while (start &lt;= end) {
    let sum = 0;
    const mid = Math.floor((start + end) / 2);
    for (let i = 0; i &lt; N; i++) {
      if (array[i] &gt;= mid) {
        sum += Math.floor(array[i] / mid);
      }
    }

    // ...생략
}</code></pre>
<p><img src="https://velog.velcdn.com/images/lee-yun-pyo/post/791cf52f-4f98-4cfa-9f06-fd92ba841c6a/image.png" alt=""></p>
]]></description>
        </item>
    </channel>
</rss>