MyBatis에서 SQL을 XML에 쓸지, 자바 코드에 어노테이션으로 쓸지 고민되지 않나요?

둘 다 같은 SQL을 실행하지만, 관리 방식이 완전히 다릅니다. 프로젝트 규모와 쿼리 복잡도에 따라 선택이 갈립니다.

개념 정의

MyBatis는 SQL을 XML 파일 에 작성하거나, Mapper 인터페이스의 메서드에 어노테이션 으로 직접 붙이는 두 가지 방식을 제공합니다. 어떤 방식이든 최종 실행 결과는 동일하지만, 코드 구조와 유지보수 경험이 달라집니다.

XML 방식

SQL을 별도 XML 파일에 분리하는 전통적인 방식입니다.

XML
<!-- UserMapper.xml -->
<mapper namespace="com.example.mapper.UserMapper">
  <select id="findByStatus" resultType="User">
    SELECT id, name, email, status
    FROM users
    WHERE status = #{status}
    ORDER BY created_at DESC
  </select>

  <insert id="save" parameterType="User"
          useGeneratedKeys="true" keyProperty="id">
    INSERT INTO users (name, email, status)
    VALUES (#{name}, #{email}, #{status})
  </insert>
</mapper>

위 XML에서 핵심은 namespace가 Mapper 인터페이스의 풀 패키지 경로와 일치해야 한다는 점입니다. id는 인터페이스의 메서드명과 매칭됩니다.

대응하는 인터페이스는 메서드 시그니처만 선언합니다.

JAVA
public interface UserMapper {
    List<User> findByStatus(String status);
    void save(User user);
}

어노테이션 방식

SQL을 자바 코드 안에 직접 작성합니다.

JAVA
public interface UserMapper {
    @Select("SELECT id, name, email, status " +
            "FROM users WHERE status = #{status} " +
            "ORDER BY created_at DESC")
    List<User> findByStatus(String status);

    @Insert("INSERT INTO users (name, email, status) " +
            "VALUES (#{name}, #{email}, #{status})")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    void save(User user);
}

XML 파일 없이 인터페이스 하나로 완결됩니다. 단순한 CRUD에서는 코드량이 줄어들고, SQL과 메서드가 같은 파일에 있어서 탐색이 빠릅니다.

비교표

기준XML어노테이션
SQL 위치별도 XML 파일자바 인터페이스 내
동적 SQL<if>, <foreach> 등 강력제한적 (<script> 태그 필요)
가독성 (단순 쿼리)파일 분리로 탐색 필요메서드 바로 위에 있어서 직관적
가독성 (복잡한 쿼리)XML 들여쓰기로 구조 파악 용이문자열 연결이 지저분해짐
IDE 지원XML 자동완성, SQL 하이라이팅문자열 안이라 IDE 지원 약함
리팩토링메서드명 변경 시 XML도 수정 필요메서드와 SQL이 함께 이동
팀 협업DBA가 XML만 수정 가능DBA가 자바 코드를 건드려야 함

XML은 복잡한 쿼리와 동적 SQL 에서 강하고, 어노테이션은 단순 CRUD와 빠른 개발 에서 강합니다.

동적 SQL에서 차이가 극명해진다

어노테이션으로 동적 SQL을 작성하려면 <script> 태그를 써야 합니다.

JAVA
@Select("<script>" +
        "SELECT * FROM users" +
        "<where>" +
        "  <if test='name != null'> AND name = #{name}</if>" +
        "  <if test='status != null'> AND status = #{status}</if>" +
        "</where>" +
        "</script>")
List<User> search(@Param("name") String name,
                  @Param("status") String status);

같은 쿼리를 XML로 작성하면 훨씬 깔끔합니다.

XML
<select id="search" resultType="User">
  SELECT * FROM users
  <where>
    <if test="name != null"> AND name = #{name}</if>
    <if test="status != null"> AND status = #{status}</if>
  </where>
</select>

문자열 안에 XML 태그를 넣는 것 자체가 가독성을 해칩니다. 조건이 3개 이상 들어가면 어노테이션 방식은 유지보수가 어려워집니다.

주의할 점

혼용 시 우선순위 충돌

같은 메서드에 어노테이션과 XML 매핑이 동시에 존재하면 XML이 우선 합니다. 이를 모르고 어노테이션만 수정하면 변경이 반영되지 않아서 "분명히 고쳤는데 왜 안 바뀌지?" 상황이 됩니다. 한 프로젝트에서 방식을 혼용할 때는 메서드별로 명확히 분리해야 합니다.

어노테이션의 컴파일 타임 한계

XML의 SQL 오류는 애플리케이션 시작 시 파싱 단계에서 잡힙니다. 반면 어노테이션의 SQL은 문자열이라 컴파일 시점에 문법 오류를 잡을 수 없습니다. 두 방식 모두 런타임 이전에 오류를 발견하려면 Mapper 빈 로딩 시점의 검증에 의존해야 합니다.

선택 기준

PLAINTEXT
단순 CRUD 위주 + 소규모 프로젝트       → 어노테이션
동적 SQL 많음 + DBA 협업               → XML
기존 프로젝트에 이미 XML이 있음         → XML 유지
신규 프로젝트 + Spring Boot             → 어노테이션으로 시작, 복잡해지면 XML 전환

정리

항목설명
XML 방식SQL을 별도 파일에 분리, 동적 SQL 강력, DBA 협업 용이
어노테이션 방식자바 코드에 SQL 직접 작성, 단순 쿼리에 적합
동적 SQLXML이 압도적으로 깔끔, 어노테이션은 <script> 필요
혼용 시 주의XML이 어노테이션보다 우선, 같은 메서드에 중복 정의 피하기
실무 추천복잡한 쿼리가 예상되면 XML, 단순하면 어노테이션
댓글 로딩 중...