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

> CREATE HANDLER 참고 문서

# CREATE HANDLER

서버 설정 파일을 편집하지 않고 SQL에서 정의한 사용자 정의 HTTP handler를 생성합니다. SQL로 정의한 handler는 구성 기반 [HTTP 인터페이스 handler](/docs/ko/concepts/features/interfaces/http)를 대체할 수 있습니다.

<div id="syntax">
  ## 구문
</div>

```sql theme={null}
CREATE HANDLER [IF NOT EXISTS] name [ON CLUSTER cluster]
[PROTOCOL protocol_name|ANY]
URL [PREFIX|REGEXP] '/path'
[METHODS (GET, POST)]
[TYPE query]
AS [SELECT|INSERT|...] ...
```

지정된 `name`으로 handler를 생성합니다. 이 이름은 SQL 쿼리를 통해 handler를 관리하고, 진단 메시지에 표시하며, handler의 순서를 정하는 데 사용됩니다.

<div id="clauses">
  ## 절
</div>

* `PROTOCOL` — 선택 사항입니다. 프로토콜 이름을 지정하면 핸들러는 지정된 [조합형 프로토콜](/docs/ko/concepts/features/configuration/server-config/composable-protocols)에서만 활성화됩니다. 그렇지 않으면 핸들러는 내장 `http`/`https` 포트와 모든 HTTP 유형의 [조합형 프로토콜](/docs/ko/concepts/features/configuration/server-config/composable-protocols) 리스너를 비롯한 모든 HTTP 엔드포인트에서 활성화됩니다. `PROTOCOL ANY`는 후자의 기본 동작을 명시적으로 선택하며, `ALTER HANDLER`에서는 이전에 설정된 프로토콜 제한을 제거합니다. 문자 그대로 `any`라는 이름의 프로토콜은 백쿼트로 참조할 수 있습니다: ``PROTOCOL `any` ``.
* `URL` — 필수입니다. 정확한 URL, `URL PREFIX` 또는 `URL REGEXP` 형식으로 지정할 수 있습니다. 정확한 URL과 접두사는 생성 또는 변경 시점에 모호성을 검사하며, 모호성이 있으면 예외가 발생합니다. 정규식은 모호성을 검사할 수 없습니다. URL은 `?` 쿼리 문자열과 `#` 프래그먼트 식별자를 제외하고 일치시킵니다. `URL PREFIX`는 기본 경로로서 경로 세그먼트 경계에서 일치합니다. 이는 [구성으로 정의된 핸들러](/docs/ko/concepts/features/interfaces/http)의 `url_prefix` 규칙과 동일한 의미입니다. `URL PREFIX '/api/v1'`는 `/api/v1`, `/api/v1/`, `/api/v1/write`와 일치하지만 `/api/v1beta`와는 일치하지 않습니다. 접두사 끝의 `/`는 무시되므로 `'/api/v1/'`와 `'/api/v1'`는 동일하게 동작합니다.
* `METHODS` — 선택 사항입니다. 허용되는 HTTP 메서드 목록입니다. 기본값은 `GET`뿐입니다. 지원되는 메서드는 `GET`, `POST`, `PUT`, `DELETE`입니다. 데이터를 변경하는 메서드인 `POST`, `PUT`, `DELETE`는 수정 쿼리를 실행할 수 있으며, `GET`, `HEAD`와 같은 안전한 메서드는 항상 `readonly` 모드에서 실행됩니다. 따라서 데이터를 수정하는 쿼리(예: `INSERT` 또는 DDL)를 실행하는 핸들러는 최소 하나의 데이터 변경 메서드를 허용해야 합니다. 읽기 전용 메서드만(예: 기본값인 `GET`) 허용하는 이러한 핸들러를 생성하면 예외가 발생합니다. `readonly` 모드에서도 부수 효과가 유지되는 쿼리는 특수한 경우입니다. `BACKUP` 및 `RESTORE`는 지속적인 부수 효과를 발생시키며, 세션을 변경하는 SQL 문 `SET`, `SET ROLE`, `USE`, `BEGIN TRANSACTION`, `COMMIT`, `ROLLBACK`, `SET TRANSACTION SNAPSHOT`은 `session_id` 사용 시 요청 간에 유지되는 세션 또는 트랜잭션 상태를 변경합니다. 또한 `CREATE TEMPORARY TABLE` / `CREATE TEMPORARY VIEW`는 세션 내에서 유지되는 객체를 생성하지만, 안전한 메서드의 `readonly` 모드는 이들 중 어느 것도 차단하지 않습니다. 기존 임시 테이블에 대한 변경도 `readonly` 모드에서 차단되지 않으므로, 이를 대상으로 할 수 있는 쿼리도 동일하게 처리됩니다. 데이터베이스로 한정되지 않은 대상 테이블을 가진 `INSERT`(한정되지 않은 이름은 세션 임시 테이블로 해석될 수 있음), `DROP TEMPORARY TABLE`, 데이터베이스로 한정되지 않은 테이블의 `DROP TABLE` / `TRUNCATE TABLE`, 데이터베이스로 한정되지 않은 테이블의 `ALTER`이 이에 해당합니다(`ALTER TEMPORARY TABLE`은 동일한 SQL 문입니다). 데이터베이스로 한정된 대상은 임시 테이블일 수 없으므로 이러한 쿼리에는 이 규칙이 적용되지 않습니다. HTTP는 안전한 메서드에 부수 효과가 없을 것을 요구합니다(`GET`에 대해 선언된 핸들러는 응답 본문이 억제되어 효과를 확인할 수 없는 `HEAD`에도 제공됩니다). 따라서 이러한 쿼리를 실행하는 핸들러는 *데이터 변경 메서드만* 나열해야 하며, 안전한 메서드를 포함하도록 생성하거나 변경하면 예외가 발생합니다. 복합 SQL 문은 내부 SQL 문까지 검사합니다. `statement1 PARALLEL WITH statement2 ...` 및 `EXECUTE AS <user> <statement>`에서는 실제로 실행되는 래핑된 SQL 문에 위 규칙이 적용됩니다(각 SQL 문은 `readonly` 모드를 유지하는 핸들러 컨텍스트의 복사본에서 실행됨). 단독 `EXECUTE AS <user>`는 전체 세션을 다른 사용자로 실행하므로 자체적으로 세션 변경으로 간주됩니다. 또한 단독이거나 SQL 문을 래핑하는 모든 `EXECUTE AS` 핸들러는 최소 하나의 데이터 변경 메서드를 허용해야 합니다. 가장에는 `IMPERSONATE` 권한이 필요하며, 안전한 메서드의 `readonly` 모드는 이를 거부합니다.
* `TYPE` — 선택 사항입니다. 현재 지원되는 유일한 유형은 `query`입니다.
* `AS` — 이 핸들러가 호출할 SQL 쿼리입니다. 쿼리는 매개변수화할 수 있습니다. 쿼리는 핸들러를 생성하거나 변경할 때 구문적 정확성만 검사하며, 분석하지는 않습니다. 예를 들어 핸들러 생성 시점에 쿼리에서 참조하는 테이블이 없을 수 있습니다. `FORMAT` 및 유사한 절은 전체 `CREATE`/`ALTER` SQL 문이 아니라 쿼리에 속합니다. 모호성을 피하기 위해 쿼리를 괄호로 묶을 수 있습니다. `INSERT` 쿼리에는 `VALUES` 또는 `FORMAT` 절 뒤에 인라인 데이터가 포함되어서는 안 됩니다. 인라인 페이로드는 핸들러 정의에 보존할 수 없으므로, 이러한 핸들러를 생성하거나 변경하면 예외가 발생합니다. 데이터는 HTTP 본문에서 제공하거나 `INSERT ... SELECT`로 계산해야 합니다. 본문을 읽는 쿼리가 있는 핸들러에 대한 요청, 즉 본문에서 데이터를 가져오는 `INSERT` 또는 `_request_body` 매개변수를 사용하는 쿼리는 길이를 선언해야 합니다. `Content-Length` 헤더가 없는 청크되지 않은 요청에는 `411 Length Required`로 응답합니다. 그렇지 않으면 본문을 스트림 끝까지 읽게 되어 연결이 끊긴 경우도 완전한 요청으로 받아들여질 수 있기 때문입니다. 이러한 핸들러의 모든 메서드도 본문을 포함하는 메서드(`POST`, `PUT` 또는 `DELETE`)여야 합니다. `METHODS` 절에서 안전한 메서드(예: 기본값인 `GET`)로 생성하면 예외가 발생합니다. 안전한 메서드는 요청 본문을 제공하지 않으므로 쿼리가 빈 본문을 조용히 읽게 되기 때문입니다. 선언된 `GET`은 `HEAD` 요청에도 제공되므로, 안전한 메서드와 본문을 포함하는 메서드를 혼합하면 해당 호출을 계속 사용할 수 있습니다. `INSERT ... SELECT`는 본문을 읽지 않습니다(데이터는 `SELECT`에서 가져옵니다). 따라서 `SELECT`가 요청 본문에서 공급되는 `input` 테이블 함수를 읽는 경우를 제외하면 이러한 요구 사항이 적용되지 않습니다. 본문을 읽는 `INSERT`는 핸들러 자체의 쿼리여야 합니다. `EXECUTE AS` 및 `PARALLEL WITH`는 자신이 감싼 SQL 문을 요청 본문 없이 실행하므로, 모든 업로드를 조용히 버리는 대신 이러한 구문으로 감싼 쿼리는 생성 시 거부됩니다. 본문을 읽는 쿼리는 `_request_body` 매개변수도 사용해서는 안 됩니다. 요청 본문은 하나뿐이며, `_request_body`를 바인딩하면 쿼리가 입력 데이터를 읽기 전에 본문을 소비하므로 모든 업로드가 조용히 손실되는 대신 이러한 핸들러는 생성 시 거부됩니다. 쿼리 자체의 본문 입력 또는 `_request_body` 중 하나만 사용하고 둘 다 사용하지 마십시오. 본문을 읽지 않는 핸들러에는 이러한 요구 사항이 없습니다. 이러한 핸들러에 대한 요청 본문은 무시되며 핸들러 쿼리에 절대 추가되지 않습니다. 저장된 쿼리 텍스트는 핸들러를 다시 로드하거나 호출할 때마다 서버에서 파서 깊이와 역추적 횟수 제한 없이 다시 구문 분석됩니다. 따라서 `max_parser_depth` / `max_parser_backtracks`를 높인 세션에서 생성된 핸들러도 일반적인 세션 제한에서 계속 로드하고 호출할 수 있습니다.

