728x90
300x250

[JSP] 13. Jsp/Servlet(MVC) Maven 기반의 다중 파일 업로드, 다운로드 구현(2)


이전의 글에 이어서 작성하도록 하겠다.


이전의 글

- [JSP] 12. Jsp/Servlet(MVC) Maven 기반의 다중 파일 업로드, 다운로드 구현(1), 2020-09-24

   https://yyman.tistory.com/1414



14. Controller - BoardInsertMultiResultController 


고민을 나름대로 하여 만든 것이다. 인터넷 자료 등도 열심히 참고하고, 삽질을 많이 하였다.


package com.fileWeb.controller;


import java.io.File;

import java.io.IOException;

import java.io.PrintWriter;

import java.util.HashMap;

import java.util.Iterator;

import java.util.List;

import java.util.Map;


import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


import org.apache.commons.fileupload.FileItem;

import org.apache.commons.fileupload.disk.DiskFileItemFactory;

import org.apache.commons.fileupload.servlet.ServletFileUpload;


public class BoardInsertMultiResultController implements Controller {


private Map<String, Object> reqMap = null; // req 정보(MultiRequest)

private Map<Integer, Map<String, Object>> fileMap = null; // 다중 파일 지원

private static int num = 0; 

@Override

public void execute(HttpServletRequest req, HttpServletResponse res) throws

ServletException, IOException {

fileMap = new HashMap<Integer, Map<String, Object>>(); 

reqMap = new HashMap<>();

num = 1;

        

        PrintWriter out = res.getWriter();

        out.println("<HTML><HEAD><TITLE>Multipart Test</TITLE></HEAD><BODY>");

try {

            

            //디스크상의 프로젝트 실제 경로얻기

            //String contextRootPath = "c:" + File.separator + "upload";

String dirName = "upload" ; 

String contextRootPath = req.getSession().getServletContext().getRealPath("/") + dirName;

            System.out.println("실제경로:" + contextRootPath);

            

            //1. 메모리나 파일로 업로드 파일 보관하는 FileItem의 Factory 설정

            DiskFileItemFactory diskFactory = new DiskFileItemFactory(); //디스크 파일 아이템 공장

            diskFactory.setSizeThreshold(4096); //업로드시 사용할 임시 메모리

            diskFactory.setRepository(new File(contextRootPath + "/WEB-INF/temp")); //임시저장폴더

            

            //2. 업로드 요청을 처리하는 ServletFileUpload생성

            ServletFileUpload upload = new ServletFileUpload(diskFactory);

            upload.setSizeMax(3 * 1024 * 1024); //3MB : 전체 최대 업로드 파일 크기

            

            //3. 업로드 요청파싱해서 FileItem 목록구함​​

            List<FileItem> items = upload.parseRequest(req); 


            Iterator<FileItem> iter = items.iterator(); //반복자(Iterator)로 받기​            

            while(iter.hasNext()) { //반목문으로 처리​    

                FileItem item = (FileItem) iter.next(); //아이템 얻기

                 //4. FileItem이 폼 입력 항목인지 여부에 따라 알맞은 처리

                

                if(item.isFormField()){ 

                //파일이 아닌경우

                    processFormField(out, item);

                    

                } else {

                //파일인 경우

                // System.out.println("오류:");

                    processUploadFile(out, item, contextRootPath);

                // System.out.println("오류2:");

                }

            }

            

        } catch(Exception e) {

            out.println("<PRE>");

            e.printStackTrace(out);

            out.println("</PRE>");

        }

out.println( "usrID(Map): " + reqMap.get("usrID") );

out.println( "usrPasswd(Map):" + reqMap.get("usrPasswd") );

        

        out.println("</BODY></HTML>");

// req.setAttribute("usrID", reqMap.get("usrID"));

// req.setAttribute("login", 1);//Object Type으로 넘어감

        req.setAttribute("reqMap", reqMap);

        req.setAttribute("fileMap", fileMap);

        

        // 방법3

        for( Integer key : fileMap.keySet() ){


            Map<String, Object> fileMapNode = fileMap.get(key);

            System.out.println( String.format("키 : %s, 값: %s", key, fileMapNode.get("fileName") ));

            

        }

        

// System.out.println("오류3:" + reqMap.get("usrID"));

HttpUtil.forward(req, res, "/WEB-INF/view/board/insertResult.jsp");

}


//업로드한 정보가 파일인경우 처리

private void processUploadFile( PrintWriter out, FileItem item, String contextRootPath)

throws Exception {

Map<String, Object> fileNode = new HashMap<String, Object>();

String name = item.getFieldName(); // 파일의 필드 이름 얻기

String fileName = item.getName(); // 파일명 얻기

// 임시 - 실제 원본 이름 추출

File originalFile = new File(fileName);

String originalFileName = originalFile.getName();

// System.out.println("임시:" + originalFileName );

String contentType = item.getContentType(); // 컨텐츠 타입 얻기

long fileSize = item.getSize(); // 파일의 크기 얻기

// 업로드 파일명을 현재시간으로 변경후 저장

String fileExt = fileName.substring(fileName.lastIndexOf("."));

String uploadedFileName = System.currentTimeMillis() + ""; 

System.out.println(fileExt);

System.out.println(uploadedFileName);

// 저장할 절대 경로로 파일 객체 생성

File uploadedFile = new File(contextRootPath + "/upload/" + uploadedFileName);

item.write(uploadedFile); //파일 저장

//========== 뷰단에 출력 =========//

out.println("<P>");

out.println("파라미터 이름:" + name + "<BR>");

out.println("파일 이름:" + fileName + "<BR>");

out.println("콘텐츠 타입:" + contentType + "<BR>");

out.println("파일 사이즈:" + fileSize + "<BR>");

//확장자가 이미지인겨우 이미지 출력

if(".jpg.jpeg.bmp.png.gif".contains(fileExt.toLowerCase())) {

out.println("<IMG SRC='upload/" 

+ uploadedFileName 

+ "' width='300'><BR>");

}

out.println("</P>");

out.println("<HR>");

out.println("실제저장경로 : "+uploadedFile.getPath()+"<BR>");

out.println("<HR>");

// 파일 정보

fileNode.put("name", name);

fileNode.put("fileName", originalFileName);

fileNode.put("contentType", contentType);

fileNode.put("fileSize", fileSize);

fileNode.put("fileExt", fileExt);

fileNode.put("uploadedFileName", uploadedFileName);

fileNode.put("realName", uploadedFile.getName());

fileNode.put("realPath", uploadedFile.getPath());

fileMap.put(num, fileNode);

num++;

}

private void processFormField(PrintWriter out, FileItem item) 

throws Exception{

String name = item.getFieldName(); //필드명 얻기

Object value = item.getString("UTF-8"); //UTF-8형식으로 필드에 대한 값읽기

// out.println(name + ":" + value + "<BR>"); //출력

reqMap.put(name, value);

}


}



파일명: BoardInsertMultiResultController.java


[첨부(Attachments)]

BoardInsertMultiResultController.zip



15. View - insertResult.jsp


사소해보이지만, jsp파일 내에서도 자료구조를 사용할 수 있다.


JSP: <%@ page import="java.util.*" %>

Java: import java.util.*;


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

    pageEncoding="UTF-8"%>

<%@ page import="java.util.*" %>

<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8">

<title>업로드 - 결과</title>

</head>

<body>

<h3>업로드 결과</h3>

<%


//Object name = request.getAttribute("usrID");

//Object login = request.getAttribute("login");

Object obj = request.getAttribute("reqMap");

Map<String, Object> map = null;

if(obj != null){

map = (HashMap<String, Object>)obj;

}


%>


<table style="width:700px;border:1px solid #e2e2e2;">

<tr>

<td style="width:20%;">

<%

        out.println("name : " + map.get("usrID") + "<br />");

%>

</td>

<td style="width:20%;border-left:1px solid #e2e2e2;">

<%

out.println("login : " + map.get("usrPasswd") + "<br />");

%>

</td>

</tr>

<tr>

<td>

</td>

<td>

</td>

</tr>

</table>


</body>

</html>


파일명: insertResult.jsp


[첨부(Attachments)]

insertResult.zip



16. 라이브러리(Libraries)


pom.xml으로 자동 생성된 라이브러리이긴 한데 수동으로 추출해보았다.





17. 맺음글(Conclusion)


실질적으로 다중 파일 업로드와 다운로드 구현에 대해서 자세히 살펴보았다.



* 참고자료(References)


1. [서블릿/JSP] Apache Commons FileUpload를 이용한 파일업로드 구현하기, https://dololak.tistory.com/720?category=636501, Accessed by 2020-09-24, Last Modified 2019-07-24.


2. FrontController & Command Pattern - 프론트 컨트롤러와 커맨드 패턴, https://dailyworker.github.io/servlet-advened/, Accessed by 2020-09-24, Last Modified 2019-07-09.


3. 파일전송/업로드(Multipart) , https://gunbin91.github.io/jsp/2019/05/28/jsp_11_file.html, Accessed by 2020-09-24, Last Modified 2019-05-28.

-> Multipart 방식으로 소개되어 있는데, 현재 서블릿 버전에서는 충돌 발생함. (www.servlets.com)


4. Servlets.com | com.oreilly.servlet, http://www.servlets.com/cos/, Accessed by 2020-09-24, Last Modified 2002-03-01.

-> 비추천: 2002년에서 멈춰버린 프로젝트


5. .[jsp] form 타입이 enctype="multipart/form-data" 일때 request.getParameter() 불가 값이 null로 오는 문제, http://blog.naver.com/PostView.nhn?blogId=software705&logNo=220551397421&parentCategoryNo=&categoryNo=&viewDate=&isShowPopularPosts=false&from=postView, Accessed by 2020-09-24, Last Modified 2015-11-27.

-> 비추천: oreilly.servlet jar 파일로 구현한 MultipartRequest인데, 서블릿 충돌을 경험하게 된다.


6. [jsp/servlet] commons-fileupload 를 이용한 파일업로드 (서블릿), https://m.blog.naver.com/javaking75/220056175936, Accessed by 2020-09-24, Last Modified 2014-07-10.

-> 추천(70점): 오래된 것처럼 보이지만, 현실적으로 돌아가고 commons-io, commons-fileupload에 대해서 자세히 잘 소개하고 있음.


7. [Day49][JSP] HashMap을 사용하여 전체 회원 목록 조회 / 상세 회원 목록 조회 / JSTL, https://clapdev.tistory.com/49, Accessed by 2020-09-24, Last Modified 2019-11-05.

-> 추천(30점): c태그(JSTL)에 대해서 잘 소개하고 있음.


8. JSP 기본 실습 : 데이터 전송 받기2 [개인정보입력폼] -request-, https://whdvy777.tistory.com/entry/JSP-%EA%B8%B0%EB%B3%B8-%EC%8B%A4%EC%8A%B5-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%84%EC%86%A1-%EB%B0%9B%EA%B8%B02-%EA%B0%9C%EC%9D%B8%EC%A0%95%EB%B3%B4%EC%9E%85%EB%A0%A5%ED%8F%BC-request, Accessed by 2020-09-24, Last Modified 2013-03-08.

-> 추천(35점): 오래되어 보이지만, 기본에 충실하고 있음.


