Technote

개발 워크플로우 실무

연재 REST API 와 헤드리스 8부 중 3부

자체 엔드포인트 — 네임스페이스 · 권한 콜백 · 스키마

permission_callback 은 선택 인자가 아닙니다. 빠뜨리면 라우트가 공개되고 코어가 경고를 남깁니다. 그리고 args 스키마를 주면 검증 코드를 핸들러에 손으로 쓰지 않아도 됩니다.

코어 라우트로 안 되는 일이 생깁니다. 여러 리소스를 합쳐 한 번에 주고 싶거나, 우리 도메인의 동사(주문 취소 · 진단 시작)를 표현해야 할 때입니다. 그때 register_rest_route() 를 씁니다. 등록은 반드시 rest_api_init 안에서 합니다 — 그보다 이르면 REST 서버가 아직 없습니다.

네임스페이스는 버전을 포함합니다

네임스페이스는 벤더/버전 형태로 씁니다. wper/v1 처럼요. 버전이 붙어 있어야 응답 형태를 바꿔야 하는 날 v2 를 나란히 열고 기존 소비자를 깨뜨리지 않은 채 옮길 수 있습니다. 버전 없는 네임스페이스는 첫 변경에서 반드시 후회합니다.

벤더 부분은 프로젝트마다 고유해야 합니다. 두 플러그인이 같은 네임스페이스에 라우트를 등록하면 나중에 등록한 쪽이 조용히 무시됩니다.

add_action( 'rest_api_init', 'wper_register_routes' );

function wper_register_routes() {
	register_rest_route(
		'wper/v1',
		'/orders/(?P<id>d+)',
		[
			'methods'             => WP_REST_Server::READABLE,
			'callback'            => 'wper_get_order',

			// 필수 인자다. 빠뜨리면 라우트는 공개되고 코어가 경고를 남긴다.
			'permission_callback' => function () {
				return current_user_can( 'edit_posts' );
			},

			'args'                => [
				'id'     => [
					'type'              => 'integer',
					'required'          => true,
					'sanitize_callback' => 'absint',
				],
				'detail' => [
					'type'    => 'string',
					'enum'    => [ 'summary', 'full' ],
					'default' => 'summary',
				],
			],
		]
	);
}

permission_callback 은 선택 인자가 아닙니다

워드프레스 5.5 부터 permission_callback 이 없는 라우트는 _doing_it_wrong() 경고를 냅니다. 경고의 문구가 친절해서 오해를 낳는데, 경고가 났다고 라우트가 막히는 것은 아닙니다. 그 라우트는 그대로 공개 상태로 돕니다. 디버그가 꺼진 프로덕션에서는 경고조차 보이지 않습니다.

그래서 규약은 이렇습니다 — 공개 라우트를 만들 생각이면 '__return_true'명시적으로 씁니다. 코드를 읽는 사람이 “빠뜨린 것” 과 “일부러 연 것” 을 구별할 수 있게 하기 위해서입니다. 웹훅 수신 라우트가 바로 그 경우이고, 5회차에서 따로 다룹니다.

콜백은 true · false · WP_Error 를 돌려줄 수 있습니다. false 를 돌려주면 코어가 상태 코드를 알아서 고릅니다 — 로그인하지 않았으면 401, 로그인했는데 권한이 모자라면 403 입니다. 직접 고르려면 WP_Error 를 돌려줍니다.

검증은 스키마가 하고, 권한 콜백보다 먼저 돕니다

args 에 타입 · 필수 여부 · enum · default · sanitize_callback 을 적으면 코어가 요청을 핸들러에 닿기 전에 검증하고 정제합니다. 핸들러 안에서 if ( ! isset( … ) ) 를 늘어놓지 않아도 되고, 실패 응답의 형태도 코어 라우트와 같아집니다.

디스패치 순서 — 인자 검증이 권한 콜백보다 먼저 돈다

순서를 알아 두면 쓸모가 있습니다. 인자 검증이 먼저이므로 권한 콜백은 이미 정제된 요청을 받습니다 — 콜백 안에서 $request['id'] 를 그대로 믿고 소유권을 조회해도 됩니다. 반대로 잘못된 인자를 보내면 권한을 보기 전에 400 이 나가므로, “권한이 없는데 왜 400 이지” 같은 혼동이 생길 수 있습니다.

핸들러는 WP_REST_Response 또는 WP_Error 를 돌려줍니다. 배열을 돌려줘도 되지만 rest_ensure_response() 를 거치면 나중에 헤더를 붙일 자리가 생깁니다.

function wper_get_order( WP_REST_Request $request ) {
	$order = get_post( $request['id'] );

	if ( ! $order || 'wper_order' !== $order->post_type ) {
		return new WP_Error( 'wper_order_not_found', '주문을 찾을 수 없습니다.', [ 'status' => 404 ] );
	}

	return rest_ensure_response( [
		'id'     => $order->ID,
		'status' => $order->post_status,
	] );
}

이런 계층을 다른 사람의 사이트 위에 얹을 때 우리가 밟는 절차는 작업 과정에 그대로 적어 두었고, 설계 전반의 배경은 개발 워크플로우 아카이브에 있습니다.

다음 회차

라우트를 만들었으면 “누가 부르는가” 를 정해야 합니다. 다음 회차는 인증 — 쿠키와 nonce, 애플리케이션 비밀번호, 토큰이 각각 어떤 상황을 위한 것이고 위협 모델이 어떻게 다른지입니다.

이 주제의 다른 글

노하우 목록으로

개발 워크플로우 실무

워드프레스를 GitHub 에 올릴 때의 .gitignore

워드프레스 저장소에서 커밋해야 할 것과 절대 커밋하면 안 되는 것의 경계는 명확합니다. wp-config.php 한 파일이 저장소에 들어가는 순간 DB 비밀번호와 인증 솔트가 통째로 유출됩니다.

개발자 · 운영 · 관리자 5분 읽기

개발 워크플로우 실무

취향 논쟁을 규칙 확인으로 바꾸는 디자인 리뷰

"좀 답답해 보인다" 는 반박할 수 없고 고칠 수도 없습니다. 간격 척도 밖의 값, 팔레트 밖의 색, 기준 미달 대비, 빠진 상태 — 네 항목만 규칙으로 바꿔도 리뷰의…

디자이너 4분 읽기

₩270,000 · 신청하기