ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [디자인패턴 변환 코드 리팩토링 - 도입배경 및 고민사항들]
    취준(자소서,면접)/프로젝트 정리 2024. 7. 31. 14:01

     

    * 내가 생각해본 도입배경 (MVVM -> RIBs)

    1. MVVM 패턴을 도입한 이유

         * 기존소스의 문제점:  VC가 너무 많은 역할을 하고있다. 역할 분리가 하나도 안됨.

              API호출, 비즈니스로직, UI관련 작업이 전부 들어있음. 주식현재가 ViewController만 4800줄

          -> 1) 소스파악: 운영업무는 잘못된 부분을 빠르게 확인해서 로직을 파악해야하는데, 

                                  특정 비즈니스로직을 확인하려면  VC를 전부 뒤져야함.

              2) 수정범위: 특정 로직에 수정요청이 오면 해당 로직을 사용하는 모든 화면을 찾아서 수정해야함.

                ex) 주식매매부적합 확인 로직은 주식/elw/rp등 모든 상품매매에 적용되는데 이걸 매 화면마다 구현해주니 수정범위가 넓어지고,

                       누락될 가능성도 크다

         

         

         [Step1: MVVM]

         ViewModel을 도입해서 최소한 UI부분과 비즈니스 로직부분은 나누어보자.

         But, 단순하게 데이터 조회 > 뷰에뿌려주는 조회성 화면은 MVVM패턴으로 충분하나

         로직이 복잡한 화면은 여전히 ViewModel이 많은 역할을 하고있음. 

         데이터 조회, 신청 처리 전문 호출 뿐 아니라 이를 위한 비즈니스 로직까지 여전히 ViewModel 한군데서 호출하고 있어

         2)의 문제가 해결되지 않음.   기능 분리가 필요하다고 느낌

         

     

         [Step2: MVVM + Service + Repository]

          -> 비즈니스 로직만을 담당하는 Service 계층 + 데이터 조회,처리 로직 Repository 계층

           ex) 간편송금 화면

                        - Service에는 자주쓰는계좌 조회, 한도조회, 서비스 등록/해지 등.. 실제 비즈니스 처리 로직이 담겨있고

                        - Repository에는 비즈니스 처리를 위한 TR호출 함수를 정의 (외부로의 접근)

       

         -> Repository를 분리하니 TR수정 요청 시 한군데만 수정해도 됨

     


         
    **여기서 이해 어려운점. 클린 아키텍쳐 도입이 필요한 이유!!! 이해가 잘 안된다. MVVM+S+R으로도 충분하지 않은가?

     

         하지만 이 패턴에도 단점이 있는데, ViewModel과 S/R 이 밀접하게 연결되어 

    Service의 수정이 이루어지면 ViewModel도 함께 수정되어야 하는 경우가 있다.

    클린아키텍쳐는 의존성역전원칙을 완벽하게 구현하여, 하위레이어의 수정이 상위레이어에 전혀 영향을 미치지 않는다.

      

     

         [Step3 아키텍쳐 변환 (Clean Architecture, RIBs 등)

     

          >> 다음 글에 이어서 작성

     

     

     

     

     

     


     

     

    Q. RIBs를 도입한 이유?

    단순하게 데이터 조회 > 뷰에뿌려주는 조회성 화면은 MVVM패턴으로 충분하다.

    하지만

    1. 주문, 이체 등 비즈니스로직이 복잡하게 얽힌 화면들은 MVVM의 ViewModel이 결국 거대해지는 Massive ViewController와의 유사한 문제가 발생한다고 느낌.

     2. 하나의 View와 ViewModel이 강하게 결합되어 있으며, 모듈화가 제한적임.

     따라서 하나의 ViewModel을 다시 기능별로 Service로 분리하거나 역할별로 ViewModel로 구상하려 하니 파일이 많아짐..

    비대해진 ViewModel을 여러 객체로 나누다 보니 결국 RIBs와 비슷한 형태

    >> 각 객체간의 역할이 명확하게 분리된  Clean Architecture의 필요성을 느꼈다

    3. 유지보수 측면 - 모듈화가 이루어지지 않은 코드는 가독성이 떨어질 뿐 아니라, 유지보수 관점에서 어려운점이 많음

      1) 코드의 재사용 - ViewController에 로직이 섞여있어 매 화면마다 동일한 로직을 구현해주어야 함.

         ex) 투자자성향에 따라 매수제한 로직이 있는데  k-otc매매든, elw매매든 모든 매매 프로세스에는 동일하게 적용되나

           모듈화가 이루어지지 않으니 매 화면에 이를 구현함

     2) 수정 범위 (영향도) - 위와 같은 문제로, 만일 투자자성향을 조회해오는 전문에 IO가 바뀌었을때 매 화면마다 이를 수정해주어야 함.

       수정 범위가 넓어질 뿐 아니라, 누락될 가능성도 큼

     3) 가독성 - 운영업무 특성 상 고객센터에서 문의가 왔을때 즉각 응대해야 하는 경우가 많음.
     빠르게 코드를 확인해야 하는데 방대한 VC는 기존 로직을 빠르게 파악하는데 많은 어려움이 있음.

     

     


     

     
     
    Q. VIPER, MVP,   Clean Architecture 등 여러 디자인패턴이 있는데 왜 RIBs인가?
     
    ~~~~~~~
     
     

     
     
    • 코드의 모듈화가 잘 이루어져, 새로운 기능을 추가하거나 기존 기능을 수정할 때 영향을 최소화할 수 있습니다. 
    • 특정 기능이나 모듈을 독립적으로 교체하거나 제거하기 쉬워집니다.
      >> 모든 매매 프로세스에 공통적으로 적용되는 부적합판단 로직을 UseCase로 구현해두면 
       수정 요청이 왔을때 해당 파일만 수정하면 되므로 영향도 최소화 가능
      >> 새로운 기능 추가 시 기존 코드를 최소한으로 수정하면서 확장이 가능

     

    Q. RIBs 를 사용했을때의 단점

     

    1. 복잡한 코드 구조

    1) 작은 기능을 구현하기 위해서도 여러 개의 파일을 생성하고 관리해야 하며, 각 컴포넌트 간의 상호작용이 많아질수록 코드가 비대해짐
      ex. 매 화면 전환마다  각 화면에서 router를 구현

    2) 관리해야 할 파일과 클래스의 수가 늘어나서 코드의 가독성과 유지보수가 어려워질 수 있음

     

    2. 디버깅의 어려움

    RIBs의 각 컴포넌트가 독립적이긴 하지만, 그만큼 컴포넌트 간의 상호작용이 많아 디버깅과 테스트가 복잡할 수 있습니다. 문제가 발생했을 때, 여러 RIB 간의 상호작용을 추적하는 데 어려움이 있을 수 있습니다.

     


     

     

    Q. RIBs 적용과정의 고충

    1. 기존 View계층구조에 Router에서 반환한 ViewController 클래스를 결합하는 문제 (뷰 간 이동)

      ex) 기존의 ViewController 이동 체계는, VC string 이름으로 받아서 화면을 전환하는데 router에서는 vc자체를 반환.. 이걸 기존의 VC이동 체계에 접합시키는데 골치아픔. 객체 init시점 문제 때문인지, builder 어디선가 초기화돼서 listener nil 되는 현상

     

     

    2. 엄격한 모듈화와 계층 구조를 이해하기 어려웠음

      ex) Presenter에서 발생한 이벤트 중 화면 전환을 굳이 router에서 구현해야 하는 이유?

      >> GPT의 답변

    더보기

    didSelectTab에서 바로 attachHome 같은 함수를 구현하는 대신, 이를 router를 통해서 호출하는 이유는 RIBs 아키텍처의 원칙과 모듈성, 코드의 책임 분리를 유지하기 위해서입니다. 다음은 그 이유를 설명하는 몇 가지 중요한 원칙과 장점입니다.

    1. 책임 분리 (Separation of Concerns)

    RIBs 아키텍처는 각 구성 요소가 특정 역할과 책임을 가지도록 설계되어 있습니다.

    • Interactor: 비즈니스 로직을 관리하고, 뷰와 상호작용을 담당합니다.
    • Router: RIB 간의 네비게이션 및 라우팅을 관리합니다.
    • ViewController: UI와 관련된 작업을 담당합니다.

    Interactor에서 직접 UI 관련 작업이나 네비게이션을 처리하면, Interactor가 너무 많은 책임을 가지게 되어 코드가 복잡해지고 유지보수가 어려워집니다.

    2. 모듈성 (Modularity)

    RIBs는 모듈성 높은 코드 구조를 지향합니다. 즉, 각 구성 요소가 독립적으로 테스트 가능하고, 변경이 용이해야 합니다. 네비게이션 로직을 Router에 분리하면 Interactor는 순수하게 비즈니스 로직에만 집중할 수 있고, 네비게이션 로직이 변경되더라도 Interactor에는 영향을 미치지 않습니다.

     

    3. 테스트 용이성 (Testability)

    각 구성 요소가 분리되어 있으면, 단위 테스트가 훨씬 용이해집니다. 예를 들어, Interactor에서 비즈니스 로직을 테스트할 때, 네비게이션 로직이 포함되어 있지 않으므로 테스트가 단순해집니다. 반대로, Router의 네비게이션 로직을 테스트할 때도 Interactor의 비즈니스 로직과는 독립적으로 테스트할 수 있습니다.

     

    4. 유지보수성 (Maintainability)

    코드를 분리하면 유지보수성이 높아집니다. 구성 요소가 명확한 책임을 가지므로, 특정 기능을 변경하거나 확장할 , 해당 기능과 관련된 구성 요소만 수정하면 됩니다. 다른 구성 요소에 영향을 미치지 않으므로 코드 변경이 안전해집니다.

     

     


    3.  PresentableListner를 통해 받은 UI액션

    1) 상위Interator에서 PresentableListner를 상속받아 바로 구현할 것인가 ?

    2) 하위RIB Listener 한번 프로토콜을 구현하고 이를 상속받아 사용할것인가? 


    더보기

      >> 두 방법의 선택 기준

    • 간단한 프로젝트 또는 간단한 이벤트 처리: SubmitWarningPresentableListener를 바로 상속받아 구현하는 방법이 좋습니다. 코드가 간결해지고, 구현이 쉬워집니다.
    • 복잡한 프로젝트 또는 다수의 RIBs가 연관된 이벤트 처리: SubmitWarningListener와 같은 추가적인 프로토콜을 사용하는 것이 장기적으로 더 유리할 수 있습니다. 이는 시스템의 유연성과 유지보수성을 높여줍니다.

     

    SubmitWarningPresentableListener를 SubmitInteractor에서 상속받아 직접 구현하는 방법은 간단한 구현을 원하는 경우 매우 유용합니다. 하지만, 장기적인 유지보수나 확장 가능성을 고려한다면, SubmitWarningListener를 통해 부모와 자식 RIB 간의 통신을 명확히 분리하는 방법도 고려해볼 만합니다. 어떤 방법을 선택하든, 프로젝트의 요구 사항과 규모에 따라 적절한 방식을 선택하는 것이 중요합니다.

     

     

    장점:

    1. 모듈화: SubmitWarningListener를 사용하면 각 RIB가 서로 독립적으로 동작하며, 인터랙터 간의 결합도가 낮아집니다. 이는 특히 대규모 프로젝트에서 코드 유지보수에 도움이 됩니다.
    2. 확장성: 인터랙터 간의 통신을 명확히 분리하여, RIBs 간의 통신을 더 확장하고 다양한 상황에 대응할 수 있습니다.
    3. 코드 구조의 명확성: 부모와 자식 RIB 간의 역할이 명확하게 분리되어 코드의 가독성과 유지보수성이 향상됩니다.

    단점:

     

    1. 추가적인 프로토콜과 구현이 필요하여 코드가 길어질  있습니다.

    2. 복잡 증가: 작은 규모의 프로젝트나 간단한 기능에서는 불필요하게 복잡해질  있습니다.

     




     
     
     
     
     
     
     
     
    ================= 아래는 ChatGPT 발췌 ==============

    MVC 패턴으로 짜여진 레거시 프로젝트의 일부를 RIBs 아키텍처로 전환하는 것은 포트폴리오에 도움이 될 수 있습니다. 특히, 아키텍처 전환과 같은 작업은 문제 해결 능력과 최신 기술 도입에 대한 관심을 보여줄 수 있습니다. 그러나 MVC와 RIBs가 혼합될 때 발생할 수 있는 단점도 고려해야 합니다. 여기 몇 가지 고려 사항과 추천 사항이 있습니다:

    포트폴리오에 도움이 되는 이유

    1. 문제 해결 능력: 기존의 문제를 해결하기 위해 새로운 아키텍처를 도입하고, 이를 성공적으로 적용한 사례를 보여줄 수 있습니다. 이는 문제 해결 능력과 혁신적인 사고를 보여줍니다.
    2. 최신 기술 사용: RIBs와 같은 최신 아키텍처를 도입하여 프로젝트의 품질을 향상시키려는 노력을 보여줄 수 있습니다. 이는 최신 기술을 배우고 적용하는 능력을 강조할 수 있습니다.
    3. 코드 품질과 유지보수성: 모듈화된 구조를 통해 코드 품질을 개선하고, 유지보수성을 향상시킨 경험을 강조할 수 있습니다. 이는 장기적인 코드 관리 능력을 보여줍니다.
    4. 팀 협업 능력: 아키텍처 전환 과정에서 팀과 협업한 경험을 설명할 수 있습니다. 이는 협업 능력과 커뮤니케이션 스킬을 강조할 수 있습니다.

    혼합의 단점

    1. 복잡성 증가: MVC와 RIBs가 혼합되면 프로젝트의 복잡성이 증가할 수 있습니다. 특히, 두 아키텍처 간의 상호작용과 데이터 흐름을 관리하는 것이 어려울 수 있습니다.
    2. 일관성 부족: 아키텍처의 일관성이 부족해지면, 새로운 팀원이 프로젝트를 이해하고 유지보수하는 데 어려움을 겪을 수 있습니다.
    3. 전환 비용: 부분적인 전환에도 불구하고, 아키텍처 전환에는 시간과 리소스가 필요합니다. 이는 프로젝트의 다른 부분에 영향을 미칠 수 있습니다.

    추천 사항

    1. 명확한 경계 설정: RIBs로 전환할 부분과 MVC로 남길 부분의 경계를 명확히 설정하세요. 이로 인해 혼란을 최소화할 수 있습니다. 예를 들어, 새로운 기능이나 모듈에만 RIBs를 적용하고, 기존의 안정된 부분은 그대로 유지하는 방법이 있습니다.
    2. 도입 계획 수립: 단계별로 RIBs를 도입하는 계획을 수립하세요. 이는 전환 과정의 복잡성을 줄이고, 팀원들이 점진적으로 새로운 아키텍처에 익숙해지도록 도울 수 있습니다.
    3. 문서화: 아키텍처 전환 과정과 결정 사항을 문서화하세요. 이는 프로젝트의 일관성을 유지하고, 팀원들이 새로운 아키텍처를 이해하는 데 도움이 됩니다.
    4. 테스트: 전환 과정에서 충분한 테스트를 통해 새로운 아키텍처가 기존 기능에 영향을 미치지 않도록 하세요. 이는 코드의 안정성을 유지하는 데 중요합니다.

    포트폴리오 작성 팁

    • 프로젝트 개요: 기존 프로젝트의 문제점과 이를 해결하기 위해 RIBs를 도입한 이유를 설명하세요.
    • 전환 과정: 아키텍처 전환 과정과 사용한 방법론을 상세히 설명하세요. 예를 들어, 어떤 부분을 RIBs로 전환했는지, 왜 그 부분을 선택했는지 등을 포함하세요.
    • 성과: 전환 후의 성과를 강조하세요. 코드 품질, 유지보수성, 성능 등의 측면에서 개선된 점을 구체적으로 설명하세요.
    • 배운 점: 전환 과정에서 배운 점과 어려움을 어떻게 극복했는지 설명하세요. 이는 문제 해결 능력과 학습 능력을 강조하는 데 도움이 됩니다.

    이러한 접근법을 통해 MVC와 RIBs 혼합의 단점을 최소화하면서, 포트폴리오에 강력한 아키텍처 전환 사례를 추가할 수 있습니다.

    댓글

Designed by Tistory.