쿼리
CREATE TABLE T_MEMBER ( MEMBER_SEQ INT NOT NULL AUTO_INCREMENT COMMENT '회원 번호', ACCOUNT VARCHAR(50) NOT NULL COMMENT '계정', PASSWORD VARCHAR(100) NOT NULL COMMENT '비밀번호', NICKNAME VARCHAR(10) NOT NULL COMMENT '닉네임', JOIN_DATE DATETIME NOT NULL COMMENT '가입일자', PRIMARY KEY (MEMBER_SEQ) ) COMMENT='회원' COLLATE='utf8mb4_general_ci' ;
Member.java
회원 도메인 생성
package com.example.domain;
import lombok.Data;
@Data
public class Member {
private int memberSeq;
private String account;
private String password;
private String nickname;
private String joinDate;
}
MemberJoinForm.java : validation을 따로 구현하기 위해
메세지 프로퍼티 추가
package com.example.controller.form;
import javax.validation.GroupSequence;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
import org.hibernate.validator.constraints.Length;
import com.example.validation.ValidationSteps;
import lombok.Data;
@Data
@GroupSequence({
MemberJoinForm.class,
ValidationSteps.Step1.class,
ValidationSteps.Step2.class,
ValidationSteps.Step3.class,
ValidationSteps.Step4.class,
ValidationSteps.Step5.class,
ValidationSteps.Step6.class,
})
public class MemberJoinForm {
@NotEmpty(groups = ValidationSteps.Step1.class, message = "{MemberSaveForm.account.notEmpty}")
@Email(groups = ValidationSteps.Step2.class, message = "{MemberSaveForm.account.pattern}")
private String account;
@NotEmpty(groups = ValidationSteps.Step3.class, message = "{MemberSaveForm.password.notEmpty}")
@Pattern(regexp = "^(?=.*[A-Za-z])(?=.*\\d)(?=.*[$@$!%*#?&])[A-Za-z\\d$@$!%*#?&]{8,12}$",groups = ValidationSteps.Step4.class, message = "{MemberSaveForm.password.pattern}")
private String password;
@NotEmpty(groups = ValidationSteps.Step5.class, message = "{MemberSaveForm.nickname.notEmpty}")
@Length(groups = ValidationSteps.Step6.class, min = 2, max = 10, message = "{MemberSaveForm.nickname.length}")
private String nickname;
}
config/messages/message.properties
BoardSaveForm.boardType.notEmpty=종류를 선택하세요.
BoardSaveForm.title.notEmpty=제목을 입력해주세요.
BoardSaveForm.contents.notEmpty=내용을 입력해주세요.
BoardSaveForm.userName.notEmpty=회원이름을 입력해주세요.
BoardSaveForm.title.length=제목을 2이상 10이하로 설정해주세요.
MemberJoinForm.account.notEmpty=계정을 입력해주세요
MemberJoinForm.account.email=계정은 이메일 형식으로 임력하세야 합니다.
MemberJoinForm.password.notEmpty=패스워드를 입력해주세요
MemberJoinForm.password.pattern=비밀번호는 영문+숫자+특수문자 혼합 최소8자~12자까지 입력하셔야 합니다.
MemberJoinForm.nickname.notEmpty=닉네임을 입력하세요
MemberJoinForm.nickname.length=닉네임은 최소 2자~최대 10자까지 입력하셔야 합니다.
<?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.example.mapper.MemberMapper">
<!-- 계정이 있는지 없는지 -->
<select id="selectMemberAccount" parameterType="String">
SELECT COUNT(1)
FROM T_MEMBER
WHERE ACCOUNT = #{account}
</select>
<insert id="insertMember"
parameterType="com.example.domain.Member">
INSERT INTO T_MEMBER
(
ACCOUNT,
PASSWORD,
NICKNAME,
JOIN_DATE
)
VALUES
(
#{account},
#{password},
#{nickname},
NOW()
)
</insert>
</mapper>
MemberMapper.java
package com.example.mapper;
import com.example.controller.form.MemberJoinForm;
public interface MemberMapper {
int selectMemberAccount(String account);
void insertMember(MemberJoinForm form);
}
package com.example.service;
import org.springframework.stereotype.Service;
import com.example.controller.form.MemberJoinForm;
import com.example.mapper.MemberMapper;
import lombok.RequiredArgsConstructor;
@Service
@RequiredArgsConstructor
public class MemberService {
private final MemberMapper memberMapper;
public int selectMemberAccount(String account) {
return memberMapper.selectMemberAccount(account);
}
public void insertMember(MemberJoinForm form) {
memberMapper.insertMember(form);
}
}
package com.example.controller;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.Assert;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.example.controller.form.MemberJoinForm;
import com.example.service.MemberService;
import lombok.RequiredArgsConstructor;
@Controller
@RequestMapping("/member")
@RequiredArgsConstructor
public class MemberController {
private final MemberService memberService;
@GetMapping("/form")
public String form() {
return "/member/form";
}
@PostMapping("/join")
@ResponseBody
public HttpEntity<Boolean> join(@Validated MemberJoinForm form) {
// 계정 중복체크 : true면 사용중인 계정
boolean isUseAccount=memberService.selectMemberAccount(form.getAccount()) > 0 ;
Assert.state(!isUseAccount, "이미 사용중인 계정입니다");
memberService.insertMember(form);
return new ResponseEntity<Boolean>(true, HttpStatus.OK);
}
/**
* 가입 완료 화면
* @return
*/
@GetMapping("/join-complete")
public String joinComplete() {
return "/member/join-complete";
}
}
HttpEntity와 ResponseEntity의 차이?
- ResponseEntity : 사용자의 HttpRequest에 대한 응답데이터를 포함하는 클래스, HttpStatus, HttpHeaders, HttpBody를 포함한다. HttpEntity를 상속받으며, RestTemplate 및 @Controller메서드에 사용하고 있다.
- HttpEntity : HTTP요청 또는 응답에 해당하는 HttpHeader와 HttpBody를 포함하는 클래스.
HttpEntity 클래스를 상속받아 구현한 클래스가 RequestEntity, ResponseEntity 클래스이다.
member/form.html 파일 생성
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Bootstrap demo</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-iYQeCzEYFbKjA/T2uDLTpkwGzCiq6soy8tYaI1GyVh/UjpbCx/TYkiZhlZB6+fzT" crossorigin="anonymous">
</head>
<body>
<div class="container">
<form id="member-join-form" method="post">
<input type="hidden" th:name="${_csrf.parameterName}"
th:value="${_csrf.token}" />
<div class="row mb-3">
<label for="account" class="col-sm-2 col-form-label">계정</label>
<div class="col-sm-10">
<input type="text" class="form-control"
name="account" id="account"/>
</div>
</div>
<div class="row mb-3">
<label for="password" class="col-sm-2 col-form-label">비밀번호</label>
<div class="col-sm-10">
<input type="password" class="form-control"
name="password" id="password" />
</div>
</div>
<div class="row mb-3">
<label for="nickname" class="col-sm-2 col-form-label">닉네임</label>
<div class="col-sm-10">
<input type="text" class="form-control"
name="nickname" id="nickname"/>
</div>
</div>
<div class="row mb-3">
<label for="nickname" class="col-sm-2 col-form-label">파일업로드</label>
<div class="col-sm-10">
<input type="file" class="form-control"
name="profileImage" id="profileImage"/>
</div>
</div>
<button type="submit" class="btn btn-primary">저장</button>
</form>
</div>
<!-- 비동기로 넘기기 위한 준비 -->
<script src="https://code.jquery.com/jquery-3.6.1.min.js"></script>
<script>
$(function(){
var $form= $('#member-join-form'); //html의 element 찾음
$form.submit(function() {
try{
/* var data = {
account: $form.find('input[name=account]').val(),
password: $form.find('input[name=password]').val(),
nickname: $form.find('input[name=nickname]').val(),
} */
var formData=new FormData($form[0])
console.log('formData',formData);
//서버 전송하기전에 Object형태를 String으로 바꿔줘야함
/* var jsonValue=JSON.stringify(data);
console.log('jsonValue',jsonValue) */
//form에서 submit이 발생하는 것은 핸들링.
//비동기로 전송하기 위해
$.ajax({
url: '/member/join',
type: 'post',
data: formData,
contentType:false,
processData:false,
success: function() { //서버로 따지면 컨트롤러
location.href='/member/join-complete';
},
error: function(data){
//console.log(data);
console.log(data.responseJSON);
alert(data.responseJSON.message);
}
});
}catch(e){
console.log(e);
}
//페이지가 전환되지 않게 방지
return false;
});
});
</script>
</body>
</html>
비동기 업로드를 하기 위해 ajax와 FormData를 이용한 업로드:
https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=mk1126sj&logNo=221016837263