[SpringBoot] TIL 079 - 23.11.15

유진·2023년 11월 14일
0

SpringBoot : 필터, 인터셉터

필터

  • 필터 : 로그인 안하면 주소창으로 못들어가게끔 막음!

LoginFilter.java

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);
			// 다음 필터 또는 컨트롤러로 이동
			
		}
		
	}

}

FilterConfig.java

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;
	}
	
}


MainController.java

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:/"; // 메인페이지로 이동
	}
	
}


-> 메세지 출력 후 메인페이지로 리다이렉트됨


인터셉터

BoardTypeInterceptor.java

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);
	}

	
}

BoardService.java

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();

}

BoardServiceImpl.java

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();
	}

}

BoardMapper.java

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();

}

board-mapper.xml

<?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>

InterceptorConfig.java

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/**"); // 가로 채지 않을 경로
		
	}
	
	
	
	
}

header.html

<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이 안되서 화이트라벨 에러 페이지 나타난 것

404.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: 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>

500.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>

예외 처리 코드

ExceptionController.java

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 해주지 않음
	}
	
}

0개의 댓글