[모두의 이력서_14일차] Next.js TS에서 로그인 구현 (cors + react-query + axios + recoil)
이제 로그인 페이지 관련 css를 다 만들었으니 이전에 백에서 만든 로그인 라우터로 로그인 기능을 구현해 보겠습니다.
cors 해결하기
클라이언트에서는 http://localhost:3000, 서버는 http://localhostL:5000이라 하면
클라이언트에서 axios.get('http://localhost:8000')하게되면 서로 다른 origin으로 인해 CORS 이슈가 생깁니다.
저는 CORS 미들웨어를 사용하여 CORS를 해결했습니다.
(이게 가장 쉬운 방법입니다!)
1. cors 설치하기
npm i cors
2. express 앱에 적용하기
import express from "express";
import cors from 'cors';
const app = express();
app.use(cors()); //모든 접근 허용
import 해오고 설정해 주시면 됩니다.
axios 세팅하기
로그인 기능을 구현하기 위해 서버와 통신을 해야 됩니다.
서버와 통신하는 방법으로 Axios라이브러리를 사용했습니다.
Aiox 특징
- 운영 환경에 따라 브라우저의 XMLHttpRequest 객체 또는 Node.js의 http api 사용
- Promise(ES6) API tkdyd
- 요청과 응답 데이터의 변형
- HTTP 요청 취소
- HTTP 요청과 응답을 JSON 형태도 자동 변경
1. axios 설치
npm install axios --save
2. axios 세팅
intstance를 설정해 주고 설정해 준 값을 가지고 미리 uri를 작성하려고 합니다.
api
|- index.ts // 인스턴스를 만들고, 인스턴스를 내보냄
|- user.ts // 인스턴스를 import해서, api를 호출하는 함수를 모아 높는 파일(여기서는 /users인 api만 작성 예정)
index.ts는 아래 블로그를 참고해서 작성했습니다.
https://pinokio0702.tistory.com/373
[Axios][업무][베트남🇻🇳] - Axios instance 생성하고 api 요청 함수 작성하는 방법
안녕하세요. 회사에서 베트남 시니어 개발자 코드를 통해 학습한 내용을 일부 기록한 글입니다. axios를 잘 정리해서 사용한 것 같아서 따라서 사용하고 있습니다. 이 코드를 보고 개발하는 프로
pinokio0702.tistory.com
// index.ts
// axios 인스텐스를 만들고, 인스턴스를 내보냄
import axios from "axios";
const instance = axios.create({
baseURL: "http://localhost:5000/api",
});
// 요청 타임아웃 설정
instance.defaults.timeout = 2500;
// 요청 인터셉터 추가
instance.interceptors.request.use(
(config) => {
// 요청을 보내기 전에 수행할 로직
return config;
},
(error) => {
// 요청 에러가 발생했을 때 수행할 로직
console.log(error); // 디버깅
return Promise.reject(error);
}
);
// 응답 인터셉터 추가
instance.interceptors.response.use(
(response) => {
// 응답에 대한 로직 작성
const res = response.data;
return res;
},
(error) => {
// 응답에 에러가 발생했을 때 수행할 로직
console.log(error); // 디버깅
return Promise.reject(error);
}
);
export default instance;
3. user.ts
백에서 로그인 라우터를 만든 거 토대로 user.ts에 API를 만들었습니다.
// 인스턴스를 import해서 api를 호출하는 함수를 모아 높는 파일
import instance from ".";
export const login = async ({
email,
password,
}: {
email: string;
password: string;
}) => {
const result = await instance.post("/users/login", {
email,
password,
});
return result;
};
타입은 나중에... 정리할 예정입니다.
react-query 기본세팅 및 사용하기
react-query는 서버의 값을 클라이언트에 가져오거나, 캐싱, 값 업데이트, 에러핸들링 등 비동기 과정을 더욱 편하게 하는 데 사용됩니다.
1. react-query 설치하기
타입스크립트 사용하시면 아래와 같이 설치해 주어야 됩니다.
react-query 설치
npm i @tanstack/react-query
devtools 설치
npm i -D @tanstack/react-query-devtools
2. _app.tsx에 설정해 줍니다.
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
const _app = ({ Component }: AppProps) => {
return (
<>
<QueryClientProvider client={queryClient}>
<ReactQueryDevtools initialIsOpen={false} />
<Component />
</QueryClientProvider>
</>
);
};
export default _app;
3. react-query mutation 사용하기
mutation 설정
여기서 login은 axios에서 만든 api입니다.
const mutation = useMutation({
mutationFn: login,
onSuccess: (data) => {
console.log(data);
},
});
submit에 넣기
mutation.mutate({ email: email, password: password });
Login.tsx
import { useMutation } from "@tanstack/react-query";
import { validateEmail, validatePassword } from "../utils/regExp";
import { login } from "../api/user";
const Login = () => {
const [warningMsg, setWarningMsg] = useState(false);
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const mutation = useMutation({
mutationFn: login,
onSuccess: (data) => {
console.log(data);
},
});
const onEmailHandler = (e: ChangeEvent<HTMLInputElement>) => {
const { value } = e.target as any;
setEmail(value);
};
const onPasswordHandler = (e: ChangeEvent<HTMLInputElement>) => {
const { value } = e.target as any;
setPassword(value);
};
const onSubmitHandler = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault(); // 리프레시 막기
if (
!email ||
!password ||
!validateEmail(email) ||
!validatePassword(password)
) {
setWarningMsg(true);
}
mutation.mutate({ email: email, password: password });
};
return (
... 생략
);
};
export default Login;
이러고 알맞게 로그인하면 cosole.log(data)에는 아래와 같은 결과가 나옵니다.
이제 유저의 nickName을 로컬에 넣고 리코일에다가도 넣어 로그인 관리를 하겠습니다.
recoil 세팅 및 사용하기
1. recoil 설치하기
npm install recoil
2. _app.tsx에 리코일 설정하기
import { RecoilRoot } from "recoil";
return(
<RecoilRoot>
<Component />
</RecoilRoot>
)
3. recoil-persist 설치
localstorage에 넣어줘서 구현해도 되지만 조금 더 편하게 관리하기 위해 recoil-persist설치하겠습니다.
recoil-persist
recoil persist 영구적으로 사용자가 캐시를 삭제할 때까지 영구적으로 state 관리
쉽게 설명하자면 로그인 성공 시 아래와 같은 효과를 만들기 위해 사용할 예정입니다.
4. 리코일 파일 만들고 설정하기
front
|- recoil
|- user.ts
user.ts
import { atom } from "recoil";
import { recoilPersist } from "recoil-persist";
const { persistAtom } = recoilPersist(); // 페이지가 변경되더라도 상태관리를 유지하기 위해 사용된다.
interface User {
id: string;
nickName: string;
}
export const userAtom = atom<User | null>({
key: "USER_DATA",
default: null,
effects_UNSTABLE: [persistAtom],
});
5. 로그인 설정
import { useSetRecoilState } from "recoil";
const setUser = useSetRecoilState(userAtom);
recoil의 useSetRecoilState를 사용하시면
const [test, setTest] = useState()에서 setTest와 같은 효과가 나타납니다.
import { useRouter } from "next/router";
const Login = () => {
const router = useRouter();
const mutation = useMutation({
mutationFn: login,
onSuccess: (data) => {
setUser(data.data);
router.replace("/");
},
});
}
이제 react-query와 같이 사용하여 로그인을 성공할 경우 setUser에 넣어주고 싶은 데이터를 넣어주시면 됩니다.
마지막으로 로그인하면 메인페이지로 돌아갈 수 있도록 하기 위해 router.replace("/");를 사용해 주었습니다.
push로 이동시키면 history stack에 쌓여서 뒤로가기가 가능하고
replace로 이동시키면 history stack에 안쌓여서 뒤로가기 불가능
최종코드
// Login.tsx
import { ChangeEvent, FormEvent, useContext, useState } from "react";
import Link from "next/link";
import { useRouter } from "next/router";
import { useMutation } from "@tanstack/react-query";
import { useSetRecoilState } from "recoil";
import Logo from "../components/common/Logo";
import { IsDark } from "../context/type";
import { ThemeContext } from "../context/themeContext";
import { validateEmail, validatePassword } from "../utils/regExp";
import { login } from "../api/user";
import { userAtom } from "../recoil/user";
import * as styled from "../styles/Login";
const Login = () => {
const setUser = useSetRecoilState(userAtom);
const router = useRouter();
const { isDark } = useContext(ThemeContext) as IsDark;
const mode = isDark ? "darkTheme" : "lightTheme";
const [warningMsg, setWarningMsg] = useState(false);
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const mutation = useMutation({
mutationFn: login,
onSuccess: (data) => {
setUser(data.data);
router.replace("/");
},
});
const onEmailHandler = (e: ChangeEvent<HTMLInputElement>) => {
const { value } = e.target as any;
setEmail(value);
};
const onPasswordHandler = (e: ChangeEvent<HTMLInputElement>) => {
const { value } = e.target as any;
setPassword(value);
};
const onSubmitHandler = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault(); // 리프레시 막기
if (
!email ||
!password ||
!validateEmail(email) ||
!validatePassword(password)
) {
setWarningMsg(true);
}
mutation.mutate({ email: email, password: password });
};
return (
<styled.LoginWrap mode={mode}>
<styled.LoginCon mode={mode}>
<Logo />
<styled.LoginformWrap>
<form onSubmit={onSubmitHandler}>
<styled.InputBox mode={mode}>
<p>이메일</p>
<input
autoFocus
type="text"
value={email}
onChange={onEmailHandler}
/>
</styled.InputBox>
<styled.InputBox mode={mode}>
<p>비밀번호</p>
<input type="password" onChange={onPasswordHandler} />
</styled.InputBox>
{warningMsg && (
<styled.WarningMsg>
가입되어 있지 않은 계정이거나,
<br />
이메일 또는 비밀번호가 일치하지 않습니다.
</styled.WarningMsg>
)}
<styled.btn type="submit">로그인</styled.btn>
</form>
</styled.LoginformWrap>
<styled.RegisterBox>
<Link href="/Register" as="/auth/register">
<span>아직도 회원이 아니세요?</span>
<span>회원가입 하기</span>
</Link>
</styled.RegisterBox>
</styled.LoginCon>
</styled.LoginWrap>
);
};
export default Login;
로그인 성공-!
참고자료
https://velog.io/@wiostz98kr/React-Express-CORS-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0
React + Express | CORS 설정하기
CORS개념은 이전 포스팅에서 다뤘으므로 생략하겠습니다. 프론트는 http://localhost:3000, 서버는 http://localhost:8000이라고 가정할 때, 프론트에서 axios.get('http://localhost:8000')하게 되면 서로 다른 o
velog.io
https://pinokio0702.tistory.com/373
[Axios][업무][베트남🇻🇳] - Axios instance 생성하고 api 요청 함수 작성하는 방법
안녕하세요. 회사에서 베트남 시니어 개발자 코드를 통해 학습한 내용을 일부 기록한 글입니다. axios를 잘 정리해서 사용한 것 같아서 따라서 사용하고 있습니다. 이 코드를 보고 개발하는 프로
pinokio0702.tistory.com
https://inpa.tistory.com/entry/AXIOS-%F0%9F%93%9A-%EC%84%A4%EC%B9%98-%EC%82%AC%EC%9A%A9
📚 AXIOS 설치 & 특징 & 문법 💯 정리
Axios 라이브러리 Axios는 브라우저, Node.js를 위한 Promise API를 활용하는 HTTP 비동기 통신 라이브러리 아다. 쉽게 말해서 백엔드랑 프론트엔드랑 통신을 쉽게하기 위해 Ajax와 더불어 사용한다. 이미
inpa.tistory.com
https://velog.io/@gkj8963/React-Query
React Query(TypeScript + React v18)
0 . installnpm i react-queryimportindex.jsimport { QueryClient, QueryClientProvider } from "react-query";queryClient 생성 후 provider에 넣어주기fetcher 함수 만들기
velog.io
react-query 개념 및 정리
react-query 개념 및 정리, react, react16, hook, useState, useRef, useMemo, useEffect, useReducer, useCallback, useQuery 동기적으로 실행
kyounghwan01.github.io
[42byte] Recoil로 로그인 상태 관리하기
42byte는 42서울 학생들만 이용할 수 있는 커뮤니티다. 따라서 OAuth 프로토콜을 이용한 Access Token이 필요하다. 서버에서 url에 담아서 보내준 Access Token으로 로그인을 정보를 관리하는 방법에 대해
velog.io
https://im-designloper.tistory.com/102
[Next.js] next/router 사용하기 (공식문서 내용 정리)
next/router 사용하기 router 객체에 접근하기 위해서 useRouter를 사용한다 1. import 후 import { useRouter } from 'next/router' 2. 아래의 형태로 사용 const router = useRouter() 아래처럼 console에서 router 객체에 대한 정
im-designloper.tistory.com
https://www.youtube.com/watch?v=joMOF30x_NY
https://www.youtube.com/watch?v=yAodvlX7oug