J-한솔넷

나의 API 문서화 전략 본문

프로그래밍/PHP

나의 API 문서화 전략

jhansol 2023. 9. 11. 15:09

제가 API 문서화에 관심을 가진 것이 컨트리를 앱을 개발하면서 인듯 합니다. 제가 서버와 백엔드를 맞고, 서울에 있는 개발자가 안드로이드 앱을 개발하기로 하고 시작했는데, API 개발 후 Postman 등에서 읽을 수 있는 Json 형식의 문서로 만들어 달라는 요청에 문서화를 하기 시작했습니다. 단순히 Postman에만 국한 할 것이 아니라 나중에 유지보수 등을 위해서 프로젝트 내에 문서화되어 Swagger UI를 통해 지속적으로 관리되고, UI를 톷해 바로 테스트가 가능하도록 하기 위해 Swagger PHP를 사용하여 문서화를 하기로 결정했습니다.
그리고 제가 주로 사용하는 프레임워크가 Laravel을 이용하다보니 프레임워크를 지원하는 도구가 있었으면 해서 찾아보니 L5-Swagger가 있었습니다. 이를 이용하여 문서화를 하기 시작했습니다. 도구나 Swagger UI를 통해 보이는 결과물은 매우 만적합니다. 한 가지 문제라면 문서화를 하기 위해 주석이 코드에 비해 너무 많아 어히려 코드의 가독성을 떨어뜨리는 문제가 발생했습니다. 그래서 이것을 어떻게하면 단순화하고, 효과적으로 관리할까하고 고민하게 되었습니다. 이 고민은 지금도 하고 있고, 나름데로 최적화 중입니다.이재는 어느 정도 체계가 잡혀 정리할 수 있을 것 같아 포스팅합니다.

시작하기 전에

지금까지 Swagger PHP를 사용하면서 이해가 부족하여 애를 많이 애를 먹어서 나름데로 영문 사이트를 참고삼아 문서의 내용을 한글화 했습니다. PHP-Swagger-Documentation이라는 제목으로 저장해두었습니다. 자세한 내용은 영문 사이트 또는 저의 저장소를 방문하여 참고하십시오.

OpenApi 3.0

PHP SwaggerOpenApi를 기반으로 합니다. 저의 경우 OpenApi 3.0 버전에 맞추어 작업하고 있습니다. 문서화 규칙을 보면 보다 다양한 구성요소가 있을 수 있겠으나 저의 경우 아래와 같이 구분하여 사용하고 있습니다.

문서정보

@OA\Info 어노테이션에 기록되는 내용으로 문서의 제목, 설명, 라이센스, 연락처 등의 정보가 기록됩니다.

서버정보

@OA\Server어노테이션으로 Api 실행 가능한 서버 정보를 기록합니다. 이 부분은 앞의 어노테이션을 이용하여 다수의 서버 정보를 기록할 수 있습니다.

태그

Api 기능을 태그 단위로 그룹화 하기 위해 태그 이름을 설정합니다. @OA\Tag를 이용하여 복수로 설정 가능합니다.

보안(인증)

@OA\securityScheme 어노테이션을 이용하여 보안토큰, 인증관련 정보를 설정합니다. 이 항목 역시 복수로 지정 가능합니다.

Api 기능 및 경로 정보(엔드포인트)

Api 문서화의 핵심이 되는 부분으로 각 기능의 경로(URL)과 태깅, 파라메터, 응답 메시지 등에 대한 정보를 기술하는 부분입니다. 저는 이 부분에 대해 어떻게 간소화할지 고민해왔습니다.

컴포넌트

엔드포인트에서 주로 사용되는 파라미터와 응답 메시지부분에서 자주 사용하는 데이터 유형을 컴포넌트로 구성해두고 엔드포인트에서 참조하는 형태로 이용하기 위해 쿼리 파라메터, 스트림 데이터, 응답 데이터 구조를 기술합니다.

문서화 전략

