개발 환경
  - J2SE
    JDK 8 Update 291
  - DBMS
    Oracle 18c
  - WAS(Web Container)
    Apache Tomcat 8.5
  - Servlet/JSP Spec
    Servlet 3.1 / JSP 2.3
  - 소프트웨어 형상관리(Software Configuration Management)
    GIT
  - 기타
    javascript, AJAX, JSON, jQuery, CSS3, HTML5 

 

JSP/Servlet으로 이번에는 UI도 만들어서 웹 프로그래밍을 팀플로 진행해보았다 :) 기능을 만들 때 생각처럼 되지 않는 것들도 있었지만 어쨌든 팀원들과 함께 프로젝트를 완성해서 뜻깊은 경험이었다.!

 

커스텀 태그(Custom Tag, 사용자 정의 태그) 이해

 

커스텀 태그(Custom Tag, 사용자 정의 태그)란?

- JSP 언어의 확장인 커스텀 태그는 동적 기능을 캡슐화하는 메커니즘을 제공한다.

- 커스텀 태그는 사용자 정의 JSP 요소로 특정 작업을 수행하기 위하여 사용자에 의해 만들어진 태그이다.

- JSTL도 커스텀 태그의 일정으로 JSP 페이지 개발 및 유지 관리를 단순화한다.

 

커스텀 태그 장점

- 재 사용성 향상 : 한번 작성한 커스텀 태그는 어떤 JSP 컨테이너에서든지 사용 가능하다.

- 쉽고 단순한 JSP 제작 : 자바 코드에 익숙하지 않은 개발자들도 커스텀 태그를 통해서 쉽게 JSP 페이지를 작성할 수 있게 된다.

- 코드의 가독성 향상 : 스크립트 코드를 줄일 수 있기 때문에 JSP 코드의 가독성을 높일 수 있다.

 

커스텀 태그 종류

- JSP 1.2 기반의 커스텀 태그

- JSP 2.0 또는 그 이상 버전의 SimpleTag를 사용한 커스텀 태그

- JSP 2.0 또는 그 이상 버전의 태그 파일을 사용한 커스텀 태그

 

커스텀 태그 관련 인터페이스

javax.servlet.jsp.tagext_JspTag 인터페이스 

- 선언된 메소드는 없으며, Tag 및 SimpleTag 인터페이스의 상위 인터페이스이다.

 

javax.servlet.jsp.tagext.Tag 인터페이스

- JSP 1.2에서 제공하는 커스텀 태그를 구현할 경우에 사용한다.

- 단순한 태그 처리 시 사용하는 커스텀 태그이다.

- JspTag 인터페이스를 상속받았다.

 

javax.servlet.jsp.tagext.IterationTag 인터페이스

- JSP 1.2에서 제공하는 커스텀 태그

- 반복적인 작업을 처리하는 커스텀 태그이다.

- JspTag, Tag 인터페이스를 상속받앙ㅆ다.

 

javax.servlet.jsp.tagext.BodyTag 인터페이스

- JSP 1.2에서 제공하는 커스텀 태그를 구현할 경우에 사용한다.

- 태그의 body 내용이 있을 때 사용하는 커스텀 태그이다.

- InterationTag, JspTag, Tag 인터페이스를 상속 받았다.

 

JSP 1.2 기반 커스텀 태그 관련 인터페이스 및 클래스

javax.servlet.jsp.tagext.JspTag 인터페이스

- 선언된 메소드는 없으며, Tag 및 SimpleTag 인터페이스의 상위 인터페이스이다.

 

javax.servlet.jsp.tagext.Tag 인터페이스

- JSP 1.2에서 제공하는 커스텀 태그를 구현할 경우에 사용한다.

- 단순한 태그 처리 시 사용하는 커스텀 태그이다.

- JspTag 인터페이스를 상속받았다.

 

javax.servlet.jsp.tagext.IterationTag 인터페이스

- JSP 1.2에서 제공하는 커스텀 태그를 구현할 경우에 사용한다.

- 반복적인 작업을 처리하는 커스텀 태그이다.

- JspTag, Tag 인터페이스를 상속받았다.

 

javax.servlet.tagext.BodyTag 인터페이스

- JSP 1.2에서 제공하는 커스텀 태그를 구현할 경우에 사용한다.

- 태그의 body 내용이 있을 때 사용하는 커스텀 태그이다.

- IterationTag, JspTag, Tag 인터페이스를 상속받았다.

 

javax.servlet.jsp.tagext.TagSupport 클래스

- 커스텀 태그를 구현하기 위한 클래스의 최상위 클래스이다.

- IterationTag, JspTag, Tag 인터페이스를 구현한 클래스이다.

 

javax.servlet.jsp.tagext.BodyTagSupport 클래스

- 몸체(body)가 있는 커스텀 태그를 구현하기 위한 클래스의 상위 클래스이다.

- TagSupport 클래스의 하위 클래스이다.

- BodyTag, IterationTag, JspTag, Tag 인터페이스를 구현한 클래스이다.

 

JSP 2.0 기반 커스텀 태그 관련 인터페이스 및 클래스

 

javax.servlet.jsp.tagext.SimpleTag 인터페이스

- JSP 2.0 에서 제공하는 커스텀 태그 구현 시 사용한다.

- Tag, IterationTag 를 하나로 묶어서 좀 더 쉽게 구현이 가능한 커스텀 태그이다.

- JspTag 인터페이스를 상속 받았다.

 

javax.servlet.jsp.tagext.SimpleTagSupport 클래스

- SimpleTag를 구현하는 태그 핸들러를 정의하기 위한 상위 클래스이다.

- SimpleTag 인터페이스를 구현한 클래스이다.

 

javax.servlet.jsp.tagext.DynamicAttributes 

- 커스텀 태그가 동적 속성을 처리하기 위해서는 이 인터페이스를 구현해야 한다.

 

 

 

필터(filter)이해

 

필터란 ?

- 필터는 클라이언트 요청의 사전 처리 및 후 처리에 호출되는 객체로 요청을 변환하거나, 응답을 변경하는 기능을 수행한다.

- 필터는 주로, 인증, 로깅, 데이터 압축이나 변환, 요청 암호화 및 암호 해독, 입력 유효성 검사 등과 같은 필터링 작업을 수행하는 데 사용된다.

- 하나의 서블릿이나 JSP에 대한 요청이나 응답을 수정해야 하는 경우에는 필터를 만들 필요가 없으며, 여러 개의 서블릿에 대한 요청 및 응답에 사전 처리 또는 사후 처리를 적용하려는 경우에 작성한다.

- request 필터 : 요청(Request)이 백엔드(back end)의 자원(Servlet, JSP 등)에 도달하기 전에 필요한 전 처리를 한다.

- response 필터 : 서버에서 요청(Request)에 대한 처리를 한 후, 클라이언트에 응답(Response)하기 전에 후 처리를 한다.

- 필터들을 여러 개 사용 하면 연쇄적으로 사용할 수도 있다.

 

용도

- request 필터

: 인증 - 사용자 ID를 기반으로 요청을 차단

: 로깅 및 감사 - 웹 응용 프로그램의 사용자 추적(요청정보를 로그파일로 작성)

: 인코딩 작업 등

 

- response 필터

: 데이터 압축 - 다운로드 크기를 줄인다.

: XML 컨텐츠의 XSL/T 변환 (XSL/T : XML 문서를 다른 XML 문서로 변환하는데 사용하는 XML 기반 언어

: 총 서비스 시간측정 등

 

필터(filter) 관련 클래스 및 인터페이스

javax.servlet.Filter 인터페이스

- 필터 클래스는 javax.servlet.Filter 인터페이스를 구현하여 작성한다.

- 필터는 doFilter() 메소드에서 필터링을 수행한다.

- 모든 필터는 초기화 매개 변수를 얻을 수 있는 FilterConfig 객체에 액세스 할 수 있다.

 

javax.servlet.FilterChain 인터페이스 

- FilterChain은 자원에 대한 필터링된 요청의 호출 체인에 대한 뷰를 제공하는 개발자에게 서블릿 컨테이너가 제공하는 객체이다.

- 필터는 FilterChain을 사용하여 체인의 다음 필터를 호출하거나 호출하는 필터가 체인의 마지막 필터인 경우 체인의 끝에 있는 리소스를 호출한다. 

 

javax.servlet.ServletRequestWrapper 클래스

- ServletRequestWrapper 클래스는 ServletRequest 인터페이스에 정의된 모든 메소드를 구현한 클래스

- javax.servlet.http 패키지에 정의된 HttpServletRequestWrapper 클래스는 HttpServletRequest 인터페이스에 정의된 모든 메소드를 구현한 클래스

- ServletRequestWrapper와 HttpServletRequestWrapper 클래스의 메소드 중 변경해야 하는 메소드를 재정의하여 구현하고 필터의 doFilter() 메소드에 넘겨주면, 클라이언트의 요청 정보를 변경할 수 있는 래퍼 클래스이다.

 

javax.servlet.ServletResponseWrapper 클래스

- ServletResponseWrapper 클래스는 ServletResponse 인터페이스에 정의된 모든 메소드를 구현한 클래스

- javax.servlet.http 패키지에 정의된 HttpServletResponseWrapper 클래스는 HttpServletResponse 인터페이스에 정의된 모든 메소드를 구현한 클래스

- ServletResponseWrapper와 HttpServletResponseWrapper 클래스의 메소드 중 변경해야 하는 메소드를 재정의하여 구현하고 필터의 doFilter() 메소드에 넘겨주면, 응답 결과를 변경할 수 있는 wrapper 클래스이다.

 

필터(filter) 설정

형식 - 1 : web.xml 에서 설정

- 필터 인터페이스를 구현한 클래스를 작성한 후 서블릿 컨테이너가 필터를 찾아서 호출할 수 있도록 웹 애플리케이션 배포 기술자 파일(web.xml)에 필터를 등록한다.

<web-app ...>
	:
    <filter>
    	<filter-name>필터이름</filter-name>
        <filter-class>패키지명을 포함한 필터클래스 명</filter-class>
        [<init-param>
        	<param-name>파라미터이름</param-name>
            <param-value>파라미터값</param-value>
        </init-param>]
    </filter>
    <filter-mapping>
    	<filter-name>필터이름</filter-name>
        <url-pattern>경로패턴</url-pattern>
    </filter-mapping>
    :
</web-app>

 

형식 - 2 : @WebFilter Annotation으로 설정

- Servlet 3.0부터 제공되는 Annotation으로 필터 처리를 위한 필터 객체를 등록 한다.

- web.xml의 관련 태그 : <filter>, <filter-mapping>

@WebFilter(
	attribute1=value1,
    attribute2=value2,
    ...
)

	public class TheFilter implements favax.servlet.Filter {
    	// implements Filter's methods
    }

 

 

package com.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;

@WebFilter("/*")
public class TimerFilter implements Filter{
	private FilterConfig filterConfig;
	
	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		// 필터클래스의 객체가 생성될 때 한번 실행
		
		this.filterConfig = filterConfig;
	}
	
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		// 클라이언트의 요청이 있을때마다 실행
		
		// request 필터
		long begin = System.currentTimeMillis();
		
		// 다음 필터 또는 필터의 마지막이면 end-pointer(jsp, 서블릿)를 실행
		chain.doFilter(request, response);
		
		// response 필터
		long after =System.currentTimeMillis();
		
		String uri = "";
		if(request instanceof HttpServletRequest) {
			HttpServletRequest req = (HttpServletRequest)request;
			
			uri = req.getRequestURI();
			filterConfig.getServletContext().log(uri + " : " + (after - begin) + "ms");
		}
	}

	@Override
	public void destroy() {
		// 필터 클래스의 객체가 파괴될 때 한 번 실행
		
	}
	
}

 

위를 사용해서

콘솔 창에 응답시간을 찍을 수 있다. 

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<style type="text/css">
*{
	padding: 0; margin: 0;
}

body {
    font-size:14px;
	font-family:"맑은 고딕", 나눔고딕, 돋움, sans-serif;
}

a {
	color: #000;
	text-decoration: none;
}
a:hover, a:active {
	color: tomato;
	text-decoration: underline;
}

.btn {
    color:#333;
    font-weight:500;
    border:1px solid #cccc;
    background-color:#fff;
    text-align:center;
    cursor:pointer;
    padding:3px 10px 5px;
    border-radius:4px;
}
.btn:active, .btn:focus, .btn:hover {
	 background-color:#e6e6e6;
	 border-color: #adadad;
	 color: #333;
}
.boxTF {
    border:1px solid #999;
    padding:4px 5px 5px;
    border-radius:4px;
    background-color:#fff;
}
.boxTA {
    border:1px solid #999;
    height:150px;
    padding:3px 5px;
    border-radius:4px;
    background-color:#fff;
    resize: none;
}

textarea:focus, input:focus {
	outline: none;
}

h3 {
	margin: 10px;
}

.box{
	width: 650px;
	margin: 30px auto;
}

.box table {
	width: 100%;
	border-collapse: collapse;
	border-spacing: 0;
}

.board-title {
	width:100%;
	height:50px;
	line-height:50px;
	text-align:left;
	font-weight: bold;
	font-size:15px;
}

.board-body tr:first-child{
	border-top: 3px solid #777;
}
.board-body tr {
	height: 40px;
	border-bottom: 1px solid #777;
}
.board-body td:first-child{
	text-align: center;
	width: 100px;
	background: #eee;
}

</style>

<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script type="text/javascript">
function sendOk() {
	var f = document.boardForm;
	
	// 유효성 검사
	var returnNow = false;
	var s;
	$("form input, form textarea").each(function() {
		if(! $(this).val().trim()) { // 공백까지 없애서 유효성 검사
			s = $(this).closest("tr").find("td:first-child").text();
			s = s.replace(/\s/g, '');
			
			alert(s + "을(를) 입력하세요 !!!");
			
			$(this).focus();
			returnNow = true;
			
			return false; // return false : each를 빠져나갈 수 있다. (break; 개념)
		}
	});
	
	if(returnNow) return;
	
	alert("보내기 성공...");
}
</script>

</head>
<body>

<h3>each - break 구현</h3>

<div class="box">
	<div class="board-title">
    	<h3><span>|</span> 게시판</h3>
	</div>
	
	<form name="boardForm" method="post">
	  <table class="board-body">
	  <tr> 
		  <td >제 목</td>
		  <td style="padding-left:10px;"> 
			<input type="text" name="subject" maxlength="100" class="boxTF" style="width: 97%;">
		  </td>
	  </tr>

	  <tr> 
		  <td>작성자</td>
		  <td style="padding-left:10px;"> 
			<input type="text" name="name" size="35" maxlength="20" class="boxTF">
		  </td>
	  </tr>

	  <tr> 
		  <td style="padding-top:5px;"  valign="top">내 용</td>
		  <td valign="top" style="padding:5px 0px 5px 10px;"> 
			<textarea name="content" cols="75" rows="12" class="boxTA" style="width: 97%;"></textarea>
		  </td>
	  </tr>

	  <tr>
		  <td>패스워드</td>
		  <td style="padding-left:10px;"> 
			   <input type="password" name="pwd" size="35" maxlength="7" class="boxTF">&nbsp;(게시물 수정 및 삭제시 필요 !!!)
		   </td>
	  </tr> 
	  </table>

	  <table>
		 <tr align="center"> 
		  <td height="45">
			  <button type="button" class="btn" onclick="sendOk();"> 등록하기 </button>
			  <button type="reset" class="btn"> 다시입력 </button>
			  <button type="button" class="btn"> 취소하기 </button>
		  </td>
		</tr>
	  </table>
	</form>
</div>

</body>
</html>

return false를 통해 each를 빠져나갈 수 있다.

1. append/appendTo, prepend/prependTo

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>메소드 연습 예제</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script type="text/javascript">
$(function() {
    // 마지막에 새로운 요소 추가
    $(".box1 ul").append("<li>C/C++</li>");
    $("<li>HTML 5</li>").appendTo(".box2 ul");

    // 첫 번째에 새로운 요소 추가
    $(".box3 ul").prepend("<li>JSP/Servlet</li>");
    $("<li>ORACLE</li>").prependTo(".box4 ul");
})

</script>

</head>
<body>

<h3>조작(Manipulation)</h3>

<div class="box box1">
    <ul>
        <li>자바</li>
        <li>스프링</li>
    </ul>
</div>

<div class="box box2">
    <ul>
        <li>javascript</li>
        <li>css</li>
    </ul>
</div>

<div class="box box3">
    <ul>
        <li>ASP.NET</li>
        <li>PHP</li>
    </ul>
</div>

