Skip to content

Admin 멀티 모듈 적용

Lee kangmin edited this page Sep 24, 2024 · 2 revisions

Admin 멀티 모듈에 적용하기 !

image

  • apiadmin은 따로 띄우는 것이다❗️
    • 아예 격리된 망에서 띄우는 것이다. 같이 있으면 안된다.
    • 별도의 애플리케이션을 띄우는 것이다 !!
  • 외부에서 들어오는 요청(송금, . . . )
    • 외부 클라이언트가 api 서버에서 /admin을 한다고 admin 서버에 접속할 수 없다 ❌
  • admin 서버
    • public IP로 들어오는 요청을 받지 않는다. 회사 내부망에서만 접속이 가능하다. 당연한 말이지만, 일반 사용자는 admin 서버가 있는지도 모르게 해야 한다.
    • api와는 별도의 애플리케이션
  • 같은 domain을 의존해야 하나요?
    • yes. domain 재사용 하려고 멀티 모듈을 적용한 것이다.
  • Security Config hasRole
    • Admin을 hasRole로 구분할 필요는 없다 ❌
    • 어차피 다른 서버이다 !

멀티 모듈을 왜 사용하는가?

  • 공통 코드 추출 ❗️ 재사용성 ❗️
  • apiadmin이 멀티 모듈로 관리되지 않을 때 테이블의 변경사항이 발생한다면?
    • 주로 클라이언트에 가까운 api에서 변경이 자주 발생한다.
      • ex) 컬럼 추가
    • api쪽 db에 변경이 발생할 때마다 admin db에도 똑같은 작업을 반복 수행해주어야 한다.
  • 멀티 모듈 적용 시 apiadmindomain 모듈을 함께 사용한다.
    • domain이 한 번만 변경될 것이다. 같은 domain 모듈에 의존하고 있기 때문이다 ❗️

Common 모듈을 비추하는 이유

image

  • common
    • 🙅 공통 로직을 전부 다 여기다 쓴다. → 계속 커진다.
    • God Object: 모든 걸 다 갖고 있는 모듈을 경계해야 한다.
    • 이 코드는 admin에도 있어야 할 것 같고 api에도 있어야 할 것 같은데?
      • → 이러면서 코드가 **common**에 계속해서 추가된다.
      • 그럼 모듈을 나누는 게 의미가 없어질 수가 있다. 😵
  • **“공통”**이라는 것은 결속력이 없다.
  • domain
    • Entity, Repository만 있다. → 역할이 명확하다.
    • 나눠야 하는 이유: 재사용성이 높아서 !!

멀티 모듈과 build.gradle - api 모듈에서도 jpa 의존성이 필요하다면?

1. java-library plugin 추가 → implementation 자리에 모듈 이름 적기

build.gradle(root)

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.3.3'
    id 'io.spring.dependency-management' version '1.1.6'
    id 'java-library'
}

allprojects {
    apply plugin: 'java'
    apply plugin: 'org.springframework.boot'
    apply plugin: 'io.spring.dependency-management'
    apply plugin: 'java-library'

    dependencies {
        compileOnly 'org.projectlombok:lombok'
        annotationProcessor 'org.projectlombok:lombok'
        testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
    }

    repositories {
        mavenCentral()
    }
    tasks.named('test') {
        useJUnitPlatform()
    }
    java {
        toolchain {
            languageVersion = JavaLanguageVersion.of(17)
        }
    }
}

bootJar {
    enabled = false
}

jar {
    enabled = false
}

build.gradle(badminton-api)

dependencies {
    implementation project(':badminton-domain')
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    implementation 'org.springframework.boot:spring-boot-starter-security'
    implementation group: 'org.springdoc', name: 'springdoc-openapi-starter-webmvc-ui', version: '2.6.0'
    implementation 'io.jsonwebtoken:jjwt-api:0.12.3'
    implementation group: 'me.paulschwarz', name: 'spring-dotenv', version: '4.0.0'

    runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.3'
    runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.3'
    testImplementation 'org.springframework.security:spring-security-test'

}

test {
    useJUnitPlatform()
}

bootJar {
    enabled = true
}

jar {
    enabled = false
}

build.gradle(badminton-domain)

dependencies {
    api('org.springframework.boot:spring-boot-starter-data-jpa')
    implementation 'org.springframework.boot:spring-boot-starter-security'
    implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation 'org.springframework.security:spring-security-test'
    runtimeOnly 'com.mysql:mysql-connector-j'
    implementation group: 'org.springdoc', name: 'springdoc-openapi-starter-webmvc-ui', version: '2.6.0'

}

bootJar {
    enabled = false
}

jar {
    enabled = true
}

image

변경 전에는 build.gradle(badminton-api)에

implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

의존성이 추가되어 있다.

이를 아래와 같이 바꿔보자.

plugin에 `id 'java-library`` 를 추가하고, build.gradle(badminton-domain)에서 JPA 의존성을 추가하는 부분을 아래와 같이 수정한다.

api 'org.springframework.boot:spring-boot-starter-data-jpa'

이렇게 하면 api 모듈에 의존성이 전이되어 JPA 기능을 사용할 수 있다.

다만, 이를 남용하면 안된다. 왠만하면 implementation으로 쓰는 것이 좋다. api를 쓰면 의존성이 전이된다.

2. 어댑터 패턴 사용

API가 필요로 하는 JPA의 기능을 domain 모듈에 Adapter로 만들면 된다.

domain에 어댑터 클래스를 하나 만든다. 어댑터 클래스가 JPA의 기능을 내부적으로 들고 있는 것이다. api 모듈이 JPA에 의존을 안하고 domain에 있는 adapter를 쓰면 된다. api 모듈은 JPA를 몰라도, 기능을 사용할 수 있다.

apidomain에 의존하고 있으니 domain의 영향을 받는다.

모듈 빌드를 하면 jar파일이 생긴다. 의존을 안하게 만들면 빌드를 안해도 된다❗️

외부 의존이 필요할 때 이를 한 번 감싸서 실제적인 것에 의존하지 않도록 하는 것이 **adapter pattern**이다.

인터페이스에 의존한다는 것

인터페이스에서 메서드를 정의할 때 메서드 header만 정의한다. 구현(body)을 정의하지 않는다. body가 변경되지 않기 떄문에 이를 추상화라고 하는 것이다.

구현이 바뀔 때 B가 바뀌고, A는 변하지 않는다. 의존성의 흐름이 B → A로 흐르기 떄문이다.

DIP가 되면 의존을 받는 것이 아니라 의존을 하는 것이다.

header가 바뀌면? 이때는 당연히 영향이 갈 것이다. 그러나 header가 바뀔 확률은 body가 바뀔 확률보다 훨씬 낮다.