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주차 기본/심화/공유과제 ] 로그인, 회원가입 페이지 #6

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

Conversation

pepperdad
Copy link
Member

@pepperdad pepperdad commented May 9, 2024

✒️ 관련 이슈번호

🧩 기본 과제

  1. 메인 페이지

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

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

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

    • 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에 대해

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

📌 내가 새로 알게 된 부분

emotion에 ts를 처음 사용해봐서, props에 해당하는 타입을 주는 방법을 알게 되었습니다!

<Input
            name="id"
            placeholder="아이디"
            value={id}
            onChange={(e) => onChange(e)}
            ref={idRef}
            $isError={error.id}
          />

const Input = styled.input<{ $isError: boolean }>`
  width: 15rem;
  padding: 1rem;

  border: 1px solid #ccc;
  border-radius: 5px;

  ${(props) =>
    props.$isError &&
    css`
      border-color: red;
    `}
`;

동영상 넣는 방법을 알 수 있었습니다!
아래와 같이 video 태그를 사용해서 넣어주었습니다!

        <video muted autoPlay loop preload="auto" width={'250px'}>
          <source src={video} />
        </video>

ts에서 axios 요청을 보내고 응답을 받을 때, error 객체의 타입을 아래와 같이 설정해서 처리할 수 있습니다.

catch (error) {
    if (isAxiosError(error)) {
      alert(error.response?.data.message);
    }

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

StyleLint를 적용했는데, styled를 import 과정에서 아래와 같이 macro가 계속 붙습니다...
import styled from '@emotion/styled/macro';
혹시 해결하는 법 아시는 분 공유 부탁드립니다!


🥺 소요 시간

  • 15h

🌈 구현 결과물

영상

https://silk-title-f5a.notion.site/4-7c971fc1eceb4d6aa4fcda07b4a873f5?pvs=4

Copy link
Member

@gudusol gudusol left a comment

Choose a reason for hiding this comment

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

이슈까지 파서 작업을 하셨군요, 좋네요. 코드도 깔끔하게 잘 작성하신 것 같습니다.
api 분리도 잘되어있고, 페이지 안에 로직, 구조, 스타일도 위치에 맞게 딱 분리되어 있어서 읽기도 편했던 것 같습니다.

4주차 과제 고생 많으셨습니다. 많이 배워갑니다~! 👍

week4/index.html Outdated
@@ -0,0 +1,18 @@
<!doctype html>
<html lang="en">
Copy link
Member

Choose a reason for hiding this comment

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

ko 드리뷰 시작하겠습니다.

Choose a reason for hiding this comment

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

p3) en대신 ko사용하시는게 좋을 거 같아여

Copy link
Member Author

Choose a reason for hiding this comment

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

ㅋㅋㅋ 감사합니다 반영완료했습니다!

import { theme } from './styles/theme.ts';

ReactDOM.createRoot(document.getElementById('root')!).render(
<>
Copy link
Member

Choose a reason for hiding this comment

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

p4: ThemeProvider로 감싸져 있어서, <></>는 굳이 필요없어 보입니다!

Copy link
Member Author

Choose a reason for hiding this comment

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

반영 완료 했습니다!

Comment on lines +5 to +14
${reset}

html,
body,
div,
span,
applet,
object,
iframe,
h1,
Copy link
Member

Choose a reason for hiding this comment

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

p5: 혹시 라이브러리를 통한 ${reset}과 css 코드로 reset하는 부분이 둘다 있는 이유가 있을까요?

Copy link
Member Author

Choose a reason for hiding this comment

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

흠,,, 이 부분도 생각해보니 reset 을 했으니 굳이 하는 이유가 없겠군요! 기존의 css코드로 reset하는 부분은 제가 기존에 사용하는 코드를 가져왔습니다!

Comment on lines 11 to 18
const [onIdError, setIdError] = useState({
open: false,
message: 'id를 입력해주세요',
});
const [onPwError, setPwError] = useState({
open: false,
message: 'password를 입력해주세요',
});
Copy link
Member

Choose a reason for hiding this comment

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

p5: [onIdError, setOnIdError]가 아니고 state 변수 이름에만 on을 붙이는 이유가 있을까요?

Choose a reason for hiding this comment

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

어라라 그렇네요 저도 궁금해요

Copy link
Member Author

Choose a reason for hiding this comment

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

그러게요..? 제 왜인지 모르겠는데 이게 토글 로직이랑 헷갈렸던 것 같아요!
제가 on이라는 접두사는 보통 토글 상태에 사용하는데,,,
수정했습니다!

password,
};

const { data, location } = (await login(loginInfo))!;
Copy link
Member

Choose a reason for hiding this comment

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

p5: 잘 몰라서 그런데, api의 login 함수에서도 요청하는 부분에 await이 되어있는데, 이 함수를 호출할 떄도 await을 해줘야 하는건가요..?

Copy link
Member

Choose a reason for hiding this comment

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

하하 어제 얘기했던거처럼 있는게 맞네요,,, 하나 배워갑니다 ㅎㅎ

Copy link
Member Author

Choose a reason for hiding this comment

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

네! async로 작성한 함수는 promise를 반환하기 때문에 await 으로 받아야합니다!

Comment on lines 101 to 104
<Form onSubmit={handleSubmit}>
<div>
<Title>회원가입</Title>
</div>
Copy link
Member

Choose a reason for hiding this comment

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

p5: 저 별 생각없이 form 태그 안쓰고, input이랑 button 만 가지고 구현했는데,,, form 태그 좋네요 ㅎㅎ

Comment on lines +159 to +160
<Button type="submit">회원가입</Button>
<Button type="button" onClick={() => navigate(-1)}>
Copy link
Member

Choose a reason for hiding this comment

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

p5: Button에 따라서 type도 다 다르게 준 디테일 좋네요 👍👍

Comment on lines +184 to +190
padding: 4rem 6rem;

color: white;

background-color: green;
border: 1px solid #ccc;
border-radius: 10px;
Copy link
Member

Choose a reason for hiding this comment

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

p5: rem와 px을 둘다 사용하시는 것 같은데 각각을 쓰는 기준이 있으신가요? border에는 px을 쓰시는 건가요?

Copy link
Member Author

Choose a reason for hiding this comment

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

border-radius나 border 같은 부분은 10px 미만으로 주로 사용되어서 rem으로 적으면 0.1rem 이런 식으로 적게되서 직관적이지 못하더라고요!
그런 로직들은 px로 사용하고 있습니다!

Comment on lines 21 to 27
const fetchData = async () => {
if (id) {
const response = await getUser(id);

setUserInfo(response.data);
}
};
Copy link
Member

Choose a reason for hiding this comment

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

p5: 함수 선언은 useEffect 밖에 하는 것이 더 낫지 않을까요? 이 안에서만 사용하는 함수라 일부로 안에서 선언하신건가요?

Copy link
Member Author

Choose a reason for hiding this comment

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

흠,,, 그러게요 useEffect 안에서만 사용되면 괜찮을 것 같은데! 다른 부분에서도 사용되면 빼도 괜찮을 것 같아요!

Copy link
Member Author

@pepperdad pepperdad May 15, 2024

Choose a reason for hiding this comment

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

일단 저도 빼는 걸로 반영했습니다!


<Box>
<ToggleButton onClick={() => setShowPasswordChange(!showPasswordChange)}>
비밀번호 변경 {showPasswordChange ? '⇧' : '⇩'}
Copy link
Member

Choose a reason for hiding this comment

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

p5: 우와 화살표 방향까지 !

Copy link

@Bowoon1216 Bowoon1216 left a comment

Choose a reason for hiding this comment

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

4주차 과제 넘 수고하셨어요
코드리뷰하는 내내 코드가 넘 깔끔하고 잘 읽혀서 배울 점이 많았네요😄
합세 때도 파이팅 입니다~~!

week4/index.html Outdated
as="style"
crossorigin
href="https://cdn.jsdelivr.net/gh/orioncactus/[email protected]/dist/web/variable/pretendardvariable.css" />
<title>Vite + React + TS</title>

Choose a reason for hiding this comment

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

p5: 여기 title도 적절하게 바꿔줘도 좋을 것 같아요 (사실 저도 자주 깜빡하는 부분이긴 합니다 😶‍🌫️ )
<title> 도영이의 행복한 로그인 <title>
이라던지... ㅎㅎ

Copy link
Member Author

Choose a reason for hiding this comment

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

맞네요 ㅎㅎ
감사합니다! 반영 완료했습니다

