본문 바로가기
프론트엔드/리액트

[React] Axios로 Spring과 동기화하는 가이드

by AI읽어주는남자 2025. 10. 16.
반응형

Axios를 이용한 Spring Boot와 React 연동 가이드

1. Axios란?

Axios는 브라우저와 Node.js 환경 모두에서 사용할 수 있는 Promise 기반의 HTTP 클라이언트 라이브러리입니다. 비동기적으로 HTTP 통신을 쉽게 할 수 있도록 도와주며, 대표적인 경쟁 기술로는 JavaScript에 내장된 fetch() API와 jQuery의 ajax()가 있습니다.

Axios의 주요 특징

  • 자동 JSON 데이터 변환: 요청 시 JavaScript 객체를 자동으로 JSON으로 변환하고, 응답 시 JSON 데이터를 자동으로 JavaScript 객체로 변환해 줍니다. fetch()는 이 과정을 수동(JSON.stringify(), res.json())으로 처리해야 합니다.
  • Promise 기반: async/await 문법과 함께 사용하여 코드를 더 간결하고 가독성 있게 작성할 수 있습니다.
  • 브라우저 호환성: 구형 브라우저를 포함한 대부분의 브라우저를 지원합니다.
  • 에러 처리: catch를 통해 네트워크 오류나 잘못된 응답(4xx, 5xx)을 쉽게 처리할 수 있습니다.
  • 요청/응답 인터셉터: 요청을 보내기 전이나 응답을 받기 후에 공통 로직(예: 토큰 추가, 에러 로깅)을 삽입할 수 있습니다.

2. 설치 방법

React (npm/yarn)

React 프로젝트에서는 npm 또는 yarn을 통해 간단하게 설치할 수 있습니다.

# npm 사용 시
npm install axios

# yarn 사용 시
yarn add axios

JavaScript (CDN)

HTML 파일에서 직접 사용하려면 <script> 태그를 통해 CDN을 추가합니다.

<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>

3. 기본 문법

Axios는 HTTP 요청 메서드에 해당하는 다양한 함수를 제공합니다.

axios.METHOD(url, [data], [config])
  • METHOD: get, post, put, delete 등 HTTP 메서드.
  • url: 요청을 보낼 서버의 주소 (API Endpoint).
  • data (선택 사항): 요청 본문(body)에 담아 보낼 데이터. 주로 post, put 요청에서 사용됩니다.
  • config (선택 사항): 요청 헤더(headers) 등 추가적인 설정을 위한 객체.
    • headers: { 'Content-Type': 'application/json' } (기본값) 또는 { 'Content-Type': 'multipart/form-data' } (파일 업로드 시).

메서드별 사용 예시

// GET: 데이터 조회
axios.get('/api/data');

// POST: 데이터 생성 (body에 데이터 포함)
axios.post('/api/data', { name: '새 항목', value: 10 });

// PUT: 데이터 수정
axios.put('/api/data/1', { name: '수정된 항목', value: 20 });

// DELETE: 데이터 삭제
axios.delete('/api/data/1');

4. 비동기 처리 방식

1. .then() / .catch() (Promise 체이닝)

요청 성공 시 .then() 블록이, 실패 시 .catch() 블록이 실행됩니다.

const fetchData = () => {
  axios.get('https://api.example.com/data')
    .then(response => {
      // 성공 시 response.data에 서버 응답이 담겨 있음
      console.log('데이터:', response.data);
    })
    .catch(error => {
      // 실패 시
      console.error('에러 발생:', error);
    });
};

2. async / await (동기적 스타일)

async 함수 내에서 await 키워드를 사용하면, 비동기 코드를 동기 코드처럼 작성할 수 있어 가독성이 향상됩니다. 에러 처리는 try...catch 문을 사용합니다.

const fetchData = async () => {
  try {
    const response = await axios.get('https://api.example.com/data');
    // 성공 시
    console.log('데이터:', response.data);
  } catch (error) {
    // 실패 시
    console.error('에러 발생:', error);
  }
};

5. Spring Boot와 React 연동 예제 (Spring → React)

시나리오

  • Backend (Spring Boot): 간단한 게시물 목록을 제공하고, 새 게시물을 등록하는 API를 만듭니다.
  • Frontend (React): Spring Boot API를 호출하여 게시물 목록을 화면에 표시하고, 폼을 통해 새 게시물을 등록합니다.

Backend: Spring Boot 설정 (localhost:8080)

1. DTO (Data Transfer Object) 생성

PostDto.java: 클라이언트와 주고받을 데이터 구조를 정의합니다.

// PostDto.java
package com.example.demo;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@AllArgsConstructor
public class PostDto {
    private long id;
    private String title;
    private String content;
}

2. Controller 생성 (@RestController)

PostApiController.java: API 엔드포인트를 정의합니다.

  • @CrossOrigin("http://localhost:3000"): React 개발 서버(localhost:3000)로부터의 요청을 허용하기 위한 CORS 설정입니다. (React 기본 포트는 3000, Vite는 5173일 수 있습니다.)
  • @GetMapping: 게시물 목록을 조회하는 API.
  • @PostMapping: 새 게시물을 등록하는 API. @RequestBody는 React가 보낸 JSON 데이터를 PostDto 객체로 변환해 줍니다.
// PostApiController.java
package com.example.demo;

import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;

@RestController
@RequestMapping("/api/posts")
@CrossOrigin("http://localhost:3000") // React 앱의 주소를 허용
public class PostApiController {

    private final List<PostDto> posts = new ArrayList<>();
    private final AtomicLong counter = new AtomicLong();

    // 초기 데이터
    public PostApiController() {
        posts.add(new PostDto(counter.incrementAndGet(), "첫 번째 게시물", "안녕하세요!"));
        posts.add(new PostDto(counter.incrementAndGet(), "두 번째 게시물", "Axios 연동 예제입니다."));
    }

    // GET: 모든 게시물 조회
    @GetMapping
    public List<PostDto> getAllPosts() {
        System.out.println("GET /api/posts 요청 받음");
        return posts;
    }

    // POST: 새 게시물 등록
    @PostMapping
    public PostDto createPost(@RequestBody PostDto newPost) {
        System.out.println("POST /api/posts 요청 받음: " + newPost.getTitle());
        PostDto post = new PostDto(
            counter.incrementAndGet(),
            newPost.getTitle(),
            newPost.getContent()
        );
        posts.add(post);
        return post; // 생성된 게시물 정보 반환
    }
}

Frontend: React 설정 (localhost:3000)

1. Axios 설치 (이미 완료했다고 가정)

2. React 컴포넌트 작성 (PostComponent.js)

  • useEffect를 사용하여 컴포넌트가 처음 렌더링될 때 Spring 서버로부터 게시물 목록을 가져옵니다 (axios.get).
  • useState를 사용하여 게시물 목록(posts), 새 게시물 제목(newTitle), 내용(newContent)을 상태로 관리합니다.
  • handleSubmit 함수에서 axios.post를 호출하여 사용자가 입력한 데이터를 Spring 서버로 전송합니다.
// PostComponent.js
import React, { useState, useEffect } from 'react';
import axios from 'axios';

