#1. Axios

1. Axios의 개념

1) Axios란?

  • Axios는 브라우저, Node.js를 위한 Promise API를 활용하는 HTTP 비동기 통신 라이브러리이다.
  • 상대적으로 다른 HTTP 통신 라이브러리들과 비교해서 문서화가 잘 되어 있고,  API가 다양하다.
  • 프론트엔드와 백엔드의 통신을 쉽게 하기 위해 Ajax와 함께 많이 사용한다.

 

(JS에서 Axios 사용하기 https://chan-co.tistory.com/53)

 

※ 비동기 방식이란?

  • 비동기 방식은 웹페이지를 리로드 하지 않고 데이터를 불러오는 방식이며, Ajax를 통해 서버에 요청을 한 후 멈춰있는게 아니라 프로그램은 계속 돌아간다는 의미를 내포하고 있다.
  • 장점으로는 한번 요청한 리소스를 재요청할 경우 불필요한 리소스는 요청하지 않고, 필요한 부분만 불러와 사용할 수 있다.

 

2) Axios의 특징

  • 운영 환경에 따라 브라우저의 XMLHttpRequest 객체 또는 Node.js의 HTTP API 사용.
  • Promise(ES6) API 사용.
  • 요청 및 응답 데이터 변환.
  • HTTP 요청 취소 및 요청과 응답을 JSON 형태로 자동 변경.

 

3) Axios의 기본 구조 

  • Axios를 사용해서 GET, POST, PUT, DELETE 등의 메서드로 API 요청이 가능하다.
    • GET : 데이터 조회
    • POST: 데이터 입력(등록)
    • PUT: 데이터 수정
    • DELETE: 데이터 삭제

 

4) Axios 설치하기

yarn add axios
  • 해당 리액트 프로젝트 폴더에서 터미널(명령프롬프트)를 통해 위 명령어를 입력하여 라이브러리를 설치한다.

import axios from 'axios';
  • axios를 사용할 jsx 파일에서 import 해주면 사용 가능하다.

 

2. Axios 사용하기

1) 데이터 정의

- myschool.json

{
  "department": [
    {
      "id": 101,
      "dname": "컴퓨터공학과",
      "loc": "1호관"
    },
    {
      "id": 102,
      "dname": "멀티미디어학과",
      "loc": "2호관"
    },
    {
      "id": 201,
      "dname": "전자공학과",
      "loc": "3호관"
    },
    {
      "id": 202,
      "dname": "기계공학과",
      "loc": "4호관"
    }
  ]
}
  • json 파일에 사용할 정보들를 저장한다.

 

- App.jsx

import React, { memo, useCallback, useEffect, useRef, useState } from 'react';
import axios from 'axios';

const Department = () => {

  // ----------------------------------------------------------------- hooks 정의 시작
  // 화면에 표시할 상태값 (ajax로 받아올 json, 초기값을 빈 배열로 정의)
  const [ department, setDepartment ] = useState([]);

  // 검색 키워드 값 정의
  const [ keyword, setKeyword ] = useState('');

  // 삭제할 항목에 대한 id값을 저장하기 위한 상태값
  const [ deleteId, setDeleteId ] = useState(-1);

  // 수정할 항목에 대한 id값을 저장하기 위한 상태값
  const [ updateId, setUpdateId ] = useState(-1);
  // ----------------------------------------------------------------- hooks 정의 끝

  return (
    <div>
      
    </div>
  );
};

// 함수형 컴포넌트의 리렌더링 성능 최적화
export default memo(Department);
  • 상단에 hook을 import 하고, useState를 통해 사용할 상태값들을 정의한다.

 

2) GET (데이터 가져오기)

- App.jsx

