Java에서 MyBatis를 사용할 때, foreach
구문에 빈 배열이나 리스트가 들어오는 상황은 종종 발생합니다. 이때 적절한 처리를 하지 않으면 SQL 문법 오류나 의도치 않은 전체 조회와 같은 문제가 생길 수 있습니다.
저는 최근 IN
절을 동적으로 구성하기 위해 <foreach>
태그를 사용하던 중, 전달받은 리스트가 빈 배열일 경우 SQL 오류가 발생하는 문제를 겪었습니다. 이 문제를 해결하기 위해 어떤 방식들이 있는지 조사하고, 실제로 적용해 보면서 가장 적합한 해결 방법을 찾게 되었습니다.
문제 상황: 빈 배열이 들어오면 SQL 오류 발생
예를 들어 아래와 같이 idList
라는 리스트를 기반으로 사용자 정보를 조회하는 SQL을 구성한다고 가정합니다.
<select id="selectUserList" resultType="User">
SELECT * FROM users WHERE id IN
<foreach collection="idList" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
이때 idList
가 비어 있다면, 생성된 SQL은 다음과 같이 됩니다.
SELECT * FROM users WHERE id IN ()
이는 SQL 문법상 허용되지 않기 때문에 오류가 발생하게 됩니다.
해결 방법 정리
이 문제를 해결하기 위해 다음과 같은 여러 방식을 검토하였고, 각각의 장단점을 파악했습니다.
1. Java 코드에서 사전 필터링
SQL을 실행하기 전에 Java 단에서 리스트가 비어 있는지 확인하고, 빈 결과를 반환하거나 실행을 생략하는 방식입니다.
if (idList == null || idList.isEmpty()) {
return Collections.emptyList(); // 또는 사용자 정의 처리
} else {
return userMapper.selectUserList(idList);
}
- 장점: 가장 안전하고 예측 가능
- 단점: 로직이 Java 단에 분산되어 MyBatis 쿼리와 분리됨
2. MyBatis <if>
조건문으로 감싸기
SQL 내부에서 리스트가 비어 있는지를 판단하여 foreach
를 감쌀 수 있습니다.
<if test="idList != null and idList.size() > 0">
AND id IN
<foreach collection="idList" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</if>
- 장점: MyBatis XML 내부에서 제어 가능
- 단점: 조건이 빠질 경우 전체 데이터가 조회되는 위험 있음
3. <choose>
를 활용한 안전한 처리
가장 안전한 방식은 <choose>
태그를 활용하여 리스트가 비어 있을 경우 **무조건 false가 되는 조건(예: 1=0
)**을 삽입하는 것입니다.
<choose>
<when test="idList != null and idList.size() > 0">
AND id IN
<foreach collection="idList" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</when>
<otherwise>
AND 1 = 0
</otherwise>
</choose>
- 장점: 리스트가 비어도 SQL 문법 오류 없이 안전하게 동작
- 단점: 쿼리 길이가 다소 길어짐
결론: 실무에서는 <choose>
방식이 가장 안전
여러 방식을 직접 테스트해 본 결과, 실무에서는 빈 리스트일 경우 자동으로 무조건 실패하는 조건을 삽입하는 <choose>
방식이 가장 예측 가능하고 안전했습니다. 특히 API 요청에 따라 리스트가 동적으로 결정되는 상황이라면, 이 방식이 SQL 오류를 방지하면서도 정확한 제어가 가능합니다.
'개발 (Development) > Java' 카테고리의 다른 글
[Java] PostgreSQL의 timestamptz를 Java MyBatis에서 Instant로 받는 방법 (2) | 2025.07.28 |
---|---|
[Java] ISO 8601 형식의 시간 출력하기 (현재 시간과 과거 시간 구하기) (2) | 2025.07.20 |
[Java/SpringBoot] 서버 간 API 호출 오류: Connection reset 에러 분석 및 해결 방법 (0) | 2025.07.13 |
[Java] LinkedHashMap에서 특정 값을 가진 항목 제거하는 방법 (0) | 2025.07.05 |
[Java/SpringBoot] Spring OAuth2 시스템에서 발생한 Access Token 만료 및 인증 오류 대응 기록 (2) | 2025.06.28 |