[ react ] 리액트랑 Mysql 연결하기 (signup, login, validate) & .env 환경변수 사용

Suji Kang·2023년 9월 22일
0

🐾 .env 환경변수

📝 .env로 시작하는 파일

node가 실행되면서 (react, express 등이 실행될때) 사용하는 환경 변수를 설정해주는 파일

 .env
    가장 최상위,(우선순위가 낮다)
  .env.localhost
    .env보다 우선순위가 놓은 파일로서 test 환경 제외하고 모든 환경에서 로딩됨
  .env.development
    개발환경에서만 로딩되는 환경변수들
  .env.test
    테스트 환경에서만 로딩되는 환경변수들
  ...

리액트랑 mysql이랑 연결을 하려면, mysql 정보를 써줘야하는데, 정보를 그대로 올리면 정보가 모든사람이 볼수있겠지❓🤨 그래서 환경 변수에 쓰고 .gitignore에 넣어줘야해 ❗️

.gitignore

.env.local

env.local

DB_HOST=localhost
DB_USER=board23user
DB_PASSWORD=1234
DA_PORT=3306
DB_DATABASE=board23

app.js

//mysql설정
const pool = mysql.createPool({
    host: process.env.DB_HOST,
    user: process.env.DB_USER,
    database: process.env.DB_DATABASE,
    password: process.env.DB_PASSWORD,
    port: process.env.DB_PORT,
});

.env 파일에 있는 환경변수를 실행중인 프로그램의 env객체 안에 넣는 방법 dotenv 라이브러리를 활용한다.

dotenv.config()
함수를 사용하면 기본적으로 .env 안에 있는 변수들을 process.env안에 만들어준다.

이때 만들고 싶은 변수가 .env.local 안에있는 변수라면 dotenv.config({path:'경로'})옵션을 추가하여 만들수있다.

🔎 dotenv 라이브러리설치

npm i dotenv

app.js

const dotenv = require('dotenv')
dotenv.config({path: '../.env.local'});

📝 src폴더pages폴더 만들어서 auth폴더를 만들고, 그안에login.js,
singup.js 파일을 만든다.

📌 login pagesignup page스타일이 똑같다.
이렇게 똑같을때는 굳이 둘다 만들필요가 없다.

src폴더styles폴더 만들어서 auth폴더를 만들고, 그안에auth.styles.js 파일을 만든다.

auth.styles.js

import styled from "@emotion/styled";

export const BgImg = styled.div`
  /* border: 5px solid black; */
  width: 100%;
  background-image: url('/auth-bg.jpg');
  background-repeat: no-repeat;
  background-size: cover;
  background-position: center;
`
export const Wrap = styled.div`
  /* border: 5px solid green; */
  min-height: 100vh;
  /* background-color: black; */
  background-color: hsla(var(--ui-color-background-100), 0.8);
  display: flex;
  justify-content: center;
  align-items: center;
  position: relative;

  padding-top: 100px;
  padding-bottom: 150px;
`

export const AuthBox = styled.div`
  /* border: 5px solid black; */
  width: 400px;
  display: flex;
  flex-direction: column;
  row-gap: 30px;
  & > h1{
    text-align: center;
  }
`

export const AuthForm = styled.form`
  /* border: 3px solid gold; */
  display: flex;
  flex-direction: column;
  row-gap: 60px;
`

export const Input = styled.input`
  background:none;
  padding: 10px 5px;
  border-radius: 10px;
  border: 1px solid hsl(var(--ui-color-foreground-090));
  outline: none;
  width: 100%;
`
export const Button = styled.button`
  padding: 10px 5px;
  border-radius: 10px;
  cursor: pointer;
  border: none;
  background-color: hsl(var(--ui-color-primary));
`
export const ErrMsg = styled.p`
  color: red;
  font-size: var(--font-xs);
  padding-left: 10px;
`

export const InputBoxWrap = styled.div`
  display: flex;
  flex-direction: column;
  row-gap: 20px;
`
export const LogoImg = styled.img`
  width: 100px;
  align-self: center;
`
export const AuthBody = styled.div`
  display: flex;
  flex-direction: column;
  row-gap: 20px;
`
export const Line = styled.div`
  border-top: 1px solid black;
  border-left: 1px solid black;
`
export const AuthFooter = styled.div`
  display: flex;
  justify-content: center;
  column-gap: 15px;
  align-items: center;
  & > .line{
    height: 10px;
  }
`
export const CancelIcon = styled.div`
  position: absolute;
  right: 30px;
  top: 30px;
  cursor: pointer;
`

export const Select = styled.select`
  width: 100%;
  padding: 10px 15px;
  background:none;
  & + input{
    margin-top: 5px;
    border-radius: 1px;
  }
`
export const Option = styled.option`
  background: rgba(256, 256, 256, 0.8);
  padding: 10px;
`

만든것을 모두 다른파일에서도 사용할수있도록 export 해준다


