ABOUT ME

남들보다 한삽 더뜨는 개발자 Email: niketjssun31@gmail.com Tel: 010.9565.3242

Today
Yesterday
Total
  • [Flutter] 관심사가 우선일까, 레이어가 우선일까?
    Flutter/project 2024. 1. 18. 19:34
    레포지토리 패턴으로 구성된 리버팟 아키텍처로 프로젝트를 진행하다가 크게 아키텍처 구조를 갈아엎은 적이 있었다. 이유는 어떤 특정한 기준으로 아키텍처 구조를 잡았다가 볼륨이 커지면서 레어어간의 질서를 깨뜨리는 도메인들이 등장했기 때문이었다. 개발자라면 한 번쯤 깊게 고민해 봤을 법한 주제인 거 같아 실제 프로젝트를 하면서 느꼈던 것들을 글로 남겨보려고 한다.

     

     

    "해당 글의 내용은 프로젝트는 레포지토리 패턴으로 구성된 리버팟 아키텍처를 배경으로 하고 있습니다. 특정 아키텍처 패턴에 종속된 주제는 아니지만  리버팟 아키텍처에 대해 배경지식이 필요하다면 아래 글을 참고해 주세요"

     

     

    [Flutter] Repository pattern + Riverpod feat.안드레아

    로그인 회원가입 등 구현에 대해 이야기하기 전, 아키텍처에 관해 이야기를 먼저 해야 구현 부분을 매끄럽게 이어갈 수 있겠다고 생각했다. 아키텍처에 대해 진지하게 생각한 것은 한 백엔드

    nomal-dev.tistory.com

     

     

    먼저 아키텍처 구축 초기단계에서, 관심사에 어떤 걸 두어야할 지 고민해 보는 시간이 필요합니다.

     

    우리는 종종 Screen == 관심사라고 착각하기가 쉽습니다. 저 역시 가장 처음 시작한 것이 퍼블리싱작업이었기 때문에 작업중인 화면의 역할에따라 그뒤에 이어지는 작업들도 화면이 비슷하다싶으면 같은 관심사로 분류했었죠.

     

    하지만 이것이 잘못된 분류 방법이란 걸 프로젝트를 반절 진행하고 나서야 깨닫게 되었습니다. 🥲

     

    ✓ 화면 단위가 관심사가 되어서는 안 된다

     

    [Project] 맛있는 외식의 시작, '미리'

    ✓ What Project? "지역 소상공인 식음료매장들의 당일 재고 상품을 파격적인 할인가로 제공하는 O2O 커머스 서비스" 코로나로 인해 전국의 학교들이 비대면수업을 하면서 지역 경제가 침체되던 때

    nomal-dev.tistory.com

     

    '미리' 앱을 보면 쿠폰을 발행을 할수있는 기능이 존재하며 쿠폰의 종류는 다음과같습니다.

    1. 가맹점에서 발행한 쿠폰
    2. '미리'에서 직접 발행한 이벤트성 쿠폰

    '가게 쿠폰' 다운로드와 '미리 쿠폰' 다운로드

     

    가맹점에서 발행한 쿠폰은 '가게목록 > 가게 상세페이지 > 가게 쿠폰 화면'에서 다운로드가 가능하며, 미리에서 발행한 쿠폰은 'My > 내 쿠폰함 > 미리쿠폰 화면'에서 다운로드가 이루어집니다.

     

    화면단위로 관심사를 만든다면 '가게'라는 관심사 안에 가게 쿠폰화면, 쿠폰 다운로드 로직이 있게 되고 'My'라는 관심사안에 미리쿠폰화면, 쿠폰 다운로드 로직이 존재하게 됩니다.

    하지만 쿠폰을 다운로드하는 화면과 쿠폰 다운로드 로직은 두 관심사안에 모두 동일하게 구성되어 있기 때문에 코드 중복을 피하기 위해 보통 Common이라는 공용 관심사를 만들어 관리하게 되죠.

     

    이러한 케이스가 많이 일어나지 않는 작은 프로젝트면 위와 같은 방식으로 구성한 뒤, 예외 케이스라 생각하고 개발을 진행할 수 있습니다.

    하지만 볼륨이 조금이라도 커지게 된다면? 각각의 관심사에 있는 파일보다 Common 폴더에 있는 파일이 훨씬 많이 생성되는 것을 나중에 체감하게 될 것입니다.

     

    이제 관심사를 화면단위로 했을 때의 위험성을 알았습니다. 그럼 어떻게 구성해야 할까요?

     

     먼저 관심사가 무엇인지 짚고 넘어가야합니다.

     

    📌 관심사란?

    • 소프트웨어 설계에서의 특정 관점이나 고려해야 할 주제를 어떠한 기준으로 나누어 모듈화 된 것

    관심사를 나누는 기준은 정말 다양하다.

     

    비슷한 기능을 하는것끼리 묶으면 기능 기준, 특정 성능(ex.최적화, 사용자 경험 관련 등)을 기준으로 묶으면 성능기준, 특정 분야나 주제에 집중하는 도메인 기준 등이 있는데 사람들이 흔히 사용하는 도메인 기준으로 알아보겠다.

     

    📌 도메인이란?

    • 해결하고자 하는 문제의 영역
    • 비즈니스나 문제 해결을 위한 주제

    위의 케이스에선 '쿠폰 다운로드'라는 기능이 가게와 MY에 중복된다.

    가게라는 도메인과 MY라는 도메인에서 쿠폰의 경우는 부가적인 기능을 할 뿐,

    각각의 도메인의 주제와 연관성이 많이 떨어진다.

     

    도메인은 해결하고자 하는 문제의 영역이라고 했다.

    만약 '쿠폰'이라는 도메인이 있다면 '쿠폰 다운로드'라는 기능은 쿠폰이라는 해결하고자 하는 문제의 영역에 자연스럽게 포함시킬수있다.

    그렇기 때문에 해당 케이스에서는 '쿠폰'이라는 별개의 도메인을 두는 게 올바른 선택이라고 볼 수 있다.

     

     

    🤷🏻‍♂️ "그럼 MY는 도메인이 아니지 않나? MY는 screen 단위잖아"

     

    이런 케이스에서는 내가 해결하고자하는 문제의 영역을 어떻게 받아드리냐에따라 도메인을 나누는 기준을 조금 러프하게 가져가는게 맞다고 생각했다.

    MY (마이 페이지 화면)

     

    위를 보고 도메인을 어떤식으로 나눌것인지 감이 잡히는가?

     

    MY 화면을 보면 유저 정보 수정, 쿠폰, 포인트,내 리뷰, 앱 설정, 공지사항, 앱정보 등을 각각 도메인으로 두고 관심사를 나눌수도 있다.

     

    하지만 내 리뷰를 제외한다면 도메인은 여러개가 생기지만 도메인 당 차지하는 파일의 갯수가 매우 적어서

    "관심사를 세밀히 나누는게 오히려 오버헤드일 수도 있을 것이다"라는 생각을 했었다.

     

    MY는 스크린 단위이면서, 해당화면에서만 존재하는 작은 기능들을 포함하는 도메인의 역할로도 사용할수있다고 판단해서 MY라는 관심사를 그대로 사용하기로 했다.

     

    무엇이든 본인 상황에 딱 맞게 적용하기는 어렵다. 그렇기때문에 경우에따라서 이렇게 유연하게 대처하는 것도 좋다고 생각한다.

     

     

    하지만 이렇게 구성해도 또 다른 고민거리가 생기게 된다.

     


    ❗️지금부터 관심사는, 도메인 기준으로 나눈 경우를 베이스로 글이 진행됩니다.


     

     

    ✓ 관심사가 먼저일까, 레이어가 먼저일까?

    관심사는 이제 어떤 식으로 나눠야 하는지 감이 잡혔다. 그럼 관심사 안에 동일한 레이어를 둘지, 레이어 안에 동일한 관심사를 둘 지 어떤 프로젝트 구조가 맞는가에 대한 고민이 생긴다. 무슨 의미인지 다음 도메인을 기준으로 관심사를 나눈 두 케이스를 보며 알아보자. 

    concern-first

    도메인 안에 레이어를 둔 경우

     

    관심사를 레이어보다 상위 폴더에 두는 경우에는  관심사 간에 독립성이 강화되므로 관심사별로 팀프로젝트를 진행하는 경우 강점을 보인다. 이렇게 모듈화가 가능하다는 특성이 있기 때문에 대규모 프로젝트에 적합하다.

     

    예를 들어 커머스 앱을 만드는 회사라면 검색팀과 결제팀을 명확히 나눌 수 있다.(물론 대기업에서는 각각 다른 서버를 사용하겠지만)

    하지만 모듈화를 명확하게 하지 못하는 경우에는 오히려 단점으로 부각될 수 있다.

     

     

    📌 서로 연관된 기능을 가진 관심사가 각각 모듈화가 되면 의존성이 높아져 문제점이 발생할 수 있다.

    만약 커머스앱의 결제팀과 주문팀이 있고 유저가 주문을 취소를 할 때 사용한 쿠폰과 포인트를 다시 살리는 로직이 필요하다고 가정해 보자.

     

     

    ✍🏼 서버 사이드에서 발생할 수 있는 문제점

     

     첫 번째로, 주문을 롤백한 후에 결제를 취소할 것인지, 결제를 취소한 후에 주문을 롤백할 것인지'와 같이 어떤 순서로 롤백을 해야 하는가에 대한 문제가 발생한다. 

     

     두 번째는, 사용한 쿠폰과 포인트는 어느 부서가 롤백할 것인가에 대한 문제점이 있다. 첫 번째와 마찬가지로 언제 롤백을 하는 게 가장 적합한지 프로젝트의 환경과 상황에 따라 달라질 수 있다.

     

     

    ✍🏼 클라이언트 사이드에서 발생할 수 있는 문제점

     

    결제팀과 주문팀이 나누어져 있지만 Presentation layer에 결제와 주문이 공존해야 하는 경우가 있을 수 있다.

    위의 화면은 결제-주문이 성공적으로 이루어졌을 때 혹은 실패했을 때의 상황을 알려주는 부분이다.

    UI상으로는 결제 유무만 보여주는 것처럼 구성되어 있지만 사실은 결제 중에 문제가 생겼거나 주문 중에 문제가 생긴 경우를 모두 포함한다.

    그렇다고 Common으로 빼기엔 명확하게 한 케이스(결제, 주문)에서만 중복되기 때문에 오버헤드라고 느낄 수 있다.

     

     

    📌 특정 관심사의 내부 코드가 다른 관심사의 코드에 사용될 수도 있다.

    회원가입 화면과 정보수정 화면

     

    바로 위에서 다뤘던 이슈와 비슷한 맥락이다.

    '미리'앱을 보면 회원가입 화면에 있는 유저 전화번호 인증 로직이  MY > 정보수정 화면의 전화번호 변경 기능에서 그대로 사용되는 케이스가 있다.

    만약 회원가입이라는 관심사 안에 전화번호 인증 로직을 작성했다면, 'MY'라는 관심사에서 회원가입 내부 코드를 끌어다 쓰는 격이 될 수 있다. 이렇게 관심사 간의 경계를 무너뜨리고 싶지 않다면 중복코드 또는 파일을 구성해서 사용하는 수밖에 없다.

     

     

    📌 통합 테스트가 어려워진다.

    통합 테스트는 각각의 기능 테스트가 선행된 후에 진행된다. 따라서 전체 시스템이 완성된 후에 모든 기능 간의 통합 테스트가 가능하므로 테스트의 어려움을 초래할 수 있다.

     

    결제-주문과 같이 로직의 순서가 고정된 경우에도 반드시 결제 테스트가 성공해야지 주문이 이루어지기 때문에 결제 부분의 테스트가 이상이 없어야 '주문' 단위 테스트가 가능하다는 불편함도 존재한다. 

     

     

    Layer-first

    레이어 안에 도메인을 둔 경우

     

    레이어를 기준으로 관심사를 나누면 관심사를 레이어 별로 유연하게 둘 수 있다는 장점이 있다.

    또한 각 계층 간에 역할이 명확히 분할되어 있기 때문에 특정 계층을 수정할 때 전체 시스템을 변경할 필요가 없다는 장점이 있다.

    이러한 특징 때문에 계층단위의 유닛 테스트를 수행하는 데 있어 강점을 보인다.

    하지만 layer-first도 다음과 같은 단점이 존재한다.

     

     

    📌 개발자 간의 협업이 비교적 힘들어진다.

    협업을 할 때 보통 관심사 하나를 가지고 api 연동부터 위젯 제작까지 이루어지는 게 일반적이다.

    하지만 관심사가 계층 간에 통일성이 떨어지는 방법인 만큼 분업의 과정이 힘들 수도 있다.

     

     

    📌 개발의 피로도가 증가한다.

    concern-first의 경우에는 한 폴더 안에서 해당 기능에 대한 개발이 모두 이루어지기 때문에 비슷한 작업의 파일들을 찾는데 큰 어려움이 있진 않다.

    하지만 레이어를 기준으로 관심사를 나누게 된다면 api연동할 때는 데이터 레이어의 해당 관심사를 찾고, 모델링을 위해선 도메인 레이어에서 파일을 생성하는 등 상위 폴더를 기준으로 하는 이동이 매우 잦기 때문에 개별 기능에 대한 작업을 하는 과정이 복잡해진다. 

     

    어떤 것 하나 우세한 것이 아닌 두 방법론 모두 장단점이 존재한다. 우리는 어떤 방식으로 프로젝트를 구성하는 게 좋은 것일까?

     

    내가 프로젝트를 진행할 때 많은 인사이트를 주셨던 CodyYun님과 함께 아키텍처에 대해 이야기를 하다가 다음과 같이 말씀하신 적이 있다.

    출처: 코드팩토리 오픈채팅방

     

    아키텍처는 하나의 방법론에 불과하기 때문에 꼭 어떤 것을 정확하게 적용시킬 필요도 없고 그게 잘 안된다고 해서 스트레스를 받을 필요도 없다. 아까 관심사를 나누는 기준에도 상황에 맞게 유연하게 한 것처럼 팀의 상황과 프로젝트의 볼륨에 따라 프로젝트 구조를 유연하게 맞춰가면 되는 것이다.

     

    아키텍처에 정답이 없는 것처럼 프로젝트 구조에도 정답이 없다.

     

     

    Layer-First에 기반한 나만의 프로젝트 구조

    관심사는 고정되어있지 않다. 만약 설계와 개발이 동시에 진행 중인 프로젝트라면 새로운 관심사가 등장할 수도 있고 기존의 기능이 계속 수정돼서 어쩔 수 없이 관심사를 분할하는 과정이 필요할 수도 있다. 

     

    하지만 레이어는 항상 4개로 고정되어 있다. 아까 layer-fisrt를 소개할 때 '관심사를 레이어 별로 유연하게 둘 수 있다'라고 말했었다. 이 말은 즉슨, 새로운 관심사를 추가할 때 수평적으로 확장이 가능하단 것이다.

     

    동일한 기능에 대해 도메인 서버와 클라이언트의 관심사가 다른 경우가 있다고 생각해 보자.

     

    '미리'서버의 경우에는 쿠폰 목록 조회 기능이 '가게'라는 관심사와 '유저'라는 관심사에 각각 별개로 존재한다.

    하지만 우린 이 글이 시작할 때쯤에 이러한 상황엔 '쿠폰'이라는 관심사를 두는 게 좋을 거 같다고 판단했었다.

     

    '쿠폰'은 앱 클라이언트에만 존재하는 관심사다. 그러므로 사실 '쿠폰'은 데이터 레이어와 별 관련이 없으며 오히려 클라이언트단의 비즈니스 로직이 있는 application 레이어와 관련이 있다.

    그렇다면 다음과같은 구조가 가능해진다.

     

    • Data-Layer
      • api의 베이스 경로를 관심사로 둔다.
      • 관심사: Store, User
    • Application-Layer
      • 비즈니스 로직에 맞게 관심사를 생성한다.
      • 관심사: Coupon

     

    이렇게 데이터 레이어를 베이스 경로에 맞춰 '가게'라는 관심사와 '유저'라는 관심사로 만들고 api를 관리한다면 프로젝트 구조측면에서 좀 더 가시적으로 서버와 분리된 느낌을 줄수있다.

     


     

    다음 시간에는 프로젝트에서 어떤식으로 위의 구조가 적용되고 리버팟 아키텍처를 어떻게 사용 했는지, Data-Layer부터 Presentation-Layer까지 각각의 레이어에 실제 적용 사례와 함께 알아보도록 하겠습니다.

     

    긴 글 읽어주셔서 감사합니다.

     

    이 글이 꼭 정답은 아닙니다. 잘못된 부분이나 부족한 부분을 알려주시면 학습 후 수정하겠습니다.

     

     

Designed by Tistory.