<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id = 'btn'>동기식 처리</button>
<script>
window.addEventListener('load', (e)=> {
//DOM 찾아오기
let btn = document.getElementById('btn');
btn.addEventListener('click', (e)=> {
// 이 경우 모든 문장은 순서대로 하나씩 처리 - 동기식처리
const start = Date.now();
for(let i=0; i<10000000; i++){}
const end = Date.now();
console.log(end-start + 'ms');
console.log('작업 완료');
})
})
</script>
</body>
</html>
하나의 작업이 완료되기 전에 다른 자원을 사용하는 작업이나 일정한 시간 단위로 다른 작업을 수행하는 것이 가능
타이머 : 일정한 시간 단위로 함수를 호출하는데 비동기로 처리
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id = 'btn'>동기식 처리</button>
<script>
window.addEventListener('load', (e)=> {
//DOM 찾아오기
let btn = document.getElementById('btn');
btn.addEventListener('click', (e)=> {
// 이 경우 모든 문장은 순서대로 하나씩 처리 - 동기식처리
/*const start = Date.now();
for(let i=0; i<10000000; i++){}
const end = Date.now();
console.log(end-start + 'ms');
console.log('작업 완료');*/
//setTimeout 을 이용하면 두번째 매개변수 시간이 지난 후 한번만 호출
//setinterval 을 이용하면 두번째 매개변수 시간 마다 호출
setTimeout(()=>{
const start = Date.now();
for(let i=0; i<10000000; i++){}
const end = Date.now();
console.log(end-start + 'ms');
},0);
console.log('작업 완료')
})
})
</script>
</body>
</html>
try
setTimeout(() => {throw new Error('에러');}, 1000);
}catch(e){
console.log(e);
}
const promise = new Promise((resolve, reject) => {
비동기 작업을 수행하는 코드
if(조건){
비동기 작업이 성공적으로 수행된 경우 작업
resolve 사용
}else{
비동기 작업이 실패했을 때 수행할 작업
reject 사용
}
})
reject 의 속성
Promise 의 후속 함수
콜백 헬 : 타이머를 이용해서 콜백 함수를 연속해서 호출하는 것
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id = 'btn'>버튼</button>
<script>
// 콜백을 연속해서 호출하는 방식
function increaseAndPrint(n,callback){
setTimeout(()=>{
const increased = n + 1;
console.log(increased);
if(callback){
callback(increased)
}
}, 1000)
}
document.getElementById('btn').addEventListener('click', (e) => {
increaseAndPrint(0, n=>{
increaseAndPrint(n, n=>{
increaseAndPrint(n, n=>{
increaseAndPrint(n, n=>{
increaseAndPrint(n, n=>{
console.log('종료');
})
})
})
})
})
})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id = 'btn'>버튼</button>
<script>
/*document.getElementById('btn').addEventListener('click', (e) => {
increaseAndPrint(0, n=>{
increaseAndPrint(n, n=>{
increaseAndPrint(n, n=>{
increaseAndPrint(n, n=>{
increaseAndPrint(n, n=>{
console.log('종료');
})
})
})
})
})
})*/
//Promise를 사용하도록 수정
function increaseAndPrint(n,callback){
return new Promise((resolve, reject)=> {
setTimeout(()=>{
n = n + 1;
console.log(n);
resolve(n)
},1000)
})
}
document.getElementById('btn').addEventListener('click', (e)=>{
increaseAndPrint(0)
.then(n =>{
return increaseAndPrint(n);
})
.then(n =>{
return increaseAndPrint(n);
})
.then(n =>{
return increaseAndPrint(n);
})
.then(n =>{
return increaseAndPrint(n);
})
.then(n =>{
return increaseAndPrint(n);
})
.catch(e =>{
console.error(e);
})
})
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id = 'btn'>버튼</button>
<script>
//Promise를 사용하도록 수정
function increaseAndPrint(n,callback){
return new Promise((resolve, reject)=> {
setTimeout(()=>{
n = n + 1;
console.log(n);
resolve(n)
},1000)
})
}
document.getElementById('btn').addEventListener('click', (e)=>{
increaseAndPrint(0)
.then(
// resolve 가 전달한 데이터를 가지고 다음 함수를 호출
increaseAndPrint)
.then(increaseAndPrint)
.then(increaseAndPrint)
.then(increaseAndPrint)
.then(increaseAndPrint)
.catch(e =>{
console.error(e);
})
})
</script>
</body>
</html>
asnyc function 함수이름(){
await 비동기 처리 코드;
}
function fethitems(){
return new Promise(resolve, reject){
let items = [1,2,3];
resolve(items);
}
}
async function logitems(){
// 이 작업은 비동기로 동작하지만 아래 문장들은 여기 처리 내용이 정상적으로
// 수행된 후 호출
let result = await.fetchitems();
console.log(result);
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id = 'btn'>버튼</button>
<script>
//Promise를 리턴하는 함수
function sleep(ms){
return new Promise(resolve => {
setTimeout(resolve,ms);
})
}
async function process(){
console.log("시작");
await sleep(1000);
console.log("종료");
}
document.getElementById('btn').addEventListener('click', (e)=>{
process().then(()=>{
console.log('Process 가 종료된 후 호출');
});
console.log("비동기 수행");
})
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id = 'btn'>버튼</button>
<script>
//Promise를 리턴하는 함수
function sleep(ms){
return new Promise(resolve => {
setTimeout(resolve,ms);
})
}
async function makeError(){
await sleep(1000);
//예외 객체를 생성해서 강제로 예외를 발생
const error = new Error();
throw error;
}
async function process(){
try{
await makeError();
}catch(e){
console.log(e);
}
}
document.getElementById('btn').addEventListener('click', (e)=>{
process().then(()=>{
console.log('Process 가 종료된 후 호출');
});
console.log("비동기 수행");
})
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id = 'btn'>버튼</button>
<script>
const getOne = async() => {
await sleep(1000);
return '1'
}
const getTwo = async() => {
await sleep(500);
return '2'
}
const getThree = async() => {
await sleep(1500);
return '3'
}
function sleep(ms){
return new Promise(resolve => setTimeout(resolve,ms));
}
async function processAll(){
// 3개의 함수를 동시에 호출해서 결과를 배열로 생성
// 3개 함수 호출 모두 정상적으로 호출되어야 다음으로 진행할 수 있음
// 하나라도 실패하면 다음 진행이 불가하다
const [one, two, three] = await Promise.all([getOne(),getTwo(),getThree()]);
console.log(one);
console.log(two);
console.log(three);
}
document.getElementById('btn').addEventListener('click', (e)=>{
processAll();
})
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id = 'btn'>버튼</button>
<script>
const getOne = async() => {
await sleep(1000);
return '1'
}
const getTwo = async() => {
await sleep(500);
return '2'
}
const getThree = async() => {
await sleep(1500);
return '3'
}
function sleep(ms){
return new Promise(resolve => setTimeout(resolve,ms));
}
async function processAll(){
// 3개의 함수를 동시에 호출해서 결과를 배열로 생성
// 3개 함수 호출 모두 정상적으로 호출되어야 다음으로 진행할 수 있음
// 하나라도 실패하면 다음 진행이 불가하다
// race 를 호출하면 3개 중에서 가장 먼저 종료된 것의 결과를 리턴 //two 의 sleep 은 500ms
const winner = await Promise.race([getOne(),getTwo(),getThree()]);
console.log(winner);
}
document.getElementById('btn').addEventListener('click', (e)=>{
processAll();
})
</script>
</body>
</html>
자바 스크립트의 비동기 통신 처리를 구현하는 기술
비동기적으로 데이터를 주고받는 기술
최근에는 Fetch API 나 axios 라이브러리를 이용하는 경우가 많음
생성
속성
readyState : 객체의 상태를 나타내는 속성
status : 사버의 응답 상태
statusText : 서버로부터 응답의 상태로 정상적으로 응답을 받으면 OK 그렇지 않으면 NotFound
responseURL : 응답의 연속된 URL
responseText : 서버로부터 온 문자열 , json 이나 csv를 읽을 때 사용
responseXML : 서버로부터 온 문자열
timeout : 요청이 자동으로 종료될 때 까지 걸린 시간
함수
abort() : 취소
getAllResponseHeaders() : 모든 응답 헤더 가져옴
getResponseHeader(이름) : 인자에 해당하는 헤더 정보만 가져옴
open(요청 방식, 요청 URL, 비동기 전송 여부) : ajax 요청 초기화
send(내용) : 요청을 전송
setRequestHeader(이름, 값) : 헤더 설정
sendAsBinary() : 바이너리 데이터 전송
이벤트
abort : 중단
error : 에러 발생
load : 성공
loadend : 응답 종료 - 성공 과 실패 모두에 대응
loadstart : 요청 시작
progress : 진행 중
readystatedchange : readyState 값이 변경될 때 사용
브라우저의 보안 방식으로 문서나 스크립트가 다른 출처에서 가져온 리소소와 상호작용 하는 것을 제한 하는 것
출처는 포트번호 까지
적용되지 않는 경우
적용 되는 경우
서버 측에서 하용 : CORS
클라이언트 측에서 접근 : Proxy 를 이용
We can read txt file!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id = 'btn'>버튼</button>
<script>
document.getElementById('btn').addEventListener('click',(e)=>{
//ajax 객체 생성
let request = new XMLHttpRequest();
console.log(request);
//요청 생성
request.open('GET', '../data/data.txt')
//요청 전송
request.send('');
//응답이 온 경우 수행하는 문
request.addEventListener('load', function(){
alert(request.responseText)
})
})
</script>
</body>
</html>
{
"count":3,
"data":[{"code":1, "name":"lee"},
{"code":2, "name":"eui"},
{"code":3, "name":"joo"} ]
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id = 'btn'>버튼</button>
<script>
document.getElementById('btn').addEventListener('click',(e)=>{
//ajax 객체 생성
let request = new XMLHttpRequest();
console.log(request);
//요청 생성
request.open('GET', '../data/data.json')
//요청 전송
request.send('');
//응답이 온 경우 수행하는 문
request.addEventListener('load', function(){
//alert(request.responseText)
//json 파싱
let result = JSON.parse(request.responseText);
console.log(result.count);
for(item of result.data){
console.log(item.code + ':' + item.name)
}
})
})
</script>
</body>
</html>
<?xml version="1.0" encoding="utf-8"?>
<Companys>
<mem>
<name>lee</name>
<number>1</number>
</mem>
<mem>
<name>eui</name>
<number>2</number>
</mem>
<mem>
<name>joo</name>
<number>3</number>
</mem>
</Companys>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id = 'btn'>버튼</button>
<script>
document.getElementById('btn').addEventListener('click',(e)=>{
//ajax 객체 생성
let request = new XMLHttpRequest();
console.log(request);
//요청 생성
request.open('GET', '../data/data.xml')
//요청 전송
request.send('');
//응답이 온 경우 수행하는 문
request.addEventListener('load', function(){
let result = request.responseXML;
//name 태그의 데이터를 추출
let names = result.getElementsByTagName('name');
//배열 순회
for(let i=0; i<names.length; i = i +1){
let name = names[i].childNodes[0].nodeValue;
console.log(name);
}
})
})
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id = 'btn'>버튼</button>
<script>
document.getElementById('btn').addEventListener('click',(e)=>{
//ajax 객체 생성
let request = new XMLHttpRequest();
console.log(request);
//요청 생성
request.open('GET', 'https://jsonplaceholder.typicode.com/comments?postId=1')
//요청 전송
request.send('');
//응답이 온 경우 수행
request.addEventListener('load', function(){
let result = JSON.parse(request.responseText);
for(item of result){
console.log(item.id)
console.log(item.name);
}
})
})
</script>
</body>
</html>
// 비어있는 상태로 생성
let formData = new FormData();
// 아이디에 해당하는 폼의 데이터를 가지고 생성
let formData = new FormData(document.getElementById(폼의 아이디));
// 데이터 추가
formData.append("이름":값)
requset.open('POST', 'url');
// 파일이 없는 경우만 수행
request.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
let param = '';
for(let pair of formData.entries()){
param += pair[0] + '=' + pair[1] + '&';
}
request.send(param);
let formData = new FormData(); //비어있는 상태로 생성
let formData = new FormData(document.getElementById(폼의아이디));//아이디에 해당하는 폼의 데이터를 가지고 생성
//데이터 추가
formData.append("이름", 값)
request.open("POST", "url");
request.send(formData);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id = 'btn'>버튼</button>
<script>
document.getElementById('btn').addEventListener('click', (e)=>{
fetch('../data/data.json')
.then((response)=> response.json())
.then((data)=> console.log(data))
.catch((error)=> console.log('문제 발생'))
})
</script>
</body>
</html>
자바스크립트에서 비동기 데이터 요청에 많이 사용하는 라이브러리
리액트 프로젝트에서 서버의 데이터 활용하기
리액트 프로젝트 생성
App.js 를 수정해서 데이터 가져오는 부분을 Ajax 코드로 수정
import './App.css';
function App()
{
return (
<button onClick={(e) =>{
let request = new XMLHttpRequest();
//요청 생성
request.open('GET', 'https://jsonplaceholder.typicode.com/users')
//요청 전송
request.send('');
//요청 처리
request.addEventListener('load', ()=>{
//받아온 json 데이터 파싱
let result = JSON.parse(request.responseText);
console.log(result);
})
}}>클릭</button>
);
}
export default App;
import './App.css';
function App()
{
return (
<button onClick={(e) =>{
//요청을 전송하고 전송에 성공하면 그 다음 then 이 호출되는데
//여기서는 받아온 데이터를 파싱한다
//파싱에 성공하면 성공한 결과가 그 다음 then으로 넘어 간다
fetch('https://jsonplaceholder.typicode.com/users')
.then((response)=> response.json())
.then((data)=> {
//데이터를 사용하는 코드 작성
console.log(data)
})
}}>클릭</button>
);
}
export default App;
node.js 를 위한 Promise API를 활용하는 HTTP 비동기 통신 라이브러리
거의 모든 브라우저에서 사용 가능
리액트에서 설치
node 프로젝트에서는 설치해서 사용 가능
리액트는 node runtime 을 이용하는 SPA 프레임워크이기 때문
node 프로젝트가 아닌 경우는 설치해서 사용
$ npm install axios
OR
$ yarn add axios
axios.요청메서드('url')
.then(function(response){
//요청을 하고 응답을 받아오는데 성공했을 때 수행할 내용
})
.catch(function(error){
//에러가 발생핬을 때 수행할 내용
})
.then(function(){
//항상 수행할 내용
})
App.js 파일의 내용을 수정해서 라이브러리를 이용해서 데이터 가져오기
Data
axios.post("url",{data})
axios.delete("url")
axios.put("url",{data})
useState 와 useEffect를 이용한 데이터 로딩 및 출력
useState를 이용해서 요청 상태(데이터)를 관리하고 useEffect 를 이용해서 요청을 시작
데이터 목록을 위한 Users.jsx 파일을 생성하고 작성
import React, {useState, useEffect} from 'react';
import axios from 'axios';
function Users(){
//필요한 상태 생성
const [users,setUsers] = useState(null);
const[loading,setLoading] = useState(false);
const[error, setError] = useState(null);
//컴포넌트가 랜더링 된 후 한번 만 수행하는 코드 만들기
useEffect(()=>{
// 비동기 형태로 실행하기 위해서 함수를 생성
// 스레드 처럼 동작시키기 위해서 함수를 생성
const fetchUsers = async() =>{
try{
// 요청이 시작될 때 데이터 초기화
setError(null);
setUsers(null);
setLoading(true);
// 데이터 요청
const response = await axios.get(
'https://jsonplaceholder.typicode.com/users'
);
//읽어 낸 데이터 출력
setUsers(response.data);
}catch(e){
setError(e);
}
setLoading(false);
};
//함수 호출
fetchUsers();
}, [])
if(loading) return <div>로딩 중..!</div>
if(error) return <div>에러발생....!</div>
if(!users) return null;
return(
<ul>
{users.map(user=>(
<li key = {user.id}>
{user.username} ({user.name})
</li>
))}
</ul>
)
}
export default Users;
import './App.css';
import Users from './components/Users';
function App()
{
return (
<Users/>
);
}
export default App;
리액트에서 데이터를 요청한느 구문은 별도의 함수로 구현해서 필요할 때 데이터를 업데이트할 수 있도록 만들어주는 것이 좋다.
App.js 파일에서 버튼을 누르면 데이터를 수정할 수 있도록 수정
import React, {useState, useEffect} from 'react';
import axios from 'axios';
function Users(){
//필요한 상태 생성
//읽어온 데이터를 관리할 state
const [users, setUsers] = useState(null);
//로딩 여부를 관리할 state
const [loading, setLoading] = useState(false);
//에러 발생 여부를 관리할 state
const [error, setError] = useState(null);
//비동기 형태로 실행하기 위해서 함수를 생성
//스레드 처럼 동작시키기 위해서 함수를 생성
const fetchUsers = async()=>{
try{
//요청이 시작될 때 데이터 초기화
setError(null);
setUsers(null);
setLoading(true);
//데이터 요청
const response = await axios.get(
'https://jsonplaceholder.typicode.com/users');
//읽어낸 데이터 출력
setUsers(response.data);
}catch(e){
setError(e);
}
setLoading(false);
};
//컴포넌트가 랜더링 된 후 한 번 만 수행하는 코드 만들기
useEffect(()=>{
//함수 호출
fetchUsers();
}, [])
if(loading) return alert('로딩중...!') //보기 쉽도록 alert 사용
if(error) return alert('에러발생...!')
if(!users) return null;
return(
<>
<button onClick={fetchUsers}>데이터 새로고침</button>
<ul>
{users.map(user => (
<li key = {user.id}>
{user.username} ({user.name})
</li>
))}
</ul>
</>
)
}
export default Users;
import React, {useReducer, useEffect} from 'react';
import axios from 'axios';
//리듀서 함수 - 변경할 속성이 첫번째 매개변수이고 두번째 매개변수는 그외 데이터
//변경하고자 하는 데이터를 리턴하도록 한다
function 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.data
}
default:
throw new Error('에러 발생');
}
}
function Users(){
const [state, dispatch] = useReducer(reducer, {
loading:false,
data:null,
error:null
});
//비동기 형태로 실행하기 위해서 함수를 생성
//스레드 처럼 동작시키기 위해서 함수를 생성
const fetchUsers = async()=>{
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', data:e})
}
};
//컴포넌트가 랜더링 된 후 한 번 만 수행하는 코드 만들기
useEffect(()=>{
//함수 호출
fetchUsers();
}, [])
//state의 내용을 비구조화 할당
const {loading, data:users, error} = state;
if(loading) return <div>로딩 중...</div>
if(error) return <div>에러 발생</div>
if(!users) return null;
return(
<>
<button onClick={fetchUsers}>데이터 새로고침</button>
<ul>
{users.map(user => (
<li key = {user.id}>
{user.username} ({user.name})
</li>
))}
</ul>
</>
)
}
export default Users;
import {useReducer, useEffect} from 'react';
//리듀서 함수 - 변경할 속성이 첫번째 매개변수이고 두번째 매개변수는 그외 데이터
//변경하고자 하는 데이터를 리턴하도록 한다.
function reducer(state,action){
switch(action.type){
case 'LOADING':
return{
loading:true,
data:null,
error:null
}
case 'SUCESS':
return{
loading:false,
data:action.data,
error:null
}
case 'ERROR':
return{
loading:false,
data: null,
error:action.data
}
default:
throw new Error('에러 발생');
}
}
function useAsync(callback, deps=[]){
const [state, dispatch] = useReducer(reducer,{
loading:false,
data:null,
error:null
});
const fetchData = async()=>{
dispatch({type:'LOADING'});
try{
const data = await callback();
dispatch({type:'SUCESS', data});
}catch(e){
dispatch({type:'ERROR', e});
}
}
useEffect(()=>{
fetchData();
},deps);
return [state, fetchData];
}
export default useAsync;