본문 바로가기
  • Praha
Study/Spring

Spring Boot Gradle 멀티모듈 프로젝트 생성/빌드

by Richard.J.78 2024. 10. 24.

이번에는 지난번 spring boot maven 멀티모듈 생성/빌드 단계 정리에 이어서  java Gradle 멀티 모듈 생성 및 빌드 까지의 단계를 정리합니다. 

 

스펙

  • IDE: IntelliJ
  • JDK: 17(Temurin 17.0.13)
  • spring boot: 3.3.4
  • Gradle: 
  • mybatis: 3.0.3
  • mysql: 8.0.36

모듈

  • gradle_demo
    • demo_core: DB, Repository, Service, Com
    • demo_api: Controller

 

#1.프로젝트 생성

  • 프로젝트명: gradle_demo / 언어: Java / 타입: Gradle-Goovy
  • 그룹: com.example
  • 아티팩트: gradle_demo
  • 패키지명: com.example.gradledemo

새 프로젝트 생성

 

 

부모프로젝트 종속성 설정(하위 자식 모듈에 공통으로 주입)

  • Spring Boot DevTools
  • Lombok
  • Validation

 

 

 

#2.하위모듈 생성

  • 프로젝트 > 새로 만들기 > 모듈 > Java

모듈 생성

 

  • demo_core
    • 시스템빌드: Gradle
    • JDK: 17
    • Gradle DSL: Groovy
    • 상위: gradle_demo

신규 모듈 demo_core 생성

 

  • demo_api
    • 시스템빌드: Gradle
    • JDK: 17
    • Gradle DSL: Groovy
    • 상위: gradle_demo

신규 모듈 demo_api 생성

 

 

 

#3.부모 프로젝트 src 삭제

  • 부모 프로젝트는 자식 모듈의 dependencis만 관리하기 때문에 별도의 Java 소스가 필요없어 삭제 처리

src 삭제

 

 

#4.부모 프로젝트(rootProject gradle_demo) settings.gradle 확인

부모프로젝트(root)settings.gradle파일에 생성한 하위 모듈을 include 하고 있는지 확인(없으면 하위 모듈 include)

rootProject / settings.gradle

 

 

#5.부모 프로젝트 build.gradle 설정(주석 참고)

  • 부모 프로젝트 build.gradle
plugins {
    id 'java'
    id 'org.springframework.boot' version '3.3.4'
    id 'io.spring.dependency-management' version '1.1.6'
}
//root project 실행가능 Jar 파일 생성 기능 OFF
bootJar.enabled = false

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

//전체 프로젝트 공통 설정
allprojects {
    group = 'com.example'
    version = '0.0.1-SNAPSHOT'

    java {
        sourceCompatibility = JavaVersion.VERSION_17
        targetCompatibility = JavaVersion.VERSION_17
    }

    repositories {
        mavenCentral()
    }
}

//하위 프로젝트 공통 설정
subprojects {
    apply plugin: 'java'
    apply plugin: 'org.springframework.boot'
    apply plugin: 'io.spring.dependency-management'

    dependencies {
        implementation 'org.springframework.boot:spring-boot-starter-validation' 

        compileOnly 'org.projectlombok:lombok'
        developmentOnly 'org.springframework.boot:spring-boot-devtools'
        annotationProcessor 'org.projectlombok:lombok'

        testImplementation 'org.springframework.boot:spring-boot-starter-test'
        testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
    }

    tasks.named('test') {
        useJUnitPlatform()
    }
}

//하위 프로젝트 demo_core 개별 설정(core 모듈은 실행가능한 Jar 는 필요 없지만, api 모듈이 참조할 plain.jar 는 생성해줘야함)
project(':demo_core') {
    jar {
        enabled = true
        manifest {
            attributes('Start-Class': 'com.example.demo_api.GradleDemoApplication') //필수!! 메인 클래스 지정
        }
    }

    bootJar {
        enabled = false
    }
}

//하위 프로젝트 demo_api 개별 설정(실행 가능한 Jar 생성 / demo_core 모듈 종속성 설정)
project(':demo_api') {
    jar {
        enabled = false
    }

    bootJar {
        enabled = true
    }

    dependencies {
        implementation(project(':demo_core'))
    }
}

 

#6.demo_core build.gradle 설정

  • demo_core build.gradle
    • 기본적인 설정은 부모프로젝트 build.gradle에 넣어놨기 때문에 demo_core 모듈에서는 DB, mybatis 관련 dependencies설정만 추가
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
    implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.3'

    runtimeOnly 'com.mysql:mysql-connector-j'

    testImplementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter-test:3.0.3'
    testImplementation platform('org.junit:junit-bom:5.10.0')
    testImplementation 'org.junit.jupiter:junit-jupiter'

}

 

#7.demo_api pom.xml 설정

  • demo_api build.gradle
    • 기본적인 설정은 부모프로젝트 build.gradle에 넣어놨기 때문에 demo_api 모듈에서는 필요한 web, thymeleaf dependencies 설정만 추가
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
    testImplementation platform('org.junit:junit-bom:5.10.0')
    testImplementation 'org.junit.jupiter:junit-jupiter'
}

 

 

#8.패키지 디렉토리 / 파일 생성

  • 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
ㄴ static
ㄴ templates
ㄴ application.yaml

 

#9.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

 

 

#10. 테스트 코드 작성

아래 코드는 간단하게 회원 이름을 조회하는 코드입니다.

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.demo_core.mapper")
public class AppConfig {
    private final TestMapper testMapper;

    @Bean
    public TestRepository testRepository() {
        return new TestRepository(testMapper);
    }

    @Bean
    public TestService testService() {
        return new TestService(testRepository());
    }
}
  • GradleDemoApplication(main class)
package com.example.demo_api;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication(scanBasePackages = "com.example")
public class GradleDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(GradleDemoApplication.class, args);
        System.out.println("== start gradle demo 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);
    }
}

 

#11.Spring Boot 실행 테스트(IntelliJ 로컬머신)

  • server port : 9090
  • 인텔리제이에서 메인클래스를 실행하여 localhost:9090/api/test/3에서 테스트 진행(포스트맨)

 

 

#12.Gradle build

  • 빌드 명령어
./gradlew clean build

 

 

#12. jar 파일 실행

java -jar demo_api-0.0.1-SNAPSHOT.jar

 

 

postman으로 테스트 성공(200 OK)

localhost:9090/api/test/4

 

 

이상 spring boot gradle 멀티 모듈 프로젝트 생성 및 빌드 과정에 대한 정리를 마치겠습니다.

아주 기본적인 프로젝트 설정이기 때문에 위 내용을 참고하여 프로젝트에 응용하시면 될 것 같습니다.

참고로 시간이 지날수록 spring boot 및 각 dependency 버전이 변경되기 때문에 자신의 상황에 맞춰 업데이트 해서 진행하셔야 합니다. 

 

댓글