Elixir 프로그래밍

Elixir Phoenix : Router와 Controller

BitzFlex 2019. 10. 8. 01:21

Router는 request URI를 어떤 모듈의 어떤 함수가 처리할지를 지정하는 모듈입니다.

 

router 모듈내에는 복수개의 pipeline을 둘수 있으며, pipeline은 http request를 받았을 때 해야하는 일련의 일들의 모임이라 생각할 수 있습니다. 

 

위의 소스코드에서는 두개의 pineline이 있으며, :browser와 :api로  각각 html request와 json api 를 처리하는 경우에 대한 정의라고 생각하면 됩니다.  이에 대한 자세한 설명은  https://hexdocs.pm/phoenix/routing.html#pipelines을 참조하면 되며, 일반적인 경우 이에 대한 기능 추가는 하지 않아도 됩니다. 

 

위의 코드에서 중요한 부분은 

 

  scope "/"HelloWebWeb do

    pipe_through :browser

 

    get "/"PageController, :index 

    get "/hello" , PageController, :hello

    get "/now" , PageController, :now

    get "/calc" , PageController, :calc

  end

 

부분입니다. 이는  어떤 URI 에 대해 어떤 method 요청을 , 어떤 모듈의 어떤 함수가 처리할지를 지정합니다.

위의 예에서, 

 

http의 GET method로 /hello URI 가 요청되는 경우, PageController의 :hello 함수를 호출하도록 지정한 것 입니다. 

동일 PC에서 실행하는 경우에는 브라우저의 http://localhost:4000/hello 를 입력하면, PageController 모듈의 :hello 함수가 호출되고

:hello 함수의 결과값이 브라우저에 response로 전송됩니다. 

 

위의 코드에서는 /hello, /now, /calc 에 대해 처리할 함수를 지정하였습니다. 

 

다음은 PageController.ex의 소스코드 입니다. 

 

코드에는 위의 router에서 지정한 함수들이 있습니다. 

 

