- GraphQL 및 REST API
- 프론트엔드
- 인스턴스 변수
- 엔터티
- 문서
- 메소드 및 매개변수 설명
- 호환성 파괴적인 변경
- 파괴적 변경이란
- 파괴적 변경이 아닌 것은
- 실험적, 베타, 그리고 일반적으로 사용 가능한 기능
- 선언된 매개변수
- 배열 유형
- HTTP 상태 도우미 사용하기
- HTTP 동사 선택하기
- GitLab Rails 코드베이스에서 API 경로 도우미 사용하기
- 사용자 정의 유효성 검사기
- 내부 API
- N+1 문제 회피
- 테스트
- 변경 로그 항목 포함
API 스타일 가이드
이 스타일 가이드는 API 개발을 위한 모범 사례를 권장합니다.
GraphQL 및 REST API
우리는 고객에게 두 가지 유형의 API를 제공합니다:
병렬로 두 개의 API를 지원하는 기술적 부담을 줄이기 위해, 그들은 가능한 한 많이 구현을 공유해야 합니다. 예를 들어, 동일한 services를 공유할 수 있습니다.
프론트엔드
프론트엔드에서 개발할 때 사용할 API에 대한 자세한 정보는 프론트엔드 가이드를 참조하십시오.
인스턴스 변수
인스턴스 변수를 사용하지 마십시오. Rails 뷰에서 하는 것처럼 액세스할 필요가 없으므로 로컬 변수를 사용하세요.
엔터티
엔드포인트의 페이로드를 제시하는 데 항상 Entity를 사용하세요.
문서
새로운 또는 업데이트된 API 엔드포인트는 내부적이거나 기능 플래그 뒤에 숨겨진 경우를 제외하고 문서와 함께 제공되어야 합니다. 문서는 동일한 머지 리퀘스트에 있어야 하며, 엄격하게 필요한 경우에는 원래의 머지 리퀘스트와 동일한 마일스톤으로 이후에 제공되어야 합니다.
Markdown과 OpenAPI 정의 파일에서 API 리소스를 문서화하는 세부 정보는 문서 스타일 가이드 RESTful API 페이지를 참조하십시오.
메소드 및 매개변수 설명
모든 메소드는 Grape DSL을 사용하여 설명되어야 합니다.
(좋은 예는 environments.rb
를 참조하십시오):
- 메소드 요약에 대한
desc
. 추가 정보를 위해 블록을 전달해야 합니다. 예를 들어:- 엔드포인트가 추가된 GitLab 버전. 기능 플래그 뒤에 숨겨진 경우 대신 언급하세요: 이 기능은 :feature_flag_symbol 기능 플래그에 의해 제한됩니다.
- 엔드포인트가 폐기될 경우 해당 폐기 예정일
- 메소드 매개변수에 대한
params
. 매개변수의 설명, 유효성 검사 및 매개변수형 변환으로 작용합니다.
좋은 예는 다음과 같습니다:
desc '모든 방송 메시지 가져오기' do
detail '이 기능은 GitLab 8.12에서 추가되었습니다.'
success Entities::System::BroadcastMessage
end
params do
optional :page, type: Integer, desc: '현재 페이지 번호'
optional :per_page, type: Integer, desc: '페이지 당 메시지 수'
end
get do
messages = System::BroadcastMessage.all
present paginate(messages), with: Entities::System::BroadcastMessage
end
호환성 파괴적인 변경
우리는 GitLab 주요 릴리스에서 신뢰할 수 있는 REST API v4에 대한 호환성 파괴적인 변경을 가해서는 안 됩니다.
우리의 REST API는 GitLab 버전과 독립적으로 버전을 유지합니다.
현재 REST API 버전은 4
입니다. REST API에 대한 의미 있는 버전 설정을 따르기로 했습니다,
즉 주요 버전 변경(아마도 5
가 가장 가능성이 높음)이 발생할 때까지 파괴적인 변경을 가할 수 없습니다.
버전 5
가 예정되지 않았기 때문에 드문 예외을 허용합니다.
호환성 파괴적인 변경 대신의 백워드 호환성 수용
변경된 기능을 이전 API 스키마에 계속적으로 적응하여 대부분의 경우에 백워드 호환성을 유지할 수 있습니다.
예를 들어, 우리의 REST API는 work_in_progress
와 draft
필드를 모두 노출합니다.
예외
예외 사항은 다음과 같습니다:
- 특정 기능을 주요 GitLab 릴리스에서 제거해야 할 때
- 백워드 호환성을 유지할 수 없을 때 어떤 형태에서도.
- 기능이 이전에 실험적 또는 베타로 지정되었을 때.
이 예외 사항은 드문 경우여야 합니다.
이 예외 사항에서 실현된 기능을 제거하는 대신 언제나 다음과 같이 수행해야 합니다:
- 필드나 인수에 대한 빈 응답 반환(예:
"null"
또는[]
). - 인수를 번역하지 마십시오.
파괴적 변경이란
파괴적 변경의 몇 가지 예는 다음과 같습니다:
- 필드, 매개변수 또는 열거형 값의 제거 또는 이름 변경. JSON 응답에서 필드는 어떤 JSON 키든지 해당합니다.
- 엔드포인트의 제거.
- 새로운 리디렉션 추가(모든 클라이언트가 리디렉션을 따르는 것은 아님).
- 어떤 응답의 콘텐츠 유형 변경.
- 응답에서 필드 유형 변경. JSON 응답에서 이것은
Number
,String
,Boolean
,Array
, 또는Object
유형의 어떤 변경이든 해당합니다. - 새로운 필수 인수 추가.
- 인증, 권한 또는 다른 헤더 요구 사항 변경.
-
500
이외의 어떤 상태 코드 변경.
파괴적 변경이 아닌 것은
파괴적 변경이 아닌 변경의 몇 가지 예는 다음과 같습니다:
- 엔드포인트 추가, 비필수 인수, 필드 또는 열거형 값과 같은 추가적인 변경.
- 오류 메시지 변경.
-
500
상태 코드에서 지원하는 상태 코드로의 변경(이것은 버그 수정입니다). - 응답에서 반환된 필드의 순서 변경.
실험적, 베타, 그리고 일반적으로 사용 가능한 기능
API 엘리먼트를 실험적 또는 베타 기능으로 추가할 수 있습니다. 이것은 추가적인 변경이어야 하며 그렇지 않은 경우, 파괴적인 변경으로 분류됩니다.
실험 또는 베타로 표시된 API 엘리먼트는 백워드 호환성을 보장하지 않아도 되며 언제든지 사전 통보 없이 변경하거나 제거할 수 있습니다.
실험 상태에 있는 동안:
- 기본적으로 끔 상태인 피쳐 플래그를 사용하세요.
- 플래그가 끔 상태일 때:
- 추가된 엔드포인트는
404 Not Found
를 반환해야 합니다. - 추가된 인수는 무시되어야 합니다.
- 추가된 필드는 노출되어서는 안 됩니다.
- 추가된 엔드포인트는
- API 문서는 실험 상태를 문서로하고, 피쳐 플래그는 문서로 기록되어야 합니다.
- OpenAPI 문서는 변경을 기술하면 안 됩니다(예를 들어, 숨김 옵션 사용을 통해하는 것).
베타 상태에 있는 동안:
- 기본적으로 켠 상태인 피쳐 플래그를 사용하세요.
- API 문서는 베타 상태를 문서로하고, 피쳐 플래그는 문서로 기록되어야 합니다.
- OpenAPI 문서는 변경을 기술하면 안 됩니다.
기능이 일반적으로 사용 가능해질 때:
- 피쳐 플래그를 제거하세요.
- API 문서에서 실험 또는 베타 상태를 제거하고, 변경 사항을 프로그래밍 방식으로 발견할 수 있도록 OpenAPI 문서를 추가하세요.
선언된 매개변수
Grape는 params
블록에서 선언된 매개변수에만 접근할 수 있도록 합니다. 전달된 매개변수 중에서 허용되지 않은 매개변수는 필터링합니다.
– https://github.com/ruby-grape/grape#declared
상위 네임스페이스에서 매개변수 제외하기
기본적으로 declared(params)
는 모든 상위 네임스페이스에서 정의된 매개변수를 포함합니다.
– https://github.com/ruby-grape/grape#include-parent-namespaces
대부분의 경우에는 상위 네임스페이스에서 매개변수를 제외해야 합니다:
declared(params, include_parent_namespaces: false)
declared(params)
를 사용해야 하는 경우
매개변수 해시를 메소드 호출의 인수로 전달할 때 항상 declared(params)
를 사용해야 합니다.
예를 들어:
# 나쁨
User.create(params) # 사용자가 `admin=1`을 제출했다고 상상해보세요. :)
# 좋음
User.create(declared(params, include_parent_namespaces: false).to_h)
참고:
declared(params)
는 Hashie::Mash
객체를 반환하며, 여기에 .to_h
를 호출해야 합니다.
그러나 하나의 요소에 직접적으로 접근할 때에는 params[key]
를 사용할 수 있습니다.
예를 들어:
# 좋음
Model.create(foo: params[:foo])
배열 유형
Grape v1.3+에서는 배열 유형은 coerce_with
블록이나 매개변수로 반드시 정의되어야 하며, API 요청에서 문자열이 전달될 때 유효성 검사에 실패합니다. 자세한 내용은 Grape 업그레이드 문서를 참조하십시오.
nil
입력의 자동 강제 형변환
Grape v1.3.3 이전에는 nil
값을 가진 배열 매개변수는 자동으로 빈 배열로 강제 형변환되었습니다. 그러나 v1.3.3에서의 이 PR로 인해 더는 해당하지 않습니다. 예를 들어, 선택적 매개변수를 가진 PUT /test
요청을 정의했다고 가정해봅시다.
일반적으로 PUT /test?user_ids
로의 요청은 Grape에서 params
를 { user_ids: nil }
로 전달합니다.
이는 빈 배열을 예상하는 엔드포인트에서 오류를 발생시킬 수 있으며, nil
입력을 제대로 처리하지 못할 수 있습니다. 이전 동작을 유지하기 위해 모든 API 호출의 before
블록에서 사용되는 coerce_nil_params_to_array!
라는 도우미 메소드가 있습니다.
이 변경으로 PUT /test?user_ids
요청은 Grape에서 params
가 { user_ids: [] }
로 전달됩니다.
이에 대한 더 간단한 사용을 위한 Grape 트래커에서의 오픈 이슈가 있습니다.
HTTP 상태 도우미 사용하기
200 이외의 HTTP 응답에 대해서는 lib/api/helpers.rb
에서 제공되는 도우미를 사용하여 올바른 동작(예: not_found!
또는 no_content!
)을 보장해야 합니다. 이러한 도우미는 Grape 내에서 throw
를 호출하고 엔드포인트를 실행을 중단합니다.
DELETE
요청의 경우 일반적으로 destroy_conditionally!
도우미를 사용해야 하며, 이는 기본적으로 성공 시 204 No Content
응답을 반환하거나 주어진 If-Unmodified-Since
헤더가 범위를 벗어나면 412 Precondition Failed
응답을 반환합니다. 이 도우미는 전달된 리소스에서 #destroy
를 호출하지만, 블록을 전달하여 사용자 지정 삭제 메소드를 구현할 수도 있습니다.
HTTP 동사 선택하기
새로운 API 라우트를 정의할 때에는 올바른 HTTP 요청 방법을 사용해야 합니다.
PATCH
와 PUT
중에서 선택하기
Rails 애플리케이션에서 PATCH
와 PUT
요청 방법은 모두 컨트롤러의 update
메소드로 라우팅됩니다. Grape에서는 업데이트를 수행하는 엔드포인트에 대해 명시적으로 PATCH
또는 PUT
HTTP 동사를 설정해야 합니다.
특정 리소스의 모든 속성을 업데이트하는 경우, PUT
요청 방법을 사용해야 합니다. 특정 리소스의 일부 속성을 업데이트하는 경우, PATCH
요청 방법을 사용해야 합니다.
여기 PATCH
에 좋은 예시가 있습니다: PATCH /projects/:id/protected_branches/:name
여기 PUT
에 좋은 예시가 있습니다: PUT /projects/:id/merge_requests/:merge_request_iid/approve
좋은 PUT
엔드포인트는 일반적으로 ID와 동사만 가지고 있거나, 단일 값을 가지고 있어서 키/값 쌍을 나타내야 합니다.
Rails 블로그에는 웹 API 엔드포인트에서 PATCH
가 일반적으로 가장 적합한 동사인 이유에 대해 자세히 설명되어 있습니다.
GitLab Rails 코드베이스에서 API 경로 도우미 사용하기
Graped에 의해 생성된 API 경로 도우미를 사용할 때 상대적인 URL 아래에 GitLab을 설치하기 때문에 이를 고려해야 합니다. 이러한 API 경로 도우미 사용은 모두 expose_path
도우미 호출 내에 포장되어야 합니다.
예를 들어:
- endpoint = expose_path(api_v4_projects_issues_related_merge_requests_path(id: @project.id, issue_iid: @issue.iid))
사용자 정의 유효성 검사기
API 요청에서 특정 매개변수를 유효성 검사하기 위해서는 해당 매개변수를 전달하기 전에 (예: Gitaly에) 검증해야 합니다. 지금까지 추가한 사용자 정의 유효성 검사기와 그들을 사용하는 방법에 대해 기술하고 있습니다. 또한 새로운 사용자 정의 유효성 검사기를 추가하는 방법에 대한 가이드도 작성했습니다.
사용자 정의 유효성 검사기 사용하기
-
FilePath
:GitLab은 파일 경로를 횡단해야 하는 다양한 기능을 지원합니다.
FilePath
유효성 검사기 는 매개변수 값에 대해 다양한 경우를 유효성 검사합니다. 주로 상대적인 경로인지,../../
상대적인 횡단을 포함하는지,File::Separator
를 사용하여 절대 경로인지 여부를 확인합니다. 예를 들어, 절대 경로인 경우/etc/passwd/
가 포함됩니다. 기본적으로 절대 경로는 허용되지 않습니다. 그러나 필요한 경우 허용되는 절대 경로에 대한 목록을 옵션으로 전달할 수 있습니다. 다음과 같이 허용되는 절대 경로에 대한 목록을 전달할 수 있습니다:requires: :file_path, type: String, file_path: { allowlist: ['/foo/bar/', '/home/foo/', '/app/home'] }
-
Git SHA
:Git SHA
유효성 검사기 는 Git SHA 매개변수가 유효한 SHA인지 확인합니다. 이는commit.rb
파일에 언급된 정규식을 사용하여 확인합니다. -
부재
:부재
유효성 검사기 는 지정된 매개변수 해시에서 특정 매개변수가 없는지 확인합니다. -
IntegerNoneAny
:IntegerNoneAny
유효성 검사기 는 주어진 매개변수의 값이정수
,None
, 또는Any
인지 확인합니다. 요청에서는 이러한 값 중 하나만 허용됩니다. -
ArrayNoneAny
:ArrayNoneAny
유효성 검사기 는 주어진 매개변수의 값이Array
,None
, 또는Any
인지 확인합니다. 요청에서는 이러한 값 중 하나만 허용됩니다. -
EmailOrEmailList
:EmailOrEmailList
유효성 검사기 는 문자열 또는 유효한 이메일 주소만 포함하는 문자열 목록의 값인지 확인합니다. 요청에서는 유효한 이메일 주소로만 구성된 목록만 허용됩니다.
새로운 사용자 정의 유효성 검사기 추가
사용자 정의 유효성 검사기는 플랫폼으로 전송하기 전에 매개변수를 유효성 검사하는 훌륭한 방법입니다. 시작할 때부터 잘못된 매개변수를 식별하면 서버와 플랫폼 간의 번갈아가며 하는 일을 줄일 수 있습니다.
사용자 정의 유효성 검사기를 추가해야 하는 경우 해당 유효성 검사기를 validators
디렉토리의 별도 파일에 추가해야 합니다.
API 추가에 Grape를 사용하기 때문에 유효성 검사기 클래스에서 Grape::Validations::Validators::Base
클래스를 상속합니다.
지금 해야 할 일은 두 개의 매개변수, 즉 params
해시와 유효성을 검사할 param
이름을 가져가는 validate_param!
메서드를 정의하는 것뿐입니다.
메서드 내용은 매개변수 값의 유효성을 검사하고 적절한 오류 메시지를 호출자 메서드에 반환합니다.
마지막으로 다음과 같이 유효성 검사기를 등록합니다:
Grape::Validations.register_validator(<validator name as symbol>, ::API::Helpers::CustomValidators::<YourCustomValidatorClassName>)
유효성 검사기를 추가한 후 해당 유효성 검사기에 대한 rspec
를 검사기의 별도 파일에 추가하는 것을 잊지 마십시오. 이 파일은 validators
디렉토리에 있어야 합니다.
내부 API
내부 API는 내부 사용을 위해 문서화되어 있습니다. 어떤 엔드포인트가 다른 컴포넌트에서 사용되고 있는지 알 수 있도록 최신 상태를 유지하세요.
N+1 문제 회피
API 엔드포인트에서 레코드 모음을 반환할 때 흔히 발생하는 N+1 문제를 피하기 위해 이전 로딩을 사용해야 합니다.
API 내에서 이를 수행하는 표준 방법은 모델이 API에서 반환된 연결 및 데이터를 사전로드하는 with_api_entity_associations
라는 스코프를 구현하는 것입니다. 이러한 스코프의 예는
Issue
모델에서 확인할 수 있습니다.
동일한 모델이 API에서 여러 개체를 가지는 상황(예: UserBasic
, User
, UserPublic
)에서 이 스코프를 적용할지에 대해서는 재량으로 결정해야 합니다. 연속적인 엔티티가 해당 스코프에 따라 최적화되었을 수 있습니다.
with_api_entity_associations
스코프는
데이터를 자동으로 사전로딩
하여 할 일 API에서 반환될 때 할 일
대상을 위해 실행됩니다.
사전로드에 대한 자세한 내용 및 토론은 이 병합 요청에서 확인할 수 있습니다.
테스트로 검증
API 엔드포인트가 컬렉션을 반환할 때 항상 N+1 문제가 발생하지 않는지 현재와 미래에 대해 확인하기 위해 테스트를 추가하세요.
ActiveRecord::QueryRecorder
를 사용하여 이 작업을 수행할 수 있습니다.
예시:
def make_api_request
get api('/foo', personal_access_token: pat)
end
it 'N+1 쿼리를 회피합니다', :request_store do
# 먼저, API가 단일 레코드를 반환할 때 PostgreSQL 쿼리를 얼마나 실행하는지 기록합니다
control = ActiveRecord::QueryRecorder.new { make_api_request }
# 이제 두 번째 레코드를 만들고, API가 이전보다 더 많은 쿼리를 실행하지 않도록 확인합니다
expect { make_api_request }.not_to exceed_query_limit(control)
end
테스트
새 API 엔드포인트에 대한 테스트를 작성할 때, /spec/fixtures/api/schemas
에 위치한 스키마 픽스처를 사용하는 것을 고려해보세요. 주어진 스키마와 일치하는 응답을 expect
할 수 있습니다:
expect(response).to match_response_schema('merge_requests')
또한 테스트에서 테스트로 확인하기도 바랍니다.
변경 로그 항목 포함
모든 고객에게 노출되는 변경 사항은 반드시 변경 로그 항목을 포함해야 합니다. 내부 API는 이에 포함되지 않습니다.