내용

스토어를 사용하는 이유가 없어지는 것 같아요 데이터 관련 비즈니스 로직은 store에 집중시키고 UI 관련한 공통화 필요한 비즈니스로직이 hook에서 처리하는게 현재 프로젝트 구조에 더 맞을 것 같습니다

=> 스토어의 색깔이 희미해진다는 말씀이 공감됩니다. 다만 그렇게 하면 몇가지 우려되는 부분이 있습니다!

첫번째는 전역상태로 관리할 필요가 없는 데이터도 모두 전역 상태로 관리해야합니다. 그리고 그에 따라 큰 도메인을 맡고 있는 product, promotion 등의 store 모듈이 급속도로 커질까 우려됩니다.

두번째는 store 모듈 각각의 크기가 커지면서 price, stock 등의 작은 상태에 대한 비즈니스 로직의 응집성이 떨어질 것 같다는 생각이 듭니다. 이부분은 어떻게 해결하면 좋을지 여쭤보고 싶습니다!

내부에서 포맷팅해서 사용하는 방식은 프론트에서 공통화 처리하기에 좋은 방식이긴 하지만 실 API와 달라 디버깅하는데 어려움이 있고,

=>  저희가 선언한 인터페이스를 엔티티로 사용하고 API 응답을 엔티티에 맞게 포맷팅한다고 하면 외부 인터페이스(API) 이후로 모든 데이터가 저희가 정의한 엔티티 형태로 흘러가서 디버깅이 오히려 쉽지 않을까 생각했습니다. API 에서 문제가 있어 에러가 발생한다면 포맷팅 부분을 바로 보면 될 것 같습니다! 이해를 잘 못해서 혹시 간단한 예시 하나만 들어주실 수 있으실까요??

포맷팅 방식이 백엔드와 크게 다른 환경이 아닌 저희한테는 불필요한 로직이 항상 들어가게 되어 오히려 성능저하가 되지 않을까 하는 생각이 들어 지향하고 싶지는 않아요

=> 맞는 것 같습니다...ㅎㅎ 저희는 API 응답구조 그대로 인터페이스를 사용하고 있어서 전부도 끊고가는 건 필요없는 포맷팅 로직도 과하게 들어가고 작업만 번거로워질 수 있을 것 같습니다. 이건 특정 키의 타입이 프론트에서 원하는 타입과 다르거나( Y/N 과 boolean ), api 구조가 달라지면 컴포넌트를 공통화할 수 있는 경우( 일반상품/전단상품 컴포넌트 ), 또는 의미가 같은데 key와 구조가 다른 경우 ( 페이지네이션 인터페이스 )같이 필요한 경우에 한해서만 끊어서 작업하는 건 어떨까 하는 생각이 듭니다.

그리고 재현님이 생각하는 아키텍처 구조를 저희에게 이해시키기 위해서는 리팩토링과 함께 진행하는 것이 아닌 기존 코드로 디렉토리나 파일을 분류하는 작업을 하는 것이 더 나았을 것 같아요 그렇다면 작업자 외의 사람은 익숙한 코드를 통해 아 이 코드가 이러한 성격을 띄어서 이쪽으로 분류가 되었구나 라고 이해하기 더 쉽지 않았을까 합니다

=> 말씀 정말 감사합니다ㅎㅎ 현재 상태에서 파일만 분류해서 각 역할에 대한 설명을 같이 공유드리는게 빠르게 방향을 잡기도 지선님, 태남님께서 받아들이시기도 훨씬 효과적이였을 것 같습니다! 다음부터는 효과적으로 공유드릴 수 있는 방법에 대해 더 많이 생각해보겠습니다ㅎㅎ

재현님 이 방향성은 product, review같이 주 도메인이 복잡한경우에 괜찮을것 같습니다. 하지만 서비스가 작은 도메인일 경우 이렇게 하위 도메인을 세세하게 끊어버리면 오히려 관리 복잡성이 증대될거라고 생각하여 모든 케이스에서 이 아키텍쳐를 사용할 수 있을까가 조금 걱정되는 부분입니다!

