Spring 23通知、全選択、管理者アクセス制限
104669 ワード
作成公告(+確認)、全選択、削除 のIDがないとエラーは発生しません intercepter(管理者レベルでない場合は近い) 管理層アクセス
の2番目のクエリ を追加js分離
管理層アクセス
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
//인터셉터 만드는 방법 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;
}
}
}
@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";
}
}
<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>
<!-- 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
@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;
}
}
@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;
}
}
}
public interface NoticeService {
int write(NoticeVo vo) throws Exception;
List<NoticeVo> getNoticeList() throws Exception;
int deleteNotice(String str) throws Exception;
}
@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);
}
}
public interface NoticeDao {
int write(NoticeVo vo) throws Exception;
List<NoticeVo> getNoticeList() throws Exception;
int deleteNotice(String[] delArr) throws Exception;
}
@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);
}
}
@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
<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>
<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>
<%@ 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分離
Reference
この問題について(Spring 23通知、全選択、管理者アクセス制限), 我々は、より多くの情報をここで見つけました
https://velog.io/@lecharl/스프링23
テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol
Reference
この問題について(Spring 23通知、全選択、管理者アクセス制限), 我々は、より多くの情報をここで見つけました https://velog.io/@lecharl/스프링23テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol