본문 바로가기

수업 정리

53일차 수업정리(DB 연동 + 54일차 + 55일차)

**데이터 베이스 연동 준비

    => 데이터베이스에 접속

    => 프로그래밍 언어와 연동에 필요한 드라이버 준비

1. Dynamic Web Project 생성

    => 프로젝트 설정 파일인 web.xml파일이 포함되도록 생성

    => servlet-api.jar 파일과 jstl.jar 파일을 WebContent/WEB-INF/lib 디렉토리에 복사

        - servlet-api.jar : JDK SE 버전을 설치한 상태에서 HttpServlet 클래스를 이용하기 위해

        - jstl.jar : jsp 페이지에서 if 나 for를 java 코드를 이용하지 않고 사용하기 위해 

 

2. JDBC

    => Java를 이용하여 데이터베이스에 접속하는 방식

 

3. JDBC를 사용하는 방법

  1) JDK가 제공하는 API를 이용하는 방법

    => Connection, Statement(PreparedStatement, CallableStatement), ResultSet을 이용

  2) 프레임 워크를 이용하는 방법

    => JPA(Hibernate - SI를 제외한 전 분야), MyBatis(공공기관 SI), Spring의 JDBC 프레임워크(공부할 때만)

 

4. 연동방법

  1) 데이터베이스 드라이버를 애플리케이션에 사용할 수 있도록 복사

    => 일반 Application : build path에 복사

    => Web Application : WebContent/WEB-INF/lib에 복사

    => maven : pom.xml에 작성(Spring)

    => gradle : json에 저장(Android)

  2) Driver Class를 로드

    => 한번만 수행 - 일반 Application에서는 하지 않아도 됨

        - Class.forName(String driverClassName)

    => 데이터베이스 종류마다 다름

        - MySQL : com.mysql.jdbc.Driver

        - Oracle : oracle.jdbc.driver.OracleDriver
  3) 데이터베이스 연결

    => 형식 : Connection 연결변수명 = DriverManager.getConnect(String url, String account, String password);

    => url은 데이터베이스 종류마다 다르게 설정

        - MySQL : jdbc:mysql://HOST:PORT/DBNAME

        - Oracle : jdbc:oracle:thin:@HOST:PORT:SID - Oracle 11g까지 기본

                     jdbc:oracle:thin:@HOST:PORT/SERVICENAME - Oracle 12g이후 기본

  4) 데이터베이스 사용

    => Statement를 이용하여 SQL을 실행

    => int나 ResultSet으로 실행 결과를 받아서 사용

  5) 사용한 자원을 반납 - close()

 

5. 샘플 데이터베이스 작성

-- item 테이블
-- code는 정수, 기본키, 자동증가하는 형식
-- title은 고정 문자열(50자 -한글 16자)
-- category는 가변 문자열(50자 -한글16자) - 오라클에서는 varchar2
-- description은 긴 문자열(오라클에서는 clob)
create table Item(
	code int primary key auto_increment,
	title char(50) not null,
	category varchar(50), 
	description text
)engine=innodb default charset=utf8;

-- 샘플데이터 작성
insert into Item values(1, 'Java', 'language','오픈소스 라이브러리가 많은 범용 프로그래밍 언어'); 
insert into Item values(2, 'Eclipse', 'IDE', '프로그래밍을 편리하게 할 수 있도록 해주는 오픈 소스 프로그램'); 
insert into Item values(3, 'Tomcat', 'Web Application Server','apache web server를 이용하는 오픈 소스 프로그램'); 
insert into Item values(4, 'Oracle', 'DataBase', '대기업이나 공공기관이 주로 사용하는 관계형 DBMS'); 
insert into Item values(5, 'MySQL', 'DataBase', '오픈 소스 기반의 관계형 DBMS - Maria DB와 거의 동일'); 
insert into Item values(6, 'MongoDB', 'DataBase', '가장 많이 언급되는 NoSQL');
insert into Item values(7, 'DBeaver', 'DB Tool', '데이터베이스 사용을 쉽게 해주는 프로그램');
insert into Item values(8, 'Spring', 'Framework', '자바 프레임워크');
insert into Item values(9, 'Android', 'Mobile OS', 'OHA 컨소시엄이 만든 Linux 기반의 모바일 운영체제');
insert into Item values(10, 'Android Studio', 'IDE', 'Android 용 프로그램을 개발할 수 있도록 해주는 프로그램');
insert into Item values(11, 'iOS', 'Mobile OS', 'Apple이 만든 unix 기반의 모바일 운영체제');
insert into Item values(12, 'Xcode', 'IDE', 'iOS 및 Mac 용 프로그램을 개발할 수 있도록 해주는 프로그램');

commit;

select * from Item;

 

6. 데이터 베이스 드라이버를 프로젝트에 복사

    => WebContent/WEB-INF/lib 디렉토리에 복사

        - jstl-1.2.jar, mysql-connector-java-5.1.22-bin.jar

 

7. 연동할 DB 테이블을 표현할 DTO 클래스 작성

    => domain.Item

package domain;

public class Item {
	//클라이언트용 프로그램이면 속성 앞의 접근 지정자를 public으로 하고
	//생성자만 만들고 접근자 메소드는 만들지 않아도 됨
	private int code;
	private String title;
	private String category;
	private String description;
	
	public Item() {
		super();
		// TODO Auto-generated constructor stub
	}

	public Item(int code, String title, String category, String description) {
		super();
		this.code = code;
		this.title = title;
		this.category = category;
		this.description = description;
	}

	public int getCode() {
		return code;
	}
	public void setCode(int code) {
		this.code = code;
	}

	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}

	public String getCategory() {
		return category;
	}
	public void setCategory(String category) {
		this.category = category;
	}

	public String getDescription() {
		return description;
	}
	public void setDescription(String description) {
		this.description = description;
	}

	@Override
	public String toString() {
		return "Item [code=" + code + ", title=" + title + ", category=" + category + ", description=" + description
				+ "]";
	}
	
	
}

 

8. Item 테이블과 연동할 DAO 클래스 생성후, 필요 변수와 연결, 해제 메소드 생성 - 서버에서 사용시 싱글톤 패턴 사용

    => DAO

    => Connection, PreparedStatement, ResultSet이 필요

package dao;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;


public class ItemDao {
	//데이터베이스 연동에 필요한 변수
	private Connection con;
	private PreparedStatement pstmt;
	private ResultSet rs;
	
	private ItemDao() {
		//드라이버 클래스 로드
		try {
			Class.forName("");
		}catch (Exception e) {
			System.err.println("드라이버 클래스 로드 실패");
			System.out.println(e.getMessage());
			e.printStackTrace();
		}
	}
	
	private static ItemDao itemDao;
	
