diff --git "a/hyunmin/ZeroBase/\354\236\205\353\254\270\354\236\220 \354\236\220\353\260\224\354\212\244\355\201\254\353\246\275\355\212\270/01. JS-\353\215\260\354\235\264\355\204\260.md" "b/hyunmin/ZeroBase/\354\236\205\353\254\270\354\236\220 \354\236\220\353\260\224\354\212\244\355\201\254\353\246\275\355\212\270/01. JS-\353\215\260\354\235\264\355\204\260.md"
new file mode 100644
index 0000000..6cef046
--- /dev/null
+++ "b/hyunmin/ZeroBase/\354\236\205\353\254\270\354\236\220 \354\236\220\353\260\224\354\212\244\355\201\254\353\246\275\355\212\270/01. JS-\353\215\260\354\235\264\355\204\260.md"
@@ -0,0 +1,307 @@
+> 제로베이스 자바스크립트 기초개념 JS 데이터 부분 정리
+> 축약된 부분이 존재할 수 있습니다.
+
+# 원시형 데이터
+
+### 문자 데이터
+
+JS에서 문자 데이터를 만드는 방법은 세 가지가 존재한다.
+
+> " ", ' ', \` \`
+
+백틱 같은 경우에는 문자 데이터 중간에 변수를 보간할 수 있다.
+
+```js
+const s1 = "황현민";
+const s2 = "18살";
+
+// 템플릿 리터럴
+// 이런식으로 중간에 값을 넣는 것을 데이터 보간이라고
+const s3 = `안녕하세요! 제 이름은 ${s1}이고 나이는 ${s2}입니다.`;
+```
+
+### 숫자 데이터
+
+JS에서 숫자 데이터는 정수 및 부동소수점 숫자를 나타낸다.
+
+```JS
+const n1 = 123
+const n2 = 123.1233
+
+// 만약 산술연산을 다른 데이터와 하게 된다면?
+const n3 = 123 + "안녕" // 문자 데이터를 우선해서 문자 데이터가 된다.
+const n4 = 123 + undefined // NaN (숫자가 아닌 숫자)가 나온다.
+```
+
+### 불린(boolean) 데이터
+
+boolean은 true와 false를 가지는 참/거짓 논리 데이터이다.
+
+```js
+const a = true;
+const b = false;
+```
+
+### null 데이터
+
+null 데이터는 존재하지 않는(noting), 비어 있는(empty), 알 수 없는(unknown) 값을 명시적으로 나타낸다.
+
+```JS
+let age = null
+```
+
+### undefined 데이터
+
+undefined 데이터는 값이 할당되지 않은 상태를 나타낼 때 사용한다.
+변수는 선언했지만, 값을 할당하지 않았다면 해당 변수에 undefined가 암시적으로 사용된다.
+
+```js
+let age;
+console.log(age);
+```
+
+# 참조형 데이터
+
+### 배열 데이터
+
+배열(array) 데이터는 순서가 있는 여러 데이터의 집합이다.
+배열에 포함 각 데이터는 아이템(Item)혹은 요소(Element)라고 부른다.
+
+```js
+const f = ["사과", "수박", "메론"];
+const n = [123, 456, 789];
+
+// 배열으 길이 확인
+console.log(f.length); // 3
+console.log(n.length); // 5
+
+// 배열의 아이템 번호(index)로 아이템을 확인 (indexing)
+// 숫자는 0부터 시작한다. (Zero-Base-Numbering)
+console.log(f[2]); // 메론
+console.log(n[1]); // 456
+
+// 배열의 모든 아이템을 순회하고 싶다?
+for (let i = 0; i < f.length; i += 1) {
+ console.log(f[i]);
+}
+for (let i = 0; i < n.length; i += 1) {
+ console.log(n[i]);
+}
+```
+
+### 객체 데이터
+
+객체(object) 데이터는 순서가 없는 Key(키)와 Value(값)의 쌍으로 어우러진 데이터 집합이다.
+객체에 포함된 각 데이터를 속성(Property)라고 부르고,
+만약 그 데이터가 함수인 경우에는, 메소드(method)라고 부른다.
+
+```js
+const user = {
+ name: "현민",
+ age: 18,
+ isValid: true,
+ email: "gyejeongjin@gmail.com",
+ hello: function () {
+ return `내 이름은 ${this.name}이다. 내 나이는 ${this.age}`;
+ },
+};
+
+// 점 표기법(Dot Notation)을 사용해, 객체의 속성이나 메소드에 접근할 수 있다.
+console.log(user.name);
+console.log(user.age);
+console.log(user.isValid);
+console.log(user.email);
+console.log(user.hello());
+
+// 대괄호 표기법(Bracket Notation)을 사용해, 객체의 속성이나 메소드에 접근할 수 있다.
+console.log(user["name"]);
+console.log(user["age"]);
+console.log(user["isValid"]);
+console.log(user["email"]);
+console.log(user["hello"]());
+
+// 대괄호 표기법은 좀 더 동적으로 사용 할 수 있다.
+const key = "name";
+console.log(user[key]);
+```
+
+### 함수 데이터
+
+함수(Function) 데이터, 어떤 작업을 수행하기 위해 필요한 여러 코드의 집합으로, 코드를 추상화하고 재사용성을 확보한다.
+이 함수를 자바스크립트에서는 하나의 데이터 종류로 취급한다.
+
+```js
+// 함수 선언문(Declaration)
+function add(a, b) {
+ // console.log(a);
+ // console.log(b);
+ return a + b;
+}
+
+console.log(add);
+console.log(add(1, 2));
+console.log(add(31, 14));
+console.log(add(5, 8));
+
+// 함수 표현식
+const sub = function (a, b) {
+ return a - b;
+};
+
+console.log(sub);
+console.log(sub(2, 1));
+console.log(sub(14, 10));
+console.log(sub(8, 5));
+```
+
+# 변수
+
+변수(Variable)란, 데이터(값)의 이름을 지정한 것이다.
+이름이 있으면, 그 이름으로 언제든지 데이터를 재사용 할 수 있다.
+
+```js
+// const 키워드는 상수(Constant)를 의미하며, 한 번 선언하면 다른 값으로 변경 할 수 없다.
+const c = 12;
+console.log(c);
+console.log(c);
+// c = 34; // Error: Assignment to constant variable.
+// JS는 Error가 발생하면 아래 코드를 싹다 무시한다.
+
+// let 키워드는 선언한 값을 다른 값으로 바꿀 수 있다.
+let l = 12;
+console.log(l);
+console.log(l);
+l = 13;
+console.log(l);
+console.log(l);
+```
+
+일단 기본적으로 const를 사용하고 값을 바꿔야 한다면 let으로 바꿔서 사용하면 된다.
+
+# 형 변환
+
+형 변환(Type Convension)이란, 데이터가 상황에 따라 적절한 데이터 타입(자료형)으로 변환되는 것을 말한다.
+
+```js
+const a = 1;
+const b = "1";
+
+// == 동등 연산자
+console.log("동등", a == b); // true
+// === 일치 연산자
+cnsole.log("일치", a === b); // false
+
+// 다음 코드는 모두 true를 출력합니다.
+console.log("=================");
+console.log(123 == "123");
+console.log(1 == true);
+console.log(0 == false);
+console.log(null == undefined);
+console.log("" == false);
+// 동등연산자는 비교하는 개념에서는 명확하지 않아 쓰는걸 권장하지 않는다.
+
+// 다음 코드는 모두 false 출력합니다.
+console.log("=================");
+console.log(123 === "123");
+console.log(1 === true);
+console.log(0 === false);
+console.log(null === undefined);
+console.log("" === false);
+```
+
+# 참(Truthy)과 거짓(Falsy)
+
+```js
+// '참'으로 평가되는 값 (Truthy)
+if (true) {
+ console.log("참!");
+}
+if ({}) {
+ console.log("참!");
+}
+if ([]) {
+ console.log("참!");
+}
+if (42) {
+ console.log("참!");
+}
+if ("0") {
+ console.log("참!");
+}
+if ("false") {
+ console.log("참!");
+}
+if (new Date()) {
+ console.log("참!");
+}
+if (-42) {
+ console.log("참!");
+}
+if (12n) {
+ console.log("참!");
+}
+if (3.14) {
+ console.log("참!");
+}
+if (-3.14) {
+ console.log("참!");
+}
+if (Infinity) {
+ console.log("참!");
+}
+if (-Infinity) {
+ console.log("참!");
+}
+// ...
+
+// '거짓'으로 평가되는 값 (Falsy)
+if (false) {
+ console.log("거짓..");
+}
+if (null) {
+ console.log("거짓..");
+}
+if (undefined) {
+ console.log("거짓..");
+}
+if (42) {
+ console.log("거짓..");
+}
+if (0) {
+ console.log("거짓..");
+}
+if (-0) {
+ console.log("거짓..");
+}
+if (NaN) {
+ console.log("거짓..");
+}
+if (0n) {
+ console.log("거짓..");
+}
+if ("") {
+ console.log("거짓..");
+}
+// ...
+```
+
+참인 값은 너무 많기 때문에, 거짓인 값만 외워주면 된다.
+
+# 데이터 타입 확인
+
+지금까지 배운 데이터 타입을 코드상에서 확인하는 방법 알아보기
+
+```JS
+const data = {
+ string: "123",
+ number: 123,
+ boolean: true,
+ null: null,
+ undefined: undefined,
+ array: [1, 2, 3],
+ object: { a: 1, b: 2, c: 3 },
+ function: function () {},
+};
+```
+
+이런 객체가 존재할 때, 어떤식으로 데이터 타입을 확인할 수 있을지 알아보자.
diff --git "a/hyunmin/ZeroBase/\354\236\205\353\254\270\354\236\220 \354\236\220\353\260\224\354\212\244\355\201\254\353\246\275\355\212\270/02. JS-\354\227\260\354\202\260\354\236\220\354\231\200 \352\265\254\353\254\270.md" "b/hyunmin/ZeroBase/\354\236\205\353\254\270\354\236\220 \354\236\220\353\260\224\354\212\244\355\201\254\353\246\275\355\212\270/02. JS-\354\227\260\354\202\260\354\236\220\354\231\200 \352\265\254\353\254\270.md"
new file mode 100644
index 0000000..30e05c4
--- /dev/null
+++ "b/hyunmin/ZeroBase/\354\236\205\353\254\270\354\236\220 \354\236\220\353\260\224\354\212\244\355\201\254\353\246\275\355\212\270/02. JS-\354\227\260\354\202\260\354\236\220\354\231\200 \352\265\254\353\254\270.md"
@@ -0,0 +1,297 @@
+> 제로베이스 자바스크립트 기초개념 연산자와 구문 부분 정리
+> 축약된 부분이 존재할 수 있습니다.
+
+# 연산자
+
+## 산술 연산자
+
+```js
+// 산술 연산자 (Arithmetic operator)
+
+console.log(1 + 2);
+console.log(5 - 7);
+console.log(3 * 4);
+console.log(10 / 2);
+console.log(7 % 5);
+```
+
+## 할당 연산자
+
+```js
+// 할당 연산자 (Assignment Operators)
+
+let a = 3;
+console.log(a);
+
+// a = a + 2
+a += 2;
+console.log(a);
+
+// a = a - 2
+a -= 2;
+console.log(a);
+
+// a = a * 2
+a *= 2;
+console.log(a);
+
+// a = a / 2
+a /= 2;
+console.log(a);
+
+// a = a % 2
+a %= 2;
+console.log(a);
+```
+
+## 증감 연산자
+
+```js
+// 증감 연산자 (Increment & Decrement Operators)는 변수를 1씩 더하거나 빼는 연산자이다.
+
+// ++ 기호가 뒤에 있는 경우
+let b = 3;
+console.log(b++);
+console.log(b);
+
+// ++ 기호가 앞에 있는 경우
+let c = 3;
+console.log(++c);
+console.log(c);
+
+// -- 기호가 뒤에 있는 경우
+let d = 3;
+console.log(d--);
+console.log(d);
+
+// -- 기호가 앞에 있는 경우
+let f = 3;
+console.log(--f);
+console.log(f);
+
+// 증감 연산자 보다는, 할당 연산자를 사용하는 것을 추천한다.
+let e = 3;
+e += 1;
+console.log(e);
+e -= 1;
+console.log(e);
+```
+
+## 부정 연산자
+
+```js
+// 부정 연산자(Negation Operator)는 참과 거짓의 반대값을 불린 데이터로 반환합니다.
+
+console.log(!true); // false
+console.log(!false); // true
+
+console.log("중첩 사용!!");
+console.log(!0); // true
+console.log(!!0); // false
+console.log(!!!0); // true
+console.log(!!!!0); // false
+
+console.log("거짓(Falsy)!!");
+console.log(!null); // true
+console.log(!NaN); // true
+console.log(!undefined); // true
+console.log(!""); // true
+
+console.log("참(Truthy");
+console.log(!{}); // false
+console.log(![]); // false
+console.log(!"A"); // false
+```
+
+## 비교 연산자
+
+```js
+// 비교 연산자(Comparison Operators)는 두 데이터를 비교할 때 사용한다.
+const a = 1;
+const b = 3;
+
+// 동등(형 변환!)
+console.log(a == b);
+
+// 부등(형 변환!)
+console.log(a != b);
+
+// 일치
+console.log(a === b);
+
+// 불일치
+console.log(a !== b);
+
+// 큼
+console.log(a > b);
+
+// 크거나 같음
+console.log(a >= b);
+
+// 작음
+console.log(a < b);
+
+// 작거나 같음
+console.log(a <= b);
+```
+
+## 논리 연산자
+
+강의가 현재 이상해서 다음껄로
+
+## 삼항 연산자
+
+```js
+// 삼항 연산자(Ternary Operator)
+// 조건 ? (조건이 참일 때 실행) : (조건이 거짓일 때 실행)
+
+const f = ["사과", "바나나", "체리"];
+
+// IF 조건문
+if (f.length > 0) {
+ console.log("과일이 존재합니다.");
+} else
+ console.log("과일이 존재하지 않습니다.");
+}
+
+// 삼항 연산자
+const message =
+ f.length > 0 ? "과일이 존재합니다." : "과일이 존재하지 않습니다";
+console.log(message);
+```
+
+## 전개 연산자
+
+```js
+// 전개 연산자(Spread Operator)
+
+// 배열 데이터
+const numbers = [1, 2, 3];
+console.log(numbers); // [1, 2, 3]
+console.log(...numbers); // 1, 2, 3
+
+const n1 = [1, 2, 3];
+const n2 = [2, 3, 4];
+const n3 = n1.concat(n2);
+const n4 = [...n1, ...n2];
+console.log(n3); // [1, 2, 3, 2, 3, 4]
+console.log(n4); // [1, 2, 3, 2, 3, 4]
+
+// 객체 데이터
+const o1 = { a: 1, b: 2, c: 3 };
+const o2 = { b: 99, c: 100, d: 101 };
+const o3 = Object.assign({}, o1, o2);
+const o4 = { ...o1, ...o2 };
+console.log(o3); // { a: 1, b: 99, c: 100, d: 101}
+console.log(o4); // { a: 1, b: 99, c: 100, d: 101}
+```
+
+# 구문
+
+## 조건문 -if
+
+```js
+const age = 20;
+if (age <= 20) {
+ console.log("성인");
+}
+
+const num = 7;
+if (num % 2 === 0) {
+ console.log("짝수");
+} else {
+ console.log("홀수");
+}
+
+const score = 100;
+if (score >= 90) {
+ console.log("A");
+} else if (score >= 80) {
+ console.log("B");
+} else if (score >= 70) {
+ console.log("C");
+} else if (score >= 60) {
+ console.log("D");
+} else {
+ console.log("F");
+}
+```
+
+## 조건문 -switch
+
+```js
+// switch (조건) {
+// case 값1:
+// break;
+// case 값2:
+// break;
+// case 값3:
+// break;
+// default:
+// }
+
+const prod = "스마트폰";
+switch (prod) {
+ case "노트북":
+ break;
+ case "스마트폰":
+ break;
+}
+```
+
+## 반복문 -for
+
+```js
+// for
+// for (초기화; 조건; 증감) {}
+for (let i = 0; i < 10; i += 1) {
+ console.log(i);
+}
+```
+
+## 반복문 -for of
+
+```js
+// for of 문
+// for (const 아이템변수 of 배열) {}
+const fs = ["사과", "바나나", "체리"];
+for (let i = 0; i < fs.length; i += 1) {
+ const f = fs[i];
+ console.log(f);
+}
+for (const f of fs) {
+ if (f === "체리") {
+ continue;
+ }
+ console.log(f);
+}
+```
+
+## 반복문 -for in
+
+```js
+// for in 문
+// for (const 키변수 in 객체) {}
+const user = {
+ name: "이백원",
+ age: 85,
+ isValid: true,
+ email: "gyejeongjin@gmail.com",
+};
+for (const key in user) {
+ if (key === "age") continue;
+ console.log(key, user[key]);
+}
+```
+
+## 반복문 -While
+
+```js
+// while 문
+// while (조건) {}
+let count = 1;
+while (count <= 10) {
+ count += 1;
+ console.log(count);
+}
+```
diff --git "a/hyunmin/ZeroBase/\354\236\205\353\254\270\354\236\220 \354\236\220\353\260\224\354\212\244\355\201\254\353\246\275\355\212\270/03. JS-\355\225\250\354\210\230.md" "b/hyunmin/ZeroBase/\354\236\205\353\254\270\354\236\220 \354\236\220\353\260\224\354\212\244\355\201\254\353\246\275\355\212\270/03. JS-\355\225\250\354\210\230.md"
new file mode 100644
index 0000000..b3660d1
--- /dev/null
+++ "b/hyunmin/ZeroBase/\354\236\205\353\254\270\354\236\220 \354\236\220\353\260\224\354\212\244\355\201\254\353\246\275\355\212\270/03. JS-\355\225\250\354\210\230.md"
@@ -0,0 +1,285 @@
+> 제로베이스 자바스크립트 기초개념 함수 부분 정리
+> 축약된 부분이 존재할 수 있습니다.
+
+# 함수
+
+## 함수 - 데이터와 호출
+
+함수 데이터를 만들고 사용하기위해서는 `호출`을 해야한다.
+이 둘의 차이를 알아보자.
+
+```js
+function hello() {
+ return "Hello";
+}
+
+// 함수 데이터(Data)
+console.log(hello); // 함수 덩어리(코드) 그 자체가 출력된다
+console.log(typeof hello); // function 타입이 나오게 된다.
+// 이렇게 사용하면 함수안에 코드를 사용하는게 아니라 함수 안 코드를 확인만 하는 것이 된다.
+
+// 함수 호출(Call)
+console.log(hello()); // 반환 데이터인 Hello가 출력된다.
+console.log(typeof hello()); // 반환하는 데이터의 타입인 string이 출력된다.
+```
+
+## 기명 함수 vs 익명 함수
+
+```js
+const h1El = document.querySelector("h1");
+
+// 기명 함수 - function 이름() {}
+function handler() {
+ console.log(h1El.textContent);
+}
+h1El.addEventListener("click", handler);
+
+// 익명 함수 - function () {}
+h1El.addEventListener("click", function () {
+ console.log(h1El.textContent);
+});
+
+// 이름있으면 기명 함수, 없으면 익명 함수
+// 상황에 맞게 사용하면 된다.
+```
+
+## 함수 - 선언과 표현
+
+```js
+// 함수 선언문(Declaration)
+function hello() {
+ console.log("Hello~");
+}
+
+// 함수 표현식(Expression)
+const world = function () {
+ console.log("World");
+};
+
+// 일반적으로는 어떻게 만들어서 사용하든 상관이 없음
+```
+
+## 호이스팅
+
+하지만 함수 선언문과 표현식의 결정적인 차이는 호이스팅 가능 여부이다.
+
+일반적으로 자바스크립트 코드는 위에서 아래로 읽어진다.
+원래는 world처럼 선언되기 전에 사용하면 에러가 나는 것이 맞지만, 호이스팅이라는 개념이 적용되면 hello함수처럼 호출을 하고 정의를 하는 것이 가능해진다.
+
+호이스팅은 끌어올려지다라는 의미를 지니고 있다.
+함수 선언문으로 만들어진 함수는 자바스크립트가 위로 끌어올린다.
+하지만 함수 표현식은 위로 끌어올려지지 않기 때문에 선언문처럼 사용될 수 없다.
+
+```js
+// 호이스팅(Hoisting)
+hello(); // OK!
+world(); // Error..
+
+// 함수 선언문(Declaration)
+function hello() {
+ console.log("Hello~");
+}
+
+// 함수 표현식(Expression)
+const world = function () {
+ console.log("World");
+};
+```
+
+## 함수 - 반환과 종료
+
+```js
+function sayHi(name) {
+ return `Hi, ${name}`; // return시 함수는 종료된다.
+ console.log("동작하지 않음");
+}
+const h = sayHi("200woni"); // 반환
+console.log(h); // "Hi, 200woni"
+
+console.log(sayHi("200woni")); // "Hi, 200woni"
+```
+
+또한, 우리가 반환을 할 때 반환값없이 해줄 수도 있다.
+
+```js
+function sayHi(name) {
+ return `Hi, ${name}`; // return시 함수는 종료된다.
+ console.log("동작하지 않음");
+}
+const h = sayHi("200woni"); // 반환
+console.log(h); // "Hi, 200woni"
+
+console.log(sayHi("200woni")); // "Hi, 200woni"
+//===============================================//
+function a() {
+ // return // undefined
+}
+function b() {
+ return; // undefined
+}
+function c() {
+ return undefined;
+}
+
+console.log(a()); // undefined
+console.log(b()); // undefined
+console.log(c()); // undefined
+
+// 결국 3가지가 다 똑같은 명시하느냐 안하느냐의 차이
+```
+
+## 인수와 매개변수
+
+함수 호출시에 넣어주는 값은 인수, 함수 정의할 때 인수를 받아줄 매개체가 되어주는 변수가 매개변수이다.
+
+```js
+// 인수(Argument)와 매개변수(Parameter)
+
+function add(a, b) {
+ return a + b;
+}
+console.log(add(2, 1));
+console.log(add(7, 4));
+console.log(add("A", "B"));
+```
+
+### 매개변수의 기본값
+
+```js
+// 매개변수의 기본값(Default Value)
+function add(a, b = 1) {
+ return a + b;
+}
+console.log(add(2));
+console.log(add(7, undefined)); // 위랑 같음
+console.log(add());
+```
+
+### 나머지 매개변수
+
+```js
+// 나머지 매개변수(Rest Parameter)
+
+function add(a, b, ...rest) {
+ console.log(a, b, rest);
+ return rest.reduce((acc, cur) => acc + cur, 0);
+}
+const res = add(1, 2, 3, 4, 5, 6, 7, 8);
+console.log(res);
+```
+
+## 화살표 함수
+
+```js
+// 일반 함수
+function hello1() {
+ return "Hello~";
+}
+const add1 = function (a, b) {
+ return a + b;
+};
+
+const log1 = function (c) {
+ console.log(c);
+};
+
+// 화살표 함수
+const hello2 = () => "Hello~";
+const add2 = (a, b) => a + b;
+const log2 = (c) => {
+ console.log(c);
+};
+```
+
+나중에 this라는 개념을 배우게 되면 일반 함수와 화살표 함수에서 사용하는 것이 다르기 때문에 화살표가 있으면 무조건 화살표 함수라는 것을 기억해두자!
+
+## 즉시실행함수
+
+```js
+// 함수 정의(표현)
+const double = () => {
+ // ...
+};
+// 함수 실행(호출)
+double();
+
+// 함수 정의 및 실행
+// 즉시실행함수(IIFE, Immediately Invoked Function Expression)
+(() => {
+ // ...
+})();
+
+// '즉시실행함수'의 다양한 사용법
+(() => {})();
+(function () {})();
+(function () {})()();
+!(function () {})();
++(function () {})();
+```
+
+## 콜백
+
+callback 그대로 뒤에서 호출되는 함수라는 것이다.
+간단하게 생각하면 함수의 인수로 사용되는 함수이다. 인수로 사용되었다는 것은 어디선가는 호출이 된다는 것이기에 뒤에 호출되는 함수인 콜백 함수 인 것이다.
+
+```js
+// 콜백
+
+const a = (callback) => {
+ console.log("A");
+ callback();
+};
+const b = () => {
+ console.log("B");
+};
+
+a(b);
+// "A"
+// "B"
+
+// ============================== //
+
+function add(a, b, cb) {
+ // 1초 뒤 실행
+ setTimeout(() => {
+ cb(a + b);
+ }, 1000);
+}
+add(3, 7, (result) => {
+ console.log(result);
+});
+```
+
+## 호출 스케줄링
+
+setTimeout
+
+```js
+// 콜백을 호출하는 타이머 설정
+const timeout = setTimeout(() => {
+ console.log("Hello~");
+}, 5000);
+
+// 타이머를 취소
+const btnEl = document.querySelector("button");
+btnEl.addEventListener("click", () => {
+ console.log("타이머 취소");
+ clearTimeout(timeout);
+});
+```
+
+setInterval
+
+```js
+// 콜백을 반복 호출하는 타이머 설정
+const timeout = setInterval(() => {
+ console.log("Hello~");
+}, 3000);
+
+// 타이머를 취소
+const btnEl = document.querySelector("button");
+btnEl.addEventListener("click", () => {
+ console.log("타이머 취소");
+ clearInterval(timeout);
+});
+```
diff --git "a/hyunmin/ZeroBase/\354\236\205\353\254\270\354\236\220 \354\236\220\353\260\224\354\212\244\355\201\254\353\246\275\355\212\270/04. JS-\355\221\234\354\244\200\353\202\264\354\236\245\352\260\235\354\262\264.md" "b/hyunmin/ZeroBase/\354\236\205\353\254\270\354\236\220 \354\236\220\353\260\224\354\212\244\355\201\254\353\246\275\355\212\270/04. JS-\355\221\234\354\244\200\353\202\264\354\236\245\352\260\235\354\262\264.md"
new file mode 100644
index 0000000..fe5c6f3
--- /dev/null
+++ "b/hyunmin/ZeroBase/\354\236\205\353\254\270\354\236\220 \354\236\220\353\260\224\354\212\244\355\201\254\353\246\275\355\212\270/04. JS-\355\221\234\354\244\200\353\202\264\354\236\245\352\260\235\354\262\264.md"
@@ -0,0 +1,889 @@
+> 제로베이스 자바스크립트 기초개념 표준 내장 객체 부분 정리
+> 축약된 부분이 존재할 수 있습니다.
+
+# 표준 내장 객체 함수
+
+간단하게 우리가 어떤 데이터에서 필요할 때, 바로바로 쓸 수 있는 기능이라고 생각하면된다. (자주 사용하는 함수만 적어두었음)
+
+## String
+
+String(문자) 표준 내장 객체(Built-in Object)
+
+### .length
+
+문자의 길이를 반환한다.
+
+```js
+const msg1 = "안녕하세요 반갑습니다.";
+console.log(msg1.length);
+
+const msg2 =
+ "저는 부산에서 살고 있습니다. 저는 부산소프트웨어마이스터고등학교를 다니고 있습니다.";
+console.log(msg2.length);
+```
+
+### .includes()
+
+문자에서 특정 문자 포함되어 있는지 확인합니다.
+
+```js
+const msg1 = "Hello World!";
+const msg2 = "안녕하세요";
+
+console.log(msg1.includes("!"));
+console.log(msg2.includes("안녕"));
+```
+
+### .replace()
+
+문자에서 특정 문자를 다른 문자로 바꾼 새로운 문자를 **반환**합니다.
+하지만, 첫 번째로 조건에 맞는 문자를 만나면 그 문자만 변경한다.
+
+```js
+const msg1 = "Hello World!";
+const msg2 = "안녕하세요";
+
+console.log(msg1.replace("World", "월드"));
+console.log(msg2.replace("안녕하", "안녕히계"));
+```
+
+### .replaceAll()
+
+문자에서 특정 문자를 다른 문자로 모두 바꾼 새로운 문자를 반환합니다.
+즉, replace의 전체 버전이라고 보면 된다.
+
+```js
+const msg3 = "안 녕 하 세 요";
+
+console.log(msg1.replaceAll(" ", ""));
+```
+
+### .slice()
+
+문자에서 일부를 추출해 새로운 문자를 반환합니다.
+
+```js
+const msg1 = "Hello World!";
+
+console.log(msg1.slice(0, 5)); // "Hello"
+console.log(msg1.slice(6, -1)); // "World"
+console.log(msg.slice(6)); // "World!"
+```
+
+### .split()
+
+문자를 구분자로 나누어 배열로 반환합니다.
+
+```js
+const msg1 = "Hello World!";
+
+console.log(msg1.split(" ")); // ["Hello", "World!"]
+// 아래처럼 응용도 가능
+console.log(msg1.split("").reverse().join("")); // "!dlroW olleH"
+```
+
+### .toLowerCase()
+
+문자를 영어 소문자로 바꾼 새로운 문자를 반환합니다.
+
+```js
+const msg1 = "Hello World!";
+
+console.log(msg1.toLowerCase());
+```
+
+### .toUpperCase()
+
+문자를 영어 대문자로 바꾼 새로운 문자를 반환합니다.
+
+```js
+const msg1 = "Hello World!";
+
+console.log(msg1.toUpperCase());
+```
+
+### .trim()
+
+문자에서 앞뒤 공백을 제거한 새로운 문자를 반환합니다.
+
+```js
+const msg4 = " Hello!!! ";
+
+console.log(msg4.trim());
+```
+
+## Number
+
+Number(숫자) 표준 내장 객체(Built-in Object)
+
+### .toFixed()
+
+숫자에서 지정된 자릿수까지 표현하는 새로운 문자를 반환합니다.
+
+```js
+const num = 3.141592;
+
+console.log(num.toFixed(2)); // "3.14"
+console.log(Number(num.toFixed(2))); // 3.1
+```
+
+### .toLocalString()
+
+숫자에서 현지 언어 형식으로 바꾼 새로운 문자를 반환합니다.
+
+```js
+const num1 = 1000;
+const num2 = 100000000;
+
+console.log(num1.toLocaleString());
+console.log(num2.toLocaleString());
+```
+
+### Number(), Number.parseInt(), Number.parseFloat()
+
+이 함수는 위 함수들과 다르게 클래스 메소드이기 때문에 데이터.메소드의 프로토타입 메소드와 다르게 정적 메소드라고 불린다. (Number는 클래스를 호출하는 방식)
+지금은 데이터에서 사용되냐 클래스에서 사용되냐만 알면 되고, 나중에 클래스파트에서 자세히 알아볼 것이다.
+
+**Number()** 같은 경우는 광범위하게 해석을 하고, 유연하게 사용할 수 있다.
+`Number(데이터)`
+하지만 숫자와 불리언형정도가 제대로 변환이 되고 다른 것들은 NaN이 뜬다.
+즉, Number()는 간단하게 숫자 변환을 할 때는 괜찮지만 명확하게 변환하고 싶다고 한다면 어울리지 않는다.
+
+**Number.parseInt()** 같은 경우에는 숫자를 추출해 명확하고 에측가능하게 동작하고, 진법지정이 가능하고, 정수만 처리한다.
+`Number.parseInt("문자", 진수)`
+즉, 문자를 정수로 변환하는 기능을 한다.
+
+```js
+console.log(Number.parseInt(123abc, 10)) // 123
+```
+
+Number보다는 조금 더 정확하고 예측가능하게 변환하고 싶다면 사용하면 된다.
+
+**Nuber.parseFloat()** 같은 경우 parseInt + 부동소수점 실수 처리 가능이라고 보면 된다.
+`Number.parseFloat("문자")`
+
+```js
+console.log(Number.parseFloat("123.123"));
+```
+
+그 외에 parseInt와 다른점은 없다.
+
+### Number.isInteger()
+
+데이터가 정수(숫자 데이터)인지 확인합니다.
+`Number.isInteger(데이터)`
+위 처럼 사용하고 Boolean값으로 반환해준다.
+
+### Number.isNaN()
+
+데이터가 'NaN'인지 확인합니다.
+`Number.isNaN()`
+위 처럼 사용하고 Boolean값으로 반환해준다.
+
+## Math
+
+Math(수학) 표준 내장 객체(Built-in Object)
+
+### Math.abs()
+
+주어진 숫자의 절댓값을 반환합니다.
+
+```js
+console.log(Math.abs(7)); // 7
+console.log(Math.abs(-7)); // 7
+console.log(Math.abs(3.14)); // 3.14
+console.log(Math.abs(-3.14)); // 3.14
+```
+
+### Math.ceil()
+
+주어진 숫자를 올림해 **정수**를 반환합니다.
+
+```js
+console.log(Math.ceil(3.14141411)); // 4
+console.log(Math.ceil(3.14141441 * 100) / 100); // 3.15
+```
+
+### Math.floor()
+
+주어진 숫자를 내림해 **정수**를 반환합니다.
+
+```js
+console.log(Math.floor(3.14141411)); // 3
+console.log(Math.floor(3.14141441 * 100) / 100); // 3.14
+```
+
+### Math.round()
+
+주어진 숫자를 반올림해 **정수**를 반환합니다.
+
+```js
+console.log(Math.round(3.1)); // 3
+console.log(Math.round(3.6)); // 4
+```
+
+### Math.max()
+
+주어진 숫자중 가장 큰 숫자를 반환합니다.
+
+```js
+console.log(Math.max(10, 128, 12, 49, 1)); // 128
+```
+
+### Math.min()
+
+주어진 숫자중 가장 작은 숫자를 반환합니다.
+
+```js
+console.log(Math.min(10, 128, 12, 49, 1)); // 1
+```
+
+### Math.random()
+
+'0' 이상 '1' 미만의 난수(무작위 수)를 반환합니다.
+
+```js
+console.log(Math.random());
+
+// 원하는 범위의 랜덤값 뽑기
+function Random(min = 0, max = 10) {
+ return Math.floor(Math.random() * (max - min)) + min;
+}
+
+console.log(Random()); // 0~9
+console.log(Random(0, 100)); // 0~99
+console.log(Random(101, 200)); // 101~199
+```
+
+## Date
+
+Date(날짜) 표준 내장 객체(Built-in Object)
+
+일단 Data는 날짜를 뽑는 클래스이다.
+
+```js
+// 'new Date()'를 통해서 반환된 인스턴스를 '타임 스탬프(timestamp)'라고 한다.
+let date = new Date();
+console.log(date); // 현재 날짜가 나옴 (타임 스탬프)
+
+// 우리가 원하는 날짜로 생성 할 수도 있다.
+date = new Date(2006, 4, 8, 0, 0, 0);
+console.log(date);
+
+// 타임 스탬프에서 각 정보를 얻을 수 있다.
+console.log(date.getFullYear);
+console.log(date.getMonth + 1); // 1~12가 아닌 0~11을 반환함
+console.log(date.getDate);
+console.log(getDayKo(date.getay)); // 애도 0부터 시작해서 일요일 = 0 토요일 6이다. (함수 만드는 것도 좋은 선택)
+console.log(date.getHours);
+console.log(date.getMinutes);
+console.log(date.getSeconds);
+
+function getDayKo(day) {
+ switch (day) {
+ case 0:
+ return "일요일";
+ case 1:
+ return "월요일";
+ case 2:
+ return "화요일";
+ case 3:
+ return "수요일";
+ case 4:
+ return "목요일";
+ case 5:
+ return "금요일";
+ case 6:
+ return "토요일";
+ }
+}
+```
+
+### .getTime()
+
+유닉스 타임(UNIX Time)으로부터 경과한 시간(ms)을 반환합니다.
+유닉스 타임은 1970.01.01 00:00:00 시간을 의미합니다.
+
+```js
+const date1 = new Date();
+console.log(date1.getTime());
+```
+
+### Date.now()
+
+현재 시간을 유닉스 타임으로 변환합니다.
+유닉스 타임은 1970.01.01 00:00:00 시간을 의미합니다.\
+
+```js
+const date2 = Date.now();
+console.log(date2);
+```
+
+### .toISOString()
+
+날짜 인스턴의 협정 세계시(UTC)를 'ISO 8601' 포맷으로 변환합니다.
+'ISO 8601'은 날짜와 시간을 표현하는 국제 표준 규격입니다.
+
+```js
+console.log(new Date().toISOString);
+```
+
+우리가 데이터를 저장할 때, 데이터베이스에 저장해야하는 경우가 생길 수도 있는데 이러한 상황에서 전세계로 사용되는 서비스라면 한국 표준시보다는 국제 표준 포멧으로 바꿔서 저장할 떄 유용할 것이다.
+
+## Array
+
+Array(배열) 표준 내장 객체(Built-in Object)
+
+### .length
+
+배열의 길이(숫자)를 반환한다.
+
+```js
+const fruits = ["Apple", "Banana", "Cherry"];
+
+console.log(fruits.length);
+```
+
+### .at
+
+배열을 인덱싱하며, 만약 음수를 사용하면 뒤에서부터 인덱싱합니다.
+
+```js
+const fruits = ["Apple", "Banana", "Cherry"];
+
+console.log(fruits.at(1));
+console.log(fruits.at(-1)); // 마지막 요소, 뒤에서부터 인덱싱 가능
+```
+
+### .concat()
+
+배열에서 주어진 배열을 병합해 새로운 배열을 반환합니다.
+
+```js
+const name = ["현민", "황현민"];
+const age = ["18", "19"];
+const na = this.name.concat(age);
+console.log(na);
+```
+
+### .every()
+
+배열의 모든 요소가 콜백 테스트를 통과하는지 확인합니다.
+만약 테스트가 하나라도 실패하면, 이후 테스트를 진행하지 않고 'false'를 반환합니다.
+
+```js
+const numbers = ["12", "456", "132", "60", "1"];
+const isValid = numbers.every((item) => item < 200);
+console.log(isValid);
+
+const users = [
+ { name: "현민", age: 18 },
+ { name: "200원", age: 200, email: "200@gmail.com" },
+ { name: "황현민", age: 19, email: "gyejeongjin@gmail.com" },
+];
+console.log(users.every((user) => user.email));
+console.log(users.every((user) => user.age));
+```
+
+### .filter()
+
+배열에서 콜백 테스트를 통과하는 모든 요소로 새로운 배열을 만들어 반환합니다.
+만약 모든 요소가 테스트를 통과하지 못하며 빈 배열을 반환합니다.
+
+```js
+const numbers = ["12", "456", "132", "60", "1"];
+const filterNumbers = numbers.filter((number) => number < 30);
+console.log(filterNumbers);
+
+const users = [
+ { name: "현민", age: 18 },
+ { name: "200원", age: 200, email: "200@gmail.com" },
+ { name: "황현민", age: 19, email: "gyejeongjin@gmail.com" },
+];
+const youngUsers = users.filter((user) => user.age < 30);
+console.log(youngUsers);
+const userWithEmail = users.filter((user) => user.email);
+console.log(userWithEmail);
+const userWithPhone = users.filter((user) => user.phone);
+console.log(userWithPhone);
+```
+
+### .find()
+
+배열에서 콜백 테스트를 처음으로 통과하는 요소를 반환합니다.
+만약 테스트를 통과하면, 이후 테스트는 진행하지 않습니다.
+만약 모든 테스트가 실패하면, 'undefined'를 반환합니다.
+
+```js
+const numbers = ["12", "456", "132", "60", "1"];
+const foundNumbers = numbers.find((number) => number < 30);
+console.log(foundNumbers);
+
+const users = [
+ { name: "현민", age: 18 },
+ { name: "200원", age: 200, email: "200@gmail.com" },
+ { name: "황현민", age: 19, email: "gyejeongjin@gmail.com" },
+];
+const foundUser = users.find((user) => !user.email);
+console.log(foundUser);
+```
+
+### .findIndex()
+
+배열에서 콜백 테스트를 처음으로 통과하는 요소의 인덱스를 반환합니다.
+만약 테스트가 통과하면, 이후 테스트를 진행하지 않습니다.
+만약 모든 테스트가 실패하면, -1을 반환합니다.
+
+```js
+const numbers = ["12", "456", "132", "60", "1"];
+const foundIndex = numbers.findIndex((number) => number < 30);
+console.log(foundIndex);
+
+const users =
+ { name: "현민", age: 18 },
+ { name: "200원", age: 200, email: "200@gmail.com" },
+ { name: "황현민", age: 19, email: "gyejeongjin@gmail.com" },
+];
+const foundUserIndex = users.findIndex((user) => !user.email);
+console.log(foundUserIndex);
+```
+
+### .forEach()
+
+배열의 각 요소에 대해 콜백을 호출합니다.
+만약 배열이 비어있다면, 아무런 동작도 하지 않습니다.
+만약 반복을 종료하고 싶다면, for반복문을 사용하여야 합니다.
+
+```js
+const numbers = ["12", "456", "132", "60", "1"];
+numbers.forEach((number) => {
+ console.log(number);
+});
+
+let sum = 0;
+numbers.forEach((number) => {
+ sum += number;
+});
+console.log("합계: ", sum);
+
+for (const number of numbers) {
+ if (number > 100) {
+ break;
+ }
+ console.log(number);
+}
+```
+
+### .includes()
+
+배열에서 특정 요소가 포함되어 있는지 확인합니다.
+
+```js
+const f = ["사과", "바나나", "체리"];
+console.log(f.includes("사과"));
+console.log(f.includes("버나나")); // false
+
+const n = [10, 20, 30, 40];
+console.log(n.includes(10));
+console.log(n.includes(123123));
+```
+
+### .join()
+
+배열의 모든 요소를 연결해 하나의 문자열로 만듭니다.
+
+```js
+const f = ["사과", "바나나", "체리"];
+console.log(f.join());
+console.log(f.join(""));
+console.log(f.join(", "));
+console.log(f.join("/"));
+
+const msg = "Hello World";
+console.log(msg.split("").reverse().join(""));
+```
+
+### .map()
+
+배열의 모든 요소에 대해 각 콜백을 호출하고 반환된 결과로 새로운 배열을 반환한다.
+
+```js
+const numbers = [17, 20, 100, 5, 200];
+const doubleNumbers = numbers.map((number) => number * 2);
+console.log(doubleNumbers);
+console.log(numbers);
+
+const fruits = ["apple", "banana", "cherry"];
+const capitalizedFruits = fruits.map((fruit) => fruit.toUpperCase());
+console.log(capitalizedFruits);
+console.log(fruits);
+
+const users = [
+ { name: "현민", age: 18 },
+ { name: "200원", age: 200, email: "200@gmail.com" },
+ { name: "황현민", age: 19, email: "gyejeongjin@gmail.com" },
+];
+
+const userEmails = users.map((user) => user.email);
+console.log(userEmails);
+console.log(userEmails.filter((email) => email));
+
+const f = ["사과", "바나나", "체리"];
+const UpperF = f.map((e, i) => {
+ console.log(i);
+});
+```
+
+### .push()
+
+배열의 마지막에 하나 이상의 요소를 추가하고, 배열의 새로운 길이를 반환합니다.
+배열 원본이 변경됩니다.
+
+```js
+const fruits = ["apple", "banana", "cherry"];
+console.log(fruits.push("durian"));
+console.log(fruits.length);
+console.log(fruits);
+
+const numbers = [17, 20, 100, 5, 200];
+console.log(numbers.push(9, 10, 11));
+console.log(numbers.length);
+console.log(numbers);
+```
+
+### .reduce()
+
+배열의 각 요소에 대해 콜백을 호출하고, 각 콜백의 반환 값을 다음 콜백으로 전달해 마지막 콜백의 반환 값을 최종 반환합니다.
+
+```js
+const numbers = [10, 19, 29, 13, 120, 123];
+
+let sum1 = 0;
+numbers.forEach((number) => {
+ sum += number;
+});
+console.log(sum);
+
+// number가 배열안에 값, accumulator가 누적값
+const sum2 = numbers.reduce((accumulator, number) => {
+ return accumulator + number;
+}, 0); // 콜백 함수, 초기값
+console.log(sum2);
+
+const users = [
+ { name: "현민", age: 18 },
+ { name: "200원", age: 200, email: "200@gmail.com" },
+ { name: "황현민", age: 19, email: "gyejeongjin@gmail.com" },
+];
+const sum3 = users.reduce((acc, user) => acc + user.age, 0);
+console.log(sum3);
+```
+
+### .reverse()
+
+배열의 순서를 반전합니다.
+배열 원본이 변경됩니다.
+
+```js
+const fruits1 = ["APPLE", "BANANA", "CHERRY"];
+console.log(fruits1.reverse());
+console.log(fruits1);
+
+// 원본이 변경 안되었으면 좋겠다?
+const fruits2 = ["APPLE", "BANANA", "CHERRY"];
+console.log([...fruits2].reverse());
+console.log(fruits2);
+```
+
+### .slice()
+
+배열의 일부를 추출해 새로운 배열로 반환합니다.
+
+```js
+const numbers = [100, 200, 300, 400, 500, 600, 700, 800, 900];
+
+console.log(numbers.slice(0, 3));
+console.log(numbers.slice(4, -1));
+console.log(numbers.slice(4));
+console.log(numbers.slice(-4));
+console.log(numbers.slice(-4, -1));
+console.log(numbers);
+```
+
+### .some()
+
+배열의 요소 중 콜백 테스트를 통과하는 요소가 하나라도 있는지 확인합니다.
+만약 테스트가 통과하면, 이후 테스트는 진행하지 않습니다.
+.every의 하나인 버전이라고 생각하면 된다.
+
+```js
+const numbers = [17, 20, 199, 1, 40];
+const isValid = numbers.some((number) => number > 200);
+console.log(isValid);
+
+const users = [
+ { name: "현민", age: 18 },
+ { name: "200원", age: 200, email: "200@gmail.com" },
+ { name: "황현민", age: 19, email: "gyejeongjin@gmail.com" },
+];
+console.log(users.some((user) => user.email));
+console.log(users.some((user) => user.phone));
+```
+
+### .sort()
+
+배열의 요소를 콜백의 반환 값에 따라 정렬합니다.
+만약 콜백을 제공하지 않으면, 요소를 유니코드 포인트 순서대로 정렬합니다.
+배열 원본이 변경됩니다.
+
+```js
+const numbers = [17, 20, 199, 1, 40];
+
+numbers.sort();
+console.log(numbers);
+
+numbers.sort((a, b) => a - b);
+console.log(numbers);
+
+numbers.sort((a, b) => b - a);
+console.log(numbers);
+
+const users = [
+ { name: "현민", age: 18 },
+ { name: "200원", age: 200, email: "200@gmail.com" }
+ { name: "황현민", age: 19, email: "gyejeongjin@gmail.com" }
+]
+
+users.sort((a, b) => a.age - b.age);
+console.log(...users);
+
+users.sort((a, b) => b.age - a.age);
+console.log(...users);
+
+// 콘솔창에서 볼 떄는 sort라는 것이 딱 한번만 일어난 것이기 때문에 users를 펼쳐서 볼 때 값이 바뀌게 된다.
+// 그래서, 배열이나 객체 데이터를 출력할때, 내용이 많아서 숨겨진다면 ...users처럼 숨겨지지 않는 상태로 바꿔서 출력해야 한다.
+```
+
+### .splice(인덱스, 삭제개수, 추가요소)
+
+배열의 요소를 추가하거나 삭제하거나 교체합니다.
+배열 원본이 변경됩니다.
+
+```js
+// splice(인덱스, 삭제개수, 추가요손)
+
+// 요소 추가
+const f1 = ["사과", "바나나", "체리"];
+f1.splice(2, 0, "두리안");
+console.log(f1);
+
+// 요소 삭제
+const f2 = ["사과", "바나나", "체리"];
+f2.splice(1, 1);
+console.log(f2);
+
+// 요소 교체
+const f3 = ["사과", "바나나", "체리"];
+f3.splice(1, 1, "두리안", "오렌지", "망고");
+```
+
+### .unshift()
+
+배열의 시작 부분에 하나 이상의 요소를 추가하고, 배열의 새로운 길이를 반환합니다.
+배열 원본이 변경됩니다.
+push의 반대 개념이라고 생각하면 된다.
+
+```js
+const f1 = ["사과", "바나나", "체리"];
+console.log(f1.unshift("두리안"));
+console.log(f1.length);
+console.log(f1);
+
+const numbers = [17, 20, 199, 60, 31];
+console.log(numbers.unshift(9, 10, 11));
+console.log(numbers.length);
+console.log(numbers);
+```
+
+### 배열 메소드 인덱스
+
+배열 메소드의 콜백은 항상 현재 반복의 인덱스를 얻을 수 있다.
+
+```js
+const numbers = [17, 20, 199, 60, 31];
+numbers.every((num, idx) => {
+ console.log(num, idx);
+ return true;
+});
+numbers.filter((num, idx) => {
+ console.log(num, idx);
+ return true;
+});
+numbers.reduce((acc, cur, idx) => {
+ console.log(acc, cur, idx);
+ return acc + cur;
+}, 0);
+```
+
+### Array.isArray()
+
+배열 데이터인지 확인합니다.
+
+```js
+const f = ["사과", "바나나", "체리"];
+// 유사배열
+const arrayLikeFruits = {
+ 0: "사과",
+ 1: "바나나",
+ 2: "체리",
+ length: 3,
+};
+
+console.log(Array.isArray(f));
+console.log(Array.isArray(arrayLikeFruits));
+```
+
+### Array.from()
+
+유사 배열(Array-Like)을 실제 배열로 반환합니다.
+
+```js
+const fruits = ["Apple", "Banana", "Cherry"];
+// 유사배열
+const arrayLikeFruits = {
+ 0: "Apple",
+ 1: "Banana",
+ 2: "Cherry",
+ length: 3,
+};
+console.log(fruits);
+console.log(arrayLikeFruits);
+
+console.log(fruits[1]);
+console.log(arrayLikeFruits[1]);
+
+console.log(fruits.length);
+console.log(arrayLikeFruits.length);
+
+console.log(Array.isArray(fruits));
+console.log(Array.isArray(arrayLikeFruits));
+
+console.log(fruits.map((fruit) => fruit.toUpperCase()));
+console.log(fruits.frm(arrayLikeFruits).map((fruit) => fruit.toUpperCase()));
+```
+
+## Object
+
+Object(객체) 표준 내장 객체(Built-in Object)
+
+### Object.assign(대상, 출처1, 출처2, ...)
+
+하나 이상의 '출처 객체(Source)'로부터 '대상 객체(Target)'로 속성을 복사하고 대상 객체를 반환합니다.
+
+```js
+const target = { a: 1, b: 2 };
+const source1 = { b: 3, c: 4 }
+const source2 = { c: 5, d: 6 };
+// 원본 객체 변함
+// const result = Object.assign(target, source1, source2);
+// console.log(target);
+// console.log(result);
+
+// 원본 객체 변하지 않음
+const result = Object.assign({}, target, source1, source2);
+console.log(target);
+console.log(result);
+
+const userA = {
+ name: "200원"
+ age: 200,
+};
+const userB = {
+ age: 22,
+ email: "gyejeongjin@gmail.com",
+ isValid: true,
+};
+// const thw = Object.assign(userA, userB);
+const thw = Object.assign({}, userA, userB);
+
+console.log(thw);
+console.log(userA);
+```
+
+### Object.keys()
+
+객체의 모든 키를 배열로 반환합니다.
+순서는 보장하지 않습니다.
+
+```js
+const user = {
+ name: "200원",
+ age: 200,
+ email: "200woni@naver.com",
+ isValid: true,
+};
+const keys = Object.keys(user);
+console.log(keys);
+
+keys.forEach((key) => {
+ const el = document.createElement("div");
+ el.innerHTML = `${key}: ${user[key]}`;
+ document.body.append(el);
+});
+```
+
+### Object.values()
+
+객체의 모든 값을 배열로 반환합니다.
+순서는 보장하지 않습니다.
+
+```js
+const user = {
+ name: "200원",
+ age: 200,
+ email: "200woni@naver.com",
+ isValid: true,
+};
+const values = Object.values(user);
+console.log(values);
+```
+
+## JSON
+
+JSON(JavaScript Object Notation)은 데이터 전달을 위한 표준 데이터 포맷입니다.
+문자, 숫자, 불린, Null, 객체, 배열만 사용
+문자는 큰 따옴표만 사용
+후행 쉼표 사용 불가 (마지막 쉼표)
+'.json' 확장자의 파일 사용 가능
+
+```json
+{
+ "name": "200원",
+ "age": 200,
+ "email": "200woni@naver.com",
+ "isValid": true
+}
+```
+
+**JSON.stringify()**
+
+```js
+const user = {
+ name: "200원",
+ age: 200,
+ email: "200woni@naver.com",
+ isValid: true, // 후행 쉼표
+};
+console.log(JSON.stringify(user));
+```
+
+자바스크립트 데이터를 JSON 문자로 변환합니다.
+**JSON.parse()**
+
+```js
+const json = JSON.stringify(user);
+console.log(json);
+console.log(JSON.parse(json));
+```
+
+JSON문자를 자바스크립트 데이터로 변환합니다.
diff --git "a/hyunmin/ZeroBase/\354\236\205\353\254\270\354\236\220 \354\236\220\353\260\224\354\212\244\355\201\254\353\246\275\355\212\270/05. JS-DOM.md" "b/hyunmin/ZeroBase/\354\236\205\353\254\270\354\236\220 \354\236\220\353\260\224\354\212\244\355\201\254\353\246\275\355\212\270/05. JS-DOM.md"
new file mode 100644
index 0000000..9aee28a
--- /dev/null
+++ "b/hyunmin/ZeroBase/\354\236\205\353\254\270\354\236\220 \354\236\220\353\260\224\354\212\244\355\201\254\353\246\275\355\212\270/05. JS-DOM.md"
@@ -0,0 +1,414 @@
+> 제로베이스 자바스크립트 기초개념 DOM 부분 정리
+> 축약된 부분이 존재할 수 있습니다.
+
+# DOM
+
+DOM(Document Object Model)이란, HTML 문서를 객체로 표현한 것으로, JS에서 HTML을 제어할 수 있게 해줍니다.
+
+## Node와 Element
+
+**노드(Node)**
+
+- 요소 텍스트, 주석 등의 각 구조를 의미합니다.
+ **요소(Element)**
+- 노드의 하위 객체로 요소를 의미합니다.
+
+```js
+const parentEl = document.querySelector(".parent");
+
+// 요소의 모든 자식 노드를 확인합니다.
+// 요소, 텍스트, 주석 전부 나옴
+console.log(parentEl.childNodes);
+
+// 요소의 모든 자식 요소를 확입합니다.
+// 자식 요소만 나옴
+console.log(parentEl.children);
+```
+
+## 검색과 탐색
+
+### document.querySelector(선택자)
+
+선택자로 검색한 요소를 하나 반환합니다.
+만약 검색 결과가 없으면, 'null'을 반환합니다.
+
+```js
+// 가장 먼저 만나는 요소를 반환한다.
+const el = document.querySelector("div");
+console.log(el);
+
+// 이런식으로 자식 요소를 찾을 수도 있다.
+el.querySelector("div");
+```
+
+### document.querySelectorAll(선택자)
+
+선택자로 검색한 모든 요소를 NodeList 객체로 반환합니다.
+
+```js
+const nodeList = document.querySelectorAll("div");
+console.log(nodeList);
+
+// NodeList는 유사 배열이며, '.forEach()' 메소드는 내장되어 있지만,
+// 기타 배열 메소드는 사용할 수 없다.
+nodeList.forEach((e, i) => console.log(i + 1, e));
+
+// NodeList 객체는 'Array.from()' 메소드를 통해서 배열로 변환할 수 있다.
+const nodes = Array.from(nodeList);
+const names = nodes.map((e) => e.textContent);
+console.log(names);
+```
+
+### document.getElementById(아이디)
+
+HTML 'id' 속성(Atrributes) 값으로 검색한 요소를 하나 반환합니다.
+만약 검색 결과가 없으면, 'null'을 반환합니다.
+
+```js
+const idEl = document.getElementById("apple");
+console.log(idEl);
+
+const selEl = document.querySelector("#apple");
+console.log(selEl);
+```
+
+### 노드.parentElement
+
+노드의 부모 요소를 반환합니다.
+
+```js
+const selEl = document.querySelector("#apple");
+console.log(selEl);
+console.log(selEl.parentElement);
+```
+
+### Sibling
+
+**요소.previousElementSibling**
+
+- 요소의 이전 형제 요소를 반환합니다.
+ **요소.nextElementSibling**
+- 요소의 다음 형제 요소를 반환합니다.
+
+```js
+const selEl = document.querySelector("#apple");
+console.log(selEl.previousElementSibling);
+console.log(selEl.nextElementSibling);
+```
+
+### 자식 요소
+
+**요소.children**
+
+- 요소의 모든 자식 요소를 반환합니다.
+ **요소.firstElementChild**
+- 요소의 첫 번째 자식 요소를 반환합니다.
+ **요소.lastElementChild**
+- 요소의 마지막 자식 요소를 반환합니다.
+
+```js
+const selEl = document.querySelector("div");
+console.log(selEl.children);
+console.log(selEl.children[selEl.children.length - 1]);
+console.log(selEl.firstChild);
+console.log(selEl.lastChild);
+```
+
+## 생성, 조회, 수정
+
+### document.createElement(태그이름)
+
+HTML 요소를 메모리상에 생성해 반환합니다.
+
+```js
+const divEl = document.createElement("div");
+divEl.textContent = "안녕하세요!";
+divEl.classList.add("hello");
+console.log(divEl);
+
+const inputEl = document.createElement("input");
+inputEl.value = "입력하세요!";
+console.log(inputEl);
+
+const buttonEl = document.createElement("button");
+buttonEl.textContent = "버튼입니다!";
+console.log(buttonEl);
+
+// HTML body에 추가하기
+document.body.append(divEl, inputEl, buttonEl);
+```
+
+### append
+
+**요소.prepend(노드1, 노드2, ...)**
+
+- 하나 이상의 노드를 요소의 첫 번째 자식으로 삽입합니다.
+ **요소.append(노드1, 노드2, ...)**
+- 하나 이상의 노드를 요소의 마지막 자식으로 삽입합니다.
+ **노드.appendChild(노드1)**
+- 하나의 노드를 노드의 마지막 자식으로 삽입하고, 삽입한 노드를 반환합니다.
+
+```js
+const parentEl = document.createElement(".parent");
+
+const divEl = document.createElement("div");
+divEl.textContent = "새로운 요소!";
+
+const inputEl = document.createElement("input");
+
+const res1 = parentEl.prepend(new Comment(" 새로운 주석 "));
+const res2 = parentEl.append(divEl, "새로운 텍스트");
+const res3 = (parentEl.appendChild(inputEl).placeholder = "값을 입력하세요!");
+
+console.log(res1, res2);
+console.log(res3);
+```
+
+### 요소.remove
+
+요소를 제거합니다.
+
+```js
+const el = document.querySelector("#apple");
+
+console.log(el);
+el.remove();
+```
+
+### 노드.contains(노드)
+
+주어진 노드가 대상 노드를 포함한 후손인지 확인합니다.
+
+```js
+const parentEl = document.querySelector(".parent");
+const appleEl = document.querySelector("#apple");
+
+console.log(parentEl.contains(appleEl));
+console.log(document.body.contains(parentEl));
+console.log(document.body.contains(appleEl));
+console.log(document.body.contains(document.body));
+console.log(parentEl.contains(document.body));
+console.log(appleEl.contains(document.body));
+```
+
+### 노드.textContent
+
+노드의 모든 텍스트를 확인(GET)하거나 지정(SET)합니다.
+
+```js
+// GET
+const el = document.querySelector("#apple");
+console.log(el.textContent);
+
+// SET
+el.textContent = "오렌지";
+console.log(el.textContent);
+```
+
+### 요소.innerHTML
+
+요소의 내부 HTML을 확인(GET)하거나 지정(SET)합니다.
+
+```js
+// GET
+const el = document.querySelector("#apple");
+console.log(el.innerHTML);
+
+// SET
+el.innerHTML = `
두리안
`;
+console.log(el.innerHTML);
+```
+
+### 요소.dataset
+
+요소의 'data-' 속성을 확인(GET)하거나 지정(SET)합니다.
+
+```js
+const el = document.querySelector("#apple");
+const str = "Hello World";
+const num = 123;
+const obj = {
+ a: 1,
+ b: 2,
+};
+
+console.log(el.dataset);
+
+el.dataset.helloWorld = str;
+el.dataset.number = num;
+// 객체 같은 경우는 JSON문자 형식으로 변환해야된다.
+el.dataset.object = JSON.stringify(obj);
+
+console.log(el.dataset);
+
+console.log(el.dataset.helloWorld);
+console.log(el.dataset.number);
+console.log(JSON.parse(el.dataset.number));
+console.log(el.dataset.object);
+console.log(JSON.parse(el.dataset.object));
+```
+
+### 요소.classList
+
+요소의 'class' 속성을 제어합니다.
+
+**요소.classList.add()**
+
+- 값을 추가
+ **요소.classList.remove()**
+- 값을 제거
+ **요소.classList.toggle()**
+- 값을 토
+ **요소.classList.contains()**
+- 값을 확인
+
+```js
+const el = document.querySelector("#apple");
+
+el.classList.add("active");
+console.log(el.classList.contains("active"));
+
+el.classList.remove("active");
+console.log(el.classList.contains("active"));
+
+el.addEventListener("click", () => {
+ el.classList.toggle("active");
+ console.log(el.classList.contains("active"));
+});
+```
+
+### 요소.style
+
+요소의 'style' 속성을 확인(GET)하거나 지정(SET)합니다.
+
+```js
+const el = document.querySelector("#apple");
+
+// 개별 지정 할 수 있다.
+el.style.width = "100px";
+el.style.fontSize = "20px";
+el.style.backgroundColor = "green";
+el.style.position = "absolute";
+
+// 한 번에 지정할 수 있습니다.
+Object.assign(el.style, {
+ width: "100px",
+ fontSize: "20px",
+ backgroundColor: "green",
+ position: "absolute",
+});
+
+console.log(el.style);
+console.log(el.style.width);
+console.log(el.style.fontSize);
+console.log(el.style.backgroundColor);
+console.log(el.style.position);
+```
+
+### Attribute(속성)
+
+참고로 HTML의 속성은 Attribute이고, CSS와 JS의 속성은 Property라고 한다.
+**요소.getAttribute(속성)**
+
+- 요소의 속성을 확인합니다.
+ **요소.setAttribute(속성, 값)**
+- 요소에 속성과 값을 지정합니다.
+ **요소.hasAttribute(속성)**
+- 요소에 속성이 있는지 확인합니다.
+ **요소.removeAttribute(속성)**
+- 요소에 속성을 제거합니다.
+
+```js
+const el = document.querySelector("#apple");
+
+console.log(el.getAttribute("class"));
+console.log(el.getAttribute("title"));
+
+// 덮어쓰여진다.
+// el.setAttribute("class", "hello-world")
+el.setAttribute("title", "Hello-World");
+
+console.log(el.hasAttribute("class"));
+console.log(el.hasAttribute("title"));
+console.log(el.hasAttribute("value"));
+
+el.removeAttribute("class");
+el.removeAttribute("title");
+```
+
+### 크기와 좌표
+
+**window.innerWidth**
+
+- 화면(Viewport)의 너비를 얻습니다.
+ **window.innerHeight**
+- 화면(Viewport)의 높이를 얻습니다.
+ **window.scrollX**
+- 화면에서 스크롤된 x축의 위치를 얻습니다.
+ **window.scrollY**
+- 화면에서 스크롤된 y축의 위치를 얻습니다.
+
+```js
+console.log(window.innerWidth);
+console.log(window.innerHeight);
+
+console.log(window.scrollX);
+console.log(window.scrollY);
+
+window.addEventListener("scroll", () => {
+ console.log(window.scrollY);
+});
+```
+
+### scrollTo()
+
+**window.scrollTo()** 와 **요소.scrollTo()** 가 있음
+지정된 좌표로 대상을 스크롤합니다.
+
+대상.scrollTo(X좌표, Y좌표)
+대상.scrollTo({
+left: X좌표,
+top: Y좌표,
+behavior: "smooth"
+})
+
+```js
+setTimeout(() => {
+ // window.scrollTo(0, 200);
+ window.scrollTo({
+ left: 100,
+ top: 200,
+ behavior: "smooth",
+ });
+}, 2000);
+```
+
+### Width | Height
+
+**요소.offsetWidth**
+
+- 테두리 선을 포함한 요소의 너비를 얻습니다.
+ **요소.offsetHeight**
+- 테두리 선을 포함한 요소의 높이를 얻습니다.
+ **요소. clientWidth**
+- 테두리 선을 제외한 요소의 너비를 얻습니다.
+ **요소, clientHeight**
+- 테두리 선을 제외한 요소의 높이를 얻습니다.
+ **요소. scrollWidth**
+- 테두리 선을 제외한 요소의 스크롤 영역 너비를 얻습니다.
+ **요소. scrollHeight**
+- 테두리 선을 제외한 요소의 스크롤 영역 높이를 얻습니다.
+
+```js
+const parentEl = document.querySelector(".parent");
+const appleEl = document.querySelector(".apple");
+
+console.log(parentEl.clientWidth, parentEl.clientHeight);
+console.log(appleEl.clientWidth, appleEl.clientHeight);
+
+console.log(parentEl.offsetWidth, parentEl.offsetHeight);
+console.log(appleEl.offsetWidth, appleEl.offsetHeights);
+
+console.log(parentEl.scrollWidth, parentEl.scrollHeight);
+console.log(appleEl.scrollWidth, appleEl.scrollHeight);
+```
diff --git "a/hyunmin/ZeroBase/\354\236\205\353\254\270\354\236\220 \354\236\220\353\260\224\354\212\244\355\201\254\353\246\275\355\212\270/06. JS-\354\235\264\353\262\244\355\212\270.md" "b/hyunmin/ZeroBase/\354\236\205\353\254\270\354\236\220 \354\236\220\353\260\224\354\212\244\355\201\254\353\246\275\355\212\270/06. JS-\354\235\264\353\262\244\355\212\270.md"
new file mode 100644
index 0000000..8d9e76a
--- /dev/null
+++ "b/hyunmin/ZeroBase/\354\236\205\353\254\270\354\236\220 \354\236\220\353\260\224\354\212\244\355\201\254\353\246\275\355\212\270/06. JS-\354\235\264\353\262\244\355\212\270.md"
@@ -0,0 +1,289 @@
+> 제로베이스 자바스크립트 기초개념 이벤트(Event) 부분 정리
+> 축약된 부분이 존재할 수 있습니다.
+
+# 이벤트
+
+화면에서 발생할 수 있는 다양한 상황
+
+## 이벤트 추가 및 제거
+
+### 대상.addEventListener(이벤트 종류, 핸들러)
+
+대상에서 청취(Listen)할 이벤트 종류와 이벤트가 발생하였을 때 호출할 콜백(Handler)을 등록한다.
+
+```js
+const parentEl = document.querySelector(".parent");
+const childEl = document.querySelector(".child");
+
+parentEl.addEventListener("click", () => {
+ console.log("부모");
+});
+
+childeEl.addEventListener("click", () => {
+ console.log("자식");
+});
+
+// 이벤트 버블링 발생
+```
+
+### 대상.removeEventListener(이벤트 종류, 핸들러)
+
+대상에게 등록했던 이벤트 핸들러를 제거합니다.
+
+```js
+const parentEl = document.querySelector(".parent");
+const childEl = document.querySelector(".child");
+
+// 삭제하기 위해서는 기명함수가 필요하다.
+const handler = () => {
+ console.log("부모");
+};
+
+parentEl.addEventListener("click", handler);
+childEl.addEventListener("click", () => {
+ parentEl.removeEventListener("click", handler);
+});
+```
+
+## 이벤트 객체
+
+.addEventListener() 핸들러의 첫 매개변수로, 발생한 이벤트의 정보를 가진 객체를 전달합니다.
+
+```js
+const parentEl = document.querySelector(".parent");
+
+parentEl.addEventListener("click", (event) => {
+ console.log(parentEl);
+ console.log(event.target);
+});
+
+const inputEl = document.querySelector("input");
+inputEl.addEventListener("keydown", (event) => {
+ console.log(event.key);
+ console.log(inputEl.value);
+ console.log(event.target.value);
+});
+```
+
+## 이벤트 기본 동작 방지
+
+**event.preventDefault()**
+
+- 이벤트의 기본 동작을 방지합니다.
+ 이벤트의 기본 동작이란 a태그에서 href에 적힌 링크로 이동하는 것이 기본 동작이라고 볼 수 있다.
+
+```js
+// 태그에서 페이지 이동 방지!
+const anchorEl = document.querySelector("a");
+anchorEl.addEventListener("click", (e) => {
+ e.preventDefault();
+});
+
+// 마우스의 휠의 스크롤 동작 방지!
+const parentEl = document.querySelector(".parent");
+parentEl.addEventListener("wheel", (e) => {
+ e.preventDefault();
+});
+```
+
+## 이벤트 버블링
+
+```js
+const parentEl = document.querySelector(".parent");
+const childEl = document.querySelector(".child");
+const anchorEl = document.querySelector("a");
+
+window.addEventListener("click", () => {
+ console.log("윈도우");
+});
+document.documentElement.addEventListener("click", () => {
+ console.log("HTML");
+});
+document.body.addEventListener("click", () => {
+ console.log("body");
+});
+parentEl.addEventListener("click", (e) => {
+ console.log("parent"); // e.stopPropagation(); // 버블링 정지!
+});
+childEl.addEventListener("click", () => {
+ console.log("child");
+});
+anchorEl.addEventListener("click", () => {
+ console.log("a");
+});
+```
+
+이러한 코드가 있다고 하였을 때, a 요소를 클릭했다고 해보자.
+그러면 a 요소의 상위 요소들의 같은 이벤트들은 전부 발생할 것이다.
+
+이런식으로 하위 요소의 이벤트를 발생하였을 때, 같은 이벤트의 상위 요소의 이벤트도 발생되는 현상을 이벤트 버블링이라고 한다.
+
+이를 멈추게 하기 위해서는 **event.stopPropagation()** 을 사용 해야한다.
+
+## 이벤트 캡처링
+
+```js
+const parentEl = document.querySelector(".parent");
+const childEl = document.querySelector(".child");
+const anchorEl = document.querySelector("a");
+
+window.addEventListener("click", () => {
+ console.log("윈도우");
+});
+document.documentElement.addEventListener("click", () => {
+ console.log("HTML");
+});
+document.body.addEventListener("click", () => {
+ console.log("body");
+});
+parentEl.addEventListener("click", (e) => {
+ console.log("parent"); // e.stopPropagation(); // 버블링 정지!
+});
+childEl.addEventListener("click", () => {
+ console.log("child");
+});
+anchorEl.addEventListener("click", (e) => {
+ e.preventDefault();
+ console.log("a");
+});
+```
+
+이러한 코드가 있다고 해보자.
+
+a > c > p > b > h > w 순서대로 이벤트 버블링이 발생하여 차례차례 이벤트가 발생할 것이다.
+그런데 여기서 a보다 body요소가 먼저 이벤트가 발생되면 좋겠다라고 생각한다면, 우리가 아직 몰랐던 addEventListener의 3번째 인수를 사용할 수 있다.
+
+```js
+document.body.addEventListener(
+ "click",
+ () => {
+ console.log("body");
+ },
+ { capture: true },
+);
+```
+
+이런식으로 객체의 형태로 capture: true를 해준다면 a요소를 클릭하고 이벤트 버블링을 확인한 후에 capture가 되어있는 요소의 이벤트를 가장 먼저 발생시킨다.
+또한 capture는 capture되어는 요소 중에 더 상위요소를 먼저 발생시킨다.
+
+## 한글 입력 이벤트 중복
+
+브라우저 입력기(IME)의 CJK(중국어, 일본어, 한국어) 문자 구성 중에는 이벤트 핸들러가 2번 실행될 수 있습니다.
+
+```js
+const inputEl = document.querySelector("input");
+inputEl.addEventListener("keydown", (e) => {
+ // e.isComposing - 한글 입력 중인지 여부를 확인합니다.
+ // if (e.isComposing) return;
+ if (e.key === "Enter") {
+ const h1El = document.createElement("h1");
+ h1El.textContent = inputEl.value;
+ document.body.append(h1El);
+ }
+});
+```
+
+그래서 이러한 코드가 실행되었을 때, h1이 두 개가 만들어질 수도 있다.
+그럴 떄, e.isComposing을 사용해서 CJK 문자 구성 중에서는 return 시키면 해결이 된다.
+
+## 마우스와 포인터 이벤트
+
+> 포인터 이벤트는 마우스 이벤트의 하위 이벤트이다. (사실상 둘이 같다고 봐야됨)
+
+```js
+const parentEl = document.querySelector(".parent");
+const childEls = document.querySelectorAll(".child");
+
+childEls.forEach((childEl) => {
+ // click - 클릭했을 때,
+ // dblclick - 더블 클릭했을 때
+ childEl.addEventListener("click", () => {
+ childEl.classList.toggle("active");
+ }); // contextmenu - 우클릭 했을 때
+
+ childEl.addEventListener("contextmenu", (e) => {
+ e.preventDefault();
+ console.log(childEl.textContent);
+ });
+});
+
+// mousedown - 버튼을 누를 때
+// mouseup - 버튼을 땔 때
+// mouseenter - 포인터가 요소로 들어갈 때
+// mouseleave - 포인터가 요소에서 나올 때
+parentEl.addEventListener("mousedown", () => {
+ parentEl.classList.add("active");
+});
+parentEl.addEventListener("mouseup", () => {
+ parentEl.classList.remove("active");
+});
+
+// mousemove - 포인터가 움직일 때
+parentEl.addEventListener("mousemove", (e) => {
+ // console.log(e.x, e.y);
+});
+
+parentEl.addEventListener("wheel", (e) => {
+ // console.log("Parent Wheel");
+});
+```
+
+## 키보드 이벤트
+
+```js
+const inputEl = document.querySelector("input");
+
+// keydown - 키를 누를 때
+// keyup - 키를 땔 때
+inputEl.addEventListener("keydown", (e) => {
+ console.log(e.key);
+ if (e.key === "Enter") {
+ console.log("엔터");
+ }
+ if (e.key === "Escape") {
+ console.log("ESC");
+ }
+ if (e.key === " ") {
+ console.log("스페이스");
+ }
+});
+```
+
+keypress같은 경우에는 Deprecated되어서 더 이상 사용하지 않는다.
+
+## 양식과 포커스이벤트
+
+양식(form)에서 사용하는 이벤트에 대해 알아보자.
+
+```js
+const formEl = document.querySelector("#login");
+const inputEls = document.querySelectorAll("input");
+
+inputEls.forEach((el) => {
+ // focus(focusin) - 요소가 포커스를 얻었을 때
+ el.addEventListener("focus", () => {
+ formEl.classList.add("active");
+ }); // blur(focusout) - 요소가 포커스를 잃었을 때
+ el.addEventListener("blur", () => {
+ formEl.classList.remove("active");
+ }); // input - 값이 변경되었을 때 // change - 상태가 변경되었을 때
+ el.addEventListener("input", (e) => {
+ console.log(e.target.value);
+ });
+});
+
+// submit - 제출 버튼을 선택했을 때
+formEl.addEventListener("submit", (e) => {
+ e.preventDefault();
+ const data = {
+ id: e.target[0].value,
+ pw: e.target[1].value,
+ }; // fetch(https://....)
+ console.log("서버로 제출했습니다.");
+});
+
+// reset - 리셋 버튼을 선택했을 때
+formEl.addEventListener("reset", () => {
+ console.log("모든 값이 초기화되었습니다.");
+});
+```
diff --git "a/hyunmin/ZeroBase/\354\236\205\353\254\270\354\236\220 \354\236\220\353\260\224\354\212\244\355\201\254\353\246\275\355\212\270/07. JS-\354\266\224\352\260\200\355\225\231\354\212\265.md" "b/hyunmin/ZeroBase/\354\236\205\353\254\270\354\236\220 \354\236\220\353\260\224\354\212\244\355\201\254\353\246\275\355\212\270/07. JS-\354\266\224\352\260\200\355\225\231\354\212\265.md"
new file mode 100644
index 0000000..d7a0d7b
--- /dev/null
+++ "b/hyunmin/ZeroBase/\354\236\205\353\254\270\354\236\220 \354\236\220\353\260\224\354\212\244\355\201\254\353\246\275\355\212\270/07. JS-\354\266\224\352\260\200\355\225\231\354\212\265.md"
@@ -0,0 +1,342 @@
+> 제로베이스 자바스크립트 기초개념 추가학습 부분 정리
+> 축약된 부분이 존재할 수 있습니다.
+
+# 추가학습
+
+사이사이 넣기 애매했던거 배우는 시간
+
+## 구조 분해 할당
+
+배열이나 객체의 구조에 맞게 바로 개별 변수에 값을 할당하는 방법으로,
+필요한 값만 추출하여 변수에 할당할 수 있습니다.
+
+### 배열 구조 분해 할당
+
+```js
+const number = [1, 2, 3];
+// const a = number[0];
+// const b = number[1];
+// const c = number[2];
+const [a, b, c] = numbers;
+
+console.log(a, b, c);
+```
+
+기본적으로 이렇게 동작이되고, 다양한 패턴들이 존재한다고 한다.
+**선언과 분리**
+
+```js
+const number = [1, 2, 3];
+let a;
+let b;
+let c;
+if (number.length) {
+ [a, b, c] = numbers;
+}
+
+console.log(a, b, c);
+```
+
+선언과 분리를 하는 이유는 변수에는 scope라는 유효범위가 있는데 if문같은데 넣어서 선언을 해버린다면 밖에서는 사용할 수 없는 상황이 생길 수도 있기때문이다.
+**기본값**
+
+```js
+const number = [, , 3];
+const [a = 0, b, c] = number;
+
+console.log(a, b, c);
+```
+
+이런식으로 기본값을 지정해줄 수 있다.
+**반환 값 무시**
+
+```js
+const number = [1, 2, 3];
+const [, , c] = number;
+
+console.log(a, b, c);
+```
+
+이렇게 원하는 값만 빼먹을 수 있다.
+**나머지 할당**
+
+```js
+const number = [1, 2, 3];
+const [a, ...rest] = number;
+
+console.log(a, b, c);
+```
+
+### 객체 구조 분해 할당
+
+```js
+const user = {
+ name: "200won",
+ age: 200,
+ isValid: true,
+};
+// const name = user.name;
+// const age = user.age;
+// const isValid = user.isValid;
+const { name, age, isValid } = user;
+
+console.log(name, age, isValid);
+```
+
+객체 데이터는 순서가 없기때문에, 순서없이 적어도 사용가능하다.\
+**선언과 분리**
+
+```js
+const user = {
+ name: "200won",
+ age: 200,
+ isValid: true,
+};
+let name;
+let age;
+let isValid;
+if (user) {
+ ({ name, age, isValid } = user);
+}
+
+console.log(name, age, isValid);
+```
+
+**기본값**
+
+```js
+const user = {
+ name: "200won",
+ age: 200,
+ isValid: true,
+};
+const { isValid = false } = user;
+
+console.log(isValid);
+```
+
+**변수명 변경**
+
+```js
+const user = {
+ name: "200won",
+ age: 200,
+ isValid: true,
+};
+const { name: a, age: b, isValid: c } = user;
+
+console.log(a, b, c);
+```
+
+이런식으로 객체를 받고 이름을 변경해서 변수를 생성할 수 있다.
+**기본값 + 변수명 변경**
+
+```js
+const user = {
+ name: "200won",
+ age: 200,
+ isValid: true,
+};
+const { name: a, age: b, isValid: c = false } = user;
+
+console.log(a, b, c);
+```
+
+**나머지 할당**
+
+```js
+const user = {
+ name: "200won",
+ age: 200,
+ isValid: true,
+};
+const { name, ...rest } = user;
+
+console.log(name, rest);
+```
+
+배열과 동일하게 나머지 할당도 사용이 가능하다.
+
+## 선택적 체이닝
+
+**?.**
+
+- 대괄호 혹은 점 표기법의 대상이 null 혹은 undefined인 경우, 에러 대신 undefined를 반환합니다.
+ 우리가 변수를 활용하다보면 의도치 않게 null.abc라든가 이렇게 사용될 수 있을 때가 있다. 그럴 때 선택적 체이닝을 사용해서 에러없이 아래 코드를 실행시킬 수 있다!
+
+```js
+console.log(null?.abc);
+console.log(undefined?.abc);
+
+const el = document.querySelector("h1");
+console.log(el?.textContent);
+
+// const numbers = [1, 2, 3];
+const number = null;
+// 대괄호에서도 쓸 수 있다 ㄷㄷ
+console.log(numbers?.[0]);
+
+const user = {
+ name: "200원",
+ age: 22,
+};
+// const user = null;
+console.log(user?.name);
+```
+
+함수에도 `user.func?.()`처럼 사용할 수 있다.
+
+## 모듈
+
+자바스크립트 코드를 파일로 구분해서 데이터를 내보내거나 가지고 와서 사용할 수 있는 개념이다.
+
+```js
+function add(a, b) {
+ return a + b;
+}
+function sub(a, b) {
+ return a - b;
+}
+function getUserBirthYear(user) {
+ const year = new Date().getFullYear;
+ return year - user.age;
+}
+const fruits = ["사과", "바나나", "체리"];
+const addFruits = (fruit) => {
+ fruits.push(fruit);
+};
+
+console.log(add(2, 7));
+console.log(sub(2, 7));
+
+const thw = {
+ name: "200원",
+ age: 200,
+};
+const hhm = {
+ name: "황현민",
+ age: 19,
+};
+console.log(getUserBirthYear(thw));
+console.log(getUserBirthYear(hhm));
+
+addFruits("오렌지");
+addFruits("망고");
+console.log(fruits);
+```
+
+위에 있는 함수들을 활용하고 있다.
+그럼 이것을 모듈개념으로 정리해보자.
+
+일단 계산하는 함수를 모아두는 calculator.js 파일을 만들어주자.
+
+```js
+function add(a, b) {
+ return a + b;
+}
+function sub(a, b) {
+ return a - b;
+}
+```
+
+그럼 이런식으로 작성하면 될까?
+아니다 이제 import와 export라는 개념을 사용해줘야 한다.
+
+```js
+// calculaotr.js
+export function add(a, b) {
+ return a + b;
+}
+export function sub(a, b) {
+ return a - b;
+}
+```
+
+```js
+// main.js
+
+// 추가해주기
+import { add, sub } from "./calculator.js";
+```
+
+그리고 또한 HTML에서 불러오는 js파일을 module와 시켜줘야 한다.
+
+```html
+
+```
+
+이런식으로 모듈화를 시켜준다면, 작동이 잘 되는것을 알 수 있다.
+
+그럼 따른 함수들도 모듈화를 시켜보자.
+
+```js
+import { add, sub } from "./calculator.js";
+import { getUserBirthYear } from "./user.js";
+import { fruits, addFruits } from "./fruits.js";
+
+console.log(add(2, 7));
+console.log(sub(2, 7));
+
+const thw = {
+ name: "200원",
+ age: 200,
+};
+const hhm = {
+ name: "황현민",
+ age: 19,
+};
+console.log(getUserBirthYear(thw));
+console.log(getUserBirthYear(hhm));
+
+addFruits("오렌지");
+addFruits("망고");
+console.log(fruits);
+```
+
+이런식으로 main.js에 있던 함수를 구분별로 모듈화를 시켰고, import로 main.js에 불러와서 사용할 수 있다.
+
+이런식으로 하면 좀 더 간결하게 관리할 수 있어진다.
+또한 기능들을 폴더에 넣어서 관리할 수도 있다.
+
+### import & export 사용 패턴
+
+import & export를 하나라도 가지고 있으면 module이다.
+**import(가져오기)**
+
+- JS 파일의 최상단에 위치해야 합니다.
+
+```js
+import {} from "./module.js";
+```
+
+**export(내보내기)**
+
+```js
+// 기본 내보내기(Default export)
+// - 이름이 필요치 않습니다.
+// - 모듈에서 1번만 사용할 수 있습니다.
+// export default 데이터;
+export default [1, 2, 3];
+
+// 이름 내보내기(Named export)
+// - 이름이 필수입니다.
+// - 모듈에서 여러번 사용할 수 있습니다.
+// export const 이름1 = 데이터1;
+// export const 이름2 = 데이터2;
+// export function 이름3() {}
+// export const a = 1;
+// export const b = 3;
+
+const a = 1;
+const b = 3;
+
+// 모아서 내보내기 (객체 아님)
+export { a as a1, b };
+```
+
+```js
+// 기본 내보내기는 이름을 import에서 지정함
+// 이름 내보내기는 import에서 이름대로 가져오고 이름을 바꾸고 싶다면 as 사용
+// 와일드카드(*)을 사용해서 다 가져올 수도 있음
+import abc, { a as b } from "./module.js";
+```
diff --git "a/hyunmin/ZeroBase/\354\236\205\353\254\270\354\236\220 \354\236\220\353\260\224\354\212\244\355\201\254\353\246\275\355\212\270/08. JS-\355\201\264\353\236\230\354\212\244.md" "b/hyunmin/ZeroBase/\354\236\205\353\254\270\354\236\220 \354\236\220\353\260\224\354\212\244\355\201\254\353\246\275\355\212\270/08. JS-\355\201\264\353\236\230\354\212\244.md"
new file mode 100644
index 0000000..a29c586
--- /dev/null
+++ "b/hyunmin/ZeroBase/\354\236\205\353\254\270\354\236\220 \354\236\220\353\260\224\354\212\244\355\201\254\353\246\275\355\212\270/08. JS-\355\201\264\353\236\230\354\212\244.md"
@@ -0,0 +1,272 @@
+> 제로베이스 자바스크립트 기초개념 클래스 부분 정리
+> 축약된 부분이 존재할 수 있습니다.
+
+# 클래스
+
+클래스라는 것에 대해서 배워보자
+
+## 개요
+
+```js
+// 객체 리터럴
+const thw = {
+ name: "200원",
+ age: 200,
+ getBirthYear() {
+ const year = new Date().getFullYear();
+ return year - this.age;
+ },
+};
+
+const hhm = {
+ name: "황현민",
+ age: 19,
+ getBirthYear() {
+ const year = new Date().getFullYear();
+ return year - this.age;
+ },
+};
+
+console.log(thw.getBirthYear());
+console.log(hhm.getBirthYear());
+console.log(thw.getBirthYear === hhm.getBirthYear); // false
+```
+
+이렇게 객체 두 개의 동일한 함수를 놓고 사용하게 되면 두 개의 함수가 같지 않다고 판단하여 메모리에 두 개가 할당이 된다. 하지만 내용은 같기에 비효율적이라고 볼 수 있다.
+
+```js
+const thw = {
+ name: "200원",
+ age: 200,
+ getBirthYear() {
+ const year = new Date().getFullYear();
+ return year - this.age;
+ },
+};
+
+const hhm = {
+ name: "황현민",
+ age: 19,
+};
+
+console.log(thw.getBirthYear());
+// 함수의 this.age의 this가 hhm을 가리키게 됨
+console.log(thw.getBirthYear.call(hhm));
+// console.log(thw.getBirthYear === hhm.getBirthYear); // false
+```
+
+그럴 때, 이런식으로 사용하게 되면 메모리에 함수는 하나만 존재하게 되니 좀 더 효율적이라고 볼 수 있다.
+하지만 이 부분도 계속 thw객체에서 함수를 불러와야하는 행위를 해야하기에 이때 클래스를 활용해 볼 수 있다.
+
+### 프로토타입
+
+```js
+// 프로토타입
+function User(name, age) {
+ this.name = name;
+ this.age = age;
+}
+User.prototype.getBirthYear = function () {
+ const year = new Date().getFullYear();
+ return year - this.age;
+};
+
+const thw = new User("thw", 200);
+const hhm = new User("hhm", 19);
+
+console.log(thw);
+console.log(hhm);
+console.log(thw.getBirthYear());
+console.log(hhm.getBirthYear());
+console.log(thw.getBirthYear() === hhm.getBirthYear());
+```
+
+이런식으로 프로토타입을 이용해서 기존의 방법을 개선할 수 있다.
+그럼에도 단점이 존재하는데, User부분이 function으로 되어있어서 함수인지 클래스인지 헷갈릴수 있다.
+그래서 이것을 자바스크립트의 최신문법으로 class로 바꿀 수 있다.
+
+```js
+// 프로토타입
+class User {
+ constructor(name, age) {
+ this.name = name;
+ this.age = age;
+ }
+ getBirthYear() {
+ const year = new Date().getFullYear();
+ return year - this.age;
+ }
+}
+
+const thw = new User("thw", 200);
+const hhm = new User("hhm", 19);
+
+console.log(thw);
+console.log(hhm);
+console.log(thw.getBirthYear());
+console.log(hhm.getBirthYear());
+console.log(thw.getBirthYear() === hhm.getBirthYear());
+```
+
+이런식으로 prototype을 class로 바꿔서 사용할 수도 있다.
+
+## Getter & Setter
+
+값을 얻거나 지정할 떄 사용하는 함수
+
+```js
+// class
+class User {
+ constructor(first, last) {
+ this.first = first;
+ this.last = last;
+ this.fullName = `${first} ${last}`;
+ }
+}
+
+const thw = new User("200", "원");
+
+// Get
+console.log(thw.first);
+//Set
+thw.fullName = "황 현민";
+
+console.log(thw);
+```
+
+이런식으로 하게되면 fullName을 바꾸게 될 때, first와 last는 안바껴서 불편할 수 있다. 또 다른 구조라면 더 불편할 수도 있다.
+이럴 때 클래스의 Getter와 Setter라는 개념이 사용된다.
+
+```js
+// class
+class User {
+ constructor(first, last) {
+ this.first = first;
+ this.last = last;
+ }
+ get fullName() {
+ return `${this.firstName} ${this.lastName}`;
+ }
+ set fullName(value) {
+ const names = value.split(" ");
+ this.first = names[0];
+ this.last = names[1];
+ }
+}
+
+const thw = new User("200", "원");
+
+// Get
+console.log(thw.fullName);
+//Set
+thw.fullName = "황 현민";
+
+console.log(thw);
+```
+
+이런식으로 Getter와 Setter를 사용해서 함수를 속성처럼 사용할 수 있다.
+
+## 정적 메소드
+
+정적 메소드(static method)는 주로 클래스의 유틸리티(보조) 함수를 만들 때 사용됩니다.
+인스턴스와는 연결되지 않으며 , 클래스 자체에서 호출해야 합니다.
+
+```js
+// 첫 번째 방식이 클래스의 생성자를 이용해서 인스턴스를 만드는 방식이고
+// 이 생성자 방식을 좀 더 쉽게 만든 것이 기호를 사용해서 인스턴스를 만드는 두 번째의 리터럴 방식이다.
+const fruits = new Array("Apple", "Banana", "Cherry");
+// const fruits = ["Apple", "Banana", "Cherry"];
+
+// fruits.includes("Apple");
+// fruits.filter(item => item);
+// fruits.push("orange");
+// 프로토타입 함수의 그 클래스의 모든 인스턴스에서 사용이 가능하다.
+Array.prototype.abc = function () {
+ console.log(this);
+ return this.map((item) => item.slice(0, 1).toLowerCase());
+};
+
+const newFruits = fruits.abc();
+console.log(newFruits);
+console.log(Array.isArray(fruits));
+// fruits.isArray()가 없는 이유를 이제 알겠나?
+// 모든 데이터 형식의 isArray()를 넣게 되면 낭비가 심하기 때문이다.
+// 누가봐도 객체인데 객체.isArray()??를 하는 것이 이상하기 때문이다.
+// 이러하여 정적 메소드를 사용해서 보조하는 기능을 만드는 것 같다.
+console.log(Array.isArray(newFruits));
+
+const user = { name: "200원" };
+console.log(Array.isArray(user));
+// user.isArray()
+```
+
+위 코드를 보며 prototype 함수와 정적 메소드의 차이를 확인할 수 있다.
+
+그럼 실제로 정적 메소드를 어떻게 만들 수 있을까?
+
+```js
+class User {
+ constructor(first, last) {
+ // this 키워드는 클래스로 생성된 인스턴스라고 생각하면 된다.
+ this.firstName = first;
+ this.lastName = last;
+ }
+ static isUser(user) {
+ return user instanceof User;
+ }
+}
+
+const thw = new User("200", "원");
+const hhm = new user("황", "현민");
+const hhh = {
+ name: "황황현",
+ age: 100,
+};
+
+console.log(User.isUser(thw));
+console.log(User.isUser(hhm));
+console.log(User.isUser(hhh));
+```
+
+이런식으로 정적메소드를 만들어서 사용할 수 있다.
+
+## 상속
+
+클래스의 속성과 메소드를 다른 클래스에게 확(Extends)해서 재사용하는 기능을 말합니다.
+
+```js
+class A {
+ constructor(a) {
+ this.a = a;
+ }
+}
+
+class B extends A {
+ constructor(a, b) {
+ // super를 사용하게 되면 부모 클래스의 생성자에 접근해서 값을 넣어준다.
+ super(a);
+ this.b = b;
+ }
+}
+
+const a = new A(1);
+const b = new B(1, 2);
+
+// a의 prototype이 Object라는 것은 a도 부모 클래스로 object를 상속받고 있다는 것이다.
+console.log(a);
+// b의 prototype은 A클래스로 부모 클래스로 A클래스를 상속받고 있다는 것이다.
+console.log(b);
+
+// A클래스에서 만들었기에 A클래스의 인스턴스가 맞음
+console.log(a instanceof A); // true
+// B클래스에서 만들었지만 A클래스의 확장 버전이기 때문에 A클래스의 인스턴스라고 볼 수 있음
+console.log(b instanceof A); // true
+
+// A클래스는 B라는 클래스의 상위 클래스이기 때문에 B라는 클래스의 인스턴라고 볼 수 없다.
+console.log(a instanceof B); // false
+// B클래스에서 만들었기에 Bs클래스의 인스턴스가 맞음
+console.log(b instanceof B); // true
+
+console.log(a instanceof Object); // true
+console.log(b instanceof Object); // true
+```
diff --git "a/hyunmin/ZeroBase/\354\236\205\353\254\270\354\236\220 \354\236\220\353\260\224\354\212\244\355\201\254\353\246\275\355\212\270/09. JS-\353\271\204\353\217\231\352\270\260.md" "b/hyunmin/ZeroBase/\354\236\205\353\254\270\354\236\220 \354\236\220\353\260\224\354\212\244\355\201\254\353\246\275\355\212\270/09. JS-\353\271\204\353\217\231\352\270\260.md"
new file mode 100644
index 0000000..1b44e70
--- /dev/null
+++ "b/hyunmin/ZeroBase/\354\236\205\353\254\270\354\236\220 \354\236\220\353\260\224\354\212\244\355\201\254\353\246\275\355\212\270/09. JS-\353\271\204\353\217\231\352\270\260.md"
@@ -0,0 +1,600 @@
+> 제로베이스 자바스크립트 기초개념 비동기 부분 정리
+> 축약된 부분이 존재할 수 있습니다.
+
+# 비동기
+
+## 개요
+
+### 동기
+
+코드는 작성된 순서대로 실행되며, 하나의 작업이 끝나기 전에는 다음 작업이 시작되지 않습니다.
+
+```js
+console.log(1);
+console.log(2);
+alert("확인");
+console.log(3);
+console.time("Loop!");
+for (let i = 0; i < 10000000; i++) {}
+console.timeEnd("Loop!");
+console.log(4);
+```
+
+이런식으로 전 코드가 실행되기전까지는 뒤에 코드가 실행되지 않는다.
+
+### 비동기
+
+코드는 작성된 순서대로 실행되지만, 특정 작업이 끝나기 전에 다음 작업이 시작될 수 있습니다.
+
+```js
+console.log(1);
+console.log(2);
+console.log(3);
+console.time("Loop!");
+// 비동기 함수
+setTimeout(() => {
+ for (let i = 0; i < 10000000; i++) {}
+ console.timeEnd("Loop!");
+}, 0);
+console.log(4);
+```
+
+```js
+console.log(1);
+const h1El = document.querySelector("h1");
+h1El.addEventListener("click", () => {
+ console.log("클릭");
+});
+console.log(2);
+```
+
+```js
+console.log(1);
+fetch("https://api....")
+ .then((res) => res.json())
+ .then((data) => console.log(data));
+console.log(2);
+```
+
+## 콜백과 콜백 지옥
+
+```js
+function timer() {
+ setTimeout(() => {
+ console.log(1);
+ }, 2000);
+}
+
+timer();
+console.log(2);
+```
+
+이렇게 존재할 때 숫자1을 2보다 먼저 출력시키고 싶다고 한다면 어떻게 해야할까?
+물론 setTimeout안에 console.log(2)를 넣을수도 있겠지만, 만약 모듈화가 되어있어서 건드릴 수 없다면? 그럴 때 콜백을 사용하면 된다.
+
+```js
+function timer(callback) {
+ setTimeout(() => {
+ console.log(1);
+ callback();
+ }, 2000);
+}
+
+timer(() => {
+ console.log(2);
+});
+```
+
+이 콜백 패턴의 핵심은 우리가 비동기 함수를 호출할 때, 콜백 함수를 전달해줘서 콜백 함수가 정확히 어디서 실행될지 지정해주는 것이 핵심이다.
+
+하지만, 단점이 존재하는데 바로 콜백 지옥이다.
+
+```js
+function renderImage(callback) {
+ const imgEl = document.createElement("img");
+ imgEl.src = "https://picsum.photos/3000/2000";
+ imgEl.addEventListener("load", () => {
+ document.body.append(imgEl);
+ callback();
+ });
+}
+renderImage(() => {
+ console.log("Done 1");
+});
+renderImage(() => {
+ console.log("Done 2");
+});
+renderImage(() => {
+ console.log("Done 3");
+});
+renderImage(() => {
+ console.log("Done 4");
+});
+```
+
+우리가 코드를 이런식으로 작성하게 된다면, Done의 순서가 없이 출력이 될 것이다.
+왜냐하면, 첫 번째가 가장 먼저 도착할 것이라는 보장이 없기 때문이다.
+인터넷 속도에 따라서 로드되는 속도가 서로 다르기 때문이다.
+즉, 출발은 순서대로 해도 도착은 순서대로 된다는 보장이 없다.
+
+그래서 이 문제점을 해결하기 위해 동기적으로 코드를 바꾸면..
+
+```js
+function renderImage(callback) {
+ const imgEl = document.createElement("img");
+ imgEl.src = "https://picsum.photos/3000/2000";
+ imgEl.addEventListener("load", () => {
+ document.body.append(imgEl);
+ callback();
+ });
+}
+renderImage(() => {
+ console.log("Done 1");
+ renderImage(() => {
+ console.log("Done 2");
+ renderImage(() => {
+ console.log("Done 3");
+ renderImage(() => {
+ console.log("Done 4");
+ });
+ });
+ });
+});
+```
+
+그럼 이런식의 코드가 만들어지는데 마치 콜백들이 개미지옥같이 들여쓰기가 계속 되는 모습을 콜백 지옥이라고 한다.
+
+이것을 개선하기 위해서는 promise라는 클래스를 사용할 수 있다.
+promise는 다음시간에 배울 것이기에 일단 수정부터 해보겠다.
+
+```js
+function renderImage()
+ return new Promise((resolve) => {
+ const imgEl = document.createElement("img");
+ imgEl.src = "https://picsum.photos/3000/2000";
+ imgEl.addEventListener("load", () => {
+ document.body.append(imgEl);
+ resolve();
+ });
+ });
+}
+renderImage()
+ .then(() => {
+ console.log("Done 1");
+ return renderImage();
+ })
+ .then(() => {
+ console.log("Done 2");
+ return renderImage();
+ })
+ .then(() => {
+ console.log("Done 3");
+ return renderImage();
+ })
+ .then(() => {
+ console.log("Done 4");
+ });
+```
+
+이런식으로 promise를 사용하게 된다면, 콜백 지옥에서 벗어나 좀 더 가독성 좋게 코드를 짤 수 있다.
+
+## Promise
+
+비동기 작업의 완료나 실패 지점을 지정하고 그 결과를 반환할 수 있습니다.`
+
+```js
+function timer(cb) {
+ setTimeout(() => {
+ console.log(1);
+ cb("Done!");
+ });
+}
+
+timer((msg) => {
+ console.log(msg);
+ console.log(2);
+});
+```
+
+이 코드를 promise를 사용한 코드로 바꿔본다면
+
+````js
+function timer() {
+ return new Promise((resolve, reject) => {
+ if (error) {
+ // reject 호출시 resolve 호출 안됨
+ reject();
+ }
+ setTimeout(() => {
+ console.log(1);
+ // resolve 호출시 reject 호출 안됨
+ resolve("Done!");
+ });
+ });
+}
+
+timer()
+ .then((msg) => {
+ console.log(msg);
+ console.log(2);
+ return timer();
+ })
+ .then((msg) => {
+ console.log(msg);
+ console.log(2);
+ return timer();
+ })
+ .then((msg) => {
+ console.log(msg);
+ console.log(2);
+ });
+ ```
+resolve와 reject는 반대되는 개념이라고 생각하면 된다.
+reject는 추후에 다시 다루게 될 것이다.
+```js
+function loadImage(src) {
+ return new Promise((resolve) => {
+ const imgEl = document.createElement("img");
+ imgEl.src = src;
+ imgEl.addEventListener("load", () => {
+ resolve(imgEl);
+ });
+ });
+}
+loadImage("https://picsum.photos/3000/2000").then((imgEl) => {
+ document.body.append(imgEl);
+ console.log("Done");
+});
+loadImage("https://picsum.photos/100/200").then((imgEl) => {
+ console.log(imgEl);
+})
+````
+
+그리고 위에 사진을 불러오는 코드를 좀 더 promise를 사용해서 활용성있게 만들어줄 수도 있습니다.
+promise라는게 약속을 나타내는 이름이다 보니 콜백 함수가 어떤 시점에 실행 된다는 것을 약속한다는 의미를 지니고 있고, 그때 약속이 이행되었으면 then메소드를 호출하겠다는 것이다.
+
+다음에는 async와 await에 대해서 배워볼 것이다.
+then보다 최신 기술이기에 배워두면 좋다.
+
+## Async & Await
+
+```js
+const h1El = document.querySelector("h1");
+const ulEl = document.createElement("ul");
+document.body.append(ulEl);
+
+h1El.addEventListener("click", () => {
+ ulEl.textContent = "Loading..."; // promise instance가 반환이 된다.
+ fetch("https://api.heropy.dev/v0/users")
+ .then((res) => res.json())
+ .then((data) => {
+ const { users } = data;
+ const liEls = users.map((user) => {
+ const liEl = document.createElement("li");
+ liEl.textContent = user.name;
+ const imgEl = document.createElement("img");
+ imgEl.src = user.photo?.url || "https://heropy.dev/favicon.png";
+ liEl.prepend(imgEl);
+ return liEl;
+ });
+ ulEl.textContent = "";
+ ulEl.append(...liEls);
+ });
+});
+```
+
+일단 이런식으로 기본 코드가 있을 때,
+
+```js
+const h1El = document.querySelector("h1");
+const ulEl = document.createElement("ul");
+document.body.append(ulEl);
+
+h1El.addEventListener("click", async () => {
+ ulEl.textContent = "Loading..."; // promise instance가 반환이 된다. // await로 데이터를 가져오는 것을 기다림 (promise instance에만 사용가능)
+ const res = await fetch("https://api.heropy.dev/v0/users"); // 데이터 분석이 끝나는 것을 기다림
+ const data = await res.json(); // promise instance 반환함
+ const { users } = data;
+ const liEls = users.map((user) => {
+ const liEl = document.createElement("li");
+ liEl.textContent = user.name;
+ const imgEl = document.createElement("img");
+ imgEl.src = user.photo?.url || "https://heropy.dev/favicon.png";
+ liEl.prepend(imgEl);
+ return liEl;
+ });
+ ulEl.textContent = "";
+ ulEl.append(...liEls);
+});
+```
+
+async와 await을 사용해서 위 코드처럼도 만들어줄 수 있다.
+await은 무조건 promise instance에만 사용할 수 있고 await을 사용하는 가장 가까운 함수에 async가 있어야 한다.
+
+## 예외 처리
+
+`fetch("https://api.heropy.dev/v0/users");` 만약 이런식으로 서버로 데이터를 요청하는 코드를 작성하였을 때, 우리가 글자를 잘 못 작성하거나 서버가 고장나있는 상태라면 오류가 나게될 것이다. 이러한 예외 상황들이 있을 때, 처리하는 코드를 만드는 것도 중요하다고 할 수 있다.
+
+### try~catch
+
+이 때, 우리는 try~catch를 사용해서 예외 처리를 해줄 수 있다.
+
+```js
+const h1El = document.querySelector("h1");
+const ulEl = document.createElement("ul");
+document.body.append(ulEl);
+
+h1El.addEventListener("click", async () => {
+ ulEl.textContent = "Loading..."; // promise instance가 반환이 된다.
+ try {
+ const res = await fetch("https://api.heropy.dev/v0/users");
+ const data = await res.json();
+ const { users } = data;
+ const liEls = users.map((user) => {
+ const liEl = document.createElement("li");
+ liEl.textContent = user.name;
+ const imgEl = document.createElement("img");
+ imgEl.src = user.photo?.url || "https://heropy.dev/favicon.png";
+ liEl.prepend(imgEl);
+ return liEl;
+ });
+ ulEl.textContent = "";
+ ulEl.append(...liEls);
+ } catch (error) {
+ console.log(error);
+ }
+});
+```
+
+이런식으로 try에서 시도를 해보다가 에러가 발생하면 catch부분에서 에러를 받아서 코드를 실행할 수 있다.
+
+또한, try나 catch에 둘 다 넣어야 하는 코드가 있다면 finally 구문에 넣어주면 된다.
+
+```js
+try {
+ // 시도 해보다가 에러나면 그 아래부터 실행안하고 catch로 넘어감
+} catch (error) {
+ // 에러나면 실행됨
+} finally {
+ // 에러가 나든 안나든 실행됨
+}
+```
+
+그리고 여기서 reject를 말해볼 건데
+만약, img를 불러오다가 error가 나게 되면 resolve밖에 없는 promise는 에러를 띄우지 않고 계속 약속을 이행하겠다라고만 하기에 우리는 reject라는 것들 추가해 addListener로 error라는 이벤트를 추가해서 reject를 실행시켜줄 수 있다.
+그리고 reject에서 new Error라는 객체를 넘겨서 catch의 error에서 받을 수 있다.
+
+```js
+reject(new Error("이미지를 로드할 수 없어.."));
+```
+
+그러면 이행과 거부. 예외 처리 부분을 한 번 정리해보자
+
+```js
+// 이행과 거부, 예외 처리
+
+//매개 변수
+// resolve - 약속을 이행하는 함수(정상 처리)
+// reject - 약속을 거부하는 함수(에러 상황)
+
+// 용어 정리
+// Pending - 약속이 이행되거나 거부되기 전 상태
+// Fulfilled - 약속이 이행된 상태
+// Rejected - 약속이 거부된 상태
+function loadImage(src) {
+ // Pending...
+ return new Promise((resolve, reject) => {
+ if (!src) {
+ reject(new Error("이미지 경로가 필요해요")); // Rejected
+ }
+ const imgEl = document.createElement("img");
+ imgEl.src = src;
+ imgEl.addEventListener("load", () => {
+ resolve(imgEl); // Fulfilled
+ });
+ imgEl.addEventListener("error", () => {
+ reject(new Error("이미지를 불러올 수 없어요")); // Rejected
+ });
+ });
+}
+
+// .then() /.catch() /.finally()
+// - 약속이 이행되었을 때 호출(then)하거나,
+// - 약속이 거부되있을 때 호출(catch)하거나,
+// - 이행 및 거부와 상관없이 항상 호출(finally)하는 메소드를 제공할 수 있습니다.
+loadImage("https://picsum.photo/300")
+ .then((imgEl) => {
+ document.body.append(imgEl);
+ })
+ .catch((error) => {
+ console.log(error.message);
+ })
+ .finally(() => {
+ console.log("Done");
+ });
+
+// try / catch / finally
+// - 에러(예외)가 발생할 수 있는 코드의 실행을 시도(try)하고,
+// - 에러가 발생하면 시도를 종료해 에러를 잡아내며(catch),
+// - 에러 여부와 상관없이 항상 실행(finally)하는 코드를 정의할 수 있습니다.
+(async () => {
+ try {
+ const imgEl = await loadImage("https://picsum.photo/300");
+ document.body.append(imgEl);
+ } catch (error) {
+ console.log(error.message);
+ } finally {
+ console.log("Done!");
+ }
+})();
+```
+
+이렇게 이행과 거부 부분을 정리할 수 있을 것 같다.
+
+# 네트워크 통신을 위한 fetch 함수
+
+우리가 앞에서 사용했던 fetch함수에 대해서 자세히 알아보고 네트워크 통신을 하기 위해서 알아야 하는 것들을 공부해보자.
+
+```js
+fetch("https://api.heropy.dev/v0/users")
+ .then((res) => res.json())
+ .then((data) => console.log(data));
+```
+
+우리가 fetch로 서버의 주소를 입력하게 되면 서버에 데이터를 가지러 가게 된다.
+그 시작지점이 client이고 도착 부분이 서버이다. 그리고 방향에 따라서 요청(Request)/응답(Response)이라고 부른다.
+
+그래서 우리가 웹 브라우저에서 서버로 요청을 하는 것이기에 웹사이트가 클라이언트 인 것이다.
+
+그럼 이제 요청과 응답에 필요한 것들을 알아보자.
+
+### 요청(Request)
+
+- url: 요청 서버 주
+- method: 요청 종류(GET, POST, PUT, DELETE 등)
+- headers: 요청 메타 정보 (클라이어트의 환경 정보)
+- body: 요청 데이터(POST, PUT)
+ 우리는 fetch를 할 때 url만 넣었는데 나머지 정보들을 어떻게 넣을까?
+ 바로 두 번째 인수인 객체에 넣어주면 된다.
+
+```js
+fetch("https://api.heropy.dev/v0/users", {
+ method: "GET",
+ headers: {},
+ body: undefined,
+})
+ .then((res) => res.json())
+ .then((data) => console.log(data));
+```
+
+우리가 원래 주소만 넣어주었던 이유는 나머지 정보를 생략해도 자동으로 값을 넣어주기 때문에 주소만 적어준 것이다.
+
+### 응답(Response)
+
+- status: 응답 상태 코드(200, 400, 500 등)
+- headers: 응답 메타 정보
+- body: 응답 데이
+- ok: 정상적인 처리 여부
+ 이런식의 정보가 들어있다.
+
+### CRUD
+
+CRUD는 create, read, update, delete를 합친 단어이다.
+
+- Create: POST - 데이터 생성
+- Read: GET - 데이터 조회
+- Update: PUT(PATCH) - 데이터 수정
+- Delete: DELETE - 데이터 삭제
+ 우리가 요청을 할 때, method로 넘길 수 있는 것들이다.
+
+### URL 구조
+
+https://www.heropy.dev/p/QOWqjv?key=value&a=12&b=34#h1-title
+
+- https: 통신 규약(Protocol)
+- www.heropy.dev: 도메인
+- p/QOWqjv: 경로(Path)
+- key=value&a=12&b=34: 쿼리(Query)
+- \#h1-title: 해시(hash)
+
+### HTTP 상태 코드
+
+- 1xx: 처리
+- 2xx: 성공
+- 3xx: 리다이랙트
+- 4xx: 클라이언트 오류
+- 5xx: 서버 오류
+
+- 200: 정상적으로 처리됨
+- 400: 잘못된 요청
+- 401: 인증 정보가 부족
+- 403: 권한이 없음
+- 404: 찾을 수 없음
+- 500: 서버 오류
+
+### fetch 함수
+
+- fetch(url, options)
+- options.method: 요청 종류(GET, POST, PUT, DELETE 등)
+- options.headers: 요청 메타 정보
+- options.body: 요청 데이터
+
+```js
+// fetch(url, options)
+// options.method: 요청 종류(GET, POST, PUT, DELETE 등)
+// options.headers: 요청 메타 정보
+// options.body: 요청 데이터
+fetch("https://api.heropy.dev/v0/users", {
+ method: "POST",
+ headers: {
+ "Context-Type": "application/json",
+ },
+ body: JSON.stringify({
+ name: "황현민",
+ age: 19,
+ }),
+})
+ .then((res) => res.json())
+ .then((data) => console.log(data));
+```
+
+이런식으로 사용할 수 있다.
+
+# 반복문에서 비동기 처리
+
+우리가 반복문에서 비동기 처리할 떄 주의 해야되는 점을 알아보자.
+
+```js
+async function getMovies(movieName) {
+ const res = await fetch(
+ `https://omdbapi.com/?apikey=7035c60c&s=${movieName}`,
+ );
+ return await res.json();
+}
+
+const titles = ["frozen", "avengers", "avatar"];
+
+// 배열 메소드 반복
+titles.forEach(async (title, index) => {
+ const movies = await getMovies(title);
+ console.log(`${index + 1} ${title}`, movies);
+});
+```
+
+이런식의 코드가 존재한다고 하였을 때, 우리는 영화가 순서대로 출력된다고 생각할 수 있다. 하지만 forEach를 돌리면서 영화의 순서가 뒤죽박죽으로 출력이된다.
+
+우리가 배열데이터의 사용하는 콜백을 활용하는 메소드들 map, filter, reduce 등..의 메소드들의 콜백 함수는 배열 아이템의 순서대로 독립적으로 호출되는 개념이기에 하나의 콜백함수가 완료되는 것을 기다리지 않고, 바로 다음 콜백을 실행한다.
+
+즉, 호출은 순서대로 되지만 await로 기다리는 행위는 독립적으로 진행되어 먼저 응답을 받는 쪽이 출력이 되는 것이다. (이전 콜백의 기다림을 다음 콜백이 기다리지 않는다.)
+우리가 순서대로 데이터를 받아와야 하는 상황이라면 배열의 콜백을 활용하는 메소드는 사용하면 안된다.
+
+순서를 보장받고 싶다면 간단하게 for반복문을 사용하면 된다.
+
+```js
+async function getMovies(movieName) {
+ const res = await fetch(
+ `https://omdbapi.com/?apikey=7035c60c&s=${movieName}`,
+ );
+ return await res.json();
+}
+
+const titles = ["frozen", "avengers", "avatar"];
+
+// 배열 메소드 반복
+// titles.forEach(async (title, index) => {
+// const movies = await getMovies(title);
+// console.log(`${index + 1} ${title}`, movies);
+// });
+
+// for 반복문
+(async () => {
+ let index = 1;
+ for (const title of titles) {
+ const movies = await getMovies(title);
+ console.log(`${index + 1} ${title}`, movies);
+ index += 1;
+ }
+})();
+```
+
+이런식으로 for문을 사용하게 된다면 await키워드를 사용해서 기다리는 것이 가능하다.
diff --git "a/hyunmin/ZeroBase/\354\236\205\353\254\270\354\236\220 \354\236\220\353\260\224\354\212\244\355\201\254\353\246\275\355\212\270/10. JS-\354\213\254\355\231\224\355\225\231\354\212\265.md" "b/hyunmin/ZeroBase/\354\236\205\353\254\270\354\236\220 \354\236\220\353\260\224\354\212\244\355\201\254\353\246\275\355\212\270/10. JS-\354\213\254\355\231\224\355\225\231\354\212\265.md"
new file mode 100644
index 0000000..6d34adc
--- /dev/null
+++ "b/hyunmin/ZeroBase/\354\236\205\353\254\270\354\236\220 \354\236\220\353\260\224\354\212\244\355\201\254\353\246\275\355\212\270/10. JS-\354\213\254\355\231\224\355\225\231\354\212\265.md"
@@ -0,0 +1,571 @@
+> 제로베이스 자바스크립트 기초개념 심화학습 부분 정리
+> 축약된 부분이 존재할 수 있습니다.
+
+# 심화학습
+
+심화적인 부분 학습
+
+## 함수 재귀
+
+함수 재귀(Recursion)란, 함수가 자기 자신을 호출하는 것을 말합니다.
+
+```js
+let i = 0;
+function a() {
+ console.log(i, "A");
+
+ i += 1;
+ if (i < 4) {
+ a();
+ }
+}
+a();
+```
+
+이런식으로 함수안에서 자신을 부르는 것을 함수 재귀라고 한다.
+
+그럼 유용하게 어떻게 사용될 수 있을까?
+
+```js
+const neo = { name: "Neo" };
+const evan = { name: "Evan", parent: neo };
+const lewis = { name: "Lewis", parent: evan };
+const amy = { name: "Amy", parent: lewis };
+
+const getRootUser = (user) => {
+ if (user.parent) {
+ return getRootUser(user.parent);
+ }
+ return user;
+};
+
+console.log(getRootUser(amy));
+```
+
+사용하는 예시를 보면 이런식으로 유용하게 사용할 수도 있다.
+
+## 함수 this
+
+일반 함수와 화살표 함수에 따라 다르게 정의됩니다. <- 가장 중요한 부분
+일반 함수는 호출 위치에서 this가 정의됩니다.
+화살표 함수는 선언 위치(렉시컬 스코프)에서 this가 정의됩니다.
+
+```js
+function User() {
+ this.name = "User";
+ return {
+ name: "Neo",
+ age: 80, // getInfo() { // return `${this.name}는 ${this.age}입니다.`; // },
+ getInfo: () => {
+ return `${this.name}는 ${this.age}입니다.`;
+ },
+ };
+}
+
+const u = new User();
+console.log(u.name);
+console.log(u.age);
+console.log(u.getInfo());
+
+const evan = {
+ name: "Evan",
+ age: 25,
+};
+console.log(u.getInfo.call(evan));
+```
+
+일반 함수는 자신이 호출되는 범위인 return 안에 객체의 name과 age를 출력시키고
+화살표 함수는 호출된 함수를 감싸는 가장 가까운 함수가 this를 정의하는 영역이 되어 name은 User가 뜨고 age는 undefined가 뜬다.
+
+그래서 this키워드를 사용할 때, 일반 함수를 사용할 것인지 화살표 함수를 사용할 것인지 정하고 코드를 작성해야 한다.
+
+## call, bind, apply
+
+```js
+const neo = {
+ name: "Neo",
+};
+const amy = {
+ name: "Amy",
+ getInfo(age, city) {
+ return `${this.name}는 ${age}세이고, ${city}에 삽니다.`;
+ },
+};
+console.log(amy.getInfo(22, "서울"));
+
+// .call(this, 인수1, 인수2, ...)
+// 대상 함수를 주어진 객체(this)의 메소드로 실행합니다.
+console.log(amy.getInfo.call(neo, 1, "부산"));
+
+// .apply(this, [인수1, 인수2, ...])
+// 대상 함수를 주어진 객체(this)의 메소드로 실행합니다.
+console.log(amy.getInfo.apply(neo, [85, "서울"]));
+
+// .bind(this)
+// 대상 함수를 주어진 객체(this)의 메소드로 실행할 수 있는 새로운 함수를 반환합니다.
+const neoGetInfo = amy.getInfo.bind(neo);
+setTimeout(() => {
+ console.log(neoGetInfo(85, "서울"));
+}, 1000);
+```
+
+3가지 모두 함수가 없는 객체에게 함수를 빌려주는 느낌은 비슷하다.
+
+## Throttle & Debounce
+
+Throttle & Debounce같은 경우는 직접 구현하기가 쉽지 않기에 외부 라이브러리를 사용해 볼 것이다.
+https://lodash.com/
+CDN copies에 들어가서
+
+```html
+
+```
+
+이러한 코드를 복사해주고 html에 붙여넣기 해주자. (main.js위에)
+
+```js
+// Throttle
+// - 정해진 시간 간격으로 함수를 실행하도록 제한합니다.
+window.addEventListener(
+ "scroll",
+ _.throttle(function () {
+ console.log("Scroll!");
+ }, 400),
+);
+
+// Debounce
+// - 정해진 시간 동안 함수가 실행되지 않으면, 함수를 실행합니다.(마지막에 한 번만 실행)
+async function getMovies(movieName) {
+ const res = await fetch(
+ `https://omdbapi.com/?apikey=7035c60c&s=${movieName}`,
+ );
+ return await res.json;
+}
+const inputEl = document.querySelector("input");
+inputEl.addEventListener(
+ "input",
+ _.debounce(function () {
+ console.log(getMovies(inputEl.value));
+ }, 400),
+);
+```
+
+둘 다 함수의 실행 횟수를 제한하는 느낌이다. (최적화!)
+
+## 불변성과 가변성
+
+**불변성**
+
+- 생성된 데이터가 메모리에서 변경되지 않는 것을 의미합니다.
+ **가변성**
+- 생성된 데이터가 메모리에서 변경될 수 있음을 의미합니다.
+
+원시형(문자, 숫자, 불린, null, undefined)은 불변성
+참조형(배열, 객체, 함수)는 가변성을 가지고 있습니다.
+
+```js
+const a = 1;
+let b = a;
+b = 2;
+console.log(a);
+console.log(b);
+
+const c = { x: 1, y: 2 };
+const d = c;
+d.x = 99;
+console.log(c);
+console.log(d);
+
+const e = [1, 2, 3];
+const f = e;
+f[0] = 99;
+console.log(e);
+console.log(f);
+```
+
+![[Pasted image 20240619201350.png]]
+이런식으로 작동이 되는 것을 알고 있으면 나중에 에러를 줄일 수 있다.
+(코드를 순서대로 보며 사진처럼 생각해보자)
+
+이것으로 알 수 있는 것은 참조형같은 경우는 우리가 원치않게 변하게 만드는 일이 생길 수 있기때문에 복사를 해서 사용을 해줘야 한다.
+
+근데 여기서 복사는 두 가지로 나뉘게 된다.
+그래서 다음시간에는 얕은 복사와 깊은 복사에 대해서 공부해 볼 것이다.
+
+## 얕은 복사 & 깊은 복사
+
+**얕은 복사(Shallow copy)**
+
+- 참조형의 최상위 레벨만 복사하는 것을 말합니다.
+ **깊은 복사(Deep copy)**
+- 참조형의 모든 레벨을 복사하는 것을 말합니다.
+
+```js
+const a = [
+ { x: 1, y: [1, 2] },
+ { x: 2, y: [3, 4] },
+ { x: 3, y: [5, 6] },
+];
+```
+
+여기 여러 레벨을 가지고 있는 배열이 있다.
+
+여기서 얕은 복사는 최상위인 배열만을 복사하는 것이고, 깊은 복사는 저 배열의 모든 레벨을 복사하는 것이다. (배열, 배열안 객체, 배열안 객체안 배열도 복사)
+
+```js
+const a = [
+ { x: 1, y: [1, 2] },
+ { x: 2, y: [3, 4] },
+ { x: 3, y: [5, 6] },
+];
+
+const c = { x: 1, y: 2 };
+// const d = c;
+// 얕은 복사 2개
+const d = Object.assign({}, c);
+// const d = { ...c };
+d.x = 99;
+console.log(c);
+console.log(d);
+
+const e = [1, 2, 3];
+// const f = e;
+// 얕은 복사 2개
+// const f = e.slice() // 똑같은 얕은 복사가 되지만 의미가 자른다는 의미여서 않좋음
+const f = [...e];
+console.log(e);
+console.log(f);
+
+const g = [
+ { x: 1, y: 2 },
+ { x: 3, y: 4 },
+];
+// 깊은 복사
+const h = _.cloneDeep(g);
+h[0].x = 99;
+console.log(g);
+console.log(h);
+```
+
+얕은 복사는 간단하게 할 수 있지만, 깊은 복사같은 경우는 구현하기가 어렵기에 lodash의 함수를 사용해서 깊은 복사를 해주었다.
+
+깊은 복사는 레벨이 얼마나 존재하는지 모르기에 성능상 불안정적인 부분이 있어서 얕은 복사로 최대한 해결해보고 안된다고 하면 깊은 복사를 사용하는게 맞다.
+
+즉, 그냥 상황에 맞게 얕은 복사와 깊은 복사를 사용하면 된다.
+
+## 렉시컬 스코프
+
+우리가 화살표 함수에서 this키워드를 사용할 때 렉시컬 스코프에서 정의가 된다고 배웠다.
+
+그래서 이번시간에 렉시컬 스코프에 대해서 좀 더 자세히 알아볼 것이다.
+
+**렉시컬 스코프(Lexical Scope)**
+
+- 정적 스코프(Static Scope)라고도 합니다.
+- 함수를 선언한 위치에서 유효하게 접근 가능한 유효 범위를 말합니다.
+
+```js
+const a = {
+ fnA() {
+ console.log("fnA", this);
+ const b = {
+ fnB() {
+ console.log("fnB", this);
+ const c = {
+ fnC() {
+ console.log("fnC", this);
+ console.log("a", a);
+ console.log("b", b);
+ console.log("c", c);
+ console.log("x", x);
+ },
+ };
+ return c;
+ },
+ };
+ return b;
+ },
+ fnX() {
+ console.log("fnX", this);
+ const x = {
+ fnY() {
+ console.log("fnY", this);
+ console.log("a", a);
+ console.log("b", b);
+ console.log("x", x);
+ },
+ };
+ },
+};
+a.fnA().fnB().fnC();
+a.fnY();
+```
+
+이 코드를 보면 렉시컬 스코프에 대해서 이해할 수 있다.
+
+fnC를 기준으로 설명을 해주게 된다면, fnC는 fnB에 속하고 fnB는 fnA에 속하기에 fnC입장에서 보면 fnA와 fnB의 범위가 유효한 범위이다. 하지만 fnC입장에서 fnX의 포함된 부분이 아니기에 fnC에서 fnX에 접근을 할 수 없다.
+
+이렇게 fnC의 상위의 범위를 렉시컬 스코프(정적 스코프)라고 부른다. 왜 정적 스코프라고도 부르냐면 fnC를 만드는 단계에서 정적으로 유효 범위가 정해지기 때문이다.
+
+물론 위 코드는 일반 함수이기에 호출된 위치에서 this키워드가 정의되지만 화살표함수라면 가장 먼저 만난 일반 함수의 this를 사용하게 될 것이다.
+
+## 클로저
+
+클로저(Closure)는 함수가 선언될 때의 렉시컬 스코프를 기억하고 있다가, 함수가 호출될 때 그 스코프에 접근할 수 있는 개념(특성)을 말합니다.
+
+```js
+let count1 = 0;
+function c1() {
+ return (count1 += 1);
+}
+console.log(c1());
+console.log(c1());
+console.log(c1());
+
+let count2 = 77;
+function c2() {
+ return (count2 += 1);
+}
+console.log(c2());
+console.log(c2());
+console.log(c2());
+
+////////////////////////////////////////////////
+
+function createCount(count) {
+ return function () {
+ return (count += 1);
+ };
+}
+const c3 = createCount(0);
+console.log(c3());
+console.log(c3());
+console.log(c3());
+const c4 = createCount(77);
+console.log(c4());
+console.log(c4());
+console.log(c4());
+```
+
+구분선 부분의 위쪽을 보게 되면 변수와 함수를 같이 만들어야지 함수를 사용해서 변수의 값을 늘릴 수 있다.
+
+이 부분을 축소하기 위해서 우리는 클로져라는 개념을 사용할 수 있다.
+구분선 아래 부분을 보면 c3이 함수를 반환받는데 c3라는 함수는 count라는 변수가 정의되어 있지 않다. 하지만 함수에서 반환을 할 때 count라는 함수를 사용하고 있기 때문에, c3함수가 호출이 될 때, 그 함수가 만들어질 때의 렉시컬 스코프를 가지고 있어서 count라는 변수에 접근할 수 있는 개념(특징)이다.
+
+**클로저의 사용예시**
+
+```js
+const h1El = document.querySelector("h1");
+
+let h1IsRed = false;
+h1El.addEventListener("click", () => {
+ h1IsRed = !h1IsRed;
+ h1El.style.color = h1IsRed ? "red" : "black";
+});
+```
+
+이런식으로 변수와 함수를 분리해서 사용하는 경우가 있는데 이러한 상황이 계속된다면 변수를 계속 만들어야 하는 불편함이 있을 것이다.
+
+이것을 클로저라는 특성을 활용하여 현재 코드를 더 효율적으로 만들어 관리할 수 있다.
+
+```js
+const h1El = document.querySelector("h1");
+
+const createToggleHandler = () => {
+ let isRed = false;
+ return (event) => {
+ isRed = !isRed;
+ event.target.style.color = isRed ? "red" : "black";
+ };
+};
+
+h1El.addEventListener("click", createToggleHandler());
+```
+
+이런식으로 코드를 작성하게 되면, 재사용도 가능하고 효율성도 좋아지기에 클로저를 사용할 수 있는 상황에서 사용하면 좋다.
+
+## 가비지 컬렉션과 메모리 누수
+
+**가비지 컬렉션**
+
+- 더 이상 사용되지 않는 메모리를 해제하는 프로세스로 자바스크립트 엔진이 자동으로 처리합니다.
+ **메모리 누수**
+- 더 이상 필요치 않은 데이터가 해제되지 못해 메모리에 계속 차지되는 것을 말합니다.
+
+가비지 컬렉션을 효율적으로 동작하기 위한 주의해야 할 점을 알아볼 것이다.
+
+### 메모리 누수가 되는 다양한 상황들
+
+**불필요한 데이터 참조를 피하세요!**
+
+```js
+// 불필요한 데이터 참조를 피하세요!
+const user = {
+ name: "Neo",
+ age: 85,
+ emails: ["abc@gmail.com", "xyz@naver.com"],
+};
+const removedEmail = user.emails.splice(1, 1);
+console.log(removedEmail);
+console.log(user.emails);
+```
+
+이런식으로 splice로 잘라서 확인하기 위해 변수 담아둔다면 가비지 컬렉션이 메모리를 순회하면서 저 부분을 찾아도 removedEmail이 참조를 하고 있기에 지울 수가 없어진다.
+
+그렇기에 확인을 하고나면 변수를 지워줘야 한다.
+
+**불필요한 전역 변수 사용을 피하세요!**
+
+```js
+// 불필요한 전역 변수 사용을 피하세요!
+window.hello = "Hello world!";
+window.thw = { name: "200won", age: 85 };
+```
+
+우리가 어디에서나 접근할 수 있는 객체를 전역 객체라고 부른다.
+window도 전역 객체이다. 이렇게 전역 객체에서 어떤 속성에 데이터를 할당하게 되면 우리가 직접 제거를 하지 않는 이상 데이터를 제거하는 상황을 만들기가 어렵다. 그렇기에 전체영역에서 사용할 수 있는 변수에게 데이터를 만드는 행위를 주의해야 한다.
+
+**제거된 요소가 참조되지 않도록 주의하세요!**
+
+```js
+const h1El = document.querySelector("h1");
+window.addEventListener("click", () => {
+ console.log(h1El);
+ h1El.remove();
+});
+```
+
+이렇게 되면 우리는 querySelector를 활용해서 h1El에 할당한 것이기에 화면상에서는 제거 되었지만, 저 변수가 사라지지 않는다면 메모리상에 계속 존재해 사용할 수 있게 된다.
+
+```js
+window.addEventListener("click", () => {
+ const h1El = document.querySelector("h1");
+ if (h1El) {
+ console.log(h1El);
+ h1El.remove();
+ }
+});
+```
+
+이런식으로 코드를 작성하게 되면, 완전히 가비지 컬렉션을 사용해서 제거할 수 있다.
+
+**불필요한 타이머를 해제하세요!**
+
+```js
+// 불필요한 타이머를 해제하세요!
+let a = 0;
+setInterval(() => {
+ a += 1;
+}, 100);
+setTimeout(() => {
+ console.log(a); // 10
+}, 1000);
+```
+
+이 부분의 코드는 메모리가 계속 낭비되고 있는 중이다. 왜냐하면 setInterval을 사용하면 setTimeout으로 값을 확인하고 나서도 계속 값이 늘어나기 때문이다.
+
+그러기에 타이머를 해제하는 코드가 필요해진다.
+
+```js
+// 불필요한 타이머를 해제하세요!
+let a = 0;
+const intervalId = setInterval(() => {
+ a += 1;
+}, 100);
+setTimeout(() => {
+ console.log(a); // 10
+ clearInterval(intervalId);
+}, 1000);
+```
+
+이런식으로 clearInterval이라는 함수를 사용해서 interval을 멈춰줄 수 있다.
+
+**불필요한 클로저를 제거하세요!**
+
+```js
+// 불필요한 클로저를 제거하세요!
+const getFn = (x) => {
+ return (name) => {
+ x += 1;
+ console.log(x);
+ return `Hello ${name}~`;
+ };
+};
+const fn = getFn(0);
+console.log(fn("Neo"));
+console.log(fn("Lewis"));
+fn("Evan");
+fn("Amy");
+```
+
+getFn을 호출하여 fn이 함수를 리턴받게 되었는데 그 함수에서 x가 사용되기에 렉시컬 스코프에서 x를 가져와서 사용하게 된다. 하지만 x는 출력하는 행위 외에는 아무 행동도 하고 있다. 이렇게 클로저가 발생되어 의미없는 변수를 참조해서 메모리가 사용되는 현상을 피해야한다.
+
+---
+
+지금까지 우리가 본 예시들 전부 불필요한 것들을 사용하지 않게 만드는 것이였다.
+그래서 우리가 코드를 작성할 떄는 꼭 필요한 내용만 넣어줘야 한다. 개발이 끝나게 되면 console.log같은 부분은 다 지워줘서 메모리를 불필요하게 차지하는 데이터를 사용하지 않게 만들 수 있다.
+
+## 콜 스택과 이벤트 루프
+
+자바스크립트는 저수준의 오래 걸릴 수 있는 일(Timer, Network 등)은 Web API에게 위임하고, 고수준의 작업은 자바스크립트 엔진(싱글 스레드)에서 처리하는 방식으로 빠른 속도와 확장성을 유지합니다.
+
+```js
+setTimeout(() => {
+ console.log(1);
+}, 0);
+window.addEventListener("load", () => {
+ console.log(2);
+});
+fetch("/").then(() => console.log(3));
+for (let i = 0; i < 1000; i++) {
+ console.log(4);
+}
+```
+
+그래서 이런 코드를 실행하였을 때, 작성한 순서대로 실행되는 것이 아니라 다른게 실행 될 수도 있는 현상이 발생한다.
+
+그럼 이제 콜 스택과 이벤트 루프에 대해서 알아보기 전에 두 가지 용어만 정리하고 가보자
+
+**FIFO(FIrst In First Out)**
+
+- 선입선출, 먼저 들어온 데이터가 먼저 나감
+ **LIFO(Last In First Out)**
+- 후입선출, 마지막에 들어온 데이터가 먼저 나감
+
+![[Pasted image 20240620165903.png]]
+자바스크립트 엔진입장에서는 Heap이라는 영역으로 메모리가 관리가 되고, Call Stack 즉 호출된 함수들의 내역이 쌓이는 곳이다.
+
+이 부분의 JS Runtime이 자바스크립트 엔진이 동작하는 부분이다.
+
+그리고 자바스크립트는 저수준의 오래걸리는 일들은 엔진에서 하지않고 브라우저에서 동작한다고 하였는데
+![[Pasted image 20240620170202.png]]
+바로 그것이 Web API라는 것이다. 이렇게 오래걸릴 수 있는 일들이 끝나게 되면 콜 스택으로 가는 것이 아니라. Queue라는 영역으로 가서 하나씩 쌓이게 된다.
+![[Pasted image 20240620170324.png]]
+Queue영역에서는 Call Stack의 함수가 전부 호출되서 비워지면 Event Loop라는 것을 통해서 Queue에 있는 순서대로 Call Stack으로 올라가 처리가 될 수 있다.
+
+```js
+function a() {
+ console.log("A");
+ function b() {
+ setTimeout(() => {
+ console.log("B1");
+ console.log("B2");
+ }, 0);
+ }
+ b();
+}
+function c() {
+ console.log("C");
+}
+function first() {
+ a();
+ c();
+}
+function second() {
+ c();
+}
+first();
+second();
+```
+
+코드와 위 사진을 보며 어떤식으로 동작되는지 생각해보자!