9. Java Map 반복(Iteration)시키는 3가지 방법, https://stove99.tistory.com/96, Accessed by 2020-09-24, Last Modified 2011-11-13.


-> 자주 까먹을 수 있는 방법이라고 본다. 다중 프로그래밍을 다루고 있어서 까먹는 일이 많아서 적어본다.


Map<String, String> map = new HashMap<String, String>();

         

map.put("키1", "값1");

map.put("키2", "값2");

map.put("키3", "값3");

map.put("키4", "값4");

map.put("키5", "값5");

map.put("키6", "값6");

         

         

        // 방법1

        Iterator<String> keys = map.keySet().iterator();

        while( keys.hasNext() ){

            String key = keys.next();

            System.out.println( String.format("키 : %s, 값 : %s", key, map.get(key)) );

        }

         

        // 방법2

        for( Map.Entry<String, String> elem : map.entrySet() ){

            System.out.println( String.format("키 : %s, 값 : %s", elem.getKey(), elem.getValue()) );

        }

         

        // 방법3

        for( String key : map.keySet() ){

            System.out.println( String.format("키 : %s, 값 : %s", key, map.get(key)) );

        }


- https://stove99.tistory.com/96 [스토브 훌로구]


반응형
728x90
300x250

[JSP] 12. Jsp/Servlet(MVC) Maven 기반의 다중 파일 업로드, 다운로드 구현(1)


조금 실질적으로 도움이 되는 프로젝트라고 주장한다.

다중 파일 업로드, 다운로드 프로젝트를 조금 실용적으로 유용하게 사용할 수 있도록 특화하여 따로 만들어보게 되었다.


사용하는 것은 쉽지만, 구현하는 것은 다소 많은 시간이 소요된다고 본다.

현재 시중에 판매되는 교제 등의 내용이 공개 비판하면 그렇지만, 품질이 매우 낮다는 점이다.


최신 버전에 가까운 프로젝트를 위주로 직접 태스트를 엄선해서 수차례 검증하고, 작성하였다.


* apache-tomcat-9.0.37-windows-x64

* Maven - 3.6.3/1.16.0.20200610-1735   - http://maven.apache.org/download.cgi

  (pom.xml)

   - javax.servlet-api 4.0.1

   - commons-io 2.8.0 (apache project)   -  http://commons.apache.org/

   - commons-fileupload 1.4 (apache project)   - http://commons.apache.org/


적용한 모델은 MVC2 모델을 적용하였으니 참고하면 도움이 되겠다.



1. 결과


다운로드, 업로드 기능에 많은 시간이 소요된 프로젝트였다.



그림 1. 결과 



그림 2. 결과 




그림 3. 결과


그림 4. 결과




그림 5. 결과




2. 프로젝트 구성


작업을 해야하는 양이 조금 된다. 하나 잘 만들어놓으면 활용하거나 개선을 해서 사용해보는 것도 좋은 방법인 거 같다.



그림 6. 프로젝트 구성도



3. 프로젝트 초기 설정


새 프로젝트 만들기를 누른다.



그림 7. 새 프로젝트 만들기


Maven의 Maven Project를 클릭한다.

Next를 누른다.



그림 8. New Maven Project


"org.apache.maven.archetypes   | maven-archetype-webapp"를 선택하고 Next를 누른다.




그림 9. New Maven Project(2)


패키지명과 Artifact Id를 입력하고, Finish를 누른다.



그림 10. New Maven Project(3)


톰캣 서버 설정 등을 완료한다. 그리고 빌드를 시도하면, 이런 화면을 볼 수 있다.



4. 프로젝트 속성 - Java Build Path, Project Facets


14버전으로 변경해준다.



그림 11. Java Build Path -> Libraries 탭의 JRE System Library 버전


버전을 깔맞춤해줘야 한다.

14버전이면, 14로 일관성있게 설정 해준다.



그림 12. Project Facets -> Java 버전확인


마찬가지로 버전을 14로 맞춰준다.


비고: 대략 초기 Maven Project를 생성하면, 1.6? 1.7 버전으로 셋팅되어 있음.



5. Controller 생성하기(Servlet 만들기)


프로젝트에서 오른쪽 버튼을 클릭해서 New->Servlet을 클릭해서 하나 만들어준다.



그림 13. Servlet 만들기


패키지: com.fileWeb.controller

클래스명: FrontController(java)



6. pom.xml 설정하기(MvnRepository.com)


pom.xml 설정으로 필요한 라이브러리를 구비해준다.



<?xml version="1.0" encoding="UTF-8"?>


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

  <modelVersion>4.0.0</modelVersion>


  <groupId>com.mavenUpload</groupId>

  <artifactId>web</artifactId>

  <version>0.0.1-SNAPSHOT</version>

  <packaging>war</packaging>


  <name>File - Multi Web Upload Project</name>

  <!-- FIXME change it to the project's website -->

  <url>http://www.example.com</url>


  <properties>

    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

    <maven.compiler.source>1.7</maven.compiler.source>

    <maven.compiler.target>1.7</maven.compiler.target>

  </properties>


  <dependencies>

    <dependency>

      <groupId>junit</groupId>

      <artifactId>junit</artifactId>

      <version>4.11</version>

      <scope>test</scope>

    </dependency>

    <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->

<dependency>

    <groupId>javax.servlet</groupId>

    <artifactId>javax.servlet-api</artifactId>

    <version>4.0.1</version>

    <scope>provided</scope>

</dependency>

<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->

<dependency>

    <groupId>commons-io</groupId>

    <artifactId>commons-io</artifactId>

    <version>2.8.0</version>

</dependency>

<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->

<dependency>

    <groupId>commons-fileupload</groupId>

    <artifactId>commons-fileupload</artifactId>

    <version>1.4</version>

</dependency>

  </dependencies>


  <build>

    <finalName>web</finalName>

    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->

      <plugins>

        <plugin>

          <artifactId>maven-clean-plugin</artifactId>

          <version>3.1.0</version>

        </plugin>

        <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->

        <plugin>

          <artifactId>maven-resources-plugin</artifactId>

          <version>3.0.2</version>

        </plugin>

        <plugin>

          <artifactId>maven-compiler-plugin</artifactId>

          <version>3.8.0</version>

        </plugin>

        <plugin>

          <artifactId>maven-surefire-plugin</artifactId>

          <version>2.22.1</version>

        </plugin>

        <plugin>

          <artifactId>maven-war-plugin</artifactId>

          <version>3.2.2</version>

        </plugin>

        <plugin>

          <artifactId>maven-install-plugin</artifactId>

          <version>2.5.2</version>

        </plugin>

        <plugin>

          <artifactId>maven-deploy-plugin</artifactId>

          <version>2.8.2</version>

        </plugin>

      </plugins>

    </pluginManagement>

  </build>

</project>



파일명: pom.xml


[첨부(Attachments)]

pom.zip



7. web.xml 설정하기


web.xml 설정에 관한 것이다.

jsp가 아닌 *.do로 처리하는 프로젝트에 대한 내용으로 작성되었다.

UTF-8 한글 언어셋도 정의하였다.


<?xml version="1.0" encoding="UTF-8"?>


<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 

xmlns="http://java.sun.com/xml/ns/javaee" 

xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" 

id="WebApp_ID" version="3.0">


  <display-name>Archetype Created Web Application</display-name>

  

  <welcome-file-list>


    <welcome-file>index.html</welcome-file>

    <welcome-file>index.htm</welcome-file>

    <welcome-file>index.do</welcome-file>

    <welcome-file>default.html</welcome-file>

    <welcome-file>default.htm</welcome-file>

    <welcome-file>default.do</welcome-file>

  </welcome-file-list>

  

  <servlet>

  <servlet-name>FrontController</servlet-name>

  <servlet-class>com.fileWeb.controller.FrontController</servlet-class>

  <init-param>

<param-name>charset</param-name>

<param-value>UTF-8</param-value>

</init-param>

  </servlet>

  <servlet-mapping>

  <servlet-name>FrontController</servlet-name>

  <url-pattern>*.do</url-pattern>

  </servlet-mapping>

</web-app>



파일명: web.xml


[첨부(Attachments)]

web.zip



8. Controller - HttpUtil.java


패키지명: com.fileWeb.controller

클래스명: HttpUtil (java)



package com.fileWeb.controller;


import java.io.File;

import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.io.PrintWriter;


import javax.servlet.RequestDispatcher;

import javax.servlet.ServletConfig;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;



public class HttpUtil extends HttpServlet {

private static final long serialVersionUID = 1L;

private static String charset = null;


public static void forward(HttpServletRequest req, HttpServletResponse res,

String path) throws ServletException, IOException {

try {

RequestDispatcher dispatcher = req.getRequestDispatcher(path);

dispatcher.forward(req, res);

}catch(Exception e) {

e.printStackTrace();

}

}

public static void fileUpload(HttpServletRequest req, HttpServletResponse res,

String path) throws ServletException, IOException {


charset = (String) req.getAttribute("charset");

System.out.println(charset);

PrintWriter out = res.getWriter();

// 파일 업로드된 경로

String root = req.getSession().getServletContext().getRealPath("/");

String savePath = root + "upload" + File.separator + "upload";

// 서버에 실제 저장된 파일명

String filename = "1600955663095" ;

System.out.println("파일 실제 폴더경로:" + savePath);

// 실제 내보낼 파일명

String orgfilename = "license한글.txt" ;

req.setCharacterEncoding(charset);

res.setCharacterEncoding(charset);

InputStream in = null;

OutputStream os = null;

File file = null;

boolean skip = false;

String client = "";

try{

    // 파일을 읽어 스트림에 담기

    try{

        file = new File(savePath, filename);

        in = new FileInputStream(file);

    }catch(FileNotFoundException fe){

        skip = true;

    }

    client = req.getHeader("User-Agent");

    // 파일 다운로드 헤더 지정

    res.reset() ;

    res.setContentType("application/octet-stream");

    res.setHeader("Content-Description", "JSP Generated Data");

    if(!skip){

        // IE

        if(client.indexOf("MSIE") != -1){

            res.setHeader ("Content-Disposition", "attachment; filename="+new String(orgfilename.getBytes("KSC5601"),"ISO8859_1"));

        }else{

            // 한글 파일명 처리

            orgfilename = new String(orgfilename.getBytes("KSC5601"),"iso-8859-1");

            res.setHeader("Content-Disposition", "attachment; filename=\"" + orgfilename + "\"");

            res.setHeader("Content-Type", "application/octet-stream; charset=utf-8");

        }  

        res.setHeader ("Content-Length", ""+file.length() );

        os = res.getOutputStream();

        

        byte b[] = new byte[(int)file.length()];

        int leng = 0;

        while( (leng = in.read(b)) > 0 ){

            os.write(b,0,leng);

        }

    }else{

    // 한글 깨짐 - 해결

    res.setContentType("text/html;charset=" + charset);

        out.println("<html><head>");

        out.println("<script language='javascript'>alert('파일을 찾을 수 없습니다.');history.back();</script>");

        out.println("</head><body></body></html>");

    }

    in.close();

    os.close();

}catch(Exception e){

e.printStackTrace();

}

    

}


}



파일명: HttpUtil.java


