[SPRING] Todo ver2 (2) - ๐Ÿ”‘Session, Filter, โš ๏ธ ์—๋Ÿฌ ์ฒ˜๋ฆฌ

๋ฆผ๋ฏผ์ง€ยท2025๋…„ 4์›” 2์ผ

Today I Learn

๋ชฉ๋ก ๋ณด๊ธฐ
39/62

Todo ์„œ๋น„์Šค์˜ ํšŒ์› CRUD, ํšŒ์›๋ณ„ ๊ฒŒ์‹œ๋ฌผ CRUD ๊ธฐ๋Šฅ์ด ๋ชจ๋‘ ๊ตฌํ˜„๋˜์—ˆ๋‹ค

์ด์ œ ํšŒ์›๊ฐ€์ž… ํ›„ ์ฟ ํ‚ค์™€ ์„ธ์…˜์„ ํ™œ์šฉํ•ด ์ธ์ฆ ๊ธฐ๋Šฅ์„ ๋งŒ๋“ค์–ด๋ณด์ž

โ˜‘๏ธ ํ๋ฆ„ ์ •๋ฆฌ

๐Ÿ“Œ ์š”๊ตฌ์‚ฌํ•ญ
1. ํšŒ์›๊ฐ€์ž… ํ›„ ๋กœ๊ทธ์ธํ•˜๊ธฐ
2. ๋กœ๊ทธ์ธํ•˜๋ฉด ์ฟ ํ‚ค์™€ ์„ธ์…˜ ๋‚จ๊ธฐ๊ธฐ
3. @Configuration ์„ ํ™œ์šฉํ•ด ํŽ˜์ด์ง€๋ณ„๋กœ ํ•„ํ„ฐ๋ฅผ ๋“ฑ๋กํ•˜๊ธฐ
4. ๋กœ๊ทธ์•„์›ƒํ•˜๋ฉด ์ฟ ํ‚ค&์„ธ์…˜ ์‚ญ์ œํ•˜๊ธฐ
5. ์ด๋ฉ”์ผ๊ณผ ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์ผ์น˜ํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ HTTP Status 401์„ ๋ฐ˜ํ™˜

์ „์ฒด์ ์ธ ํšŒ์›๊ฐ€์ž… ๋กœ๊ทธ์ธ ์„ธ์…˜ ์œ ์ง€ ๋กœ๊ทธ์•„์›ƒ ๋กœ์ง์„ ์ •๋ฆฌํ•ด๋ณด์ž

  • ํšŒ์› ๊ฐ€์ž… /signup
    : name email password ์ž…๋ ฅํ•ด์„œ ํšŒ์›๊ฐ€์ž…
    โ†’ MemberService.signUp()์„ ํ†ตํ•ด ๊ตฌํ˜„๋จ
    โ†’ ์—ฌ๊ธฐ๋Š” ๋กœ๊ทธ์ธ ์•ˆํ•ด๋„ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•˜๊ฒŒ ๊ตฌ์„ฑํ•ด์ค˜์•ผํ•จ

  • ๋กœ๊ทธ์ธ /login
    : email password๋กœ ์š”์ฒญ ๋ณด๋‚ด๋ฉด ์ €์žฅ๋œ ํšŒ์›์ •๋ณด์—์„œ ๊ฒ€์ฆํ•˜๊ณ , ๋งž์œผ๋ฉด session ์ƒ์„ฑํ•ด์ฃผ๊ธฐ
    โ†’ ์ด๊ฑธ ์ฟ ํ‚ค์— ์ €์žฅํ•ด๋†“๊ณ  ๋‚˜์ค‘์— ํ•„์š”ํ•  ๋•Œ๋งˆ๋‹ค ์ฟ ํ‚ค๋ฅผ ๋ณด๋‚ด์„œ ์ธ์ฆํ•˜์ž

  • ์ƒํƒœ ํ™•์ธ /check
    : ์‚ฌ์šฉ์ž๊ฐ€ ์ด ํŽ˜์ด์ง€์— ์ ‘์†ํ• ๊ฑฐ์•ผ!๋ผ๊ณ  ํ•  ๋•Œ๋งˆ๋‹ค ๋กœ๊ทธ์ธํ–ˆ๋Š”์ง€ ๊ฒ€์ฆ
    โ†’ ์•„๊นŒ ์ƒ์„ฑ๋œ session์ฟ ํ‚ค๋ฅผ ์ „์†กํ•˜๊ณ , ์„œ๋ฒ„๊ฐ€ ์ด๋ฅผ ์กฐํšŒํ•ด์„œ ๊ฒ€์ฆ
    โ†’ ๋กœ๊ทธ์ธ๋œ ์ƒํƒœ๋ฉด ํšŒ์›์ •๋ณด๋ฅผ ๋ฑ‰๊ธฐ / ์•„๋‹ˆ๋ฉด ๋กœ๊ทธ์ธํ•˜๋ผ๊ณ  ์•Œ๋ ค์ฃผ๊ธฐ

  • ๋กœ๊ทธ์•„์›ƒ /logout
    : ์„ธ์…˜ ์‚ญ์ œํ•ด์ฃผ๊ธฐ

โœ… ์ด๋•Œ filter๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์‚ฌ์šฉ์ž์˜ ์š”์ฒญ์ด ์ปจํŠธ๋กค๋Ÿฌ๋กœ ๊ฐ€๊ธฐ ์ „์— ๋ฏธ๋ฆฌ ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ฃผ์ž


๐Ÿ” ์ธ์ฆ ๊ตฌํ˜„ํ•˜๊ธฐ

๐Ÿงฉ ์ „์ฒด ๋กœ์ง ํŒŒ์•…

1๏ธโƒฃ ์‚ฌ์šฉ์ž๊ฐ€ email, password๋ฅผ JSON์œผ๋กœ ๋ณด๋ƒ„
2๏ธโƒฃ memberService.login()์—์„œ ์ด๋ฉ”์ผ & ๋น„๋ฐ€๋ฒˆํ˜ธ ๊ฒ€์ฆ
3๏ธโƒฃ ์„ธ์…˜์„ ์ƒ์„ฑํ•˜๊ณ  session.setAttribute("user", email) ์ €์žฅ
4๏ธโƒฃ ์ดํ›„ ํด๋ผ์ด์–ธํŠธ๋Š” ๋ชจ๋“  ์š”์ฒญ์— ๋กœ๊ทธ์ธ ์ƒํƒœ ์œ ์ง€

๐Ÿ”‘ ๋กœ๊ทธ์ธ Login

1. @PostMapping์œผ๋กœ ๋กœ๊ทธ์ธ์„ ๊ตฌํ˜„ํ•˜์ž

//๋กœ๊ทธ์ธ
@PostMapping("/login")
public ResponseEntity<String> login(@RequestBody MemberRequestDto dto,
                                        HttpServletRequest request,
                                        HttpServletResponse response) {}

์‚ฌ์šฉ์ž์—๊ฒŒ์„œ email password ์ •๋ณด๋ฅผ ์ž…๋ ฅ๋ฐ›์•„์•ผํ•จ โ†’ @RequestBoy MemberRequestDto dto
request โ†’ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ณด๋‚ธ ์š”์ฒญ ์ •๋ณด๋ฅผ ๋‹ด๊ณ  ์žˆ์Œ
response โ†’ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์‘๋‹ต์„ ๋ณด๋‚ผ ๋•Œ ์‚ฌ์šฉ

์ด์ œ login ๋ฉ”์„œ๋“œ ๋‚ด๋ถ€๋ฅผ ๊ตฌํ˜„ํ•ด๋ณด์ž
์ด๋ฉ”์ผ๊ณผ ๋น„๋ฒˆ์„ ๋„ฃ์–ด์„œ ๊ทธ ํšŒ์›์ด ์กด์žฌํ•˜๊ณ  ๋น„๋ฒˆ๋„ ๋งž์œผ๋ฉด(service์—์„œ) ๋กœ๊ทธ์ธ ์„ธ์…˜&์ฟ ํ‚ค์„ ์ œ๊ณตํ•ด์ค€๋‹ค

2. ํšŒ์› ๊ฒ€์ฆ ๋ฐ session ์ƒ์„ฑ

Member member = memberService.login(dto.getEmail(), dto.getPassword()); //ํšŒ์›์ด ์กด์žฌํ•˜๋Š”์ง€ ๊ฒ€์ฆ
        HttpSession session = request.getSession(); //์„ธ์…˜ ์š”์ฒญํ•˜๊ธฐ
        session.setAttribute(Const.LOGIN_USER, member.getId());
        
        return ResponseEntity.ok("๋กœ๊ทธ์ธ ์„ฑ๊ณต");

request.getSession()
โ†’ ํ˜„์žฌ ์„ธ์…˜์ด ์—†์œผ๋ฉด ์ƒˆ๋กœ์šด ์„ธ์…˜์„ ์ƒ์„ฑ / ๊ธฐ์กด์— ์„ธ์…˜์ด ์žˆ์œผ๋ฉด ํ•ด๋‹น ์„ธ์…˜์„ ๋ฐ˜ํ™˜

session.setAttribute("user", member.getEmail())
โ†’ ์„ธ์…˜์— ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ์ €์žฅ (user ํ‚ค์— ์ด๋ฉ”์ผ ์ €์žฅ)

โžก๏ธ ์ดํ›„ ์ธ์ฆ์ด ํ•„์š”ํ•œ API์—์„œ session.getAttribute("user")๋กœ ๋กœ๊ทธ์ธ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค!!


๐Ÿšช ๋กœ๊ทธ์•„์›ƒ

์„ธ์…˜์„ ์‚ญ์ œํ•ด์ฃผ์–ด์•ผํ•œ๋‹ค

//๋กœ๊ทธ์•„์›ƒ
    //๋กœ๊ทธ์•„์›ƒ
    @PostMapping("/logout")
    public ResponseEntity<String> logout(HttpServletRequest request, HttpServletResponse response) {
        // ๋กœ๊ทธ์ธํ•˜์ง€ ์•Š์œผ๋ฉด HttpSession์ด null๋กœ ๋ฐ˜ํ™˜๋œ๋‹ค.
        HttpSession session = request.getSession(false);
        // ์„ธ์…˜์ด ์กด์žฌํ•˜๋ฉด -> ๋กœ๊ทธ์ธ์ด ๋œ ๊ฒฝ์šฐ
        if(session != null) {
            session.invalidate(); // ํ•ด๋‹น ์„ธ์…˜(๋ฐ์ดํ„ฐ)์„ ์‚ญ์ œํ•œ๋‹ค.
            return ResponseEntity.ok("๋กœ๊ทธ์•„์›ƒ ์„ฑ๊ณต");
        } else{
            return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
        }
    }

HttpSession session = request.getSession(false) ์—ฌ๊ธฐ false๋กœ ๋˜์–ด์žˆ์œผ๋ฉด ์„ธ์…˜์ด ์—†์„๋•Œ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•˜์ง€ ์•Š๋Š”๋‹ค => ๋กœ๊ทธ์ธํ•˜์ง€ ์•Š์•˜๋‹ค๋ฉด session์ด null๋กœ ์„ค์ •๋จ!

๋กœ๊ทธ์ธ์ด ๋˜์–ด์žˆ์œผ๋ฉด session.invalidate()๋กœ ์„ธ์…˜์„ ์‚ญ์ œ!


๐Ÿ•ณ๏ธ ํ•„ํ„ฐ

1. ํ•„ํ„ฐ(Filter)๋ž€?

: HTTP ์š”์ฒญ/์‘๋‹ต์„ ๊ฐ€๋กœ์ฑ„์„œ ์ฒ˜๋ฆฌํ•˜๋Š” ์—ญํ• 
โ†’ ๋กœ๊ทธ์ธ ์ƒํƒœ ํ™•์ธ, JWT ๊ฒ€์ฆ, ์š”์ฒญ ๋กœ๊น… ๋“ฑ์˜ ์ž‘์—…์„ ์ˆ˜ํ–‰

@Slf4j
public class LoginFilter implements Filter {

    private static final String[] WHITE_LIST = {"/", "/members/signup", "/members/login", "/members/logout"};

WHITE_LIST์— ๊ฒ€์ฆ์„ ํ•˜์ง€ ์•Š์•„๋„ ๋˜๋Š” ํŽ˜์ด์ง€๋ฅผ ๋„ฃ์–ด์ฃผ์ž

    @Override
    public void doFilter(ServletRequest servletRequest, 
    				ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
        // ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์šด ์บ์ŠคํŒ…
        HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
        String requestURI = httpRequest.getRequestURI();

        // ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์šด ์บ์ŠคํŒ…
        HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;

        log.info("๋กœ๊ทธ์ธ ํ•„ํ„ฐ ๋กœ์ง ์‹คํ–‰");

์ด ํด๋ž˜์Šค ์•„๋ž˜์— ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธ์ธ์ง€ ์•„๋‹Œ์ง€ ๊ฒ€์ฆํ•˜๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ๋จผ์ € ๋งŒ๋“ค์–ด๋ณด์ž!
boolean ํƒ€์ž…์œผ๋กœ ๋ฐ˜ํ™˜ํ•œ๋‹ค

    private boolean isWhiteList(String requestURI) {
        // request URI๊ฐ€ whiteListURL์— ํฌํ•จ๋˜๋Š”์ง€ ํ™•์ธ
        // ํฌํ•จ๋˜๋ฉด true, ํฌํ•จ๋˜์ง€ ์•Š์œผ๋ฉด false ๋ฐ˜ํ™˜
        return PatternMatchUtils.simpleMatch(WHITE_LIST, requestURI);
    }
}

์ด ๋ฐ›์€ ๊ฒฐ๊ณผ๋ฅผ ํ† ๋Œ€๋กœ ๋กœ๊ทธ์ธํ•˜๋ฉด ์„ธ์…˜๊ฐ’์ด ์žˆ์œผ๋‹ˆ๊นŒ doFilter ํ•ด์ฃผ๊ณ 
๋กœ๊ทธ์ธํ•˜์ง€ ์•Š์•˜๋”ฐ๋ฉด ์„ธ์…˜๊ฐ’์ด ์—†์œผ๋‹ˆ๊นŒ ์—๋Ÿฌ๋ฅผ ๋˜์ ธ์ค€๋‹ค!

        // ๋กœ๊ทธ์ธ์„ ์ฒดํฌ ํ•ด์•ผํ•˜๋Š” URL์ธ์ง€ ๊ฒ€์‚ฌ
        // whiteListURL์— ํฌํ•จ๋œ ๊ฒฝ์šฐ true ๋ฐ˜ํ™˜ -> !true = false
        if (!isWhiteList(requestURI)) {
            // ๋กœ๊ทธ์ธ ํ™•์ธ -> ๋กœ๊ทธ์ธํ•˜๋ฉด session์— ๊ฐ’์ด ์ €์žฅ๋˜์–ด ์žˆ๋‹ค๋Š” ๊ฐ€์ •.
            // ์„ธ์…˜์ด ์กด์žฌํ•˜๋ฉด ๊ฐ€์ ธ์˜จ๋‹ค. ์„ธ์…˜์ด ์—†์œผ๋ฉด session = null
            HttpSession session = httpRequest.getSession(false);

            // ๋กœ๊ทธ์ธํ•˜์ง€ ์•Š์€ ์‚ฌ์šฉ์ž์ธ ๊ฒฝ์šฐ
            if (session == null || session.getAttribute(Const.LOGIN_USER) == null) {
                throw new RuntimeException("๋กœ๊ทธ์ธ ํ•ด์ฃผ์„ธ์šฉ์šฉ๊ฐ€๋ฆฌ์น˜ํ‚จ ๋จน๊ณ ์‹ถ๋‹น");
            }
            // ๋กœ๊ทธ์ธ ์„ฑ๊ณต ๋กœ์ง
        }
        // 1๋ฒˆ๊ฒฝ์šฐ : whiteListURL์— ๋“ฑ๋ก๋œ URL ์š”์ฒญ์ด๋ฉด ๋ฐ”๋กœ chain.doFilter()
        // 2๋ฒˆ๊ฒฝ์šฐ : ํ•„ํ„ฐ ๋กœ์ง ํ†ต๊ณผ ํ›„ ๋‹ค์Œ ํ•„ํ„ฐ ํ˜ธ์ถœ chain.doFilter()
        // ๋‹ค์Œ ํ•„ํ„ฐ ์—†์œผ๋ฉด Servlet -> Controller ํ˜ธ์ถœ
        filterChain.doFilter(servletRequest, servletResponse);

    }

๊ทธ๋ฆฌ๊ณ  ๋‚ด๊ฐ€ ๋งŒ๋“  ์ด ๋กœ๊ทธ์ธ ํ•„ํ„ฐ๋ฅผ
@Configuration์„ ์ด์šฉํ•ด ํ•„ํ„ฐ๋ฅผ ์ง์ ‘ ๋“ฑ๋กํ•ด์•ผํ•œ๋‹ค!!

@Configuration
public class Config {

    @Bean
    public FilterRegistrationBean loginFilter() {
        FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>();
        filterRegistrationBean.setFilter(new LoginFilter()); // Filter ๋“ฑ๋ก
        filterRegistrationBean.setOrder(1); // Filter ์ˆœ์„œ ์„ค์ •
        filterRegistrationBean.addUrlPatterns("/*"); // ์ „์ฒด URL์— Filter ์ ์šฉ

        return filterRegistrationBean;
    }
}

setFilter๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋กœ๊ทธ์ธํ•„ํ„ฐ๋ฅผ ํ•„ํ„ฐ์— ๋„ฃ์–ด์ฃผ๊ณ , ์ „์ฒด URL์— 1๋ฒˆ์งธ ์ˆœ์„œ๋กœ ์ ์šฉํ•œ๋‹ค!


๐Ÿ‘ฉโ€๐Ÿณ ์ปค์Šคํ…€ ์˜ˆ์™ธ ๋งŒ๋“ค๊ธฐ

์ง€๊ธˆ๊นŒ์ง€์˜ ์—๋Ÿฌ ๋˜์ง€๊ธฐ๋„ ์ข‹์ง€๋งŒ,,

{
	"timestamp": "2025-03-26T14:26:45",
	"status": 400,
	"error": "BAD_REQUEST",
	"code": "C001",
	"message": "์ž˜๋ชป๋œ ์ž…๋ ฅ๊ฐ’์ž…๋‹ˆ๋‹ค",
	"path": "/api/login",
	"fieldErrors": [
		{
			"field": "username",
			"message": "์•„์ด๋”” ์ž…๋ ฅ์€ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค"
		}
	]
}
{
    "code": "C001",
    "message": "์•„์ด๋”” ์ž…๋ ฅ์€ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค",
    "status": "BAD_REQUEST"
}

์ด๋Ÿฐ์‹์œผ๋กœ ์‘๋‹ต์„ ๋ฐ›๊ณ ์‹ถ๋‹ค!

๊ทธ๋Ÿฌ๋ ค๋ฉด~~

๐Ÿš€ ์˜ˆ์™ธ ํด๋ž˜์Šค(Custom Exception) ์ƒ์„ฑ
โ†’ ๋กœ๊ทธ์ธ ์‹คํŒจ ์‹œ ํŠน์ • ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋„๋ก CustomException์„ ๋งŒ๋“ ๋‹ค.

๐Ÿงฉ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ํ•ธ๋“ค๋Ÿฌ(Global Exception Handler) ์ƒ์„ฑ
โ†’ @ControllerAdvice์™€ @ExceptionHandler๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์˜ˆ์™ธ ๋ฐœ์ƒ ์‹œ ์ผ๊ด€๋œ JSON ์‘๋‹ต์„ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ์„ค์ •ํ•œ๋‹ค.

