본문 바로가기
DataBase

Spring boot with MongoDB - Spring Data MongoDB를 사용해보자.

by khds 2024. 3. 7.

 

들어가기

 

Springboot로 채팅방을 구현하는 프로젝트를 진행하려고 한다. 채팅방은 실시간으로 채팅 내용을 저장할 수 있게 데이터베이스에 저장하도록 생각하였다. 하지만 채팅내용이 보내질 때마다 데이터베이스에 저장을 하는 비용이 발생한다. 이를 위한 해결 방법으로는 메모리에 채팅 내용을 저장해두고 스케줄링하여 일정 주기마다 데이터베이스에 Bulk Insert 하는 방식, RDB보다 데이터 접근 비용이 적은 NoSQL을 사용하는 방식 등이 있다.

나는 두번째 방법을 선택하여 NoSQL 중 MongoDB를 사용하기로 하였다.

그러기 위해선 MongoDB에 대한 사용방법을 알아야 한다.

이 글은 MongoDB에 대한 내용과 Spring boot 프로젝트에서 Spring Data MongoDB를 통해 데이터 저장, 조회하는 방법에 대해 공부한 내용을 작성한 것이다.

 

 

목차

1. MongoDB란?

2. Atlas를 통해 원격 MongoDB 저장소 생성, MongoDB Compass를 통한 편의성 확보

3. Spring Data MongoDB를 사용해 보자.

4. 패키지 정보(_class 필드)가 데이터에 같이 저장되는 문제 및 해결 

 

 

본론

1. MongoDB란?

 

MongoDB는 NoSQL 데이터베이스로 분류되며 의미 그대로 SQL을 즉, 테이블 기반 관계형 데이터베이스 구조를 사용하지 않는 데이터베이스이다. 

NoSQL의 특징으로는 유연한 스키마를 제공하고 응답 속도가 빠르며, 배우기 쉽고 간편하게 개발할 수 있으며, 확장성 높은 오픈 소스 문서 지향 데이터베이스다.

관계형 데이터베이스는 스케일 업(서버의 크기 확장)보다 스케일 아웃(서버의 개수 확장)을 하는 것이 더욱 어렵지만, NoSQL에서는 스케일 아웃 또한 쉽게 가능하다. 이처럼 관계형 데이터베이스로 하기 어려웠던 작업들을 쉽게 해결할 수 있도록 설계된 DB라고도 할 수 있다. 그렇다고 대체제로서 사용되기보다는 관계형 데이터베이스와 함께 사용되는 경우가 많다.

데이터를 보관하는 방식은 테이블 형태 대신 key-value 방식, document 방식, 그래프 방식 등이 있고 MongoDB는 document 방식을 사용한다. Json 형태의 배열 방식으로 document를 구성하여 내용을 저장하기에 가시성이 향상된다. 

 

그렇다면 전체적인 구조가 RDBMS와 어떻게 다른가?

아래의 그림을 봐보자. 

 

위 사진에서 알 수 있듯이 RDBMS에서의 Table, Row, Column이 각각 Collection, Document, Field에 해당한다.

 

이외에도 MongoDB에는 RDBMS와 다르게 장점이 될 수 있는 특징들을 가지고 있다.

 

첫 번째로 서버 스케일링이다. 위에서 언급했다시피 NoSQL은 스케일업과 스케일아웃이 RDBS보다 간편하다. 서버 분산에 대한 솔루션을 자체적으로 지원하여, 서버 확장 시 애플리케이션을 변경하지 않고 MongoDB가 알아서 스케일링하니 애플리케이션 단에서는 크게 신경 쓸 것이 없다.

 

두 번째는 MongoDB는 컬렉션 단에서 동적 스키마를 가지고 있어 스키마 생성이 자유롭다는 것이다. 
RDBMS 같은 경우 테이블을 생성하고 값들을 추가하지만, MongoDB에서는 Document를 삽입하면 자동으로 컬렉션이 생성된다. 스키마를 생성 없이 Document를 먼저 삽입할 수 있고 , 어떤 데이터 타입이든 상관없다. 같은 컬렉션 내 Document라도 필드가 달라도 된다..!

또한 Join이 없어 하나의 Document에 다 담겨있어 응답속도 빠르다.

