BangDori 님의 상세페이지[7팀 강병준] Chapter 2-2. 디자인 패턴과 함수형 프로그래밍

주소

https://bangdori.github.io/front_6th_chapter2-2/

과제의 핵심취지

  • React의 hook 이해하기
  • 함수형 프로그래밍에 대한 이해
  • 액션과 순수함수의 분리

과제에서 꼭 알아가길 바라는 점

  • 엔티티를 다루는 상태와 그렇지 않은 상태 - cart, isCartFull vs isShowPopup
  • 엔티티를 다루는 컴포넌트와 훅 - CartItemView, useCart(), useProduct()
  • 엔티티를 다루지 않는 컴포넌트와 훅 - Button, useRoute, useEvent 등
  • 엔티티를 다루는 함수와 그렇지 않은 함수 - calculateCartTotal(cart) vs capaitalize(str)

기본과제

  • Component에서 비즈니스 로직을 분리하기
  • 비즈니스 로직에서 특정 엔티티만 다루는 계산을 분리하기
  • 뷰데이터와 엔티티데이터의 분리에 대한 이해
  • entities -> features -> UI 계층에 대한 이해
  • Component에서 사용되는 Data가 아닌 로직들은 hook으로 옮겨졌나요?
  • 주어진 hook의 책임에 맞도록 코드가 분리가 되었나요?
  • 계산함수는 순수함수로 작성이 되었나요?
  • 특정 Entitiy만 다루는 함수는 분리되어 있나요?
  • 특정 Entitiy만 다루는 Component와 UI를 다루는 Component는 분리되어 있나요?
  • 데이터 흐름에 맞는 계층구조를 이루고 의존성이 맞게 작성이 되었나요?

심화과제

  • 재사용 가능한 Custom UI 컴포넌트를 만들어 보기
  • 재사용 가능한 Custom 라이브러리 Hook을 만들어 보기
  • 재사용 가능한 Custom 유틸 함수를 만들어 보기
  • 그래서 엔티티와는 어떤 다른 계층적 특징을 가지는지 이해하기
  • UI 컴포넌트 계층과 엔티티 컴포넌트의 계층의 성격이 다르다는 것을 이해하고 적용했는가?
  • 엔티티 Hook과 라이브러리 훅과의 계층의 성격이 다르다는 것을 이해하고 적용했는가?
  • 엔티티 순수함수와 유틸리티 함수의 계층의 성격이 다르다는 것을 이해하고 적용했는가?
  • 전체적으로 분리와 재조립이 더 수월해진 결합도가 낮아진 코드가 되었나요?

과제 셀프회고

과제를 하면서 내가 제일 신경 쓴 부분은 무엇인가요?

이전 과제에서 느꼈던 아쉬움을 극복하고 나만의 방향 잡기

  1. 요구사항 분석하기
  2. Top-Down 방식의 설계 + 리팩토링 시도
  3. 나만의 기준 세우기

이전 과제에서는 시간의 부족보다는 내부 구조와 계층에 대한 이해 부족이 더 큰 문제였습니다. 그래서 리팩토링을 하면서도 스스로 설명하기 어렵고, 갈증만 쌓이는 느낌이었습니다. 마치 ‘밑 빠진 독에 물 붓기’ 같았죠.

이번에는 그런 갈증을 해결하고자, 처음부터 Top-Down 방식으로 설계하며 리팩토링을 진행했고, 방향성 설정이나 구조화는 전적으로 제 판단에 따라 진행했습니다. (제 방식이 얼마나 유효한지 검증해보고 싶었습니다.)

NOTE

첫 번째 레슨: 요구사항 분석하기

이번엔 코드보다 먼저 요구사항을 ‘나만의 언어’로 해석하는 것에 집중했습니다. 지금까지의 설계는 대부분 ‘코드를 위한 설계’에 가까웠는데, 준일 코치님께서 말씀하신 **“요구사항이 먼저고, 코드는 그 수단”**이라는 말을 되새기며 시작했습니다.

그래서 아래처럼 요구사항을 직접 정리했습니다. 완벽하게 이해하진 못했지만, 이 과정을 통해 **비즈니스 주체가 되는 엔티티(Cart, Coupon 등)**는 어느 정도 명확히 구분할 수 있었습니다.

screenshot1 screenshot2

NOTE

두 번째 레슨: Top-Down 설계 + 리팩토링 진행

React는 내부 동작이 잘 추상화되어 있어서, 저는 UI와 도메인 중심으로 작업을 진행했습니다. 리팩토링은 아래와 같은 흐름으로 했습니다:

1. 페이지 분리

  • App
    • AdminPage
    • CartPage

2. 상태 좁히기

전역으로 사용되지 않아도 되는 것들은 요구사항에 맞춰서 상태를 좁히기

  • 전역 상태가 아닌 경우 페이지 내부로 이동
  • AdminPage, CartPage 전용 상태/함수는 각 페이지로
  • Notification은 전역 분리

3. 공용 모듈 분리하기

눈에 보이는 중복 로직에 대해서만 분리 (너무 디테일한 요소까지 잡아서 분리해버리면 추후 확장성을 고려하지 못하게 되거나 시간이 너무 오래 걸릴 수 있음)

  • constants
    • 전역으로 사용되는 초기 데이터 및 상수
  • lib
    • 가격 포맷팅 함수
  • hooks
    • 디바운스 관리 훅
      • 함수를 실행하는 경우라면 라이프 사이클에 관여하지 않고 클로저로 가능하지만, 값을 관리해야하기 때문에 use 방식으로 구현
    • 로컬스토리지 관리 훅
      • 멀티탭 환경까지 고민해야할까? 라고 했는데 이미 하신 분들이 계셨었다.
      • 오버엔지니어링은 하는 게 좋다는 지난 리뷰가 기억나서 시간 나면 해야겠다! 라고 했는데, 그 시간은 돌아오지 않았다. - 르블랑의 법칙.......
  • 아이콘

아이콘 모듈을 분리할 때는 사용성이나 확장성에 대한 고민을 해봤다. svg를 추가할때마다 컴포넌트를 추가해야한다면?..

  1. svg 파일이 수백개 이상 증가하게 되는 경우 관리하기가 어렵고
  2. 한 파일에서 load해야하는 svg 컴포넌트가 많아지고

이렇게 되면 사용하기 어렵고 굉장히 복잡해질 것 같은데 어떻게 쉽게 사용하고 관리하지?를 고민해봤는데 svg sprites 기법이 도움이 되었습니다 - SVG Sprite 기법을 사용해 나만의 특별한 Icon 컴포넌트 개발

4. 컴포넌트 분리

  • 관리자 페이지 컴포넌트 분리
  • 카트 페이지 컴포넌트 분리

컴포넌트를 분리하는 과정에서 결합되어 있는 훅스들도 어느정도 보여서 엔티티별로 훅스들도 분리했습니다.

  • product 분리
  • coupon 분리
  • notification 분리
  • cart 분리
  • 기타 훅스 분리하기
    • 비즈니스 로직을 담은 훅스를 use__Service.ts 서비스 계층으로 분리
    • form 상태 및 로직들은 use__Form.ts으로 분리

5. 디테일 챙기기

이제 여기서부터는 디테일한 부분 개선

  • 순수 함수 / 함수형 처리
  • 매직 넘버 상수화 처리
  • 함수 분리
    • formattor
    • validator

NOTE

세 번째 레슨: 나만의 기준 세우기

심화 과제에서는 FSD 아키텍처(Folder by Feature)를 적용해봤습니다. 예전에 써봤던 경험이 있어서 이번엔 더 명확하게 해보고 싶었고, 과거와 어떻게 다른 방식으로 정리할 수 있을지 궁금했습니다.

물론… 늘 고민하게 되는 widgets vs features의 정의에서 AI와도 논쟁이 있었고, 오히려 코드에 더 갇혀 요구사항과 멀어지는 경험도 했습니다.

그래서 기준을 다음과 같이 정리하고 나서야 각 레이어의 역할이 명확해졌습니다:

심화 과제에서는 FSD Architecture를 적용하였습니다. 이전에 사용해본 경험이 있어서 이번에 더 명확하게 해보고 싶었고, 과거의 나와 어떻게 다른 방식으로 분류할 지 궁금했기 때문입니다.

하지만 늘 그랬듯이, widgets / features 레이어에 대한 끊임없는 질문 공새와 답이 없는 토론이 오고갔습니다. (AI도 왔다리 갔다리 하더라) 그리고 이 과정에서 요구사항과는 점점 거리가 멀어지고, 코드에만 너무 얽매여버리는 모습을 발견했다. 그래서 다음과 같이 레이어별 정의를 내리고 진행했습니다..

  • app: 진입점 및 전역 프로바이더 등
  • pages: 페이지 컴포넌트
  • widgets: UI + 비즈니스 로직의 조합으로 구성된 컴포넌트
  • features
    • 비즈니스 로직 (비즈니스 로직을 수행하기 위한 규칙 등이 포함되어야 한다)
    • 혹은 여러 도메인을 조합해 하나의 기능 흐름을 구성 (유즈케이스)
      • model은 비즈니스 로직
      • ui는 ui 상태와 관련된 로직
  • entities: 데이터 / 저장소
  • shared: 공용 모듈

기준을 세우니까 코드 분리 속도도 빨라지고, 합의의 중요성도 체감했습니다.

그리고 !!!!!!!

지난주에는 팀원들과 하지 못했던 기술적 토론을, 이번 주에는 Entity와 UI에 대해 노션에 정리하며 이야기 나눌 수 있었습니다!! 이전까지는 '엔티티는 뭐야?', 'UI는 뭐야?' 처럼 개념이 추상적으로만 떠올랐는데, 어떻게 설명하면 잘 이해될까를 고민하며 실생활 예시를 중심으로 대화를 나누다 보니 개념이 더 명확해졌습니다. 페어 4팀(4팀/7팀) 최고!!!!!! 우리팀 머시따!!

과제를 다시 해보면 더 잘 할 수 있었겠다 아쉬운 점이 있다면 무엇인가요?

Top Down은 너무 오래걸렸던 것 같다. 그리고 디테일한 요소에 대해 신경을 많이 쓰지 못한 것 같다.

그리고 이번에 fsd architecture를 이용해서 심화 과제를 적용했는데, 시간도 부족한 상황에서 너무 억지로 급하게 적용한 것 같아서 개인적으로 큰 아쉬움이 남는다.

리뷰 받고 싶은 내용이나 궁금한 것에 대한 질문 편하게 남겨주세요 :)

  1. Section 단위로 파일을 나누는 기준이 depth(폴더 깊이) 기준이 되어야 할까, 아니면 추상화 수준(레이어 구조)을 기준으로 나누는 게 더 적절할까?

예를 들어 depth 1 수준에서 파일을 나누려다 보니 가독성이 떨어져서 Section 단위로 나누는 것이 나아 보였는데, 이렇게 되면 추상화 수준이 섞여서 일관성이 무너지는 것 같아 고민입니다. 이럴 때 추상화 수준은 depth를 기준으로 봐야 할까요, 아니면 도메인 레이어 구조(shared, entities, widgets, pages 등)를 기준으로 봐야 할까요?

  1. 페이지 내부에 남아있는 로직 들을 훅으로 분리하고자 할 때, 이 훅들은 어떤 네이밍과 역할 기준으로 분리하는 것이 적절할까요?
  2. 저는 상태 변경은 Store에, 비즈니스 로직은 Service 계층에 분리하는 구조를 따르고 있는데, 이런 구조가 적절한지 고민됩니다.

과제 피드백

안녕하세요 병준님! 5주차 과제 잘 진행해주셨네요 ㅎㅎ 고생하셨습니다!

Section 단위로 파일을 나누는 기준이 depth(폴더 깊이) 기준이 되어야 할까, 아니면 추상화 수준(레이어 구조)을 기준으로 나누는 게 더 적절할까? 예를 들어 depth 1 수준에서 파일을 나누려다 보니 가독성이 떨어져서 Section 단위로 나누는 것이 나아 보였는데, 이렇게 되면 추상화 수준이 섞여서 일관성이 무너지는 것 같아 고민입니다. 이럴 때 추상화 수준은 depth를 기준으로 봐야 할까요, 아니면 도메인 레이어 구조(shared, entities, widgets, pages 등)를 기준으로 봐야 할까요?

병준님께서 말씀하시는 Section의 의미를 정확히 이해하기가 어렵네요..! 아마 폴더를 말씀하시는거겠죠?

추상화 수준에 포커스를 맞춰서 판단해보자면 저는 레이어구조라고 생각해요! 그런데 일단 Section이 뭘 의미하는지에서부터 제가 어떻게 답변드려야 좋을지 헷갈리네요 ㅠㅠ 이건 다시 문의 채널에 남겨주세요!

페이지 내부에 남아있는 로직 들을 훅으로 분리하고자 할 때, 이 훅들은 어떤 네이밍과 역할 기준으로 분리하는 것이 적절할까요?

고민이 된다면 그냥 다 분리해버리세요 ㅎㅎ 일단 useCartPage 로 분리하는거죠. 그 다음에 useCartPage에서 쓰이는 코드를 보고 역할을 추론하여 네이밍 하는거죠

이름을 먼저 생각하는게 아니라, "이 코드가 무슨 일을 하고 있지?"를 토대로 추론해야 한다고 생각해요.

지금 과제를 수행해주신 내용을 기준으로 보면 잘 분리해주셔서 더 이상 분리하지 않아도 좋을 것 같긴해요!

저는 상태 변경은 Store에, 비즈니스 로직은 Service 계층에 분리하는 구조를 따르고 있는데, 이런 구조가 적절한지 고민됩니다.

적절하다고 생각해요! 그럼에도 고민이 된다면 반대로 적절하지 않을 수 있는 이유 혹은 코드 조각을 찾아보시면 어떨까요?

필요에 따라 여러 개의 계층이 더 생길 수도 있고, store의 로직도 순수함수로 분리하는 등의 과정이 필요할 수 있답니다 ㅎㅎ

가령 상태관리 라이브러리를 교체하는 상황을 생각해보시면 좋을 것 같아요!