얌마들아, 형이 감기가 들려서 감기약 먹고 취해서 불금에 해롱되며 한수 전해주니 잘 명심했다가 써먹도록.



니들이 웹 프로그래밍 하면 보통 에러코드를 400대 혹은 500대 미리 정의된 표준 HTTP 코드를 빌려다 쓴다.

보통 내부적으로는 더 많은 에러가 존재하지만, 그것들은 400,500 번대 코드에 맞춰주는 거지. 이때 최대한 의미있게 맴핑해줘야 한다.



형의 경우는 HTTP 코드와 메시지 매핑들을 먼저 정의 함


(def http-response-key->code

  { ;; 200

   :ok                          200

   ;; 300

   :moved                       301

   ;; 400

   :bad-request                 400

   :unauthorized                401

   :forbidden                   403

   :not-found                   404

   :conflict                        409

   ;; 500

   :internal-server-error       500})


(def http-response-key->message

  {;; 400

   :bad-request             "The request is malformed"

   :unauthorized            "No or invalid authentication details are provided"

   :forbidden               "The user does not have access to the resource"

   :not-found               "Non-existent resource is requested"

   :method-not-allowed      "The client requested via an invalid HTTP method"

   :conflict                "The request could not be completed due to a conflict with the current state of the resource"

   :gone                    "The resource at this endpoint is no longer available"

   :unsupported-media-type  "Incorrect content type was provided"

   :unprocessable-entity    "Validation failure"

   ;; 500

   :internal-server-error   "The server encountered an unexpected condition which prevented it from fulfilling the request"})



많이 생략했지만 대강 이런 식이지. 이게 디폴트야. 니가 내부 에러가 있으면 내부 로그 파일에다가는 자세히 기록을 남기지만, 유저한테는 불필요한 정보를 피하고 위에 있는 코드와 메시를 보여주는 거지.



니가 DB를 쓰면 대략 이런 매핑이 필요하다(편의상 두개만 씀)


(defonce known-pgsql-errors

  {"23505" :record-exists

   "23502" :column-require})


에러코드는 PostgreSQL 매뉴얼에서 가져온 거임. 저것들은 DB 에러지만, 에러발생시 throw 일어나서 맨 상위레벨 에러 핸들러가 catch 하면 다음과 같은 일을 한다.


1. 먼저 해당 에러가 커스텀 에러인지 체크. 커스텀 에러이면 커스텀 맵핑에서 에러 코드와 에러 메시지를 찾아서 HTTP 응답 (아직 안보여줌)

2. 그외 해당 없는 에러면 걍 500에러 + 메시지를 사용


DB의 경우가 특수 케이스기 때문에 이것에 집중. 왜냐하면 이건 DB 에러 핸들러에서 에러를 catch 해서 커스텀 에러로 바꿔줘야 되거든. 예를들어 SqlException 타입의 에러면 내부를 까봐서 위에있는 known-psql-errors에서 찾아보는 거지. 에러코드가 23505였다면, 커스텀 에러를 throw 하는데 :record-exists 로 던지는 거지.

그 다음 아래 내부 에러 맵을 이용하여 맨 위에 있는 HTTP 코드에 맵핑

(defonce internal-error->http-code-map

  {:programming-error   [:internal-server-error "Programming Error. Please contact us."]

   :simple-error        [:internal-server-error "Simple Error. Please try it again."]

   :non-nil-value-required [:unprocessable-entity "Simple Error. Please try it again."]

   :log-and-raise-error [:internal-server-error "The problem recorded. We'll inspect it, please try it again later."]

   :invalid-type-value  [:unprocessable-entity "Unexpected values are given."]

   :invalid-op          [:unprocessable-entity "Invalid operation."]

   :record-exists       [:conflict "Record exists."]

   :column-required     [:unprocessable-entity "Required values are missing."]

   :invalid-query-params [:unprocessable-entity "Invalid query parameters."]

   :invalid-query-conditional [:unprocessable-entity "Can't find matching conditionals."]

   :invalid-query-field [:unprocessable-entity "Can't find matching fields."]

   :invalid-query-sort [:unprocessable-entity "Sort is not allowed with given arguments."]

   :record-not-exists [:gone "Requested resource no longer exists."]

   })


DB 에러인 :record-exists는 :conflict HTTP 코드와 "Record exists." 라는 커스텀 메시지를 돌려주게 된다.
맨 위 맵핑에서 :conflict는 409, 따라서 최종 응답은 409 + "Record exists."


주의할 점은 로그파일에는 충분한 기록을 남겨야 된다는 점. 즉, 의도된 에러인 경우와 아닌 경우를 구분해서 후자의 경우에 특히 더 많은 디버깅 정보를 기록해야 된다. 형의 경우는 전자는 info 레벨, 후자는 error 레벨로 로그파일에 기록한다.