오늘의 학습
자바스크립트는 싱글스레드이기때문에 기본적으로 동기적으로 코드를 실행한다. 모든 코드를 동기적으로 실행할 때 발생할 수 있는 문제점은 앱 성능 저하를 발생시킬 수 있다는 것이다.
다만, 비효율성을 극복하기 위한 비동기처리 메서드이지만 '문제'가 발생한다.
const getName = (name) => {
setTimeout(() => {
const myName = name;
return myName;
}, 1000);
};
const showName = () => {
console.log(getName());
};
getName('with');
showName();
// --- 결과 ---
// undefined
getName()
은 파라미터를 받아 1초 뒤에 그 파라미터를 myName
으로 반환한다. 그리고 showName()
은 getName()
에서 반환한 이름을 콘솔에 표시한다.
하지만 setTimeout
이 비동기로 처리되는 메서드이기 때문에 showName()
은 앞선 코드를 기다리지 않고 바로 실행된다. 1초 뒤에 name
을 리턴하기 때문에 당장 showName()
에서 보여질 이름값이 없는 것이다.
우리가 생각한 정상적인 작동은 showName()
이 getName()
이 이름을 리턴을 모두 마치기를 기다렸다가 실행되는 것이다. 이렇게 비동기로 처리 되는 것을 기다린 후에 실행되도록 명령하는 것이 callback
이다. 콜백에 showName()
을 넣어주면 getName()
이 끝난 후 그 값을 받아 콘솔에 이름을 보여줄 것이다.
const getName = (name, callback) => {
setTimeout(() => {
const myName = name;
callback(myName);
}, 1000);
};
const showName = (name) => {
console.log(name);
};
getName('with', (name) => {
showName(name);
});
getName()
두번째 파라미터에 callback을 넣어주고, myName
선언 후 callback 함수를 실행시킨다.
함수를 실행할 때는 함수 선언에서와 같이 두번째 파라미터 자리에 비동기 코드가 실행된 후에 실행할 함수를 넣어준다. 여기에서는 showName()
일 것이다. showName()
파라미터에 들어가는 값은 getName()
선언 시, callback()
에 들어간 파라미터를 가져오게 된다. 즉, myName
을 받아오는 것이다.
이렇게 비동기로 실행되는 메서드에서 비롯되는 문제는 callback function을 통해 해결 할 수 있지만, 단점은 가독성이 떨어지고 복잡하다는 것이다.
이러한 것을 해결하기 위해 Promise
가 나왔고, 또한 이것을 더 편하게 사용하고자 async, await
이 나왔다.
fetch
나 axios
에서는 Promise로 반환되는 경우가 많기때문에 정확한 사용을 위해서는 callback function의 원리, 그리고 Promise의 원리에 대해 구체적으로 알 필요가 있다.
// Axios를 이용한 Get 기본구조
import React, { useState, useEffect } from 'react';
import axios from 'axios';
const Users = () => {
// state는 3개를 만든다.
// 정보를 담을 것, 로딩, 에러
const [users, setUsers] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const fetchUsers = async () => {
// async & await을 통해 비동기처리
// try, catch 구문을 통해 '성공', 'error' 감지
try {
setUsers(null); // state initializing
setError(null); // state initializing
setLoading(true); // loading start!
const response = await axios.get(
'https://jsonplaceholder.typicode.com/users/'
);
setUsers(response.data);
} catch (e) {
setError(e); // error 발생
}
setLoading(false); // loading end!
};
useEffect(() => { //useEffect를 통해 컴포넌트가 생성될 때 api 연동 함수 실행
fetchUsers();
}, []);
// 조건에 따라 렌더링 화면 분기
if (loading) return <div>로딩중..</div>;
if (error) return <div>에러가 발생했습니다.</div>;
if (!users) return null;
return (
<>
<ul>
{users.map((item) => (
<li key={item.id}>{item.username}</li>
//배열을 map할 땐, 항상 key 값을 부여한다.
))}
</ul>
<button onClick={fetchUsers}>다시 불러오기</button>
<!-- api 다시 불러오는 버튼 --!>
</>
);
};
export default Users;
리액트에서 axios 를 이용하여 REST API를 호출하는 가장 기본적인 구조이다.
이것을 바탕으로 useReducer
를 사용하거나 또는 redux
를 이용하여 상태관리의 편리함을 추가한다.
import React, { useEffect, useReducer } from 'react';
import axios from 'axios';
// reducer
const reducer = (state, action) => {
switch (action.type) {
case 'LOADING':
return {
loading: true,
data: null,
error: null,
};
case 'SUCCESS':
return {
loading: false,
data: action.data,
error: null,
};
case 'ERROR':
return {
loading: false,
data: null,
error: action.error,
};
default:
throw new Error(`Unhandled action type : ${action.type}`);
}
};
const Users = () => {
//initailState
const [state, dispatch] = useReducer(reducer, {
loading: false,
data: null,
error: null,
});
const fetchUsers = async () => {
// dispatch를 통해 reducer로 action을 전달하고 reducer는 전달받은 action을 참조하여 switch 문에 따라 새로운 state를 생성한다.
dispatch({ type: 'LOADING' });
try {
const response = await axios.get(
'https://jsonplaceholder.typicode.com/users'
);
dispatch({ type: 'SUCCESS', data: response.data });
} catch (e) {
dispatch({ type: 'ERROR', error: e });
}
};
useEffect(() => {
fetchUsers();
}, []);
const { loading, data: users, error } = state;
if (loading) return <div>로딩중..</div>;
if (error) return <div>에러가 발생했습니다.</div>;
if (!users) return null;
return (
<>
<ul>
{users.map((item) => (
<li key={item.id}>{item.username}</li>
))}
</ul>
<button onClick={fetchUsers}>다시 불러오기</button>
</>
);
};
export default Users;
useReducer를 이용하면, reducer를 export해서 다른 컴포넌트에서도 이용할 수 있다. 이로 인해 useState를 필요 이상 많이 사용하는 것을 방지 할 수 있다.
끝.