Whiteship's Note


[Expert One-on-One J2EE Design and Development] J2EE 아키텍처 3

Spring/J2EE D&D : 2009.09.03 14:00


Web Ties Design

이전에 보았던 각기 다른 네 가지 아키텍처에 모두 적용가능하다.

웹 단을 미들 단의 비즈니스 인터페이스에 의존하는 별도의 계층으로 구분하는 것이 중요하다. 그렇게 해야 비즈니스 객체를 바꾸지 않고도 웹 단을 수정할 수 있으며 웹 단을 참조하지 않고도 비즈니스 객체를 테스트할 수 있다.

MVC 아키텍처 패턴

이 패턴은 스몰토크 유저 인터페이스를 위해 처음으로 문서화 되었으며 가장 성공적인 OO 아키텍처 패턴중 하나이다.

중략~

웹 단에서 MVC 아키텍처 패턴을 사용하라. 스트럿츠같이 작성해야 할 애플리케이션 코드의 양을 줄여주며, 기본적인 MVC 패턴 구현체를 사용하라.

Designing Applications for Portability

특정 플랫폼에 종속적인 것은 추상화 계층을 사용하여 구현해야 한다. 인터페이스 자체는 플랫폼에 독립적이기 때문이다.

구현 이식성과 디자인 이식성의 차이를 알아야 한다. 구현 이식성이랑 어떤 서버에 배포하든 코드를 고칠 필요가 없는 것이고, 디자인 이식성은 분명한 인터페이스 약간을 다시 구현하기만 하면 어떤 서버에든 배포할 수 있는 것이다. 완전한 구현 이식성을 달성하기는 매우 많은 노력이 필요할 수 있지만, 디자인 이식성은 달성할만하며 충분한 비즈니스 가치를 가져다 준다. 이식성이 비즈니스 요구사항이 아니더라도, 디자인 이식성은 좋은 OO 디자인 실천으로 자연스래 달성할 수 있다.

Summary

- 분산 아키텍처의 단점: 보다 복잡하고, 구현, 테스트, 유지보수 하기 힘들다. 주의깊게 설계하지 않으면 성능 문제가 발생한다.
- 분산 아키텍처의 장점" 특정 경우에 따라, 보다 견고하고 확장성이 좋을 수 있다. 특정 비즈니스 요구사항에 따라 분산 아키텍처가 필요할 수도 있다.
=> 진짜 장점을 주지 못한다면 분산 아키텍처를 피하는 것이 최선이다.

- EJB 2.0에 웹 서비스가 포함되었다. 따라서 RMI에 종속적이지 않게 되었다. 로컬 인터페이스를 통해 EJB가 실행되고 있는 동일한 JVM에 접근할 수 있다. 따라서 분산 아키텍처에 종속적이지 않게 되었다.

- 언제 EJB를 사용할 것인가. EJB는 특정 문제를 매우 잘 해결할 수 있는 복잡하고, 강력한 기술이다. 하지만 대부분의 애플리케이션에 적절하지 않다.

- 나머지 dao, tiered architecture, four J2EE architecture, web-tire design issue 생략~
top


[이슈트래커] 레드마인 redmine 괜찮네~

Good Tools : 2009.09.03 09:18


이슈트래커는 상용제품인 Jira만 써봤는데, 봄싹에서 Redmine이라는 걸 설치했길래 살펴봤는데, 괜찮습니다. UI도 깔끔한 편이고, 제공해주는 기능과 플러그인도 많이 있네요.

개인적으로 가장 맘에 드는건, 소스 코드 뷰 기능이 기본으로 탑재되어 있다는거.. Jira나 Bamboo에서는 그럴수가 없었고, Fisheye라는 걸 설치해야만 했었는데, 플러그인만 추가하면 소스 코드에 코멘트까지 다는 Crucible의 기능까지 할 수 있다고 합니다. 아틀라시안.. 제품 두 개(Fisheye, Crucible)와 Jira의 주요 기능이 한 곳에 들어있는 느낌입니다.

http://dev.springsprout.org/redmine

봄싹과 관련해서 버그나 새로운 추가 기능 요구가 있으신 분들은 위 이슈 트래커를 이용해주세요~

곳곳에 숨어있는(?) 그래프 들이 눈을 즐겁게 합니다.





top


[Expert One-on-One J2EE Design and Development] J2EE 아키텍처 2