const Department = () => {
  
  // ----------------------------------------------------------------- hooks 정의 시작
  ~
  // ----------------------------------------------------------------- hooks 정의 끝


  // ----------------------------------------------------------------- 데이터 호출 시작
  // 페이지가 처음 열렸을 때와 검색어가 반영되었을 때 데이터를 받아온다
  useEffect(() => {
    const getDepartment = async () => {
      try {
        const response = await axios.get('http://localhost:3001/department', {
          // 검색어가 있다면 dname 값으로 설정, 그렇지 않으면 정의 안함
          params: keyword ? { dname: keyword } : null
        });
        setDepartment(department => response.data);
      } catch(e) {
        console.error(e);
      }
    }
    getDepartment();
  }, [keyword]);
  // ----------------------------------------------------------------- 데이터 호출 끝
  
  
  // ----------------------------------------------------------------- 검색 시작
  // 검색어 입력 요소에 연결할 참조 변수
  const myKeywordInput = useRef();

  // 검색 버튼에 대한 클릭 이벤트
  const onButtonClick = e => {
    setKeyword(myKeywordInput.current.value);
  };
  // ----------------------------------------------------------------- 검색 끝

  return (
    <div>
    
      {/* 검색기능 */}
      <form>
        <input type="text" name='keyword' ref={myKeywordInput} />
        <button type='button' onClick={onButtonClick}>검색</button>
      </form>
      
      <hr />
    
      {/* 데이터를 가져와 화면에 보여준다 */}
      <table border='1'>
        <thead>
          <tr>
            <th>학과번호</th>
            <th>학과명</th>
            <th>학과위치</th>
          </tr>
        </thead>
        <tbody>
          {department.length > 0 ? (
            department.map((v,i) => {
              return (
                <tr key={v.id}>
                  <td>{v.id}</td>
                  <td>{v.dname}</td>
                  <td>{v.loc}</td>
                </tr>
              );
            })
          ) : (
            <tr>
              <td colSpan='3' align='center'>검색결과가 없습니다.</td>
            </tr>
          )}
        </tbody>
      </table>
    </div>
  );
};

※ 출력결과

데이터를 가져온 모습
"검색"을 누르면 해당 키워드가 검색된다.

 

2) POST (데이터 입력)

- App.jsx

const Department = () => {
  
  // ----------------------------------------------------------------- hooks 정의
  ~
  // ----------------------------------------------------------------- hooks 정의 끝


  // ----------------------------------------------------------------- 데이터 호출 시작
  ~
  // ----------------------------------------------------------------- 데이터 호출 끝
  
  
  // ----------------------------------------------------------------- 검색 시작
  ~
  // ----------------------------------------------------------------- 검색 끝
  
  
  // ----------------------------------------------------------------- 데이터 추가 시작
  // form에서 submit 이벤트가 발생할 때 호출할 이벤트 핸들러
  // -> 성능 최적화를 위해 useCallback 사용
  const onDepartmentSave = useCallback((e) => {
    // 버튼의 본 기능 차단 (페이지 이동 차단)
    e.preventDefault();

    // form 안에 있는 입력 요소의 값 추출
    const setDname = e.target.dname.value;
    const setLoc = e.target.loc.value;

    const postDepartment = async () => {

      let json = null;

      try {
        const response = await axios.post('http://localhost:3001/department', {
          dname: setDname,
          loc: setLoc
        });
        json = response.data;
      } catch(e) {
        console.error(e)
      }

      if(json !== null) {
        // 기존의 상태값과 배열간 병합을 처리하기 위해 응답결과를 배열로 묶음
        const addArr = [json];
        // 배열 병함 결과로 상태값을 갱신함
        setDepartment(department => department.concat(addArr));
      }
    };
    postDepartment();
  }, [])
  // ----------------------------------------------------------------- 추가 끝

  return (
    <div>
    
      {/* 검색기능 */}
      ...
      
      {/* 저장기능 */}
      <form onSubmit={onDepartmentSave}>
        <label htmlFor="dname">학과: </label>
        <input type="text" name='dname' id='dname' />

        <label htmlFor="loc">위치: </label>
        <input type="text" name='loc' id='loc' />

        <button type='submit'>저장하기</button>
      </form>
      
      <hr />
    
      {/* 데이터를 가져와 화면에 보여준다 */}
      ...
      
    </div>
  );
};

※ 출력결과

"학과"와 "위치"를 입력하고 "저장하기" 버튼을 누르면 데이터가 저장된다

 

3) delete (데이터 삭제)

