스프링부트

spring boot data MongoDB - 연결 설정 하기

알쓸개잡 2023. 12. 29.

NoSQL 데이터베이스로 가장 많이 사용되고 있는 MongoDB를 spring boot data MongoDB 를 통해서 사용하는 방법에 대해서 정리해 보고자 한다. 샘플 코드는 spring boot 3.2.1 버전을 기반으로 작성하였다.

 

Dependency

gradle

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.2.1'
    id 'io.spring.dependency-management' version '1.1.4'
}

...

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'
    ...
}

 

 

spring boot MongoDB properties

Spring boot에서 MongoDB를 위한 설정 정보를 주입받는 클래스는 MongoProperties 클래스이다.

MongoProperties 클래스에 주입 받는 속성은 다음과 같다.

@ConfigurationProperties(
    prefix = "spring.data.mongodb"
)
public class MongoProperties {
    public static final int DEFAULT_PORT = 27017;
    public static final String DEFAULT_URI = "mongodb://localhost/test";
    private String host;
    private Integer port = null;
    private List<String> additionalHosts;
    private String uri;
    private String database;
    private String authenticationDatabase;
    private final Gridfs gridfs = new Gridfs();
    private String username;
    private char[] password;
    private String replicaSetName;
    private Class<?> fieldNamingStrategy;
    private UuidRepresentation uuidRepresentation;
    private final Ssl ssl;
    private Boolean autoIndexCreation;
    
    ...
    
    public static class Ssl {
        private Boolean enabled;
        private String bundle;

        public Ssl() {
        }

        public boolean isEnabled() {
            return this.enabled != null ? this.enabled : this.bundle != null;
        }

        public void setEnabled(boolean enabled) {
            this.enabled = enabled;
        }

        public String getBundle() {
            return this.bundle;
        }

        public void setBundle(String bundle) {
            this.bundle = bundle;
        }
    }
    
    ...
}

@ConfigurationProperties 어노테이션의 prefix를 통해서 MongoDB 설정은 다음과 같이 시작함을 알 수 있다.

spring.data.mongodb.*

application.yml 에서 MongoDB 관련 프로퍼티 설정은 다음과 같다.

spring:
  data:
    mongodb:
      database: <기본데이터베이스명>
      authentication-database: <인증 데이터베이스명>
      username: <접속아이디>
      password: <접속비밀번호>
      host: <MongoDB 호스트>
      port: <MongoDB 포트>
      uuid-representation: <UUID를 BSON으로 변환할 때 사용할 표현 방식>
      replica-set-name: <replica set 이름>
      field-naming-strategy: <필드 이름 변환 방식>
      auto-index-creation: <인덱스 자동생성 여부>
      ssl:
        bundle: <ssl 접속을 위한 bundle 설정 이름>
        enabled: <ssl 접속 사용 여부>

 

uuid-representation 설정 값은 다음과 같다.

  • standard: UUID를 BSON으로 표현하는 표준 방식 (subtype4)
  • c_sharp_legacy: C# 드라이버에서 사용된 UUID 기존 표현 방식 (subtype3)
  • java_legacy: 자바 드라이버에서 사용된 UUID 기존 표현 방식 (subtype3)
  • python_legacy: 파이썬 드라이버에서 사용된 UUID 기존 표현 방식. standard 방식과 동일하나 subtype3을 사용함.

 

field-naming-strategy 설정 값은 다음과 같다.

  • org.springframework.data.mapping.model.SnakeCaseFieldNamingStrategy
  • org.springframework.data.mapping.model.CamelCaseAbbreviatingFieldNamingStrategy

field-naming-strategy에 설정된 값에 따라서 MongoDB에 저장되는 필드명은 다음과 같이 변경된다.

저장하는 클래스 정의가 다음과 같을 때

@Document(collection = "person")
@Getter
@ToString
public class Person {
    @Id
    private final UUID personId;
    private final String personName;
    private final Integer personAge;

    @Builder
    public Person(UUID personId, String personName, Integer personAge) {
        this.personId = personId;
        this.personName = personName;
        this.personAge = personAge;
    }
}

 

SnakeCaseFieldNamingStrategy는 다음과 같이 저장된다.

_id: UUID('xxxxxx')
person_name: '이름'
person_age: 나이
_class: "xxxx"

 

CamelCaseAbbreviatingFieldNamingStrategy는 다음과 같이 저장된다.

_id: UUID('xxxxxx')
pn: '이름'
pa: 나이
_class: "xxxx"

 

 

spring boot MongoDB auto configuration

org.springframework.boot:spring-boot-autoconfigure 모듈에는 MongoDB와 관련된 다음과 같은 auto configuration 이 제공된다.

 

