스프링23_공지사항, 전체선택, 관리자 접근 제한

  • 공지사항 작성(+확인), 전체선택, 삭제
  • 없는 아이디여도 에러 안나게
  • 인터셉터(관리자레벨이 아니면 접근 ㄴㄴ)

✨인터셉터, 관리자 접근

NoticeInterceptor

//인터셉터 만드는 방법 2가지
//1. extend HandlerInterceptorAdapter
//2. implements HandlerInterceptor
public class NoticeInterceptor extends HandlerInterceptorAdapter {

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		
		MemberDto loginUser = (MemberDto) request.getSession().getAttribute("loginUser");
		
		//관리자인지 확인 (레벨로 하면 더 좋음, 여기서는 그냥 아이디로)
		if("admin".equals(loginUser.getUserId())) {
			return true;
		}else {
			//홈으로 보내버리기, app999
			//response.sendRedirect(request.getServletContext().getContextPath());
			request.setAttribute("msg", "관리자 계정이 아닙니다.");
			request.getRequestDispatcher("/WEB-INF/views/error/errorPage.jsp").forward(request, response);
			return false;
		}
		
	}
}


ErrorProcessor

@ControllerAdvice(annotations = Controller.class)
@Slf4j
public class ErrorProcessor {

	@ExceptionHandler(Exception.class)
	public String errorProcess(Exception e) {
		//서버도 예외를 알게하기
		log.error(e.toString());
		log.error(e.getMessage());
		e.printStackTrace();
		return "error/exception";
	}
}


ErrorPage.jsp

<body>

	<%@ include file="/WEB-INF/views/common/header.jsp" %>

	<h1>에러페이지~~</h1>
	<h2 id="msg">${msg}</h2>
	
	<script type="text/javascript">
		console.log(document.getElementById('msg').innerText);	//메세지 띄우기
		alert(document.getElementById('msg').innerText);	//메세지 띄우기
		window.location.href = "${root}";	//홈화면으로 보내기
	</script>
	
</body>


servlet-context.xml

	<!-- interceptor 등록 -->
	<interceptors>
		<interceptor>
			<mapping path="/notice/**"/>
			<!-- list 만 제외, 일반회원은 list 볼 수 있어야 하니까 -->
			<exclude-mapping path="/notice/list/**"/>
			<beans:bean class="com.kh.app999.interceptor.NoticeInterceptor"></beans:bean>
		</interceptor>
	</interceptors>



페이징

PageVo




C,S,D

MemberServiceImpl

@Service
@Slf4j
@Transactional
public class MemberServiceImpl implements MemberService {

	@Autowired
	private MemberDao dao;
	
	@Autowired
	private PasswordEncoder pe;
	
	@Override
	public int join(MemberDto dto, HttpServletRequest req) throws Exception{
		//회원가입 처리
		
		//회원번호, 시퀀스 nextval
		int no = dao.getMemberSeq();
		//insert 처리
		dto.setUserNo(no);
		//암호화
		dto.setUserPwd(pe.encode(dto.getUserPwd()));
		log.info(dto.toString());
		int result = dao.insertMember(dto);
		
		
		//////////////////////
		
		//파일 업로드(우리 서버에)
		MultipartFile f = dto.getF();
		
		if(!f.isEmpty()) {
			//원래 이름	-> db에 저장
			//변경된 이름 -> db에 저장
			//서비스 레이어에서 작업하도록
			String changeName = System.currentTimeMillis()+"_"+f.getOriginalFilename();
			dto.setChangeName(changeName);
			//사이즈, 타입
			System.out.println("===============");
			System.out.println(f.getOriginalFilename());
			System.out.println(f.getSize());
			System.out.println(f.getContentType());
			System.out.println("===============");
			
			//파일을 톰캣에 저장
			String path = req.getServletContext().getRealPath("/resources/upload/profile/");
			//파일을 서버에 저장
			//getREalPath == /spring999prjFinal/src/main/webapp
//			File file = new File("D:/uploadForSpring/999prj/profile/"+ changeName);
			File file = new File(path+ changeName);
			f.transferTo(file);
			
			//db에 insert
			dao.uploadProfile(dto);
		}
		
		return result;
	}

