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주차 기본/공유 과제 ] 보운이의 로그 #5

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

Conversation

Bowoon1216
Copy link
Contributor

@Bowoon1216 Bowoon1216 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-settings-bowoon


📌 내가 새로 알게 된 부분

  • 이번 과제에 새로 배웠던 useRef를 적용해보려고 노력했습니다
  • const idRef = useRef<HTMLInputElement>(null); const pwRef = useRef<HTMLInputElement>(null); const nicknameRef = useRef<HTMLInputElement>(null); const phoneRef = useRef<HTMLInputElement>(null);
  • 저번 과제 코드리뷰에서 스타일 컴포넌트랑 css파일을 같이써서 혼동이 올 수 있다는 리뷰가 있었어서 최대한 스타일 컴포넌트만 사용하려고 노력했습니다!
  • <InputSort>ID</InputSort> <div style={{width: '100%'}}> <Input onChange={(e) => setId(e.currentTarget.value)} type="text" placeholder="아이디를 입력하세요" />
  • 하지만!! 스타일 속성이 한 두개 있고 이름 지정하는게 더 힘든 상황에서는 인라인으로 스타일 지정해줬습니다.. ㅠㅠ 이런 경우에 다들 어떻게 구현 하셨는지 궁금합니다 👍

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

  • 스타일 컴포넌트를 주로 사용했는데, 특정 요소를 가져와야 하는일이 있어서 id값을 부여한 태그들이 있습니다.. 이는 스타일 컴포넌트의 장점이 클래스 이름을 지정해주지 않아도 된다라는 것을 생각해볼때 (저한테는) 찜찜한 부분이 있었습니다.. ㅠ
  • <Input ref={idRef} id="idInput" type="text" />
  • 혹시 이런 상황이 있으실 때 어떻게 구현하셨는지 궁금합니다

🥺 소요 시간

  • 7h

🌈 구현 결과물

https://oceanic-ant-b58.notion.site/e6bc6909520f45518681bbf706b03ff0?pvs=4

@@ -0,0 +1 @@
export const BASE_URL = 'http://34.64.233.12:8080';

Choose a reason for hiding this comment

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

p5)base_url을 이렇게 빼서 사용할 수 도 있군여 ?!

Copy link
Member

Choose a reason for hiding this comment

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

p1: 서버 주소는 .env 파일로 빼주세요!

Comment on lines +1 to +21
import {Area, HelpP, IMG, Input, InputContaier, InputSort, MoveButton} from '../style/styleComponents';
import loginImg from '../assets/img2.jpg';
import {Link, useNavigate} from 'react-router-dom';
import {useState} from 'react';
import axios from 'axios';
import {BASE_URL} from '../assets/base-url';

