블로그 이미지
App 개발에 대한 내용을 다룰 예정입니다. AppleSherbet

카테고리

분류 전체보기 (39)
한국프로야구 어플 (18)
세미나 (2)
Java Development (13)
Android App소개 (3)
기타 (2)
당근마켓 (1)
Total297,962
Today12
Yesterday16
Viewing and posting comments
 
이전 튜토리얼에서 홈페이지(말그대로 index페이지)를 완성했었다. 이제 Post 상세 페이지를 작성해보자.
Post 페이지는 현재 포스트 내용, 포스트에 붙은 Comment들을 보여준다. 그리고 이번장에서 새로운 Comment를 추가 하는 것 까지 해보겠다.
 
Creating the 'show' action
 
Post 상세 페이지를 보여주기 위해서 Appliation 컨트롤러에 show() 액션을 만들자.
/yabe/app/controller/Application.java
 
public static void show(Long id) {
    Post post = Post.findById(id);
    render(post);
}
 
show() 액션은 매우 심플하다. 특정 포스트에 대한 id값을 인자로 받아서 Post.findById(id)를 이용해 Post 객체를 얻는다. 그리고 render(post) 를 호출하여 show.html을 브라우져에 렌더링 할 것을 요청한다.
 
[여기서 잠깐]
만약 show 액션에 들어온 id 파라메터가 정상적인 값이 아닌 경우 id값은 null이 되고 Play는 자동으로 validation error를 error 컨테이너에 넣게 된다. 차후 validation 값을 체크해 해당 에러를 핸들링 할 수 있다.
 
이제 브라우져에 보이게 될 show.html 을 만들자.
/yabe/app/views/Application/show.html
 
#{extends 'main.html' /}
#{set title:post.title /}
 
#{display post:post, as:'full' /}
 
우리가 이미 display tag를 만들어 놓았기 때문에 코드는 아주 간단하다. display tag를 full 모드로 호출하고 있다.
 
Adding links to the details page
 
이제 Post 상세 페이지로 이동 할 수 있는 링크를 만들자. 기존에 만들었던 display tag에 모든 링크는 '#' 로 남겨 두었다. 이 #으로 남겨놓은 링크 부분을 Application.show 액션을 호출 하게끔 해야한다. 이전에 설명 했듯이 Play에서는 @{...} 노테이션을 이용하면 쉽게 링크를 만들 수 있다. (만약 @{...} 의 기능을 눈으로 확인하고 싶다면 렌더링된 html 페이지를 '소스보기'로 한번 확인해 보면 이해가 쉬울 것이다.)
자 이제 /yabe/app/views/tags/diaplay.html 을 수정하자.
 

${_post.title}

 
홈페이지를 refresh 하고 post 의 타이틀을 클릭해보자.


잘나오는 것을 볼 수 있다. 이제 다시 홈으로 돌아갈 수 있도록 blog title 에 홈으로 가는 링크를 달아보자.
/yabe/app/views/main.html 을 열자
 
About this blog

${blogTitle}

${blogBaseline}

 
이제 홈과 Post 상세 페이지를 왔다갔다 할 수 있게 됐다.
 
Specifying a better URL
 
브라우져상에서 보면 상세 페이지의 URL이 아래와 같다.
 
/application/show?id=1
 
이렇게 나오는 이유는 Play에서 route 파일에 특별히 명시해두지 않은 URL의 요청이 오면 'catch all' route를 타기 때문이다.
 
*    /{controller}/{action}            {controller}.{action}  
 
Post 의 show액션을 위해 더 나은 URL을 명시해보자. REST Architecture에서 보면 URL 혹은 URI는 Web에 존재하는 모든 리소스를 표현하는 주소라고 이야기 한다. 그만큼 URL자체가 명확하게 표현하고자 하는바를 나타내는 것이 좋다.
/yabe/conf/routes 파일을 수정하자.
 
GET     /posts/{id}                             Application.show
 
{id} 라고 명시된 부분은 URL path에서 추출되는 것이다. URI pattern에 대해 궁금하다면 이곳을 한번 읽고 넘어가자.
 
브라우져를 refresh하고 URL이 수정한대로 잘 나오는지 보자.


Adding pagination
 
Post간의 이동을 쉽게 하기 위해 pagenation(페이지 번호)을 추가 할 것이다. view template에서 사용할 수 있게Post 클래스에 이전/다음 포스트를 리턴하는 메소드를 추가하자.
/yabe/app/models/Post.java
 
public Post previous() {
    return Post.find("postedAt < ? order by postedAt desc", postedAt).first();
}
 
public Post next() {
    return Post.find("postedAt > ? order by postedAt asc", postedAt).first();
}
 
위 코드는 최적화된 코드는 아니나 지금은 이정도로 충분하다. 이제 show.html template에 pagination 링크를 달아보자.  (#{display /} 바로 전에 추가)
/yabe/app/views/Application/show.html 
 

 
 
자 이제 comment form 을 만들시간이다. Application controller 에 postComment() 메소드를 만드는 것으로 부터 시작해보자.
/yabe/app/controllers/Application.java
public static void postComment(Long postId, String author, String content) {
    Post post = Post.findById(postId);
    post.addComment(author, content);
    show(postId);
}

postComment()에서는 Post 클래스에 미리 만들어 놓았던 addComment()를 사용하고 있다. 그리고 곧바로 show() 액션을 호출한다. 이제, show.html에 Comment 입력할 수 있는 form을 만들자. (html의 기본을 알고 있다면 form에 대해서는 쉽게 이해할 수 있을 것이다.)

show.html (#{display /} 부분 바로앞)에 아래의 코드를 추가하자.
/yabe/app/views/Application/show.html 

Post a comment

#{form @Application.postComment(post.id)}

#{/form}

코드에서 보면 현재 #{form @Application.postComment(post.id)} 라고 호출하고 있다. 실제 postComment 메소드를 살펴보면postComment(Long postId, String author, String content) 라고 되어있고 이는 post.id 외에 나머지 인자는 form 안에 name="author" 부분과 name="content" 부분에서 유저가 입력하는 값이 전달 됨을 알 수 있다.
 

이제 Comment 를 달수 있게 됐다. 

 

Adding validation

현재로썬 Comment를 만들때 아무런 validation 체크를 하지 않고 있다. author 필드와 content 필드는 모두 필수 입력 항목이므로 validation을 추가해보자. Play validation 메카니즘을 이용해 쉽게 http 파라메터의 validation 체크를 할 수 있다.
postComment() 액션 메소드에 @Required validation을 추가해보자.
/yabe/app/controllers/Application.java
 

public static void postComment(Long postId, @Required String author, @Required String content) {
    Post post = Post.findById(postId);
    if (validation.hasErrors()) {
        render("Application/show.html", post);
    }
    post.addComment(author, content);
    show(postId);
}

위처럼 인자에 @Required 어노테이션의 추가한후 Error 가 발생했는지 여부를 확인해 간단히 validation 체크를 할 수가 있다.
validation 객체는 controller에서 사용할 수 있는 헬퍼이고 validation.hasErrors() 메소드를 통해 에러 여부를 확인한다. 즉, 둘 값중 하나라도 값이 비어있으면 hasError()메소드는 false를 리턴한다. validation error 가 있을 경우 render("Application/show.html", post) 를 호출함으로써 명시적으로 post 인자를 전달하면서 show 템플릿을 호출한다. - 사실 단순히 show(postId); 를 호출해줘도 show 템플릿이 보여질 것이다. 하지만 stateless 한 특징상 show액션이 다시 불리면 validation error 정보가 사라지게 된다. 

자 이제 show.html 을 수정해서 validation error 가 있을 경우 저장되어있는 error 정보를 표시해주자. 
/yabe/app/views/Application/show.html 
 
 

Post a comment

#{form @Application.postComment(post.id)} #{ifErrors}

All fields are required!

#{/ifErrors}

#{/form}

Play 에서는 validation error 가 발생할 경우 뷰에서 사용할 수 있는 몇가지 유용한 태그를 제공한다. 위에서 사용한 #{ifErrors} 태그는 validation error 가 발생했을 경우에만 true 가 된다. #{else} 구문과 함께 사용할 수 있으며, 관련 다른 태그로 #{errors} - 순환을 하며 에러 메세지를 출력가능, #{error '필드 이름'/} - 특정 필드의 에러 메세지 출력할때 사용 등이 있다.

또 한가지 주목할 부분은 각 필드에 ${params.author} , ${params.content} 를 사용했다는 것이다. 이것은 postComment() 액션 호출시 넘겨진 파라메터들을 기억해서 값이 있을 경우 해당 필드에 표시해주는 역할을 한다. 

이제 포스팅을 하는 사용자에게 좀 더 그럴듯한 UI 를 제공하기 위해 에러가 날 경우 자동으로 포커싱해주는 약간의 JavaScript 코드를 넣겠다. 우선 [JQuery][JQuery Tools Expose] 자바스크립트 라이브러리 두개를 다운로드 받자. 그리고 yabe/public/javascripts 폴더에 복사한다. 
이제 라이브러리를 넣기 위해 main.html 을 아래와 같이 수정하자. (</head> 바로 위에 넣자)


    


이제 이 스크립트들을 사용할 수 있게됐다. show.html 템플릿을 아래처럼 수정하자.(show.html 맨 아래에 추가한다.)




예전보다 상당히 좋아졌다. 우리는 여기에 두 가지만 더 구현해 보겠다. 첫째로 성공적으로 comment 를 달았을 경우 성공했다는 메세지를 표시해보자. 우리는 flash scope 를 이용해 구현할 것이다. flash scope 란 ruby on rails 의 flash 와 거의 동일하다. 즉, 특정 컨트롤러의 액션에서 flash 메세지를 만들고 다음 액션의 처리까지만 메세지가 유지된다. 액션 A에서 만들어진 flash 메세지는 곧 이어 호출되는 액션 B에서 까지만(액션B의 template 포함) 유효한것이다. 

이제 postComment 액션을 수정해서 flash 에 성공 메세지를 추가하자. 

public static void postComment(Long postId, @Required String author, @Required String content) {
    Post post = Post.findById(postId);
    if(validation.hasErrors()) {
        render("Application/show.html", post);
    }
    post.addComment(author, content);
    flash.success("Thanks for posting %s", author);
    show(postId);
}

이제 flash scope 동안 전달된 메세지를 표시하기위해 show.html 을 수정하자. (show.html 가장 위에 추가한다)

…
#{if flash.success}
    

${flash.success}

#{/if} #{display post:post, as:'full' /} …


이제 마지막으로 postComment 액션을 위한 깜~~끔한 URL 을 새로 정의하자. (현재는 catch all route를 사용한다.)
/conf/routes 파일을 아래 처럼 수정하자.
POST    /posts/{postId}/comments                Application.postComment
Play framework으로 블로그 개발하기 (4) - Viewing and posting comments (끝)
저작자 표시
신고
Posted by AppleSherbet
 Building the first screen 
 
  전 시간에서 Model (데이터 베이스) - User, Post, Comment 를 만들어 보았다. 이제 드디어 우리 블로그의 첫 페이지를 만들어 보자. 누구나 그렇듯 UI가 보여야 개발하는 재미가 생기고 특히 요즘은 개발 기술만큼이나 UI가 중요하게 여겨지는 시대가 온 것 같다. 아래의 이미지는 우리가 만들 블로그의 Mock-up 이미지이다.


 
 Bootstrapping with default data


  시작하기 전에 먼저 test data를 로드 하는 부분을 추가하자. 아무리 블로그를 이쁘게 만든다고 해도 data가 없다면 블로그가 횡해 보이지 않겠는가! 또한 아직 아무런 UI도 만들지 않았기 때문에 우리가 포스팅을 할 수 조차 없다. 데이터를 DB에 로딩하는 가장 좋은 방법은 예전에도 설명했던 Fixture 를 이용하는 방법이다. 어플리케이션이 로드할때 Fixture를 이용해 초기 데이터를 DB에 저장하는 것이다. 그렇게 하기 위해
/yabe/app/Bootstrap.java 파일을 만들어보자.
import play.*;
import play.jobs.*;
import play.test.*;
 
import models.*;
 
@OnApplicationStart
public class Bootstrap extends Job {
 
    public void doJob() {
        // Check if the database is empty
        if(User.count() == 0) {
            Fixtures.loadModels("initial-data.yml");
        }
    }
 
}
  눈치 챘겠지만 우리가 주목해야할 부분은
@OnApplicationStart 어노테이션이다. 이 어노테이션은 Play로 하여금 어플리케이션이 처음 시작할때 BootStrap 클래스의 doJob() 메소드를 자동으로 수행하도록 해준다. test 시 사용했던 data.yml 파일을 yabe/conf/initial-data.yml 로 복사하자. 이미 만들어 놓은 것이 있기 때문에 그대로 사용하면 된다. 이제 play run 커맨드를 이용해 http://localhost:9000 페이지가 브라우져에 별 에러 없이 나오는지 확인해보자.

 The blog home page

  이제 진짜 블로그 페이지를 만들어보자. 맨 처음 설명했었던 어떻게 index.html이 렌더링 되는지 기억하는가? 요약하면 사용자가 브라우져를 통해 / (루트) URL에 접속하면 request를 받은 play 서버는 route 파일을 참조해
controllers.Application.index() 액션 메소드를 수행한다. index() 액션에서는 다시 render() 함수가 호출되고 render 함수는 index와 같은 이름을 가진 /yabe/app/views/Application/index.html 을 찾아 랜더링을 수행한다.
이제 index 코드를 먼저 우리의 블로그에 맡게 수정해보자.
/yabe/app/controllers/Application.java 파일을 열고 index() 액션 메소드를 Post의 리스트를 로딩하도록 수정한다.
package controllers;
 
import java.util.*;
import play.*;
import play.mvc.*;
import models.*;
 
public class Application extends Controller {
 
    public static void index() {
        Post frontPost = Post.find("order by postedAt desc").first();
        List<Post> olderPosts = Post.find(
            "order by postedAt desc"
        ).from(1).fetch(10);
        render(frontPost, olderPosts);
    }
}
  코드에서 볼 수 있듯이 render 메소드로 Object type을 넘길 수 있다. 또한 넘길 수 있는 파라메터 수의 제한도 없다. Model 을 상속 받으므로써 사용할 수 있는 find() 헬퍼 메소드의 사용 또한 주목해서 보자. first() 메소드는 쿼리 결과 리스트의 첫번째 Post를 리턴하고, from~fetch() 메소드는 특정 번호부터 10개의 Post들을 리런해준다. frontPost는 가장 최신 포스팅을 의미하며 olderPosts는 최신 10개의 포스팅들을 의미한다.(가장 최신 포스트를 메인화면에 자세히 보여주고 나머지 10개의 최근 포스팅들을 리스트로 보여주려는 의도)
이제
/yabe/app/views/Application/index.html 을 열어서 frontPost와 olderPosts로 저장된 객체를 표시하도록 수정하자.
#{extends 'main.html' /}
#{set title:'Home' /}
 
#{if frontPost}
    

${frontPost.title}

${frontPost.content.nl2br()}
#{if olderPosts}

Older posts from this blog

#{list items:olderPosts, as:'oldPost'}

${oldPost.title}

#{/list}
#{/if} #{/if} #{else}
There is currently nothing to read here.
#{/else}
  index.html 에서는 예상했듯이 render메소드로 전달된 frontPost, olderPosts 객체를 그대로 사용할 수 있다. 물론 frontPost.author, frontPost.postedAt 등의 값에 접근도 가능하다. 여기서
frontPost.postedAt.format('MMM dd') 를 주목하자. 우리는 따로 format('MMM dd') 를 정의해준 적이 없다. 또한  frontPost.postedAt은 Date 타입인데 JDK Java 의 Date 타입에는 format이라는 메소드가 있지도 않다. 결론부터 말하면 Java object extentions 라고 해서 Play에서는 특정 타입의 변수에 유용한 메소드들을 추가적으로 정의해 두어 런타임시에 view에서 쉽게 사용할 수 있다.
 
  Custom extension또한 정의가 가능하다. 즉, 개발자가 특정 java object type에 필요한 메소드를 추가적으로 정의하면 어디서든 해당 type의 변수에서 custom extension을 쓸 수 있다. 이러한 extension 기능은 view의 코드를 깔끔하게 유지시켜주고 중복또한 제거해 줄 수 있어서 매우 유용하다. 이곳에서 간단하게 읽어보고 진행하는 것을 추천한다.

  Java object extentions을 사용한 부분이 한군데 더 있다. 바로
frontPost.comments.size().pluralize() 이다 이 메소드는 size()가 복수의 값을(>=2) 리턴할 경우 문자열에 "s"를 추가해 복수를 표현해 준다.
  또 한가지 봐야 할 부분은 Groovy 를 사용했다는 점이다.
#{list items:olderPosts, as:'oldPost'} 가 대표적인 예인데 Java와 문법이 흡사하다. 즉, olderPost 리스트에 있는 item 갯수만큼 순환을 돌게되며 #{list }... #{/list} 안에서는 oldPost 로(as:'oldPost' 에 의해) 접근 할 수 있다. 나머지 문법들도 jsp 와 같은 스크립트 언어를 사용해 봤다면 쉽게 이해할 수 있을 것이다.

자, 이제 브라우져에서 refresh를 해보자.


UI는 좀 별루지만 아무튼 잘 보이고 있다.

  이미 눈치챈 분들도 있겠지만 index.html을 보면 거의 같은 코드가 중복되고 있다. 또한 앞으로 우리는 그와 같은 코드를 몇몇 다른 코드에서 다시 사용할 것이므로 중복된 코드를 따로 분리해보자. Play에서는 이런 기능을 지원하기 위해 Play tag 를 지원해준다. Tag를 만들기 위해
/yabe/app/views/tag/display.html 파일을 만들자. display tag는 두개의 파라메터를 인자로 갖는다: Post object와 display mode(여기선 "home", "teaser", "full). Display tag 를 아래처럼 구현해준다.
*{ Display a post in one of these modes: 'full', 'home' or 'teaser' }*
 

${_post.title}

#{if _as != 'teaser'}
Detail:
${_post.content.nl2br()}
#{/if}
#{if _as == 'full'}

${_post.comments.size() ?: 'no'} comment${_post.comments.size().pluralize()}

#{list items:_post.comments, as:'comment'}
Detail:
${comment.content.escape().nl2br()}
#{/list}
#{/if}
  /yabe/app/views/tag/ 에 만드는 것은 관례이므로 따라야 한다. display tag는 _as 값에 따라 다르게 결과를 표시해주는 것을 볼 수 있다. 이제 중복 코드를 분리했으니 다시 index.html 을 아래 처럼 수정하자.
#{extends 'main.html' /}
#{set title:'Home' /}
#{if frontPost}
    #{display post:frontPost, as:'home' /}
    #{if olderPosts.size()}
        

Older posts from this blog

#{list items:olderPosts, as:'oldPost'} #{display post:oldPost, as:'teaser' /} #{/list}
#{/if} #{/if} #{else}
There is currently nothing to read here.
#{/else}
  index 코드를 보면 #{display post:olderPosts, as:'oldPost'} 와 같이 post, as 란 이름으로 파라메터를 전달한다. display tag에서는 이와 같이 전달된 파라메터에 '_' 를 앞에 붙인 _post, _as 로 접근해 해당인자를 사용할 수 있다. 다시 브라우져를 refresh 해보자.

 Improving the layout

  index.html 을 보면 main.html 을 상속(extends)받고 있는 것을 볼 수 있다. 모든 view에서 공통으로 보여주고 싶은 layout 즉, blog title, 로그인 link 등을 추가하기 위해
main.html을 수정하자.

/yabe/app/views/main.html
    
        #{get 'title' /}		
        
        
        
    
    
        
        
        
        
#{doLayout /}
  자 이제 다시 브라우져를 refresh 해보자. blogTitle, blogBaseLine 부분을 제외하고는 잘 동작 하는 것 같다. 잘 동작하도록 해주기 위해 render(...) 메소드에 해당 파라메터를 넘겨주는 방법이 있다 하지만 main.html 은 모든 view에서 공통으로 사용될 것이므로 조금 다른 방법으로 파라메터를 넘겨보자. Application controller에 아래와 같이 addDefault() 메소드를 추가하자.
@Before
static void addDefaults() {
    renderArgs.put("blogTitle", Play.configuration.getProperty("blog.title"));
    renderArgs.put("blogBaseline", Play.configuration.getProperty("blog.baseline"));
}
  renderArgs에 put을 통해 넣은 파라메터는 모든 template 에서 사용가능하다는 장점이 있다. @Before 어노테이션을 이용해 다른 액션이 불리기 전에 가장 먼저 수행 하도록 했다. 여기서 우리는 Play.configuration 객체를 이용해 파라메터를 가져오고 있는 것을 볼 수 있다. 아직 configuration 파일에 필요한 정보를 넣지 않았으므로 /yabe/conf/application.conf에 configuration 정보를 넣어주자.
# Blog engine configuration
# ~~~~~
blog.title=Yet another blog
blog.baseline=We will write about nothing
  이제 Play.configuration.getProperty 메소드는 정상적으로 설정 정보 즉, Yet another blog와 We will... 에 대한 문자열 정보를 읽어올 수 있을 것이다.
다시 브라우져를 refresh 해보자. 잘 나오는가!
 

Adding some style
 

  자 이제 블로그의 홈페이지가 거의 완성되어 간다. 하지만 정말 볼품이 없다. style을 추가해서 블로그를 멋지게 바꿔보자. 좀 전에 main.html 에 이미 우리는 /public/stylesheets/main.css 경로를 추가 했었다. 그러므로 해당 파일을 /public/stylesheets/ 폴더에 넣어주기만 하면 될 것이다.
  css 에 대해 이곳에서 설명하는 것은 무리가 있으므로 생략하겠다. 혹시 css에 대해 전혀 모른다면 wikipedia 에서 간단히 개념 정도는 알고 넘어가자.

  그럼 이곳에서 main.css파일을 다운받아
/public/stylesheets/ 폴더에 넣어주자. 다시 브라우져를 refresh 해보자. 몇배 알흠다워진 우리 블로그를 볼 수 있다.

 

저작자 표시
신고
Posted by AppleSherbet
Play2 A first iteration of the data model

주말에 열심히 노느라 2탄을 이제야 하게된다.
자~ 이제 우리는 처음으로 우리의 YABE 블로그에 Model 을 하나 추가하겠다.

JPA 소개
Model 레이어는 대부분의 잘 디자인 된 어플리케이션의 가장 중심부에 위치한다. 그만큼 중요한 역할을 하고 있다.
우리가 만들려고하는 어플리케이션이 블로그인 만큼 User, Post 그리고 Comment 등등의 모델 클래스들을 만들게 될 것이다.
Play가 자바 기반인 만큼 우린 database를 직접 엑세스 하는 방식이 아닌
ORM(Object Relational Mapper) 방식을 사용하여 엑세스 할 것이다.
ORM을 이용하면 예측하기 힘든 실수를 미연에 방지할 수 있다.
Java Persistence API(JPA)는 ORM(Object Relational Mapper) 을 정의해놓은 자바 스팩 이다. 그리고 Play 에서는 JPA를 사용하여 만들어진 Hibernate 를 이용해 DB를 엑세스 한다. 만약 JPA나 Hibernate에 대한 사전 지식이 없다해도 이번 장을 이해하는데 전혀 어려움이 없을 것이다. (혹시 이런 기술에 대해서 전혀 처음 들었다면 wikipedia 등을 한번 참고 하는 것도 도움이 될 것이다.)
** ORM: 데이터베이스 연계처리를 위하여 기존의 SQL에 의존하는 것이 아니라, 직접 테이블의 컬럼을 자바 Class에 매핑하거나XML형태의 SQL을 실행하여 처리를 수행하는 Persistence Layer를 담당하는 Framework 개발모델

시작하면서 부터 머리아프니 이제 ORM, JPA, Hibernate 이런건 잠시 잊도록 하자. 우리는 Database 엑세스 또한 순수하게 자바코드상에서 할 것이다. JPA에 대해 궁금하다면 이곳에서~

The User class

우리 블로그는 User 클래스를 만드는 것으로 부터 시작해보자. /yabe/app/models/User.java 를 만들고 아래와 같이 구현한다.
package models;
import java.util.*;
import javax.persistence.*;
import play.db.jpa.*;

@Entity
public class User extends Model {

    public String email;
    public String password;
    public String fullname;
    public boolean isAdmin;

    public User(String email, String password, String fullname) {
        this.email = email;
        this.password = password;
        this.fullname = fullname;
    }
}
User 클래스에 명시된 @Entity 어노테이션과 상속받고 있는 Model 클래스를 주목해서 보자.
@Entity 어노테이션은 이 클래스가 JPA 엔티티라는 것을 표시하는 것이다. 그리고 Model 클래스는 우리에게 유용한 JPA Helper 를 제공해준다. 선언된 모든 field 는 자동으로 database의 field로 맵핑된다. 우리는 각각의 필드에 대한 setter 와 getter를 만들필요가 없음을 유의하자.

[여기서 잠깐] 
1.위와 같이 구현하면 기본동작으로 DB 테이블 이름은 User 가 된다. 만약 테이블 이름을 바꾸고 싶으면 @Table(name="blog_user") 와 같은 어노테이션을 User 클래스에 붙여주면 된다. 
2. 반드시 Model 클래스를 상속 받아야 하는 것은 아니다 다만, 상속 받는 것이 빠르고 쉬운 개발을 가능하게 해준다.(많은 JPA 헬퍼를 이용)

  JPA를 기존에 사용해본 사람이라면 JPA가 @id 프로퍼티를 필요로 한다는 것을 알고 있을 것이다. 하지만 Play에서는 Model 클래스가 자동으로 생성된 id 값을 제공한다. 자바 개발자의 경우 위의 코드에 모든 field가 public으로 선언된 것을 의아하게 생각할지도 모른다. 일반적인 자바 클래스에서는 인스턴스 변수의 경우 private으로 선언하고 getter와 setter를 만들기 때문이다. 하지만 Play의 모델에선 이와 같은 부분을 알아서 처리해주며 자동으로 getter와 setter메소드 또한 생성해 준다.
 
  이제 브라우져로 돌아가서 refresh를 눌러보자. 잘 못 구현한 부분이 없다면 아무일도 일어나지 않을 것이다. 하지만 우리는 단지 Model클래스만 생성한 것이므로 어플리케이션에 별다른 변화는 없다.

  첫번째 테스트 만들기 User 클래스를 만들었으니 Test 코드를 짜기에 적절한 때가 왔다. 테스트를 하기 위해 잠시 서버를 종료하고 (Ctrl + c) 다음과 같이 입력하자.

Play test


Play test 명령어는 Play run 명령어와 거의 동일하다. 다만, test 가 가능하게 하는 test runner 모듈을 함께 로딩한다. Test 모드로 Play를 시작하면 Play는 framework ID를 자동으로 %test 로 바꾼고 해당 ID를 이용해 application.conf 파일을 로딩한다.
** framework ID 에 대해서는 이곳을 참고하자. (아주 간단하므로 꼭 읽어보자)

이제 브라우져를 열고 URL에 http://localhost:9000/@tests 라고 입력 해보자.
기본 테스트를 선택하고 테스트 시작 버튼을 눌러보자. 모든 테스트가 성공할 것이며 성공한 테스트는 초록색 불이 들어온다.(실제로 이 테스트는 어떠한 테스트도 수행하지 않는다. 비어있으므로) 


모델을 테스트 하기위해 JUnit test 를 사용한다. /yabe/test/ 폴더에 보면 이미 BasicTests.java 파일이 존재한다. 파일을 열어보자
import org.junit.*;
import play.test.*;
import models.*;
 
public class BasicTest extends UnitTest {
    @Test
    public void aVeryImportantThingToTest() {
        assertEquals(2, 1 + 1);
    }
}

이제 불필요한 aVeryImportantThingToTest()란 메소드를 지우고 새 메소드를 만들자. 우리가 만들 새 테스트는 새로운 유저를 만들고 데이터 베이스에 저장해서 다시 추출하는 테스트이다.
@Test
public void createAndRetrieveUser() {
    // Create a new user and save it
    new User("bob@gmail.com", "secret", "Bob").save();
    
    // Retrieve the user with e-mail address bob@gmail.com
    User bob = User.find("byEmail", "bob@gmail.com").first();
    
    // Test 
    assertNotNull(bob);
    assertEquals("Bob", bob.fullname);
}
위 코드에서 볼 수 있듯이 Model 클래스는 우리에게 아주 유용한 메소드를 제공한다: save() find(). (끊임 없이 사용하게 될 메소드)
[여기서 잠깐]
Play에서 제공하는 더 많은 메소드를 구경하고 싶다면 play JPA support chapter를 참고하라
 
  다시 브라우져로 돌아가 refresh 를 누르고 BasicTest를 선택한 후 Start 버튼을 눌러보자. 실수가 없었다면 모두 초록색 불이 들어올 것이다.   한가지 테스트를 더 추가하자. 이번 테스트는 특정 이름과 비밀번호를 가진 유저가 있는지 체크하는 테스트이다.
User.java 를 열고 connect() 메소드를 추가하자.
public static User connect(String email, String password) {
    return find("byEmailAndPassword", email, password).first();
}
그리고 다시 /yabe/test/BasicTests.java 파일에 tryConnectAsUSer() 함수를 추가하자.
@Test
public void tryConnectAsUser() {
    // Create a new user and save it
    new User("bob@gmail.com", "secret", "Bob").save();
    
    // Test 
    assertNotNull(User.connect("bob@gmail.com", "secret"));
    assertNull(User.connect("bob@gmail.com", "badpassword"));
    assertNull(User.connect("tom@gmail.com", "secret"));
}
다시 브라우져로 테스트를 돌려보자 :)

Post 클래스

Post 클래스는 블로그의 하나의 Post를 표현하는 Entity 객체이다. /yabe/app/models/Post.java 를 만들고 아래와 같이 구현한다.
package models;
 
import java.util.*;
import javax.persistence.*;
import play.db.jpa.*;
 
@Entity
public class Post extends Model {
 
    public String title;
    public Date postedAt;
    
    @Lob
    public String content;
    
    @ManyToOne
    public User author;
    
    public Post(User author, String title, String content) {
        this.author = author;
        this.title = title;
        this.content = content;
        this.postedAt = new Date();
    }
} 

우리의 위 코드에서 긴 글(여기서는 블로그 포스트의 내용)을 large text database에 저장하기 위해 @Lob이라는 JPA 어노테이션을 사용한다. 그리고 User 클래스와의 Relation을 표현하기 위해 @ManyToOne 어노테이션을 사용한다. 이것은 여러 블로그 포스트가 한명의 유저의 포스트가 될 수 있음을 의미한다. 혹시 이부분이 이해가 되지 않는다면 관계형 데이터베이스의 기초를 들여다 보기 바란다.
  이제 Post class가 잘 동작하는지를 보기 위해 새로운 테스트 케이스를 추가하자. 그전에 먼저 해야할 것이 있다. 현재 구현상 테스트를 수행할 때마다 데이터 베이스에 새로운 데이터가 저장된다. 이와 같은 경우 테스트를 수행하면 할 수록 불필요한 데이터가 계속 쌓이게 될것이다.
  이와 같은 문제를 방지하기 위해 BasicTests.java에 다음과 같이 setup() 메소드를 추가하자.
public class BasicTest extends UnitTest {
 
    @Before
    public void setup() {
        Fixtures.deleteDatabase();
    }
    …
}

@Before 어노테이션은 JPA의 핵심 어노테이션 중 하나이다. @Before 어노테이션에 의해 테스트 메소드가 수행되기 전에 setup() 메소드가 최초로 한번 수행되게 된다.
Fixture 클래스는 database를 쉽게 조작하기 위해 만들어진 play의 utility 클래스로 대표적으로 아래 두개의 기능을 갖는다:
- deleteAll(): testing을 위해 database를 초기화 시킨다.
- load(filename): YAML 포맷의 초기 데이터를 읽어와 database에 저장한다. 

  자 이제 아래 처럼 새로운 테스트 케이스를 추가한다.
@Test
public void createPost() {
    // Create a new user and save it
    User bob = new User("bob@gmail.com", "secret", "Bob").save();
    
    // Create a new post
    new Post(bob, "My first post", "Hello world").save();
    
    // Test that the post has been created
    assertEquals(1, Post.count());
    
    // Retrieve all posts created by Bob
    List<Post> bobPosts = Post.find("byAuthor", bob).fetch();
    
    // Tests
    assertEquals(1, bobPosts.size());
    Post firstPost = bobPosts.get(0);
    assertNotNull(firstPost);
    assertEquals(bob, firstPost.author);
    assertEquals("My first post", firstPost.title);
    assertEquals("Hello world", firstPost.content);
    assertNotNull(firstPost.postedAt);
}
Adding Comments

자 이제 마지막으로 추가해야 할것은 바로 Comment 이다. 쉽게 말하면 댓글기능을 추가해보자. Comment 모델 클래스는 매우 직관적이다.
package models;
 
import java.util.*;
import javax.persistence.*;
import play.db.jpa.*;
 
@Entity
public class Comment extends Model {
 
    public String author;
    public Date postedAt;
     
    @Lob
    public String content;
    
    @ManyToOne
    public Post post;
    
    public Comment(Post post, String author, String content) {
        this.post = post;
        this.author = author;
        this.content = content;
        this.postedAt = new Date();
    }
 
}

Post 와 마찮가지로 @Lob 어노테이션으로 Large text를 저장하고 있고, 하나의 Post 에 여러개의 댓글을 달수 있다는 의미로 @ManyToOne 어노테이션을 사용했다. Comment 모델 글래스를 완성했으니 다시 Test case를 작성해보자 /yabe/test/BasicTests.java 파일에 postComments() 란 테스트 메소드를 추가한다.
@Test
public void postComments() {
    // Create a new user and save it
    User bob = new User("bob@gmail.com", "secret", "Bob").save();
 
    // Create a new post
    Post bobPost = new Post(bob, "My first post", "Hello world").save();
 
    // Post a first comment
    new Comment(bobPost, "Jeff", "Nice post").save();
    new Comment(bobPost, "Tom", "I knew that !").save();
 
    // Retrieve all comments
    List<Comment> bobPostComments = Comment.find("byPost", bobPost).fetch();
 
    // Tests
    assertEquals(2, bobPostComments.size());
 
    Comment firstComment = bobPostComments.get(0);
    assertNotNull(firstComment);
    assertEquals("Jeff", firstComment.author);
    assertEquals("Nice post", firstComment.content);
    assertNotNull(firstComment.postedAt);
 
    Comment secondComment = bobPostComments.get(1);
    assertNotNull(secondComment);
    assertEquals("Tom", secondComment.author);
    assertEquals("I knew that !", secondComment.content);
    assertNotNull(secondComment.postedAt);
}
테스트 코드를 보면 특정 Post 에 해당하는 여러개의 Comment를 저장하기 위한 방법이 복잡하다는 걸 느낄 것이다. 특히 각각의 Comment를 저장하기 위해 Comment마다 save(쿼리)를 호출 해주고 있다. 조금 더 좋은 접근을 위해 Post 클래스를 수정해보자. Post 클래스에 comment 필드 삽입:
...
@OneToMany(mappedBy="post", cascade=CascadeType.ALL)
public List<Comment> comments;
 
public Post(User author, String title, String content) { 
    this.comments = new ArrayList<Comment>();
    this.author = author;
    this.title = title;
    this.content = content;
    this.postedAt = new Date();
}
...
mappedBy="post" 는 Comment 클래스의 post field 가 이 두 객체(테이블)의 관계를 소유(owning) 하고 있다는 것을 의미한다. 쉽게 말해 두 객체가 관계를 맺고 그 중 한개가 '주'가 되는것이다. JPA에서 양방향의 어떤 관계를 정의할때는 이와 같이 관계의 주가 되는 객체를 정해주는 것이 중요하다. 지금과 같은 경우는 비록 여러 Comment 가 하나의 Post에 속해있지만 '주'가 되는것은 Comment 쪽의 post field로 정해줬다. (inverse relation 이라고 부르는 것 같다) cascade=CascadeType.ALL 을 정해주므로써 Post 하나를 지우면 그 포스트에 대한 모든 Comment 들을 자동으로 지워주게 된다.

자 이제 모델 객체간의 관계(relationship) 정의는 모두 끝났으니 Post 클래스에 Comment를 쉽게 추가 할 수 있는 헬퍼(helper) 메소드를 추가하자.
public Post addComment(String author, String content) {
    Comment newComment = new Comment(this, author, content).save();
    this.comments.add(newComment);
    this.save();
    return this;
}
이제 잘 동작하는지 확인하기 위한 새로운 테스트 코드를 넣자
@Test
public void useTheCommentsRelation() {
    // Create a new user and save it
    User bob = new User("bob@gmail.com", "secret", "Bob").save();
 
    // Create a new post
    Post bobPost = new Post(bob, "My first post", "Hello world").save();
 
    // Post a first comment
    bobPost.addComment("Jeff", "Nice post");
    bobPost.addComment("Tom", "I knew that !");
 
    // Count things
    assertEquals(1, User.count());
    assertEquals(1, Post.count());
    assertEquals(2, Comment.count());
 
    // Retrieve Bob's post
    bobPost = Post.find("byAuthor", bob).first();
    assertNotNull(bobPost);
 
    // Navigate to comments
    assertEquals(2, bobPost.comments.size());
    assertEquals("Jeff", bobPost.comments.get(0).author);
    
    // Delete the post
    bobPost.delete();
    
    // Check that all comments have been deleted
    assertEquals(1, User.count());
    assertEquals(0, Post.count());
    assertEquals(0, Comment.count());
}
모두 초록색으로 나오는 것을 볼 수 있다.


Use the Fixture to write more complicated test 좀더 복잡한 테스트를 위해 사전에 준비된 데이터를 이용할 수 있다. Fixture를 이용하면 테스트를 수행하기전에 필요한 데이터(yaml file)를 미리 로딩해서 테스트에 사용할 수 있다. Fixtures.loadModels() 메소드는 yaml 파일으로 부터 데이터를 로드해 database에 로딩해주는 역할을 한다. data.yml 파일의 크기가 좀 크기 때문에 이곳에서 다운받아서 쓰기바란다. data.yml은
/yabe/test/data.yml 에 저장하면 된다. 자 이제 우리는 준비된 데이터를 가지고 assertion 테스트를 수행 할 수 있다. /yabe/test/BasicTests.java 파일에 fullTest() 함수를 아래와 같이 추가하자.
@Test
public void fullTest() {
    Fixtures.loadModels("data.yml");
 
    // Count things
    assertEquals(2, User.count());
    assertEquals(3, Post.count());
    assertEquals(3, Comment.count());
 
    // Try to connect as users
    assertNotNull(User.connect("bob@gmail.com", "secret"));
    assertNotNull(User.connect("jeff@gmail.com", "secret"));
    assertNull(User.connect("jeff@gmail.com", "badpassword"));
    assertNull(User.connect("tom@gmail.com", "secret"));
 
    // Find all of Bob's posts
    List<Post> bobPosts = Post.find("author.email", "bob@gmail.com").fetch();
    assertEquals(2, bobPosts.size());
 
    // Find all comments related to Bob's posts
    List<Comment> bobComments = Comment.find("post.author.email", "bob@gmail.com").fetch();
    assertEquals(3, bobComments.size());
 
    // Find the most recent post
    Post frontPost = Post.find("order by postedAt desc").first();
    assertNotNull(frontPost);
    assertEquals("About the model layer", frontPost.title);
 
    // Check that this post has two comments
    assertEquals(2, frontPost.comments.size());
 
    // Post a new comment
    frontPost.addComment("Jim", "Hello guys");
    assertEquals(3, frontPost.comments.size());
    assertEquals(4, Comment.count());
}
YAML에 대해 궁금하다면 여기를 참고로 하자. YAML 자체는 정말 간단하고 YAML이 Play에만 사용되는 것이 아니므로 한번 훑어 보기를 추천한다.

Play framework으로 블로그 개발하기 (2) - A first iteration of the data model (끝)
저작자 표시
신고
Posted by AppleSherbet

Play! framework는 쉽게 말해 요즘 뜨는(?) 자바 기반 웹 프레임워크이다. 
아 또 무슨 프레임워크야???!!! 하는 분들이 많겠지만 늘 그렇듯 그냥 재밌고 신기하니 공부하기도 한다. 나처럼...
한 2~3년전 대학원에서 공부할때 Ruby on Rails를 공부한적이 있다. 이유는? 1번 Ruby 언어가 신기하고 매력적이어서 2번 Rails 또한 매력적이고 신기해서.  Play framework은 겉모습은 rails의 자바 버전이라고 할만큼 유사하다. 
얼마전 자바 개발자 컨퍼런스를 갔다가 양수열님의 Play framework에 대한 소개를 듣고 아~ 이건 또 뭘까 하고 바로 책을 사들고 왔다.
현재 국내에 번역서는 'Play Framework Cookbook' 이란 책이 1권 있으나 처음 시작하기에는 적절하지 못한 것 같다. 
그래서 바로 책은 덥어두고(항상 반복되는...) framework 홈페이지 가이드 및 ebook을 한권 구매해서 차근차근 공부를 시작했다.
이번에 시작할 튜토리얼은 홈페이지에 있는 Play Framework로
"YABE" - Yet Another Blog Engine" 블로그를 만드는 튜토리얼을 해석한 내용으로 튜토리얼 자체가 자세한 설명이 빠져있는 관계로 약간의 자세한 설명과 함께 진행하려 한다.
난 항상 어떤일의 기획/과정 보단... 끝까지 진행하느냐가 더 중요하다고 생각한다. 이번 튜토리얼을 끝까지 진행하기를 바라며 시작해본다. 

** 제가 실제 이 튜토리얼을 따라가보면서 내용을 업데이트 할 예정이라서 업데이트 주기가 조금 늦을 수도 있습니다. 양해 바랍니다. :)

프로젝트 시작하기

소개
 
이 튜토리얼은 Play 어플리케이션을 만드는데 필요한 대부분의 기술을 다루면서 설치부터 완성까지 모든 과정을 함께 할 것이다. 각오하시라...ㄷㄷㄷ

YABE 

Yet Another Blog Engine - YABE. 우리가 만들 블로그 엔진 이름이다. 일반적인 브로그가 포함할 기술을 대부분 갖게 될 것이며 차후 블로그에 Admin 계정등을 추가하여 만들 예정이다. (블로그 자체는 평범하나 실제 웹 개발에 필요한 많은 기술들을 사용하는데 적절한 예제라고 생각된다)

준비사항(Prererquisites)

Play framework 가 java기반이므로 당연히 JDK가 설치되어 있어야 한다.  JDK의 최신 버전은 오라클 홈페이지에서 다운받을 수 있다. 다운로드 링크: http://www.oracle.com/technetwork/java/javase/downloads/index.html
이 튜토리얼에서는 JAVE EE 에 대한 지식은 요구하고 있지 않으며 JAVA 에 대한 지식과 html/css 등의 개념을 조금만 알고 있으면 어느정도 따라가는데 무리가 없을 것이다.
개발을 진행하기 위해서는 IDE또한 필요하다. 다음은 사용하기 무리가 없는 몇몇 IDE 리스트이다.
추천 IDE: Eclipse, Netbeans, Textmate, E-TextEdit, Intellij IDEA

써본봐로는 기존의 자바 개발자라면 역시 Eclipse가 편할 것이다. 각각의 프로젝트를 Play framework에 맞게 설정하는 방법은 이곳: http://www.playframework.org/documentation/1.0.1/ide 에 잘 설명돼어있다. 아주 간단한 방법으로 가능하니 설명은 생략하겠다.

Play 설치하기

Play를 설치하는 것은 아주 간단하다. 홈페이지에서 최신 안정 버전을 다운로드 받아 원하는 디렉토리에 압축을 풀면 된다.
다만 play 폴더의 홈 디렉토리를 os별로 path를 지정해 주어야 한다. 예) 윈도우의 내컴퓨터 - 속성 - 고급 - 환경변수 - path 에 play경로 추가. 
주의: window 유저라면 설치 경로에 공백이 없는 것을 추천한다. (이유는 모르겠음.. 하지만 한글역시 없는것을 추천)
자! 설치가 됐다면 이제 command line(윈도우면 도스창, Mac이면 terminal) 에 play 라고 쳐보자. OS와 상관없이 help 메세지가 나오면 성공적으로 설치된 것이다. 

