Packaging CRA on Spring Boot
Packaging CRA on Spring Boot๐ฅ
Written By Jun Park, VCANUS
1๊ฐ์ Spring Boot ์๋ฒ์ REST API ์๋ฒ์ React App WAS ์๋ฒ๋ฅผ ํฌํจํ๋ ๋ฐฉ๋ฒ
๐ Spring Boot Setup
1. Init Spring Boot Project
- spring initializr์์ ํ๋ก์ ํธ ์์ฑ
- ์ด ๊ธ์์ ์ฌ์ฉํ๋ ์ค์
- Project: Maven Project
- Language: Java
- Spring Boot: 2.4.5(as default)
- Project Metadata
- Packaging: Jar
- Java: 8
- Dependencies
- Spring Web
- Rest Repositories
- Spring Data JPA
- H2 Database
2. Rest API ๊ฐ๋ฐ
application.properties
์ Rest API base path ์ถ๊ฐ
// src/main/resources/application.properties
// root๋ React๋ฅผ ํธ์คํธํ ๊ฒ์ด๊ธฐ ๋๋ฌธ์
// Rest API์ root๋ฅผ /api๋ก ์ค์ ํ์ฌ /api ์ดํ ์์ฒญ์ Rest ์์ฒญ์ผ๋ก ์ฒ๋ฆฌ
spring.data.rest.base-path=/api
// ๋๋ ์๋์ผ๋ก ๊ฐ ์์ฒญ๋ง๋ค ์ง์
// ์ด ๊ธ์ base-path๋ฅผ ์ค์ ํ ์์ ๋ฐฉ๋ฒ์ ์ฌ์ฉํ๋ค๊ณ ๊ฐ์
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ProductController {
private final ProductService service;
@Autowired
public ProductController(ProductService service) {
this.service = service;
}
@GetMapping("/api/product/1") // ์ฃผ์๋ฅผ ๋ช
์
public String selectProduct() {
return service.select(1);
}
// ๊ทธ ์ธ CRUD Mapping...
}
- Entity, Repository ๊ฐ๋ฐ
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import java.util.Objects;
@Entity
public class User {
private @Id @GeneratedValue Long id;
private String name;
// ๊ทธ ์ธ constructor, getter, setter...
}
import org.springframework.data.repository.CrudRepository;
public interface UserRepository extends CrudRepository<User, Long> {}
- Repository ~ Database ์ฐ๊ฒฐ
// ํ
์คํธ๋ฅผ ์ํ ๋ฐ๋ชจ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฌ์ฉ
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class DatabaseLoader implements CommandLineRunner {
private final UserRepository repository;
@Autowired
public DatabaseLoader(UserRepository repository) {
this.repository = repository;
}
@Override
public void run(String... args) throws Exception {
this.repository.save(new User("Paul"));
this.repository.save(new User("Tom"));
}
}
- ํ ์คํธ
// after run spring boot app
$ curl localhost:8080/api/users
// Result: two users, Paul and Tom
๐ React Setup
1. React ์์ฑ
- Spring boot project root์์ Create React App
$ npx create-react-app frontend
2. React ๊ฐ๋ฐ
- API ์์ฒญ
// npm install axios OR yarn add axios
import axios from 'axios';
const Users = () => {
const [users, setUsers] = useState();
useEffect(() => {
axios.get('/api/employees')
.then(response => setUsers(response.data._embedded.employees));
return () => setUsers([])
}, []);
return (
<React.Fragment>
<h2>Users</h2>
<UserList users={users} />
</React.Fragment>
);
}
3. ํ ์คํธ
- ํ ์คํธ์ฉ proxy ์ค์
// frontend/src/**/setupProxy.js
// npm install http-proxy-middleware OR yarn add http-proxy-middleware
const { createProxyMiddleware } = require('http-proxy-middleware');
// npm start ์ ํฌํธ๋ 3000์ผ๋ก tomcat ํฌํธ 8080์ API ์์ฒญ ์ CORS ์๋ฐ
// '/api' ํ์ ์ฃผ์๋ก ์์ฒญ ์ 8080์ผ๋ก ์์ ์ ํ
// ๋ฐฐํฌ ์์๋ ๊ฐ์ tomcat ์๋ฒ ๋ด์์ ์์ฒญํ๊ธฐ์ ๋ฌด๊ด
module.exports = (app) => {
app.use(
createProxyMiddleware('/api', {target: 'http://127.0.0.1:8080'})
);
};
- ํ ์คํธ
$ npm start
๐ Packaging
1. pom.xml ์์
- frontend-maven-plugin ์ถ๊ฐ
<plugins>
...
<plugin>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<!-- Use the latest released version:
https://repo1.maven.org/maven2/com/github/eirslett/frontend-maven-plugin/ -->
<version>1.11.3</version>
<configuration>
<workingDirectory>frontend</workingDirectory>
<installDirectory>target</installDirectory>
</configuration>
<executions>
<execution>
<id>install node and npm</id>
<goals>
<goal>install-node-and-npm</goal>
</goals>
<configuration>
<!-- node.js, npm version -->
<nodeVersion>v14.16.1</nodeVersion>
<npmVersion>7.10.0</npmVersion>
</configuration>
</execution>
<execution>
<id>npm install</id>
<goals>
<goal>npm</goal>
</goals>
<configuration>
<arguments>install</arguments>
</configuration>
</execution>
<execution>
<id>npm run build</id>
<goals>
<goal>npm</goal>
</goals> <configuration>
<arguments>run build</arguments>
</configuration>
</execution>
</executions>
</plugin>
...
</plugins>
- React build ํ์ผ ์ด๋ ์๋ํ๋ฅผ ์ํ maven-antrun-plugin ์ถ๊ฐ
<plugins>
...
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<phase>generate-resources</phase>
<configuration>
<!-- frontend/build ์ดํ ํ์ผ์ Spring boot build ๊ฒฝ๋ก์ classes/static์ผ๋ก ๋ณต์ฌ-->
<target>
<copy todir="${project.build.directory}/classes/static">
<fileset dir="${project.basedir}/frontend/build"/>
</copy>
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
...
</plugins>
2. build & package
maven clean
maven install
// target ํด๋์ jar ์์ฑ
3. run
$ java -jar target/result.jar
โ๏ธ ํ์ด์ง ์ด๋์ react-router-dom ํ์ฉ
import {
BrowserRouter as Router,
Switch,
Route,
Link
} from 'react-router-dom';
const App = () => {
return (
<div className='App'>
<Router>
<div>
<nav>
<ul>
<li>
<Link to='/'>Home</Link>
</li>
<li>
<Link to='/simulator'>Simulator</Link>
</li>
<li>
<Link to='/users'>Users</Link>
</li>
</ul>
</nav>
{/* A <Switch> looks through its children <Route>s and
renders the first one that matches the current URL. */}
<Switch>
<Route path='/'>
<Home />
</Route>
<Route path='/simulator'>
<Simulator />
</Route>
<Route path='/users'>
<Users />
</Route>
</Switch>
</div>
</Router>
</div>
);
};
Leave a comment