스프링 부트 회원가입 - seupeuling buteu hoewongaib

점프 투 스프링부트 0장 들어가기 전에 0-01 머리말 0-02 저자소개 0-03 주요변경이력 0-04 이 책을 읽기 전에 1장 스프링부트 개발 준비! 1-01 필자가 생각하는 스프링부트란? 1-02 자바 설치하기 1-03 스프링부트 개발 환경 준비하기 1-04 스프링부트 맛보기 1-05 스프링부트 도구 설치하기 2장 스프링부트의 기본 요소 익히기! 2-01 스프링부트 프로젝트의 구조 2-02 컨트롤러 2-03 JPA 2-04 엔티티 2-05 리포지터리 2-06 도메인 별로 분류하기 2-07 질문 목록과 템플릿 2-08 ROOT URL 2-09 서비스 2-10 질문 상세 2-11 답변 등록 2-12 스태틱 디렉터리와 스타일시트 2-13 부트스트랩 2-14 템플릿 상속 2-15 질문 등록과 폼 2-16 공통 템플릿 3장 SBB 서비스 개발! 3-01 내비게이션바 3-02 페이징 3-03 게시물에 일련번호 추가하기 3-04 답변 개수 표시 3-05 스프링 시큐리티 3-06 회원가입 3-07 로그인과 로그아웃 3-08 엔티티 변경 3-09 글쓴이 표시 3-10 수정과 삭제 3-11 추천 3-12 앵커 3-13 마크다운 3-14 검색 3-15 SBB 추가 기능 4장 세상에 선보이는 SBB 서비스! 4-01 서버 4-02 AWS 라이트세일 4-03 서버 접속 설정 4-04 서버 접속 프로그램 4-05 SBB 오픈 4-06 서버 스크립트 4-07 개발과 서버 환경 분리 4-08 Nginx 4-09 로깅 4-10 도메인 4-11 SSL 4-12 PostgreSQL A. 부록 A-01 인텔리제이 사용하기 A-02 AWS 라이트세일 사용 취소 A-03 댓글 (삭제예정) B. 마치며

Backend 처리

DB준비


테이블 생성

CREATE TABLE MEMBERS(
   ID VARCHAR(50) PRIMARY KEY ,
   PWD VARCHAR(50),
   NAME  VARCHAR(50),
   EMAIL VARCHAR(50),
   AUTH INTEGER
);

테이블을 다음과 같이 생성한다.

테스트용 계정 추가

INSERT INTO MEMBERS
VALUES ('admin', 'admin', 'admin', '', 1);

로그인 테스트를 위해 테스트 계정을 생성해준다.

MemberDto 작성


DB의 형식과 동일하게 DTO를 작성해준다.

// MemberDto

import lombok.Getter;

@Getter
public class MemberDto {

    private String id;
    private String pwd;
    private String name;
    private String email;
    private int auth;

    public MemberDto() {
    }

    public MemberDto(String id, String pwd, String name, String email, int auth) {
        this.id = id;
        this.pwd = pwd;
        this.name = name;
        this.email = email;
        this.auth = auth;
    }

    public void setId(String id) {
        this.id = id;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public void setAuth(int auth) {
        this.auth = auth;
    }

    @Override
    public String toString() {
        return "id='" + id + '\'' +
                ", pwd='" + pwd + '\'' +
                ", name='" + name + '\'' +
                ", email='" + email + '\'' +
                ", auth=" + auth;
    }
}

mapper.xml 및 DAO 준비


mapper.xml

MyBatis를 사용하기 위해 다음과 같이 SQL 쿼리문을 작성해주었다.

<?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.hwangduil.springbootbackend.dao.MemberDao">

      <!-- 아아디 중복 확인 -->
    <select id="getId" parameterType="com.hwangduil.springbootbackend.dto.MemberDto" resultType="java.lang.Integer">
        SELECT IFNULL(COUNT(*), 0) FROM MEMBERS
        WHERE ID = #{id}
    </select>

