Develop Study/Spring
(25.02.18) Spring Boot 의 MongoDB 연결
모 스타트업 회사에 인턴쉽에 참여하게 되면서 엔터티와 테이블기반의 RDMBS를 활용하는 MySQL을 사용했지만,
AI 학습용 데이터 및 대용량 데이터를 다루는 NoSQL기반의 DB를 활용을 해야했기 때문에,
MySQL기반과 다르게 Spring Boot 와 NoSQL인 MongoDB를 활용하는 방법을 학습하면서 기록했다.
MongoDB
- NoSQL 기반의 데이터베이스 관계형 데이터베이스(RDBMS)와는 다른 구조로 데이터를 저장
- 문서 지향(Document-Oriented) 형식
- 데이터를 문서 단위로 저장
- BSON(Binary JSON) 형식으로 저장
- MongoDB에서 지원하는 JSON (JavaScript Object Notation)을 기반 이진 형식(Binary format)으로 저장되는 형식
- JSON 과 똑같이 생겼지만, 필드와 필드 값을 key-value 형태로 저장하는 방식 : JSON 째로 저장할 경우 파일자체가 너무 커지기 때문에 Binary Data 형태로 저장
MongoDB의 주요 특징
- 스키마 유용성
- 데이터를 삽입할 때 특정 컬럼이나 테이블을 맞추거나 지정할 필요 없이 MongoDB는 스키마가 유연하게 활용할 수 있음
- ex) 사용자와 상품 데이터를 하나의 컬렉션(Collection)에 저장해도 문제 X : 사실상 다른 Table로 설정을 해야하지만 그럴 필요 없음
- 각가의 데이터는 기본적으로 ObjectId 사용 하여 저장
- MongoDB는 각 문서에 대해 ObjectId를 자동으로 생성하여 _id 필드에 저장
- 12바이트 길이의 이진 데이터로 구성되며, 이를 24자리의 16진수 문자열로 표현
- 데이터를 삽입할 때 특정 컬럼이나 테이블을 맞추거나 지정할 필요 없이 MongoDB는 스키마가 유연하게 활용할 수 있음
// 사용자 데이터 (User)
{
"_id": "user1",
"name": "John",
"email": "john@example.com",
"age": 30
}
// 상품 데이터 (Product)
{
"_id": "product1",
"name": "Laptop",
"price": 1200,
"brand": "BrandX"
}
- 확장성(Scalability): 분산형 Architecture
- 수평 확장을 지원하여 데이터를 여러 서버에 분산 저장가능 → 성능 향상, 문서 중심이기 때문에
- 자동으로 Sharding을 지원해서 데이터가 늘어나면 자동으로 분산 가능
- 빠른 읽기/쓰기가 가능하며, 대용량 데이터 처리에 적합합니다.
- ex) 쇼핑몰에서 상품 데이터를 빠르게 조회하거나 업데이트
- 자동으로 Sharding을 지원해서 데이터가 늘어나면 자동으로 분산 가능
- 수평 확장을 지원하여 데이터를 여러 서버에 분산 저장가능 → 성능 향상, 문서 중심이기 때문에
- 다양한 인덱스 옵션:
- 인덱스(쿼리)를 사용해 검색 성능을 최적화
- 특정 필드나 조건을 기준으로 문서를 검색을 가능
- 단일 인덱스
- find: 특정 조건에 맞는 데이터를 빠르게 검색할 수 있습니다.
- sort: 데이터를 효율적으로 정렬할 수 있습니다.
- filter: 특정 조건을 기준으로 데이터를 빠르게 필터링할 수 있습니다.
- 복합 인덱스
- 단일 인덱스 결합
- 인덱스(쿼리)를 사용해 검색 성능을 최적화
// **name**은 오름차순(1), **age**는 내림차순(-1)으로 인덱스를 생성
db.users.createIndex({ "name": 1, "age": -1 });
- 복잡한 Query 대응 가능
- Aggregation Framework를 통해 복잡한 데이터 집계 및 필터링 작업 가능
Aggregation Framework
- SQL의 GROUP BY, HAVING, JOIN 등과 유사한 기능을 MongoDB 에서 활용할 수 있도록 하게 하는 복잡한 데이터 처리 및 변환을 할 수 있게 하는 일종의 Framework
- Aggregation Pipeline 기반
- 여러 개의 Stage로 구성되며, 각각 입력 데이터를 변형하거나 필터링 → 파이프라인을 통해 데이터를 단계적으로 처리
- Stage
- 데이터를 변형하는 작업 : 문서(document)를 받아서 처리한 후, 결과를 다음 단계로 순차적으로 전달
- $match: 조건에 맞는 데이터를 필터 (SQL의 WHERE와 유사)
- $group: 데이터를 그룹화하여 집계 (SQL의 GROUP BY와 유사)
- $sort: 데이터를 정렬
- $project: 필요한 필드를 선택하거나 새로운 필드를 계산
- $limit: 결과의 개수를 제한
- $skip: 특정 개수만큼 데이터를 건너뛰기
- $unwind: 배열을 분해하여 각 요소를 개별 문서로 변환
- $lookup: 다른 컬렉션과 조인(join)하는 데 사용
- 데이터를 변형하는 작업 : 문서(document)를 받아서 처리한 후, 결과를 다음 단계로 순차적으로 전달
- example: 특정 카테고리 내 상품을 가격대별로 묶어 정렬하는 작업
// 예시 문서: products 컬렉션
{
"_id": "product1",
"name": "Laptop",
"price": 1200,
"category": "Electronics"
},
{
"_id": "product2",
"name": "Phone",
"price": 800,
"category": "Electronics"
},
{
"_id": "product3",
"name": "Shoes",
"price": 150,
"category": "Fashion"
}
import org.springframework.data.mongodb.core.Aggregation;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.stereotype.Service;
import java.util.List;
// Spring Application 기준 Aggregation Framework 를 활용한 작업
@Service
public class ProductService {
private final MongoTemplate mongoTemplate;
public ProductService(MongoTemplate mongoTemplate) {
this.mongoTemplate = mongoTemplate;
}
public List<Object> getPriceBuckets() {
// Aggregation Pipeline 생성
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(Criteria.where("category").is("Electronics")), // 카테고리 필터링
Aggregation.bucket("price", // 가격대별로 묶기
Aggregation.BucketBoundaries.builder().bucket(0, 500).bucket(500, 1000).bucket(1000, 1500).build(),
Aggregation.BucketBuilder::count
),
Aggregation.sort(Aggregation.sort(Direction.ASC, "_id")) // 가격대별로 정렬
);
// Aggregation 실행
return mongoTemplate.aggregate(aggregation, Product.class, Object.class).getMappedResults();
}
}
@Aggregation 을 사용하는 예제 (가장 많이 사용)
// 예시 데이터
[
{
"email": "alice@example.com",
"name": "Alice",
"purchases": [
{ "productId": "67b2cb63e6a9334bbaeb569e", "quantity": 3 },
{ "productId": "67b2cb63e6a9334bbaeb56a0", "quantity": 4 }
]
},
{
"email": "bob@example.com",
"name": "Bob",
"purchases": [
{ "productId": "67b2cb63e6a9334bbaeb569e", "quantity": 10 },
{ "productId": "67b2cb63e6a9334bbaeb56a3", "quantity": 2 }
]
},
{
"email": "charlie@example.com",
"name": "Charlie",
"purchases": [
{ "productId": "67b2cb63e6a9334bbaeb569e", "quantity": 5 }
]
}
]
public interface UserRepository extends MongoRepository<User, String> {
@Aggregation(pipeline = {
"{ '$match': { 'purchases': { '$not': { '$elemMatch': { 'quantity': { '$gte': ?0 } } } } } }"
})
List<User> findUserWithPurchasesUnderMax(int maxQuantity);
}
- @Query 처럼 사용을 하면서 SQL대신 Stage를 활용해서 사용할 수 있음
{
"$match": {
"purchases": {
"$not": {
"$elemMatch": {
"quantity": { "$gte": ?0 }
}
}
}
}
}
- Stage
- $elemMath : 배열 내부 요소 중 조건을 만족하는 항목 검사
- $not : $elemMatch 조건을 만족하는 문서를 제외
- $gte : ?0 : 특정 값보다 미만 항목이 있는지 확인
- $gt 특정 값 미만
- 동적으로 변수를 받아오는 부분에 대해서는 SpEL을 사용 → 위에서는 ?0 : 0번 인덱스 파라미터
- SpEL
- Spring Expression Language (SpEL)은 Spring에서 제공하는 표현 언어로, 문자열, 변수, 메서드 호출, 컬렉션 조작, 조건문 등 다양한 표현식을 평가하고 실행할 수 있는 기능
- SpEL
Spring Boot 에서의 MongoDB
- Gradle 의존성 추가
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'
}
단, 'org.springframework.boot:spring-boot-starter-data-jpa' 과 중첩이 되어서 의존성 주입이 되어있으면 충돌이 나버림
2. application.yml 설정
spring:
data:
mongodb:
uri: mongodb://localhost:27017/{DB이름}
spring:
data:
mongodb:
host: localhost
port: 27017
database: {데이터베이스 이름}
username: {이름}
password: {비밀번호}
authentication-database: admin
- 비밀번호가 있는경우 설정이 가능
3. MongoDB Entity
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
@Document(collection = "users")
public class User {
@Id
private String id;
private String name;
private int age;
}
- @Document: 이 클래스가 특정 MongoDB의 컬렉션(위에서는 users, MySQL 에서의 table = “users”)에
- @Id: MongoDB에서 이 필드가 문서의 고유 식별자
- 각각의 엔터티 역할을 하는 Documnet는 관계가 없기 때문에 @OneToMany 등 관계를 정의를 할 필요가 없음
4. MongoRepository
import org.springframework.data.mongodb.repository.MongoRepository;
public interface UserRepository extends MongoRepository<User, String> {
User findByName(String name);
}
- MongoDB와 데이터를 연결하려면 Spring Data MongoDB에서 제공하는 MongoRepository를 구현하는 interface로 구성 가능
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public User createUser(User user) {
return userRepository.save(user);
}
public User getUserByName(String name) {
return userRepository.findByName(name);
}
}
- 위와 같이 똑같이 비지니스 로직에서도 똑같이 사용이 가능
그 외에는 Spring Boot에서 DB응 활용하는 디자인패턴은 일정하기 때문에
똑같이 사용할 수 있을 것이다.
'Develop Study > Spring' 카테고리의 다른 글
(25.04.03) Singleton Pattern & Spring Bean (0) | 2025.04.03 |
---|---|
(25.03.13) Java Spring JPA @Converter & AttributeConverter (0) | 2025.03.13 |
(24.10.14) Swagger & Spring REST Doc (2) | 2024.10.14 |
(24.08.07)[17주차] Spring Batch와 스케쥴링 - 활용 및 정리 (0) | 2024.08.07 |
(24.08.06)[17주차] Spring Batch와 스케쥴링 (0) | 2024.08.06 |