Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[7팀 김영우] [Chapter 2-2] 디자인 패턴과 함수형 프로그래밍 #15

Open
wants to merge 43 commits into
base: main
Choose a base branch
from

Conversation

ywkim95
Copy link

@ywkim95 ywkim95 commented Jan 14, 2025

과제 체크포인트

기본과제

  • React의 hook 이해하기

  • 함수형 프로그래밍에 대한 이해

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

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

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

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

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

심화과제

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

  • 엔티티 -> 리파지토리 -> 유즈케이스 -> UI 계층에 대한 이해

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

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

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

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

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

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

체크포인트

  • 단일 책임 원칙 (Single Responsibility Principle)
    • 함수/훅이 하나의 명확한 목적만을 가지고 있는가?
    • [�x] 함수/훅의 이름이 그 목적을 명확하게 나타내는가?
  • 순수 함수 (Pure Functions)
    • 동일한 입력에 대해 항상 동일한 출력을 반환하는가?
    • 외부 상태를 변경하지 않는가?
    • 입력 데이터를 직접 수정하지 않고 새로운 데이터를 반환하는가?
  • 합성 가능성 (Composability)
    • 함수/훅을 다른 함수/훅과 쉽게 조합할 수 있는가?
  • 관심사 분리 (Separation of Concerns)
    • 비즈니스 로직과 UI 로직이 적절히 분리되어 있는가?
  • DRY (Don't Repeat Yourself) 원칙
    • 이 함수/훅이 중복 코드를 제거하는 데 도움이 되는가?
  • 테스트 용이성 (Testability)
    • 함수/훅에 대한 단위 테스트를 쉽게 작성할 수 있는가?
      (input에 대한 output을 정의하기 수월한가)
  • 에러 처리 (Error Handling)
    • 예상 가능한 에러 상황을 적절히 처리하고 있는가?

과제 셀프회고

이번 과제는 고민이 많이 되는 주제 중에 하나 였습니다. 이전에 사내 업무에서 많이 경험한 것이라 어려운 부분은 없었으나 코치님의 피드백이나 Q&A 세션을 통하여 생각을 더 많이 할 수 있었습니다.
데이터, 액션, 계산에 관한 부분을 어떻게 접근할지, 나는 현재 사내에서 3가지를 명확히 나누어서 하고있는지, 행동과 화면에 대하여 어떻게 분리하고 사용하고 있는지 등 꽤나 고민을 많이 하게된 시간이었습니다.

과제에서 좋았던 부분

  1. 초기 AdminPage에서는 로직과 UI가 혼재되어 있는데, 의외로 로직이 서로를 사용하는 경우가 거의 없어 분리하는 데 어려움이 들지않아 굉장히 좋았습니다.
  2. 코드를 개선하며 흐름을 파악할 수 있도록 의도적으로 작성된 것처럼 느껴져 처음 또는 많이 경험하지 못한 사람들에게 좋은 길이 되는 것 같아 만족스러웠습니다.
  3. 심화 과제에서 localStorage나 msw를 사용하여 데이터를 다루게 되는 방법을 다시 한 번 경험하게 되어서 좋았습니다.

과제를 하면서 새롭게 알게된 점

사내에서 경험한 주제라 자연스레 더 배우지 않게되는 상황이었는데 다시 한 번 경험을 하며, 저는 액션과 순수함수를 분리하는 것을 제대로 못하고 있다는 것을 알게 되었습니다. 또한 너무 많은 추상화가 독이 될 수 있다는 것을 알게 되었습니다...
과거에 하나의 프로젝트에서 컴포넌트를 굉장히 잘게 쪼갠적이 한 번 있었는데, 그때 당시만 하더라도 괜찮다고 생각이 들었지만 지금 돌아보니 유지관리가 굉장히 어려운 것도 알게 되었고, 노력 대비 결과물이 썩 마음에 들진 않습니다. 다행히도 이러한 시간을 통하여 내가 부족한 점을 알게되어 만족합니다.

과제를 진행하면서 아직 애매하게 잘 모르겠다 하는 점, 혹은 뭔가 잘 안되서 아쉬운 것들

코드 중에서 여러 개를 돌려가며 사용하는 함수가 있는데, 이러한 함수가 과연 확장성이 좋을지 고민이 됩니다.
요구사항이 변경되면서 name은 다르게 처리한다 등의 변경 사항에 대해서 대처하기가 애매하다는 판단이 드는데, 여러 개의 값을 받을 수 있도록 처리하는 함수 하나 또는 각각의 처리를 맡는 함수 여러 개 중에서 어떤 것이 더 선호될까 라는 고민이 생겼습니다..!

const useNewProduct = ({ onProductAdd }: UseNewProductProps) => {
  const [newProduct, setNewProduct] = useState<Omit<Product, 'id'>>(initialNewProduct);
  
  // ... 선언부

  // Product의 Key를 받아 업데이트 해주는 로직 
  const handleChangeProduct =
    <T extends keyof Omit<Product, 'id'>>(key: T) =>
    (e: ChangeEvent<FormElementType>) => {
      const value = e.target.type === 'number' ? parseInt(e.target.value) : e.target.value;
      setNewProduct((prev) => ({
        ...prev,
        [key]: value as Product[T],
      }));
    };

  // 다른 로직

  return {
    handleChangeProductName: handleChangeProduct('name'),
    handleChangeProductPrice: handleChangeProduct('price'),
    handleChangeProductStock: handleChangeProduct('stock'),
    // ... 기타 다른 반환 값들
  };
};

제출 시간 이후의 생각난 것들

  • 폴더를 나눌 때 ProductForm.tsx는 Admin에 속해야 하는 데 실수로 Cart에 넣어 버린 것.
  • AdminCouponForm.tsx에서 현재 쿠폰 목록까지 포함된 점
  • 커스텀 훅들의 명칭을 다시 수정하지 않은 점 (책임에 맞게 명칭이 변경되어야한다고 생각함)
    • useProductSet -> 이름만 봐선 뭔지 모르겠음. 적어도 useProductAccordion 등과같은 명칭으로 수정되어야한다고 생각함
  • useAdminEditProduct에 handleRemoveDiscount를 넣은 점 -> 이건 차라리 useAdminProductList로 빼내어 두 개의 discount 함수가 같이 있었어야 된다고 생각함

리뷰 받고 싶은 내용이나 궁금한 것에 대한 질문

1/16(목)에 피드백을 받은 내용 중, 프론트엔드 개발자가 토론할 부분은 없을까? 라는 질문에 대해서 주신 피드백을 생각을 해보았습니다. 첫 번째로 얘기한 주제가 컨벤션이라는 내용에 대해서 의외라고 말씀해주셔서 저도 놀랐습니다.
사실 제가 사람들과 대화를 정말 안해봐서 어떠한 주제가 좋을지, 이 사람이 관심이 없으면 어떡할까, 내가 모르는 내용이 나오면 어떻게 대처할까 이러한 생각부터 먼저 하게되고, 그렇게 생각하니 말을 안하게되고, 또 생각이 나고,... 이러한 것이 반복되어, 사람들을 보면 딱히 말을 먼저 안꺼내게 되어버렸습니다. 실제로 회사에서 인사를 제외한 한 마디도 하지않고 퇴근을 한 적도 있었구요. 그런데 이대로 가다가는 안될 것 같아 젭과 같이 온라인에서라도 말을 먼저 꺼내보려 노력 중입니다. 또 저보다 높은 위치에 있는 사람(나이나 직급 상관없이 내가 높다고 인지하는 사람들...?)에 대해서는 조금 긴장을 하게되어서 말을 잘 못하는 것도 좀 큰 것 같아요.

기반 지식이 조금 없는 것 같다는 말을 해주셨는데, 저도 그렇게 생각을 하고 있습니다. 조금 알게되지 않았나 라는 마음이 조금씩 솟아나고 있었는데 정곡을 찔려서 다시금 저를 되돌아 보는 시간이 되었던 것 같아요. 조금 배웠다고 기고만장하는 게 저의 제일 큰 단점 같아요.

이번 멘토링을 통해서 제 자신에 대해서 다시 한 번 생각하는 기회가 생겨서 다행이라고 생각합니다.

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

- 의존성 설치
- README 문서 작성
- basic 테스트 통과
- getAppliedDiscount는 util함수로 분리
- getRemainingStock는 기존 util함수 사용
- getMaxDiscount는 util 함수로 분리
- CartItemCardWrapper 추가하여 메서드 및 util 함수 계산 처리
- getAppliedDiscount는 여기서만 사용한다고 판단하여 내부 메서드로 변경
- CartItemCard는 순수 UI 로직으로 변경
- ProductCardWrapper 추가하여 메서드 및 util 함수 계산 처리
- getMaxDiscount는 여기서만 사용한다고 판단하여 내부 메서드로 변경
- ProductCard는 순수 UI 로직으로 변경
- useNewCoupon, useNewProduct, useProductSet 으로 분리
- 추상화 시도
- EditProductForm 컴포넌트 분리
- ProductForm 컴포넌트 분리
- ManageCoupon 컴포넌트 분리
- ProductDiscount 컴포넌트 분리
- ManageCoupon => AdminCouponForm 명칭변경
- useNewCoupon 훅 위치 변경
- 기존 EditProductForm, ProductDiscount 삭제 및 AdminProductContent 추가
- AdminProductList 및 AdminNewProduct로 UI 및 로직 분리
- 각각의 커스텀 훅을 컴포넌트에 맞게 위치 변경
- admin과 cart에서 사용하는 컴포넌트 별로 분리
- newDiscount, editingProduct에 대한 각각의 커스텀 훅을 생성
- 공통되는 부분이 필요한 곳은 useAdminProductList를 통하여 생성 및 전달
- useAdminEditProduct의 handleRemoveDiscount에서 계산 로직 getNewProduct를 분리
- App에 있는 initial 변수 및 newDiscount 변수 생성 및 constant파일로 이동 및 적용
@ywkim95 ywkim95 changed the title [WIP] [7팀 김영우] [Chapter 2-2] 디자인 패턴과 함수형 프로그래밍 [7팀 김영우] [Chapter 2-2] 디자인 패턴과 함수형 프로그래밍 Jan 16, 2025
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

msw도 ci 파이프라인 추가하신 것 보고 많이 배웠습니다~!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이거도 제가 gitea action 배우면서 조금씩 알아가게 되었는데 확실히 이런 것 배우면 여러 곳에서 사용할 수 있겠더라구요 다른 분들도 학습하시면 도움 많이 될 듯해요!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오.. 이런 부분까지 꼼꼼하게 챙기셨군요! 번거로우셨을텐데 고생하셨습니다!

onProductUpdate: vi.fn(),
};
test('상품 수정 테스트', () => {
// given
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

given when then 형식으로 테스트 작성해주셨군요!

테스트에 필요한 데이터와 조건, 테스트 목적이 잘 보이게되어서 좋은 것 같네요!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이것 말고 TDD로 접근하시려면 red-green-refactoring 이러한 관계도 있더라구요! 한 번 학습해보시면 좋을 것 같아요!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

맞아요~ 용님 말씀하신대로 깔끔하게 파악할 수 있어서 좋네요!

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이번에도 container-presenter 패턴으로 구현하셨군요!
확실히 외부에서 주입해주는 방식으로 구현하면 컴포넌트 테스트가 쉬워질 것 같네요.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네 맞아요...! 준일 코치님의 멘토링 이후에 조금씩 적용해보려고 하는데 굳이 이렇게까지 해야되나...? 싶은 경우가 조금씩 보이더라구요..!

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

우와 MSW 사용해서 api-mock까지 구현하셨네요! 저는 본 항목 챙기기에도 정신 없었는데.. 대단하세요!

Comment on lines +45 to +49
http.put('/products/:id', async ({ request }) => {
const body = (await request.json()) as Product;
const newProduct = mockProducts.find((p) => p.id === body.id);
return HttpResponse.json({ ...newProduct, ...body });
}),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

전체적으로 필요한 로직만 딱 쓰여있어서 좋네요 ㅎㅎ
여기에 에러 핸들링을 추가하면 더 완성도가 높아질 것 같아요~ 유효하지 않은 id가 들어왔을 때, 404 에러를 반환하는 방식으로 처리하는 것도 좋을 것 같습니다.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

엇 그렇습니다..! early return에 관련된 내용을 추가해야겠습니다..!

@2Estella
Copy link

코드 잘 보고 갑니다~
UI 분리도 깔끔하게 하셨고, 커스텀 훅도 세세하게 분리가 되어 있어서 전체적으로 파악하기 좋았던 것 같습니다!
덕분에 좋은 인사이트를 얻어 갑니다 :) 담주도 화이팅!

@nogy21
Copy link

nogy21 commented Jan 18, 2025

이번주도 역시 여러모로 많이 배우고 갑니다~!
const로만 선언해서 사용하신 부분이나 msw로 api 모킹 테스트 짜신것 등 인상적이었습니다!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants