Controller

Controller



요청을 처리해 응답을 생성
요청(request)마다 새로운 인스턴스(instance)가 생성되며 응답(response)을 생성하거나 응답을 뷰에 넘김

Understanding Controllers and Actions


컨트롤러 생성하기



GGTS 로 생성 하는 방법 1
grails 프로젝트에서 컨트롤러 우측 클릭 후 New - Controller - Contoller 이름







GGTS 로 생성 하는 방법 2
Grails Command History 를 이용하여 Command 작성
-> create-controller net.grails.my.BookController




BookController가 생성된 화면



BookController의 index 메서드를 변경한다


package net.grails.my

class BookController {

    def index() { 
		render "Hello, My Grails!"
	}
}







mygarails를 실행한다


Console 창에서 정상작동을 확인한다


브라우저에서 BookController에 접근해본다.
BookController는 어플리케이션 루트의 하위로 /book URI로 매핑된다.
접속 주소 : http://localhost:8080/mygrails/book/



액션 만들기



컨트롤러는 코드블럭으로 구현된 프로퍼티를 가지며 각 프로퍼티는 URI에 매핑된다.
기본적으로 액션 이름과 동일한 URI로 (아래 소스는 /book/list)로 매핑된다.
접속주소 : http://localhost:8080/mygrails/book/list

package net.grails.my

class BookController {	
	def list = {
		return model;
	}
}



Default Action(기본 액션)


컨트롤러의 URI에 매핑하는 기본 액션이 있다.
Book 컨트롤러의 기본 주소는 http://localhost:8080/mygrails/book/ 이고
액션을 지정하지 않으면 index 액션이 실행된다.
기본 액션을 변경 할 수있다.

package net.grails.my

class BookController {	
	static def defaultAction = "list"
	def list = {
		render "list Action";
	}
	
	def index ={
		render "index Action"
	}
}



실행 후 http://localhost:8080/mygrails/book/ 에 접속 해보면 처음과는 다르게 index 액션이 실행되는 것이 아닌 list 액션이 실행된 것을 볼 수 있다.


Controllers and Scopes


Available Scopes(이용할 수 있는 스콥)


스콥은 HashMap 객체이다.
이용가능한 스콥은 아래와 같다.
  1. servletContext - 전체 웹 어플리케이션의 상태를 공유하는 정보. javax.servlet.ServletContext의 인스턴스
  2. session - 사용자의 상태 정보. HttpSession의 인스턴스
  3. params - 파리미터들의 맵
  4. flash - 현재 요청부터 다음 요청까지 전달해야 할 프로퍼티를 저장

Accessing Scopes(스콥 사용하기)


아래와 같이 소스 변경 후 브라우저에서 http://localhost:8080/mygrails/book/?id=kshmeme 로 접속한다.
package net.grails.my

class BookController {	
	static def defaultAction = "list"
	def list = {
		def id = params.id
		render "list Action, "  + id
	}
	
	def index ={
		def id = params.id
		render "index Action, " + id
	}
}




브라우저에서 확인


Using Flash Scope(Flash 스콥)



현재 요청부터 다음 요청까지 전달해야 할 프로퍼티를 저장
소스를 아래와 같이 변경한다.
브라우저에서
http://localhost:8080/mygrails/book/view?id=kshmeme
http://localhost:8080/mygrails/book/view?id=kshmeme2

두가지 경우의 주소로 접근한다.


package net.grails.my
 
class BookController {  
    static def defaultAction = "list"
    def meview = {
        def id = params.id
        def message = flash.message
        render "meview : " + message; 
    }
     
    def view = {
     
		def id = params.id
		def msg = "user id is " + id
		if( id == "kshmeme"){
			flash.message = msg
			redirect(action:meview)
		}		
        render "view : " + msg        
    }
}



첫번째 경우 view 액션에서 flash로 지정한 데이터가 meview로 넘어간 걸 확인 할 수 있다.



두번째 경우 view 액션이 실행된 걸 확인할 수 있다


Models and Views


Returning the Model(모델 반환하기)



모델은 렌더링할 때 뷰에서 사용하는 맵을 의미함
이 맵의 키는 뷰에서 접근할 수 있는 변수 이름으로 변환된다

모델을 반환하는 방법은 두가지이다
1. 명시적으로 맵 인스턴스를 반환하는 방법
BookController 소스를 아래와 같이 변경한다.

package net.grails.my

class BookController {

    def view = {
		[id : params.id]
	}
}


view.gsp를 추가한다.





view.gsp 소스를 아래와같이 적용한다


입력하신 아이디는 ${id} 입니다.




실행후 브라우저로 확인한다.
확인 주소 : http://localhost:8080/mygrails/book/view?id=kshmeme




2. 모델을 반환하는 두번째 방법은 명시적으로 모델을 반환하지 않을 경우에 컨트롤러의 프로퍼티들이 리턴된다.
BookController 소스를 아래와 같이 변경한다.(view.gsp 내용 동일, 확인주소 동일)
package net.grails.my

class BookController {
String id;
    def view = {
		id = params.id
	}
}






spring에서 사용하는 ModelAndView를 이용할 수 도있다.
BookController 소스를 아래와 같이 변경한다.(view.gsp 내용 동일, 확인주소 동일)

package net.grails.my

import org.springframework.web.servlet.ModelAndView

class BookController {
String id;
    def view = {
		id = params.id
		return new ModelAndView("/book/view", [ id : id ])
	}
}




Selecting the View(뷰 선택하기)



이제까지 어떤 view(gsp, jsp)를 사용할 지 소스에서 지정하지 않았다.
이제까지의 예제 처럼 BookController의 view Action 을 만들면
grails-app/views/book/view.gsp 를 찾는다(또는 grails-app/views/book/view.jsp)
이건 Convention(관례?)으로 찾기 때문이다
경로 : grails-app/views/${ControllerName}/ActionName.gsp(jsp)



view.gsp 와 view.jsp 두개의 파일이 동시에 존재할 경우 문서에는 jsp를 먼저 찾는다고 나와 있다
개인적으로 실제로 해보면 gsp 를 찾았다



BookController 의 view Action에서 기본값인 view.gsp를 렌더링 하지 않고 다른 view 를 렌더링 하기 위해서는
render 를 이용하여 지정해 줄 수 있다.

BookController 의 내용을 아래와 같이 수정한다.
view의 내용을 render 메서드에서 view, model을 명시적으로 지정해 주었다.

package net.grails.my

import org.springframework.web.servlet.ModelAndView

class BookController {
String id;
    def view = {
		id = params.id
		render(view:"newview", model:[id:id])
	}
}




views/book/ 경로에 newview.gsp를 추가한다.
newview.gsp 의 소스는 아래와 같이 변경한다.
newview 입니다.<br />
입력하신 아이디는 ${id} 입니다.




실행 후 브라우저에서 확인한다.
확인주소 : http://localhost:8080/mygrails/book/view?id=kshmeme
아래와 같이 newview.gsp 가 렌더링 된 걸 확인할 수 있다.




Rendering a Response(응답 만들기)


텍스트나 코드의 조각(snippet)을 컨트롤러에서 바로 렌더링이 필요할 경우에도 render 메서드가 사용된다.
일반 텍스트를 바로 렌더링 할 수도 있고 contentType을 지정하여 xml을 렌더링 할 수도있다.

xml을 렌더링 하는 걸 예로 들면..
BookController 의 소스를 아래와 같이 변경한다.
package net.grails.my

import org.springframework.web.servlet.ModelAndView

class BookController {
String id;
    def view = {
		id = params.id
		render(text:"<root>text</root>", contentType:"text/xml", encoding:"UTF-8")
	}
}




실행후 브라우저에서 확인한다.
xml을 바로 렌더링 한 걸 확인할 수 있다.
확인주소 : http://localhost:8080/mygrails/book/view





Redirects and Chaining



Redirects(리다이렉트)


redirect 메소드를 이용하여 액션을 redirct 할 수 있다.

다른 action으로 리다이렉트

BookController 의 소스를 아래와 같이 변경한다.
확인주소 : http://localhost:8080/mygrails/book/view
view action을 호출하면 detailView주소로 redirect 시킬 것이다.

params 인자는 파라미터를 넘겨주는 것으로 생략 가능하다


package net.grails.my

import org.springframework.web.servlet.ModelAndView

class BookController {
    def view = {
		redirect(action:detailView, params:[myparam:"param1"])
	}
	
	def detailView = {
		render (text : "detailView",  encoding:"UTF-8")
	}
}






다른 컨트롤러에 있는 액션으로 리다이렉트
BookController 의 소스를 아래와 같이 변경한다.
확인주소 : http://localhost:8080/mygrails/book/view


package net.grails.my

import org.springframework.web.servlet.ModelAndView

class BookController {
    def view = {
		redirect(controller:'dvd',action:'dvdView', params:[myparam:"myparam"])
	}
}




net.grails.my 패키지에 DvdController를 생성한다.
DvdController 소스를 아래와 같이 변경한다.

package net.grails.my

class DvdController {

    def dvdView = {
		render "dvdView"
	}
}







브라우저에서 확인을 하면
http://localhost:8080/mygrails/dvd/dvdView?myparam=myparam
주소로 리다이렉트 된 걸 확인할 수 있다.




url 또는 uri로도 리다이렉트가 가능하다.

uri로 리다이렉트 하기
BookController 의 소스를 아래와 같이 변경한다.
확인주소 : http://localhost:8080/mygrails/book/view