const Department = () => {
  
  // ----------------------------------------------------------------- hooks 정의
  ~
  // ----------------------------------------------------------------- hooks 정의 끝


  // ----------------------------------------------------------------- 데이터 호출 시작
  ~
  // ----------------------------------------------------------------- 데이터 호출 끝
  
  
  // ----------------------------------------------------------------- 검색 시작
  ~
  // ----------------------------------------------------------------- 검색 끝
  
  
  // ----------------------------------------------------------------- 데이터 추가 시작
  ~
  // ----------------------------------------------------------------- 데이터 추가 끝
  
  
  // ----------------------------------------------------------------- 삭제 시작
  // 삭제하기 버튼이 클릭되었을 때 호출할 이벤트 핸들러
  const onDeleteClick = useCallback(e => {
    // 클릭된 자기 자신
    // const current = e.target;

    // 클릭된 자신에게 숨어 있는 data-id값 추출
    const id = parseInt(e.target.dataset.id);
    // 삭제 대상임을 알림
    setDeleteId(id);
  }, []);

  // deleteId가 변경되었을 때 실행할 hook
  useEffect(() => {
    if(deleteId > -1) {
      // id가 일치하지 않는 항목만 filter로 걸러내어 상태값 갱신 (같지 않은 모든 항목을 리턴)
      // 성능향상을 위해 상태값을 함수형 업데이트로 처리함
      // -> 상태값을 파라미터로 받는 콜백에서 상태값 생신 결과를 리턴
      setDepartment(department => department.filter((v,i) => v.id !== deleteId));

      // TODO: 백엔드에 삭제 요청
      const deleteDepartment = async () => {
        try {
          // ajax를 통한 데이터 삭제 요청
          await axios.delete(`http://localhost:3001/department/${deleteId}`);
        } catch(e) {
          console.error(e);
        }
      };
      deleteDepartment();

      // 상태변수 원래 상태로 돌리기
      setDeleteId(-1);
    }
  }, [deleteId]);
  // ----------------------------------------------------------------- 삭제 끝

  return (
    <div>
    
      {/* 검색기능 */}
      ...
      
      {/* 저장기능 */}
      ...
    
      {/* 데이터를 가져와 화면에 보여준다 */}
      <table border='1'>
        <thead>
          <tr>
            <th>학과번호</th>
            <th>학과명</th>
            <th>학과위치</th>
            <th>삭제</th>
          </tr>
        </thead>
        <tbody>
          {department.length > 0 ? (
            department.map((v,i) => {
              return (
                <tr key={v.id}>
                  <td>{v.id}</td>
                  <td>{v.dname}</td>
                  <td>{v.loc}</td>
                  <td>
                    <button type='button' data-id={v.id} onClick={onDeleteClick}>
                      삭제하기
                    </button>
                  </td>
                </tr>
              );
            })
          ) : (
            <tr>
              <td colSpan='4' align='center'>검색결과가 없습니다.</td>
            </tr>
          )}
        </tbody>
      </table>
    </div>
  );
};

※ 출력결과

"학과"와 "위치"를 입력하고 "저장하기" 누르면 데이터가 저장된다
"삭제하기" 버튼을 누르면 삭제된다

 

4) PUT (데이터 수정하기)

