26/08/05 IL(I Learned) - Spring Boot 설정 관리와 JPA Auditing

Let's take a break·2026년 8월 6일

Spring Boot 설정 관리와 JPA Auditing을 활용한 공통 기능 설계

이번 프로젝트에서는 단순히 데이터를 저장하고 조회하는 기능을 구현하는 것을 넘어, 프로젝트에서 반복적으로 사용되는 공통 기능을 효율적으로 관리하는 방법을 학습하였다.

특히 Spring Boot의 @ConfigurationProperties를 활용하여 설정 파일의 값을 객체로 관리하는 방법과, JPA Auditing을 이용하여 엔티티의 생성 시간과 수정 시간을 자동으로 관리하는 기능을 구현하였다.

또한 Repository를 인터페이스와 구현체로 분리하는 구조와 @Transactional을 이용한 트랜잭션 관리까지 학습하면서, 프로젝트의 유지보수성과 확장성을 고려한 설계 방법을 이해할 수 있었다.


@ConfigurationProperties를 이용한 설정 객체 관리

Spring Boot에서는 데이터베이스 정보, API Key, 프로젝트 이름 등 다양한 설정을 application.yml에 작성한다.

이러한 설정을 하나의 객체로 관리하기 위해 @ConfigurationProperties를 사용하였다.

@ConfigurationProperties(prefix = "app")
@Validated
public record AppProperties(
        @NotBlank String name,
        @NotBlank String version
) {
}

prefix = "app"은 설정 파일에서 app으로 시작하는 값을 하나의 객체에 자동으로 연결(Binding)한다.

예를 들어 설정 파일에 다음과 같이 작성되어 있다고 가정하자.

app:
  name: Library
  version: 1.0

애플리케이션이 실행되면 Spring Boot는

application.yml

↓

app.name

↓

AppProperties.name

────────────

app.version

↓

AppProperties.version

순서로 값을 읽어 객체를 생성한다.

따라서 Controller나 Service에서는

appProperties.name()

처럼 메서드를 호출하여 설정 값을 사용할 수 있다.


객체로 관리하는 이유

설정이 하나만 있다면

@Value("${app.name}")

만으로도 충분하다.

하지만

app:
  name:
  version:
  company:
  description:
  email:

처럼 설정이 많아질 경우

각각 @Value를 사용하는 것보다 하나의 객체로 관리하는 것이 훨씬 편리하다.


@Validated를 이용한 설정 검증

이번 프로젝트에서는 설정 객체에도 Validation을 적용하였다.

@Validated

일반적으로 Validation은 사용자의 입력(Form)에만 사용하는 것으로 생각하기 쉽다.

하지만 설정 파일도 잘못 작성될 수 있다.

예를 들어

app:
  name:

처럼 프로젝트 이름이 비어 있다면

애플리케이션이 정상적으로 동작하지 않을 수도 있다.

이때

@NotBlank

를 적용하면

Spring Boot가 실행되는 순간

application.yml

↓

Validation

↓

실패

↓

Application 종료

가 된다.

즉, 잘못된 설정으로 서버가 실행되는 것을 미리 방지할 수 있다는 점을 새롭게 학습하였다.


@ConfigurationPropertiesScan의 역할

설정 객체를 자동으로 등록하기 위해

프로젝트에서는

@ConfigurationPropertiesScan

을 사용하였다.

Spring Boot가 시작되면

@SpringBootApplication

↓

@ConfigurationPropertiesScan

↓

@ConfigurationProperties 검색

↓

Bean 등록

↓

DI 가능

순서로 동작한다.

만약 이 Annotation이 없다면

AppProperties가 Bean으로 등록되지 않아

@RequiredArgsConstructor

에서 주입받을 수 없다.

따라서 설정 객체를 자동으로 검색하여 Spring Container에 등록하는 역할을 수행한다는 점을 이해하였다.


BaseEntity를 이용한 공통 필드 관리

프로젝트에는 모든 Entity가 공통으로 사용하는 정보를 관리하기 위한 BaseEntity가 존재하였다.

@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity {

    @CreatedDate
    private LocalDateTime createdAt;

    @LastModifiedDate
    private LocalDateTime updatedAt;

}

대부분의 데이터는

  • 생성일
  • 수정일

정보를 가지고 있다.

예를 들어

제목가격생성일수정일
Java300002026-08-052026-08-10

처럼 관리된다.

만약 모든 Entity에

private LocalDateTime createdAt;
private LocalDateTime updatedAt;

를 직접 작성한다면

중복 코드가 계속 발생한다.

그래서 공통 기능을 BaseEntity에 작성하고

public class BookEntity
        extends BaseEntity

