3 분 소요

 인프런 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술편을 학습하고 정리한 내용 입니다.

회원 가입

도메인

hello.login.domain.member.Member

1
2
3
4
5
6
7
8
9
10
11
12
13
@Data  
public class Member {  
  
    private Long id;  
  
    @NotEmpty  
    private String loginId; // 사용자 입력 로그인 ID    
    @NotEmpty  
    private String name;    // 사용자 이름  
    @NotEmpty  
    private String password;  
  
}

id 는 pk용이고 loginId는 사용자가 입력한 로그인 id이다.

추가적으로 @NotEmpty로 Validator 사용했다.

리포지토리

hello.login.domain.member.MemberRepository

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
@Slf4j  
@Repository  
public class MemberRepository {  
  
    private static Map<Long, Member> store = new HashMap<>();  
    private static long sequence = 0L; // static  
  
    public Member save(Member member) {  
        member.setId(++sequence);  
        log.info("save : member={}", member);  
        store.put(member.getId(), member);  
        return member;  
    }  
  
    public Member findById(Long id) {  
        return store.get(id);  
    }  
  
    public Optional<Member> findByLoginId(String loginId) {  
        return findAll().stream()  
                .filter(m -> m.getLoginId().equals(loginId))  
                .findFirst();  
        /*  
        위와 같은 코드.  
        List<Member> all = findAll();        
        for (Member m : all) {            
	        if (m.getLoginId().equals(loginId)) {                
		        return Optional.of(m);            
		    }        
		}        
		return Optional.empty();        
		*/    
	}  
  
    public List<Member> findAll() {  
        return new ArrayList<>(store.values());  
    }  
  
    public void clearStore() {  
        store.clear();  
    }  
}

여기선 DB를 사용하지 않고, 메모리에서만 사용하기 때문에

1
2
private static Map<Long, Member> store = new HashMap<>();  
private static long sequence = 0L; // static  

이렇게 map 과 시퀀스로 쓸 데이터를 하나 만들어 줬다.

그리고 저장, id로 조회, 로그인 id로 조회, 전체 조회, 테스트때 사용할 초기화 메서드를 만들었다.

다른건 어렵지 않은데,

1
2
3
4
5
public Optional<Member> findByLoginId(String loginId) {  
        return findAll().stream()  
                .filter(m -> m.getLoginId().equals(loginId))  
                .findFirst();   
	}  

로그인 아이디로 멤버를 찾아내는 메서드이다.

스트림을 사용했고 혹시 없을 수도 있기 때문에 Optional로 감싸줬다.

1
2
3
4
5
6
7
8
9
public Optional<Member> findByLoginId(String loginId) {
	List<Member> all = findAll();  
	for (Member m : all) {  
	    if (m.getLoginId().equals(loginId)) {  
	        return Optional.of(m);  
	    }  
	}  
	return Optional.empty();
}

이 코드와 같은 의미 이다. Stream 문법을 사용 하니까 가독성이 많이 올라 갔다.

컨트롤러

hello.login.web.member.MemberController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Controller  
@RequiredArgsConstructor  
@RequestMapping("/members")  
public class MemberController {  
  
    private final MemberRepository memberRepository;  
  
  
    @GetMapping("/add")  
    public String addForm(@ModelAttribute("member") Member member) {  
        return "members/addMemberForm";  
    }  
  
    @PostMapping("/add")  
    public String save(@Validated @ModelAttribute("member") Member member, BindingResult bindingResult) {  
        if (bindingResult.hasErrors()) {  
            return "members/addMemberForm";  
        }  
  
        memberRepository.save(member);  
        return "redirect:/";  
    }  
}

폼을 띄워주고, 지난번에 배운 Validator를 사용해서 검증도 했다.

이제 뷰 템플릿 만들자.

뷰 템플릿

resources/templates/members/addMemberForm.html

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
<!DOCTYPE HTML>  
<html xmlns:th="http://www.thymeleaf.org">  
<head>  
    <meta charset="utf-8">  
    <link th:href="@{/css/bootstrap.min.css}"  
          href="../css/bootstrap.min.css" rel="stylesheet">  
    <style>  
        .container {  
            max-width: 560px;  
        }  
        .field-error {  
            border-color: #dc3545;  
            color: #dc3545;  
        }  
    </style>  
</head>  
<body>  
  
<div class="container">  
  
    <div class="py-5 text-center">  
        <h2>회원 가입</h2>  
    </div>  
  
    <h4 class="mb-3">회원 정보 입력</h4>  
  
    <form action="" th:action th:object="${member}" method="post">  
  
        <div th:if="${#fields.hasGlobalErrors()}">  
            <p class="field-error" th:each="err : ${#fields.globalErrors()}" th:text="${err}">전체 오류 메시지</p>  
        </div>  
  
        <div>  
            <label for="loginId">로그인 ID</label>  
            <input type="text" id="loginId" th:field="*{loginId}" class="form-control"  
                   th:errorclass="field-error">  
            <div class="field-error" th:errors="*{loginId}"></div>  
        </div>  
        <div>  
            <label for="password">비밀번호</label>  
            <input type="password" id="password" th:field="*{password}" class="form-control"  
                   th:errorclass="field-error">  
            <div class="field-error" th:errors="*{password}"></div>  
        </div>  
        <div>  
            <label for="name">이름</label>  
            <input type="text" id="name" th:field="*{name}" class="form-control"  
                   th:errorclass="field-error">  
            <div class="field-error" th:errors="*{name}"></div>  
        </div>  
  
  
        <hr class="my-4">  
  
        <div class="row">  
            <div class="col">  
                <button class="w-100 btn btn-primary btn-lg" type="submit">회원 가입</button>  
            </div>  
            <div class="col">  
                <button class="w-100 btn btn-secondary btn-lg" onclick="location.href='items.html'"  
                        th:onclick="|location.href='@{/}'|"  
                        type="button">취소</button>  
            </div>  
        </div>  
  
    </form>  
  
</div> <!-- /container -->  
</body>  
</html>

검증도 잘 된다.

지금 따로 처리가 없어서, 로그에서 가입이 된걸 볼 수 있다.

댓글남기기