토비의 스프링 3의 springusergroup 예제에 대한 이야기 세 번째.

도메인 오브젝트를 DAO와 DAO 클라이언트 사이의 결합을 낮춰주고 구현 기술과 방법에 독립적으로 만들기 위한 DTO로 삼는다면, 더 나아가서 전 계층에서 일관되게 유지되고 접근할 수 있는 데이터 홀더이자 도메인 로직을 가진 오브젝트로 사용하려면 DAO는 그에 맞는 형태의 구조의 오브젝트를 돌려줘야 한다. 관건은 관계를 가진 엔티티/오브젝트에는 원한다면 언제든지 접근해야 하고, 그 오브젝트가 DAO에서 미리 읽어온 정보로 초기화 되어있지 않다면 어느 시점에라도(심지어 뷰에 이르러서라도) 다이내믹하게 DB를 호출해서 정보를 가져와야 한다. 물론 이를 사용하는 쪽에서는 전혀 그런 사실을 눈치채지 못하게 투명하게 그 과정이 백그라운드에서 자동으로 일어나야 한다. 일명 lazy-loading이다.

Lazy-loading하면 왠지 부정적인 느낌으로 들리지만 사실은 Smart-loading이라고 해줄 수 있을만큼 영리한 방식으로 동작한다. ORM은 오브젝트와 관계형(relational) DB 사이의 미묘한 차이를 효과적으로 줄여주는 기술이다. 하지만 오브젝트를 사용하더라도 RDB의 특징을 잘 이해할 필요가 있다. User테이블은 Group테이블의 PK인 id 컬럼의 값을 FK로 가지고 있다. 예를 들면 group_id 같은 필드이다. 그래서, 단지 관계를 가지고 있는 Group 테이블의 id만 필요하다면 굳이 Group테이블을 조인해서 읽을 필요가 없다. 하지만 User – Group 오브젝트 관계에서는 User 오브젝트가 groupid라는 정수형 필드 값을 가지지 않는다. Group의 id가 필요하더라도 Group오브젝트에 가서 id를 읽어와야 한다. 이것이 RDB 정보를 오브젝트로 전환했을 때 발생하는 부담이다.

그런데 이를 부담이 아닌 것처럼 만들 수 있다. 사실은 영리한 lazy-loading 덕분이다. Lazy-loading은 User 오브젝트에 직접 연결된 Group오브젝트를 필요한 시점에서 초기화 해준다. 그렇다고 초기화용 유틸 메소드를 따로 실행하지는 않는다. User 오브젝트를 사용하는 입장에서는 이미 처음부터 Group 오브젝트의 정보도 모두 존재하는 것처럼 사용하면 된다. 단지 Group의 내부 정보를 사용할 때 뒤에서 알아서 Group 테이블의 레코드를 읽어서 다이나믹하게 그 값을 채워넣어주는 것이다.

그런데 만약 어떤 User의 소속 Group에 대한 group_id만 필요한 경우가 있다고 해보자. 전통적인 SQL기반의 DAO라면 간단하다. FK는 User 테이블에 있기 때문에 Group 테이블에 접근할 것도 없이 select * from User 해도 그만이다. User 안에 group_id가 있기 때문이다.

반면에 오브젝트로 전환되면 Group의 id가 필요할 때 user.getGroupid()라고 할 수 없다. 이 때는 user.getGroup().getId()라고 해야 한다. 결국 User 클래스의 group 필드에 Group타입의 오브젝트가 들어있어야 한다는 말이다. 그렇다면 단지 group_id가 필요한 경우에도, User테이블에서 다 읽어올 수 있는 값인데도 Group 테이블의 정보를 모두 읽어오는 lazy-loading이 필요할까?

그건 아니다. Lazy-loading은 게으를 지는 몰라도 멍청하지는 않다. Lazy-loading에 의해서 Group정보를 실제로 가져오는 시점은 user.getGroup().getName()과 같이 FK/PK를 제외한, 즉 User테이블에서는 가져올 수 없는 값을 읽어올 때 뿐이다. 따라서 user.getGroup().getId()는 lazy-loading을 발생시키지 않는다. 영리하지 않는가?

 

그렇다면 lazy-loading 구현은 어떻게 할 수 있을까?

Lazy-loading은 투명해야 한다. User 오브젝트 입장에서 Group이 이미 모든 값을 가진, DB정보를 읽어와 초기화된 오브젝트라고 생각하게 해야 한다. 실제 초기화는 Group의 id가 아닌 접근자(getter)를 사용하는 시점이다. 디자인 패턴을 제대로 공부한 사람이라면 Lazy-loading하면 딱 떠오르는 패턴이 있어야 한다. 바로 프록시 패턴이다. 프록시 패턴은 런타임 시점에 다이내믹하게 오브젝트의 접근 방법을 제어(access control)해주는 패턴이다.

프록시 패턴은 구조적으로는 데코레이터랑 비슷하다. 하지만 그 의도(intent)와 사용 목적이 다르다. 사실 객체지향 디자인패턴의 구현 방법은 따져보면 다 비슷하다. 구조는 두가지로 분류할 수 있다. 하나는 블랙박스 패턴, GoF책의 용어로 하자면 Scope가 클래스인 것이고, 다른 하나는 화이트박스 패턴, Scope가 오브젝트인 것이다. 전자는 상속을 후자는 합성(위임, 프로토콜, 인터페이스, 메시지.. 뭐라고 해도 좋다)을 확장을 위해서 사용한다. 결국 두 가지 구현 방법, 구조면 되는 것을 굳이 십여 개의 패턴으로 세분화 한 것은 각각의 사용 목적과 해결하고자 하는 문제가 다르기 때문이다. 얘기가 샜다. –_-;

아무튼 프록시 패턴은 오브젝트 사이의 접근 방식을 다이내믹하게 제어해주는 것이 목적이다. A->B 사용 의존관계가 있을 때 A입장에서 B처럼 보이는 B2를 낑겨 넣는다. A->B2->B 그리고 B2가 B인 것처럼 A한테 행세 하면서 A의 B에 대한 접근을 제어한다. 접근 제어 방법은 다양한 데 그 중에서 가장 대표적이라고 볼 수 있는 것이 바로 lazy-loading이다. 프록시 패턴 = lazy loading이라고 생각하는 사람이 있을만큼 대표적이다. 그래서 프록시 패턴을 한마디로 ‘필요할 때 만든다’라고 설명하기도 한다. 즉, 초기에는 A->B2 형태로 존재하다가 A가 B의 기능을 사용하려고 하는 시점이 되면 B2가 B를 슬쩍 만들고는 모든 A의 요청을 B에게 위임해서 처리해서 마치 A가 B를 바로 사용하는 것처럼 사기치는 것이다. A->(사기)->B2->(떠넘기기)->B 구조라고 볼 수 있다.

여기서 관건은 실제 B가 아닌 B2가 어떻게 A한테 B인것처럼 행세하느냐 이다. 디자인 패턴의 정답은 두 가지 뿐이니 둘 중의 하나이다. 상속이거나 오브젝트 합성이다. 기본적으로 후자인 오브젝트 합성을 사용하는 것이 일반적이다. 즉, B2가 구현한 인터페이스를 두고, 이를 A가 사용하게 한다. 물론 인터페이스라는 것은 자바의 interface 키워드를 말하는게 아니다. 이걸 잘못 이해해서 인터페이스 얘기가 나오면 자바 언어의 단점을 들먹이며 그 때문에 디자인 패턴이 필요하다고 말하는 인간들도 있는데, 이런 사람들은 인터페이스라는 말이 무슨 뜻인지 이해를 못해서 그런 거다. 인터페이스란 A가 B를 어떻게 사용할지 미리 정해둔 약속이자, B를 바라보는 A의 창이다. 이런 정해진 접근방법이 있다면 interface 키워드로 정의된 인터페이스를 사용하든, 추상 슈퍼 클래스를 사용하든, 단지 미리 정해둔 다이나믹한 프로토콜을 사용하든, 전문으로 된 테스트 메시지를 사용하든 모두 인터페이스다. GoF가 말한 programming in interface라는 것은 그런 뜻이다. 그것이 지켜지면 B2는 A에게 B처럼 행세할 수 있다.

