아이디와 비번 찾기를 만들어보자.
다만, 사용자가 아이디 찾기 혹은 비번 찾기를 시도한 계정을 로그인하기 위한 화면으로 바로 보내주고 싶다. 물론 아이디 입력란에 내가 찾은 아이디도 바로 입력해주고 싶다.
Motivation 에서 유저의 필드에 사용 할 만한 정보가... 이메일 정도밖에 없다.
아이디 찾기에서는 이메일을 입력받아 해당하는 유저를 찾아주자.
회원가입시에 이메일 인증을 진행했고, 이메일의 유니크함을 보장하고 있으니 괜찮을 것 같다.
비밀번호 찾기에서는 유저의 로그인 아이디와 이메일을 받아서 해당하는 유저를 찾고, 랜덤한 비밀번호를 생성해 해당 유저의 비밀번호를 바꿔주고 그 비밀번호를 이메일로 발송해주도록 했다.
이번 글에서 내가 다루고싶은 내용은 아이디찾기 비밀번호 찾기의 로직이 아니라 사용자가 찾기를 시도한 계정을 바로 로그인할 수 있게 이동시키는 과정이다.
@PreAuthorize("isAnonymous()")
@PostMapping("/findUsername")
public String findUsername(
@NotBlank @Length(min = 4) String email
) {
return memberService.findByEmail(email)
.map(member ->
rq.redirect(
"/usr/member/login?lastUsername=%s".formatted(member.getUsername()),
"해당 회원의 아이디는 `%s` 입니다.".formatted(member.getUsername())
)
)
.orElseGet(() -> rq.historyBack("`%s` (은)는 존재하지 않은 회원 이메일 입니다.".formatted(email)));
}
@PreAuthorize("isAnonymous()")
@PostMapping("/findPassword")
public String findPassword(
@NotBlank @Length(min = 4) String username,
@NotBlank @Length(min = 4) String email
) {
return memberService.findByUsernameAndEmail(username, email)
.map(member -> {
memberService.sendTempPasswordToEmail(member);
return rq.redirect(
"/usr/member/login?lastUsername=%s".formatted(member.getUsername()),
"해당 회원의 이메일로 임시 비밀번호를 발송하였습니다."
);
}).orElseGet(() -> rq.historyBack("일치하는 회원이 존재하지 않습니다."));
}
각각 아이디와 비밀번호 찾기 이다.
아이디와 비밀번호 찾기를 통해 해당하는 유저를 찾고, login 으로 redirect 시켜주겠다. 거기에 더해서 redirect url 에 찾기를 통해 찾은 유저의 로그인 아이디를 lastUsername 으로 추가해줬다.
<div class="form-control">
<label class="label">
<span class="label-text">아이디</span>
</label>
<input class="input input-bordered" maxlength="30"
name="username" placeholder="아이디" th:autofocus="${UtThy.inputAttributeAutofocus(param.lastUsername)}"
th:value="${UtThy.value(param.lastUsername)}" type="text">
</div>
<div class="form-control">
<label class="label">
<span class="label-text">비밀번호</span>
</label>
<input class="input input-bordered" maxlength="30"
name="password" placeholder="비밀번호" th:autofocus="${!UtThy.inputAttributeAutofocus(param.lastUsername)}" type="password">
</div>
로그인 템플릿의 코드 중 아이디와 비밀번호 입력란에 해당하는 코드이다.
Util Class 의 정적 메서드를 호출하여 파라미터로 lastUsername 이 존재하는지 확인했다.
파라미터로 lastUsername 이 존재한다면 아이디 입력란의 value 가 lastUsername 의 value 로 결정 될 것이고, autofocus 는 비밀번호 입력란으로 설정 될 것이다.
반대로 파라미터로 lastUsername 이 없다면 아이디 입력란이 autofocus 될 수 있도록 만들었다.(찾기를 거친게 아니라, 그냥 로그인하러 로그인 페이지로 올 때가 이에 해당한다.)
th:with="UtThy=${T(Motivation.standard.util.Ut.thy)}"
Thymeleaf 템플릿에서 Motivation.standard.util.Ut.thy 클래스의 정적 메서드를 호출하기 위한 코드다. 물론, Ut.thy 도 정적 클래스다.
private static String getFirstStrOrEmpty(List<String> requestParameterValues) {
return Optional.ofNullable(requestParameterValues)
.filter(values -> !values.isEmpty())
.map(values -> values.get(0).replace("%20", "").trim())
.orElse("");
}
public static boolean inputAttributeAutofocus(List<String> requestParameterValues) {
return !str.hasLength(getFirstStrOrEmpty(requestParameterValues));
}
public static boolean isBlank(List<String> requestParameterValues) {
return !hasText(requestParameterValues);
}
public static boolean hasText(List<String> requestParameterValues) {
return str.hasLength(getFirstStrOrEmpty(requestParameterValues));
}
public static String value(List<String> requestParameterValues) {
return getFirstStrOrEmpty(requestParameterValues);
}
파라미터가 있는지 판별하거나, 그 value 를 얻을 수 있는 정적 메서드들이 Ut.thy 에 정의되어 있다.
실제로 Motivation 을 실행해보자.
아이디도 잘 찾아지고
비밀번호도 잘 찾아지며
찾은 계정의 아이디가 입력 된 상태로 로그인 화면으로 잘 넘어간다.
이 방식이 보안상 올바르지 않을 수 있겠지만 사용자를 매우 편하게 해줄 것 같아 구현해보았다.
역시 난 친절하다!