초보 개발자

promise 도대체 이게 뭐냐고..ㅜㅜㅜㅜ 본문

AI 웹개발 트랙 - 내배캠/9주차 ~

promise 도대체 이게 뭐냐고..ㅜㅜㅜㅜ

taehyeki 2022. 2. 28. 19:50

동기, 비동기는 이름부터 마음에 안든다. 

동기는 위에서부터 차례대로 코드하나가 실행이 끝나면 그제서야 다음코드를 실행하고 또 끝나면 다음코드를 실행하고,

다음코드로 가기위한 전제조건이 앞의 코드가 실행이 완료되면 간다.

 

비동기는 앞의코드가 끝나지 않아도 바로 뒤의 코드로 넘어간다. 앞의 코드의 연산이 끝날 때 까지 코드 실행을 멈추지 않고 다음 코르를 먼저 실행시키는 것이다.

 

가장 대표적인 사례가 ajax라고한다. 아래와 같은 코드가 있다고 할 때 언디파인드가 리턴이 되는데 그 이유는 ajax를 통해서 값을 받아올 때 까지 기다려주지 않아서 기존에 있던 undefinded 그대로 출력이 된 것이다.

function getData() {
	var tableData;
	$.get('https://domain.com/products/1', function(response) {
		tableData = response;
	});
	return tableData;
}

console.log(getData()); // undefined

 

두번째는 settimeout이다. 

// #1
console.log('Hello');
// #2
setTimeout(function() {
	console.log('Bye');
}, 3000);
// #3
console.log('Hello Again');

hello

bye

hello again 순으로 출력이 될 것 같지만

hello

hello again

bye 이 순서로 출력이 된다. 이것도 비동기 방식으로 실행이 되기 때문이다.

 

따라서 앞서 야기된 문제를 해결하기 위해선 콜백함수를 사용하면 된다.

function getData(callbackFunc) {
	$.get('https://domain.com/products/1', function(response) {
		callbackFunc(response); // 서버에서 받은 데이터 response를 callbackFunc() 함수에 넘겨줌
	});
}

getData(function(tableData) {
	console.log(tableData); // $.get()의 response 값이 tableData에 전달됨
});

이렇게 하면 특정 로직이 끝났을 때 원하는 동작을 실행시킬 수 있다.

 

콜백함수의 동작방식은 맛집의 예약 방법과 비슷하다고 한다. 먼저 줄을 서있기 때문에 대기자 명단이 이름을 적고

주변 구경하다가 카톡으로 슬슬 오라고 문자가 올 것이다. 그 카톡이 오는 시점이 콜백함수가 호출되는 시점이라고 한다. 자리가 났을 때만 연락이 오기에 미리가서 기다릴 필요도, 자리가 났는지 확인할 필요도 없다. 자리가 준비된 시점, 즉 데이터가 준비된 시점에서 우리가 원하는 동작등을 수행할 수 있다.

 

여기서 문제가 발생한다.

비동기 처리 로직을 위해서 콜백함수에 함수를 연달아 이용하다보면 가독성도 떨어지도 로직변경도 어렵다 이걸 콜백 지옥이라고 한다.

$.get('url', function(response) {
	parseValue(response, function(id) {
		auth(id, function(result) {
			display(result, function(text) {
				console.log(text);
			});
		});
	});
});

 

이 콜백 지옥을 해결하기 위해서 바로 promise와 async를 사용한다고한다.

이 단어들이 나오기까지 정말 한참이 걸렸다.

 

 

프로미스는 자바스크립트 비동기 처리에 사용되는 객체이다. 이게 왜필요하냐면 주로 서버에서 받아온 데이터를 표시할 때 주로 프로미스를 사용한다. api가 실행이 되면 바로 보내지는게 아니라 어느정도 시간이 소요되는데 비동기로 처리해버리기 때문에 값이 들어오기도 전에 그 다음 코드로 넘어가기에 받아온 값을 사용할 수가 없다. 따라서 이걸 해결하기 위한 방법이 프로미스이다.

 

앞서 아래와 같은 코드를 콜백함수를 사용하여 해결하였는데 이게 길어지면 길어질수록 문제가 된다.

function getData(callbackFunc) {
  $.get('url 주소/products/1', function(response) {
    callbackFunc(response); // 서버에서 받은 데이터 response를 callbackFunc() 함수에 넘겨줌
  });
}

getData(function(tableData) {
  console.log(tableData); // $.get()의 response 값이 tableData에 전달됨
});

아래는 프로미스를 적용한 것이다.

function getData(callback) {
  // new Promise() 추가
  return new Promise(function(resolve, reject) {
    $.get('url 주소/products/1', function(response) {
      // 데이터를 받으면 resolve() 호출
      resolve(response);
    });
  });
}

// getData()의 실행이 끝나면 호출되는 then()
getData().then(function(tableData) {
  // resolve()의 결과 값이 여기로 전달됨
  console.log(tableData); // $.get()의 reponse 값이 tableData에 전달됨
});

new Promise() resolve() then()과 같이 갑자기 어려워보이는 것이 나타났다. 찬찬히 살펴보자

 

