Section4 unit10 과제 2. Proxy를 설정해 HTTP 통신하기

leekoby·2023년 4월 4일
0

CodeStates

목록 보기
6/6
post-thumbnail

🔧변경내용🔨

제목날짜내용
발행일23.04.04

📌들어가기에 앞서


해당 포스트는 Proxy를 학습한 것을 정리한 내용입니다.


Bare Minimum Requirements

백엔드의 개발 서버 역할을 해줄 api와, 프론트엔드의 개발 서버 역할을 해줄 my-app에 각각 접근하여 npm install

cd api
npm install

cd my-app
npm install

백엔드와 프론트엔드 각각 npm install을 할 시, 터미널 각각 따로 열어 npm install을 해줘야 데이터 요청 흐름을 확인하기 편하다.

이어 api에 접근한 터미널에서는 npm run dev를, my-app에 접근한 터미널에서는 npm start를 통해 각기 개발 서버를 연다.

//api terminal
npm run dev
//my-app terminal
npm start

그러면 터미널이 각기 이런 화면이 뜬다.
[그림] 백엔드 API 개발 서버가 구동 중인 터미널

[그림] 프론트엔드 개발 서버가 구동 중인 터미널

이 상태에서 Get all Books 라고 쓰여 있는 버튼을 눌러 봅시다.

CORS 에러가 뜨며 응답을 제대로 받아오지 못하는 것을 확인할 수 있다.

이 단계에서부터 proxy 기능을 사용해 CORS 에러를 해결해보자.

과제1

페어와 함께 webpack dev server의 proxy 기능을 사용해 우회하여 응답을 받아옵니다.

과제2

레포지토리로 받아온 과제를 살펴보면 api2 라는 폴더가 존재하고 있습니다. 실제로 프로젝트 및 실무를 할 때, 하나의 도메인이 아닌 여러 개의 도메인에서 응답을 받아와야 하는 경우가 종종 있습니다. 이럴 때는 유연하게 proxy를 설정할 필요가 있습니다.

페어와 함께 webpack dev server의 proxy 기능 대신 http-proxy-middleware의 proxy 기능을 사용하여 proxy를 유연히 설정해 2개의 도메인에서 모두 응답을 받아옵니다.
페어와 함께 api2에 관련된 fetch 함수를 만들고, 컴포넌트를 하나 이상 만들어 2개의 도메인에서 모두 응답을 받아오는지 테스트 해봅니다.




🌈 1. 과제 1

webpack dev server의 proxy 기능을 사용해 우회하여 응답을 받아오기

package.json

우회할 api주소를 적었다.

...
},
	"proxy" : "http://localhost:3080"
}

BookService.js

그리고 기존의 fetch, 혹은 axios를 통해 요청하던 부분에서 도메인 부분을 제거하여주니 CORS 에러를 해결해 되었다.

export const getAllBooks = async () => {

    const response = await fetch('/api/books');
    return await response.json();
}

export const createBook = async (data) => {
    const response = await fetch('/api/book', {
        method: 'POST',
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify({book: data})
      })
    return await response.json();
}



🌈 2. 과제 2

하나의 도메인이 아닌 여러 개의 도메인에서 응답을 받아와야 하는 경우가 종종 있는데, 이럴 때는 유연하게 proxy를 설정해야 한다.

  • 이번에는 webpack dev server의 proxy 기능 대신 http-proxy-middleware의 proxy 기능을 사용,
  • proxy를 설정해 2개의 도메인에서 모두 응답을 받고,
  • api2에 관련된 fetch 함수를 만들고,
  • 컴포넌트를 하나 이상 만들어 2개의 도메인에서 모두 응답을 받는지 테스트했다.

api2 폴더에도 동일하게 npm installnpm run dev로 실행 시킨다.

[그림] 백엔드 API2 개발 서버가 구동 중인 터미널

package.json

앞서 작성했던 우회할 api주소를 지운다(맨 밑).

....
},

}

setupProxy.js

http-proxy-middleware 라이브러리 설치

npm install http-proxy-middleware --save

그리고 React App의 src 파일 안에서 setupProxy.js 파일을 생성 후 아래와 같이 작성

const { createProxyMiddleware } = require('http-proxy-middleware');

module.exports = function (app) {
  return (app.use(
    "/api",
    createProxyMiddleware({
      target: "http://localhost:3080",
      changeOrigin: true,
    })
  ),
    app.use(
      "/api2",
      createProxyMiddleware({
        target: "http://localhost:3070",
        changeOrigin: true,
      })
    )
  )
};

BookService.js

먼저 api2에 관련된 fetch 함수를 만든다.


export const getAllBooks = async () => {

    const response = await fetch('/api/books');
    return await response.json();
}

export const createBook = async (data) => {
    const response = await fetch('/api/book', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ book: data })
    })
    return await response.json();
}


export const getAllTodos = async () => {

    const response = await fetch('/api2/todos');
    return await response.json();
}

export const createTodo = async (data) => {
    const response = await fetch('/api2/todo', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ book: data })
    })
    return await response.json();
}

CreateTodo.js

CreateBook 컴포넌트를 재활용했다.

