 |
|
난이도 : 초급 Scott Davis, 편집장, AboutGroovy.com
옮긴이: 박찬욱 dwkorea@kr.ibm.com
2008 년 5 월 06 일 Groovy Server Pages(GSP)는 Grails 웹 프레임워크에서 웹 페이지 부분을 담당합니다.
Grails 마스터하기
의 세 번째인 이번 회에서 Scott Davis는 GSP에 대해 자세하게 설명합니다. Grails TabLibs를 사용하고, GSP와 부분 코드 조각을 함께 섞어 사용하면서, (스캐폴딩으로) 자동으로 생성되는 뷰에 기본 템플릿을 커스터마이징하는 게 얼마나 쉬운지 살펴볼 것입니다.
이번 연재의 첫 번째, 두 번째 글에서는 Grails 웹 프레임워크의 기본 구성 요소를 소개했다. 거듭 말하지만 Grials는 모델-뷰-컨트롤러(MVC) 아키텍처 패턴(
참고자료 참고)에 기반을 두고 있으며, 프레임워크의 부분들과 함께 바인딩되는 설정보다는 관례를 선호한다. Grails는 외부 설정 파일과 연계하는 오래되고, 에러 발생 확률이 높은 수동적인 부류의 방법을 바꿔, 좀 더 직관적인 파일과 디렉터리 이름을 사용한다. 예를 들어, 첫 번째 글에서는 컨트롤러는 Controller라는 접사가 붙고 grails-app/controller 디렉터리에 저장되는 것을 살펴 봤다. 두 번째 글에서는 grails-app/domain 디렉터리에 찾을 수 있는 도메인 모델에 대해 배웠다.
이번 달에는 Grails 뷰에 대해 얘기하면서 MVC 3부작을 마무리 지을 것이다. (우리가 예상했듯이) 뷰는 grails-app/views 디렉터리에 저장된다. 그러나 분명하고 직관적인 디렉터리 이름보다는 뷰에 대한 이야기를 더 해볼 생각이다. 먼저 GSP(Groovy Server Pages)에 대해 얘기해보고, 다른 대안 뷰 옵션으로 관심을 옮길 것이다. Grails 태그 라이브러리(TagLibs)에 대해 배워보고, 자체적으로 TagLib을 만드는 작업이 얼마나 쉬운지 알아볼 것이다. 또한 일반적인 GSP 코드 조각에 스스로 만든 부분 템플릿을 넣음으로써 DRYness(참고자료 참조)를 유지하려는 지속적인 싸움을 어떻게 해나가는지를 볼 것이다. 마지막으로 스캐폴딩 뷰에 기본 템플릿을 순간적으로 변경하는 방법을 배워보고, 이로 인해 Grails 애플리케이션의 기본적인 룩-앤-필(look-and-feel)로 뛰어넘어 자동으로 생성되는 뷰의 편리성까지 모두 갖춘 모습을 배울 것이다.
Grails 애플리케이션 보기
Grails는 프리젠테이션 티어로 GSP를 사용한다. GSP에서 Groovy는 단지 그 기초를 이루는 기술뿐만 아니라, 스크립틀릿(scriptlet) 한두 개를 빠르게 작성하길 원하는 경우 쓸 수 있는 언어를 나타내기도 한다. 그런 의미에서 GSP에서는 웹 페이지에 자바 코드를 섞어 쓸 수 있는 JSP(Java™ Server Pages), 그리고 HTML 태그 사이에 루비 코드를 끼워넣을 수 있는 RHTML(루비온레일스의 핵심 기술)과 비슷하다.
물론 스크립틀릿은 자바 커뮤니티에서 오랫동안 난색을 표하는 기술이었다. 스크립틀릿은 가장 낮은 형태(예를 들어 복사와 붙여 넣기)의 기술 재사용을 불러오며, 또한 기술 윤리적으로도 타락한 또 다른 죄악(우리가 할 수 있는 일과 해야 하는 일에 심한 불균형이 있기 때문에)을 불러온다. GSP에서 G는 정직한 자바 시민들의 구현 언어임에 틀림없다는 의미로 "기분 좋음"을 생각나게 한다. 그루비 TagLibs와 일부 템플릿은 웹 페이지 사이 행동과 코드 공유에 대해 좀 더 세련된 방법을 제공한다.
GSP는 Grails의 페이지 중심 MVC 세계에서 뷰 기반이 된다. 페이지는 단위 측정의 기본이 된다. List 페이지는 Show 페이지에 연결된다. 또한 Show 페이지는 Edit 페이지로 연결된다. 스트럿츠(Struts)에 익숙한 개발자나 최신 레일스의 열성적인 팬이라면 이미 웹 생명 주기 타입에 친숙할 것이다.
최근 몇 년간 페이지 중심적이지 않은 뷰 기술이 폭발적으로 나타났기 때문에 이 내용을 언급했다. JSF(JavaServer Faces)와 Tapestry 같은 컴포넌트 지향 웹 프레임워크는 사람들의 마음(mindshare)을 얻고 있다. Ajax 혁명은 Dojo와 Yahoo!UI(YUI) 라이브러리와 같은 거대한 자바스크립트 기반 솔루션을 낳았다. 어도비 플래시 구글 웹 툴킷 같은 리치 인터넷 기술은 더 풍부하고, 데스크톱에서의 사용자 경험을 웹에 배치할 수 있다는 편리성을 약속했다. 운 좋게도, Grails는 여기서 언급한 모든 뷰 기술을 쉽게 다룰 수 있다.
MVC 관심의 분리(separation of concerns)의 최종 목적은 원하는 대로 모든 뷰를 웹 애플리케이션에 입힐 수 있게 하는 것이다. Grails의 가장 유명한 일반적인 플러그인 기반 구조는 수많은 GSP 대안 기술을 단지 grails install-plugin으로 설치할 수 있게 해준다(이용할 수 있는 플러그인 리스트 링크는 참고자료를 참고하고, 명령 프롬프트에서 grails list-plugins를 입력해보자). 플러그인은 대부분 사용자들이 쓰는 프리젠테이션 티어 기술과 함께 Grails를 쓰면서 사용자들이 원하는 결과로 탄생하기 때문에 매우 커뮤니티 지향적이다.
자동으로 JSF를 후킹하는 것처럼, Grails가 이 두 기술을 동시에 사용하지 못하게 막지는 않는다. Grails 애플리케이션은 표준 자바 EE 애플리케이션이므로 lib 디렉터리에 필요한 JAR를 넣을 수 있다. 또한 WEB-INF/web.xml 설정 파일에 필요한 설정을 추가할 수 있으며, 일반적인 작업으로 애플리케이션을 작성할 수 있다. Grails 애플리케이션은 GSP뿐만 아니라 JSP도 지원하기 때문에 표준 서블릿 컨테이너에 배포할 수 있다. (컴포넌트 지향 웹 프레임워크인) Echo2와 Wicket에 대한 플러그인이 있지만, JSF나 Tapestry에 대한 정식 플러그인은 아직 없다.
이와 비슷하게 Dojo와 YUI 같은 Ajax 프레임워크를 Grails에 추가하는 단계는 일반적인 작업과 전혀 다를 것이 없다. 단지 web-app/js 디렉터리에 자바스크립트 라이브러리를 복사하면 된다. Prototype과 Scriptaculous는 기본적으로 Grails에 설치되어 있다. RichUI 플러그인은 다양한 Ajax 라이브러리에서 선택한 UI 위젯에 따라 사용하는 것이 최선의 방법이다.
플러그인 목록을 보면 플렉스, OpenLazlo, GWT와 ZK 같은 최신 RIA 클라이언트를 지원하는 것을 알 수 있다. 분명한 점은 Grails 애플리케이션에 어느 뷰 솔류선을 선택하더라도 문제가 없다는 점이다. 그러나 이번 글에서는 Grails가 가장 기본적으로 제공하는 뷰 기술인 GSP에 대해 좀 더 자세히 얘기해보자.
GSP 101
우리는 두 가지 방법으로 GSP 페이지를 지저분하게 할 수 있다. .gsp 파일 확장자는 <g:로 시작되는 태그를 엄청나게 사용했다는 결정적인 증거다. 사실상 GSP 페이지는 단지 동적인 내용을 표현하는 Grails 태그가 혼합된 표준 HTML일 뿐이다. 이전 절에서 언급된 몇몇 대안 뷰 기술은 자바, 액션스크립트 혹은 다른 프로그래밍 언어 이면에서 이뤄지는 HTML, CSS와 자바스크립트의 상세 내용을 숨겨주는 불투명한(opaque) 추상 레이어가 된다. GSP는 필요에 따라서 더 쉽게 프레임워크를 분리하며, 기존 웹 기술을 더 편리하게 사용할 수 있도록 해주는 표준 HTML을 포괄하는 작은 그루비 퍼사드다.
그러나 지금부터는 여행 계획 애플리케이션에서 GSP를 거의 찾아 볼 수 없게 될 것이다(이번 연재 첫 번째와 두 번째 글에서 여행 계획자 애플리케이션을 만들기 시작했다. 지금까지 내용을 따라 하지 못했다면, 지금이 따라잡을 좋은 시간이다). 현재 우리가 만든 뷰에서는 동적 스캐폴딩을 사용하고 있기 때문에 trip-planner/grails-app/views는 비어있다. Listing 1에 나오는 grails-app/controller/TripController.groovy를 텍스트 편집기로 열어 동적 스캐폴딩을 가능하도록 해주는 명령을 찾아보자.
Listing 1. TripController 클래스
class TripController{
def scaffold = Trip
}
|
def scaffold = Trip 줄에서 Grails가 실행 시에 동적으로 GSP를 생성하라고 명시하고 있다. 도메인 모델 변화에 따라 동기화된 뷰를 자동으로 유지해 주므로 매우 좋지만, 프레임워크를 배우는 동안에는 오히려 도움이 되지 않는다.
trip-planner 디렉터리 루트 경로에서 grails generate-all Trip을 입력하자. 기존 컨트롤러에 덮어쓸지 물어보면 y를 입력하자. (반복적인 질문 없이 모두 덮어쓰고 싶다면 a를 입력할 수도 있다.) 이제 완전한 TripController 클래스에서 create, edit, list, (이 중에서도 특히) show라는 이름으로 된 클로져를 볼 수 있다. 또한 grails-app/views/trip 디렉터리에서 create.gsp, edit.gsp, list.gsp, show.jsp 등 GSP 네 개를 볼 수 있다.
이제 설정보다 관례를 즐길 준비가 됐다. http://locahost:9090/trip-planner/trip/list에 들어가, TripController에 Trip 도메인 모델 객체의 리스트를 산출하고, trip/list.gsp 뷰에 전달할 것을 요청해보자. Listing 2처럼 다시 한 번 텍스트 편집기로 TripController.groovy를 살펴보자.
Listing 2. 완성된 TripController 클래스
class TripController{
...
def list = {
if(!params.max) params.max = 10
[ tripList: Trip.list( params ) ]
}
...
}
|
이 짧은 클로저는 데이터베이스에서 Trip 레코드 열 개를 반환하고, 이를 POGO로 변환해 tripList로 명명된 ArrayList에 저장한다. 그 다음 list.gsp 페이지에서 tripList를 반복해 읽어 들여 행 단위 별로 HTML에 바인딩해 주게 된다.
다음 절에서는 웹 페이지에서 각 Trip을 보여주는 데 쓰는 <g:each> 태그를 포함하는 매우 대중적인 Grails 태그를 살펴보자.
Grails 태그
<g:each>는 일반적으로 사용되는 Grails 태그다. 리스트에 포함된 각 아이템에 대해 반복해서 접근한다. 텍스트 편집기로 grails-app/views/tip/list.gsp(Listing 3에 나오는)를 열어 실제로 사용되는 모습을 확인해보자.
Listing 3. list.gsp 뷰
<g:each in="${tripList}" status="i" var="trip">
<tr class="${(i % 2) == 0 ? 'even' : 'odd'}">
<td><link action="show" id="${trip.id}">${trip.id?.encodeAsHTML()}</g:link></td>
<td>${trip.airline?.encodeAsHTML()}</td>
<td>${trip.name?.encodeAsHTML()}</td>
<td>${trip.city?.encodeAsHTML()}</td>
<td>${trip.startDate?.encodeAsHTML()}</td>
<td>${trip.endDate?.encodeAsHTML()}</td>
</tr>
</g:each>
|
<g:each> 태그에서 status 속성은 반복 횟수를 의미하는 간단한 필드다(이 값이 다음 줄인 세 번째 구문부터 CSS 스타일을 even인지 odd인지를 결정하는 데 사용된다는 점을 기억하자). var 속성은 현재 아이템을 유지하는 데 쓰는 변수 이름이다. 만약 이름을 foo로 변경한다면, ${foo.airline?.encodeAsHTML()} 이후 모든 줄을 변경할 필요가 있다. (?. 연산자는 NullPointerException을 피하는 그루비만의 방법이다. 이 단축어는 airline이 null이 아니라면, encodeAsHTML() 메서드를 호출하라는 의미며, null이라면 단순히 빈 문자열을 반환한다.)
또 다른 공통 Grails 태그로 <g:link>가 있다. 생각했던 대로 HTML <a href> 링크를 만들어준다. <a href> 태그를 바로 사용을 못하지는 않지만, Grails의 태그는 편리하게도 action, id, controller 속성을 받아 들일 수 있다. 주위를 anchor 태그로 감싸지 않고 href를 다루고 싶다면, <g:createLink>를 대신 사용할 수 있다. list.gsp 제일 위에서 링크를 반환하는 세 번째 태그인 <g:createLinkTo>를 볼 수 있다. 이 태그는 논리적인 controller,
action, id 속성 대신 dir과 file 속성을 받는다. Listing 4는 link와 createLinkTo 사용 예를 보여준다.
Listing 4. link 대 createLinkTo 태그
<div class="nav">
<span class="menuButton"><a class="home" href="${createLinkTo(dir:'')}">Home</a></span>
<span class="menuButton"><link class="create" action="create">New Trip</g:link></span>
</div>
|
Listing 4에서는 두 개의 호환성 있는 다른 폼에서 Grails 태그, 즉 꺽음 괄호에서 사용한 태그 혹은 {}에서 메서드 호출을 호출할 수 있다는 점을 기억하자. {} 표현(공식적으로는 Expression Language 혹은 EL 문법)은 또 다른 속성 내에서 메서드를 호출할 때 더 적절하다.
list.gsp에서 몇 줄 내려오면, Listing 5에서 확인할 수 있는 또 다른 유명한 Grails 태그인 <g:if>를 볼 수 있다. 여기서는 flash.message 속성이 null이 아니면, 화면에 보여주라"라는 의미를 말하고 있다.
Listing 5. <g:if> 태그
<h1>Trip List</h1>
<if test="${flash.message}">
<div class="message">${flash.message}</div>
</g:if>
|
여기서는 단지 자동 생성된 뷰를 보여주므로 실제로 사용할 때는 더 많은 Grails 태그를 볼 수 있을 것이다. <g:paginate> 태그는 데이터베이스에 화면에 보이는 값 열 개보다 더 많은 Trip을 포함하고 있는 경우 "previous"와 "next" 링크를 보여준다. <g:sortable> 태그는 정렬 목적으로 클릭할 수 있는 컬럼 헤더를 만들어 준다. 다른 <g:form>과 <g:submit> 같이 HTML 폼에 관련된 태그는 GSP 페이지를 잠깐 둘러보면서 확인해보자. 온라인 Grails 문서에서 사용할 수 있는 모든 Grails 태그 목록과 사용 예제를 보여준다(참고자료 참조).
커스텀 태그 라이브러리
표준 Grails 태그가 매우 유용하긴 하지만, 결국 자체적으로 만든 커스텀 태그가 필요한 상황이 발생한다. (나를 포함한) 숙련된 자바 개발자는 대부분 공공연하게 "좋다, 커스텀 TagLib은 여기서 사용할 수 있는 적절한 아키텍처 해결책이다."라고 말하다가도, 어느 누구도 보지 않는다는 생각이 들 때면 슬그머니 스크립틀릿으로 코드를 작성한다. 커스텀 JSP TagLib 작성은 상당한 노력이 필요하기 때문에, 가장 쉬운 방법이라는 장점 덕에 일상적인 업무에서 스크립틀릿이 이기는 경우가 매우 흔하다. 이 방법이 올바른 방법은 아니지만, (불행하게도) 매우 쉬운 방법이다.
스크립틀릿만 보면 비명이 나온다. 스크립틀릿은 가장 진실한 말을 엉망으로 만들어 버린다. 스크립틀릿은 HTML의 태그 기반 패러다임을 깨뜨리며, 정제되지 않은 코드를 볼 수 있도록 노출시킨다. 스크립틀릿 코드 자체가 나쁘다는 것은 아니다. 캡슐화 결여와 재사용이 어렵다는 점이 나쁘다는 것이다. 스크립틀릿을 재사용할 수 있는 유일한 방법은 복사-붙여넣기뿐이다. 이 방법은 버그 양산, 불필요한 코드의 비대화(code bloat)하며, 뻔뻔스럽게도 DRY를 위반한다. 그리고 여기서는 아직 스크립틀릿의 테스트 용이성(testability) 결여에 대해서는 말도 꺼내지 않았다.
이런 점을 감안하면서도, 마감에 임박했을 때는 정상적인 역할보다 JSP 스크립틀릿을 더 많이 썼던 점을 자백하겠다. JSTL(JSP Standard Tag Library)은 오랫동안 내면에 있는 악마적인 성향을 막는 데 도움이 됐다. 하지만 필자가 자바 코드로 커스텀 JSP 태그를 작성하고, 컴파일해 정확한 양식으로 정확한 장소에서 TLD(Tag Library Descriptor)를 얻어 사용할 때쯤에는, 태그를 작성을 시작했을 때 가졌던 의미를 완전하게 잊어버리게 된다. 새로운 JSP 태그를 검증하는 테스트를 작성하면서야 겨우 내 의도가 적절했다고 말할 수 있었다.
이와 대조적으로 Grails에서 커스텀 TagLibs 작성은 매우 쉬운 일이다. 프레임워크는 테스트 작성을 포함해 옳은 방향의 일을 쉽게 할 수 있도록 해준다. 예를 들어, 일반적으로 웹 페이지 하단에는 저작권을 표시하는 반복어구를 필요로 한다.
© 2002 - 2008, FakeCo Inc. All Rights Reserved.로 읽도록 할 것이다. 여기서 두 번째 나오는 연도가 항상 현재 연도로 나오게 하고 싶은 경우가 있다. Listing 6은 이럴 경우 스크립틀릿을 사용하는 방법을 보여준다.
Listing 6. 스크립틀릿으로 저작권 표시하기
<div id="copyright">
© 2002 - ${Calendar.getInstance().get(Calendar.YEAR)},
FakeCo Inc. All Rights Reserved.
</div>
|
이제 현재 연도를 어떻게든 받아오는 방법을 알았고, 다음으로는 같은 일을 하는 커스텀 태그를 만들 것이다. grails create-tag-lib Date를 입력하면서 시작하자. 이 코드는 grails-app/taglib/DateTagLib.groovy(TagLib)와 grails-app/test/integration/DateTagLibTests.groovy(테스트) 파일을 만들어 준다. DateTagLib.groovy에 Listing 7에 있는 코드를 추가하자.
Listing 7. 간단한 커스텀 Grails 태그
class DateTagLib {
def thisYear = {
out << Calendar.getInstance().get(Calendar.YEAR)
}
}
|
Listing 7은 <g:thisYear> 태그를 만들어 준다. 위에서 볼 수 있듯이, 연도는 출력 스트림에 바로 내보낸다. Listing 8은 실제로 사용하는 새로운 태그를 보여준다.
Listing 8. 커스텀 태그를 이용한 저작권 고지
<div id="copyright">
© 2002 - <g:thisYear />, FakeCo Inc. All Rights Reserved.
</div>
|
이 시점에서 모든 작업을 완료했다고 생각할 수 있다. 하지만 여기까지는 단지 목표의 반 정도만 온 것이라 겸손하게 말하고 싶다.
TagLibs 테스트
이제 모든 것이 좋아 보이지만, 앞으로 사용한 태그가 문제를 일으키지 않는다는 것을 보증하는 테스트를 작성할 필요가 있다. Working Effectively with Legacy Code의 저자인 Michael Feathers는 테스트 없으면 어느 코드나 레거시 코드가 된다고 말했다. DateTagLigTests.groovy에 Listing 9에 있는 코드를 추가하자.
Listing 9. 커스텀 태그 테스트
class DateTagLibTests extends GroovyTestCase {
def dateTagLib
void setUp(){
dateTagLib = new DateTagLib()
}
void testThisYear() {
String expected = Calendar.getInstance().get(Calendar.YEAR)
assertEquals("the years don't match", expected, dateTagLib.thisYear())
}
}
|
GroovyTestCase는 JUnit 3.x의 TestCase에 대한 작은 그루비 퍼사드다. 한 줄의 간단한 태그에 대한 테스트를 작성하는 것이 지나쳐 보일 수도 있지만, 짤막한 코드가 얼마나 많은 문제의 소지가 되는지 알면 놀랄 것이다. 테스트 작성은 어렵지 않으니 나중에 후회하는 것보다 안전하게 작성해 놓는 게 더 좋다. grails test-app를 입력해 테스트를 작성하자. 모든 것이 완료되면, Listing 10에 나오는 메세지를 볼 수 있을 것이다.
Listing 10. Grails에 테스트 전달
-------------------------------------------------------
Running 2 Integration Tests...
Running test DateTagLibTests...
testThisYear...SUCCESS
Running test TripTests...
testSomething...SUCCESS
Integration Tests Completed in 506ms
-------------------------------------------------------
|
TripTests의 출현에 놀랐다면 걱정하지 말라. grails create-domain-class Trip을 입력했을 때, 우리를 대신해 Grails가 테스트를 생성했다. 사실 Grails의 모든 create 명령어는 대응되는 테스트를 생성한다. 그렇다. 테스트는 현대 소프트웨어 개발에서 매우 중요하다. 테스트를 작성하는 습관이 아직 없다면, Grails가 여러분을 옳은 방향으로 조금씩 끌어가 줄 것이다. 절망할 필요가 없다.
grails test-app 명령어는 테스트 실행뿐만 아니라 훌륭한 HTML 보고서를 만들어 준다. 그림 1처럼 브라우저에서 Test/reports/html/index.html을 열어 표준 JUnit 테스트 보고서를 확인해보자.
그림 1. 단위 테스트 보고서
여러분이 짠 간단한 커스텀 태그가 테스트됐다. 이제 좀 더 복잡한 테그를 빌드해 보자.
고급 커스텀 태그
좀더 세련된 태그에서는 속성과 태그 바디를 사용할 수 있다. 예를 들어, 현재 상황에서 저작권 태그 해결책은 내 취미처럼 여전히 상당한 복사/붙여넣기가 필요하다. 우린 <g:copyright startYear="2002">FakeCo Inc.</g:copyright>와 같은 진정한 재사용이 가능한 태그로 현재 행동을 모두 감쌀 것이다. Listing 11에 나오는 코드를 보자.
Listing 11. 속성과 바디를 처리하는 Grails 태그
class DateTagLib {
def thisYear = {
out << Calendar.getInstance().get(Calendar.YEAR)
}
def copyright = { attrs, body ->
out << "<div id='copyright'>"
out << "© ${attrs['startYear']} - ${thisYear()}, ${body()}"
out << " All Rights Reserved."
out << "</div>"
}
}
|
attrs는 태그 속성에 대한 HashMap임을 기억하자. 여기서 attrs는 startYear 속성을 획득하는 데 사용했다. 필자는 클로저로 사용하는 thisYear 태그를 호출했다(thisYear 태그는 필요하다면 중괄호({}) 안에서 GSP 페이지를 생성할 수 있게 호출하는 역할을 하는 동일한 클로저다). 이와 비슷하게, body는 클로저로 태그에 전달돼, 다른 태그와 똑같은 방식으로 호출하게 된다. 이 방법은 여기서 사용한 커스텀 태그가 GSP의 어느 수준(depth)에서라도 해당 수준 태그의 내부 태그로 사용될 수 있다는 점을 보장한다.
커스텀 TagLibs가 표준 Grails TagLibs로 같은 g: 네임스페이스를 사용한다는 것을 아마도 알아차렸을 것이다. TagLibs에서 커스텀 네임스페이스로 교체하고 싶다면, DateTagLib.groovy에 static namespace = 'trip'을 추가하자. GSP에서 TagLib은 이제 <trip:copyright startYear="2002">FakeCo Inc.</trip:copyright>로 읽을 수 있다.
부분 템플릿
커스텀 태그는 복사/붙여넣기를 해야 하는 스크립틀릿과 달리 짧은 코드를 재사용하는 데 매우 효과적인 방법이다. 더 규모가 큰 GSP 마크업 블록에서는 부분 템플릿(partial template)을 사용할 수 있다.
공식 Grails 문서에서는 부분 템플릿을 템플릿으로 부른다. 한 가지 문제점은 템플릿이란 단어가 Grails에서 여러 의미로 중복해서 쓰인다는 점이다. 다음 절에서 볼 수 있듯이, 스캐폴딩 뷰 변경에 대한 기본 템플릿을 설치할 것이다. 템플릿 변경은 이번 절에서 얘기하는 내용에 대한 부분 템플릿을 포함한다. 이 글에서는 혼란을 줄이기 위해, 레일스 커뮤니티에서 약간의 명명법을 빌려왔으며, 부분 템플릿이나 단지 부분(partial)만으로도 부른다.
부분 템플릿은 다수의 웹 페이지에 걸쳐 공유할 수 있는 GSP 코드의 큰 덩어리다. 예를 들어, 우리가 만든 모든 페이지에 걸쳐 표준 꼬리말을 원한다고 가정해보자. 이를 달성하려면 _footer.gsp라는 이름으로 부분 템플릿을 만들어야 한다. 앞에 나오는 밑줄은 이 GSP가 완벽하지 않지만 정형화되었으며, 표현이 규칙에 맞는(well-formed) GSP란 것을 프레임워크(뿐만 아니라 개발자에게 시각적인 증거로)에 알려 주는 힌트다. grails-app/views/trip 디렉터리에 이 파일을 생성했다면, Trip 뷰에서만 볼 수 있다. 모든 페이지에서 전체적으로 공유할 수 있도록 grails-app/views 디렉터리에 _footer.gsp 파일을 만들어보자. Listing 12는 전체적으로 공유되는 꼬리말에 대한 부분 템플릿을 보여준다.
Listing 12. Grails 부분 템플릿
<div id="footer">
<g:copyright startYear='2002'>FakeCo, Inc.</g:copyright>
<div id="powered-by">
<img src="${createLinkTo(dir:'images', file:'grails-powered.jpg')}" />
</div>
</div>
|
여기서 볼 수 있는 것처럼, 부분 템플릿은 자체적으로 HTML/GSP 문법으로 표현할 수 있도록 해준다. 이에 반해 커스텀 TagLib은 그루비 문법으로 작성된다. 마음속으로 이 두 가지를 직관적으로 유지할 수 있는 방법은 일반적으로 TagLib은 좀더 작은 행동을 캡슐화하는 데 적절하고, 부분 템플릿은 레이아웃 요소를 재사용하는 데 더 적절하다고 정리하는 것이다.
이번 예제에서 작성된 대로 작동하려면, "Powered by Grails" 버튼을 grails-app/web-app/images 디렉터리로(참고자료 참조) 다운로드할 필요가 있다. 높은 해상도의 16x16 파비콘(favicons)을 포함해서, 다운로드 페이지에 들어가면 상당한 양의 Grails 공식 이미지(branding collateral)를 볼 수 있을 것이다.
Listing 13은 list.gsp 하단부에 새로 만든 꼬리말을 포함하는 방법을 보여준다.
Listing 13. 부분 템플릿 표현
<html><body>
...
<g:render template="/footer" />
</body></html>
|
템플릿을 화면에 그릴 때는 밑줄이 남는 것을 기억하자. Trip 디렉터리에 _footer.gsp를 저장했다면, 앞에 나오는 슬래시 또한 남겨둬야 한다. 이 방법에 대해 생각해보면, grails-app/view 디렉터리는 뷰 상속 구조의 최상위가 된다.
기본 스캐폴딩 커스터마이징하기
이제 테스트 가능하며, 재사용 가능한 좋은 컴포넌트를 갖게 됐으니, 이 컴포넌트를 기본 스캐폴딩의 일부분으로 추가할 수 있다. 컨트롤러에 def scaffold = Foo를 입력했을 때 동적으로 생성되어 받을 수 있도록 이 부분을 다시 호출하자. 기본 스캐폴딩은 grails generate-views Trip이나 grails generate-all Trip을 입력했을 때 생성되어 얻을 수 있는 GSP에 대한 소스 역할을 한다.
grails install-templates를 입력해 기본 스캐폴딩을 커스터마이징하자. 프로젝트에서 grails-app/src/templates 디렉터리를 새로 추가하자. artifacts, scaffolding, war라는 이름의 디렉터리 세 개를 볼 수 있다.
artifacts 디렉터리는 Controller, DomainClass,
TagLib와 같은 이름이 들어간 다양한 그루비 클래스 템플릿을 가지고 있다. 예를 들어, 모든 컨트롤러가 특정 추상 상위 클래스를 상속하게 하려는 경우, 여기서 변경을 반영할 수 있다. 새로운 컨트롤러를 전부 우리가 수정한 템플릿 코드에 기반을 두도록 할 것이다(일부 사람들은 동적 스캐폴딩이 모든 컨트롤러의 기본 행동으로 하기 위해 def scaffold = @artifact.name@을 추가하기도 한다).
war 디렉터리는 모든 자바 EE 개발자들에게 친숙한 web.xml 파일을 포함하고 있다. 매개변수, 필터나 서블릿 추가가 필요하다면 그 내용을 여기 있는 web.xml에 넣으면 된다(JSF 열광자들도 여기에 주의를 기울일 건가?). grails war를 입력했을 때, 여기 있는 web.xml 파일은 결과로 반환되는 WAR에 포함되게 된다.
Scaffolding 디렉터리는 뷰를 동적으로 생성하는 데 필요한 기본 재료를 포함하고 있다. list.gsp를 열고, 파일 제일 아래에 <render template="/footer"/>를 추가하자. 이 템플릿은 모든 뷰에서 공유되므로 글로벌 부분 템플릿으로 사용되어야 하는지 한번 더 확인하자.
list 뷰에 대한 변경을 했으니, 이제 우리의 변경 내용이 효율적인지 증명하는 시간이다. 기본 템플릿 수정은 서버를 재 시작 할 필요가 있는 몇 가지 중 경우 중 하나이다. 일단 Grails가 다시 올라오고 시작되면, 브라우저에서 http://localhost:9090/trip-planner/airline/list로 들어가자. AirlineController에 기본 스캐폴딩을 사용했다면, 새로운 꼬리말은 페이지의 제일 아래에서 확인해볼 수 있다.
결론
Grails 마스터하기
의 Part 3을 끝마쳤다. 이제 GSP와 Grails에서 이용할 수 있는 또 다른 뷰 기술에 대해 좀 더 많은 내용을 알게 됐다. 생성되는 페이지에 사용되는 기본 태그에 대해 한층 더 이해할 수 있었다. 또한 스크립틀릿 작성이 매우 더러운 코드를 만들어 낸다는 것을 명백하게 느꼈을 것이다. 대신 커스텀 TagLib 작성으로 올바른 코드 작성이 얼마나 쉬운지도 명백하게 느꼈을 것이다. 부분 템플릿을 만드는 방법을 살펴 봤고, 기본 스캐폴딩 뷰에 부분 템플릿을 추가하는 게 얼마나 쉬운지도 살펴 봤다.
다음 Grails 웹 프레임워크 여행은 Ajax에 초점을 맞출 생각이다. 전체 페이지에 대한 재로딩 없는 "마이크로" HTTP 요청을 만드는 능력은 구글 지도(Google Maps), 플리커(Flickr) 등 유명한 웹 사이트들이 사용하는 비밀스런 정보다. Grails에 똑같은 마법을 적용할 것이다. 특히 다-대-다 연관을 만들고, 자연스럽고, 즐길 수 있는 사용자 경험을 만드는 데 Ajax를 사용할 생각이다.
자, 그럼 그 때까지 Grails 마스터하기와 좋은 시간을 즐겨보자.
참고자료 교육
-
Grails 마스터하기
: Grails에 대해 더 많은 정보를 얻으려면 이번 연재를 더 읽고 함께 따라해 보자.
-
Grails: Grails 웹 사이트를 방문하자.
-
Grails Framework Reference Documentation:Grails 바이블
-
Groovy Recipes
(Pragmatic Bookshelf, 2008년 3월): Scott Davis의 신간에서 그루비와 Grails에 대해 더 배워보자.
-
Practically Groovy
: 이 developerWorks 연재는 그루비의 실질적인 사용방법을 살펴보고, 이를 언제, 어디서 적용할 수 있는지 안내해준다.
-
그루비: 프로젝트 웹 사이트에서 그루비에 대해 더 배워보자.
-
AboutGroovy.com: 최신 그루비 뉴스와 관련 글 링크가 있다.
-
DRY: 정보 중복의 해악을 강조하는 프로세스 원칙
-
모델-뷰-컨트롤러(Model-View-Controller: Grails가 준수하는 대중적인 아키텍처 패턴
-
Grails 플러그인: 모든 Grails 플러그인에 대한 문서
-
GSP 태그 레퍼런스: 표준 GSP 태그에 대한 가이드
-
동적 태그 라이브러리: 커스텀 태그 작성을 깊이 공부해보자.
-
태그 라이브러리 테스트: TagLib의 통합 테스트를 이해하자.å
- "Getting started with JavaServer Faces 1.2, Part 1: Building basic applications"(Richard Hightower, developerWorks, 2007년 12월): JSF를 빠르게 익히도록 예제 중심의 튜토리얼 연재인 이번 연재를 경험해보자.
- "Ajax — A guide for the perplexed, Part 2: Develop a Dojo-based blog reader"(Gal Shachor, Ksenya Kveler, and Maya Barnea, developerWorks, 2007년 12월): Atom 프로토콜 사용을 도와주는 데 보조를 맞출 수 있도록 Dogo 툴킷을 가까이 하자.
- "자바 개발자를 위한 Ajax: 구글 웹 툴킷(Google Web Toolkit) 연구"(Philip McCarthy, 한국 developerWorks, 2006년 8월): GWT의 능력에 대해 배워보고, 간단한 예제 애플리케이션을 통해서 실습해보자.
- "Rich Internet Applications의 기술 옵션"(Vaibhav V. Gadge, developerWorks, 2006년 9월): RIA 개요를 읽어보자.
-
기술 서적 서점: Grails 관련 책과 기술 주제를 찾아보자.
-
한국 developerWorks 자바 기술 존: 자바 프로그래밍의 다양한 측면에 대해 작성된 수많은 기사를 찾아 볼 수 있다.
제품 및 기술 얻기
토론
필자소개  | 
|  | Scott Davis는 국제적으로 유명한 저자이자 강사, 소프트웨어 개발자다. 저서로는 Groovy Recipes: Greasing the Wheels of Java, GIS for Web Developers: Adding Where to Your Application, The Google Maps API, JBoss At Work가 있다. |
기사에 대한 평가
 |
| 이 문서 북마킹 하기
|
|