헷갈리는 내용들

해피데빙·2022년 3월 26일
0

TIL

목록 보기
42/45

Credentials
:쿠키, 인증헤더, TLS client certificates(증명서)를 말한다.

credentials : true

다른 도메인간에 쿠키 공유를 허락하는 옵션
default는 false
서버간 도메인이 다른 경우 이 옵션을 활성화하지 않으면 로그인되지 않을 수 있다
header의 Access-Control-Allow-Credential: true 로 만들어준다

ex. 로그인
(db에서 id, pw를 조회해서 있으면 '로그인이라는 상태를 저장해주는 토큰'을 발급 받는 것)
토큰을 저장하는 게 refresh는 cookie, access는 state에 저장하기도
그러므로 cookie에 저장하니까 이 조건을 활성화해야 한다

withCredentials:true

Credentials 이 있는 CORS 요청은 Client와 Server 둘다 Credentials를 사용하겠다는 속성을 설정해줘야 통신이 가능하다.

즉 도메인이 다른 서버에서 클라이언트의 쿠키 값을 설정하기 위해서는 클라이언트의 withCredentials가 true로 되어 있어야 한다.

withCredentials는 header의 속성이 아니므로 axios를 사용할 때 header과 별개의 객체로 보내야 한다

async & await

async 함수

async는 function 앞에 위치

async function f() {
  return 1;
}

function 앞에 async를 붙이면 해당 함수는 항상 프라미스를 반환합니다. 프라미스가 아닌 값을 반환하더라도 이행 상태의 프라미스(resolved promise)로 값을 감싸 이행된 프라미스가 반환되도록 합니다.

아래 예시의 함수를 호출하면 result가 1인 이행 프라미스가 반환됩니다. 직접 확인해 봅시다.

async function f() {
return 1;
}

f().then(alert); // 1
명시적으로 프라미스를 반환하는 것도 가능한데, 결과는 동일합니다.

async function f() {
return Promise.resolve(1);
}

f().then(alert); // 1
async가 붙은 함수는 반드시 프라미스를 반환하고, 프라미스가 아닌 것은 프라미스로 감싸 반환합니다. 굉장히 간단하죠? 그런데 async가 제공하는 기능은 이뿐만이 아닙니다. 또 다른 키워드 await는 async 함수 안에서만 동작합니다. await는 아주 멋진 녀석이죠.

await
await 문법은 다음과 같습니다.

// await는 async 함수 안에서만 동작합니다.
let value = await promise;
자바스크립트는 await 키워드를 만나면 프라미스가 처리(settled)될 때까지 기다립니다. 결과는 그 이후 반환됩니다.

1초 후 이행되는 프라미스를 예시로 사용하여 await가 어떻게 동작하는지 살펴봅시다.

async function f() {

let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("완료!"), 1000)
});

let result = await promise; // 프라미스가 이행될 때까지 기다림 (*)

alert(result); // "완료!"
}

f();
함수를 호출하고, 함수 본문이 실행되는 도중에 (*)로 표시한 줄에서 실행이 잠시 '중단’되었다가 프라미스가 처리되면 실행이 재개됩니다. 이때 프라미스 객체의 result 값이 변수 result에 할당됩니다. 따라서 위 예시를 실행하면 1초 뒤에 '완료!'가 출력됩니다.

await('기다리다’라는 뜻을 가진 영단어 – 옮긴이)는 말 그대로 프라미스가 처리될 때까지 함수 실행을 기다리게 만듭니다. 프라미스가 처리되면 그 결과와 함께 실행이 재개되죠. 프라미스가 처리되길 기다리는 동안엔 엔진이 다른 일(다른 스크립트를 실행, 이벤트 처리 등)을 할 수 있기 때문에, CPU 리소스가 낭비되지 않습니다.

await는 promise.then보다 좀 더 세련되게 프라미스의 result 값을 얻을 수 있도록 해주는 문법입니다. promise.then보다 가독성 좋고 쓰기도 쉽습니다.

일반 함수엔 await을 사용할 수 없습니다.
async 함수가 아닌데 await을 사용하면 문법 에러가 발생합니다.

function f() {
let promise = Promise.resolve(1);
let result = await promise; // Syntax error
}
function 앞에 async를 붙이지 않으면 이런 에러가 발생할 수 있습니다. 앞서 설명해 드린 바와 같이 await는 async 함수 안에서만 동작합니다.

프라미스 체이닝 챕터의 showAvatar() 예시를 async/await를 사용해 다시 작성해봅시다.

먼저 .then 호출을 await로 바꿔야 합니다.
function 앞에 async를 붙여 await를 사용할 수 있도록 해야 합니다.
async function showAvatar() {

// JSON 읽기
let response = await fetch('/article/promise-chaining/user.json');
let user = await response.json();

// github 사용자 정보 읽기
let githubResponse = await fetch(https://api.github.com/users/${user.name});
let githubUser = await githubResponse.json();

// 아바타 보여주기
let img = document.createElement('img');
img.src = githubUser.avatar_url;
img.className = "promise-avatar-example";
document.body.append(img);

// 3초 대기
await new Promise((resolve, reject) => setTimeout(resolve, 3000));

img.remove();

return githubUser;
}

showAvatar();
코드가 깔끔해지고 읽기도 쉬워졌습니다. 프라미스를 사용한 것보다 훨씬 낫네요.

await는 최상위 레벨 코드에서 작동하지 않습니다.
await을 이제 막 사용하기 시작한 분들은 최상위 레벨 코드(top-level code)에 await을 사용할 수 없다는 사실을 잊곤 합니다. 아래와 같은 코드는 동작하지 않습니다.

// 최상위 레벨 코드에선 문법 에러가 발생함
let response = await fetch('/article/promise-chaining/user.json');
let user = await response.json();
하지만 익명 async 함수로 코드를 감싸면 최상위 레벨 코드에도 await를 사용할 수 있습니다.

(async () => {
let response = await fetch('/article/promise-chaining/user.json');
let user = await response.json();
...
})();
await는 ‘thenable’ 객체를 받습니다.
promise.then처럼 await에도 thenable 객체(then 메서드가 있는 호출 가능한 객체)를 사용할 수 있습니다. thenable 객체는 서드파티 객체가 프라미스가 아니지만 프라미스와 호환 가능한 객체를 제공할 수 있다는 점에서 생긴 기능입니다. 서드파티에서 받은 객체가 .then을 지원하면 이 객체를 await와 함께 사용할 수 있습니다.

await는 데모용 클래스 Thenable의 인스턴스를 받을 수 있습니다.

class Thenable {
constructor(num) {
this.num = num;
}
then(resolve, reject) {
alert(resolve);
// 1000밀리초 후에 이행됨(result는 this.num2)
setTimeout(() => resolve(this.num
2), 1000); // (*)
}
};

async function f() {
// 1초 후, 변수 result는 2가 됨
let result = await new Thenable(1);
alert(result);
}

f();
await는 .then이 구현되어있으면서 프라미스가 아닌 객체를 받으면, 내장 함수 resolve와 reject를 인수로 제공하는 메서드인 .then을 호출합니다(일반 Promise executor가 하는 일과 동일합니다). 그리고 나서 await는 resolve와 reject 중 하나가 호출되길 기다렸다가((*)로 표시한 줄) 호출 결과를 가지고 다음 일을 진행합니다.

async 클래스 메서드
메서드 이름 앞에 async를 추가하면 async 클래스 메서드를 선언할 수 있습니다.

class Waiter {
async wait() {
return await Promise.resolve(1);
}
}

new Waiter()
.wait()
.then(alert); // 1
async 메서드와 async 함수는 프라미스를 반환하고 await를 사용할 수 있다는 점에서 동일합니다.

에러 핸들링
프라미스가 정상적으로 이행되면 await promise는 프라미스 객체의 result에 저장된 값을 반환합니다. 반면 프라미스가 거부되면 마치 throw문을 작성한 것처럼 에러가 던져집니다.

예시:

async function f() {
await Promise.reject(new Error("에러 발생!"));
}
위 코드는 아래 코드와 동일합니다.

async function f() {
throw new Error("에러 발생!");
}
실제 상황에선 프라미스가 거부 되기 전에 약간의 시간이 지체되는 경우가 있습니다. 이런 경우엔 await가 에러를 던지기 전에 지연이 발생합니다.

await가 던진 에러는 throw가 던진 에러를 잡을 때처럼 try..catch를 사용해 잡을 수 있습니다.

async function f() {

try {
let response = await fetch('http://유효하지-않은-주소');
} catch(err) {
alert(err); // TypeError: failed to fetch
}
}

f();
에러가 발생하면 제어 흐름이 catch 블록으로 넘어갑니다. 여러 줄의 코드를 try로 감싸는 것도 가능합니다.

async function f() {

try {
let response = await fetch('http://유효하지-않은-url');
let user = await response.json();
} catch(err) {
// fetch와 response.json에서 발행한 에러 모두를 여기서 잡습니다.
alert(err);
}
}

f();
try..catch가 없으면 아래 예시의 async 함수 f()를 호출해 만든 프라미스가 거부 상태가 됩니다. f()에 .catch를 추가하면 거부된 프라미스를 처리할 수 있습니다.

async function f() {
let response = await fetch('http://유효하지-않은-url');
}

// f()는 거부 상태의 프라미스가 됩니다.
f().catch(alert); // TypeError: failed to fetch // (*)
.catch를 추가하는 걸 잊으면, 처리되지 않은 프라미스 에러가 발생합니다(콘솔에서 직접 확인해 봅시다). 이런 에러는 프라미스와 에러 핸들링 챕터에서 설명한 전역 이벤트 핸들러 unhandledrejection을 사용해 잡을 수 있습니다.

async/await와 promise.then/catch
async/await을 사용하면 await가 대기를 처리해주기 때문에 .then이 거의 필요하지 않습니다. 여기에 더하여 .catch 대신 일반 try..catch를 사용할 수 있다는 장점도 생깁니다. 항상 그러한 것은 아니지만, promise.then을 사용하는 것보다 async/await를 사용하는 것이 대개는 더 편리합니다.

그런데 문법 제약 때문에 async함수 바깥의 최상위 레벨 코드에선 await를 사용할 수 없습니다. 그렇기 때문에 관행처럼 .then/catch를 추가해 최종 결과나 처리되지 못한 에러를 다룹니다. 위 예시의 (*)로 표시한 줄처럼 말이죠.

async/await works well with Promise.all
여러 개의 프라미스가 모두 처리되길 기다려야 하는 상황이라면 이 프라미스들을 Promise.all로 감싸고 여기에 await를 붙여 사용할 수 있습니다.

// 프라미스 처리 결과가 담긴 배열을 기다립니다.
let results = await Promise.all([
fetch(url1),
fetch(url2),
...
]);
실패한 프라미스에서 발생한 에러는 보통 에러와 마찬가지로 Promise.all로 전파됩니다. 에러 때문에 생긴 예외는 try..catch로 감싸 잡을 수 있습니다.

요약
function 앞에 async 키워드를 추가하면 두 가지 효과가 있습니다.

함수는 언제나 프라미스를 반환합니다.
함수 안에서 await를 사용할 수 있습니다.
프라미스 앞에 await 키워드를 붙이면 자바스크립트는 프라미스가 처리될 때까지 대기합니다. 처리가 완료되면 조건에 따라 아래와 같은 동작이 이어집니다.

에러 발생 – 예외가 생성됨(에러가 발생한 장소에서 throw error를 호출한 것과 동일함)
에러 미발생 – 프라미스 객체의 result 값을 반환
async/await를 함께 사용하면 읽고, 쓰기 쉬운 비동기 코드를 작성할 수 있습니다.

async/await를 사용하면 promise.then/catch가 거의 필요 없습니다. 하지만 가끔 가장 바깥 스코프에서 비동기 처리가 필요할 때같이 promise.then/catch를 써야만 하는 경우가 생기기 때문에 async/await가 프라미스를 기반으로 한다는 사실을 알고 계셔야 합니다. 여러 작업이 있고, 이 작업들이 모두 완료될 때까지 기다리려면 Promise.all을 활용할 수 있다는 점도 알고 계시기 바랍니다.

과제
async와 await를 사용하여 코드 변경하기
프라미스 체이닝 챕터의 예시 중 하나를 .then/catch 대신 async/await를 사용해 다시 작성해봅시다.

function loadJson(url) {
return fetch(url)
.then(response => {
if (response.status == 200) {
return response.json();
} else {
throw new Error(response.status);
}
})
}

loadJson('no-such-user.json')
.catch(alert); // Error: 404
해답
async와 await를 사용해서 '다시 던지기' 예시 재작성하기
프라미스 체이닝 챕터에서 다뤘던 ‘다시 던지기(rethrow)’ 관련 예시를 기억하실 겁니다. 이 예시를 .then/catch 대신 async/await를 사용해 다시 작성해 봅시다.

그리고 demoGithubUser 안의 반복(recursion)은 반복문(loop)을 사용해 작성하도록 합시다. async/await를 사용하면 쉽게 작성할 수 있습니다.

class HttpError extends Error {
constructor(response) {
super(${response.status} for ${response.url});
this.name = 'HttpError';
this.response = response;
}
}

function loadJson(url) {
return fetch(url)
.then(response => {
if (response.status == 200) {
return response.json();
} else {
throw new HttpError(response);
}
})
}

// 유효한 사용자를 찾을 때까지 반복해서 username을 물어봄
function demoGithubUser() {
let name = prompt("GitHub username을 입력하세요.", "iliakan");

return loadJson(https://api.github.com/users/${name})
.then(user => {
alert(이름: ${user.name}.);
return user;
})
.catch(err => {
if (err instanceof HttpError && err.response.status == 404) {
alert("일치하는 사용자가 없습니다. 다시 입력해 주세요.");
return demoGithubUser();
} else {
throw err;
}
});
}

demoGithubUser();
해답
async가 아닌 함수에서 async 함수 호출하기
‘일반’ 함수가 하나 있는데, 여기서 async 함수를 어떻게 하면 호출하고, 그 결과를 사용할 수 있을까요?

async function wait() {
await new Promise(resolve => setTimeout(resolve, 1000));

return 10;
}

function f() {
// ...코드...
// async wait()를 호출하고 그 결과인 10을 얻을 때까지 기다리려면 어떻게 해야 할까요?
// f는 일반 함수이기 때문에 여기선 'await'를 사용할 수 없다는 점에 주의하세요!
}
참고: 문제 자체는 아주 간단하지만, async와 await를 학습한 지 얼마 안 된 개발자들이 쉽게 접하는 상황입니다.

해답
댓글댓글을 달기 전에 마우스를 올렸을 때 나타나는 글을 먼저 읽어주세요.
추가 코멘트, 질문 및 답변을 자유롭게 남겨주세요. 개선해야 할 것이 있다면 댓글 대신 이슈를 만들어주세요.
잘 이해되지 않는 부분은 구체적으로 언급해주세요.
댓글에 한 줄짜리 코드를 삽입하고 싶다면 태그를, 여러 줄로 구성된 코드를 삽입하고 싶다면

 태그를 이용하세요. 10줄 이상의 코드는 plnkr, JSBin, codepen 등의 샌드박스를 사용하세요.

findOrCreate

create an entry in the table
unless it can find one fulfilling the query options.

In both cases, it will return an instance (either the found instance or the created instance) and a boolean indicating whether that instance was created or already existed.

The where option
: considered for finding the entry

the defaults option
: used to define what must be created in case nothing was found. If the defaults do not contain values for every column, Sequelize will take the values given to where (if present).

Let's assume we have an empty database with a User model which has a username and a job.

const [user, created] = await User.findOrCreate({
  where: { username: 'sdepold' },
  defaults: {
    job: 'Technical Lead JavaScript'
  }
});
console.log(user.username); // 'sdepold'
console.log(user.job); // This may or may not be 'Technical Lead JavaScript'
console.log(created); // The boolean indicating whether this instance was just created
if (created) {
  console.log(user.job); // This will certainly be 'Technical Lead JavaScript'
}

FindOne

The findOne method obtains the first entry it finds (that fulfills the optional query options, if provided).

const project = await Project.findOne({ where: { title: 'My Title' } });
if (project === null) {
console.log('Not found!');
} else {
console.log(project instanceof Project); // true
console.log(project.title); // 'My Title'
}

profile
노션 : https://garrulous-gander-3f2.notion.site/c488d337791c4c4cb6d93cb9fcc26f17

0개의 댓글