Project 만들기

이제 Play가 성공적으로 설치됐으므로 우리 프로젝트를 생성해보자. Comment line 에서 원하는 디렉토리 (프로젝트를 생성하고 싶은 디렉토리)로 이동해 다음과 같이 쳐보자.
 
~$ play new yabe
다음과 같이 프롬프트에 어플리케이션의 full name 을 입력하라고 나올 것이다. Yet Another Blog Engine 이라고 입력하자.
 

 
이제 play 는 자동으로 yabe라는 디렉토리를 만들고 그 안에 필요한 여러 서브 디렉토리 및 파일들을 만들었다.  
생성된 디렉토리에 대해 간단히 알아보자

app/ - 어플리케이션의 core 부분으로 (가장 중요한 역할을 한다는 말씀) model, controller, view 의 세부 디렉토리로 나뉜다. 이 세 폴더에 .java 파일들이 존재하게 된다.

conf/ - 어플리케이션의 모든 설정 파일이 들어있는 디렉토리이다. 대표적으로 application.conf 파일, route파일 그리고 messages 파일등이 존재하게 된다. application.conf 파일을 열어보면 대부분은 어플리케이션과 관련된 많은 설정들이 주석 처리 되어있는 것을 볼 수 있다.(db, cache, JPA 등등) route파일은 특정 컨트롤러에 대한 URL을 매핑 해놓은 것으로 차후 다시 설명된다. messages 파일은 다국어 지원을 위한 설정 파일이다.

lib/ - 필요한 자바 라이브러리를 저장하기 위한 곳이다.

public/ -  public으로 사용할 리소스들이 저장되는 곳으로 javascript(js파일), image(이미지 리소스), style (css)의 디렉토리가 생긴다. 

test/ - 이곳에는 모든 어플리케이션 테스트와 관련된 코드가 저장된다.

만약 자바 개발자라면 .class 파일은 어디 저장되는지 궁금할 것이다. 결과적으로 어디에도 없다. Play 는 순수 java 소스 파일만을 사용한다 그리고 이 소스 파일을 컴파일 하는 것은 framework 안에 있는 eclipse compiler 가 담당하게 된다. (개발자는 그런 과정에 대해 모르고 있어도 무방하다.)
** 여기서 말하는 eclipse compiler는 play framework 에서 auto compiling 을 위해 채택한 complier인것 같다. 

위와 같은 구조는 개발과정을 아주 편하게 바꿔준다. 첫번째로 Play는 자동으로 소스 코드가 수정된 것을 감지하고 런타임시에 동적으로 reload (컴파일) 하게 된다. 이것은 우리가 따로 컴파일을 수행한다던가 서버를 재실행 하지 않아도 변경사항을 바로 브라우져로 확인 할 수 있어 매우 생산성이 높아진다. 두번째로 에러가 발생 할 경우 exception 스택이 아닌 정확한 소스의 에러 위치를 찾아내 준다. (물론 콘솔에서는 exception 스택또한 표시해 준다.)
** 실제 play에서는 바이트 코드의 캐쉬를 tmp/ 디렉토리에 저장해두고 어플리케이션을 새로 로드할때 사용한다고 한다. 이는 새로 로드할때의 속도를 빠르게 하기 위함이며 play clean 명령어로 지울 수 있다.
 

