프로젝트/mongoDB

Spring Boot + Kotlin + MongoDB로 CRUD 해보기

Junuuu 2024. 2. 12. 00:01
728x90

개요

Spring Boot + Kotlin + MongoDB를 활용하여 CRUD를 수행해보고자 합니다.

만약 MongoDB에 대해 잘 모르겠다면 "MongoDB란?"이라는 글을 참고하시고 오셔도 좋을 것 같습니다.

실제 코드는 github을 참고해 주세요.

 

 

환경

  • Spring Boot 3.1
  • JDK 17
  • MongoDB with Docker

 

Gradle 의존성 추가

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

 

web와 mongodb에 대한 의존성을 추가해 줍니다.

 

docker-compose.yml

version: '3'
services:
  mongodb:
    image: mongo
    container_name: mongodb-container
    ports:
      - "27017:27017"
    environment:
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD: password

 

mongoDB에 대한 docker compose 파일을 작성하였습니다.

docker-compose up -d 명령어로 백그라운드에서 mongoDB를 실행할 수 있습니다.

 

application.yml

spring:
  data:
    mongodb:
      uri: mongodb://root:password@localhost:27017/mongodb-container?authSource=admin

docker compose 파일에 적힌 대로 uri를 명시해 주며 인증을 위해 authSource=admin을 쿼리파라미터로 붙여주었습니다.

 

 

Member Entity 만들기

@Document
data class Member(
        @Id
        var id: String? = null,

        @Field("histories")
        val histories: MutableMap<String, String>
) {
        fun addHistories(additionalHistories: List<String>) {
                additionalHistories.forEach {
                        histories[it] = it
                }
        }
}

@Document, @Field 어노테이션을 활용하여 mongoDB에 영속될 수 있는 객체로 만들어냅니다.

 

 

MemberRepository

interface MemberRepository: MongoRepository<Member, String> {
}

MongoRepository를 상속받습니다.

 

MemberService

@Service
@Transactional(readOnly = true)
class MemberService(
        private val memberRepository: MemberRepository,
){
    fun findUserBy(id: String): Member {
        return memberRepository.findByIdOrNull(id) ?: throw IllegalArgumentException("${id}로 회원을 조회할 수 없습니다")
    }

    @Transactional
    fun signUp(memberHistories: List<String>): String{
        val histories = memberHistories.map { it to it }.toMap().toMutableMap()
        val member = Member(histories = histories)
        return memberRepository.save(member).id!!
    }

    @Transactional
    fun addMemberHistories(id: String, additionalHistories: List<String>): Member{
        val member = memberRepository.findByIdOrNull(id) ?: throw IllegalArgumentException("${id}로 회원을 조회할 수 없습니다")
        member.addHistories(additionalHistories)
        return memberRepository.save(member)
    }

    @Transactional
    fun withdraw(id: String){
        memberRepository.deleteById(id)
    }

}

memberRepository를 활용하여 CRUD를 수행하였습니다.

 

MemberController

@RestController
@RequestMapping("/members")
class MemberController(
        private val memberService: MemberService,
) {

    @GetMapping("/{id}")
    fun getMemberById(@PathVariable id: String): ResponseEntity<Member> {
        val member = memberService.findUserBy(id)
        return ResponseEntity.ok(member)
    }

    @PostMapping
    fun signUp(@RequestBody signUpRequest: SignUpRequest): ResponseEntity<String> {
        val id = memberService.signUp(signUpRequest.histories)
        return ResponseEntity.ok(id)
    }

    @PutMapping("/{id}/history")
    fun addMemberHistories(
            @PathVariable id: String,
            @RequestBody addHistoryRequest: AddHistoryRequest,
    ): ResponseEntity<Member> {
        val member = memberService.addMemberHistories(id = id, additionalHistories = addHistoryRequest.histories)
        return ResponseEntity.ok(member)
    }

    @DeleteMapping("/{id}")
    fun withdraw(@PathVariable id: String): ResponseEntity<Unit> {
        memberService.withdraw(id = id)
        return ResponseEntity.noContent().build()
    }
}

 

REST API를 활용하여 CRUD를 구현하였습니다.

 

기타 DTO들

data class AddHistoryRequest(
    val histories: List<String>,
)

data class SignUpRequest(
    val histories: List<String>,
)

 

 

실제 API 호출을 통한 테스트 - curl

회원 가입

curl --location 'http://localhost:8080/members' \
--header 'Content-Type: application/json' \
--data '{
    "histories": [
        "wakeup", "eat", "study"
    ]
}'

 

회원 조회

curl --location 'http://localhost:8080/members/6589c650554cf81aee139c3c'

 

 

 

회원 이력 수정

curl --location --request PUT 'http://localhost:8080/members/6589c650554cf81aee139c3c/history' \
--header 'Content-Type: application/json' \
--data '{
    "histories": [
        "drink","sleep"
    ]
}'

 

 

회원 탈퇴

curl --location --request DELETE 'http://localhost:8080/members/1'

 

 

마무리

이 글을 작성하기 전에 JPA로 JSON Column CRUD 해보기라는 글을 작성해 보았습니다.

데이터베이스가 MySQL에서 MongoDB로 변환되었지만 Spring에서 지원하는 추상화 덕분에 이전에 작성했던 코드를 재사용하여 최소한의 수정으로 MongoDB CRUD 튜토리얼을 수행해 볼 수 있었습니다.

Spring의 추상화의 강력함을 다시 한번 느끼게 되는 것 같습니다.

 

 

 

참고자료

https://docs.spring.io/spring-data/mongodb/docs/current-SNAPSHOT/reference/html/#mongodb-connectors

https://medium.com/@sumanzadeakhil/spring-boot-mongodb-rest-api-crud-with-kotlin-227d296bbbef

https://www.mongodb.com/compatibility/spring-boot

https://www.baeldung.com/spring-data-mongodb-connection