Docker Compose로 Spring Boot + MongoDB 구성(part.2)
IDE의 데이터베이스 연동 기능을 이용하여 제대로 구동 중인지 다시 한 번 확인 가능하다.
IntelliJ에서 확인하면 아래와 같은 모습이다.

User와 Password에는 MongoDB 컨테이너를 docker exec 하여 createUser() 했던 내용으로 인증 과정을 거치면 된다.
이를 application.properties 혹은 application.yml로 적용하면 된다.
(필자는 use guest_book로 새로운 데이터 베이스를 구축했다.)
properties라면
spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017
spring.data.mongodb.database=guest_book
yml이라면
spring:
data:
mongodb:
host: localhost
port: 27017
database: guest_book
와 같다.
애플리케이션과 DB 연동 정보를 입력했으니 연동해보자.
일단 위에서 비활성화시켜 둔 MongoDB의 의존성을 다시 활성화시켜 주자.
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.mongodb:mongodb-driver-sync:4.10.2'
implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
이제 소스를 작성해보자.
크게
- 데이터 형식을 잡는 Model
- DB 동작을 수행하는 Repository
- 동작을 실행하는 Service
- 사용자의 동작을 받는 Controller
를 작성할 것이며, 프로젝트 구조는

와 같다.
Model
데이터 핸들링을 위한 Model 객체를 생성한다.
package com.jaehoonman.guestbookspringboot.model;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import java.util.Date;
@Data //Getter, Setter lombok 자동 설정.
@Document(collection = "guest_book") // Mapping collection 지정.
public class GuestBook {
@Id
private String id;
private String title;
private String writer;
private String contents;
private Date createdTime = new Date();
}
(날짜는 제대로 안 나오긴 하지만 일단 테스트니…)
Repository
DB와 상호 작용하며 동작을 수행하는 Repository는 간단하게 MongoRepository 인터페이스를 extends 하는 것으로 기본 동작들을 제공받을 수 있다.
package com.jaehoonman.guestbookspringboot.repository;
import com.jaehoonman.guestbookspringboot.model.GuestBook;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface GuestBookRepository extends MongoRepository<GuestBook, Long> {
GuestBook findById(String id);
void deleteById(String id);
}
버전 차이인지는 잘 모르겠지만 findById와 같이 Id값을 통해 데이터를 찾는 기능이 동작하지 않아 커스텀 메서드를 만들었다.
(MongoRepository<GuestBook, Long> 처럼 지정한 ID 타입이 실제 @Id 필드 타입(String)과 다른 것이 원인일 수 있다.)
Service
사용자의 요청을 Controller에서 전달 받아 수행하고 싶은 동작을 지정하는 Service 레이어를 작성하자.
먼저 수행할 동작의 인터페이스를 지정한다.
package com.jaehoonman.guestbookspringboot.service;
import com.jaehoonman.guestbookspringboot.model.GuestBook;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public interface GuestBookService {
GuestBook createGuestBook(GuestBook guestBook);
List<GuestBook> getAllGuestBooks();
GuestBook getGuestBookById(String id);
void deleteGuestBookById(String id);
}
이후 해당 서비스를 implements하여 Repository와 연동, 수행할 동작을 지정해주자.
package com.jaehoonman.guestbookspringboot.service.impl;
import com.jaehoonman.guestbookspringboot.model.GuestBook;
import com.jaehoonman.guestbookspringboot.repository.GuestBookRepository;
import com.jaehoonman.guestbookspringboot.service.GuestBookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class GuestBookImpl implements GuestBookService {
private final GuestBookRepository guestBookRepository;
@Autowired
public GuestBookImpl(GuestBookRepository guestBookRepository){
this.guestBookRepository = guestBookRepository;
}
@Override
public GuestBook createGuestBook(GuestBook guestBook) {
return this.guestBookRepository.save(guestBook);
}
@Override
public List<GuestBook> getAllGuestBooks() {
return this.guestBookRepository.findAll();
}
@Override
public GuestBook getGuestBookById(String id){
return this.guestBookRepository.findById(id);
}
@Override
public void deleteGuestBookById(String id) {
this.guestBookRepository.deleteById(id);
}
}
Controller
HTTP Method에 맞는 처리를 수행하도록 Controller를 작성하자.
package com.jaehoonman.guestbookspringboot.controller;
import com.jaehoonman.guestbookspringboot.model.GuestBook;
import com.jaehoonman.guestbookspringboot.service.GuestBookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/guestbook")//호스트 맵핑
public class GuestBookController {
//서비스 주입
private final GuestBookService guestBookService;
@Autowired
public GuestBookController(GuestBookService guestBookService){
this.guestBookService = guestBookService;
}
//GET HTTP 메서드로 모든 데이터 읽어오기.
@GetMapping
public List<GuestBook> getAllGuestBook(){
return guestBookService.getAllGuestBooks();
}
//GET HTTP 메서드로 id에 해당하는 데이터 읽어오기.
@GetMapping("/{id}")
public GuestBook getGuestBookByTitle(@PathVariable String id){
return guestBookService.getGuestBookById(id);
}
//POST HTTP 메서드로 새로운 데이터 insert.
@PostMapping
public GuestBook createGuestBook(@RequestBody GuestBook guestBook){
return guestBookService.createGuestBook(guestBook);
}
//DELETE HTTP 메서드로 id에 해당하는 데이터 삭제하기.
@DeleteMapping("/{id}")
public void deleteGuestBookById(@PathVariable String id){
guestBookService.deleteGuestBookById(id);
}
}
이제 서비스를 실행시키고 동작을 확인하자.
Postman과 같은 툴을 사용해도 되지만 필자는 터미널에서 curl 커맨드를 사용해보겠다.
동작 확인
데이터 삽입
curl -d '{"title":"안녕", "writer":"재훈맨", "contents":"하세요"}' -H "Content-Type: application/json" -X POST http://localhost:8080/[ENDPOINT]
(id는 자동 생성되기에 따로 지정하지 않는다.)
조회
전체 조회
curl -X GET http://localhost:8080/[ENDPOINT]
id로 조회
curl -X GET http://localhost:8080/[ENDPOINT]/[id]
삭제
curl -X DELETE http://localhost:8080/[ENDPOINT]/[id]

동작을 확인하였으니 이제 Docker Compose로 구성해보자.
Docker Compose 작성 및 컨테이너화
docker-compose.yml 파일은 Dockerfile과 같은 application Root 위치에 생성한다.
version: "3" # Docker compose의 버전 선택
services: # 컨테이너의 이름, 이미지, 포트 등을 정의하여 컨테이너 생성 및 관리
app:
image: guest-book:0.0.1 # 기반이 되는 이미지 지정
container_name: spring-boot-guest-book # 생성될 컨테이너 이름 정의
ports:
- 8080:8080
depends_on: # 의존성 주입으로 해당 서비스가 먼저 생성, 실행된다
- mongodb
environment: # 환경 변수 설정.
- SPRING_DATA_MONGODB_URI=mongodb://jaehoonman:1234@mongodb:27017/guest_book
mongodb:
image: mongo:latest
container_name: mongodb-guest-book
environment:
- MONGO_INITDB_ROOT_USERNAME=jaehoonman
- MONGO_INITDB_ROOT_PASSWORD=1234
restart: always # 재시작 정책
ports:
- 27017:27017
app의 서비스에서 기반이 되는 image는 우리가 이전에 build 했던 [이미지이름]:[버전] 의 이미지를 지정해준다.
environment는 application.properties 혹은 application.yml에 지정하였던 localhost 환경을 더 이상 사용하지 못하니, docker compose를 구성하는 환경에 맞춰서 바꿔준다.
추가로 application.properties에 설정했던 host 옵션도 바꿔주자.
#spring.data.mongodb.host=localhost
spring.data.mongodb.host=mongodb
spring.data.mongodb.port=27017
spring.data.mongodb.database=guest_book
옵션이 변경되었으니 이를 적용시키기 위해 build clean, build 를 통해 JAR 파일을 다시 빌드해주고, 다시
$ docker build -t [태그명]:[버전] .
의 커맨드로 docker image를 빌드해주자.
mongodb 서비스에서도 environment에 DB 접속 정보를 기입해준다.
작성이 완료되었다면 터미널을 통해 application Root 위치로 cd 하여 아래의 명령어를 입력하자.
docker compose up

app 서비스에서 depends_on 으로 지정하였던 mongodb 서비스가 먼저 구성이 되고, 이후 app이 구성되는 것을 확인할 수 있다.
생성되는 컨테이너 이름도 container_name에 맞게 구성된다.
이전과 같은 동작 테스트를 통해 정상 여부를 판단할 수 있다.
Ref.
https://velog.io/@max9106/Spring-Boot-MongoDB
https://sangmaeng.tistory.com/25
https://umanking.github.io/2023/08/04/spring-boot-mongodb/
https://yang1s.tistory.com/30
https://imasoftwareengineer.tistory.com/51