React

Axios

차돌박이츄베릅 2023. 7. 6. 10:21

http를 이용해서 서버와 통신하기 위해 사용하는 패키지

https://axios-http.com/kr/docs/req_config

 

요청 Config | Axios Docs

요청 Config 다음은 요청을 만드는 데 사용할 수 있는 config 옵션들 입니다. 오직 url만 필수입니다. method를 지정하지 않으면 GET방식이 기본값 입니다. { url: '/user', method: 'get', baseURL: 'https://some-domain.

axios-http.com

 

설치

yarn add axios

 

테스트용 json-server랑 사용할거면

https://console-log.tistory.com/149

 

환경변수 적용

환경변수 적용할 땐 대신 서버를 껐다켜야 반영됨.
깃헙에 올리면 안됨

(./env)

REACT_APP_SERVER_URL=http://localhost:4000

 

사용법

GET

axios.get(url[, config])

// 조회 함수
// db로부터 값을 가져오기 위해선 비동기 함수를 만들거. 서버 통신 자체가 비동기 통신
const fetchTodos = async () => {
    // 응답을 받을 때까지 기다리도록 await 키워드 추가
    // 안에 들은 data 부분만 받아오려고 구조분해할당
    // 환경변수 적용함
    const { data } = await axios.get(`${process.env.REACT_APP_SERVER_URL}/todos`);
    setTodos(data);
};

 

POST

axios.post(url[, data[, config]])

// 추가 함수
// 변수이름이 서브밋을 눌렀을때 발생하는 핸들러라는 뜻으로 지어줌
// post 요청을 하는거니까 async함수
const onSubmitHandler = async () => {
    // axios.post('http://localhost:4000/todos', inputValue);

    // 환경변수 적용
    await axios.post(`${process.env.REACT_APP_SERVER_URL}/todos`, inputValue);

    // 컴포넌트가 렌더링되는 조건: 1.state 2.props 3.부모컴포넌트
    // state가 안변해서 렌더링이 안되기때문에
    // 그래서 todos도 바꿔줘야 됨
    // setTodos([...todos, inputValue]);

    // state에서는 db에서 자동으로 생성되는 id값을 알 수 없으니까 다시 DB를 읽어오는 방식이 적합할 수 있음
    fetchTodos();
};

 

DELETE

axios.delete(url[, config])

// 삭제 함수
// 클릭되면 axios를 통해서 삭제가 되어야하기 떄문에 어씽크함수. 몇번째 아이템이 삭제되는지 id를 알려줘야 함
const onDeleteButtonClickHandler = async (id) => {
    axios.delete(`${process.env.REACT_APP_SERVER_URL}/todos/${id}`);

    // 마찬가지로 state도 바꿔서 렌더링되게 해줘야함
    // filter 사용하면 됨
    setTodos(
        todos.filter((item) => {
            return item.id !== id;
        })
    );
};

 

PATCH

axios.patch(url[, data[, config]])

// 수정 함수
const onUpdateButtonClickHandler = async () => {
    // await 왜 안씀?? -> setTodos로 렌더링 시켜줄거라?
    axios.patch(`${process.env.REACT_APP_SERVER_URL}/todos/${targetId}`, {
        title: contents,
    });

    setTodos(
        todos.map((item) => {
            if (item.id == targetId) {
                return { ...item, title: contents };
            } else {
                return item;
            }
        })
    );
};

 

 

 

 


axios interceptor

요청과 응답의 중간부분에서 가로채서 어떠한 역할을 하는게 인터셉터

  • 요청 헤더 추가. request header에 content-type 적용
  • 인증 관리: token 등 인정 관련 로직 적용
  • 로그 관련 로직 삽입
  • 에러 핸들링: 서버 응답 코드에 대한 오류 처리(controller)
  • 통신시작 및 종료에 대한 전역 상태를 관리하여 spinner, progress bar 등 구현 가능

 

(src/axios/api.js)

import axios from 'axios';

// create 인자로 configuration관련 객체가 들어감 (https://axios-http.com/docs/req_config)
// 무슨 인자를 기본으로 url을 달고 호출할꺼냐 baseURL
const instance = axios.create({
    baseURL: process.env.REACT_APP_SERVER_URL,
    // timeout: 1,
});
// timeout 나는 언제까지 기다릴꺼야 하는 초(ms) 단위. 1000이 1초
// timeout: 1 // 강제로 오류내기

// request, response에 적용해보기
// ⭐ 요청할때 content타입에 대해 지정을 하거나, 
// 토큰에 관련된 인증을 삽입하거나, 서버 응답에 대한 오류처리, 
// 통신의 시작과 종료에 대한 전역상태를 관리해서 스피너나 프로그레스바를 관리할 수 있음
// 인터셉터를 적극 활용해서 고도화~

// 인자로 2개의 함수가 항상 들어감
instance.interceptors.request.use(
    // 요청을 보내기 전 수행되는 함수
    // 항상 config라는 인자를 받음
    function (config) {
        console.log('인터셉터 요청 성공!');
        return config;
    },

    // 오류 요청을 보내기 전 수행되는 함수
    function (error) {
        console.log('인터셉터 요청 오류!');
        return Promise.reject(error);
    }
);

instance.interceptors.response.use(
    // 응답을 내보내기 전 수행되는 함수
    function (response) {
        console.log('인터셉터 응답 받았습니다.');
        return response;
    },

    // 오류응답을 내보내기 전 수행되는 함수
    function (error) {
        console.log('인터셉터 응답 오류!');
        return Promise.reject(error);
    }
);

export default instance;

 

(App.jsx)

import { useEffect, useState } from 'react';
import api from './axios/api';

function App() {
    const [todos, setTodos] = useState(null);
    // JSON방식의 디비는 아이디 속성은 자동으로 입력됨. title같은거만 포스트로 보내주면 id는 자동처리 해줌
    const [inputValue, setInputValue] = useState({
        title: '',
    });
    const [targetId, setTargetId] = useState('');
    const [contents, setContents] = useState('');

    // 조회 함수
    const fetchTodos = async () => {
        const { data } = await api.get('/todos');
        // console.log('data', data);
        setTodos(data);
    };

    // 추가 함수
    const onSubmitHandler = async () => {
        // 인터셉터 적용
        await api.post('/todos', inputValue);

        fetchTodos();
    };

    // 삭제 함수
    const onDeleteButtonClickHandler = async (id) => {
        await api.delete(`/todos/${id}`);

        setTodos(
            todos.filter((item) => {
                return item.id !== id;
            })
        );
    };

    // 수정 함수
    const onUpdateButtonClickHandler = async () => {
        await api.patch(`/todos/${targetId}`, {
            title: contents,
        });

        setTodos(
            todos.map((item) => {
                if (item.id == targetId) {
                    return { ...item, title: contents };
                } else {
                    return item;
                }
            })
        );
    };

    useEffect(() => {
        // 1. db로부터 값을 가져올 것이다.
        fetchTodos();
    }, []);

    // 데이터가 비동기이기 때문에 아래 return 부분이 기다려주지 않고 먼저 실행되서 오류가 남. 따라서 옵셔널 체이닝 사용. todos?.map ...
    return (
        <div>
            <section>
                {/* 수정 영역 */}
                <input
                    type="text"
                    placeholder="수정할 아이디"
                    value={targetId}
                    onChange={(e) => {
                        setTargetId(e.target.value);
                    }}
                />
                <input
                    type="text"
                    placeholder="수정할 내용"
                    value={contents}
                    onChange={(e) => {
                        setContents(e.target.value);
                    }}
                />
                {/* 넘겨줄 내용 없으니까 인자로 안쓰고 아래처럼 핸들러를 연결해주면 됨 */}
                <button onClick={onUpdateButtonClickHandler}>수정</button>
            </section>
            <br></br>
            <br></br>
            <section>
                {/* INPUT 영역 */}
                {/* form 태그 안의 button은 기본적으로 submit속성을 가지고 있어서 누르면 새로고침이 됨. 그래서 새로고침을 막기 위해 form태그에서 onSubmit으로 event.preventDefault()처리해서 막아줘야 됨~ */}
                <form
                    onSubmit={(e) => {
                        e.preventDefault();

                        // 버튼 클릭 시, input에 들어있는 값(state)를 이용하여 DB에 저장(post 요청)
                        onSubmitHandler();
                    }}
                >
                    <input
                        type="text"
                        value={inputValue.title}
                        onChange={(e) => {
                            setInputValue({ title: e.target.value });
                        }}
                    />
                    <button>추가</button>
                </form>
            </section>
            <section>
                {/* 데이터 영역 */}
                {todos?.map((item) => {
                    return (
                        <div key={item.id}>
                            {item.id} : {item.title}
                            &nbsp;
                            {/* onClick={onDeleteButtonClickHandler(item.id)}으로 하면 인자로 보낼 때 함수가 실행되어버리기 때문에 함수로 한 번 더  감싸줘야 함 */}
                            <button onClick={() => onDeleteButtonClickHandler(item.id)}>삭제</button>
                        </div>
                    );
                })}
            </section>
        </div>
    );
}

export default App;

 

 

 

 


개발자도구 네트워크탭 확인하기

네트워크 쪽 개발을 할 때는

브라우저의 네트워크 탭에서 어떤 로그가 생기는지 확인하면서 개발을 진행해야 함

 

빨간점 옆에 금지 아이콘 누르면 clear됨

fetch/XHR에서 확인

 

  • headers: URL, 메서드, 상태 코드 등을 확인할 수 있음
  • payload: 우리가 보낸 body를 확인할 수 있음
  • response: 우리가 보낸 요청에 대한 서버의 응답값을 확인할 수 있음. 

 

 

 

 

 

 

 

import { useEffect, useState } from 'react';
import './App.css';
import axios from 'axios';

function App() {
    const [todos, setTodos] = useState(null);
    // JSON방식의 디비는 아이디 속성은 자동으로 입력됨. title같은거만 포스트로 보내주면 id는 자동처리 해줌
    const [inputValue, setInputValue] = useState({
        title: '',
    });
    const [targetId, setTargetId] = useState('');
    const [contents, setContents] = useState('');

    // 조회 함수
    // 2. db로부터 값을 가져오기 위해선 비동기 함수를 만들거. 서버 통신 자체가 비동기 통신
    const fetchTodos = async () => {
        // 응답을 받을 때까지 기다리도록 await 키워드 추가
        // 안에 들은 data 부분만 받아오려고 구조분해할당
        const { data } = await axios.get('http://localhost:4000/todos');
        setTodos(data);
    };

    // 추가 함수
    // 변수이름이 서브밋을 눌렀을때 발생하는 핸들러라는 뜻으로 지어줌
    // post 요청을 하는거니까 async함수
    const onSubmitHandler = async () => {
        // axios.post('http://localhost:4000/todos', inputValue);

        // 환경변수 적용
        // axios.post(`${process.env.REACT_APP_SERVER_URL}/todos`, inputValue);

        // 컴포넌트가 렌더링되는 조건: 1.state 2.props 3.부모컴포넌트
        // state가 안변해서 렌더링이 안되기때문에
        // 그래서 todos도 바꿔줘야 됨
        // setTodos([...todos, inputValue]);

        // state에서는 db에서 자동으로 생성되는 id값을 알 수 없으니까 다시 DB를 읽어오는 방식이 적합할 수 있음
        fetchTodos();
    };

    // 삭제 함수
    // 클릭되면 axios를 통해서 삭제가 되어야하기 떄문에 어씽크함수. 몇번째 아이템이 삭제되는지 id를 알려줘야 함
    const onDeleteButtonClickHandler = async (id) => {
        // await axios.delete(`${process.env.REACT_APP_SERVER_URL}/todos/${id}`);
        await api.delete(`/todos/${id}`);

        // 마찬가지로 state도 바꿔서 렌더링되게 해줘야함
        // filter 사용하면 됨
        setTodos(
            todos.filter((item) => {
                return item.id !== id;
            })
        );
    };

    // 수정 함수
    const onUpdateButtonClickHandler = async () => {
        // await 왜 안씀??
        await api.patch(`/todos/${targetId}`, {
            title: contents,
        });

        setTodos(
            todos.map((item) => {
                if (item.id == targetId) {
                    return { ...item, title: contents };
                } else {
                    return item;
                }
            })
        );
    };

    useEffect(() => {
        // 1. db로부터 값을 가져올 것이다.
        fetchTodos();
    }, []);

    // 데이터가 비동기이기 때문에 아래 return 부분이 기다려주지 않고 먼저 실행되서 오류가 남. 따라서 옵셔널 체이닝 사용. todos?.map ...
    return (
        <div>
            <section>
                {/* 수정 영역 */}
                <input
                    type="text"
                    placeholder="수정할 아이디"
                    value={targetId}
                    onChange={(e) => {
                        setTargetId(e.target.value);
                    }}
                />
                <input
                    type="text"
                    placeholder="수정할 내용"
                    value={contents}
                    onChange={(e) => {
                        setContents(e.target.value);
                    }}
                />
                {/* 넘겨줄 내용 없으니까 인자로 안쓰고 아래처럼 핸들러를 연결해주면 됨 */}
                <button onClick={onUpdateButtonClickHandler}>수정</button>
            </section>
            <br></br>
            <br></br>
            <section>
                {/* INPUT 영역 */}
                {/* form 태그 안의 button은 기본적으로 submit속성을 가지고 있어서 누르면 새로고침이 됨. 그래서 새로고침을 막기 위해 form태그에서 onSubmit으로 event.preventDefault()처리해서 막아줘야 됨~ */}
                <form
                    onSubmit={(e) => {
                        e.preventDefault();

                        // 버튼 클릭 시, input에 들어있는 값(state)를 이용하여 DB에 저장(post 요청)
                        onSubmitHandler();
                    }}
                >
                    <input
                        type="text"
                        value={inputValue.title}
                        onChange={(e) => {
                            setInputValue({ title: e.target.value });
                        }}
                    />
                    <button>추가</button>
                </form>
            </section>
            <section>
                {/* 데이터 영역 */}
                {todos?.map((item) => {
                    return (
                        <div key={item.id}>
                            {item.id} : {item.title}
                            &nbsp;
                            {/* onClick={onDeleteButtonClickHandler(item.id)}으로 하면 인자로 보낼 때 함수가 실행되어버리기 때문에 함수로 한 번 더  감싸줘야 함 */}
                            <button onClick={() => onDeleteButtonClickHandler(item.id)}>삭제</button>
                        </div>
                    );
                })}
            </section>
        </div>
    );
}

export default App;

'React' 카테고리의 다른 글

Custom Hooks  (0) 2023.07.06
Thunk  (0) 2023.07.06
json-server  (0) 2023.07.06
Redux Devtools  (0) 2023.07.06
Redux Toolkit  (0) 2023.07.06