📝 components 폴더를 만들어준다

signup.js

import { AuthBody, AuthBox, AuthForm, BgImg, Button, CancelIcon, ErrMsg, Input, InputBoxWrap, Option, Select, Wrap } from "../../styles/auth/auth.styles";
import CloseIcon from '@mui/icons-material/Close';
import { useState } from "react";

const JoinPage = () => {
  // state 변수 9 개
  // 사용자가 email 에 입력한 값
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [passwordCheck, setPasswordCheck] = useState('');
  const [question, setQuestion] = useState('');
  const [answer, setAnswer] = useState('');

  const [emailErrMsg, setEmailErrMsg] = useState('');
  const [passwordErrMsg, setPasswordErrMsg] = useState('');
  const [passwordCheckErrMsg, setPasswordCheckErrMsg] = useState('');
  const [answerErrMsg, setAnswerErrMsg] = useState('');

  const emailInputHandler = (e) => {
      setEmail(e.target.value);

      const emailText = e.target.value;

      if (emailText === '') {
          setEmailErrMsg('이메일은 필수 입력해주세요');
      } else if (!emailText.includes('@')) {
          setEmailErrMsg('이메일 형식이 올바르지 않습니다.');
      } else {
          setEmailErrMsg('');
      }
  }

  const passwordInputHandler = (e) => {
      const passwordText = e.target.value;
      setPassword(passwordText);


      if (passwordText === '') {
          setPasswordErrMsg('비밀번호는 필수 입력입니다.');
      } else if (passwordText.length < 6) {
          setPasswordErrMsg('비밀번호는 6자리 이상이어야 합니다.');
      } else {
          setPasswordErrMsg('');
      }

      if (passwordText !== passwordCheck) {
          setPasswordCheckErrMsg('비밀번호가 일치하지 않습니다.');
      }
  }

  const passwordCheckInputHandler = (e) => {
      const passwordCheckText = e.target.value;
      setPasswordCheck(passwordCheckText);

      if (passwordCheckText === '') {
          setPasswordCheckErrMsg('비밀번호 확인은 필수 입력입니다.');
      } else if (passwordCheckText !== password) {
          setPasswordCheckErrMsg('비밀번호가 일치하지 않습니다.');
      } else {
          setPasswordCheckErrMsg('');
      }
  }

  const onSelectHandler = (e) => {
      // console.log([e.target]);
      setQuestion(parseInt(e.target.value));
  }

  const onAnswerInputHandler = (e) => {
      setAnswer(e.target.value);
      if (e.target.value === '') {
          setAnswerErrMsg('이메일 찾기 응답은 필수 입력 값입니다.');
      } else {
          setAnswerErrMsg('');
      }
  }

  return (
      <BgImg>
          <Wrap>
              <CancelIcon><CloseIcon /></CancelIcon>
              <AuthBox>
                  <h1>회원가입</h1>
                  <AuthBody>
                      <AuthForm>
                          <InputBoxWrap>
                              <div className="input-box">
                                  <Input onChange={emailInputHandler}
                                      type="text" placeholder="아이디" />
                                  <ErrMsg>{
                                      emailErrMsg
                                  }</ErrMsg>
                              </div>
                              <div className="input-box">
                                  <Input onChange={passwordInputHandler}
                                      type="password" placeholder="비밀번호" />
                                  <ErrMsg>
                                      {
                                          passwordErrMsg
                                      }
                                  </ErrMsg>
                              </div>
                              <div className="input-box">
                                  <Input onChange={passwordCheckInputHandler}
                                      type="password" placeholder="비밀번호 확인" />
                                  <ErrMsg>{
                                      passwordCheckErrMsg
                                  }</ErrMsg>
                              </div>
                              <div className="input-box">
                                  <Select onChange={onSelectHandler}>
                                      <Option value={1}>내가 태어난 곳은?</Option>
                                      <Option value={2}>어린시절 나의 별명은?</Option>
                                      <Option value={3}>나의 강아지 이름은?</Option>
                                  </Select>
                                  <Input onChange={onAnswerInputHandler}
                                      type="text" placeholder="이메일을 찾을 때의 질문에 답하세요" />
                              </div>
                          </InputBoxWrap>
                          <Button>회원가입하기</Button>
                      </AuthForm>
                  </AuthBody>
              </AuthBox>
          </Wrap>
      </BgImg>
  );
}

export default JoinPage;

login.js

import { Link } from "react-router-dom";
import { AuthBody, AuthBox, AuthFooter, AuthForm, BgImg, Button, CancelIcon, ErrMsg, Input, InputBoxWrap, Line, LogoImg, Wrap } from "../../styles/auth/auth.styles";
import CloseIcon from '@mui/icons-material/Close';