const CreateTodo = ({ onChangeForm, handleSubmit }) => {


    return (
        <div className="form-wrapper">
            <div className="form">
                <form>
                    <div className="input-group">
                        <label>todo</label>
                        <input
                            type="text"
                            onChange={(e) => onChangeForm(e)}
                            name="book"
                            placeholder="todo"
                        />
                    </div>
                    <div className="input-group">
                        <label>category</label>
                        <input
                            type="text"
                            onChange={(e) => onChangeForm(e)}
                            name="category"
                            placeholder="category"
                        />
                    </div>
                    <div className="input-group">
                        <label>author</label>
                        <input
                            type="text"
                            onChange={(e) => onChangeForm(e)}
                            name="author"
                            placeholder="author"
                        />
                    </div>
                    <button
                        className="submit-button"
                        onClick={() => handleSubmit()}
                    >Submit
                    </button>
                </form>
            </div>
        </div>
    )
}

export default CreateTodo;

DisplayBoard.js

DisplayBoard에서는 getAllTodo의 버튼이 클릭 될 때 이벤트를 만들었다.

const DisplayBoard = ({ numberOfBooks, getAllBook, numberOfTodos, getAllTodo }) => {

    return (
        <div className="display-wrapper">
            <div className="display-box">
                <div className="display-board">
                    <h4>생성된 book 수</h4>
                    <div className="number">
                        {numberOfBooks}
                    </div>
                </div>
                <div className="display-board">
                    <h4>생성된 todo 수 </h4>
                    <div className="number">
                        {numberOfTodos}
                    </div>
                </div>
                <div className="get-button">
                    <button onClick={() => getAllBook()}>Get all Books</button>
                    <button onClick={() => getAllTodo()}>Get all Todos</button>
                </div>
            </div>
        </div>
    )
}

export default DisplayBoard;

TodoTable.js

BookTable을 재활용했다.

const TodoTable = ({ todos }) => {


    if (todos.length === 0) return null;

    return (
        <div className="table-wrapper">
            <div className="table-box">
                <h2>My Todos</h2>
                <div className="table-scroll">
                    <table>
                        <thead>
                            <tr>
                                <th>Id</th>
                                <th>Todos</th>
                                <th>Category</th>
                                <th>Author</th>
                            </tr>
                        </thead>
                        <tbody>
                            {todos.map((todo, index) => {
                                console.log(todo.todo)
                                return (
                                    <tr key={index} className={index % 2 === 0 ? 'odd' : 'even'}>
                                        <td>{index + 1}</td>
                                        <td>{todo.todo}</td>
                                        <td>{todo.category}</td>
                                        <td>{todo.author}</td>
                                    </tr>
                                )
                            })}
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    )
}

export default TodoTable;

App.js

모든 컴포넌트들을 내려 준다.

import { useState } from 'react';
import './App.css';
import Header from './components/Header';
import BookTable from './components/BookTable';
import TodoTable from './components/TodoTable';
import DisplayBoard from './components/DisplayBoard';
import CreateBook from './components/CreateBook';
import CreateTodo from './components/CreateTodo';
import { getAllBooks, createBook, getAllTodos, createTodo } from './services/BookService';
import Footer from './components/Footer';

function App() {

  const [bookShelf, setBookShelf] = useState({});
  const [myTodo, setMyTodo] = useState([]);
  const [books, setBooks] = useState([]);
  const [todos, setTodos] = useState([])
  const [numberOfBooks, setNumberBooks] = useState(0);
  const [numberOfTodos, setNumberTodos] = useState(0);

  const handleSubmit = () => {
    createBook(bookShelf)
      .then(() => {
        setNumberBooks(numberOfBooks + 1);
      });
  }
  const handleTodoSubmit = () => {
    createTodo(myTodo)
      .then(() => {
        setNumberTodos(numberOfTodos + 1);
      });

  }

  const getAllBook = () => {
    getAllBooks()
      .then(data => {
        setBooks(data);
        setNumberBooks(data.length);
      });
  }

  const getAllTodo = () => {
    getAllTodos()
      .then(data => {
        setTodos(data);
        setNumberTodos(data.length);
      });
  }


  const handleOnChangeForm = (e) => {
    let inputData = bookShelf;
    if (e.target.name === 'book') {
      bookShelf.book = e.target.value;
    } else if (e.target.name === 'category') {
      bookShelf.category = e.target.value;
    } else if (e.target.name === 'author') {
      bookShelf.author = e.target.value;
    }
    setBookShelf(inputData);
  }

  const handleOnChangeTodoForm = (e) => {
    let inputData = myTodo;
    if (e.target.name === 'todo') {
      myTodo.todo = e.target.value;
    } else if (e.target.name === 'category') {
      myTodo.category = e.target.value;
    } else if (e.target.name === 'author') {
      myTodo.author = e.target.value;
    }
    setMyTodo(inputData);
  }

  return (
    <div className="main-wrapper">
      <div className="main">
        <Header />
        <CreateBook
          bookShelf={bookShelf}
          onChangeForm={handleOnChangeForm}
          handleSubmit={handleSubmit}
        />
        <CreateTodo
          myTodo={myTodo}
          onChangeForm={handleOnChangeTodoForm}
          handleSubmit={handleTodoSubmit}
        />
        <DisplayBoard
          numberOfBooks={numberOfBooks}
          getAllBook={getAllBook}
          getAllTodo={getAllTodo}
          numberOfTodos={numberOfTodos}
        />
        <BookTable books={books} />
        <TodoTable todos={todos} />
        <Footer />
      </div>
    </div>
  );
}

export default App;



📚 레퍼런스

(블로깅) CORS 에러 해결과 Proxy

React환경의 Multi-Proxy 구현

CORS에러 해결 방법 : CRA에서 Proxy 설정하기

리액트에서 프록시 여러 개 설정하는 방법

0개의 댓글