๊ธฐ๋Šฅ๋ฐฉ๋ฒ•
๋กœ๊ทธ์ธ ์‹คํŒจ ์‹œ ์˜ˆ์™ธ ๋ฐœ์ƒCustomException ํด๋ž˜์Šค ํ™œ์šฉ
์—๋Ÿฌ ์ฝ”๋“œ ์ผ๊ด€์„ฑ ์œ ์ง€Error Enum ์ •์˜
์ „์—ญ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ@RestControllerAdvice + @ExceptionHandler ์‚ฌ์šฉ
JSON ์‘๋‹ต ํ˜•ํƒœErrorResponseDTO

1. ๋กœ๊ทธ์ธ ์‹คํŒจ์‹œ ์˜ˆ์™ธ ๋งŒ๋“ค๊ธฐ customException

์ปค์Šคํ…€ ์˜ˆ์™ธ โ†’ ๋‚ด๊ฐ€ ์›ํ•˜๋Š” ์ƒํ™ฉ์—์„œ ์›ํ•˜๋Š” ์—๋Ÿฌ๋ฅผ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ๋”ฐ
์ปค์Šคํ…€ ์—๋Ÿฌ๋ฅผ ๋งŒ๋“ค๋–„๋Š” RuntimeException์„ ์ƒ์†ํ•ด์„œ ๋งŒ๋“ค๋ฉด ๋œ๋‹ค

@Getter
public class CustomException extends RuntimeException {
    private final Errors error;

    public CustomException(Errors error) {
        super(error.getMessage());
        this.error = error;
    }
}

super(error.getMessage()); ์ด ๊ณผ์ •์„ ํ†ตํ•ด ์ƒ์†๋ฐ›์€ ๋ถ€๋ชจ์—๊ฒŒ์„œ๋ถ€ํ„ฐ ์—๋Ÿฌ ๋ฉ”์„ธ์ง€๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค

2. ์—๋Ÿฌ ์ฝ”๋“œ Enum

Enum์„ ์‚ฌ์šฉํ•˜๋ฉด ๋‚ด๊ฐ€ ์›ํ•˜๋Š” ์ด๋ฆ„๊ณผ ์—๋Ÿฌ์ฝ”๋“œ๋กœ ๋ฐ˜ํ™˜ํ•ด์ค„ ์ˆ˜ ์žˆ๋‹ค
(๊น”๋”ํ•จ์€ ๋ณด๋„ˆ์Šค~!!)

@Getter
@AllArgsConstructor
public enum Errors {
    INVALID_LOGIN("C001", "์•„์ด๋”” ๋˜๋Š” ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์ผ์น˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.", HttpStatus.BAD_REQUEST),
    REQUIRED_USERNAME("C002", "์•„์ด๋”” ์ž…๋ ฅ์€ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค.", HttpStatus.BAD_REQUEST);

    private final String code;
    private final String message;
    private final HttpStatus status;
}

3. ์ „์—ญ ์˜ˆ์™ธ์ฒ˜๋ฆฌ & ์‘๋‹ต ํ˜•ํƒœ ๋งŒ๋“ค๊ธฐ

GlobalExceptionHandler๋ฅผ ํ†ตํ•ด ์—๋Ÿฌ๋ฅผ ํ•ธ๋“ค๋งํ• ๊ฑฐ๋‹ค
์ด๋ฆ„์—์„œ ๋ณด์ด๋‹ค์‹ถ์ด, ์ „์—ญ์—์„œ(ํ”„๋กœ๊ทธ๋žจ ์ „์ฒด์—) ๋ฐœ์ƒํ•˜๋Š” ์—๋Ÿฌ๋“ค์„ ์žก์•„์„œ ์ฒ˜๋ฆฌํ•ด์ฃผ๋Š” ํ†ตํ•ฉ ์—๋Ÿฌ์ฒ˜๋ฆฌ๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹น

์–ด๋–ค ํ˜•ํƒœ๋กœ ๋ฐ˜ํ™˜ํ• ์ง€ ์ •ํ•ด์ฃผ์–ด์•ผํ•˜๋ฏ€๋กœ ErrorResponseDto์œผ๋กœ ํ˜•์‹์„ ๋งŒ๋“ค์–ด์ฃผ์ž

@Getter
@Builder
public class ErrorResponseDto {
    private LocalDateTime timestamp;
    private int status;
    private String error;
    private String code;
    private String message;
    private String path;
}

์‹œ๊ฐ„, ์ƒํƒœ, ์—๋Ÿฌ, ์ฝ”๋“œ, ๋ฉ”์„ธ์ง€, ๊ฒฝ๋กœ ํ˜•์‹์œผ๋กœ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ๋งŒ๋“ค์—ˆ๋”ฐ

@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(CustomException.class)
    public ResponseEntity<ErrorResponseDto> handleCustomException(CustomException ex, HttpServletRequest request) {
        Errors error = ex.getError();

        ErrorResponseDto errorResponse = ErrorResponseDto.builder()
                .timestamp(LocalDateTime.now())
                .status(error.getStatus().value())
                .error(error.getStatus().getReasonPhrase())
                .code(error.getCode())
                .message(error.getMessage())
                .path(request.getRequestURI())
                .build();

        return ResponseEntity.status(error.getStatus()).body(errorResponse);
    }
    }

๊ทธ๋Ÿผ ์ด์ œ ์ „์—ญ์—๋Ÿฌ์ฒ˜๋ฆฌํ•ด์ฃผ๋Š” ๊ธ€๋กœ๋ฒŒ ํ•ธ๋“ค๋Ÿฌ์—์„œ ๋งŒ๋“ค์–ด๋‘”๊ฒƒ๋“ค์„ ๋ชจ๋‘ ์ ์šฉํ•ด๋ณด์ž

@ExceptionHandler(CustomException.class)
์ต์…‰์…˜ ํ•ธ๋“ค๋Ÿฌ๋กœ ์•„๊นŒ ๋งŒ๋“ค์–ด๋‘” ์ปค์Šคํ…€์—๋Ÿฌ๋ฅผ ๋„ฃ์–ด์ค€๋‹ค

๋ฐ˜ํ™˜๊ฐ’์€ public ResponseEntity<ErrorResponseDto> ์•„๊นŒ ๋งŒ๋“ค์–ด๋‘” ํ˜•์‹์œผ๋กœ

์ด๋„˜์œผ๋กœ ๋งŒ๋‘˜์–ด๋‘” Errors error = ex.getError(); ๋ฅผ ํ†ตํ•ด ์—๋Ÿฌ๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค

ErrorResponseDto errorResponse = ErrorResponseDto.builder()
                .timestamp(LocalDateTime.now())
                .status(error.getStatus().value())
                .error(error.getStatus().getReasonPhrase())
                .code(error.getCode())
                .message(error.getMessage())
                .path(request.getRequestURI())
                .build();

์ดํ›„ error๋ฅผ ์‚ฌ์šฉํ•ด์„œ getterํ•ด์ฃผ๋ฉด ์—๋Ÿฌ๋“ค์„ ๋‚ด๊ฐ€ ์›ํ•˜๋Š”๋Œ€๋กœ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๊ฒŒ ๋˜๋Š”๊ฒƒ์ด๋‹ค~!!!!!!! ์–ํ˜ธ
๊ทธ๋Ÿฌ๋ฉด ์ด๋ ‡๊ฒŒ

์—๋Ÿฌ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๋ชจ์Šต์„ ๋ณผ ์ˆ˜ ์žˆ๋”ฐ

์—๋Ÿฌ์ฝ”๋“œ๋ฅผ ๋‹ค๋ฅด๊ฒŒ ์ถœ๋ ฅํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด Enum์—์„œ ์ด ๋ถ€๋ถ„๋งŒ ๋ฐ”๊ฟ”์ฃผ๋ฉด ๋œ๋‹ค!

0๊ฐœ์˜ ๋Œ“๊ธ€