깽쟈
491
2016-05-23 17:49:06
5
6664

jpa를 이용하여 json 데이터를 가져올 때 지연로딩을 이쁘게 커버하는 방법?


안녕하세요. jpa 입문하여 열심히 공부중입니다.

스프링 4, 스프링-데이터-jpa, jackson-databind, 하이버네이트 사용중입니다.

@ResponseBody를 이용하여 json 데이터를 가져오려고 하는데, 문제가 있습니다. Entity 중 fetch=FetchType.LAZY 로 설정해놓은 것들은 jackson-databind가 변환할 때 proxy를 못 찾는 오류가 발생합니다. 그래서 일단 json으로 변환하길 원하는 객체를 jpa로 불러와서 아무 의미 없는 명령을 하나 써넣는데요. 예를 들어

@Entity
public class Scene
{
    @Id
    private long id;

    private String sceneName;

    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="work_id")
    private Work work;
}

위의 Entity를 가져오려고 할 때 다음과 같이 서비스 클래스를 만듭니다.

@Service
public class SceneServiceImpl implements SceneService
{
    public Scene getScene() {
        Scene scene = sceneRepository.findOne(1);
        Object temp = scene.getWork();
        return scene;
    }
}

return 바로 직전에 Object temp = scene.getWorks(); 이 부분인데요 ;;; (공부하는 사람이니 귀엽게 봐주세요)

저렇게라도 하지않으면 Lazy 로딩되어있는 것들이 불러와지지 않으니까 json이 계속해서 proxy 에러를 쏟아냅니다. 그렇다고, 기본 지연로딩 전략을 EAGER로 하지는 않을 생각입니다. 필요할 때에만 쓰여서요.

이런 경우 jpa를 사용하시는 분들은 보통 어떻게 처리하시나요? JPQL(QueryDSL)로 하시나요? 세련된 방법 부탁드립니다.

1
  • 답변 5

  • 제타건담
    7k
    2016-05-23 19:33:26

    저도 공부하는 입장이지만 한말씀 드리면..

    저 같은 경우는 이번에 JPA 책 내신 김영한님과 메일 주고 받으며 결론을 내린건데요..저도 현업적으로는 써본 경험이 없어서..

    그분 얘기로는..

    엔티티와 DTO(또는 VO)를 별개로 해서 작업한다였습니다..간단한거는 엔티티를 그대로 전송한다고는 하지만..

    엔티티가 개발하는 과정에 있어서 변경되는 상황이 많다보니 엔티티가 변경되면 관련된 부분들이 같이 변경이 되는 곳이 많아서..

    그래서 저 같은 경우는 Repository에서는 엔티티를 return 하고 Service 에서는 DTO를 return 하는 식으로 해서 컨셉을 잡았습니다..물론 변환하는 엔티티->DTO로 변환해주는 Converter도 만들어야겠죠(근데 하다보니 그 반대, 즉 DTO->엔티티 로 변환하는 Converter도 만들어야겠더군요..왜냐면 Repository에서는 엔티티로 받아서 처리하게끔 설계하다보니..)

    암튼 그렇게 하고 본 질문에 속하는 Lazy 같은 경우는 fetch join을 사용했습니다..fetch join이라고 한 이유는 이걸 구현하는 방법이 JPQL이나 QueryDSL로 가능하기 때문에 그런거고..저 같은 경우는 QueryDSL로 했습니다..

    정리하자면..

    1. Fetch Join으로 엔티티 조회

    2. 엔티티 -> DTO로 변환하는 컨버터를 이용해 DTO로 객체 변환

    3. 변환된 DTO를 Json Mapping으로 사용

    이렇게 되겠습니다..

  • lazer
    1k
    2016-05-23 23:15:52

    제타건담님 //

    converter 를 구현하신다는게 entity get -> dto set 을 자동으로 해주는 역할을 말씀하시는건지

    entity -> dto class 자동 선언을 말씀하시는건지 궁금합니다.


  • 제타건담
    7k
    2016-05-23 23:51:16

    Spring MVC에서 보시면 타입간의 변환과 관련된 Converter 를 등록하는 부분이 있습니다..

    Spring에서는 Converter 인터페이스를 제공하는데 이 인터페이스를 구현한 클래스를 만들어서 Converter 클래스를 만들고 이것을 spring bean으로 등록해서 사용합니다..

    저 같은 경우는 Service bean에서 Converter 인터페이스를 구현한 클래스를 @Autowired로 Injection 받아서 엔티티를 DTO로 변환한뒤 DTO를 return 하게끔 했습니다..

  • 깽쟈
    491
    2016-05-25 08:06:32

    답변 감사합니다.

    spring-data-jpa를 이번에 사용해보고 있는데요. 그것으로  JPQL이나 QueryDSL을 최소화 할 수 있을꺼라 생각되었는데, 실제로는 JPQL(혹은 QueryDSL)에 꽤 많이 의존하게 된다는 생각이 듭니다. Mybatis에서 JPA로 넘어오면서 (아직 완벽하게 넘어온 것은 아니고, 현재는 둘을 같이 사용중입니다. 컨버팅하면서 공부를 하고 있습니다) SQL 스러운 문법을 최대한 안 쓰게 되는 것 같아 좋아했었습니다만... 이제는 약간 혼란스럽습니다.

    엔티티의 대부분을 LAZY로 설정할 필요가 있고, 그것들을 써야할 때는 결국 글로벌 페치 전략인 것이군요. 저도 그 책 보면서 공부하고 있습니다만 뭔가 다른 응용방법은 없나 했는데, 살짝 김이 빠진 느낌이 드네요 ㅎㅎ

    DTO를 사용하지 않고 엔티티로만 만들려고 했던 것 자체가 욕심이었나봅니다.

  • 제타건담
    7k
    2016-05-25 14:20:55

    저는 그렇게 부정적으로까지 보지는 않습니다..제가 봤을때 깽쟈님이 혼란을 느끼시는 이유는..

    내가 테이블을 조회한다고 생각하는 개념에서 JPA를 접근해서 그런게 아닐까..하고 조심스럽게 추측해봅니다..

    테이블을 조회하는게 하니라 클래스를 조회한다고 생각해보세요..

    LAZY로 설정해야 하는 이유는 퍼포먼스측면에서 그런거지..만약 퍼포먼스 측면을 고려할 필요가 없다면..

    즉 퍼포먼스를 고려한 코딩이나 그렇지 않은 코딩이나 실행 속도 및 메모리 차지하는 것이 심히 차이가 나는게 아니라면 JPQL이나 QueryDSL을 사용할 필요는 없습니다..걍 EntityManager의 find를 써도 된다는거죠..

    그리고 엔티티와 DTO 분리에 대해 한마디 더 말씀드리면..사실 클래스에서 엔티티로 바로 넘겨도 상관없는 그런 성격의 것도 있습니다..

    그러나 참조가 다단계로 이루어지는 그런 상황 예를 들면 엔티티의 참조관계가 A->B->C->A 이런식으로 클래스 설계가 진행이 된 상황에서..

    이 엔티티 객체를 그대로 사용하게 되면 무한루프 식의 참조가 발생합니다..

    엔티티만 놓고 보자면 이런 설계는 나쁜 설계라고 말할수는 없습니다..그러나 사용용도가 엔티티가 아닌 DTO로 이용하게 된다면 문제가 발생하는 거죠..

    그래서 엔티티와 DTO의 분리를 얘기하는 것도 있습니다..흔히 이런 상황이 발생하는 것중에 대표적인 것이 엔티티를 return해서 이를 json으로 표현할때 무한루프식의 참조가 발생하는 거죠..

    물론 엔티티에 @JSONIGNORE(이 어노테이션이 맞나는 몰겠네요)를 붙여서 특정 필드는 JSON 변환에서 제외시킬수도 있습니다..그러나 이렇게 진행할 경우 엔티티라는 성격의 클래스에 엔티티와 무관한 성격의 어노테이션이 붙는데다가..이런저런 이유로 어노테이션이 늘어나게 되면 어노테이션만 덕지덕지 붙은 더러운(?)코드가 됩니다..

    그래서 역할에 따라 분리하는 그런게 필요한거죠..개인적으로는 JPA 공부하며 만족하고 있습니다..가장 좋은 것은 local 로 개발할때 HSQL로 메모리 DB 사용해서 테스트하며 만든 코드들을 ORACLE에서 사용시 별도의 수정없이 이용하는 부분이 가장 메리트더군요..

    글치만..통계성 쿼리의 경우는 DB 고유의 쿼리를 이용하는게 더 낫습니다..DB 벤더에서 상황에 맞는 함수들을 제공해주기 때문에 그런것들을 이용하는게 훨씬 더 나으니까요..

  • 로그인을 하시면 답변을 등록할 수 있습니다.