DataSource와 Connection

3줄 요약

  • DataSource는 애플리케이션이 데이터베이스 커넥션을 얻는 표준 진입점이다.
  • Spring Boot는 설정과 의존성을 바탕으로 DataSource와 커넥션 풀을 자동 구성할 수 있다.
  • 커넥션은 비싼 자원이므로 트랜잭션 경계, 풀 크기, 쿼리 시간, 외부 호출이 함께 관리되어야 한다.

핵심 정리

  • JDBC의 실제 DB 통신은 Connection을 통해 이루어진다. DataSource는 Connection 생성과 풀링을 추상화한다.
  • 커넥션 풀은 매 요청마다 커넥션을 새로 만드는 비용을 줄이지만, 무한 자원이 아니다.
  • 트랜잭션이 길어지면 커넥션을 오래 점유한다. 외부 API 호출을 트랜잭션 안에 넣으면 풀 고갈의 원인이 된다.
  • Spring은 트랜잭션과 리소스 동기화를 통해 같은 트랜잭션 안에서 같은 Connection을 사용하게 도와준다.
  • Spring JDBC는 DataSourceUtils와 트랜잭션 동기화 인프라를 통해 현재 트랜잭션에 묶인 Connection을 재사용하게 한다. 직접 dataSource.getConnection()을 호출하고 닫는 방식은 이 흐름을 깨뜨릴 수 있다.
  • 운영에서는 느린 쿼리, 풀 대기 시간, active connection 수를 함께 관찰해야 한다.

헷갈리는 지점

  • DataSource를 단순 설정 객체로 생각하기 쉽다. 코드에서 직접 많이 보이지 않기 때문이다.
    • 핵심은 DataSource가 DB 커넥션을 얻는 핵심 인프라라는 점이다.
    • Repository와 트랜잭션의 실제 DB 접근은 결국 커넥션 자원으로 이어진다.
  • 커넥션 풀 크기를 키우면 DB 성능 문제가 해결된다고 보기 쉽다. 대기 시간이 줄어들 것처럼 보이기 때문이다.
    • 핵심은 DB가 처리할 수 있는 동시 작업량에도 한계가 있다는 점이다.
    • 풀 크기 조정은 쿼리 최적화와 트랜잭션 범위 조정 뒤에 판단해야 한다.
  • 트랜잭션이 없으면 커넥션 관리도 필요 없다고 생각하기 쉽다. commit/rollback이 없어서 가볍게 느껴지기 때문이다.
    • 핵심은 읽기 쿼리도 Connection을 사용한다는 점이다.
    • 모든 DB 접근은 풀과 timeout, 네트워크 비용의 영향을 받는다.

확인 질문

  • DataSource의 역할은 무엇인가?
    • 애플리케이션이 데이터베이스 Connection을 얻는 표준 진입점이다.
  • 트랜잭션이 길어지면 어떤 자원이 오래 점유되는가?
    • 데이터베이스 Connection과 lock 같은 DB 자원이 오래 점유될 수 있다.
  • 커넥션 풀 문제를 볼 때 함께 확인해야 할 것은 무엇인가?
    • 느린 쿼리, 트랜잭션 범위, 외부 호출, DB 처리량, active connection 수를 함께 봐야 한다.

JdbcTemplate와 예외 변환

3줄 요약

  • JdbcTemplate은 JDBC의 반복 코드와 예외 처리를 줄여 주는 Spring의 핵심 데이터 접근 도구다.
  • Spring은 SQL 예외를 기술 독립적인 DataAccessException 계층으로 변환한다.
  • 직접 JDBC를 쓰더라도 커넥션 획득, statement 정리, 결과 매핑, 예외 변환을 일관되게 유지해야 한다.

핵심 정리

  • 순수 JDBC는 Connection, PreparedStatement, ResultSet 정리 코드가 반복된다.
  • JdbcTemplate은 자원 정리와 예외 변환을 처리하고, 개발자는 SQL과 파라미터, RowMapper에 집중한다.
  • DataAccessException은 unchecked exception 계층이며, JDBC, JPA 등 구현 기술 차이를 애플리케이션 코드에서 덜 의식하게 한다.
  • JdbcTemplate은 설정된 뒤 thread-safe하게 공유해서 여러 Repository에 주입할 수 있다. 매 쿼리마다 새로 만들 필요가 없다.
  • JdbcTemplate은 ORM보다 SQL 제어력이 높고, 복잡한 조회나 성능 민감 쿼리에 적합할 수 있다.
  • 단순함 때문에 도메인 모델링까지 SQL 중심으로 끌려가면 서비스와 DB 스키마가 과하게 결합될 수 있다.

