GXT RIA App 만들기2011. 3. 7. 18:14
세션은 얼마나 지속될까?
기본 30분입니다. 웹서버마다 was마다 다른지는 모르겠지만 제가 아는 시간은 30분입니다.
실제 30분인지 테스트를 해볼까요.
아래 메소드를 RiaApp.java에 추가하고 로그인 성공했을 경우 실행되는 Success()메소드를 통해 호출해보도록 하자.

▶ 아래 코드는 60초 마다 서버에 세션이 유효한지 요청을 보내 세션을 확인하는 코드다. 진행 상태바를 통해 매분마다 1%씩 움직이도록 했다. 예상대로 세션이 30분이라면 30%까지 진행되다가 멈추게 될까? 테스트 해보면 100%를 보여주고 종료됩니다.
애기인 즉은 서버와 통신할 경우 세션이 유지되어 계속 access할 경우 무제한으로 사용할 수 있다는 것이다.
 
// RiaApp.java에 추가하세요
	public void roop(){
		String stime = new Date().toString();
		final MessageBox box = MessageBox.progress("세션 시간 측정 중...", "시작시간은 : "+stime,  
		"Initializing...");  
		 final ProgressBar bar = box.getProgressBar();  
		 final Timer t = new Timer() {  
			 float i;  
			 @Override  
			 public void run() {  
				 String stime = new Date().toString();
				 System.out.println("middle time: "+ stime);
//				 continueSession();
				 try{
					 service.getSessionData( new AsyncCallback() {
							@Override
							public void onSuccess(Void result) {
							}
							@Override
							public void onFailure(Throwable caught) {
								String time = new Date().toString();
								MessageBox.alert("로그인", "세션이 종료되었습니다. \n 종료시간:"+ time, btnListener);
								cancel();  
//								box.close();	
							}
						});
				 }catch(Throwable e){
					
				 }
				 bar.updateProgress(i / 100, (int) i + "% Complete");  
				 i += 1;  
				 if (i > 100) {  
					 cancel();  
					 box.close();  
				 }  
			 }
   	  };  
   	  t.scheduleRepeating(60000);  // 60초
	}

// 로그인 성공 후 실행되어야 하므로 기존 onSubmit()메소드를 수정하자.
	public void onSubmit() {
		service.getSessionData( new AsyncCallback() {
			@Override
			public void onFailure(Throwable caught) {
				// 버튼 리스트를 설정
				MessageBox.alert("로그인", "세션이 종료되었습니다. \n 다시 로그인 해주세요", btnListener);
			}
			@Override
			public void onSuccess(Void result) {
				// TODO Auto-generated method stub
				roop(); // 추가
			}
		});
	}

▶ 아래 처럼 진행되다가..100%를 채우고 종료된다...

로그를 보면 세션유지시간은 30분이지만 계속 연장되어진다.
----------------------------------------------
middle time: Thu Mar 10 12:44:17 KST 2011
로그인 아이디는 : benneykwag@gmail.com
세션 생성시간 : Thu Mar 10 11:11:02 KST 2011
설정된 세션유지시간: 30
마지막 AccessTime :Thu Mar 10 12:43:17 KST 2011
----------------------------------------------


▶ 이번에는 세션이 끊기도록 코딩해서 Refresh나 서버와 통신을 하지 않고 얼마나 유지되는지 보자.
위에서 60초로 테스트 했다면 이번에는 35분으로 변경해 보자.

// RiaApp.java
	public void roop(){
...
...
   	  t.scheduleRepeating(60000*35); //35분 
	}

// RiaAppPgmServiceImpl.java
	@Override
	public void getSessionData() throws Throwable {
		HttpSession session = getSession();
		if( null == session.getAttribute("loginid")) // index.html페이지에서 전달해주고 loginChk.jsp에서 생성한 세션정보
			throw new Throwable("세션이 종료되었습니다.");
		else{
			System.out.println("로그인 아이디는 : "+(String)session.getAttribute("loginid"));
			System.out.println("세션 생성시간 : "+ new java.util.Date(session.getCreationTime()) );
			System.out.println("설정된 세션유지시간: "+ session.getMaxInactiveInterval()/60);
			System.out.println("마지막 AccessTime :"+ new java.util.Date(session.getLastAccessedTime()));
			
			
			System.out.println("----------------------------------------------");
		}
	}


이제 실행해보자. 35분 후 세션이 종료되었는지 유지되고 있는지 테스트 해보면 알수 있겠다.
Posted by 베니94
GXT RIA App 만들기2011. 3. 1. 23:08
오늘은 GWT에서 Service라는 개념에 대해 알아보고 실습해보자. GWT에서는 서버와 통신(RPC)하는 하나의 행위를 서비스라는 이름으로 불리운다. 지금까지 배웠던 부분들은 모두 Client단으로 최종 javascript로 변환되어 사용자에게 UI로 보여지는 것이다. 그러나 화면만으로는 프로그램이 데이터를 가질 수 없으므로 서버단과 통신하는 무엇인가가 필요한데 이것을 Service라 지칭한다.
서비스는 아래와 같이 2개의 인터페이스와 1개의 클래스가 한 쌍이된다.


1. com.gxt.riaapp.client.RiaAppPgmService 
   - 패키지를 보면 client아래 존재한다. 이 인터페이스에는 서비스에서 사용할 메소드를 명시하게 된다.
   - 이클립스에서는 최초 이 인터페이스를 생성하면 자동으로 나머지 인터페이스와 클래스를 생성해준다.
   - 우리가 메소드를 추가하고 싶다면 이 인터페이스에 메소드를 추가하게 되는데 추가할 경우 자동으로 xxxAsync, Impl 두개의 파일에 해당 메소드가 기본 구현되어지게 된다.

2. com.gxt.riaapp.client.RiaAppPgmServiceAsync
   - 이 인터페이스는 1번 인터페이스를 생성하면 자동으로 생성되게 된다. 이 인터페이스는 이클리스가 알아서 제어 해주므로 사실 개발자는 손댈 필요가 없다. 이 인터페이스는 서버와 통신을 주고 받는 역할을 한다고 보면되겠다.

3. com.gxt.riaapp.server.RiaAppPgmServiceImpl
  - 패키지에 주목하자. server아래 존재한다. 이 클래스는 Client와는 반대로 Server에서 실행 할 메소드들이 구현되어야 한다. 즉 위의 1,2번에 명시된 메소드가 최종 구현되는 곳이다. 이 클래스에서 SQL을 날리고 데이터를 불러와 리턴하면 Async인터페이스를 통해 xxxService가 클라이언트에게 전달한다고 생각하면 되겠다.

▶ 서비스를 생성해 보자. com.gxt.riaapp.client를 선택하자 여기서 마우스 우측클릭을 하고 아래와 같이 선택하자.


▶ RiaAppPgmService라는 이름으로 생성해보자.


▶ 위에서 설명했듣 client에 2개의 인터페이스 server에 한개의 클래스가 생성되었을 것이다. 여기서 한가지 주목할 부분이 web.xml파일이다. war/WEB-INF/web.xml파일을 열어보자. 아래와 같이 web.xml파일이 변경되었을 것이다. 이는 서버스가 Servlet기반이기 때문에 하나의 서비스가 추가되었을 경우 해당 서비스 이름으로 서블릿 설정이 web.xml파일에 추가되게 된다. 이 또한 자동으로 생성되게 되므로 손댈 필요는 없겠다


  
  
  
  
  
    RiaApp.html
  
  
  	RiaAppPgmService
  	com.gxt.riaapp.server.RiaAppPgmServiceImpl
  
  
  	RiaAppPgmService
  	/riaapp/RiaAppPgmService
  



▶ 이제 우리는 서비스에 메소드를 추가하고 그 메소드를 구현 해보 도록 하자. 구현이 끝나면 클라이언트에서 해당
 메소드를 호출하면 서버는 클라이언트에게 정보를 전달하는 지 확인해볼 수 있을 것이다.
▶ RiaAppPgmService.java를 열어 메소드를 선언해보자
package com.gxt.riaapp.client;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;

@RemoteServiceRelativePath("RiaAppPgmService")
public interface RiaAppPgmService extends RemoteService {
	public int getSessionData() throws Throwable ; // 서버에서 세션을 전달받아 리턴하자.
	/**
	 * Utility class for simplifying access to the instance of async service.
	 */
	public static class Util {
		private static RiaAppPgmServiceAsync instance;
		public static RiaAppPgmServiceAsync getInstance(){
			if (instance == null) {
				instance = GWT.create(RiaAppPgmService.class);
			}
			return instance;
		}
	}
}

▶ 이제 Async 인터페이스와 Impl클래스를 열어보라 위에서 선언한 메소드가 선언 또는 구현되어 있을 것이다.
package com.gxt.riaapp.client;

import com.google.gwt.user.client.rpc.AsyncCallback;

public interface RiaAppPgmServiceAsync {
	public void getSessionData(AsyncCallback callback) ; // 서버에서 세션을 전달받아 리턴하자.
}
package com.gxt.riaapp.server;

import javax.servlet.http.HttpSession;

import com.gxt.riaapp.client.RiaAppPgmService;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

