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. 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 , Ext Gwt강좌2011. 1. 11. 10:27
2011/01/06 - [Gxt , Ext Gwt강좌] - Ext Gwt[Gxt] 강좌07-GxtSmpl모듈 개발하기


1. 우리는 gxtsmpl.client아래에 GxtSmplService라는 이름으로 서비스를 생성하였다. 이제 생성된 서비스를 이용하여
     서비스내에 메소드를 작성하고 이 작성된 메소드를 Client UI에서 이용할 수 있도록 코딩하도록 하자.
우선 추가한 Service에 데이터베이스에서 테이블의 내용을 끌고 올 메소드를 정의하자.
// GxtSmplService.java   
public interface GxtSmplService extends RemoteService {   
/**  
 * Utility class for simplifying access to the instance of async service.  
 */  
  public static class Util {   
    private static GxtSmplServiceAsync instance;   
      public static GxtSmplServiceAsync getInstance(){   
        if (instance == null) {   
          instance = GWT.create(GxtSmplService.class);   
        }   
        return instance;   
      }   
  }   
  // Return type과 LoadConfig에 주목하자.   
  public BaseListLoadResult getSystemUserList(BaseListLoadConfig config, HashMap param) throws Throwable;   
}   
// GxtSmplServiceAsync.java   
public interface GxtSmplServiceAsync {   
  public void getSystemUserList(BaseListLoadConfig config, HashMap param,
 AsyncCallback> callback);   
}  
2. 위와 같이 GxtSmplService.java에 메소드를 정의하게 되면 같은 패키지의 GxtSmplServiceAsync.java와 GxtSmplServiceImpl.java에도 동일한 메소드가 정의 된다. GxtSmplService.java와 GxtSmplServiceAsync.java는 인터페이스로 메소드를 정의하고 실제 구현은 server패키지내의 GxtSmplServiceImpl.java에서 하게 된다. com.hansol.gxtsmpl.server.GxtServiceImpl.java를 열어서 아래와 같이 시스템 로그를 남겨 클라이언트에서 서버와 통신하는지 알아보도록 하자.
@Override  
public BaseListLoadResult getSystemUserList(BaseListLoadConfig config, HashMap param) 
throws Throwable {   
  System.out.println("호출되나.....");   
}  

이제 실제 클라이언트 단에서 위의 메소드를 콜하도록 GridSmplTabItem클래스의 prepareService메소드를 수정하자.
	
private GxtSmplServiceAsync service; // 서비스선언
private HashMap param = new HashMap(); // 파라메터 전달용 hashmap
	/**
	 * Desc : 서버통신모듈 관련
	 */
	public void prepareService(){
		downGridPanel = new ContentPanel();
		downGridPanel.setHeading("관리자 현황");
		downGridPanel.setWidth(800);
		downGridPanel.setHeight(400);
		service = (GxtSmplServiceAsync) Registry.get(GxtApp.SERVICE);
		proxy = new RpcProxy>() {
			@Override
			public void load(Object loadConfig,	AsyncCallback> callback) {
				//try{param.put("search_name", search_name.getValue());}catch(Exception e){System.out.println("!!!!!!!!!!!!!!!!"+ e.toString());};
				
				service.getSystemUserList((BaseListLoadConfig) loadConfig, param, callback); // 추가한 메소드 호출
			}
		};
		loader = new BaseListLoader>(proxy);
		loader.setRemoteSort(true);
		store = new ListStore(loader);
		store.setMonitorChanges(true);
	}
------------------------------------------------------------------------------------------
// GxtApp.java의 상단에 아래 내용 추가해주세요. 그래야 위에서 호출하는 GxtApp.SERVICE가 
// 에러가 안나겠죠.
------------------------------------------------------------------------------------------
// 서버와 통신모듈 선언
	public static final String SERVICE = "GxtSmplService";

3. 이제 예제를 실행하면 아래와 같이 Console창에 로그가 출력되는 것을 확인 할 수 있다.

4. 이제 Client와 Server가 통신하는 것을 확인했으니 Impl클래스를 제대로 구현해서 Grid에 내용이 표시되도록 해보자. 우리는 데이터베이스안 데이터를 Select해와야 하므로 디비와 통신하기 위한 설정이 필요하다. 여기서는 간단하게 jdbc드라이버와 jdbc드라이버를 구현하는 클래스를 만들도록 해보자.
우선 jdbc드라이버를 war/WEB-INF/lib폴더에 카피하고 이클립스가 인식하도록 아래 그림과 같이 Build Path를 잡아주도록 하자.


5. 이제 DBSource라는 클래스를 만들고 디비커넥션을 맺어 올수 있도록 코딩하자. 클래스는 server 아래에 두도록 한다.
package com.hansol.gxtsmpl.server;   
  
import java.sql.Connection;   
import java.sql.DriverManager;   
  
public class DBSource {   
  static String driver;   
  static String url;   
  static String user;   
  static String passwd;   
  
  public static  Connection getConnection( String name) throws Exception{   
    driver = "oracle.jdbc.driver.OracleDriver";   
    url = "jdbc:oracle:thin:@디비서버아이피:포트:SID";      
    user = "user명";   
    passwd = "패스워드";   
    Class.forName(driver);   
    Connection con = DriverManager.getConnection(url, user, passwd);   
    return con;       
  }   
}  
6. 이제 Impl클래스의 메소드를 변경해보자. 기존 System.out.println("호출되나''");는 삭제하고 아래와 같이 코딩하자.
@Override  
public BaseListLoadResult getSystemUserList(   
  BaseListLoadConfig config, HashMap param) throws Throwable {   
  // return시 java.util.List에 LoginUserInfo객체를 차곡차곡 쌓아 리턴한다.   
  List returnValue = new ArrayList();   
  Connection conn = null; // 데이터베이스 커넥션 객체   
  PreparedStatement pstmt = null;  // 쿼리를 실행할 Statement객체   
  ResultSet rs = null;  // 쿼리를 실행한 후 꺼내온 데이터를 담을 객체   
  StringBuffer sql = new StringBuffer(); // SQL을 정의할 StringBuffer클래스   
  try{   
    conn = DBSource.getConnection(); // DBSource클래스에서 커넥션을 생성한다.   
    // 이하 쿼리 정의    
    // 쿼리는 테이블 내의 데이터를 모두 가져온다.    
    // 이때 주의할 것은 Client Grid의 컬럼정의 시(new ColumnConfig("userSabun",    
    // "사번", 75)) ColumnConfig생성시 사용된 이름과 컬럼명이 동일해야 Sort처리시 쿼리에     
    // 문제가 발생하지 않는다 해서 아래 쿼리에서는 alias를 주어 실제 컬럼명과 Grid에서   
    //  사용된 컬럼명을 일치 시켜주도록 하고 SubQuery를 사용해 sort시 문제가 발생하지    
    // 않도록 조치했다.   
    sql.append(" select * from (");   
    sql.append(" Select userid userSabun, username userName,     \n");   
    sql.append("  orgoid userOrgOid, orgname userOrgName,    \n");   
    sql.append("  teamname userTeamName, inputdate,     \n");   
    sql.append("  confirmyn confirmYnTmp,        \n");   
    sql.append("  decode(confirmyn,'Y','예','아니오') confirmYnName \n");   
    sql.append("  From COMMONLOGINUSER         \n");   
    sql.append(" ) \n");   
    // Sort에 대한 처리 LoadConfig에서 Sort에 대한 속성이 정의 되어 있다.   
    // Grid화면에서 컬럼을 클릭할 때 마다 Sort에 대한 속성이 변경되어 서버에 전달된다.   
    if(config.getSortField() == null) sql.append(" order by username asc ");   
    else sql.append(" order by "+ config.getSortField()+" "+ config.getSortDir());   
    // 쿼리문을 통해 statement생성   
    pstmt = conn.prepareStatement(sql.toString());   
    // statement를 통해 생성 된 결과를 ResultSet에 담는다.   
    rs = pstmt.executeQuery();   
    // loop돌면서   
    while(rs.next()){   
      // LoginUserInfo객체를 생성   
      LoginUserInfo entity = new LoginUserInfo();   
      entity.setUserName(rs.getString("username"));   
      entity.setUserSabun(rs.getString("userSabun"));   
      entity.setUserOrgOid(rs.getString("userOrgOid"));   
      entity.setUserOrgName(rs.getString("userOrgName"));   
      entity.setUserTeamName(rs.getString("userTeamName"));   
      entity.setInputDate(rs.getDate("inputdate"));   
      entity.setConfirmYnName(rs.getString("confirmYnName"));   
      entity.setConfirmYnTmp(rs.getString("confirmYnTmp"));   
      // return할 java.util.List객체에 차곡차곡 쌓는다.   
      returnValue.add(entity);   
    }   
  } finally {   
    // 사용된 자원에 대한 해제는 필수적으로...   
    try{ rs.close(); }catch(Exception e){}   
    try{ pstmt.close(); }catch(Exception e){}   
    try{ conn.close(); }catch(Exception e){}   
  }   
  // java.util.List에 담긴 데이터를 최종 리턴Type인 BaseListLoadResult를 생성하여 리턴.   
  return new BaseListLoadResult (returnValue);   
}  


7. 이제 코딩이 끝났으니 이클립스 Console에서 빨간색 버튼을 클릭해 실행을 정지 시키고 다시 시작해보자 우리는 Server쪽 메소드를 변경하였으므로 이과정이 꼭 필요하다. Client쪽이 변경된 경우에는 브라우저를 리프레시하면 변경된 내용이 적용되지만 서버쪽은 꼭 리스타트해줘야 변경된 내용을 확인 할 수 있다.
내용에 비해 Grid가 좀 큰듯하여 TabItem의 Width를 500으로 Grid의 Height를 240으로 변경하였다.실행된 화면을 확인하자

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
GXT RIA App 만들기2011. 1. 9. 13:07

오늘 부터 Gxt를 이용해 실제 사용 가능 한 어플리케이션을 개발해보자 .
 이 어플리케이션에는 아래의 내용이 기본 들어가게 된다.

- 사용자 권한->사용자별 프로그램
- 세션 -> 특정 시간을 사용하지 않을 경우 재로그인 유도
- GXT의 모든 UI객체
- 다양한 종류의 RPC통신 방법(솔직히 몇개안됨)GXT를 이용해 이미 개발한 바 있는
 시스템의
소스를 일부 수정하여 만들어보도록 한다.


관련 기술
1. Gxt
2. iBatis
3. Database
4. Jsp
등등....

2011/01/06 - [Gxt, Gwt] - 스크린 샷
Posted by 베니94