회원가입, 로그인 목업 페이지를 만들고 로그인 기능구현을 먼저 시작 하였다. 서버에서 acessToken
과 refreshToken
이 들어오는것을 확인 했고 이를 이용해 로그인 로직을 작성 했다.
로컬 스토리지를 다룰 함수들이 들어있는 파일을 만들었다.
export const setLocalStorage = (key: string, token: string) => {
localStorage.setItem(key, token);
};
export const deleteLocalstorage = (key: string) => {
localStorage.removeItem(key);
};
export const getLocalstorage = (key: string) => {
return localStorage.getItem(key) as string;
};
로컬스토리지에 토큰이 있는지 유무에 따라 로그인 상태를 관리할 것이기 때문에 리덕스 툴킷을 활용해 state를 관리 해줬다.
import { createSlice } from "@reduxjs/toolkit";
import { getLocalstorage } from "../util/localStorage";
interface LoginState {
isLogined: boolean;
acesstoken: undefined | string;
refreshtoken: undefined | string;
}
interface Action {
payload: { acessToken: string; refreshToken: string };
}
const refreshToken = getLocalstorage("refreshToken");
const acessToken = getLocalstorage("acessToken");
// 로그인 상태의 초기값을 결정하는 코드
const initialState: LoginState = {
isLogined: refreshToken && acessToken ? true : false,
acesstoken: acessToken ? acessToken : undefined,
refreshtoken: refreshToken ? refreshToken : undefined,
};
export const loginSlice = createSlice({
name: "checkingLogin",
initialState,
// 로그인 및 로그아웃을 처리하는 액션
reducers: {
login: (state, action: Action) => {
state.isLogined = true;
state.acesstoken = action.payload.acessToken;
state.refreshtoken = action.payload.refreshToken;
},
logout: (state) => {
state.isLogined = false;
state.acesstoken = undefined;
state.refreshtoken = undefined;
},
},
});
export const { login, logout } = loginSlice.actions;
export default loginSlice;
로그인을 할 페이지에 서버와 데이터 통신에 대한 로직을 작성 해주었다.
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
// 유효성 검사를 위한 boolean값
const [isValid, setIsValid] = useState({
isEmail: false,
isPassword: false,
});
const dispatch = useDispatch();
const isLogined = useSelector((state: RootState) => {
return state.loginSlice.isLogined;
});
const navigate = useNavigate();
// loginSlice.ts에서 설정해 놓은것 처럼 서버에서 토큰을 보내주면
// isLogined값이 true로 변하고 로그인에 성공하면 navigate를 이용해 홈으로 보내준다.
if (isLogined) {
navigate("/");
}
const handleLogin: React.FormEventHandler<HTMLFormElement> = (e) => {
e.preventDefault();
axios
// post 메서드를 이용해 user의 email, password를 보내준다.
.post("/user/login", {
userName: email,
password: password,
})
// 서버에서 header로 코튼을 보내줄것이기 때문에 'heaeders.' 로 토큰을 받는다.
.then((res) => {
const acessToken: string | undefined = res.headers.authorization;
const refreshToken: string | undefined = res.headers.refresh;
const userId = res.data.userId;
// 서버가 토큰을 보내줬다면 타입이 string이 될 것이기 때문에 if문을 사용 해줬다.
if (typeof acessToken === "string" && typeof refreshToken === "string") {
// loginSlice.ts에 있는 login()을 이용해 initialState값을 변경 해준다.
// setLocalStorage를 이용해 로컬스토리지에 토큰과 유저id를 저장 해준다.
dispatch(login({ acessToken, refreshToken }));
setLocalStorage("acessToken", acessToken);
setLocalStorage("refreshToken", refreshToken);
setLocalStorage(userId, userId);
} else {
window.alert("로그인에 실패하였습니다.");
}
})
.catch((err) => {
const errMessage = (err.response as AxiosResponse<{ message: string }>)?.data.message;
window.alert(errMessage);
});
};