Spring/J2EE D&D : 2009.09.02 18:19


Accessing Data

(예전) EJB를 사용하면 엔티티 빈이라는 하나의 선택지 밖에 없다. (EJB 3.0부터는 JPA가 있지요.)

J2EE Data Access Shibboleths

- 데이터베이스 간의 이식성은 항상 중요하다.
- O/R mapping은 관계형 DB를 사용할 때 항상 최선의 솔루션이다.

데이터 접근 전략을 사용할 때, 비즈니스 로직과 데이터 접근 로직을 추상화 계층으로 분리하는 것이 좋다.

Entity Beans

특정 DB에 종속되지는 않지만, EJB 컨테이너와 특정 O/R 맵핑 기술에 묶이게 된다. 성능이 안 좋다. 아키텍처적인 유연성이 떨어지고 테스트하기 어렵다.

EJB 2.0이 되고나서도, 너무 기초적인 O/R 맵핑인데다, 비효율적으로 관계형 DB를 사용하기 때문에 성능이 좋치 않다.

JDO

JDO는 엔티티 빈에 비해 성능이 좋고, 특정 RDBMS에 종속적이지도 않다. EJB에도 종속적이지 않다. 단점은  JDO 구현체가 아직 비교적 성숙하지 못하다는 것이다.

다른 O/R 맵핑 솔루션

탑링크와 코코베이스는 JDO 보다 성숙한 O/R 맵핑 솔루션으로 대부분의 J2EE 애플리케이션에서 사용할 수 있다. 다양한 기능과, 고성능의 O/R 맵핑을 제공한다. (하이버네이트 얘기는 하나도 없지 왜..)

JDBC

암묵적인 J2EE에서 JDBC와 SQL은 악마라는 소문이있다. 하지만 이 책에서 제공하는 고 수준의 라이브러리를 사용하면 (이때부터 JdbcTemplate이 나오나 봅니다. 캬..) O/R 맵핑이 없는 상황에서 최선책이 될 수 있다. 제대로 사용하면 JDBC는 좋은 성능을 발휘할 것이다. 데이터를 자연스럽게 캐시할 수 있는 O/R 맵핑 계층이 있다면 JDBC는 적절하지 않다.

상태 관리

J2EE 아키텍처에서 또 다른 중요한 의사결정은 서버-측 상태를 어떻게 관리할 것이냐이다. 이것으로 인해 서버 클러스터 환경에서 애플리케이션이 어떻게 동작하는지 그리고 어떤 J2EE 컴포넌트를 사용해야 하는지 결정된다.

서버-측 상태가 필요한지 결정하는이 중요하다. 애플리케이션을 단일 서버에서 운영할 떄는 서버-측 상태를 유지하는 것이 별 문제는 아니다. 여러 서버를 클러스터 환경에서 운영한다면 페일오버(failover)나 server affinity 문제 등을 피하기 위해 서버-측 상태를 복사해야 한다.

서버-측 상태가 필요하다면 유지해야 할 양을 최소화 하는 것이 좋다.

서버-측 상태를 유지하지 않는 애플리케이션이 그러는 애플리케이션보다 확장성이 좋고, 간결하며 클러스터 환경에 배포하기 좋다.

서버-측 상태를 어디에 저정해야 할지 결정해야 한다. 저장할 정보의 종류에 따라 달라진다. 사용자 세션 정보냐, 비즈니스 객체 상태냐. 둘 다냐. 분산 EJB는 웹 단의 상태와 상관없이 stateless session bean으로 확장성을 최대화 한다.

J2EE는 웹 애플리케이션에서 상태 관리에 사용할 두 가지 옵션을 제공한다. HTTP session 객체와 stateful session EJB다.  상태 관리가 필요하다고 해서 EJB가 필요한건 아니다.

J2EE 아키텍처

생략~


top


[EGIU] Unit 19, 20정리

모하니?/English : 2009.09.02 17:29


Unit 19. Present Tenses for the futer (I am doing / I do)

A: 현재 진행형으로 미래를 표현하기

이미 결정했거나 그렇게 하려고 조정/약속/예정되어 있는 행위를 표현한다.
결정했다는 의미를 나타낼 땐, I am going to (do)를 사용해도 된다.
하지만, 조정/약속/예정의 뜻을 나타낼 때는 I am doing이 더 자연스럽다.

