본문 바로가기

Libraries/React

[React] 리액트로 서버에 데이터 요청하는 방법 (fetch, axios.then, async/await 사용하기)

728x90

 

   목차 

1. 서버 만들기 
2. React 설치하기 
3. 서버에 대이터 요청하는 방법 2가지
4. fetch로 통신하기 
5. 라이브러리(axios)를 사용해서 통신하기 
6. async/await 이용하기
7.  async/await과 axios.then() 차이점 

 

 

 

 

리액트로 서버에 데이터를 요청하는 방법을 알아보자!

 

 

 


 

 

 

 1. 서버 만들기 

 

서버에 데이트를 요청하려면 서버가 있어야한다.

이해를 위해 직접 서버를 만들어보자! (express를 이용할 것이다)

 

 1. 폴더 기본 구성하기 

 

폴더 기본 구성은 아래와 같다.

 

 

 

 

2 . 터미널에서 server 폴더로 이동한다. 

 

 

 

 

 3. node.js 프로젝트를 하니까 npm init을 해서 package.json을 만들어준다. 

 

 

 

 

 4. npm i express를 해서 익스프레스를 설치해준다. 

 

 

 

* 참고) 'Express'란?

 

Express는 Node.js를 위한 빠르고 유연한 웹 애플리케이션 프레임워크이다.
Express를 사용하면 Node.js를 이용하여 웹 서버를 간단하고 쉽게 구축할 수 있다.

Express는 다음과 같은 기능을 제공한다.

 

1) 라우팅(Routing): URL 경로와 HTTP 요청 메소드(GET, POST, PUT, DELETE 등)에 따라 서버의 특정 엔드포인트로 요청을 보내는 기능을 관리한다.

 

2) 미들웨어(Middleware): 요청과 응답 사이에 실행되는 함수이다. 이를 통해 요청 및 응답을 변경하거나 처리할 수 있다. Express의 핵심 기능 중 하나이다.

3) 템플릿 엔진 지원: Express는 템플릿 엔진을 지원하여 서버 측에서 동적으로 HTML을 생성할 수 있다. 많은 템플릿 엔진 중에는 Pug, EJS 등이 있다.

 

4) 정적 파일 제공: Express를 사용하여 정적 파일(이미지, CSS, JavaScript 등)을 제공할 수 있다.

 

5) 세션 및 쿠키 관리: 사용자의 세션 상태를 유지하고 인증 및 권한 부여를 처리할 수 있다.

 

6) 미들웨어 및 라우팅 모듈화: Express는 미들웨어 및 라우팅을 모듈화하여 관리할 수 있다. 이를 통해 코드를 모듈화하고 유지보수성을 향상시킬 수 있다.

이러한 기능들을 통해 Express는 Node.js 기반의 웹 애플리케이션을 빠르고 효율적으로 개발할 수 있도록 도와준다.

 

 

 

 5. server 폴더 안에 app.js (서버파일)을 만들어준다. 

 

 

 

 

 6. app.js에 express 서버 코드를 짤건데, npm 홈페이지에서 express를 검색해서 샘플 코드를 복붙한다. 

 

app.js

const express = require('express')
const app = express()

app.get('/', function (req, res) {
  res.send('Hello World')
})

app.listen(3000)

 

 

해당 주소 링크

https://www.npmjs.com/package/express

 

express

Fast, unopinionated, minimalist web framework. Latest version: 5.0.0-beta.2, last published: 5 hours ago. Start using express in your project by running `npm i express`. There are 78125 other projects in the npm registry using express.

www.npmjs.com

 

 

 

 7. 서버 실행됐을 때 콜백함수로 실행할 함수를 넣어줘서 잘 실행되는지 확인할 수 있게 console.log를 찍게 한다. 

 

app.js

const express = require("express");
const app = express();

app.get("/", function (req, res) {
  res.send("Hello World");
});

app.listen(3000, () => {
  console.log("server start!");
});

 

 

 

 8. 서버 실행시켜보기 

 

 