Application 시작해보기 (Running the application)

이제 어플리케이션을 새로 만들었으므로 실행을 해 볼 수 있다. 방금전 생성한 yabe 디렉토리로 이동 후 콘솔(command line)에서 play run 을 입력해보자.

서버가 시작되고 브라우져를 통해 결과를 볼 수 있다. 브라우져를 실행해 url 창에 http://localhost:9000 을 입력해보자. Welcome 화면이 뜨며 이것은 어플리케이션이 정상적으로 실행 됐다는 것을 의미한다.


이 어플리케이션이 어떻게 이런 페이지를 띄우는지 한번 살펴보자.
브라우져가 URL 을 입력 받으면 웹서버의 즉 Play framework의 /conf/route 파일을 제일 먼저 참조하게 된다. route 파일을 열어 보면 아래와 같은 첫번째 route를 볼 수 있다.
GET     /                                       Application.index
이 route는 단순히 웹서버가 / path로 GET 요청(request)을 받으면 Application 컨트롤러의 index 메소드(play서는 이 index메소드를 액션이라고 부른다.)를 호출 하라는 의미이다. 

일반적으로 자바 어플리케이션을 만들게 되면 우리는 일반적으로 아래와 같은 main 메소드를 만든다. 이는 어플리케이션의 최초 시작지점이기도 하다.
public static void main(String [] args){
 ...
} 
이와는 다르게 Play 에서는 요청 URL에 기반한 여러 시작지점(Entry point)이 있게 된다. 그리고 우린 이러한 메소드를 Action 메소드(액션 메소드) 라고 한다. 이러한 액션 메소드들은 모두 controller 클래스에 정의된다. (index 메소드도 action 메소드 이다.)
즉, URL 에 따라 route 에 맵핑된 액션 메소드 중 하나가 실행 될 수 있는 것이다. 

이제 controller.Application 이란 이름의 컨트롤러가 어떻게 구성돼어있는지 살펴보자. 이를 위해 yabe/app/controllers/Application.java 파일을 열어보자.
package controllers;
 
import play.mvc.*;
 
public class Application extends Controller {
 
	public static void index() {
		render();
	}

}
모든 controller 클래스는 play.mvc.Controller 클래스를 상속 받는다. 이 클래스는 controller 를 위한 많은 유용한 메소드를 제공하고 있다. 그 대표적인 메소드가 위 예제의 index() 메소드에 있는 render() 메소드이다. 
index 메소드는 public static void 로 정의돼있다. 이와 같이 모든 액션 메소드는 public static void 로 정의해야 한다. static을 사용하는 이유는 controller 클래스가 인스턴스화 (new) 되지 않기때문이며, public으로 정의된 것은 play framework에서 URL을 통해 액션 메소드를 수행할 수 있는 권한을 갖기 위함이다. (객체지향의 기본적인 내용이므로 자세한 내용은 생략하겠다.)
위에서 보여지는 index() 액션 메소드는 render() 메소드를 호출 하고 있다. 이는 Play에게 특정 view template를 랜더링하라(보여줘라) 라는 의미이다. 일반적으로 이 템플릿은
/app/view 디렉토리에 있는 html 파일(항상 그렇지는 않다)이 될 것이고 render() 메소드에 의해 적절한 html 파일이 웹 브라우져에 보여지게 될 것이다.   

설정보다 관례가 우선한다(Convention Over Configuration) 란 말이 있다. 레일즈를 공부할 때 처음 나왔던 말이다. 이는 Play framework에서도 그대로 사용된다. 기존의 많은 web framework들은 너무 많은 설정을 필요로 한다. "설정보다 관례가 우선한다" 란말은 우리가 framework를 사용하여 개발할때 어떤 약속들(관례들)을 지켜주면 기존에 필요로 했던 많은 설정들을 대체할 수 있다는 뜻이다. (좀 더 궁금한 분들은 Convention Over Configuration 를 검색해보길 바란다)
여기서
/app/view/index.html이 default template으로 랜더링 되는 이유는 바로 관례가 작용한 예이다. 즉, 우리는 "컨트롤러에 액션 메소드 이름과 mapping 된 template 이름을 동일하게 하자" 라는 관례를 만든것이다.  그래서 index()액션 메소드에서 단지 render()만 호출 했음에도 불구하고 "같은" 이름의 index.html이 불리는 것이다. 

이제 index.html을 열어보자.
Play에서 expression 언어는 Groovy를 사용한다. 이는 개발의 속도/편의를 위해 사용된다. 만약 Groovy에 대한 이해가 없다하더라도 이 튜토리얼을 진행하는데는 무리가 없다.
아래는 많이 사용되는 
expression 언어 문법에 대한 요약설명이다.

