본문 바로가기

자바/스프링부트 기초

스프링부트! 게시판 만들면서 배우기! 3.로그인, 회원가입 구현하기

자! 오늘은 로그인 , 회원가입 기능을 구현해보도록 하겠습니다! 로그인은 따로 페이지를 두지 않고 저번에 만들었던 index.jsp 상단의 로그인 바를 이용하겠습니다!

 

자 우선 로그인이 되려면 회원가입 부터 되야겠죠?

회원가입 Jsp를 한번 만들어보겠습니다!

 

저는 WEB-INF-Jsp 밑에 member라는 폴더를 만들어서 안에 register.jsp를 만들어 줄 예정입니다! 

register.jsp 마찬가지로 부트스트랩을 이용할거기때문에 전에 만들어놓은 head.jspf 파일을 include해줍니다.

디자인은 하고싶으신대로 하셔두되구요! 전 이런 형식으로 나오게 했습니다!

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
    <head>
		<%@include file="../part/head.jspf" %> 
        <title>Insert title here</title>
    </head>
    <style>

        form {
            padding: 10px;
            border: 1px solid black;
            width: 420px;
            margin: auto;
        }
        img {
            height: 250px;
            width: 250px;
            display: block;
            margin: auto;

        }
		fieldset {
			margin-left: 40px;
		}
		input {
			margin-top: 10px;
			width: 250px;
		}
        #legend {
            text-align: center;
        }
    </style>
    
    <body>
        <form class="form-horizontal" action='./doRegister' method="POST">
		
            <img src="/images/my_logo.png">
			<div id="legend">
				<legend>회원가입</legend>
			</div>
            <fieldset>

             
                <div class="control-group">
                    <!-- Username -->
                    <label class="control-label" for="username">아이디</label>
                    <div class="controls">
                        <input
                            type="text"
                            id="userId"
                            name="userId"
                            placeholder="아이디를 적어주세요"
                            class="input-xlarge">
                    </div>
                </div>
                
                <div class="control-group">
                    <!-- Password-->
                    <label class="control-label" for="password">비밀번호</label>
                    <div class="controls">
                        <input
                            type="password"
                            id="password"
                            name="password"
                            placeholder=""
                            class="input-xlarge">
                        <p class="help-block">비밀번호를 입력해주세요</p>
                    </div>
                </div>
                
                <div class="control-group">
                    <!-- Password -->
                    <label class="control-label" for="password_confirm">비밀번호 재입력</label>
                    <div class="controls">
                        <input
                        	onblur="checkPassword()"
                            type="password"
                            id="password_confirm"
                            name="password_confirm"
                            placeholder=""
                            class="input-xlarge">
                        <p class="help-block">비밀번호를 다시 입력해주세요!</p>
                    </div>
                </div>
                


      			<div class="control-group">
                    <!-- E-mail -->
                    <label class="control-label" for="name">이름</label>
                    <div class="controls">
                        <input type="text" id="name" name="name" placeholder="" class="input-xlarge">
                        <p class="help-block">이름을 입력해주세요.</p>
                    </div>
                </div>
                
                <div class="control-group">
                    <!-- E-mail -->
                    <label class="control-label" for="email">E-mail</label>
                    <div class="controls">
                        <input type="text" id="email" name="email" placeholder="" class="input-xlarge">
                        <p class="help-block">Please provide your E-mail</p>
                    </div>
                </div>
                
                <div class="control-group">
                    <!-- E-mail -->
                    <label class="control-label" for="tel">연락처</label>
                    <div class="controls">
                        <input type="tel" id="tel" name="tel" placeholder="" class="input-xlarge">
                        <p class="help-block">연락처를 입력해주세요!</p>
                    </div>
                </div>

         

        
                <div class="control-group">
                    <!-- Button -->
                    <div class="controls">
                        <button class="btn btn-success">가입하기</button>
                        <button type="button" class="btn btn-danger" onclick="history.back()">취소</button>
                    </div>
                </div>
            </fieldset>
        </form>
       <script src="/js/register.js">

    </script>

register.js

let password = document.getElementById('password'); 
let password_confirm = document.getElementById('password_confirm');
console.log(password);
console.log(password_confirm);
function checkPassword() {
	   if(password.value !== password_confirm.value) {
	    alert("비밀번호가 일치 하지 않습니다.");
	    password.value ="";
	    password_confirm.value = "";
	    password.focus();
	   }	
}

 

