๋ณต์ก์ฑ์ ์ฃฝ์์ด๋ค. ๊ฐ๋ฐ์์๊ฒ ์๊ธฐ๋ฅผ ์์๊ฐ๋ฉฐ, ์ ํ์ ๊ณํํ๊ณ ์ ์ํ๊ณ ํ ์คํธํ๊ธฐ ์ด๋ ต๊ฒ ๋ง๋ ๋ค. - ๋ ์ด ์ค์ง, ๋ง์ดํฌ๋ก์ํํธ ์ต๊ณ ๊ธฐ์ ์ฑ ์์(CTO)
๋์๋ฅผ ์ธ์ด๋ค๋ฉด?
๋์๊ฐ ๋์๊ฐ๋ ์ด์ ๋, ๊ฐ ๋ถ์ผ๋ฅผ ๋ด๋นํ๋ ํ์ด ์๊ธฐ ๋๋ฌธ์ด๋ค. ๊ทธ๋ฆฌ๊ณ ์ ์ ํ ์ถ์ํ์ ๋ชจ๋ํ ๋๋ฌธ์ด๋ค.
์ํํธ์จ์ด ํ๋ ๋์์ฒ๋ผ ๊ตฌ์ฑํ๋ค. ํ์ง๋ง, ๋ง์ ํ์ด ์ ์ํ๋ ์์คํ ์ ๋น์ทํ ์์ค์ผ๋ก ๊ด์ฌ์ฌ๋ฅผ ๋ถ๋ฆฌํ๊ฑฐ๋ ์ถ์ํ๋ฅผ ์ด๋ค๋ด์ง ๋ชปํ๋ค. ์ด๋ฒ ์ฅ์์๋ ์์คํ ์์ค์์๋ ๋์ ์ถ์ํ ์์ค์ ํตํด ๊นจ๋ํจ์ ์ ์งํ๋ ๋ฐฉ๋ฒ์ ์ดํด๋ณธ๋ค.
์์คํ ์ ์๊ณผ ์์คํ ์ฌ์ฉ์ ๋ถ๋ฆฌํ๋ผ
์ค์ ๋ ผ๋ฆฌ๋ ์ผ๋ฐ ์คํ ๋ ผ๋ฆฌ์ ๋ถ๋ฆฌํด์ผ ๋ชจ๋์ฑ์ด ๋์์ง๋ค.
์ ์๊ณผ ์ฌ์ฉ์ ์์ฃผ ๋ค๋ฅด๋ค. ๊ฑด๋ฌผ์ ์ง์ ๋์, ์ค์ ์ฌ์ฉํ ๋ ํ๊ฒฝ๊ณผ ์ฌ๋๋ค์ด ๋ฌ๋ผ์ง๋ ๊ฒ์ ์๊ฐํด๋ณด์. ๋ง์ฐฌ๊ฐ์ง๋ก ์ํํธ์จ์ด ์์คํ ๋ ์ ํ๋ฆฌ์ผ์ด์ ๊ฐ์ฒด๋ฅผ ์ ์ํ๊ณ , ์์กด์ฑ์ ์ฐ๊ฒฐํ๋ ์ค๋น๊ณผ์ ๊ณผ ๋ฐํ์ ๋ก์ง์ ๋ถ๋ฆฌํด์ผ ํ๋ค.
/* Code 1-1 */
public Service getService() {
if (service == null)
service = new MyServiceImpl(...); // ๋ชจ๋ ์ํฉ์ ์ ํฉํ ๊ฐ์ฒด์ธ๊ฐ..?
return service;
}
์์ ๋จ๊ณ๋ ๋ชจ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ด ํ์ด์ผ ํ ๊ด์ฌ์ฌ์ด๋ค. ์ด๋ฐ ๊ด์ฌ์ฌ๋ก ํํ๋๋ ๊ฒฝ์ฐ ์ด๋ฅผ ๋ถ๋ฆฌํ๋ ๊ฒ์ด ์ข๋ค. ์์ ์ฝ๋๋ฅผ ์ด๋ฅผ ๋ถ๋ฆฌํ์ง ์์ ์์์ด๋ค. ์ด๋ Lazy Initialization, Lazy Evaluation ์ด๋ผ๋ ๊ธฐ๋ฒ์ด๋ค. ๋ค์์ ์ฅ์ ์ ๊ฐ์ง๋ค.
- ์ค์ ๋ก ํ์ํ ๋๊น์ง ๊ฐ์ฒด๋ฅผ ์์ฑํ์ง ์์ผ๋ฏ๋ก ๋ถํ์ํ ๋ถํ๊ฐ ๊ฑธ๋ฆฌ์ง ์๋๋ค.
- ๋ฐ๋ผ์ ๊ทธ๋งํผ ์ ํ๋ฆฌ์ผ์ด์ ์์ ์๊ฐ์ด ์งง์์ง๋ค.
- ์ด๋ค ๊ฒฝ์ฐ์๋ null์ ๋ฐํํ์ง ์๋๋ค.
ํ์ง๋ง, ๋ค์์ ๋จ์ ์ ๊ฐ๋๋ค.
getService
๋ฉ์๋๊ฐ ํด๋น ๊ฐ์ฒด์ ์ฌ์ฉ์ฌ๋ถ์ ๊ด๊ณ์์ดMyServiceImpl
์์ฑ์ ์ธ์์ ๋ช ์์ ์ผ๋ก ์์กดํ๋ค.MyServiceImpl
์ด ๋ฌด๊ฑฐ์ด ๊ฐ์ฒด๋ผ๋ฉด ๋จ์ํ ์คํธ๊ฐ ์ด๋ ค์์ง๋ค.- ํ
์คํธ ์ ์ฉ ๊ฐ์ฒด(Test Double, mock object)๋ก
service
๋ณ์์ ํ ๋นํด์ผ ํ๋ค. - service๊ฐ null์ผ ๋์ ๊ทธ๋ ์ง ์์ ๋๋ ํ ์คํธํด์ผ ํ๋ค. ์ด๋ ์์ฑ ๋ก์ง๊ณผ ์ฌ์ฉ ๋ก์ง์ด ํผ์ฌ๋์ด ์๊ธฐ ๋๋ฌธ์ ๋ฐ์ํ๋ค.
- ๊ฒฐ๊ณผ์ ์ผ๋ก ์๊ฒ๋๋ง SRP์ ์ค์ํ์ง ๋ชปํ๊ณ ์๋ค.
- ํ
์คํธ ์ ์ฉ ๊ฐ์ฒด(Test Double, mock object)๋ก
- ๊ฐ์ฅ ์ค์ํ ๋ฌธ์ ๋ ์์ฑ์
MyServiceImpl
์ด ๋ชจ๋ ์ํฉ์ ์ ํฉํ ๊ฐ์ฒด์ธ๊ฐํ๋ ์ ์ด๋ค.
์ด๊ธฐํ ์ง์ฐ์ ํ ๋ฒ ์ ๋ ์ฌ์ฉํ๋ค๋ฉด ๋ณ๋ก ์ฌ๊ฐํ ๋ฌธ์ ๊ฐ ์๋๋ค. ํ์ง๋ง ์ด๋ฐ ์ค์ ๊ธฐ๋ฒ์ ์ฌ์ฉํ๋ค๋ฉด, ์ ํ๋ฆฌ์ผ์ด์ ์ ์ค์ ๋ก์ง์ด ๊ณง๊ณง์ ํฉ์ด์ ธ ์์ ๊ฒ์ด๋ค. ๋ชจ๋์ฑ์ ์ ์กฐํ๋ฉฐ ์ค๋ณต์ด ์ฌํ๋ค. ์ค์ ๋ ผ๋ฆฌ๋ ์ผ๋ฐ ์คํ ๋ ผ๋ฆฌ์ ๋ถ๋ฆฌํด์ผ ๋ชจ๋์ฑ์ด ๋์์ง๋ค.
Main ๋ถ๋ฆฌ
- main์ด builder์๊ฒ ๊ฐ์ฒด ์์ฑ์ ์์ฒญํ๋ค.
- application์ builder๊ฐ ๋ง๋ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๋ค.
์ด๋ฐ ๋ฐฉ์์ ํตํด application์ด ๊ฐ์ฒด๊ฐ ์์ฑ๋๋ ๊ณผ์ ์ ์ ํ ๋ชจ๋ฅด๋ฉด์๋ ์ฌ์ฉํ ์ ์๊ฒ ํ ์ ์๋ค. application์ ๋ชจ๋ ๊ฐ์ฒด๊ฐ ์์ฑ๋์๋ค๊ณ ๊ฐ์ ํ๋ค.
ํฉํ ๋ฆฌ
main ๋ถ๋ฆฌ ์ฒ๋ผ ์์ ์์คํ
์ด๊ธฐ ์์ฑ ์์ ์ ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ๋ ์๊ฒ ์ง๋ง, ๋๋๋ก application์ด ๊ฐ์ฒด๊ฐ ์์ฑ๋๋ ์์ ์ ๊ฒฐ์ ํ ํ์๋ ์๋ค. ์๋ฅผ ๋ค์ด, ์ฃผ๋ฌธ ์ฒ๋ฆฌ ์์คํ
์์ application์ด LineItem
์ธ์คํด์ค๋ฅผ ์์ฑํ์ฌ Order์ ์ถ๊ฐํ๋ ๊ฒ์ด ์๊ฒ ๋ค.
์ด๋ฐ ๊ฒฝ์ฐ Abstract Factory ํจํด์ ์ฌ์ฉํ๋ค. ๊ทธ๋ฆผ์ ๋ณด๋ฉด, OrderProcessing์ LineItemFactory Interface์ ์์ฒญํ๋ค. ์ค์ ๊ตฌํ์ฒด๋ main์ด ๋ง๋ LineItemFactoryImplementation
์ด๊ณ , ์ด๋
์์ด LineItem
์ ์์ฑํ๋ค.
์์กด์ฑ ์ฃผ์
์ฌ์ฉ๊ณผ ์ ์์ ๋ถ๋ฆฌํ๋ ๊ฐ๋ ฅํ ๋งค์ปค๋์ฆ ํ๋๊ฐ ์์กด์ฑ ์ฃผ์ (Dependency Injection)์ด๋ค. DI๋ ์ ์ด ์ญ์ (Inversion of Control: IoC) ๊ธฐ๋ฒ์ ์์กด์ฑ ๊ด๋ฆฌ์ ์ ์ฉํ ๋ฉ์ปค๋์ฆ์ด๋ค.
์ ์ด ์ญ์ ์์๋ ํ ๊ฐ์ฒด๊ฐ ๋งก์ ๋ณด์กฐ ์ฑ ์์ ์๋ก์ด ๊ฐ์ฒด์๊ฒ ์ ์ ์ผ๋ก ๋ ๋๊ธด๋ค. ์๋ก์ด ๊ฐ์ฒด๋ ๋๊ฒจ๋ฐ์ ์ฑ ์๋ง ๋งก๊ธฐ ๋๋ฌธ์, SRP๋ฅผ ์ค์ํ ์ ์๊ฒ ๋๋ค. ์ด๋ฐ ์์กด์ฑ ๊ด๋ฆฌ๋ฅผ ํ๋๋ฐ ์์ด, ๊ฐ์ฒด๋ ์์กด์ฑ ์์ฒด๋ฅผ ์ธ์คํด์ค๋ก ๋ง๋๋ ์ฑ ์์ ์ง์ง ์๋๋ค. ์ฆ, ์์กด์ฑ์ ํด๊ฒฐํ๋ ์ ๋ด ๊ฐ์ฒด๊ฐ ๋ฐ๋ก ์์ด์ผ ํ๋ค๋ ๋ง์ด๋ค. ์ด๋ฐ ์ด๊ธฐ ์ค์ ์ ์ฃผ์ ํ๋ ๊ณผ์ ์ ์์คํ ์ ์ฒด์์ ํ์ํ๊ธฐ ๋๋ฌธ์, main ๋ฃจํด์ด๋ ํน์ container(DI Container)๋ฅผ ์ฌ์ฉํ๋ค.
/* Code 1-3 */
MyService myService = (MyService)(jndiContext.lookup(โNameOfMyServiceโ));
์ ์ฝ๋๋ฅผ ํธ์ถํ๋ ์ชฝ์์๋ ์ค์ ๋ก lookup ๋ฉ์๋๊ฐ ๋ฌด์์(์ด๋ค ๊ตฌํ์ฒด๋ฅผ) ๋ฆฌํดํ๋์ง์ ๋ํด ๊ด์ฌํ์ง ์์ผ๋ฉด์ ์์กด์ฑ์ ํด๊ฒฐํ ์ ์๋ค.
์ง์ ํ ์์กด์ฑ ์ฃผ์ ์ ์ฌ๊ธฐ์์ ํ ๋จ๊ณ ๋ ๋์๊ฐ ์์ ํ ์๋์ ์ธ ํํ๋ฅผ ์ง๋๋ค. ์์กด์ฑ์ ํ์๋ก ํ๋ ๊ฐ์ฒด๊ฐ ์ง์ ์์กด์ฑ์ ํด๊ฒฐ(์์ฑ, ์ฐ๊ฒฐ)ํ๋ ๋์ ์์ฑ์/setter ๋ฑ์ ํตํด DI ์ปจํ ์ด๋๊ฐ ํด๋น ์์กด์ฑ์ ํด๊ฒฐํ๋๋ก ๋์์ค๋ค. (๋ณดํต ์ฐ๋ฆฌ๊ฐ ๋ง์ด ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ)
ํ์ฅ
์ฒ์๋ถํฐ ๋ฏธ๋์ ์๊ตฌ์ฌํญ๊น์ง ๊ณ ๋ คํ์ฌ ์์คํ ์ ๋ง๋ ๋ค๋ ๋ฏฟ์์ ๋ฏธ์ ์ด๋ค.
์ฐ๋ฆฌ๋ ์ค๋ ์ฃผ์ด์ง ์ฌ์ฉ์ ์คํ ๋ฆฌ์ ๋ง์ถฐ ์์คํ ์ ๊ตฌํํด์ผ ํ๋ค. ๋ด์ผ์ ๋ด์ผ์ ์คํ ๋ฆฌ์ ๋ง์ถฐ ์์คํ ์ ์กฐ์ ํ๊ณ ํ์ฅํ๋ค. ์ด๊ฒ์ด ๋ฐ๋ณต์ ์ด๊ณ ์ ์ง์ ์ธ Agile ๋ฐฉ์์ด๋ค. TDD, Refactoring์ผ๋ก ์ด์ด์ง๋ ๊นจ๊ธํ ์ฝ๋๋ ์ฝ๋ ์์ค์์ ์์คํ ์ ์กฐ์ ํ๊ณ ํ์ฅํ๊ธฐ ์ฝ๊ฒ ๋ง๋ ๋ค.
์์คํ ์์ค์์๋ ์ด๋จ๊น? ์ด๋ค์์ผ๋ก ๊ด๋ฆฌํ๋ ๊ฒ์ด ์์คํ ์ ๋ฐ์์ ์ด๋ฅผ ๊ด๋ฆฌํ๊ธฐ ์ข๊ฒ ํ ์ ์์๊น? ๋ต๋ถํฐ ๋งํ์๋ฉด, ๊ด์ฌ์ฌ๋ฅผ ๊ธฐ์ค์ผ๋ก ๋๋์ด์ผ ํ๋ค.
๋จผ์ , ์ค์ผ์ผ๋ง์ ๊ณ ๋ คํ์ง ์์ ๊ตฌ์กฐ์ ๋ํด EJB1/EJB2๋ฅผ ์์๋ก ์์๋ณด์.
- EJB์ ๋ํ ์์ธํ ๋ด์ฉ์ ๋ณธ ์ฑํฐ์ ๊ด๊ณ๊ฐ ์์ผ๋ฏ๋ก ์๋ตํ๋ค. (EJB์ ๋ํ ์์ธํ ๊ฐ์๋ ๊ฐ์ฃผ๋ก ์ถ๊ฐ ๋ฐ๋)
- ์ฐ์ entity bean์ด๋ ๊ด๊ณ ๋ฐ์ดํฐ(DB ํ ์ด๋ธ์ ํ)์ ๋ฉ๋ชจ๋ฆฌ์์ ํํ์ด๋ผ๋ ๊ฒ๋ง ์๊ณ ๊ฐ์. (An entity bean is an in-memory representation of relational data, in other words, a table row.)
/* Code 2-1(Listing 11-1): An EJB2 local interface for a Bank EJB */
package com.example.banking;
import java.util.Collections;
import javax.ejb.*;
public interface BankLocal extends java.ejb.EJBLocalObject {
String getStreetAddr1() throws EJBException;
String getStreetAddr2() throws EJBException;
String getCity() throws EJBException;
String getState() throws EJBException;
String getZipCode() throws EJBException;
void setStreetAddr1(String street1) throws EJBException;
void setStreetAddr2(String street2) throws EJBException;
void setCity(String city) throws EJBException;
void setState(String state) throws EJBException;
void setZipCode(String zip) throws EJBException;
Collection getAccounts() throws EJBException;
void setAccounts(Collection accounts) throws EJBException;
void addAccount(AccountDTO accountDTO) throws EJBException;
}
/* Code 2-2(Listing 11-2): The corresponding EJB2 Entity Bean Implementation */
package com.example.banking;
import java.util.Collections;
import javax.ejb.*;
public abstract class Bank implements javax.ejb.EntityBean {
// Business logic...
public abstract String getStreetAddr1();
public abstract String getStreetAddr2();
public abstract String getCity();
public abstract String getState();
public abstract String getZipCode();
public abstract void setStreetAddr1(String street1);
public abstract void setStreetAddr2(String street2);
public abstract void setCity(String city);
public abstract void setState(String state);
public abstract void setZipCode(String zip);
public abstract Collection getAccounts();
public abstract void setAccounts(Collection accounts);
public void addAccount(AccountDTO accountDTO) {
InitialContext context = new InitialContext();
AccountHomeLocal accountHome = context.lookup("AccountHomeLocal");
AccountLocal account = accountHome.create(accountDTO);
Collection accounts = getAccounts();
accounts.add(account);
}
// EJB ์ปจํ
์ด๋ ๋
ผ๋ฆฌ
public abstract void setId(Integer id);
public abstract Integer getId();
public Integer ejbCreate(Integer id) { ... }
public void ejbPostCreate(Integer id) { ... }
// ๋๋จธ์ง๋ ๊ตฌํํด์ผ ํ์ง๋ง ์ผ๋ฐ์ ์ผ๋ก ๋น์ด์๋ค.
public void setEntityContext(EntityContext ctx) {}
public void unsetEntityContext() {}
public void ejbActivate() {}
public void ejbPassivate() {}
public void ejbLoad() {}
public void ejbStore() {}
public void ejbRemove() {}
}
์ ์ฝ๋์ ๊ฐ์ ์ ํ์ ์ธ EJB2 ๊ฐ์ฒด ๊ตฌ์กฐ๋ ์๋์ ๊ฐ์ ๋ฌธ์ ์ ์ ๊ฐ์ง๊ณ ์๋ค.
- ๋น์ง๋์ค ๋ก์ง์ด EJB2 ์ปจํ ์ด๋์ ํ์ดํธํ๊ฒ ์ฐ๊ฒฐ๋์ด ์๋ค. Entity๋ฅผ ๋ง๋ค๊ธฐ ์ํด ์ปจํ ์ด๋ ํ์ ์ subclassํ๊ณ ํ์ํ lifecycle ๋ฉ์๋๋ฅผ ๊ตฌํํด์ผ ํ๋ค.
- ์ค์ ๋ก ์ฌ์ฉ๋์ง ์์ ํ ์คํธ ๊ฐ์ฒด์ ์์ฑ์ ์ํด mock ๊ฐ์ฒด๋ฅผ ๋ง๋๋ ๋ฐ์๋ ๋ฌด์๋ฏธํ ๋ ธ๋ ฅ์ด ๋ง์ด ๋ ๋ค. EJB2 ๊ตฌ์กฐ๊ฐ ์๋ ๋ค๋ฅธ ๊ตฌ์กฐ์์ ์ฌ์ฌ์ฉํ ์ ์๋ ์ปดํฌ๋ํธ๋ฅผ ์์ฑํด์ผ ํ๋ค.
- OOP ๋ํ ๋ฑํ์๋๊ณ ์๋ค. ์์๋ ๋ถ๊ฐ๋ฅํ๋ฉฐ ์ธ๋ฐ์๋ DTO(Data Transfer Object)๋ฅผ ์์ฑํ๊ฒ ๋ง๋ ๋ค.
(์ด ๋ถ๋ถ ์ ์ดํดํ์ง ๋ชปํ๋ค..)
ํก๋จ(cross-cutting) ๊ด์ฌ์ฌ
์ด๋ก ์ ์ผ๋ก๋ ๋ ๋ฆฝ๋ ํํ๋ก ๊ตฌ๋ถ๋ ์ ์์ง๋ง ์ค์ ๋ก๋ ์ฝ๋์ ์ฐ์ฌํ๊ธฐ ์ฌ์ด ๋ถ๋ถ๋ค
ํธ๋์ญ์ , ๋ณด์, ์์์ฑ, ๋ก๊น , ๋ก๊ทธ์ธ ๋ชจ๋ ๋ฑ์ application์ ๊ฐ์ฒด ๊ฒฝ๊ณ๋ฅผ ๋๋๋๋ ๊ฒฝํฅ์ด ์๋ค. ๋ชจ๋ ๊ฐ์ฒด๊ฐ ์ ๋ฐ์ ์ผ๋ก ๋์ผํ ๋ฐฉ์์ ์ด์ฉํ๊ฒ ๋ง๋ค์ด์ผ ํ๋ค. ๊ทธ๋ ์ง ์๋๋ค๋ฉด ๊ฐ์ ๋์์ ์ํํ๋ ์ฝ๋๊ฐ ์ฌ๊ธฐ์ ๊ธฐ ํฉ์ด์ง๊ฒ ๋๋ค. ์ด๋ Aspect-Oriented Programming(AOP)๊ณผ ๊ฐ์ ๋งฅ๋ฝ์ด๋ผ ๋ณผ ์ ์๋ค.
์๋ฐ ํ๋ก์
๊ฐ๋จํ ๊ฒฝ์ฐ๋ผ๋ฉด ์๋ฐ ํ๋ก์๊ฐ ์ ์ ํ ์๋ฃจ์ ์ผ ๊ฒ์ด๋ค. ์๋๋ ์๋ฐ ํ๋ก์๋ฅผ ์ฌ์ฉํด ๊ฐ์ฒด์ ๋ณ๊ฒฝ์ด ์๋์ผ๋ก persistant framework์ ์ ์ฅ๋๋ ๊ตฌ์กฐ์ ๋ํ ์์์ด๋ค.
/* Code 3-1(Listing 11-3): JDK Proxy Example */
// Bank.java (suppressing package names...)
import java.utils.*;
// The abstraction of a bank.
public interface Bank {
Collection<Account> getAccounts();
void setAccounts(Collection<Account> accounts);
}
// BankImpl.java
import java.utils.*;
// The โPlain Old Java Objectโ (POJO) implementing the abstraction.
public class BankImpl implements Bank {
private List<Account> accounts;
public Collection<Account> getAccounts() {
return accounts;
}
public void setAccounts(Collection<Account> accounts) {
this.accounts = new ArrayList<Account>();
for (Account account: accounts) {
this.accounts.add(account);
}
}
}
// BankProxyHandler.java
import java.lang.reflect.*;
import java.util.*;
// โInvocationHandlerโ required by the proxy API.
public class BankProxyHandler implements InvocationHandler {
private Bank bank;
public BankHandler (Bank bank) {
this.bank = bank;
}
// Method defined in InvocationHandler
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
if (methodName.equals("getAccounts")) {
bank.setAccounts(getAccountsFromDatabase());
return bank.getAccounts();
} else if (methodName.equals("setAccounts")) {
bank.setAccounts((Collection<Account>) args[0]);
setAccountsToDatabase(bank.getAccounts());
return null;
} else {
...
}
}
// Lots of details here:
protected Collection<Account> getAccountsFromDatabase() { ... }
protected void setAccountsToDatabase(Collection<Account> accounts) { ... }
}
// Somewhere else...
Bank bank = (Bank) Proxy.newProxyInstance(
Bank.class.getClassLoader(),
new Class[] { Bank.class },
new BankProxyHandler(new BankImpl())
);
์ ์ฝ๋์ ๋ํ ๊ฐ๋ตํ ์ค๋ช ์ ์๋์ ๊ฐ๋ค.
- Java Proxy API๋ฅผ ์ํ Bank ์ธํฐํ์ด์ค๋ฅผ ์์ฑํ๋ค.
- ์์์ ์์ฑํ Bank ์ธํฐํ์ด์ค๋ฅผ ์ฌ์ฉํ BankImpl(POJO aka Plane Old Java Object)๋ฅผ ๊ตฌํํ๋ค. ์ฌ๊ธฐ์๋ ์์ํ ๋ฐ์ดํฐ๋ง ๋ค์ด๊ฐ๋ฉฐ ๋น์ง๋์ค ๋ก์ง์ ํฌํจ๋์ง ์๋๋ค.(๋ชจ๋ธ๊ณผ ๋ก์ง์ ๋ถ๋ฆฌ)
- InvocationHandler๋ฅผ ๊ตฌํํ๋ BankProxyHandler๋ฅผ ์์ฑํ๋ค. ์ด ํธ๋ค๋ฌ๋ Java Reflection API๋ฅผ ์ด์ฉํด Bank ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๋ ๊ฐ์ฒด๋ค์ ๋ฉ์๋์ฝ์ ๊ฐ๋ก์ฑ ์ ์์ผ๋ฉฐ ์ถ๊ฐ์ ์ธ ๋ก์ง์ ์ฝ์ ํ ์ ์๋ค. ๋ณธ ์์ ์์ ๋น์ง๋์ค ๋ก์ง(persistant stack logic)์ ์ด ๊ณณ์ ๋ค์ด๊ฐ๋ค.
- ๋ง์ง๋ง์ผ๋ก ์ฝ๋์ ๋ง์ง๋ง ๋ธ๋ญ๊ณผ ๊ฐ์ด BankImpl ๊ฐ์ฒด๋ฅผ BankProxyHandler์ ํ ๋น, Bank ์ธํฐํ์ด์ค๋ฅผ ์ฌ์ฉํด ํ๋ก์๋ ์ธํฐํ์ด์ค๋ฅผ ์ฌ์ฉํด ๋ชจ๋ธ๊ณผ ๋ก์ง์ด ๋ถ๋ฆฌ๋ ์ฝ๋๋ฅผ ์์ฑํ ์ ์๋ค. ์ด๋ก์จ ๋ชจ๋ธ๊ณผ ๋ก์ง์ ๋ถ๋ฆฌ๋ฅผ ์ด๋ค๋ธ ์ฝ๋๋ฅผ ์์ฑํ ์ ์๊ฒ ๋์๋ค.
ํ์ง๋ง ์์ ๊ฐ์ ์๋์ ์ผ๋ก ๊ฐ๋จํ ๊ฒฝ์ฐ์์๋ ๋ถ๊ตฌํ๊ณ ๊ฒฐ๊ณผ์ ์ผ๋ก ์ถ๊ฐ์ ์ธ ๋ณต์กํ ์ฝ๋๊ฐ ์๊ฒผ๋ค. ๋ ์์คํ ๋จ์๋ก ์คํ โ์ง์ โ์ ๋ช ์ํ๋ ๋ฉ์ปค๋์ฆ๋ ์ ๊ณตํ์ง ์๋๋ค.
์์ ์๋ฐ AOP ํ๋ ์์ํฌ
์ Java Proxy API์ ๋จ์ ๋ค์ Spring, JBoss์ ๊ฐ์ ์์ ์๋ฐ AOP ํ๋ ์์ํฌ๋ฅผ ํตํด ํด๊ฒฐํ ์ ์๋ค. ์๋ฅผ ๋ค์ด Spring์์๋ ๋น์ง๋์ค ๋ก์ง์ POJO๋ก ์์ฑํด ์์ ์ด ์ํ ๋๋ฉ์ธ์ ์ง์คํ๊ฒ ํ๋ค. ๊ฒฐ๊ณผ์ ์ผ๋ก ์์กด์ฑ์ ์ค์ด๋ค๊ณ ํ ์คํธ ์์ฑ์ ํ์ํ ๊ณ ๋ฏผ๋ ์ค์ด๋ ๋ค. ์ด๋ฌํ ์ฌํํจ์ user story์ ๊ตฌํ๊ณผ ์ ์ง๋ณด์, ํ์ฅ ๋ํ ๊ฐํธํ๊ฒ ๋ง๋ค์ด ์ค๋ค.
์์๋ฅผ ํตํด Spring ํ๋ ์์ํฌ์ ๋์ ๋ฐฉ์์ ๋ํด ํ์ธํด ๋ณด์.
/* Code 3-2(Listing 11-4): Spring 2.X configuration file */
<beans>
...
<bean id="appDataSource"
class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close"
p:driverClassName="com.mysql.jdbc.Driver"
p:url="jdbc:mysql://localhost:3306/mydb"
p:username="me"/>
<bean id="bankDataAccessObject"
class="com.example.banking.persistence.BankDataAccessObject"
p:dataSource-ref="appDataSource"/>
<bean id="bank"
class="com.example.banking.model.Bank"
p:dataAccessObject-ref="bankDataAccessObject"/>
...
</beans>
Bank
๊ฐ์ฒด๋ BankDataAccessObject
๊ฐ, BankDataAccessObject
๋ BankDataSource
๊ฐ ๊ฐ์ธ ํ๋ก์ํ๋ ๊ตฌ์กฐ๋ก ๋์ด ๊ฐ๊ฐ์ bean๋ค์ด โ๋ฌ์์ ์ธํโ์ ํ ๋ถ๋ถ์ฒ๋ผ ๊ตฌ์ฑ๋์๋ค. ํด๋ผ์ด์ธํธ๋ Bank์ ์ ๊ทผํ๊ณ ์๋ค๊ณ ์๊ฐํ์ง๋ง ์ฌ์ค์ ๊ฐ์ฅ ๋ฐ๊นฅ์ BankDataSource
์ ์ ๊ทผํ๊ณ ์๋ ๊ฒ์ด๋ค.
/* Code 3-3: Code 3-2์ ํ์ฉ๋ฒ */
XmlBeanFactory bf = new XmlBeanFactory(new ClassPathResource("app.xml", getClass()));
Bank bank = (Bank) bf.getBean("bank");
์์ ๊ฐ์ด ์ต์ํ์ Spring-specificํ ์ฝ๋๋ง ์์ฑํ๋ฉด ๋๋ฏ๋ก ํ๋ ์์ํฌ์ โ๊ฑฐ์โ decouple๋ ์ดํ๋ฆฌ์ผ์ด์ ์ ์์ฑํ ์ ์๋ค.
๊ตฌ์กฐ ์ ์๋ฅผ ์ํ xml์ ๋ค์ ์ฅํฉํ๊ณ ์ฝ๊ธฐ ํ๋ค ์๋ ์์ง๋ง Java Proxy๋ณด๋ค๋ ํจ์ฌ ๊ฐ๊ฒฐํ๋ค. ์ด ๊ฐ๋ ์ ์๋์ ์ค๋ช ํ EJB3์ ๊ตฌ์กฐ ๊ฐํธ์ ํฐ ์ํฅ์ ๋ฏธ์ณค๋ค. EJB3์ xml์ Java annotation์ ์ฌ์ฉํด cross-cutting concerns๋ฅผ ์ ์ํ๊ณ ์ํฌํธํ๊ฒ ๋์๋ค.
/* Code 3-4(Listing 11-5): An EBJ3 Bank EJB */
package com.example.banking.model;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.Collection;
@Entity
@Table(name = "BANKS")
public class Bank implements java.io.Serializable {
@Id @GeneratedValue(strategy=GenerationType.AUTO)
private int id;
@Embeddable // An object โinlinedโ in Bankโs DB row
public class Address {
protected String streetAddr1;
protected String streetAddr2;
protected String city;
protected String state;
protected String zipCode;
}
@Embedded
private Address address;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy="bank")
private Collection<Account> accounts = new ArrayList<Account>();
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public void addAccount(Account account) {
account.setBank(this);
accounts.add(account);
}
public Collection<Account> getAccounts() {
return accounts;
}
public void setAccounts(Collection<Account> accounts) {
this.accounts = accounts;
}
}
์์ ๊ฐ์ด EJB3์ EJB2 ๋ณด๋ค ํจ์ฌ ๊ฐ๊ฒฐํ ์ฝ๋๋ก ์์ฑํ ์ ์๊ฒ ๋์๋ค. ๋ช๋ช ์ธ๋ถ ์์ฑ๋ค์ annotation์ผ๋ก ํด๋์ค ๋ด์ ์ ์๋์ด ์์ง๋ง annotation์ ๋ฒ์ด๋์ง ์๊ธฐ ๋๋ฌธ์ ์ด์ ๋ณด๋ค ๋ ๊นจ๋ํ๊ณ ๋ช ๋ฃํ ์ฝ๋๋ฅผ ์ฐ์ถํ๋ฉฐ ๊ทธ๋ก ์ธํด ์ ์ง๋ณด์, ํ ์คํธํ๊ธฐ ํธํ ์ฅ์ ์ ๊ฐ๊ฒ ๋์๋ค.
AspectJ ๊ด์
AspectJ๋ AOP๋ฅผ ์คํํ๊ธฐ ์ํ full-featured tool์ด๋ผ ์ผ์ปฌ์ด์ง๋ค. 8~90%์ ๊ฒฝ์ฐ์๋ Spring AOP์ JBoss AOP๋ก๋ ์ถฉ๋ถํ์ง๋ง AspectJ๋ ํจ์ฌ ๊ฐ๋ ฅํ ์์ค์ AOP๋ฅผ ์ง์ํ๋ค. ๋ค๋ง ์ด๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด ์๋ก์ด ํด, ์ธ์ด ๊ตฌ์กฐ, ๊ด์ต์ ์ธ ์ฝ๋๋ฅผ ์ตํ์ผ ํ๋ค๋ ๋จ์ ๋ ์กด์ฌํ๋ค.(์ต๊ทผ ์๊ฐ๋ โannotation-form AspectJโ๋ก ์ธํด ์ ์ฉ์ ํ์ํ ๋ ธ๋ ฅ์ ๋ง์ด ์ค์ด๋ค์๋ค๊ณ ํ๋ค.) AOP์ ๋ํ ๋ ์์ธํ ๋ด์ฉ์ [AspectJ], [Colyer], [Spring]๋ฅผ ์ฐธ์กฐํ๊ธฐ ๋ฐ๋๋ค.
ํ ์คํธ ์ฃผ๋ ์์คํ ์ํคํ ์ฒ ๊ตฌ์ถ
BDUF(Big Design Up Front: ์์ผ๋ก ๋ฒ์ด์ง ๋ชจ๋ ์ฌํญ์ ์ค๊ณํ๋ ๊ธฐ๋ฒ)์ ํด๋กญ๋ค. ์ฒ์์ ์์ ๋ถ์ ๋ ธ๋ ฅ์ ๋ฒ๋ฆฌ์ง ์์ผ๋ ค๋ ์ฌ๋ฆฌ์ ์ ํญ, ๊ทธ๋ฆฌ๊ณ ์ฒ์ ์ ํํ ์ํคํ ์ฒ๊ฐ ํฅํ ์ฌ๊ณ ๋ฐฉ์์ ๋ฏธ์น๋ ์ํฅ์ผ๋ก ๋ณ๊ฒฝ์ ์ฝ์ฌ๋ฆฌ ์์ฉํ์ง ๋ชปํ๊ฒ ๋๋ค.
๊ด์ , ํน์ ๊ด์ฌ์ฌ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋ถ๋ฆฌํ๋ ๋ฐฉ์์ ๋จ์ํ ์ํคํ ์ฒ์์ ๋ณต์กํ ์ํคํ ์ฒ๋ก ํค์๊ฐ ์ ์๋ ์์์ ์ด ๋ ์ ์๋ค. ํ๋ก์ ํธ์ ์ง๋ฐ์ ์ธ ๋ฒ์, ๋ชฉํ, ์ผ์ , ์ผ๋ฐ์ ์ธ ๊ตฌ์กฐ์ ๋ํด ์๊ฐํ๋, ๋ณํ๋ ํฉ๊ฒฝ์ ๋์ฒํด ์ง๋ก๋ฅผ ๋ณ๊ฒฝํ ๋ฅ๋ ฅ๋ ๋ฐ๋์ ์ ์งํด์ผ ํ๋ค. ์ฆ, ๋๋ฌด ๋ง์ ๋ฏธ๋๋ฅผ ๋ฐ๋ผ๋ณด๊ณ ๊ณผ๋ํ๊ฒ ๋ฃ์ API๋ ์คํ๋ ค ๊ด์ฌ์ฌ๋ฅผ ๋ถ๋ฆฌํ์ง ๋ชปํ๊ณ , ์ฌ์ฉ์ ์คํ ๋ฆฌ์ ์ง์คํ์ง ๋ชปํ๋ ๊ฒฐ๊ณผ๋ฅผ ์ด๋ํ ์ ์๋ค๋ ๋ง์ด๋ค.
์ด์์ ์ธ ์์คํ ์ํคํ ์ณ๋ ๊ฐ๊ฐ POJO๋ก ๋ง๋ค์ด์ง ๋ชจ๋ํ๋ ๊ด์ฌ ๋ถ์ผ ์์ญ(modularized domains of concern)์ผ๋ก ์ด๋ฃจ์ด์ ธ์ผ ํ๋ค. ๋ค๋ฅธ ์์ญ๋ผ๋ฆฌ๋ Aspect์ ๊ฐ๋ ์ ์ฌ์ฉํด ์ต์ํ์ ๊ฐ์ญ์ผ๋ก ํตํฉ๋์ด์ผ ํ๋ค. ์ด๋ฌํ ์ํคํ ์ณ๋ ์ฝ๋์ ๋ง์ฐฌ๊ฐ์ง๋ก test-driven๋ ์ ์๋ค.
์์ฌ ๊ฒฐ์ ์ ์ต์ ํํ๋ผ
๋ชจ๋์ ๋๋๊ณ ๊ด์ฌ์ฌ๋ฅผ ๋ถ๋ฆฌํ๋ฉด ์ง์ฝ์ ์ธ ๊ด๋ฆฌ์ ๊ฒฐ์ ์ด ๊ฐ๋ฅํด์ง๋ค. ๋๋๋ก ๊ฐ๋ฅํ ๋ง์ง๋ง ์๊ฐ๊น์ง ๊ฒฐ์ ์ ๋ฏธ๋ฃจ๋ ๋ฐฉ๋ฒ์ด ์ต์ ์ด๋ผ๋ ๊ฒ์ ์์ง ๋ง์.
๋ชจ๋ํ๋ ๊ด์ฌ ๋ถ์ผ๋ก ์ด๋ฃจ์ด์ง POJO ์์คํ ์ (๋ณํ์ ๋ํ)๋ฏผ์ฒฉํจ์ ๊ฐ์ฅ ์ต์ ์ ์ ๋ณด๋ฅผ ๊ฐ์ง๊ณ ์ ์์ ์ต์ ์ ์ ํ์ ํ ์ ์๊ฒ ๋์์ค๋ค. ๊ฒฐ์ ์ ํ์ํ ๋ณต์ก๋ ๋ํ ๊ฒฝ๊ฐ๋๋ค.
๋ช ๋ฐฑํ ๊ฐ์น๊ฐ ์์ ๋ ํ์ค์ ํ๋ช ํ๊ฒ ์ฌ์ฉํ๋ผ
๋ง์ ์ํํธ์จ์ด ํ๋ค์ ํจ์ฌ ๊ฐ๋ณ๊ณ ์ง๊ด์ ์ธ ๋์์ธ์ด ๊ฐ๋ฅํ์์๋ ๋ถ๊ตฌํ๊ณ ๊ทธ์ ํ์ค์ด๋ผ๋ ์ด์ ๋ง์ผ๋ก EJB2 ๊ตฌ์กฐ๋ฅผ ์ฌ์ฉํ๋ค. ํ์ค์ ์ฌ์ทจํด โ๊ณ ๊ฐ์ ์ํ ๊ฐ์น ์ฐฝ์ถโ์ด๋ผ๋ ๋ชฉํ๋ฅผ ์์ด ๋ฒ๋ ธ๊ธฐ ๋๋ฌธ์ด๋ค.
ํ์ค์ ์์ด๋์ด์ ์ปดํฌ๋ํธ์ ์ฌ์ฌ์ฉ, ๊ด๋ จ ์ ๋ฌธ๊ฐ ์ฑ์ฉ, ์ข์ ์์ด๋์ด์ ์บก์ํ, ์ปดํฌ๋ํธ๋ค์ ์ฐ๊ฒฐ์ ์ฝ๊ฒ ๋์ ์ค๋ค. ํ์ง๋ง ์ข ์ข ํ์ค์ ๋ง๋๋ ๋ฐ์ ๋๋ ์๊ฐ์ ๋ฉํ ๊ธฐํ์ ๋ง์ถ๊ธฐ ์ด๋ ต๊ฒ ๋ง๋ค๊ณ , ํน์ ์ต์ด์ ์ ๊ณตํ๋ ค๋ ๊ธฐ๋ฅ๊ณผ ๋๋จ์ด์ง๊ฒ ๋๊ธฐ๋ ํ๋ค.
์์คํ ์ ๋๋ฉ์ธ ํนํ ์ธ์ด๊ฐ ํ์ํ๋ค
์ข์ DSL์ ๋๋ฉ์ธ ์์ญ์ ๊ฐ๋ ๊ณผ ์ค์ ๊ตฌํ๋ ์ฝ๋ ์ฌ์ด์ โ์ํต์ ๊ฐ๊ทนโ์ ์ค์ฌ ๋๋ฉ์ธ ์์ญ์ ์ฝ๋ ๊ตฌํ์ผ๋ก ๋ฒ์ญํ๋ ๋ฐ์ ์ค์ญ์ ์ค์ฌ์ค๋ค. DSL์ ํจ์จ์ ์ผ๋ก ์ฌ์ฉํ๋ฉด ์ฝ๋ ๋ฉ์ด๋ฆฌ์ ๋์์ธ ํจํด์ ์ถ์๋๋ฅผ ๋์ฌ ์ฃผ๋ฉฐ ๊ทธ์ ๋ฐ๋ผ ์ฝ๋์ ์๋๋ฅผ ์ ์ ํ ์ถ์ํ ๋ ๋ฒจ์์ ํํํ ์ ์๊ฒ ํด์ค๋ค.
DSL์ โ๋ชจ๋ ๋จ๊ณ์์์ ์ถ์ํโ์ โ๋ชจ๋ ๋๋ฉ์ธ์ POJOํโ๋ฅผ ๊ณ ์ฐจ์์ ๊ท์น๊ณผ ์ ์ฐจ์์ ๋ํ ์ผ ์ ๋ฐ์ ๊ฑธ์ณ ๋์ ์ค๋ค.
๊ฒฐ๋ก
์์คํ ์ญ์ ๊นจ๋ํด์ผ ํ๋ค.
์นจ๋ต์ ์ธ(invasive) ์ํคํ ์ณ๋ ๋๋ฉ์ธ ๋ก์ง์ ํผํด๋ฅผ ์ฃผ๊ณ ์ ์์ฑ์๋ ์ํฅ์ ์ค๋ค. ๋๋ฉ์ธ ๋ก์ง์ด ๋ชจํธํด์ง๋ฉด ๋ฒ๊ทธ๋ ์จ๊ธฐ ์ฌ์์ง๊ณ ๊ธฐ๋ฅ ๊ตฌํ์ ์ด๋ ค์ ์ง๋ค. ์ ์์ฑ์ด ์นจํด๋๋ฉด ์์ฐ์ฑ์ด ์ ํด๋๊ณ TDD๋ก ์ธํ ์ด๋ ๋ํ ์ป์ ์ ์๋ค. ์๋๋ ๋ชจ๋ ๋ ๋ฒจ์ ์ถ์ํ์์ ๋ช ํํด์ผ ํ๋ค. ์ด๋ ๊ฐ๊ฐ์ concern๋ค์ POJO๋ก ์์ฑ๋ ์ฝ๋์ aspect-like ๋ฉ์ปค๋์ฆ์ ํตํด ๊ตฌ์ฑํ ๋ ๋น๋ก์ ์คํ๋ ์ ์๋ค. ๋น์ ์ด ์์คํ ์ ๋์์ธํ๋ ๋ ์์ ์ธ ๋ชจ๋์ ๋์์ธํ๋ , ๋์ํ๋ ๋ฒ์์์ ๊ฐ์ฅ ๊ฐ๋จํ ๊ฒ์ ์ฌ์ฉํ๋ ๊ฒ์ ์์ด์๋ ์๋๋ค.