organize/스프링

스프링 웹 프로젝트 13

001cloudid 2025. 1. 12. 16:42
728x90

PK와 인덱스

  • PK를 생성하면 자동으로 인덱스 생성

인덱스를 이용하기 위한 힌트

  • 개발자의 의도를 힌트를 이용해서 전달
  • 힌트 구문은 잘못되어도 SQL 처리에 지장을 주지 않음
  • 여러 종류의 힌트가 존재 FULL/ INDEX_DESC, INDEX_ASC
    /*+ index_desc(tbl_board pk_board) */

 

데이터를 더 추가한 후

full 힌트

index 힌트

즉 데이터가 많아질수록 order by를 이용하기보다는 index를 이용

 

페이징 처리 → 오라클의 경우 다른 db와 다름, 좀 더 복잡함 → 실행계획에 따라서 어떻게 접근할지 결정됨

※실행계획 : sql을 작성했을 때 데이터베이스에서 어떻게 처리할지 순서가 결정?

 

rownum과 인덱스

  • rownum 가상 테이터 같은 것(실제 데이터가 아님), 출력되면서 붙는 번호
  • 테이블에서 최종적으로 나오면서 붙이는 컬럼
  • 인덱스를 통해서 나오는지, full 스캔을 통하는지에 따라서 rownum이 다르게 나

index → 가장 높은 번호가 가장 먼저 테이블에서 접근이 됨

페이지를 위해서는 아래의 SQL을 이용해야함

select /*+ index_desc(tbl_board pk_board) */ rownum, bno, title from tbl_board where bno>0 and rownum <= 10;

위는 1페이지에 해당할 것이다. 그렇다면 2페이지는?

select /*+ index_desc(tbl_board pk_board) */ rownum, bno, title from tbl_board where bno>0 and rownum >= 11 and rownum <=20;

를 작성하고 조회를 해보면 아무것도 뜨지 않음

FILTER에서 맞는 데이터가 하나도 없기 때문임

가능

from에 인라인뷰(select 문을 테이블 값을 들고오는 테이블로 사용 ) → 거기에 조건을 넣어줌

select *
from (select /*+ index_desc(tbl_board pk_board) */ rownum rn, bno, title from tbl_board where bno>0 and rownum >0 and rownum <=20)
where rn >10;

 

package org.zerock.domain;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class Criteria {

	private int pageNum; // 페이지
	private int amount; // 개수

	public Criteria() {
		this(1,10); // 초기값
	}

	public Criteria(int pageNum, int amount) {
		super();
		this.pageNum = pageNum;
		this.amount = amount;
	}
	
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
	PUBLIC "-//mybatis.org/DTD Mapper 3.0//EN"
	"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="org.zerock.mapper.BoardMapper">

(...생략...)

	<select id="getListWithPaging" resultType="org.zerock.domain.BoardVO"> <!-- xml에서 부등호처리 → cdata -->
		<![CDATA[  
		select *
		from (select /*+ index_desc(tbl_board pk_board) */ rownum rn, bno, title from tbl_board where bno>0 and rownum > 0 and rownum <= (2 * 10))
		where rn > (2-1) *10
		]]>
	</select>
	
</mapper>
package org.zerock.mapper;

import java.util.List;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.zerock.domain.BoardVO;
import org.zerock.domain.Criteria;

import lombok.extern.log4j.Log4j;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
@Log4j
public class BoardMapperTests {

	@Autowired
	private BoardMapper boardMapper;
	
(...생략...)

	@Test
	public void testPaging() {
		Criteria criteria = new Criteria(); // 1 페이지에 10개
		List<BoardVO> list = boardMapper.getListWithPaging(criteria);
		for(int i = 0; i < list.size(); i++) {
			log.info(list);
		}
	}
	
}

일치 → BoardMapper 수정

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
	PUBLIC "-//mybatis.org/DTD Mapper 3.0//EN"
	"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="org.zerock.mapper.BoardMapper">

(...생략...)
	
	<select id="getListWithPaging" resultType="org.zerock.domain.BoardVO"> <!-- xml에서 부등호처리 → cdata -->
		<![CDATA[  
		select *
		from (select /*+ index_desc(tbl_board pk_board) */ rownum rn, bno, title from tbl_board where bno>0 and rownum > 0 and rownum <= (#{pageNum} * #{amount}))
		where rn > (#{pageNum}-1) * #{amount}
		]]>
	</select>
	
</mapper>

변경 후 다시 테스트

 

화면 페이징 처리

package org.zerock.service;

import java.util.List;

import org.zerock.domain.BoardVO;
import org.zerock.domain.Criteria;

public interface BoardService { // 고객과 의사소통하는 용어로 사용하기 때문에 변수명을 주의

//	public void register(BoardVO boardVO);

	public Long register(BoardVO boardVO);
	
	public BoardVO get(Long bno);
	
	public int modify(BoardVO boardVO);
	
	public int remove(Long bno);
	
	public List<BoardVO> getList();

	public List<BoardVO> getList(Criteria criteria); // 오버로딩
	
}
package org.zerock.service;

import java.util.List;

import org.springframework.stereotype.Service;
import org.zerock.domain.BoardVO;
import org.zerock.domain.Criteria;
import org.zerock.mapper.BoardMapper;

import lombok.RequiredArgsConstructor;
import lombok.ToString;
import lombok.extern.log4j.Log4j;

@Service
@Log4j
@RequiredArgsConstructor
@ToString
public class BoardSeriveImpl implements BoardService{ // 인터페이스를 사용 : 실제 객체가 무엇인지 몰라도 타입만 보고 코딩할 수 있게 하기 위해서
	
	private final BoardMapper boardMapper; // @RequiredArgsConstructor + final => 자동 주입

//	@Override
//	public void register(BoardVO boardVO) {
//		boardMapper.insert(boardVO);
//	}

	@Override
	public Long register(BoardVO boardVO) {
		boardMapper.insertSelectKey(boardVO);
		return boardVO.getBno();
	}
	
	@Override
	public BoardVO get(Long bno) {
		return boardMapper.read(bno);
	}

	@Override
	public int modify(BoardVO boardVO) {
		return boardMapper.update(boardVO);
	}

	@Override
	public int remove(Long bno) {
		return boardMapper.delete(bno);
	}

	@Override
	public List<BoardVO> getList() {
		return boardMapper.getList();
	}

	@Override
	public List<BoardVO> getList(Criteria criteria) {
		return boardMapper.getListWithPaging(criteria);
	}
	
}
package org.zerock.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.zerock.domain.BoardVO;
import org.zerock.domain.Criteria;
import org.zerock.service.BoardService;

import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j;

@Controller
@RequestMapping("/board/*")
@RequiredArgsConstructor
@Log4j
public class BoardController {

	private final BoardService boardService;
	
//	@GetMapping("/list")
//	public void list(Model model) { // Model 전달 당시에는 없는데 화면에 추가적인 데이터가 필요한 경우 사용
//		log.info("BoardController list()................");
//		model.addAttribute("list", boardService.getList());
//	}
	@GetMapping("/list")
	public void list(Model model, Criteria criteria) { 
		log.info("BoardController list()................");
		log.info("criteria : " + criteria);
		model.addAttribute("list", boardService.getList(criteria));
	}
	
	@GetMapping("/register")
	public void registerGet() {
		log.info("BoardController regsterGet()............");
	}
	
	@PostMapping("/register")
	public String register(BoardVO boardVO, RedirectAttributes redirectAttributes) {
		log.info("BoardController register()....................");
		boardService.register(boardVO);
		log.info("register : " + boardVO);
		redirectAttributes.addFlashAttribute("result", boardVO.getBno()); // addFlashAttribute 잠깐 결과를 보내는 방식
		// ※ redirectAttributes.addAttribute("result : " + boardVO.getBno()) // 브라우저의 링크와 같이 연결, list?bno= 이런 형식으로 할 때 쓰임
		
		return "redirect:/board/list";
	}
	
	@PostMapping("/modify")
	public String modify(BoardVO boardVO, RedirectAttributes redirectAttributes) {
		log.info("BoardController modify()......................");
		
		int count = boardService.modify(boardVO);
		
		if(count == 1) {
			redirectAttributes.addFlashAttribute("result", "success");
		}
		return "redirect:/board/list";
	}
	
	@PostMapping("/remove")
	public String remove(@RequestParam("bno") Long bno, RedirectAttributes redirectAttributes) {
		log.info("BoardController remove()......................");
		
		int count = boardService.remove(bno);
		
		if(count == 1) {
			redirectAttributes.addFlashAttribute("result", "success");
		}
		return "redirect:/board/list";
	}
	
	@GetMapping({"/get", "/modify"})
	public void get(@RequestParam("bno") Long bno,Model model) {
		model.addAttribute("boardVO", boardService.get(bno));
	}
	
}

Mapper에서 작성자, 작성일, 수정일을 들고오지 않음

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
	PUBLIC "-//mybatis.org/DTD Mapper 3.0//EN"
	"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="org.zerock.mapper.BoardMapper">

(...생략...)
	
	<select id="getListWithPaging" resultType="org.zerock.domain.BoardVO"> <!-- xml에서 부등호처리 → cdata -->
		<![CDATA[  
		select *
		from (select /*+ index_desc(tbl_board pk_board) */ rownum rn, bno, title, writer, regdate,updateDate from tbl_board where bno>0 and rownum > 0 and rownum <= (#{pageNum} * #{amount}))
		where rn > (#{pageNum}-1) * #{amount}
		]]>
	</select>
	
</mapper>

 

JSP 처리

  • BoardController/BoardService/BoardServiceImpl 처리
  • list.jsp 처리

페이지 처리에 필요한 정보

  • 현재 페이지 번호(page) → Criteria
  • 이전과 다음으로 이동 가능한 링크의 표시 여부(prev, next)
  • 화면에서 보여지는 페이지의 시작 번호와 끝 번호(startPage, endPage)
    startPage = endPage - 9
    endPage = (int)(Math.ceil(page/10.0))*10

    total을 통한 endPage의 재계산
    realEnd = (int)(Math.ceil((total * 1.0) / amount))
    if(realEnd < endPage){
        endPage = realEnd
    }

    ※끝 페이지에서 계산하는 것이 더 편함
package org.zerock.domain;

import lombok.Getter;
import lombok.ToString;

@ToString
@Getter
public class PageDTO {

	private int startPage, endPage;
	private boolean prev, next;
	
	private int total;
	private Criteria criteria;
	
	public PageDTO(Criteria criteria, int total) {
		this.criteria = criteria;
		this.total = total;
		
		// 계산
		this.endPage = (int)(Math.ceil(criteria.getPageNum()/10.0)) * 10; 
		
		this.startPage = endPage - 9;
		
		this.prev = this.startPage > 1;
		
		int realEnd = (int)(Math.ceil((total * 1.0) / criteria.getAmount()));
		
		this.endPage = (realEnd < endPage) ? realEnd : endPage;
		
		this.next = this.endPage < realEnd;
	}
	
}
package org.zerock.mapper;

import java.util.List;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.zerock.domain.BoardVO;
import org.zerock.domain.Criteria;
import org.zerock.domain.PageDTO;

import lombok.extern.log4j.Log4j;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("file:src/main/webapp/WEB-INF/spring/root-context.xml")
@Log4j
public class BoardMapperTests {

	@Autowired
	private BoardMapper boardMapper;
	
(...생략...)

	@Test
	public void testPageDTO() {
		Criteria criteria = new Criteria(); 
		PageDTO pageDTO = new PageDTO(criteria, 1376395);
		log.info(pageDTO);
	}
	
}

화면에 전달(아직까지 total은 들고오지 않음)

package org.zerock.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.zerock.domain.BoardVO;
import org.zerock.domain.Criteria;
import org.zerock.domain.PageDTO;
import org.zerock.service.BoardService;

import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j;

@Controller
@RequestMapping("/board/*")
@RequiredArgsConstructor
@Log4j
public class BoardController {

	private final BoardService boardService;
	
//	@GetMapping("/list")
//	public void list(Model model) { // Model 전달 당시에는 없는데 화면에 추가적인 데이터가 필요한 경우 사용
//		log.info("BoardController list()................");
//		model.addAttribute("list", boardService.getList());
//	}
	@GetMapping("/list")
	public void list(Model model, Criteria criteria) { 
		log.info("BoardController list()................");
		log.info("criteria : " + criteria);
		model.addAttribute("list", boardService.getList(criteria));
		model.addAttribute("page", new PageDTO(criteria, 123));
	}

(...생략...)

}

list.jsp에 model 출력

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@include file="../include/header.jsp" %>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
            <div class="row">
                <div class="col-lg-12">
                    <h1 class="page-header">Tables</h1>
                </div>
                <!-- /.col-lg-12 -->
            </div>
            <!-- /.row -->
            <div class="row">
                <div class="col-lg-12">
                    <div class="panel panel-default">
                        <div class="panel-heading">
                            DataTables Advanced Tables
                            <button id="regBtn" type="button" class="btn btn-xs pull-right">Register New Board</button>
                        </div>
                        <!-- /.panel-heading -->
                        <div class="panel-body">
                            <table width="100%" class="table table-striped table-bordered table-hover">
                                <thead>
                                    <tr>
                                        <th>#번호</th>
                                        <th>제목</th>
                                        <th>작성자</th>
                                        <th>작성일</th>
                                        <th>수정일</th>
                                    </tr>
                                </thead>
                                <tbody>
                                <c:forEach items="${list}" var="boardVO">
                                    <tr class="odd gradeX">
                                        <td><c:out value="${boardVO.bno}" /></td>
                                        <td><a href="/board/get?bno=<c:out value='${boardVO.bno}' />"><c:out value="${boardVO.title}" /></a></td>
                                        <td><c:out value="${boardVO.writer}" /></td>
                                        <td><fmt:formatDate value="${boardVO.regdate }" pattern="yyyy-MM-dd"/> </td>
                                        <td><fmt:formatDate value="${boardVO.updateDate }" pattern="yyyy-MM-dd" /></td>
                                    </tr>
                                </c:forEach>
                                </tbody>
                            </table>
                            <!-- /.table-responsive -->
                            
                            <h3>${page}</h3>
                            
               (..생략...)

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@include file="../include/header.jsp" %>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
            <div class="row">
                <div class="col-lg-12">
                    <h1 class="page-header">Tables</h1>
                </div>
                <!-- /.col-lg-12 -->
            </div>
            <!-- /.row -->
            <div class="row">
                <div class="col-lg-12">
                    <div class="panel panel-default">
                        <div class="panel-heading">
                            DataTables Advanced Tables
                            <button id="regBtn" type="button" class="btn btn-xs pull-right">Register New Board</button>
                        </div>
                        <!-- /.panel-heading -->
                        <div class="panel-body">
                            <table width="100%" class="table table-striped table-bordered table-hover">
                                <thead>
                                    <tr>
                                        <th>#번호</th>
                                        <th>제목</th>
                                        <th>작성자</th>
                                        <th>작성일</th>
                                        <th>수정일</th>
                                    </tr>
                                </thead>
                                <tbody>
                                <c:forEach items="${list}" var="boardVO">
                                    <tr class="odd gradeX">
                                        <td><c:out value="${boardVO.bno}" /></td>
                                        <td><a href="/board/get?bno=<c:out value='${boardVO.bno}' />"><c:out value="${boardVO.title}" /></a></td>
                                        <td><c:out value="${boardVO.writer}" /></td>
                                        <td><fmt:formatDate value="${boardVO.regdate }" pattern="yyyy-MM-dd"/> </td>
                                        <td><fmt:formatDate value="${boardVO.updateDate }" pattern="yyyy-MM-dd" /></td>
                                    </tr>
                                </c:forEach>
                                </tbody>
                            </table>
                            <!-- /.table-responsive -->
                            
                            <h3>${page}</h3>
                            <div class="pull-right">
                            	<ul>
									<c:forEach begin="${page.startPage}" end="${page.endPage}" var="num">
										<li>${num}</li>
									</c:forEach>                            	
                            	</ul>
                            </div>
                            
         (..생략...)

페이지 번호 링크 부트스트랩을 이용 list.jsp

(...생략...)
                            <h3>${page}</h3>
                            <div class="pull-right">
                            	<ul class="pagination"> 
									<c:forEach begin="${page.startPage}" end="${page.endPage}" var="num">
										<li class="page-item"><a class="page-link" href="#">${num}</a></li>
									</c:forEach>                            	
                            	</ul>
                            </div>
(...생략...)

prev, next 버튼

(...생략...)
                            <h4>${page}</h4>
                            <div class="pull-right">
                            	<ul class="pagination"> 
								    <li class="page-item disabled"><a class="page-link" href="#" tabindex="-1">Previous</a></li>
									<c:forEach begin="${page.startPage}" end="${page.endPage}" var="num">
										<li class="page-item"><a class="page-link" href="#">${num}</a></li>
									</c:forEach>                            	
								    <li class="page-item"><a class="page-link" href="#">Next</a></li>
                            	</ul>
                            </div>
(...생략...)

Previous, Next list.jsp

(...생략...)
                            <h4>${page}</h4>
                            <div class="pull-right">
                            	<ul class="pagination">
                            		<c:if test="${page.prev}"> 
								    	<li class="page-item"><a class="page-link" href="#" tabindex="-1">Previous</a></li>
									</c:if>
									<c:forEach begin="${page.startPage}" end="${page.endPage}" var="num">
										<li class="page-item"><a class="page-link" href="#">${num}</a></li>
									</c:forEach>                
									<c:if test="${page.next}">            	
								    	<li class="page-item"><a class="page-link" href="#">Next</a></li>
                            		</c:if>
                            	</ul>
                            </div>
(...생략...)

다음 아래 사진과 같은 작업

(...생략...)
 <h4>${page}</h4>
                            <div class="pull-right">
                            	<ul class="pagination">
                            		<c:if test="${page.prev}"> 
								    	<li class="page-item"><a class="page-link" href="#" tabindex="-1">Previous</a></li>
									</c:if>
									<c:forEach begin="${page.startPage}" end="${page.endPage}" var="num">
										<li class="page-item ${page.criteria.pageNum == num? 'active' :''}"><a class="page-link" href="#">${num}</a></li>
									</c:forEach>                
									<c:if test="${page.next}">            	
								    	<li class="page-item"><a class="page-link" href="#">Next</a></li>
                            		</c:if>
                            	</ul>
                            </div>
(...생략...)

 

728x90

'organize > 스프링' 카테고리의 다른 글

스프링 웹 프로젝트 15  (0) 2025.01.14
스프링 웹 프로젝트 14  (0) 2025.01.13
스프링 웹 프로젝트 12  (0) 2025.01.11
스프링 웹 프로젝트 11  (0) 2025.01.09
스프링 웹 프로젝트 10  (0) 2025.01.08