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

57 lines
5.3 KiB
Markdown

---
title: interview / spring-jpa-flyway-initialization-lifecycle-circular-dependency
source_type: interview-prep
status: raw
branch: feature-build-release-supply-chain-contract
related_projects: [ca-skeleton]
tags: [interview, spring, jpa, flyway, lifecycle, circular-dependency]
created: 2026-06-23
updated: 2026-06-23
---
# Spring Boot에서 Flyway와 JPA(EntityManagerFactory) 초기화 순환 참조 및 해결 전략
## Parent
- Parent branch note: [[raw/branch-notes/feature-build-release-supply-chain-contract]]
## 면접 질문 및 핵심 답변
### 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` 메서드**를 로드할 때, 부모 클래스의 인스턴스 생성 없이 **클래스 정의 자체에서 직접 정적 메서드를 호출**하여 빈을 등록합니다.
* 따라서, `FlywayConfigurationCustomizer``static`으로 정의되면 부모 설정 클래스의 인스턴스화 및 그에 딸린 `@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 매핑을 유지해 줍니다.