new Promise()로 프로미스를 생성하고 종료될 때 까지 3가지 상태를 갖는다. 

Pending 비동기 처리 로직이 아직 완료되지 않은 상태

Fulfilled 비동기 처리가 완료되어 프로미스가 결과 값을 반환해준 상태

Rejected 비동기 처리가 실패하거나 오류가 발생한 상태

 

아래와 같이 호출하면 Pending상태이다.

new Promise();

nw Promise()메서드를 호출할 때 콜백 함수를 선언할 수 있고, 콜백 함수의 인자는 resolve와 reject이다.

new Promise(function(resolve, reject){

}

 

resolve를 아래와 같이 실행하면 Fulfilled상태가 된다.

new Promise(function(resolve, reject) {
  resolve();
});

그리고 이행상태가 되면 아래와 같이 then을 이용하여 처리결과 값을 받을 수 있다.

function getData() {
  return new Promise(function(resolve, reject) {
    var data = 100;
    resolve(data);
  });
}

// resolve()의 결과 값 data를 resolvedData로 받음
getData().then(function(resolvedData) {
  console.log(resolvedData); // 100
});

 

똑같이 아래와같이 reject를 호출하면 실패상태가 된다.

new Promise(function(resolve, reject) {
  reject();
});

그리고 실패상태가 되면 실패한 이유를 catch()로 받을 수 있다,

function getData() {
  return new Promise(function(resolve, reject) {
    reject(new Error("Request is failed"));
  });
}

// reject()의 결과 값 Error를 err에 받음
getData().then().catch(function(err) {
  console.log(err); // Error: Request is failed
});

 

쉬운 예제

function getData() {
  return new Promise(function(resolve, reject) {
    $.get('url 주소/products/1', function(response) {
      if (response) {
        resolve(response);
      }
      reject(new Error("Request is failed"));
    });
  });
}

// 위 $.get() 호출 결과에 따라 'response' 또는 'Error' 출력
getData().then(function(data) {
  console.log(data); // response 값 출력
}).catch(function(err) {
  console.error(err); // Error 출력
});

위 코드는 서버에서 응답을 받아오면 resolve()메서드를 호출하고, 응답이 없으면 reject()메서드를 호출하는 예제이다. 호출된 메서드에 따라서 then이나 catch로 분기하여 응답 혹은 오류를 출력한다.

 

또 여러번 이어서 나타낼 수 있다.

new Promise(function(resolve, reject){
  setTimeout(function() {
    resolve(1);
  }, 2000);
})
.then(function(result) {
  console.log(result); // 1
  return result + 10;
})
.then(function(result) {
  console.log(result); // 11
  return result + 20;
})
.then(function(result) {
  console.log(result); // 31
});

resolve()가 호출되면 프로미스가 대기상태에서 이행상태로 넘거가기 때문에 첫 번째 then으로 넘어가고 거기서 이행된 결과값 1을 받아 10을 더한 다음 다시 then으로 넘겨준다. 또 두번째 .then()에서도 마찬가지로 넘겨주는 식으로 계속 값을 뒤로 전달할 수 있다.

 

프로미스가 항상 정상작동하지 않는다. 상대 서버가 터지거나 하면 잘 동작하던 것들도 갑자기 안되기 때문이다. 따라서 안될 때의 상황도 고려를 해주어야 하는데. 2가지 방식이 있다.

 

첫번째방식은 then의 첫번째인자에 성공했을 때 함수 두번째 인자에 실패했을 때 함수를 각각 넣어준다.

두번째 방법은 .catch를 사용하는 방법이다. then.().catch()

function getData() {
  return new Promise(function(resolve, reject) {
    reject('failed');
  });
}

// 1. then()의 두 번째 인자로 에러를 처리하는 코드
getData().then(function() {
  // ...
}, function(err) {
  console.log(err);
});

// 2. catch()로 에러를 처리하는 코드
getData().then().catch(function(err) {
  console.log(err);
});

두 방법중에서 catch를 활용하는 방법을 쓰는 것이 더 좋다고 한다 그 이유는 첫번 째 인자가 실행이 되었을 때( 즉 이행이 되었을 때 ) 이 때에도 오류가 발생할 수 있다. catch를 사용하면 이 오류도 잡을 수 있지만 첫번째 방법을 사용하면 이 오류는 잡아내지 못한다.

 

자바스크립트 Promise 쉽게 이해하기 • 캡틴판교 (joshua1988.github.io)

 

자바스크립트 Promise 쉽게 이해하기

(중급) 자바스크립트 입문자를 위한 Promise 설명. 쉽게 알아보는 자바스크립트 Promise 개념, 사용법, 예제 코드. 예제로 알아보는 then(), catch() 활용법

joshua1988.github.io

위 블로그를 보고 프로미스에 대해서 공부해보았다. 뭔가 아직은 감이 잘 안온다.

하지만 비동기로직을 처리하기 위해선 콜백함수가 필요한데, 이 콜백함수를 많이 사용하다보면 복잡해지니까,

이걸 해결하기 위해서 Promise를 사용한다라는 것 까지만 일단 확실히 이해를 하고 넘어가야겠다.

 

다음은 async await에 대해서 살펴보자.