설명을 간단히 하자면! 안에 내용을 입력하고 가입하기를 누르면  submit이 되어 /article/doRegister 의 주소로 요청하도록 하였고 취소 버튼을 눌렀을 시 뒤로가기 하게 햇습니다. 또한 비밀번호 입력 시 확인창과 다르면 다르 입력 할 수 있도록 했습니다!

 

자 그럼 이제 MVC 중 V는 끝났구 이제 M의 model를 설계해볼까욧

일단 저는 dto 패키지를 하나 만들어서 그 안에  Member 클래스를 하나 만들어줬습니다!

저는 만들어놓고 설명이라 딴거는 나중에 차차 만들겠슴돵!

Member클래스의 내용은 다음과 같습니다.

package com.example.starter.dto;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Member {
	private String mid;
	private String password;
	private String name;
	private String email;
	private String tel;
	private String regDate;
}

자!  안에 변수들은 저희가 설계한 Member 테이블의 변수값을 넣었구요 여기서 이제 생소한게 보이실 텐데요 

바로 @Data , @AllArgsConstructor , @NoArgsConstructor 입니다. 이 어노테이션들은 저희가 설치한 Lombok 이란 라이브러리인데요! 

우선 기능을 보자면 @Data는 저희가 기존에는 getter,setter를 선언해주던것을 저 Data를 붙이면 자동으로 선언해줍니다! 이런식으로 말이죠! 그 뿐만아니라 @ToString  ,@EqualsAndHashCode, @RequiredArgsConstructor까지

선언해줍니다!  엄청 편한 기능을 해주죠

그 밑에 ArgsConstructor는  말그대로 All은 모든 인자를 받는 생성자 , No는 인자를 받지 않는 생성자를 생성해줍니다.

 

 

근데 이 좋은 롬북이 설치 시 오류를 가져올 수 있습니다.. 저 같은 경우엔 ㅠㅠ

우선 저의 상황을 말하자면 

1. 롬북이 import 되어도 어노테이션 시 get set 생성자 어느 하나 자동으로 생성되지 않았습니다.. 

그래서 찾던 중 Lombok.jar를 다운받아서 직접 설치하면 된다! 라는 글을 보고

호다닥 projectlombok.org/download 에서 다운 받았습니다

 

Download

 

projectlombok.org

원래 jar 파일을 누르면 자동 설치가 된다는데 저는 압축 풀기만 나왓습니다.. 그래서 더 찾던 중 cmd를 이용해서 실행 시킬 수 있다고해서 cmd에 들어가 lombok.jar가 있는 곳으로 이동하여 

라고 입력하시면! 짠 롬북이 실행됩니다.

이제 자동으로 설치된 이클립스를 찾아준느데요! 저는..여기서도 못찾더라구요ㅎㅎ 그래서 Specify location를 누르시고 이클립스가 설치된 곳에가 이클립스를 선택하주시고 install 해주시면 됩니다! 그리고 이클립스를 다시 들어가시면 왠만한 분들은 작동 하실 거에요 ㅎㅎ 하지만 전 또 오류가 뜨더라구요 이번에는 설치 후 들어갔더니 이클립스가 1초 후에 종료되는 오류였습니다.  이거에 대해서 또 찾다보니 이클립스 폴더 안에 eclips.ini가 있는데

거기 들가서 -vmargs 밑에 -Xbootclasspath/a:lombok.jar , -javagent:lombok.jar 를 입력해주시면 됩니다!

 요렇게 해주고 재접속하니 이제 자동으로 꺼지니도 않고 getter,setter도 완벽하게 됫습니다 ~!

 

자 이게 Model 까지 만들었으니 Controller를 만들어 볼까요

 

우선 controller 패키지 안에  MemberController.java 클래스를 하나 만들어줍니다!

 

아 그리고 MemberController위에 

	@Autowired
	ArticleService articleService;

로 ArticleService의 필드를 주입해줍니다

이로써 해당 클래스에서는 ArticleService를 선언없이 사용할 수 있습니다.

 

 

