[SpringBoot/React] Spring 백엔드 + React 프론트엔드

gyeol·2025년 5월 10일
post-thumbnail

기존 프로젝트를 모바일 웹 환경에서도 제공하기 위해 React를 도입하게 되었다. 이에 따라 기존 src/static/templates 경로에서 관리하던 파일들을 React 형식에 맞게 변경해야 했다. 백엔드와 프론트엔드 간 데이터 흐름을 명확히 이해하고자 정리한 내용이다.

  • 먼저 Spring Boot 서버를 실행
    백엔드 서버가 정상적으로 동작하고 있어야, React에서 API 요청을 보냈을 때 올바른 응답을 받을 수 있다.

  • 이후, 새로운 터미널 창을 열어 React 프로젝트 폴더로 이동한다.
    cd [리액트 폴더 경로]

  • 터미널 경로가 올바르게 변경된 것을 확인한 뒤, React 개발 서버를 실행한다. npm start

  • 명령어를 입력하면 자동으로 브라우저가 열리며 http://localhost:3000 포트에서 웹페이지가 실행된다.

  • React는 모바일 환경을 기준으로 개발 중이므로, 브라우저의 개발자 도구를 활용해 모바일 화면으로 전환해준다.
    크롬 기준으로 F12를 눌러 개발자 도구를 연 후, 상단 메뉴에서 '디바이스 모드(📱)'로 전환하면 다양한 모바일 해상도로 화면을 확인할 수 있다.

  • React 프로젝트에서는 각 pages 폴더 안의 컴포넌트마다 개별 CSS 파일을 연결할 수 있지만, 공통 요소인 헤더(Header), 푸터(Footer) 등에 대한 스타일은 일반적으로 공유되는 CSS 파일에 정의한다.
    예를 들어, Main.css에 헤더/푸터 스타일을 정의해두면, 해당 스타일은 pages 폴더 내부의 모든 페이지 컴포넌트에 전역적으로 적용된다.

1. 백엔드: Spring Boot

@RestController 

기능 : React에서 요청하면, Spring이 응답하거나 DB에 저장한다

@RestController는 JSON 형식으로 데이터를 주고받기 위한 API 컨트롤러이다.

@RestController
@RequestMapping("/api")
public class ShopApiController {

    @GetMapping("/shops")
    public List<ShopDto> getAllShops() {
        return shopService.findAllShops(); // → DB에서 데이터 꺼내오기
    }

    @PostMapping("/shops")
    public void addShop(@RequestBody ShopDto shopDto) {
        shopService.save(shopDto); // → 프론트에서 받은 데이터 저장
    }
}

-> 이런식으로 Controller/api 폴더 안에 만들어주면 된다.

2. 프론트엔드: React

다음과 같은 폴더 구조로 생겼다.

react-front/
├── src/
│   ├── pages/         // 화면별 컴포넌트(html이라고 생각) (예: MainPage.js, ShopListPage.js)
│   ├── service/       // API 호출 함수들 (백엔드랑 연결) -> 8080/api 랑 연결
│   ├── App.js        // 전체 앱 라우팅, 구조 잡기 -> 3000번 포트에서 쓸 도메인 결정
  • pages/ 폴더: 각 페이지별 컴포넌트를 관리합니다.
  • service/ 폴더: 백엔드 API와 통신하는 함수들을 정의합니다.
  • App.js: React 앱의 라우팅을 설정하여 각 페이지 컴포넌트를 연결합니다.

3. service/ — 백엔드와 통신하는 파일 (localhost:8080/api와 연결 시켜줌)

기능: Spring API와 직접 통신. React에서는 이 함수를 호출해서 데이터를 주고받는다.

import axios from 'axios';

const BASE = 'http://localhost:8080/api';

export const fetchShopDetails = (id) =>
  axios.get(`${BASE}/shop/${id}`, { withCredentials: true }).then(res => res.data);

export const toggleBookmark = (shopId) =>
  axios.post(`${BASE}/bookmark/toggle`, { shopId }, { withCredentials: true }).then(res => res.data);

export const fetchBookmarkFolders = () =>
  axios.get(`${BASE}/bookmark/folders`, { withCredentials: true }).then(res => res.data);

export const createBookmarkFolder = (name) =>
  axios.post(`${BASE}/bookmark/folders`, { name }, { withCredentials: true }).then(res => res.data);

이런 식으로 BASE 경로를 지정한다(이 경로는 우리가 Controller/api 경로에 생성해준 controller와 연결해주면 된다)

fetchShopDetails와 같은 변수명은 추후에 pages/ 폴더에서 화면 연결하는 데에 사용된다.

4. pages/ — 실제로 화면에 데이터를 표시하는 컴포넌트

우리가 8080에서 사용했던 html 파일을 react의 .js 파일 형식에 맞게 보여준다고 생각하면 된다.

import React, { useEffect, useState } from 'react';
import { fetchShops } from '../service/ShopApi';

function ShopListPage() {
  const [shops, setShops] = useState([]);

  useEffect(() => {
    fetchShops().then(data => {
      setShops(data);
    });
  }, []);

  return (
    <div>
      <h1>가게 목록</h1>
      <ul>
        {shops.map(shop => (
          <li key={shop.id}>{shop.name}</li>
        ))}
      </ul>
    </div>
  );
}

export default ShopListPage;

→ 이런식으로 사용하는데 이때 앞서 우리가 8080포트와 연결할때 사용한 pages/(…)Api 파일과 연결시켜줘야한다.

5. App.js → 리액트 3000번 포트에서 사용할 도메인 지정

예를 들어서 우리 8080/main경로로 들어가서 보여주는 화면을 리액트에서도 3000/main 으로 사용하고싶다!!
App.js 에서 설정해주면 된다. 이때 우리가 만든 리액트 프론트 화면인 pages 폴더와 연결하면 된다.

// src/App.js
import React from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Signup from './pages/Signup';
import Login from './pages/Login';
import Mypage from './pages/Mypage';
import Main from './pages/Main';
import MapPage from './pages/Map'; 
import BookmarkList from './pages/BookmarkList';
import LikeList from './pages/LikeList';
import FoodDetailsPage from './pages/FoodDetails';
import SearchPage from './pages/Search';
import BoardMain from './pages/BoardMain';
import BoardDetails from './pages/BoardDetails';

function App() {
  return (
    <Router>
      <Routes>
        <Route path="/member/new" element={<Signup />} />
        <Route path="/member/login" element={<Login />} />
        <Route path="/myPage" element={<Mypage />} />
        <Route path="/main" element={<Main />} />
        <Route path="/shopDetails/:id" element={<FoodDetailsPage />} />
        <Route path="/map" element={<MapPage />} />
        <Route path="/myPage/bookmarkList" element={<BookmarkList />} />
        <Route path="/myPage/likeList" element={<LikeList />} />
        <Route path="/search" element={<SearchPage />} />
        <Route path="/board/main" element={<BoardMain />}/>
        <Route path="/board/:id" element={<BoardDetails />}/>
      </Routes>
    </Router>
  );
}

export default App;
profile
공부 기록 공간 '◡'

0개의 댓글