1️⃣ 동적 쿼리란?
- 고정된 SQL이 아니라 조건이나 사용자 입력에 따라 변화하는 쿼리 방식
- 쿼리를 미리 정의하지 않고, 실행 시점에 조건에 맞게 생성
2️⃣동적 SQL의 필요성
- 다양한 검색 조건이나 필터링이 필요한 기능 구현에 필수적
- 사용자의 입력값에 따라 유연한 쿼리 생성 가능
3️⃣ 전통적인 JDBC 방식으로 동적 SQL 구현하기
StringBuilder sql = new StringBuilder("SELECT * FROM user WHERE 1=1");
List<Object> parameters = new ArrayList<>();
if(name != null) {
sql.append(" AND name = ?");
parameters.add(name);
}
if(age != null) {
sql.append(" AND age = ?");
parameters.add(age);
}
// PreparedStatement로 값 바인딩 후 실행
4️⃣ JPA의 Criteria API 방식
- JPA가 제공하는 타입 안전한 동적 쿼리 작성 방법
- 코드가 복잡하고 가독성이 떨어지는 단점이 존재
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> user = query.from(User.class);
List<Predicate> predicates = new ArrayList<>();
if(name != null) predicates.add(cb.equal(user.get("name"), name));
if(age != null) predicates.add(cb.equal(user.get("age"), age));
query.where(cb.and(predicates.toArray(new Predicate[0])));
🚨 StringBuilder로 동적 쿼리 작성 시 SQL Injection 위험성
⚠️ 위험한 예시
StringBuilder sql = new StringBuilder("SELECT * FROM user WHERE 1=1");
if (name != null) {
sql.append(" AND name = '" + name + "'");
}
이런 형태는 사용자의 입력값이 직접적으로 쿼리 문자열에 들어가기 때문에, SQL Injection 공격에 매우 취약
❗️ SQL Injection이란?
사용자가 악의적인 SQL 구문을 입력하여 의도하지 않은 명령을 실행시키는 공격
(예시)
홍길동'; DROP TABLE user; --
이 값을 위의 name 변수에 그대로 넣으면 최종 쿼리는 아래와 같이 완성
SELECT * FROM user WHERE 1=1 AND name = '홍길동'; DROP TABLE user; --'
결과적으로 중요한 데이터가 삭제되는 치명적인 상황이 발생할 수 있다.
✅ 해결책: PreparedStatement 사용
이러한 문제를 해결하려면, SQL을 직접 문자열로 조합하지 말고 PreparedStatement를 사용하여 바인딩하면 해결
🔑 안전한 예시
StringBuilder sql = new StringBuilder("SELECT * FROM user WHERE 1=1");
List<Object> parameters = new ArrayList<>();
if (name != null) {
sql.append(" AND name = ?");
parameters.add(name);
}
PreparedStatement pstmt = connection.prepareStatement(sql.toString());
for (int i = 0; i < parameters.size(); i++) {
pstmt.setObject(i + 1, parameters.get(i));
}
ResultSet rs = pstmt.executeQuery();
PreparedStatement는 입력된 값을 자동으로 SQL 구문으로 처리하지 않고 데이터로만 인식하기 때문에,
악의적인 SQL 구문 입력이 있어도 안전하게 처리
➕ QueryDSL, JPA(Criteria, JPAQueryFactory)를 사용하는 경우는?
QueryDSL이나 JPA의 Criteria API, 그리고 JPAQueryFactory 같은 라이브러리들은 내부적으로 자동으로 PreparedStatement 방식으로 데이터를 바인딩하여 SQL Injection 공격에서 안전
(예시)
queryFactory.selectFrom(QUser.user)
.where(QUser.user.name.eq(name))
.fetch();
'SPRING' 카테고리의 다른 글
| [SPRING] JPAQueryFactory (0) | 2025.03.30 |
|---|---|
| [SPRING] QueryDSL (1) | 2025.03.30 |
| [SPRING BOOT + JPA] 일정 관리 앱 회고 (2) | 2025.03.28 |
| [Spring Boot + JPA] LV.8 (1) | 2025.03.28 |
| [Spring Boot + JPA] LV.7 (1) | 2025.03.28 |