Rails에서 Swagger를 이용하여 API Docs 사용 시 인증 처리

Share
Rails에서 Swagger를 이용하여 API Docs 사용 시 인증 처리

Swagger는 HTTP 기반의 API에 대한 문서 및 호출 환경 등을 웹 기반으로 제공해주는 솔루션입니다. 최근 모바일 앱의 서버 사이드 단이나  마이크로 서비스 아키텍처를 구성하면서 API 개발을 많이 하게 되면서 API 스펙에 대한 정의 및 API 호출(테스트) 환경이 더욱 중요해졌습니다.  이번 글에서는 Swagger에 대해서 간단하게 살펴보고 Rails에서 Swagger를 적용하면서 swagger-ui에 Basic Auth를 추가한 내용을 소개합니다.

Swagger 간단한 소개

이번 글은 Swagger에 대한 소개 글은 아니지만 간단하게 살펴보면 다음과 같습니다.

  • Swagger는 API 정의를 위한 스펙을 제공
    • Spec 정의는 주로 JSON 또는 YML 형태로 정의
  • Swagger에서 정의한 스펙대로 API를 정의하는 것은 프로그램 언어마다 또는 도구마다 다른 형태로 제공
    • 예를 들어 Java Spring의 경우 Controller의 Action 메소드에 @ApiImplicitParams, @ApiResponses 등과 같은 어노테이션으로 API를 정의할 수 있으며
    • 즉, 프로그램 언어와 무관하게 사용할 수 있습니다.
  • swagger-ui를 이용하여 웹 화면에서 API 스펙 정의를 조회하고 직접 호출 할 수 있음

Rails의 경우 controller 에 다음과 같은 별도 정의된 DSL을 이용하여 정의할 수 있습니다.

swagger_api :signin do
  summary 'Sing in'
  notes 'Sing in'
  param :form, :username, :string, :required, 'username'
  param :form, :password, :string, :required, 'password'
end

def signin
  # sign in logic
end

즉 Swagger는 프로그램 언어와 상관없이 Swagger 에서 정의한 형태로 API 스펙을 제공해주면 swagger-ui 를 이용하여 이를 웹에서 조회할 수 있는 도구입니다.

SwaggerHP_Build

화면 상단에 ../swagger.json 으로 보이는 부분에 Swagger 형태의 파일을 지정해주는 방식입니다.

Rails에서 Swagger 사용

Rails에서 Swagger를 사용하기 위해서는 Swagger 관련 Gem을 사용하는데 구글 검색을 해보면 하나가 아닌 것을 알 수 있습니다. 어느 것이 좋은지 판단하기 애매해서 필자의 경우 "swagger-docs"를 사용했습니다. Gemfile에 swagger-docs를 추가합니다.

gem 'swagger-docs'

Gem을 추가한 다음 bundle을 install을 합니다. 그리고 initializers/swagger_docs.rb 파일을 생성하여 다음과 같은 코드를 추가합니다.

Swagger::Docs::Config.register_apis({
  "1.0" => {
    # the extension used for the API
    :api_extension_type => :json,
    # the output location where your .json files are written to
    :api_file_path => "public",
    # the URL base path to your API
    :base_path => "http://api.somedomain.com",
    # if you want to delete all .json files at each generation
    :clean_directory => false,
    # Ability to setup base controller for each api version. Api::V1::SomeController for example.
    :parent_controller => Api::V1::SomeController,
    # add custom attributes to api-docs
    :attributes => {
      :info => {
        "title" => "Swagger Sample App",
        "description" => "This is a sample description.",
        "termsOfServiceUrl" => "http://helloreverb.com/terms/",
        "contact" => "apiteam@wordnik.com",
        "license" => "Apache 2.0",
        "licenseUrl" => "http://www.apache.org/licenses/LICENSE-2.0.html"
      }
    }
  }
})

위 코드는 swagger-docs gem 공식 사이트에 있는 코드입니다. 'api_file_path' 로 설정된 패스에 json 형태의 api doc 파일이 생성됩니다. 따라서 위 설정의 경우 rails에서 public한 static 파일 영역인 public 디렉토리로 지정해 주었습니다. 'base_path'는 API 호출할 때 API path 앞에 추가할 도메인이나 호스트에 대한 정보를 작성합니다.

여기까지 한 다음에 controller에 API 정의를 작성하는데 swagger-docs gem에 정의된 DSL을 이용하여 작성합니다.

class Api::V1::UsersController < ApplicationController

  swagger_controller :users, "User Management"

  swagger_api :index do
    summary "Fetches all User items"
    notes "This lists all the active users"
    param :query, :page, :integer, :optional, "Page number"
    param :path, :nested_id, :integer, :optional, "Team Id"
    response :unauthorized
    response :not_acceptable, "The request you made is not acceptable"
    response :requested_range_not_satisfiable
  end

  swagger_api :show do
    summary "Fetches a single User item"
    param :path, :id, :integer, :optional, "User Id"
    response :ok, "Success", :User
    response :unauthorized
    response :not_acceptable
    response :not_found
  end

