-
항해 대나무숲 프로젝트 (Lv4~5)카테고리 없음 2023. 5. 4. 02:01
항해 대나무숲
항해99 주특기언어 과제 Lv.4
개발기간 : 3일
프로젝트 소개
주특기언어로 강의를 듣고 CRUD,로그인, 회원가입 구현
주제는 자유여서 항해에 관련된 주제로 정하면 좋을 것 같아서 결정.항해에 대해 불만이라던가 좋은점을 익명으로 작성해서 부담없이 소통할 수 있다.
사용한 라이브러리
styled components
react - router - dom
json-server
axios
react-query
화면구성
주요기능
게시글 입력 기능
로그인 여부 확인 후 게시글 상세페이지 렌더링
게시글 수정, 삭제
로그인, 로그아웃 기능 구현
회원가입 기능 구현
트러블 슈팅
props 조건문
- 오류가 발생한 경로
- header컴포넌트를 이용해서 상단에 header가 어떤 컴포넌트에도 보이도록 공통적으로 줬는데 그렇게 하다보니 header에 이전으로 가기 버튼을 만들어서 경로에 -1을 해주니까 잘 되돌아가기는 하나 최초시작이였던 Home컴포넌트에서 뒤로가기버튼도 적용이 되다보니 눌렀을때 이상한 경로로 가졌다.
2. 오류를 해결하기 위해 시도해본 것들
해결중 문제점jsx문법에서는 if문 자체를 사용할 수 없다.
- ome컴포넌트에서 props로 true를 보내줘서 이전으로 가기버튼에 조건문으로 true가 아닐때만 버튼이 활성화되도록 한다.
- 오류 해결 방법
{!Visible && <Button>이전으로</Button>}
- Visivle을 props로 내려주는데 값을 true로 내려줘서 Home에만 true가 되어있다. 그렇게 때문에 Visibls가 true가 아닌것만 button이 보여지게 했다.
삭제버튼
- 오류가 발생한 경로
- 삭제하기 버튼을 눌렀을때 db에서 정상적으로 삭제는 되지만 이전 페이지로 안가져서 뒤로가기를 눌러야만 이전페이지로 가졌다.
- 오류를 해결하기 위해 시도해본 것들
const CONFIRM_MESSAGE = `[삭제 확인]\n\n게시물을 정말로 삭제하시겠습니까?\n삭제를 원치 않으시면 [취소] 버튼을 눌러주세요.`; const navigate = useNavigate(); const onClickRemoveButtonHandler = (id) =>{ if(window.confirm(CONFIRM_MESSAGE)){ mutation.mutate(id) navigate("/main") } }
실패이유 useNavigate() 훅은 조건부로 호출될 수 없다 이다.
이렇게 위로 다 올려줬는데 이렇게 또 에러가 뜬다.const navigate = useNavigate(); const mutation =useMutation(removeBoards,{ onSuccess: ()=>{ QueryClient.invalidateQueries("posts") navigate("/main") } })
const navigate = useNavigate();
- 삭제하기 버튼 onClick 이벤트를 이용해서 confirm으로 삭제를 정말 할건지 확인 후 완료를 누르면 useNavigate를 사용해서 “/main”페이지로 이동되도록하려고함.
- 오류 해결 방법이것만 다시 온클릭 함수로 넣어줬더니 정상적으로 페이지 이동이 된다.
개인적인 추측으로는 useNavigate를 mutation보다 밑에서 선언해서 그런게 아닌가 싶다 . navigate("/main")
- 오류가 발생한 경로
- 수정하기 하는데 수정할때 원래 있던 값이 그대로 input에 불러오려고 했는데 동적라우팅으로 들어온 페이지에서 foundDate에서 title값을 초기값으로 쓰려고했는데 useState는 맨위에 선언되어야 하기때문에 에러가 난다.
- 오류 해결 방법
- 일단 useState의 초기값을 빈값으로 선언하고 수정하기 버튼이 눌렸을때 setState로 상태값을 foundDate.title값으로 바꿔줘서 input창이 생길때 이전 값이 보여지게 했다.
회원가입 시 'id 또는 password가 존재하지 않습니다.'
- 오류가 발생한 경로
- 회원가입 할때 401에러가 나오면서 'id 또는 password가 존재하지 않습니다.'가 나오는데 회원가입에서 나온다는게
이해가 되지 않았다. - 오류를 해결하기 위해 시도해본 것들
const handleSignUp = async (props) => { try { const response = await instance.post(`/register`, { email: props.email, password: props.password }) return response.data } catch (error) { if (error.response.status === 401) { return error.response.data } } }
- 에러메세지를 잘못 가져온건 아닌지 로그인 서버경로에 보낸건 아닌지 확인,
props를 잘못 받아온건 아닌지 확인
- 오류 해결 방법
- key값을 email이 아니고 id로 보내야 한다는걸 발견했고 email을 id로 변경했더니 정상적으로 201코드가 반환됬다
이게 명세서의 중요성인것 같다.
회원가입이 완료 및 실패했을때 UI에 표시
- 오류가 발생한 경로
- 회원가입이 완료 되었을때는 '회원가입 성공'이라는 alert가 잘 뜨지만 실패했을때에도 '회원가입 성공'이 뜬다.
- 오류를 해결하기 위해 시도해본 것들
- onSuccess를 지우고 onError만 띄우려고 하면 아예 어떤 팝업창도 뜨지 않았다.
- onSuccess의 매개변수에 error를 넣어서 console.log()에 찍어봤을때 에러메세지가 뜨는걸 확인했다.
const mutation = useMutation(handleSignUp,{ onSuccess: () =>{ alert('회원가입 성공') }, onError:(error)=>{ alert(error.message) } })
- 오류 해결 방법
const mutation = useMutation(handleSignUp,{ onSuccess: (error) =>{ if(error){ alert(error.message) } else { alert('성공') navigate('/login') } } })
- onSuccess자체에 조건문을 줘서 성공시, 실패시를 나눠서 했더니 원하는 요청으로 뜨는걸 확인할 수 있었다.
로그인 완료 및 실패했을때 UI에 표시
- 오류가 발생한 경로
- 위에 했던 방식으로 로그인을 하려고 했더니 로그인은 성공시에 토큰값을 반환해줘서 그런가 undefind만 떴다.
- 오류 해결 방법
이렇게 해주면 위에 회원가입과 마찬가지로 잘 작동되긴 하나 onError로 가져와야하는게 맞을거라고 생각을 하니 해당 코드는 가독성이 너무 떨어진다고 생각이 들었다.const mutation = useMutation(handleLogin,{ onSuccess: (data) =>{ if(data.token){ Cookies.set('token',data.token,{ expires: expiresInSeconds }) navigate('/') }else{ setWarningMessage(data.message) } } })
아직 해결하지 못해서 해결 후 다시 리드미를 수정해야 할듯 하다. - onSuccess에 error는 가져오지 못하나 data는 잘받아와지는 걸 확인함.
data.token이 담겨저 온다면 또는 없다면으로 조건문을 줘서 성공 및 실패를 띄워줬다.
과제를 하면서 추가로 알게된 내용
내가 원했던 기능은 목차에 원하는글을 눌렀을때 로그아웃 상태라면 상세페이지가 보이지 않게 하고 싶었다.
seQuery Suspense 이슈
useQuery를 여러개 한번에 가져오려는 경우 동기적으로 가져오다보니 순서에 맞게 마지막에 온것만 불러온다.
내가 원하는건 동일한 시점에 받아오고싶었다.
const [Boards,user] = useQueries([ {queryKey:'getBoards',queryFn:getBoards}, {queryKey:'getLoginData',queryFn:getBoards} ]) const isLoading = Boards.isLoading || user.isLoading; const isError = Boards.isError || user.isError; const data = [Boards.data, user.data];
이렇게 해도 여전히 user.data는 undefined가 떴고 찾아보니 user에는 data가 있는게 아니였다…
{status: 'success', isLoading: false, isSuccess: true, isError: false, isIdle: false, …}
user만 콘솔에 찍어보면 위에처럼 나오는걸 볼 수 있다. 다만 이렇게 했을때의 문제점은 로그아웃을 하게되면 아예 목차부분이 뜨지를 않는다…
그래서 이걸 개선할 방법은 매니저님께 조언을 구해서 state값으로 따로 빼주던가 아니면 useQuery로 다시 바꿔서 useQuery를 즉시 실행하게 설정해주라고 했다.
시도한 방법이 처음에는 목차에서 클릭시 이벤트로 주려고 햇으나 실패했고 redux로 관리하고 싶었으나 redux에 대한 개념이 아직 많이 잡히지 못해서 어떻게 해야 리덕스로 나눠서 할 수 있을지 감도 오지 않았다.
해결방법 : detail 컴포넌트에서 단일 조회할때 토큰의 여부를 확인하고 없으면 조회를 못해오도록 했다.
그다음 만약 조회를 못해올 경우 detail컴포넌트에서 '로그인 이후 내용 확인이 가능합니다'가 보여지도록 했다.
두번째 문제점으로는 token 유효시간이 10분으로 되어있어서 10분이후에는 로그아웃이 되고 재로그인이 필요하도록 했어야 했는데
쿠키에 제한시간을 주기위해서
사용하고 있던 라이브러리 js-Cookey를 사용해서 하려했더니 그건 초로 못주고 일수로만 줄 수 있다고 한다.
여러가지 방법이 있었지만 내가 선택한건 현재 시간을 가져와서 거기에 실행할 시간을 더해줬다.
const expiresInSeconds = new Date(new Date().getTime()+ 10*60*1000) const mutation = useMutation(handleLogin,{ onSuccess: (data) =>{ if(data.token){ Cookies.set('token',data.token,{ expires: expiresInSeconds }) navigate('/') }else{ setWarningMessage(data.message) } } })
프로젝트를 하면서 진짜 내가 너무 무지하다고 느끼면서도 뭔가 기능을 어떻게든 하나하나 구현해 나가고 있다는 부분이 너무 뿌듯했다.
아직 CSS는 너무 무난하고 비슷해서 어떤 프로젝트를 하든 비슷한 느낌을 내는점이 아직 너무 아쉽다고 생각한다.
좀 더 많은 기능을 넣고싶긴 한데 시간도 부족하고 아직 어떻게 코딩해야할지 생각이 안잡힌다.
공부를 더 해서 더 많은 기능을 추가 할 수 있는 개발자가 되고싶다.