MyBatis로 로그인, 로그아웃, 아이디 저장, 암호화 기능 구현하기 (23.08.11)

·2023년 8월 11일
0

Spring

목록 보기
3/36
post-thumbnail

🌷 MyBatis

데이터의 입력, 조회, 수정, 삭제(CRUD)를 보다 편하게 하기 위해 xml로 구조화한 Mapper 설정 파일을 통해서 JDBC를 구현한 영속성 프레임워크

-> 기존에 JDBC를 통해 구현했던 상당 부분의 코드와 파라미터 설정 및 결과 매핑을 xml 설정을 통해 쉽게 구현할 수 있게 해 준다.

🔎 공식 사이트 바로 가기

🌼 MyBatis의 흐름

이전에 JDBC Template을 통해 SQL을 실행하였다면 Mybatis는 해당 흐름을 전용 라이브러리를 통해 대체하여 동작한다.


🌼 MyBatis의 동작 구조


🌷 로그인, 로그아웃, 아이디 저장, 암호화


자, 이제 어제 구현하려고 했던 로그인 기능과 추가적으로 로그아웃, 아이디 저장 기능, 암호화까지 작성해 보자!

👀 코드로 살펴보기

📁 구조

🌱 pom.xml

...
		<!-- 오라클 JDBC 드라이버 -->
		<!-- https://mvnrepository.com/artifact/com.oracle.database.jdbc/ojdbc11 -->
		<dependency>
			<groupId>com.oracle.database.jdbc</groupId>
			<artifactId>ojdbc11</artifactId>
			<version>21.5.0.0</version>
		</dependency>

		<!-- 스프링에서 JDBC를 사용할 수 있게 하는 라이브러리 -->
		<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>${org.springframework-version}</version>
			<!-- 위 properties의 지정한 Spring 버전을 따라감 -->
		</dependency>

		<!-- MyBatis 영속성 프레임워크 -->
		<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis</artifactId>
			<version>3.5.9</version>
		</dependency>

		<!-- Spring - Mybatis 연결 모듈, 연결 역할을 하는 라이브러리 -->
		<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
		<dependency>
			<groupId>org.mybatis</groupId>
			<artifactId>mybatis-spring</artifactId>
			<version>2.0.6</version>
		</dependency>

		<!-- 커넥션 풀 기능을 사용하기 위한 라이브러리 -->
		<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-dbcp2 -->
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-dbcp2</artifactId>
			<version>2.9.0</version>
		</dependency>

		<!-- Spring-security -->
		<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-core -->
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-core</artifactId>
			<version>5.7.1</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-web -->
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-web</artifactId>
			<version>5.7.1</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-config -->
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-config</artifactId>
			<version>5.7.1</version>
		</dependency>
...

🌱 mybatis-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd" >
<configuration>

	<!-- SqlSessionTemplate 생성 시 적용될 설정 작성 부분 -->
	<settings>
		<!-- insert 또는 update에 사용되는 값 중 null이 있을 경우에 대한 설정
			 해당 설정이 없을 경우 -> SQL 구문에 null 포함되어 있다는 오류 발생
			 해당 설정이 있을 경우 -> 오류를 발생 시키지 않고 NULL 값을 컬럼에 대입
			 단, NOT NULL 제약조건이 없는 컬럼에만 가능함
			 ** value 설정 시 NULL 은 반드시 대문자로 작성 (소문자 null은 오류가 발생함) ** -->
		<setting name="jdbcTypeForNull" value="NULL" />
	</settings>
	
	<!-- 별칭 작성 부분 -->
	<!-- VO클래스의 패키지명 + 클래스명 작성하는 것이 불편하기 때문에 짧은 별칭 부여 -->
	<typeAliases>
		<typeAlias type="edu.kh.project.member.model.dto.Member" alias="Member" /> <!-- 사용자 별칭 -->
	</typeAliases>
	
	<!-- mapper 파일(SQL 작성되는파일) 위치 등록 부분 -->
	<mappers>
		<mapper resource="/mappers/member-mapper.xml" />
	</mappers>
	
</configuration>

🌱 root-context.xml

프로젝트 전반적으로 사용할 DB 연결 관련 내용(JDBC, MyBatis, DBCP) AOP, 트랜잭션 처리, 파일 업로드 등을 작성

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
	
	<!-- Root Context: defines shared resources visible to all other web components -->

	<!-- 
		프로젝트 전반적으로 사용할
		DB 연결 관련 내용(JDBC, MyBatis, DBCP) AOP,
		트랜잭션 처리, 파일 업로드 등을 작성
	 -->

	<!-- DBCP 사용을 위한 DataSource를 Bean으로 등록 -->
	<!-- DataSource란? : java에서 Connection Pool을 지원하기 위한 인터페이스 -->
	<!-- BasicDataSource : DataSource 인터페이스를 구현한 클래스, 아파치 commons.dbcp에서 제공 -->
	<!-- destroy-method="close" : 주어진 세션을 자동으로 반환(close)하라는 설정 -->
	
	<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
		
		<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
		
		<property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" />
		<property name="username" value="project" />
		<property name="password" value="project1234" />
		
		<!-- defaultAutoCommit : SQL 수행 후 자동 COMMIT 설정. (기본값 : true) -->
		<property name="defaultAutoCommit" value="false" />
		
		<!-- 커넥션 풀 설정 -->
		<property name="initialSize" value="10" /> <!-- 초기 커넥션 수, 기본 0 -->
		<property name="maxTotal" value="500" /> <!-- 최대 커넥션 수, 기본 8 -->
		<property name="maxIdle" value="100" /> <!-- 유휴(대기) 상태로 존재할 수 있는 커넥션 최대 수, 기본 8 -->
		<property name="minIdle" value="10" /> <!-- 유휴 상태로 존재할 수 있는 커넥션 최소 수, 기본 0 -->
		<property name="maxWaitMillis" value="-1" /> <!-- 예외 발생 전 커넥션이 반환 될 떄 까지 대기하는 최대 시간(ms), 기본 -1(무기한) -->
	
	</bean>
	
	
	<!-- SqlSession : sql구문을 DB에 전달, 실행하는 객체
		 SqlSessionFactory : SqlSession을 만드는 객체
		 sqlSessionFactoryBean : mybatis 설정 파일(mybatis-config.xml)과 Connection Pool 정보를 이용하여 SqlSessionFactory를 만드는 객체
		 sqlSessionTemplate : SqlSession 객체에 트랜잭션 처리 역할이 가능하도록 하는 객체 -->

	<!-- 마이바티스 SqlSession 등록하기 (xml 방식으로 bean 등록) -->
	<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">

		<!-- mybatis-config.xml 설정 불러오기 -->
		<property name="configLocation" value="classpath:mybatis-config.xml" />
		<property name="dataSource" ref="dataSource" />
	</bean>
	
	<!-- SqlSessionTemplate : 기본 SQL 실행 + 트랜잭션 관리 역할을 하는 SqlSession을 생성할 수 있게 하는 객체(Spring bean으로 등록해야함.) -->
	<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
		<constructor-arg ref="sqlSessionFactoryBean" />
	</bean>
	
	<!-- 스프링에서 사용하는 proxy를 이용한 트랜잭션 제어가 안 될 경우 추가적인 트랜잭션 매니저를 추가해서 문제 해결 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />
	</bean>
	 
</beans>

🌱 MemberController.java

...
@SessionAttributes("loginMember") // Model의 이름(key)을 적으면 session으로 추가 -> 값이 여러 개일 경우 배열 형태로 작성 {"loginMember", "test", "pw"}
public class MemberController {
	
	// 등록된 Bean 중에서 필드와 타입이 일치하는 Bean을 주입
	// -> MemberService를 구현한 MemberServiceImpl의 Bean을 주입
	@Autowired
	private MemberService service;
...
	
	// ***********************************************************************
	
	// alt + shift + J
	/** 로그인 요청 처리(진짜)
	 * @return 메인페이지 redirect 주소
	 */
	@PostMapping("/login")
	public String login(Member inputMember, Model model
						, @RequestHeader(value="referer") String referer
						, @RequestParam(value="saveId", required=false) String saveId
						, HttpServletResponse resp
						, RedirectAttributes ra) {
		
		// Member inputMember : 커맨드 객체(필드에 파라미터 담겨 있음)
		
		// @RequestHeader(value="referer" String referer)
		// -> 요청 HTTP header에서 "referer"(이전 주소) 값을 얻어와
		//	  매개변수 String referer에 저장
		
		// Model : 데이터 전달용 객체
		// -> 데이터를 K : V 형식으로 담아서 전달
		// -> 기본적으로 request scope
		// -> @SessionAttributes 어노테이션과 함께 사용 시 Session scope
		
		// @RequestParam(value="saveId") String saveId
		// -> name 속성 값이 "saveID"인 파라미터를 전달받아 저장
		// -> required=false : 필수 아님 (null 허용)
		// (주의) required 속성 미작성 시 기본 값은 true
		// -> 파라미터가 전달되지 않는 경우 주의
		
		// HttpServletResponse resp : 서버 -> 클라이언트 응답 방법을 가지고 있는 객체
		
		
		// 로그인 서비스 호출
		Member loginMember = service.login(inputMember);
	
		// DB 조회 결과 확인
		// System.out.println(loginMember);
		
		// 로그인 결과에 따라 리다이렉트 경로를 다르게 지정
		String path = "redirect:";
		
		if(loginMember != null) { // 로그인 성공 시
			path += "/"; // 메인페이지로 리다이렉트
			
			// Session에 로그인한 회원 정보 추가
			// Servlet -> HttpSession.setAttribute(key, value);
			// Spring  -> Model + @SessionAttributes
			
			// 1) model에 로그인한 회원 정보 추가
			model.addAttribute("loginMember", loginMember);
			// -> 현재는 request scope
			
			// 2) 클래스 위에 @SessionAttributes 추가
			// -> session scope로 변경
			
			// ------------ 아이디 저장 ------------
			
			/* Cookie란?
			 * - 클라이언트 측(브라우저)에서 관리하는 파일
			 * 
			 * - 쿠키 파일에 등록된 주소 요청 시마다
			 * 	 자동으로 요청에 첨부되어 서버로 전달됨
			 * 
			 * - 서버로 전달된 쿠키에
			 * 	 값 추가, 수정, 삭제 등을 진행한 후
			 * 	 다시 클라이언트에게 반환함
			 * */
			
			/* Session
			 * - 서버가 클라이언트의 정보를 저장하고 있음 (쿠키와의 차이점)
			 * */
			
			// 쿠키 생성 (해당 쿠키에 담을 데이터를 K:V로 지정)
			Cookie cookie = new Cookie("saveId", loginMember.getMemberEmail());
			
			if(saveId != null) { // 체크되었을 때
				// 한 달(30일) 동안 유지되는 쿠키 생성
				cookie.setMaxAge(60 * 60 * 24 * 30); // 초 단위로 지정
				
			} else { // 체크 안 되었을 때
				// 0초 동안 유지되는 쿠키 생성
				// -> 기존 쿠키를 삭제
				cookie.setMaxAge(0);
			}
			
			// 클라이언트가 어떤 요청을 할 때 쿠키가 첨부될지 경로(주소)를 지정
			cookie.setPath("/"); // localhost/ 이하 모든 주소
								 // ex) / , /member/login, /member/logout 등
								 //	    모든 요청에 쿠키 첨부
			
			// 응답 객체(HttpServletResponse)를 이용해서
			// 만들어진 쿠키를 클라이언트에게 전달
			resp.addCookie(cookie);

		} else { // 로그인 실패 시
			path += referer; // HTTP Header - referer(이전 주소)
			
			/* redirect(재요청) 시
			 * 기존 요청(request)이 사라지고
			 * 새로운 요청(request)을 만들게 되어
			 * 
			 * redirect된 페이지에서는 이전 요청이 유지되지 않는다.
			 * -> 유지하고 싶으면 어쩔 수 없이 Session을 이용해야 함
			 * 
			 * [Spring]
			 * 이런 상황을 해결하기 위한 객체
			 * RedirectAttributes를 제공
			 * 
			 * RedirectAttributes
			 * - 리다이렉트 시 데이터를 request scope로 전달할 수 있게 하는 객체
			 * 
			 * 응답 전 : request scope
			 * 응답 중 : session scope로 잠시 이동
			 * 응답 후 : request scope로 복귀
			 * */
			
			// addFlashAttributes : 잠시 Session에 추가
			ra.addFlashAttribute("message", "아이디 또는 비밀번호가 일치하지 않습니다.");
		}
		
		return path;
	}
	
