├── .gitignore ├── .idea ├── compiler.xml ├── misc.xml ├── modules.xml ├── modules │ ├── admin-module │ │ ├── admin-module_main.iml │ │ ├── admin-module_test.iml │ │ └── multi-module-admin-module.iml │ ├── api-module │ │ ├── api-module_main.iml │ │ ├── api-module_test.iml │ │ └── multi-module-api-module.iml │ ├── batch-module │ │ ├── batch-module_main.iml │ │ ├── batch-module_test.iml │ │ └── multi-module-batch-module.iml │ └── core-module │ │ ├── core-module_main.iml │ │ ├── core-module_test.iml │ │ └── multi-module-core-module.iml └── vcs.xml ├── README.asc ├── admin-module ├── build │ └── resources │ │ └── main │ │ ├── application.yml │ │ └── templates │ │ └── index.html └── src │ └── main │ ├── java │ └── io │ │ └── honeymon │ │ └── springboot │ │ └── multimodule │ │ └── admin │ │ ├── AdminApplication.java │ │ └── view │ │ └── IndexController.java │ └── resources │ ├── application.yml │ ├── logback-spring.xml │ └── templates │ └── index.html ├── api-module ├── build │ ├── classes │ │ └── java │ │ │ └── main │ │ │ └── META-INF │ │ │ └── spring-configuration-metadata.json │ └── resources │ │ └── main │ │ └── application.yml └── src │ └── main │ ├── ebextensions │ └── 01_init.config │ ├── java │ └── io │ │ └── honeymon │ │ └── springboot │ │ └── multimodule │ │ └── api │ │ ├── ApiApplication.java │ │ ├── config │ │ ├── ClientProperty.java │ │ └── RestTemplateConfig.java │ │ ├── root │ │ └── RootController.java │ │ └── service │ │ └── RootService.java │ └── resources │ ├── application-core-custom.yml │ ├── application.yml │ └── logback-spring.xml ├── batch-module └── src │ ├── main │ ├── java │ │ └── io │ │ │ └── honeymon │ │ │ └── springboot │ │ │ └── multimodule │ │ │ └── batch │ │ │ ├── BatchModuleApplication.java │ │ │ └── batchjob │ │ │ └── SimpleBatchJob.java │ └── resources │ │ ├── application.yml │ │ └── logback-spring.xml │ └── test │ └── java │ └── io │ └── honeymon │ └── springboot │ └── multimodule │ └── batch │ ├── BatchApplicationTest.java │ └── DevProfilesTest.java ├── build.gradle ├── core-module └── src │ └── main │ ├── java │ └── io │ │ └── honeymon │ │ └── springboot │ │ └── multimodule │ │ └── core │ │ ├── entity │ │ ├── BaseAdmin.java │ │ └── BaseUser.java │ │ ├── package-info.java │ │ └── repository │ │ ├── BaseAdminRepository.java │ │ └── BaseUserRepository.java │ └── resources │ └── application-core.yml ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── multi-module.iml └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/gradle,java,eclipse,intellij 2 | 3 | # VS_code 4 | .vscode 5 | 6 | # Mac Index 7 | .DS_Store 8 | 9 | ### Eclipse ### 10 | 11 | .metadata 12 | bin/ 13 | tmp/ 14 | *.tmp 15 | *.bak 16 | *.swp 17 | *~.nib 18 | local.properties 19 | .settings/ 20 | .loadpath 21 | .recommenders 22 | 23 | # External tool builders 24 | .externalToolBuilders/ 25 | 26 | # Locally stored "Eclipse launch configurations" 27 | *.launch 28 | 29 | # PyDev specific (Python IDE for Eclipse) 30 | *.pydevproject 31 | 32 | # CDT-specific (C/C++ Development Tooling) 33 | .cproject 34 | 35 | # CDT- autotools 36 | .autotools 37 | 38 | # Java annotation processor (APT) 39 | .factorypath 40 | 41 | # PDT-specific (PHP Development Tools) 42 | .buildpath 43 | 44 | # sbteclipse plugin 45 | .target 46 | 47 | # Tern plugin 48 | .tern-project 49 | 50 | # TeXlipse plugin 51 | .texlipse 52 | 53 | # STS (Spring Tool Suite) 54 | .springBeans 55 | 56 | # Code Recommenders 57 | .recommenders/ 58 | 59 | # Annotation Processing 60 | .apt_generated/ 61 | 62 | # Scala IDE specific (Scala & Java development for Eclipse) 63 | .cache-main 64 | .scala_dependencies 65 | .worksheet 66 | 67 | ### Eclipse Patch ### 68 | # Eclipse Core 69 | .project 70 | 71 | # JDT-specific (Eclipse Java Development Tools) 72 | .classpath 73 | 74 | # Annotation Processing 75 | .apt_generated 76 | 77 | ### Intellij ### 78 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 79 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 80 | 81 | # User-specific stuff 82 | .idea/**/workspace.xml 83 | .idea/**/tasks.xml 84 | .idea/**/usage.statistics.xml 85 | .idea/**/dictionaries 86 | .idea/**/shelf 87 | 88 | # Sensitive or high-churn files 89 | .idea/**/dataSources/ 90 | .idea/**/dataSources.ids 91 | .idea/**/dataSources.local.xml 92 | .idea/**/sqlDataSources.xml 93 | .idea/**/dynamic.xml 94 | .idea/**/uiDesigner.xml 95 | .idea/**/dbnavigator.xml 96 | 97 | # Gradle 98 | .idea/**/gradle.xml 99 | .idea/**/libraries 100 | build 101 | 102 | # CMake 103 | cmake-build-*/ 104 | 105 | # Mongo Explorer plugin 106 | .idea/**/mongoSettings.xml 107 | 108 | # File-based project format 109 | *.iws 110 | 111 | # IntelliJ 112 | out/ 113 | 114 | # mpeltonen/sbt-idea plugin 115 | .idea_modules/ 116 | 117 | # JIRA plugin 118 | atlassian-ide-plugin.xml 119 | 120 | # Cursive Clojure plugin 121 | .idea/replstate.xml 122 | 123 | # Crashlytics plugin (for Android Studio and IntelliJ) 124 | com_crashlytics_export_strings.xml 125 | crashlytics.properties 126 | crashlytics-build.properties 127 | fabric.properties 128 | 129 | # Editor-based Rest Client 130 | .idea/httpRequests 131 | 132 | ### Intellij Patch ### 133 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 134 | 135 | # *.iml 136 | # modules.xml 137 | # .idea/misc.xml 138 | # *.ipr 139 | 140 | # Sonarlint plugin 141 | .idea/sonarlint 142 | 143 | ### Java ### 144 | # Compiled class file 145 | *.class 146 | 147 | # Log file 148 | *.log 149 | 150 | # BlueJ files 151 | *.ctxt 152 | 153 | # Mobile Tools for Java (J2ME) 154 | .mtj.tmp/ 155 | 156 | # Package Files # 157 | *.jar 158 | *.war 159 | *.nar 160 | *.ear 161 | *.zip 162 | *.tar.gz 163 | *.rar 164 | 165 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 166 | hs_err_pid* 167 | 168 | ### Gradle ### 169 | .gradle 170 | /build/ 171 | 172 | # Ignore Gradle GUI config 173 | gradle-app.setting 174 | 175 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 176 | !gradle-wrapper.jar 177 | 178 | # Cache of project 179 | .gradletasknamecache 180 | 181 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 182 | # gradle/wrapper/gradle-wrapper.properties 183 | 184 | 185 | # End of https://www.gitignore.io/api/gradle,java,eclipse,intellij 186 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /.idea/modules/admin-module/admin-module_main.iml: -------------------------------------------------------------------------------- 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 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /.idea/modules/admin-module/admin-module_test.iml: -------------------------------------------------------------------------------- 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 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /.idea/modules/admin-module/multi-module-admin-module.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/modules/api-module/api-module_main.iml: -------------------------------------------------------------------------------- 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 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /.idea/modules/api-module/api-module_test.iml: -------------------------------------------------------------------------------- 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 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /.idea/modules/api-module/multi-module-api-module.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/modules/batch-module/batch-module_main.iml: -------------------------------------------------------------------------------- 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 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /.idea/modules/batch-module/batch-module_test.iml: -------------------------------------------------------------------------------- 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 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /.idea/modules/batch-module/multi-module-batch-module.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/modules/core-module/core-module_main.iml: -------------------------------------------------------------------------------- 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 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /.idea/modules/core-module/core-module_test.iml: -------------------------------------------------------------------------------- 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 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /.idea/modules/core-module/multi-module-core-module.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.asc: -------------------------------------------------------------------------------- 1 | = 스프링 부트 기반 멀티모듈 프로젝트 구성 가이드 2 | 3 | 애플리케이션을 만들 때 다중 모듈을 가지는 프로젝트를 구성하고 각 모듈의 조합에 따라서 다른 기능을 가지는 애플리케이션(예: 관리자 애플리케이션, API 제공 애플리케이션과 웹페이지를 제공하는 웹 애플리케이션) 배포본을 구성한다. 4 | 5 | 6 | 나는 각 애플리케이션의 기본실행환경은 다음과 같이 구분짓는다: 7 | 8 | * 로컬(`local`): 개발자 실행환경 9 | * 개발(`dev`): `develop` 브랜치를 기준으로 깃저장소 밀어넣기(push)가 발생날 때 빌드배포 발생 10 | * 스테이지(`stage`): 개발된 기능을 기획자와 확인하기 위한 목적으로 준운영환경에 가깝게 운영 11 | * 운영(`prod`): 실제 사용자에게 기능제공 12 | 13 | [NOTE] 14 | ==== 15 | 프로파일 명칭은 팀이나 기업마다 조금씩 다르겠지만 대략 저런 구조를 가진다고 생각한다. 16 | ==== 17 | 18 | 기존에는 이렇게 구성을 했었고 이와 관련된 설정은 모두 애플리케이션 속성파일(`application.yml` 혹은 `application.properties`)에 스프링 프로파일을 이용하여 구분지었다. 스프링 부트는 애플리케이션 속성 파일에서 사용할 스프링 프로파일을 명시하여 활성화된 프로파일에 따라 다르게 적재하는 기능을 제공한다. 각 모듈별로 이 기능을 활용하여 프로파일로 속성값을 정의해놓고 최종적으로 애플리케이션 실행단계에 적용하는 것이 가능하다. 19 | 20 | [source] 21 | ---- 22 | multi-module: 루트 프로젝트 23 | └─ core-module: Entity, Repository 및 Utility 등 24 | └─ batch-module: 배치(batch) 모음 25 | └─ api-module: API 애플리케이션 26 | └─ admin-module: 관리자 애플리케이션 27 | └─ (todo) web-module: 웹 애플리케이션 28 | ---- 29 | 30 | 위와 같은 구성을 가지는 모듈을 기준으로 설명을 하고자 한다. 애플리케이션 실행환경은 위에서 언급했듯이 '로컬', '개발', '스테이지'와 '운영'으로 구성된다. 31 | 32 | 각 모듈 하위에는 다음과 같이 애플리케이션 속성파일을 지정한다. 33 | 34 | [source,java] 35 | ---- 36 | . 37 | ├── admin-module 38 | │   └── src 39 | │   └── main 40 | │      ├── java 41 | │      │   └── io.honeymon.springboot.multimodule 42 | │      │   └── admin 43 | │      │   ├── AdminModuleApplication.java 44 | │      │   └── view 45 | │      │   └── IndexController.java 46 | │      └── resources 47 | │       ├── application.yml 48 | │      └── logback-spring.xml 49 | ├── api-module 50 | │   └── src 51 | │   └── main 52 | │      ├── java 53 | │      │   └── io.honeymon.springboot.multimodule 54 | │      │   └── api 55 | │      │   ├── ApiApplication.java 56 | │      │   └── root 57 | │      │   └── RootController.java 58 | │      └── resources 59 | │       ├── application.yml 60 | │       └── logback-spring.xml 61 | ├── batch-module 62 | │   └── src 63 | │   └── main 64 | │      ├── java 65 | │      │   └── io.honeymon.springboot.multimodule 66 | │      │   └── batch 67 | │      │   ├── BatchModuleApplication.java 68 | │      │   └── batchjob 69 | │       │   └── SimpleBatchJob.java 70 | │      └── resources 71 | │      ├── application.yml 72 | │      └── logback-spring.xml 73 | └── core-module 74 |    └── src 75 |    └── main 76 |       ├── java 77 |       │   └── io.honeymon.springboot.multimodule 78 |       │   └── core 79 |       │   ├── entity 80 |       │   │   ├── BaseAdmin.java 81 |       │   │   └── BaseUser.java 82 |       │   ├── package-info.java 83 |       │   └── repository 84 |       │   ├── BaseAdminRepository.java 85 |       │   └── BaseUserRepository.java 86 |       └── resources 87 |       └── application-core.yml 88 | ---- 89 | 90 | 이 파일에서 살펴봐야할 공통적인 속성파일은 ``application-core.yml`` 파일이다. 파일은 각각 애플리케이션 데이터소스(DataSource) 구성과 외부연계API에 사용할 클라이언트 구성에 필요한 인증키 정보를 가지고 있다. 91 | 92 | .``application-core.yml`` 93 | [source,yaml] 94 | ---- 95 | spring: 96 | flyway: 97 | enabled: false 98 | 99 | --- 100 | spring.profiles: local 101 | spring: 102 | datasource: 103 | url: jdbc:h2:mem:multi 104 | username: SA 105 | password: 106 | 107 | --- 108 | spring.profiles: dev 109 | spring: 110 | datasource: 111 | url: jdbc:h2:mem:multi-dev 112 | username: dbdev 113 | password: dbdevpassword 114 | 115 | --- 116 | spring.profiles: stage 117 | spring: 118 | datasource: 119 | url: jdbc:h2:mem:multi-stage 120 | username: dbstage 121 | password: dbstagepassword 122 | 123 | --- 124 | spring.profiles: prod 125 | spring: 126 | datasource: 127 | url: jdbc:h2:mem:multi-prod 128 | username: dbprod 129 | password: dbprodpassword 130 | 131 | --- 132 | spring.profiles: db-prod 133 | spring: 134 | datasource: 135 | url: jdbc:h2:mem:multi-db-prod 136 | username: dbprod 137 | password: dbprodpassword 138 | ---- 139 | 140 | [NOTE] 141 | ==== 142 | 별도의 프로파일로 중복되는 속성을 정의하기 보다는 실행하는 환경에 따라서 환경변수로 외부 속성변경방식을 사용하길 바란다. 143 | ==== 144 | 145 | 위 파일을 살펴보면 공통적으로 ``spring.profiles`` 를 정의하였다. ``spring.profiles.active`` 에서 ``{"local", "dev", "stage", "prod"}`` 중에서 활성화된 프로파일을 따라 적재된다. 146 | 147 | ``application-core.yml`` 파일을 이용하는 ``api-module/application.yml``을 살펴보자: 148 | 149 | .``api-module/application.yml`` 150 | [source,yaml] 151 | ---- 152 | # commons 153 | spring: 154 | profiles: 155 | include: 156 | - core 157 | client: 158 | id: honeymon 159 | key: 20180416 160 | 161 | --- 162 | spring.profiles: local 163 | client: 164 | id: honeymon-local 165 | key: 20180416-local 166 | 167 | --- 168 | spring,profiles: dev 169 | client: 170 | id: honeymon-dev 171 | key: 20180416-dev 172 | 173 | --- 174 | spring.profiles: stage 175 | client: 176 | id: honeymon-stage 177 | key: 20180416-stage 178 | 179 | --- 180 | spring.profiles: prod 181 | client: 182 | id: honeymon-prod 183 | key: 20180416-prod 184 | ---- 185 | 186 | ``api-module/application.yml``의 구성은 생각보다 간결해진다. ``spring.profiles.include``를 통해서 ``application-core.yml`` 파일을 프로파일로 읽어온다. ``spring.profiles.include``을 이용해서 다른 모듈이나 프로파일로 정의된 애플리케이션 속성파일을 지정할 수 있다. 187 | 188 | ``api-module/application.yml``에는 로깅레빌을 선언하는 정도의 선언이 기재되어 있을뿐, 데이터베이스 정보와 클라이언트 정보를 기술하고 있지는 않다. 189 | 190 | [NOTE] 191 | ==== 192 | 애플리케이션 속성은 애플리케이션 구동 시점에 적재되어 키밸류(Key-Value)의 Map 으로 합쳐지게 된다. 이 과정에서 중요한 것은 활성화하는 프로파일의 순서다. 193 | ==== 194 | 195 | 활성화화는 프로파일의 순서에 따라서 속성값이 달라진다. 즉, 나중에 활성화되는 프로파일의 값이 최종적으로 반영이 된다. 그 예로 다음과 같이 프로파일 순서를 ``{"prod", "db-prod"}``와 ``{"db-prod", "prod"}``로 선언했을 때 값이 변경되는 것을 볼 수 있다. 196 | 197 | [source,java] 198 | ---- 199 | @ActiveProfiles({"prod", "db-prod"}) 200 | @RunWith(SpringRunner.class) 201 | @SpringBootTest 202 | public class ProdAndDbProdTest { 203 | @Autowired 204 | private Environment env; 205 | 206 | @Test 207 | public void testGetDataSourceUrl() { 208 | assertThat(env.getProperty("spring.datasource.url")).isEqualTo("jdbc:h2:mem:multi-db-prod"); 209 | } 210 | } 211 | 212 | @ActiveProfiles({"db-prod","prod"}) 213 | @RunWith(SpringRunner.class) 214 | @SpringBootTest 215 | public class DbProdAndProdTest { 216 | @Autowired 217 | private Environment env; 218 | 219 | @Test 220 | public void testGetDataSourceUrl() { 221 | assertThat(env.getProperty("spring.datasource.url")).isEqualTo("jdbc:h2:mem:multi-prod"); 222 | } 223 | } 224 | ---- 225 | 226 | == 정리 227 | * 애플리케이션은 기능에 따라 애플리케이션 속성파일에 프로파일을 정의한다. 228 | ** 예: DB 관련: ``application-db.yml`` 229 | ** 예: 외부API 호출시 인증: ``application-auth.yml`` 230 | 231 | * 기능정의 애플리케이션 속성파일을 애플리케이션 속성파일에서 포함시켜 선언한다. 232 | + 233 | [source,yml] 234 | ---- 235 | spring: 236 | profiles: 237 | include: 238 | - db 239 | - auth 240 | ---- 241 | 242 | * 사전 정의한 배포환경별 프로파일 ``{"local", "dev", "stage", "prod"}`` 외에 별도로 선언이 필요하다면 각 기능별 애플리케이션 속성파일을 추가한다. 243 | + 244 | .``application-dbprod.yml`` 변경 245 | [source,yml] 246 | ---- 247 | spring: 248 | datasource: 249 | url: jdbc:h2:mem:multi-db-prod 250 | username: dbprod 251 | password: dbprodpassword 252 | flyway: 253 | enabled: false 254 | ---- 255 | 256 | 추가한 속성파일을 ``spring.profiles.active=prod,dbprod`` 처럼 활성화할 프로파일 마지막에 선언하여 속성을 한번에 변경하는 방법도 유효한 사용방법이다. 257 | 258 | * [red]#**주의**# :: ``spring.profiles.active``에서 여러 프로파일을 선언할 때 순서에 주의하자. 259 | ** 동일한 속성(키)을 정의한 프로파일이 있다면 마지막에 선언된 프로파일 속성값이 적용된다. 260 | * `spring.profiles.active=prod` 를 선언했을 때 261 | ** `application-db.yml`: `spring.profile: prod` 속성 적용 262 | ** `application-auth.yml`: `spring.profiles: prod` 속성 적용 263 | ** `application.yml`: `spring.profiles: prod` 속성 적용 264 | ** `application.yml` 포함선언(`spring.profiels.include`)한 각 속성파일(ex: `application-db.yml`, `application-auth.yml`) 내부에 정의한 ``spring.profiles: prod``가 적용된다. -------------------------------------------------------------------------------- /admin-module/build/resources/main/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | profiles: 3 | include: 4 | - core 5 | spring: 6 | thymeleaf: 7 | cache: true 8 | 9 | --- 10 | spring.profiles: local 11 | spring: 12 | thymeleaf: 13 | cache: false -------------------------------------------------------------------------------- /admin-module/build/resources/main/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello 6 | 7 | 8 |

