본문 바로가기
BackEnd/Project

[Loan] Ch03. 대출 신청 등록 기능 구현

by 개발 Blog 2024. 9. 11.

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

 

이번 장에서는 대출 신청을 등록하는 기능을 구현한다. 사용자가 신청 정보를 입력하면, 해당 정보를 데이터베이스에 저장하고 결과를 반환하는 과정을 다룬다.

 

1. ApplicationController

ApplicationController는 대출 신청 등록 요청을 처리하는 역할을 한다. 클라이언트에서 전달된 신청 정보를 @PostMapping을 통해 ApplicationService로 전달하고, 처리 결과를 응답으로 반환한다.

package com.example.loan.controller;

import com.example.loan.dto.ApplicationDTO;
import com.example.loan.dto.ResponseDTO;
import com.example.loan.service.ApplicationService;
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.ApplicationDTO.*;

@RequiredArgsConstructor
@RestController
@RequestMapping("/applications")
public class ApplicationController extends AbstractController {

    private final ApplicationService applicationService;

    @PostMapping
    public ResponseDTO<Response> create(@RequestBody Request request) {
        return ok(applicationService.create(request));
    }
}
  • create 메서드
    이 메서드는 RequestBody로부터 대출 신청 정보를 받아, applicationService.create() 메서드를 호출하여 신청 정보를 처리한 후 결과를 반환한다.

2. ApplicationDTO

대출 신청 정보를 담는 DTO(Data Transfer Object) 클래스이다. Request와 Response로 구성되어 있으며, 대출 신청을 위한 데이터를 전송하거나 응답받을 때 사용된다.

package com.example.loan.dto;

import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

public class ApplicationDTO implements Serializable {

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

        private String name;

        private String cellPhone;

        private String email;

        private BigDecimal hopeAmount;
    }

    @NoArgsConstructor
    @AllArgsConstructor
    @Builder
    @Getter
    @Setter
    public static class Response {

        private Long applicationId;

        private String name;

        private String cellPhone;

        private String email;

        private BigDecimal hopeAmount;

        private LocalDateTime appliedAt;

        private LocalDateTime contractedAt;

        private LocalDateTime createdAt;

        private LocalDateTime updatedAt;
    }
}
  • Request 클래스
    신청자의 이름, 전화번호, 이메일, 희망 대출 금액을 포함한다.
  • Response 클래스
    신청 정보와 함께 신청 ID, 신청 일자, 약정 일자, 생성 및 수정 일자가 포함된다.

3. ApplicationRepository

ApplicationRepository는 JPA를 사용하여 Application 엔티티와 데이터베이스 간의 CRUD 작업을 처리한다. 대출 신청 정보를 영속화하기 위해 JpaRepository를 상속받아 사용한다.

package com.example.loan.repository;

import com.example.loan.domain.Application;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface ApplicationRepository extends JpaRepository<Application, Long> {

}

 

4. ApplicationService 및 ApplicationServiceImpl

ApplicationService는 대출 신청과 관련된 비즈니스 로직을 처리하는 인터페이스이며, ApplicationServiceImpl은 그 구현체이다. 이 서비스는 사용자의 요청 데이터를 처리하고, 저장된 결과를 응답으로 반환하는 역할을 한다.

 

ApplicationService 인터페이스

대출 신청과 관련된 주요 기능을 정의하며, create 메서드를 통해 대출 신청 정보를 생성하는 기능을 제공한다.

package com.example.loan.service;

import static com.example.loan.dto.ApplicationDTO.Request;
import static com.example.loan.dto.ApplicationDTO.Response;

public interface ApplicationService {

    Response create(Request request);
}
  • 대출 신청과 관련된 주요 기능을 정의하며, create 메서드를 통해 대출 신청 정보를 생성하는 기능을 제공한다.

ApplicationServiceImpl 클래스

ApplicationService의 실제 구현체로, 대출 신청 정보를 데이터베이스에 저장하는 로직을 처리한다. ModelMapper를 사용해 요청 데이터를 Application 엔티티로 변환한 후, 현재 시각을 신청일자로 설정하고, ApplicationRepository를 통해 데이터를 저장한다. 저장된 Application 엔티티는 다시 Response 객체로 변환되어 응답으로 반환된다.

package com.example.loan.service;

import com.example.loan.domain.Application;
import com.example.loan.repository.ApplicationRepository;
import lombok.RequiredArgsConstructor;
import org.modelmapper.ModelMapper;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;

import static com.example.loan.dto.ApplicationDTO.Request;
import static com.example.loan.dto.ApplicationDTO.Response;

@Service
@RequiredArgsConstructor
public class ApplicationServiceImpl implements ApplicationService{

    private final ApplicationRepository applicationRepository;
    private final ModelMapper modelMapper;

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

        Application applied = applicationRepository.save(application);

        return modelMapper.map(applied, Response.class);
    }
}
  • 요청 처리: 사용자의 요청 데이터를 받아서 Application 엔티티로 변환한다.
  • 신청일자 설정: 신청일자(appliedAt)를 현재 시간으로 설정한다.
  • 데이터 저장: 변환된 Application 엔티티를 데이터베이스에 저장한다.
  • 응답 반환: 저장된 데이터를 Response 객체로 변환하여 클라이언트에게 반환한다.

5. ApplicationServiceTest

대출 신청 등록 기능에 대한 테스트 코드이다. Mockito를 사용하여 ApplicationRepository와 ModelMapper의 동작을 모의하고, 대출 신청 정보가 정상적으로 처리되는지 검증한다.

package com.example.loan.service;

import com.example.loan.domain.Application;
import com.example.loan.repository.ApplicationRepository;
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 java.math.BigDecimal;

import static com.example.loan.dto.ApplicationDTO.Request;
import static com.example.loan.dto.ApplicationDTO.Response;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class ApplicationServiceTest {

    @InjectMocks
    ApplicationServiceImpl applicationService;

    @Mock
    private ApplicationRepository applicationRepository;

    @Spy
    private ModelMapper modelMapper;

    @Test
    void Should_ReturnResponseOfNewApplicationEntity_when_RequestCreateApplication() {
        Application entity = Application.builder()
                .name("Member Kim")
                .cellPhone("010-1111-2222")
                .email("email@gmail.com")
                .hopeAmount(BigDecimal.valueOf(50000000))
                .build();

        Request request = Request.builder()
                .name("Member Kim")
                .cellPhone("010-1111-2222")
                .email("email@gmail.com")
                .hopeAmount(BigDecimal.valueOf(50000000))
                .build();

        when(applicationRepository.save(ArgumentMatchers.any(Application.class))).thenReturn(entity);

        Response actual = applicationService.create(request);

        assertThat(actual.getHopeAmount()).isSameAs(entity.getHopeAmount());
        assertThat(actual.getName()).isSameAs(entity.getName());
    }

}
  • 테스트 시나리오
    새로운 대출 신청 정보를 입력받았을 때, 요청 데이터와 저장된 데이터가 동일한지 확인하는 테스트를 진행한다. assertThat을 사용하여 희망 대출 금액과 신청자의 이름이 일치하는지 검증한다.

6. 실행 테스트

실제로 API를 통해 대출 신청 정보를 POST 요청으로 전송하고, 응답 데이터를 확인하여 구현한 로직이 정상적으로 동작하는지 검증했다. 사용자가 입력한 이름, 전화번호, 이메일, 희망 대출 금액이 데이터베이스에 올바르게 저장되었고, 응답 데이터로 반환되었다.

  • 신청 등록: 사용자가 이름, 전화번호, 이메일, 희망 대출 금액을 입력하여 대출 신청을 완료할 수 있다.
  • 응답 확인: 신청 ID와 함께 신청 일자, 생성 일자, 수정 일자가 반환되며, 모든 값이 정상적으로 처리되었음을 확인할 수 있었다.

이렇게 비즈니스 로직을 구현하고, 해당 로직에 대한 테스트 코드를 작성하여 검증한 후, 실제로 애플리케이션을 실행시켜 API가 정상적으로 동작하는지 확인했다. 이 과정을 통해 대출 신청 등록 기능이 기대한 대로 작동하는 것을 확인할 수 있었다.