본문 바로가기
Programming/HTML & CSS & JavaScript

js 6일차 - 비동기, Ajax

by yoon9i 2024. 5. 16.

###########################################################################
###########################################################################
11장. 비동기
###########################################################################

1. 동기 vs 비동기

가. 동기

    function fun() {
        "1출력"
        fun2();                 function fun2() {"2출력"} // 만약 1시간작업이라면?
        "3출력"
    }

    fun();

    실행결과:
            1출력
            2출력
            3출력 // fun2() 가 끝난후에 출력됨

나. 비동기

    function fun() {
        "1출력"
        fun2();                 function fun2() {"2출력"} // 만약 1시간작업이라면?
        "3출력
    }

    fun();

    실행결과:
            1출력
            3출력
            2출력 // fun2() 가 끝난후에 출력


2. 비동기 만드는 방법

가. Promise 객체 이용
- 문법: 함수를 작성할 떄 Promise 객체를 반환한다.

ex>
    #일반함수
    function fun() {

    }

    #비동기함수
    function fun() {
        // 성공시 호출함수: resolve
        // 실패시 호출함수: reject
        return new Promise(function(resolve, reject));
    }

- 실제 구현

    resolve 호출에 대한 처리: .then(function() {})
    reject 호출에 대한 처리: .catch(function() {})

- then().then(). ... 체인 가능

나. async / await 키워드 이용 (*)
- 내부적으로 Promise 사용됨.
- 문법:

    # 일반함수
    function fun() {

    }

    # 비동기 함수
    async function fun() {
        "1출력"
        /////////////

        xxx = await fun2(); // await 함수가 실행이 끝날때까지 
                            // fun() 실행을 중지시킴.

        ////////////
        "2출력"
    }

    var result = fun(); // result 값이 Promise 객체이다.

<!DOCTYPE html>
<html lang="en">

<head>
    <title>js16_비동기1_Promise1_생성</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script>
        // 비동기 함수 생성
        function fun() {
            // return new Promise(함수);
            // return new Promise(function (성공호출, 실패호출) { });

            // resolve: 성공시 호출, reject: 실패시 호출
            return new Promise(function (resolve, reject) {
                console.log("1");
            });
        }

        var result = fun();
        console.log("result: ", result);
        console.log("end.");
    </script>
</head>

<body>

</body>

</html>
<!DOCTYPE html>
<html lang="en">

<head>
    <title>js16_비동기1_Promise2_메서드호출</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script>
        // 비동기 함수 생성
        // 2. resolve, reject 함수 호출

        function fun(status) {
            // resolve: 성공시 호출, reject: 실패시 호출
            return new Promise(function (resolve, reject) {
                console.log("1");
                if (status == "ok") {
                    resolve(); // 성공
                } else {
                    reject(); // 실패
                }

                console.log("2");
            });
        }

        var result = fun("ok");
        console.log("result: ", result);
        console.log("end.");
    </script>
</head>

<body>

</body>

</html>
<!DOCTYPE html>
<html lang="en">

<head>
    <title>js16_비동기1_Promise2_메서드호출2_처리구현</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script>
        // 비동기 함수 생성
        // 2. resolve, reject 함수 호출
        // 3. resolve 호출에 대한 처리작업: then() 사용.
        //    reject 호출에 대한 처리작업: catch() 사용.

        function fun(status) {
            // resolve: 성공시 호출, reject: 실패시 호출
            return new Promise(function (resolve, reject) {
                console.log("1");
                //////////////////////////
                if (status == "ok") {
                    resolve(); // 성공
                } else {
                    reject(); // 실패
                }
                //////////////////////////

                console.log("2");
            });
        }

        var result = fun("ok");
        // resolve 호출에 대한 처리작업: then() 사용.
        // reject 호출에 대한 처리작업: catch() 사용.
        // 중요: resolve 호출시 즉시 then 호출안되고, 실행될 수 있는 상황에 비동기로 실행됨.

        result.then(function () {
            console.log("success");
        }) // resolve 호출시 실행.
            .catch(function () {
                console.log("fail");
            }) // reject 호출시 실행
            .finally(function () {
                console.log("finally");
            });  // 무조건 실행.


        console.log("result: ", result);
        console.log("end.");

        // 실행결과
        // 1
        // 2
        // result:
        //      ▶Promise{...}
        // end
        // success
        // finally
    </script>
