NoSQL ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ก ๊ฐ์ฅ ๋ง์ด ์ฌ์ฉ๋๊ณ ์๋ MongoDB๋ฅผ Spring 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'
...
}Groovyspring 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;
}
}
...
}Java@ConfigurationProperties ์ด๋ ธํ ์ด์ ์ prefix๋ฅผ ํตํด์ MongoDB ์ค์ ์ ๋ค์๊ณผ ๊ฐ์ด ์์ํจ์ ์ ์ ์๋ค.
spring.data.mongodb.*Javaapplication.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 ์ ์ ์ฌ์ฉ ์ฌ๋ถ>YAMLuuid-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;
}
}JavaSnakeCaseFieldNamingStrategy๋ ๋ค์๊ณผ ๊ฐ์ด ์ ์ฅ๋๋ค.
_id: UUID('xxxxxx')
person_name: '์ด๋ฆ'
person_age: ๋์ด
_class: "xxxx"JavaCamelCaseAbbreviatingFieldNamingStrategy๋ ๋ค์๊ณผ ๊ฐ์ด ์ ์ฅ๋๋ค.
_id: UUID('xxxxxx')
pn: '์ด๋ฆ'
pa: ๋์ด
_class: "xxxx"Javaspring boot MongoDB auto configuration
org.springframework.boot:spring-boot-autoconfigure ๋ชจ๋์๋ MongoDB์ ๊ด๋ จ๋ ๋ค์๊ณผ ๊ฐ์ auto configuration ์ด ์ ๊ณต๋๋ค.
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());
}
}
}
Java- spring.data.mongodb.* ์ ํ๋กํผํฐ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ ์ URI๋ฅผ ์์ฑํ๋ค.
- StandardMongoClientSettingsBuilderCustomizer๋ฅผ ํตํด์ MongoClientSettings์ connection ์ ๋ณด, uuid representation, ssl ์ฐ๊ฒฐ ์ค์ ์ ์ธํ ํ๋ค.
- MongoClientSettings ์ธ์คํด์ค๋ฅผ ์ ์ฉํ MongoClient ๋น์ ์์ฑํ๋ค.
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);
}
}JavaMongoDataAutoConfiguration์์ 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
}
Plaintext์ ์ ๋ณด๋ ์ ํ๋ฆฌ์ผ์ด์
์ ์คํํ์ ๋ 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());
}
}
JavaStandardMongoClientSettingsBuilderCustomizer ๋น์ ์ญํ ์ 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();
}
Java๋ง์ง๋ง์ผ๋ก 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() );
}
} );
}
}
}
JavaAbstractMongoClientConfiguration ํด๋์ค์ 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 ๋น์ ์ง์ ์์ฑํ๋ฉด์ ํ์ํ ์ค์ ๋ง ์ง์ ํด ์ฃผ๋ฉด ๋๋ ์ ๋์ด๋ค.
์ํ ์ฝ๋์์ ์ฌ์ฉ๋ mongodb ๋ spring boot docker compose๋ฅผ ์ฌ์ฉํ์๋ค.
spring boot docker compose์ ๋ํด์๋ ์๋ ํฌ์คํ ์ ์ฐธ๊ณ
