행복한 아빠

[Grails1.0 사용자 가이드] 6. 웹 계층 - 2 본문

Grails

[Grails1.0 사용자 가이드] 6. 웹 계층 - 2

행복한아빠 2008. 3. 5. 23:48


6. 웹 계층 - 2

6.2 Groovy Server Pages

Groovy Server Page(줄여서 GSP)는 Grails의 뷰 기술이다. 이것은 ASP나 JSP와 같은 기술의 사용자에게 친숙하게끔 설계되었다. 그러나 보다 유연하고 직관적이다.

Grails의 GSP는 grails-app/views 디렉토리에 있으며 보통 약속(convention)에 의해 자동으로 렌더링하거나 다음과 같이 render 메소드로 렌더링한다.

render(view:"index")

보통 GSP에는 마크업과 뷰 렌더링을 도와주는 GSP 태그가 섞여있다.

사용자 삽입 이미지
비록 GSP에 Groovy 로직을 포함하는게 가능하고 이것을
이 문서에서 다루지만 실용적이지 않다. 마크업과 코드가
섞이는 것은 나쁜 것이고 대부분의 GSP 페이지에는 코드가 없으면
있을 필요도 없다.

보통 GSP는 뷰를 표현하기 위해 사용하는 변수의 집합인 "모델"을 가진다. 컨트롤러에서 GSP 뷰로 모델을 전달한다. 다음 컨트롤러 액션 예제를 살펴보자.

def show = {
    [book: Book.get(params.id)]
}

이 액션은 Book 인스턴스를 찾아 book이라는 키를 가진 모델을 생성한다. GSP 뷰는 book이라는 이름으로 이 키를 참조할 수 있다.

<%=book.title%>


6.2.1 GSP 기본
다음 뷰 장에서 우리는 GSP의 기본과 가능한 것들에 대해 다룰 것이다. 첫번째로 JSP와 ASP 사용자에게 익숙한 기본 문법부터 다뤄보자.

GSP는 Groovy 코드가 포함될 수 있는 <%  %> 블럭 사용을 지원한다. (다시 말하지만 안 좋은 방법이다)

<html>
    <body>
        <% out << "Hello GSP!" %>
    </body>
</html>

이 문법과 함께 값을 출력할 수 있는 <%= %>를 사용할 수도 있다.

<html>
    <body>
        <%="Hello GSP!" %>
    </body>
</html>

GSP는 또한 다음 예제에서 보는 것 같이 JSP 스타일의 서버측 주석을 지원한다.

<html>
    <body>
        <%-- This is my comment --%>
        <%="Hello GSP!" %>
    </body>
</html>


6.2.1.1 변수와 범위
물론 <%  %> 괄호 안에 변수를 선언할 수 있다.

<% now = new Date() %>

그리고 더 아래 페이지에서 이 변수들을 재사용할 수 있다.

<%=now%>

어쨌든 GSP 범위안에는 다음을 포함한 몇 가지 미리 정의된 변수들이 있다.


6.2.1.2 로직과 반복(Iteration)
물론 아래와 같이 <%  %> 문법을 이용하여 루프 등을 포함할 수 있다.

<html>
    <body>
        <% [1,2,3,4].each { num -> %>
            <p><%="Hello ${num}!"%></p>
        <% } %>
    </body>
</html>

논리적 분기도 할 수 있다.

<html>
    <body>
        <% if(params.hello == 'true' )%>
        <%="Hello!"%>
        <% else %>
        <%="Goodbye!"%>
    </body>
</html>


6.2.1.3 페이지 지시자(Directives)
GSP 역시 몇 가지 JSP 스타일의 페이지 지시자를 지원한다.

import 지시자로 페이지에 클래스들을 임포트할 수 있다. 그렇다 하더라도 Groovy가 기본으로 임포트하는 것과 GSP 태그로 인해 거의 필요가 없다.

<%@ page import="java.awt.*" %>

또한 GSP도 contentType 지시자를 지원한다.

<%@ page contentType="text/json" %>

contentType으로 다른 포맷으로 렌더링하기 위해 GSP를 사용할 수 있다.


6.2.1.4 표현
GSP에서 앞서 소개된 <%  %> 문법은 GSP 표현의 지원으로 거의 사용하지 않는다. 이것은 주로 ASP와 JSP 개발자가 GSP를 사용하는데 편안함을 느끼게 하기 위해 존재한다. GSP 표현은 JSP EL 표현이나 Groovy GString과 유사하면 ${expr} 형식을 가진다.

<html>
    <body>
        Hello ${params.name}
    </body>
</html>

하지만 JSP EL과 다르게 ${..} 괄호안에 어떤 Groovy 표현도 올 수 있다. ${..} 안의 변수들은 기본으로 escaped가 아니다. 따라서 변수의 문자열에 있는 어떤 HTML도 직접 페이지로 출력된다. Cross-site-scripting(XSS) 공격의 위험을 줄이기 위해서, grails-app/conf/Config.groovy안의 grails.views.default.codec 설정을 통해 자동 HTML escaping을 활성화할 수 있다.

grails.views.default.codec='html'

다른 가능한 값은 기본값인 'none'과 'base64'이다.


6.2.2 GSP 태그
지금부터 덜 매력적인 JSP의 유산들은 저리 치우고, 이어지는 장에서는 GSP 내장 태그들을 다룰 것이다. 이것들은 GSP 페이지를 정의하는데 괜찮은 방법이다.

사용자 삽입 이미지
태그 라이브러리 장에서는 당신의 사용자 정의 태그 라이브러리를 추가하는 방법을 다룬다.

내장된 GSP 태그들은 g: 접두어로 사작된다. JSP와는 다르게 어떤 태그 라이브러리도 임포트할 필요가 없다. 태그가 g: 로 시작하면 자동으로 GSP 태그라 가정한다. GSP 태그의 예제는 다음과 같다.

<g:example />

GSP 태그는 다음과 같이 몸체를 가질 수 있다.

<g:example>
    Hello world
</g:example>

GSP 태그 속성에 GSP 표현을 전달할 수 있다. 사용되지 않는 표현의 경우는 문자열 값으로 가정한다.

<g:example attr="${new Date()}">
    Hello world
</g:example>

GSP 태그 속성에 도 전달할 수 있다. 이것은 named 파라메터 스타일의 문법에서 종종 사용한다.

<g:example attr="${new Date()}" attr2="[one:1, two:2, three:3]">
    Hello world
</g:example>

속성의 값 안에서는 반드시 문자열에 단일 따옴표를 사용해야 한다는 것에 주의하라.

<g:example attr="${new Date()}" attr2="[one:'one', two:'two']">
    Hello world
</g:example>

기본 문법을 끝내고 다음 장에서 Grails에 기본으로 내장된 태그들을 보자.

6.2.2.1 변수와 범위
set 태그를 이용하여 GSP 안에 변수를 정의할 수 있다.

<g:set var="now" value="${new Date()}" />

여기에서 우리는 now라는 변수에 GSP 표현의 결과값을 지정했다(간단히 새로운 java.util.Date 인스턴스를 생성). 또한 변수를 정의하기 위해 <g:set> 태그의 몸체를 사용할 수 있다.

<g:set var="myHTML">
    Some re-usable code on: ${new Date()}
</g:set>

다음 범위 중 하나에 변수를 위치시킬 수 있다.

  • page: 현재 페이지 범위에 한정 (기본)
  • request: 현재 요청(request)에 한정
  • flash: flash 범위에 넣어서 다음 요청에서 사용 가능함
  • session: 사용자 세션을 위한 범위
  • application: 애플리케이션 전체 범위

어느 범위에 변수를 위치할 지 선택하기 위해 scope 속성을 사용한다.

<g:set var="now" value="${new Date()}" scope="request" />


6.2.2.2 논리와 반복
GSP는 괜찮은 논리 태그와 반복 태그를 지원한다. 논리 태그로 전형적인 분기 시나리오를 지원하기 위한 if, else 그리고 elseif가 있다.

<g:if test="${session.role == 'admin'}">
    <%-- 관리용 기능들을 보여줌 --%>
</g:if>
<g:else>
    <%-- 기본 기능들을 보여줌 --%>
</g:else>

반복을 위해 GSP에는 eachwhile 태그가 있다.

<g:each in="${[1,2,3]}" var="num">
    <p>Number ${num}</p>
</g:each>
<g:set var="num" value="${1}" />
</g:while test="${num < 5}">
    <p>Number ${num++}</p>
</g:while>


