> ## Documentation Index
> Fetch the complete documentation index at: https://villagesql.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# MySQL 테이블을 REST API로 공개하는 방법

> VillageSQL의 vsql_rest 확장으로 MySQL 테이블을 HTTP로 제공하는 방법 — 필터, 열 선택, 페이지네이션, 테이블별 메서드, 그리고 먼저 바꿔야 하는 기본값입니다.

> [card] [VillageSQL is a drop-in replacement for MySQL with extensions.](https://villagesql.com/docs/en/8.4/stable/install-server.md)
>
> All examples on this page work on VillageSQL. Install Now →

> [!NOTE]
>
> 이 가이드는 프리뷰 기능을 사용합니다. 서버를
> `--vsql_allow_preview_extensions=ON`으로 시작하세요. 그렇지 않으면 `INSTALL EXTENSION`이 거부됩니다.

몇 개의 테이블 앞에 읽기 전용 JSON 엔드포인트를 두려면 보통 작은 서비스를 작성하고, 배포하고, 데이터베이스 계정을 주고, 살아 있게 유지해야 합니다. VillageSQL의 `vsql_rest` 확장은 대신 서버 프로세스 안에서 그 테이블을 제공하므로, 두 번째 프로세스도 새 배포도 없습니다.

기본값은 네트워크가 아니라 개발자 머신을 위해 만들어졌습니다. 어디에든 열기 전에 아래의 "포트를 노출하기 전에"를 읽으세요.

## 켜기

```sql theme={null}
INSTALL EXTENSION vsql_rest;
```

먼저 데이터베이스와 테이블을 만드세요. 리스너는 시작할 때 데이터베이스 구조를 읽고 `vsql_rest.schema_ttl`초(기본값 60) 동안 캐시하므로, 그 뒤에 만든 테이블은 캐시가 만료될 때까지 `table not found`로 응답합니다.

```sql theme={null}
CREATE DATABASE shop;
USE shop;

CREATE TABLE products (
    id       INT PRIMARY KEY,
    name     VARCHAR(40),
    category VARCHAR(20),
    price    DECIMAL(8,2),
    in_stock INT
);

INSERT INTO products VALUES
    (1, 'Budget Widget',   'widget', 29.99, 120),
    (2, 'Standard Widget', 'widget', 79.99,  40),
    (3, 'Premium Widget',  'widget', 149.99,  4),
    (4, 'Cable, 2m',       'cable',   9.99, 500),
    (5, 'Cable, 5m',       'cable',  14.99,   0);
```

그다음 데이터베이스를 지정하고, 제공할 테이블을 지정하고, 리스너를 시작하세요:

```sql theme={null}
SET GLOBAL vsql_rest.schema = 'shop';
SET GLOBAL vsql_rest.allowed_tables = 'products';
SET GLOBAL vsql_rest.vsql_rest_enabled = ON;
```

## 읽기

테이블이 경로이고, 쿼리 문자열이 필터링을 합니다. 각 필터는 `column=operator.value`입니다:

```bash theme={null}
curl 'http://127.0.0.1:3000/products?category=eq.widget&order=price.desc&limit=2'
```

```json theme={null}
[{"id":3,"name":"Premium Widget","category":"widget","price":"149.99","in_stock":4},{"id":2,"name":"Standard Widget","category":"widget","price":"79.99","in_stock":40}]
```

원하는 열만 요청하세요:

```bash theme={null}
curl 'http://127.0.0.1:3000/products?select=name,price&in_stock=eq.0'
```

```json theme={null}
[{"name":"Cable, 5m","price":"14.99"}]
```

`or=(...)`로 조건을 결합하세요:

```bash theme={null}
curl 'http://127.0.0.1:3000/products?or=(category.eq.cable,price.gt.100)&select=id,name'
```

```json theme={null}
[{"id":3,"name":"Premium Widget"},{"id":4,"name":"Cable, 2m"},{"id":5,"name":"Cable, 5m"}]
```

`limit`과 `offset`으로 페이지를 넘기세요:

```bash theme={null}
curl 'http://127.0.0.1:3000/products?order=id.asc&limit=2&offset=2'
```

```json theme={null}
[{"id":3,"name":"Premium Widget","category":"widget","price":"149.99","in_stock":4},{"id":4,"name":"Cable, 2m","category":"cable","price":"9.99","in_stock":500}]
```

연산자는 `eq`, `neq`, `lt`, `lte`, `gt`, `gte`, `like`, `cs_like`, `in`, `is`입니다. `in`은 `?id=in.(1,3)`처럼 목록을 받고, `is`는 `?price=is.null` 또는 `?price=is.not.null`처럼 null을 받습니다. `limit`이 없는 요청은 최대 `vsql_rest.max_rows`행을 반환하며, 이 값은 1000에서 시작합니다.

## 허용 목록

`allowed_tables`에 없는 테이블은 API 입장에서 존재하지 않습니다:

```bash theme={null}
curl 'http://127.0.0.1:3000/orders'
```

```json theme={null}
{"message":"table not found: orders","details":null,"hint":null,"code":"VSQL0001"}
```

응답은 404이며, 테이블이 없든 단지 숨겨졌든 똑같이 읽힙니다.

> [!WARNING]
>
> `allowed_tables`를 비워 두는 것은 테이블이 없다는 뜻이 아닙니다. **모든** 테이블이라는
> 뜻입니다. 리스너를 활성화하기 전에 공개하려는 테이블로
> 설정하세요.

## 테이블별 메서드 제한

`table_methods`는 각 테이블이 받아들이는 HTTP 메서드를 `table:METHOD,METHOD` 형식으로, 테이블 사이에 `|`를 두고 지정합니다:

```sql theme={null}
SET GLOBAL vsql_rest.table_methods = 'products:GET';
```

기본적으로 모든 테이블이 모든 메서드를 받아들이므로, 리스너가 시작되기 전에 이것을 설정하세요. 테이블이 허용하지 않은 메서드는 거부됩니다:

```json theme={null}
{"message":"method not allowed: POST","details":null,"hint":null,"code":"VSQL0001"}
```

쓰기를 허용하는 것도 같은 설정입니다:

```sql theme={null}
SET GLOBAL vsql_rest.table_methods = 'products:GET,POST';
```

```bash theme={null}
curl -X POST -H 'Content-Type: application/json' \
     -d '{"id":6,"name":"Cable, 10m","category":"cable","price":19.99,"in_stock":10}' \
     'http://127.0.0.1:3000/products'
```

성공한 삽입은 빈 본문과 함께 201로 응답합니다. 중복 키는 409로 돌아옵니다:

```json theme={null}
{"message":"Duplicate entry '6' for key 'products.PRIMARY'","details":null,"hint":null,"code":"VSQL0001"}
```

## 동작 지켜보기

다섯 개의 상태 카운터가 리스너에 대해 보고하며, 실제로 바인딩한 포트도 포함합니다:

```sql theme={null}
SHOW STATUS LIKE 'vsql_rest%';
```

```
+-----------------------------+-------+
| Variable_name               | Value |
+-----------------------------+-------+
| vsql_rest.connections_total | 0     |
| vsql_rest.http_port         | 3000  |
| vsql_rest.https_port        | 0     |
| vsql_rest.requests_active   | 0     |
| vsql_rest.requests_total    | 16    |
+-----------------------------+-------+
```

`http_port`는 아무것도 수신 대기하지 않을 때 0으로 읽히며, 리스너가 시작되었는지 확인하는 가장 빠른 방법입니다. `vsql_rest.port`를 0으로 설정하면 운영 체제에 포트 선택을 맡기고, 어떤 포트를 골랐는지는 이 카운터에서 읽습니다.

## 포트를 노출하기 전에

기본값은 문을 열어 두며, 각각은 따로 보면 합리적입니다.

`require_auth`는 `OFF`에서 시작하므로 어떤 요청도 토큰을 지니거나 필요로 하지 않습니다. 빈 `allowed_tables`는 스키마 전체를 공개합니다. 빈 `table_methods`는 `DELETE`를 포함한 모든 메서드를 허용하며, 그 상태의 리스너에 대한 `DELETE`는 성공합니다:

```bash theme={null}
curl -X DELETE 'http://127.0.0.1:3000/products?id=eq.6'
```

204로 응답하고, 행은 사라집니다.

종합하면, 아무것도 설정하지 않고 켠 리스너는 포트에 도달할 수 있는 누구에게나 스키마의 모든 테이블에서 행을 읽고, 삽입하고, 갱신하고, 삭제하게 해 줍니다. 노트북에서는 편리합니다. 그 외 어디서든, 활성화하기 전에 네 가지를 바꾸세요:

```sql theme={null}
SET GLOBAL vsql_rest.allowed_tables = 'products';
SET GLOBAL vsql_rest.table_methods  = 'products:GET';
SET GLOBAL vsql_rest.require_auth   = ON;
SET GLOBAL vsql_rest.jwt_secret     = '...';
```

그다음 `ssl_cert`와 `ssl_key`를 설정해 HTTPS로 제공하세요. 바인드 주소 설정은 없고, 리스너는 시작하는 순간부터 모든 인터페이스에 바인딩하므로, 방화벽에서 포트를 막고 리버스 프록시만 도달하게 하세요.

## 문제 해결

| 증상 | 해결 방법 |
| - | - |
| 설치 시 `ERROR 3219 ... requires preview capabilities` | 서버를 `--vsql_allow_preview_extensions=ON`으로 시작 |
| 연결 거부 | 리스너가 꺼져 있음. `vsql_rest.vsql_rest_enabled`를 확인한 뒤 `SHOW STATUS`에서 `vsql_rest.http_port`를 읽기 |
| 존재하는 테이블에 `table not found` | `allowed_tables`에 빠져 있거나, `vsql_rest.schema`가 다른 데이터베이스를 가리킴 |
| 쓰기가 405를 반환 | `table_methods`가 그 테이블에 해당 메서드를 허용하지 않음 |
| 확장을 다시 설치한 뒤 설정이 사라짐 | `UNINSTALL EXTENSION`은 유지된 설정을 삭제함. 다시 적용 |

## 함께 보기

* [vsql\_rest](https://villagesql.com/docs/ko/extensions/vsql-rest.md) — 확장이 추가하는 모든 설정이 있는 참조 페이지
* [SQL에서 HTTP 요청 보내기](https://villagesql.com/docs/ko/guides/http-requests-in-mysql.md) — 같은 서버가 응답하는 대신 밖으로 호출하기
* [외부 API 데이터로 행 보강하기](https://villagesql.com/docs/ko/guides/rest-api-enrichment.md) — 쿼리를 다른 사람의 API와 결합하기
* [MySQL 보안 강화](https://villagesql.com/docs/ko/guides/security-hardening.md) — 포트에 도달할 수 있게 되기 전에 더 닫아야 할 것


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.