Spring

[배워서 바로 쓰는 스프링 부트2] 1.3 예제 프로젝트의 구성

nineDeveloper 2020. 9. 5. 21:35
728x90

1.3 예제 프로젝트의 구성

한빛미디어 예제 다운로드: https://www.hanbit.co.kr/lib/examFileDown.php?hed_idx=4599
원본 저자 GitHub: https://github.com/miyabayt/spring-boot-doma2-sample

spring-boot-doma2-sample
  ├── build.gradle     - 부모 프로젝트의 빌드 스크립트
  ├── sample-common    - 공통으로 사용하는 유틸리티를 관리하는 모듈
  ├── sample-domain    - 도메인 객체를 관리하는 모듈
  ├── sample-web-base  - 웹 모듈의 공통 기능을 관리하는 모듈
  ├── sample-web-front - 최종 사용자용 웹 애플리케이션
  ├── sample-web-admin - 운영자용 웹 애플리케이션
  └── sample-batch     - 정기 실행 배치를 관리하는 모듈

멀티 프로젝트

멀티 프로젝트 구성 장점

  • 빌드 스크립트를 공통화할 수 있어 작성량이 줄어든다
  • 로컬/원격 저장소에 아티팩트를 업로드하지 않고도 소스 코드의 변경이 반영된다
  • 각각의 프로젝트를 관련지어 작업을 실행할 수 있다

애플리케이션 아키텍처

프레젠테이션 층

입력된 값을 받아 값을 확인하거나 값의 변환을 실시하는 층

  • 웹 모듈의 Form 클래스
  • FormValidator 클래스

애플리케이션 층

프레젠테이션 층에서 받은 값을 도메인 층에 전달하는 층

비즈니스 로직은 포함하지 않지만 화면 전환을 제어하거나 세션을 사용하여 다음 화면에 값을 전달한다

  • 웹 모듈의 Controller

도메인 층

도메인 객체를 가지고 비즈니스 로직을 처리하는 메인층

도메인 객체는 모든 계층에서 사용되지만 반대로 도메인 층은 다른 계층에 의존해서는 안됨

  • 도메인 모듈의 Service

인프라스트럭처 층

도메인 계층에서 전달된 데이터를 영속화하는 층

애플리케이션 계층의 영향을 받지 않도록 범용적인 부품으로 만들어야 함

  • 도메인 모듈의 Repository 클래스

예제 프로젝트의 빌드 스크립트

모듈의 의존관계**

웹 모듈 -> 도메인 모듈 -> 공통 모듈

  1. 스프링 부트 버전을 확장 속성으로 설정한다
  2. 테스트 코드는 Spock 프레임워크(Groovy 언어로 작성)를 이요하므로 그루비를 사용할 수 있도록 설정
  3. 모든 서브 프로젝트에서 spring-boot-gradle-plugin을 사용하도록 설정
  4. 모든 서브 프로젝트에서 dependency-management 플러그인을 사용하도록 설정
  5. 자바 컴파일러 준수 레벨을 11로 변경
  6. src/main/resources 를 클래스 경로에 추가하여 개발 중 변경 내용이 즉시 반영되도록 설정
  7. 리소스 파일의 출력 위치를 소스 파일의 출력 위치로 변경
  8. 컴파일 전에 리소스 파일의 출력을 실시
  9. 도마의 의존관계에 버전 차이가 있는 스프링 부트를 제외
  10. Spring Session 모듈을 사용하여 세션 정보를 데이터베이스에 저장
  11. Gradle의 프로젝트 속성을 bootRun의 인수에 전달
    buildscript {
    ext {
        springBootVersion = "2.1.3.RELEASE" // 1. 스프링 부트 버전을 확장 속성으로 설정한다
        spockVersion = "1.2-groovy-2.5"
        groovyVersion = "2.5.3"
        lombokVersion = "1.18.2"
        dockerComposePluginVersion = "0.6.6"
    }
    repositories {
        mavenLocal()
        mavenCentral()
        jcenter()
    }
    dependencies {
        // 1. 스프링 부트 버전을 확장 속성으로 설정한다
        classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}"
    }
    }
    