package net.grails.my

import org.springframework.web.servlet.ModelAndView

class BookController {
    def view = {
		redirect(uri:"/redirect.html")
	}
}





web-app 하위에 redirect.html을 생성한다.






redirect.html이 생성된 화면.




실행후 확인을 하면 redirect.html 페이지로 이동한 걸 확인할 수 있다.


url로 리다이렉트 하기
BookController 의 소스를 아래와 같이 변경한다.
확인주소 : http://localhost:8080/mygrails/book/view

package net.grails.my

import org.springframework.web.servlet.ModelAndView

class BookController {
    def view = {
		redirect(url:"http://www.naver.com")
	}
}



실행후 확인을 하면 naver로 이동하는 걸 확인 할 수 있다.


Chaining(체이닝)


액션이 연쇄적으로 일어날때 액션 간에 model 데이터를 유지 시켜준다.
예를들면, 액션이 first -> second -> third 이 순서대로 실행되어야 할때
first에서 생성한 model 데이터가 third 액션까지 유지되도록 해준다.
chain으로 넘어온 model 데이터는 chainModel 를 이용하여 접근 할 수 있다.

BookController 의 소스를 아래와 같이 변경한다.
확인주소 : http://localhost:8080/mygrails/book/first

package net.grails.my

import org.springframework.web.servlet.ModelAndView

class BookController {
    def first = {
		chain(action:second, model:[one:1])
	}
	def second = {
		chain(action:third, model:[two:2], params:[mymodel:"mymodel"])
	}
	def third = {
		def model = chainModel
		model.three = 3
		render(view:"view", model:model)
	}
}



view.gsp 소스를 아래와 같이 변경한다.
one :   ${one } <br />
two : ${two } <br />
three : ${three } <br />



실행하면 first 에서 생성한 데이터, second 에서 생성한 데이터, third 에서 생성한 데이터가 모두 유지되는 것을 확인할 수 있다.




Controller Interceptors



인터셉터는 요청, 세션, 어플리케이션의 상태를 처리하는 중에 가로채야 할 때 사용한다.
인터셉터는 액션으로 구현되고 before, after 두 종류가있다.
인터셉터를 하나이상의 컨트롤러에 적용 한다면 Filter를 권장한다.
BookController 소스를 아래와 같이 변경한다.
beforeInterceptor 에 인터셉터 메서드를 auth로 설정한다.
login 액션일경우에는 인터셉터가 실행되지 않는다.
userid 파라미터가 kshmeme 일경우에는 view 액션이 정상적으로 실행이 된다.
다른 아이디 일 경우에는 login 액션이 실행된다.
인터셉터가 통과 하기 위해서는 입터센터 액션에서 false를 리턴 하지 않으면 된다.

package net.grails.my

class BookController {

	def beforeInterceptor = [action:this.&auth, except:['login']]	
	
	def auth(){
		def userid = params.userid
		if("kshmeme" != userid){
			redirect(action:'login')
			return false
		}
	}
	
	def login = {
		render "loging please"
	}
	
	def view = {
		def msg = "userid : " + params.userid
		render msg
	}
}




브라우저에 http://localhost:8080/mygrails/book/view?userid=kshmeme
주소를 입력한다. userid 파라미터를 변경하면 login 액션이 실행된다.



Data Binding



요청 파라미터를 객체에 결합(binding)시키는 행위이다.
파라미터들과 객체간의 형변환도 제공한다.
Grails는 스프링이 제공하는 데이터 바인딩기능을 이용한다.

바인딩 기능을 사용하기 전에 Book 도메인 클래스를 생성한다.




도메인 클래스를 생성 한 후
Book.groovy 소스를 아래와 같이 변경한다.
(name, userid 추가)

package net.grails.my

class Book {

    static constraints = {
    }
	
	String name;
	String userid;
	
}




BookController 소스를 아래와 같이 변경한다.
view 액션에서는 도메인 클래스의 생성자에 params를 넣었다. 이 경우에는 도메인 클래스의 생성자에 params 객체를 넘기면 자동으로 파라미터를 바인딩 해야 하는 것으로 간주한다.(묵시적 생성자 방법)
view2 액션에서는 properties 를 이용하여 데이터 바인딩을 하였다. 묵시적 생성자를 이용한 방법과 결과는 동일하다.


package net.grails.my

class BookController {	
	def view = {
		
		def b = new Book(params)
		
		println "묵시적 생성자 방법 : " + b.userid
				
		def msg = "userid : " + params.userid
		render msg
	}
	
	def view2 = {
		
		def b = new Book()
		
		println "명시작 지정 방법 전 : " + b.userid
		
		b.properties = params
		println "명시적 지정 방법 후 : " + b.userid
		def msg = "userid : " + params.userid
		render msg
	}
}






