2) 만들어 볼 것!
로그인, 로그아웃, 사용자인증(로그인되어있을때 게시물접근) , 예외처리
사용자인증
controller > LoginController.java
package com.gosari.repick.controller;
import com.gosari.repick.domain.Member;
import com.gosari.repick.service.MemberService;
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 org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.support.SessionStatus;
@SessionAttributes("member")
@Controller
public class LoginContoller {
@Autowired
private MemberService memberService;
@GetMapping("/login")
public void loginView(){
}
@PostMapping("/login")
public String login(Member member, Model model){
Member findMember = memberService.getMember(member);
if(findMember != null
&& findMember.getPassword().equals(member.getPassword())){
model.addAttribute("member",findMember);
return "forward:getBoardList";
} else {
return "redirect:login";
}
}
@GetMapping("/logout")
public String logout(SessionStatus status){
status.setComplete();
return "redirect:/";
/*로그아웃하면 로그인페이지로 이동*/
}
}
@SessionAttribute :
SessionAttribute 어노테이션은 세션에 상태정보를 저장할때 사용하는데,
@SessionArribute 뒤에 ("member")라고 설정했기때문에
Model 객체 "member"라는 이름으로 저장된 데이터를 자동으로 세션에 등록한다
login() 메소드는 아이디가 설정된 Member객체와 의존성을 주입한
MemberService를 이용하여 회원정보를 검색한다.
그리고 검색결과를 세션에 "member"라는 이름으로 등록하고
비밀번호까지 체크한 후에 글 목록 화면으로 이동한다. 만약 특정아이디로
검색된 데이터가 없다면 null이 리턴되므로 다시 로그인화면으로 이동할 것이다
templates > login.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>회원 로그인</title>
</head>
<body th:align="center">
<h1>로그인</h1>
<form th:action="login" method="post">
<table th:align="center" border="1" th:cellspacing="0" th:cellpadding="0">
<tr>
<td bgcolor="orange" th:text="아이디"></td>
<td><input name="id" type="text" size="10"></td>
</tr>
<tr>
<td bgcolor="orange" th:text="비밀번호"></td>
<td><input name="password" type="password" size="10"></td>
</tr>
<tr>
<td colspan="2" align="center">
<input type="submit" value="로그인">
</td>
</tr>
</table>
</form>
</body>
</html>
domain > Member.java
package com.gosari.repick.domain;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import javax.persistence.Entity;
import javax.persistence.Id;
@Getter
@Setter
@ToString
@Entity
public class Member {
@Id
private String id;
private String password;
private String name;
private String role;
}
회원정보를 저장하는 MEMBER 테이블에는 회원과 관련된 칼럼(ID, PASSWORD, NAME, ROLE)들이 필요
해당 칼럼과 매핑될 변수들을 선언하고 롬복에서 제공하는 어노테이션 추가하여 설정 마무리
persistence > MemberRepository.java
package com.gosari.repick.persistence;
import com.gosari.repick.domain.Member;
import org.springframework.data.repository.CrudRepository;
public interface MemberRepository extends CrudRepository <Member, String>{
}
/*Memember엔티티를 이용하여 회원관련 CRUD 기능을 제공할 MeemberRepository 인터페이스 작성*/
service > MemberServiceImpl.java
package com.gosari.repick.service;
import com.gosari.repick.domain.Member;
import com.gosari.repick.persistence.MemberRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Optional;
@Service
public class MemberServiceImpl implements MemberService{
@Autowired
private MemberRepository memberRepo;
public Member getMember(Member member){
Optional<Member> findMember = memberRepo.findById(member.getId());
if(findMember.isPresent())
return findMember.get();
else return null;
}
}
MemberServiceImpl 클래스는 MemberRepository타입의 객체를 의존성 주입받는다.
MemberRepository의 findById()메소드를 통해 특정회원을 검색하여 리턴하고
만약 검색결과가 없으면 null을 리턴한다
service > MemberService.java
package com.gosari.repick.service;
import com.gosari.repick.domain.Member;
public interface MemberService {
Member getMember(Member member);
}
templates > getBoardList.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>게시글 목록</title>
</head>
<body th:align="center">
<h1>게시글 목록</h1>
<h3><font color="red" th:text="${session['member'].name}"></font>님 게시판 입장을 환영합니다 (^-^)/</h3>
<a th:href="@{/logout}">LOG_OUT</a> <!--로그아웃링크-->
<table th:align="center" border="1" th:cellpadding="0" th:cellspacing="0" th:width="1000">
<tr>
<th bgcolor="#f0ffff" width="100">번호</th>
<th bgcolor="#f0ffff" width="200">제목</th>
<th bgcolor="#f0ffff" width="150">작성자</th>
<th bgcolor="#f0ffff" width="150">등록일</th>
<th bgcolor="#f0ffff" width="100">조회수</th>
</tr>
<tr th:each="board, state : ${boardList}">
<td th:text="${state.count}">
<td><a th:href="@{/getBoard(seq=${board.seq})}" th:text="${board.title}"></a></td>
<td th:text="${board.writer}">
<td th:text="${#dates.format(board.createDate, 'yyyy-MM-dd')}">
<td th:text="${board.cnt}">
</tr>
</table>
<br>
<a th:href="@{/insertBoard}">새 글 등록</a>
</body>
</html>
세션에 저장된 사용자의 이름을 화면에 출력하기 위해 SpringEL을 사용
SpringEL은 스프링에서 제공하는 표현언어다. 세션에 'member'라는 이름으로
저장된 Member의 name변수에 접근하기 위해서는 ${session['member'].name} 표현을 사용
templates > getBoard.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html"; charset="UTF-8">
<title>게시글 상세</title>
</head>
<body th:align="center">
<h1>게시글 상세</h1>
<form th:action="@{updateBoard}" method="post">
<input name="seq" type="hidden" th:value="${board.seq}">
<!--수정할게시글번호-->
<table th:align="center" border="1" th:cellpadding="0" th:cellspacing="0">
<tr>
<td bgcolor="orange" th:text="제목" width="80"></td>
<td><input name="title" type="text" th:value="${board.title}"></td>
<!--제목수정-->
</tr>
<tr>
<td bgcolor="orange" th:text="작성자"></td>
<td th:text="${board.writer}"></td>
</tr>
<tr>
<td bgcolor="orange" th:text="내용">
<td>
<textarea name="content" th:text="${board.content}" cols="40" rows="10"></textarea>
</td>
</tr>
<tr>
<td bgcolor="orange" th:text="등록일"></td>
<td th:text="${#dates.format(board.createDate, 'yyyy-MM-dd')}"></td>
</tr>
<tr>
<td bgcolor="orange" th:text="조회수"></td>
<td th:text="${board.cnt}"></td>
</tr>
<tr>
<td colspan="2" align="center">
<input type="submit" value="게시글수정">
</td>
</tr>
</table>
</form>
<a th:href="@{/insertBoard}">글등록</a>
<a th:href="@{/getBoardList}">글목록</a>
<a th:if="${session['member'].role == 'ROLE_ADMIN'}"
<a th:href="@{/deleteBoard(seq=${board.seq})}">글삭제</a>
<!--글삭제링크를 클릭했을때 삭제할 게시글의 일련번호를 파라미터로 전달-->
</body>
</html>
게시글 상세화면에서도 관리자권한을(ROLE_ADMIN)가진 사용자만 글삭제 할 수 있도록 설정
세션에 저장된 회원객체의 권한을 확인할때도 SpringEL을 사용한다.
따라서 Member객체의 role 변수값이 "ROLE_ADMIN"인지 확인하려면
${session['member'].role == 'ROLE_ADMIN'} 표현사용
contoller > BoardController.java
package com.gosari.repick.controller;
import com.gosari.repick.domain.Member;
import org.springframework.stereotype.Controller;
import org.springframework.beans.factory.annotation.Autowired;
import com.gosari.repick.service.BoardService;
import com.gosari.repick.domain.Board;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@SessionAttributes("member")
@Controller
public class BoardController {
@Autowired
private BoardService boardService;
@ModelAttribute("member")
public Member setMember(){
return new Member();
} //*추가
/*---------READ---------*/
//게시글상세
@GetMapping("/getBoard")
public String getBoard(@ModelAttribute("member")Member member, Board board, Model model){
if(member.getId() == null){
return "redirect:login";
} //*추가
model.addAttribute("board", boardService.getBoard(board));
return "getBoard";
}
//게시글목록
@RequestMapping("/getBoardList")
public String getBoardList(@ModelAttribute("member")Member member, Model model, Board board){
if(member.getId() == null){
return "redirect:login";
} //*추가
List<Board> boardList = boardService.getBoardList(board);
model.addAttribute("boardList", boardList);
return "getBoardList";
}
/*---------CREATE---------*/
//게시글등록
@GetMapping("/insertBoard")
public String insertBoardView(@ModelAttribute("member") Member member){
if(member.getId() == null){
return "redirect:login";
} //*추가
return "insertBoard";
}
//게시글등록
@PostMapping("/insertBoard")
public String insertBoard(Board board){
boardService.insertBoard(board);
return "redirect:getBoardList";
}
/*---------UPDATE---------*/
//게시글수정
@PostMapping("/updateBoard")
public String updateBoard(@ModelAttribute("member")Member member, Board board){
if(member.getId() == null){
return "redirect:login";
} //*추가
boardService.updateBoard(board);
return "forward:getBoardList";
}
/*---------DELETE---------*/
//게시글삭제
@GetMapping("/deleteBoard")
public String deleteBoard(@ModelAttribute("member")Member member,Board board){
if(member.getId() == null){
return "redirect:login";
} //*추가
boardService.deleteBoard(board);
return "forward:getBoardList";
}
}
로그인에 성공한 사용자만 게시판 기능을 사용할수있도록 제어
클래스선언부에 @SessionAttribute("member")를 추가했기때문에
member라는 이름으로 Model에 저장한 데이터는 자동으로 세션에 등록된다.
setMember()메소드를 추가해서 Member객체 리턴
setMember()메소드위에 @ModelAttribute("member")를 선언했기 때문에
setMember()메소드가 리턴한 Member객체가 가장먼저 세션에 등록
getBoardList() 메소드는 매개변수로 @ModelAttribute("member")를 사용해
세션에 등록된 member라는 이름이 객체를 member변수에 바인딩했다
그러면 getBoardList() 메소드 안에서 member변수에 바인딩된 객체의 아이디 존재여부를 통해
로그인 여부를 판단할수있다.
즉 로그인아이디가 확인되면 글목록화면을 제공하고 그렇지않으면 로그인화면으로 리다이렉트하도록 처리
예외처리
exception > BoardException.java
package com.gosari.repick.exception;
public class BoardException extends RuntimeException{
private static final long serialVersionUID = 1L;
public BoardException(String message){
super(message);
}
}
게시판 프로그램에서 발생할수있는 모든 예외를 표현하기위해 BoardException 생성
체크드예외 - 컴파일시점에서 발생하는 예외
언체크드예외 - 컴파일은 통과하지만 실행시점에서 발생하는예외
모든예외의 최상위 부모로 사용할 BoardException 클래스를 RuntimeException을 상속하여 구현
exception > BoardNotFoundException.java
package com.gosari.repick.exception;
public class BoardNotFoundException extends BoardException{
private static final long serialVersionUID = 1L;
public BoardNotFoundException(String message) {
super(message);
}
}
static > index.html
<!-- 웰컴 파일 -->
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html"; charset="UTF-8">
<title>메인페이지</title>
</head>
<body>
<br>
<h3 align="center">게시판 프로그램입니다(^-^)/</h3>
<hr>
<p align="center"> <a href="getBoardList">글목록 바로가기</a> </p>
<p align="center"> <a href="login">로그인</a> </p>
<br>
<!--강제오류발생링크-->
<p align="center">
<a href="boardError">BoardException 발생</a>
<a href="illegalArgumentError">illegalArgumentException 발생</a>
<a href="sqlError">SQLException 발생</a>
</p>
<hr>
</body>
</html>
controller > ExceptionController.java
package com.gosari.repick.controller;
import com.gosari.repick.exception.BoardNotFoundException;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import java.sql.SQLException;
@Controller
public class ExceptionController {
@ExceptionHandler(SQLException.class)
public String numberFormatError(SQLException exception, Model model){
model.addAttribute("exception", exception);
return "/errors/sqlError";
}/*예외처리 재정의*/
@RequestMapping("/boardError")
public String boardError(){
throw new BoardNotFoundException("검색된 게시글이 없습니다");
}
@RequestMapping("/illegalArgumentError")
public String illegalArgumentError(){
throw new IllegalArgumentException("부적절한 인자가 전달되었습니다");
}
@RequestMapping("/sqlError")
public String sqlError() throws SQLException{
throw new SQLException("SQL구문에 오류가 있습니다");
}
}
exception > GrobalExceptionHandler.java
package com.gosari.repick.exception;
import com.gosari.repick.exception.BoardException;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BoardException.class)
public String handleCustomException(BoardException exception, Model model){
model.addAttribute("exception", exception);
return "/errors/boardError";
}
@ExceptionHandler(Exception.class)
public String handleException(Exception exception, Model model){
model.addAttribute("exception", exception);
return "/errors/globalError";
}
}
@ContrillerAdvice 어노테이션을 GrobalExceptionHandler 클래스에 선언함으로써
컨트롤러에서 발생하는 모든 예외를 GrobalExceptionHandler 객체가 처리하도록 했다
그리고 발생된 예외타입에따라 다양한 화면이 처리되도록 @ExceptionaHandler를 가진 메소드를 여러개 선언
handleCustomeException메소드는 BoardException타입의 예외발생시 동작
따라서 BoardNotFoundException이 발생했을때 동작
handleException은 모든예외의 최상위 부모인 Exception타입의 객체를 처리하는 메소드
BoardException타입의 예외를 제외한 나머지 모든 예외는 handleException메소드가 처리
templates > errors > boardError.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>예외</title>
</head>
<body>
<h1><font color="green">BoardException 발생!</font></h1>
<a th:href="@{/}">메인화면으로</a><hr>
<table>
<tr>
<th bgcolor="green" align="left">예외메세지: [[${exception.message}]]</th>
</tr>
<tr th:each="trace : ${exception.stackTrace}">
<td th:text="${trace}">
</tr>
</table>
</body>
</html>
templates > errors > grobalError.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>예외</title>
</head>
<body>
<h1><font color="orange">Exception 발생!</font></h1>
<a th:href="@{/}">메인화면으로</a><hr>
<table>
<tr>
<th bgcolor="orange" align="left">예외메세지: [[${exception.message}]]</th>
</tr>
<tr th:each="trace : ${exception.stackTrace}">
<td th:text="${trace}">
</tr>
</table>
</body>
</html>
templates > errors > sqlErrorhtml
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>예외</title>
</head>
<body>
<h1><font color="red">SQLException 발생!</font></h1>
<a th:href="@{/}">메인화면으로</a><hr>
<table>
<tr>
<th bgcolor="red" align="left">예외메세지: [[${exception.message}]]</th>
</tr>
<tr th:each="trace : ${exception.stackTrace}">
<td th:text="${trace}">
</tr>
</table>
</body>
</html>
<!--예외처리재정의-->
SQL문 작성 - 비어있는 MEMBER 테이블에 튜플 생성 (데이터 등록)
INSERT INTO MEMBER VALUES
('member','둘리', 'member111','ROLE_USER');
INSERT INTO MEMBER VALUES
('member2', '도우너', 'member222', 'ROLE_ADMIN');
출처 : 누구나끝까지따라할수있는 QuickStart 스프링부트
'STUDY > SpringBoot' 카테고리의 다른 글
[Springboot] 게시판따라하기(2) - 게시글 리스트 ✔정리 (0) | 2022.08.09 |
---|---|
[Springboot] 게시판따라하기(1) - 게시글 작성폼생성·작성처리 ✔정리 (0) | 2022.08.09 |
[SpringBoot] IntelliJ + OracleDB 게시판 생성 ✔정리(1) (0) | 2022.08.07 |
[SpringBoot] 22-08-04 자바와 html을 이용한 게시판예제 (2) ☑ (0) | 2022.08.07 |
[SpringBoot] 스프링부트 Annotation 정리✔ (0) | 2022.08.05 |