Day 87

ChangWoo·2023년 1월 5일
0

중앙 HTA

목록 보기
31/51

테이블 생성

CREATE TABLE SPRING_USERS (
    USER_ID VARCHAR2(100) CONSTRAINT SPRING_USER_ID_PK PRIMARY KEY,
    USER_PASSWORD VARCHAR2(64) NOT NULL,
    USER_NAME VARCHAR2(100) NOT NULL,
    USER_EMAIL VARCHAR2(255) NOT NULL CONSTRAINT SPRING_USER_EMAIL_UK UNIQUE,
    USER_TEL VARCHAR2(20) NOT NULL,
    USER_PHOTO VARCHAR2(100) DEFAULT 'default.png',
    USER_DELETED CHAR(1) DEFAULT 'N',
    USER_UPDATED_DATE DATE DEFAULT SYSDATE,
    USER_CREATED_DATE DATE DEFAULT SYSDATE
);
CREATE TABLE SPRING_USER_ROLES (
    USER_ID VARCHAR2(100) NOT NULL CONSTRAINT SPRING_USER_ROLE_USER_ID_PK REFERENCES SPRING_USERS (USER_ID),
    USER_ROLE_NAME VARCHAR2(20) NOT NULL 
    CONSTRAINT SPRING_USER_ROLE_NAME_CK CHECK (USER_ROLE_NAME IN ('ROLE_GUEST', 'ROLE_USER', 'ROLE_ADMIN'))
);
ALTER TABLE SPRING_USER_ROLES ADD CONSTRAINT SPRING_USER_ROLE_PK PRIMARY KEY (USER_ID, USER_ROLE_NAME);
  • 사용자는 역할을 가지게 된다. (손님, 사용자, 관리자)
  • 게스트는 글 작성X, 사용자는 글 작성O, 관리자는 관리 기능O
  • 같은 아이디로 같은 권한을 가질 수 없다.

기초 작업

home.jsp
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" trimDirectiveWhitespaces="true"%>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
<title>애플리케이션</title>
</head>
<body>
<c:set var="menu" value="home" />
<%@ include file="common/navbar.jsp" %>
<div class="container">
   <div class="row mb-3">
      <div class="col-12">
         <div class="border p-3 bg-light">
            <h1 class="mb-4">Spring MVC 샘플 애플리케이션</h1>
            <p>Spring MVC를 활용한 웹 애플리케이션입니다.</p>
         </div>
      </div>
   </div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
</body>
</html>
tags.jsp
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" trimDirectiveWhitespaces="true"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
navbar.jsp
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" trimDirectiveWhitespaces="true"%>
<nav class="navbar navbar-expand-sm bg-dark navbar-dark mb-3">
	<div class="container">
		<ul class="navbar-nav me-auto">
			<li class="nav-item"><a class="nav-link ${menu eq 'home' ? 'active' : '' }" href="/home">샘플 애플리케이션</a></li>
		</ul>
		<ul class="navbar-nav">
			<li class="nav-item"><a class="nav-link" href="/logout">로그아웃</a></li>
			<li class="nav-item"><a class="nav-link ${menu eq 'login' ? 'active' : '' }" href="/login">로그인</a></li>
			<li class="nav-item"><a class="nav-link ${menu eq 'register' ? 'active' : '' }" href="/register">회원가입</a></li>
		</ul>
	</div>
</nav>
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>
	<settings>
		<setting name="jdbcTypeForNull" value="NULL"/>
		<setting name="logImpl" value="SLF4J"/>
	</settings>
</configuration>
db.properties
### 데이터베이스 연결 정보 설정
db.driver-class-name=oracle.jdbc.OracleDriver
db.url=jdbc:oracle:thin:@localhost:1521:xe
db.username=hr
db.password=zxcv1234
HomeController.java
package com.sample.web.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import com.sample.service.UserService;
import com.sample.web.request.UserRegisterForm;
@Controller
public class HomeController {
	/*
	 * 로그인을 위해 service가 필요하다.
	 */
	@Autowired
	private UserService userService;
	@GetMapping("/home")
	public String home() {
        return "home";
	}
}	

실행결과

회원가입 기능

