์•ˆ๋…•ํ•˜์„ธ์š”! ์˜ค๋Š˜์€ Spring Security์—์„œ ๋กœ๊ทธ์ธ ์‹คํŒจ ์ฒ˜๋ฆฌ๋ฅผ ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ•˜๋Š”์ง€์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋กœ๊ทธ์ธ ๊ณผ์ •์—์„œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๋‹ค์–‘ํ•œ ์‹คํŒจ ์ƒํ™ฉ์„ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ช…ํ™•ํ•˜๊ฒŒ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์€ ์ค‘์š”ํ•œ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์˜ ์ผ๋ถ€์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด Spring Security์˜ AuthenticationFailureHandler ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜์—ฌ ์‚ฌ์šฉ์ž ์ •์˜ ๋กœ๊ทธ์ธ ์‹คํŒจ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋งŒ๋“ค์–ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

1. ์‚ฌ์šฉ์ž ์ •์˜ ๋กœ๊ทธ์ธ ์‹คํŒจ ํ•ธ๋“ค๋Ÿฌ ๊ตฌํ˜„

๋จผ์ €, AuthenticationFailureHandler ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” CustomAuthenticationFailureHandler ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค:

public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler {

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException {
        // ๋กœ๊ทธ์ธ ์‹คํŒจ ์‹œ ๋กœ์ง ๊ตฌํ˜„
        String errorMessage = "์•„์ด๋””์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋‹ค์‹œ ํ™•์ธํ•ด์ฃผ์„ธ์š”";

        if (exception instanceof DisabledException) {
            errorMessage = "๊ณ„์ •์ด ๋น„ํ™œ์„ฑํ™”๋˜์—ˆ์Šต๋‹ˆ๋‹ค.";
        } else if (exception instanceof CredentialsExpiredException) {
            errorMessage = "๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ๋งŒ๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.";
        }

        // ์„ธ์…˜์— ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€ ์ €์žฅ
        request.getSession().setAttribute("flash", errorMessage);
        response.sendRedirect("/login");
    }
}

์ด ํ•ธ๋“ค๋Ÿฌ๋Š” ๋กœ๊ทธ์ธ ๊ณผ์ •์—์„œ ๋ฐœ์ƒํ•˜๋Š” ๋‹ค์–‘ํ•œ ์˜ˆ์™ธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ , ์‚ฌ์šฉ์ž์—๊ฒŒ ์ ์ ˆํ•œ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

2. ๋กœ๊ทธ์ธ ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ๋กœ๊ทธ์ธ ์‹คํŒจ ๋ฉ”์‹œ์ง€ ์ฒ˜๋ฆฌ

๋‹ค์Œ์œผ๋กœ, ๋กœ๊ทธ์ธ ์ปจํŠธ๋กค๋Ÿฌ์—์„œ ์ด ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค:

@Controller
public class LoginController {

    @GetMapping("/login")
    public String loginForm(@ModelAttribute("loginForm") LoginForm form, HttpServletRequest request, Model model) {
        if (request.getSession().getAttribute("flash") != null) {
            model.addAttribute("error", request.getSession().getAttribute("flash"));
            request.getSession().removeAttribute("flash");
        }
        return "login/loginForm";
    }
}

์—ฌ๊ธฐ์„œ๋Š” ๋กœ๊ทธ์ธ ํผ์„ ์š”์ฒญํ•  ๋•Œ ์„ธ์…˜์— ์ €์žฅ๋œ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€(flash)๋ฅผ ํ™•์ธํ•˜๊ณ , ์ด๋ฅผ ๋ชจ๋ธ์— ์ถ”๊ฐ€ํ•˜์—ฌ ๋ทฐ์— ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉ์ž์—๊ฒŒ ๋กœ๊ทธ์ธ ์‹คํŒจ์˜ ๊ตฌ์ฒด์ ์ธ ์ด์œ ๋ฅผ ์•Œ๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

3. ์‚ฌ์šฉ์ž ๊ฒฝํ—˜ ํ–ฅ์ƒ

์ด๋Ÿฌํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ ๋กœ๊ทธ์ธ ์‹คํŒจ ์ฒ˜๋ฆฌ๋ฅผ ๊ตฌํ˜„ํ•จ์œผ๋กœ์จ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด๋‹ค ๋ช…ํ™•ํ•œ ํ”ผ๋“œ๋ฐฑ์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๋Š” ์ •ํ™•ํ•œ ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€๋ฅผ ํ†ตํ•ด ๋ฌธ์ œ๋ฅผ ์ธ์ง€ํ•˜๊ณ  ์ ์ ˆํ•œ ์กฐ์น˜๋ฅผ ์ทจํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๊ฒฐ๋ก 

Spring Security์—์„œ ์‚ฌ์šฉ์ž ์ •์˜ ๋กœ๊ทธ์ธ ์‹คํŒจ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์€ ๋ณด์•ˆ์ ์ธ ์ธก๋ฉด๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์˜ ์ธก๋ฉด์—์„œ๋„ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ์˜ค๋Š˜์˜ ๊ธ€์ด ์—ฌ๋Ÿฌ๋ถ„์ด ๋ณด๋‹ค ๋‚˜์€ ๋กœ๊ทธ์ธ ์‹คํŒจ ์ฒ˜๋ฆฌ ๋กœ์ง์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋˜์—ˆ๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค.