본문으로 바로가기

유효성 검사를 통해 회원가입을 하는 방법이다. 회원가입을 할때 이용약관의 동의를 해야 넘어가지고, 이메일이 맞는지, 아이디가 존재하는 지 등 유효성 검사를 거친 후 회원가입이 완료 된다. 복잡하지만 복잡하지 않는 듯한 느낌. Validator를 이용해서 유효성 검사를 할 것이다. hibernate로 유효성 검사를 할 수도 있다. 아마 hibernate가 더 간단할 것이다. 다음에 hibernate로 유효성 검사를 하여 회원가입하는 것도 만들어 봐야지!

 

하나의 기능이 구현되는 순서는 기본적으로 Controller -> Service -> ServiceImp -> DAO -> SQL(XML) -> JSP 이다. 

 

1) DB 생성 (MySQL)

1
2
3
4
5
6
7
8
9
10
11
12
CREATE TABLE `user` (
  `IDX` int(11NOT NULL AUTO_INCREMENT,
  `ID` varchar(45NOT NULL,
  `EMAIL` varchar(100NOT NULL,
  `NAME` varchar(45NOT NULL,
  `PASSWORD` varchar(45NOT NULL,
  `GRADE` int(11DEFAULT '0',
  `REGDATE` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`IDX`),
  UNIQUE KEY `EMAIL_UNIQUE` (`EMAIL`),
  UNIQUE KEY `ID_UNIQUE` (`ID`)
ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8
cs

 

 

2) 파일 및 패키지 생성

 

2-1. UserController.java 생성

1
2
3
4
5
6
7
package tody.lovely.controller;
 
import org.springframework.stereotype.Controller;
 
@Controller
public class UserController {
}
cs

- 5행 : @Controller를 등록해야 한다.

 

2-2. UserVO.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
package tody.lovely.vo;
 
import java.util.Date;
 
public class UserVO {
    
    private int IDX;
    private String ID;
    private String EMAIL;
    private String NAME;
    private String PASSWORD;
    private int GRADE;
    private Date REGDATE;
    
    public int getIDX() {
        return IDX;
    }
    public void setIDX(int iDX) {
        IDX = iDX;
    }
    public String getID() {
        return ID;
    }
    public void setID(String iD) {
        ID = iD;
    }
    public String getEMAIL() {
        return EMAIL;
    }
    public void setEMAIL(String eMAIL) {
        EMAIL = eMAIL;
    }
    public String getNAME() {
        return NAME;
    }
    public void setMAME(String nAME) {
       NAME = nAME;
    }
    public String getPASSWORD() {
        return PASSWORD;
    }
    public void setPASSWORD(String pASSWORD) {
        PASSWORD = pASSWORD;
    }
    public int getGRADE() {
        return GRADE;
    }
    public void setGRADE(int gRADE) {
        GRADE = gRADE;
    }
    public Date getREGDATE() {
        return REGDATE;
    }
    public void setREGDATE(Date rEGDATE) {
        REGDATE = rEGDATE;
    }
 
}
 
cs

- 7~13행 : DB 필드들을 선언한다.

- 15~26행 : Getter/Setter를 만든다. Alt+Shift+S를 눌러서 Generate Getters and Setters로 쉽게 만들 수 있다.

VO는 Value Object의 약자로써 read only 속성을 가진다.  DB의 필드들을 Getter/Setter 메소드 조합으로 형성된 클래스이다. DB의 정보를 담고 보내주는 역할을 한다. DTO라는 이름으로 사용하는 사람들도 있다. VO와 DTO는 거의 비슷한, 똑같은 개념이긴 하다. 두 개를 구분하라면 구분할 순 있지만, 비슷한 개념이다. 불변성이냐 아니냐의 차이이다. VO나 DTO를 쓰지않고 Map만 사용하는 사람들도 있다. 편한 걸로 쓰면 된다. 

 

2-3. UserService.java 생성

1
2
3
4
5
package tody.lovely.service;
 
public interface UserService {
 
}
cs

- 3행 : 클래스가 아닌 인터페이스다.

Service는 비즈니스 로직의 수행을 위한 메서드들을 정의한다.

 

2-4. UserServiceImp.java 생성

1
2
3
4
5
6
7
8
package tody.lovely.service;
 
import org.springframework.stereotype.Service;
 
@Service("userService")
public class UserServiceImpl implements UserService {
 
}
cs

- 5행 : @Service 어노테이션으로 Service 객체임을 선언한다.

Service를 implements받는다. Service 인터페이스를 통해 정의된 메서드들을 구현하는 클래스이다.

 

2-5. UserDAO.java 생성

1
2
3
4
5
6
7
8
9
10
package tody.lovely.dao;
 
import org.springframework.stereotype.Repository;
 
import tody.lovely.common.dao.AbstractDAO;
 
@Repository("userDAO")
public class UserDAO extends AbstractDAO {
 
}
cs

- 7행 : @Repository 어노테이션을 통해 DAO임을 선언한다.

- 8행: AbstarctDAO를 상속받았다. (MyBatis를 연동할 때 생성했다)

 

2-6. user_sql.xml 생성

1
2
3
4
5
<?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="user">
 
</mapper>
cs

- 3행 : mapper는 한개가 아니라 여러개가 있기 때문에 namespace로 user이라는 이름을 지정한다.

 

 

3) index.jsp

1
2
3
<li>
    <a href="/register/step1"><i class="fa fa-sign-in"></i>회원가입</a>
</li>
cs

- 2행 : 요청 URL을 설정한다. 회원가입을 누르면 controller의 "/register/step1"를 찾아 갈 것이다.

 

 

4) UserController.java ("/register/step1")

1
2
3
4
    @RequestMapping("/register/step1")
    public String step1() throws Exception {
        return "/user/register/step1";
    }
cs

- 1행 : @RequestMapping 어노테이션은 요청 URL을 매핑하여 해당 메서드를 실행한다.

- 3행 : 보여줄 화면의 jsp경로이다. ModelAndView를 사용하기도 하고, String으로 경로주소를 바로 적는 경우도 있다.

 

 

5) step1.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div class="panel-body">
    <div class="row">
        <div class="col-lg-12">
            <h4>이용약관</h4>
            <div class="panel-body" style="border: 1px solid #ccc">
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum tincidunt est vitae ultrices accumsan. Aliquam ornare lacus adipiscing, posuere lectus et, fringilla augue. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
        </div>
        </br>
        <h4>개인정보</h4>
        <div class="panel-body" style="border: 1px solid #ccc">
            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum tincidunt est vitae ultrices accumsan. Aliquam ornare lacus adipiscing, posuere lectus et, fringilla augue. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
        </div>
        <br/>
        <form role="form" action="/register/step2">
            <div class="form-group">
                <label class="checkbox-inline">
                    <input type="checkbox" name="agree" value="true">동의합니다.
                </label>
            </div>
            <button type="submit" class="btn btn-default">다음 단계</button>
        </form>
    </div>
</div>
cs

- 14행 : form태그이다. action으로 Controller에 요청 URL "/register/step2"를 보낸다.

- 17행 : input태그로 타입을 checkbox로 만든다. 체크를 하면 다음 단계로 넘어가게 한 것이다. 

- 20행 : button을 누르면 다음 단계로 넘어가는데, 체크가 되어있지 않으면 넘어가지 않게 할 것이다.

위 3개의 행 말고는 개인적으로 이쁘게 css 작업한거라서 보지 않아도 된다.

 

 

6) 확인

 

서버를 실행시켜 보자. 아래는 index.jsp이다. css입힌거다(ㅎㅎ)

 

 

회원가입 버튼을 누르게 되면 step1.jsp가 나오면 된다.

나왔다! 성공!

 

 

7) UserController.java (/register/step2)

