[SPRING/Mybatis] POI를 이용한 대용량 데이터 추출 (엑셀 다운) -1

오랜만에 티스토리에 개발관련 기록을 남기는 것같다. 디비의 값을 엑셀로 다운로드을 수 있도록 해달라는 요청이 있었다. (요즘은 Android를 파는 중이지만...) 요청은 하나의 대상 테이블에 대한

kyome.tistory.com

 

이전 게시글에 이어서,

이제 ResultHandler 를 작성한다.

 

0. ResultHandler (ExcelHandler.java)

 

ResultHandler는 handleResult라는 메서드를

Override 하기만 하면 쉽게 사용할 수 있다.

handleResult는 데이터가 들어오는대로

건마다 호출되는 걸로 보인다.

 

 

1. 변수 및 생성자 선언

 

ResultHandler는 Generic으로 선언할 수 있고 

내가 만들 ExcelHandler는 Map으로 받아져야하기 때문에 

Generic 변수 T는 Map을 상속받는 변수야한다는 조건을 걸었다.

 

ResultHandler를 통해 다운로드를 받을 수 있도록 할 계획이기 때문에

생성자로 Response를 받으며 파일명이나

컬럼 순서를 받을 수 있도록 생성자를 추가했다.

 

import ...

public class ExcelHandler<T extends Map<String,Object>> implements ResultHandler<T> {
	public static final Logger LOGGER = LoggerFactory.getLogger(ExcelHandler.class);
	
	private T result;
	private String title;
	private String filename;
	private SXSSFWorkbook  workbook;
	private SXSSFSheet  sheet;
	private HttpServletResponse response;
	private ResultContext<? extends T> context;
	private List<String> columnTitleList;
	private int rownum;
	
	final int TITLE = 0;
	final int BODY = 1;

	
	private ExcelHandler() {
		super();
		rownum = 0;
	}
	
