ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Spring Boot + Kotlin + MongoDB로 CRUD 해보기
    프로젝트/mongoDB 2024. 2. 12. 00:01

    개요

    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

     

    '프로젝트 > mongoDB' 카테고리의 다른 글

    MongoDB 인덱스 운영법  (0) 2024.02.22
    MongoDB 인덱스란?  (0) 2024.02.15
    MongoDB 동시성 제어  (0) 2024.02.11
    MongoDB Data Modeling  (0) 2024.02.06
    MongoDB Aggregation Operations  (0) 2024.02.05

    댓글

Designed by Tistory.