주소창에 http://localhost:3000을 쳐서 들어가면 "Hello World"가 잘 출력되는 것을 볼 수 있다. → 서버가 잘 실행되고있다는 뜻!

 

 

 

 

 9. TODO List API 서버 만들기 

 

app.js

const express = require("express");
const app = express();

let id = 2;

const todoList = [
  {
    id: 1,
    text: "할일 1",
    done: false,
  },
];

app.get("/", function (req, res) {
  res.send("Hello World");
});

app.get("/api/todo", () => {
  return todoList;
});

app.post("/api/todo", () => {
  const { text, done } = req.body;
});

app.listen(3000, () => {
  console.log("server start!");
});

 

 

 

 10. 익스프레스에서 body에서 데이터를 꺼내 쓰려면 body-parser가 필요하다. 

 

 body-parser는 express에서 기본적으로 지원하는 기능이다. 아까 봤던 express 홈페이지에 가서 찾아본다.

app.use(express.json()); 
app.use(express.urlencoded({ extended: true }));

 

두줄을 추가하고,

꺼내온 데이터를 넣어주고 응답을 보내면 api가 완성된다!

 

 

app.js

const express = require("express");
const app = express();

app.use(express.json()); // body-parser
app.use(express.urlencoded({ extended: true })); 

let id = 2;

const todoList = [
  {
    id: 1,
    text: "할일 1",
    done: false,
  },
];

app.get("/api/todo", (req,res) => {
  res.json(todoList);
});

app.post("/api/todo", (req, res) => {
  const { text, done } = req.body;
  todoList.push({ // 데이터 넣어주기
    id: id++,
    text,
    done,
  });
  return res.send("success");
});

app.listen(3000, () => {
  console.log("server start!");
});

 

 

 

 11. 주소창에 http://localhost:3000/api/todo 를 치고 들어가면 데이터가 잘 넘어간 것을 볼 수 있다. 

 

 

 

해당 주소 링크

https://expressjs.com/en/5x/api.html#req.body

 

Express 5.x - API Reference

