본문 바로가기
BackEnd/Project

[Loan] Ch02. 대출 상담 등록 기능 구현

by 개발 Blog 2024. 9. 11.

공부 내용을 정리하고 앞으로의 학습에 이해를 돕기 위해 작성합니다.

이전 시간에 정의한 Counsel 엔티티를 기반으로 상담 등록 기능을 구현한다. 이 기능에서는 사용자가 상담을 신청할 때의 데이터를 받아 데이터베이스에 저장하고, 저장된 데이터를 반환하는 방식으로 설계된다.

1. 대출 상담 등록 API 구현

우선 상담 등록을 위한 API를 만들기 위해 CounselController 클래스를 구현한다. 이 컨트롤러는 클라이언트로부터 POST 요청을 받아 상담 데이터를 처리한다.

 

CounselController

CounselController는 상담 등록을 위한 클라이언트 요청을 처리하는 컨트롤러로, 클라이언트로부터 POST 요청을 받아 서비스 계층에 전달한다. 서비스로부터 처리된 결과를 ResponseDTO 형태로 반환하는 역할을 한다.

package com.example.loan.controller;


import com.example.loan.dto.CounselDTO;
import com.example.loan.dto.ResponseDTO;
import com.example.loan.service.CounselServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import static com.example.loan.dto.CounselDTO.*;

@RequiredArgsConstructor
@RestController
@RequestMapping("/counsels")
public class CounselController extends AbstractController{
    private final CounselServiceImpl counselService;

    @PostMapping
    public ResponseDTO<Response> create (@RequestBody Request request) {
        return ok(counselService.create(request));
    }
}
  • 클라이언트가 /counsels 경로로 POST 요청을 보낸다.
  • 요청 본문에 담긴 상담 데이터를 CounselDTO.Request 객체로 변환한다.
  • create() 메서드를 호출하여 CounselServiceImpl로 요청 데이터를 전달한다.
  • 서비스 계층에서 처리된 결과(CounselDTO.Response)를 받아 응답으로 반환한다.

2. CounselDTO - 요청 및 응답 객체

CounselDTO는 상담 등록과 관련된 데이터를 주고받기 위한 DTO 객체이다. Request 객체는 클라이언트가 보낸 상담 데이터를 담고, Response 객체는 처리된 결과를 응답으로 전달하는 역할을 한다.

 

CounselDto

package com.example.loan.dto;

import lombok.*;

import java.time.LocalDateTime;

public class CounselDTO {

    @NoArgsConstructor
    @AllArgsConstructor
    @Builder
    @Getter
    public static class Request {

        private Long counselId;

        private String name;

        private String cellPhone;

        private String email;

        private String memo;

        private String address;

        private String addressDetail;

        private String zipCode;

        private LocalDateTime appliedAt;

        private LocalDateTime createAt;

        private LocalDateTime updateAt;
    }

    @NoArgsConstructor
    @AllArgsConstructor
    @Builder
    @Getter
    @Setter
    public static class Response{
        private Long counselId;

        private String name;

        private String cellPhone;

        private String email;

        private String memo;

        private String address;

        private String addressDetail;

        private String zipCode;

        private LocalDateTime appliedAt;

        private LocalDateTime createdAt;

        private LocalDateTime updatedAt;
    }
}
  • 클라이언트가 보낸 상담 요청 데이터를 Request 객체로 변환한다.
  • 저장된 상담 데이터를 Response 객체로 변환하여 응답한다.

3. Service 계층 구현

CounselService는 상담 등록을 위한 비즈니스 로직을 정의하는 인터페이스이다. create() 메서드를 통해 상담 데이터를 처리한다.

 

CounselService

package com.example.loan.service;

import com.example.loan.dto.CounselDTO;
import com.example.loan.dto.CounselDTO.Response;

public interface CounselService {
    Response create(CounselDTO.Request request);

}
  • create() 메서드를 통해 상담 데이터를 처리하고 응답 객체로 변환한다.

CounselServiceImpl

CounselServiceImpl은 CounselService 인터페이스의 구현체로, 상담 데이터를 실제로 처리하여 데이터베이스에 저장하는 역할을 한다. ModelMapper를 사용해 DTO와 엔티티 간 변환을 수행한다.

package com.example.loan.service;

import com.example.loan.domain.Counsel;
import com.example.loan.dto.CounselDTO.Response;
import com.example.loan.repository.CounselRepository;
import lombok.RequiredArgsConstructor;
import org.modelmapper.ModelMapper;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;

import static com.example.loan.dto.CounselDTO.Request;

@Service
@RequiredArgsConstructor
public class CounselServiceImpl implements CounselService {

    private final CounselRepository counselRepository;

    private final ModelMapper modelMapper;

    @Override
    public Response create(Request request){
        Counsel counsel = modelMapper.map(request, Counsel.class);
        counsel.setAppliedAt(LocalDateTime.now());

        Counsel created = counselRepository.save(counsel);

        return modelMapper.map(created, Response.class);
    }
}
  • create() 메서드를 호출하여 CounselDTO.Request를 Counsel 엔티티로 변환한다.
  • 변환된 Counsel 엔티티를 CounselRepository를 통해 저장한다.
  • 저장된 데이터를 CounselDTO.Response로 변환하여 반환한다.

ModelMapper는 DTO와 엔티티 간 변환을 편리하게 처리해 준다. 이를 통해 불필요한 변환 로직을 줄이고, 코드의 가독성과 유지보수성을 높일 수 있다.

4. Repository 계층

CounselRepository는 JPA를 사용하여 상담 데이터를 데이터베이스에 저장하고 관리하는 역할을 한다. JpaRepository를 상속받아 기본적인 CRUD 기능을 제공한다.

 

CounselRepository

package com.example.loan.repository;

import com.example.loan.domain.Counsel;
import org.springframework.data.jpa.repository.JpaRepository;

public interface CounselRepository extends JpaRepository<Counsel, Long> {

}
  • Counsel 엔티티를 데이터베이스에 저장하거나 조회한다.

5. CounselServiceTest

CounselServiceTest는 서비스 계층의 기능을 검증하기 위한 단위 테스트 클래스이다. Mockito를 사용하여 외부 의존성을 모킹(mocking)하고, 서비스 로직이 올바르게 동작하는지 확인한다.

package com.example.loan.service;

import com.example.loan.domain.Counsel;
import com.example.loan.dto.CounselDTO.Request;
import com.example.loan.dto.CounselDTO.Response;
import com.example.loan.repository.CounselRepository;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentMatchers;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import org.modelmapper.ModelMapper;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class CounselServiceTest {

    @InjectMocks
    CounselServiceImpl counselService;

    @Mock
    private CounselRepository counselRepository;

    @Spy
    private ModelMapper modelMapper;

    @Test
    void Should_ReturnResponseOfNewCounselEntity_When_RequestCounsel() {
        Counsel entity = Counsel.builder()
                .name("Member Kim")
                .cellphone("010-1111-2222")
                .email("abc@def.g")
                .memo("대출 받고싶어요")
                .zipCode("12345")
                .address("서울특별시 강남")
                .addressDetail("101동 101호")
                .build();

        Request request = Request.builder()
                .name("Member Kim")
                .cellphone("010-1111-2222")
                .email("abc@def.g")
                .memo("대출 받고싶어요")
                .zipCode("12345")
                .address("서울특별시 강남")
                .addressDetail("101동 101호")
                .build();

        when(counselRepository.save(ArgumentMatchers.any(Counsel.class))).thenReturn(entity);

        Response actual = counselService.create(request);

        assertThat(actual.getName()).isSameAs(entity.getName());
    }
}
  • CounselRepository와 ModelMapper를 모킹하여 테스트 환경을 구성한다.
  • create() 메서드의 입력과 출력을 테스트하여 상담 등록 로직이 올바르게 동작하는지 확인한다.

6. 실행 테스트

상담 등록 기능이 정상적으로 동작하는지 확인하기 위해 포스트맨(Postman)을 사용하여 API 테스트를 진행한다.

 

1. POST 요청

먼저, 포스트맨을 통해 /counsels 경로로 POST 요청을 보낸다. 요청 본문에는 상담 데이터를 JSON 형식으로 포함한다.

  • 응답 결과에서 counselId, name, email, address 등 입력한 데이터가 정상적으로 저장되었고, appliedAt, createdAt, updatedAt 등의 타임스탬프가 자동으로 추가된 것을 확인할 수 있다.

2. SQL 삽입 확인

삽입된 데이터는 다음과 같은 SQL 쿼리를 통해 데이터베이스에 저장된다.

 

이로써 상담 등록 기능이 정상적으로 구현되었음을 확인할 수 있다. 클라이언트의 요청은 서비스 계층을 통해 데이터베이스에 저장되고, 그 결과는 클라이언트에게 응답된다.