콜백의 한계점이라는 직전 포스팅에서 사용했던 코드는 

// 직원 데이터를 가져온 후 리스폰스를 파싱하고 데이터를 프로세싱 하는 예시
getEmployees((response) => {
  json(response, (data) => {
    groupEmployees(data, (result) => {
      console.log(result);
    });
  });
});

위 코드였다. getEmployees라는 함수를 이용해 웹 리퀘스트를 보내는 것을 흉내 냈었는데, 실제로 웹 리퀘스트를 보낼 때는 fetch() 라는 함수를 많이 사용한다. (fetch 함수는 promise 객체를 리턴)

const response = fetch('https://learn.codeit.kr/api/employees');

console.log(response);

위 URL은 직원 10명의 데이터가 있는 창이다. fetch는 코드로 리퀘스트(웹 브라우저가 서버에 요청)를 보내서 이 데이터를 받아오는 것이다.

URL로 접속해보면 나오는 화면

실행해보면 이렇게 나오는데

드래그 되어 있는 부분은 주의 메세지여서 무시해도 된다.

출력결과는 Promise { <pending> } 이 부분이다. fetch 함수가 Promise 라는걸 리턴해서 그 값이 리스폰스(서버가 보내주는 응답)에 저장된 것이다. 그렇다면 Promise는 무엇일까?

Promise는 비동기 작업이 완료되면 값을 알려 주는 객체로 작업이 완료되면 값을 알려줄 것을 "약속"하기 때문에 promise이다.

fetch는 오래 걸리는 작업이기 때문에 결과 값을 바로 돌려줄 수 없으니까 일단 promise를 돌려주고 나중에 작업이 완료되면 결과 값을 promise에 채워 넣어 주겠다는 개념이다. 이렇게 promise를 바로 리턴하면 좀 더 직관적인 코드를 작성할 수 있다.

리퀘스트(웹 브라우저가 서버에 요청)를 보내고 리스폰스(서버가 보내주는 응답)를 파싱(어떤 큰 자료에서 원하는 정보만 가공하고 뽑아서 원할 때 불러올 수 있게 하는것)해서 결과를 출력하는 코드를 전에는 왼쪽(콜백기반) 처럼 했지만 promise 기반 fetch 코드를 완성하면 오른쪽의 모습이다.

promise 기반 코드는 평소에 쓰는 코드와 비슷한 모습인데, 코드가 한줄씩 있고 이전 줄에서 선언한 변수를 다음줄에서 사용하고 있다. 비동기 작업을 처리할 때도 익숙한 형태로 코드를 작성할 수 있다는게 promise 문법의 큰 장점이다. (자바스크립트에서 많은 비동기 코드가 prmise를 활용하는 이유이기도 함)

Promise는 세 가지 중 하나의 상태를 가질 수 있는데

  • Pending: 비동기 작업이 끝나기를 기다릴 때
  • Fulfilled: 비동기 작업이 성공적으로 끝났을 때. 비동기 작업의 성공 결과를 결괏값으로 갖게 됨.
  • Rejected: 비동기 작업이 실패했을 때. 비동기 작업에서 발생한 오류를 결괏값으로 갖게 됨.

그리고 Promise 객체를 통해 비동기 작업의 결과를 가져오거나 오류를 적절히 처리할 수 있다. Promise 객체를 다루는 방법은 두 가지가 있는데

1. .then() 메소드 + 콜백

2. async 와 await 문법

이다. 처음 배울 때는 async 와 await이 조금 더 직관적이고 이해하기 쉽다. 

'코린이 개념잡기 > 비동기 자바스크립트' 카테고리의 다른 글

async 함수  (0) 2024.12.23
await 문법  (0) 2024.12.22
콜백의 한계점(Callback Hell)  (0) 2024.12.22
비동기 함수  (2) 2024.12.22
비동기 실행의 특징  (0) 2024.12.22

콜백을 이용해서 비동기 프로그램을 짤 수 있다는 것은 좋지만 한계점이 있다.

여러 비동기 작업을 연속적으로 처리하는 것인데 예를 들어 아래와 같은 상황이다.

// 직원 데이터를 가져온 후 리스폰스를 파싱하고 데이터를 프로세싱 하는 예시
getEmployees((response) => {
  json(response, (data) => {
    groupEmployees(data, (result) => {
      console.log(result);
    });
  });
});

코드가 조금 헷갈리는데 콜백안에 콜백이 들어가서 그렇다. 코드를 해석해보자면 

1. getEmployees 호출:

  • 이 함수는 직원 데이터를 가져오는 비동기 함수이다.
  • 이 함수는 리스폰스를 받기 위해 콜백 함수를 인자로 받는다. 이 콜백 함수는 서버로부터 응답을 받은 후 실행된다.
  • 응답이 성공적으로 오면, 해당 응답을 response로 전달받는다.