Express 5.x API Note: This is early beta documentation that may be incomplete and is still under development. express() Creates an Express application. The express() function is a top-level function exported by the express module. const express = require('

expressjs.com

 

 


 

 

 2. React 설치하기 

 

 1. client 폴더로 이동해서 React를 시작한다. 

 

 

 

 

 2. css와 test 파일 같은 불필요한 파일을 삭제하고, App.js에 최소한의 내용만 남겨둔다. 

 

App.js

function App() {
  return <div className="App">
    <h1>TODO LIST</h1>
  </div>;
}

export default App;

 

 

 


 

 

 3. 서버에 데이터 요청하는 방법 2가지 

 

서버에 데이터 요청하는 방법에는  2가지가 있다. 

1. api 사용 ▶ fetch

2. 라이브러리 사용 ▶ axios

 

위 순서대로 2가지 방법을 살펴볼 것이다!

 

 


 

 

 4. fetch로 통신하기 

 

fetch를 사용하면, 설치할 것 없이 간편하게 이용할 수 있다는 장점이 있다.

 

 1. fetch 통신을 위해 필요한 것은 '서버주소'와 'http method'이다. 

 

App.js

function App() {
  fetch('http://localhost:4000/api/todo')
  .then((response)=>response.json())
  .then((data)=> console.log(data));
  return <div className="App">
    <h1>TODO LIST</h1>
  </div>;
}

export default App;

 

 

app.js 는 4000번으로 바꿔준다. 

app.listen(4000, () => {
  console.log("server start!!");
});

 

 

fetch/server 경로로 들어가서 node app.js로 서버를 키고,

 

 

fetch/client 경로로 들어가서 npm start로 서버를 실행시킨다. 

 

통신 잘 되는지 확인하는 법 : 개발자 도구 Network 탭 들어가기 → 새로고침하기 → CORS 에러가 뜬다!

 

 

 

 2. CORS 에러 해결하기 

 

Client : localhost:3000 (리액트 개발 서버) 
Server : localhost:4000 

 

origin이 다르다. ('origin'은 host와 port를 포함한 데이터의 출처를 뜻한다.)
데이터의 출처가 다름! → 서버가 해결해야함!

서버에서 cors 정책을 허용하면 origin이 달라도 꺼내갈수있게된다.

따라서 cors를 설치한다. (npm install cors)

 

 

 

해결하면 콘솔에 뜸 (데이터 받아오기 성공!)

 

 

 

 

 3. 화면에 띄어보기 

 

App.js

import { useState } from "react";

function App() {
  const [todoList, setTodoList] = useState(null);
  fetch("http://localhost:4000/api/todo")
    .then((response) => response.json())
    .then((data) => setTodoList(data));
  return (
    <div className="App">
      <h1>TODO LIST</h1>
    </div>
  );
}

export default App;

 

 

이렇게 하면 계속 렌더링 돼서 아래와 같이 무한루프를 돌기 시작한다.

 

 

 

 

 4. 처음 컴포넌트 렌더링 될때만 실행되게 하기 → useEffect 사용! 

 

import React, { useEffect, useState } from "react";

function App() {
  const [todoList, setTodoList] = useState(null);
  
  useEffect(() => {
    fetch("http://localhost:4000/api/todo")
    .then((response) => response.json())
    .then((data) => setTodoList(data));
  }, []);
  
  return (
    <div className="App">
      <h1>TODO LIST</h1>
      {todoList === null ? ( // todoList가 null인 경우 "Loading..."이라는 메시지를 보여주고, 그렇지 않은 경우에만 todoList를 매핑하여 표시
        <div>Loading...</div>
      ) : (
        todoList.map((todo) => (
          <div key={todo.id}>
            <div>{todo.id}</div>
            <div>{todo.text}</div>
            <div>{todo.done ? "Y" : "N"}</div>
          </div>
        ))
      )}
    </div>
  );
}

export default App;

 

 

 

 

 5. POST 메서드로 데이터 추가하기 

 

import React, { useEffect, useState } from "react";

function App() {
  const [todoList, setTodoList] = useState(null);

  useEffect(() => {
    fetch("http://localhost:4000/api/todo")
      .then((response) => response.json())
      .then((data) => setTodoList(data));
  }, []);

  const onSubmitHandler = (e) => {
    e.preventDefault(); // submit 기본 동작 막기

    const text = e.target.text.value;
    const done = e.target.done.checked;

    fetch("http://localhost:4000/api/todo", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        text,
        done,
      }),
    }).then(() =>
      fetch("http://localhost:4000/api/todo")
        .then((response) => response.json())
        .then((data) => setTodoList(data))
    );
  };

  return (
    <div className="App">
      <h1>TODO LIST</h1>
      <form onSubmit={onSubmitHandler}>
        <input name="text"></input>
        <input name="done" type="checkbox"></input>
        <button type="submit" value="추가">
          추가
        </button>
      </form>

      {todoList === null ? (
        <div>Loading...</div>
      ) : (
        todoList.map((todo) => (
          <div key={todo.id}>
            <div>{todo.id}</div>
            <div>{todo.text}</div>
            <div>{todo.done ? "Y" : "N"}</div>
          </div>
        ))
      )}
    </div>
  );
}

export default App;

 

 

 

 

 6. 중복 코드를 없애기 

 

1) fetch...then 부분을 fetchData로 따로 빼기

2) 서버 url를 SERVER_URL로 따로 빼기

 

import React, { useEffect, useState } from "react";
const SERVER_URL = "http://localhost:4000/api/todo"; // 변수에 담기

