본문 바로가기

자바 웹을 다루는 기술

AJAX(js:fetch)를 이용한 게시글 더보기 만들기

더보기 정리

fetch로 하는 더보기

내 테이블에서는 게시판과 공지사항이 한테이블에 있기 때문에 boardtype이라는 구분 속성을 둔다. 공지사항에서 맨처음에는 10개만 출력이 되었다가 다음번에는 10개 출력된 페이지의 맨아래의 게시글의 번호를 가져와서 그 다음번호 부터 10개를 출력하도록 sql문을 작성한다.

 

그러니까 이 sql문은 넘어오는 boardid값이 있을때는 boardid값 다음부터 10개를 출력하도록 하고 아닐때는 그냥 처음부터 10 개를 가져오도록 해야한다.

select * 
from(
    select * 
    from board 
    where boardtype='공지사항' 
    and boardid < 400185
    order by boardid desc
)
 where rownum <= 10;

위에서 설명한 논리로 프로시저를 작성하면  이런식으로 id 값이 들어왓을때와 아닐때를 if else문으로 구분해서 작성해준다.

create or replace procedure get_more_list
(
    p_boardType in varchar,
    p_boardId in int,
    v_cursor OUT SYS_REFCURSOR
)
is
begin

 if(p_boardId is null) then
    open v_cursor
            for
            select * 
            from(
                select * 
                from board 
                where boardtype = p_boardType
                order by boardid desc
            )
            where rownum <= 10;
    else
    open v_cursor
            for
            select *
            from(
                select * 
                from board 
                where boardtype = p_boardType
                    and boardid < p_boardId
                order by boardid desc
            )
            where rownum <= 10;
    end if;
end;

 

 

 

 

 

sql문은 준비가 되었으니 이제 NoticeDAOImpl.java에서 이 프로시저를 불러주는 코드를 작성해주어야한다.

NoticeDAOImpl.java

<List<Board> getBoardMoreList(Board board)>

@Override
public List<Board> getBoardMoreList(Board board) {
    Connection conn = ConnectionHelper.getConnection("oracle");
    CallableStatement cstmt = null;
    ResultSet rs = null;
    List<Board> boardList = new ArrayList<>();
    String sql = "{call get_more_list(?,?,?)}";

    try {
        cstmt = conn.prepareCall(sql);
        cstmt.setString(1, board.getBoardtype());
        if(board.getBoardid()!=0) {
            cstmt.setInt(2, board.getBoardid());	
        }
        cstmt.registerOutParameter(3, OracleTypes.CURSOR);
        cstmt.execute();
        rs = (ResultSet)cstmt.getObject(3);
        while(rs.next()) {
            boardList.add(Board.builder()
                    .boardid(rs.getInt("boardid"))
                    .title(rs.getString("title"))
                    .contents(rs.getString("contents"))
                    .regdate(rs.getDate("regdate"))
                    .hit(rs.getInt("hit"))
                    .boardtype(rs.getString("boardtype"))
                    .fixed_yn(rs.getString("fixed_yn"))
                    .memberid(rs.getString("memberid"))
                    .build());
        }
    }catch(Exception e) {
        e.printStackTrace();
    }finally {
        ConnectionHelper.close(rs);
        ConnectionHelper.close(cstmt);
        ConnectionHelper.close(conn);
    }
    return boardList;
}

 

 

 

 

 

이제 service에서 이 함수를 불러다가 controller를 통해 jsp 에 뿌려줄 noticeList를 전달해줘야한다.

NoticeService.java

<Map<String, Object> getBoardMoreList(Board board)>

게시글 10개를 가져온다.  근데 코딩 다하고 코드 다시보니까 board값을 굳이 controller에 전달해줄 필요가 없어서 List<Board>로 리턴해줘도 될것같다. 페이징이랑 같다고 착각햇음;;

public Map<String, Object> getBoardMoreList(Board board){
		
    Map<String, Object> result = new HashMap<>();

    result.put("board", board);
    result.put("noticeList", noticeDAO.getBoardMoreList(board));
    return result;
}

여기까지 줄일 수있을꺼같다 아래와같이 써줘도 똑같이 나올듯 수정하자!!

public List<Board> getBoardMoreList(Board board){
    return noticeDAO.getBoardMoreList(board);
}

controller는 함수가 2개 필요하다! 하나는 공지사항 페이지에 입장했을때 맨처음 10개를 출력하면서 jsp로 페이지를 이동시켜 결과값을 보여줄 함수와 ajax와 연결해서 그 다음 10개를 받아다가 jsp의 페이지 이동 없이 10개의 행을 추가해줄 함수가 필요하다.

 

 

1. 공지사항 페이지로 접속하는 AllNotice.do

public String AllNotice(Board board , HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpSession session = request.getSession();
    Member loginMember = (Member)session.getAttribute("loginMember");

    session.setAttribute("boardtype", "AllNotice.do");

    //Board board = new Board();
    board.setBoardtype("공지사항");
    Map<String, Object> result = noticeService.getBoardPageList(board);

    //result를 넘겨서 걍 싹다 result하나에 넘겨주는 방법도 잇음
    //request.setAttribute("noticeList", result.get("boardList"));
    //공지사항 게시글의 리스트와 페이징에 대한 정보가 있다.
    request.setAttribute("result", result);

    boolean isAdmin = noticeService.isAdmin(loginMember);
    request.setAttribute("isAdmin", isAdmin);

    return "notice/allNotice.jsp";
}

--List<Board>로 받아왔다치면 이렇게

public String AllNotice(Board board , HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpSession session = request.getSession();
    Member loginMember = (Member)session.getAttribute("loginMember");

   //내 board테이블에서는 공지사항과 게시판이 하나의 테이블에 모두 들어잇다.
    board.setBoardtype("공지사항");

    request.setAttribute("noticeList", noticeService.getBoardPageList(board));
    
    //사용자가 관리자인지 확인하는 것
    boolean isAdmin = noticeService.isAdmin(loginMember);
    request.setAttribute("isAdmin", isAdmin);

    return "notice/allNotice.jsp";
}

 

 

2. ajax를 통해 공지사항 게시글 10개를 추가로 더 가져오는 함수

ajax와 연결하므로 현재 jsp에서 바꿔줄 정보 부분만 더 가져와 페이지를 일부 수정해주면 되니까 필요한 부분만 가져온다.

public String AjaxAllNotice(Board board ,HttpServletRequest request, HttpServletResponse response) throws Exception {
		
    JSONObject jsonObject = new JSONObject();

    board.setBoardtype("공지사항");
    Map<String, Object> result =noticeService.getBoardMoreList(board);
    System.out.println(result.get("noticeList"));
    jsonObject.put("result", result);
    return jsonObject.toString();
}

--List로 받아왔다치면 이렇게 

public String AjaxAllNotice(Board board ,HttpServletRequest request, HttpServletResponse response) throws Exception {
    JSONObject jsonObject = new JSONObject();

    board.setBoardtype("공지사항");
    
    jsonObject.put("noticeList", noticeService.getBoardMoreList(board));
    
    return jsonObject.toString();
}

 

 

 

 

 

<allNotice.jsp>

allNotice.jsp에 공지사항의 마지막 글 이후로 가져온 리스트를 덧붙여주기 위해서 html을 복사해서 안에 값을 삽입할 html의 틀을 만들어준다. 한번 더보기로 글을 가져왔더니 다음글을 가져올수 없는오류와 관리자일때와 일반사용자일때 boardi를 가져오는 데 위치가 틀리는 오류가 발생했다. (last-child를 통해 가져왓기 때문) 그래서 querySelector에서 class이름에 대한 값으로 정확하게 boardid 를 집어오기 위해 class 값을 지정해 줬다.

<tr id="noticeItem" style="display:none">
	<!-- 로그인한 멤버가 null이 아니고 root사용자라면 checkbox를 나타낸다 -->
    <c:if test='${ (not empty loginMember) && (loginMember.uid eq "root") }'>
        <th><input type='checkbox' class='boardid' name='boardid'  ></th>
    </c:if>
    <!-- boardid의 맨마지막 값을 찾아올때 관리자와 일반사용자일때 구분없이 가져오기 위함 -->
    <td  class="kk" id="boardid"></td>
    <!-- 상세보기 페이지로 가기 위해 링크 설정 -->
    <td><a href="javascript:jsDetailView({boardid})" id="title"></a></td>
    <td id="hit"></td>
    <td id="memberid"></td>
</tr>

공지사항에서 더보기 버튼을 누르면 ajax를 통해서 게시글을 10개 더 불러오는 버튼을 생성해준다. 클릭이벤트를 js 코드로 작성한 ajax 문을 넣은 함수로 등록해준다.

<div>
    <input type="button" id="moreBtn" value="더보기" onclick="jsMoreBoard()"/>
</div>

 

 

 

 

javascript에서 fetch를 이용

<jsMoreBoard()>

더보기를 하고 나면 추가된 항목에 대해서는 게시글 상세보기가 안되는 오류가 발생하는데 그때에는 링크에 javascript의 함수로 갈수 잇는 링크를 걸어주고 boardid와 함께 상세보기 페이지로 넘기면 된다.

//게시글 10개 더 가져오기
function jsMoreBoard(){
    const param = {
    //마지막 boardid를 찍어옴
        boardid: document.querySelector("#noticeList > tr:last-child > td.kk").innerHTML,
    };


    fetch("<c:url value='/board/AjaxAllBoard.do'/>", {
        method: "POST",
        headers: {
          "Content-Type": "application/json; charset=UTF-8",
        },
        body: JSON.stringify(param),
      })
      .then((response) => response.json())
      .then((json) => {
        //json으로 받아온 10개에 대한 리스트
          const noticeList = json.result.noticeList;
          //공지사항리스트에 한줄을 추가 데이터가 들어갈 html 틀을 담을 변수
          const noticeItem = document.querySelector("#noticeItem");
          //공지사항의 현재리스트의 아래 추가해주기 위한 변수
          const noticeListHTML = document.querySelector("#noticeList");

          for (let i=0;i<noticeList.length;i++) {
              const notice = noticeList[i];
              //html 복사!
              const newNoticeItem = noticeItem.cloneNode(true);
          	  //2번이나 써서 한번만 검색해주려고
              const title = newNoticeItem.querySelector("#title");

			  //복사한 html의 boardid영역에 boardid 넣기
              newNoticeItem.querySelector("#boardid").innerText = notice.boardid; 
			 //복사한 html의 title영역에 title넣기
              title.innerText = notice.title;
              //href링크에 boardid넣기
              title.href = title.href.replace("{boardid}", board.boardid);
			 //복사한 html의 memberid영역에 memberid 넣기
              newNoticeItem.querySelector("#memberid").innerText = notice.memberid; 
             //복사한 html의 hit영역에 hit 넣기
              newNoticeItem.querySelector("#hit").innerText = notice.hit; 
			  //이제 다 넣었으니 보이게 설정
              newNoticeBoardItem.style.display = "";
              //복사한 newNoticeItem을 공지사항 글 리스트의 맨아래 추가해준다.
              noticeListHTML.appendChild(newNoticeItem);
        })
 }

 

allNotice.jsp

게시글 상세보기 페이지로 넘어가기 위한 boardid 전달폼

<form name="mForm" id="mForm" method="post" action="<c:url value='/board/ViewOneBoard.do'/>" onsubmit="return false;">
	<input type="hidden" id="boardid" name="boardid"/>
</form>

 

 

게시글 상세보기로 넘겨주는 js 함수

<jsDetailView(noticeId)>

게시글 번호를 등록해주고 form을 넘겨준다. 서버에 요청이 보내지고 게시글 상세보기로 넘어간다.

 

function jsDetailView(noticeId) {
    document.querySelector("#boardid").value = noticeId;
    mForm.submit();	
}

 

 

 

 

 

 

 

 


수업내용 정리

 

ajax를 사용한 더보기

 

--> 서버에서  html을 생성해서 클라이언트에서 출력

--> 순수 DATA만 서버에 전달하고 클라이언트에서 html을 생성 출력

 

 

더보기 하면 페이징이 필요 없다

 

마지막 게시글의 번호보다 큰 값을 가져와야한다.

처음의 sql에서는 where문ㅇ르 작성하지 않고 두번째 sql문 부터 where문을 사용해서 기준에 해당하는 거 뒤로 가져오기 해야한다.

select * from(
select * 
from board
order by boardid desc;
)A
where rownum <=10;

--여기서 내가 원하는 갯수만큼만 나오면된다.
--처음 10개를 가져온다.

select * from(
select * 
from board
where boardid < 104012
order by boardid desc;
)A
where rownum <=10;
--큰거부터 했기ㄸ문에 마지막번호보다 작은 숫자로 더보기 해서 출력하믄 된다.

 

 

 

방법

1. DB에서 자료를 얻는 방법

      -1. 처음 10건 추출하는 방법

      -2. 다음 10건을 얻는 방법

2. 출력을 해줘야한다.

     -1.서버 -> 클라이언트

     -2. 클라이언트 !! 가 생성 --> 서버에 부담이 적음 클라이언트가 모든걸 담당하기 때문에 장점이 있어서 이걸로할꺼임

3. 클라이언트에서 전달받은 Data를 이용해서 HTML로 출력

 

 

controller

getBoardList2()로 진행한다.

 

service

실제 DB에서 정의 해줘야함!! DAO에 선언후 impl에서 재정의

boardid의 인자가 있으면 where절을 써줘야하고 아니면 안써줘야한다.

if문을 이용해 분기!!!

이런 걸 동적SQL이라고 부름 파라미터종류에따라서 바뀌니까

 

값을 바로 써주는건 안좋은 방식이고 ?를 통해서 setInt를 통해 추가해줘야한다.

jsp

items 바꿔주고

서버로 넘어가는 놈에 대해서만 name을 써주면된다.

 

 

 

 

 

ajax를 받아오고 josn을 리턴해주는 함수 생성

boardList를 JSONArray를 통해 jsp페이지로 넘겨주기 위해 생성한다.

근데 일단 object타입으로 넘길수있느지 봐야겠다

controller

 

 

json을 넘기는걸클라이언트쪽에서 안해도 확인할쑤있는게 잇음

api 웹서버에서 사용하면 자동화해주는거임

api를 호출하려면 boardid를 전달해야한다.

근데 ajax를 호출하는 함수를 만들지 않고도 개발하기 전에 확인 할 수 잇는 api 가있다.

postman - 사용할 줄 알아야함!! 개발자하려면 필수!!

https://binit.tistory.com/17

 

포스트맨(Postman) 사용법과 API 실행해 보기

포스트맨의 사용방법과 이를 이용하여 API를 실행하여 테스트 하는 방법에 대해 소개한다. API에 요청을 보내고 결과를 확인하고 싶을 때, 브라우저에 직접 API URL을 작성하여 결과를 확인할 수 있

binit.tistory.com

다운받고 바로 열고 회원가입하면 끝!

들어오면 바로 이 화면 뜸

 

 

 

 

 

 

 

 

 

 

row데이터 json 데이터 선택

밑에 칸에서 이제 보낼값 작성

이제 이렇게 작성한 값은 controller의 board로 넘어가서 실행된다고!!

 

이제 마지막 send를 누름 그런데 만약에 문제가 생기면 이클립스로 실행한 서버에 오류메세지가 뜬다.

아니면 내가 출력하고자 하는 메세지가 출력된다.

 

 

header의 contentType도 설정해줘야하는구냐;;; 이래서 에러뜬거임 다시!!

서버로 부터 넘어온 json데이터를 인간이 보기 편하게 볼수 있는 탭이 pretty이다.

 

위에 save 버튼 눌러서 다음에 또써도된다

 

이제 jsp에 ajax 함수를 작성해준다.

화면에 반영하지는 않고 진짜 받아오는지 확인핟ㄴ다.

화면에 뿌려주기 위해서 querySelector사용해서 그 정보를가져오고

 

css 고급선택자 p336!!! 가상요소!

343페이지 구조 가상선택자! first-child last- child 이런거

 

가상선택자 사용해서 데이터를 뽑아올수있다.

.innerText을 이용해서 태그안의 데이터를 뽑아온다.

ajax에서 파라미터를 보낼때 이걸사용하면 마지막 데이터가 뭐가 오든 지정해서 보낼 수잇다.

이제 ajax에서 데이터를 받아오고 나서는 테이블열을 반복해준다.

임시변수 boardList를 써주는 편이 이해하기 쉽다

list의 값을 하나씩 받아와서 board에 저장해서 사용한다.

innerHtml까지 해주면 원본뒤에 10개씩 추가된다.

project 10_7.zip

 

근데 이렇게 하드코딩으로 하나하나 다쓰는건 안한다!

 

자바스크립트에 dom객체의 요소

624p에서보면 노드 추가 삭제하는 기능이 있었다.

dom도 객체기 때문에 복제할 수있다.

https://webisfree.com/2015-05-06/[%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8]-clonenode()-%ED%95%A8%EC%88%98%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4-%EB%85%B8%EB%93%9C-%EB%B3%B5%EC%A0%9C%ED%95%98%EA%B8%B0 

 

[자바스크립트] cloneNode() 함수를 사용해 노드 복제하기

자바스크립트에서 엘리먼트, 노드를 복제하는 방법을 알아봅니다. 똑같은 엘리먼트를 생성해 변수에 저장하여 사용할 수 있어 편리합니다.

webisfree.com

 

이런식으로 쓰고 true면 원본과 똑같이 복제되고 false면 껍데기만 복제해온다 우리는 true값을 전달해서 사용할 것이다.

추가할 테이블 행 영역을 생성해주고

style="display:none"을 설정하고 

테이블을 변경할꺼라고 !! 라는 느낌으로 EL 태그에서 board없애줌ㅋㅋ 이걸로는 안되서

안의 글자를 바꿔줄꺼임

그러려면 태그들의 id 가 있어야함ㅋㅋㅋ

 

위의 tr에 대한 것을 boardItem에 대한 id로 가져옴

클론해온다

이제 안의 글자를 바꿔줄꺼임

태그의 id값으로 설정해준것에 대한 태그 정보를가져옴

628p 자식노드를 추가할때 사용하는 appendChild()함수를 사용해서 추가한다

 

then(json)안쪽의 것이 아까의 html 어쩌구들이 다 날아감

더보기를 해도 추가가 안되는 오류가 발생해서 수정절차 밟습니다!

 

테이블 안쪽으로 클론할 tr을 옮겨줌 

display none이라 안나옴 그래서 style 값을 바꿔줌

이제 추가된 애들의 a태그의 링크값을 바꿔줘야한다

 

대체할 문자열이라는 의미로 {}로 감싸준다.

태그에서 가져올 값을 확인해보자 console에서

then(json)에서 boardid를 대입해준다.

title객체를 3번이나 쓰기 때문에 임시변수를 사용하는것이 좋다, 검색을 1번만 하기 위해서

 

tbody 안에 안들어가 있어서 같은 수만 계속해서 반복되었음! 추가할 게시글 한줄을 제외한 나머지 table 행들을 tbody안에 추가해준다.

 

<table >
<tr id="boardItem" style="display:none">
    <td id="boardid"></td>
    <td ><a href="boardGet?boardid={boardid}" id="title"></a></td>
    <td id="writer_uid"></td>
    <td id="reg_date"></td>
    <td id="view_count"></td>  
</tr>       

   <tbody id="boardList">
   <tr>
   <c:if test="${loginMember.id=='admin'}">
       <th><input type="checkbox" name="selectall" value="selectall" onclick="selectAll(this)"></th>
   </c:if>
       <th>글번호</th>
       <th>제목</th>
       <th>작성자</th>
       <th>작성날짜</th>
       <th>조회수</th>
   </tr>

    <c:forEach items="${boardList}" var="board">
      <tr>
        <td>${board.boardid}</td>
        <td><a href="boardGet?boardid=${board.boardid}">${board.title}</a></td>
        <td>${board.writer_uid}</td>
        <td>${board.reg_date}</td>
        <td>${board.view_count}</td>  
      </tr>       
    </c:forEach>
    </tbody>	

</table>

 

project10_10.zip

자바 웹을! 책

p177

1. 로그인버튼을 누르면 서버에 전송된다

 

응답방식에 따른 수행을 위해 doGet() doPost()를 재정의 한다 

톰캣컨테이너 - > 톰캣서버

 

contextPath() - >

내 웹 어플리케이션이 실행되는 주소 , 경로!

 

c:url에서 우리가 썼었죠! 내가 가고자하는 경로명 앞에 어플리케이션 명을 써주는 그것!

getHeaderName - > 헤더의 name 속성을 반환 

 

팝업창 display할때 쓰는게 addCookie이다!

addHeader() network -> headers로 넘어오는 정보

 

sendRedirect -> 내부적으로 header 를 이용한다.

내가만든 프로젝트의 경로 말고 진짜로 다른 주소도 가능하다

따라서 서버에서 실행하지 않고 브라우저에서 실행한다 다른 서버의 자원을 실행가능하다.

페이지 주소가 내가 보낸 url로 변경된다

 

 

 

dispatcher가 비슷한 기능을 한다.

서버단에서 실행되고 내가 만든 프로젝트에 잇는 경로만사용할 수잇다. 서버에 요청한 a.do라는 페이지 주소로 나온다.

 

forwarding 하면 do로 요청한 애는 jsp로 요청되어 결과를 전달

 

insertform  -> insert.do를 호출 ->목록! 

목록으로 가는 2가지

1. forward 

f5를 누르면 글이 또 등록된다. insert.do를 통해 서버에 들어가기 때문에

insert가 f5번수만큼 된다.

장바구니 결제하면 10번 누르면 10번결제됨

2. redirect는 글의 리스트만 출력되고 재전송되지 않는다.

 

상황을 위임해야할 때는 forward 

결과가 끝나고 결과만 보여줘야할때는 redirect 

 

경우를 구분해서 사용해야한다.!!!1

 

 

form에서 name은 form을 통해서 서버로 전달할때 쓰고 id는 ajax를 통해서 보낼대 사용한다.

 

 

파일을 업로드할때는 encType을 multipart/form-data를 써야하는데 단독으로 쓰면 안되고 다른 컨테이너 추가 후 사용해ㅑ한다.

클라이언트에 응답보내기

setContentType() - > 클라이언트에게 전송할 데이터 종류를 지정합니다

 

html로 리턴하는건 view가 출력이되는것이다

json으로 리턴하는건 result에 관한게 json이다 입력도 json이다.

 

ajax로 서버에 json을 전달하면 network에 payload부분이 body에서 읽어들인부분에서 json값을 확인할수 있다.

-> 서버에서 getParameter로 읽어들일 수 없다.

 

get 방식

name=value

& 구분

보안취약

4k미만으로 데이터 전송

doGet()을 이용해서 데이터 처리

 

클라이언트 프로그램을  디버깅하는 프로그램fi\

fiddler classic으로 다운 받자

 

개발자도구를 띄워야 가능! 한걸 브라우저 없이 fiddler를 통해 http프로토콜로 데이터 주고 받는걸 다 확인할수 있다. 패킷을 다 가로챔

 

누르면 inspector의 raw에서 데이터를 볼수 잇음

 

데이터를 주고 받는 과정이 정말 중요하다...보안과도 관련된다 -> 내부데이터 구조이기 때문에 내부구조를 확인해본것임

body 데이터와 header !

body에서는 키 값 쌍으로 보내고 header에서는 관련 정보들이 들어잇다.

 

전송데이터는 body에서 4gb이다. 제한이 있음!

 

처리 속도는 get이 더빠르다 header에서 읽어오기 때문에 body 읽기 전에 읽으니까 당연한 얘기

 

로그인, 계산기를 우리가 만든 myservlet으로 진행 해보기

 

유효성검사는 서버로 데이터를 전달하기 전에 하고 들어와야함

 

preparedStatement 를 사용하면 sql문에 ?를 넣어 사용할수있으므로 statment보다 pstmt가 낫다

statement로 할때는 sql문에 like구문에 ' '를 꼭 써야함 

 

statement 를 사용하면 해킹의 위험이 있음

' ' 안에  아래 와같은 쿼리문을 넣으면 테이블전체를 공개당할 수도, 테이블전체를 삭제당할 수도있다.

select * from ALL_TABLES; 

drop table ALL_TABLES;

이때문에 preparedStatement를 사용하면 좋다

 

커넥션 풀을 통해 데이터베이스에 미리 연결을 해두고 사용

 

maxActive = 100개의 요청을 동시 처리 가능

maxIdle = 미리 연결해서 확보 해두는 객체 수

maxWait = 한명이 들어가서 사용하고 다음사람이 할당 받는데 10초 기다리는거

tomcat-dbcp.jar를 통해서 db풀로 대기 중

풀에서 연결을 가져다 쓰려면 name값을 정확히 일치하게 써야한다. 별칭

 

dom node제거하는게 oracle 책에 있음

체크 삭제는 역순으로 지워야함

 

jsp에서 삭제 버튼을 만든다. 줄마다 추가

js코드를 통해서 삭제할 부분 보내는 값에 등록하는거