Spring Boot + React.js 개발환경 연동하기

u-nij·2022년 3월 28일
66
post-thumbnail

개발 환경을 세팅하면서 관련된 지식들과 제가 겪은 오류들😅을 기록하고, 다른 분들에게 도움이 됐으면 하는 바람으로 작성합니다.

사용한 개발환경

MySQL 8.0.27
Spring Boot 2.6.5
java 11.0.9
IDE Intellij
Windows

개발 환경 설정

1. Spring Boot

프로젝트 생성

Gradle Project
Language : Java
Spring Boot : 2.6.5
Dependencies : Spring Web

https://start.spring.io/ 에서 위와 같은 설정으로 Spring Boot 프로젝트를 생성해줍니다. Spring Web 의존성에는 내장 웹 서버로 톰캣(Tomcat)이 탑재되어 있습니다.

서블릿(Servlet)이란 클라이언트의 요청을 처리하고, 그 결과를 다시 클라이언트에게 응답해주는 역할을 합니다. 서블릿 컨테이너란, 서블릿들을 위한 박스입니다.
톰캣(Tomcat)은 대표적인 서블릿 컨테이너중 하나로 웹페이지를 동적으로 생성하는 역할을 합니다.

Intellij에서 우측에 Gradle을 눌러보면, spring-boot-starter-web 안에 Tomcat이 들어가 있는걸 볼 수 있습니다.

실행


생성된 프로젝트 파일의 압축을 풀고, 이클립스에서 해당 폴더를 열게 되면 빌드를 시작합니다. 빌드가 완료된 후에, 프로젝트를 Run시키면 포트 8080에서 톰캣이 시작되었다는 콘솔 메세지가 뜹니다.


http://localhost:8080에 들어가게 되면, 에러 페이지가 보입니다!

🤣 의존성을 추가할 때 주의할 점

나중에 MySQL을 연동해줄 생각으로 프로젝트를 생성할 때부터 dependencies에 Spring-Data-JPA를 추가해줬었는데요.

프로젝트를 실행시켰을때 이런 오류를 봤습니다. AWS에서 RDS로 미리 DB를 연결해놓은 뒤라서 이유를 몰랐었는데, 거꾸로 하나하나 돌아가다 보니까 JPA 의존성이 문제였던걸 알게 됐습니다. (귀차니즘이 불러 일으킨 대참사 😥..)
그 이후로, 처음에 빌드를 해본 뒤에 설정을 쌓아가는 식으로 프로젝트를 만들어갑니다😀..!

이클립스에서 Java 버전 맞추기 (Windows 기준)

종종 실행하다보면, java 버전이 맞지 않아 에러가 발생하는 경우가 있습니다! 미리 버전을 맞춰주겠습니다.

1. File - Settings - Build, Execution, Deployment - Build Tools - Gradle


기본 설정으로 되어있는 Gradle로 프로젝트를 실행하는 것보다, IntelliJ IDEA로 실행하는게 더 빠르기 때문에 같이 바꿔주겠습니다.

2. File - Proejct Structure


2. React.js

React 설치하기

터미널을 키고 코드를 실행해줍니다.

cd src/main
npx create-react-app frontend	# npx create-reeact {프로젝트명}

설치가 완료되기까지 3분정도 소요됩니다.

설치가 완료되면, 위와 같은 구조로 frontend 폴더가 생성됩니다. 앱을 실행해보겠습니다.

실행

cd frontend	# cd {프로젝트명}
npm start


http://localhost:3000으로 들어가면 React 앱이 실행되는 것이 보입니다!


3. Spring Boot와 React 연동하기

Proxy 설정 (공식문서)

CORS 관련 오류를 방지하기 위해 proxy를 설정해주도록 하겠습니다.

CORS (Cross Origin Resource Sharing)
서버와 클라이언트가 동일한 IP주소에서 동작하고 있다면, resource를 제약 없이 서로 공유할 수 있지만, 만약 다른 도메인에 있다면 원칙적으로 어떤 데이터도 주고받을 수 없도록 하는 매커니즘입니다.

src/main/frontend 폴더에서 필요한 모듈을 설치해줍니다.

npm install http-proxy-middleware --save

src/main/frontend/src 폴더에서 setProxy.js 파일을 생성하고, 아래의 코드를 붙여넣기합니다.

// src/main/frontend/src/setProxy.js

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