export default function LoginPage() {
const navi = useNavigate();
const [id, setId] = useState('');
const [pw, setPw] = useState('');

const handleLogin = async () => {
if (id && pw) {
//만약 모두가 값이 있다면
try {
await axios
.post(`${BASE_URL}/member/login`, {
authenticationId: id,
password: pw,
})

Choose a reason for hiding this comment

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

p5)저는 fetch를 사용했는데 axios를 많이들 사용했더라구여?! 사용하는 방법 배워갑니다~ 그리고 확실히 axios를 사용하면 형변환 할일이 없어 코드가 간결해지는 것 같아염

Comment on lines +45 to +54
function handleShowPw() {
const nowState = document.getElementById('showornot')!.style.visibility;
if (nowState == 'hidden') {
//숨겨뒀다가 열려고 누름 -> 보여야함
document.getElementById('showornot')!.style.visibility = 'visible';
} else {
document.getElementById('showornot')!.style.visibility = 'hidden';
}
}

Choose a reason for hiding this comment

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

p4) 비밀번호 여닫는 폼을 이렇게도 구현할 수 있군여 배워갑니다.

Copy link
Member

Choose a reason for hiding this comment

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

P3: 리액트에서 DOM에 직접접근하여 조작하는 것은 권장되지 않는 방식입니다. state값을 styled 컴포넌트에 인자로 전달에서 인자에 따라 스타일 값을 바꿔주거나, DOM을 직접 조작해야한다면 useRef를 사용하여 조작하는 것이 나을 것 같습니다.

혹시 더 좋은 방법이 있다면 알려주시면 감사하겠습니다.

Comment on lines +45 to +52
<InputSort>ID</InputSort>
<div style={{width: '100%'}}>
<Input
onChange={(e) => setId(e.currentTarget.value)}
type="text"
placeholder="아이디를 입력하세요"
/>
{!id && <HelpP>아이디를 입력하세요</HelpP>}

Choose a reason for hiding this comment

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

p4) 혹시 div style옆 width무슨 의미인지 설명해주실 수 있을까여?! 인라인스타일이면 담부터 지양하시는게 좋을 거같아여~

Copy link
Member

Choose a reason for hiding this comment

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

pr에 작성해주신 고민이 이 부분이네요.

간단한 스타일의 경우 사실 styled component를 추가로 만드는 것이 비효율 적으로 보일 수는 있지만, 나중에 유지보수를 하거나 스타일을 변경해야 하는 상황을 생각해보면, 스타일 코드가 여기저기에 나뉘어져 있다면, 유지보수할 때의 난이도가 굉장히 올라간다고 생각합니다. 따라서 조금 비효율적으로 보일진 몰라도, styled component를 사용하는 프로젝트라면 모든 스타일을 styled로 주는 것이 좋을 것 같습니다.

태그이름은 개인적 규칙이나, 팀의 컨벤션에 맞게 작성하셔야하지만 PR에 물어보셔서 답변해드리자면 InputWrapper등의 이름으로 감싸주면 될 것 같습니다.

Copy link
Member

Choose a reason for hiding this comment

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

p5: 저도 위 생각에 동의합니다. 스타일 코드가 이렇게 나뉘어져있으면 유지보수가 힘들어 질 수 있어요!

아래 링크와 같이 본인 스스로의 규칙을 만들면 좋아요! 그리고 그 규칙들을 협업하는 팀원들과 공유한다면 더 좋을 것 같아요!

https://velog.io/@hayoung474/Front-End-%EB%B3%B5%EC%9E%A1%ED%95%9C-styled-components-%EA%B5%AC%EC%A1%B0-%EA%B0%9C%EC%84%A0%ED%95%B4%EB%B3%B4%EA%B8%B0

Comment on lines +1 to +12
import {useEffect, useState} from 'react';
import {Area, Info, MoveButton, OneLine, PwEditBtn, ShowORNot} from '../style/styleComponents';
import {Link, useParams} from 'react-router-dom';
import axios from 'axios';
import {BASE_URL} from '../assets/base-url';
import PwEditComp from './PwEditComp';

type Info = {
id: string;
nickname: string;
phone: string;
};

Choose a reason for hiding this comment

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

p4)코드를 각각 구조에 맞게 잘 나누신 것 같아여

Comment on lines +16 to +20
/*useEffect(() => {
//하이픈 추가
setPhone({});
}, [phoneRef.current?.value]);*/

Choose a reason for hiding this comment

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

p3) 주석처리한 코드는 없애시는게 좋을 거 같아여~

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.

전반적으로 코드도 깔끔하게 작성하셨고, styled 컴포넌트를 분리하시려는 노력도 너무 좋은 것 같고, TypeScript도 처음 사용해보신다고 했던 것 같은데, 잘 적용해서 과제 완성해주셔서 너무 고생 많으셨습니다!

또한 고민의 흔적들이나 궁금한점들도 PR과 주석으로 잘 남겨주셔서 리뷰하기 더 편했던 것 같습니다! 과제 정말 고생 많으셨습니다!

Comment on lines +7 to +11
<BrowserRouter>
<React.StrictMode>
<App />
</React.StrictMode>
</BrowserRouter>
Copy link
Member

Choose a reason for hiding this comment

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

p4: 일반적으로 애플리케이션의 모든 부분에 StricMode를 적용하기위해서 StricMode를 가장 바깥쪽으로 빼주는 것으로 알고 있습니다.
BrowserRouter와 React.StrictMode의 위치를 바꿔주는 것이 좋을 것 같습니다.

function MainPage() {
const {memberId} = useParams<string>();

console.log(memberId);
Copy link
Member

Choose a reason for hiding this comment

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

p4: 테스트 끝난 console.log는 삭제해주세요~

<div>
<Header>보운이의 로그인</Header>
<Section>
<ReactPlayer url={'/videos/고양이.mp4'} playing={true} loop={true} muted={true} />
Copy link
Member

Choose a reason for hiding this comment

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

p5: ReactPlayer사용해서 동영상 넣으셨군요 좋네요!

Comment on lines +10 to +12
<div>
<Header>보운이의 로그인</Header>
<Section>
Copy link
Member

Choose a reason for hiding this comment

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

p5: 가장 div가 딱히 스타일도 없고, 의미가 없는 태그라면, div대신 <></>로 감싸주는건 어떨까요?

Comment on lines +23 to +27
//location Header에 있는 게 이거 맞나..?
//useParam을 어떻게 활용하지....?
const memberId = res.headers.location;
console.log(memberId);
//아 이걸 useParam써서 메인으로 보내자
Copy link
Member

Choose a reason for hiding this comment

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

p5: ㅋㅋㅋㅋ 생각의 흐름이 주석에 남아있는게 좋네요 ㅋㅋ console 만 지워주시면 좋을 것 같습니다.

Copy link
Member

Choose a reason for hiding this comment

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

p5: ㅋㅋㅋㅋ 너무 멋져요

type="password"
placeholder="비밀번호를 입력하세요"
/>
{!pw && <HelpP>비밀번호를 입력해주세요 제발</HelpP>}
Copy link
Member

Choose a reason for hiding this comment

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

p5: pw state값에 따라 메시지 띄워주는거 좋네요!

Comment on lines +67 to +72
<Input ref={idRef} id="idInput" type="text" />
</InputContaier>
<InputContaier>
<InputSort>비밀번호</InputSort>
<div style={{width: '100%'}}>
<Input ref={pwRef} id="pwInput" type="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: pr에 특정한 요소를 가져오기 위해서, id 값을 쓰셨다고했는데, 혹시 어디에서 사용하고 있을까요? 찾아보기로는 id를 사용하시는 곳이 없는것 같아 질문합니다!

Copy link
Member

Choose a reason for hiding this comment

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

p5: 저도 여기 궁금해요!

Comment on lines +45 to +54
function handleShowPw() {
const nowState = document.getElementById('showornot')!.style.visibility;
if (nowState == 'hidden') {
//숨겨뒀다가 열려고 누름 -> 보여야함
document.getElementById('showornot')!.style.visibility = 'visible';
} else {
document.getElementById('showornot')!.style.visibility = 'hidden';
}
}

Copy link
Member

Choose a reason for hiding this comment

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

P3: 리액트에서 DOM에 직접접근하여 조작하는 것은 권장되지 않는 방식입니다. state값을 styled 컴포넌트에 인자로 전달에서 인자에 따라 스타일 값을 바꿔주거나, DOM을 직접 조작해야한다면 useRef를 사용하여 조작하는 것이 나을 것 같습니다.

혹시 더 좋은 방법이 있다면 알려주시면 감사하겠습니다.

<PwEditBtn onClick={handleShowPw}>{'>'}</PwEditBtn>
</div>
<ShowORNot id="showornot">
<PwEditComp props={memberId!} />
Copy link
Member

Choose a reason for hiding this comment

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

p3: 컴포넌트에 전달하는 인자이름을 props로 하기보다는 변수에 맞는 변수명으로 넘겨주는 것이 좋을것 같습니다!

Copy link
Member

Choose a reason for hiding this comment

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

p5: memberId뒤에 ! 를 붙인 이유가 있으실까요? 에러가 나서 그냥 추가하신 거라면, 사용하는 쪽에서 null 체크를 하는 것으로는 해결이 안되나요?
실제로 url에 id 가 없으면 memberId가 빈 값일 수도 있을 것 같아 여쭤봅니다!

import {BASE_URL} from '../assets/base-url';
import {Info, Input, MoveButton, OneLine} from '../style/styleComponents';

export default function (props: any) {
Copy link
Member

Choose a reason for hiding this comment

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

p3: typescript가 익숙하지 않다고 하셔서 적용한 것 자체가 너무 좋다고 생각하긴합니다.
하지만 제가 조금만 더 욕심내보자면, type에 any를 사용하는 것은 가능한 지양해주시는 것이 좋습니다!
여기서는 type이 string인 props가 들어있는 props객체가 넘어오고 있으므로, { props: string } 로 props의 타입을 설정해주면 되지않을까 싶은데, 혹시 해보시고 안되면 말씀해주세요!

Copy link
Member

Choose a reason for hiding this comment

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

p4: 그리고 여기 컴포넌트 부분은 pages 폴더가 아니라 따로 분리해주면 좋을 것 같아요!
그리고 네이밍도 const PwEditComp = () => {}, 혹은 const index = () => {}로 해주시면 좋을 것 같아요!

Copy link
Member

@pepperdad pepperdad left a comment

Choose a reason for hiding this comment

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

공통된 부분은 컴포넌트로 따로 빼는 연습을 많이 하시면 좋을 것 같아요~!
그리고 스타일 컴포넌트도 같은 부분은 공통으로 사용할 수 있는 common 폴더를 사용하고, 페이지 내에서 사용되는 컴포넌트는 따로 파일로 분리해서 관리하면 더 좋을 것 같아요~!

이번 주 과제 고생하셨습니다! 합세 파이팅🥧

@@ -0,0 +1 @@
export const BASE_URL = 'http://34.64.233.12:8080';
Copy link
Member

Choose a reason for hiding this comment

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

p1: 서버 주소는 .env 파일로 빼주세요!

Comment on lines +45 to +52
<InputSort>ID</InputSort>
<div style={{width: '100%'}}>
<Input
onChange={(e) => setId(e.currentTarget.value)}
type="text"
placeholder="아이디를 입력하세요"
/>
{!id && <HelpP>아이디를 입력하세요</HelpP>}
Copy link
Member

Choose a reason for hiding this comment

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

p5: 저도 위 생각에 동의합니다. 스타일 코드가 이렇게 나뉘어져있으면 유지보수가 힘들어 질 수 있어요!

아래 링크와 같이 본인 스스로의 규칙을 만들면 좋아요! 그리고 그 규칙들을 협업하는 팀원들과 공유한다면 더 좋을 것 같아요!

https://velog.io/@hayoung474/Front-End-%EB%B3%B5%EC%9E%A1%ED%95%9C-styled-components-%EA%B5%AC%EC%A1%B0-%EA%B0%9C%EC%84%A0%ED%95%B4%EB%B3%B4%EA%B8%B0

<InputSort>ID</InputSort>
<div style={{width: '100%'}}>
<Input
onChange={(e) => setId(e.currentTarget.value)}
Copy link
Member

Choose a reason for hiding this comment

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

p5: 저는 e.target.value로 사용했는데 차이점을 알고 계시나요?
한 번 참고하시길 바랍니다!

https://velog.io/@sunkim/Javascript-e.target%EA%B3%BC-e.currentTarget%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90

Comment on lines +67 to +72
<Input ref={idRef} id="idInput" type="text" />
</InputContaier>
<InputContaier>
<InputSort>비밀번호</InputSort>
<div style={{width: '100%'}}>
<Input ref={pwRef} id="pwInput" type="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: 저도 여기 궁금해요!

Comment on lines +39 to +42
authenticationId: idRef.current?.value,
password: pwRef.current?.value,
nickname: nicknameRef.current?.value,
phone: phoneRef.current?.value,
Copy link
Member

Choose a reason for hiding this comment

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

p5: 우와 state를 안쓰고 이렇게 하셨네요!

import {BASE_URL} from '../assets/base-url';
import {Info, Input, MoveButton, OneLine} from '../style/styleComponents';

export default function (props: any) {
Copy link
Member

Choose a reason for hiding this comment

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

p4: 그리고 여기 컴포넌트 부분은 pages 폴더가 아니라 따로 분리해주면 좋을 것 같아요!
그리고 네이밍도 const PwEditComp = () => {}, 혹은 const index = () => {}로 해주시면 좋을 것 같아요!

const newPwRef = useRef<HTMLInputElement>(null);
const newAgainPwRef = useRef<HTMLInputElement>(null);

function handleEditPw() {
Copy link
Member

Choose a reason for hiding this comment

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

p4: 여기도 화살표 함수로 바꾸면 좋을 것 같아요!

setPhone({});
}, [phoneRef.current?.value]);*/

function checkEachInput(props: React.RefObject<HTMLInputElement>) {
Copy link
Member

Choose a reason for hiding this comment

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

p4: 아래의 handleSignUp 함수는 화살표 함수로 작성했는데, 여기는 왜 그냥 함수로 작성하셨나요!? 혹시 본인만의 컨벤션이 있나요?

<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>보운이의 로그인</title>
</head>
<body style="margin: 0px; background-color: rgb(184, 202, 186)">
Copy link
Member

Choose a reason for hiding this comment

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

p4: 여기 글로벌 스타일로 빼면 좋을 것 같아요!

}

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

Successfully merging this pull request may close these issues.

4 participants