Skip to content

[7팀 양창훈] Chapter 2-2. 디자인 패턴과 함수형 프로그래밍#22

Open
yangchanghun wants to merge 34 commits intohanghae-plus:mainfrom
yangchanghun:main
Open

[7팀 양창훈] Chapter 2-2. 디자인 패턴과 함수형 프로그래밍#22
yangchanghun wants to merge 34 commits intohanghae-plus:mainfrom
yangchanghun:main

Conversation

@yangchanghun
Copy link

@yangchanghun yangchanghun commented Aug 3, 2025

https://yangchanghun.github.io/front_6th_chapter2-2/basic.html

과제의 핵심취지

  • 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의 책임에 맞도록 코드가 분리가 되었나요?

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

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

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

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

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

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

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

심화과제

  • 재사용 가능한 Custom UI 컴포넌트를 만들어 보기

  • 재사용 가능한 Custom 라이브러리 Hook을 만들어 보기

  • 재사용 가능한 Custom 유틸 함수를 만들어 보기

  • 그래서 엔티티와는 어떤 다른 계층적 특징을 가지는지 이해하기

  • UI 컴포넌트 계층과 엔티티 컴포넌트의 계층의 성격이 다르다는 것을 이해하고 적용했는가?

  • 엔티티 Hook과 라이브러리 훅과의 계층의 성격이 다르다는 것을 이해하고 적용했는가?

  • 엔티티 순수함수와 유틸리티 함수의 계층의 성격이 다르다는 것을 이해하고 적용했는가?

과제 셀프회고

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

컴포넌트 깔끔하게 분리하기 ?

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

엔티티와 ui를 이해한식으로 코드를 분리하려했다. 컴포넌트상에서 필요한 엔티티 및 로직만을 가져오기 위해 커스텀훅스를 만들고
커스텀 훅스를 호출해 컴포넌트상에서 호출하였다..아래처럼.....

  //App.tsx
  const { products, setProducts } = useProducts();

  //ProductForm.tsx
  const { updateProduct, addProduct } = useProducts();

테스트는 실패하였다. 왜 실패하지 한참을 생각해보니 당연한거였다.. 같은 훅스를 호출하면 독립적인 상태가 두개 생기니 동일한 상태를 건드려야하는데 다른 상태를 건드리니 원하는대로 되지 않았던거다.
props driling을 최소화한상태로 분리하려고 했지만 실패했다.
전역변수상태관리 라이브러리를 사용하면 엔티티와 ui분리가 쉽게 될거같지만 사용안하고 하려면 어떻게 구현해야할까,,,,

컴포넌트를 세세하게 분리해 확장성도 늘리고 싶었지만 쉽지 않다,..

순수함수로 바꾸려했지만 쉽지 않다...

jotai이용해서 advanced진행하려했지만 테스트가 터지고 시간이 부족해 포기했따.

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

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

@yangchanghun yangchanghun changed the title 7팀 양창훈 [7팀 양창훈] Chapter 2-2. 디자인 패턴과 함수형 프로그래밍 Aug 3, 2025
@eveneul
Copy link

eveneul commented Aug 8, 2025

포기하지 마!! 밤새워!!

Copy link

@JunilHwang JunilHwang left a comment

Choose a reason for hiding this comment

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

안녕하세요! PR(advanced 분기) 정리 및 리팩터링 시도 잘 하셨습니다. 전체 변경을 보며 “상태관리 전환”, “모듈화/패키징” 관점에서 발생할 수 있는 시나리오와 개선 포인트(응집도·결합도 포함)를 아래 흐름대로 정리했습니다.

요구하신 흐름대로:

  • 기술 요구사항 변화 시나리오 제시
  • 현재 코드가 각 시나리오에 대응한다고 했을 때 어떤 변화가 발생할지
  • 이를 토대로 코드 품질(응집도·결합도) 평가 및 개선안(AS-IS / TO-BE 코드 포함)

요약(핵심 키워드)

  • 상태관리 라이브러리 전환: jotai 사용 → zustand / redux / tanstack-query 등으로 옮길 때의 영향
  • 모듈화(패키지화) 준비: 엔티티 계층(도메인)과 UI 계층 분리, persistence와 store 분리
  • 응집도 문제: 타입/상수/초기값이 컴포넌트/atoms에 섞여 있음(순환의심)
  • 결합도 문제: 특정 콜백(addNotification)을 직접 주입하는 방식, 훅 인터페이스 일관성 부족
  • 개선 포인트: 타입/도메인/스토어/유틸 계층을 명확히 분리, 훅 인터페이스를 명확한 액션 중심으로 설계

Pull Request Body(셀프회고)에 대한 짧은 인사이트

  • "같은 훅을 여러 컴포넌트에서 호출하면 독립 상태가 생겨 테스트 실패" — 정확한 인지입니다. 훅이 지역 상태(local state)를 만든다면 호출마다 독립 인스턴스가 생깁니다. 전역 상태(Provider/atom/store)로 바꾸거나, 상위에서 상태를 올려주어야 합니다. 이 인식은 좋은 시작점입니다.
  • “전역 상태관리 라이브러리 없이 엔티티·UI 분리 가능한가?” — 가능합니다. 다만 패턴(상태 리프팅 + context + 명확한 API 추상화)이 필요합니다. 라이브러리 도입은 단순히 편리성 뿐 아니라 "어디서 상태가 변화하는지"를 명확히 하는 데 도움됩니다.
  • 다음으로 생각해볼 질문:
    • 어떤 상태(엔티티)는 전역화(공유)하고, 어떤 상태는 지역화(컴포넌트 내부)할 것인가? 기준은 무엇인가?
    • 도메인(엔티티) 훅의 public API는 어떻게 설계할 것인가? (동작 중심/이벤트 콜백/Promise 반환 등)
    • 테스트용도로 상태를 모킹하려면 어떤 경계(인터페이스)를 만들면 좋을까?

PullRequestBody의 "리뷰 받고 싶은 내용이나 궁금한 것"에 대한 답변

  • “전역 상태관리 라이브러리를 사용하면 엔티티와 ui분리가 쉽게 될 것 같은데 안쓰고 하려면?”
    방안:
    • 상태 리프팅: 최상단(AppContainer)에서 products, cart를 관리하고 하위 컴포넌트에 props로 전달.
    • Context: 전역성이 필요한 부분만 React Context로 묶기 (Provider 하나). (Context + custom hook으로 안전하게 추상화)
    • 이벤트 버스: 작은 앱에서는 간단한 이벤트 emitter로 변경 통지 가능.
    • 그러나 중복 호출 훅(로컬 상태)을 “전역 상태”로 바꾸려면 결국 전역 저장소(Provider/atom/store)나 상위로의 리프팅이 필요. 따라서 jotai 같은 전역 상태 도입은 자연스러운 선택입니다.

종합 피드백 (파일/변경사항 기반 키워드)

  1. 주요 변경 키워드
    • jotai 도입(Provider, atoms)
    • App -> AppContainer로 분리, 컴포넌트 계층 분해(페이지, tab, form, cart 등)
    • hooks: useCart, useProducts, useCoupons, useProductForm/useCouponForm 등 분리
    • utils/constants 분리 시도(initialProducts, calculateItemTotal 등)
    • ESLint/Prettier/CI(pnpm) 설정 추가
  2. 잘한 점(인사이트)
    • UI/로직 분리 시도(컴포넌트는 뷰, hooks는 로직/도메인)으로 가독성·재사용성 개선 시도는 적절합니다.
    • 토스트, 헤더, 페이지 별로 컴포넌트를 분리해 재사용성 확보한 점 긍정적입니다.
  3. 주의·개선 필요 점(핵심)
    • 타입/초기값/도메인 타입의 위치가 컴포넌트/atoms로 얽혀 순환 의존성 위험(또는 이미 존재) — 예: productAtom imports ProductWithUI from AppContainer; AppContainer imports productsAtom. (치명적 순환)
    • 훅/atoms 의존성 방향성 정리 필요 — domain 타입은 components가 아닌 공용 types로 분리해야 함.
    • 특정 패턴(addNotification)을 훅에 직접 주입하는 방식은 결합을 키움. 행동별 onSuccess/onError 형태로 API를 열어주는 게 재사용·테스트에 유리.

