Skip to content
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

Open
wants to merge 66 commits into
base: main
Choose a base branch
from

Conversation

wuzoo
Copy link
Contributor

@wuzoo wuzoo commented May 10, 2024

✨ 구현 기능 명세

🧩 기본 과제

  1. 메인 페이지
    • 메인 이미지 or 비디오 넣기
  • 내정보페이지와 회원가입 페이지로 이동할 수 있는 버튼 구현
  1. 로그인 페이지

    • 아이디와 비밀번호를 입력할 수 있는 input구현
    • Login page 이미지 넣기
    • 로그인 버튼(기능)과 회원가입 페이지 이동 버튼 구현
    • 로그인 실패시 해당 에러메세지를 alert로 띄어주기
    • useParam 활용해서 id값 보유하고 있기.
  2. 회원가입 페이지

    • 아이디, 패스워드, 닉네임, 핸드폰 번호를 입력 받는 페이지 구현
    • 회원가입 버튼 클릭시 post api 통신을 진행하고 성공시 회원가입이 완료되었다는 메시지를 보여주는 alert 띄워준 후, 로그인 메인페이지로 이동
    • 아이디 중복, 비밀번호 형식 오류, 전화번호 형식 오류 등 모든 에러 alert로 메세지 보여주기
    • 비밀번호와 전화번호 형식은 input 아래에 보여주기
  3. 마이페이지

    • get 메소드를 사용해 사용자 정보를 가져오기
    • 서버에서 받아온 ID, 닉네임, 전화번호 데이터를 렌더링
    • 비밀번호 변경 토글을 사용해 비밀번호 변경 폼을 on/off할 수 있도록 구현
    • 기존 비밀번호 입력, 새로운 비밀번호 입력, 새로운 비밀번호 확인 input 구현
    • input이 비어있을 경우 api 작동되지 않도록 구현
    • 에러 발생시 api error객체 안 error message를 사용해 alert 띄우기
    • 홈 이동 버튼 구현

🔥 심화 과제

  1. 메인페이지

    • 비디오에 여러 기능을 적용
  2. 로그인 페이지

    • input이 비어있을 경우 api요청 보내지 않고 아래 error message를 띄워주기
  3. 회원가입 페이지
    input이 비어있는 상태로 api연결 시도했을시

    • 해당 input 테두리 색상 변경

    • input에 focus 맞추기

    • api요청 금지

    • 전화번호 양식 정규표현식으로 자동입력되도록 설정 (숫자만 입력해도 "-"가 붙도록)

    • 비밀번호 검증 유틸 함수 구현 (검증 통과되지 않을시 api요청 금지)

공유과제

  • prettier, eslint, styleLint에 대해
  • lighthouse에 대해

링크 첨부(팀 블로그 링크) : https://forweber.palms.blog/now-sopt-lighthouse-juyong


📌 내가 새로 알게 된 부분

  1. 이번 과제에서는 대부분이 Input을 통해 User 정보를 기입하고, input 요소 관련 처리를 하는 것이였기 때문에 react-hook-form 라이브러리를 도입하기로 결정하였습니다.
    기존에 해당 라이브러리를 아주 간단하게만 사용해본 경험이 있었는데, 이번 과제를 통해 다양한 아주 유용한 메서드가 있다는 것을 알게 되었고 적극 활용했던 것 같습니다.

  2. axiosinterceptor를 도입하였습니다. 인터셉터는 api 요청 및 응답이 진행되기 전에, 이를 가로채어 내가 지정한 특정 로직을 수행한 후, api 요청을 진행하게 해줄 수 있는 도구입니다. 이 과정에서 use 메서드를 통해 memberId를 요청 헤더에 넣어주는 식으로 작업하였는데, 해당 방법을 도입하면서 인터셉터에 대해 좀 더 찾아보고 이해해볼 수 있는 시간이였던 것 같습니다.


💎 구현과정에서의 고민과정(어려웠던 부분) 공유!

API 요청에 있어서 정말 다양한 방법이 존재하는 것으로 알고 있습니다. 다른 분들은 요청 및 응답 과정을 어떻게 처리하셨는지 궁금하네요 ! react-query를 도입해볼까도 생각해보았지만, 이번 과제에서는 주어진 API 또한 적고, UI를 최대한 빨리 업데이트해야 한다거나, 캐싱을 적극 활용할 부분도 적다 판단하여 도입하지 않았습니다. 도입하신 분들이 있다면, 어떤 방식으로 사용하셨는지 궁금하네요 !
또한 과제에서 주어졌던 조건인, 전화번호 자동완성, 비밀번호 유효성 체크, Input 요소가 비었을 때의 처리 등등 다양한 구현 방법에 대해서 다른 분들의 의견을 듣고 싶습니다.


🥺 소요 시간

  • 24h

🌈 구현 결과물

1
2
3
4
5
6

wuzoo added 30 commits April 26, 2024 19:52
@wuzoo wuzoo self-assigned this May 10, 2024
@wuzoo wuzoo added 기본 basic 심화 advanced 공유 share 4주차 week2 labels May 10, 2024
import axios from "axios";
import { checkMemberId } from "./interceptors";

export const axiosInstance = axios.create({
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 이렇게 인스턴스를 생성해서 재사용하는 방법도 있군요! 저는 생각 못한 방법인데 항상 재사용을 고려하여 코드를 작성하시는 것 같아 많이 배우고 갑니다!

Copy link
Contributor Author

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",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 이렇게 경로랑 에러코드를 상수에 저장해주니 훨씬 활용하기 편리할 것 같아요!

Copy link
Member

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);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

혹시 로컬스토리지에 헤더를 따로 저장해주는 이유를 알 수 있을까요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

interceptor를 사용하기 위해 로컬스토리지를 활용하였습니다. 즉 로그인이 성공하면 localstorage에 멤버 아이디를 저장하고, 제가 사용한 인터셉터 use 메서드 부분을 보시면 로컬스토리지에 아이디가 있을 시 getpatch 요청에 해당 아이디를 포함시키도록 하였습니다.

Copy link

@seueooo seueooo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

컴포넌트 분리와 라이브러리 사용, 상수화, style 적용하는 방식 등에서 재사용성을 항상 고려하고 있는 모습이 보여서 코드를 보면서 많이 배웠습니다! 그동안 구현에만 집중하여 코드를 작성해왔는데 재사용성을 고려한 코드를 짤 수 있도록 주용님 코드를 보면서 많이 공부하겠습니다! 합세 파이팅!

Copy link
Member

@j-nary j-nary left a 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",
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

혹시 GlobalStyle이 아닌 다른 css 를 따로 만든 이유가 있으신가요? GlobalStyle과의 차이점이 궁금합니다!

Copy link
Contributor Author

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) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

데이터 type 지정해주신 거 정말 좋아보여요 ! 배워갑니다 !

}),
};

export const sizeStyle = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style 이렇게 구분하신 거 인상적이에요! 재활용성이 좋아보이네요! 배워갑니다.

Copy link
Contributor Author

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";
Copy link
Member

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> {
Copy link
Member

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} />
Copy link
Member

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이 들어오게 하는 방법은 어떻게 생각하시나요?
개인적인 의견일 뿐! 주용님의 의견도 궁금합니다 ^^

Copy link
Contributor Author

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",
Copy link
Member

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 마이페이지가기 = () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

헉 한글명 변수는 낯설어요! 특별히 한글로 명명한 이유가 있으실까요?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
4주차 week2 공유 share 기본 basic 심화 advanced
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants