[Spring Boot] MongoDB를 이용한 간단한 API 제작

2020. 12. 21. 02:52Spring/Spring Boot

1. MongoDB

MongoDB 서버가 설치되어 있다는 전제하에 진행된다.


[참고] Database - MongoDB - MongoDB 설치

 

 

 

2. MongoDB 설정

1) build.gradle

mongoDB 의존성을 추가해준다.

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

2) application.yml

spring:
  data:
    mongodb:
      host: [호스트 IP]
      port: 27017
      authentication-database: admin
      username: root
      password: [비밀번호]
      database: tutorial
      # uri: mongodb://localhost:27017/tutorial

3) 간단한 API 제작

API 제작 방법은 기존의 방식과 동일하다. 단, MongoDB에 접근하여 기능을 수행할 API를 사용하는 방법은 2가지가 존재한다.

  • MongoRepository
  • MongoTemplate

 

※ MongoTemplate는 MongoRepository 보다  실행할 질의에 대해 더 세부적인 제어가 가능하지만, 편리성을 생각하여 MongoRepository 기반으로 제작했다.

package org.example.domain.posts;

import org.springframework.data.mongodb.repository.MongoRepository;

public interface PostsRepository extends MongoRepository<Posts, String> {

}

기존 RDBMS를 이용했을 때에는 Entity라는 개념으로 접근했었지만, MongoDB는 Document 개념으로 접근한다.

package org.example.domain.posts;

import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

@Getter
@NoArgsConstructor
@Document(collection = "posts")
public class Posts {

    @Id
    private String _id;
    private String title;
    private String content;
    private String author;

    @Builder
    public Posts(String title, String content, String author) {
        this.title = title;
        this.content = content;
        this.author = author;
    }

    public void update(String title, String content) {
        this.title = title;
        this.content = content;
    }

    /*
    @Builder
    - 생성자 상단에 선언 시 생성자에 포함도니 필드만 빌더에 포함된다.
    - 데이터베이스에 데이터를 넣기 위한 Setter 메서드를 대체한다.
     */
}

4) '_id' 자동 증가

'_id'는 기본적으로 ObjectId()로 생성이 된다. 기존의 RDBMS의 AUTO INCREMENT처럼 '_id'를 사용하기 위한 방법은 다음과 같다.

 

우선 AUTO INCREMENT 연속성 정보를 저장하는 컬렉션을 만들어준다.

package org.example.domain.posts;

import lombok.Getter;
import lombok.Setter;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

@Getter
@Setter
@Document(collection = "database_sequences")
public class DatabaseSequence {

    @Id
    private String id;
    private Long seq;
}

 

자동 증가 시퀀스에 대한 참조를 하는 SEQUENCE_NAME을 static 필드로 선언하고 @Transient 어노테이션을 이용하여 영속 필드에서 제외시켜준다.

package org.example.domain.posts;

import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.mongodb.core.mapping.Document;

@Getter
@NoArgsConstructor
@Document(collection = "posts")
public class Posts {

    @Transient
    public static final String SEQUENCE_NAME = "posts_sequence";

    @Id
    private Long id;
    private String title;
    private String content;
    private String author;

    public void setId(Long id) {
        this.id = id;
    }

    @Builder
    public Posts(String title, String content, String author) {
        this.title = title;
        this.content = content;
        this.author = author;
    }

    public void update(String title, String content) {
        this.title = title;
        this.content = content;
    }

    /*
    @Builder
    - 생성자 상단에 선언 시 생성자에 포함도니 필드만 빌더에 포함된다.
    - 데이터베이스에 데이터를 넣기 위한 Setter 메서드를 대체한다.
     */
}

이 후에는 자동으로 증가시켜줄 수 있는 클래스를 선언해준다.

package org.example.service.SequenceGenerator;

import lombok.RequiredArgsConstructor;
import org.example.domain.posts.DatabaseSequence;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;

import java.util.Objects;

import static org.springframework.data.mongodb.core.FindAndModifyOptions.options;
import static org.springframework.data.mongodb.core.query.Criteria.where;
import static org.springframework.data.mongodb.core.query.Query.query;

@RequiredArgsConstructor
@Service
public class SequenceGeneratorService {

    private final MongoOperations mongoOperations;

    public long generateSequence(String seqName) {

        DatabaseSequence counter = mongoOperations.findAndModify(query(where("_id").is(seqName)),
                new Update().inc("seq",1), options().returnNew(true).upsert(true),
                DatabaseSequence.class);
        return !Objects.isNull(counter) ? counter.getSeq() : 1;

    }
}

그리고 새로운 Posts가 추가될 때마다 자동으로 id값이 올라가도록 Listener 클래스를 작성해준다.

package org.example.events;

import lombok.RequiredArgsConstructor;
import org.example.domain.posts.Posts;
import org.example.service.SequenceGenerator.SequenceGeneratorService;
import org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener;
import org.springframework.data.mongodb.core.mapping.event.BeforeConvertEvent;
import org.springframework.stereotype.Component;

@RequiredArgsConstructor
@Component
public class PostsModelListener extends AbstractMongoEventListener<Posts> {

    private final SequenceGeneratorService sequenceGenerator;

    @Override
    public void onBeforeConvert(BeforeConvertEvent<Posts> event) {
          event.getSource().setId(sequenceGenerator.generateSequence(Posts.SEQUENCE_NAME));
    }
}

5) 구현 화면

구현 화면


[참고] github.com/ozofweird/SpringBoot_MongoDBTest

[참고] www.baeldung.com/spring-boot-mongodb-auto-generated-field

[참고] velog.io/@hanblueblue/MongoDB-MongoTemplate-vs.-MongoRepository

[참고] gofnrk.tistory.com/38

[참고] truth1018.tistory.com/11

728x90