야래의 예는 처음으로 Api 문서화를 진행한 것입니다. 보시다 시피 코드보다 문서화를 위한 주석이 많아 가독성이 떨어지는 것을 확인할 수 있습니다. 안타깝게도 아래 내용은 비교적 단순한 내용입니다. 결국 이보다 더 심하다는 이야기가 되겠죠(ㅠㅠ)

    /**
     * @OA\Post(
     *     path="/community/deleteContent",
     *     tags={"community"},
     *     description="해당 게시물을 삭제합니다.",
     *     @OA\RequestBody(
     *          @OA\MediaType(
     *              mediaType="multipart/form-data",
     *              @OA\Schema(
     *                  type="object",
     *                  @OA\Property(
     *                      property="api_token",
     *                      type="string",
     *                      description="API 토큰"
     *                  ),
     *                  @OA\Property(
     *                      property="access_token",
     *                      type="string",
     *                      description="Access 토큰"
     *                  ),
     *                  @OA\Property(
     *                      property="cid",
     *                      type="integer",
     *                      description="게시물 ID"
     *                  )
     *              )
     *          )
     *     ),
     *     @OA\Response(
     *          response=200,
     *          description="성공",
     *          @OA\MediaType(
     *              mediaType="application/json",
     *              @OA\Schema(
     *                  @OA\Property(
     *                      property="message",
     *                      type="string"
     *                  )
     *              )
     *          )
     *     ),
     *     @OA\Response(
     *          response=400,
     *          description="잘 못된 접근",
     *          @OA\MediaType(
     *              mediaType="application/json",
     *              @OA\Schema(
     *                  @OA\Property(
     *                      property="message",
     *                      type="string"
     *                  )
     *              )
     *          )
     *     ),
     *     @OA\Response(
     *          response=401,
     *          description="인증 실폐",
     *          @OA\MediaType(
     *              mediaType="application/json",
     *              @OA\Schema(
     *                  @OA\Property(
     *                      property="message",
     *                      type="string"
     *                  )
     *              )
     *          )
     *     ),
     *     @OA\Response(
     *          response=462,
     *          description="만료된 엑세스 토큰",
     *          @OA\MediaType(
     *              mediaType="application/json",
     *              @OA\Schema(
     *                  @OA\Property(
     *                      property="message",
     *                      type="string"
     *                  )
     *              )
     *          )
     *     ),
     *     @OA\Response(
     *          response=500,
     *          description="서버오류",
     *          @OA\MediaType(
     *              mediaType="application/json",
     *              @OA\Schema(
     *                  @OA\Property(
     *                      property="message",
     *                      type="string"
     *                  )
     *              )
     *          )
     *     )
     * )
     * @brief 콘텐츠를 삭제한다.
     * @param Request $request
     * @return \Illuminate\Http\JsonResponse
     */
    public function deleteContent( Request $request ) {
        try{
            $content = $request->board_content;
            $content->delete();
            return response()->json(['message' => __('api.r200')], 200);
        }
        catch ( \Exception $e ) {
            return response()->json(['message' => __('api.r500')], 500);
        }
    }

위 내용을 보면 Api 기능 문서화에서 요청 파라메터, 처리 후 응답 부분이 대부분을 차지하고 있습니다.
그리고 아래 내용은 현재 문서화 내용입니다.

    /**
     * 직업군정보 목록을 리턴한다.
     * @param Request $request
     * @return JsonResponse
     * @OA\Get (
     *     path="/common/listOccupationalGroup",
     *     tags={"common"},
     *     @OA\Parameter ( ref="#/components/parameters/filter"),
     *     @OA\Parameter ( ref="#/components/parameters/op"),
     *     @OA\Parameter ( ref="#/components/parameters/keyword"),
     *     @OA\Parameter ( ref="#/components/parameters/page"),
     *     @OA\Parameter ( ref="#/components/parameters/page_per_items"),
     *     @OA\Parameter ( ref="#/components/parameters/order"),
     *     @OA\Parameter ( ref="#/components/parameters/dir"),
     *     @OA\Response (
     *         response=200,
     *         description="요청 성공",
     *         @OA\JsonContent (
     *             allOf={
     *                 @OA\Schema (ref="#/components/schemas/api_message"),
     *                 @OA\Schema (ref="#/components/schemas/page_info"),
     *                 @OA\Schema (ref="#/components/schemas/occupational_group_list")
     *             }
     *         )
     *     ),
     *      @OA\Response (response=404, ref="#/components/responses/404"),
     *      @OA\Response (response=500, ref="#/components/responses/500")
     * )
     */
    public function listOccupationalGroup(Request $request): JsonResponse
    {
        try {
            list($filter_field, $filter_operator, $filter_keyword) = get_filter($request);
            list($page, $page_per_items, $start_rec_no) = get_page($request);
            list($order_field, $order_direction) = get_order($request, 'name');

            $result = OccupationalGroup::orderBy($order_field, $order_direction)
                ->where('leaf_node', 1)
                ->skip($start_rec_no)
                ->take($page_per_items)
                ->when($filter_field && $filter_keyword, function (Builder $query) use ($filter_field, $filter_operator, $filter_keyword) {
                    $query->where($filter_field, $filter_operator, $filter_keyword);
                })
                ->get();

            if ($result->isNotEmpty()) return new OccupationalGroups($result, $page, $page_per_items);
            else return new Message(404);
        } catch (\Exception $e) {
            return new Message(500);
        }
    }

두 소스는 각기 다른 프로젝트이지만 문서화 내용이 많이 간결해지고, 보다 체계적으로 변경된 것을 확인할 수 있습니다. 이렇게 최적화하기 위해 아래와 같은 전략으로 문서화를 진행하고 있습니다.

문서화 전략

  1. URL 파라메터 및 쿼리파라메터 데이터를 유형별로 정리하고, 같은 유형의 데이터를 통합한다. 통합된 파라메트를 컴포넌트로 정의한다.
  2. 각 Api 기능별 Form data를 검정하고, 처리하는 클래스를 생성하고 각 클래스에 필요한 데이터 구조를 해당 코드 위에 컴포넌트로 정의한다.
  3. 응답 메시지 부분은 크게, 응답(상태) 메시지, 에러 메시지, 단일 레코드 데이터, 복수의 레코드 데이터를 포함하는 응답 메시지로 구분하여 각 클래스를 생성하고 코드 위에 컴포넌트로 정의한다.
  4. 단일 레코드 데이터는 데이터 모델 객체에 응답을 위한 기능을 구현하고, 구현된 코드 위에 데이터 구조를 컴포넌트로 정의한다.
  5. 복수 레코드나 다른 데이터와 조합이 필요한 경우 해당 기능의 클래스를 생성하고 해당 코드 위에 데이터 구조를 컴포넌트로 설정한다.
  6. 위 과정이 모두 끝나면 Api 기능 문서화 시 해당 컴포넌트를 참조하여 기술한다.

제가 주로 사용하고 있는 플렛폼이 Laravel이라 코드가 라라벨 맞추어져 있습니다. 이점 참고바랍니다.

URL 파라메터 {id}

라라벨에서 Eloquent 모델을 이용하여 데이터를 제어합니다. 해당 기능을 호출할 때 타입 힌트를 통해 의존성 주입으로 모델 디어타가 아래 함수에 전달됩니다. 그래서 제가 작성 중인 Api 기능은 /common/getOccupationalGroup/{id} 형태로 이름을 통일하고, 제한적으로 이용하고 있습니다.

    public function getOccupationalGroup(OccupationalGroup $id): JsonResponse {
        return $id->response();
    }

위 파라메터를 위해 아래와 같은 파라메터 컴포넌트를 설정합니다. 이 설정은 전역에 다 사용하므로 기본 클레스 코드 위에 기술합니다.

    /**
     * @OA\Parameter (
     *     name="id",
     *     in="path",
     *     required=true,
     *     description="일련번호",
     *     @OA\Schema (type="integer")
     * )
     */

검색을 위한 쿼리 파레메터