상세 피드백 — 개념 정의 및 항목별 개선 제안

  1. 정의: 응집도(cohesion)와 결합도(coupling)
  • 응집도(좋은 정의, 팀 기준에 맞춤):
    • 변경 시 파일/코드 수정 경로가 짧을수록(즉, 관련 코드가 한곳에 모여 있을수록) 응집도가 높다.
    • 라이브러(패키지)로 떼어낼 때 관련 파일들을 매끄럽게 분리할 수 있으면 응집도가 높다.
  • 결합도:
    • 모듈(함수/컴포넌트/훅)끼리 직접 내부 구현에 의존하기보다 명확한 인터페이스(함수 시그니처, props, 타입)를 통해 연결되면 결합도가 낮다.
    • 예: addNotification이라는 구체적 명칭 대신 onError/onSuccess 콜백을 쓰면 호출자가 행동을 선택할 수 있어 결합도가 낮아짐.
  1. 문제 정의(요약)
  • 순환 의존성 위험: 타입/초기값이 컴포넌트 파일을 참조 → atoms가 컴포넌트를 import 하는 구조
  • 훅/컴포넌트 인터페이스가 일관되지 않음(예: useCart(addNotification) vs useCart({onError, onSuccess})). 결합도가 높음.
  • 일부 유틸/계산 함수가 컴포넌트에 남아 있거나 훅 내부에 산재 → 재사용성 저하 및 테스트 어려움
  • 전역 상태 전환에 따른 마이그레이션 비용이 어느 정도인지 파악하기 어려움 (atoms 분포, 타입 위치 문제)
  1. 문제 상황(AS-IS) — 구체적 코드 예시

문제 A: 타입이 컴포넌트에 정의되어 있어 atoms가 컴포넌트를 import 함

  • AS-IS (발견된 문제: src/advanced/atoms/productAtom.ts)

    // src/advanced/atoms/productAtom.ts (AS-IS)
    import { atom } from 'jotai';
    import { ProductWithUI } from '../components/AppContainer';  // <-- 컴포넌트에서 타입을 import
    import { initialProducts } from '../constants';
    export const productsAtom = atom<ProductWithUI[]>(getInitialProducts());

    문제: AppContainer (components)에서 productsAtom을 사용 -> 순환(import cycle) 가능성. 타입은 components가 아닌 공용 위치에 있어야 합니다.

  • TO-BE: 타입을 공용 파일로 분리

    // src/advanced/types.ts (TO-BE)
    export interface ProductWithUI {
      id: string;
      name: string;
      price: number;
      stock: number;
      discounts: Array<{ quantity: number; rate: number }>;
      description?: string;
      isRecommended?: boolean;
    }
    
    // src/advanced/atoms/productAtom.ts (TO-BE)
    import { atom } from 'jotai';
    import { ProductWithUI } from '../types';  // <- 공용 타입 사용
    import { initialProducts } from '../constants';
    export const productsAtom = atom<ProductWithUI[]>(getInitialProducts());

문제 B: 훅 인터페이스가 구체적 콜백(addNotification)에 의존

  • AS-IS (useCart signature)

    // src/advanced/hooks/useCart.ts (AS-IS)
    export function useCart(addNotification: (message: string, type?: 'error'|'success'|'warning') => void) { ... }

    문제: addNotification의 구체적 명칭과 동작에 훅이 결합. 호출자가 알림 로직을 바꿀 때 훅을 고쳐야 할 수 있음.

  • TO-BE: 액션별 콜백 혹은 Promise 반환으로 느슨하게 결합

    // TO-BE: 옵션 객체로 분리
    export function useCart(options?: {
      onError?: (err: Error | string) => void;
      onSuccess?: (message?: string) => void;
    }) { ... }
    
    // 또는 action 별로 promise 반환
    const addToCart = async (product) => {
      try {
        // ...logic
        return { ok: true };
      } catch (err) {
        return { ok: false, error: err };
      }
    }

    이러면 호출자는 onSuccess/onError 또는 반환값을 보고 UI 동작(토스트 등)을 결정할 수 있음.

