앱엔진 짱이네요-_-
속도만 빨랐다면 호스팅을 여기로 옮겨도 뭐 문제가 없을 듯. 시스템적으로는 구글이 다 관리해주고, 개발환경도 매우 편하니...(하지만, DB 접근이 방식이 틀려서 힘든면도 있지만ㅠ)

pc랑 android랑 뭔가 싱크맺는 뭐 그런거 만드려다보니 여기까지 와버렸네요-_- 이걸 통해서 하면 좋을 것 같아서^^

암튼, Channel API를 제공하는데, 원하는 채널을 생성해서 그곳 페이지로 접속한 사용자들끼리 메세지를 주고 받을 수 있게 됩니다. 

앱엔진 채널부분 문서입니다.
http://code.google.com/intl/ko-KR/appengine/docs/java/channel/

보면 JavaAPI랑 JavascriptAPI 두개 있는데, 클래스도 몇 개 없습니다.
Java에서는 ChannelService를 가져와서 createChannel로 채널을 만든 뒤에, sendMessage함수로 메세지만 보내면 됩니다.
Javascript에서는 goog.appengine.Channel이라는 클래스가 존재하는데, 이걸로 오픈하고 메세지 받으면 됩니다.

일단..... 나중에 쓰려고 만든거다보니 Spring3.0과 jQuery가 들어갔네요-_-
http://mudchobo.tistory.com/470 이거 참조해서 환경 구축을 하면 됩니다~^^

ChannelController.java

package com.mudchobo.apps.exchangepp.controller;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.google.appengine.api.channel.ChannelMessage;
import com.google.appengine.api.channel.ChannelService;
import com.google.appengine.api.channel.ChannelServiceFactory;

@Controller
@RequestMapping("/channeltest")
public class ChannelController {
	
	private String channelName = "test";
	
	@RequestMapping(value="/test", method=RequestMethod.GET)
	public String test(Model model){
		ChannelService channelService = ChannelServiceFactory.getChannelService();
		String token = channelService.createChannel(channelName);
		model.addAttribute("token", token);
		return "test";
	}
	
	@RequestMapping(value="/open", method=RequestMethod.POST)
	@ResponseBody
	public String openChat(){
		ChannelService channelService = ChannelServiceFactory.getChannelService();
		channelService.sendMessage(new ChannelMessage(channelName, "open"));
		return "";
	}
	
	@RequestMapping(value="/send", method=RequestMethod.POST)
	@ResponseBody
	public String sendChat(@RequestParam("msg") String msg){
		System.out.println("message = " + msg);
		try {
			ChannelService channelService = ChannelServiceFactory.getChannelService();
			channelService.sendMessage(new ChannelMessage(channelName, URLEncoder.encode(msg, "UTF-8")));
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return "";
	}
}
최초 접속페이지는 /test 입니다.
여기서 채널을 생성해줍니다. 이 생성해서 나온 토큰을 client로 전달합니다. 그러면 그 client에서 goog.appengine.Channel("토큰값");을 통해 Channel을 생성합니다. 그러면 커넥션을 맺고 있게 되는겁니다.

클라이언트부분
test.jsp

<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page isELIgnored="false" %>
<!doctype html>
<html>
	<head>
		<script src="/_ah/channel/jsapi"></script>
		<script src="/js/lib/jquery-1.4.4.min.js" type="text/javascript"></script>
		<script type="text/javascript">
			var sendMessage = function(path, msg){
				var xhr = new XMLHttpRequest();
				xhr.open("POST", "/apps/channeltest/" + path + "?msg=" + encodeURIComponent(msg), true);
				xhr.send();
			}
			
			var onOpen = function(){
				sendMessage("open");
			};
			var onMessage = function(m){
				var msg = decodeURIComponent(m.data.split("+").join(" "));
				$("#message_box ul").append("<li>" + msg + "</li>");
				$("#message_box").scrollTop(999999999);
			};
			var onError = function(){
				alert("error");
			};
			var onClose = function(){
				alert("close");
			};
			
			$(document).ready(function(){
				var channel = new goog.appengine.Channel("${token}");
				var socket = channel.open();
				socket.onopen = onOpen;
				socket.onmessage = onMessage;
				socket.onerror = onError;
				socket.onclose = onClose;
				
				$("#send_btn").click(function(){
					sendMessage("send", $("#message").val());
					$("#message").val("");
				});
				$("#message").keyup(function(e){
					if (e.keyCode == 13){
						sendMessage("send", $("#message").val());
						$("#message").val("");
					}
				});
			});
		</script>
	</head>
	<body>
		<div id="message_box" style="width:500px;height:300px;overflow:scroll;font-size:12px">
			<ul></ul>
		</div>
		<input type="text" id="message" name="message" />
		<input type="button" value="보내기" id="send_btn" name="send_btn" /><br />
	</body>
</html>
일단 Channel클래스를 쓰려면 <script src="/_ah/channel/jsapi"></script>를 include해야해요. 그리고, new goog.appengine.Channel생성부분을 유심히 보면 함수로 onopen, onmessage, onerror, onclose를 등록할 수 있어요. 메세지를 받으면 onmessage가 호출이 되니 여기서 처리하면 됨 ㅇㅇ 보내는 것은 아까 java에서 만들어 놓은 url을 호출하면 됨. 끗~

예제주소는 여기
http://mudchobo.appspot.com/apps/channeltest/test

소스파일...귀찮아서 그냥....통째로.....-_-



ps. 한글이 깨지길래 보낼 때 인코딩하고, 다시 받을 때 url인코딩해서 보내고 받은 걸 푸는 방식으로 했더니 되네요. 

 
Posted by 머드초보

댓글을 달아 주세요

  1. BlogIcon 레몬에이드 2011.01.31 14:05 신고  댓글주소  수정/삭제  댓글쓰기

    음... 역쉬 만능! 못하시능게 없어! +ㅁ+

  2. JIN32 2011.03.09 15:47 신고  댓글주소  수정/삭제  댓글쓰기

    죄송하지만 한가지 여쭤볼께요.
    로컬에서 테스트할때

    <script src="/_ah/channel/jsapi"></script>

    이 부분에요.

    com.google.appengine.api.channel.dev.LocalChannelFailureException: Channel for application key null not found.

    이런 오류가 발생되는데...
    혹시 해결방법 알고 계시는지 궁금해서 댓글 남겨봅니다.

    암튼 좋은글 잘 보고 갑니다.
    감사합니다. ^^

  3. Jin 2011.09.06 00:57 신고  댓글주소  수정/삭제  댓글쓰기

    우와 전부터 앱엔진으로 채팅기능 구현하고싶었는데 channel api가 있는지 처음알았네요 좋은정보 감사합니다^^

  4. gslee 2011.09.22 08:41 신고  댓글주소  수정/삭제  댓글쓰기

    안녕하세요~
    구글앱엔진 java 를 공부하고있는데요안드로이드랑연결시켜서 간단하게 방명록처럼만드려고 하는데 한글이 전부깨지내요 ㅠ
    해결방법을 아시면 저에게 도움좀 부탁드립니다^^
    skyyy80@gmail.com

  5. gslee 2011.09.22 08:41 신고  댓글주소  수정/삭제  댓글쓰기

    안녕하세요~
    구글앱엔진 java 를 공부하고있는데요안드로이드랑연결시켜서 간단하게 방명록처럼만드려고 하는데 한글이 전부깨지내요 ㅠ
    해결방법을 아시면 저에게 도움좀 부탁드립니다^^
    skyyy80@gmail.com

 
여기저기 메이븐이 많이 쓰이는 것 같아 이참에 삽질을 해봤습니다.
Maven은 소프트웨어 프로젝트 관리툴인데, 의존적인 라이브러리를 서버와 연동해서 쉽게 업데이트를 해주며, 컴파일 및 배포 과정을 최소화하며 자동으로 테스트를 할 수 있게 도와주는 뭐 그런 툴인 듯 합니다.

한마디로 나름 그냥 편하려고 만든거라는거-_-

라이브러리를 수동으로 복사해서 lib폴더에 쳐넣는 행위를 막고 자동으로 라이브러리를 업데이트할 수 있게 해주는 것만해도 큰 장점인 것 같습니다.

1. Maven 다운로드 및 설치
http://maven.apache.org/download.html 여기서 최신버전인 3.0을 받아서 압축해제
환경변수 Path에 maven디렉토리/bin폴더를 걸어두셔야 어디서든 mvn을 때릴 수 있기에 추가!
환경변수 MAVEN_HOME을 maven디렉토리홈으로 해서 추가!

2. m2eclipse설치
이건 이클립스에서 maven템플릿파일을 쉽게 생성할 수 있는 플러그인입니다.
eclipse에서 Help -> Install New Software -> Add해서 url을 http://m2eclipse.sonatype.org/sites/m2e/ 로 한다음에 Maven Integration for eclipse를 체크하고 설치하면 됩니다 ㄷㄷ

3. Mavan프로젝트 생성
이제 Maven프로젝트 생성을 합니다.
New -> Project 하면 Maven Project가 새로 생겼음.
선택하고, location에서 디폴트로하고 Next하고, 우린 webapp을 만들꺼니까 groupid가 org.apache.maven.archetypes이고, Artifact Id가 maven-archetype-webapp을 선택하고 Next!
Group Id는 패키지명으로 대충 com.mudchobo.springtest라고 하고,
Artifact Id는 프로젝트 이름이니까 대충 SpringTest로....-_-

그리고 이제 cmd쳐서 콘솔로가서 SpringTest폴더로 이동. 아래 커맨드발동!
[code]mvn -Dwtpversion=2.0 eclipse:eclipse[/code]
그러면 wtp용 프로젝트로 변환이 되어있을겁니다.
그리고 이상하게 Java Compiler가 1.4로 맞춰져있는데, 1.6으로 맞춥니다.
프로젝트Properties에서 Java Compiler에서ㅓ 1.6으로 맞추고, 아래 Use default compliance setting체크해주시면 됨 ㅇㅇ
그리고 Project Facets에서 Java를 6.0으로 바꿔주시면 됨 ㅇㅇ

4. 의존성 라이브러리 추가
pom.xml
[code]<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.mudchobo.springtest</groupId>
  <artifactId>SpringTest</artifactId>
  <packaging>war</packaging>
  <version>0.0.1-SNAPSHOT</version>
  <name>SpringTest Maven Webapp</name>
  <properties>
      <spring.version>3.0.5.RELEASE</spring.version>
  </properties>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
    <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-webmvc</artifactId>
          <version>${spring.version}</version>
    </dependency>
    <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-web</artifactId>
          <version>${spring.version}</version>
    </dependency>
  </dependencies>
  <build>
    <finalName>SpringTest</finalName>
  </build>
</project>
[/code]
스프링 라이브러리 2개를 추가했습니다. 그러면 뭔가 웹에서 다운받으면서 라이브러리를 저장소에 저장해둡니다 ㄷㄷ 뭔가 알아서 처리하는 느낌!

5. 이제 스프링라이브러리를 쓰도록 web.xml수정 및 spring-servlet.xml추가!
web.xml
[code]<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
        http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

    <display-name>Court Reservation System</display-name>

    <servlet>
        <servlet-name>spring</servlet-name>
        <servlet-class>
            org.springframework.web.servlet.DispatcherServlet
        </servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>spring</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>
[/code]
spring-servlet.xml
[code]<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
</beans>[/code]

6. Maven Dependencies 라이브러리 추가.

그냥 실행하게 되면 메이븐에서 읽어드린 라이브러리가 추가되지 않아 Eclipse에 있는 Server에 SpringTest프로젝트를 추가하면 ClassNotFoundException에러가 납니다.
[code]심각: Error loading WebappClassLoader
  context: /SpringTest
  delegate: false
  repositories:
----------> Parent Classloader:
org.apache.catalina.loader.StandardClassLoader@1172e08
 org.springframework.web.servlet.DispatcherServlet
java.lang.ClassNotFoundException: org.springframework.web.servlet.DispatcherServlet[/code]
그래서 저도 뭔가 라이브러리를 추가해야된다는 생각에 구글링을 해보니 방법이 있군요!

해당 프로젝트폴더에 .setting폴더에 있는 org.eclipse.wst.common.component파일
org.eclipse.wst.common.component
[code]<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/test/java" />
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/test/resources" />[/code]위에 두줄 지우고,
[code]<dependent-module deploy-path="/WEB-INF/lib">
    <dependency-type>uses</dependency-type>
</dependent-module>[/code]3줄추가하고~

그리고 .classpath파일에 아래 클래스패스를 추가합니다.
.classpath
[code]<classpathentry exported="true" kind="con" path="org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER">
    <attributes>
        <attribute name="org.eclipse.jst.component.dependency" value="/WEB-INF/lib"/>
    </attributes>
</classpathentry>[/code]
그러면 프로젝트properties에서 Java Build Path에서 Library를 보면 Maven Dependencies가 추가되어있을겁니다~ 여기에는 pom.xml에서 라이브러리 추가한 것이 들어있어요~

이제 Server에 추가한다메 시작하면 톰캣 에러없이 동작할겁니다.

7. war파일 만들기~
프로젝트에 Export해도 되고, mvn으로 명령어를 때려도 됩니다.
[code]mvn package[/code] 때리면 packaging이 war로 되어있어서 war파일이 생성되더군요.
물론 이과정에서 ftp에 배포라던지 그런게 가능한 것 같습니다. 좀 더 연구해보고!

PS. 갑자기 느끼는 생각인데, 이것보다 더 간편한 방법이 있지 않을까 싶기도하고....-_- 후.....

참고자료
http://blog.v-s-f.co.uk/2010/09/jsf-2-1-project-using-eclipse-and-maven-2/
http://maven.apache.org/plugins/maven-eclipse-plugin/

 
Posted by 머드초보

댓글을 달아 주세요

 
앱엔진에 뭔가 제 사이트를 만들고 싶어서 이렇게 삽질을 하고 있는데, 망할 제한이 왜이렇게 많지-_-

암튼, "구글 앱 엔진"에서는 JPA를 지원합니다. 하지만, 이상하게도 잘 안됩니다-_- 굉장히 제한적으로 이것저것 막아둔 것 같습니다. 사실 구글 앱 엔진에서는 DataBase를 BigTable인지 뭐시기인지 그걸 사용하고, 직접적으로 접근을 못하기 때문에(전부 프로그래밍 또는 관리페이지(관리페이지도 매우 제한적인-_-)에서만 관리 가능), 이걸 이용하는 API에서도 엄청나게 뭔가 막아둔 것 같습니다.
뭐 좀 해보려고 하면 에러를 내뱉습니다. 검색해보면 구글앱엔진에서만 나는 에러입니다-_- 사실 아직 구글앱엔진이 프리뷰버전이기에 뭐라 따지지도 못하는 게 사실입니다^^ 정식버전(언제나오려나....Beta딱지 떼는데 10년넘게 걸리겠지-_-)나오면 매우 안정화가 되지 않을까 싶습니다^^

암튼, Spring3 + JPA의 조합으로 앱엔진에 올리는 건 성공했는데, 사실 스프링에서 제공하는 TransactionManager를 사용했어야 했는데, JPATemplate으로 뭔가 처리를 하면 잘 안되더군요-_- 일단 가져오고, persist하고, 이런건 잘 되는데, 왜 삭제가 안될까요-_- 삭제가 안되서 그냥JPATemplate빼고 했습니다-_-
JPATemplate사용해서 성공하신 분 트랙백좀 ㅠㅠ

0. 환경
Eclipse 3.5 + Google AppEngine Plugin + Spring 3.0.0
일단 스프링3다운로드 - http://www.springsource.org/download

1. 프로젝트 생성
New Project -> Google에 있는 Web Application Project 선택.
Project Name은 SosiSchedule. package는 com.mudchobo.
Use Google Web Toolkit은 체크해제. 사용안할꺼라....(이것도 언제한번 공부해야하는데-_-)
Finish.

2. 라이브러리 복사 및 build path추가
spring3에서는 spring.jar가 산산조각 났어요. 필요한 것만 넣으면 되는 듯.
일단 제가 사용한 것은....
org.springframework.asm-3.0.0.RELEASE.jar
org.springframework.beans-3.0.0.RELEASE.jar
org.springframework.context-3.0.0.RELEASE.jar
org.springframework.core-3.0.0.RELEASE.jar
org.springframework.expression-3.0.0.RELEASE.jar
org.springframework.orm-3.0.0.RELEASE.jar
org.springframework.web.servlet-3.0.0.RELEASE.jar
org.springframework.web-3.0.0.RELEASE.jar
그리고, jstl을 사용할 것이기에....
jstl.jar와 standard.jar
※이번버전에서는 lib폴더가 없습니다-_- 어디서 찾아야하는 거지-_- 암튼 그래서 2.5.6버전에서 가져왔습니다^^

앱엔진에서는 lib폴더 복사로 libpath가 잡히지 않네요. 그래서 각각 다 추가해줘야한다는...-_-
일단 war/WEB-INF/lib폴더에 복사 후에 복사한 파일 선택 후 오른쪽버튼 후, Build Path -> Add to Build Path 선택하면 됩니다^^

3. web.xml파일 수정
web.xml
[code]<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>2</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/sosischedule/*</url-pattern>
</servlet-mapping>

<welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
</welcome-file-list>[/code]
일단 sosischedule/*요청은 spring이 받습니다.

4. dispacher-servlet.xml파일과 persistence.xml파일 생성
war/WEB-INF/폴더에 생성
dispatcher-servlet.xml
[code]<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.mudchobo" />
   
    <bean id="entityManager"
        factory-bean="EMF"
        factory-method="get" />
   
    <bean id="viewResolver"
        class="org.springframework.web.servlet.view.InternalResourceViewResolver"
        p:prefix="/WEB-INF/jsp/" p:suffix=".jsp" />
</beans>[/code]

src/META-INF/ 폴더에 생성
persistence.xml
[code]<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
        http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">
   
    <persistence-unit name="transactions-optional">
        <provider>org.datanucleus.store.appengine.jpa.DatastorePersistenceProvider</provider>
        <properties>
            <property name="datanucleus.NontransactionalRead" value="true"/>
            <property name="datanucleus.NontransactionalWrite" value="true"/>
            <property name="datanucleus.ConnectionURL" value="appengine"/>
        </properties>
    </persistence-unit>
</persistence>[/code]

5. EMF클래스 생성.
이제 jpa접근할 수 있는 EntityManagerFactory클래스(EMF)를 생성해봅시다.
com.mudchobo.sosi.sosischedule.dao.EMF.java
[code]package com.mudchobo.sosischedule.dao;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

import org.springframework.stereotype.Component;

@Component
public final class EMF {
     private static final EntityManagerFactory emfInstance =
        Persistence.createEntityManagerFactory("transactions-optional");

    private EMF() {}

    public EntityManager get() {
        return emfInstance.createEntityManager();
    }
}[/code]

6. Entity클래스 생성
일단 Sosi와 Schedule이라는 Entity를 생성할 건데요. 둘의 관계는 1:N관계입니다.
com.mudchobo.sosischedule.entity.Sosi.java
[code]package com.mudchobo.sosischedule.entity;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;

import com.google.appengine.api.datastore.Key;

@Entity
public class Sosi implements Serializable {
    private static final long serialVersionUID = 5448408922872112420L;

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Key key;
   
    private String sosiName;
   
    @OneToMany(mappedBy="sosi", cascade=CascadeType.ALL)
    private List<Schedule> scheduleList = new ArrayList<Schedule>();

    public Key getKey() {
        return key;
    }

    public void setKey(Key key) {
        this.key = key;
    }

    public List<Schedule> getScheduleList() {
        return scheduleList;
    }

    public void setScheduleList(List<Schedule> scheduleList) {
        this.scheduleList = scheduleList;
    }

    public String getSosiName() {
        return sosiName;
    }

    public void setSosiName(String sosiName) {
        this.sosiName = sosiName;
    }

    public Sosi() {
       
    }
   
    public Sosi(Key key, String sosiName) {
        super();
        this.key = key;
        this.sosiName = sosiName;
    }
}[/code]
com.mudchobo.sosischedule.entity.Schedule.java
[code]package com.mudchobo.sosischedule.entity;

import java.io.Serializable;

import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;

@Entity
public class Schedule implements Serializable{
    private static final long serialVersionUID = -8676837674549793653L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Key key;
   
    private String program;
   
    @ManyToOne(fetch=FetchType.LAZY)
    private Sosi sosi;
   
    public Sosi getSosi() {
        return sosi;
    }

    public void setSosi(Sosi sosi) {
        this.sosi = sosi;
    }

    public Key getKey() {
        return key;
    }

    public void setKey(Key key) {
        this.key = key;
    }
   
   
    public String getKeyString() {
        return KeyFactory.keyToString(key);
    }
   
    public String getProgram() {
        return program;
    }

    public void setProgram(String program) {
        this.program = program;
    }
   
    public Schedule() {
    }

    public Schedule(String program, Sosi sosi) {
        this.program = program;
        this.sosi = sosi;
    }
}[/code]
일단 App Engine용 JPA에서는 ID 타입이 Long이면 관계형태를 사용할 수 없더라구요. 그래서 앱엔진에서 제공하는 Key타입이 있는데, 이걸 이용해야합니다.

7. Dao만들기
com.mudchobo.sosisochedule.SosiDao.java
[code]package com.mudchobo.sosischedule.dao;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.google.appengine.api.datastore.KeyFactory;
import com.mudchobo.sosischedule.entity.Schedule;
import com.mudchobo.sosischedule.entity.Sosi;

@Repository
public class SosiDao {
    private EntityManager em;
   
    @Autowired
    public void setEntityManager(EntityManager em) {
        this.em = em;
       
        // 소시데이터 추가
        addSosi(new Long(1), "효연");
        addSosi(new Long(2), "윤아");
        addSosi(new Long(3), "수영");
        addSosi(new Long(4), "유리");
        addSosi(new Long(5), "태연");
        addSosi(new Long(6), "제시카");
        addSosi(new Long(7), "티파니");
        addSosi(new Long(8), "써니");
        addSosi(new Long(9), "서현");
    }
   
    public void addSosi(Long id, String sosiName) {
        em.getTransaction().begin();
        em.persist(new Sosi(KeyFactory.createKey(Sosi.class.getSimpleName(), id), sosiName));
        em.getTransaction().commit();
    }
   
    @SuppressWarnings("unchecked")
    public List<Sosi> getSosiList() {
        return em.createQuery("select s from Sosi s").getResultList();
    }

    public Sosi getSosi(Long sosiId) {
        return em.find(Sosi.class, sosiId);
    }
   
    @SuppressWarnings("unchecked")
    public List<Schedule> getScheduleList(final Long sosiId) {
        Query q = em.createQuery("select s.scheduleList from Sosi s where s.key = :key");
        q.setParameter("key", KeyFactory.createKey(Sosi.class.getSimpleName(), sosiId));
        return (List<Schedule>) q.getSingleResult();
    }
   
    public void addSchedule(Long sosiId, String program) {
        em.getTransaction().begin();
        Sosi sosi = em.find(Sosi.class, sosiId);
        sosi.getScheduleList().add(new Schedule(program, sosi));
        em.getTransaction().commit();
    }
   
    public void deleteSchedule(String scheduleKey) {
        em.getTransaction().begin();
        Schedule schedule = em.find(Schedule.class, scheduleKey);
        em.remove(schedule);
        em.getTransaction().commit();
    }
}[/code]
EntityManager받을 때 디폴트로 데이터를 넣어줘야 합니다(아까 위에서 말했듯이 프로그래밍적으로만 테이블을 생성할 수 있어서 이런 형태로 데이터를 넣어줘야합니다ㅠㅠ)

일단 실행해보고 데이터가 잘 생성되었는지 보려면 아래와 같은 주소로 접속해보면 됩니다.
http://localhost:8888/_ah/admin
일단 보고 삭제까지는 되는데, 테이블 생성같은 건 안되더라구요. 그리고 여기서 보여지는데에는 한글이 깨지는데 나중에 출력해보면 잘 나오니 걱정마시길-_-
사용자 삽입 이미지
8. Service 클래스 생성
com.mudchobo.sosischedule.service.SosiService.java
[code]package com.mudchobo.sosischedule.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.mudchobo.sosischedule.dao.SosiDao;
import com.mudchobo.sosischedule.entity.Schedule;
import com.mudchobo.sosischedule.entity.Sosi;

@Service
public class SosiService {
   
    @Autowired
    private SosiDao sosiDao;
   
    public List<Sosi> getSosiList()
    {
        return sosiDao.getSosiList();
    }
   
    public Sosi getSosi(Long sosiId) {
        return sosiDao.getSosi(sosiId);
    }
   
    public List<Schedule> getScheduleList(Long sosiId) {
        return sosiDao.getScheduleList(sosiId);
    }
   
    public void deleteSchedule(String scheduleKey) {
        sosiDao.deleteSchedule(scheduleKey);
    }

    public void addSchedule(Long sosiId, String program) {
        sosiDao.addSchedule(sosiId, program);
    }
}[/code]
Service에서 하는 역할은 뭐 없네요-_-

9. Controller생성
스프링3.0에서 새로 추가된 기능인 REST기능입니다.
com.mudchobo.sosischedule.controller.SosiController.java
[code]package com.mudchobo.sosischedule.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import com.mudchobo.sosischedule.entity.Schedule;
import com.mudchobo.sosischedule.entity.Sosi;
import com.mudchobo.sosischedule.service.SosiService;

@Controller
public class SosiController {
    private static String PREFIX = "/sosischedule";
   
    @Autowired
    private SosiService sosiService;
   
    @RequestMapping(value="/", method=RequestMethod.GET)
    public String index(Model model) {
        List<Sosi> sosiList = sosiService.getSosiList();
        model.addAttribute("sosiList", sosiList);
       
        return "index";
    }
   
    @RequestMapping(value="/schedule/{sosiId}", method=RequestMethod.GET)
    public String getSchedule(
            @PathVariable("sosiId") Long sosiId,
            Model model) {
        Sosi sosi = sosiService.getSosi(sosiId);
        List<Schedule> scheduleList = sosiService.getScheduleList(sosiId);
        model.addAttribute("scheduleList", scheduleList)
            .addAttribute("sosi", sosi);
       
        return "sosi";
    }
   
    @RequestMapping(value="/schedule/{sosiId}/add", method=RequestMethod.POST)
    public String addSchedule(
            @PathVariable("sosiId") Long sosiId,
            @RequestParam("program") String program,
            Model model
            ) {
        sosiService.addSchedule(sosiId, program);
       
        return "redirect:" + PREFIX + "/schedule/" + sosiId;
    }
   
    @RequestMapping(value="/schedule/{sosiId}/{scheduleKey}", method=RequestMethod.GET)
    public String removeSchedule(
            @PathVariable("sosiId") Long sosiId,
            @PathVariable("scheduleKey") String scheduleKey,
            Model model) {
        sosiService.deleteSchedule(scheduleKey);
       
        return "redirect:" + PREFIX + "/schedule/" + sosiId;
    }
}[/code]

10. View jsp파일 생성
소시 리스트를 보여주는 index파일 입니다.
war/WEB-INF/jsp/index.jsp
[code]<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="UTF-8"%>
<%@ page isELIgnored="false" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>소녀시대 스케줄</title>
</head>
<body>
    <div>
        스케줄 확인하기
        <ul>
        <c:forEach var="sosi" items="${sosiList}">
            <li><a href="/sosischedule/schedule/${sosi.key.id}">${sosi.key.id}. ${sosi.sosiName}</a></li>
        </c:forEach>
        </ul>
    </div>
</body>
</html>[/code]

해당 소시의 스케줄을 보여주는 스케줄 파일입니다.
war/WEB-INF/jsp/sosi.jsp
[code]<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="UTF-8"%>
<%@ page isELIgnored="false" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>소녀시대 스케줄</title>
</head>
<body>
    <div>
        스케줄 확인하기
        <ul>
        <c:forEach var="sosi" items="${sosiList}">
            <li><a href="/sosischedule/schedule/${sosi.key.id}">${sosi.key.id}. ${sosi.sosiName}</a></li>
        </c:forEach>
        </ul>
    </div>
</body>
</html>[/code]
리다이렉트를 위한 파일입니다. 기존 index.html파일 지우시고, index.jsp파일 생성
index.jsp
[code]<% response.sendRedirect("/sosischedule/"); %>[/code]

앱엔진에 올려보았습니다.
http://2.latest.mudchobosample.appspot.com/sosischedule/

잘 되는 것 같네요.
 
Posted by 머드초보

댓글을 달아 주세요

  1. 2010.02.08 20:00  댓글주소  수정/삭제  댓글쓰기

    비밀댓글입니다

    • 머드초보 2010.02.12 00:05 신고  댓글주소  수정/삭제

      안녕하세요~ 답변이 늦었네요 ㅠㅠ
      근데, 파일 다 올린 것 같은데...
      저도 만들어놓고 그냥 올린거다보니 ㅠㅠ
      뭐가 빠진건가요? ㅠㅠ

  2. 나그네 2012.11.21 16:26 신고  댓글주소  수정/삭제  댓글쓰기

    안녕하세요.. 구글 앱 엔진 검색하다 들렀습니다
    죄송한데 혹시 괜찮다면 위에서 보여주신 소스를 좀 부탁드려도 될까요??
    가능하시면 actors00@nate.com 으로 부탁좀 드릴게요

  3. BlogIcon timberland españa tiendas 2012.12.24 09:24 신고  댓글주소  수정/삭제  댓글쓰기

    Bruxelles célèbre par une rétrospective de grande ampleur l'expressionniste flamand Constant Permeke (1886-1952). Cet Anversois aimait à dessiner et peindre les marins et paysans des Flandres, http://timberlandbotases.com timberland mujer 2012, qu'il représentait en athlètes aux corps anguleux et massifs et leurs femmes, nues et charnues, http://timberlandbotases.com timberland pro, tout en rondeurs. Il aimait aussi les paysages plats et les cieux de son pays natal. Dans tous les cas, il allait à l'essentiel avec une rare vigueur et sans craindre la provocation. L'accrochage met en valeur ces qualités et propose d'inattendues et justes comparaisons avec deux artistes actuels, Marlene Dumas c?té nus et Thierry de Cordier c, http://timberlandbotases.com timberland baratas?té nuées. Retrouvez cet article sur lemonde.frAnne Frank, passion nippone'Lawrence d'Arabie' en blu-rayLa belle pagaille de Skip & DieFiguration de gauche, abstraction de droiteLa couleur d'une affiche dévoile-t-elle tout d'un film?? Inscrivez-vous aux newsletters du Monde, http://timberlandbotases.com timberland españa catalogo 2011.frDevenez fan de Yahoo, http://timberlandbotases.com timberland earthkeepers! Actu sur Facebook et suivez-nous sur Twitter, http://timberlandbotases.com botas timberland mujer.
    Related articles:


    http://sanmarinosd.com/forums/topic.php?id=967873&replies=1#post-1031745 http://sanmarinosd.com/forums/topic.php?id=967873&replies=1#post-1031745

    http://www.renaissancemoms.com/forums/topic.php?id=595981&replies=1#post-634156 http://www.renaissancemoms.com/forums/topic.php?id=595981&replies=1#post-634156

    http://www.networkleisure.com/social/blog_entry.php?user=fanshuan9441&blogentry_id=945812 http://www.networkleisure.com/social/blog_entry.php?user=fanshuan9441&blogentry_id=945812

 
그냥 막 하면 잘 안되더군요. 구글링을 해보니 여러 블로그에서 이런 시도를 한 흔적들이 있었습니다-_- App Engine이 자바를 지원한다고 할 때부터 외국에서는 다양한 시도를 하나봅니다-_- 이번 Spring BlazeDS Integration도 누가 먼저 시도를 한 흔적이 있었네요.

이번 Spring BlazeDS Integration 1.0.1릴리즈 기념과 Spring교육 끝난 기념으로 간만에 삽질해봤습니다.
하지만, messaging 등의 심화적인 것은 못해보구요. 우선 서비스를 가져오는지만 해봤습니다.

삽질환경

- IDE
Eclipse3.5와 구글앱앤진 플러그인 - http://code.google.com/intl/ko-KR/eclipse/docs/download.html
Flex Builder 3.0.2
JDK 1.6.0 U14
- 라이브러리
Spring Framework 2.5.6
BlazeDS 3.2.0.3978
Spring BlazeDS Integration 1.0.1
Jackson 1.2.0
Cglib 2.1.3

1. App Engine 프로젝트 생성
프로젝트 생성하고 나서 라이브러리들을 다 복사합니다. 저는 아래와 같이 라이브러리를 복사했습니다.
기존App Engine lib, spring.jar, spring-webmvc.jar, blazeds.war에 있는 lib, jackson-core-lgpl-1.2.0.jar, jackson-mapper-lgpl-1.2.0.jar, cglib-nodep-2.1_3.jar, org.springframework.flex-1.0.1.RELEASE.jar
그리고, appengine-web.xml파일에 한줄 추가합니다.
[code]<sessions-enabled>true</sessions-enabled>[/code]
이거 필요한건지는 잘 모르겠군요-_-
WEB-INF폴더아래 blazeds.war파일에 들어있는 flex폴더를 복사합니다(*-config.xml의 파일이 있는 것)

2. 서비스 클래스 생성
이제 서비스를 만들어봅시다. 초간단 헬로우서비스를-_-
src폴더에 만들어봅시다. 전 com.mudchobo.springblazedsserver.service패키지에 HelloService클래스를 생성했음!
HelloService.java
[code]package com.mudchobo.springblazedsserver.service;

public class HelloService {

    public String sayHello(String name) {
        return "Hello, " + name;
    }
}[/code]

3. 설정파일 생성 및 설정
스프링관련 설정을 해야해요. web.xml에서 디폴트로 설정된 servlet설정을 지우고 아래를 추가
web.xml
[code]<context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/config/*-context.xml
        </param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
   
    <!--  SpringDispatcherServlet -->
    <servlet>
        <servlet-name>flex</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>flex</servlet-name>
        <url-pattern>/messagebroker/*</url-pattern>
    </servlet-mapping>[/code]
flex라는 이름의 서블릿을 만들었으니 스프링 설정파일이름인 flex-servlet.xml을 생성합니다.
flex-servlet.xml
[code]<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:flex="http://www.springframework.org/schema/flex"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
        http://www.springframework.org/schema/flex
        http://www.springframework.org/schema/flex/spring-flex-1.0.xsd">
   
    <flex:message-broker />
   
    <flex:remoting-destination ref="helloService"/>
</beans>[/code]
flex라는 네임스페이스를 제공하는데요. <flex:message-broker />이 한줄로 모든 설정이 되어버립니다. M1 삽질했을 때에는 네임스페이스 없어서 bean써주고, 다 설정했던 기억이 나네요. 네임스페이스로 한줄로-_-

remoting-destination태그는 destination을 설정하는 건데, 해당 bean을 ref하면 해당 bean이름으로 destination으로 flex에서 가져올 수 있어요.
그럼 서비스를 설정할 설정파일을 생성해봅시다. configlocation을 /config/*-context.xml을 잡았는데, /config/services-context.xml파일을 만들어봅시다^^
services-context.xml
[code]<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">

    <bean id="helloService" class="com.mudchobo.springblazedsserver.service.HelloService" />
   
</beans>[/code]
방금 만든 HelloService를 bean으로 설정.

마지막으로 flex/services-config.xml에서 default-channels를 추가합시다.
[code]<services>
        <service-include file-path="remoting-config.xml" />
        <service-include file-path="proxy-config.xml" />
        <service-include file-path="messaging-config.xml" />
        <default-channels>
            <channel ref="my-amf"/>
        </default-channels>       
    </services>[/code]
그리고 이것도 추가해야해요.
[code]<system>
        <manageable>false</manageable>
            ....
       </system>[/code]
이거 추가안하면 앱엔진에서 이런 에러로그를 뿜음-_-
org.springframework.beans.factory.BeanCreationException: Error creating bean with name '_messageBrokerDefaultHandlerMapping': Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name '_messageBroker': Invocation of init method failed; nested exception is org.springframework.beans.factory.BeanInitializationException: MessageBroker initialization failed; nested exception is java.lang.NoClassDefFoundError: java.lang.management.ManagementFactory is a restricted class. Please see the Google App Engine developer's guide for more details.

관리자 기능이라고 하는 것 같은데, 정확히 뭔지는 잘 모르겠지만, 끄면 잘 됩니다-_-

4. 클라이언트 프로젝트 생성
flex project를 생성할 때 이렇게 생성해주면 편합니다.
Flex Project선택 -> Project name쓰고, Application server type은 J2EE, Create combined Java~~는 체크해제, Use remote object access service는 체크하고, Next.
그 다음 Serverlocation 셋팅을 Root folder는 AppEngine의 war폴더를 지정해주면 되구요.
Root URL은 앱엔진 기본 실행 경로인 http://localhost:8080하면 되구요. Context root는 /로 지정하면 됩니다.
그러면 디버그나 Run시에 localhost:8080/프로젝트명/프로젝트명.html로 실행이 돼요.
코드는
SpringBlazeDSClient.mxml
[code]<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">

    <mx:RemoteObject id="srv" destination="helloService" />
    <mx:TextInput id="inputName" />
    <mx:Button label="전송" id="btnConfirm" click="srv.sayHello(inputName.text)" />
    <mx:Label id="labelResult" text="{srv.sayHello.lastResult}" />
   
</mx:Application>[/code]
이 코드 너무 활용하는 것 같아-_- 암튼 destination은 helloService로 설정했기 때문에 이걸로 지정하면 됩니다.

5. 이제 배포 및 실행
이제 swf파일도 appengine프로젝트에 생성하고, AppEngine을 배포하고 실행하면 또다른 에러를 보실 수 있습니다-_-
[RPC Fault faultString="Detected duplicate HTTP-based FlexSessions, generally due to the remote host disabling session cookies. Session cookies must be enabled to manage the client connection correctly." faultCode="Server.Processing.DuplicateSessionDetected" faultDetail="null"]
와....미쳐버립니다. 이건 뭔가....검색해보니 앱엔진이 여러 서블릿배포할 때 1개의 클라이언트 정보를 동일하게 배포를 해서 어쩌구 라고 번역기를 돌리니 써있네요-_- 이걸 픽스한 jar파일이 돌아다닙니다-_-
기존 flex-messaging-core.jar파일을 위 파일로 교체해주면 되더군요.

이제 실행하면 잘 될겁니다.
실행주소입니다.
http://mudchobo1.appspot.com/SpringBlazeDSClient/SpringBlazeDSClient.html


사용자 삽입 이미지

스크린샷.....

messaging이나 security적용한 것도 한번 해봐야겠네요.

참고사이트
http://www.adobe.com/jp/devnet/flex/articles/google_app_eng_w_beazeds_p2.html
http://martinzoldano.blogspot.com/2009/04/appengine-adobe-blazeds-fix.html
 
Posted by 머드초보

댓글을 달아 주세요

  1. 빠방 2009.11.09 15:57 신고  댓글주소  수정/삭제  댓글쓰기

    좋은 예제 감사드립니다 (꾸벅)
    덕분에 spring blazeds intergration 1.0.1과 ibatis 연동에 성공했습니다.
    이전에 올려주신 연동예제와 새로 릴리즈된 1.0.1 예제가 아니었으면 짧은 영어실력때문에 도저히 알아먹지 못해고 포기해버렸을꺼에요 ㅠㅠ

  2. 옹씨루 2009.11.12 15:41 신고  댓글주소  수정/삭제  댓글쓰기

    좋은 정보 감사합니다.

 
아.....드디어 6주차의 과정이 끝났네요. 토요일마다 나가서 들으려니 죽을 것 같았어요-_-
3차 강의신청도 10월중순쯤에 받고, 11월에 시작된다고 하니 관심있으신 분들은 KSUG 메일링 리스트에 가입을 하세요~^^
3차 강의에 관심있으신 분들을 위해-_- 후기를 남깁니다-_-(뭐 전 KSUG랑 관계없는 사람이구요-_- 그냥 강의가 좋아서 글을 남깁니다-_- 어차피 선착순이고 하루이틀이면 다차기 때문에 빨리하지 않으면 손해임 ㄷㄷ)
그리고, 강의를 들으시는 분들은 스프링을 한번쯤 사용해보신 분이 가장 적당한 것 같습니다^^ 그냥 멋도 모르고 사용해보신 분이면 더 좋구요(저처럼-_-). 그런 분들은 왜 이렇게 사용하지? 이런걸 알게 되는 좋은 기회일테니까요^^

Korea Spring User Group http://groups.google.com/group/ksug

스프링을 국내에 널리 알린 KSUG에서 하는 강의라 역시 스프링강의 중에는 최고입니다. 그냥 책을 보면서 스프링을 익히는 것 보단 스프링에 대해서 정말 잘 알고 관심있어하시는 분께 강의를 들으니 이해가 가지 않았던 개념도 많이 잡히더군요.

1주차 - IoC / DI
전 1주차 때가 역시...맘에 듭니다. 의존성을 개선하는 좋은 예제와 Junit을 이용한 테스트를 하는 것을 배우게 될 것입니다. 의존성을 개선하는 과정에서도 테스트는 항상 같기 때문에 항상 통과해야합니다. 테스트는 리펙토링을 할 때 매우 유용하더군요.
또한 예제를 준비해오셨는데, IoC/DI개념을 이해하기 좋을만한 예제를 준비해서 쉽게 실습합니다. 셋팅 관련된 것은 거의다 만들어오시고, 실제 중요한 로직만 코딩하는 방식으로 주로합니다. ^^

2주차 - JDBC
스프링에서 제공하는 JdbcTemplate을 처음부터 만들어봅니다. 이걸 만드는 이유는 Spring에 있는 Template류의 구현 패턴을 이해하기 위함이였습니다. JDBC Template를 만들면서 느끼는 점은 스프링이 참 대단하구나 라는 것을 느끼게 됩니다-_- Strategy pattern을 이용해 중복코드는 제거해가는 것을 직접해보는데, 이클립스의 강력한 기능인 리펙토링(사실 개발하면서 써본적 없음 ㅠㅠ)을 이용해서 합니다. 이클립스 리펙토링기능도 많이 알게 되는 좋은 시간이였던 것 같습니다. 덕분에 이클립스의 많은 기능을 배웠던 것 같아요^^

3주차 - Transaction & Advanced JDBC
전 대규모프로젝트나 트랜잭션이 필요한 프로젝트를 해본적이 거의 없어서-_- 트랜젝션에 대한 개념이 조금 전무했었습니다. 그냥 두개의 테이블에 데이터를 넣는데, 두번째가 실패하면 첫번째도 롤백이라는 간단한 개념만 알고 있었죠-_- 업무단위라고 주로하죠^^
이번 강의도 트랜젝션을 적용하기 위해선 업무로직과 섞이기 마련인데요. 이걸 트랜젝션만 따로 빼주는 Transaction Template을 만들어봅니다^^ 그러면 업무로직에만 집중할 수 있는 걸 보여주죠^^
이것보다 더 편한 AOP를 이용한 선언적 트랜젝션도 배우고요^^
그리고, 트랜젝션하면서 이건 대체 뭐하는 거지 라고 생각된 Propagation에 대해서도 잘 설명해줍니다^^ 재미있는 사례와 함께요^^

4주차 - Web MVC
전 웹에 관심이 많다보니 MVC가 역시 ^^ 여기서도 SpringDispatcherSerlvet이 어떤식으로 동작하는지를 알기 위해 FrontController라는 DispatcherServlet과 비슷한 걸 만들어보는 실습을 하게 됩니다^^ 이런 원리를 알고 사용하면 역시나 나머지에 대한 이해가 참 빨라집니다^^
이것 외에는 MVC쪽은 많이 삽질을 해봐서 복습한다는 내용으로 많이 들었던 것 같네요.

5주차 - Annotation-style Programming
1~4주차까지 한 내용을 다 애노테이션으로 바꿉니다-_- 지금까지 한 내용들은 애노테이션을 사용하기전에 개념과 원리를 잡기 위한 내용이였다고 보면 되구요. 애노테이션으로 바꾸면서 이것들을 매우 쉽게 사용할 수 있게 되었습니다. 설정에 들어가는 XML도 줄어들구요.
이거 처음부터 애노테이션을 사용해서 배워버리면 이게 왜 이렇게 되는지 알기 힘들고, 사용법만 배우는 건 완전 겉핥기가 되버리기 때문에 이렇게 강의를 구성한 것 같아요. 좋아요^^

6주차 - Security
Security는 역시 보안, 인증관련된 부분이라 어려운 것이라고 하더군요. 예전부터 로그인할 때 인증하는 것이라고만 알고있어서 더 알고 싶었던 것도 있었는데요. 근데, 역시나 하루만에 많은 걸 가르칠 수 없었기에 웹인증과 권한관련된 내용만 했습니다.
그래도 유용했던 것은 그 전 Acegi때 했던 필터 기반을 먼저 보여주고, 네임스페이스 기반을 보여줘서 원리에 대해서 잘 알았던 것 같네요. 그리고, 사실 어떻게 쓰는 줄 몰라서 못해봤었는데, 사용법도 알게 되어서 좋았네요^^


덕분에 많은 것을 배웠던 것 같네요. 강사님이 중간중간 현장 얘기도 좀 해주시고, 스프링관련 얘기도 해주시고 그래서 더욱 재미있었던 것 같네요.

3차 신청하시는 분들 참고하세요-_-
 
Posted by 머드초보

댓글을 달아 주세요