티스토리 툴바

블로그 이미지
소프트웨어 개발 경험을 공유하고 싶은 재밌게 사는 소프트웨어 엔지니어입니다^^

카테고리

Chungha Story (44)
Agile Experience (21)
My Family (4)
Life Style (8)
Programming (8)
Android (2)
Total25,694
Today5
Yesterday35
새로운 업무를 받음

얼마전 오래된 시스템(legacy system)에 새로운 플랫폼의 부가(extension) 모듈을 이식하라는 업무를 할당 받았습니다. 쉽게 생각하면 새로운 플랫폼은 유연성을 크게 가질수 있도록 작성되었으니 쉽게 이식 될 수 있다고 오해 할 수 있습니다. 저는 먼저 오래된 시스템과 새로운 플랫폼의 의존성을 확인하였습니다. 오래된 시스템의 일부 코드를 수정(modification)하는 것은 일단 허용하기로 하였습니다. 하지만 새로운 플랫폼의 부가 모듈을 바로(쉽고 빠르게 수정없이) 이식하기에는 문제가 있었습니다. 

무엇이 문제였을까

새로운 플랫폼의 부가 모듈은 하위 모듈(base library)에 의존성(dependency)을 가지고 있었습니다. 일반적으로 계층 기반(layer based)으로 모듈을 작성할 때 하위 계층에서 상위 계층을 직접 참조하는 것은 원칙적으로 불허 하지만, 그 반대의 경우는 허 하는 경우가 많습니다. 간단히 생각해보면 이는 크게 문제가 되지 않을 수 있지만, 상위 모듈이 하위 모듈에 의존하기 때문에 상위 모듈의 유연성, 재사용성은 매우 떨어지게 됩니다.

해결 방법

이와 같은 상황을 해결하는 방법은 다음과 같습니다.
  1. 오래된 시스템에 하위 계층까지 모두 이식한다.
  2. 새로운 플랫폼의 부가모듈을 컴파일 옵션으로 시스템에 따라 다른 하위 레이어를 사용하도록 수정한다.
  3. 새로운 플랫폼의 부가모듈을 복사하여 오래된 시스템에 맞도록 수정한다.
  4. 새로운 플랫품의 부가모듈이 가지는 하위 계층에 대한 의존성을 제거한다.

의존성을 역전시키자

위의 문제에서 하위 계층에 대한 의존성을 제거하는 것(해결방법 4)을 객체지향 디자인에서는 의존성 역전 원칙(The Dependency Inversion Principal)이라고 합니다.
High level modules should not depend upon low level modules. Both should depend upon abstractions.
Abstractions should not depend upon details. Details should depend upon abstractions.
이 원칙은 상위모듈은 하위 모듈의 추상 클래스(또는 인터페이스)에만 의존함으로써 구체적인 구현에 대한 의존성은 갖지 않도록 하는 것입니다. 
위의 상황에서 의존성 문제가 되는 하위 모듈 중의 하나는 타이머입니다. 만약에 해결방법 1로 해결한다면 다음과 같은 그림처럼 작업이 이루어질 것입니다. 즉, 오래된 시스템에도 TimerB가 존재하지만, TimerA까지 이식함으로써 부가 모듈을 이식 가능하게 합니다. 이는 타이머 기능의 중복을 만들게 됩니다. 때에 따라서 TimerA의 의존성 때문에 다른 모듈들이 줄줄이 딸려가는 일이 생길수도 있습니다.
해결방법 2로 하게 되면 모듈의 구현 복잡도는 증가하게 될 것이며, 해결방법 3은 새로운 소스코드 브랜치가 생기게 되어 관리에 대한 부담이 증가하게 됩니다.
마지막으로 해결방법 4로 이 문제를 해결하면 다음과 같은 그림처럼 작업이 이루어 질 것입니다.
즉, 부가모듈은 ITimer라는 인터페이스에만 의존하게 되고 새로운 플랫폼과 오래된 시스템은 각각 해당 인터페이스를 Adapter Pattern을 이용하여 TimerA, TimerB로 구현하면 됩니다. 결과적으로 부가 모듈만 깔끔하게 이식할 수 있습니다.
저는 이 문제를 해결방법 4를 이용하여 해결했습니다. 제가 정의한 추상클래스는 다음과 같습니다.
IMpTimerListener는 타이머의 이벤트를 받기 위한 추상클래스이며, IMpTimer는 타이머 등록 및 해제를 위한 추상클래스입니다.

