발표

API 연동 자동화

김영재0412 2025. 12. 16. 13:37

API Tool은 거의 모든 응용 프로그램 개발자에게 필수적인 도구입니다. API의 수가 적을 때는 큰 문제가 없지만, API가 많아지면 모든 API를 수동으로 관리하는 것은 점점 어려워지기 마련입니다. 이번 글에서는 특히 백엔드 엔지니어들이 자주 겪는 API 연동 자동화에 관한 문제와 이를 Postman으로 어떻게 개선할 수 있는지 살펴보겠습니다.

1. API 연동 자동화의 필요성

해당 내용을 본격적으로 다루기 이전, 연동 자동화의 필요성에 대해 알아보고 저희 팀에서 사용 중인 API Tool인 Postman에 대해 간략하게 설명하겠습니다.

API Tool이란?

API 요청을 생성하고 테스트하여 서버와 클라이언트 간 통신을 검증할 수 있는 Tool입니다. 대표적으로 “Postman, Insomnia, Hoppscotch” 등이 있습니다. 저희는 여러 Tool을 검토한 끝에 아래와 같은 이유로 Postman을 최종적으로 선택하여 사용중에 있습니다.

  • 직관적인 인터페이스 팀 협업 지원이 용이함
  • 다양한 프로토콜(GraphQL, WebSocket 등)을 지원
  • API 요청 생성, 테스트, 문서화를 간편하게 수행 가능
Postman

그러나 사용하다 보니 몇 가지 문제점도 발견하게 되었습니다.

  • Collection 관리의 어려움: 여러 프로젝트의 Collection을 팀이 공동 관리하다 보니 Collection 또는 API 중복 발생
  • API 수동 추가: Collection에 API를 수동 추가/변경하다가 누락 발생
  • API 갯수 제한: 사용 중인 Tier에서 API 최대 3개로 제한
APIs 갯수 제한

 

Collection 관리나 API 수동 추가와 같은 반복 작업을 수작업으로 처리하는 것은 비효율적일 뿐 아니라 휴먼 에러를 일으킬 가능성도 높습니다.

이런 문제를 해결하기 위해, 저희는 Postman의 문제보다 사용방식의 문제라 생각하여, Postman을 활용한 자동화 방식을 도입하여 개선하기로 했습니다.

2. API 연동 자동화 구현

요구 사항:

  • 서버가 배포될 때 수동이 아닌 자동으로 API 추가/변경 반영
  • 비용 효율적이어야한다.

우선 Postman에서 제공하는 기능을 찾아보았습니다. Github Repository Integration 기능이 있었으나, Enterprise Tier 부터 사용이 가능했습니다. 해당 기능만을 위해 Enterprise Tier를 사용하는 것은 비용적으로 부담되었습니다. 결국, Postman에서 제공하는 기능을 사용하는 것이 아닌 직접 구현하기로 하였습니다.

우선 Postman API 중 Collection Update API가 있는지 찾아보았고 다행히 제공하고 있었습니다. 이제 문제는 간단해져 Postman Collection Update API의 양식에 맞추면 요청만 날리면 문제는 해결입니다. Postman Collection Update API에는 CollectionId와 인증키, Postman 2.0 양식의 JSON 파일이 필요했습니다. CollectionId와 인증키인 APIKey는 Postman에서 쉽게 얻을 수 있었습니다.

CollectionId는 Collection Detail에서 확인 가능합니다.

 

다음은, Postman 2.0 양식의 JSON 파일을 얻는 프로세스입니다.

 

1. OpenAPI 3.0 양식의 JSON 추출

저는 API 문서화 및 설계 도구를 목적으로 Swagger를 사용하고 있습니다. NestJs에서 Swagger를 사용하신다면, 아래와 같이 사용중에 있으실 겁니다.

const config = new DocumentBuilder()
    .setTitle('anonymo-us')
    .setDescription('anonymo-us description')
    .setVersion('1.0.0')
    .addServer(`http://localhost`)
    .build();

  const document = SwaggerModule.createDocument(app, config);

  const swaggerCustomOptions: SwaggerCustomOptions = {
    swaggerOptions: {
      persistAuthorization: true,
    },
  };

  SwaggerModule.setup('api-docs', app, document, swaggerCustomOptions);

 

여기서 document는 OpenAPI 3.0 양식의 JSON 형태로 추출됩니다. 이 JSON을 바탕으로 Postman JSON 파일을 생성하기로 하였습니다.

 

2. Postman 2.0 JSON 변환

OpenAPI 3.0 양식의 JSON 파일을 Postman 2.0 양식의 JSON 파일로 변환하기 위해 Postman에서 제공하는 openapi-to-postmanv2 라이브러리를 사용하였습니다. openapi-to-postmanv2의 여러 옵션을 조합해 원하는 형태의 Postman 2.0 양식의 JSON 파일을 만들었습니다.

 

openapi-to-postmanv2 옵션은 아래와 같습니다.

import fs from "fs";
import Converter from "openapi-to-postmanv2";

function generatePostmanJson(document) {
  Converter.convert(
    { type: "json", data: document },
    {
      schemaFaker: false,
      requestNameSource: "Fallback",
      indentCharacter: "Space",
      folderStrategy: "Tags",
      requestParametersResolution: "Example",
      exampleParametersResolution: "Example",
      parametersResolution: "Schema",
      includeDeprecated: true,
    },
    (err, conversionResult) => {
      const collection = conversionResult.output[0].data;

      const postman = {
        collection: collection,
      };

      fs.writeFileSync(
        "postman/postman.json",
        JSON.stringify(postman, null, 2)
      );
    }
  );
}

 

3. CI / CD를 통한 자동화

저희는 CI / CD로 Github Actions을 사용하고 있습니다. Github Actions을 통해 서버 배포가 완료되고 난 후 Postman Collection 업데이트 요청을 보내기로 하였습니다. 변환한 JSON 파일을 저장하고 이를 같이 Github Repository에 배포하였습니다.

Github Actions Yaml 파일은 아래와 같습니다.

postman:
    name: Sync Postman Collection
    needs: deploy
    environment: ${{ github.ref_name }}
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/dev'
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Update Postman Collection
        run: |
          CODE=$(curl --location --request PUT 'https://api.getpostman.com/collections/${{ vars.POSTMAN_COLLECTION_ID }}' \
          --header 'Content-Type: application/json' \
          --header "X-API-Key: ${{ secrets.POSTMAN_API_KEY }}" \
          --data "@${{ github.workspace }}/postman/postman.json" \
          --write-out "%{http_code}" \
          --silent \
          --output /dev/null)

          if [ $CODE -eq 200 ]; then
            echo "✅ Postman Collection updated successfully ✅";
            exit 0;
          else
            echo "❌ Postman Collection update failed ❌";
            exit 1;
          fi
        env:
          POSTMAN_API_KEY: ${{ secrets.POSTMAN_API_KEY }}

 

API 연동은 개발 서버와 연동을 기준으로 작업하였기에 if문으로 처리하였습니다. 서버 배포인 deploy 작업이 완료되면 postman 작업이 진행됩니다.

Postman에서 제공하는 Update Collection API로 요청을 보내고 성공 실패 여부를 표시하는 간단한 작업입니다. 이로써 API 연동 자동화 작업이 완료되었습니다.


Github Actions postman 작업

 

3. API 연동 자동화 개선하기

이렇게 끝날 줄 알았던 자동화는 몇 가지 문제점이 발생합니다.

 

postman.json 파일의 병합 충돌

 

병합을 할 때 postman.json 파일에서 충돌이 나는 경우가 빈번히 생겼습니다. 원인을 찾아보니 아래와 같았습니다.

위 값은 사용하지 않는 값이기에 postman.json에서 제거하기로 하였습니다.

import fs from "fs";
import Converter from "openapi-to-postmanv2";

function generatePostmanJson(document) {
  Converter.convert(
    { type: "json", data: document },
    {
      schemaFaker: false, 
      requestNameSource: "Fallback",
      indentCharacter: "Space",
      folderStrategy: "Tags",
      requestParametersResolution: "Schema",
      parametersResolution: "Schema",
      includeDeprecated: true,
    },
    (err, conversionResult) => {
      const collection = conversionResult.output[0].data;
      removeIdFields(collection);
      collection.info._postman_id = undefined;

      const postman = {
        collection: collection,
      };

      fs.writeFileSync(
        "postman/postman.json",
        JSON.stringify(postman, null, 2)
      );
    }
  );
}

function removeIdFields(obj) {
  if (Array.isArray(obj)) {
    obj.forEach((item) => removeIdFields(item));
  } else if (typeof obj === "object" && obj !== null) {
    Object.keys(obj).forEach((key) => {
      if (key === "id") {
        delete obj[key];
      } else {
        removeIdFields(obj[key]);
      }
    });
  }
}

 

requestParametersResolution, exampleParametersResolution 옵션을 Schema로 변경하여 값이 무작위가 아닌 Schema 타입의 값으로 설정되도록 개선했습니다.

무작위 값 → Schema 타입

 

removeIdFields 함수를 생성하여 id 필드를 제거하여 불필요한 충돌을 방지하도록 개선하였고, collection info의 id 값 또한 undefined로 대체하여 개선하였습니다.

불필요한 id값 제거

 

개선 후 API 추가/ 수정 외엔 변경점이 생기지 않아, 사용하지 않는 값들로 인한 병합 충돌이 더는 발생하지않았습니다.

 

의미없는 postman job 동작

 

postman.json 파일의 병합 충돌을 해결하고 나니 postman.json의 변경점이 없어도 Github Actions — postman job은 실행되었습니다. 실행되어도 문제는 없지만, Github Actions의 실행 비용을 절감하고자 개선을 결정하였습니다.

GitHub Actions의 changed-files를 사용하여 postman.json 파일의 변경 사항이 있을 때만 postman job을 실행하도록 설정했습니다.

deploy:
  name: anonymo-us
  environment: ${{ github.ref_name }}
  runs-on: ubuntu-latest
  outputs:
    any_changed: ${{ steps.changed-files.outputs.any_changed }}

  steps:
    - name: Checkout
      uses: actions/checkout@v4

    - name: Check for Changes in postman.json
      if: github.ref == 'refs/heads/dev'
      id: changed-files
      uses: tj-actions/changed-files@v45
      with:
        files: 'postman/postman.json'

postman:
  name: Sync Postman Collection
  needs: deploy
  environment: ${{ github.ref_name }}
  runs-on: ubuntu-latest
  if: ${{ needs.deploy.outputs.any_changed == 'true' }}
  steps:
    - name: Checkout
      uses: actions/checkout@v4

    - name: Update Postman Collection
      run: |
        CODE=$(curl --location --request PUT 'https://api.getpostman.com/collections/${{ vars.POSTMAN_COLLECTION_ID }}' \
        --header 'Content-Type: application/json' \
        --header "X-API-Key: ${{ secrets.POSTMAN_API_KEY }}" \
        --data "@${{ github.workspace }}/postman/postman.json" \
        --write-out "%{http_code}" \
        --silent \
        --output /dev/null)

        if [ $CODE -eq 200 ]; then
          echo "✅ Postman Collection updated successfully ✅";
          exit 0;
        else
          echo "❌ Postman Collection update failed ❌";
          exit 1;
        fi
      env:
        POSTMAN_API_KEY: ${{ secrets.POSTMAN_API_KEY }}

changed-files 작업으로 postman.json에 변경 사항이 있는 경우만 postman 작업이 실행되어 GitHub Actions 실행 비용을 절감할 수 있었습니다.

 

Error: Too many levels of nesting to fake this schema 오류

 

깊은 참조 구조로 Too many levels of nesting 오류가 발생하여 관련 옵션인 optimizeConversion 옵션과 stackLimit을 수정하였습니다. optimizeConversion 옵션은 변환 성능을 최적화하는 데 도움을 주지만, stackLimit을 설정하려면 optimizeConversion 옵션의 변경이 불가피합니다. 그 후 중첩 한도의 제한을 풀기 위해 stackLimit를 50으로 설정하여 Too many levels of nesting 오류를 해결하였습니다. 자세한 옵션은 해당 링크에서 확인하실 수 있습니다.

    {
      schemaFaker: false,
      requestNameSource: "Fallback",
      indentCharacter: "Space",
      folderStrategy: "Tags",
      requestParametersResolution: "Schema",
      exampleParametersResolution: "Schema",
      parametersResolution: "Schema",
      includeDeprecated: true,
      optimizeConversion: false,
      stackLimit: 50,
    }

결론

위와 같은 개선 작업을 통해 Postman — API 연동 자동화를 더욱 원활하게 구축하였습니다. 저희는 이제 서버 배포를 한 후 Postman에 API를 더는 수동으로 작업하지않고 커피 한잔의 여유를 가지게 되었습니다. 현재는 postman이 아닌 Apidog이란 다른 API Tool을 사용하지만 기본 프로세스는 동일하기에 큰 수정없이 쉽게 마이그레이션까지 완료하였기에 다른 API Tool을 사용한다하더라도 쉽게 적용 가능할 것입니다.