mjeongriver
article thumbnail
Published 2023. 1. 20. 18:11
day71-react TIL/React

1. Ajax란?

- AJAX ( Asynchronous Javascript and XML) 은 웹 페이지의 이동없이 필요한 데이터만 전송하는 기술입니다.

 

 

- 웹 어플리케이션에서 데이터를 가져올 때 서버쪽 데이터가 필요할 때 ajax기법을 사용하게 됩니다.

- 이 작업은 시간이 오래 걸릴 수도 있기 때문에 비동기적으로 처리하게 됩니다.

- 비동기(asynchronous)는 요청이 끝날 때 까지 기다리는 것이 아니라, 동시에 여러 작업을 수행하게 됩니다.

- 나중에 react에서는 다른 서버의 REST API와 통신을 이용하여 데이터베이스 데이터를 가져올 수 있습니다.

 

2. ES6의 fetch를 이용해서 리액트에서 데이터 처리하기

- fetch를 하면 promise라는 객체를 반환함. ( promise.then(콜백 함수) )

Promise = fetch(요청주소)

 

1) 이벤트 클릭 시 처리하기

- 데이터를 가져와서 useState()에 저장하는 작업입니다.

- 극단적으로 표현해 데이터 통신에 100초가 소요되면 useState는 100초 간 undefined 상태가 됩니다.

- 렌더링 시에 에러를 나타내기 때문에, undefined에 관한 처리를 동시에 진행합니다.

import { useEffect, useState } from "react";

const App = () => {

    let [raw, setRaw] = useState();

    const handleClick = () => {
        fetch('https://raw.githubusercontent.com/yopy0817/data_example/master/hi.json')
        .then( response => response.json() )
        .then( data => {
            //console.log(data)
            setRaw(data)
        })
    }

    return (
        <div>
            <h3>클릭시에 fetch로 데이터 가져오기</h3>
            <button type="button" onClick={handleClick}>데이터로드</button>
            {raw !== undefined ?
                <div>
                    {raw.userId}<br />
                    {raw.userPw}<br />
                    {raw.userName}<br />
                </div>
                : undefined
            }
        </div>
    )

}

export default App;

2) 화면 렌더링 완료시 데이터 처리하기 useEffect() 훅 사용

  • 비동기 작업을 컴포넌트에 바로 쓰고 state를 변경하면, 무한루프에 빠지게 됩니다.
  • 그래서 useEffect() 훅을 이용하여 첫번째 렌더링 완료시만 데이터만 가져오도록 처리합니다.

import { useEffect, useState } from "react";

const App = () => {

    let [data, setData] = useState()

    //렌더링 완료시 데이터 로드
    useEffect( () => {
        fetch('https://raw.githubusercontent.com/yopy0817/data_example/master/hi.json')
        .then( response => response.json() )
        .then( data => {
            setData(data)
        })
    }, [])


    return (
        <div>
            <h3>로딩시에 fetch로 데이터 가져오기(같은표현)</h3>
            {data && <div>
                        {data.userId}<br />
                        {data.userPw}<br />
                        {data.userName}<br />
                    </div>
            }
        </div>
    )

}

export default App;

 

* 예제

import { Fragment, useEffect, useState } from "react";

const App = () => {

    /*
    Ajax를 이용해서 외부데이터 가져오기
    1. Promise = fetch()
    */

    //클릭해서 데이터가져오기
    const [raw, setRaw] = useState();
    const handleClick = () => {
        fetch("https://raw.githubusercontent.com/yopy0817/data_example/master/hi.json")
        .then( response => response.json() )
        .then( data => setRaw(data) )

    }


    //화면이 mount이후 데이터가져오기 - useEffect()
    const [data, setData] = useState();

    useEffect(() => {
        fetch("https://raw.githubusercontent.com/yopy0817/data_example/master/hi.json")
        .then(response => response.json() )
        .then(data => setData(data) )

    }, []);

    return (
        <Fragment>

            <button onClick={handleClick}>데이터가져오기</button>
            {
                raw === undefined ?
                <div>
                    데이터준비중 
                </div>
                :
                <div>
                    아이디:{raw.userId}<br/>
                    PW: {raw.userPw}<br/>
                    이름: {raw.userName}<br/>
                </div>
            }

            
            <h3>mount이후 데이터 가져오기</h3>
            {
                data && <div>
                    아이디:{data.userId}<br/>
                    PW:{data.userPw}<br/>
                    이름:{data.userName}<br/>
                </div>
            }





        </Fragment>
    )

}

export default App;

 

3. Axios로 데이터 처리하기

- 엑시오스는 비동기를 더 편하게 처리하는 라이브러리 입니다.

 

- 엑시오스 설치

npm add axios

- 엑시오스

Promise = axios.get(요청주소)

 

1) 이벤트 클릭시 처리하기 (fetch와 거의 같음 😊)

import axios from "axios";
import { useState } from "react";

const App = () => {

    const handleClick = () => {
        axios.get('https://raw.githubusercontent.com/yopy0817/data_example/master/hi.json')
        .then(response => {
            setData( response.data );
        })
    }

    return (
        <div>
            <h3>엑시오스데이터</h3>
            <button type="button" onClick={handleClick}>데이터로드</button>
            {data !== undefined ?
                <div>
                    {data.userId}<br />
                    {data.userPw}<br />
                    {data.userName}<br />
                </div>
                :
                undefined
            }
        </div>
    )
}
export default App;

 

4. async, await 적용하기

- ES6의 문법입니다. 비동기 코드를 간결하게 작성할 수 있게 합니다.

* async, await규칙

1) 어싱크 함수 안에서 어웨잇을 사용한다.

2) function 앞에 async 키워드를 추가 함수는 언제나 프라미스를 반환합니다.

3) 리턴이 프로미스라면 await을 적용하고 then절을 없앨 수 있다.

 

- 비동기에서 3번의 데이터 통신을 한다고 가정합시다.

- 1,2,3,4,5,6의 순서로 출력될 것 같지만 아닙니다.

- 순서를 보장하지 않습니다.

    ...생략
    const handleClick = () => {
        axios.get('https://raw.githubusercontent.com/yopy0817/data_example/master/hi.json')
        .then(response => {
            console.log(response.data);
            console.log(1)
        })
        console.log(2)

        axios.get('https://raw.githubusercontent.com/yopy0817/data_example/master/hello.json')
        .then(response => {
            console.log(response.data);
            console.log(3)
        })
        console.log(4)
        
        axios.get('https://raw.githubusercontent.com/yopy0817/data_example/master/by.json')
        .then(response => {
            console.log(response.data);
            console.log(5)
        })
        console.log(6)
    }

 

* 예제

import axios from "axios";
import { Fragment, useState } from "react";

const App = () => {

    /*
    -Axios는 비동기를 편하게 처리하는 라이브러리 입니다 (fetch로 사용해도 무방합니다)
    -설치 npm add axios
    -Axios는 get(), post()함수를 제공하고, 사용했을때 리턴은 Promise

    */

    const [data, setData] = useState();

    //순서를 보장받고 싶다면?
    const handleClick = () => {

        axios.get("https://raw.githubusercontent.com/yopy0817/data_example/master/hi.json")
        .then(response => {
            console.log(response.data) 
            console.log(1);

            axios.get("https://raw.githubusercontent.com/yopy0817/data_example/master/by.json")
            .then(response => {
                console.log(response.data) 
                console.log(3);

                axios.get("https://raw.githubusercontent.com/yopy0817/data_example/master/hello.json")
                .then(response => {
                    console.log(response.data) 
                    console.log(5);
                });
            });
        });


    }


    //순서를 보장하지 않음
    /*
    const handleClick = () => {
        axios.get("https://raw.githubusercontent.com/yopy0817/data_example/master/hi.json")
        .then(response => {
            console.log(response.data) 
            console.log(1);
        });

        console.log(2);

        axios.get("https://raw.githubusercontent.com/yopy0817/data_example/master/by.json")
        .then(response => {
            console.log(response.data) 
            console.log(3);
        });
        
        console.log(4);

        axios.get("https://raw.githubusercontent.com/yopy0817/data_example/master/hello.json")
        .then(response => {
            console.log(response.data) 
            console.log(5);
        });

        console.log(6);
    }
    */

    return (
        <Fragment>
            <h3>엑시오스로 데이터 가져오기</h3>

            <button onClick={handleClick}>데이터가져오기</button>
            {
                data && <div>
                    아이디: {data.userId}<br/>
                    PW: {data.userPw}<br/>
                    이름: {data.userName}
                </div>
            }



        </Fragment>
    )
}

export default App;

 

- Axios는 이미 Promise를 반환합니다.

- Axios앞에 await을 사용할 수 있고, then() 절을 생략 할  수 있습니다.

- Axios를 호출하는 부모함수에는 await을 반드시 달아줍니다.

 

* async, await의 장점

- 코드의 간결성

- 어싱크, 어웨잇은 동기적 방식(순서를) 보장한다

 

* async, await 적용하여 변경하기