	public static ItemDao sharedInstance() {
		if(itemDao == null)
			itemDao = new ItemDao();
		return itemDao;
	}
	
	//연결 메소드와 해제 메소드
	//연결과 해제는 모든 곳에서 사용되는 부분이므로 
	//중복하여 코딩하지 않기 위해 별도의 메소드로 생성
	//이 메소드는 코드의 중복을 회피하기 위해 작성한 메소드이므로
	//private로 생성하여 외부 호출 방지
	private void connect() {
		try {
			con = DriverManager.getConnection(
					"jdbc:mysql://localhost:3306/mysql?useUnicode=true&characterEncoding=utf8",
					"root", "123123");
		}catch (Exception e) {
			System.err.println("Connect 실패");
			System.out.println(e.getMessage());
			e.printStackTrace();
		}
	}
	private void close() {
		try {
			if(rs != null)
				rs.close();
			if(pstmt != null)
				pstmt.close();
			if(con != null)
				con.close();
		}catch (Exception e) {
			System.err.println("Close 실패");
			System.out.println(e.getMessage());
			e.printStackTrace();
		}
	}
	
}

 

9. Service 인터페이스와 ServiceImpl 구현

    => 사용자의 요청마다 호출되는 형태로 템플릿 메소드 패턴을 적용하고 사용자요청 1 : 메소드 1개씩 매핑 권장

  1) Service 인터페이스 생성

    => service.ItemService

package service;

public interface ItemService {

}

  2) ServiceImpl 클래스를 생성

    => service.ItemServiceImpl

    => Service 인터페이스를 implements

    => Dao 클래스를 주입받아야 함

package service;

import dao.ItemDao;

public class ItemServiceImpl implements ItemService {
	private ItemDao itemDao;
	
	private ItemServiceImpl() {
		//Dao 인스턴스를 생성
		itemDao = ItemDao.sharedInstance();
	}
	
	private static ItemService itemService;
	
	public static ItemService sharedInstance() {
		if(itemService == null)
			itemService = new ItemServiceImpl();
		return itemService;
	}
}

 

10. Controller 클래스 생성

    => HttpServlet 으로 부터 상속

    => Service를 주입 받아야 함

    => 싱글톤을 설정하지 않아도 WAS가 싱글톤으로 처리

    => url 패턴을 설정해야 하는데 이전에는 확장자 패턴, 최근에는 디렉토리 패턴에 작업을 기재하는 형태로 작성

        - iteminsert.do 같은 방식이었는데, item/insert 형태로 많이 작성

     => item 디렉토리 패턴을 사용

    => controller.ItemConte

package controller;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import service.ItemService;
import service.ItemServiceImpl;

@WebServlet({ "/", "/item/*" })
public class itemController extends HttpServlet {
	private static final long serialVersionUID = 1L;

	//서비스 인스턴스 참조 변수
	private ItemService itemService;
	
    public itemController() {
        super();

        itemService = ItemServiceImpl.sharedInstance();
    }

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//공통된 부분을 제거한 주소를 만듬
		String contextPath = request.getContextPath();
		String requestURI = request.getRequestURI();
		String command = requestURI.substring(contextPath.length());
		
		//전송방식 저장
		String method = request.getMethod();
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

		doGet(request, response);
	}

}

 

Dao는 Service 안에, Service는 Controller안에 들어가야 함

 

11. 시작 페이지 출력 작업

  1) WebContent 디렉토리에 index.jsp 파일로 생성

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Item CRUD</title>
</head>
<body>
	<h3>Item CRUD 작업</h3>
	
</body>
</html>

 

  2) Controller 클래스의 doGet메소드에서 시작요청을 처리하는 코드 작성

    => 시작 요청이 오면 WebContent/index.jsp로 포워딩하도록 설정

    => 단순 페이지 이동은 포워딩으로 처리

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//공통된 부분을 제거한 주소를 만듬
		String contextPath = request.getContextPath();
		String requestURI = request.getRequestURI();
		String command = requestURI.substring(contextPath.length());
		
		//전송방식 저장
		String method = request.getMethod();
		
		//시작요청이 온 경우 index.jsp페이지로 포워딩
		if(command.equals("/")) {
			RequestDispatcher dispatcher = request.getRequestDispatcher("index.jsp");
			dispatcher.forward(request, response);
		}
	}

  3) 404 에러가 난 경우

    => Controller의 가장 상단에 어노테이션에서 처리하지 않는 요청이 아닌지 확인

    => doGet 메소드의 비교 문장에서 요청하는 처리를 맞게 했는지

    => 포워딩할 주소와 실제 페이지의 위치가 같은지 확인

    => 이클립스와 톰갯의 경우 가끔 html페이지이면 못읽고, jsp 페이지이면 읽어내는 경우가 발생

 

12. 조회작업

    => 전체 데이터 조회를 한다던가 데이터 개수를 파악하는 일들을 할 때 파라미터가 없음

        - 여러 데이터 중에서 하나의 데이터를 선택하여 조회하는 경우 파라미터로 기본키의 값을 주어야 함

        - 조건을 주고 데이터를 조회하는 경우 파라미터로 조건과 값을 맞춰야 함

        - 페이징 처리를 할 때는 현재 페이지 번호와 데이터 개수를 추가로 넘겨주어야 함

        - 조회를 할 때 매개변수가 많은 경우는 4개까지 가능

        - 현재 페이지 번호, 페이지당 데이터 갯수, 검색에 사용할 필드명, 검색에 사용할 값

 

    => 리턴 타입은 4가지

        - Scala Type : 데이터 개수를 찾아오는 경우, ID나 Nickname 존재 여부

        - Map이나 DTO : 기본키를 가지고 데이터를 1개를 조회하는 경우 (상세보기, 로그인)

        - List<Scala> : id목록을 가져온다던가 하는 경우

        - List<Map 이나 DTO> : 기본키가 아닌 항목을 가지고 데이터를 조회하는 경우

 

13. 전체 데이터 조회

  1) 요청을 생성 : index.jsp 파일에 요청을 생성

 

  2) DAO 작업

    => 메소드 모양 : public List<Item> list();

    => sql 작성 : item 테이블의 전체 데이터를 가져오는 SQL

        - 데이터 1개 : select * from item; 

        - 데이터가 2개 이상이면 order by를 이용하여 정렬(기본키 오름차순 출력이 default)

  3) Service 작업

    => Service 인터페이스에 메소드 구현

