[Spring Boot] 멀티 모듈 환경에서 프로젝트 환경설정관리의 중요성(feat. application.yml)

Simple·2023년 8월 5일
1

개선 시리즈

목록 보기
1/5

상황


진행하고있는 프로젝트의 멀티 모듈 프로젝트 설정은 각 모듈이 관리하고 있다.

즉, 프로젝트 설정의 중복을 허용한 상황이다.

그렇다면 기존에 왜 중복을 허용했을까?

  • 3개의 모듈이 사용하는 설정, 2개의 모듈이 사용하는 설정, 1개의 모듈만 사용하는 설정 등 다양한 상황이 존재했다.
  • 기존 중복의 정도가 많이 중복되거나 문제가 될 것 같지 않았다.

사실 core를 통해 깔끔하게 관리할 수 있다는 것을 알았으나 개선 사항으로 미뤘다. 추후에 어떤 결과를 낳았는지,,, 곧 알 수 있다.

이와 같은 상태로 프로젝트를 진행하는중, mongodb에 저장하는 로직이 core 모듈에 존재하기 때문에 core의 application.yml에 mongodb의 uri를 설정해주고, local과 dev의 mongodb는 다르기 때문에 환경에 맞는 uri를 설정해줬다.

그런데 dev환경 실행시 localhost:27017만 연결하는 문제를 직면했다.
분명 스프링 부트 실행시 로그에도 "dev"의 표시도 명확하지만 local mongodb에만 연결이 되었다.

실행을 여러번 하면서 문득 떠오른 것이,
어? 설정은 core에 했는데 실행은 api 모듈에서 하는 것 때문인가? 라는 생각을 했고
api 모듈 application.yml에 dev환경에 맞는 mongodb uri를 넣어준 결과
연결이 잘 된 결과를 볼 수 있었다.

기존 api 모듈의 appication.yml

spring:
  profiles:
    active: local

  servlet:
    multipart:
      max-file-size: 10MB

cloud:
  aws:
    credentials:
      access-key: ${S3_ACCESS_KEY}
      secret-key: ${S3_SECRET_KEY}
    s3:
      bucket: ${S3_BUCKET}
      file-path: ${S3_FILEPATH}
    region:
      static: ${S3_REGION}
    stack:
      auto: false
jwt:
  secret: ${SECRET_KEY}
  access-expiration-time: ${ACCESS_EXPIRED_TIME}
  refresh-expiration-time: ${REFRESH_EXPIRED_TIME}
  refresh-valid-time: ${REFRESH_VALID_TIME}

---
spring:
  config:
    activate:
      on-profile: local

  data:
    redis:
      host: ${REDIS_HOST}
      port: ${REDIS_PORT}

    mongodb:
      uri: ${MONGO_DB_URI}

  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: ${DB_URL}
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}

  jpa:
    open-in-view: false
    database-platform: org.hibernate.dialect.MySQL8Dialect
    hibernate:
      ddl-auto: validate
      naming:
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
    properties:
      hibernate:
        show_sql: true
        format_sql: true
#    defer-datasource-initialization: true
  sql:
    init:
      mode: never
---
spring:
  config:
    activate:
      on-profile: dev

  data:
    redis:
      host: ${REDIS_HOST}
      port: ${REDIS_PORT}

    mongodb:
      uri: ${DEV_MONGO_DB_URI}

  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: ${DEV_DB_URL}
    username: ${DEV_DB_USERNAME}
    password: ${DEV_DB_PASSWORD}

  jpa:
    open-in-view: false
    database-platform: org.hibernate.dialect.MySQL8Dialect
    hibernate:
      ddl-auto: validate
      naming:
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
    properties:
      hibernate:
        show_sql: true
        format_sql: true
  sql:
    init:
      mode: never

기존 core 모듈의 appicaliton.yml

spring:
  profiles:
    active: local

chat-gpt:
  api-key: ${CHAT_GPT_API_KEY}
  model: ${CHAT_GPT_MODEL}
  role: ${CHAT_GPT_ROLE}

cloud:
  aws:
    credentials:
      access-key: ${S3_ACCESS_KEY}
      secret-key: ${S3_SECRET_KEY}
    s3:
      bucket: ${S3_BUCKET}
      file-path: ${S3_FILEPATH}
    region:
      static: ${S3_REGION}
    stack:
      auto: false
jwt:
  secret: ${SECRET_KEY}
  access-expiration-time: ${ACCESS_EXPIRED_TIME}
  refresh-expiration-time: ${REFRESH_EXPIRED_TIME}
  refresh-valid-time: ${REFRESH_VALID_TIME}

