with-RL
프로그램 / Java2024년 1월 20일

Spring Boot Micro Service Architecture 만들기 (3)

이 포스트는 이전 포스트에서 생성한 Spring Boot MSA 프로젝트를 기반으로 MongoDB와 연동해서 간단한 CRUD를 해 보는 과정에 대한 설명입니다.

이 포스트는 다음 과정을 완료한 후에 참고하시길 바랍니다.

1. MongoDB 설치 (docker)

services:
  mongodb:
    image: mongo:latest
    container_name: mongodb-mymsa
    ports:
      - 27017:27017
$ docker-compose -f docker.yml up

2. MongoDB 기능 구현

  data:
    mongodb:
      uri: mongodb://localhost:27017/mymsa
      auto-index-creation: true
package com.mymsa.web.model;

import lombok.*;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.FieldType;
import org.springframework.data.mongodb.core.mapping.MongoId;

import java.util.Date;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Document
@ToString
public class BlogPost {

    @MongoId(value = FieldType.OBJECT_ID)
    private String post_id;

    private String title;

    private String context;

    private Date create_time;

}
package com.mymsa.web.repository;

import com.mymsa.core.context.MSAContext;
import com.mymsa.web.model.BlogPost;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.Date;

@Repository
public class BlogPostRepository {

    @Autowired
    private ReactiveMongoTemplate mongoClient;

    public Mono<BlogPost> create(MSAContext context, BlogPost blogPost) {
        blogPost.setCreate_time(new Date());
        return mongoClient.save(blogPost);
    }

    public Flux<BlogPost> findAll(MSAContext context) {
        return mongoClient.findAll(BlogPost.class);
    }

    public Mono<BlogPost> findById(MSAContext context, String post_id) {
        Query query = Query.query(Criteria.where("post_id").is(post_id));
        return mongoClient.findOne(query, BlogPost.class);
    }

    public Mono<BlogPost> update(MSAContext context, BlogPost blogPost) {
        Query query = Query.query(Criteria.where("post_id").is(blogPost.getPost_id()));
        Update update = Update.update("title", blogPost.getTitle())
                .set("context", blogPost.getContext());

        return mongoClient.findAndModify(query, update, BlogPost.class)
                .switchIfEmpty(Mono.error(new Exception(String.format("no blogPost: %s", blogPost.getPost_id()))));
    }

    public Mono<BlogPost> deleteById(MSAContext context, String post_id) {
        Query query = Query.query(Criteria.where("post_id").is(post_id));
        return mongoClient.findAndRemove(query, BlogPost.class);
    }

}
package com.mymsa.web.service;

import com.mymsa.core.context.MSAContext;
import com.mymsa.web.model.BlogPost;
import com.mymsa.web.repository.BlogPostRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Service
public class BlogPostService {

    @Autowired
    private BlogPostRepository blogPostRepository;

    public Mono<BlogPost> create(MSAContext context, BlogPost blogPost) {
        return blogPostRepository.create(context, blogPost);
    }

    public Flux<BlogPost> findAll(MSAContext context) {
        return blogPostRepository.findAll(context);
    }

    public Mono<BlogPost> findById(MSAContext context, String post_id) {
        return blogPostRepository.findById(context, post_id);
    }

    public Mono<BlogPost> update(MSAContext context, BlogPost blogPost) {
        return blogPostRepository.update(context, blogPost);
    }

    public Mono<BlogPost> deleteById(MSAContext context, String post_id) {
        return blogPostRepository.deleteById(context, post_id);
    }

}
package com.mymsa.web.controller;

import com.mymsa.core.context.MSAContext;
import com.mymsa.web.model.BlogPost;
import com.mymsa.web.service.BlogPostService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@RequestMapping("/app-web")
@RestController
@Slf4j
public class BlogPostController {

    @Autowired
    private BlogPostService blogPostService;

    @PostMapping("/blog-post")
    public Mono<BlogPost> create(@RequestHeader(value = "sessionID", required = false) String sessionID, @RequestBody BlogPost blogPost) {
        MSAContext context = new MSAContext(sessionID);
        log.info("{}|create-blog-post||", context);
        return blogPostService.create(context, blogPost)
                .doOnSuccess(result -> {
                    log.info("{}|create-blog-pos|Y|{}", context, result);
                })
                .doOnError(error -> {
                    log.error("{}|create-blog-pos|N|", context, error);
                });
    }

    @GetMapping("/blog-post")
    public Flux<BlogPost> findAll(@RequestHeader(value = "sessionID", required = false) String sessionID) {
        MSAContext context = new MSAContext(sessionID);
        log.info("{}|findAll-blog-post||", context);
        return blogPostService.findAll(context)
                .doOnComplete(() -> {
                    log.info("{}|findAll-blog-post|Y|{}", context);
                })
                .doOnError(error -> {
                    log.error("{}|findAll-blog-post|N|", context, error);
                });
    }

    @GetMapping("/blog-post/{post_id}")
    public Mono<BlogPost> findById(@RequestHeader(value = "sessionID", required = false) String sessionID, @PathVariable String post_id) {
        MSAContext context = new MSAContext(sessionID);
        log.info("{}|find-blog-post||{}", context, post_id);
        return blogPostService.findById(context, post_id)
                .doOnSuccess(result -> {
                    log.info("{}|find-blog-post|Y|{}", context, result);
                })
                .doOnError(error -> {
                    log.error("{}|find-blog-post|N|", context, error);
                });
    }

    @PutMapping("/blog-post")
    public Mono<BlogPost> update(@RequestHeader(value = "sessionID", required = false) String sessionID, @RequestBody BlogPost blogPost) {
        MSAContext context = new MSAContext(sessionID);
        log.info("{}|update-blog-post||{}", context, blogPost);
        return blogPostService.update(context, blogPost)
                .doOnSuccess(result -> {
                    log.info("{}|update-blog-post|Y|{}", context, result);
                })
                .doOnError(error -> {
                    log.error("{}|update-blog-post|N|", context, error);
                });
    }

    @DeleteMapping("/blog-post/{post_id}")
    public Mono<BlogPost> deleteById(@RequestHeader(value = "sessionID", required = false) String sessionID, @PathVariable String post_id) {
        MSAContext context = new MSAContext(sessionID);
        log.info("{}|delete-blog-post||{}", context, post_id);
        return blogPostService.deleteById(context, post_id)
                .doOnSuccess(result -> {
                    log.info("{}|delete-blog-post|Y|{}", context, result);
                })
                .doOnError(error -> {
                    log.error("{}|delete-blog-post|N|", context, error);
                });
    }

}

상단 툴바의 ‘Configurations’에서 ‘my-msa[build]’을 선택하고 실행을 눌러서 프로그램을 빌드합니다.

3. 서버 실행 및 기능 시험

$ java "-Dspring.profiles.active=local" -jar .\app-eureka\build\libs\app-eureka-1.0.0.jar
$ java "-Dspring.profiles.active=local" -jar .\app-gateway\build\libs\app-gateway-1.0.0.jar
$ java "-Dspring.profiles.active=local" -jar .\app-web\build\libs\app-web-1.0.0.jar
{
  "title": "blog post titie #1",
  "context": "context #1"
}
{
  "post_id": "<post_id>",
  "title": "blog post modified title #1",
  "context": "context modify for test #1"
}

참고

이 포스트와 관련 포스트입니다.