	public ExcelHandler(HttpServletResponse response, String filename) {
		this();
		this.response = response;
		this.title = filename;
		try {
			this.filename = URLEncoder.encode(filename.replace(" ", "_"),"UTF-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		};
		workbook = new SXSSFWorkbook(10000);
		sheet = workbook.createSheet(title);
	}
	
	public ExcelHandler(HttpServletResponse response, 
    	String filename,List<String> orderedColumnTitleList) {
		this(response,filename);
		this.columnTitleList = orderedColumnTitleList;
	}

	...
    

 

 

 

 

 

 

 

 

2.  엑셀에 쓰기 메서드 구현

 

DB 에서 조회한 결과의 Column이 몇 개이고 이름이 무엇이든 상관없이

독립적으로 실행이 되어야 재사용이 가능해지기 때문에 

 

엑셀에 입력하는 기능에만 집중했고 

나머지는 파라미터로 받도록 했다.

 

private void write(int type,int currentRow,CellStyle style) {
		
	if (columnTitleList == null) {
		columnTitleList = new ArrayList<String>(context.getResultObject().keySet());
	}
	SXSSFRow row = sheet.createRow(currentRow);
	
	if(columnTitleList.size() == 0 ) {
		return;
	}
	
	for(int i = 0 ; i < columnTitleList.size() ; i++) {
		SXSSFCell cell = row.createCell(i);
		String tempValue = "";
		
		switch (type) {
			case TITLE:
				tempValue = columnTitleList.get(i);
				break;
			case BODY:
				if(context.getResultObject().containsKey(columnTitleList.get(i))) {
					tempValue = context.getResultObject().get(columnTitleList.get(i)).toString();
				}
				
				break;
		}
//		스타일 객체가 없다면 기본으로
		if(style != null) {
			cell.setCellStyle(style);
		}
		cell.setCellValue(tempValue);	
	}
}
	

 

 

 

3. handleResult Override

 

데이터가(한개의 행이) 호출 될 때마다 workbook에 입력해주고

현재의 행을 나타내는 변수를 ++ 해준다.

 

헤더전용 CellStyle 과 내용전용 CellStyle을 

미리 선언해두어서 write에 마지막 파라미터로 넣어주면

필요에 따라 셀스타일을 변경하면서 입력할 수도 있다.

 

	...

@Override
public void handleResult(ResultContext<? extends T> resultContext) {
	if(resultContext.getResultObject() == null) {
		return;
	}
	this.context = resultContext;
	result =  context.getResultObject();
	
	
	Font headerFont = workbook.createFont();
	headerFont.setFontName("맑은 고딕");
	headerFont.setBold(true);
       
	CellStyle headerStyle = workbook.createCellStyle();
	headerStyle.setBorderTop(BorderStyle.THIN);
	headerStyle.setBorderBottom(BorderStyle.THIN);
	headerStyle.setBorderLeft(BorderStyle.THIN);
	headerStyle.setBorderRight(BorderStyle.THIN);
        
	headerStyle.setAlignment(HorizontalAlignment.CENTER);
	headerStyle.setVerticalAlignment(VerticalAlignment.CENTER);
	headerStyle.setFillForegroundColor(IndexedColors.LIGHT_CORNFLOWER_BLUE.getIndex());
	headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
	headerStyle.setFont(headerFont);

	Font bodyFont = workbook.createFont();
	bodyFont.setFontName("맑은 고딕");
        
	CellStyle bodyStyle= workbook.createCellStyle();
	bodyStyle.setBorderTop(BorderStyle.THIN);
	bodyStyle.setBorderBottom(BorderStyle.THIN);
	bodyStyle.setBorderLeft(BorderStyle.THIN);
	bodyStyle.setBorderRight(BorderStyle.THIN);
        
	bodyStyle.setAlignment(HorizontalAlignment.LEFT);
	bodyStyle.setFont(bodyFont);
		
	if(rownum == 0 ) {			
		write(TITLE, rownum,headerStyle);
		write(BODY, rownum+1,bodyStyle);
	}else {
		write(BODY, rownum+1,bodyStyle);
	}
	rownum++;
}

...

 

 

 

 

 

 

4. 엑셀 다운로드

 

생성자로 Response 객체를 받는 이유는 여기에 있다.

Response 객체의 헤더에 첨부파일을 넣어서 

OutStream으로 파일을 내보낸다.

 

파일 처리는 항상 그렇듯 예외처리를 성실하게 해줘야하고

종료시에 close 를 호출해줘야한다.

 

	...
    
public void download() throws IOException{
	LOGGER.debug("## start excel download : "+filename);
	response.setHeader("Content-Disposition", "attachment; filename=" 
        				+ filename.replaceAll(" ", "_") + ".xlsx;");
	response.setCharacterEncoding(Constants.ENCODING);
		
	ServletOutputStream stream = response.getOutputStream();
	OutputStream out = new BufferedOutputStream(stream);
	
	try {
		response.resetBuffer();
		response.setBufferSize(1024 * 4);
		workbook.write(out);
		
	} catch (Exception e) {
		out.flush();
		out.close();
		stream.close();
	} finally {
		out.flush();
		out.close();
		stream.close();
	}
	
	if (workbook != null) {
		try {
			workbook.dispose();
		} catch (Exception e) {
			workbook.close();
		} finally {
			workbook.close();
		}
	}
	workbook.close();
}

public void close() {
	workbook.dispose();
	try {
		workbook.close();
	} catch (IOException e) {
		e.printStackTrace();
	}
}

...

 

 

 

 

 

도움이 되었다면

로그인이 필요 없는 공감 버튼 꾹 눌러주세요! 

 

 

 

 

오랜만에 티스토리에 개발관련 기록을 남기는 것같다.

디비의 값을 엑셀로 다운로드을 수 있도록 해달라는 요청이 있었다.

(요즘은 Android를 파는 중이지만...)

 

요청은 하나의 대상 테이블에 대한 엑셀 다운로드 구현이지만

어차피 개발할 거라면 재사용할 수 있도록 개발했다.

 

 

 

 

 

 

 

 

 

 

0. 의존성 추가 (pom.xml)

 


...
		<!-- excel -->
		<dependency>
		    <groupId>org.apache.poi</groupId>
		    <artifactId>poi</artifactId>
		    <version>4.1.1</version>
		</dependency>
		
		<dependency>
		    <groupId>org.apache.poi</groupId>
		    <artifactId>poi-ooxml</artifactId>
		    <version>4.1.1</version>
		</dependency>
...

 

 

 

 

1. SQL (ExcelMapper.xml)

 

엑셀 다운로드용 쿼리를 모아둘 목적으로 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.co.excel.dao.ExcelMapper">
	<resultMap id="excelListDownloadMap" type="java.util.HashMap" >
    	<result column="CONTENT_NO"     	property="번호"    	jdbcType="INTEGER" />
    	<result column="CONTENT_TITLE"     	property="제목"    	jdbcType="VARCHAR" />
    </resultMap>

    <select id="contentListDownload" resultMap="excelListDownloadMap">
		<![CDATA[
			SELECT CONTENT_NO ,CONTENT_TITLE
			FROM TB_CONTENT
			WHERE CONTENT_NO < 100
		]]>
	</select>
</mapper>

 

 

 

2.  DAO (ExcelMapper.java)

 

현재 개발환경에서는 Mapper 를 사용하기 때문에

interface로 간단하게 DAO를 선언할 수 있다.

반환값을 void, 파라미터를 ResultHandler로 선언하면

Mybatis에서 조회되는 값을 바로 핸들링할 수 있다.

 

(ResultHandler 클래스를 잘 짜놓는다면...ㅎㅎㅎ)

ResultHandler는 너무 길어지니 이어지는 게시글에 자세히 적는걸로!

 

import java.util.Map;

import org.apache.ibatis.session.ResultHandler;
import org.springframework.stereotype.Repository;

@Repository
public interface ExcelMapper {
	public void contentListDownload(ResultHandler<Map<String,Object>> ExcelHander);
}

 

 

 

 

 

 

3. Service (ExcelService.java)

 

난 서비스를 ResultHandler과 DAO의 매핑 공간으로 사용했다.

매핑을 해놓으면 해당 메서드를 호출할때

ExcelHandler에 조회 결과가 매핑되는 걸로 보인다.

난 이 ResultHandler를 통해 다운로드 받을 엑셀 형식을 정의하고

다운로드 로직을 ResultHandler 에 넣어놓았다. (excelHandler.download())

 

import java.io.IOException;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import kr.co.excel.dao.ExcelMapper;

@Service
public class ExcelService {
	@Autowired
	private ExcelMapper excelMapper;
	
	ExcelHandler<Map<String,Object>> excelHandler;
	
	public void contentListDownload(
			HttpServletRequest request,
			HttpServletResponse response){
		excelHandler = new ExcelHandler<>(response,"테스트");
		excelMapper.contentListDownload(excelHandler);
		
		try {
			excelHandler.download();
		} catch (IOException e) {
			e.printStackTrace();
			excelHandler.close();
		}
	}
}

 

 

 

 

 

4. Controller (ExcelController.java)

 

View페이지가 별도로 필요없는 RestController를 선언했다. 

엑셀 다운로드시 Response 객체가

필요하기 때문에 파라미터로 선언해놓았다.

Request 객체는 추후에 필요할 듯 싶어서

넣어 놓았을 뿐 당장은 필요없다.

 

import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import kr.co.excel.service.ExcelService;

@RestController
public class ExcelController {
	
	@Autowired
	ExcelService excelService;
	
	@RequestMapping(value = "/contentListDown.do")
	public void contentListDownload(
			HttpServletRequest request,
			HttpServletResponse response){
		excelService.contentListDownload(request,response);
	}
}

 

 

 

 

도움이 되었다면

로그인이 필요 없는 공감 버튼 꾹 눌러주세요! 

 

 

 

 

 


 

[SPRING/Mybatis] POI를 이용한 대용량 데이터 추출 (엑셀 다운) -2

 

[SPRING/Mybatis] POI를 이용한 대용량 데이터 추출 (엑셀 다운) -2

[SPRING/Mybatis] POI를 이용한 대용량 데이터 추출 (엑셀 다운) -1 오랜만에 티스토리에 개발관련 기록을 남기는 것같다. 디비의 값을 엑셀로 다운로드을 수 있도록 해달라는 요청이 있었다. (요즘은 A

kyome.tistory.com


 

 

 

 

설정방법

1. log4j.xml파일을 생성(수정)

Spring 으로 세팅하면 기본적으로 log4j를 포함하고 있기 때문에 별도 생성할 필요는 없지만 혹시 없다면 log4j.xml 파일을 생성해야 한다.

Path : src/main/resources/log4j.xml

 

1.log4j.xml 구성

Appender, logger, root 로 구성되어있는 걸로 보인다.

 

Appender는 로그를 찍을 대상이나 어떤 방식으로 찍을 지를 결정하는 설정으로 보인다.

기본 설정값은 console에 찍는 방식으로 되어있고 PatternLayout 클래스를 사용해서 Layout을 잡는다.

 

logger는 Application Loggers라고 주석이 달린 것처럼 package와 같이 영역을 지정하고 해당 영역에서 사용할 logger를 정의하는 태그로 보인다.

하위 파라미터로 level을 받아서 출력할 로그수준을 정한다.

appender-ref 태그를 사용하여 ref 값에 참조할 appender를 입력하여 출력방식을 정할 수 있다.

 

root는 default라고 생각하면 되는 것 같다. 설정하지 않은 logger에 대해서만 root logger를 출력하게 한다. 구성요소는 logger와 유사하다.

**같은 로그가 두번 찍힌다면 Additivity 속성에 대해 확인

 

 

2.설정 파일 기본값

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">

	<!-- Appenders -->
	<appender name="console" class="org.apache.log4j.ConsoleAppender">
		<param name="Target" value="System.out" />
		<layout class="org.apache.log4j.PatternLayout">
			<param name="ConversionPattern" value="%-5p: %c - %m%n" />
		</layout>
	</appender>
	
	<!-- Application Loggers -->
	<logger name="kr.co.ohjooyeo">
		<level value="info" />
	</logger>
	
	<!-- 3rdparty Loggers -->
	<logger name="org.springframework.core">
		<level value="info" />
	</logger>
	
	<logger name="org.springframework.beans">
		<level value="info" />
	</logger>
	
	<logger name="org.springframework.context">
		<level value="info" />
	</logger>

	<logger name="org.springframework.web">
		<level value="info" />
	</logger>

	<!-- Root Logger -->
	<root>
		<priority value="warn" />
		<appender-ref ref="console" />
	</root>
			
</log4j:configuration>

 

사용방법

사용할 클래스에 private static final 로거 변수 선언 이렇게 선언을 하면 xml에서 해당 어플리케이션(패키지)의 로거를 생성

1. 선언

Logger의 name을 패키지로 잡았을 경우, 패키지내의 클래스명.class를 파리미터로 선언

private static final Logger logger = LoggerFactory.getLogger(선언한 클래스명.class);

 

Logger의 name을 변수명으로 잡았을 경우, logger의 이름(문자열)을 파리미터로 선언

private static final Logger logger = LoggerFactory.getLogger("test");
<!-- Application Loggers --> <logger name="test"> <level value="info" /> </logger>

Log4j의 구조

1. Logger

  • 로그의 주체 : 로그 파일을 작성하는 클래스
  • 로그레벨을 가짐 (로그문의 레벨과 로거 레벨를 비교하여 로그의 출력여부를 결정)
  • 출력할 메시지를 Appender에 전달

2.Appender

  • 전달된 로그를 어디에 출력할지 결정 ( 콘솔 / 파일 / DB 등)
    • Appender 종류
      • WriterAppender :
        Writer 객체에 로그를 남기는 Appender
      • ConsoleAppender :
        System.out, System.err에 로그를 남기는 Appender

        - 옵션
        Target : System.out / System.err
        Follow : true -> SystemOutStream 에 저장
        activeOption : appender를 활성화

      • FileAppender :
        파일에 로그를 남기는 Appender

        - 옵션
        File : 파일명 Append : 추가 모드 여부 (true/false)
        BufferedIO : 버퍼 사용 여부 (true/false)
        BufferSize : 버퍼 사이즈

        --??--
        Threshold : (AppenderSkelton으로부터 계승)
        ImmediateFlush : (WriteAppender로부터 계승)
        Encoding : (WriteAppender로부터 계승)

      • RollingFileAppender :
        크기에 따라 File명을 변환하며 로그를 남기는 Appender

        - 옵션
        MaxFileSize : 최대 파일 사이즈
        MaxBackupIndex : 로그를 최대 개수
        File, Append, BufferdIO, BufferSize, Threshold, ImmediateFlush, Encoding

      • DailyRollingFileAppender :
        날짜에 따라 File명을 변환하며 로그를 남기는 Appender

        - 옵션 DatePattern : 날짜 형식(yyyy-MM, yyyy-ww,yyyy-MM-dd, yyyy-MM-dd-a, yyyy-MM-dd-HH 등등)

      • RollingFileAppender : 크기에 따라 File명을 변환하며 로그를 남기는 Appender
      • AsyncAppender : Logging Event 발생시 Thread를 생성하여 로그를 남기는 Appender

        -옵션
        triggeringPolicy : 로그 트리거 옵션
        rollingPolicy : 로그 정책 옵션
        org.apache.log4j.rolling.TimeBasedRollingPolicy : 시간 베이스 org.apache.log4j.rolling.SizeBasedTriggeringPolicy : 사이즈 베이스org.apache.log4j.rolling.FilterBasedTriggeringPolicy : 필터 베이스org.apache.log4j.rolling.FixedWindowRollingPolicy : 인덱스 베이스 백업 파일
      • SMTPAppender :
        로그를 이메일로 전달하는 Appender

출처: <https://m.blog.naver.com/PostView.nhn?blogId=youngchanmm&logNo=221029597791&proxyReferer=https%3A%2F%2Fwww.google.co.kr%2F>

 

 

3. Layout

  • 어떤 형식으로 출력할지 결정
  • Layout 종류
    • DateLayout
    • HTMLLayout
    • PatternLayout (일반적으로 사용)
      • PatternLayout (org.apache.log4j.PatternLayout) 상세 설명
        • C : 클래스명을 출력
          설정을 추가하여 클래스 이름 또는 특정 패키지 이상만 출력하도록 설정 가능

          - 추가 설정 -
          m : 로그로 전달된 메시지를 출력한다.
          M : 로그를 수행한 메소드명을 출력한다.
          n : 줄 바꿈
          p : 로그 이벤트명 (DEBUG등)
          r : 로그 처리시간 (milliseconds)

        • d : 로그 시간을 출력한다. java.text.SimpleDateFormat에서 적절한 출력 포맷을 지정할 수 있다.
        • F : 파일 이름을 출력한다. 로그 시 수행한 메소드, 라인번호가 함께 출력된다.
        • l (location) : 로깅 이벤트가 발생한 클래스의 풀네임.메서드명(파일명:라인번호) 출력
        • L : 라인 번호만 출력
        • m : 로그로 전달된 메시지 출력
        • M : 로그를 수행한 메소드명 출력
        • n : 줄바꿈
        • p : 로그 이벤트명 (레벨명)
        • r : 로그 처리시간 (milliseconds)
        • T : 로깅 이벤트가 발생한 스레드명

          출처: <https://androphil.tistory.com/420>
      • SimpleLayout
      • XMLLayout

 

 

Log4j 란?
log4j는 프로그램을 작성하는 도중에 로그를 남기기 위해 사용되는 자바기반 로깅 유틸리티이다. 디버그용 도구로 주로 사용되고 있다.
log4j의 최근 버전에 의하면 높은 등급에서 낮은 등급으로의 6개 로그 레벨을 가지고 있다. 설정 파일에 대상별(자바에서는 패키지)로 레벨을 지정이 가능하고 그 등급 이상의 로그만 저장하는 방식이다.
1. 목적
System.out.println() 을 사용하여 로그를 확인할 경우 사용하지 않게 되면 일일이 주석처리를 해야 한다.
로그의 레벨이나 로그문의 레벨에 따라서 로그를 유연하게 출력하여 불필요한 업무를 줄이고 성능을 최적화 할 수 있다.
(그대로 둔다면 프로그램 성능에 영향을 미칠 수 있다.)
2. 종류
6개의 레벨로 구성됨
1.
FATAL : 아주 심각한 에러가 발생한 상태를 나타낸다.
2.
ERROR : 어떠한 요청을 처리하는 중 문제가 발생한 상태를 나타낸다.
3.
WARN : 프로그램의 실행에는 문제가 없지만, 향후 시스템 에러의 원인이 될 수 있는 경고성 메시지를 나타낸다.
4.
INFO : 어떠한 상태변경과 같은 정보성 메시지를 나타낸다.
5.
DEBUG : 개발 시 디버그 용도로 사용하는 메시지를 나타낸다.
6.
TRACE : 디버그 레벨이 너무 광범위한 것을 해결하기 위해서 좀 더 상세한 이벤트를 나타낸다.
개요
GenericServlet을 확장한 HttpServlet 클래스 이용하여 서블릿 생성
클라이언트 요청을 GET, POST등으로 구분하여 처리
리다이렉트, 리프래시를 다루는 방법 학습
초기화 매개변수를 이용 설정 정보를 외부 파일에 두는 방법 및 서블릿에서 참고하는 방법
JDBC를 이용하여 데이터베이스회원 정보를 등록, 조회, 변경, 삭제
데이터베이스에서 데이터 가져오기
데이터베이스를 사용하기 위한 요소
1) JDBC 드라이버 : 데이터베이스를 요청, 전달, 결과를 받을 때 사용할 도구 자바에서 제공하는 DB와 통신하는 기술 자바에 데이터 베이스 접근하기 위해 필요
2) SQL : 데이터베이스에 명령을 내릴 언어
1. 회원 목록 조회 구현
샘플 데이터 생성
-- 샘플 테이블 생성 create table MEMBERS ( MNO integer not null comment '일련번호', EMAIL VARCHAR(40) not null comment '이메일', PWD VARCHAR(100) not null comment '암호', MNAME VARCHAR(50) not null comment '이름', CRE_DATE DATETIME not null comment '가입일', MOD_DATE DATETIME not null comment '마지막암호변경일' ) COMMENT '회원기본정보'; -- 제약조건, 인덱스 등 설정 ALTER TABLE MEMBERS ADD CONSTRAINT PK_MEMBERS PRIMARY KEY ( MNO ); CREATE UNIQUE INDEX UIX_MEMBERS ON MEMBERS (EMAIL ASC ); ALTER TABLE MEMBERS MODIFY COLUMN MNO INTEGER NOT NULL AUTO_INCREMENT COMMENT '회원일련번호'; -- 샘플데이터 입력 INSERT INTO MEMBERS(EMAIL, PWD, MNAME, CRE_DATE, MOD_DATE) VALUES ('s2@test.com','1111','임꺽정',NOW(),NOW()); INSERT INTO MEMBERS(EMAIL, PWD, MNAME, CRE_DATE, MOD_DATE) VALUES ('s3@test.com','1111','일지매',NOW(),NOW()); INSERT INTO MEMBERS(EMAIL, PWD, MNAME, CRE_DATE, MOD_DATE) VALUES ('s4@test.com','1111','이몽룡',NOW(),NOW()); INSERT INTO MEMBERS(EMAIL, PWD, MNAME, CRE_DATE, MOD_DATE) VALUES ('s5@test.com','1111','성춘향',NOW(),NOW());
SQL
JDBC 드라이버 준비
Type1 JDBC 드라이버 : 자바 실행환경 (Java Runtime Environment) 에 기본 포함 ODBC 드라이버를 사용
Type4 JDBC 드라이버 : MySQL 통신 프로토콜에 맞추어 데이터베이스와 직접 통신 ⇒ ODBC 드라이버를 필요로 하지 않음
서블릿 만들기
@WebServlet("/member/list") public class MemberListServlet extends GenericServlet { @Override public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException { Connection conn = null; Statement stmt = null; ResultSet rs = null; try { DriverManager.registerDriver(new com.mysql.jdbc.Driver()); conn = DriverManager.getConnection("jdbc:mysql://ec2-52-79-233-2.ap-northeast-2.compute.amazonaws.com/workbook?serverTimezone=Asia/Seoul", "workbook", "password"); stmt = conn.createStatement(); rs = stmt.executeQuery("select * from MEMBERS"); response.setContentType("text/html; charset=UTF-8"); PrintWriter out = response.getWriter(); out.println("<html><head><title>회원목록</title></head>"); out.println("<body><h1>회원목록</h1>"); while (rs.next()) { out.println( rs.getInt("MNO") + "," + rs.getString("MNAME") + "," + rs.getString("EMAIL") + "," + rs.getDate("CRE_DATE") + "<br>"); } out.println("</body></html>"); } catch(Exception e){ throw new ServletException(e); }finally { try {if (rs != null) rs.close();} catch(Exception e) {} try {if (stmt != null) stmt.close();} catch(Exception e) {} try {if (conn != null) conn.close();} catch(Exception e) {} } } }
Java
1) JDBC 관련 객체 선언
Connection conn = null; Statement stmt = null; ResultSet rs = null;
Java
2) JDBC API 예외 발생 대비
try { } catch(Exception e){ throw new ServletException(e); }finally { }
Java
서블릿에서 발생한 예외는 서블릿 컨테이너에 던짐
3) DriverManager가 사용할 JDBC 드라이버 등록
DriverManager.registerDriver( new com.mysql.jdbc.Driver()) // new com.mysql.jdbc.Driver() == java.sql.Driver 인터페이스 구현체
Java
java.sql.Driver 인터페이스 구현체
DriverManager에 등록됨
getMajorVersion(), getMinorVersion() : JDBC 드라이버 버전 제공
acceptsURL() : JDBC URL이 드라이버에서 사용가능한지 알려줌
connect() : 데이터베이스와 연결 수행 > Connect 객체 반환
4) DriverManager가 사용할 JDBC 드라이버 등록
DriverManager.registerDriver( new com.mysql.jdbc.Driver()) // new com.mysql.jdbc.Driver() == java.sql.Driver 인터페이스 구현체
Java
5) 데이터베이스에 연결
conn = DriverManager.getConnection("jdbc:mysql://ec2-52-79-233-2.ap-northeast-2.compute.amazonaws.com/workbook?serverTimezone=Asia/Seoul", "workbook", "password");
Java
DriverManager.getConnection(JDBC URL, DBMS 사용자 ID, DBMS 사용자 PW);
** JDBC URL의 구조
jdbc:mysql:thin:@localhost:3306:studydb // jdbc:mysql = 사용할 JDBC 드라이버 // thin = 드라이버 타입 // @localhost:3306 = 서버주소, 포트번호 // studydb = db 서비스 id
Java
⇒ java.sql.Connection 인터페이스 구현체 반환 : SQL을 실행할 객체를 얻을 수 있음
주요메서드
1) SQL문 실행 객체 반환 : createStatement(), prepareStatement(), prepareCall()
2) 트랜잭션 처리 수행 : commit(), rollback()
6) SQL 실행 객체 준비
stmt = conn.createStatement();
Java
⇒ java.sql.Statement 인터페이스 구현체 반환 : 데이터베이스에 질의하는 객체
주요 메서드
1) executeQuery() : 결과가 만들어지는 SQL문 실행 시 사용 (주로 SELECT 문에서 실행)
2) executeUpdate() : DML, DDL 실행시 사용
3) execute() : SELECT, DML, DDL 모두사용가능
4) executeBatch() : addBate()로 여러개의 SQL 등록, 한번에 실행
7) 데이터베이스에 SQL문 보내기
rs = stmt.executeQuery("select * from MEMBERS");
Java
⇒ java.sql.ResultSet 인터페이스 구현체 반환
주요 메서드
1) first() : 서버에서 첫번째 레코드 가져오기
2) last() : 서버에서 마지막 레코드 가져오기
3) previous() : 서버에서 이전 레코드 가져오기
4) next() : 서버에서 다음 레코드 가져오기
5) getXXX(컬럼명) : 특정 컬럼을 타입에 맞추어 가져오기 (getInt,getString,getDate ... )
6) updateXXX() : 특정 컬럼의 값을 변경
7) deleteRow() : 현재 레코드 지우기
8) select 결과 가져오기
while (rs.next()) { out.println( rs.getInt("MNO") + "," + rs.getString("MNAME") + "," + rs.getString("EMAIL") + "," + rs.getDate("CRE_DATE") + "<br>"); }
Java
rs.next() : t/f를 반환 다음이 있을 경우, rs 객체는 해당 레코드를 가져옴
9) JDBC 프로그래밍의 마무리
finally { try {if (rs != null) rs.close();} catch(Exception e) {} try {if (stmt != null) stmt.close();} catch(Exception e) {} try {if (conn != null) conn.close();} catch(Exception e) {} }
Java
finally에 자원을 해제하기 좋음
자원 해제 시 자원 선언의 역순으로 처리
개요
서블릿 프로그램의 핵심은 Servlet 인터페이스를 이해하는 것
Servlet을 먼저 배운 다음 HttpServlet을 사용하는 방법을 배워야 함
CGI 프로그램과 서블릿
1.CGI의 이해
CGI : 웹서버와 프로그램이 주고받는 규칙
CGI프로그램: CGI 규칙에 따라 웹서버와 데이터를 주고 받도록 작성된 프로그램
[웹브라우저] ——요청→ [웹서버] —————실행→ [프로그램] [웹브라우저] ←응답—— [웹서버] ←결과(CGI 규칙)- [프로그램]
2. 서블릿
: 자바로 만든 CGI 프로그램
다른 CGI와 다른점 : 웹서버와 직접 데이터를 주고받지 않음, 전문 프로그램에 의해 관리됨
서블릿 컨테이너
: 서블릿의 생명주기를 관리하는 프로그램
서블릿 컨테이너가 서블릿을 대신하여 CGI 규칙에 따라 웹 서버와 데이터를 주고 받음
⇒ 더 이상 개발자가 CGI 규칙을 신경 쓸 필요가 없어짐
대신, 서블릿 컨테이너와 서블릿 사이의 규칙을 알아야 함
[웹서버] ————요청위임→ [서블릿 컨테이너] ——실행→ [서블릿(.class)] [웹서버] ←결과(CGI 규칙) — [서블릿 컨테이너] ←결과(Servlet 규칙)- [서블릿(.class)]
** 서블릿 컨테이너에 형식을 맞추어서 올리는 이유는 서블릿 컨테이너가 웹서버에 CGI규칙에 맞추어서 결과를 보내기 때문
웹프로젝트 준비
1. 웹 프로젝트 생성
View Original
http ://127.0.0.1:9999/web01 (웹어플리케이션 이름)/Hello(서블릿이름)
WebConent : 웹 컨텐트 파일을 저장할 작업 폴더, 서버에 자동 배치할 때 이 폴더의 내용물을 서버의 배치 폴더로 복사
2. 웹프로젝트 폴더 구조
View Original
src : 자바 소스 파일 두는 곳, 서블릿 클래스나 필터,프로퍼티 파일, 리스너 등 필요한 모든 자바 클래스 파일을 두는 곳
build/classes : .class 파일이 놓이는 곳
WebContent/WEB-INF : 웹 애플리케이션의 설정과 관련된 파일을 두는 곳 이 폴더는 클라이언트에서 요청할 수 없음 ⇒ HTML,JS,CSS 등 파일 두면 안됨
WebContent/WEB-INF/web.xml : 웹 애플리케이션의 배치 설명서 (DD 파일)
서블릿이나 필터, 리스너, 매개변수, 기본 웹페이지 등 웹어플리케이션 컴포넌트의 배치 정보 작성
서블릿 컨테이너는 클라이언트 요청을 처리할 때 이 파일의 정보를 참고
(서블릿을 실행하기전 참고) 서블릿 클래스 찾거나 필터 실행하여 수행
WebConent/WEB-INF/lib
: jar를 두는 폴더 ( jar : 클래스 파일 + 프로퍼티 파일을 모아놓은 보관소)
서블릿 만들기
1. 서블릿 작성
public class HelloWorld implements Servlet{ ServletConfig config; @Override public void init(ServletConfig config) throws ServletException { System.out.println("init() 호출"); this.config = config; } @Override public void destroy() { System.out.println("destroy() 호출함"); } @Override public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException { System.out.println("service() 호출됨"); } @Override public ServletConfig getServletConfig() { System.out.println("getServletConfig() 호출됨"); return this.config; } @Override public String getServletInfo() { System.out.println("getServletInfo() 호출됨"); return "version=1.0; author=jh; copyright=jh"; } }
Java
2. javax.servlet.Servlet 인터페이스
Servlet 인터페이스를 구현해서 컨테이너가 그에 해당하는 서블릿( CGI 프로그램 .class)을 호출
1) 생명주기에 관련 메서드 : init() , service, destroy()
init() : 서블릿 컨테이너가 서블릿을 생성한 후 초기화 작업 수행하는 메서드 클라이언트의 요청을 처리하기 전 준비 작업 ex) DB 연결, 외부스토리지 서버 연결, 프로퍼티 로딩 등
service() : 클라이언트가 요청할 때 마다 호출하는 메서드 실질적으로 서비스 수행(할일 작성)
destroy() : 서블릿 컨테이너가 종료되거나 웹 애플리케이션이 멈출 때 수행 ex) 확보한 자원을 해제, 데이터 저장 등 마무리 작업 수행
2) Servlet 인터페이스 기타 메서드 : getServletConfig(), getServletInfo()
getServletConfig() : 서블릿 설정 정보를 다루는 ServletConfig 객체를 반환 서블릿 이름, 서블릿 초기 매개변수, 서블릿 환경정보 등 얻을 수 있음
getServletInfo() : 서블릿 작성자에 대한 정보, 권리 등을 담은 문자열을 반환
3. 서블릿 배치 정보 작성
배치 설명서 파일(DD 파일) (WEB-INF/web.xml)에 서블릿을 등록
서블릿 컨테이너가 찾을 수 있도록 등록하는 과정
<!-- 서블릿 선언 --> <servlet> <servlet-name>Hello</servlet-name> <servlet-class>lesson03.servlets.HelloWorld</servlet-class> </servlet> <!-- 서블릿을 URL과 연결 --> <servlet-mapping> <servlet-name>Hello</servlet-name> <url-pattern>/Hello</url-pattern> </servlet-mapping>
XML
등록되어 있지 않다면 ?
[웹서버] ————요청위임 (이 과정에서 찾지 못함)→ [서블릿 컨테이너] ——실행→ [서블릿(.class)]
1) 서블릿 선언 <servlet>
servlet-name : 서블릿의 별명 (서블릿과 이름이 같을 필요 없음)
servlet-class : 패키지이름을 포함한 서블릿 클래스명
2) 서블릿 URL 부여 <servlet-mapping>
serlet-name : 별명으로 서블릿을 가져옴
url-pattern : 서블릿을 요청할 때 클라이언트가 사용할 URL (/ = 컨텍스트 루트 의미)
4. 서블릿 구동 절차
서블릿 컨테이너 HelloWorld
1) 요청 ——> 2) 서블릿인스턴스 없으면 클래스 로딩 ————————> 인스턴스 생성 ———————> 생성자 호출 ————————> init() 호출 ————————> 3) service() 호출 ——————> <—— 4)응답 5)컨테이너 종료 → 6) destory() 호출 ——————>
컨테이너는 서블릿 인스턴스를 가지고 있음
요청이 오면 인스턴스를 먼저 확인하고 없으면 클래스를 로딩
서블릿 인스턴스는 하나만 생성되어 사용되기 때문에 인스턴스변수에 특정 사용자를 위한 데이터를 보관해서는 안됨
클라이언트가 보낸 데이터를 일시적으로 보관하기 위해서 서블릿 인스턴스 변수를 사용하면 안됨
5. 웰컴 파일들
디렉토리 기본 웹 페이지
컨텍스트 루트 디렉토리( ex. http:// 127.0.0.1:9000/web03)에 요청 시 응답
WEB-INF/web.xml 일부분
<welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list>
XML
위와 같은 파일명 하나를 선택해서 WebContent 하위에 생성하면 자동으로 불러옴
이클립스로 톰캣서버를 실행하면 웹어플리케이션은 WTP (Web Tool Platform) 플러그인이 관리하는 임시 폴더에 배포됨
Path : .metadata/.plugins/org.eclipse.wst.server.core/tmp0/wtpwebapps/web03
View Original
GenericServlet 사용
GenericServlet 추상 클래스를 사용하여 서블릿 생성
웹브라우저에서 서블릿으로 데이터를 보내는 방법
서블릿에서 데이터를 꺼내는 방법
서블릿에서 작업한 결과를 웹브라우저로 보내는 방법
1. GenericServlet이 없던 시절 (문제상황)
서블릿 만들 때 마다 servlet 인터페이스에 맞게 구현 (5개의 메서드)
⇒ 그렇지만, 클라이언트 요청 마다 호출 되는 service() 외에는 반드시 구현해야 할 필요가 없음
⇒ 빈 메서드를 구현하는 식으로 인터페이스를 맞춤 (ex. public void destroy(){ })
2. GenericServlet 의 용도
GenericServlet = 추상 클래스 (하위 클래스에게 공통의 필드와 메서드를 상속해 주고자함)
service() 를 제외한 init(), destroy(), getServletConfig(), getServletInfo() 미리 구현
// GenericServlet 구현 public class HelloWorld extends GenericServlet{ @Override public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException { System.out.println("servlet 호출됨"); } }
Java
3. ServletRequest
service() 매개변수 중 하나, 클라이언트 요청 정보를 다루는 객체
getParameter() : GET, POST 요청에 들어온 매개변수 가져옴
getRemoteAddr() : 서비스를 요청한 클라이언트의 IP 주소를 반환
getScheme() : URI 형식의 스킴을 반환 ** scheme : 자원을 식별하는 최상위 분류 기호 (ex. http, ftp,file, news 등)
getProtocol() : 요청 프로토콜의 이름과 버전 (ex. HTTP/1.1)
getParameterNames() : 요청 정보에서 매개변수 이름만 추출하여 반환
getParameterValues() : 요청 정보에서 값만 추출하여 반환
getParameterMap() : 요청 정보에서 매개변수들을 Map에 담아 반환
getCharacterEncoding() : POST 요청의 매개변수에 대해 문자 집합을 설정 ** 주의 : 처음 getParameter() 호출하기 전 이 메서드를 호출해야만 적용됨
GET 요청 매개변수에 대해서 문자 집합 설정 방법 매개변수가 URI에 포함되어서 옴 ⇒ 서블릿컨테이너에서 URI의 인코딩 형식 설정 (URIEncoding=" ")
<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443" URIEncoding="UTF-8"/>
XML
4. ServletResponse
응답과 관련된 기능 제공
인코딩 타입 설정, 문자집합 지정, 출력 데이터 임시 보관하는 버퍼 크기 조정, 데이터 출력하기 위한 출력 스트림 준비
setContentType() : 출력할 데이터의 인코딩 형식과 문자 집합 지정 클라이언트가 올바르게 화면 출력을 할 수 있도록 지정
response.setContentType("text/plain");
Java
setCharacterEncoding() : 출력할 데이터의 문자 집합 지정
response.setCharacterEncoding("UTF-8");
Java
getWriter() : 클라이언트로 출력할 수 있도록 출력 스트림 객체 반환 ** 참고 : 이미지, 동영상 같은 바이너리 데이터 출력은 getOutputStream()
PrintWriter writer = response.getWriter();
Java
5. @WebServlet 애노테이션을 이용한 서블릿 배치 정보 설정
web.xml 대신 애노테이션을 이용해 배치 정보 작성
() 안에 Servlet의 URL 정보 작성
@WebServlet("/calc") public class CalculatorServlet extends GernericServlet { ... }
Java
<!-- web.xml에 등록되어있는 정보를 주석처리해도 정상 작동--> <!-- <servlet> <servlet-name>CalculatorServlet</servlet-name> <servlet-class>lesson03.servlets.CalculatorServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>CalculatorServlet</servlet-name> <url-pattern>/calc</url-pattern> </servlet-mapping> -->
XML
6. @WebServlet 애너테이션의 주요 속성
name : 서블릿의 이름을 설정, 기본값은 "" @WebServlet(name="서블릿이름")
urlPatterns : 서블릿의 URL 목록 설정, 속성값으로 String , {} @WebServlet(urlPatterns="/calc") @WebServlet(urlPatterns={"/calc","calc.do", "calculator.action"})
value : urlPatterns 와 같은 용도 (생략가능) @WebServlet(value="/calc") @WebServlet("/calc")

+ Recent posts

"여기"를 클릭하면 광고 제거.