[스프링SPRING] 회원가입 및 로그인기능 구현

MemberController.java 컨트롤러 생성

  • 회원가입 기능과 로그인 기능 입력
  • http://localhost:8088/member/insert에서 뷰페이지를 볼 수 있다.
  • 한글처리하는 방법
  • redirect의 의미 : 주소줄에 페이지 전환을 의미
  • 파라미터를 가지고 다니는 방법 두가지
    • 첫번째방법 : MemberVO vo
    • 두번째 방법 : @ModelAttribute("userid") String id , 주 사용처 : 페이징처리시 페이지넘버
    • 보통 하나의 파라미터가 있다면 아래 방법을 사용하고 두개이상의 파라미터를 가져가려면 VO객체를 가지고 이동한다.
    • 만약 하나의 파라미터가 VO객체 안에 있는 파라미터라면 VO객체로 들고 다니면 된다.
  • 제어
    • 컨트롤러 : 페이지이동에 대한 제어
    • 서비스 : 페이지이동외의 나머지 제어
  • 메인페이지로 이동시 해당 정보 있는 경우 : => main페이지로 이동
    • if(returnVO.getUserid() != null) return "redirect:/main";
    • 에러발생 equest processing failed; nested exception is java.lang.NullPointerException: Cannot invoke “com.itwillbs.domain.MemberVO.getUserid()” because “returnVO” is null
    • 따라서 if(returnVO.getUserid() != null) return "redirect:/main"; 대신 if(returnVO != null) return "redirect:/main"; 사용한다.
  • HttpSession : 세션값생성
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
package com.itwillbs.test;


import javax.inject.Inject;
import javax.servlet.http.HttpSession;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.itwillbs.domain.MemberVO;
import com.itwillbs.service.MemberService;

@Controller
@RequestMapping(value = "/member/*")
public class MemberController {
//3-1. 서비스 처리 객체를 주입(DI)
@Inject
private MemberService service;

private static final Logger l = LoggerFactory.getLogger(MemberController.class);

/* 회원가입 처리하는 동작 */
//insert라는 하나의 주소로 get과 post방식 즉 입력과 출력 둘 다 처리 가능

// http://localhost:8088/test/insert
// http://localhost:8088/test/member/insert
// http://localhost:8088/member/insert

@RequestMapping(value = "/insert", method = RequestMethod.GET)
//value="/member/insert"에서 member를 빼도 됨
public String insertGET() throws Exception {
l.info("C: 회원가입 입력페이지 GET");
return "/member/insertMember";
}

@RequestMapping(value = "/insert", method = RequestMethod.POST)
//value="/member/insertPro"에서 member를 빼도 됨
public String insertPOST(MemberVO vo) throws Exception{

//1. 한글처리 : request객체가 없다 => web.xml에서 filter태그로 인코딩해야한다.

//2. 전달된 파라미터 받기
//request.getParameter라는 내장객체가 없다. 따라서 메서드의 매개변수를 통해 가져올 수 있다.
//l.info("C: "+ request.getParameter()); 에러발생
l.info("C: "+ vo);

//3. 서비스객체 생성(직접생성안하고 의존주입)
//3-2. 서비스객체호출
service.insertMember(vo);
l.info("C: 회원가입 처리페이지 POST");

//4. 로그인페이지로 이동(주소줄과 view페이지 동시에 insert->login 변경되어야함)
return "redirect:/member/login";
}

/* 로그인 기능 */
// http://localhost:8088/member/login

@RequestMapping(value = "/login", method = RequestMethod.GET)
public String loginGET() throws Exception{
l.info("C: 로그인 입력페이지 GET");
return "/member/loginForm";
}


@RequestMapping(value = "/login", method = RequestMethod.POST)
//public String loginPOST(@ModelAttribute("userid") String id, @ModelAttribute("userpw") String pw) throws Exception{
public String loginPOST(MemberVO vo, HttpSession session, RedirectAttributes rttr) throws Exception{
l.info("C: 로그인 처리페이지 POST");

//1.한글처리 => web.xml에서 완료

//2.전달받은 파라미터 저장 => loginPOST()메서드의 파라미터값으로 저장함.
l.info("C: "+ vo.getUserid() + vo.getUserpw());

//3.서비스객체생성 => 22번째 코드로 의존주입완료
//4.서비스 로그인 체크 동작(HttpSession)
MemberVO returnVO = service.loginMember(vo);
l.info("C: 리턴VO결과(서비스에서 예외처리를 진행했으므로 null이 출력되면 코드에 문제있다는 의미) "+returnVO);

//5.메인페이지로 이동(주소줄과 view페이지 동시에 main으로 변경되어야함)
// 해당 정보 있는 경우 : => main페이지로 이동
//if(returnVO.getUserid() != null) {
//에러가 발생하는 이유:
if(returnVO != null) {
//5.세션값생성
session.setAttribute("id", returnVO.getUserid());

//RedirectAttributes의 addAttribute메서드 사용해보려고 했으나 실패. 에러 발생
//Map<String, Object> map = new HashMap<String, Object>();
//map.put("username", returnVO.getUsername());
//map.put("useremail", returnVO.getUseremail());
//rttr.addAttribute("mvo", map);

rttr.addFlashAttribute("mvo", returnVO);
//l.info("C: 모델에 뭐 들어있니?"+model);
return "redirect:/member/main";
} else {
// 해당 정보 없는 경우 : => login페이지로 이동
return "redirect:/member/login";
}
}//end of loginPOST()
}




