Skip to content

Conversation

@move-hoon
Copy link
Member

@move-hoon move-hoon commented Jun 16, 2025

🔗 관련 이슈

📘 작업 유형

  • ✨ Feature (기능 추가)
  • 🐞 Bugfix (버그 수정)
  • 🔧 Refactor (코드 리팩토링)
  • ⚙️ Chore (환경 설정)
  • 📝 Docs (문서 작성 및 수정)
  • ✅ Test (기능 테스트)
  • 🎨 style (코드 스타일 수정)

📙 작업 내역

  • ImportSelector 기반 모듈별 인프라 선택 적용 기능을 도입했습니다.
  • 현재는 AsyncConfig, JpaConfig만 설정되어 있는 상황인데, 추가 Config 클래스가 생성될 시 다음과 같이 추가해주시면 되겠습니다.
  1. infra 모듈에 config 하위에 해당 클래스를 패키지를 생성한 후 클래스 구현
  2. InfraBaseConfigGroup 열거형 클래스에 생성한 클래스 등록
  3. 적용할 모듈의 InfraConfig 클래스에 적용할 클래스 등록

🧪 테스트 내역

  • 브라우저/기기에서 동작 확인
  • 엣지 케이스 테스트 완료
  • 기존 기능 영향 없음

🎨 스크린샷 또는 시연 영상 (선택)

기능 미리보기 기능 미리보기
기능 설명 기능 설명

✅ PR 체크리스트

  • 커밋 메시지가 명확합니다
  • PR 제목이 컨벤션에 맞습니다
  • 관련 이슈 번호를 작성했습니다
  • 기능이 정상적으로 작동합니다
  • 불필요한 코드를 제거했습니다

💬 추가 설명 or 리뷰 포인트 (선택)

  • 설계를 하다보니, 생각할 부분이 너무 많아져서 좀 오래걸렸습니다..
  • 제가 모듈을 다시 살펴보면서 든 생각이 있는데 이에 대해서 민우님의 생각이 궁금합니다.

새로운 InfraStructure 관련 모듈 생성? or 기존 infra 모듈 내부에 구현

  • 현재 domain 모듈은 순수한 비즈니스 로직을 담당하기 위한 계층으로, 핵심 도메인 모델과 서비스 로직만을 포함하고 있으며 외부 인프라 세부 사항에 의존하지 않도록 구성되어야 합니다.
  • 즉, 도메인 모듈은 Repository 인터페이스(포트)에만 의존해야 하며, 구체 구현체를 직접적으로 의존하면 안됩니다.
  • 예를 들어, 구체 구현체가 RDB → NoSQL로 변경되더라도 도메인 모듈의 코드는 변경되지 않아야 합니다. 이는 도메인이 레포지토리 인터페이스(포트)에만 의존하고, 구현체는 인프라스트럭처 모듈에서 관리되기 때문입니다.
  • 따라서, NoSQL로 전환이 필요한 경우에도, 새로운 구현체를 인프라스트럭처 모듈에 추가하고 DI(의존성 주입)를 통해 교체함으로써, 도메인 계층은 그대로 유지되며 변경에 닫힌 구조를 유지할 수 있을 것 같다는 생각이 들었습니다.
  • 새로운 InfraStructure 모듈을 생성할 경우 내부에 하위 모듈로 RDB, NoSQL 등의 Repository 구현체 모듈을 두면 어떨까 하는 생각이 들긴합니다.
  • 그래서 이에 대해서 새로운 모듈을 만들지 아니면 infra 모듈에서 repository 구현체를 관리할 지 의견을 주시면 감사할 것 같습니다. (저는 Infra 모듈은 각종 Config, 외부 API 통신을 관리하기에 별도 모듈을 또 만드는 방식이 나을 것 같다는 생각입니다.)

domain 모듈은 별도의 Config 클래스를 생성해서 infra 모듈에 의존하지 않고 별도 관리

  • 도메인 모듈의 경우 순수 비즈니스 로직을 담당하므로 웬만하면 Config 클래스가 필요하지 않을 것 같다고 생각이 들었습니다.
  • 그런데 JpaConfig 클래스를 infra 모듈에서 관리하고 있는데, 해당 클래스는 Entity들을 Hibernate EntityManager에 등록시켜주고, 찾은 Repository 인터페이스들의 구현체를 자동 생성해 주는 기능을 제공하고 있습니다.
  • 따라서 결국 JpaConfig 클래스 하나를 위해 infra 모델을 의존하게 되는 상황이 벌어집니다.
  • 하지만 저는 해당 클래스 하나로 인해 infra 모듈에 의존하게 되는 방식이 좋지 못하다고 판단했습니다.
  • 따라서 위와 같은 상황이 발생할 경우 CoreDomainConfig 클래스를 따로 운영해서, 관리해주는게 어떨까 하는 생각이 들었습니다.
  • 또한, 앞으로 도메인 모듈을 위해 Config 클래스가 필요하게 된다면 별도로 도메인 모듈에서 구현을 해주는게 어떨까 하는 생각도 같이 들었습니다.
  • 참고로 망나니개발자님의 멀티모듈 코드에서도 Domain 모듈에 대해서는 별도의 Config 클래스를 운영 중이신것을 확인했습니다.