6.2.2.3 검색과 필터링
객체의 collection이 있을 경우 종종 어떤 방법으로든 이것을 정렬하거나 필터링할 필요가 있다. 이 작업을 위해 GSP는 findAllgrep을 지원한다.

Stephen King's Books:
<g:findAll in="${books}" expr="it.author == 'Stephen King'">
    <p>Title: ${it.title}</p>
</g:findAll>

expr 속성은 필터로 쓸 Groovy 표현을 갖는다. 필터를 지정함으로 클래스로 필터하는 것처럼 grep 태그는 비슷한 작업을 한다.

<g:grep in="${books}" filter="NonFictionBooks.class">
    <p>Title: ${it.title}</p>
</g:grep>

또는 정규식을 사용할 수 있다.

<g:grep in="${books.title}" filter="~/.*?Groovy.*?/">
    <p>Title: ${it}</p>
</g:grep>

위에 것은 GPath를 사용하는 흥미로운 예제이다. GPath는 Groovy 언어에서의 XPath 같은 것이다. 원래 books collection은 Book 인스턴스의 집합이다. 그러나 Book이 title을 가졌다고 가정하면 books.title 표현을 이용하여 책 제목의 목록을 얻을 수 있다. Groovy는 마술같이 자동으로 Book 인스턴스의 목록에서 각 제목을 획득하여 새로운 책제목 목록을 반환한다.


6.2.2.4 Link와 Resource
GSP는 또한 컨트롤러와 액션으로 가는 link 관리를 도와주는 태그들도 지원한다. link 태그를 이용해 컨트롤러와 액션 이름 쌍을 지정하여 자동으로 URL Mappings을 기준의 link를 만들수 있다(URL Mapping을 변경해도 잘 동작한다). 다음에서 몇 가지 link 예제를 볼 수 있다.

<g:link action="show" id="1">Book 1</g:link>
<g:link action="show" id="${currentBook.id}">${currentBook.name}</g:link>
<g:link controller="book">Book Home</g:link>
<g:link controller="book" action="list">Book List</g:link>
<g:link url="[action:'list',controller:'book']">Book List</g:link>
<g:link action="list" params="[sort:'title',order:'asc',author:currentBook.author]">
    Book List
</g:link>


6.2.2.5 폼 그리고 필드

폼 기본
GSP는 HTML form과 필드를 다루는 것에 도움이 되는 여러 가지 태그를 지원하는데 가장 기본은 form 태그이다. form 태그는 일반 HTML form 태그에 컨트롤러/액션을 인식하는 기능을 추가한 버전이라 생각하면 된다. url 속성에 어떤 컨트롤러와 액션이 매핑될지 지정할 수 있다.

<g:form name="myForm" url="[controller:'book',action:'list']">...</g:form>

이 경우 우리는 myform이라는 폼을 생성했고 이 폼은 BookController의 list 액션에 sumbit한다. 그 외는 보통의 모든 HTML 속성이 적용된다.


폼 필드
form 구성이 쉬운 것 같이 GSP는 다음을 포함한 다양한 종류의 필드를 다루는 태그들을 지원한다.

  • textField: 타입이 'text'인 input 필드
  • checkBox: 타입이 'checkbox'인 input 필드
  • radio: 타입이 'radio'인 input 필드
  • hiddenField: 타입이 'hidden'인 input 필드
  • select: HTML select 박스를 다루기 위한 태그

이 필드들은 그 값으로 GSP 표현을 사용할 수 있다.

<g:textField name="myField" value="${myValue}" />

또한 GSP에는 위 태그를 확장한 버전을 가지고 있는데 radio 태그의 그룹을 생성하는 radioGroup, 로케일을 위한 localeSelect, 통화를 위한 currencySelect 그리고 time zone을 위한 timeZoneSelect 같은 것들이 있다.


다중 submit 버튼
다중 submit 버튼을 다룰 때 발생하는 문제를 Grails에서는 actionSumit 태그로 우아하게 다룬다. 이것은 그냥 보통의 submit 같이 보이지만 sumit할 때 action을 지정할 수 있다.

<g:actionSubmit value="Some update label" action="update" />


6.2.2.6 태그를 메소드로 호출하기
GSP태그가 다른 태그 기술과 주요하게 다른 하나는 GSP 태그는 일반 태그로 호출되거나 컨트롤러태그 라이브러리 또는 GSP 뷰에서 메소드로 호출될 수 있다는 것이다.

