[TIL] 스파르타) JS/SQL 강의 수강, TMDB 프로젝트 수정
[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가 엄청 유용한 기능이라고하니 당장에 쓸 곳이 떠오르지는 않지만 추후 프로젝트들에서 적용할 기회를 포착해봐야겠다.