티스토리 뷰
- MemberController
@Controller//사용자의 요청을 처리하고 난 후 정해지 뷰에 객체를 넘겨주는 역할
@RequestMapping(value = "/member")//요청 URL을 어떤 메서드가 처리할지.. /member경로가 메서드를 처리
public class MemberController {
private final MemberService memberService;
@Autowired //기본값 true
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
@RequestMapping(value = "register", method = RequestMethod.GET, // /register경로가 메서드를 처리 GET방식으로
produces = MediaType.TEXT_HTML_VALUE)
public ModelAndView getRegister() {
ModelAndView modelAndView = new ModelAndView("member/register");
return modelAndView;
}
- 개인정보 DB
CREATE SCHEMA `study_member`;
CREATE TABLE `study_member`.`users`
(
`email` VARCHAR(50) NOT NULL,
`password` VARCHAR(128) NOT NULL,
`nickname` VARCHAR(10) NOT NULL,
`name` VARCHAR(5) NOT NULL,
`contact` VARCHAR(12) NOT NULL,
`address_postal` VARCHAR(6) NOT NULL,
`address_primary` VARCHAR(100) NOT NULL,
`address_secondary` VARCHAR(100) NOT NULL,
`registered_on` DATETIME NOT NULL DEFAULT NOW(),
CONSTRAINT PRIMARY KEY (`email`),
CONSTRAINT UNIQUE (`nickname`),
CONSTRAINT UNIQUE (`contact`)
);
DB 생성은 이메일, 패스워드, 닉네임, 이름, 연락처, 우편번호, 주소, 상세주소로 나누어진다. 기본키를 email로 설정한 것은 중복이 있을 수 있기 때문이다. nickname과 contact는 중복이 되어서는 안 되기 때문에 UNIQUE키를 사용하였다.
- UserEntity
package dev.shlee.studymemberbbs.entities.member;
import java.util.Date;
import java.util.Objects;
public class UserEntity {
private String email;
private String password;
private String nickname;
private String name;
private String contact;
private String address_postal;
private String address_primary;
private String address_secondary;
private String registered_on;
private Date registeredOn;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getContact() {
return contact;
}
public void setContact(String contact) {
this.contact = contact;
}
public String getAddress_postal() {
return address_postal;
}
public void setAddress_postal(String address_postal) {
this.address_postal = address_postal;
}
public String getAddress_primary() {
return address_primary;
}
public void setAddress_primary(String address_primary) {
this.address_primary = address_primary;
}
public String getAddress_secondary() {
return address_secondary;
}
public void setAddress_secondary(String address_secondary) {
this.address_secondary = address_secondary;
}
public String getRegistered_on() {
return registered_on;
}
public void setRegistered_on(String registered_on) {
this.registered_on = registered_on;
}
public Date getRegisteredOn() {
return registeredOn;
}
public void setRegisteredOn(Date registeredOn) {
this.registeredOn = registeredOn;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UserEntity that = (UserEntity) o;
return Objects.equals(email, that.email);
@Override
public int hashCode() {
return Objects.hash(email);
}
}
Entity
- Entity DB테이블로 1:1 매핑된다. 테이블이 가지지 않는 컬럼을 필드로 가져서는 안 되며 Entity클래스는 다른 클래스를 상속받거나, 인터페이스 구현체여서는 안된다.
- MemberMapper.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="dev.shlee.studymemberbbs.mappers.IMemberMapper">
<select id="selectUserByEmail"
resultType="dev.shlee.studymemberbbs.entities.member.UserEntity">
SELECT `email` AS `email`,
`password` AS `password`,
`nickname` AS `nickname`,
`name` AS `name`,
`contact` AS `contact`,
`address_postal` AS `addressPostal`,
`address_primary` AS `addressPrimary`,
`address_secondary` AS `addressSecondary`,
`registered_on` AS `registeredOn`
FROM `study_member`.users
WHERE BINARY `email` = #{email} String 형태로 값을 넣어주고 따옴표가 자동 삽입된다.
LIMIT 1
</select>
- IMemberMapper.java
package dev.shlee.studymemberbbs.mappers;
import dev.shlee.studymemberbbs.entities.member.EmailAuthEntity;
import dev.shlee.studymemberbbs.entities.member.UserEntity;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper // 이것은 매퍼입니다라는 표시라고 생각
public interface IMemberMapper {
UserEntity selectUserByEmail(@Param(value = "email") String email);
//@Param은 변수에 대한 설명을 표시. UserEntity안에 select된 값이 email인것을 문자열로 받아서 가져오겠다.
}
- MemberService
@Service(value = "dev.shlee.studymemberbbs.services.MemberService")
public class MemberService {
private final IMemberMapper memberMapper;//변수로 만들때 I는 빼고 만드는게 국룰
@Autowired
public MemberService(IMemberMapper memberMapper) {
this.memberMapper = memberMapper; // 인터페이스를 가져온다.
}
- SendEmailAuthResult
package dev.shlee.studymemberbbs.enums.member;
import dev.shlee.studymemberbbs.interfaces.IResult;
public enum SendEmailAuthResult implements IResult { // member 메서드에 있는 SnedEmailAuth의 결과
EMAIL_DUPLICATED // 이미 사용중인 이메일 입니다.
}
- CommonResult
package dev.shlee.studymemberbbs.enums;
import dev.shlee.studymemberbbs.interfaces.IResult;
public enum CommonResult implements IResult {
FAILURE, // 실패?
SUCCESS // 인증 번호를 전송하였습니다. 5분 이내에 입력해 주세요.
//name메서드를 구현하고 있어서 안 터짐.
}
- IResult
package dev.shlee.studymemberbbs.interfaces;
public interface IResult {
}
- 위에 CommonResult와 SendEmailAuthResult만의 값을 가져오기 위해서 IResult를 생성해주고 implements를 사용하여 인터페이스 자체를 상속받도록 해준다.
- 이메일 인증 DB 생성
CREATE TABLE `study_member`.`email_auths`
(
`index` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`email` VARCHAR(50) NOT NULL,
`code` VARCHAR(6) NOT NULL,
`salt` VARCHAR(128) NOT NULL,
`created_on` DATETIME NOT NULL DEFAULT NOW(),
`expires_on` DATETIME NOT NULL,
`expired_flag` BOOLEAN NOT NULL DEFAULT FALSE,
CONSTRAINT PRIMARY KEY (`index`)
);
- 이메일과 인증번호코드 암호화하기 위한 salt 보낸 시간, 받은 시간, 성공 or 실패 확인을 위한 expired_flag를 생성해준다.
- EmailAuthEntity
package dev.shlee.studymemberbbs.entities.member;
import java.util.Date;
import java.util.Objects;
public class EmailAuthEntity {
private int index;
private String email;
private String code;
private String salt;
private Date createdOn;
private Date expiresOn;
private Boolean expiredFlag;
public EmailAuthEntity() {
}
public EmailAuthEntity(int index, String email, String code, String salt, Date createdOn, Date expiresOn, Boolean expiredFlag) {
this.index = index;
this.email = email;
this.code = code;
this.salt = salt;
this.createdOn = createdOn;
this.expiresOn = expiresOn;
this.expiredFlag = expiredFlag;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = salt;
}
public Date getCreatedOn() {
return createdOn;
}
public void setCreatedOn(Date createdOn) {
this.createdOn = createdOn;
}
public Date getExpiresOn() {
return expiresOn;
}
public void setExpiresOn(Date expiresOn) {
this.expiresOn = expiresOn;
}
public Boolean getExpiredFlag() {
return expiredFlag;
}
public void setExpiredFlag(Boolean expiredFlag) {
this.expiredFlag = expiredFlag;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
EmailAuthEntity that = (EmailAuthEntity) o;
return index == that.index;
}
@Override
public int hashCode() {
return Objects.hash(index);
}
}
- MemberMapper.xml
<insert id="insertEmailAuth"
useGeneratedKeys="true"
keyColumn="index"
keyProperty="index"
parameterType="dev.shlee.studymemberbbs.entities.member.EmailAuthEntity">
INSERT INTO `study_member`.`email_auths` (`email`, `code`, `salt`, `created_on`, `expires_on`, `expired_flag`)
VALUES (#{email}, #{code}, #{salt}, #{createdOn}, #{expiresOn}, #{expiredFlag})
</insert>
- insert구문을 만들어 준다.
- MemberService
@Transactional
public Enum<? extends IResult> sendEmailAuth(UserEntity user, EmailAuthEntity emailAuth) throws NoSuchAlgorithmException { //UserEntity에게 이메일 검증을 요청하겠다. user은 controller에서 받아온다.
UserEntity existingUser = this.memberMapper.selectUserByEmail(user.getEmail());
if (existingUser != null) {
return SendEmailAuthResult.EMAIL_DUPLICATED; // null이 아니면 값이 있다는 것
}
String authCode = RandomStringUtils.randomNumeric(6); //6자의 랜덤한 문자열 가져오기
String authSalt = String.format("%s%s%f%f",
user.getEmail(),
authCode,
Math.random(),
Math.random());
StringBuilder authSaltHashBuilder = new StringBuilder();
MessageDigest md = MessageDigest.getInstance("SHA-512");
md.update(authSalt.getBytes(StandardCharsets.UTF_8));
for (byte hashByte : md.digest()) {
authSaltHashBuilder.append(String.format("%02x", hashByte));
}
authSalt = authSaltHashBuilder.toString();
Date createOn = new Date();
Date expiresOn = DateUtils.addMinutes(createOn, 5);
emailAuth.setCode(authCode);
emailAuth.setSalt(authSalt);
emailAuth.setCreatedOn(createOn);
emailAuth.setExpiresOn(expiresOn);
emailAuth.setExpiredFlag(false);
if (this.memberMapper.insertEmailAuth(emailAuth) == 0) {
return CommonResult.FAILURE;
}
return CommonResult.SUCCESS;
}
}
- UserEntity으로 이메일 검증을 요청한다. UserEntity타입인 existingUser 변수에 memberMapper에서 select 한 user email을 변수에 할당한다.
만약 existingUser이 null이 아니라면 이미 값이 있다는 것이기 때문에 "이미 사용중인 이메일입니다"를 리턴해준다.
그 후 실패와 성공 여부를 반환.
- MemberController
@RequestMapping(value = "/email",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE) //응답결과를 json으로 해석 조언
@ResponseBody
public String postEmail(UserEntity user, EmailAuthEntity emailAuth) throws NoSuchAlgorithmException { // 엔티티에서 가져온다.
Enum<?> result = this.memberService.sendEmailAuth(user, emailAuth);
return result.name().toLowerCase();
}
}
- @ResponseBody는 문자열로 받기 위해 사용. 매개변수로 UserEntity user와 EmailAuthEntity emailAuth를 사용
Enum <?> result에 값들을 넣어주고 리턴할 때에는 name()을 사용하여 String으로 받아준다.
후기: 처음에는 너무 어렵고 이해도 안 되고 헷갈리는 부분이 많았지만 주위 사람들에게 물어보면서 하나하나씩 과정들을 나열해보고 코드를 직접 해석해보니 많은 도움이 되었다. 뭔가 이제 공부하는 방법을 깨달은 거 같기도..?
앞으로도 차근차근 코드를 해석하면서 직접 블로그에 순서대로 나열하는 연습을 많이 해야될거 같다!
feat. 밥상우, 승띵 옆에서 도와줘서 진짜 고맙다ㅠㅎ

'웹 개발 > SpringBoot' 카테고리의 다른 글
| [Spring Boot] 프로젝트 구조 (0) | 2022.11.03 |
|---|---|
| [Spring Boot] MVC 패턴 (2) | 2022.11.03 |
| [String Boot] email 인증 (3) | 2022.11.02 |
| [Spring Boot] Test 인증번호 6자리 (3) | 2022.11.02 |
| [Spring Boot] interface, XML, Service (2) | 2022.11.02 |