public class RiaAppPgmServiceImpl extends RemoteServiceServlet implements RiaAppPgmService {
	private HttpSession getSession() {
		return this.getThreadLocalRequest().getSession();
	}
	@Override
	public int getSessionData() throws Throwable {
		// TODO Auto-generated method stub
		System.out.println("여기는 서버예요...");
		return 0;
	}
}



▶ 이제 client에서 getSessionData메소드를 호출해보자 Impl클래스의 메소드가 실행되어 System.out.println("여기는 서버예요..");가 실행되는 지 보도록 하자. 또한 실행됨과 동시에 리턴되는 값이 무엇인지도 확인해보자. 이러한 형태로 서버와 통신해서 최종적으로는 데이터베이스의 데이터등을 리턴하는 것이다.
▶ RiaApp.java를 수정해서 위의 getSessionData메소드를 호출해보자.
	public void onModuleLoad() {
		model = new RiaAppModel();
	    Registry.register(MODEL, model);  // 최초 실행시 Registry에 RiaAppModel을 등록한다.
		dispatcher = Dispatcher.get();
	    dispatcher.addController(new AppController());
	    dispatcher.addController(new NavigationController());  // 좌측
	    dispatcher.addController(new ContentController());
	    dispatcher.dispatch(AppEvents.Init);
	    
	    if(model.getEntries().size()>0) // 등록된 첫번째 프로그램을 보여준다.
			showPage(model.getEntries().get(0));

	    onSubmit(); // 서버쪽 메소드를 호출해서 세션이 유효한지 확인한다.
	   
	}

	public int onSubmit() {
		service.getSessionData( new AsyncCallback() {
			@Override
			public void onSuccess(Integer result) {
				String time = new Date().toString();
				//if(result == -1){
					System.out.println("서버에서 리턴해준 결과는 "+result);
				//}			}
			@Override
			public void onFailure(Throwable caught) {
				MessageBox.alert("로그인", "세션이 종료되었습니다. \n 다시 로그인 해주세요", null);
			}
		});
		return 0;
	}


▶ 이제 실행해 보자.  아래 그림처럼 server 쪽 메소드에서 "여기는 서버예요"를 출력하고 그 다음 클라이언트쪽 메소드에서 "서버에서 리턴해준..."라고 출력된다. 여기서 주목할 부분은 위의 onSubmit()메소드에서 보여주는 것처럼 서버쪽 메소드를 호출하고 결과를 리턴받는데 성공할 경우 onSuccess()메소드가 실행되고 그렇지 않고 문제가 발생할 경우 onFailure()메소드가 호출되게 된다.
이 두개의 메소드는 두고두고 나올테니 기억하도록 하자.

▶ 이제 getSessionData메소드를 역할에 맞게 수정하도록 하자. 이 메소드는 세션이 유효한지 체크하는 것이다.
일단 메소드의 리턴 type을 변경하자 단지 세션이 유효한지 여부를 판단 할 것이라면 유효하지 않은 경우에는 onFailure메소드가 실행되도록 하고 onFailure메소드에서는 유효하지 않다는 메시지와 함께 로그인 페이지로 이동하도록 하면 될것이므로 리턴타입은 void로 변경하는것이 좋을 듯 하다.

// RiaAppPgmService.java
	public void getSessionData() throws Throwable ; // 서버에서 세션을 전달받아 리턴하자.

// RiaAppPgmServiceAsync.java
	public void getSessionData(AsyncCallback callback) ; // 서버에서 세션을 전달받아 리턴하자.

// RiaAppPgmServiceImpl.java
	@Override
	public void getSessionData() throws Throwable {
		// TODO Auto-generated method stub
		System.out.println("여기는 서버예요...");
	}

// RiaApp.java 
	public void onSubmit() {
		service.getSessionData( new AsyncCallback() {
			@Override
			public void onFailure(Throwable caught) {
				MessageBox.alert("로그인", "세션이 종료되었습니다. \n 다시 로그인 해주세요", null);
			}
			@Override
			public void onSuccess(Void result) {
				// TODO Auto-generated method stub
				
			}
		});
	}
이제 기존 int형이었던 메소드를 void로 변경하였으니 Impl클래스의 메소드를 구현해보도록 하자. 이 메소드는 세션을 구해와 유효한지 확인하는 메소드이므로 일단 세션정보를 얻어오도록 코딩하자. RiaAppPgmServiceImpl 클래스에 아래의 메소드를 추가하고 세션을 구해와 유효한지 확인해보자.
package com.gxt.riaapp.server;

import javax.servlet.http.HttpSession;

import com.gxt.riaapp.client.RiaAppPgmService;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

public class RiaAppPgmServiceImpl extends RemoteServiceServlet implements RiaAppPgmService {
	private HttpSession getSession() {
		return this.getThreadLocalRequest().getSession();
	}
	@Override
	public void getSessionData() throws Throwable {
		HttpSession session = getSession();
		if( null == session.getAttribute("loginid")) // index.html페이지에서 전달해주고 loginChk.jsp에서 생성한 세션정보
			throw new Throwable("세션이 종료되었습니다.");
		else
			System.out.println("로그인 아이디는 : "+(String)session.getAttribute("loginid"));
	}
}


이제 실행해 보자. RiaApp.html파일을 실행할 경우와 index.html파일을 통해 로그인을 했을 경우와 비교를 해보자. 일단 RiaApp.html을 실행할 경우 아래 그림처럼 세션이 종료되었다는 경고창이 보일 것이다. 이는 로그인을 하지 않았으므로 세션이 존재하지 않아 경고창을 보여주는 것이다.

이제 index.htlm파일을 실행하여 정상적으로 로그인해보자. 아래 그림처럼 서버쪽 메소드에서 세션정보를 확인할 수 있다.

▶ 자 이제 생각을 해보자. RiaApp.html은 실제 프로그램이 돌아가는 영역이고 index.html은 로그인 페이지다. RiaApp.html을 실행해서 세션이 존재하지 않을 경우 즉 로그인이 되어 있지 않거나 일정시간 사용하지 않아 세션이 끊겼을 경우 "세션이 종료되었습니다.."라는 메시지를 출력해주었다. 그럼 이제 무엇을 해야할 까? 경고창이 나오고 ok버튼을 클릭했을 경우 로그인 페이지로 유도하는 것이 필요할 듯 하다. 로그인 페이지로 유도하고 로그인을 하고 다시 RiaApp.html을 호출하여 프로그램을 보여줄수 있도록 하는것이다.
▶ 그럼 무엇이 필요할까 생각해보자. 우선 위의 경고창에서 보듣 "ok"버튼을 클릭했을 경우 이 클릭한 이벤트를 이용해서 로그인 페이지로 이동하는 코드를 만들어 보자. 우선 "ok"버튼을 감시할 리스너를 선언하자.
// RiaApp.java를 수정하자.
	/**
	 * Native Javascript Call
	 */
	private native void goIndex() /*-{ 
		$wnd.location.href = "/index.html"; 
	}-*/;

	/**
	 * 세션이 유효하지 않을 경우 뛰워주는 경고창 내의 OK버튼을 감시할 리스너
	 */
	final Listener btnListener = new Listener() {  
		public void handleEvent(MessageBoxEvent ce) {  
			goIndex();
		}  
	};  
	public void onSubmit() {
		service.getSessionData( new AsyncCallback() {
			@Override
			public void onFailure(Throwable caught) {
				// 버튼 리스트를 설정
				MessageBox.alert("로그인", "세션이 종료되었습니다. \n 다시 로그인 해주세요", btnListener);
			}
			@Override
			public void onSuccess(Void result) {
				// TODO Auto-generated method stub
				
			}
		});
	}

이제 실행해 보자. RiaApp.html을 실행해 보면 로그인을 하지 않았으므로 "세션이 종료되었습니다. 다시 로그인해주세요"라는 메시지를 출력하고 "OK"버튼을 클릭하면 로그인 페이지로 이동하게 될것이다. 이렇게 하면 기본적으로 로그인에 대한 처리가 완료되었다.
Posted by 베니94
GXT RIA App 만들기2011. 1. 31. 17:53
오늘 강좌에서는 로그인 페이지와 세션에 대해 애기해보자. 우리는 지금까지 어플리케이션의 뼈대를 구성하고 추가적으로 개발할 프로그램이 자리 잡을 수 있도록 프로그램영역과 좌측 트리메뉴 등을 구성했다. 이제 관리자페이지에 접근할 수 있도록 로그인 페이지를 만들고 아이디와 패스워드를 통해 인증처리를 추가해 보도록 하자.


 

로그인 페이지를 만들어 보자 . 우선 본인은 디자이너가 아니므로 내 손으로 html을 만들면 흉해질 것이므로 tistory의 로그인페이지를 살짝 긁어와 맘대로 사용해보자. 뭐 상업적으로 이용하는건 아니니깐 .. 암튼 본인이 바꿀 수 있으면 바꾸시고 ~

▷ 티스토리에서 훔쳐온 로그인페이지를 그대로 이용하자

▷ 소스는 아래와 같다. war폴더에 index.html이라는 이름으로 파일을 생성하자.






	
▷ 이제 실행시켜 보자. 근데 실행하기 전에 Run Configuration에 대해 간략히 언급을 하고 가자. 아래와 같이 Run Configuration을 실행해보자.

▶ Run Configuration 의 실행 모습이다. 기본적으로 해당 프로젝트의 Root에 마우스우측 클릭하고 실행하면 그 프로젝트의 이름으로 된 html파일이 실행되도록 설정되어 있다 . 나의 경우 Name필드에 .html을 떼어버리고 RiaApp로 변경하고 그 밑의 Server 탭에서 서버구동시 포트를 변경할 수 있도록 세팅가능하도 여러개의 GWT어플리케이션을 구동할 때 포트를 달리하여 사용할 수 있다..

▶ Server탭의 포트 설정 부분

▶ 위의 설정이 끝나면 Apply를 클릭하고 다음번에 GWT어플리케이션을 실행할 때는 아래 그림처럼 하면 간편하죠!


▶ 자 이제 본론으로 들어가서 브라우저에 우리가 만든 index.html을 실행 해보도 록 하자.

이제 아이디와 패스워드를 아무렇게나 넣고 로그인 버튼을 클릭해보자. 아마 아무 반응이 없을 것이다. 콘솔 창을 통해 무슨일이 일어났는지 보자. 아래 로그를 보니 /common/loginChk.jsp가 없다고 나온다. 우리는 index.html에 계정을 넣고 로그인 버튼을 클릭했을 경우 /common/loginChk.jsp로 보내도록 코딩했다. 이 파일에서는 index.html에서 받아온 계정을 확인하고 세션을 생성하고 실제 Gxt 어플리케이션을 실행하는 역할을 하도록 해야한다.

▶ /common/loginChk.jsp를 생성하고 아래와 같이 코딩하자.
<%
// 1. 로그인 계정의 유효성을 확인하는 로직
// 2. 세션 생성
// Gwt 어플리케이션에서 이 세션을 어떻게 이용할 수 있는지
// 확인할 수 있다.
session.setAttribute("loginid", request.getParameter("loginid"));
%>
< script language='javascript'>
parent.location.href="/RiaApp.html?gwt.codesvr=127.0.0.1:9997#0001";
< /script>
▶ 이제 다시 실행해 보자. index.html을 실행하고 계정을 입력하고 로그인 버튼을 클릭하면 Gxt어플리케이션이 실행될 것이다.

오늘은 여기까지... 다음에는 RiaApp.html을 조금 정비하고 GWT에서 서비스를 이용하는 방법과 세션의 사용법을 배워보도록 하자.
Posted by 베니94
GXT RIA App 만들기2011. 1. 28. 09:14

오늘은 상단부분을 완성해보도록 하자. 최초 생각했던 것은 상단에는 단순 html을 삽입 로그인 정보정도만 보여주려고 했으나 계획을 변경해서 일반어플리케이션에서 볼수 있는 풀다운 메뉴와 툴바를 추가해보도록 하자. 풀다운 메뉴는 좌측 메뉴로는 도저히 정리되지 않을 경우나 좌측메뉴와는 별개로 성격이 아주 다른 프로그램들을 모아두거나 하면 될테고 툴바는 미리 보여줄 필요가 있는 자주 쓰는 프로그램을 모아놓는 용도로 사용하자.

☞ 오늘 만들게 될 화면이다. 최상단에 메뉴가 들어가고 그 아래에 툴바를 배치하자.

최초 계획과는 달리 상단부분에 html이 아닌 Menu와 툴바를 넣기로 했으므로 기존 AppView클래스의 northPanel을 HtmlContainer가 아닌 ContentPanel로 변경하여 Menu와 툴바를 삽입하도록 하자.

☞ northPanel과 createNorth()메소드를 변경하자.
//	private HtmlContainer 	northPanel;	// 상단영역
	private ContentPanel	northPanel;

	private void createNorth() {
		BorderLayoutData data = new BorderLayoutData(LayoutRegion.NORTH, 45);
		data.setMargins(new Margins(0, 5, 5, 5));
		northPanel = new ContentPanel();
		northPanel.setHeaderVisible(false);
		ToolBar tb = new ToolBar();
		Button topBtn = new Button("로그아웃");
		topBtn.setIcon(RiaApp.ICONS.go_out());
		tb.add(topBtn);
		topBtn = new Button("사용자정보수정");
		topBtn.setIcon(RiaApp.ICONS.accordion());
		tb.add(topBtn);
		topBtn = new Button("접속로그");
		topBtn.setIcon(RiaApp.ICONS.text());
		tb.add(topBtn);
		tb.setAlignment(HorizontalAlignment.RIGHT);
		northPanel.setBottomComponent(tb);
		Menu menu = new Menu();   
		  
	    MenuItem item1 = new MenuItem("Edit");   
	    menu.add(item1);   
	  
	    MenuItem item2 = new MenuItem("Open File");   
	    menu.add(item2);   
	  
	    Menu sub = new Menu();   
	    sub.add(new MenuItem("readme.txt"));   
	    sub.add(new MenuItem("helloworld.txt"));   
	    item2.setSubMenu(sub);   
	    	
	    MenuBar bar = new MenuBar();   
	    bar.setBorders(true);   
	    bar.setStyleAttribute("borderTop", "none");   
	    bar.add(new MenuBarItem("New", menu));   
	  
	    Menu sub2 = new Menu();   
	    sub2.add(new MenuItem("Cut"));   
	    sub2.add(new MenuItem("Copy"));   
	  
	    MenuBarItem edit = new MenuBarItem("Edit", sub2);   
	    bar.add(edit);   
	  
	    sub = new Menu();   
	    sub.add(new MenuItem("Search"));   
	    sub.add(new MenuItem("File"));   
	    sub.add(new MenuItem("Java"));   
	  
	    MenuBarItem item3 = new MenuBarItem("Search", sub);   
	    bar.add(item3);   
	       
	    menu = new Menu();   
	  
	    CheckMenuItem menuItem = new CheckMenuItem("I Like Cats");   
	    menuItem.setChecked(true);   
	    menu.add(menuItem);   
	  
	    menuItem = new CheckMenuItem("I Like Dogs");   
	    menu.add(menuItem);   
	  
	  
	    menu.add(new SeparatorMenuItem());   
	  
	    MenuItem radios = new MenuItem("Radio Options");   
	    menu.add(radios);   
	  
	    Menu radioMenu = new Menu();   
	    CheckMenuItem r = new CheckMenuItem("Blue Theme");   
	    r.setGroup("radios");   
	    r.setChecked(true);   
	    radioMenu.add(r);   
	    r = new CheckMenuItem("Gray Theme");   
	    r.setGroup("radios");   
	    radioMenu.add(r);   
	    radios.setSubMenu(radioMenu);   
	  
	    MenuItem date = new MenuItem("Choose a Date");   
	    menu.add(date);   
	  
	    date.setSubMenu(new DateMenu());   
	       
	    MenuBarItem item4 = new MenuBarItem("Foo", menu);   
	    bar.add(item4);   
	    northPanel.add(bar);
	    
	    northPanel.setAutoHeight(true);
		viewport.add(northPanel, data);
	}
☞ 위와 같이 코딩하면 에러가 날것이다. RiaApp.ICONS...이 부분이다. 이부분은 툴바에 설정하는 아이콘 이미지로 com.gxt.riaapp.client.resource라는 이름으로 패키지를 생성하고 아래 클래스를 추가해 보자. 아래의 ResourceIcons클래스는 보여질 이미지를 추가하고 해당 이미지를 툴바나 기타 아이콘으로 사용하기 위함이다.
package com.gxt.riaapp.client.resource.icon;

import com.google.gwt.user.client.ui.AbstractImagePrototype;
import com.google.gwt.user.client.ui.ImageBundle;
@SuppressWarnings("deprecation")
public interface ResourceIcons extends ImageBundle {

  @Resource("table.png")
  AbstractImagePrototype table();

  @Resource("add16.gif")
  AbstractImagePrototype add16();

  @Resource("add24.gif")
  AbstractImagePrototype add24();

  @Resource("add32.gif")
  AbstractImagePrototype add32();

  @Resource("application_side_list.png")
  AbstractImagePrototype side_list();

  @Resource("application_form.png")
  AbstractImagePrototype form();

  @Resource("connect.png")
  AbstractImagePrototype connect();

  @Resource("user_add.png")
  AbstractImagePrototype user_add();

  @Resource("user_delete.png")
  AbstractImagePrototype user_delete();

  @Resource("accordion.gif")
  AbstractImagePrototype accordion();

  @Resource("add.gif")
  AbstractImagePrototype add();

  @Resource("delete.gif")
  AbstractImagePrototype delete();

  @Resource("calendar.gif")
  AbstractImagePrototype calendar();

  @Resource("menu-show.gif")
  AbstractImagePrototype menu_show();

  @Resource("list-items.gif")
  AbstractImagePrototype list_items();

  @Resource("album.gif")
  AbstractImagePrototype album();

  @Resource("text.png")
  AbstractImagePrototype text();

  @Resource("plugin.png")
  AbstractImagePrototype plugin();
  
  @Resource("music.png")
  AbstractImagePrototype music();
  
  @Resource("table_refresh.png")
  AbstractImagePrototype refresh();
  
  @Resource("folder_go.png")
  AbstractImagePrototype go_out();
  
  @Resource("excel_download16.png")
  AbstractImagePrototype excel16();
  
  @Resource("excel_download32.png")
  AbstractImagePrototype excel32();
}
☞ 같은 패키지에 이미지도 추가하자. gxt쪽 샘플에서 사용하는 이미지로 gxt 루트에서 찾아보면 될것이다.
 
마지막으로 테마를 변경하자. 이 강좌 맨위의 이미지를 보면 이전에 푸른색 계열의 색상에서 세련된 회색계열로 변경되었다. 개인적으로 푸른색은 좀 촌스러워서 바꿔봤다. war폴더내의 RiaApp.html파일에 한줄 추가하자.
    
     
    
Posted by 베니94
GXT RIA App 만들기2011. 1. 20. 23:09

오늘 시간에는 중간 프로그램 영역을 코딩하도록 하자. 중간 프로그램 영역은 좌측 메뉴에서 하나의 Row를 클릭했을 때 이곳에 해당 프로그램이 보여지도록 해야한다.
1. Controller와 View를 하나씩 생성하자. ContentController.java와 ContentView.java를 mvc패키지내에 생성하고 아래와 같이 코딩하자.

☞ ContentController.java
package com.gxt.riaapp.client.mvc;

import com.extjs.gxt.ui.client.mvc.AppEvent;
import com.extjs.gxt.ui.client.mvc.Controller;
import com.gxt.riaapp.client.AppEvents;

public class ContentController extends Controller {
	private ContentView view;

	public ContentController() {
		registerEventTypes(AppEvents.Init, AppEvents.ShowPage);
	}

	public void initialize() {
		view = new ContentView(this);
	}

	public void handleEvent(AppEvent event) {
		forwardToView(view, event);
	}

}
☞ ContentView.java
package com.gxt.riaapp.client.mvc;

import com.extjs.gxt.ui.client.Registry;
import com.extjs.gxt.ui.client.event.EventType;
import com.extjs.gxt.ui.client.mvc.AppEvent;
import com.extjs.gxt.ui.client.mvc.Controller;
import com.extjs.gxt.ui.client.mvc.View;
import com.extjs.gxt.ui.client.widget.ContentPanel;
import com.extjs.gxt.ui.client.widget.TabPanel;
import com.gxt.riaapp.client.AppEvents;
import com.gxt.riaapp.client.mvc.model.Entry;

public class ContentView extends View {
	private TabPanel tabPanel;
	
	public ContentView(Controller controller) {
		super(controller);
	}
	
	@Override
	protected void initialize() {
		tabPanel = new TabPanel();
		tabPanel.setCloseContextMenu(true);
		tabPanel.setBorderStyle(false);
		tabPanel.setBodyBorder(false);
		tabPanel.setTabScroll(true);
		tabPanel.setAnimScroll(true);
		ContentPanel center = (ContentPanel) Registry.get(AppView.CENTER_PANEL);
	    center.add(tabPanel);
	    center.layout();
	}
	
	public void onShowPage(Entry entry) {
		System.out.println("ContentView.onShowPage() called.." + entry.getName());
	}
	
	@Override
	protected void handleEvent(AppEvent event) {
		EventType type = event.getType();
		if(type == AppEvents.ShowPage){	// Init일 경우에는 작동하지 않음.
			Entry entry = event.getData(); // 이벤트에 전달된 객체를 가져온다.
			onShowPage(entry);
		}
	}
}

☞ RiaApp.java를 수정하자.
	public void onModuleLoad() {
		model = new RiaAppModel();
	    Registry.register(MODEL, model);  // 최초 실행시 Registry에 RiaAppModel을 등록한다.
	    
		dispatcher = Dispatcher.get();
	    dispatcher.addController(new AppController());
	    dispatcher.addController(new NavigationController());  // 좌측
	    dispatcher.addController(new ContentController());
	    dispatcher.dispatch(AppEvents.Init);
	    
	    if(model.getEntries().size()>0) // 등록된 첫번째 프로그램을 보여준다.
			showPage(model.getEntries().get(0));
	}

	/**
	 * ShowPage이벤트를 가지고 있는 Controller를 통해 ContentView를 보여준다. 여기서 중요한 것은 이벤트와 함께
	 * Entry 객체를 전달했다는 것이다. 이 객체는 화면에 보여줘야할 우리가 클릭한 객체로 이 객체를 전달하면 ContentView에서
	 * 이 객체를 이벤트를 통해 전달 받고 화면에 보여주게 된다.
	 * 
	 * @param entry
	 */
	public static void showPage(Entry entry) {
		AppEvent appEvent = new AppEvent(AppEvents.ShowPage, entry); // 이벤트에 객체전달
		appEvent.setHistoryEvent(true);
		appEvent.setToken(entry.getId());
		Dispatcher.forwardEvent(appEvent);  // ShopPage이벤트가 등록되어 있는 ContentController가 ContentView를 보여주게된다.
	}

☞ 이제 실행해 보자 이번에 수정한 부분은 ContentController를 RiaApp에 등록했고 이 컨트롤러를 통해 ContentView가 실행되게 된다. 이때 ContentView가 보여지게 되는 과정에 주목하자.
 RiaApp에 ContentController를 등록하면서 우리는 RiaAppModel의 첫번째 객체를 RiaApp.showPage()메소드에 전달했다.
이 메소드는 AppEvent를 생성하는데 AppEvent는 인자로 이벤트타입과 객체를 받게 된다. 이 애기는 특정이벤트를 콜하면서 객체를 전달할 수 있다는 애기다. showPage메소드의 맨아래 코딩을 보면 Dispatcher클래스의 forwardEvent()메소드를 통해 해당 이벤트가 등록되어 있는 Controller에게 제어권을 넘기고 해당 Controller는 handleEvent메소드에 정의 된 View를 보여지게 된다.
 여기서는 ContentController를 통해 ContentView가 보여지게 되는 것이다. 그러나 이번에도 실행한 결과 별반 달라진게 없을 것이다. 단지 아래 그림처럼 ContentView클래스의 onShowPage()가 호출되는 것을 콘솔을 통해 확인할 수 있다.

2. 이제 프로그램 영역이 마저 보일 수 있도록 수정해 보자

☞ SmplProgram.java를 com.gxt.riaapp.client.mvc 아래에 추가하고 아래와 같이 코딩하자. 이 클래스를 우측 프로그램 영역에 보이도록 할 것이다.
package com.gxt.riaapp.client.mvc;

import com.extjs.gxt.ui.client.widget.LayoutContainer;
import com.extjs.gxt.ui.client.widget.VerticalPanel;
import com.extjs.gxt.ui.client.widget.button.Button;
import com.extjs.gxt.ui.client.widget.layout.FlowLayout;
import com.google.gwt.user.client.Element;

public class SmplProgram  extends LayoutContainer {
	@Override
	protected void onRender(Element parent, int index) {
		// TODO Auto-generated method stub
		super.onRender(parent, index);
		VerticalPanel vp = new VerticalPanel();
		vp.setSpacing(10);
		Button btn =new Button("테스트");
		setLayout(new FlowLayout(10));
		vp.add(btn);
		add(vp);
	}
}
☞ RiaAppModel.java의 생성자를 아래와 같이 수정하자.
	public RiaAppModel() {
		Category pgm = new Category("시스템관리","SYS00002");
		pgm.add("시스템사용자관리", "0001", new SmplProgram());
		addCategory(pgm);
		loadEntries(this);
	}
☞ Entry 클래스의 생성자를 약간 수정하자.
	
    public Entry(String name, String id, LayoutContainer example) {
		this.name = name;
		set("name", name);
		set("id", id);
		this.id = id;
		set("program", example); // example를 program으로 변경하자.
	}
	public LayoutContainer getProgram() {  // 추가하자
		return (LayoutContainer) get("program");
	}
☞ model패키지내에 Page클래스를 추가하자. 이 클래스는 Entry클래스를 받아 TabPanel에 TabItem을 추가할 수 있도록 구현되어 있다.
package com.gxt.riaapp.client.mvc.model;

import com.extjs.gxt.ui.client.Style.Scroll;
import com.extjs.gxt.ui.client.widget.TabItem;
import com.extjs.gxt.ui.client.widget.TabPanel;
/**
 * 이 클래스는 Entry를 받아 TabPanel에 추가할 수 있는 TabItem으로 변환하여 TabPanel에 추가하는 역할을 한다.
 * @author KYS
 *
 */
public class Page extends TabPanel {
	protected Entry entry;
	public Page(final Entry entry) {
		this.entry = entry;
		setTabPosition(TabPosition.BOTTOM);
		setBorderStyle(false);
		setBodyBorder(false);
		
		TabItem demo = new TabItem();
		demo.setScrollMode(Scroll.AUTO);
		demo.setHideMode(entry.getHideMode());
		demo.add(entry.getProgram());  // RiaAppModel에서 add한 프로그램
		add(demo);
	}
}

☞ ContentView 클래스의 onShowPage() 메소드를 아래와 같이 추가 코딩하도록 하자.
	public void onShowPage(Entry entry) {
		System.out.println("ContentView.onShowPage() called.." + entry.get("page"));
		Page page = entry.get("page");
	    if (page == null) {	// entry에 page가 세팅되어 있지 않다면 이 Entry는 우측 프로그램 영역에 없는 Entry이다.
	      page = new Page(entry);	// Page객체를 생성하고
	      entry.set("page", page); // entry에 page를 추가한다.
	    }
	    /**
	     * page id로 검색해서 없다면 추가하는 로직
	     */
		TabItem item = tabPanel.findItem("page__" + page.getId(), false);
		if (item == null) {
			item = new TabItem();
			item.setData("entry", entry);
			item.setClosable(entry.isClosable());
			item.setId("page__" + page.getId()); // 나중에 찾을 수 있도록 아이디 세팅
			item.setText(entry.getName());
			item.setLayout(new FitLayout());
			item.add(page); // TabItem
			tabPanel.add(item);
		}
	}
☞ 이제 다시 실행해 보도 록 하자 실행된 브라우저에서 F5키를 누르면 RiaAppModel에 추가한 SmplProgram이 프로그램 영역에 보이는 것을 확인 할 수 있다.
 
3. 여기까지 해서 프로그램영역에 신규로 작성한 프로그램이 출력되도록 코딩하였다. 그러나 위의 그림처림 "시스템사용자관리"가 화면에 출력되는 것은 좌측 메뉴를 클릭해서 출력되는 것이 아니라 RiaApp.java에 RiaAppModel클래스에 등록된 프로그램중 0번째 즉 첫번째 프로그램이 출력되도록 코딩되어 있어 보여지는 것이다.
 등록된 프로그램이 복수개이면 좌측 메뉴를 클릭해서 우측 프로그램 영역에 보여지도록 해야한다. 보통은 Model에 등록된 0번째 프로그램은 게시판이나 전체상황을 볼수 있는 상황판이 떠야하므로 0번째 프로그램에는 게시판이나 상황판을 배치하고 이 후부터 업무용 프로그램을 배치하도록 하는것이 맞을 듯 하다.

이제부터는 여러개의 프로그램을 등록하고 좌측 메뉴의 프로그램을 클릭해서 우측 프로그램 영역에 출력되도록 만들어보자.

☞ SmplProgram.java를 아래와 같이 수정하자.
public class SmplProgram  extends LayoutContainer {
	String txt = "";
	public SmplProgram(String txt) {
		this.txt = txt;
	}
	@Override
	protected void onRender(Element parent, int index) {
		// TODO Auto-generated method stub
		super.onRender(parent, index);
		VerticalPanel vp = new VerticalPanel();
		vp.setSpacing(10);
		Button btn =new Button(txt);
		setLayout(new FlowLayout(10));
		vp.add(btn);
		add(vp);
	}
}


☞ RiaAppModel을 아래와 같이 수정하여 여러개의 프로그램을 좌측메뉴에 추가해보자.
	public RiaAppModel() {
		Category pgm = new Category("시스템관리","SYS00002");
		pgm.add("시스템사용자관리1", "0001", new SmplProgram("시스템사용자관리1"));
		pgm.add("시스템사용자관리2", "0002", new SmplProgram("시스템사용자관리2"));
		pgm.add("시스템사용자관리3", "0003", new SmplProgram("시스템사용자관리3"));
		pgm.add("시스템사용자관리4", "0004", new SmplProgram("시스템사용자관리4"));
		addCategory(pgm);
		loadEntries(this);
	}
☞ 여기까지 실행해서 좌측 트리메뉴를 확인하면 위에서 추가한 4개의 트리가 보일 것 이다. 그러나 트리를 클릭해도 아무런 반응이 없을 것이다. 이제 트리를 클릭했을 경우 우측 프로그램 영역에 프로그램이 보여지도록 하자. 좌측 영역이므로 NavigationView클래스에서 해당 트리가 SelectionChanged 될 경우 클릭한 프로그램이 보이도록 수정하자. 아래와 같이 NavigationView.java의 initialize()메소드에 추가 코딩을 하자.
	@Override
	protected void initialize() {
		super.initialize();
		model = (RiaAppModel) Registry.get(RiaApp.MODEL);
		/**
		 * 트리내에서 다른 트리를 클릭했을 경우.
		 */
		SelectionService.get().addListener(
				new SelectionChangedListener() {
					public void selectionChanged(SelectionChangedEvent event) {
						List sel = event.getSelection();
						if (sel.size() > 0) {
							TreeModel m = (TreeModel) event.getSelection().get(0);
							if (m != null && m instanceof Entry) {
								RiaApp.showPage((Entry) m);
							}
						}
					}
				}
		);
		// 필터 생성
		....
☞ 이제 실행해서 작동이 되는지 확인해보자. 실행하여 좌측 트리메뉴를 펼쳐 클릭해보면 아래와 같이 클릭한 프로그램에 우측 TabPanel에 TabItem으로 추가되는 것을 볼수 있다.

☞ 마지막으로 프로그램 영역의 맨 아래 부분에 대한 처리를 추가하도록 하자. 우리는 프로그램 영역의 아래쪽에 도움말 탭을 추가하려고 한다. 즉 현재 위의 그림과 같이 보여지고 있는 프로그램 영역과 함께 도움말 탭을 추가하여 해당 프로그램에 대한 도움말을 추가할 영역을 만들어 주자. 도움말 영역은 html파일이 추가 될 수 있도록 url을 지정가능하게 하자.

// Entry.java
	public String getSourceUrl() {
		Object o = (Object) get("program");
		String name = o.getClass().getName();

		name = name.substring(name.lastIndexOf("program.") + 9);
		name = "code/" + name + ".html";
		name = name.replaceFirst("\\.", "/");

		return name;
	}
// Page.java
		....
		TabItem demo = new TabItem();
		demo.setScrollMode(Scroll.AUTO);
		demo.setHideMode(entry.getHideMode());
		demo.setText("프로그램");
		demo.add(entry.getProgram());
		add(demo);
		if (entry.isClosable()) {
			TabItem source = new TabItem();
			source.setText("도움말");
			source.setUrl(entry.getSourceUrl());
			add(source);
		}
Posted by 베니94
GXT RIA App 만들기2011. 1. 18. 12:28

이전 강좌에서 우리는 AppView.java에 프로그램의 뼈대를 구성하였다. 이어 이번에는 좌측화면과, 프로그램영역을 제어할 Controller를 생성하고 이Controller에 의해 제어될 View를 구성해 보자.
1. 좌측 메뉴을 제어할 Controller와 AppView내의 viewport의 좌측에 해당되는 부분을 재정의할 View클래스를 생성한다.

☞ NavigationController.java
package com.gxt.riaapp.client.mvc;
import com.extjs.gxt.ui.client.mvc.AppEvent;
import com.extjs.gxt.ui.client.mvc.Controller;
import com.gxt.riaapp.client.AppEvents;
/** * 좌측트리메뉴를 제어할 컬트롤 클래스 
* @author KYS
* 
*/
public class NavigationController extends Controller{	
	private NavigationView view;	// AppView.java에서 정의한 viewport의 WestPanel을 구성함.	
	public NavigationController() {		
		registerEventTypes(AppEvents.Init);	// 사용할 이벤트 등록	
	}	
	public void initialize() {	    
		view = new NavigationView(this);	 
 	}	
	@Override	public void handleEvent(AppEvent event) {		
		forwardToView(view, event);	// 이벤트가 발생할 경우 view로 이동.	
	}
}
☞ NavigationView.java
package com.gxt.riaapp.client.mvc;
import com.extjs.gxt.ui.client.Registry;
import com.extjs.gxt.ui.client.mvc.AppEvent;
import com.extjs.gxt.ui.client.mvc.Controller;
import com.extjs.gxt.ui.client.mvc.View;
import com.extjs.gxt.ui.client.widget.ContentPanel;
import com.extjs.gxt.ui.client.widget.TabItem;
import com.extjs.gxt.ui.client.widget.TabPanel;
import com.extjs.gxt.ui.client.widget.TabPanel.TabPosition;
import com.extjs.gxt.ui.client.widget.layout.FitLayout;
/** * AppView클래스의 WEST_PANEL을 구성한다. 
*  
* @author KYS 
*  
*/
public class NavigationView extends View {	
	private ContentPanel westPanel;	
	private TabPanel tabPanel;	
	private TabItem listItem, treeItem;	

	@Override	protected void initialize() {		
		// TODO Auto-generated method stub		
		super.initialize();		
		// AppView의 좌측 패널로 정의된 ContentPanel을 얻어온다. 이렇게 얻어오기 위해선		
		// AppView에 Registry.register(WEST_PANEL, westPanel); 와 같이 기술되어 있어야한다.		
		westPanel = (ContentPanel) Registry.get(AppView.WEST_PANEL);		
		westPanel.setHeading("Navigation");		
		westPanel.setLayout(new FitLayout());		
		westPanel.add(createTabPanel());		
		westPanel.syncSize();	
	}	
	public NavigationView(Controller controller) {		
		super(controller);	
	}	
	@Override	protected void handleEvent(AppEvent event) {	
	}	
	private TabPanel createTabPanel() {		
		tabPanel = new TabPanel();		
		tabPanel.setMinTabWidth(70);		
		tabPanel.setBorderStyle(false);		
		tabPanel.setBodyBorder(false);		
		tabPanel.setTabPosition(TabPosition.BOTTOM);	
	
		treeItem = new TabItem();		
		treeItem.setText("Tree");		
		tabPanel.add(treeItem);	
	
		listItem = new TabItem();		
		listItem.setText("List");		
		tabPanel.add(listItem);		
		return tabPanel;	
	}
}
☞ 위에서 주목해야 할 부분은 "westPanel = (ContentPanel) Registry.get(AppView.WEST_PANEL);"으로 AppView.java에 아래와 같이 추가해야한다. AppView에서 각 영역을 생성했고 이 생성된 영역을 Registry에 등록시키므로 해서 어디서든 Registry.get()으로 이용할 수 있다. 세션처럼 말이다.
/** 
* 좌측 메뉴영역 
*/
private void createWest() {    
	...	
	viewport.add(westPanel, data);	
	Registry.register(WEST_PANEL, westPanel);  // Rigistry에 등록
}
/** 
* 중간 프로그램 영역 
*/
private void createCenter() {    
	....		
	viewport.add(centerPanel, data);	
	Registry.register(CENTER_PANEL, centerPanel); // Rigistry에 등록
}
/** * 상단 영역 구성하기 */
private void createNorth() {    
	....	
	viewport.add(northPanel, data);	
	Registry.register(NORTH_PANEL, northPanel);  // Registry에 등록
}
☞ NavigationController를 RiaApp내의 Dispatcher에 추가하자.
	
public void onModuleLoad() {		
	dispatcher = Dispatcher.get();	    
	dispatcher.addController(new AppController());	    
	dispatcher.addController(new NavigationController());  // 좌측	    
	dispatcher.dispatch(AppEvents.Init);	
}

 

☞ 여기까지 코딩이 끝났다면 실행시켜 결과를 확인을 해보자. 좌측 메뉴 패널이 변경된 것을 확인 할 수 있다.


☞ 이제 남은 것은 툴바를 생성하고 툴바내에 필터를 추가할 것이고, 2개의 TabItem, Tree, List TabItem을 구현해야한다. 그러기 전에 2개의 TabItem에 들어갈 객체를 정의해야 한다. 이 객체는 BaseTreeModel을 상속 받고 getter와 setter메소드들로 채워져 있다. 아래는 Entry.java의 전문이다. com.gxt.riaapp.client.mvc 아래 model 패키지를 생성하고 Entry.java 추가하자.
package com.gxt.riaapp.client.mvc.model;
import com.extjs.gxt.ui.client.Style.HideMode;
import com.extjs.gxt.ui.client.data.BaseTreeModel;
import com.extjs.gxt.ui.client.widget.LayoutContainer;
/** 
* 좌측 메뉴의 TreeItem과 ListItem에 채워질 entity class 
*  
* @author winkos1 
*  
*/
public class Entry extends BaseTreeModel {	
	private String name;	
	private String id;	
	private boolean fill;	
	private boolean closable = true;	
	private HideMode hideMode = HideMode.DISPLAY;	
	protected Entry() {	
	}		
	/**	 
	* Constructor	 
	* 	 
	* @param name	 
	* @param example	 
	* @param image	 
	*/	
	public Entry(String name, LayoutContainer example, String image) {		
		this.name = name;		
		set("name", name);		
		set("image", image);		
		set("example", example);	
	}	

	public Entry(String name, LayoutContainer example) {		
		this.name = name;		
		set("name", name);		
		set("example", example);	
	}	

	public Entry(String name, String id, LayoutContainer example) {		
		this.name = name;		
		set("name", name);		
		set("id", id);		
		this.id = id;		
		set("example", example);	
	}	

	public Entry(String name, LayoutContainer example, String image,	
				boolean fill) {		
		this(name, example, image);		
		this.fill = fill;	
	}	

	public Entry(String name, LayoutContainer example, String image,	
			boolean fill, boolean closable) {		
		this(name, example, image, fill);		
		this.closable = closable;	
	}	

	public Entry(String name, LayoutContainer example, String image,			
		boolean fill, boolean closable, HideMode hideMode) {		
		this(name, example, image, fill, closable);		
		this.setHideMode(hideMode);	
	}	

	public String getId() {		
		return id;	
	}		

	public HideMode getHideMode() {		
		return hideMode;	
	}	

	public String getName() {		
		return (String) get("name");	
	}	

	public boolean isClosable() {		
		return closable;	
	}	

	public boolean isFill() {		
		return fill;	
	}	

	public void setFill(boolean fill) {		
		this.fill = fill;	
	}	

	public void setHideMode(HideMode hideMode) {		
		this.hideMode = hideMode;	
	}	

	public String toString() {		
		return getName();	
	}
}

☞ 이번에는 Entry클래스의 상위클래스를 생성하자. 이클래스는 Category클래스로 트리에서 parent역할을 담당할 클래스다. model 패키지에 Category.jav를 추가하고 아래와 같이 코딩하자.
package com.gxt.riaapp.client.mvc.model;
import java.io.Serializable;
import com.extjs.gxt.ui.client.Style.HideMode;
import com.extjs.gxt.ui.client.data.BaseTreeModel;
import com.extjs.gxt.ui.client.widget.LayoutContainer;
public class Category extends BaseTreeModel implements Serializable {	
	protected Category() {	
	}	

	public Category(String name) {		
		set("name", name);	
	}	

	public Category(String name, String id) {		
		set("name", name);		
		set("id", id);	
	}	

	public String getId(){		
		return  (String)get("id");	
	}	

	public String getName() {		
		return (String) get("name");	
	}	

	public String toString() {		
		return getName();	
	}	

	public void add(String title, LayoutContainer page, String image) {		
		add(new Entry(title, page, image));	
	}	

	public void add(String title, LayoutContainer page, String image,			
		boolean fill) {		
		add(new Entry(title, page, image, fill));	
	}	

	public void add(String title, LayoutContainer page) {		
		add(new Entry(title, page ));	
	}	
	
	public void add(String title, String id, LayoutContainer page) {			
		add(new Entry(title, id,  page ));	
	}	

	public void add(String title, LayoutContainer page, String image,			
		boolean fill, boolean closable, HideMode hideMode) {		
		add(new Entry(title, page, image, fill, closable, hideMode));	
	}
}

☞ NavigationView.java를 수정하자. 수정할 내용은 툴바를 추가하고 툴바내 필터를 삽입한다. 그리고 TreeItem과 ListItem을 구현하도록 하자.
	
private ToolBar toolBar;	
private StoreFilterField filter;
private TreePanel tree;	
private TreeStore treeStore;		
@Override	protected void initialize() {		
	// TODO Auto-generated method stub		
	super.initialize();		
	// 필터 생성		
	filter = new StoreFilterField() {			
		@Override			
		protected boolean doSelect(Store store,					
			ModelData parent, ModelData child, String property,					
			String filter) {								
			String name = child.get("name");				
			name = name.toLowerCase();				
			if (name.indexOf(filter.toLowerCase()) != -1) return true;				
			return false;			
		}		
	};		
	// AppView의 좌측 패널로 정의된 ContentPanel을 얻어온다. 이렇게 얻어오기 위해선		
	// AppView에 Registry.register(WEST_PANEL, westPanel); 와 같이 기술되어 있어야한다.		
	westPanel = (ContentPanel) Registry.get(AppView.WEST_PANEL);		
	westPanel.setHeading("Navigation");		
	westPanel.setLayout(new FitLayout());		
	westPanel.add(createTabPanel());
				
	toolBar = (ToolBar) westPanel.getTopComponent();	    
	IconButton filterBtn = new IconButton("icon-filter");	  
  	    
	filterBtn.setWidth(20);	    
	toolBar.add(filterBtn);	    
	toolBar.add(filter);	    	    

	createListContent();  // ListItem구현	    
	createTreeContent();	// TreeIem구현	
    		
	westPanel.syncSize();	
}	
private void createTreeContent() {	    
	TreeLoader loader = new BaseTreeLoader(new TreeModelReader>()) {	      
		@Override	      
		public boolean hasChildren(ModelData parent) {	        
			return parent instanceof Category;	      
		}	    
	};	    
	treeStore = new TreeStore(loader);	    
	tree = new TreePanel(treeStore);	    
	tree.expandAll();	    
	tree.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);	    
	tree.getStyle().setLeafIcon(IconHelper.createStyle("icon-list"));	    
	tree.setDisplayProperty("name");	
    
	SelectionService.get().addListener(new SourceSelectionChangedListener(tree.getSelectionModel()));	    
	SelectionService.get().register(tree.getSelectionModel());	
    
	filter.bind(treeStore);	    

	treeItem.setBorders(false);	    
	treeItem.setLayout(new FitLayout());	    
	treeItem.add(tree);	  
}		
@SuppressWarnings("unchecked")	
private void createListContent() {		
	listItem.setLayout(new FitLayout());	    
	listItem.setBorders(false);	    

	ListView list = new ListView();	    
	list.setDisplayProperty("name");	    
	list.setBorders(false);	    
	list.getSelectionModel().addSelectionChangedListener(new SelectionChangedListener() {	      
		@Override	      
		public void selectionChanged(SelectionChangedEvent se) {	    	  
			Entry e = se.getSelection().get(0);	        
			if (e != null && e instanceof Entry) {	        	
			// 나중에 구현	        		        
			}	      
		}	    
	});	    

	ListStore store = new ListStore();	    
	store.setStoreSorter(new StoreSorter(new Comparator() {	      
		public int compare(Entry e1, Entry e2) {	        
			return e1.getName().compareTo(e2.getName());	      
		}	    
	}));	    

	list.setStore(store);	    
	filter.bind((Store) store);	    
	listItem.add(list);	
}

☞ 여기까지 끝났다면 실행해보도록 하자. 실행 결과를 보면 "Navigation"클자 아래에 필터와 필터버튼이 추가된 것을 제외하곤 달라진 것이 없을 것이다. 아직 Tree에 아무것도 추가하지 않았으므로 보여지는것이 없는 것이다. 이제 좌측메뉴에 트리가 출력되도록 수정해보자.
☞ com.gxt.riaapp.client.mvc하위에 RiaAppModel.java를 추가하자. 이 클래스는 Category와 Entry클래스를 이용해 좌측 TreeItem과 ListItem에 출력될 수 있도록 구성한다. Category는 상위 폴더를 Entry는 폴더아래의 최종 프로그램이라고 할 수 있다.

package com.gxt.riaapp.client.mvc;
import java.util.ArrayList;
import java.util.List;
import com.extjs.gxt.ui.client.data.BaseTreeModel;
import com.extjs.gxt.ui.client.data.ModelData;
import com.gxt.riaapp.client.mvc.model.Category;
import com.gxt.riaapp.client.mvc.model.Entry;
public class RiaAppModel  extends BaseTreeModel {	
	protected List entries = new ArrayList();	

	public RiaAppModel() {		
		Category pgm = new Category("시스템관리","SYS00002");
		//		pgm.add("시스템사용자관리", "0001", new SystemManagerMain());		
		pgm.add("시스템사용자관리", "0001");		
		addCategory(pgm);		
		loadEntries(this);	
	}	

	public void addCategory(ModelData child) {		
		if(child != null)			
			add(child);	  
	}	

	public List getEntries() {		
		return entries;	
	}	

	private void loadEntries(TreeModel model) {		
		for (ModelData child : model.getChildren()) {			
			if (child instanceof Entry) {				
				entries.add((Entry) child);			
			} else if (child instanceof Category) {				
				loadEntries((Category) child);			
			}		
		}	
	}
}

☞ 위 클래스에서는 Category를 생성하고 생성된 Category클래스에 pgm.add()를 이용하여 Entry클래스를 하위로 추가한것을 볼 수 있다. Category클래스의 add()메소드를 보자. add메소드가 여러종류가 있지만 위에서 주석이 된 부분과 그 아래 부분의 add()메소드를 보면 주석달린 add(String title, String id, LayoutContainer page) 메소드는 인자로 LayoutContainer를 받고 있다.
☞ 이 클래스는 우리가 여러개의 프로그램을 작성해서 우측 프로그램 영역에 보여지게 할 것인데 이때 작성되는 프로그램은 모두 LayoutContainer를 상속받아야 한다. 두번째 add(String title, String id)메소드는 테스트를 추가한 메소드로 화면상에 보여주기만 하려고 제목과 아이디만 인자로 받고 있다. 이 메소드를 추가하도록 하자 Category.java와 Entry.java에 아래와 같이 메소드를 추가하도록 하자.
// Category.java
public void add(String title, String id) {		
	add(new Entry(title, id ));	
}

// Entry.java	
public Entry(String name, String id) {		
	this.name = name;		
	set("name", name);		
	set("id", id);		
	this.id = id;	
}
☞ 이제 위에서 작성한 RiaAppModel클래스를 TreeItem과 ListItem에 추가하여 좌측메뉴에 "시스템관리->시스템사용자관리"가 보여지도록 수정해보자. RiaApp.java를 아래와 같이 수정하자.
public static final String MODEL = "RiaAppModel";
private RiaAppModel model;	

public void onModuleLoad() {		
	model = new RiaAppModel();	    
	Registry.register(MODEL, model);  // 최초 실행시 Registry에 RiaAppModel을 등록한다.	    		
	dispatcher = Dispatcher.get();	    
	dispatcher.addController(new AppController());	    
	dispatcher.addController(new NavigationController());  // 좌측	    
	dispatcher.dispatch(AppEvents.Init);	
}
☞ NavigationView.java를 열고 아래와 같이 모델이 세팅될 수 있도록 수정한다.
private RiaAppModel model;
...	
	@Override	
	protected void initialize() {		
		super.initialize();		
		model = (RiaAppModel) Registry.get(RiaApp.MODEL);
		...
	}
	private void createTreeContent() {
		...	    
		filter.bind(treeStore);	    
		loader.load(model); // RiaAppModel set	    	    
		treeItem.setBorders(false);
		...
	}	
	@SuppressWarnings("unchecked")	
	private void createListContent() {
		...	    
		ListStore store = new ListStore();	    
		store.setStoreSorter(new StoreSorter(new Comparator() {	      
			public int compare(Entry e1, Entry e2) {	        
				return e1.getName().compareTo(e2.getName());	      
			}	    
		}));	    
		store.add(model.getEntries());	    	    
		list.setStore(store);
		...
	}

☞ 실행해서 결과를 확인해보자. 아래의 그림과 같이 좌측 트리에 "시스템관리->시스템사용자관리"가 출력되는 것을 볼 수 있다. 필터에 키인을 하고 잘 작동하는지도 확인한다.

Posted by 베니94
GXT RIA App 만들기2011. 1. 17. 23:56
오늘은 이전 강좌에 이어 프로그램의 뼈대를 완성해 보자


AppView.java를 마져 수정하자. AppView클래스는 아래와 같이 크게 4개의 변수가 사용된다.

  1. northPanel     : 상단 구성에 사용 될 객체로 Html로 구성할 것이므로 HtmlContainer를 사용하도록 한다.
  2. westPanel     : 좌측 메뉴영역으로 ContentPanel클래스를 사용하도록 하자. ContentPanel은 다양한 컴포넌트를 탑재할 수 있는 클래스로 앞으로도 많이 사용될 것이다.
  3. contentPanel : 중간 프로그램 영역으로 좌측과 동일하게 ContentPanel클래스를 사용하도록 하자
  4. viewport       : 위의 1~3의 영역을 올려 놓을 객체로 Viewport클래스를 사용한다. Viewport클래스는 Layout에 따라 컴포넌트를 탑재할 수 클래스이다.

▶ AppView.java를 수정하자. 좌,우,상단의 변수를 설정하자. 또한 initialize()함수를 통해 각 영역을 탑재할 viewport를 생성한다. viewport가 화면에 보여지기 위해선 RootPanel에 viewport를 add하자.
 public static final String CENTER_PANEL = "centerPanel";
public static final String WEST_PANEL = "westPanel";
public static final String NORTH_PANEL = "northPanel";
public static final String VIEWPORT = "viewPort";
private Viewport 		viewport; 	// 상단, 좌측, 중간의 3부분을 탑재 할 viewportt
private HtmlContainer 	northPanel;	// 상단영역
private ContentPanel 	westPanel;	// 좌측 메뉴영역
private ContentPanel 	centerPanel;// 중간 프로그램영역

@Override
protected void initialize() {
    super.initialize();
    viewport = new Viewport();
    viewport.setLayout(new BorderLayout());

    RootPanel.get().add(viewport);
}
▶ 이제 각각의 영역을 생성할 메소드를 코딩해 보자. 아래 메소드들은 각각의 영역에 맞게 HtmlContainer, ContentPanel등을 생성하여 viewport에 추가하고 있다.
/**
 * 상단 영역 구성하기
 */
private void createNorth() {
    StringBuffer sb = new StringBuffer();
    sb.append("");
    sb.append("");
    sb.append("");
    sb.append("
상단 영역
"); northPanel = new HtmlContainer(sb.toString()); BorderLayoutData data = new BorderLayoutData(LayoutRegion.NORTH, 75); data.setMargins(new Margins(0,0,0,0)); viewport.add(northPanel, data); } /** * 좌측 메뉴영역 */ private void createWest() { BorderLayoutData data = new BorderLayoutData(LayoutRegion.WEST, 220, 150, 320); data.setMargins(new Margins(5, 5, 5, 5)); data.setCollapsible(true); westPanel = new ContentPanel(); ToolBar toolBar = new ToolBar(); westPanel.setTopComponent(toolBar); viewport.add(westPanel, data); } /** * 중간 프로그램 영역 */ private void createCenter() { centerPanel = new ContentPanel(); centerPanel.setBorders(false); centerPanel.setHeaderVisible(false); centerPanel.setLayout(new FitLayout()); BorderLayoutData data = new BorderLayoutData(LayoutRegion.CENTER); data.setMargins(new Margins(5, 5, 5, 0)); viewport.add(centerPanel, data); }

▶ 위에서 만든 메소드를 initialize()함수에서 실행 하도록 코딩하자.
	@Override
	protected void initialize() {
		super.initialize();
		viewport = new Viewport();
		viewport.setLayout(new BorderLayout());
		
		createNorth();
		createWest();
		createCenter();
		
		RootPanel.get().add(viewport);
	}
▶ 이제 실행해서 결과물을 확인해 보도록 하자. RiaApp.gwt.xml파일에서 마우스 우측 클릭하고 Run As -> Web Application을 클릭 한다. 주소를 카피해서 브라우저에 실행 한다.
▶ 실행된 모습이다. 우리가 원했던 형태로 잘 구성된 것을 확인 할 수 있다.
Posted by 베니94
GXT RIA App 만들기2011. 1. 15. 03:44

1. 본격적인 코딩에 들어가기 전에 프로젝트 생성 시 만들어진 파일들을 정리하자

▶ RiaApp.java를 아래와 같이 정리한다.


▶ war 밑에 위치한 RiaApp.html을 아래와 같이 정리한다.


▶ RiaApp.gwt.xml파일을 아래와 같이 정리한다.


▶ 이제 gxt기능을 사용하기 위한 라이브러리를 등록하자. gxt.jar파일을 그림과 같이 lib폴더에 복사하고 Build Path를 잡아주자.




2. 이제 필요한 클래스를 하나씩 만들어 보자. 현재 상태에서 어플리케이션을 실행해도 아무것도 나오지 않는다.
우리는 여기에 하나씩 살을 붙여 UI를 구성해 보도록 하자


▶ 이번 강좌에서 필요한 클래스는 3가지 클래스가 필요하다.
 AppEvents.java  EventType클래스를 이용해 이벤트를 정의 한다.
 AppController.java  Controller 클래스를 상속받아 AppView가 실행될 수 있도록 돕는다.
 Controller 클래스는 MVC모델의 C에 해당된다고 생각하면 되겠다.
 AppView.java  View클래스를 상속받아 화면에 보여질 UI구성요소 등을 정의한다.
 MVC모델의 V에 해당되겠다.

▶ 일단 AppEvents.java에 이벤트를 정의 하자. 이름에서 대강의 쓰임새를 알 수 있을 것이다. 이렇게 이벤트를 정의하여
    Controller나 View에서 사용할 수 있는 것이다.
package com.gxt.riaapp.client;
import com.extjs.gxt.ui.client.event.EventType;
public class AppEvents {
    public static final EventType Init = new EventType();
    public static final EventType ShowPage = new EventType();
    public static final EventType HidePage = new EventType();
    public static final EventType TabChange = new EventType();
    public static final EventType ChartLoading = new EventType();
    public static final EventType Login = new EventType();
    public static final EventType Error = new EventType();
}
▶ 이번에는 우리가 만들 어플리케이션의 뼈대를 구성할 View 클래스를 생성해 보자. 아래 클래스는 View클래스를
    상속 받고 최소한의 생성자와 handleEvent() 메소드만 정의 되어 있다. 여기에 살을 붙여 기본 틀을 구성하게 된다.
package com.gxt.riaapp.client.mvc;

import com.extjs.gxt.ui.client.mvc.AppEvent;
import com.extjs.gxt.ui.client.mvc.Controller;
import com.extjs.gxt.ui.client.mvc.View;

public class AppView extends View {
    public AppView(Controller controller) {
        super(controller);
    }
    @Override
    protected void handleEvent(AppEvent event) {
    }
}
▶ 다음은 위의 두개의 클래스를 통제할 Controller클래스를 생성한다. 이 클래스는 최초의 Init이벤트가 호출될 시 AppView로
    이동하여 기본 화면 틀을 보이도록 제어하는 역할을 하게 된다.
package com.gxt.riaapp.client.mvc;

import com.extjs.gxt.ui.client.mvc.AppEvent;
import com.extjs.gxt.ui.client.mvc.Controller;
import com.gxt.riaapp.client.AppEvents;

public class AppController extends Controller {
    private AppView appView;
    public AppController() {
        appView = new AppView(this);
        registerEventTypes(AppEvents.Init); // 등록과 동시에 handleEvent메소드 호출
    }
    @Override
    public void handleEvent(AppEvent event) {
        if (event.getType() == AppEvents.Init) { // 호출시 전달된 이벤트가 Init라면 
            forwardToView(appView, event);
        }
    }
}
▶ 이제 위에서 생성한 Controller를 RiaApp클래스에 탑재 해 보도록 하자. 아래 소스에서 주목할 부분은 Dispatcher 클래스에
    Controller를 추가하고 이벤트를 전달하여 Dispatcher내의 여러 Controller들이 움직이도록 하는데 있다.
package com.gxt.riaapp.client;

import com.extjs.gxt.ui.client.mvc.Dispatcher;
import com.google.gwt.core.client.EntryPoint;
import com.gxt.riaapp.client.mvc.AppController;

/**
 * Entry point classes define onModuleLoad().
 */
public class RiaApp implements EntryPoint {
	private Dispatcher dispatcher;
	public void onModuleLoad() {
		dispatcher = Dispatcher.get();
	    dispatcher.addController(new AppController());
	    dispatcher.dispatch(AppEvents.Init);
	}
}

3. 이제 AppView클래스를 수정하여 아래 그림과 같은 표대를 만들어 보자 .

Posted by 베니94
GXT RIA App 만들기2011. 1. 13. 21:09
강좌 소스를 공유하기 위해 구글프로젝트에 프로젝트를 생성하고 구글에서 제공하는 svn을 이용해
소스를 받을 수 있도록 세팅한다
.

 

1. 프로젝트를 생성하자
   - 구글 코드 code.google.co.kr에 접속하여 프로젝트를 생성한다.
   - 이를 통해 강좌 소스를 svn을 통해 내려받을 수 있다. 이부분은 따라하지 마세요



▶code.google.com으로 접속해서 ext-gwt-ria-app로 검색하면 아래처럼 우리가 생성한 프로젝트를 확인할 수 있다.


▶ File -> New -> Other를 선택한 후 아래화면에서 "SVN"입력한다.


 
▶ "Project from SVN"을 선택하고 "Next"를 클릭한면 아래화면을 확인 할 수 있다.
▶ "Create a new repository location"을 체크하고 "Next"를 클릭한다.


▶ URL란에 http://ext-gwt-ria-app.googlecode.com/svn/RiaApp/를 입력하고 "Next"를 클릭한다.


▶ Date를 체크하고 "Finish"를 클릭한다.


▶svn client가 위에서 정해 준 주소로 접속해 RiaApp프로젝트를 검색한다.


▶ "Next"를 클릭한다.


▶ 로컬디스크에 저장할 workspace를 선택한다. 별다른게 없다면 "Finish"를 클릭하자.


▶ 최종 로컬 이클립스에 내려온 모습니다. 이전에 프로젝트가 동일한 이름으로 만들어져 있었다면 이클립스는 프로젝트를
    지우고 svn서버에서 내려받은 파일로 프로젝트를 구성하게 될것 이다.


▶ 이 과정을 거치게 되면 강좌소스를 변경하게 되면 svn을 통해 소스를 내려받을 수 있게 된다. 간단하게 내가 소스를 수정하고
   수정된 소스를 내려받는 과정을 알아보도록 하자.
   사전에 RiaApp.java를 일부 수정하였다. 수정된 내용을 내려 받을 수 있는지 보도록 하자.
▶ 그전에 필자가 이미 소스를 수정해서 커밋했으므로 어떤 내용이 커밋되었는지 구글 코드를 통해 먼저확인 할 수 있다.
▶ 구글코드(code.google.com)에 접속해 "ext-gwt-ria-app"로 프로젝트를 검색하고 아래와 같이 "Source"를 클릭하여
    변경된 내용을 확인 할 수 있다. "r5 변경된 내용을 내려받는지 확인한다"는 조금전에 필자가 커밋한 내용이다.
    "r5"를 클릭하자


▶ "r5"를 클릭하면 변경된 내용이 어떤 파일인지 확인 할 수 있다. 변경된 파일은 RiaApp.java이고 로그메시지는 "변경된 내용..."이다.
    이제 자신의 이클립스의 svn을 통해 변경된 RiaApp.java를 내려받아 보자.


▶ 아래의 그림과 같이 RiaApp.java파일에서 마우스우측 클릭하고 Team->Update를 클릭하자. 만약 어떤 파일이 수정되었는지
    모른다면 프로젝트 루트에서 같은 방법으로 전체 파일을 업데이트 받을 수 있다.

▶ 아래와 같이 RiaApp.java가 svn을 통해서 필자가 업데이트 한 내용을 로컬로 내려받은 것을 확인 할 수 있다.


이렇게 해서 강좌에서 사용되는 소스파일을 간단하게 svn을 통해 내려받게 된다. 그러나 여러분에게는 권한이 없으므로 각자가 수정한 소스를 커밋할 수는 없다. 단 로컬에서 수정은 자유롭다. - 끝 -
Posted by 베니94
GXT RIA App 만들기2011. 1. 11. 00:40
1. Gxt를 사용할 수 있도록 프로젝트 설정을 변경하자.

 ▶ com.gxt.riaapp에서 우측 마우스 클릭하고 "Google Web Toolkit"-> Convert project into GWT project를 선택하자


▶ Configure for using Ext GWT (GXT)를 선택한다.

▶ 내려받은 Gxt모듈의 디렉토리를 지정해준다. 이 부분이 이해되지 않는다면 이전 강좌를 확인하고 Gxt라이브러리를
    다운받도록 하자.


▶ 아래와 같이 RiaApp.gwt.xml파일에 Gxt관련 한줄이 추가된 것을 확인 할 수 있다. 물론 내부적으로는 Gxt라이브러리
   홈을 지정해 줌으로써 classpath등이 잡히게 될 것이다.

Posted by 베니94