Q&A
- refreshToken, accessToken 같은것들을 최대한 안전하게 저장하는 방법이 어떤게 있을까요?? 로컬스토리지, 쿠키 등등 현업에서는 어떻게 진행되는지도 궁금해요~!
- 정답이 정해져있지 않다. Next 프레임워크를 사용하면 색다른 방법이 있는데, 그게 아니라면 프론트에서 할 수 있는 역량이 제한되어 있다. 100% 완벽하게 보안적으로 처리하는 것은 불가능하다.
- Next부터는 프론트엔드 서버가 존재하므로 프론트엔드 서버를 통해 쿠키나 스토리지에 저장해서 외부에서 접근하지 못하도록 제어 가능
- 회원 정보 요청할 때 비밀번호를 요청 body에 보내야 한다고 하는데 맞는 방식일까요?
- 비밀번호는 암호화되어야 함. 비밀번호를 직접 리턴해서는 안 됨
- 암호화된 비밀번호는 복호화할 수 없도록 해야 함.
- 민재: recoil 같은 외부 상태관리 라이브러리는 언제 쓰는 게 좋을까요? 현재 프로젝트에서는 props 전달 깊이가 2 이상일 때부터 쓰고 있는데 적당한지 궁금합니다!
- 상태관리는 크게 쓸 일이 많지 않다.
- 유저 정보 관련된 것, 로그인 여부를 확인할 때 주로 사용함.
- 페이지 이동 시 정보를 유지해야 할 경우에는 상태관리가 필요하다.
- react-query로 그때그때 필요할 때 요청해도 대부분의 서비스에서 소화 가능함
- 토큰 만료 시간도 백엔드에 요청하는 게 좋을까요?
- 만료 시간을 알고 있는 게 토큰 만료 여부를 체킹하는 데 도움이 됨
중간 코드 리뷰
- 중간 멘토 리뷰 때 다른 팀 코드 본 게 맞더라구용..ㅎㅎ
- 페이지 컴포넌트가 하나의 요소만 포함한다면 굳이 분리를 하지 않아도 될 것 같다.
- 동일한 더미 데이터를 쓰는 곳도 있을텐데 페이지마다 만드셨네용
- 어차피 지워질 데이터라고 생각해서 페이지마다 구현하다보니 일단 따로 만들긴 했습니당..ㅎㅎ
- dummy 폴더를 만들어놓고 가져다쓰는 게 데이터 관리하는 게 쉽다. 어차피 나중에 없어질 데이터이긴 하지만 중복 더미 데이터를 줄일 수 있고 개발 시간을 단축할 수 있다.
- components/route 이름 개선 필요!
- css in js 방식으로 컴포넌트를 만드는 것과 인라인 스타일링은 리액트 렌더링에 차이를 가져온다.
- 인라인으로 하면 렌더링하는 속도가 조금 느리긴 한데 눈에 띌 정도는 아니다. 그래도 가급적 컴포넌트로 만들어서 적용하자.
axios interceptor 활용 및 토큰 만료 여부 확인하는 참고 코드 공유
import Axios, { AxiosInstance, AxiosRequestConfig } from ‘axios’; import { createContext, useContext, useMemo } from ‘react’; import { refresh, refreshErrorHandle } from ‘lib/refresh’; import useAppContext, { actions } from ‘contexts/App’; import { API_HOST } from ‘constants/Api’; import { toast } from ‘react-toastify’; export const AxiosContext = createContext<AxiosInstance>(undefined); export function AxiosProvider({ children }: React.PropsWithChildren<unknown>) { const { store, dispatch } = useAppContext(); const axios = useMemo(() => { const axios = Axios.create({ baseURL: API_HOST, }); // NOTE: 요청 인터셉터 추가 axios.interceptors.request.use((config: AxiosRequestConfig) => { return refresh(config, dispatch, actions, store); }, refreshErrorHandle); // NOTE: 응답 인터셉터 추가 axios.interceptors.response.use( function (response) { // 응답 데이터를 가공 // ... return response; }, function (error) { // 오류 응답을 처리 // ... if (error.response) { toast.error(error.response); if (error.response.status >= 400 && error.response.status < 500) { // ANCHOR: TOKEN 파기 if (error.response.statusText === ‘Unauthorized’) { dispatch(actions.logout()); } } } return Promise.reject(error); } ); return axios; }, [store]); return ( <AxiosContext.Provider value={axios}>{children}</AxiosContext.Provider> ); } export default function useAxiosContext() { return useContext(AxiosContext); }
import { Action, Actions, Store } from ‘contexts/App’; import axios, { AxiosRequestConfig } from ‘axios’; import { API_HOST } from ‘constants/Api’; import moment from ‘moment’; const refresh = async ( config: AxiosRequestConfig, dispatch: React.Dispatch<Action>, actions: Actions, store: Store ) => { const refreshToken = store.RefreshToken; const expireAt = store.ExpiresAt; let token = store.Authorization; // 토큰이 만료되었고, refreshToken 이 저장되어 있을 때 if (moment(expireAt).diff(moment()) < 0 && refreshToken) { const body = { refresh: refreshToken, }; // 토큰 갱신 서버통신 const { data } = await axios.post(`${API_HOST}/users/token/refresh/`, body); token = data.access; dispatch(actions.setToken(data.access, data.refresh)); } if (!!token) { config.headers[‘Authorization’] = `JWT ${token}`; } return config; }; const refreshErrorHandle = (err: any) => { // Cookie.remove(“refreshToken”); }; export { refresh, refreshErrorHandle };
- 글로벌 스타일 세팅하는 방법 소개(관련 레포)
- Date 객체 사용
- 글로벌 서비스를 하게 되면 현지 시간을 기준으로 날짜가 나오기 때문에 문제가 될 수 있다.
- 동일한 시간을 반드시 보여줘야 한다면 다른 방식을 활용하는 게 좋음
- scrollTo의 경우 Safari 같은 브라우저에서 작동하지 않을 수 있다.
- .nvmrc 세팅 좋네요~!
쉴 땐 쉬어야 합니다~! 잠은 주무시면서 하세용ㅎㅎ 개발이라는 마라톤을 계속 잘 달리기 위해서라면 잘 쉬어야 합니다
뮤지컬 러버 기동님