Home
Backend Engineering
스프링부트 멀티모듈 구성하기(3) - API 모듈 작성하기
울이
울이
November 17, 2022
2 min

Table Of Contents

01
API Module 만들기 1 - 의존성 추가
02
API Module 만둘기 2 - 패키지 세팅하기
03
API Module 만들기 3 - Repository 만들기
04
API Module 만들기 4 - Service 만들기
05
API Module 설정추가 - application.yml
06
API Module 마무리
  • module-api에서 module-core를 잘 호출하는지 확인 할 수 있는 간단한 API를 만들어보자
  • module-core에서 CustomerOrder 엔티티를 선언했는데, 이에 맞게 module-api에서는 CustomerOrder 읽어오기 / 쓰기 API를 만들어 보자.
  • 전체 적인 구조를 보았을 때, 나중에 가서는 역할이 달라 질 수 있겠지만(활용하기 나름…) CustomerOrder를 간단히 사용 해 보자

API Module 만들기 1 - 의존성 추가

  • 나의 module-apibuild.gradle.kts에는 아래와 같이 비어있다.

    plugins{
    
    }
    
    dependencies{
    
    }
    
  • module-api에서 혹시 다른 디펜던시를 사용하고싶다면, build.gradle.ktsdependencies에 추가 해 주자

API Module 만둘기 2 - 패키지 세팅하기

  • module-core에서 말했듯이, 멀티모듈을 사용하려면 base package 이름이 같아야한다.

  • com.wool 패키지를 생성하고, 하위에 사용 할 패키지들을 따로 생성 해 주자

    • (패키지는 아니지만…) ModuleApiApplication.kt 를 생성하고 스프링 부트 어플리케이션을 선언 해 주자

      package com.wool
      
      import org.springframework.boot.autoconfigure.SpringBootApplication
      import org.springframework.boot.runApplication
      
      @SpringBootApplication
      class ModuleApiApplication
      
      fun main(args: Array<String>) {
          runApplication<ModuleApiApplication>(*args)
      }
      
    • com.wool.controller : API Controller, 외부 Request를 받아 Response 해주는 역할

    • com.wool.repository : API에서 사용 할 Repository, module-core에서 선언한 엔티티를 불러와서 Repository에서 사용

    • com.wool.service : API에서 사용 할 Service, repository에서 불러온 데이터를 가공하여 controller에게 전달

  • 이 외에도 각각의 dto들이 있을 수 있다. 이건, 생성하면서 같이 보기로 하자.

API Module 만들기 3 - Repository 만들기

  • com.wool.repository 패키지를 생성하고, ModuleCoreCustomerRepository.ktModuleCoreOrderRepository.kt를 생성하자
  • 나는 module-core에서 엔티티가 왔다는 것을 기억하고싶어서, 레포지토리 이름 앞에 ModuleCore를 붙여주었다.
  • 신기한점은, module-core에서 선언한 엔티티를 module-api에서 사용 할 수 있다는 것이다.
  • module-core 라는 모듈명이 붙지 않아도, 그대로 사용이 가능하다.

Repository 만들기 1 - ModuleCoreCustomerRepository.kt

  • JPA를 사용하기 위해 JpaRepository를 상속받는다.

    // com.wool.repository.ModuleCoreCustomerRepository.kt
    package com.wool.repository
    
    import com.wool.entity.Customer
    import org.springframework.data.jpa.repository.JpaRepository
    
    interface ModuleCoreCustomerRepository : JpaRepository<Customer, Long> {
    
    }
    

Repository 만들기 2 - ModuleCoreOrderRepository.kt

  • 마찬가지로 JPA를 사용하기때문에 JPARepository를 상속받는다.

    package com.wool.repository
    
    import com.wool.entity.Customer
    import org.springframework.data.jpa.repository.JpaRepository
    
    interface ModuleCoreCustomerRepository:JpaRepository<Customer, Long> {
    
    }
    