const LoginPage = () => {
  return (
      <BgImg>
          <Wrap>
              <CancelIcon><CloseIcon /></CancelIcon>
              <AuthBox>
                  <LogoImg src="/logo.svg" alt="logo" />
                  <AuthBody>
                      <AuthForm>
                          <InputBoxWrap>
                              <div className="input-box">
                                  <Input type="text" placeholder="아이디" />
                                  <ErrMsg>이메일은 필수 입력입니다.</ErrMsg>
                              </div>
                              <div className="input-box">
                                  <Input type="password" placeholder="비밀번호" />
                                  <ErrMsg >비밀번호는 필수 입력입니다.</ErrMsg>
                              </div>
                          </InputBoxWrap>
                          <Button>로그인 하기</Button>
                      </AuthForm>
                      <Line className="line"></Line>
                      <AuthFooter>
                          <Link to="">이메일 찾기</Link>
                          <Line className="line"></Line>
                          <Link to="">비밀번호 찾기</Link>
                          <Line className="line"></Line>
                          <Link to="/join">회원가입 하기</Link>
                      </AuthFooter>
                  </AuthBody>
              </AuthBox>
          </Wrap>
      </BgImg>

  );
}

export default LoginPage;

📝 form tag안에 있는 버튼을 클릭하면 화면이 refresh되는것이 기본동작원리이다. 속에있는값이 전달이 되고, 다시 새로고침이 된다. e.preventDefault(); 를 쓰면 클릭해도 새로고침이 안된다.


🐾 유효성 검사의 논리 (2:26/9/22)

  1. input 태그의 값이 변화할때마다 state변수저장해준다
  2. 버튼을 클릭 했을떄 state변수에 저장된 값을 유효한 값인지 검사한다.
    그리고, 유효한 값이 아니라면 에러메세지 state변수를 변화해준다.
    에러메세지 state변수바뀌었으니까, re-rendering이 된다(에러메세지가 보인다)

모든 input state 변수의 값이 유효한 값임을 확인했다면 값을 서버쪽으로 전송한다.


📝 state변수는 몇개 만들어야할까❓

정답은❓ 8개

그래서, 여기서 ❗️

📌 유효성 검사를 쉽게 하도록 도와주는 라이브러리를

formik
yup
라이브러리를 사용한다.

👉 일단은 라이브러리는 없이 사용해보자 ❗️❗️

유효성 검사
validate.js

import React, { useState } from 'react';

const ValidateTest = () => {
   const [email, setEmail] = useState('');
   const [password, setPassword] = useState('');

   const [emailErrMsg, setEmailErrMsg] = useState('');
   const [passwordErrMsg, setPasswordErrMsg] = useState('');

   const onJoin = (e) => {
       e.preventDefault();//클릭해도 리프레시 안되게함
       console.log('email', email);
       console.log('password', password);

       if (email === '') {
           setEmailErrMsg('이메일을 입력해주세요');
       } else if (!email.includes('@')) {
           setEmailErrMsg('이메일 형식이 올바르지 않습니다.');
       } else {
           setEmailErrMsg('');
       }


       if (password === '') {
           setPasswordErrMsg('비밀번호를 입력해주세요');
       } else if (password.length < 6) {
           setPasswordErrMsg('비밀번호는 6자리 이상이어야 합니다.');
       } else {
           setPasswordErrMsg('');
       }
   }

   const onEmailChange = (e) => {
       console.log('e.target.value', e.target.value);
       setEmail(e.target.value); //이메일을 입력하면 setEmail을 통해 state 변수에 저장
   }

   const onPasswordChange = (e) => {
       console.log('e.target.value', e.target.value);
       setPassword(e.target.value); //비밀번호를 입력하면 setPassword를 통해 state 변수에 저장
   }

   return (
       <>
           <h1>유효성검사</h1>
           <form>
               <input onChange={onEmailChange} id="email" name="email" type="text" placeholder="아이디를 입력하시오" />
               <p>{emailErrMsg}</p>
               <input onChange={onPasswordChange} id="password" name="password" type="password" placeholder="비밀번호를 입력하시오" />
               <p>{passwordErrMsg}</p>
               <button onClick={onJoin}>회원가입하기</button>
           </form>
       </>
   );
}

export default ValidateTest;


🐾 리액트의 스테이트 변수 관리 원칙

나중에 한번에 바꿔준다

 const [number, setNumber] = useState(0);

 function a(){
    setNum(100);
    setNum(200);
    setNum(300);
 }

<button onClick={onClick}>버튼</button>
 버튼을 클릭하면 
  number라는 state변수의 값을 100으로 대입되야해
    number라는 state변수의 값을 200으로 대입되야해
        number라는 state변수의 값을 300으로 대입되야해
            마지막 300으로 대입된 값만 화면에 보여진다.

1. 최신 state를 조회하는 방법

setState 함수에 인자로 함수를 전달한다

2. state변수를 활용하지 않는 방법

profile
나를위한 노트필기 📒🔎📝

0개의 댓글