	// 로그아웃
	@GetMapping("/logout")
	public String logout(SessionStatus status, HttpSession session) {
		
		// SessionStatus : 세션 상태를 관리하는 객체
		
		// 세션 무효화
		// Servlet -> HttpSession.invalidate()
		
		// Spring
		// 1) HttpSession을 이용한 경우
		// 	-> HttpSession.invalidate()
		
		// session.invalidate(); // 세션 무효화
		
		// 2) Model + @SessionAttributes를 이용한 경우
		//	-> SessionStatus.setComplete()
		
		status.setComplete();
		
		return "redirect:/";
	}

	/* 스프링 예외 처리 방법(3종류, 우선 순위, 중복 사용)
	 * 
	 * 1순위 : 메소드 단위로 처리
	 * 		-> try-catch / throws
	 * 
	 * 2순위 : 클래스 단위로 처리
	 * 		-> @ExceptionHandler
	 * 
	 * 3순위 : 프로그램 단위(전역) 처리
	 * 		-> @ControllerAdvice
	 * */
	
	// 현재 클래스에서 발생하는 모든 예외를 모아서 처리
	// @ExceptionHandler(Exception.class)
	public String exceptionHandler(Exception e, Model model) {
		
		// Exception e : 예외 정보를 담고 있는 객체
		// Model model : 데이터 전달용 객체 (request scope가 기본)
		
		e.printStackTrace(); // 예외 내용/발생 메소드 확인
		
		model.addAttribute("e", e);
		
		// forward 진행
		// -> View Resolver의 prefix, suffix를 붙여 JSP 경로로 만듦
		return "common/error";
	}

}

🌱 MemberService.java

💭 왜 Service Interface를 사용할까?

  1. 프로젝트에 규칙성을 부여하기 위해서
  2. 클래스 간의 결합도를 약화시키기 위해서 (객체 지향적 설계)
  3. Spring AOP 사용을 위해서
public interface MemberService {

	/** 로그인 서비스
	 * @param inputMember (email, pw)
	 * @return email, pw가 일치하는 회원 정보 또는 null
	 */
	Member login(Member inputMember);
	
}

🌱 MemberServiceImpl.java

package edu.kh.project.member.model.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import edu.kh.project.member.model.dao.MemberDAO;
import edu.kh.project.member.model.dto.Member;

@Service // Service Layer
		 // 비지니스 로직(데이터 가공, dao 호출, 트랜잭션 제어) 처리하는 클래스라고 명시한 것
		 // + Bean으로 등록하는 어노테이션
public class MemberServiceImpl implements MemberService {
	
	// @Autowired : 작성된 필드와 Bean으로 등록된 객체 중
	//				타입이 일치하는 Bean을 해당 필드에
	//				자동으로 주입(Injection)하는 어노테이션
	//				== DI(Dependency Injection, 의존성 주입)
	//				-> 객체를 직접 만들지 않고 Spring이 만든 객체를 주입함 (Spring에 의존)
	@Autowired
	private MemberDAO dao;

	@Autowired // bean으로 등록된 객체 중 타입이 일치하는 객체를 DI (의존성 주입)
	private BCryptPasswordEncoder bcrypt;
	