const handleClick = async () => {
    let response = await axios.get('https://raw.githubusercontent.com/yopy0817/data_example/master/hi.json')
    console.log(response.data);
    setData( response.data );

    console.log(1);

    let response2 = await axios.get('https://raw.githubusercontent.com/yopy0817/data_example/master/hello.json')
    console.log(response2.data);

    console.log(2);

    let response3 = await axios.get('https://raw.githubusercontent.com/yopy0817/data_example/master/by.json')
    console.log(response3.data);

    console.log(3);

}

 

5. JS ES6 문법 Promise, async, await

- 프로미스는 자바스크립에 내장된 내장 객체입니다.

- 프로미스 객체에서는 2가지 형태를 지닙니다.

 

1) 상태(state)

- pending: 수행 중

- fullfilled: 성공적 완료

- rejected: 실패

 

2) 프로듀서 - 정보를 제공하는 제공자(즉, promise), 컨슈머 - 사용자(즉, 호출하는 사람)

 

* 프로미스 생성

- executor콜백함수를 전달해야 하며 executor콜백함수는 다시 resolve함수와 reject함수를 받습니다.

//매개변수는 컨슈머가 분명 콜백함수를 전달할거야!(그 콜백함수로 리턴해줄게!)
let promise = new Promise( (success, fail) => {

    //producer 입장에서 성공했을 때 성공을 알리는 함수
    success("success"); 

    //producer 입장에서 실패했을 때 실패를 알리는 함수
    //fail(new Error("fail"));

});

console.log(promise)

* 프로미스의 사용(then(), catch())

- then은 promise가 정상적으로 실행 된다면, 결과를 실행시켜주는 콜백함수이다.

- catch는 promise가 실패했을 때, 결과를 실행시켜주는 콜백함수이다.

//....생략
promise.then( data => console.log(data) )
       .catch( error => console.log(error) )

 

* 프로미스의 적용

- 결과가 언제 돌아갈지 모르지만, 내가 이것을 완료되면 Promise를 전달해 줄테니, 너는 then함수만 이용해서 결과를 받아서 처리해!

 

- ajax fetch()의 내부모습 예시

//자바스크립트로 구현되어있는 fetch라고 가정
function myfetch(req) { 
    //비동기적 실행.. 10초..
    return new Promise( (success, fail) => {
        success("data....");
    });
}

//fetch와 같은 실행구조를 보인다.
myfetch("http://localhost:5502/~~~~~").then( response => console.log(response) );

 

* 예제

<!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>
    
    <script>
        //프로미스 객체 - 자바스크립트 내장객체 (특별한 패턴을 제공해줍니다.)

        //excutor라는 콜백함수를 전달받고 excutor첫번째는 성공시킬 함수, 실패일 때 실행시켜줄 함수
        var promise = new Promise(function(success, fail) {

            //성공
            // setTimeout(function() {
            //     success("success"); //3초 이후 실행
            // }, 3000);

            //실패
            fail(new Error("에러남"));

        });

        // console.log(promise);
        promise.then(function(response) {
            console.log(response);
        }).catch(function(err) {
            console.log(err);
        })





    </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>
    
    <script>
        //예시 ) myFetch
        /*
        function myFetch(url) {
            //....url을 가지고 처리
            return new Promise(function(success, fail) {
                success("data....");
            });
        }

        //var result = myFetch("http://~~~~~~");
        //console.log(result); //promise

        myFetch("http://~~~~~~")
        .then(response => response)
        .then(data => data)
        .then(data => console.log(data));

        */


        //async(이거비동기야) - function앞에 선언하게 되면 return값을 자동으로 Promise변형해줍니다.
        
        async function myFetch(url) {
            return "data..";            
        }

        //var result = myFetch("http://.....");
        //console.log(result);

        //myFetch("http://.....")
        //.then(response => console.log(response));


        //await(기다려) - 리턴이 Promise라면 await을 적용할 수 있고 then절을 생략할 수 있습니다. (단, async함수 안에서만 사용 할 수 있음)

        (async () => {

            let result = await myFetch("http://.....");
            console.log(result);
            
        })();



    </script>

</body>
</html>

 

* Promise를 더 쉽게 사용할 수 있게 해주는 async(이거 비동기야)

- async란 function앞에 선언하며, return값을 자동으로 Promise로 변경해주는 역할을 한다.
- 즉, 비동기 함수의 실행 형태로 변경해준다.

 

* async, await규칙

1) 어싱크 함수 안에서 어웨잇을 사용한다

2) function 앞에 async 키워드를 추가 함수는 언제나 프라미스를 반환합니다.

