2025-04-08T19:56:24

This commit is contained in:
2025-04-08 19:56:24 +09:00
parent a75a1dbd0f
commit eef061c1c9
100 changed files with 18639 additions and 0 deletions

337
docs/35_jdbc_client.md Normal file
View File

@@ -0,0 +1,337 @@
# **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`를 적극 활용하는 것이 추천됩니다!** 🚀