React +6

LEE EUI JOO·2023년 2월 13일
0

Web Programming

목록 보기
15/17

자바스크립트 동기/비동기 처리


동기 처리 : 순서대로 처리

  • 하나의 작업이 완료되어야만 다음 작업을 처리
<!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>


비동기 처리를 해야 하는 이유

  • Server 에서 데이터를 가져오는 작업 : 스마트폰에서는 서버에 데이터를 요청할 때 비동기로 요청하지 않으면 reject 된다.
  • 파일을 읽는 작업
  • 암호화 혹은 복호화 작업
  • 작업 예약 - 타이머

Promise

  • 개념 : 자바스크립트에서는 비동기 처리를 위해서 콜백 함수를 사용
    • 콜백 패턴은 콜백 헬의 문제가 발생할 수 있는데, 비동기 처리 안에서 콜백 함수를 연속으로 호출하는 경우 가독성이 떨어지고 에러가 발생한느 경우 에러 처리가 어렵다
      • ES6에서는 비동기 처리를 위한 패턴으로 Promise를 제공한다
try
	setTimeout(() => {throw new Error('에러');}, 1000);
}catch(e){
	console.log(e);
}
  • 예외를 처리하지 못함
    • 예외를 처리하지 못하는 이유는 비동기 처리의 콜백 함수는 해당 이벤트가 발생하면 Task Queue 로 이동한 후 호출 스택이 비워졌을 때 호출 스택으로 이동해서 실행이 되는데 비동기 함수는 콜백 함수가 실행될 때 까지 기다리지 않고 즉시 종료되기 때문에 호출스택에서 제거되어 예외를 처리할 수 없다
  • Promise 사용
const promise = new Promise((resolve, reject) => {
	비동기 작업을 수행하는 코드
	if(조건){
			비동기 작업이 성공적으로 수행된 경우 작업
			resolve 사용
	}else{
			비동기 작업이 실패했을 때 수행할 작업
			reject 사용
	}
})
  • reject 의 속성

    • pending : 비동기 처리가 아직 수행되지 않은 상태
    • fulfilled : 비동기 작업을 모두 수행한 상태 - 성공
    • rejected : 비동기 작업을 모두 실패한 상태 - 실패
    • sttled : 비동기 작업을 모두 수행한 상태 - 성공 과 실패 모두
  • Promise 의 후속 함수

    • then : 성공한 경우 수행
    • catch : 예외가 발생했을 때 수행
  • 콜백 헬 : 타이머를 이용해서 콜백 함수를 연속해서 호출하는 것

<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('종료'); 
                            })
                        })
                    })
                })
            })
        })

  • Promise 사용
<!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>

  • 1초마다 성공하면 찍힘
<!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>

async & await

  • 비동기 처리를 위한 문법
asnyc function 함수이름(){
	await 비동기 처리 코드;
}
  • 앞의 작업이 정상적을 처리되어야 만 뒤의 문장들을 처리
  • 비동기 처리 코드는 Promise를 리턴하는 코드
  • await resolve 에 삽입된 데이터를 리턴한다
function fethitems(){
	return new Promise(resolve, reject){
		let items = [1,2,3];
		resolve(items);
	}
}

async function logitems(){
// 이 작업은 비동기로 동작하지만 아래 문장들은 여기 처리 내용이 정상적으로
// 수행된 후 호출
	let result = await.fetchitems();
	console.log(result);
}

  • 비동기 처리 후에 다른 작업을 수행하고자 하는 경우에는 then 이용
<!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>


Ajax (Asynchronous javascript + XML)

  • 자바 스크립트의 비동기 통신 처리를 구현하는 기술

  • 비동기적으로 데이터를 주고받는 기술

    • 전체 화면의 재출력 없이 서버로부터 데이터를 받아서 사용
  • 최근에는 Fetch API 나 axios 라이브러리를 이용하는 경우가 많음

    • 클라이언트의 요청없이 서버로부터 데이터를 받아와야 하는 경우는 SSE(Server Sent Events)를 이용하고 실시간 양방향 통신을 구현하고자 하는 경우는 Web Socket 을 사용한다