회고(retrospective)

사실, 이 업무를 진행하면서 모든 의존성을 위와 같은 방법으로 해결하지는 못했습니다. 기존 구현에 따라 해결방법 2를 사용한 경우도 있었습니다. (Define으로 구현되어 있어서 불가피 했음) 하지만 이러한 리팩토링을 통해 위의 부가모듈의 의존성은 상당히 사라지게 되었고 재사용성은 크게 증가하게 되었습니다. (추후 재사용 요구사항이 예상되는 모듈이었습니다.) 모든 모듈에 대해서 재사용성에 포커스를 두는 것은 over-engineering의 가능성이 있습니다. 하지만 이와 같이 재사용 이슈가 예상되는 모듈에 대해서는 의존성을 철저히 관리함으로써 추후 유지보수 업무를 줄일 수 있을 것입니다.^^ 감사합니다.

저작자 표시 비영리 동일 조건 변경 허락
Posted by sozu

Trackback | http://sozu.tistory.com/trackback/19 관련글 쓰기

댓글을 달아 주세요

  1. 2009/12/02 18:11 heestory.kr  댓글주소  수정/삭제  댓글쓰기

    ㅋㅋ역시 정리 잘하셔-ㅎㅎㅎ
    기회가 될때마다 이렇게 의존성을 없애고 리펙토리를 수행하다보면 더욱더 좋아지겠죠-ㅎㅎ힘냅시다ㅠ.ㅋ

  2. 2009/12/07 11:36 Eminency  댓글주소  수정/삭제  댓글쓰기

    좋은 내용인데? ^^
    아, 멀어진 개발이여 ㅠㅠ
    (급수정 -_-)

    • 2009/12/03 10:46 청하 sozu  댓글주소  수정/삭제

      형님..멍어진 개발은 머에요??ㅋㅋㅋ 아놔~ 오타 작렬^^;;;
      아직 늦지 않았습니다^---^ 천직은 버릴수 없다고 하죠..ㅋㅋ

  3. 2009/12/03 16:04 Eastwood  댓글주소  수정/삭제  댓글쓰기

    형 저 동섭이에요~
    Dependency Inversion Principle의 개념은 이 포스트를 통해서 제가 이해한 바로는, 하위 계층에 대한 의존성을 '제거'한다는 것이네요, 그런데 왜 제거의 뜻인 Removal 등을 쓰지 않고, '역전'의 뜻을 가진 Inversion 쓸까요?

    • 2009/12/03 16:15 청하 sozu  댓글주소  수정/삭제

      짱좋은 질문입니다..^^
      위의 그림을 보면서 설명 드릴게용~ (그림 두개)
      첫번째 그림에서 Extension Module에서 TimerA쪽으로 화살표가 있습니다. (이는 TimerA를 직접 사용한다는 의미입니다.)
      하지만 두번째 그림에서 Extension Module에서 ITimer으로 화살표가 있고 거꾸로 TimerA가 ITimer쪽으로 화살표가 있습니다. (이는 ITimer를 상속 받아서 TimerA를 구현한다는 의미입니다.)
      즉, 첫번째 그림에서는 TimerA쪽으로 화살표가 있었지만 두번째 해결방법에서는 거꾸로 TimerA가 ITimer쪽으로 화살표가 있지요.
      이것이 바로 의존성을 '역전'시킨다는 의미입니다.^^

      말씀하신데로 의존성을 제거하는 것이 맞습니다. 역전은 어떻게 제거를 했느냐는 의미로 받아드리시면 될듯 하네요~ 충분한 설명이 되었나요?^^ 감사합니다~

  4. 2009/12/04 08:28 박형근  댓글주소  수정/삭제  댓글쓰기

    설명이 깔끔합니다!
    문단마다 소제목을 붙이니까 아주 보기좋군요~
    저도 흉내내 봐야겠습니다.^^

  5. 2009/12/07 00:10 PatternLoader  댓글주소  수정/삭제  댓글쓰기

    의존성과 싸우는 모습이 너무 보기 좋습니다!! 역시 청하님은 멋쟁이세요!!