Spring Boot http 테스트

강서진·2024년 1월 2일

4번째 필수 강좌 Part 1. ch3. 10, 11강 요약

POST

CreateDeveloper 클래스 내부에 Request와 Response의 static 멤버 클래스를 만들어준다. Request는 CreateDeveloper 객체를 입력받을 때, Response는 반환할 때 사용한다.
Response 객체 내부에 Response 객체를 반환하는 static 메서드 fromEntity를 가진다.

	@Getter
    @Setter
    @NoArgsConstructor
    @AllArgsConstructor
    @Builder
    @ToString
    public static class Response{
        private DeveloperLevel developerLevel;
        private DeveloperSkillType developerSkillType;
        private Integer experienceYears;
        private String memberId;

        public static Response fromEntity(Developer developer){
            return Response.builder()
                    .developerLevel(developer.getDeveloperLevel())
                    .developerSkillType(developer.getDeveloperSkillType())
                    .experienceYears(developer.getExperienceYears())
                    .memberId(developer.getMemberId())
                    .build();
        }

CreateDeveloper.Response 객체를 반환하도록 DeveloperService와 DeveloperController를 수정한다. 이를 포스트맨같은 외부 라이브러리를 사용하지 않고 테스트하려면, test 디렉토리 밑에 http 폴더를 만들고 내부에 dev.create.http 파일을 생성한다.
아래와 같이 적어서 Run 버튼을 누르면 요청이 보내진다.

POST https://localhost:8080/create-developer
Content-Type: application/json

{
"developerLevel":"JUNIOR",
"developerSkillType":"BACK_END",
"experienceYears":2,
"memberId": "testDeveloper",
"name":"Tester",
"age":25
}

다만 이 http 파일 형식은 IntelliJ Ultimate 버전에서만 지원한다. 나는 커뮤니티 버전을 사용하고 있어서 PostMan을 대신 사용했다.

PostMan에서 JSON을 넣어 보내려면 Body에서 raw를 선택하고 Text가 아닌 JSON을 선택하면 된다.

POST 메서드로 RequestBody에 CreateDeveloper.Request를 받으면, 먼저 Java Bean 검증을 통과한 후 validateCreateDeveloperRequest인 비즈니스 검증 과정을 거친다. 검증을 통과하면 CreateDeveloper.Response에 있는 fromEntity 메서드를 통해 Response 객체가 반환된다.

GET (전체)

그 다음으로, 문자열 리스트를 반환하도록 만들었던 GET 메서드로 맵핑된 developers를 저장된 Developer 객체를 반환하도록 수정한다. 이때, Developer 엔티티를 그대로 반환하기보다는 DTO를 반환하는 것이 좋다. 엔티티에는 출력하기에 불필요한 정보들이 포함되어있기도 하고, 트랜잭션이 제대로 없는 상태에서 정보에 접근하려고 하다보면 문제가 발생할 수 있기 때문이다.

정보를 저장하는 엔티티와 응답할 때 반환하는 객체는 분리해주는 것이 유연성이나 유지보수 면에서 좋다.

@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class DeveloperDTO {
    private DeveloperLevel developerLevel;
    private DeveloperSkillType developerSkillType;
    private String memberId;

    public static DeveloperDTO fromEntity(Developer developer){
        return DeveloperDTO.builder()
                .developerLevel(developer.getDeveloperLevel())
                .developerSkillType(developer.getDeveloperSkillType())
                .memberId(developer.getMemberId())
                .build();
    }
}

마찬가지로 DTO에도 DTO 객체를 반환하는 static 메서드를 만들어 주었다.

DeveloperController의 GET맵핑된 부분을 수정한다.

	@GetMapping("/developers")
    public List<DeveloperDTO> getAllDevelopers() {
        log.info("GET /developers HTTP/1.1");

        return dMakerService.getAllDevelopers();
    }

그리고 DMakerService에 getAllDevelopers()를 만들어준다.

	public List<DeveloperDTO> getAllDevelopers() {
        return developerRepository.findAll()
                .stream().map(DeveloperDTO::fromEntity)
                .collect(Collectors.toList());
    }

Developer 객체를 전부 받아오는데, 바로 entity 그대로 반환하지 않고 DeveloperDTO로 변환하는 것을 확인할 수 있다.
PostMan으로 developer를 등록하고, 테스트하여 제대로 반환하는 것을 확인하였다.

GET (하나만)

다음으로는 하나의 객체만을 반환하는 GET 맵핑된 메서드이다. 하나의 Developer 객체 정보를 반환하기 때문에 DeveloperDetailDTO를 새로 만들었다.

@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class DeveloperDetailDTO {
    private DeveloperLevel developerLevel;
    private DeveloperSkillType developerSkillType;
    private Integer experienceYears;
    private String memberId;
    private String name;
    private Integer age;

    public static DeveloperDetailDTO fromEntity(Developer developer){
        return DeveloperDetailDTO.builder()
                .developerLevel(developer.getDeveloperLevel())
                .developerSkillType(developer.getDeveloperSkillType())
                .experienceYears(developer.getExperienceYears())
                .memberId(developer.getMemberId())
                .name(developer.getName())
                .age(developer.getAge())
                .build();
    }
}

Developer 엔티티와 비슷하지만 DB id나 생성 날짜 등의 불필요한 정보는 제외하였다.
다음으로 컨트롤러에 path variable로 memberId를 받아오도록 맵핑하였다.

	@GetMapping("/developer/{memberId}")
    public DeveloperDetailDTO getAllDeveloperDetail(
            @PathVariable(name = "memberId") String memberId) {

        return dMakerService.getDeveloperDetail(memberId);
    }

DMakerService로 넘어가 getDeveloperDetail을 만들어준다.

	public DeveloperDetailDTO getDeveloperDetail(String memberId) {
        return developerRepository.findByMemberId(memberId)
                .map(DeveloperDetailDTO::fromEntity)
                .orElseThrow(() -> new DMakerException(DMakerErrorCode.NO_DEVELOPER));
    }

findByMemberId로 반환되는 객체는 Optional로, map 메서드를 적용할 수 있다. get()보다는 orElseThrow로, 객체가 있다면 자동으로 get을 적용하고, 만약 빈 Optional이라면 DMakerErrorCode를 throw하게 만들었다.

실행해보면 제대로 작동하는 것을 확인할 수 있다.

0개의 댓글