JavaScript/DreamCoding

프로미스 활용하기(JavaScript Promise)

느리지만 꾸준하게 2021. 8. 3. 18:09

프로미스(Promise)는 약속이라는 뜻으로 JS에서 비동기를 간편하게 처리해주는 오브젝트이다.

프로미스 정해진 장시간의 기능을 수행하고 나서 정상적으로 기능이 수행되어졌으면 성공의 메세지와 함께 처리된 결과값을 전달해주고 기능을 수행하다가 예상치 못한 문제가 발생하면 에러를 전달해준다.

 

JS안에 내장되어져 있는 오브젝트로 비동기적인 것을 수행할 때 콜백함수 대신에 유용하게 쓸 수 있다.

프로미스는 state와(프로세스가 무거운 일을 수행하고 있는 중인지 아니면 기능 수행이 완료가 되어서 성공했는지 실패했는지 상태에 대해 이해하는 것이 중요하고) producer / consumer(원하는 데이터를 제공하는 사람과 데이터를 필요로하는 사람 즉, 쓰는 사람의 차이점)의 차이점을 이해하는 것이 중요하다.

 

프로미스의 상태는 만들어져서 지정한 오퍼레이션이 수행중일 때는 pending상태가 되고 오퍼레이션을 성공적으로 다 끝내면 fufilled state가 된다.(완벽하게 끝낸 상태)

아니면 파일을 찾을 수 없거나 네트워크에 문제가 생기면 rejected state가 된다.

그리고 프로미스에는 원하는 기능을 수행해서 해당하는 데이터를 만들어내는 producer 오브젝트가 있고 원하는 데이터를 소비하는 consumer로 나눠진다.

첫번째 producer를 보면 원하는 기능을 비동기적으로 실행하는 프로미스를 만들어보자

프로미스는 클래스여서 new라는 키워드를 이용해 오브젝트를 생성할 수 있고 프로미스의 생성자를 보면 executor라는 콜백함수를 전달해 주어야 한다. 콜백함수에는 또 다른 두가지의 콜백함수를 받는다. 기능을 정상적으로 수행해서 마지막에 데이터를 전달하는 resolve와 기능수행하다 문제가 생기면 호출하는 reject 두가지로 나눠져 있다.

프로미스 안에서는 조금 무거운 일들을 하는데 네트워크에서 데이터를 받아오거나 파일에서 큰 데이터를 읽어오는 과정은 시간이 꽤 걸린다. 그런 것을 동기적으로 처리하게 되면 파일을 읽어오고 네트워크에서 데이터를 받아오는 동안 그 다음라인에 코드가 실행되지 않기 때문에 시간이 조금 걸리는 일들은 promise를 만들어서 비동기적으로 처리하는 것이 좋다. 그래서 네트워크 통신하던지 파일을 읽어서 온다던지 이런 것들은 비동기적으로 처리하는 것이 좋다.

 

콘솔을 찍어보면 바로 doing somethoing라는 것이 나오고 여기서 프로미스가 전달한 executor라는 콜백함수가 바로 실행이 되는 것을 확인할 수 있다. 프로미스 안에 네트워크 통신을 하는 코드를 작성하면 프로미스가 만들어지는 그 순간

네트워크 통신을 수행하게 된다. 

 

여기서 포인트는 네트워크 요청을 사용자가 요구했을 때만 하는 경우라면(버튼 눌렀을 때) 불필요한 통신이 일어나는 코드를 작성하면 안된다. 그래서 프로미스를 만드는 순간 그 안에 전달한 executor라는 콜백함수가 실행이 되기 때문에 이점을 유의하자.

 

새로운 프로미스가 만들어질 때는 전달한 executor라는 콜백함수가 자동적으로 바로 실행이 된다.

setTimeout(())을 이용해서 원하는 콜백함수를 2초정도 있다가 할건데 블록안에서는 기능을 잘 수행했다라고 한다면 resolve라는 콜백함수를 호출하고 데이터를 받아와서 사용자이름은 jay라고 성공적으로 네트워크에서 받아온 혹은 파일에서 읽어온(그것들을 가공한) 데이터를 resolve라는 콜백함수를 이용해서 전달하면 된다.

 

프로미스는 2초정도 무언가를 하다가 마지막에 resolve라는 콜백함수를 호출하면서 jay라는 값을 전달해주는 프로미스를 만들어보았다.

다음으로는 위에 걸 이용하는 consumers를 만들어보자.

consumers는 then이나 catch나 finally를 이용해서 값을 받아올 수 있다.

promise라는 변수를 만들었는데 값이 정상적으로 잘 수행이 된다면 그러면 값을 받아온다고 하자

원하는 기능을 수행하는 콜백함수를 전달해주면 된다.

여기서 value라는 파라미터는 프로미스가 정상적으로 잘 수행이 되어서 마지막으로 resolve 콜백함수에서 전달된

jay라는 값이 들어오게 된다.

그래서 value가 들어오면 콘솔로그에 출력하게 되면 2초뒤에 jay라고 나온다. 즉 then이라는 것은 프로미스가 정상적으로 잘 수행이 되어서 최종적으로 resolve라는 콜백함수를 통해서 전달한 값이 value의 파라미터로 전달되는 것이다.

then은 똑같은 프로미스를 리턴하기 때문에 리턴된 프로미스에 캐치를 다시 호출할 수 있다.(체이닝이라고 한다)

즉 프로미스에서도 then을 호출하게 되면 다시 프로미스가 리턴이 되고 리턴된 프로미스의 캐치를 등록하는 거다.

resolve 콜백일때
jay출력

resolve말고 reject를 쓰게되면 Error라는 오브젝트를 사용해서 에러가 발생한다.