외울문장
- Alex is getting married next month.
- I'm not working tomorrow.
- What time are you ariving?

B:현재형으로 미래 표현하기

시간이 정해져 있거나 공적으로 딱 짜여져 있는 경우에 사용한다.
하지만 사적인 경우에는 현재 진행형을 쓰는 것이 자연스럽다.

외울문장
- My train leaves at 11.30.
- The firm begins at 8.15.

틀린문제 0개.

Unit 20. (I'm) going to (do)

I am going to do ~: 무언가를 하려고 결정했고, 하려고 한다.
I am doing은 무언가가 예정되어 있는 것인 반면, I am going to do는 결정했다는 의미가 강하다.
둘의 차이가 미세하기 때문에 혼용하기도 한다.
미래에 어떤 일이 일어날 것이라는 표현으로 쓰기도 한다.
I was goting to ~: 그려려고 했었지만, 하지 않았다는 것을 표현할 때 사용한다.

외울문장
- What are you going to eat?
- Who are you going to invite?
- I'm not going to accept it.

틀린문제 0개.


top


[OSAF 테스트 가이드 초안] 서비스 통합 테스트

OSAF : 2009.09.01 19:40


서비스 통합 테스트

(테스트할 메서드 내에서 new를 이용하여 객체를 생성하는 등) 의존성을 목킹하기 까다로운 상황 또는,
사용하는 DAO의 기능을 충분히 테스트 했으며, DAO 실행 시간이 오래 걸리지 않을 경우
굳이 목킹하여 단위테스트를 작성할 필요 없이 통합 테스트를 작성 할 수 있습니다.

테스트 하지 않을 것

서비스 단위 테스트와 동일합니다.

테스트 할 것

  • 서비스 단위 테스트에서 하지 않은 테스트

테스트 코드 작성 방법

  • 스프링 @Test 설정 추가
  • 테스트 클래스 이름

서비스 단위 테스트와 겹치지 않도록 테스트할 클래스 이름 뒤에 Test를 붙여줍니다.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/testApplicationContext.xml")
@Transactional
public class PurYearPlanServiceImplTest {

@Autowired PurYearPlanServiceImpl service;

...
}
  • 테스트 작성하기
	@Test
public void update() {
PurYearPlan entity = new PurYearPlan();
service.add(entity);
assertThat(entity.getAmount(), is(new BigDecimal(0)));

entity.addDetail(makeNewDetailWithPriceAndQty(10, 7));
service.update(entity);
checkPriceQtyAndAmount(entity, 10, 7, 70);

entity.addDetail(makeNewDetailWithPriceAndQty(20, 3));
service.update(entity);
checkPriceQtyAndAmount(entity, 15, 10, 150);
}

private void checkPriceQtyAndAmount(PurYearPlan entity, int price, int qty, int amount) {
assertThat(entity.getPrice(), is(new BigDecimal(price)));
assertThat(entity.getQty(), is(new BigDecimal(qty)));
assertThat(entity.getAmount(), is(new BigDecimal(amount)));
}

private PurYearPlanDetail makeNewDetailWithPriceAndQty(int price, int qty) {
PurYearPlanDetail detail = new PurYearPlanDetail();
detail.setPrice(new BigDecimal(price));
detail.setQty(new BigDecimal(qty));
return detail;
}

생각해 볼 것

  • 서비스 단위 테스트와 통합 테스트 구분이 좀 애매 하다.

무조건 단위 테스트를 하기로 하고,
단위 테스트가 정 불편한 어떤 이유(그 이유를 정리해둬야겠다)를 가진 것들만 통합 테스트를 하는것이 좋치 않을까?

  • 서비스 단위 테스트와 통합 테스트의 성능 차이는 꽤 크다.

그런면에서도 통합테스트 보다는 단위 테스트가 더 좋아보인다.
쌓이고 쌓이고 쌓이다보면 빌드 하는데 엄청 오래 거릴 것이다.

  • 테스트 작성 및 이해가 비교적 쉽다

목킹을 하지 않기 때문인지, 목킹 코딩에 익숙치 않아서 인지,
목킹을 하면 역시 가독성도 떨어지고 테스트 작성이 완만하지 않다.
그럼 다시 반대로 통합 테스트를 기본으로 하고 복잡하고 오래 걸리는 DAO를 사용할 경우에만 단위 테스트로 옮길까?

top


[OSAF 테스트 가이드 초안] 서비스 단위 테스트

OSAF : 2009.09.01 19:34


서비스 단위 테스트

DAO를 사용하지 않는 부분을 테스트 하거나,
테스트 하려는 부분에서 사요하는 DAO 기능이 충분한 테스트를 거치지 않았거나,
테스트에서 사용하는 DAO가 아직 제대로 구현되지 않았거나,
실행이 오래 걸리는 DAO를 사용할 경우에
DAO를 목킹(mocking)하여 서비스 계층의 코드만 단위 테스트 할 수 있습니다.

테스트 하지 않을 것

DAO쪽으로 단순 위임하는 코드는 테스트하지 않습니다.

	public PurYearPlanDetail getPurYearPlanDetailById(int detailid) {
return dao.getPurYearPlanDetailById(detailid);
}

테스트 할 것

  • 서비스 인터페이스에 새로 추가하고 구현한 메서드
  • OSAF의 GenericService에서 상속받은 것을 재정의한 메서드

OSAF의 GenericService에서 상속 받은 것 중에서 재정의한 메서드를 테스트 합니다.

	public void update(PurYearPlan entity) {
super.update(entity);
entity.calcAll();
}
  • 비즈니스 로직

서비스에 포함되어 있는 복잡한 비즈니스 로직을 테스트 합니다.

	public void addPurYearPlanDetail(int yearplanid, PurYearPlanDetail detail) {
detail.calcAmount();
get(yearplanid).addDetail(detail);
}

public String getAutoGeneratedCode(Date year) {
String currentCode = null;
SimpleDateFormat formatter = new SimpleDateFormat("yyyy");
String syear = formatter.format(year);

String previousCode = dao.getCurrentCode(year);
if (previousCode != null) {
Integer currentNo = Integer.valueOf(previousCode.substring(4)) + 1;
currentCode = (currentNo / 10) > 0 ? syear + currentNo.toString()
: syear + "0" + currentNo.toString();
} else {
currentCode = syear + "01";
}
return currentCode;
}

테스트 코드 작성 방법

  • Mockito 러너 추가

MockitoJUnitRunner를 추가합니다.

  • 테스트 클래스 이름

테스트 대상인 클래스 이름에 UnitTest를 붙여줍니다.

@RunWith(MockitoJUnitRunner.class)
public class PurYearPlanServiceImplUnitTest {

}
  • 의존성 Mocking

@Mock을 사용해서 의존하는 인터페이스에 대한 목 객체를 만들고, @Before를 붙인 setUp 메서드에서 테스트 객체에 주입해 줍니다.
@Mock을 이용하면 mock(PurYearPlanDao.class); 같은 코드를 호출하지 않아도 MockitoJUnitRunner에 의해 자동으로 목 객체가 생성됩니다.

@RunWith(MockitoJUnitRunner.class)
public class PurYearPlanServiceImplUnitTest {

PurYearPlanServiceImpl service;
@Mock PurYearPlanDao mockDao;

@Before
public void setUp() {
service = new PurYearPlanServiceImpl();
service.setDao(mockDao);
}
...
}
  • 테스트 작성

테스테에 필요한 객체들을 정의하고 테스트 이후에 변경되는 값의 현재 상태를 확인합니다.
Mockito의 stub()을 사용하여 목 객체가 어떻게 동작해야 하는지 정의해 줍니다.
그런 다음 테스트 하려는 메서드를 실행합니다.
마지막으로 비즈니스 로직이 제대로 수행되었는지 여러 객체의 상태들을 확입합니다.

	@Test
public void addPurYearPlanDetail() throws Exception {
PurYearPlan yearPlan = new PurYearPlan();
int yearplanid = 1;
yearPlan.setId(yearplanid);
PurYearPlanDetail detail = new PurYearPlanDetail();

when(mockDao.get(yearplanid)).thenReturn(yearPlan);

service.addPurYearPlanDetail(yearplanid, detail);
assertThat(yearPlan.getDetails().size(), is(1));
}

생각해 볼 것

  • 목킹을 사용하는 여러 가지 경우를 좀 더 구체적으로 작성해야겠다.
top


[OSAF 테스트 가이드 초안] DAO 통합 테스트

OSAF : 2009.09.01 18:32


어렴풋이 생각은 해봤었는데, 이번 기회에 정리해 보네요. 매우 주관적이며 OSAF 프레임워크를 사용하는 경우에 해당하는 가이드이기 때문에 통용될리는 없다고 생각합니다. 하지만 적어도 저한테는 이렇게 하는 것이 타당해 보이며, 조금만 응용하면, 다른 프레임워크 또는 별도의 프레임워크가 없는 경우에도 어느정도 적절할 것으로 보입니다. 그럼.. 차근차근 정리해 보겠습니다.

DAO 통합 테스트

DAO 테스트는 항상 데이터베이스를 필요로 하기 때문에 통합 테스트로 볼 수 있습니다.

테스트하지 않을 것

  • 프레임워크 코드

OSAF의 GenericDao로부터 상속받은 코드와 하이버네이트 코드는 테스트하지 않습니다.

테스트 할 것

  • 하이버네이트 도메인 모델 검증

도메인 모델에 맵핑 정보가 제대로 설정되었는지 확인합니다.
OSAF GenericDao가 제공하는 save로 도메인 모델을 저장해보는 것으로 확인합니다.

	@Test
public void crud() throws Exception {
Code code = new Code();
code.setCodeCate(CodeCate.CAR_TYPE);
code.setName("BLK");

cd.save(code);
}
  • DAO에서 재정의하는(overriding)하는 addRestrictions 메서드

해당 메서드에서 정의하는 검색 조건과 정렬 옵션에 따른 쿼리가 제대로 동작하는지 테스트 합니다.

	protected Criteria addRestrictions(Criteria c, CodeParams params) {
CriteriaUtils.ilike(c, "name", params.getName(), MatchMode.ANYWHERE);
CriteriaUtils.conditionalEq(c, "codeCate", params.getCodeCate());
c.addOrder(Order.asc("codeCate"));
return c;
}
  • DAO 인터페이스에 별도로 추가한 메서드의 구현체

OSAF에서 상속받지 않은 코드로, DAO 인터페이스에 추가하고, DaoImpl 클래스에서 구현한 메서드도 테스트가 필요합니다.

//DAO 인터페이스
public interface CodeDao extends GenericDao<Code, CodeParams> {
public List<Code> findByCodeCate(CodeCate codeCate);
}

//DAO 인터페이스 구현체
@Repository
public class CodeDaoImpl extends GenericDaoImpl<Code, CodeParams> implements CodeDao{

...

@SuppressWarnings("unchecked")
public List<Code> findByCodeCate(CodeCate codeCate){
return super.getSession().createCriteria(Code.class).add(
Restrictions.eq("codeCate", codeCate)).list();
}

}

테스트 코드 작성 방법

  • 스프링 @Test 선언

애노테이션 추가: DAO 테스트에 스프링 테스트 러너, 설정 파일 위치, 트랜잭션을 설정합니다.

테스트 설정 파일 위치: 테스트용 스프링 설정파일은 test 폴터 밑에 위치한 testApplicationContext.xml을 사용합니다.

트랜잭션 설정: OSAF의 DAO는 트랜잭션 당 세션을 유지하기 위해 getCurrentSession()을 사용하고 있기 때문에 @Transactional을 반드시 추가해야 합니다.

테스트 클래스 이름: 테스트할 DAO 구현체 이름뒤에 Test를 붙여서 작명합니다.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="/testApplicationContext.xml")
@Transactional()
public class CodeDaoImplTest {

}
  • DBUnitSupport 클래스 상속.

DBUnit을 편하게 사용할 수 있도록 DBUbnitSupport 클래스를 상속 받습니다.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="/testApplicationContext.xml")
@Transactional()
public class CodeDaoImplTest extends DBUnitSupport{

}
  • 테스트 데이터를 적성합니다.

XML 테스트 데이터가 필요한 경우 테스트 클래스와 동일한 패키지에 두어 접근성을 높여줍니다.
XML 테스트 데이터는 다음과 같이 작성합니다.

<dataset>
<code id="1" name="test" codeCate="10" />
<code id="2" name="test" codeCate="10" />
<code id="3" name="test" codeCate="20" />
<code id="4" name="test" codeCate="20" />
</dataset>
  • 테스트를 작성합니다.

테스트 데이터가 필요한 테스트는 DBUnitSupport에서 상속받은 insertXmlData();
또는 inserXlsData(); 메서드를 이용하여 테스트 데이터를 넣어줍니다.

	@Test
public void search() throws Exception {
insertXmlData("testData.xml");
CodeParams params = new CodeParams();
OrderPage orderPage = new OrderPage();

params.setName("te");
assertThat(cd.search(params, orderPage).size(), is(4));

params.setName("");
params.setCodeCate(PersistentEnumUtil.valueOf(CodeCate.class, 20));
assertEquals(2, cd.search(params, orderPage).size());
}