insertMember.jsp 회원가입 입력받는 페이지 생성

  • 회원가입 입력페이지 구현
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
insertMember.jsp 입니다
<h1>views의 폴더명은 컨트롤러의 모듈명과 동일</h1>
<h1> 회원가입 페이지 </h1>

<fieldset>
<legend> ITWILL 회원가입 </legend>
<form action="/member/insert" method="post">
ID : <input type="text" name="userid"> <br>
PW : <input type="password" name="userpw"> <br>
NAME : <input type="text" name="username"> <br>
EMAIL : <input type="text" name="useremail"> <br>

<input type="submit" value="회원가입">
</form>
</fieldset>




주소줄에서 test빼기

아래 방법으로 서버 주소줄에서 test를 뺄 수 있다.

  • servers탭 > server인 Tomcat 더블클릭 > Modules클릭 > Add Web Module클릭 > Path: /test/로 변경 > ok클릭 > 저장 후 서버 재시작
  • 이제 http://localhost:8088/test/member/insert가 아닌 http://localhost:8088/member/insert로 접속가능하다




loginForm.jsp 로그인 입력받는 페이지 생성

  • 로그인 입력 페이지 구현
  • action태그의 속성값(주소)이 없어도 동일하게 동작한다.
    • 그 이유는 action태그에 주소가 없으면 자기 자신을 호출한다.
    • GET방식으로 한번 호출 뒤 다시 자기자신을 호출하니까 POST방식이 호출되므로 주소가 있든 없든 동일한 결과를 볼 수 있다.
    • 주소는 동일하지만 찾아갈 메서드가 GET에서 POST로 달라진다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
loginFrom.jsp
<h1>views의 폴더명은 컨트롤러의 모듈명과 동일</h1>
<h1> 로그인 </h1>

<fieldset>
<legend> ITWILL 로그인 </legend>
<!-- <form action="/member/login" method="post"> -->
<form action="" method="post">
ID : <input type="text" name="userid"> <br>
PW : <input type="password" name="userpw"> <br>
<input type="submit" value="로그인">
<input type="button" onclick="location.href='/member/insert'" value="회원가입">
</form>
</fieldset>




MemberService.java 인터페이스 생성

  • 일반적으로는 DAO랑 같은 메소드명을 사용하여 일관성을 유지한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.itwillbs.service;

import com.itwillbs.domain.MemberVO;

public interface MemberService {
// 회원 가입 (일반회원가입테이블에 sns계정 컬럼 추가하기)
// 일반적으로는 DAO랑 같은 메소드명을 사용하여 일관성을 유지한다
public void insertMember(MemberVO vo);

// 로그인기능 DAO랑 같은 이름으로 하지않은이유? 이름이 달라도 메서드생성가능하다는 걸 보여주기 위해서.
// public MemberVO readMemberWithIDPW(String userid,String userpw) throws Exception;

// 로그인기능
public MemberVO loginMember(MemberVO vo);

}




MemberServiceImpl.java 인터페이스 오버라이딩 메서드 구현

  • 예외처리를 VO객체로 할 수 있다.
    • catch구문안에 returnVO = null;을 넣어 실행하다 문제가 생겼을때 해당 데이터를 보내지않겠다는 의미로 예외처리를 할 수 있다.
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
31
32
33
34
35
36
37
38
39
40
package com.itwillbs.service;

import javax.inject.Inject;
import org.springframework.stereotype.Service;
import com.itwillbs.domain.MemberVO;
import com.itwillbs.persistence.MemberDAO;

@Service
public class MemberServiceImpl implements MemberService {

//DB와 연결 (의존주입)
@Inject
private MemberDAO mdao;

//회원가입
@Override
public void insertMember(MemberVO vo) {
//컨트롤러 -> 서비스 호출 -> DAO 호출 -> Mapper -> DB
System.out.println("S : 회원가입동작");
if(vo == null) {
//처리
return;
}
mdao.insertMember(vo);
}

//로그인기능
@Override
public MemberVO loginMember(MemberVO vo) {
System.out.println("S : 컨트롤러에서 호출받으면 필요한 정보를 받아서 DAO로 전달");
MemberVO returnVO = null;
try {
returnVO = mdao.readMemberWithIDPW(vo.getUserid(), vo.getUserpw());
} catch (Exception e) {
e.printStackTrace();
returnVO = null; //실행하다 문제가 생겼을때 해당 데이터를 보내지않겠다는 의미 = 예외처리
}
return returnVO; //null이 반환되면 앞의 코드가 문제가 있다는 것을 바로 알수있다.
}
}




