ProTable 컴포넌트
ProTable은 Page Designer에서 사용하는 범용 원격 테이블 컴포넌트입니다. HTTP API를 직접 호출하고 열, 검색 도구 모음, 페이지네이션, 행 작업을 페이지에서 설정할 수 있습니다.
이 가이드에서는 다음 작업을 설명합니다.
- Custom URL을 GET 또는 POST 페이지 API에 연결
- Query Context를 통해 Input과 다른 컴포넌트의 현재 값을 API에 전달
- Search 버튼으로 첫 페이지부터 새 검색 시작
- 페이지를 이동할 때 현재 검색 조건 유지
- 응답 경로, 열, 고유 행 키, 표시 스타일 설정
- Java 예제로 ProTable 호환 백엔드 API 구현
이 가이드는 Spark에 포함된 상품 검색 예제를 사용합니다. API에는 23개의 상품이 있으며 keyword로 상품 코드와 이름을 대소문자 구분 없이 부분 일치 검색할 수 있습니다.
1. ProTable을 사용하는 경우
1.1 권장 사용 사례
다음 경우에는 ProTable을 사용합니다.
- 페이지에서 사용자 정의 백엔드 API를 직접 호출해야 하는 경우
- 데이터가 단일 Business Object가 아니라 외부 서비스 또는 집계 서비스에서 오는 경우
- API가 페이지네이션, 필터, 정렬 또는 권한 로직을 직접 관리하는 경우
- 버튼이 API를 호출한 뒤 반환된 배열을
setTableData로 테이블에 쓰는 경우
1.2 ProTable, DataList, TaskList 비교
| 컴포넌트 | 주요 데이터 | 적합한 용도 |
|---|---|---|
| ProTable | Custom URL 또는 Model Data Source | 범용 HTTP API, 집계 데이터, 사용자 정의 페이지네이션 계약 |
| DataList | Business Object | DQL, 모델 필드, Object Action, 모델 데이터 권한 |
| TaskList | Spark Tasks | 작업 필터, 상태, 처리, 작업 컨텍스트 |
ProTable은 Model Data Source도 지원하며 모델 연결 방식은 DataList와 비슷합니다. Business Object, DQL, 모델 필터는 DataList 컴포넌트 가이드를 참고하십시오. 이 가이드에서는 ProTable의 Custom URL을 중점적으로 설명합니다.
2. 이 가이드의 예제
예제 API:
POST https://spark.hiforce.ai/spark/api/sample/protable/products
예제 페이지 설정:
- 데이터 소스: Custom URL
- 요청 메서드: POST
- 기본 페이지 크기: 10개
- Left Toolbar:
Key WordInput 하나와SearchButton 하나 - Query Context: Input 값을
keyword요청 필드에 매핑 - Search 상호작용: 현재 ProTable의
reloadTable호출 - 열:
code,name,category,price,stock
Spark에 로그인하고 해당 애플리케이션 권한이 있으면 이 가이드의 Runtime 페이지에서 동작을 확인할 수 있습니다.
3. Custom URL 페이지네이션 계약
3.1 POST 요청
ProTable이 첫 페이지를 로드할 때 페이지 필드와 Query Context 파라미터를 JSON 본문으로 합칩니다.
{
"current": 1,
"pageSize": 10,
"keyword": "Graphite"
}
| 필드 | 타입 | 설명 |
|---|---|---|
current | number | 현재 페이지 번호, 1부터 시작 |
pageSize | number | 페이지당 레코드 수 |
keyword | string | 이 예제에서 Query Context로 추가한 검색 파라미터 |
current와 pageSize는 ProTable이 자동으로 관리합니다. Query Context에서 다시 정의하지 마십시오.
3.2 GET 요청
GET을 선택하면 같은 파라미터가 URL 쿼리 문자열로 변환됩니다.
/spark/api/sample/protable/products?current=1&pageSize=10&keyword=Graphite
GET 요청에서는 undefined, null, 빈 문자열이 생략됩니다. 배열과 객체는 JSON 문자열로 직렬화되므로 백엔드에서 JSON으로 파싱해야 합니다.
3.3 기본 응답 구조
예제는 Spark 표준 ApiResult를 반환하며 페이지 데이터는 data 아래에 있습니다.
{
"success": true,
"code": 200,
"message": "Product data loaded successfully.",
"data": {
"total": 1,
"current": 1,
"pageSize": 10,
"requestContext": {
"environment": "DEV",
"renderMode": "RUNTIME"
},
"records": [
{
"id": 4,
"code": "DSP-2002",
"name": "Graphite Monitor 32",
"category": "DISPLAY",
"status": "ACTIVE",
"price": 699.00,
"stock": 21,
"updatedAt": "2026-07-25T16:40:00Z"
}
]
}
}
데이터 소스 경로를 다음과 같이 설정합니다.
Rows Path: data.records
Total Path: data.total
Current Page Path: data.current
Page Size Path: data.pageSize
중첩 속성에는 점 경로를 사용합니다. 레코드 배열이 result.items에 있으면 Rows Path를 result.items로 설정합니다.
페이지 API는 현재 페이지 배열 길이가 아니라 필터 적용 후 전체 레코드 수를 반환해야 합니다. 그렇지 않으면 ProTable이 전체 페이지 수를 올바르게 계산할 수 없습니다.
3.4 페이지 요청 컨텍스트 Header
ProTable이 Custom URL로 실제 HTTP 요청을 보낼 때 두 개의 표준 Header를 자동으로 추가합니다. 백엔드는 이를 사용해 테스트 데이터를 선택하고, 디자이너 접근을 제한하거나, 요청 출처를 기록할 수 있습니다.
| Header | 값 | 설명 |
|---|---|---|
X-Spark-Page-Environment | DEV, TEST, PROD | 현재 페이지 환경. Page Designer의 데이터 미리보기는 DEV 사용 |
X-Spark-Page-Render-Mode | DESIGN_PREVIEW, RUNTIME | Page Designer, 디자이너에서 연 Preview, 실제 레코드 미리보기는 DESIGN_PREVIEW, 일반 실행 페이지는 RUNTIME 사용 |
환경과 렌더링 모드는 서로 독립적인 두 차원입니다. 예를 들어 개발 환경의 실행 페이지는 DEV + RUNTIME을 보내고, Page Designer 안에서 실제 레코드를 불러오면 DEV + DESIGN_PREVIEW를 보냅니다. 이 값은 안정적인 열거형이므로 백엔드가 페이지 URL이나 Referer로 호출 상황을 추측해서는 안 됩니다.
이 규칙은 ProTable이나 Custom URL에만 적용되는 것이 아니라 모든 Page Designer 및 Page Runtime 백엔드 요청에 적용되는 공통 계약입니다. Model Data Source, Data Form, Task 컴포넌트, 업로드, 행 작업, Send Request도 같은 Header를 사용합니다. GET과 POST의 동작은 동일합니다. Custom URL이 교차 출처 서비스를 가리키면 서비스의 CORS 설정에서 Access-Control-Allow-Headers에 두 Header를 모두 허용해야 합니다.
4. ProTable 추가 및 설정
4.1 컴포넌트 추가
- 대상 페이지를 Page Designer에서 엽니다.
- 컴포넌트 팔레트에서 Data를 펼칩니다.
- ProTable을 지원되는 컨테이너로 끌어 놓습니다.
- ProTable에
productTable처럼 안정적이고 알아보기 쉬운 이름을 지정합니다.
안정적인 이름을 사용하면 Button의 Component API에서 올바른 대상을 쉽게 선택할 수 있습니다.
4.2 Custom URL 설정
ProTable의 Data Source에서 Custom URL을 선택하고 다음 값을 입력합니다.
| 속성 | 값 |
|---|---|
| URL | https://spark.hiforce.ai/spark/api/sample/protable/products |
| Request Method | POST |
| Rows Path | data.records |
| Total Path | data.total |
| Current Page Path | data.current |
| Page Size Path | data.pageSize |
URL은 세 가지 형식을 지원합니다.
https://api.example.com/products
connection://erp/products
/products # Connection Key도 선택
- 같은 출처의 Spark API는 전체 URL 또는 사이트 상대 경로를 사용할 수 있습니다.
- 외부 서비스는 애플리케이션 설정에서 External Connection을 만들고 해당 Connection Key를 사용합니다.
- 시크릿, 토큰, 비밀번호는 연결 설정에 저장하고 URL이나 Query Context에 입력하지 마십시오.
- API에는 로그인 상태, 데이터 권한, 백엔드 인증이 계속 적용됩니다. 보호된 API를 브라우저에서 직접 열면
401이 반환될 수 있습니다.
Response Fields 설정
Model Data Source는 Business Object 필드 메타데이터를 직접 읽을 수 있습니다. Custom URL에서는 Data Source에 Response Fields를 설정해야 합니다. 각 항목에는 다음 값이 포함됩니다.
| 속성 | 설명 |
|---|---|
| Field Path | 단일 레코드를 기준으로 한 필드 경로. 예: name, owner.name |
| Label | Columns 등 필드 선택기에 표시할 이름 |
| Data Type | TEXT, NUMBER, BOOLEAN, DATE, DATETIME, URL, OBJECT, ARRAY |
더하기 버튼으로 직접 추가하거나 Test & Import를 클릭해 최대 20개 레코드를 요청하고 탐지된 필드를 병합할 수 있습니다. 가져오기 규칙은 다음과 같습니다.
- 사용자가 명시적으로 클릭할 때만 백엔드를 요청하며, 대화 상자를 여는 것만으로는 요청하지 않습니다.
- 현재 URL, Method, Query Context, Rows Path를 사용합니다.
- 중첩 객체는 점 경로로 변환하고, 배열은 배열 필드 자체만 등록합니다.
- 기존 Label, Data Type, 순서를 유지하고 새 경로만 추가합니다.
- 예제 값은 저장하지 않으며, 빈 응답이나 실패가 기존 필드를 덮어쓰지 않습니다.
Test & Import는 DEV + DESIGN_PREVIEW 페이지 요청 컨텍스트를 사용합니다. Data Source 변경 사항은 Save를 클릭해야 페이지 설정에 반영되며, Cancel은 현재 초안을 버립니다.
4.3 Columns 설정
Columns에 다음 항목을 순서대로 추가합니다.
| Label | Data Field |
|---|---|
| Code | code |
| Name | name |
| Category | category |
| Price | price |
| Stock | stock |
Data Field는 점 경로를 지원합니다. 레코드에 owner.name이 있으면 다음 값을 입력합니다.
owner.name
열 너비는 내용에 맞게 설정합니다. 코드 열은 좁게 설정할 수 있지만 이름 열에는 더 많은 공간이 필요합니다. 긴 텍스트가 테이블 전체를 늘리지 않도록 하십시오.
4.4 페이지네이션과 행 키 설정
권장 설정:
Pagination / Page Size: 10
Row Unique Key: id
Row Unique Key 필드는 안정적이고 고유해야 합니다. 중복되거나 변경될 수 있는 이름, 상태 등의 필드는 사용하지 마십시오.
Runtime에서는 페이지당 10, 20, 50개를 선택할 수 있습니다. 페이지 번호나 크기가 바뀌면 ProTable은 현재 Query Context 조건을 유지한 채 API를 다시 요청합니다.
5. Left Toolbar에 검색 컨트롤 추가
ProTable을 선택하고 Left Toolbar에 Input을 추가합니다.
Label: Key Word
Name: productKeywordInput
Allow Clear: true
예제 페이지의 Input 내부 ID는 field_bfd1e2b2입니다. 이 ID를 복사하지 말고 FX 편집기에서 방금 추가한 Input을 선택하십시오.
Allow Clear를 켜면 Input에 텍스트가 있을 때 지우기 아이콘이 표시됩니다. 아이콘은 값만 비웁니다. 키워드 없이 테이블을 다시 로드하려면 Search를 클릭해야 합니다.
일반적인 배치:
- Left Toolbar: Input, Date Picker, Select, Radio, Search
- Toolbar: Create, Import, Export 등 업무 작업
검색 조건과 Search를 같은 가로줄에 유지하려면 이 예제처럼 Search도 Left Toolbar에 넣습니다.
6. Query Context로 keyword 연결
Query Context는 Runtime의 값을 HTTP 요청 파라미터로 매핑합니다. 이 예제는 다음 흐름을 만듭니다.
Input 현재 값 → productKeyword 변수 → keyword 요청 필드
6.1 변수 추가
- ProTable의 Data Source를 편집합니다.
- Query Context에서 FX를 클릭합니다.
- Add Variable을 클릭합니다.
- Variable Name에
productKeyword를 입력합니다. - Source에서 Component Value를 선택합니다.
- Component에서 추가한
Key WordInput을 선택합니다.
6.2 요청 파라미터 추가
Query Context 파라미터 영역에 다음 값을 추가합니다.
Parameter: keyword
Value: [[productKeyword]]
Runtime은 각 요청 직전에 Input의 현재 값을 다시 읽습니다. 입력이 없으면 keyword가 비어 있으며 예제 백엔드는 빈 키워드를 무시하고 전체 레코드를 반환합니다.
6.3 자주 사용하는 변수 소스
| Source | 사용 사례 |
|---|---|
| Component Value | Input, Select, Radio, Date Picker, Uploader 등의 현재 값 |
| URL Parameter | 브라우저 URL의 쿼리 파라미터 |
| Page Context | 페이지 컨텍스트 또는 이전 상호작용이 기록한 값 |
| Current Row | 행 작업에 있는 현재 레코드의 필드 |
| Static Value | 고정 문자열, 숫자 또는 불리언 값 |
Target Value는 하나의 변수를 참조하거나 여러 값을 조합할 수 있습니다.
[[productKeyword]]
prefix-[[productKeyword]]
Target Value가 정확히 [[variableName]]이면 Runtime은 가능한 경우 원래 타입을 유지합니다. 배열, 객체, 숫자, 불리언을 먼저 문자열로 바꿀 필요가 없습니다.
7. Search 버튼 설정
Left Toolbar에 Button을 추가합니다.
Text: Search
Button Type: Primary
클릭 상호작용을 설정합니다.
- Search Button을 선택하고 Interaction editor를 엽니다.
- Component API 작업을 추가합니다.
- Target Component에서
productTable같은 현재 ProTable을 선택합니다. - Function에서
reloadTable을 선택합니다. - 상호작용을 저장합니다.
reloadTable은 새 검색을 시작합니다.
- 현재 페이지를
1로 초기화합니다. - Query Context의 컴포넌트 값을 다시 계산합니다.
- 새
current,pageSize,keyword로 API를 요청합니다.
이 동작은 일반적인 페이지 문제를 막습니다. 사용자가 2페이지에서 결과가 한 개뿐인 키워드를 입력한 상태로 current: 2를 전송하면 API는 올바르게 빈 배열을 반환합니다. Search가 reloadTable을 호출하면 current: 1이 전송되어 결과가 표시됩니다.
일반 페이지 이동은 새 검색이 아닙니다. ProTable은 current만 변경하고 현재 keyword를 유지합니다.
Search 클릭: 1페이지로 초기화 + 최신 검색 조건 사용
페이지 클릭: 페이지 번호 변경 + 현재 검색 조건 유지
8. Java 백엔드 예제
Spark 저장소의 전체 구현 위치:
spark-sample/src/main/java/hiforce/spark/sample/protable/
spark-console이 spark-sample을 포함하므로 API는 Spark Console과 함께 배포됩니다. 다음은 Custom URL에 필요한 핵심 코드입니다.
8.1 요청 DTO
package hiforce.spark.sample.protable.model;
import lombok.Getter;
import lombok.Setter;
import java.math.BigDecimal;
@Getter
@Setter
public class ProTableProductQueryRequest {
private Integer current = 1;
private Integer pageSize = 10;
private String keyword;
private String category;
private String status;
private BigDecimal minPrice;
private BigDecimal maxPrice;
}
keyword 외에도 카테고리, 상태, 가격 범위 필드를 제공합니다. 저장소의 전체 구현에는 범용 conditions와 sortBy도 포함됩니다.
8.2 행 DTO와 페이지 결과
package hiforce.spark.sample.protable.model;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.math.BigDecimal;
import java.time.Instant;
import java.util.List;
@Getter
@AllArgsConstructor
public class ProTableProductDTO {
private final Long id;
private final String code;
private final String name;
private final String category;
private final String status;
private final BigDecimal price;
private final Integer stock;
private final Instant updatedAt;
}
@Getter
@AllArgsConstructor
public class ProTablePageResult<T> {
private final long total;
private final int current;
private final int pageSize;
private final List<T> records;
}
실제 프로젝트에서는 두 public 클래스를 각각 같은 이름의 파일에 배치합니다. 여기서는 요청과 응답 계약을 함께 보여 주기 위해 한 코드 블록에 표시했습니다.
8.3 Controller
package hiforce.spark.sample.protable.web;
import hiforce.spark.base.model.result.ApiResult;
import hiforce.spark.base.web.SparkApiResultHelper;
import hiforce.spark.sample.protable.model.ProTablePageResult;
import hiforce.spark.sample.protable.model.ProTableProductDTO;
import hiforce.spark.sample.protable.model.ProTableProductQueryRequest;
import hiforce.spark.sample.protable.service.ProTableProductService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Locale;
@RestController
@RequestMapping("/spark/api/sample/protable/products")
public class ProTableProductController {
private final ProTableProductService productService;
public ProTableProductController(ProTableProductService productService) {
this.productService = productService;
}
@PostMapping
public ApiResult<ProTablePageResult<ProTableProductDTO>> query(
@RequestBody(required = false) ProTableProductQueryRequest request,
@RequestHeader(value = "Accept-Language", required = false) String language) {
Locale locale = SparkApiResultHelper.resolveLocale(language);
return SparkApiResultHelper.success(
locale,
productService.query(request),
"SPARK-SAMPLE-PROTABLE-0001");
}
}
Controller는 DTO와 페이지 결과를 ApiResult로 반환합니다. 사용자에게 보이는 성공 메시지는 Controller에 직접 쓰지 않고 Lattice Message Code SPARK-SAMPLE-PROTABLE-0001로 현지화합니다.
8.4 부분 일치 검색과 페이지네이션
package hiforce.spark.sample.protable.service;
import hiforce.spark.sample.protable.model.ProTablePageResult;
import hiforce.spark.sample.protable.model.ProTableProductDTO;
import hiforce.spark.sample.protable.model.ProTableProductQueryRequest;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
@Service
public class ProTableProductService {
private static final int MAX_PAGE_SIZE = 100;
private static final List<ProTableProductDTO> PRODUCTS = List.of(
product(1, "SPK-1001", "SparkBook Air", "COMPUTING", "ACTIVE", "999.00", 32,
"2026-07-27T14:20:00Z"),
product(2, "SPK-1002", "SparkBook Pro", "COMPUTING", "ACTIVE", "1599.00", 18,
"2026-07-28T09:10:00Z"),
product(3, "DSP-2001", "Aurora Monitor 27", "DISPLAY", "ACTIVE", "499.00", 45,
"2026-07-26T08:30:00Z"),
product(4, "DSP-2002", "Graphite Monitor 32", "DISPLAY", "ACTIVE", "699.00", 21,
"2026-07-25T16:40:00Z"),
product(5, "ACC-3001", "Pulse Mechanical Keyboard", "ACCESSORY", "ACTIVE", "129.00", 86,
"2026-07-24T11:00:00Z"),
product(6, "ACC-3002", "Flow Wireless Mouse", "ACCESSORY", "ACTIVE", "79.00", 120,
"2026-07-23T13:15:00Z"),
product(7, "ACC-3003", "Runtime Dock", "ACCESSORY", "ACTIVE", "219.00", 38,
"2026-07-22T09:45:00Z"),
product(8, "STO-4001", "Core SSD 1TB", "STORAGE", "ACTIVE", "149.00", 64,
"2026-07-21T15:20:00Z"),
product(9, "STO-4002", "Core SSD 2TB", "STORAGE", "ACTIVE", "269.00", 41,
"2026-07-20T10:05:00Z"),
product(10, "NET-5001", "Lattice Router", "NETWORK", "ACTIVE", "189.00", 27,
"2026-07-19T17:25:00Z"),
product(11, "NET-5002", "Mesh Access Point", "NETWORK", "ACTIVE", "139.00", 54,
"2026-07-18T12:35:00Z"),
product(12, "CMP-6001", "Build Station", "COMPUTING", "DRAFT", "2399.00", 7,
"2026-07-17T08:50:00Z"),
product(13, "DSP-2003", "Silk Portable Display", "DISPLAY", "ACTIVE", "329.00", 33,
"2026-07-16T14:10:00Z"),
product(14, "ACC-3004", "Spark AI Headset", "ACCESSORY", "ACTIVE", "199.00", 49,
"2026-07-15T10:45:00Z"),
product(15, "ACC-3005", "Console Camera", "ACCESSORY", "ACTIVE", "159.00", 72,
"2026-07-14T09:35:00Z"),
product(16, "STO-4003", "Archive Drive 8TB", "STORAGE", "ACTIVE", "289.00", 25,
"2026-07-13T18:20:00Z"),
product(17, "NET-5003", "Secure Gateway", "NETWORK", "DRAFT", "799.00", 5,
"2026-07-12T07:30:00Z"),
product(18, "CMP-6002", "Runtime Server", "COMPUTING", "ACTIVE", "3499.00", 4,
"2026-07-11T16:00:00Z"),
product(19, "ACC-3006", "USB-C Travel Hub", "ACCESSORY", "DISCONTINUED", "59.00", 0,
"2026-07-10T11:40:00Z"),
product(20, "DSP-2004", "Aurora Display Mini", "DISPLAY", "DRAFT", "279.00", 12,
"2026-07-09T13:05:00Z"),
product(21, "PRN-7001", "Flow Label Printer", "OFFICE", "ACTIVE", "249.00", 28,
"2026-07-08T08:25:00Z"),
product(22, "AUD-8001", "Aurora Conference Speaker", "AUDIO", "ACTIVE", "349.00", 16,
"2026-07-07T15:35:00Z"),
product(23, "PWR-9001", "Core Power Station", "POWER", "DRAFT", "899.00", 9,
"2026-07-06T10:15:00Z"));
public ProTablePageResult<ProTableProductDTO> query(ProTableProductQueryRequest request) {
ProTableProductQueryRequest query = request == null
? new ProTableProductQueryRequest()
: request;
int current = Math.max(query.getCurrent() == null ? 1 : query.getCurrent(), 1);
int pageSize = Math.min(
Math.max(query.getPageSize() == null ? 10 : query.getPageSize(), 1),
MAX_PAGE_SIZE);
String keyword = StringUtils.trimToNull(query.getKeyword());
List<ProTableProductDTO> filtered = PRODUCTS.stream()
.filter(product -> keyword == null
|| StringUtils.containsIgnoreCase(product.getCode(), keyword)
|| StringUtils.containsIgnoreCase(product.getName(), keyword))
.sorted(Comparator.comparing(ProTableProductDTO::getCode))
.toList();
long requestedOffset = (long) (current - 1) * pageSize;
int fromIndex = (int) Math.min(requestedOffset, filtered.size());
int toIndex = Math.min(fromIndex + pageSize, filtered.size());
return new ProTablePageResult<>(
filtered.size(),
current,
pageSize,
new ArrayList<>(filtered.subList(fromIndex, toIndex)));
}
private static ProTableProductDTO product(long id, String code, String name,
String category, String status, String price,
int stock, String updatedAt) {
return new ProTableProductDTO(
id, code, name, category, status,
new BigDecimal(price), stock, Instant.parse(updatedAt));
}
}
핵심 동작은 세 가지입니다.
StringUtils.trimToNull은 빈 키워드를null로 바꾸어 키워드 필터를 적용하지 않습니다.containsIgnoreCase는 상품 코드와 이름을 모두 검사하므로Graphite로Graphite Monitor 32를 찾습니다.- 필터 적용 후
total을 계산하고current와pageSize로 현재 페이지를 자릅니다.
keyword 흐름에 집중하기 위해 위 Service에서는 전체 구현의 카테고리, 상태, 가격, 범용 조건, 다중 필드 정렬을 생략했습니다. 23개 상품과 페이지 동작은 실제 예제와 같습니다.
8.5 GET API 확장
GET도 지원하려면 쿼리 파라미터로 같은 Request DTO를 만들고 POST 검색 메서드를 재사용합니다.
@GetMapping
public ApiResult<ProTablePageResult<ProTableProductDTO>> queryByGet(
@RequestParam(defaultValue = "1") Integer current,
@RequestParam(defaultValue = "10") Integer pageSize,
@RequestParam(required = false) String keyword,
@RequestHeader(value = "Accept-Language", required = false) String language) {
ProTableProductQueryRequest request = new ProTableProductQueryRequest();
request.setCurrent(current);
request.setPageSize(pageSize);
request.setKeyword(keyword);
return query(request, language);
}
실제 API는 다음 선택 파라미터도 지원합니다.
| 파라미터 | 설명 |
|---|---|
category | 대소문자 구분 없는 카테고리 정확 일치 |
status | 대소문자 구분 없는 상태 정확 일치 |
minPrice / maxPrice | 가격 범위 |
conditions | 범용 조건 배열 |
sortBy | 필드 이름을 asc 또는 desc로 매핑한 순서 있는 Map |
GET에서는 conditions와 sortBy가 URL 인코딩된 JSON 문자열입니다. POST에서는 JSON 배열 또는 객체로 직접 보냅니다.
9. 고급 설정
9.1 여러 검색 조건
Left Toolbar에 Category Select, Status Radio, Price Input을 추가하고 Query Context에서 각각 매핑할 수 있습니다.
{
"keyword": "[[productKeyword]]",
"category": "[[productCategory]]",
"status": "[[productStatus]]",
"minPrice": "[[minimumPrice]]",
"maxPrice": "[[maximumPrice]]"
}
빈 값을 필터 없음으로 처리할지는 백엔드가 정의합니다. 선택 문자열에는 trimToNull을 사용하고 선택 숫자에는 특수 문자열 대신 null을 사용하십시오.
9.2 Row Actions
Row Actions에 View, Edit, Delete 등의 작업을 추가할 수 있습니다. 행 작업에는 현재 레코드 컨텍스트가 있으므로 FX에서 현재 행의 id, code 등을 읽을 수 있습니다.
수정 성공 후 현재 ProTable의 reloadTable을 호출해 서버의 최신 데이터를 다시 가져옵니다. 위험한 작업에는 확인 단계를 추가하고 권한과 업무 검증을 백엔드에서도 수행하십시오.
9.3 setTableData
버튼이 다른 API를 호출해 전체 레코드 배열을 이미 얻은 경우 다음 함수를 사용할 수 있습니다.
setTableData(data)
전달한 배열로 로컬 테이블 데이터를 교체하고 페이지를 1로 초기화합니다. 데이터를 저장하지 않으며 외부 응답 객체에서 data.records를 자동으로 추출하지 않습니다. 인수는 레코드 배열 자체여야 합니다.
일반 원격 페이지네이션에는 reloadTable을 사용하여 조건, 전체 개수, 페이지 처리를 백엔드에서 일관되게 관리하십시오.
9.4 표시 속성
| 속성 | 설명 |
|---|---|
| Enable Row Number | 첫 열에 페이지를 이어 가는 연속 행 번호 표시 |
| Table Size | large, middle, small 밀도 설정 |
| Show Header | 열 헤더 행 표시 여부 |
| Bordered | 테이블 테두리 표시 여부 |
| Row Unique Key | 각 행의 고유 식별 필드, 기본값은 id |
10. 예제 검증
페이지를 저장한 뒤 Preview와 Runtime에서 확인합니다.
- 키워드를 비워 두면 첫 로드에서 1페이지와 전체 23개가 표시됩니다.
- 2페이지로 이동하면 다음 데이터가 표시됩니다.
- 2페이지에서
Graphite를 입력하고 Search를 클릭합니다. - 요청은
current: 1을 사용하며DSP-2002 / Graphite Monitor 32만 반환합니다. - Input을 지우고 Search를 클릭하면 1페이지부터 전체 23개가 다시 표시됩니다.
- 여러 페이지에 걸쳐 일치하는 키워드를 검색한 뒤 페이지를 이동하고 이후 요청에도 키워드가 포함되는지 확인합니다.
브라우저 개발자 도구에서 POST Request Payload를 확인합니다.
{
"current": 1,
"pageSize": 10,
"keyword": "Graphite"
}
응답의 data.total, data.current, data.pageSize, data.records가 요청과 일치하는지 확인합니다.
11. 문제 해결
11.1 2페이지에서 Graphite를 검색하면 찾을 수 없음
Search가 ProTable의 reloadTable 대신 일반 요청으로 설정되었을 수 있습니다.
Interaction: Component API
Target Component: current ProTable
Function: reloadTable
reloadTable은 페이지를 1로 초기화합니다. 일반 페이지 이동은 현재 조건을 유지합니다.
11.2 키워드를 입력해도 데이터가 바뀌지 않음
다음 순서로 확인합니다.
- Query Context에
keyword파라미터가 있습니다. productKeyword가 올바른 Input의 Component Value를 읽습니다.- Target Value가
[[productKeyword]]입니다. - 백엔드 DTO 필드도
keyword입니다. - Search가 현재 ProTable의
reloadTable을 호출합니다.
11.3 Input에 지우기 아이콘이 없음
Allow Clear가 켜져 있고 Runtime Input에 텍스트가 있는지 확인합니다. 빈 Input에는 아이콘이 표시되지 않습니다. 지운 뒤 Search를 클릭하십시오.
11.4 API에는 데이터가 있지만 테이블이 비어 있음
Rows Path를 확인합니다. 이 예제의 레코드 배열 위치:
data.records
잘못된 사용자 정의 경로를 설정하면 ProTable은 다른 경로를 추측하지 않고 빈 배열을 표시합니다.
11.5 전체 개수 또는 페이지 번호가 올바르지 않음
다음을 확인합니다.
- Total Path가 필터 적용 후 전체 개수를 가리킴
- Current Page Path가 서버가 반환한 현재 페이지를 가리킴
- Page Size Path가 서버가 실제 사용한 페이지 크기를 가리킴
- 백엔드 페이지 번호가 1부터 시작함
11.6 API가 401, 403 또는 CORS 오류를 반환함
401: 세션이 유효하지 않거나 API에 인증이 필요합니다.403: 현재 사용자에게 API 또는 데이터 권한이 없습니다.- CORS: 외부 서비스가 현재 Spark 출처를 허용하지 않습니다.
외부 시스템에는 External Connection 또는 통제된 백엔드 프록시를 사용하십시오. 페이지 설정에 장기 자격 증명을 노출하지 마십시오.
11.7 GET conditions를 파싱할 수 없음
GET은 배열과 객체를 JSON 문자열로 직렬화합니다. URL 디코딩 후 JSON 파서를 사용하고 복잡한 객체를 쉼표로 나누지 마십시오. 복잡한 조건에는 POST를 권장합니다.
11.8 Custom URL의 Data Field에 선택 항목이 없음
Data Source를 열고 Response Fields를 설정한 뒤 저장하십시오. 직접 추가하거나 Test & Import를 사용할 수 있습니다. Response Fields는 데이터 소스 계약에 속하므로 테이블 미리보기 레코드 하나를 불러와도 필드 목록이 임시로 변경되지 않습니다.
12. 설정 확인 목록
- ProTable에 안정적이고 고유한 컴포넌트 이름이 있음
- Data Source가 Custom URL로 설정됨
- Response Fields를 직접 설정하거나 Test & Import로 가져온 뒤 저장함
- URL, Request Method, 인증이 올바름
- Rows, Total, Current Page, Page Size 경로가 응답과 일치함
- Columns의 Data Field가 레코드 필드와 일치함
- Row Unique Key가 안정적이고 고유함
- Query Context 파라미터 이름이 백엔드 DTO 필드와 일치함
- FX 변수가 올바른 Component Value를 읽음
- Search가 현재 ProTable의
reloadTable을 호출함 - 새 검색이 1페이지로 돌아감
- 일반 페이지 이동이 현재 조건을 유지함
- 빈 키워드, 일치 키워드, 일치하지 않는 키워드를 모두 검증함
- Preview와 Runtime에서 도구 모음, 테이블, 페이지네이션을 확인함
위 설정을 완료하면 ProTable을 사용자 정의 HTTP API의 안정적인 목록 화면으로 사용하고 검색 컨트롤, 서버 페이지네이션, 페이지 상호작용을 올바르게 연결할 수 있습니다.