본문 바로가기

수업 정리

63~66일차 수업 정리(Spring MVC Project)

**Spring MVC Project를 위한 데이터베이스 테이블을 생성

    => Oracle과 MyBatis -> Hibernate로 변경 -> MySQL

1. Oracle에 작성

drop table item;

CREATE TABLE  ITEM(
	itemid number(5),
	itemname VARCHAR2(20),
	price number(6),
	description VARCHAR2(50),
	pictureurl VARCHAR2(20),
	PRIMARY KEY (itemid)
);

insert into item values(1, 'Lemon', 500, 'Vitamin-A', 'lemon.jpg'); 
insert into item values(2, 'Orange', 1500, 'Vitamin-B', 'orange.jpg'); 
insert into item values(3, 'Kiwi', 2000, 'Vitamin-C', 'kiwi.jpg'); 
insert into item values(4, 'Grape', 1000, 'Vitamin-D', 'grape.jpg'); 
insert into item values(5, 'Strawberry', 2000, 'Vitamin-E', 'strawberry.jpg'); 
insert into item values(6, 'Mandarin', 300, 'Vitamin-F', 'mandarin.jpg'); 
commit;

select * from ITEM;

    => 오라클은 테이블이름과 컬럼명을 모두 대문자를 사용 : DTO를 만들지 않고 Map 이용시 중요

 

2. MySQL에 작성

drop table item;

-- 데이터 삽입, 삭제, 갱신이 빈번하면 innodb
-- 주 업무가 조회라면 MyISAM(Indexed Sequential Access Media)으로 설정
-- 한글이 있으면 인코딩 설정을 해주어야 함
CREATE TABLE  ITEM(
	itemid int,
	itemname VARCHAR(20),
	price int,
	description VARCHAR(50),
	pictureurl VARCHAR(20),
	PRIMARY KEY (itemid)
)engine=innodb default charset=utf8;

insert into item values(1, 'Lemon', 500, 'Vitamin-A', 'lemon.jpg'); 
insert into item values(2, 'Orange', 1500, 'Vitamin-B', 'orange.jpg'); 
insert into item values(3, 'Kiwi', 2000, 'Vitamin-C', 'kiwi.jpg'); 
insert into item values(4, 'Grape', 1000, 'Vitamin-D', 'grape.jpg'); 
insert into item values(5, 'Strawberry', 2000, 'Vitamin-E', 'strawberry.jpg'); 
insert into item values(6, 'Mandarin', 300, 'Vitamin-F', 'mandarin.jpg'); 
commit;

select * from ITEM;

    => MySQL은 테이블 이름이나 컬럼 이름을 소문자로 사용

 

**Spring MVC Project 설정

1. Spring MVC Project를 생성

    => 기본 패키지 이름을 3 level 이상으로 설정

    => 3번째 패키지 이름이 application의 ContextPath(URL 기본경로) : Tomcat에서 수정 가능

        - 실제 도메인 연결시는 없어짐, 포트번호 생략하려고 하면 http : 80번, https : 443번을 사용

 

2. 애플리케이션 실행

    => url - http://localhost:9000/pk/

 

3. 포트번호 생략

    => server.xml에서 tomcat의 port를 80번으로 변경

<!-- Servers/Tomcat.../server.xml에서 수정 -->
<Connector connectionTimeout="20000" port="80" protocol="HTTP/1.1" redirectPort="8443"/>

 

4. ContextPath를 제거

    => ContextPath를 /로 변경

  1) Tomcat 설정을 수정

    => [Servers] 탭에서 Tomcat을 더블 클릭

    => Modules탭에서 실행하고자 하는 프로젝트를 선택하고 [Edit]버튼을 누른 후 path를 /로 설정

  2) 재실행

 

  3) 다른 컴퓨터의 브라우저에서는 http://IP주소 만 입력하면 접속이 됨

 

5. Java, Spring, JUnit, Servlet, JSP 버전을 변경

    => pom.xml에서 수행

  1) Java와 Spring버전 변경은 properties 태그에서 수행

<properties>
	<java-version>1.8</java-version>
	<org.springframework-version>5.0.7.RELEASE</org.springframework-version>
	<org.aspectj-version>1.6.10</org.aspectj-version>
	<org.slf4j-version>1.6.6</org.slf4j-version>
</properties>

    => java는 람다와 스트림을 사용한다면 1.8 이상

    => Spring은 4.0.0 이상을 사용

  2) dependencies 태그 안에서 servlet과 jsp의 dependency를 수정

<!-- Servlet -->
<dependency>
	<groupId>javax.servlet</groupId>
	<artifactId>javax.servlet-api</artifactId>
	<version>3.1.0</version>
	<scope>provided</scope>
</dependency>
<dependency>
	<groupId>javax.servlet.jsp</groupId>
	<artifactId>javax.servlet.jsp-api</artifactId>
	<version>2.3.0</version>
	<scope>provided</scope>
</dependency>

    => scope 태그를 남겨두어야 함

        - scope가 있으면 현재 프로젝트의 라이브러리를 가져가지 않고 실행되는 곳의 라이브러리 사용

        - servlet과 jsp는 java에 있는 것을 사용하지 않고 WAS의 것을 사용

  3) JUnit - dependencies 안에서 수정(4.12로 변경, Spring 5.0이상은 4.12부터 테스트 가능

    => 단위(클래스나 메소드) 테스트 라이브러리

<!-- Test -->
<dependency>
	<groupId>junit</groupId>
	<artifactId>junit</artifactId>
	<version>4.12</version>
	<scope>test</scope>
</dependency> 

    => scope를 남겨두는데 이 scope는 테스트 할때 사용되고 배포를 할 때는 삭제가 되는 라이브러리 설정

 

  4) servlet 버전을 변경한 경우 web.xml 파일의 DTD 수정

    => 이 작업없이도 실행에는 문제 X, 이 프로젝트를 다른 환경으로 가져가면 에러 발생

    => 구글에 web.xml 서블릿 버전으로 검색하면 DTD 검색됨

 

    => 기존 webapp 태그를 아래 내용으로 수정

<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">

 

6. 기본 설정이 끝나면 재실행

 

7. 여러명이 하는 프로젝트라면 여기까지는 일반적으로 project Manager가 결정

 

**오라클의 데이터를 MyBatis를 이용해서 읽어오기

    => MyBatis : SQL Mapper : SQL과 프로그래밍 언어의 코드를 분리

    => CRUD(Create, Read, Update, Delete)를 위한 프로젝트 구조

        - Controller <- Service <- Repository <- MyBatis Session(xml, interface)

        - DTO

        - Interceptor, AOP Class

        - Error Page 설정

 

1. pom.xml 파일에 필요한 의존성 설정

    => 필수 : oracle, spring-jdbc, mybatis, spring-mybatis : 필수

    => Spring-test : Test를 하고자 하면(거의 필수)

    => lombok : DTo 클래스를 편리하게 만들고자 하면(최근에 많이 사용)

    => log4jdbc : 데이터베이스 연동시 로그를 출력하고자 하면(개발시에만 사용)

 

  1) Oracle의 Repository 설정 : Oracle의 경우 Maven 중앙저장소에 없어서 설정

<repositories>
	<repository>
		<id>oracle</id>
		<name>ORACLE JDBC Repository</name>
		<url>http://maven.jahia.org/maven2</url>
	</repository>
</repositories>

 

  2) dependencies 에 나머지 의존성 설정

<dependency>
	<groupId>com.oracle</groupId>
	<artifactId>ojdbc7</artifactId>
	<version>12.1.0.2</version>
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-jdbc</artifactId>
	<version>${org.springframework-version}</version>
</dependency>
<dependency>
	<groupId>org.mybatis</groupId>
	<artifactId>mybatis</artifactId>
	<version>3.4.6</version>
</dependency>
<dependency>
	<groupId>org.mybatis</groupId>
	<artifactId>mybatis-spring</artifactId>
	<version>1.3.2</version>
</dependency>
<dependency>
	<groupId>org.projectlombok</groupId>
	<artifactId>lombok</artifactId>
	<version>1.18.8</version>
