FreeGridv1.6.0English

서버 예제 (Java·Node·Python·PHP·C#·Go)

6개 언어의 서버 도우미가 그리드 요청을 안전한 SQL로 바꿉니다: 페이지, 정렬, 필터, 빠른 검색, 고급 필터, 서버 그룹, 변경 저장.

국내 업무 화면에서 흔한 "조회 → 편집 → 저장"은 loadData와 saveChanges 두 함수면 됩니다. 조회는 검색 조건과 함께 POST JSON으로 보내고, 저장은 추가·수정·삭제 행을 { created, updated, deleted }로 한 번에 보낸 뒤 성공하면 변경 표시를 지웁니다. 수만 건이 넘으면 restDatasource로 서버가 정렬·필터·페이지·그룹을 처리하게 하세요. Spring Security를 쓰면 csrfHeaders가 JSP의 _csrf 메타 태그를 읽어 붙입니다.

import { createGrid, loadData, saveChanges, restDatasource, csrfHeaders } from '@freegridjs/core';

// search → edit → save (eGovFrame / Spring MVC screens)
await loadData(grid, '/order/selectOrders.do', { headers: csrfHeaders, params: () => ({ status: form.status.value }) });
await saveChanges(grid, '/order/saveOrders.do', { headers: csrfHeaders });   // { created, updated, deleted }

// very large lists: the server sorts, filters, pages and groups
createGrid(el, { columns, datasource: restDatasource('/api/orders/rows'), pagination: { pageSize: 50 }, groupBy: ['status'] });

서버 쪽은 Java 클래스 네 개(GridRequest, GridQuery, GridRows, GridSave)를 내려받아 프로젝트에 넣으면 됩니다. Java 8 문법이라 전자정부 표준프레임워크 3.x~5.x와 Spring Boot에서 모두 동작하고, JdbcTemplate·MyBatis 어느 쪽에도 맞습니다. GridQuery는 허용한 컬럼만 SQL로 바꾸고 값은 모두 바인딩하므로 SQL 인젝션 걱정이 없습니다. 숫자·날짜가 잘못된 요청은 IllegalArgumentException이 나므로 400으로 돌려주세요. 표준프레임워크 5.0.1 공식 웹 샘플(MyBatis·HSQL)과 Spring Boot에서 테스트했습니다.

Java 서버 도우미 내려받기 (freegrid-java-server.zip)

// Spring Boot + JdbcTemplate.  restDatasource('/api/orders/rows'), saveChanges(grid, '/api/orders/save')
@RestController
@RequestMapping("/api/orders")
public class OrderController {

    /** Grid column id → SQL column. Only these can be sorted, filtered or grouped. */
    private static final GridQuery QUERY = new GridQuery()
        .column("orderNo", "ORDER_NO", GridQuery.Type.TEXT)
        .column("orderDate", "ORDER_DATE", GridQuery.Type.DATE)
        .column("status", "STATUS", GridQuery.Type.TEXT)
        .column("qty", "QTY", GridQuery.Type.NUMBER)
        .defaultOrderBy("ID");
    // .dialect(GridQuery.Dialect.LIMIT) for MySQL, MariaDB, PostgreSQL (LIMIT ? OFFSET ?)

    private final JdbcTemplate jdbc;
    public OrderController(JdbcTemplate jdbc) { this.jdbc = jdbc; }

    @PostMapping("/rows")
    public Map<String, Object> rows(@RequestBody Map<String, Object> body) {
        GridQuery.Sql sql = QUERY.build(GridRequest.from(body));
        Long total = jdbc.queryForObject("SELECT COUNT(*) FROM ORDERS" + sql.where, Long.class, sql.countArgs());
        List<Map<String, Object>> rows = jdbc.queryForList(
            "SELECT ID AS \"id\", ORDER_NO AS \"orderNo\", ORDER_DATE AS \"orderDate\", STATUS AS \"status\", QTY AS \"qty\" FROM ORDERS"
                + sql.where + sql.orderBy + sql.page, sql.pageArgs());
        return GridRows.of(rows, total);              // { rows, total }, dates as "YYYY-MM-DD"
    }

    @PostMapping("/save")
    @Transactional
    public Map<String, Object> save(@RequestBody Map<String, Object> body) {
        GridSave save = GridSave.from(body);          // created / updated / deleted
        for (Map<String, Object> r : save.getDeleted()) jdbc.update("DELETE FROM ORDERS WHERE ID = ?", r.get("id"));
        for (Map<String, Object> r : save.getUpdated())
            jdbc.update("UPDATE ORDERS SET STATUS = ?, QTY = ? WHERE ID = ?", r.get("status"), r.get("qty"), r.get("id"));
        for (Map<String, Object> r : save.getCreated())
            jdbc.update("INSERT INTO ORDERS (ORDER_NO, ORDER_DATE, STATUS, QTY) VALUES (?, ?, ?, ?)",
                r.get("orderNo"), GridSave.date(r.get("orderDate")), r.get("status"), r.get("qty"));
        return Collections.singletonMap("ok", true);
    }

    /** Bad numbers, dates or filter ops answer 400 instead of 500. */
    @ExceptionHandler(IllegalArgumentException.class)
    public ResponseEntity<Map<String, Object>> bad(IllegalArgumentException e) {
        return ResponseEntity.badRequest().body(Collections.singletonMap("message", e.getMessage()));
    }
}

Node.js·Python·PHP·C#·Go

Java가 아니어도 같은 요청 형식을 그대로 처리하는 도우미를 언어별로 준비했습니다. 모두 외부 라이브러리 없이 파일 하나(Go는 패키지 하나)로 되어 있고, Java 도우미와 같은 규칙을 지킵니다. 허용한 컬럼만 정렬·필터·그룹할 수 있고, 모든 값은 바인딩 파라미터로 들어가며, LIKE 검색의 %·_는 이스케이프합니다. 잘못된 숫자·날짜·연산자는 400 { message }로 답합니다. 거르는 규칙은 화면의 필터와 같습니다. 범위의 위아래가 뒤집혀도 같은 결과가 나오고, 빠른 검색은 띄어쓰기로 나눈 단어가 모두 들어 있는 행을 찾으며, 날짜는 시간이 있는 컬럼도 날짜 단위로 비교합니다. POST JSON과 method: 'GET' 요청을 모두 읽습니다(Java 도우미도 같습니다). 빠른 검색, 고급 필터, 서버 그룹, 변경 저장까지 지원하며, 저장소의 examples 폴더에 1,000건 예제 데이터로 바로 실행되는 프로젝트가 있습니다.

import { createQuery, readRequest, readSave, toRows } from './freegrid-query.js';

const QUERY = createQuery({
  columns: {                                   // grid column id → SQL column: only these can be sorted / filtered
    orderNo: { sql: 'ORDER_NO', type: 'text' },
    orderDate: { sql: 'ORDER_DATE', type: 'date' },
    qty: { sql: 'QTY', type: 'number' },
  },
  dialect: 'limit',                            // SQL Server / Oracle: 'standard'; pg: placeholder: 'dollar'
  defaultOrderBy: 'ID',
});

app.post('/api/orders/rows', (req, res) => {
  const sql = QUERY.build(readRequest(req.body));
  const total = db.prepare('SELECT COUNT(*) AS n FROM ORDERS' + sql.where).get(sql.countArgs).n;
  const rows = db.prepare(SELECT + sql.where + sql.orderBy + sql.page).all(sql.args);
  res.json(toRows(rows, total));               // { rows, total }, dates as "YYYY-MM-DD"
});

app.post('/api/orders/save', (req, res) => {
  const { created, updated, deleted } = readSave(req.body);
  // INSERT / UPDATE / DELETE in one transaction
});

ID는 브라우저에서 오므로, 수정·삭제 전에 로그인한 사용자가 그 행을 바꿀 권한이 있는지(예: WHERE ID = ? AND OWNER_ID = ?) 꼭 확인하세요.

이 페이지에 대한 질문

어떤 서버와 함께 쓸 수 있나요?

JSON으로 답하는 서버라면 무엇이든 됩니다. Java(Spring Boot, 전자정부 프레임워크), Node.js, Python, PHP, C#(.NET), Go용 도우미가 그리드 요청을 안전한 SQL로 바꿔 줍니다.

서버 필터가 SQL 인젝션에 안전한가요?

네. 허용한 컬럼만 정렬·필터할 수 있고, 모든 값은 바인딩 파라미터로 들어가며, LIKE 검색은 %와 _를 이스케이프합니다.