로그인 페이지를 예시
src/
|-- components/
| |-- LoginForm.tsx
|-- features/
| |-- auth/
| | |-- authSlice.ts
| | |-- authUseCase.ts
|-- App.tsx
|-- index.tsx
위의 건 폴더및 파일 구조
// 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('잘못된 자격 증명');
}
};
// 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));
}
};
// 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;
// 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;
// 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')
);