현재 진행형인 프로젝트의 총 개발리더의 입장에서,뷰단을 바라본다면 과연 이 프로젝트가 성공적으로 가는 프로세스인가,아니면 그 반대인가.
거의 900명에 육박하는 이정도 규모의 프로젝트는 앞으로도 접하기 힘든 프로젝트 일것 같다.
이런 대규모의 프로젝트의 전형적인 문제들인 커뮤케이션,의사결정,정치역학,인력관리등등의 논외로 하고, 내가 속한 뷰단에 초점을 맞춰보고 싶다.
프로젝트 초반에(현재는 중반에 접어들고 있는 시점이라 본다) 총 책임자의 골짱,몸짱,얼짱 이란 표현이 무척 와닿는다.
내 관점으로 재 해석하면 골짱의 역할은 모델링이고, 몸짱의 역할은 프로세스이고, 얼짱의 역할은 플렉스이다.
과연, 골짱과 몸짱과는 별개로 얼짱이 될수 있을까?
과연, 골짱,몸짱,얼짱이 효과적으로 분할 및 통합의 프로세스가 이뤄질수 없나?
과연, 얼짱을 위한 골짱(데이타 모델링)이 필요할까?
과연, 얼짱다운 화면이란건 뭔가?
솔직히 순서대로,
없다.
아주 순진한 원론적인 이야기 이다.이중적이게도,현실에선 이렇게 할수 있는 방법을 찾아내야 한다.
있어야 한다.
현실적으로,이건 기술의 문제라기 보다는 조직의 구조적인(커뮤니케이션,정치역학등) 인자에 더 영향을 받는것 같다.
이 정도 대규모라면? 복잡다단하다.
없다.
용어도 생소한 UI 컨설턴트중에는 뷰단을 위해 모델단의 변경을 말하는 이들이 있다, 꼬리가 머리를 흔드는 꼴이다.
시행 착오중이다.
한도 끝도 없는 주제이다.
이 부분은 선택한 기술과는 무관한 부분이 더 많다고 생각한다.플렉스 다운 화면이란 말에서, 플렉스를 다른 신기술 용어로 대체해보라.
다시 내가 총 결정권자이라면
과연 뷰단의 솔루션으로 선택한 플렉스가 올바른 선택인가?
개발편이성,유지보수성,비용면 등등에서 상대적 우위를 차지하고 있나? UI 가 이쁜건 알고 있으니 고만해라.
지금까지 내린 부분적 결론은 성급한 선택이었다.
현 시점에서 얼짱 역할을 맡은 뷰단은 기묘하게 성형수술을 시도한 추녀같다.수술비용도 만만치 않지만,아마 나중에는 코가 주저앉을지도 모른다.
아침에 Ryan Stewart 포스팅에 링크된 Flex Interface Guidelines 을 보다 문득 들었던 생각을 적어본다.
그리고, 다시 DATA MODELING FUNDAMENTALS A Practical Guide for IT Professionals - Paulraj Ponniah pdf를 보고 있다.
ps
한달전쯤에 WallMart 의 GXXX 시스템의 구축의 프로젝트를 설명하면서 자사의 솔루션을 소개하러 HP 차장님 세분이 강좌를 한적이 있었다.
'EIS 같은 기보적인(?) 수준에서, IDM,EDW,DPP ' 도표를 보여주면서, 상당히 깊이있게 번갈아 가면서 진행을 했었다.
커뮤니케이션 문제를 어떻게 풀었냐는 질문에,총 리더와, 부 리더, 그리고 그 밑의 7개파트(?) 정도의 중간 리더가 프로젝트 전반에 걸쳐서 지속적으로 협력햇었다 라는
아주~ 일반적인 답변을 들었다.
대규모 프로젝트의 접근방식은 말 그대로 특화된 방법론이 필요한것 같다.수준이,차원이 틀리다.
Flex(Cairngorm) + Spring + iBATIS 연동
1)전체 아키텍처
3-Tier 구조 : Presentation(혹은 UI) Layer,Business Layer,Persistence Layer 로 구성
1.1 Presentation Layer : Flex
1.2 Business Layer : Spring(부분)
1.3 Persistence Layer : iBATIS
Flex : adobe 사의 enterprise용 presentation tier 솔루션
Cairngorm : Flex framework
Spring : layered Java/J2EE application framework
iBATIS : data mapper framework
2)환경 설정
2.1 J2EE 설정 파일 : web.xml
2.2 Flex 설정 파일 : remoting-config.xml, services-config.xml
2.3 Spring 설정 파일 : applicationContext.xml
2.4 iBATIS 설정 파일 : SqlMapConfig.xml 과 sqlmap.xml 파일들
2.1 J2EE 설정 파일 web.xml 수정 사항
//아래처럼 context-param, listener 태그를 추가한다.
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
2.2.1 Flex 설정 파일 remoting-config.xml 수정 사항
//아래처럼 <factory>spring</factory> 태그를 추가하고, <source>contactService</source> 처럼 패키지 이름이 아니라,
서비스 이름을 적어준다(applicationContext.xml 파일에 해당 서비스에 맞는 자바빈즈 설정을 해준다)
<destination id="contactService">
<properties>
<factory>spring</factory>
<source>contactService</source>
</properties>
</destination>
//서비스 추가시 위 형식과 동일하게 계속 추가해준다.
2.2.2 Flex 설정 파일 services-config.xml 수정 사항
//아래처럼 factories 태그를 추가한다.
//더불어, 관련 class 파일 두개를 패키지 경로에 맞게(flex.samples.factories) 넣어준다.
// SpringFactory.class, SpringFactory$SpringFactoryInstance.class 이다.
<factories>
<factory id="spring" class="flex.samples.factories.SpringFactory"/>
</factories>
2.3 Spring 설정 파일 applicationContext.xml 수정 사항
applicationContext.xml 파일을 참고한다,그 안의 내용을 요약하면,
2.3.1 Application 설정
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location"><value>classpath:../application.properties</value></property>
</bean>
application.properties 는 jdbc 실제 정보를 입력한다.
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost/ibatis?user=root&password=mysql
jdbc.username=root
jdbc.password=mysql
2.3.2 DB Connection 설정
dataSource와 transactionManager 설정을 한다.
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName"><value>${jdbc.driverClassName}</value></property>
<property name="url"><value>${jdbc.url}</value></property>
<property name="username"><value>${jdbc.username}</value></property>
<property name="password"><value>${jdbc.password}</value></property>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource"><ref local="dataSource"/></property>
</bean>
2.3.3 iBATIS 설정
<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
<property name="configLocation" value="classpath:../SqlMapConfig.xml" />
</bean>
2.3.4 DAO 설정
Dao 인터페이스의 구현클래스 지정한다.
<bean id="contactDao" class="com.appfoundation.flexibatis.dao.ibatis.ContactDaoImpl">
<property name="dataSource"><ref local="dataSource"/></property>
<property name="sqlMapClient"><ref local="sqlMapClient"/></property>
</bean>
<bean id="mycontactDao" class="com.appfoundation.flexibatis.dao.ibatis.MyContactDaoImpl">
<property name="dataSource"><ref local="dataSource"/></property>
<property name="sqlMapClient"><ref local="sqlMapClient"/></property>
</bean>
클래스가 추가되면 위와 같은 형식으로 추가한다.
2.3.5 Flex 서비스 설정
Service 인터페이스의 구현클래스 지정한다.
<bean id="contactService" class="com.appfoundation.flexibatis.services.ContactServiceImpl">
<property name="contactDao"><ref bean="contactDao"/></property>
</bean>
<bean id="myContactService" class="com.appfoundation.flexibatis.services.MyContactServiceImpl">
<property name="mycontactDao"><ref bean="mycontactDao"/></property>
</bean>
2.3.6 sqlmapconfig.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMapConfig PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"
"http://ibatis.apache.org/dtd/sql-map-config-2.dtd">
<sqlMapConfig>
<settings useStatementNamespaces="true"/>
<sqlMap resource="../sqlmaps/Contact.xml" />
<sqlMap resource="../sqlmaps/MyContact.xml"/>
</sqlMapConfig>
2.3.7 Contact.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap
PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
"http://ibatis.apache.org/dtd/sql-map-2.dtd">
<sqlMap namespace="Contact">
<typeAlias alias="contact" type="com.appfoundation.flexibatis.domain.Contact"/>
<resultMap id="contactResult" class="contact">
<result property="objectIdentifier" column="objectidentifier"/>
<result property="name" column="name" />
<result property="businessPhone" column="business_phone"/>
<result property="mobilePhone" column="mobile_phone" />
<result property="homePhone" column="home_phone"/>
<result property="addressLine1" column="address_line1" />
<result property="addressLine2" column="address_line2"/>
<result property="addressLine3" column="address_line3"/>
<result property="city" column="city"/>
<result property="state" column="state"/>
<result property="zip" column="zip"/>
<result property="email" column="email"/>
<result property="company" column="company"/>
<result property="title" column="title"/>
<result property="responsibility" column="responsibility"/>
<result property="image" column="image"/>
<result property="department" column="department"/>
<result property="phoneExtension" column="phone_extension"/>
<result property="fax" column="fax"/>
<result property="status" column="status"/>
<result property="refferedBy" column="reffered_by"/>
<result property="country" column="country"/>
<result property="webSite" column="website"/>
</resultMap>
<sql id="contacts_table" >
contacts
</sql>
<select id="getContacts" resultMap="contactResult">
select *
from <include refid="contacts_table" />
order by name
</select>
<select id="getContact" resultMap="contactResult">
select *
from <include refid="contacts_table" />
where objectidentifier = #value#
</select>
<insert id="insertContact" parameterClass="contact" >
<selectKey resultClass="int" keyProperty="objectIdentifier">
SELECT max(objectidentifier) from contacts
</selectKey>
insert into <include refid="contacts_table"/>(objectidentifier, name, business_phone,mobile_phone, home_phone, address_line1, address_line2, address_line3,city, state, zip, email, company, title, responsibility, image,department, phone_extension, fax, status, reffered_by, country, website)
values(#objectIdentifier#+1,
#name#,
#businessPhone#,
#mobilePhone#,
#homePhone#,
#addressLine1#,
#addressLine2#,
#addressLine2#,
#city#,
#state#,
#zip#,
#email#,
#company#,
#title#,
#responsibility#,
#image#,
#department#,
#phoneExtension#,
#fax#,
#status#,
#refferedBy#,
#country#,
#webSite#)
</insert>
<update id="updateContact" parameterClass="contact">
update <include refid="contacts_table"/>
set name = #name#,
business_phone = #businessPhone#,
mobile_phone = #mobilePhone#,
home_phone = #homePhone#,
address_line1 = #addressLine1#,
address_line2 = #addressLine2#,
address_line3 = #addressLine3#,
city = #city#,
state = #state#,
zip = #zip#,
email = #email#,
company = #company#,
title = #title#,
responsibility = #responsibility#,
image = #image#,
department = #department#,
phone_extension = #phoneExtension#,
fax = #fax#,
status = #status#,
reffered_by = #refferedBy#,
country = #country#,
website = #webSite#
where objectidentifier = #objectIdentifier#
</update>
<delete id="deleteContact" parameterClass="int">
delete from <include refid="contacts_table"/> where objectidentifier = #value#
</delete>
</sqlMap>
2.3.8 실제 자바 구현 클래스
총 세 부분으로 나뉜다
domain - flex에서의 vo 클래스라고 생각하면 된다.
dao - dao 인터페이스와, 구현 클래스
service - service 인터페이스와,구현 클래스
Contact 클래스.
--------------------------------------------------
package com.appfoundation.flexibatis.domain;
import java.io.Serializable;
public class Contact implements Serializable {
int objectIdentifier;
String name;
String businessPhone;
String mobilePhone;
String homePhone;
String addressLine1;
String addressLine2;
String addressLine3;
String city;
String state;
String zip;
String email;
String company;
String title;
String responsibility;
String image;
String department;
String phoneExtension;
String fax;
String status;
String refferedBy;
String country;
String webSite;
....
셋터,겟터
//이 글 아래 참고 사이트에 가면 해당 클래스 다운받을수 있다.
--------------------------------------------------
ContactDao 클래스.
--------------------------------------------------
package com.appfoundation.flexibatis.dao;
import com.appfoundation.flexibatis.domain.Contact;
import java.util.List;
public interface ContactDao {
public List getContacts();
public int updateContact(Contact contact);
public int deleteContact(int contactKey);
public int insertContact(Contact contact);
}
--------------------------------------------------
ContactDaoImpl 클래스.
--------------------------------------------------
package com.appfoundation.flexibatis.dao.ibatis;
import java.util.List;
import org.springframework.orm.ibatis.SqlMapClientTemplate;
import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;
import org.springframework.dao.DataAccessException;
import com.appfoundation.flexibatis.domain.Contact;
import com.appfoundation.flexibatis.dao.ContactDao;
public class ContactDaoImpl extends SqlMapClientDaoSupport implements ContactDao {
//이 글 아래 주의 사항 꼭 참고!
public List getContacts() throws DataAccessException {
return getSqlMapClientTemplate().queryForList("Contact.getContacts");
}
public int insertContact(Contact contact) throws DataAccessException {
return ((Integer)getSqlMapClientTemplate().insert("Contact.insertContact", contact)).intValue();
}
public int updateContact(Contact contact) throws DataAccessException {
return ((Integer)getSqlMapClientTemplate().update("Contact.updateContact", contact)).intValue();
}
public int deleteContact(int contactKey) throws DataAccessException {
return ((Integer)getSqlMapClientTemplate().delete("Contact.deleteContact", contactKey)).intValue();
}
}
--------------------------------------------------
ContactService 클래스.
--------------------------------------------------
package com.appfoundation.flexibatis.services;
import com.appfoundation.flexibatis.domain.Contact;
import java.util.List;
public interface ContactService {
public abstract List getContacts() throws Exception;
public abstract int insertContact(Contact contact) throws Exception;
public abstract int updateContact(Contact contact) throws Exception;
public abstract int deleteContact(int contactKey) throws Exception;
}
--------------------------------------------------
ContactServiceImpl 클래스.
--------------------------------------------------
package com.appfoundation.flexibatis.services;
import com.appfoundation.flexibatis.domain.*;
import com.appfoundation.flexibatis.dao.ContactDao;
import java.util.List;
import java.io.Serializable;
public class ContactServiceImpl implements Serializable, ContactService {
// injected by Spring
ContactDao contactDao;
public ContactDao getContactDao() {
return contactDao;
}
public void setContactDao(ContactDao contactDao) {
this.contactDao = contactDao;
}
public List getContacts() throws Exception {
return contactDao.getContacts();
}
public int insertContact(Contact contact) throws Exception{
return ((Integer)contactDao.insertContact(contact));
}
public int deleteContact(int contactKey) throws Exception {
return ((Integer)contactDao.deleteContact(contactKey));
}
public int updateContact(Contact contact) throws Exception {
return ((Integer)contactDao.updateContact(contact));
}
}
--------------------------------------------------
2.3.9 MySQL 테이블 구조
# Host: localhost Database: ibatis
# ------------------------------------------------------
# Server version 5.0.27-community-nt
#
# Table structure for table contacts
#
DROP TABLE IF EXISTS `contacts`;
CREATE TABLE `contacts` (
`objectidentifier` int(11) NOT NULL,
`name` varchar(120) default NULL,
`business_phone` varchar(16) default NULL,
`mobile_phone` varchar(16) default NULL,
`home_phone` varchar(16) default NULL,
`address_line1` varchar(120) default NULL,
`address_line2` varchar(120) default NULL,
`city` varchar(45) default NULL,
`state` varchar(25) default NULL,
`zip` varchar(10) default NULL,
`email` varchar(120) default NULL,
`company` varchar(120) default NULL,
`title` varchar(45) default NULL,
`responsibility` varchar(45) default NULL,
`image` varchar(45) default NULL,
`department` varchar(45) default NULL,
`phone_extension` varchar(16) default NULL,
`fax` varchar(16) default NULL,
`status` varchar(45) default NULL,
`reffered_by` varchar(45) default NULL,
`address_line3` varchar(120) default NULL,
`country` varchar(60) default NULL,
`website` varchar(120) default NULL,
PRIMARY KEY (`objectidentifier`)
) ENGINE=InnoDB DEFAULT CHARSET=euckr;
#
# Dumping data for table contacts
#
INSERT INTO `contacts` VALUES (0,'기본값',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'def@default.com',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
INSERT INTO `contacts` VALUES (1,'양현석',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'iamyh@hitel.net',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
INSERT INTO `contacts` VALUES (2,'jjong jj',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'jjong@offton.co.kr',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
INSERT INTO `contacts` VALUES (3,'기본값',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'def@default.com',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
INSERT INTO `contacts` VALUES (4,'test',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'test@hitel.net',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
3)테스트 환경
Tomcat 5.0
Flex 3.0b,Cairngorm 2.2
Spring 2.0.6
iBATIS 2.3.0.677
MySQL 5.0
4)참고 사이트
Flex+Spring
http://coenraets.org/flex-spring
Flex+Cairngorm+Spring+iBATIS
http://www.appfoundation.com/blogs/giametta/2007/05/09/flex-spring-ibatis-caringorm-bringing-it-all-together/
바로 위 Chris Giametta가 구현한 자바 클래스 코딩을 좀 달리했다,난 SqlMapClientDaoSupport extend 해서 구현한거다.
주의)
ContactDaoImpl 클래스를 코딩할때 아래처럼 Contact.getContacts,해줘야한다
이분 블로그 처럼 getContacts 하고,다른 map 파일에서 똑같은 이름의 메소드가 있다면,아래 에러가 난다.
이 에러때문에 한참 헤맸었다.
//com.ibatis.sqlmap.client.SqlMapException: There is already a statement named getContacts in this SqlMap.
위와 관련해서 sqlmapconfig.xml 에 아래의 태그를 추가해준다.
<settings useStatementNamespaces="true"/>
public List getContacts() throws DataAccessException {
return getSqlMapClientTemplate().queryForList("Contact.getContacts");
}
3tier 각 부분별로 framework 을 적용해봤다.
그런데, 정말 중요한 질문은 이거다.
왜 Spring 을 쓰나?
왜 iBATIS 를 쓰나?
왜 Cairngorm 을 쓰나?
왜 Flex 를 쓰나?
명쾌한 자신만을 답을 가지고 있다면 이 조합이 꽤 쓸만할꺼다.
ps
지금까지 포스팅한글중에 가장 길고 정성들인것 같다.
flexibatisspring.zip
댓글을 달아 주세요
댓글 RSS 주소 : http://iamyhs.com/tc/rss/comment/258