## What is this PR?
- 칸반 칼럼 레이아웃 추가
- 칸반 리스트 높이 고정. height 600px.
- 칸반 리스트 overflow 발생 시, 스크롤 발생
## Changes
- container 너비 고정 및 중앙 정렬. width : 1280px
- .column-container 및 .column의 width 제거
- container 크기에 유동적으로 변화
## Screenshots
![image](
)
close #1
localhost:7979/movies/3492902/api/movies/585888 로 url이 변경되는 이슈?
await fetch('api/movies/128124') 하게 되면 상대경로로 요청하게 된다.
즉, await fetch('/api/movies/128712')로 앞에 슬래시를 붙여줘야 위와 같이 문제가 생기지 않고 localhost:7979/movies/69685가 된다.
movies 평균 평점 데이터 저장을 따로 할지 score 데이터들만 가지고 매번 평균을 계산해올지 결정
반정규화 기법
계산된 컬럼 추가
•
배치 프로그램으로 총판매액, 평균잔고, 계좌평가를 미리 계산하고 그 결과를 특정 칼럼에 추가한다.
await를 뭉치기 위해 Promise 공부 + dummy data를 resolve해주는 new Promise 사용해서 await result();
Promise 객체
웹에서 데이터를 얻어서 보내주거나
얻지 못한다면 (에러 발생시) 이유와 함께 에러를 발생시키도록 한 약속!
.then() 은 Promise 객체 내에서 resolve()에 전달된 데이터를 받는다.
.catch() 는 Promise 객체 내에서 에러 발생시 reject()에 전달된 데이터를 받는다.
Promise의 3가지 상태
•
Pending (대기)
•
Fulfilled (완료)
•
Rejected (거부)
•
순서도
1.
pending
1-1 -resolve-> 2. fulfilled (settled)
1-2 -reject-> 2. pending (settled)
JSON.parse
string 을 json 객체로 변환시켜주는 메서드.
const json = '{"result":true, "count":42}';
const obj = JSON.parse(json);
JavaScript
복사
Promise.resolve()
주어진 값으로 이행하는 Promise.then 객체를 반환합니다. 그 값이 프로미스인 경우, 해당 프로미스가 반환됩니다
resolve()로 값을 전달해주지 않으면 Promise의 state가 pending에서 변하지 않는다. resolve()를 사용하고 종료된다면 state는 fulfilled 된다.
const promise1 = Promise.resolve(123);
//or
const promise1 = new Promise((resolve, reject) => {
resolve(123);
});
// then의 인자로 resolve의 인자(123)가 들어간다.
promise1.then((value) => {
console.log(value);
// expected output: 123
});
// 아래처럼 배열도 인자로 사용가능.
var p = Promise.resolve([1,2,3]);
p.then(function(v) {
console.log(v[0]); // 1
});
JavaScript
복사
파라미터가 1개일때 함수이름만 쓰면, 암묵적으로 함수의 매개변수로 전달 됨(?)
getHen()
.then(getEgg)
.then(cook)
.then(console.log);
Plain Text
복사
Promise.reject()
거부된 Promise 객체를 반환합니다.
아래처럼 에러가 발생한 경우 reject로 객체를 넘긴다.
const promise1 = new Promise((resolve, reject) => {
fs.readFile(filePath, 'utf8', (err, data)=>{ // 뭔가 요청을 안에서 한다.
if(err) {
reject(err)
}else {
resolve(data)
}
});
});
promise1.then(value => console.log(value));
//에러 발생시 에러가 콘솔창에 찍힌다.
JavaScript
복사
Promise.catch()
Promise 객체를 리턴하며, reject 된 케이스만 다룬다.즉, 에러가 발생한 경우에만 호출된다.
const promise1 = new Promise((resolve, reject) => {
throw 'Uh-oh!';
});
promise1.catch((error) => {
console.error(error);
});
// expected output: Uh-oh!
JavaScript
복사
.then()으로 체이닝하며 일련의 과정을 수행시켰을 경우,수행 과정 중 에러가 발생했을 때.catch()내부의 콜백함수가 실행되어서 에러핸들링을 하게 된다.에러가 발생한 뒤의 .then()은 수행되지 않는다.
끝까지 에러가 발생하지 않으면 마지막 .then()까지 실행하고.catch()는 실행하지 않는다.
Promise.all()
Promise 객체 배열을 인자로 받아서,인자로 받은 Promise를 모두 병렬적으로 동시에 실행하고,모든 Promise가 호출이 끝나기까지 기다렸다가 결과를 한번에 반환한다.
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo'); // func, delay, param1, param2,,,
});
Promise.all([promise1, promise2, promise3]).then((values) => {
console.log(values);
});
// expected output: Array [3, 42, "foo"]
// promise3은 100ms의 지연이 있어서 이게 완료되면 한번에 반환
JavaScript
복사
아래는 async function으로 병렬적으로 호출하는 방법 (Promise.all()을 쓰는게 더 좋다.)
function delay(ms){
return new Promise(resolve => setTimeout(resolve, ms));
}
async function getApple() {
await delay(1000);
return 'Apple';
}
async function getBanana() {
await delay(2000);
return 'Banana';
}
async function pickFruits() {
const applePromise = getApple();
const bananaPromise = getBanana();
const apple = await applePromise;
const banana = await bananaPromise;
return `${apple} + ${banana}`;
}
pickFruits().then(console.log);
//Promise 두개가 병렬적으로 실행되므로 3초 뒤가 아니라 2초 뒤에 'Apple + Banana'가 출력됨
//Promise.all 을 사용하면
function pickAllFruits() {
return Promise.all([getApple(), getBanana()])
.then(fruits => fruits.join(' + '));//fruits === ['Apple', 'Banana']
}
pickAllFruits().then(console.log);
// 병렬적으로 호출했을 때 가장 먼저 완료되는 Promise를 출력하기
function pickFastOne() {
return Promise.race([getApple(), getBanana()]);
}
pickFastOne().then(console.log);
//Banana보다 짧은 시간이 걸리는 Apple만 출력된다.
JavaScript
복사
async function ( async & await )
function에 async만 붙여주면 Promise 객체를 리턴하는 함수로 만들 수 있다! like Magic!
에러 처리는 try ~ catch 구문을 이용해야 한다
function resolveAfter2Seconds() {
return new Promise(resolve => {
setTimeout(() => {
resolve('resolved');
}, 2000);
});
}
async function asyncCall() { // 프로미스 객체를 리턴하는 함수가 되버린다.
console.log('calling');
try {
const result = await resolveAfter2Seconds();
} catch(error) {
console.log(error);
}
console.log(result);
// expected output: "resolved"
}
asyncCall();
JavaScript
복사
await
await 키워드는 Promise를 기다리기 위해 사용됩니다. async function 내부(코드블럭)에서만 사용할 수 있다.
Promise 객체를 리턴하는 함수는(async 키워드가 안붙어도) 모두 await로 완료를 기다리게 만들 수 있다.
function resolveAfter2Seconds(x) { // async가 안 붙었지만 Promise 객체 리턴
return new Promise(resolve => {
setTimeout(() => {
resolve(x);
}, 2000);
});
}
async function f1() {
var x = await resolveAfter2Seconds(10);// x에는 resolve(x)로 리턴된 x가 할당된다.
// Promise객체를 리턴하는 함수이므로 await로 비동기적으로 함수실행하고 종료는 동기적으로 기다리게 만들 수 있다.
console.log(x);// 10// console.log(x)는 await를 기다렸다가 호출된다.
}
f1();
//10초 뒤에 10이 콘솔에 찍힘
JavaScript
복사
await을 안쓴 경우와 쓴 경우
fetch('http://www.naver.com/data.json')
.then(response => response.json())
.then(json => {
console.log(json)
})
async function request() {
let response = await fetch('http://www.naver.com/data.json')
let json = await response.json()
console.log(json)
}
JavaScript
복사
additional
•
node.js 와 브라우저의 공통점 Promise 객체 사용가능.
•
브라우저에선 fs.readFile 못쓰고 node.js 에서 가능.
•
node.js에는 fetch()가 없다. 3rd party 사용해야함.
Parallel
지금까지 배운 내용들을 토대로 Parallel 하게 두 API 에 접근해야 한다고 생각해봅시다.
all 을 통해서 fetch 두 개를 받고 다시 all 을 한 번 더 사용하여 json 형식으로 바꿔줍니다.
나머지 부분에 대해서는 위와 동일합니다.
movieListAsync =async () => {
try {
const [moviesResponse, suggestionsResponse] =await Promise.all([
fetch("https://yts.lt/api/v2/list_movies.json"),
fetch("https://yts.lt/api/v2/movie_suggestions.json?movie_id=100")
]);
const [movies, suggestions] =await Promise.all([moviesResponse.json(), suggestionsResponse.json()]);
console.log(movies, suggestions);
}catch(e){
console.log(e);
}finally {
console.log("finally");
}
};
JavaScript
복사
for, for...of는 순차적 실행되지만 forEach는 아니다?
forEach 는 배열 요소를 돌면서 callback을 실행할 뿐, 한 callback이 끝날때 까지 기다렸다가 다음 callback을 실행하는 것이 아니다.
Q. 지금 아래의 코드는 result가 1초 후 한꺼번에 10개가 출력되는데, 이걸 1초 간격으로 10번 출력되게 코드를 고쳐주세요.
function test() {
const promiseFunction = () =>
new Promise((resolve) => setTimeout(() => resolve("result"), 1000));
Array(10)
.fill(0)
.forEach(async () => {
const result = await promiseFunction();
console.log(result);
});
}
test();
JavaScript
복사
forEach는 자신이 실행하는 callback 함수가 비동기 작업을 하는지 안하는지는 아무런 관심이 없다. forEach에 의해 즉각적으로 실행된 10개의 callback 들은 다같이 1000ms를 기다리고, 1000ms가 되는 순간 순차적으로 event loop에 의해 call stack으로 옮겨져 하나씩 실행된다.
리팩터링
성능측정
getMoviesDetailsById() 최적화
await는 동기적으로 처리되므로 232ms로 느리다.
60ms로 빠르다. Promise.all()로 병렬 실행시킨다.
getMoviesWithCountry() 최적화
for...of문은 순차처리가 된다. 1s나 걸린다.
1초후 10개 출력되는 코드를 1초 간격으로 10번 출력되게 코드를 고쳐주세요. → for, for...of문을 사용한다.
map으로 먼저 프로미스를 담은 배열로 만들어두고 Promise.all로 병렬적으로 실행시켰다. 198ms로 빠르다.
promise.all 을 이용한 병렬 처리는 순차적으로 병렬 처리하는 것과는 다르다는 것
6. Promise.all 이 forEach와 다른 점
여기까지 읽고 나면, "앞서 1번에서 소개된 면접질문 (forEach 사용 코드) 역시 10개의 코드가 1초만에 모두 resolve 되니까, 이것도 병렬처리 아닌가? 왜 궂이 Promise.all 을 사용해야 하지?" 라는 의문이 들 수도 있다. 적어도 나는 그런 의문이 들었다. (머쓱)
forEach를 사용한 아래 코드를 보면 둘의 확실한 차이를 알 수 있다.
const target_url = ["ur11", "ur12", "url3"];
function async_download(url) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(url);
resolve();
}, 1000);
});
}
async function parallel(array) {
array.forEach(async (url) => {
await async_download(url);
});
// all done은 언제 찍힐까?
await console.log("all done :)");
}
parallel(target_url);
JavaScript
복사
Promise.all에서 그랬던 것 처럼, 병렬처리가 완료된 후 어떤 작업 (all done) 을 하길 의도하고 코드를 작성했지만, 실상은 "all done :)" 이 가장 먼저 출력되고 있다.
이는 아래와 같이 then 을 사용해서 코드를 바꿔도 마찬가지이다.
async function parallel(array) {
array.forEach(async (url) => {
await async_download(url);
});
}
parallel(target_url).then(console.log("all done :)"));
JavaScript
복사
모든 비동기 작업이 끝나고 수행되길 원했던 함수가 기대를 저버리고 맨 처음 실행되고 있다. 왜일까?
forEach 는 배열을 돌며 callback을 호출하기만 하면 맡은 바 임무를 다 한 것으로 생각하고 종료된다. 사실 호출한 callback들은 pending 상태로 resolve 되지 않았지만, forEach 입장에서는 할 일을 다 한 것이다.
배열의 비동기 작업에 forEach를 사용하면 순차처리이든, 병렬처리이든 올바르게 작동하기 힘든 이유가 여기에 있다. forEach는 콜백만 실행하고 끝나버리기에 비동기 작업의 처리 상태를 추적하지 못하고, 따라서 이후의 흐름을 제어하기도 어렵다.
하지만 map과 promise.all을 사용하면 callback들이 return하는 promise 들을 새로운 배열에 잘 담아두었다가 모든 promise가 resolved 되는 타이밍을 감지할 수 있다.
따라서 배열의 요소들에 비동기 작업을 실시한 후 (순차든, 병렬이든), 어떤 작업을 해야 한다면 forEach가 아닌 map과 Promise.all을 사용하는 것이 좋다.
7. 정리
•
배열의 요소들에 비동기 작업을 실시할 때는 순차처리 와 병렬처리 중 어떤 방식이 필요한지에 따라 접근법이 달라진다.
•
forEach를 비롯한 배열의 요소에 callback을 실행하는 방식인 ES5 array methods (map, filter, reduce...) 를 사용할 땐, callback이 async 하더라도 전체 method는 async 하지 않음을 유의해야 한다.
•
일반적으로 순차처리는 for 또는 for...of 문을 통해, 병렬처리는 map + promise.all 을 통해 구현할 수 있다.
•
순차처리가 꼭 필요한 상황이 아니라면, 성능상으로 유리한 병렬처리를 고려하자.
비동기 함수의 콜백함수의 리턴값을 어떻게 받아오는가?Promise
Promise 객체는 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타냅니다.
더 리팩터링하기
const [responseData, responseCreditsData, responseCertificationData] = await Promise.all(
await Promise.all([
fetch(`${apiBaseUrl}movie/${movieId}?api_key=${apiKey}`),
fetch(`${apiBaseUrl}movie/${movieId}/credits?api_key=${apiKey}`),
fetch(`${apiBaseUrl}movie/${movieId}/release_dates?api_key=${apiKey}`),
]).then(promises => promises.map(promise => promise.json()))
);
JavaScript
복사
내부의 await Promise.all()한 값은 뭘까? 배열에 [ Response, Response, Response ,,,]라는 프로미스들의 배열이 들어올 것이다.
그럼 이걸 다시 프로미스로 감싸주어야 await Promise()해줄수 있다?
정답 코드라고 여겨짐. 이걸 await하면 값이 나옴
console.log(
await Promise.all([
fetch(`${apiBaseUrl}movie/${movieId}?api_key=${apiKey}`),
fetch(`${apiBaseUrl}movie/${movieId}/credits?api_key=${apiKey}`),
fetch(`${apiBaseUrl}movie/${movieId}/release_dates?api_key=${apiKey}`),
]).then(promises => promises.map(promise => promise.json()))
);
JavaScript
복사
console.log(
await Promise.all([
fetch(`${apiBaseUrl}movie/${movieId}?api_key=${apiKey}`),
fetch(`${apiBaseUrl}movie/${movieId}/credits?api_key=${apiKey}`),
fetch(`${apiBaseUrl}movie/${movieId}/release_dates?api_key=${apiKey}`),
])
);
JavaScript
복사