spring boot를 이용하여 java maven 멀티 모듈 생성 및 빌드 까지의 단계를 정리합니다.
스펙
- IDE: IntelliJ
- JDK: 17(Temurin 17.0.13)
- spring boot: 3.3.4
- maven: 3.9.8
- mybatis: 3.0.3
- mysql: 8.0.36
모듈
- mvdemo
- demo_core: DB, Repository, Service, Com
- demo_api: Controller
#1.프로젝트 생성
- 프로젝트명: mvdemo / 언어: Java / 타입: Maven
- 그룹: com.example
- 아티팩트: mvdemo
- 패키지명: com.example.mvdemo
부모프로젝트 종속성 설정(하위 자식 모듈에 공통으로 주입)
- Spring Boot DevTools
- Lombok
- Validation
#2.하위모듈 생성
- 프로젝트 > 새로 만들기 > 모듈 > Java
- demo_core
- 시스템빌드: Maven
- JDK: 17
- 상위: mvdemo
- demo_api
- 시스템빌드: Maven
- JDK: 17
- 상위: mvdemo
#3.부모 프로젝트 src 삭제
- 부모 프로젝트는 자식 모듈의 dependencis만 관리하기 때문에 별도의 Java 소스가 필요없어 삭제 처리
#4.부모 프로젝트 pom.xml설정
- spring-boot-starter dependency 추가
- groupid: org.springframework.boot
- artifactId: spring-boot-starter
- 위 dependency를 추가 하지 않을 경우 빌드 후 java -jar demo_api.jar 실행시 아래 에러를 만날 수 있음
Exception in thread "main" java.lang.NoClassDefFoundError: org/springframework/boot/SpringApplication
at com.example.demo_api.DemoApiApplication.main(DemoApiApplication.java:13)
Caused by: java.lang.ClassNotFoundException: org.springframework.boot.SpringApplication
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:525)
- build 설정 추가
- 모듈 빌드의 경우 pluginManagement를 사용해야함
- maven-compiler-plugin : 메이븐 컴파일러 플러그인을 추가 안할 경우 아래 에러를 만나게됨
부모 프로젝트 pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>mvdemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>mvdemo</name>
<description>mvdemo</description>
<modules>
<module>demo_core</module>
<module>demo_api</module>
</modules>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.13.0</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
#5.demo_core pom.xml설정
- dependency 추가
- Jdbc api / Spring Data Jdbc
- Mybatis Framework
- MySQL Driver
- demo_core 모듈은 DB, Repository, Service로직을 처리하는 모듈로 별도의 실행 파일을 만들지 않기 때문에 Build 설정을 추가 하지 않습니다.
- demo_core pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>mvdemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>demo_core</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter-test</artifactId>
<version>3.0.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
</dependencies>
</project>
#6.demo_api pom.xml 설정
- packaging타입 추가
- <packaging>jar</packaging>
- dependency 추가
- demo_core : demo_api모듈은 demo_core모듈을 참조하기 때문에 반드시 추가
- Spring web
- Thymeleaf (선택사항 / 프론트 화면을 같이 구성할 경우에만 추가)
- build 설정 추가
- resource: maven표준 설정인 src/main/resources 사용
- sourceDirectory: maven표준 설정인 src/main/java 사용
- plugin
- spring-boot-maven-plugin
- maven-jar-plugin(중요함 / 여기서 삽질 천번 ㅋㅋ)
- 모듈 빌드시 manifest정보를 지정해줘야 오류가 안남
- demo_api pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>mvdemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>demo_api</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo_api</name>
<packaging>jar</packaging>
<description>demo_api</description>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>demo_core</artifactId>
<version>0.0.1-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<sourceDirectory>src/main/java</sourceDirectory>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>3.3.4</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.4.2</version>
<configuration>
<archive>
<manifest>
<mainClass>com.example.demo_api.DemoApiApplication</mainClass>
<addClasspath>true</addClasspath>
<addExtensions>true</addExtensions>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>
#7.패키지 디렉토리 / 파일 생성
- demo_core
src/main/java/com/example/demo_core
ㄴ common
ㄴ App.config
ㄴ entity
ㄴ Member(class)
ㄴ mapper
ㄴ TestMapper(interface)
ㄴ repository
ㄴ TestRepo(class)
ㄴ service
ㄴ TestService(class)
src/main/resources/com/example/demo_core
ㄴ mapper
ㄴ testMapper.xml
ㄴ application.properties
- demo_api
src/main/java/com/example/demo_api
ㄴ controller
ㄴ TestController(class)
ㄴ DemoApiApplication(main class)
src/main/resources/com/example/demo_api
ㄴ static
ㄴ templates
ㄴ application.yaml
#8.application 설정
- demo_core모듈의 resources에 application.properties 파일을 생성하여 mybatis / db connect 설정 정보를 추가 한다.
spring.application.name=demo_core
mybatis.mapper-locations=classpath:com/example/demo_core/mapper/**/*.xml
mybatis.type-aliases-package=com.example.demo_core
mybatis.configuration.map-underscore-to-camel-case=true
mybatis.configuration.auto-mapping-unknown-column-behavior=warning
mybatis.configuration.auto-mapping-behavior=partial
logging.level.com.payco.spa_core.mapper=trace
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/squid_proxy?serverTimezone=Asia/Seoul&characterEncoding=UTF-8
spring.datasource.username=test
spring.datasource.password=demo123
logging.level.org.springframework.jdbc=trace
- demo_api모듈의 resources에 application.yaml 파일을 생성하여 서버 정보, 타임리프 정보를 추가 한다.
- 참고로 타임리프 classpath 정보가 없을 경우 에러를 만나게 된다.
spring:
application:
name: demo_api
mvc:
static-path-pattern: /static/**
thymeleaf:
prefix: classpath:/templates/
suffix: .html
cache: false
server:
port: 9090
#9. 테스트 코드 작성
아래 코드는 간단하게 회원 이름을 조회하는 코드입니다.
mysql db table에 샘플 데이터를 준비해주세요.(별도 설명 X)
- demo_core / entity / Member(class)
package com.example.demo_core.entity;
import lombok.Data;
@Data
public class TestMember {
private Long id;
private String name;
public TestMember(Long id, String name) {
this.id = id;
this.name = name;
}
}
- demo_core / mapper / TestMapper(interface)
package com.example.demo_core.mapper;
import com.example.demo_core.entity.TestMember;
import org.apache.ibatis.annotations.Mapper;
import java.util.Optional;
@Mapper
public interface TestMapper {
Optional<TestMember> findById(Long id);
}
- demo_core / mapper / testMapper.xml (resource)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo_core.mapper.TestMapper">
<select id="findById" parameterType="Long" resultType="TestMember">
select * from test_member where id = #{id}
</select>
</mapper>
- demo_core / repository / TestRepository(class)
package com.example.demo_core.repository;
import com.example.demo_core.entity.TestMember;
import com.example.demo_core.mapper.TestMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
@RequiredArgsConstructor
public class TestRepository {
private final TestMapper testMapper;
public Optional<TestMember> findById(Long id) {
return testMapper.findById(id);
}
}
- demo_core / service / TestService(class)
package com.example.demo_core.service;
import com.example.demo_core.entity.TestMember;
import com.example.demo_core.repository.TestRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.Optional;
@Service
@RequiredArgsConstructor
public class TestService {
private final TestRepository testRepository;
public String findById(Long id) {
Optional<TestMember> member = testRepository.findById(id);
return member.map(TestMember::getName).orElse(null);
}
}
- demo_core / common / AppConfig(class)
package com.example.demo_core.common;
import com.example.demo_core.mapper.TestMapper;
import com.example.demo_core.repository.TestRepository;
import com.example.demo_core.service.TestService;
import lombok.RequiredArgsConstructor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@RequiredArgsConstructor
@MapperScan(basePackages = "com.example")
public class AppConfig {
private final TestMapper testMapper;
@Bean
public TestRepository testRepository() {
return new TestRepository(testMapper);
}
@Bean
public TestService testService() {
return new TestService(testRepository());
}
}
- DemoApiApplication(main class)
package com.example.demo_api;
import com.example.demo_core.common.AppConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Import;
@Import(AppConfig.class)
@SpringBootApplication(scanBasePackages = "com.example")
public class DemoApiApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApiApplication.class, args);
System.out.println("== start demo Api application ==");
}
}
- TestController(class)
package com.example.demo_api.controller;
import com.example.demo_core.service.TestService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("api")
public class TestController {
private final TestService testService;
@GetMapping("/test/{id}")
public String test(@PathVariable("id") Long id) {
log.info("TestController#test({})", id);
return testService.findById(id);
}
}
#10.Spring Boot 실행 테스트(IntelliJ 로컬머신)
- server port : 9090
- 인텔리제이에서 메인클래스를 실행하여 localhost:9090/api/test/2에서 테스트 진행(포스트맨)
#11.maven build
- 빌드 명령어(테스트 스킵)
./mvnw clean package install -pl demo_api -am -Dmaven.test.skip=true
#12. jar 파일 실행
java -jar demo_api-0.0.1-SNAPSHOT.jar
postman으로 테스트 성공(200 OK)
localhost:9090/api/test/2
이상 spring boot maven 멀티 모듈 프로젝트 생성 및 빌드 과정에 대한 정리를 마치겠습니다.
아주 기본적인 프로젝트 설정이기 때문에 위 내용을 참고하여 프로젝트에 응용하시면 될 것 같습니다.
참고로 시간이 지날수록 spring boot 및 각 dependency 버전이 변경되기 때문에 자신의 상황에 맞춰 업데이트 해서 진행하셔야 합니다.
'Study > Spring' 카테고리의 다른 글
Spring Boot에서 예외(Exception) 처리하기 (0) | 2024.10.26 |
---|---|
Spring Boot Gradle 멀티모듈 프로젝트 생성/빌드 (1) | 2024.10.24 |
댓글