      <!-- 회원 가입 -->
    <insert id="addMember" parameterType="com.hwangduil.springbootbackend.dto.MemberDto">
        INSERT INTO MEMBERS(ID, PWD, NAME, EMAIL, AUTH)
        VALUES (#{id}, #{pwd}, #{name}, #{email}, 3)
    </insert>

      <!-- 로그인 -->
    <select id="login" parameterType="com.hwangduil.springbootbackend.dto.MemberDto" resultType="com.hwangduil.springbootbackend.dto.MemberDto">
        SELECT ID, NAME, EMAIL, AUTH FROM MEMBERS
        WHERE ID = #{id} AND PWD = #{pwd}
    </select>

</mapper>

MemberDao

package com.hwangduil.springbootbackend.dao;

import com.hwangduil.springbootbackend.dto.MemberDto;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

@Mapper
@Repository
public interface MemberDao {

    int getId(MemberDto dto);
    int addMember(MemberDto dto);
    MemberDto login(MemberDto dto);

}

스프링부트에서 DAO는 인터페이스로 작성하고 @Mapper를 통해 mapper.xml의 id를 찾아간다.

아이디 중복검사는 DB를 체크해서 아이디가 존재하면 숫자가 카운트되며 중복되는 아이디가 없을 때 0을 반환한다.
회원가입은 MemberDto를 받아서 추가되는 경우 숫자가 카운트된다.
로그인은 MemberDto를 받아서 MemberDto를 반환해주는데, 이는 프론트엔드단에서 세션을 위해 객체형태로 반환해주는 것이다.

Service 및 Controller


MemberService

import com.hwangduil.springbootbackend.dao.MemberDao;
import com.hwangduil.springbootbackend.dto.MemberDto;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
@RequiredArgsConstructor
public class MemberService {

    private final MemberDao dao;

    public boolean getId(MemberDto dto) {
        int n = dao.getId(dto);
        return n > 0;
    }

    public boolean addMember(MemberDto dto) {
        int n = dao.addMember(dto);
        return n > 0;
    }

    public MemberDto login(MemberDto dto) {
        return dao.login(dto);
    }

}

우선 getId는 아이디가 중복되는 경우 true를 반환하며 그렇지 않은 경우 false를 반환할 것이다.

addMember는 회원으로 등록되었을 때 true를, 어떤 이유로 인해 등록되지 않은 경우 false를 반환한다.

login은 DB로부터 로그인한 유저의 모든 정보를 가져올 것이기 때문에 MemberDto 형태를 반환형으로 지정해주었다.

MemberController

import com.hwangduil.springbootbackend.dto.MemberDto;
import com.hwangduil.springbootbackend.service.MemberService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
@RequiredArgsConstructor
public class MemberController {

    public final Logger logger = LoggerFactory.getLogger(MemberController.class);

    private final MemberService service;

    @PostMapping("/getId")
    public String getId(MemberDto dto) {
        logger.info("MemberController getId()");
        boolean b = service.getId(dto);
        if(b) {
            return "no";
        }

        return "ok";

    }

    @PostMapping("/addMember")
    public String addMember(MemberDto dto) {
        logger.info("MemberController addMember()");
        boolean b = service.addMember(dto);

        logger.info(dto.toString());

        if(b) {
            return "ok";
        }

        return "no";
    }

    @PostMapping("/login")
    public MemberDto login(MemberDto dto) {
        logger.info("MemberController login()");
        return service.login(dto);
    }

}

각각은 중요한 정보가 오고가므로 PostMapping 처리 해준다.

getId는 서비스에서의 getId로부터 받아온 값에 대해 true인 경우 아이디가 중복되므로 "no"를 리턴해주도록 했다.

addMember도 마찬가지로 회원가입이 완료되어 카운트 된 숫자가 0보다 클 경우 true를 반환한다. true일 때 회원가입이 완료되므로 "ok"를 리턴한다.

login은 로그인한 정보를 세션에 객체로 저장해두기 위해 MemberDto를 반환하여 프론트엔드단까지 가져간다.

Frontend 처리

index.html


처음으로 로딩되는 페이지이다. 여기서 바로 로그인 페이지로 넘어가도록 자바스크립트로 처리해주었다.

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>index</title>
</head>
<body>
    <script type="text/javascript">
        location.href="./auth/login.html";
    </script>
</body>
</html>

회원가입


<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <title>회원가입</title>
    <style>
        #app {
            margin: auto;
            margin-top: 60px;
            width: 60%;
            border: 3px solid #8ac007;
            padding: 10px;
        }

        table, th, td {
            border: 1px solid black;
        }
        p {
            font-size: 8px;
        }
    </style>
</head>
<body>

    <h2>회원 가입</h2>

    <div id="app">

        <!-- <form id="frm" action="/addMember" method="post"> -->
            <table>
                <tr>
                    <td>아이디</td>
                    <td>
                        <input type="text" id="userid" placeholder="아이디" size="20" />
                        <p id="idcheck"></p>
                        <button type="button" id="idCheckBtn">중복확인</button>
                    </td>
                </tr>
                <tr>
                    <td>사용할 아이디</td>
                    <td>
                        <input type="text" id="id" name="id" size="20" readonly />
                    </td>
                </tr>
                <tr>
                    <td>비밀번호</td>
                    <td>
                        <input type="text" id="pwd" name="pwd" placeholder="비밀번호" size="20" />
                    </td>
                </tr>
                <tr>
                    <td>이름</td>
                    <td>
                        <input type="text" id="name" name="name" placeholder="이름" size="20" />
                    </td>
                </tr>
                <tr>
                    <td>이메일</td>
                    <td>
                        <input type="text" id="email" name="email" placeholder="이메일 주소" size="20" />
                    </td>
                </tr>
                <tr>
                    <td colspan="2">
                        <button type="button" id="account">회원가입</button>
                    </td>
                </tr>
            </table>
        <!-- </form> -->
    </div>

기본적인 형식은 위와 같다. form 태그를 사용하지 않았으며 jQuery를 사용해서 AJAX로 처리해줄 것이다.

    <script type="text/javascript">

        $(document).ready(function() {

            // 아이디 중복 검사에 대한 로직이다. `getId`에 입력받은 아이디를 JSON 형식으로 POST 요청을 통해 넘겨준다.

            // 이를 통해 서버단으로부터 전달받은 내용(`res`)가 각각 어떤 메시지를 리턴해주는가에 따른 작업을 처리한다.


            $("#idCheckBtn").click(function() {
                $.ajax({
                    url: "http://localhost:3000/getId",
                    type: "POST",
                    data: { "id": $("#userid").val() },
                    success: function(res) {
                        // alert('success')
                        if(res.trim() == 'ok') {
                            $("#idcheck").html("사용 가능한 아이디입니다.");
                            $("#id").val($("#userid").val());
                        } else {
                            $("#idcheck").html("사용할 수 없는 아이디입니다.");
                            $("#userid").val("");
                            $("#userid").focus();
                        }
                    },
                    error: function() {
                        alert('error')
                    }
                });
            });


            // 가입버튼을 클릭했을 때 처리해줄 내용
            // addMember에 POST로 요청을 보낸다. 이 때 보낼 정보는 data로 지정된 JSON 형식의 자바스크립트 객체이다.
            $("#account").click(function() {

                $.ajax({
                    url: "http://localhost:3000/addMember",
                    type: "POST",
                    data: {
                        "id": $("#id").val(),
                        "pwd": $("#pwd").val(),
                        "name": $("#name").val(),
                        "email": $("#email").val(),
                        "auth": 1
                    },

                    // 마찬가지로 리턴받은 메시지(res)의 결과에 따라 처리해준다.
                    success: function(res) {
                        if(res.trim() == 'ok') {
                            console.log(res);
                            alert("가입되었습니다!");
                            // location.href="login.html";
                        } else {
                            alert("가입되지 않았습니다!");
                            $("#userid").val("");
                            $("#pwd").val("");
                            $("#name").val("");
                            $("#email").val("");
                        }
                    }
                });
            });


              // 아래는 넘겨줄 데이터가 많을 때 form 태그에 아이디를 부여해서 serialize() 메소드로 한번에 데이터를 넘겨주는 방법이다.
            /*
            $("#account").on("click", function() {

                let params = $("#frm").serialize();

                if($("#id").val() === "") {
                    $("#idcheck").html("유효한 아이디가 아닙니다.");
                }

                $.ajax({
                    url: "http://localhost:3000/addMember",
                    type: "POST",
                    data: params,
                    success: function(res) {
                        if(res.trim() == 'ok') {
                            console.log(res);
                            alert("가입되었습니다!");
                            // location.href="login.html";
                        } else {
                            alert("가입되지 않았습니다!");
                            $("#userid").val("");
                            $("#pwd").val("");
                            $("#name").val("");
                            $("#email").val("");
                        }
                    }
                });
            });
            */

        });

    </script>
</body>
</html>

로그인


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <title>로그인</title>

    <style>
        #app {
            margin: auto;
            margin-top: 60px;
            width: 60%;
            border: 3px solid #8ac007;
            padding: 10px;
        }

        table, th, td {
            border: 1px solid black;
        }
    </style>
</head>
<body>
    <h2>Login</h2>
    <div id="app">
        <table>
            <tr>
                <td>아이디</td>
                <td>
                    <input type="text" id="id" placeholder="아이디" size="20" />
                </td>
            </tr>
            <tr>
                <td>비밀번호</td>
                <td>
                    <input type="text" id="pwd" placeholder="비밀번호" size="20" />
                </td>
            </tr>
            <tr>
                <td colspan="2">
                    <button type="button" id="login">로그인</button>
                    <a href="account.html">회원가입</a>
                </td>
            </tr>
        </table>
    </div>

    <script>


        // 로그인시 입력한 아이디와 비밀번호를 넘겨준다.
          // 서버단의 로그인 함수가 MemberDto를 반환하기 때문에 프론트엔드단에서 JSON 형태로 넘어온다.
        // 아무것도 입력하지 않으면 JSON의 각 Key에 대한 Value가 비어있을 것이기 때문에 조건식으로 검사해준다.
          // 로그인이 처리되어 정상적으로 넘어왔다면 sessionStorage에 로그인 정보를 저장해서 다른 페이지로 넘겨줄 것이다.
        $(document).ready(function() {
            $("#login").click(function() {
                $.ajax({
                    url: "http://localhost:3000/login",
                    type: "POST",
                    data: {
                        id: $("#id").val(),
                        pwd: $("#pwd").val()
                    },
                    success: function(json) {
                        // alert('success');
                        // alert(JSON.stringify(json));
                        if(json === "") {
                            alert("아이디 또는 비밀번호를 확인하세요.");
                            $("#id").val("");
                            $("#pwd").val("");
                        } else {
                            // 로그인 정보를 세션에 저장
                            sessionStorage.setItem("login", JSON.stringify(json));
                            alert(`${json.name}님 환영합니다!`);
                            location.href = "../boards/boards.html";
                        }
                    },
                    error: function() {
                        alert('error');
                    }
                });
            });
        });

    </script>
</body>
</html>