# **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
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>6.x.x</version>
</dependency>
```

---

### **📌 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<User> 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<User> 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<User> 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<T> clazz)` | 결과를 **자동 매핑하여 반환** | `.query(User.class).list()` |
| `query(RowMapper<T> rowMapper)` | 커스텀 **RowMapper를 사용한 쿼리 실행** | `.query((rs, rowNum) -> new User(rs.getLong("id"), rs.getString("name"))).list()` |
| `queryForObject(Class<T> clazz)` | 단일 결과 반환 (`Optional<T>` 지원) | `.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<Connection, T> callback)` | JDBC 커넥션을 직접 다룰 때 사용 | `.execute(conn -> conn.prepareStatement("DELETE FROM users").executeUpdate())` |

---

## **2. JdbcClient 주요 메서드 예제**
### **📌 단일 데이터 조회 (`queryForObject`)**
```java
Optional<User> 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<User> 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<User> 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`를 적극 활용하는 것이 추천됩니다!** 🚀