public interface ItemService {
	//Item 테이블의 모든 데이터를 읽어오는 메소드
	public void list(HttpServletRequest request, HttpServletResponse response);
}
@Override
public void list(HttpServletRequest request, HttpServletResponse response) {
	//1. 파라미터 읽기
	
	//2. 파라미터 변환 작업이나 알고리즘 처리
	
	//3. 호출할 Dao 메소드의 매개변수를 생성
	
	//4. Dao의 메소드를 호출하여 결과를 저장
	List<Item> list = itemDao.list();
	
	//5. Dao 메소드 호출 결과를 View로 전달하기 위해 request나 session에 저장
	// 포워딩 할 경우 : request, 리다이렉트 할 경우 : session
	request.setAttribute("list", list);
}

  4) Controller 작업

    => 요청에 필요한 Service 메소드를 호출하고 그 결과를 확인하여 필요한 View 페이지로 이동하도록 작성

    => 조회는 필요한 메소드를 호출하고 결과 페이지로 포워딩 시키면 됨

    => doGet 메소드에 추가

else if(command.equals("/item/list")) {
		//전체 데이터를 가져오는 서비스 메소드를 호출
		itemService.list(request, response);
		//결과 페이지로 이동
		//현재 요청이 /item/list이므로 ../view/list.jsp이면 WebContent/view/list.jsp가 됨
		RequestDispatcher dispatcher = request.getRequestDispatcher("../view/list.jsp");
		dispatcher.forward(request, response);
	}

  5) View 작업 - Designer가 있는 경우에는 데이터의 모양을 설명하고 이 작업을 동시에 진행

    => WebContent 디렉토리에 view 디렉토리를 생성하고 list.jsp를 만들어서 출력

 

  6) 처리과정

    => index.jsp에서 전체 데이터 조회를 클릭하면 ItemController의 doGet 메소드로 가야함

        - 여기서 출력이 안되면 URL을 확인

    => ItemController에서 ItemServiceImpl의 메소드를 호출

        - 여기서 출려깅 안되면 메소드를 잘못 호출

    => ItemServiceImpl 클래스에서 ItemDao의 메소드를 호출

        - 여기서 출력이 안되면 메소드를 잘못 호출

    => ItemDao 클래스의 마지막에서 리턴할 데이터를 출력

        - 여기서 출력이 잘못되었을 경우 sql과 매개변수를 확인

        - 매개변수가 잘못된 경우 ItemServiceImpl에서 잘못 넘겨주었을 수도 있음

 

    => ItemServiceImpl에서 ItemDao 메소드의 매개변수를 출력

    => ItemController의 doGet 메소드로 와서 결과페이지 이름과 출력페이지 이름이 정확한지 확인

        - 이 부분이 잘못되면 404에러가 나던지 화면에 아무것도 보이지 않음

    => list.jsp 파일을 다시 확인

        - ItemServiceImpl에서 저장한 attribute 이름과 출력 내용이 일치하는지 확인

 

13. 데이터 삽입

    => 과정 : 삽입 요청 -> 입력 페이지로 이동 -> 삽입요청 -> 실제 데이터 삽입요청 -> Controller -> 입력페이지로 이

        동 및 사용자의 입력 -> Controller -> Service -> Dao -> Repository -> Dao -> ServiceImpl -> Controller -> 결

        과 페이지

    => 기본키 

        - 기본키나 Unique 속성의 데이터를 입력하는 경우는 중복검사를 해주어야 함

        -  기본키를 자동설정하는 경우 sequence나 auto)increment를 이용하는 경우는 삽입하는 SQL에서 처리

        - 가장 큰 번호를 찾아 +1하는 경우 Dao에 메소드를 추가해야 함

    => 가장 큰 번호를 찾아서 +1하는 방식은 Oracle, MySQL이 동일하므로 데이터 베이스를 변경해도 그대로 둬도 됨

        - 하나의 SQL구문을 별도로 실행해야 한다는 점은 단점

  1) index.jsp 파일에 삽입 요청을 생성

    - <li><a href="item/insert">데이터 삽입</a></li>

  2) ItemController 클래스에 위의 요청을 처리하는 코드를 doGet에 삽입

else if(command.equals("/item/insert") && method.equals("GET")) {
	//입력 페이지로 이동
	RequestDispatcher dispatcher = request.getRequestDispatcher("../view/insert.jsp");
	dispatcher.forward(request, response);
    }

 

  3) WebContent/view/insert.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>
	<h3>아이템 입력</h3>
	<!-- title, category, description을 입력 -->
	<form method="post" id="itemform">
		카테고리
		<select name="category" id="category">
			<option value="language">언어</option>
			<option value="IDE">통합개발환경</option>
			<option value="Database">데이터베이스</option>
			<option value="etc">기타</option>
		</select><br />
		제목<input type="text" name="title" id="title"/>
		<br/>
		설명<textarea rows="10" cols="30" 
		name="description" id="description"></textarea>
		<input type="submit" value="전송" />
		<input type="button" value="메인" id="mainbtn"/>
	</form>
</body>
	<script>
		document.getElementById("mainbtn")
			.addEventListener("click", function(){
				location.href = "../";
			})
	</script>
</html>

 

  4) ItemDao 클래스에 가장 큰 code를 찾아오는 메소드와 데이터를 삽입하는 메소드를 생성

    => 가장 큰 code를 찾아오는 메소드 - select max(code) form item

//가장 큰 글번호를 찾아오는 메소드
public int maxCode() {
	int result = 0;
	connect();
	
	try {
		pstmt.getConnection().prepareStatement("select max(code) from item");
		//SQL을 실행
		rs = pstmt.executeQuery();
		if(rs.next()) {
			result = rs.getInt("max(code)");
		}
	}catch (Exception e) {
		System.err.println("가장 큰 글번호 찾기 에러");
		System.out.println(e.getMessage());
		e.printStackTrace();
	}
	close();
	return result;
}

    => 데이터를 삽입하는 메소드

        - 데이터 삽입과 갱신은 DTO나 Map을 매개변수로 받고 정수를 리턴

        - 데이터 삭제는 일반적으로 Primary Key를 매개변수로 받고 정수를 리턴

        - insert into 테이블명(컬럼명 나열) values(?, ?....)

    => 고정된 값을 ?대신에 직접입력(오라클 오늘날짜: sysdate, 일련번호는 시퀀스명, MyASQL : Auto_increment 생략 

public int insert(Item item) {
	//-1로 초기화하여 -1이 리턴되면 작업 실패
	int result = -1;
	connect();
	try {
		pstmt = con.prepareStatement("insert into item(code, category, title, description) "
				+ "values(?, ?, ?, ?)");
		//?의 값을 바인딩
		pstmt.setInt(1,  item.getCode());
		pstmt.setString(2,  item.getCategory());
		pstmt.setString(3,  item.getTitle());
		pstmt.setString(4,  item.getDescription());
		
		//SQL 실행
		result = pstmt.executeUpdate();
		
		
		}catch (Exception e) {
		System.err.println("삽입 에러");
		System.out.println(e.getMessage());
		e.printStackTrace();
	}
	close();
	return result;
}

 

  5) ItemService 클래스에 데이터 삽입처리를 위한 메소드를 생성

    => ItemService 인터페이스에 데이터 삽입처리를 위한 메소드를 선언