GSP에서 태그를 메소드로 호출하기
태그가 메소드로 호출될 때 태그는 결과를 response에 직접 쓰는 대신 문자열로 반환한다. 따라서 createLinkTo 태그를 예를 들면 마찬가지로 메소드로 호출할 수 있다.

Static Resource: ${createLinkTo(dir:"images", file:"logo.jpg")}

이것은 특히 마크업 속성 안에서 태그를 사용할 필요가 있을 때 유용한다.

<img src="${createLinkTo(dir:'images', file:'logo.jpg')}" />

이런 특성을 지원하지 않는 뷰 기슐에서는 태그 안에 태그를 포함해야 한다. 이럴 경우 코드가 빠르게 지저분해지며 종종 드림위버같은 위지윅(WYSWIG) 툴에서 나쁜 영향을 주어 잘 정의(well-formed)되지 않은 마크업으로 인식하고 렌더링을 시도한다.

<img src="<g:createLinkTo dir="images" file="logo.jpg" />" />


컨트롤러와 태그 라이브러리에서 태그를 메소드로 호출하기
컨트롤러와 태그 라이브러리에서도 마찬가지로 태그를 호출할 수 있다. 기본 g: 네임스페이스를 가진 태그는 접두어 없이 호출할 수 있으며 이것은 문자열을 반환한다.

def imageLocation = createLinkTo(dir:"images", file:"logo.jpg")

어쨌든 이름 충돌을 피하기 위해 접두어 네임스페이스를 사용할 수도 있다.

def imageLocation = g.createLinkTo(dir:"images", file:"logo.jpg")

사용자 정의 네임스페이스가 있다면 대신 그 접두어를 사용할 수 있다. (FCK 에디터 플러그인 예제)

def editor = fck.editor()


6.2.3 뷰와 템플릿
뷰와 함께 Grails는 템프릿 개념을 가지고 있다. 템플릿은 뷰를 유지보수가 가능한 부분으로 분리하는데 유용한데 이것은 구조적인 뷰를 구성하기 위해 높은 수준의 재사용이 가능한 메커니즘을 제공하는 레이아웃과 결합하여 사용한다.

템플릿 기본
Grails는 뷰가 템플릿인지 식별하기 위해 뷰 이름 앞에 밑줄을 붙이기로 하는 약속(convention)을 이용한다. 예를 들어 grails-app/views/book/_bookTempate.gsp 위치에 Books를 렌더링하기 위한 템플릿을 가질 수 있다.

<div class="book" id="${book?.id}">
    <div>Title: ${book?.title}</div>
    <div>Author: ${book?.author?.name}</div>
</div>

이 템플릿을 렌더링하기 위해 grails-app/views/book의 뷰들은 render 태그를 사용할 수 있다.

<g:render template="bookTemplate" model="[book:myBook]" />

render 태그의 model 속성을 이용하여 어떻게 모델을 전달하는지 주목한다. 만일 여러 개의 Book 인스턴스를 가지고 있을 경우 역시 render 태그를 사용하여 각 Book에 대해 템플릿을 렌더링할 수 있다.

<g:render template="bookTemplate" var="book" collection="${bookList}" />


공유 템플릿
이전 예전에서는 grails-app/views/book에 있는 BookController와 그 뷰들에 특수화된 템플릿을 다뤘다. 하지만 애플리케이션 전체에 걸쳐 공유하는 템플릿을 원할 것이다.

이런 경우 템플릿을 루트 뷰 디렉토리(grails-app/views)나 그 아래 아무 하위 디렉토리에 놓는다. 그리고 템플릿 속성에 템플릿 이름 앞에 /를 사용하여 상대 템플릿 패스를 지정한다. 예를 들어 템플릿 이름이 grails-app/views/shared/_mySharedTemplate.gsp이라면 다음과 같이 참조할 수 있다.

<g:render template="/shared/mySharedTemplate" />

또한 이 기술을 사용하여 어떤 뷰나 컨트롤러에서도 어떤 디렉토리에 있는 템플릿도 참조할 수 있다.

<g:render template="/book/bookTemplate" model="[book:myBook]" />


컨트롤러와 태그 라이브러리에서 템플릿 사용
컨트롤러의 render 메소드를 사용하여 컨트롤에서도 템플릿을 렌더링할 수 있다. 이것은 Ajax 애플리케이션에서 유용하다.

