(FrontEnd) layout/Header.js 로그인, 회원가입 버튼으로 변경
import React from 'react';
import { Link, useNavigate } from 'react-router-dom';
const Header = () => {
const navigate = useNavigate(); // Hook 함수는 직접 사용 불가(X)
function gotoLogin() {
navigate("/login");
}
// return은 화면을 그리겠다.
return (
<div className='container header'>
<header className='d-flex flex-wrap align-items-center justify-content-center justify-content-md-between py-3 mb-4 border-bottom'>
<div id='logo-area' className='col-md-1 mb-2 mb-md-0'>
<a href="/home" className='d-inline-flex link-body-emphasis text-decoration-none'>
<img src={require('../logo.png')} alt="logo" width={40} />
</a>
</div>
<ul className='nav col-12 col-md-6 mb-2 justify-conter'>
<li><Link to="/home" className='nav-link px-2 link-secondary'>홈</Link></li>
<li><Link to="/boardList" className='nav-link px-2 link-secondary'>게시판</Link></li>
<li><Link to="/qnaList" className='nav-link px-2 link-secondary'>질문응답</Link></li>
</ul>
<div className='col-md-3 text-end me-3'>
<button type='button' className='btn btn-outline-primary me-2' onClick={gotoLogin}>로그인</button>
<button type='button' className='btn btn-outline-primary'>회원가입</button>
</div>
</header>
</div>
);
}
export default Header;
- 로그인, 회원가입 버튼 생성
- onClick 함수 생성
Login.js 화면 수정
import React, { useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
// RestAPI -> axios
import axios from 'axios';
function Login() {
// 변수
const [user, setUser] = useState({
username: '',
password: '',
}); // useState() 괄호 안은 초기하는 값. json헝태(딕셔너리)
// 함수
// 값이 바뀔 때마다 user에 값을 넣음
const handleChange = (e) => {
const { name, value } = e.target; //target=> username, password 둘 중 하나
setUser({ ...user, [name]: value});
}
// 핵심
// async function handleSubmit(e){}
const handleSubmit = async (e) => {
e.preventDefault(); // submit동안 다른 이벤트가 발생하지 않도록 중지시키는 것
try{
const formData = new FormData();
formData.append('username', user.username);
formData.append('password', user.password);
console.log(formData.get('username'));
console.log(formData.get('password'));
// axios 백엔드 호출
const resp = await axios({
url: 'http://localhost:8080/api/member/login', // Rest API 호출
method: 'POST', // GET, POST, DELETE, PUT, ...
data: formData,
withCredentials: true,
});
if(resp.status == 200) {
// ...
}
} catch(error) {
console.log('로그인 에러: ' + error);
alert("로그인 실패!");
}
}
return (
<div className="container card form-register"
style={{ maxWidth: '400px', padding: '1rem' }}>
<div>
<div className="my-3 border-bottom">
<h4 className="text-start">로그인</h4>
</div>
<form onSubmit={handleSubmit}>
<div className="text-start mb-3">
<label htmlFor="username" className="form-label">사용자이름</label>
<input type="text" name="username"
placeholder="사용자이름" className="form-control" required
value={user.username} onChange={handleChange} />
</div>
<div className='text-start mb-3'>
<label htmlFor="password" className="form-label">비밀번호</label>
<input type="password" name="password"
placeholder="비밀번호" className="form-control" required
value={user.password} onChange={handleChange} />
</div>
<button type="submit" className='btn btn-primary me-2'>로그인</button>
<Link to={'/home'} className='btn btn-secondary'>취소</Link>
</form>
</div>
</div>
);
}
export default Login;
- react에서는 <label> 태그 속성 for -> htmlFor
(BackEnd) restcontroller/RestMemberController.java 생성
package com.eunji.backboard.restcontroller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.eunji.backboard.dto.Header;
import com.eunji.backboard.entity.Member;
import com.eunji.backboard.service.MemberService;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import java.util.Map;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.PostMapping;
@RequiredArgsConstructor
@RequestMapping("/api/member")
@RestController
@Log4j2
public class RestMemberController {
private final MemberService memberService;
@PostMapping("/login")
public Header<Member> login(@RequestParam Map<String, String> logInfo) {
log.info(String.format("▶▶▶ React에서 넘어온 정보: %s", logInfo.get("username")));
// 계정정보 객체
// Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String username = logInfo.get("username");
String password = logInfo.get("password");
try{
Member member = this.memberService.getMemberByUsernameAndPassword(username, password);
if(member != null) {
Header<Member> result = Header.OK(member);
return result;
} else {
Header<Member> fail = Header.OK("Member Not Found");
return fail;
}
} catch(Exception e) {
log.catching(e);
Header<Member> fail = Header.OK("Member Not Found");
return fail;
}
}
}
(BackEnd) service/MemberService.java getMemberByUsernameandPassword() 메서드 생성
// 24.07.04 React에서 넘어온 정보로 로그인 확인하기
public Member getMemberByUsernameAndPassword(String username, String password) {
Optional<Member> _member = this.memberRepository.findByUsername(username);
Member realMember;
if(_member.isPresent()) {
realMember = _member.get(); // 같은 이름의 사용자 정보가 다 넘어옴(암호화된 비밀번호까지)
// plain text와 암호화된 값이 같은 값을 가지고 있는지 체크
boolean isMatched = passwordEncoder.matches(password, realMember.getPassword());
if(isMatched)
return realMember;
else
throw new NotFoundException("Member not found!");
} else {
throw new NotFoundException("Member not found!");
}
}
- postman으로 테스트
- member 객체 정보 나오는 것 확인할 수 있음
(FrontEnd) Login.js axios 부분 작성
import React, { useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
// RestAPI -> axios
import axios from 'axios';
function Login() {
// 변수
const navigate = useNavigate();
const [user, setUser] = useState({
username: '',
password: '',
}); // useState() 괄호 안은 초기하는 값. json헝태(딕셔너리)
// 함수
// 값이 바뀔 때마다 user에 값을 넣음
const handleChange = (e) => {
const { name, value } = e.target; //target=> username, password 둘 중 하나
setUser({ ...user, [name]: value});
}
// 핵심
// async function handleSubmit(e){}
const handleSubmit = async (e) => {
e.preventDefault(); // submit동안 다른 이벤트가 발생하지 않도록 중지시키는 것
try{
const formData = new FormData();
formData.append('username', user.username);
formData.append('password', user.password);
console.log(formData.get('username'));
console.log(formData.get('password'));
// axios 백엔드 호출
const resp = await axios({
url: 'http://localhost:8080/api/member/login', // Rest API 호출
method: 'POST', // GET, POST, DELETE, PUT, ...
data: formData,
withCredentials: true,
});
console.log(resp);
if(resp.data.resultCode == 'OK') {
const { email, mid, role, username} = resp.data.data;
const transactionTime = resp.data.transactionTime;
console.log(email, mid, role, username);
// localStorage에 저장
localStorage.setItem("username", username);
localStorage.setItem("email", email);
localStorage.setItem("mid", mid);
localStorage.setItem("role", role);
localStorage.setItem("loginDt", transactionTime);
console.log(localStorage);
alert('로그인 성공');
// 다른페이지로 데이터 전달
navigate("/home", {data: {userData: resp.data.data}});
} else {
alert('로그인 실패')
}
} catch(error) {
console.log('로그인 에러: ' + error);
alert("로그인 실패!");
}
}
- localStorage에 데이터 저장
Home.js, localStorage 사용해서 로그인 정보 출력
function Home() {
var username, email, role, loginDt;
if(localStorage != null) {
username = localStorage.getItem("username");
email = localStorage.getItem("email");
role = localStorage.getItem("role");
loginDt = localStorage.getItem("loginDt");
}
return (
<div className='container card' style={{maxWidth: '350px'}}>
<h4>로그인정보</h4>
<div>
<label className='form-label'>
{username}
</label><br />
<label className='form-label'>
{email}
</label><br />
<label className='form-label'>
{role}
</label><br />
<label className='form-label'>
{loginDt}
</label>
</div>
</div>
);
}
export default Home;
- localStorage에 저장된 값 변수에 담아서 출력시켜줌
Header.js에서 로그아웃 기능 추가
// 로그아웃
function logout() {
localStorage.removeItem('username');
localStorage.removeItem('email');
localStorage.removeItem('mid');
localStorage.removeItem('role');
localStorage.removeItem('loginDt');
window.location.reload();
}
<div className='col-md-3 text-end me-3'>
{localStorage.getItem("username") != null ? (
<button type='button' className='btn btn-outline-primary' onClick={logout}>로그아웃</button>
) : (
<>
<button type='button' className='btn btn-outline-primary me-2' onClick={gotoLogin}>로그인</button>
<button type='button' className='btn btn-outline-primary'>회원가입</button>
</>
)}
</div>
- 로그인, 로그아웃 시 버튼 분기태우기
더보기
localStorage 사용방법
// 데이터 저장하기
localStorage.setItem("key", value);
// 데이터 읽기
localStorage.getItem("key");
// 데이터 삭제
localStorage.removeItem("key");
// 모든 데이터 삭제
localStorage.clear();
// 저장된 키/값 쌍의 개수
localStorage.length;
'Spring Boot > STUDY' 카테고리의 다른 글
[Spring Boot] 타임리프(Thymeleaf)란? (0) | 2024.07.17 |
---|---|
[Spring Boot] React연동 프로젝트(4) - 상세 화면 (0) | 2024.07.04 |
[Spring Boot] React연동 프로젝트(2) - 페이징 기능 구현 (0) | 2024.07.03 |
[Spring Boot] React연동 프로젝트(1) - 프로젝트 생성 및 리스트 출력 (1) | 2024.07.02 |
[Spring Boot] JPA 프로젝트 - 비밀번호 찾기 및 변경 기능 구현(2) (0) | 2024.06.28 |