생각해볼 것

  • 테스트 데이터베이스와 실제 데이터페이스가 다르다

배포용으로는 MySQL을 사용하면서 테스트할 때는 HSQL을 사용한다. 과연 제대로 테스트 했다고 볼 수 있을까?



top


[Expert One-on-One J2EE Design and Development] J2EE 아키텍처 1

Spring/J2EE D&D : 2009.09.01 08:47


엔터프라이즈 아키텍처의 목표
- 견고함(be robust): 사용자가 신뢰할 수 있고 버그가 없어야 한다.
- 성능과 확장성이 좋아야 함: 확장을 고려하여 보통 여러 개의 서버 인스턴스를 하나의 클러스터에 배포한다. 클러스터링은 복잡한 애플리케이션 서버 기능을 필요로 한다. 애플리케이션이 클러스터 환경에서 효율적으로 동작하도록 설계되어 있는지 확인해야 한다.
- OO 설계 원리 장점 취하기: we should apply J2EE to realize OO design, not let J2EE technologies dictate object design
- 불필요한 복잡성 기피하기: XP에서는 "동작하게 만드는 가장 간단한 것"을 하도록 권장한다.
- 유지보수와 확장성: 각각의 컴포넌트가 분명한 책임을 가지고 있는지, 컴포넌트 끼리 강하게 결합되어 유지보수를 방해하진 않는지 확인하라.
- 제 때 배포하기
- 쉽게 테스트하기
- 재사용 권장하기

