Factory 패턴으로 Zustand 스토어 생성

이 글에서는 Zustand 스토어를 Factory 패턴을 통해 생성하는 방법을 배웁니다.

요약

  • Factory 패턴을 사용하여 인증 로직을 여러 프로젝트에서 공유하는 방법을 배웁니다.
  • Zustand 스토어의 구조와 사용법을 이해합니다.
  • 공통 로직과 개별 로직을 분리하여 유지보수성을 높이는 방법에 대해 논의합니다.

배경/문제

3개의 프론트엔드 프로젝트(lms.web, lms-admin.web, lms-integration-admin.web)가 동일한 인증 로직을 사용하지만, 각 프로젝트마다 초기화해야 할 스토어와 로그인 후 처리의 차이점이 있습니다. 또한, 스키마 검증 실패 시의 처리도 다릅니다. 이러한 상황에서 Factory 패턴을 활용하여 공통 로직과 각 프로젝트에 특화된 로직을 분리할 수 있습니다.

접근/해결 전략

Factory 패턴을 통해 공통된 인증 로직을 정의하고, 각 프로젝트별로 필요한 설정을 주입하여 유연하게 사용할 수 있는 구조를 만듭니다. 이를 통해 코드의 재사용성을 높이고, 유지보수를 용이하게 합니다.

구현 포인트

1. 타입 정의

AuthStoreConfig 및 AuthState 인터페이스를 정의합니다. 아래 코드는 shared/auth/types.ts 파일에서 정의된 내용입니다.

// shared/auth/types.ts
export interface AuthStoreConfig {
    storesToReset?: Array<{ getState: () => { reset?: () => void } }>
    onLogin?: () => void
    onLogoutFallback?: () => void
    onSchemaValidationFail?: () => void
}

export interface AuthState {
    user: User | null
    isAuthenticated: boolean
    isHydrated: boolean
    login: (userData: User) => void
    logout: () => Promise<void>
    validateToken: () => Promise<void>
    setHydrated: () => void
}

2. Factory 함수

createAuthStore 함수를 정의하여 인증 스토어를 생성합니다. 이 함수는 프로젝트별로 전달된 설정을 바탕으로 동작합니다. 아래 코드는 shared/auth/authStoreFactory.ts 파일에서 정의된 내용입니다.

// shared/auth/authStoreFactory.ts
export function createAuthStore(config: AuthStoreConfig = {}) {
    const {
        storesToReset = [],
        onLogin,
        onLogoutFallback,
        onSchemaValidationFail,
    } = config;

    return create<AuthState>()(
        persist(
            (set, get) => ({
                user: null,
                isAuthenticated: false,
                isHydrated: false,

                login: (userData) => {
                    const currentUser = get().user;

                    // 다른 사용자로 로그인 시 스토어 초기화
                    if (currentUser && currentUser.id !== userData.id) {
                        localStorage.clear();
                        sessionStorage.clear();
                        storesToReset.forEach((store) =>
                            store.getState().reset?.()
                        );
                    }

                    set({ user: userData, isAuthenticated: true });
                    onLogin?.();
                },

                logout: async () => {
                    // 공통 로그아웃 로직
                    localStorage.setItem('isExplicitLogout', 'true');
                    // ...
                },

                // ...
            }),
            {
                name: 'auth-storage',
                onRehydrateStorage: () => (state) => {
                    if (state) {
                        state.isHydrated = true;
                    }
                },
            }
        )
    );
}

3. 프로젝트별 사용

각 프로젝트에서 인증 스토어를 사용하는 방법입니다. 아래 코드는 각 프로젝트별로 스토어를 생성하는 예시입니다.

// lms.web/src/stores/authStore.ts
import { createAuthStore } from '@/shared/auth';
import { useCartStore } from './cartStore';
import { useTermStore } from './termStore';
import { useTenantStore } from './tenantStore';

export const useAuthStore = createAuthStore({
    storesToReset: [useCartStore, useTermStore, useTenantStore],
    onLogin: () => {
        console.log('LMS Web: 로그인 완료');
    },
    onSchemaValidationFail: () => {
        useCartStore.getState().reset();
    },
});
// lms-admin.web/src/stores/authStore.ts
import { createAuthStore } from '@/shared/auth';
import { useTenantStore } from './tenantStore';

export const useAuthStore = createAuthStore({
    storesToReset: [useTenantStore],
});
// lms-integration-admin.web/src/stores/authStore.ts
import { createAuthStore } from '@/shared/auth';

export const useAuthStore = createAuthStore({
    // 초기화할 스토어 없음
});

주의사항/트레이드오프

  • 성능: Factory 패턴을 사용하면 공통 로직이 한 곳에서 정의되어 성능 저하 없이 관리할 수 있습니다.
  • 보안: 사용자 인증 로직을 재사용하는 만큼, 공통 로직의 보안성을 철저히 검토해야 합니다.
  • 유지보수: 공통 로직에서 발견된 버그는 모든 프로젝트에 반영되므로, 수정 후 모든 프로젝트에서 테스트를 수행해야 합니다.

마무리

  • Factory 패턴을 사용하여 인증 로직을 구현하는 방법을 배웠습니다.
  • 각 프로젝트에서 필요에 따라 스토어 초기화 및 사용자 처리 로직을 간편하게 커스터마이징할 수 있습니다.
  • 체크리스트:
    • 공통 로직 구현 여부 확인
    • 각 프로젝트별 특정 로직 주입
    • 테스트 수행 및 배포 준비

관련 링크

  • [[Zustand Persist와 Hydration]]
  • [[의존성 주입 패턴]]
  • [[SOLID 원칙 - 개방폐쇄 원칙]]

© 2024. Chiptune93 All rights reserved.