</dependency>
<dependency>
	<groupId>org.bgee.log4jdbc-log4j2</groupId>
	<artifactId>log4jdbc-log4j2-jdbc4</artifactId>
	<version>1.16</version>
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-test</artifactId>
	<version>${org.springframework-version}</version>
</dependency>

 

  3) pom.xml 수정후 저장하고 잠시 기다렸다가 재실행

    => 실행이 안되면 라이브러리를 다운받지 못한 것이나 설정 실패

 

2. 필요한 클래스를 생성

    => MyBatis를 xml로 사용

  1) DTO 클래스 생성 : kr.co.pk.item.domain

    => 자료형과 이름을 정확하게 기재

  2) DAO 클래스 생성 : kr.co.pk.item.

package kr.co.pk.item.dao;

import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

@Repository
public class ItemDAO {
	@Autowired
	//MyBatis를 xml로 이용할 때 사용하는 클래스
	private SqlSession sqlSession;
}

 

  3) Service 인터페이스 생성 : kr.co.pk.item.service.ItemService

package kr.co.pk.item.service;

public interface ItemService {

}

 

  4) Service 인터페이스의 내용을 구현할 ServiceImpl 클래스를 생성 : kr.co.pk.item.service.ItemServiceImpl

    => DAO를 주입받음

package kr.co.pk.item.service;

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

import kr.co.pk.item.dao.ItemDAO;

@Service
public class ItemServiceImpl implements ItemService {
	@Autowired
	private ItemDAO itemDao;
}

 

  5) Controller 클래스를 생성 : kr.co.pk.item.controller.ItemController

    => Service를 주입받음

package kr.co.pk.item.controller;

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

import kr.co.pk.item.service.ItemService;

@Controller
public class ItemController {
	@Autowired
	private ItemService itemService;
	
}

 

3. 데이터베이스 프레임워크 설정(Oracle + MyBatis)

  1) 데이터베이스 접속 정보를 저장

    => root-context.xml 파일에 작성

    => 데이터베이스는 모든 서비스에 이용될 가능성이 높으므로 root-context.xml 파일에 작성

<!-- 모든곳에서 사용할 Bean을 생성 -->
<!-- 데이터 베이스 접속 정보를 저장하는 bean을 생성 
데이터베이스 종류별로 설정 -->
<bean id="dataSource"
	class="org.springframework.jdbc.datasource.DriverManagerDataSource">
	<property name="driverClassName">
		<value>oracle.jdbc.driver.OracleDriver</value>
	</property>
	<property name="url">
		<value>jdbc:oracle:thin:@192.168.0.200:1521:xe</value>
	</property>
	<property name="username">
		<value>system</value>
	</property>
	<property name="password">
		<value>*******</value>
	</property>
</bean>

 

  2) Test 클래스를 만들어서 테스트

    => src/test/java 디렉토리에 생성

package kr.co.pk;

import javax.sql.DataSource;

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;

//설정 파일을 읽어오는 코드
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {
		"file:src/main/webapp/WEB-INF/spring/root-context.xml"})
public class ItemTest {
	//데이터베이스 접속 정보를 주입
	@Autowired
	private DataSource dataSource;
	
	//데이터베이스 연결을 테스트
	@Test
	public void connectTest() {
		try {
			System.out.println(dataSource.getConnection());
		}catch(Exception e) {
			System.out.println(e.getMessage());
			e.printStackTrace();
		}
	}
}

 

  3) XML을 이용해서 MyBatis를 연동할 때 사용할 매퍼 파일을 생성
    =>src/resources 디렉토리에 별도의 디렉토리(mappers)를 생성해서 만드는 것이 일반적

    => mappers/item.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="item">
  
  </mapper>

 

  4) root-context 파일에 xml을 이용해서 MyBatis를 사용할 수 있도록 해주는 Bean추가

<!-- XML을 이용한 MyBatis 사용 설정 -->
<bean id="sqlSessionFactory"
class="org.mybatis.spring.SqlSessionFactoryBean">
	<property name="dataSource" ref="dataSource" />
	<!-- mappers 디렉토리 안에 있는 모든 xml을 mapper 로 사용
	다른 용도의 xml 파일이 있으면 에러 -->
	<property name="mapperLocations"
		value="classpath:mappers/**/*.xml" />
</bean>

<bean id="sqlSession"
class="org.mybatis.spring.SqlSessionTemplate">
	<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>

 

  5) Test 클래스에 MyBatis 설정을 확인하기 위해 Test Source 추가

//MyBatis 사용 클래스를 주입
@Autowired
private SqlSession sqlSession;
	
//MyBatis 설정 정보를 테스트
@Test
public void mybatisTest() {
	System.out.println(sqlSession);
}

    => mapper 파일에 sql이 하나도 없어서 에러 발생 (sql을 작성하고 다시 테스트)

 

4. 웹 애플리케이션이 시작할 때 Item 테이블의 전체 데이터를 가져와서 출력

  1) sql 매퍼 파일에 필요한 SQL을 생성

<!-- Item 테이블의 데이터를 전부 가져오는 SQL -->
<select id="allitem"
	resultType="kr.co.pk.item.domain.Item">
		select itemid, itemname, price, description, pictureurl from item	
</select>

 

  2) ItemDAO 클래스에 전체 데이터를 가져오는 메소드를 생성

public List<Item> allitem(){
	return sqlSession.selectList("item.allitem");
}

 

  3) ItemService 인터페이스에 전체 데이터를 가져오는 메소드를 선언

//전체 데이터를 가져오는 메소드
public void allitem(
		HttpServletRequest request, 
		HttpServletResponse response);

 

 

  5) ItemController 클래스에 시작 요청을 처리하는 메소드를 수정

    => 단순한 웹 애플리케이션을 만들 때와 웹 서버를 만들 때는 Controeller가 조금씩 수정 

    => 기존 homeController 내용은 삭제

@RequestMapping(value = "/", method = RequestMethod.GET)
public String home(HttpServletRequest request, HttpServletResponse response) {
	//서비스의 메소드를 호출
	itemService.allitem(request, response);
	return "home";
}

 

  6) 기존의 home.jsp 파일을 삭제하고 새로 생성한 후 작성

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

<!-- jsp를 이용하여 데이터 출력시 이코드는 거의 필수 -->
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>ITEM</title>
</head>
<body>
	<div align="center" class="body">
		<h2>상품 목록</h2>
		<table border="1">
			<tr class="header">
				<th width="80">상품 ID</th>
				<th width="300">상품 이름</th>
				<th width="100">상품 가격</th>
			</tr>
			<c:forEach var="item" items="${list}">
				<tr class="recode">
					<td width="80">${item.itemid}</td>
					<td width="300">${item.itemname}</td>
					<td width="100" align="right">${item.price}원</td>
				</tr>
			</c:forEach>
		</table>
	</div>
</body>
</html>

 

  7) webapp 디렉토리에 css 디렉토리를 만들고 home.css 파일을 만들고 작성

div.body{
	overflow-y:auto;
	margin-top:50px;
	margin-bottom:50px;
}
tr.header{
	background:#C9BFED;
}
tr.record{
	backgorund:#EDEDED;
}

 

  8) home.jsp에 home.css를 적용

<link rel="stylesheet" href="${pageContext.request.contextPath}/css/home.css">

 

  9) servlet-context.xml 파일에 추가

<!-- Controller가 처리하지 못하는 URL은 WAS가 처리하도록 하는 설정 -->
<default-servlet-handler />

 

  10) 다시 실행

 

5. 상세보기 구현

    => 목록보기에서 하나를 골라서 그 하나를 자세히 보는 기능

    => 목록보기에서 목록을 클릭하면 클릭한 목록을 자세히 보여주는 형태로 구현

        - 클릭시 하나의 데이터를 찾아 올수 있도록 기본키 값을 어떻게 넘겨줄 것인지를 고려

        - 웹에서는 url 뒤에 파라미터로 넘겨주던가 /값을 작성하는 형태를 이용

    => 최근에는 파라미터로 넘기기 보다는 URL에 변수를 추가하는 형태로 많이 구현

  1) home.jsp 파일에 제목을 출력하는 부분을 수정

<a href="detail/${item.itemid}">${item.itemname}</a>

 

  2) item.xml 파일에 상세보기를 위한 SQL 구현

<!-- Item 테이블에서 데이터 1개를 가져오는 SQL -->
<select id="detailitem" 
parameterType="java.lang.Integer" 
resultType="kr.co.pk.item.domain.Item">
	select itemid, itemname, price, description, pictureurl 
	from item 
	where itemid = #{itemid}
</select>

 

  3) mapper 테스트 : Test 클래스에 작성

@Test
public void detailitem() {
	System.out.println(sqlSession.selectOne("item.detailitem",1));
}

 

  4) ItemDAO에 상세보기를 위한 메소드를 작성

//Item 테이블에서 1개의 데이터를 가져오는 메소드
public Item detailitem(Integer itemid) {
	return sqlSession.selectOne("item.detailitem", itemid);
}

 

  5) itemService인터페이스에 상세보기를 위한 메소드 선언

//상세보기를 위한 메소드
public void detailitem(HttpServletRequest request, HttpServletResponse response);

 

  6) itemServiceImpl 클래스에 상세보기를 위한 메소드를 구현

@Override
public void detailitem(HttpServletRequest request, HttpServletResponse response) {
	//요청 주소의 마지막 부분 가져오기
	//localhost/detail/itemid
	String requestURI = request.getRequestURI();
	String [] ar = requestURI.split("/");
	String itemid = ar[ar.length-1];
		
	//DAO의 메소드 호출
	Item item = itemDao.detailitem(Integer.parseInt(itemid));
		
	//결과를 저장
	request.setAttribute("item", item);
}

 

  7) ItemController 클래스에 상세보기를 위한 메소드를 구현

@RequestMapping(value = {"detail/{itemid}"}, method = RequestMethod.GET)
public String detail(HttpServletRequest request, HttpServletResponse response) {
	//서비스의 메소드를 호출
	itemService.detailitem(request, response);
	return "detail";
}

 

  8) 이미지를 프로젝트 내에 복사

    => webapp 디렉토리에 img 디렉토리 배치

 

  9) detail.jsp 파일을 생성하고 작성

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>상세보기</title>
<link rel="stylesheet" 
	href="${pageContext.request.contextPath}/css/style.css">
</head>
<body>
	<div align="center" class="body">
		<h2>상품 상세 화면</h2>
		<table border="1">
			<tr class="header">
				<td>
					<img src="${pageContext.request.contextPath}/img/${item.pictureurl}"/>
				<td>
				<td>
					<table>
						<tr height="50">
							<td width="80">상품명</td>
							<td width="160">${item.itemname}</td>
						</tr>
						<tr height="50">
							<td width="80">가격</td>
							<td width="160">${item.price}원</td>
						</tr>	
						<tr height="50">
							<td width="80">비고</td>
							<td width="160">${item.description}</td>
						</tr>
						<tr>
							<td colspan="2" align="center"
							width="240">
								<a href="${pageContext.request.contextPath}/">
									■목록으로 돌아가기
								</a>
							</td>
						</tr>
					</table>
				</td>
			</tr>
		</table>
	</div>
</body>
</html>

 

6. MySQL로 변환

  1) pom.xml 파일에 mysql 의존성을 추가

<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<version>5.1.49</version>
</dependency>

 

  2) root-context.xml 파일에서 DataSource를 MySQL로 변경

<bean id="dataSource"
	class="org.springframework.jdbc.datasource.DriverManagerDataSource">
	<property name="driverClassName">
		<value>com.mysql.jdbc.Driver</value>
	</property>
	<property name="url">
		<value>jdbc:mysql://localhost:3306/myproject?
		useUnicode=yes&amp;characterEncoding=UTF-8	</value>
	</property>
	<property name="username">
		<value>root</value>
	</property>
	<property name="password">
		<value>900826</value>
	</property>
</bean>

 

7. Mapper 인터페이스를 이용한 MyBatis 사용

    => Mapper 인터페이스를 이용하면 xml파일을 만들 필요 없고, DAO도 만들 필요가 없음

  1) Mapper로 사용할 인터페이스를 생성, 전체 데이터를 가져오는 메소드와 itemid를 이용하여 상세보기 메소드 생성

    => kr.co.pk.item.dao.ItemMapper

public interface ItemMapper {
	@Select("select itemid, itemname, price, description, pictureurl from item")
	public List<Item> allitem();
	
	@Select("select itemid, itemname, price, description, pictureurl from item "
			+ "where itemid = #{itemid}")
	public List<Item> detailitem(Integer itemid);
}

 

  2) root-context.xml 파일에 MapperFactoryBean을 생성

<!-- 인터페이스를 이용하여 MyBatis를 이용할 때 생성 -->
<bean id="ItemMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
	<property name="sqlSessionFactory" ref="sqlSessionFactory" />
	<property name="mapperInterface" value="kr.co.pk.item.dao.ItemMapper" />
</bean>

 

  3) ServiceImpl 클래스에서 ItemMapper를 주입받아서 사용하면 됨

@Autowired
//private ItemDAO itemDao;
	
private ItemMapper itemDao;

 

  4) 전자정부 프레임워크나 기업에서 만든 프레임워크 들은 샘플 프로젝트에서 xml을 이용하는 방식 채택

 

 

8. hibernate로 변환

    => DTO 클래스를 확인 : 자료형을 확인

  1) pom.xml 파일에 hibernate와 spring-orm의 의존성을 설정

<!-- hibernate 의존성 -->
<dependency>
	<groupId>org.hibernate</groupId>
	<artifactId>hibernate-entitymanager</artifactId>
	<version>5.4.2.Final</version>
</dependency>

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-orm</artifactId>
	<version>${org.springframework-version}</version>
</dependency>

 

  2) Item 클래스와 테이블을 매핑하기 위한 하이버네이트 설정 파일 작성

        => kr.co.pk.item.dao 패키지에 item.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
	"-//Hibernate/Hibernate Mapping DTD 3.0//EN" 
	"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="kr.co.pk.item.domain">
	<class name="Item" table="ITEM">
		<id name="itemid" column="itemid" />
		<property name="itemname" column="itemname"/>
		<property name="price" column="price"/>
		<property name="description" column="description"/>
		<property name="pictureurl" column="pictureurl"/>
	</class>
</hibernate-mapping>

 

  3) root-context.xml 파일에 hibernate bean을 추가

    => 하이버네이트는 트랜잭션이 필수라서 tx네임스페이스를 추가

<!-- 하이버네이트 설정 -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
	<property name="dataSource" ref="dataSource"/>
	<property name="mappingResources">
		<list>
			<value>kr/co/pk/item/dao/item.hbm.xml</value>
		</list>
	</property>
	<property name="hibernateProperties">
			<value>hibernate.dialect=org.hibernate.dialect.MySQLDialect</value>
	</property>
</bean>
	
<tx:annotation-driven/>

<bean id="transactionManager" 
    class="org.springframework.orm.hibernate5.HibernateTransactionManager">
	<property name="sessionFactory" ref="sessionFactory"/>

</bean>

 

  4) ItemDAO 클래스에 Hibernate 의 SessionFactory를 주입하고 메소드 코드를 수정

@Autowired
private SessionFactory sessionFactory;
	
//Item 테이블의 전체 데이터를 가져오는 메소드
public List<Item> allitem(){
	//return sqlSession.selectList("item.allitem");
	return sessionFactory.getCurrentSession().createCriteria(Item.class).list();
}
//Item 테이블에서 1개의 데이터를 가져오는 메소드
public Item detailitem(Integer itemid) {
	//return sqlSession.selectOne("item.detailitem", itemid);
	return sessionFactory.getCurrentSession().get(Item.class, itemid);
}

 

  5) ItemServiceImpl의 메소드위에 @Transactional을 추가

    => 트랜잭션은 Service에 적용

 

**프로젝트에서 MyBatis를 XML로 사용하는 형태로 변환 - 데이터베이스는 오라클

    => 우리나라 공공기관이 대기업 SI에서는 아직까지 Oracle에 XML을 이용하는 MyBatis 방식을 많이 사용하기 때문

1. root-context.xml 파일에서 MySQL의 DataSource를 주석 처리하고 오라클의 DataSource를 주석해제

 

2. ItemDAO 클래스에서 하이버네이트 사용하는 부분을 주석처리하고 MyBatis 사용하는 부분으로 변경

 

3. ItemServiceImpl 클래스에서 MyBatis를 xml로 호출하는 형태로 변환

 

**다양한 View 출력

1. 파일 다운로드

    => 파일의 경로를 a 태그의 href에 설정하면 다운로드가 만들어짐

    => 링크에 설정된 파일이 브라우저가 사용가능한 파일이면 다운로드가 되지 않고 브라우저가 사용

        - 텍스트 파일이나 이미지 파일의 경우 브라우저가 출력

        - 음악, 동영상 파일은 재생

        - 강제 다운로드는 파일링크에서 마우스 오른 쪽을 클릭해서 다른 이름으로 저장을 하면 됨

          -> 무조건 다운로드가 되게 하려면 별도의 View를 만들어서 출력을 해야 함

 

2. Spring ViewResolver

    => Spring Controller는 View에 의존적이지 않음

        - Spring Controller는 View의 이름을 만들어주는 것이지 실제 View를 결정하지 않음

        - 실제 View를 결정하는 것은 ViewResolver

  1) ViewResolver의 종류

    => InternalResourceViewResolver : 기본적으로 제공되는 ViewResolver로 JSTLView로 출력

    => BeanNameViewResolver : View의 이름을 가지고 직접 출력할 View를 지정

  2) jsp 이외의 View로 출력하고자 하는 경우

    => BeanNameViewResolver 클래스의 bean을 생성해야 함

    => 그리고 난 후, <bean id="출력할 뷰 이름" class="출력할 뷰 클래스 이름"/>로 설정

  3) jsp도 이용할 것라면 주의

    => InternalResourceViewResolver에 order 프로퍼티를 추가해서 BeanNameViewResolver의 order값보다 높게 설정

       - BeanNameViewResolver가 처리하지 못하면 InternalResourceViewResolver가 처리하도록 해주어야 함

 

3. 오늘 출력 ( FileDownload View, CSV, Excel, PDF, JSON, XML 출력)

    => 공공기관 같은 곳에서 OpenAPI 서버를 만드는 경우에는 모두 중요

    => OpenAPI 서버가 아니라 자신의 App이나 기업을 위한 API를 만들경우 JSON과 XML이 중요

 

**프로젝트 내의 img 디렉토리의 파일 다운로드 링크 만들기

1. home.jsp 파일에 파일 다운로드 링크 만들기

<ul>
	<li>
		<a href="fileview" class="menu">파일 목록 보기</a>
	</li>
</ul>

 

2. home.css 파일에 스타일 추가

.menu {
	display: block;
	width: 10em;
	margin: 3px 5px;
	padding: .5em 1em;
	text-decoration: none;
	text-align: center;
	color: #fff;
	background-color: rgb(190, 190, 190);
	border: 2px solid rgb(175, 175, 175);
	border-radius: 6px;
	text-shadow: #666 .1em .1em .1em;
	box-shadow: 0 5px 3px rgba(0, 0, 0, .5);
	position: relative;
	-webkit-transition: background-color 0.2s, border-color 0.2s, top .2s,
		box-shadow 0.2s;
	-moz-transition: background-color 0.2s, border-color 0.2s, top .2s,
		box-shadow 0.2s;
	-o-transition: background-color 0.2s, border-color 0.2s, top .2s,
		box-shadow 0.2s;
	-ms-transition: background-color 0.2s, border-color 0.2s, top .2s,
		box-shadow 0.2s;
	transition: background-color 0.2s, border-color 0.2s, top .2s,
		box-shadow 0.2s;
}

ul {
	list-style-type: none;
	margin: 0;
	padding: 0;
}

li {
	margin-bottom:3em;
}

/* a 태그에 포커스가 오거나 마우스가 올라오면 적용
: 다음에 나오는 것은 가상 선택자, 가상 클래스 */
a:hover, a:focus {
	background-color: #fdca00;
	border-color: #fda700;
}

a:active {
	top: 3px;
	box-shadow: 0 1px 2px rgba(0, 0, 0, .5);
}

 

3. ItemService 인터페이스에 img 디렉토리의 파일 목록을 보여주는 메소드를 선언

//img 디렉토리의 파일이름 목록을 가져오기 위한  메소드
public void fileview(
		HttpServletRequest request, 
		HttpServletResponse response);

 

4. ItemServiceImpl 클래스에 파일 목록을 보여주는 메소드를 구현

@Override
public void fileview(HttpServletRequest request, HttpServletResponse response) {
	//프로젝트 내의 디렉토리에 대한 절대 경로 가져오기
	String imgPath = 
			request.getServletContext().getRealPath("/img");
	//디렉토리 안의 모든 파일에 대한 이름을 가져오기 - File 클래스 이용
	File f = new File(imgPath);
	String [] fileList = f.list();
		
	//파일이름을 List에 저장하고 List를 request에 저장
	List<String> list = new ArrayList<String>();
	for(String imsi : fileList) {
		list.add(imsi);
	}
	request.setAttribute("list", list);
		
}

 

5. HomeController 클래스에 클라이언트의 요청이 오면 ServiceImpl 메소드를 호출하는 메소드를 작성

    => fileview를 GET방식으로 요청하면 ItemServiceImpl 클래스의 fileview()를 호출

@Controller
public class HomeController {
	@Autowired
	private ItemService itemService;
	
	@RequestMapping(value = "fileview", method = RequestMethod.GET)
	public String fileview(HttpServletRequest request, HttpServletResponse response) {
		itemService.fileview(request, response);
		return "fileview";
	}
}

 

 

6. views 디렉토리에 fileview.jsp 파일을 만들고 작성

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!-- if 나 forEach를 사용하기 위한 태그 라이브러리 설정 -->
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>    
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>파일 목록</title>
</head>
<body>
	<div align="center" class="body">
		<h2>파일 목록</h2>
		<table border="1">
			<c:forEach var="item" items="${list}">
				<tr>
					<td><a href="${pageContext.request.contextPath}/img/${item}">${item}</a></td>
				</tr>	
			</c:forEach>
		</table>
	</div>
</body>
</html>

 

7. 디렉토리에 있는 파일이 이미지 파일이라 다운로드 되지 않고 브라우저에 출력

    => 파일이 텍스트를 제외한 문서, 이미지, 사운드, 비디오 파일이 아니라면 여기까지만 구현해도 다운로드 가능

 

**모든 파일이 다운로드 가능하도록 수정

1. fileview.jsp 파일에서 링크를 수정

<!--download 라는 요청에 filename 이라는 파라미터로 파일 이름을 넘겨주도록 요청을 생성  -->
<td><a href="download?filename=${item}">${item}</a></td>

 

2. HomeController 클래스에 download 요청을 처리할 메소드를 생성

//파일 다운로드 뷰를 출력하는 요청
@RequestMapping(value="download", method=RequestMethod.GET)
public String download(HttpServletRequest request, HttpServletResponse response, Model model) {	
	//아래 문장이 출력되지 않으면 View에서 요청과 Controller 처리 메소드의
	//RequestMapping이 맞지 않는 것입니다.
	//System.out.println("Controller");
		
	//파라미터 읽기
	String filename = request.getParameter("filename");
		
	//이 줄에서 null 이 리턴되면 ? 뒤에 이름과 위의 이름이 안맞는 것입니다.
	//System.out.println("파라미터:" + filename);
		
	//데이터를 저장
	model.addAttribute("filename", filename);
		
	return "download";
}

 

3. 다운로드를 처리할 View 클래스 생성 : kr.co.pk.view.DownloadView

    => Controller에서 리턴하는 데이터를 읽어서 다운로드 받을 파일의 절대경로로 설정하는 부분만 수정하여 사용

package kr.co.pk.view;

import java.io.File;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.Map;

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

import org.springframework.util.FileCopyUtils;
import org.springframework.web.servlet.view.AbstractView;

public class DownloadView extends AbstractView{
	public DownloadView() {
		//형식을 설정
		setContentType("application/download; charset=utf-8");
	}
	
	//출력할 뷰를 그리는 메소드
	//model은 Controller에서 전달한 데이터
	//request는 클라이언트의 요청 정보를 저장
	//response는 클라이언트에게 응답할 정보를 저장
	@Override
	protected void renderMergedOutputModel(Map<String, Object> model,
			HttpServletRequest request, HttpServletResponse response) throws Exception{
		//파일의 절대 경로 만들기
		//절대경로는 운영체제에 따라 디렉토리 표현방법이 다름
		//windows는 \\ 그 외 운영체제는 /
		//상대경로는 무조건 /
		String filename = (String) model.get("filename");
		String filePath = request.getServletContext().getRealPath("/img") + "/" + filename;
		
		File file = new File(filePath);
		
		//파일의 형식과 크기를 설정
		response.setContentType(getContentType());
		response.setContentLength((int)file.length());
		
		//브라우저 종류를 판단
		//파일명에 한글이 있을 경우 인코딩 방식 때문에 판단
		String userAgent = request.getHeader("User-Agent");
		//ie에서 접속시 userAgent에 rv라는 문자열이 포함되어 있음
		boolean ie = userAgent.indexOf("rv") > -1;
		
		//파일이름 설정
		 String fileName = null;
		 if(ie) {
			 filename = URLEncoder.encode(file.getName(), "utf-8");
		 }else {
			 filename = new String(file.getName().getBytes("utf-8"), "iso-8859-1");
		 }
		 response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\";");
		
		 //파일 다운로드 받는 로직
		 response.setHeader("Content-Transfer-Encoding", "binary");
		 OutputStream out = response.getOutputStream();
		 FileInputStream fis = null;
		 try {
			 fis = new FileInputStream(file);
			 FileCopyUtils.copy(fis, out);
		 }finally {
			if(fis != null) {
				try {
					fis.close();
				}catch (Exception e) {
					System.out.println(e.getMessage());
					e.printStackTrace();
				}
			}
		}
		 out.flush();
	}
}

 

4. servlet-context.xml 파일의 ViewResolver 수정

    => jsp가 아닌 출력이 있으면 이 설정은 추가를 해 주어야 함

        - 기존 ViewResolver의 order를 1로 설정하고 BeanNameViewResolver 빈을 추가하고 order를 0으로 설정

  1) 기존 Viewresolver의 order를 추가

<!-- Controller에서 forwarding 하는 View 이름을 넘겨주었을 때 실제 사용할 View를 결정하는 설정 -->
<!-- Controller가 리턴판 뷰 이름을 가지고 실제 출력할 뷰를 설정하는 뷰 리졸버 jstlview(jsp)로 출력 -->
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	<beans:property name="prefix" value="/WEB-INF/views/" />
	<beans:property name="suffix" value=".jsp" />
	<beans:property name="order" value="1" />
</beans:bean>

 

  2) 새로운 ViewResolver 추가

<!-- BeanNameViewResolver 설정 -->
<beans:bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
	<beans:property name="order" value="0" />
</beans:bean>

 

  3) Controller에서 download 뷰를 리턴하면 추가한 View 클래스로 출력하도록 설정을 추가

<!-- download 출력설정 -->
<beans:bean id ="download" class="kr.co.pk.view.DownloadView" />

 

**Excel 출력

1. excel 출력을 위한 의존성 설정 - apache poi

<!-- Excel 출력을 위한 의존성 -->
<dependency>
	<groupId>org.apache.poi</groupId>
	<artifactId>poi</artifactId>
	<version>3.16</version>
</dependency>

 

2. home.jsp 파일에 엑셀 출력을 위한 요청을 생성

<li><a href="item.xls" class="menu">엑셀 출력</a></li>

 

3. HomeController 클래스에 item.xls 요청을 처리하는 메소드를 생성하여 작성

 

 

4. Excel로 출력할 수 있는 View 클래스를 생성

    => AbstractExcelView 로 부터 상속 받는 클래스 : kr.co.pk.view.excel

package kr.co.pk.view;

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

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

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.web.servlet.view.document.AbstractXlsView;

import kr.co.pk.item.domain.Item;

public class ExcelView extends AbstractXlsView {

	//workbook은 출력할 엑셀 파일
	@Override
	protected void buildExcelDocument(Map<String, Object> model, Workbook workbook, 
			HttpServletRequest request, HttpServletResponse response) throws Exception {
		//출력할 데이터 가져오기
		List<Item> list = (List<Item>)model.get("list");
		
		//시트를 생성
		Sheet sheet = workbook.createSheet("ITEM");
		//열 너비 설정
		sheet.setColumnWidth(1, 256*20);
		sheet.setColumnWidth(2, 256*40);
		sheet.setColumnWidth(3, 256*20);
		
		//제목셀 생성
		
		//0번행 생성
		Row firstRow = sheet.createRow(0);
		
		//셀 생성
		Cell cell = firstRow.createCell(0);
		cell.setCellValue("상품명");
		cell = firstRow.createCell(1);
		cell.setCellValue("상품설명");
		cell = firstRow.createCell(2);
		cell.setCellValue("가격");
		
		//데이터 출력
		
		//행번호를 저장할 변수
		int rowNum = 1;
		for(Item item : list) {
			//행을 생성
			Row row = sheet.createRow(rowNum++);
			//셀을 생성하여 출력
			Cell c = row.createCell(0);
			c.setCellValue(item.getItemname());
			
			c = row.createCell(1);
			c.setCellValue(item.getDescription());
			
			c = row.createCell(2);
			c.setCellValue(item.getPrice() + "원");
		}
	}

}

 

5. servlet-context.xml 파일에 Controller에서 리턴한 뷰 이름과 Excel 뷰를 연결

<!-- Excel 출력설정 -->
<beans:bean id ="excel" class="kr.co.pk.view.ExcelView" />

 

**PDF 출력

1. 프로젝트 안에 한글을 춝\력할 수 있는 폰트를 복사

    => webapp/font/malgun.ttf

 

2. pom.xml 파일에 iText API 라이브러리의 의존성을 설정

<!-- PDF 출력을 위한 의존성 -->
<dependency>
	<groupId>com.lowagie</groupId>
	<artifactId>itext</artifactId>
	<version>2.1.7</version>
</dependency>

 

3. home.jsp 파일에 pdf 요청을 위한 링크를 생성

<li><a href="item.pdf" class="menu">PDF 출력</a></li>

 

4. homeController 클래스에 item.pdf 요청을 처리하는 메소드를 생성

//PDF로 출력하는 요청을 처리하는 메소드
@RequestMapping(value = "item.pdf", method = RequestMethod.GET)
public String pdf(
		HttpServletRequest request, HttpServletResponse response, Model model) {
	//ITEM 테이블의 데이터를 전부 읽어오는 서비스를 호출
	itemService.allitem(request, response);
	
	//Model에 데이터 저장
	model.addAttribute("list", request.getAttribute("list"));
	
	//출력할 뷰를 결정
	return "pdf";
}

 

5. PDF로 출력할 View 클래스를 생성 - AbstractPdfView 클래스로부터 상속

    => kr.co.pk.view.PdfView

package kr.co.pk.view;

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

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


import org.springframework.web.servlet.view.document.AbstractPdfView;

import com.lowagie.text.Cell;
import com.lowagie.text.Document;
import com.lowagie.text.Font;
import com.lowagie.text.Paragraph;
import com.lowagie.text.Table;
import com.lowagie.text.pdf.BaseFont;
import com.lowagie.text.pdf.PdfWriter;

import kr.co.pk.item.domain.Item;

public class PdfView extends AbstractPdfView {