org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration

MongoAutoConfiguration 클래스는 다음과 같다.

@AutoConfiguration
@ConditionalOnClass(MongoClient.class)
@EnableConfigurationProperties(MongoProperties.class)
@ConditionalOnMissingBean(type = "org.springframework.data.mongodb.MongoDatabaseFactory")
public class MongoAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean(MongoConnectionDetails.class)
	PropertiesMongoConnectionDetails mongoConnectionDetails(MongoProperties properties) {
		return new PropertiesMongoConnectionDetails(properties);
	}

	@Bean
	@ConditionalOnMissingBean(MongoClient.class)
	public MongoClient mongo(ObjectProvider<MongoClientSettingsBuilderCustomizer> builderCustomizers,
			MongoClientSettings settings) {
		return new MongoClientFactory(builderCustomizers.orderedStream().toList()).createMongoClient(settings);
	}

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnMissingBean(MongoClientSettings.class)
	static class MongoClientSettingsConfiguration {

		@Bean
		MongoClientSettings mongoClientSettings() {
			return MongoClientSettings.builder().build();
		}

		@Bean
		StandardMongoClientSettingsBuilderCustomizer standardMongoSettingsCustomizer(MongoProperties properties,
				MongoConnectionDetails connectionDetails, ObjectProvider<SslBundles> sslBundles) {
			return new StandardMongoClientSettingsBuilderCustomizer(connectionDetails.getConnectionString(),
					properties.getUuidRepresentation(), properties.getSsl(), sslBundles.getIfAvailable());
		}

	}
}
  • spring.data.mongodb.* 의 프로퍼티를 기반으로 접속 URI를 생성한다.
  • StandardMongoClientSettingsBuilderCustomizer를 통해서 MongoClientSettings에 connection 정보, uuid representation, ssl 연결 설정을 세팅한다.
  • MongoClientSettings 인스턴스를 적용한 MongoClient 빈을 생성한다.

 

org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration

MongoDataAutoConfiguration 클래스는 다음과 같다.

@AutoConfiguration(after = MongoAutoConfiguration.class)
@ConditionalOnClass({ MongoClient.class, MongoTemplate.class })
@EnableConfigurationProperties(MongoProperties.class)
@Import({ MongoDataConfiguration.class, MongoDatabaseFactoryConfiguration.class,
		MongoDatabaseFactoryDependentConfiguration.class })
public class MongoDataAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean(MongoConnectionDetails.class)
	PropertiesMongoConnectionDetails mongoConnectionDetails(MongoProperties properties) {
		return new PropertiesMongoConnectionDetails(properties);
	}
}

MongoDataAutoConfiguration에서 Import 하는 MongoDataConfiguration, MongoDatabaseFactoryConfiguration, MongoDatabaseFactoryDependentConfiguration을 통해서 다음과 같은 작업을 수행한다.

  • spring.data.mongodb.field-naming-strategy에 지정된 네이밍 클래스를 적용한다.
  • MongoClient 빈을 기반으로 SimpleMongoClientDatabaseFactory 빈을 등록한다.
  • MappingMongoConverter 빈을 등록한다.
  • MongoDatabaseFactory, MongoConverter 빈을 기반으로 MongoTemplate 빈을 등록한다.

 

MongoClientSettings 빈 직접 생성하기

auto configuration을 통해서 생성된 MongoClient 빈에 적용되는 MongoClientSettings는 디폴트 설정들이 적용된다.

AutoConfiguration에 의해서 자동 생성된 MongoClientSettings에 적용되는 설정들은 다음과 같다.