XMLHttpRequest 객체

  • 생성

    • new XMLHttpRequest()
  • 속성

    • readyState : 객체의 상태를 나타내는 속성

      • 0 : 객체 생성 직후
      • 1 : open()을 호출한 것
      • 2 : send()를 호출한 상태
      • 3 : 서버에서 응답이 오기 시작한 상태
      • 4 : 서버의 응답이 종료된 상태
    • status : 사버의 응답 상태

      • 100번대 : 처리중
      • 200번대 : 정상 응답
      • 300번대 : 리다이렉트 중
      • 400번대 : 클라이언트 오류
      • 500번대 : 서버 오류
    • 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 값이 변경될 때 사용


SOP (Same Origin Policy - 동일 출처 정책)

  • 브라우저의 보안 방식으로 문서나 스크립트가 다른 출처에서 가져온 리소소와 상호작용 하는 것을 제한 하는 것

    • 출처는 포트번호 까지

    • 적용되지 않는 경우

      • img, link, video, audio, object, embed, applet 태그에는 적용되지 않음
    • 적용 되는 경우

      • ajax(XMLHttpRequest) 와 Fetch API

Ajax 나 Fetch API 를 이용해서 다른 출처의 데이터를 가져오는 방법

  • 서버 측에서 하용 : CORS

    • 서버 측에서 추가 HTTP 헤더를 사용해서 다른 철처의 자원에 접근할 수 있도록 권한을 부여하는 것
  • 클라이언트 측에서 접근 : Proxy 를 이용

    • 자바스크립트에서는 가능하지 않고 다른 프레임워크를 이용해야 하는데 React 의 경우는 설정을 이용해서 가능

Ajax 로 텍스트 내용 가져오기

프로젝트 내에 읽을 텍스트 파일 생성

  • 나중에는 파일은 서버에서 생성해야 한다
  • data.txt
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>


JSON Parsing

  • 데이터가 json 문자열인 경우 JSON.Parse(문자열)을 호출하면 문자열을 자바스크립트 데이터로 변경함
  • data 디렉터리에 data.json 파일을 생성하여 데이터를 생성
{
    "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 Parsing

  • XML 은 responseXML 속성으로 읽으 DOM 객체를 리턴
  • nodeValues 나 attributes 속성을 이용해서 읽어오고 객체를 찾을 때는 getElementById 혹은 getElementByTagName 등의 메서드를 이용
  • data 디렉토리에 data.xml 파일을 만들어 데이터를 생성
<?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>


GET 방식의 파라미터 설정

  • 스크립트 코드 수정
<!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>


POST 방식의 파라미터

  • 파일이 존재하지 않는 경우
// 비어있는 상태로 생성
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);

Fetch API

  • 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)=>{
           fetch('../data/data.json')
           .then((response)=> response.json())
           .then((data)=> console.log(data))
           .catch((error)=> console.log('문제 발생'))
       })
    </script>
</body>
</html>


axios 라이브러리

  • 자바스크립트에서 비동기 데이터 요청에 많이 사용하는 라이브러리

  • 리액트 프로젝트에서 서버의 데이터 활용하기

    • 리액트에서는 서버에서 데이터를 가져오는 별도의 라이브러리가 존재하지 않는다
  • 리액트 프로젝트 생성

    • 프로젝트 이름 : react-api
  • 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;


데이터 가져오는 부분을 Fetch API로 변경

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;


axios 라이브러리 사용

  • 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를 이용한 데이터 로딩 및 출력

  • url : https://jsonplaceholder.typicode.com/users

  • useState를 이용해서 요청 상태(데이터)를 관리하고 useEffect 를 이용해서 요청을 시작

    • 요청을 한 후 상태는 3가지를 관리해줘야 한다
      • 요청의 결과
      • 로딩 상태
      • 에러
  • 데이터 목록을 위한 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;
  • App.js 에 출력
import './App.css';
import Users from './components/Users';
function App()
{
  return (
    <Users/>
  );
}
export default App;

  • 없는 url 을 넣어서 에러를 발생시켜 볼 것


  • 리액트에서 데이터를 요청한느 구문은 별도의 함수로 구현해서 필요할 때 데이터를 업데이트할 수 있도록 만들어주는 것이 좋다.

  • 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;


useState 대신에 useReducer 사용

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;
  • reducer 를 이용하면 state를 사용하지 않기 때문에 다른 컴포넌트에서도 사용할 수 있다.

reducer 를 별도의 파일로 구성해서 재사용성을 증가시키기

  • 리듀서 함수를 가지고 있을 파일을 생성하고 작성
  • useAsync.js
    • 이렇게 Hook 을 사용하는 별도의 파일을 CustomHook 이라고 한다
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;

profile
무럭무럭 자라볼까

0개의 댓글