그 후 /doRegister를 매핑한 메소드를 하나 만들어줍니다

	@RequestMapping("/doRegister")
	public String doRegister(@RequestParam Map<String,Object> param ,Model model) {
		// 로그인 ID의 중복성 체크
		
		Map<String , Object> checkLoginId = memberService.checkLoginIdDup((String)param.get("userId"));
		if( ((String)checkLoginId.get("resultCode")).startsWith("F-")) {
			model.addAttribute("alertMsg",checkLoginId.get("msg"));
			model.addAttribute("historyBack",true);
			return "common/redirect";
		}
		
		Map<String, Object> RegisterRs = memberService.register(param);

		if ( ((String)RegisterRs.get("resultCode")).startsWith("F-") ) {
			model.addAttribute("alertMsg", RegisterRs.get("msg"));
			model.addAttribute("historyBack", true);
			return "common/redirect";
		}

		model.addAttribute("alertMsg", RegisterRs.get("msg"));
		model.addAttribute("redirectUrl","/");
		
		return "common/redirect";
		
	}

우선 파라미터부터 볼까요? 스프링부트에선 인자안에 @RequestParam 을 붙여주면 jsp로부터 날라온 데이터를 받을 수있습니다. 저는 Map<String,Object>로 받아 아이디,비번 등 4가지 입력값을 map에다 저장합니다. 그리고 Model 객체는 다시 jsp로 정보를 전달할 때 사용합니다. 마치 jsp에서 request.getParameter와 비슷한 느낌입니다.

 

그 다음 코드는 아이디 중복 체크 코드인데요 이건 Service에서 해줄 겁니다. Service는  Mvc 패턴에서

DAO를 호출해 DB내용을 가져와 데이터를 처리하고 다시 컨트롤러로 가져다 주는 역할을 합니다.

자 그럼 서비스와 Dao를 한번 만들어보겠습니다.

우선 Service 패키지를 하나 만들고 그 안에 MemberService를 하나 만들어줍니다! 우선 서비스에서 할 기능은

(서비스는 컨트롤러에서 데이터를 받아 DAO로 전달하여 쿼리문을 실행하고 그 결과값을 다시 컨트롤러로 전달해줍니다.)  컨트롤러에서 받은 데이터를 insert하는 register과  id를 받아 중복체크를 하는 메소드 입니다.

Service는 인터페이스이기에 이를 구현해주는 클래스가 또 있어야 겠죠?

MemberServiceImpl 를 하나 만들어줍니다.  MemberServiceImpl에서도 마찬가지로

@Autowired MemberDao memberdao를 해줍니다.

(사실 따로 만드는 이유가 궁금해 검색해보니 , 약간 관습? 그런거라고 하더라구요)

package com.example.starter.service;

import java.util.Map;

import com.example.starter.dto.Member;

public interface MemberService {
	public Map<String, Object> checkLoginIdDup(String userId);
	public Map<String, Object> register(Map<String, Object> param);
}

MemberSeriviceImpl

package com.example.starter.service;

import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;

