미새문지

24.09.02 day58 작업 일지 본문

개발 TIL

24.09.02 day58 작업 일지

문미새 2024. 9. 2. 23:19
728x90

타입스크립트를 쓰는 순간부터 모든 값에 타입을 명시해줘야 하는데 아직 익숙하지 않은 것 같다.

회원가입 3페이지에는 생일, 성별, 피부타입, 퍼스널컬러, 피부 고민  이렇게 총 5개의 작성칸이 있는데 본인은 api로 백엔드에 데이터를 보내줄 때 각각 다른 값으로 전달하는 줄 알고 있었다.

생일과 성별은 그렇다고 치고 피부 타입과 퍼스널 컬러는 radio버튼이라 선택지 중 하나만 선택할 수 있기 때문에  이것도 선택된 값으로 보내주고 제일 하단의 피부 고민만 checkbox여서 이 값만 배열로 선택해 보낼 예정이였지만, 오늘 재희넴과 작업 현황을 공유하며 회의를 한 결과 피부, 컬러, 고민 셋 다 한 배열로 받는걸로 정해졌다.

  const handleSkinTypeChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const value = parseInt(e.target.value, 10);
    const updatedTrouble = trouble.filter((item) => item < 1 || item > 5);
    const newTrouble = [...updatedTrouble, value];
    const sorted = [...newTrouble].sort((a, b) => a - b);

    dispatch(setSkinType(value));
    dispatch(setTrouble(sorted));
    console.log(sorted);
  };

  const handleColorChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const value = parseInt(e.target.value, 10);
    const updatedTrouble = trouble.filter(
      (item) => !(item >= 6 && item <= 9) && item !== 0
    );
    const newTrouble = [...updatedTrouble, value];
    const sorted = [...newTrouble].sort((a, b) => a - b);

    dispatch(setColor(value));
    dispatch(setTrouble(sorted));
    console.log(sorted);
  };

  const handleTroubleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const value = parseInt(e.target.value);
    let updateValue;

    if (trouble.includes(value)) {
      updateValue = trouble.filter((item) => item !== value);
    } else {
      updateValue = [...trouble, value];
    }

    const sorted = [...updateValue].sort((a, b) => a - b);

    dispatch(setTrouble(sorted));
    console.log(updateValue);
  };

radio버튼은 한 개의 값만 선택이 되기 때문에 handler 함수를 통해 값을 지정받아 배열에 넣는방식으로 작성했다. 피부 타입부터 시작해서 value값이 1~ 로 정수값으로 들어간다. 그래서 해당 라디오 값을 받고 나서 현재 배열에 선택지의 value가 있으면 전부 제거하고 선택값을 배열에 추가하는 방식으로 했다.

checkbox경우에는 해당 박스 클릭 시 선택한 값이 배열에 있으면 제거 없으면 추가하는 방식으로 진행했다. 그리고 백엔드에 데이터를 올바르게 보내주기 위해 오름차순으로 정렬했다.

 

생일의 경우에는 string타입이 아니라 Date 타입으로 보내주는 걸로 정해져서 해당 타입을 수정했다.

근데 리덕스에 저장될 값을 Date로 하게 되면 input에 입력할 때 타입이 꼬여 에러가 발생한다. 타입을 중간에 바꿀생각을 못하고 계속 Date값으로 받으려고 하다보니 너무 시간을 날려먹었다..

import { createSlice, PayloadAction } from "@reduxjs/toolkit";

interface SignupState {
  email: string;
  password: string;
  passwordConfirm: string;
  nickname: string;
  birth: string;
  gender: string;
  skinType: number;
  color: number;
  trouble: number[];
}

const initialState: SignupState = {
  email: "",
  password: "",
  passwordConfirm: "",
  nickname: "",
  birth: "",
  gender: "",
  skinType: 0,
  color: 0,
  trouble: [],
};

const signupSlice = createSlice({
  name: "signup",
  initialState,
  reducers: {
    setEmail: (state, action: PayloadAction<string>) => {
      state.email = action.payload;
    },
    setPassword: (state, action: PayloadAction<string>) => {
      state.password = action.payload;
    },
    setPasswordConfirm: (state, action: PayloadAction<string>) => {
      state.passwordConfirm = action.payload;
    },
    setNickname: (state, action: PayloadAction<string>) => {
      state.nickname = action.payload;
    },
    setBirth: (state, action: PayloadAction<string>) => {
      state.birth = action.payload;
    },
    setGender: (state, action: PayloadAction<string>) => {
      state.gender = action.payload;
    },
    setSkinType: (state, action: PayloadAction<number>) => {
      state.skinType = action.payload;
    },
    setColor: (state, action: PayloadAction<number>) => {
      state.color = action.payload;
    },
    setTrouble: (state, action: PayloadAction<number[]>) => {
      state.trouble = action.payload;
    },
  },
});

export const {
  setEmail,
  setPassword,
  setPasswordConfirm,
  setNickname,
  setBirth,
  setGender,
  setSkinType,
  setColor,
  setTrouble,
} = signupSlice.actions;
export default signupSlice.reducer;

리덕스 코드는 string으로 받아오고 입력값을 받을 때도 string으로 받아준 다음 데이터를 보낼 때 타입을 Date로 변경하게 코드를 작성했다.

  const handleBirthChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const inputValue = e.target.value;

    if (/^\d*$/.test(inputValue) && inputValue.length <= 8) {
      dispatch(setBirth(inputValue));
    }
  };

생일의 정규식은 숫자만 입력 가능하게 했고 연도, 월, 일을 포함해 총 8자 이하까지 작성할 수 있게 했다. 해당 규칙에 맞는 값이 입력되면 리덕스의 birth value에 값이 들어가게 된다.

 

  const handleSubmit = async () => {
    try {
      const formattedBirth = new Date(
        birth.slice(0, 4) + "-" + birth.slice(4, 6) + "-" + birth.slice(6, 8)
      );

      const userData = {
        email: email,
        password: password,
        nickname: nickname,
        birth: formattedBirth,
        gender: gender,
        trouble: trouble,
      };

      const response = await axios.post("/api/user/register", userData);

      console.log("회원가입 성공", response.data);
    } catch (error) {
      console.log("회원가입 실패", error);
    }
  };

데이터를 다 입력하고 완료 버튼을 누르면 서버에게 api를 통해 데이터를 전송하는데 백엔드의 api 명세서를 확인했을 때 회원가입은 POST방식으로 /api/user/register로 데이터를 보내면 된다.

데이터를 보내기 전에 string으로 저장했던 리덕스 birth값을 연월일방식으로 "-"특수문자를 이용해 나눠졌다. 해당 값은 서버에 들어갈 때 yyyy-mm-dd 방식으로 들어가게 된다.

회원가입의 모든 데이터를 userData 객체로 묶어 해당 api로 요청을 보낸다. 이러한 요청 방식은 비동기적으로 이루어져야 하기 때문에 async, await을 사용해 비동기적으로 전송해준다.

요청 성공 시 회원가입 성공이라는 console이 실패 시 에러코드와 함께 회원가입 실패 console이 뜬다.

 

회의를 통해 회원가입 2페이지인 닉네임 작성 창에서 정규식과 랜덤 생성 방식을 정했다.

  const handleNicknameCheck = (e: React.ChangeEvent<HTMLInputElement>) => {
    const value = e.target.value;
    const isValid = /^[a-zA-Z0-9가-힣]*$/.test(value);

    dispatch(setNickname(value));

    if (value.length <= 10 && value.length > 0 && isValid) {
      setNicknameValid(true);
    } else {
      setNicknameValid(false);
    }
  };

닉네임은 특수문자가 불가능하며 한글, 영어, 숫자만 가능하게 했다. 입력된 값은 리덕스에 담기게 되며 1~10글자와 정규식을 통과한 닉네임이면 다음 버튼이 활성화되고 그렇지 않으면 버튼이 비활성화 되어 다음 페이지로 못넘어가게 된다.

 

 

닉네임 랜덤 생성은 버튼을 누를 시 랜덤으로 지정된 값을 합쳐서 값이 입력되는데 합쳐지는 값은 색상과 동물로 정했다.

