티스토리 뷰

 

Gradle 멀티 프로젝트 관리

안녕하세요! 이번 시간에는 아시는 분들은 거의다 아시는(!?) Gradle을 이용한 멀티 프로젝트(모듈) 관리에 대해 소개하려고 합니다. 모든 코드는 Github에 있으니 참고하셔서 보시면 더 좋으실 것

jojoldu.tistory.com

이동욱 님의 블로그에서 정말 친절한 설명과 함께 Gradle 세팅을 진행해보았다

진행 중인 개인 프로젝트는 멀티 모듈로 나눌 필요가 없는 규모다

요즘 관심사가 스프링이 가진 다양한 기능들을 접해보는 데 있기 때문에 경험 삼아 시도했다

 

혼자 진행하고 있는 프로젝트이기 때문에 front까지 루트에서 모듈로 관리하기로 했고

현재는 api, common, service, vue로 나눠둔 상태다, 이 구조는 딱히 좋다고 말할 수 없다

편의상 front / back을 한 곳에 몰아넣은 것뿐이고 협업을 하거나 개별적인 빌드를 위해서는

모듈을 분리하는 편이 훨씬 낫다

 

그럼에도 불구하고 왜 넣었는가 하면 스프링부트에서 npm plugin을 이용해 front까지 통합 빌드 후

결과물을 backend module에 resources/static/ 경로 밑에 둬서 서버에서 모든 처리를 가능하게 할 수 있기 때문이다

분리할 필요가 생긴다면 front 모듈을 그저 프로젝트 외부로 옮기기만 하면 된다

추후 Spring Batch 를 학습하면서 batch 모듈을 추가할 계획이다

 

 

실습해볼 때 폴더 구조, 파일 이름을 바꿔서 해보는 게 도움이 많이 되는 것 같다

그래야 빌드 시에 에러가 나고! 에러 스택 트레이스를 읽어보며

빌드가 된 부분은 왜 된 것이고, 안 된 부분은 무엇 때문에 안 된 건지 이해할 수 있었다

블로그 글들을 보면서 한 번에 이해하는 게 가장 좋겠지만 부딪혀봐야 더 기억에 잘 남는 것 같다

 

 

현재 build.gradle의 subproject에서는 뭉탱이로 dependencies를 관리하고 있는데

프로젝트 진척시키면서 개선해가야겠다

Spring Restdocs 설정까지 마쳤고, module-api에서 문서가 만들어지게끔 해놨다

 

Spring boot, Vue.js 빌드를 통합하기 위해 node plugin을 다운로드하였고 task를 설정해 front 빌드가 우선적으로 된 후 backend 빌드가 수행되도록 세팅했다 common에서는 querydsl 이용을 위한 세팅을 따로 잡아줬는데,

기본 세팅은 김영한 님의 인프런 강의를 통해 했던 설정을 그대로 가져왔는데

gradle 버전이 달라지면서 에러가 나서 검색을 통해 설정을 바꿔줬다

기존 JPA QueryDSL 설정은 나중에 또 필요할 때 써먹도록 다른 글로 옮겨 두었다

 

완성본은 아니기에 필요 없는 설정도 많이 들어가 있지만 프로젝트가 돌아갈 수 있도록 세팅해놨기 때문에

이 글을 참고한다면 필요에 맞게 라이브러리를 넣고 빼고 하시면 되겠다

 


21.12.08

 

현재는 restdocs 관련 task들은 module-api로 querydsl 관련 설정들은 module-core로 옮겨둔 상태이다

module-vue 빌드에는 npm이 아닌 yarn으로 바꿨고 등등.. 너무 많이 바뀌어서 글을 새로 써야겠다

그래도 아래에서 달라진 건 dependency와 package manager 뿐이므로 충분히 참고할 수 있을 것이다

루트 경로 build.gradle에는 프로젝트 전체에서 필요한 dependency와 build task만 남겨두도록 하는 것이 좋아 보인다

buildscript {
    ext {
        springBootVersion = '2.5.3.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}'
        classpath 'io.spring.gradle:dependency-management-plugin:1.0.11.RELEASE'
    }
}

plugins {
    id 'org.springframework.boot' version '2.5.3'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id "org.asciidoctor.jvm.convert" version "3.3.2"
    id "com.github.node-gradle.node" version "3.1.0"
    id 'java'
}

repositories {
    mavenCentral()
}

subprojects {
    apply plugin: 'java'
    apply plugin: 'org.asciidoctor.jvm.convert'
    apply plugin: 'org.springframework.boot'
    apply plugin: 'io.spring.dependency-management'

    group = 'com.woomool-market'
    version = '0.0.1-SNAPSHOT'
    sourceCompatibility = '11'
    targetCompatibility = '11'

    repositories {
        mavenCentral()
    }

    configurations {
        asciidoctorExtensions

        compileOnly {
            extendsFrom annotationProcessor
        }
    }

    ext {
        snippetsDir = file('build/generated-snippets')
    }

    dependencies {
        implementation 'org.springframework.boot:spring-boot-starter-batch'
        implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
        implementation 'org.springframework.boot:spring-boot-starter-data-redis'
        implementation 'org.springframework.boot:spring-boot-starter-hateoas'
        implementation 'org.springframework.boot:spring-boot-starter-mail'
        implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
        implementation 'org.springframework.boot:spring-boot-starter-validation'
        implementation 'org.springframework.boot:spring-boot-starter-web'
        implementation 'org.springframework.session:spring-session-core'
        implementation 'com.googlecode.json-simple:json-simple:1.1.1'
        implementation 'org.springframework.boot:spring-boot-starter-security'
        implementation 'org.mapstruct:mapstruct:1.4.1.Final'
        implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.5.8'

        // Java 8 date/time type `java.time.LocalDateTime` not supported by default:
        // add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling
        // (through reference chain: com.woomoolmarket.service.member.dto.response.MemberResponse["createdDate"])
        implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'

        // Argon2PasswordEncoder 쓸라면 넣어줘야 됨
        implementation 'org.bouncycastle:bcprov-jdk15on:1.69'

        // Restdocs 왜 인식을 못 하니 이녀석아
        asciidoctorExtensions 'org.springframework.restdocs:spring-restdocs-asciidoctor'

        developmentOnly 'org.springframework.boot:spring-boot-devtools'
        runtimeOnly 'com.h2database:h2'
        runtimeOnly 'mysql:mysql-connector-java'
        implementation 'org.mariadb.jdbc:mariadb-java-client'


        // lombok -> mapstruct -> binding 순서 안 맞추면 안 돌아감... 주의
        compileOnly 'org.projectlombok:lombok:1.18.20'
        annotationProcessor 'org.projectlombok:lombok'
        annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.1.Final'
        annotationProcessor 'org.projectlombok:lombok-mapstruct-binding:0.2.0'

        // jjwt ?!
        implementation group: 'io.jsonwebtoken', name: 'jjwt', version: '0.9.1'

        testCompileOnly 'org.projectlombok:lombok:1.18.20'
        testAnnotationProcessor 'org.projectlombok:lombok'

        testImplementation 'org.springframework.boot:spring-boot-starter-test'
        testImplementation 'org.springframework.batch:spring-batch-test'
        testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'
        testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-params', version: '5.7.2'

        // security test
        testImplementation 'org.springframework.security:spring-security-test'
    }

    test {
        outputs.dir snippetsDir
        useJUnitPlatform()
    }

    asciidoctor {
        configurations 'asciidoctorExtensions'
        inputs.dir snippetsDir
        dependsOn test
    }

    task copyDocument(type: Copy) {
        dependsOn asciidoctor
        // 소스 코드에 html파일을 복사
        from "${asciidoctor.outputDir}"
        into 'src/main/resources/static/docs'
    }

    build {
        dependsOn copyDocument
    }
}

node {
    version = '16.6.1'
    npmVersion = "7.20.3"
    workDir = file("./module-vue")
    npmWorkDir = file("./module-vue")
    nodeModulesDir = file("./module-vue")
}

task setUp(type: NpmTask) {
    description = "Install Node.js packages"
    args = ['install']
    inputs.files file('package.json')
    outputs.files file('node_modules')
}

task buildVue(type: NpmTask, dependsOn: setUp) {
    description = "Build vue.js"
    args = ['run', 'build']
}

processResources.dependsOn 'buildVue'

project(':module-api') {
    dependencies {
        implementation project(':module-common')
    }
}
project(':module-api') {
    dependencies {
        implementation project(':module-service')
    }
}
project(':module-service') {
    dependencies {
        implementation project(':module-common')
    }
}

bootJar {
    mainClassName = 'com.woomoolmarket.ModuleApiApplication'

    dependsOn asciidoctor
    copy {
        from "${asciidoctor.outputDir}"
        into 'src/main/resources/static/docs'
    }
}

 

 


2022.01.28

 

세부 모듈 build.gradle 까지 첨부하기에는 너무 방대하므로 링크로 대신함

module-coremodule-service / module-api

 

 

Project Root, build.gradle

buildscript {
    ext {
        springBootVersion = '2.5.3.RELEASE'
    }
    dependencies {
        classpath 'org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}'
        classpath 'io.spring.gradle:dependency-management-plugin:1.0.11.RELEASE'
    }
}

plugins {
    id 'org.springframework.boot' version '2.5.3'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id "org.asciidoctor.jvm.convert" version "3.3.2"
    id "com.github.node-gradle.node" version "3.1.0"
    id 'jacoco'
    id 'java'
}

repositories {
    mavenCentral()
}

group = 'com.woomool-market'
version = '1.0'
sourceCompatibility = '11'
targetCompatibility = '11'

subprojects {
    apply plugin: 'java'
    apply plugin: 'jacoco'
    apply plugin: 'org.asciidoctor.jvm.convert'
    apply plugin: 'org.springframework.boot'
    apply plugin: 'io.spring.dependency-management'

    repositories {
        mavenCentral()
    }

    // log4j2 이슈 대응하기 위한 버전 변경
    // tomcat 대신 undertow 사용해보기 위해 tomcat module 제외
    configurations {
        compileOnly {
            extendsFrom annotationProcessor
        }
        all {
            exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
            exclude module: 'spring-boot-starter-tomcat'
            resolutionStrategy.eachDependency { DependencyResolveDetails details ->
                if (details.requested.group == 'org.apache.logging.log4j') {
                    details.useVersion '2.15.0'
                }
            }
        }
    }

    ext {
        set('springCloudVersion', "2020.0.3")
    }

    dependencies {
        implementation 'org.springframework.boot:spring-boot-starter-undertow'
        implementation('org.springframework.boot:spring-boot-starter-web') {
            exclude module: 'spring-boot-starter-tomcat'
        }

        implementation 'org.springframework.boot:spring-boot-starter-mail'
        implementation 'org.springframework.boot:spring-boot-starter-cache'
        implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
        implementation 'org.springframework.boot:spring-boot-starter-data-redis'
        implementation 'org.springframework.boot:spring-boot-starter-log4j2:2.6.1'

        implementation 'org.springframework.boot:spring-boot-starter-security'
        implementation 'org.springframework.boot:spring-boot-starter-validation'
        implementation 'org.springframework.security.oauth:spring-security-oauth2'
        implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
        implementation 'org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure'
        implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'

        implementation 'org.mapstruct:mapstruct:1.4.1.Final'
        implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.7.1'

        runtimeOnly 'com.h2database:h2'
        runtimeOnly 'mysql:mysql-connector-java'
        compileOnly 'org.projectlombok:lombok:1.18.20'
        annotationProcessor 'org.projectlombok:lombok'
        annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.1.Final'
        annotationProcessor 'org.projectlombok:lombok-mapstruct-binding:0.2.0'

        testCompileOnly 'org.projectlombok:lombok:1.18.20'
        testAnnotationProcessor 'org.projectlombok:lombok'
        testImplementation 'org.springframework.boot:spring-boot-starter-test'
        testImplementation 'org.springframework.security:spring-security-test'
        testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-params', version: '5.7.2'
    }

    dependencyManagement {
        imports {
            mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
        }
    }

    jacoco {
        toolVersion = "0.8.7"
        reportsDirectory = file("$buildDir/customJacocoReportDir")
    }

    jacocoTestReport {
        reports {
            xml.enabled false
            csv.enabled false
            html.destination file("${buildDir}/jacocoHtml")
        }
    }

    test {
        useJUnitPlatform()
    }
}

// npm -> yarn 으로 변경
node {
    version = '16.6.1'
    yarnVersion = "1.22.17"
    download = true
    distBaseUrl = 'https://nodejs.org/dist'
    workDir = file("./module-vue-ts/nodejs")
    yarnWorkDir = file("./module-vue-ts/yarn")
    nodeModulesDir = file("./module-vue-ts")
}

task setUp(type: YarnTask) {
    description = "Install Node.js packages"
    args = ['install']
    inputs.files file('package.json')
    outputs.files file('node_modules')
}

task buildVue(type: YarnTask, dependsOn: setUp) {
    description = "Build vue.js"
    args = ['run', 'build']
}

processResources.dependsOn 'buildVue'

bootJar {
    mainClassName = 'com.woomoolmarket.ModuleApiApplication'
}
댓글
링크
글 보관함
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Total
Today
Yesterday