subprojects {
apply plugin: "java"
// 2. 테스트 코드는 Spock 프레임워크(Groovy 언어로 작성)를 이요하므로 그루비를 사용할 수 있도록 설정
apply plugin: "groovy"
apply plugin: "idea"
apply plugin: "eclipse"
// 3. 모든 서브 프로젝트에서 spring-boot-gradle-plugin을 사용하도록 설정
apply plugin: "org.springframework.boot"
// 4. 모든 서브 프로젝트에서 dependency-management 플러그인을 사용하도록 설정
apply plugin: "io.spring.dependency-management"
sourceCompatibility = 11 // 5. 자바 컴파일러 준수 레벨을 11로 변경
targetCompatibility = 11
[compileJava, compileTestJava, compileGroovy, compileTestGroovy].options.encoding = "UTF-8"

sourceSets {
    test.resources {
        // 테스트 시 src/main/resources에 있는 설정 파일을 사용
        srcDirs "src/main/resources"
        srcDirs "src/test/resources"
    }
}

repositories {
    mavenCentral()
    jcenter()

    // jasperreports
    maven { url "http://jasperreports.sourceforge.net/maven2/" }
    maven { url "http://jaspersoft.artifactoryonline.com/jaspersoft/third-party-ce-artifacts/" }
}

dependencyManagement {
    imports {
        mavenBom org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES
    }
}

idea {
    module {
        downloadJavadoc = true
        downloadSources = true

        inheritOutputDirs = false
        outputDir = compileJava.destinationDir
        testOutputDir = compileTestJava.destinationDir
    }
}

eclipse {
    classpath {
        containers.remove("org.eclipse.jdt.launching.JRE_CONTAINER")
        containers "org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-11"
    }
}

bootRun {
    // 6. `src/main/resources` 를 클래스 경로에 추가하여 개발 중 변경 내용이 즉시 반영되도록 설정
    sourceResources sourceSets.main
    jvmArgs "-XX:TieredStopAtLevel=1", "-Xverify:none"
}

ext["groovy.version"] = groovyVersion

dependencies {
    compileOnly "org.projectlombok:lombok:${lombokVersion}"
    annotationProcessor "org.projectlombok:lombok:${lombokVersion}"
    testCompile "org.assertj:assertj-core"
    testCompile "org.spockframework:spock-core:${spockVersion}"
    testCompile "org.mockito:mockito-core"
}

}

task startFakeSmtpServer(type: JavaExec) {
classpath = buildscript.configurations.classpath
args = ["--start-server", "-m"] // -m (memory-mode)
main = "com.nilhcem.fakesmtp.FakeSMTP"
}

apply plugin: "docker-compose"

dockerCompose {
useComposeFiles = ["docker/docker-compose.yml"]
}

project(":sample-common") {
bootJar {
enabled = false
}

jar {
  enabled = true
}
dependencies {
  // springframework
  annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"
  compile "org.springframework.boot:spring-boot-starter"

  compile "org.apache.commons:commons-lang3"
  compile "org.apache.commons:commons-text:1.4"
  compile "org.apache.commons:commons-compress:1.14"
  compile "commons-codec:commons-codec"
  compile "org.apache.commons:commons-digester3:3.2"
  compile "commons-io:commons-io:2.5"
  compile "org.apache.tika:tika-core:1.15"
  compile "dom4j:dom4j"
  compile "com.ibm.icu:icu4j:59.1"
}

}

project(":sample-domain") {
bootJar {
enabled = false
}

jar {
  enabled = true
}

// 도마(Doma) 2를 위한 코드
// Java클래스와 SQL파일의 출력 디렉터리를 동일하게 한다
// 7. 리소스 파일의 출력 위치를 소스 파일의 출력 위치로 변경
processResources.destinationDir = compileJava.destinationDir
// 컴파일하기 전에 SQL파일을 출력 디렉터리에 복사하기 때문에 의존관계를 역전한다
// 8. 컴파일 전에 리소스 파일의 출력을 실시
compileJava.dependsOn processResources

dependencies {
    compile project(":sample-common")

    // springframework
    compile "org.springframework.boot:spring-boot-starter-aop"
    compile "org.springframework.boot:spring-boot-starter-validation"
    compile "org.springframework.boot:spring-boot-starter-mail"
    compile "org.springframework.boot:spring-boot-starter-thymeleaf"
    compile "org.springframework.boot:spring-boot-starter-jdbc"
    compile "org.springframework.boot:spring-boot-starter-json"

    // doma exclude springframework
    annotationProcessor "org.seasar.doma.boot:doma-spring-boot-starter:1.1.1"
    compile("org.seasar.doma.boot:doma-spring-boot-starter:1.1.1") {
        // 9. 도마의 의존관계에 버전 차이가 있는 스프링 부트를 제외
        exclude group: "org.springframework.boot"
    }

    // jackson
    compile "com.fasterxml.jackson.dataformat:jackson-dataformat-csv"

    // modelmapper
    compile "org.modelmapper:modelmapper:0.7.5"

    // thymeleaf
    compile "org.codehaus.groovy:groovy:${groovyVersion}"
    compile("nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:2.3.0") {
        exclude group: "org.codehaus.groovy", module: "groovy"
    }

    // mysql database
    compile "mysql:mysql-connector-java"
    compile "org.flywaydb:flyway-core"

    testCompile "org.springframework.boot:spring-boot-starter-test"
    testCompile "org.spockframework:spock-spring:${spockVersion}"
}

}

