1. 학습 주제
MyBatis에서 SQL을 실행하는 방식을 한 단계 더 발전시켰다.
단, 이번 TIL에서는 요청대로 api_practic 폴더 내용은 제외하고 정리한다.
- #{}와 ${}의 차이
- MyBatis SQL 작성 방식 2가지
- XML + DAO 방식
- 어노테이션 + Mapper 방식
- Mapper 인터페이스 + XML 조합 방식
- 동적 SQL에서 <if> 사용하기
- 실무에서 선호되는 Mapper + XML 패턴 이해하기
2. #{}와 ${} 차이
MyBatis에서 값을 SQL에 넣을 때는 크게 두 방식이 있다.
#{}
→ PreparedStatement 방식
${}
→ Statement 방식
3. #{}
#{}는 PreparedStatement의 파라미터 바인딩 방식이다.
예:
WHERE menu_code = #{code}
MyBatis는 이 값을 SQL 문자열에 바로 붙이지 않고, ? 자리로 처리한 뒤 안전하게 값을 바인딩한다.
실제 흐름은 이런 느낌이다.
WHERE menu_code = ?
그리고 code 값이 나중에 들어간다.
장점:
- SQL Injection에 비교적 안전하다.
- 문자열에 따옴표를 직접 붙이지 않아도 된다.
- 일반적인 값 전달에는 #{}를 사용한다.
대부분의 경우 #{}를 쓰는 것이 기본이다.
4. ${}
${}는 Statement처럼 문자열을 그대로 SQL에 끼워 넣는 방식이다.
예:
ORDER BY ${columnName}
값이 menu_price라면 SQL이 이렇게 만들어진다.
ORDER BY menu_price
즉 값이 SQL 문자열에 직접 합쳐진다.
주의할 점:
- SQL Injection 위험이 있다.
- 사용자 입력값을 그대로 넣으면 위험하다.
- 컬럼명, 테이블명처럼 #{}로 바인딩할 수 없는 SQL 조각에 제한적으로 사용한다.
정리하면:
일반 값: #{}
SQL 문법 조각: ${}
5. MyBatis SQL 작성 방식 2가지
이번 범위에서는 MyBatis를 사용하는 방식이 크게 두 가지로 비교된다.
1. XML + DAO
2. 어노테이션 + Mapper
그리고 이후에는 이 둘을 섞은 실무형 패턴으로 발전한다.
Mapper 인터페이스 + XML SQL
6. XML + DAO 방식
기존 방식은 DAO에서 문자열로 Mapper SQL을 호출했다.
[MenuDAO.java]
public List<MenuDTO> selectAllMenu(SqlSession sqlSession){
return sqlSession.selectList("MenuMapper.selectAllMenu");
}
흐름은 다음과 같다.
Service
↓
MenuDAO
↓
SqlSession.selectList("MenuMapper.selectAllMenu")
↓
menu-mapper.xml
↓
SQL 실행
이 방식은 SQL이 XML에 분리되어 있어 SQL 관리가 쉽다.
하지만 Java 코드에서 SQL id를 문자열로 호출하기 때문에 오타가 나도 컴파일 단계에서 잡기 어렵다.
예:
"MenuMapper.selectAllMenu"
이 문자열이 틀려도 실행 전까지 알기 어렵다.
7. 어노테이션 + Mapper 방식
[MenuMapper.java]
@Select("SELECT menu_code, menu_name, menu_price, category_code, orderable_status " +
"FROM tbl_menu " +
"WHERE orderable_status = 'Y' " +
"ORDER BY menu_code")
List<MenuDTO> selectAllMenu();
이 방식은 Mapper 인터페이스의 메서드에 SQL을 직접 붙인다.
Service에서는 문자열 SQL id를 호출하지 않고 Mapper 인터페이스를 가져온다.
[MenuService.java]
MenuMapper menuMapper = sqlSession.getMapper(MenuMapper.class);
return menuMapper.selectAllMenu();
흐름은 다음과 같다.
Service
↓
SqlSession.getMapper(MenuMapper.class)
↓
MenuMapper.selectAllMenu()
↓
어노테이션 SQL 실행
장점은 문자열 호출보다 타입 안정성이 좋아진다는 점이다.
메서드 이름이 Java 코드로 존재하므로 오타를 컴파일 단계에서 어느 정도 잡을 수 있다.
8. Mapper 인터페이스 + XML 방식
[MenuMapper.java]
public interface MenuMapper {
List<MenuDTO> selectAllMenu();
MenuDTO selectMenuByCode(int code);
int insertMenu(MenuDTO menu);
int updateMenu(MenuDTO menu);
int deleteMenu(int code);
}
SQL은 XML에 작성한다.
[MenuMapper.xml]
<mapper namespace="com.ohgiraffers.section03.remix.MenuMapper">
여기서 중요한 연결 규칙은 다음과 같다.
XML namespace
= Mapper 인터페이스의 풀 클래스명
XML id
= Mapper 인터페이스의 메서드명
예:
int updateMenu(MenuDTO menu);
<update id="updateMenu" parameterType="com.section03.remix.MenuDTO">
UPDATE tbl_menu
SET menu_name = #{name},
menu_price = #{price},
category_code = #{categoryCode}
WHERE menu_code = #{code}
</update>
MyBatis가 인터페이스 메서드와 XML SQL을 연결해준다.
9. 호출 흐름의 변화
기존 방식은 문자열 기반이었다.
Service
↓
MenuDAO
↓
SqlSession 문자열 호출
↓
XML SQL 실행
예:
sqlSession.selectList("MenuMapper.selectAllMenu");
변경된 방식은 Mapper 인터페이스 기반이다.
Service
↓
MenuMapper 인터페이스
↓
XML SQL 실행
예:
MenuMapper menuMapper = sqlSession.getMapper(MenuMapper.class);
return menuMapper.selectAllMenu();
이 변경의 의미는 크다.
문자열 SQL id 직접 호출
→ Mapper 메서드 호출
즉 Java 코드 입장에서는 SQL을 문자열로 부르는 것이 아니라, 인터페이스 메서드를 호출하는 구조가 된다.
10. MyBatis 실무 표준 패턴
MyBatis 실무 표준 패턴은 다음이다.
Mapper 인터페이스 + XML SQL
장점만 정리하면:
Mapper 인터페이스
→ 타입 안정성
→ 메서드 기반 호출
→ 문자열 SQL id 오타 감소
XML
→ SQL 분리
→ 복잡한 SQL 관리에 유리
→ 동적 쿼리 가독성 좋음
즉 어노테이션 방식은 간단한 SQL에는 편하지만, SQL이 길어지고 조건이 많아지면 XML이 더 읽기 좋다.
그래서 실무에서는 보통:
Java에서는 Mapper 인터페이스 메서드 호출
SQL은 XML에 분리
하는 방식을 많이 사용한다.
11. 동적 SQL
조건에 따라 SQL 일부를 붙였다 뺐다 하는 동적 SQL을 학습했다.
[DynamicSqlMapper.xml]
<if test="price gte 0 and price lte 10000">
<![CDATA[
AND A.MENU_PRICE < #{ price }
]]>
</if>
<if>는 조건이 true일 때만 SQL 조각을 추가한다.
예를 들어 가격이 10000 이하이면 다음 조건이 붙는다.
AND A.MENU_PRICE < ?
가격 범위가 다르면 다른 조건이 붙는다.
<if test="price gt 10000 and price lte 20000">
AND A.MENU_PRICE BETWEEN 10000 AND #{ price }
</if>
12. 검색 조건에 따른 SQL 변경
검색 조건이 name인지 category인지에 따라 SQL이 달라진다.
<if test="condition == 'category'">
JOIN tbl_category B ON (A.CATEGORY_CODE = B.CATEGORY_CODE)
</if>
카테고리 검색일 때만 JOIN이 붙는다.
<if test="condition == 'name'">
AND A.MENU_NAME LIKE CONCAT('%', #{ value }, '%')
</if>
이름 검색일 때는 메뉴명 LIKE 조건이 붙는다.
<if test="condition == 'category' ">
AND B.CATEGORY_NAME = #{ value }
</if>
카테고리 검색일 때는 카테고리명 조건이 붙는다.
즉 같은 메서드라도 전달된 조건에 따라 다른 SQL이 만들어진다.
13. 동적 SQL에서 XML이 유리한 이유
동적 SQL은 조건이 많아질수록 어노테이션보다 XML이 읽기 쉽다.
예를 들어 다음 기능들은 XML에서 더 관리하기 좋다.
<if>
<choose>
<when>
<otherwise>
<foreach>
<where>
<trim>
<set>
이번 코드에는 일부 TODO도 남아 있지만, 학습 방향은 명확하다.
조건에 따라 WHERE 절 추가
조건에 따라 JOIN 추가
반복문으로 IN 절 생성
입력된 값만 SET 절에 포함
이런 SQL은 Java 어노테이션 안에 문자열로 작성하면 금방 복잡해진다.
그래서 Mapper 인터페이스 + XML 조합이 실무에서 많이 쓰인다.
14. typeAlias
[mybatis-config.xml]에는 typeAlias 설정이 있다.
<typeAliases>
<typeAlias type="com.ohgiraffers.section01.SearchCriteria" alias="SearchCriteria"/>
<typeAlias type="com.ohgiraffers.section01.MenuDTO" alias="MenuDTO"/>
</typeAliases>
이렇게 하면 XML에서 긴 클래스명을 매번 쓰지 않아도 된다.
parameterType="SearchCriteria"
resultMap="menuResultMap"
MenuDTO, SearchCriteria 같은 짧은 이름으로 사용할 수 있어 XML이 더 깔끔해진다.
15. properties 설정 분리
DB 연결 정보도 별도 파일로 분리했다.
[connection-info.properties]
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost/menudb
username=ohgiraffers
password=ohgiraffers
그리고 MyBatis 설정에서 가져온다.
<properties resource="config/connection-info.properties"/>
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
여기서 ${driver} 같은 표현은 SQL 파라미터가 아니라 설정값 치환이다.
SQL에서 사용하는 ${}와는 맥락이 다르지만, 문자열 치환이라는 점은 비슷하다.
16. 핵심 정리
- #{}는 PreparedStatement 방식으로 값을 안전하게 바인딩한다.
- ${}는 Statement 방식으로 문자열을 SQL에 직접 치환한다.
- 일반 값 전달에는 대부분 #{}를 사용한다.
- ${}는 SQL Injection 위험이 있으므로 제한적으로 사용해야 한다.
- MyBatis 사용 방식에는 XML + DAO 방식과 어노테이션 + Mapper 방식이 있다.
- XML + DAO 방식은 SQL 분리는 좋지만 문자열 호출이라 타입 안정성이 약하다.
- 어노테이션 + Mapper 방식은 간단한 SQL에 편하고 Mapper 메서드 호출이 가능하다.
- 실무에서는 Mapper 인터페이스 + XML SQL 조합이 많이 사용된다.
- Mapper 인터페이스는 타입 안정성을 제공한다.
- XML은 SQL 분리와 동적 쿼리 가독성에 유리하다.
- Service → MenuDAO → SqlSession 문자열 호출 구조가 Service → MenuMapper 인터페이스 → XML SQL 실행 구조로 변경되었다.
- 동적 SQL은 조건에 따라 SQL 조각을 추가하거나 생략하는 기능이다.
- <if>는 조건이 참일 때만 SQL 일부를 붙인다.
- typeAlias를 사용하면 XML에서 긴 클래스명을 짧게 사용할 수 있다.
- DB 연결 정보는 properties 파일로 분리해 관리할 수 있다.
'TIL > [TIL]' 카테고리의 다른 글
| [TIL]Spring + MyBatis의 @Transactional, 그리고 JPA 입문 개념 정리 (0) | 2026.06.09 |
|---|---|
| [TIL]MyBatis 동적 쿼리와 Spring MyBatis 기본 흐름 (0) | 2026.06.08 |
| [TIL]MyBatis 영속성 프레임워크, SqlSession 생명주기, XML Mapper CRUD (0) | 2026.06.04 |
| [TIL]Spring Boot 인터셉터 등록, REST API 응답 설계, Validation, Swagger (0) | 2026.06.02 |
| [TIL]Spring Boot 파일 업로드, 전역 예외 처리, 인터셉터 (0) | 2026.06.01 |