<div class="box box4">
    <ul>
        <li>MariaDB</li>
        <li>MySQL</li>
    </ul>
</div>    

</body>
</html>

2. before, after

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>메소드 연습 예제</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script type="text/javascript">
$(function() {
    var a = 0, b = 0;

    $(".btn1").click(function () {
        $("p.p2").before("<p>앞-"+(++a)+"번째 문장 추가</p>");
    });

    $(".btn2").click(function () {
       $("p.p2").after("<p>뒤-"+(++b)+"번째 문장 추가</p>"); 
    });

});

</script>

</head>
<body>

<h3>조작(Manipulation)</h3>

<div style="margin: 20px;">
    <button type="button" class="btn btn1"> Node.js 앞에 추가 </button>
    <button type="button" class="btn btn2"> Node.js 뒤에 추가 </button>
</div>
<hr>

<p class="p1">자바스크립트</p>
<p class="p2">Node.js</p>
<p class="p3">vue.js</p>

</body>
</html>

3. wrap, wrapAll, wrapInner

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>메소드 연습 예제</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<style type="text/css">
*{
    padding: 0; margin: 0;
}

body {
    font-size:14px;
    font-family:"맑은 고딕", 나눔고딕, 돋움, sans-serif;
}

a {
    color: #000;
    text-decoration: none;
}
a:hover, a:active {
    color: tomato;
    text-decoration: underline;
}

.btn{
    color:#333;
    font-weight:500;
    border:1px solid #ccc;
    background-color:#fff;
    text-align:center;
    cursor:pointer;
    padding:3px 10px 5px;
    border-radius:4px;
}

ul {
    list-style: none;
}
li{
    padding: 0;
}

h3{
    margin: 30px;
}

.box{
    box-sizing: border-box;
    width: 350px;
    min-height: 50px;
    margin: 20px auto;
    border: 3px dotted gray;
    padding: 15px; 
}

label {
    background: yellow;
}
</style>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script type="text/javascript">
$(function() {
   // p태그를 각각 새로운 div로 감싸기
   $(".btn1").click(function () {
       $("p").wrap("<div class='box'></div>");
   });

   // 모든 ul을 하나의 div로 감싸기. ul 사이에 p태그 등이 존재하면 모든 ul은 묶고 p 태그는 아래로 간다.
   $(".btn2").click(function () {
       $("ul").wrapAll("<div class='box'></div>");
   });

   // 선택한 요소 안을 감싸기
   $(".btn3").click(function () {
      $("p").wrapInner("<label></label>"); 
   });
});

</script>

</head>
<body>

<h3>조작(Manipulation)</h3>

<div style="margin: 20px;">
    <button type="button" class="btn btn1"> p 태그를 각 div로 감싸기 </button>
    <button type="button" class="btn btn2"> 모든 ul태그를 하나의 div로 감싸기 </button>
    <button type="button" class="btn btn3"> p 태그 안에 label 태그 추가 </button>
</div>
<hr>

<p>자바스크립트</p>
<p>CSS</p>
<hr>

<ul>
    <li>자바</li>
    <li>스프링</li>
</ul>

<h4>데이터베이스</h4>

<ul>
    <li>오라클</li>
    <li>MYSQL</li>
</ul>

</body>
</html>

4. clone

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>메소드 연습 예제</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">

<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script type="text/javascript">
$(function() {
    $("ul li").click(function() {
		alert($(this).text());
	});
	
	$(".btn").click(function() {
		// box1의 ul을 복사하여 box2에 추가. 
		$(".box1 ul").clone().appendTo(".box2"); 
		// 이벤트는 복제되지 않음. 실행순서가 늦어서..
		
		// box3의 ul을 복사하여 box4에 추가. 이벤트도 복제
		$(".box3 ul").clone(true).appendTo(".box4");
	});
});

</script>

</head>
<body>
<h3>조작(Manipulation)</h3>
<div style="margin: 20px;">
    <button type="button" class="btn"> 복사하기 </button>
</div>
<hr>

<div class="box box1">
    <ul>
        <li>자바</li>
        <li>스프링</li>
        <li>파이썬</li>
    </ul>
</div>
<div class="box box2"></div>

<div class="box box3">
    <ul>
        <li>HTML</li>
        <li>javascript</li>
        <li>css</li>
    </ul>
</div>
<div class="box box4"></div>


</body>
</html>

 

Selector

DOM의 요소( element )를 선택.

 

DOM(Document Object Model)

- HTML 문서의 요소를 제어하기 위해 웹 브라우저에서 처음 지원

- DOM은 동적으로 문서의 내용, 구조, 스타일에 접근하고 변경하기 위한 방법

- 브라우저별로 DOM 구현이 호환되지 않음에 따라, W3C에서 DOM 표준 규격을 작성

 

기본 셀렉터

#id : 지정된 아이디를 가지는 엘리먼트와 일치하는 셀렉터.

element : 모든 <element> 엘리먼트와 일치하는 셀럭터(태그 등)

.class : 지정된 클래스명을 가지는 요소를 선택.

* : 모든 엘리먼트를 선택한다.

 

예제

더보기
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ page trimDirectiveWhitespaces="true"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<style type="text/css">
* {
	padding: 0; margin: 0; box-sizing: border-box;
}
body {
	font-style: 14px;
	font-family: 맑은 고딕, 나눔고딕, 돋움, sans-serif;
}
a {
	color: #000; text-decoration: none;
}
a:hover, a:acrive {
	color: tomato; text-decoration: underline;
}
h3 {
	margin: 10px;
}

span, label {
	display: block;
}
.box {
	width: 350px; min-height: 50px; margin: 20px auto; padding: 15px;
	border: 3px dotted gray; 
}
</style>
<script type="text/javascript" src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script type="text/javascript">