1
2
3
4
5
6
7
8
9
10
    @RequestMapping("/register/step2")
    public ModelAndView step2(@RequestParam(value="agree", defaultValue="false") Boolean agree) throws Exception {
        if(!agree) {
            ModelAndView mv = new ModelAndView("user/register/step1");
            return mv;
        }
        ModelAndView mv = new ModelAndView("user/register/step2");
        mv.addObject("registerRequest"new RegisterRequest());
        return mv;
    }
cs

- 1행 : @RequestMapping 어노테이션을 선언한다.

- 2행 : step1.jsp에서 17행의 input태그의 이름이 agree이다. form태그로 타고 온 파라미터값이다. 디폴트값이 false다.

- 3~6행 : agree의 checkbox가 true가 아니면(체크하지 않았으면) 다시 step1.jsp로 돌려보낸다.

- 7행 : 4번 Controller에서는 String으로 경로주소를 썼지만 여기는 ModelAndView로 화면에 보여줄 jsp를 선언한다.

- 8행 : ModelAndView객체에 정보를 담아 jsp로 넘겨준다. 아직 RegisterRequest가 없어서 에러가 뜰 것이다. 만들자.

 

 

8) RegisterRequest.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
package tody.lovely.util;
 
public class RegisterRequest {
    
    private String email;
    private String id;
    private String name;
    private String pw;
    private String checkPw;
    
    //비밀번호 확인
    public boolean isPwEqualToCheckPw() {
        return pw.equals(checkPw);
    }
 
    public String getEmail() {
        return email;
    }
 
    public void setEmail(String email) {
        this.email = email;
    }
 
    public String getId() {
        return id;
    }
 