다음은 비즈니스 요구사항에 따른 부가적인 목표
- 다양한 클라이언트 타입 지원하기: 웹 애플리케이션, Swing 애플리케이션, 자바 애플릿 등. 보통은 "얇은" 웹 인터페이스만 널리 사용하고 있다.
- 이식성: 비즈니스 요구사항에 따라 데이터베이스 같은 자원의 이식성이 얼마나 용이한지 고려해야 할 수도 있다.

분산 아키텍처를 사용할 것인지 결정하기
- 분산 애프리케이션은 복잡하고, 런타임 오버헤드를 증가시키며, 만족할만한 성능을 내기 위해 충분한 설계를 필요로하기 때문에 중요한 의사결정이다.

분산 아키텍처가 제공하는 장점
- 다양한 클라이언트를 지원할 수 있는 능력
- 애플리케이션 컴포넌트를 각기 다른 물리적 서버에 배포할 수 있는 능력.

분산 아키텍처의 문제
- 성능 문제: 원격 호출은 로컬 호출보다 느리다.
- 복잡도: 개발, 디버그, 배포, 유지보수가 힘들다.
- OO 설계 적용에 제약이 생긴다

J2EE 설계에서 새로 고려할 것
- 생략

EJB 사용 시점
- EJB는 J2EE가 제공하는 하나의 선택일 뿐이다.
- 요구사항이 분산 아키텍처를 필요로 하고 RMI/IIOP가 원격 프로토콜로 적당할 경우 EJB는 표준 구현체를 제공해줄 수 있다.

EJB를 사용한다는 것의 의미
- 애플리케이션을 테스트하기 힘들다.
- 애플리케이션을 배포하기 힘들다.
- 원격 인터페이스는 OO 디자인 실천을 방행한다.
- 간단한 것도 어렵게 만든다.
- 애플리케이션 서버 선택 범위가 좁아진다.

EJB 사용에 대한 의문
- 생략

EJB 사용을 강하게 주장하는 경우
- 생략

경우에 따라 EJB 사용을 고려해볼 만한 경우
-  개발자가 멀티-쓰레드 코드를 작성하지 않아도 된다.
- CMT(컨테이너가 관리하느 트랜잭션)를 통해 Transparent 트랜잭션 관리를 제공한다.
- 선언적인/프로그래밍적인 롤-기반 시큐리티를 제공한다.
top