본문 바로가기

리포지토리 만들기

@정소민fan2025. 9. 13. 23:32

도메인 엔티티를 만들었으니까 이제 Spring Data JPA를 사용해서 리포지토리 계층을 만들어보자

라이브러리부터

implementation("org.springframework.boot:spring-boot-starter-data-jpa")
runtimeOnly("com.h2database:h2")

이렇게 spring data jpa를 추가해주고, h2 데이터베이스 의존성도 추가해주자.

implementation("p6spy:p6spy:3.9.1")

그리고 이건 데이터베이스 로깅을 좀 더 상세하게 보여주는 의존성이라고 하는데, 일단 추가해주자. 딱히 필요없나?

p6spy는 JDBC 드라이버 프록시이다. 실행되는 모든 SQL 쿼리를 중간에 가로채서 로그로 남겨주는 역할을 한다.

yml 파일 설정

application.yml로 들어가서 설정을 해주자

spring:
  datasource:
    url: "jdbc:p6spy:h2:tcp://localhost:9092/~/blog;"
    username: "sa"
    password: "123"
    driver-class-name: "com.p6spy.engine.spy.P6SpyDriver"
  jpa:
    show-sql: true
    hibernate:
      ddl-auto: update
      naming:
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
    open-in-view: false
    properties:
      hibernate:
        default_batch_fetch_size: 1000
        globally_quoted_identifiers: true

보면 데이터베이스 연결 부분이 조금 다른데, p6spy를 이용하여 sql 로그를 볼거기 때문에 살짝 다르다.

driver-class-name 도 p6spy를 사용하도록 바꿔주자.

  • physical-strategy : JPA 엔티티의 필드 이름을 실제 데이터베이스 컬럼 이름으로 어떻게 매핑할지 전략을 설정한다. 지금 설정은 엔티티에 정의된 이름 그대로 사용하는 전략인데, 기본값으로는 SpringPhysicalNamingStrategy을 사용하면 카멜 케이스를 스네이크 케이스로 바꿀수 있다.
  • open-in-view : OSIV 패턴을 사용할지 여부를 결정한다.
  • default_batch_fetch_size : 지연 로딩으로 설정된 컬렉션을 조회할 때, 여기서 지정된 사이즈만큼의 ID를 모아 IN 쿼리를 통해 한번에 가져오도록 할 수 있다. N+1 문제를 방지할 수 있음!!
  • globally_quoted_identifiers : 하이버네이트가 생성하는 모든 SQL에서 테이블이나 컬럼명같은 식별자를 따옴표로 감싸준다. 이렇게 하면 데이터베이스의 예약어와 테이블/컬럼명이 충돌하는 것을 방지해준다고 한다. 예약어 때문에 애먹은 적이 한두번이 아닌데, 정말 편한 설정이다.

ddl-auto

spirng:jpa:hibernate:ddl-auto는 중요하니까 한번 짚고 넘어가자.

여기서는 옵션을 5가지 줄 수 있다.

  • create : 기존 테이블을 모두 삭제하고 엔티티를 기반으로 모두 새로 생성
  • create-drop : 애플리케이션 종료 시 모든 테이블 삭제
  • update : 엔티티와 기존 테이블 간의 차이점을 확인하고 변경된 부분만 반영. 주로 개발 환경에서 사용한다
  • validate : 엔티티와 테이블이 일치하는지 검사하고, 일치하지 않으면 애플리케이션 실행 불가
  • none : 아무것도 하지 않음. 운영 환경에서 사용

create는 정말정말.... 위험하니까 잘 보고 사용하자. 잘못되면 소중한 데이터가 모두 날아가버리는 불상사가 발생한다

Repository

kotlin-jdsl을 추가로 의존해주자. Querydsl과 유사한 역할을 한다. 나도 아직 사용법은 잘 모름...

implementation("com.linecorp.kotlin-jdsl:spring-data-jpa-support:3.5.5")
implementation("com.linecorp.kotlin-jdsl:jpql-dsl:3.5.5")
implementation("com.linecorp.kotlin-jdsl:jpql-render:3.5.5")

그리고 리포지토리를 추가해주자

package simpleblog.domain.member

import com.linecorp.kotlinjdsl.dsl.jpql.jpql
import com.linecorp.kotlinjdsl.support.spring.data.jpa.repository.KotlinJdslJpqlExecutor
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.jpa.repository.JpaRepository

interface MemberRepository : JpaRepository<Member, Long>, KotlinJdslJpqlExecutor {
    fun findMemberByEmail(email: String): Member

}

fun MemberRepository.findMembersByPage(pageable: Pageable): Page<Member?> =
    findPage(pageable){
        select(entity(Member::class))
            .from(entity(Member::class))
            .orderBy(path(Member::id).asc())
    }

JpaRepository는 스프링 data jpa을 사용할수 있게 해주는 인터페이스이고, KotlinJdslExecutor는 복잡한 쿼리나 동적 쿼리를 손쉽게 생성해줄 수 있는 코틀린 jdsl을 사용하기 위한 인터페이스이다. 밑에 확장 함수로 만들어진 findMemberByPage가 코틀린 jdsl로 만들어진 함수이다.

findMemberByPage 에서는 Pageble을 인자로 받고 있는데, 정말 편리한 인터페이스다. 이 인터페이스만 있으면 페이지, 리미트 등을 개발자가 따로 명시해둘 필요도 없다. 컨트롤러에서도 Pageble을 추가해두면 된다!!

 

그런데 저렇게 인터페이스로 추가하는것은 2.x 버전에서나 사용하는 방식이라고 한다. 현재 3.x 버전 사용 중인데, 어떻게 사용하려나...

비슷한 방식으로 Comment나 Member도 만들어주자

package simpleblog.domain.member

import com.linecorp.kotlinjdsl.dsl.jpql.jpql
import com.linecorp.kotlinjdsl.support.spring.data.jpa.repository.KotlinJdslJpqlExecutor
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.jpa.repository.JpaRepository

interface MemberRepository : JpaRepository<Member, Long>, KotlinJdslJpqlExecutor {
    fun findMemberByEmail(email: String): Member

}

fun MemberRepository.findMembersByPage(pageable: Pageable): Page<Member?> =
    findPage(pageable){
        select(entity(Member::class))
            .from(entity(Member::class))
            .orderBy(path(Member::id).asc())
    }
package simpleblog.domain.comment

import com.linecorp.kotlinjdsl.support.spring.data.jpa.repository.KotlinJdslJpqlExecutor
import org.springframework.data.jpa.repository.JpaRepository

interface CommentRepository : JpaRepository<Long, Comment>, KotlinJdslJpqlExecutor {
}

 

JPA을 써보면서 보니까 JPA랑 코틀린이 전혀 어울리지 않는다는 포스팅이 많이 보인다. 젯브레인에서 만든 Exposed라는 코틀린 전용 ORM도 있던데... 이것도 나중에 한번 써봐야지

'Spring > Kotlin' 카테고리의 다른 글

마이그레이션 계획  (0) 2025.12.03
서비스, API 작성  (2) 2025.10.01
도메인 엔티티 작성  (0) 2025.09.01
AuditingEntity 작성  (2) 2025.08.31
코틀린 + 스프링 프로젝트  (1) 2025.08.31
정소민fan
@정소민fan :: 코딩은 관성이야

코딩은 관성적으로 해야합니다 즐거운 코딩 되세요

목차