=> 의견 너무 감사드립니다ㅎㅎ 말씀해주신대로 제가 가장 작업하면서 이게 맞나? 의구심이 들었던 부분입니다. 그래서 저도 마지막에 생각을 했던 내용은 세부 도메인 별로 파일/훅을 분리하지 말고 큰 도메인 파일 한개의 내부에 UseCase 단위로 훅을 구현하는게 어떨까 생각했습니다.

산지배송으로 예를 들어보면 훅의 단위는 상품을 주문한다 useOrderProduct 상품 주문을 철회한다 useCancelOrderProduct 같은 느낌입니다. 이렇게 되면 UseCase 에서 단일 도메인이 아닌 여러개의 도메인에 의존하면서 훅의 단위가 커져서 추상화 레벨을 줄일 수 있지 않을까 생각했습니다. (클린/헥사고날 아키텍처에서도 use case를 단위로 service 계층에 구현하는데 제가 이해를 잘 못하고 적용해서 생긴 문제입니다...ㅎㅎ)

ui 로직(dom 조작 및 이벤트 핸들링)은 컴포넌트 내부에서 처리해도 될것 같은데 사실상 usePriceUI도 사용하는곳 컴포넌트는 한곳이나 테스트코드 구현시 모킹할 데이터가 많다고 하여 hooks로 빼신것 같은데 이런 복잡하지 않은 usePriceUI 같은 케이스는 컴포넌트로 이동한다면 모킹 코드가 더 줄어들 수 있다고 하네요! (hooks에 있는 모든 도메인의 ui를 컴포넌트로 옮기라는건 아닙니다)

=> 뷰 로직을 컴포넌트 내부에 넣어도 jest는 jsdom 환경에서 동작이되어서 window 객체는 똑같이 모킹해줘야 한다고 알고있습니다! 그리고 컴포넌트에서 호출하는 비즈니스 로직( const user = useUserStore()) 도 전부 모킹해줘야 컴포넌트 테스트 코드가 정상적으로 동작하기 때문에 모킹 코드가 오히려 늘어날 것 같다는 생각이 듭니다. 혹시 관련해서 테스트코드 예제 한번만 간단하게 공유해주실 수 있으실까요??

두분 말씀주신대로 비즈니스 로직만 정확히 분리된다면 뷰로직은 컴포넌트 내부에서 구현해도 괜찮을 것 같습니다ㅎㅎ 복잡하거나 공통화된 ui 로직( useUIUtils )의 경우에만 분리해서 선택적으로 상요하면 좋을 것 같다는 생각이 듭니다ㅎㅎ

정리

Store vs Hook 역할 분담

데이터 비즈니스 로직은 store, UI 공통 로직은 hook. 단, 전역 상태 불필요한 데이터까지 store에 넣으면 모듈이 비대해지고 응집성이 떨어진다.

데이터 포맷팅 전략

전부 끊는 건 오버엔지니어링. 타입 불일치(Y/N→boolean), 컴포넌트 공통화 가능, 키/구조만 다른 경우에 한해 선택적으로 포맷팅한다.

도메인 크기별 적용

복잡한 도메인(product, review)은 세부 분리 효과적. 작은 도메인은 UseCase 단위(useOrderProduct, useCancelOrderProduct)로 훅을 구현해 추상화 레벨을 줄인다.

UI 로직 위치

비즈니스 로직만 분리되면 뷰 로직은 컴포넌트 내부 OK. 복잡하거나 공통화된 UI 로직(useUIUtils)만 선택적으로 분리한다.

테스트 모킹 이슈

컴포넌트로 옮겨도 jsdom 환경에서 window 모킹 + useUserStore 등 비즈니스 로직 모킹은 동일하게 필요. 모킹 코드가 줄지 않을 수 있다.

프론트엔드 아키텍처 토론 내용 정리: 상태 관리와 비즈니스 로직 분리 전략

이 토론은 프론트엔드 개발에서 스토어(Store)와 커스텀 훅(Custom Hooks), 그리고 컴포넌트 간의 역할 분담과 책임에 대한 깊이 있는 논의를 다룹니다. 특히 데이터 관련 비즈니스 로직을 스토어에, UI 관련 공통 비즈니스 로직을 훅에서 처리하는 구조적 접근 방식과 그에 따른 우려사항들이 주요 쟁점으로 제기되었습니다[1][2][3].

스토어와 훅의 역할 분담 논의