확인주소 :
http://localhost:8080/mygrails/book/view?userid=kshmeme
http://localhost:8080/mygrails/book/view2?userid=kshmeme

콘솔창에서 데이터를 로그로 확인할 수 있다.





도메인 클래스 Author 를 생성한다.
Author 소스를 아래와 같이 변경한다.

package net.grails.my

class Author {

    static constraints = {
    }
	
 	String id
}





Book 도메인 클래스 소스를 아래와 같이 변경한다.

package net.grails.my

class Book {

    static constraints = {
    }
	
	String name;
	String userid;
	Author author;
}





바로위 BookController 소스에 아래와 같이 view3 액션을 추가한다
...........
	def view3 = {
		
		def b = new Book(params)
		
		println "author.id: " + b.author.id
		
		b.properties = params
		println "book.userid: " + b.userid
		def msg = "userid : " + params.userid
		render msg
	}
.............


확인주소: http://localhost:8080/mygrails/book/view3?userid=kshmeme&author.id=myid
파라미터에 author.id 처럼 값이 들어가면 book 도메인 클래스의 author를 찾아서 author의 id 값을 데이터 바인딩 해준다.

Console 로그를 통하여 확인 할 수 있다.


이와 같은 방법으로 여러개의 도메인 클래스에 데이터를 바인딩 할 수 도있다.



Map Based Binding


객체의 속성에 대해서 맵에서 값을 변환, 할당 가능 하다.
바인더는 객체의 속성이름에 해당하는 맵의 키로 객체의 속성을 찾아서 항목을 연결한다.

Person domain을 생성한다.

package net.grails.my

class Person {

    static constraints = {
    }
	String firstName
	String lastName
	Integer age
}



BookController 클래스에 아래와같이 bind 액션을 추가한다.
확인주소 : http://localhost:8080/mygrails/book/bind

	def bind ={
		def bindingMap = [firstName : "Peter", lastName : "Gabriel", age : 54]
		 //bindingMap  의 key,value가 person 의 프로퍼티로 매핑된다.
		def person = new Person(bindingMap);
		
		println "firstName :" + person.firstName		
		println person.lastName
		println person.age
		
		render ("firstName :" + person.firstName	+ "<br />"  
			+ "lastName :" + person.lastName	
			+ "<br />" +"age :" + person.age	+ "<br />")
	}




아래와같이 내용을 확인 할 수 있다.







내용 확인중
Binding To Collections And Maps
Custom Data Converters
Date Formats For Data Binding


XML and JSON Responses


Using the render method to output XML(render 메소드를 이용하여 XML 형식으로 응답하기)




BookController 클래스에 아래와같이 list 액션을 추가 또는(존재할경우) 수정한다.
요청에 대해서 xml로 응답 하고자 할때 아래와 같이 xml을 만들 수 있다.
확인주소 : http://localhost:8080/mygrails/book/list


	def list = {
		//테스트 데이터 생성
		List<Book> lists = new ArrayList<Book>()
		(1..10).each{
			seq->def b = new Book()
			b.name = "이름" + seq
			b.userid = "userid" +seq
			lists.add b
		}
		
		
		 //한글이 깨질 위험이 있으므로 인코딩 설정 해준다
		//
		render(contentType:"text/xml", encoding:"UTF-8") {
			books { // root : books
				for(b in lists){
					//element : book, attribute : userid, text:name
					book(b.name, userid : b.userid)	
				}
			}
		}
	}



응답받은 xml을 아래와 같이 확인할 수 있다.




Using the render method to output JSON(render 메소드를 이용하여 JSON형식으로 응답하기)


요청에 대해서 JSON 형태로 응답을 주고자 할때는 아래와 같이 JSON 형태로 응답을 줄 수 있다.

BookController 클래스에 아래와같이 list 액션을 추가 또는(존재할경우) 수정한다.
확인주소 : http://localhost:8080/mygrails/book/list



	def list = {
		//테스트 데이터 생성
		List<Book> lists = new ArrayList<Book>()
		(1..10).each{
			seq->def b = new Book()
			b.name = "이름" + seq
			b.userid = "userid" +seq
			lists.add b
		}
		 
		render(contentType:"text/json", encoding:"UTF-8") {
			books =array{ // array 인 것에 유의
				for(b in lists){
					
					book( userid : b.userid)	
				}
			}
		}

	}


실행하면 다운로드를 받아서 데이터를 확인 한다.
또는 크롬으로 실행해서 브라우저에서 바로 확인할 수 있다.


{"books":[{"userid":"userid1"},{"userid":"userid2"},{"userid":"userid3"},{"userid":"userid4"},{"userid":"userid5"},{"userid":"userid6"},{"userid":"userid7"},{"userid":"userid8"},{"userid":"userid9"},{"userid":"userid10"}]}