	@Override
	protected void buildPdfDocument(Map<String, Object> model, Document document, PdfWriter writer,
			HttpServletRequest request, HttpServletResponse response) throws Exception {
		//데이터 가져오기
		List<Item> list = (List<Item>)model.get("list");

		//테이블을 생성 - 앞의 숫자는 열의 수, 뒤의 숫자는 행의 수
		Table table = new Table(3, list.size()+1);
		//패딩 설정
		table.setPadding(5);
		
		//폰트 생성 - 이 작업을 하지 않으면 한글 출력이 안됨
		BaseFont bfKorean = BaseFont.createFont(request.getServletContext().getRealPath("/font")
				+ "/malgun.ttf", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
		Font font = new Font(bfKorean);
		
		Cell cell = new Cell(new Paragraph("상품명", font));
		cell.setHeader(true);
		table.addCell(cell);
		
		cell = new Cell(new Paragraph("상품설명", font));
		cell.setHeader(true);
		table.addCell(cell);
		
		cell = new Cell(new Paragraph("가격", font));
		cell.setHeader(true);
		table.addCell(cell);
		
		table.endHeaders();
		
		//데이터 출력
		for(Item item : list) {
			Cell imsi = new Cell(new Paragraph(item.getItemname(), font));
			table.addCell(imsi);
			
			imsi = new Cell(new Paragraph(item.getDescription(), font));
			table.addCell(imsi);
			
			imsi = new Cell(new Paragraph(item.getPrice() + "원", font));
			table.addCell(imsi);
		}
		//테이블을 문서에 추가
		document.add(table);
	}

}

 

6. servlet-context.xml 파일에 Controller에서 리턴한 뷰 이름과 뷰 클래스를 매핑

<!-- PDF 출력설정 -->
<beans:bean id ="pdf" class="kr.co.pk.view.PdfView" />

 

**JSON 출력

1. pom.xml 파일에 jackson-databind 라이브러리의 의존성을 설정

<!-- JSON 출력을 위한 라이브러리 -->
<dependency>
	<groupId>com.fasterxml.jackson.core</groupId>
	<artifactId>jackson-databind</artifactId>
	<version>2.9.7</version>
</dependency>

 

2. home.jsp 파일에 json 요청을 위한 링크를 생성

<li><a href="item.json" class="menu">ITEM JSON 출력</a></li>

 

3. HomeController 클래스에 item.json 요청을 처리하는 메소드 생성

// JSON로 출력을 위한 메소드
@RequestMapping(value = "item.json", method = RequestMethod.GET)
public String json(HttpServletRequest request, HttpServletResponse response, Model model) {
	// ITEM 테이블의 데이터를 전부 읽어오는 서비스를 호출
	itemService.allitem(request, response);
	// Model에 데이터 저장
	model.addAttribute("list", request.getAttribute("list"));
	// 출력할 뷰를 결정
	return "itemlist";
}

 

4. itemlist와 jsonview를 연결해주는 설정을 servlet-context.xml 파일에 추가

<!-- PDF 출력설정 -->
<beans:bean id ="itemlist" 
class="org.springframework.web.servlet.view.json.MappingJackson2JsonView" />

 

============================================================================

20.07.09

**CSV 형식의 문자열을 출력

1. JSON 출력을 위한 Controller 클래스를 추가

    => kr.co.pk.controller.JSONController

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

import kr.co.pk.item.service.ItemService;

@RestController
public class JSONController {
	@Autowired
	private ItemService itemService;
	
	//문자열을 출력하는 요청 처리
	@RequestMapping(value ="dataformat.csv", method = RequestMethod.GET)
	public String csv() {
		String result = "dataformat:csv,mxl,json";
		
		return result;
	}
}

 

2. home.jsp 파일에 위의 요청을 호출하는 LINK를 추가

<li><a href="dataformat.csv" class="menu">문자열 출력</a></li>

 

**json 출력

1. 앞에서 만든 Controller 클래스에 요청 처리 메소드를 추가 

//JSON 출력을 위한 메소드
@RequestMapping(value ="dataformat.json", method = RequestMethod.GET)
public Map<String, Object> json() {
	List<String> list = new ArrayList<String>();
	list.add("csv: 구분자로 구분된 텍스트");
	list.add("xml: 태그로 데이터를 표현");
	list.add("json: 자바 스크립트의 데이터 표현법을 이용해서 데이터를 표현");
	
	Map<String, Object> result = new HashedMap<String, Object>();
	result.put("dataformat", list);
		
	return result;
}

 

2. home.jsp 파일에 위의 요청을 호출하는 LINK를 추가

<li><a href="dataformat.csv" class="menu">문자열 출력</a></li>

 

 

**ajax를 이용한 json출력

1. 앞에서 만든 Controller 클래스에 요청 처리 메소드를 추가 

//ajax 출력을 위한 메소드
@RequestMapping(value ="ajax.json", method = RequestMethod.GET)
public Map<String, Object> ajaxjson() {
	//테이블 형식의 데이터 구조
	//Map 대신에 DTO를 사용해도 됨
	List<Map> list = new ArrayList<Map>();
	
	Map<String, Object> map = new HashedMap<String, Object>();
	map.put("Encapsulation", 
			"캡슐화 - 불필요한 부분은 숨기고 필요한 부분만 노출하여 사용하는 것으로 정보은닉");
	list.add(map);
	
	map = new HashedMap<String, Object>();
	map.put("Inheritance", 
			"상속: 하위클래스가 상위클래스의 모든 것을 물려받는 ㄱ서으로 재사용성이 증가하고"
			+ "유지보수가 편리해짐 직접 만든 클래스의 경우는 대부분의 경우 클래스를 여러개"
			+ "만들다가 동일한 부분이 있어 상위클래스를 생성 - 이런 이유때문에 다이어그램"
			+ "에서는 하위클래스에서 상위클래스로 화살표가 만들어짐 - is a 관계 또는 "
			+ "generalization 이라고 함");
	list.add(map);
	
	map = new HashedMap<String, Object>();
	map.put("Polymorphism", 
			"다형성: 동일한 메시지에 대하여 반응하는 성질로 프로그래밍에서는 동일한 코드가"
			+ "어떤 인스턴스가 대입되었느냐에 따라 다른 클래스의 메소드를 호출하는 것"
			+ "- 인터페이스나 추상 클래스에 메소드를 선언하고 다른 클래스들이 인터페이스나"
			+ "추상 클래스를 상속받아서 메소드를 재정의 하는 방식으로 작성"
			+ "- Inheritance 와 Method Overriding을 이용하여 구현");
	list.add(map);
	
	Map<String, Object> result = new HashedMap<String, Object>();
	result.put("oop", list);
	
	return result;
}

 

2. home.jsp 파일에 위의 요청을 ajax로 처리하는 코드 작성

    => Map(List(Map)) : 객체(배열(객체))

  1) ajax 요청을 위한 DOM을 생성

<li><a href="#" class="menu" id="ajaxbtn">ajax 요청</a></li>

 

  2) 스크립트 코드를 작성

	<div id="disp"></div>
<script>
	document.getElementById("ajaxbtn").addEventListener("click", function(event){
		//ajax 객체를 생성
		var request = new XMLHttpRequest();
		//요청할 URL 생성
		request.open('get', 'ajax.json');
		//전송
		request.send('');
		//데이터를 가져오면 호출될 함수를 설정
		request.addEventListener('load', function(data){
			//데이터 확인 - 404와 관련된 HTML 에러가 발생하면 open에 작성한 URL과 
			//Controller의 RequestMapping에 작성한 URL을 비교
			//alert(data.target.responseText)		//xml이면 responseXML
			
			//JSONParsing - 언어에 따라 파싱하는 방법이 다름
			var result = JSON.parse(data.target.responseText);
			var list = result.oop;
			//alert(list);
			document.getElementById("disp").innerHTML += 
				list[0].Encapsulation + "<br/>";
			document.getElementById("disp").innerHTML += 
				list[1].Inheritance + "<br/>";
			document.getElementById("disp").innerHTML += 
				list[2].Polymorphism + "<br/>";
		})
	})
</script>

 

**ItemServiceImpl의 Allitem 메소드의 결과를 XML로 출력

1. XML 출력을 위한 의존성을 pom.xml에 작성

<!-- XML 출력을 위한 라이브러리 -->
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-oxm</artifactId>
	<version>${org.springframework-version}</version>
</dependency>
		
<dependency>
	<groupId>javax.xml.bind</groupId>
	<artifactId>jaxb-api</artifactId>
	<version>2.1</version>
</dependency>
		
<dependency>
	<groupId>com.sun.xml.bind</groupId>
	<artifactId>jaxb-impl</artifactId>
	<version>2.2.11</version>
</dependency>

<dependency>
	<groupId>com.sun.xml.bind</groupId>
	<artifactId>jaxb-core</artifactId>
	<version>2.2.11</version>
</dependency>

 

2. home.jsp 파일에  XML요청을 생성

<li><a href="item.xml" class="menu">XML 요청</a></li>

 