Index pages

9 | 10 | -------------------------------------------------------------------------------- /admin-module/src/main/java/io/honeymon/springboot/multimodule/admin/AdminApplication.java: -------------------------------------------------------------------------------- 1 | package io.honeymon.springboot.multimodule.admin; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class AdminApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(AdminApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /admin-module/src/main/java/io/honeymon/springboot/multimodule/admin/view/IndexController.java: -------------------------------------------------------------------------------- 1 | package io.honeymon.springboot.multimodule.admin.view; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.beans.factory.annotation.Value; 5 | import org.springframework.stereotype.Controller; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | 8 | import javax.annotation.PostConstruct; 9 | 10 | /** 11 | * @author jiheon.kim on 2018. 7. 2. 12 | */ 13 | @Slf4j 14 | @Controller 15 | public class IndexController { 16 | @Value("${spring.thymeleaf.cache}") 17 | private boolean thymeleafCache; 18 | @Value("${spring.datasource.url}") 19 | private String databaseUrl; 20 | 21 | @PostConstruct 22 | public void setUp() { 23 | log.debug("Batch enabled? {}", thymeleafCache); 24 | log.debug("Spring database url: {}", databaseUrl); 25 | log.info("BatchJob Enabled: {}, Spring Database URL: {}", thymeleafCache, databaseUrl); 26 | } 27 | 28 | @GetMapping("/") 29 | private String index() { 30 | return "index"; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /admin-module/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | profiles: 3 | include: 4 | - core 5 | spring: 6 | thymeleaf: 7 | cache: true 8 | 9 | --- 10 | spring.profiles: local 11 | spring: 12 | thymeleaf: 13 | cache: false -------------------------------------------------------------------------------- /admin-module/src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | true 7 | 8 | applicatoin.%d{yyyy-MM-dd}.log 9 | 30 10 | 11 | 12 | INFO 13 | 14 | 15 | 16 | %d{yyyy:MM:dd HH:mm:ss.SSS} %-5level --- [%thread] %logger{35} : %msg %n 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /admin-module/src/main/resources/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello 6 | 7 | 8 |

Index pages

9 | 10 | -------------------------------------------------------------------------------- /api-module/build/classes/java/main/META-INF/spring-configuration-metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "hints": [], 3 | "groups": [ 4 | { 5 | "sourceType": "io.honeymon.springboot.multimodule.api.config.ClientProperty", 6 | "name": "client", 7 | "type": "io.honeymon.springboot.multimodule.api.config.ClientProperty" 8 | } 9 | ], 10 | "properties": [ 11 | { 12 | "sourceType": "io.honeymon.springboot.multimodule.api.config.ClientProperty", 13 | "name": "client.id", 14 | "type": "java.lang.String" 15 | }, 16 | { 17 | "sourceType": "io.honeymon.springboot.multimodule.api.config.ClientProperty", 18 | "name": "client.key", 19 | "type": "java.lang.String" 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /api-module/build/resources/main/application.yml: -------------------------------------------------------------------------------- 1 | # commons 2 | spring: 3 | profiles: 4 | include: 5 | - core 6 | client: 7 | id: honeymon 8 | key: 20180416 9 | 10 | --- 11 | spring.profiles: local 12 | client: 13 | id: honeymon-local 14 | key: 20180416-local 15 | 16 | --- 17 | spring.profiles: dev 18 | client: 19 | id: honeymon-dev 20 | key: 20180416-dev 21 | 22 | --- 23 | spring.profiles: stage 24 | client: 25 | id: honeymon-stage 26 | key: 20180416-stage 27 | 28 | --- 29 | spring.profiles: prod 30 | client: 31 | id: honeymon-prod 32 | key: 20180416-prod 33 | spring: 34 | jpa: 35 | open-in-view: false -------------------------------------------------------------------------------- /api-module/src/main/ebextensions/01_init.config: -------------------------------------------------------------------------------- 1 | # @see https://docs.aws.amazon.com/ko_kr/elasticbeanstalk/latest/dg/ebextensions.html 2 | container_commands: 3 | 10-apache-httpd-conf: 4 | command: "cp .ebextensions/httpd/httpd.conf /etc/httpd/conf/httpd.conf" -------------------------------------------------------------------------------- /api-module/src/main/java/io/honeymon/springboot/multimodule/api/ApiApplication.java: -------------------------------------------------------------------------------- 1 | package io.honeymon.springboot.multimodule.api; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class ApiApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(ApiApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /api-module/src/main/java/io/honeymon/springboot/multimodule/api/config/ClientProperty.java: -------------------------------------------------------------------------------- 1 | package io.honeymon.springboot.multimodule.api.config; 2 | 3 | import lombok.Getter; 4 | import lombok.NoArgsConstructor; 5 | import lombok.Setter; 6 | import org.springframework.boot.context.properties.ConfigurationProperties; 7 | 8 | /** 9 | * @author jiheon.kim on 2018. 7. 2. 10 | */ 11 | @Getter 12 | @Setter 13 | @NoArgsConstructor 14 | @ConfigurationProperties("client") 15 | public class ClientProperty { 16 | private String id; 17 | private String key; 18 | } 19 | -------------------------------------------------------------------------------- /api-module/src/main/java/io/honeymon/springboot/multimodule/api/config/RestTemplateConfig.java: -------------------------------------------------------------------------------- 1 | package io.honeymon.springboot.multimodule.api.config; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 6 | import org.springframework.boot.web.client.RestTemplateBuilder; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.http.HttpHeaders; 10 | import org.springframework.http.HttpRequest; 11 | import org.springframework.http.MediaType; 12 | import org.springframework.http.client.ClientHttpRequestExecution; 13 | import org.springframework.http.client.ClientHttpRequestInterceptor; 14 | import org.springframework.http.client.ClientHttpResponse; 15 | import org.springframework.util.Assert; 16 | import org.springframework.util.StringUtils; 17 | import org.springframework.web.client.RestTemplate; 18 | 19 | import java.io.IOException; 20 | 21 | /** 22 | * @author jiheon.kim on 2018. 7. 2. 23 | */ 24 | @Configuration 25 | @EnableConfigurationProperties({ClientProperty.class}) 26 | public class RestTemplateConfig { 27 | @Autowired 28 | RestTemplateBuilder restTemplateBuilder; 29 | 30 | @Autowired 31 | ClientProperty clientProperty; 32 | 33 | @Bean 34 | public RestTemplate restTemplate() { 35 | return restTemplateBuilder.interceptors(new CustomClientHttpRequestInterceptor(clientProperty.getKey(), clientProperty.getId())) 36 | .build(); 37 | } 38 | 39 | public class CustomClientHttpRequestInterceptor implements ClientHttpRequestInterceptor { 40 | public static final String HEADER_CLIENT = "Client"; 41 | private final String apiKey; 42 | private String clientId; 43 | 44 | 45 | public CustomClientHttpRequestInterceptor(String apiKey) { 46 | Assert.hasText(apiKey, "Required apiKey."); 47 | 48 | this.apiKey = String.format("Bearer %s", apiKey); 49 | } 50 | 51 | /** 52 | * HttpHeader [{@link HttpHeaders#AUTHORIZATION}, "Client"] 추가 53 | * 54 | * @param apiKey 55 | * @param clientId 56 | */ 57 | public CustomClientHttpRequestInterceptor(String apiKey, String clientId) { 58 | this(apiKey); 59 | this.clientId = clientId; 60 | } 61 | 62 | 63 | @Override 64 | public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { 65 | HttpHeaders headers = request.getHeaders(); 66 | headers.setContentType(MediaType.APPLICATION_JSON_UTF8); 67 | headers.set(HttpHeaders.AUTHORIZATION, apiKey); 68 | 69 | if (StringUtils.hasText(clientId)) { 70 | headers.set(HEADER_CLIENT, clientId); 71 | } 72 | return execution.execute(request, body); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /api-module/src/main/java/io/honeymon/springboot/multimodule/api/root/RootController.java: -------------------------------------------------------------------------------- 1 | package io.honeymon.springboot.multimodule.api.root; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.web.bind.annotation.RestController; 5 | 6 | /** 7 | * @author jiheon.kim on 2018. 7. 2. 8 | */ 9 | @Slf4j 10 | @RestController 11 | public class RootController { 12 | } 13 | -------------------------------------------------------------------------------- /api-module/src/main/java/io/honeymon/springboot/multimodule/api/service/RootService.java: -------------------------------------------------------------------------------- 1 | package io.honeymon.springboot.multimodule.api.service; 2 | 3 | import io.honeymon.springboot.multimodule.api.config.ClientProperty; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.beans.factory.annotation.Value; 6 | import org.springframework.stereotype.Service; 7 | 8 | import javax.annotation.PostConstruct; 9 | import javax.annotation.Resource; 10 | 11 | /** 12 | * @author jiheon.kim on 2018. 7. 5. 13 | */ 14 | @Slf4j 15 | @Service 16 | public class RootService { 17 | @Resource 18 | private ClientProperty clientProperty; 19 | @Value("${spring.datasource.url}") 20 | private String databaseUrl; 21 | 22 | @PostConstruct 23 | public void setUp() { 24 | log.info("Client property: [ID: {}, KEY: {}]", clientProperty.getId(), clientProperty.getKey()); 25 | log.info("Database URL: {}", databaseUrl); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /api-module/src/main/resources/application-core-custom.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | datasource: 3 | url: jdbc:h2:mem:multi-db-prod 4 | username: dbprod 5 | password: dbprodpassword 6 | flyway: 7 | enabled: false -------------------------------------------------------------------------------- /api-module/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | # commons 2 | spring: 3 | profiles: 4 | include: 5 | - core 6 | client: 7 | id: honeymon 8 | key: 20180416 9 | 10 | --- 11 | spring.profiles: local 12 | client: 13 | id: honeymon-local 14 | key: 20180416-local 15 | 16 | --- 17 | spring.profiles: dev 18 | client: 19 | id: honeymon-dev 20 | key: 20180416-dev 21 | 22 | --- 23 | spring.profiles: stage 24 | client: 25 | id: honeymon-stage 26 | key: 20180416-stage 27 | 28 | --- 29 | spring.profiles: prod 30 | client: 31 | id: honeymon-prod 32 | key: 20180416-prod 33 | spring: 34 | jpa: 35 | open-in-view: false -------------------------------------------------------------------------------- /api-module/src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | true 7 | 8 | applicatoin.%d{yyyy-MM-dd}.log 9 | 30 10 | 11 | 12 | INFO 13 | 14 | 15 | 16 | %d{yyyy:MM:dd HH:mm:ss.SSS} %-5level --- [%thread] %logger{35} : %msg %n 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /batch-module/src/main/java/io/honeymon/springboot/multimodule/batch/BatchModuleApplication.java: -------------------------------------------------------------------------------- 1 | package io.honeymon.springboot.multimodule.batch; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class BatchModuleApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(BatchModuleApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /batch-module/src/main/java/io/honeymon/springboot/multimodule/batch/batchjob/SimpleBatchJob.java: -------------------------------------------------------------------------------- 1 | package io.honeymon.springboot.multimodule.batch.batchjob; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.beans.factory.annotation.Value; 5 | import org.springframework.stereotype.Component; 6 | 7 | import javax.annotation.PostConstruct; 8 | 9 | /** 10 | * @author jiheon.kim on 2018. 7. 2. 11 | */ 12 | @Slf4j 13 | @Component 14 | public class SimpleBatchJob { 15 | @Value("${spring.batch.job.enabled}") 16 | private boolean batchJobEnabled; 17 | @Value("${spring.datasource.url}") 18 | private String databaseUrl; 19 | 20 | @PostConstruct 21 | public void setUp() { 22 | log.debug("Batch enabled? {}", batchJobEnabled); 23 | log.debug("Spring database url: {}", databaseUrl); 24 | log.info("BatchJob Enabled: {}, Spring Database URL: {}", batchJobEnabled, databaseUrl); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /batch-module/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | profiles: 3 | include: 4 | - core 5 | main: 6 | web-application-type: none 7 | batch: 8 | job: 9 | enabled: false 10 | 11 | --- 12 | spring.profiles: local 13 | 14 | --- 15 | spring.profiles: dev 16 | spring: 17 | batch: 18 | job: 19 | enabled: true 20 | 21 | --- 22 | spring.profiles: stage 23 | spring: 24 | batch: 25 | job: 26 | enabled: true 27 | 28 | --- 29 | spring.profiles: prod 30 | spring: 31 | batch: 32 | job: 33 | enabled: true 34 | -------------------------------------------------------------------------------- /batch-module/src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | true 7 | 8 | applicatoin.%d{yyyy-MM-dd}.log 9 | 30 10 | 11 | 12 | INFO 13 | 14 | 15 | 16 | %d{yyyy:MM:dd HH:mm:ss.SSS} %-5level --- [%thread] %logger{35} : %msg %n 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /batch-module/src/test/java/io/honeymon/springboot/multimodule/batch/BatchApplicationTest.java: -------------------------------------------------------------------------------- 1 | package io.honeymon.springboot.multimodule.batch; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.ActiveProfiles; 7 | import org.springframework.test.context.junit4.SpringRunner; 8 | 9 | /** 10 | * @author jiheon.kim on 2018. 7. 2. 11 | */ 12 | @ActiveProfiles 13 | @RunWith(SpringRunner.class) 14 | @SpringBootTest 15 | public class BatchApplicationTest { 16 | 17 | @Test 18 | public void contextLoaded() { 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /batch-module/src/test/java/io/honeymon/springboot/multimodule/batch/DevProfilesTest.java: -------------------------------------------------------------------------------- 1 | package io.honeymon.springboot.multimodule.batch; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | import org.springframework.core.env.Environment; 8 | import org.springframework.test.context.ActiveProfiles; 9 | import org.springframework.test.context.junit4.SpringRunner; 10 | 11 | import static org.assertj.core.api.Assertions.assertThat; 12 | 13 | /** 14 | * @author jiheon.kim on 2018. 7. 2. 15 | */ 16 | @ActiveProfiles("dev") 17 | @RunWith(SpringRunner.class) 18 | @SpringBootTest 19 | public class DevProfilesTest { 20 | @Autowired 21 | Environment env; 22 | 23 | @Test 24 | public void testBatchEnabled() { 25 | assertThat(env.getProperty("spring.batch.job.enabled")).isEqualTo("true"); 26 | } 27 | 28 | @Test 29 | public void testDataSourceUrl() { 30 | assertThat(env.getProperty("spring.datasource.url")).isEqualTo("jdbc:h2:mem:multi-dev"); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext { 3 | springBootVersion = '2.0.3.RELEASE' 4 | } 5 | repositories { 6 | mavenCentral() 7 | } 8 | dependencies { 9 | classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") 10 | } 11 | } 12 | 13 | 14 | subprojects { 15 | apply plugin: 'java' 16 | apply plugin: 'eclipse' 17 | 18 | group = 'io.honeymon.springboot.multimodule' 19 | version = '0.0.1-SNAPSHOT' 20 | 21 | targetCompatibility = 1.8 22 | sourceCompatibility = 1.8 23 | compileJava.options.encoding = 'UTF-8' 24 | 25 | repositories { 26 | mavenCentral() 27 | } 28 | } 29 | 30 | project(":core-module") { 31 | apply plugin: 'org.springframework.boot' 32 | apply plugin: 'io.spring.dependency-management' 33 | 34 | bootJar.enabled = false 35 | jar.enabled = true 36 | 37 | dependencies { 38 | compile('org.springframework.boot:spring-boot-starter-data-jpa') 39 | compile('org.flywaydb:flyway-core') 40 | compile('org.projectlombok:lombok') 41 | compile('org.springframework.boot:spring-boot-starter-logging') 42 | 43 | compileOnly "org.springframework.boot:spring-boot-configuration-processor" 44 | 45 | runtime('com.h2database:h2') 46 | runtime('com.microsoft.sqlserver:mssql-jdbc') 47 | runtime('mysql:mysql-connector-java') 48 | 49 | testCompile('org.springframework.boot:spring-boot-starter-test') 50 | } 51 | } 52 | 53 | project(":api-module") { 54 | apply plugin: 'org.springframework.boot' 55 | apply plugin: 'io.spring.dependency-management' 56 | 57 | bootJar { 58 | from('./src/main/ebextensions') { 59 | into('.ebextensions') 60 | } 61 | } 62 | 63 | dependencies { 64 | compile project(":core-module") 65 | 66 | compile('org.springframework.boot:spring-boot-starter-data-rest') 67 | compile('org.springframework.boot:spring-boot-starter-hateoas') 68 | compile('org.springframework.boot:spring-boot-starter-web-services') 69 | compile('org.springframework.boot:spring-boot-starter-webflux') 70 | compile('org.springframework.data:spring-data-rest-hal-browser') 71 | 72 | compileOnly "org.springframework.boot:spring-boot-configuration-processor" 73 | 74 | testCompile('org.springframework.boot:spring-boot-starter-test') 75 | testCompile('io.projectreactor:reactor-test') 76 | } 77 | } 78 | 79 | 80 | project(":admin-module") { 81 | apply plugin: 'org.springframework.boot' 82 | apply plugin: 'io.spring.dependency-management' 83 | 84 | 85 | dependencies { 86 | compile project(":core-module") 87 | compile('org.springframework.boot:spring-boot-starter-web') 88 | 89 | compileOnly "org.springframework.boot:spring-boot-configuration-processor" 90 | } 91 | } 92 | 93 | 94 | project(":batch-module") { 95 | apply plugin: 'org.springframework.boot' 96 | apply plugin: 'io.spring.dependency-management' 97 | 98 | 99 | dependencies { 100 | compile project(":core-module") 101 | compile('org.springframework.boot:spring-boot-starter-batch') 102 | 103 | compileOnly "org.springframework.boot:spring-boot-configuration-processor" 104 | 105 | testCompile('org.springframework.boot:spring-boot-starter-test') 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /core-module/src/main/java/io/honeymon/springboot/multimodule/core/entity/BaseAdmin.java: -------------------------------------------------------------------------------- 1 | package io.honeymon.springboot.multimodule.core.entity; 2 | 3 | 4 | import lombok.Getter; 5 | import org.springframework.data.jpa.domain.AbstractPersistable; 6 | 7 | import javax.persistence.Column; 8 | import javax.persistence.Entity; 9 | import javax.persistence.MappedSuperclass; 10 | 11 | /** 12 | * @author jiheon.kim on 2018. 6. 29. 13 | */ 14 | @Getter 15 | @Entity 16 | @MappedSuperclass 17 | public abstract class BaseAdmin extends AbstractPersistable { 18 | @Column(length = 50, unique = true, nullable = false) 19 | private String username; 20 | 21 | @Column(length = 200, nullable = false) 22 | private String password; 23 | 24 | @Column(length = 50, nullable = false) 25 | private String firstName; 26 | 27 | @Column(length = 50, nullable = false) 28 | private String lastName; 29 | 30 | 31 | } 32 | -------------------------------------------------------------------------------- /core-module/src/main/java/io/honeymon/springboot/multimodule/core/entity/BaseUser.java: -------------------------------------------------------------------------------- 1 | package io.honeymon.springboot.multimodule.core.entity; 2 | 3 | 4 | import lombok.Getter; 5 | import org.springframework.data.jpa.domain.AbstractPersistable; 6 | 7 | import javax.persistence.Column; 8 | import javax.persistence.Entity; 9 | import javax.persistence.MappedSuperclass; 10 | 11 | /** 12 | * @author jiheon.kim on 2018. 6. 29. 13 | */ 14 | @Getter 15 | @Entity 16 | @MappedSuperclass 17 | public abstract class BaseUser extends AbstractPersistable { 18 | @Column(length = 50, unique = true, nullable = false) 19 | private String username; 20 | 21 | @Column(length = 200, nullable = false) 22 | private String password; 23 | 24 | @Column(length = 50, nullable = false) 25 | private String firstName; 26 | 27 | @Column(length = 50, nullable = false) 28 | private String lastName; 29 | 30 | 31 | } 32 | -------------------------------------------------------------------------------- /core-module/src/main/java/io/honeymon/springboot/multimodule/core/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * @author jiheon.kim on 2018. 6. 29. 3 | */ 4 | package io.honeymon.springboot.multimodule.core; -------------------------------------------------------------------------------- /core-module/src/main/java/io/honeymon/springboot/multimodule/core/repository/BaseAdminRepository.java: -------------------------------------------------------------------------------- 1 | package io.honeymon.springboot.multimodule.core.repository; 2 | 3 | import io.honeymon.springboot.multimodule.core.entity.BaseAdmin; 4 | import io.honeymon.springboot.multimodule.core.entity.BaseUser; 5 | import org.springframework.data.jpa.repository.JpaRepository; 6 | 7 | /** 8 | * @author jiheon.kim on 2018. 7. 2. 9 | */ 10 | public interface BaseAdminRepository extends JpaRepository { 11 | } 12 | -------------------------------------------------------------------------------- /core-module/src/main/java/io/honeymon/springboot/multimodule/core/repository/BaseUserRepository.java: -------------------------------------------------------------------------------- 1 | package io.honeymon.springboot.multimodule.core.repository; 2 | 3 | import io.honeymon.springboot.multimodule.core.entity.BaseUser; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | /** 7 | * @author jiheon.kim on 2018. 7. 2. 8 | */ 9 | public interface BaseUserRepository extends JpaRepository { 10 | } 11 | -------------------------------------------------------------------------------- /core-module/src/main/resources/application-core.yml: -------------------------------------------------------------------------------- 1 | # 테스트 작성시 프로파일을 지정하지 않는 경우는... 2 | spring: 3 | datasource: 4 | url: jdbc:h2:mem:multi 5 | username: SA 6 | password: 7 | flyway: 8 | enabled: false 9 | 10 | --- 11 | spring.profiles: local 12 | spring: 13 | datasource: 14 | url: jdbc:h2:mem:multi-local 15 | username: SA 16 | password: 17 | flyway: 18 | enabled: false 19 | 20 | --- 21 | spring.profiles: dev 22 | spring: 23 | datasource: 24 | url: jdbc:h2:mem:multi-dev 25 | username: dbdev 26 | password: dbdevpassword 27 | flyway: 28 | enabled: false 29 | 30 | --- 31 | spring.profiles: stage 32 | spring: 33 | datasource: 34 | url: jdbc:h2:mem:multi-stage 35 | username: dbstage 36 | password: dbstagepassword 37 | flyway: 38 | enabled: false 39 | 40 | --- 41 | spring.profiles: prod 42 | spring: 43 | datasource: 44 | url: jdbc:h2:mem:multi-prod 45 | username: dbprod 46 | password: dbprodpassword 47 | flyway: 48 | enabled: false -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ihoneymon/multi-module/437518480a5827f61975bb8bb0f0ba35b4883edc/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Jul 02 16:46:02 KST 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.8.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /multi-module.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'multi-module' 2 | 3 | include "core-module" 4 | include "api-module" 5 | include "admin-module" 6 | include "batch-module" 7 | 8 | --------------------------------------------------------------------------------