Files
llm-wiki/raw/interviews/spring-jpa-flyway-initialization-lifecycle-circular-dependency.md
T

5.3 KiB

title, source_type, status, branch, related_projects, tags, created, updated
title source_type status branch related_projects tags created updated
interview / spring-jpa-flyway-initialization-lifecycle-circular-dependency interview-prep raw feature-build-release-supply-chain-contract
ca-skeleton
interview
spring
jpa
flyway
lifecycle
circular-dependency
2026-06-23 2026-06-23

Spring Boot에서 Flyway와 JPA(EntityManagerFactory) 초기화 순환 참조 및 해결 전략

Parent

면접 질문 및 핵심 답변

Q1. Spring Boot 애플리케이션 기동 시, Flyway 마이그레이션과 JPA(Hibernate) 초기화 중 어느 것이 먼저 실행되어야 하며 그 이유는 무엇입니까?

답변:

  • 실행 순서: Flyway 마이그레이션이 항상 JPA 초기화보다 먼저 실행되어야 합니다.
  • 이유: JPA의 EntityManagerFactory가 초기화되는 과정에서 엔티티 매핑 정보를 바탕으로 데이터베이스 스키마 검증(Hibernate ddl-auto: validate 또는 update)을 수행하거나, 영속성 컨텍스트를 구성하기 때문입니다. 만약 최신 테이블 정의나 변경 사항(DDL)이 데이터베이스에 먼저 반영되어 있지 않다면, JPA 초기화 단계에서 테이블/컬럼 부재로 인해 SchemaManagementException 등의 예외를 던지며 애플리케이션 기동이 실패하게 됩니다.
  • Spring Boot의 처리: Spring Boot는 이를 보장하기 위해 flywayInitializer 빈을 entityManagerFactory 빈보다 먼저 생성하도록 자동 구성(DependsOn)합니다.

Q2. JPA 설정 클래스(@Configuration) 내에서 일반(인스턴스) @Bean 메서드로 FlywayConfigurationCustomizer를 정의했을 때 순환 참조(Circular Dependency) 에러가 발생하는 메커니즘을 설명하고, 이를 해결하기 위한 static @Bean 적용 원리를 설명해 주세요.

답변:

  • 순환 참조 발생 메커니즘:

    1. Spring Boot가 스키마 마이그레이션을 수행하기 위해 flywayInitializer 빈 생성을 시작합니다.
    2. 이 과정에서 커스텀 Flyway 설정을 반영하고자 컨테이너에 등록된 모든 FlywayConfigurationCustomizer 빈들을 찾습니다.
    3. 만약 이 Customizer 빈이 설정 클래스(@Configuration) 내에 일반 @Bean 메서드로 선언되어 있다면, Spring은 이 메서드를 호출하기 위해 먼저 부모 설정 클래스의 인스턴스를 생성해야 합니다.
    4. 부모 설정 클래스 인스턴스화 과정에서 내부에 선언된 영속성 필드(@PersistenceContext EntityManager)나 JPA 관련 종속성 빈 주입을 시도합니다.
    5. 이를 주입하려면 entityManagerFactory 빈이 먼저 완성되어 있어야 하므로 JPA 초기화를 트리거합니다.
    6. 하지만 entityManagerFactory는 스키마 보장을 위해 flywayInitializer가 끝날 때까지 대기(DependsOn)하므로, flywayInitializer -> Customizer -> Configuration -> EntityManagerFactory -> flywayInitializer로 이어지는 데드락성 순환 참조가 발생합니다.
  • static @Bean을 통한 해결 원리:

    • Spring 프레임워크는 @Configuration 클래스 내에 선언된 static @Bean 메서드를 로드할 때, 부모 클래스의 인스턴스 생성 없이 클래스 정의 자체에서 직접 정적 메서드를 호출하여 빈을 등록합니다.
    • 따라서, FlywayConfigurationCustomizerstatic으로 정의되면 부모 설정 클래스의 인스턴스화 및 그에 딸린 @PersistenceContext EntityManager 주입 처리가 뒤로 지연(Defer)됩니다.
    • 이 덕분에 Flyway 초기화가 아무런 JPA 간섭 없이 완료되고, 그 이후에 비로소 설정 클래스 인스턴스화 및 entityManagerFactory 구성이 순차적으로 완료되면서 순환 참조 고리가 완벽히 해소됩니다.

Q3. PostgreSQL 환경에서 엔티티의 대용량 텍스트 필드를 매핑할 때, @Lob 어노테이션을 쓰면 발생하는 문제와 클린 아키텍처 관점에서의 대안은 무엇입니까?

답변:

  • 문제점:
    • Hibernate는 PostgreSQL 환경에서 @Lob 어노테이션이 붙은 String 필드를 일반 text 컬럼이 아니라 oid (Large Object 식별자인 숫자형) 타입으로 매핑하려고 시도합니다.
    • 이로 인해 Flyway 스크립트에서 선언한 실제 데이터 타입인 text와 불일치가 발생합니다.
    • 개발 환경에서 ddl-auto: update가 켜져 있으면 Hibernate가 기존 text 컬럼을 oid 타입으로 변환하려고 alter table alter column ... set data type oid 쿼리를 던지고, PostgreSQL이 이 묵시적 캐스팅을 거부해 column cannot be cast automatically to type oid 에러를 유발하며 실행이 중단됩니다.
  • 대안:
    • @JdbcTypeCode(SqlTypes.LONGVARCHAR) 사용:
    • 특정 데이터베이스 벤더에 종속적인 @Column(columnDefinition = "text") 기술은 클린 아키텍처의 DB 이식성 원칙(ArchUnit 제약 조건)을 위반합니다.
    • 반면 @JdbcTypeCode(SqlTypes.LONGVARCHAR)는 JDBC 표준 레벨의 대용량 가변 길이 문자열 힌트를 주어, PostgreSQL 환경에서는 안전하게 text 컬럼에 매핑되면서도 다른 DB(Oracle, H2 등)로 전환했을 때 벤더 종속성 없이 포터블한 DDL 매핑을 유지해 줍니다.