[TIL] 내일배움캠프 React 과정 2022.12.23

Today I Learned

  • 회원가입 유효성 검사 추가
  • 로그인 기능 구현

 


팀 프로젝트 진행 기록

회원가입 시 DB 구조 수정하기

"users": [
    {
      "id": "93e88455-88ac-4aa1-92f8-d02e06748d3a",
      "userId": "eeee",
      "userPw": "12345678",
      "userName": "f",
      "isLogin": true
    },
  ]

회원가입할 때 서버에 이런 구조로 데이터가 저장되는데 기본적으로 userId와 userPw, userName을 저장했다. 그리고 애초에 가입할 때 userId가 중복되지 않게 할 거라 id가 필요할까 생각하고 넣지 않았었는데 json-server가 자동으로 생성해주길래 얘를 아예 uuid로 설정하고 마이페이지의 param.id로 쓰기로 했다. 그리고 isLogin이라는 값은 로그인 상태인지를 판단해서 헤더에 로그인<->로그아웃을 바꿔준다든가 로그인했을 때만 글을 작성할 수 있게 하는 등의 기능을 위해 추가했다.

 

근데 생각해보니까 로그인 회원가입 페이지 모두 axios.get으로 서버의 데이터를 불러오는 과정도 필요할 거 같다. 왜냐하면 회원가입할 때는 중복된 아이디를 걸러내기 위해서 비교해 볼 기존 데이터가 필요하고, 로그인 페이지에는 당연히 데이터가 있어야 아이디와 비밀번호가 일치하는지 확인할 수 있기 때문이다. 이거 생각보다 일이 커지겠는데...

 

+) 로그인 했을 경우 isLogin도 true로 변경해야 하기 때문에 axios.patch까지 사용해야 할 거 같다 ㅎㅎㅎㅎ

 

 

javascript로 로그인 기능 구현하기

콘솔에 찍어봤을 때 state를 불러오지 못한다.

 

어떻게 이런 바보같은 실수를... extraReducers에 fulfilled와 rejected를 적어주지 않은 것을 발견했다. 이 부분만 수정하니 useSecletor로 state의 데이터를 잘 불러온다 ^^

 

const logInHandler = () => {
    const user = users.find((user) => user.userId === userId && user.userPw === userPw)

    if (user === undefined) {
        alert("로그인 실패")
    } else {
        alert("로그인 성공")
    }
};

로그인 로직을 어떻게 설정할까 고민하다 find 메서드를 사용해보기로 했다. 조건을 만족하는 요소를 찾지 못하면 undefined를 반환하는 find의 특성을 이용해서 userId와 userPw가 모두 일치할 때만 로그인할 수 있고 그 외의 경우에는 로그인에 실패한다. 아직 두 가지 경우만 설정했지만 추후에 아이디가 틀렸을 때, 비밀번호가 틀렸을 때 등의 다양한 경우를 설정할 수도 있을 거 같다.

 

// 모두 일치할 때
} else {
    alert("로그인 성공")

    const switchUser = {
        id: user.id,
        userId: user.userId,
        userPw: user.userPw,
        loggedIn: true,
    }

    // 로그인 성공하면 loggedIn을 true로 변경함
    dispatch(__switchLoggedIn(switchUser))
}

로그인 성공하면 loggedIn을 true로 변경하도록 axios.patch를 실행하는 thunk 함수를 하나 더 만들었다.

 

 

useRef() 사용하기

어제 리액트 쿼리 강의를 보다가 ref 쓰는 개 생각보다 간단해 보여서 잠깐 시간을 내서 ref 강의를 시청했다ㅎㅎ 지금까지는 queryselector로 특정 요소를 가져왔었는데 이제 ref를 쓰면 될 거 같다!

 

사용 예시

import { useRef } from 'react';

const day_input = useRef();
const time_input = useRef();

useRex를 import하고 useRef로 변수를 선언한다.

 

<input ref={day_input} />
<input ref={time_input} />

ref를 사용할 요소에 ref를 사용한다.

 

day_input.current.value === ""

time_input.current.focus();

최종적으로 ref를 통해 quert selector와 비슷하게 특정 DOM을 선택해서 사용할 수 있다.

 

 

회원가입 시 유효성 검사

일단 if문으로 아이디랑 텍스트, 닉네임을 모두 입력하지 않았을 경우를 if문으로 설정해주려고 했는데 이게 3x3=9가지 경우를 if문으로 작성해야 되는 건가 혼란스러웠다. 근데 if문에 return false;를 작성하면 아래 코드부터는 아무것도 진행하지 말아라!라는 의미가 된다는 것을 알게 됐다.

 

아이디 유효성을 검사하는 정규표현식(Regular Expression)

var idRegExp = /^[a-zA-z0-9]{4,12}$/; //아이디 유효성 검사
  •  / : 자바스크립트의 정규표현식의 처음과 끝을 의미한다.
  • [ ] : 문자셋이다. 예를 들면 [a-z]라고 적을 경우 정규표현식에 만족해야 하는 값들은 반드시 a~z사이의 값만 넣을 수 있다.
  • ^ : 문장의 처음을 뜻한다.
  • $ : 문장의 마지막을 뜻한다.
  • { } : 문자열 길이를 뜻한다. 예를 들어 {4,12}일 경우 최소 길이 4, 최대 길이 12이다.

