본문 바로가기
부트캠프 개발일지 2023-2024/React 리액트

[9주차] 리액트심화: interceptor, instance, .env

by whereanna00 2023. 11. 29.

interceptor란?

inter 중간에서

cepter 가로채는

HTTP 통신을 할 때, 즉 요청을 하고 응답을 하는 그 중간에서 사이에서 뭔가를 가로채서 어떠한 것을 한다는 개념

 


interceptor의 필요성

axios.get("http://localhost:3001/todos");
axios.post("http://localhost:3001/todos", todo);
axios.delete(`http://localhost:3001/todos/${todoId}`);

 

위와 같이 호출하는 부분이 몇백개가 된다고 했을 때, 포트를 변경해야하는 경우 일일이 모든 주소를 바꾸는 것은 time-consuming 할 것이다. 또는, 요청할 때마다 console.log를 통해 로깅을 하려고 할 때도 몇백개의 console.log를 적는 것은 인적리소스 낭비이다.

 

 

그래서

interceptor는 요청과 응답 가운데에 껴서

[출처 : https://javascript.plainenglish.io/how-to-implement-a-request-interceptor-like-axios-896a1431304a]

 

(1) 요청(request)가 처리되기 전 http request가 서버에 전달되기전

(2) 응답(response)의 then(성공) 또는 catch(실패)가 처리되기 전 

으로 프로세스를 나누고, 개발자가 그 중간단계에서 코드에 관여할 수 있게 해준다.

 

 

이외에도

  • 요청 헤더 추가
  • 인증 관리
  • 로그 관련 로직 삽입
  • 에러 핸들링

에 큰 도움이 된다.

 


실습1

이 코드에서 시작됩니다.

 

GitHub - dancinncoder/axios-GPDP

Contribute to dancinncoder/axios-GPDP development by creating an account on GitHub.

github.com

 

0. 리펙토링 개념으로 .env 파일 만들기 

0-1. root scope에 .env 파일 만들기

0-2. 변수를 만들어 서버 주소 넣기

// .env

REAT_APP_SERVER_URL = http://localhost:3001

 

BEFORE

 // 조회 함수
  const fetchTodos = async() => {
    const {data} = await axios.get('http://localhost:3001/todos');
    console.log('response', data);
    setTodos(data);
  }

 

 

AFTER

  // 조회 함수
  const fetchTodos = async() => {
    const {data} = await axios.get(`${process.env.REACT_APP_SERVER_URL}/todos`);
    console.log('response', data);
    setTodos(data);
  }

 

server를 끄고 다시 실행시켜보면, 데이터가 잘 들어온것을 확인할 수 있다. 이제 나머지 URL도 모두 바꿔준다.

더보기
import axios from "axios";
import { useEffect, useState } from "react";

function App() {
  const [todos, setTodos] = useState(null);
  const [inputValue, setInputValue] = useState({
    title: '',
  });
  const [editedId, setEditedId] = useState('');
  const [editedContent, setEditedContent] = useState('');

  // 조회 함수
  const fetchTodos = async() => {
    const {data} = await axios.get(`${process.env.REACT_APP_SERVER_URL}/todos`);
    console.log('response', data);
    setTodos(data);
  }

  // 추가 함수
  const onSubmitHandler = async() => {
    await axios.post('http://localhost:3001/todos', inputValue);
    // 컴포넌트에도 랜더링되게 하기 (state 도 바꾸기)
    // setTodos([...todos, inputValue ]);
    fetchTodos();
  }


  // 삭제 함수
  const onDeleteHandler = async(id) => {
    await axios.delete(`http://localhost:3001/todos/${id}`);
    setTodos(todos.filter((item)=> item.id !== id));
  }

  useEffect (() => {
    //db로부터 값을 가져오기
    fetchTodos();
  }, [])

  // 수정함수
  const onUpdatedHandler = async() => {
    await axios.patch(`http://localhost:3001/todos/${editedId}`,{
      title: editedContent,
    })
    setTodos(todos.map((item)=>{
      if(item.id == editedId){
        return {...item, title: editedContent}
      } else {
        return item;
      }
      })
    );
  }

  return (
    <div>
      {/* 수정 영역 */}
      <form>
        <input value={editedId} type="text" placeholder="수정할 아이디" onChange={(e) => {
          setEditedId(e.target.value);
        }}/>
        <input value={editedContent} type="text" placeholder="수정할 내용" onChange={(e)=> {
          setEditedContent(e.target.value);
        }}/>
        <button onClick={()=> onUpdatedHandler(editedId, editedContent)}>수정반영하기</button>
      </form>
      {/* input 영역 */}
      <div>
        <form onSubmit={(e)=>{
          e.preventDefault();
          // 버튼 클릭 시, input에 들어있는 값(state)를 이용하여 DB에 저장(post 요청)
          onSubmitHandler();
          setInputValue("");
        }}>
          <input value={inputValue.title} onChange={(ev)=>{
            setInputValue({ title: ev.target.value});
          }} type="text" />
          <button type="submit">추가</button>
        </form>
      </div>
      {/* 데이터 영역 */}
      <div>
        {todos?.map((item)=> {
          return(
            <div key={item.id}>
              {item.id} : {item.title} &nbsp; <button onClick={()=> onDeleteHandler(item.id)}>삭제</button>
            </div>
          );
        })}
      </div>
    </div>
  );
}

export default App;

 

전체코드

더보기
import axios from "axios";
import { useEffect, useState } from "react";

function App() {
  const [todos, setTodos] = useState(null);
  const [inputValue, setInputValue] = useState({
    title: '',
  });
  const [editedId, setEditedId] = useState('');
  const [editedContent, setEditedContent] = useState('');

  // 조회 함수
  const fetchTodos = async() => {
    const {data} = await axios.get(`${process.env.REACT_APP_SERVER_URL}/todos`);
    console.log('response', data);
    setTodos(data);
  }

  // 추가 함수
  const onSubmitHandler = async() => {
    await axios.post(`${process.env.REACT_APP_SERVER_URL}/todos`, inputValue);
    // 컴포넌트에도 랜더링되게 하기 (state 도 바꾸기)
    // setTodos([...todos, inputValue ]);
    fetchTodos();
  }


  // 삭제 함수
  const onDeleteHandler = async(id) => {
    await axios.delete(`${process.env.REACT_APP_SERVER_URL}/todos/${id}`);
    setTodos(todos.filter((item)=> item.id !== id));
  }

  useEffect (() => {
    //db로부터 값을 가져오기
    fetchTodos();
  }, [])

  // 수정함수
  const onUpdatedHandler = async() => {
    await axios.patch(`${process.env.REACT_APP_SERVER_URL}/todos/${editedId}`,{
      title: editedContent,
    })
    setTodos(todos.map((item)=>{
      if(item.id == editedId){
        return {...item, title: editedContent}
      } else {
        return item;
      }
      })
    );
  }

  return (
    <div>
      {/* 수정 영역 */}
      <form>
        <input value={editedId} type="text" placeholder="수정할 아이디" onChange={(e) => {
          setEditedId(e.target.value);
        }}/>
        <input value={editedContent} type="text" placeholder="수정할 내용" onChange={(e)=> {
          setEditedContent(e.target.value);
        }}/>
        <button onClick={()=> onUpdatedHandler(editedId, editedContent)}>수정반영하기</button>
      </form>
      {/* input 영역 */}
      <div>
        <form onSubmit={(e)=>{
          e.preventDefault();
          // 버튼 클릭 시, input에 들어있는 값(state)를 이용하여 DB에 저장(post 요청)
          onSubmitHandler();
          setInputValue("");
        }}>
          <input value={inputValue.title} onChange={(ev)=>{
            setInputValue({ title: ev.target.value});
          }} type="text" />
          <button type="submit">추가</button>
        </form>
      </div>
      {/* 데이터 영역 */}
      <div>
        {todos?.map((item)=> {
          return(
            <div key={item.id}>
              {item.id} : {item.title} &nbsp; <button onClick={()=> onDeleteHandler(item.id)}>삭제</button>
            </div>
          );
        })}
      </div>
    </div>
  );
}

export default App;

 

 

 

1. instance 만들기, baseURL 설정하기

1-1. src > axios > api.js 만들기

1-2. 우리가 직접 만드는 instance 만들기

const data = axios.get("http://localhost:4000/");

 

기존 axios.get~ 방식은 순수 axios로 custom 설정이 되어있지 않은 instance(=axios)이다.

 

이제 api.js 파일에

import axios from "axios";

// 인자로 configuration 객체가 들어간다.
const instance = axios.create({
  baseURL: process.env.REACT_APP_SERVER_URL,
});

export default instance;

 

 

1-3. App.jsx 에서 api import 하기

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

 

1-4. axios 를 api로 바꾸기

1-5. ${process.env.REACT_APP_SERVER_URL} 를 지우기

 

결과코드

더보기
// import axios from "axios";
import { useEffect, useState } from "react";
import api from './axios/api';

function App() {
  const [todos, setTodos] = useState(null);
  const [inputValue, setInputValue] = useState({
    title: '',
  });
  const [editedId, setEditedId] = useState('');
  const [editedContent, setEditedContent] = useState('');

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

  // 추가 함수
  const onSubmitHandler = async() => {
    await api.post(`/todos`, inputValue);
    // 컴포넌트에도 랜더링되게 하기 (state 도 바꾸기)
    // setTodos([...todos, inputValue ]);
    fetchTodos();
  }


  // 삭제 함수
  const onDeleteHandler = async(id) => {
    await api.delete(`/todos/${id}`);
    setTodos(todos.filter((item)=> item.id !== id));
  }

  useEffect (() => {
    //db로부터 값을 가져오기
    fetchTodos();
  }, [])

  // 수정함수
  const onUpdatedHandler = async() => {
    await api.patch(`/todos/${editedId}`,{
      title: editedContent,
    })
    setTodos(todos.map((item)=>{
      if(item.id == editedId){
        return {...item, title: editedContent}
      } else {
        return item;
      }
      })
    );
  }

  return (
    <div>
      {/* 수정 영역 */}
      <form>
        <input value={editedId} type="text" placeholder="수정할 아이디" onChange={(e) => {
          setEditedId(e.target.value);
        }}/>
        <input value={editedContent} type="text" placeholder="수정할 내용" onChange={(e)=> {
          setEditedContent(e.target.value);
        }}/>
        <button onClick={()=> onUpdatedHandler(editedId, editedContent)}>수정반영하기</button>
      </form>
      {/* input 영역 */}
      <div>
        <form onSubmit={(e)=>{
          e.preventDefault();
          // 버튼 클릭 시, input에 들어있는 값(state)를 이용하여 DB에 저장(post 요청)
          onSubmitHandler();
          setInputValue("");
        }}>
          <input value={inputValue.title} onChange={(ev)=>{
            setInputValue({ title: ev.target.value});
          }} type="text" />
          <button type="submit">추가</button>
        </form>
      </div>
      {/* 데이터 영역 */}
      <div>
        {todos?.map((item)=> {
          return(
            <div key={item.id}>
              {item.id} : {item.title} &nbsp; <button onClick={()=> onDeleteHandler(item.id)}>삭제</button>
            </div>
          );
        })}
      </div>
    </div>
  );
}

export default App;

 

따라서 앞으로 포트위치를 바꿔주고 싶다면, env에서만 바꿔주면 된다!

 

 

GitHub - dancinncoder/axios-GPDP

Contribute to dancinncoder/axios-GPDP development by creating an account on GitHub.

github.com

 


실습2

request, response 사이에서 코드에 관여하기

이 파일에서 시작합니다.

 

1. 기본 포맷

//api.js

import axios from "axios";

// 인자로 configuration 객체가 들어간다.
const instance = axios.create({
  baseURL: process.env.REACT_APP_SERVER_URL,
});

instance.interceptors.request.use(
  //요청을 보내기 전에 수행되는 함수
  function(){},
  //오류 요청을 보내기 전에 수행되는 함수
  function(){}
)

instance.interceptors.response.use(
  //응답을 내보내기 전 수행되는 함수
  function(){},
  //오류응답을 내보내기 전 수행되는 함수
  function(){}
)

export default instance;

 

2. 채우기

import axios from "axios";

// 인자로 configuration 객체가 들어간다.
const instance = axios.create({
  baseURL: process.env.REACT_APP_SERVER_URL,
});

instance.interceptors.request.use(
  //요청을 보내기 전에 수행되는 함수
  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;

 

서버가 갑자기 안되서 해결해야함.

error Command failed with exit code 1.
728x90
반응형