Hwirin-Kim 님의 상세페이지[1팀 김휘린] Chapter 2-2. 디자인 패턴과 함수형 프로그래밍

과제의 핵심취지

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

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

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

배포 링크

https://hwirin-kim.github.io/front_6th_chapter2-2/

기본과제

  • Component에서 비즈니스 로직을 분리하기

  • 비즈니스 로직에서 특정 엔티티만 다루는 계산을 분리하기

  • 뷰데이터와 엔티티데이터의 분리에 대한 이해

  • entities -> features -> UI 계층에 대한 이해

  • Component에서 사용되는 Data가 아닌 로직들은 hook으로 옮겨졌나요?

  • 주어진 hook의 책임에 맞도록 코드가 분리가 되었나요?

  • 계산함수는 순수함수로 작성이 되었나요?

  • Component에서 사용되는 Data가 아닌 로직들은 hook으로 옮겨졌나요?

  • 주어진 hook의 책임에 맞도록 코드가 분리가 되었나요?

  • 계산함수는 순수함수로 작성이 되었나요?

  • 특정 Entitiy만 다루는 함수는 분리되어 있나요?

  • 특정 Entitiy만 다루는 Component와 UI를 다루는 Component는 분리되어 있나요?

  • 데이터 흐름에 맞는 계층구조를 이루고 의존성이 맞게 작성이 되었나요?

심화과제

  • 이번 심화과제는 Context나 Jotai를 사용해서 Props drilling을 없애는 것입니다.

  • 어떤 props는 남겨야 하는지, 어떤 props는 제거해야 하는지에 대한 기준을 세워보세요.

  • Context나 Jotai를 사용하여 상태를 관리하는 방법을 익히고, 이를 통해 컴포넌트 간의 데이터 전달을 효율적으로 처리할 수 있습니다.

  • Context나 Jotai를 사용해서 전역상태관리를 구축했나요?

  • 전역상태관리를 통해 domain custom hook을 적절하게 리팩토링 했나요?

  • 도메인 컴포넌트에 도메인 props는 남기고 props drilling을 유발하는 불필요한 props는 잘 제거했나요?

  • 전체적으로 분리와 재조립이 더 수월해진 결합도가 낮아진 코드가 되었나요?

과제 셀프회고

이번 과제는 쉽다고 느꼈지만 결론적으로 썩 맘에드는 결과물은 나오지 않아서 좀 아쉽다. 일단 첫 시작은 cart, coupon, proudcts를 단순 훅으로 분리하는 작업을 진행했고 그 다음에 UI를 나누는 작업을 진행했다. 사실 나는 Hint폴더가 있다는걸 모르고 진행해서 좀 엉망진창으로 만들어진 느낌이 강했다.

그러다 같은 조원인 아름님의 코드를 보게 되었는데, model에서 깔끔하게 비즈니스로직을 작성하고 hook에서는 모델에서 만든 함수를 가져다가 상태 변경만 하는 깔끔한 구조를 보고 나도 그런 식으로 바꿨다.

근데 그렇게 하다보니 hooks에서 깔끔한 상태변경만 하고 싶은데, addNotification이라는 토스트 알림 함수가 존재하여 "이렇게 여러 일을 해도 되나..?" 하는 생각이 들었다.

그래서 나는 handler들을 모아둔 Hook을 하나 더 만들게 된다. 이 핸들러훅에서 비로소 컴포넌트로 전달될 함수가 탄생하게 된다.

그래서 데이터 흐름이 어떤 방향이 되었냐 하면..

User Interaction
UI Components (pages/components)
useAppCore (앱단에 집중된 훅)
Handler Hooks (hooks/useXXXHandlers.ts)
Entity Hooks (entities/useXXX.ts)
Models (entities/xxx.model.ts)
순수함수 & State Updates

이런 방식을 가게 되었다.

그리고 심화과제는 기본과제와 똑같은 구조에서 Jotai를 통해 프롭스를 제거해줬다. 따라서 심화과제의 데이터 흐름은 다음과 같다.

User Interaction
UI Components (pages/components)
Jotai Atoms (전역 상태 직접 접근)
Entity Hooks (Jotai 기반 상태 관리)
Models (순수 비즈니스 로직)
순수함수 & State Updates

즉, 중앙에 집중된 훅들과 핸들러를 통하지 않고 곧바로 전역에 뿌려진 아톰에 접근하는 것이다.

이번 과제에서 프롭스가 너무 많으면서도 깊게 뻗어있어서 Jotai를 적용 후 프롭스를 일부 걷어내다가.. 나머지는 그냥 AI에게 맡겼다. 역시나 이 부분은 간단하고 명확한 작업이라서 금방 해줬다.

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

역시나 제일 신경 쓴 부분은 테스트였다. 뭐 하나 수정하면 테스트 진행이 1순위이다.

테스트를 제외하고 가장 크게 신경쓴 부분은 코드의 관심사 분리였다. 그러나 이번 과제에서의 내 코드가 그렇게 까지 잘 분리된것같지 않는다.

나는 일단 model이라는 파일에 일종의 액션함수를 작성했고, 엔티티커스텀훅 내부에서 상태를 만들고 model의 액션함수들을 가져다가 상태를 업데이트할 수 있도록 만들었다.

