Spring 리드·아키텍트 프레임워크 면접

Spring 리드 · 아키텍트 (10년+) 프레임워크 3문항 조회수 14 · 2026-08-11 (화) 07:41:01
1 Spring Core - Bean Lifecycle
Hard

Q. 대규모 Spring 애플리케이션에서 Bean의 생성 순서와 의존성 주입 타이밍이 비즈니스 로직에 영향을 줄 수 있는 상황이 발생했습니다. BeanPostProcessor, BeanFactoryPostProcessor, @DependsOn, @Lazy, 그리고 ApplicationContextInitializer의 실행 순서와 각각의 역할을 설명하고, 복잡한 초기화 로직을 가진 시스템에서 Bean 생성 순서를 제어하기 위한 아키텍처 전략을 제시해주세요.

Spring Container의 초기화 과정에서 각 확장 포인트가 개입하는 시점과 범위를 먼저 생각해보세요.

A. 모범답안

Spring Container 초기화 과정은 ApplicationContextInitializer(컨텍스트 초기화 전) → BeanFactoryPostProcessor(Bean 정의 메타데이터 수정) → BeanPostProcessor 등록 → Bean 인스턴스화 → BeanPostProcessor.postProcessBeforeInitialization → @PostConstruct/@InitializingBean → BeanPostProcessor.postProcessAfterInitialization 순으로 진행됩니다. BeanFactoryPostProcessor는 Bean 정의 자체를 수정할 수 있어 PropertyPlaceholderConfigurer 같은 설정 처리에 사용되며, BeanPostProcessor는 생성된 Bean 인스턴스를 가로채 프록시 생성이나 AOP 적용에 활용됩니다. @DependsOn은 명시적 순서 제어, @Lazy는 실제 사용 시점까지 초기화 지연에 사용하되, 순환 참조나 복잡한 초기화가 필요한 경우 SmartLifecycle 인터페이스를 활용해 phase 기반 순차 초기화를 구현하는 것이 효과적입니다. 대규모 시스템에서는 모듈별로 별도의 ApplicationContext를 구성하고 parent-child 관계로 관리하거나, Spring Boot의 AutoConfiguration 순서 제어(@AutoConfigureBefore, @AutoConfigureAfter)를 활용해 초기화 복잡도를 낮추는 전략을 권장합니다.

핵심 포인트
  • • BeanFactoryPostProcessor는 Bean 정의 메타데이터 수정, BeanPostProcessor는 Bean 인스턴스 후처리
  • • ApplicationContextInitializer → BeanFactoryPostProcessor → Bean 생성 → BeanPostProcessor 순서로 실행
  • • SmartLifecycle과 phase를 활용한 순차적 초기화 제어
  • • 모듈별 ApplicationContext 분리와 parent-child 관계를 통한 복잡도 관리
답변에 넣으면 좋은 키워드
BeanPostProcessor BeanFactoryPostProcessor ApplicationContextInitializer SmartLifecycle Bean Lifecycle @DependsOn
실무에서는

멀티 데이터소스 환경에서 트랜잭션 매니저와 JPA EntityManager의 초기화 순서를 제어할 때 필수적입니다.

Follow-up 질문

BeanPostProcessor를 사용해 모든 Bean에 공통 로깅이나 모니터링을 적용할 때, 성능 오버헤드를 최소화하기 위한 방법은 무엇입니까?

2 Spring AOP - Proxy Mechanism
Hard

Q. Spring AOP의 JDK Dynamic Proxy와 CGLIB Proxy의 내부 동작 원리, 성능 특성, 제약사항을 비교 설명하고, 각 방식에서 self-invocation 문제가 발생하는 이유와 해결 방법을 제시해주세요. 또한 대규모 트래픽 환경에서 프록시 생성 비용과 런타임 오버헤드를 고려한 AOP 전략 설계 방안을 설명해주세요.

프록시 객체가 실제 타겟 객체를 어떻게 감싸고 메서드 호출을 가로채는지 생각해보세요.

A. 모범답안

JDK Dynamic Proxy는 인터페이스 기반으로 java.lang.reflect.Proxy를 사용해 런타임에 프록시 클래스를 생성하며, InvocationHandler를 통해 메서드 호출을 가로챕니다. CGLIB은 바이트코드 조작으로 타겟 클래스를 상속한 서브클래스를 생성하므로 인터페이스 없이도 동작하지만 final 클래스나 메서드에는 적용 불가하며, 생성 비용이 더 높지만 메서드 호출 성능은 JDK Proxy보다 우수합니다. self-invocation 문제는 프록시를 거치지 않고 내부 메서드를 직접 호출(this.method())할 때 발생하며, AopContext.currentProxy()로 현재 프록시를 가져오거나 self-injection, AspectJ 컴파일 타임 위빙으로 해결 가능합니다. 대규모 환경에서는 @Transactional이나 @Cacheable 같은 선언적 AOP는 유지하되, 성능에 민감한 핫패스에는 프로그래매틱 방식을 사용하고, 프록시 생성 비용을 줄이기 위해 prototype 스코프 Bean은 최소화하며, 필요시 ProxyFactoryBean의 캐싱 전략을 최적화하는 것이 효과적입니다.

핵심 포인트
  • • JDK Proxy는 인터페이스 기반 reflection, CGLIB은 클래스 상속 기반 바이트코드 조작
  • • CGLIB은 생성 비용이 높지만 호출 성능이 우수하며 final 제약 존재
  • • self-invocation은 프록시를 거치지 않는 내부 호출에서 발생하며 AopContext나 AspectJ로 해결
  • • 성능 민감 구간은 프로그래매틱 방식, prototype Bean 최소화로 프록시 생성 비용 절감
답변에 넣으면 좋은 키워드
JDK Dynamic Proxy CGLIB InvocationHandler self-invocation AopContext AspectJ
실무에서는

트랜잭션 처리나 캐싱 로직에서 같은 클래스 내부 메서드 호출 시 AOP가 적용되지 않는 문제를 해결할 때 필수적입니다.

Follow-up 질문

Spring Boot 2.0 이후 CGLIB이 기본 프록시 방식으로 변경된 이유와, 이로 인해 발생할 수 있는 호환성 문제는 무엇입니까?

3 Spring Transaction - Propagation & Isolation
Hard

Q. Spring의 7가지 트랜잭션 전파 옵션(REQUIRED, REQUIRES_NEW, NESTED, SUPPORTS, NOT_SUPPORTED, MANDATORY, NEVER) 중에서 실무에서 주의가 필요한 REQUIRES_NEW와 NESTED의 동작 차이를 설명하고, 각각이 적합한 비즈니스 시나리오를 제시해주세요. 또한 분산 트랜잭션 환경에서 트랜잭션 격리 수준(Isolation Level)과 전파 옵션을 조합할 때 발생할 수 있는 데드락, 팬텀 리드, 성능 저하 문제를 어떻게 예방하고 모니터링하시겠습니까?

REQUIRES_NEW는 완전히 독립적인 새 트랜잭션을, NESTED는 세이브포인트를 활용한다는 점에서 차이가 있습니다.

A. 모범답안

REQUIRES_NEW는 기존 트랜잭션을 일시 중단하고 완전히 독립적인 새 물리 트랜잭션을 시작하므로, 외부 트랜잭션 롤백이 내부에 영향을 주지 않으며 감사 로그나 알림 발송처럼 메인 로직 실패와 무관하게 커밋되어야 하는 경우 적합합니다. NESTED는 JDBC 세이브포인트를 사용해 중첩 트랜잭션을 구현하므로 내부 트랜잭션 롤백 시 세이브포인트까지만 롤백되지만 외부 롤백 시 전체가 롤백되며, 부분 실패 허용이 필요한 배치 처리나 선택적 부가 기능 실행에 유용하나 모든 DB가 세이브포인트를 지원하지는 않습니다. 격리 수준은 READ_COMMITTED를 기본으로 하고 필요한 경우에만 REPEATABLE_READ나 SERIALIZABLE을 선택적으로 적용하되, 트랜잭션 범위를 최소화하고 락 대기 시간을 모니터링해야 합니다. 데드락 예방을 위해 테이블 접근 순서를 일관되게 유지하고, 긴 트랜잭션은 비동기 처리나 이벤트 기반 아키텍처로 분리하며, Spring Actuator와 APM 도구로 트랜잭션 지속 시간과 락 경합을 실시간 추적하는 것이 중요합니다.

핵심 포인트
  • • REQUIRES_NEW는 독립적 물리 트랜잭션으로 외부 롤백과 무관, NESTED는 세이브포인트 기반 부분 롤백
  • • REQUIRES_NEW는 감사 로그, NESTED는 부분 실패 허용 배치에 적합
  • • 격리 수준은 READ_COMMITTED 기본, 필요시 선택적 상향 조정
  • • 데드락 예방을 위한 테이블 접근 순서 일관성 유지와 트랜잭션 범위 최소화
답변에 넣으면 좋은 키워드
REQUIRES_NEW NESTED Savepoint Isolation Level Propagation 데드락 READ_COMMITTED
실무에서는

주문 처리 중 포인트 적립 실패 시에도 주문은 완료되어야 하거나, 배치 작업에서 일부 항목 실패를 허용해야 할 때 필수적입니다.

Follow-up 질문

MSA 환경에서 Saga 패턴을 구현할 때, Spring Transaction의 전파 옵션을 어떻게 활용하거나 대체하시겠습니까?

댓글 0

로그인 후 댓글을 작성할 수 있습니다.

아직 댓글이 없습니다. 첫 번째 댓글을 남겨보세요!