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

이 글에서는 실무에서 사용할 수 있는 React + SpingBoot 프로젝트 구조를 만드는 과정을 알아보겠습니다. 이전에 Vue + SpringBoot 프로젝트를 만드는 방법을 알아보았는데요. 이 방법은 전문가처럼 React나 Vue 같은 Javascript 프레임워크를 사용할 수 있는 구조가 아니기 때문에 토이 프로젝트 혹은 간단한 팀 프로젝트에서 이용할 수 있는 구조이지 실무에서는 사용하기 부적합한 구조입니다. 이번 글에서는 프론트엔드 전문가처럼은 아니지만 그에 준하는 수준으로 React를 사용할 수 있는 SpringBoot 프로젝트를 만들어보겠습니다.

스프링 부트 프로젝트 만들기

먼저 프로젝트의 중심이 되는 스프링 부트 프로젝트를 만들어보겠습니다. Gradle이나 Maven과 같은 빌드 도구를 이용할 수 있지만 조금 더 손쉽고 편한 방법으로 프로젝트를 만들기 위해 스프링 부트 프로젝트를 Spring initializr를 이용해보겠습니다.

https://start.spring.io - 스프링 부트 프로젝트 만들기

 Spring initializr를 사용하면 쉽게 Spring Boot 프로젝트를 만들 수 있습니다. 빌드 도구는 Gradle을 선택해주세요. 언어는 Java, 스프링 부트 버전은 2.5.4, 자바 버전은 8입니다. 라이브러리는 Spring Web만 추가하도록 하겠습니다.

 

Controller 클래스 만들기

SpringBoot 프로젝트를 만들었으니 요청(Request)을 처리할 수 있는 Controller를 추가해보도록 하겠습니다. 프론트엔드와 백엔드를 분리하게 되면 통신할 때 응답 값으로는 주로 JSON을 사용하기 때문에 @RestController를 이용하는 게 좋습니다.

@RestController
public class HelloWorldController {

    @GetMapping("hello")
    public List<String> hello() {
        return Arrays.asList("안녕하세요", "Hello");
    }
}

 예제와 코드를 작성하고 SpringBoot 프로젝트를 실행해주세요. 문제가 없이 웹 서버가 동작한다면 http://localhost:8080/hello 주소로 요청할 때 아래와 같은 페이지(데이터)를 확인하실 수 있습니다. 브라우저마다 보이는 방식이 다를 수도 있습니다.

http://localhost:8080/hello 요청에 대한 응답 결과

 

React 프로젝트 만들기

React의 경우에는 npx를 사용해서 React 프로젝트를 만들 수 있습니다. npx는 npm이 제공하는 하나의 CLI 도구로 npm을 쉽게 사용할 수 있게 해 줍니다. npx는 node 5.2 이상에서 사용할 수 있는 패키지 실행 도구이니, node를 최신 버전으로 업데이트해주세요.

npx create-react-app frontend

 npx를 사용해 React 프로젝트를 만들게 되면 최종적으로 아래와 같은 화면을 확인할 수 있습니다.

npx create-react-app frontend

 일반적으로 React 프로젝트를 구성할 때와 별 다른 차이점은 없습니다. 만약 타입스크립트를 이용한 React 프로젝트를 구성하고 싶으시다면 아래의 명령어를 이용해서 프로젝트를 구성하세요. Typescript 버전의 React 프로젝트를 만드는 것은 명령어에 --template typescript 옵션 하나만 추가해주면 됩니다.

npx create-react-app my-app --template typescript

# or

yarn create react-app my-app --template typescript

SpringBoot의 Controller와 연동하기

React 프로젝트를 만들었으니 SpringBoot에 요청(Request)을 하고 응답(Response)을 받아 화면에 보여주는 작업을 해보겠습니다. 백엔드와 통신하기 위해 가장 먼저 진행해야 할 작업은 proxy를 설정해주는 것입니다.

 

- Proxy 설정하기

package.json

...

"proxy": "http://localhost:8080"

...

 로컬 개발 환경에서 React 프로젝트는 3000번 포트로 SpringBoot 프로젝트는 8080번 포트로 실행이 되기 때문에 CORS 문제가 발생해 proxy 설정은 필수적으로 해야 합니다. frontend 프로젝트 폴더의 package.json 파일에 위에서 제시한 proxy를 설정하는 구문을 추가해주세요.

 

- 서버와 통신하고 처리 결과 보여주기

App.js

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

function App() {
    const [message, setMessage] = useState([]);

    useEffect(() => {
        fetch("/hello")
            .then((response) => {
                return response.json();
            })
            .then(function (data) {
                setMessage(data);
            });
    }, []);

    return (
        <div className="App">
            <header className="App-header">
                <img src={logo} className="App-logo" alt="logo"/>
                <p>
                    Edit <code>src/App.js</code> and save to reload.
                </p>
                <a
                    className="App-link"
                    href="https://reactjs.org"
                    target="_blank"
                    rel="noopener noreferrer"
                >
                    Learn React
                </a>
                <ul>
                    {message.map((text, index) => <li key={`${index}-${text}`}>{text}</li>)}
                </ul>
            </header>
        </div>
    );
}