	@Override
	public MemberDto login(MemberDto dto) throws Exception {
		//DB에서 회원 정보 조회(기준:id)
		MemberDto dbUser = dao.getMember(dto);
		//없는 id여도 에러 안나게
		if(dbUser == null) {
			return null;
		}
		
		//비번 일치 체크
		if(pe.matches(dto.getUserPwd(), dbUser.getUserPwd())) {
			//일치함-> 멤버 리턴
			return dbUser;
		}else {
			//불일치-> null 리턴
			return null;
		}
	}

	@Override
	public MemberDto editMember(MemberDto dto) throws Exception {
		//비밀번호 한번 더 확인..근데 여기선 패스
		//비번을 입력했을 때만 수정 가능!!
		if(dto.getUserPwd().length()>0) {
			dto.setUserPwd(pe.encode(dto.getUserPwd()));
		}
		int result = dao.updateMember(dto);
		MemberDto m = null;
		if(result > 0) {
			m = dao.getMember(dto);
		}
		return m;
	}

}


NoticeController

@Controller
@RequestMapping("notice")
@Slf4j
public class NoticeController {
	
	@Autowired
	private NoticeService service;

	//공지사항 화면 보여주기
	@GetMapping(value = "/list")
		public String list(Model model) throws Exception {
		//리스트 조회
		List<NoticeVo> list = service.getNoticeList();
		model.addAttribute("list", list);
		return "notice/list";
	}
	
	//공지사항 작성 화면 보여주기(디비가서 리스트 조회)
	@GetMapping("write")
	public String write() {
		
		return "notice/write";
	}
	
	//공지사항 작성 로직 처리
	@PostMapping("write")
	public String write(NoticeVo vo, HttpServletRequest req) throws Exception {
		System.out.println(vo);
		
//		한번에 많이 만들때 이렇게
//		for(int i=0; i<500; i++) {
//			service.write(vo);
//		}
		
		int result = service.write(vo);
		if(result>0) {
			return "redirect:/notice/list";
		}else {
			req.setAttribute("msg", "공지사항 작성 실패");
			return "error/errorPage";
		}
	}
	
	//공지사항 삭제
	@PostMapping("delete")
	@ResponseBody
	public String delete(String str) throws Exception {
		System.out.println(str);	// 삭제할 번호들: 1, 5, ...
		System.out.println(str.length()/2);	// 
		
		int result = service.deleteNotice(str);
		
		log.warn("선택한 row 개수 : {}", result);
		
		if(result == str.length()/2) {
			return "ok";
		}else {
			return "fail_" + result;
		}
		
	}	
	
}


NoticeService 인페

public interface NoticeService {

	int write(NoticeVo vo) throws Exception;

	List<NoticeVo> getNoticeList() throws Exception;

	int deleteNotice(String str) throws Exception;

}


NoticeServiceImpl

@Service
public class NoticeServiceImpl implements NoticeService{

	@Autowired
	private NoticeDao dao;
	
	@Override
	public int write(NoticeVo vo) throws Exception {
		
		return dao.write(vo);
	}

	@Override
	public List<NoticeVo> getNoticeList() throws Exception {
		return dao.getNoticeList();
	}

	@Override
	public int deleteNotice(String str) throws Exception {
		String[] delArr = str.split(",");
		return dao.deleteNotice(delArr);
	}

}


NoticeDao 인페

public interface NoticeDao {

	int write(NoticeVo vo) throws Exception;

	List<NoticeVo> getNoticeList() throws Exception;

	int deleteNotice(String[] delArr) throws Exception;

}


NoticeDaoImpl

@Repository
public class NoticeDaoImpl implements NoticeDao{

	@Autowired
	private SqlSession sqlSession;
	
	@Override
	public int write(NoticeVo vo) throws Exception {
		return sqlSession.insert("notice.insertNotice", vo);
	}

