이번 프로젝트에서는 단순히 데이터를 저장하고 조회하는 기능을 구현하는 것을 넘어, 프로젝트에서 반복적으로 사용되는 공통 기능을 효율적으로 관리하는 방법을 학습하였다.
특히 Spring Boot의 @ConfigurationProperties를 활용하여 설정 파일의 값을 객체로 관리하는 방법과, JPA Auditing을 이용하여 엔티티의 생성 시간과 수정 시간을 자동으로 관리하는 기능을 구현하였다.
또한 Repository를 인터페이스와 구현체로 분리하는 구조와 @Transactional을 이용한 트랜잭션 관리까지 학습하면서, 프로젝트의 유지보수성과 확장성을 고려한 설계 방법을 이해할 수 있었다.
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를 사용하는 것보다 하나의 객체로 관리하는 것이 훨씬 편리하다.
이번 프로젝트에서는 설정 객체에도 Validation을 적용하였다.
@Validated
일반적으로 Validation은 사용자의 입력(Form)에만 사용하는 것으로 생각하기 쉽다.
하지만 설정 파일도 잘못 작성될 수 있다.
예를 들어
app:
name:
처럼 프로젝트 이름이 비어 있다면
애플리케이션이 정상적으로 동작하지 않을 수도 있다.
이때
@NotBlank
를 적용하면
Spring Boot가 실행되는 순간
application.yml
↓
Validation
↓
실패
↓
Application 종료
가 된다.
즉, 잘못된 설정으로 서버가 실행되는 것을 미리 방지할 수 있다는 점을 새롭게 학습하였다.
설정 객체를 자동으로 등록하기 위해
프로젝트에서는
@ConfigurationPropertiesScan
을 사용하였다.
Spring Boot가 시작되면
@SpringBootApplication
↓
@ConfigurationPropertiesScan
↓
@ConfigurationProperties 검색
↓
Bean 등록
↓
DI 가능
순서로 동작한다.
만약 이 Annotation이 없다면
AppProperties가 Bean으로 등록되지 않아
@RequiredArgsConstructor
에서 주입받을 수 없다.
따라서 설정 객체를 자동으로 검색하여 Spring Container에 등록하는 역할을 수행한다는 점을 이해하였다.
프로젝트에는 모든 Entity가 공통으로 사용하는 정보를 관리하기 위한 BaseEntity가 존재하였다.
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class BaseEntity {
@CreatedDate
private LocalDateTime createdAt;
@LastModifiedDate
private LocalDateTime updatedAt;
}
대부분의 데이터는
정보를 가지고 있다.
예를 들어
| 제목 | 가격 | 생성일 | 수정일 |
|---|---|---|---|
| Java | 30000 | 2026-08-05 | 2026-08-10 |
처럼 관리된다.
만약 모든 Entity에
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
를 직접 작성한다면
중복 코드가 계속 발생한다.
그래서 공통 기능을 BaseEntity에 작성하고
public class BookEntity
extends BaseEntity
처럼 상속하여 사용하였다.
이번 프로젝트에서 새롭게 학습한 기능 중 하나는 JPA Auditing이었다.
Auditing은 Entity가 생성되거나 수정되는 시간을 자동으로 기록하는 기능이다.
프로젝트에서는
@EnableJpaAuditing
을 활성화하였다.
사용자가
새 책 등록
을 수행하면
내부적으로
Book 저장
↓
Persist
↓
@CreatedDate
↓
현재 시간 저장
이 자동으로 수행된다.
예를 들어
2026-08-06 10:30
에 저장했다면
createdAt도
2026-08-06 10:30
으로 자동 저장된다.
개발자가
entity.setCreatedAt(...)
를 직접 호출하지 않아도 된다.
책 정보를 수정하면
가격 변경
↓
UPDATE
↓
@LastModifiedDate
↓
현재 시간 저장
이 자동으로 수행된다.
예를 들어
기존 데이터가
| 생성일 | 수정일 |
|---|---|
| 08/01 | 08/01 |
이었다면
가격 수정 후에는
| 생성일 | 수정일 |
|---|---|
| 08/01 | 08/06 |
처럼 수정일만 변경된다.
이를 통해 변경 이력을 자동으로 관리할 수 있다는 점을 학습하였다.
이번 프로젝트에서는 Repository를 인터페이스와 구현체로 분리하였다.
BookRepository
↓
BookRepositoryImpl
↓
JpaRepository
Controller는
BookRepository
만 알고 있다.
실제 구현은
BookRepositoryImpl
이 담당한다.
이 구조를 사용하면
나중에
JPA
↓
MyBatis
로 변경하더라도
Controller와 Service는 수정하지 않아도 된다.
즉,
구현보다 인터페이스에 의존하는 구조를 만들 수 있다는 점을 이해하였다.
Service에서는
@Transactional
과
@Transactional(readOnly = true)
를 함께 사용하였다.
@Transactional
은
처럼 데이터를 변경하는 작업에서 사용한다.
동작 과정은
Transaction 시작
↓
DB 작업
↓
성공
↓
Commit
────────────
실패
↓
Rollback
이다.
즉, 여러 작업 중 하나라도 실패하면 이전 상태로 되돌려 데이터의 일관성을 유지한다.
조회 기능에서는
@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을 통한 트랜잭션 관리 방식을 학습하면서 객체지향적인 설계와 데이터의 일관성을 유지하는 방법을 함께 배울 수 있었다.