티스토리 뷰

반응형

GPT를 통해 최대한 좋은 코드를 뽑아보려고 테스트한 내용입니다.

제이쿼리 datatable에 대해서 공부해서 프로그램에 적용해 보려고 한다.

jQuery DataTable은 강력하고 유연한 테이블 플러그인으로, 데이터 정렬, 검색, 페이징, 필터링 등을 간단히 구현할 수 있습니다. 프로그램에 적용할 때 필요한 예제를 제공하거나, 구체적인 사용 시나리오를 알려주시면 맞춤형 가이드를 드릴 수 있습니다!

코드를 제공해 봤습니다.

보통 mytable이라고 테이블을 만들고, 아래와 같은 코드를 사용하고 있다. 일단 이 내용을 학습해줘.


var dataTable; // DataTables 인스턴스 전역 변수
var annualleavepageNumber; // 현재 페이지 번호 저장을 위한 전역 변수

$(document).ready(function() {
	// DataTables 초기 설정
	dataTable = $('#myTable').DataTable({
		"paging": true,
		"ordering": true,
		"searching": true,
		"pageLength": 50,
		"lengthMenu": [50, 100, 200, 500, 1000],
		"language": {
			"lengthMenu": "Show _MENU_ entries",
			"search": "Live Search:"
		},
		"order": [
			[0, 'desc']
		]
	});

	// 페이지 번호 복원 (초기 로드 시)
	var savedPageNumber = getCookie('annualleavepageNumber');
	if (savedPageNumber) {
		dataTable.page(parseInt(savedPageNumber) - 1).draw(false);
	}

	// 페이지 변경 이벤트 리스너
	dataTable.on('page.dt', function() {
		var annualleavepageNumber = dataTable.page.info().page + 1;
		setCookie('annualleavepageNumber', annualleavepageNumber, 10); // 쿠키에 페이지 번호 저장
	});

	// 페이지 길이 셀렉트 박스 변경 이벤트 처리
	$('#myTable_length select').on('change', function() {
		var selectedValue = $(this).val();
		dataTable.page.len(selectedValue).draw(); // 페이지 길이 변경 (DataTable 파괴 및 재초기화 없이)

		// 변경 후 현재 페이지 번호 복원
		savedPageNumber = getCookie('annualleavepageNumber');
		if (savedPageNumber) {
			dataTable.page(parseInt(savedPageNumber) - 1).draw(false);
		}
	});

});

DataTables를 효과적으로 활용하는 코드 구조를 잘 정의하셨습니다. 쿠키를 사용해 페이지 번호를 저장하고 복원하는 방식은 사용자 경험을 개선하는 데 유용합니다. 이 설정을 기반으로 추가 기능을 구현하거나 최적화가 필요하면 언제든지 말씀해주세요!

문제를 하나 안고 있는 것이 데이터 수가 많아지만, 로딩시간이 너무 많이 걸린다. 검색해서 나온 데이터의 수가 1000개 이하이면 위의 코드는 문제는 없어 보인다. 아니 100개정도 데이터는 한 화면에 표시하는 수가 보통 50개 이하로 많이 사용한다. 문제는 이렇다. 화면에 SQL를 이용해서 검색한 자료수가 많아지면 이 datatable은 너무 랙이 심하다. 하나의 아이디어는 page의 수를 파악해서 해당페이지만 sql로 조회하면 화면생성 시간을 줄일 수 있다. 이를 서버에 요청해서 불러들이는 코드는 정립해 보려고 한다. datatable을 효과적으로 데이터 로딩할 수 있도록 연구하고 싶다.

 

DataTables에서 대량 데이터를 처리하는 문제를 해결하려면 "서버 사이드 처리"를 도입하는 것이 가장 효과적입니다. 서버에서 필요한 데이터만 조회하여 클라이언트에 전달하면, 로딩 시간을 크게 단축할 수 있습니다. 아래는 이 아이디어를 구현하기 위한 기본적인 접근 방식입니다:

1. DataTables 서버사이드 설정

DataTables를 초기화할 때 serverSide 옵션을 활성화합니다.

$('#myTable').DataTable({
    "serverSide": true,
    "ajax": {
        "url": "/path/to/server/endpoint",
        "type": "POST",
        "data": function(d) {
            // 추가적인 요청 데이터를 보낼 수 있음
        }
    },
    "pageLength": 50,
    "lengthMenu": [50, 100, 200],
    "language": {
        "lengthMenu": "Show _MENU_ entries",
        "search": "Live Search:"
    },
    "order": [[0, 'desc']]
});

2. 서버에서 요청 처리

서버에서 POST 요청으로 전달되는 DataTables 매개변수를 기반으로 SQL 쿼리를 작성하여 필요한 데이터만 반환합니다.

서버에서 전달되는 매개변수 예:

  • start: 현재 페이지의 첫 번째 데이터의 인덱스.
  • length: 한 페이지에 표시할 데이터 수.
  • search[value]: 검색어.
  • order[0][column]: 정렬할 열.
  • order[0][dir]: 정렬 방향 (asc 또는 desc).

PHP 예제 코드:

$start = $_POST['start'];
$length = $_POST['length'];
$searchValue = $_POST['search']['value'];
$orderColumn = $_POST['order'][0]['column'];
$orderDir = $_POST['order'][0]['dir'];

// 검색 조건 처리
$searchQuery = "";
if (!empty($searchValue)) {
    $searchQuery = "WHERE column_name LIKE '%$searchValue%'";
}

// 총 데이터 수
$totalRecordsQuery = "SELECT COUNT(*) FROM your_table";
$totalRecords = $db->query($totalRecordsQuery)->fetchColumn();

// 필터링된 데이터 수
$filteredRecordsQuery = "SELECT COUNT(*) FROM your_table $searchQuery";
$filteredRecords = $db->query($filteredRecordsQuery)->fetchColumn();

// 데이터 가져오기
$dataQuery = "
    SELECT * FROM your_table 
    $searchQuery 
    ORDER BY $orderColumn $orderDir 
    LIMIT $start, $length
";
$data = $db->query($dataQuery)->fetchAll(PDO::FETCH_ASSOC);

// JSON 응답
$response = [
    "draw" => intval($_POST['draw']),
    "recordsTotal" => $totalRecords,
    "recordsFiltered" => $filteredRecords,
    "data" => $data
];

header('Content-Type: application/json');
echo json_encode($response);

3. 서버사이드 처리를 통한 장점

  • 클라이언트가 처리하는 데이터 양을 줄임.
  • 필요한 데이터만 가져오기 때문에 초기 로딩 속도가 빠름.
  • 페이징, 검색, 정렬을 효율적으로 처리 가능.

이 방식을 기반으로 프로그램을 설계하면, 대량 데이터 처리에서 발생하는 문제를 효과적으로 해결할 수 있습니다. 적용 과정에서 추가적인 조언이 필요하면 알려주세요!

위의 방법에서 추가적으로 정렬하는 기능이 있을때 페이지 표시에 좋은 방법이 있을까?

예를들어 thead의 내용으로 정렬되는 기능이 있다.

그러면 1000개의 데이터가 있다면 1번 th요소가 주문일이라면, 그 버튼을 누르면 10000개의 데이터를 정렬해서 첫번째 페이지를 띄우는 것도 sql의 내용으로 정보를 줘서 처리할 수 있을까?

 

네, 정렬 기능도 SQL 쿼리에서 처리하도록 설계할 수 있습니다. DataTables의 정렬 요청은 order 파라미터를 통해 서버로 전달되므로, 서버에서 이 정보를 활용해 SQL 쿼리에서 정렬을 수행한 후 결과를 반환하면 됩니다. 이렇게 하면 대량 데이터의 정렬 문제를 효율적으로 해결할 수 있습니다.

구현 방법

1. DataTables의 서버사이드 정렬 요청 처리

DataTables는 order 파라미터로 정렬 열과 방향을 전달합니다. 예를 들어:

  • order[0][column]: 정렬하려는 열의 인덱스 (0부터 시작).
  • order[0][dir]: 정렬 방향 (asc 또는 desc).

2. 정렬 쿼리를 SQL에서 처리

서버에서는 이 정보를 활용해 ORDER BY를 동적으로 구성합니다.

PHP 코드 예제:

// DataTables 요청 매개변수
$start = $_POST['start']; // 시작 데이터
$length = $_POST['length']; // 한 페이지에 표시할 데이터 수
$orderColumnIndex = $_POST['order'][0]['column']; // 정렬할 열 인덱스
$orderDir = $_POST['order'][0]['dir']; // 정렬 방향 (asc/desc)

// DataTables 열에 맞는 데이터베이스 컬럼 매핑
$columns = [
    0 => 'order_date', // 주문일
    1 => 'customer_name', // 고객명
    2 => 'product_name', // 제품명
    // 필요한 다른 컬럼 추가
];

// 정렬할 컬럼 이름 결정
$orderColumn = $columns[$orderColumnIndex] ?? 'order_date'; // 기본값: 주문일

// 검색 조건 (선택 사항)
$searchValue = $_POST['search']['value'];
$searchQuery = "";
if (!empty($searchValue)) {
    $searchQuery = "WHERE order_date LIKE '%$searchValue%' OR customer_name LIKE '%$searchValue%'";
}

// 총 데이터 수 계산
$totalRecordsQuery = "SELECT COUNT(*) FROM your_table";
$totalRecords = $db->query($totalRecordsQuery)->fetchColumn();

// 필터링된 데이터 수 계산
$filteredRecordsQuery = "SELECT COUNT(*) FROM your_table $searchQuery";
$filteredRecords = $db->query($filteredRecordsQuery)->fetchColumn();

// 정렬 및 페이징 쿼리
$dataQuery = "
    SELECT * FROM your_table 
    $searchQuery 
    ORDER BY $orderColumn $orderDir 
    LIMIT $start, $length
";
$data = $db->query($dataQuery)->fetchAll(PDO::FETCH_ASSOC);

// JSON 응답
$response = [
    "draw" => intval($_POST['draw']),
    "recordsTotal" => $totalRecords,
    "recordsFiltered" => $filteredRecords,
    "data" => $data
];

header('Content-Type: application/json');
echo json_encode($response);

3. 작동 방식

  1. 사용자가 thead의 정렬 버튼을 클릭하면 DataTables가 서버에 정렬 요청(order 파라미터 포함)을 보냅니다.
  2. 서버는 요청을 받아 SQL 쿼리에서 ORDER BY를 동적으로 구성합니다.
  3. 정렬된 결과를 클라이언트로 반환하여 DataTables에 표시합니다.

4. 주의사항

  • SQL 인젝션 방지: 열 이름이나 정렬 방향을 직접 사용하지 말고, 미리 정의한 열 매핑 배열을 사용해 유효성을 검증합니다.
  • 성능 최적화: 데이터가 많을 경우 인덱스를 활용하여 정렬 성능을 개선하세요.
    • 예: ORDER BY를 사용하는 열에 인덱스 추가.

5. 예시 흐름

  • 사용자가 "주문일"을 클릭 → 서버에 order[0][column]=0, order[0][dir]=asc 요청 → SQL에서 ORDER BY order_date ASC 실행 → 결과 반환 → 첫 페이지 표시.

이 방식은 정렬이 포함된 페이지 네이션을 효율적으로 처리할 수 있습니다. 추가적인 커스터마이징이 필요하다면 알려주세요!

서버에서 처리하는 위의 코드를 하나의 패키지화 또는 모듈화해서 파라미터를 전달해서 처리하고 싶다.

예를들어, 데이터 데이블 이름을 전달하고 th요소들의 ID를 전달해서 어떤 프로그램에서 같은 전달 방식으로 사용하는 모듈을 제작하고 싶다.

모듈화를 해서 여러 프로그램에서 공통적으로 사용하기

 