[첨부(Attachments)]

HttpUtil.zip



9. Controller의 인터페이스 (Controller.java)


Controller.java 파일이다.

인터페이스로 설계되었다.


package com.fileWeb.controller;


import java.io.IOException;


import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


public interface Controller {

public void execute(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException;

}



파일명: Controller.java


[첨부(Attachments)]

Controller.zip




10. Controller - FrontController와 Command 패턴 적용으로 구성함. 


설명도 중요하지만, 코드로 살펴보도록 하겠다.


package com.fileWeb.controller;


import java.io.IOException;

import java.util.HashMap;

import java.util.Map;


import javax.servlet.ServletConfig;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import javax.servlet.http.HttpSession;


/**

 * Servlet implementation class HomeController

 */

public class FrontController extends HttpServlet {

private static final long serialVersionUID = 1L;

       

private String charset = null;

    public FrontController() {

        super();

    }


protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

doAction(req, res);

}

protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

doAction(req, res);

}

// FrontController 패턴 & Command 패턴

protected void doAction(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {


ServletConfig sc = this.getServletConfig();

charset = sc.getInitParameter("charset");

req.setAttribute("charset", charset);

req.setCharacterEncoding(charset);

res.setContentType("text/html; charset=" + charset);

System.out.println(charset);

String uri = req.getRequestURI();

System.out.println("uri : " + uri);

String conPath = req.getContextPath();

System.out.println("conPath : " + conPath);

String command = uri.substring(conPath.length());

System.out.println("command : " + command);

Controller subController = null;

System.out.println("reqMapSize : " + req.getParameterMap().size());


if(command.equals("/board/insert.do")){

System.out.println("insert");

System.out.println("----------------");


    subController = new BoardInsertController();

    subController.execute(req, res);

   

}else if (command.equals("/board/insertResult.do")) {

System.out.println("insertResult");

System.out.println("----------------");


    subController = new BoardInsertResultController();

subController.execute(req, res);

}

else if(command.equals("/board/insertMultiResult.do")){

System.out.println("insertResult");

System.out.println("----------------");


    subController = new BoardInsertMultiResultController();

subController.execute(req, res);

}else if(command.equals("/board/download.do")) {

System.out.println("download");

System.out.println("----------------");

    HttpUtil.fileUpload(req, res, null);

}else if(command.equals("/board/update.do")){

System.out.println("update");

System.out.println("----------------");

}else if(command.equals("/board/select.do")){

System.out.println("select");

System.out.println("----------------");

}else if(command.equals("/board/delete.do")){

System.out.println("delete");

System.out.println("----------------");

}

}

}



파일명: FrontController.java


[첨부(Attachments)]

FrontController.zip



11. Controller - BoardInsertController.java


BoardInsertController의 핵심 내용은 "insert.jsp" 파일을 불러오는 데 있다.

없어도 된다고 생각했는데, 기능이 많아질 경우를 대비한다면, 있는 것이 낫다고 생각했다.


package com.fileWeb.controller;


import java.io.IOException;

import java.util.HashMap;

import java.util.Map;


import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


public class BoardInsertController implements Controller {


@Override

public void execute(HttpServletRequest req, HttpServletResponse res) throws

ServletException, IOException {

HttpUtil.forward(req, res, "/WEB-INF/view/board/insert.jsp");

}


}



파일명: BoardInsertController.java


[첨부(Attachments)]

BoardInsertController.zip



12. View - board/insert.jsp


삽입 페이지에 대한 것이다.

구현 목표는 두 가지에 대한 실험이다.


하나는 순수한 POST 처리 방식이고, 하나는 POST기반의 multipart/form-data 방식에 관한 것이다.


실험 결과를 먼저 소개하면, 

1. multipart/form-data를 정의해버리면, 기본 정의된 request로는 parameter를 전송받을 수가 없다.


* com.oreilly.servlet   (결과: 오래된 프로젝트 / 서블릿 버전 충돌 발생)

  - http://www.servlets.com/cos/


다양한 검색을 통해서 MultipartRequest 정의를 활용해서 이 문제를 oreilly.Servlet으로 해결할 수 있다는 글을 봐서 적용하였으나 
서블릿 충돌 문제가 발생하였다. (사용할 수 없는 오래된 라이브러리)

2. Apache Common-io, Apache Common-FileUpload를 적용하면, 해결할 수 있다. (2020-09-24일 기준)



<%@ 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>


<!-- 파일 업로드 화면 -->

<!-- 일반적으로 Multipart form으로 전송된 데이터는

      일반 request 메서드로 받아올 수 없습니다.  enctype="multipart/form-data" -->

<form method="post" action="insertResult.do" >

<table style="width:700px;border:1px solid #e2e2e2;">

<tr>

<td style="width:20%">

파일명

</td>

<td>

<input type="text" name="usrID" size="10">

<input type="file" name="uploadFile" multiple>

</td>

</tr>

<tr>

<td colspan="2">

<input type="submit" value="전송">

</td>

</tr>

</table>

</form>



<!-- 멀티파트/데이터 전송 -->

<form method="post" action="insertMultiResult.do" enctype="multipart/form-data" >

<table style="width:700px;border:1px solid #e2e2e2;">

<tr>

<td style="width:20%">

파일명

</td>

<td>

<input type="text" name="usrID" size="10">

<input type="password" name="usrPasswd" size="10">

<input type="file" name="uploadFile" multiple>

<input type="file" name="uploadFile" multiple>

<input type="file" name="uploadFile" multiple>

</td>

</tr>

<tr>

<td colspan="2">

<input type="submit" value="전송">

</td>

</tr>

</table>

</form>


</body>

</html>


파일명: insert.jsp


[첨부(Attachments)]

insert.zip




13. Controller - BoardInsertResultController


multipart/form-data를 미지원하는 타입으로 결과를 출력하는 방법에 대하여 작성하였다.


package com.fileWeb.controller;


import java.io.IOException;

import java.util.HashMap;

import java.util.Map;


import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


public class BoardInsertResultController implements Controller {


@Override

public void execute(HttpServletRequest req, HttpServletResponse res) throws

ServletException, IOException {


Map<String, Object> reqMap = new HashMap<String, Object>();

reqMap.put("usrID", req.getParameter("usrID"));

reqMap.put("usrPasswd", req.getParameter("filename"));


            req.setAttribute("reqMap", reqMap);

        

HttpUtil.forward(req, res, "/WEB-INF/view/board/insertResult.jsp");

}


}


파일명: BoardInsertResultController.jsp


[첨부(Attachments)]

BoardInsertResultController.zip



2부에서 만나요.


양이 조금 많아서 2부에서 글을 이어서 소개하려고 한다.


- [JSP] 12. Jsp/Servlet(MVC) Maven 기반의 다중 파일 업로드, 다운로드 구현(2), 2020-09-24

   https://yyman.tistory.com/1415


반응형
728x90
300x250

[JSP] 11. WAR 배포하기 - 웹 사이트 올리기


개발을 하였으면, 사용자가 접속할 수 있도록 배포를 해주어야 한다.

기존의 Apache Web Server나 Microsoft IIS 서버의 경우에는 프로젝트 그대로 올려주면 되는데, JSP는 두 가지 방법을 지원하고 있다.


WAR 형태 파일로 압축하여 서버에 올리는 방법이 있다.




1. 리눅스, 윈도우 환경에서 "Eclipse IDE(Spring Tool-Suite 4)"로 개발했을 때


1. 배포할 spring 프로젝트를 우클릭하여 export를 선택한다.

2. export 메뉴가 나오면 Web 카테고리 안의 WAR file을 선택하고 Next를 누른다.

3. Destination에 war파일을 저장할 위치를 지정해준다. (tomcat이 설치된 폴더의 webapps 폴더로 지정해주면 편리하다)


4. Export source files와 Overwrite existing file 항목을 체크해준다.

5. 마지막으로 finish를 누르면 war파일이 생성된다.



그림 1. File 메뉴 모습


File -> Export를 클릭한다.



그림 2. Eclipse의 Export 작업


"Web 폴더"의 "War file"을 선택한 후, "Next"를 누른다.



그림 3. Eclipse의 Export 작업


작업한 웹 프로젝트를 선택하고 "Browse"를 누른다.



그림 4. Eclipse의 Export 작업


배포할 파일의 내보낼 위치를 선택한다.

저장(S)를 누른다.



그림 5. Eclipse의 Export 작업


"Finish"를 누른다.




2. Windows(윈도우) 환경에서의 아파치 서버에 배포하기


{tomcat 설치 경로}\webapps 폴더에 생성한 war 파일을 복사한다.

{tomcat 설치 경로}/bin 으로 들어가서 startup.bat을 실행하면 tomcat이 시작된다.


tomcat을 시작하면, 자동으로 프로젝트 이름과 동일한 폴더가 생성되면서 war 파일로 묶여있던 패키지가 풀린다.


웹브라우저에서 localhost:8080이나 아이피주소:8080로 해당 프로젝트에 접속하면 된다. 

프로젝트명이 test인 경우 http://localhost:8080/test 로 접속하면 spring 프로젝트에 접속된다.




그림 6. 배포 폴더에 war 파일 모습


webapps폴더에 war 파일을 붙여넣는다.



그림 7. bin의 startup.bat(Apache Tomcat 9)


bin폴더에 있는 startup.bat을 실행하여 웹 서버를 실행해준다.



그림 8. Apache Tomcat 9 - 서버 가동중




그림 9. 웹 브라우저에서 확인하기


웹 브라우저에서 접속하여 확인하면, 동작하는 것을 알 수 있다.



3. Linux(Ubuntu) 환경에서 배포


마찬가지로 tomcat이 설치된 디렉토리의 webapps 디렉토리에 war 파일을 복사하고 tomcat을 재시작 해주면 된다.

(/var/lib/tomcat{버전}/webapps/ 디렉토리에 복사한다.)


tomcat을 재실행한다.

Windows와 동일하게 tomcat을 시작하면 자동으로 프로젝트 이름과 동일한 폴더가 생성되면서 war 파일로 묶여있던 패키지가 풀린다.


$ sudo service tomcat{버전} restart


웹브라우저에서 해당 서버의 아이피주소:8080로 접속할 수 있다.


운영체제: 리눅스 우분투(Ubuntu-20 Desktop 64bit)


[명령어 요약]

user@localhost :sudo -i

root#apt update

(처음 우분투 설치하면, 업데이트 하라고 GUI화면에 올라옴. 재부팅을 몇 번 해보고 나서 작업하는 것을 권장함.)

root#apt install tomcat9

root#service tomcat9 start


(war 파일 경로 찾기)

#mv {파일명}.war /var/lib/tomcat9/webapps

(수동 컴파일 버전은 수동 설치 경로로 할 것)


(잠시 기다리고 나서 1분 이내)

* 파이어폭스 열어서 접속할 것




그림 10. 우분투(리눅스)에서 톰캣9 - 설치하기




그림 11. 우분투(리눅스)에서 ifconfig 명령어 집합(net-tools) 설치 및 톰캣 실행하기 - 리눅스




그림 12. 파이어폭스에서 localhost 구경하기 - 리눅스





그림 13. 톰캣 설치 경로 찾기 - 리눅스






그림 14. war 파일 복사 및 경로 이동하기 - 리눅스





그림 15. 루트에 배포된 war 파일 모습 - 리눅스






4. 루트 경로에 배포하기


* ROOT에 war파일 배포할 경우


1. 프로젝트 war파일 생성


- Eclipse에서 프로젝트 우클릭 -> Export - Web - WAR file 선택 -> Next버튼 클릭


- Destination에 war파일 생성될 위치 지정하고, 파일명은 반드시 'ROOT.war'로 입력한 후 Finish버튼 클릭


* 파일명을 꼭 ROOT로 해야하는 것은 아니지만 ROOT가 아닌 다른 걸로 하려면 별도의 설정이 필요함


2. Tomcat 폴더/webapps/ROOT 폴더의 이름을 변경


* Tomcat을 설치하면 위 경로에 ROOT폴더가 있는데 이 폴더가 존재하면 ROOT.war가 압축해제되지 않음


3. Tomcat 폴더/webapps 폴더에 생성된 ROOT.war파일 이동



그림 16. root.war 파일 설치하기


초기에 ROOT폴더는 이름을 변경해서 둔다.



그림 17. 서버 실행하기


bin폴더에 들어가서 startup.bat을 실행한다.


그림 18. 루트 홈페이지 모습


"서버주소:포트"로 홈페이지에 접속하면, 루트 홈페이지로 풀린 프로젝트를 살펴볼 수 있다.



그림 19. 서버 동작 후, 프로젝트 컴파일된 모습


서버 동작 시키고 나서 webapps 폴더를 관찰해보면, ROOT 폴더가 새로 생겨있는 것을 살펴 볼 수 있다.



* 참고자료(References)


1. Spring 프로젝트 배포하기(tomcat 이용), https://attackyourheart.tistory.com/50, Accessed by 2020-09-22, Last Modified 2015-01-20.

2. Tomcat에 war파일 배포하는 방법, https://its-easy.tistory.com/4, Accessed by 2020-09-22, Last Modified 2018-02-13.

반응형
728x90
300x250

[JSP] 10. JSP 폴더 구성에 대한 것


이전 게시글을 따라 수행하면, 허점이 하나 있다는 것을 알 수 있다.

뷰 파일로 만든 .jsp파일을 직접 주소만 알게 되면, 접근할 수도 있다는 취약점이 있다.


1. [JSP] 9. MVC 디자인 패턴 - 초간단 이해, FrontController 패턴(1), 2020. 9. 20. 17:24

   https://yyman.tistory.com/1401


이러한 점을 설명하면서 알아두면 좋은 폴더의 구성에 대해서 소개하려고 한다.



[JSP 폴더에 대한 설명]


1) 프로젝트명/java resources/src/

   servlet, Class, Interface 등 예를 들면 java파일을 담는 곳

    - src 밑에 폴더 만들어서 작성  

      예) someFolder, SomeClass.java 식으로 사용


프로젝트를 개설해보면, 일부 살짝 차이가 있을 수 있겠으나 통상적으로 비슷하다.


2) 프로젝트명 "webContent"  

   - jsp 문서를 작성하면 된다.


3) 프로젝트명/webContent/WEB-INF/ 

   - 아래는 접근이 안됨, 보안 영역임.

    - 프로젝트명/webContent/WEB-INF/lib/  폴더에 각종

      jar 파일, ojdbc6 등을 붙여 넣어야 실행이 가능함.


※ jsp는 주소가 노출되어 있어서 공격을 받을 수 있다

    그래서, 

    @webServlet("*.do")와  같은 URL Mapping 어노테이션을 사용하여 URL 숨김 효과를 본다.

    또는 web.xml 에 정의하여 사용.





2. 9번 글 - 프로젝트에 적용해보기


WebContest에 있는 addressInsert.jsp와 addressList.jsp를 WEB-INF안에 view 폴더를 만들어서 이동시킨다.



그림 1) WEB-INF\view 폴더에 있는 jsp파일 실행시키기


jsp파일 경로를 입력하였으나 동작되지 않는 것을 확인할 수 있다.



2-1. 소스코드 변경하기


서블릿으로 WEB-INF\View에 있는 jsp파일을 사용하려면, 

AddressInsertController.java와 AddressListController.java의 파일 내용을 수정한다.



그림 2) 수정된 AddressInsert.java 파일



그림 3) 수정된 AddressList.java 파일


package com.eduJsp.controller;


import java.io.IOException;


import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


public class AddressInsertController implements Controller {


@Override

public void execute(HttpServletRequest req, HttpServletResponse res) throws

ServletException, IOException{


HttpUtil.forward(req, res, "/WEB-INF/view/addressInsert.jsp");

}


}




package com.eduJsp.controller;


import java.io.IOException;


import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


public class AddressListController implements Controller {


@Override

public void execute(HttpServletRequest req, HttpServletResponse res) throws

ServletException, IOException {

HttpUtil.forward(req, res, "/WEB-INF/view/addressList.jsp");

}


}




3. 결과


서블릿을 통해서 결과를 확인해보면, 이해할 수 있을 것으로 보인다.



그림 4) Servlet 파일(*.do) - 호출하기



4. 작업내용 정리해보기


변경작업을 수행한 이력을 체크해본 것이다. 조금 알기 쉽게 표기해보았으니 참고하여 정리해보면 좋을 듯 하다.



그림 5) 프로젝트 구조 - 변경 사항


반응형
728x90
300x250

[JSP] 9. MVC 디자인 패턴 - 초간단 이해, FrontController 패턴(1)


이전의 글을 보면, 순수하게 "JSP/서블릿"으로 처리하는 방법에 대해서 소개하고 있는데, 실질적으로 개발에서 자주 사용할 수 있는 패턴에 대해서 소개해보려고 한다.


기존 개발에 대해서 다시 한번 생각해보도록 하자.



그림 1. 기존 개발방법론의 문제점 - 요약


기존 개발방법론을 살펴보면, 크게 JSP, Servlet 처리에 대해서 하나의 코드 안에 "개발 코드(액션 코드 또는 동적 코드)", "사용자 인터페이스(UI)" 등이 혼잡되어 구현되는 문제가 있을 수 있다.


Servlet을 사용해보면 알겠지만, 자바 코드 내에서 홈페이지 코드라고 불리는 html, css 등을 넣어준다는 게 보통 쉬운 일이 아니다.

물론 저레벨의 C언어 등으로 개발해보면, 언어 특성 때문에 어쩔 수 없이 MVC 개발이 어려운 경우가 있을 수도 있다.


이야기하고자 하는 것은 MVC패턴으로 개발을 하게 되는 배경은 "소프트웨어공학"에서 유지보수 차원의 문제부터 역할 전문화 등의 각종 공학적인 문제 때문에 패턴이라는 게 생겼을 것으로 보인다.


[첨부(Attachments)]

200920_Diagram1.pptx




1. 디자인 패턴이란?


디자인 패턴은 프로그램을 개발할 때 사용하는 일정한 뼈대(프레임) 또는 틀이라고 생각하면 된다.


디자인 패턴만 전문적으로 다루고 있는 참고서적이나 참고 사이트가 있는데 찾아보면 도움이 될 것이라고 본다.

(다소 처음에는 이해가 안 될 수도 있음. 개발을 조금 해봐야 필요성을 인지하게 된다.)





2. Model View Controller (MVC) 디자인 패턴


php나 asp 개발을 해보면, 디자인패턴이 초기에 바로 적용되진 않는다. 

물론 최근에는 aspx(asp.net)의 경우에는 model 1, model 2 느낌의 개발이 적용되어 있다고 본다.

이유는 간단한게 C#의 직접적인 경쟁자는 Java 프로그래밍 언어라는 점이다.


Java 진영의 JSP 개발을 하게 되면, MVC 패턴의 적용을 먼저 시작하고 들어가게 된다.


그림 2. MVC Model 1, MVC Model 2 구조


도식화된 그림으로 이해하고 있어도 무방하다. 그림 1에서 언급했던 이야기는 그림 2로서 정리해볼 수가 있다.


[첨부(Attachments)]

200920_Diagram2.pptx


현재에도 Model 1이 사용되는 이유로는 장점이 있다.

초기 개발 속도에 있어서는 빠를 수가 있다.

무슨 이야기냐면, 컨트롤러와 뷰 영역을 신경쓰지 않고 개발하기 때문에 어렵게 공부하고 개발할 필요가 없는 것이다.

JSP 코드나 Servlet 코드 내에 통합적으로 코드를 집어넣으면 되는데, 굳이 컨트롤러를 복잡하게 매번 신경쓰고 개발하지 않아도 되는
프로젝트가 있을 수 있다.

Model2는 설계가 어려운 반면, 유지보수로 갔을 때는 역할 구분이 쉽다는 장점을 가지고 있다.


이전의 방법과 현재의 방법에서 적절한 방법을 찾아서 적용하는 것도 중요하다고 본다.


* 모델은 서비스와 데이터베이스 처리를 담당하는 역할을 하며, 각 로직처리, DB 질의 처리 기능을 수행한다.

* 는 JSP, CSS, HTML를 사용하여 구현함. 

* 컨트롤러는 뷰와 모델을 중개하는 역할을 수행하며, 클라이언트가 전달한 파라마터를 추출하여 모델로 전달하고, 처리결과를 보여주는 기능을 한다.



3. Eclipse의 Dynamic Web Project로 구현해보기


Apache Maven을 활용하면, 속도감이 있게 구현할 수 있겠으나 원초적인 방법으로 구현해보려고 한다.



그림 3. Dynamic Web Project - 생생하기


New 메뉴에서 Project -> Others를 누르면 위의 화면이 뜬다.

Web 폴더에서 Dynamic Web Project를 선택한 후 Next를 누른다.




그림 4. Dynamic Web Project - 생생하기


프로젝트 명을 입력하고 Next를 누른다.

Finish를 눌러도 무방하나 web.xml 파일을 자동으로 생성하려고 한다.



그림 5. Dynamic Web Project - 생생하기


Next를 누른다.



그림 6. Dynamic Web Project - 생생하기


Generate web.xml deployment descriptor에 체크를 한 후 Finish를 누르면 web.xml이 포함된 신규 프로젝트가 생성되는 것을 확인할 수 있다.



그림 7. Dynamic Web Project - 생생하기


성공적으로 신규 프로젝트가 생성되었다.



4. web.xml 설정하기


Front Controller 패턴을 적용하기 위해서 Web.xml 파일을 살짝 수정해주어야 한다.



그림 8. web.xml 수정하기


<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">

  <display-name>edu-jsp-MVC</display-name>

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

  

<!-- Front Controller 패턴 -->

<servlet>

<servlet-name>front</servlet-name>

<servlet-class>com.eduJsp.controller.FrontController</servlet-class>

<init-param>

<param-name>charset</param-name>

<param-value>UTF-8</param-value>

</init-param>

</servlet>

<servlet-mapping>

<servlet-name>front</servlet-name>

<url-pattern>*.do</url-pattern>

</servlet-mapping>

  

</web-app>


[소스 코드]


[첨부(Attachments)]

web.zip




5. FrontController 생성하기(서블릿) - Controller


FrontController 패턴의 초기 컨트롤러를 생성하겠다.


그림 9. Servlet 만들기 - FrontController 생성


Java Resources를 오른쪽 버튼으로 클릭한다.

New-> Servlet을 클릭한다.



그림 10. Servlet 만들기 - FrontController 생성


Java Package 명은 com.eduJsp.controller로 입력한다.

Class Name 명은 FrontController로 입력한다.

Next를 누른다.



그림 11. Servlet 만들기 - FrontController 생성


Next를 누른다.



그림 12. Servlet 만들기 - FrontController 생성


init, service만 체크해준다.

Finish를 누른다.




그림 13. Servlet-API 복사, 붙여넣기 하기


servlet-api 파일을 apahce-tomcat 설치 경로에서 lib 폴더에서 찾아보면 파일이 있다.

프로젝트의 WEB-INF/lib 폴더에 "복사, 붙여넣기"를 해준다.





6. Controller 인터페이스 생성하기 - Controller


Controller에 대한 인터페이스에 관한 것이다.

공통적으로 사용할 인터페이스이니 지정을 한번 해주면 된다.



그림 14. Interface 만들기


"com.eduJsp...." 폴더에서 오른쪽 버튼을 클릭한다.

New->Interface를 클릭한다.



그림 15. Interface 만들기


"Name(파일명)"을 Controller로 입력한다.

Finish를 누른다.



그림 16. Interface 만들기


package com.eduJsp.controller;


import java.io.IOException;


import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


public interface Controller {


public void execute(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException;

}


* 파일명: Controller.java


[첨부(Attachments)]

Controller.zip




7. AddressInsertController와 AddressListController - Controller


이번에 수행할 작업은 앞서 6에서 생성한 Controller 인터페이스를 바탕으로 List, Insert 컨트롤러를 만들어보도록 하겠다.



그림 17. Controller 구현부 - 만들기


com.eduJsp 오른쪽 버튼 -> New -> Class를 누른다.




그림 18. Controller 구현부 - 만들기


두 개를 만들어야 함. (그림 17, 그림 18과정 - 반복)
- ClassName(클래스명): AddressInsertController

- ClassName(클래스명): AddressListController


클래스를 두개 만들어준다.

참고로 클래스를 생성할 때 Interfaces를 Add하여 Finish를 누르면 메서드(Method)가 자동으로 생성된다.



8. FrontController.java 코드 작성하기(서블릿) - Controller


앞서 작성한 컨트롤러(인터페이스)를 바탕으로 FrontController를 작성해보겠다.



그림 19. FrontController.java 코드 작성


package com.eduJsp.controller;


import java.io.IOException;

import java.util.HashMap;


import javax.servlet.ServletConfig;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


public class FrontController extends HttpServlet {

private static final long serialVersionUID = 1L;

    

private String charset = null;

private HashMap<String, Controller> list = null;

    public FrontController() {

        super();

    }


    @Override

public void init(ServletConfig sc) throws ServletException {

    charset = sc.getInitParameter("charset");

    list = new HashMap<String, Controller>();

   

    list.put("/board/insert.do", new AddressInsertController());

    list.put("/board/list.do", new AddressListController());

   

}


    @Override

protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {


    req.setCharacterEncoding(charset);

    String url = req.getRequestURI();

    String contextPath = req.getContextPath();

    String path = url.substring(contextPath.length());

   

    Controller subController = list.get(path);

    subController.execute(req, res);

   

}


}



* 파일명: FrontController.java


[첨부(Attachments)]

FrontController.zip




9. HttpUtil 코드 작성하기(서블릿) - Controller


RequestDispatcher가 해당 주제에서는 핵심이라고 보면 되겠다.

앞서 작성한 AddressListController나 AddressInsertController 등에서 jsp파일 경로 등을 요청했을 때, 작업을 처리해주는 역할을 수행한다.



그림 20. HttpUtil 코드 작성


com.eduJsp 폴더를 오른쪽 버튼한다.

New->Servlet을 클릭한다.




그림 21. HttpUtil 코드 작성


Class Name은 HttpUtil로 지정한다.

Next를 누른다.



그림 22. HttpUtil 코드 작성


Next를 누른다.




그림 23. HttpUtil 코드 작성


service만 체크하고 Finish를 누른다.

함수 원형을 생성하기 귀찮아서 하나만 선택하였다.

참고로 다음 작업에서는 함수 원형을 살짝 수정할 것이다.




그림 24. HttpUtil 코드 작성



package com.eduJsp.controller;


import java.io.IOException;


import javax.servlet.RequestDispatcher;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


public class HttpUtil extends HttpServlet {

private static final long serialVersionUID = 1L;

       

public static void forward(HttpServletRequest req, HttpServletResponse res, 

String path) throws ServletException, IOException {

try {

RequestDispatcher dispatcher = req.getRequestDispatcher(path);

dispatcher.forward(req, res);

}catch(Exception ex) {

System.out.println("forward 오류:" + ex);

}


}


}



* 파일명: HttpUtil.java


[첨부(Attachments)]

HttpUtil.zip




10. addressList.jsp, addressInsert.jsp 파일 생성하기 - View (뷰 영역 만들기)


이번 작업에서는 뷰 영역을 만들어주겠다.



그림 25. 뷰 생성하기


WebContent 폴더에서 오른쪽 버튼을 클릭한다.

New->JSP File을 클릭한다.




그림 26. 뷰 생성하기


File name은 addressList로 입력한 후 Finish를 누른다.



그림 27. 뷰 생성하기


File name은 addressInsert로 입력한 후 Finish를 누른다.


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

    pageEncoding="UTF-8"%>

<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8">

<title>목록 - MVC 페이지</title>

</head>

<body>

<h3>목록 - MVC 페이지</h3>

</body>

</html>


* 파일명: addressList.jsp


[첨부(Attachments)]

addressList.zip


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

    pageEncoding="UTF-8"%>

<!DOCTYPE html>

<html>

<head>

<meta charset="UTF-8">

<title>삽입 - MVC 페이지</title>

</head>

<body>

<h3>삽입 - MVC 페이지</h3>

</body>

</html>


* 파일명: addressInsert.jsp


[첨부(Attachments)]

addressInsert.zip



11. AddressInsertController.java, AddressListController.java


AddressInsertController.java, AddressListController.java 파일에 관한 것이다.


package com.eduJsp.controller;


import java.io.IOException;


import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


public class AddressInsertController implements Controller {


@Override

public void execute(HttpServletRequest req, HttpServletResponse res) throws

ServletException, IOException{


HttpUtil.forward(req, res, "/WEB-INF/view/addressInsert.jsp");

}


}


* 파일명: AddressInsertController.java


package com.eduJsp.controller;


import java.io.IOException;


import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


public class AddressListController implements Controller {


@Override

public void execute(HttpServletRequest req, HttpServletResponse res) throws

ServletException, IOException {

HttpUtil.forward(req, res, "/WEB-INF/view/addressList.jsp");

}


}


* 파일명: AddressListController.java



12. 결과


서버에 올려서 확인해보면, 아래와 같은 결과를 출력해볼 수 있다.



그림 28. 출력하기 - 결과(1)



그림 29. 출력하기 - 결과(2)



13. UML 설계(Controller 설계)


"bottom->top" 방식으로 UML 결과를 보여주도록 하겠다.



그림 30. UML 설계도 - edu-jspMVC 프로젝트


예를 들면, 이러한 설계도를 바탕으로 코드를 작성하기에 앞서 한번 더 생각해볼 수 있다.

물론 코드 학습에 있어서는 직접 짜봐야 감이 오는 건 분명하다.


* UML 작성 프로그램: Modelio Open Source 4.0



14. 질의처리 및 로직에 관한 영역 - Model 영역(선택)


모델은 서비스와 데이터베이스 처리를 담당하는 역할을 하며, 각 로직처리, DB 질의 처리 기능을 수행한다.

패키지를 기준으로 보면, com.edu-jspMVC.service에 해당된다고 보면 된다.


모델 영역은 데이터베이스를 사용하지 않거나 복잡한 로직을 구현할 일이 없다면, 선택으로 둬도 무방할 것 같다.


14-1. VO(Value Object) - Model 영역(선택)


정보를 저장할 목적으로 만든 VO(Value Object) 객체라고 정의할 수 있다.


* tableName(테이블명): addressbook


 키

 항목명

 속성 

 PK(기본키)

num

인덱스(ID)

 

name

nvarchar2(20)

 

address

nvarchar2(100)

 

birthdate

date


표 1. addressbook 테이블


package com.eduJsp.vo;


import java.sql.Date;


public class AddressVO {

private int num;

private String name;

private String address;

private Date birthdate;

public int getNum() {

return num;

}

public void setNum(int num) {

this.num = num;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public String getAddress() {

return address;

}

public void setAddress(String address) {

this.address = address;

}

public Date getBirthdate() {

return birthdate;

}

public void setBirthdate(Date birthdate) {

this.birthdate = birthdate;

}

}


* 파일명: AddressVO.java


[첨부(Attachments)]

AddressVO.zip




14-2. Service 설계 - Model 영역(선택)


클래스명은 "AddressService" 이런 형태로 파일을 구성하여 사용하면 된다.

인터페이스를 설계해도 무방하다.


package com.eduJsp.service;


import java.util.List;


import com.eduJsp.vo.AddressVO;


public interface IAddress {


public AddressVO getAddress(Integer num) ;

public List<AddressVO> allData();

public int insertAddress(AddressVO addressVO);

public int updateAddress(AddressVO addressVO);

public int deleteAddress(AddressVO addressVO);

}


* 파일명: IAddress.java


[첨부(Attachments)]

IAddress.zip


package com.eduJsp.service;


import java.util.List;

import com.eduJsp.vo.AddressVO;


public class AddressService implements IAddress{


@Override

public AddressVO getAddress(Integer num) {

return null;

}


@Override

public List<AddressVO> allData() {

// TODO Auto-generated method stub

return null;

}


@Override

public int insertAddress(AddressVO addressVO) {

// TODO Auto-generated method stub

return 0;

}


@Override

public int updateAddress(AddressVO addressVO) {

// TODO Auto-generated method stub

return 0;

}


@Override

public int deleteAddress(AddressVO addressVO) {

// TODO Auto-generated method stub

return 0;

}

}



* 파일명: AddressService.java


[첨부(Attachments)]

AddressService.zip




14-3. DAO(Data Access Object) - Model 영역(선택)


데이터베이스 처리에 관한 실질적인 코드를 구현하는 곳이다.

현재 프로그램들의 대부분은 DB를 사용하지만, 꼭 사용해야 하는 것은 아니다.


패키지를 기준으로 보면, "com.edu-jspMVC.dao"에 해당된다고 보면 된다.

예를 들면, 클래스명 "AddressDAO" 이런 형태로 파일을 구성하여 사용하면 된다.


여기 부분에 예를 들면, Resultset, Preparedstatement, Connection 등의 기능을 활용하여 SQL문장 등을 정의하는 것이다.


package com.eduJsp.dao;


import java.sql.Connection;

import java.sql.PreparedStatement;

import java.sql.ResultSet;


import com.eduJsp.vo.AddressVO;


public class AddressDAO {


