SpringBoot : 필터, 인터셉터
package edu.kh.project.common.filter;
import java.io.IOException;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
public class LoginFilter implements Filter{
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest)request; // 다운캐스팅
HttpServletResponse resp = (HttpServletResponse)response;
HttpSession session = req.getSession();
if ( session.getAttribute("loginMember") == null ) { // 로그인이 되어있지 않은 경우
// (cf) 타임리프 -> session 화면에서 지울 수 없음
resp.sendRedirect("/loginError");
} else { // 로그인 된 경우
chain.doFilter(request, response);
// 다음 필터 또는 컨트롤러로 이동
}
}
}
package edu.kh.project.common.config;
import java.util.Arrays;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import edu.kh.project.common.filter.LoginFilter;
@Configuration // 설정 관련 파일에서 꼭 작성해줘야 하는 어노테이션!
public class FilterConfig {
@Bean
public FilterRegistrationBean<LoginFilter> loginFilter(){
FilterRegistrationBean<LoginFilter> resiRegistrationBean = new FilterRegistrationBean<LoginFilter>();
resiRegistrationBean.setFilter(new LoginFilter());
String[] url = {"/myPage/*", "/board2/*"}; // , 로 추가 계속 가능함!
resiRegistrationBean.setUrlPatterns(Arrays.asList(url)); // url 패턴 여러 개 지정 --> 컬렉션 중 리스트 형태로 만들어줌
resiRegistrationBean.setName("loginFilter"); // 이름
resiRegistrationBean.setOrder(1); // 여러 필터가 있을 때 순서
return resiRegistrationBean;
}
}
package edu.kh.project.main.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
@Controller
public class MainController {
@RequestMapping("/")
public String mainForward(Model model) {
model.addAttribute("name", "홍길동");
// Spring MVC : /webapp/WEB-INF/views/common/main.jsp
// Spring Boot (+ thymeleaf 템플릿 엔진)
// src/main/resources/templates/common/main.html
return "common/main";
}
@GetMapping("/loginError")
public String loginError(RedirectAttributes ra) {
ra.addFlashAttribute("message", "로그인 후 이용해주세요");
return "redirect:/"; // 메인페이지로 이동
}
}
-> 메세지 출력 후 메인페이지로 리다이렉트됨
package edu.kh.project.common.interceptor;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import edu.kh.project.board.model.service.BoardService;
import jakarta.servlet.ServletContext;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
// Interceptor : 요청/응답을 가로채는 객체
// Client <-> (Filter) <-> Dispatcher Servlet <-> (Interceptor) <-> Controller
// Filter : 전반적으로 쓰일 보안/인증/인가 관련 작업, 문자열 인코딩, 이미지 데이터 압축..
// Interceptor : 요청에 대한 '데이터 가공' -> Controller로 넘겨주기 위한 정보/데이터 가공,
// 세부적으로 쓰일 보안/인증
// Handle = '처리' 의미
public class BoardTypeInterceptor implements HandlerInterceptor {
/* preHandle : 전처리 Dispathcer Servlet -> Contoller 사이
* postHandle : 후처리 Controller -> Dispathcer Servlet 사이
* afterCompletion : 뷰 완성 후 View Resolver -> Dispathcer Servlet 사이
* */
@Autowired
private BoardService service;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// application scope 내장 객체 얻어오기
ServletContext application = request.getServletContext();
// application scope에 BOARD_TYPE이 조회되어 세팅되지 않았다면
// -> 서버 시작 후 누구도 요청을 한적이 없을 경우
if(application.getAttribute("boardTypeList") == null) {
// 조회 서비스 호출
System.out.println("BOARD_TYPE 조회 서비스 호출");
List<Map<String, Object>> boardTypeList
= service.selectBoardTypeList();
System.out.println("boardTypeList: " + boardTypeList);
// application scope 세팅
application.setAttribute("boardTypeList", boardTypeList);
}
return HandlerInterceptor.super.preHandle(request, response, handler);
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// TODO Auto-generated method stub
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
// TODO Auto-generated method stub
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
package edu.kh.project.board.model.service;
import java.util.List;
import java.util.Map;
public interface BoardService {
/** 게시판 종류 조회
* @return boardTypeList
*/
List<Map<String, Object>> selectBoardTypeList();
}
package edu.kh.project.board.model.service;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import edu.kh.project.board.model.dao.BoardMapper;
@Service
public class BoardServiceImpl implements BoardService{
@Autowired
private BoardMapper mapper;
// 게시판 종류 조회
@Override
public List<Map<String, Object>> selectBoardTypeList() {
return mapper.selectBoardTypeList();
}
}
package edu.kh.project.board.model.dao;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface BoardMapper {
/** 게시판 종류 조회
* @return boardTypeList
*/
List<Map<String, Object>> selectBoardTypeList();
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="edu.kh.project.board.model.dao.BoardMapper">
<!-- 게시판 종류 목록 조회 -->
<select id="selectBoardTypeList" resultType="map">
SELECT * FROM "BOARD_TYPE" ORDER BY 1
</select>
</mapper>
package edu.kh.project.common.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import edu.kh.project.common.interceptor.BoardTypeInterceptor;
@Configuration
public class InterceptorConfig implements WebMvcConfigurer{
@Bean // 개발자 @Bean 등록, 관리 Spring
public BoardTypeInterceptor boardTypeInterceptor() {
return new BoardTypeInterceptor();
}
// @Bean // 개발자 @Bean 등록, 관리 Spring
// public BoardTypeInterceptor 다른인터셉터() {
// return new 다른인터셉터();
// }
@Override // 인터셉터 사용하려면 등록해줘야 함!
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor( boardTypeInterceptor() )
.addPathPatterns("/**") // 가로챌 경로 지정(여러개 작성시 , 로 구분)
.excludePathPatterns("/css/**", "/images/**", "/js/**"); // 가로 채지 않을 경로
// registry.addInterceptor( 다른인터셉터() )
// .addPathPatterns("/**") // 가로챌 경로 지정(여러개 작성시 , 로 구분)
// .excludePathPatterns("/css/**", "/images/**", "/js/**"); // 가로 채지 않을 경로
}
}
<link rel="stylesheet" th:href="@{/css/main-style.css}">
<script th:src="@{https://kit.fontawesome.com/e5fede6c09.js}" crossorigin="anonymous"></script>
<header>
<!-- 클릭 시 메인페이지로 이동하는 로고 -->
<section>
<a th:href="@{/}">
<img th:src="@{/images/logo.jpg}" id="homeLogo">
</a>
</section>
<!-- 검색창 부분 -->
<section>
<section class="search-area">
<!-- form 내부 input 태그 값을 서버 또는 페이지로 전달 -->
<form th:action="@{/search}" method="GET" name="search-form">
<!-- fieldset : form 내부에서 input을 종류별로 묶는 용도로 자주 사용 -->
<fieldset>
<!-- search : 텍스트 타입과 기능적으로는 똑같으나,
브라우저에 의해 다르게 표현될 수 있음 .-->
<!-- autocomplete : HTML 기본 자동완성 사용 X -->
<input type="search" id="query" name="query"
autocomplete="off" placeholder="검색어를 입력해주세요."
>
<button id="searchBtn" class="fa-solid fa-magnifying-glass"></button>
</fieldset>
</form>
</section>
</section>
<section></section>
</header>
<nav>
<ul> <!-- 반복문 : li 포함해서 4개가 만들어짐! -->
<li th:each="boardType : ${application.boardTypeList}">
<a href="#" th:text="${boardType.BOARD_NAME}">게시판 이름</a> <!-- DB에서 가져온 컬럼명이므로 BOARD_NAME 대문자 작성! -->
</li>
</ul>
</nav>
-> 이미지 2개 세팅하기
CF | Error 의미
- 404 : 페이지 못찾음
- 400 : Bad Request -> 파라미터 빼먹었을 때
- 403 : 포비든 에러 -> 서버로부터 거부당함
- 500 : 서버 문제 발생
: error mapping이 안되서 화이트라벨 에러 페이지 나타난 것
<!DOCTYPE html> <!-- 자동완성 : ! + Enter -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>페이지를 찾을 수 없습니다</title>
<style>
img {
height: 90vh;
display: block;
margin: auto;
}
.btn-area {
text-align: center;
}
</style>
</head>
<body>
<img src="/images/error/404.png">
<div class="btn-area">
<button onclick="location.href='/'">메인 페이지</button>
<button onclick="histoty.back()">이전 페이지</button>
</div>
</body>
</html>
<!DOCTYPE html> <!-- 자동완성 : ! + Enter -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>서버 에러 발생</title>
<style>
img {
height: 50vh;
display: block;
margin: auto;
}
.btn-area {
text-align: center;
}
</style>
</head>
<body>
<img src="/images/error/500.png">
<div class="btn-area">
<button onclick="location.href='/'">메인 페이지</button>
<button onclick="histoty.back()">이전 페이지</button>
</div>
</body>
</html>
예외 처리 코드
package edu.kh.project.common.exception;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice // 예외처리용 Controller에 붙이는 어노테이션
public class ExceptionController {
@ExceptionHandler(Exception.class)
public String exceptionHandler(Exception e) {
e.printStackTrace(); // 에러 내용 콘솔에 출력
return "error/500"; // /templates/error/500.html
// 만약 return 구문이 없다면 흰 화면이 뜸! 500.html 페이지로 자동 mapping 해주지 않음
}
}