<div id="priority">
  ## 우선순위
</div>

서버 구성에 정의된 핸들러가 SQL로 정의된 핸들러보다 우선합니다. SQL로 정의된 핸들러는 이름의 사전식 순서대로 일치합니다.

<div id="parameters">
  ## 매개변수
</div>

매개변수화된 쿼리의 쿼리 매개변수는 구성으로 정의된 handler와 마찬가지로 다음에서 제공됩니다.

* 쿼리 문자열의 HTTP URL 매개변수에서 `param_<name>` 규칙을 사용합니다(예: `?param_id=42`는 `{id:Type}`에 바인딩됩니다).
* `URL REGEXP`의 이름 지정 캡처 그룹을 사용합니다(예: `URL REGEXP '/users/(?P<id>\d+)'`는 `{id:Type}`에 바인딩됩니다).
* 쿼리에서 매개변수를 선언하는 handler의 요청 본문 양식 필드를 사용합니다. `application/x-www-form-urlencoded` 본문(예: `curl -d 'param_id=42'`) 및 `multipart/form-data` 본문의 필드는 본문을 포함하는 메서드 `POST`, `PUT`, `DELETE`에서 URL 매개변수와 동일한 방식으로 `{name:Type}` 매개변수에 바인딩됩니다. URL과 본문 모두에 있는 매개변수는 URL의 값을 사용합니다. 양식으로 파싱된 본문은 handler 계층에서 소비되므로 `INSERT` 데이터로 쿼리에 전달되지 않습니다. 본문을 `_request_body`로만 사용하는 handler는 양식 파싱 대신 원시 본문을 받습니다. 다른 매개변수와 함께 `_request_body`를 선언하는 handler는 둘 다 받습니다. 즉, 본문이 양식으로 파싱되기 전에 원시 본문의 복사본이 `_request_body`에 보존됩니다(`http_max_request_param_data_size`의 제한을 따름).

handler를 호출할 때 표준 ClickHouse HTTP 헤더(예: `X-ClickHouse-Database`, `X-ClickHouse-User`, `X-ClickHouse-Key`)는 일반적인 방식으로 적용됩니다.

함수 [`currentHandler`](/docs/ko/reference/functions/regular-functions/other-functions#currentHandler) 및 [`currentRequestURL`](/docs/ko/reference/functions/regular-functions/other-functions#currentRequestURL)을 사용하면 호출된 handler와 요청 URL에 따라 쿼리 동작을 사용자 정의할 수 있습니다.

<div id="access-control">
  ## 액세스 제어
</div>

`CREATE HANDLER`, `DROP HANDLER`, `ALTER HANDLER`를 사용하려면 각각 `CREATE HANDLER`, `DROP HANDLER`, `ALTER HANDLER` 권한이 필요합니다.

[`system.handlers`](/docs/ko/reference/system-tables/handlers) 테이블을 읽으려면 `SHOW HANDLERS` 권한이 필요합니다. 핸들러 쿼리에 포함될 수 있는 시크릿은 사용자가 시크릿을 볼 수 있는 추가 권한을 갖고 있지 않으면 해당 테이블에서 마스킹됩니다([`system.handlers`](/docs/ko/reference/system-tables/handlers) 참조).

핸들러를 호출하는 데는 별도의 권한이 필요하지 않지만, 쿼리를 호출할 때는 일반적인 방식으로 권한을 확인하며 인증도 정상적으로 적용됩니다. 특정 쿼리에 대한 액세스를 캡슐화하려면 [`SQL SECURITY DEFINER`가 포함된 `VIEW`](/docs/ko/reference/statements/create/view#sql_security)를 생성하고 해당 뷰를 SELECT하는 핸들러를 정의하십시오.

<div id="storage">
  ## 스토리지
</div>

핸들러는 [이름이 지정된 컬렉션](/docs/ko/concepts/features/configuration/server-config/named-collections)과 마찬가지로 설정 파일의 `query_rules_storage` 섹션에서 구성하는 로컬 또는 Keeper 스토리지에 저장됩니다:

```xml theme={null}
<query_rules_storage>
    <type>local</type> <!-- or zookeeper -->
    <path>/var/lib/clickhouse/handlers/</path>
</query_rules_storage>
```

Keeper Storage를 사용하면 handler가 모든 레플리카에서 자동으로 동기화됩니다. 따라서 명시적인 `ON CLUSTER` 절은 불필요하며, 지정하면 모든 레플리카가 동일한 handler를 생성하려고 시도합니다. Storage가 복제된 경우 `CREATE`, `ALTER`, `DROP HANDLER`에서 `ON CLUSTER`를 무시하도록 `ignore_on_cluster_for_replicated_handler_queries` 설정을 활성화하십시오. 이는 `ignore_on_cluster_for_replicated_named_collections_queries`와 동일한 방식입니다.

<div id="alter-handler">
  ## ALTER HANDLER
</div>

```sql theme={null}
ALTER HANDLER name
[PROTOCOL protocol_name|ANY]
[URL [PREFIX|REGEXP] '/path']
[METHODS (GET, POST)]
[TYPE query]
[AS SELECT ...]
```

handler를 새 handler로 교체합니다. `ALTER` 쿼리에는 일부 절만 포함할 수 있으므로, 예를 들어 URL 또는 쿼리만 변경할 수 있습니다. 지정하지 않은 절은 기존 값을 유지합니다. `PROTOCOL ANY`는 기존 protocol 제한을 제거하여 handler가 다시 모든 HTTP endpoint에서 활성화되도록 합니다.

<div id="drop-handler">
  ## DROP HANDLER
</div>

```sql theme={null}
DROP HANDLER [IF EXISTS] name
```

지정한 이름의 handler를 삭제합니다.

<div id="introspection">
  ## 내부 검사
</div>

[`system.handlers`](/docs/ko/reference/system-tables/handlers) 테이블에는 SQL로 정의된 모든 handler가 나열됩니다. [`system.query_log`](/docs/ko/reference/system-tables/query_log) 테이블은 각 쿼리의 handler 이름과 HTTP 요청 경로(쿼리 문자열 제외)를 `http_handler_name` 및 `http_request_url` 컬럼에 기록합니다.

<div id="example">
  ## 예시
</div>

```sql theme={null}
CREATE HANDLER my_handler URL '/my_handler' AS SELECT version();
```

```bash theme={null}
$ curl 'http://localhost:8123/my_handler'
```

정규식 URL을 사용하는 매개변수화된 handler:

```sql theme={null}
CREATE HANDLER get_user URL REGEXP '/users/(?P<id>\d+)' AS SELECT * FROM users WHERE id = {id:UInt64};
```

```bash theme={null}
$ curl 'http://localhost:8123/users/42'
```

<div id="related-statements">
  ## 관련 SQL 문
</div>

`CREATE HANDLER`는 `CREATE` SQL 문 계열에 속하며, `ALTER` 및 `DROP`과 관련이 있습니다.
