-
Notifications
You must be signed in to change notification settings - Fork 0
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주차 기본/심화/공유과제 ] SOPT 로그인, 회원가입 구현하기 #6
base: main
Are you sure you want to change the base?
Conversation
…ler 반환하는 커스텀 훅 생성
import axios from "axios"; | ||
import { checkMemberId } from "./interceptors"; | ||
|
||
export const axiosInstance = axios.create({ |
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.
감사합니다 ! 한서님 코드 또한 저도 많이 보고 배운답니당
@@ -0,0 +1,18 @@ | |||
export const END_POINTS = { | |||
SIGN_UP: "/member/join", | |||
USER_INFO: "/member/info", |
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.
저도 경로 부분 상수로 지정해줄 생각은 못 했는데 확실히 2주연속 코리하면서 느끼는 거지만 주용님은 참 섬세하시네요!
try { | ||
const { headers } = await postLogin(data); | ||
|
||
localStorage.setItem("memberId", headers.location); |
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.
interceptor
를 사용하기 위해 로컬스토리지를 활용하였습니다. 즉 로그인이 성공하면 localstorage
에 멤버 아이디를 저장하고, 제가 사용한 인터셉터 use
메서드 부분을 보시면 로컬스토리지에 아이디가 있을 시 get
과 patch
요청에 해당 아이디를 포함시키도록 하였습니다.
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.
컴포넌트 분리와 라이브러리 사용, 상수화, style 적용하는 방식 등에서 재사용성을 항상 고려하고 있는 모습이 보여서 코드를 보면서 많이 배웠습니다! 그동안 구현에만 집중하여 코드를 작성해왔는데 재사용성을 고려한 코드를 짤 수 있도록 주용님 코드를 보면서 많이 공부하겠습니다! 합세 파이팅!
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: "flex", | ||
justifyContent: "center", | ||
alignItems: "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.
혹시 GlobalStyle이 아닌 다른 css 를 따로 만든 이유가 있으신가요? GlobalStyle과의 차이점이 궁금합니다!
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
을 통해 렌더링되는 App
의 자식 라우트 컴포넌트들을 뷰의 가운데 정렬시키기 위해 main
요소에 스타일링을 해준 것입니다 !
GlobalStyle과는 달리 main 요소에만 쓰이는 스타일이라서 따로 빼주었습니다.
import { axiosInstance } from "./axiosInstance"; | ||
import { ChangePwdType, UserDataType } from "@/types/api"; | ||
|
||
export const postSignUp = async (formData: UserDataType) => { |
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.
데이터 type 지정해주신 거 정말 좋아보여요 ! 배워갑니다 !
}), | ||
}; | ||
|
||
export const sizeStyle = { |
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.
style 이렇게 구분하신 거 인상적이에요! 재활용성이 좋아보이네요! 배워갑니다.
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 { SizeType } from "@/types/size"; | ||
|
||
export interface IButton extends ComponentPropsWithRef<"button"> { | ||
variant?: "primary" | "secondary" | "outline" | "default"; |
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 부분이 이렇게 구현하는 부분이었군요!
variant 통해서 재사용성을 높인 부분 인상적입니다. 좋은 코드 감사합니다 :)
import { ImgHTMLAttributes } from "react"; | ||
import { imgStyle, shapeStyle, sizeStyle } from "./Img.style"; | ||
|
||
export interface ImgProps extends ImgHTMLAttributes<HTMLImageElement> { |
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.
ImgHTMLAttributes 상속받은 부분 굉장히 섬세해보이네요! 좋아보입니다!
<div> | ||
<Label id={label}>{label}</Label> | ||
<div css={[inputWrapperStyle(isError)]}> | ||
<input ref={ref} css={[inputStyle, sizeStyle[size]]} {...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.
size까지 컴포넌트로 만든 부분이 인상적입니다.
그런데 css 속성보려면 inputStyle과 sizeStyle 둘 다 나눠서 봐야해서 조금 가독성이 떨어지는 부분도 있는 것 같습니다.
inputStyle에 size props을 줘서 한 눈에 style이 들어오게 하는 방법은 어떻게 생각하시나요?
개인적인 의견일 뿐! 주용님의 의견도 궁금합니다 ^^
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.
음.. 고민이 되는 질문이시네요 ! 저는 오히려 input
의 공통 스타일은 inputStyle
에, size에 관련된 스타일은 sizeStyle
에 들어있기 때문에 스타일을 파악하기 더 용이하다고 생각하였습니다. 긴 스타일 코드 속에 특정 속성이 어딨는지 찾을 필요 없이 말입니다 !
하지만 진님 말씀도 이해가 되네요. inputStyle이 size prop
을 받는다고 가정했을 때, 스타일 내부 코드만 잘 구분해주면 더 좋은 코드가 될 수 있을 것 같다고도 생각합니다. 의견 감사합니다!
@@ -0,0 +1,18 @@ | |||
export const END_POINTS = { | |||
SIGN_UP: "/member/join", | |||
USER_INFO: "/member/info", |
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.
저도 경로 부분 상수로 지정해줄 생각은 못 했는데 확실히 2주연속 코리하면서 느끼는 거지만 주용님은 참 섬세하시네요!
return () => window.removeEventListener("keypress", handleKeyDown); | ||
}); | ||
|
||
const 마이페이지가기 = () => { |
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.
헉 한글명 변수는 낯설어요! 특별히 한글로 명명한 이유가 있으실까요?
✨ 구현 기능 명세
🧩 기본 과제
로그인 페이지
회원가입 페이지
마이페이지
🔥 심화 과제
메인페이지
로그인 페이지
회원가입 페이지
input이 비어있는 상태로 api연결 시도했을시
해당 input 테두리 색상 변경
input에 focus 맞추기
api요청 금지
전화번호 양식 정규표현식으로 자동입력되도록 설정 (숫자만 입력해도 "-"가 붙도록)
비밀번호 검증 유틸 함수 구현 (검증 통과되지 않을시 api요청 금지)
공유과제
링크 첨부(팀 블로그 링크) : https://forweber.palms.blog/now-sopt-lighthouse-juyong
📌 내가 새로 알게 된 부분
이번 과제에서는 대부분이 Input을 통해 User 정보를 기입하고, input 요소 관련 처리를 하는 것이였기 때문에
react-hook-form
라이브러리를 도입하기로 결정하였습니다.기존에 해당 라이브러리를 아주 간단하게만 사용해본 경험이 있었는데, 이번 과제를 통해 다양한 아주 유용한 메서드가 있다는 것을 알게 되었고 적극 활용했던 것 같습니다.
axios
의interceptor
를 도입하였습니다. 인터셉터는 api 요청 및 응답이 진행되기 전에, 이를 가로채어 내가 지정한 특정 로직을 수행한 후, api 요청을 진행하게 해줄 수 있는 도구입니다. 이 과정에서use
메서드를 통해memberId
를 요청 헤더에 넣어주는 식으로 작업하였는데, 해당 방법을 도입하면서 인터셉터에 대해 좀 더 찾아보고 이해해볼 수 있는 시간이였던 것 같습니다.💎 구현과정에서의 고민과정(어려웠던 부분) 공유!
API 요청에 있어서 정말 다양한 방법이 존재하는 것으로 알고 있습니다. 다른 분들은 요청 및 응답 과정을 어떻게 처리하셨는지 궁금하네요 !
react-query
를 도입해볼까도 생각해보았지만, 이번 과제에서는 주어진 API 또한 적고, UI를 최대한 빨리 업데이트해야 한다거나, 캐싱을 적극 활용할 부분도 적다 판단하여 도입하지 않았습니다. 도입하신 분들이 있다면, 어떤 방식으로 사용하셨는지 궁금하네요 !또한 과제에서 주어졌던 조건인, 전화번호 자동완성, 비밀번호 유효성 체크, Input 요소가 비었을 때의 처리 등등 다양한 구현 방법에 대해서 다른 분들의 의견을 듣고 싶습니다.
🥺 소요 시간
🌈 구현 결과물