- 1220 WebServer 10일차2023년 12월 20일 09시 20분 30초에 업로드 된 글입니다.작성자: 삶은고구마
multipart/form-data로 보낼 때.(말 그대로 여러 파트로 나눠서 보낸다)
boadDetail(게시글 상세 페이지)에서 아이디와 사용자 이름 함께 출력하기.
-1:N 관계일 경우 collection을 사용하고 (어제 attachement 예제)
-1:1 관계일 경우 asociation을 사용한다.
어제는 board 테이블과 attachment 테이블만 조인했었다.
여기에 사용자 이름 컬럼을 갖고있는 member 필드까지 조인시켜보자!
member 테이블에서 필요한 값은 name 뿐이므로 (나중에 더 필요한게 있다면 select에 컬럼명 추가 하면 됨)
별칭 m.name을 갖고와 member_name이란 별칭을 지어줬다.
<select id="findById" resultMap="BoardVoMap"> select b.*, m.name member_name, a.id attach_id, a.board_id, a.original_filename, a.renamed_filename, a.reg_date attach_reg_date from board b left join member m on b.member_id = m.id left join attachment a on b.id = a.board_id where b.id = #{id} </select>
그리고 collection 위에(이전) association을 작성한다..(태그 순서지키기,collection 이후에 사용하면 에러가 발생하더라)
<!-- property:필드명 javaType=타입 --> <association property="member" javaType="Member" > <id column="member_id" property="id"/> <result column="member_name" property="name"/> </association>
그리고 어제 첨부파일 listArray를 추가해줬던 것 처럼 BoardVO 클래스에 Member 클래스를 추가한다.
private Member member;
boardDetail.jsp에 memberId를 출력했던 p태그에 덧붙였다.
아이디(이름) 이렇게 나올 것이다.
<p class="mb-3 font-normal text-gray-500">${board.memberId}(${board.member.name})</p>
내가 원하는대로 아이디와 이름이 나온다..
결과
html 태그 작성 막기
사용자가 의도적으로 title , content에 태그를 작성하는 경우가 있을 수 있다.
예를 들어 title을 작성하는 input에 아래와 같이 작성하면..
실제로 alert가 팝업된다. 심지어 해당글을 조회하려고 할 때마다 뜬다.
이런식으로 이용 방해, 혹은 정보탈취까지 노리는 행위를 XSS 공격 이라고 한다.
<script>alert('바보!')</script>
/** * XSS 공격 * -Cross-site Scripting 공격 * -"악성 스크립트를 웹페이지에 삽입"해서 클라이언트 개인정보를 탈취하는 공격법 * -script태그로 구성된 내용을 필터링 없이 화면에 출력할 때 취약할 수 있다... * * 해결법 * -Escpae Html처리를 통해서 사용자입력값이 html요소로 작동하지 못하도록 막기. */
해결방법은 텍스트 작성 영역안에 쓰여진 태그가 html로서 작동하지 못하도록 막는 것이다.
<와 >를 작다/크다 의미로 사용하는 부등호로 replace 해주면 된다.
파라메터로 받는 str 문자열값에 어떤 태그가 들어와도 아래와 같이 처리하면 태그로서의 기능을 잃는다..
public static String escapeHtml(String str) { return str.replaceAll("<","<").replaceAll(">",">"); }
이제 title로 보이는 스크립트 태그
조회수 증가 처리 하기
->사이트/어플마다 방식이 다양한데 수업에서는 쿠키를 사용하기로 하였다.
전체요약
1.첫 조회때만 쿠키를 생성 & 조회수 1증가
2.두번째 조회 때 부터는 생성된 쿠키를 확인 후, 쿠키값이 존재한다면 증가처리를 하지 않는다.
이미 읽었던 전적이 있으니 또 읽는다면 n번째 조회이므로 증가처리를 하지 않는 것이다.
BoardList ->BoardDetail로 들어가는 (게시글 클릭) 순간 조회수가 발생하므로
BoardDetailServlet에서 처리한다.
BoardDetailServlet
전체코드
@WebServlet("/board/BoardDetail") public class BoardDetailServlet extends HttpServlet { private BoardService boardService = new BoardService(); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1.사용자 입력값 처리 long id = Long.parseLong(req.getParameter("id")); System.out.println(id); //2.업무로직 //조회수 관련 처리 Cookie[] cookies = req.getCookies(); List<String> boardCookieValues = getBoardCookieValues(cookies); boolean hasRead = boardCookieValues.contains(String.valueOf(id));//현재 게시글 읽음 여부. System.out.println("현재 게시글 읽었니?:"+hasRead); //true:이미 읽음 //false:처음 읽음 //조회 BoardVo board = boardService.findById(id , hasRead); //id와 조회여부까지 확인한다. //xss공격대비 escapehtml처리 String safeHtml = HelloMvcUtils.escapeHtml(board.getContent()); //개행문자(\n) -> <br> board.setContent(HelloMvcUtils.convertLineFeedToBr(safeHtml)); req.setAttribute("board",board); //응답 쿠키 생성. 즉, 처음 읽은 글이라면 생성하자. if(!hasRead) { //안읽은 녀석이니까. boardCookieValues.add(String.valueOf(id));//현재 게시글 id 추가! //조회수 관련 처리 String value = String.join("/",boardCookieValues); Cookie cookie = new Cookie("board", value); //유효기간을 정할 수 있다[초단위로] cookie.setMaxAge(365 * 24 * 60 * 60); //1년. , 만약 음수면 session 종료시 삭제 / 0이면 즉시삭제 cookie.setPath(req.getContextPath() + "/board/BoardDetail"); //지정한 경로일 때만 클라이언트->서버로 쿠키전송 resp.addCookie(cookie);//쿠키는 여러번 호출 할 수 있음. } //3.forward req.getRequestDispatcher("/WEB-INF/views/board/boardDetail.jsp").forward(req,resp); } private List<String> getBoardCookieValues(Cookie[] cookies) { List<String> boardCookieValues = new ArrayList<>(); if(cookies !=null) { for(Cookie cookie:cookies) { String name = cookie.getName(); String value = cookie.getValue(); System.out.println("쿠키 이름:"+name +" /쿠키 값:"+value); //쿠키 이름:board/쿠키 값:92 if("board".equals(name)) { String[] temp = value.split("/"); for(String _id: temp) { boardCookieValues.add(_id); } } } } return boardCookieValues; } }
select로 해당 게시글의 상세 내용을 조회하는 것이므로 get방식을 사용한다.
1.현재 내가 어느 게시글을 클릭했는지 id를 확인할 수 있다.
ex)92번째 게시글 클릭했을 경우 92라고 출력된다.
//1.사용자 입력값 처리 long id = Long.parseLong(req.getParameter("id")); System.out.println(id);
2.cookies라는 배열에 클라이언트로부터 받은 cookie들을 모아둔다.
쿠키값이 없으면 null이 반환된다. 쿠키는 여러개가
쿠키들만 따로 모은 String타입의 List가 있는데 getBoardCookieValues 메소드는 아래에서 설명하겠다.
//조회수 관련 처리 Cookie[] cookies = req.getCookies(); List<String> boardCookieValues = getBoardCookieValues(cookies); boolean hasRead = boardCookieValues.contains(String.valueOf(id));//현재 게시글 읽음 여부. System.out.println("현재 게시글 읽었니?:"+hasRead); //true:이미 읽음 //false:처음 읽음
3.쿠키 생성하기
쿠키를 생성해야 하는 조건
해당 게시글을 "처음" 읽었을 때이다. 조회수 0->1
그리고 쿠키생성조건이나 유효시간등도 원하는대로 설정할 수 있다.
아래 코드에서는 게시글 상세 서블릿 - BoardDetail의 경로일때만 쿠키를 전송하기로 함.
그 외의 페이지에서는 쿠키 생성하지 않는다.
setMaxAge로 쿠키의 유효기간을 설정 할 수 도있다.
만료 시간 쿠키 종류 특징 session cookie -1로 지정,세션만료시 쿠키 자동 삭제
임시쿠키, 브라우저 종료시 삭제persistent cookie n초로 지정한 경우 영속성쿠키 마지막 addcookie로 쿠키를 클라이언트로 전송한다.
//응답 쿠키 생성. 즉, 처음 읽은 글이라면 생성하자. if(!hasRead) { //안읽은 녀석이니까. boardCookieValues.add(String.valueOf(id));//현재 게시글 id 추가! //조회수 관련 처리 String value = String.join("/",boardCookieValues); Cookie cookie = new Cookie("board", value); //유효기간을 정할 수 있다[초단위로] cookie.setMaxAge(365 * 24 * 60 * 60); //1년. , 만약 음수면 session 종료시 삭제 / 0이면 즉시삭제 cookie.setPath(req.getContextPath() + "/board/BoardDetail"); //지정한 경로일 때만 클라이언트->서버로 쿠키전송 resp.addCookie(cookie);//쿠키는 여러번 호출 할 수 있음. }
4.getBoardCookieValues 메소드
private List<String> getBoardCookieValues(Cookie[] cookies) { List<String> boardCookieValues = new ArrayList<>(); if(cookies !=null) { for(Cookie cookie:cookies) { String name = cookie.getName(); String value = cookie.getValue(); System.out.println("쿠키 이름:"+name +" /쿠키 값:"+value); //쿠키 이름:board/쿠키 값:92 if("board".equals(name)) { String[] temp = value.split("/"); for(String _id: temp) { boardCookieValues.add(_id); } } } } return boardCookieValues; }
Q:이녀석은 무얼 하는데 쓰나요?
A:받아온 쿠키값들을 확인해서 쿠키이름이 board인 녀석들의 값(누적)을 확인하여 /기준으로 쪼개어 확인함.
쿠키 이름:board /쿠키 값:25/92/93/72/94/81/84/96/86/60/68/82/87/85/97/95
이런식으로 1회 이상 읽은 board id를 항시 갖고있어야한다.
그래야 이미 조회된 글인지 아닌지 구별하여 hasRead값에 true/false를 줄 수 있기 때문.
위의 2번 코드를 보면 String value = String.join("/",boardCookieValues); <-코드로 누적함.
이 배열값을 1번의 아래의 코드로 리턴하여 , 조회 여부를 확인함.
즉, 이 배열에 포함된(contains) id는 true인 것이다.
걍 쉽게 생각해서 지금까지 1번이라도 읽었던 애들이면 리스트에 적어놨다가
또 조회할 때 리스트에 있는지 없는지 확인하고, 존재한다면 조회수를 또 증가 시킬 수 없으므로 false딱지 붙임
List<String> boardCookieValues = getBoardCookieValues(cookies); boolean hasRead = boardCookieValues.contains(String.valueOf(id));//현재 게시글 읽음 여부.
5. 그리고 해당 게시글의 조회 여부를 확인하는 메소드
BoardVo board = boardService.findById(id , hasRead); //id와 조회여부까지 확인한다.
6.service단의 findById
hasRead가 false라면 첫 조회이므로 1증가를 위해 updateBoardReadCount메소드를 실행하고,
그렇지 않다면 단순조회만 한다.
//1220 조회여부까지 추가한 findById - true는 이미 읽음 / false: 첫조회이므로 false때만 증가처리 public BoardVo findById(long id, boolean hasRead) { //id로 게시글 1개를 찾는다. SqlSession session = getSqlSession(); //조회수 증가 처리 BoardVo board = null; int result=0; try { if(!hasRead) result = boardDao.updateBoardReadCount(session,id); board = boardDao.findById(session, id); session.commit(); } catch(Exception e) { session.rollback(); throw e; } finally { session.close(); } return board; }
게시글 수정하기.
전제 조건:
1.관리자이거나
2.해당게시글의 작성자이거나
[수정중..]
'공부 > web-server' 카테고리의 다른 글
0112 (0) 2024.01.12 0111 ubuntu 설치 - 설정 (0) 2024.01.11 1219 WebServer 9일차 (1) 2023.12.19 1215 WebServer 8일차 (0) 2023.12.15 1214 WebServer 7일차 (1) 2023.12.14 다음글이 없습니다.이전글이 없습니다.댓글