def show = {
    def b = Book.get(params.id)
    render(template:"bookTemplate", model:[book:b])
}

컨트롤러의 render 메소드는 response에 직접 쓰는데 이것은 가장 일반적인 경우다. 대신 템플릿의 결과를 문자열로 얻을 필요가 있을 경우 render 태그를 사용할 수 있다.

def show = {
    def b = Book.get(params.id)
    String content = g.render(template:"bookTemplate", model:[book:b])
    render content
}

g. 네임스페이스를 사용함으로써 Grails에게 "render 메소드 대신 태그를 메소드로 호출한다"는 것을 알리는 것에 주목하라.

6.2.4 Sitemesh 레이아웃

레이아웃 생성
Grails는 뷰 레이아웃을 지원하기 위해 데코레이터(decorator) 엔진인 Sitemesh를 이용한다. grails-app/views/layouts 디렉토리에 레이아웃들이 위치한다. 전형적인 레이아웃은 아래와 같다.

<html>
    <head>
        <title><g:layoutTitle default="An example decorator" /></title>
        <g:layoutHead />
    </head>
    <body onload="${pageProperty(name:'body.onload')}">
        <div class="menu"><!--my common menu goes here--></menu>
            <div class="body">
                <g:layoutBody />
            </div>
        </div>
    </body>
</html>

핵심 요소들은  layoutHead, layoutTitle 그리고 layoutBody 태그 사용법이고 아래 이것들이 하는 일을 설명한다.

  • layoutTitle: 목표 페이지의 제목 출력
  • layoutHead: 목표 페이지의 head 태그 내용 출력
  • layoutBody: 목표 페이지의 body 태그 내용 출력

이 예제는 목표 페이지를 검사하고 페이지 속성을 반환하는 pageProperty 태그도 설명하고 있다.

레이아웃 실행(triggering)
레이아웃을 실행하기 위한 몇 가지 방법이 있다. 가장 간단한 방법은 뷰에 meta 태그를 추가하는 것이다.

<html>
    <head>
        <title>An Example Page</title>
        <meta name="layout" content="main"></meta>
    </head>
    <body>This is my content!</body>
</html>

이 경우 페이지를 레이아웃하기 위해서 grails-app/views/layouts/main.gsp을 사용할 것이다. 이전 장의 레이아웃 폼을 사용한다면 결과는 아래와 같을 것이다.

<html>
      <head>
          <title>An Example Page</title>
      </head>
      <body onload="">
            <div class="menu"><!--my common menu goes here--></menu>
                 <div class="body">
     This is my content!
                 </div>
            </div>
      </body>
</html>


약속(convention)에 의한 레이아웃
레이아웃을 결합하기 위한 두 번째 방법은 "약속(convention)에 의한 레이아웃"을 이용하는 것이다. 예를 들어 다음과 같은 컨트롤러가 있다면

class BookController {
    def list = { ... }
}

grails-app/views/layouts/book.gsp라는 레이아웃을 만들수 있는데 약속에 의해 이 레이아웃은 BookController에서 렌더링을 위임(delegate)하는 모든 뷰들에게 적용된다.

대신, BookController의 list 액션에만 적용하는 grails-app/views/layouts/book/list.gsp라는 레이아웃을 만들 수 있다.

만일 layout을 지정하는 자리에 위에서 설명한 두 가지가 모두 있을 경우 list 액션이 실행될 때 액션은 전자의 방법(meta 태그를 이용한 방법)을 취한다.

인라인 레이아웃
Grails도 역시 applyLayout 태그를 통해 Sitemesh의 인라인 레이아웃 개념을 지원한다. 템플릿이나 URL 또는 컨텐트 임의의 구역에 레이아웃을 적용하기 위해 applyLayout 태그를 사용한다. 이는 본질적으로 템플릿을 장식함(decorating)으로써 뷰 구조를 보다 모듈화할 수 있도록 한다.

아래에 몇 가지 사용 예제가 있다.

<g:applyLayout name="myLayout" template="bookTemplate" collection="${books}" />
<g:applyLayout name="myLayout" url="http://www.google.com" />
<g:applyLayout name="myLayout">
The content to apply a layout to
</g:applyLayout>


---
원문: 6.2 Groovy Server Pages

0 Comments
댓글쓰기 폼