이 포스트는 이전 포스트에서 생성한 Spring Boot MSA 프로젝트를 기반으로 MongoDB와 연동해서 간단한 CRUD를 해 보는 과정에 대한 설명입니다.
이 포스트는 다음 과정을 완료한 후에 참고하시길 바랍니다.
1. MongoDB 설치 (docker)
- 우선 데이터를 저장할 MongoDB를 설치하겠습니다. 이번 과정에서는 docker를 이용해서 설치하도록 하겠습니다.
- 우선 ‘Docker Desktop’을 실행합니다.
- docker.yml 파일을 만들고 아래와 같이 편집합니다.
services:
mongodb:
image: mongo:latest
container_name: mongodb-mymsa
ports:
- 27017:27017
- 터미널에서 아래와 같이 명령을 실행합니다.
$ docker-compose -f docker.yml up
- ‘Docker Desktop’에 ‘mongodb-mymsa’ 컨테이너가 추가되어 있는 것을 확인할 수 있습니다.
- docker-compose 명령을 실행한 터미널에서 Ctrl+C를 입력해서 명령 실행을 종료합니다.
- 방금 전에 실행한 명령으로 인해 MongoDB가 종료됐습니다.
- ‘Docker Desktop’에서 ‘mongodb-mymsa’ 컨테이너에서 ‘Start’ 버튼을 눌러서 컨테이너를 재 시작합니다.
- 이후로는 ‘Docker Desktop’에서 컨테이너의 시작, 정지를 관리하면 됩니다.
2. MongoDB 기능 구현
- 이제 MongoDB를 이용해서 간단한 정보를 생성/조회/수정/삭제하는 기능을 만들어보겠습니다.
- MongoDB의 아래와 같은 접속 관련 정보를 ‘app-web/src/main/resources/application.yml’에 추가합니다.
data:
mongodb:
uri: mongodb://localhost:27017/mymsa
auto-index-creation: true
- 데이터 형식을 정의한 ‘BlogPost’ 클래스를 web-app의 ‘com.mymsa.web.model’ 패키지에 생성하고 아래와 같이 편집합니다.
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;
}
- MongoDB에 BlogPost 데이터를 저장/조회/수정/삭제하는 기능을 수행하는 BlogPostRepository 클래스를 ‘com.mymsa.web.repository’ 패키지에 생성하고 아래와 같이 편집합니다.
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);
}
}
- BlogPostRepository를 이용해 BlogPost 데이터를 관리하고 필요한 기능을 수행하는 BlogPostService 클래스를 ‘com.mymsa.web.service’ 패키지에 생성하고 아래와 같이 편집합니다.
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);
}
}
- HTTP를 이용해서 외부로부터 BlogPost 데이터를 관련 명령을 받고 처리 결과를 리턴하는 기능을 수행하는 BlogPostController 클래스를 ‘com.mymsa.web.controller’ 패키지에 생성하고 아래와 같이 편집합니다.
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[clean]’을 선택하고 실행을 눌러서 기존의 모든 build 정보를 삭제합니다. 이 명령을 실행하기 전에는 my-msa 관련 모든 프로그램은 정지된 상태이어야 합니다.
상단 툴바의 ‘Configurations’에서 ‘my-msa[build]’을 선택하고 실행을 눌러서 프로그램을 빌드합니다.
3. 서버 실행 및 기능 시험
- 이제 MongoDB에 정보를 생성/조회/수정/삭제하는 기능을 확인해 보겠습니다.
- Power Shell을 3개 이상 실행하고 각각 Power Shell에서 cd 명령을 이용해 my-msa 프로젝트 폴더로 이동합니다.
- 첫 번째 Power Shell에서 아래 명령을 실행해서 app-eureka 서버를 실행합니다.
$ java "-Dspring.profiles.active=local" -jar .\app-eureka\build\libs\app-eureka-1.0.0.jar
- 두 번째 Power Shell에서 아래 명령을 실행해서 app-gateway 서버를 실행합니다.
$ java "-Dspring.profiles.active=local" -jar .\app-gateway\build\libs\app-gateway-1.0.0.jar
- 나머지 Power Shell에서 아래 명령을 실행해서 app-web 서버를 실행합니다.
$ java "-Dspring.profiles.active=local" -jar .\app-web\build\libs\app-web-1.0.0.jar
- 이제 REST API를 툴을 이용해서 기능을 시험해 보겠습니다. 저는 크롬 플러그인으로 쉽게 사용할 수 있는 Talend API Tester – Free Edition을 사용했습니다.
- 우선 생성 테스트를 위해서 아래와 같이 json을 POST 합니다.
◦ Method: POST
◦ URL: http://localhost:9001/app-web/blog-post
◦ BODY: <아래 내용>
{
"title": "blog post titie #1",
"context": "context #1"
}
- 이제 MongoDB의 내용을 확인해 보겠습니다. MongoDB의 내용을 확인하기 위한 툴로 저는 Studio 3T를 이용했습니다.
- localhost의 MongoDB에 접속하고 mymsa 데이터베이스의 blogPost 컬렉션을 확인하면 방금 전에 입력한 데이터가 저장된 것을 확인할 수 있습니다.
- 테스트를 위해서 위 내용을 조금씩 변경하면서 POST 요청을 해서 여러 개의 BlogPost 정보를 생성해 봅니다. DB의 정보를 새로고침 해보면 아래와 같이 여러 개의 데이터가 생성된 것을 확인할 수 있습니다.
- 이번에는 아래와 같은 GET 명령을 이용해 생성된 데이터를 조회해 보겠습니다.
◦ Method: GET
◦ URL: http://localhost:9001/app-web/blog-post - 여러 개의 BlogPost 정보를 확인할 수 있습니다.
- 이번에는 아래와 같이 GET 명령을 이용해 한 개의 데이터를 조회해 보겠습니다.
◦ Method: GET
◦ URL: http://localhost:9001/app-web/blog-post/<post_id> - URL의 마지막 부분의 <post_id>는 조회하고자 하는 BlogPost의 post_id 값입니다.
- 특정한 한 개의 BlogPost를 확인할 수 있습니다.
- 이번에는 아래와 같이 PUT 명령을 이용해 데이터의 내용을 수정해 보겠습니다.
◦ Method: PUT
◦ URL: http://localhost:9001/app-web/blog-post
◦ BODY: <아래 내용>
{
"post_id": "<post_id>",
"title": "blog post modified title #1",
"context": "context modify for test #1"
}
- 위 내용 중 post_id는 수정하고자 하는 BlogPost의 post_id로 변경해야 합니다.
- DB의 정보를 새로고침 해보면 아래와 같이 한 개의 데이터가 수정된 것을 확인할 수 있습니다.
- 이번에는 아래와 같이 DELETE 명령을 이용해 데이터를 삭제해 보겠습니다.
◦ Method: DELETE
◦ URL: http://localhost:9001/app-web/blog-post/<post_id> - URL의 마지막 부분의 <post_id>는 삭제하고자 하는 BlogPost의 post_id 값입니다.
- DB의 정보를 새로고침 해보면 아래와 같이 한 개의 데이터가 삭제된 것을 확인할 수 있습니다.
- app-gateway 창을 확인하면 수행하는 명령에 관련된 로그가 잘 찍히고 있는 것을 확인할 수 있습니다.
- app-web 창을 확인하면 수행하는 명령에 관련된 로그가 잘 찍히고 있는 것을 확인할 수 있습니다.
- app-gateway와 app-web의 sessionID 값을 비교해서 값이 같으면 동일한 명령에 대한 처리 로그인 것을 알 수 있습니다.
참고
이 포스트와 관련 포스트입니다.
'프로그램 > Java' 카테고리의 다른 글
Spring Boot Micro Service Architecture 만들기 (2) (0) | 2023.07.09 |
---|---|
Spring Boot Micro Service Architecture 만들기 (1) (0) | 2023.07.09 |