register-form.jsp
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" trimDirectiveWhitespaces="true"%>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
<title>애플리케이션</title>
</head>
<body>
<c:set var="menu" value="register" />
<%@ include file="common/navbar.jsp" %>
<div class="container">
	<div class="row mb-3">
		<div class="col-12">
			<h1 class="border bg-light p-2 fs-4">회원가입</h1>
		</div>
	</div>
	<div class="row mb-3">
		<div class="col-12">
			<p>회원가입 정보를 입력하세요</p>
			<form id="form-register" class="border bg-light p-3" method="post" action="register">
				<div class="mb-3">
					<label class="form-label">접속 권한</label>
					<div>
						<div class="form-check form-check-inline">
							<input class="form-check-input" type="checkbox" name="roleName" value="ROLE_GUEST" checked>
							<label class="form-check-label">게스트</label>
						</div>
						<div class="form-check form-check-inline">
							<input class="form-check-input" type="checkbox" name="roleName" value="ROLE_USER" disabled="disabled">
							<label class="form-check-label">사용자</label>
						</div>
						<div class="form-check form-check-inline">
							<input class="form-check-input" type="checkbox" name="roleName" value="ROLE_ADMIN" disabled="disabled">
							<label class="form-check-label">관리자</label>
						</div>
					</div>
				</div>
				<div class="mb-3">
					<label class="form-label">아이디</label>
					<input type="text" class="form-control form-control-sm" name="id" />
				</div>
				<div class="mb-3">
					<label class="form-label">비밀번호</label>
					<input type="password" class="form-control form-control-sm" name="password" />
				</div>
				<div class="mb-3">
					<label class="form-label">이름</label>
					<input type="text" class="form-control form-control-sm" name="name" />
				</div>
				<div class="mb-3">
					<label class="form-label">이메일</label>
					<input type="text" class="form-control form-control-sm" name="email" />
				</div>
				<div class="mb-3">
					<label class="form-label">전화번호</label>
					<input type="text" class="form-control form-control-sm" name="tel" />
				</div>
				<div class="text-end">
					<a href="/home" class="btn btn-secondary btn-sm">취소</a>
					<button type="submit" class="btn btn-primary btn-sm">가입</button>
				</div>
			</form>
		</div>
	</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
</body>
</html>
login-form.jsp
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" trimDirectiveWhitespaces="true"%>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
<title>애플리케이션</title>
</head>
<body>
<c:set var="menu" value="login" />
<%@ include file="common/navbar.jsp" %>
<div class="container">
	<div class="row mb-3">
		<div class="col-12">
			<h1 class="border bg-light p-2 fs-4">로그인</h1>
		</div>
	</div>
	<div class="row mb-3">
		<div class="col-12">
			<p>아이디와 비밀번호를 입력하고 로그인 버튼을 클릭하세요</p>
			<form id="form-register" class="border bg-light p-3" method="post" action="login">
				<div class="mb-3">
					<label class="form-label">아이디</label>
					<input type="text" class="form-control form-control-sm" name="id" />
				</div>
				<div class="mb-3">
					<label class="form-label">비밀번호</label>
					<input type="password" class="form-control form-control-sm" name="password" />
				</div>
				<div class="text-end">
					<a href="/home" class="btn btn-secondary btn-sm">취소</a>
					<button type="submit" class="btn btn-primary btn-sm">로그인</button>
				</div>
			</form>
		</div>
	</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
</body>
</html>
User.java
package com.sample.vo;
import java.sql.Date;
import org.apache.ibatis.type.Alias;
@Alias("User")	
public class User {
	private String id;
	private String password;
	private String name;
	private String email;
	private String tel;
	private String photo;
	private String deleted;
	private Date updatedDate;
	private Date createdDate;	
	public User() {}
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	public String getTel() {
		return tel;
	}
	public void setTel(String tel) {
		this.tel = tel;
	}
	public String getPhoto() {
		return photo;
	}
	public void setPhoto(String photo) {
		this.photo = photo;
	}
	public String getDeleted() {
		return deleted;
	}
	public void setDeleted(String deleted) {
		this.deleted = deleted;
	}
	public Date getUpdatedDate() {
		return updatedDate;
	}
	public void setUpdatedDate(Date updatedDate) {
		this.updatedDate = updatedDate;
	}
	public Date getCreatedDate() {
		return createdDate;
	}
	public void setCreatedDate(Date createdDate) {
		this.createdDate = createdDate;
	}		
}

UserRole.java

package com.sample.vo;
import org.apache.ibatis.type.Alias;
@Alias("UserRole")
public class UserRole {

private String userId;
private String roleName;

public UserRole() {}

public UserRole(String userId, String roleName) {
	this.userId = userId;
	this.roleName = roleName;
}
public String getUserId() {
	return userId;
}
public void setUserId(String userId) {
	this.userId = userId;
}
public String getRoleName() {
	return roleName;
}
public void setRoleName(String roleName) {
	this.roleName = roleName;
}	
}
UserMapper.java

package com.sample.mapper;

import org.apache.ibatis.annotations.Mapper;
import com.sample.vo.User;

@Mapper
public interface UserMapper {

void insertUser(User user);
void updateUser(User user);
User getUserById(String userId);
User getUserByEmail(String email);
}
UserRoleMapper.java

package com.sample.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.sample.vo.UserRole;

