본문 바로가기

Developer

React 테이블 정렬 기능 구현하기 : 클라이언트 정렬부터 was서버까지 -2

2024.11.21 - [Developer] - React 테이블 정렬 기능 구현하기 : 클라이언트 정렬부터 was서버까지 -1

 

React 테이블 정렬 기능 구현하기 : 클라이언트 정렬부터 was서버까지 -1

이번 글에서는 React를 사용하여 게시판 테이블에 정렬 기능을 구현하는 방법을 남겨보도록 하겠습니다. 특히, 이번 글에서는 클라이언트 측 정렬에 초점을 맞추어 작업을 진행합니다. 📌 목표R

peynes.tistory.com

 

이전 글에서는 React 클라이언트에서 정렬 상태를 관리하고, 이를 기반으로 데이터를 요청하는 방법을 다뤘습니다. 이번 글에서는 WAS와 SQL 쿼리를 수정하여 클라이언트에서 전달받은 정렬 조건(sortKey, direction)을 기반으로 데이터를 동적으로 반환하는 방법을 설명합니다.

리액트 정렬기능 썸네일

📌 목표

  • 클라이언트에서 전달받은 정렬 조건을 기반으로 SQL 쿼리를 동적으로 구성
  • SQL Injection 방지 및 성능 최적화
  • WAS Controller와 Service를 수정하여 데이터를 올바르게 반환

📂 기존 SQL 쿼리 분석

아래는 기존 제가 작성하던 SQL 쿼리입니다. 이 쿼리는 기본적으로 ROW_NUMBER()를 사용하여 순번을 부여하고, 고정된 정렬 기준(ROW_NUM DESC)을 사용합니다.

<select id="selectUserHistoryList" parameterType="java.util.HashMap" resultType="ParamMap">
  SELECT
    ROW_NUMBER() OVER(ORDER BY REG_DT ASC) AS ROW_NUM,
    USERS_EMAIL,
    USERS_LOGIN_DEVICE,
    USERS_LOGIN_COURS,
    DATE_FORMAT(REG_DT, '%Y-%m-%d %H:%i:%s') AS REG_DT
  FROM USERS_ACCESS_HISTORY
  WHERE 1=1
  <if test="searchKeyWord != null and searchKeyWord != ''">
    <choose>
      <when test="searchKey == 'email'">
        AND USERS_EMAIL LIKE CONCAT('%', #{searchKeyWord}, '%')
      </when>
      <otherwise></otherwise>
    </choose>
  </if>
  <choose>
    <when test="sortKey != null and sortDirection != null">
      ORDER BY ${sortKey} ${direction}
    </when>
    <otherwise>
      ORDER BY ROW_NUM DESC
    </otherwise>
  </choose>
  LIMIT #{pageSize}
  OFFSET #{startRow}
</select>

문제점:

  1. SQL Injection 위험: ${sortKey}${direction}이 문자열로 직접 삽입되므로 악의적인 입력값에 취약합니다.
  2. 기본값 설정 부족: 기본 정렬 기준이 명확하지 않다고 판단하였습니다.

🛠️ 수정된 SQL 쿼리

아래는 개선된 SQL 쿼리입니다. 동적 정렬 조건을 안전하게 처리하고, 불필요한 계산을 제거하여 성능을 최적화했습니다.

<select id="selectUsersHistoryList" parameterType="java.util.HashMap" resultType="ParamMap">
  SELECT
  	ROW_NUMBER() OVER(ORDER BY REG_DT ASC) AS ROW_NUM,
    USERS_EMAIL,
    USERS_LOGIN_DEVICE,
    USERS_LOGIN_COURS,
    DATE_FORMAT(REG_DT, '%Y-%m-%d %H:%i:%s') AS REG_DT
  FROM USERS_ACCESS_HISTORY
  WHERE 1=1
  <!-- 검색 조건 -->
  <if test="searchKeyWord != null and searchKeyWord != ''">
    <choose>
      <when test="searchKey == 'email'">
        AND USERS_EMAIL LIKE CONCAT('%', #{searchKeyWord}, '%')
      </when>
      <otherwise></otherwise>
    </choose>
  </if>

  <!-- 동적 정렬 -->
  ORDER BY 
  <choose>
    <when test="sortKey != null and sortDirection != null">
      <if test="sortKey == 'USERS_EMAIL' or sortKey == 'USERS_LOGIN_DEVICE' or sortKey == 'USERS_LOGIN_COURS' or sortKey == 'REG_DT'">
        ${sortKey} ${sortDirection}
      </if>
    </when>
    <otherwise>
      REG_DT DESC <!-- 기본값: 등록일 내림차순 -->
    </otherwise>
  </choose>

  <!-- 페이징 처리 -->
  LIMIT #{pageSize}
  OFFSET #{startRow}
</select>

개선점:

  1. SQL Injection 방지: 허용된 컬럼만 정렬할 수 있도록 제한했습니다.
  2. 기본값 명확화: 기본 정렬 기준을 등록일 내림차순(REG_DT DESC)으로 설정했습니다.

📂 WAS Controller 및 Service 수정

Controller 코드:

@GetMapping("/users/access-history")
public ResponseEntity<List<Map<String, Object>>> getUsersHistory(
        @RequestParam(required = false) String searchKey,
        @RequestParam(required = false) String searchValue,
        @RequestParam(required = false) String sortKey,
        @RequestParam(required = false) String sortDirection,
        @RequestParam int pageSize,
        @RequestParam int startRow) {

    Map<String, Object> params = new HashMap<>();
    params.put("searchKey", searchKey);
    params.put("searchValue", searchValue);
    params.put("sortKey", sortKey);
    params.put("sortDirection", sortDirection);
    params.put("pageSize", pageSize);
    params.put("startRow", startRow);

    List<Map<String, Object>> historyList = userService.getUsersHistory(params);
    return ResponseEntity.ok(historyList);
}

Service 코드:

@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;

    public List<Map<String, Object>> getUsersHistory(Map<String, Object> params) {
        return userMapper.selectUsersHistoryList(params);
    }
}

📊 테스트 시나리오

1) 기본 요청 (등록일 내림차순):

GET /users/access-history?pageSize=10&startRow=0

2) 이메일 기준 오름차순 정렬:

GET /users/access-history?sortKey=USERS_EMAIL&sortDirection=ASC&pageSize=10&startRow=0

3) 검색 조건 + 접속기기 기준 내림차순:

GET /users/access-history?searchKey=email&searchValue=test@example.com&sortKey=USERS_LOGIN_DEVICE&sortDirection=DESC&pageSize=10&startRow=0

실제 진행중인 테이블

📌 결론 및 다음 단계

이번 글에서는 WAS와 SQL 쿼리를 수정하여 클라이언트에서 전달받은 동적 정렬 조건을 처리하는 방법을 되집어 보면서 설명하였습니다. 다음 추가로 진행할 내용은 지금까지 수정한 내용대로 했을경우 React 클라이언트측(화면)에서 정렬 데이터가 안넘어오는 오류가 발생하였는데 관련된 내용을 정리해볼까 합니다.

이전 클라이언트 측 정렬 진행 확인하기

2024.11.21 - [Developer] - React 테이블 정렬 기능 구현하기 : 클라이언트 정렬부터 was서버까지 -1

 

React 테이블 정렬 기능 구현하기 : 클라이언트 정렬부터 was서버까지 -1

이번 글에서는 React를 사용하여 게시판 테이블에 정렬 기능을 구현하는 방법을 남겨보도록 하겠습니다. 특히, 이번 글에서는 클라이언트 측 정렬에 초점을 맞추어 작업을 진행합니다. 📌 목표R

peynes.tistory.com

 

 

반응형