	@Override
	public List<NoticeVo> getNoticeList() throws Exception {
		return sqlSession.selectList("notice.getNoticeList");
	}

	@Override
	public int deleteNotice(String[] delArr) throws Exception {
		return sqlSession.update("notice.deleteNotice", delArr);
	}

}


NoticeVo

@Data
public class NoticeVo {

	private long no;
	private String title;
	private String content;
	private long writer;
	private Date writeDate;	//작성날짜
	private Date editDate;	//최종수정날짜
	private String del;
	//JOIN한 테이블의 컬럼을 넣는다고???
	private String userNick;
}



디비

notice-mapper.xml

<mapper namespace="notice">

	<insert id="insertNotice" parameterType="noticeVo">
		INSERT INTO NOTICE
		(
			 NO 
		    ,TITLE
		    ,CONTENT 
		    ,WRITER 
		)
		VALUES
		(
			 NOTICE_SEQ.NEXTVAL
			,#{title}
			,#{content}
			,#{writer}
		)
		
	</insert>
 
 	<select id="getNoticeList" resultType="noticeVo">
 		SELECT *
 		FROM NOTICE n
 		JOIN MEMBER m ON(n.WRITER = m.USER_NO)
 		WHERE DEL = 'N'
 		ORDER BY NO
 	</select>
 	
 	<update id="deleteNotice" >
 		UPDATE NOTICE
 		SET
 		DEL = 'Y'
 		WHERE NO IN 
 		<foreach collection="array" item="n" open="(" close=")" separator=",">
 		#{n}
 		</foreach>
 		
 	</update>
</mapper>


NOTICE

DROP TABLE NOTICE CASCADE CONSTRAINTS;
CREATE TABLE NOTICE(
	 NO NUMBER PRIMARY KEY
    ,TITLE  VARCHAR2(100)
	,CONTENT VARCHAR2(4000)
    ,WRITER NUMBER
	,WRITE_DATE  DATE DEFAULT SYSDATE
    ,EDIT_DATE  DATE NULL
	,DEL CHAR(1) DEFAULT('N')
    ,CONSTRAINT NOTICE_FK FOREIGN KEY(WRITER) REFERENCES MEMBER(USER_NO) ON DELETE CASCADE
);

DROP SEQUENCE NOTICE_SEQ;
CREATE SEQUENCE NOTICE_SEQ NOCACHE NOCYCLE;

SELECT * FROM NOTICE;


mybatis-config.xml

<configuration>
	<settings>
		<setting name="cacheEnabled" value="true"/>
		<setting name="autoMappingBehavior" value="FULL"/>
		<setting name="mapUnderscoreToCamelCase" value="true"/>
		<setting name="jdbcTypeForNull" value="NULL"/>
	</settings>
	<!-- 경로의 별칭 지정 -->
	
	<typeAliases>
		<typeAlias type="com.kh.app999.member.entity.MemberDto" alias="memberDto"/>
		<typeAlias type="com.kh.app999.notice.vo.NoticeVo" alias="noticeVo"/>
	
	</typeAliases> 
	
</configuration>



notice/list.jsp

<body>

	<%@ include file="/WEB-INF/views/common/header.jsp" %>
	
	<div id="div-main">
		<div style="text-align:center;">
			<h1>공지사항</h1>
		</div>
		
		<table border="1" style="margin:auto;">
			<thead>
				<tr>
					<th><input type="checkbox" id="allCheck"></th>
					<th>글번호</th>
					<th>제목</th>
					<th>작성자</th>
					<th>작성시간</th>
				</tr>
			</thead>
			<tbody>
				<c:forEach items="${list}" var="n">
					<tr>
						<td><input type="checkbox" class="checkbox-del" value="${n.no}"></td>
						<td>${n.no}</td>
						<td>${n.title}</td>
						<td>${n.userNick}</td>
						<td>${n.writeDate}</td>
					</tr>
				</c:forEach>
			</tbody>
		</table>
		
		<a href="${root}/notice/write">공지 작성</a>
		<button onclick="del();">삭제하기</button>
	</div>
	
	<script type="text/javascript">
		//전체 선택
		//let allCheck = document.querySelector('#allCheck');
		let allCheck = document.querySelector('thead input[type=checkbox]');
		let delArr = document.getElementsByClassName('checkbox-del');
		
		allCheck.onchange = function(e){
			//console.log(e.target.checked);
			console.log(this.checked);
			if(this.checked){
				for(let i=0; i<delArr.length; i++){
					delArr[i].checked = true;
				}
			}else{
				for(let i=0; i<delArr.length; i++){
					delArr[i].checked = false;
				}
			}
		}
		
		//삭제하기
		function del() {
			//삭제할 번호(들) 가져오기
			let delArr = document.getElementsByClassName('checkbox-del');
			//가져온 번호(들)을 하나의 문자열로 합치기
			let result = "";
			
			for(let i = 0; i<delArr.length; i++){
				let t = delArr[i];
				if(t.checked){
					//children[1]가 no가 있음
					//console.log(t.parentNode.parentNode.children[1].innerText);					
					console.log(t.value);	
					result += t.value + ',';
				}
			}
			
			$.ajax({
				url : "${root}/notice/delete",
				data : {"str" : result},
				type : "post",
				success : function(data){
					console.log(data);
				}, 
				error : function(error){
					console.log(error)
				},
				complete : function(){
					//새로고침
					window.location.reload();
				}
			});
		}//del()
	</script>
</body>


notice/write.jsp

<body>

	<%@ include file="/WEB-INF/views/common/header.jsp" %>
	
	<div id="div-main">
		<div style="text-align:center;">
			<h1>공지사항 작성하기</h1>
		</div>
		
		<form action="" method="post" style="text-align:center;" onsubmit="return writeCheck();">
			제목 : <input type="text" name="title"><br>
			작성자 : <input type = "text" value="${loginUser.userNick}" readonly="readonly"/> <br>
			<input type = "hidden" name = "writer" value="${loginUser.userNo}"/> <br>
			내용 : <br> <textarea rows="30" cols="100" name = "content"></textarea> <br>
			 <input type = "submit" value = "공지사항 작성" />
		</form>
		
	</div>
	
	<script type="text/javascript">
		function writeCheck(){
			return confirm("작성하시겠습니까?");
		}
	
	</script>
</body>


common/header.jsp

  • 제이쿼리 추가함
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<c:set var="root" value="${pageContext.request.contextPath}"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<link rel="stylesheet" href="${root}/resources/css/common/header.css">

	<div id="div-header">
		<table border="1">
			<tr>
				<td>빈칸</td>
				<td colspan="2"><a href="${root}"><img width="100%" height="50px" src="${pageContext.request.contextPath}/resources/imgs/qr.png"></a></td>
				<!-- href="/app999" -->
				<c:if test="${empty loginUser}">
					<td>빈칸</td>
				</c:if>
				<c:if test="${not empty loginUser}">
					<td><a href="${root}/member/mypage"><img src="${root}/resources/upload/profile/${loginUser.changeName}"></a></td>
				</c:if>
			</tr>
			<tr>
				<td><a href="${root}/notice/list">공지사항</a></td>
				<td>메뉴2</td>
				<td>메뉴3</td>
				<c:if test="${empty loginUser}">
					<td>
						<a href="${root}/member/login">로그인</a><br>
						<a href="${root}/member/join">회원가입</a>
					</td>
				</c:if>
				<c:if test="${not empty loginUser}">
					<td>
						${loginUser.userNick}님 환영합니다!<br>
						<a href="${root}/member/logout">로그아웃</a>
					</td>
				</c:if>
			</tr>
		</table>
	</div>


home.jsp

<body>
	<%@ include file="/WEB-INF/views/common/header.jsp" %>
	<script type="text/javascript" src="${root}/resources/js/home.js"></script>

	<div id="div-main">
		<h1>홈페이지</h1>
	</div>
</body>



js 분리

좋은 웹페이지 즐겨찾기