에러 페이지
ErrorController.java
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.http.HttpServletRequest;
@Controller
public class ErrorController {
@GetMapping("/error")
public String error() {
return "error";
}
}
error.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>ERROR</h1>
문제 발생
code : [[${code}]]
</body>
</html>
로그인 한 사람으로 사용자 제한
IndexService.java
public int write(Map<String, Object> map) {
// DB에 있는 mid, ip 추가하기
// map.put("mtcate", map.get("cate"));
map.put("mid", util.getSession().getAttribute("mid")); // 로그인 한 사용자의 mid
map.put("ip", util.getIP());
return indexDAO.write(map);
}
}
IndexController.java
@GetMapping("/write")
public String write() {
return "redirect:/login?error=2077"; // 로그인 안 한 사람이 write치고 들어오면 에러 페이지로
}
@PostMapping("/write")
public String write(@RequestParam Map<String, Object> map, HttpServletRequest request) {
// String ip = util.getIP();
// 2024-03-11 로그인 검사
if(util.getSession().getAttribute("mid") != null) {
int result = indexService.write(map);
return "write";
} else {
return "redirect:/login";
}
}
// 이렇게 작성도 가능함.
String url = "freeboard";
return "redirect:/" + url;
board.html
로그인 한 사람만 글 쓰도록 th:if="${session.mid ne null}"
<div th:if="${session.mid ne null}">
<button type="button" th:with="cate=${board[0].mtcate}" class="btn btn-dark" th:onclick="|location.href='@{/write(cate=${cate})}'|">글쓰기</button>
<button type="button" class="btn btn-dark" th:onclick="|location.href='@{/write(cate=${param.cate})}'|">글쓰기</button>
</div>
board.html 전체 코드
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{menu.html :: head}" />
</head>
<body id="page-top">
<!-- Navigation-->
<th:block th:insert="~{menu.html :: menu}" />
<br>
<br>
<!-- 2024-03-08 -->
<aside class="text-center">
<div class="container px-5">
<span>[[${#lists.size(board)}]]개 글이 있습니다</span>
<div th:if="${#lists.size(board) le 0}">
<h2>출력할 데이터가 없습니다.</h2>
<h3>관리자에게 문의하세요.</h3>
</div>
<div th:unless="${#lists.size(board) le 0}">
<div class="table table-hover">
<div class="row" th:each="row : ${board }">
<div class="col-1" th:text="${row.mtno }"></div>
<div class="col-6 text-start" th:onclick="|location.href='@{/detail(no=${row.mtno})}'|">[[${row.mttitle }]]</div>
<div class="col-2" th:text="${row.mname }"></div>
<div class="col-2" th:text="${row.mtdate }"></div>
<div class="col-1" th:text="${row.mtread }"></div>
</div>
</div>
</div>
<div th:if="${session.mid ne null}">
<button type="button" th:with="cate=${board[0].mtcate}" class="btn btn-dark" th:onclick="|location.href='@{/write(cate=${cate})}'|">글쓰기</button>
<button type="button" class="btn btn-dark" th:onclick="|location.href='@{/write(cate=${param.cate})}'|">글쓰기</button>
</div>
</div>
</aside>
<!-- footer -->
<th:block th:insert="~{menu.html :: footer}" />
</body>
</html>
menu.html
<th:block th:if="${session.mid ne null}">
<li class="nav-item"><a class="nav-link me-lg-3" href="/myInfo">[[${session.mname}]]</a></li>
<li class="nav-item"><a class="nav-link me-lg-3" href="/logout">LOGOUT</a></li>
</th:block>
<th:block th:unless="${session.mid ne null}">
<li class="nav-item"><a class="nav-link me-lg-3" href="/login">LOGIN</a></li>
</th:block>
메뉴 만들기
AdminController.java
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/admin")
public class AdminController {
@GetMapping("/menu")
public String menu() {
return "admin/menu";
}
}
menu.html (admin)
* 경로 : templates/admin (폴더 생성)/menu.html
DB 새로 생성
CREATE TABLE `menu` (
`cate` INT(11) NULL DEFAULT NULL,
`catename` VARCHAR(50) NULL DEFAULT NULL COLLATE 'utf8mb4_general_ci',
`comment` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8mb4_general_ci',
UNIQUE INDEX `cate` (`cate`) USING BTREE
)
COMMENT='화면 상단에 보여줄 메뉴\r\n멀티보드를 활용해서 mtcate 값만 바꿔주면 됨.\r\ncate = unique (카테고리 중복 피하도록, 겹치지 않음)'
COLLATE='utf8mb4_general_ci'
ENGINE=InnoDB
;
AdminController
@Controller
@RequestMapping("/admin")
public class AdminController {
@Autowired
private AdminService adminService;
AdminService
@Service
public class AdminService {
@Autowired
private AdminDAO adminDAO;
}
AdminDAO (인터페이스)
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
@Repository
@Mapper
public interface AdminDAO {
}
AdminController
@GetMapping("/menu")
public String menu(Model model) {
List<Map<String, Object>> menu = adminService.menu();
model.addAttribute("menu",menu);
return "admin/menu";
}
AdminService
@Service
public class AdminService {
@Autowired
private AdminDAO adminDAO;
public List<Map<String, Object>> menu() {
return adminDAO.menu();
}
}
AdminDAO (인터페이스)
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
@Repository
@Mapper
public interface AdminDAO {
List<Map<String, Object>> menu();
}
adminMapper.xml
아까 만들어준 menu 테이블의 속성 SELECT 하기.
<mapper namespace="com.example.web.dao.AdminDAO">
<select id="menu" resultType="Map">
SELECT cate, catename, comment
FROM menu
</select>
</mapper>
IndexController
메뉴도 불러오고, 보드도 불러옴
@GetMapping({ "/index", "/" })
public String index(Model model) {
// 메뉴 불러오기
List<Map<String, String>> menu = indexService.menu();
model.addAttribute("menu", menu);
return "index";
}
@GetMapping("/freeboard")
public String freeboard(@RequestParam(value="cate", defaultValue="1") int cate, Model model) {
// 메뉴 불러오기
List<Map<String, Object>> menu = indexService.menu();
model.addAttribute("menu", menu);
List<BoardDTO> board = indexService.freeboard(cate);
model.addAttribute("board", board);
return "board";
}
IndexService
public List<Map<String, Object>> menu() {
return indexDAO.menu();
}
IndexDAO
List<Map<String, Object>> menu();
indexMapper.xml
<select id="menu" resultType="Map">
SELECT cate, catename
FROM menu
</select>
관리자 메뉴 페이지
Admin/menu.html
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>admin MENU</title>
</head>
<body>
<h1>관리자 메뉴페이지~</h1>
<table border="1">
<tr>
<th>카테고리 번호</th>
<th>카테고리 이름</th>
<th>비고</th>
</tr>
<tr th:each="m : ${menu}">
<td th:text="${m.cate}"></td>
<td th:text="${m.catename}"></td>
<td th:text="${m.comment}"></td>
</tr>
</table>
<br>
<form action="menu" method="post">
<input type="number" name="cate" placeholder="카테고리 번호">
<input type="text" name="catename" placeholder="카테고리 이름">
<input type="text" name="comment" placeholder="카테고리 설명">
<button type="submit">추가하기</button>
</form>
</body>
</html>
AdminController
@PostMapping("/menu")
public String menu(@RequestParam Map<String, Object> map) {
// System.out.println(map);
adminService.menuInsert(map);
return "redirect:/admin/menu";
}
}
-> menuInsert 생성 (서비스, DAO)
AdminService
public void menuInsert(Map<String, Object> map) {
adminDAO.menuInsert(map);
}
AdminDAO
void menuInsert(Map<String, Object> map);
adminMapper
mapper 작성 후 메뉴 추가 가능
<insert id="menuInsert" parameterType="Map">
INSERT INTO menu VALUES(#{cate}, #{catename}, #{comment})
</insert>
IndexController
@GetMapping({"/index", "/"})
public String index(Model model) {
List<Map<String, Object>> menu = indexService.menu();
model.addAttribute("menu", menu);
return "index";
}
application.properties
# session time out = 1800 -> 30분
server.servlet.session.timeout=1800
#### error page
# error page에 exception 정보 포함? true/false
server.error.include-exception=true
# error page에 stacktrace 포함? ALWAYS / NEVER / ON_PARAM
server.error.include-stacktrace=ALWAYS
# 기본 노출 페이지
server.error.whitelabel.enabled=true
# error 응답 처리 할 path
# server.error.path=/error
삭제하기 postDel
IndexController
@PostMapping("/postDel")
public String postDel(@RequestParam("no") int no) {
// System.out.println(no);
int result = indexService.postDel(no);
return "redirect:/freeboard";
}
IndexService
public int postDel(int no) {
return indexDAO.postDel(no);
}
IndexDAO
int postDel(int no);
indexMapper
<update id="postDel" parameterType="int">
UPDATE multiboard SET mtdel=0
WHERE mtno=#{no}
</update>
detail.html
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script src="https://unpkg.com/sweetalert/dist/sweetalert.min.js"></script>
<script type="text/javascript">
function del(no){
Swal.fire({
title: "삭제합니까?",
text: "해당 내용을 삭제합니다. 복구가 불가능합니다.",
icon: "warning",
showCancelButton: true,
confirmButtonColor: "#3085d6",
cancelButtonColor: "#d33",
confirmButtonText: "삭제"
}).then((result) => {
if (result.isConfirmed) {
//Swal.fire({title: "삭제를 선택했습니다.",text: "삭제합니다.",icon: "success"});
//location.href="/postDel?no="+no;
let form = document.createElement('form');
form.setAttribute('method','post');
form.setAttribute('action','/postDel');
let input = document.createElement('input');
input.setAttribute('type','hidden');
input.setAttribute('name','no');
input.setAttribute('value', no);
form.appendChild(input);
document.body.appendChild(form);
form.submit();
}
});
}
</script>
</head>
<body id="page-top">
<!-- Navigation-->
<th:block th:insert="~{menu.html :: menu}"></th:block>
<!-- Mashead header-->
<!-- Quote/testimonial aside-->
<aside class="text-center">
<div class="container px-5">
<div class="p-3 mt-5 mb-2 rounded" style="background-color: #FAFAFA">
<div class="border-bottom">
<h3 th:text="${detail.mttitle}"></h3>
</div>
<div class="border-bottom" style="background-color: #c0c0c0">
<div class="row">
<div class="col-6 text-start">
[[${detail.mname }]]님
<i class="bi bi-pencil-fill" th:id="${detail.mtno }" onclick="update(this.id)"></i>
<i class="bi bi-trash2-fill" th:id="${detail.mtno }" onclick="del(this.id)"></i>
</div>
<div class="col-6 row text-end">
<div class="col-7" th:text="${detail.mtread }"></div>
<div class="col-5" th:text="${detail.mtip }"></div>
</div>
</div>
</div>
<div class="mt-3 text-start" th:utext="${detail.mtcontent }" style="min-height: 300px; height: auto"></div>
</div>
<a href="/index" class="btn">게시판으로</a>
</div>
</aside>
indexMapper.xml
<update id="postDel" parameterType="int">
UPDATE multiboard SET mtdel=0
WHERE mtno=#{no}
</update>
수정하기 UPDATE
update.html
th:value="${update.mttitle}"
textarea에 [[ ${update.mtcontent} ]] 추가
<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
<th:block th:insert="~{menu.html :: head}" />
</head>
<body>
<th:block th:insert="~{menu.html :: menu}" />
<br>
<br>
<aside class="text-center">
<div class="container px-5">
<br> <br>
<h1>글 수정하기</h1>
<!-- 카테고리, 글번호, 페이지번호 -->
들어온 카테고리 : [[${param.cate}]]<br> [[${update}]]
<form action="/postUpdate" method="post">
<div class="form-group col-md-6 d-flex align-items-center justify-content-center">
<input type="text" id="title" name="title" class="form-control" th:value="${update.mttitle}" placeholder="제목" aria-label="제목">
</div>
<div class="form-group col-md-6 d-flex align-items-center justify-content-center">
<textarea id="content" name="content" class="form-control" aria-label="With textarea" style="height: 300px;">[[${update.mtcontent}]]</textarea>
</div>
<div class="form-group col-md-12">
<input type="hidden" name="mtno" th:value="${update.mtno}"> <input type="hidden" name="mtcate" th:value="${update.mtcate}">
<button type="submit" class="btn btn-dark">글쓰기</button>
</div>
</form>
<input type="hidden" name="mtcate" th:value="${param.cate}">
</div>
</aside>
BoardDTO 삭제 후 내용들 변경
-> mapper에서 BoardDTO를 Map으로 변경
-> IndexController, IndexService, IndexDAO : DTO 자리에 **Map<String, Object>** 으로 교체
@PostMapping("/postUpdate")
public String postUpdate(@RequestParam("no") int no, Model model) {
if(util.getSession().getAttribute("mid") != null) {
// 메뉴 불러오기
List<Map<String, Object>> menu = indexService.menu();
model.addAttribute("menu", menu);
Map<String, Object> update2 = indexService.detail(no);
model.addAttribute("update", update2);
return "update";
} else {
return "redirect:/login";
}
}
@PostMapping("/postUpdate")
public String postUpdate(@RequestParam() Map<String, Object> map) {
indexService.postUpdate(map);
return "redirect:/detail?no="+ map.get("mtno");
}
}
IndexService.java
public void postUpdate(Map<String, Object> map) {
map.put("mid", util.getSession().getAttribute("mid"));// 본인 맞는지
indexDAO.postUpdate(map);
}
IndexDAO
void postUpdate(Map<String, Object> map);
indexMapper.xml
<update id="postUpdate" parameterType="Map">
UPDATE multiboard SET mttitle=#{title}, mtcontent=#{content}
WHERE mtno=#{mtno} AND mno=(SELECT mno FROM member WHERE mid=#{mid})
</update>
detail.html
[[${detail.mname }]]님
<i class="bi bi-pencil-fill" th:id="${detail.mtno }" onclick="update(this.id)">
<i class="bi bi-trash2-fill" th:id="${detail.mtno }" onclick="del(this.id)">
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script src="https://unpkg.com/sweetalert/dist/sweetalert.min.js"></script>
<script type="text/javascript">
function update(no){
Swal.fire({
title: "수정합니까?",
text: "해당 내용을 수정합니다.",
icon: "warning",
showCancelButton: true,
confirmButtonColor: "#3085d6",
cancelButtonColor: "#d33",
confirmButtonText: "수정"
}).then((result) => {
if (result.isConfirmed) {
let form = document.createElement('form');
form.setAttribute('method','get');
form.setAttribute('action','/postUpdate');
let input = document.createElement('input');
input.setAttribute('type','hidden');
input.setAttribute('name','no');
input.setAttribute('value', no);
form.appendChild(input);
document.body.appendChild(form);
form.submit();
}
});
}
<!-- Quote/testimonial aside-->
<aside class="text-center">
<div class="container px-5">
<div class="p-3 mt-5 mb-2 rounded" style="background-color: #FAFAFA">
<div class="border-bottom">
<h3 th:text="${detail.mttitle}"></h3>
</div>
<div class="border-bottom" style="background-color: #c0c0c0">
<div class="row">
<div class="col-6 text-start">
[[${detail.mname }]]님
<i class="bi bi-pencil-fill" th:id="${detail.mtno }" onclick="update(this.id)"></i>
<i class="bi bi-trash2-fill" th:id="${detail.mtno }" onclick="del(this.id)"></i>
</div>
<div class="col-6 row text-end">
<div class="col-7" th:text="${detail.mtread }"></div>
<div class="col-5" th:text="${detail.mtip }"></div>
</div>
</div>
</div>
<div class="mt-3 text-start" th:utext="${detail.mtcontent }" style="min-height: 300px; height: auto"></div>
</div>
<a href="/index" class="btn">게시판으로</a>
</div>
</aside>
freeboard 수정
IndexController
List<Map<String, String>> menu = indexService.menu();
Map에서 String, String임. (서비스, DAO에서도 String, String으로 바꿔야 함.)
@GetMapping("/freeboard")
public String freeboard(@RequestParam(value="cate", defaultValue="1") int cate, Model model) {
// 메뉴 불러오기
List<Map<String, String>> menu = indexService.menu();
model.addAttribute("menu", menu);
List<Map<String, Object>> board = indexService.freeboard(cate);
model.addAttribute("board", board);
return "board";
}
IndexService
변수 없는 freeboard는 삭제 (dao에서도)
public List<Map<String, Object>> freeboard(int cate) {
return indexDAO.freeboard(cate);
}
public List<Map<String, String>> menu() {
return indexDAO.menu();
}
IndexDAO
List<Map<String, Object>> freeboard(int cate);
List<Map<String, String>> menu();
로그인 안됐던 이유
MemberController
추가 내용 :
- @Autowired : private IndexService indexService;
- @getmapping("/login")의 로그인 매개변수에 모델 추가 = login(Model model)
- @postmapping에서
map으로 바꿨던 걸 result 로 변경함.
import java.util.List;
import java.util.Map;
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.RequestParam;
import com.example.web.service.IndexService;
import com.example.web.service.MemberService;
import com.example.web.util.Util;
import jakarta.servlet.http.HttpSession;
@Controller
public class MemberController {
@Autowired
private MemberService memberService;
@Autowired
private IndexService indexService;
@Autowired
private Util util;
@GetMapping("/login") // 화면만 보여줌
public String login(Model model) {
// 메뉴 불러오기
List<Map<String, String>> menu = indexService.menu();
model.addAttribute("menu", menu);
return "login";
}
@PostMapping("/login") // 실제 로그인 작업
public String login2(@RequestParam Map<String, Object> map) {
System.out.println(map); // 콘솔 출력 : {id=test, pw=01234567}
Map<String, Object> result = memberService.login(map); // 받아노는 값이 하나가 아니니까 map 에 넣어줘
System.out.println(result);
if (util.str2Int(result.get("count")) == 1) { // mapper에서 오는 count(*) 의 별칭
// 정상 로그인 -> 세션 -> board 이동
HttpSession session = util.getSession();
session.setAttribute("mid", map.get("id"));
session.setAttribute("mname", result.get("mname"));
return "redirect:/freeboard";
} else {
// 로그인 불가
return "redirect:/login";
}
}
@GetMapping("/logout")
public String logout(HttpSession session) {
if (session.getAttribute("mid") != null) {
session.removeAttribute("mid");
}
if (session.getAttribute("mname") != null) {
session.removeAttribute("mname");
}
session.invalidate();
return "redirect:/login";
}
}
AOP
- AOP (Aspect-Oriented Programming) = 관점 지향 프로그래밍
build.gradle
implementation 'org.springframework.boot:spring-boot-starter-aop'
-> 코드 추가 후 gradle update
AOPConfig
@Aspect = 어드바이스(Advice), 포인트컷(Pointcut), 및 인트로덕션(Introduction) 등을 정의 가능
package com.example.web.util;
import java.util.Arrays;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Component;
@Aspect // 어드바이스(Advice), 포인트컷(Pointcut), 및 인트로덕션(Introduction) 등을 정의 가능
@Component
public class AOPConfig {
@Pointcut("execution(* com.example.web.controller.*.*(..))")
public void cut() {
// 포인트컷 표현식이 필요한 경우 여기에 정의
}
@Before("cut()")
public void before(JoinPoint joinPoint) {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
System.out.println("시작할 때 : " + methodSignature.getName()); // 실행 메소드명
System.out.println("시작할 때 : " + methodSignature.getMethod());
// 파라미터
Object[] args = joinPoint.getArgs();
System.out.println(Arrays.toString(args));
// 파라미터 배열의 종류 값
for (Object object : args) {
System.out.println("파라미터 타입 : " + object.getClass().getSimpleName());
System.out.println("파라미터 값 : " + object);
}
}
@After("cut()")
public void after(JoinPoint joinPoint) {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
// 실행되는 함수 이름을 가져오고 출력
System.out.println(methodSignature.getName() + " 메소드가 종료됨");
}
}
'개발 공부 Today I Learned' 카테고리의 다른 글
[국비 76일차 TIL] 공공데이터포털 API, jsoup, JPA (0) | 2024.03.13 |
---|---|
[국비 75일차 TIL] 전자정부 프레임워크 스프링 파일 업로드 fileUp (0) | 2024.03.12 |
[국비 73일차 TIL] 전자정부 프레임워크 스프링 notice write login DB (0) | 2024.03.08 |
[국비 72일차 TIL] 전자정부 프레임워크 스프링 프로젝트 (0) | 2024.03.07 |
[국비 71일차 TIL] 스프링 레거시 & 스프링 부트 설정, 타임리프 (0) | 2024.03.06 |
댓글