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

스프링부트 멀티모듈 구성하기(3) - API 모듈 작성하기
November 22, 2022
1 min
© 2022, All Rights Reserved.

Quick Links

Home

Social Media