각 함수들은   

  def hello(conn,_paramdo

    html conn, """

    <HTML>

    <HEAD>

    <TITLE> Hello !! </TITLE> </HEAD>

    <BODY>

        <h1> Hello Phoenix Web </h1>

    </BODY>

    </HTML>  

    """

  end

와 같이. conn 과 param을 인자로 받습니다.

 

인자 conn은  http의 request에 대한 정보와 response 정보를 가지는 map 구조체 입니다.

conn에는 request 정보로, 호출된 method 와 상대의 ip 주소, header 정보등 http request의 모든 정보를 가지고 있습니다.

 

controller 함수는 이 conn에 response 정보를 저장하여 반환하면, 그 결과가 클라이언트(브라우저)측에 전송되게 됩니다.

즉, controller 함수의 결과값은 항상 conn 이여야 합니다.

 

 

위의 코드에서 html conn, """ ... """ 은,   Phoenix.Controller.html(conn, """...""") 함수를 호출하는 것 입니다.

Phoenix.Controller.html(conn, """...""") 를 html conn, """...""" 으로 호출할 수 있는 것은, 소스코드의 위에 있는 

useHelloWebWeb, :controller이 있기 때문입니다. (자세한 내용은 본 글에서는 생략하며, 이의 내용을 몰라도 개발에 문제가 없습니다.)

 

Phoenix.Controller.html 함수는 두번째 파라미터로 주어진 데이터를 html 화하여 첫번째 파라이터인 conn에 response 정보를 저장하고 conn을 return 합니다 (response의 content-type: text/html).  이런 흐름이기 때문에 위의 hello 함수는 html response 정보가 저장된 conn을 return 하고, 그 정보가 클라이언트에게 전송됩니다. 

 

conn은 Plug.Conn 타입이며, 이에 대한 상세한 정보는 

https://hexdocs.pm/plug/Plug.Conn.htm

https://devhints.io/phoenix-conn를 참조하세요. 

 

 

아래의 함수는  URI /now 에 대한 처리 함수로, 현재 시간을 보여주는 HTML을 응답으로 전송합니다. 

  def now(conn,_paramdo

    now = :calendar.local_time() |> NaiveDateTime.from_erl! |> NaiveDateTime.to_string

 

    html conn, """

    <HTML>

    <HEAD>

    <TITLE> Now </TITLE> </HEAD>

    <BODY>

        <h1> #{now} </h1>

    </BODY>

    </HTML>  

    """

  end

 

위의 hello 와 거의 비슷하지만,  현재 시간을 문자열로 만들고 이를 HTML 문자열내에 string interpolation으로 표현한 것 입니다.

 

이와 같이 string interpolation 을 통하여 HTML에 변수의 값이나 DB의 쿼리 결과값등을 넣을수 있지만, 이를 좀더 편하고 일반화한것이 Template와 View 모듈입니다. (이에 대한 설명은 다음글에 적겠습니다.)

 

 

 

세번째 함수, calc는 두수를 URI의 인자로 받아서 그 결과값을 JSON 타입(content-type: application/json)으로 반환하는 Rest API함수를 구현한 것입니다.

 

  def calc(conn,%{"a" => valueA , "b" => valueB}) do

    sumValue = String.to_integer(valueA) + String.to_integer(valueB)

    json conn, %{result: :ok, sum: sumValue}       

  end

 

  def calc(conn,_paramdo

    json conn, %{result: :error}    

  end

 

위의 함수는 http://localhost:4000/calc?a=1&b=2 와 같이 호출하면되며, 

URI  인자 a=1&b=2 는 calc 함수의 두번째 인자인 param에 map의 형태로 전달됩니다. 

 

함수 calc(conn,%{"a" => valueA , "b" => valueB})  에서는 

인자로 주어진 두값을 pattern matching으로 가져와서 , 두수의 합을 계산한후 그 결과를 JSON으로 반환합니다.

map에 주어진 인자값은 문자열이므로, 두수의 계산을 수행하기 전에 정수형으로 변환한 후 계산을 수행하도록 구현되어 있습니다.

 

Phoenix.Controller.html(conn, """...""") 이 HTML 결과를 반환하는 것에 반해, 

Phoenix.Controller.json(conn,  json_data) 는 결과값을 JSON type으로 반환 합니다. 

 

와 같이, 결과의 content-type이 application/json으로 되어 있는 것을 확인할 수 있습니다.

 

json 함수는 두번째 인자로 JSON으로 변환될 수 있는 모든 자료형을 줄수 있습니다. 이는 List, Map 등이며, JSON 으로 변환될 수 있는 자료형이면, 내부에서 자동으로 JSON 형식으로 변환합니다. 

 

 

같은 이름의 함수, calc 

  def calc(conn,%{"a" => valueA , "b" => valueB}) do

  def calc(conn,_paramdo

가 두개 있는 것은,  URI 인자로 a,b가 주어지지 않은 경우에 대한 처리 목적입니다.

 

http://localhost:4000/calc?a=1&b=2 또는 http://localhost:4000/calc?a=1&b=2&c=3 와 같이 

a와 b 가 주어진 경우 (또는 그 외의 값이 주어진 경우도 포함)에는 pattern matching  %{"a" => valueA , "b" => valueB} 에 맞으므로

함수 calc(conn,%{"a" => valueA , "b" => valueB})가 호출되며, 

 

http://localhost:4000/calc 또는 http://localhost:4000/calc?a=1  또는 http://localhost:4000/calc?b=2

와 같이  a와 b가 모두 주어지지 않는 모든 경우에 대해서는  calc(conn,_param)가 호출됩니다. 

 

이와 같은 방식으로, pattern matching으로  Rest API 구현시 함수의 인자 체크를 쉽게 할 수 있습니다.

 

전체 코드는 https://github.com/BitzFlex/hello_web.git에서 받으실 수 있습니다.