React(생활코딩)_16일차_React & Ajax

Lina Hongbi Ko·2023년 10월 9일
0

React_생활코딩

목록 보기
17/23

저번시간까지 리액트 라우터에 대해 공부해보았다.

이번시간에는 생코님 책의 React & Ajax 파트를 실습해보려 한다.

책을 읽기전에 일단, Ajax라는 것에 대해 대충은 알고 있었지만 정확한 개념정리를 하지 않아서 찝찝한 기분이 들었다. 그래서 비동기란 무엇인지, 그리고 ajax에 대해 찾아보고 이와 관련된 여러 개념들을 다시 찾아보고 정리했다.

✏️ 동기 vs 비동기

▪️ 동기 (Synchronous)

: 요청을 보냈을때 응답이 돌아와야 다음 동작을 수행한다. A작업이 모두 진행될까지 B작업은 대기하는 방식이다.

console.log('1');
console.log('2');
console.log('3');

콘솔창을 보면, 1 -> 2 -> 3 순으로 결과가 나오는 것을 확인할 수 있다. 이렇게 위에서 아래로 순차적 작업이 실행된다.

▪️ 비동기 (Asynchronous)

: 요청을 보냈을때 응답 상태와 상관없이 다음 동작을 수행한다. A작업이 시작하면 동시에 B작업이 실행된다. A,B작업 모두 결과값이 나오는대로 출력된다.

console.log('1');
setTimeout(()=>{
	console.log('2');
},0);
console.log('3');

콘솔창을 확인하면 1 -> 3 -> 2 순으로 결과가 나오는 것을 확인할 수 있다. setTimeout() 메소드에 지연시간을 0으로 셋팅해서 순서대로 나올 것으로 예상했지만 그렇지 않다. setTimeout() 메소드는 비동기적 API이기 때문이다.

  1. 첫번째줄에서 console.log('1')을 읽어 콘솔에서 1을 보여준다.
  2. 두번째줄에서 비동기 메소드인 setTimeout() API를 실행했기 때문에, 콜백함수를 실행하기 위해 Task queue에 넣는다. 이 때, event loop가 call stack이 비었을때 이 콜백을 넣기 때문에 대기 한다.
  3. call stack에 있던 console.log('3')을 실행시킨다.
  4. event loop가 callstack에 콜백함수를 넣어 console.log('2')가 실행된다.

자 정리하면, 동기적 방식은 하나가 끝나야 다른 하나도 실행하지만 비동기적 방식은 하나가 끝나기전에 다른 하나도 실행할 수 있다. 그래서 비동기는 동기보다 복잡하지만 하나의 작업을 실행하는데 그 시간 동안 다른 작업도 실행할 수 있다는 장점이 있다. 하지만 단점으로는 다른 작업 또한 동시에 병렬적으로 해서 어떤 순서로 결과가 나올 지 예측할 수 없다.

이걸 통신에 적용해도 마찬가지이다.

📍 동기 통신 vs 비동기 통신

동기 통신은 페이지가 전체 리로딩된다. 즉, 통신을 하는 동안 다른 처리를 아무것도 하지 못하고 현재 하고 있는 통신에만 집중하는 것이다. 페이지를 이동할 때 페이지가 로딩되는게 끝날때까지 기다리는 상황이 대표적인 예에 해당된다.

비동기 통신은 페이지는 가만히 있고 일부만 리로딩 되는 것이다. 즉, 통신을 하면서 다른 처리를 동시에 병렬적으로 처리할 수 있다. 예를 들면, 데이터가 로딩되는 동안 버튼을 클릭하거나 다른 페이지로 이동하는 것처럼 보이지만 기다릴 필요없이 바로 다른 처리나 통신을 하는 예가 이에 해당된다.

예를 들어,

인스타에서 좋아요 버튼을 누를 때마다 페이지 전체가 리로드 되거나 구글에서 검색하는데 추천 검색어가 로드될때마다 페이지가 또 리로드 된다면?

요런 상황들이 계속 발생되면 아주 불편할 것이다. 그래서 우리는 비동기식으로 데이터를 주고 받으며 이 불편함을 해소했다.

자바스크립트를 사용한 비동기 통신은 클라이언트와 서버간 데이터를 주고 받기 위해 HTTP 통신을 사용한다.

그리고 우리는 '자바스크립트에서 비동기 HTTP 통신을 가능하게 해주는 것'을 'Ajax'라고 한다.
(비동기 HTTP통신이란 response와 request를 비동기식으로 다룰 수 있다는 것을 의미)

▪️ Ajax

Ajax는 Asynchronous Javascript And XML의 약자이다. 말 그대로, 비동기 자바스크립트와 XML이라는 뜻이다. 2005년 Jesse James Garett이 처음 만들어낸 말로, HTML, XHTML, CSS, JavaScript, DOM, XMLHttpRequest객체, 등을 비롯해 기존의 여러 기술을 사용하는 새로운 접근법을 설명하는 용어이다.

💡 Ajax

: "비동기 통신 웹개발의 기법(접근법)을 설명하는 용어"

좀 더 풀어서 설명하면, 서버에서 데이터를 받아올 때 전체 페이지를 새로고침 하지 않아도 페이지 일부를 위한 데이터를 동적으로 로드하는 기법이다. JavaScript로 브라우저가 가지고 있는 XMLHttpRequest 객체를 이용해 클라이언트와 서버간 비동기 통신하는 방식이다. 그리고 Ajax의 x가 XML을 뜻하지만, 요즘은 가벼운 용량과 JavaScript의 일부라는 장점이 있는 JSON을 더 많이 사용한다.

이전에 동기식으로 데이터를 로드할 때와 ajax를 이용해 데이터를 비동기식으로 로드할때의 차이를 그림으로 보면 쉽게 알 수 있다.

그리고 사람들이 많이 하는 오해 중, Ajax라는 말을 jQuery의 Ajax를 가르키는 때가 종종 있는데, 이는 정확한 말이 아니다. Ajax라는 기법을 조금 더 쉽게 사용하기 위해서 jQuery라이브러리의 ajax함수를 쓴 것 뿐, 그 용어 자체가 아니란 말이다. 초기의 Ajax라는 기술 자체가 많이 복잡하고 코드가 지저분해서, jQuery의 ajax API를 많이 이용해 생긴 오해이다. (jQuery를 사용하면 코드를 훨씬 간단하게 작성할 수 있고 호환성도 보장된다고 한다)

그림에서 보았듯, 초기의 ajax의 기법을 구현할때 XMLHttpRequest API를 이용해 XMLHttpRequest 객체를 생성하고 ajax 요청 처리를 한다. XMLHttpRequest API는 ajax기법과 함께 탄생한 기술이고 브라우저에서 제공하는 WEB API이다. XMLHttpRequest는 XML만 받아오는게 아니라 XML 이외의 모든 종류의 데이터를 받아오는데 사용할수 있다. 그리고 HTTP 이외의 프로토콜도 지원한다.(file과 ftp도 포함)

💡 XMLHttpRequest()

사용법은

const XMLHttpRequestObj = new XMLHttpRequest();

XMLHttpRequest 생성자로 객체를 만든 다음, 이 객체의 프로퍼티나 메서드를 사용해 ajax통신을 구현한다.
위에서도 언급했듯, XMLHttpRequest API를 이용한 비동기식 통신은 원하는 순서대로 결과를 얻을 수 없기 때문에 async와 await와 같은 것들로 동기식으로 코드를 관리해야해야 하는데, 사용방식이 복잡해 가독성이 좋지 않다.

그래서 좀 더 쉽게 사용할수 있도록, 현재는 대표적으로 fetch api와 axios 라이브러리를 많이 사용한다.

💡 Axios

Axios는 ajax를 잘 구현하기 위한 대표적인 라이브러리이다. jQuery ajax 라이브러리와 같은 거라고 생각하면 된다.
이 라이브러리는 "Promise based HTTP client for the browser and node.js" 라고 명시되어있는데, 이 말은 브라우저, node.js를 위한 Promise API를 활용하는 HTTP 비동기 통신 라이브러리 라는 말이다.

Promise객체를 리턴하고, 크로스 브라우징 기반으로 브라우저 호환성이 좋다. 그리고 fetch에는 존재하지 않는 기능이 많다. (자동으로 JSON 데이터 형식으로 변환, 요청 취소 및 타임아웃 설정 가능, 등)
라이브러리이기 때문에 설치 후 사용 가능 하다.

// axios 라이브러리 설치

//npm 사용
npm i axios

// yarn 사용
yearn add axios

axios의 request method는 다음과 같은 것들이 있다.

  • GET : axios.get(url[, config])
  • POST : axios.post(url, data[, config])
  • PUT : axios.put(url, data[, config])
  • DELET : axios.delete(url[, config])
/* GET */

// 단순 데이터(페이지 요청, 지정된 요청) 요청을 수행할 경우 url에 파라미터 넣기
axios.get('https://test.com/user?userid=3')
  .then(function (response) {
    // 성공 로직
  })
  .catch(function (error) {
    // 에러 로직
  })
  .finally(function () {
    // 항상 실행 로직
  });
 
// 파라미터 데이터를 포함시키는 경우 params 옵션을 이용하여 파라미터를 넣기!
axios.get('https://test.com/user', {
    params: {
      userId: 3
    }
  })
  .then(function (response) {
  // ~
  })
  .catch(function (error) {
  // ~
  })
  .finally(function () {
  // ~
  });
  

/* POST */

// POST 메서드에는 일반적으로 데이터를 Message Body에 포함시켜 보낸다.
axios.post("https://test.com/user", {
		name: 'Fred',
		userid: 100
    })
    .then(function (response) {

    }).catch(function (error) {

    })
    

/* PUT */

// PUT 메서드는 서버에 있는 데이터베이스의 내용을 변경하는 것을 주목적으로 한다.
// 내부적으로 get -> post 과정을 거치기 때문에 post 메서드와 비슷한 형태
axios.put("https://test.com/user", {
        name: 'Fred',
        userid: 100
    })
    .then(function (response) {
         
    }).catch(function (error) {
        
    })
    

/* DELETE */

// delete 메서드에는 일반적으로 body가 비어있다.
axios.delete('https://test.com/user?userid=3')
  .then(function (response) {

  })
  .catch(function (error) {

  })

// query나 params가 많아져서 헤더에 많은 정보를 담을 수 없을 때는 두 번째 인자에 data를 추가한다.
axios.delete('https://test.com/user',{
    data: {
      userid: 3,
      username: "foo"
    }
  })
  .then(function (response) {
  
  })
  .catch(function (error) {
  
  })

💡Fetch

es6에서 지원된 Ajax기법을 구현하기 위한 자바스크립트 내장 API이다.
promise 기반으로 만들어졌기에 Axios와 마찬가지로 데이터를 다루는데 어렵지 않으며, 라이브러리처럼 따로 설치하지 않고도 자바스크립트 내에서 바로 API를 사용하기 때문에 편리하다. 즉, 웹 환경이라면 그냥 바로 사용할 수 있는 것이 장점이다.
기본 응답 결과는 Response객체이고, json으로 바꾸거나 text로 바꾸는 별도의 처리 과정이 필요하다.

구형 브라우저에서는 지원하지 않고, 네트워크 에러 발생시 계속 기다려야 하는 단점이 있으며, axios와는 달리 API 요청을 취소 할 수 없다.

사용법을 보면 fetch()함수 안에 2개의 인자를 작성한다.

fetch(url, [options])
  • url : 접근하고자하는 url이고, 필수로 작성해야한다.
  • [options] : 선택매개변수, 생략할 경우 default는 'GET'방식.
    - GET : fetch()함수의 디폴트방식. 옵션인자가 필요없다.
    • POST : method를 POST로 지정해주고, headers 옵션을 통해 JSON 포맷을 사용한다고 알려주어야 하며, body 옵션에 JSON.stringify() 메소드로 JavaScript 값이나 객체를 JSON 문자열로 변환 후 넣어준다.
    • PUT : method를 PUT으로 지정해주고 POST 방식처럼 사용한다.
    • DELETE : method를 DELETE로 지정해주고, 보낼 데이터가 없으므로 header와 body옵션이 필요하지 않다.
/* GET */

fetch('https://test.com/user?userid=3') //url에 userid란 파라미터를 추가
  .then(res => res.json())
  .then(res => {
    if (res.success) {
        // 로직 ~
    }
  });
 
 
 /* POST */
 
 fetch('https://test.com/user', {
    method: 'post',
    headers: {
    	"Content-Type": "application/json",
  	},
    body: JSON.stringify({
        name: "Jeru",
        batch: 1
    })
  })
  .then(res => res.json())
  .then(res => console.log(res));
  
  
/* PUT */

fetch('https://test.com/user', {
  method: "PUT",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "Maru",
    body: "Test!",
    userId: 20,
  }),
})
  .then((res) => res.json())
  .then((res) => console.log(res));
  
  
/* DELETE */

fetch('https://test.com/user?userid=3', { 
		//url에 userid란 파라미터를 추가
       method: "DELETE",
    })
  .then((res) => res.json())
  .then((res) => console.log(res));

📍 axios vs fetch

  • axios
    장점:
    1. response timeout 처리 방법이 있음
    2. promise 기반으로 다루기 쉬움
    3. 크로스브라우징이 가능해 호환성이 좋음

    단점:
    1. 모듈 설치 해야함

  • fetch
    장점 :
    1. 내장 라이브러리라서 따로 설치 안해줘도됨
    2. promise 기반으로 다루기 쉬움
    3. 내장라이브러리라 사용하는 프레임워크가 안정적이지 않을때 좋음

    단점 :
    1. 구형 브라우저는 지원하지 않음 (브라우저 호환성 상대적으로 떨어짐)
    2. 기능 부족

  • 결론
    : 둘 중에 편한 것을 써도 좋으나, react-native에서는 fetch를 사용하는것이 좋다. react-native의 경우 지속적인 업데이트가 되어서 불안정하므로, axios가 반영하지 못할 수 있는 우려 때문이다.

📍 XHR API vs axios vs fetch

: axios와 fetch는 Promise 기반이지만 XHR은 XMLHttpRequest 객체를 기반이다. XHR API는 async와 결합해 관리하기 어렵다.


요로코럼 "Ajax"에 대해 살펴보았으니,
생코님 책에 있는 리액트와 ajax를 이용한 예제를 실습해보자.

✏️ Ajax로 컴포넌트 초기화하기

// ReactAjax.js 파일

import React from "react";

function ReactAjax() {
  return (
    <div className="ReactAjax">
      <h1>WEB</h1>
      <nav>
        <ul>
          <li>
            <a href="1">HTML</a>
          </li>
          <li>
            <a href="2">CSS</a>
          </li>
          <li>
            <a href="3">JS</a>
          </li>
        </ul>
      </nav>
      <article>
        <h2>Welcome</h2>
        Hello, React &amp; Ajax
      </article>
    </div>
  );
}

export default ReactAjax;

이 코드의 화면을 보면,

요로코럼 결과화면이 나오는 것을 확인할 수 있다.
ajax에 대해 배워보았으니, 위의 글목록(HTML, CSS, JS)을 표시할때 ajax방식으로 외부의 파일에 있는 내용을 가져와서 나타내려고 한다. 또, 하단의 글 내용도 해당 리스트 링크를 클릭했을때, 그에 맞는 정보를 외부에서 가져와 보도록 해보자.

그렇게 하기 위해서는 일단 데이터가 필요하므로, json 파일을 생성하자. 그리고 ajax 방식으로 데이터를 가져올때는 public 디렉토리 내의 파일을 가져올 수 있으므로 루트에 맞게 작성해보자.

// public / list.json 파일

[
	{"id":1, "title":"HTML"},
    {"id":2, "title":"CSS"},
    {"id":3, "title":"JS"}
]

그리고 각 리스트의 item의 id에 따른 상세정보를 표시하는 각각의 json 파일도 작성하자.

