[수미수의 개발 브로구]

[WebFlux] Spring WebFlux API 서버 만들기 본문

Language & Framework/WebFlux

[WebFlux] Spring WebFlux API 서버 만들기

수미수 2023. 8. 27. 23:22
반응형

들어가기 전

  지난 글에서는 IntelliJ 를 이용하여 Spring WebFlux 프로젝트를 생성 하였습니다. 이번 글에서는 간단하게 Customer 라는 도메인에서 Customer 정보를 조회 하는 API 를 만들 예정입니다.

패키지 구성하기

  패키지 구성은 웹 어플리케이션 개발을 어떠한 아키텍처 패턴을 사용하여 개발 할 것인가에 따라 다양하게 구성할 수 있습니다. 해당 글에서는 가장 많이 사용되는 전통적인 레이어드 아키텍처 방식을 이용하여 패키지를 아래 그림과 같이 구성 하였으며, 각각의 기능은 다음과 같습니다. 

application

  일반적은 요구 사항에 대한 Use Case 를 구현한 클래스들로 구성하며, 흔히 말하는 Service 클래스들로 구성 합니다.

controller

  Presentation Layer 로 일반적으로 요청을 최초로 받는 EndPoint 를 정의한 클래스로 흔히 말하는 Controller 클래스들로 구성합니다.

domain

  속성과 행위를 관리하는 entity 클래스들로 구성 합니다. entity란 DDD 에서 정의하기를 "고유의 식별자를 갖는 객체로 자신의 라이프 사이플을 갖는다" 라고 정의 하고 있다.  이는 도메인 모델을 구현한 도메인 객체라 하며 속성(데이터)과 행위를 가지고 있는데, 예를 들어 사용자(User) 라는 도메인 객체를 생성했을 때, 속성은 username, password 가 속성 값이 될 수 있다. 그리고 도메인 내의 행위로는 "비밀번호 변경"과 같이 해당 도메인 고유의 행위를 가지게 되며, 도메인에 대한 비지니스 로직이 이에 속하게 된다.

dto

  Data Tansfer Object 로 레이어간 데이터들을 이동시키기 위해서 사용되는 클래스들로 구성합니다. 일반적으로 데이터베이스로 부터 나온 데이터를 전체 View로 응답 하지 않고 DTO 객체를 사용하는 것이 활용 예로 보면 됩니다.

infrastructure

  Infrastructure 패키지에는 기반 기술에 대한 클래스들로 구성 합니다. 예를 들면, DB 와 연동하거나 API 호출과 관련된 클래스들로 구성 합니다.

 

  대략적인 패키지 구성을 하고, 이제는 실제 API 를 만들도록 하겠습니다. 만들려는 API는 Customer 도메인에서 Customer 정보를 조회 하는 API를 만들 예정입니다. 먼저, Customer의 정보와 행위를 관리하는 Customer Entity를 만들고, 우리가 만들고자 하는 Customer 조회라는 Use Case 에 대한 Service 클래스를 만듭니다. 그리고, 이러한 Use Case 를 API 로 노출 하고 요청을 받을 수 있는 Controller 클래스를 만들도록 하겠습니다. 

  해당 예제에서는 InMemory 방식으로 Map 객체에 데이터를 저장하고, 해당 데이터를 조회 하는 형태로 구성 할 것입니다. 실제 데이터베이스와 연동은 R2DBC 를 이용한 글을 참고 바랍니다.

Entity 모델 만들기

  Customer Entity 는 주민등록 번호, 고객 명, 고객 아이디 정보를 가지도록 구성하였습니다. 해당 Entity 클래스는 실제 테이블 정보와 맵핑 하도록 구성하며, 비지니스 로직을 Entity 객체에 구현 합니다.

data class CustomerEntity(

    var residentRegistrationNumber: String,
    val customerName: String,
    val customerId: String,

    )
Infrastructure 클래스 만들기

  Infrastructure 패키지에는 실제 데이터를 어떻게 가져 올것인가에 대한 기반 기술로 구성한다고 하였습니다. 이러한 데이터는 DB를 통해서 가져올 수 있고, API 를 통해서 가져 올 수 있습니다. 

  우선 Repository 패턴을 이용하여 기본 Repository 를 만듭니다.

import com.example.springwebfluxdemo.customer.domain.CustomerEntity
import reactor.core.publisher.Flux

interface CustomerRepository {
    fun getCustomerList() : Flux<CustomerEntity>
}

  그리고, InMemory 기반 기술로 Map 으로 부터 Customer 리스트를 가져 오도록 Repository 구현체를 만듭니다. 이러한 Repository 인터페이스를 통해서 DBMS 또는 다른 외부로 부터 데이터를 가져오는 구현체를 만들 수 있습니다.

import com.example.springwebfluxdemo.customer.domain.CustomerEntity
import org.springframework.stereotype.Component
import reactor.core.publisher.Flux

@Component
class CustomerInMemoryRepository : CustomerRepository{
    override fun getCustomerList(): Flux<CustomerEntity> {
        val customerEntity1 = CustomerEntity("1234", "테스트1", "001")
        val customerEntity2 = CustomerEntity("5678", "테스트2", "002")
        val customerList = mutableListOf<CustomerEntity>(customerEntity1, customerEntity2)

        return Flux.fromIterable(customerList)
    }
}

  이제 인메모리 형태의 Map 에 하드 코딩한 정보를 요청 응답으로 사용 하도록 할 것입니다.

Service 클래스 만들기

  Serivce 클래스는 Use Case 를 정의하는 클래스이며, 이번 예제의 Use Case 는 "고객 전체 정보를 조회 한다" 입니다. 따라서 아래와 같이 Service 클래스를 만들고, InMemory 방식의 구현체를 주입하여, Customer 정보를 응답 하도록 합니다.

import com.example.springwebfluxdemo.customer.domain.CustomerEntity
import com.example.springwebfluxdemo.customer.infrastructure.CustomerRepository
import org.springframework.stereotype.Service
import reactor.core.publisher.Flux

@Service
class CustomerService(
    val customerInMemoryRepository: CustomerRepository
) {

    /**
     *  Customer 정보 조회
     */
    fun getCustomerList(): Flux<CustomerEntity> {

        return customerInMemoryRepository.getCustomerList()
    }
}
Controller 만들기

  이제, 고객 정보를 요청 하는 API 를 만들기 위해서, 최초 사용자 요청이 진입 하여 처리 하는 Controller 를 만들고 API URI 를 정의 할 것이다.

@RestController
class CustomerController(
    val customerService: CustomerService
) {

    @PostMapping("/customers")
    fun getCustomerDetail(): Flux<CustomerEntity> {
        return customerService.getCustomerList()
    }
}

테스트 

서버 기동 

서버를 구동하면, 아래와 같이 정상적으로 기동 됩니다.

API 테스트

  이제 API 클라이언트 툴인 PostMan 을 이용하여 API를 테스트 합니다.

  위 그림과 같이  Controller 에 정의한 API URI 로 요청 하니, InMemory 구현체에 하드코딩한 Map 정보가 응답으로 나오는 것을 확인 할 수 있습니다.

반응형