LikeGecko

OpenAPI Spec을 이용한 인터페이스 정의 자동화
|

development

OpenAPI Specを活用したインタフェース正義自動化

  • oas
  • openapi
  • frontend

서론

프론트엔드가 서버측과 통신하기 위한 API사용을 쉽게 하기 위해서 서버측에서 다양한 스펙으로 API인터페이스를 제공할 수 있습니다. REST API중에서는 대표적으로 OpenAPI Spec에 맞춰 API를 개발하고, 이를 Swagger나 Postman을 통해서 문서화 하여 공유하게 됩니다. 그렇게 작성된 API문서가 공유되면 해당 API명세를 확인해야 하는 개발자A는 해당 API를 개발한 개발자B에게 직접 엔드포인트와 사용방법등을 요청할 필요가 없습니다. 단지 웹으로 배포된 Swagger문서를 통해 대화하고 API의 사용법을 습득하게 됩니다.

아쉬운 점

이번 글에서는 Swagger팀에서 제공하는 예제 API를 사용해 보겠습니다. https://petstore.swagger.io/

이제 API문서는 준비가 완료 되었습니다! 프론트엔드 입장에서 문서에 정의된 스키마를 어떻게 코드로 옮겨올 수 있을까요? 제가 가장먼저 생각나는 방법은 ‘옮겨적기’입니다. OpenAPI Spec으로 작성된 Swagger문서에는 이미 해당 API가 어떤 parameter를 원하고, 응답으로 어떤 형태를 반환하는지 상세하게 적혀 있습니다. 실제로 예제중 펫의 상태를 넘겨 해당 상태의 펫 목록을 반환하는 API를 정의 해 보겠습니다.

Parameters와 Responses를 보니 해당 API를 호출하는 함수를 아래와 같이 정의할 수 있을것 같습니다.

// 상황에 따라 category, tag등도 개별 정의가 필요할 수 있지만 여기서는 간략화 하였습니다.
type PetStatus = 'available' | 'pending' | 'sold';

interface Pet {
  id: number;
  category: {
    id: number;
    name: string;
  };
  name: string;
  photoUrls: string[];
  tags: { id: number; name: string }[];
  status: PetStatus;
}

function getPetListByStatus(status: PetStatus) {
  const res = http.get('/pet/findByStatus');
}

타입스크립트를 사용하여 응답에 대한 모델을 정의하고, IDE의 타입시스템을 이용하여 강력한 추론을 사용할 수 있게 되었습니다. 다만 아래와같은 의문점들이 생깁니다.

  • 어플리케이션 서버에 작성한 모델에 대한 정의를 프론트엔드에서 다시 재정의 하고 있음
  • 어플리케이션 서버에서 정의한 모델에 대해 의존하고 있기 때문에 서버 변경에 취약함
  • 위 두 문제점을 통해, 프론트엔드에 작성된 모델은 믿을 수 없음

우리는 계속해서 변화하는 소프트웨어를 만들기 때문에, 열심히 옮겨 작성했던 시점에는 맞았지만 곧 서버측 정의와 달라지게 될겁니다. 프로덕트에 대한 개발이 안정기에 접어들고 변화가 더뎌진다면, 커뮤니케이션을 통해 충분히 하나하나 대응할 수 있겠지만 활발히 개발중인 프로덕트의 경우 스키마나 모델의 변화가 일주일에도 여러번 일어날 수 있습니다.

사례

저는 이전 팀에서 Apollo Client와 GraphQL Codegen을 통한 프론트엔드 모델 자동생성의 경험이 있었습니다. 새로운 팀에 합류한 뒤, restfulAPI를 사용하는 환경에서도 이와같은 경험을 구현하고자 팀 내에 OpenAPI Generator의 도입을 제안 하였습니다. 프론트엔드 개발자가 한명뿐인 새로운 환경에서 OpenAPI 문서가 보장하는 한, 모델 구현부분을 모두 generator와 서버 개발자들의 문서 작성으로 책임을 넘기고 저는 UI와 상태관리에 집중할 수 있게 되었습니다.