문제 C: persistence(로컬스토리지) 코드가 여러 곳에 중복

  • AS-IS:

    • cartAtom.ts, productsAtom.ts, useProducts.ts, useCoupons.ts 등이 각각 localStorage read/write를 담당.
    • 중복과 책임 분산 문제 발생.
  • TO-BE: persistence 층(혹은 store 레이어)로 일원화

    // src/advanced/persistence.ts
    export function load<T>(key: string, fallback: T): T { ... }
    export function save<T>(key: string, value: T) { ... }
    
    // atoms 초기화에서 load 사용
    const getInitialProducts = () => load<ProductWithUI[]>('products', initialProducts);

    이러면 테스트/교체(예: IndexedDB, 서버 동기화)시 한 곳만 바꾸면 됨.

  1. 문제 해소되는 상황(TO-BE) — 구체적 제안 및 코드

A. 타입·상수 분리 (핵심)

  • Move:
    • src/advanced/types.ts (모든 도메인 타입)
    • src/advanced/constants.ts (initialProducts, initialCoupons)
  • Benefit: atoms/hooks/components 간 순환 의존 제거, 패키지 분리 쉬움

B. 훅 인터페이스 개선

  • AS-IS: useCart(addNotification)
  • TO-BE: useCart({ onError, onSuccess }) 또는 반환 Promise
    • 예: addToCart(product).then(res => ...) or useCart({onError:onToast})

C. 상태관리 라이브러리 전환 시나리오(구체적 영향/예시)

  • 배경: 현재는 jotai atoms 중심 구조. 만약 다른 라이브러리로 바꾼다면 각 훅·atom의 역할을 어떻게 옮길지 설명.

시나리오 1) jotai -> zustand

  • 영향:
    • atoms -> 전역 store (useStore)로 전환.
    • selector/derived state는 zustand의 selector로 구현.
    • persistence: zustand 미들웨어(persist) 사용 가능.
  • 변경 범위:
    • atoms 파일들을 zustand store slice로 옮김 (cartAtom/productAtom -> create() store)
    • useCart/useProducts 훅은 store 호출(선택자 사용)으로 교체.
  • AS-IS (jotai)
    // atoms/cartAtom.ts
    export const cartAtom = atom<CartItem[]>(getInitialCart());
    // useCart uses useAtom(cartAtom)
  • TO-BE (zustand)
    // store/useStore.ts
    import create from 'zustand';
    export const useStore = create((set, get) => ({
      cart: getInitialCart(),
      addToCart: (product) => set(state => ({ cart: addOrInc(state.cart, product) })),
      // ... other actions
    }));
    
    // useCart hook
    export function useCart(options) {
      const cart = useStore(state => state.cart);
      const addToCart = useStore(state => state.addToCart);
      return { cart, addToCart, ... };
    }
  • 비용: 중복 로직 없음. 다만 atoms → store migration 스크립트와 타입 위치 정리 필요.

시나리오 2) jotai -> redux (RTK)

  • 영향:
    • atoms → slice(ducks)로 이동. 액션/리듀서 구조로 변경. 미들웨어(Thunk) 이용 가능.
    • 좋은 점: 엄격한 액션 로그/디버깅, 명확한 액션별 테스트.
  • TO-BE 예시:
    // store/cartSlice.ts (RTK)
    const cartSlice = createSlice({
      name: 'cart',
      initialState: getInitialCart(),
      reducers: {
        addToCart(state, action) { ... },
        removeFromCart(state, action) { ... },
      }
    });
    export const { addToCart } = cartSlice.actions;
  • 비용: 액션/리듀서/타입 등 파일이 더 늘어남. 기존 atoms 및 jotai 전개함수(derived) 변환 필요.

