[2024.04.23 TIL] 내일배움캠프 7일차 (콜백 함수, 동기/비동기, DOM, 클래스)

My_Code·2024년 4월 23일
0

TIL

목록 보기
9/112
post-thumbnail

본 내용은 내일배움캠프에서 활동한 내용을 기록한 글입니다.
(강의보면서 따로 정리가 필요해 보이는 내용을 정리한 내용)


💻 TIL(Today I Learned)

📌 Today I Done

✏️ 4주차 (콜백 함수와 동기/비동기 처리)

1) 콜백함수

  • 다른 코드의 인자로 넘겨주는 함수를 의미
  • forEach나 SetTimeout 등은 이 콜백함수를 적절한 타이밍에 알아서 사용함
  • 다시 말하면, 제어권을 넘겨줄테니 너가 알고 있는 그 로직으로 처리해줘!
  • 콜백함수를 인자로 넘겨줌으로써 그 제어권도 함께 위임

2) 콜백 함수 내부의 this에 다른 값 바인딩하기

  • 콜백 함수 내부에서 this가 문맥에 맞는 객체를 바라보게 할 수는 없을까?
  • 가장 좋은 방법 -> bind 메서드 활용
var obj1 = {
    name: 'obj1',
    func: function () {
        console.log(this.name);
    }
};

//함수 자체를 obj1에 바인딩
//obj1.func를 실행할 때 무조건 this는 obj1로 고정해줘!
setTimeout(obj1.func.bind(obj1), 1000);

var obj2 = { name: 'obj2' };
//함수 자체를 obj2에 바인딩
//obj1.func를 실행할 때 무조건 this는 obj2로 고정해줘!
setTimeout(obj1.func.bind(obj2), 1500);

3) 동기 vs 비동기


4) 동기 : synchronous

  • 현재 실행중인 코드가 끝나야 다음 코드를 실행하는 방식
  • 계산이 복잡해서 CPU가 계산하는 데에 오래 걸림

5) 비동기 : asynchronous

  • 실행 중인 코드의 완료 여부와 무관하게 즉시 다음 코드로 넘어가는 방식
  • setTimeout, addEventListner 등
  • 요청, 실행 대기, 보류 등과 관련된 코드는 모두 비동기적 코드
  • 그래서 웹서버 통신에서는 대부분 비동기를 사용

6) 비동기 작업의 동기적 표현(1) - Promise(1)

  • Promise는 비동기 처리에 대해 끝나면 알려달라는 약속을 의미
  • Promise 인자로 넘어가는 콜백함수를 실행
  • 콜백함수 내에 resolve() 함수가 있다면 resolve 또는 reject가 실행되기 전까지 기다림
  • 확실히 콜백지옥은 없지만 중복인 부분이 많음
new Promise(function (resolve) {
    setTimeout(function () {
        var name = '에스프레소';
        console.log(name);
        resolve(name);
    }, 500);
}).then(function (prevName) {
    return new Promise(function (resolve) {
        setTimeout(function () {
            var name = prevName + ', 아메리카노';
            console.log(name);
            resolve(name);
        }, 500);
    });
}).then(function (prevName) {
    return new Promise(function (resolve) {
        setTimeout(function () {
            var name = prevName + ', 카페모카';
            console.log(name);
            resolve(name);
        }, 500);
    });
}).then(function (prevName) {
    return new Promise(function (resolve) {
        setTimeout(function () {
            var name = prevName + ', 카페라떼';
            console.log(name);
            resolve(name);
        }, 500);
    });
});

7) 비동기 작업의 동기적 표현(2) - Promise(2)

  • 중복되는 부분을 제거하기 위해서 중복되는 부분을 함수로 만듦
const addCoffee = (name) => {
    return function (prevName) {
        return new Promise(function (resolve) {
            setTimeout(function () {
                var newName = prevName ? `${prevName}, ${name}` : name;
                console.log(newName);
                resolve(newName);
            }, 300);
        });
    }
}

addCoffee("에스프레소")()
    .then(addCoffee(" 아메리카노"))
    .then(addCoffee(" 카페모카"))
    .then(addCoffee(" 카페라떼"));

8) 비동기 작업의 동기적 표현(3) - Generator

  • *가 붙은 함수가 제너레이터 함수
  • 비동기 작업이 완료되는 시점마다 next 메서드를 호출해주면 Generator 함수 내부소스가 위 -> 아래 순차적으로 진행 됨
var addCoffee = function (prevName, name) {
    setTimeout(function () {
        coffeeMaker.next(prevName ? prevName + ', ' + name : name);
    }, 500);
};

var coffeeGenerator = function* () {
    var espresso = yield addCoffee('', '에스프레소');
    console.log(espresso);
    var americano = yield addCoffee(espresso, '아메리카노');
    console.log(americano);
    var mocha = yield addCoffee(americano, '카페모카');
    console.log(mocha);
    var latte = yield addCoffee(mocha, '카페라떼');
    console.log(latte);
};

var coffeeMaker = coffeeGenerator();
coffeeMaker.next();

9) 비동기 작업의 동기적 표현(4) - Promise + Async/await

  • Promise ~ then과 동일한 효과를 얻을 수 있음
var addCoffee = function (name) {
    return new Promise(function (resolve) {
        setTimeout(function () {
            resolve(name);
        }, 500);
    });
};

var coffeeMaker = async function () {
    var coffeeList = '';
    var _addCoffee = async function (name) {
        coffeeList += (coffeeList ? ', ' : '') + await addCoffee(name);
    };
    await _addCoffee('에스프레소');
    console.log(coffeeList);
    await _addCoffee('아메리카노');
    console.log(coffeeList);
    await _addCoffee('카페모카');
    console.log(coffeeList);
    await _addCoffee('카페라떼');
    console.log(coffeeList);
};

coffeeMaker();

10) 비동기 -> 동기로 사용하는 이유? (중요)

  • 순서를 보장할 수 없는 과정에서 순서를 보장받기 위해 사용


✏️ 5주차 (DOM과 클래스, 클로저)

1) DOM이란?

  • Document Object Model
  • Document(HTML 파일)를 Javascript가 알아먹을 수 있는 Object 형태로 Modeling 한 것
  • 즉, 자바스크립트가 HTML 문서를 해석(파싱)한 결과물을 의미
  • 브라우저에 기본적으로 내장된 API
    => API : 다른 시스템에서 제공하는 기능을 사용할 수 있도록 도와주는 중간자 역할

2) Document관련 API

/** 찾아봅시다 */

// 해당 id명을 가진 요소 하나를 반환합니다.
document.getElementById("id명")

// 해당 선택자를 만족하는 요소 하나를 반환합니다.
document.querySelector("선택자")

// 해당 class명을 가진 요소들을 배열에 담아 인덱스에 맞는 요소를 반환합니다.
document.getElementsByClassName("class명")[인덱스]

// 해당 태그명을 가진 요소들을 배열에 담아 인덱스에 맞는 요소를 반환합니다.
document.getElementsByTagName("태그명")[인덱스]

// 해당 선택자를 만족하는 모든 요소들을 배열에 인덱스에 맞는 요소를 반환합니다.
document.querySelectorAll("선택자명")[인덱스]

// 새로운 노드를 생성합니다.
const div = document.createElement('div');
document.body.append(div);
document.body.append(div);

3) 클래스와 인스턴스

  • 클래스 : 다양한 종류의 책상을 만드는 일종의 설계도
  • 인스턴스 : 설계도를 보고 만들어진 실제 책상

4) Class란?

  • 객체를 생성하기 위한 일종의 템플릿 (붕어빵 틀)
  • constructor() 라는 생성자 함수를 사용
  • new 연산자를 통해서 새로운 객체(인스턴스)를 생성
  • 클래스 내에 있는 메서드에서는 this를 이용해서 객체의 요소 접근
