공부/프로그래밍

[react, springboot] react 와 spring boot 로 구성하기, 묶어 build 하기

demonic_ 2020. 1. 19. 11:28

Springboot 프로젝트를 생성한다.

여기서는 Intellij 를 이용해 생성했고, Gradle과 Java 버전 11을 사용했다.

이 과정은 생략하겠다.

react를 설치하는 방법은 다양하지만 여기서는 Create react app 을 이용해 생성하고자 한다

관련 항목은 아래 링크에서 설명되어 있다

https://reactjs-kr.firebaseapp.com/docs/installation.html

 

Getting Started – React

A JavaScript library for building user interfaces

reactjs.org

 

node.js 6버전 이상이 설치되어 있어야 한다

npm을 이용해 create-react-app 을 설치한다

npm install -g create-react-app

 

frontend 폴더를 만들고 그 안에 리액트 프로젝트를 생성한다

mkdir frontend
cd frontend

create-react-app my-app

 

다음과 같은 로그가 나오고 완료된다.

Success! Created my-app at /Users/dgpark/git/project/react/frontend/my-app
Inside that directory, you can run several commands:

  npm start
    Starts the development server.

  npm run build
    Bundles the app into static files for production.

  npm test
    Starts the test runner.

  npm run eject
    Removes this tool and copies build dependencies, configuration files
    and scripts into the app directory. If you do this, you can’t go back!

We suggest that you begin by typing:

  cd my-app
  npm start

Happy hacking!
​

 

폴더에 접근한 뒤 리액트를 실행한다.

cd my-app
npm start


> my-app@0.1.0 start /react/frontend/my-app
> react-scripts start

ℹ 「wds」: Project is running at http://192.168.2.139/
ℹ 「wds」: webpack output is served from /
ℹ 「wds」: Content not from webpack is served from /Users/dgpark/git/project/react/frontend/my-app/public
Compiled successfully!

You can now view my-app in the browser.

  Local:            http://localhost:3000/
  On Your Network:  http://192.168.2.139:3000/

Note that the development build is not optimized.
To create a production build, use npm run build.
​

 

localhost:3000 으로 접속하면 다음 화면이 뜬다.

 

폴더구성은 다음과 같다

 

 

 

# React 를 작성한다

편의를 위해 react-bootstrap와 react-router-dom을 설치, 사용한다

그리고 API 호출을 위해 axios 도 설치한다

npm install bootstrap react-bootstrap --save
npm install react-router-dom --save
npm install axios --save

 

2개 메뉴를 생성(Main, Dashboard)

MainComponent.jsx

import React, {Component} from "react";

class MainComponent extends Component {    
    render() {
        return(
            <div>
                Main 페이지
            </div>
        )
    }
}

export default MainComponent

 

Dashboard.jsx

import React, {Component} from "react";


class DashboardComponent extends Component {
    render() {
        return(
            <div>
                Dashboard 페이지
            </div>
        )
    }
}

export default DashboardComponent

 

router 를 이용해 페이지를 연결한다

TopMenuComponent.jsx

import React, {Component} from "react";
import {Navbar} from "react-bootstrap";
import {BrowserRouter as Router, Route} from "react-router-dom";

import MainComponent from './MainComponent'
import DashboardComponent from './DashboardComponent'

class TopMenuComponent extends Component {
    render() {
        return (
            <Router>
                <Navbar
                    bg="dark"
                    variant="dark"
                    className="mb-4" >
                    <Navbar.Brand href="/">
                        Home
                    </Navbar.Brand>
                    <Navbar.Brand href="/main">
                        Main
                    </Navbar.Brand>
                    <Navbar.Brand href="/dashboard">
                        Dashboard
                    </Navbar.Brand>
                </Navbar>

                <Route path="/main" component={MainComponent} />
                <Route path="/dashboard" component={DashboardComponent} />
            </Router>
        )
    }
}

export default TopMenuComponent;

 

설정된 것을 App.js 에 넣어 마무리한다

import React from 'react';
import 'bootstrap/dist/css/bootstrap.min.css'
import './App.css';

import TopMenuComponent from "./component/TopMenuComponent";

function App() {
  return (
      <div className="App">
          <div>
              <TopMenuComponent>
              </TopMenuComponent>
          </div>
      </div>
  );
}

export default App;

 

react 서버를 새로 실행한다

npm start

그럼 이제 axios를 이용해 호출해보자

 

 

 

# 서버와 통신하기 (API)

ApiController 를 생성하여 매핑한다

@RestController
public class ApiController {

    @GetMapping("/api/hello")
    public HashMap hello() {
        HashMap result = new HashMap();
        result.put("message", "안녕하세요");

        return result;
    }
}

 

MainComponent.jsx 파일을 열어 통신코드를 추가한다.