</head>

<body>

</body>

</html>
<!DOCTYPE html>
<html lang="en">

<head>
    <title>js16_비동기1_Promise2_메서드호출2_처리구현2_체인</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script>

        function fun(status) {
            // resolve: 성공시 호출, reject: 실패시 호출
            return new Promise(function (resolve, reject) {
                console.log("1");

                if (status == "ok") {
                    resolve(100); // 성공
                    // 값도 넣을수 있음

                    // 1. 임의의 서버와 연동해서 문자열 JSON 을 받음.
                    // ex) "{age:20}"
                } else {
                    reject(); // 실패
                }


                console.log("2");
            });
        }

        var result = fun("ok");


        result.then(function (n) {
            // resolve 의 값 -> n

            // 2. 문자열 JSON ==> JSON
            // ex) return JSON.parse()
            console.log("success", n);

            return n + 100;
        }) // resolve 호출시 실행.
            .then(function (m) {
                // return n + 100 -> m

                // 3. {age:20} 에서 age 이용해서 20을 얻음.
                console.log("success2", m);
            })
            .catch(function () {
                console.log("fail");
            }) // reject 호출시 실행
            .finally(function () {
                console.log("finally");
            });  // 무조건 실행.


        console.log("result: ", result);
        console.log("end.");

    </script>
</head>

<body>

</body>

</html>
<!DOCTYPE html>
<html lang="en">

<head>
    <title>js16_비동기1_Promise2_메서드호출2_처리구현2_체인2_활용</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script>

        function fun(status) {
            // resolve: 성공시 호출, reject: 실패시 호출
            return new Promise(function (resolve, reject) {
                console.log("1");

                if (status == "ok") {
                    // 서버연동했다고 가정
                    // 1. 임의의 서버와 연동해서 문자열 JSON 을 받음.
                    // ex) "{age:20}"

                    var result = '{"age":20}';
                    resolve(result);
                } else {
                    reject(); // 실패
                }


                console.log("2");
            });
        }

        var result = fun("ok");


        result.then(function (n) {
            // resolve 의 값 -> n

            // 2. 문자열 JSON ==> JSON
            // ex) return JSON.parse()

            console.log("result:", n); // {"age":20} 리턴

            return JSON.parse(n);
        }) // resolve 호출시 실행.
            .then(function (m) {
                // 3. {age:20} 에서 age 이용해서 20을 얻음.

                console.log("result:", m.age); // success2 20
            })
            .catch(function () {
                console.log("fail");
            }) // reject 호출시 실행
            .finally(function () {
                console.log("finally");
            });  // 무조건 실행.


        console.log("result: ", result);
        console.log("end.");

    </script>
</head>

<body>

</body>

</html>
<!DOCTYPE html>
<html lang="en">

<head>
    <title>js16_비동기1_Promise2_메서드호출2_처리구현2_체인3_arrow함수</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script>

        function fun(status) {
            // resolve: 성공시 호출, reject: 실패시 호출
            return new Promise(function (resolve, reject) {
                console.log("1");

                if (status == "ok") {
                    // 서버연동했다고 가정
                    // 1. 임의의 서버와 연동해서 문자열 JSON 을 받음.
                    // ex) "{age:20}"

                    var result = '{"age":20}';
                    resolve(result);
                } else {
                    reject(); // 실패
                }


                console.log("2");
            });
        }

        var result = fun("ok");


        // arrow
        // 다음과 같은 형식이 사용빈도가 높음.
        result.then((n) => JSON.parse(n)) // resolve 호출시 실행.
            .then(m => console.log("success2", m.age))
            .catch(() => console.log("fail")) // reject 호출시 실행
            .finally(() => console.log("finally"));  // 무조건 실행.


        console.log("result: ", result);
        console.log("end.");

    </script>
</head>

<body>

</body>

</html>
<!DOCTYPE html>
<html lang="en">

<head>
    <title>js16_비동기2_async함수</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script>
        // async 키워드로 비동기 함수 생성

        // 1. 일반 함수
        function fun() {
            return "일반함수";
        }

        // 2. 비동기 함수
        async function aSyncfun() {
            return "비동기함수"; // 이전 방식의 resolve("비동기 함수");
            // 예외발생 발생되었다면 이전 방식의 reject(); 동일
        }

        // 일반함수 호출
        var result = fun();

        // 비동기함수 호출
        var result2 = aSyncfun();

        // 출력
        console.log("일반함수: ", result); // "일반함수"
        console.log("비동기함수: ", result2); // Promise 객체 반환

        // 비동기 함수 aSyncfun() 리턴값 얻기
        result2.then(n => console.log(n))
            .catch(() => console.log("catch"))
            .finally(() => console.log("finally"))
    </script>
</head>

<body>

</body>

</html>
<!DOCTYPE html>
<html lang="en">

<head>
    <title>js16_비동기2_async함수2_await</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script>

        // async 와 await 사용
        // async 기능: 함수를 비동기함수로 만듬.
        // await: async 함수내의 처리를 wait 시킴.
        //        await 로 지정된 함수가 끝날때 까지.

        var result;
        async function asyncFun() {
            console.log("1");

            // result = (function () { console.log("await 함수"); return 100; })();
            result = await (function () { console.log("await 함수"); return 100; })();

            console.log("END.");
            console.log("내부에서 result: ", result);

            // return 값; .then() 으로 얻는다.
        }

        var promise = asyncFun();
        console.log("외부에서 result: ", result);
        /*
            - await 지정하지 않은 경우 실행 결과
            1
            await 함수
            END.
            내부에서 result: 100
            외부에서 result: 100

            - await 지정한 경우 실행 결과
            1
            await 함수
            외부에서 result: ", undefined
            END.
            내부에서 result: 100
        */

    </script>
</head>

<body>

</body>

</html>

 

###########################################################################
###########################################################################
12장. Ajax
###########################################################################

1. AJAX(Asynchronous Javascript And Xml)
- 비동기 + 자바스크립트 + xml(데이터포맷의미, 현재는 JSON)
==> 클라이언트에서 JS 이용해서 서버와 비동기로 데이터 통신(JSON) 을 하는 방식.

2. 서버와 통신하는 2가지 방법
1> 비 Ajax

                    요청: a태그, 새로고침, 명시적인 url입력후 엔터, form의 submit
    웹브라우저 -----------------> 서버
              <----------------
                    응답: html

    - 응답을 html로 받으면 웹 브라우저의 전체 화면이 리로딩 됨.
    - 1M 가 되는 전체 html을 서버에서 받아야된다. (네트워크 부하가 심하다.)
    - 전체 html 을 받기 때문에 깜박임이 있음. 사용자 UI/UX 가 매우 불편하다.

2> Ajax

                    요청: JS 이용(XMLHttpRequest 객체)
    웹브라우저 -----------------> 서버 (reqres.in 사이트)
                      <----------------
                    응답: JSON
                    ex> {mesg:"logout"}

    - 응답을 JSON으로 받으면 웹 브라우저의 전체 화면이 리로딩 안됨. (깜박거림이 없음.)
    - 크기가 매우 작은 JSON 데이터를 받는다. (네트워크 부하가 거의 없다.)
    - 전체 html 을 받지 않기 때문에 깜박임이 없음. 사용자 UI/UX 가 매우 편하다.
    - React 같은 Front-end 프레임워크는 내부적으로 Ajax 로 서버와 연동한다.

3> Ajax 구현
가. 명시적인 XMLHttpRequest 이용 (OLD 방법, 사용안함)
https://reqres.in/api/users/2

나. async + await + fetch() 이용
https://reqres.in/api/users/2

<!DOCTYPE html>
<html lang="en">

<head>
    <title>js17_Ajax1_async_await_fetch</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script>

        var url = "https://reqres.in/api/users/2";
        var req = async function () {
            var response = await fetch(url);
            console.log(response);
            console.log(response.ok);
            console.log(response.status);
            console.log(response.url);

            // 명시적으로 성공/실패 체크해야된다.
            if (response.status != 200) {
                // 예외처리 코드
            } else {
                // 성공했을때 실행 코드
                // 하단의 코드가 성공코드니 else{} 에 넣으면 된다.
            }

            var json = await response.json();
            console.log(json);

            var person = json.data;
            console.log(person);

            var id = person.id;
            var email = person.email;
            var first_name = person.first_name;
            var last_name = person.last_name;
            var avatar = person.avatar;

            var table = `
                    <table border="1">
                        <tr>
                            <th>아이디</th>
                            <th>이메일</th>
                            <th>이름</th>
                            <th>아바타</th>
                        </tr>
                        <tr>
                            <td>${id}</td>
                            <td>${email}</td>
                            <td>${first_name}</td>
                            <td><img src='${avatar}' width="100" height="100"></td>
                        </tr>
                    </table>         
                `;

            document.querySelector("#result").innerHTML = table;
        };

    </script>
