-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[ 4주차 기본,심화 과제 ] 라우팅과 서버통신 #10
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR을 엄~~청 상세하게 써 줘서 승희가 어느 부분에서 고민하면서 코드를 짰는지 집중해서 볼 수 있었어요 !!! 그리고 고민한 흔적이나 로직 설계할 때 api분리하려고 머리쓴게 너무너무 잘 보여서 감탄하면서 보았습니당
나도 항상 view랑 side effect를 분리하여서 작성하려고 하는데 어느정도로 분리하여야 하는지 (?)에 대한 기준이 늘 모호해서 제대로 분리가 안되는 듯 하는데 승히 코드 보고 인사이트 많이 얻어갑니다 ~😘
바빴을텐데 수고 많았어요 🥹💗
display: grid; | ||
grid-template-columns: auto 23rem; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오와 이렇게 하면 됐구나 ...
나는 이 생각을 못하고 중복버튼이 있는 경우를 따로 생각해서 조건부 width 지정해줬는데 !
뭔가 styled component를 사용하려니 기존에 알던 css들을 삐그덕 거리면서 적용하게 되는 느낌이었는데 .. 승히 코드를 보며 다시 한 번 느끼고 갑니다 .. 더 연습해야겠구망!
// 마이페이지 내부 콘텐츠 컴포넌트 | ||
const MyPageInfo = () => { | ||
const param = useParams(); | ||
const { username, nickname } = useGetUserInfo(param.userId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오홍 커스텀 훅 !
나도 이번 과제에서 view랑 api로직을 분리하려고 했었는데 .. 뭔가 분리하려니 굳이 ..? 싶은 코드들이 나오게 되는 것 같아서 따로 분리하지 않았었는데 이렇게 커스텀 훅을 통해서 GET을 받아오니 너무너무 깔끔하고 좋다 !!!!
커스텀 훅은 중복되는 로직이 있는 경우에 (side effect 로직의 재사용성이 높을 경우!) 사용하는게 좋다고 알고 있어서 이번 과제에서 로직 분리할 때 커스텀훅을 생각해보지 못했는데,
사실 우리 과제에서는 myPage에서만 유저 정보가 필요하니 크게 중복된다고 할 순 없지만, 더 규모가 커진다면 이렇게 유저 정보 받아오는 걸 커스텀 훅으로 관리하는게 아주아주 효율적일 것이라는 생각이 드네요 !
배워갑니다용 🥹
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
맞아요! 사실 저희 과제에서는 커스텀훅으로 분리하기에는 오히려 재사용성이 너무 떨어지다보니 불필요한 분리가 아닐까 싶을 수 있지만, 저희 과제의 궁극적인 목적인 추후 더 큰 프로젝트에서 활약하기 위한 연습을 하는 것이기 때문에, 과제 특성상 규모가 작다는 이유로 재사용성이 낮은 것을 고려하여 어떠한 구현방식을 선택하지 않을 필요는 절대절대 없다고 생각합니다!!! 과제에서 열심히 연습해보아요 😎
</p> | ||
</div> | ||
) : ( | ||
<div>로딩중...</div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
로딩중 야무지다
display: grid; | ||
grid-template-columns: 1fr 2fr; | ||
align-items: center; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
승히는 css를 진짜 야무지게 잘 쓰는 것 같아
import Layout from "../components/Layout"; | ||
import { useState } from "react"; | ||
import { useNavigate } from "react-router"; | ||
import postLogin from "../api/postLogin"; | ||
import Buttons from "../components/Buttons"; | ||
import InputContainer from "../components/InputContainer"; | ||
import { createPortal } from "react-dom"; | ||
import styled from "styled-components"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
import 순서를 맞춰주면 좋을 것 같아요 !
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
요거 지난 과제에서도 서진언니가 코멘트 달아줬었는데 반영을 못했네요 ㅠㅠ import 순서를 자동으로 맞춰주는 lint 규칙을 활용해서 앞으로는 잊지않고 꼭꼭 순서 맞추려고합니다!!
|
||
return ( | ||
<div> | ||
<Layout title="33th SOPT" buttons={Buttons(btnInfo)} /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
버튼을 공통 컴포넌트로 분리하여서 이렇게 사용하게 되었군뇨 !
prop으로 컴포넌트 자체를 넘기는 것은 처음보는지라 .. (제가 마주한 코드가 몇 개 안되어서 그런 거인 듯 합니당) 처음에 봤을 때 약간 생소했는데 혹시 이렇게 컴포넌트 자체를 prop으로 넘기는게 흔히 있는 일인가요 ? (단순 궁금)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
헉 사실 흔하지는 않은 것 같아요 저도 처음써보는 형태인 것 같습니다 ㅋㅎㅋㅎㅎ
이번 케이스에서는 하나의 Wrapper 안에 Button의 개수가 다양했는데요! 따라서 일반적인 데이터만을 전달해서 컴포넌트를 렌더링하는 것이아닌, 내부 컴포넌트들을 그대로 전달하는 children을 활용하고 싶었습니다. 그러나 Layout 컴포넌트가 감싸는 진짜 내부 내용물 때문에 children을 중복으로 두번 사용할 수 없는 문제에 봉착했고! 그에 따라 children으로 전달해주고 싶었던 버튼들의 묶음을 또하나의 별도 컴포넌트로 분리하여 Props로 전달해주었어요!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
잔디선생님들이 코드리뷰에 하나의 모듈과 하나의 컴포넌트는 한가지 책임만 가질 수 있게 해주면 좋겠다고 이야기해줬는데 어떤 방식으로 모듈을 분리해줄 수 있을지를 진짜 많이 배워갈 수 있었던 것 같아요! 그 만큼 props drilling 문제를 해결할 수 있는 방법도 함께 고민해보아야겠네요!! 프로젝트의 규모가 커지면서 정말 재사용하기 좋은 형태로 코드를 짜는 방식이 무엇인지에 대해 많이 고민해볼 필요를 느끼게 되었습니다🥹🥹 뭔가 항상 코드리뷰하면 도움은 못되고 항상 배워가는 것 같아요!! 진짜 좋은 코드에 대해 이야기해볼 수 있는 멋진 잔디가 되어볼께요ㅎㅎ🍀 이번주도 너무 바빴을텐데 과제하느라 너무 고생많았어요!!
<Routes> | ||
<Route path="/" element={<Home />} /> | ||
<Route path="/login" element={<Login />} /> | ||
<Route path="/signup" element={<SignUp />} /> | ||
<Route path="/mypage" element={<MyPage />}> | ||
<Route path=":userId" element={<MyPageInfo />} /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
진짜 생각해보니 userID 값만 바꿔서 사용자 정보에 접근할 수 있는 문제가 생길 수있겠군요...
이렇게 페이지 패스와 url을 다르게 설정하는 방법으로 이를 해결할 수 있구나...진짜 섬세함에 다시 한번 감탄했어요..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
증말루,, 요런 거까지 챙기는 뜽희는 멋쟁이✨
&:not(:disabled) { | ||
cursor: pointer; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
진짜 미친 섬세함...
& > span:nth-child(1) { | ||
padding-left: 1rem; | ||
font-weight: 600; | ||
border-left: 0.5rem solid ${({ theme }) => theme.colors.sopt}; | ||
height: 1.3rem; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
따로 컴포넌트 안만들고 이렇게 적용할 수도 있겠군요.. 진짜 너무 야무지다..
<Layout title="MY PAGE" buttons={Buttons(btnInfo)}> | ||
<Outlet /> | ||
</Layout> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
복습하면서 outlet으로 적용해볼 생각을 못했는데 이렇게 중첩된 라우트를 관리하는 방식이 있겠군용
Layout 구조 위에 outlet으로 하위 라우트 컴포넌트만 렌더링 되는 걸로 이해했는데 맞나용??🥹
// ID 중복체크 API | ||
const getIdCheck = async ({ ID, setExist }) => { | ||
try { | ||
const res = await axios.get( | ||
`${import.meta.env.VITE_BASE_URL}/check?username=${ID}` | ||
); | ||
res.data.isExist ? setExist("true") : setExist("false"); | ||
} catch { | ||
(err) => { | ||
console.log(err); | ||
}; | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
set함수까지 넘겨주기보다는 중복체크 후 결과만 return해주고 그 결과값을 바탕으로 해당 컴포넌트에서 set함수를 핸들링해주는것은 어떤가요? 그러면 좀 더 단일책임을 따르는 좋은 함수가 될 수 있을 것 같아용~!
const [ID, setId] = useState(""); | ||
const [PW, setPw] = useState(""); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
여기 왜 ID, PW는 대문자로 한지 궁금해요!
<InputContainer custom={true}> | ||
<label htmlFor="id">ID</label> | ||
<div> | ||
<input | ||
type="text" | ||
id="id" | ||
value={ID} | ||
onChange={(e) => setId(e.target.value)} | ||
/> | ||
<CheckBtn | ||
type="button" | ||
onClick={function () { | ||
getIdCheck({ ID, setExist }); | ||
}} | ||
$isExist={isExist}> | ||
중복체크 | ||
</CheckBtn> | ||
</div> | ||
</InputContainer> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이렇게 결국 custom만 줘서 껍데기만 사용하는 경우가 된다면, 이런 경우까지 과연 공통 컴포넌트로 사용해야 할까요?! 고민해보면 좋을 것 같아용 ㅎㅎ 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이번에 숭이가 ✨우수 과제✨를 수상해서 과제 구경하러 한 번 더 놀러왔어요,,~ 정말 승희 코드 볼 때마다 넘넘 감탄뿐이고,, 앞으로 더 열심히 해야겠따 라는 다짐이 드는 코드,, 정말 멋져요💛💛💛
PR에 승희가 고민했던 내용들도 자세하게 써줘서 제가 생각하지 못했던 부분까지 고찰하게 해주는 것 같아서 정말 몰입해서 읽었던 것 같습니다 ! 서현이가 우수과제 안 줄 수 없었다는 말 완전 인정 ><
) { | ||
try { | ||
const res = await axios.post(`${import.meta.env.VITE_BASE_URL}/sign-in`, { | ||
username, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이렇게 단축해서 POST로 보낼 수도 있구나 알아갑니당
<Routes> | ||
<Route path="/" element={<Home />} /> | ||
<Route path="/login" element={<Login />} /> | ||
<Route path="/signup" element={<SignUp />} /> | ||
<Route path="/mypage" element={<MyPage />}> | ||
<Route path=":userId" element={<MyPageInfo />} /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
증말루,, 요런 거까지 챙기는 뜽희는 멋쟁이✨
}; | ||
|
||
useEffect(() => { | ||
getUserInfo(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
위에 함수 선언해주고 useEffect에서 불러주면 되는데 컴포넌트 최상단이 코드 최상단이랑 같은 줄 알구 useEffect 안에서 함수 선언해줬던 밥오,,
@@ -0,0 +1,47 @@ | |||
import styled from "styled-components"; | |||
|
|||
// 버튼 컴포넌트 (공통) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
공통 컴포넌트까지 만들어준 이 숭 어쩌면 조아,,,
|
||
const navigate = useNavigate(); | ||
|
||
const btnInfo = [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
공통 컴포넌트에 props만 다르게 줘서 다양한 버튼을 만든 거,, 진짜 너무 신기하네요,, 이런 생각 어케 하는 건가요 승희님 ? ?? ??
✨ 구현 기능 명세
🌱 기본 조건
.env
파일 사용하기🧩 기본 과제
[ 로그인 페이지 ]
/mypage/:userId
로 넘어갑니다. (여기서userId
는 로그인 성공시 반환 받은 사용자의 id)/signup
으로 이동합니다.[ 회원가입 페이지 ]
/login
으로 이동합니다.[ 마이 페이지 ]
/mypage/:userId
의 userId를 이용해 회원 정보를 조회합니다.🌠 심화 과제
[ 로그인 페이지 ]
createPortal
을 이용합니다.[ 회원가입 페이지 ]
💎 PR Point
디렉토리와 컴포넌트 구조는 다음과 같습니다
처음엔 api 통신 코드를 api 통신이 발생하는 컴포넌트 내에서 구현했었는데요!
SRP
원칙을 지키고자 각 역할에 따라 디렉토리를 확실히 분리해보았습니다.pages
디렉토리components
디렉토리api
디렉토리utils
디렉토리아쉬운 점은, 이렇게 단일 역할에 따라 컴포넌트를 세부적으로 분리하다보니 어쩔 수 없니 props의 사이즈가 커지게 되고, props drilling이 조금 발생하더라고요! 전역 상태 관리 라이브러리를 해당 주차 과제에서는 사용할 수 없기 때문에 도입하지 않았지만, 추후 리팩토링 시 Context API 혹은 Recoil을 통해 상태를 관리해볼 수 있을 것 같습니다. (개인적으로 Recoil보다 더 낯선 Context API를 사용해보고 싶습니다)
마이페이지를
/mypage/:userId
로 하나의 페이지로 관리할 수 있었으나, 세미나에서 배웠던 Outlet을 활용해볼 수 있는 유일한 부분인 것 같아서 내부 콘텐츠 부분을 렌더링하는 컴포넌트MyPageInfo
컴포넌트를 따로 분리하여 Outlet을 사용해봤습니다!🚨 그런데 사실 이 방법은 바람직하지 않습니다!
이번 과제는 outlet을 활용하기 위에 위와 같이 라우터를 설계하여 마이페이지의 url이 다음과 같이 완성되는데요!
http://localhost:5173/mypage/185
그런데 마이페이지 url은 이렇게 생기면 안된다는 사실…!!!!
마이페이지 url이 이렇게 생겼을 경우, 사용자가 주소창에
/mypage/1
이렇게 입력하면 1번 유저의 마이페이지에 마음대로 접근할 수 있겠죠?그리고 서버 통신의 api 는
/check?username=${ID}
이렇게 생겼지만, 라우터의 path는 서버 통신 url과는 무관하게 설정해줘도 됩니다!!따라서, api 통신 코드에서는 url에 따라
/check?username=${ID}
이렇게 조회할 사용자의 id 값을 넣어서ID
번 유저의 정보를 가져오고, 그 데이터를/mypage
페이지 내부에 뿌려주기만 하는거죠!이렇게 url로 다른 유저의 마이페이지에 마음대로 접근하는 것을 막는 방식이 훨씬 더 바람직하답니다 😈
처음에 습관적으로 .then .catch 방식을 사용했었는데요!
세미나에서 .then .catch 방식보다 try catch 문을 사용하는 것을 더 추천한다는 내용을 떠올려서
api 통신 코드를 모두 try catch 문으로 변경했습니다!
지금은 소용 없어진 논리와 코드이지만….^ 과제하면서 가장 많이 고민했던 지점이기에 기록 남깁니당
🐵중복체크에 뜬금없이 로그인 api를 사용한 숭의 논리
처음엔 사용자정보 get을 쓰는거겠지? 싶었는데, 회원가입 절차의 경우
memberId
를 알 수가 없어서 이 api는 쓸 수 없고,그렇다면.. 회원가입 post를 날려서 ‘이미 존재하는’ 사용자 에러를 받으면??
중복체크 실패 처리는 가능하지만, 만약 성공한다면? 그럼 그대로 post 통신이 실행돼버리면서 비밀번호 등 나머지 정보를 입력하기도 전에 미완성 데이터가 서버로 넘어가버린다...
그래서 결론은!
괴상하지만…. 로그인 api를 활용하자!
로그인 post의 에러메시지는 다음과 같다.
따라서 중복 체크할 경우, 에러 메시지에 따라 다음과 같이 처리할 수 있다.
제 코드, 다른 파트원들 코드를 보면서 다양한 에러를 맞다보니 이제는 에러코드만 봐도 어느 부분을 체크하면 될지 감이 잡히더라고요!
대체로 클라의 문제인 4XX 케이스의 경우 다음과 같다는 결론을 내렸습니다
400
→ 리퀘스트 body가 뭔가 잘못됨404
→ api 통신 url이 잘못됨.env
파일을 src 내부에 위치시켜줘서 base url이 undefined로 찍혀서 문제생기는 경우가 많았음405
→ 메소드가 잘못됨 (post인데 get으로 보내는 등 .. )🥺 소요 시간, 어려웠던 점
🌈 구현 결과물
i.e.e.e.2023-11-17.i.i.6.30.36.mov
i.e.e.e.2023-11-17.i.i.6.31.46.mov
i.e.e.e.2023-11-17.i.i.6.32.52.mov