// public / 1.json 파일
{
	"id": "1",
    "title" : "HTML",
    "desc" : "HTML is ..."
}
// public / 2.json 파일
{
	"id": "2",
    "title" : "CSS",
    "desc" : "CSS is ..."
}
// public / 3.json 파일
{
	"id": "3",
    "title" : "JS",
    "desc" : "JS is ..."
}

요로코럼 데이터들을 다 완성했으면 이제

ajax로 컴포넌트를 초기화해보자.

일전에 만들었던 nav태그를 컴포넌트로 일단 만들어보자.

// ReactAjax.js 파일
import React, { Component } from 'react';

class Nav extends Component {
	render(){
    	retrun(
        	<nav>
            	<ul>
                	<li><a href="1">HTML</a></li>
                    <li><a href="2">CSS</a></li>
                    <li><a href="3">JS</a></li>
                </ul>
            </nav>
        );
    }
}

function ReactAjax() {
	return(
    	<div className="ReactAjax">
        	<h1>WEB</h1>
            <Nav></Nav>
            <article>
            	<h2>Welcome</h2>
                Hello, React &amp; Ajax
            </article>
        </div>
    );
}

export default ReactAjax;

요로코럼 리스트를 Nav 컴포넌트로 만들어주었다. 이제 Nav 컴포넌트가 생성되는 시점에서 ajax를 사용해 만들어져야 한다. 책에서 말하길, Component 클래스의 메소드 중 컴포넌트가 애플리케이션에 탑재되어 살아나기 시작하는 시점에 componentDidMount 라는 메소드가 많이 사용된다고 한다. 그래서 이 메소드는 컴포넌트를 초기화할때 네트워크 통신을 하기에 최적의 메소드라고 한다.

이 메소드 이용해 fetch API를 호출해보자.

이때 가져오려는 데이터는 public 디렉토리 안의 list.json 파일이다.

// ReactAjax.js 파일

import React, { Component } from 'react';

