표준 메서드

이 장에서는 표준 메서드인 List, Get, Create, Update, Delete 개념을 정의합니다. 표준 메서드는 복잡성을 줄일 뿐만 아니라 일관성을 높이는 효과가 있습니다. Google API 저장소에서 제공되는 API 메서드의 70% 이상이 표준 메서드이므로 더욱 쉽게 학습하고 사용할 수 있습니다.

다음 표는 표준 메서드를 HTTP 메서드에 매핑하는 방법에 대해 설명한 것입니다.

표준 메서드 HTTP 매핑 HTTP 요청 본문 HTTP 응답 본문
List GET <collection URL> 해당 사항 없음 리소스* 목록
Get GET <resource URL> 해당 사항 없음 리소스*
Create POST <collection URL> 리소스 리소스*
Update PUT or PATCH <resource URL> 리소스 리소스*
Delete DELETE <resource URL> 해당 사항 없음 google.protobuf.Empty**

*메서드가 반환될 필드 하위 집합을 지정하는 응답 필드 마스크를 지원할 경우 List, Get, Create, Update 메서드에서 반환되는 리소스에 일부 데이터가 포함될 수 있습니다. 경우에 따라 API 플랫폼이 모든 메서드에 대해 필드 마스크를 기본적으로 지원하기도 합니다.

**리소스를 즉시 삭제하지 않는 Delete 메서드에서 반환되는 응답(예: 플래그 업데이트 또는 장기 실행 삭제 작업 만들기)에는 장기 실행 작업 또는 수정된 리소스가 포함되어야 합니다.

또한 단일 API를 호출하는 시간 범위 내에 요청이 완료되지 않으면 표준 메서드가 장기 실행 작업반환할 수도 있습니다.

이어지는 섹션에서는 각 표준 메서드에 대해서 자세하게 설명합니다. 각 예제에는 HTTP 매핑에 필요한 주석과 함께 .proto 파일에서 정의하는 메서드가 나와있습니다. 표준 메서드를 사용하는 예제는 Google API 저장소에서 많이 찾아볼 수 있습니다.

목록

List 메서드는 컬렉션 이름과 매개변수 0개 이상을 입력으로 사용하고 입력과 일치하는 리소스 목록을 반환합니다.

List는 일반적으로 리소스를 검색하는 데 사용됩니다. List는 단일 컬렉션에서 크기가 제한적이고 캐시되지 않는 데이터에 적합합니다. 더욱 광범위한 경우에는 커스텀 메서드 Search사용해야 합니다.

일괄 가져오기(예: 여러 리소스 ID를 입력한 후 각 ID에 해당하는 객체를 반환하는 메서드)는 List가 아닌 커스텀 BatchGet 메서드로 구현되어야 합니다. 하지만 동일한 기능을 제공하는 기존 List 메서드가 이미 있으면 대신 이를 위해 List 메서드를 재사용할 수 있습니다. 커스텀 BatchGet 메서드를 사용할 경우 이 메서드는 HTTP GET매핑되어야 합니다.

적용 가능한 공통 패턴: 페이지로 나누기, 결과 정렬

적용 가능한 이름 지정 규칙: 필터 필드, 결과 필드

HTTP 매핑:

  • List 메서드는 HTTP GET 동사를 사용해야 합니다.
  • 리소스가 나열되는 컬렉션 이름을 수신하는 요청 메시지 필드는 URL 경로로 매핑되어야 합니다. 컬렉션 이름이 URL 경로에 매핑되면 URL 템플릿의 마지막 세그먼트(컬렉션 ID)는 리터럴이어야 합니다.
  • 그 밖에 모든 요청 메시지 필드는 URL 쿼리 매개변수로 매핑되어야 합니다.
  • 요청 본문이 없으므로 API 구성에서 body 절을 선언해서는 안 됩니다.
  • 응답 본문에는 선택 사항인 메타데이터와 함께 리소스 목록이 포함되어야 합니다.

예를 들면 다음과 같습니다.

// Lists books in a shelf.
rpc ListBooks(ListBooksRequest) returns (ListBooksResponse) {
  // List method maps to HTTP GET.
  option (google.api.http) = {
    // The `parent` captures the parent resource name, such as "shelves/shelf1".
    get: "/v1/{parent=shelves/*}/books"
  };
}

message ListBooksRequest {
  // The parent resource name, for example, "shelves/shelf1".
  string parent = 1;

  // The maximum number of items to return.
  int32 page_size = 2;

  // The next_page_token value returned from a previous List request, if any.
  string page_token = 3;
}

message ListBooksResponse {
  // The field name should match the noun "books" in the method name.  There
  // will be a maximum number of items returned based on the page_size field
  // in the request.
  repeated Book books = 1;

  // Token to retrieve the next page of results, or empty if there are no
  // more results in the list.
  string next_page_token = 2;
}