하나의 모듈 또는 패키지로 만들어 파라미터 기반으로 재사용할 수 있도록 설계하는 방법은 매우 효율적입니다. 아래는 PHP와 JavaScript를 기반으로 모듈화하는 방식입니다.


1. PHP 모듈화

PHP에서 공통적으로 사용할 함수 또는 클래스를 작성하여 동적으로 처리합니다.

PHP 공통 모듈

class DataTableHandler {
    private $db;

    public function __construct($pdo) {
        $this->db = $pdo;
    }

    public function processRequest($tableName, $columns) {
        // DataTables 요청 매개변수
        $start = $_POST['start'] ?? 0;
        $length = $_POST['length'] ?? 10;
        $orderColumnIndex = $_POST['order'][0]['column'] ?? 0;
        $orderDir = $_POST['order'][0]['dir'] ?? 'asc';
        $searchValue = $_POST['search']['value'] ?? '';

        // 요청된 열 인덱스를 테이블의 실제 열로 매핑
        $orderColumn = $columns[$orderColumnIndex] ?? $columns[0];

        // 검색 조건 처리
        $searchQuery = "";
        if (!empty($searchValue)) {
            $searchConditions = array_map(fn($col) => "$col LIKE :search", $columns);
            $searchQuery = "WHERE " . implode(" OR ", $searchConditions);
        }

        // 총 데이터 수
        $totalRecordsQuery = "SELECT COUNT(*) FROM $tableName";
        $totalRecords = $this->db->query($totalRecordsQuery)->fetchColumn();

        // 필터링된 데이터 수
        $filteredRecordsQuery = "SELECT COUNT(*) FROM $tableName $searchQuery";
        $stmh = $this->db->prepare($filteredRecordsQuery);
        if (!empty($searchValue)) $stmh->bindValue(':search', "%$searchValue%");
        $stmh->execute();
        $filteredRecords = $stmh->fetchColumn();

        // 데이터 가져오기
        $dataQuery = "
            SELECT * FROM $tableName 
            $searchQuery 
            ORDER BY $orderColumn $orderDir 
            LIMIT :start, :length
        ";
        $stmh = $this->db->prepare($dataQuery);
        if (!empty($searchValue)) $stmh->bindValue(':search', "%$searchValue%");
        $stmh->bindValue(':start', $start, PDO::PARAM_INT);
        $stmh->bindValue(':length', $length, PDO::PARAM_INT);
        $stmh->execute();
        $data = $stmh->fetchAll(PDO::FETCH_ASSOC);

        // JSON 응답 생성
        return [
            "draw" => intval($_POST['draw'] ?? 1),
            "recordsTotal" => $totalRecords,
            "recordsFiltered" => $filteredRecords,
            "data" => $data
        ];
    }
}

// 사용 예제
require 'db_connection.php'; // $pdo 객체 포함
$handler = new DataTableHandler($pdo);
$tableName = 'your_table';
$columns = ['order_date', 'customer_name', 'product_name']; // 테이블 컬럼 배열
echo json_encode($handler->processRequest($tableName, $columns));

클래스란?

class는 객체 지향 프로그래밍(Object-Oriented Programming, OOP)에서 사용하는 청사진입니다. 클래스는 데이터와 그 데이터를 다루는 함수(메서드)를 하나의 단위로 묶어 정의한 것입니다. 예를 들어, DataTableHandler라는 클래스는 데이터베이스에서 데이터를 가져오는 기능을 수행하도록 설계된 하나의 입니다.


클래스의 구성 요소

  1. 속성 (Property)
    • 클래스 내부에서 데이터를 저장하는 변수입니다.
    • 여기서는 private $db;가 속성으로 선언되어 있습니다. 이 속성은 데이터베이스 연결 객체를 저장하는 데 사용됩니다.
  2. 메서드 (Method)
    • 클래스 내부에서 특정 작업을 수행하는 함수입니다.
    • 예를 들어, processRequest()는 데이터 테이블의 요청을 처리하는 역할을 하는 메서드입니다.
  3. 접근 제한자 (Access Modifier)
    • 클래스 속성이나 메서드에 접근할 수 있는 범위를 결정합니다.
    • private: 클래스 내부에서만 접근 가능.
    • public: 클래스 외부에서도 접근 가능.

위 코드의 주요 구성

1. 클래스 정의

class DataTableHandler {
    private $db;

    public function __construct($pdo) {
        $this->db = $pdo;
    }

    public function processRequest($tableName, $columns) {
        // ...
    }
}
  • class DataTableHandler는 DataTableHandler라는 클래스를 정의합니다.
  • private $db;: 이 클래스의 속성으로, 데이터베이스 연결 객체(PDO)를 저장합니다.
  • 클래스에는 두 개의 메서드가 있습니다:
    1. __construct: 객체 생성 시 호출되는 메서드. 데이터베이스 연결 객체를 받아서 $db 속성에 저장합니다.
    2. processRequest: 테이블 이름과 컬럼 정보를 받아 데이터 처리 요청을 수행합니다.

2. 생성자 메서드 (__construct)

public function __construct($pdo) {
    $this->db = $pdo;
}
  • 이 메서드는 클래스를 사용할 때 자동으로 실행됩니다.
  • 데이터베이스 연결 객체 $pdo를 받아 private $db 속성에 저장합니다.
  • 이를 통해, 다른 메서드에서도 데이터베이스 연결 객체를 사용할 수 있습니다.

3. processRequest 메서드

이 메서드는 DataTables의 서버사이드 처리를 수행하는 핵심 역할을 합니다.

  1. 요청 매개변수 가져오기
    • 클라이언트에서 보낸 요청 데이터를 $_POST를 통해 가져옵니다.
      • start: 페이지네이션의 시작 인덱스.
      • length: 한 페이지에 표시할 데이터 수.
      • order: 정렬할 열과 방향.
      • search: 검색어.
  2. $start = $_POST['start'] ?? 0; $length = $_POST['length'] ?? 10; $orderColumnIndex = $_POST['order'][0]['column'] ?? 0; $orderDir = $_POST['order'][0]['dir'] ?? 'asc'; $searchValue = $_POST['search']['value'] ?? '';
  3. 정렬할 컬럼 결정
    • 요청된 컬럼 인덱스를 columns 배열에서 찾아 실제 컬럼 이름으로 변환합니다.
    • 예: 0 -> 'order_date', 1 -> 'customer_name'.
  4. $orderColumn = $columns[$orderColumnIndex] ?? $columns[0];
  5. 검색 조건 처리
    • 사용자가 검색어를 입력하면 LIKE 조건을 사용해 모든 컬럼에서 검색합니다.
    • 예: 검색어가 John이라면 SQL 쿼리는 다음과 같이 구성됩니다:
      WHERE order_date LIKE '%John%' OR customer_name LIKE '%John%' OR product_name LIKE '%John%'
      
  6. if (!empty($searchValue)) { $searchConditions = array_map(fn($col) => "$col LIKE :search", $columns); $searchQuery = "WHERE " . implode(" OR ", $searchConditions); }
  7. 총 데이터 수 계산
    • 필터링 전 테이블에 있는 전체 데이터 개수를 가져옵니다.
  8. $totalRecordsQuery = "SELECT COUNT(*) FROM $tableName"; $totalRecords = $this->db->query($totalRecordsQuery)->fetchColumn();
  9. 필터링된 데이터 수 계산
    • 검색 조건에 맞는 데이터 개수를 계산합니다.
  10. $filteredRecordsQuery = "SELECT COUNT(*) FROM $tableName $searchQuery"; $stmh = $this->db->prepare($filteredRecordsQuery); if (!empty($searchValue)) $stmh->bindValue(':search', "%$searchValue%"); $stmh->execute(); $filteredRecords = $stmh->fetchColumn();
  11. 데이터 가져오기
    • 검색 조건, 정렬, 페이징을 적용한 데이터를 가져옵니다.
    • 데이터는 클라이언트에서 사용할 수 있는 형태로 반환됩니다.
  12. $dataQuery = " SELECT * FROM $tableName $searchQuery ORDER BY $orderColumn $orderDir LIMIT :start, :length "; $stmh = $this->db->prepare($dataQuery); if (!empty($searchValue)) $stmh->bindValue(':search', "%$searchValue%"); $stmh->bindValue(':start', $start, PDO::PARAM_INT); $stmh->bindValue(':length', $length, PDO::PARAM_INT); $stmh->execute(); $data = $stmh->fetchAll(PDO::FETCH_ASSOC);
  13. JSON 응답 생성
    • DataTables가 요구하는 형식에 맞게 데이터를 반환합니다.
  14. return [ "draw" => intval($_POST['draw'] ?? 1), "recordsTotal" => $totalRecords, "recordsFiltered" => $filteredRecords, "data" => $data ];