3. DTO 클래스를 수정 - Item

@XmlAccessorType(XmlAccessType.FIELD)
//출력할 속성과 순서를 설정
@XmlType(name = "", propOrder= {"itemid", "itemname", "price", 
		"description", "pictureurl"})
@Data
public class Item {
	//숫자 데이터의 경우 null 이 될 가능성이 있는 경우는 Wrapper 클래스 사용
	private Integer itemid;
	private String itemname;
	private Integer price;
	private String description;
	private String pictureurl;
	
}

 

4. DTO 클래스의 List를 속성으로 갖는 ItemReport.java 파일 생성

@XmlAccessorType(XmlAccessType.FIELD)
//전체 태그를 설정
@XmlRootElement(name="ITEMLIST")
public class ItemReport {
	//DTO 1개만 출력될 태그 설정
	@XmlElement(name="ITEM")
	private List<Item> list;
	
	private List<Item> getList(){
		return list;
	}
	public void setList(List<Item> list) {
		this.list = list;
	}

}

 

5. HomeController에 xml 요청을 처리하는 메소드를 생성

// xml 출력을 위한 메소드
@RequestMapping(value = "item.xml", method = RequestMethod.GET)
public String xml(HttpServletRequest request, HttpServletResponse response, Model model) {
	// ITEM 테이블의 데이터를 전부 읽어오는 서비스를 호출
	itemService.allitem(request, response);
	List<Item> list = (List<Item>)request.getAttribute("list");
	ItemReport itemReport = new ItemReport();
	itemReport.setList(list);
		
	//Model에 데이터 저장
	model.addAttribute("list", itemReport);
		
	// 출력할 뷰를 결정
	return "itemxml";
}

 

6. Servlet-context.xml 파일에 Controller에서 리턴한 뷰 이름을 XML로 출력하도록 설정

<!-- XML 출력설정 -->
<beans:bean id ="marshaller" 
class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
	<beans:property name="classesToBeBound">
		<beans:list>
			<beans:value>kr.co.pk.item.domain.ItemReport</beans:value>
		</beans:list>
	</beans:property>
</beans:bean>
	
<beans:bean id="itemxml" 
class="org.springframework.web.servlet.view.xml.MarshallingView">
	<beans:property name="marshaller" ref="marshaller" />
	<beans:property name="modelKey" value="list" />	
</beans:bean>

 

**예외 발생시키기

1. home.jsp 파일에 링크 추가

<li><a href="exception" class="menu">예외처리</a></li>

 

2. HomeController에 exception 요청이 왔을 때 처리할 메소드를 생성

// exception요청이  get방식으로 온경우 처리하기 위한 메소드
@RequestMapping(value = "exception", method = RequestMethod.GET)
public String exception(HttpServletRequest request, HttpServletResponse response, Model model) {
	// 출력할 뷰를 결정
	return "exception";
}

 

3. exception.jsp 파일을 만들고 작성

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>예외처리</title>
</head>
<body>
	<form method="post">
		나누어지는 수<input type="text" name="num1" /><br/>
		나누는 수 <input type="text" name="num2" /><br/>
		<input type="submit" value="전송" />
	</form>
</body>
</html>

 

4. HomeController에 위의 페이지에서 전송을 눌렀을 때 처리할 메소드를 생성

// exception요청이  post방식으로 온경우 처리하기 위한 메소드
@RequestMapping(value = "exception", method = RequestMethod.POST)
public String exceptionprocess(HttpServletRequest request, HttpServletResponse response, Model model) {
	//파라미터 읽기
	String num1 = request.getParameter("num1");
	String num2 = request.getParameter("num2");
	
	//파라미터 가지고 작업
	int result = Integer.parseInt(num1) / Integer.parseInt(num2);
	
	//결과 저장
	model.addAttribute("result", result);
	
	//결과 출력 페이지 설정
	return "result";
}

 

5. 결과를 출력할 result.jsp 파일을 만들고 내용을 출력

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>결과 출력</title>
</head>
<body>
${result}
</body>
</html>

 

6. 숫자가 아닌 데이터나 두번째 입력란에 0을 대입하면 예외가 발생

    => 실제 프로젝트라면 전송을 누를때 유효성 검사를 하여 예외가 발생하지 않도록 해야 함

    => 예외가 발생했을 때 WAS는 예외 메시지와 printStackTrace를 출력하는데 유저는 이 메시지를 볼 필요가 없음

        - 개발자는 이 메시지를 보고 예외를 수정, 사용자에게는 다른 형태의 예외 메시지 출력

 

**에러페이지 출력 설정

1. views 디렉토리에 error라는 디렉토리를 생성하고, exception.jsp 파일 생성

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
 <!-- exception 객체를 사용할 수 있는 페이지 -->
 <%@ page isErrorPage = "true" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>예외 페이지</title>
</head>
<body>
	ㅅㅂㅇ ㅈㅇㅄ<br/>
	빠른 시일 내로 고칠게요<br/>
	예외내용:${result }
</body>
</html>

 

2. HomeController 클래스에 예외가 발생하면 error/exception.jsp 파일을 출력하도록 하는 설정을 추가

//예외가 발생하면 처리하는 메소드
@ExceptionHandler(Exception.class)
public String handleException(Exception e, Model model) {
	model.addAttribute("result", e.getMessage());
	return "error/exception";
}

    => 별도의 클래스를 만들어서 클래스 이름 상단에 @ControllerAdvice("패키지명") 추가후 메소드를 만들면 패키지에서 발생한 예외를 처리

 

**Spring MVC View 설정

    => 단순한 페이지 이동은 Controller에 처리하는 메소드를 만들지 않고 servlet-context.xml 파일에 아래 태그를 추가

<view-controller path="요청경로" view-name="뷰이름" />

    => Controller URL패턴을 '/*', '/' 설정시 상대경로인 모든요청을 Controller가 처리, 처리하지 못하면 404에러 발생

        - 위의 경우 404에러를 방지하려면 servlet-context.xml 파일에 <default-servlet-handler/>를 추가해야 함

        - 해당 설정 추가시 Controller가 처리하지 못하는 요청은 WAS가 처리해달라고 요청

 

**Spring 메시지 출력

    => Spring에서 properties 파일의 내용을 출력하고자 할 때는 아래 설정을 추가

<bean class="org.springframework.context.support.ResourceBundleMessageSource"
	id="messageSource">
    <property name="basenames">
    	<list>
        	<value>메시지 파일 경로</value>
            ...
        </list>
    </property>
</bean>

    => 지역화(브라우저 언어 설정에 따라 다른 문자열 출력) 설정

        - 메시지 파일이름_언어코드.properties로 만들면 됨

    => 프로퍼티 파일을 작성할 때는 key = value 형식으로 설정

    => value는 방드시 인코딩이 되어야 함

        - IDE의 Workspace 설정에서 text file encoding을 UTF-8로 설정했으면 자동으로 인코딩 됨

    => jsp 파일에서 메시지 출력

<%@ taglib prefix="spring" uri= http://www.springframework.org/tags" %>를 추가하고
<spring:message code="key" var="변수명" />

    => key값을 출력하고 변수에 저장 - var는 생략 가능

 

**메시지 출력과 지역화

1. home.jsp 파일에 링크를 추가

<li><a href="validation" class="menu">유효성 검사</a></li>

 

2. 메시지를 저장할 파일을 src/main/resources 디렉토리에 message 디렉토리를 만들고 label.properties로 생성

email=email
password=password
loginform.help=email/password root/1234
loginform.title=Login Form

 

3. 프로퍼티 파일을 사용하기 위한 설정을 servlet-context.xml 파일에 추가

<!-- 메시지 파일의 경로 설정 -->
<beans:bean id="messageSource" 
class="org.springframework.context.support.ResourceBundleMessageSource">
	<beans:property name="basenames">
		<beans:list>
			<beans:value>message/label</beans:value>
		</beans:list>
	</beans:property>
</beans:bean>

 

4. HomeController에 validation 요청이 get방식으로 오면 처리하는 메소드 생성

@RequestMapping(value = "validation", method = RequestMethod.GET)
public String validation() {		
	//결과 출력 페이지 설정
	return "validation";
}

    => 위 메소드 대신에 servlet-context.xml 파일에 아래 설정을 추가해도 됨

<!-- validation 요청이 오면 view name을  validation로 설정 -->
<view-controller path="validation" view-name="validation"/>

 

5. 메시지를 출력할 validation.jsp 파일을 view 디렉토리에 생성하고 작성

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!-- spring 태그를 사용하기 위한 설정 -->    
<%@ taglib prefix="spring"
uri="http://www.springframework.org/tags" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title><spring:message code="loginform.title" /></title>
</head>
<body>
	<form method="post">
		<spring:message code="email" />
		<input type="email" name="email" id="email"/><br/>
		<spring:message code="password" />
		<input type="password" name="password" id="password"/><br/>
		<p><spring:message code="loginform.help"/></p>
		<input type="submit" value="로그인"/>
	</form>
</body>
</html>

 

6. 기존에 만든 프로퍼티 파일의 이름을 label_en.properties로 변경

 

7. label_ko.properties 파일을 만들고 동일한 이름의 key에 value를 변경

email=\uC774\uBA54\uC77C
password=\uD328\uC2A4\uC6CC\uB4DC
loginform.help=email/password root/1234
loginform.title=\uB85C\uADF8\uC778 \uD3FC

 

**Spring Form 관련 Custom Tag

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>

    => 유효성 검사를 서버에서 한경우 이전 입력 값을 출력하는 부분이나 메시지 출력이 쉬워짐

 

1. <form:form>

    => action을 설정하지 않으면 이 페이지에 오게 만든 요청

    => method를 생략하면 post

    => id는 입력 폼의 값을 저장하는 Command 객체의 이름이 할당

 

2. <form:input> : input 객체 생성 

3. <form:password> : password 객체 생성

4. <form:hidden> : hidden 객체 생성

    => path 속성에 DTO의 프로퍼티 명을 기재시 name과 id를 프로퍼티 명으로 설정

    => 데이터가 넘어왔다면 그 데이터를 value에 설정

<form:form commandName="member">
	<form:input path="email" />
</form:form>

위처럼 작성시 아래처럼 변경됨

<form method="post">
	<input type="text" name="email" id="email" value="${member.email}" />
</form>

 

5. <form:select>

    => item 속성에 List 타입의 속성을 대입하면 자동으로 option을 생성

 

6. <checkboxes>, <form:radiobuttons>

    => item 속성에 List 타입의 속성을 대입하면 자동으로 생성

 

7. <form:textarea>

    => path만 설정

 

8. <form:errors path="프로퍼티 이름">

    => 프로퍼티 명으로 만들어진 에러메시지를 출력

 

**Spring Form 관련 태그 사용 실습

1. email, password, logintype을 String으로 갖는 DTO 클래스 작성

 

package kr.co.pk.item.domain;

public class Member {
	private String email;
	private String password;
	private String loginType;
	
	public Member(String email, String password, String loginType) {
		super();
		this.email = email;
		this.password = password;
		this.loginType = loginType;
	}
	public Member() {
		super();
		// TODO Auto-generated constructor stub
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String getLoginType() {
		return loginType;
	}
	public void setLoginType(String loginType) {
		this.loginType = loginType;
	}
	
	@Override
	public String toString() {
		return "Member [email=" + email + ", password=" + password + ", loginType=" + loginType + "]";
	}
	
	
}

 

2. HomeController에 loginType에 관련된 데이터를 만들어주는 메소드를 생성

@ModelAttribute("loginTypes")
public List<String> loginTypes(){
	List<String> list = new ArrayList<String>();
	list.add("개인회원");
	list.add("기업회원");
	list.add("비회원");
	return list;
}

 

3. validation 요청을 처리하는 메소드를 수정

@RequestMapping(value = "validation", method = RequestMethod.GET)
public String validation(@ModelAttribute("member") Member member) {		
	//결과 출력 페이지 설정
	return "validation";
}

 

4. validateion.jsp 파일을 수정

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!-- spring 태그를 사용하기 위한 설정 -->    
<%@ taglib prefix="spring"
uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="form"
uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title><spring:message code="loginform.title" /></title>
</head>
<body>
	<form:form modelAttribute="member">
		<spring:message code="email" />
		<form:input path="email"/><br/>
		<spring:message code="password" />
		<form:password path="password"/><br/>
		<form:select path="loginType" items="${loginTypes}"/>
		<p><spring:message code="loginform.help"/></p>
		<input type="submit" value="로그인"/>
	</form:form>
</body>
</html>

 

============================================================================

20.07.10

** 파일 업로드 실습

1. pom.xml 파일에 파일 업로드 라이브러리 의존성 작성

<!-- 파일 업로드 라이브러리 -->
<dependency>
	<groupId>commons-fileupload</groupId>
	<artifactId>commons-fileupload</artifactId>
	<version>1.3.1</version>
</dependency>

 

2. home.jsp 파일에 링크 추가

<li><a href="fileupload" class="menu">파일 업로드</a></li>

 

3.HomeController 클래스에 fileupload 요청이 GET방시긍로 전송되었을 때 처리하는 메소드를 작성

@RequestMapping(value = "fileupload", method = RequestMethod.GET)
public String fileupload() {		
	//결과 출력 페이지 설정
	return "fileupload";
}

 

4. fileupload.jsp 파일을 views 디렉토리에 생성하고 작성

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>파일 업로드</title>
</head>
<body>
	<form action="requestupload" method="post" encrype="multipart/form-data">
		이름<input type="text" name="number" /><br /> 
		파일<input type="file" name="report" /><br /> 
		<input type="submit" value="제출" />
	</form>
	
	<form action="requestparamupload" method="post" encrype="multipart/form-data">
		이름<input type="text" name="number" /><br />
		파일<input type="file" name="report" /><br /> 
		<input type="submit" value="제출" />
	</form>
	
	<form action="commandupload" method="post" encrype="multipart/form-data">
		이름<input type="text" name="number" /><br />
		파일<input type="file" name="report" /><br /> 
		<input type="submit" value="제출" />
	</form>
</body>
</html>

 

5. web.xml 파일에 파라미터 인코딩 필터 설정을 확인하고 없으면 추가

<filter>
	<filter-name>encodingFilter</filter-name>
	<filter-class>org.springframework.web.filter.CharacterEncodingFilter
	</filter-class>
	<init-param>
		<param-name>encoding</param-name>
		<param-value>UTF-8</param-value>
	</init-param>
</filter>
<filter-mapping>
	<filter-name>encodingFilter</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

 

6. Servlet-context.xml 파일에 MultipartResolver 클래스의 bean을 작성

<beans:bean id="multipartResolver" 
	class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
</beans:bean>

 

7. HomeController 클래스에 첫번째 폼의 전송요청을 처리하는 메소드를 작성

@RequestMapping(value = "requestupload", method = RequestMethod.GET)
public String upload(MultipartHttpServletRequest request) {		
	//파라미터 읽어오기
	String number = request.getParameter("number");
	MultipartFile report = request.getFile("report");
	
	//출력
	System.out.println("number : " + number);
	System.out.println("filename : " + report.getOriginalFilename());
	return "fileupload";
}

 

 

 

9. Command 객체 이용

  1) 파라미터를 저장할 

package kr.co.pk.item.domain;

import org.springframework.web.multipart.MultipartFile;

public class Command {
	private String number;
	private MultipartFile Report;
	public Command(String number, MultipartFile report) {
		super();
		this.number = number;
		Report = report;
	}
	public Command() {
		super();
		// TODO Auto-generated constructor stub
	}
	public String getNumber() {
		return number;
	}
	public void setNumber(String number) {
		this.number = number;
	}
	public MultipartFile getReport() {
		return Report;
	}
	public void setReport(MultipartFile report) {
		Report = report;
	}
	@Override
	public String toString() {
		return "Command [number=" + number + ", Report=" + Report + "]";
	}
}

 

  2) HomeController 클래스에 세번째 폼 처리 메소드를 생성

@RequestMapping(value = "commandupload", method = RequestMethod.POST)
public String upload(Command command) {
	
	//출력
	System.out.println("number : " + command.getNumber());
	System.out.println("filename : " + command.getReport().getOriginalFilename());
	return "redirect:/";
}