Get

Get 메서드는 리소스 이름과 매개변수 0개 이상을 사용하고 지정된 리소스를 반환합니다.

HTTP 매핑:

  • Get 메서드는 HTTP GET 동사를 사용해야 합니다.
  • 리소스 이름을 수신하는 요청 메시지 필드는 URL 경로로 매핑되어야 합니다.
  • 그 밖에 모든 요청 메시지 필드는 URL 쿼리 매개변수로 매핑되어야 합니다.
  • 요청 본문이 없으므로 API 구성에서 body 절을 선언해서는 안 됩니다.
  • 반환되는 리소스는 전체 응답 본문으로 매핑되어야 합니다.

예를 들면 다음과 같습니다.

// Gets a book.
rpc GetBook(GetBookRequest) returns (Book) {
  // Get maps to HTTP GET. Resource name is mapped to the URL. No body.
  option (google.api.http) = {
    // Note the URL template variable which captures the multi-segment resource
    // name of the requested book, such as "shelves/shelf1/books/book2"
    get: "/v1/{name=shelves/*/books/*}"
  };
}

message GetBookRequest {
  // The field will contain name of the resource requested, for example:
  // "shelves/shelf1/books/book2"
  string name = 1;
}

Create

Create 메서드는 상위 리소스 이름, 리소스, 매개변수 0개 이상을 사용합니다. 지정된 상위 리소스 아래 새로운 리소스를 만들고 새롭게 만들어진 리소스를 반환합니다.

API에서 리소스 생성을 지원할 경우 만들 수 있는 리소스 유형마다 Create 메서드가 있어야 합니다.

HTTP 매핑:

  • Create 메서드는 HTTP POST 동사를 사용해야 합니다.
  • 요청 메시지에는 리소스를 만들 상위 리소스 이름을 지정하는 parent 필드가 있어야 합니다.
  • 리소스가 포함되는 요청 메시지 필드는 HTTP 요청 본문으로 매핑되어야 합니다. google.api.http 주석이 Create 메서드에 사용될 경우 body: "<resource_field>" 형식을 사용해야 합니다.
  • 요청에는 호출자가 클라이언트에서 할당되는 id를 선택하도록 <resource>_id라고 하는 필드가 포함될 수 있습니다. 이 필드는 리소스 안에 있을 수 있습니다.
  • 그 밖에 모든 요청 메시지 필드는 URL 쿼리 매개변수로 매핑되어야 합니다.
  • 반환되는 리소스는 전체 HTTP 응답 본문으로 매핑되어야 합니다.

Create 메서드가 클라이언트에서 할당된 리소스 이름을 지원하고 이 리소스가 이미 있으면 요청이 오류 코드 ALREADY_EXISTS와 함께 실패하거나 다른 서버에서 할당된 리소스 이름을 사용해야 하고 생성된 리소스 이름이 전달된 리소스 이름과 다를 수 있다는 점을 명시해야 합니다.

리소스 스키마가 변경될 때 요청 스키마와 리소스 스키마 모두 업데이트할 필요가 없도록 Create 메서드는 입력 리소스를 취해야 합니다. 클라이언트에서 설정할 수 없는 리소스 필드의 경우에는 '출력 전용' 필드로 명시되어야 합니다.

예를 들면 다음과 같습니다.

// Creates a book in a shelf.
rpc CreateBook(CreateBookRequest) returns (Book) {
  // Create maps to HTTP POST. URL path as the collection name.
  // HTTP request body contains the resource.
  option (google.api.http) = {
    // The `parent` captures the parent resource name, such as "shelves/1".
    post: "/v1/{parent=shelves/*}/books"
    body: "book"
  };
}

message CreateBookRequest {
  // The parent resource name where the book is to be created.
  string parent = 1;

  // The book id to use for this book.
  string book_id = 3;

  // The book resource to create.
  // The field name should match the Noun in the method name.
  Book book = 2;
}

rpc CreateShelf(CreateShelfRequest) returns (Shelf) {
  option (google.api.http) = {
    post: "/v1/shelves"
    body: "shelf"
  };
}

message CreateShelfRequest {
  Shelf shelf = 1;
}

업데이트

Update 메서드는 리소스와 매개변수 0개 이상이 포함된 요청 메시지를 사용합니다. 이 메서드가 실행되면 지정한 리소스와 속성을 업데이트하고 업데이트된 리소스를 반환합니다.

리소스의 이름 또는 상위 리소스가 포함된 속성을 제외하고 변경 가능한 리소스 속성은 Update 메서드를 통해 변경 가능해야 합니다. 리소스 이름 변경 또는 이동에 대한 기능은 Update 메서드에서 구현되어서는 안 되고 대신 커스텀 메서드에서 처리되어야 합니다.