class Nav extends Component {
	componentDidMount() {
    	fetch('list.json')
        	.then(function(result){
            	return result.json();
            })
            .then(function(json){
            	console.log(json);
            });
    }
    render() {
    	... 생략 ...

fetch API의 첫번째 인자로 가져오려고 하는 데이터의 주소(list.json)를 받았다. 그리고 list.json에 담긴 데이터를 브라우저가 가지고 오게 되면 이 데이터를 어떻게 처리할지 첫번째 then 함수에 구현해야 한다.
(then 함수의 인자로 전달된 함수(콜백함수)는 list.json에 대한 호출이 끝난 다음에 호출되도록 약속 되어 있다.)

그리고 then 함수의 콜백함수 인자인 result를 대상으로 json() 메서드를 사용했다. 이 메서드는 브라우저가 list.json 파일의 데이터를 자바스크립트 객체로 변환시킨다. 그리고 이 변환된 자바스크립트 객체는 두번째 then함수의 콜백함수 인자로 들어가게 된다.

콘솔창을 보면 JSON 형식의 텍스트가 자바스크립트 객체로 변환된 것을 알 수 있다.

이 자바스크립트 객체로 Nav 컴포넌트의 리스트를 생성하려면 state에 자바스크립트 객체를 넣어 렌더해준다.

// ReactAjax.js 파일

import React, { Component } from 'react';

class Nav extends Component {
	state = {
    	list: []
    }
    componentDidMount() {
    	fetch('list.json')
        .then(function(result){
        	result.json();
        })
        .then(function(json){
        	console.log(json);
        	this.setState({list:json});
        }.bind(this));
    }
    render(){
    	let listTag = [];
        for(let i = 0; i < this.state.list.length; i++) {
        	const li = this.state.list[i];
            listTag.push(<li key={li.id}><a href={li.id}>{li.title}</a></li>);
        }
        return(
        	<nav>
            	{listTag}
            </nav>
        );
    }
}

... 생략 ...

코드를 살펴보면,
일단 state에 list라는 배열을 추가하고, fetch의 두번째 then 함수에서 setState() API를 사용해 JSON 데이터(변환된 자바스크립트 객체)를 list에 설정한다.
그리고나서 화면에 보여주기 위해 listTag 라는 빈 배열을 생성하고, 반복문을 통해 list 데이터를 li 컴포넌트에 넣어 리스트가 나오도록 한다.

책에서 두가지를 강조했는데,
1. 컴포넌트가 생성될때 Ajax를 통해 해당 컴포넌트를 초기화해야 할 경우에는 componentDidMount 라는 메소드를 사용한다.
2. Ajax를 사용해 가져온 데이터로 직접 렌더링에 영향을 주는 것이 아니라 state에 넘긴 다음, render 메소드가 state의 변화에 영향을 받아 처리하도록 한다.

✏️ Ajax로 컴포넌트 상태변경하기

Ajax를 이용해서 리스트를 생성했다면 이제, 리스트 항목을 클릭했을때 그에 해당하는 정보가 나오도록 구현해보자.

// ReactAjax.js 파일

import React, { Component } from 'react';

class Nav extends Component {
	... 생략 ...
}

class Article extends Component {
	render(){
    	return(
        	<article>
            	<h2>{this.props.title}</h2>
                {this.props.desc}
            </article>
        );
    }
}

 class ReactAjax extends Component {
 	state = {
    	article: {title:"Welcome", desc:"Hello, React & Ajax"}
    }
    render(){
    	return(
        	<div className="ReactAjax">
            	<h1>WEB</h1>
                <Nav></Nav>
               <Article title={this.state.article.title} desc={this.state.article.desc}></Article>
            </div>
        );
    }
 }
 
 export default ReactAjax;

ReactAjax 컴포넌트를 클래스방식으로 바꿔 state를 추가해주었다. 그리고 Article 컴포넌트를 만들어서 넣어주었다.

이제 Nav 컴포넌트의 항목을 클릭했을때, article의 내용이 변경되게 해보자.

// ReactAjax.js 파일

import React, { Component } from 'react';

class Nav extends Component {
	... 생략 ...
    render(){
    	let listTag = [];
        for(let i = 0; i < this.state.list.length; i++) {
        	const li = this.state.list[i];
            listTag.push(<li key={li.id}>
            	<a href={li.id} data-id={li.id} onClick={function(e){
                	e.preventDefault();
                    console.log('trigger');
                    this.props.onClick(e.target.dataset.id);
                }.bind(this)}>{li.title}</a>
            </li>);
        }
        return(
            ... 생략 ...
        );
    }
}

... 생략 ...

class ReactAjax extends Component {
	... 생략 ...
    render() {
    	return(
        	<div className="ReactAjax">
            	<h1>WEB</h1>
                <Nav onClick={function(id){
                	fetch(id +'.json')
                    .then(function(result){
                    	return result.json();
                    })
                    .then(function(json) {
                    	this.setState({
                        	article:{
                            	title:json.title,
                            	desc:json.desc
                            }
                        });
                    }.bind(this))
                }.bind(this)}></Nav>
                <Article title={this.state.article.title} desc={this.state.article.desc}></Article>
            </div>
        );
    }
}

export default ReactAjax;

리스트의 a태그에 이벤트(선택항목의 id값 넘김)를 넣어주고, Nav 컴포넌트에 id값을 인자로 넣어주어 ajax통신을 하게 만든다.

이렇게하고 저장하면 리스트의 항목을 선택했을때 article 영역의 내용이 Ajax로 가져온 데이터의 내용으로 변경되는 것을 확인할 수 있다.


출처
https://developer.mozilla.org/ko/docs/Web/Guide/AJAX
https://velog.io/@kysung95/%EA%B0%9C%EB%B0%9C%EC%83%81%EC%8B%9D-Ajax%EC%99%80-Axios-%EA%B7%B8%EB%A6%AC%EA%B3%A0-fetch
https://velog.io/@leehaeun0/Ajax-%EB%8A%94-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC-%EC%9D%B8%EA%B0%80%EC%9A%94
https://ji-musclecode.tistory.com/65
https://velog.io/@daybreak/%EB%8F%99%EA%B8%B0-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B2%98%EB%A6%AC
https://kr98gyeongim.tistory.com/108
생활코딩! 리액트 프로그래밍 책

profile
프론트엔드개발자가 되고 싶어서 열심히 땅굴 파는 자

0개의 댓글