🐈오늘 배운 것
✔️Next.js Auth : iron-session으로 유저 인증하기
Next.js 에서 iron-session 라이브러리를 사용해 유저를 인증하는 로직을 복습했다. 로직 자체는 간단하다.
1. 유저가 이메일로 받은 인증번호를 입력한다.
2. 해당 인증번호와 일치하는 유저 데이터를 찾는다.
3. 데이터를 찾는데 성공하면 쿠키를 통해 유저 아이디를 암호화하여 응답한다.
4. 다음 요청에서 쿠키와 함께 서버에 요청을 보내면, 쿠키를 확인하여 인증 여부를 결정한다.
iron-session은 클라이언트쪽에 데이터를 저장한다는 점에서 토큰과 비슷하지만 payload가 암호화 된다는 점이 차이점이라고 한다. 또한 쿠키 생성 및 응답을 위한 helper 함수(withIronSessionApiRoute) 를 제공해주고 있어서 편리하게 사용할 수 있다. 전에 jwt 토큰을 express 서버에서 직접 구현하는 실습을 한 적이 있는데, iron-session을 사용하면서 헬퍼 함수를 이용해 많은 부분을 간소화했다고 느꼈다.
먼저 클라이언트 쪽 로직의 일부분.
- useForm 에서 input 에 등록할 register 함수와 submit 함수를 가져와준다. 각 form 별로 실행할 함수가 달라야한다.
- 그리고 fetch를 통해 받아올 토큰 로딩 상태와 토큰 데이터 상태 등 만들어둔 fetch 커스텀 훅인 usePost로 가져온다. 이 부분에서 어이없게 많이 헤맸다. 구조분해할당에서 이름을 변경하여 정의하려면 "원래 변수 이름 : 바꿀 변수 이름" 의 순서로 작성해야 하는데, 반대로 작성하는 바람에 상태가 필요한 함수에서 제대로 참조를 할 수가 없었다. 바본가ㅜ
// token 인증을 위한 useForm
const {register: tokenRegister, handleSubmit: tokenHandleSubmit} = useForm();
// token 데이터 및 fetch 요청 함수
const [
confirmToken,
{loading: tokenLoading, response: tokenResponse, error: tokenError},
] = usePost('/api/users/confirm');
// submit token
const onValidToken = token => {
// 인증번호 발급 끝나기 전 버튼 클릭 방지
if (tokenLoading) return;
confirmToken(token);
};
// 로그인 완료 후 home 으로 리다이렉션
const router = useRouter();
useEffect(() => {
if (tokenResponse?.ok) {
router.push('/');
}
}, [tokenResponse, router]);
사용자가 입력한 인증 번호를 검증하는 api 로직의 일부분은 아래와 같다.
// prisma client 의 findUnique 와 include 를 함께 사용하면,
// 특정 레코드와 관계있는 다른 레코드를 함께 조회할 수 있다.
const foundToken = await client.token.findUnique({
// token 테이블에서 입력된 token 과 일치하는 데이터 찾기
where: {
payload: token,
},
// 그리고 userId 로 연결되어 있는 user 테이블의 데이터를 포함하기
include: {
user: true,
},
});
iron-session을 통해 쿠키를 생성하고 응답하려면 withIronSessionApiRoute 라는 함수에 세션 로직과 쿠키 옵션을 함께 전달하여 실행해야 한다. 니꼬쌤은 아래와 같이 withSession 이라는 헬퍼 함수를 만들어서 핸들링했다.
import {withIronSessionApiRoute} from 'iron-session/next';
const cookieOptions = {
cookieName: 'podosession',
password: process.env.COOKIE_PASSWORD,
// secure : HTTPS (production ONLY)
// cookieOptions: {
// secure: process.env.NODE_ENV === "production",
// },
};
// withApiSession 커스텀 함수를 import 하여 로직을 전달하면,
// 위와 같이 작성된 쿠키 옵션을 함께 전달하여 iron-session 헬퍼 함수를 실행한다.
export function withApiSession(fn) {
return withIronSessionApiRoute(fn, cookieOptions);
}
쿠키에 user id 를 저장하는 방법은 아래와 같다.
// session에 user 데이터 저장
req.session.user = {
id: foundToken.userId,
};
await req.session.save();
// 위 과정을 거치면 다음 요청에서 아래와 같이 쿠키에 접근할 수 있다.
console.log(req.session.user); // {user : {id : ...}}
쿠키에 데이터를 저장했다면 인증번호 토큰은 필요가 없기 때문에 지워줄 수 있다.
// 세션이 발급되었으면 token은 필요가 없으므로 삭제한다.
await client.token.deleteMany({
// token에서 userId 속성이 현재 userId와 일치하는 레코드는 모두 삭제
where: {
userId: foundToken.userId,
},
});
delete 와 deleteMany 옵션이 있는데, 후자는 where 절에서 userId 항목이 해당 데이터를 포함하는 모든 레코드를 삭제한다.
연습을 위해 한 로직에 필요한 내용을 전부 작성했다가 재활용 할 부분을 분리하는 연습을 했다. 복습이지만 완벽하게 이해하지 못한 부분이 많아서 이상하게 에러가 나는 부분이 많았다. 그 중 거의 대부분은 프로퍼티에 잘못 접근하거나(예를 들면 session에 접근할 때 req.session 이 아니고 req.body.session 이렇게 접근한다던지...) 구조분해할당 이름 변경할 때 순서를 거꾸로 쓴다던지... 그런데 이런데서 실수가 발생하면 어떤 에러메시지도 나오지 않기 때문에 디버깅에 정말 많은 시간을 할애할 수밖에 없는 것 같다... 분명히 로직이 다 맞는데 undefined가 나온다는건 문법이 틀렸을 가능성이 매우 높은 것 같다.
🐈더 공부할 것
1. 유저 권한부여
2. 비동기 함수
🐈오늘의 느낀 점
백엔드 부분은 거의 다뤄본 적이 없다보니 더 어렵고 이해하는데 시간이 오래걸리는 것 같다. 그래도 api 를 만들고 요청, 응답하는 연습을 하는게 조금 익숙해진 것 같다. 뭔가 만든다는 느낌보다는 코드를 보고 이해하고 공부한다는 느낌.
저녁에 집에서 집중이 잘 안되어서 집 근처 카페를 갔다. 장소를 바꾸니 훨씬 집중이 잘 되는 느낌... 집에서는 고양이도 있고 밤만 되면 방해를 엄청해서 집중이 쉽지 않은데. 100% 할 일을 다 마무리하진 못했지만 큰 부분을 끝내서 홀가분하다.
'TIL' 카테고리의 다른 글
[TIL] 2022-1012 (0) | 2022.10.12 |
---|---|
[TIL] 2022-1011 (0) | 2022.10.12 |
[TIL] 2022-1005 (0) | 2022.10.05 |
[TIL] 2022-1003 (0) | 2022.10.03 |
[Day 73] 2022-0928 (1) | 2022.09.29 |