HTTP 매핑:

  • 표준 Update 메서드는 리소스 부분 업데이트를 지원해야 하고 update_mask라고 하는 FieldMask 필드와 함께 HTTP 동사 PATCH를 사용합니다. 클라이언트에서 입력으로 제공하는 출력 필드를 무시해야 합니다.
  • 반복되는 필드에 첨부하는 등 고급 패치 시맨틱스가 필요한 Update 메서드는 커스텀 메서드구현되어야 합니다.
  • Update 메서드가 전체 리소스 업데이트만 지원할 경우 HTTP 동사 PUT사용해야 합니다. 하지만 전체 업데이트는 새로운 리소스 필드를 추가할 때 하위 호환성 문제가 발생하므로 사용하지 않는 것이 좋습니다.
  • 리소스 이름을 수신하는 메시지 필드는 URL 경로로 매핑되어야 합니다. 이 필드는 리소스 메시지 자체에 포함될 수도 있습니다.
  • 리소스가 포함되는 요청 메시지 필드는 요청 본문으로 매핑되어야 합니다.
  • 그 밖에 모든 요청 메시지 필드는 URL 쿼리 매개변수로 매핑되어야 합니다.
  • 응답 메시지는 업데이트된 리소스 자체가 되어야 합니다.

API가 클라이언트에서 할당된 리소스 이름을 허용하는 경우 서버는 클라이언트가 존재하지 않는 리소스 이름을 지정하여 새로운 리소스를 만들도록 허용할 수 있습니다. 그렇지 않으면 Update 메서드가 존재하지 않는 리소스 이름으로 인해 실패해야 합니다. 이것이 유일한 오류 조건이라면 오류 코드 NOT_FOUND사용해야 합니다.

리소스를 만들 수 있는 Update 메서드가 포함된 API는 Create 메서드도 제공해야 합니다. Update 메서드가 이를 위한 유일한 방법인 경우 리소스를 만드는 방법이 명확하지 않기 때문입니다.

예를 들면 다음과 같습니다.

// Updates a book.
rpc UpdateBook(UpdateBookRequest) returns (Book) {
  // Update maps to HTTP PATCH. Resource name is mapped to a URL path.
  // Resource is contained in the HTTP request body.
  option (google.api.http) = {
    // Note the URL template variable which captures the resource name of the
    // book to update.
    patch: "/v1/{book.name=shelves/*/books/*}"
    body: "book"
  };
}

message UpdateBookRequest {
  // The book resource which replaces the resource on the server.
  Book book = 1;

  // The update mask applies to the resource. For the `FieldMask` definition,
  // see https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#fieldmask
  FieldMask update_mask = 2;
}

삭제

Delete 메서드는 리소스 이름과 매개변수 0개 이상을 사용하고 지정된 리소스를 삭제하거나 삭제 예약합니다. Delete 메서드는 google.protobuf.Empty반환해야 합니다.

API는 Delete 메서드에서 반환된 정보를 반복적으로 호출할 수 없으므로 사용해서는 안 됩니다.

HTTP 매핑:

  • Delete 메서드는 HTTP DELETE 동사를 사용해야 합니다.
  • 리소스 이름을 수신하는 요청 메시지 필드는 URL 경로로 매핑되어야 합니다.
  • 그 밖에 모든 요청 메시지 필드는 URL 쿼리 매개변수로 매핑되어야 합니다.
  • 요청 본문이 없으므로 API 구성에서 body 절을 선언해서는 안 됩니다.
  • Delete 메서드가 리소스를 즉시 삭제할 경우 빈 응답을 반환해야 합니다.
  • Delete 메서드가 장기 실행 작업을 시작하면 장기 실행 작업을 반환해야 합니다.
  • Delete 메서드가 리소스를 삭제 중으로만 표시할 경우 업데이트된 리소스를 반환해야 합니다.

Delete 메서드 호출은 실제로 멱등성이어야 하지만 동일한 응답을 생성할 필요는 없습니다. Delete 요청 개수와 상관없이 리소스는 최종적으로 삭제되어야 하지만 첫 번째 요청에서만 성공 코드를 반환해야 합니다. 이후 요청은 google.rpc.Code.NOT_FOUND가 되어야 합니다.

예를 들면 다음과 같습니다.

// Deletes a book.
rpc DeleteBook(DeleteBookRequest) returns (google.protobuf.Empty) {
  // Delete maps to HTTP DELETE. Resource name maps to the URL path.
  // There is no request body.
  option (google.api.http) = {
    // Note the URL template variable capturing the multi-segment name of the
    // book resource to be deleted, such as "shelves/shelf1/books/book2"
    delete: "/v1/{name=shelves/*/books/*}"
  };
}

message DeleteBookRequest {
  // The resource name of the book to be deleted, for example:
  // "shelves/shelf1/books/book2"
  string name = 1;
}