on my way

React에서 Axios를 이용한 REST API 호출 및 비동기 프로그래밍 이해하기 본문

Computer Science/React

React에서 Axios를 이용한 REST API 호출 및 비동기 프로그래밍 이해하기

wingbeat 2024. 7. 16. 18:38

API 테스트

API란?

API는 애플리케이션 프로그래밍 인터페이스(Application Programming Interface)의 약자로, 프로그램이나 애플리케이션 간에 데이터를 주고받는 방법을 말해요.

쉽게 말하면, API는 두 프로그램이 서로 대화할 수 있게 해주는 통로라고 할 수 있어요.

 

예를 들어, 당신이 좋아하는 앱에서 날씨 정보를 보여준다고 생각해보세요.

그 앱이 직접 날씨 데이터를 수집하지 않고, 다른 날씨 서비스에서 데이터를 가져온다고 할 때, 이때 API를 통해 데이터를 가져옵니다.

 

라이브러리는 코드의 모음으로, 프로그램에서 특정 기능을 쉽게 사용할 수 있게 도와줘요.

예를 들어, 어떤 수학 문제를 풀어야 하는 프로그램을 만든다면, 복잡한 수학 계산을 미리 정의해둔 라이브러리를 가져와서 사용할 수 있어요.

 

요약하자면:

  • API: 서로 다른 프로그램이 데이터를 주고받는 방법이나 규칙.
  • 라이브러리: 특정 기능을 쉽게 사용할 수 있게 모아둔 코드의 모음.

API는 프로그램들이 서로 데이터를 주고받게 해주고, 라이브러리는 프로그램에 필요한 기능을 쉽게 사용할 수 있게 도와줘요.

 

Axios란?

Axios는 HTTP 요청을 보낼 때 사용하는 JavaScript 라이브러리입니다.

Axios를 사용하면 GET, POST, PUT, DELETE 요청을 쉽게 보낼 수 있으며, Promise 기반으로 비동기 처리를 지원합니다.

 

React에서 API 호출하기

React에서는 Axios를 사용하여 API를 호출할 수 있습니다.

다음은 예시 코드입니다. 이 코드는 JSONPlaceholder라는 온라인 REST API에서 포스트 데이터를 가져옵니다.

 

Api1.js

import axios from "axios";
import { useState } from "react";

const Api1 = () => {
  const [data, setData] = useState([]); // state 초기화

  // API 호출 함수
  const callApi = () => {
    axios.get("https://jsonplaceholder.typicode.com/posts").then((res) => {
      console.log(res);
      setData(res.data);
    });
  };

  return (
    <>
      <button onClick={callApi}>API 호출</button>
      <div>
        <ul>
          <li>제목</li>
          {data.map((d) => (
            <li key={d.id}>{d.title}</li>
          ))}
        </ul>
      </div>
    </>
  );
};

export default Api1;

이 코드는 버튼을 클릭하면 callApi 함수가 실행되고, JSONPlaceholder에서 데이터를 가져와 data 상태에 저장합니다. 저장된 데이터는 리스트로 렌더링됩니다.

 

App.js

import logo from "./logo.svg";
import "./App.css";
import Api1 from "./Api1";

function App() {
  return <Api1 />;
}

export default App;

이 코드는 Api1 컴포넌트를 불러와서 렌더링합니다.

결과적으로 버튼을 클릭하면 API를 호출하고 데이터를 화면에 표시합니다.

 

  1. F/E (Front-End):
    • 사용자 인터페이스가 있는 부분으로, React와 같은 프론트엔드 라이브러리를 사용합니다.
    • 이곳에서 사용자가 어떤 행동(예: 버튼 클릭)을 하면 API 요청을 보냅니다.
  2. B/E (Back-End):
    • 서버에서 데이터를 처리하고 응답을 돌려주는 부분입니다.
    • API 서버가 이 역할을 담당하며, 데이터베이스와의 연동도 여기서 이루어집니다.
  3. Axios 요청:
    • React에서 Axios를 사용해 API 서버로 GET, POST 등의 요청을 보냅니다.
  4. API 응답:
    • 서버에서 JSON 형식으로 데이터를 응답합니다.
  5. 비동기 처리:
    • Axios는 비동기적으로 데이터를 받아오며, 데이터를 받은 후 React의 state를 업데이트하여 화면에 렌더링합니다.