import com.example.starter.dao.MemberDao;
import com.example.starter.dto.Member;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class MemberServiceImpl implements MemberService {
	@Autowired
	MemberDao memberDao;
	
	public Map<String, Object> checkLoginIdDup(String userId) {
		int count = memberDao.getLoginIdDupCount(userId);
		
		String resultCode = "";
		String msg = "";
		
		if ( count == 0 ) {
			resultCode = "S-1";
			msg = "사용가능한 로그인 ID 입니다.";
		}
		else {
			resultCode = "F-1";
			msg = "이미 사용중인 로그인 ID 입니다.";
		}
		
		Map<String, Object> rs = new HashMap<String, Object>();
		rs.put("resultCode", resultCode);
		rs.put("msg", msg);
		
		return rs;
	}
	
	@Override
	public Map<String, Object> register(Map<String, Object> param) {
		memberDao.register(param);
		System.out.println("아이디의 객체 타입"+param.get("userId").getClass().getName());
		String newId = (String)param.get("userId");

		String resultCode = "";
		String msg = "";

		if ( newId ==null ) {
			resultCode = "F-1";
			msg = "회원가입에 실패했습니다.";
		}
		else {
			resultCode = "S-1";
			msg = "회원가입 되었습니다.";
		}

		Map<String, Object> rs = new HashMap<String, Object>();
		rs.put("resultCode", resultCode);
		rs.put("msg", msg);
		rs.put("newId", newId);

		return rs;
	}

 자 어노테이션부터 @Slf4j는 저번에 소개드린 sql문 실행시 실행문과 결과를 콘솔창으로 확인 할 수있도록 해주는 어노테이션입니다.

그후 @Service를 선언해줘 이 클래스는 service의 역활을 하겠다 해줍니다.

 

다음은 이제 중복 체크 부분입니다. 이는 Dao를 먼저 설계하고 다시 보도록 하겠습니다.

 

우선 저희는 Mybatis를 이용할 예정이기에 interface Dao를 만들어주고 이를 사용할 Dao.xml를 만들어 줘야합니다

우선 Member Dao를 보겠습니다

package com.example.starter.dao;

import java.util.Map;

import org.apache.ibatis.annotations.Mapper;

import com.example.starter.dto.Member;

@Mapper
public interface MemberDao {
	
	public int getLoginIdDupCount(String userId);
	public void register(Map<String, Object> param);

}

 

어노테이션 @Mapper를 선언해줌으로서 Mybatis에서 이 클래스를 참조 할 수 잇도록 해줍니다.

getLoginDupCount 는 userId를 인자로 받아 int를 반환하는 메소드 입니다.

MemberDao.xml를 보면 작성방식은

<mapper> 태그를 통해 Mapper해놓은 Dao를 작성해줍니다.

그 후 

중복체크 부분 부터 보면 해당 id를 받아 테이블내에 같은 id를 가진 칼럼의 갯수를 반환합니다.

select 문 안에 id ="메소드 이름" parameterType ="전달받은 데이터 타입" 저희는 userID를 받으므로 String입니다.

resultType = 반환 타입입니다. 갯수를 반환하므로 int입니다.

안에 #{userId}는 전달받은 변수를 가져다 쓰는 것입니다. 변수를 쓰는방법은 $과 #이 있는데

$는 userId = park라면 그대로 park가 적히는것이고 #는 'park517' 처럼 작은 따옴표를 포함해 줍니다.

 

그리고 register부분을 보면 useGeneratedKeys라는 것이 있습니다 이건  insert를 하고 기본키를 반환해준다는 의미입니다. 그럼 register를 수행 후 member 테이블의 기본키인 mid를 반환해준다는 의미입니다.

<?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.starter.dao.MemberDao">
	<select id="getLoginIdDupCount" parameterType="String" resultType="int">
		SELECT COUNT(*)
		FROM member
		WHERE mid = #{userId}
	</select>
	
	<insert id="register" parameterType="map" useGeneratedKeys="true"
		keyProperty="mid">
		INSERT INTO member
		SET mid = #{userId},
		password = #{password},
		name = #{name},
		email = #{email},
		tel = #{tel},
		regDate = NOW()
	</insert>
</mapper> 

자 그럼 Service를 다시 볼까요?

userId를 Dao로 넘겨줘서 그 결과값으로 int이 날라옵니다.

이때  그 값이 0이면 테이블안에 같은 id값이 없다는 거겠죠? 그러면 resutlCode에 성공코드와 msg에 사용이 가능하다는것을 담아서 반환합니다.

public class MemberServiceImpl implements MemberService {
	@Autowired
	MemberDao memberDao;
	
	public Map<String, Object> checkLoginIdDup(String userId) {
		int count = memberDao.getLoginIdDupCount(userId);
		
		String resultCode = "";
		String msg = "";
		
		if ( count == 0 ) {
			resultCode = "S-1";
			msg = "사용가능한 로그인 ID 입니다.";
		}
		else {
			resultCode = "F-1";
			msg = "이미 사용중인 로그인 ID 입니다.";
		}
		
		Map<String, Object> rs = new HashMap<String, Object>();
		rs.put("resultCode", resultCode);
		rs.put("msg", msg);
		
		return rs;
	}
	

그럼 Controller 부분에서

		Map<String , Object> checkLoginId = memberService.checkLoginIdDup((String)param.get("userId"));
		if( ((String)checkLoginId.get("resultCode")).startsWith("F-")) {
			model.addAttribute("alertMsg",checkLoginId.get("msg"));
			model.addAttribute("historyBack",true);
			return "common/redirect";
		}

반환받은 맵 안에 성공코드가 F-이면 중복된 아이디가 있다는 메세지와 historyBack에 true를 담아 common/redirect.jsp로 넘겨줍니다.

redirect.jsp는 빈 페이지로 자바 스크립트만 수행하는용도로 따로 만들었습니다.

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>

<script>
	var alertMsg = '${alertMsg}';
	var historyBack = '${historyBack}' == 'true';
	var redirectUrl = '${redirectUrl}';
</script>

<script>
	if (alertMsg) {
		alert(alertMsg);
	}
	if (historyBack) {
		location.href = document.referrer;
	}
	if ( redirectUrl ) {
		location.replace(redirectUrl);
	}
</script> 

 만약 값이 넘어왔을 떄 historyBack이 true이면 document.referrer를 수행해줍니다

여기서 referrer은 뒤로가기 후 새로고침까지 해주는 명령어 입니다.

alertMsg가 있다면 그 메세지 내용은 alert를 이용하여 띄워줍니다.

 

그리고 다시 MemeberController에서 날라온 resultCode가 F-1이 아니면

날라온 파라미터들을 register에 담아 보내 

회원가입을 수행합니다.

		Map<String, Object> RegisterRs = memberService.register(param);

		if ( ((String)RegisterRs.get("resultCode")).startsWith("F-") ) {
			model.addAttribute("alertMsg", RegisterRs.get("msg"));
			model.addAttribute("historyBack", true);
			return "common/redirect";
		}

		model.addAttribute("alertMsg", RegisterRs.get("msg"));
		model.addAttribute("redirectUrl","/");
		
		return "common/redirect";

 

 

자 이제 서버를 실행시키고 index.jsp로 들어가 회원가입버튼을 누르고 회원가입을 진행해 보겠습니다 ㅎㅎ

 

회원가입에 성공 하셨다면 회원가입 되었습니다 라는 문구와 함께 확인 버튼 눌렀을 시 index.jsp로 돌아가는걸 볼 수있습니다! 또 콘솔창에서 쿼리 실행문과 결과를 볼 수 있습니다

테이블에도 잘 들어갔네요 ㅎㅎ 그럼 이제 로그인에 대해서 알아볼까요?

 

index.jsp에서 로그인 버튼을 잘 보시면 저는 버튼을 만들어 onclick - href로 조작하도록 했습니다.

로그인 버튼 눌렀을 시 /login으로 요청하게되고

 

이 /login은  MemberController에  login이라는 메소드로 만들어줬습니다.

이제 컨트롤러를 만들어 줬으니 Service, ServiceImpl, Dao를 만들어 줘야겠죠?

	@RequestMapping("/login")
	public String login (@RequestParam Map<String,Object> param , Model model ,HttpSession session ) {
		String id = (String)param.get("id");
		String password = (String)param.get("password");
		
		Member member = memberService.getMember(id,password);
		if(member == null) {
			model.addAttribute("alertMsg", "일치하는 회원이 존재하지 않습니다.");
			model.addAttribute("historyBack", true);
			return "common/redirect";
		}
		session.setAttribute("loginMember", member);
		
		model.addAttribute("alertMsg", "로그인 되었습니다.");
		model.addAttribute("historyBack",true);
		
		return "common/redirect";
	}

MemberService에 를 추가해줍니다.

	public Member getMember(String id , String password);

그후 MemberServiceImpl에도 추가해줍니다!

	@Override
	public Member getMember(String id ,String password) {
		Member member = memberDao.getMember(id,password);
		return member;
	}

다음 Dao 부분도!

	public Member getMember(String id ,String password);

마지막으로 Dao.xml!

	<select id="getMember" parameterType="String" resultType="Member">
		SELECT *
		FROM member
		WHERE mid = #{id}
		and password = #{password}
	</select>

 

 

이제 Controller 코드를 다시 보면

파라미터로 입력한 id password를 받아 memberService.getMember에 넣어줘서

Member 테이블에서 해당 id와 password가 일치하는 항목을 찾아 리턴 받아

session에 해당 Member 데이터를 저장합니다.

세션에 저장함으로써 다른 페이지에서도 이를 사용 할 수 있게 관리해줍니다.

	@RequestMapping("/login")
	public String login (@RequestParam Map<String,Object> param , Model model ,HttpSession session ) {
		String id = (String)param.get("id");
		String password = (String)param.get("password");
		
		Member member = memberService.getMember(id,password);
		if(member == null) {
			model.addAttribute("alertMsg", "일치하는 회원이 존재하지 않습니다.");
			model.addAttribute("historyBack", true);
			return "common/redirect";
		}
		session.setAttribute("loginMember", member);
		
		model.addAttribute("alertMsg", "로그인 되었습니다.");
		model.addAttribute("historyBack",true);
		
		return "common/redirect";
	}

자 이제 아까 회원가입했던 아이디 패스워드로 접속하게 되면

이렇게 나옵니닷!

 

 

자 오늘은 로그인 회원가입을 알아봣는데용! 다음 글에서는 이제 게시물에대한 글을 천천히 올려보겠습니다!