    public AddressVO selectAddress(Integer num) {


    Connection conn = null;

    PreparedStatement pstmt = null;

    ResultSet rs = null;

   

    AddressVO node = new AddressVO();

   

    String sql = "select NUM, NAME, ADDRESS, BIRTHDATE " +

      " from addressbook" + 

      " where num=?";

    System.out.println(sql);

   

    // 달력 날짜 출력 버그 개선

   

    try {

    //conn = session.connect();

   

    pstmt = conn.prepareStatement(sql);

    pstmt.setInt(1, num);

   

    rs = pstmt.executeQuery();

   

    if ( rs.next() ) {

    node.setNum(rs.getInt(1));

    node.setName(rs.getNString(2));

    node.setAddress(rs.getNString(3));

    node.setBirthdate(rs.getDate(4));

    }

   

   

    }catch(Exception ex) {

    System.out.println("오류 발생: " + ex);

    }

    finally {

    // session.close(conn, pstmt, rs);

    }

   

        return node;


    }

}


* 파일명: AddressDAO.java


[첨부(Attachments)]

AddressDAO.zip




15. MVC 프로젝트 구성도


MVC 프로젝트를 구성한 모습이다.



그림 31. MVC Project 구성


그림 31처럼 이런 형태로 구성된다고 보면 되겠다.



16. 맺음글(Conclusion)


글을 다소 길게 쭉 작성했는데, MVC를 좀 더 정확하게 소개하고 싶어서 그런 점 이해하길 바란다.


MVC 패턴의 원리에 대해서 JSP/Servlet을 기반으로 소개하였다.

예를 들면, Spring Frameworks를 먼저 접하기보다는 순수한 부분에 대해서도 생각해봐야 한다고 본다.



* 참고자료(References)


반응형
728x90
300x250

[JSP] 8. 영속프레임워크 MyBatis를 활용한 CRUD 구현 - JSP와 Oracle(XML 방식)


조금 알기 쉽게 작성하였다.

"영속 프레임워크"라고 보면 된다.

영속 프레임워크라는 것은 DAO 객체에서 데이터베이스 데이터의 처리 기능을 제공하는 프레임워크이다.


대표적인 것: myBatis, Hibernate가 있다.


* 사용되는 언어: JSP / Servlet,
* 데이터베이스: Oracle 12 이상 (Oracle 18g에서 태스트 완료 하였음.)

* 프레임워크: mybatis-3.5.5

[첨부(Attachments)]

mybatis-3.5.5.jar

mybatis-3.5.5(원본).7z



1. 최소한 관련 프레임워크에 대해서 이해해보기


프레임워크의 특징에 대해서 간단하게 작성해보았다.


[특징]


myBatis란 그나마 학습하기 쉽고, 사용방법이 간단함.

초기 셋팅을 조금한 후에 나머지는 SQL명령문과 간단한 자바 소스코드로 구현하여 사용할 수 있음.

(iBatis 프로젝트로 시작해서 현재는 Apache Foundation에서 관리하는 자바 오픈소스 프레임워크)


http://www.mybatis.org


hibernate란 ORM(Object-Relational Mapping) 프레임워크라는 영속 프레임워크로서 자바와 객체와 데이터베이스를 매핑하여 데이터를 처리하므로 엔터프라이즈 환경에 적합한 특징을 가지고 있음.

(배우기 어려움. - 객체 모델링 경험이 요구됨.)


http://hibernate.org 



[생각해보기]


생각을 조금해본다면, 꼭 반드시 웹 프로젝트에만 MyBatis를 활용할 필요가 없음.
일반적인 Swing 프로젝트 연습 등에서도 사용해볼 수도 있겠음.

- 프레임워크 사용하기 전에 고민해야 할 점

1. 기본적으로 제공하는 JDBC 구현에 대해서도 생각해보기

(이유: 자바에서 자체적으로 제공하는 jdbc 구현(예: ResultSet, prepareStatement 등)에 대해서 이해하고 있으면 좋음.)


2. 프레임워크를 사용하는 것이 만능인지, 고민하기

3. SQL Injection 등의 보안 문제에 대해서 생각하기

   (프레임워크를 사용하면, SQL Injection 문제는 간단하게 해소된다.)


결론은 좋긴 좋다. 프레임워크!!!


[코드 비교하기] 


 Mybatis 사용전 코드 방식

 Mybatis 사용후 코드 방식

 

public Entity selectFAQList(UserConnection conn, Entity param)  throws SQLException 

{

    UserStatement stmt = null; //stmt 초기값 선언
    ResultSet rslt = null; //rslt 초기값 선언
    StringBuffer sql = new StringBuffer();
    sql.append("\n SELECT *"); //
    sql.append("\n FROM"); //코드 추가
    sql.append("\n TABLE1");
    stmt = conn.prepareStatement(sql.toString());
    rslt = stmt.executeQuery();
    Entity _DATA = new Entity();
    _DATA.put("_DATA", EntityUtil.ResultSetToClobList(rslt));
    return _DATA;
}

 <?xml version="1.0" encoding="UTF-8"?>

  <ENTITY id="table.getTable1List" type="SQL" return="List">
    <![CDATA[
        SELECT *
            FROM
            TABLE1
    ]]>
    <PARAMS>    
    </PARAMS>
  </ENTITY>

// xml로 빼내서 쿼리문을 작성하면 내부적 처리는 Mybatis에서 // 모두 처리해주므로
// Entity ID값을 java에서 호출만하면 된다.




2. 초기 환경설정 셋팅하기


시중 블로그를 다수 검색하고, 교제등을 참고하였으나 왕초보 수준으로 알기 쉽게 적용하는 방법은 나오지 않은 거 같아서 작성해보려고 한다.



그림 1. 초기 셋팅해줘야 하는 프로젝트 파일


최소 못해도 기본적으로 갖춰줘야 하는 소스 파일들을 몇 개 찝어보았다.


"Address_****.파일확장자명"으로 구성된 파일은 사용자에 따라 임의적으로 구성해봐도 무방하다.


ojdbc8_g.jar은 어디에 있는가?

오라클 설치파일이 있는 폴더에 보면 jdbc 폴더가 있는데 해당 위치에 있다.



그림 2. 오라클 설치 파일이 있는 폴더



그림 3. jdbc 폴더에 있는 readme.txt



그림 4. jdbc/lib 폴더 내에 있음.



[첨부(Attachments)]

oracle12lib.z01

oracle12lib.z02

oracle12lib.zip


(참고로 반디집을 통해서 압축을 풀 수 있음.)


소스코드를 통해서 살펴보는 것이 조금 빠를 수 있다고 주장해 본다.



3. 데이터베이스 설계하기


예제 데이터베이스를 설계하도록 하겠다.


tableName(테이블명): addressbook


 키

 항목명

 속성 

 PK(기본키)

num

인덱스(ID)

 

name

nvarchar

 

address

nvarchar

 

birthdate

date




그림 5. 테이블 만들기(오라클)


그림 5는 SQL Developer를 통해서 테이블을 생성한 모습이다. 

원래 복잡하게 쿼리라는 것을 통해서 작성해야 하는데, 세상이 시간이 지나다보니 편리해진 것도 있다고 주장한다.


CREATE TABLE "C##USER"."ADDRESSBOOK" 