헷갈리는 지점

  • JdbcTemplate을 오래된 기술로 보기 쉽다. JPA와 Repository 예제가 더 자주 보이기 때문이다.
    • 핵심은 JdbcTemplate이 여전히 명시적 SQL 제어가 필요한 곳에서 강력하다는 점이다.
    • 복잡한 리포트 조회나 대량 처리에는 ORM보다 예측 가능할 수 있다.
  • DataAccessException을 잡아서 모두 복구해야 한다고 생각하기 쉽다. 예외 이름이 데이터 접근 실패를 구체적으로 알려 주기 때문이다.
    • 핵심은 대부분의 데이터 접근 예외는 즉시 복구하기 어렵다는 점이다.
    • 필요한 경우에만 도메인 예외로 변환하고, 나머지는 상위에서 일관되게 처리한다.
  • SQL이 코드에 보이면 설계가 나쁘다고 오해하기 쉽다. Repository 추상화가 더 깔끔해 보이기 때문이다.
    • 핵심은 SQL 노출 자체보다 적절한 계층에 SQL 책임이 모여 있는지가 중요하다는 점이다.
    • Repository 내부의 명시적 SQL은 오히려 성능과 의도를 선명하게 만들 수 있다.

확인 질문

  • JdbcTemplate이 줄여 주는 JDBC 반복 작업은 무엇인가?
    • 커넥션과 statement 관리, 결과 처리, 예외 변환 같은 반복 작업이다.
  • DataAccessException의 장점은 무엇인가?
    • 데이터 접근 기술별 예외를 Spring의 기술 독립적인 unchecked 예외 계층으로 다룰 수 있게 한다.
  • JdbcTemplate이 JPA보다 더 적합할 수 있는 경우는 언제인가?
    • 복잡한 SQL, 성능 민감 조회, 대량 처리처럼 SQL 제어력이 중요한 경우다.

Spring Data Repository

3줄 요약

  • Spring Data Repository는 반복적인 CRUD와 쿼리 작성 패턴을 인터페이스 선언으로 줄여 준다.
  • 메서드 이름 쿼리, @Query, Projection, Pageable, EntityGraph 등으로 조회 의도를 표현할 수 있다.
  • 편리함 때문에 긴 메서드 이름과 숨겨진 쿼리가 늘어나면 SQL 의도와 성능을 잃기 쉽다.

핵심 정리

  • Repository 인터페이스는 도메인 객체의 저장소 역할을 추상화한다.
  • Spring Data JPA는 메서드 이름을 분석해 JPQL 쿼리를 만들거나, @Query로 직접 정의한 쿼리를 실행한다.
  • findById 같은 일부 메서드는 식별자를 대상으로 하는 reserved method이므로, 이름이 항상 엔티티 필드명 그대로 해석된다고 보면 안 된다.
  • 쿼리가 단순하면 메서드 이름 쿼리가 빠르고 읽기 쉽다. 조건이 길고 조인이 많아지면 @Query, Specification, Querydsl, JdbcTemplate 같은 선택지를 검토한다.
  • Repository는 데이터 접근 경계이지 비즈니스 의사결정 위치가 아니다. 도메인 규칙은 Service나 Domain으로 올려야 한다.
  • Pagination, sorting, projection은 성능과 응답 모델을 함께 고려해 설계한다.

헷갈리는 지점

  • Repository 인터페이스만 만들면 데이터 접근 설계가 끝났다고 생각하기 쉽다. CRUD가 자동으로 제공되기 때문이다.
    • 핵심은 조회 요구와 성능 특성은 여전히 설계해야 한다는 점이다.
    • 어떤 aggregate를 어떤 fetch plan으로 가져올지 결정해야 한다.
  • 메서드 이름 쿼리가 길어져도 타입 안전하니 괜찮다고 보기 쉽다. 컴파일이 되기 때문이다.
    • 핵심은 긴 이름이 쿼리 의도와 유지보수를 흐릴 수 있다는 점이다.
    • 복잡한 쿼리는 명시적 @Query나 전용 조회 구현이 더 낫다.
  • Repository에 비즈니스 검증을 넣기 쉽다. DB 조회와 규칙 확인이 붙어 있기 때문이다.
    • 핵심은 Repository는 데이터 접근 방법을 숨기고, 규칙 판단은 유스케이스나 도메인이 맡는다는 점이다.
    • Repository 메서드는 가능하면 질문과 저장 행위에 집중한다.

