[Spring Webflux:2편] R2DBC

최근 Spring WebFlux와 R2DBC의 조합이 많은 주목을 받고 있다. 이들은 MVC와 JDBC의 전통적인 조합을 넘어서, 현대적인 리액티브 프로그래밍 환경에서 훨씬 우수한 성능을 발휘한다고 알려져 있다. 그렇다면 R2DBC란 정확히 무엇이며, 왜 Spring WebFlux와 잘 맞는 것일까?

R2DBC?

R2DBC(Reactive Relational Database Connectivity)는 리액티브 프로그래밍을 지원하는 새로운 형태의 데이터베이스 연결 드라이버로, 논블록킹(non-blocking) 방식을 채택하고 있다. Reactive Relational Database Connectivity라는 단어에서 나타나다시피, R2DBC는 기존의 관계형 데이터베이스 예를 들어 MySQL이나 PostgreSQL을 리액티브 프로그래밍 환경에 맞게 연결해주는 중요한 역할을 한다.

논블록킹 방식으로 작동한다면 R2DBC는 그럼 어느 서버에서 논블록킹 처리가 발생하는지에 대한 의문이 있었다. 데이터베이스 서버의 스레드가 논블록킹 방식으로 작동한다는 것인지, 아니면 DB 서버를 호출하는 서버의 스레드가 논블록킹 방식으로 작동한다는 것인지 혼동되었다. 그러나 이는 R2DBC가 연결 드라이버임을 이해한다면 이는 데이터베이스 서버의 스레드가 아니라, DB 서버를 호출하는 서버 쪽의 스레드에서 비동기적으로 이루어지는 것임을 알 수 있다. 더 명확한 이해를 위해 스레드 관점에서 JDBC와 R2DBC가 어떻게 동작하는지 보자.

JDBC의 작동 원리
JDBC는 데이터베이스 요청을 처리할 때, 클라이언트 측 스레드가 데이터베이스 작업을 요청하고 그 응답을 기다리는 동안 해당 스레드가 블록된다. 이러한 방식은 동기적이며, 각 작업이 순차적으로 완료되기를 기다리는 방식으로 운영되기에 블록킹 방식이다.

R2DBC의 작동 원리
R2DBC는 리액티브 프로그래밍 모델을 따르는 비동기 및 논블록킹 연결 드라이버이다. 클라이언트 측에서 데이터베이스 요청을 보내면, R2DBC는 이 요청을 비동기적으로 처리하며, 이는 클라이언트 측의 스레드가 데이터베이스 서버로부터 응답을 기다리는 동안에도 다른 작업을 계속해서 수행할 수 있도록 한다. 따라서 R2DBC를 사용할 때, 논블록킹 처리는 DB를 호출하는 웹 서버 또는 애플리케이션 서버의 클라이언트 측 스레드에서 발생한다.

이러한 R2DBC의 특성은 안타깝게도 ORM인 JPA와의 호환성 문제를 야기한다. JPA는 블록킹 방식의 데이터베이스 작업을 가정하고 설계된 기술로, 각 데이터베이스 작업이 완료될 때까지 스레드를 대기 상태로 유지한다. 반면, R2DBC는 이러한 대기 없이 스레드를 계속 활용할 수 있는 비동기 처리를 지향하기 때문에 JPA와 같은 블록킹 기반의 ORM 프레임워크는 R2DBC와 함께 사용할 경우 성능 저하나 디자인 상의 제약을 초래할 수 있다. R2DBC를 최대한 활용하기 위해서는 직접 구현해야하는 부분도 많다.

R2DBC와 JDBC의 차이점

특징JDBCR2DBC
연결 방식동기적 (Synchronous)비동기적 (Asynchronous)
블록킹 여부블록킹 (Blocking)논블록킹 (Non-Blocking)
프로그래밍 모델명령-응답 방식 (Command-Response)리액티브 스트림 (Reactive Streams)
성능고성능을 제공하지만 스레드 대기 시간으로 인해 제한적일 수 있음동시 요청 처리에 우수하며 스레드 자원을 효율적으로 사용
스레드 사용스레드가 작업을 기다리는 동안 블록됨스레드가 작업을 기다리는 동안 다른 작업 수행 가능
리소스 관리더 많은 스레드 필요적은 수의 스레드로 더 많은 작업 처리 가능
데이터베이스 지원대부분의 관계형 데이터베이스 지원현재는 주요 RDBMS에 대한 지원이 확대되는 중
적합한 사용 케이스전통적인 웹 애플리케이션, 엔터프라이즈 애플리케이션고성능 및 스케일이 중요한 마이크로서비스, 비동기 처리 애플리케이션
R2DBC와 JDBC의 차이점

Spring WebFlux와의 시너지

Spring WebFlux는 비동기, 논블록킹 웹 프레임워크로, R2DBC와 함께 사용될 때 데이터베이스와 웹 계층 모두에서 리액티브 프로그래밍을 통한 일관된 비동기 처리를 제공한다. 이 조합은 애플리케이션의 전체적인 성능과 확장성을 극대화하며, 사용자에게 빠른 응답 시간을 제공한다. 자세한 사항은 MVC와 WebFlux, JDBC와 R2DBC의 실제 성능 차이를 잘 비교한 블로그 글을 참고하면 좋을 것 같다.

Spring에서 R2DBC 적용

implementation 'org.springframework.boot:spring-boot-starter-webflux'
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-data-r2dbc'
implementation 'com.github.jasync-sql:jasync-mysql:2.2.0'

R2DBC를 사용하려면 위 라이브러리를 추가해야한다.

@Table("rooms")
public class Room {
  @Id
  private Long id;
  private String name;
  private String type; // 예: 싱글, 더블, 스위트

  // getters and setters
}

@Table("reservations")
public class Reservation {
  @Id
  private Long id;
  private Long roomId;
  private LocalDate checkIn;
  private LocalDate checkOut;
  private String guestName;

  // getters and setters
}

  • @Id: R2DBC에서는 JPA와 마찬가지로 @Id 어노테이션이 해당 필드가 엔티티의 고유 식별자임을 나타낸다.
  • @Table: @Table 어노테이션은 엔티티 클래스가 데이터베이스의 어떤 테이블과 매핑될 것인지 명시한다.
public interface RoomRepository extends ReactiveCrudRepository<Room, Long> {
  Monp<Room> findById(Long id);
}

public interface ReservationRepository extends ReactiveCrudRepository<Reservation, Long> {
  Flux<Reservation> findByGuestName(String guestName);
}

[Spring Webflux:2편] R2DBC

댓글 남기기

Scroll to top