module.exports = function(app) {
  app.use(
    '/api',
    createProxyMiddleware({
      target: 'http://localhost:8080',	# 서버 URL or localhost:설정한포트번호
      changeOrigin: true,
    })
  );
};

이제 프론트엔드에서 '/api'로 요청을 보내면, 백엔드인 8080포트(=target)로 요청이 도착하게 됩니다.

Axios를 이용한 서버 통신

Axios란 백엔드와 프론트엔드 사이의 통신을 쉽게 하기 위해 사용하는 라이브러리입니다.

1. 프론트엔드

src/main/frontend 폴더에서 axios를 설치해줍니다.

npm install axios --save

src/main/frontend/src/App.js의 내용을 지우고 아래 코드를 붙여넣기 해주세요.

// src/main/frontend/src/App.js

import React, {useEffect, useState} from 'react';
import axios from 'axios';

function App() {
   const [hello, setHello] = useState('')

    useEffect(() => {
        axios.get('/api/hello')
        .then(response => setHello(response.data))
        .catch(error => console.log(error))
    }, []);

    return (
        <div>
            백엔드에서 가져온 데이터입니다 : {hello}
        </div>
    );
}

export default App;

2. 백엔드

src/main/java/com.demogroup.demoweb 에서 Controller 패키지를 생성하고, HelloWorldController.java 파일을 만들어주겠습니다.

// src/main/java/com.demogroup.demoweb/Controller/HelloWorldController.java

package com.demogroup.demoweb.Controller;

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

@RestController
public class HelloWorldController {

    @GetMapping("/api/hello")
    public String test() {
        return "Hello, world!";
    }
}

Tip : @RestController = @Controller + @ResponseBody


폴더 계층도를 참고해주세요!

실행된 모습

스프링 프로젝트를 빌드하고 실행해줍니다. npm start로 리액트도 실행시켜주어야 합니다!

localhost:3000에서 localhost:8080/api/hello로부터 데이터를 요청해 받아와 출력하는 것을 볼 수 있습니다.


4. 빌드하기

build.gradle 파일 하단에 코드를 추가합니다. SpringBoot 프로젝트가 build 될 때 React 프로젝트가 먼저 build되고, 결과물을 SpringBoot 프로젝트 build 결과물에 포함시킨다는 스크립트입니다.

def frontendDir = "$projectDir/src/main/frontend"

sourceSets {
	main {
		resources { srcDirs = ["$projectDir/src/main/resources"]
		}
	}
}

processResources { dependsOn "copyReactBuildFiles" }

task installReact(type: Exec) {
	workingDir "$frontendDir"
	inputs.dir "$frontendDir"
	group = BasePlugin.BUILD_GROUP
	if (System.getProperty('os.name').toLowerCase(Locale.ROOT).contains('windows')) {
		commandLine "npm.cmd", "audit", "fix"
		commandLine 'npm.cmd', 'install' }
	else {
		commandLine "npm", "audit", "fix" commandLine 'npm', 'install'
	}
}

task buildReact(type: Exec) {
	dependsOn "installReact"
	workingDir "$frontendDir"
	inputs.dir "$frontendDir"
	group = BasePlugin.BUILD_GROUP
	if (System.getProperty('os.name').toLowerCase(Locale.ROOT).contains('windows')) {
		commandLine "npm.cmd", "run-script", "build"
	} else {
		commandLine "npm", "run-script", "build"
	}
}

task copyReactBuildFiles(type: Copy) {
	dependsOn "buildReact"
	from "$frontendDir/build"
	into "$projectDir/src/main/resources/static"
}

홈 디렉토리로 빠져나와 빌드를 실행합니다.

./gradlew build

BUILD SUCCESSFUL 메세지가 보인다면, build/libs 폴더에 jar 파일로 결과물이 생성되어 있을 겁니다.

java -jar build/libs/demo-web-0.0.1-SNAPSHOT.jar


성공적으로 실행된 모습입니다! 포트번호가 8080으로 바뀐 것을 확인할 수 있습니다.

profile
삶은 달걀이다

28개의 댓글

comment-user-thumbnail
2022년 8월 14일

많은 도움이 됐습니다. 감사합니다!

1개의 답글
comment-user-thumbnail
2022년 10월 27일

감사합니다. !!!
좋은하루 되세요 !

1개의 답글
comment-user-thumbnail
2022년 12월 12일

안녕하세요! 좋은글 많은 도움 됐습니다. 감사합니다.
한가지 질문 드려도 될까요??

빌드 후 jar 실행시에는 백엔드 데이터를 잘 가져오는데 8080서버와 3000서버를 동시에 켰을 때는 3000번 서버에서 백엔드 데이터를 불러오지 못하고 스크립트 에러를 뱉습니다.
axios 설정이 잘못된거가 싶은데.. 구글링을 통해 좀 수정해봐도 해결이 안되네요..
코드는 세 번 똑같이 진행하여서 오타는 없을거라고 생각합니다.. 아래는 에러 내용인데 뭔가 잘못된게 있을까요 ㅜㅜ

GET http://localhost:3000/api/hello 404 (Not Found)

AxiosError {message: 'Request failed with status code 404', name: 'AxiosError', code: 'ERR_BAD_REQUEST', config: {…}, request: XMLHttpRequest, …}

2개의 답글
comment-user-thumbnail
2023년 1월 30일

연동하는 건 얼추했는데...
프런트-->백으로 데이터 넘기는 걸 어떻게 하면 되나요??
백엔드의 데이터를 프런트로 넘겨주는 건 test해보니 잘 되는데
그 반대가 참 힘드네요

1개의 답글
comment-user-thumbnail
2023년 2월 5일

큰 도움이 됐어요 ㅎ 감사합니다.

답글 달기
comment-user-thumbnail
2023년 2월 7일

도움 많이 됐어요 감사합니다. 그런데 꼭 react를 실행하고 스프링 프로젝트를 빌드해야 하나요? react를 먼저 실행하지 않고 스프링만 빌드하는 방법은 없을까요?

답글 달기
comment-user-thumbnail
2023년 2월 25일

감사합니다. 최근에 빌드하면 실행 되지 않고 있습니다.
installReact' (type 'Exec')
specified for property '$1' does not exist.
에러가 나네요 혹시 해결하셨나요?

답글 달기
comment-user-thumbnail
2023년 5월 15일

작성자님 저는 빌드까지는 필요없어서 4번빌드전까지 따라서 쭉 해봤습니다.
덕분에 많이 배워갑니다 감사합니다!

1개의 답글
comment-user-thumbnail
2023년 7월 28일

큰도움이되었습니다 감사합니다.

답글 달기
comment-user-thumbnail
2023년 8월 20일

안녕하세요~ 지금 설정 제대로 쭈욱 하고 있다가
8080에서 "Hello World"는 출력이 잘 되는데
boot 실행하고 npm start 한 뒤 포트 3000에서
백언드에서 가져온 데이터입니다" 만 뜨고 뒤에 Hello World를 가져오지 못하고 있습니다.
어떤 문제가 있을까요?

2개의 답글
comment-user-thumbnail
2023년 9월 29일

좋은 글 감사합니다

답글 달기
comment-user-thumbnail
2024년 1월 12일

프론트엔드에서 백엔드 데이터를 가져오지 못하시는 분들은 npm start 후 터미널에 다음과 같은 경고 에러가 나오지 않는지 확인해 보십시오.
DeprecationWarning: 'onBeforeSetupMiddleware' option is deprecated. Please use the 'setupMiddlewares' option.
저는 이걸로 구글링해서 웹팩 설정 파일(webpackDevServer.config.js) 내용 수정했더니 해결되었습니다.

답글 달기
comment-user-thumbnail
2024년 4월 11일

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

module.exports = function(app){
app.use(
createProxyMiddleware({
target: 'http://localhost:8080',
changeOrigin: true,
pathFilter : '/api',
}),
);
};

문법이 변경되어 http-proxy-middleware 모듈 사용시 위와 같이 pathFilter의 속성값으로 지정해줘야 하네요 :) 후에 또 문법이 변경될 수도 있으니 그 때는 https://www.npmjs.com/package/http-proxy-middleware 링크를 참고하시면 될 것 같습니다.

1개의 답글
comment-user-thumbnail
2024년 8월 14일

안녕하세요 4.빌드하기 에서

해당 코드 builld.gradle 하단에 붙여넣은 후 build 실행하니

A problem occurred starting process 'command'npm''
Cause: error=2, No such file or directory 이라고 나옵니다 이게 무슨 문제일까요?

답글 달기