</head>

<body>
    <button onclick="req()">요청</button>
    <div id="result"></div>
</body>

</html>
<!DOCTYPE html>
<html lang="en">

<head>
    <title>js17_Ajax1_async_await_fetch2_then_catch함수</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script>

        var url = "https://reqres.in/api/users/2";
        var req = async function () {

            await fetch(url) // Response 객체
                .then(function (response) { return response.json() })
                .then(function (json) {
                    var person = json.data;
                    console.log(person);

                    var id = person.id;
                    var email = person.email;
                    var first_name = person.first_name;
                    var last_name = person.last_name;
                    var avatar = person.avatar;

                    var table = `
                    <table border="1">
                        <tr>
                            <th>아이디</th>
                            <th>이메일</th>
                            <th>이름</th>
                            <th>아바타</th>
                        </tr>
                        <tr>
                            <td>${id}</td>
                            <td>${email}</td>
                            <td>${first_name}</td>
                            <td><img src='${avatar}' width="100" height="100"></td>
                        </tr>
                    </table>         
                `;

                    document.querySelector("#result").innerHTML = table;
                })
                .catch(function () {
                    console.log("error 발생");
                })
                .finally(function () {
                    console.log("finally 발생");
                });
        };

    </script>
</head>

<body>
    <button onclick="req()">요청</button>
    <div id="result"></div>
</body>

</html>
<!DOCTYPE html>
<html lang="en">

<head>
    <title>js17_Ajax1_XMLHttpRequest객체</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script>

        // XMLHttpRequest 객체 이용
        var httpRequest;

        // 요청처리 기능하는 함수
        function req() {
            httpRequest = new XMLHttpRequest();

            // 서버에서 응답할 때 처리하는 이벤트 지정
            httpRequest.onreadystatechange = res; // 콜백처리. res 함수명만 지정.

            httpRequest.open("get", "https://reqres.in/api/users/2", true); // true: 비동기
            httpRequest.send(null);
        }

        // 응답처리 기능하는 함수: reqres.in 에서 반환하는 JSON 처리.
        function res() {
            // 성공했을 경우
            if (httpRequest.readyState == 4 && httpRequest.status == 200) {
                // 서버에서 응답한 데이터 얻기
                var result = httpRequest.responseText; // String 으로 반환됨.
                console.log(result); // 문자열

                // JSON 객체로 변경
                var json = JSON.parse(result);
                console.log(json); // json 객체

                var person = json.data;
                console.log(person);

                var id = person.id;
                var email = person.email;
                var first_name = person.first_name;
                var last_name = person.last_name;
                var avatar = person.avatar;

                var table = `
                    <table border="1">
                        <tr>
                            <th>아이디</th>
                            <th>이메일</th>
                            <th>이름</th>
                            <th>아바타</th>
                        </tr>
                        <tr>
                            <td>${id}</td>
                            <td>${email}</td>
                            <td>${first_name}</td>
                            <td><img src='${avatar}' width="100" height="100"></td>
                        </tr>
                    </table>         
                `;

                document.querySelector("#result").innerHTML = table;
            }
        }


    </script>
</head>

<body>
    <button onclick="req()">요청</button>
    <div id="result"></div>
</body>

</html>


#####################################
React.js에서 매우 많이 사용되는 코드 패턴
1. arrow 함수
2. 객체분해할당(특히 JSON )
3. async, await 이용한 비동기처리
   ==> 서버와 연동시 필수

4. fetch() 함수
  ==> 서버에 요청하는 함수
  ==> 일반적으로  async, await와 같이 사용.

5. 백티 사용
6. 모듈
   - import 및 export

7. Array객체 메서드
  - map함수
  - filter함수
  - foreach함수