   ( "NUM" NUMBER(*,0) NOT NULL ENABLE, 

"NAME" NVARCHAR2(20), 

"ADDRESS" NVARCHAR2(100), 

"BIRTHDATE" DATE, 

CONSTRAINT "ADDRESSBOOK_PK" PRIMARY KEY ("NUM")



  USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS 

  STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645

...............................

(중략)


ALTER TRIGGER "C##USER"."ADDRESSBOOK_TRG" ENABLE;



그림 6. SQL문으로 Create Table 구성하기


그림 6은 흔히 CRUD(Create Read Update Delete)에서의 Create를 의미하는 작업이다.

예를 들면 원래는 이렇게 복잡하게 작성해야 하는데 지금은 훨씬 편하게 만들 수 있다고 보면 된다.




4. 프로젝트 구성하기 - 과정 소개 (소스코드를 통해서 살펴보는 프로젝트 구성)


1단계 설계

SQL - 테이블 작성


2단계 기본 환경설정 작성(준비)

web.xml을 먼저 작성한다.              (암기가 되는 부분인가? 불가능. 이거 외우는 거 불가능.)

mybatis-config.xml                     (암기가 되는 부분인가? 불가능. 이거 외우는 거 불가능.)

SqlMapSessionFactory.java           (암기가 되는 부분인가? 불가능. 이거 외우는 거 불가능.)

AddressDto.java 

addressMapper.xml                    (암기가 되는 부분인가? 불가능. 이거 외우는 거 불가능.)


3단계(간단한 CRUD 템플릿 준비)

addressDao.java                       (암기가 되는 부분인가? 코드는 간단하나 2단계 셋팅이 안 되어 있으면 의미 없음.)


4단계(인터페이스 설계 및 구현부 작성)

address.java 

addressImpl.java


5단계(뷰 페이지 구성하기)

web.xml (수정 - 작업)

servlet 페이지로 진행 또는 jsp파일로 진행해도 무방




5. 소스코드


소스코드는 순서대로 소개하겠다.


(2단계 - 소스코드)


<?xml version="1.0" encoding="UTF-8"?>


<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"

version="3.1">


<display-name>edu-mybatis</display-name>

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

<!-- Board List -->

<servlet>

<servlet-name>boardList</servlet-name>

<servlet-class>com.edu.view.BoardListServlet</servlet-class>

</servlet>

<servlet-mapping>

<servlet-name>boardList</servlet-name>

<url-pattern>/board/list.do</url-pattern>

</servlet-mapping>

<!-- Board Insert -->

<servlet>

<servlet-name>boardInsert</servlet-name>

<servlet-class>com.edu.view.BoardInsertServlet</servlet-class>

</servlet>

<servlet-mapping>

<servlet-name>boardInsert</servlet-name>

<url-pattern>/board/insert.do</url-pattern>

</servlet-mapping>

<!-- Board Delete -->

<servlet>

<servlet-name>boardDelete</servlet-name>

<servlet-class>com.edu.view.BoardDeleteServlet</servlet-class>

</servlet>

<servlet-mapping>

<servlet-name>boardDelete</servlet-name>

<url-pattern>/board/delete.do</url-pattern>

</servlet-mapping>

</web-app>


* 파일명: web.xml


[첨부(Attachments)]

web.zip



예를 들면, 이런 형태로 구성해서 사용할 수 있다.

크게 어렵게 작성하진 않았으니 참고하면 도움이 될 것 같다.


<?xml version="1.0" encoding="UTF-8"?>


<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 

"http://mybatis.org/dtd/mybatis-3-config.dtd">


<configuration>


<environments default="development">

    <environment id="development">

      <transactionManager type="JDBC"/>

      <dataSource type="POOLED">

        <property name="driver" value="oracle.jdbc.driver.OracleDriver" />

        <property name="url" value="jdbc:oracle:thin:@127.0.0.1:1521:orcl"/>

        <property name="username" value="사용자 계정"/>

        <property name="password" value="비밀번호"/>

      </dataSource>

    </environment>

  </environments>


  <mappers>

    <mapper resource="com/edu/db/addressMapper.xml"/>              // 리소스 증가시 추가해서 사용해보기

  </mappers>


</configuration>


* 파일명: mybatis-config.xml



[첨부(Attachments)]

mybatis-config.zip



package com.edu.db;


import java.io.IOException;

import java.io.InputStream;

import org.apache.ibatis.io.Resources;

import org.apache.ibatis.session.SqlSessionFactory;

import org.apache.ibatis.session.SqlSessionFactoryBuilder;


public class SqlMapSessionFactory {

public static SqlSessionFactory ssf;


    static{


        String resource = "com/edu/db/mybatis-config.xml";

        InputStream inputStream = null;


        try {

            inputStream = Resources.getResourceAsStream(resource);

        } catch (IOException e) {

            e.printStackTrace();

        }


        ssf = new SqlSessionFactoryBuilder().build(inputStream);


    }

    

    public static SqlSessionFactory getSqlSessionFactory(){

        return ssf;

    }

    

}



* 파일명: SqlMapSessionFactory.java


[첨부(Attachments)]

SqlMapSessionFactory.zip


package com.edu.db;


import java.sql.Timestamp;


public class AddressDto {

private int num;

private String name;

private String address;

private Timestamp birthdate;

public int getNum() {

return num;

}

public void setNum(int num) {

this.num = num;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public String getAddress() {

return address;

}

public void setAddress(String address) {

this.address = address;

}

public Timestamp getBirthdate() {

return birthdate;

}

public void setBirthdate(Timestamp birthdate) {

this.birthdate = birthdate;

}

}



* 파일명: AddressDto.java


[첨부(Attachments)]

AddressDto.zip


<?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="com.edu.db.mappers.addressMapper">

<select id="allAddress" resultType="com.edu.db.AddressDto">

select * from addressbook

</select>


<select id="selectAddress" parameterType="Integer" resultType="com.edu.db.AddressDto">

select NUM, NAME, ADDRESS, BIRTHDATE

from addressbook

  where num=#{num}

</select>


<insert id="insertAddress" parameterType="com.edu.db.AddressDto">

insert into

addressbook(NAME, ADDRESS, BIRTHDATE)

values

(#{name},#{address},#{birthdate})

</insert>


<delete id="deleteAddress" parameterType="Integer">

DELETE FROM AddressBook

WHERE NUM = #{num}

</delete>

<update id="updateAddress" parameterType="com.edu.db.AddressDto" >

update addressbook

set birthdate = #{birthdate}, name = #{name}, address =#{address}

where num = #{num}

</update>

</mapper>


* 파일명: addressMappper.xml


[첨부(Attachments)]

addressMapper.zip


(3단계 - 소스코드)


package com.edu.db;

import java.io.*;

import java.util.*;

import org.apache.ibatis.io.*;

import org.apache.ibatis.session.*;


public class AddressDao {

private AddressDao() {}

    private static AddressDao dao;


    public static AddressDao getInstance(){


        if(dao == null){

            dao = new AddressDao();

        }


        return dao;

    }

    

    // SQL 세션 열기

    SqlSessionFactory factory = SqlMapSessionFactory.getSqlSessionFactory();


    public AddressDto selectAddress(Integer num) {


        SqlSession session = factory.openSession();


        AddressDto addressDTO = session.selectOne("com.edu.db.mappers.addressMapper.selectAddress", num);

        session.close();


        return addressDTO;


    }


 


    public int updateAddress(AddressDto addressDTO) {


        SqlSession session = factory.openSession();


        int update = session.update("com.edu.db.mappers.addressMapper.updateAddress", addressDTO);


        // update나 delete의 경우 반드시 커밋 필요.

        // session.commit();을 해주거나 factory.openSession(true);로 설정하면 자동 커밋된다.

        session.commit(); 

        session.close();


        return update;


    }

    

    

    public int insertAddress(AddressDto addressDTO) {

   

    SqlSession session = factory.openSession();

    int insert = session.insert("com.edu.db.mappers.addressMapper.insertAddress", addressDTO);

   

    session.commit();

    session.close();

   

    return insert;

   

    }

    

    public int deleteAddress(Integer num) {

   

    SqlSession session = factory.openSession();

    int delete = session.delete("com.edu.db.mappers.addressMapper.deleteAddress", num);

   

    session.commit();

    session.close();

   

    return delete;

    }


}


* 파일명: AddressDao.java


특징: Singleton 패턴을 적용함.


[첨부(Attachments)]

AddressDao.zip



(4단계 - 소스코드)


package com.edu.db;


public interface Address {


public AddressDto getAddress(Integer num);

public int updateAddress(AddressDto addressDTO);

public int insertAddress(AddressDto addressDTO); 

public int deleteAddress(Integer num);

}


* 파일명: Address.java


특징: Interface 파일


[첨부(Attachments)]

Address.zip


package com.edu.db;


public class AddressImpl implements Address {


AddressDao dao = AddressDao.getInstance();

@Override

public AddressDto getAddress(Integer num) {

return dao.selectAddress(num);

}


@Override

public int updateAddress(AddressDto addressDTO) {

return dao.updateAddress(addressDTO);

}

@Override

public int insertAddress(AddressDto addressDTO) {

return dao.insertAddress(addressDTO);

}

@Override

public int deleteAddress(Integer num) {

return dao.deleteAddress(num);

}


}


* 파일명: AddressImpl.java


특징: Address 인터페이스 구현파일


[첨부(Attachments)]

AddressImpl.zip


(5단계 - Servlet 파일 예)


package com.edu.view;


import java.io.IOException;

import java.io.PrintWriter;

import java.sql.Timestamp;

import java.text.SimpleDateFormat;


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 com.edu.db.Address;

import com.edu.db.AddressDto;

import com.edu.db.AddressImpl;


/**

 * Servlet implementation class boardListServlet

 */

public class BoardListServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

       

    /**

     * @see HttpServlet#HttpServlet()

     */

    public BoardListServlet() {

        super();

    }


/**

* @see HttpServlet#doGet(HttpServletRequest req, HttpServletResponse res)

*/

protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

res.setContentType("text/html;charset=UTF-8");

PrintWriter out = res.getWriter();

out.print("<br/>");

AddressImpl address = new AddressImpl();

AddressDto addressDto = address.getAddress(1);

out.println("<html><head><title>CRUD - List2</title></head>");

out.println("<body><h2>MyBatis - List</h2>");

SimpleDateFormat format1 = new SimpleDateFormat ( "yyyy-MM-dd" );

String birthdate = format1.format(addressDto.getBirthdate());

out.print(addressDto.getNum() + "/" + addressDto.getName() + "/");

out.print(addressDto.getAddress() + "/" + birthdate);

out.print("<br/>");

out.println("</body></html>");

out.close();

}

/**

* @see HttpServlet#doPost(HttpServletRequest req, HttpServletResponse res)

*/

protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

doGet(req, res);

}


}



* 파일명: boardListServlet.java


어노테이션으로 맵핑하지 않고, web.xml에 URL 맵핑하였음.


[첨부(Attachments)]

BoardListServlet.zip


package com.edu.view;


import java.io.IOException;

import java.io.PrintWriter;

import java.sql.Timestamp;


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 com.edu.db.Address;

import com.edu.db.AddressDto;

import com.edu.db.AddressImpl;


/**

 * Servlet implementation class boardInsertServlet

 */

public class BoardInsertServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

       

    /**

     * @see HttpServlet#HttpServlet()

     */

    public BoardInsertServlet() {

        super();

    }


/**

* @see HttpServlet#doGet(HttpServletRequest req, HttpServletResponse res)

*/

protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

res.setContentType("text/html;charset=UTF-8");

PrintWriter out = res.getWriter();

out.print("<br/>");

AddressImpl address = new AddressImpl();

AddressDto dbNode = new AddressDto(); 

dbNode.setName("도도" + serialVersionUID);

dbNode.setAddress("행복시 행복동");

// 버그1: new Date() 사용안됨. (2020을 3920으로 인식함.) 

// 버그2: new Timestamp() 사용안됨. (2020을 3920으로 인식함.)

String userDate = "2020-02-01";

java.sql.Timestamp sqlDate = java.sql.Timestamp.valueOf(userDate);

dbNode.setBirthdate(sqlDate);

int result = address.insertAddress(dbNode);

AddressDto addressDto = address.getAddress(1);

out.println("<html><head><title>CRUD - Insert</title></head>");

out.println("<body><h2>MyBatis - Insert</h2>");

out.print("<br/>");

out.print("등록여부:" + result + "</br>");

out.print("<br/>");

out.print(addressDto.getNum() + "/" + addressDto.getName() + "/");

out.print(addressDto.getAddress() + "/" + addressDto.getBirthdate());

out.println("</body></html>");

out.close();

}

/**

* @see HttpServlet#doPost(HttpServletRequest req, HttpServletResponse res)

*/

protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

doGet(req, res);

}


}


* 파일명: boardInsertServlet.java


[첨부(Attachments)]

BoardInsertServlet.zip



package com.edu.view;


import java.io.IOException;

import java.io.PrintWriter;

import java.sql.Timestamp;


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 com.edu.db.Address;

import com.edu.db.AddressDto;

import com.edu.db.AddressImpl;


/**

 * Servlet implementation class boardInsertServlet

 */

public class BoardDeleteServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

       

    /**

     * @see HttpServlet#HttpServlet()

     */

    public BoardDeleteServlet() {

        super();

    }


/**

* @see HttpServlet#doGet(HttpServletRequest req, HttpServletResponse res)

*/

protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

res.setContentType("text/html;charset=UTF-8");

PrintWriter out = res.getWriter();

out.print("<br/>");

AddressImpl address = new AddressImpl();

int result = address.deleteAddress(5);

AddressDto addressDto = address.getAddress(5);

out.println("<html><head><title>CRUD - Delete</title></head>");

out.println("<body><h2>MyBatis - Delete</h2>");

out.print("<br/>");

out.print("삭제여부:" + result + "</br>");

out.print("<br/>");

if ( addressDto != null ) {

out.print(addressDto.getNum() + "/" + addressDto.getName() + "/");

out.print(addressDto.getAddress() + "/" + addressDto.getBirthdate());

}

out.println("</body></html>");

out.close();

}


/**

* @see HttpServlet#doPost(HttpServletRequest req, HttpServletResponse res)

*/

protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

doGet(req, res);

}


}



* 파일명: BoardDeleteServlet.java


[첨부(Attachments)]

BoardDeleteServlet.zip



package com.edu.view;


import java.io.IOException;

import java.io.PrintWriter;

import java.sql.Timestamp;


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 com.edu.db.AddressDto;

import com.edu.db.AddressImpl;


@WebServlet("/board/update.do")

public class BoardUpdateServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

       

    /**

     * @see HttpServlet#HttpServlet()

     */

    public BoardUpdateServlet() {

        super();

    }


/**

* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)

*/

protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

res.setContentType("text/html;charset=UTF-8");

PrintWriter out = res.getWriter();

out.print("<br/>");

AddressImpl address = new AddressImpl();

AddressDto dbNode = new AddressDto();

dbNode.setNum(3);

dbNode.setName("도도수정" + serialVersionUID);

