타인이 되지 말고 자신이 되어봐요

Be yourself, not others

Programming/Spring(Spring Boot)

Spring Boot와 React를 위한 개발환경 구축하고 같이 빌드하기

kcdevdes 2023. 6. 20. 21:01

참고자료

https://7942yongdae.tistory.com/136

 

Spring - 실무에서 사용하는 React + SpringBoot 프로젝트 만들기 with Gradle

이 글에서는 실무에서 사용할 수 있는 React + SpingBoot 프로젝트 구조를 만드는 과정을 알아보겠습니다. 이전에 Vue + SpringBoot 프로젝트를 만드는 방법을 알아보았는데요. 이 방법은 전문가처럼 React

7942yongdae.tistory.com

 

Spring Boot와 React를 이용해 프론트엔드랑 벡엔드를 둘 다 구축하려는 사람 입장에선 두 프로젝트가 하나의 IDE에서 같이 동작하게 설정하는 게 사실 가장 편리하고 관리하기도 쉬울 것이다. 그래서 이번에 프로젝트를 만들기 위해 두 개의 프레임워크를 Intellij IDEA에서 같이 작동하도록 설정하고 같이 Gradle 빌드하는 방법을 정리해 본다.


Spring Boot 설정하기

https://start.spring.io/

스프링 이니셜라이저 웹페이지에서 Spring Boot 설정을 마치고 의존성으로는 Spring Web과 다른 부가적인 라이브러리를 추가한다. 이후 GENERATE를 하고 다운로드한 zip파일을 해제한 후 Intellij IDEA로 열어준다.

 

 

React 설정하기

IDE에서 프로젝트가 위의 사진과 같이 열리면 이젠 React 프로젝트를 생성해 준다. React는 다음 커맨드를 터미널로 실행해 프로젝트 폴더 최상단에 위치하도록 생성한다.

npx create-react-app <프로젝트 이름> # 해당 글에선 frontend로 지정했다

이후 프로젝트 구조를 보면 다음과 같이 만들어질 것이다!

 

아래의 커맨드를 터미널에 입력하여 잘 작동하는지 확인해 보자

cd frontend
npm start

3000번 포트로 React가 잘 작동하는 것을 확인할 수 있다!

 

이젠 React가 Spring Boot로 만든 API에서 정보를 가져올 수 있도록 Proxy 설정을 해야 한다. frontend/package.json으로 이동해 보자.

{
  "name": "frontend",
  "version": "0.1.0",
  "private": true,
  ....
  "proxy": "http://localhost:8080"
}

package.json 파일 최하단에 다음과 같은 문구를 추가하여, React에서 발생하는 요청이 http://localhost:8080(Spring Boot의 기본 주소)로 흘러가도록 만든다. 이제 React에서 fetch(), axios.get()을 사용할 때 HTTP 요청들이 내부에서 호출할 때 Spring Boot로 들어가게 될 것이다.

 

 

Spring Boot에 API 추가하기

이제 React를 종료하고, Spring Boot로 가보자. com.example.demo 하위에 controller 패키지를 생성한다. 그다음, HelloController라는 파일을 추가하고 다음과 같은 코드를 작성한다.

package com.example.demo.controller

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

@RestController
class HelloController {
    @GetMapping("/api/hello")
    fun getGreeting() : String = "Hello, Client!"
}

자바일 경우는 다음과 같다.

package com.example.demo.controller;

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

@RestController
public class HelloController {
    @GetMapping("/api/hello")
    public String getGreeting() {
		return "Hello, Client!";
    }
}

이제 스프링은 /api/hello란 경로가 추가되었다! React에서 이 경로로 Get 요청을 보내보자!


React에서 GET 요청 보내기

React에서 HTTP 요청을 fetch로 보낼 수도 있지만, 여기선 Axios라이브러리를 사용하겠다. 다음과 같은 커맨드로 axios를 설치해 주자.

npm install axios

이제 frontend/src/App.js를 열고 다음과 같이 수정해 주자!

import logo from './logo.svg';
import './App.css';
import {useEffect, useState} from "react";
import axios from "axios";