	@Override
	public Member login(Member inputMember) {

		// 암호화 추가 예정
		// System.out.println("암호화 확인 : " + bcrypt.encode(inputMember.getMemberPw()));
		
		// bcrypt 암호화는 salt가 추가되기 때문에
		// 계속 비밀번호가 바뀌게 되어서 DB에서 비교 불가능!
		// -> 별도로 제공해 주는 matches(평문, 암호문)을 이용해 비교
		
		// DAO 메소드 호출
		Member loginMember = dao.login(inputMember);
		
		if(loginMember != null) { // 아이디가 일치하는 회원이 조회된 경우
			
			// 입력한 pw, 암호화된 pw 같은지 확인
			
			// 같을 경우
			if(bcrypt.matches(inputMember.getMemberPw(),
							  loginMember.getMemberPw())) {
				
				// 비밀번호를 유지하지 않기 위해서 로그인 정보에서 제거
				loginMember.setMemberPw(null);
				
			} else { // 다를 경우
				loginMember = null; // 로그인 실패처럼 만듦
			}
		}
		
		return loginMember;
	}
	
}

🌱 MemberDAO.java

package edu.kh.project.member.model.dao;

import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import edu.kh.project.member.model.dto.Member;

@Repository // Persistence Layer, 영속성 관련 클래스
			// (파일, DB 관련 클래스) + Bean 등록(== Spring이 객체로 만들어 둔다.)
public class MemberDAO {

	// SqlSessionTemplate (마이바티스 객체) DI
	@Autowired // 등록된 Bean 중에서 SqlSessionTemplate 타입의 Bean을 주입
	private SqlSessionTemplate sqlSession;
	
	/** 로그인 DAO
	 * @param inputMember
	 * @return 회원 정보 또는 null
	 */
	public Member login(Member inputMember) {
		
		// 마이바티스를 이용해서 1행 조회(selectOne)
		
		// sqlSession.selectOne("namespace값.id값", 전달할 값)
		// -> namespace가 일치하는 Mapper에서
		//	  id가 일치하는 SQL 구문을 수행 후
		//	  결과를 1행(dto, 기본 자료형) 반환
		
//		return sqlSession.selectOne("memberMapper.id값", 전달할 값);
		return sqlSession.selectOne("memberMapper.login", inputMember);
	}
	
}

🌱 member-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="memberMapper">
	<!-- namespace : 공간(영역, 지역, 태그)의 이름 -->

	<!-- mapper 파일 생성 시 아래 태그는 반드시 삭제!!! -->
  	<!-- <cache-ref namespace=""/> -->
  	
  	<!-- 
  		resultMap
  		- SELECT 조회 결과(ResultSet) 컬럼명과
  		  컬럼 값을 옮겨 담을 DTO의 필드명이 같지 않을 때
  		  이를 매핑시켜 SELECT 시 자동으로 담기게 하는 역할
  		  
  		- 속성
  		type : 연결할 DTO (패키지명 + 클래스명 또는 별칭)
  		id	 : 만들어진 resultMap을 지칭할 식별명(이름)
  		
  		<id> 태그 : PK 역할 커럼 - 필드 매핑
  		<result> 태그 : <id> 제외 나머지
  	 -->

	<resultMap type="Member" id="member_rm">
		
		<!-- DB의 기본 키(복합키라면 여러 개 작성) -->		
		<id property="memberNo" column="MEMBER_NO"/>
		
		<!-- DB의 일반 컬럼들 -->
		<result property="memberEmail" column="MEMBER_EMAIL"/>
		<result property="memberPw" column="MEMBER_PW"/>
		<result property="memberNickname" column="MEMBER_NICKNAME"/>
		<result property="memberTel" column="MEMBER_TEL"/>
		<result property="memberAddress" column="MEMBER_ADDR"/>
		<result property="profileImage" column="PROFILE_IMG"/>
		<result property="enrollDate" column="ENROLL_DATE"/>
		<result property="memberDeleteFlag" column="MEMBER_DEL_FL"/>
		<result property="authority" column="AUTHORITY"/>
	
	</resultMap>  	
	
  	<!-- 
  		SQL 관련 태그 속성
  		
  		- parameterType : 전달받은 값의 자료형
   						  	기본 : 패키지명 + 클래스명
   						  	별칭 : Mybatis 별칭 또는 사용자 지정 별칭
   						  
   		- parameterMap 	: (ibatis 호환용이라 사용 안 함)
   		
   		- resultType	: select 결과를 담아서 반환할 자료형
   						  단, DTO를 작성할 경우 '필드명 = 컬럼명'인 경우에만 사용 가능
   						  				 memberNo  MEMBER_NO (사용 X)
   						  
   		- resultMap		: select 결과의 컬럼명과 결과를 저장할 DTO 필드명이 다를 경우
   						  이를 알맞게 매핑(연결)시켜 주는 <resultMap> id 작성
  	-->
  	
  	<!-- 
  		** 마이바티스에서 전달받은 값을 SQL에 작성하는 방법 **
  		
  		#{변수명|필드명} : PreparedStatement와 유사
 					   SQL에 값 대입 시 양쪽에 '' 붙여서 대입함
  		
  		${변수명|필드명} : Statement와 유사
  					   SQL에 값 대입 시 양쪽에 아무것도 붙이지 않음
  		
  		[사용 예시]
  		test1 = "user01"
  		test2 = MEMBER_EMAIL
  		
  		- MEMBER_EMAIL이 'user01'인 회원 조회
  		SELECT * FROM MEMBER WHERE ${test2} = #{test1}
  								   MEMBER_EMAIL = 'user01'
  	 -->
  	
  	<select id="login" parameterType="Member" resultMap="member_rm">
  		SELECT MEMBER_NO, MEMBER_EMAIL, MEMBER_NICKNAME, MEMBER_PW,
			MEMBER_TEL, MEMBER_ADDR, PROFILE_IMG, AUTHORITY,
			TO_CHAR(ENROLL_DATE, 'YYYY"년" MM"월" DD"일" HH24"시" MI"분" SS"초"') AS ENROLL_DATE 
		FROM "MEMBER"
		WHERE MEMBER_DEL_FL = 'N'
		AND MEMBER_EMAIL = #{memberEmail}
  	</select>
  	
