Post

Spring DB 1

JDBC

등장 이유

  • 보통 애플리케이션은 클라이언트에서 요청이 들어오면 서버에서 DB에서 요청에 맞는 데이터를 알맞게 처리한다.

  • 이런 과정에서 애플리케이션 서버와 DB는 서로 통신을 해야한다.

    image

    • 커넥션 연결 - TCP/IP를 사용
    • SQL 전달
    • 결과 응답
  • 위와 같은 과정을 통해 DB와 통신하는데 여기서 문제가 발생한다.

  • 위 그림에서 Mysql -> Oracle DB로 바뀔 시 각각 데이터 베이스마다 케넥션 연결과 같은 사용 방법이 다 다르다는 점이다.

  • 그렇기 때문에 DB를 바꾸면 애플리케이션의 모든 DB를 다 고쳐야되는 문제가 발생하고 이런 문제를 해결하기 위해 JDBC라는 자바 표준이 등장한다.

JDBC 표준 인터페이스

  • JDBC(Java Database Connectivity)는 자바에서 데이터베이스에 접속할 수 있도록 하는 자바 API

    image

  • 대표 기능

    • java.sql.Connection - 연결
    • java.sql.Statement - SQL을 담은 내용
    • java.sql.ResultSet - SQL 요청 응답
  • 자바는 위와 같은 표준 인터페이스를 정의해두었다.

  • 개발자는 이런 인터페이스를 사용해 개발하고 각 DB 벤더(회사)에서 제공하는 JDBC 드라이버만 갈아끼우면 된다.

  • 하지만 이럼에도 일부 사용법의 차이는 해결하지 못했다. 대표적으로 페이징 SQL은 DB마다 문법의 차이가 있다. 이런 부분은 JPA로 해결 가능하다.

커넥션 풀

  • DB와 애플리케이션이 연결되기 위해선 Connection을 생성하고 해당 커넥션에 연결하는 작업이 필요하다.
  • 하지만 매 요청마다 커넥션을 만드는 것은 TCP/IP로 매번 DB와 통신해야되며 커넥션을 만드는 과정도 리소스를 많이 잡아먹는다.
  • 이런 문제를 해결하기 위해 DB는 커넥션 풀이라는 개념을 도입했다.
  • 커넥션 풀이란 미리 애플리케이션 로딩 시점에 커넥션을 미리 만들어 놓고 보관한 뒤 풀에서 꺼내서 쓰는 것을 말한다.
  • 이런 방법을 통해 다양한 이점을 얻을 수 있다. 그렇기 때문에 스프링에서는 hikariCP라는 오픈 소스를 사용해 커넥션 풀을 관리한다.

DataSource

  • 커넥션을 얻는 방법은 매우 다양하다.
  • 직접 JDBC DriverManager를 사용하거나, 커넥션 풀인 hikariCP를 사용하는 등 매우 다양하기 때문에 변경을 쉽게 하기 위해 스프링에서는 DataSource라는 이름으로 추상화했다.
  • DataSource를 사용하면 커넥션을 얻어오는 방식을 바꾸더라도 기존 코드를 변경하지 않고 사용이 가능하다.

Transaction

ACID

  • 원자성(Automicity) - 트랜잭션 내에서 실행된 작업들은 마치 하나의 작업인 것처럼 모두 성공하거나 실패해야함
  • 일관성(Consistency) - 모든 트랜잭션은 일관성 있는 데이터베이스 상태를 유지해야함. 예를 들어 db에서 정한 무결성 제약 조건을 항상 만족해야함
  • 격리성(Isolation) - 동시에 실행되는 트랜잭션들이 서로에게 영향을 미치지 않도록 격리해야함. 격리성은 동시성과 관련된 성능 이슈로 격리 수준을 선택 가능
  • 지속성(Durability) - 트랜잭션을 성공적으로 끝내면 그 결과가 항상 기록되어야함. 중간에 시스템에 문제가 발생해도 데이터베이스 로그 등을 사용해 성공한 트랜잭션 내용을 복구해야함

DB 연결과 DB 세션

  • 애플리케이션에서 DB에 접근할 때 DB 서버에 연결을 요청하고 커넥션을 맺는다.

  • 이때 DB 서버는 내부에서 세션이라는 것을 만들어 해당 요청을 실행하게 된다.

  • DB의 모든 기능은 이 세션을 통해 실행되며, 커넥션을 닫거나, 세션을 강제로 종료하면 그때 세션이 종료된다.

    image

  • 이와 같이 10개의 커넥션을 생성하면 세션도 10개 만들어진다.