2. json(response) 호출:

  • json 함수는 response를 JSON 형식으로 파싱하는 함수이다.
  • 이 함수도 콜백을 받으며, 파싱된 데이터를 data로 전달한다.
  • 즉, response가 JSON으로 변환되어 data에 저장된다.

3. groupEmployees(data) 호출:

  • data는 이제 직원 데이터의 배열이다.
  • groupEmployees 함수는 이 배열을 받아서 그룹화하는 기능을 수행한다. 이 함수도 콜백을 받아 결과를 result로 전달한다.

4. console.log(result):

  • 최종적으로 그룹화된 결과가 result에 저장되고, 이를 콘솔에 출력된다.
더보기
  • request : 웹브라우저가 서버에 요청
  • response : 서버가 보내주는 응답
  • parshing : 어떤 큰 자료에서 원하는 정보만 가공하고 뽑아서 원할 때 불러올 수 있게 하는것
  • parser : 파싱을 수행하는 프로그램
  • json(JavaScript Object Notation) : Javascript 객체 문법으로 데이터를 쉽게 교환 하고 저장하기 위한 텍스트 기반의 데이터 교환 표준, 쉽게 말해 일반적으로 서버에서 클라이언트로 데이터를 보낼 때 사용하는 양식이다.
  • 디버깅(Debugging) : 프로그래밍 과정에서 발생하는 오류나 비정상적인 연산, 즉 버그를 찾아 수정하는 프로세스
  • 테스팅 : 소프트웨어의 품질을 평가하고, 개발 과정에서 발생할 수 있는 오류를 사전에 발견하고 수정하는 과정

콜백 기반 코드는 모든 작업을 함수 안에서 처리하기 때문에 작업을 이어서 하려면 콜백 안에 콜백을 등록해야 한다. 그렇기 때문에 콜백을 중첩해서 사용해야 하고, 이런 현상을 콜백 헬(Callback Hell, 콜백 지옥)이라고 부른다. 코드를 이해하기도 힘들고, 디버깅이나 테스팅 같은 걸 하기도 어렵기 때문이다. (하지만 콜백을 이용해서 비동기 프로그램을 구현하다보면 어쩔 수 없이 발생하는 문제였다.) 이 불편함을 해소하기 위해 Promise라는 것이 2015년에 등장했다.

참고로 모든 상황에 Promise 기반 코드가 더 좋고, 모든 비동기 작업은 Promise로 처리하는 것은 아니다. Promise는 비동기 코드의 결괏값을 활용해야 할 때 특히 유용하고 이런 경우가 아니라면 여전히 콜백을 사용한다.

'코린이 개념잡기 > 비동기 자바스크립트' 카테고리의 다른 글

await 문법  (0) 2024.12.22
Promise  (0) 2024.12.22
비동기 함수  (2) 2024.12.22
비동기 실행의 특징  (0) 2024.12.22
콜백과 비동기 함수  (0) 2024.12.22

비동기 함수 복습

setTimeout() 함수

setTimeout(callback, delay);
  1. setTimeout() 이전에 있는 코드 실행
  2. setTimeout() 함수 실행 : delay 만큼 기다리는 타이머를 시작
  3. setTimeout() 이후에 있는 코드 실행
  4. delay가 지나면 callback 실행

asyncFunction() 함수 (사실 함수는 아니고 비동기 함수(async function)를 생성하는 객체이다.)

async function 선언은 AsyncFunction 객체를 반환하는 하나의 비동기 함수를 정의한다. 비동기 함수는 이벤트 루프를 통해 비동기적으로 작동하는 함수로, 암시적으로 Promise 를 사용하여 결과를 반환한다. 그니까 그냥 비동기 함수라고 쳐본다면,

asyncFunction(callback, ...);

asyncFunction()을 호출하고 callback을 전달하는 형태로, 동작은 setTimeout과 비슷하다.

  1. asyncFunction() 이전에 있는 코드 실행
  2. asyncFunction() 함수 실행 : 특정 조건이 만족되면(ex. 3초가 지나 어떤 '이벤트'가 발생했다.) callback이 실행되도록 한다.
  3. asyncFunction() 이후에 있는 코드 실행
  4. 특정 조건이 만족되는 시점에 callback 실행

이 흐름만 이해하고 있다면 어떤 비동기 함수든 쉽게 사용할 수 있다. 


자주 사용하는 비동기 함수 TOP 4

  1. setInterval() 함수
  2. DOM의 addEventListener() 함수
  3. React의 useEffect() 함수
  4. Express의 get() 함수

setInterval() 함수

setTimeout()과 비슷한 setInterval() 함수로, setInterval() 함수는 시간 간격을 두고 콜백을 반복적으로 실행한다.

console.log('Start');