</mapper>

🌱 error.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Error</title>
    <style>
        #error-container{
            width: 800px;
            height: 300px;
            text-align: center;
           
            position: absolute;
            top : 0; bottom: 0; left: 0; right: 0;
            margin: auto;
        }
       
        #error-container > h1{ margin-bottom: 50px; }


        .error-cnotent-title{
            text-align: left;
            font-weight: bold;
        }
       
        #btn-area{ text-align: center;  }


    </style>
</head>
<body>


    <div id="error-container">
        <h1>서비스 이용 중 문제가 발생했습니다</h1>
       
        <span class="error-content-title"> 발생한 예외 : ${e}</span>
        <p>
            자세한 문제 원인은 이클립스 콘솔을 확인해주세요.
        </p>
       
        <div id="btn-area">
            <a href="/">메인 페이지</a>
            <%-- <a href="${header.referer}">이전 페이지</a> --%>
           
            <button onclick="history.back()">이전 페이지</button>
           
            <!-- referer : 페이지 방문 흔적 -->
        </div>
    </div>
   
</body>
</html>

🌱 ExceptionController.java

package edu.kh.project.common;

import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

// 예외 처리용 컨트롤러 (프로젝트 전역)
@ControllerAdvice
public class ExceptionController {

	@ExceptionHandler(Exception.class)
	public String exceptionHandler(Exception e, Model model) {
		
		// Exception e : 예외 정보를 담고 있는 객체
		// Model model : 데이터 전달용 객체 (request scope가 기본)
		
		e.printStackTrace(); // 예외 내용/발생 메소드 확인
		
		model.addAttribute("e", e);
		
		// forward 진행
		// -> View Resolver의 prefix, suffix를 붙여 JSP 경로로 만듦
		return "common/error";
	}
	
}

🌱 spring-security.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:security="http://www.springframework.org/schema/security"
	xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-5.7.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<!-- xml을 이용한 bcrypt 암호화 객체를 bean으로 등록 -->
	<bean id="bcryptPasswordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
</beans>
profile
풀스택 개발자 기록집 📁

0개의 댓글