function App() {
  const [todoList, setTodoList] = useState(null);

    // 함수로 만들기
  const fetchData = () => {
    fetch(SERVER_URL) // 여기서 쓰기
      .then((response) => response.json())
      .then((data) => setTodoList(data));
  };

  useEffect(() => {
    fetchData(); // 여기서 쓰기
  }, []);

  const onSubmitHandler = (e) => {
    e.preventDefault();

    const text = e.target.text.value;
    const done = e.target.done.checked;

    fetch(SERVER_URL, { // 여기서 쓰기
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        text,
        done,
      }),
    }).then(() =>
    fetchData() // 여기서 쓰기
    );
  };

  return (
    <div className="App">
      <h1>TODO LIST</h1>
      <form onSubmit={onSubmitHandler}>
        <input name="text"></input>
        <input name="done" type="checkbox"></input>
        <button type="submit" value="추가">
          추가
        </button>
      </form>

      {todoList === null ? (
        <div>Loading...</div>
      ) : (
        todoList.map((todo) => (
          <div key={todo.id}>
            <div>{todo.id}</div>
            <div>{todo.text}</div>
            <div>{todo.done ? "Y" : "N"}</div>
          </div>
        ))
      )}
    </div>
  );
}

export default App;

 

 


 

 

 5. 라이브러리(axios)를 사용해서 통신하기 

 

 1. axios 라이브러리 설치하기 

 

npm i axios

 

 

 

 

 2. axois를  import하기 

 

import axios from 'axios'

 

 

 

 3. axios로 바꿔보고, 비교를 위해 fetch를 주석처리한다. 

 

import axios from "axios";
import React, { useEffect, useState } from "react";
const SERVER_URL = "http://localhost:4000/api/todo";

function App() {
  const [todoList, setTodoList] = useState(null);

  const fetchData = () => {
    axios
      .get(SERVER_URL)
      .then((response) => {
      setTodoList(response.data);
    });

    // fetch(SERVER_URL)
    //   .then((response) => response.json())
    //   .then((data) => setTodoList(data));
  };

  useEffect(() => {
    fetchData();
  }, []);

  const onSubmitHandler = (e) => {
    e.preventDefault();

    const text = e.target.text.value;
    const done = e.target.done.checked;

    axios
      .post(SERVER_URL, {
        text,
        done,
      })
      .then(() => {
        fetchData();
      });

    // fetch(SERVER_URL, {
    //   method: "POST",
    //   headers: {
    //     "Content-Type": "application/json",
    //   },
    //   body: JSON.stringify({
    //     text,
    //     done,
    //   }),
    // }).then(() =>
    // fetchData()
    // );
  };

  return (
    <div className="App">
      <h1>TODO LIST</h1>
      <form onSubmit={onSubmitHandler}>
        <input name="text"></input>
        <input name="done" type="checkbox"></input>
        <button type="submit" value="추가">
          추가
        </button>
      </form>

      {todoList === null ? (
        <div>Loading...</div>
      ) : (
        todoList.map((todo) => (
          <div key={todo.id}>
            <div>{todo.id}</div>
            <div>{todo.text}</div>
            <div>{todo.done ? "Y" : "N"}</div>
          </div>
        ))
      )}
    </div>
  );
}

export default App;

 

 


 

 

 6. async/await  사용하기 

 

import axios from "axios";
import React, { useEffect, useState } from "react";
const SERVER_URL = "http://localhost:4000/api/todo";

function App() {
  const [todoList, setTodoList] = useState(null);

  const fetchData = async () => {
    const response = await axios.get(SERVER_URL);
    setTodoList(response.data);
  };

  // const fetchData = () => {
  //   axios.get(SERVER_URL).then((response) => {
  //     setTodoList(response.data);
  //   });
  // };

  useEffect(() => {
    fetchData();
  }, []);

  const onSubmitHandler = async (e) => {
    e.preventDefault(); 

    const text = e.target.text.value;
    const done = e.target.done.checked;

    await axios.post(SERVER_URL, {text, done});
    fetchData();
  };

  // const onSubmitHandler = (e) => {
  //   e.preventDefault();

  //   const text = e.target.text.value;
  //   const done = e.target.done.checked;

  //   axios
  //     .post(SERVER_URL, {
  //       text,
  //       done,
  //     })
  //     .then(() => {
  //       fetchData();
  //     });
  // };

  return (
    <div className="App">
      <h1>TODO LIST</h1>
      <form onSubmit={onSubmitHandler}>
        <input name="text"></input>
        <input name="done" type="checkbox"></input>
        <button type="submit" value="추가">
          추가
        </button>
      </form>

      {todoList === null ? (
        <div>Loading...</div>
      ) : (
        todoList.map((todo) => (
          <div key={todo.id}>
            <div>{todo.id}</div>
            <div>{todo.text}</div>
            <div>{todo.done ? "Y" : "N"}</div>
          </div>
        ))
      )}
    </div>
  );
}