// 실행 안됨. DOM이 준비 되지 않았으므로
// $("p").css("border", "1px solid #777");
	
// DOM이 로딩 되었을 때 실행
$(function() {
	// 모든 요소
	$("*").css({color:"#333", "font-size":"13px"}); 
	// {}는 자바스크립트에서 객체
	
	// 태그 선택자
	$("p").css("border","2px dashed #333");
	
	// id 선택자
	$("#layout1").css({width:'300px', padding:'10px', border:'2px solid green'});
	
	// class 선택자
	$(".red").css("color", "red");
	
	// AND
	$("label.underline").css("text-decoration", "underline");
	
	// 바로 아래 자식 하나
	$("div > label").css("color", "tomato");
	
	// 모든 자식(손자도 포함)
	$("div label").css("background", "#ff0");
	
	// 인접 형제. 바로 다음 형제 하나
	$("label + span").css("border", "1px dashed red")
	
	// 다음에 나오는 일반 형제
	$("label ~ span").attr("title", "과목");
	
});

</script>

</head>
<body>

<h3> selector</h3>

<div class="box">
	<div id="layout1">
		<label>프로그래밍</label>
		<span class="red">자바</span>
		<span>C언어</span>
		<span class="underline">코틀린</span>
		<p>
			<label class="red underline">데이터베이스</label>
			<span>오라클</span>
			<span>MySQL</span>
			<span>빅데이터</span>
		</p>
	</div>
	<div id="layout2">
		<label>웹</label>
		<span>HTML</span>
		<span>CSS</span>
		<span>JavaScript</span>
		<p>
			<label>웹프로그래밍</label>
			<span>JSP</span>
			<span>PHP</span>
			<span>ASP.NET</span>
		</p>
		<p>
			<span>React</span>
			<label>기타</label>
			<span>jQuery</span>
		</p>
	</div>
	
</div>
</body>
</html>
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ page trimDirectiveWhitespaces="true"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<style type="text/css">
* {
	padding: 0; margin: 0; box-sizing: border-box;
}
body {
	font-style: 14px;
	font-family: 맑은 고딕, 나눔고딕, 돋움, sans-serif;
}
a {
	color: #000; text-decoration: none;
}
a:hover, a:acrive {
	color: tomato; text-decoration: underline;
}
h3 {
	margin: 10px;
}

.box {
	width: 350px; min-height: 50px; margin: 20px auto; padding: 15px;
	border: 3px dotted gray; 
}

.box span, .box label {
	display: block;
}

</style>
<script type="text/javascript" src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script type="text/javascript">

// 실행 안됨. DOM이 준비 되지 않았으므로
// $("p").css("border", "1px solid #777");
	
// DOM이 로딩 되었을 때 실행
$(function() {
	// span 태그의 글자색을 tomato로
	$("span").css("color", "tomato");
	
	// span 태그와 label 태그의 글자에 밑줄
	$("span, label").css("text-decoration", "underline");
	
	// div1 아이디의 글자색을 blue
	$("#div1").css("color", "blue");
	
	// div2 아이디의 글자를 진하게
	$("#div2").css("font-weight", "700");
	
	// c1 클래스의 배경색을 yellow로
	$(".c1").css("background", "yellow");
	
	// input 요소 중 name=subject인 요소
	$("input[name=subject]").css("background", "#eee");
	
	// form의 input 요소 중 name 속성을 가진 요소
	$("form input[name]").css("border", "none");
	
	// form의 input 요소 중 type=text와 type=password인 요소
	$("form input[type=text], form input[type=password]").css("border-bottom", "1px solid blue");
	
	// form의 input 요소 중 name 속성 값이 'a'로 시작하는 요소
	$("form input[name^=a]").css("border-right", "3px solid red");
	
	// form의 input 요소 중 title 속성 값이 '버튼'으로 끝나는 요소
	$("form input[title$='버튼']").css("border-left", "3px solid black");
	
	// form의 input 요소 중 name 속성 값이 'x'가 포함된 요소
	$("form input[name*=x]").css("border-left", "3px solid black");
	
	// form의 input 요소 중 title 속성 값에 '내용'이라는 단어(띄어쓰기 구분)가 들어간 요소
	$("form input[title~='내용']").css("background", "yellow");
	
	
});

</script>

</head>
<body>

<h3> selector</h3>

<div class="box">
	<div id="div1">테스트 1</div>	
	<div id="div2" class="c1">테스트 2</div>
	<span>테스트 3</span>
	<span>테스트 4</span>
	<div id="div3" class="c1">테스트 5</div>
	<span class="c1">테스트 6</span>
	<span class="c2">테스트 7</span>
</div>
<hr>

<div class="box">
	<p> <input type="text" name="subject" title="과목 입력"> </p>
	<form>
		<p> <input type="text" name="subject" title="과목 입력"> </p>
		<p> <input type="text" name="ax1" title="1장 제목 입력"> </p>
		<p> <input type="text" name="as2" title="1장 내용 입력"> </p>
		<p> <input type="text" name="bs1" title="2장 제목 입력"> </p>
		<p> <input type="text" name="bx2" title="2장 내용 입력"> </p>
		<p> <input type="password" name="pwd" title="패스워드 입력"> </p>
		<p> <input type="file" name="selectFile"> </p>
		<p>
			<input type="button" value="입력완료" title="입력 버튼">
			<input type="reset" value="다시입력" title="리셋 버튼">
		</p>
	</form>
</div>

</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<style type="text/css">
*{
	padding: 0; margin: 0;
}

body {
    font-size:14px;
	font-family:"맑은 고딕", 나눔고딕, 돋움, sans-serif;
}

