<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>space_developher.log</title>
        <link>https://velog.io/</link>
        <description>어제보다 오늘 더, 오늘보다 내일 더</description>
        <lastBuildDate>Wed, 25 Jan 2023 02:58:37 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>space_developher.log</title>
            <url>https://images.velog.io/images/space_developher/profile/e00bbb4d-25ef-403a-b952-82c15aa1a43f/social.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. space_developher.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/space_developher" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[ 자바스크립트 ] 자바스크립트 딥다이브 46장 제네레이터와 async / await]]></title>
            <link>https://velog.io/@space_developher/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%94%A5%EB%8B%A4%EC%9D%B4%EB%B8%8C-46%EC%9E%A5-%EC%A0%9C%EB%84%A4%EB%A0%88%EC%9D%B4%ED%84%B0%EC%99%80-async-await</link>
            <guid>https://velog.io/@space_developher/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%94%A5%EB%8B%A4%EC%9D%B4%EB%B8%8C-46%EC%9E%A5-%EC%A0%9C%EB%84%A4%EB%A0%88%EC%9D%B4%ED%84%B0%EC%99%80-async-await</guid>
            <pubDate>Wed, 25 Jan 2023 02:58:37 GMT</pubDate>
            <description><![CDATA[<h1 id="1-제네레이터">1. 제네레이터</h1>
<h2 id="1-정의">1) 정의</h2>
<p><code>ES6</code> 에서 도입된 <code>제네레이터</code>는 코드 블록의 실행을 일시 중지했다가 필요한 시점에 재개할 수 있는 특수한 함수이다.</p>
<h3 id="1-제네레이터-함수는-함수-호출자에게-함수-실행의-제어권을-양도할-수-있다">#1. 제네레이터 함수는 함수 호출자에게 함수 실행의 제어권을 양도할 수 있다.</h3>
<p>일반 함수를 호출하면 제어권이 함수에게 넘어가고 함수 코드를 일괄 실행한다. 
즉, 함수 호출자는 함수를 호출한 이후 함수 실행을 제어할 수 없다.
그러나, 제네레이터 함수는 함수 실행을 함수 호출자가 제어할 수 있다.
다시 말해, 함수 호출자가 함수 실행을 일시 중지시키거나 재개시킬 수 있다.
이는 함수의 제어권을 함수 호출자에게 양도할 수 있다는 것을 의미한다.</p>
<h3 id="2-제네레이터-함수는-함수-호출자와-함수의-상태를-주고-받을-수-있다">#2. 제네레이터 함수는 함수 호출자와 함수의 상태를 주고 받을 수 있다.</h3>
<p>일반 함수를 호출하면 매개변수를 통해 함수 외부에서 값을 주입받고 함수 코드를 일괄 실행하여 결과값을 함수 외부로 반환한다.
즉, 함수를 실행하고 있는 동안에는 함수 외부에서 함수 내부로 값을 전달하여 함수의 상태를 변경할 수 없다.
그러나 제네레이터 함수는 함수 호출자와 양방향으로 함수의 상태를 주고 받을 수 있다.
다시 말해, 제네레이터 함수는 함수 호출자에게 상태를 전달할 수 있고, 함수 호출자로부터 상태를 전달받을 수 있다.</p>
<h3 id="3-제네레이터-함수를-호출하면-제네레이터-객체를-반환한다">#3. 제네레이터 함수를 호출하면 제네레이터 객체를 반환한다.</h3>
<p>일반 함수를 호출하면 함수 코드를 일괄 실행하고 값을 반환하지만, 제네레이터 함수를 호출하면 함수 코드를 실행하는 것이 아니라 이터러블이면서 동시에 이터레이터인 제네레이터 객체를 반환한다.</p>
<h2 id="2-제네레이터-함수의-정의방법">2) 제네레이터 함수의 정의방법</h2>
<p>제네레이터 함수는 <code>function*</code> 키워드를 통해 선언하며, 또한 하나 이상의 <code>yield</code> 표현식을 포함해야한다. 
위의 조건을 제외하고는 일반 함수를 정의하는 방법과 동일하다.</p>
<pre><code>// 제네레이터 함수 선언문
function* getDenFunc(){
    yield 1;
}

// 제네레이터 함수 표현식
const genExpFunc = function*(){
    yield 1;
}

// 제네레이터 메서드
const obj = {
    * genObjMethod(){
        yield1;
    }
}

// 제네레이터 클래스 메서드
class MyClass {
    * genClsMethod(){
        yield 1;
    }
}</code></pre><p>아스타리스크 <code>*</code> 의 위치는 <code>function</code> 키워드와 함수 이름 사이라면 어디든지 상관없다.</p>
<pre><code>// 키워드에 바로 붙여도 허용된다.
function* test(){
    yield 1;
}

// 양쪽 띄어도 허용된다.
function * test(){
    yield 1;
}

// 식별자에 바로 붙여도 허용된다.
function *test(){
    yield 1;   
}</code></pre><h3 id="주의사항">주의사항</h3>
<p>제네레이터 함수는 <code>화살표 함수</code> 로 정의할 수 없으며, <code>new 연산자</code> 와 함께 생성자 함수로 호출할 수 없다.</p>
<h2 id="3-제네레이터-객체">3) 제네레이터 객체</h2>
<p>제네레이터 함수를 호출하면 일반 함수처럼 함수 코드 블록을 실행하는 것이 아니라, 제네레이터 객체를 생성해서 반환한다.</p>
<p>제네레이터 함수가 반환한 제네레이터 객체는 이터러블이면서 동시에 이터레이터이다.</p>
<p>다시 말해서 제네레이터 객체는 <code>Symbol.iterator</code> 메서드를 상속 받는 이터러블이면서 <code>value</code> 와 <code>done</code> 프로퍼티를 갖는 이터레이터 리절트 객체를 반환하는 <code>next</code> 메서드를 소유하는 이터레이터다.</p>
<p>제네레이터 객체는 <code>next</code> 메서드를 가지는 이터레이터이므로, <code>Symbol.iterator</code> 메서드를 호출해서 별도로 이터레이터를 생성할 필요가 없다.</p>
<pre><code>// 제네레이터 함수
function* getFunc(){
    yield 1;
    yield 2;
    yield 3;
}

// 제네레이터 함수를 호출하면 제네레이터 객체를 반환한다.
const generator = getFunc();

// 제네레이터 객체는 이터러블이면서 동시에 이터레이터다.
// 이터러블은 Symbol.iterator 메서드를 직접 구현하거나 프로토타입 체인을 통해 상속받은 객체다.
console.log(Symbol.iterator in generator); // true

// 이터레이터는 next 메서드를 갖는다.
console.log(`next` in generator); // true</code></pre><p>제네레이터 객체는 <code>next</code> 메서드를 갖는 이터레이터이지만, 이터레이터에는 없는 <code>return</code>, <code>throw</code> 메서드를 갖는다.</p>
<h3 id="1-제네레이터-객체의-동작">#1. 제네레이터 객체의 동작</h3>
<p>제네레이터 객체의 세 개의 메서드를 호출하면 다음과 같이 동작한다.</p>
<pre><code>function* genFunc() {
  try {
    yield 1;
    yield 2;
    yield 3;
  } catch (e) {
    console.error(e)
  }
}

const generator = genFunc();</code></pre><ol>
<li><code>next</code> 메서드를 호출하면 제네레이터 함수의<code>yield</code> 표현식까지 코드 블록을 실행하고, <code>yield</code> 된 값을 <code>value</code> 프로퍼티 값으로 갖으며, 남은 <code>yield</code> 값의 존재여부에 따라 <code>done</code> 프로퍼티 값을 <code>true/false</code> 로 갖는 이터레이터 리절트 객체를 반환한다.</li>
</ol>
<pre><code>console.log(generator.next()); 
// { value: 1, done:false }</code></pre><ol start="2">
<li><code>return</code> 메서드를 호출하면 인수로 전달받은 값을 <code>value</code> 프로퍼티 값으로, <code>done</code> 프로퍼티 값을 <code>true</code> 로 갖는 이터레이터 리절트 객체를 반환한다.</li>
</ol>
<pre><code>console.log(generator.return(&#39;End!&#39;));; 
// { value: &quot;End!&quot;, done:true}</code></pre><ol start="3">
<li><code>throw</code> 메서드를 호출하면 인수로 전달받은 에러를 발생시키고 <code>undefined</code> 를 <code>value</code> 프로퍼티 값으로, <code>done</code> 프로퍼티 값은 <code>true</code> 로 갖는 이터레이터 리절트 객체를 반환한다.</li>
</ol>
<pre><code>console.log(generator.throw(&#39;Error&#39;)); 
// { value: undefined, done:true }</code></pre><h3 id="2-제네레이터의-일시-중지와-재개">#2. 제네레이터의 일시 중지와 재개</h3>
<p>제네레이터는 <code>yield</code> 키워드와 <code>next</code> 메서드를 통해 실행을 일시 중지했다가 필요한 시점에 다시 재개할 수 있다.
제네레이터 함수를 호출하면 제네레이터 객체를 반환하며, <code>next</code> 메서드를 통해 제네레이터 함수의 코드 블록을 실행하지만, <code>yield</code> 표현식까지만 실행한다.
<code>yield</code> 키워드는 제네레이터 함수의 실행을 일시 중지시키거나, <code>yield</code> 키워드 뒤에 오는 표현식의 평가 결과를 제네레이터 함수 호출자에게 반환한다.</p>
<pre><code>function* genFunc() {
  yield 1;
  yield 2;
  yield 3;
}

const generator = genFunc();

console.log(generator.next()); 
// {value :1, done: false}

console.log(generator.next());
// {value :2, done: false}

console.log(generator.next());
// {value :3, done: false}

console.log(generator.next());
// {value :undefined, done: true}</code></pre><h3 id="주의사항-1">주의사항</h3>
<p>이터레이터의 <code>next</code> 메서드와 달리 제네레이터의 <code>next</code> 메서드는 인수를 전달할 수 있다.
제네레이터 객체의 <code>next</code> 메서드에 전달한 인수는 제네레이터 함수의 <code>yield</code> 표현식을 할당받는 변수에 할당된다.</p>
<p><code>yield</code> 표현식을 할당받는 변수에 <code>yield</code> 표현식의 평가 결과가 할당되지 않는 것에 주의해야한다.</p>
<pre><code>function* genFunc() {
    // 처음 next 메서드를 호출하면 첫 번째 yield 표현식까지 실행되고 중지된다.
    // 이때 yield 값 1 은 next 메서드가 반환한 리절트 객체의 value 프로파티에 할당된다.
    // x 변수에는 아직 아무것도 할당되지 않았다.
    // x 변수의 값은 next 메서드가 두 번째 호출될 때 결정된다.
    const x = yield 1;

    const y = yield(x + 10);

    return x + y;
}

const generator = genfunc(0);

// 처음 호출하는 next 메서드에는 인수를 전달하지 않는다.
// 만약 처음 호출하는 next 메서드에 인수를 전달할 경우 무시된다.
let res = generator.next();
console.log(res) // { value: 1, done: false }

// x 변수에 10이 할당된다.
res = generator.next(10);
console.log(res) // { value: 20, done: false }</code></pre><h1 id="2-비동기-처리">2. 비동기 처리</h1>
<h2 id="1-asyncawait">1) async/await</h2>
<p><code>ES8</code> 에서 제네레이터보다 간단하고 가독성 좋게 비동기 처리를 동작하도록 구현할 수 있는 <code>async/await</code> 가 도입되었다.
<code>프로미스</code> 기반으로 동작한다. 프로미스의 후속 처리 없이 <code>동기 처리</code> 처럼 프로미스가 처리 결과를 반환하도록 구현할 수 있다.</p>
<h3 id="1-async-함수">#1. async 함수</h3>
<p><code>await</code> 키워드는 반드시 <code>async</code> 함수 내부에서 사용해야 한다.
<code>async</code> 함수는 언제나 <code>프로미스</code> 를 반환한다.
<code>async</code> 함수가 명시적으로 프로미스를 반환하지 않더라도 <code>async</code> 함수는 암묵적으로 반환값을 <code>resolve</code> 하는 프로미스를 반환한다.
클래스의 <code>constructor</code> 메서드는 <code>async</code> 함수가 될 수 없다.</p>
<pre><code>async function foo(n){
    return n;
}
foo(1).then(v =&gt; console.log(v)); // 1</code></pre><pre><code>const bar = async function(n){
    return n;
}
bar(2).then(v =&gt; console.log(v)); // 2</code></pre><pre><code>const baz = async n =&gt; n;
baz(3).then(v =&gt; console.log(v)); // 3</code></pre><pre><code>const obj = {
    async foo(n){
        return n;
    }
}

obj.foo(4).then(v =&gt; console.log(v)); // 4</code></pre><pre><code>class MyClass {
    async bar(n){
        return n;
    }
}
const myClass = new MyClass();
myClass.bar(5).then(v =&gt; console.log(v)); // 5</code></pre><h3 id="2-에러처리">#2. 에러처리</h3>
<p><code>async/await</code> 에서 <code>에러 처리</code>는 <code>try/catch</code> 문을 사용할 수 있다.</p>
<pre><code>async function foo(){
    try{
        const wrongURL = `...`;

        const response = await fetch(wrongURL);
        const data = await response.json();
    } catch(e){
        console.log(e);
    }
}

foo();</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[ 자바스크립트 ] 자바스크립트 딥다이브 44장 REST API]]></title>
            <link>https://velog.io/@space_developher/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%94%A5%EB%8B%A4%EC%9D%B4%EB%B8%8C-44%EC%9E%A5-REST-API</link>
            <guid>https://velog.io/@space_developher/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%94%A5%EB%8B%A4%EC%9D%B4%EB%B8%8C-44%EC%9E%A5-REST-API</guid>
            <pubDate>Tue, 24 Jan 2023 13:16:22 GMT</pubDate>
            <description><![CDATA[<h1 id="1-rest-api">1. REST API</h1>
<h2 id="1-정의">1) 정의</h2>
<p><code>REpresentational State Transfer</code> 의 약자
웹의 <code>HTTP</code> 의 장점을 활용한 클라이언트가 서버의 리소스에 접근하는 방식을 규정한 <code>아키텍처</code> 를 의미한다.</p>
<p><code>REST API</code> 는 <code>REST</code> 를 기반으로 <code>서비스 API</code> 를 구현한 것을 의미한다.</p>
<h2 id="2-구성">2) 구성</h2>
<p><code>REST API</code> 는 <code>자원</code>, <code>행위</code>, <code>표현</code> 으로 구성되며, <code>HTTP</code> 요청의 내용을 이해할 수 있다.</p>
<table>
<thead>
<tr>
<th>구성 요소</th>
<th>내용</th>
<th>표현 방법</th>
</tr>
</thead>
<tbody><tr>
<td>자원 (Resource)</td>
<td>자원</td>
<td>URI(엔드 포인트)</td>
</tr>
<tr>
<td>행위 (Verb)</td>
<td>자원에 대한 행위</td>
<td>HTTP 요청 메서드</td>
</tr>
<tr>
<td>표현(Representations)</td>
<td>자원에 대한 행위의 구체적 내용</td>
<td>페이로드</td>
</tr>
</tbody></table>
<h1 id="2-rest-api-설계-원칙">2. REST API 설계 원칙</h1>
<ol>
<li><code>URI</code> 는 리소스를 표현하는데 집중한다.</li>
<li><code>메서드</code> 는 HTTP 요청 메서드를 통한다.</li>
</ol>
<h2 id="1-uri는-리소스를-표현해야한다">1) URI는 리소스를 표현해야한다.</h2>
<p><code>URI</code> 는 리소스를 표현하는데 중점을 둬야하며, 리소스를 식별할 수 있는 이름은 동사보다 명사를 사용한다.
따라서, 이름에 <code>get</code> 같은 행위에 대한 표현이 들어가서는 안된다.</p>
<pre><code># BAD
GET / getTodos / 1  --- get X
GET / todos / show / 1 --- show X

# GOOD
GET / todos / 1</code></pre><h2 id="2-리소스에-대한-행위는-http-요청-메서드로-표현한다">2) 리소스에 대한 행위는 HTTP 요청 메서드로 표현한다.</h2>
<p><code>HTTP 요청 메서드</code> 는 <code>클라이언트</code> 가 <code>서버</code> 에게 <code>요청의 종류</code> 와 <code>목적(리소스에 대한 행위)</code> 을 알리는 방법이다.</p>
<p>메서드는 <code>GET</code>, <code>POST</code>, <code>PUT</code>, <code>PATCH</code>, <code>DELETE</code> 로 구성되며, <code>CRUD</code> 를 구현한다.</p>
<table>
<thead>
<tr>
<th>HTTP 요청 메서드</th>
<th>종류</th>
<th>목적</th>
<th>페이로드</th>
</tr>
</thead>
<tbody><tr>
<td>GET</td>
<td>index/retrieve</td>
<td>모든 혹은 특정 리소스 취득</td>
<td>X</td>
</tr>
<tr>
<td>POST</td>
<td>create</td>
<td>리소스 생성</td>
<td>O</td>
</tr>
<tr>
<td>PUT</td>
<td>replace</td>
<td>리소스의 전체 교체</td>
<td>O</td>
</tr>
<tr>
<td>PATCH</td>
<td>modify</td>
<td>리소스의 일부 수정</td>
<td>O</td>
</tr>
<tr>
<td>DELETE</td>
<td>delete</td>
<td>모든 혹은 특정 리소스 삭제</td>
<td>X</td>
</tr>
</tbody></table>
<p>리소스에 대한 행위는 <code>HTTP 요청 메서드</code> 를 통해 표현하며 <code>URI</code> 를 표현하지 않는다.
예를 들어 리소스를 취득하는 경우에는 <code>GET</code>, 리소스를 삭제하는 경우에는 <code>DELETE</code> 를 사용하여 리소스에 대한 행위를 명확히 표현한다.</p>
<pre><code># BAD
GET / todos / delete / 1

# GOOD
DELETE / todos / 1</code></pre><h1 id="3-json-server-를-이용한-rest-api-실습">3. JSON Server 를 이용한 REST API 실습</h1>
<p><code>HTTP 요청</code> 을 전송하고 응답을 받으려면 서버가 필요하나다.
<code>JSON Server</code> 를 사용해서 가상 <code>REST API</code> 서버를 구축하여 <code>HTTP</code> 요청을 전송하고 응답을 받는 실습을 진행해본다.</p>
<h2 id="1-json-server-설치">1) JSON-SERVER 설치</h2>
<p><code>npm</code> 을 이용해서 <code>json-server</code> 를 로컬 설치한다.</p>
<pre><code>mkdir json-server-exam &amp;&amp; json-server-exam
npm init -y
npm install json-server --save-dev</code></pre><h2 id="2-dbjson-파일-생성">2) db.json 파일 생성</h2>
<p><code>root</code> 폴더 아래에 <code>db.json</code> 파일을 생성한다. 해당 파일은 데이터베이스 역할을 한다.</p>
<pre><code>{
    &quot;todos&quot;: [
      {
        &quot;id&quot;: 1,
        &quot;content&quot;: &quot;HTML&quot;,
        &quot;completed&quot;: true
      },
      {
        &quot;id&quot;: 2,
        &quot;content&quot;: &quot;CSS&quot;,
        &quot;completed&quot;: false
      },
      {
        &quot;id&quot;: 3,
        &quot;content&quot;: &quot;Javascript&quot;,
        &quot;completed&quot;: true
      }
    ],
    &quot;users&quot;: [
      {
        &quot;id&quot;: 1,
        &quot;name&quot;: &quot;Lee&quot;,
        &quot;role&quot;: &quot;developer&quot;
      },
      {
        &quot;id&quot;: 2,
        &quot;name&quot;: &quot;Kim&quot;,
        &quot;role&quot;: &quot;designer&quot;
      }
    ]
  }</code></pre><h2 id="3-script-추가">3) script 추가</h2>
<p><code>package.json</code> 내 <code>script</code> 에 <code>json-server</code> 관련 스크립트를 추가한다.</p>
<pre><code>// package.json
...
&quot;scripts&quot;: {
  &quot;test&quot;: &quot;echo \&quot;Error: no test specified\&quot; &amp;&amp; exit 1&quot;,
  &quot;start&quot;: &quot;json-server --watch db.json&quot;
},
...</code></pre><h2 id="4-script-실행">4) script 실행</h2>
<pre><code>npm run start</code></pre><h2 id="5-json-server-접속">5) JSON-SERVER 접속</h2>
<pre><code>localhost:3000</code></pre><h2 id="6-indexhtml-파일-생성">6) index.html 파일 생성</h2>
<p><code>JSON-SERVER</code> 에서 읽는 파일을 별도로 지정할 수 있다.
경로는 <code>./public/index.html</code> 파일이다.</p>
<pre><code>// index.html 

&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;IE=edge&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
    &lt;title&gt;Document&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;pre&gt;&lt;/pre&gt;
    &lt;script&gt;
        const XHR = new XMLHttpRequest()
        XHR.open(`GET`, `/todos`)

        XHR.send()

        XHR.onload = () =&gt; {
            if(XHR.status == 200){
                document.querySelector(`pre`).textContent = XHR.response
            }else {
                console.log(`error`, XHR.status, XHR.statusText)
            }
        }
    &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><h1 id="4-post-요청">4. <code>POST</code> 요청</h1>
<pre><code>&lt;script&gt;
  const XHR = new XMLHttpRequest();

  XHR.open(`POST`, `/todos`);
  XHR.setRequestHeader(`Content-Type`, `application/json`);

  XHR.send(JSON.stringify({ id: 4, content: &quot;React&quot;, complete: true }));

  XHR.onload = () =&gt; {
    if (XHR.status == 200 || XHR.status === 201) {
      document.querySelector(`pre`).teßxtContent = XHR.response;
    } else {
      console.log(`error`, XHR.status, XHR.statusText);
    }
  };
&lt;/script&gt;</code></pre><h1 id="5-put-요청">5. <code>PUT</code> 요청</h1>
<pre><code>&lt;script&gt;
  const XHR = new XMLHttpRequest();

  XHR.open(`PUT`, `/todos/4`);
  XHR.setRequestHeader(`Content-Type`, `application/json`);

  XHR.send(JSON.stringify({ id: 4, content: &quot;React-2&quot;, complete: true }));

  XHR.onload = () =&gt; {
    if (XHR.status == 200) {
      document.querySelector(`pre`).teßxtContent = XHR.response;
    } else {
      console.log(`error`, XHR.status, XHR.statusText);
    }
  };
&lt;/script&gt;</code></pre><h1 id="6-patch-요청">6. <code>PATCH</code> 요청</h1>
<pre><code>&lt;script&gt;
  const XHR = new XMLHttpRequest();

  XHR.open(`PATCH`, `/todos/4`);
  XHR.setRequestHeader(`Content-Type`, `application/json`);

  XHR.send(JSON.stringify({ content: &quot;React-3&quot; }));

  XHR.onload = () =&gt; {
    if (XHR.status == 200) {
      document.querySelector(`pre`).teßxtContent = XHR.response;
    } else {
      console.log(`error`, XHR.status, XHR.statusText);
    }
  };
&lt;/script&gt;</code></pre><h1 id="7-delete-요청">7. <code>DELETE</code> 요청</h1>
<pre><code>&lt;script&gt;
    const XHR = new XMLHttpRequest();

    XHR.open(`DELETE`, `/todos/4`);
    XHR.send();
    XHR.onload = () =&gt; {
        if (XHR.status == 200) {
            document.querySelector(`pre`).teßxtContent = XHR.response;
        } else {
            console.log(`error`, XHR.status, XHR.statusText);
        }
    };
