기록
  • 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("<","&lt;").replaceAll(">","&gt;");
        }

    이제 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
    댓글