MyBatis는 전통적으로 SQL을 XML로 관리해왔습니다.
하지만 최근에는 개발자가 XML보다 Java 코드에 익숙하고,
간단한 쿼리는 Interface에 직접 어노테이션으로 작성하고자 하는 경우가 많습니다.
그렇다면 MyBatis XML 없이, Mapper 어노테이션만으로도 실무 개발이 가능할까요?
이 글에서는 어노테이션 기반 Mapper의 장단점, 적용 방법, 실무 적합성까지 모두 정리해보겠습니다.
어노테이션 기반 Mapper란?
MyBatis에서는 다음과 같은 어노테이션을 통해 SQL을 직접 Mapper 인터페이스에 작성할 수 있습니다:
- @Select
- @Insert
- @Update
- @Delete
예시:
@Mapper
public interface UserMapper {
@Select("SELECT * FROM user WHERE id = #{id}")
User findById(Long id);
@Insert("INSERT INTO user (username, email) VALUES (#{username}, #{email})")
void insertUser(User user);
}
어노테이션 Mapper의 장점
장점 | 설명 |
빠른 개발 | 단순한 SQL은 XML 없이 바로 작성 가능 |
IDE 자동 완성 | Java 코드에 작성하므로 IDE 지원이 좋음 |
한눈에 로직 확인 | Mapper 인터페이스만 봐도 SQL 확인 가능 |
유지보수 간편 | XML 파일을 따로 관리하지 않아도 됨 |
실전 적용 방법
1. @Mapper 어노테이션 선언
@Mapper
public interface ProductMapper {
...
}
또는 @MapperScan(basePackages = "com.example.mapper")으로 패키지 스캔
2. SQL 어노테이션 사용
@Select("SELECT * FROM product WHERE category = #{category}")
List<Product> findByCategory(String category);
3. 파라미터 여러 개일 때는 @Param 필수
@Select("SELECT * FROM user WHERE username = #{username} AND status = #{status}")
User findByUsernameAndStatus(@Param("username") String username, @Param("status") String status);
한계와 단점
단점 | 설명 |
동적 SQL 작성 어려움 | <if>, <where> 같은 조건 분기 불가 |
SQL 길어지면 가독성 하락 | 복잡한 쿼리는 Java 코드에 어울리지 않음 |
SQL 재사용 어려움 | XML처럼 SQL fragment(sql 태그) 정의 불가 |
유지보수 시 불편 | 개발자마다 스타일 다르면 복잡해짐 |
실무 기준 언제 쓰고, 언제 쓰지 말아야 할까?
상황 | 어노테이션 Mapper 사용? |
단순한 단건 조회 | (OK) 사용 가능 |
단일 Insert, Update | (OK) 사용 가능 |
복잡한 동적 조건 쿼리 | (NO) XML 권장 |
페이징, 정렬, 검색 | (NO) XML 권장 |
다중 테이블 조인 + DTO 매핑 | (NO) ResultMap 있는 XML 권장 |
재사용 가능한 SQL 쿼리 | (NO) sql fragment 사용 불가 |
추천 전략: 혼합 사용 (XML + 어노테이션)
가장 실용적인 방법은 두 방식을 상황에 맞게 혼용하는 것입니다.
전략 예시:
용도 | 방식 |
단순 조회 / 단건 등록 | 어노테이션 |
복잡 쿼리 / 조인 / 페이징 | XML |
공통 조건 조립 | XML fragment 사용 |
테스트용 빠른 쿼리 | 어노테이션 |
실무 예제 구조 제안
@Mapper
public interface UserMapper {
@Select("SELECT * FROM user WHERE id = #{id}")
User findById(Long id);
List<User> selectUserListByCondition(UserSearchDto condition);
}
<!-- resources/mapper/UserMapper.xml -->
<mapper namespace="com.example.mapper.UserMapper">
<select id="selectUserListByCondition" resultType="User">
SELECT * FROM user
<where>
<if test="username != null">
AND username LIKE CONCAT('%', #{username}, '%')
</if>
</where>
ORDER BY created_at DESC
</select>
</mapper>
마무리
MyBatis는 매우 유연한 프레임워크입니다.
단순한 경우엔 어노테이션 기반 Mapper,
복잡한 경우엔 XML Mapper를 쓰는 혼합 전략이 실무에서는 가장 적합합니다.
무조건 한 방식만 고집하기보다는,
유지보수성과 가독성을 기준으로 상황에 맞는 선택을 해보세요.
댓글