확인 질문

  • Spring Data Repository의 핵심 가치는 무엇인가?
    • 반복적인 CRUD와 단순 쿼리 구현을 인터페이스 선언으로 줄이는 것이다.
  • 메서드 이름 쿼리가 길어질 때 검토할 대안은 무엇인가?
    • @Query, Projection, Specification, Querydsl, JdbcTemplate, 전용 Repository 구현을 검토한다.
  • Repository에 넣지 말아야 할 책임은 무엇인가?
    • 핵심 비즈니스 의사결정과 유스케이스 정책이다.

JPA Entity와 영속성 컨텍스트

3줄 요약

  • JPA의 영속성 컨텍스트는 엔티티 생명주기, 변경 감지, 1차 캐시, 지연 로딩을 관리한다.
  • 엔티티 조회와 연관관계 접근은 SQL 실행 시점과 다를 수 있으므로 트랜잭션 경계 안에서 이해해야 한다.
  • N+1 문제, 과도한 eager loading, DTO 변환 위치는 JPA 성능과 설계의 핵심 위험 지점이다.

핵심 정리

  • Entity는 단순 테이블 행 DTO가 아니라 식별자와 생명주기를 가진 영속 객체다.
  • 영속성 컨텍스트는 같은 트랜잭션 안에서 엔티티 동일성, 변경 감지, 쓰기 지연을 제공한다.
  • 지연 로딩은 필요한 시점에 연관 데이터를 가져오지만, 트랜잭션 밖에서 접근하면 예외나 추가 쿼리 문제가 생길 수 있다.
  • N+1은 목록 조회 후 각 엔티티의 연관관계를 반복 접근하면서 추가 쿼리가 N번 발생하는 대표 성능 문제다.
  • 해결은 무조건 eager loading이 아니라 fetch join, EntityGraph, batch size, 전용 조회 쿼리, DTO projection 중 요구에 맞는 선택이다.

헷갈리는 지점

  • Entity를 API 응답 DTO처럼 써도 된다고 생각하기 쉽다. 필드가 이미 필요한 데이터를 갖고 있기 때문이다.
    • 핵심은 Entity가 영속성 컨텍스트와 도메인 상태를 가진 객체라는 점이다.
    • API 응답은 별도 DTO로 잘라 영속성 경계와 외부 계약을 분리하는 편이 안전하다.
  • Lazy loading은 항상 성능 최적화라고 보기 쉽다. 당장 필요한 것만 가져온다는 설명 때문이다.
    • 핵심은 접근 패턴에 따라 Lazy가 N+1을 만들 수 있다는 점이다.
    • 목록 조회에서는 fetch plan을 명시적으로 설계해야 한다.
  • EAGER로 바꾸면 N+1이 해결된다고 생각하기 쉽다. 미리 가져오면 추가 쿼리가 없어질 것처럼 보이기 때문이다.
    • 핵심은 전역 eager는 불필요한 데이터 로딩과 다른 N+1 문제를 만들 수 있다는 점이다.
    • 조회 유스케이스별 fetch join이나 EntityGraph를 우선 검토한다.

확인 질문

  • 영속성 컨텍스트가 제공하는 대표 기능은 무엇인가?
    • 1차 캐시, 엔티티 동일성, 변경 감지, 쓰기 지연, 지연 로딩 지원이다.
  • N+1 문제는 언제 발생하는가?
    • 목록을 한 번 조회한 뒤 각 엔티티의 연관관계를 접근하면서 추가 쿼리가 N번 발생할 때 생긴다.
  • N+1을 해결하기 위해 무조건 EAGER로 바꾸면 안 되는 이유는 무엇인가?
    • 전역 로딩 전략이 불필요한 데이터 로딩과 다른 성능 문제를 만들 수 있기 때문이다.