로그인 페이지를 예시

src/
|-- components/
|   |-- LoginForm.tsx
|-- features/
|   |-- auth/
|   |   |-- authSlice.ts
|   |   |-- authUseCase.ts
|-- App.tsx
|-- index.tsx

위의 건 폴더및 파일 구조

  1. features/auth/authUseCase.ts : 로그인 비즈니스 로직 Use Case
// features/auth/authUseCase.ts
import { User } from '../domain/user';

export const loginUseCase = async (username: string, password: string): Promise<User> => {
  // 여기에서 실제 로그인 비즈니스 로직을 구현합니다.
  // 예를 들어, 서버로 요청을 보내고 인증된 사용자 정보를 반환합니다.
  // 실패 시 에러 throw
  if (username === 'user' && password === 'password') {
    return {
      id: 1,
      username: 'user',
    };
  } else {
    throw new Error('잘못된 자격 증명');
  }
};
  1. features/auth/authSlice.ts : Redux Toolkit 슬라이스
// features/auth/authSlice.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { loginUseCase } from './authUseCase';
import { AppThunk } from '../../app/store';
import { User } from '../domain/user';

interface AuthState {
  user: User | null;
  error: string | null;
}

const initialState: AuthState = {
  user: null,
  error: null,
};

const authSlice = createSlice({
  name: 'auth',
  initialState,
  reducers: {
    loginSuccess: (state, action: PayloadAction<User>) => {
      state.user = action.payload;
      state.error = null;
    },
    loginFailure: (state, action: PayloadAction<string>) => {
      state.user = null;
      state.error = action.payload;
    },
  },
});

export const { loginSuccess, loginFailure } = authSlice.actions;

export default authSlice.reducer;

export const loginUser = (username: string, password: string): AppThunk => async (dispatch) => {
  try {
    const user = await loginUseCase(username, password);
    dispatch(loginSuccess(user));
  } catch (error) {
    dispatch(loginFailure('로그인 실패: ' + error.message));
  }
};
  1. components/LoginForm.tsx : 로그인 폼 컴포넌트
// components/LoginForm.tsx
import React, { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { loginUser } from '../features/auth/authSlice';
import { RootState } from '../app/store';

const LoginForm: React.FC = () => {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');
  const dispatch = useDispatch();
  const error = useSelector((state: RootState) => state.auth.error);

  const handleLogin = () => {
    dispatch(loginUser(username, password));
  };

  return (
    <div>
      <h2>로그인</h2>
      <div>
        <input type="text" placeholder="사용자 이름" onChange={(e) => setUsername(e.target.value)} />
      </div>
      <div>
        <input type="password" placeholder="비밀번호" onChange={(e) => setPassword(e.target.value)} />
      </div>
      <div>
        <button onClick={handleLogin}>로그인</button>
      </div>
      {error && <div>{error}</div>}
    </div>
  );
};

export default LoginForm;
  1. App.tsx : 앱 진입점
// App.tsx
import React from 'react';
import LoginForm from './components/LoginForm';

const App: React.FC = () => {
  return (
    <div className="App">
      <h1>로그인 페이지</h1>
      <LoginForm />
    </div>
  );
};

export default App;
  1. index.tsx : 앱 초기화 및 스토어 설정
// index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit';
import authReducer from './features/auth/authSlice';
import App from './App';

const store = configureStore({
  reducer: {
    auth: authReducer,
  },
});

export type RootState = ReturnType<typeof store.getState>;
export type AppThunk<ReturnType = void> = ThunkAction<ReturnType, RootState, unknown, Action<string>>;

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);