export const colors = [
  "빨강색",
  "주황색",
  "노랑색",
  "초록색",
  "파랑색",
  "남색",
  "보라색",
  "검정색",
  "하양색",
  "핑크색",
  "회색",
  "자주색",
  "주홍색",
  "다홍색",
  "청록색",
  "살색",
  "옥색",
];

export const animals = [
  "호랑이",
  "사자",
  "곰",
  "토끼",
  "고양이",
  "개",
  "여우",
  "코끼리",
  "판다",
  "늑대",
  "사막여우",
  "고릴라",
  "원숭이",
  "닭",
  "칠면조",
  "악어",
  "도마뱀",
  "코뿔소",
  "하마",
];
import { animals, colors } from "../../data/Data";

Data파일에 임시로 색상과 동물의 한글이름을 배열로 등록해서 닉네임 작성 페이지에 가져왔다.

  const handleRandomNickname = () => {
    const randomColor = colors[Math.floor(Math.random() * colors.length)];
    const randomAnimal = animals[Math.floor(Math.random() * animals.length)];
    const randomNickname = `${randomColor}${randomAnimal}`;

    dispatch(setNickname(randomNickname));
    setNicknameValid(true);
  };

버튼을 누를 시 Math.random()을 통해 해당 배열의 길이만큼 난수를 생성해서 무작위로 선택한다.

이 후 선택된 값 두개를 합쳐서 새로운 닉네임으로 지정하고 해당 값을 리덕스에 저장해서 화면에 출력해준다.

닉네임도 axios를 통해 DB에 중복된 닉네임이 있는지 확인하는 코드를 짜야 되지만 명세서말고 재희님이 따로 작성해준게 있었는데 잊어먹어서 다음 회의때 바로 적용하려고 한다.


회원가입은 추가적인 부분이 없으면 마무리되었고 이메일 로그인 방식도 axios를 통해 간단히 구현했다.

import React, { useState } from "react";
import "../../scss/login/emailLogin.scss";
import CommonInput from "../common/commonInput";
import PwVisible from "../common/pwVisible";
import axios from "axios";

// interface EmailLoginProps {
//   typeValue: string;
//   placeholderValue: string;
//   value: string;
//   onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
// }

const EmailLoginBox: React.FC = () => {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [showPassword, setShowPassword] = useState(false);

  const handleSubmit = async () => {
    try {
      const userData = {
        email,
        password,
      };

      console.log(userData);

      const response = await axios.post("/api/user/login", userData);

      console.log("로그인 성공", response.data);
    } catch (error) {
      console.log("로그인 실패", error);
    }
  };

  return (
    <>
      <div className="loginInputForm">
        <form action="">
          <CommonInput
            typeValue="email"
            placeholderValue="이메일"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
          />
          <CommonInput
            typeValue={showPassword ? "text" : "password"}
            placeholderValue="비밀번호"
            value={password}
            onChange={(e) => setPassword(e.target.value)}
          />
          <PwVisible onToggle={setShowPassword} />
          <span className="forgotPw">
            비밀번호를 잊으셨나요? <span>비밀번호 재설정</span>
          </span>
          <button className="loginBtn" onClick={handleSubmit}>
            로그인
          </button>
        </form>
      </div>
    </>
  );
};
export default EmailLoginBox;

로그인은 회원가입처럼 여러 페이지가 있는게 아니기 때문에 리덕스를 쓸 필요없이 useState로 해당 페이지에서 상태관리를 해도 괜찮다. 입력된 이메일과 비밀번호를 객체로 묶어 회원가입처럼 api 명세서에 맞게 요청 전송해주면 되며, 해당 api는 POST 방식에 /api/user/login으로 요청전송하면 된다.

728x90

'개발 TIL' 카테고리의 다른 글

24.09.04 day60 jwt 오류 해결, 카카오 로그인 구현 중  (3) 2024.09.04
24.09.03 day59 작업 일지  (1) 2024.09.03
24.08.30 day57 작업 일지  (0) 2024.08.30
24.08.29 day56 작업 일지  (0) 2024.08.29
24.08.28 day55 작업 일지  (2) 2024.08.28