파라미터로 함수를 전달하는 함수
import React, { useState } from 'react';
const Callback = () => {
const [ cnt, setCnt] = useState(0);
let count1 = 0;
const a =(fx, fx2) => {
console.log("A함수 실행");
setCnt(() => fx(fx2));
}
const b = (fx2) => {
console.log("B함수 실행");
count1 = cnt + 100;
fx2();
return count1;
}
const c = () => {
console.log("C함수 호출");
console.log(count1);
}
const clickHandler = () => {
a(b, c);
}
return (
<div>
<button onClick={clickHandler}>버튼</button>
</div>
);
};
export default Callback;
Callback
컴포넌트를 불러올 때,useState
를 사용하여cnt
라는 상태 변수를 초기화하고, 상태를 변경하기 위한setCnt
함수를 생성,count1
이라는 변수도 초기화- 사용자가 UI에 표시된 버튼을 클릭하면,
clickHandler
함수가 호출clickHandler
함수 내에서a
함수가 호출되며,b
함수와C
함수를 인자로 전달a
함수 내에서 consoleA함수 실행
출력 ->setCnt
를 호출하여 상태 변경, 이때,b
함수가 인자로c
함수를 전달받아 호출b
함수 내에서 consoleB함수 실행
출력 ->count1
변수에cnt
상태 값에 100을 더한 값을 할당,c
함수 호출c
함수 내에서 consoleC함수 호출
과 함께count1
값 출력b
함수는count1
값을 반환하고,setCnt
함수는 이 값을 사용해 상태를 변경, 상태가 변경되면 컴포넌트가 다시 렌더링 됨
결과
버튼
비동기 작업의 결과를 나타내는 객체로, 작업이 성공적으로 완료되거나 실패한 후에 실행될 콜백 함수를 관리
import React from 'react';
const PromiseStudy = () => {
const a = new Promise((resolve, reject) =>{
console.log("프로미스 호출");
resolve("A함수 resolve 결과 리턴")
});
const clickHandler = () => {
a.then(b);
}
const b = (str) => {
console.log(str);
}
return (
<div>
<button onClick={clickHandler}>버튼</button>
</div>
);
};
export default PromiseStudy;
import React from 'react';
const PromiseStudy = () => {
const a = new Promise((resolve, reject) =>{
console.log("프로미스 호출");
if(1 !== 1){
resolve();
}else {
reject(new Error("오류입니다."));
}
});
const clickHandler = () => {
a.then(() => {
console.log("1번 then 호출");
return new Promise((resolve, reject) =>{
resolve("리턴!!!");
});
})
.catch((error) =>{
console.log(error);
})
.then(b);
}
const b = (str) => {
console.log(str);
}
return (
<div>
<button onClick={clickHandler}>버튼</button>
</div>
);
};
export default PromiseStudy;
Register.js (수정)
/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react'
import React, { useState } from 'react';
import LoginInput from '../../components/UI/Login/LoginInput/LoginInput';
import { FiUser, FiLock } from 'react-icons/fi';
import { Link } from 'react-router-dom';
import {BiRename} from 'react-icons/bi';
import axios from 'axios';
;
const container = css`
display: flex;
flex-direction: column;
align-items: center;
padding: 70px 30px;
`;
const logo = css`
margin: 50px 0px;
font-size: 34px;
font-weight: 600;
`;
const mainContainer = css`
display: flex;
flex-direction: column;
align-items: center;
border: 1px solid #dbdbdb;
border-radius: 10px;
padding: 40px 20px;
width: 400px;
`;
const authForm = css`
width: 100%;
`;
const inputLabel = css`
margin-left: 5px;
font-size: 12px;
font-weight: 600;
`;
const loginButton = css`
margin: 10px 0px ;
border: 1px solid #dbdbdb;
border-radius: 7px;
width: 100%;
height: 50px;
background-color: white;
font-weight: 900;
cursor: pointer;
&:hover {
background-color: #fafafa;
}
&:active {
background-color: #eee;
}
`;
const signupMessage = css`
margin-top: 20px;
font-size: 14px;
font-weight: 600;
color: #777;
`;
const register = css`
margin-top: 10px;
font-weight: 600;
`;
const errorMsg = css`
margin-left: 5px;
margin-bottom: 20px;
font-size: 12px;
color:red;
`;
const Register = () => {
const [registerUser, setRegisterUser] = useState( { email:"",password:"",name:"" } );
const [errorMessages, setErrorMessages] = useState( { email: "", password: "" , name: "" } );
const onChangeHandle = (e) => {
const { name, value } = e.target;
setRegisterUser( { ...registerUser, [name]:value } );
}
const registeSubmit = async() => {
const data = {
...registerUser
}
const option = {
headers: {
"Content-Type" : "application/json"
}
}
try{
const response = await axios.post("http://localhost:8080/auth/signup", JSON.stringify(data), option);
setErrorMessages({email: "", password: "" , name: ""}); //빈값 ( 로그인 성공 시, error 메시지 뜨지않음 )
}catch(error){
setErrorMessages({email: "", password: "" , name: "",...error.response.data.errorData}); //객체 (error.response.data.errorData)
}
// .then( response => {
// setErrorMessages({email: "", password: "" , name: ""}); //빈값 ( 로그인 성공 시, error 메시지 뜨지않음 )
// console.log(response);
// })
// .catch(error => {
// setErrorMessages({email: "", password: "" , name: "",...error.response.data.errorData}); //객체 (error.response.data.errorData)
// });
}
return (
<div css= {container}>
<header>
<h1 css= { logo } >SIGN UP</h1>
</header>
<main css={ mainContainer }>
<div css={authForm}>
<label css={ inputLabel }>Email</label>
<LoginInput type="email" placeholder="Type your email" onChange={onChangeHandle} name="email">
<FiUser />
</LoginInput>
<div css={errorMsg}>{errorMessages.email}</div>
<label css={ inputLabel }>Password</label>
<LoginInput type="password" placeholder="Type your password" onChange={onChangeHandle} name="password">
<FiLock />
</LoginInput>
<div css={errorMsg}>{errorMessages.password}</div>
<label css={ inputLabel }>Name</label>
<LoginInput type="text" placeholder="Type your name" onChange={onChangeHandle} name ="name">
<BiRename />
</LoginInput>
<div css={errorMsg}>{errorMessages.name}</div>
<button css={ loginButton } onClick={registeSubmit}>REGISTER</button>
</div>
<div></div>
</main>
<div css= { signupMessage }>Already a user?</div>
<footer>
<div css = { register }><Link to="/login">LOGIN</Link></div>
</footer>
</div>
);
};
export default Register;
등록을 정상 입력 했을 때
Email, Password, Name 중 잘못 입력 했을 때
JavaScript
의 문법async
는 함수를 비동기로 선언하며, await
는 Promise를 기다리는 동안 코드 실행을 일시 중단함const registeSubmit = async() => {
const data = {
...registerUser
}
const option = {
headers: {
"Content-Type" : "application/json"
}
}
try{
const response = await axios.post("http://localhost:8080/auth/signup", JSON.stringify(data), option);
setErrorMessages({email: "", password: "" , name: ""}); //빈값 ( 로그인 성공 시, error 메시지 뜨지않음 )
}catch(error){
setErrorMessages({email: "", password: "" , name: "",...error.response.data.errorData}); //객체 (error.response.data.errorData)
}
// .then( response => {
// setErrorMessages({email: "", password: "" , name: ""}); //빈값 ( 로그인 성공 시, error 메시지 뜨지않음 )
// console.log(response);
// })
// .catch(error => {
// setErrorMessages({email: "", password: "" , name: "",...error.response.data.errorData}); //객체 (error.response.data.errorData)
// });
}
SpringBoot
service > AuthenicationService
package com.toyproject.bookmanagement.service;
import org.springframework.stereotype.Service;
import com.toyproject.bookmanagement.entity.User;
import com.toyproject.bookmanagement.exception.CustomException;
import com.toyproject.bookmanagement.exception.ErrorMap;
import com.toyproject.bookmanagement.repository.UserRepository;
import lombok.RequiredArgsConstructor;
@Service
@RequiredArgsConstructor
public class AuthenticationService {
private final UserRepository userRepository;
public void checkDuplicatedEmail(String email) {
User userEntity = userRepository.findUserByEmail(email);
if(userEntity != null) {
throw new CustomException("Duplicated Email",
ErrorMap.builder().put("email", "사용중인 이메일입니다.").build());
}
}
}
repository > UserRepository
package com.toyproject.bookmanagement.repository;
import org.apache.ibatis.annotations.Mapper;
import com.toyproject.bookmanagement.entity.User;
@Mapper
public interface UserRepository {
public User findUserByEmail(String email);
}
entitiy > Authority, Role, User
Authority
package com.toyproject.bookmanagement.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@AllArgsConstructor
@Builder
@NoArgsConstructor
@Data
public class Authority {
private int authorityId;
private int userId;
private int roleId;
private Role role;
}
Role
package com.toyproject.bookmanagement.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Role {
private int roleId;
private String roleName;
}
User
package com.toyproject.bookmanagement.entity;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class User {
private int userId;
private String email;
private String password;
private String name;
private List<Authority> authorities;
}
resources > mappers
mappers > 오른쪽 마우스 클릭 > New > Other >
UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.toyproject.bookmanagement.repository.UserRepository">
<resultMap type="com.toyproject.bookmanagement.entity.User" id="userMap">
<id property="userId" column="user_id"/>
<result property="email" column="email"/>
<result property="password" column="password"/>
<result property="name" column="name"/>
<result property="provider" column="provider"/>
<collection property="authorities" javaType="list" resultMap="authorityMap" />
</resultMap>
<resultMap type="com.toyproject.bookmanagement.entity.Authority" id="authorityMap">
<id property="authorityId" column="authority_id"/>
<result property="userId" column="user_id"/>
<result property="roleId" column="role_id"/>
<association property="role" resultMap="RoleMap"></association>
</resultMap>
<resultMap type="com.toyproject.bookmanagement.entity.Role" id="RoleMap">
<id property="roleId" column="role_id"/>
<result property="roleName" column="role_name"/>
</resultMap>
<select id="findUserByEmail" resultMap="userMap">
select
ut.user_id,
ut.email,
ut.password,
ut.name,
ut.provider,
at.authority_id,
at.user_id,
at.role_id,
rt.role_id,
rt.role_name
from
user_tb ut
left outer join authority_tb at on(at.user_id = ut.user_id)
left outer join role_tb rt on(rt.role_id = at.role_id)
where
ut.email = #{email}
</select>
</mapper>
- repository > UserRepositoty 경로 복사
exception > ErrorMap(클래스 생성)
package com.toyproject.bookmanagement.exception;
import java.util.HashMap;
import java.util.Map;
public class ErrorMap {
private Map<String, String> errorMap;
private ErrorMap() {
errorMap = new HashMap<>();
}
public static ErrorMap builder() {
return new ErrorMap();
}
public ErrorMap put(String key, String value) {
errorMap.put(key, value);
return this;
}
public Map<String, String> build(){
return errorMap;
}
}
controller > AuthenticationController (수정)
package com.toyproject.bookmanagement.controller;
import javax.validation.Valid;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.toyproject.bookmanagement.aop.annotation.ValidAspect;
import com.toyproject.bookmanagement.dto.auth.SignupReqDto;
import com.toyproject.bookmanagement.service.AuthenticationService;
import lombok.RequiredArgsConstructor;
@RestController
@RequestMapping("/auth")
@RequiredArgsConstructor
public class AuthenticationController {
private final AuthenticationService authenticationService;
@PostMapping("/login")
public ResponseEntity<?> login() {
return ResponseEntity.ok(null);
}
@CrossOrigin
@ValidAspect
@PostMapping("/signup")
public ResponseEntity<?> signup(@Valid @RequestBody SignupReqDto signupReqDto, BindingResult bindingResult) {
authenticationService.checkDuplicatedEmail(signupReqDto.getEmail());
return ResponseEntity.ok(null);
}
}
MySQL에서 Select문 확인해보기
결과
- 회원가입 페이지
- 결과
entity > User
User
package com.toyproject.bookmanagement.entity;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class User {
private int userId;
private String email;
private String password;
private String name;
private String provider;
private List<Authority> authorities;
}
repository > UserRepository
UserRepository (interface)
package com.toyproject.bookmanagement.repository;
import org.apache.ibatis.annotations.Mapper;
import com.toyproject.bookmanagement.entity.User;
@Mapper
public interface UserRepository {
// 이메일 중복확인
public User findUserByEmail(String email);
// 유저 등록
public int saveUser (User user);
}
@Mapper
는 MyBatis에서 사용되는 어노테이션으로, 해당 인터페이스를 데이터베이스 매퍼로 표시하며 SQL 쿼리와 자바 메서드를 매핑하기 위해 사용
dto > auth > SignupReqDto
package com.toyproject.bookmanagement.dto.auth;
import javax.validation.constraints.Email;
import javax.validation.constraints.Pattern;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import com.toyproject.bookmanagement.entity.User;
import lombok.Data;
@Data
public class SignupReqDto {
@Email
private String email;
@Pattern(regexp = "^(?=.*[A-Za-z])(?=.*\\d)(?=.*[@$!%*#?&])[A-Za-z\\d@$!%*#?&]{8,16}$",
message = "비밀번호는 영문자, 숫자, 특수문자를 포함하여 8 ~ 16 글자로 작성하세요.")
private String password;
@Pattern(regexp = "^[가-힣]{2,7}$",
message = "한글이름만 작성 가능합니다.")
private String name;
public User toEntity() {
return User.builder()
.email(email)
.password(new BCryptPasswordEncoder().encode(password))
.name(name)
.build();
}
}
service
AuthenticationService
package com.toyproject.bookmanagement.service;
import org.springframework.stereotype.Service;
import com.toyproject.bookmanagement.dto.auth.SignupReqDto;
import com.toyproject.bookmanagement.entity.User;
import com.toyproject.bookmanagement.exception.CustomException;
import com.toyproject.bookmanagement.exception.ErrorMap;
import com.toyproject.bookmanagement.repository.UserRepository;
import lombok.RequiredArgsConstructor;
@Service
@RequiredArgsConstructor
public class AuthenticationService {
private final UserRepository userRepository;
public void checkDuplicatedEmail(String email) {
User userEntity = userRepository.findUserByEmail(email);
if(userEntity != null) {
throw new CustomException("Duplicated Email",
ErrorMap.builder().put("email", "사용중인 이메일입니다.").build());
}
}
public void signup(SignupReqDto signupReqDto) {
User userEntity = signupReqDto.toEntity();
userRepository.saveUser(userEntity);
}
}
resources > mappers
UserMapper.xml (추가)
<insert id="saveUser" parameterType="com.toyproject.bookmanagement.entity.User">
insert into user_tb
values (0, #{email},#{password},#{name},#{provider})
</insert>
MyBats
를 사용하여 데이터베이스에 새 사용자를 추가하는sql 쿼리
를 정의하는 코드parameterTyp
은 쿼리에 전달되는 파라미터의 타입을 지정insert into user_tb
는user_tb
테이블에 새 레코드를 추가하는 sql 쿼리values (0, #{email},#{password},#{name},#{provider})
는 추가할 레코드의 값을 지정,0
은 자동 증가되는 primary key를 의미하며#{email},#{password},#{name},#{provider}
는 각각 전달된 User의 객체의 email, password, name, provider속성
을 나타냄- 즉,
entity,User
의 객체를 전달받아user_tb
테이블에 새 사용자를 레코드를 추가하는 sql 쿼리를 정의
controller
AuthenticationController (추가)
package com.toyproject.bookmanagement.controller;
import javax.validation.Valid;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.toyproject.bookmanagement.aop.annotation.ValidAspect;
import com.toyproject.bookmanagement.dto.auth.SignupReqDto;
import com.toyproject.bookmanagement.service.AuthenticationService;
import lombok.RequiredArgsConstructor;
@RestController
@RequestMapping("/auth")
@RequiredArgsConstructor
public class AuthenticationController {
private final AuthenticationService authenticationService;
@PostMapping("/login")
public ResponseEntity<?> login() {
return ResponseEntity.ok(null);
}
@CrossOrigin
@ValidAspect
@PostMapping("/signup")
public ResponseEntity<?> signup(@Valid @RequestBody SignupReqDto signupReqDto, BindingResult bindingResult) {
authenticationService.checkDuplicatedEmail(signupReqDto.getEmail());
authenticationService.signup(signupReqDto);
return ResponseEntity.ok(null);
}
}