스프링부트, 리액트 프로젝트 함께 빌드하기

JeongMin·2019년 11월 5일
5
post-thumbnail

Webtoon 토이프로젝트를 진행하다보니 프론트엔드 서버(react)와 백엔드 서버(를 따로 빌드하고, 실행하는 것이 약간 불편하게 느껴지기 시작했다..
그래서 'Spring 프로젝트에 프론트엔드 파일들을 넣어두고 Gradle을 통해서 build 한번에 프론트와 백엔드 모두 Build 할 수 있지않을까?' 라는 생각이 들어 서칭을 시작해보았다.
아니나다를까 이미 그런 생각을 하신 분들이 많았고, 좋은 예제들이 있었다. 그래서 찾았던 예제를 바탕으로 내가 직접 적용해본 과정을 기록해보기로 했다.

해당 프로젝트의 전체 코드는 이 곳에서 볼 수 있습니다.

1. Frontend 파일 Spring Boot Project로 옮기기

## spring projcet root

|- frontend
|    - webtoon
|        - ...
|- src
|    - main
|        - java
|            - com.essri.webtoon
|                - ...
  • 내가 옮길 프론트엔드 프로젝트 디렉토리의 이름은 webtoon, 위의 구조처럼 우선 스프링 프로젝트의 최상단에 frontend 디렉토리를 생성하고, frontend 폴더 하위로 옮겨주었다.

2. frontend 프로젝트의 package.json 수정하기

{
  "name": "webtoon-react",
  "version": "1.0.0",
  "description": "",
  "main": "App.js",
  "dependencies": {
    "acorn": "^7.1.0",
    "bootstrap": "^4.3.1",
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "react-router": "^5.0.1",
    "react-router-dom": "^5.0.1",
    "react-scripts": "3.0.1",
    "reactstrap": "^8.1.1",
    "stringquery": "^1.0.8",
    "webpack": "4.29.6"
  },
  "devDependencies": {},
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "author": "Mark",
  "license": "ISC",
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}
  • 내가 진행했던 package.json 파일은 위와 같다. 사실 크게 수정할 내용은 없으며 기본적으로 create-react-app을 했을때 적용되어지는 라이브러리들만 포함하고 있으면 문제없다.

3. Gradle 수정 및 Task 추가하기

3.1 nodejs 의존 추가

classpath("com.moowork.gradle:gradle-node-plugin")
  • gradle build를 통해서 react project도 빌드되려면 기본적으로 nodejs가 필요하다. 그래서 위와 같이 buildscript에 의존을 추가해주어서 nodejs를 설치할 수 있게한다.

3.2 Task 추가

def webappDir = "$projectDir/frontend/webtoon"

// node 버전 및 npm, node 설치파일 저장 디렉토리 명시
node {
    version = '12.6.0'
    download=true
    workDir = file("${project.buildDir}/nodejs")
    npmWorkDir=file("${project.buildDir}/npm")
}

// npm install 과정
task appNpmInstall(type: NpmTask) {
    workingDir = file("${project.projectDir}/frontend/webtoon")
    args = ["install"]
}

// yarn build
task yarnBuild(type: YarnTask) {
    workingDir = file("${project.projectDir}/frontend/webtoon")
    args = ['build']
}

// 빌드된 결과 resources로 이동
task copyWebApp(type: Copy) {
    from "frontend/webtoon/build"
    into 'build/resources/main/static/.'
}

yarnBuild.dependsOn appNpmInstall
copyWebApp.dependsOn yarnBuild
compileJava.dependsOn copyWebApp
  • react 프로젝트를 빌드하기위해서 npm installreact build를 진행하고, 그 결과를 백엔드에서 사용할 수 있게 resources 폴더로 옮겨주는 과정을 Gradle에 작성해주었다.

4. Build 하기 !!

  • 프로젝트 최상단에서 gradle clean build를 해주게 되면 빌드가 진행된다. (test코드를 제외하고싶다면 -x test를 붙여주면 된다.)

5. 발생했었던 예외상황들..

5.1 travis build error

  • 이 프로젝트는 travis CI를 통해 빌드하고, AWS의 CodeDeploy를 통해 배포하도록 되어있었다. 여기에서 프로젝트 파일들을 압축할 때 node_modules 폴더를 포함시키는 바람에 빌드로그가 너무 많이 나오게 되어서 빌드를 실패했다. => 압축할 때 -x 옵션으로 불필요한 파일들을 제외시켜주었다.

5.2 Route 문제

  • 처음에 전부 파일들을 병합하고 빌드하는데까지 무리없었지만 ViewResolver를 분명히 서버코드에 작성하지 않았었는데 어떻게 메인페이지가 출력된다는걸까? 하고 의심했었다.
  • 내 리액트 프로젝트는 페이지가 하나 뿐이라서 route를 설정을 하지않아도 될 줄 알았다.. 설정 전에는 역시 리액트 프로젝트의 메인페이지에 제대로 접근하지못했고 계속 삽질만 하다가.. 깨닫게 되었다..

5.3 CodeDeploy 에러

deploy-error.png

  • 처음에는 빌드 후 배포가 잘되더니 계속 프로젝트파일을 압축 후 CodeDeploy를 통해 배포하는 부분에서 뻗어버렸다.. 아무래도 프론트엔드 프로젝트를 합쳐서 빌드한 후로 부터 에러가 나기 시작했기 때문에 배포하는 용량적인 측면이 아닐까하고 생각하고, 서칭을 시작했다.
  • 결국 찾은 해결방법은 공식문서 였다.

6. 후기..

  • 아무리 봐도 문제점이 없어보일 때는 잠깐 쉬고, 보면 보일 때가 더 많은 것 같다.. 정말 간단한 문제들이였지만 이번에는 삽질을 너무 많이 했다..

profile
안녕하세요 https://ggomi.github.io

5개의 댓글

comment-user-thumbnail
2020년 1월 4일

spa 로 구성된 react를 스프링과 연동하여 사용하고 싶은데 저렇게 빌드한 다음 어떻게 해야 하나요?
일단 war 파일 실행하면 index 페이지는 접속됩니다.
그러나 react router에 매핑 되어 있는 다른 주소들은 호출 해도 없는 페이지라고 나옵니다 ㅠㅠ

1개의 답글
comment-user-thumbnail
2020년 4월 17일

안녕하세요! 많은 도움을 받고 있습니다. 위에서는 gradle로 하셨지만 maven으로 같은 과정을 할 때 궁금한게 있어서 혹시 답변주실 수 있을까해서 글을 남깁니다.
저는 webpack으로 target/class/static 밑에 bundle.js를 떨어뜨리는 방식으로 하려고 하는데, 리액트 프로젝트를 하나의 bundle.js로 만들어서 떨어뜨리는게 가능한가요? 만약 방향이 틀렸다면 웹팩을 사용해서 스프링부트의 target 밑에 넣으려면 어떻게 하면 좋을까요? 계속 삽질하다가 질문합니다 ㅠㅠ

1개의 답글