4. 사용 예제

require 'db_connection.php'; // $pdo 객체 포함
$handler = new DataTableHandler($pdo);
$tableName = 'your_table';
$columns = ['order_date', 'customer_name', 'product_name']; // 테이블 컬럼 배열
echo json_encode($handler->processRequest($tableName, $columns));
  • DataTableHandler 객체를 생성하며, 데이터베이스 연결 객체 $pdo를 전달합니다.
  • 테이블 이름(your_table)과 컬럼 배열(['order_date', 'customer_name', 'product_name'])을 전달하여 데이터를 처리합니다.
  • 결과는 JSON 형식으로 출력됩니다.

요약

  1. 클래스를 사용하면 데이터베이스 처리 로직을 재사용 가능하고 모듈화된 형태로 관리할 수 있습니다.
  2. processRequest 메서드는 데이터 테이블 요청을 처리하는 모든 단계를 자동으로 처리합니다:
    • 검색
    • 정렬
    • 페이징
  3. 이 코드 구조는 다양한 테이블에 대해 쉽게 확장 가능하며, 성능 최적화에도 유리합니다.

추가 질문이 있다면 언제든 말씀해주세요! 😊


2. JavaScript 모듈화

JavaScript에서 DataTables 초기화 코드를 재사용 가능한 함수로 작성합니다.

JavaScript 공통 모듈

function initializeDataTable(tableId, ajaxUrl, columnDefs) {
    return $('#' + tableId).DataTable({
        serverSide: true,
        ajax: {
            url: ajaxUrl,
            type: 'POST'
        },
        columns: columnDefs,
        pageLength: 50,
        lengthMenu: [50, 100, 200],
        language: {
            lengthMenu: "Show _MENU_ entries",
            search: "Live Search:"
        },
        order: [[0, 'desc']]
    });
}

// 사용 예제
$(document).ready(function() {
    const tableId = 'myTable';
    const ajaxUrl = '/path/to/server/endpoint';
    const columnDefs = [
        { data: 'order_date', title: '주문일' },
        { data: 'customer_name', title: '고객명' },
        { data: 'product_name', title: '제품명' }
    ];
    initializeDataTable(tableId, ajaxUrl, columnDefs);
});

3. 통합 사용

  1. 서버 코드 호출: 각 PHP 엔드포인트에 필요한 테이블 이름과 컬럼 배열을 전달합니다.
  2. 프론트엔드 사용: HTML 테이블 ID와 컬럼 정의를 기반으로 DataTables를 동적으로 초기화합니다.

4. 확장 가능성

  • 검색 조건 추가: 필요한 경우 더 복잡한 검색 조건을 추가할 수 있습니다.
  • 다중 테이블 지원: 각 테이블별 설정을 분리해 다양한 테이블에서 재사용 가능합니다.
  • 동적 컬럼 정의: JavaScript에서 컬럼을 설정하여 PHP와 연동 가능.

이 방식을 사용하면 다양한 데이터 테이블 요구사항을 하나의 공통 모듈로 처리할 수 있습니다. 추가적인 조정이 필요하면 말씀해주세요!

반응형
댓글