처럼 상속하여 사용하였다.


JPA Auditing

이번 프로젝트에서 새롭게 학습한 기능 중 하나는 JPA Auditing이었다.

Auditing은 Entity가 생성되거나 수정되는 시간을 자동으로 기록하는 기능이다.

프로젝트에서는

@EnableJpaAuditing

을 활성화하였다.

사용자가

새 책 등록

을 수행하면

내부적으로

Book 저장

↓

Persist

↓

@CreatedDate

↓

현재 시간 저장

이 자동으로 수행된다.

예를 들어

2026-08-06 10:30

에 저장했다면

createdAt도

2026-08-06 10:30

으로 자동 저장된다.

개발자가

entity.setCreatedAt(...)

를 직접 호출하지 않아도 된다.


@LastModifiedDate

책 정보를 수정하면

가격 변경

↓

UPDATE

↓

@LastModifiedDate

↓

현재 시간 저장

이 자동으로 수행된다.

예를 들어

기존 데이터가

생성일수정일
08/0108/01

이었다면

가격 수정 후에는

생성일수정일
08/0108/06

처럼 수정일만 변경된다.

이를 통해 변경 이력을 자동으로 관리할 수 있다는 점을 학습하였다.


Repository 추상화

이번 프로젝트에서는 Repository를 인터페이스와 구현체로 분리하였다.

BookRepository

↓

BookRepositoryImpl

↓

JpaRepository

Controller는

BookRepository

만 알고 있다.

실제 구현은

BookRepositoryImpl

이 담당한다.

이 구조를 사용하면

나중에

JPA

↓

MyBatis

로 변경하더라도

Controller와 Service는 수정하지 않아도 된다.

즉,

구현보다 인터페이스에 의존하는 구조를 만들 수 있다는 점을 이해하였다.


@Transactional의 역할

Service에서는

@Transactional

@Transactional(readOnly = true)

를 함께 사용하였다.

일반 Transaction

@Transactional

  • INSERT
  • UPDATE
  • DELETE

처럼 데이터를 변경하는 작업에서 사용한다.

동작 과정은

Transaction 시작

↓

DB 작업

↓

성공

↓

Commit

────────────

실패

↓

Rollback

이다.

즉, 여러 작업 중 하나라도 실패하면 이전 상태로 되돌려 데이터의 일관성을 유지한다.


readOnly = true

조회 기능에서는

@Transactional(readOnly = true)

를 사용하였다.

조회는 데이터를 변경하지 않기 때문에 JPA는 변경 사항을 추적할 필요가 없다.

따라서 Dirty Checking과 같은 작업을 생략하여 불필요한 비용을 줄일 수 있다.

예를 들어

도서 목록 조회

↓

SELECT

↓

readOnly Transaction

↓

조회만 수행

처럼 동작하며, 읽기 전용 작업에 적합하다.


학습한 내용

학습 내용세부 학습 내용
@ConfigurationProperties여러 설정 값을 하나의 객체로 바인딩하여 관리하는 방법
@Validated설정 객체에도 Validation을 적용하여 실행 전에 오류를 검증하는 방법
@ConfigurationPropertiesScan설정 객체를 자동으로 검색하여 Bean으로 등록하는 과정
BaseEntity여러 Entity에서 공통으로 사용하는 필드를 상속으로 관리하는 방법
JPA Auditing@CreatedDate, @LastModifiedDate를 이용하여 생성일과 수정일을 자동으로 기록하는 기능
Repository 추상화인터페이스와 구현체를 분리하여 유지보수성과 확장성을 높이는 구조
@Transactional여러 데이터 변경 작업을 하나의 트랜잭션으로 관리하는 방법
readOnly = true조회 전용 트랜잭션을 사용하여 성능을 최적화하는 방법

느낀 점

이번 프로젝트를 통해 애플리케이션은 단순히 기능을 구현하는 것뿐만 아니라, 반복되는 기능을 공통화하고 설정을 체계적으로 관리하는 설계가 중요하다는 점을 배울 수 있었다. 특히 BaseEntity와 JPA Auditing을 활용하여 생성일과 수정일을 자동으로 관리하는 기능을 구현하면서 중복 코드를 줄이는 방법을 익혔고, @ConfigurationProperties를 통해 설정을 객체로 관리함으로써 유지보수성과 안정성을 높일 수 있다는 점을 이해하였다. 또한 Repository를 인터페이스와 구현체로 분리하는 구조와 @Transactional을 통한 트랜잭션 관리 방식을 학습하면서 객체지향적인 설계와 데이터의 일관성을 유지하는 방법을 함께 배울 수 있었다.

0개의 댓글