그리고 그 상태변경 함수들에 조건별 알림처리 등이 있는것이 맘에들지 않아서 핸들러 훅을 정의하고 핸들러 훅 내부에서 조건별 알림처리등을 진행했다.

나름 모델은 액션함수만 쓸거야, 엔티티훅은 상태변경만 다룰거야, 핸들러훅은 기타 다른 동작들을 섞어줄거야 하는 분기가 담겼다.

하지만 뭔가 이게 더 복잡성만 증대시킨것이 아닌가..? 하는 생각도 들었다. 어차피 model에서 상태가 어떻게 변할지 예측이 가능하므로, 엔티티훅이라고 만든 상태변경훅에서 핸들러 훅이 하던 역할을 같이 해주면 굳이 핸들러 훅을 만들지 않아도 되기 때문이다. (물론 그러면 하나의 훅이 덩치가 약간 증가되므로 안좋을것같기도 하지만...)

결과적으로 약간 가독성이 좋지 않은 코드가 되었긴 하지만 내가 중점을 뒀던 부분은 각 코드가 가진 역할의 분리였다..!!

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

준일코치님께 네임스페이스에 관한 피드백을 받았는데, 너무나도 충격이였다. 사실 피드백을 받는 시점에 내 코드의 가독성이 매우 안좋았는데, 나는 그것이 코드의 구조가 잘못되어 그렇게 보이는것이라고 생각하고 있었다.

하지만 준일코치님은 네임스페이스에 관한 조언을 해주시며 내 코드를 수정하기 시작했는데, 같은 코드임에도 어떻게 사용하느냐가 정말 큰 가독성 차이를 가져온다는것을 알게 되었다.

그래서 피드백이 끝난 후 곧바로 적용해봤지만... 아직은 서툴고 새벽에 급한 마음으로 수정해서인지 내가 충격을 받았던 그만큼의 깔끔함은 나오지 않았다.

이 부분을 처음부터 알고 적용했더라면 더 깔끔한 구조가 되지 않았을까..? 하는 의문이 들면서 앞으로도 그 방식을 사용해 연습 해볼 예정이다.

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

위에도 언급했던 내용인데, 저는 model, entitiy hook, handler hook 세 가지 계층을 나눴습니다.

모델에서는 순수함수로만 구성하면서 도메인별 비즈니스 로직을 담당하고,

엔티티 훅에서는 상태관리에 초점을 두면서 실패나 성공등의 리턴이 필요할 수 있다고 생각이 들어 Result 값을 반환했습니다.

// result 값
{
 success: true,
 message: MESSAGES.SUCCESS.PRODUCT_ADDED,
 type: "success",
}

핸들러 훅에서는 사이드 이펙트 관리를 하며 엔티티와 UI컴포넌트간의 연결다리 역할을 했습니다.

그런데 이 핸들러 훅은 엔티티 훅에서도 해줄 수 있는 역할이라고 생각이 듭니다. 이미 비즈니스 로직은 명확하게 모델에서 분리하였고, 상태를 관리하는 곳에서 어떤 사이드 이펙트나 결과에 관한 UI 로직을 바로 처리하면 더 직관적이지 않나? 라는 생각입니다.

그래서 코치님께서 이 과제를 진행하셨다면 어느정도까지 분리하여 쓰실지, 현실적으로 "여기까지 분리하면 충분하다!" 는 기준이나 실전 팁이 있으신지 너무 궁금합니다..!!

과제 피드백

안녕하세요 휘린님! 5주차 과제 너무 잘 해주셨네요 ㅎㅎ 고생하셨습니다!!


코치님께서 이 과제를 진행하셨다면 어느정도까지 분리하여 쓰실지, 현실적으로 "여기까지 분리하면 충분하다!" 는 기준이나 실전 팁이 있으신지 너무 궁금합니다..!!

저는 아마 추상화 수준을 계속 생각할 것 같아요 ㅎㅎ

https://junilhwang.github.io/TIL/clean-code/%EC%A1%B0%EA%B0%81%EB%AA%A8%EC%9D%8C/%EC%B6%94%EC%83%81%ED%99%94%EB%A5%BC-%EC%A0%95%EB%A0%AC%ED%95%98%EA%B8%B0/

이 글의 내용을 일단 참고해주시면 좋겠어요!

글에서 다루지 않는 부분에 대해 조금 더 설명드리자면,

  1. 일단 컴포넌트 내부의 도메인을 다루는 로직을 hook으로 분리합니다.
  2. hook에서 순수함수를 분리합니다.
  3. 그리고 추상화 수준이 정렬되었는지 확인하면서 이를 정렬시키는 방식으로 계속 분리해나갑니다.

저는 3번이 제일 중요하다고 생각해요!

지금 휘린님께서는 명확하게 어떤 계층으로 분리할지 정리해놓은 상황이기 때문에 이에 대한 일관성만 잘 유지할 수 있으면 충분할 것 같아요 ㅎㅎ

사실 이런 기준을 정할 때 "팀에서 합의된 것"의 우선순위가 제일 높아서요 ㅋㅋ 제가 가지고 있는 기준은 저희 팀에서 정리된 기준에 가깝답니다.