Kotlin

[Kotlin, Spring, JPA] Boilerplate 프로젝트 만들기

keep it simple 2023. 2. 20. 04:30

Boilerplate란?

  • 단순한 반복작업을 없애주고 생산성을 향상시켜주는 작업을 Boilerplate라 한다. 우리가 흔히 아는 보일러가 맞다. 보일러의 통은 보일러를 만드는데 무조건 필요하다. 기능만 업그레이드할 뿐 플레이트는 계속 찍어낸다. 개발에서 이런 보일러 통 생산같이 단순한 반복작업은 없앨수 있으면 무조건 없애는게 좋다.라이브러리의 버전은 달라질 수 도 있으나 구조는 비슷하므로 Koltin,Spring,JPA로boilerplate 프로젝트를 만들어보자!

1. https://start.spring.io/ 에서 밑과 같이 의존성 설정 후 생성 (DB는 취향껏, java 11버전이라 스프링 부트 버전 2.7.8로 선택했다. - 스프링 부트 3.0 버전 이상은 자바 17버전으로만 사용 가능)

2. build.gradle에 밑과 같이 추가하여 이미지처럼 맞춘다. 

plugins {
    kotlin("plugin.allopen")  version "1.6.21"
}

noArg{
   annotation("javax.persistence.Entity")
}

allOpen{
   annotation("javax.persistence.Entity")
   annotation("javax.persistence.Embeddable")
   annotation("javax.persistence.MappedSuperclass")
}

  • noArg : @Entity가 달린 엔티티 클래스에 기본 생성자를 생성해주기위에 noarg 플러그인 사용 - 만약 이 플러그인을 적용안하면 컴파일 에러는 발생을 안하지만 쿼리가 날라갈 때 에러가 발생한다.
  • allOpen:  JPA 구현체인 Hiberate는 지연로딩(Lazy Loading)을 사용할 때 CGLIB를 사용해서 프록시 객체를 만든다. 하지만 코틀린은 모든 클래스가 final 속성(final 클래스는 프록시 객체를 생성하지 못한다.)이기 때문에 allopen 플러그인을 통해 제거해야한다. -만약 이 플러그인을 적용안하면 즉시로딩(Eager Loading)으로 사용되서 불필요한 쿼리가 나가서 서버 성능이 저하된다.

3. 공통으로 엔티티에 사용할 BaseEntity 클래스 생성하기

@MappedSuperclass
@EntityListeners(AuditingEntityListener::class)
abstract class BaseEntity {
    @Id
    @GeneratedValue
    val id: Long = 0
    
    @CreatedDate
    @Column(nullable = false, updatable = false)
    protected var createdAt: LocalDateTime = LocalDateTime.MIN

    @LastModifiedDate
    @Column(nullable = false)
    protected var updatedAt: LocalDateTime = LocalDateTime.MIN
}
package com.project.boilerplate.domain

import com.project.boilerplate.infrastructure.persistence.BaseEntity
import javax.persistence.Entity
import javax.persistence.Table

@Entity
@Table(name = "member")
class Member(
    val name: String
): BaseEntity() {
}
  • 위처럼 BaseEntity생성후 Member 엔티티처럼 상속받으면 된다. 직접적으로 BaseEntity클래스만 사용할일은 없기에 추상화 클래스로 생성
  • @MappedSuperclass는 공통 매핑 정보로 사용된다. 위와 같이 createdAt(생성일)/ updatedAt(수정일)은 어떤 Entity든 같이 사용해야되서 해당 어노테이션을 사용했다. 
  • @EntityListeners(AuditingEntityListener::class)는 Entity에 상태가 변경될 때마다 이벤트 발생시켜주는 어노테이션 
  • @Id 는 엔티티의 primary key인지 알려주는 어노테이션이다
  • @GeneratedValue 는 기본 키를 자동 생성하게 해주는 옵션
  • @CreatedDate은 생성 시간을 자동 저장하게해준다.
  • @LastModifiedDate은 수정 시간을 자동 저장하게 해준다.

4. 메인 클래스에 @EnableJpaAuditing 붙혀서 auditing 활성화

@EnableJpaAuditing
@SpringBootApplication
class BoilerplateApplication

fun main(args: Array<String>) {
   runApplication<BoilerplateApplication>(*args)
}

DDD Layered 아키텍처 적용

계층들 순서는 밑처럼 진행된다.

  • presentation: 사용자 UI나 클라이언트 request/response 해주는 계층. 흔히 controller라고 한다.
  • application: 비즈니스 로직 정의. 도메인 계층과 인프라 계층을 연결하는 계층이다. 흔히 service라고 한다.
  • domain: 실질적으로 도메인에 대한 정보이며 도메인에 대한 모든 책임을 지는 계층. 흔히 entity라고한다.
  • infrastructure: 외부와의 통신을 담당하는 계층 
    • config: 설정 관련 
    • persistence:  jpa관련 repository 관련

장점

  • 간단하다. 한 눈에 파악하기 쉽다. -> 유지보수가 쉽다.
  • 일관성이 있다.
  • 위처럼 각 계층마다 관심사가 분리되어 loosely coupling(느슨한 결합)된 형태다.

결론

  • 위처럼 필수적인 기본 세팅만 해놨다. 이후 필요한 부분이나 공통적으로 사용하는 부분은 추가할 예정이다. 위에 layered 아키텍처를 선택한 이유는 현재 DDD 책을 공부하고 있기도 해서 토이프로젝트에 적용해 보려고 만들었다. 

reference