본문 바로가기
Mybatis/동적 SQL 작성

동적 정렬/페이징 구현

by DEVLIB 2025. 4. 17.
728x90

예시 시나리오

사용자가 아래와 같은 조건을 넘겨준다고 가정해요:

  • 검색어: name = "홍길동"
  • 정렬 기준: orderBy = "created_at"
  • 정렬 방향: orderDir = "desc"
  • 페이지 번호: page = 2, 페이지 크기: size = 10

1. 검색 + 정렬 + 페이징 DTO

public class UserSearchCondition {
    private String name;
    private String orderBy;   // 정렬할 컬럼명
    private String orderDir;  // ASC / DESC
    private int page;         // 현재 페이지
    private int size;         // 페이지당 개수
}

2. Mapper 인터페이스

List<User> searchUsers(UserSearchCondition cond);

3. Mapper XML: 동적 정렬 + 페이징 처리

<select id="searchUsers" resultType="User" parameterType="UserSearchCondition">
  SELECT * FROM users
  <where>
    <if test="name != null and name != ''">
      AND name LIKE CONCAT('%', #{name}, '%')
    </if>
  </where>

  <if test="orderBy != null and orderBy != ''">
    ORDER BY ${orderBy}
    <if test="orderDir != null and orderDir != ''">
      ${orderDir}
    </if>
  </if>

  LIMIT #{size} OFFSET #{page} * #{size}
</select>
 

주의사항: ${} vs #{}

표현 설명 주의사항
${} 문자열 치환 SQL 인젝션 위험 있음, 반드시 사용자 입력 검증 필요
#{} PreparedStatement 방식 바인딩 안전하고 권장됨

${orderBy}, ${orderDir}는 반드시 화이트리스트 검증을 통해만 사용해야 합니다.
예: orderBy는 "name", "created_at", "id" 중에만 허용


안전하게 정렬 컬럼 제한하는 방법

1. Enum 클래스 또는 유효성 검사 코드

private static final Set<String> ALLOWED_COLUMNS = Set.of("name", "created_at", "id");

2. Mapper 호출 전에 검증

if (!ALLOWED_COLUMNS.contains(cond.getOrderBy())) {
    cond.setOrderBy("id"); // 기본 정렬 컬럼
}

 


페이징 계산 팁

int page = cond.getPage();   // 1부터 시작
int size = cond.getSize();

int offset = (page - 1) * size;

 

그리고 XML에서는 이렇게:

LIMIT #{size} OFFSET #{offset}

마무리 정리

항목 기능사용  
동적 정렬 ORDER BY ${orderBy} ${orderDir} 컬럼/방향 조건부 정렬
페이징 LIMIT #{size} OFFSET #{offset} 페이지 기반 조회
보안 ${}는 사용자 입력 검증 필수 SQL 인젝션 주의

응용 예

  • 게시판 목록 + 정렬 + 페이징
  • 관리자 목록 검색 + 등록일 내림차순 정렬
  • 상품 검색 + 가격순 정렬 + 페이지 이동
LIST

'Mybatis > 동적 SQL 작성' 카테고리의 다른 글

동적 조건 검색 처리  (0) 2025.04.17
if, choose, where, trim 태그 사용  (0) 2025.04.17