/^[a-zA-z0-9]{4,12}$/ 을 분석하면,

→ 영문 대/소문자, 숫자만 사용할 수 있고 길이는 최소 4, 최대 12를 만족해야 정규표현식에 만족한다.

 

function checkUserId(id) {
    var idRegExp = /^[a-zA-z0-9]{4,12}$/; //아이디 유효성 검사
    if (!idRegExp.test(id)) {
        alert("아이디는 영문 대소문자와 숫자 4~12자리로 입력해야합니다!");
        form.userId.value = "";
        form.userId.focus();
        return false;
    }
    return true; //확인이 완료되었을 때
}

if조건문에서 test는 정규표현식과 id의 값이 일치하는지 아닌지 확인하는 함수이다. 일치하면 true를 return 일치하지 않으면 false를 return한다. 만약 일치하지 않을 경우 사용자에게 해당 조건을 알려주는 알림창을 띄워주고 폼에 입력된 값을 공백으로 초기화하고 포커스를 맞춰준다. 정규표현식과 id의 값이 같을 경우 return true를 하고 다음 함수를 실행한다.

 

RegExp.prototype.test()

text() 메서드는 주어진 문자열이 정규 표현식을 만족하는지 판별하고, 그 여부를 true 또는 false로 반환한다.

 

참고

https://goddino.tistory.com/52

https://olsh1108o.tistory.com/entry/JS-회원가입-유효성-검사

 

 

posts와 comments DB 구조 수정하기

{
  "posts": [],
  "commnets": []
}

원래는 posts와 comments를 나누고 댓글을 작성할 때 댓글이 속한 본문의 postId를 포함해서 넘겨줄 생각이었다.

 

{
  "posts": [
    {
      "id": 0,
      "title": "탕수육",
      "categoryA": "부먹",
      "categoryB": "찍먹",
      "like": 0,
      "comments": [
        {
          "id": 0,
          "comment": "나는 부먹이 좋다!",
          "isA": true
        },
        {
          "id": 1,
          "comment": "나는 찍먹이 좋다!",
          "isA": false
        },
        {
          "id": 2,
          "comment": "나는 부먹이 좋다!",
          "isA": true
        }
    }
  ]
}

그런데 메인페이지에서 posts를 불러오는 것 외에 각 posts에 속하는 commets 데이터까지 필요한 부분이 있어서 DB 구조를 수정해서 해당 본문 객체 안에 commentfmf 넣기로 했다. 이렇게 하니까 메인페이지에서 각 게시물마다 속한 댓글을 불러오기는 편해졌는데 댓글을 등록할 때 문제가 생겼다.

 

axios.post("http://localhost:3001/posts/1", payload);

posts에서 특정 아이디를 가진 게시물에 comment를 추가하면 되지 않을까 싶었지만 계속 다른 post들과 동일 선상에 데이터가 추가되어서 실패했고 결국 axios.post로 객체 안에 객체를 추가하는 방법은 찾을 수 없었다.

 

그래서 다시 원래 생각했던 대로 posts와 comments를 분리하고 댓글을 작성할 때 postId를 넣어주는 방법으로 돌아가기로 했다. 메인페이지에 각 게시물에 속한 댓글을 불러오는 것은 팀원들과 늦은 시간까지 머리를 맞대고 생각한 끝에 posts를 map을 돌린 후 그 안에서 다시 한번 comments를 map으로 돌리는 방법으로 해결할 수 있었다.

 

 


회고

오늘은 오후 5시에 크리스마스 기념 모든 트랙의 대원들이 zep에서 모이는 행사가 있었다. colormytree에 내배캠을 수료할 시점의 나에게 메시지를 남기기도 하고 고마운 동기들에게 마음을 표현하는 내배캠 어워즈도 있었다. 상 받은 분들은 캠과 마이크를 켜고 소상수감을 말했는데 다들 텐션이 좋으셔서 재미있었다 ㅋㅋ

https://colormytree.me/2022/01GM1ZZZ292YDDZDD3BCTEEKBV

 

저녁에는 DB 구조를 어떻게 해야할까 팀원들과 함께 이것저것 수정하면서 시간을 보냈다. 결국 자정을 넘길 때까지 메인페이지에 데이터를 불러오는 문제를 해결하느라 피곤했지만, 덕분에 크리스마스이브 기념으로 귀여운 인증샷도 찍을 수 있었다🥰

 

팀플에서 내가 담당한 역할의 진행 상황을 남겨보자면 오늘은 거의 하루종일 회원가입 유효성 검사를 위해 시간을 보냈고 내일은 비밀번호를 hash화 해서 DB에 저장하는 방법을 찾아보고, CSS를 간단하게 구현해서 추가 기능이었는 form help text를 처음부터 구현하는 게 편할 거 같다. 아이디 형식이 틀렸을 때, 아이디를 입력하지 않았을 때 등 모든 상황을 alert으로 알려주려니 우선순위를 어디에 둬야 할지 모르겠어서 오히려 헷갈린다.