C.W.K.
Stream
Lesson 05 of 06 · published

Action 합성 — 체인, 래퍼, hook

~11 min · actions, composition, patterns

Level 0React 입문자
0 XP0/54 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
좋은 Action 하나 생기면 감싸고 싶어짐. 모든 action 로그. 확인 단계 추가. 성공에 follow-up 돌리기. 패턴 셋이 대부분 커버.

패턴 1 — 횡단 관심사로 action 래핑

모든 save action 을 분석 서비스에 로그하고 싶음. 어떤 action 도 감싸고 side effect 추가하는 higher-order action 작성:

function withLogging<S>(name: string, action: (s: S, fd: FormData) => Promise<S>) {
  return async (s: S, fd: FormData): Promise<S> => {
    logEvent(`action.start.${name}`);
    try {
      const result = await action(s, fd);
      logEvent(`action.success.${name}`);
      return result;
    } catch (e) {
      logEvent(`action.failure.${name}`);
      throw e;
    }
  };
}

const loggedSaveDraft = withLogging('saveDraft', saveDraft);

패턴 2 — 단일 제출에 action 체인

가끔 제출 하나가 action 둘 트리거 (draft 저장, 그 다음 이메일 보냄). Action 함수 안에서 composition:

async function saveAndNotify(_prev, fd) {
  const draft = await saveDraft(fd);
  await sendNotification(draft.id);
  return draft;
}

React 시점에선 Action 하나로 유지 — pending state 가 체인 전체 걸침.

패턴 3 — 재사용 가능한 action 로직을 hook 으로 추출

같은 action 연결 (유효성, 로깅, optimistic 업데이트) 이 컴포넌트들에 반복되면 커스텀 hook 으로 감쌈. Hook 이 action 과 관련 state 노출.

Action 은 함수; 함수는 합성됨. 특별한 프레임워크 필요 없음. 명확성 위해 평범한 async 함수 리팩토링한다면 Action 도 같은 방식으로. 로깅, 재시도, rate limiting, optimistic-update 셋업 — 다 그냥 함수 합성.

Code

재사용 가능한 useFormAction hook·tsx
import { useActionState, useOptimistic } from "react";

type ActionFn<S> = (state: S, formData: FormData) => Promise<S>;

export function useFormAction<S>(
  action: ActionFn<S>,
  initial: S,
  optimisticUpdater?: (current: S, fd: FormData) => S
) {
  const [state, formAction, isPending] = useActionState(action, initial);
  const [optimistic, addOptimistic] = useOptimistic(
    state,
    optimisticUpdater ?? ((s) => s)
  );

  const wrappedAction = async (fd: FormData) => {
    if (optimisticUpdater) addOptimistic(fd as unknown as Parameters<typeof addOptimistic>[0]);
    await formAction(fd);
  };

  return { state: optimistic, action: wrappedAction, isPending };
}

// 호출자 — action + initial + 선택적 optimistic updater 선언, 깔끔한 번들 받음.
// const { state, action, isPending } = useFormAction(saveTitle, { title: '' }, ...);

External links

Exercise

Subscribe-form action 가져와. withLogging (패턴 1) 으로 감싸고 각 제출이 start + success/failure 를 console 에 출력하는지 확인. 그 다음 action-state 연결을 컴포넌트 안에서 쓰는 useSubscribeForm() hook 으로 추출. Hook 이 action 라이프사이클 소유하면 컴포넌트가 순수 렌더링이 되는 거 관찰.
Hint
withLogging 시그니처가 action 미러: (prevState, formData) => Promise<newState>. 래퍼가 계약 보존; hook 이 조립 추상화.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.