[react] axios

young-gue Park·2023년 4월 4일
0

React

목록 보기
15/17
post-thumbnail

⚡ axios


📌 axios란?

🔷 브라우저나 Node.js에서 사용 가능한 HTTP 클라이언트 라이브러리

  • 백엔드와 프론트엔드 간의 통신을 수월하게 만든다.
  • Promise API를 주로 활용하며 Get, Post 등의 메서드들을 쉬운 방법으로 사용할 수 있다.
// 간단한 명령어로 axios 추가하기
yarn add axios

📌 axios 실습

🔷 axios 실습을 위해 알아두어야 할 것들이 있다.

  1. JSON PlaceHolder

  2. useReducer

    • useReducer의 prop으로 reducer 함수와 초기 상태를 넣어 사용한다.
    • useState와 유사하지만 update할 항목을 내부 로직으로 바꿔줄 수 있다는 점에서 다르다.

      useState: setState를 통해 현재의 State를 변환
      useReducer: 다양한 로직이 들어간 reducer 함수를 이용해 현재의 State를 변환

    ❗ 하지만 useReducer로는 async await를 통한 네트워크 호출을 할 수 없다. 상태만을 관리하며 컴포넌트가 순수해야하기 때문인데, 왜 순수해야 하는지에 대해서는 뒤에 설명한다.

  1. React Router

    • SPA를 위해 원활한 페이지 이동을 제공한다.
    • 공식 라이브러리는 아니지만 많이 사용되고 있다.
    • index.js에 <BrowserRouter> 태그를 추가하고 실습해본다.

      😭 그런데...
      프로그래머스 강의 상으로는 Router V5를 사용하고 있지만 현재는 V6를 사용한다.
      그리고... V6부터 바뀐 것이 상당히 많아 강의를 따라가도 의미가 없다. ㅜㅜ
      본격적인 Router 사용은 추후에 기회가 닿을 때 해보기로 한다.

  2. 이전에 제작해둔 컴포넌트와 훅들

    • 이전에 제작하여 사용하는 컴포넌트와 훅들에 대한 설명은 생략한다.
    • 더 편한 확장과 import를 위해 구조를 일부 바꾸었다.


컴포넌트는 폴더를 더 나누어 세분화하고 index.js에서 모든 호출을 관리한다.
이렇게 하면 import를 하는 방법도 기존과 달라진다.
// 바뀐 import 예시
import { Header } from './components';
import { useAsync } from './hooks';

🔷 axios 실습 코드

💻 src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import App from './App';
import reportWebVitals from './reportWebVitals';

ReactDOM.render(
  <React.StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode>,
  document.getElementById('root')
);

reportWebVitals();

💻 contexts/PostProvider.js

import { createContext, useCallback, useContext, useEffect, useReducer } from "react";

const PostContext = createContext();
export const usePostContext = () => useContext(PostContext);

const reducer = (state, action) => {
    switch (action.type) {
        case 'INIT_POSTS': {
            return action.payload
        }
        case 'ADD_POST': {
            return [...state, action.payload]
        }
        case 'DELETE_POST': {
            const payload = action.payload;
            return state.filter(item => item.id !== payload.id);
        }
        default: {
            console.error('Wrong type');
            break;
        }
    }
}

const PostProvider = ({ children, initialPosts, handleDeletePost, handleAddPost }) => {
    const [posts, dispatch] = useReducer(reducer, initialPosts || []);

    useEffect(() => {
        dispatch({ type: 'INIT_POSTS', payload: initialPosts || [] });
    }, [initialPosts]);

    const onAddPost = useCallback(async (post) => {
        const payload = await handleAddPost(post);
        dispatch({ type: 'ADD_POST', payload });
    }, [handleAddPost])

    const onDeletePost = useCallback(async (id) => {
        const payload = await handleDeletePost(id);
        dispatch({ type: 'DELETE_POST', payload });
    }, [handleDeletePost])

    return (
        <PostContext.Provider value={{ posts, onDeletePost, onAddPost }}>
            {children}
        </PostContext.Provider>
    )
};

export default PostProvider;

Prop Driiling 발생을 막기 위해 사용하는 context API이다.
Context API (클릭)

포스트 생성, 추가, 삭제를 이용한 useReducer 로직이다. 해당 메서드들은 JSON Placeholder 사이트에서 알 수 있다.


💻 domain/PostAddForm/index.js

import { usePostContext } from "../../../contexts/PostProvider";
import { useForm } from "../../../hooks";
import Spinner from "../../base/Spinner";

const PostAddForm = () => {
    const { onAddPost } = usePostContext();

    const { isLoading, errors, handleChange, handleSubmit } = useForm({
        initialValues: {
            userId: 1,
            title:'',
            body: ''
        },
        onSubmit: async (values) => {
            await onAddPost(values);
        },
        validate: ({ title, body }) => {
            const errors = {};
            if (!title) errors.title = "제목을 입력해주세요."
            if (!body) errors.body = "내용을 입력해주세요."
            return errors;
        }
    });

    return (
        <form  onSubmit={handleSubmit}>
            <div>
                <input type="text" name="title" onChange={handleChange}/>
                {errors.title}
            </div>
            <div>
                <input type="text" name="body" onChange={handleChange} />
                {errors.body}
            </div>
            {isLoading ? <Spinner /> : <button type="submit">Add</button>}
        </form>
    )
}

export default PostAddForm;

useForm 훅
Spinner 컴포넌트


💻 domain/PostItem/index.js

import { useCallback, useState } from "react";
import { Header, Spinner, Text } from "../..";
import { usePostContext } from "../../../contexts/PostProvider";

const PostItem = ({ post }) => {
    const [loading, setLoading] = useState(false);
    const { onDeletePost } = usePostContext();

    const handleDeletePost = useCallback(async (id) => {
        setLoading(true);
        await onDeletePost(id);
        setLoading(false);
    }, [onDeletePost]);

    return (
        <li>
            <Header strong level={3}>
                {post.title}
            </Header>
            <Text>{post.body}</Text>
            <div>
                {loading ? (
                    <Spinner />
                ) : (
                    <button onClick={() => handleDeletePost(post.id)}>Delete</button>
                )}
            </div>
        </li>
    )
}

export default PostItem;

Header, Text 컴포넌트


💻 domain/PostList/index.js

import { usePostContext } from "../../../contexts/PostProvider";
import PostItem from "../PostItem";

const PostList = () => {
    const { posts } = usePostContext();
    
    return (
        <ul>
            {
                posts.map((post) => (
                    <PostItem key={post.id} post={post} />
                ))
            }
        </ul>
    )
}

export default PostList;

💻 App.js

import axios from 'axios';
import { useAsync } from './hooks';
import { Header } from './components';
import { Spinner } from './components';
import PostList from './components/domain/PostList';
import PostProvider from './contexts/PostProvider';
import { useCallback } from 'react';
import PostAddForm from './components/domain/PostAddForm';

const App = () => {
  const initialPosts = useAsync(async () => {
    return await axios
    .get('https://jsonplaceholder.typicode.com/posts')
    .then(response => response.data);
  }, []);

  const handleDeletePost = useCallback(async (id) => {
    return await axios.delete(`https://jsonplaceholder.typicode.com/posts/${id}`).then(() => ({ id }));
  }, []);

  const handleAddPost = useCallback(async (post) => {
    return await axios.post(`https://jsonplaceholder.typicode.com/posts`, post).then((response) => response.data);
  }, []);

  return (
    <PostProvider 
      initialPosts={initialPosts.value}
      handleDeletePost={handleDeletePost}
      handleAddPost={handleAddPost}
    >
    <div>
      <Header>Posts</Header>
      <PostAddForm></PostAddForm>
        {initialPosts.isLoading ?  <Spinner /> : <PostList />}
    </div>
    </PostProvider>
  );
}

export default App;

useAsync 훅

🖨 완성

모든 기능이 정상 작동한다.

이상으로 axios를 실습해보았다.

profile
Hodie mihi, Cras tibi

0개의 댓글