제안된 구조의 핵심 아이디어

토론에서 제안된 아키텍처는 데이터 관련 비즈니스 로직은 전역 스토어에, UI 관련 공통화가 필요한 비즈니스 로직은 커스텀 훅에서 처리하는 구조입니다. 이는 현대 프론트엔드 개발에서 관심사의 분리(Separation of Concerns) 원칙을 적용한 접근법으로, 각 계층의 책임을 명확히 구분하고자 하는 시도입니다[4].

스토어 중심 구조의 우려사항

전역 상태 관리의 과도한 확장: 가장 큰 우려는 전역상태로 관리할 필요가 없는 데이터까지 모두 전역 상태로 관리해야 한다는 점입니다. 이는 Redux와 같은 전역 상태 관리 라이브러리의 일반적인 문제점 중 하나로, 불필요한 보일러플레이트 코드를 증가시키고 애플리케이션의 복잡도를 높일 수 있습니다[5][6].

대형 스토어 모듈의 관리 복잡성: product, promotion 등의 큰 도메인을 담당하는 스토어 모듈이 급속도로 커질 우려가 제기되었습니다. 이는 단일 책임 원칙(Single Responsibility Principle)을 위반할 가능성이 있으며, 코드의 유지보수성을 저하시킬 수 있습니다[7][8].

비즈니스 로직 응집성 저하: 스토어 모듈 각각의 크기가 커지면서 price, stock 등의 작은 상태에 대한 비즈니스 로직의 응집성이 떨어질 수 있다는 우려가 표명되었습니다[9].

데이터 포맷팅 전략에 대한 의견 차이

내부 포맷팅 방식의 장단점

디버깅 효율성: 선언한 인터페이스를 엔티티로 사용하고 API 응답을 엔티티에 맞게 포맷팅하는 방식은 디버깅 시 일관된 데이터 구조를 제공할 수 있다는 장점이 있습니다. 외부 인터페이스(API) 이후 모든 데이터가 정의된 엔티티 형태로 흘러가므로 추적이 용이합니다[3].

성능 오버헤드 우려: 반면 백엔드와 크게 다르지 않은 환경에서는 불필요한 포맷팅 로직이 항상 들어가게 되어 성능 저하를 야기할 수 있다는 우려가 제기되었습니다. 이는 현대 웹 애플리케이션에서 성능 최적화가 중요한 고려사항임을 보여줍니다[5].

선택적 포맷팅 전략

토론에서는 다음과 같은 경우에만 선택적으로 포맷팅을 적용하는 것이 적절하다는 결론에 도달했습니다:

  • 타입 불일치: Y/N과 boolean과 같이 프론트엔드에서 원하는 타입과 다른 경우
  • 컴포넌트 공통화: 일반상품/전단상품 컴포넌트처럼 API 구조가 달라져도 컴포넌트를 공통화할 수 있는 경우
  • 구조적 차이: 페이지네이션 인터페이스처럼 의미는 같지만 key와 구조가 다른 경우

아키텍처 도입 방식에 대한 성찰

기존 접근법의 한계

토론에서는 새로운 아키텍처를 리팩토링과 함께 진행하는 것보다는 기존 코드를 디렉토리나 파일로 분류하는 작업을 먼저 진행하는 것이 더 효과적이었을 것이라는 반성이 있었습니다. 이는 팀원들이 아키텍처의 의도를 이해하기 위해서는 익숙한 코드를 통해 각 역할의 성격을 파악하는 것이 중요함을 시사합니다[10].

점진적 도입의 중요성

아키텍처 변경은 단순한 기술적 결정이 아니라 팀 전체의 이해와 합의가 필요한 과정입니다. 특히 복잡한 개념을 도입할 때는 이론적 설명보다는 실제 코드 예시를 통한 설명이 더 효과적일 수 있습니다[4].

도메인 크기별 아키텍처 적용 전략

복잡한 도메인에서의 효과성

product, review와 같은 주요 도메인이 복잡한 경우에는 제안된 아키텍처가 효과적일 수 있다는 의견이 있었습니다. 도메인 주도 설계(DDD) 관점에서 볼 때, 복잡한 비즈니스 로직을 가진 도메인은 세밀한 구조화가 도움이 될 수 있습니다[3][9].

소규모 도메인의 과도한 복잡성

반면 서비스가 작은 도메인일 경우, 하위 도메인을 세세하게 끊어버리면 오히려 관리 복잡성이 증대될 수 있다는 우려가 제기되었습니다. 이는 아키텍처 적용에 있어서 상황에 맞는 유연한 접근이 필요함을 보여줍니다[8].

UseCase 중심의 대안적 접근

이러한 문제의 해결책으로 UseCase 단위로 훅을 구현하는 방식이 제안되었습니다. 예를 들어:

  • useOrderProduct (상품을 주문한다)
  • useCancelOrderProduct (상품 주문을 철회한다) 이 접근법은 단일 도메인이 아닌 여러 도메인에 의존하면서 훅의 단위를 키워 추상화 레벨을 줄일 수 있는 장점이 있습니다. 이는 클린 아키텍처나 헥사고날 아키텍처에서 UseCase를 단위로 서비스 계층을 구현하는 패턴과 유사합니다[11][8].

UI 로직의 위치와 테스트 전략

컴포넌트 내부 vs 커스텀 훅

단순한 UI 로직의 경우: usePriceUI와 같이 복잡하지 않고 한 곳에서만 사용되는 경우, 컴포넌트 내부로 이동하는 것이 모킹 코드를 줄일 수 있다는 의견이 있었습니다[12][13].

복잡한 UI 로직의 경우: 반대로 복잡하거나 공통화된 UI 로직(예: useUIUtils)의 경우에만 분리해서 선택적으로 사용하는 것이 적절하다는 결론에 도달했습니다[14][15].

테스트 코드 작성의 실제적 고려사항

모킹 코드의 복잡성: 뷰 로직을 컴포넌트 내부에 넣어도 Jest는 jsdom 환경에서 동작하므로 window 객체는 동일하게 모킹해야 합니다. 또한 컴포넌트에서 호출하는 비즈니스 로직(예: const user = useUserStore())도 모두 모킹해야 하므로, 모킹 코드가 오히려 늘어날 수 있다는 실용적 관점이 제시되었습니다[12][16].

테스트 격리의 중요성: React 컴포넌트 테스트에서는 외부 의존성을 적절히 모킹하여 테스트 대상을 명확히 격리하는 것이 중요합니다. 이는 단위 테스트의 핵심 원칙 중 하나입니다[13].

현대 프론트엔드 아키텍처 트렌드와의 연결점

서버 상태와 클라이언트 상태의 분리

현대 프론트엔드 개발에서는 서버 상태와 클라이언트 상태를 명확히 구분하는 것이 중요한 트렌드입니다. React Query와 같은 서버 상태 관리 라이브러리의 등장은 이러한 필요성을 반영합니다[5][10].

비즈니스 로직과 뷰 로직의 분리

토론에서 제기된 비즈니스 로직과 UI 로직의 분리는 MVI(Model-View-Intent) 아키텍처와 같은 현대적 패턴과 일치하는 방향입니다. 이는 뷰와 비즈니스 로직을 완전히 분리하여 변화에 대한 대응력을 높이는 것을 목표로 합니다[17].

점진적 복잡성 관리

현대 프론트엔드 아키텍처는 거대한 구조보다는 작고 간단한 형태부터 시작해 점진적으로 복잡하게 발전시킬 수 있는 방향을 선호합니다. 이는 팀의 학습 곡선과 유지보수성을 동시에 고려한 실용적 접근법입니다[1][10].

결론 및 권장사항

  1. 기존 코드 분류
  2. 도메인별 전략 결정
  3. UseCase 단위 설계
  4. 선택적 추상화 적용
  5. 테스트 구조 설계

이 토론은 프론트엔드 아키텍처 설계에서 마주하는 현실적인 고민들을 잘 보여줍니다. 완벽한 아키텍처는 존재하지 않으며, 팀의 규모, 프로젝트의 복잡성, 도메인의 특성을 종합적으로 고려한 상황에 맞는 선택이 중요합니다.