@Mapper
public interface UserRoleMapper {

void insertUserRole(UserRole userRole);
void deleteUserRole(UserRole userRole);
List<UserRole> getUserRolesByUserId(String userId);	
}

users.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="com.sample.mapper.UserMapper">

<!-- 
	void insertUser(User user)
 -->
 <insert id="insertUser" parameterType="User">
 	insert into spring_users
 		(user_id, user_password, user_name, user_email, user_tel)
 	values
 		(#{id}, #{password}, #{name}, #{email}, #{tel})	
 </insert>
 
 <!-- 
 	void updateUser(User user)
 	(id는 식별자이기 때문에 절대 바꿀 수 없다.)
  -->
  <update id="updateUser" parameterType="User">
  	update
  		spring_users
  	set
  		user_password = #{password},
  		user_tel = #{tel},
  		user_photo = #{photo},
  		user_deleted = #{deleted},
  		user_updated_date = sysdate
  	from
  		user_id = #{id}
  </update>
  
  <!-- 
  		User getUserById(String userId)
   -->
  <select id="getUserById" parameterType="string" resultType="User">
  	select
  		user_id as id,
  		user_password as password,
  		user_name as name,
  		user_email as email,
  		user_tel as tel,
  		user_photo as photo,
  		user_deleted as deleted,
  		user_updated_date as updatedDate,
  		user_created_date as createdDate
  	from
  		spring_users
  	where
  		user_id = #{value}
  </select>
  
  <!-- 
  		User getUserByEmail(String email)
   -->
  <select id="getUserByEmail" parameterType="string" resultType="User">
  	select
  		user_id as id,
  		user_password as password,
  		user_name as name,
  		user_email as email,
  		user_tel as tel,
  		user_photo as photo,
  		user_deleted as deleted,
  		user_updated_date as updatedDate,
  		user_created_date as createdDate
  	from
  		spring_users
  	where
  		user_email = #{value}
  </select>
  </mapper>

user-roles.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="com.sample.mapper.UserRoleMapper">
<!-- 
	void insertUserRole(UserRole userRole)
 -->
<insert id="insertUserRole" parameterType="UserRole">
	insert into spring_user_roles
		(user_id, user_role_name)
	values
		(#{userId}, #{roleName})
</insert>

<!-- 
	void deleteUserRole(UserRole userRole)
 -->
 <delete id="deleteUserRole" parameterType="UserRole">
 	delete from
 		spring_user_roles
 	where
 		user_id = #{userId}
 		and user_role_name = {#roleName}
 </delete>
 
 <!-- 
 	List<UserRole> getUserRolesByUserId(String userId)
  -->
  <select id="getUserRolesByUserId" parameterType="string" resultType="UserRole">
  	select
  		user_id as id,
  		user_role_name as roleName,
  	from
  		spring_user_roles
  	where
  		user_id = #{value}
  </select>	  
</mapper>
UserService.java
package com.sample.service;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.sample.exception.ApplicationException;
import com.sample.mapper.UserMapper;
import com.sample.mapper.UserRoleMapper;
import com.sample.vo.User;
import com.sample.vo.UserRole;
import com.sample.web.request.UserRegisterForm;

@Service
public class UserService {

/*
 * Service 객체에 Mapper가 주입된다.
 */
@Autowired
private UserMapper userMapper;
@Autowired
private UserRoleMapper userRoleMapper;

public void registerUser(UserRegisterForm userRegisterForm) {
	User savedUser = userMapper.getUserById(userRegisterForm.getId());
	if (savedUser != null) {
		throw new ApplicationException("["+userRegisterForm.getId()+"] 사용할 수 없는 아이디입니다.");
	}
	savedUser = userMapper.getUserByEmail(userRegisterForm.getEmail());
	if(savedUser != null) {
		throw new ApplicationException("["+userRegisterForm.getEmail()+"] 사용할 수 없는 이메일입니다.");
	}
	
	User user = new User();
	// 이름이 같은 것끼리 복사된다.(userRegisterForm에 있는 값이 User에 복사된다. / User에 없는 값은 복사되지 않는다.)
	BeanUtils.copyProperties(userRegisterForm, user);
	userMapper.insertUser(user);
	
	UserRole userRole = new UserRole(userRegisterForm.getId(), userRegisterForm.getRoleName());
	userRoleMapper.insertUserRole(userRole);
}

public void login(String id, String password) {
	
}

public void changePassword(String id, String oldPassword, String password) {
	
}

public void deleteUser(String userId) {
	
}

public void addUserRole(UserRole userRole) {
	
}
}

실행결과(회원가입 화면)

실행결과(회원가입 후)

컨트롤러 및 요청 핸들러 메소드 작성하기

- 컨트롤러 작성하기
	
	@Controller
	public class HomeController {

		// 요청 URI "locahost/home"과 매핑되는 요청핸들러 메소드다.
		@GetMapping("/home")
		public String home() {

			// 뷰 이름 반환하기, 실제 jsp 경로 -> "/WEB-INF/views/" + home + ".jsp"
			return "home";
		}
	}

	
	@Controller
	@RequestMapping("/user")
	public class UserController {
	
		// 요청 URI "locahost/user/detail"과 매핑되는 요청핸들러 메소드다.
		@GetMapping("/detail")
		public String detail() {

			// 뷰 이름 반환하기, 실제 jsp 경로 -> "/WEB-INF/views/" + user/detail + ".jsp"
			return "user/detail";
		}

	}


	@Controller
	@RequestMapping("/posts") 
	public class PostsController {
	
		// 요청 URI "locahost/posts/list"과 매핑되는 요청핸들러 메소드다.
		@GetMapping("/list")
		public String list() {

			// 뷰 이름 반환하기, 실제 jsp 경로 -> "/WEB-INF/views/" + posts/detail + ".jsp"
			return "posts/list";

		}

		// 요청 URI "locahost/posts/detail"과 매핑되는 요청핸들러 메소드다.
		@GetMapping("/detail")
		public String detail() {

			// 뷰 이름 반환하기, 실제 jsp 경로 -> "/WEB-INF/views/" + posts/detail + ".jsp"
			return "posts/detail";
		}
	} 

스프링 MVC 예외처리

스프링 MVC의 예외처리

@ControllerAdvice 어노테이션
	- 모든 컨트롤러에서 공통으로 사용하는 기능이 구현된 클래스에 적용하는 어노테이션이다.
	- 대표적인 공통기능
		* 예외처리
		* 파라미터값 변환 

@ExceptionHandler 어노테이션
	- 예외처리를 담당하는 핸들러 메소드에 적용하는 어노테이션이다.
	- 예시
		@ExceptionHandler(예외클래스.class)
		public  String  handle예외클래스(예외클래스 변수명) {
			return "오류페이지이름";
		}
		* 컨트롤러에서 요청을 처리하다가 @ExceptionHandler에 지정한 예외가 발생하면 예외처리 메소드가 자동으로 실행된다.
		* 예외처리 메소드는 매개변수로 발생한 예외객체를 전달 받을 수 있다.
		* 예외처리 메소드가 반환하는 오류페이지(JSP)로 사용자의 요청을 내부이동시킨다.
	- 예외처리 규칙
		* 발생한 예외클래스와 일치하는 @ExceptionHandler(예외클래스.class) 정의가 있으면 해당 예외처리 메소드가 실행된다.
		* 발생한 예외클래스와 일치하는 @ExceptionHandler(예외클래스.class) 정의가 없고, 
                                                           @ExceptionHandler(부모예외클래스.class) 정의가 있으면 해당 예외처리 메소드가 실행된다.
		* 발생한 예외클래스와 일치하는 @ExceptionHandler(예외클래스.class) 정의도 있고,
								   @ExceptionHandler(부모예외클래스.class) 정의도 있으면 더 구체적인 예외클래스가 지정된 예외처리 메소드가 실행된다.
error/500.jsp
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" 
	trimDirectiveWhitespaces="true" isErrorPage="true"%>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
<title>애플리케이션</title>
</head>
<body>
<c:set var="menu" value="home" />
<%@ include file="../common/navbar.jsp" %>
<div class="container">
	<div class="row mb-3">
		<div class="col-12">
			<div class="alert alert-danger">
				<h1 class="mb-4">Spring MVC 샘플 애플리케이션 오류 페이지</h1>
				<p>오류 내용 : <strong class="text-danger"><%=exception.getMessage() %></strong><p>
			</div>
			
			<p class="text-end">
				<a href="/home" class="btn btn-link"></a>으로 이동해서 다시 시도해보시기 바랍니다.
			</p>
		</div>
	</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
</body>
</html>
ExceptionHandlerControllerAdvice.java
package com.sample.web.advice;

import org.springframework.dao.DataAccessException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

import com.sample.exception.ApplicationException;

@ControllerAdvice
public class ExceptionHandlerControllerAdvice {

	@ExceptionHandler(ApplicationException.class)
	public String handleApplicationException(ApplicationException ex) {
		ex.printStackTrace();
		return "error/500";
	}
	

	@ExceptionHandler(DataAccessException.class)
	public String handleDataAccessException(DataAccessException ex) {
		ex.printStackTrace();
		return "error/db";
	}
	
	
	@ExceptionHandler(RuntimeException.class)
	public String handleRuntimeException(RuntimeException ex) {
		ex.printStackTrace();
		return "error/server";
	}
	
}

실행결과

업로드중..

profile
한 걸음 한 걸음 나아가는 개발자

0개의 댓글