# **Spring JDBC Client 소개 및 활용** Spring 6부터 도입된 **`JdbcClient`**는 기존 `JdbcTemplate`보다 **더 간결하고 현대적인 API**를 제공합니다. 특히, **메서드 체이닝과 함수형 스타일의 코드 작성이 가능**하여, 기존 JDBC 코드보다 훨씬 직관적인 사용이 가능합니다. 이 글에서는 `JdbcClient`의 주요 기능과 사용법을 **예제와 함께 설명**하겠습니다. --- ## **1. JdbcClient란?** `JdbcClient`는 **Spring 6 및 Spring Boot 3에서 새롭게 추가된 기능**으로, 기존 `JdbcTemplate`이 가진 **장점은 유지하면서도, 코드의 가독성과 유지보수성을 개선한 API**입니다. ✔ 기존 `JdbcTemplate`은 **보일러플레이트 코드가 많음** ✔ `JdbcClient`는 **메서드 체이닝을 지원하여 코드가 간결해짐** ✔ **SQL 실행 및 데이터 조회를 직관적으로 처리 가능** --- ## **2. JdbcClient 사용법** ### **📌 2.1 의존성 추가** Spring Boot 3 이상에서는 **자동으로 포함**되므로 별도 의존성 추가는 필요 없습니다. 하지만, Spring Framework 6에서 단독으로 사용할 경우 다음과 같은 의존성이 필요합니다. ```xml org.springframework spring-jdbc 6.x.x ``` --- ### **📌 2.2 `JdbcClient` 빈 설정** `JdbcClient`는 **DataSource를 기반으로 생성**됩니다. ```java import org.springframework.jdbc.core.simple.JdbcClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.sql.DataSource; @Configuration public class DatabaseConfig { @Bean public JdbcClient jdbcClient(DataSource dataSource) { return JdbcClient.create(dataSource); } } ``` ✔ **`JdbcClient.create(dataSource)`** 를 사용하여 인스턴스 생성 ✔ **Spring Boot 환경에서는 자동 설정되므로 따로 등록할 필요 없음** --- ## **3. CRUD 예제** ### **📌 3.1 데이터 삽입 (INSERT)** ```java import org.springframework.jdbc.core.simple.JdbcClient; import org.springframework.stereotype.Repository; @Repository public class UserRepository { private final JdbcClient jdbcClient; public UserRepository(JdbcClient jdbcClient) { this.jdbcClient = jdbcClient; } public int insertUser(String name, String email) { return jdbcClient.sql("INSERT INTO users (name, email) VALUES (:name, :email)") .param("name", name) .param("email", email) .update(); } } ``` ✔ `sql("INSERT INTO users ...")` → **SQL 작성** ✔ `param("name", name)` → **이름 기반 파라미터 바인딩** ✔ `update()` → **INSERT/UPDATE/DELETE 실행** --- ### **📌 3.2 단일 데이터 조회 (SELECT - 단건 검색)** ```java import org.springframework.jdbc.core.simple.JdbcClient; import org.springframework.stereotype.Repository; import java.util.Optional; @Repository public class UserRepository { private final JdbcClient jdbcClient; public UserRepository(JdbcClient jdbcClient) { this.jdbcClient = jdbcClient; } public Optional findById(Long id) { return jdbcClient.sql("SELECT * FROM users WHERE id = :id") .param("id", id) .query(User.class) .optional(); } } ``` ✔ `query(User.class)` → **자동으로 User 객체로 매핑** ✔ `optional()` → **결과가 없을 경우 Optional.empty() 반환** --- ### **📌 3.3 여러 데이터 조회 (SELECT - 리스트 검색)** ```java import org.springframework.jdbc.core.simple.JdbcClient; import org.springframework.stereotype.Repository; import java.util.List; @Repository public class UserRepository { private final JdbcClient jdbcClient; public UserRepository(JdbcClient jdbcClient) { this.jdbcClient = jdbcClient; } public List findAll() { return jdbcClient.sql("SELECT * FROM users") .query(User.class) .list(); } } ``` ✔ `query(User.class).list()` → **리스트 형태로 결과 반환** --- ### **📌 3.4 데이터 업데이트 (UPDATE)** ```java import org.springframework.jdbc.core.simple.JdbcClient; import org.springframework.stereotype.Repository; @Repository public class UserRepository { private final JdbcClient jdbcClient; public UserRepository(JdbcClient jdbcClient) { this.jdbcClient = jdbcClient; } public int updateEmail(Long id, String email) { return jdbcClient.sql("UPDATE users SET email = :email WHERE id = :id") .param("email", email) .param("id", id) .update(); } } ``` ✔ `update()`를 사용하여 데이터를 수정 --- ### **📌 3.5 데이터 삭제 (DELETE)** ```java import org.springframework.jdbc.core.simple.JdbcClient; import org.springframework.stereotype.Repository; @Repository public class UserRepository { private final JdbcClient jdbcClient; public UserRepository(JdbcClient jdbcClient) { this.jdbcClient = jdbcClient; } public int deleteById(Long id) { return jdbcClient.sql("DELETE FROM users WHERE id = :id") .param("id", id) .update(); } } ``` ✔ `update()`를 사용하여 DELETE 실행 --- ## **4. RowMapper 없이 자동 매핑하기** 기존 `JdbcTemplate`에서는 **RowMapper를 사용하여 수동으로 매핑**해야 했지만, `JdbcClient`는 **자동으로 Java 객체와 매핑**할 수 있습니다. ```java import org.springframework.jdbc.core.simple.JdbcClient; import org.springframework.stereotype.Repository; import java.util.List; @Repository public class UserRepository { private final JdbcClient jdbcClient; public UserRepository(JdbcClient jdbcClient) { this.jdbcClient = jdbcClient; } public List findAll() { return jdbcClient.sql("SELECT * FROM users") .query(User.class) .list(); } } ``` ✔ **User 객체의 필드명과 DB 컬럼명이 일치하면 자동 매핑** --- ## **5. 정리** | 기능 | JdbcTemplate | JdbcClient | |------|-------------|------------| | SQL 실행 방식 | `jdbcTemplate.query(sql, rowMapper)` | `jdbcClient.sql(sql).query(clazz)` | | 파라미터 바인딩 | `new MapSqlParameterSource()` 사용 | `.param("name", value)` 사용 | | 결과 변환 | `RowMapper` 필요 | **자동 매핑 가능** | | 코드 스타일 | 절차 지향 | 함수형 스타일 | | 가독성 | 비교적 복잡 | **간결하고 직관적** | ✔ `JdbcClient`는 **Spring Boot 3 이상에서 사용 가능** ✔ 기존 `JdbcTemplate` 대비 **더 간결하고 직관적인 코드 작성 가능** ✔ **자동 매핑 기능 제공**하여 `RowMapper` 작성 불필요 Spring Boot 3 이상을 사용한다면, `JdbcTemplate` 대신 **더 강력하고 간결한 `JdbcClient`를 활용하는 것이 추천됩니다!** 🚀 --- # **Spring `JdbcClient` 주요 메서드 정리** Spring 6 및 Spring Boot 3에서 도입된 `JdbcClient`는 **기존 `JdbcTemplate`보다 간결한 API**를 제공합니다. 아래는 `JdbcClient`의 주요 메서드를 **설명과 함께 표로 정리**한 것입니다. --- ## **1. JdbcClient 주요 메서드 정리** | 메서드 | 설명 | 예제 | |--------|------|------| | `sql(String sql)` | 실행할 SQL을 설정 | `jdbcClient.sql("SELECT * FROM users")` | | `param(String name, Object value)` | **이름 기반** 파라미터 바인딩 | `.param("id", 1L)` | | `paramSource(SqlParameterSource params)` | **Map 또는 Bean**을 파라미터로 전달 | `.paramSource(new MapSqlParameterSource(Map.of("id", 1L)))` | | `query(Class clazz)` | 결과를 **자동 매핑하여 반환** | `.query(User.class).list()` | | `query(RowMapper rowMapper)` | 커스텀 **RowMapper를 사용한 쿼리 실행** | `.query((rs, rowNum) -> new User(rs.getLong("id"), rs.getString("name"))).list()` | | `queryForObject(Class clazz)` | 단일 결과 반환 (`Optional` 지원) | `.queryForObject(User.class).optional()` | | `list()` | 여러 개의 결과를 리스트로 반환 | `.query(User.class).list()` | | `optional()` | 단일 결과를 **Optional**로 반환 | `.queryForObject(User.class).optional()` | | `update()` | INSERT, UPDATE, DELETE 실행 | `.sql("UPDATE users SET name = :name WHERE id = :id").update()` | | `execute(Function callback)` | JDBC 커넥션을 직접 다룰 때 사용 | `.execute(conn -> conn.prepareStatement("DELETE FROM users").executeUpdate())` | --- ## **2. JdbcClient 주요 메서드 예제** ### **📌 단일 데이터 조회 (`queryForObject`)** ```java Optional user = jdbcClient.sql("SELECT * FROM users WHERE id = :id") .param("id", 1L) .queryForObject(User.class) .optional(); ``` ✔ **SQL 실행 후 `User.class`로 자동 매핑** ✔ 결과가 없을 경우 `Optional.empty()` 반환 --- ### **📌 여러 데이터 조회 (`query` + `list`)** ```java List users = jdbcClient.sql("SELECT * FROM users") .query(User.class) .list(); ``` ✔ `User.class` 타입으로 리스트 변환 --- ### **📌 데이터 삽입 (`update`)** ```java int rows = jdbcClient.sql("INSERT INTO users (name, email) VALUES (:name, :email)") .param("name", "Alice") .param("email", "alice@example.com") .update(); ``` ✔ `update()`를 실행하여 INSERT 수행 ✔ 반영된 행 수(`int`) 반환 --- ### **📌 데이터 삭제 (`update`)** ```java int rowsDeleted = jdbcClient.sql("DELETE FROM users WHERE id = :id") .param("id", 1L) .update(); ``` ✔ 삭제된 행 수 반환 --- ### **📌 수동 매핑 (RowMapper 사용)** ```java List users = jdbcClient.sql("SELECT * FROM users") .query((rs, rowNum) -> new User(rs.getLong("id"), rs.getString("name"))) .list(); ``` ✔ `RowMapper`를 직접 정의하여 결과 매핑 가능 --- ## **3. 정리** - `JdbcClient`는 기존 `JdbcTemplate`보다 **간결하고 현대적인 API** 제공 - **메서드 체이닝 지원**으로 **코드 가독성이 향상됨** - `queryForObject()`, `query()`를 활용하면 **자동 매핑 가능** - `update()`를 사용하여 **INSERT, UPDATE, DELETE 실행** Spring Boot 3 이상을 사용한다면 **`JdbcTemplate`보다 `JdbcClient`를 적극 활용하는 것이 추천됩니다!** 🚀