기록
  • 1219 WebServer 9일차
    2023년 12월 19일 19시 57분 35초에 업로드 된 글입니다.
    작성자: 삶은고구마

    1218은 테스트때문에 진도를 안나감..

     

    목차

    - AdminMemberListServlet 

     기능 통합 (검색, 페이지바기능 합침)

    - BoardDetailServlet 작업

    - BoardCreateServlet 추가

     


    BoardDetailServlet 

    게시판 상세 페이지 작업을 진행하였다.

    말 그대로, 게시판 목록에서 내가 원하는 제목을 클릭했을 때 해당 게시글의 세부사항을 볼 수 있는 페이지 작업을 진행.

    구성요소는 위에서부터 제목/ 작성자 아이디/ 내용 / 조회수/ 작성일 이다.

     

    글을 썼을 때는 분명

    띄우기1

    띄우기2

    이런식으로 개행을 했는데 ..?

    막상 값을 받아와 출력하면 한줄로 이어져있다.

    console.log("boardCreate.js - 게시글 작성시 유효성 검사하는 js페이지");
    document.boardCreateFrm.addEventListener('submit',()=> {
        const frm = e.target;
        const title = frm.title;
        const content = frm.content;
        
        //제목 유효성 검사
        if(!/^.+$/.test(title.value.trim()))
        {
            alert('제목을 반드시 작성해주세요.');
            e.preventDefault();
            return; //제출막기..
        }
        
        //내용 유효성 검사
        //정규표현식의 .에는 \n이 포함되지 않는다.
        if(!/^(.|\n)+$/.test(content.value.trim()))
        {
            alert('내용을 반드시 작성해주세요.');
            e.preventDefault();
            return; //제출막기..
        }
    
    });

     

    유효성 검사 시 \n이 포함되지 않아 문자열에 그대로 반영되면서 개행이 진행되지 않은 것 같은데[?

    utilClass를 따로 만들어 내용문자열에 포함된 \n을 <br>로 바꿔주는 메소드를 만들었다.

    public static String convertLineFeedToBr(String str)
        {
            return str.replaceAll("\n","<br/>");
        }

     

    그리고 detailServelt에서 해당 메소드를 사용해서 개행 처리를 한다.

    해당 내용이 포함된 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.업무로직
            BoardVo board = boardService.findById(id);
            //개행문자(\n) -> <br>
            board.setContent(HelloMvcUtils.convertLineFeedToBr(board.getContent()));
            req.setAttribute("board",board);
            System.out.println(board);
    
            //3.forward
            req.getRequestDispatcher("/WEB-INF/views/board/boardDetail.jsp").forward(req,resp);
        }
    }

     

    수정 후.

    내가 원하는대로 보기 좋게 바뀌었다.

     


     

    https://jang2r.tistory.com/27

     

    개발노트 :: JSTL 날짜 포맷 <fmt:formatDate> <fmt:parseDate>

    JSTL 날짜 포맷 fmt:parseDate - 문자열 -> Date 타입으로 변경 fmt:formatDate - Date 타입 -> 문자열으로 변경 1. JSTL ${date1} 결과값 Tue Jan 01 00:00:00 KST 2019 2. JSTL ${date2} 결과값 2019-01-01 3. pattern 형식 y(The year) 년

    jang2r.tistory.com


    작성일 날짜 변환

    받아온 board.regDate를 yy/MM/dd HH:mm형식으로 바로 변경할 수 없다.

    중간에 형변환을 한 번 더 해야 가능함.

    즉, String->date형->원하는형 으로 진행한다.

    fmt:parseDate - String형을 받아 원하는 포맷 Date형으로 변환

    fmt:formatDate - Date형을 받아 원하는 포맷으로 변환

    작성일
    <fmt:parseDate value="${board.regDate}" pattern="yyyy-MM-dd'T'HH:mm" var="regDate"/>
    <fmt:formatDate value="${regDate}" pattern="yy/MM/dd HH:mm"/>

     


     

    그 외에 상세 페이지에 작성시 첨부한 첨부파일 갯수와 파일명도 보여줄 수 있는데

    이건 BoardCreateServlet에서 정리하겠다.

    아래는 boardDetail.jsp의 일부인데,

    c:foreach를 사용해 attachments의 갯수만큼 반복문을 실행하여 갯수만큼 첨부파일명을 보여주는 코드다.

     <c:forEach items = "${board.attachments}" var="attach">
            <a href="#" class="flex items-center text-blue-600 hover:underline">
                <img src="../images/file.png" class="w-[16px] mr-1">
                ${attach.originalFilename}
            </a>

    BoardCreateServlet 

    boardCreate.jsp 에서 정말 중요한 코드가 있음.

    제목,내용,첨부파일등을 전송해주는 form을 아래 형식으로 작성해줘야 한다

    1.name은 각자 원하는 것으로 네이밍하면 되고

    2.method는 post방식이다. (insert - dml)

    3.enctype = "multipart/form-data"를 꼭 써줘야 한다.

    input file형식을 전송할 때 필요한 속성값이며, 여러개를 첨부할 수 있다.

    ★주의점★

    이 방법을 사용하면 req.getParameter로 값을 받아 올 수 없다. 확인하면 null값이다.

    정 받고 싶다면 해당 멀티파트리퀘스트객체. getParameter로 받아 올 수있다 하는데 나중에 테스트 해보겠다.

     <form name="boardCreateFrm" method="post" enctype="multipart/form-data">

     

    유익한 링크들.

    https://m.blog.naver.com/bb_/222134798237

     

    [JAVA] 멀티파트 폼(multipart/form-data)으로 넘긴 파라미터 request.getParameter 로 받기

    [JAVA] 멀티파트 폼(multipart/form-data)으로 넘긴 파라미터 request.getParameter 로 받기 멀티파트...

    blog.naver.com

    https://riucc.tistory.com/354

     

    [JSP] - 파일 enctype="multipart/form-data" 사용 시 request.getParameter null 해결방법

    ○ 파일 enctype="multipart/form-data" 사용 시 request.getParameter null 해결방법 파일이나 이미지 처리 폼에서 enctype="multipart/form-data" 을 사용하는데,대부분 파일이나 이미지는 게시물 작성같은 다른 입력

    riucc.tistory.com

     


    @WebServlet("/board/boardCreate")
    public class BoardCreateServlet extends HttpServlet
    {
        private BoardService boardService = new BoardService();
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            req.getRequestDispatcher("/WEB-INF/views/board/boardCreate.jsp").forward(req,resp);
        }
    
        /**
         * 파일 업로드 처리..
         * 1.commons-io,commons-fileupload 의존성 추가(pom.xml)
         * 2.form[method=post][enctype=multipart/form-data]
         * 3.DiskFileItemFacroty / ServletFileUpload 요청처리
         *  - 저장경로
         *  - 파일 최대 크기
         * 
         * 
         * 
         * @param req
         * @param resp
         * @throws ServletException
         * @throws IOException
         */
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //1.사용자 입력값 처리 및 파일 업로드
            File repository = new File("C:\\Workspaces\\web_server_workspace\\helloo-mvc\\src\\main\\webapp\\upload\\board");
            int sizeThreshold = 10 * 1024 * 1024; //10MB
    
            DiskFileItemFactory factory = new DiskFileItemFactory();
            factory.setRepository(repository);
            factory.setSizeThreshold(sizeThreshold);
    
            BoardVo board = new BoardVo();
    
    
            //실제 요청을 핸들링할 객체..
            ServletFileUpload s = new ServletFileUpload(factory);
            try {
                //전송된 값을 하나의 FileItem으로 처리
                List<FileItem> fileItemList = s.parseRequest(req);
                for(FileItem item: fileItemList)
                {
                    String name = item.getFieldName();//input의 name
                    if(item.isFormField()){
                        //일반 텍스트 : board 객체에 설정
    
                        String value = item.getString("utf-8");
                        System.out.println(name+"/"+value);
                        //Board객체에 설정자 로직 구현..
                        board.setValue(name,value);
    
                    }
                    else {
                        //파일 : 서버 컴에 저장 파일정보를 attachment 객체로 만들어서 db에 저장
                        if(item.getSize() > 0) {
                            System.out.println(name);
                            String originalFileName = item.getName();
                            System.out.println("원본 파일명: " + originalFileName);
                            System.out.println("파일크기: " + item.getSize() + "byte");
    
                            int dotIndex = originalFileName.lastIndexOf(".");
                            String ext = dotIndex >- 1? originalFileName.substring(dotIndex) : "";
                            System.out.println("extextextextextext: " + ext ); //.png
    
                            UUID uuid = UUID.randomUUID();//랜덤 uuid 발급 b9c2bc5f-9a0c-4e7f-9d87-7d6feb70e244
                            String renamedFileName = uuid+ext; //저장된 파일명(덮어쓰기방지,인코딩이슈방지) 위에 발급된 uuid에 원본의 확장자명을 붙여서 새 파일명만든다.
                            System.out.println("새 파일명 : "+renamedFileName);
    
                            //서버 컴퓨터 파일 저장
                            File upFile = new File(repository,renamedFileName);
                            item.write(upFile);
    
                            //Attachment 객체 생성
                            Attachment attach = new Attachment();
                            //현재 할 수 있는건 원본명과, 새이름 set.
    
                            attach.setOriginalFilename(originalFileName);
                            attach.setRenamedFilename(renamedFileName);
                            board.addAttachment(attach);
    
    
                        }
                    }
                }
            } 
            catch (Exception e)
            {
                throw new RuntimeException(e);
            }
    
            System.out.println("board+attach : " +board);//board객체,attach객체 모두 확인되어야함.
    
    
            //2.업무로직
            int result = boardService.insertBoard(board);
            req.getSession().setAttribute("msg","게시글을 성공적으로 등록하였습니다");
    
            //3.게시판 목록페이지로 redirect
            resp.sendRedirect(req.getContextPath()+"/board/boardList");
        }
    }

     


     

    - 기존 board 객체 클래스에는 첨부파일에 관련된 필드가 없었다.

    - board entity는 테이블과 1대1매칭되는 관계라, 새로운 "첨부파일 관련 필드" 를 추가하기 보다는,

    - 첨부파일에 관련된 Attachment  테이블과 1대1로 매칭되는 Attachment 클래스를 만든후

    (pk,board id,원본파일명,새파일명,등록일)

    - board를 상속하는 확장클래스인 boardVo를 만든다. 

      예전에 jdbc 수업 때 주소록 클래스를 이런식으로 만들었다.

      1대 N의 관계 일 때 이런 구조로 클래스를 짜면 될 것 같다.

      한 회원은 여러 주소를 가질 수 있다 , 한 게시글은 첨부파일을 안가질 수도, 1개,2개 등 여러 개 가질 수있다.

    /**
     * vo 클래스 value object
     * - immutable한 value객체(수정하지않는,불변)
     * - entity 클래스를 확장한 객체
     * - 기존 entity는 수정하지말고 상속하여 확장/사용
     */
    public class BoardVo extends Board {
    
        //보드를 상속하고, 원래 보드에 없는 첨부파일 갯수 필드를 하나 추가함
        private int attachCount;
        private List<Attachment> attachments = new ArrayList<>();
        .
        .
        .
        생략

     

     

    기존 board 객체를 사용한 메소드는 boardVo로 변경해주고,

    BoardService에서 insertBoard(게시글 추가기능)은 아래와 같이 작성해준다.

    첨부파일은 n개 일 수도있으니 반복문을 사용하여 insert해준다.

     

    그리고 dml의 특성상, 전부 성공 commit / 전부 실패 rollback 처리를 해줘야 한다.

    보드객체/첨부파일객체를 따로 insert하여 모두 성공했을 때만 session.commit 처리를 해야한다.

    =>만약 보드객체값만 성공하고 첨부파일은 실패한다면 일관성에 위배 되지않을까?

    그럴바엔 아예 실패로 처리해서 게시글 자체가 insert되지 않도록 해야한다.

    :★하나의 세션,하나의 메소드 안에서 관리하기

     

    public int insertBoard(BoardVo board)
        {
            int result = 0;
            SqlSession session = getSqlSession();
    
            try
            {
                //board + attach 둘 다 insert하고 commit or rollback 처리 해야함
    
                //board 테이블에 등록
                result = boardDao.insertBoard(session,board);
    
                System.out.println("board get id())" + board.getId());
                //attach 테이블에 등록
                List<Attachment> attachments = board.getAttachments();
                if(!attachments.isEmpty())
                {
                    for(Attachment attach : attachments)
                    {
                        attach.setBoardId(board.getId());
                        result = boardDao.insertAttachment(session,attach);
                    }
                }
    
    
                session.commit();
            }
            catch(Exception e)
            {
                //에러가 발생할 경우 롤백하기
                session.rollback();
                throw  e;
            }
            finally
            {
                session.close();
            }
            return result;
        }

     

     

    BoardDao

    //게시글추가
    public int insertBoard(SqlSession session, Board board)
        {
            return session.insert("board.insertBoard",board);
        }
        
    //첨부파일추가
    public int insertAttachment(SqlSession session, Attachment attach) {
            return session.insert("board.insertAttachment",attach);
        }

     

    board-mapper.xml

    <insert id="insertBoard">
            insert into
            board (id, title, member_id, content)
            values(
            seq_board_id.nextval,
            #{title},
            #{memberId},
            #{content}
            )
            <selectKey order="AFTER" resultType="_int" keyProperty="id">
                select
                seq_board_id.currval
                from
                dual
            </selectKey>
        </insert>
    
        <insert id="insertAttachment">
            insert into
                attachment(id,board_id,original_filename,renamed_filename)
            values(
                seq_attachment_id.nextval,
                #{boardId},
                #{originalFilename},
                #{renamedFilename}
    
            )
    
        </insert>

     

    board insert 후 해당 board의 pk값인 id를 fk로 써야 하는 attachment를 insert 해야 하는데

    (어느 게시글에 무슨 첨부파일이 있는지 알아야 하니까..^^)

    selectKey 태그를 사용하면  board insert 이후(after) 해당 id 값을 알 수 있다.

    그리고 이 값을 사용해 attachment insert 시 사용 할 수 있다!

    boardService에서 attach.setBoardId(board.getId());를 보면

    board의 아이디를 받아 attach에 setBoardId 한다. 그리고 다음 줄에서 바로 dao insertAttachment 진행.

     System.out.println("board get id())" + board.getId());
                //attach 테이블에 등록
                List<Attachment> attachments = board.getAttachments();
                if(!attachments.isEmpty())
                {
                    for(Attachment attach : attachments)
                    {
                        attach.setBoardId(board.getId());
                        result = boardDao.insertAttachment(session,attach);
                    }
                }

     

     

     

     

    selectKey 에 관한 도움글

    https://yookeun.github.io/java/2014/07/11/mybatis-selectkey/ 

     

    mybatis에서 selectKey 사용법

    DB작업을 하다보면 먼저 사전에 어떤 키값을 가져와서 증가시켜서 입력하거나 혹은 입력후에 증가된 키값을 가져올 필요가 있다. 이럴때 mybatis에서 제공하는 selectKey를 이용하면 별도의 쿼리로

    yookeun.github.io

    구조

    '공부 > web-server' 카테고리의 다른 글

    0111 ubuntu 설치 - 설정  (0) 2024.01.11
    1220 WebServer 10일차  (0) 2023.12.20
    1215 WebServer 8일차  (0) 2023.12.15
    1214 WebServer 7일차  (1) 2023.12.14
    1212 WEB SERVER 5일차  (0) 2023.12.12
    댓글