    public void setId(String id) {
        this.id = id;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public String getPw() {
        return pw;
    }
 
    public void setPw(String pw) {
        this.pw = pw;
    }
 
    public String getCheckPw() {
        return checkPw;
    }
 
    public void setCheckPw(String checkPw) {
        this.checkPw = checkPw;
    }
 
}
cs

회원가입을 하는데 필요한 Command객체들을 담는다. VO와 마찬가지이다.

- 12~14행 : 비밀번호와 비밀번호 확인이 일치하는 지 검사하는 메소드이다.

 

 

9) step2.jsp

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
<div class="panel-body">
    <div class="row">
        <div class="col-lg-6">
            <form:form role="form" commandName="registerRequest" action="/register/step3" method="post">
                <div class="form-group input-group">
                    <span class="input-group-addon"><i class="fa fa-check"></i></span>
                    <form:input type="text" class="form-control" placeholder="ID" path="id"/>
                    <form:errors path="id"/>
                </div>
                <div class="form-group input-group">
                    <span class="input-group-addon"><i class="fa fa-envelope"></i></span>
                    <form:input type="email" class="form-control" placeholder="Email" path="email"/>
                    
                </div>
                <div class="form-group input-group">
                    <span class="input-group-addon"><i class="fa fa-user"></i></span>
                    <form:input type="text" class="form-control" placeholder="Name" path="name"/>
                    <form:errors path="name"/>
                </div>
                <div class="form-group input-group">
                    <span class="input-group-addon"><i class="fa fa-lock"></i></span>
                    <form:password class="form-control" placeholder="Password" path="pw"/>
                    <form:errors path="pw"/>
                </div>
                <div class="form-group input-group">
                    <span class="input-group-addon"><i class="fa fa-lock"></i></span>
                    <form:password class="form-control" placeholder="Password Check" path="checkPw"/>
                    <form:errors path="checkPw"/>
                </div>
                <button type="submit" class="btn btn-default">가입하기</button>
                <button type="reset" class="btn btn-default">취소하기</button>
            </form:form>
        </div>
    </div>
</div>
cs

- 4행 : form태그에 정보들이 담길 Command 객체 이름이다. method="post"를 하게 되면 파리미터값들이 주소에 보이지 않게 된다.

- 7행 : path는 RegisterRequest.java에 있는 프로퍼티와 연결된다. 두 개의 이름이 같아야 한다.

- 8행 : errors의 내용을 가져온다. path로 프로퍼티를 지정한다.

**** input 타입에 email을 해줬다면, 유효성 검사에 email을 넣을 필요 없다!

 

 

10) 확인

 

10-1. 동의하지 않고 다음단계를 누른다.

주소 : https://localhost:8080/register/step1

 

주소 : https://localhost:8080/register/step2?

step2.jsp로 넘어가지 않고, step1.jsp화면에 머물러 있는 것을 확인한다.

 

10-2. 동의하고 다음단계를 누른다.

주소 : https://localhost:8080/register/step1

 

주소 : https://localhost:8080/register/step2?agree=true

step2.jsp로 넘어와졌다. 성공!

지금 주소에 agree의 값이 적혀있는데, 보이기 싫으면 form태그에 mehtod="post"를 하면 된다. 고치러가야지.

 

 

11) RegisterRequestValidator.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
package tody.lovely.util;
 
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
 
public class RegisterRequestValidator implements Validator{
    
    private static final String emailRegExp =
            "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@" +
            "[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";
    private Pattern pattern;
 
    public RegisterRequestValidator() {
        pattern = Pattern.compile(emailRegExp);
    }
 
    @Override
    public boolean supports(Class<?> clazz) {
        return RegisterRequest.class.isAssignableFrom(clazz);
    }
 
    @Override
    public void validate(Object target, Errors errors) {
        RegisterRequest regReq = (RegisterRequest) target;
        
        if(regReq.getEmail() == null || regReq.getEmail().trim().isEmpty()) {
            errors.rejectValue("email""required""필수 정보 입니다.");
        } else {
            Matcher matcher = pattern.matcher(regReq.getEmail());
            if(!matcher.matches()) {
                errors.rejectValue("email""bad""올바르지 않는 형식입니다.");
            }
        }
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name""required""필수 정보 입니다.");
        ValidationUtils.rejectIfEmpty(errors, "pw""required""필수 정보 입니다.");
        ValidationUtils.rejectIfEmpty(errors, "checkPw""required""필수 정보 입니다.");
        if(!regReq.getPw().isEmpty()) {
            if(!regReq.isPwEqualToCheckPw()) {
                errors.rejectValue("checkPw""nomatch""비밀번호가 일치하지 않습니다.");
            }
        }
    }
 
}
cs

step2.jsp에서 작성한 것들의 유효성을 검사하는 클래스이다. 

- 12행 : 이메일의 정규표현이다.

- 28행 : 가져온 객체를 RegisterRequest에 담는다.

- 30~31행 : 이메일을 입력하지 않으면 에러를 보낸다.

- 33~35행 : 이메일 정규패턴을 확인하고 맞지않으면 에러를 보낸다.  *** input 타입을 email로 했으면 필요 없음!

- 38~40행 : 입력되지 않으면 에러를 보낸다.

- 41~45행 : 비밀번호가 입력되지 않고, 비밀번호와 비밀번호 확인이 맞지않으면 에러를 보낸다.

 

- errors.rejectValue(String field, String errorCode, String defaultMessage)

필드에 대한 에러코드를 추가, 에러코드에 대한 메시지가 존재하지 않을 경우 defaultMessage를 사용한다.

- rejectIfEmpty(Errors errors, String field, String erreCode, String defaultMessage)

값이 공백일 경우, 지정된 errorCode를 가지는 지정된 field가 없으면 defaultMessage를 사용한다.

- rejectIfEmptyOrWhitespace(Errors errors, String field, String erreCode, String defaultMessage)

값이 공백이거나 공백을 포함하고 있는 경우, 지정된 errorCode를 가지는 지정된 field가 없으면 defaultMessage를 사용한다.

다른 메소드도 보려면 여기를 클릭하세요.

 

 

12) UserController.java (/register/step3)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    @Resource(name="userService")
    private UserService userSer;
 
    @RequestMapping("/register/step3")
    public ModelAndView step3(RegisterRequest regReq, Errors errors) throws Exception{
        new RegisterRequestValidator().validate(regReq, errors);
        if(errors.hasErrors()) {
            ModelAndView mv = new ModelAndView("user/register/step2");
            return mv;
        }
        try {
            userSer.register(regReq);
        } catch (AlreadyExistingEmailException e) {
            errors.rejectValue("email""duplicate""이미 가입된 이메일입니다.");
            ModelAndView mv = new ModelAndView("user/register/step2");
            return mv;
        } catch (AlreadyExistingIdException e) {
            errors.rejectValue("id""duplicate""이미 가입된 아이디입니다.");
            ModelAndView mv = new ModelAndView("user/register/step2");
            return mv;
        }
        ModelAndView mv = new ModelAndView("user/register/step3");
        return mv;
    }
cs

- 1행 : Service 영역을 접근하기 위한 선언이다. @Resource 어노테이션으로 빈(Bean)을 수동으로 등록한다.

- 5행 : 커맨드 객체와 연결된 Errors객체를 생성하여 파라미터로 전달한다.커맨드 객체를 전달 받지 않아도 Errors 객체를 이용해서 지정한 값을 구할 수 있다. Errors객체는 getFieldValue() 메서드를 제공하여 커맨드 객체의 특정 프로퍼티 값을 가져온다. Errors타입의 파라미터는 반드시 커맨드 객체를 위한 파라미터(RegisterRequest) 다음에 위치해야 한다.

- 6행 : RegisterRequestValidator()를 호출한다. RegisterRequest 커맨트 객체값의 유효성을 검사하고 결과를 Errors객체에 담는다.

- 7~8행 : 결과를 담은 errors가 error를 가지면 errors의 내용과 함께 step2.jsp로 간다.

- 12행 : 회원 등록을 위한 비지니스 로직을 수행한다.

- 13~20행 : 예외처리를 해준 것이다.

 

 

17) UserService.java

1
2
    void register(RegisterRequest regReq) throws Exception;
 
cs

 

 

18) UserServiceImpl.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    @Resource(name="userDAO")
    private UserDAO userDAO;
 
    @Override
    public void register(RegisterRequest regReq) throws Exception {
        UserVO email = userDAO.selectByEmail(regReq.getEmail());
        if(email!=null) {
            throw new AlreadyExistingEmailException(regReq.getEmail()+" is duplicate email.");
        }
        UserVO id = userDAO.selectById(regReq.getId());
        if(id!=null) {
            throw new AlreadyExistingIdException(regReq.getId()+" is duplicate id.");
        }
        userDAO.insertUser(regReq);
    }
cs

- 1행 : @Resource 어노테이션으로 수동으로 빈(Bean)을 등록한다.

- 6~9행 : email이 존재하는 지 확인한다. 있으면 AlreadyExistingEmailException으로 던진다.

- 10~13행 : id가 존재하는 지 확인한다. 있으면 AleradyExistingIdException으로 던진다.

 

18-1. 예외처리

AlreadyExistingEmailException.java

1
2
3
4
5
6
7
package tody.lovely.exception;
 
public class AlreadyExistingEmailException extends RuntimeException{
    public AlreadyExistingEmailException(String message) {
        super(message);
    }
}
cs

AlreadyExistingIdException.java

1
2
3
4
5
6
7
package tody.lovely.exception;
 
public class AlreadyExistingIdException extends RuntimeException{
    public AlreadyExistingEmailException(String message) {
        super(message);
    }
}
cs

회원을 등록하는 과정에서 생기는 예외처리이다. 그래서 RuntimeException을 상속받았다.

 

 

19) UserDAO.java

1
2
3
4
5
6
7
8
9
10
11
    public UserVO selectByEmail(String email) {
        return (UserVO)selectOne("user.selectByEmail", email);
    }
 
    public UserVO selectById(String id) {
        return (UserVO)selectOne("user.selectById", id);
    }
 
    public void insertUser(RegisterRequest regReq) {
        insert("user.register", regReq);
    }
cs

실제 데이터베이스에 접근하는 클래스이다. AbstarctDAO를 상속받아서 AbstarctDAO 메소드를 사용한다. return할때 "user.XXXX"를 볼 수 있는데,  user는 mapper의 namespace를 뜻하고, 뒤에는 쿼리의 이름을 뜻한다. 이렇게 선언하면 수많은 mapper중에서 찾아간다.

- 1행 : email을 찾는다.

- 5행 : id를 찾는다.

- 9행 : 회원을 등록한다.

 

 

20) user_sql.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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<?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="user">
 
    <select id="selectByEmail" resultType="tody.lovely.vo.UserVO">
        <![CDATA[
          SELECT
              EMAIL,
              NAME
          FROM
              USER
          WHERE
              EMAIL = #{email}       
      ]]>
    </select>
    
    <select id="selectById" resultType="tody.lovely.vo.UserVO">
        <![CDATA[
          SELECT
              ID,
              NAME
          FROM
              USER
          WHERE
              ID = #{id}       
      ]]>
    </select>
    
    <insert id="register">
        <![CDATA[
            INSERT INTO USER
            (
                EMAIL,
                ID,
                NAME,
                PASSWORD,
                REGDATE
            )
            VALUES
            (
                #{email},
                #{id},
                #{name},
                #{pw},
                SYSDATE()
            )
        ]]>
    </insert>
 
</mapper>
cs

- 3행 : namespace가 user이다. 아까 DAO에서 "user.XXXX"에서 본 user이다. 그리고 각각의 쿼리의 이름에 찾아간다.

- 5행 : email을 찾는다. <select>태그를 이용해 명시한다. resultType은 쿼리의 값이 UserVO에 담겨서 반환된다는 뜻이다.

- 17행 : id를 찾는다. <select>태그를 이용해 명시한다. resultType은 쿼리의 값이 UserVO에 담겨서 반환된다는 뜻이다.

- 29행 : 회원을 등록한다. <insert>태그를 이용해 명시한다. 

 

***** VALUES의 #{}값을 쓸때 대문자, 소문자에 유의해야 한다. jsp에서 path와 여기의 #{}가 같아야한다. 대소문자까지도.

** <![CDATA[]>는 단순히 로그를 이쁘게 찍히게 하려고 만든것이다. 없어도 상관 없다. 그리고 길게 쭉 적어도 상관없다.

 

 

21) step3.jsp

1
2
3
    <div class="alert alert-success">
        축하드립니다, 성공적으로 회원가입이 되었습니다!
    </div>
cs

 

 

22) 확인

**** CSS 적용한 거라서 다를 수 있음 주의!

 

22-1. 아이디나 비밀번호 입력이 빠질 경우

 

 

22-2. 이메일 형식이 맞지 않을 경우(input타입 email)

 

기본적으로 제공해주는 기능이다. input type="email"이면 email형식인지 확인해준다.

 

22-3. 이메일 형식이 맞지 않을 경우(email 유효성 검사)

만약 그냥 input type="text"로 했을 경우, validator를 통해 유효성검사를 해줘야 한다.

 

22-4. 비밀번호 확인이 틀릴 경우

 

22-5. 가입된 이메일이나 아이디가 있는 경우

 

22-6. 가입이 성공한 경우

짠! 최종 회원가입 성공!!!!!

 

 

23) DB 확인

 

 

IDX 2에 들어가있는것을 확인 할 수 있다!

 

 

끝나따 끝나버려따!!! ٩(ˊᗜˋ*)و

이제 로그인 로그아웃 ... (먼산)