//Item 테이블에 데이터를 저장하는 메소드
	public void insert(HttpServletRequest request, HttpServletResponse response);

 

    => ItemServiceImpl 클래스에 삽입처리를 위한 메소드 구현

@Override
public void insert(HttpServletRequest request, HttpServletResponse response) {
	//1. 파라미터 읽기
	String category = request.getParameter("category");
	String title = request.getParameter("title");
	String description = request.getParameter("description");
		
	//2. 파라미터를 가지고 필요한 작업 수행
	//가장 큰 code를 찾고 +1하여 code에 대입
	int code = itemDao.maxCode() +1;	
		
	//3. 호출할 Dao 메소드의 매개변수를 생성
	Item item = new Item();
	item.setCode(code);
	item.setCategory(category);
	item.setTitle(title);
	item.setDescription(description);
		
	//4. Dao의 메소드를 호출하여 결과를 저장
	int result = itemDao.insert(item);
		
	//5. 결과를 저장
	request.getSession().setAttribute("result", result);
		
}

 

  6) ItemItemController 클래스의 doGet 메소드에 삽입요청을 처리하는 코드를 추가

else if(command.equals("/item/insert") && method.equals("POST")) {
	//삽입을 처리
	itemService.insert(request, response);
	//삽입하고 결과 페이지로 이동
	//작업을 수행했으므로 목록보기로 리다이렉트
	response.sendRedirect("list");
}

 

14. 상세보기(파라미터 이용하기)

    => 목록에서 제목을 클릭하여 세부내용을 확인하는 형태로 많이 구현 함

        - 제목에 링크를 설정, 이 링크에 상세보기를 위한 기본키 값을 같이 전달해야 함

        - 예전에는 파라미터 형태로 기본키 값을 넘겼는데 최근에는 URL에 포함하는 형태로 구현

  1) 목록보기에서 제목부분에 링크를 설정

<td> &nbsp;<a href="item/detail?code=${item.code}">${item.title}</a></td>

    => 예전의 링크의 경우 파라미터를 이용해서 데이터를 전달 했는데 최근의 Web에서는 파라미터는 form의 데이터를

        전송할 때 주로 사용하고 url에 데이터를 포함시켜 전송하는 경우가 많음

 

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

//상세보기를 위한 메소드
public Item detail(int code) {
	Item item = null;
	connect();
	
	try {
		//sql 생성
		pstmt = con.prepareStatement("select * from item where code = ?");
		pstmt.setInt(1, code);
			
		//SQL 실행
		rs = pstmt.executeQuery();
		
		//하나의 행이 나오는 쿼리 결과 처리
		if(rs.next()) {
			item = new Item();
			item.setCode(rs.getInt("code"));
			item.setTitle(rs.getString("title"));
			item.setCategory(rs.getString("category"));
			item.setDescription(rs.getString("description"));
		}
	}catch (Exception e) {
		System.err.println("상세보기 에러");
		System.out.println(e.getMessage());
		e.printStackTrace();
	}
	
	close();
	return item;
}

 

  3) ItemService 인터페이스에 상세보기 처리를 위한 메소드를 선언

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

 

  4) ItemServiceImpl 클래스에 상세보기 처리를 위한 메소드를 구현

	@Override
	public void detail(HttpServletRequest request, HttpServletResponse response) {
		//1. 파라미터 읽기
		try {
			request.setCharacterEncoding("utf-8");
		}catch (Exception e) {
			System.err.println("인코딩 에러");
			System.out.println(e.getMessage());
			e.printStackTrace();
		}
		String code = request.getParameter("code");
		//2. 파라미터를 가지고 일반적인 처리
		
		//3. DAO 메소드의 매개변수 만들기
		int c = Integer.parseInt(code);
		//4. DAO 메소드를 호출하여 결과를 저장
		Item item = itemDao.detail(c);
		//5. 이동방법에 따라 사용할 데이터를 저장
		request.setAttribute("item", item);
	}

 

  5) ItemController에서 상세보기 처리를 위한 코드를 doGet에 작성

else if(command.equals("/item/detail")) {
			//상세보기 처리
			itemService.detail(request, response);
			//겨로가 페이지로 이동
			RequestDispatcher dispatcher = request.getRequestDispatcher("../view/detail.jsp");
			dispatcher.forward(request, response);
		}

 

  6) WebContent/view/detail.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>
	<table border="1" align="center">
		<tr>
			<td>제목</td>
			<td>${item.title}</td>
		</tr>
		<tr>
			<td>내용</td>
			<td>${item.description}</td>
		</tr>
		<tr>
			<td colspan="2" align="center">
				<!-- 수정과 삭제는 기본키를 넘겨받아야 합니다. -->
				<!-- 절대 경로로 링크를 생성 -->
				<a href="${pageContext.request.contextPath}/item/update/${item.code}">수정</a>&nbsp;&nbsp;
				<a href="${pageContext.request.contextPath}/item/delete/${item.code}">삭제</a>&nbsp;&nbsp;
				<a href="${pageContext.request.contextPath}/item/list">목록</a>
			</td>
		</tr>	
				
	</table>
</body>
</html>

 

15. 데이터 수정

    => 기본키를 가지고 데이터를 찾아와서 출력한 후 수정한 내용을 데이터 베이스에 반영

  1) ItemController 클래스에 데이터 수정을 GET방식으로 요청하면 처리하는 코드를 doGET 메소드에 작성

    => 상세보기랑 데이터 수정화면을 만드는 것은 처리 내용은 같은데 화면 출력 방법이 다름

else if(command.indexOf("/item/update") >= 0) {
	//상세보기를 처리
	itemService.detail(request, response);
	//결과 페이지로 이동
	//결과 페이지에 jsp 파일이 제대로 생성되었는지 확인
	RequestDispatcher dispatcher = 
		request.getRequestDispatcher(
			"../../view/update.jsp");
	dispatcher.forward(request, response);
}

 

  2) update.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>
	<!-- action을 생략하면 이전과 동일한 요청 -->
	<h3>데이터 수정</h3>
	<form method="post">
		코드<input type="text" value="${item.code}"
		name="code" id="code" readonly="readonly" /><br/>
		카테고리<input type="text" value="${item.category}"
		name="category" id="category" readonly="readonly" /><br/>
		이름<input type="text" value="${item.title}"
		name="title" id="title" /><br/>
		설명<textarea name="description" id="description"
		rows="10" cols="30">
		${item.description}</textarea><br/>
		<input type="submit" value="수정" />
		<input type="button" value="메인" id="mainbtn"/>
		<input type="button" value="목록" id="listbtn"/>
		
	</form>
</body>
<script>
	document.getElementById("mainbtn")
		.addEventListener("click", function(event){
		location.href=
			"${pageContext.request.contextPath}/";
	});
	document.getElementById("listbtn")
	.addEventListener("click", function(event){
	location.href=
		"${pageContext.request.contextPath}/item/list";
	});
</script>
</html>

  3) ItemDao 클래스에 데이터 수정을 위한 메소드를 생성

    => public int update(Item item)모양

    => sql

        - update 테이블 명

          set 수정할 컬럼 = 값, 수정할 컬럼=값..

          where 기본키 = 값;

        - where가 생략되는 경우도 있음

public int update(Item item) {
	//-1로 초기화해서 -1이 리턴되면 작업 실패
	int result = -1;
	connect();
	try {
		pstmt = con.prepareStatement("update item set title=?, description=? where code=?");
		//?에 값을 바인딩
		pstmt.setString(1, item.getTitle());
		pstmt.setString(2, item.getDescription());
		pstmt.setInt(3, item.getCode());
		
		//SQL 실행
		result = pstmt.executeUpdate();
		
	}catch(Exception e) {
		System.out.println(e.getMessage());
		e.printStackTrace();
	}
	close();
	return result;
}

 

  4) ItemServie 인터페이스에 수정요청처리할 메소드를 선언

//수정 처리를 위한 메소드
public void update(HttpServletRequest request, HttpServletResponse response);

 

  5) ItemServiceImpl 클래스에 수정 요청을 처리할 메소드를 구현

@Override
public void update(HttpServletRequest request, HttpServletResponse response) {
	//1.파라미터를 읽기
	try {
		request.setCharacterEncoding("utf-8");
	} catch (UnsupportedEncodingException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	String code = request.getParameter("code");
	String title = request.getParameter("title");
	String description = request.getParameter("description");
	String category = request.getParameter("category");
			
	//Dao 의 파라미터 만들기
	Item item = new Item();
	item.setCode(Integer.parseInt(code));
	item.setTitle(title);
	item.setDescription(description);
	item.setCategory(category);
	
	int result = itemDao.update(item);
			
	//웹 사이트만 고려하면 저장할 필요가 없습니다.
	request.getSession().setAttribute("result", result);
			
}

 

  6) ItemController 클래스에 수정 요청을 처리하는 코드를 doGet 메소드에 작성

else if(command.indexOf("/item/update") >= 0 && method.equals("GET")) {
	//상세보기를 처리
	itemService.detail(request, response);
	//결과 페이지로 이동
	//결과 페이지에 jsp 파일이 제대로 생성되었는지 확인
	RequestDispatcher dispatcher = 
		request.getRequestDispatcher(
			"../../view/update.jsp");
		dispatcher.forward(request, response);
	}else if(command.indexOf("/item/update") >= 0
		&& method.equals("POST")) {
	//데이터 수정을 처리
	itemService.update(request, response);
	//결과 페이지로 이동
	response.sendRedirect("..list");
}

 

16. 데이터 삭제

  1) ItemDao 클래스에 데이터 삭제를 위한 메소드를 구현

    => public int delete(기본키) 의 형태

    => sql : delete from 테이블명 where 기본키=?;

//데이터 삭제를 위한 메소드
public int delete(int code) {
	int result = -1;
	connect();
	try {
		pstmt = con.prepareStatement(
				"delete from item where code = ?");
		pstmt.setInt(1, code);
		
		result = pstmt.executeUpdate();
	}catch (Exception e) {
		System.out.println(e.getMessage());
		e.printStackTrace();
	}
	close();
	return result;
}

 

  2) Item 인터페이스에 데이터 삭제와 관련된 메소드를 생성

//삭제 처리를 위한 메소드
public void delete(HttpServletRequest request, HttpServletResponse response);

 

  3) ItemServiceImpl 클레스에 데이터 삭제와 관련된 메소드를 구현

@Override
public void delete(HttpServletRequest request, HttpServletResponse response) {
	//마지막 /뒤의 값 가져오기
	String[] ar =request.getRequestURI().split("/");
	String code = ar[ar.length-1];
	
	int result = itemDao.delete(Integer.parseInt(code));
	request.getSession().setAttribute("result", result);	
}

 

  4) ItemController 클래스의 doGet 메소드에서 삭제 요청을 처리하는 코드를 추가

else if(command.indexOf("/item/delete") >= 0) {
		//데이터 삭제를 처리
		itemService.delete(request, response);
		//결과 페이지로 이동
		response.sendRedirect("../list");
	}

 

**Connection Pool

    => 데이터베이스 연동시마다 데이터베이스 연결을 만들어주고 연결을 해제하는 것은 자원의 낭비

        - 미리 데이터베이스 연결 객체를 만들어 두고 필요시 만들어진 객체를 이용하여 사용, 불필요 시는 반납하는 방식으로 데이터베이스를 사용하는 것을 Connection Pool이라고 함

        - 이전에는 Connection Pool을 개발자가 직접 구현했지만 최근에는 라이ㅡ러리 형태로 많이 제공

        - 여러 종류의 DataBase Connection Pool 라이브러리가 존재(톰캣에서도 제공)

    => 데이터베이스 접속정보는 일반적으로 개발시와 운영시는 다름

        - 이런 정보를 java 코드에 문자열로 만들어서 사용하는 것은 배포시 불필요한 오류 발생

        - 처음 1회 사용되고 실행중에는 변경X, 배포시 변경되는 문자열이나 숫자데이터의 값은 별도의 파일이나 데이터베이스에 저장해두고 사용하는 것이 바람직

    => java web application에서는 META-INF 디렉토리

 

 

3. 

 

 

 

 

 

 

 

**나누어서 읽기

    => 많은 데이터 출력시 화면 크기에 제약이 있어 일부분만 출력하고 필요시 나머지 부분을 출력하는 방식을 사용

        - 이전에는 목록보기에서만 이런식의 구현이 사용되었는데 최근에는 상세보기에서도 이방식이 사용됨

1. 구현 방식

  1) 전체 데이터를 읽어온 후 보기를 누르면 출력만 해주는 방식

    => 한번 가져오면 잘 변경되지 않는 경우

    => 업데이트 이벤트를 만들어 주어야 함

    => 이렇게 만들어진 대표적인 모바일 애플리케이션이 메일 앱

  2) 페이지 단위로 데이터를 가져와서 출력하는 방식

    => 이 경우는 사용자가 많이 접근하는 형태로 반드시 정렬하여 가져와야 함

 

2. 데이터베이스에서 페이징

  1) MySQL은 select 구문의 맨 마지막 limit 시작번호, 데이터 개수

    => 시작번호는 0부터 시작

  2) Oracle은 inline view(from 절에 사용된 select 구문)이용

  3) Mongo DB는 skip과 limit 함수를 이용

 

3. Html 이외의 출력

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

    => 위의 설정에서 contentType이 출력 형식

        - contentType을 text/xml로 설정시, xml 형식으로 출력할 수 있고 text/json으로 설정시 json형식으로 출력 가능

        - json은 기본적으로 제공이 안되므로 json라이브러리를 이용하여 출력

        - JSONObject : Map

        - JSONArray : 배열 - List

 

4. 모바일 서버를 만들때는 여기까지를 가지고 만듬

 

5. 웹 페이지에서의 구현

    => ajax나 webSocket의 개념을 알아야 함

    => 더보기 구현시 이전 출력 내용은 그대로 두고, 새로 받아온 내용을 전체화면 갱신없이 출력해야 함

        - 이때 사용되는 기술이 ajax와 webSocket

        - ajax는 한번 받아오면 연결을 끊음

        - web Socket은 끊는다고 할 때 까지 연결을 끊지 않음

    => 요청하지 않아도 서버가 클라이언트에게 데이터를 전송(웹사이트 광고)할때, webPush(SSE, Notification)을 이용

    => ajax, web Socket, web Push를 합쳐서 web Communication api라고 함

    => 더보기나 페이징은 ajax를 이용해서 구현

        - 최근의 웹에서 댓글 작성도 ajax를 이용

    => react, vue, angular를 기반으로 하는 SPA(Single Page Application)에서 ajax는 필수

    => ajax를 쉽게 사용할 수 있도록 해주는 자바스크립트 라이브러리 중 하나가 jquery

        - bakcend 개발자들은 jquery를 ajax 사용을 쉽게 하기 위한 목적과 크로스 브라우징 때문에 이용

 

6. 전통적인 방식의 페이징 처리

    => 하단에 페이지 번호를 출력해두고 페이지 번호를 클릭하면 페이지 번호에 해당하는 데이터를 가져오는 방식

    => 필요한 데이터

        - 현재 페이지 번호

        - 페이지당 출력할 데이터 개수 : 최근 웹페이지에서 보면 출력 데이터 개수를 설정하는 경우가 있으므로 필요

                                                (고정된 값일시 생략)

        - 현재 페이지 번호에 해당하는 데이터를 찾아올수 있어야 함

        - 데이터 하단에 출력할 페이지 번호를 찾아야 함

        - 종료 페이지 번호를 구해야 하는데 이 번호는 현재 페이지 번호, 한 페이지에 출력할 페이지 번호의 개수로 계산

        - 종료 페이지 번호에서 한 페이지에 출력할 페이지 번호의 개수 -1을 빼면 시작하는 페이지번호가 만들어짐

        - 전체 데이터 개수와 페이지당 출력할 데이터 개수를 가지고 전체 페이지 개수를 구함

        - 종료 페이지 번호가 전체 페이지 개수보다 크면 종료 페이지 번호를 전체 페이지 개수로 변환

        - 시작 페이지 옆의 이전 페이지로 이동할 수 있는 링크의 생성 여부를 결정

          (시작 페이지 번호가 1이면 없고, 1이 아니면 있어야 함)

        - 종료 페이지 번호 옆의 다음페이지로 이동할 수 있는 링크의 생성여부를 결정

          (종료 페이지 번호가 전체페이지 번호와 동일하면 없고, 그렇지 않으면 있어야 함)

        - 페이지 번호를 출력하지 않고 더보기, 스크롤로 구현할 경우 시작페이지번호, 시작, 다음은 필요 없음

      

    => 올림 : Math.ceil, 반올림 : Math.round, 버림 : Math.floor

  1) ItemDao 클래스에 전체 데이터 개수를 리턴하는 메소드를 생성

    => 전체 페이지 개수를 알아야 하기 때문

    => 페이징 뿐 아니라 더보기를 만들 때도 필요

//페이징과 더보기 구현을 위한 전체데이터 개수를 세는 메소드
public int getCount() {
	//기본 데이터 개수는 0
	int result = 0;
	connect();
		
	//SQL을 생성
	try {
		pstmt = con.prepareStatement("select count(*) from item");
		//SQL을 실행
		rs = pstmt.executeQuery();
		if(rs.next()) {
			//결과를 저장
			result = rs.getInt(1);
		}
	}catch (Exception e) {
		System.out.println(e.getMessage());
		e.printStackTrace();
	}
	
	return result;
}

 

  2) 페이지번호와 한 페이지에 출력할 데이터 개수를 매개변수로 받아서 페이지 번호에 해당하는 데이터만 리턴하는 함

    수를 ItemDao에 생성

    => 페이지당 데이터 개수를 변경할 수 없다면 페이지당 데이터 개수는 매개변수로 설정하지 않아도 됨

    => 기존 메소드를 그냥 둔 상태에서 매개변수만 다르게 해서 구현시 Method Overloading이라고 함

//페이지 번호와 페이지당 데이터 개수를 입력받아서 
//페이지 번호에 해당하는 데이터만 리턴해주는 메소드
public List<Item> list(int pageno, int perpagecnt){
	List<Item> list = new ArrayList<Item>();
	connect();
	
	try {
		pstmt = con.prepareStatement("select * from item limit ?,?");
		//데이터 시작번호는 페이지번호 -1에 데이터 개수를 곱한 것
		pstmt.setInt(1, (pageno-1)*perpagecnt);
		//가져올 데이터 개수는 페이지당 출력할 데이터 개수
		pstmt.setInt(2, perpagecnt);
		rs = pstmt.executeQuery();
		
		while(rs.next()) {
			Item item = new Item();
			item.setCode(rs.getInt("code"));
			item.setCategory(rs.getString("category"));
			item.setTitle(rs.getString("title"));
			
			list.add(item);
		}
	}catch (Exception e) {
		System.out.println(e.getMessage());
		e.printStackTrace();
	}
	
	close();
	return list;
}

 

  3) ItemServiceImpl 클래스의 데이터 목록을 가져오는 메소드를 수정

@Override
public void list(HttpServletRequest request, HttpServletResponse response) {
	//1.파라미터 읽기
			
	//페이지 번호와 페이지 당 데이터 개수를 저장할 변수를 생성
	int pageno = 1;
	int perpagecnt = 5;
			
	//파라미터로 페이지 번호와 페이지 당 데이터개수가 넘어오면
	//페이지 번호와 페이지당 데이터 개수 변경
	String no = request.getParameter("no");
	String pagecnt = request.getParameter("pagecnt");
			
	if(no != null) {
		pageno = Integer.parseInt(no);
	}
	if(pagecnt != null) {
		perpagecnt = Integer.parseInt(pagecnt);
	}
			
	//2.파라미터 변환 작업이나 알고리즘 처리
			
	//3.호출할 Dao 메소드의 매개변수를 생성
			
	//4.Dao의 메소드를 호출해서 결과를 저장
	List<Item> list = itemDao.list(pageno, perpagecnt);
			
			
	//전체 데이터 개수를 가져오기
	int totalCount = itemDao.getCount();
	//데이터 출력화면에 표시할 마지막 페이지 번호와 시작 페이지 번호를
	//생성
	//하나의 페이지에 페이지 번호를 10개씩 출력
	//종료 페이지 번호를 임시로 계산
	//1 - 10, 2 - 10, 12 - 20, 21 - 30
	int endPage = (int)(Math.ceil(pageno/10.0)*10.0);
	int startPage = endPage - 9; 
			
	//전체 페이지 개수 구하기
	int tempEndPage = (int)(Math.ceil(totalCount/(double)perpagecnt));
	//끝나는 페이지 번호가 전체 페이지 개수보다 크면 끝나는 페이지 번호 수정
	if(endPage > tempEndPage) {
		endPage = tempEndPage;
	}
			
	//이전과 다음의 출력 여부 생성
	boolean prev = startPage == 1 ? false : true;
	boolean next = endPage * perpagecnt >= totalCount ? false : true;
			
	//5.Dao 메소드 호출 결과를 View로 전달하기 위해서
	//request 나 session 에 저장
			
	//포워딩 할 거면 request
	//리다이렉트 할 거면 session
	request.setAttribute("list", list);
	request.setAttribute("startpage", startPage);
	request.setAttribute("endpage", endPage);
	request.setAttribute("pageno", pageno);
	request.setAttribute("prev", prev);
	request.setAttribute("next", next);
	//전체 데이터 개수를 출력하고자 하면 저장
	request.setAttribute("totalcount", totalCount);		
}

 

  4) list.jsp 파일을 수정

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!-- 제어문 사용을 위한 태그 라이브러리를 설정 -->
<%@ taglib prefix="c" 
	uri="http://java.sun.com/jsp/jstl/core" %>    
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>목록보기</title>
</head>
<body>

	<h3 align="center">데이터 목록 보기</h3>
	<table align="center" border="1">
		<tr>
			<th>코드</th>
			<th>카테고리</th>
			<th>이름</th>
		</tr>
		<c:forEach var="item" items="${list}">
			<tr>
				<td>&nbsp;${item.code}</td>
				<td>&nbsp;${item.category}</td>
				
				<!-- 기본키의 값을 파라미터로 전송 -->
				<!-- <td>&nbsp;<a href="detail?code=${item.code}">${item.title}</a></td>  -->
				
				<td>&nbsp;<a href="detail/${item.code}">
				${item.title}</a></td>
			</tr>	
		</c:forEach>	
	</table>
	<div align = "center">
		<c:if test="${prev == true}">
			<a href="list?no=${startpage-1}">이전</a>
		</c:if>
		
		<c:forEach var="idx" begin="${startpage}" end="${endpage}">
			<c:if test="${pageno == idx}">
				${pageno}&nbsp;
			</c:if>
			<c:if test="${pageno != idx}">
				<a href="list?no=${idx}">${idx}</a>&nbsp;
			</c:if>
		</c:forEach>
		
		<c:if test="${next == true}">
			<a href="list?no=${endpage+1}">다음</a>
		</c:if>
	</div>
</body>
</html>

 

**ajax와 REST API Server 구축 및 암호화

1. REST API

    => 디바이스나 화면 크기에 상관없이 동일한 콘텐츠를 사용하는 경우에는 하나의 URL을 이용

    => Web Browser, Android, iOS Application등이 동일콘텐츠 이용시, 동일URL을 이용해서 콘텐츠를 사용가능해야 함

    => 서버 입장에서 여러 디바이스의 요청을 서로 다른 URL을 이용해서 처리시 유지보수가 어렵고 서버 Application을

        여러개 구현해야 할 가능성이 높아 관리가 어려워짐

    => 클라이언트 입장에서는 디바이스 별로 다른 URL을 요청하므로 클라이언트용 애플리케이션 개발이 어려워짐

    => 이 경우 선택한 데이터 포맷은 XML과 JSON

        - XML : 태그형식으로 표현하기 때문에 개발자가 구조파악하기가 쉬움

        - JSON : 자바스크립트 데이터 표현방법으로 표현되어 있고, XML이후에 나온 포맷

            -> 단점 : 개발자의 구조 파악이 어려움, 나중에 나온 포맷이라 외부 라이브러리를 이용하여 쉽게 사용 가능

            -> 장점 : XML보다 경량이고, 자바스크립트나 파이썬 같은 언어와 데이터 작성 방법이 동일하므로 파싱이 수월

    => Java Web Programming 만으로 가능하지만 Spring 같은 Framework 사용시 조금 더 쉽게 구현

    => Server가 View를 만들지 않고 데이터를 만들어서 전송하는 개념

 

2. ajax

    => 비동기 데이터 전송

    => REST API Server는 View가 아닌 데이터를 전송해주기 때문에 HTML파일에서 Server와 통신하기 위해서 이전에

        사용하던 a태그나 자바스크립트의 페이지 이동 또는 iframe을 사용할 수 없음

    => REST API Server와 통신하기 위해서는 다른 기술을 이용해야하는 데 그 중의 하나가 ajax

    => ajax는 자신의 도메인 데이터가 가져올수 있도록 설계되어 있는데 서버측에서 CORS가 가능하도록 설계해주면

        ajax로 다른 도메인의 데이터도 가져올수 있음

        - 상대방 서버가 CORS가 가능하도록 설계되어 있지 않다면 proxy를 이용하여 ajax 요청을 자신의 서버에게 보내

          고 서버용 프로그래밍언어를 이용하여 외부 데이터를 가져오고 다시 ajax에 전달하는 방식을 이용

 

3. 암호화

    => 평문을 그냥 알아볼수 없도록 만드는 것

    => 복호화 : 암호화된 문장을 평문으로 변경하는 것

  1) 암호화 방식

    => 평문을 비문으로 만든 후 비문을 복호화할 수 없게하고, 평문과 비문을 비교할 수 있게 하는 방식(비밀번호)

    => 비문을 평문으로 복호화 할 수 있도록 만드는 방식

  2) 암호화 알고리즘

    => MD5, sha-256, res등 여러가지 알고리즘이 있음

 

**더보기 구현

1. json 생성을 위한 라이브러리를 프로젝트에 복사

    => view에 데이터를 전달하기 위해서 수행

 

2. ServiceImpl 클래스에서 데이터 목록을 만들어주는 메소드를 수정

public void list(HttpServletRequest request, HttpServletResponse response) {
		//1. 파라미터 읽기
		
		//페이지 번호와 페이지당 데이터 개수를 저장할 변수를 생성
		int pageno = 1;
		int perpagecnt = 5;
		
		//파라미터로 페이지 번호와 페이지당 데이터 개수가 넘어오면 페이지 번호와 페이지당 데이터 개수 변경
		String no = request.getParameter("no");
		String pagecnt = request.getParameter("pagecnt");
		
		System.out.println("no : " + no);
		System.out.println("pagecnt : " + pagecnt);
		if(no != null) {
			pageno = Integer.parseInt(no);
		}
		if(pagecnt != null) {
			perpagecnt = Integer.parseInt(pagecnt);
		}
		
		//2. 파라미터 변환 작업이나 알고리즘 처리

		
		//3. 호출할 Dao 메소드의 매개변수를 생성
		
		//4. Dao의 메소드를 호출하여 결과를 저장
		//JSON 출력을 위해서 JSON 데이터 형식으로 변환
		//List, 배열 -> JsonArray
		//DTO, Map -> JsonObject
		List<Item> list = itemDao.list(pageno, perpagecnt);
		JSONArray ar = new JSONArray();
		for(Item item : list) {
			JSONObject obj = new JSONObject();
			obj.put("code", item.getCode());
			obj.put("title", item.getTitle());
			obj.put("category", item.getCategory());
			obj.put("description", item.getDescription());
			
			ar.put(obj);
		}
		
		//전체 데이터 개수 가져오기
		int totalCount = itemDao.getCount();
		//데이터 출력화면에 표시할 마지막 페이지 번호와 시작 페이지 번호를 생성
		//하나의 페이지에 페이지 번호를 10개씩 출력
		//종료 페이지 번호를 임시로 계산(1-10, 2-10, 12-20, 21-30)
		int endPage = (int)(Math.ceil(pageno/10.0)*10.0);
		
		//페이징에서는 뒤로 가는게 있어서 시작페이지 번호가 필요하지만 더보기 형태의 구현은 뒤로가는게 없어서 시작페이지 번호가 필요없음
		//int startPage = endPage - 9;
		
		//전체 페이지 개수 구하기
		int tempEndPage = (int)(Math.ceil(totalCount)/(double)perpagecnt);
		//끝나는 페이지 번호가 전체 페이지 개수보다 크면 끝나는 페이지 번호 수정
		if(endPage > tempEndPage) {
			endPage = tempEndPage;
		}
		
		//이전도 필요가 없음
		//이전과 다음의 출력 여부 생성
		//boolean prev = startPage == 1 ? false : true;
		
		//다음데이터의 존재여부를 위해 있는 것이 좋음
		boolean next = endPage*perpagecnt >= totalCount ? false : true;
		
		//Dao의 매개변수를 확인
		
		//5. Dao 메소드 호출 결과를 View로 전달하기 위해 request나 session에 저장
		// 포워딩 할 경우 : request, 리다이렉트 할 경우 : session
//		request.setAttribute("list", list);
//		request.setAttribute("startpage", startPage);
//		request.setAttribute("endpage", endPage);
//		request.setAttribute("pageno", pageno);
//		request.setAttribute("prev", prev);
//		request.setAttribute("next", next);
		
		//REST API 서버를 구현할 때는 JSONObject로 묶어서 1개만 저장
		JSONObject result = new JSONObject();
		//현재 페이지 번호, 종료 페이지 번호, 다음 존재여부, 출력할 데이터
		result.put("pageno", pageno);
		result.put("endpage", endPage);
		result.put("next", next);
		result.put("ar", ar);
		
		//request에 저장
		request.setAttribute("result", result);
		
		
	}

 

  2) Controller를 확인해서 list 요청시 list.jsp로 출력하는 것을 확인하고 list.jsp 파일을 만들어 앞에서 저장한 내용 출력

 

  3) 테스트

    => POST방식은 테스트가 어렵지만 GET 방식은 브라우저를 이용해서 테스트가 가능 

    => 서버를 실행히키고 브라우저 창에 item/list 를 추가하여 확인

    => 여기까지가 서버 구현에서는 끝

 

5. Web Client에서 데이터를 요청해서 출력하기

  1) index.jsp 페이지에서 목록보기 요청을 수정

<li><a href="item/itemlist">전체 데이터 조회</a></li>

  2) ItemController에서 itemlist요청이 온 경우 출력할 페이지로 포워딩하는 코드를 doGet메소드에 작성

else if(command.equals("/item/itemlist")) {
	RequestDispatcher dispatcher = request.getRequestDispatcher("../view/itemlist.jsp");
	dispatcher.forward(request, response);
}

  3) doGet 메소드에서 삽입이나 수정, 삭제 이후에 이동하는 곳도 itemlist로 변경

 

  4) itemlist.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>
	<h2 align="center">목록 보기</h2>
	<table align="center" border="1" id="table">
		<tr>
			<th>코드</th>
			<th>카테고리</th>
			<th>제목</th>
		</tr>	
		
	</table>
</body>

<!-- jquery를 사용하기 위한 링크 설정 
ajax를 편리하게 사용하기 위해서 -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>

<script>
	//페이지 번호 변수
	var pageno = 1;
	
	//ajax 요청을 해서 table에 데이터를 출력해주는 함수
	function adddata(){
		$.ajax({
			url:"list",
			dataType:"json",
			data:{"no":pageno},
			success:function(data){
				//더보기 버튼을 삭제
				$('#add').remove();
				//배열을 순회해서 하나의 객체를 한 줄로 출력
				$.each(data.ar, function(index, item){
					disp = "<tr><td>" + item.code + "</td>";
					disp += "<td>" + item.category + "</td>";
					disp += "<td>" + item.title + "</td></tr>";
					$('#table').html($('#table').html() + disp);
				});
				//더보기 버튼 만들기
				//현재 페이지가 종료 페이지보다 작을 때 만 생성
				if(data.pageno < data.endpage){
					//페이지 번호 하나 올리기
					pageno = pageno + 1;
					
					disp = "<tr id='add'>" + 
						"<td colspan='3' align='center'>" + 
						"더보기"	+ "</td></tr>";
					$("#table").html($("#table").html() + disp);
					//id 가 add 객체를 click 하면 adddata 라는 함수를 호출
					document.getElementById("add")
					.addEventListener("click", adddata);
				}
			}
		});
	};
	
	//태그를 전부를 읽고 난 후 수행
	window.addEventListener("load", function(e){
		adddata();
	})
</script>

</html>