넘블에서 진행한 연계 챌린지에 참여하였습니다!
✔ 프로젝트 소개
저희 팀에서는 동물에 관한 짧은 영상을 올리는 숏폼을 주제로
자신의 반려동물을 자랑하고 싶은사람! 혹은 반려동물을 키우고 싶지만 여건이 되지않은 사람들을 위해
영상을 직접 올리거나 모아 둘 수 있는 서비스를 기획하였습니다.
✔ 역할 담당
저는 이번 프로젝트에서 로그인 / 회원 가입 / 마이 페이지 / 관심 영상 조회 등 회원 계정과 관련된 부분을 담당하여 개발 하였습니다. 제가 구현한 api 명세는 다음과 같습니다.
✔ 사용한 라이브러리 와 이유
- 프레임워크 : React, Next.js
- style : postCSS
- 상태관리(server) : react-query
- 상태관리(client) : redux-toolkit
SSR까지 구현이 목표였기에 SSR을 간편하게 할 수 있는 next.js 프레임워크를 사용하였고 server state관리를 위해 react-query를 사용하였습니다. 외에도 client-side에서 컴포넌트간에 유지되어야 하는 상태가 있어 (모달 창이나 에러메세지 컴포넌트) 그 부분은 redux-toolkit을 사용해 관리 하였습니다.
✔ 주요 코드에 대한 설명
프로젝트 내의 코드를 전부 리뷰하기엔 너무 양이 많아 로그인 부분만 우선 간단하게 리뷰하겠습니다.
우선 프로젝트를 진행하며 공부했던 이론을 정리한 글 입니다.
2022.04.25 - [CS] - 사용자 로그인 이해하기 (쿠키와 세션)
2022.04.30 - [CS] - 토큰 기반의 로그인 인증 방법 이해하기 (JWT)
🤔 로그인 로직
- JWT 기반의 인증 방법으로 로그인을 진행 했고, 쿠키 - 세션 방식의 로그인만 구현해본 경험이 있고, 토큰 방식의 로그인자체를 공부해 보는게 처음 이였기 때문에 좋은 코드에 대해서 고민을 많이 했습니다.
- 보안과 토큰 보관에 관해 벡엔드 개발자 분에게 질문을 많이 드리고 도움을 많이 받았습니다.
💡 토큰 관리
효율적인 토큰 관리를 위해 refresh 토큰은 scure httpOnly의 쿠키로 저장하고 access 토큰은 프론트로 리턴받아 프론트에서 핸들링하는 방식을 사용하기로 하였습니다.
- 프론트에서 로그인 유지나 SSR을 위해 access token을 가지고 핸들링 하는 것이 좋을 것 같다고 판단하여 위 방식을 선택했습니다.
- 진행 하면서 아쉽게도 https 방식이 아닌 이상 secure 옵션의 cookie를 브라우저내에 저장하는 부분에서 이슈가 있었고 시간관계상 https를 적용하고 로그인 로직을 수정할 시간이 없어 refresh token과 access token을 둘다 리턴 받아 프론트에서 토큰 만료 시 재요청을 하는 방식으로 구현을 완료했습니다.
토큰 만료 확인과 재요청 코드
import axios from "axios";
axios.defaults.baseURL = "http://3.36.157.185:80/api";
axios.defaults.withCredentials = true;
const client = axios.create({
baseURL: "http://3.36.157.185:80/api",
withCredentials: true,
});
client.interceptors.response.use(
(response) => {
return response;
},
async (error) => {
if (error.response && error.response.status === 401) {
const originalRequest = error.config;
const refresh = localStorage.getItem("refresh");
if (!refresh) {
return Promise.reject(error);
}
const data = await axios
.get("/refresh-token", {
headers: {
Authorization: refresh!,
},
})
.then((response) => response.data);
const { accessToken, refreshToken } = data;
localStorage.setItem("access", accessToken);
localStorage.setItem("refresh", refreshToken);
client.defaults.headers.common.Authorization = `${accessToken}`;
originalRequest.headers.Authorization = `${accessToken}`;
return client(originalRequest);
}
return Promise.reject(error);
}
);
export default client;
✔ 추후 리팩토링 사항
아쉽게도 시간이 부족해 구현하기 급급해 좋은 코드에 대해 고민할 시간이 너무 부족 했습니다. 챌린지 마감은 끝났지만 배포 후에 발견되는 에러와 중복되고 지저분한 코드들 리팩토링을 다같이 하기로 결정했습니다. 😁
제가 구현한 부분 중에는
- 빠르게 구현하느라 데이터 로딩 하는 부분 페이지 여기 저기에 복사 / 붙여 넣기로 구현되어 잇습니다. 이 부분 hooks로 더 깔끔하게 구현할 수 있지 않을까 생각합니다.
- 에러 메세지, 성공 메세지를 토스트 메세지 형식으로 띄워 주는 hooks를 만들었는데 페이지를 라우팅 하면서 메세지 open하는 action을 dispatch 하는 방식으로 동작하는데 에러 혹은 성공을 해당 페이지에서 일일이 dispatch 해주고 넘어갈 페이지로 라우팅 해줘야 합니다. hooks에 로직을 추가 해서 더 좋을 코드로 만들 수 있지 않을까 고민해 보고 싶습니다.
- react-query라는 라이브러리를 처음 사용해봤는데 아무래도 활용을 100% 하지 못한 것 같아 아쉽습니다. 로그인 성공 시 user라는 key값으로 유저 정보를 queryClient에 넣어 주는 방식으로 하긴 했는데 이 데이터를 캐싱하고 다른 페이지에서 재사용하는 부분은 라이브러리 기능을 제대로 활용하지 못한 것 같아 더 공부해보고 싶습니다.
- 영상 삭제에 event 버블링을 막긴 했는데 그래도 자식 노드에 이벤트가 걸려서 다행이 deps가 자식 노드 하나 밖에 없어서 부모 - 자식 노드에 모두 id 값을 줘서 임시로 영상 삭제 이벤트를 캐치 했습니다. 시간이 없어 버블링과 캡쳐링 관련한 메소드와 에러 발생 원인을 정확히 파악하고 효율적인 방법으로 이벤트를 핸들링하고 싶습니다.
지금 생각나는게 이정도고 아마 코드를 천천히 보면서 찾으면 급하게 만든 코드를 더 찾을 수 있을 것 같습니다. 추후 팀원들과 에러를 해결하면서 이 부분도 수정하고 싶습니다.
✔ 소감
- 팀 프로젝트 경험이 없었는데 다른 팀원들의 생각과 코드를 보며 많이 배울 수 있었던 것 같습니다.
- 대략적인 프로젝트 플로우가 정해진 챌린지 였지만 구체적인 기획 부터 배포까지 하나의 프로젝트 플로우를 경험 할 수 있어 좋았습니다.
- 제가 부족한 부분이 뭔지 알 수 있었고 성장할 수 있었던 느낌이 들어 좋은 경험이였습니다.
'react' 카테고리의 다른 글
CSS-in-JS vs CSS-module vs Post CSS 차이 구분하기 (0) | 2022.09.02 |
---|---|
Swiper Navigator(화살표) 바깥으로 빼기 (0) | 2022.07.21 |
[React 상태관리] react-query 입문 하기 (0) | 2022.04.13 |
react 무한스크롤 구현하기(react-intersection-observer, createAsyncThunk 사용) (0) | 2022.04.07 |
[React 상태 관리] useReducer 사용해 리팩토링 하기 (+ custom hook) (0) | 2022.03.30 |
댓글