---
spring:
  config:
    activate:
      on-profile: local

  data:
    redis:
      host: ${REDIS_HOST}
      port: ${REDIS_PORT}

    mongodb:
      uri: ${MONGO_DB_URI}

  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: ${DB_URL}
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}

  jpa:
    open-in-view: false
    database-platform: org.hibernate.dialect.MySQL8Dialect
    hibernate:
      ddl-auto: create
      naming:
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
    properties:
      hibernate:
        show_sql: true
        format_sql: true
    defer-datasource-initialization: true
  sql:
    init:
      mode: always
---
spring:
  config:
    activate:
      on-profile: dev

  data:
    redis:
      host: ${REDIS_HOST}
      port: ${REDIS_PORT}

    mongodb:
      uri: ${DEV_MONGO_DB_URI}

  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: ${DEV_DB_URL}
    username: ${DEV_DB_USERNAME}
    password: ${DEV_DB_PASSWORD}

  jpa:
    open-in-view: false
    database-platform: org.hibernate.dialect.MySQL8Dialect
    hibernate:
      ddl-auto: validate
      naming:
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
    properties:
      hibernate:
        show_sql: true
        format_sql: true
  sql:
    init:
      mode: never

batch모듈은 생략한다.

이렇듯 api 모듈에서만 사용하는 설정과 별개로
jwt, redis, mysql, mongodb의 중복 코드가 엄청나다.

개선


  • 공통되는 부분을 core에서 관리한다.

api 모듈의 appication.yml

spring:
  profiles:
    active: local
    include: core

  servlet:
    multipart:
      max-file-size: 10MB

---
spring:
  config:
    activate:
      on-profile: local

---
spring:
  config:
    activate:
      on-profile: dev

core 모듈의 appication.yml

chat-gpt:
  api-key: ${CHAT_GPT_API_KEY}
  model: ${CHAT_GPT_MODEL}
  role: ${CHAT_GPT_ROLE}

cloud:
  aws:
    credentials:
      access-key: ${S3_ACCESS_KEY}
      secret-key: ${S3_SECRET_KEY}
    s3:
      bucket: ${S3_BUCKET}
      file-path: ${S3_FILEPATH}
    region:
      static: ${S3_REGION}
    stack:
      auto: false

jwt:
  secret: ${SECRET_KEY}
  access-expiration-time: ${ACCESS_EXPIRED_TIME}
  refresh-expiration-time: ${REFRESH_EXPIRED_TIME}
  refresh-valid-time: ${REFRESH_VALID_TIME}

---
spring:
  config:
    activate:
      on-profile: local
  data:
    redis:
      host: ${REDIS_HOST}
      port: ${REDIS_PORT}

    mongodb:
      uri: ${MONGO_DB_URI}

  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: ${DB_URL}
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}

  jpa:
    open-in-view: false
    database-platform: org.hibernate.dialect.MySQL8Dialect
    hibernate:
      ddl-auto: create
      naming:
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
    properties:
      hibernate:
        show_sql: true
        format_sql: true
    defer-datasource-initialization: true
  sql:
    init:
      mode: always

---
spring:
  config:
    activate:
      on-profile: dev

  data:
    redis:
      host: ${REDIS_HOST}
      port: ${REDIS_PORT}

    mongodb:
      uri: ${DEV_MONGO_DB_URI}

  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: ${DEV_DB_URL}
    username: ${DEV_DB_USERNAME}
    password: ${DEV_DB_PASSWORD}

  jpa:
    open-in-view: false
    database-platform: org.hibernate.dialect.MySQL8Dialect
    hibernate:
      ddl-auto: validate
      naming:
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
    properties:
      hibernate:
        show_sql: true
        format_sql: true
  sql:
    init:
      mode: never

batch 모듈의 appication.yml

spring:
  profiles:
    active: local
    include: core

  batch:
    job:
      enabled: false
    jdbc:
      initialize-schema: always

server:
  port: 8081

---
spring:
  config:
    activate:
      on-profile: local

ml-server:
  url: ${ML_SERVER_URL}

---
spring:
  config:
    activate:
      on-profile: dev

ml-server:
  url: ${DEV_ML_SERVER_URL}

비교도 안될 정도로 훨씬 깔끔해진 것을 볼 수 있다.

기대효과


  • 동일한 코드의 반복을 제거함으로써 잠재적인 버그의 위협 사전 차단
  • 반복되는 코드로 인한 유지 보수의 오버헤드 감소

느낀점


  • 잠재적인 버그를 직접 몸소 경험함으로써 중복되는 코드의 위험성을 느꼈고,
  • 환경별 설정 및 관리 대한 편의성을 느꼈다.
profile
몰입하는 개발자

0개의 댓글