import React, {Component} from "react";
import axios from "axios";

class MainComponent extends Component {    
    constructor(props) {
        super(props)
        this.state = {
            message: ""
        }
    }

    componentDidMount() {
        this.getApi();
    }

    getApi = () => {
        axios.get("http://localhost:8080/api/hello")
            .then(res => {
                console.log(res);
                this.setState({
                    message: res.data.message
                })
            })
            .catch(res => console.log(res))
    }

    render() {
        return(
            <div>
                Main 페이지
            </div>
        )
    }
}

export default MainComponent

 

브라우저에서 확인하면 브라우저 로그에 다음 에러를 확인할 수 있다

Access to XMLHttpRequest at 'http://localhost:8080/api/hello' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
MainComponent.jsx:11 Error: Network Error
    at createError (createError.js:16)
    at XMLHttpRequest.handleError (xhr.js:83)

 

 

CORS는 Cross Origin Resource Sharing의 약자로 도메인 또는 포트가 다른 서버의 자원을 요청하는 매커니즘이다

상세한것은 아래 페이지 참조한다

 

https://velog.io/@wlsdud2194/cors

 

CORS에 대한 간단한 고찰

이 포스트에서는 CORS에 대한 이슈에 대해서 다뤄볼려고 합니다. 웹 개발을 하다보면 한번쯤 겪게되는 이슈로 클라이언트와 서버의 오리진이 다를 때 발생하는 이슈입니다. 🤔 CORS? 크로스 도메인? CORS는 Cross Origin Resource Sharing의 약자로 도메인 또는 포트가 다른 서버의 자원을 요청하는 매커니즘을 말합니다. 이...

velog.io

 

이것을 해제하기 위해선 서버측에 작업이 필요하다

WebConfig.java 를 생성하여 다음과 같이 설정한다

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry
                .addMapping("/api/**")
                .allowedOrigins("http://localhost:3000")
        ;
    }
}

 

 

서버를 새로 실행한 후 새로고침해보면 다음처럼 데이터를 받아오는데 성공한다

{data: {…}, status: 200, statusText: "", headers: {…}, config: {…}, …}
data: {message: "안녕하세요"}
status: 200
statusText: ""
headers: {content-type: "application/json"}
config: {url: "http://localhost:8080/api/hello", method: "get", headers: {…}, transformRequest: Array(1), transformResponse: Array(1), …}
request: XMLHttpRequest {readyState: 4, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, onreadystatechange: ƒ, …}
__proto__: Object

 

 

 

 

# frontend 와 backend 같이 build 하기

frontend 빌딩하기

# 터미널로 frontend/my-app 폴더로 접근한 후
npm run build

 

 

실행하고 나면 아래와같이 폴더가 생성된다

 

다만 이대로 bootJar 로 만들면 frontend 의 build 내용이 포함되지 않는다

그래서 gradle 설정을 변경해야 한다

build.gradle 파일은 열어 다음을 수정하면 npm run build 와 자바 컴파일을 동시에 한다

plugins {
    ...

    # moowork.node 추가
    id "com.moowork.node" version "1.3.1"
}

apply plugin: "com.moowork.node"
...

# (아래 추가)

def webappDir = "$projectDir/frontend/my-app"

task appNpmInstall(type: NpmTask) {
    workingDir = file("${webappDir}")
    args = ["run", "build"]
}

task copyWebApp(type: Copy) {
    from 'frontend/my-app/build'
    into "build/resources/main/static"
}

copyWebApp.dependsOn(appNpmInstall)
compileJava.dependsOn(copyWebApp)

 

이제 bootJar 를 생성하면 build/resources/main/static 에 파일이 생성되 있음을 확인할 수 있다.

 

build/libs 폴더에 있는 jar 파일을 실행하면 localhost:8080 으로도 react가 연결됨을 확인할 수 있다.

 

 

문제는 Main, Dashboard 의 연결이 안된다는 점이다.

다음과 같이 404에러가 발생한다. Spring 에서 해당 URL Mapping 을 찾을 수 없기 때문이다

 

서버쪽에서 WebController를 생성하여 error가 발생해도 index.html 로 이동하도록 유도한다

 

WebController.java

 

@Controller
public class WebController implements ErrorController {
    @GetMapping({"/", "/error"})
    public String index() {
        return "index.html";
    }

    @Override
    public String getErrorPath() {
        return "/error";
    }
}

 

이제 다시 bootJar 를 실행하고 만들어진 jar 파일을 실행해보자

java -jar build/libs/react-0.0.1-SNAPSHOT.jar

 

제대로 이동되는 것을 확인할 수 있다.

 

 

 

 

github:

https://github.com/lemontia/springboot_react

 

lemontia/springboot_react

Contribute to lemontia/springboot_react development by creating an account on GitHub.

github.com