project(":sample-web-base") {
bootJar {
enabled = false
}

jar {
    enabled = true
}

dependencies {
    compile project(":sample-domain")

    // springframework
    compile "org.springframework.boot:spring-boot-starter-cache"
    compile("org.springframework.boot:spring-boot-starter-web") {
        // Jetty를 사용하기 위해 내장된 톰캣 제외
        exclude module: "spring-boot-starter-tomcat"
    }
    compile "org.springframework.boot:spring-boot-starter-security"
    compile "org.springframework.boot:spring-boot-starter-jetty"
    // 세션 보관 위치로 DB를 사용할 경우
    // 10. Spring Session 모듈을 사용하여 세션 정보를 데이터베이스에 저장
    compile "org.springframework.session:spring-session-jdbc"
    // 세션 보관 위치로 redis를 사용할 경우
    //compile "org.springframework.boot:spring-boot-starter-data-redis"

    // thymeleaf
    compile "org.thymeleaf.extras:thymeleaf-extras-springsecurity5"

    // jasperreports
    compile "net.sf.jasperreports:jasperreports:6.4.0"
    compile "com.lowagie:itext:2.1.7.js5"

    // apache POI
    compile "org.apache.poi:poi:3.16"
    compile "org.apache.poi:poi-ooxml:3.16"

    // EhCache
    compile "net.sf.ehcache:ehcache"

    // webjars
    compile "org.webjars:webjars-locator-core"
    compile "org.webjars:bootstrap:3.3.7"
    compile "org.webjars:jquery:2.2.4"
    compile "org.webjars:jquery-validation:1.17.0"
    compile "org.webjars:bootstrap-datepicker:1.7.1"
    compile("org.webjars.bower:iCheck:1.0.2") {
        exclude module: "jquery"
    }
    compile "org.webjars:html5shiv:3.7.3"
    compile "org.webjars:respond:1.4.2"
    compile "org.webjars:AdminLTE:2.3.8"
    compile "org.webjars:font-awesome:4.7.0"
    compile "org.webjars:ionicons:2.0.1"

    testCompile "org.springframework.security:spring-security-test"
    testCompile "org.springframework.boot:spring-boot-starter-test"
    testCompile "org.spockframework:spock-spring:${spockVersion}"
}

}

project(":sample-web-front") {
bootJar {
launchScript()
}

configurations {
    developmentOnly
    runtimeClasspath {
        extendsFrom developmentOnly
    }
}

dependencies {
    compile project(":sample-web-base")
    developmentOnly "org.springframework.boot:spring-boot-devtools"

    testCompile "org.springframework.security:spring-security-test"
    testCompile "org.springframework.boot:spring-boot-starter-test"
    testCompile "org.spockframework:spock-spring:${spockVersion}"
}

}

project(":sample-batch") {
bootRun {
// 프로젝트 프로퍼티를 인수로 건넨다
// 11. Gradle의 프로젝트 속성을 bootRun의 인수에 전달
if (project.hasProperty("args")) {
args project.args.split("\s+")
}
}

dependencies {
    compile project(":sample-domain")

    // springframework
    compile "org.springframework.boot:spring-boot-starter-batch"

    testCompile "org.springframework.boot:spring-boot-starter-test"
    testCompile "org.springframework.batch:spring-batch-test"
    testCompile "org.spockframework:spock-spring:${spockVersion}"
}

}

task wrapper(type: Wrapper) {
gradleVersion = "4.10.2"
}


##### 예제 프로젝트 설정(`setting.gradle`)
```groovy
include "sample-common", "sample-domain", "sample-web-base", "sample-web-front", "sample-web-admin", "sample-batch"
메인 애플리케이션 클래스가 없는 모듈의 경우(build.gradle)

bootJar 작업(실행 가능한 JAR 파일 작성 작업)을 실행할 때 오류가 발생 아래와 같이 설정한다

bootJar {
  enabled = false
}

롬복 이용하기

https://projectlombok.org/features/all

Lombok을 이용하여 boilerplate code의 절감을 도모

애플리케이션 실행시에는 필요하지 않기 때문에 ScopecompileOnly

의존관게에 롬복 추가하기(build.gradle)
dependencies {
  compileOnly "org.projectlombok:lombok:${lombokVersion}"
  annotationProcessor "org.projectlombok:lombok:${lombokVersion}"
}

Getter와 Setter 구현 배제 하기

@Getter@Setter 애너테이션을 작성하면 애너테이션 프로세서에 의해 GetterSetter가 자동 생성 됨

@Getter@Setter 애너테이션 부여하기
@Getter
@Setter
public class Person {
  String name;
}
자동 생성된 Getter와 Setter
public class Persion {
  String name;

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }
}

변수 타입을 val로 통일하기

Lombok에서는 로컬 변수 타입을 val로 설정할 수 있음
긴 타입명도 val로 통일할 수 있으며 자동 생성되는 타입에는 final이 부여됨

자바 10부터 로컬 변수 타입 추론 var를 사용할 수 있음
var로 정의된 변수는 final로 한정되지 않음

변수 타입을 자동으로 생성하기
public String valExample() {
  val example = new ArrayList<String>();
  example.add("Hello, World!");

  val foo = example.get(0);
  return foo.toLowerCase();
}
728x90