댓글을 달아 주세요

  1. 스니 2018.10.02 15:05

    회원가입 유효성 검사까지 따라왔는데 가입하기를 누르면 500에러가 발생합니다. 다른 곳에 문의를 해 봤을 때 DB연동의 문제라고 하는데 구글링해서 나와있는 글들을 보고 해도 몇 일 째 에러가 안없어져서요.. 소스 그대로 붙이고 패키지명만 수정한 거라 어떤 것을 수정해야 하는 지 알려주실 수 있나요? ㅎㅎ

    • BlogIcon #에게 2018.10.02 15:20 신고

      에러로그를 잘 살펴보면 어디가 문제인지 나와있을 거에요. 뭐가 빠졌다던지, 어디서 null이 발생한다던지... 500에러여도 다 달라서 제가 에러로그를 보지 않는이상 답변해드리기 힘들어요ㅠㅠ 그리고 여기 글에는 DB연동부분은 안건들이기때문에 소스를 그대로 붙여넣으면 연동부분에서 에러는 날 일이 없을 거 같아요ㅠㅠ 스니님이 DB연동하신 부분을 다시 검토해보셔야 할 거 같아요.
      https://to-dy.tistory.com/27?category=700248 제 글 중에 DB연동 글이 있는데 이부분 참고하셔서 보시면 될거에요. 회원가입부터 로그인, 로그아웃까지 글들은 하나의 프로젝트로 다 한 거라서 비교해보시면 해결법이 나와있지 않을까 싶어요.

  2. 스니 2018.10.02 15:51

    (❁´▽`❁) #에게 님 블로그 글을 보면서 만든 거라서요..
    Exception

    org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException:
    ### Error querying database. Cause: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for user.selectByEmail
    ### Cause: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for user.selectByEmail
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:894)
    org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:789)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:648)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
    org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    Root Cause

    org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException:
    ### Error querying database. Cause: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for user.selectByEmail
    ### Cause: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for user.selectByEmail
    org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:75)
    org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:371)
    com.sun.proxy.$Proxy4.selectOne(Unknown Source)
    org.mybatis.spring.SqlSessionTemplate.selectOne(SqlSessionTemplate.java:163)
    zest.web.common.dao.AbstractDAO.selectOne(AbstractDAO.java:45)
    zest.web.dao.UserDAO.selectByEmail(UserDAO.java:17)
    zest.web.service.UserServiceImpl.register(UserServiceImpl.java:21)
    zest.web.controller.UserController.step3(UserController.java:47)
    sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    java.lang.reflect.Method.invoke(Method.java:498)
    org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:213)
    org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:126)
    org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:96)
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:617)
    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:578)
    org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
    org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:923)
    org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:852)
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
    org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:789)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:648)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
    org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)

    초보자라 하나씩 따라하면서 만들었는데 에러가 떠서 고생하고 있네요 ㅠㅠ
    이게 그 에러로그인데 괜찮으시면 확인 해 주시겠어요? ..

    • BlogIcon #에게 2018.10.02 16:18 신고

      지금 user.selectByEmail을 못 찾고 있는데..
      오타일 가능성이 매우 커요.. dao에서 쓴 쿼리 id랑 mapper 파일(xml)에서 쓴 쿼리 이름이랑 같은 지 확인해보시겠어요??

  3. 스니 2018.10.02 16:36

    네 DAO랑 user_sql.xml 확인했는데 오타는 없었어요.. (❁´▽`❁) #에게 님 코드로도 확인했구요.. 구글링중에 mysql 8 버전이 스프링이랑 못붙어서 db 접속이 안된다는 글을 보고 지금 mysql 버전 낮춰서 다운 받고 있어요 혹시 mysql 8 쓰셨나요??

    • BlogIcon #에게 2018.10.02 16:44 신고

      헐!! 네 저 프로젝트는 mysql 5.7을 사용했어요!! 버전때문이라면 대박... 어이 없는... 버전 낮추고 꼭 되었으면 좋겠네요ㅠㅠ!!

    • 스니님 2019.05.17 16:46

      같은 에러인데 어떻게 해결하셨나요?

  4. 초보.. 2019.03.18 19:15

    안녕하세요 따라하면서 질문을 올립니다.
    저는 mapper 규칙을 /mapper/**/*Mapper.xml로 했고
    파일은 UserMapper.xml로 해서 DAO 주소를 'tbUser.listUser'로 지정했습니다.
    디비에 내용을 일부 넣고 테스트 하려니 500에러가 뜨네요

    <?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">심각: Servlet.service() for servlet [appServlet] in context with path [] threw exception [Request processing failed; nested exception is org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException:
    ### Error querying database. Cause: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for tbUser.listUser
    ### Cause: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for tbUser.listUser] with root cause
    java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for tbUser.listUser
    이렇게 뜨네요. 오타나 그런거를 해봤지만 전혀 없었고요. 주소 중복은 저거 1개만 했기 때문에 전혀 이상없어 보이고요.. 뭐가 문제일까요?
    <mapper namespace="tbUser">

    <select id="listUser" resultType="java.util.Map">
    SELECT ID,PASSWORD
    FROM
    TB_USER
    </select>
    </mapper>

    • BlogIcon #에게 2019.03.19 17:35 신고

      헐 이제 봤네요!!! 죄송합니다ㅠㅠ 오타 문제가 전혀 아니라면.. 파일 UserMapper.xml 의 위치가 mapper 폴더 안에 있지 않거나 선언한 부분과 전혀 다른 곳에 있을 거 같아요.. 선언한 부분이랑 위치를 다시 확인해보시겠어요?

    • 초보 2019.03.19 18:40

      경로는 /mapper/user/UserMapper.xml로 설정했습니다.
      /mapper/**/*Mapper.xml 이거랑 경로 규칙 설정에는 이상이 없는거죠??

    • 초보 2019.03.19 18:55

      톰켓을껏다 켰는데..

      심각: Servlet.service() for servlet [appServlet] in context with path [] threw exception [Request processing failed; nested exception is org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException:
      ### Error querying database. Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Could not create connection to database server.

      하면서 커넥션 에러도 나네요;;

    • BlogIcon #에게 2019.03.19 19:02 신고

      위의 분처럼 mysql 버전이 달라서 그런걸까요....? mapper 부분에서 500에러는 오타문제 말고는 딱히 아는 부분이 없어서 걱정이네요ㅠㅠ 아니면 classpath*:/mapper/.. 처럼 * 을 붙여보시겠어요? 흠...

    • 초보 2019.03.19 19:09

      경로에 * 추가했는데도 안됩니다.. 일단 구글링 더해보고 정 안되겠다 싶으면 다시 여쭤보겠습니다.. 감사합니다!

    • BlogIcon #에게 2019.03.19 19:09 신고

      네 경로 설정엔 문제가 없는데 context 설정이 문제인거 같아요...! datasource나 mapper 쪽을 다시 봐야할 거 같아요

    • 초보 2019.03.19 20:03

      SqlSessionFactoryBean 클래스의
      mapperLocations의 매개변수에 값이 업성서 경로를 고치면서 하니까 위에 댓글에 올렸던 에러는 없어졌는데 다른 에러가 생겼네요 ㅎㅎ 신경써주셔서 감사합니다.

    • 초보 2019.03.19 20:09

      내용에 있는 maven 버전이랑 mysql 버전의 차이때문에 에러가 추가적으로 있었는데. maven 버전 수정해서 다시 업뎃 하니까 잘됩니다. 감사합니다

    • BlogIcon #에게 2019.03.19 21:59 신고

      와! 해결하셔서 다행이네요! maven과 mysql버전 차이의 문제도 있다니...! 좋은 정보 감사드립니다!

  5. chris 2019.03.27 17:00

    spring 카테고리 내용 잘 보고 있습니다.
    css 파일은 따로 들고 계신가요 아니면 프레임워크 등을 이용하신건가요..~?

  6. BlogIcon 쥬니준 2019.04.02 15:38 신고

    궁금한게 있는데욤
    @RequestMapping("register/step1";)
    이렇게 되어 있는데 register는 views 경로 안에 register라는 폴더를 의미하는거죵? 그리고 return "/user/register/step1"에서 user는 user폴더를 의미하는거구용??? 맞나요?

    • BlogIcon #에게 2019.04.09 11:17 신고

      음 간단히 말씀 드리자면, RequestMapping은 주소를(주소창에 보여지는 주소?) 말하는 것이고, return은 web app 폴더 경로를 말한는 거에요. 답이 늦어서 죄송합니다 ㅠ

  7. 오르막내리막 2019.04.09 15:20

    step2 에서
    <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
    taglib 써야지 form:input 이 문법 쓰는게 가능한걸로 아는데...
    태그라이브러리 붙이면 500 에러 뜹니다;

    • BlogIcon #에게 2019.04.09 15:27 신고

      해당 태그가 있어야 하는게 맞아요 jar 파일이 없거나 버전 문제일 듯 싶어요. 혹시 refresh 나 clean 해보시겠어요? 아파치 껐다 키면 될 수도 있어요..

  8. BlogIcon 쥬니준 2019.04.09 15:49 신고

    3시간째 계속 500 에러ㅠㅠㅠㅠㅠㅠㅠ
    https://to-dy.tistory.com/31?category=702830
    선생님 게시글에 저랑 똑같은 에러 관련된 게시물 있었서 봤는데; 아직 이해가 부족해서 엄청 좌절하고 있었는데요....
    저는

    Neither BindingResult nor plain target object for bean name 'registerRequest' available as request attribute

    이렇게 떴었는데;
    뭐때문인지 모르겠지만 결국 됩니다ㅠㅠ...
    선생님 혹시 이 프로젝트에 관한게 git에도 업로드 되어있나요ㅠㅠ??
    Hibernate 이용한 회원가입하면 다른 부분을 어떻게 손봐야할지 몰라서 참고가 가능한지 궁금합니다ㅠ

    • BlogIcon #에게 2019.04.09 15:58 신고

      네ㅠㅠ git엔 따로 없어요..Hibernate 이용한 회원가입 부분에 있는 것만 수정하면 됩니당. 다른 부분 손볼 곳이 없어요! RegisterRequestValidator 클래스가 없어지고 RegisterRequest 클래스에서 검증을 하는 거에요. 그래서 Controller도 검증하는 부분만 수정한 거구요!

  9. BlogIcon 쥬니준 2019.04.09 19:33 신고

    저도 윗분들 처럼 500 에러 중
    Mapped Statements collection does not contain value for user.selectByEmail

    selectByEmail 에러가 계속 나는데요...
    버젼 바꾼다는건 mysql 워크벤치까지 삭제하고 5.7로 다운받아서 해야하는건가요??

  10. BlogIcon 쥬니준 2019.04.09 19:52 신고

    ㅠㅠ...제가 그럼 Mysql 8.0 이랑 워크벤치 사용 중인데 프로그램 자체를 굳이 삭제할 필요는 없는건가요???

  11. BlogIcon 쥬니준 2019.04.10 16:15 신고

    저는 윗분들과 같은 에러인데 pom.xml에서 mysql, maven 버전을 낮춰도 보고 mysql서버 및 워크벤치도 다 삭제하고 5.7버전으로 설치도 해보고....스팰링이나 기타등등의 문제점을 다 찾아봤지만 되지 않아서 포기했습니다ㅠㅠ...흑흑...

    • BlogIcon #에게 2019.04.10 16:19 신고

      앗! 제가 오늘 프로젝트 생성해서 validator 회원가입 만들어봤거든요! 비교해 보시겠어요? https://github.com/todyDev/spring-sign-up-and-in 깃 주소입니당!!

    • BlogIcon 쥬니준 2019.04.10 21:06 신고

      와 감사합니다ㅠㅠ...
      드디어 해결 됐습니다...
      원인은 context-mapper.xml 에서 property 에 경로 부분인 classpath:/mapper/**/*_sql.xml 이 부분이였네요
      sql 파일을 mapper폴더에 넣지를 않았었네요ㅠㅠ감사합니다!!!

    • BlogIcon #에게 2019.04.10 21:08 신고

      와!! 다행이네여!!! 굿굿!! 열공하세용

  12. 코머 2019.04.16 16:49

    <form:errors path="email"/> 요거 빠진건가요 없어도 되는건가요 저는 이거없으니 작동이 잘안되네요

  13. ._. 2019.07.03 11:45

    오류가 뜨지않는데 회원가입하고 가입하기 버튼을 누르면 값이 안넘어가고 reset처럼 되는데 왜그럴까요.. 코드문제일까요?ㅠㅠ

    • BlogIcon #에게 2019.07.03 11:51 신고

      컨트롤러에서의 코드 문제로 보여요. 값이 넘어오지 않는 것과, 화면 이동이 일어나지 않는 것으로 보면요....

  14. 초보입니다 2019.09.05 17:22

    BCryptPasswordEncoder를 사용해서 회원가입시에 비밀번호 암호화를 해보려고 하는데요...

    step2 컨트롤러 부분중
    ModelAndView mv = new ModelAndView("/common/register/step2";);
    mv.addObject("registerRequest", new RegisterRequest());
    return mv;

    이 부분에서 하는것으로 생각해서 작성해보았는데 저의 능력 부족으로 인해 ㅠㅠ
    자꾸 안되고있는데 해당 부분에서 작업을 요청하는것이 맞나요?

    그리고 ModelAndView에서 작업을 한다면 그냥 model 방식으로 vo값을 가져와하는것과 차이가 있을까요?

    • BlogIcon #에게 2019.09.05 21:16 신고

      비밀번호를 가져와서 암호화해서 db로 보내야 하기 때문에 step3에서 이루어져야 할 거 같아요.

      registerRequest 는 값 검증을 위해 사용하는 것이기 때문에, vo에서 값 검증을 하겠다고 하면 사용해도 괜찮지 않을까 생각듭니당

  15. 초보자 2020.01.01 19:00

    전체적인 코드정리는 되어있으나 경로가 불확실하게 나와있어서 진행이 되지않습니다 위에 패키지이름보고 유추한 것을 제외하고는 잘모르겠는데... 혹시 전체적인 경로같은거좀 상세하게 정리해주실수있나요..?

    • BlogIcon #에게 2020.01.01 20:03 신고

      안녕하세요! 2)파일 패키지 및 파일 생성 부분에 1열들을 보면 패키지 경로들이 있습니다! 그래도 혹시나 싶어서 깃 주소 남겨요! validator 폴더에 들어가면 코드를 볼 수 있을 거에요! 감사합니다! https://github.com/todyDev/spring-sign-up-and-in

  16. 초보자 2020.01.03 11:15

    UserVO.java 에 코드중에서 MAME이 있는데 NAME으로 고쳐서 써야되나요???

  17. 개발자를꿈꾸는퍼블리셔 2020.07.17 02:41

    2020-07-17 공부!
    감사합니다 덕분에 공부가 아주 잘 되어 가고있습니다! 감사합니다!

  18. 글로리 2020.10.12 15:09

    css 적용은 어떻게 하는건가요?

  19. S-V 2021.11.08 16:25

    안녕하세요 스프링부트로 프로젝트진행중에 글 잘 봤습니다 ~
    DB에 정보를 넘길때는 RegisterRequest객체를 사용하고, DB값을 불러올때는 userVO를 사용하는 것 맞나요?
    맞다면 userVO 하나로 DB에 정보 넘기는 것과 DB값 불러오는 것을 전부 수행하지 않고 두개로 나눈 이유가 있을까요??

    • BlogIcon #에게 2021.12.07 10:02 신고

      같이 쓰셔도 상관없습니다. vo의 경우 단순하게 값을 가져오는 것이고, registerRequest 객체에서는 비밀번호를 추가로 체크하는 로직을 넣은 차이 밖에 없습니다.