결국 어떻게든 A->B2 관계를 다이내믹하게 만들 수 있다면 된다. 다이내믹하다는 것은 A가 직접 B나 B2를 만들지 않는다는 뜻이다. A가 사용할 B, B2를 정해주는 것은 제3의 오브젝트 책임이고, 이렇게 해서 B대신 B2를 A가 사용하게 만드는 방식을 DI라고 하는 거다. DI는 이런 기법을 말하는 것이다.

얘기가 또 샜는데. 다시 돌아와서 정리해보자. A->B는 여기서 User->Group이다. 프록시 패턴을 사용해서 lazy-loading을 적용할 것이니 Group2가 필요하다. Group2는 Group가 인터페이스를 공유해서 A(User)가 입장에서 B(Group) 처럼 보여야 한다. 그렇다면 Group에 대한 인터페이스(여기선 자바의 interface)를 만들어야 하는가? 도메인 오브젝트인 Group에 굳이 그럴 이유는 없다. 다시 말하지만 인터페이스는 A가 B에 접근할 때 사용하는 정해진 약속일 뿐이다. User는 Group의 필드 정보에 접근하면 된다. 즉, Group의 수정자, 접근자에 접근하면 된다. 결국 Group이 자바 빈 규약을 따라서 만들어진 오브젝트라면 Group의 모든 프로퍼티 메소드(getter/setter)가 User가 알아야 할 Group의 인터페이스이다. 그래서 이 때는 lazy-loading을 위한 B2를 만들 때 인터페이스-구현 클래스 방식보다는 슈퍼 클래스-서브 클래스 방식이 낫다. Group의 인터페이스인 모든 public 메소드를 동일하게 가지고 있는 서브 클래스를 만들면 된다. 여기서 혼동하지 말아야 할 것은, 비록 상속을 사용했지만 이건 상속에 의한 확장(클래스 스코프, 블랙박스)이 아니다. 클래스 상속을 사용하지만 이 것은 인터페이스를 이용한 오브젝트 합성의 한 방법일 뿐이고, 여전히 programming to interface를 지키는 것이다.

그래서, 이런 lazy-loading에는 클래스를 이용한 프록시 오브젝트를 만드는 기법을 사용하는 것이 편리하다. 물론 직접 Group을 상속한 프록시를 만드는 것은 번거롭다. 자동으로 Group의 모든 기능을 가진 위임 프록시를 만들고, 그 기능을 중앙에서 제어하게 하는 방식을 쓰는 것이 자연스럽다. 나중에 User-Group이 아니라, Article->User나 Group->Category와 같은 의존 관계에도 동일한 lazy-loading을 적용하려면 다이내믹하게 프록시를 만들고 기능을 확장할 수 있도록 재활용 가능한 프록시를 만드는 것이 좋다.

이 때 주의 할 점은 프록시인 Group2의 메소드를 사용한다고 바로 Group을 DB에서 가져오면 안된다는 것이다. 앞에서 설명한 것처럼 원래 User테이블에서 읽어올 수 있는 FK인 group_id는 lazy-loading용 프록시인 Group2에 미리 넣어주고, Group2에게 getId()를 요청하면 그것만 돌려주면 그만이다. 이를 통해서 불필요한 DB접근을 최소화 할 수 있다. 사실 FK 값만 넘겨서 새로운 관계를 만드는 작업은 RDB에서 자주 일어난다. 따라서 id는 프록시가 제공하는 것을 원칙으로 하고, id외의 다른 필드, 즉 User 테이블에서 가져올 수 없는 정보에 접근하려고 할 때만 DB를 읽어서 Group을 가져와 User->Group2(프록시)->Group 구조로 만들어주면 된다.

