-
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주차 기본/심화/공유 과제 ] 로그인, 회원가입 구현해보기 #4
base: main
Are you sure you want to change the base?
Conversation
카드 클릭 -> 오픈 매칭 성공 -> 오픈 고정 매칭 실패 -> 오픈 취소
카드 매칭 -> 스코어 +1
headercontroller -> header 로 짧게 변경
null은 falsy한 값이므로 똑같게 사용됨 null인 경우와 다른 falsy한 값(0 등)일 경우가 다른 경우로 분기되어야 하는 상황은 제외
코드를 봤을때 이해하기 쉽도록 5로 하드코딩하는 것이 아닌 상수에서 값을 가져오기
전화번호 -은 실행 안됨
const router = createBrowserRouter([ | ||
{ | ||
path: '/', | ||
element: <Login />, | ||
}, | ||
{ | ||
path: '/signup', | ||
element: <Signup />, | ||
}, | ||
{ | ||
path: '/main/:memberId', | ||
element: <Main />, | ||
}, | ||
{ | ||
path: '/mypage/:memberId', | ||
element: <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.
path를 좀 더 안전하게 사용하는 방법도 있다는 것 아시나요!
route.ts를 하나 만든 다음,
const STATIC = {
HOME: "/",
LOGIN: "/login",
SIGNUP: "/signup",
};
const DYNAMIC = {
MYPAGE: "/mypage/:id",
};
export const PATH = {
...STATIC,
...DYNAMIC,
} as const;
이런식으로 작성해보는 것도 좋을것같아요! 저희 합세 조 라우터는 위와같이 세팅할 예정이라 ㅎㅎ
return; | ||
} | ||
try { | ||
const response = await axios.post('http://34.64.233.12:8080/member/login', { |
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.
이렇게 baseUrl같은 경우에는 환경변수로 빼둬봅시다 ㅎㅎ
if (response.status === 200) { | ||
const memberId = response.headers.location; | ||
alert('로그인이 완료되었습니다.'); | ||
navigate(`/main/${memberId}`); | ||
} |
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.
음 어차피 성공하면 200번이 뜰텐데 if문으로 작성해주신 이유가 있을까용?
interface InfoTextProps { | ||
text: string; | ||
} | ||
|
||
export default function InfoText({ text }: InfoTextProps) { | ||
return <I.Text>{text}</I.Text>; | ||
} |
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.
요롷게 text를 prop으로 주는 것도 좋지만, 추후 확장성 대비해서 children을 적극활용해봐요!
관련 고민 제 아티클 남겨둡니다! 심화스터디 아티클이에요!
https://popeyes9304.tistory.com/
if (response.status === 200) { | ||
const memberId = response.headers.location; | ||
alert('로그인이 완료되었습니다.'); | ||
navigate(`/main/${memberId}`); |
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()
//이렇게 매번 사용하기 귀찮잖아요??
useEasyNavigate 커스텀 훅으로 사용해도 좋을 것 같아요!
제 과제 코드 중에 useEasyNavigate 참고해보세용~
function goMypage() { | ||
navigate(`/mypage/${memberId}`); | ||
} | ||
|
||
function goSignup() { | ||
navigate('/signup'); | ||
} |
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.
오 여기에 만들어두셨네요!
이를 커스텀 훅으로 분리해서 사용한다면 조금 더 많은 곳에서 사용할 수 있겠죠??
한 번 고민해보시길 ㅎㅎ
@@ -0,0 +1,13 @@ | |||
import * as B from './ButtonStyle'; | |||
interface ButtonProps { | |||
children: any; |
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.
children의 type을 바꿔봅시다 ㅎㅎ
구글링하면 바로 나올거에요!
interface ButtonProps { | ||
children: any; | ||
onClick: () => void; | ||
type?: 'button' | 'submit' | 'reset'; | ||
} |
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.
또한 button 같은 경우에는
interface ButtonProps extends extends React.ButtonHTMLAttributes<HTMLButtonElement> {
}
이런식으로 interface를 상속 받는다면 따로 onClick을 정의해줄 필요가 없어집니다!
if (response.status === 200) { | ||
setUserData(response.data.data); | ||
} |
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.
여기도 if문의 의도가 궁금합니다!
<Title text="마이 페이지" /> | ||
<div> | ||
<Label text="ID" /> | ||
<Input type="text" id="id" value={userData.authenticationId} readOnly /> |
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.
오 readOnly 꼼꼼하네요 좋습니다 👍👍👍
const phoneAutoHyphen = (phone: string) => { | ||
return phone.replace(/[^0-9]/g, '').replace(/^(\d{3})(\d{4})(\d{4})$/, '$1-$2-$3'); | ||
}; |
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.
이런애들은 유틸함수로 빼봅시다 ㅎㅎ
function validatePassword(password: any) { | ||
const minLength = 8; | ||
const hasNumbers = /\d/; | ||
const hasEnglish = /[a-zA-Z]/; | ||
const hasSpecial = /[!@#$%^&*(),.?":{}|<>]/; | ||
|
||
if (password.length < minLength) { | ||
alert('비밀번호는 최소 8자 이상이어야 합니다.'); | ||
return false; | ||
} | ||
if (!hasNumbers.test(password)) { | ||
alert('비밀번호에는 최소 한 개의 숫자가 포함되어야 합니다.'); | ||
return false; | ||
} | ||
if (!hasEnglish.test(password)) { | ||
alert('비밀번호에는 최소 한 개의 영어 알파벳이 포함되어야 합니다.'); | ||
return false; | ||
} | ||
if (!hasSpecial.test(password)) { | ||
alert('비밀번호에는 최소 한 개의 특수 문자가 포함되어야 합니다.'); | ||
return false; | ||
} | ||
return true; | ||
} |
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.Form isOpen={isOpen}> | ||
<div> | ||
<Label text="기존 비밀번호" /> | ||
<Input | ||
type="text" | ||
id="current-password" | ||
value={currentPassword} | ||
onChange={(e) => setCurrentPassword(e.target.value)} | ||
/> | ||
</div> | ||
<div> | ||
<Label text="새 비밀번호" /> | ||
<Input | ||
type="text" | ||
id="new-password" | ||
value={newPassword} | ||
onChange={(e) => setNewPassword(e.target.value)} | ||
/> | ||
</div> | ||
<div> | ||
<Label text="비밀번호 확인" /> | ||
<Input | ||
type="text" | ||
id="confirm-password" | ||
value={confirmPassword} | ||
onChange={(e) => setConfirmPassword(e.target.value)} | ||
/> | ||
</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.
요기 컴포넌트 분리하면 가독성이 더올라갈 것 같아요!
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.
전반적으로 관심사 분리를 잘 해주셨는데,
스타일 코드를 분리하신 것처럼, 커스텀훅으로 기능을 분리하는 연습을 해보는 것도 좋을 것 같아요!
기능을 아예 전담으로 위임해버리면, 다른 쪽에서도 사용 가능하니까!
또한 api 관련된 코드도 따로 모아놓으면 더 컴포넌트가 깔끔해지겠죠?
한 번 고민해보시길 ㅎㅎ
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.
컴포넌트 별로 파일 세심하게 구분하신 부분, 에러처리 자세하고 깔끔하게 처리하신 부분 많이 배워갑니다 👍 👍
4주차 과제 수고하셨습니다!
if (!currentPassword || !newPassword || !confirmPassword) { | ||
alert('비밀번호를 입력해주세요.'); | ||
return; | ||
} |
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.
newPassword와 confirmPassword 일치여부 비교하는 로직도 들어가면 좋을 것 같아요!
|
||
try { | ||
const response = await axios.patch( | ||
'http://34.64.233.12:8080/member/password', |
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.
BASE_URL의 경우는 환경변수 등으로 빼두면 좋을 것 같습니다 ㅎㅎ
} catch (error: any) { | ||
if (error.response) { | ||
alert(error.response.data.message); | ||
} else if (error.request) { | ||
alert('응답을 받지 못했습니다.'); | ||
} else { | ||
alert('요청 중 오류가 발생했습니다.'); | ||
} |
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 goHome = () => { | ||
navigate(`/main/${memberId}`); | ||
}; |
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.
이런 부분은 따로 빼두었다가 여러 파일에서 사용할 수 있으면 좋을 것 같습니다 ㅎㅎ
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.
요 파일은 사용이 안되는 파일인가요?? 만약 그렇다면 삭제해도 좋을 것 같습니다!
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.
이렇게 하나하나 분리하신 부분 세심하네요 👍 배워갑니다!
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.
커밋컨벤션에서 remove
는 보통 파일이나 폴더를 삭제할 때 사용하고
chore
는 기타 변경사항으로 많이 사용합니다, 빌드 관련된 커밋이나, assets관련된 커밋으로요!
또한 스타일 분리 (C dot네이밍 사용) 은 기능은 변경되지 않았지만 코드 가독성을 위해 개선한 것이니 refactor
을 사용해도 좋을 것 같습니다!
주석 지우는건 chore로 사용하는 것이 더 의미있지 않을가요??
✨ 구현 기능 명세
🧩 기본 과제
메인 페이지
로그인 페이지
회원가입 페이지
마이페이지
🔥 심화 과제
메인페이지
로그인 페이지
회원가입 페이지
input이 비어있는 상태로 api연결 시도했을시
해당 input 테두리 색상 변경
input에 focus 맞추기
api요청 금지
전화번호 양식 정규표현식으로 자동입력되도록 설정 (숫자만 입력해도 "-"가 붙도록)
비밀번호 검증 유틸 함수 구현 (검증 통과되지 않을시 api요청 금지)
https://forweber.palms.blog/nowsopt-lint-imddoy
📌 내가 새로 알게 된 부분
💎 구현과정에서의 고민과정(어려웠던 부분) 공유!
🥺 소요 시간
15h
🌈 구현 결과물
https://imdevdoy.notion.site/ecf9dc6794f349c7876c5e152f4279bd?pvs=4