// setInterval(callback, interval): interval 단위는 밀리초.
setInterval(() => console.log('2초가 지났습니다'), 2000);

console.log('End');
Start
End
2초가 지났습니다
2초가 지났습니다
2초가 지났습니다
2초가 지났습니다
...

(callback은 interval이 지난 후 처음으로 실행된다.)

만약 콜백이 호출되는 것을 멈추고 싶다면 setInterval() 함수의 리턴값을 저장해 놨다가 clearInterval()을 실행하면 된다.

console.log('Start');

const intervalID = setInterval(() => console.log('2초가 지났습니다'), 2000);

// 7초 후에 setInterval() 해제
setTimeout(() => clearInterval(intervalID), 7000);

console.log('End');
Start
End
2초가 지났습니다
2초가 지났습니다
2초가 지났습니다

2초가 지났습니다를 2초마다 출력하지만 7초 후에 clearInterval() 함수가 setInterval() 함수를 해제하기 때문에 2초 지났습니다는 세 번만 출력된다.


DOM의 addEventListener() 함수

addEventListener()는 웹 페이지 요소에 상호 작용이 있을 경우 실행할 함수를 등록한다.

const btn = document.querySelector('.my-btn');

btn.addEventListener('click', () => console.log('button clicked!'));

// ...

자바스크립트로는 웹 페이지의 요소(예: 버튼, 이미지, 문단 등)를 코드로 가져올 수 있는데, 첫 번째 줄에서는 my-btn 클래스를 가진 버튼 요소를 찾아서 btn이라는 변수에 저장했다.

그리고 두 번째 줄은, btn에 클릭('click') 이벤트가 발생할 시 () => console.log('button clicked!') 콜백이 실행되도록 해 줬다. btn.addEventListener() 아래에 있는 코드를 실행하다가 사용자가 btn 요소를 클릭하면 콜백이 실행돼서 'button clicked!'라고 콘솔에 출력되는 동작 방식이다.


React의 useEffect() 함수

function PostList() {
  // ...

  useEffect(() => console.log('render finished!'), []);

  return (
    <div className="post-list">
      <div className="post-item">...</div>
      <div className="post-item">...</div>
      <div className="post-item">...</div>
      ...
    </div>
  );
}

React는 웹사이트 화면을 만드는 데 사용하는 라이브러리이다. React에서는 웹사이트 화면을 컴포넌트(Component) 단위로 분리해서 만든다.

위에서 정의한 PostList()는 어떤 웹사이트의 포스팅 목록에 해당하는 컴포넌트인데, 안에 useEffect()라는 비동기 함수가 있다. 이 함수는 컴포넌트가 모두 화면에 그려지는 시점에 콜백을 실행해 준다.

그러니까 React는 PostList() 함수의 내용을 쭉 실행하고 리턴되는 HTML 요소들을 그리기 시작하는데(화면에 그리는 작업은 시간이 조금 걸린다.) 작업이 완료되고 나면 useEffect()에 등록된 콜백이 실행되고 render finished!라는 메시지가 출력되는 것이다.

지금은 단순히 콜백에서 메시지를 출력하지만 실제로는 웹 리퀘스트를 보내 데이터를 받아 오는 등 다양한 작업을 한다. 콜백을 바로 실행하지 않고 일단 화면을 그리기 때문에 웹 페이지가 더 빨리 로딩되는 것처럼 보이게 할 수 있다.


Express의 get() 함수

app.get('/hello', (req, res) => {
  res.send('Success!');
});

// ...

Express는 서버를 만들 때 사용하는데 서버란 어떤 리퀘스트가 들어오면 리스폰스를 돌려주는 프로그램을 의미한다. 서버는 DOM의 버튼 예시와 비슷하게 리퀘스트가 언제 들어올지를 모르기 때문에 리퀘스트에 대한 처리는 비동기 형태로 한다.

app.get() 함수는 /hello 주소로 GET 리퀘스트가 들어오면 두 번째 아규먼트인 콜백을 실행하는데 콜백은 ‘Success’라는 내용을 담고 있는 리스폰스를 보내준다.

그리고 여기서 재미있는 점은 콜백에 파라미터가 있다는 점이다. 나중에 실행할 콜백에 파라미터가 있는 경우도 많지만 이번 예시의 경우 들어온 리퀘스트 객체(req)와 돌려줄 리스폰스 객체(res)를 파라미터로 받고 있다. 프로그래머는 이걸 이용해서 적절한 리스폰스를 보내면 된다.


 

'코린이 개념잡기 > 비동기 자바스크립트' 카테고리의 다른 글

Promise  (0) 2024.12.22
콜백의 한계점(Callback Hell)  (0) 2024.12.22
비동기 실행의 특징  (0) 2024.12.22
콜백과 비동기 함수  (0) 2024.12.22
콜백(callback)  (0) 2024.12.21

+ Recent posts