dbNode.setAddress("행복시 행복동");


// 버그1: new Date() 사용안됨. (2020을 3920으로 인식함.) 

// 버그2: new Timestamp() 사용안됨. (2020을 3920으로 인식함.)

String userDate = "2020-07-01";

java.sql.Timestamp sqlDate = java.sql.Timestamp.valueOf(userDate);

dbNode.setBirthdate(sqlDate);

int result = address.updateAddress(dbNode);

AddressDto addressDto = address.getAddress(3);

out.println("<html><head><title>CRUD - Update</title></head>");

out.println("<body><h2>MyBatis - Update</h2>");

out.print("<br/>");

out.print("수정여부:" + result + "</br>");

out.print("<br/>");

out.print(addressDto.getNum() + "/" + addressDto.getName() + "/");

out.print(addressDto.getAddress() + "/" + addressDto.getBirthdate());

out.println("</body></html>");

out.close();

}


/**

* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)

*/

protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

doGet(req, res);

}


}



* 파일명: BoardUpdateServlet.java


특징: web.xml으로 맵핑하지 않고, @WebServlet으로 어노테이션을 사용하여 매핑처리함.


[첨부(Attachments)]

BoardUpdateServlet.zip




6. 맺음글


순수한 JSP 기반의 MyBatis 프레임워크 사용방법에 대해서 소개해보았다.

많은 도움이 되었으면 좋겠다.


1. [JSP] 19. MyBatis-3.5.5 와 Maven / Servlet 연동하기 (Oracle 19g) - Java 방식, 2020-10-01

https://yyman.tistory.com/1434



[참고 자료(Reference)]


1. [Java, OpenJDK] Timestamp 형식 변환, https://infotake.tistory.com/16, Accessed by 2020-09-18, Last Modified 2018-09-13.

-> 참고 이유: Timestamp 명령 사용방법 참고함.


2. 내가 그리는 세상  DB(mysql)에 timestamp로 저장된 값 java에서 불러오기_getTimestamp(), https://yoonka.tistory.com/450, Accessed by 2020-09-18, Last Modified 2013-10-28.


3. [JSP] Mybatis 사용하기  또리야 개발하자, https://ddoriya.tistory.com/entry/JSP-Mybatis-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0, Accessed by 2020-09-17, Last Modified 2014-11-18.

-> 추천(60점 이상): 이 사이트의 글만 가지고 따라하면, 완벽하게 동작이 되진 않음. 몇 가지 보충을 해줘야 해서 추가적으로 검색하였음.
그래도 순수한 MyBatis 원리에 대해서 많은 영감을 얻었음.


4. Oracle와 mybatis 연동 실습, https://dlgkstjq623.tistory.com/228, Accessed by 2020-09-18, Last Modified 2019-06-05.

-> 추천(40점 이상): 이 사이트의 글을 이해하려면, Spring Framework 지식과 JSTL 지식이 있어야 함.

   기본적인 흐름 정도 파악하는데 참고하였음. (이론적인 느낌을 얻었음.)


5. MyBatis – 마이바티스 3 | 소개, https://mybatis.org/mybatis-3/ko/index.html, Accessed by 2020-09-18, Last Modified 2020-06-05.

-> 추천(20점 이상): 설명서가 조금 어렵게 되어 있음. 셋팅 파일을 가지고 셋팅한 후 설명서를 참고하면 도움이 될 수도 있음.


6. 4. Spring Boot Oracle DB 연동(JSP, MyBatis), https://dotheright.tistory.com/173, Accessed by 2020-09-18, Last Modified 2020-07-15.

-> 삽질하여 실패와 탐구 등의 횟수 기록이 담겨져 있음. ("삽질을 하게 된다.??" 이런 느낌을 보여줌.)

-> Spring Framework 기반으로 만든 소스코드를 소개하고 있음. (이 게시글 작성에 있어서 크게 영향을 주지 못함.)


7. [JSP] JSP에 MyBatis 연결하기, https://yuja-kong.tistory.com/8, Accessed by 2020-09-18, Last Modified 2018-04-17.

-> 추천(50점 이상): 순수한 JSP와 MyBatis 연결에 대해서 소개하고 있는데, 이 글을 작성할 수 있는 가능성을 만들어 줌.


8. JSP 서블릿 한글 세팅(한글 깨짐 해결 하기), https://developsd.tistory.com/100, Accessed by 2020-09-18, Last Modified 2019-05-18.

-> 추천(20점): 오라클DB(이하 "UTF-8 셋팅한 오라클")로 데이터를 불러오려고 했을 때, ?????라고 한글을 인식하지 못했음.

   간단한 명령어 하나로 해결하는 데 도움을 받았음. (가끔은 쉬운 게 어렵고, 어려운 게 쉬울 때도 있다는 생각이 듬.)


1. 파일의 인코딩 속성을 "UTF-8"으로 변경

2. response.setContentType("text/html;charset=UTF-8");


반응형
728x90
300x250

[JSP] 7. JSP, Servlet(JDBC) 프로그래밍

 

간결하고 쉽게 작성해보았다.

일반 게시판에서 사용되는 페이징 기법은 제거하고 JSP, Servlet(서블릿) 사용 방법에 대해서 소개한다.

(I tried to write concise and easy.)

(Removes the paging technique used in general bulletin board and introduces how to use JSP, Servlet.)

 

 

* Apache Tomcat-9.0.27
* Microsoft Windows 10
* MariaDB 10.4

 


1. 아파치 톰캣 실행하기

 

톰캣을 실행한다. startup.bat 배치 파일을 실행하면 된다.

(Run Tomcat. Just run the startup.bat batch file.)

 

 

Figure 1) Tomcat folder

그림 1) 톰캣 폴더의 모습

 

 

Figure 2) Tomcat external library folder

그림 2) 톰캣 외부 라이브러리 폴더

 

"C:\apache-tomcat-9.0.27\lib"는 tomcat의 서블릿, DBCP, Catalina 등이 담겨져 있는 라이브러리 폴더이다.

("C:\apache-tomcat-9.0.27\lib" is a library folder containing tomcat's servlets, DBCP, Catalina, and so on.)

 

 

Figure 3) Tomcat run

그림 3) 톰캣 실행의 모습

 


2. 이클립스의 모습(Eclipse)

 

이클립스 웹 작업에 있어서 가장 중요한 것은 아무래도 환경설정을 꼽겠다.

(Perhaps the most important thing when working with Eclipse web is configuration.)

 

 

Figure 4) Programming work in Eclipse

그림 4) 이클립스에서의 프로그래밍 작업 모습

 

 

 

Figure 5) Adding "MariaDB-Java-client-2.4.4.jar" and "Tomcat Jars" to Libraries tab in Java Build Path

그림 5) Java Build Path에 Libraries 탭에 "MariaDB-Java-client-2.4.4.jar", "Tomcat Jars" 추가 모습

 

[첨부(Attachment)]

mariadb-java-client-2.4.4.jar

 

초기 작업할 수 있도록 프로젝트 파일도 첨부해서 올려놓도록 하겠다.

(We will also attach a project file for the initial work.)

 

[첨부(Attachment)]

Stone.z01

Stone.z02

Stone.z03

Stone.zip

 


3. HeidiSQL 10.2.0.5599에서 게시판 DB 설계하기(Designing board DB in HeidiSQL 10.2.0.5599)

 

예제에 사용한 게시판 DB 설계를 하였다.

가볍게 따라서 작성하면 될 것이다.

(Designed bulletin board DB used in the example.)

(Simply follow along.)

 

 

Figure 6) "board_list" Board Design Example

그림 6) "board_list" 게시판 설계의 예

 


4. 프론트엔드에서 사용자 접속의 예(Example of User Connections at the Front End)

 

아래의 그림은 프론트엔드에서의 웹 사이트이다.(The figure below shows a Web site at the front end.)

http://localhost:8080/Stone/Board/list.jsp

http://localhost:8080/Stone/Board/write.jsp

 

Figure 7) Example of list.jsp

그림 7) list.jsp의 예

 

 

Figure 8) Example of write.jsp

그림 8) write.jsp의 예

반응형
728x90
300x250
[JSP] 6. Apache Tomcat 9 기반으로 한 Eclipse에서 Servlet 사용하기(Using servlets in Eclipse based on Apache Tomcat 9)

 

Apache Tomcat 9와 Eclipse Java 2019-09를 활용한 Dynamic Web Project를 하나 진행하도록 하겠다.

해당 예제는 톰캣에 탑재되어 있는 기본 예제로 진행하였다.

(The Apache Tomcat 9 and the Eclipse Java 2019-09 A Dynamic Web Project will utilize the proceeds to one.

This example is a basic example of Tomcat.)

 


1. 실습 환경(Environmental Practice)

 

1. MS윈도우 10 1909(MS Windows 10 1909)

2. Apache Tomcat 9

3. Eclipse Java 2019-09

4. OpenJDK 13

 


2. 프로젝트 생성하기(Create a project)

 

프로젝트를 아래의 그림처럼 생성한다.(Create a project as shown below.)

 

 

 


3. Servlet 생성하기

 

서블랫에 대해서는 다음에 소개하도록 하겠다.

(I'll introduce you to the Servlet later.)

 

 

Java Resources 탭에서 src 폴더를 오른쪽 버튼을 클릭한다.

(Right-click the src folder on the Java Resources tab.)

 

 

그리고 New-> Servlet을 클릭한다.

(Then click New-> Servlet.)

 

 

 

 

 

 

 

 


4. Apache Tomcat 9의 Servlet API 찾기(Finding the Servlet API for Apache Tomcat 9)

 

서블랫의 경로는 아래의 그림처럼 존재한다.

C:\{Apache 설치경로}\lib\servlet-api.jar

(The servlet's path exists as shown below.

C:\{Apache installation path}\lib\servlet-api.jar)

 

 

 


5. 이클립스에 적용하기(Apply to Eclipse)

 

이클립스에 적용하는 방법이다. {프로젝트명} 폴더를 클릭 후 오른쪽 버튼을 클릭한다.

그리고 "Properties"를 클릭한다.

(This is how you apply it to Eclipse.

Right click on the {Project Name} folder.Then click "Properties".)

 

 

 

 

 


6. 소스코드 구현하기(Implement the source code)

 

소스코드를 작성해주면 된다.(Write the source code.)

 

 


7. 디버그 및 태스트하기

 

Eclipse Java 2019-09부터는 Server에 Apache Tomcat이 사라졌다.

(Starting with Eclipse Java 2019-09, Apache Tomcat disappeared from Server.)

 

 


7-1. WAS에 WAR 배포하기

 

WAS(이하 "Web Application Server")에 방금 작성한 자바 프로젝트를 배포하는 방법이다.

(How to deploy a Java project you just created to WAS ("Web Application Server").)

 

 

File->Export를 클릭한다.(Click File-> Export.)

 

 

 

 

배포 위치는 c:\apache-tomcat-***\webapps에 넣어주면 된다.

(The distribution location is in "c:\apache-tomcat-***\webapps.")

 

 


8. 웹 사이트 접속해보기(Go to the website)

 

웹 사이트에 접속하면 확인해볼 수 있다.(You can check it by visiting the website.)

 

반응형

+ Recent posts