Developer Note/국비과정 수업내용 정리&저장

24년 11월 1일

DH_PARK 2024. 11. 5. 01:08

오늘은 MVC패턴으로 실제 웹사이트 만드는 작업중.

CRUD 기능을 포함하고 있고 , Servlet 과 jsp 를 나누어서 작업한다. 이클립스 dynamic web project 생성해서 사용.

  • webapp 폴더에 resources 라는 공통적으로 사용할 header 나 footer같은 jsp 파일들 작성.
  • css나 js 같은 공통적으로 적용할 css 나 개별 페이지에 적용할 파일들도 이곳에 저장.
  • webapp 폴더안에서 개발자 외에 타인이 접근할 수 없게 jsp 파일은 WEB-INF 폴더 안에 생성한다.
  • lib 폴더안은 각종 라이브러리를 넣는다.
  • 안에서 세세하게 user,book 사용자별 나누고 , 같은 폴더 안 페이지별로 나눈다.

header나 footer 같은 jsp 파일 불러올때는 include태그를 사용한다.

각 섹션마다 layout 클래스를 적용해서 padding 을 줘서 가운데로 약간 몰리게 설정

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<nav class="layout navbar navbar-expand-lg navbar-light bg-light">
  <div class="container-fluid p-0">
    <a class="navbar-brand" href="#">HOME</a>
    <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
    </button>
    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav me-auto mb-2 mb-lg-0">
   
        <li class="nav-item dropdown m-4 " >
          <a class="nav-link dropdown-toggle " s href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
            도서관리
          </a>
          <ul class="dropdown-menu" aria-labelledby="navbarDropdown">
          
          <%
          	String r = (String)session.getAttribute("role");
          if(r!=null && "ROLE_MEMBER".equals(r)){
          
          %>
            <li><a class="dropdown-item" href="${pageContext.request.contextPath}/book/add">도서등록</a></li>
            <%
          }
            %>
            <li><a class="dropdown-item" href="${pageContext.request.contextPath}/book/list">도서조회</a></li>
          </ul>
        </li>
        <li class="nav-item dropdown m-4">
          <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
            도서대여
          </a>
          <ul class="dropdown-menu" aria-labelledby="navbarDropdown">
            <li><a class="dropdown-item" href="#">대여신청</a></li>
            <li><a class="dropdown-item" href="#">대여조회</a></li>
          </ul>
        </li>
        
      </ul>
      
 
      
    </div>
  </div>
</nav>

부트스트랩을 적용한 nav jsp 파일.

이 부분임.

가장 처음에 들어오면 나오는 index.jsp 페이지이다.

(web.xml 에서 welcome-file 부분에 index.jsp 를 넣어서 실행시 자동으로 시작되게 설정해놓음)

  <!-- Filter -->
  <filter>
  <filter-name>UTF-8_FILTER</filter-name>
  <filter-class>Filter.EncodingFilter</filter-class>
  </filter>
  
  <filter-mapping>
  <filter-name>UTF-8_FILTER</filter-name>
  <url-pattern>/*</url-pattern>
  </filter-mapping>

UTF-8 인코딩을 해주는 서블릿 filter 를 만들어 모든 파일에 매핑시킨다.

(url-pattern을 /* , 즉 모든파일로 지정해서 모든 파일에 한글이 깨지지 않도록 해준다.)

book/add 서블릿으로 form 데이터를 보낸다.

 <servlet>
  <servlet-name>FC</servlet-name>
  <servlet-class>Controller.FrontController</servlet-class>
  </servlet>
  
  <servlet-mapping>
  <servlet-name>FC</servlet-name>
  <url-pattern>/</url-pattern>
  </servlet-mapping>

web.xml 에 frontController가 무조건 실행되게 설정 .

모든 요청은 frontcontroller를 거쳐가게 된다

보낸 form 은 frontController에 의해 받은 데이터에 따라 각각의 다른 Controller를 실행한다.

기본적으로 “/” 일 시에는 HomeController가 실행되는데 이때 HomeController는 index.jsp 로 포워딩을 한다.

예외처리를 하는 ExceptionHadler 함수 정의.

	public void ExceptionHandler(Exception e,HttpServletRequest req,HttpServletResponse resp) throws ServletException{
		try {
			req.setAttribute("exception", e);
			req.getRequestDispatcher("/WEB-INF/view/book/error.jsp").forward(req, resp);
		}catch(Exception ex) {
			throw new ServletException(ex);
		}
	//예외가 일어난다면 이 함수가 실행되는데 . setAttribute 로 예외와 error페이지로 포워딩처리를 하는 기능을 가진다.
	}
@Override
	public void execute(HttpServletRequest req, HttpServletResponse resp) {
		
		try {
			//Method==GET -> 페이지 표시(Forwarding)
			String method = req.getMethod();
			if("GET".equals(method)) {
				System.out.println("[BC] GET /book/add..");
				req.getRequestDispatcher("/WEB-INF/view/book/add.jsp").forward(req, resp);
				return ;
			}

BookAddController에서 execute 함수를 재정의 메소드 방식을 받아온 후에 메소드 방식을 문자열로 받은후 GET 이라면 add.jsp 로 포워딩처리

			// 파라미터 받기
			Integer bookCode = Integer.parseInt( req.getParameter("bookCode") );
			String bookName = req.getParameter("bookName");
			String publisher = req.getParameter("publisher");
			String isbn = req.getParameter("isbn");
			BookDto bookDto = new BookDto(bookCode,bookName,publisher,isbn);
			System.out.println("bookDto : " + bookDto);

파라미터들을 받은 후에 파라미터 정보를담은 BookDto 객체를 생성.

bookService 의 bookRegistration 함수 호출.

Map<String,Object> bookRegistration(BookDto dto){
	Map<String,Object> returnValue = new HashMap();  //리턴을 해줄 hashmap 선언
	
	int result = 0; //데이터가 CRUD 됐는지 결과를 담는 result
	result = bookDaoImpl.insert(dto) //controller에서 받아온 dto 를 받아와서 dao에서 insert 함수를 호출.
	if(result>0) {
		returnValue.put("success",true);  //완료됐다면 true 를 담는다
		returnValue.put("message","도서등록 완료"); //String 형태로 메시지를 담아줌
		}
			}
			return returnValue;

 

Map<String,Object> returnVal=  bookService.bookRegistration(bookDto);
			
			boolean isAdded = (boolean)returnVal.get("success");
			String message = (String)returnVal.get("message");
			
			
			// 뷰로이동(내용전달 - ?)
			if(isAdded) {
				resp.sendRedirect(req.getContextPath()+ "/?message="+URLEncoder.encode(message,"UTF-8"));
				return ;
			}else {
				req.getRequestDispatcher("/WEB-INF/view/book/add.jsp").forward(req, resp);
				return ;
			}

Service 로직에서 dao에 접근하여 Map<String,Object> 형태인 returnValue를 반환받은후에 그 결과값을 Controller까지 가져온다.

조건문으로 isAdded에 true가 들어있다면 주소창에 “ 도서등록 완료 “ 쿼리스트링이 담긴 url 로 Redirect 한다.

false라면 다시 add.jsp 로 돌아가서 정보를 재입력 하도록 한다.

 


페이징 처리

수업시간 내내 어려웠던 페이징처리 부분이다.

코드를 한번 살펴보자.

생성자와 ExceptionHadler 예외처리 함수까지는 동일.

우선 , 페이징 처리를 하기 위해서는 page의 dto 가 따로 필요하다.

 

public class Criteria {
	//현재 페이지
	private int pageno; // 현재 페이지의 넘버
	private int amount; // 한 페이지의 나타나는 요소 개수
	private String type;
	private String keyword;

수업에는 criteria 라고 하는 페이지의 규격을 정하는 class를 정의했다.

 

public class PageDto {
	private static final long serialVersionUID = 5L;

	
	//페이지정보(전체페이지,현재페이지)
	private int totalpage;			//총게시물건수 / amount
	private Criteria criteria;		//현재페이지,한페이지당 읽을 게시물의 건수가 저장되어있음
	
	//블럭정보
	private int pagePerBlock;		//블럭에 표시할 페이지개수(15건 지정)
	private int totalBlock;			//totalpage / pagePerBlock
	private int nowBlock;			//현재페이지번호 /pagePerBlock
	
	//표시할 페이지(블럭에표시할 시작페이지-마지막페이지)
	private int startPage;
	private int endPage;
	
	//NextPrev 버튼 표시여부
	private boolean prev,next;

그리고 page 처리를 하기 위해 페이지의 상태를 나타내는 dto를 정의

	public PageDto(int totalcount,Criteria criteria) {
		
		
		this.criteria = criteria;
		
		//전체페이지 계산
		totalpage =(int)Math.ceil((1.0*totalcount)/criteria.getAmount());  //올림 처리해서 나머지있을 페이지까지 만듬
		
		//블럭계산
		pagePerBlock=15;
		totalBlock = (int)Math.ceil( (1.0*totalpage) / pagePerBlock ); //총 있을 리스트 블럭의 개수
		nowBlock =  (int)Math.ceil ((1.0*criteria.getPageno()) / pagePerBlock); //현재 몇번째 블럭인지 16이상부터는 1.? 이니까 2로 되서 2번째 블럭
		
		//Next,Prev 버튼 활성화 유무
		prev=nowBlock>1;	//현재 블럭이 1보다 크면 이전 버튼이 첫번째 블럭이면 이전버튼이 없으니까 같거나 작아야하는거아닌가
		next=nowBlock<totalBlock;
		
		//블럭에 표시할 페이지 번호 계산
		//
		this.endPage = (nowBlock * pagePerBlock<totalpage) ? nowBlock * pagePerBlock : totalpage ;
		
		this.startPage=nowBlock * pagePerBlock -pagePerBlock + 1;
		 
		
	}

페이지가 생성되었을 때 pageDto. 각 요소들을 보자면

totalPage(전체페이지 개수) : 아래에 있는 이런 블럭의 총 개수를 설정한다.

요소의 총 개수 / 한 페이지의 요소개수 를 해서 총 페이지 개수 설정

 

pagePerBlock = 한 줄에 있을 페이지의 개수. 나는 15개로 길이를 잡았다.

 

totalBlock = 15개 길이짜리 리스트 블럭의 총 개수.

전체페이지개수 / pagePerBlock(15) 를 해서 구한다.

ex) 페이지가 25개라면 15를 나누면 1 하고 나머지 10이 남는데 이 때 남아있는 요소들도 페이지가 있어야 하기 때문에 올림을 해서 뒤에 애매하게 남은 페이지의 개수까지 길이로 잡는다.

 

nowBlock = 현재 몇번째 블럭인지 나타내는 요소. ex) 현재 페이지가 55이라면 15로 나누어서 나머지가 10이 남는데 올림처리를 해서 4가 된다. 4번째 페이지의 중간쯤 있기 때문에..

 

endPage = 나타낼 블럭 마지막 페이지. ex) nowBlock = 2, pagePerBlock = 10, totalpage = 18 , nowBlock * pagePerBlock는 2 * 10 = 20

20이 totalpage (18)보다 크므로, endPage는 totalpage의 값인 18이 됩니다.

 

startPage = 블럭 첫번째 페이지. ex) 현재블럭 4 * 15 - 15 + 1 = 60 - 15 + 1 = 46.

 

	@Override
	public void execute(HttpServletRequest req, HttpServletResponse resp) {
		
		try {
			//Method==GET -> 페이지 표시(Forwarding)
			String method = req.getMethod();
		
				System.out.println("[BC] GET /book/list..");
			
			
				//파라미터		처음 - 전체조회 , 키워드 조회 , 페이지번호
				String pageno = req.getParameter("pageno");		//int 가 맞는데 null 예외 때문에 String 으로 일단 받음 왜 ? String 은 없으면 빈 문자열인데 int 는 없으면 null 이기 떄문
				String amount = req.getParameter("amount");
				String type = req.getParameter("type");
				String keyword = req.getParameter("keyword");
				Criteria criteria = null;
				if(pageno==null) {
					criteria = new Criteria();	//pageNo = 1  amount = 10 type = null = keyword = null	
				}else {
					
						criteria = new Criteria(pageno,10,type,keyword);
					}

Criteria의 정보를 받아온다.

pageno 가 null이라면 pageNo가 1이고 amount가 10 인 기본생성자 생성.

아니라면 pageNo을 받고 amount가 10인 생성자 호출

service 함수 중 Map 타입의 getBooks 반환받음.

Map rvalue 와 List list 선언.

Type이 null 이라면 몇번째 데이터 부터 가져와야 하는지를 담은변수 offset 을 선언 .

현재 페이지 번호 - 1 * 페이지 당 항목수 = 15 , 30 , 45 이런식으로 증가.

BookDao 에서 limit 부분검색하는 select 정의한다.

		pstmt = conn.prepareStatement("SELECT * FROM bookdb.tbl_book order by bookcode desc limit ?,?");
		pstmt.setInt(1, offset);
		pstmt.setInt(2, amount);

offset부터 amount 개수 만큼 조회하도록 하는 select 를 호출한다.

int total = 요소의 총 개수

total 과 criteria 정보를 담은 객체 생성

Map rvalue 에 select 결과 list 와 pageDto 객체정보를 담아서 리턴한다.

다시 컨트롤러로 돌아와

request에 list와 pageDto 를 요청 객체에 담아 list.jsp로 포워딩한다.

tbody에서 list 요소를 불러와 list 길이만큼 순회하며 tbody를 생성한다.

아래 tfoot 페이지 블럭에서는 type 과 keyword를 불러온 뒤에 Prev가 true이고 검색조건type이 null이라면 전체검색으로 url에 페이지번호만 나타낸 채 URL 을 생성한다.

여기서 getStartPage() -1 을 a태그로 감싸서 이전 버튼을 누를시 이전 리스트넘버로 이동되게함.

 

그 후 startpage 와 endpage 를 불러와서 반복문으로 startpage 부터 endpage 까지 생성되도록 하고

type이 있다면 URL 에 type과 keyword 를 가진채로 페이지를 생성한다.

next button 도 마찬가지로 a태그 안에서 +1 을 해서 클릭할 시 다음 페이지로 넘어가게 한다.

 

 



페이징 처리 우리가 이용하는 사이트로 봤을 때는 정말 간단하게 생겨서 몰랐는데 안에는 이렇게 복잡한 로직들이 가득히 들어가 있을 줄은 정말 몰랐다.

언젠가 할 프로젝트에서도 이 단계에서 아마 많이 시간이 소요되고 막히게 될 것 같은 느낌이 든다.

'Developer Note > 국비과정 수업내용 정리&저장' 카테고리의 다른 글

24년 11월 5일  (0) 2024.11.11
24년 11월 4일  (4) 2024.11.06
24년 10월 31일  (1) 2024.11.04
24년 10월 30일  (1) 2024.11.04
24년 10월 29일  (0) 2024.11.03