<BrowserRouter>
<Routes>
{routerData.map((data, i) => (
<Route key={`route-${i}`} path={data.path} element={data.element} />

Choose a reason for hiding this comment

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

p5: 이렇게 path, element를 따로 빼서 활용하는 방법을 보니까 생각났는데 map을 사용하지 않고 더 간결하게 구현할 수 있는 방법도 있더라구요
참고하셔도 좋을 것 같아서 링크 남깁니다~!
https://medium.com/@duchanjo/react-router-routerprovider-ee36a0dfd6fc

Copy link
Member Author

@pepperdad pepperdad May 15, 2024

Choose a reason for hiding this comment

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

감사합니다!
반영 완료 했습니다!

@@ -0,0 +1,94 @@
import axios, { isAxiosError } from 'axios';

const API_URL = `${import.meta.env.VITE_BASE_URL}`;

Choose a reason for hiding this comment

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

p5: env파일로 숨겨 놓기.. 아주 좋네요 👍

Comment on lines 11 to 18
const [onIdError, setIdError] = useState({
open: false,
message: 'id를 입력해주세요',
});
const [onPwError, setPwError] = useState({
open: false,
message: 'password를 입력해주세요',
});

Choose a reason for hiding this comment

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

어라라 그렇네요 저도 궁금해요

`;

const Img = styled.img`
width: ${(props) => props.width};

Choose a reason for hiding this comment

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

p5: 달라지는 속성을 props로 받아서 재사용성을 높이는 방법 👍 좋네요
좋은 정보 하나 배워갑니다~!

Copy link
Member Author

Choose a reason for hiding this comment

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

저도 아직 컴포넌트로 분리하지는 않았지만, 이 부분을 컴포넌트로 분리하면 더 좋을 것 같네요 ㅎㅎ

week4/index.html Outdated
@@ -0,0 +1,18 @@
<!doctype html>
<html lang="en">

Choose a reason for hiding this comment

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

p4) en 대신 ko로 작성하시는게 좋을 것 같아염~

Copy link
Member Author

Choose a reason for hiding this comment

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

넵! 반영 완료했습니다!

Comment on lines +1 to +5
import styled from '@emotion/styled';

import Router from './Router';

function App() {

Choose a reason for hiding this comment

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

p4) emotion을 사용했군여~ 저도 한번 참고해서 배워가겠습니다~~

Comment on lines 8 to 26
const Router = () => {
const routerData = [
{
path: '/',
element: <Home />,
},
{
path: '/:id',
element: <Main />,
},
{
path: '/signup',
element: <Signup />,
},
{
path: '/mypage/:id',
element: <Mypage />,
},
];

Choose a reason for hiding this comment

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

p5) router Data안에 이렇게 경로와 요소를 넣을 수도 있군여

Copy link
Member Author

Choose a reason for hiding this comment

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

맞아요~
react router v6 부터 아래 링크와 같이 createRouter를 사용하는 방식을 권장하고 있습니다!
참고하면 좋을 것 같아요!

https://reactrouter.com/en/main/routers/create-browser-router#createbrowserrouter

Comment on lines +41 to +54
export const login = async (loginInfo: LoginInfo) => {
try {
const response = await axios.post(`${API_URL}/member/login`, {
authenticationId: loginInfo.id,
password: loginInfo.password,
});

return { data: response.data, location: response.headers.location };
} catch (error) {
if (isAxiosError(error)) {
alert(error.response?.data.message);
}
}
};

Choose a reason for hiding this comment

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

p5)저는 이번에 axios대신에 fetch를 사용했는데여~~찾아보니 axios를 사용하면 형변환을 할 필요가 없다고 하더라구여?! 확실히 도영님 코드 보니까 간결한 느낌이 확 드네여

Comment on lines +23 to +34
if (!id) {
setIdError((prev) => {
return { ...prev, open: true };
});

setTimeout(() => {
setIdError((prev) => {
return { ...prev, open: false };
});
}, 2000);
return;
}

Choose a reason for hiding this comment

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

p4) 혹시 이부분 코드설명가능할까여?!

이전 상태값을 활용하여 입력된 ID 값의 유효성을 검사하고, ID가 존재하지 않을 때 오류 메시지를 관리하는 로직을 보여주는 코드인것 같은데 맞을까여?

Copy link
Member Author

Choose a reason for hiding this comment

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

맞아요! 이 부분은 입력된 아이디가 없을 때, 오류 메세지를 보여주고, setTimeout 함수를 사용해서 2초 뒤에 에러 메시지가 사라지게 구현했습니다!

Comment on lines +135 to +152
const Input = styled.input`
width: 20rem;
padding: 1rem;

border: 1px solid #ccc;
border-radius: 5px;
`;

const Error = styled.p`
position: absolute;
bottom: -1.5rem;

color: red;
font-size: 1.2rem;
`;

const Button = styled.button`
padding: 1rem;

Choose a reason for hiding this comment

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

p5) px대신 rem사용 굿입니당

Comment on lines +6 to +7
import { SignUpInfo, signUp } from '../../api';
import { formatPhoneNumber, validatePassword } from '../../utils';

Choose a reason for hiding this comment

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

p4) 이번 합세 때 path alias관련해서 새롭게 알게 되었는데 담에 한번 적용해보시는 것도 좋을 거같아염

Copy link
Member Author

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
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[ feat ] 4주차 과제 구현
4 participants