분류 전체보기 (277)

반응형

 

트랙잭션

 

: 데이터베이스의 상태를 변화시키기 해서 수행하는 작업의 단위

 

네임스페이스 하나에 스키마(url)가 2개 추가됨

 

 

  • 설정

root-context.xml

상단에 수정

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:context="http://www.springframework.org/schema/context"
   xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
   xmlns:aop="http://www.springframework.org/schema/aop"
   xmlns:tx="http://www.springframework.org/schema/tx"
   xsi:schemaLocation="http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
      http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
      http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
      http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd">

 

</beans> 안에 추가

   <!-- 트랜잭션 관리자의 빈을 정의 -->
   <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
   </bean>
   
   <!-- 에너테이션 기반의 트랜잭션 제어를 활성화 함 -->
   <tx:annotation-driven/>

 

=> dataSource를 쓰기 위해선 DB 정보가 필요함

더보기

root-context.xml 파일에 추가 되어있어야 함

 

root-context.xml

	<bean id="dataSource" 
	   class="org.apache.commons.dbcp2.BasicDataSource" 
	   destroy-method="close">
	   <property name="driverClassName" 
	   value="oracle.jdbc.driver.OracleDriver" />
	   <property name="url" 
	   value="jdbc:oracle:thin:@localhost:1521:xe" />
	   <property name="username" value="pc10_2" />
	   <property name="password" value="java" />
	</bean>

 

 

 

  • 실습

EmployeeServiceImpl.java

상단에 @Transactional 추가

	//root-context.xml로 인해서 오류 발생 시 롤백을 해줌
	@Transactional
	@Override
	public int createPost(EmployeeVO employeeVO) {
		/*
		EmployeeVO(empNo=A111, empName=김, empAddress=대전 1, empTelno=010-111-2222, empSalary=100, filename=null, 
		licenseVOList=[
			LicenseVO(empNo=null, licNo=A1111, licNm=언어영역, licDt=Fri May 03 00:00:00 KST 2024), 
			LicenseVO(empNo=null, licNo=A1112, licNm=수학영역, licDt=Sat May 04 00:00:00 KST 2024)
		])
		 */
		//1) EMPLOYEE 테이블에 insert
		//employeeVO.getUploadFile() : MultipartFile
		int result = this.employeeDao.createPost(employeeVO);
		
		List<LicenseVO> licenseVOList = employeeVO.getLicenseVOList();
		log.info("createPost->licenseVOList : " + licenseVOList);
		
		for (LicenseVO licenseVO : licenseVOList) {
			//2) LicenseVO(empNo=A111, licNo=A1111, licNm=언어영역, licDt=Fri May 03 00:00:00 KST 2024),
			licenseVO.setEmpNo(employeeVO.getEmpNo());
			result += this.employeeDao.insertLicenseVO(licenseVO);
		}
		
		//3) 파일업로드
		MultipartFile uploadFile = employeeVO.getUploadFile();
											// 파일 객체	, attachVO.globalCode
		result += uploadController.uploadOne(uploadFile, employeeVO.getEmpNo());
		
		//4) employeeVO.filename=null
		//글로벌 코드를 조건으로 하여 첫번쨰 첨부파일 객체를 가져옴
		AttachVO attachVO = this.attachDao.getFileName(employeeVO.getEmpNo());
		log.info("attachVO(2) : " + attachVO);
		
		//5) employeeVO.filename을 업데이트
		result += this.employeeDao.updateFileName(attachVO);
		
		return result;
	}

 

 

 

예외처리

 

: 특정한 문제가 일어났을 때 처리를 중단하고 다른 처리를 하는 것

 

상태 코드를 이용하여 오류 페이지 설정을 할 수 있음

 

- 예외 종류

스프링 프레임워크 예외

사용자 정의 예외

의존 라이브러리에서 발생한 예외

시스템 예외

 

 

 

  • HTTP 오류 코드 
오류 코드 설명
400 Bad Request. 문법 오류(잘못 입력한 url)
404* Not Found. 요청한 문서를 찾지 못함(url확인 및 캐시 삭제가 필요한 상태)
405 Method not allowed. 메소드 허용 안됨(메소드 매핑이 안 될 때 발생)
415 서버의 요청에 대한 승인 거부. (ContentType, Content Encoding 데이터 확인 필요)
500* 서버 내부 오류. (웹 서버가 요청사항을 수행할 수 없을 때 발생)
505 HTTP Version Not Supported.

 

 

 

  • 실습

web.xml

상단 변경

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.1" xmlns="http://java.sun.com/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
   https://java.sun.com/xml/ns/javaee/web-app_3_1.xsd">

 

</web-app> 안에 추가

	<error-page>
		<error-code>400</error-code>
		<location>/error/error400</location>
	</error-page>
	<error-page>
		<error-code>404</error-code>
		<location>/error/error404</location>
	</error-page>
	<error-page>
		<error-code>500</error-code>
		<location>/error/error500</location>
	</error-page>
	<!-- HTTP 상태 코드를 사용하여 오류 페이지 설정 끝 -->

 

 

ErrorController.java

package kr.or.ddit.utils;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class ErrorController {
	
	//요청 URI : /error/error400
	@GetMapping("/error/error400")
	public String error400() {
		//forwarding : jps
		return "error/error400";
	}
	
	//요청 URI : /error/error404
	@GetMapping("/error/error404")
	public String error404() {
		//forwarding : jps
		return "error/error404";
	}
	
	//요청 URI : /error/error500
	@GetMapping("/error/error500")
	public String error500() {
		//forwarding : jps
		return "error/error500";
	}
}

 

 

error400.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<div class="content-wrapper pt-5">
  <section class="content">
    <div class="error-page">
      <h2 class="headline text-warning">400</h2>
      <div class="error-content">
        <h3><i class="fas fa-exclamation-triangle text-warning"></i> Oops! Page not found.</h3>
        <p>
          	잘못된 요청 정보입니다. <a href="{% link index.md %}">return to 메인</a>.
        </p>
      </div>
    </div>
  </section>
</div>

 


error404.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<div class="content-wrapper pt-5">
  <section class="content">
    <div class="error-page">
      <h2 class="headline text-warning">404</h2>
      <div class="error-content">
        <h3><i class="fas fa-exclamation-triangle text-warning"></i> Oops! Page not found.</h3>
        <p>
          	잘못된 요청 정보입니다. <a href="/">return to 메인</a>.
        </p>
      </div>
    </div>
  </section>
</div>

 


error500.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<div class="wrapper">
    <!-- Main content -->
    <section class="content">
      <div class="error-page">
        <h2 class="headline text-danger">500</h2>

        <div class="error-content">
          <h3><i class="fas fa-exclamation-triangle text-danger"></i> Oops! Something went wrong.</h3>

          <p>
            We will work on fixing that right away.
            Meanwhile, you may <a href="/">return to dashboard</a> or try using the search form.
          </p>

          <form class="search-form">
            <div class="input-group">
              <input type="text" name="search" class="form-control" placeholder="Search">

              <div class="input-group-append">
                <button type="submit" name="submit" class="btn btn-danger"><i class="fas fa-search"></i>
                </button>
              </div>
            </div>
            <!-- /.input-group -->
          </form>
        </div>
      </div>
      <!-- /.error-page -->
</div>
<!-- ./wrapper -->

 

결과 화면1 : 404 예시

 

 

LprodController.java

	/*
	요청URI : /lprod/list or /lprod/list?currentPage=2 or /lprod/list?currentPage=
	요청파라미터 : {keyword=개똥이}
	요청방식 : get
	*/
	@RequestMapping(value="/list", method=RequestMethod.GET)
	public ModelAndView list(ModelAndView mav, 
					@RequestParam(value="currentPage", required=false, defaultValue="1") int currentPage, 
					@RequestParam(value="keyword",required=false,defaultValue="") String keyword) {
		log.info("list에 왔다");
		log.info("keyword : " + keyword);
		
		
		System.out.println(10/0);
		
		
		Map<String,Object> map = new HashMap<String,Object>();
		map.put("keyword",keyword);
		map.put("currentPage",currentPage);
		
		//전체 행의 수
		int total = this.lprodService.getTotal(map);
		log.info("list->total : " + total);
		
		//Model(데이터)
		//상품분류 목록
		List<LprodVO> lprodVOList = this.lprodService.list(map);
		log.info("list->lprodVOList : " + lprodVOList);
		
		
		mav.addObject("articlePage", new ArticlePage<LprodVO>(total, currentPage, 10, lprodVOList, keyword));
		
		//View(jsp)
		mav.setViewName("lprod/list");
		
		return mav;
	}

 

결과 화면2

 

 

 

예외 타입을 이용한 에러 페이지 설정

 

: 예외 타입을 확실하게 기입해놓아 에러를 처리하는 방식

 

 

- 설정 방법

1. 웹 컨테이너(tomcat서버) 설정 파일(web.xml)의 exception-type 태그 요소에 예외 타입을 설정

2. 웹 컨테이너(tomcat서버) 설정 파일(web.xml)의 location 요소에 이동 대상 페이지 및 URI를 지정함

IOException, SQLException, NullPointerException, ArrayIndexOutOfBoundsException, ArtimeticException(0으로 나눌경우)

 

 

 

  • 실습

web.xml

</web-app> 안에 추가

	<error-page>
		<exception-type>java.lang.ArithmeticException</exception-type>
		<location>/error/errorException</location>
	</error-page>

 

 

ErrorController.java

하단에 추가

	//요청 URI : /error/errorException
	@GetMapping("/error/errorException")
	public String errorException() {
		//forwarding : jps
		return "error/errorException";
	}

 

 

errorException.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<div class="content-wrapper pt-5">
  <section class="content">
    <div class="error-page">
      <h2 class="headline text-warning">errorException</h2>
      <div class="error-content">
        <h3><i class="fas fa-exclamation-triangle text-warning"></i> Oops! Page not found.</h3>
        <p>
          	숫자를 0으로 나눌 수 없습니다. <a href="/">return to 메인</a>.
        </p>
      </div>
    </div>
  </section>
</div>

 

결과 화면3

 

 

 

  • 기본 오류 페이지 설정

web.xml

	<!-- 기본 오류 페이지 설정 시작 -->
	<error-page>
	   <location>/error/errorDefault</location>
	</error-page>
	<!-- 기본 오류 페이지 설정 시작 -->

 

 

 

예외 처리 애너테이션

 

 

대표적으로 5가지의 종류가 있음

IOException

SQLException

NullPointerException

ArrayIndexOutOfBoundsException

ArithmeticException

 

 

CommonExceptionHandler.java

package kr.or.ddit.exception;

import java.io.IOException;
import java.sql.SQLException;

import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

import lombok.extern.slf4j.Slf4j;

//스프링 컨트롤러에서, 발생하는 예외를 처리하는 핸들러 클래스임을 명시함
//web.xml, errorPage, try~catch.. 말고 컨트롤러에서 처리함
@Slf4j
@ControllerAdvice
public class CommonExceptionHandler {

	//괄호 안에 설정한 예외 타입을 해당 메서드가 처리한다는 의미
	//IOException, SQLException, NullPointerException, 
	//ArrayIndexOutOfBoundsException, ArithmeticException   
	@ExceptionHandler(Exception.class)
	public String handle(Exception e, Model model) {
		log.error("CommonExceptionHandler->handle : " + e.toString());
		
		//예외에 대한 내용을 Model 객체를 이용해서 전달하여 뷰(View) 화면에서 출력이 가능함
		model.addAttribute("exception", e);
		
		//forwarding : jsp
		return "error/errorCommon";
	}
}

 

 

errorCommon.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<p>${exception.getMessage()}</p>
<p>
	<c:forEach var="stack" items="${exception.getStackTrace()}" varStatus="stat">
		<h3>${stack.toString()}</h3>
	</c:forEach>
</p>

 

결과 화면4

 

 


errorCommon.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<section class="content">
   <div class="error-page">
   <!-- model.addAttribute("exception", e); -->
      <h2 class="headline text-warning">${exception.getMessage()}</h2>
      <div class="error-content">
         <c:forEach var="stack" items="${exception.getStackTrace()}" varStatus="stat">
            <h3>
               <i class="fas fa-exclamation-triangle text-warning"></i>${stack.toString()}
            </h3>
         </c:forEach>
         <p>
             만약, 메인으로 이동하고자 하면
            <a href="/">메인</a> 을 클릭해주세요.
         </p>
         <form class="search-form">
            <div class="input-group">
               <input type="text" name="search" class="form-control"
                  placeholder="Search">
               <div class="input-group-append">
                  <button type="submit" name="submit" class="btn btn-warning">
                     <i class="fas fa-search"></i>
                  </button>
               </div>
            </div>

         </form>
      </div>

   </div>

</section>

 

결과 화면5

 

 

 

404 에러 페이지 처리

 

 

404를 프로그래밍적으로 처리하고 싶다면 404 발생 시 예외를 발생시키도록 설정해야 한다. (기본적으로 404는 exception 상황이 아니다.) 

 

=> web.xml에서 DispatcherServlet을 등록할 때 throwExceptionIfNoHandlerFound 초기화 파라미터를 true로 설정

 

 

 

web.xml
servlet-context.xml의 하단 </init-param> 바로 아래에 추가

		<!-- 404오류를 처리할 수 있도록 설정 -->
		<init-param>
			<param-name>throwExceptionIfNoHandlerFound</param-name>
			<param-value>true</param-value>
		</init-param>

 

 

CommonExceptionHandler.java

하단에 추가

	@ExceptionHandler(NoHandlerFoundException.class)
	@ResponseStatus(HttpStatus.NOT_FOUND) // 상태코드가 not found => 404
	public String handle404(Exception e) {
		log.error("CommonExceptionHandle->handle : " + e.toString());
		
		return "error/error404";
	}

 

결과 화면6

 

 

반응형
반응형

 

AOP 설정

 

: 관점 지향 프로그래밍

 

 

Aspect(애스팩트) : AOP(Aspect Oriented Programming)의 단위가 되는 횡단 관심사
- 횡단 관심사(Cross-Cutting Concern) : 핵심(core) 비즈니스 로직(삼겹살구어먹기, 빵또아의 아이스크림)과 다소 거리가 있지만, 여러 모듈에서 공통적이고 반복적인 처리를 요구하는 내용(불판닦기, 불판교체, 빵또아의 빵)
- 횡단 관심사 분리(Separation Of Cross-Cutting Concern) : 횡단 관심사에 해당하는 부분(불판닦기, 불판교체, 빵또아의 빵)을 분리해서 한 곳으로 모으는 것을 의미
- Component : 골뱅이Aspect와 짝궁. component-scan시 "여기 봐주세요"라는 의미
- JoinPoint : 어드바이스가 적용될 수 있는 위치
- Advice(로그 출력 한다) : 어떤 부가기능(불판닦기)을 언제(삼겹살을 굽기 전(Before)에) 사용할지 정의
            * 언제? 
            - Before : 조인포인트(createPost()) 전에 실행. (삼겹살을 굽기 직전에)
            - After : 조인 포인트(createPost())에서 처리가 완료된 후 실행(삽겹살을 굽고 먹은 직후 실행)
            - Around : 조인 포인트(createPost()) 전후에 실행(삽겹살을 굽기 직전과 먹은 직후 실행)
            - After Returning : 조인 포인트(createPost())가 정상적으로 종료 후 실행
            - After Throwing : 조인 포인트(createPost())에서 예외 발생 시 실행. 예외가 발생안되면 실행 안함

 

 

pom.xml

dependencies 안에 추가

      <!-- AOP(Aspect Oriented Programming : 관점 지향 프로그래밍) 시작 
      1) aspectjrt => 이미 있으므로 생략
      2) aspectjweaver => 없으므로 의존 관계를 정의
      -->
      <!-- https://mvnrepository.com/artifact/aspectj/aspectjweaver
      1.5.4 -->
      <!-- AspectJ RunTime -->
      <dependency>
          <groupId>org.aspectj</groupId>
          <artifactId>aspectjrt</artifactId>
          <version>${org.aspectj-version}</version>
      </dependency>    
      <!-- AspectJ Weaver -->
      <dependency>
          <groupId>org.aspectj</groupId>
          <artifactId>aspectjweaver</artifactId>
          <version>${org.aspectj-version}</version>
      </dependency>
      <!-- AspectJ Tools -->
      <dependency>
          <groupId>org.aspectj</groupId>
          <artifactId>aspectjtools</artifactId>
          <version>${org.aspectj-version}</version>
      </dependency>
      <!-- AOP(Aspect Oriented Programming : 관점 지향 프로그래밍) 끝 -->

 

이미 있는 경우 아래 코드 지움

		<dependency>
		    <groupId>org.aspectj</groupId>
		    <artifactId>aspectjrt</artifactId>
		    <version>${org.aspectj-version}</version>
		</dependency>

 

 

root-context.xml

상단 부분 수정

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns:context="http://www.springframework.org/schema/context"
   xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
   xmlns:aop="http://www.springframework.org/schema/aop"
   xmlns:tx="http://www.springframework.org/schema/tx"
   xsi:schemaLocation="http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
      http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
      http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
      http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd
      http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd">

 

하단에 추가

   <!-- 스프링 AOP 활성화 -->
   <aop:aspectj-autoproxy>
   
   </aop:aspectj-autoproxy>
   
   <!-- kr.or.ddit.aop 패키지를 컴포넌트 스캔 대상으로 등록 -->
   <context:component-scan base-package="kr.or.ddit.aop">
   </context:component-scan>

 

 

 

포인트컷 표현식

 

 

ServiceLoggerAdvice.java

로보트에 : AOP대상(로그, 보안, 트랜잭션, 에러)
포인트컷 표현식. 별쩜쩜별괄호쩜쩜괄호


excution : 포인트컷(대상(메소드)을 선별하는 것) 지정자
(* : 임의의 1개의 리턴타입
.. : 임의의 0개 이상


kr.or.ddit.*..*(..) : 패키지 밑의 각각의 패키지가 있고 그 하위에 모든 파일/패키지 각각의 메소드가 있고
         (..) : 모든 파라미터


결론 : 포인트컷에 포함된 메서드를 대상으로 그 메서드가 실행되기 전에 로그를 출력해보자
Before어드바이스 : 조인 포인트 전에 실행됨. 예외가 발생하는 경우만 제외하고 항상 실행됨

 

 

 

  • @Before 예시

ServiceLoggerAdvice.java

package kr.or.ddit.aop;

import java.util.Arrays;

import org.aopalliance.intercept.Joinpoint;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
@Aspect
public class ServiceLoggerAdvice {
	@Before("execution(* kr.or.ddit.*..*(..))")
	public void startLog(JoinPoint jp) {
		log.info("startLog");
		
		//.getSignature() : 어떤 클래스의 어떤 메서드가 실행되었는지 보여줌. 파라미터 타입은 무엇인지 보여줌
		// kr.or.ddit.service.BoardService.register(BoardVO)
		log.info("startLog : " + jp.getSignature());
		
		//.getArgs() : 전달 된 파라미터 정보를 보여줌
		// [BoardVO [boardNo=127,title=개똥이]]
		log.info("startLog : " + Arrays.toString(jp.getArgs()));
	}
}

 

 

=> Maven build 하기

 

 

결과 화면1-1

 

결과 화면1-2

 

 

+보이지 않을 시

더보기

log4j.xml

debug 로 수정해서 테스트 해보기 => 다 하면 원래대로 되돌려야 함

	<!-- Application Loggers -->
	<logger name="kr.or.ddit">
		<level value="debug" />
	</logger>

	<root>
		<priority value="debug" />
		<appender-ref ref="console" />
	</root>

 

 

  • @AfterReturning 추가

ServiceLoggerAdvice.java

하단에 추가

package kr.or.ddit.aop;

import java.util.Arrays;

import org.aopalliance.intercept.Joinpoint;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
@Aspect
public class ServiceLoggerAdvice {
	@Before("execution(* kr.or.ddit.*..*(..))")
	public void startLog(JoinPoint jp) {
		log.info("startLog");
		
		//.getSignature() : 어떤 클래스의 어떤 메서드가 실행되었는지 보여줌. 파라미터 타입은 무엇인지 보여줌
		// kr.or.ddit.service.BoardService.register(BoardVO)
		log.info("startLog : " + jp.getSignature());
		
		//.getArgs() : 전달 된 파라미터 정보를 보여줌
		// [BoardVO [boardNo=127,title=개똥이]]
		log.info("startLog : " + Arrays.toString(jp.getArgs()));
	}
	
	//어드바이스
	//조인 포인트(메소드)가 정상적으로 종료한 후에 실행됨. 예외 발생 시 실행 안됨
	@AfterReturning("execution(* kr.or.ddit.*..*(..))")
	public void logReturning(JoinPoint jp) {
		log.info("logReturning");
		
		//.getSignature() : 어떤 클래스의 어떤 메서드가 실행되었는지 보여줌. 파라미터 타입은 무엇인지 보여줌
		// kr.or.ddit.service.BoardService.register(BoardVO)
		log.info("logReturning : " + jp.getSignature());
	}
}

 

결과 화면2 : startLog 후 작동하며 중간에 에러 발생 시 logReturning이 작동 안 함

 

 

 

  • @AfterThrowing

ServiceLoggerAdvice.java

하단에 추가

	//어드바이스
	//조인 포인트(메서드)에서 예외 발생 시 실행. 예외가 발생 안 되면 실행 안 됨
	//throwing : 예외 발생 시 메시지가 들어감
	@AfterThrowing(pointcut="excution(* kr.or.ddit.*..*(..))", throwing="e")
	public void logException(JoinPoint jp, Exception e) {
		log.info("logException");
		
		//.getSignature() : 어떤 클래스의 어떤 메서드가 실행되었는지 보여줌. 파라미터 타입은 무엇인지 보여줌
		// kr.or.ddit.service.BoardService.register(BoardVO)
		log.info("logException : " + jp.getSignature());

		//예외 메시지를 보여줌
		log.info("logException : " + e);
	}

 

 

 

  • @After

ServiceLoggerAdvice.java

하단에 추가

	//어드바이스
	//조인 포인트(메서드)를 완료한 후에 실행함. 예외 발생이 되더라도 항상 실행 됨
	@After("execution(* kr.or.ddit.*..*(..))")
	public void endLog(JoinPoint jp) {
		log.info("endLog");
		
		//.getSignature() : 어떤 클래스의 어떤 메서드가 실행되었는지 보여줌. 파라미터 타입은 무엇인지 보여줌
		// kr.or.ddit.service.BoardService.register(BoardVO)
		log.info("endLog : " + jp.getSignature());
		
		//.getArgs() : 전달 된 파라미터 정보를 보여줌
		// [BoardVO [boardNo=127,title=개똥이]]
		log.info("endLog : " + Arrays.toString(jp.getArgs()));
	}

 

결과 화면3

 

 

 

  • @Around 예시

ServiceLoggerAdvice.java

하단에 추가

	//ProceedingJoinPoint : around 어드바이스에서 사용함
	// 횡단관심사 - 포인트컷 대상 core메소드 - 횡단관심사
	// 스프링프레임워크가 컨트롤 하고 있는 비즈니스로직 호출을 가로챔. 책임이 around 어드바이스로 전가됨
	// 그래서 비즈니스 메소드에 대한 정보를 around 어드바이스 메소드가 가지고 있어야 하고
	// 그 정보를 스프링 컨테이너가 around 어드바이스 메소드로 넘겨주면
	// ProceedingJoingPoint 객체로 받아서 around 어드바이스가 컨트롤 시 활용함
	@Around("execution(* kr.or.ddit.*..*(..))")
	public Object timeLog(ProceedingJoinPoint pjp) throws Throwable{
	   
		//메소드 실행 직전 시간 체킹
		long startTime = System.currentTimeMillis();
		log.info("pjpStart : " + Arrays.toString(pjp.getArgs()));
		
		//메소드 실행
		Object result = pjp.proceed();
		
		//메소드 실행 직후 시간 체킹
		long endTime = System.currentTimeMillis();
		log.info("pjpEnd : " + Arrays.toString(pjp.getArgs()));
		
		//직후 시간 - 직전 시간 => 메소드 실행 시간
		log.info(pjp.getSignature().getName() + " : " + (endTime - startTime));
		
		return result;
	}

 

결과 화면4 : 총 509초가 걸림

 

 

 

=> log.info 를 사용하지 않아도 편히 이용할 수 있음

 

 

반응형
반응형

 

페이징 처리

 

 

total : 모든 행
size : 행을 몇개씩 나누는지
totalPages : total/size
블록크기 : 한 화면에 몇개씩 보이는지 ex) [1][2][3][4][5]
  - startPage : 블록의 시작점 페이지

페이징 설명 예시

 

 

 

실습

 

 

ArticlePage.java

package kr.or.ddit.utils;

import java.util.List;

public class ArticlePage<T> {
	//전체글 수
	private int total;
	
	// 현재 페이지 번호
	private int currentPage;
	
	// 전체 페이지수 
	private int totalPages;
	
	// 블록의 시작 페이지 번호 ex) [1][2][3] 에서의 1
	private int startPage;
	
	//블록의 종료 페이지 번호 ex) [1][2][3] 에서의 3
	private int endPage;
	
	//검색어
	private String keyword = "";
	
	//요청URL
	private String url = "";
	
	//select 결과 데이터
	private List<T> content;
	
	//페이징 처리
	private String pagingArea = "";
	
	
	//생성자(Constructor) : 페이징 정보를 생성
	//               753            1            10         select결과10행
	public ArticlePage(int total, int currentPage, int size, List<T> content, String keyword) {
		//size : 한 화면에 보여질 목록의 행 수
		this.total = total; //753
		this.currentPage = currentPage; //1
		this.content = content;
		this.keyword = keyword;
		
		//전체글 수가 0이면?
		if(total==0) {
			totalPages = 0;//전체 페이지 수
			startPage = 0;//블록 시작번호
			endPage = 0; //블록 종료번호
		}else {//글이 있다면
			//전체 페이지 수 = 전체글 수 / 한 화면에 보여질 목록의 행 수
			//3 = 31 / 10
			totalPages = total / size;//75
			
			//나머지가 있다면, 페이지를 1 증가
			if(total % size > 0) {//나머지3
			   totalPages++;//76
			}
			
			//페이지 블록 시작번호를 구하는 공식
			// 블록시작번호 = 현재페이지 / 페이지크기 * 페이지크기 + 1
			startPage = currentPage / 5 * 5 + 1;//1
			
			//현재페이지 % 페이지크기 => 0일 때 보정
			if(currentPage % 5 == 0) {
			   startPage -= 5;
			}
			
			//블록종료번호 = 시작페이지번호 + (페이지크기 - 1)
			//[1][2][3][4][5][다음]
			endPage = startPage + (5 - 1);//5
			
			//종료페이지번호 > 전체페이지수
			if(endPage > totalPages) {
			   endPage = totalPages;
			}
		}
		
		pagingArea += "<div class='col-sm-12 col-md-7'>";
		pagingArea += "<div class='dataTables_paginate paging_simple_numbers' id='example2_paginate'>";
		pagingArea += "<ul class='pagination'>";
		pagingArea += "<li class='paginate_button page-item previous "; 
		if(this.startPage<6) {
			pagingArea += "disabled ";
		}
		pagingArea += "'";
		pagingArea += "id='example2_previous'>";
		pagingArea += "<a href='"+this.url+"?currentPage="+(this.startPage-5)+"&keyword="+this.keyword+"' aria-controls='example2' data-dt-idx='0' tabindex='0' ";
		pagingArea += "class='page-link'>Previous</a></li>";
		
		for(int pNo=this.startPage;pNo<=this.endPage;pNo++) {      
		pagingArea += "<li class='paginate_button page-item ";
			if(this.currentPage == pNo) {
				pagingArea += "active";
			}
			pagingArea += "'>";
			pagingArea += "<a href='"+this.url+"?currentPage="+pNo+"&keyword="+this.keyword+"' aria-controls='example2' data-dt-idx='1' tabindex='0' ";
			pagingArea += "class='page-link'>"+pNo+"</a>";
			pagingArea += "</li>";
		}
		pagingArea += "<li class='paginate_button page-item next "; 
		if(this.endPage>=this.totalPages) {
			pagingArea += "disabled";
		}
		pagingArea += "' id='example2_next'><a ";
		pagingArea += "href='"+this.url+"?currentPage="+(this.startPage+5)+"&keyword="+this.keyword+"' aria-controls='example2' data-dt-idx='7' ";
		pagingArea += "tabindex='0' class='page-link'>Next</a></li>";
		pagingArea += "</ul>";
		pagingArea += "</div>";
		pagingArea += "</div>";
	}//end 생성자
	
	public int getTotal() {
		return total;
	}
	
	public void setTotal(int total) {
		this.total = total;
	}
	
	public int getCurrentPage() {
		return currentPage;
	}
	
	public void setCurrentPage(int currentPage) {
		this.currentPage = currentPage;
	}
	
	public int getTotalPages() {
		return totalPages;
	}
	
	public void setTotalPages(int totalPages) {
		this.totalPages = totalPages;
	}
	
	public int getStartPage() {
		return startPage;
	}
	
	public void setStartPage(int startPage) {
		this.startPage = startPage;
	}
	
	public int getEndPage() {
		return endPage;
	}
	
	public void setEndPage(int endPage) {
		this.endPage = endPage;
	}
	
	public String getKeyword() {
		return keyword;
	}
	
	public void setKeyword(String keyword) {
		this.keyword = keyword;
	}
	
	public String getUrl() {
		return url;
	}
	
	public void setUrl(String url) {
		this.url = url;
	}
	
	public List<T> getContent() {
		return content;
	}
	
	public void setContent(List<T> content) {
		this.content = content;
	}
	
	//전체 글의 수가 0인가?
	public boolean hasNoArticles() {
		return this.total == 0;
	}
	
	//데이터가 있나?
	public boolean hasArticles() {
		return this.total > 0;
	}
	
	public void setPagingArea(String pagingArea) {
		this.pagingArea = pagingArea;
	}
	
	//페이징 블록을 자동화
	public String getPagingArea() {
		return this.pagingArea;
	}
}

 

 

LprodService.java

아래에 추가

public int getTotal();

 

 

LprodServiceImpl.java

아래에 추가

//DI, IoC
@Inject
LprodMapper lprodMapper;

@Override
public int getTotal() {
	return this.lprodMapper.getTotal();
}

 

 

LprodMapper.java
interface로 만듬

mapper을 사용했기에 root-context.xml에 설정해야함.

더보기

root-context.xml 설정

</beans>위에 추가

	<!-- Mapper 인터페이스 설정 
	개발자가 직접 DAO를 설정하지 않아도
	자동으로 Mapper 인터페이스를 활용하는 객체를 생성하게 됨
	.별별. => (중첩된)패키지 하위의 모든 것
   -->
   <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
      <property name="basePackage" value="kr.or.ddit.**.mapper" />
   </bean>
package kr.or.ddit.mapper;

import java.util.List;
import java.util.Map;

import kr.or.ddit.vo.LprodVO;
import kr.or.ddit.vo.ProductVO;

public interface LprodMapper {

	//전체 행의 수
	public int getTotal();

	public int createPost(LprodVO lprodVO);

	// 상품 분류 목록
	public List<LprodVO> list(Map<String, Object> map);

	// 상품 분류 상세
	public LprodVO detail(LprodVO lprodVO);

	// 상품 분류 수정
	public int updatePost(LprodVO lprodVO);

	// 상품 분류 삭제
	public int deletePost(LprodVO lpordVO);

	public int getLprodId();

	public int insertProduct(ProductVO productVO);
}

 

 

lprod2_SQL.xml

list 부분 수정 및 where 생성

<?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="lprod">

	<!-- LprodVO(lprodId=14, lprodGu=P501, lprodNm=분식류) -->
	<insert id="createPost" parameterType="lprodVO">
		insert into lprod(lprod_id,lprod_gu,lprod_nm)
		values(#{lprodId},#{lprodGu},#{lprodNm})
	</insert>
	
	<select id="list" resultType="lprodVO" parameterType="hashMap">
		WITH U AS(
		    SELECT ROW_NUMBER() OVER(ORDER BY LPROD_ID DESC) RNUM
		                ,  T.*
		    FROM
		    (
		        SELECT LPROD_ID, LPROD_GU, LPROD_NM
		        FROM   LPROD
		        WHERE  1 = 1
		        <include refid="where"></include>
		    ) T
		)
		SELECT U.*
		FROM     U
		WHERE U.RNUM BETWEEN ((1 * 10)-(10-1)) AND (1 * 10)
	</select>
	
	<sql id="where">
		AND (LPROD_ID 	LIKE '%' || #{keyword} || '%' 
			OR LPROD_GU	LIKE '%' || #{keyword} || '%'
			OR LPROD_NM LIKE '%' || #{keyword} || '%')
	</sql>
	
	
	<!-- LPROD : 1 -->
	<!-- DB영역과 Java의 영역을 서로 매핑하는 역할을 함 -->
	<resultMap type="lprodVO" id="lprodMap">
		<result property="lprodId" column="LPROD_ID"/>
		<result property="lprodGu" column="LPROD_GU"/>
		<result property="lprodNm" column="LPROD_NM"/>
		<!-- 1:N이기에 collection 사용 -->
		<collection property="productVOList" resultMap="productMap"></collection>
	</resultMap>
	
	<!-- PRODUCT : N -->
	<resultMap type="productVO" id="productMap">
		<result property="productId" column="PRODUCT_ID"/>
		<result property="pname" column="PNAME"/>
		<result property="unitPrice" column="UNIT_PRICE"/>
		<result property="description" column="DESCRIPTION"/>
		<result property="manufacturer" column="MANUFACTURER"/>
		<result property="category" column="CATEGORY"/>
		<result property="unitsInStock" column="UNITS_IN_STOCK"/>
		<result property="condition" column="CONDITION"/>
		<result property="filename" column="FILENAME"/>
		<result property="quantity" column="QUANTITY"/>
	</resultMap>
	
	<!-- PRODUCT : N -->
	<!-- join을 했기에 resultMap을 사용 -->
	<select id="detail" parameterType="lprodVO" resultMap="lprodMap">
		SELECT A.LPROD_ID, A.LPROD_GU, A.LPROD_NM,
		    B.PRODUCT_ID, B.PNAME, B.UNIT_PRICE, B.DESCRIPTION, B.MANUFACTURER,
		    B.CATEGORY, B.UNITS_IN_STOCK, B.CONDITION, B.FILENAME, B.QUANTITY
		FROM     LPROD A LEFT OUTER JOIN PRODUCT B ON(A.LPROD_GU = SUBSTR(B.PRODUCT_ID,1,4))
		WHERE  A.LPROD_GU = #{lprodGu}
	</select>
	
	<update id="updatePost" parameterType="lprodVO">
		UPDATE LPROD
		SET LPROD_ID=#{lprodId}, LPROD_NM=#{lprodNm}
		WHERE LPROD_GU=#{lprodGu}		
	</update>
	
	<delete id="deletePost" parameterType="lprodVO">
		DELETE FROM LPROD 
		WHERE LPROD_GU = #{lprodGu}
	</delete>
	
	<select id="getLprodId" resultType="Integer">
		SELECT MAX(LPROD_ID)+1
		FROM LPROD
	</select>
	
	<insert id="insertProduct" parameterType="productVO">
		INSERT INTO PRODUCT(PRODUCT_ID, PNAME, UNIT_PRICE)
		VALUES(#{productId}, #{pname}, #{unitPrice})
	</insert>
</mapper>

 

결과 화면1

 

 

 

  • 페이징 추가

 

 

LprodController.java

map.put("currentPage",currentPage) 추가

package kr.or.ddit.controller;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import kr.or.ddit.service.LprodService;
import kr.or.ddit.utils.ArticlePage;
import kr.or.ddit.vo.LprodVO;
import kr.or.ddit.vo.ProductVO;
import lombok.extern.slf4j.Slf4j;

/*
Controller 어노테이션
스프링 프레임워크에게 "이 클래스는 웹 브라우저의 요청(request)를
받아들이는 컨트롤러야" 라고 알려주는 것임.
스프링은 servlet-context.xml의 context:component-scan의 설정에 의해
이 클래스를 자바빈 객체로 등록(메모리에 바인딩).
*/
//스프링 프레임워크에게 "이 클래스는 컨트롤러야"라고 알려주자
@Slf4j
@RequestMapping("/lprod")
@Controller
public class LprodController {
	
	//DI(의존성 주입) / IoC(제어의 역전)
	@Autowired
	LprodService lprodService;
	
	/*
	요청URI : /lprod/list or /lprod/list?currentPage=2 or /lprod/list?currentPage=
	요청파라미터 : {keyword=개똥이}
	요청방식 : get
	*/
	@RequestMapping(value="/list", method=RequestMethod.GET)
	public ModelAndView list(ModelAndView mav, 
					@RequestParam(value="currentPage", required=false, defaultValue="1") int currentPage, 
					@RequestParam(value="keyword",required=false,defaultValue="") String keyword) {
		log.info("list에 왔다");
		log.info("keyword : " + keyword);
		
		Map<String,Object> map = new HashMap<String,Object>();
		map.put("keyword",keyword);
		map.put("currentPage",currentPage);
		
		//전체 행의 수
		int total = this.lprodService.getTotal();
		log.info("list->total : " + total);
		
		//Model(데이터)
		//상품분류 목록
		List<LprodVO> lprodVOList = this.lprodService.list(map);
		log.info("list->lprodVOList : " + lprodVOList);
		
		
		mav.addObject("articlePage", new ArticlePage<LprodVO>(total, currentPage, 10, lprodVOList, keyword));
		
		//View(jsp)
		mav.setViewName("lprod/list");
		
		return mav;
	}
}

 

 

list.jsp

하단에 추가

	
	<hr />

	<div class="col-sm-12 col-md-7">
		<div class="dataTables_paginate paging_simple_numbers"
			id="example2_paginate">
			<ul class="pagination">
				<li class="paginate_button page-item previous disabled"
					id="example2_previous"><a href="#" aria-controls="example2"
					data-dt-idx="0" tabindex="0" class="page-link">Previous</a></li>
				<li class="paginate_button page-item active"><a href="/lprod/list?currentPage=1"
					aria-controls="example2" data-dt-idx="1" tabindex="0"
					class="page-link">1</a></li>
				<li class="paginate_button page-item "><a href="/lprod/list?currentPage=2"
					aria-controls="example2" data-dt-idx="2" tabindex="0"
					class="page-link">2</a></li>
				<li class="paginate_button page-item "><a href="/lprod/list?currentPage=3"
					aria-controls="example2" data-dt-idx="3" tabindex="0"
					class="page-link">3</a></li>
				<li class="paginate_button page-item "><a href="/lprod/list?currentPage=4"
					aria-controls="example2" data-dt-idx="4" tabindex="0"
					class="page-link">4</a></li>
				<li class="paginate_button page-item "><a href="/lprod/list?currentPage=5"
					aria-controls="example2" data-dt-idx="5" tabindex="0"
					class="page-link">5</a></li>
					
				<li class="paginate_button page-item next" id="example2_next"><a
					href="#" aria-controls="example2" data-dt-idx="7" tabindex="0"
					class="page-link">Next</a></li>
			</ul>
		</div>
	</div>

 

결과 화면2 : 페이지 이동은 되지만 1이 고정으로 표시됨

 

 

 

  • 페이지에 따라 표시되게 변경

list.jsp

변경

	<hr />

	<div class="col-sm-12 col-md-7">
		<div class="dataTables_paginate paging_simple_numbers"
			id="example2_paginate">
			<ul class="pagination">
				<li class="paginate_button page-item previous disabled"
					id="example2_previous"><a href="#" aria-controls="example2"
					data-dt-idx="0" tabindex="0" class="page-link">Previous</a></li>
				<c:forEach var="pNo" begin="${articlePage.startPage}" end="${articlePage.endPage}">
					<li class="paginate_button page-item 
								<c:if test="${pNo==param.currentPage}">active</c:if>
							"><a href="/lprod/list?currentPage=${pNo}"
						aria-controls="example2" data-dt-idx="1" tabindex="0"
						class="page-link">${pNo}</a></li>
				</c:forEach>
					
				<li class="paginate_button page-item next" id="example2_next"><a
					href="#" aria-controls="example2" data-dt-idx="7" tabindex="0"
					class="page-link">Next</a></li>
			</ul>
		</div>
	</div>

 

결과 화면3

 

 

 

  • Next 기능 적용

list.jsp

Next 부분 수정 + 제일 마지막에서 Next를 막음

<li class="paginate_button page-item next
    <c:if test="${articlePage.endPage==articlePage.totalPages}">disabled</c:if>
" id="example2_next"><a
    href="/lprod/list?currentPage=${articlePage.startPage+5}" aria-controls="example2" data-dt-idx="7" tabindex="0"
    class="page-link">Next</a></li>

 

결과 화면4

 

 

 

  • 이전 페이지 이동 기능 추가

list.jsp

가장 첫 페이지일 경우 제외

<li class="paginate_button page-item previous 
        <c:if test="${articlePage.startPage < 6}"> disabled </c:if>
    "
    id="example2_previous"><a href="/lprod/list?currentPage=${articlePage.startPage-5}" aria-controls="example2"
    data-dt-idx="0" tabindex="0" class="page-link">Previous</a></li>

 

결과 화면5

 

 

 

  • 대체

list.jsp

	<hr />
	<!-- EL태그 정리 
	== : eq(equal)
	!= : ne(not equal)
	<  : lt(less than)
	>  : gt(greater than)
	<= : le(less equal)
	>= : ge(greater equal)
	-->
	<div class="row">
		<div class="col-sm-12 col-md-7">
			<div class="dataTables_paginate paging_simple_numbers"
				id="example2_paginate">
				<ul class="pagination">
					<li class="paginate_button page-item previous 
							<c:if test="${articlePage.startPage < 6}"> disabled </c:if>
						"
						id="example2_previous"><a href="/lprod/list?currentPage=${articlePage.startPage-5}" aria-controls="example2"
						data-dt-idx="0" tabindex="0" class="page-link">Previous</a></li>
					<c:forEach var="pNo" begin="${articlePage.startPage}" end="${articlePage.endPage}">
						<li class="paginate_button page-item 
									<c:if test="${pNo==param.currentPage}">active</c:if>
								"><a href="/lprod/list?currentPage=${pNo}"
							aria-controls="example2" data-dt-idx="1" tabindex="0"
							class="page-link">${pNo}</a></li>
					</c:forEach>
						
					<li class="paginate_button page-item next
						<c:if test="${articlePage.endPage==articlePage.totalPages}">disabled</c:if>
					" id="example2_next"><a
						href="/lprod/list?currentPage=${articlePage.startPage+5}" aria-controls="example2" data-dt-idx="7" tabindex="0"
						class="page-link">Next</a></li>
				</ul>
			</div>
		</div>
	</div>

 

위의 작성한 최종본을 아래와 대체 가능하다

 

 

	<hr />
	<!-- EL태그 정리 
	== : eq(equal)
	!= : ne(not equal)
	<  : lt(less than)
	>  : gt(greater than)
	<= : le(less equal)
	>= : ge(greater equal)
	-->
	<div class="row">
		${articlePage.pagingArea}
	</div>

 

 

 

  • 검색 기능 추가

LprodController.java

package kr.or.ddit.controller;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import kr.or.ddit.service.LprodService;
import kr.or.ddit.utils.ArticlePage;
import kr.or.ddit.vo.LprodVO;
import kr.or.ddit.vo.ProductVO;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RequestMapping("/lprod")
@Controller
public class LprodController {
	
	//DI(의존성 주입) / IoC(제어의 역전)
	@Autowired
	LprodService lprodService;
	
	/*
	요청URI : /lprod/list or /lprod/list?currentPage=2 or /lprod/list?currentPage=
	요청파라미터 : {keyword=개똥이}
	요청방식 : get
	*/
	@RequestMapping(value="/list", method=RequestMethod.GET)
	public ModelAndView list(ModelAndView mav, 
					@RequestParam(value="currentPage", required=false, defaultValue="1") int currentPage, 
					@RequestParam(value="keyword",required=false,defaultValue="") String keyword) {
		log.info("list에 왔다");
		log.info("keyword : " + keyword);
		
		Map<String,Object> map = new HashMap<String,Object>();
		map.put("keyword",keyword);
		map.put("currentPage",currentPage);
		
		//전체 행의 수
		int total = this.lprodService.getTotal(map);
		log.info("list->total : " + total);
		
		//Model(데이터)
		//상품분류 목록
		List<LprodVO> lprodVOList = this.lprodService.list(map);
		log.info("list->lprodVOList : " + lprodVOList);
		
		
		mav.addObject("articlePage", new ArticlePage<LprodVO>(total, currentPage, 10, lprodVOList, keyword));
		
		//View(jsp)
		mav.setViewName("lprod/list");
		
		return mav;
	}
}

 

 

LprodServiceImpl.java

package kr.or.ddit.service.impl;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import javax.inject.Inject;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import kr.or.ddit.dao.AttachDao;
import kr.or.ddit.dao.LprodDao;
import kr.or.ddit.mapper.LprodMapper;
import kr.or.ddit.service.LprodService;
import kr.or.ddit.vo.AttachVO;
import kr.or.ddit.vo.LprodVO;
import kr.or.ddit.vo.ProductVO;
import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
public class LprodServiceImpl implements LprodService {

	//데이터베이스 접근을 위해 LprodDao 인스턴스를 주입받자
	//DI(Dependency Injection):의존성 주입
	//IoC(Inversion of Control):제어의 역전
	@Autowired
	LprodDao lprodDao;
	
	//어디에 업로드? root-context.xml에 적어둠
	@Autowired
	String uploadFolder;
	
	@Autowired
	AttachDao attachDao;
	
	//DI, IoC
	@Inject
	LprodMapper lprodMapper;
	
	@Override
	public int getTotal(Map<String, Object> map) {
		return this.lprodMapper.getTotal(map);
	}
}

 

 

LprodMapper.java

package kr.or.ddit.mapper;

import java.util.List;
import java.util.Map;

import kr.or.ddit.vo.LprodVO;
import kr.or.ddit.vo.ProductVO;

public interface LprodMapper {

	//전체 행의 수
	public int getTotal(Map<String, Object> map);
}

 

 

lprod2_SQL.xml

<?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="kr.or.ddit.mapper.LprodMapper">

	<sql id="where">
		AND (LPROD_ID 	LIKE '%' || #{keyword} || '%' 
			OR LPROD_GU	LIKE '%' || #{keyword} || '%'
			OR LPROD_NM LIKE '%' || #{keyword} || '%')
	</sql>
	
	<select id="list" resultType="lprodVO" parameterType="hashMap">
		WITH U AS(
		    SELECT ROW_NUMBER() OVER(ORDER BY LPROD_ID DESC) RNUM
		                ,  T.*
		    FROM
		    (
		        SELECT LPROD_ID, LPROD_GU, LPROD_NM
		        FROM   LPROD
		        WHERE  1 = 1
		        <include refid="where"></include>
		    ) T
		)
		SELECT U.*
		FROM     U
		WHERE U.RNUM BETWEEN ((1 * 10)-(10-1)) AND (1 * 10)
	</select>
	
	<!-- 전체 행의 수
	public int getTotal() -->
	<select id="getTotal" parameterType="hashMap" resultType="int">
		SELECT COUNT(*)
		FROM LPROD
		WHERE 1 = 1
		<include refid="where"></include>
	</select>
</mapper>

 

결과 화면6 : U7로 검색

 

 

 

  • 페이징 처리 및 비동기로 검색 처리

ArticlePage.java

package kr.or.ddit.utils;

import java.util.List;

public class ArticlePage<T> {
	//전체글 수
	private int total;
	
	// 현재 페이지 번호
	private int currentPage;
	
	// 전체 페이지수 
	private int totalPages;
	
	// 블록의 시작 페이지 번호 ex) [1][2][3] 에서의 1
	private int startPage;
	
	//블록의 종료 페이지 번호 ex) [1][2][3] 에서의 3
	private int endPage;
	
	//검색어
	private String keyword = "";
	
	//요청URL
	private String url = "";
	
	//select 결과 데이터
	private List<T> content;
	
	//페이징 처리
	private String pagingArea = "";
	
	
	//생성자(Constructor) : 페이징 정보를 생성
	//               753            1            10         select결과10행
	public ArticlePage(int total, int currentPage, int size, List<T> content, String keyword) {
		//size : 한 화면에 보여질 목록의 행 수
		this.total = total; //753
		this.currentPage = currentPage; //1
		this.content = content;
		this.keyword = keyword;
		
		//전체글 수가 0이면?
		if(total==0) {
			totalPages = 0;//전체 페이지 수
			startPage = 0;//블록 시작번호
			endPage = 0; //블록 종료번호
		}else {//글이 있다면
			//전체 페이지 수 = 전체글 수 / 한 화면에 보여질 목록의 행 수
			//3 = 31 / 10
			totalPages = total / size;//75
			
			//나머지가 있다면, 페이지를 1 증가
			if(total % size > 0) {//나머지3
			   totalPages++;//76
			}
			
			//페이지 블록 시작번호를 구하는 공식
			// 블록시작번호 = 현재페이지 / 페이지크기 * 페이지크기 + 1
			startPage = currentPage / 5 * 5 + 1;//1
			
			//현재페이지 % 페이지크기 => 0일 때 보정
			if(currentPage % 5 == 0) {
			   startPage -= 5;
			}
			
			//블록종료번호 = 시작페이지번호 + (페이지크기 - 1)
			//[1][2][3][4][5][다음]
			endPage = startPage + (5 - 1);//5
			
			//종료페이지번호 > 전체페이지수
			if(endPage > totalPages) {
			   endPage = totalPages;
			}
		}
		
		pagingArea += "<div class='col-sm-12 col-md-7'>";
		pagingArea += "<div class='dataTables_paginate paging_simple_numbers' id='example2_paginate'>";
		pagingArea += "<ul class='pagination'>";
		pagingArea += "<li class='paginate_button page-item previous "; 
		if(this.startPage<6) {
			pagingArea += "disabled ";
		}
		pagingArea += "'";
		pagingArea += "id='example2_previous'>";
		pagingArea += "<a href='"+this.url+"?currentPage="+(this.startPage-5)+"&keyword="+this.keyword+"' aria-controls='example2' data-dt-idx='0' tabindex='0' ";
		pagingArea += "class='page-link'>Previous</a></li>";
		
		for(int pNo=this.startPage;pNo<=this.endPage;pNo++) {      
		pagingArea += "<li class='paginate_button page-item ";
			if(this.currentPage == pNo) {
				pagingArea += "active";
			}
			pagingArea += "'>";
			pagingArea += "<a href='"+this.url+"?currentPage="+pNo+"&keyword="+this.keyword+"' aria-controls='example2' data-dt-idx='1' tabindex='0' ";
			pagingArea += "class='page-link'>"+pNo+"</a>";
			pagingArea += "</li>";
		}
		pagingArea += "<li class='paginate_button page-item next "; 
		if(this.endPage>=this.totalPages) {
			pagingArea += "disabled";
		}
		pagingArea += "' id='example2_next'><a ";
		pagingArea += "href='"+this.url+"?currentPage="+(this.startPage+5)+"&keyword="+this.keyword+"' aria-controls='example2' data-dt-idx='7' ";
		pagingArea += "tabindex='0' class='page-link'>Next</a></li>";
		pagingArea += "</ul>";
		pagingArea += "</div>";
		pagingArea += "</div>";
	}//end 생성자
	
	public int getTotal() {
		return total;
	}
	
	public void setTotal(int total) {
		this.total = total;
	}
	
	public int getCurrentPage() {
		return currentPage;
	}
	
	public void setCurrentPage(int currentPage) {
		this.currentPage = currentPage;
	}
	
	public int getTotalPages() {
		return totalPages;
	}
	
	public void setTotalPages(int totalPages) {
		this.totalPages = totalPages;
	}
	
	public int getStartPage() {
		return startPage;
	}
	
	public void setStartPage(int startPage) {
		this.startPage = startPage;
	}
	
	public int getEndPage() {
		return endPage;
	}
	
	public void setEndPage(int endPage) {
		this.endPage = endPage;
	}
	
	public String getKeyword() {
		return keyword;
	}
	
	public void setKeyword(String keyword) {
		this.keyword = keyword;
	}
	
	public String getUrl() {
		return url;
	}
	
	public void setUrl(String url) {
		this.url = url;
	}
	
	public List<T> getContent() {
		return content;
	}
	
	public void setContent(List<T> content) {
		this.content = content;
	}
	
	//전체 글의 수가 0인가?
	public boolean hasNoArticles() {
		return this.total == 0;
	}
	
	//데이터가 있나?
	public boolean hasArticles() {
		return this.total > 0;
	}
	
	public void setPagingArea(String pagingArea) {
		this.pagingArea = pagingArea;
	}
	
	//페이징 블록을 자동화
	public String getPagingArea() {
		return this.pagingArea;
	}
}

 

 

BookController.java

currentPage추가

package kr.or.ddit.controller;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
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.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;


import kr.or.ddit.service.BookService;
import kr.or.ddit.utils.ArticlePage;
import kr.or.ddit.vo.BookVO;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Controller
public class BookController {
	
	@Autowired
	BookService bookService;
	
	/*
	요청URI : /list?keyword=알탄 or /list or /list?keyword=
	요청파라미터 : keyword=알탄 or 없음
	요청방식 : get
	
	required=false : 선택사항. 파라미터가 없어도 무관
	*/
	@RequestMapping(value="/list", method=RequestMethod.GET)
	public ModelAndView list(ModelAndView mav, 
			@RequestParam(value="currentPage",required=false,defaultValue="1") int currentPage,
			@RequestParam(value="keyword",required=false,defaultValue="") String keyword) {
		log.info("list에 왔다");
		log.info("list->keyword : " + keyword);
		
		//map{"keyword":"알탄"}
		Map<String,Object> map = new HashMap<String,Object>();
		map.put("keyword", keyword);
		map.put("currentPage", currentPage);
		
		
		//도서 전체 행수
		int total = this.bookService.getTotal(map);
		log.info("list->total : " + total);
		
		
		//도서 목록 map{"keyword":"알탄"}
		List<BookVO> bookVOList = this.bookService.list(map);
		log.info("list->bookVOList : " + bookVOList);
		
		//Model : 데이터
		mav.addObject("articlePage", new ArticlePage<BookVO>(total, currentPage, 10, bookVOList, keyword));
		
		//View : jsp
		//forwarding : 데이터가 넘어감
		mav.setViewName("book/list");
		
		return mav;
	}
	
	/*
	요청URI : /listAjax
	요청파라미터(JSON->String) : {keyword:알탄} or {keyword:""}
	요청방식 : post
	 */
	@ResponseBody
	@RequestMapping(value="/listAjax", method=RequestMethod.POST)
	public ArticlePage<BookVO> listAjax(@RequestBody Map<String,Object> map) {
		log.info("list에 왔다");
		log.info("list->map : " + map);
		
		//map{"keyword":"알탄","currentPage":1}
		//map.put("keyword", keyword);
		
		int total = this.bookService.getTotal(map);
		log.info("listAjax -> total : " + total);
		
		//도서 목록 map{"keyword":"알탄"}
		List<BookVO> bookVOList = this.bookService.list(map);
		log.info("list->bookVOList : " + bookVOList);
		
		return new ArticlePage(total, Integer.parseInt(map.get("currentPage").toString()), 10, bookVOList, map.get("keyword").toString());
	}
}

 

 

BookService.java

전체 게시글 조회 추가

package kr.or.ddit.service;

import java.util.List;
import java.util.Map;

import kr.or.ddit.vo.BookVO;

public interface BookService {
	
	public int getTotal(Map<String, Object> map);
}

 

 

BookServiceImpl.java

전체 게시글 조회 추가

package kr.or.ddit.service.impl;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import kr.or.ddit.dao.AttachDao;
import kr.or.ddit.dao.BookDao;
import kr.or.ddit.service.BookService;
import kr.or.ddit.utils.UploadController;
import kr.or.ddit.vo.AttachVO;
import kr.or.ddit.vo.BookVO;
import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
public class BookServiceImpl implements BookService{

	@Autowired
	UploadController uploadController;
	
	@Autowired
	BookDao bookDao;
	
	@Autowired
	String uploadFolder;
	
	@Autowired
	AttachDao attachDao;
	
	// 도서 목록
	@Override
	public List<BookVO> list(Map<String, Object> map) {
		return this.bookDao.list(map);
	}
    
	@Override
	public int getTotal(Map<String, Object> map) {
		return this.bookDao.getTotal(map);
	}

}

 

 

BookDao.java

전체 게시글 조회 추가

package kr.or.ddit.dao;

import java.util.List;
import java.util.Map;

import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import kr.or.ddit.vo.BookVO;

@Repository
public class BookDao {

	@Autowired
	SqlSessionTemplate sqlSessionTemplate;
	
	//도서 목록
	public List<BookVO> list(Map<String, Object> map) {
		//.selectList("namespace.id",파라미터)
		return this.sqlSessionTemplate.selectList("book.list",map);
	}
    
	public int getTotal(Map<String, Object> map) {
		return this.sqlSessionTemplate.selectOne("book.getTotal", map);
	}

}

 

 

book_SQL.xml

<?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="book">
	<sql id="where">
		<if test="keyword!=null and keyword!=''">
			AND (TITLE 		LIKE '%' || #{keyword} || '%' 
				OR CATEGORY LIKE '%' || #{keyword} || '%'
				OR PRICE 	LIKE '%' || #{keyword} || '%')
		</if>
	</sql>
	
	<!-- 도서 목록
	where 1 = 1은 늘 참임. 
	조건이 2개 이상일 때 WHERE + AND
	조건이 1개일 때 WHERE이어야 함. 
		WHERE(생략) 
		AND   => 오류 발생
		==>
		WHERE 1 = 1
		AND(생략)
		AND => 정상
		
		True and True  = True
		True and False = False
		
		keyword : "알탄" (/list?keyword=알탄)
		keyword : ""  (/list?keyword=)
	-->
	<!-- bookVO의 값으로 매핑되게 작업해놓음 -->
	<select id="list" resultType="bookVO" parameterType="hashMap">
		SELECT T.*
		FROM
		(
		    SELECT ROW_NUMBER() OVER(ORDER BY BOOK_ID DESC) RNUM
		        , BOOK_ID, TITLE, CATEGORY, PRICE, INSERT_DATE
		    FROM BOOK
		    WHERE 1=1
		    <include refid="where"></include>
		) T
		WHERE T.RNUM BETWEEN ((#{currentPage} * 10) - (10 - 1)) AND (#{currentPage} * 10)
	</select>
	
	<!-- 도서 전체 행수 -->
	<select id="getTotal" parameterType="hashMap" resultType="int">
		SELECT COUNT(*)
		FROM BOOK
		WHERE 1 = 1
		<include refid="where"></include>
	</select>
	
</mapper>

 

 

list.jsp

ajax 변경 및 페이징 추가

<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="/resources/js/jquery.min.js"></script>
<title>도서 목록</title>
<script type="text/javascript">
$(function(){
	$("#btnSearch").on("click",function(){
		let keyword = $("input[name='keyword']").val();
		
		//json오브젝트
		let data = {
			"keyword":keyword
		};
		
		console.log("data : ", data);
		
 		//$(this).parent().submit();
 		//새롭게 검색 : 1페이지로 초기화
		getList(keyword,1);
	});
	
	// 전체 보기
	$("#btnSearchAll").on("click",function(){
		getList("",1);
	});
	
	
	// 목록 함수 호출
// 	getList("");
});

// 목록
function getList(keyword, currentPage) {
	
	// JSON 오브젝트
	let data = {
		"keyword":keyword,
		"currentPage":currentPage
	};
	// data : {"keyword":"알탄","currentPage":"1"}
	console.log("data : ", data);
	
	//아작나써유..(피)씨다타써...
	$.ajax({
		url: "/listAjax",
		contentType:"application/json;charset=utf-8",
		data:JSON.stringify(data),
		type:"post",
		dataType:"json",
		success:function(result){
			// result : articlePage
			// result.content : List<BookVO>
			console.log("result.content : ", result.content);
			
			let str = "";
			
			$.each(result.content, function(idx, bookVO){
				str += "<tr>";
				str += "<td>" + (idx+1) + "</td>";
				str += "<td><a href='/detail?bookId=" + bookVO.bookId + "'>" + bookVO.title + "</a></td>";
				str += "<td>" + bookVO.category + "</td>";
				str += "<td>" + bookVO.price + "원</td>";
				str += "</tr>";
			});
			
			$(".clsPagingArea").html(result.pagingArea);
			
			$("#trShow").html(str);
		}
	});
}

</script>
</head>
<body>
<h3>도서 목록</h3>
<p>
	<form>
		<input type="text" name="keyword" value="" placeholder="검색어를 입력하세요"/>
		<!-- submit / button / reset -->
		<button type="button" id="btnSearch">검색</button>
		<button type="button" id="btnSearchAll">전체보기</button>
	</form>
	<a href="/create">도서 등록</a>
</p>

<table border="1">
	<thead>
		<tr>
			<th>번호</th><th>제목</th><th>카테고리</th><th>가격</th>
		</tr>
	</thead>
	<tbody id="trShow">
		<!-- data : mav.addObject("articlePage", 페이지네이션객체); -->
		<!-- articlePage.content => List<BookVO> -->
		<!-- row : bookVO 1행 -->
		<c:forEach var="bookVO" items="${articlePage.content}" varStatus="stat">
			<tr>
				<td>${bookVO.rnum}</td>
				<td><a href="/detail?bookId=${bookVO.bookId}">${bookVO.title}</a></td>
				<td>${bookVO.category}</td>
				<td><fmt:formatNumber value="${bookVO.price}" pattern="#,###" />원</td>
			</tr>
		</c:forEach>
	</tbody>
</table>

<hr />
<div class="row clsPagingArea">
	${articlePage.pagingArea}
</div>

</body>
</html>

 

결과 화면7

 

 

반응형
1 ··· 5 6 7 8 9 10 11 ··· 93