시나리오 3) jotai -> tanstack-query

  • tanstack-query는 서버 상태(비동기 캐시)에 강점. 로컬 엔티티(클라이언트 기본) 관리에는 적합하지 않음(로컬상태는 별도 보관 권장).
  • 권장 사용: products/coupons을 서버 API로 관리할 때 캐시 수단으로 사용. cart는 client-side store(zustand/jotai/Redux)로 유지.

결론: 어떤 라이브러리로 전환할지 결정하는 기준

  • 앱 규모 & 동시성: 여러 컴포넌트가 상태를 읽고 자주 동기화하면 중앙 store (zustand/Redux) 권장
  • 서버 동기화/캐싱 필요시 tanstack-query 권장(원격 데이터)
  • 단순 전역 상태 & 원자적 업데이트가 많으면 jotai 유지도 괜찮음
  1. 모듈화(패키지화) 관점: 응집도/결합도 체크리스트
  • 응집도 높은 모듈(바람직)
    • 도메인 모델 + 관련 유틸(계산 함수들) + 최소한의 저장소 인터페이스(예: productsModel) 묶기
    • UI는 별도의 패키지(React 컴포넌트들)
  • 현재 코드에서 떼어낼 수 있는 단위
    • domain/entities (types + models + initial data)
    • domain/store (atoms 또는 store adapters)
    • ui/components (Header, Cart, Forms)
    • ui/hooks (useCart/useProducts interface only — 내부는 store adapter로 교체)
  • 체크: "패키지로 떼어낼 때 수정 파일 수" — 현재 구조는 atoms/hooks/components가 섞여 있어 떼어내려면 타입 추출과 import 변경이 필요(응집도 중간). 개선하면 패키지화 쉽습니다.

구체적 개선 제안(우선순위)

  1. 타입/도메인 분리 (필수)

    • 이동: 모든 ProductWithUI, Coupon, CartItem 타입을 src/advanced/types.ts (혹은 src/types.ts)로 이동
    • 변경: components, atoms, hooks 모두 해당 공용 타입을 import 하도록 수정
  2. 초기데이터/constants 정리

    • initialProducts, initialCoupons를 컴포넌트가 아닌 constants 파일로 유지(이미 추가됨). constants는 types에 의존하지 않도록 주의.
  3. atoms/스토어와 컴포넌트 의존성 역전

    • atoms는 UI를 import 하지 않도록(현재 productAtom가 AppContainer import함). 항상 타입·상수만 참조.
  4. 훅 인터페이스 개선

    • useCart 등 훅은 options object로 콜백을 받거나 액션별 반환값(Promise)을 사용.
    • 예: useCart({onError, onSuccess}) 또는 addToCart(product).then(...). 이러면 addNotification 의존 제거 가능.
  5. persistence 한 곳으로 모으기

    • localStorage 로직을 한 파일(persistence helper)로 한 번에 관리. 테스트 용도로 mock이 쉬움.
  6. 테스트/유닛화 대응성

    • 각 훅은 내부에서 store에 직접 접근하지 말고(혹은 adapter 패턴), 인젝션 가능한 store 인터페이스를 받게 만들어 모킹이 쉬워지도록 함.

구체적 코드 예시(핵심 변경 전후)

문제 1: productAtom 순환 참조 해결

  • AS-IS:
    // src/advanced/atoms/productAtom.ts
    import { atom } from 'jotai';
    import { ProductWithUI } from '../components/AppContainer'; // <-- BAD
    import { initialProducts } from '../constants';
    export const productsAtom = atom<ProductWithUI[]>(getInitialProducts());
  • TO-BE:
    // src/advanced/types.ts
    export interface ProductWithUI { /* ... */ }
    
    // src/advanced/atoms/productAtom.ts
    import { atom } from 'jotai';
    import { ProductWithUI } from '../types';
    import { initialProducts } from '../constants';
    export const productsAtom = atom<ProductWithUI[]>(getInitialProducts());