3) 리턴이 프로미스라면 await을 적용하고 then절을 없앨 수 있다.

 

* Promise를 더 쉽게 사용할 수 있게 해주는 await(기다려)

- 리턴이 프로미스라면 await을 적용하고 then절을 없앨 수 있다. (단 async함수 안에서만 사용 가능합니다)

(async function() {
    let data = await myfetch();
    console.log(data)

})();

 

* async, await의 장점

- 코드의 간결성

- 어싱크, 어웨잇은 동기적 방식(순서)을 보장한다.

 

* 동기적 콜백 vs 비동기적 콜백 예제

<!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>
    
    <script>
        //동기적 콜백 vs 비동기적 콜백

        //동기적 콜백
        function aaa(func) {
            func("success");
        }

        //비동기적 콜백
        function bbb(func) {
            setTimeout(function() {
                func("success");
            }, 2000);
        }

        console.log(1);
        //동기적 콜백의 반환
        aaa(function(result) {
            console.log(result); //success
            console.log(2);
        })

        console.log(3);


        //비동기적 콜백의 반환
        bbb(function(result) {
            console.log(result); //success
            console.log(4);
        })

        console.log(5);

    </script>


</body>
</html>

 

* app.js

import axios from "axios";
import { Fragment, useState } from "react";

const App = () => {

    /*
    -Axios는 비동기를 편하게 처리하는 라이브러리 입니다 (fetch로 사용해도 무방합니다)
    -설치 npm add axios
    -Axios는 get(), post()함수를 제공하고, 사용했을때 리턴은 Promise

    */

    const [data, setData] = useState();


    //async await의 장점
    //1. 코드가 간결해집니다.
    //2. 동기적인 호출 방식을 보장해줍니다.
    const handleClick = async () => {

        let result = await axios.get("https://raw.githubusercontent.com/yopy0817/data_example/master/hi.json")
        console.log(result.data);

        console.log(1);

        let result2 = await axios.get("https://raw.githubusercontent.com/yopy0817/data_example/master/by.json")
        console.log(result2.data);

        console.log(2);

        let result3 = await axios.get("https://raw.githubusercontent.com/yopy0817/data_example/master/hello.json")
        console.log(result3.data);

        console.log(3);

    }


    /*
    //순서를 보장받고 싶다면?
    const handleClick = () => {

        axios.get("https://raw.githubusercontent.com/yopy0817/data_example/master/hi.json")
        .then(response => {
            console.log(response.data) 
            console.log(1);

            axios.get("https://raw.githubusercontent.com/yopy0817/data_example/master/by.json")
            .then(response => {
                console.log(response.data) 
                console.log(3);

                axios.get("https://raw.githubusercontent.com/yopy0817/data_example/master/hello.json")
                .then(response => {
                    console.log(response.data) 
                    console.log(5);
                });
            });
        });
    }
    */


    //순서를 보장하지 않음
    /*
    const handleClick = () => {
        axios.get("https://raw.githubusercontent.com/yopy0817/data_example/master/hi.json")
        .then(response => {
            console.log(response.data) 
            console.log(1);
        });

        console.log(2);

        axios.get("https://raw.githubusercontent.com/yopy0817/data_example/master/by.json")
        .then(response => {
            console.log(response.data) 
            console.log(3);
        });
        
        console.log(4);

        axios.get("https://raw.githubusercontent.com/yopy0817/data_example/master/hello.json")
        .then(response => {
            console.log(response.data) 
            console.log(5);
        });

        console.log(6);
    }
    */

    return (
        <Fragment>
            <h3>엑시오스로 데이터 가져오기</h3>

            <button onClick={handleClick}>데이터가져오기</button>
            {
                data && <div>
                    아이디: {data.userId}<br/>
                    PW: {data.userPw}<br/>
                    이름: {data.userName}
                </div>
            }



        </Fragment>
    )
}

export default App;

 

* 뉴스 API키로 한국 뉴스 데이터 불러와서 NewsList 작성 

(category 누를 때마다 주제에 맞는 기사 불러와짐)

-  App.js, NewsHome.js, NewsList.js, NewsArticle.js, NewsCategory.js, NewsList.module.css

 

import { Route, Routes } from "react-router-dom";
import NewsHome from "./component2/NewsHome";
import Header from './layout/Header';


const App = () => {

    return (
        <Routes>
            <Route element={<Header/>}>
                <Route path="/" element={<NewsHome/> }/>
                <Route path="/:category" element={<NewsHome/> }/>
            </Route>
        </Routes>
    )

}