&lt;/script&gt;</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[ 자바스크립트 ] 자바스크립트 딥다이브 39장 DOM]]></title>
            <link>https://velog.io/@space_developher/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%94%A5%EB%8B%A4%EC%9D%B4%EB%B8%8C-39%EC%9E%A5-DOM</link>
            <guid>https://velog.io/@space_developher/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%94%A5%EB%8B%A4%EC%9D%B4%EB%B8%8C-39%EC%9E%A5-DOM</guid>
            <pubDate>Sat, 10 Dec 2022 01:22:33 GMT</pubDate>
            <description><![CDATA[<p>브라우저 렌더링 엔진은 HTML 문서를 파싱하여 브라우저가 이해할 수 있는 자료구조인 DOM 을 생성한다.
DOM 은 HTML 문서의 계층적 구조와 정보를 표현하며 이를 제어할 수 있는 API, 즉 프로퍼티와 메서드를 제공하는 트리 자료구조이다.</p>
<h1 id="노드">노드</h1>
<h2 id="html-요소와-노드-객체">HTML 요소와 노드 객체</h2>
<p>HTML 요소는 HTML 문서를 구성하는 개별적인 요소를 의미한다.</p>
<ul>
<li>HTML 요소의 구조
<img src="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbAerqp%2FbtrvkGjtEy8%2FNlEaA4Ad3KTdWN2tuPKHXk%2Fimg.png" alt=""></li>
</ul>
<p>HTML 요소는 렌더링 엔진에 의해 파싱되어 DOM을 구성하는 요소 노드 객체로 변환된다.</p>
<ul>
<li>HTML 요소와 노드 객체
<img src="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJ0tB4%2FbtrviHwcZZm%2FtLnZ6FEirUhcf2UTsDv7MK%2Fimg.png" alt=""></li>
</ul>
<p>노드 객체들로 구성된 트리 자료구조를 DOM이라 한다. DOM 트리라고 부르기도 한다.</p>
<h2 id="노드-객체의-상속-구조">노드 객체의 상속 구조</h2>
<p>DOM은 DOM을 제어할 수 있는 API, 즉 프로퍼티와 메서드를 제공하는 트리 자료구조이다.
DOM 을 구성하는 노드 객체는 브라우저 환경에서 제공하는 호스트 객체다.
노드 객체도 자바 스크립트 객체이므로 프로토 타입에 의한 상속 구조를 갖으며, <code>Object</code>, <code>EventTarget</code>, <code>Node</code> 인터페이스를 상속받는다.</p>
<p>예를 들어 <code>input</code> 요소 노드 객체의 경우, <code>HTMLInputElement</code>, <code>HTMLElement</code>, <code>Element</code>, <code>Node</code>, <code>EventTarget</code>, <code>Object</code> 의 <code>prototype</code> 에 바인딩되어 있는 프로토타입 객체를 상속받아 프로토타입 체인에 있는 모든 프로토타입의 프로퍼티나 메서드를 상속받아 사용할 수 있다.</p>
<h3 id="정리">정리</h3>
<p>DOM은 HTML 문서의 계층적 구조와 정보를 표현하는 것은 물론 노드 객체의 종류, 즉 노드 타입에 따라 필요한 기능을 프로퍼티와 메서드의 집합인 DOM API로 제공한다. 
이 DOM API를 통해 HTML의 구조나 내용 또는 스타일 등을 동적으로 조작할 수 있다.</p>
<h2 id="요소-노드-취득">요소 노드 취득</h2>
<p>HTML의 구조나 내용 또는 스타일 등을 동적으로 조작하려면 먼저 요소 노드를 취득해야 한다.
DOM은 요소 노드를 취득할 수 있는 다양한 메서드를 제공한다.</p>
<h3 id="id-를-이용한-요소-노드-취득-getelementbyid">ID 를 이용한 요소 노드 취득 (getElementById)</h3>
<p><code>Document.prototype.getElementById</code> 메서드는 인수로 전달한 <code>id 어트리뷰트 값</code> 을 갖는 하나의
요소 노드를 탐색하여 반환한다. id값은 HTML 문서 내에서 유일한 값이어야 하지만, id가 여러개 존재하더라도 에러가 발생하지 않는다.</p>
<p>인수로 전달된 id값을 갖는 첫 번째 요소 노드가 반환한다. 
단, id 값이 중복되더라도 첫 번째 id값만 반환하며, 요소가 존재하지 않는 경우 null을 반환한다.</p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;body&gt;
    &lt;ul&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;orange&quot;&gt;Orange&lt;/li&gt;
    &lt;/ul&gt;
    &lt;script&gt;
      // id 값이 &#39;banana&#39;인 요소 노드를 탐색하여 반환한다.
      // 두 번째 li 요소가 파싱되어 생성된 요소 노드가 반환된다.
      const $elem = document.getElementById(&#39;banana&#39;);

      // 취득한 요소 노드의 style.color 프로퍼티 값을 변경한다.
      $elem.style.color = &#39;red&#39;;
    &lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre><pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;body&gt;
    &lt;ul&gt;
      &lt;li id=&quot;banana&quot;&gt;Apple&lt;/li&gt;
      &lt;li id=&quot;banana&quot;&gt;Banana&lt;/li&gt;
      &lt;li id=&quot;banana&quot;&gt;Orange&lt;/li&gt;
    &lt;/ul&gt;
    &lt;script&gt;
      // getElementById 메서드는 언제나 단 하나의 요소 노드를 반환한다.
      // 첫 번째 li 요소가 파싱되어 생성된 요소 노드가 반환된다.
      const $elem = document.getElementById(&#39;banana&#39;);

      // 취득한 요소 노드의 style.color 프로퍼티 값을 변경한다.
      $elem.style.color = &#39;red&#39;;
    &lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre><h3 id="태그-이름을-이용한-요소-노드-취득getelementsbytagname">태그 이름을 이용한 요소 노드 취득(getElementsByTagName)</h3>
<p><code>Document.prototype/Element.prototype.getElementsByTagName</code> 메서드는 인수로 전달한 태그 이름을 갖는 모든 요소 노드들을 탐색하여 반환한다.</p>
<p>탐색된 요소들은 <code>HTMLCollection</code> 객체에 담겨 반환되며, <code>HTMLCollection</code> 객체는 유사 배열 객체이면서 이터러블이다.</p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;body&gt;
    &lt;ul&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;orange&quot;&gt;Orange&lt;/li&gt;
    &lt;/ul&gt;
    &lt;script&gt;
      // 태그 이름이 li인 요소 노드를 모두 탐색하여 반환한다.
      const $elems = document.getElementsByTagName(&#39;li&#39;);

      // 취득한 모든 요소 노드의 style.color 프로퍼티 값을 변경한다.
      // HTMLCollection 객체를 배열로 변환하여 순회하며 color 프로퍼티 값을 변경한다.
      [...$elems].forEach(elem =&gt; { elem.style.color = &#39;red&#39;; });
    &lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre><h3 id="class-를-이용한-요소-노드-취득getelementsbyclassname">class 를 이용한 요소 노드 취득(getElementsByClassName)</h3>
<p>Document.prototype/Element.prototype.getElementsByClassName 메서드는 인수로 전달한 class 어트리뷰트 값을 갖는 모든 요소 노드들을 탐색하여 반환한다.</p>
<p>탐색된 값은 HTMLCollection 객체에 담아 반환한다.</p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;body&gt;
    &lt;ul&gt;
      &lt;li class=&quot;fruit apple&quot;&gt;Apple&lt;/li&gt;
      &lt;li class=&quot;fruit banana&quot;&gt;Banana&lt;/li&gt;
      &lt;li class=&quot;fruit orange&quot;&gt;Orange&lt;/li&gt;
    &lt;/ul&gt;
    &lt;script&gt;
      // class 값이 &#39;fruit&#39;인 요소 노드를 모두 탐색하여 HTMLCollection 객체에 담아 반환한다.
      const $elems = document.getElementsByClassName(&#39;fruit&#39;);

      // 취득한 모든 요소의 CSS color 프로퍼티 값을 변경한다.
      [...$elems].forEach(elem =&gt; { elem.style.color = &#39;red&#39;; });

      // class 값이 &#39;fruit apple&#39;인 요소 노드를 모두 탐색하여 반환한다.
      const $apples = document.getElementsByClassName(&#39;fruit apple&#39;);

      // 취득한 모든 요소 노드의 style.color 프로퍼티 값을 변경한다.
      [...$apples].forEach(elem =&gt; { elem.style.color = &#39;blue&#39;; });
    &lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre><h3 id="css-선택자를-이용한-요소-노드-취득">CSS 선택자를 이용한 요소 노드 취득</h3>
<h4 id="queryselector">querySelector</h4>
<p><code>Document.prototype/Element.prototype.querySelector</code> 메서드는 인수로 전달한 CSS 선택자를 만족시키는 하나의 요소 노드를 탐색하여 반환한다.</p>
<p>인수로 전달된 CSS 선택자를 만족시키는 요소 노드가 존재하지 않는 경우 null을 반환</p>
<p><code>querySelector</code> 를 이용하여 특수한 조건을 통해 요소를 취득할 수 있다.</p>
<pre><code>const $apple = document.querySelector(&#39;.apple&#39;);

// $apple 노드는 &#39;#fruits &gt; li.apple&#39;로 취득할 수 있다.
console.log($apple.matches(&#39;#fruits &gt; li.apple&#39;));  // true

// $apple 노드는 &#39;#fruits &gt; li.banana&#39;로 취득할 수 없다.
console.log($apple.matches(&#39;#fruits &gt; li.banana&#39;)); // false</code></pre><pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;body&gt;
    &lt;ul&gt;
      &lt;li class=&quot;apple&quot;&gt;Apple&lt;/li&gt;
      &lt;li class=&quot;banana&quot;&gt;Banana&lt;/li&gt;
      &lt;li class=&quot;orange&quot;&gt;Orange&lt;/li&gt;
    &lt;/ul&gt;
    &lt;script&gt;
      // class 어트리뷰트 값이 &#39;banana&#39;인 첫 번째 요소 노드를 탐색하여 반환한다.
      const $elem = document.querySelector(&#39;.banana&#39;);

      // 취득한 요소 노드의 style.color 프로퍼티 값을 변경한다.
      $elem.style.color = &#39;red&#39;;
    &lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre><h4 id="queryselectorall">querySelectorAll</h4>
<p><code>Document.prototype/Element.prototype.querySelectorAll</code> 메서드는 인수로 전달한 CSS 선택자를 만족시키는 모든 요소 노드를 탐색하여 반환한다. 
<code>querySelectorAll</code> 메서드는 여러 개의 요소 노드 객체를 갖는 DOM 컬렉션 객체인 <code>NodeList</code> 객체를 반환한다. 
<code>NodeList</code> 객체는 유사 배열 객체이면서 이터러블이다.</p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;body&gt;
    &lt;ul&gt;
      &lt;li class=&quot;apple&quot;&gt;Apple&lt;/li&gt;
      &lt;li class=&quot;banana&quot;&gt;Banana&lt;/li&gt;
      &lt;li class=&quot;orange&quot;&gt;Orange&lt;/li&gt;
    &lt;/ul&gt;
    &lt;script&gt;
      // ul 요소의 자식 요소인 li 요소를 모두 탐색하여 반환한다.
      const $elems = document.querySelectorAll(&#39;ul &gt; li&#39;);
      // 취득한 요소 노드들은 NodeList 객체에 담겨 반환된다.
      console.log($elems); // NodeList(3) [li.apple, li.banana, li.orange]

      // 취득한 모든 요소 노드의 style.color 프로퍼티 값을 변경한다.
      // NodeList는 forEach 메서드를 제공한다.
      $elems.forEach(elem =&gt; { elem.style.color = &#39;red&#39;; });
    &lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre><h3 id="특정-요소를-취득할-수-있는지-확인하는-방법-match">특정 요소를 취득할 수 있는지 확인하는 방법 (match)</h3>
<p><code>Element.prototype.matches</code> 메서드 사용하여 인수로 전달한 CSS 선택자를 통해 특정 요소 노드를 취득할 수 있는지 확인할 수 있다.</p>
<p>이벤트 위임을 사용할 때 유용하다.</p>
<pre><code>const $apple = document.querySelector(&#39;.apple&#39;);

// $apple 노드는 &#39;#fruits &gt; li.apple&#39;로 취득할 수 있다.
console.log($apple.matches(&#39;#fruits &gt; li.apple&#39;));  // true

// $apple 노드는 &#39;#fruits &gt; li.banana&#39;로 취득할 수 없다.
console.log($apple.matches(&#39;#fruits &gt; li.banana&#39;)); // false</code></pre><h3 id="htmlcollection과-nodelist">HTMLCollection과 NodeList</h3>
<p>DOM 컬렉션 객체인 <code>HTMLCollection</code> 과 <code>NodeList</code> 는 DOM API가 여러 개의 결과값을 반환하기 위한 DOM 컬렉션 객체이다. HTMLCollection과 NodeList는 모두 유사 배열 객체이면서 이터러블이다.
따라서 for...of 문으로 순회할 수 있으며 스프레드 문법을 사용하여 간단히 배열로 변환할 수 있다.</p>
<h4 id="htmlcollection">HTMLCollection</h4>
<p><code>HTMLCollection</code> 는 노드 객체의 상태 변화를 실시간으로 반영하는 살아 있는 객체라는 것이다.
상태가 즉시 반영 되기 때문에 에러가 나기도 함, 이를 해결하기 위해 배열로 변환 하여 사용 권장한다.</p>
<h4 id="nodelist">NodeList</h4>
<p><code>NodeList</code> 는 실시간으로 객체의 상태 변경을 반영하지 않는 객체이다.
<code>NodeList.prototype.forEach</code> 메서드를 상속 받아 사용할 수 있다.
<code>childNodes</code> 프로퍼티가 반환하는 <code>NodeList</code> 객체는 <code>HTMLCollection</code> 객체와 동일하게 실시간 객체로 동작하므로 주의가 필요하다.</p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;head&gt;
  &lt;style&gt;
    .red { color: red; }
    .blue { color: blue; }
  &lt;/style&gt;
&lt;/head&gt;
&lt;html&gt;
  &lt;body&gt;
    &lt;ul id=&quot;fruits&quot;&gt;
      &lt;li class=&quot;red&quot;&gt;Apple&lt;/li&gt;
      &lt;li class=&quot;red&quot;&gt;Banana&lt;/li&gt;
      &lt;li class=&quot;red&quot;&gt;Orange&lt;/li&gt;
    &lt;/ul&gt;
    &lt;script&gt;
      // class 값이 &#39;red&#39;인 요소 노드를 모두 탐색하여 HTMLCollection 객체에 담아 반환한다.
      const $elems = document.getElementsByClassName(&#39;red&#39;);
      // 이 시점에 HTMLCollection 객체에는 3개의 요소 노드가 담겨 있다.
      console.log($elems); // HTMLCollection(3) [li.red, li.red, li.red]

      // HTMLCollection 객체의 모든 요소의 class 값을 &#39;blue&#39;로 변경한다.
      for (let i = 0; i &lt; $elems.length; i++) {
        $elems[i].className = &#39;blue&#39;;
      }

      // HTMLCollection 객체의 요소가 3개에서 1개로 변경되었다.
      console.log($elems); // HTMLCollection(1) [li.red]
    &lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre><p><img src="https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdejO35%2FbtrviI9JhiG%2FhzSyVI9bKKzHKGpugjgbLk%2Fimg.png" alt=""></p>
<p>위 예제는 <code>HTMLCollection</code> 객체를 for 문으로 순회화며 class 값을 &#39;red&#39;에서 &#39;blue&#39;로 변경했다.하지만 두 번째 요소는 바뀌지 않았다. 이유는 다음과 같다. <code>$elems.length</code> 는 3이므로 3번 반복된다.</p>
<ol>
<li>첫 번째 반복 (i==0)
<code>$elems[0] li</code> 의 첫 번째 요소가 &#39;blue&#39;로 변경된다. 이때 첫 번째 li 요소가 변경되었으므로 <code>getElementsByClassName</code> 메서드의 인자로 전달한 &#39;red&#39;와는 더는 일치하지 않기 때문에 <code>HTMLCollection</code> 객체의 <code>$elems</code> 에서 실시간으로 제거된다.</li>
<li>두 번째 반복 (i==1)
첫 번째 반복에서 li 요소가 삭제되었으므로 뒤에 남아 있던 리스트들은 한 칸씩 앞으로 밀리게 된다. 따라서 <code>$elems[0]</code> 은 두 번째 li 요소가 되고 $elems[1]은 세 번째 il요소가 된다. 즉 세 번째 li의 값이 바뀌고 또한 제거된다.</li>
<li>세 번째 반복 (i==2)
첫 번째, 세번째 li 요소가 제거되고 <code>$elems</code> 에는 두 번째 li 태그만 남아있게 되어 <code>$elem.length</code> 가 1이 된다. 조건식에 맞지 않으므로 반복이 종료된다.</li>
</ol>
<p>이처럼 <code>HTMLCollection</code> 객체는 실시간으로 노드 객체의 상태 변경을 반영하기 때문에 for 문 순회를 통해 상태를 변경할 때는 주의해야 한다. </p>
<h4 id="해결방법">해결방법</h4>
<ul>
<li><p><code>while</code> 문 이용 : 객체가 남아 있지 않을 때까지 무한 반복</p>
<pre><code>// while 문으로 HTMLCollection에 요소가 남아 있지 않을 때까지 무한 반복
let i = 0;
while ($elems.length &gt; i) {
$elems[i].className = &#39;blue&#39;;
}</code></pre></li>
<li><p>고차함수 이용 : <code>HTMLCollection</code> 객체를 사용하지 않는 고차함수로 해결</p>
<pre><code>// 유사 배열 객체이면서 이터러블인 HTMLCollection을 배열로 변환하여 순회
[...$elems].forEach(elem =&gt; elem.className = &#39;blue&#39;);</code></pre></li>
<li><p><code>querySelectorAll</code> 이용 : <code>NideList</code> 객체는 실시간으로 노드 객체의 상태 변경을 반영하지 않음</p>
</li>
</ul>
<p>따라서 노드 객체의 상태 변경과 상관없이 안전하게 DOM 컬렉션을 사용하려면 <code>HTMLCollection</code> 이나 <code>NodeList</code> 객체를 배열로 변환하여 사용하는 것을 권장한다. 또한 고차 함수를 사용하여 순회한다.</p>
<h3 id="textcontent">textContent</h3>
<p>Node.prototype.textContent 프로퍼티는 setter와 getter 모두 존재하는 접근자 프로퍼티로서 요소 노드의 텍스트와 모든 자손 노드의 텍스트를 모두 취득하거나 변경한다.</p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;body&gt;
    &lt;div id=&quot;foo&quot;&gt;Hello &lt;span&gt;world!&lt;/span&gt;&lt;/div&gt;
  &lt;/body&gt;
  &lt;script&gt;
    // #foo 요소 노드의 텍스트를 모두 취득한다. 이때 HTML 마크업은 무시된다.
    console.log(document.getElementById(&#39;foo&#39;).textContent); // Hello world!
  &lt;/script&gt;
&lt;/html&gt;</code></pre><p>textContent로 변경시 문자열로 인식되어 텍스트로 취급한다. 즉, HTML 마크업이 파싱되지 않는다.</p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;body&gt;
    &lt;div id=&quot;foo&quot;&gt;Hello &lt;span&gt;world!&lt;/span&gt;&lt;/div&gt;
  &lt;/body&gt;
  &lt;script&gt;
    // #foo 요소 노드의 모든 자식 노드가 제거되고 할당한 문자열이 텍스트로 추가된다.
    // 이때 HTML 마크업이 파싱되지 않는다.
    document.getElementById(&#39;foo&#39;).textContent = &#39;Hi &lt;span&gt;there!&lt;/span&gt;&#39;;
    // 위의 문장을 수행한 결과는 다음과 같다. 문자열로 들어간다.
    //  &lt;div id=&quot;foo&quot;&gt;&quot;Hi &lt;span&gt;there!&lt;/span&gt;&quot;&lt;/div&gt;
  &lt;/script&gt;
&lt;/html&gt;</code></pre><h3 id="innerhtml">innerHTML</h3>
<p>Element.prototype.innerHTML 프로퍼티는 setter와 getter 모두 존재하는 접근자 프로퍼티로서 요소 노드의 HTML 마크업을 취득하거나 변경한다.</p>
<p>textContent 프로퍼티는 HTML 마크업을 무시하고 텍스트만 반환하지만 innerHTML 프로퍼티는 HTML 마크업이 포함된 문자열을 그대로 반환한다. 또한 마크업 태그를 삽입할 수도 있다.</p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;body&gt;
    &lt;div id=&quot;foo&quot;&gt;Hello &lt;span&gt;world!&lt;/span&gt;&lt;/div&gt;
  &lt;/body&gt;
  &lt;script&gt;
    // #foo 요소의 콘텐츠 영역 내의 HTML 마크업을 문자열로 취득한다.
    console.log(document.getElementById(&#39;foo&#39;).innerHTML);
    // &quot;Hello &lt;span&gt;world!&lt;/span&gt;&quot;
  &lt;/script&gt;
&lt;/html&gt;</code></pre><pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;body&gt;
    &lt;div id=&quot;foo&quot;&gt;Hello &lt;span&gt;world!&lt;/span&gt;&lt;/div&gt;
  &lt;/body&gt;
  &lt;script&gt;
    // HTML 마크업이 파싱되어 요소 노드의 자식 노드로 DOM에 반영된다.
    document.getElementById(&#39;foo&#39;).innerHTML = &#39;Hi &lt;span&gt;there!&lt;/span&gt;&#39;;
  &lt;/script&gt;
&lt;/html&gt;</code></pre><p>innerHTML으로 문자열을 할당하면 문자열에 포함되어 있는 HTML 마크업이 파싱되어 요소 노드의 자식 노드로 DOM에 반영된다. 이때 사용자로부터 입력받은 데이터를 그대로 innerHTML 프로퍼티에 할당하는 것은 크로스 사이트 스크립팅 공격(XXS)에 취약하므로 위험하다. 마크업 내에 자바스크립트 악성 코드가 포함되어 있다면 파싱 과정에서 그대로 실행될 가능성이 있기 때문이다.</p>
<p>단, HTML5 에서는 innerHTML 프로퍼티로 삽입된 script 요소 내의 자바스크립트 코드를 실행하지 않는다. 하지만 다른 방식으로 공격을 할 수 있다.</p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;body&gt;
    &lt;div id=&quot;foo&quot;&gt;Hello&lt;/div&gt;
  &lt;/body&gt;
  &lt;script&gt;
    // 에러 이벤트를 강제로 발생시켜서 자바스크립트 코드가 실행되도록 한다.
    document.getElementById(&#39;foo&#39;).innerHTML
      = `&lt;img src=&quot;x&quot; onerror=&quot;alert(document.cookie)&quot;&gt;`;
  &lt;/script&gt;
&lt;/html&gt;</code></pre><p>이처럼 innerHTML 프로퍼티를 사용한 DOM 조작은 구현이 간단하고 직관적인 장점이 있지만
크로스 사이트 스크립팅 공격에 취약한 단점도 있다.</p>
<h3 id="노드-생성과-추가">노드 생성과 추가</h3>
<p>Document.prototype.createElement(tagName) 메서드는 요소 노드를 생성하여 반환한다.
createElement 메서드의 메개변수 tagName에는 태그 이름을 나타내는 문자열을 인수로 전달한다.</p>
<pre><code>// 1. 요소 노드 생성
const $li = document.createElement(&#39;li&#39;);

// 2. 텍스트 노드 생성
const textNode = document.createTextNode(&#39;Banana&#39;);

// 3. 텍스트 노드를 $li 요소 노드의 자식 노드로 추가
$li.appendChild(textNode);</code></pre><h3 id="노드-삽입">노드 삽입</h3>
<p>Node.prototype.appendChild 메서드는 인수로 전달받은 노드를 자신을 호출한 노드의 마지막 자식 노드로 DOM에 추가한다. 이때 노드를 추가할 위치를지정할 수 없고 언제나 마지막 자신 노드로 추가한다.</p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;body&gt;
    &lt;ul id=&quot;fruits&quot;&gt;
      &lt;li&gt;Apple&lt;/li&gt;
      &lt;li&gt;Banana&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/body&gt;
  &lt;script&gt;
    // 요소 노드 생성
    const $li = document.createElement(&#39;li&#39;);

    // 텍스트 노드를 $li 요소 노드의 마지막 자식 노드로 추가
    $li.appendChild(document.createTextNode(&#39;Orange&#39;));

    // $li 요소 노드를 #fruits 요소 노드의 마지막 자식 노드로 추가
    document.getElementById(&#39;fruits&#39;).appendChild($li);
  &lt;/script&gt;
&lt;/html&gt;</code></pre><p>Node.prototype.inserBefore(newNode, childNode) 메서드는 첫 번째 인수로 전달받은 노드를 두 번째 인수로 전달받은 노드 앞에 삽입한다.</p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;body&gt;
    &lt;ul id=&quot;fruits&quot;&gt;
      &lt;li&gt;Apple&lt;/li&gt;
      &lt;li&gt;Banana&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/body&gt;
  &lt;script&gt;
    const $fruits = document.getElementById(&#39;fruits&#39;);

    // 요소 노드 생성
    const $li = document.createElement(&#39;li&#39;);

    // 텍스트 노드를 $li 요소 노드의 마지막 자식 노드로 추가
    $li.appendChild(document.createTextNode(&#39;Orange&#39;));

    // $li 요소 노드를 #fruits 요소 노드의 마지막 자식 요소 앞에 삽입
    $fruits.insertBefore($li, $fruits.lastElementChild);
    // Apple - Orange - Banana
  &lt;/script&gt;
&lt;/html&gt;</code></pre><h3 id="노드-이동">노드 이동</h3>
<p>DOM에 이미 존재하는 노드를 appendChild 또는 insertBefore 메서드를 사용하면 DOM에 다시 추가하면 현재 위치에서 노드를 제거하고 새로운 위치에 노드를 추가한다. 즉, 노드가 이동한다.</p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;body&gt;
    &lt;ul id=&quot;fruits&quot;&gt;
      &lt;li&gt;Apple&lt;/li&gt;
      &lt;li&gt;Banana&lt;/li&gt;
      &lt;li&gt;Orange&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/body&gt;
  &lt;script&gt;
    const $fruits = document.getElementById(&#39;fruits&#39;);

    // 이미 존재하는 요소 노드를 취득
    const [$apple, $banana, ] = $fruits.children;

    // 이미 존재하는 $apple 요소 노드를 #fruits 요소 노드의 마지막 노드로 이동
    $fruits.appendChild($apple); // Banana - Orange - Apple

    // 이미 존재하는 $banana 요소 노드를 #fruits 요소의 마지막 자식 노드 앞으로 이동
    $fruits.insertBefore($banana, $fruits.lastElementChild);
    // Orange - Banana - Apple
  &lt;/script&gt;
&lt;/html&gt;</code></pre><h3 id="노드-복사">노드 복사</h3>
<p>Node.prototype.cloneNode([deep : true | false]) 메서드는 노드의 사본을 생성하여 반환한다.
매게변수 deep에 true를 인수로 전달하면 노드를 깊은 복사하여 모든 자손 노드가 포함된 사본을 생성하고, false를 인수로 전달하거나 생략하면 얕은 복사하여 노드 자신만의 사본을 생성한다. 이때 텍스트 노드도 자손 노드이므로 텍스트 노드도 복사되지 않는다.</p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;body&gt;
    &lt;ul id=&quot;fruits&quot;&gt;
      &lt;li&gt;Apple&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/body&gt;
  &lt;script&gt;
    const $fruits = document.getElementById(&#39;fruits&#39;);
    const $apple = $fruits.firstElementChild;

    // $apple 요소를 얕은 복사하여 사본을 생성. 텍스트 노드가 없는 사본이 생성된다.
    const $shallowClone = $apple.cloneNode();
    // 사본 요소 노드에 텍스트 추가
    $shallowClone.textContent = &#39;Banana&#39;;
    // 사본 요소 노드를 #fruits 요소 노드의 마지막 노드로 추가
    $fruits.appendChild($shallowClone);

    // #fruits 요소를 깊은 복사하여 모든 자손 노드가 포함된 사본을 생성
    const $deepClone = $fruits.cloneNode(true);
    // 사본 요소 노드를 #fruits 요소 노드의 마지막 노드로 추가
    $fruits.appendChild($deepClone);
  &lt;/script&gt;
&lt;/html&gt;</code></pre><h3 id="노드-교체">노드 교체</h3>
<p>Node.prototype.replaceChild(newChild, oldChild) 메서드는 자신을 호출한 노드의 자식 노드를 다른 노드로 교체한다. 첫 번째 매개변수 newChild에는 교체할 새로운 노드를 인수로 전달하고, 두 번째 매개변수 oldChild에는 이미 존재하는 교체될 노드를 인수로 전달한다.</p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;body&gt;
    &lt;ul id=&quot;fruits&quot;&gt;
      &lt;li&gt;Apple&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/body&gt;
  &lt;script&gt;
    const $fruits = document.getElementById(&#39;fruits&#39;);

    // 기존 노드와 교체할 요소 노드를 생성
    const $newChild = document.createElement(&#39;li&#39;);
    $newChild.textContent = &#39;Banana&#39;;

    // #fruits 요소 노드의 첫 번째 자식 요소 노드를 $newChild 요소 노드로 교체
    $fruits.replaceChild($newChild, $fruits.firstElementChild);
  &lt;/script&gt;
&lt;/html&gt;</code></pre><h3 id="노드-삭제">노드 삭제</h3>
<p>Node.prototype.removeChild(child) 메서드는 child 매개변수에 인수로 전달한 노드를 DOM에서 삭제한다.</p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;body&gt;
    &lt;ul id=&quot;fruits&quot;&gt;
      &lt;li&gt;Apple&lt;/li&gt;
      &lt;li&gt;Banana&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/body&gt;
  &lt;script&gt;
    const $fruits = document.getElementById(&#39;fruits&#39;);

    // #fruits 요소 노드의 마지막 요소를 DOM에서 삭제
    $fruits.removeChild($fruits.lastElementChild);
  &lt;/script&gt;
&lt;/html&gt;</code></pre><h2 id="어트리뷰트">어트리뷰트</h2>
<p>HTML 문서의 구성 요소인 HTML 요소는 여러 개의 어트리뷰트(속성)을 가질 수 있다. HTML 요소의 동작을 제어하기 위한 추가적인 정보를 제공하는 HTML 어트리뷰트는 HTML 요소의 시작 태그에 어트리뷰트 이름 = 어티르뷰트 값 형식으로 정의한다.</p>
<pre><code>&lt;input id=&quot;user&quot; type=&quot;text&quot; value=&quot;ungmo2&quot;&gt;</code></pre><h3 id="html-어트리뷰트-vs-dom-프로퍼티">HTML 어트리뷰트 vs DOM 프로퍼티</h3>
<p>요소 노드 객체에는 HTML 어트리뷰트에 대응하는 프로퍼티가 존재한다. 이 DOM 프로퍼티들은 HTML 어트리뷰트 값을 초기값으로 가지고 있다.</p>
<p>HTML 어트리뷰트의 역할은 HTML 요소의 초기 상태를 지정하는 것이다. 즉, HTML 어트리뷰트 값은 HTML 요소의 초기 상태를 의미하며 이는 변하지 않는다.</p>
<p>DOM 프로퍼티의 역할은 사용자의 입력에 의한 상태 변화에 반응하여 언제나 최신 상태를 유지한다.</p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;body&gt;
  &lt;input id=&quot;user&quot; type=&quot;text&quot; value=&quot;ungmo2&quot;&gt;
  &lt;script&gt;
    const $input = document.getElementById(&#39;user&#39;);

    // attributes 프로퍼티에 저장된 value 어트리뷰트 값
    console.log($input.getAttribute(&#39;value&#39;)); // ungmo2

    // 요소 노드의 value 프로퍼티에 저장된 value 어트리뷰트 값
    console.log($input.value); // ungmo2
  &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><p>위의 예제를 보면 input value의 초기값은 &quot;ungmo2&quot; 인 것을 알 수 있다.
하지만 사용자의 입력으로 &quot;foo&quot; 등과 같이 다른 문자열을 입력하면 value의 값이 바뀐다.
&quot;foo&quot;는 최신 상태이다. 그리고 새로 고침을 하면 다시 &quot;ungmo2&quot;가 된다.</p>
<p>이처럼 요소 노드는 2개의 상태를 가진다. 즉 초기 상태와 최신 상태를 관리해야 한다.
요소 노드의 초기 상태는 어트리뷰트 노드가 관리하며, 노드의 최신 상태는 DOM 프로퍼티가 관리한다.</p>
<h3 id="data-어트리뷰트와-dataset-프로퍼티">data 어트리뷰트와 dataset 프로퍼티</h3>
<p>data 어트리뷰트와 dataset 프로퍼티를 사용하면 HTML 요소에 정의한 사용자 정의 어트리뷰트와 자바스크립트 간에 데이터를 교환할 수 있다. data 어트리뷰트는 data-user-id, data-role과 같이 data- 접두사 다음에 임의의 이름을 붙여 사용한다.</p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;body&gt;
  &lt;ul class=&quot;users&quot;&gt;
    &lt;li id=&quot;1&quot; data-user-id=&quot;7621&quot; data-role=&quot;admin&quot;&gt;Lee&lt;/li&gt;
    &lt;li id=&quot;2&quot; data-user-id=&quot;9524&quot; data-role=&quot;subscriber&quot;&gt;Kim&lt;/li&gt;
  &lt;/ul&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre><p>data 어트리뷰트 값은 HTMLElement.dataset 프로퍼티로 취득할 수 있다. data 어트리뷰트의 data- 접두사 다음에 붙인 임의의 이름을 카멜 케이스로 변환한 프로퍼티를 가지고 있다. 이 프로퍼티로 data 어트리뷰트의 값을 취득하거나 변경할 수 있다.</p>
<pre><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;body&gt;
  &lt;ul class=&quot;users&quot;&gt;
    &lt;li id=&quot;1&quot; data-user-id=&quot;7621&quot; data-role=&quot;admin&quot;&gt;Lee&lt;/li&gt;
    &lt;li id=&quot;2&quot; data-user-id=&quot;9524&quot; data-role=&quot;subscriber&quot;&gt;Kim&lt;/li&gt;
  &lt;/ul&gt;
  &lt;script&gt;
    const users = [...document.querySelector(&#39;.users&#39;).children];

    // user-id가 &#39;7621&#39;인 요소 노드를 취득한다.
    const user = users.find(user =&gt; user.dataset.userId === &#39;7621&#39;);
    // user-id가 &#39;7621&#39;인 요소 노드에서 data-role의 값을 취득한다.
    console.log(user.dataset.role); // &quot;admin&quot;

    // user-id가 &#39;7621&#39;인 요소 노드의 data-role 값을 변경한다.
    user.dataset.role = &#39;subscriber&#39;;
    // dataset 프로퍼티는 DOMStringMap 객체를 반환한다.
    console.log(user.dataset); // DOMStringMap {userId: &quot;7621&quot;, role: &quot;subscriber&quot;}
  &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[ 자바스크립트 ] 자바스크립트 딥다이브 37장 Set 과 Map]]></title>
            <link>https://velog.io/@space_developher/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%94%A5%EB%8B%A4%EC%9D%B4%EB%B8%8C-37%EC%9E%A5-Set-%EA%B3%BC-Map</link>
            <guid>https://velog.io/@space_developher/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%94%A5%EB%8B%A4%EC%9D%B4%EB%B8%8C-37%EC%9E%A5-Set-%EA%B3%BC-Map</guid>
            <pubDate>Wed, 07 Dec 2022 00:58:50 GMT</pubDate>
            <description><![CDATA[<h1 id="set">Set</h1>
<p>Set 객체는 중복되지 않는 유일한 값들의 집합으로, Set 객체는 배열과 유사하지만 차이가 있다.</p>
<table>
<thead>
<tr>
<th>구분</th>
<th>배열</th>
<th>Set</th>
</tr>
</thead>
<tbody><tr>
<td>동일한 값을 중복하여 포함할 수 있다.</td>
<td>O</td>
<td>X</td>
</tr>
<tr>
<td>요소 순서에 의미가 있다.</td>
<td>O</td>
<td>X</td>
</tr>
<tr>
<td>인덱스로 요소에 접근할 수 있다.</td>
<td>O</td>
<td>X</td>
</tr>
</tbody></table>
<p>Set 객체는 수학적 집합을 구현하기 위한 자료구조로, Set 객체를 통해 교집합, 합집합, 차집합, 여집합등을 구현할 수 있다.</p>
<h2 id="set-객체의-생성">Set 객체의 생성</h2>
<p>Set 객체는 Set 생성자 함수로 생성한다. 
인수를 전달하지 않으면 빈 Set 객체가 생성된다.</p>
<pre><code>const set = new Set();
console.log(set); // Set(0) {}</code></pre><p>Set 생성자 함수는 이터러블을 인수로 전달받아 Set 객체를 생성한다. 이 때 이터러블의 중복된 값은 Set 객체에 요소로 저장되지 않는다.</p>
<pre><code>const set1 = new Set([1, 2, 3, 3]);
console.log(set); // Set(3) {1, 2, 3}

const set2 = new Set(`hello`);
console.log(set); // Set(4) { &quot;h&quot;, &quot;e&quot;, &quot;l&quot;, &quot;o&quot; }</code></pre><p>중복을 허용하지 않는 Set 객체의 특성을 활용하여 배열에서 중복된 요소를 제거할 수 있다.</p>
<pre><code>// 배열의 중복 요소 재거
const uniq = array =&gt; array.filter((v, i, self) =&gt; self.indexOf(v) === i);
console.log(uniq[2, 1, 2, 3, 4, 3, 4]); // [2, 1, 3, 4]

// Set 을 사용한 배열의 중복 요소 제거
const uniq = array =&gt; [...new Set(array)];
console.log(uniq([2, 1, 2, 3, 4, 3, 4])); // [2, 1, 3, 4]</code></pre><h2 id="요소-개수-확인">요소 개수 확인</h2>
<p>Set 객체의 요소 개수를 확인할 때는 Set.prototype.size 프로퍼티를 사용한다.</p>
<pre><code>const { size } = new Set([1, 2, 3, 3]);
console.log(size); // 3</code></pre><p>size 프로퍼티는 setter 함수 없이 getter 함수만 존재하는 접근자 프로퍼티다.
따라서 size 프로퍼티에 숫자를 할당하여 Set 객체의 요소 개수를 변경할 수 없다.</p>
<pre><code>const set = new Set([1, 2, 3]);
console.log(Object.getOwnPropertyDescriptor(Set.prototype, `size`));
// {set: undefined, enumerable: false, configurable: true, get: f}

set.size = 10; // 무시된다.
console.log(set.size); // 3</code></pre><h2 id="요소-추가">요소 추가</h2>
<p>set 객체에 요소를 추가할 때는 Set.prototype.add 메서드를 사용한다.</p>
<pre><code>const set = new Set();
cosnole.log(set); // Set(0) {}

set.add(1);
console.log(set); // Set(1) {}</code></pre><p>add 메서드는 새로운 요소가 추가된 Set 객체를 반환한다. 따라서 add 메서드를 호출한 후에 add 메서드를 연속적으로 호출하여 메서드 체이닝 할 수 있다.</p>
<pre><code>const set = new Set();
set.add(1).add(2);
console.log(set); // Set(2) {1, 2}</code></pre><p>Set 객체에 중복된 요소의 추가는 허용되지 않는다. 이때 에러가 발생하지는 않고 무시된다.</p>
<pre><code>const set = new Set();
set add(1).add(2).add(2);
console.log(set); // Set(2) {1, 2}</code></pre><p>일치 비교 연산자 === 를 사용하면 NaN 과 NaN 을 다르고 평가한다. 하지만 Set 객체는 NaN 과 NaN 을 같다고 평가하여 중복 추가를 허용하지 않는다. 
0과 -0 은 일치 비교 연산자 === 과 마찬가지로 같다고 평가하여 중복 추가를 허용하지 않는다.</p>
<pre><code>const set = new Set();

console.log(NaN === NaN); // false
console.log(0 === -0); // true

// NaN 과 NaN 을 같다고 평가하여 중복 추가를 허용하지 않는다.
set.add(NaN).add(NaN);
console.log(set); // Set(1) {NaN}

// 0 과 -0 을 같다고 평가하여 중복 추가를 허용하지 않는다.
set.add(0).add(-0);
console.log(set); // Set(2) {NaN, 0}</code></pre><p>Set 객체는 객체나 배열과 같이 자바스크립트의 모든 값을 요소로 저장할 수 있다.</p>
<pre><code>const set = new Set();

set
    .add(1)
    .add(`a`)
    .add(true)
    .add(undefined)
    .add(null)
    .add({})
    .add([]);

console.log(set); // Set(7) {1, &quot;a&quot;, true, undefined, null, {}, []}</code></pre><h2 id="요소-존재-여부-확인">요소 존재 여부 확인</h2>
<p>Set.prototype.has 메서드를 사용해서 Set 객체에 특정 요소가 존재하는지 확인한다. 
불리언 값을 반환한다.</p>
<pre><code>const set = new Set([1, 2, 3]);

console.log(set); // Set(3) {1, 2, 3}
console.log(set.has(2)); // true
console.log(set.has(4)); // false</code></pre><h2 id="요소-삭제">요소 삭제</h2>
<p>Set.prototype.delete 메서드를 사용해서 특정 요소를 삭제한다.
불리언 값을 반환하며 인덱스가 아닌 삭제하려는 요소값을 인수로 전달해야한다.
Set 객체는 순서에 의미가 없다. 다시 말해서 배열과 같이 인덱스를 갖지 않는다.</p>
<pre><code>const set = new Set([1, 2, 3]);

// 요소 2를 삭제한다.
set.delete(2);
console.log(set); // Set(2) {1, 3}

// 요소 1를 삭제한다.
set.delete(1);
console.log(set); // Set(1) {3}</code></pre><p>만약 존재하지 않는 Set 객체의 요소를 삭제하려하면 에러 없이 무시된다.</p>
<pre><code>const set = new Set([1, 2, 3]);

// 존재하지 않는 요소 0를 삭제하면 에러없이 무시된다.
set.delete(0);
console.log(set); // Set(3) {1, 2, 3}</code></pre><p>delete 메서드는 삭제 성공 여부를 나타내는 불리언 값을 반환한다.
따라서 Set.prototype.add 메서드와 달리 연속적으로 메서드 체이닝을 할 수 없다.</p>
<pre><code>const set = new Set([1, 2, 3]);

// delete 는 불리언 값을 반환한다.
set.delete(1).delete(2); // TypeError: set.delete(...) delete is not a function</code></pre><h2 id="요소-일괄-삭제">요소 일괄 삭제</h2>
<p>Set 객체의 모든 요소를 일괄 삭제하려면 Set.prototype.clear 메서드를 사용한다.
clear 메서드는 언제나 undefined 를 반환한다.</p>
<pre><code>const set = new Set([1, 2, 3]);

set.clear();
console.log(set); // Set(0) {}</code></pre><h2 id="요소-순회">요소 순회</h2>
<p>Set.prototype.forEach를 사용하며, Array.prototype.forEach 메서드와 유사하게 콜백 함수와 forEach 메서드의 콜백 함수 내부에서 this 로 사용될 객체(옵션)를 인수로 전달한다.
이때 콜백 함수는 다음과 같이 3개의 인수를 전달받는다.</p>
<ul>
<li>첫 번째 인수 : 현재 순회 중인 요소값</li>
<li>두 번째 인수 : 현재 순회 중인 요소값</li>
<li>세 번째 인수 : 현재 순회 중인 Set 객체 자체</li>
</ul>
<p>첫 번째 인수와 두 번째 인수는 같은 값이다. 이처럼 동작하는 이유는 Array.prototype.forEach 메서드와 인터페이스를 통일하기 위함이며, 다른 의미는 없다.
Array.prototype.forEach 메서드의 콜백 함수는 두 번째 인수로 현재 순회 중인 요소의 인덱스를 전달받지만, Set 객체는 순서에 의미가 없어서 배열과 같이 인덱스를 갖지 않는다.</p>
<pre><code>const set = new Set([1, 2, 3]);
set.forEach((v1, v2, set) =&gt; console.log(v1, v2, set));
/*
1 1 Set(3) {1, 2, 3}
2 2 Set(3) {1, 2, 3}
3 3 Set(3) {1, 2, 3}
*/</code></pre><p>Set 객체는 이터러블이다. 따라서 for ... of 문으로 순회할 수 있으며, 스프레드 문법과 배열 디스트럭처링의 대상이 될 수도 있다.</p>
<pre><code>const set = new Set([1, 2, 3]);

// Set 객체는 Set.prototype의 Symbol.iterator 메서드를 상속받는 이터러블이다.
console.log(Symbol.iterator in set); // true

// 이터러블인 Set 객체는 for ... of 문으로 순회할 수 있다.
for (const value of set) {
  console.log(value); // 1 2 3
}

// 이터러블인 Set 객체는 스프레드 문법의 대상이 될 수 있다.
console.log([...set]); // [1, 2, 3]

// 이터러블인 Set 객체는 배열 디스트럭처링 할당의 대상이 될 수 있다.
const [a, ...rest] = set;
console.log(a, rest); // 1, [2, 3]</code></pre><p>Set 객체는 요소의 순서에 의미를 갖지 않지만 Set 객체를 순회하는 순서는 요소가 추가된 순서를 따른다.
이는 다른 이터러블의 순회와 호환성을 유지하기 위함이다.</p>
<h2 id="집합-연산">집합 연산</h2>
<p>Set 객체는 수학적 집합을 구현하기 위한 자료구조이다. 따라서 Set 객체를 통해 교집합, 합집합, 차집합 등을 구현할 수 있다. 집합 연산을 수행하는 프로토타입 메서드를 구현하면 다음과 같다.</p>
<h3 id="교집합intersection">교집합(intersection)</h3>
<p>교집합은 집합 A 와 집합 B 의 공통 요소로 구성된다.</p>
<pre><code>Set.prototype.intersection = function (set) {
  const result = new Set();

  for (const value of set) {
    // 2개의 set의 요소가 공통되는 요소이면 교집합의 대상이다.
    if (this.has(value)) result.add(value);
  }

  return result;
};

const setA = new Set([1, 2, 3, 4]);
const setB = new Set([2, 4]);

// setA와 setB의 교집합
console.log(setA.intersection(setB)); // Set(2) {2, 4}
// setB와 setA의 교집합
console.log(setB.intersection(setA)); // Set(2) {2, 4}</code></pre><h3 id="합집합union">합집합(union)</h3>
<p>합집합은 집합 A 와 집합 B 의 중복 없는 모든 요소로 구성된다.</p>
<pre><code>Set.prototype.union = function (set) {
  // this(Set 객체)를 복사
  const result = new Set(this);

  for(const value of set) {
    // 합집합은 2개의 Set 객체의 모든 요소로 구성된 집합이다. 중복된 요소는 포함되지 않는다.
   result.add(value);
};

const setA = new Set([1, 2, 3, 4]);
const setB = new Set([2, 4]);

// setA와 setB의 합집합
console.log(setA.union(setB)); // Set(4) {1, 2, 3, 4}
// setB와 setA의 합집합
console.log(setB.union(setA)); // Set(4) {2, 4, 1, 3}</code></pre><h3 id="차집합-difference">차집합 (difference)</h3>
<p>차집합은 집합 A 에는 존재하지만 집합 B 에는 존재하지 않는 요소로 구성된다.</p>
<pre><code>Set.prototype.difference = function (set) {
  // this(Set 객체)를 복사
  const result = new Set(this);

  for (const value of set) {
    // 차집합은 어느 한쪽 집합에는 존재하지만 다른 한쪽 집합에는 존재하지 않는 요소로 구성된 집합이다.
    result.delete(value);
  }

  return result;
};

const setA = new Set([1, 2, 3, 4]);
const setB = new Set([2, 4]);

// setA에 대한 setB의 차집합
console.log(setA.difference(setB)); // Set(2) {1, 3}
// setB에 대한 setA의 차집합
console.log(setB.difference(setA)); // Set(0) {}</code></pre><h3 id="부분-집합과-상위-집합-issuperset">부분 집합과 상위 집합 (isSuperset)</h3>
<p>집합 A가 집합 B에 포함되는 경우 집합 A 는 집합 B 의 부분 집합이며, 집합 B 는 집합 A 의 상위 집합이다.</p>
<pre><code>// this가 subset의 상위 집합인지 확인한다.
Set.prototype.isSuperset = function (subset) {
  for (const value of subset) {
    // superset의 모든 요소가 subset의 모든 요소를 포함하는지 확인
    if (!this.has(value)) return false;
  }

  return true;
};

const setA = new Set([1, 2, 3, 4]);
const setB = new Set([2, 4]);

// setA가 setB의 상위 집합인지 확인한다.
console.log(setA.isSuperset(setB)); // true
// setB가 setA의 상위 집합인지 확인한다.
console.log(setB.isSuperset(setA)); // false</code></pre><h1 id="map">Map</h1>
<p>Map 객체는 키와 값의 쌍으로 이루어진 컬렉션이다. Map 객체는 객체와 유사하지만 차이도 있다.</p>
<table>
<thead>
<tr>
<th>구분</th>
<th>객체</th>
<th>Map 객체</th>
</tr>
</thead>
<tbody><tr>
<td>키로 사용할 수 있는 값</td>
<td>문자열 또는 심벌 값</td>
<td>객체를 포함한 모든 값</td>
</tr>
<tr>
<td>이터러블</td>
<td>X</td>
<td>O</td>
</tr>
<tr>
<td>요소 개수 확인</td>
<td>Object.keys(obj).length</td>
<td>map.size</td>
</tr>
</tbody></table>
<h2 id="map-객체의-생성">Map 객체의 생성</h2>
<p>Map 객체는 Map 생성자 함수로 생성한다. Map 생성자 함수에 인수를 전달하지 않으면 빈 Map 객체가 생성된다.</p>
<pre><code>const map = new Map();
console.log(map); // Map(0) {}</code></pre><p>Map 생성자 함수는 이러터블을 인수로 전달받아 Map 객체를 생성한다. 이때 인수로 전달되는 이터러블은 키와 값의 쌍으로 이루어진 요소로 구성되어야 한다.</p>
<pre><code>const map1 = new Map([[&#39;key1&#39;, &#39;value1&#39;], [&#39;key2&#39;, &#39;value2&#39;]]);
console.log(map1); // Map(2) {&quot;key1&quot; =&gt; &quot;value1&quot;, &quot;key2&quot; =&gt; &quot;value2&quot;}

const map2 = new Map([1, 2]); // TypeError: Iterator value 1 is not an entry object</code></pre><p>Map 생성자 함수의 인수로 전달한 이터러블에 중복된 키를 갖는 요소가 존재하면 값이 덮어써진다. 따라서 Map 객체에는 중복된 키를 갖는 요소가 존재할 수 없다.</p>
<pre><code>const map = new Map([[&#39;key1&#39;, &#39;value1&#39;], [&#39;key1&#39;, &#39;value2&#39;]]);
console.log(map); // Map(1) {&quot;key1&quot; =&gt; &quot;value2&quot;}</code></pre><h2 id="요소-개수-확인-1">요소 개수 확인</h2>
<p>Map 객체의 요소 개수를 확인할 때는 Map.prototype.size 프로퍼티를 사용한다.</p>
<pre><code>const { size } = new Map([[&#39;key1&#39;, &#39;value1&#39;], [&#39;key2&#39;, &#39;value2&#39;]]);
console.log(size); // 2</code></pre><p>size 프로퍼티는 setter 함수 없이 getter 함수만 존재하는 접근자 프로퍼티다. 따라서 size 프로퍼티에 숫자를 할당하여 Map 객체의 요소 개수를 변경할 수 있다.</p>
<h2 id="요소-추가-1">요소 추가</h2>
<p>Map 객체에 요소를 추가할 때는 Map.prototype.set 메서드를 사용한다.</p>
<pre><code>const map = new Map();
console.log(map); // Map(0) {}

map.set(&#39;key1&#39;, &#39;value1&#39;); 
console.log(map); // Map(1) {&quot;key1&quot; =&gt; &quot;value1&quot;}</code></pre><p>set 메서드는 새로운 요소가 추가된 Map 객체를 반환한다. 따라서 set 메서드를 호출한 후에 set 메서드를 연속적으로 호출할 수 있다.</p>
<pre><code>const map = new Map();

map
.set(&#39;key1&#39;, &#39;value1&#39;)
.set(&#39;key2&#39;, &#39;value2&#39;);

console.log(map); // Map(2) {&quot;key1&quot; =&gt; &quot;value1&quot;, &quot;key2&quot; =&gt; &quot;value2&quot;}</code></pre><p>Map 객체에는 중복된 키를 갖는 요소가 존재할 수 없기 때문에 중복된 키를 갖는 요소를 추가하면 값이 덮어써진다. 이때 에러가 발생하지는 않는다.</p>
<pre><code>const map = new Map();

map
.set(&#39;key1&#39;, &#39;value1&#39;)
.set(&#39;key1&#39;, &#39;value2&#39;)

console.log(map); // Map(1) {&quot;key1&quot; =&gt; &quot;value2&quot;}</code></pre><p>일치 비교 연산자 === 를 사용하면 NaN 과 NaN 을 다르다고 평가한다. 하지만 Map 객체는 NaN 과 NaN 을 같다고 평가하여 중복 추가를 허용하지 않는다. 0과 -0은 일치 비교 연산자 === 와 마찬가지로 같다고 평가하여 중복 추가를 허용하지 않는다.</p>
<p>객체는 문자열 또는 심벌 값만 키로 사용할 수 있다. 하지만 Map 객체는 키 타입에 제한이 없다. 따라서 객체를 포함한 모든 값을 키로 사용할 수 있다. 이는 Map 객체와 일반 객체의 가장 두드러지는 차이점이다.</p>
<pre><code>const map = new Map();

const lee = { name: &#39;Lee&#39; };
const kim = { name: &#39;Kim&#39; };

// 객체로 키로 사용할 수 있다.
map
.set(lee, &#39;developer&#39;)
.set(kim, &#39;designer&#39;)

console.log(map);
// Map(2) { {name: &quot;Lee&quot;} =&gt; &quot;developer&quot;, {name: &quot;Kim&quot;} =&gt; &quot;designer&quot; }
</code></pre><h2 id="요소-취득">요소 취득</h2>
<p>Map 객체에서 특정 요소를 취득하려면 Map.prototype.get 메서드를 사용한다. get 메서드의 인수로 키를 전달하면 Map 객체에서 인수로 전달한 키를 갖는 값을 반환한다. Map 객체에서 인수로 전달한 키를 갖는 요소가 존재하지 않으면 undefined 를 반환한다.</p>
<pre><code>const map = new Map();

const lee = { name: &#39;Lee&#39; };
const kim = { name: &#39;Kim&#39; };

map
.set(lee, &#39;developer&#39;)
.set(kim, &#39;designer&#39;);

console.log(map.get(lee)); // developer
console.log(map.get(&#39;key&#39;)); // undefined
</code></pre><h2 id="요소-존재-여부-확인-1">요소 존재 여부 확인</h2>
<p>Map 객체에 특정 요소가 존재하는지 확인하려면 Map.prototype.has 메서드를 사용한다. has 메서드는 특정 요소의 존재 여부를 나타내는 불리언 값을 반환한다.</p>
<pre><code>const lee = { name: &#39;Lee&#39; };
const kim = { name: &#39;Kim&#39; };

const map = new Map([[lee, &#39;developer&#39;], [kim, &#39;designer&#39;]]);

console.log(map.has(lee)); // true
console.log(map.has(&#39;key&#39;)); // false
</code></pre><h2 id="요소-삭제-1">요소 삭제</h2>
<p>Map 객체의 요소를 삭제하려면 Map.prototype.delete 메서드를 사용한다. delete 메서드는 삭제 성공 여부를 나타내는 불리언 값을 반환한다.</p>
<pre><code>const lee = { name: &#39;Lee&#39; };
const kim = { name: &#39;Kim&#39; };

const map = new Map([[lee, &#39;developer&#39;], [kim, &#39;designer&#39;]]);

map.delete(kim);
console.log(map); // Map(1) { {name: &quot;Lee&quot;} =&gt; &quot;developer&quot; }
</code></pre><h2 id="요소-일괄-삭제-1">요소 일괄 삭제</h2>
<p>Map 객체의 요소를 일괄 삭제하려면 Map.prototype.clear 메서드를 사용한다. clear 메서드는 언제나 undefined 를 반환한다.</p>
<pre><code>const lee = { name: &#39;Lee&#39; };
const kim = { name: &#39;Kim&#39; };

const map = new Map([[lee, &#39;developer&#39;], [kim, &#39;designer&#39;]]);

map.clear();
console.log(map); // Map(0) {}
</code></pre><h2 id="요소-순회-1">요소 순회</h2>
<p>Map 객체의 요소를 순회하려면 Map.prototype.forEach 메서드를 사용한다. Map.prototype.forEach 메서드는 Array.prototype.forEach 메서드와 유사하게 콜백 함수와 forEach 메서드의 콜백 함수 내부에서 this 로 사용될 객체(옵션)를 인수로 전달한다.
이때 콜백 함수는 다음과 같이 3개의 인수를 전달받는다.</p>
<ul>
<li>첫 번째 인수: 현재 순회 중인 요소값</li>
<li>두 번째 인수: 현재 순회 중인 요소키</li>
<li>세 번째 인수: 현재 순회 중인 Map 객체 자체</li>
</ul>
<pre><code>const lee = { name: &#39;Lee&#39; };
const kim = { name: &#39;Kim&#39; };

const map = new Map([[lee, &#39;developer&#39;], [kim, &#39;designer&#39;]]);

map.forEach((value, key, self) =&gt; console.log(value, key, self));
/*
developer {name: &quot;Lee&quot;} Map(2) {
    {name: &quot;Lee&quot;} =&gt; &quot;developer&quot;,
    {name: &quot;Kim&quot;} =&gt; &quot;designer&quot;
}
designer {name: &quot;Kim&quot;} Map(2) {
    {name: &quot;Lee&quot;} =&gt; &quot;developer&quot;,
    {name: &quot;Kim&quot;} =&gt; &quot;designer&quot;
}
*/</code></pre><p>Map 객체는 이터러블이다. 따라서 for ... of 문으로 순회할 수 있으며, 스프레드 문법과 배열 디스트럭처링 할당의 대상이 될 수도 있다.</p>
<pre><code>const lee = { name: &#39;Lee&#39; };
const kim = { name: &#39;Kim&#39; };

const map = new Map([[lee, &#39;developer&#39;], [kim, &#39;designer&#39;]]);

// Map 객체는 Map.prototype의 Symbol.iterator 메서드를 상속받는 이터러블이다.
console.log(Symbol.iterator in map); // true

// 이터러블인 Map 객체는 for ... of 문으로 순회할 수 있다.
for (const entry of map) {
  console.log(entry); // [{name: &quot;Lee&quot;}, &quot;developer&quot;] [{name: &quot;Kim&quot;}, &quot;designer&quot;]
}

// 이터러블인 Map 객체는 스프레드 문법의 대상이 될 수 있다.
console.log([...map]);
// [{name: &quot;Lee&quot;}, &quot;developer&quot;] [{name: &quot;Kim&quot;}, &quot;designer&quot;]

// 이터러블인 Map 객체는 배열 디스트럭처링 할당의 대상이 될 수 있다.
const [a, b] = map;
console.log(a, b); // [{name: &quot;Lee&quot;}, &quot;developer&quot;] [{name: &quot;Kim&quot;}, &quot;designer&quot;]</code></pre><p>Map 객체는 이터러블이면서 동시에 이터레이터인 객체를 반환하는 메서드를 제공한다.</p>
<table>
<thead>
<tr>
<th>Map 메서드</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>Map.prototype.keys</td>
<td>Map 객체에서 요소키를 값으로 갖는 이터러블이면서 동시에 이터레이터인 객체를 반환한다.</td>
</tr>
<tr>
<td>Map.prototype.values</td>
<td>Map 객체에서 요소값을 값으로 갖는 이터러블이면서 동시에 이터레이터인 객체를 반환한다.</td>
</tr>
<tr>
<td>Map.prototype.entries</td>
<td>Map 객체에서 요소키와 요소값을 값으로 갖는 이터러블이면서 동시에 이터레이터인 객체를 반환한다.</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ 알뜰신잡 ] MVC 패턴]]></title>
            <link>https://velog.io/@space_developher/%EC%95%8C%EB%9C%B0%EC%8B%A0%EC%9E%A1-MVC-%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@space_developher/%EC%95%8C%EB%9C%B0%EC%8B%A0%EC%9E%A1-MVC-%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Thu, 01 Dec 2022 23:28:26 GMT</pubDate>
            <description><![CDATA[<h1 id="mvc-패턴이란">MVC 패턴이란?</h1>
<p>디자인 패턴 중 하나으로, Model &amp; View &amp; Controller 로 역할을 구분한 개발 방법론이다.</p>
<h2 id="모델1-아키텍쳐">모델1 아키텍쳐</h2>
<p>JSP(View) &amp; JavaBean(Service) 
뷰와 로직이 섞인다.</p>
<h3 id="장점">장점</h3>
<p>구조가 단순하다.</p>
<h3 id="단점">단점</h3>
<p>출력과 로직 코드가 섞여 JSP 코드가 복잡해진다,
프론트와 백엔드가 혼재되어 분업이 용이하지 않다.
유지보수가 어렵다.</p>
<h2 id="모델-2-아키텍쳐">모델 2 아키텍쳐</h2>
<p>JSP(View) &amp; JavaBean(Service) &amp; 서블릿</p>
<h3 id="장점-1">장점</h3>
<p>뷰와 로직의 분리로 모델1에 비해 덜 복잡하고, 분업이 용이하며, 유지보수가 쉽다.</p>
<h3 id="단점-1">단점</h3>
<p>모델1에 비해 습득이 어렵고 작업량이 많다.</p>
<h1 id="mvc-흐름">MVC 흐름</h1>
<ol>
<li>사용자가 원하는 기능을 처리하기 위한 모든 요청을 컨트롤러에 보낸다.</li>
<li>컨트롤러는 모델을 사용하고, 모델은 알맞은 비즈니스 로직을 수행한다.</li>
<li>컨트롤러는 사용자에게 보이는 뷰를 선택한다.</li>
<li>선택된 뷰는 사용자에게 알맞는 결과 화면을 보여준다. 이때 사용자에게 보여줄 데이터는 컨트롤러를 통해서 전달받는다.</li>
</ol>
<h1 id="mvc-구성-요소">MVC 구성 요소</h1>
<h2 id="model">Model</h2>
<p>값과 기능을 가지고 있는 객체
비즈니스 로직을 수행한다.</p>
<h2 id="view">View</h2>
<p>모델에 포함된 데이터의 시각화</p>
<h2 id="controller">Controller</h2>
<p>모델 객체로의 데이터 흐름을 제어
뷰와 모델의 역할을 분리</p>
<h1 id="왜-mvc-패턴을-사용하는가">왜 MVC 패턴을 사용하는가?</h1>
<h2 id="장점-2">장점</h2>
<ul>
<li>각 컴포넌트의 코드 결합도를 낮추기 위해 사용된다.</li>
<li>코드의 재사용성을 높이기 위해 사용된다.</li>
<li>구현자들 간의 커뮤니케이션 효율성을 높이기 위해 사용된다.</li>
</ul>
<h1 id="사용시-주의사항">사용시 주의사항</h1>
<h3 id="model-에서-view의-접근-또는-역할-수행">Model 에서 View의 접근 또는 역할 수행</h3>
<h3 id="view-에서-일어나는-과한-값-검증과-예외-처리">View 에서 일어나는 과한 값 검증과 예외 처리</h3>
<ul>
<li>단일 책임 원칙 위반<h3 id=""></h3>
</li>
</ul>
<h1 id="참고">참고</h1>
<p><a href="https://www.youtube.com/watch?v=uoVNJkyXX0I">우아한 테크</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ 자바스크립트 ] 자바스크립트 딥 다이브 - 32장 String]]></title>
            <link>https://velog.io/@space_developher/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%94%A5-%EB%8B%A4%EC%9D%B4%EB%B8%8C-32%EC%9E%A5-String</link>
            <guid>https://velog.io/@space_developher/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%94%A5-%EB%8B%A4%EC%9D%B4%EB%B8%8C-32%EC%9E%A5-String</guid>
            <pubDate>Thu, 01 Dec 2022 09:04:32 GMT</pubDate>
            <description><![CDATA[<p>표준 빌트인 객체인 <code>String</code> 은 원시 타입인 문자열을 다룰 때 사용된다.</p>
<h2 id="string-생성자-함수">String 생성자 함수</h2>
<p>String 객체는 생성자 함수 객체로, new 연산자와 함께 호출하면 String 인스턴스를 생성한다.
String 생성자 함수에 인수를 전달하지 않고 new 연산자와 함께 호출할 경우 <code>[[StringData]]</code> 내부 슬롯에 빈 문자열을 할당한 String 래퍼 객체를 생성한다.</p>
<pre><code>// 값을 할당하지 않은 경우
const strObj = new String();
console.log(strObj); // String {length: 0, [[PrimitiveValue]]: &quot;&quot;}</code></pre><pre><code>// 값을 할당한 경우
const strObj = new String(`Lee`);
console.log(strObj); String {0:&quot;L&quot;, 1:&quot;e&quot;, 2:&quot;e&quot;, length: 3, [[PrimitiveValue]]: &quot;Lee&quot;}</code></pre><p>String 래퍼 객체는 배열과 마찬가지로 length 프로퍼티와 인덱스를 가지는 유사 배열 객체이면서 이터러블이다. 따라서 배열과 유사하게 인덱스를 사용하여 각 문자에 접근할 수 있다.</p>
<pre><code>console.log(strObj[0]); // &#39;L&#39;</code></pre><p>단, 문자열은 원시값이므로 변경할 수 없으며, 변경을 시도하더라도 에러가 발생하지 않는다.</p>
<pre><code>strObj[0] = `S`;
console.log(strObj); // &#39;Lee&#39;</code></pre><p>String 생성자 함수의 인수로 숫자가 아닌 값을 전달하면 인수를 숫자로 강제 변환한 후 할당한다.</p>
<pre><code>let strObj = new String(123);
console.log(strObj); // String {0:&quot;1&quot;, 1:&quot;2&quot;, 2:&quot;3&quot;, length: 3, [[PrimitiveValue]]: &quot;123&quot;}

strObj = new String(null);
console.log(strObj); // String {0: &quot;n&quot;, 1: &quot;u&quot;, 2: &quot;l&quot;, : &quot;l&quot;, length: 4, [[PrimitiveValue]]: &quot;null&quot;}</code></pre><p>String 연산자를 사용하지 않고, String 생성자 함수를 호출하면 String 인스턴스가 아닌 문자열을 반환한다. 이를 이용하여 명시적으로 타입을 변환하기도 한다.</p>
<pre><code>String(1); // &quot;1&quot;
String(NaN); // &quot;NaN&quot;
String(Infinity); // &quot;Infinity&quot;
String(true); // &quot;true&quot;
String(false); // &quot;false&quot;</code></pre><h2 id="string-프로퍼티">String 프로퍼티</h2>
<h3 id="length-프로퍼티">Length 프로퍼티</h3>
<pre><code>`Hello`.length; // 5</code></pre><h2 id="string-메서드">String 메서드</h2>
<p>String 객체에는 원본 String 래퍼 객체(String 메서드를 호출한 String 래퍼 객체)를 직접 변경하는 메서드는 존재하지 않는다. 즉, String 객체의 메서드는 언제나 새로운 문자열을 반환한다. 문자열은 변경 불가능(Immutable)한 원시값이기 때문에 String 래퍼 객체도 읽기 전용 객체로 제공된다.</p>
<h3 id="stringprototypeindexof-메서드">String.prototype.indexOf 메서드</h3>
<p>해당 메서드는 대상 문자열에서 인수로 전달받은 문자열을 검색하여 첫 번째 인덱스를 반환한다.
검색에 실패하면 -1을 반환한다.</p>
<pre><code>const str = `Hello World`;

str.indexOf(`l`); // 2
str.indexOf(`or`); // 7
str.indexOf(`x`); // -1</code></pre><p>2번째 인수로 검색을 시작할 인덱스를 전달할 수 있다.</p>
<pre><code>// 첫 번째 `o` 를 건너띄고 두 번째 `o` 의 인덱스를 검색한다.
str.indexOf(`o`, 5); // 7</code></pre><p>특정 문자열이 존재하는지 확인할 때 다음과 같이 사용하면 좋으나, ES6 에서 도입된 includes 메서드를 사용하면 가독성을 더 높일 수 있다.</p>
<pre><code>// indexOf 를 사용할 경우
str.index(`Hello`) !== -1

// includes 를 사용할 경우
str.includes(`Hello`)</code></pre><h3 id="stringprototypesearch-메서드">String.prototype.search 메서드</h3>
<p>해당 메서드는 대상 문자열에서 인수로 전달받은 정규표현식과 매치하는 문자열을 검색하여 일치하는 문자열의 인덱스를 반환한다. 
검색에 실패하면 -1을 반환한다.</p>
<pre><code>const str = `Hello World`

str.search(/o/); // 4
str.search(/x/); // -1</code></pre><h3 id="stringprototypeincludes-메서드">String.prototype.includes 메서드</h3>
<p>해당 메서드는 대상 문자열에 인수로 전달받은 문자열이 포함되어 있는지를 확인하여 true/false 로 반환한다.
2번째 인수로 검색을 시작할 인덱스를 전달할 수 있다.</p>
<pre><code>const str = `Hello World`;

str.includes(`Hello`); // true
str.includes(`x`); // false

str.includes(`l`, 3); // true
str.includes(`l`, 10); // false</code></pre><h3 id="stringprototypestartswith">String.prototype.startsWith</h3>
<p>ES6 에서 도입된 메서드로, 대상 문자열이 인수로 전달받은 문자열로 시작하는지 확인하여 그 결과를 true/false 로 반환한다.</p>
<pre><code>const str = `Hello World`;

str.startsWith(`He`); // true
str.startsWith(`x`); // false</code></pre><pre><code>// 문자열 str의 인덱스 5부터 인수로 전달한 ` `로 시작하는지 확인한다.
str.startsWith(` `, 5); // true</code></pre><h3 id="stringprototypeendswith">String.prototype.endsWith</h3>
<p>ES6 에서 도입된 메서드로, 대상 문자열이 인수로 전달받은 문자열로 끝나는지 확인하여 그 결과를 true/false 로 반환한다.</p>
<pre><code>const str = `Hello World`;

str.endsWith(`ld`); // true
str.endsWith(`x`); // false</code></pre><pre><code>// 문자열 str의 처음부터 인덱스 5까지 인수로 전달한 ` `로 끝나는지 확인한다.
str.endsWith(&#39;lo&#39;, 5); // -&gt; true</code></pre><h3 id="stringprototypecharat">String.prototype.charAt</h3>
<p>해당 메서드는 대상 문자열에서 인수로 전달받은 인덱스에 위치한 문자를 검색하여 반환한다.
문자열의 범위를 벗어난 인덱스를 호출할 경우 빈 문자열을 반환한다.</p>
<pre><code>const str = `Hello`;

str.charAt(0); // &quot;H&quot;
str.charAt(5); // &quot;&quot;</code></pre><h3 id="stringprototypesubstring">String.prototype.substring</h3>
<p>해당 메서드는 대상 문자열에서 첫 번째 인수로 전달받은 인덱스에 위치하는 문자부터 두 번째 인수로 전달받은 인덱스에 위치하는 문자의 바로 이전까지의 부분 문자열을 반환한다.</p>
<pre><code>const str = `Hello World`;

// 인덱스 1부터 인덱스 4 이전(포함X) 까지의 부분 문자열을 반환한다.
str.substring(1,4); // &quot;ell&quot;</code></pre><p>indexOf 메서드와 함께 사용하면 특정 문자열을 기준으로 앞뒤에 위치한 문자열을 얻을 수 있다.</p>
<pre><code>const str = `Hello World`;

// 스페이스` `를 기준으로 앞의 문자열을 얻는다.
str.substring(0, str.indexOf(` `)); // &quot;Hello&quot;

// 스페이스` `를 기준으로 뒤에 있는 문자열을 얻는다.
str.substring(str.indexOf(` `)+1, str.length); // &quot;World&quot;</code></pre><h3 id="stringprototypeslice-메서드">String.prototype.slice 메서드</h3>
<p>해당 메서드는 substring 메서드와 동일하게 동작한다.
인수를 음수로 전달할 경우 대상 문자열의 가장 뒤에서부터 시작하여 인수의 수만큼 문자열을 잘라내어 반환한다.</p>
<pre><code>const str = `Hello World`;

str.slice(0, 5); // &quot;Hello&quot;
str.slice(-5); // &quot;World&quot;</code></pre><h3 id="stringprototypetouppercase-메서드">String.prototype.toUpperCase 메서드</h3>
<p>해당 메서드는 대상 문자열을 모두 대문자로 변환환 문자열을 반환한다.</p>
<pre><code>const str = `Hello World`;

str.toUpperCase(); // &quot;HELLO WORLD&quot;</code></pre><h3 id="stringprototypetolowercase-메서드">String.prototype.toLowerCase 메서드</h3>
<p>해당 메서드는 대상 문자열을 모두 소문자로 변환한 문자열을 반환한다.</p>
<pre><code>const str = `Hello World`;

str.toLowerCase(); // `hello world`</code></pre><h3 id="stringprototypetrim-메서드">String.prototype.trim 메서드</h3>
<p>해당 메서드는 대상 문자열의 앞뒤 공백이 존재할 경우 제거한 문자열을 반환한다.</p>
<pre><code>const str = ` Hello World `;

str.trim(); // &quot;Hello World&quot;</code></pre><h3 id="stringprototyperepeat-메서드">String.prototype.repeat 메서드</h3>
<p>해당 메서드는 대상 문자열을 인수로 전달받은 정수만큼 반복해서 연결한 새로운 문자열을 반환한다.
인수가 0이면 빈 문자열을 반환하고, 인수를 생략하면 기본값으로 0이 설정된다.
음수를 전달할 경우 에러가 발생한다.</p>
<pre><code>const str = `abc`;

str.repeat(); // &quot;&quot;
str.repeat(2); // &quot;abcabc&quot;
str.repeat(-1); // Uncaught RangeError: Invalid count value: -1</code></pre><h3 id="stringprototypereplace-메서드">String.prototype.replace 메서드</h3>
<p>해당 메서드는 대상 문자열에서 첫 번째 인수로 전달받은 문자열 또는 정규표현식을 검색하여 두 번째 인수로 전달한 문자열로 치환한 문자열을 반환한다.
검색된 문자열이 여러 개 존재할 경우 첫 번째 검색된 문자열만 치환한다.</p>
<pre><code>const str = `Hello World`;

str.replace(`world`, `Lee`); // &quot;Hello Lee&quot;
str.replace(`l`, `x`); // &quot;Hexlo World&quot;</code></pre><h3 id="stringprototypesplit-메서드">String.prototype.split 메서드</h3>
<p>해당 메서드는 대상 문자열에서 첫 번째 인수로 전달한 문자열 또는 정규 표현식을 검색하여 문자열을 구분한 후 분리된 문자열로 이뤄진 배열을 반환한다.
인수로 빈 문자열을 전달할 경우 모든 문자를 분리하고, 인수를 생략할 경우 문자열 전체를 단일 요소로 하는 배열을 반환한다.
두 번째 인수로 반환되는 배열의 길이를 지정할 수 있다.</p>
<pre><code>const str = `How are you doing?`;

str.split(` `); // [&quot;How&quot;, &quot;are&quot;, &quot;you&quot;, &quot;doing?&quot;]
str.split(` `, 2); // [&quot;How&quot;, &quot;are&quot;]</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[ 알뜰신잡 ] NginX 가 무엇인가?]]></title>
            <link>https://velog.io/@space_developher/%EC%95%8C%EB%9C%B0%EC%8B%A0%EC%9E%A1-NginX-%EA%B0%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80</link>
            <guid>https://velog.io/@space_developher/%EC%95%8C%EB%9C%B0%EC%8B%A0%EC%9E%A1-NginX-%EA%B0%80-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80</guid>
            <pubDate>Sat, 26 Nov 2022 01:47:02 GMT</pubDate>
            <description><![CDATA[<h1 id="apache-와-nginx-의-차이점">Apache 와 NginX 의 차이점</h1>
<h2 id="apache-서버">Apache 서버</h2>
<h3 id="1-커넥션-생성">1. 커넥션 생성</h3>
<p><code>Apache</code> 서버는 요청이 들어오면 커넥션을 형성하기 위해 프로세스를 생성하여, 새로운 클라이언트의 요청이 들어올 때마다 새로운 프로세스를 생성한다.</p>
<h3 id="2-prefork-방식-채택">2. Prefork 방식 채택</h3>
<p>프로세스를 만드는 작업은 시간이 많이 소비되는 작업이기 때문에 요청이 들어오기 전에 미리 프로세스를 만들어 두는 <code>PreFork</code> 방식을 채택하여, 새로운 클라이언트 요청이 들어오면 미리 만들어 놓은 프로세스를 사용하고, 만약 프로세스가 모두 할당되었다면, 추가로 새로운 프로세스를 만들었다.</p>
<p>이러한 구조로 인해 개발하기 쉬운 장점이 있고, 다양한 모듈을 만들어서 서버에 적용하여 <code>확장성</code> 이 좋다.</p>
<h3 id="3-단점">3. 단점</h3>
<p>클라이언트에서 서버로 동시에 연결되는 커넥션이 많아졌을 때 더이상 <code>커넥션이 연결되지 않는 현상</code> 이 발생하였다. (<code>C10K 문제</code> 라고 한다.)</p>
<h4 id="-c10k-문제란">** C10K 문제란?</h4>
<p><code>서버의 초당 요청 처리수</code> 는 서버가 1초당 얼마나 많은 요청을 처리할 수 있는지를 나타내는 지표이고, <code>동시에 연결된 커넥션 수</code> 는 요청을 처리하기 위해 서버가 클라이언트와 연결된 커넥션 수를 나타내는 지표이다.</p>
<p>한 클라이언트는 하나의 커넥션을 통해서 여러개의 요청을 보낼 수 있고, 하나의 커넥션은 긴 시간동안 유지될 수 있기 때문에 두 지표는 같다고 볼 수 없다.</p>
<p>이 당시에는 각 요청마다 매번 커넥션을 만들기엔 비효율적이었고, 따라서 이미 만들어진 커넥션이 있다면 재활용하자는 생각을 하게된다.
HTTP 프로토콜의 헤더 중 <code>Keep-Alive</code> 가 존재하는데, 이 헤더에 적힌 시간만큼 커넥션을 유지한다.</p>
<p>이렇게 커넥션이 길게 유지되는 상태에서 <code>커넥션 수</code>가 <code>1만단위</code> 로 넘어가는 순간 서버는 새로운 커넥션을 생성하지 못하는 <code>문제</code> 가 발생한다.</p>
<p>하드웨어 문제가 아닌 Apache 서버의 <code>구조적 문제</code> 로 발생하였다.
동시에 처리하는 프로세스가 많아지면 서버의 <code>메모리 부족</code> 으로 이어지며, Apache 의 확장성은 프로세스가 차지하는 <code>리소스 양</code> 을 늘렸으며, 많은 커넥션의 요청이 들어오기 시작하면 CPU 코어가 프로세스를 변경하는 컨텍스트 스위치의 양이 증가하며 <code>CPU의 부하</code> 가 걸리게되었다.</p>
<p>쉽게 이야기해서 Apache 의 구조는 수많은 커넥션을 감당하기에는 부적합한 구조였다.</p>
<p>이러한 문제를 해결하기 위해 생긴 것이 <code>Nginx</code> 서버이다.</p>
<h2 id="nginx">Nginx</h2>
<h3 id="1-등장-이유">1. 등장 이유</h3>
<p>최초의 <code>Nginx</code> 는 <code>Apache</code> 서버와 함께 사용하기 위해 만들어졌다.
Apache 서버가 감당해야했던 수 많은 커넥션을 Nginx 가 앞단에서 대신해서 유지하도록 하였다.
Nginx 는 그 자체가 <code>웹 서버</code>이기 때문에 <code>정적파일</code> 에 대한 요청을 스스로 <code>해결</code> 할 수 있다.
Apache 는 클라이언트가 <code>동적파일</code> 에 대한 요청을 받았을 때만 <code>커넥션</code> 을 형성한다.
Apahce 서버의 리소스를 커넥션 유지에 사용하지 않고, 개발자가 원하는 <code>로직 처리</code> 를 위해 사용하는 것이다.</p>
<h3 id="2-동시-커넥션이-가능한-이유">2. 동시 커넥션이 가능한 이유</h3>
<p>만들어지는 프로세스의 수의 차이에 있다.
<code>Master Process</code> 는 설정 파일을 읽고 해당 내용으로 <code>Worker Process</code> 를 생성하는 프로세스이다.
<code>Worker Process</code> 가 실제로 작업을 하는 프로세스인데, 생성될 때 각자 지정된 <code>Listen Socket</code> 을 지정받는다.
해당 Socket 에 새로운 클라이언트로부터 요청이 들어오면 커넥션을 형성하고 요청을 처리한다. 해당 커넥션은 정해진 <code>Keep-Alive</code> 기간만큼 유지된지만, <code>Worker Process</code> 는 해당 커넥션의 아무런 요청이 없으면 새로운 커넥션을 형성하거나 이미 만들어진 다른 커넥션으로부터 요청을 처리한다.</p>
<p>Nginx 에서는 이러한 커넥션 형성, 커넥션 제거, 새로운 요청을 처리하는 것을 <code>이벤트</code> 라 한다.</p>
<p>이러한 <code>이벤트</code> 들은 OS 커널이 <code>큐 형식</code> 으로 <code>Worker Process</code> 에게 전달되며, 처리될 때까지 <code>비동기 방식</code> 으로 대기한다.
<code>Worker Process</code> 는 하나의 쓰레드로 이벤트를 꺼내서 처리한다.
이러한 구조는 <code>Worker Process</code> 가 계속해서 일을 처리한다는 장점이 있다. (Apache 서버는 요청이 없다면 방치되기 때문에 서버 리소스를 훨씬 효율적으로 사용할 수 있게 된다.)</p>
<p>단, 이러한 이벤트 요청 중 하나가 <code>시간이 오래 소요되는 작업</code> 일 경우에는 요청이 처리되는 시간동안 블로킹 될 것으로 예상되지만, Nginx 는 이러한 상황을 방지하기 위해 시간이 오래 소요되는 작업들을 수행하는 <code>쓰레드 풀</code> 을 만들어서 그곳에서 <code>수행</code> 하도록 한다.</p>
<p><code>Worker Process</code> 가 처리 중 시간이 오래 소요될 것으로 판단하면, <code>쓰레드 풀</code> 에 해당 이벤트를 위임하고, 다른 이벤트를 처리한다.</p>
<p>이러한 <code>Worker Process</code> 는 일반적으로 <code>CPU 의 수</code> 만큼 생성한다. 이러면 코어가 담당하는 프로세스의 수를 변경하는 과정을 줄일 수 있다. CPU 가 프로세스를 바꾸는 부가적인 일(컨텍스트 스위칭)을 하지 않아도 되기 때문에 수행능력이 향상된다.</p>
<p>이를 Nginx 의 이벤트 기반 구조라고 한다.</p>
<h3 id="3-단점-1">3. 단점</h3>
<p>개발자가 기능추가를 시도했다가 돌아가는 <code>Worker Process</code> 를 종료하는 상황이 생길 수 있다.
해당 <code>Worker Process</code> 가 관리하고 있던 커넥션과 요청을 더 이상 처리할 수 없게 된다.
따라서, Nginx 는 개발자가 직접 모듈을 만들기 까다롭다.</p>
<h3 id="4-장점">4. 장점</h3>
<ol>
<li>동시 커넥션 양 최소 10배 증가(일반적으로 100 ~ 1000배 증가)</li>
<li>동일한 커넥션 수일 때 속도 2배 향상</li>
</ol>
<ul>
<li>프로세스를 적게 만들다보니 가볍다.</li>
</ul>
<ol start="3">
<li>동적 설정 변경</li>
</ol>
<ul>
<li>프로세스를 적게 만들기 때문에 설정파일을 변경하고 적용하면 <code>Master Process</code> 는 설정에 맞는 <code>Worker Process</code> 를 별도로 생성하고, 기존의 <code>Worker Process</code> 는 새로운 커넥션을 형성하지 않도록 차단한다. 시간이 지나 기존의 <code>Worker Process</code> 들의 이벤트가 종료되면 해당 프로세스를 종료한다.</li>
<li>이러한 동적 변경은 여러 동시 커넥션을 관리하던 중 뒷 단의 서버가 추가되는 상황이 발생할 수 있고, 이때 Nginx 는 <code>로드밸런서</code> 의 역할을 담당하게 된다. 요청을 여러 서버로 분산하는 작업을 수행한다. 동적으로 설정파일을 변경할 수 있기 때문에 가능한 방식이다.</li>
</ul>
<h3 id="5-추가적인-기능">5. 추가적인 기능</h3>
<h4 id="ssl-터미네이션">SSL 터미네이션</h4>
<p>Nginx 가 클라이언트와는 HTTPS 로 통신하고, 서버와는 HTTP 로 통신하는 것을 의미한다.
이러한 구조로 서버가 복호화하는 과정을 하지 않도록 할 수 있다.
서버가 비즈니스 로직을 처리할 수 있도록 리소스를 줄여준다.</p>
<p>일반적으로 Nginx 와 서버는 같은 네트워크에 있는 경우가 많기 때문에 http 통신을 해도 보안적 위험이 비교적 적다.</p>
<h4 id="캐싱">캐싱</h4>
<p>HTTP 프로토콜을 통해 전달하는 컨텐츠를 캐싱할 수 있다. 캐싱을 하는 경우에는 클라이언트와 가깝게 배치한다. 한번 서버로 부터 받은 응답을 스스로 보관하고 클라이언트에 전달한다.</p>
<h4 id="etc">ETC</h4>
<p><code>HSTS</code>, <code>CORS 처리</code>, <code>TCP/UDP 커넥션 부하 분산</code>, <code>HTTP/2</code></p>
<h2 id="nginx-설정-템플릿">Nginx 설정 템플릿</h2>
<p><a href="https://github.com/h5bp/server-configs-nginx">https://github.com/h5bp/server-configs-nginx</a></p>
<h1 id="참고">참고</h1>
<p><a href="https://www.youtube.com/watch?v=6FAwAXXj5N0">우아한 테코톡</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ 프론트엔드의 기본 소양] (1) 인터넷 ]]></title>
            <link>https://velog.io/@space_developher/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C%EC%9D%98-%EA%B8%B0%EB%B3%B8-%EC%86%8C%EC%96%91-1-%EC%9D%B8%ED%84%B0%EB%84%B7</link>
            <guid>https://velog.io/@space_developher/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C%EC%9D%98-%EA%B8%B0%EB%B3%B8-%EC%86%8C%EC%96%91-1-%EC%9D%B8%ED%84%B0%EB%84%B7</guid>
            <pubDate>Sat, 26 Nov 2022 00:53:25 GMT</pubDate>
            <description><![CDATA[<h1 id="1-인터넷은-어떻게-동작하는가">1. 인터넷은 어떻게 동작하는가?</h1>
<h1 id="참고">참고</h1>
<p><a href="https://developer.mozilla.org/ko/docs/Learn/Common_questions/How_does_the_Internet_work">https://developer.mozilla.org/ko/docs/Learn/Common_questions/How_does_the_Internet_work</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ 프론트엔드의 기본 소양 ] (1) 개발자 로드맵]]></title>
            <link>https://velog.io/@space_developher/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C%EC%9D%98-%EA%B8%B0%EB%B3%B8-%EC%86%8C%EC%96%91-1-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EB%A1%9C%EB%93%9C%EB%A7%B5</link>
            <guid>https://velog.io/@space_developher/%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C%EC%9D%98-%EA%B8%B0%EB%B3%B8-%EC%86%8C%EC%96%91-1-%EA%B0%9C%EB%B0%9C%EC%9E%90-%EB%A1%9C%EB%93%9C%EB%A7%B5</guid>
            <pubDate>Fri, 25 Nov 2022 16:00:11 GMT</pubDate>
            <description><![CDATA[<h2 id="개발자-로드맵">개발자 로드맵</h2>
<p><a href="roadmap.sh">roadmap.sh</a> 는 개발자의 공부 방향에 대해 로드맵을 만들어서 공개하였다.
다양한 로드맵이 존재하는데, 포괄적인 프론트엔드 웹 개발 로드맵부터, 조금 더 세부적인 Javascript, React, Vue 등의 로드맵까지 존재한다.
프레임워크(리액트의 경우는 라이브러리로 봐야하지만)와는 별개로 프론트엔드 개발자라면 <code>웹 개발</code> 을 필수적으로 알아야 하므로, <a href="https://roadmap.sh/frontend">프론트엔드 로드맵</a> 을 중점으로 보도록 하겠다.</p>
<p>현재 프론트엔드 로드맵(이하 로드맵)은 2022년 버전까지 나와있으며, 2023년 버전이 되어도 크게 변화되는 부분은 없을 것이다.
다만, 최근 등장한 기술 스택들로 인해 내용이 추가될 것으로 예상되므로, 이 부분도 추가적으로 공부를 해야할 것으로 판단된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ 프론트엔드의 기본 소양 ] (0) 시작]]></title>
            <link>https://velog.io/@space_developher/0-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C%EC%9D%98-%EA%B8%B0%EB%B3%B8-%EC%86%8C%EC%96%91-%EB%93%A4%EC%96%B4%EA%B0%80%EB%A9%B0-</link>
            <guid>https://velog.io/@space_developher/0-%ED%94%84%EB%A1%A0%ED%8A%B8%EC%97%94%EB%93%9C%EC%9D%98-%EA%B8%B0%EB%B3%B8-%EC%86%8C%EC%96%91-%EB%93%A4%EC%96%B4%EA%B0%80%EB%A9%B0-</guid>
            <pubDate>Fri, 25 Nov 2022 15:38:27 GMT</pubDate>
            <description><![CDATA[<h1 id="들어가며">들어가며</h1>
<p>개발자로서 발돋음한지 어느덧 2년의 시간이 지났다.
처음 개발 공부를 시작했을 때 가졌던 욕심은 점차 안주와 나태로 변해진 것 같다.
2년간 가장 열심히 공부했을 때가 이직 준비했던 2개월간의 기간이었던 것을 생각하면 반성한다.
다시 초심으로 돌아가기 위해 다른 기술 블로그와 개인 페이지 등을 토대로 프론트엔드 개발자라면 반드시 알아야할 기본 소양부터 공부하고자 한다.</p>
<h1 id="공부-방향">공부 방향</h1>
<p>큰 줄기는 <a href="https://roadmap.sh/frontend">2022 프론트엔드 개발자 로드맵</a> 를 따르려고 한다.
추가적으로 <a href="https://techblog.woowahan.com/">우아한형제들:기술블로그</a>나 <a href="https://tech.kakao.com/blog/">카카오 테크</a>, <a href="https://d2.naver.com/home">네이버 D2</a> 등의 유니콘 기업들의 기술 블로그와 개인 블로그를 보면서 여러 견문을 넓히려고 한다.</p>
<h1 id="목표">목표</h1>
<ol>
<li>프론트엔드 개발자라면 반드시 알아야하는 기본 소양을 획득한다.</li>
<li>주니어 레벨에서 시니어 레벨로 스텝업 할 수 있는 기회를 마련한다.</li>
<li>다른 사람들에게도 설명할 수 있는 확실한 지식을 얻는다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ Javascript ] 자바스크립트 딥다이브 29장 Math]]></title>
            <link>https://velog.io/@space_developher/Javascript-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%94%A5%EB%8B%A4%EC%9D%B4%EB%B8%8C-29%EC%9E%A5-Math</link>
            <guid>https://velog.io/@space_developher/Javascript-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%94%A5%EB%8B%A4%EC%9D%B4%EB%B8%8C-29%EC%9E%A5-Math</guid>
            <pubDate>Thu, 24 Nov 2022 06:41:40 GMT</pubDate>
            <description><![CDATA[<p><code>Math</code> 는 표준 빌트인 객체로, 수학적인 상수와 함수를 위한 프로퍼티와 메서드를 제공한다.
<code>Math</code> 는 생성자 함수가 아니다. 따라서 정적 프로퍼티와 정적 메서드만 제공한다.</p>
<pre><code>new Math(123)
Uncaught TypeError: Math is not a constructor</code></pre><h3 id="mathpi">Math.PI</h3>
<p>해당 프로퍼티는 <code>PI 값</code> 을 반환한다.</p>
<pre><code>Math.PI; // 3.14159...</code></pre><h3 id="mathabs">Math.abs</h3>
<p>해당 메서드는 인수로 전달된 <code>숫자의 절대값</code> 을 반환한다. 절대값은 <code>0</code> 또는 <code>양수</code> 이다.</p>
<pre><code>Math.abs(-1);        // 1
Math.abs(&#39;-1&#39;);      // 1
Math.abs(&#39;&#39;);        // 0
Math.abs([]);        // 0
Math.abs(null);      // 0
Math.abs(undefined); // NaN
Math.abs({});        // NaN
Math.abs(&#39;string&#39;);  // NaN
Math.abs();          // NaN</code></pre><h3 id="mathround">Math.round</h3>
<p>해당 메서드는 인수로 전달된 숫자의 <code>소수점 이하</code> 를 <code>반올림한 정수</code> 를 반환한다.</p>
<pre><code>Math.round(1.4);  //  1
Math.round(1.6);  //  2
Math.round(-1.4); // -1
Math.round(-1.6); // -2
Math.round(1);    //  1
Math.round();     // NaN</code></pre><h3 id="mathceil">Math.ceil</h3>
<p>해당 메서드는 인수로 전달된 숫자의 <code>소수점 이하</code> 를 <code>올림한 정수</code> 를 반환한다.</p>
<pre><code>Math.ceil(1.4);  //  2
Math.ceil(1.6);  //  2
Math.ceil(-1.4); // -1
Math.ceil(-1.6); // -1
Math.ceil(1);    //  1
Math.ceil();     // NaN</code></pre><h3 id="mathfloor">Math.floor</h3>
<p>해당 메서드는 인수로 전달된 숫자의 <code>소수점 이하</code> 를 <code>내림한 정수</code> 를 반환한다. </p>
<pre><code>Math.floor(1.9);  //  1
Math.floor(9.1);  //  9
Math.floor(-1.9); // -2
Math.floor(-9.1); // -10
Math.floor(1);    //  1
Math.floor();     // NaN</code></pre><p><code>Math.ceil</code> 과 <code>Math.floor</code> 는 반대의 개념이다.</p>
<h3 id="mathsqrt">Math.sqrt</h3>
<p>해당 메서드는 인수로 전달된 숫자의 <code>제곱근</code> 을 반환한다.</p>
<pre><code>Math.sqrt(9);  // 3
Math.sqrt(-9); // NaN
Math.sqrt(2);  // 1.414213562373095
Math.sqrt(1);  // 1
Math.sqrt(0);  // 0
Math.sqrt();   // NaN</code></pre><h3 id="mathrandom">Math.random</h3>
<p>해당 메서드는 <code>임의의 난수</code> 를 반환한다. 반환값은 <code>0</code> 에서 <code>1</code> 미만의 실수다.
<code>0</code> 은 포함되지만, <code>1</code> 은 포함되지 않는다.</p>
<pre><code>Math.random(); // 0에서 1 미만의 임의의 난수(0.4480066088909287)</code></pre><p><code>random</code> 메서드와 <code>floor</code> 메서드를 사용하여 랜덤한 정수를 만들 수 있다.</p>
<pre><code>const random = Math.floor((Math.random() * 10) + 1);
console.log(random); // 3</code></pre><h3 id="mathpow">Math.pow</h3>
<p>해당 메서드는 <code>첫번째 인수</code>를 <code>밑</code>으로, <code>두 번째 인수</code>를 <code>지수</code>로 <code>거듭제곱</code> 한 결과를 반환한다.
지수의 값이 없을 경우엔 <code>NaN</code> 을 반환한다.</p>
<pre><code>Math.pow(2, 8);  // 256
Math.pow(2, -1); // 0.5
Math.pow(2);     // NaN</code></pre><h4 id="es7-에-도입된-지수-연산자">ES7 에 도입된 지수 연산자</h4>
<p><code>Math.pow</code> 와 동일한 기능을 하나 가독성 좋은 코드를 만들 수 있다.
<code>앞</code> 의 피연산자는 <code>밑</code> , <code>뒤</code> 의 피연산자는 <code>지수</code> 를 의미한다.</p>
<pre><code>2 ** 2; // 4
2 ** 2 ** 2; // 16</code></pre><h3 id="mathmax">Math.max</h3>
<p>해당 메서드는 전달받은 인수 중에서 <code>가장 큰 수</code> 를 반환한다.</p>
<pre><code>Math.max(1); // 1
Math.max(1, 2); // 2
Math.max(1, 2, 3); // 3

// 인수가 없는 경우
Math.max(); // -Infinity

// ES6 스프레드 문법
Math.max(...[1, 2, 3]); // 3</code></pre><h3 id="mathmin">Math.min</h3>
<p>해당 메서드는 전달받은 인수 중에서 <code>가장 작은 수</code> 를 반환한다.</p>
<pre><code>Math.min(1); // -&gt; 1
Math.min(1, 2); // -&gt; 1
Math.min(1, 2, 3); // -&gt; 1

// 인수가 없는 경우
Math.min(); // -&gt; Infinity

// ES6 스프레드 문법
Math.min(...[1, 2, 3]); // -&gt; 1</code></pre><p><code>Math.max</code> 와 <code>Max.min</code> 은 반대되는 개념이다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ Git ] 병합(Merge) 취소하기]]></title>
            <link>https://velog.io/@space_developher/Git-%EB%B3%91%ED%95%A9Merge-%EC%B7%A8%EC%86%8C%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@space_developher/Git-%EB%B3%91%ED%95%A9Merge-%EC%B7%A8%EC%86%8C%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 22 Nov 2022 08:15:14 GMT</pubDate>
            <description><![CDATA[<p>브랜치를 병합하던 중 병합을 취소하고 싶은 경우 사용한다.</p>
<p><code>rebase</code> 명령어와 유사하게 동작한다.</p>
<pre><code>git merge --abort</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[ JAVASCRIPT ] 자바스크립트 딥다이브 - 28장 Number]]></title>
            <link>https://velog.io/@space_developher/JAVASCRIPT-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%94%A5%EB%8B%A4%EC%9D%B4%EB%B8%8C-28%EC%9E%A5-Number</link>
            <guid>https://velog.io/@space_developher/JAVASCRIPT-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%94%A5%EB%8B%A4%EC%9D%B4%EB%B8%8C-28%EC%9E%A5-Number</guid>
            <pubDate>Mon, 31 Oct 2022 15:16:53 GMT</pubDate>
            <description><![CDATA[<p>표준 빌트인 객체인 <code>Number</code> 는 원시 타입인 숫자를 다룰 때 유용한 프로퍼티와 메서드를 제공한다.</p>
<h2 id="1-number-생성자-함수">1. Number 생성자 함수</h2>
<p>Number 객체는 생성자 함수 개체다. 따라서 new 연산자와 함께 호출하여 Number 인스턴스를 생성 할 수 있다.</p>
<pre><code>const numObj = new Number();

console.log(numObj);</code></pre><p>Number 생성자 함수에 인수를 전달하지 않고 new 연산자와 함께 호출하면 [[NumberData]] 내부 슬롯에 0을 할당한 Number 래퍼 객체를 생성한다.</p>
<pre><code>const numObj = new Number();
console.log(numObj); // Number { [[PrimitiveValue]]: 0 }

const numObj = new Number(10);
console.log(numObj); // Number { [[PrimitiveValue]]: 10 }</code></pre><p>Number 생성자 함수의 인수로 숫자가 아닌 값을 전달하면 인수를 숫자로 강제 변환한다.</p>
<pre><code>const numbObj2 = new Number(`10`);
console.log(numObj); // Number { [[PrimitiveValue]]: 10 }

const numbObj3 = new Number(`Hi`);
console.log(numObj); // Number { [[PrimitiveValue]]: NaN }</code></pre><p>new 연산자를 사용하지 않고 Number 생성자 함수를 호출하면 Number 인스턴스가 아닌 숫자를 반환한다. 이를 이용해서 명시적으로 타입을 변환하기도 한다.</p>
<pre><code>// 문자열 타입 =&gt; 숫자 타입
Number(&#39;0&#39;);     // -&gt; 0
Number(&#39;-1&#39;);    // -&gt; -1
Number(&#39;10.53&#39;); // -&gt; 10.53

// 불리언 타입 =&gt; 숫자 타입
Number(true);  // -&gt; 1
Number(false); // -&gt; 0</code></pre><h2 id="2-number-프로퍼티">2. Number 프로퍼티</h2>
<h3 id="1-numberepsilon">1) Number.EPSILON</h3>
<p><code>Number.EPSILION</code> 은 1과 1보다 큰 숫자 중에서 가장 작은 숫자와의 차이가 같다.</p>
<pre><code>0.1 + 0.2 // 0.3000000000000004
0.1 + 0.2 === 0.3 // false</code></pre><p>2진법으로 변환했을 때 무한소수가 되어 미세한 오차가 발생한다.</p>
<pre><code>function isEqual(a, b){
    return Math.abs(a-b) &lt; Number.EPSILON;
}</code></pre><p>위와 같이 사용한다.</p>
<h3 id="2-numbermax_value">2) Number.MAX_VALUE</h3>
<p>자바스크립트에서 표현할 수 있는 가장 큰 양수 값이다. <code>Number.MAX_VALUE</code> 보다 큰 숫자는 <code>Infinity</code> 뿐이다.</p>
<h3 id="3-numbermin_value">3) Number.MIN_VALUE</h3>
<p>자바스크립트에서 표현할 수 있는 가장 작은 양수 값이다. <code>Number.MIN_VALUE</code> 보다 작은 숫자는 0이다.</p>
<h3 id="4-numbermax_safe_integer">4) Number.MAX_SAFE_INTEGER</h3>
<p>자바스크립트에서 안전하게 표현할 수 있는 가장 작은 정수 값이다.</p>
<h3 id="5-numbermin_safe_integer">5) Number.MIN_SAFE_INTEGER</h3>
<p>자바스크립트에서 안전하게 표현할 수 있는 가장 작은 정수 값이다.</p>
<h3 id="6-numberpositive_infinity">6) Number.POSITIVE_INFINITY</h3>
<p>양의 무한대를 나타내는 숫자 값 Infinity와 같다.</p>
<h3 id="7-numbernegative_infinity">7) Number.NEGATIVE_INFINITY</h3>
<p>음의 무한대를 나타내는 숫자 값 -Infinity와 같다.</p>
<h3 id="8-numbernan">8) Number.NaN</h3>
<p>숫자가 아님을 나타내는 숫자 값이다. Number.NaN은 window.NaN과 같다.</p>
<h2 id="3-number-메서드">3. Number 메서드</h2>
<h3 id="1-numberisfinite">1) Number.isFinite</h3>
<p>인수로 전달된 숫자 값이 정상적인 유한수인지 검사하여 그 결과를 <code>Boolean</code> 값으로 반환한다.</p>
<pre><code>// 인수가 정상적인 유한수이면 true를 반환한다.
Number.inFinite(0)                // true
Number.inFinite(Number.MAX_VALUE) // true
Number.inFinite(Number.MIN_VALUE) // true

Number.inFinite(Infinity)  // false
Number.inFinite(-Infinity) // false</code></pre><h3 id="2-numberisinteger">2) Number.isInteger</h3>
<p>인수로 전달된 숫자 값이 정수인지 검사하여 그 결과를 <code>Boolean</code> 값으로 반환한다.</p>
<pre><code>// 인수가 정수이면 true를 반환한다.
Number.isInteger(0)    // true
Number.isInteger(11)   // true
Number.isInteger(-11)  // true

// 0.5는 정수가 아니다.
Number.isInteger(0.5)   // false

// &#39;123&#39;을 숫자로 암묵적 타입 변환하지 않는다.
Number.isInteger(&#39;123&#39;) // false

// false를 숫자로 암묵적 타입 변환하지 않는다.
Number.isInteger(false) // false

// Infinity/-Infinity는 정수가 아니다.
Number.isInteger(Infinity)  // false
Number.isInteger(-Infinity) // false</code></pre><h3 id="3-numberisnan">3) Number.isNaN</h3>
<p>인수로 전달된 숫자값이 NaN인지 검사하여 그 결과를 <code>Boolean</code> 값으로 반환한다.</p>
<pre><code>// 인수가 NaN이면 true를 반환한다.
Number.isNaN(NaN); // true</code></pre><p>Number.isNaN 메서드는 빌트인 전역 함수 isNaN과 차이가 있다. 빌트인 전역 함수 isNaN은 전달받은 인수를 숫자로 암묵적 타입 변환하고 검사를 수행하지만  Number.isNaN 메서드는 변환하지 않는다.</p>
<pre><code>// Number.isNaN은 인수를 숫자로 암묵적 타입 변환하지 않는다.
Number.isNaN(undefined); // false

// isFinite는 인수를 숫자로 암묵적 타입 변환한다. undefined는 NaN으로 암묵적 타입 변환된다.
isNaN(undefined); // true</code></pre><h3 id="4-numberissafeinteger">4) Number.isSafeInteger</h3>
<p>인수로 전달된 숫자값이 안전한 정수인지 검사하여 그 결과를 <code>Boolean</code> 값으로 반환한다.여기서 안전한 정수 값은 -(253-1)과 253-1 사이의 정수 값이다.</p>
<h3 id="5-numberprototypetoexponential">5) Number.prototype.toExponential</h3>
<p>숫자를 지수 표기법으로 변환하여 문자열로 반환한다.</p>
<pre><code>(77.1234).toExponential(); // &quot;7.71234e+1&quot;</code></pre><h3 id="6-numberprototypetofixed">6) Number.prototype.toFixed</h3>
<p>숫자를 반올림하여 문자열로 반환한다.</p>
<h3 id="7-numberprototypetoprecision">7) Number.prototype.toPrecision</h3>
<p>인수로 전달받은 전체 자릿수까지 유효하도록 나머지 자릿수를 반올림하여 문자열로 반환한다.
인수를 생략하면 기본값이 0이 지정된다.</p>
<pre><code>// 소수점 이하 반올림. 인수를 생략하면 기본값 0이 지정된다.
(12345.6789).toFixed(); // -&gt; &quot;12346&quot;

// 소수점 이하 1자리수 유효, 나머지 반올림
(12345.6789).toFixed(1); // -&gt; &quot;12345.7&quot;

// 소수점 이하 2자리수 유효, 나머지 반올림
(12345.6789).toFixed(2); // -&gt; &quot;12345.68&quot;</code></pre><h3 id="8-numberprototypetostring">8) Number.prototype.toString</h3>
<p>숫자를 문자열로 변환하여 반환한다.</p>
<pre><code>// 인수를 생략하면 10진수 문자열을 반환한다.
(10).toString(); // &quot;10&quot;
// 2진수 문자열을 반환한다.

(16).toString(2); // &quot;10000&quot;
// 8진수 문자열을 반환한다.

(16).toString(8); // &quot;20&quot;
// 16진수 문자열을 반환한다.

(16).toString(16); // &quot;10&quot;

10.toString(); // Uncaught SyntaxError: Invalid or unexpected token</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[ VSCODE ] Unable to initialize Git Error]]></title>
            <link>https://velog.io/@space_developher/VSCODE-Unable-to-initialize-Git-Error</link>
            <guid>https://velog.io/@space_developher/VSCODE-Unable-to-initialize-Git-Error</guid>
            <pubDate>Wed, 26 Oct 2022 04:36:00 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가며">들어가며</h2>
<p><code>MAC OS Ventura 13.0</code> 으로 업데이트 하면서 VS CODE 내 문제가 발생했다.
<img src="https://velog.velcdn.com/images/space_developher/post/40e19b21-9d83-44e6-a9b9-7461509cc74b/image.png" alt=""></p>
<p>아무래도 MAC OS에 기본적으로 설치되어 있던 Git에 장애가 발생한 것 같다.
Git을 재설치하여 장애를 해결하도록 한다.</p>
<h2 id="해결-방법">해결 방법</h2>
<h3 id="1-git-설치">1) GIT 설치</h3>
<h4 id="a-home-brew-설치">a. Home Brew 설치</h4>
<p>터미널에 아래의 CLI 를 입력하여 설치한다.</p>
<pre><code>/bin/bash -c &quot;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)&quot;</code></pre><p><a href="https://brew.sh/index_ko">https://brew.sh/index_ko</a></p>
<h4 id="b-git-설치">b. Git 설치</h4>
<p>터미널에 아래의 CLI 를 입력하여 설치한다.</p>
<pre><code>brew install git</code></pre><p><a href="https://git-scm.com/download/mac">https://git-scm.com/download/mac</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ JAVASCRIPT ] 자바스크립트 딥다이브 - 24장 클로저]]></title>
            <link>https://velog.io/@space_developher/JAVASCRIPT-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%94%A5%EB%8B%A4%EC%9D%B4%EB%B8%8C-24%EC%9E%A5-%ED%81%B4%EB%A1%9C%EC%A0%80</link>
            <guid>https://velog.io/@space_developher/JAVASCRIPT-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%94%A5%EB%8B%A4%EC%9D%B4%EB%B8%8C-24%EC%9E%A5-%ED%81%B4%EB%A1%9C%EC%A0%80</guid>
            <pubDate>Tue, 25 Oct 2022 15:40:31 GMT</pubDate>
            <description><![CDATA[<h1 id="1-클로저란">1. 클로저란?</h1>
<p>클로저는 자바스크립트 고유의 개념이 아니다. 함수를 일급 객체를 취급하는 함수형 프로그래밍 언에서 사용되는 중요한 특성이다.</p>
<pre><code>const x = 1;

function outerFunc(){
    const x = 10;

    function innerFunc(){
        console.log(x); // 10
    }

    innerFunc();
}

outerFunc();</code></pre><p>outer 함수 내부에서 중첩 함수 innerFunc가 정의되고 호출되었다. 이때 중첩 함수 innerFunc의 상위 스코프는 외부 함수 outerFunc의 스코프이다. 따라서 중첩 함수 innerFunc 내부에서 자신을 포함하고 있는 외부 함수 outerFunc의 x 변수에 접근할 수 있다.</p>
<p>만약 innerFunc 함수가 outerFunc 함수의 내부에서 정의된 함수가 아니라면 innerFunc 함수를 outerFunc 함수의 내부에서 호출한다 하더라도 outerFunc 함수의 변수에 접근할 수 없다.</p>
<p>이 같은 현상이 발생하는 이유는 자바스크립트가 렉시컬 스코프를 따르는 프로그래밍 언어이기 때문이다.</p>
<h1 id="2-렉시컬-스코프">2. 렉시컬 스코프</h1>
<p>자바스크립트 엔진은 함수를 어디서 호출했는지가 아니라 함수를 어디에서 정의했는지에 따라 상위 스코프를 결정한다. 이를 렉시컬 스코프(정적 스코프)라고 한다.</p>
<pre><code>const x = 1;

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

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

foo(); // 1
bar(); // 1</code></pre><p>위의 예제를 보면 foo 함수와 bar 함수는 전역에서 정의된 함수이다. 함수의 상위 스코프는 함수를 어디서 정의했느냐에 따라 결정되므로 foo 함수와 bar 함수의 상위 스코프는 전역이다.
함수를 어디서 호출하는지는 함수의 상위 스코프 결정에 어떠한 영향도 주지 못한다. 즉, 함수의 상위 스코프는 함수를 정의한 위치에 의해 정적으로 결정되고 변하지 않는다.</p>
<p><code>실행 컨텍스트</code> 에서 스코프의 실체는 실행 컨텍스트의 렉시컬 환경이다. 이 렉시컬 환경은 자신의 <code>외부 렉시컬 환경에 대한 참조</code> 를 통해 상위 렉시컬 환경과 연결된다. 이것이 바로 스코프 체인이다.</p>
<p>따라서 함수의 상위 스코프를 결정한다는 것은 렉시컬 환경의 외부 렉시컬 환경에 대한 참조에 저장할 참조값을 결정한다는 것과 같다. 렉시컬 환경의 외부 렉시컬 환경에 대한 참조에 저장할 참조값이 바로 상위 렉시컬 환경에 대한 참조이며, 이것이 상위 스코프이기 때문이다. 이 개념을 반영해서 다시 한번 렉시컬 스코프를 정의해보면 다음과 같다.</p>
<p>렉시컬 환경의 외부 렉시컬 환경에 대한 참조에 저장할 참조값, 즉 상위 스코프에 대한 참조는 함수 정의가 평가되는 시점에 함수가 정의된 환경(위치)에 의해 결정된다. 이것이 바로 렉시컬 스코프다.</p>
<h1 id="3-함수-객체의-내부-슬롯-environment">3. 함수 객체의 내부 슬롯 [[Environment]]</h1>
<p>함수가 정의된 환경과 호출되는 환경은 다를 수 있다. 따라서 렉시컬 스코프가 가능하려면 함수는 자신이 호출되는 환경과는 상관없이 자신이 정의된 환경, 즉 상위 스코프를 기억해야 한다. 이를 위해 함수는 자신의 내부 슬롯 [[Environment]]에 자신이 정의된 환경, 즉 상위 스코프의 참조를 저장한다.</p>
<p>따라서 함수 객체에 내부 슬롯[[Environment]]에 저장된 현재 실행 중인 실행 컨텍스트의 렉시컬 환경의 참조가 바로 상위 스코프이다. 또한 자신이 호출되었을 때 생성될 함수 렉시컬 환경의 <code>외부 렉시컬 환경에 대한 참조</code> 에 저장될 참조값이다. 함수 객체는 내부 슬롯 [[Environment]]에 저장한 렉시컬 환경의 참조, 즉 상위 스코프로 자신이 존재하는 한 기억한다.</p>
<h2 id="4-클로저와-렉시컬-환경">4. 클로저와 렉시컬 환경</h2>
<pre><code>const x = 1;


// 1
function outer(){
    const x = 10;
    const inner = function(){ console.log(x); }; // 2
    return inner;
}

// outer 함수를 호출하면 중첩 함수 inner를 반환한다.
// 그리고 outer 함수의 실행 컨텍스트는 실행 컨텍스트 스택에서 팝 되어 제거된다.
const innerFunc = outer(); // 3
innerFunc(); // 4 10</code></pre><p>위의 예제에서 outer 함수를 호출하면 outer 함수는 중첩 함수 inner를 반환하고 생명 주기를 마감한다. 즉, outer 함수의 실행이 종료되면 outer 함수의 실행 컨텍스트는 실행 컨텍스트 스택에서 제거된다. 이때 outer 함수의 지역 변수 x와 변수 값 10을 저장하고 있던 outer 함수의 실행 컨텍스트가 제거되었으므로 outer 함수의 지역 변수 x 또한 생명주기를 마감한다. 따라서 outer 함수의 지역 변수 x 는 더이상 유효하지 않게 되어 x 변수에 접근할 수 있는 방법은 달리 없어 보인다.</p>
<p>그러나 위 코드의 실행결과는 outer 함수의 지역 변수 x의 값인 10이다. 이미 생명 주기가 종료되어 실행 컨텍스트 스택에서 제거된 outer 함수의 지역 변수 x가 동작되고 있다.</p>
<p>이처럼 외부 함수보다 중첩 함수가 더 오래 유지되는 경우 중첩 함수는 이미 생명 주기가 종료한 외부 함수의 변수를 참조할 수 있다. 이러한 중첩 함수를 클로저라고 부른다.</p>
<blockquote>
<p>클로저는 함수와 그 함수가 선언된 렉시컬 환경과의 조합이다.</p>
</blockquote>
<p>자바스크립트의 모든 함수는 자신의 상위 스코프를 기억한다. 모든 함수가 기억하는 상위 스코프는 함수를 어디서 호출하든 상관없이 유지된다. 따라서 함수를 어디서 호출하든 상관없이 함수는 언제나 자신이 기억하는 상위 스코프의 식별자를 참조할 수 있으며 식별자에 바인딩된 값을 변경할 수도 있다.</p>
<p>위 예제에서 inner 함수는 자신이 평가될 때 자신이 정의된 위치에 의해 결정된 상위 스코프를 [[Environment]] 내부 슬롯에 저장한다. 이때 저장된 상위 스코프는 함수가 존재하는 한 유지된다.</p>
<p>위의 예제에서 outer 함수가 평가되어 함수 객체를 생성할 때(1) 현재 실행 중인 실행 컨텍스트의 렉시컬 환경, 즉 전역 렉시컬 환경을 outer 함수 객체의 [[Environment]] 내부 슬롯 스코프로서 저장한다.</p>
<p>outer 함수를 호출하면 outer 함수의 렉시컬 환경이 생성되고 앞서 outer 함수 객체의 [[Environment]] 내부 슬롯에 저장된 전역 렉시컬 환경을 outer 함수 렉시컬 환경의 <code>외부 렉시컬 환경에 대한 참조</code> 에 할당한다.</p>
<p>그리고 중첩 함수 inner가 평가된다.(2) 이때 중첩 함수 inner는 자신의 [[Environment]] 내부 슬롯에 현재 실행중인 실행 컨텍스트의 렉시컬 환경, 즉 outer 함수의 렉시컬 환경을 상위 스코프로서 저장한다.</p>
<p>outer 함수의 실행이 종료하면 inner 함수를 반환하면서 outer 함수의 생명주기가 종료된다.(3) 즉, outer 함수의 실행 컨텍스트가 실행 컨텍스트 스택에서 제거된다. 이때 outer 함수의 실행 컨텍스트는 실행 컨텍스트 스택에서 제거되지만 outer 함수의 렉시컬 환경까지 소멸하는 것은 아니다.</p>
<p>outer 함수의 렉시컬 환경은 inner 함수의 [[Environment]] 내부 슬롯에 의해 참조되고 있고 inner 함수는 전역 변수 innerFunc에 의해 참조되고 있으므로 가비지 컬렉션 대상이 아니기 때문이다.</p>
<p>outer 함수가 반환한 inner 함수를 호출(4)하면 inner 함수의 실행 컨텍스트가 생성되고 실행 컨텍스트 스택에 푸쉬된다. 그리고 렉시컬 환경의 외부 렉시컬 환경에 대한 참조는 inner 함수 객체의 [[Environment]] 내부 슬롯에 저장되어 있는 참조값이 할당된다.</p>
<p>중첩 함수 inner는 외부 함수 outer보다 더 오래 생존했다. 이때 외부 함수보다 더 오래 생존한 중첩 함수는 외부 함수의 생존 여부와 상관없이 자신의 ㅈ어의된 위치에 의해 결정된 상위 스코프를 기억한다. 이처럼 중첩 함수 inner의 내부에서는 상위 스코프를 참조할 수 있으므로 상위 스코프의 식별자를 참조할 수 있고 식별자의 값도 변경할 수도 있다.</p>
<p>클로저는 중첩 함수가 상위 스코프의 식별자를 참조하고 있고 중첩 함수가 외부 함수보다 더 오래 유지되는 경우에 한정하는 것이 일반적이다.</p>
<p>클로저에 의해 참조되는 상위 스코프의 변수를 자유 변수라고 부른다. 클로저란 <code>함수가 자유 변수에 의해 닫혀있다</code>라는 뜻이다.</p>
<p>이론적으로 클로저는 상위 스코프를 기억해야 하므로 불필요한 메모리 점유를 걱정할 수도 있다. 하지만 모던 자바스크립트 엔진은 클로저가 참조하고 있지 않는 식별자는 기억하지 않는다.</p>
<h2 id="5-클로저의-활용">5. 클로저의 활용</h2>
<p>클로저는 상태를 안전하게 변경하고 유지하기 위해 사용한다. 다시 말해 상태가 의도치 않게 변경되지 않도록 상태를 안전하게 은닉하고 특정 함수에게만 상태 변경을 허용하기 위해 사용한다.</p>
<pre><code>const counter = (function(){
    // 카운트 상태 변수
    let num = 0;

    // 클로저인 메서드를 갖는 객체를 반환한다.
    // 객체 리터럴은 스코프를 만들지 않는다.
    // 따라서 아래 메서드들의 상위 스코프는 즉시 실행 함수의 렉시컬 환경이다.
    return {
        // num: 0 - 프로퍼티는 public 하므로 은닉되지 않는다.
        increase(){
            return ++num;
        },
        decrease(){
            return num &gt;0 ? --num : 0;
        }
    }
})

console.log(counter.increase()); // 1
console.log(counter.increase()); // 2

console.log(counter.decrease()); // 1
console.log(counter.decrease()); // 0</code></pre><p>즉시 실행함수는 한 번만 실행되므로 increase 가 호출될 때마다 num 변수가 재차 초기화 될 일이 없다.
위의 예제의 increase, decrease 메서드의 상위 스코프는 increase, decrease 메서드가 평가되는 시점에 실행중인 실행 컨텍스트인 즉시 실행 함수 실행컨텍스트의 렉시컬 환경이다.</p>
<pre><code>// 함수를 인수로 전달받고 함수를 반환하는 고차 함수
// 이 함수는 카운트 상태를 유지하기 위한 자유 변수 counter를 기억하는 클로저를 반환한다.
function makeCounter(predicate) {
  // 카운트 상태를 유지하기 위한 자유 변수
  let counter = 0;

  // 클로저를 반환
  return function() {
    // 인수로 전달받은 보조 함수에 상태 변경을 위임한다.
    counter = predicate(counter);
    return counter;
  };
}

// 보조 함수
function increase(n) {
  return ++n;
}

// 보조 함수
function decrease(n) {
  return --n;
}

// 함수로 함수를 생성한다.
// makeCounter 함수는 보조 함수를 인수로 전달받아 함수를 반환한다.
const increaser = makeCounter(increase); // 1
console.log(increaser()); // 1
console.log(increaser()); // 2

// increaser 함수와는 별개로 독립된 렉시컬 환경을 갖기 때문에 카운터 상태가 연동하지 않는다.
const decreaser = makeCounter(decrease); // 2
console.log(decreaser()); // -1
console.log(decreaser()); // -2</code></pre><p>makeCounter 함수를 호출해 함수를 반환할 때 반환된 함수는 자신만의 독립된 렉시컬 환경을 갖는다.
전역 변수 increaser 와 decreaser 에 할당된 함수는 각각 자신만의 독립된 렉시컬 환경을 갖기 때문에 카운트를 유지하기 위한 자유 변수 counter 를 공유하지 않아 카운터의 증감이 연동되지 않는다.
따라서 독립된 카운터가 아니라 연동하여 증감이 가능한 카운터를 만들려면 렉시컬 환경을 만들려면 렉시컬 환경을 공유하는 클로저를 만들어야 한다.</p>
<pre><code>// 함수를 반환하는 고차 함수
// 이 함수는 카운트 상태를 유지하기 위한 자유 변수 counter를 기억하는 클로저를 반환한다.
const counter = (function () {
  // 카운트 상태를 유지하기 위한 자유 변수
  let counter = 0;

  // 함수를 인수로 전달받는 클로저를 반환
  return function (predicate) {
    // 인수로 전달받은 보조 함수에 상태 변경을 위임한다.
    counter = predicate(counter);
      return counter;
  };
}());


// 보조 함수
function increase(n) {
  return ++n;
}

// 보조 함수
function decrease(n) {
  return --n;
}

// 함수로 함수를 전달하여 호출
console.log(counter(increase()); // 1
console.log(counter(increase()); // 2

// 자유 변수를 공유한다.
console.log(counter(decrease()); // 1
console.log(counter(decrease()); // 0</code></pre><h2 id="6-캡슐화와-정보-은닉">6. 캡슐화와 정보 은닉</h2>
<p>캡슐화는 객체의 상태를 나타내는 프로퍼티와 프로퍼티를 참조하고 동작할 수 있는 동작인 메서드를 하나로 묶은 것을 말한다. 캡슐화는 객체의 특정 프로퍼티나 메서드를 감출 목적으로 사용하기도 하는데 이를 정보 은닉이라 한다.</p>
<p>자바스크립트 객체의 모든 프로퍼티와 메서드는 기본적으로 외부에 공개되어 있다.</p>
<pre><code>function Person(name, age){
    this.name = name; // public
    let _age = age; // private

    // 인스턴스 메서드
    this.sayHi = function (){
        console.log(`Hi My name is ${this.name}. I am ${_age}`);
    };
}

const me = new Person(`Lee`, 20);
me.sayHi(); // Hi! My name is Lee. I am 20

console.log(me.name); // Lee
console.log(me._age); // undefined</code></pre><p>위의 예제 name 프로퍼티는 외부로 공개되어 있어서 참조와 변경이 자유롭다. 즉 name 프로퍼티는 public 하다. 하지만 <code>_age</code> 변수는 Person 생성자 함수의 지역 변수이므로 Person 생성자 함수 외부에서 참조하거나 변경할 수 없다. 즉, <code>_age</code> 변수는 private 하다.</p>
<pre><code>const Person = (function() {
  let _age = 0; // private

  // 생성자 함수
  function Person(name, age) {
    this.name = name; // public
    _age = age;
  }

  // 프로토타입 메서드
  Person.prototype.sayHi = function() {
    console.log(`Hi! My name is ${this.name}. I am ${_age}.`);
  };

  // 생성자 함수를 반환
  return Person;
}());

const me = new Person(&#39;Lee&#39;, 20);
me.sayHi(); // Hi! My name is Lee. I am 20.
console.log(me.name); // Lee
console.log(me._age); // undefined</code></pre><p>정보 은닉이 가능한 것처럼 보이지만, Person 생성자 함수가 여러 개의 인스턴스를 생성할 경우 <code>_age</code> 변수의 상태가 유지되지 않는다.</p>
<pre><code>const me = new Person(&#39;Lee&#39;, 20);
me.sayHi(); // Hi! My name is Lee. I am 20.

const you = new Person(&#39;Kim&#39;, 30);
you.sayHi(); // Hi! My name is Kim. I am 30.

// _age 변수 값이 변경된다.
my.sayHi(); // Hi! My name is Lee. I am 30</code></pre><p>이는 <code>Person.prototype.sayHi</code> 메서드가 단 한 번 생성되는 클로저이기 때문에 발생하는 현상이다. <code>Person.prototype.sayHi</code> 메서드는 즉시 실행 함수가 호출될 때 생성된다.</p>
<p>이처럼 자바스크립트는 정보 은닉을 완벽하게 지원하지 않는다. 
Node.js(ver 12이상)에 구현되었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ HTTP] HTTP 완벽가이드 스터디 - 7장 캐시]]></title>
            <link>https://velog.io/@space_developher/HTTP-HTTP-%EC%99%84%EB%B2%BD%EA%B0%80%EC%9D%B4%EB%93%9C-%EC%8A%A4%ED%84%B0%EB%94%94-7%EC%9E%A5-%EC%BA%90%EC%8B%9C</link>
            <guid>https://velog.io/@space_developher/HTTP-HTTP-%EC%99%84%EB%B2%BD%EA%B0%80%EC%9D%B4%EB%93%9C-%EC%8A%A4%ED%84%B0%EB%94%94-7%EC%9E%A5-%EC%BA%90%EC%8B%9C</guid>
            <pubDate>Sun, 23 Oct 2022 03:59:09 GMT</pubDate>
            <description><![CDATA[<h1 id="0-들어가며">0. 들어가며</h1>
<h2 id="웹-캐시란">웹 캐시란?</h2>
<p>자주 쓰이는 문서의 사본을 자동으로 보관하는 HTTP 장치로, 웹 요청이 캐시에 도착했을 때 캐시된 로컬 사본이 존재한다면, 원 서버가 아니라 캐시로부터 제공된다.</p>
<h2 id="웹-캐시의-혜택">웹 캐시의 혜택</h2>
<ul>
<li>불필요한 데이터 전송을 줄여서 네트워크 비용을 줄여준다.</li>
<li>네트워크 병목을 줄여준다. 대역폭을 늘리지 않고도 페이지를 빨리 불러 올 수 있다.</li>
<li>원 서버에 대한 요청을 줄여준다. 이로 인해 서버의 부하를 줄일 수 있으며, 더 빨리 응답할 수 있게된다.</li>
<li>페이지를 먼 곳에서 불러올수록 긴 시간이 소요되나, 캐시를 통해 지연시간을 줄여준다.</li>
</ul>
<h1 id="1-불필요한-데이터-전송">1. 불필요한 데이터 전송</h1>
<p>여러 클라이언트들이 하나의 서버에 동일한 문서를 요청을 하면, 동일한 데이터를 각각 전송해야하기 때문에 불필요한 데이터 전송으로 인해 값비싼 네트워크 대역폭을 잡아먹고, 전송을 느리게 만들며, 웹 서버에 부하를 준다.</p>
<p>캐시를 이용하면 첫 번째 서버 응답은 캐시에 보관되고, 캐시된 사본이 뒤이은 요청들에 대한 응답으로 사용되어 원 서버의 트래픽 낭비를 줄일 수 있다.</p>
<h1 id="2-대역폭-병목">2. 대역폭 병목</h1>
<p>일반적으로 많은 네트워크가 원격 서버보다 로컬 네트워크 클라이언트에 더 넓은 대역폭을 제공하며 속도가 더 빠른 장점이 있다.</p>
<h1 id="3-갑작스런-요청-쇄도flash-crowds">3. 갑작스런 요청 쇄도(Flash Crowds)</h1>
<p>갑작스러운 사건(유명인사 관련 뉴스)으로 인해 갑자기 많은 트래픽이 발생할 때 네트워크와 서버에 장애를 야기한다.</p>
<h1 id="4-거리로-인한-지연">4. 거리로 인한 지연</h1>
<p>거리가 멀수록 서버로 부터 응답받는 지연시간이 늘어난다.
도쿄-샌프란시스코 사이의 왕복시간보다, 사무실에 캐시를 설치하면 수천 킬로에서 수십미터로 거리를 줄일 수 있다.</p>
<h1 id="5-적중과-부적중">5. 적중과 부적중</h1>
<p>캐시는 유용하지만, 모든 문서의 사본을 저장하지는 않는다. (비용 문제와 최신 상태에 대한 문제)
캐시에 요청이 도착했을 때 대응하는 사본이 있는 경우 요청이 처리되며, 이를 캐시 적중이라고 한다.
반면, 대응되는 사본이 없는 경우는 원 서버로 전달되기만 한다. 이를 캐시 부적중이라고 한다.</p>
<h2 id="1-재검사">1) 재검사</h2>
<p>원 서버가 가지고 있는 콘텐츠는 변경될 수 있기 때문에 캐시된 사본이 최신인지 점검해야한다.
이를 <code>HTTP 재검사</code>라고 부르며, 콘텐츠의 일부를 가져와서 검사할 수 있는 특별한 요청을 정의했다.</p>
<p>캐시는 클라이언트가 사본을 요청했을 때 충분히 오래된 경우에만 재검사를 진행한다.</p>
<h3 id="느린-적중">느린 적중</h3>
<p>재검사는 원 서버에 작은 재검사 요청을 보내고, 콘텐츠가 변경되지 않았다면 아주 작은 사이즈의 <code>304 Not Modified</code> 라는 응답을 받는다. 캐시는 이를 신선하다고 표시한 뒤 클라이언트에 제공한다. 
이를 <code>재검사 적중</code> 혹은 <code>느린 적중</code> 이라 한다.
이는 캐시 적중 보다는 느리나 캐시 부적중 보다는 빠르다. (서버로부터 객체 데이터를 받을 필요가 없기 떄문이다.)
<img src="https://velog.velcdn.com/images%2Fnoahshin__11%2Fpost%2F6800708e-4f6a-4394-b089-22d5f4c12ac0%2Fimage.png" alt=""></p>
<p>HTTP는 캐시된 객체를 재확인하기 위해 몇 가지 도구를 제공하며, 가장 많이 사용되는 것은 <code>If-Modified-Since 헤더</code> 이다. 
서버에게 보내는 <code>GET</code> 요청에 이 헤더를 추가하면 캐시된 시간 이후에 변경된 경우에만 사본을 보내달라는 의미가 된다.</p>
<p><code>GET If-Modified-Since</code> 요청이 서버에 도착하는 경우 세가지 상황이 발생할 수 있다.</p>
<ol>
<li>서버 콘텐츠가 변경되지 않은 경우</li>
<li>서버 콘텐츠가 변경된 경우</li>
<li>객체가 삭제된 경우</li>
</ol>
<h4 id="재검사-적중">재검사 적중</h4>
<p>서버 객체가 변경되지 않았다면 <code>304 Not Modified</code> 라는 응답을 보낸다.
<img src="https://velog.velcdn.com/images%2Fnoahshin__11%2Fpost%2F467bff9a-ac01-438c-8860-cd8c6fc24766%2Fimage.png" alt=""></p>
<h4 id="재검사-부적중">재검사 부적중</h4>
<p>서버 객체가 캐시된 사본과 다르다면 서버는 콘텐츠 전체와 함께 <code>HTTP 200 OK</code> 응답을 보낸다.</p>
<h4 id="객체-삭제">객체 삭제</h4>
<p>서버 객체가 삭제됬다면 서버는 <code>404 Not Found</code> 응답을 보내며, 캐시는 사본을 삭제한다.</p>
<h2 id="2-캐시-적중률">2) 캐시 적중률</h2>
<p>캐시가 요청을 처리하는 비율을 <code>캐시 적중률</code> 이라 하며, <code>0</code> 에서 <code>1</code> 의 값으로 되어있지만, <code>퍼센트</code> 로 표시되기도 한다.
<code>0%</code> 는 모든 요청이 캐시 부적중이며, <code>100%</code> 는 모든 요청이 적중된 경우다.
캐시 적중률이 <code>40%</code> 만 되어도 괜찮은 편이다.</p>
<p>얼마나 많은 웹 트랜잭션을 외부로 내보내지 않았는지를 보여준다. 이를 개선하면 전체 지연시간이 줄어든다.</p>
<h2 id="3-바이트-적중률">3) 바이트 적중률</h2>
<p>큰 객체의 경우 덜 접근되지만, 그 크기로 인해 전체 트래픽에 더 영향을 끼친다.
바이트 단위 적중률은 캐시를 통해 제공된 모든 바이트의 비율을 표현하며, 이 측정값은 트래픽이 절감된 정도를 포착해낸다.
바이트 단위 적중률 <code>100%</code> 는 모든 바이트가 캐시에서 왔으며, 어떤 트래픽도 인터넷으로 나가지 않았음을 의미한다.</p>
<p>바이트 단위 적중률은 얼마나 많은 바이트가 인터넷으로 나가지 않았는지를 보여주며, 이를 개선하면 대역폭 절약을 최적화한다.</p>
<h2 id="4-적중과-부적중의-구별">4) 적중과 부적중의 구별</h2>
<p>HTTP는 클라이언트에게 응답이 캐시 적중되었는지, 아니면 원 서버 접근인지를 표현해주지 않는다.
두 경우 모두 응답코드는 <code>200 OK</code> 로 전달된다.</p>
<p>클라이언트가 응답이 캐시에서 왔는지 알아내는 방법은 <code>Date 헤더</code> 와 <code>Age 헤더</code> 를 통해 알 수 있다.
응답의 <code>Date 헤더</code> 값을 현재 시각과 비교하여 응답의 생성일이 더 오래되었다면 클라이언트는 응답이 캐시된 것임을 알아낼 수 있다.
응답이 오래되었는지 말해주는 <code>Age 헤더</code> 를 통해 알 수 있다.</p>
<h1 id="6-캐시-토폴로지">6. 캐시 토폴로지</h1>
<p>캐시는 한 명의 사용자에게만 할당될 수 있지만, 수천 명의 사용자들에게 공유될 수도 있다.
한명에게만 할당된 캐시는 <code>개인 전용 캐시(Private Cashe)</code> 라고 부르며, 한명의 사용자가 자주 찾는 페이지를 담는다.
공유된 캐시는 <code>공용 캐시(Public Cashe)</code> 라고 부르며, 사용자 집단에게 자주 쓰이는 페이지를 담는다.</p>
<h2 id="1-개인-전용-캐시">1) 개인 전용 캐시</h2>
<p>개인 전용 캐시는 많은 저장 공간을 필요로 하지 않으므로 작고 저렴하다.
웹 브라우저는 개인 전용 캐시를 내장하고 있으며, 자주 쓰이는 문서를 PC 의 디스크와 메모리에 캐시해놓는다. 
사용자가 캐시 사이즈와 설정을 수정할 수 있도록 허용한다.
<img src="https://velog.velcdn.com/images%2Fnoahshin__11%2Fpost%2F2f7ec7db-9d0a-4a14-91bd-e3f2ca92056b%2Fimage.png" alt=""></p>
<h2 id="2-공용-프락시-캐시">2) 공용 프락시 캐시</h2>
<p>공용 캐시는 <code>프락시 캐시</code> 라고 불리는 <code>프락시 서버</code> 다. 여러 사용자가 접근하기 때문에 불필요한 트래픽을 줄일 수 있다. 처음 접근했을 때 객체를 단 한번만 가져와 사본을 공유하기 때문에 네트워크 트래픽을 줄인다.</p>
<h2 id="3-프락시-캐시-계층들">3) 프락시 캐시 계층들</h2>
<p>작은 캐시에서 캐시 부적중이 발생했을 때 더 큰 부모 캐시가 트래픽을 처리하도록 만드는 계층 방식이다.
클라이언트 단에는 작은 캐시 단위를 사용하고, 상단에는 문서를 공유하기 위한 더 크고 강력한 캐시를 사용하자는 형태이다.
단, 프락시 연쇄가 길어질수록 성능 저하가 심해진다.</p>
<h2 id="4-캐시망-콘텐츠-라우팅-피어링">4) 캐시망, 콘텐츠 라우팅, 피어링</h2>
<p>네트워크 아키텍처를 통해 프락시 캐시는 복잡한 방법으로 상호 대화하여 어떤 캐시와 통신할 것인지, 아니면 요청이 캐시를 우회해서 원 서버로 바로 가도록 할 것인지에 대한 캐시 커뮤니케이션을 동적으로 내린다.</p>
<p>콘텐츠 라우팅을 위해 설계된 캐시들은 다음과 같은 일을 할 수 있다.</p>
<ul>
<li>URL에 근거하여 부모 캐시와 원 서버 중 하나를 동적으로 선택한다.</li>
<li>URL에 근거하여 특정 부모 캐시를 동적으로 선택한다.</li>
<li>부모 캐시에게 가기 전에 캐시된 사본을 로컬에서 찾아본다.</li>
<li>다른 캐시들이 캐시된 컨텐츠에 부분적으로 접근할 수 있도록 허용하되 캐시를 통한 인터넷 트랜짓을 허용하지 않는다.</li>
</ul>
<p>선택적인 피어링을 지원하는 캐시는 형제 캐시라고 한다. HTTP 는 형제 캐시를 지원하지 않기에 인터넷 캐시 프로토콜(ICP)나, 하이퍼텍스트 캐시 프로토콜(HTCP) 같은 프로토콜을 통해 HTTP 를 확장했다.
<img src="https://velog.velcdn.com/images%2Fnoahshin__11%2Fpost%2F49d21aef-a715-4225-a30d-b68ccda5b0a3%2Fimage.png" alt=""></p>
<h1 id="7-캐시-처리-단계">7. 캐시 처리 단계</h1>
<ol>
<li>요청 받기 - 캐시는 네트워크로부터 도착한 요청 메시지를 읽는다.</li>
<li>파싱 - 캐시는 메시지를 파싱하여 URL과 헤더들을 추출한다.</li>
<li>검색 - 캐시는 로컬 복사본이 있는지 검사하고, 사본이 없다면 사본을 받아온다. (그리고 로컬에 저장한다.)</li>
<li>신선도 검사 - 캐시는 캐시된 사본이 노후된 사본인지 확인하고, 노후되었다면 변경사항이 있는지 서버에 확인한다.</li>
<li>응답 생성 - 캐시는 새로운 헤더와 캐시된 본문으로 응답 메시지를 만든다.</li>
<li>발송 - 캐시는 네트워크를 통해 응답을 클라이언트에게 돌려준다.</li>
<li>로깅 - 선택적으로 캐시는 로그파일에 트랜잭션에 대해 서술한 로그 하나를 남긴다.
<img src="https://velog.velcdn.com/images%2Fnoahshin__11%2Fpost%2F298747d7-44e5-4103-8a5b-902ec7013472%2Fimage.png" alt=""></li>
</ol>
<h2 id="1-요청-받기">1) 요청 받기</h2>
<p>캐시는 네트워크 커넥션에서의 활동을 감지하고, 들어오는 데이터를 읽어들인다.
고성능 캐시는 여러 개의 들어오는 커넥션들로부터 데이터를 동시에 읽어들이고 메시지 전체가 도착하기 전에 트랜잭션 처리를 시작한다.</p>
<h2 id="2-파싱">2) 파싱</h2>
<p>캐시는 요청 메시지를 여러 부분으로 파싱하여 헤더 부분을 조작하기 쉬운 자료 구조에 담는다. 
이는 캐싱 소프트웨어가 헤더 필드를 처리하고 조작하기 쉽게 만들어준다.</p>
<h2 id="3-검색">3) 검색</h2>
<p>캐시는 URL을 알아내고 그에 맞는 로컬 사본이 있는지 검사한다. 로컬 복사본은 메모리에 저장됬을 수도 있고 디스크나 다른 PC에 있을 수도 있다. 만약 문서를 로컬에서 가져올 수 없다면 캐시는 상황이나 설정에 따라서 원 서버나 부모 프락시에서 가져오거나 실패를 반환한다.</p>
<p>캐시된 객체는 서버 응답 본문과 원 서버 응답 헤더를 포함하고 있으므로, 캐시 적중 동안 올바른 서버 헤더가 반환될 수 있다. 캐시된 객체는 객체가 얼마나 오랫동안 캐시에 머무르고 있었는지를 알려주는 기록이나 얼마나 자주 사용되었는지 등에 대한 몇몇 메타데이터를 포함한다.</p>
<h2 id="4-신선도-검사">4) 신선도 검사</h2>
<p>HTTP는 캐시가 일정 기간 동안 서버 문서의 사본을 보유할 수 있도록 해준다. 이 기간 동안 문서는 <code>신선</code> 한 것으로 간주되고 캐시는 서버와의 접촉 없이 문서를 제공할 수 있다. 하지만 일단 캐시된 사본이 너무 오래되었을 경우 그 객체는 <code>노후</code> 된 것으로 간주되어 변경점을 확인하기 위해 재검사를 진행한다.
신선도 검사 규칙은 매우 복잡하다.</p>
<h2 id="5-응답-생성">5) 응답 생성</h2>
<p>캐시된 서버 응답 헤더를 토대로 응답 헤더를 생성한다. 이 기저 헤더들은 캐시에 의해 수정되고 늘어난다.
캐시는 클라이언트에 맞게 이 헤더를 조정해야한다. 예를 들어 클라이언트가 HTTP/1.1 응답을 기대한다면 서버가 다른 응답을 반환한다면 헤더를 적절하게 번역해야한다.
또한, 캐시 신선도 정보를 삽입하며, 요청이 프락시 캐시를 거쳐갔음을 알려주기 위한 <code>Via 헤더</code> 를 종종 포함시킨다.
단, 캐시가 <code>Date 헤더</code> 를 조정해서는 안된다. <code>Date 헤더</code> 는 객체가 원 서버에서 최초로 생겨난 일시를 표현하는 것이다.</p>
<h2 id="6-전송">6) 전송</h2>
<p>응답 헤더가 준비되면 캐시는 응답을 클라이언트에게 돌려준다. 프락시 캐시는 클라이언트와 커넥션을 유지할 필요가 있다. 고성능 캐시는 종종 로컬 저장장치와 네트워크 I/O 버퍼 사이에서 문서의 콘텐츠 복사를 피함으로써 데이터를 효과적으로 전송하기 위해 노력한다.</p>
<h2 id="7-로깅">7) 로깅</h2>
<p>각 캐시 트랜잭션이 완료된 후 통계 캐시 적중과 부적중 횟수에 대한 통계를 갱신하고 로그 파일에 요청 종류, URL, 무엇이 일어났는지 알려주는 항목을 추가한다.</p>
<h2 id="8-캐시-처리-플로-차트">8) 캐시 처리 플로 차트</h2>
<p><img src="https://blog.kakaocdn.net/dn/b84VJu/btrDWUgtXcX/KBKe7BC3dh30ZAofDf3M7k/img.png" alt=""></p>
<h1 id="8-사본을-신선하게-유지하기">8. 사본을 신선하게 유지하기</h1>
<p>문서들은 시간에 따라 변경될 수 있으며, 오래된 데이터를 제공하는 캐시는 불필요하다. 캐시된 데이터는 서버의 데이터와 일치하도록 관리되어야한다.
HTTP는 어떤 캐시가 사본을 갖고 있는지 서버가 기억하지 않더라도 캐시된 사본이 서버와 일치하도록 유지하는 <code>문서 만료</code> 와 <code>서버 재검사</code> 라는 매커니즘을 가지고 있다.</p>
<h2 id="1-문서-만료">1) 문서 만료</h2>
<p>HTTP는 <code>Cache-Control</code> 과 <code>Expires</code> 라는 특별한 헤더를 이용해서 원 서버가 각 문서에 유효기간을 붙일 수 있게 해준다. (우유팩의 유통기한과 유사하다.)
이 헤더들은 콘텐츠가 얼마나 오랫동안 신선한 상태를 보일 수 있는지 보여준다.
<img src="https://blog.kakaocdn.net/dn/cdN9cw/btrsTb2b7GE/VvoRE3TwK1jiZX0VvpogG0/img.png" alt=""></p>
<p>캐시 문서가 만료되기 전이라면 캐시는 서버와의 접촉없이 사본을 제공할 수 있다. 그러나 캐시된 문서가 만료되면 캐시는 반드시 서버와 문서 사이의 변경점을 검사하고, 변경되었다면 새 유효기간이 달린 신선한 사본을 가져와야한다.</p>
<h2 id="2-유효기간과-나이">2) 유효기간과 나이</h2>
<p>서버의 응답 헤더로 아래의 내용이 전달된다.</p>
<table>
<thead>
<tr>
<th>헤더</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>Cache-Control: max-age</td>
<td>max-age는 문서의 최대 나이를 정의한다. </br> 문서가 처음 생성된 이후부터 신선하지 않다고 변경될 때까지의 경과한 시간의 합법적 최대값(초 단위)이다.</br> Cache-Control: max-age=484200</td>
</tr>
<tr>
<td>Expires</td>
<td>절대 유효기간을 명시한다. </br>유효기간이 경과했다면 문서는 더 이상 신선하지 않다.</br> Expires: Fri, 05 Jul 2002, 05:00:00 GMT</td>
</tr>
</tbody></table>
<h2 id="3-서버-재검사">3) 서버 재검사</h2>
<p>캐시된 문서가 만료되었다는 것은 문서와 원 서버의 데이터가 다르다는 것을 의미하지는 않는다.
다만 검사할 시간이 되었음을 의미하며, 문서가 변경되었는지 여부를 물어보는 것을 <code>서버 재검사</code> 라고 부른다. </p>
<ul>
<li>재검사 결과 컨텐츠가 변경되었다면 캐시는 새로운 사본을 가져와 오래된 데이터 대신 저장한 뒤 클라이언트에게도 보내준다.</li>
<li>재검사 결과 컨텐츠가 변경되지 않았다면, 캐시는 새 만료일을 포함한 새 헤더들만 가져와서 캐시 안의 헤더들을 갱신한다.</li>
</ul>
<p>캐시는 문서의 신선도를 매 요청마다 검증할 필요가 없고 문서가 만료되었을 때 한번만 서버와 재검사를 하면 된다.
서버 트래픽을 절약하고 사용자 응답 시간을 개선하는 방법이다.</p>
<h3 id="http-프로토콜이-요구하는-캐시의-행동">HTTP 프로토콜이 요구하는 캐시의 행동</h3>
<p>캐시는 다음 중 하나를 반환하는 행동이 요구된다.</p>
<ul>
<li><code>충분히 신선한</code> 캐시된 사본</li>
<li>원 서버와 재검사되었기 때문에 충분히 신선하다고 확신할 수 있는 캐시된 사본</li>
<li>에러메시지(재검사 해야하는 원 서버가 다운된 경우)</li>
<li>경고 메시지가 부착된 캐시된 사본</li>
</ul>
<h2 id="4-조건부-메서드와의-재검사">4) 조건부 메서드와의 재검사</h2>
<p>HTTP는 캐시가 서버에게 <code>조건부 GET</code> 이라는 요청을 보낼수 있도록 해주며, 서버가 갖고 있는 문서가 캐시가 갖고 있는 것과 다른 경우에만 객체 본문을 보내달라고 한다. <code>조건부 GET</code> 은 <code>GET</code> 요청 메시지에 특별한 조건부 헤더를 추가함으로써 시작된다. 웹 서버는 조건이 참인 경우에만 객체를 반환한다.</p>
<table>
<thead>
<tr>
<th>헤더</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>If-Modified-Since: <date></td>
<td>문서가 주어진 날짜 이후로 수정되었다면 요청 메서드를 처리한다.</br> 캐시된 버전으로부터 콘텐츠가 변경된 경우에만 콘텐츠를 가져오기 위해 Last-Modified 서버 응답 헤더와 사용된다.</td>
</tr>
<tr>
<td>If-None-Match: <tags></td>
<td>마지막 변경된 날짜를 맞춰보는 대신, 서버는 문서에 대한 일련번호와 같이 동작하는 태그를 제공할 수 있다. </br> If-None-Match 헤더는 캐시된 태그가 서버에 있는 문서 태그와 다를 때만 요청을 처리한다.</td>
</tr>
</tbody></table>
<h2 id="5-if-modified-since-날짜-재검사">5) If-Modified-Since: 날짜 재검사</h2>
<p>가장 흔히 쓰이는 캐시 재검사 헤더는 <code>If-Modified-Since</code> 이다.
<code>IMS 요청</code> 이라고 불리며, 서버에게 리소스가 특정 날짜 이후로 변경된 경우에만 요청한 본문을 보내달라고 한다.</p>
<ul>
<li>만약 날짜가 변경되었다면, 조건은 참이되고, GET 요청은 성공하며, 새 문서에 만료 날짜와 그 외의 정보들이 담긴 헤더와 함께 캬시에 반환된다.</li>
<li>만약 날짜 이후에 변경되지 않았다면, 조건은 거짓이고, 서버는 <code>304 Not Modified</code> 를 클라이언트에게 돌려주며, 별도의 본문은 전달하지 않는다. 응답은 헤더 및 갱신에 필요한 내용만 전달한다. 
예를 들어 <code>Content-Type</code> 헤더는 잘 변하지 않기에 보내지 않고, 새 만료 날짜는 보내준다.</li>
</ul>
<p><code>If-Modified-Since</code> 헤더는 응답 헤더인 <code>Last-Modified</code> 와 함께 동작한다.
원 서버는 제공하는 문서에 최근 변경 일시를 붙이고, 재검사하려고 할 때 사본이 마지막 수정된 날짜가 담긴 if-Modified-Since 헤더를 포함한다.</p>
<h2 id="6-if-none-match-엔터티-태그-재검사">6) If-None-Match: 엔터티 태그 재검사</h2>
<p>최근 변경 일시 재검사가 행해지기 어려운 경우, 태그를 비교해서 새 객체를 요청하는 방법으로 재검사를 진행하는 방법이다.</p>
<ul>
<li>일정 간격으로 다시 쓰여지지만 동일한 데이터를 포함하는 문서</li>
<li>철자나 주석의 변경같이 사소한 변경으로, 전 세계 캐시가 변경하기엔 불필요한 문서</li>
</ul>
<p>태그가 변경되지 않은 경우 새 객체를 요청하는 방식으로 재검사 여부를 확인한다.
<img src="https://velog.velcdn.com/images/juyeon0526/post/5a4272d6-4de0-4cc7-aa6f-fce4cf845195/image.jpeg" alt=""></p>
<p>만약 위의 경우 엔터티 태그가 변경되었다면 서버는 <code>200 OK</code> 응답으로 새 콘텐츠를 새 ETag 와 함께 반환한다.</p>
<h2 id="7-약한-검사기와-강한-검사기">7) 약한 검사기와 강한 검사기</h2>
<p><code>약한 검사기</code> 란 모든 캐시된 사본을 무효화시키지 않고 문서의 특정 부분만 고칠 수 있도록 허용하고 싶은 경우 사용하기 위한 방법이다. 단, 중요한 내용이 변경된 경우 변경된다. 이 방법은 <code>HTTP/1.1</code> 부터 지원한다.
추가적으로 조건부 특정 범위 가져오기 같은 몇몇 동작들은 서버에서 구분하지 못하기 때문에 <code>W/</code> 같은 접두사로 약한 검사기를 구분한다.
<code>강한 검사기</code> 란 콘텐츠가 바뀔때 마다 바뀐다. </p>
<h2 id="8-언제-태그를-사용하고-언제-last-modified-일시를-사용하는가">8) 언제 태그를 사용하고 언제 Last-Modified 일시를 사용하는가?</h2>
<ul>
<li>서버가 엔터티 태그를 반환했다면 반드시 엔터티 태그 검사기를 사용해야한다.</li>
<li>서버가 <code>Last-Modified</code> 값만 반환했다면 클라이언트는 <code>If-Modfied-Since</code> 검사를 사용할 수 있다.</li>
<li>엔터티 태그와 최근 변경 일시가 모두 사용 가능하다면 HTTP/1.0 과 HTTP/1.1 캐시 모두 적절히 응답할 수 있도록 클라이언트는 각각을 위해 두 가지의 재검사 정책을 사용해야한다.</li>
<li>HTTP/1.1 원 서버는 실현 불가능하지만 않다면 엔터티 태그 검사기를 보내야하며, 강한 엔터티 태그 대신 약한 엔터티 태그를 보낼수도 있다. 또한 <code>Last-Modified</code> 값을 같이 보내는 것도 선호된다.</li>
<li>HTTP/1.1 캐시나 서버가 <code>If-Modified-Since</code> 와 엔터티 태그 조건부 헤더를 모두 받았다면 요청의 모든 조건부 헤더 필드의 조건에 부합되지 않는 한 <code>304 Not Modified</code> 를 반환해서는 안된다.</li>
</ul>
<h1 id="9-캐시-제어">9. 캐시 제어</h1>
<p>HTTP는 문서가 만료되기 전까지 얼마나 오랫동안 캐시될 수 있게 할 것인지 서버가 설정할 수 있는 여러 가지 방법을 정의한다.</p>
<ul>
<li>Cache-Control: no-store &gt;&gt; 응답의 사본을 만드는 것을 금지한다.</li>
<li>Cache-Control: no-cache &gt;&gt; 서버와 재검사를 하지 않고서는 캐시에서 클라이언트로 제공하지 않는다.</li>
</ul>
<blockquote>
<p>no-store와 no-cache 헤더는 캐시가 검증되지 않은 캐시된 객체로 응답하는 것을 막는다.</p>
</blockquote>
<ul>
<li>Cache-Control: must-revalidate &gt;&gt; 캐시가 이 객체의 신선하지 않은 사본을 원 서버와의 최초의 재검사 없이는 제공해서는 안됨을 의미한다. 또한 캐시가 신선도 검사를 시도했을 때, 원 서버가 사용할 수 없는 상태라면 캐시는 반드시 <code>504 Gateway Timeout error</code> 를 반환해야 한다.</li>
<li>Cache-Control: max-age &gt;&gt; 신선하다고 간주된 문서가 서버로부터 온 이후로 흐른 시간이다.</li>
<li>Expires &gt;&gt; 초 단위의 시간 대신 실제 만료 날짜</li>
</ul>
<h2 id="---휴리스틱-만료">-- 휴리스틱 만료</h2>
<p><code>Cache-Control: max-age</code> 나 <code>Expires</code> 헤더를 포함하지 않은 경우 사용되며, 캐시는 경험적인 방법으로 최대 나이를 계산한다.
LM 알고리즘에 의해 동작하며, 신선도 유지기간의 상한선을 설정하여 운영한다.
아무런 단서가 없을 경우는 기본 신선도의 유지기간은 1시간에서 하루로 설정된다.</p>
<h2 id="---클라이언트-신선도-제약">-- 클라이언트 신선도 제약</h2>
<p>클라이언트는 Cache-Control 요청 헤더를 사용하여 만료 제약을 엄격하게 하거나 느슨하게 할 수 있다.</p>
<h2 id="주의할-점">주의할 점</h2>
<p>문서 만료 기간을 잘못 설정했다면 어떤 변경사항도 캐시에 반영되지 않는 점을 주의해야한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ Git ] Remote 변경하기]]></title>
            <link>https://velog.io/@space_developher/Git-Remote-%EB%B3%80%EA%B2%BD%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@space_developher/Git-Remote-%EB%B3%80%EA%B2%BD%ED%95%98%EA%B8%B0</guid>
            <pubDate>Wed, 19 Oct 2022 06:30:05 GMT</pubDate>
            <description><![CDATA[<h2 id="들어가며">들어가며</h2>
<p>최근 서비스 중인 프로젝트 레파지토리를 비트버켓에서 깃허브로 변경하였다.
이에 따라 현재 사용중이던 프로젝트의 원격 주소 변경이 필요하였다.</p>
<h2 id="방법">방법</h2>
<h3 id="remote-주소-확인">remote 주소 확인</h3>
<pre><code>git remote -v</code></pre><h3 id="remote-주소-제거">remote 주소 제거</h3>
<pre><code>git remote remove origin</code></pre><h3 id="새로운-repository-remote-연결">새로운 repository remote 연결</h3>
<pre><code>git remote add origin &lt;remote-url&gt;</code></pre><p>나의 경우 ssh 방식으로 repository 를 연결하였기 때문에 ssh code를 통해 연결을 진행하였다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ JS ] 자바스크립트 딥다이브 스터디 - 19장 프로토타입]]></title>
            <link>https://velog.io/@space_developher/JS-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%94%A5%EB%8B%A4%EC%9D%B4%EB%B8%8C-%EC%8A%A4%ED%84%B0%EB%94%94-19%EC%9E%A5-%ED%8F%AC%ED%86%A0%ED%83%80%EC%9E%85</link>
            <guid>https://velog.io/@space_developher/JS-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%94%A5%EB%8B%A4%EC%9D%B4%EB%B8%8C-%EC%8A%A4%ED%84%B0%EB%94%94-19%EC%9E%A5-%ED%8F%AC%ED%86%A0%ED%83%80%EC%9E%85</guid>
            <pubDate>Mon, 17 Oct 2022 11:42:29 GMT</pubDate>
            <description><![CDATA[<p>자바스크립트는 프로토타입 기반의 객체지향 프로그래밍 언어이다.</p>
<blockquote>
<p>클래스</p>
<p>ES6 에서 도입된 <code>클래스</code> 또한 함수이며, 기존 프로토타입 기반 패턴의 문법적 설탕이라 할 수 있다.
클래스와 생성자 함수 모두 프로토타입 기반의 인스턴스를 생성하지만, 정확히 동일하게 동작하지는 않는다.
클래스는 생성자 함수보다 엄격하며, 생성자 함수에서 제공하지 않는 기능도 제공한다.
이에 따라 새로운 객체 생성 방식으로 보는 것이 더 합당하다.</p>
</blockquote>
<p><code>자바스크립트</code> 에서는 원시 타입을 제외한 나머지 값들은 모두 <code>객체</code> 다.</p>
<h2 id="1-객체-지향-프로그래밍">1. 객체 지향 프로그래밍</h2>
<p>객체 지향 프로그래밍은 절차지향적 관점이 아닌, 객체의 집합으로 프로그램을 표현하ㄴ려는 프로그래밍 패러다임을 의미한다.
실제 세계의 실체를 프로그래밍에 반영하려 하였으며, 이에 따라 특징이나 성질을 나타내는 <code>속성</code> 을 표현하려 하였다. 이 중 필요한 속성만 간추려 내는 것을 <code>추상화</code> 라고 한다.</p>
<pre><code>const person = {
    name: `Lee`,
    age: 30
};</code></pre><p>위는 이름과 나이의 속성으로 표현된 객체인 <code>person</code> 이다.</p>
<pre><code>const circle = {
    radius: 5,
    getDiameter(){
        return 2 * this.radiusl
    }
}</code></pre><p>위는 객체의 <code>상태</code> 를 나타내는 데이터 <code>radius</code> 와 상태 데이터를 조작할 수 있는 <code>동작</code> <code>getDiameter</code> 를 하나의 논리적인 단위로 묶어 표현한다.
<code>상태</code> 는 <code>프로퍼티</code> , <code>동작</code> 을 <code>메서드</code> 라고 부른다.</p>
<h2 id="2-상속과-프로토타입">2. 상속과 프로토타입</h2>
<blockquote>
<p>상속 : 객체의 프로퍼티 또는 메서드를 다른 객체가 그대로 사용할 수 있는 것을 말한다.</p>
</blockquote>
<p>자바스크립트는 프로토타입을 기반으로 상속을 구현하여 불필요한 중복을 제거한다.</p>
<pre><code>// 생성자 함수
function Circle(radius){
    this.radius = radius;
    this.getArea = function (){
        return Math.PI * this.radius ** 2;
    }
}

// 반지름이 1인 인스턴스 생성
const circle1 = new Circle(1);

// 반지름이 2인 인스턴스 생성
const circle2 = new Circle(2);

console.log(circle1.getArea === circle2.getArea); // false</code></pre><p>위의 예제 메서드 <code>getArea</code> 는 인스턴스 <code>circle1</code> 과 <code>circle2</code> 에서 모두 사용되었으나, 다른 인스턴스이기 때문에 주소값이 달라서 <code>false</code> 가 나온다.
그러나 동일한 동작을 하고 있기 때문에 메서드 하나만 생성하여 모든 인스턴스가 공유해서 사용하는 것이 바람직하다.
이를 상속을 통해 해결할 수 있다.</p>
<pre><code>function Circle(radius){
    this.radius = radius;
}

// Circle 생성자 함수가 생성하는 모든 인스턴스에 getArea 메서드를 프로토타입에 추가한다.
Circle.prototype.getArea = function (){
    return Math.PI * this.radius ** 2;
}

// 인스턴스 생성
const circle1 = new Circle(1);
const circle2 = new Circle(2);

// Circle 생성자 함수가 생성한 모든 인스턴스는 부모 객체의 역할을 하는 프로토타입 `Circle.prototype` 
// 로부터 메서드를 상속받는다.
// 즉, Circle 생성자 함수가 생성하는 모든 인스턴스는 하나의 `getArea` 메서드를 공유한다.
console.log(circle1.getArea === circle2.getArea); // true</code></pre><p>위의 예제 <code>getArea</code> 는 prototype 을 공유하여 상속받기 때문에 동일한 메서드이다.
따라서 두 인스턴스에 존재하는 <code>getArea</code>는 하나의 메서드이다.</p>
<p>Circle 생성자 함수가 생성한 모든 인스턴스는 Circle.prototype 의 모든 프로퍼티와 메서드를 상속받는다.</p>
<h2 id="3-프로토타입-객체">3. 프로토타입 객체</h2>
<p>모든 객체는 <code>[[Prototype]]</code> 이라는 내부 슬롯을 가지며, 내부 슬롯의 값은 프로토타입의 참조다. (null 인 경우도 있다)
객체가 생성될 때 객체 생성 방식에 따라 프로토타입이 결정되고, <code>[[Prototype]]</code> 에 저장된다.</p>
<pre><code>예시)
객체 리터럴에 의해 생성된 객체의 프로토타입: Object.prototype
생성자 함수에 의해 생성된 객체의 프로토타입: 생성자 함수의 prototype 프로퍼티에 바인딩되어 있는 객체</code></pre><p>모든 객체는 하나의 프로토타입을 가지며, 모든 프로토타입은 생성자 함수와 연결되어 있다.
<img src="https://velog.velcdn.com/images%2Fnoahshin__11%2Fpost%2Fdac7e559-f67d-4b59-bf17-4d2c9dd795b1%2Fimage.png" alt=""></p>
<p><code>[[Prototype]]</code> 내부 슬롯에는 직접 접근할 수 없지만, <code>__proto__</code> 접근자 프로퍼티를 통해 자신의 프로토타입 <code>[[Prototype]]</code> 내부 슬롯이 가리키는 프로토타입에 간접적으로 접근할 수 있다.
프로토타입은 자신의 <code>constructor</code> 프로퍼티를 통해 생성자 함수에 접근할 수 있고, 생성자 함수는 자신의 <code>prototype</code> 프로퍼티를 통해 프로토타입에 접근할 수 있다.</p>
<h3 id="1-__proto__-접근자-프로퍼티">1) <code>__proto__</code> 접근자 프로퍼티</h3>
<p>모든 객체는 <code>__proto__</code> 접슨자 프로퍼티를 통해 자신의 프로토타입 <code>[[Prototype]]</code> 내부 슬롯에 간접적으로 접근할 수 있다.</p>
<h4 id="-1--__proto__-는-접근자-프로퍼티다">-1- <code>__proto__</code> 는 접근자 프로퍼티다.</h4>
<p>내부 슬롯은 프로퍼티가 아니다. 원칙적으로 내부 슬롯과 내부 메서드에 직접적으로 접근하거나 호출할 수 있는 방법을 제공하지 않는다.</p>
<p><code>Object.prototype</code> 의 접근자 프로퍼티인 <code>__proto__</code> 는 <code>getter/setter</code> 함수라고 부르는 접근자 함수<code>[[Get]]</code>, <code>[[Set]]</code> 를 통해 <code>[[Prototype]]</code> 내부 슬롯의 값을 취득하거나 할당한다.</p>
<pre><code>const obj = {};
const parent = { x:1 };

// getter 함수인 get __proto__가 호출되어 obj 객체의 프로토타입을 취득한다.
obj.__proto__;

// setter 함수인 set __proto__가 호출되어 obj 객체의 프로토타입을 교체한다.
obj.__proto__ = parent;

console.log(obj.x); // 1</code></pre><h4 id="-2--__proto__-접근자-프로퍼티는-상속을-통해-사용된다">-2- <code>__proto__</code> 접근자 프로퍼티는 상속을 통해 사용된다.</h4>
<p><code>__proto__</code> 접근자 프로퍼티는 객체가 직접 소유하는 프로퍼티가 아닌 <code>Object.prototype</code> 의 프로퍼티다.
모든 객체는 상속을 통해 <code>Object.prototype.__proto__</code> 접근자 프로퍼티를 사용할 수 있다.</p>
<pre><code>const person = { name: `Lee` }

// 객체는 __proto__ 프로퍼티를 소유하지 않는다.
console.log(person.hasOwnProperty(`__proto__`)); // false

// __proto__ 프로퍼티는 모든 객체의 프로토타입 객체인 Object.prototype의 접근자 프로퍼티다.
console.log(Object.getOwnPropertyDescriptor(Object.prototype, `__proto__`));
// { get: f, set: f, enumerable: false, configurable: true }

// 모든 객체는 Object.prototype의 접근자 프로퍼티 __proto__를 상속받아 사용할 수 있다.
console.log({}.__proto__ === Object.prototype); // true</code></pre><blockquote>
<p>Object.prototype</p>
</blockquote>
<p>모든 객체는 프로토타입의 계층 구조인 <code>프로토타입 체인</code> 에 묶여있다.
자바스크립트 엔진은 객체의 프로퍼티에 접근하려고 할 때, 해당 객체에 접근하려는 프로퍼티가 없다면 <code>__proto__</code> 접근자 프로퍼티가 가리키는 참조를 따라 자신의 부모 역할을 하는 프로토타입의 프로퍼티를 순차적으로 검색한다.
프로토타입 체인의 종점인 최상위 객체는 <code>Object.prototype</code> 이며, 이 객체의 프로퍼티와 메서드는 모든 객체에 상속된다.</p>
<h4 id="-3--__proto__-접근자-프로퍼티를-통해-프로토타입에-접근하는-이유">-3- <code>__proto__</code> 접근자 프로퍼티를 통해 프로토타입에 접근하는 이유</h4>
<p>상호 참조에 의해 프로토타입 체인이 생성되는 것을 방지하기 위해 접근자 프로퍼티로 접근한다.</p>
<pre><code>const parent = {};
const child = {};

// child 의 프로토타입을 parent 로 설정
child.__proto__ = parent;

// parent 의 프로토타입을 child 로 설정
parent.__proto__ = child; // TypeError: Cyclic __proto__ value</code></pre><p>위의 예제에서는 parent 객체를 child 객체의 프로토타입으로 설정한 후 child 객체를 parent 객체의 프로토타입으로 설정했다. 이러한 코드가 에러 없이 처리되면 서로가 자신의 프로토타입이 되는 비정상적인 프로토타입 체인이 만들어지기 때문에 <code>__proto__</code> 접근자 프로퍼티는 에러를 발생시킨다.
<img src="https://velog.velcdn.com/images/april_5/post/7dcd23f4-b4fb-4e9a-88c4-7c20aeae9c83/image.png" alt=""></p>
<p>때문에 프로토타입 체인은 단방향 링크드 리스트로 구현되어야 한다.</p>
<h4 id="-4--__proto__-접근자-프로퍼티를-코드-내에서-직접-사용하는-것은-권장하지-않는다">-4- <code>__proto__</code> 접근자 프로퍼티를 코드 내에서 직접 사용하는 것은 권장하지 않는다.</h4>
<p><code>__proto__</code> 접근자 프로퍼티는 ES5 이전에 존재하지 않았고, 직접 상속을 통해 Object.prototype 을 상속받지 않는 객체를 생성할 수도 있어서 사용할 수 없는 경우가 있다.</p>
<pre><code>// obj는 프로토타입 체인의 종점이다. 따라서 Object.__prototype__을 상속받을 수 없다.
const obj = Object.create(null);

// obj는 Object.__proto__를 상속받을 수 없다.
console.log(obj.__proto__) // undefined

// 따라서 __proto__보다 Object.getPrototypeOf 메서드를 사용하는 편이 좋다.
console.log(Object.getPrototypeOf(obj)) // null</code></pre><p>따라서 <code>__proto__</code> 접근자 프로퍼티 대신 프로토타입의 참조를 취득하고 싶은 경우에는 <code>Object.getPrototypeOf</code> 메서드를 사용하고, 프로토타입을 교체하고 싶은 경우 <code>Object.setPrototypeOf</code> 메서드를 사용할 것을 권장한다.</p>
<pre><code>const obj = {};
const parent = { x: 1 };

// obj 객체의 프로토타입을 취득
Object.getPrototypeOf(obj); // obj.__proto__;

// obj 객체의 프로토타입을 교체
Object.setPrototypeOf(obj, parent); // obj.__proto__ = parent;</code></pre><p><code>Object.getPrototypeOf</code> 메서드와 <code>Object.setPrototypeOf</code> 메서드는 <code>get Object.prototype__proto__</code> 와 <code>set Object.prototype__proto__</code> 의 처리내용과 정확히 일치한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ JS ] 자바스크립트 딥다이브 스터디 - 14장 전역변수의 문제점]]></title>
            <link>https://velog.io/@space_developher/JS-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%94%A5%EB%8B%A4%EC%9D%B4%EB%B8%8C-%EC%8A%A4%ED%84%B0%EB%94%94-14%EC%9E%A5-%EC%A0%84%EC%97%AD%EB%B3%80%EC%88%98%EC%9D%98-%EB%AC%B8%EC%A0%9C%EC%A0%90</link>
            <guid>https://velog.io/@space_developher/JS-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EB%94%A5%EB%8B%A4%EC%9D%B4%EB%B8%8C-%EC%8A%A4%ED%84%B0%EB%94%94-14%EC%9E%A5-%EC%A0%84%EC%97%AD%EB%B3%80%EC%88%98%EC%9D%98-%EB%AC%B8%EC%A0%9C%EC%A0%90</guid>
            <pubDate>Sat, 15 Oct 2022 00:15:46 GMT</pubDate>
            <description><![CDATA[<h1 id="전역-변수의-문제점">전역 변수의 문제점</h1>
<h2 id="1-변수의-생명-주기">1. 변수의 생명 주기</h2>
<h3 id="1-지역-변수의-생명-주기">1) 지역 변수의 생명 주기</h3>
<blockquote>
<p>변수는 자신이 선언된 위치에서 생성되고 소멸한다.</p>
</blockquote>
<p>전역 변수는 애플리케이션의 생명 주기와 같다.
지역 변수는 함수가 호출되면 생성되고 함수가 종료하면 소멸한다.</p>
<pre><code>function foo(){
    var x=&#39;local&#39;;
    console.log(x); // local
    return x;
}

foo();
console.log(x); // ReferenceError: x is not defined</code></pre><p>위의 코드 예제에서 지역변수 <code>x</code>는 함수 <code>foo</code>가 호출되기 전에는 생성되지 않는다.
함수 내부의 변수 선언문이 실행되지 않기 때문이다.</p>
<h4 id="-1--함수-내-변수-선언은-언제-이뤄지는가">-1- 함수 내 변수 선언은 언제 이뤄지는가?</h4>
<p>변수의 선언 시점은 <code>런타임</code> 이전인 <code>코드 평가</code> 단계에서 호이스팅을 통해 최상단에서 이뤄진다.
함수 내부의 변수 선언은 <code>런타임</code>시 함수가 동작하며 <code>코드 평가</code>가 이뤄지기 떄문에, 함수가 호출되어 내부가 <code>코드 평가</code> 되는 시점에 변수 선언이 이뤄진다.</p>
<p><strong>즉, 지역변수의 생명 주기는 함수의 생명 주기와 일치한다</strong>.</p>
<p>단, 지역변수가 함수보다 오래 생존하는 경우가 존재하며, 이를 <code>클로저</code>라고 한다.
클로저와 관련한 내용은 차후 파트에서 상세히 다룰 예정이다.</p>
<p>변수는 값을 저장하기 위해 메모리 공간을 <code>확보</code> 한 시점부터 메모리 공간이 <code>해제</code> 되어 메모리 풀에 반환되는 시점까지를 의미한다.</p>
<p>함수 내부에서 선언된 지역 변수는 함수가 생성한 스코프에 등록된다. 이를 <code>렉시컬 스코프</code> 라고 부르며, 이 렉시컬 스코프가 메모리에서 소멸될 때까지 변수는 <code>유효</code> 하다.</p>
<p>할당된 메모리 공간은 어느 것도 참조하지 않을 때 <code>가비지 콜렉터</code> 에 의해 해제되어 메모리 풀에 반환된다. 다시 이야기하면, 참조하고 있다면 메모리 공간은 <code>확보된 상태</code> 로 남아있게된다.</p>
<p>일반적으로는 함수가 종료하면 함수가 생성한 스코프로 소멸하지만, 스코프가 참조되고 있다면 해당 스코프는 해제되지 않고 생존한다. 이를 앞서 이야기한 <code>클로저</code> 라고 한다.</p>
<h4 id="-2--호이스팅의-발생-범위">-2- 호이스팅의 발생 범위</h4>
<blockquote>
<p><code>코드 평가</code> 가 이뤄질 때 <code>변수 선언</code>을 진행하며, 이때 호이스팅도 함께 이뤄진다.
따라서, 호이스팅은 스코프 단위로 동작한다.</p>
</blockquote>
<h3 id="2-전역-변수의-생명-주기">2) 전역 변수의 생명 주기</h3>
<p>전역 코드는 <code>애플리케이션</code>이 로드되어 코드가 로드 되면 바로 <code>실행</code> 되며, 더 이상 실행할 문이 없을 때 <code>종료</code> 한다.</p>
<p><code>var</code> 키워드로 선언한 전역 변수는 <code>전역 객체</code> 의 프로퍼티가 된다.
이는, 전역 변수의 생명주기가 전역 객체의 생명 주기와 일치한다는 것을 말한다.</p>
<h4 id="-1--전역-객체">-1- 전역 객체</h4>
<p>코드가 실행되기 이전에 자바스크립트 엔진에 의해 가장 먼저 생성되는 <code>특수한 객체</code> 이다.
클라이언트 사이드 환경(브라우저) 에서는 <code>window</code>, 서버 사이드 환경(Node.js)에서는 <code>global</code> 객체를 의미한다.</p>
<p>환경에 따라 전역 객체를 가리키는 다양한 식별자(window, self, this, global)가 존재하였으나, ES11 에서는 <code>globalThis</code> 로 통일되었다.</p>
<p><code>전역 객체</code>는 <code>표준 빌트인 객체</code>(Object, String, Number, Function, Array, ... )와 환경에 따른 <code>호스트 객체</code>(클라이언트 Web API 또는 Node.js의 호스트 API) 그리고, var 키워드로 선언한 <code>전역 변수</code>와 <code>전역 함수</code>를 프로퍼티로 갖는다.</p>
<h2 id="2-전역-변수의-문제점">2. 전역 변수의 문제점</h2>
<h3 id="암묵적-결합">암묵적 결합</h3>
<p>전역 변수를 사용할 경우 코드 어디에서든 참조하고 할당할 수 있다.
이는 <code>암묵적 결합</code> 을 허용하는 것으로, 상태가 의도하지 않게 변경될 수 있는 위험성도 높아진다.</p>
<h3 id="긴-생명-주기">긴 생명 주기</h3>
<p>전역 변수는 애플리케이션이 종료될 때 함께 소멸되므로, 메모리 리소스도 오랜 기간동안 소비한다.
또한, 의도치 않게 변수 이름이 중복되어 의도치 않는 재할당이 이뤄질 수도 있다.</p>
<pre><code>var x = 1;
// ...
var x = 100;

console.log(x); // 100</code></pre><h3 id="스코프-체인-상에서-종점에-존재한다">스코프 체인 상에서 종점에 존재한다.</h3>
<p>자바스크립트는 스코프 체인을 통해 값을 찾아가는데, 전역 변수가 가장 마지막에 검색된다는 것을 의미한다.
그렇기 때문에 전역 변수의 검색 속도가 가장 느리다.</p>
<h3 id="네임스페이스-오염">네임스페이스 오염</h3>
<p>자바스크립트는 하나의 전역 스코프를 공유하기 때문에 다른 파일 내에서 동일한 이름으로 명명된 전역 변수나 전역 함수가 스코프 내에 존재할 경우 예상하지 못한 결과를 가져올 수 있다.</p>
<h2 id="3-전역-변수의-사용을-억제하는-방법">3. 전역 변수의 사용을 억제하는 방법</h2>
<p>지역 변수를 적극적으로 사용한다. (변수의 스코프는 좁을수록 좋다.)</p>
<h3 id="1-즉시-실행-함수">1) 즉시 실행 함수</h3>
<p>즉시 실행 함수는 함수 정의와 동시에 호출되어, 단 한번만 호출된다.
모든 코드를 즉시 실행 함수로 감싸면 모든 변수는 즉시 실행 함수의 지역 변수가 된다.</p>
<pre><code>(function (){
    var foo = 10; // 즉시 실행 함수의 지역 변수
}())

console.log(foo); // ReferenceError: foo is not defined</code></pre><h3 id="2-네임스페이스-객체">2) 네임스페이스 객체</h3>
<p>전역에 네임스페이스 역할을 담당할 객체를 생성하고, 전역 변수처럼 사용하고 싶은 변수를 프로퍼티로 추가하는 방식이다.</p>
<pre><code>var MYAPP = {}; // 전역 네임스페이스 객체
MYAPP.name = `Lee`;

console.log(MYAPP.name); // Lee</code></pre><p>네임스페이스 객체에 또 다른 네임스페이스 객체를 추가해서 계층으로 구성할 수도 있다.</p>
<pre><code>...
MYAPP.person = {
    name: `Lee`,
    age: 30
}

console.log(MYAPP.person.name); // name</code></pre><p>식별자 충돌을 방지하는 효과는 있으나, 이 또한 전역 변수에 해당되므로 유용한 방법은 아니다.</p>
<h3 id="3-모듈-패턴">3) 모듈 패턴</h3>
<p>즉시 실행 함수로 감싸 하나의 모듈을 만들 수 있다.
전역 변수의 억제 및 캡슐화도 할 수 있다는 점에서 강점이 있다.</p>
<pre><code>var Counter = (function (){
    // private 변수
    var num = 0;

    return {
        increase(){
            return ++num;
        },
        decrease(){
            return --num;
        }
    }
}())

private 변수는 외부로 노출되지 않는다.
console.log(Counter.num); //undefined

console.log(Counter.increase()); // 1
console.log(Counter.increase()); // 2
console.log(Counter.increase()); // 3
console.log(Counter.decrease()); // 2</code></pre><p>즉시 실행 함수의 결과로 반환되는 객체는 외부로 노출되는 <code>퍼블릭 멤버</code> 이며, 함수 내부에 존재하는 변수는 <code>프라이빗 멤버</code>이다.</p>
<h3 id="4-es6-모듈">4) ES6 모듈</h3>
<p>ES6 모듈은 파일 자체의 독자적인 모듈 스코프를 제공하며, 이에 따라 <code>var 키워드</code> 로 변수를 선언하더라도 전역변수로 처리되지 않는다.
<code>type=&quot;module&quot;</code> 어트리뷰트를 추가하면 로드된 자바스크립트 파일은 모듈로서 동작한다.</p>
<pre><code>&lt;script type=&quot;module&quot; src=&quot;lib.mjs&quot;&gt;&lt;/script&gt;</code></pre><p>ES6 모듈을 사용하기 위해서는 <code>Webpack</code> 등의 모듈 번들러를 사용하는 것이 일반적이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ Git Hub ] Private Repository 잔디 심기]]></title>
            <link>https://velog.io/@space_developher/Git-Hub-Private-Repository-%EC%9E%94%EB%94%94-%EC%8B%AC%EA%B8%B0</link>
            <guid>https://velog.io/@space_developher/Git-Hub-Private-Repository-%EC%9E%94%EB%94%94-%EC%8B%AC%EA%B8%B0</guid>
            <pubDate>Thu, 06 Oct 2022 23:50:27 GMT</pubDate>
            <description><![CDATA[<h3 id="근면성실의-상징-잔디">근면성실의 상징 &lt;잔디&gt;</h3>
<p>개발자의 근면성실함을 시각적으로 보여주는 <code>Git Hub</code> 의 잔디심기
Repository 에 Commit 할 경우 작업 이력이 표시되어진다.</p>
<p>일반적으로 <code>Public Repository</code> 로 개발을 하기 때문에 <code>Private Repository</code> 로 작업한 Commit 의 경우 제대로 작업 이력이 표시되지 않을 수 있다.</p>
<p>내 소중한 잔디를 허무하게 날릴 수 없지..
<code>Private Repository</code> 를 확인해보자</p>
<h3 id="일반적-잔디창">일반적 잔디창</h3>
<p><img src="https://velog.velcdn.com/images/space_developher/post/9543f531-e485-4a2c-a750-860d0d6fb13c/image.png" alt=""></p>
<h3 id="private-repository-잔디창-보이기">Private Repository 잔디창 보이기</h3>
<p><img src="https://velog.velcdn.com/images/space_developher/post/02c1746a-993f-41bd-9476-cd302b07c98e/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/space_developher/post/9fb52f1f-51e1-4174-b03c-713ac1140342/image.png" alt=""></p>
<p>크게 어렵지 않다. 내가 고생해서 심은 잔디 관리를 놓치지 말자</p>
]]></description>
        </item>
    </channel>
</rss>