MemberDAO.java 인터페이스생성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import com.itwillbs.domain.MemberVO;

public interface MemberDAO {
//해당 도메인에 관련된 기능 선언
public String getTime();

//C: 회원가입
public void insertMember(MemberVO vo);

//R: 회원 정보 조회 - 사용자 ID 해당하는 정보 가져오기
public MemberVO readMember(String userid) throws Exception;

//R: 회원 정보 조회 - ID,PW정보에 해당하는 사용자 정보
public MemberVO readMemberWithIDPW(String userid,String userpw) throws Exception;
}




MemberDAOImpl.java 인터페이스 오버라이딩 메서드 구현

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import java.util.HashMap;
import java.util.Map;

import javax.inject.Inject;

import org.apache.ibatis.session.SqlSession;
import org.springframework.stereotype.Repository;

import com.itwillbs.domain.MemberVO;

@Repository
public class MemberDAOImpl implements MemberDAO {

//DB연결 (xml에서 만들어진 객체를 가져다 사용하고자함 = 의존 주입)
@Inject
private SqlSession sqlSession; //mapper위치까지 접근 가능 but mapper가 여러개일수있음 => mapper구분필요

//mapper구분하는 값 namespace
private static final String namespace = "com.itwillbs.mappers.memberMapper";

@Override
public String getTime() {
String result = sqlSession.selectOne(namespace+".getTime"); //괄호안에 쿼리구문입력하기
return result;
}

//회원가입구현 -> memberMapper.xml 작성 후 아래 메서드 작성 -> MemberDAOtest.java 이동
@Override
public void insertMember(MemberVO vo) {
System.out.println("#####");
sqlSession.insert(namespace+".insertMember", vo); //괄호안은 (쿼리구문, 매개변수)순으로 입력하기
}

//회원 정보 조회-사용자 ID 해당하는 정보 가져오기
@Override
public MemberVO readMember(String userid) throws Exception {
//테스트(컨트롤러) 호출 -> 정보를 저장 -> DB로이동
MemberVO vo = sqlSession.selectOne(namespace+".readMember", userid); //괄호안의 물음표를 콤마뒤에 쓰는거임
return vo;
}

//인터페이스 선언 -> 서브클래스 구현
//회원 정보 조회 - ID,PW정보에 해당하는 사용자 정보
@Override
public MemberVO readMemberWithIDPW(String userid, String userpw) throws Exception {
//테스트(컨트롤러) 호출 -> 정보를 저장 -> DB로이동

//String인자를 2개를 가져갈 수 없기때문에 파라미터 두개를 객체인 Map에 넣어서 가지고 넘긴다
//return sqlSession.selectOne(namespace+".readMemberWithIDPW", userid, userpw);

// DB로 정보를 전달하기 위해서는 sqlSeesion 객체 활용
// * 1개 이상의 정보를 전달할때는 객체 단위로 전달
// * 객체(VO) 안에 저장이 안되는 정보의 경우 Map을 사용
// Map은 key-value형태 : 이때 key값은 sql구문의 #{ㅇㅇㅇ} 이름과 같아야함

Map<String, Object> paramMap = new HashMap<String, Object>();
paramMap.put("userid", userid);
paramMap.put("userpw", userpw);

return sqlSession.selectOne(namespace+".readMemberWithIDPW", paramMap);
}
}




memberMapper.xml 구현

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
31
32
<?xml version="1.0" encoding="UTF-8"?>
<!-- DTD지정 -->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">


<mapper namespace="com.itwillbs.mappers.memberMapper">
<select id="getTime" resultType="string">
select now()
</select>

<!-- values(?,?,?,?) sql문법 대신에 Mybatis 문법#{}을 사용
get() 또는 set() 마이바티스가 결정해서 메서드실행-->
<insert id="insertMember">
insert into tbl_member(userid,userpw,username,useremail)
values (#{userid},#{userpw},#{username},#{useremail})
</insert>

<select id="readMember" resultType="com.itwillbs.domain.MemberVO">
select *
from tbl_member
where userid=#{userid}
</select>

<select id="readMemberWithIDPW" resultType="com.itwillbs.domain.MemberVO">
select *
from tbl_member
where userid=#{userid} and userpw=#{userpw}
</select>

</mapper>




MemberVO.java 생성

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
31
32
33
import java.sql.Timestamp;

public class MemberVO {
private String userid;
private String userpw;
private String username;
private String useremail;
private Timestamp regdate;
private Timestamp updatedate;

//생성자 2개 : 기본생성자, 멤버변수를 인자로 전부를 가진 생성자
public MemberVO() {}
public MemberVO(String userid, String userpw, String username, String useremail, Timestamp regdate,
Timestamp updatedate) {
super();
this.userid = userid;
this.userpw = userpw;
this.username = username;
this.useremail = useremail;
this.regdate = regdate;
this.updatedate = updatedate;
}

//get() set()메서드 생성
public String getUserid() {
return userid;
}
public void setUserid(String userid) {
this.userid = userid;
}

(중략)
}