@EnableTransactionManagement
@EntityScan("com.mangkyu.appstore")
@EnableJpaRepositories("com.mangkyu.appstore")
public class CoreDomainConfig {
}

편하게 읽어주시고 천천히 리뷰달아주시면 감사하겠습니다 🙇🏻‍♂️

move-hoon added 12 commits June 16, 2025 19:34
- @async 메서드 실행을 위한 Executor를 taskExecutor()로 분리 구성
- 이벤트 리스너의 실행도 비동기로 처리되도록 ApplicationEventMulticaster 재정의
- 각 기능별 Executor를 분리하여 용도에 맞는 설정을 구성
- 트랜잭션 관리 활성화를 위한 @EnableTransactionManagement 적용
- JPA 엔티티 스캔 경로 지정 (@EntityScan)
- JPA 리포지토리 스캔 경로 지정 (@EnableJpaRepositories)
- AsyncConfig와 JpaConfig를 그룹에 포함시켜 모듈별 인프라 선택 등록에 활용
- 추후 Config 클래스 추가 시 해당 enum에 반드시 명시
- InfraBaseConfigGroup 배열을 받아 필요한 인프라 설정만 선택적으로 등록할 수 있도록 설계
- EnableInfraBaseConfig 어노테이션에 지정된 InfraBaseConfigGroup 열거형 값을 기반으로, 각 모듈에서 필요한 인프라 설정 클래스만 선택적으로 스프링 빈으로 등록하도록 구현
- DeferredImportSelector 인터페이스를 구현하여, 스프링 애플리케이션 컨텍스트 초기화 시점에 필요한 설정만 지연해서 임포트함으로써 불필요한 빈 등록을 방지
- Admin 모듈 InfraConfig에 @EnableInfraBaseConfig 어노테이션 적용
- InfraBaseConfigGroup.JPA 설정을 통해 JPA 관련 빈만 선택적으로 등록하도록 구성
- Apis 모듈 InfraConfig에 @EnableInfraBaseConfig 어노테이션 적용
- InfraBaseConfigGroup.JPA와 ASYNC 설정을 통해 필요한 인프라 빈을 선택적으로 등록
- Batch 모듈 InfraConfig에 @EnableInfraBaseConfig 어노테이션 적용을 위한 어노테이션 주석 추가
- 향후 @EnableInfraBaseConfig([InfraBaseConfigGroup.FCM])로 FCM 인프라 설정 모듈 자동 등록 예정
Copy link
Member

@minwoo1999 minwoo1999 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM


private fun getValues(metadata: AnnotationMetadata): Array<InfraBaseConfigGroup> {
val attributes = metadata.getAnnotationAttributes(EnableInfraBaseConfig::class.java.name)
@Suppress("UNCHECKED_CAST")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unchecked_cast를 쓴 이유가 궁금해요!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

결론적으로는 Spring에서 어노테이션 파싱 시 타입을 보장해주기 때문에, 관련한 컴파일러의 경고를 끄기 위해서였습니다.

런타임에는 Java 5 이전 버전과의 호환성을 유지하기 위해 제네릭의 타입 정보가 소거(type erasure) 됩니다.
따라서 컴파일러는 Array처럼 구체적인 타입 정보를 런타임에 확인할 수 없다는 점에서 경고를 발생시키게 됩니다.
이는 “정말로 이 타입이 맞는지 런타임에 보장할 수 없는데 괜찮겠느냐?“는 의미의 경고입니다.

하지만 Spring 프레임워크는 어노테이션을 파싱할 때 내부적으로 타입 정보를 정확하게 처리해주기 때문에,
@EnableInfraBaseConfig에 선언된 InfraBaseConfigGroup 배열 타입은 안전하게 사용할 수 있습니다.

따라서, 스프링에서 보장해주기 때문에 관련 경고를 끄기 위하여 사용했습니다!

Comment on lines +41 to +50
@Bean(name = ["taskExecutor"])
fun taskExecutor(): Executor {
val executor = ThreadPoolTaskExecutor()
executor.corePoolSize = 3
executor.setThreadNamePrefix("async-")
executor.setWaitForTasksToCompleteOnShutdown(true)
executor.setAwaitTerminationSeconds(30)
executor.initialize()
return executor
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이건 일단 예시코드라고 보면되는거죠!?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

예시코드이자, 실전에 사용을 염두해두고 짠 코드긴 합니다!

Comment on lines +6 to +9
@EntityScan("org.yapp")
@EnableJpaRepositories("org.yapp")
class CoreDomainConfig {
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 동일한 생각입니다.
domain 모듈에서는 Repository interface만 정의하고, 설정은 전혀 포함하지 않는 것이 더 바람직해 보입니다.
별도의 구현체는 infra 계층에서 구성하고, 필요한 경우 교체 가능한 형태로 가져가는 구조가 좋다고 생각합니다!

@minwoo1999
Copy link
Member

infra 모듈에서 repository 구현체를 관리할 지 의견을 주시면 감사할 것 같습니다. (저는 Infra 모듈은 각종 Config, 외부 API 통신을 관리하기에 별도 모듈을 또 만드는 방식이 나을 것 같다는 생각입니다.
-> 만약 모듈을 분리한다고하면 어떤 네이밍으로 분리할지 궁금합니다. 분리는 해도 좋으나 다만 관리해야 할 모듈이 너무 많아지는게 ㅠㅠ 조금 걱정이긴하네요

@minwoo1999
Copy link
Member

제가 생각한 구조입니다

domain/
├── model/ ← @entity
└── repository/ ← Repository 인터페이스

infra/
├── repository/ ← Repository 구현체 (Spring Data JPA에 위임)
└── config/ ← JPA 설정 (JpaConfig 등)

infra는 domain의 모델과 레포지토리 인터페이스를 가져와서 실제 DB 연동을 구현

infra는 @EntityScan, @EnableJpaRepositories 설정을 통해 자동 구현체 생성

domain은 순수 비즈니스 계층 → 설정 없이 interface와 model만 제공

// infra/src/main/kotlin/org/yapp/infra/config/JpaConfig.kt
package org.yapp.infra.config

import org.springframework.boot.autoconfigure.domain.EntityScan
import org.springframework.context.annotation.Configuration
import org.springframework.data.jpa.repository.config.EnableJpaRepositories
import org.springframework.transaction.annotation.EnableTransactionManagement

@configuration
@EnableTransactionManagement
@EntityScan("org.yapp.domain.model")
@EnableJpaRepositories("org.yapp.domain.repository")
class JpaConfig

의존성 방향

infra → domain (O)

domain → infra (X)

@move-hoon
Copy link
Member Author

domain/
├── model/ ← https://github.com/entity
└── repository/ ← Repository 인터페이스

infra/
├── repository/ ← Repository 구현체 (Spring Data JPA에 위임)
└── config/ ← JPA 설정 (JpaConfig 등)

민우님 의견 주셔서 감사합니다 ㅎㅎ
Infra ──→ Domain 단방향 의존 방식이 맞는 것 같습니다!!
해당 조건을 지키려면 무조건 infra의 Config를 사용하지 않고, 도메인만의 Config 클래스를 별도로 구현을 해야할 것 같네요!
(해당 코드가 망나니개발자님께서 말씀하셨던 우발적 중복이지 않을까? 라는 생각이 들었습니다!!)

아 그리고, 저도 다시 생각해보니 repository 구현체를 infra 모듈에서 관리하는 부분에 대해서는 긍정적인 입장으로 바뀌었습니다 😊

처음에는 DB를 외부 시스템과는 좀 다른 범주로 생각했었는데, 결국 DB도 외부 API와 마찬가지로 외부 시스템에 해당하고,
이를 기술적으로 다루는 책임은 모두 infra 계층에 두는 게 맞겠다는 생각이 들었습니다.

@minwoo1999
Copy link
Member

domain/
├── model/ ← https://github.com/entity
└── repository/ ← Repository 인터페이스
infra/
├── repository/ ← Repository 구현체 (Spring Data JPA에 위임)
└── config/ ← JPA 설정 (JpaConfig 등)

민우님 의견 주셔서 감사합니다 ㅎㅎ Infra ──→ Domain 단방향 의존 방식이 맞는 것 같습니다!! 해당 조건을 지키려면 무조건 infra의 Config를 사용하지 않고, 도메인만의 Config 클래스를 별도로 구현을 해야할 것 같네요! (해당 코드가 망나니개발자님께서 말씀하셨던 우발적 중복이지 않을까? 라는 생각이 들었습니다!!)

아 그리고, 저도 다시 생각해보니 repository 구현체를 infra 모듈에서 관리하는 부분에 대해서는 긍정적인 입장으로 바뀌었습니다 😊

처음에는 DB를 외부 시스템과는 좀 다른 범주로 생각했었는데, 결국 DB도 외부 API와 마찬가지로 외부 시스템에 해당하고, 이를 기술적으로 다루는 책임은 모두 infra 계층에 두는 게 맞겠다는 생각이 들었습니다.

도메인만의 Config 클래스를 별도로 구현을 해야할 것 같네요! -> 이 방향 너무좋은 것 같아요!
repository 구현체를 infra 모듈에서 관리하는 부분에 대해서는 긍정적인 입장 -> 저도 매우 동감!!

이 방향으로 config 셋팅 진행해주시면 될 것 같아요~

@move-hoon move-hoon merged commit b07f956 into develop Jun 16, 2025
2 checks passed
@move-hoon move-hoon deleted the BOOK-39-feature/#5 branch June 16, 2025 14:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BOOK-39/feat] ImportSelector 기반 모듈별 인프라 구성 적용 기능 구현

3 participants