ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Elixir Phoenix : View와 Template
    Elixir 프로그래밍 2019. 10. 13. 14:22

    Elixir Phoenix는 MVC 모델을 따릅니다. 

    이전의 글에서 Controller에 대한 내용을 적었고, 이번글에서는 View에 대한 내용입니다. Model에 해당하는 Ecto는 다음글에 적겠습니다.

     

    이전글에서 Controller는 Router의 설정에 따라 각각의 URI에 대한 어떤 처리를 수행하고, 그 결과인 HTML이나 JSON을 반환하였습니다.

    JSON인 경우, Map이나 List 등의 Elixir의 데이터 구조체를 주면 자동으로 JSON으로 변환되어 코드가 지저분해지거나 그러진 않습니다. 

     

    하지만, 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

     

    이 방식으로 아주 복잡한 HTML도 생성하여 반환하는 것이 가능하기는 하지만, 

    Controller에서 직접 HTML 생성을 하게 되면 코드 관리 측면에서 좋지 않습니다.

    Controller의 소스코드가 길어지기도 하며, 코드를 읽는 것도 불편해집니다. 

     

    이를 위해 만들어진것이 View와 Template 입니다.

     

    • Controller는 View의 render 함수를 호출하여, 결과 HTML 또는 JSON을 생성합니다. (JSON을 View와 Template를 이용하여 생성하는 것도 가능합니다. 
    • 하나의 Controller는 하나의 View를 가집니다.
    • 하나의 View는 복수개의 Template를 가집니다.  각각의 Template는 View 내의 개별 함수로 만들어지게 됩니다. (이런 구조이기 때문에 거의 대부분의 View는 아무런 코드가 없습니다.)
    • View, Controller, Template간의 연관은  Module 이름으로 결정이 됩니다. (별도의 설정이 없으며, 함수의 이름으로 결정이 됩니다.)

    위에 적었듯이, Controller, View, Template는 파일의 이름과 폴더의 이름으로 어떤 Controller용 View인지가 결정이 되며, Template는 View 내에 함수로 만들어지게되며, 이 만들어지는 함수의 이름이 Template의 파일이름입니다.  (이에 대한 설명은 아래에 좀더 상세히 적겠습니다.) 

     

    이에 대한 폴더 구조를 아래의 그림으로 표현하였습니다. 

    controller, view , template

    위의 그림에서와 같으 Elixir의 source file 이름은 "_"로 구분된 소문자를 사용하며, 모듈의 이름은 각 단어를 대문자로 시작하도록 합니다.

    page_controller.ex 소스파일에는 PageController 모듈을 구현합니다.

     

    위의 그림에서 contoller, view, template의 관계는 다음과 같습니다.

    • controllers folder에 "page_controller.ex (PageController Module)" 이 있으면, 
    • views folder에 "page_view.ex (PageView Module)이 있어야 합니다. 
    • page_view.ex 파일이 있으며, templates 폴더에 "page" sub folder가 있어야 하며, 이 폴더에 template file들이 위치 합니다.

     


    Controller에서 View 의 사용

     

    사실 Controller에서는 View Module의 render/3 함수를 호출하는 것이 다입니다.

    render함수에서는 반환될 HTML(또는 JSON)을 template를 이용하여 생성하여 반환하고, 이 반환값은 Controller에서 Plug.conn을 통하여 클라이언트(브라우저)에 reponse로 전달됩니다.

     

    이전의 router code에 

     

      scope "/"HelloWebWeb do

        pipe_through :browser

     

        get "/"PageController, :index

        get "/hello" , PageController, :hello

        get "/now" , PageController, :now

        get "/calc" , PageController, :calc

     

        get "/response1" , PageController, :response1  

      end

     

    와 같이 /response1"에 대한 처리 PageController, :response1 을 추가하고,

     

    PageController 모듈에 

      def response1(conn, _paramdo

        render(conn, "response1.html")

      end

     

    을 추가한후,   브라우저에서 http://localhost:4000/response1 을 입력하면, 

    다음과 같은 오류가 납니다.

     

    이는 PageController의 response1 함수에서 render(conn, "response1.html") 을 호출하면 , 

    이는 PageView.render("response1.html", param) 를 호출하며, 현재 PageView 모듈에 render("response1.html",  param) 함수가 정의되어 있지 않기 때문입니다.

     

    defmodule HelloWebWeb.PageView do

      use HelloWebWeb, :view

     

      def render("response1.html"_assignsdo

        "/Response1 의 응답입니다."

      end

    end

     

    와 같이 정의하면,  정상적인 실행이 됩니다.

     

    즉, PageController에서 render(conn, "response1.html")을 호출하면,  내부적으로 이는 PageController의 View인 PageView 모듈의 render("response1.html", assigns)를 호출하게 됩니다. 

     

    PageView module에는 pattern matching에 따라, 여러개의 render 함수를 가지고 있을 수 있으며, 첫번째 인자에 따라 호출될 render 함수가 결정됩니다.

     

    위에서 본것과 같이 View Module의 render 함수에서 response의 결과를 생성할수 있지만, 이는 Controller에서 Elixir 소스코드내에 반환할 문자열을 사용하는 것과 별로 차이가 없으며, 이런 식으로 사용되지는 않습니다. 

     

    이에 대한 방편으로 사용하는 것이 Template 입니다. 


    Template의 사용

     

    위의 그림에서와 같이 PageView에서 사용할 template는 폴더 templates/page 에 있어야 하며, 파일의 확장지는 "eex"를 사용합니다. 

    template/page 폴더에는 복수개의 파일을 둘수 있으며, 각각의 파일은 View 모듈의 render 함수를 만드는데 사용됩니다.

     

    위의 예에서는 

    PageView 모듈에  render("index.html", assigns) 함수가 생성됩니다. 

     

    즉, template 폴더에 있는 각각의 확장자 eex 파일은 View 모듈에 render 함수를 만드는데 사용되며

    각 render 함수는 첫번째 파라미터로 template 아래에 있는 파일의 이름중 "eex"를 제외한 파일 이름을 파라미터로 가지게 됩니다.

     

    index.html.eex ->  render("index.html", assigns)  함수가 만들어지게 됩니다. 

     

    Router에 

      scope "/"HelloWebWeb do

        pipe_through :browser

     

        get "/"PageController, :index

        get "/hello" , PageController, :hello

        get "/now" , PageController, :now

        get "/calc" , PageController, :calc



        get "/response1" , PageController, :response1

        get "/response2" , PageController, :response2

     

      end

    와 같이 /response2 에 대한 내용을 정의하고, 

     

    contoller PageController에

     

     

      def response2(conn, _paramdo

        render(conn, "response2.html")

      end

     

    와 같이 정의한 후,  브라우저에서, "http://localhost:4000/response2" 에 접속하면, 

    와 같은 오류화면이 나타납니다. 이는 PageView에 render("response2.html", assigns) 를 정의하던가, 

    폴더 templates/page 에 "response2.html.eex" 파일을 생성하고 그 파일에 결과를 나타낼 내용을 기술하라는 오류 입니다. 

     

    위의 response1 은 직접 render("response1.html",assigns)를 정의한 것이고,  

    이번 reponse2에는 아래와 같으 templates/page 폴더에 "response2.html.eex" 파일을 생성하겠습니다. 

    이와 같이 정의하면, 

     

    PageView Module에 render("response2.html",assigns) 함수가 생기고, 이 함수는 response2.html.eex 파일의 EEx evaluation(아래에 내용을 적겠습니다.)의 결과를 반환하게 됩니다. 

     


    ".eex"  , EEx (Embedded Elixir) 의 사용

     

    EEx는 문자열 내에 Elixir code를 반영하여 그 결과를 생성하는 일을 할수 있습니다. 

     

    이에 대한 설명은 :  https://elixirschool.com/ko/lessons/specifics/eex/ 을 참조하시기 바랍니다. 

     

    iex를 이용하여 EEx 를 실행해보면,, 

    와 같습니다.

     

    즉, EEx 에서는  문자열내에 "<%=  %>" 또는  "<%  %>" 사이에 있는 내용은 Elixir 소스 코드라 생각하고, 그 코드를 실행시킵니다.

     

    위의 예에서와 같이, 

    "<%=  %>"  와 "<%  %>" 의 차이는 "<%= %>"는 Elixir 코드를 실행시킨 결과를  문자열에 기입을 하고, "<%  %>"는 코드를 수행만 시키고,그 결과는 문자열에 기입하기 않는 것입니다.

     

    Elixir Phoenix 의 ".eex" 파일은 위의 EEx 에서 사용할 내용을 기술한 파일입니다.

     

    view의 render 함수가 호출되고, 해당 eex 파일의 내용이 있을 경우에는 eex 파일의 내용을 EEx 의 eval_file 함수를 이용하여 실행시키고 그 결과물을 HTML의 결과물로 반환합니다.

     

    이런식으로, html template 속에 elixir code를 실행시킬 수 있으므로,  HTML을 elixir code를 적용하여 간편하게 만들 수 있습니다.

     

    아래는 위의 EEx 기능을 적용하여, 위의 render(conn, "response2.html") 에 현재 시간을 출력하도록 template 파일 response2.html.eex의 내용을 변경한 내용입니다.

     

     

    <section class="phx-hero">

      <h1>Response 2</h1>

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

      <h1>현재 시간은 <%= now %> 입니다.</h1>

    </section>

     

     

    위에서 기술한 바와 같이 template 는 결국 view module 내에 하나의 함수로 생성되므로,  template .eex 파일내에서 view module에 정의된 함수들을 호출하는 것도 가능하며, 다른 template 파일을 rendering 하게 할 수도 있습니다. 

    그리고, assigns로 주어진 파라미터 값을 이용하거나, conn 의 값을 이용하는 것도 가능합니다. 

     

    이에 대한 설명은 https://hexdocs.pm/phoenix/views.html#rendering-templates 을 참조하시기 바랍니다.

     

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

Designed by Tistory.