Skip to content

Instantly share code, notes, and snippets.

@guersam
Last active February 26, 2019 12:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save guersam/8817ab3b27c554611c1c91e296bb1e75 to your computer and use it in GitHub Desktop.
Save guersam/8817ab3b27c554611c1c91e296bb1e75 to your computer and use it in GitHub Desktop.
http4s 서버에서 JSON 응답을 돌려주기까지

http4s에서 어떻게 IO#unsafeRunSync 없이 JSON 응답을 반환할 수 있는지 문의가 들어와서 보충설명 드립니다:

https://github.com/guersam/kcd2019/blob/v0.1.0/src/main/scala/com/guersam/kcd2019/web/ApiRoutes.scala#L28-L33

슬라이드에서 잠깐 보여드렸던 것처럼 http4s 서버는 Request[F] => F[Response[F]] 형태의 순수 함수입니다. 미들웨어나 루트 합성을 위해 KleisliOptionT같은 데이터 타입들이 추가로 쓰이기는 하지만 결국 Request에서 Response로 가는 순수 함수이고, 중간에 데이터베이스 연결 같은 이펙트가 발생할 수 있기 때문에 Request => Response 대신 Request => F[Response]와 같은 형태를 띄고 있다고 생각하시면 돼요. 아래같은 모듈이라고 생각하셔도 되겠네요.

trait HttpServer[F[_]] {
  def handleRequest(req: Request): F[Response]
}

이 DSL 레이어는 TCP 소켓같은 저수준 관심사와 분리되어 있기 때문에 Request 값을 직접 만들어서 넘겨줌으로써 서버를 실제로 띄우지 않고도 서버 로직을 테스트할 수 있습니다. https://http4s.org/v0.20/testing/

한편 임의의 데이터 타입 AResponse[F] 형태로 변환하는 데는 EntityEncoder[F, A]가 필요합니다. 보여드린 예제에서는 http4s-circe 모듈이 제공하는 jsonEncoderOf 함수를 통해 io.circe.Encoder[A]에서 org.http4s.EntityEncoder[F, A]를 유도할 수 있었어요. https://http4s.org/v0.20/entity/

좀더 구체적으로 들어가면 org.http4s.impl.EntityResponseGenerator 헬퍼가 암시적인 EntityEncoder[F, A]클래스를 사용해 A, F[A] 등을 F[Response[F]]로 바꿔주는 역할을 하고 trait Http4sDsl에 포함된 trait Responses[F[_]]가 각 상태 코드 객체에 이런 문법 확장을 입혀 줍니다.

요약하면 http4s 서버는 Request[F] => F[Response[F]]의 순수 함수 형태이기 때문에 HTTP 응답이 이펙트 F[_] 안에만 들어있으면 되고, 실제로 이 것이 실행되는 시점은 서버를 마운트하는 등의 구체적 행동이 일어난 시점 이후로 미뤄진다고 보시면 되겠습니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment