Dev/Springboot

SpringBoot + JPA 환경에서 RestAPI 생성시 고려해야할 것들

린네의 2024. 1. 29. 19:33

 

회사에서 API 를 직접 생성해서 제공하고, 외부 API 를 가져와서  데이터를 파싱하는건 아주 흔한일이다.

처음에 지식이 없는 상태로 구축을 진행하게 되면, 유지보수 때 아주 힘들 수 있으므로 처음 개발을 진행할 때 항상 염두에 두어야 하는것들을 정리했다.   

 

 

1. DTO 를 따로 만들자 

Entity 는 여러군데에서 쓰기 때문에 변화가 많다. 따라서 외부에서 쓸 때 변화가 없는 request / response dto 를 따로 만들어서 작업하도록 하자.  실제로 유지보수할 때, 응답에 포함되는 responseBody 항목이 바뀌면 관련된 가이드 문서부터 시작해서 내가 제공한 사이트에서 나는 오류발생시 대응등.... 불편한점이 한두가지가 아니다.  그러므로 반드시 규격화된 DTO 를 생성하여 Entity 가 변경되더라도 응답규격은 일정하게 유지해야한다.

 

 

2.  @JsonIgnore 을 사용하면 사용되는 필드는 노출 되지 않는다.

@JsonIgnore 을 사용하면 내가 노출을 원하지 않는 필드를 설정할 수 있다.

 

 

3.  컬렉션 리턴시  규격화된 Result 를 생성해서 사용하자

  단순한 엔티티 반환시에는 크게 문제가 없지만, 반환값에 컬렉션이 포함될 경우 기본 json 형식이 깨지는 문제가 있을 수 있다.  리턴시  리턴할 규격을 정한 DTO 를 선언하고  감싸서 보내게 되면 이런 문제를 해결 할 수 있다.

 

예를 들면 다음과 같이 구현할 수 있다. 

    @GetMapping("/api/v2/members")
    public Result memberV2() {
        List<Member> findMembers = memberService.findMembers();
        Collection<Object> collect = findMembers.stream().map( m -> new MemberDTO(m.getName()))
                .collect(Collectors.toList());

        return new Result(collect.size(), collect);
    }


    @Data
    @AllArgsConstructor
    static class Result<T> {
        private int count;
        private T data;
    }



4. 양방향 연관관계가 있을 경우, 한쪽에 대해서는 @JsonIgnore 를 추가해주도록 하자 
  

 양방향 연관관계에서 주의해야할 점이 바로 무한루프의 발생가능성이다. ( 연관관계에 무한루프에 대해서는 다음 포스팅을 참고 )   따라서 양방향 연관관계가 있을 경우 따라서 한쪽과의 연관관계를 끊어줘야하기 때문에 @JsonIgnore 을 사용하여 노출되지 않도록 하는것이 필요하다.

 



5.  지연 로딩 문제를 해결 할 때는 Hibernate5Module 를 사용하자

 지연로딩을 사용할 경우,  JSON 형식으로 리턴시 문제가 생길 수 있다.   지연로딩설정을 해두면,  연관관계가 있는 객체에 대해 진짜 엔티티 객체가아닌  프록시 객체 상태로 호출이 되어 InvalidDefinitionException 을 발생 시킨다.

 

이럴 때 Hibernate5Module 를 사용하면 문제를 해결할 수 있다.

사용방법은 간단한데, build.gradle 파일에 아래 내용을 추가해주면 된다. 그러면 프록시객체는 제외하고 리턴하게 되어 InvalidDefinitionException 이 발생하지 않는다.

 

implementation 'com.fasterxml.jackson.datatype:jackson-datatype-hibernate5-jakarta'

 

 

 

6.  repository 에서  DTO 로 할지 Entity 로 할지에 대한 문제 결정 

 

레파지토리에서 바로 DTO 로 받아올지, 우선 Entity 로 받아올지는 사실 개발자의 선택이다.  성능차이는 크지 않기 때문에,  어떻게 받아오던지보다 서비스단에서 DTO 로 변환하여 결과적으로 리턴되는 객체에 대해서 DTO 로 구성하는것에 초점을 맞추는게 좋다.
 ~ 결과적으로는 사실 레파지토리에선 엔티티 받아온다음에 서비스단에서 디티오로 바꿔주는게 일순위임 


7.  컬렉션을 DTO 로 반환할 경우 컬렉션에 연관된 엔티티의 의존도를 제거하자

 

만약 A 라는 엔티티 안에 B 엔티티가 리스트형태로 들어가 있다면, 리턴시  B 객체 자체도 DTO 로 변경해줘야 한다.

즉 리턴되는 DTO 의 형태는 'A 엔티티에 대한 response DTO'  안에 ' B 엔티티 DTO 에대한 리스트' 가 포함되어야 한다.

 

class ADto {

	List<BDto> blist; 
    
    ...
}

 



 

 

 

참고 및 출처  :  실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화