구현 방법은 클래스 프록시를 쓴다고 했는데, 자바 API에는 인터페이스를 이용한 다이내믹 프록시만 제공된다. 그에 대한 클래스 버전의 프록시를 만드는 것은 CGLib을 사용해야 한다. CGLib은 사용방법이 다양하지만, 단순한 다이내믹 프록시 구현이라면 JDK의 다이나믹 프록시(Proxy 클래스)를 사용하는 것과 거의 비슷하다.

CGLib 써서 구현이 어려울까? 아니다. 쉽다. 살짝 제약이 있긴 하지만 어떤 엔티티에도 적용 가능한 lazy-loading 프록시 팩토리를 구현했더니 20줄 정도면 충분했다. 괄호가 한 줄씩 먹는 걸 빼면 10여줄이면 충분하다. TDD로 만든답시고 테스트 꼬박 다 만들고, DI적용하려고 인터페이스 따로 정의하고, 빈 설정 등록하고, 각종 예외상황에 대한 테스트까지 다 만들어서 돌려봐도 30분 정도면 충분했다.

사용방법은 이렇다. Lazy-loading 프록시 오브젝트 다이내믹하게 만들어진다. JDBC를 사용하는 UserDao에서 User를 읽어올 때 lazy-loading을 적용하기로 했으면 group 필드에 대해서는 다음과 같은 코드로 프록시를 만들어 넣으면 된다.

user.setGroup(UserDaoJdbc.this.entityProxyFactory.createProxy(Group.class, groupDao, rs.getInt("groupid")));

여기서 entityProxyFactory는 UserDao에서 DI 받은 EntityProxyFactory 타입 빈 오브젝트다. 실제 사용한 빈 클래스는 EntityProxyFactory를 구현한 CglibEntityProxyFactory이다. 프록시를 만들 타입과 lazy-loading에 사용할 DAO 그리고 id(FK) 값만 넣어주면 끝.

이후로 user.getGroup()이나 user.getGroup().getId() 또는 ${user.group.id}을 사용하면 프록시에서 id 값을 가져오거나 프록시 자체를 리턴한다. 하지만 ${user.group.name}과 갈이 원래 User테이블에서는 가져올 수 없는 정보에 접근하면 DAO의 get(id)를 실행해서 Group 오브젝트를 읽어와 이를 사용하게 해준다. 물론 한번 읽어오면 그 뒤로는 DB에 다시 접근할 것 없이 읽어둔 Group 정보를 계속 활용한다.

 

그렇다면 항상 이렇게 lazy-loading으로 접근해야 할까? 물론 그건 아니다. 만약 해당 DAO메소드에서 User를 가져가서 사용하는데, 대부분 Group 정보를 같이 사용한다면 이 때는 아예 JOIN을 해서 User-Group을 한번에 읽어와서 User-Group 오브젝트로 만들어주는 것이 낫다. 또는 Group이 자주 변경되지 않는, 조회만 자주 되는 정보라면 메모리 캐시에 두어도 좋다. 프록시 생성기를 조금 지능적으로 만들면 캐시에서 가져올 수 있는 정보라면 lazy-loading 프록시 대신 캐시 정보를 읽어서 넣어주게 만들 수도 있다.

 

아무튼 이렇게 해서 JDBC를 사용하는 경우에도 어렵지 않게 도메인 오브젝트 구조를 유지하고, lazy-loading을 적용할 수 있다는 것을 Springusergroup 예제에서 보여주고 있다. 물론 아주 간단한 구현이다. 단지 아이디어를 제공해주는 것이 목적이니 이를 적절히 확장하고 실전에서 활용하는 것은 독자의 몫이다.