이렇게 프론트엔드와 백엔드가 API를 통해 상호작용하며, 사용자는 웹 애플리케이션을 통해 데이터를 확인할 수 있습니다.

 


 

REST API란?

REST API(Representational State Transfer Application Programming Interface)는 웹 서비스를 만들기 위한 아키텍처 스타일입니다.

REST는 간단하고 직관적인 설계 원칙을 따르며, 웹에서 데이터를 요청하고 처리하는 데 사용됩니다.

REST API는 다음과 같은 특징이 있습니다:

  1. 자원(Resource) 기반: 모든 데이터는 자원(resource)으로 간주되며, 각 자원은 고유한 URI(Uniform Resource Identifier)로 식별됩니다.
  2. 표현(Representation): 클라이언트는 서버의 자원을 요청하고, 서버는 요청된 자원의 표현을 클라이언트에 보냅니다. 이 표현은 주로 JSON이나 XML 형식입니다.
  3. HTTP 메서드 사용: REST API는 HTTP 메서드를 사용하여 자원에 대한 작업을 수행합니다. 주로 사용되는 메서드는 다음과 같습니다:
    • GET: 자원 조회
    • POST: 자원 생성
    • PUT: 자원 전체 수정
    • PATCH: 자원 부분 수정
    • DELETE: 자원 삭제
  4. 무상태성(Stateless): 각 요청은 독립적이며, 서버는 이전 요청에 대한 정보를 저장하지 않습니다. 클라이언트는 필요한 모든 정보를 각 요청에 포함시켜야 합니다.
  5. 캐시 가능(Cacheable): 응답은 캐시될 수 있어야 합니다. 이를 통해 성능을 향상시킬 수 있습니다.

 

예제 코드로 이해하기

REST API 예제

import axios from 'axios';

// GET 요청으로 모든 게시물 가져오기
axios.get('https://jsonplaceholder.typicode.com/posts')
  .then(response => {
    console.log('모든 게시물:', response.data);
  })
  .catch(error => {
    console.error('에러 발생:', error);
  });

// POST 요청으로 새로운 게시물 생성하기
axios.post('https://jsonplaceholder.typicode.com/posts', {
    title: '새 게시물',
    body: '게시물 내용',
    userId: 1
  })
  .then(response => {
    console.log('새로운 게시물 생성:', response.data);
  })
  .catch(error => {
    console.error('에러 발생:', error);
  });

 

비동기 처리 예제

import axios from 'axios';
import { useState } from 'react';

const ApiExample = () => {
  const [data, setData] = useState([]); // state 초기화

  // API 호출 함수
  const callApi = async () => {
    try {
      const response = await axios.get('https://jsonplaceholder.typicode.com/posts');
      setData(response.data);
    } catch (error) {
      console.error('API 호출 중 에러 발생:', error);
    }
  };

  return (
    <>
      <button onClick={callApi}>API 호출</button>
      <div>
        <ul>
          {data.map((d) => (
            <li key={d.id}>{d.title}</li>
          ))}
        </ul>
      </div>
    </>
  );
};

export default ApiExample;

이 예제에서는 비동기 함수 callApi를 통해 API를 호출하고, 그 결과를 data 상태에 저장합니다.

버튼을 클릭하면 callApi 함수가 실행되고, API로부터 데이터를 받아와 화면에 렌더링합니다.

 

비동기 처리와 REST API를 이해하고 나면, 웹 개발에서 데이터를 효율적으로 주고받는 방법을 더 잘 이해할 수 있습니다.

이를 통해 사용자에게 더 나은 경험을 제공할 수 있습니다.

 


RESTful API와 REST API는 매우 밀접한 관계가 있지만, 엄밀히 따지면 다소 차이가 있습니다.

아래에서 두 개념을 명확히 설명하겠습니다.

 

REST API

REST API는 Representational State Transfer (REST) 원칙을 기반으로 한 API입니다.

REST는 웹 서비스를 설계하는 아키텍처 스타일로, 2000년 로이 필딩(Roy Fielding)의 박사 논문에서 처음 제안되었습니다. REST의 주요 원칙은 다음과 같습니다:

  1. 자원(Resource) 기반: URI를 통해 자원을 식별합니다.
  2. 표현(Representation): 클라이언트는 자원의 상태를 요청하고, 서버는 자원의 표현을 클라이언트에 보냅니다. 일반적으로 JSON 또는 XML 형식을 사용합니다.
  3. HTTP 메서드 사용: 자원에 대한 작업은 HTTP 메서드를 통해 수행합니다. 주요 메서드는 GET, POST, PUT, DELETE 등이 있습니다.
  4. 무상태성(Stateless): 각 요청은 독립적이며, 서버는 요청 간의 상태를 저장하지 않습니다.
  5. 캐시 가능(Cacheable): 응답은 캐시될 수 있어야 합니다.
  6. 계층 구조(Layered System): 클라이언트는 여러 계층의 서버와 상호 작용할 수 있습니다.

 

RESTful API

RESTful API는 REST 아키텍처 원칙을 준수하여 설계된 API입니다.

"RESTful"이라는 용어는 REST 원칙을 잘 따르고 있는지를 나타냅니다.

즉, RESTful API는 REST의 제약 조건과 원칙을 따르는 API를 말합니다.

차이점 요약

  • REST API: REST 아키텍처 원칙을 기반으로 하는 모든 API를 의미합니다.
  • RESTful API: REST 아키텍처 원칙을 충실히 따르는 API를 의미합니다.

따라서 모든 RESTful API는 REST API이지만, 모든 REST API가 RESTful API는 아닐 수 있습니다.

RESTful API는 REST 원칙을 엄격히 따르는 반면, REST API는 이러한 원칙을 완전히 준수하지 않을 수 있습니다.

 

예제

RESTful API와 REST API의 차이를 예제로 설명해 보겠습니다.

RESTful API 예제

  • GET 요청: GET /users/123
    • 특정 사용자의 정보를 조회합니다.
    • 클라이언트는 /users/123 URI를 통해 사용자 자원을 식별합니다.
  • POST 요청: POST /users
    • 새로운 사용자를 생성합니다.
    • 클라이언트는 /users URI를 통해 사용자 자원을 식별하고, 요청 본문에 새 사용자 데이터를 포함합니다.

REST API 예제 (비 RESTful)

  • GET 요청: GET /getUser?id=123
    • 특정 사용자의 정보를 조회합니다.
    • URI에 자원의 상태를 표현하지 않고, 쿼리 파라미터를 사용합니다.
  • POST 요청: POST /createUser
    • 새로운 사용자를 생성합니다.
    • URI가 동사(createUser)로 되어 있어 RESTful 원칙을 위반합니다.

요약

RESTful API는 REST 원칙을 철저히 따르는 API이고, REST API는 그 원칙을 기반으로 하지만 반드시 엄격히 따르지는 않을 수 있습니다. 이 차이를 이해하면 더 나은 API 설계를 할 수 있습니다.

 


 

비동기(Asynchronous)란?

비동기 처리란 어떤 작업이 완료될 때까지 기다리지 않고, 다음 작업을 먼저 수행하는 방식입니다.

웹 개발에서는 주로 서버에 요청을 보내고 응답을 기다릴 때 사용됩니다.

비동기 처리를 통해 사용자는 페이지가 멈추지 않고, 다른 작업을 계속할 수 있습니다.

예를 들어, 웹 페이지에서 데이터를 서버로 요청하는 동안 사용자는 다른 버튼을 클릭하거나 페이지를 스크롤할 수 있습니다.

 

비동기를 왜 사용하나요?

비동기(asynchronous) 처리는 웹 개발에서 중요한 역할을 합니다. 그 이유는 다음과 같습니다:

  1. 사용자 경험 향상:
    • 비동기 처리를 사용하면 페이지를 새로 고침하지 않고도 데이터를 주고받을 수 있습니다. 이는 사용자 경험을 크게 향상시킵니다. 예를 들어, 사용자가 게시물을 작성하고 서버에 저장할 때, 페이지를 새로 고침하지 않고도 저장 결과를 즉시 확인할 수 있습니다.
  2. 성능 향상:
    • 비동기 처리는 요청을 보내고 응답을 기다리는 동안 다른 작업을 계속할 수 있게 합니다. 이를 통해 애플리케이션의 성능을 향상시킬 수 있습니다. 동기 방식으로 요청을 처리하면, 응답을 기다리는 동안 다른 작업이 블록됩니다.
  3. 자원 효율성:
    • 비동기 처리는 서버 자원을 효율적으로 사용할 수 있게 합니다. 서버는 클라이언트의 요청을 비동기적으로 처리하여 더 많은 요청을 동시에 처리할 수 있습니다.
  4. 코드 가독성 및 유지보수성 향상:
    • 비동기 코드를 작성하면, 비동기 작업의 완료 시점을 명확하게 할 수 있어 코드의 가독성과 유지보수성이 향상됩니다. Promise나 async/await 같은 비동기 처리 방법은 비동기 작업을 더 쉽게 관리할 수 있게 합니다.

 