export default App;
import { Fragment } from "react";
import NewsCategory from "./NewsCategory";
import NewsList from "./NewsList";

const NewsHome = () => {

    return (
        <Fragment>
           
          <NewsCategory/>
          <NewsList/>

        </Fragment>
    )
}

export default NewsHome;
import axios from "axios";
import { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import NewsArticle from "./NewsArticle";

import styled from './NewsList.module.css'; //css


const NewsList = () => {

    //1. API 가져오기
    //news API 개인 key

    //5. 라우터로 들어오는 값에 처리
    const {category} = useParams();
    //category 없거나 undefinded이면 all로 할당 
    const query = (category || 'all') === 'all' ? '' : `&category=${category}`;


    //2. useEffect에서 화면로딩시 데이터처리
    const [data, setData] = useState();
    useEffect( () => {
        (async () => {
            const url = `https://newsapi.org/v2/top-headlines?country=kr${query}&apiKey=위에 입력한 개인키`;
            console.log(url);

            
            let {data: {articles}} = await axios.get(url);
            setData(articles);
            setLoading(true); //로딩완료

        })();
    }, [query]) //6. 변화가 일어날 때 마다 재실행할 변수

    //3. 데이터 로딩처리 (데이터가 오기 전에 state는 undefined)
    const [loading, setLoading] = useState(false);
    if(loading === false) {
        return <div>로딩중</div>
    }

    //4. li태그를 컴포넌트로 변경

    return (
        <div className={styled.news_container}>
           <h3>오늘의 헤드라인</h3>
           <ul className={styled.news_wrap}>
            {   
                data.map( (item, index) => <NewsArticle key={index+1} item={item} /> )
            }
           </ul>
        </div>
    )

}

export default NewsList;


//NewsList에 data.map((item, index) => <NewsArticle key={index+1} item={item}/>)
// 그래서 const NewsArticle = ( {item} ) => 안에 넣어주고 props로 넘겨줌.
//원래 {item.url} 이었는데 {url}로 변경

const NewsArticle = ({ item }) => {

    /* 1. url, urlToImage, title, author, description, publishedAt */

    const { url, urlToImage, title, author, description, publishedAt } = item;
    // console.log(props);

    let date = new Date(publishedAt);
    let year = date.getFullYear();
    let month = date.getMonth() + 1;
    let day = date.getDate();
    

    //props로 넘겨줌.
    return (
        <li>
            <a href={url}>
                <img src={urlToImage} alt={title} />
                <div>
                    <p>{author === null? "기자 없음" : author}</p>
                    {/* <p>{publishedAt}</p> */}
                    <p>{`${year}년 ${month}월 ${day}일`}</p>
                    <p>{title}</p>
                    <p>{description}</p>
                </div>
            </a>
        </li>
    )
}

export default NewsArticle;
import { NavLink } from "react-router-dom";


const NewsCategory = () => {

    /*
    business
    entertainment
    general
    health
    science
    sports
    technology 
    1. 카테고리 맵회전
    2. 라우터설정
    3. NewsList에서는 라우터값을 처리
    */
    const category = [
        {name: "all", topic: "전체"},
        {name: "business", topic: "비지니스"},
        {name: "entertainment", topic: "연예"},
        {name: "general", topic: "일반"},
        {name: "health", topic: "건강"},
        {name: "science", topic: "과학"},
        {name: "sports", topic: "스포츠"},
        {name: "technology", topic: "기술"},
    ]

    const myStyle = {
        color : "pink",
        borderBottom: "3px solid red"
    }
    return (
        <ul>
            {
                category.map( (item, index) => <li key={index}>
                    <NavLink to={ item.name ==='all' ? '/' : `/${item.name}`} style={({isActive}) => isActive ? myStyle : undefined }>{item.topic}</NavLink>
                </li>)
            }
        </ul>

    )
}

export default NewsCategory;
.news_container {
    padding: 50px 40px;
}

.news_wrap {
    display: flex;
    flex-direction: column;
}

.news_wrap li {
    position: relative;
    min-height: 150px;
    padding-left: 200px;
    list-style: none;
}

.news_wrap li img {
    position: absolute;
    top: 0;
    left: 0;
    width: 150px;
    height: 100px;
}

.news_wrap li a {
    color: #000;
    text-decoration: none;
}

'TIL > React' 카테고리의 다른 글

day72-react  (0) 2023.01.25
day70-react  (0) 2023.01.19
day69-react  (0) 2023.01.18
day68-react  (0) 2023.01.18
day67-react  (0) 2023.01.17
profile

mjeongriver

@mjeongriver

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

검색 태그