핵심 권장사항:

  1. 점진적 도입: 새로운 아키텍처는 기존 코드 분류부터 시작하여 점진적으로 적용
  2. 도메인별 맞춤 전략: 복잡한 도메인과 단순한 도메인에 각기 다른 접근법 적용
  3. UseCase 중심 설계: 세부 도메인별 분리보다는 UseCase 단위의 응집성 있는 설계 고려
  4. 선택적 추상화: 모든 로직을 분리하기보다는 실제 공통화와 재사용성을 기준으로 선택적 적용
  5. 실용적 테스트 전략: 이론적 완벽성보다는 실제 유지보수와 개발 효율성을 고려한 테스트 구조 설계 프론트엔드 아키텍처는 지속적으로 진화하는 영역입니다. 중요한 것은 완벽한 구조를 만드는 것이 아니라 팀이 이해하고 효과적으로 활용할 수 있는 실용적이고 진화 가능한 구조를 만드는 것입니다[17].

출처 [1] 프론트엔드 생태계의 상태 관리 패러다임 변화 - Jaehyeon's Dev Blog https://jaehyeon48.github.io/web/frontend-state-management-paradigm/ [2] 쇼핑몰 앱에서 Redux를 이용한 상태 관리 (1) - 개발해도유 https://doyu-l.tistory.com/395 [3] 프론트엔드에서 Domain-Driven Design(DDD) 적용하기 - coin FE https://asdf1326.tistory.com/45 [4] 프론트엔드 아키텍처의 이해: 관심사의 분리 - F-Lab https://f-lab.kr/insight/understanding-frontend-architecture [5] 프론트엔드 상태 관리 최적화하기: React Query와 커스텀 훅 활용 전략 https://blog.riido.io/optimizing-frontend-state-management-strategies-using-react-query-and-custom-hooks/ [6] [React] Redux vs React Hooks - velog https://velog.io/@ykh0316/React-Redux-vs-React-Hooks [7] [React] 상태 관리 라이브러리에 대한 고찰 (feat. redux-tookit, recoil ... https://yong-nyong.tistory.com/94 [8] React 프로젝트 구조 설계를 넘어서 프론트엔드 아키텍처 설계에 대한 ... https://xiubindev.tistory.com/142 [9] DDD(Domain Driven Design) - velog https://velog.io/@icyeong/DDDDomain-Driven-Design [10] Global State Management with React Hooks - Academind https://academind.com/tutorials/global-state-management-with-react-hooks [11] 웹 프론트엔드 클린 아키텍쳐(Clean Architecture) 의 역사 https://flowersayo.tistory.com/148 [12] React 테스트 코드에서 Mocking 사용하기(1) - 백엔드 API 요청 모킹하기 https://cheeseb.github.io/testing/react-testing-mocking/ [13] 테스팅 방안 - React https://ko.legacy.reactjs.org/docs/testing-recipes.html [14] Advanced React Hooks: Custom Hooks for Complex State ... https://codedamn.com/news/reactjs/advanced-react-hooks-custom-hooks [15] State Management and React Hooks: A Perfect Pair https://blog.pixelfreestudio.com/state-management-and-react-hooks-a-perfect-pair/ [16] 실용적인 리액트 테스트 전략 - velog https://velog.io/@sdong001/실용적인-리액트-테스트-전략 [17] 프론트엔드에서 비즈니스 로직과 뷰 로직 분리하기 (feat. MVI 아키텍쳐) https://velog.io/@teo/MVI-Architecture [18] 복잡한 애플리케이션을 위한 프론트엔드 아키텍처 - 펼치는 공간 https://de-velop-humus.tistory.com/32 [19] 도메인 주도 설계(DDD) https://go-gradually.tistory.com/m/entry/도메인-주도-설계DDD의-핵심-개념-정리 [20] React환경에서 Hooks로 Redux 사용 하기 https://changhoi.kim/posts/etc/react-redux-hooks/ [21] [React] Redux와 Redux Toolkit 비교 및 구현 방법 - Develog - 티스토리 https://daunje0.tistory.com/197 [22] Create custom hooks state in react and Manipulate global state from ... https://stackoverflow.com/questions/63301453/create-custom-hooks-state-in-react-and-manipulate-global-state-from-everywhere-w [23] Context over a custom hook? Where to store global state? : r/reactjs https://www.reddit.com/r/reactjs/comments/ol02tt/context_over_a_custom_hook_where_to_store_global/