Promise란?

Promise는 자바스크립트에서 비동기 작업을 처리하는 방법 중 하나입니다. Promise는 다음 세 가지 상태를 가집니다:

  • 대기(Pending): 비동기 작업이 아직 완료되지 않은 상태.
  • 이행(Fulfilled): 비동기 작업이 성공적으로 완료된 상태.
  • 거부(Rejected): 비동기 작업이 실패한 상태.

Promise 객체는 .then(), .catch(), .finally() 메서드를 사용하여 비동기 작업의 결과를 처리합니다.

 

.then(), .catch(), .finally()

Promise가 이행되면 .then(),

거부되면 .catch(),

작업이 완료되면 (성공 여부와 관계없이) .finally()가 호출됩니다.

// 예제: Promise와 then, catch, finally 사용하기
const fetchData = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      // 비동기 작업
      const success = true;
      if (success) {
        resolve("데이터를 성공적으로 가져왔습니다!");
      } else {
        reject("데이터를 가져오지 못했습니다.");
      }
    }, 1000);
  });
};

fetchData()
  .then((data) => {
    console.log(data); // "데이터를 성공적으로 가져왔습니다!" 출력
  })
  .catch((error) => {
    console.error(error); // 오류 메시지 출력
  })
  .finally(() => {
    console.log("작업이 완료되었습니다."); // 작업 완료 메시지 출력
  });

 

async/await

JavaScript에서는 비동기 작업을 처리하는 데 여러 가지 방법이 있습니다.

그 중 대표적인 방법이 .then(), .catch(), .finally() 체인을 사용하는 방법과 async/await 문법을 사용하는 방법입니다.

두 방법 모두 비동기 코드를 더 읽기 쉽고 관리하기 쉽게 만들어 줍니다.

 

async/await는 ES2017 (ES8)에서 도입된 새로운 문법으로, Promise 기반의 코드를 보다 읽기 쉽고 동기 코드처럼 작성할 수 있게 해줍니다.

  • async 함수는 항상 Promise를 반환합니다.
  • await 키워드는 Promise가 이행될 때까지 기다립니다.
// 예제: async/await 사용하기
const fetchData = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const success = true;
      if (success) {
        resolve("데이터를 성공적으로 가져왔습니다!");
      } else {
        reject("데이터를 가져오지 못했습니다.");
      }
    }, 1000);
  });
};

const getData = async () => {
  try {
    const data = await fetchData(); // Promise가 이행될 때까지 기다림
    console.log(data); // "데이터를 성공적으로 가져왔습니다!" 출력
  } catch (error) {
    console.error(error); // 오류 메시지 출력
  } finally {
    console.log("작업이 완료되었습니다."); // 작업 완료 메시지 출력
  }
};

getData();

설명

  • fetchData 함수는 1초 후에 데이터를 성공적으로 가져오거나 오류를 발생시키는 Promise를 반환합니다.
  • getData 함수는 async 키워드로 정의되며, 이 함수 내부에서 await 키워드를 사용하여 fetchData가 완료될 때까지 기다립니다.
  • await fetchData()는 fetchData가 성공적으로 완료되면 반환된 데이터를 data 변수에 저장하고, 실패하면 catch 블록으로 넘어갑니다.
  • try 블록 안에서 await fetchData()를 호출하면 Promise가 성공적으로 이행될 때까지 기다린 후, 그 결과를 data 변수에 할당합니다.
  • catch 블록에서는 fetchData가 실패할 경우 오류 메시지를 출력합니다.
  • finally 블록은 Promise의 성공 여부와 관계없이 항상 실행되어 "작업이 완료되었습니다."라는 메시지를 출력합니다.