물론 이는 단점이 될 수도 있는 것이, 스키마가 자유로운 만큼 데이터 중복이 발생하기 쉬어 주의를 기울여야 한다. MongoDB에서는 Document의 최대 크기는 16MB로 제한을 두었는데, 그 이유는 자유로운 스키마로 인해 너무 무분별한 사용을 제한한 것이다.

 

세 번째는 Mongodb는 화면에 표현할 때는 Json형식이지만, 저장할 때는 BSON(Binary Json) 형식으로 저장하여 더 빠르고 효율적으로 데이터를 저장할 수 있다. 이는 다양한 타입으로도 제공된다. 

 

네 번째로 모든 Document에는 _id 필드가 있다. RDBMS에 Primary Key라고 생각하면 되고, 따로 지정을 하지 않는다면 ObjectId 타입의 고유한 값을 저장한다.

 

이렇듯 MongoDB는 높은 성능과 확장성을 제공할 수 있지만, 형식이 엄격하지 않아 문제가 발생할 가능성이 높고, 관계형 데이터베이스의 ACID 트랜잭션 처리만큼 엄격한 트랜잭션 처리를 보장하지 않는다.

 

 

2. Atlas를 통해 원격 MongoDB 저장소 생성, MongoDB Compass를 통한 편의성 확보

 

우선 데이터베이스가 생성될 'Organization'을 생성해야 하는데, 이는 https://naman-develop.tistory.com/172를 참고하길 바란다. 정리가 잘되어 있어 실습하는데 어려움은 없을 것이다.

Organization을 생성 후 아래의 'Browse collections'를 클릭하면 저장소를 쉽게 확인할 수 있다. 

 

 

그리고 아래와 같이 데이터베이스를 생성하고 컬렉션과 다큐먼트를 쉽게 추가할 수 있다.

 

하지만 브라우저 상에서 확인하기에는 불편함이 있을 수 있다.

여기서 추천하는 것은 MongoDB Compass를 설치하여 확인하는 것이다. 브라우저가 아닌 응용 프로그램으로 확인하여 가독성이 향상되는 장점이 있다. 

우선 아래와 같이 'Connect'을 클릭하자.

 

아래와 같이 여러 연결 방식이 있는데, Compass를 클릭하고 다운로드를 한다.(참고로 이후 Spring boot와 연동하기 위해서 Drivers를 통해 설정정보를 확인할 것이다.)

 

설치를 한 후 실행하면 URI를 입력해야 하는데, 아래의 URI를 복사하여 입력해 준다. 

horoscope는 필자의 UserId로 사용자마다 설정한 정보로 입력이 되어있을 것이다. <password>는 생성한 계정의 패스워드를 입력하면 된다.

 

URI를 입력 후 들어가면 브라우저를 통해 봤던 화면이 응용 프로그램에서 확인할 수 있게 된다.

 

 

3. Spring Data MongoDB를 사용해 보자.

 

이제 Spring boot에서 MongoDB를 연동해 보자.

먼저 의존성을 추가해 준다.

implementation ('org.springframework.boot:spring-boot-starter-data-mongodb')

 

그다음에는 위에서 생성해 둔 MongoDB 데이터베이스와 연동할 것이다.

Atlas 창에서 Connent 버튼을 눌러 Driver를 클릭해 주자.

 

이후 Driver를 Java로 변경한 후 아래의 코드를 복사한다. 주의할 것은 아래의 코드는 user, password 뿐만 아니라 uri 자체로 사용자마다 다르게 만들어지므로 다른 사람의 uri에 user, password만 다르게 설정한다면 MongoDB에 연결이 되지 않을 것이다. 

 

복사한 코드를 application.properties 혹은 yml파일에 데이터베이스 이름만 추가하여 붙여 넣기 한다. 

mongodb.net/<database> 부분으로 위에서 복사한 코드엔 database가 제외된 코드이며 데이터베이스를 생성 후 추가해 줘야 원활하게 연결이 된다.

spring.data.mongodb.uri=mongodb+srv://<user>:<password>@cluster0.okuu2rz.mongodb.net/<database>?retryWrites=true&w=majority&appName=Cluster0

 

이렇게 MongoDB와 연동을 했으니 본격적으로 데이터를 저장하고 조회해 보자. 

Spring boot에서 MongoDB에 접근하는 방법은 두 가지 있다.

MongoTemplate를 사용하는 방식과 Spring Data MongoDB를 사용하는 방식이다.

MongoTemplate은 더욱 복잡한 접근이 가능하고, Spring Data MongoDB는 Spring Data JPA와 유사하게 동작하여 JPA에 익숙한 개발자라면 쉽게 사용이 가능하다. 코드 자체도 간결하고 말이다. 

필자는 채팅 내용을 단순히 저장, 조회하는 기능만을 사용할 것이기에 Spring Data MongoDB를 사용하였다.

 

참고사항으로 Sprnig boot는 MongoDB 레퍼지토리를 자동으로 구성해 주지만, Spring 프레임워크를  사용한다면 @EnableMongoRepositories를 활용하여야 한다. 또는 Spring boot에서도 복잡한 설정을 위해서는 위 어노테이션이 사용된다고 한다. 이 글에서는 간단한 기능만을 구현할 것이기에 굳이 사용하지는 않겠다.

 

우선 데이터를 담을 객체를 생성해 보자. 아래의 코드를 보자.

import jakarta.persistence.Id;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.bson.types.ObjectId;
import org.springframework.data.mongodb.core.mapping.Document;

@Document(collection = "chatting_content") // 실제 몽고 DB 컬렉션 이름
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class ChattingContent {
    @Id
    private String id; // 또는 ObjectId
    private String name;
    private Long age;

    public ChattingContent(String name, Long age) {
        this.name = name;
        this.age = age;
    }
}

 

JPA 엔티티 생성과 유사하다. @Entity 대신 @Document를 사용하여  컬렉션으로 지정해 준다.

그리고 데이터 저장 시 id를 넣지 않고 다른 필드들만으로 데이터베이스에 저장하여 id를 자동 생성하기 위해서 @Id를 달고 String 혹은 ObjectId 타입으로 id를 추가한다.

 

이제 데이터베이스에 접근할 Repository를 작성해야 한다. JPA와 상당히 유사하며 아래와 같이 작성하였다. 

public interface TestRepository extends MongoRepository<ChattingContent, String> {
    ChattingContent findChattingContentByName(String name);
}

 

Repository를 기반으로 데이터를 저장하고 조회해 보자.

우선 데이터의 저장이다. 아래와 같이 작성하였다. 

@RestController
public class TestController {

    @Autowired
    private TestRepository testRepository;

    @GetMapping("/save")
    public ChattingContent test(@RequestParam("name") String name, @RequestParam("age") Long age) {
        ChattingContent content = new ChattingContent(name,age);

        return testRepository.save(content);
    }
}

 

위와 같이 코드를 작성한 후 실행하면 아래와 같이 데이터가 들어간 것을 확인할 수 있다.

 

여기서 문제가 하나 있다. _class라는 필드는 원하지 않았는데 자동으로 패키지 정보와 함께 들어가 있다. 이에 대한 해결책은 아래를 확인하길 바란다.

 

이제 데이터를 조회해 보자. 아래와 같이 코드를 작성하였다.

    @GetMapping("/find")
    public ChattingContent test(@RequestParam("name") String name) {
        ChattingContent content = testRepository.findChattingContentByName(name);

        return content;
    }

 

위와 같이 코드를 작성한 후 실행하면 아래와 같이 데이터가 조회되는 것을 확인할 수 있다.

 

4. 패키지 정보(_class 필드)가 데이터에 같이 저장되는 문제 및 해결 

 

위에서 간단하게 데이터를 저장하고 조회해 보았다. 하지만 데이터를 저장하는 과정에서 문제가 있었다. 저장된 데이터에 _class라는 값으로 패키지 정보가 자동으로 추가된 것이다. 만약 서로 다른 애플리케이션이 동일한 데이터를 저장, 조회할 경우 서로 다른 패키지 정보로 인하여 데이터를 공유하지 못하는 문제가 생긴다. 

 

이는 단순히 uri만 설정하였을 뿐, 추가적인 설정을 전혀 해주지 않아 Spring boot에서 자동으로 설정한 기능 중 하나이다. 이를 해결하기 위해서 아래와 같이 Config 파일을 작성하면 된다.

@Configuration
public class MongodbConfig {

    @Bean
    public MappingMongoConverter mappingMongoConverter(
            MongoDatabaseFactory mongoDatabaseFactory,
            MongoMappingContext mongoMappingContext
    ) {
        DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDatabaseFactory);
        MappingMongoConverter converter = new MappingMongoConverter(dbRefResolver, mongoMappingContext);
        converter.setTypeMapper(new DefaultMongoTypeMapper(null));  
        return converter;
    }
}

 

이후 다시 저장을 시도해 보면 아래와 같이 _class가 추가되지 않은 것을 확인할 수 있다. 

 

 

결론

이렇게 MongoDB에 연동하고 간단하게 데이터 저장, 조회하는 과정을 학습해 보았다. 

위에서 언급했다시피 일반적으로 RDBMS를 주로  사용하고  NoSQL은 특별한 경우에 사용한다고 한다. 그렇기에 RDBMS뿐만 아니라 NoSQL에 대해서도 어느 정도 알아두어야겠다고 생각한다.

이번엔 기초적인 학습만을 하였지만, 추후엔 더욱 정교한 학습과 실습을 진행해 보겠다.

궁금한 사항이나 잘못된 내용은 댓글 달아주시면 감사하겠습니다..!

 

 

참고

https://velog.io/@joonghyun/Springboot-MongoDB-Springboot%EC%99%80-MongoDB-%EC%97%B0%EA%B2%B0%ED%95%98%EA%B8%B0

 

[Springboot / MongoDB] Springboot와 MongoDB 연결하기

저는 평소에 데이터베이스로는 관계형 데이터베이스인 MySQL을 사용했습니다.하지만, 크롤링한 데이터를 저장해야 하는 상황이 발생했기 때문에 NoSQL인 MongoDB를 사용하기로 했습니다.구글링을

velog.io

https://naman-develop.tistory.com/172

 

[웹 크롤링 프로젝트] 01. MongoDB 사용법

🙆‍♂️ Mongo DB 기존 데이터 베이스는 정형 데이터만 저장할 수 있지만 Mongo DB는 No-SQL이라고 해서 데이터베이스 형식으로 데이터를 저장하는 것이 아닌 딕셔너리 형태로 데이터를 저장하는 DB

naman-develop.tistory.com

https://sig03.medium.com/spring-boot-mongodb-%EC%97%B0%EB%8F%99-%EC%8B%9C-class-%EC%A0%80%EC%9E%A5%EB%90%98%EB%8A%94-%EB%AC%B8%EC%A0%9C-mongodbconfig-java-%ED%8C%8C%EC%9D%BC-%EC%9C%84%EC%B9%98-e7657a4858ba

 

[Spring Boot] Mongodb 연동 시 _class 저장되는 문제: MongoDBConfig.java 파일 위치

Spring Boot + Mongodb 를 연동한 RestApi 서버를 구현해 보고 있다.

sig03.medium.com

https://www.whatap.io/ko/blog/173/?gad_source=1&gclid=Cj0KCQjwir2xBhC_ARIsAMTXk84oAB31t32Fm6eXwTZxvKVk6xHmzJ8RJTtncnJiDT-6NN4p39huDuUaAl6uEALw_wcB

 

RDBMS와 NoSQL의 차이

RDBMS와 NoSQL database는 어떻게 다르고, 어떻게 써야 할까요?

www.whatap.io

https://somoly.tistory.com/133

 

MongoDB _class 필드 제거하기

MongoDB 사용시, SpringBoot 자동설정을 이용할 경우 아래와 같이 _class 에 Entity Class 의 package 정보가 함께 저장됩니다. 이 부분을 제거하기 위해서는 DefaultMongoTypeMapper 설정을 변경하면 됩니다. { "_id" :

somoly.tistory.com

https://colevelup.tistory.com/45

 

[MongoDB] MongoDB란? 특징과 장단점을 예제로 확인해보자.

몽고디비는 대용량 데이터를 처리할 때 우수한 성능을 발휘하는 NoSQL 데이터베이스입니다. 그러나 트랜잭션 처리의 엄격성이 상대적으로 떨어지며, 네이티브 조인을 지원하지 않는 등 모든 용

colevelup.tistory.com

https://velog.io/@shinabeuro/MongoDB-%EC%8A%A4%ED%82%A4%EB%A7%88-%EB%94%94%EC%9E%90%EC%9D%B8-concise-guide

 

MongoDB 스키마 디자인 (concise guide)

MongoDB 데이터베이스를 사용할때엔티티 간의 관계에 따라서 어떤식으로 스키마를 구성하면 좋을지 직관적으로 따르기 좋은 가이드를 정리해본다.MongoDB 스키마 디자인에서 첫번째 가이드는 다음

velog.io