diff --git a/bootstrap/src/main/java/kr/ac/kookmin/stream/StreamServerApplication.java b/bootstrap/src/main/java/kr/ac/kookmin/stream/StreamServerApplication.java deleted file mode 100644 index 7e19e23d..00000000 --- a/bootstrap/src/main/java/kr/ac/kookmin/stream/StreamServerApplication.java +++ /dev/null @@ -1,15 +0,0 @@ -package kr.ac.kookmin.stream; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.context.properties.ConfigurationPropertiesScan; - -@SpringBootApplication -@ConfigurationPropertiesScan -public class StreamServerApplication { - - public static void main(String[] args) { - SpringApplication.run(StreamServerApplication.class, args); - } - -} diff --git a/bootstrap/src/main/java/kr/ac/kookmin/stream/bootstrap/StreamServerApplication.java b/bootstrap/src/main/java/kr/ac/kookmin/stream/bootstrap/StreamServerApplication.java new file mode 100644 index 00000000..cf1e4c9c --- /dev/null +++ b/bootstrap/src/main/java/kr/ac/kookmin/stream/bootstrap/StreamServerApplication.java @@ -0,0 +1,20 @@ +package kr.ac.kookmin.stream.bootstrap; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.AutoConfigurationPackage; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.ConfigurationPropertiesScan; + +// 메인 클래스는 bootstrap 패키지에 두지만, 빈·JPA 엔티티와 레포지토리·설정 바인딩은 모든 모듈이 있는 루트 패키지에서 찾는다 +@SpringBootApplication(scanBasePackages = StreamServerApplication.BASE_PACKAGE) +@AutoConfigurationPackage(basePackages = StreamServerApplication.BASE_PACKAGE) +@ConfigurationPropertiesScan(StreamServerApplication.BASE_PACKAGE) +public class StreamServerApplication { + + static final String BASE_PACKAGE = "kr.ac.kookmin.stream"; + + public static void main(String[] args) { + SpringApplication.run(StreamServerApplication.class, args); + } + +} diff --git a/bootstrap/src/main/java/kr/ac/kookmin/stream/bootstrap/config/TimezoneConfig.java b/bootstrap/src/main/java/kr/ac/kookmin/stream/bootstrap/config/TimezoneConfig.java new file mode 100644 index 00000000..43234362 --- /dev/null +++ b/bootstrap/src/main/java/kr/ac/kookmin/stream/bootstrap/config/TimezoneConfig.java @@ -0,0 +1,18 @@ +package kr.ac.kookmin.stream.bootstrap.config; + +import jakarta.annotation.PostConstruct; +import java.util.TimeZone; +import kr.ac.kookmin.stream.common.DateUtil; +import org.springframework.context.annotation.Configuration; + +/** + * JVM 기본 시간대를 서울로 고정한다. 컨테이너 시간대(UTC)와 관계없이 {@code LocalDateTime.now()} 등이 한국 시간을 쓰게 한다. + */ +@Configuration +public class TimezoneConfig { + + @PostConstruct + void setDefaultTimeZone() { + TimeZone.setDefault(TimeZone.getTimeZone(DateUtil.KST)); + } +} diff --git a/bootstrap/src/test/java/kr/ac/kookmin/stream/DomainImplAccessTests.java b/bootstrap/src/test/java/kr/ac/kookmin/stream/bootstrap/DomainImplAccessTests.java similarity index 97% rename from bootstrap/src/test/java/kr/ac/kookmin/stream/DomainImplAccessTests.java rename to bootstrap/src/test/java/kr/ac/kookmin/stream/bootstrap/DomainImplAccessTests.java index f0cca2fe..5deb4b75 100644 --- a/bootstrap/src/test/java/kr/ac/kookmin/stream/DomainImplAccessTests.java +++ b/bootstrap/src/test/java/kr/ac/kookmin/stream/bootstrap/DomainImplAccessTests.java @@ -1,4 +1,4 @@ -package kr.ac.kookmin.stream; +package kr.ac.kookmin.stream.bootstrap; import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes; import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; diff --git a/bootstrap/src/test/java/kr/ac/kookmin/stream/ModularityTests.java b/bootstrap/src/test/java/kr/ac/kookmin/stream/bootstrap/ModularityTests.java similarity index 67% rename from bootstrap/src/test/java/kr/ac/kookmin/stream/ModularityTests.java rename to bootstrap/src/test/java/kr/ac/kookmin/stream/bootstrap/ModularityTests.java index 4e08dea7..5ff71dec 100644 --- a/bootstrap/src/test/java/kr/ac/kookmin/stream/ModularityTests.java +++ b/bootstrap/src/test/java/kr/ac/kookmin/stream/bootstrap/ModularityTests.java @@ -1,4 +1,4 @@ -package kr.ac.kookmin.stream; +package kr.ac.kookmin.stream.bootstrap; import org.junit.jupiter.api.Test; import org.springframework.modulith.core.ApplicationModules; @@ -6,7 +6,8 @@ class ModularityTests { - private final ApplicationModules modules = ApplicationModules.of(StreamServerApplication.class); + // 메인 클래스 패키지(bootstrap)가 아니라 루트 패키지를 기준으로 모듈을 나눈다 + private final ApplicationModules modules = ApplicationModules.of(StreamServerApplication.BASE_PACKAGE); @Test void verify() { diff --git a/bootstrap/src/test/java/kr/ac/kookmin/stream/MySqlIntegrationTest.java b/bootstrap/src/test/java/kr/ac/kookmin/stream/bootstrap/MySqlIntegrationTest.java similarity index 98% rename from bootstrap/src/test/java/kr/ac/kookmin/stream/MySqlIntegrationTest.java rename to bootstrap/src/test/java/kr/ac/kookmin/stream/bootstrap/MySqlIntegrationTest.java index 9ad15035..36ae1e06 100644 --- a/bootstrap/src/test/java/kr/ac/kookmin/stream/MySqlIntegrationTest.java +++ b/bootstrap/src/test/java/kr/ac/kookmin/stream/bootstrap/MySqlIntegrationTest.java @@ -1,4 +1,4 @@ -package kr.ac.kookmin.stream; +package kr.ac.kookmin.stream.bootstrap; import java.util.Map; import org.springframework.boot.test.context.SpringBootTest; diff --git a/bootstrap/src/test/java/kr/ac/kookmin/stream/RentalApplyConcurrencyTest.java b/bootstrap/src/test/java/kr/ac/kookmin/stream/bootstrap/RentalApplyConcurrencyTest.java similarity index 99% rename from bootstrap/src/test/java/kr/ac/kookmin/stream/RentalApplyConcurrencyTest.java rename to bootstrap/src/test/java/kr/ac/kookmin/stream/bootstrap/RentalApplyConcurrencyTest.java index 1fb15c15..b837187a 100644 --- a/bootstrap/src/test/java/kr/ac/kookmin/stream/RentalApplyConcurrencyTest.java +++ b/bootstrap/src/test/java/kr/ac/kookmin/stream/bootstrap/RentalApplyConcurrencyTest.java @@ -1,4 +1,4 @@ -package kr.ac.kookmin.stream; +package kr.ac.kookmin.stream.bootstrap; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; diff --git a/docs/conventions/architecture.md b/docs/conventions/architecture.md index fcd48b89..0bc04259 100644 --- a/docs/conventions/architecture.md +++ b/docs/conventions/architecture.md @@ -201,7 +201,11 @@ Gradle 모듈 분리가 도메인 간 경계를 컴파일 타임에 막고, Spri ### 4-2. 패키지 컨벤션 (탐지 규칙) -- `@Modulithic` 메인 클래스는 `bootstrap`의 `{basePackage}`에 둔다. +- 메인 클래스는 `{basePackage}.bootstrap`에 두고, 탐색 범위는 `{basePackage}`로 넓힌다. 기본값(메인 클래스 패키지)으로 두면 다른 모듈의 빈·JPA 엔티티·레포지토리·`@ConfigurationProperties`를 찾지 못한다. + - 빈: `@SpringBootApplication(scanBasePackages = ...)` + - JPA 엔티티·레포지토리: `@AutoConfigurationPackage(basePackages = ...)` + - 설정 바인딩: `@ConfigurationPropertiesScan(...)` + - Modulith 모듈 탐지: `ApplicationModules.of("{basePackage}")` (클래스를 넘기면 그 패키지가 루트가 된다) - **Gradle 경로와 Java 패키지는 별개다.** 도메인은 Gradle 상 `:core:domain:{도메인}`이지만 패키지는 루트 직속 형제로 둔다. ``` @@ -262,7 +266,7 @@ import org.springframework.modulith.ApplicationModule; // bootstrap/src/test/.../ModularityTests.java class ModularityTests { - private final ApplicationModules modules = ApplicationModules.of(Application.class); + private final ApplicationModules modules = ApplicationModules.of("{basePackage}"); // 메인 클래스 패키지(bootstrap)가 아닌 루트 기준 @Test void verify() {