백엔드 면접 학습 노트 목차

Spring·JPA — 백엔드 면접 (실제 기출 기반·심화)

한국 백엔드 면접의 실무 핵심이자 가장 깊게 파는 영역이다. "IoC/DI가 뭔가", "Bean 스코프", "AOP", "@Transactional 동작 원리", "영속성 컨텍스트", "N+1과 해결" — 단골이고, VSFe 패턴으로 self-invocation·fetch join 페이징·프록시까지 파고든다.

면접관이 노리는 단골 함정 Top: ① @Transactional self-invocation ② Checked 예외는 기본 롤백 안 됨 ③ 즉시로딩도 N+1 ④ 컬렉션 fetch join은 페이징 불가 ⑤ 싱글톤 빈에 프로토타입 빈 주입.


1. IoC·DI (★★ 단골)

실제 질문: "IoC와 DI를 설명하라." · "구현체가 딱 하나뿐이면 구체 클래스를 그냥 써도 되는데, 왜 Bean으로 등록하나요?" (VSFe)

  • IoC(제어의 역전): 객체 생성·생명주기 제어를 컨테이너에 위임.
  • DI(의존성 주입): 의존 객체를 외부(컨테이너)에서 주입 → 결합도↓·테스트 용이. 방식: 생성자(권장)·세터·필드.

깊이 — 왜 Bean인가(★): 구현체가 하나여도 Bean으로 두는 이유 = 생명주기 관리·AOP 프록시 적용·테스트 시 교체·일관성. 생성자 주입 권장 이유 = 불변(final) 보장·순환 참조를 컴파일/기동 시점에 발견·테스트 쉬움.

꼬리질문: "필드 주입(@Autowired 필드)의 단점?" → 테스트 어려움·불변 불가·순환 참조 은폐. / "DI는 스프링 전용?" → 아니다(일반 패턴).

함정: ❌ "필드 주입이 가장 간단하니 최선" → 생성자 주입 권장.


2. Bean 스코프·생명주기 (★ 단골)

실제 질문: "Bean의 스코프와 생명주기를 설명하라." · "싱글톤 빈이 프로토타입 빈을 주입받으면 어떻게 되나요?" · "프로토타입 빈의 소멸 콜백은 호출되나요?" (WeareSoft·VSFe)

  • 스코프: singleton(기본·컨테이너당 1개)·prototype(요청마다 새 인스턴스)·request/session(웹).
  • 생명주기: 생성 → 의존성 주입 → 초기화 콜백(@PostConstruct) → 사용 → 소멸 콜백(@PreDestroy).

깊이 — 싱글톤에 프로토타입 주입(★): 싱글톤은 생성 시 한 번만 주입받으므로 계속 같은 프로토타입 인스턴스를 쓴다(매번 새로 안 생김). → **ObjectProvider/Provider·@Lookup**으로 매번 새로 조회. 프로토타입의 소멸 콜백은 호출 안 됨(컨테이너가 생성·주입까지만 관리).

꼬리질문: "싱글톤 빈에 가변 상태(필드)를 두면?" → 스레드 안전 문제(공유). / "스프링 싱글톤 = GoF 싱글톤?" → 아니다. 컨테이너 범위 단일 인스턴스.

함정: ❌ "프로토타입을 주입하면 매번 새 객체" → 싱글톤이 들고 있으면 고정.


3. AOP (★★ 단골)

실제 질문: "AOP를 설명하라." · "@Aspect는 어떻게 동작하나요?" (VSFe)

  • AOP: 횡단 관심사(로깅·트랜잭션·보안)를 핵심 로직과 분리. 용어 Aspect/Advice/Pointcut/JoinPoint/Weaving.
  • Spring AOP = 프록시 기반: JDK 동적 프록시(인터페이스 기반) 또는 CGLIB(클래스 상속 기반·인터페이스 없을 때). 호출을 프록시가 가로채 부가 기능을 위빙.

깊이 — 프록시의 한계(★ self-invocation의 뿌리): 프록시는 외부에서 들어오는 호출만 가로챈다 → 같은 클래스 내부 메서드 호출(this.method())은 프록시를 안 거쳐 AOP가 적용 안 됨. private·final 메서드도 프록시 불가.

꼬리질문: "JDK 프록시 vs CGLIB?" → 인터페이스 유무. 스프링부트 기본은 CGLIB. / "self-invocation 해결?" → 빈 분리·AopContext.currentProxy()·AspectJ.

