로그인과 세션 스코프 빈
진행 중인 프로젝트를 테스트 해보던 중 문제를 발견했다.
문제 상황은 로그인을 한 번이라도 실패를 했을 때이다. 해결하고 싶은 문제 첫번째는 로그인 실패 후, 다른 페이지로 나갔다가 다시 로그인 페이지 진입 시에 로그인 실패 화면이 유지되는 것이고, 두번째는 로그인 실패 후 로그인에 성공하고, 로그아웃 후 다시 로그인 페이지로 진입하는 경우에 로그인 실패 화면이 유지되는 것이다.
위 두가지 상황 모두 사용자 입장에서 버그처럼(?) 느껴질 수 있는 상당히 어색한 부분이었다.
문제를 해결하기 위해 매핑되는 컨트롤러와 서비스 로직을 다시 한 번 살펴보았다.
(수정 전 컨트롤러)
@Controller
@RequestMapping("/user")
@RequiredArgsConstructor
public class SignInContorller {
private final UserService userService;
private final UserBean signInUserBean; //로그인 정보를 담은 session scope 객체
@GetMapping("/sign_in")
public String signIn(@ModelAttribute("tempSignInUserBean") UserBean tempsignInUserBean, Model model) {
return "user/sign_in";
}
@PostMapping("/sign_in")
public String signIn_pro(HttpServletRequest request, @Valid @ModelAttribute("tempSignInUserBean")
UserBean tempSignInUserBean, BindingResult result) {
if(result.hasErrors()) {
return "user/sign_in";
}
String[] inputEmail = tempSignInUserBean.getUser_email2().split("@");
tempSignInUserBean.setUser_email1(inputEmail[0]);
tempSignInUserBean.setUser_email2(inputEmail[1]);
userService.getSignInUserInfo(tempSignInUserBean);
HttpSession session = request.getSession();
session.setAttribute("tempSignInUserBean", tempSignInUserBean);
if(signInUserBean.isUserSignIn()) {//로그인 성공 시
return "redirect:/store";
} else {//로그인 실패 시 다시 로그인 페이지로
return "user/sign_in";
}
}
}
(수정 전 service layer)
@Service
@RequiredArgsConstructor
public class UserService {
private final UserDao userDao;
private final UserBean signInUserBean;
public void getSignInUserInfo(UserBean tempSignInUserBean) { //입력된 로그인 정보로 가입한 사용자가 있는지 조회
Optional<UserBean> tempSignInUserBean2 = userDao.getSignInUserInfo(tempSignInUserBean);
if(tempSignInUserBean2.isPresent()) { //가입된 사용자가 있다 = 로그인 성공
signInUserBean.setUser_idx(tempSignInUserBean2.get().getUser_idx());
signInUserBean.setUser_nickname(tempSignInUserBean2.get().getUser_nickname());
signInUserBean.setUserSignIn(true);
} else { //조회 결과가 없다 = 로그인 실패
signInUserBean.setUserSignInFail(true);
}
}
}
문제는 바로 로그인 실패 시에도, 세션 스코프 빈인 signInUserBean을 사용했기 때문이다. 위의 코드를 보면 로그인 실패 시에 signInUserBean에 로그인 실패 여부를 나타내는 변수인 userSignInFail에 true를 설정한다. 세션 스코프가 유지되는 동안 계속 유효하기 때문에 userSignInFail = true가 계속 유지된다. 그래서 앞서 언급한 문제가 발생했던 것이다.
(수정 후 컨트롤러)
@Controller
@RequestMapping("/user")
@RequiredArgsConstructor
public class SignInContorller {
private final UserService userService;
private final UserBean signInUserBean;//로그인 정보를 담은 session scope 객체
@GetMapping("/sign_in")
public String signIn(@ModelAttribute("tempSignInUserBean") UserBean tempsignInUserBean, Model model) {
return "user/sign_in";
}
@PostMapping("/sign_in")
public String signIn_pro(HttpServletRequest request, @Valid @ModelAttribute("tempSignInUserBean")
UserBean tempSignInUserBean, BindingResult result) {
if(result.hasErrors()) {
tempSignInUserBean.setUserSignInFail(true); //로그인 실패 시(유효성 검사 실패)
return "user/sign_in";
}
String[] inputEmail = tempSignInUserBean.getUser_email2().split("@");
tempSignInUserBean.setUser_email1(inputEmail[0]);
tempSignInUserBean.setUser_email2(inputEmail[1]);
userService.getSignInUserInfo(tempSignInUserBean);
if(signInUserBean.isUserSignIn()) { //로그인 성공 시
HttpSession session = request.getSession();
session.setAttribute("signInUserBean", signInUserBean);
return "redirect:/store";
} else { //로그인 실패 시(가입된 정보가 없음)
return "user/sign_in";
}
}
}
(수정 후 서비스 클래스)
@Service
@RequiredArgsConstructor
public class UserService {
private final UserDao userDao;
private final UserBean signInUserBean;
public void getSignInUserInfo(UserBean tempSignInUserBean) { //입력된 로그인 정보로 가입한 사용자가 있는지 조회
Optional<UserBean> tempSignInUserBean2 = userDao.getSignInUserInfo(tempSignInUserBean);
if(tempSignInUserBean2.isPresent()) { //가입된 사용자가 있다 = 로그인 성공
signInUserBean.setUser_idx(tempSignInUserBean2.get().getUser_idx());
signInUserBean.setUser_nickname(tempSignInUserBean2.get().getUser_nickname());
signInUserBean.setUserSignIn(true);
} else { //조회 결과가 없다 = 로그인 실패
tempSignInUserBean.setUserSignInFail(true);
}
}
}
tempSignInUserBean은 로그인 처리를 위한 용도의 커맨드 객체이다. 로그인 실패 시에는 이 객체에 userSignInFail = false를 세팅하는 것이 용도에도 맞고, 지금의 문제를 해결할 수 있는 방법이다. signInUserBean은 여러 페이지에서 로그인 유저의 정보가 필요하기 때문에 생성한 스프링 빈이므로 로그인 성공 시에만 세션 영역에 signInUserBean을 바인딩하는 것으로 수정하였다.