ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [TIL] 스파르타) JS/SQL 강의 수강, TMDB 프로젝트 수정
    TIL-sparta 2024. 4. 24. 19:42

     

     

    [KDC] 엑셀보다 쉽고 빠른 SQL - 1주차 | Notion

    매 주차 강의자료 시작에 PDF파일을 올려두었어요!

    teamsparta.notion.site

     

     

    [스파르타코딩클럽] JS 문법 종합반 | Notion

    설치 프로그램

    teamsparta.notion.site

    SQL 1주차, JS 문법 종합반 4~5주차 강의 수강 완료

     

    --

     

    학습 키워드: MySQL, SELECT, WHERE, query, javascript, promise, async, await, generator, class, inheritance, static, closure

     

    1. SQL SELECT 연습

    SELECT ANIMAL_ID, NAME FROM ANIMAL_INS
        WHERE INTAKE_CONDITION<>'Aged'

     - <>: where에서 사용하는 '같지 않다' 의 의미를 갖는 operator.

     - "ANIMAL_INS 테이블에서 INTAKE_CONDITION이 'Aged'와 다른 데이터의 ANIMAL_ID 와 NAME 컬럼"을 조회하게 된다.

     

     

    2. Promise, Async, Await

     - 자주 언급하던 그 '예전 프로젝트'에서 async await을 많이 썼었다. 거기에서 await이 받는 객체가 promise라고 알고 있어서 'promise를 사용했다' 라고 얘기해왔는데, 오늘 강의를 보니 promise는 ES6에서 나왔고 async await은 ES7에서 소개된 방식이라고 한다. '혹시 내가 뭔가 잘 못 이해하고 있었나?' 라는 생각이 들어서 한 번 알아봤다.

     - 먼저 await이 기다려주는 객체는 promise 객체가 맞다. 이 부분은 정확했는데 문제는 이 promise 객체를 받는 방법을 잘못 이해하고 있었던 것 같다. 내배캠 2주차 개인 과제를 진행하면서도 async / await 을 사용했으므로 해당 코드를 고치면서 예를 들어보겠다.

    // 기존 버전 (잘못된 예시, 작동은 함)
    const getTopRatedMoviesList = async (isLocal) => {
        let list = {};
        await fetch(isLocal? LOCAL_FILE : URL, options)
            .then(res => res.json())
            .then(data => {
                list = data['results'] || data;
                // console.log("TMDB list retrieved.");
            })
            .catch(err => {
                console.error(err);
            });
        console.log(list);
        return list;
    }
    // 수정한 버전
    const fetchTopRatedMoviesList = async (isLocal) => {
        try{
            let data = await (await fetch(isLocal? LOCAL_FILE : URL, options)).json();
            console.log(data);
            return data["results"]; // 로컬 파일 수정으로 '|| data' 부분이 필요없어짐
        } catch (err) {
            console.log(err);
        }
    }

     - 영화 목록을 TMDB (혹은 isLocal이 true면 로컬 파일)에서 받아오는 스크립트다. 두 버전은 정상적으로 동작하고 input과 output이 동일하다. 하지만 기존 버전의 스크립트는 ES6 (2015) 의 방식과 ES7 (2017) 의 방식을 혼용했는데, 뭔가 어설프게 구현했다고 할 수 있겠다. Promise 객체 뒤에 .then() 을 이어 붙여서 callback 함수를 작성하는 방식은 ES6에서 소개된 것 (정확히는 ES6에서 promise가 처음 소개되고 한 달 뒤라고 한다) 으로, async await 없이도 사용할 수 있어야 한다. 반면에 수정한 버전은 ES7의 async 와 await 만을 사용하여 동일한 데이터를 받아올 수 있게 되어서 error handling을 위한 try catch 문을 추가했음에도 스크립트가 훨씬 간결해졌다.

     

     

    3. Generator (function*)와 yield, next

     - Iterator를 반환한다고 한다. yield의 선언때마다 멈춰서 비동기 진행을 처리하고, next를 호출해 멈춘 위치부터 재개하는 방식이라고 한다. ES6에서 소개되었다고 한다. 살짝 조사해보니 커다란 사이즈의 async 작업을 처리해야할 때 구간 구간 나누어 처리하는 방식으로도 쓴다고 한다.

    var addCoffee = function (prevName, name) {
    	setTimeout(function () { // 3) 500 millis (0.5초) 이후에 callback 실행
    		coffeeMaker.next(prevName ? prevName + ', ' + name : name); // 4) next 호출로 generator iteration 재개
    	}, 500);
    };
    var coffeeGenerator = function* () {
    	var espresso = yield addCoffee('', '에스프레소'); // 2) yield 마다 멈춤
    	console.log(espresso);
    	var americano = yield addCoffee(espresso, '아메리카노'); // 5) 재개된 iteration이 yield에서 멈춘다 (3~5를 '카페라떼'까지 반복)
    	console.log(americano);
    	var mocha = yield addCoffee(americano, '카페모카');
    	console.log(mocha);
    	var latte = yield addCoffee(mocha, '카페라떼');
    	console.log(latte);
    };
    var coffeeMaker = coffeeGenerator();
    coffeeMaker.next(); // 1) 첫 번째 next()로 시작

     - coffeeGenerator 가 generator function 인데, yield마다 addCoffee 라는 함수를 호출하고 해당 함수 내의 비동기 함수인 setTimeout에서 callback으로 다시 coffeeMaker.next()를 호출하는 구조다.

     - 이렇게 하고 맨 아래의 coffeeMaker.next()를 호출하면 가장먼저 var espresso 라인의 yield에서 addCoffee를 실행하며 멈추고, setTimeout에서 지정한 시간(0.5초)가 흐르면 그 callback에서 다시 여태까지 추가된 커피 이름 목록을 인자로하는 coffeeMaker.next()가 호출되면서 generator의 iteration이 0.5초 간격으로 순차적으로 재개된다.

     

     

    4. Class와 Inheritance, 그리고 static method

     - 다른 언어에서 많이 배운 클래스와 상속이다. 다만 Javascript에서의 class와 inheritance의 작성 방식은 이전에 배웠던 언어들과 미묘한 차이가 있으므로 어떤 식으로 작성되는지 간단하게 알아보자.

    class Animal {
        // js에서는 클래스 이름 대신 constructor 키워드로 constructor를 선언한다
        constructor(name, age, numLegs) {
            /* property name과 get/set 메소드들의 이름이 겹치지 않도록
             이름 앞에 underscore('_')를 붙여준다. (js convention) */
            this._name = name;
            this._age = age;
            this._numLegs = numLegs;
        }
    
        // setter는 set, getter는 get 키워드를 이용하여 작성한다.
        set name(name) {
            // do some validation if necessary
            this._name = name;
        }
    
        get name() {
            // do something if necessary
            return name;
        }
        
        ...
    
        makeNoise() {
            console.log('???');
        }
    }
    
    // 상속은 마찬가지로 extends를 사용한다.
    class Dog extends Animal {
        constructor(name, age) {
            // 상위 클래스의 컨스트럭터를 호출할 땐 Java처럼 super()를 이용한다.
            super(name, age, 4);
        }
    
        // override할 메소드는 다시 작성한다. 별도의 표시는 필요 없다.
        makeNoise() {
            console.log('bark');
        }
    }

     - JS에서는 클래스의 전역변수를 property라고 부른다. 프로퍼티의 getter와 setter는 get, set 키워드로 작성되는데, 프로퍼티 이름을 이 녀석들이 사용하고 프로퍼티는 이름 앞에 underscore ( _ )를 붙여주는 convention이 있다고 한다.

     - property는 따로 선언해주지 않아도 this로 할당할 때 알아서 선언된다.

     - static method: 이 부분은 다른 언어랑 똑같다. 객체에서 호출하는 method와 달리 static method는 클래스에 묶인채로 호출한다.

     

     

    5. Closure

     - 완전히 처음 들어보는 개념이 5주차 마지막 강의에 나왔다. 자바스크립트에 존재하는 독특한 구조인데, call stack과 scope, 그리고 GC(Garbage Collection) 와 관련이 있다. 먼저 코드를 살펴보자.

    let num = 0; // global scope
    
    const increase = (function () { // outer function scope
      // 카운트 상태 변수
      let num = 0;
    
      // 클로저
      return function () { // inner function scope
        return ++num;
      };
    })();
    
    console.log(increase()); // 1
    console.log(increase()); // 2
    console.log(num); // 0
    num = 45;
    console.log(increase()); // 3

     - increase 라는 function이 있는데, 이 function은 어떤 function (funcA라고 지칭)이 return값으로 넘겨준 그 내부 function (funcB라고 지칭)이다.

     - 이 구조에서 funcB가 그 외부 scope인 funcA의 scope에 존재하는 num을 참조하는데, 이 상태로 function이 return되면 funcA가 더 이상 사용되지 않더라도 GC에서 'increase의 값인 함수 funcB'의 outer scope가 되는 'funcA의 scope에 존재하는 num'을 처리하지 않는다는게 핵심이다.

     - 따라서 이 후에 전역 scope에서 increase()를 따로 콜해도 전역 scope의 num을 참조하는것이 아니라 funcA 스코프의 num을 참조하고 있는 상태가 유지되어 해당 변수를 다른 scope의 작업에서 발생할 수 있는 불시의 변경으로 부터 보호할 수 있게 된다.

     - 자바스크립트에서는 함수의 스코프가 호출 당시가 아닌 정의될 당시의 scope로 결정되기 때문이라고 한다. Closure가 엄청 유용한 기능이라고하니 당장에 쓸 곳이 떠오르지는 않지만 추후 프로젝트들에서 적용할 기회를 포착해봐야겠다.

     

     

    728x90
Designed by Tistory.