함정: ❌ "AOP는 모든 메서드 호출에 걸린다" → 내부 호출·private엔 안 걸림.


4. @Transactional — 동작·전파·self-invocation (★★★ 최다 함정)

실제 질문: "@Transactional의 동작 원리는?" · "@Transactional(readOnly=true)는 어떤 기능인가요?" · "트랜잭션 전파(propagation)를 설명하라." (VSFe·velog)

  • 동작: AOP 프록시가 메서드 호출을 가로채 begin(트랜잭션 시작) → 실제 메서드 실행 → 정상이면 commit / 예외면 rollback. 커밋/롤백은 메서드 실행 결과에 따라 결정되므로 메서드 실행이 그 사이에 온다. 즉 프록시 기반이라 3절의 한계를 그대로 물려받는다.
  • 전파(propagation): REQUIRED(기본·있으면 참여, 없으면 새로)·REQUIRES_NEW(항상 새 트랜잭션·기존 일시정지)·NESTED(중첩 세이브포인트) 등.
  • readOnly=true: JPA flush 모드를 MANUAL로 → 변경 감지(dirty checking) 생략·DB 읽기 최적화 힌트.

깊이 — 3대 함정(★ 최다 단골):

  1. self-invocation: 같은 클래스 내부에서 this.txMethod()로 호출하면 프록시를 안 거쳐 트랜잭션·전파(REQUIRES_NEW)가 무시된다. → 빈 분리·TransactionTemplate·AspectJ.
  2. 기본 롤백은 Unchecked 예외만: RuntimeException/Error는 롤백, Checked 예외는 커밋(롤백하려면 rollbackFor=Exception.class).
  3. private/final 메서드엔 미적용(프록시 불가).

꼬리질문: "읽기에 트랜잭션을 왜?" → 일관된 스냅샷·readOnly 최적화. / "REQUIRES_NEW를 self-call하면?" → 무시(프록시 우회).

함정: ❌ "예외만 던지면 무조건 롤백" → Checked는 커밋. ❌ "내부 메서드에 @Transactional 붙이면 적용" → self-invocation으로 무시.


5. 영속성 컨텍스트 — 4대 기능 (★★★ 단골)

실제 질문: "ORM/JPA를 쓰는 이유는?" · "**영속성 컨텍스트(Persistence Context)**를 설명하라." · "1차 캐시·쓰기 지연·변경 감지가 뭔가요?" (VSFe·ecsimsw)

  • 1차 캐시: (id, 엔티티) 맵 → 같은 트랜잭션 내 동일 PK 조회는 DB 안 가고 캐시 반환(동일성 보장).
  • 쓰기 지연(write-behind): persist 시 INSERT를 쓰기지연 SQL 저장소에 모았다 flush/commit 시점에 일괄 전송.
  • 변경 감지(dirty checking): flush 시 엔티티와 스냅샷을 비교 → 변경분 자동 UPDATE(별도 save 불필요).
  • 지연 로딩: 프록시로 두고 실제 사용 시점에 조회.

깊이: 영속성 컨텍스트 (준영속/detached)에선 dirty checking·지연 로딩이 안 된다 → LazyInitializationException(뷰에서 지연 컬렉션 접근).

꼬리질문: "1차 캐시 = 2차 캐시?" → 아니다. 1차는 트랜잭션/EntityManager 범위, 2차는 애플리케이션 범위(공유). / "변경 감지가 동작 안 한다?" → 준영속이거나 readOnly·flush 안 됨.

함정: ❌ "엔티티를 바꾸면 항상 UPDATE된다" → 영속 상태 + flush여야.


6. 지연/즉시 로딩·프록시·N+1 (★★★ 최다 단골)

실제 질문: "N+1 문제를 설명하라." · "지연 로딩과 즉시 로딩의 차이는?" · "N+1을 어떻게 해결하나요?" (VSFe·incheol-jung)

  • Lazy vs Eager: 연관 엔티티를 언제 조회하느냐. Lazy는 프록시로 두고 접근 시 초기화, Eager는 즉시 조인/추가 조회.
  • N+1: 연관 엔티티를 건별로 추가 쿼리 N번 더 보냄(1번 조회 + N번 연관). 즉시 로딩도 N+1에서 자유롭지 않다(시점만 다름).

해결책 비교(★):

방법특징·한계
fetch join(JPQL)한 쿼리 조인. ⚠️ 컬렉션 fetch join은 페이징 불가(메모리 페이징 경고)·카테시안 중복(distinct)·둘 이상 Bag(=List) 동시 fetch join 불가(MultipleBagFetchException·Set이면 회피)
@EntityGraphLEFT OUTER JOIN으로 한 번에·메서드 단위
@BatchSize / default_batch_fetch_sizeIN 쿼리로 N개 묶어 1~몇 회 → 컬렉션 + 페이징 양립

꼬리질문: "컬렉션 fetch join에 페이징하면?" → 페이징을 메모리에서 처리(firstResult/maxResults … applying in memory 경고·OOM 위험) → BatchSize. / "MultipleBagFetchException?" → 둘 이상 컬렉션(Bag=List) 동시 fetch join → 하나만 fetch join + 나머지 BatchSize. / "왜 즉시 로딩도 N+1?" → JPQL은 연관을 모르고 먼저 실행 후 연관을 건별 조회.

함정: ❌ "지연 로딩 쓰면 N+1 없다" → 루프 접근 시 발생. ❌ "fetch join이면 무조건 해결" → 컬렉션 페이징 깨짐.


7. 기타 단골 — Filter/Interceptor·DispatcherServlet (★)

실제 질문: "Filter와 Interceptor의 차이는?" · "DispatcherServlet의 역할은? 수많은 @Controller를 어떻게 구분하나요?" (VSFe·WeareSoft)

  • Filter(서블릿 컨테이너 레벨·스프링 밖) vs Interceptor(스프링 MVC 레벨·핸들러 접근 가능).
  • DispatcherServlet(Front Controller): 모든 요청을 받아 HandlerMapping으로 @Controller를 찾고 → HandlerAdapter 실행 → ViewResolver. 어노테이션이 동작하는 원리 = 리플렉션·프록시.

꼬리질문: "Filter는 언제?" → 인증·로깅·인코딩 등 스프링 밖 공통. / "Lombok @Data를 잘 안 쓰는 이유?" → 무분별한 setter·equals/hashCode 위험.


8. 데이터 접근 기술 비교·MVC 변천 (★ 기출 보강·2026-07)

실제 질문: "MyBatis와 JPA의 차이·장단점은? 언제 무엇을 쓰나?" · "Spring JDBC(JdbcTemplate) 는 무엇을 해주나?" · "DAO·DTO·VO의 차이는?" · "MVC1과 MVC2의 차이는?" (velog·gyoogle)

MyBatis(SQL Mapper) vs JPA(ORM):

  • JPA: 객체-테이블 매핑, 메서드 호출로 쿼리 생성 → 객체 중심·생산성·DB 비의존·기본 CRUD/페이징 내장. 단 러닝커브(영속성 컨텍스트·연관관계·N+1)·복잡 통계 쿼리는 부담. SQL을 몰라도 되는 게 아니라 잘할수록 잘 씀.
  • MyBatis: SQL을 XML/애노테이션에 직접 작성해 매핑 → 복잡 조인·튜닝·동적 쿼리에 직관적·세밀한 제어. 단 DB 종속(예: Oracle 페이징을 MySQL로 못 옮김)·보일러플레이트(매퍼/인터페이스) 많음.
  • 언제: 도메인 중심·CRUD 다수·빠른 개발 → JPA / 복잡한 통계·레거시 SQL·정교한 튜닝 → MyBatis. 실무는 JPA(기본) + 복잡 조회는 QueryDSL/native, 또는 두 기술 혼용.

Spring JDBC(JdbcTemplate): JDBC 저수준 반복(Connection 열고 닫기·Statement·ResultSet·예외·트랜잭션)을 프레임워크에 위임. JdbcTemplate·NamedParameterJdbcTemplate·RowMapper 제공, JDBC의 SQLException(checked)을 스프링 DataAccessException(unchecked) 계층으로 변환해 전파. ORM과 SQL 매퍼의 중간(직접 SQL + 보일러플레이트 제거).

DAO / DTO / VO:

  • DAO(Data Access Object): DB 접근(CRUD) 로직 캡슐화. 인터페이스/구현 분리.
  • DTO(Data Transfer Object): 계층 간 데이터 전달용. 로직 없이 필드 + getter/setter(가변).
  • VO(Value Object): 값 자체·불변(read-only)·동등성은 값으로 판단(DTO와 달리 setter 없음).

