본문 바로가기

배움의 즐거움/프로그래밍

(33) Graphql-ruby - 에러 핸들링

반응형




Errors in GraphQL


GraphQL 에는 다양한 종류의 에러가 있다. 이번 포스팅에서는 주요 에러들과 언제 이를 적용해야 되는지에 대해서 알아볼 것이다.


Validation Errors


GraphQL 은 타입 언어이기 때문에 쿼리를 실행하기 전에 모든 쿼리에 대해서 유효성 검사를 실행한다. 만약에 요청된 쿼리가 유효하지 않다면 이는 실행되지 않을 것이다. 대신 응답은 에러와 함께 되돌려보내질 것이다.

{
  "errors" => [ ... ]
}

각 에러는 message, line, column 그리고 path 를 가지고 있다.

유효성 검사 규칙은 GraphQL 스펙의 일부분이며 GraphQL-Ruby 에 내장되어 있기에 쿼리를 수행할 때 모든 유효성 검사를 스킵하도록 validate: false 를 전달하는 방법 외에는 이 행동에 대해 별도로 커스터마이징 할 수 있는 방법이 없다. 


Analysis Errors


GraphQL-Ruby 는 실행전 분석을 지원하는데, 이 것은 쿼리를 실행하는 것 대신에 "errors"를 리턴할 수 있다. 더 자세한 사항은 Analysis guide 에서 확인할 수 있다.


GraphQL Invariants


GraphQL-Ruby 가 쿼리를 실행하는 동안, 만족되어야 할 몇 개의 제한들이 있다. 예를 들어:

  • Non-null 필드는 nil을 리턴하지 말아야 한다.
  • 인터페이스와 유니언 타입은 반드시 각 타입에 알맞게 객체를 rsesolve 해야 한다.

이러한 제한들은 GraphQL 스펙의 일부분이고, 이러한 것들이 위반될 시에는 어떻게든 명시되어야 한다. 더 자세한 사항은 Type Errors 에서.



Top-level "errors"


GraphQL 스펙은 쿼리를 실행하는 동안 발생한 에러 정보를 담을 수 잇는 top-level "errors" 키를 제공한다. "errors" 와 "data" 는 부분적인 성공이 있을 시에는 둘 다 존재 할 수도 있다.

너의 스키마에서 GraphQL::ExecutionError (또는 이 것의 서브클래스)를 발생시키므로써 "errors" 키를 추가할 수 있다. 더 자세한 내용은 Execution Errors guide 에서


Unhandled Errors (Crashes)


발생한 에러가 rescue 되지 않을 때, GraphQL 쿼리는 모두 크래쉬 하며 레일즈 컨트롤러와 같이 이를 둘러싼 코드가 예외를 반드시 핸들링 해야 한다. 예를 들어 레일즈는 아마 일반적인 500  에러를 리턴할 것이다.


Errors as Data


만약 사용자가 에러 메시지를 읽기를 원한다면 GraphQL 필드와 타입을 이용하여, 에러를 스키마에 표현해 줄 수 있다. 이러한 접근 방법에서, 에러는 반드시 타입이 정의된 데이터여야 하며 다른 데이터처럼 스키마에서 queryable 이어야 한다.

더 보고싶다면 Mutation Errors 확인


Top-level "errors"


GraphQL은 응답에 top-level "errors" key  를 허용하는데 이는 쿼리의 실행 중 어떤 부분에서 잘못되었는지에 관한 정보를 포함한다. 예를 들어,

{
  "errors" => [ ... ]
}


일부만 성공했을 경우에 응답은 "data" 와 "errors" 를 둘다 가지고 있을 수 있다.

{
  "data" => { ... } # 성공한 일부 쿼리
  "errors" => [ ... ] # 일부 쿼리가 실행되는 것을 막은 부분에 대한 에러
}


When to Use Top-Level Errors


일반적으로 top-level error 은 개발자가 시스템이 문제가 있다고 인식해야 하는 예외적인 상황에서만 쓰여야 한다. 

예를 들어 GraphQL 은 non-null 필드가 nil 을 리턴할 때 에러가 반드시 "errors" 키에 추가되어야 한다고 말한다. 이러한 경우는 클라이언트가 이를 복구할 수 없다. 대신 서버 측에서 해당 케이스에 대한 수정이 필요하다.

만약 클라이언트가 수정할 수 있는 에러가 있음을 알리고 싶다면 mutation errors 를 이용하여 스키마의 일부분으로 에러를 만드는 것을 고려해라.


Adding Errors to the Array


GraphQL-Ruby 에서 GraphQL::ExecutionError (or a subclass of it) 를 발생시키므로써 에러를 추가할 수 있다. 예를 들어,

raise GraphQL::ExecutionError, "Can't continue with this query"


에러가 발생할 때, 이 에러의 message "errors" 키에  추가되고 GraphQL-Ruby 는 여기에 자동적으로 linecolumnpath 을 추가하게 된다. 즉, 에러는 아래와 같이 생길 것이다.

{
  "errors" => [
    {
      "message" => "Can't continue with this query",
      "locations" => [
        {
          "line" => 2,
          "column" => 10,
        }
      ],
      "path" => ["user", "login"],
    }
  ]
}


Customizing Error JSON


기본적인 에러 JSON 은 "message""locations" , "path" 를 포함하고 있다. 새로운 GraphQL 버전은 커스텀 데이터를 에러JSON 의 "extensions" 키에 넣을 것을 권장한다.

이 것을 두 가지 방법으로 커스터마이징 할 수 있다.

    • 에러를 발생시킬 때 extension 를 전달하라. 예를 들어 

      raise GraphQL::ExecutionError.new("Something went wrong", extensions: { "code" => "BROKEN" })
      

        이 경우,  "extensions" => { "code" => "BROKEN" } 은 error JSON 에 추가될 것이다.

       
    • GraphQL::ExecutionError 의 서브클래스 내에 있는 #to_h 를 오버라이드 해라. 예를 들어

      class ServiceUnavailableError < GraphQL::ExecutionError
        def to_h
          super.merge({ "extensions" => {"code" => "SERVICE_UNAVAILABLE"} })
        end
      end
      

       이제, "extensions" => { "code" => "SERVICE_UNAVAILABLE" } 는 error JSON 에 추가될 것이다.

    Type Errors


    GraphQL 사양에서는 쿼리를 실행할 때 특정 가정을 사실이라고 가정한다. 그러나 어떤 코드가 이 가정을 위반하는 상황도 있을 수 있는데 이럴 때 타입 에러가 발생한다.

    여기 GraphQL-Ruby 에서 커스터마이징 할 수 있는 두 가지 타입 에러가 있다.

    • null: false 를 가진 필드가 nil 리턴
    • 필드가 유니언 또는 인터페이스를 값으로써 리턴하였지만 해당 값이 유니언,인터페이스의 멤버로 resolve 될 수 없을 때


    이러한 케이스에 대한 행동은 Schema.type_error hook 를 정의함으로써 지정할 수 있다.

    class MySchema < GraphQL::Schema
      def self.type_error(err, query_ctx)
         # Handle a failed runtime type coercion
      end
    end
    

    이 것은 GraphQL::UnresolvedTypeError 또는 GraphQL::InvalidNullError 의 인스턴스와 쿼리 context(GraphQL::Query::Context) 를 호출한다.


    만약 hook 를 지정하지 않았다면, 기본적인 설정이 적용될 것이다.

    • 예상하지 못한 nil 값은 응답의 "error:" 키에 에러를 추가할 것이다.

    • 해결되지 않은 Union / Interface 타입은 GraphQL::UnresolvedTypeError 에러를 발생시킬 것이다.

    타입 resolution 에 실패한 객체는 nil 로 취급될 것이다.


    * 해당 글은 번역기 돌리다가 크롬 번역기 말도 안되는 해석에 지친 본인이 나중에 참고할 의도로 대충대충 발로 해석한 것이니 참고용으로만 사용하시길 바랍니다.

    * 출처: http://graphql-ruby.org/errors/overview.html

    * 출처: http://graphql-ruby.org/errors/execution_errors.html

    * 출처: http://graphql-ruby.org/errors/type_errors.html


    반응형