2025.04.14
동기 작업(Synchronous) : 하나의 작업을 실행하고 마친 뒤에 다음 작업을 순차적으로 실행함(작업이 완료될 때까지 다음 코드 실행 블로킹)
비동기 작업(Asynchronous) : 메인 흐름은 멈추지 않는 상태에서 특정 작업들을 백그라운드에서 처리하여 동시에 처리하는 것처럼 실행함(넌블로킹)
비동기 작업을 더 직관적이고 체계적으로 처리하기 위해 ES6에서 도입된 기능
<script>
function increase(number) {
const promise = new Promise((resolve, reject) => {
setTimeout(
() => {
const result = number + 10;
/* 실패 시 reject*/
if(result > 50) {
const e = new Error('NumberTooBig');
return reject(e);
}
/* 성공 시 resolve */
resolve(result);
},
1000
);
});
return promise;
}
console.log(increase(0));
increase(0)
.then(number => { // promise 내부의 resolve가 호출되고 결과가 담기고 난 뒤가 then이 실행되는 시점
console.log(number);
return increase(number); // increase(10);
})
.then(number => {
console.log(number);
return increase(number); // increase(20);
})
.then(number => {
console.log(number);
return increase(number); // increase(30);
})
.then(number => {
console.log(number);
return increase(number); // increase(40);
})
.then(number => {
console.log(number);
return increase(number); // increase(50);
})
.then(number => {
console.log(number);
console.log('end');
})
.catch(e => { // reject에 들어간 에러 객체를 콜백함수의 인수로 넣어준다.
console.log(e, '가 발생했네');
})
.finally(() => {
console.log('finally 실행...');
});
</script>
promise를 더 쉽게 사용할 수 있게 도와주는 방법
<script>
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 run() {
try{
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, '가 발생했네');
}
}
run();
</script>
Javascript를 사용하면 필요할 때 서버에 네트워크 요청을 보내고 새로운 정보를 받아올 수 있다.
<div id="root"></div>
<script type="text/babel">
async function callAPI() {
/* fetch 호출 -> Promise 객체 반환 */
const promise = fetch('https://jsonplaceholder.typicode.com/users');
console.log(promise); // [[PromiseState]]: "pending"
/* PromiseResult라는 프로미스 안의 프로퍼티에는 직접 접근이 불가능
[[]]으로 감싸져 있는 곳은 직접 접근이 불가능
따라서 반드시 await 또는 .then()으로 결과를 꺼내야 함 */
console.log(promise['[[PromiseResult]]']);
/* async await를 활용해서 실제 응답(Response 객체) 꺼내기 */
const response = await promise;
console.log(response);
/* 응답 상태 코드 확인 */
console.log(`응답 상태: ${response.status}`);
/* 응답 헤더 출력 (Headers는 이터러블 객체) */
console.log('응답 헤더');
// console.log(response.headers);
/* response.headers에 대한 내용 중 일부는 숨김 프로퍼티([Symbol.iterator])라서 for of문으로 확인함 */
/* Symbol.iterator는 이 객체가 이터러블(반복 가능한 객체)임을 의미하는 표준 속성
.next().value는 그 중 첫 번째 헤더 [key, value]를 반환 */
// console.log(response.headers[Symbol.iterator]().next().value);
/* response.headers는 이터러블이기 때문에
for...of문에서 구조 분해 할당으로 [key, value]로 순회
헤더 전체를 "Content-Type: application/json" 같은 형태로 출력*/
for(let [key, value] of response.headers) {
console.log(`${key}: ${value}`);
}
/* 본문(body) 사용 여부 체크 */
console.log(`본문 내용 사용 여부: ${response.bodyUsed}`);
/* Response 객체의 text()메소드 */
// const responseText = await response.text();
// console.log(responseText);
/* Response 객체의 json()메소드: 결과로 넘어온 json 문자열을 파싱(문자열을 잘라서 js 객체로 변환)해서 promise 객체를 반환
직렬화 역직렬화
*/
const json = await response.json();
console.log('json' , json);
/* 파싱된 객체 배열 반복 출력 */
for(let i = 0; i < json.length; i++){
console.log(json[i]);
}
console.log(`본문 내용 사용 여부: ${response.bodyUsed}`);
/* 응답을 1회 받고 난 후 body 내용을 확인 후에는 더 이상 응답 body내용에 접근할 수 없다. */
// json = await response.json();
// console.log(json);
// console.log('end'); // fetch가 비동기 방식으로 동작한다는 걸 확인하기 위한 출력 구문
}
function App() {
const onClickHandler = () => {
callAPI();
}
return <button onClick={onClickHandler}>API 요청</button>
}
ReactDOM.createRoot(document.getElementById('root')).render(<App/>);
</script>
<div id="root"></div>
<script type="text/babel">
function callAPI() {
/* 1. API 요청 */
fetch('https://jsonplaceholder.typicode.com/users')
/*응답(Response 객체) 처리*/
.then((response) => {
console.log(response);
/* 본문(body)을 JSON으로 파싱 (비동기) */
return response.json();
/* 파싱된 결과 사용 */
}).then((json) => {
/* 배열 형태로 출력됨 */
console.log(json);
});
}
function App() {
const onClickHandler = () => {
callAPI();
}
return <button onClick={onClickHandler}>API 요청</button>
}
ReactDOM.createRoot(document.getElementById('root')).render(<App/>);
</script>
JavaScript의 내장 함수가 아니기 때문에, 사용 전에 CDN이나 npm 설치가 필요
/* javascript 내장 함수가 아니기 때문에 CDN 방식으로 링크 추가 */
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<div id="root"></div>
<script type="text/babel">
function callAPI() {
// console.log('실행되나');
axios.get('https://jsonplaceholder.typicode.com/users')
.then(res => {
console.log(res);
console.log(res.data)})
.catch(err => console.log(err));
}
function App() {
const onClickHandler = () => {
callAPI();
}
return <button onClick={onClickHandler}>API 요청</button>
}
ReactDOM.createRoot(document.getElementById('root')).render(<App/>);
</script>
비동기 작업은 실행 순서를 보장하지 않기 때문에, 작업의 순서가 중요할 경우 Promise나 async/await을 사용해 명시적으로 순서를 제어해야 한다
<div id="root"></div>
<script type="text/babel">
const API_KEY = '123456789qwerasdfzxcv';
const {useEffect, useState} = React;
function Weather() {
const [position, setPosition] = useState({});
const [cityName, setCityName] = useState('');
const [weather, setWeather] = useState({});
const [wind, setWind] = useState({});
useEffect(() => {
new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition((currentPosition) => {
setPosition({
longitude: currentPosition.coords.longitude,
latitude: currentPosition.coords.latitude
})
resolve(currentPosition.coords);
});
}).then(coords => {
fetch(`https://api.openweathermap.org/data/2.5/weather?lat=${coords.latitude}&lon=${coords.longitude}&appid=${API_KEY}`)
.then(response => response.json())
.then(json => {
console.log(json);
console.log(json.name); // 조회 된 도시 이름 문자열
console.log(json.weather[0]); // 조회 된 날씨 객체
console.log(json.wind); // 조회 된 바람 객체
/* state 3개에 각각 따로 저장 */
setCityName(json.name);
setWeather(json.weather[0]);
setWind(json.wind);
});
});
},
[]
);
return (
<>
<h3>현재 위치</h3>
<h4>{`경도: ${position.longitude} 위도: ${position.latitude}`}</h4>
<h4>{`조회 도시: ${cityName}`}</h4>
<h4>{`날씨: ${weather.main} 날씨설명: ${weather.description}`}</h4>
<h4>{`풍향: ${wind.deg}도 풍속: ${wind.speed}m/s`}</h4>
</>
);
}
function App() {
return (
<>
<h1>오늘의 날씨</h1>
<Weather/>
</>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<App/>);
</script>
<div id="root"></div>
<script type="text/babel">
const API_KEY = '123456789qwerasdfzxcv';
const {useEffect, useState} = React;
function Weather() {
const [position, setPosition] = useState({});
const [cityName, setCityName] = useState('');
const [weather, setWeather] = useState({});
const [wind, setWind] = useState({});
function getPosition() {
return (
new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition((currentPosition) => {
setPosition({
longitude: currentPosition.coords.longitude,
latitude: currentPosition.coords.latitude
})
resolve(currentPosition.coords);
});
})
);
}
function getWeather(coords) {
return fetch(`https://api.openweathermap.org/data/2.5/weather?lat=${coords.latitude}&lon=${coords.longitude}&appid=${API_KEY}`)
.then(response => response.json());
}
useEffect(() => {
async function setWeatherState() {
const currentPosition = await getPosition();
const result = await getWeather(currentPosition);
setCityName(result.name);
setWeather(result.weather[0]);
setWind(result.wind);
}
setWeatherState();
},
[]
);
return (
<>
<h3>현재 위치</h3>
<h4>{`경도: ${position.longitude} 위도: ${position.latitude}`}</h4>
<h4>{`조회 도시: ${cityName}`}</h4>
<h4>{`날씨: ${weather.main} 날씨설명: ${weather.description}`}</h4>
<h4>{`풍향: ${wind.deg}도 풍속: ${wind.speed}m/s`}</h4>
</>
);
}
function App() {
return (
<>
<h1>오늘의 날씨</h1>
<Weather/>
</>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<App/>);
</script>
직렬화(Serialization)
역직렬화(Deserialization)
외부 API 호출하는 경우