맞습니다. 이러한 문서를 기반으로한 코드 생성은 문서가 OpenAPI Specification을 정확히 지키고 있다는것을 전제로 하고 있습니다. description하나, name하나 잘못 작성한 이유로 generator가 동작하지 않는 경우가 많았습니다. 다음에는 이 generator를 통해 어떻게 서버와의 타입 일치를 보장할 수 있었는지 작성 하겠습니다.


はじめに

フロントエンドがサーバー側と通信するためのAPI使用を容易にするため、サーバー側では様々な仕様でAPIインターフェースを提供することができます。REST APIの中では、代表的にOpenAPI Specに沿ってAPIを開発し、これをSwaggerやPostmanを通じて文書化して共有することになります。 そのように作成されたAPI文書が共有されると、そのAPI仕様を確認する必要がある開発者Aは、そのAPIを開発した開発者Bに直接エンドポイントや使用方法などを要請する必要がなくなります。 単にウェブで配布されたSwagger文書を通じて対話し、APIの使用法を習得することになります。

不満点

今回の記事では、Swaggerチームが提供するサンプルAPIを使用してみましょう。 https://petstore.swagger.io/ これでAPI文書は準備完了です!フロントエンド側から見て、文書に定義されたスキーマをどのようにコードに移行できるでしょうか?私が最初に思いつく方法は「書き写す」ことです。 OpenAPI Specで作成されたSwagger文書には、既にそのAPIがどのようなパラメータを求め、応答としてどのような形式を返すのか詳細に記載されています。 実際に例の中からペットの状態を渡して該当状態のペットリストを返すAPIを定義してみましょう。 ParametersとResponsesを見ると、そのAPIを呼び出す関数を以下のように定義できそうです。

// 状況によってcategory、tagなども個別定義が必要かもしれませんが、ここでは簡略化しました。
type PetStatus = 'available' | 'pending' | 'sold';
interface Pet {
  id: number;
  category: {
    id: number;
    name: string;
  };
  name: string;
  photoUrls: string[];
  tags: { id: number; name: string }[];
  status: PetStatus;
}
function getPetListByStatus(status: PetStatus) {
  const res = http.get('/pet/findByStatus');
}

TypeScriptを使用して応答のモデルを定義し、IDEのタイプシステムを利用して強力な推論を使用できるようになりました。 しかし、以下のような疑問点が生じます。

アプリケーションサーバーに作成したモデルの定義をフロントエンドで再定義している アプリケーションサーバーで定義したモデルに依存しているため、サーバーの変更に脆弱 上記二つの問題点により、フロントエンドに作成されたモデルは信頼できない

私たちは常に変化するソフトウェアを作成しているため、一生懸命書き写した時点では正確でも、すぐにサーバー側の定義と異なるものになってしまうでしょう。 製品開発が安定期に入り変化が緩やかになれば、コミュニケーションを通じて一つ一つ対応することも可能ですが、活発に開発中の製品の場合、スキーマやモデルの変更が週に何度も発生する可能性があります。

事例

私は以前のチームでApollo ClientとGraphQL Codegenを通じたフロントエンドモデルの自動生成の経験がありました。 新しいチームに参加した後、RESTful APIを使用する環境でも同様の経験を実現するため、チーム内にOpenAPI Generatorの導入を提案しました。 フロントエンド開発者が一人だけの新しい環境で、OpenAPI文書が保証する限り、モデル実装部分をすべてgeneratorとサーバー開発者の文書作成に責任を委ね、私はUIと状態管理に集中できるようになりました。 そうです。このような文書ベースのコード生成は、文書がOpenAPI Specificationを正確に守っているという前提に基づいています。 descriptionひとつ、nameひとつ間違って作成したことで、generatorが動作しないケースが多くありました。 次回は、このgeneratorを通じてどのようにサーバーとの型一致を保証できたのかについて書きたいと思います。