Spring/API

회원 조회 API 개발 / Result 클래스로 유연한 JSON 반환

JWonK 2022. 6. 25. 19:16
728x90
반응형

회원 조회는 값을 가져와 화면에 보여주기만 하면 된다. 즉, 생성 / 수정 없이 조회만 하면 된다 -> REST API : GET Method 사용


 

회원 조회 V1 : 응답 값으로 엔티티를 직접 외부에 노출

@RestController
@RequiredArgsConstructor
public class MemberApiController {

    private final MemberService memberService;
    
    @GetMapping("/api/v1/members")	
	public List<Member> membersV1() {
		return memberService.findMembers();
	}

}

V1 코드는 매우 간단하다. memberService를 통해 가지고 있는 모든 엔티티를 리스트 형태로 반환해주는 코드이다. 하지만 이제는 딱 보면 알 수 있듯이 엔티티로 API를 조회하는 좋지 않은 코드이다. 당연히 DTO로 변환해서 받아주어야 한다.

 

  • 문제점
    • 엔티티에 프레젠테이션 계층을 위한 로직이 추가된다.
    • 기본적으로 엔티티의 모든 값이 노출된다.
    • 응답 스펙을 맞추기 위해 로직이 추가된다. (@JsonIgnore, 별도의 뷰 로직 등등)
    • 실무에서는 같은 엔티티에 대해 API가 용도에 따라 다양하게 만들어지는데, 한 엔티티에 각각의 API를 위한 프레젠테이션 응답 로직을 담기는 어렵다.
    • 엔티티가 변경되면 API 스펙이 변한다.
    • 추가로 컬렉션을 직접 반환하면 향후 API 스펙을 변경하기 어렵다. (별도의 Result 클래스 생성으로 해결)
  • 결론
    • API 응답 스펙에 맞추어 별도의 DTO를 반환한다.

 

※ 참고 : 엔티티를 외부에 노출하지 않는다.

실무에서는 member 엔티티의 데이터가 필요한 API가 계속 증가하게 된다. 어떤 API는 name 필드가 필요하지만, 어떤 API는 name필드가 필요없을 수 있다. 결론적으로 엔티티 대신에 API 스펙에 맞는 별도의 DTO를 생성하여 노출해야한다.

 

 

 

회원 조회 V2 : 응답 값으로 엔티티가 아닌 별도의 DTO 사용

@RestController
@RequiredArgsConstructor
public class MemberApiController {

    private final MemberService memberService;

    @GetMapping("/api/v2/members")
    public Result memberV2(){
        List<Member> members = memberService.findMembers();

        List<MemberDto> collect = members.stream()
                .map(m -> new MemberDto(m.getUsername()))
                .collect(Collectors.toList());

        return new Result(collect);
    }
    
    @Data
    @AllArgsConstructor
    static class Result<T>{
        private T data;
    }

    // 고객들의 이름의 반환해야한다고 가정
    @Data
    @AllArgsConstructor
    static class MemberDto{
        private String name;
    }
  • 엔티티를 DTO로 변환해서 반환한다.
  • 엔티티가 변해도 API 스펙이 변경되지 않는다.
  • 추가로 Result 클래스로 컬렉션을 감싸서 향후 필요한 필드를 추가할 수 있다.

 


 

※ Result 클래스로 컬렉션을 감싸는 이유는 Json 스펙에 맞춰 좀 더 유연하게 API를 반환하기 위함이다.

만약 감싸지 않을 경우에는 리스트 자체로 [ {Data1}, {Data2}, {Data3}, ,,,] 이런 식으로 반환된다. 만약 API가 요구하는 것이 Data 뿐만 아니라 Data의 수도 반환을 요청했을 경우 [ {Count}, {Data1}, {Data2}, {Data3} ,,] 이런 식으로 반환이 불가능하다. 리스트이기 때문에 Data와 Count객체가 동일해야한다. 

 

따라서, 좀 더 유연한 Json 객체 반환을 위해 { "count" : 3, "Data" : [ {Data1}, {Data2}, {Data3} ] } 이런 식으로 반환해주어야한다. 그렇기 때문에 Result 클래스로 원하는 Json형태로 만들어 준 뒤 반환해주는 것이다.

 

 

위 처럼 반환해주기 위한 코드

    @GetMapping("/api/v2/members")
    public Result memberV2(){
        List<Member> members = memberService.findMembers();

        List<MemberDto> collect = members.stream()
                .map(m -> new MemberDto(m.getUsername()))
                .collect(Collectors.toList());

        return new Result(collect.size(), collect);
    }
    
    @Data
    @AllArgsConstructor
    static class Result<T>{
    	private int count;
        private T data;
    }

이런 식으로 원하는 Json 형태로 만들어준 뒤 반환해주면 좀 더 유연하게 API 개발이 가능하다.

728x90
반응형