Expressions - ${....} 

${...} 는 dynamic (동적인) 내용(contents)를 view에 삽입하는 대표적인 notation이다. 예를 들면 <h1>${myTitle}</h1> 과 같이 view에 넣었다면 myTitle 변수 값에 따라 브라우져에 표시된다. 간단히 변수의 내용을 표시(출력)할때 사용된다.

Tags - #{....}

#{...} 는 view fragment (뷰 조각) 이라고 이해할 수 있다. 즉 개발의 편의를 위해 사용된다. 예를들면 <a href ...> 는 많이 사용되는 html 태그이다. 이는 Play에서 #{a @Application.index()}Go to Home #{/a} 와 같이 사용될 수 있다. 랜더링된 브라우져의 소스보기를 하면 해당 태그는 <a href...> 식의 html 태그로 바뀐것을 볼 수 있다. 이외에 #{list..}, #{if} 등 다양한 태그가 있다.

Actions - @{...}

@{...} 는 route파일을 기반으로 컨트롤러의 액션으로 링크를 표현할때 사용된다. 위의 #{a @Application.index()}Go to Home{/a} 와 같이 "/index" 경로를 입력하는 것이 아닌 @controller.action 형태로의 소스 코딩이 가능해진다. 

다시 위의 index.html로 돌아가보자. 여기서 사용된 #{extends /} 태그는 Play에게 현재 template이 'main.html' 이라는 다른 template을 상속하고 있다는 것을 알려준다. 이는 공통으로 사용되는 부분을 재사용할 수 있는 강력한 기능이다. 

자 이제 /yabe/app/views/main.html파일을 보자

    
    
    #{get 'moreStyles' /}
    
    
    #{get 'moreScripts' /}
  
  
    #{doLayout /}
  

위 소스에 #{doLayout /} 태그가 보인다. 즉, 우리의 index.html 은 실제론 mail.html의 <body>#{doLayout /}</body> 위치에 삽입되는 것이다.
여기서 잠깐 Play가 실제 소스의 수정만으로 어플리케이션을 다시 로딩하는지 확인해보자.
다시 
yabe/app/controllers/Application.java 파일을 열어서 render() 함수에 세미콜론을 빼고 저장한다.
package controllers;
import play.mvc.*;
 
public class Application extends Controller {
 
	public static void index() {
		render()
	}

}
이제 브라우져로 돌아가서 Refresh 버튼을 눌러보자(F5).


위에서 언급했던 기능으로 신기하게 에러를 낸 소스코드를 찾아가 보여준다. 또한, 단지 소스를 수정하는 것 만으로 수정내용이 적용되는 것을 알 수 있다. 이제 다시 에러를 수정하자. 

이제
yabe/app/views/Application/index.html 을 열고 아래와 같이 수정하자.
#{extends 'main.html' /}
#{set title:'Home' /}
 

A blog will be here

방금전과 같이 저장을 하고 브라우져를 refresh 해보면 변경된 사항을 확인 할 수 있다.

데이터 베이스 설정하기

본격적으로 블로그를 만들기에 앞서 우리가 사용할 데이터베이스가 필요하다. 개발의 목적으로 Play는 자체적으로 H2 라는 DBMS를 가지고 있다. 이것은 우리가 개발의 목적으로 사용하기에 좋은 선택이 될 것이다. 우리는 file system기반 데이터 베이스와 메모리 데이터 베이스중에 선택 할 수 있다. 하지만 편의상 메모리 데이터 베이스로 개발을 진행 하도록 하겠다. 차 후 mysql 등의 데이터 베이스와 연동하는 것은 비교적 쉽다. (메모리 데이타베이스는 서버에 있는 동적 메모리 영역에 데이터를 저장하기때문에 서버가 종료되면 데이터는 날라간다.)
데이터베이스를 설정하기 위해
yabe/conf/application.conf 파일의 아래 라인의 주석을 제거한다.
db=mem
자! 이제 데이터베이스를 위한 설정은 모두 끝났다. 메모리 디비를 사용할 경우 놀라울 정도로 쉽게 데이터베이스를 설정할 수 있다. 브라우져로 돌아가서 refresh를 눌러보자. console (도스, 쉘..) 에서 자동으로 데이터베이스를 시작하는 것을 볼 수 있다.

-  Play framework으로 블로그 개발하기 (1) - 끝
저작자 표시
신고
Posted by AppleSherbet

최근에 달린 댓글

최근에 받은 트랙백

글 보관함

티스토리 툴바