a {
	color: #000;
	text-decoration: none;
}
a:hover, a:active {
	color: tomato;
	text-decoration: underline;
}

h3 {
	margin: 10px;
}

.btn {
	color:#333;
	font-weight:500;
	border:1px solid #ccc;
	background-color:#fff;
	text-align:center;
	cursor:pointer;
	padding:3px 10px 5px;
	border-radius:4px;
	font-family:"Malgun Gothic", "맑은 고딕", NanumGothic, 나눔고딕, 돋움, sans-serif;
}
.btn:active, .btn:focus, .btn:hover {
	background-color:#e6e6e6;
	border-color: #adadad;
	color: #333;
}

textarea:focus, input:focus{
	outline: none;
}
.boxTF {
	border:1px solid #999;
	padding:3px 5px 5px;
	border-radius:4px;
	background-color:#fff;
	font-family:"Malgun Gothic", "맑은 고딕", NanumGothic, 나눔고딕, 돋움, sans-serif;
}

.box{
	box-sizing: border-box;
	width: 500px;
	min-height: 50px;
	margin: 20px auto;
	border: 3px dotted gray;
	padding: 15px; 
}
</style>

<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script type="text/javascript">
$(function() {
	$("form input + span").hide();
	$("form input[type=text]").css("border", "1px solid #aaa");
	
	// focus 이벤트. button 제외
	$("form input").not($(":button")).focus(function() {
		$(this).css("border", "1px solid #f28011");
		// 이벤트를 발생시킨 바로 다음 형제중 span 태그
		$(this).next("span").show();
	});
	
	// blur 이벤트. button 제외
	$("form input").not($(":button")).blur(function() {
		$(this).css("border", "1px solid #aaa");
		// 이벤트를 발생시킨 바로 다음 형제중 span 태그
		$(this).next("span").hide();
	});
});

</script>

</head>
<body>

<h3>selectors 예제</h3>

<div class="box">
<form>
	<p>
		<input type="text" name="id">
	    <span>아이디 입력.</span>
	    <span>5~10자 이내</span>
	</p>
	<p>
		<input type="text" name="name">
		<span>이름 입력.</span>
	</p>
	<p>
		<input type="text" name="age">
		<span>나이 입력.</span>
	</p>
	<p>
		<input type="text" name="birth">
		<span>생년월일 입력.</span>
	</p>
	<p style="padding: 5px;">
	<!-- 
		<button type="button" class="btn"> 등록하기 </button>
		<button type="button" class="btn"> 등록취소 </button>
	-->
		<input type="button" class="btn" value="등록하기">		
		<input type="button" class="btn" value="등록취소">		
	</p>
</form>
</div>

</body>
</html>

focus 이벤트에서 button을 제외

blur이벤트에서 button을 제외

 

이외 filter 가 여러가지 있음. 

 

선택된 요소 접근 메소드

더보기
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<style type="text/css">
*{
	padding: 0; margin: 0;
}

body {
    font-size:14px;
	font-family:"맑은 고딕", 나눔고딕, 돋움, sans-serif;
}

a {
	color: #000;
	text-decoration: none;
}
a:hover, a:active {
	color: tomato;
	text-decoration: underline;
}

.btn{
    color:#333;
    font-weight:500;
    border:1px solid #ccc;
    background-color:#fff;
    text-align:center;
    cursor:pointer;
    padding:3px 10px 5px;
    border-radius:4px;
}

ul {
	list-style: none;
}
li{
	padding: 0;
}

h3{
	margin: 30px;
}

.box{
	box-sizing: border-box;
	width: 350px;
	min-height: 50px;
	margin: 20px auto;
	border: 3px dotted gray;
	padding: 15px; 
}
</style>

<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script type="text/javascript">
$(function() {
	$(".btnOk").click(function() {
		// var name = $(".std").html(); // html 소스 반환(innerHTML 속성)
		var name = $(".std").text(); // 텍스트만 반환
		
		// 속성값 가져오기
		var num = $(".std").attr("data-num");
		
		// document.getElementById("score").value = 90;
		$("#score").val(90); // value 속성에 값 설정
		
		// value 속성 값 가져오기
		var subject = $("#subject").val();
		var score = $("#score").val();
		
		var s = "<p>학번 : <b>" + num + "</b></p>";
		s +="<p>이름 : <b>" + name + "</b></p>";
		s +="<p>과목 : <b>" + subject + "</b></p>";
		s +="<p>점수 : <b>" + score + "</b></p>";
		
		// 텍스트 설정
		// $("#layout").text(s);
		
		// html 콘텐츠 설정
		$("#layout").html(s);
		
	});
	
})

/*
-- 선택된 요소 접근 
  .html() : 해당 요소의 HTML 콘텐츠를 반환하거나 설정한다.
  .text() : 해당 요소의 텍스트 콘텐츠를 반환하거나 설정한다.
  .val() : <form>요소의 값을 반환하거나 설정한다
  .attr() : 해당 요소의 명시된 속성의 속성값을 반환하거나 설정한다.
  .width() : 선택한 요소 중에서 첫 번째 요소의 너비를 픽셀 단위의 정수로 반환하거나 설정한다.
  .height() : 선택한 요소 중에서 첫 번째 요소의 높이를 픽셀 단위의 정수로 반환하거나 설정한다.
  .position() : 선택한 요소 중에서 첫 번째 요소에 대해 특정 위치에 존재하는 객체를 반환한다.(getter 메소드)

-- 메소드 체이닝(method chaining)
  여러 개의 메소드가 연속으로 호출되는 것
  예 : $("ul").find("li").eq(1).append("자바");
*/
</script>

</head>
<body>

<h3>선택된 요소 접근</h3>
<div style="margin: 20px;">
	<button type="button" class="btn btnOk"> 확인 </button>
</div>
<hr>

<div class="box">
	<p>
		<!-- data-* : 개발자에 의해 만들어진 속성 -->
	    <span class="std" data-num="1"><b>홍길동</b></span>
	</p>
	<p> <input type="text" id="subject" value="자바"> </p>
	<p> <input type="text" id="score"> </p>
</div>

<div class="box">
	<p>결과</p>
	<div id="layout"></div>
</div>

</body>
</html>

jQeury 개요

- jQuery는 빠르고 간결한 JavaScript Library 이다.

- HTML document traversing(탐색), 이벤트 처리, 애니메이션, AJAX를 단순화하여 빠른 웹 개발을 가능하게 한다.

- 다양한 브라우저 지원

- 경량 파일(90kb 수준의 파일크기)

- CSS3 규격 지원

- 존 레식에 의해 개발

 

jQuery를 사용하기 위한 라이브러리 포함

<script type="text/javascript" src="jquery-1.12.4.min.js"></script> 

 

CDN(Content Delivery Network)을 이용 jQuery 라이브러리 포함

<script src="http://code.jquery.com/jquery-3.6.0.min.js"></script>

 

core 예제

더보기
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ page trimDirectiveWhitespaces="true"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">

<script type="text/javascript" src="https://code.jquery.com/jquery-3.6.0.min.js"></script>

<script type="text/javascript">
// DOM이 로드 될 때 실행
/*
// 이벤트 핸들러 프로퍼티 방식
// 테스트-2 만 실행
window.onload = function() {
	console.log("테스트-1");
};

window.onload = function() {
	console.log("테스트-2");
};
*/

/*
// addEventListener
// 둘 다 실행
window.addEventListener("load", function() {
	console.log("테스트-1");
});
window.addEventListener("load", function() {
	console.log("테스트-2");
});
*/
/*
// jquery를 이용한 실행
// 둘 다 실행
jQuery(document).ready(function() {
	console.log("테스트-1")
});
jQuery(document).ready(function() {
	console.log("테스트-2")
});
*/
/*
// jQuery(document).ready(function(){}); 의 짧은 표현
jQuery(function(){
	console.log("테스트-1");
});
jQuery(function(){
	console.log("테스트-2");
});
*/
/*
// jQuery === $
$(function(){
	console.log("테스트-1");
});
$(function(){
	console.log("테스트-2");
});
*/
/*
$(document).ready(function() {
	// DOM 객체만 로드 되면 바로 실행
	console.log("테스트-1");
});
$(window).on("load", function() {
	// 해당 페이지의 모든 외부 리소스(이미지, css 등)가 로드 된 후 실행
	// $(function(){}); 보다 느림
	console.log("테스트-2");
})
*/

$(function() {
	$(document.body).css("background", "#eee");
});
</script>

</head>
<body>

<h3> jQuery Core 예제</h3>
<p>
  jQuery( elements ), $( elements )
</p>

<p>
  $() = $(document).ready() = jQuery() = jQuery(document).ready()
</p>

</body>
</html>

 

 

파일업로드를 위한 <form> 태그

<form> 태그의 enctype 속성

- <form> 태그 안에 정의된 요청 파라미터를 클라이언트에서 서버로 전송할 때 인코딩 방법을 지정한다.

- HTTP 헤더의 Content-Type 을 지정하여 서버에 어떤 종류의 데이터가 전송되는지를 전달한다.

- method="post"인 경우에만 이 속성은 유효하다.

- 속성 값

1) application/x-www-form-urlencoded

기본 enctype 속성으로 요청 파라미터는 key1=value1&key2=value2 형태로 나열된다.

공백은 +, 영숫자가 아닌 문자는 %HH(문자코드의 16진수 2자리)로 인코딩하여 전송한다.

이 content type은 바이너리 데이터(파일 등) 전송에서는 사용할 수 없다.

2) multipart/form-data

문자 인코딩을 하지 않고 전송되며, 파일을 전송할 때 사용한다.

여러 형태의 데이터 (파일 내용, 텍스트 입력 값 등)가 동시에 전달되며 각 데이터는 boumdary=...(경계정보) 뒤에 붙은 문자열을 이용하여 구분한다.

3) text/plain 

공백이 +문자로 변환되며 특수 문자를 인코딩 하지 않는다.

 

예제 (ch09)

더보기

ex01

<%@ page contentType="text/html; charset=UTF-8"%>
<%@ page trimDirectiveWhitespaces="true"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
</head>
<body>

<h3>파일 업로드</h3>
<%--
  - form 태그에서 enctype을 생략하면 기본이 application/x-www-form-urlencoded
  - application/x-www-form-urlencoded 속성 값은 파라미터를 주소형식으로 인코딩하여 전송
    파라미터는 "이름1=값1&이름=값2" 형태로 body영역에 실어서 전송된다.
  - application/x-www-form-urlencoded 속성 값에서 파일을 서버로 전송하면 파일 이름만 전송 된다.
  - enctype 속성은 method가 post인 경우만 유효
 --%>

<form action="ex01_ok.jsp" method="post" >
<p> 제목 : <input type="text" name="subject"> </p>
<p> 파일 : <input type="file" name="selectFile"> </p>
<p>
	<button type="submit">등록하기</button>
</p>

</form>

</body>
</html>

ex01_ok

<%@ page contentType="text/html; charset=UTF-8"%>
<%@ page trimDirectiveWhitespaces="true"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%
	request.setCharacterEncoding("utf-8");
	
	// request.getParameter()는 문자열만 전달 받는다.
	String subject = request.getParameter("subject");
	// 파일은 enctype="application/x-www-form-urlencoded" 에서는 이름만 전송 받는다.
	String selectFile = request.getParameter("selectFile");
%>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
</head>
<body>

<h3>요청 받은 정보</h3>

<p>제목 : <%= subject %> </p>
<p>파일 : <%=selectFile %> </p>

</body>
</html>

 위 예제를 통해서 파일을 전달 받지 못하는 것을 알 수 있다. 파일의 이름만을 받았음.

 

ex02

<%@ page contentType="text/html; charset=UTF-8"%>
<%@ page trimDirectiveWhitespaces="true"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
</head>
<body>

<h3>파일 업로드</h3>
<%--
  * form 태그에서 enctype이 multipart/form-data인 경우
    - 문자 인코딩을 하지 않고 전송하며, 파일을 전송할 때 사용
    - 피일의 내용도 전송된다.
  * enctype 속성은 method가 post인 경우만 유효
  
 --%>

<form action="ex02_ok.jsp" method="post" enctype="multipart/form-data">
<p> 제목 : <input type="text" name="subject"> </p>
<p> 파일 : <input type="file" name="selectFile"> </p>
<p>
	<button type="submit">등록하기</button>
</p>

</form>

</body>
</html>

ex02_ok

<%@page import="java.net.URLDecoder"%>
<%@page import="java.io.InputStream"%>
<%@page import="java.util.Enumeration"%>
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ page trimDirectiveWhitespaces="true"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%
	request.setCharacterEncoding("utf-8");
	
	// enctype="multipart/form-data"로 넘어온 파라미터는 request.getParameter()로 받을 수 없다.
	// String subject = request.getParameter("subject");
	// String selectFile = request.getParameter("selectFile");
	
	
%>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
</head>
<body>

<h3>요청 받은 정보</h3>

<%
	String contentType = request.getContentType();
	out.print("<p>contentType : " + contentType + "</p>");
	out.print("<hr>");
	
	out.print("<h3>[[헤더 정보들]]....</h3>");
	Enumeration<String> e = request.getHeaderNames();
	while(e.hasMoreElements()) {
		String name = e.nextElement();
		String value = request.getHeader(name);
		out.print("<p>" + name + " : " + value + "</p>");
	}
	out.print("<hr>");
	
	out.print("<h3>[[request Body 영역으로 넘어온 데이터]]</h3>");
	
	InputStream is = request.getInputStream();
	byte[] buf = new byte[2048];
	int size;
	String str;
	while( (size = is.read(buf)) != -1) {
		// enctype = "application/x-www-form-urlencoded" 인 경우
		// str = new String(buf, 0, size);
		// str = URLDecoder.decode(str, "utf-8");
		
		// enctype = "nultipart/form-data" 인 경우
		str = new String(buf, 0, size, "utf-8");
				
		out.print("<p>" + str + "</p>");
	}
	out.print("<hr>");
	
%>

</body>
</html>

파일이 넘어왔음을 알 수 있다.

 

cos.jar을 이용한 업로드 (권장하지 않음)

<%@ page contentType="text/html; charset=UTF-8"%>
<%@ page trimDirectiveWhitespaces="true"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
</head>
<body>

<h3>파일 업로드</h3>
<%--
  * cos.jar를 이용한 업로드
    http://servlets.com 
 --%>

<form action="ex03_ok.jsp" method="post" enctype="multipart/form-data">
<p> 제목 : <input type="text" name="subject"> </p>
<p> 파일 : <input type="file" name="selectFile"> </p>
<p>
	<button type="submit">등록하기</button>
</p>

</form>

</body>
</html>
<%@page import="com.oreilly.servlet.multipart.DefaultFileRenamePolicy"%>
<%@page import="com.oreilly.servlet.MultipartRequest"%>
<%@page import="java.io.File"%>
<%@ page contentType="text/html; charset=UTF-8"%>
<%@ page trimDirectiveWhitespaces="true"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%
	request.setCharacterEncoding("utf-8");

	// String root = pageContext.getServletContext().getRealPath("/"); 도 가능
	String root = session.getServletContext().getRealPath("/");
	String pathname = root + "uploads" + File.separator + "pds";
	File f= new File(pathname);
	if(! f.exists() ) {
		f.mkdirs();	
	}
	
	String encType = "utf-8"; // 클라이언트가 보낸 정보의 인코딩
	int maxFileSize = 5*1024*1024; // 최대 업로드 용량(5MB)
	
	MultipartRequest mreq = null;
				// request, 파일저장경로, 파일최대크기, 인코딩, 중복파일명보호
	mreq = new MultipartRequest(request, pathname, maxFileSize, encType, 
			new DefaultFileRenamePolicy() );
	
	// 제목 
	String subject = mreq.getParameter("subject");
	
	// 서버에 저장된 파일 이름
	String saveFilename = mreq.getFilesystemName("selectFile");
	
	// 클라이언트가 올린 파일 이름
	String originalFilename = mreq.getOriginalFileName("selectFile");
	
	// 파일 크기
	long fileSize = 0;
	File file = mreq.getFile("selectFile"); // 업로드된 파일에 대한 File 객체
	if(file != null) {
		fileSize = file.length();
	}
	

%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
</head>
<body>

<h3>파일 업로드 결과 : cos.jar</h3>

<p> 제목 : <%=subject%> </p>
<p> 클라이언트가 올린 파일 이름 : <%=originalFilename %></p>
<p> 서버에 저장된 파일 이름 : <%=saveFilename %></p>
<p> 파일 크기 : <%=fileSize %>byte.</p>


</body>
</html>

 

+ Recent posts