문제 2: useCart API 개선(결합도 완화)

  • AS-IS:
    // src/advanced/hooks/useCart.ts
    export function useCart(addNotification) {
      const addToCart = useCallback((product) => {
        // ... addNotification('..')
      });
      return { addToCart }
    }
  • TO-BE 1 (콜백 옵션):
    export function useCart({ onError, onSuccess } = {}) {
      const addToCart = useCallback((product) => {
        try {
          // logic
          onSuccess?.('장바구니에 담겼습니다');
        } catch (err) {
          onError?.(err);
        }
      }, []);
      return { addToCart }
    }
    // 사용처: const { addToCart } = useCart({ onSuccess: msg => addNotification(msg) });
  • TO-BE 2 (Promise 반환):
    const addToCart = async (product) => {
      try {
        // logic
        return { ok: true };
      } catch (e) {
        return { ok: false, error: e };
      }
    }
    // 사용처:
    addToCart(product).then(res => {
      if (res.ok) addNotification('성공');
      else addNotification(res.error.message, 'error');
    });

문제 3: persistence 유틸화

  • AS-IS: 여러 파일에서 localStorage 중복
  • TO-BE:
    // src/advanced/persistence.ts
    export const load = <T>(key: string, fallback: T): T => {
      const saved = localStorage.getItem(key);
      if (!saved) return fallback;
      try { return JSON.parse(saved) as T; } catch { return fallback; }
    };
    export const save = (key: string, value: unknown) => {
      try { localStorage.setItem(key, JSON.stringify(value)); } catch {}
    };
    // 사용:
    const getInitialProducts = () => load<ProductWithUI[]>('products', initialProducts);

마무리: 현재 코드가 "잘 작성된 코드"인지?

  • 긍정적 요소:
    • UI/로직 분리 시도, 컴포넌트 분해가 잘 되어 있음(재사용성 향상).
    • atom 기반 전역 상태로 바꾸려는 의도는 중복 훅 호출 문제를 해결하려는 합리적인 접근.
  • 개선 필요:
    • 타입/상수의 위치 문제(순환 의존) 해결이 최우선입니다. 이 문제를 해결하지 않으면 빌드·런타임 이슈가 날 가능성 큽니다.
    • 훅 인터페이스 설계(결합도 낮추기). addNotification 같은 구체적 용어를 훅 내부에 직접 요구하지 않도록 리팩터링 권장.
    • persistence와 같은 cross-cutting concern을 한 곳으로 모아 응집도 향상.
    • 패키징을 염두에 둔다면 domain(엔티티 + 모델 + 계산함수)을 깔끔하게 떼어낼 수 있게 구조 정리가 필요.

우선순위 액션 아이템(권장)

  1. src/advanced/types.ts로 모든 타입 추출 -> 모든 파일 import 경로 교체(순환 문제 해결)
  2. persistence 유틸 작성 후 atoms/hooks에서 이를 사용하도록 정리
  3. useCart/useProducts/useCoupons 훅의 인터페이스를 options object(또는 Promise)로 바꾸어 addNotification 종속성 제거
  4. atoms 파일에서 컴포넌트/뷰 관련 import 전부 제거
  5. 상태관리 라이브러리 전환을 고려한다면 store-adapter 레이어(예: storeAdapter.ts)를 만들어 jotai/zustand/redux로 교체할 때 adapter만 바꾸면 하위 훅/컴포넌트 영향 최소화

추가 질문(제가 도와드릴 수 있는 것)

  • 원하시면 다음 중 하나를 구체적으로 도와드리겠습니다:
    1. 타입/초기값 분리(파일 목록 & 자동 패치 예시 패치)
    2. useCart/useProducts 훅 인터페이스 리팩터링(AS-IS -> TO-BE 전체 코드 변환)
    3. jotai → zustand 마이그레이션 가이드(구체적 코드 변환 스텝)
    4. 패키지화(도메인 패키지 + UI 패키지) 구조 제안 + 파일 이동 스크립트

감사합니다. 필요하시면 우선순위 1(타입 분리)부터 구체적인 코드 patch(수정안)를 만들어 드릴게요. 어느 작업을 먼저 도와드릴까요?

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