TokyoAJ

도쿄아재

SPRINGBOOT 2025.04.04

MyBatis로 동적 SQL 작성하는 방법 – 실무에서 자주 쓰는 패턴 총정리

MyBatis의 가장 큰 장점 중 하나는 XML로 동적 SQL을 유연하게 작성할 수 있다는 점입니다.

조건에 따라 WHERE절이 달라지고, IN 목록이 바뀌고, SET 절이 동적으로 조립되어야 하는 상황은 실무에서 매우 흔하죠.

이 글에서는 MyBatis에서 가장 자주 사용되는 동적 SQL 태그들과

실전 예제를 통해 간결하고 안전하게 사용하는 방법을 소개합니다.


기본 예제 구조

<mapper namespace="com.example.mapper.UserMapper">

<select id="selectUserList" resultType="User">
SELECT * FROM user
<where>
<if test="username != null">
AND username = #{username}
</if>
<if test="status != null">
AND status = #{status}
</if>
</where>
</select>

</mapper>


자주 쓰는 동적 SQL 태그

1. <if> – 조건 분기 처리

<if test="status != null">
AND status = #{status}
</if>
  1. null 또는 특정 조건에 따라 SQL 일부를 추가합니다.
  2. test 속성에는 SpEL(스프링 표현식) 문법이 들어갑니다.


2. <where> – 조건절을 자동으로 조립

<where>
<if test="username != null">
AND username = #{username}
</if>
<if test="email != null">
AND email = #{email}
</if>
</where>
  1. 첫 번째 조건이 AND로 시작해도 자동으로 제거됨 (WHERE로 처리)
  2. 조건이 하나도 없으면 WHERE 자체도 생략됨


3. <set> – UPDATE 쿼리용

<update id="updateUser">
UPDATE user
<set>
<if test="username != null"> username = #{username}, </if>
<if test="email != null"> email = #{email}, </if>
</set>
WHERE id = #{id}
</update>
  1. 마지막에 오는 , 자동 제거 처리됨
  2. 필드가 동적으로 바뀌는 UPDATE에 유용


4. <choose>, <when>, <otherwise>if/else if/else 구문

<choose>
<when test="role == 'admin'">
AND level = 'high'
</when>
<when test="role == 'user'">
AND level = 'normal'
</when>
<otherwise>
AND level = 'guest'
</otherwise>
</choose>
  1. 하나의 조건만 적용되도록 할 때 사용
  2. 여러 if와 달리 동시에 여러 조건이 적용되지 않음


5. <foreach> – IN 절, 다중 값 처리

<foreach collection="idList" item="id" open="(" separator="," close=")">
#{id}
</foreach>

전체 예시:

<select id="selectUsersByIds" resultType="User">
SELECT * FROM user
WHERE id IN
<foreach collection="idList" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
  1. idList는 Java에서 전달된 List 타입
  2. open/close/separator로 괄호 및 구분자 자동 처리


6. <trim> – SQL 조각 수동 제어

<trim prefix="WHERE" prefixOverrides="AND |OR ">
<if test="status != null"> AND status = #{status} </if>
<if test="email != null"> AND email = #{email} </if>
</trim>
  1. prefixOverrides를 통해 앞의 AND 또는 OR 제거 가능
  2. <where> 태그보다 더 세밀하게 제어할 수 있음


실무 예제: 조건 검색 + 페이징

<select id="selectPostList" resultType="Post">
SELECT * FROM post
<where>
<if test="category != null"> AND category = #{category} </if>
<if test="keyword != null">
AND (title LIKE CONCAT('%', #{keyword}, '%')
OR content LIKE CONCAT('%', #{keyword}, '%'))
</if>
</where>
ORDER BY created_at DESC
LIMIT #{limit} OFFSET #{offset}
</select>
  1. 유저가 검색 조건 없이도 호출 가능
  2. keyword는 title/content 둘 다 검색
  3. 페이징 처리 지원


자주 발생하는 실수

실수설명해결 방법
AND가 앞에 붙어서 문법 오류조건이 없을 경우 AND가 남음<where> 또는 <trim> 사용
<foreach> 안에 item 이름 안 맞음#{userId} 대신 #{item} 써야item="userId" 정확히 설정
null 체크 누락조건 없이 조회하거나 NPE 발생항상 <if test="!= null"> 확인


마무리 정리

태그설명
<if>조건 분기
<where>조건절 자동 정리
<set>UPDATE 컬럼 조립
<foreach>IN 또는 반복 처리
<choose>if / else if / else
<trim>WHERE, SET 등의 수동 버전


MyBatis의 동적 SQL은 정말 강력한 기능이지만,

잘못 쓰면 SQL이 꼬이고 디버깅이 어려워질 수 있습니다.


따라서 규칙을 정해 두고, XML 구조를 최대한 명확하게 유지하는 것이 중요합니다.

이 글에 소개된 예제 패턴들만 익혀도 실무에서 대부분의 복잡한 SQL을 처리할 수 있습니다.

댓글

good************* 2025.04.04 09:29:23
블로그에서 소개한 MyBatis 동적 SQL 작성법과 실무 패턴 정리가 큰 도움이 되었습니다. 유익한 정보로 실무 적용에 많은 도움이 되었으며, 앞으로도 기대됩니다. 감사합니다!