function PostComponent() {
  const [posts, setPosts] = useState([]);
  const [newTitle, setNewTitle] = useState('');
  const [newContent, setNewContent] = useState('');

  // 1. 컴포넌트 마운트 시 데이터 가져오기 (GET 요청)
  useEffect(() => {
    const fetchPosts = async () => {
      try {
        // Spring Boot API에 GET 요청 (전체 주소 사용)
        const response = await axios.get('http://localhost:8080/api/posts');
        setPosts(response.data); // 응답 데이터를 상태에 저장
      } catch (error) {
        console.error('게시물 데이터를 가져오는 데 실패했습니다.', error);
      }
    };

    fetchPosts();
  }, []); // 빈 배열을 전달하여 한 번만 실행되도록 설정

  // 2. 폼 제출 시 데이터 보내기 (POST 요청)
  const handleSubmit = async (e) => {
    e.preventDefault(); // 폼 기본 동작(새로고침) 방지

    const newPost = { title: newTitle, content: newContent };

    try {
      // Spring Boot API에 POST 요청
      const response = await axios.post('http://localhost:8080/api/posts', newPost);

      // 응답으로 받은 새 게시물을 기존 목록에 추가
      setPosts([...posts, response.data]);

      // 입력 필드 초기화
      setNewTitle('');
      setNewContent('');
    } catch (error) {
      console.error('게시물을 등록하는 데 실패했습니다.', error);
    }
  };

  return (
    <div>
      <h1>게시물 목록</h1>
      <ul>
        {posts.map(post => (
          <li key={post.id}>
            <strong>{post.title}</strong>: {post.content}
          </li>
        ))}
      </ul>

      <hr />

      <h2>새 게시물 작성</h2>
      <form onSubmit={handleSubmit}>
        <div>
          <label>제목: </label>
          <input 
            type="text" 
            value={newTitle} 
            onChange={(e) => setNewTitle(e.target.value)} 
          />
        </div>
        <div>
          <label>내용: </label>
          <input 
            type="text" 
            value={newContent} 
            onChange={(e) => setNewContent(e.target.value)} 
          />
        </div>
        <button type="submit">등록</button>
      </form>
    </div>
  );
}

export default PostComponent;

6. CORS (Cross-Origin Resource Sharing) 문제 해결

문제점

보안상의 이유로 브라우저는 다른 출처(Origin)의 리소스를 요청하는 것을 기본적으로 차단합니다. 여기서 출처는 프로토콜, 호스트(도메인), 포트 번호를 모두 포함합니다.

  • React 앱: http://localhost:3000
  • Spring Boot 앱: http://localhost:8080

위 두 주소는 포트 번호가 다르므로 다른 출처로 간주되어 CORS 정책 위반 오류가 발생합니다.

해결 방안 (Spring Boot)

Spring Boot에서 특정 출처의 요청을 허용하도록 설정하면 간단히 해결할 수 있습니다.

  • Controller 레벨 설정: 특정 컨트롤러의 모든 메서드에 CORS를 적용합니다.
    @CrossOrigin("http://localhost:3000")
    @RestController
    public class MyController { ... }
  • 메서드 레벨 설정: 특정 메서드에만 적용합니다.
    @CrossOrigin("http://localhost:3000")
    @GetMapping("/api/data")
    public String getData() { ... }
  • 전역 설정: 애플리케이션 전체에 동일한 CORS 정책을 적용합니다.
    // WebConfig.java
    @Configuration
    public class WebConfig implements WebMvcConfigurer {
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**") // 모든 경로에 대해
                    .allowedOrigins("http://localhost:3000") // 허용할 출처
                    .allowedMethods("GET", "POST", "PUT", "DELETE") // 허용할 HTTP 메서드
                    .allowCredentials(true); // 쿠키 등 자격 증명 허용
        }
    }

7. 요약: Spring → React 데이터 흐름

  1. [React] 사용자가 페이지에 접속하면 PostComponent가 렌더링됩니다.
  2. [React] useEffect 훅이 실행되어 axios.get('http://localhost:8080/api/posts') 요청을 보냅니다.
  3. [Spring] PostApiController@GetMapping 메서드가 요청을 수신합니다.
  4. [Spring] 컨트롤러는 서비스나 리포지토리를 통해 데이터를 조회한 후 List<PostDto> 객체를 반환합니다.
  5. [Spring] Spring의 Jackson 라이브러리가 List<PostDto> 자바 객체를 JSON 문자열로 직렬화(Serialize)합니다.
  6. [Spring] 직렬화된 JSON 데이터를 HTTP 응답 본문에 담아 React로 보냅니다.
  7. [React] Axios가 응답을 받으면, JSON 문자열을 자동으로 JavaScript 객체로 파싱(Parse)하여 response.data에 담아줍니다.
  8. [React] setPosts(response.data)가 호출되어 컴포넌트의 posts 상태가 업데이트됩니다.
  9. [React] 상태가 변경되었으므로 컴포넌트가 리렌더링되어 화면에 게시물 목록이 표시됩니다.
반응형