안녕하세요, 코린이의 코딩 학습기 채니 입니다.
[리액트를 다루는 기술]의 책을 참고하여 포스팅한 개인 공부 내용입니다.
비동기 작업의 이해
동기적 처리는 요청이 끝날 때까지 기다리는 동안 중지 상태가 되므로 다른 작업을 할 수 없습니다.
즉, 요청이 끝난 후 다음 작업이 실행되는 것
하지만 비동기 처리는 웹 애플리케이션이 멈추지 않으므로 동시에 여러 요인을 처리할 수 있고, 기다리는 동안 다른 함수를 호출할 수도 있습니다.
https://chanychu.tistory.com/281
setTimeout 함수를 사용해 특정 작업을 예약하여 비동기적 처리를 할 수도 있습니다.
function printMe() {
console.log('Hello world');
}
setTimeout(printMe, 3000);
console.log('대기 중..');
// 콘솔 출력 값
대기 중..
Hello world
setTimeout 함수를 호출 후 3초 동안 멈추는 것이 아닌 코드가 위부터 아래까지 모두 호출된 후 3초 뒤에 printMe 함수가 호출되는 것을 확인할 수 있습니다.
자바 스크립트에서 비동기 작업 시 흔히 사용하는 방법 중 하나인 콜백함수를 사용한 것입니다.
콜백 함수
파라미터 값이 주어지면 1초 뒤 10을 더해 반환하는 함수가 있다고 가정,
해당 함수가 처리된 직후 어떠한 작업을 하고 싶다면 아래와 같이 작업해야할 것입니다.
function increase(number, callback) {
setTimeout(() => {
const result = number + 10;
if(callback) {
callback(result);
}
}, 1000)
};
increase(0, result => {
console.log(result);
})
// 콘솔 출력값
10
콜백 함수를 이용해 10이 더해진 result 값이 출력!
만일 1초에 거쳐서 10, 20, 30, 40을 출력한다면 아래와 같이 작업해야할 것입니다.
function increase(number, callback) {
setTimeout(() => {
const result = number + 10;
if(callback) {
callback(result);
}
}, 1000)
};
console.log('작업 시작');
increase(0, result => {
console.log(result);
increase(result, result => {
console.log(result);
increase(result, result => {
console.log(result);
increase(result, result => {
console.log(result);
console.log('작업 완료');
});
});
});
});
// 콘솔 출력값
작업 시작
10
20
30
40
작업 완료
콜백 안에 콜백 함수를 넣어 구현할 수 있지만, 너무 중첩되다보니 가독성이 나빠집니다.
이러한 형태의 코드를 '콜백 지옥'이라고 표현합니다.
Promise
- 콜백 지옥 같은 코드가 형성되지 않게 하는 방안 (ES6 도입)
- then을 사용
function increase(number) {
const promise = new Promise((resolve, reject) => {
// resolve는 성공, reject은 실패
setTimeout(() => {
const result = number + 10;
if(result > 50) {
// 50보다 높으면 에러 발생
const e = new Error('NumberTooBig');
return reject(e);
}
resolve(result); // number + 10 값 성공 처리
}, 1000);
});
return promise;
}
increase(0)
.then(number => {
// Promise에서 resolve된 값은 .then을 통해 받아올 수 있음
console.log(number);
return increase(number); // Promise를 리턴하면
})
.then(number => {
// 또 .then으로 처리 가능
console.log(number);
return increase(number);
})
.then(number => {
console.log(number);
return increase(number);
})
.then(number => {
console.log(number);
return increase(number);
})
.then(number => {
console.log(number);
return increase(number);
})
.catch(e => {
// 도중에 에러 발생 시 catch절을 통해 알 수 있음
console.log(e);
});
이렇게 then을 사용하기 때문에 콜백 지옥이 형성되지 않습니다.
※ Promise 관련 포스팅
https://chanychu.tistory.com/282
https://23life.tistory.com/110
async/await
- Promise를 더 쉽게 사용할 수 있도록 해주는 ES2017(ES8) 문법
- 함수 앞부분에 async 키워드, 해당 함수 내부에서 Promise 앞부분에 await 키워드 사용
- Promise가 끝날 때까지 기다리고, 결과 값을 특정 변수에 담음
function increase(number) {
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
const result = number + 10;
if (result > 50) {
const e = new Error('NumberTooBig');
return reject(e);
}
resolve(result);
}, 1000)
});
return promise;
}
async function runTask() {
try { // try~catch 구문을 사용해 에러 처리
let result = await increase(0);
console.log(result);
result = await increase(result);
console.log(result);
result = await increase(result);
console.log(result);
result = await increase(result);
console.log(result);
result = await increase(result);
console.log(result);
result = await increase(result);
console.log(result);
} catch(e) {
console.log(e);
}
};
runTask();
※ async/await 관련 포스팅
https://chanychu.tistory.com/285
axios로 API 호출해서 데이터 받아 오기
axios
- 가장 많이 사용되고 있는 자바스크립트 HTTP 클라이언트
- HTTP 요청을 Promise 기반으로 처리
프로젝트를 생성하여 라이브러리를 설치해 사용하겠습니다.
$ yarn create react-app news-viewer
$ cd news-viewer
$ yarn add axios
prettier 설정
.prettierrc (최상위 디렉터리)
{
"singleQuote": true,
"semi": true,
"useTabs": false,
"tabWidth": 2,
"trailingComma": "all",
"printWidth": 80
}
파일 자동 불러오기 활성화를 위해 jsconfig.json 파일 생성
jsconfig.json (최상위 디렉터리)
{
"compilerOptions": {
"target": "es6"
}
}
App.js
import { useState } from 'react';
import axios from '../node_modules/axios/index';
function App() {
const [data, setData] = useState(null);
const onClick = () => {
axios.get("https://jsonplaceholder.typicode.com/todos/1").then(response => {
console.log(response);
setData(response.data);
})
}
return (
<div>
<div>
<button onClick={onClick}>불러오기</button>
</div>
{data && <textarea rows={7} value={JSON.stringify(data, null, 2)} readOnly={true} />}
</div>
);
}
export default App;
axios.get 함수 → 파라미터로 전달된 주소에 GET 요청을 해줍니다.
그리고 해당 결과는 .then을 통하여 비동기적으로 확인할 수 있습니다.
※ JSON.stringify 관련 문서 (JSON.stringify(data, null, 2)에서의 매개인자들은 어떤 걸 뜻하는 가?)
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify
async/await 적용 시
function App() {
const [data, setData] = useState(null);
const onClick = async () => {
try {
const response = await axios.get("https://jsonplaceholder.typicode.com/todos/1");
setData(response.data);
} catch(e) {
console.log(e);
}
...
...
동일하게 결과가 잘 출력 됩니다.