const Department = () => {
  
  // ----------------------------------------------------------------- hooks 정의
  ~
  // ----------------------------------------------------------------- hooks 정의 끝


  // ----------------------------------------------------------------- 데이터 호출 시작
  ~
  // ----------------------------------------------------------------- 데이터 호출 끝
  
  
  // ----------------------------------------------------------------- 검색 시작
  ~
  // ----------------------------------------------------------------- 검색 끝
  
  
  // ----------------------------------------------------------------- 데이터 추가 시작
  ~
  // ----------------------------------------------------------------- 데이터 추가 끝
  
  
  // ----------------------------------------------------------------- 삭제 시작
  ~
  // ----------------------------------------------------------------- 삭제 끝
  
  
  // ----------------------------------------------------------------- 수정 시작
  // 수정하기 버튼이 클릭되었을 때 호출할 이벤트 핸들러
  const onUpdateClick = useCallback(e => {
    e.preventDefault();
    // 수정할 항목에 대한 인덱스 번호를 상태값으로 설정한다.
    setUpdateId(parseInt(e.target.dataset.id));
  }, []);

  // 수정에 대한 저장 버튼이 클릭되었을 때 호출될 이벤트 핸들러
  const onUpdateSubmit = useCallback(e => {
    e.preventDefault();

    // form 요소 내의 input 요소들을 name 속성값으로 접근하여 입력값을 얻는다.
    const setId = e.target.id.value;
    const setDname = e.target.dname.value;
    const setLoc = e.target.loc.value;

    // 백엔드에 데이터 수정 요청
    const putDepartment = async () => {

      try {
        const response = await axios.put(`http://localhost:3001/department/${setId}`, {
          dname: setDname,
          loc: setLoc,
        });

        // 함수형 업데이트
        // -> 콜백함수의 파라미터로 상태값의 복사본이 전달되므로 이 값을 직접 수정해도 된다.
        setDepartment(department => {
          // 원본 상태값과 비교하여 수정된 항목의 배열 인덱스를 찾는다.
          const find = department.findIndex((v,i) => v.id === response.data.id);
          // 원본 상태값의 해당 인덱스 번호(find) 위치 부터 1개를 ajax로 반환받은 수정결과 데이터로 교체한다.
          department.splice(find, 1, response.data);
          return department;
        });

      } catch(e) {
        console.error(e);
      }
    }
    putDepartment();

    // 상태변수 되돌리기
    setUpdateId(-1);
  }, []);
  // ----------------------------------------------------------------- 수정 끝

  return (
    <div>
    
      {/* 검색기능 */}
      ...
      
      {/* 저장기능 */}
      ...
    
      {/* 데이터를 가져와 화면에 보여준다 */}
      <form onSubmit={onUpdateSubmit}>
        <table border='1'>
          <thead>
            <tr>
              <th>학과번호</th>
              <th>학과명</th>
              <th>학과위치</th>
              <th>수정</th>
              <th>삭제</th>
            </tr>
          </thead>
          <tbody>
            {department.length > 0 ? (
              department.map((v,i) => {
                return(
                  <tr key={v.id}>
                    {/* 상태값에 저장되어 있는 수정할 항목의 인덱스에 해당하는 원소라면? */}
                    {updateId === v.id ? (
                      <>
                        {/* 수정을 위한 input 요소를 표시 */}
                        <td><input type="hidden" name='id' defaultValue={v.id} />{v.id}</td>
                        <td><input type="text" name='dname' defaultValue={v.dname} /></td>
                        <td><input type="text" name='loc' defaultValue={v.loc} /></td>
                        <td colSpan='2' align='center'>
                          <button type='submit'>저장</button>
                        </td>
                      </>
                    ) : (
                      <>
                        {/* 데이터를 텍스트로 출력 */}
                        <td>{v.id}</td>
                        <td>{v.dname}</td>
                        <td>{v.loc}</td>
                        <td>
                          <button type='button' data-id={v.id} onClick={onUpdateClick}>
                            수정하기
                          </button>
                        </td>
                        <td>
                          <button type='button' data-id={v.id} onClick={onDeleteClick}>
                            삭제하기
                          </button>
                        </td>
                      </>
                    )}
                  </tr>
                );
              })  
            ) : (
              <tr>
                <td colSpan='5' align='center'>
                  검색결과가 없습니다.
                </td>
              </tr>
            )}
          </tbody>
        </table>
      </form>
    </div>
  );
};

※ 출력결과

"수정하기" 버튼을 누르면 수정할 수 있다
수정 후 "저장" 버튼을 누르면 데이터가 수정된다

'국비수업 > React' 카테고리의 다른 글

[React/Redux] Redux  (0) 2022.05.20
[React] Axios-hooks  (0) 2022.05.17
[React] Hooks  (0) 2022.04.29
[React] CSS 사용하기  (0) 2022.04.26
[React] Props (Properties)  (0) 2022.04.25

+ Recent posts