저의 프로젝트에서는 Query string은 주로 검색을 위해 이용합니다. 모든 검색 기능이 필요한 Api 기능에는 field, op, keyword의 파라메터를 가지고 있고 이 3개를 이용하여 검색합니다. 그리고 정렬을 위해 order, dir이라는 2두 개의 파라메트를 가집니다. 마지막으로 페이징을 위해 page, page_per_items라는 2개의 파라메터를 가지고 있고, 이들 7개의 파라메터를 기본 클래스 코드 위에 컴포넌트로 설정합니다. 코드는 아래와 같습니다.

    /**
     * @OA\Parameter (
     *     name="filter",
     *     in="query",
     *     required=false,
     *     description="필터 대상 필드명",
     *     @OA\Schema (type="string")
     * )
     * @OA\Parameter (
     *     name="op",
     *     in="query",
     *     required=false,
     *     description="필터 연산자, 기본값 : like",
     *     @OA\Schema (type="string", enum={"like","=",">=","<=",">","<","<>"})
     * )
     * @OA\Parameter (
     *     name="keyword",
     *     in="query",
     *     required=false,
     *     description="필터 키워드",
     *     @OA\Schema (type="string")
     * )
     * @OA\Parameter (
     *     name="page",
     *     in="query",
     *     required=false,
     *     description="요청 페이지 번호, 기본값 : 1",
     *     @OA\Schema (type="integer")
     * )
     * @OA\Parameter (
     *     name="page_per_items",
     *     in="query",
     *     required=false,
     *     description="요청 페이지당 최대 항목 수, 기본값 : 50",
     *     @OA\Schema (type="integer")
     * )
     * @OA\Parameter (
     *     name="order",
     *     in="query",
     *     required=false,
     *     description="정렬 요청 필드",
     *     @OA\Schema (type="string")
     * )
     * @OA\Parameter (
     *     name="dir",
     *     in="query",
     *     required=false,
     *     description="정렬 방식, (asc, desc), 기본값 : asc",
     *     @OA\Schema (type="string")
     * )
     */

위 컴포넌트를 참조하여 아래와 같이 기능을 기술합니다.

    /**
     * 직업군정보 목록을 리턴한다.
     * @param Request $request
     * @return JsonResponse
     * @OA\Get (
     *     path="/common/listOccupationalGroup",
     *     tags={"common"},
     *     @OA\Parameter ( ref="#/components/parameters/filter"),
     *     @OA\Parameter ( ref="#/components/parameters/op"),
     *     @OA\Parameter ( ref="#/components/parameters/keyword"),
     *     @OA\Parameter ( ref="#/components/parameters/page"),
     *     @OA\Parameter ( ref="#/components/parameters/page_per_items"),
     *     @OA\Parameter ( ref="#/components/parameters/order"),
     *     @OA\Parameter ( ref="#/components/parameters/dir"),
     *     @OA\Response (
     *         response=200,
     *         description="요청 성공",
     *         @OA\JsonContent (
     *             allOf={
     *                 @OA\Schema (ref="#/components/schemas/api_message"),
     *                 @OA\Schema (ref="#/components/schemas/page_info"),
     *                 @OA\Schema (ref="#/components/schemas/occupational_group_list")
     *             }
     *         )
     *     ),
     *      @OA\Response (response=404, ref="#/components/responses/404"),
     *      @OA\Response (response=500, ref="#/components/responses/500")
     * )
     */

Form 데이터를 위한 문서화

현재 제가 진행중인 프로젝트 초기만 하더라도 이 부분은 제대로 정립이 않되었습니다. 하지만 Laravel의 Request를 활용하면서부터 Form Data 관련 컴포넌터 설정이 만족스럽게 관리 가능하게 단순화 되었습니다. 데이터 구조는 변한 것은 없습니다. 단지 데이터를 처리하는 객체 위에 문서화함으로 변경 내용을 바로 반영할 수 있어 효과적이라고 생각이 됩니다.

class RequestJoinWorker extends FormRequest {
    use RequestValidation;

    public function authorize(): bool {return true;}

    /**
     * 유효성 검사 규칙을 리턴한다.
     * @return array
     * @OA\Schema (
     *     schema="join_worker_profile",
     *     title="회원가입 근로자 프로필",
     *     @OA\Property (
     *          property="family_name",
     *          type="string",
     *          description="성"
     *     ),
     *     @OA\Property (
     *          property="given_names",
     *          type="string",
     *          description="이름"
     *     ),
     *     @OA\Property (
     *          property="hanja_name",
     *          type="string",
     *          description="한자이름"
     *     ),
     *     @OA\Property (
     *          property="identity_no",
     *          type="string",
     *          description="신분증 번호, 암호화 필요"
     *     ),
     *     @OA\Property (
     *          property="sex",
     *          type="string",
     *          enum={"M","F"},
     *          description="성별"
     *     ),
     *     @OA\Property (
     *          property="birthday",
     *          type="date",
     *          description="생년월일"
     *     ),
     *     @OA\Property (
     *          property="birth_country_id",
     *          type="integer",
     *          description="국가 일련번호"
     *     ),
     *     @OA\Property (
     *          property="another_nationality_ids",
     *          type="array",
     *          @OA\Items(type="integer"),
     *          description="그 외 다른 국적"
     *     ),
     *     @OA\Property (
     *          property="old_family_name",
     *          type="string",
     *          description="이전 성"
     *     ),
     *     @OA\Property (
     *          property="old_given_names",
     *          type="string",
     *          description="이전 이름"
     *     ),
     *     required={"family_name", "given_names", "identity_no", "sex", "birthday", "birth_country_id"}
     * )
     */
    public function rules(): array {
        return [
            'email' => ['required', (new ValidCryptData())->type('email'), new CryptDataUnique('users', 'email')],
            'country_id' => ['required', 'integer', 'exists:countries,id'],
            'cell_phone' => ['required', new ValidCryptData()],
            'address' => ['required', new ValidCryptData()],
            'family_name' => ['required'],
            'given_names' => ['required'],
            'identity_no' => ['required', new ValidCryptData()],
            'sex' => ['required', 'in:M,F'],
            'birthday' => ['required', 'date'],
            'birth_country_id' => ['required', 'integer', 'exists:countries,id'],
            'another_nationality_ids' => ['nullable', new ExistsValues('countries', 'id')],
            'management_org_id' => ['nullable', 'integer', 'exists:users,id'],
            'password' => [(new ValidCryptData())->nullable()->required($this->input('login_method') == 10)],
        ];
    }
}