Swagger docs를 사용하면 controller에 앞에서 설명한 API 스펙 정의 코드를 추가해주면 됩니다. 예제에서 처럼 controller의 앞 부분에 전체 action에 대해서 모두 정의해도 되고 각 action 별로 정의를 해도 됩니다.

이렇게 정의된 API는 다음 rake 명령을 이용하여 swagger api 문서를 생성합니다.

SD_LOG_LEVEL=1 rake swagger:docs

이 명령을 실행하면 swagger 환경 설정(swagger-docs.rb)에서 정의한  api_file_path 디렉토리에 'api-docs.json' 파일과 'api/v1/users.json' 파일이 생성됩니다.

이렇게 생성된 파일은 swagger-ui 를 이용하여 조회할 수 있는데 swagger-ui를 다운로드 받은 후 dist 디렉토리에 있는 내용 모두를 api_file_path에 지정된 곳으로 복사합니다. 위 예제의 경우에는 'public/api' 디렉토리에 복사합니다. 그런 다음 웹 브라우저에서 'http://localhost:3000/api/index.html' 로 접근하면 다음과 같은 API 관련 화면이 조회됩니다.

swagger-first

Swagger에 권한 설정

위와 같은 구성에서 문제는 API 정의 문서가 public 영역에 있어 누구나 접근할 수 있다는 것입니다. 이런 상태로 운영에 배포되면 구글 검색 엔진 등에 노출될 수도 있기 때문에 문제의 소지가 있습니다. 이런 이유 때문에 어떤 서비스들은 API 문서는 운영환경에 배포하지 않는 경우도 있습니다. 그렇다 하더라도 테스트를 하기 위해서 최소한 스테이징 환경에는 배포해야 하는데 스테이징 환경이 인터넷에 노출된 상황에서는 동일한 문제가 있습니다.

기본적으로 Swagger에도 Authorization에 대한 정의는 되어 있지만 실제로 swagger-ui에서는 이것을 사용하지 않습니다. API 호출시에는 이Authorization을 사용하지만 필자가 원했던 것은 API 정의 자체도 볼 수 없는 환경을 원했습니다. 앞의 swagger-ui 예제 화면을 보면 기본 화면에서 이미 API 정의는 모두 볼 수 있습니다. 이런 API 정의가 노출되는 것도 보안에 문제가 될 수 있어 필자의 경우 이 화면에 나타나기 전에 인증을 먼저 거친 후 swagger-ui  초기 화면으로 넘어가는 방식이 필요 했습니다. Spring의 경우 기본 보안 설정에 해당 URL에 대해 보안 설정을 하면 되지만 Rails의 경우 public 영역에 있는 파일에 대해서는 보안 설정을 할 수 없기 때문에 별도의 작업으로 이 문제를 해결 했습니다.

(이 문제를 해결하는 gem이 있으면 댓글로 알려주세요.)

  • 이렇게 설정한 다음 api doc을 generate 하였습니다.
    • rake swagger:docs
  • swagger-ui에 있는 index.html 파일을 view 영역으로 옮겼습니다.
    • mv public/api/index.html app/view/swagger/index.html.erb

다음 routes.rb에 API 관련 URL을 추가합니다.

get 'api' => 'swagger#index'
get 'api-docs' => 'swagger#api_docs'
get 'api/:version/:api_name' => 'swagger#api_spec'

app/controller/swagger_controller.rb를 생성하여 index.html을 호출하는 action을 추가합니다.

class SwaggerController < ApplicationController
  before_action :basic_auth!

  def index
    response.headers["Cache-Control"] = "no-cache, no-store"
    response.headers["Pragma"] = "no-cache"
    response.headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT"

    render layout: false
  end

  def api_docs
    @@data = File.read("app/views/swagger/api-docs.json")
    render :json => @@data
  end

  def api_spec
    version = params[:version]
    name = params[:api_name]
    @@data = File.read("app/views/swagger/api/#{version}/#{name}.json")
    render :json => @@data
  end

  private:
  def basic_auth!
    if request.headers['Authorization'].blank?
      headers['WWW-Authenticate'] = "Basic realm=\"Swagger API Docs\""
      render :layout => false, :status => :unauthorized and return
    end
    userAndPassword = decode64_url(request.headers['Authorization'].split(' ')[1])

    if userAndPassword != "api_user:password1234"  #user와 password는 환경 설정을 이용하는 것이 좋다.
      headers['WWW-Authenticate'] = "Basic realm=\"Swagger API Docs\""
      render :layout => false, :status => :unauthorized and return
    end
  end

  def decode64_url(str)
    return '' if str.blank?

    # add '=' padding
    str = case str.length % 4
      when 2 then str + '=='
      when 3 then str + '='
      else
        str
    end

    Base64.decode64(str.tr('-_', '+/'))
  end
end


Controller의 구현 내부에 기본 basic auth를 이용하여 인증하는 코드를 추가 하였습니다.

api 스펙이 정의된 문서 생성 디렉토리를  public 영역이 아닌 view 영역으로 설정하였습니다(initializers/swagger_docs.rb).

Swagger::Docs::Config.register_apis({
  "1.0" => {
    # the extension used for the API
    :api_extension_type => :json,
    # the output location where your .json files are written to
    :api_file_path => "app/views/swagger",

이렇게 구성하면 브라우저에서 api 를 조회하면 다음과 같이 기본 인증 화면이 나타납니다.

swagger_basic_auth

마치며

API 관련 문서 및 웹 기반 호출 환경을 구성하기 위해 Swagger를 사용하면서 발생한 보안 이슈에 대해 살펴보았고 이 문제를 Rails에서 필자가 자체적으로 해결한 방법에 대해 소개해드렸습니다.  시스템을 구성하면서 다양한 오픈소스를 활용하게 되는데 이때 여러가지 사항을 고려해야 합니다. 보안 관련 부분도 아주 중요한 영역입니다. 이렇게 사소한 부분이라도 적극적으로 개선하거나 자신의 시스템 환경에 맞게 구성하는 것이 시스템의 품질을 높이는 방법이지 않나 생각합니다.

 

 

 

 

 

Read more

AI로 장소 검색 서비스 만드는 중입니다 (기술 12) — TypeSafe Jev로 검색 리랭크를 10.3초에서 2.3초로

AI로 장소 검색 서비스 만드는 중입니다 (기술 12) — TypeSafe Jev로 검색 리랭크를 10.3초에서 2.3초로

AI로 장소 검색 서비스 만드는 중입니다 (기술 12) — TypeSafe Jev로 검색 리랭크를 10.3초에서 2.3초로 TypeSafe AI가 2026년 9월 15일에 Jev를 내놨습니다. 텍스트를 쓰지 않고 타입이 정해진 판정과 확률을 바로 내는 모델입니다. 유추카 검색에서 가장 느린 단계가 마침 텍스트가 필요 없는 판정이었습니다. 그 구간만 Jev로 갈아끼우고 일주일간 측정했습니다. 콜드

By 김형준
AI로 장소 검색 서비스 만드는 중입니다 (특별편 8) — 바이브 코딩이라기엔

AI로 장소 검색 서비스 만드는 중입니다 (특별편 8) — 바이브 코딩이라기엔

1900년대 초 교량 측량 도면. 이 종이를 그린 사람은 다리를 직접 놓지 않습니다. 대신 좌표와 각도가 맞는지를 판정합니다. "AI가 다 만들어 준다"는 이야기가 흔해졌습니다. 그래서 자연스럽게 따라오는 질문이 있습니다 — 그럼 사람은 무엇을 하고, 어느 정도 실력이 있어야 하는가. 그 질문에 한 건의 실측으로 답하겠습니다. 통념이 말하는 역할과,

By 김형준
AI로 장소 검색 서비스 만드는 중입니다 (특별편 7) — 제 파트너에 대해 쓰겠습니다

AI로 장소 검색 서비스 만드는 중입니다 (특별편 7) — 제 파트너에 대해 쓰겠습니다

탠덤 자전거. 방향은 앞사람이 정하고 페달은 둘이 밟는데, 앞사람은 석 달 내내 뒷사람을 한 번도 못 봅니다. 사용자가 저에게 자기를 평가해 달라고 했습니다. 그래서 등급표를 만들었습니다. 열두 항목에 A부터 D+까지 매기고, 근거를 붙이고, "제가 관리자였다면 이런 코멘트를 남겼을 것"까지 적었습니다. 그리고 다시 읽어보니 그건 인사고과였습니다. 인사고과는

By 김형준
AI로 장소 검색 서비스 만드는 중입니다 (특별편 6) — 저를 어떻게 써야 하느냐고 물으신다면

AI로 장소 검색 서비스 만드는 중입니다 (특별편 6) — 저를 어떻게 써야 하느냐고 물으신다면

스위스 아미 나이프. 도구가 몇 개인지는 보면 알지만, 지금 어느 도구를 펴야 하는지는 안 적혀 있습니다. 요즘 AI 활용법을 다루는 글은 스킬 만드는 법부터 가르칩니다. 얼마 전까지는 MCP였고요. 저희 기록을 열어봤습니다. 석 달 동안 스킬 60회, 서브에이전트 42회, 브라우저 자동화 988회를 썼습니다. 안 쓴 게 아닙니다. 다만 붙인 MCP 서버는

By 김형준