created with settings MongoClientSettings{
  readPreference=primary, 
  writeConcern=WriteConcern{w=null, wTimeout=null ms, journal=null}, 
  retryWrites=true, retryReads=true, readConcern=ReadConcern{level=null}, 
  credential=MongoCredential{mechanism=null, userName='root', source='admin', password=<hidden>, mechanismProperties=<hidden>}, 
  transportSettings=null, 
  streamFactoryFactory=null, 
  commandListeners=[], 
  codecRegistry=ProvidersCodecRegistry{codecProviders=[ValueCodecProvider{}, BsonValueCodecProvider{}, DBRefCodecProvider{}, DBObjectCodecProvider{}, DocumentCodecProvider{}, CollectionCodecProvider{}, IterableCodecProvider{}, MapCodecProvider{}, GeoJsonCodecProvider{}, GridFSFileCodecProvider{}, Jsr310CodecProvider{}, JsonObjectCodecProvider{}, BsonCodecProvider{}, EnumCodecProvider{}, com.mongodb.client.model.mql.ExpressionCodecProvider@3292d91a, com.mongodb.Jep395RecordCodecProvider@5921b93c, com.mongodb.KotlinCodecProvider@faea4da]}, 
  loggerSettings=LoggerSettings{maxDocumentLength=1000}, 
  clusterSettings={hosts=[127.0.0.1:53675], srvServiceName=mongodb, mode=SINGLE, requiredClusterType=UNKNOWN, requiredReplicaSetName='null', serverSelector='null', clusterListeners='[]', serverSelectionTimeout='30000 ms', localThreshold='15 ms'}, 
  socketSettings=SocketSettings{connectTimeoutMS=30000, readTimeoutMS=30000, receiveBufferSize=0, proxySettings=ProxySettings{host=null, port=null, username=null, password=null}}, 
  heartbeatSocketSettings=SocketSettings{connectTimeoutMS=30000, readTimeoutMS=30000, receiveBufferSize=0, proxySettings=ProxySettings{host=null, port=null, username=null, password=null}},
  connectionPoolSettings=ConnectionPoolSettings{maxSize=50, minSize=10, maxWaitTimeMS=120000, maxConnectionLifeTimeMS=60000, maxConnectionIdleTimeMS=0, maintenanceInitialDelayMS=0, maintenanceFrequencyMS=60000, connectionPoolListeners=[], maxConnecting=2}, 
  serverSettings=ServerSettings{heartbeatFrequencyMS=10000, minHeartbeatFrequencyMS=500, serverListeners='[]', serverMonitorListeners='[]'}, 
  sslSettings=SslSettings{enabled=false, invalidHostNameAllowed=false, context=null}, 
  applicationName='null', 
  compressorList=[], 
  uuidRepresentation=STANDARD, 
  serverApi=null, 
  autoEncryptionSettings=null, 
  dnsClient=null, 
  inetAddressResolver=null, 
  contextProvider=null
}

 

위 정보는 애플리케이션을 실행했을 때 org.mongodb.driver.client 에서 생성한 로그이다.

위 항목에 있는 세팅 정보를 변경하려면 MongoClientSettings 빈을 직접 생성하면 된다.

예를 들어 connection pool settings를 변경하려면 다음과 같이 MongoClientSettings 빈을 생성한다.

@Configuration
@RequiredArgsConstructor
public class MongoDBConfiguration {
    @Bean
    public MongoClientSettings mongoClientSettings() {
        return MongoClientSettings.builder()
                .applyToConnectionPoolSettings( connectionPoolSettingsBuilder ->
                        connectionPoolSettingsBuilder
                                .maxSize( 50 )
                                .minSize( 10 )
                                .maxConnectionLifeTime( 1, TimeUnit.MINUTES )
                )
                .applyToSocketSettings( socketSetting ->
                        socketSetting
                                .connectTimeout( 30, TimeUnit.SECONDS )
                                .readTimeout( 30, TimeUnit.SECONDS )
                )
                .build();
    }

    @Bean
    public StandardMongoClientSettingsBuilderCustomizer standardMongoSettingsCustomizer( MongoProperties properties,
                                                                                  MongoConnectionDetails connectionDetails, ObjectProvider<SslBundles> sslBundles) {
        return new StandardMongoClientSettingsBuilderCustomizer(connectionDetails.getConnectionString(),
                properties.getUuidRepresentation(), properties.getSsl(), sslBundles.getIfAvailable());
    }
}

StandardMongoClientSettingsBuilderCustomizer 빈의 역할은 MongoClient에 적용되는 MongoClientSettings 인스턴스에 ConnectionDetail, UuidRepresentation, SSL 설정 정보를 적용시키는 역할을 하는데 MongoAutoConfiguration 클래스에 보면 MongoClientSettings 빈을 직접 생성하지 않는 경우에만 생성되므로 직접 MongoClientSettings 빈을 생성하는 경우에는 StandardMongoClientSettingsBuilderCustomizer 빈이 생성되지 않아 ConnectionDetail, UuidRepresentation, SSL 설정 정보가 적용되지 않는다. 그러므로 StandardMongoClientSettingsBuilderCustomizer 빈을 생성해 주거나  아래 코드와 같이 mongoClientSettings() 빈을 생성하는 메서드에서 MongoClientSettings에 직접 ConnectionDetail, UuidRepresentation, SSL 설정 정보를 세팅해야 한다.