MVC1 vs MVC2:

  • MVC1: JSP가 컨트롤러(자바)+뷰(HTML) 둘 다 담당 → 구조 단순·소형에 적합, 유지보수 어려움·역할 분리 안 됨.
  • MVC2: 요청을 컨트롤러(서블릿) 가 받아 Model 처리 후 View(JSP)로 → 관심사 분리·유지보수·역할 분담. 오늘날 Spring MVC(DispatcherServlet)가 MVC2의 발전형(프론트 컨트롤러).

함정: ❌ "JPA를 쓰면 SQL 몰라도 된다" → 오히려 SQL 이해가 성능(N+1·페치)의 전제. ❌ "VO=DTO" → VO는 불변·값 동등성, DTO는 가변 전달 객체.

(출처: Spring — Data Access·JdbcTemplate·MyBatis 공식·하이버네이트 문서 교차검증. 기출 프레이밍: docs/02-velog.)


흔한 오답·함정 정리

  • 내부 메서드 @Transactional 적용됨 → self-invocation으로 무시.
  • 예외 던지면 무조건 롤백 → Checked는 커밋(rollbackFor 필요).
  • 지연 로딩이면 N+1 없음 → 루프 접근 시 발생.
  • fetch join이면 페이징 OK → 컬렉션은 메모리 페이징(BatchSize).
  • 프로토타입 주입하면 매번 새 객체 → 싱글톤이 들고 있으면 고정.

한국 면접 단골 Q&A (답변 골격)

질문핵심 답
IoC/DI·왜 Bean제어 위임·주입·생명주기/AOP/테스트
생성자 주입 권장불변·순환참조 조기 발견·테스트
싱글톤+프로토타입고정됨 → ObjectProvider/@Lookup
AOP·프록시횡단 관심사·JDK/CGLIB·내부 호출 미적용
@Transactional 동작AOP 프록시·begin/commit/rollback
self-invocation내부 호출은 프록시 우회·무시
롤백 기준Unchecked만 기본·Checked는 rollbackFor
영속성 컨텍스트1차캐시·쓰기지연·변경감지·지연로딩
N+1연관 건별 N쿼리·즉시로딩도 발생
N+1 해결fetch join/EntityGraph/BatchSize
컬렉션 fetch join 페이징메모리 페이징→BatchSize

꼬리질문 대비 (상 난이도)

  • "@Transactional이 안 먹어요" → self-invocation·private/final·Checked 예외·프록시 미적용.
  • "MultipleBagFetchException?" → 컬렉션 둘 동시 fetch join → 하나만 + BatchSize.
  • "OSIV(Open Session In View)?" → 뷰까지 영속성 유지(LazyInit 방지)·but 커넥션 오래 점유 → 트랜잭션 밖 DTO 권장.
  • "변경 감지로 의도치 않은 UPDATE?" → 영속 엔티티 수정 → flush 시 자동 → readOnly·DTO로 제어.
  • "2차 캐시?" → 애플리케이션 범위 공유 캐시(엔티티 캐시)·정합성 주의.

한 줄 요약 — IoC/DI(생성자 주입·왜 Bean=생명주기/AOP/테스트)·Bean 스코프(싱글톤+프로토타입은 고정→Provider). AOP=프록시(JDK/CGLIB·내부 호출 미적용). @Transactional=AOP 프록시(self-invocation 무시·Unchecked만 기본 롤백·private/final 불가·전파 REQUIRED/REQUIRES_NEW·readOnly). 영속성 컨텍스트=1차캐시·쓰기지연·변경감지·지연로딩. N+1(즉시로딩도 발생)→fetch join(컬렉션 페이징 불가·MultipleBagFetchException)/EntityGraph/BatchSize.

(출처 — 한국 면접 기출·교차검증 2026-06: VSFe/Tech-Interview — JAVA_SPRING·WeareSoft — spring·ecsimsw — 영속성 컨텍스트·incheol-jung — N+1 · Spring Framework Reference(Transaction·AOP)·하이버네이트 문서 교차검증.)

Java·객체지향 — OOP·JVM·GC·컬렉션API·REST 설계 — 멱등성·상태코드·버저닝·GraphQL