class Person {
    // constructor는 이름을 변경할 수 없어요.
    constructor(name, age) {
        // 여기서 말하는 this는 만들어질 인스턴스를 의미한다고 생각해주세요!
        this.name = name;
        this.age = age;
    }
    // 다양한 메소드를 아래와 같이 정의할 수 있어요.
    // 여기서 this.name으로 내부 값을 접근해야 함을 잊지 마세요! :)
    sayHello() {
        console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`);
    }
}

// person1, person2 인스턴스 생성
const person1 = new Person("Alice", 30);
const person2 = new Person("Bob", 25);

// 만든 객체를 토대로 메서드 호출해보기
person1.sayHello(); // 출력: "Hello, my name is Alice and I am 30 years old."
person2.sayHello(); // 출력: "Hello, my name is Bob and I am 25 years old."

5) Getter, Setter 연습

class Car {
    constructor(modelName, modelYear, type, price) {
        this._modelName = modelName;
        this._modelYear = modelYear;
        this._type = type;
        this._price = price;
    }
    get modelName() {
        return this._modelName;
    }
    // 입력값에 대한 검증까지 가능하다
    set modelName(value) {
        // 유효성 검사
        if (value.length <= 0) {
            console.log("[오류] 모델명이 입력되지 않았습니다. 확인해주세요!");
            return;
        } else if (typeof value !== "string") {
            console.log("[오류] 입력된 모델명이 문자형이 아닙니다!");
            return;
        }
        // 검증이 완료된 경우에만 setting!
        this._modelName = value;
    }
    get modelYear() {
        return this._modelYear;
    }
    set modelYear(value) {
        // 유효성 검사
        if (value.length !== 4) {
            console.log("[오류] 입력된 년도가 4자리가 아닙니다.확인해주세요!");
            return;
        } else if (typeof value !== "string") {
            console.log("[오류] 입력된 모델명이 문자형이 아닙니다!");
            return;
        }
        // 검증이 완료된 경우에만 setting!
        this._modelYear = value;
    }
    get type() {
        return this._type;
    }
    set type(value) {
        if (value.length <= 0) {
            console.log("[오류] 타입이 입력되지 않았습니다. 확인해주세요!");
            return;
        } else if (value !== "g" && value !== "d" && value !== "e") {
            // g(가솔린), d(디젤), e(전기차)가 아닌 경우 오류
            console.log("[오류] 입력된 타입이 잘못되었습니다. 확인해주세요!");
            return;
        }
        // 검증 완료!
        this._type = value;
    }
    get price() {
        return this._price;
    }
    set price(value) {
        if (typeof value !== "number") {
            console.log("[오류] 가격으로 입력된 값이 숫자가 아닙니다. 확인해주세요!");
            return;
        } else if (value < "1000000") {
            console.log("[오류] 가격은 100만원보다 작을 수 없습니다. 확인해주세요!");
            return;
        }
        // 검증이 완료된 경우
        this._price = value;
    }

    makeNoise() {
        console.log(`${this._modelName} : 빵빵!!!`);
    }

    showYear() {
        console.log(`${this._modelName}${this._modelYear}년도식 모델입니다.`);
    }
}


const car1 = new Car('Sorento', '2023', 'e', 5000);
const car2 = new Car('SM5', '1999', 'g', 3000);
const car3 = new Car('Palisade', '2010', 'd', 4500);

// car1.showYear();
// car1.makeNoise();
// car2.showYear();
// car2.makeNoise();
// car3.showYear();
// car3.makeNoise();

// getter 예시1
console.log(car1.modelName);
// setter 예시1
car1.modelName = "Santafe";
console.log(car1.modelName);

6) 상속 (Inheritance)

  • 오버라이딩 : 부모로부터 물려받은 메서드를 재정의하는 것
    => 메서드 이름 일치
  • 오버로딩 : 부모로부터 물려받은 메서드의 인자를 다르게 설정하는 것
    => 근데 자바스크립트에서는 문법상 존재x
    => 메서드 이름 일치
    => 매개변수의 개수 또는 타입이 달라야 함
  • 자식 클래스 생성 시 constructor가 다르면 super()를 통해 부모에게도 알려
class Car {
    constructor(modelName, modelYear, type, price) {
        this.modelName = modelName;
        this.modelYear = modelYear;
        this.type = type;
        this.price = price;
    }
    // 모델명 출력하는 메서드
    makeNoise() {
        console.log(`${this.modelName}: 빵!`);
    }
    // getters
    getModelName() {
        return this.modelName;
    }
    getModelYear() {
        return this.modelYear;
    }
    getPrice() {
        return this.price;
    }
    // 새로운 가격 반영
    setPrice(newPrice) {
        this.price = newPrice;
    }
}

// 전기차 클래스
class ElectronicCar extends Car {
    constructor(modelName, modelYear, price, chargeTime) {
        // Car에 있는 constructor를 통해 자동차를 만들었어요!
        super(modelName, modelYear, "e", price);
        this.chargeTime = chargeTime;
    }
    setChargeTime(time) {
        this.chargeTime = time;
    }
    getChargeTime() {
        return this.chargeTime;
    }
}

// 자동차 만들기
const car1 = new Car("Sorento", "2023", "e", 5000);
const car2 = new Car("SM5", "1999", "g", 3000);
const car3 = new Car("QM6", "2010", "g", 4500);

car1.makeNoise();
car2.makeNoise();
car3.makeNoise();

console.log(car1.getModelName());
console.log(car1.getModelYear());
console.log(car1.getPrice());

// 가격변동
car1.setPrice(6000);
console.log(car1.getPrice());


const eleCar1 = new ElectronicCar("뉴아이오닉5", "2023", 7000, 60);

console.log(eleCar1.getModelName()); // 추가
console.log(eleCar1.getChargeTime()); // 추가
eleCar1.setChargeTime(10); // 추가
console.log(eleCar1.getChargeTime()); // 추가

7) 정적 메서드(Static Method)

  • 인스턴스를 만들 필요가 없을 때 사용
  • 인스턴스 간에 복제해서 만들지 않을 때 사용
  • 똑같은 것을 공유해서 사용하는 경우
class Calculator {
    static add(a, b) {
        return a + b;
    }

    static subtract(a, b) {
        return a - b;
    }
}

console.log(Calculator.add(3, 5));  // 8
console.log(Calculator.subtract(3, 5));  // -2

8) 클로저

  • 클로저 는 함수와 그 함수가 선언된 렉시컬 환경과의 조합
  • 함수가 선언된 렉시컬 환경 -> 함수가 선언될 당시의 외부 변수등의 정보
  • 렉시컬 스코프
    => JS엔진은 함수를 어디서 호출 했는지가 중요한 게 아님
    => 어디에 정의 했는지에 따라서 스코프(상위 스코프)를 결정함
  • 정의된 환경에 대한 정보를 저장하는 곳 : outer

9) 클로저와 렉시컬 환경

  • 외부 함수보다 중접 함수가 더 오래 유지되는 경우, 중첩 함수는 이미 생명 주기가 종료한 외부 함수의 변수를 (여전히) 참조할 수 있음
const x = 1;

function outer() {
    const x = 10;

    const inner = function () {
        console.log(x);
    };
    return inner;
}

const innerFunc = outer();
innerFunc();

// 실행 컨텍스트에서 outer()는 inner()를 return하고 
// 끝났음에도 불구하고 여전히 outer()의 변수, 
// 즉 inner()의 외부 변수를 참조하고 있음
// 이는 똑똑한 가비지 컬렉터가 안쓰는 것만 가져가기 때문

10) 클로저의 활용

  • 클로저는 주로 상태를 안전하게 변경하고 유지하기 위해 사용
  • 상태를 안전하게 은닉 (특정 함수에게만 변경 허용)
  • 아래 링크를 참고 함

    https://poiemaweb.com/js-closure

// 함수를 반환하는 고차 함수
// 이 함수는 카운트 상태를 유지하기 위한 자유 변수 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

📌 Tomorrow's Goal

✏️ 강의 내용 복습하기

  • TIL에 작성한 내용들 복습하기
  • 특히 3주차, 4주차 더 공부하기

✏️ 개인과제 시작하기

  • 개인과제 시작하기


📌 Today's Goal I Done

✔️ 자바스크립트 문법 강의 시청(개인 공부)

  • 생각보다 제공받은 강의에서 자바스크립에 대해서 구체적으로 설명함
  • 기본적인 내용을 알기에 빠르게 보면서 잘 모르는 부분만 반복 학습
  • 여기에 작성한 내용들은 강의를 보면서 처음보거나 헷갈리는 내용을 정리함

profile
조금씩 정리하자!!!

0개의 댓글