API Module 만들기 4 - Service 만들기

  • 이제 방금 만든 Repository를 주입해서 사용 할 Service를 만들어보자
  • service 패키지에는, Controller에서 들어온 데이터를 가지고 Repository와 소통 해 주어야 하기 때문에 dto를 만들어준다
  • customerCustomerDto를, orderOrderDtoOrderRequestDto 를 만들어준다
    • OrderRequestDto : 컨트롤러에서 받아주는 데이터 형태. OrderDto에 매핑되어있는 Customer 작업을 위해 customer_id를 만들어 준다
    • OrderDto : OrderRequestDto 에서 받아온 customer 데이터를 가지고, Customer를 찾아서 Order에 매핑해주는 역할을 한다

Service Dto 만들기 1 - CustomerDto.kt

  • Customer 데이터를 엔티티로 만들어주는 역할을 하는 data클래스이다

    package com.wool.service.dtos.customer
    
    import com.wool.entity.Customer
    
    data class CustomerDto(
        val customerNickName: String,
        val customerAddress: String,
    ) {
        fun toEntity() =
            Customer(
                customerNickName = this.customerNickName,
                customerAddress = this.customerAddress
            )
    }
    
    • toEntity() : CustomerDtoCustomer 엔티티로 만들어주는 역할을 한다

Service Dto 만들기 2 - OrderRequestDto.kt

  • Controller에서 데이터를 받아오는 OrderRequestDto.kt 를 만들어준다

    package com.wool.service.dtos.order
    
    data class OrderRequestDto(
        val orderStoreName: String,
        val orderStoreAddress: String,
        val orderItem: String,
        val orderPrice: Int,
        val customerId: Long,
    )
    
    • customerId : OrderRequestDto에서 추출해 CustomerRepository를 통해 Customer를 찾기 위한 값

Service Dto 만들기 3 - OrderDto.kt

  • OrderRequestDto를 통해 Customer 객체를 받아 온 후에 OrderDto에 매핑되어 값을 받아오도록 하는 data클래스

    package com.wool.service.dtos.order
    
    import com.wool.entity.Customer
    import com.wool.entity.Order
    
    data class OrderDto(
        val orderStoreName: String,
        val orderStoreAddress: String,
        val orderItem: String,
        val orderPrice: Int,
        val customer: Customer,
    ){
        fun toEntity() = Order(
            orderStoreName = this.orderStoreName,
            orderStoreAddress = this.orderStoreAddress,
            orderItem = this.orderItem,
            orderPrice = this.orderPrice,
            customer = this.customer
        )
    }
    

Service 만들기 1 - CustomerService.kt

  • CusomterDto와 함께 ModuleCoreCustomerRepository에서 통신한 데이터를 조합하고 Controller로 넘겨주는 역할

    package com.wool.service
    
    import com.wool.repository.ModuleCoreCustomerRepository
    import com.wool.service.dtos.customer.CustomerDto
    import org.springframework.stereotype.Service
    
    @Service
    class CustomerService(
        private val customerRepository: ModuleCoreCustomerRepository
    ) {
    
        fun getCustomers() = customerRepository.findAll()
    
        fun saveCustomers(customerDto: CustomerDto) {
            this.customerRepository.save(customerDto.toEntity())
        }
    }
    
    • getCustomers() : ModuleCoreCustomerRepository에서 findAll()을 통해 모든 Customer를 가져온다
    • saveCustomers() : CustomerDtoCustomer 엔티티로 만들어 ModuleCoreCustomerRepository에 저장한다

Service 만들기 2 - OrderService.kt

  • Controller에서 받아온 OrderRequestDto를 통해 Customer를 찾아 OrderDto에 매핑하여 값을 불러오고 저장하는 역할

    package com.wool.service
    
    import com.wool.entity.Customer
    import com.wool.repository.ModuleCoreCustomerRepository
    import com.wool.repository.ModuleCoreOrderRepository
    import com.wool.service.dtos.order.OrderRequestDto
    import com.wool.service.dtos.order.OrderDto
    import org.springframework.stereotype.Service
    

@Service class OrderService( private val orderRepository: ModuleCoreOrderRepository, private val customerRepository: ModuleCoreCustomerRepository ) {

  fun getOrders() = orderRepository.findAll()

  fun saveOrder(orderRequestDto: OrderRequestDto) {
      val customer: Customer = customerRepository.findById(orderRequestDto.customerId).get()
      //customer가 있을 경우
      if (customer != null) {
          val orderDto = OrderDto(
              orderStoreName = orderRequestDto.orderStoreName,
              orderStoreAddress = orderRequestDto.orderStoreAddress,
              orderItem = orderRequestDto.orderItem,
              orderPrice = orderRequestDto.orderPrice,
              customer = customer
          )
          orderRepository.save(orderDto.toEntity())
      } else {
          //customer가 없을 경우
          throw Exception("customer가 없습니다.")
      }

  }

}

  - `getOrders()` : `ModuleCoreOrderRepository`에서 `findAll()`을 통해 모든 `Order`를 가져온다
  - `saveOrder()` : `OrderRequestDto`를 통해 `Customer`를 찾아 `OrderDto`에 매핑하여 값을 불러오고 저장한다

## API Module 만들기 5 - Controller

- 외부의 요청을 받아 줄 수 있는 `Controller`를 만든다
- 자세하게 만든 기능이 아니기 때문에, 컨트롤러는 Dto 설정 외에 크게 볼 것이 없는 것 같다
- <img src="./img/3-apimodule/controller.png">

### Controller 만들기 1 - CustomerController.kt

```kotlin
package com.wool.controller

import com.wool.service.CustomerService
import com.wool.service.OrderService
import com.wool.service.dtos.customer.CustomerDto
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RestController


@RestController
class CustomerController(
  private val customerService: CustomerService
) {

  @PostMapping("/customers")
  fun saveCustomers(@RequestBody customerDto: CustomerDto) {
      return this.customerService.saveCustomers(customerDto)
  }

  @GetMapping("/customers")
  fun getCustomers() = this.customerService.getCustomers()
}
  • saveCustomers() : CustomerDtoCustomerService에 넘겨주어 저장한다
  • getCustomers() : CustomerService에서 Customer를 가져와 반환한다

Controller 만들기 2 - OrderController.kt

package com.wool.controller

import com.wool.service.OrderService
import com.wool.service.dtos.order.OrderRequestDto
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RestController


@RestController
class OrderController(
    private val orderService: OrderService
) {

    @GetMapping("/orders")
    fun getOrders() = orderService.getOrders()

    @PostMapping("/orders")
    fun saveOrders(@RequestBody orderRequestDto: OrderRequestDto) {
        //save order
        return orderService.saveOrder(orderRequestDto)
    }
}
  • getOrders() : OrderService에서 Order를 가져와 반환한다
  • saveOrders() : OrderRequestDtoOrderService에 넘겨주어 저장한다

API Module 설정추가 - application.yml

  • 서버를 동작시키는 여러 설정값들은 application.yml 에서 관리하자
  • application.yml에 적은 값은, docker-compose로 실행한 postgresql 정보를 가져와 작성했다
  • resources 패키지 아래에 application.yml을 만들고 아래와 같이 적는다
    spring:
      datasource:
        hikari:
          pool-name: HikariCp
          maximum-pool-size: 2
          minimum-idle: 2
        username: wool
        password: password1234
        url: jdbc:postgresql://localhost:9876/wooldb
        driver-class-name: org.postgresql.Driver
      jpa:
        hibernate:
          ddl-auto: update
        show-sql: true
        properties:
          hibernate:
            format_sql: true
            default_schema: springtest
      jackson:
        serialization:
          fail-on-empty-beans: false
    

API Module 마무리

  • 이번 글에서는 module-core에 작성한 엔티티를 가져와 module-api에서 사용 해 보았다
  • 실제로 서버를 실행해서 테스트 해 보면 데이터 조회 / 저장 이 잘 된다
  • ModuleApiApplication.kt를 실행 한 후 아래의 API 테스트의 데이터로 테스트한다

API 테스트하기

Customer 조회

  • URL : http://localhost:8080/customers
  • Method : GET
  • Response
    [
      {
        "customerId": 1,
        "customerNickName": "올리버",
        "customerAddress": "서울특별시에서 살고싶음",
        "createdAt": "2022-11-20T00:00:00",
        "updatedAt": "2022-11-20T00:00:00"
      },
      {
        "customerId": 2,
        "customerNickName": "미래",
        "customerAddress": "정자동에서 사는 성공한 삶",
        "createdAt": "2022-11-21T00:00:00",
        "updatedAt": "2022-11-21T00:00:00"
      }
    ]
    

Customer 생성

  • URL : http://localhost:8080/customers
  • Method : POST
  • Body : application/json
    {
        "customerNickName": "미래",
        "customerAddress": "정자동에서 사는 성공한 삶"
    }
    

Order 조회

  • URL : http://localhost:8080/orders
  • Method : GET
  • Response
    [
        {
            "orderId": 2,
            "orderUUID": "014e018a-3391-4c54-a913-3230e65a0013",
            "orderStoreName": "얌얌김밥",
            "orderStoreAddress": "서울특별시 얌얌로 김밥동",
            "orderItem": "불고기참치김밥",
            "orderPrice": 6500,
            "customer": {
                "customerId": 1,
                "customerNickName": "올리버",
                "customerAddress": "서울특별시에서 살고싶음",
                "createdAt": "2022-11-20T00:00:00",
                "updatedAt": "2022-11-20T00:00:00"
            },
            "createdAt": "2022-11-21T00:00:00",
            "updatedAt": "2022-11-21T00:00:00"
        },
        {
            "orderId": 3,
            "orderUUID": "caf72de8-7174-48d9-9e06-d5665ca2225e",
            "orderStoreName": "얌얌김밥",
            "orderStoreAddress": "서울특별시 얌얌로 김밥동",
            "orderItem": "불고기참치김밥",
            "orderPrice": 6500,
            "customer": {
                "customerId": 2,
                "customerNickName": "미래",
                "customerAddress": "정자동에서 사는 성공한 삶",
                "createdAt": "2022-11-21T00:00:00",
                "updatedAt": "2022-11-21T00:00:00"
            },
            "createdAt": "2022-11-21T00:00:00",
            "updatedAt": "2022-11-21T00:00:00"
        }
    ]
    

Order 생성

  • URL : http://localhost:8080/orders
  • Method : POST
  • Body : application/json
    {
        "orderStoreName": "얌얌김밥",
        "orderStoreAddress": "서울특별시 얌얌로 김밥동",
        "orderItem": "불고기참치김밥",
        "orderPrice": 6500,
        "customerId": 2
    }
    

Tags

#Spring#Kotlin#SpringKotlin#Multimodule#gralde
Previous Article
스프링부트 멀티모듈 구성하기(2) - 코어모듈에 Entity 적용 해놓기
울이

울이

개발자 울이

경험하고 사용하고 개선하는 것, 그리고 소통하면서 성장하는 것을 좋아합니다.

Expertise

Backend
Frontend
Data Engineering

Social Media

githublinkedinwebsiteinstagram

Related Posts

ActiveMQ와 JMS를 사용한 SpringBoot 메시지
ActiveMQ와 JMS를 사용한 SpringBoot 메시지
December 24, 2022
1 min
© 2023, All Rights Reserved.

Quick Links

Home

Social Media