SPRING

디버깅 및 인터셉트

silver-w 2024. 10. 31. 12:03

1) @Slf4j ( > Lombok)


Log log = new Log() 객체 생성 -> @Slf4j를 적으면 spring builder에서 log객체를 주입한다.

매개값은 String만 가능하다.

package com.example.sakila.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

import lombok.extern.slf4j.Slf4j;

@Slf4j 
@Controller
public class HelloController {
	@GetMapping("/hello")
	public String hello() {
		// loggin 프레임워크 사용
		log.debug("gg");
		
		return "";
	}
	
}

 

로그 설정은 properties에서 할 수 있다.

properties에서 설정 #log setting : logging.level.패키지명

logging.level.com.example.sakila.controller = debug
# 패키지 범위로도 설정할 수 있다
logging.level.com.example.sakila = debug
레벨순서 : fatal error warn debug trace
 - debug로 설정하면 debug로 부터 왼쪽에 있는 레벨 log를 보여준다.
 - 보통 new throws exception 범위는 error로 설정한다.

 

<<콘솔 결과>>

 

 

2) intercept


Controller에서 response, request 객체가 실행되기 직전에 intercept해서 특정 코드를 실행한다 

 - ex : 세션 검증

 

<<HandlerInterceptor 인터페이스>>

public interface HandlerInterceptor {

	default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		return true;
	}
	
	default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable ModelAndView modelAndView) throws Exception {
	}
	
	default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
			@Nullable Exception ex) throws Exception {
	}

}

- preHandle() : Controller가 실행되기 전에 intercept 한다. true 반환하면 Controller 코드 진행 false 반환시 return;과 같음

- postHandle() : Controller가 실핸된 후에 intercept 한다.

 

1. << Intercept Component 설정 >>

package com.example.sakila;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component 
public class OffInterceptor implements HandlerInterceptor {

	@Override 
	public boolean preHandle
    (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		
		log.debug(request.getRequestURL().toString() + "reqeust intercepted");
		
		// 로그인을 하지 않았다면 = response + return false 
        
		HttpSession session = request.getSession();
        
		if(session.getAttribute("loginStaff") == null) {
			response.sendRedirect(request.getContextPath() + "/off/login");  
            // off/login.jsp on..로 시작하는 컨트롤러를 intercept
		
			return false;
		}
		
		return HandlerInterceptor.super.preHandle(request, response, handler);
	}


}

 

// HandlerInterceptor : Default 메서드로 되어있다 (구현하지 않으면 기본 메서드 구현됨)

@Component :  스프링에게 객체(new OffInterceptor(); 를 만들어서 spring beans에 넣어달라 -> Autowired 사용 가능)
 (=> Stereotype에 소속된다. 구현 하지 않고 그냥 구현 후 spring beans 에 등록)
@Controller : 내부적으로 서블릿 구현 후 spring beans 에 등록  
@Mapper : 내부적으로로  jdbc 구현 후 spring beans 에 등록

 

 

<<@SpringBootApplication>>

※ 각 스프링부트 프로젝트마다 디폴트로 가장 먼저 실행되는 클래스가 만들어져있다. @ SpringBootApplication : 쉽게 생각해서, 어느 컨트롤러를 실행하던, 가장 먼저 실행하는 프로세스를 정하는 클래스로 지정하는 애노테이션여기에 인터셉트를 적용하려면,  webMvcConfigurer를 구현해야한다.

 

※ 스프링부트 프로젝트 마다 가장 먼저 실행되는 클래스

 

2. @SpringBootApplication 을 적용받는 클래스에, Interceptor 구현하기

package com.example.sakila;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@SpringBootApplication
public class SakilaApplication implements WebMvcConfigurer {
	@Autowired private OnInterceptor oninterceptor; 
	
	public static void main(String[] args) { 
		SpringApplication.run(SakilaApplication.class, args);
	}
	
	// 인터셉터 설정 (1)인터셉터 클래스 구현 (2) 인터셉터 맵핑
	@Override
	public void addInterceptors(InterceptorRegistry registry) {	
        // InterceptorRegistry = 인터셉터의 List. 인터셉터를 구현하여 여기다가 add하면 된다
		// (1) 클래스 구현 및 (2) 인터셉트 리스트, 맵핑
		registry.addInterceptor(oninterceptor).addPathPatterns("/on/**");
		WebMvcConfigurer.super.addInterceptors(registry);
	}
}

 WebMvcConfigurer 인터페이스의 addInterceptors()메서드 오버라이딩 

    ㄴ 오버라이딩을 위해 WebMvcConfigurer를 구현.

    ㄴ registry는 인터셉터의 List이다. 해당 리스트에 원하는 인터셉트를 add하면 된다. 

registry.addInterceptor(oninterceptor).addPathPatterns("/on/**");

 

② @Autowired로 미리 설정한 인터셉트인 OnInterceptor타입 의존성 주입

 


① 컨트롤러 - 파라미터값 유효성 검사 및 get/post 분기

@Slf4j
@Controller
public class LoginController {
	@Autowired com.example.sakila.mapper.Staff staffMapper;
	@GetMapping("/off/login")
	public String login() {
		log.debug("off/login run");
		return "/off/login";
	}
	
	@PostMapping("/on/login")
	public String login(Model model, HttpSession session,
							@RequestParam(name="staffId", defaultValue = "1") int staffId, // int staffId = Integer.parseInt(reqeust.getParameter("staffId")
							@RequestParam(name = "password") String password) {
		Staff paramStaff = new Staff();
		paramStaff.setStaffId(staffId);
		paramStaff.setPassword(password);
		
		Staff loginStaff = com.example.sakila.mapper.Staff.login(paramStaff);
		if(loginStaff == null) {
			model.addAttribute("msg", "Login failed");
			return "/off/login";
		}
		session.setAttribute("loginStaff", loginStaff);
		
		return "redirect:/on/login";
	}
}
@RequestParam(name="staffId", defaultValue = "1") int staffId, 값 유효하지 않으면 디폴트 값으로 1 저장
// int staffId = Integer.parseInt(reqeust.getParameter("staffId")

@RequestParam() String password - 파라미터와 vo타입 변수명이 같으면 생략 가능

 

Mapper에서 사용할 staffMapper 객체 의존성 주입

MyBatis는 무조건 매개변수 1개밖에 처리 못함 (Map 또는 Vo 타입 활용)

 

 

※ 로그아웃 -> loginController 에 포함 가능

 

	@GetMapping("/on/logout")
	public String logout(HttpSession session) {
		log.debug("/on/logout run");
		session.invalidate();
		return "redirect:/off/login";
	}

컨트롤러 특징

 - 한 클래스에 URL을 여러개 둘수있으며, 
   하나의 URL에 Mapping이 중복되면 충돌이 난다.