티스토리 뷰

- 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
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2026/04   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30
글 보관함