export default App;

 

 

위 코드에서 주석 뺀 버전

import axios from "axios";
import React, { useEffect, useState } from "react";
const SERVER_URL = "http://localhost:4000/api/todo";

function App() {
  const [todoList, setTodoList] = useState(null);

  const fetchData = async () => {
    const response = await axios.get(SERVER_URL);
    setTodoList(response.data);
  };

  useEffect(() => {
    fetchData();
  }, []);

  const onSubmitHandler = async (e) => {
    e.preventDefault();

    const text = e.target.text.value;
    const done = e.target.done.checked;

    await axios.post(SERVER_URL, { text, done });
    fetchData();
  };

  return (
    <div className="App">
      <h1>TODO LIST</h1>
      <form onSubmit={onSubmitHandler}>
        <input name="text"></input>
        <input name="done" type="checkbox"></input>
        <button type="submit" value="추가">
          추가
        </button>
      </form>

      {todoList === null ? (
        <div>Loading...</div>
      ) : (
        todoList.map((todo) => (
          <div key={todo.id}>
            <div>{todo.id}</div>
            <div>{todo.text}</div>
            <div>{todo.done ? "Y" : "N"}</div>
          </div>
        ))
      )}
    </div>
  );
}

export default App;

 

 


 

 

 7. async/awaitaxios.then() 차이점 

 

async/await axios.then()은 모두 비동기 코드를 작성하는 방법이지만, 몇 가지 차이점이 있다.

 

1. 문법적 차이점

- async/await: async 키워드를 함수 앞에 붙이고, 비동기적으로 처리되어야 하는 부분을 await 키워드와 함께 사용하여 표현한다. 이는 비동기 코드를 보다 동기적으로 보이도록 만들어준다.

- axios.then(): Promise 객체의 .then() 메서드를 사용하여 비동기 작업을 처리한다. 이는 ES6에서 도입된 Promise를 기반으로 한다.

 

2. 가독성

- async/await: 코드를 보다 동기적으로 보이게 하고, 비동기 작업의 흐름을 보다 명확하게 파악할 수 있다.

- axios.then(): 비동기 작업의 흐름이 .then() 메서드 체인으로 연결되어 있어, 가독성이 낮을 수 있다.

 

3. 에러 처리

- async/await: try...catch 블록을 사용하여 에러 처리를 간편하게 할 수 있다. 함수 내에서 발생한 에러를 캐치하고 처리할 수 있다.

- axios.then(): 각 .then() 메서드 체인에서 발생한 에러를 따로 처리해주어야 한다.

 

4. 세부 제어

- async/await: 비동기 작업을 보다 세밀하게 제어할 수 있다. 예를 들어 여러 개의 비동기 작업을 병렬로 실행하고 결과를 기다릴 수 있다.

- axios.then(): .then() 메서드 체인을 통해 비동기 작업의 순서를 보장하고, 각각의 작업 결과를 처리할 수 있다.

 

요약하면, async/await은 코드를 보다 명확하게 만들어주고, 에러 처리를 간편하게 해준다. 반면에 axios.then()은 Promise를 직접 다루는 방식으로, 보다 세밀한 제어가 필요한 경우에 유용하다.

 

 

728x90