DB 락

  • 만약 세션 1이 트랜잭션을 시작하고 데이터를 수정한 뒤 커밋을 수행하지 않았는데 다른 세션 2가 해당 데이터에 접근해 다른 값으로 데이터를 수정한다면 어떻게 될까?
  • 이런 일이 발생한다면 트랜잭션의 원자성이 깨지게 되는 것이다. 이런 문제를 해결하기 위해 DB는 트랜잭션이 시작되면 다른 세션에서 접근할 수 없게 막는 DB 락이라는 개념을 제공한다.
  • DB 락은 서로 다른 세션이 같은 데이터에 접근할 때 락을 획득한 사람만이 데이터를 변경할 수 있게 해주는 장치이다.
  • DB 락은 조회 시 사용하지 않는다. 하지만 만약 조회 시에도 락을 획득하고 다른 세션이 데이터에 접근하는 것을 막을 수 있는데 select for update 를 사용하면 조회 시에도 락을 획득하게 된다.

트랜잭션 추상화

  • 우리의 애플리케이션은 보통 프리젠테이션 계층과 서비스 계층, 데이터 접근 계층으로 나뉜다.

  • 이 중 프리젠테이션 계층은 웹과 관련된 계층이고 서비스 계층은 비즈니스 로직과 관련된 계층, 데이터 접근 계층은 DB와 관련된 계층이다.

  • 이때 서비스 계층은 중요한 점이 있는데 순수한 자바 코드로만 작성해야 된다는 점이다.

  • 만약 서비스 계층이 특정 DB에 종속적으로 구현한다면 데이터 접근 계층에서 적용한 기술을 다른 기술로 바꿀 때 서비스 계층 또한 코드를 다 바꿔야한다.

  • 또 트랜잭션의 경우도 서비스 계층에서 시작하는게 일반적인데 각 DB 접근 기술마다 트랜잭션을 시작하는 방법이 다른다는 문제도 있다.

    image

  • 이런 문제를 해결하기 위해 스프링은 트랜잭션 기술을 추상화한 트랜잭션 매니저를 제공해준다.

    image

트랜잭션 동기화

  • 스프링의 트랜잭션 매니저는 크게 두 가지 역할을 한다

    • 트랜잭션 추상화
    • 리소스 동기화
  • 여기서 리소스 동기화란 트랜잭션이 시작하고 끝까지 같은 DB 커넥션을 유지해야한다. 서비스 계층에서 시작된 커넥션을 데이터 접근 계층까지 유지해야하는건데 이런 부분을 트랜잭션 매니저가 도와준다.

    image

  • 트랜잭션 매니저는 트랜잭션 동기화 매니저를 제공한다. 동기화 매니저는 쓰레드 로컬을 사용해 커넥션을 동기화 해준다.

트랜잭션 템플릿

  • 트랜잭션 매니저를 사용하면 많은 부분 추상화가 가능해진다.

  • 하지만 트랜잭션 매니저만으로는 아직 해결하지 못한 부분이 있는데 바로 트랜잭션 안에서 로직 성공시 커밋하고 실패시 롤백하는 부분은 아직 남아있다.

  • 이런 공통 부분을 스프링에서는 템플릿 콜백 패턴을 적용해 해결했다.

  • 트랜잭션 템플릿 사용 로직

    1
    2
    3
    4
    5
    6
    7
    8
    
    txTemplate.executeWithoutResult((status) -> { 
    try {
    //비즈니스 로직
    bizLogic(fromId, toId, money);
        } catch (SQLException e) {
    throw new IllegalStateException(e);
        } 
    })
    

트랜잭션 AOP

  • 하지만 이럼에도 아직 서비스 계층에 비즈니스 로직을 제외한 try catch 문이 들어있는 등 완벽히 비즈니스 로직만 남기지 못했다.

  • 이런 부분은 스프링은 AOP를 사용해 해결했다.

    image

  • @Transaction 어노테이션이 바로 AOP를 적용하는 어노테이션이다.

  • 참고) 스프링의 트랜잭션 AOP를 위한 포인트컷, 어드바이스, 어드바이저

    • 어드바이저: BeanFactoryTransactionAttributeSourceAdvisor
    • 포인트컷: TransactionAttributeSourcePointcut
    • 어드바이스: TransactionInterceptor

image

예외

  • 체크 예외를 언체크 예외로 변환할 때는 꼭 기존 예외를 포함시켜야한다.

  • 언체크 예외의 경우 개발자가 놓칠 수 있기 때문에 문서화가 중요하다.

  • DB 관련 예외들은 DB 접근 기술마다 던지는 예외의 종류가 다르다.

  • 그렇기 때문에 DB 접근 기술 관련 예외는 스프링이 추상화해서 제공한다.

    image

  • 위와 같이 언체크 예외를 상속 받은 예외들을 제공하는데 크게 2가지로 구분할 수 있다.

    • Transient - 일시적이라는 뜻으로, 다시 시도했을 때 성공 가능성이 있음, 쿼리 타임아웃이나 락과 관련된 오류
    • NonTransient - 성공 가능성 X, SQL 문법 오류 등
  • 스프링은 예외 변환기를 통해 SQLException의 ErrorCode에 맞는 적절한 스프링 데이터 접근 예외로 변환해준다.

출처)

[스프링 DB 1편 - 데이터 접근 핵심 원리 강의 - 대시보드인프런 (inflearn.com)](https://www.inflearn.com/course/스프링-db-1/dashboard)
This post is licensed under CC BY 4.0 by the author.