응답 메시지

상태 메시지 응답

단순히 상태코드와 상태코드에 대한 한줄의 메시지만 전달하는 전용 클레스를 생성하고 해당 코드 위에 각 상태에 대한 컴포넌트를 설정하고 이를 이용합니다.

/**
 * @OA\Schema (
 *     schema="api_message",
 *     title="Api 메시지",
 *     @OA\Property (
 *          property="message",
 *          type="string",
 *          description="에러 메시지"
 *     )
 * )
 * @OA\Response (
 *      response=200,
 *      description="요청성공",
 *      @OA\JsonContent (
 *          @OA\Schema (ref="#/components/schemas/api_message")
 *      )
 * )
 * @OA\Response (
 *      response=201,
 *      description="생성됨, 추가절차 필요",
 *      @OA\JsonContent (
 *          @OA\Schema (ref="#/components/schemas/api_message")
 *      )
 * )
 * @OA\Response(
 *     response="401",
 *     description="인증 실폐",
 *     @OA\JsonContent(
 *          @OA\Schema (ref="#/components/schemas/api_message")
 *     )
 * )
 * @OA\Response(
 *     response="403",
 *     description="권한 없음",
 *     @OA\JsonContent(
 *          @OA\Schema (ref="#/components/schemas/api_message")
 *     )
 * )
 * @OA\Response(
 *     response="404",
 *     description="자료 없음 (찾지 못함)",
 *     @OA\JsonContent(
 *          @OA\Schema (ref="#/components/schemas/api_message")
 *     )
 * )
 * @OA\Response(
 *      response=406,
 *      description="수용할 수 없음",
 *      @OA\JsonContent(
 *          @OA\Schema (ref="#/components/schemas/api_message")
 *      )
 * )
 * @OA\Response(
 *      response=461,
 *      description="잘 못된 엑세스 토큰임",
 *      @OA\JsonContent(
 *          @OA\Schema (ref="#/components/schemas/api_message")
 *      )
 * )
 * @OA\Response(
 *     response="462",
 *     description="엑세스 토큰 만료됨",
 *     @OA\JsonContent(
 *          @OA\Schema (ref="#/components/schemas/api_message")
 *     )
 * )
 * @OA\Response(
 *     response="465",
 *     description="알 수 없는 단말기",
 *     @OA\JsonContent(
 *          @OA\Schema (ref="#/components/schemas/api_message")
 *     )
 * )
 * @OA\Response(
 *      response=472,
 *      description="인증번호 생성 불가",
 *      @OA\JsonContent(
 *          @OA\Schema (ref="#/components/schemas/api_message")
 *      )
 * )
 * @OA\Response(
 *     response="500",
 *     description="서버 오류",
 *     @OA\JsonContent(
 *          @OA\Schema (ref="#/components/schemas/api_message")
 *     )
 * )
 */
class Message extends JsonResponse {
    function __construct(?int $code = 200) {
        parent::__construct(['message' => __('api.r' . $code)], $code);
    }
}

단순 레코드 데이터 응답

