React API

WebStudy / / 2021. 2. 18. 23:39

axios

Rest API를 요청하게 될 때 promise API를 활용하는 HTTP 비동기 통신 라이브러리

REST API

클라이언트와 서버가 소통하는 방식

REST 구성

  • 자원(RESOURCE) - URI
  • 행위(VERB) - HTTP METHOD
  • 표현(Representations)

REST 특징

  1. Uniform Interface
    • URI로 지정한 리소스에 대한 조작을 통일되고 한정적인 인터페이스로 수행하는 아키텍처 스타일
  2. Stateless
    • 무상태성 성격
    • 작업을 위한 상태 정보를 따로 저장하고 관리하지 않는다.
    • 세션 정보나 쿠키 정보를 별도로 저장하고 관리하지 안히 때문에 API 서버는 들어오는 요청만을 단순히 처리한다.
    • 서비스의 자유도가 높아지고 서버에서 불필요한 정보를 관리하지 않는다.
      • 구현이 단순해짐
  3. Cacheable
    • HTTP 기존 웹 표준을 그대로 사용
    • 웹에서 사용하는 기존 인프라를 그대로 활용이 가능
    • HTTP가 가진 캐싱 기능이 적용
    • HTTP 프로토콜 표준에서 사용하는 Last-Modified태그나 E-Tag를 이용하여 캐싱 구현이 가능
  4. Self-descriptiveness
    • REST API 메시지만 보고도 이를 쉽게 이해할 수 있는 자체 표현 구조로 되어 있다.
  5. Client-Server 구조
    • REST 서버는 API를 제공
    • 클라이언트는 사용자 인증이나 Context(세션, 로그인 정보) 등을 직접 관리하는 구조로 각각의 역할이 확실히 구본된다.
    • 클라이언트와 서버에서 개발해야 할 내용이 명확해지고 서로간 의존성이 줄어든다.
  6. 계층형 구조
    • REST 서버는 다중 계층으로 구성될 수 있다.
      • 보안, 로드 밸런싱, 암호화 계층을 추가해 구조상의 유연성을 둘 수 있다.
      • PROXY, 게이트웨이 같은 네트워크 기반의 중간매체를 사용할 수 있다.

REST API 디자인 가이드

  1. URI는 정보의 자원을 표현해야 한다.
  2. 자원에 대한 행위는 HTTP Method로 표현한다.

REST API 중심 규칙

  1. URI는 정보의 자원을 표현해야 한다.
    • 리소스 명은 동사보다는 명사를 사용
  2. 자원에 대한 행위는 HTTP Method로 표현한다.
    • 잘못된 사용: GET /members/delete/1
      • delete같이 행위에 대한 표현이 들어가서는 안된다.
    • 옳은 사용: DELETE /members/1

URI 설계 시 주의할 점

  1. 슬래시 구분자는 계층 관계를 나타내는 데 사용
  2. URI 마지막 문자로 슬래시를 포함하지 않는다.
  3. 가독성을 위해 -은 사용이 가능, _은 적합하지 않다.
  4. URI 경로에는 소문자가 적합
  5. 파일 확장자는 URI에 포함시키지 않는다.
    • REST API에서는 메시지 바디 내용의 포맷을 나타내기 위한 파일 확장자를 URI 안에 포함시키지 않는다.
    • Accept header를 사용한다.
    • 잘못된 사용: http://restapi.example.com/members/soccer/345/photo.jpg
    • 옳은 사용: GET /members/soccer/345/photo HTTP/1.1 Host: restapi.example.com Accept: image/jpg

리소스 간의 관계를 표현하는 방법

REST 리소스 간에는 연관 관계가 있을 수 있다.

  • GET: /users/{userid}/devices
    • 일반적으로 소유 'has' 관계를 표현할 때
  • GET: /users/{userid}/likes/devices
    • 관계명이 애매하거나 구체적으로 표현이 필요할 때

자원을 표현하는 Colloection, Document

Document: 하나의 문서 또는 객체
Collection: 문서들의 또는 객체들의 집합

  • http://restapi.example.com/sports/soccer/players/13
    • sports, players 컬렉션과 soccer, 13를 의미하는 Document로 URI가 이루어진다.
    • 컬렉션은 복수로 사용하고 있다.

useSTate와 useEffect로 데이터를 로딩하기

컴포넌트에서 api요청하는 가장 기본적인 방법
api 요청할 때 3가지 종류의 상태를 관리해야 함

  1. 요청의 결과
  2. 로딩 상태
  3. 에러
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);

    const fetchUsers = async () => {
        try{
            setUsers(null);
            setError(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 <div>로딩중...</div>
    if(error)
        return <div>에러 발생: {error.}</div>
    if(!users)
        return null;
    return(
        <>
            <ul>
                {users.map(user => (
                    <li key={user.id}>
                        {user.username} ({user.name})
                    </li>
                ))}
            </ul>
            <button onClick = {fetchUsers}>새로고침</button>
        </>
    );
}

export default Users;

Reducer로 데이터 로딩하기

  • asyncReducer.js

      // Loading, Success, Error
      export function asyncReducer(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: true,
                      data: null,
                      error: action.error,
                  };
              default:
                  throw new Error('Unhandled action type: ${action.type}');
          }
      }
  • Users.js

      import React, {useEffect, useReducer} from 'react';
      import axios from 'axios';
      import { asyncReducer } from './asyncReducer';
    
      function  Users(){
          const [state, dispatch] = useReducer(asyncReducer, {
              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',
                      error: e,
                  })
              }
          };
    
          useEffect(() => {
              fetchUsers();
          }, []);
    
          const {loading, data: users, error} = state; 
    
          if(loading) 
              return <div>로딩중...</div>
          if(error)
              return <div>에러 발생: {error}</div>
          if(!users)
              return null;
          return(
              <>
                  <ul>
                      {users.map(user => (
                          <li key={user.id}>
                              {user.username} ({user.name})
                          </li>
                      ))}
                  </ul>
                  <button onClick = {fetchUsers}>새로고침</button>
              </>
          );
      }
    
      export default Users;

    useAsync Custom Hook 만들기

    import {useEffect, useReducer, useCallback} from 'react';
    

// Loading, Success, Error
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: true,
data: null,
error: action.error,
};
default:
throw new Error('Unhandled action type: ${action.type}');
}
}

function useAsync(callback, deps = [], skip = false){

const [state, dispatch] = useReducer(reducer,{
    loading: false,
    data: null,
    error: null,
});

const fetchData = useCallback(async () => {
    dispatch({ type: 'LOADING' });
    try{
        const data = await callback();
        dispatch({type:'SUCCESS', data});
    }catch(e){
        dispatch({type:'ERROR',error: e});
    }
}, [callback]);


useEffect(() => {
    if(skip)
        return;

    fetchData();
}, deps);

return [state, fetchData];

}

export default useAsync;


# react-async로 요청 상태 관리하기
요청 상태 관리해주는 Hook을 제공하는 library    
웬만한 기능들을 제공한다.

장점
- 컴포넌트에서 비동기 작업이 필요한 기능들을 제공
- 훅, 컴포넌트 형태로 사용이 가능
- 특정 promise를 기다리는 작업을 중간에 중지도 가능함

단점
- 옵션이 복잡

사용
```js
import React from 'react';
import axios from 'axios';
import {useAsync} from 'react-async';

async function getUser({id}){
    const response = await axios.get('https://jsonplaceholder.typicode.com/users/'+id);
    return response.data;
}

function  User({id}){
    const {
        data: user,
        error,
        isLoading,
    } = useAsync ({
        promiseFn: getUser,
        id,
        watch: id
    });
    //const {loading, data: user, error} = state;

    if(isLoading) 
        return <div>로딩중...</div>
    if(error)
        return <div>에러 발생: {error}</div>
    if(!user)
        return null;

    return(
        <div>
            <h2>{user.username}</h2>
            <p>
                <b>Email: </b>{user.email}
            </p>
        </div>
    );
}

export default User;

Context에서 비동기작업 상태 관리하기

'WebStudy' 카테고리의 다른 글

React Router  (0) 2021.02.19
React 기본 3  (0) 2021.02.15
React 기본 2  (0) 2021.02.13
React 기본 1  (0) 2021.02.12
Web Server 및 WAS  (0) 2021.02.06
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기