프로미스에서 then이라는 API를 이용해서 성공적인 케이스를 잘 다뤘다면 catch라는 함수를 이용해서 error가 발생했을 때 어떻게 처리할 건지 콜백함수를 등록해주면 된다. 그러면 빨간 에러가 발생안하고 받아온 에러가 콘솔로그에 출력된다.

reject일 때
error발생(잡히지 않은 에러인데 then이라는 것을 이용해서 성공적인 case만 다뤄서 uncaught라고 나온다
error 문구 출력

결론적으로 프로미스 오브젝트를 만들 때 비동기적으로 수행하고 싶은 코드들을 작성하고 나서 성공적으로 했다면 resolve를 하고 실패했다면 reject를 써서 실패한거와 왜실패했는지 에러를 전달하게 된다.

 

프로미스를 이용해서 then과 catch를 이용해서 성공한 값 실패한 에러를 받아와서 원하는 방식으로 처리해주면 된다.

최근에 추가된 것이 있는데 finally라는 것이다. finally는 성공하든 실패하든 무조건 마지막에 호출되어지는 것이다.

그래서 아무런 인자를 받지않고 콘솔로그를 출력하면 실패했을 때도 에러콜백함수가 처리되고나서 finally가 호출된다.

위에서 reject 대신에 resolve를 쓰게되면 jay라는 성공적인 콜백함수가 수행되고 나서 다시 finally가 수행된다. 즉 성공하든 실패하든 상관없이 어떤 기능을 마지막으로 수행하고 싶을 때, 그 때 finally를 사용할 수 있다.

이제 Promise chaining에 대해서 알아보면

서버에서 숫자를 받아오는 새로운 promise를 만들거다.

앞에서 프로미스는 resolve와 reject라는 콜백함수를 받는 executor라는 콜백함수를 전달해줘야 한다.

서버통신을 해야하니까 1초정도 있다가 숫자를 전달하는 promise를 만든다.

fetchNumber라는 프로미스를 .then()을 이용해 fetchNumer가 성공적으로 잘되면 숫자를 2배로 곱하고 다시 그 숫자를 3배로 곱하고 또 다른서버에 보내서 다른 수로 변환된 값을 받아올 것이다. 새로운 프로미스를 리턴할건데 이것은 다른 서버랑 통신할 것이다.

그래서 리졸브와 리젝트를 받아서 setTimeout을 이용해서 숫자에 1을 뺀 값을 다시 resolve를 통해서 전달해본다.

 

순서는 1이 전달되면  2  6  5순서로 되서 5가 출력이 된다. 여기서 then은 값을 바로 전달할 수도 있고 아니면 promise를 전달해도 된다. then에서는 값을 바로 전달해도 되고 또 다른 비동기인 프로미스를 전달해도 되는 것이다. 최종적으로는 1초 + 1초 = 2초가 걸린다.

 

이렇게 then then then 여러가지를 동시에 묶어서 다른 비동기적인 것들을 묶어서 처리할 수 있다.

 

 

다음으로는 프로미스를 chaining했을 때 어떻게 에러를 핸들링 할 수 있는지 알아보자.

총 3가지의 프로미스 함수를 리턴하는 것이 있다.

 

여기서 달걀을 받아오는 부분에 문제가 생겨서 네트워크에서 받아오는데 실패하게 되면 즉 달걀 받아오다가 에러가 발생하고 제일 마지막에 catch를 작성하게 되면 아래 콘솔창과 같이 나온다.

catch 통해서 egg받아오는 부분에서 에러발생했지만 에러가 제일 밑으로 전달되면서 catch가 잡혀지는 것을 볼 수 있다.

여기서 달걀 받아올 때 문제가 생기면 준비한 다른 재료로 대체를 하고싶은데 그렇게 하려면 다른것을 전달해준다.

달걀대신에 우는 이모티콘을 넣어주면 전체적인 프로미스 체인에 구멍처리를 해주는 것이다. 이렇게 하면 프로미스 체인이 실패하지 않고 계란프라이 그림까지 나오게 된다.

다섯번쨰로는 저번에 만들었던 콜백을 프로미스로 바꾸는 작업을 해보면

프로미스를 리턴할 건데 프로미스 안에서 2초있다가 기능을 수행하게 만들어 본다. 로그인이 잘 되면 success대신에 resolve를 써서 id를 전달해주고 만약 실패한다면 reject를 이용해서 에러를 전달한다.

getRoles도 똑같이setTimeout을 promise안에다가 넣고 success대신에 resolve와 reject를 이용하면 된다.

콜백을 전달 받지 않아도 되어서 코드가 한층 더 깔끔해졌다. 

userStorage에 loginUser를 호출해서 id와 password를 전달한 다음에 이것이 잘되면은 .then을 이용해서 user를 받아와서 userStorage안에 있는 getRoles를 호출하는데(user =>userStorage.getRoles) 인자가 같으니까 user를 생략

사용자의 역할을 잘받으면 alert를 이용해서 사용자를 출력한다.

 

순서대로 userStorage에서 로그인을 한 다음에 로그인이 성공하면 유저가 전달되어서 그 유저를 이용해서 userStorage.getRoles를 호출하게 되고 다 성공적으로 되면 최종적으로 받아오는 유저 이용해서 alert창을 나타낸다.

수행하다가 문제가 생기면 catch를 이용해 콘솔창에 나타낸다.

id가 password가 올바를때
올바르지 않을 때

 

 

참고: https://www.youtube.com/watch?v=JB_yU6Oe2eE&list=PLv2d7VI9OotTVOL4QmPfvJWPJvkmv6h-2&index=12