ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • spring-data-jpa + querydsl 로 개발하기 - 1
    Back-end 2016. 6. 8. 00:31
    728x90
    소프트웨어 개발이라는것은 퍼즐 맞추기나 블럭쌓기 보다는 회화 그리기나 조각하기에 더 가깝다고 할 수 있다. 안개가 겉히듯 서서히 선명해지는 것이 소프트웨어 개발의 특성이라고 본다. 




    spring에서 개발되는 제품들은 소프트웨어 개발의 이런 특성을 잘 고려하여 설계가 되어 있다. spring을 이용하여 프로그램을 개발할때 이런 점진적, 반복적 개발이 가능하도록 지원하고 있다. 

    제로 컨피규레이션이라고 하여 모든 상황에 대해서 미리 지정된 기본동작으로 작동하며, 개발자자 추가설정을 해나가면서 개별 프로젝트가 원하는 요구사항에 맞춰가게 되는 방식이다. 비교적 최근에 추가된 spring-boot를 통해 빠른 개발이 완성되어가는 모습이다.


    사용하는 기술 간단 소개


    JPA (java persistence api)

    persistence (영속)라는 단어는 데이터를 영구저장한다는 의미로 사용하며 프로그램이 종료된 후에도 계속 유지되도록 저장하는 것을 영속화라고 한다. 쉽게 말하면 데이터베이스에 데이터를 저장하는것을 말한다.

    JPA라는것은 이런 데이터 영속화를 관리해주는 표준 API를 말하는것으로 java ee의 스팩이다. 다만 어려가지 상황상 JPA = hibernate 라고 봐도 된다. 참고로 JPA는 RDBMS에만 적용되는 규격이다.

    참고로 이글은 JPA스팩자체에 대한 가이드는 아니다. JPA의 OR매핑에 대한 지식은 어느정도 알고 있는 상태에서 spring 방식으로 저장소(repository)를 개발하는 방법에 대한 글이다.


    spring-boot (http://projects.spring.io/spring-boot)

    springframework는 상당히 유연하게 설계가 되어 있다. 다르게 말하면 한가지 문제를 해결하기 위해서 너무 다양한 옵션을 제공한다. 이런 방식은 실제프로젝트를 진행할때 다양하게 변화하는 요구사항에 대응하기에는 좋을수도 있지만 다양하게 지원하는 옵션사항중에 내가 어떤것을 사용할지 결정하기위해 어느정도 학습의 기간이 필요해지고 대부분의 프로젝트가 결국에는 다양한 옵션중에 실제적으로 사용하기 쉽고 직관적인 몇가지 형태로 굳어지게 된다.

    spring-boot는 여기서 부터 출발한다고 볼 수 있다. spring-boot는 springframework의 각 서브 프로젝트들이 일반적으로 사용되는 페턴들에 맞게 미리 설정(config)된 상태로 프로젝트 개발을 시작할 수 있게 해준다. 또한 개발자의 추가적인 설정을 통해 얼마든지 spring의  다양한 옵션사용과 확장등이 가능하다.


    spring-data-jpa (http://projects.spring.io/spring-data-jpa)

    spring-data 프로젝트는 최근 나와 있는 다양한 데이터 관리 솔루션(mysql, mongodb, radis ...)을 동일한 방식의 인터페이스로 사용할 수 있게 해주는 프로젝트이다. 이렇게 되면 한번만 배우면 여러 데이터 관리 솔루션을 같은 방식으로 다룰 수 있게 되어 학습비용이 줄어들기를 기대한다고 볼수 있다. (물론 해당 데이터 솔루션을 전혀 모르는 상태에서 spring-data가 모든걸 해결해주진 않는다.) 그중spring-data-jpa는 spring-data의 인터페이스로 JPA 를 다룰 수 있게 해주는 프로젝트 이다.




    queryDsl (http://www.querydsl.com)

    각종 데이터베이스나 검색엔진이 모두 자기만의 데이터 검색을 위한 쿼리 문법을 가지고 있다. 이렇게 다양한 각 데이터 쿼리 솔류션들의 쿼리 문법을 모두 익히는 것은 쉽지 않다. queryDsl은 몇가지 데이터관리 엔진에 대해서 하나의 통일된 문법으로 쿼리를 작성할 수 있게 해준다. 그리고 여기에 더해서 중요한 특징은 쿼리작성을 java 코드로 하며 type safe 하게 작성할 수 있다는 점이다.

    ~batis를 사용하는 개발자에게는 자바로 쿼리를 작성한다는것이 익숙하지 안음을 넘어서 불쾌하게 느껴지기 까지 할 수 있겠지만 빠른 개발과 쉬운 디버깅을 통해 개발자가 일찍 퇴근할 수 있는것보다  중요한 미덕은 없을것이다.



    위와 같이 기존의 일반 SQL도 queryDsl을 통해 생성할 수 있다.



    Type safe (형식안전성)

    JPA도 java코드로 쿼리를 작성하는 criteria를 지원을 한다. 다만 필자가 queryDsl을 도입하게 된 이유는 바로 type safe라는 개념때문이다. 기존 솔루션은 쿼리 문자열을 string 으로 작성해서 orm 엔진으로 넘기는 방식인데 여기에는 큰 문제가 있다. 쿼리 문자열에 오타나 잘못 작성된 부분이 있는지 확인하기가 어렵다는 것이다. 프로그램을 실행후 해당 쿼리가 실행되는 로직이 돌아가기 전에는 쿼리에 잘못이 있는지 확인이 어렵다. 또 만약 데이터 베이스의 스키마가 변경된다면 프로그램 안에서 그 부분과 관련된 쿼리 문자열이 모두 수정되어야 한다. JPA의 criretia도 필드이름을 string으로 넘기는 방식이라 type safe한 방식이 아니다. 또한 문법이 꽤 복잡하다.


    소프트웨어의 요구사항은 계속해서 바뀌고 개발도중에는 데이터베이스 스키마도 수시로 변경될 가능성이 있다. 그때마다 관련 쿼리가 문제 없는지 확인하는 과정은 지루하고 실수하기 쉽다. queryDsl을 사용한다면 Entity 도메인 객체에 수정이 일어나면 관련된 쿼리 코드에 컴파일 에러가 발생하게 된다. 컴파일 에러가 발생하는 코드를 수정하고 컴파일 에러가 없어진다면 적어도  소프트웨어 동작중에 쿼리가 잘못되어 에러가 발생하는 경우는 없다고 봐도 된다. 이런걸 Type safe 하다고 한다. 여기서 얻은 안정감으로 더욱 과감하게 적극적으로 소프트웨어의 품질 향상을 위한 수정이 가능하게 된다. 단위 테스트 코드를 작성하는 것과 비슷한 원리이다.




    구현

    이제부터는 간단한 샘플 데이터를 통해 실제로 spring-data-jpa를 사용하여 점진적, 반복적으로 개발 해나가는 과정을 소개 한다.


    샘플 소스코드

    아래 필자의 개인 저장소에 이글에서 소개하는 샘플 프로젝트 소스를 볼 수 있다.

    https://github.com/adrenalinee/sample/tree/master/sample-spring-data-jpa




    샘플 도메인 엔티티 객체



    위와 같은 관계의 Entity 를 가지고 spring-data-jpa 사용법을 몇단계로 나눠서 소개 한다. 

    추가로 몇가지 REST API를 개발한다고 생각하고 어떻게 애자일적인 방식으로 접근해나갈지에 각 단계별로 생각해보기로 한다.


    domain entity java code









    0단계 - 설정

    String 설정

    @Configuration class 에 @EnableJpaRepositories 추가



    스프링이 개발 소스에서 JpaRepository 인터페이스를 상속하는 인터페이스를 모두 찾아서 spring bean으로 등록해준다.





    1단계 - JpaRepository 상속

    spring data jpa에서 제공하는  JpaRepository 인테페이스를 상속


    JpaRepository를 상속하기만 하면 되며, 인터페이스에 따로 @Repository등의 어노테이션을 추가할 필요는 없다.



    spring data Repository를 상속한 위 인터페이스를 통해 도메인 엔티티(@Entity) 하나에 대해서 아래와 같은 기능을 제공한다.


    Repository 제공 기능

     method 이름

     기능

     save()

     레코드 저장 (insert, update)

     findOne()

     primary key로 레코드 한건 찾기
     findAll()

     전체 레코드 불러오기. 정렬(sort), 페이징(pageable) 가능

     count() 레코드 갯수
     delete() 레코드 삭제



    JpaRepository를 상속하는 인터페이스를 생성하는것 만으로 위 기능을 바로 사용할 수 있다.


    애자일

    프로그램 개발  초기단계에 적은 코딩만으로 동작하는 결과를 보고 싶을때는 일단 JpaRepository 인터페이스를 구현하는것만으로 기본적인 기능을 갖춘 Respository 객체가 만들어 졌다.

    spring-boot-starter-data-jpa, spring-boot-stater-web 두 라이브러리만 추가하면 1단계와 같은 방법으로 빠르게 CRUD에 해당하는 기본적인 REST API를 개발 + 배포가 가능하다.


    2단계 - 쿼리 메서드

    JpaRepository를 상속하는 인터페이스에 아래 규칙에 맞는 이름으로 메서드를 추가하면 간단한 select 문을 대신할 수 있는 조회기능을 추가 할 수 있다.



    query mehtod 이름 생성 전략

    아래 이름으로 시작하는 메서드는 query method 임을 스프링에게 알린다.

    mthod 이름 

    설명 

     findBy로 시작

     쿼리를 요청하는 메서임을 알림

     countBy로 시작

     쿼리 결과 레코드 수 요청하는 메서드 임을 알림



    필드 쿼리 표현식

    위의 findBy… 에 이어 아래와 같이 해당 repository 도메인의 필드이름을 입력하면 검색 쿼리를 실행한 결과를 전달한다. SQL의 where 절을 메서드이름을 통해서 전달한다고 보면 된다.


    메서드의 반환형이 도메인 객체이면 하나의 결과만 전달하고

    반환형이 List 이면 쿼리에 해당하는 모든 객체를 전달한다.


    query method에 포함할 수 있는 키워드

     메서드이름 키워드

     샘플

     설명

     And

     findByEmailAndUserId(String email, String userId)

     여러필드를 and 로 검색

     Or

     findByEmailOrUserId(String email, String userId)

     여러필드를 or 로 검색

     Between

     findByCreatedAtBetween(Date fromDate, Date toDate)

     필드의 두 값 사이에 있는 항목 검색

     LessThan

     findByAgeGraterThanEqual(int age)

     작은 항목 검색

     GreaterThanEqual

     findByAgeGraterThanEqual(int age)

     크거나 같은 항목 검색

     Like

     findByNameLike(String name)

     like 검색

     IsNull

     findByJobIsNull()

     null 인 항목 검색

     In

     findByJob(String … jobs)

     여러 값중에 하나인 항목 검색

     OrderBy

     findByEmailOrderByNameAsc(String email)

     검색 결과를 정렬하여 전달



    생각할 수 있는 거의 모든 연산자가 가능하다. 예를들어

    List<User> findFirst10ByNameAndAgeGreaterThanEqualOrderByBirthday(String name, int age);

    위 메서드 이름만 보면 어떤 데이터를 원하는지 바로 알 수 있을 것이다.


    한가지 문제점은 이 쿼리 메서드 자체는 type safe 하지 않다. 데이터베이스 스키마가 변경된다고 해서 메서드 이름에서 컴파일 에러가 날일은 없다. 그렇지만 springSTS (혹은 eclipse spring ide plugin)의 경우 쿼리 메서드명을 분석해서 필드이름등이 잘못되었을 경우에는 코드상에 에러 표시를 해준다. 또한 프로그램 실행시 spring이 로딩되는 과정에서 쿼리 메서드명이 유효하지 않을 경우 에러를 발생하면서 프로그램이 실행되지 않는 안전장치가 되어있다.





    애자일

    특정 비지니스 로직을 위한 데이터 조회나 간단한 연산등을 추가하여 기본적인 CRUD에서 좀더 발전된 API의 개발이 가능해진다.

    예제 코드)



    spring data jpa의 web 프로젝트 지원

    query method의 입력 변수로 Pageable을 추가하면 Page 타입을 반환형으로 사용할 수 있다.

    Pageable 객체를 통해 페이징과 정렬을 위한 파라미터를 전달한다.



    아래와 같이 controller에서 부터 Pageable을 전달받는다.


     query parameter 명

     설명

     page

     몇번째 페이지 인지를 전달

     size

     한 페이지에 몇개의 항목을 보여줄것인지 전달

     sort

     정렬정보를 전달. 정렬정보는 필드이름,정렬방향 의 포맷으로 전달한다. 여러 필드로 순차적으로 정렬도 가능하다.


    예: sort=createdAt,desc&sort=userId,asc



    아래는 위 controller를 통해 http 요청으로 페이징과 정렬된 데이터를 전달 받는 URI 샘플

    GET /users?page=1&size=10&sort=createdAt,desc&sort=userId,asc


    위와 같이 웹 페이지 개발에 필수적인 정렬과 페이징정보를 접속 url에서 부터 Repository 객체가지 바로 전달이 가능하다.



    spring-data-jpa + querydsl 로 개발하기 - 2 글로 이어집니다.




Designed by Tistory.