React - SpringBoot 연결하기 (회원가입+todo리스트 만들기) 1

2024. 7. 15. 18:20React

Login.js

import React, { useContext, useState } from "react";
import LoginContext from "./LoginContext";

const Login = () => {
    //App.js에 작성한 Context에서 loginMember와 LoginContext의 키가 일치하는 값을 가져와 대입
    //만약 로그인한 값이 없으면 로그인 화면으로 이동
    const {loginMember, setLoginMember} = useContext(LoginContext);

    //아이디, 비밀번호 상태 변수
    const [id, setId] = useState('');
    const [pw, setPw] = useState('');

    //로그인 버튼 함수
    const loginBtn = () => {
        fetch("/login", {
            method: "POST",
            headers: {
                "Content-Type" : "application/json", //사용자 -> 서버에 로그인한 정보가 일치하는게 있는지 확인
                "Accept" : "application/json"},  // 서버 -> 사용자에게 사용자가 작성한 정보가 존재하는지에 대한 여부 전달
            body : JSON.stringify({id : id, pw : pw}) //본문으로 id, pw 작성된 내용 전달
        })
        .then(res => res.json())
        .then(map => {
            console.log(map);

            //로그인 실패시
            if(map.loginMember === null){
                alert('아이디 또는 비밀번호가 일치하지 않습니다.');
                return;
            }

            //로그인 성공시
            setLoginMember(map.loginMember);
            //App.js에 로그인 성공한 정보가 올라감. App.js는 로그인 정보를 다른 js에 전달

            //id, pw값 모두 지우기
            setId('');
            setPw('');
        })
    }

    return(
        <div className="login-container">
            <table>
                <tbody>
                    <tr>
                        <th>ID</th>
                        <td>
                            <input type="text" onChange={e => setId(e.target.value)} value={id}/>
                        </td>
                    </tr>
                    <tr>
                        <th>PW</th>
                        <td>
                            <input type="password" onChange={e => setPw(e.target.value)} value={pw}/>
                        </td>
                        <td>
                            <button onClick={loginBtn}>로그인</button>
                        </td>
                    </tr>
                </tbody>
            </table>
        </div>
    )
}
export default Login;

 

LoginContext.js

import React,{createContext} from "react";

const LoginContext = createContext();
export default LoginContext;

 

SignUp.js

import React, {useState} from "react";

const Signup = () => {

    const [id, setId] = useState('');
    const [pw, setPw] = useState('');
    const [pwCheck, setPwCheck] = useState('');
    const [name, setName] = useState('');
    const [result, setResult] = useState('');

    //아이디 중복검사
    const [idValidation, setIdValidation] = useState(false);
    //false = 사용 불가, true = 사용 가능

    //중복검사 이벤트 핸들러
    const idDup = (eventId) => {
        //eventId = 현재 입력하는 이벤트가 발생한 id
        setId(eventId);

        //4글자 미만이면 중복검사 x
        //입력 받은 아이디의 공백을 제거하고 길이가 4보다 작다면
        //trim() : 양 옆의 공백 제거
        if(eventId.trim().length < 4){
            setIdValidation(false);
            return;
        }

        //DB에 중복되는 아이디가 있는지 비동기로 아이디 중복검사 수행
        //axios나 fetch 사용
        fetch("/idCheck?id=" + eventId) //SpringBoot Controller와 연결할 Mapping url
        .then(res => res.text())
        .then(result => {

            //중복이 아닐 때 true 맞다면 false
            //(Number(result) === 0) -> SpringBoot에서 중복이 아니면 0, 중복이면 1 이상의 값 전달
            //꼭 숫자값이 아니라 글자여도 상관없음
            if(Number(result) === 0){
                setIdValidation(true);
            }
            else{
                setIdValidation(false);
            }
        })
    }

    const signUpBtn = () => {
        //아이디 유효하지 않으면 가입 x
        if(!idValidation){
            alert("유효한 아이디가 아닙니다.");
            return;
        }

        if(pw !== pwCheck){
            alert('비밀번호와 비밀번호 확인이 일치하지 않습니다.');
            return;
        }
        //비밀번호, 비밀번호 확인이 일치하는지 확인

        //회원가입 비동기 요청
        const inputValues = {}; //초기에 들어온 값이 없으니 빈 공간으로 설정
        inputValues.id = id; //id 값이 들어오면 inputValues에 id 값을 작성
        inputValues.pw = pw; //pw 값이 들어오면 inputValues에 pw 값을 작성
        inputValues.name =name;
        //input에 id값으로 ksh를 작성하고 pw로 ksh1234를 작성시
        //inputValues = {ksh, ksh1234}; 가 됨

        fetch("/signup", {
            method:"POST", //스프링부트 컨트롤러에 @PostMapping("/signup")에 전달

            //headers : 메일로 치면 메일 주소,제목 처럼 초기에 어떤 것을 보내는지 설정
            headers: {"Content-Type" : "application/json"}, //<form> 태그에 해당 내용들이 기본값으로 들어가 있음
            
            //body : 내용 본문 작성
            body : JSON.stringify(inputValues) //사용자가 작성한 모든 값들을 보내기
        })
        .then(res => res.text())
        .then(result => {
            if(Number(result) > 0){ //결과가 1 이상이면 회원가입 완료처리
                setResult('회원 가입 성공');

                //input 값들 모두 초기화
                setId('');
                setPw('');
                setPwCheck('');
                setName('');

            }
            else{
                setResult('회원 가입 실패');
            }
        })
    }

    return(
        <div className="signup-container">
            {/* label의 htmlFor와 input의 id를 생략하고 싶다면 input을 라벨 안에 작성해주면 됨 */}
            <label>
                ID : 
                <input type="text" onChange={e => idDup(e.target.value)}
                value={id}
                className={idValidation ? '' : 'id-err'}
                />
            </label>
            
            <label>
                PW : 
                <input type="password" onChange={e => setPw(e.target.value)}
                value={pw}
                />
            </label>
            <label>
                PW CHECK : 
                <input type="password" onChange={e => setPwCheck(e.target.value)}
                value={pwCheck}
                />
            </label>
            <label>
                NAME : 
                <input type="text" onChange={e => setName(e.target.value)}
                value={name}
                />
            </label>
            
            <button onClick={signUpBtn}>가입하기</button>
            <hr/>

            {/* 회원가입 결과 출력 */}
            <h3>{result}</h3>
        </div>
    )
}
export default Signup;

** headers: {"Content-Type" : "application/json"}  =>  <form> 태그에 해당 내용들이 기본값으로 들어가 있음
- Content-Type : 데이터 전달 시 이미지인지, 파일인지 등 정보를 전달
- application/json
     - application : 코딩하는 폴더 자체. 폴더 1개 = 어플리캐이션 1개
     - json : 사용자나 개발자가 작성한 값들을 key-value의 형태로 1개 이상 주고 받을 수 있는 형태

 

 

 

App.js

import React, {useState} from 'react';
import LoginContext from './components/LoginContext';
import Signup from './components/Signup';
import Login from './components/Login';
import './App.css';

// App.js는 제일 상위 컴포넌트(객체)
function App() {

  //회원 가입 창 보이기 / 숨기기
  const [signUpView, setSignUpView] = useState(false);

  //로그인한 회원 정보 저장
  const [loginMember, setLoginMember] = useState(null);
  return (
    <LoginContext.Provider value={{loginMember, setLoginMember}}>
      <button onClick={() => {setSignUpView(!signUpView)}}>
        {signUpView ? '회원 가입 닫기' : '회원 가입 열기'}
      </button>
      {/* 회원 가입 화면 */}
      <div className='"signup-wrapper'>
        {/* signUpView가 true 일 경우에만 실행되는 공간 = 눈에 보여짐 */}

        {/* 조건식 && (true일 경우 실행) */}
        {signUpView === true && (<Signup/>)}
      </div>

      <h1>Todo List</h1>
      {/* 로그인을 해야 TodoList 보이도록 */}
      <Login/>
    
    </LoginContext.Provider>
  );
}

export default App;

** 로그인을 한 다음에 로그인한 정보를 모든 창에 띄워주기
=> App.js에서 div태그 대신에 LoginContext 태그로 return을 감싸주면 LoginContext 태그 안의 모든 태그에서는 로그인/로그아웃한 정보가 공유됨

 

** <LoginContext.Provider value={{loginMember, setLoginMember}}>

- value = {} -> 하나의 값만 작성
- value = {{}} -> 2개 이상의 값을 작성
- loginMember = 처음 로그인 안된 초기값을 가지고 있음
- setLoginMember = 로그인 후 로그인한 정보를 가지고 있음

회원 가입 닫음
회원 가입 열음