export default App;

 proxy 설정이 끝났다면 백엔드 서버로 데이터를 요청하고 요청에 대한 응답 결과를 보여주는 작업을 진행해야 합니다. 요청과 응답을 App.js 하나의 파일에서 처리할 수 있도록 수정하였습니다. 서버의 "/hello" 주소로 데이터를 요청하고 응답 결과를 화면에 보여주게 만들었습니다.

프론트엔드와 백엔드 연동 결과

 위에서 제시한 일련의 작업을 수행한 후 npm start 명령어를 이용해 React 프로젝트를 실행시키면 이와 같은 결과물을 확인할 수 있습니다. 이제 React 프로젝트와 SpringBoot 프로젝트를 한 모듈에 만들고 연동하는 과정이 끝났습니다.

 

 그렇다면 이걸로 모든 작업이 끝난 걸까요? 대답은 "아니요"입니다. 프로젝트를 구성만 하는 단계는 끝났지만 배포를 위해 하나의 jar 파일 혹은 패키지로 빌드해야 하는 과정이 남아있습니다.

 

Gradle을 이용해서 프로젝트 빌드하기

CI/CD 환경을 구축하기 위해서는 프로젝트가 잘 패키지화될 수 있도록 해주어야 합니다. 프로젝트를 패키지화한다는 것은 CI/CD 환경을 떠나 정리가 잘된 책 한 권을 가지는 것과 같은 의미를 가집니다. 자신이 봐도 깔끔하고 다른 사람이 보기에도 정리정돈이 깨끗이 잘되어 있다면 얼마나 좋을까요?

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"
}

 위의 내용을 현재 SpringBoot 프로젝트의 build.gradle 파일의 끝 부분에 추가로 입력해주세요. 결과적으로 SpringBoot 프로젝트와 React 프로젝트가 하나의 패키지가 될 수 있는 build 구성이 완료됩니다.

 

 설정한 build.gradle의 내용은 SpringBoot 프로젝트가 build 될 때 React 프로젝트를 먼저 build 하고 결과물을 SpringBoot 프로젝트 build 결과물에 포함시킨다는 스크립트입니다. 조금 더 정확히 설명을 드리면 gradle을 이용해 프로젝트를 build 하는 경우 task가 서로 의존 관계를 가지기 때문에 processResources를 기점으로 installReact > buildReact > copyReactBuildFiles 순으로 실행이 됩니다.

 

gradle을 이용한 프로젝트 build 실행과 결과

./gradlew build

build.gradle 파일에 스크립트를 추가했으니 명령어를 통해 어떤 결과가 나오는지 확인해보겠습니다. 프로젝트 폴더로 이동 한 뒤 build 명령어를 실행시켜주세요.

./gradlew build

 gradle의 build 명령어를 입력하면, 정의한 build.gradle에 정의되어 있는 내용에 따라 build가 진행됩니다. BUILD SUCCESSFUL 메시지가 보인다면 프로젝트가 문제없이 build 되었다는 이야기입니다.

build 폴더에 만들어진 결과물

 gradle의 build 결과물은 기본적으로 프로젝트 폴더의 buid 폴더에 생성됩니다. SpringBoot 프로젝트의 build 결과물 중 중요한 부분은 build/libs 폴더의 내용입니다. SpringBoot 프로젝트의 경우 배포를 위해 프로젝트를 빌드하면 jar(기본) 파일로 만들어지기 때문입니다.

SpringBoot build 결과물 jar 파일 실행

 정상적으로 jar 파일이 만들어져 있다면, 잘 실행되는지 확인할 차례인데요. 잘 실행되는지 여부는 jar 파일을 두 번 클릭하거나 CLI를 이용해 "java -jar [실행할 jar 파일명]"을 입력해주시면 됩니다. jar 파일이 실행이 되었다면 브라우저를 이용해 http://localhost:8080 페이지에 접속해주세요. 그럼 이미지와 같이 웹 애플리케이션이 동작하는 모습을 확인할 수 있습니다.

 

배포 시에만 React 빌드 파일 포함시키기

./gradlew bootJar

 build task에 React 빌드를 포함시키면 로컬 환경에서 개발을 할 때 불편한 사항들이 생깁니다. 예를 들어 클라이언트(React)를 실행하고 서버(SpringBoot)를 동작시키면 processResources task가 실행되면서 불필요하게 npm install과 react build가 실행됩니다. 그런 이유로 위에서 이야기한 프로젝트 빌드 구조는 개발자가 개발을 하는 Local 환경에서는 적합하지 않은 구조를 가지게 됩니다. 

 

 이런 문제를 해결하는 방법으로는 여러 가지가 있을 수 있겠지만 Spring Boot로 만들어진 프로젝트는 jar로 배포되는 것이 일반적이기 때문에 프로젝트가 만들어지고 배포되는 시점(bootJar) 실행되는 시점에만 React 빌드 파일이 포함될 수 있게 수정하는 것이 좋습니다.

tasks.bootJar {
    dependsOn "copyReactBuildFiles"
}

spring-boot-with-reactjs-master.zip
0.42MB

 이 글에서 진행한 모든 작업은 기본적으로 github 저장소를 통해 제공됩니다. 소스 코드나 프로젝트 내용이 필요하신 분은 github 저장소를 이용하시거나 첨부파일을 다운받아 사용하시면 됩니다.

 

 

반응형

댓글

Designed by JB FACTORY