상세한 구현 내용과 테스트 코드, 적용 코드는 토비의 스프링 3의 부록 CD에서 찾을 수 있다. 이제 일주일쯤 지나면 나오겠구나. 인쇄소 휴가기간도 걸리고 해서 책이 나오는 데 시간이 조금 걸린다고 한다.

책에 미처 넣지 못한 예제 설명 중에서 두 가지는 대충 끝냈고, 이제 한 가지만 남았다. 휴~

Related posts:

  1. [토스3] 스프링 JDBC DAO에 lazy-loading 기능 적용하기
  2. Spring 3.0 (15) Jdbc 모듈의 선택 라이브러리 분석
  3. [토스3] 매핑 가능한 BeanPropertySqlParameterSource
  4. Spring 3.0 @MVC 메소드에서 자동으로 리턴 모델에 추가되는 것들
  5. [토스3] 테스트를 위한 필드 주입 유틸
  6. [토스3] 스프링 3.0.4 <mvc:default-servlet-handler/>를 이용해서 UrlRewriteFilter없이 깔끔한 URL을 만들기
  7. Spring Expressions(SpEL)를 이용한 Mockito Argument Matcher 만들기
  8. 그럭저럭
  9. 스프링이 쓰는 Dynamic Proxy는 Proxy 패턴인가 Decorator 패턴인가?
  10. 인터페이스 프로그래밍과 IDE 코드네비게이션편의성
  11. Dependency Injection의 Dependency란 무엇인가?
  12. 토비의 스프링 3 출간지연과 동영상 소식
  13. 토비의 스프링 3이 나오기까지 (5)
  14. 토비의 스프링 3이 나오기까지 (11)
  15. Rod Johnson의 Testing with Spring

Facebook comments:

to “[토스3] 스프링 JDBC DAO에 lazy-loading 적용하기 (2)”

  1. … [Trackback]…

    [...] Read More Infos here: toby.epril.com/?p=1077 [...]…

  2. I’m amazed, I must say. Seldom do I come across a blog that’s
    equally educative and amusing, and without a doubt, you’ve hit the nail on the head. The issue is something which too few people are speaking intelligently about. Now i’m very happy that I came across this during my search for something concerning this.

  3. thanks for share!

  4. Check out here
    [url=http://www.pctpat.com/game/michael-kors5055-cronografo-reloj-de-oroa4e.html]michael kors madrid[/url]
    michael kors madrid

  5. Browse Around This Web-site

  6. thank you for share!

  7. thank you for share!

  8. Browse Around These Guys
    [url=http://www.sendee.com/images/rssibutton.php?/how-to-lose-weight-quickly-in-a-weekmeizitangoriginal-sale.html]original sale[/url]
    original sale

  9. thank you for share!

  10. Pop Over To This Web-site
    [url=http://outlettonlineshop.com]ugg outlet[/url]
    ugg outlet

  11. CLICK To Investigate
    [url=http://mensbootsoutlet.com]uggs for men[/url]
    uggs for men

  12. mbt sawa shoes [토스3] 스프링 JDBC DAO에 lazy-loading 적용하기 (2) » Toby’s Epril

  13. thanks for share!

  14. Comprar Zapatillas Gucci Hombre
    Should you be doubtful about regardless of whether you must data file an insurance coverage claim, will not contact your insurance company and make that sort of inquiry. Attempt to question somebody else that does not work with your insurance company due to the fact a lot of companies put these telephone calls to the document as mishaps and they can try to use these people to enhance your rates.

    https://www.apresskibar-bumpers.nl/images/apr2/27309-adidas-schoenen-kopen-utrecht.jpg

    If you think stressed at any point in the daytime, turn to your favorite tracks or tunes genre. This can help you to relax and boosts your mind-set. Using conventional or upbeat audio may help you develop a balance and stability, eradicating the daily challenges which you sense.

    https://www.canadian-poker.ca/images/can2/10833-nike-air-max-90-ultra-moire-blackblack.jpg

  15. viagra online
    Not just should you program a web site that may be scalable, it must be upgradable, too. Modern technology is transferring with a lightning tempo, and you need to maintain. Improving your software as well as converting for the latest, greatest foundation must be smooth. Policy for this BEFORE you start designing making it straightforward to manage later.
    levitra 20mg
    Make sure all of your posts and articles possess a Facebook button on the top. This button makes it easy to your visitors to discuss your posts on Fb. Most people is not going to want to discuss your site content on Facebook or twitter if they have to go through copying and pasting a link.
    kamagra österreich
    Locate a help group in your area or on the web. Many people discover it beneficial to explore their yearnings and have assistance from others who happen to be through the knowledge of giving up smoking. Once you have cease, you may offer you support to the people from the previously stages, strengthening your very own good choice.
    Viagra generika

  16. kamagra jelly
    Continue to keep regularly in contact with your reps. Ask questions and check together with them to determine if they need any assist. When they bring in somebody new, make certain you understand it personally. Sensing they may have the assistance of the recruit will greatly assist to maintaining them enthused and motivated relating to your merchandise.
    Cialis lilly
    When you start to truly feel stress and anxiety, ensure that you make a move. Start up the television or begin writing in a journal. Tend not to just stay there and let your signs and symptoms obtain the best of you. When in uncertainty, take steps to occupy the mind to you personally can unwind a little.
    viagra wirkung
    Will you detest having to continuously hear the sound of your apple ipad tablet? If you have, anyone can mute it. The more mature iPads do not have this option, however the iOS 4.3 Apple company will assist you to try this. Simply click and hold to the Amount-downward switch, as well as your noise will mute.
    kamagra prezzo

  17. cialis ricetta
    Before you decide to put somebody on your email marketing checklist, you should have their authorization. When you don’t have their own approval, you will be liable for giving junk e-mail and you will have numerous grievances. Your email service provider can even dissolve your account should they get blowing wind of the things you’re performing. Protect against that from taking place by requesting that individuals join your collection.
    kamagra kopen
    Listed here is a hint for organic and natural growing plants! Utilize a bad weather determine. Most plants require about an inches of water per week. To learn how much you have to water, it is very important recognize how much drinking water the plants and flowers obtained from rain. As rainfall can vary significantly inside a area, don’t depend upon your conditions document rather make use of a rainfall gauge to determine the sum that dropped on your location.
    cialis pris
    1 solid word of advice for to keep good health since you are getting older is to eat a healthy diet program. An eating plan which happens to be properly-well-balanced involves meals rich in veggies, many fruits, and grain. Ensure you restrict your consumption of trans excess fat, saturated fats and bad cholesterol. By eating a properly-balanced diet regime, the body is supplied the primary vitamins and minerals it must keep optimum wellness.
    levitra 20

  18. cialis original
    If your company banking account has overdraft, eliminate it. The volume of get your interest shell out whenever your profile goes into the negative is astronomical! Rather, when finances are restricted you ought to speak with your bank about acquiring a credit line to help you out for now, and then continue to keep that line of credit available when it’s repaid in the event that.
    viagra générique
    Have others assist you to advertise your organization on social media. Social media marketing will allow you to find customers who are enthusiastic about your products and will be delighted to spread details, discount coupons, and standard testimonials relating to your business and products. Make use of them sensibly since they will assist you to get to potential prospects who you are not able to attain almost every other way.
    propecia belgique
    Be sure you know what insurance you will be purchasing. An affordable beater auto that you simply bought to get a track doesn’t will need comprehensive insurance. It could be more affordable to get a fresh vehicle than to get it replaced. Learning the dissimilarities among the types of protection can make you significantly better prepared when looking at estimates.
    viagra online

Leave a Reply

(required)

(required)

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

© 2017 Toby's Epril Suffusion theme by Sayontan Sinha