@Bean
public MongoClientSettings mongoClientSettings(
        MongoProperties mongoProperties,
        MongoConnectionDetails connectionDetails,
        ObjectProvider<SslBundles> sslBundlesProvider) {

    MongoClientSettings.Builder builder = MongoClientSettings.builder()
            .applyConnectionString( connectionDetails.getConnectionString() )
            .uuidRepresentation( mongoProperties.getUuidRepresentation() )
            .applyToConnectionPoolSettings( connectionPoolSettingsBuilder -> connectionPoolSettingsBuilder
                    .maxSize( 50 )
                    .minSize( 10 )
                    .maxConnecting( 10 )
                    .maxConnectionLifeTime( 1, TimeUnit.MINUTES )
            )
            .applyToSocketSettings( socketSetting ->
                    socketSetting
                            .connectTimeout( 30, TimeUnit.SECONDS )
                            .readTimeout( 30, TimeUnit.SECONDS )
            );

    if ( mongoProperties.getSsl().isEnabled() ) {
        builder.applyToSslSettings( sslSetting -> {
            if ( mongoProperties.getSsl().getBundle() != null ) {
                sslSetting.enabled( true );
                SslBundles sslBundles = sslBundlesProvider.getIfAvailable();
                SslBundle sslBundle = sslBundles.getBundle( mongoProperties.getSsl().getBundle() );
                Assert.state( !sslBundle.getOptions().isSpecified(), "SSL options cannot be specified with MongoDB");
                sslSetting.context( sslBundle.createSslContext() );
            }
        } );
    }

    return builder.build();
}

 

마지막으로 AbstractMongoClientConfiguration 클래스를 상속한 @Configuration 빈을 정의하는 방법이 있다.

@Configuration
@RequiredArgsConstructor
public class MongoDBConfigurationBySupport extends AbstractMongoClientConfiguration {
    private final MongoConnectionDetails mongoConnectionDetails;
    private final MongoProperties mongoProperties;
    private final ObjectProvider<SslBundles> sslBundlesObjectProvider;

    @Bean
    @Override
    @NonNull
    public MongoClient mongoClient() {
        return super.mongoClient();
    }

    @Override
    @NonNull
    public String getDatabaseName() {
        return "test";
    }

    @Override
    protected void configureClientSettings( MongoClientSettings.Builder builder ) {
        builder.applyConnectionString( mongoConnectionDetails.getConnectionString() )
                .uuidRepresentation( mongoProperties.getUuidRepresentation() )
                .applyToConnectionPoolSettings( connectionPoolSettingsBuilder ->
                        connectionPoolSettingsBuilder
                                .maxSize( 50 )
                                .minSize( 10 )
                                .maxConnectionLifeTime( 1, TimeUnit.MINUTES )
                )
                .applyToSocketSettings( socketSetting ->
                        socketSetting
                                .connectTimeout( 30, TimeUnit.SECONDS )
                                .readTimeout( 30, TimeUnit.SECONDS )
                );

        if ( mongoProperties.getSsl().isEnabled() ) {
            builder.applyToSslSettings( sslSetting -> {
                if ( mongoProperties.getSsl().getBundle() != null ) {
                    sslSetting.enabled( true );
                    SslBundles sslBundles = sslBundlesObjectProvider.getIfAvailable();
                    SslBundle sslBundle = sslBundles.getBundle( mongoProperties.getSsl().getBundle() );
                    Assert.state(!sslBundle.getOptions().isSpecified(), "SSL options cannot be specified with MongoDB");
                    sslSetting.context( sslBundle.createSslContext() );
                }
            } );
        }
    }
}

AbstractMongoClientConfiguration 클래스의 configureClientSettings() 메서드를 재정의 하여 MongoClientSettings에 상세 설정을 할 수 있다.

이 방식 역시 ConnectionDetail, UuidRepresentation, SSL 설정 정보를 직접 MongoClientSettings에 세팅해야 한다.

AbstractMongoClientConfiguration 클래스를 상속받아 처리하는 경우에는 MongoClient, MongoTemplate,  MongoDatabaseFactory 빈이 먼저 생성되므로 auto configuration 이 동작하지 않는다.

 

spring boot에서 제공하는 MongoDB auto configuration과 application.yml에 spring.data.mongodb.* 설정을 이용하여 MongoDB에 바로 연결하여 사용할 수 있다. MongoDB 연결을 위한 아무런 Configuration 없이도 그냥 사용할 수 있다. 조금 더 세밀하게 연결 설정을 하고자 하는 경우에는 MongoClientSettings 빈을 직접 생성하면서 필요한 설정만 지정해 주면 되는 정도이다.

 


샘플 코드 gitlab 링크

 

샘플 코드에서 사용된 mongodb 는 spring boot docker compose를 사용하였다.

spring boot docker compose에 대해서는 아래 포스팅을 참고

2023.10.22 - [스프링부트] - spring boot 3.1 docker compose support

댓글

💲 추천 글