function App() {
// 메세지를 담기 위한 state
  const [message, setMessage] = useState("");

  useEffect(() => {
  // 우리가 만든 서버로 보내는 GET 요청
    axios.get("/api/hello")
        .then((response) => {
          setMessage(response.data);
          console.log(response.data);
        })
        .catch((error) => {
          console.log(error);
        })
  }, []);

  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          A message from Spring Boot : <code>{message}</code>
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

export default App;

npm start로 React를 작동시키면 아마 다음과 같은 오류가 출력될 것이다.

아까 제작한 Spring Boot를 실행하지 않아 정상적으로 정보를 가져올 수 없기 때문인데, Spring Boot를 작동시키면 해결된다. 그럼 Spring Boot를 작동시키기 위해 main() 메서드 옆의 실행 버튼을 눌러보자.

그리고 React를 새로고침하게 되면 Hello, Client! 란 메시지가 잘 전달되는 것을 알 수 있다.

이젠 IDE의 Configuration을 조금 건드려보자.

 

Configuration 작성

Intellij IDEA 우측상단의 Configuration은 현재 1개(Spring Boot 실행)만 등록이 되어있을 것이다.

Edit Configurations를 들어간 후, 좌측 상단 +버튼으로 NPM 설정을 들어가자.

package.json을 frontend/package.json으로, command를 start로, Node는 프로젝트에 해당하는 Node로 설정한 후, OK로 저장한다.

이젠 SpringBoot와 React를 모두 Intellij IDEA에서 작동시킬 수 있게 되었다!

 

이제 터미널에서 작동하던 React를 중지시키고, Configuration에서 Spring과 React를 동시에 작동시키면 하단 Run창에서 양측 애플리케이션의 상태를 모두 확인할 수 있게 된다.

패키지로 빌드하기

이젠. jar파일로 React와 SpringBoot를 하나로 묶는 작업이 남았다. 아래의 코드를 build.gradle 하단에 추가 작성해 주자.

def frontendDir = "$projectDir/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 "$buildDir/resources/main/static"
}

각 task의 의존성으로 인해, installReact -> buildReact -> copyReactBuildFiles 순으로 실행되게 된다.

 

installReact에서는 npm으로 필요한 의존성을 설치하고, buildReact에선 React를 build 하여 배포할 수 있게 변환, 마지막으론 copyReactBuildFiles를 통해 해당 빌드파일을 SpringBoot의 resources로 복사해 준다.

 

원리는 다음과 같다. 톰캣 서버는 API 요청이 들어올 때, 첫 번째로 Spring Controller에서 해당 API를 처리할 수 있는지 검색한다. 없다면 그다음에 resource에 담겨있는 파일을 검색하기 때문에 우리가 "/"에 해당하는 API를 제작하지만 않는다면, http://localhost:8080/이란 요청은 static에 담긴 React 빌드 파일로 향하게 된다.

 

이젠 Gradle로 빌드를 돌려보자.

 

터미널에서 프로젝트 최상단으로 이동한 후, 다음 커맨드를 입력해 보자.

./gradlew build

 

하단과 같이 BUILD SUCCESSFUL이라 출력되면 성공한 것이다.

이젠 생성된 빌드파일을 실행할 차례이다. 실행하기 전 Intellij IDEA에서 돌아가는 React와 Spring Boot 애플리케이션들을 모두 종료하고 실행하여야 한다. Intellij IDEA 애플리케이션들이 3000, 8080 포트를 이미 사용하고 있기 때문에 .jar파일이 돌아가지 않게 된다.

 

커맨드는 다음과 같다.

cd build/libs # 빌드 파일로 이동
ls # 빌드 파일 확인
java -jar <빌드 파일 이름> # 여기선 demo-0.0.1-SNAPSHOT.jar

이제 http://localhost:8080/으로 접속해 보자!

성공이다! 이제 배포할 때엔 저 jar 파일로만 배포하면 된다 :)

 

틀린 내용이나 질문은 언제나 환영입니다!