await의 특징

  1. 비동기 함수 내부에서만 사용 가능: await는 async 함수 내부에서만 사용할 수 있습니다.
  2. Promise 반환: await는 Promise가 완료될 때까지 기다리며, Promise가 성공적으로 완료되면 그 결과 값을 반환하고, 실패하면 오류를 발생시킵니다.
  3. 코드 가독성 향상: await를 사용하면 비동기 코드를 마치 동기 코드처럼 작성할 수 있어 가독성이 크게 향상됩니다.

이처럼 await를 사용하면 비동기 작업을 보다 직관적이고 읽기 쉽게 작성할 수 있습니다.

 

요약

  • .then(), .catch(), .finally(): Promise 객체의 메서드로, 비동기 작업이 이행, 거부, 완료될 때 각각 호출됩니다.
  • async/await: 비동기 함수를 작성할 때 Promise 기반의 코드를 동기 코드처럼 읽기 쉽게 만들어 주는 문법입니다.

두 방법 모두 비동기 코드를 더 효율적이고 관리하기 쉽게 만들어 주지만, async/await는 특히 비동기 작업이 많거나 중첩된 경우에 코드의 가독성을 크게 향상시킵니다.

 


HTTP GET 요청이란?

GET 요청은 서버로부터 데이터를 요청하는 방법입니다. 주로 데이터를 조회할 때 사용됩니다. 예를 들어, 사용자가 게시물 목록을 보고자 할 때 GET 요청을 보냅니다.

예제:

axios.get("https://jsonplaceholder.typicode.com/posts")
  .then((response) => {
    console.log(response.data); // 서버로부터 받은 데이터
  })
  .catch((error) => {
    console.error("에러 발생:", error);
  });

HTTP POST 요청이란?

POST 요청은 서버로 데이터를 전송하는 방법입니다. 주로 데이터를 생성하거나 업데이트할 때 사용됩니다. 예를 들어, 사용자가 새로운 게시물을 작성할 때 POST 요청을 보냅니다.

예제:

axios.post("https://jsonplaceholder.typicode.com/posts", {
    title: '새 게시물',
    body: '게시물 내용',
    userId: 1
  })
  .then((response) => {
    console.log(response.data); // 서버로부터 받은 응답 데이터
  })
  .catch((error) => {
    console.error("에러 발생:", error);
  });

CORS와 @CrossOrigin

CORS(Cross-Origin Resource Sharing)는 웹 브라우저가 서로 다른 도메인에서 자원을 요청할 때 발생하는 문제를 해결하기 위한 보안 기능입니다. 예를 들어, 우리가 http://localhost:3000에서 실행 중인 React 애플리케이션이 http://localhost:8080의 서버 API를 호출할 때, CORS 정책에 의해 차단될 수 있습니다.

Spring Boot에서 이러한 문제를 해결하기 위해 @CrossOrigin 어노테이션을 사용합니다.

import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MyController {

  @CrossOrigin(origins = "http://localhost:3000")
  @GetMapping("/api/data")
  public List<String> getData() {
    return List.of("Data 1", "Data 2", "Data 3");
  }
}

이 코드는 React 애플리케이션에서 http://localhost:3000에서 오는 API 요청을 허용합니다. 만약 @CrossOrigin을 사용하지 않으면, Axios로 요청을 보낼 때 CORS 정책에 의해 에러가 발생합니다.


Spring Initializr란?

Spring Initializr는 Spring Boot 프로젝트를 쉽게 생성할 수 있도록 도와주는 웹 도구입니다. Spring Initializr를 사용하면 필요한 종속성을 선택하고, 프로젝트 설정을 구성한 후, 프로젝트를 다운로드하여 바로 개발을 시작할 수 있습니다.

 

Spring Initializr 사용 방법:

  1. Spring Initializr 웹사이트에 접속합니다.
  2. 프로젝트 메타데이터(이름, 그룹, 아티팩트, 설명 등)를 입력합니다.
  3. 종속성(Dependencies)을 선택합니다. 예를 들어, Spring Web, Spring Data JPA 등.
  4. Generate 버튼을 클릭하여 프로젝트를 다운로드합니다.
  5. 다운로드한 프로젝트를 IDE에서 열고 개발을 시작합니다.

이제 API 테스트를 위한 기본적인 개념과 사용 방법을 알게 되었습니다. React와 Spring Boot를 연동하여 더 강력한 웹 애플리케이션을 개발할 수 있습니다.