Laravel에서는 Eloquent 모델을 통해 관리되고 있습니다. 여기에 데이터를 제어하기 위한 메소드들이 정의되어 있기 때문에 여기에 응답 메시지를 정의해두고 있습니다. 저는 response()라는 메소드로 정의했습니다.

    /**
     * 데이터를 Json 문자열로 리턴한다.
     * @return JsonResponse
     * @OA\Schema (
     *     schema="country",
     *     title="국가정보",
     *     @OA\Property (
     *          property="id",
     *          type="integer",
     *          description="일련번호"
     *     ),
     *     @OA\Property (
     *          property="name",
     *          type="string",
     *          description="국가명"
     *     ),
     *     @OA\Property (
     *          property="en_name",
     *          type="string",
     *          description="국가명 (영문)"
     *     ),
     *     @OA\Property (
     *          property="code",
     *          type="string",
     *          description="국가코드"
     *     ),
     *     @OA\Property (
     *          property="continent",
     *          type="string",
     *          description="대륙명"
     *     ),
     *     @OA\Property (
     *          property="en_continent",
     *          type="string",
     *          description="대륙명 (영문)"
     *     ),
     *     @OA\Property (
     *          property="language_code",
     *          type="string",
     *          description="언어코드"
     *     )
     * )
     */
    public function response() : JsonResponse {
        return new Data($this->toArray());
    }

위 코드에 응답 메시지를 위해 Data라는 객체를 생성하고 있습니다. 이것은 단순 데이터를 담아 응답하기 위한 클래스를 별도로 정의해두고 활용하고 있습니다. 해당 클래스 코드는 아래와 같습니다.

class Data extends JsonResponse {
    function __construct(array $data, ?int $code = 200) {
        parent::__construct(['message' => __('api.r' . $code)] + $data, $code);
    }
}

복수 레코드 또는 다른 데이터와 조합한 응답

이 유형은 현재 페이지 단위의 목록으로 응답하는 곳에 이용하고 있습니다. 이 부분을 위해 별도의 응답 클래스를 작성하여 이용합니다.
여기에는 페이지 단위 목록으로 응답하는 경우가 대부분이라 아래의 PageResponse 클래스를 상속받아 각 응답별 클래스를 생성합니다.

class PageResponse extends JsonResponse {
    function __construct(array $arr, int $page = 0, int $page_per_items = 0) {
        parent::__construct([
            'message' => __('api.r200'),
            'page' => $page,
            'page_per_items' => $page_per_items,
            'item_count' => count($arr),
            'items' => $arr
        ]);
    }
}

위 클래스를 상속받아 실재 응답 클래스를 생성합니다.

/**
 * @OA\Schema (
 *     schema="occupational_group_list",
 *     title="직업군정보 목록",
 *     @OA\Property (
 *          property="items",
 *          type="array",
 *          description="직업군정보 목록",
 *          @OA\Items (
 *              type="object",
 *              allOf={@OA\Schema(ref="#/components/schemas/occupational_group")}
 *          )
 *     )
 * )
 */
class OccupationalGroups extends PageResponse {
    function __construct(Collection $collection, $page, $page_per_items) {
        $data = [];
        foreach($collection as $t) $data[] = $t->toArray();
        parent::__construct($data);
    }
}

컴포넌트 참조를 통한 단순화

위와 같이 컴포넌트를 생성했다면 이재는 이를 참조하여 Api 기능(엔드포인트)를 기술해주면 됩니다.

```php
    /**
     * 직업군정보 목록을 리턴한다.
     * @param Request $request
     * @return JsonResponse
     * @OA\Get (
     *     path="/common/listOccupationalGroup",
     *     tags={"common"},
     *     @OA\Parameter ( ref="#/components/parameters/filter"),
     *     @OA\Parameter ( ref="#/components/parameters/op"),
     *     @OA\Parameter ( ref="#/components/parameters/keyword"),
     *     @OA\Parameter ( ref="#/components/parameters/page"),
     *     @OA\Parameter ( ref="#/components/parameters/page_per_items"),
     *     @OA\Parameter ( ref="#/components/parameters/order"),
     *     @OA\Parameter ( ref="#/components/parameters/dir"),
     *     @OA\Response (
     *         response=200,
     *         description="요청 성공",
     *         @OA\JsonContent (
     *             allOf={
     *                 @OA\Schema (ref="#/components/schemas/api_message"),
     *                 @OA\Schema (ref="#/components/schemas/page_info"),
     *                 @OA\Schema (ref="#/components/schemas/occupational_group_list")
     *             }
     *         )
     *     ),
     *      @OA\Response (response=404, ref="#/components/responses/404"),
     *      @OA\Response (response=500, ref="#/components/responses/500")
     * )
     */
    public function listOccupationalGroup(Request $request): JsonResponse
    {
        try {
            list($filter_field, $filter_operator, $filter_keyword) = get_filter($request);
            list($page, $page_per_items, $start_rec_no) = get_page($request);
            list($order_field, $order_direction) = get_order($request, 'name');

            $result = OccupationalGroup::orderBy($order_field, $order_direction)
                ->where('leaf_node', 1)
                ->skip($start_rec_no)
                ->take($page_per_items)
                ->when($filter_field && $filter_keyword, function (Builder $query) use ($filter_field, $filter_operator, $filter_keyword) {
                    $query->where($filter_field, $filter_operator, $filter_keyword);
                })
                ->get();

            if ($result->isNotEmpty()) return new OccupationalGroups($result, $page, $page_per_items);
            else return new Message(404);
        } catch (\Exception $e) {
            return new Message(500);
        }
    }
    /**
     * 지정 직업군 정보를 리턴한다.
     * @param OccupationalGroup $id
     * @return JsonResponse
     * @OA\Get (
     *     path="/common/getOccupationalGroup/{id}",
     *     tags={"common"},
     *     @OA\Parameter ( ref="#/components/parameters/id"),
     *     @OA\Response (
     *         response=200,
     *         description="요청 성공",
     *         @OA\JsonContent (
     *             allOf={
     *                 @OA\Schema (ref="#/components/schemas/api_message"),
     *                 @OA\Schema (ref="#/components/schemas/occupational_group")
     *             }
     *         )
     *     )
     * )
     */
    public function getOccupationalGroup(OccupationalGroup $id): JsonResponse {
        return $id->response();
    }
    /**
     * 직업군 정보를 갱신한다.
     * 이용 대상 : 운영 실무자
     * @param RequestUpdateOccupationalGroup $request
     * @param OccupationalGroup $id
     * @return JsonResponse
     * @OA\Post (
     *     path="/operator/updateOccupationalGroup/{id}",
     *     tags={"operator"},
     *     security={{"BearerAuth":{}, "AccessTokenAuth": {}}},
     *     @OA\Parameter(ref="#/components/parameters/id"),
     *     @OA\RequestBody (
     *          @OA\MediaType (
     *              mediaType="multipart/form-data",
     *              @OA\Schema (ref="#/components/schemas/editable_occupational_group")
     *          )
     *     ),
     *     @OA\Response (response=200, ref="#/components/responses/200"),
     *     @OA\Response (response=400, ref="#/components/responses/400"),
     *     @OA\Response (response=401, ref="#/components/responses/401"),
     *     @OA\Response (response=404, ref="#/components/responses/404"),
     *     @OA\Response (response=500, ref="#/components/responses/500"),
     * )
     */
    public function updateOccupationalGroup(RequestUpdateOccupationalGroup $request, OccupationalGroup $id) : JsonResponse {
        try {
            $id->fill([
                'en_name' => $request->input('en_name'),
                'description' => $request->input('description'),
                'en_description' => $request->input('en_description'),
                'active' => $request->boolean('active') ? 1 : 0,
                'is_education_part' => $request->boolean('is_education_part') ? 1 : 0,
            ])->save();
            return ApiMessage::message();
        } catch (\Exception $e) {
            return ApiMessage::message(500);
        }
    }

마치며

이렇게 Api 문서화가 된 경우 자체 Swagger UI를 통해 별도의 도구 없이 웹상에서 테스트를 진행하며 앱이나, 프론트엔드 어플리케이션을 개발할 수 있허 매우 편리합니다. 그리고 코드에 문서화 내용이 포함되어 있으므로 코드를 수정하고, 변경된 내용을 바로 수정하여 관리할 수 있다는 점에서 유용합니다.

'프로그래밍 > PHP' 카테고리의 다른 글

삽질의 연속  (0) 2024.04.20
메모리 프로파일링?  (0) 2023.11.09
ChatGPT를 이용한 코딩?  (0) 2023.10.26
WSL2를 쓰야하나!!!  (0) 2023.09.14