# DB Connect Model
- Jsp에서 DB를 연동하는 방법은 두가지가 있다.
종류 | 특징 |
Model 1 | - 한 곳에서 Jsp를 사용하여 서블릿없이 서버, 화면의 작업을 전부 처리하는 방식 - 소스가 복잡해지므로 잘 사용하지는 않으나 소규모로 빠르게 작업을 처리해야하는 경우 사용하기도 한다. |
Model 2 | - 서버와 화면 영역을 구분하여 처리하는 방식 - 서버의 작업은 서블릿에서 처리하며 화면은 Jsp에서 처리한다. - 소스의 가독성은 좋으나 라이브러리 등의 선 준비 작업이 필요하다. |
[예시] MODEL 1 방식
# DeptVO.java
package vo;
public class DeptVO {
// Mybatis 사용 시 DB의 컬럼명과 변수명은 가급적 일치하는 것이 좋다.
private int deptno;
private String dName;
private String loc;
//
public int getDeptno() {
return deptno;
}
public void setDeptno(int deptno) {
this.deptno = deptno;
}
public String getdName() {
return dName;
}
public void setdName(String dName) {
this.dName = dName;
}
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
}
# Context.xml
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<!-- Context
- DB접근을 위한 리소스 파일
- WebContent의 META-INF의 폴더 내에 위치한다.
- 리소스 파일을 통해서 DB접속에 필요한 정보를 키와 값의 형태로 준비하고 사용하는 형태를 JNDI (Java Naming Directory Interface)라고 한다.
속성
(1) name
- DataSource에 대한 JNDI명칭
(2) auth
- 인증 주체
(3) DriverClassName
- 연결할 데이터베이스 종류에따른 드라이버 클래스 명칭
(4) factory
- 연결할 데이터베이스 종류에따른 Connection Pool 생성 클래스 명칭
(5) maxActive
- 동시에 최대로 데이터베이스에 연결할 수 있는 Connection의 수
- 한번에 접속 가능한 유저 수
- 만약 해당 값을 10으로 지정한 뒤 11명째의 유저가 접속하려면 1명이 나가야한다.
(6) maxIdle
- 동시에 IDLE 상태로 대기할 수 있는 최대 수
(7) maxWait
- 새로운 연결이 될 때까지 대기할 수 있는 시간
- 응답대기시간
- 값을 -1로 설정하는 경우 무한으로 대기하게 된다.
-->
<Resource
auth="Container"
name="jdbc/oracle_test"
type="javax.sql.DataSource"
driverClassName="oracle.jdbc.driver.OracleDriver"
factory="org.apache.commons.dbcp.BasicDataSourceFactory"
url="jdbc:oracle:thin:@localhost:1521:xe"
username="test" password="1111"
maxActive="20" maxIdle="10" maxWait="1"/>
</Context>
# Jdbc_dept.jsp
<%@page import="vo.DeptVO"%>
<%@page import="java.util.ArrayList"%>
<%@page import="java.util.List"%>
<%@page import="java.sql.ResultSet"%>
<%@page import="java.sql.PreparedStatement"%>
<%@page import="java.sql.Connection"%>
<%@page import="javax.sql.DataSource"%>
<%@page import="javax.naming.Context"%>
<%@page import="javax.naming.InitialContext"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!-- Model 1
- 한 곳에서 Jsp를 사용하여 서블릿없이 서버, 화면의 작업을 전부 처리하는 방식
- 소스가 복잡해지므로 잘 사용하지는 않으나 소규모로 빠르게 작업을 처리해야하는 경우 사용하기도 한다.
-->
<%
// InitialContext
// - JNDI서비스를 제공하는 객체 생성
InitialContext ic = new InitialContext();
// 컨텍스트 객체 생성
// - java:comp/env는 톰캣에서 리소스를 관리하는 가상의 주소이다.
// - lookup함수는 JNDI의 메소드로서 name과 key값의 정보를 통해서 데이터소스의 객체를 얻는다.
// LOOKUP은 JNDI의 메소드로서 NAME과 KEY값을 통해서 DATASOURCE의 객체를 얻는다.
Context ctx = (Context)ic.lookup("java:comp/env");
// DB객체 생성
// - jdbc/oracle_test는 context.xml에 등록한 계정 정보를 뜻한다.
DataSource ds = (DataSource)ctx.lookup("jdbc/oracle_test");
// 커넥션 객체 생성
// - getConnection은 DB연동을 가져오는 메소드이다.
Connection conn = ds.getConnection();
// 커넥션 유무 파악
System.out.println("--DB연결 성공--");
// 쿼리문 생성
String sql = "select * from dept";
// PreparedStatement
// - statement를 상속하는 인터페이스로 Sql 쿼리문을 동작시키는 객체
// - 쿼리문을 반복해서 사용할 수 있도록 돕는다.
PreparedStatement pstmt = conn.prepareStatement(sql);
// executeQuery
// - Sql 처리 결과를 저장하는 객체
// - next 메소드는 쿼리 결과의 행 데이터가 존재하는지를 뜻한다.
// - 값이 있는 경우 true, 없는 경우 false를 반환한다.
ResultSet rs = pstmt.executeQuery();
List<DeptVO> dept_list = new ArrayList();
//
while(rs.next()){
DeptVO vo = new DeptVO();
// getInt : 쿼리의 결과를 숫자 타입으로 받는다.
// getString : 쿼리의 결과를 문자열 타입으로 받는다.
vo.setDeptno(rs.getInt("deptno"));
vo.setdName(rs.getString("dname"));
vo.setLoc(rs.getString("loc"));
// add를 사용하여 ArrayList에 저장한다.
dept_list.add(vo);
}
// close는 DB연동을 종료하는 메소드이다.
// 스트림과 마찬가지로 닫을 때는 역순으로 닫아준다.
rs.close();
pstmt.close();
conn.close();
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<style type="text/css">
table{border-collapse: collapse;}
</style>
<script type="text/javascript">
function send(no) {
// 받은 값을 인풋태그(히든)의 값으로 지정해준 뒤 서버 전송을 할 수 있다.
var f = document.getElementById("m_form");
var hid = document.getElementById("hid");
hid.value = no;
f.submit();
}
</script>
</head>
<body>
<!-- 버튼 태그 내 폼 태그를 보내지 못하므로 대신 아이디 값을 넘긴다. -->
<form id="m_form" action="sawon_list.jsp">
<table border="1">
<caption>:: 부서 테이블 ::</caption>
<tr>
<th>번호</th>
<th>부서명</th>
<th>위치</th>
</tr>
<%
for(int i=0;i<dept_list.size();i++){
DeptVO vo = dept_list.get(i);
%>
<tr>
<td><%= vo.getDeptno() %></td>
<td>
<!-- [참고] a태그에서 값 전송
- 폼 태그를 사용할 수 없으므로 a태그의 send함수를 통해서 VO의 값을 넘긴다.
- javascript:함수를 사용하여 받은 파라미터 값을 히든 태그의 값으로 보내주는 함수를 통해 서버로 값을 전송한다.
- 자주 사용하는 방식은 아니다.
-->
<a href="javascript:send('<%=vo.getDeptno()%>')">
<%= vo.getdName() %>
</a>
</td>
<td><%= vo.getLoc() %></td>
</tr>
<% }%>
</table>
<!-- [참고] 인풋태그의 히든 타입
- 타입이 히든인 인풋타입은 화면 상에서 보이지 않는다.
- 히든타입은 개발자가 사용자가 확인할 수 없는 방식으로 값을 넘길 때 사용한다.
-->
<input type="hidden" id="hid" name="deptno">
</form>
</body>
</html>
[예시] MODEL 2 방식
# oracle 계정 및 table 생성
# apache.org 에서 라이브러리 다운로드 후 lib에 저장

라이브러리 | 역할 |
ojdbc6 | oracle에서 제공하는 라이브러리로 DB와 자바간에 커넥션을 생성, 해제 |
dbcp | objbc에서 DB접근시마다 커넥, 해체를 반복하는 단점을 해결 |
pool | Connection Pool 사용 |
# context.xml 에 JNDI 등록
<?xml version="1.0" encoding="UTF-8"?>
<!-- JMDI을 통해서 oracle_test라는 계정에 접속 -->
<Context>
<Resource
auth="Container"
name="jdbc/oracle_test"
type="javax.sql.DataSource"
driverClassName="oracle.jdbc.driver.OracleDriver"
factory="org.apache.commons.dbcp.BasicDataSourceFactory"
url="jdbc:oracle:thin:@localhost:1521:xe"
username="test" password="1111"
maxActive="20" maxIdle="10" maxWait="1"/>
</Context>
# DBService.java
package service;
import java.sql.Connection;
import javax.naming.InitialContext;
import javax.sql.DataSource;
public class DBService {
// 싱글톤 패턴
static DBService single = null;
// getInstance로 객체를 생성한다.
public static DBService getInstance() {
// 생성되지 않았으면 객체 생성
if (single == null)
single = new DBService();
// 생성된 객체정보를 반환
return single;
}
// 데이터소스 객체
DataSource ds;
// 서비스 생성자
private DBService() {
// 컨텍스트가 존재하지 않을 수 없을 수 있으므로 예외처리
try {
//
InitialContext ic = new InitialContext();
ds = (DataSource)ic.lookup("java:comp/env/jdbc/oracle_test");
} catch (Exception e) {
}
}
// 커넥션 연동 메소드
public Connection getConnection() {
//
Connection conn = null;
//
try {
//
conn = ds.getConnection();
} catch (Exception e) {
}
//
return conn;
}
}
# SungjukVO.java
package vo;
public class SungjukVO {
//
private String name;
private int kor;
private int eng;
private int mat;
private int total;
private int avg;
private int no;
private int rank;
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public int getRank() {
return rank;
}
public void setRank(int rank) {
this.rank = rank;
}
public int getTotal() {
return total;
}
public void setTotal(int total) {
this.total = total;
}
public int getAvg() {
return avg;
}
public void setAvg(int avg) {
this.avg = avg;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getKor() {
return kor;
}
public void setKor(int kor) {
this.kor = kor;
}
public int getEng() {
return eng;
}
public void setEng(int eng) {
this.eng = eng;
}
public int getMat() {
return mat;
}
public void setMat(int mat) {
this.mat = mat;
}
}
# SungjukDAO.java
package dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import service.DBService;
import vo.SungjukVO;
public class SungjukDAO {
/* DAO
* - Data Access Object
* - 쿼리문을 통해서 실행된 결과를 List형식으로 돌려주는 클래스
*/
/* 싱글톤 패턴
* - 필요한 하나의 객체만 생성해두고 다른 클래스에서 같은 객체를 계속 사용하는 패턴
* - 객체 1개만 생성해서 지속적으로 사용
* - 서비스 클래스에서는 DB의 접속 과정을 담당한다.
*/
static SungjukDAO single = null;
// getInstance로 객체를 생성한다.
public static SungjukDAO getInstance() {
// 생성되지 않았으면 객체 생성
if (single == null)
single = new SungjukDAO();
// 생성된 객체정보를 반환
return single;
}
// 목록 조회
public List<SungjukVO> selectList() {
//
List<SungjukVO> list = new ArrayList<SungjukVO>();
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
// 쿼리문문
String sql = "select * from sungtb_view";
//
try {
// 커넥션 객체 생성
conn = DBService.getInstance().getConnection();
// 명령처리객체 생성
pstmt = conn.prepareStatement(sql);
// 결과 처리 객체 생성
rs = pstmt.executeQuery();
// 쿼리문 결과를 읽는다.
while (rs.next()) {
SungjukVO vo = new SungjukVO();
// VO객체에 저장
vo.setName(rs.getString("name"));
vo.setKor(rs.getInt("kor"));
vo.setEng(rs.getInt("eng"));
vo.setMat(rs.getInt("mat"));
vo.setTotal(rs.getInt("tot"));
vo.setNo(rs.getInt("no"));
vo.setAvg(rs.getInt("avg"));
vo.setRank(rs.getInt("rank"));
// ArrayList에 추가
list.add(vo);
}
} catch (Exception e) {
// e.printStackTrace은 예외를 출력하는 메소드이다.
e.printStackTrace();
} finally {
// 연동 객체 종료
try {
if (rs != null)
rs.close();
if (pstmt != null)
pstmt.close();
if (conn != null)
conn.close();
} catch (SQLException e) {
//
e.printStackTrace();
}
}
//
return list;
}
// 삭제
// - DML 기능은 변경되는 행의 수를 숫자로 반환한다.
public int delete(int no) {
//
int res = 0;
//
Connection conn = null;
PreparedStatement pstmt = null;
// 쿼리문문 생성
// - 물음표는 파라미터를 대신한다.
String sql = "delete from sungtb where no = ?";
//
try {
// 커넥션 생성
conn = DBService.getInstance().getConnection();
// 명령처리객체 생성
pstmt = conn.prepareStatement(sql);
// setInt 메소드를 통해서 파라미터의 값을 지정한다.
// - setInt(?의 위치, 값)
pstmt.setInt(1, no);
// 쿼리문 동작
res = pstmt.executeUpdate();
} catch (Exception e) {
//
e.printStackTrace();
} finally {
//
try {
if (pstmt != null)
pstmt.close();
if (conn != null)
conn.close();
} catch (SQLException e) {
//
e.printStackTrace();
}
}
// 처리한 행수의 값이 나온다.
return res;
}
// 등록
public int insert(SungjukVO svo) {
//
int res = 0;
//
Connection conn = null;
PreparedStatement pstmt = null;
// 쿼리문문 생성
String sql = "insert into sungtb values(seq_sungtb_no.nextVal,?,?,?,?)";
//
try {
// 커넥션 생성
conn = DBService.getInstance().getConnection();
// 명령처리객체 생성
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, svo.getName());
pstmt.setInt(2, svo.getKor());
pstmt.setInt(3, svo.getEng());
pstmt.setInt(4, svo.getMat());
// 쿼리문 동작
res = pstmt.executeUpdate();
} catch (Exception e) {
//
e.printStackTrace();
} finally {
//
try {
if (pstmt != null)
pstmt.close();
if (conn != null)
conn.close();
} catch (SQLException e) {
//
e.printStackTrace();
}
}
//
return res;
}
// 수정
public int update(SungjukVO svo) {
//
int res = 0;
//
Connection conn = null;
PreparedStatement pstmt = null;
// 쿼리문문 생성
String sql = "update sungtb set name = ?, kor = ?, eng = ?, mat = ? where no = ?";
//
try {
// 커넥션 생성
conn = DBService.getInstance().getConnection();
// 명령처리객체 생성
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, svo.getName());
pstmt.setInt(2, svo.getKor());
pstmt.setInt(3, svo.getEng());
pstmt.setInt(4, svo.getMat());
pstmt.setInt(5, svo.getNo());
// 쿼리문 동작
res = pstmt.executeUpdate();
} catch (Exception e) {
//
e.printStackTrace();
} finally {
//
try {
if (pstmt != null)
pstmt.close();
if (conn != null)
conn.close();
} catch (SQLException e) {
//
e.printStackTrace();
}
}
//
return res;
}
}
# jdbc_Sungjuk.jsp
<%@page import="vo.SungjukVO"%>
<%@page import="java.util.List"%>
<%@page import="dao.SungjukDAO"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
SungjukDAO s_dao = SungjukDAO.getInstance();
List<SungjukVO> sung_list = s_dao.selectList();
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script type="text/javascript">
function show_form() {
var disp = document.getElementById("disp");
disp.style.display = 'block';
}
// 등록 취소
function my_cancel() {
var disp = document.getElementById("disp");
disp.style.display = "none";
// 값의 취소
// - name 속성으로 값을 찾은 뒤 값을 초기화한다.
// - getElementByName 메소드는 name값으로 태그를 찾는 메소드이다. (같은 명칭이 있을 수 있으므로 배열로 반환한다.)
document.getElementsByName("name")[0].value = "";
document.getElementsByName("kor")[0].value = "";
document.getElementsByName("eng")[0].value = "";
document.getElementsByName("mat")[0].value = "";
//
var disp2 = document.getElementById("disp2");
disp2.style.display = "none";
}
// 등록
function send(f) {
// trim은 값의 양쪽 공백을 제거하는 함수이다.
var name = f.name.value.trim();
var kor = f.kor.value.trim();
var eng = f.eng.value.trim();
var mat = f.mat.value.trim();
//
if(name==''){
alert("이름은 반드시 입력되어야합니다.");
f.name.focus();
return;
}
// 숫자만 가능한 정규식
var number = /^[0-9]+$/;
//
if(!number.test(kor) || kor < 0 || kor > 100){
alert("0에서 100사이의 정수만 입력가능합니다.");
f.kor.value = '';
f.kor.focus();
return;
}
if(!number.test(eng) || eng < 0 || eng > 100){
alert("0에서 100사이의 정수만 입력가능합니다.");
f.eng.value = '';
f.eng.focus();
return;
}
if(!number.test(mat) || mat < 0 || mat > 100){
alert("0에서 100사이의 정수만 입력가능합니다.");
f.mat.value = '';
f.mat.focus();
return;
}
//
f.action = "sung_register.jsp";
f.submit();
}
// 삭제
function del(no) {
// confirm
// - 확인 알림창을 뜨게하는 함수
// - 확인 클릭 시 true, 취소 시 false를 반환한다.
if(!confirm("정말로 삭제하시겠습니까?")){
return; // 아무 동작도 하지 않고 함수를 나옴
}
// location.href
// - 페이지로 이동하는 함수
// - 뒤에 파라미터를 붙여주고자하는 경우 url?변수=값1&변수2=값2... 의 형식으로 전송한다.
// - 폼태그 없이 호출이 가능하다.
// - get방식의 전송만 가능하다.
location.href="sung_del.jsp?no="+no;
}
// 수정
function update(no,name,kor,eng,mat) {
//
var disp2 = document.getElementById("disp2");
disp2.style.display = 'block';
//
document.getElementsByName("name2")[0].value = name;
document.getElementsByName("kor2")[0].value = kor;
document.getElementsByName("eng2")[0].value = eng;
document.getElementsByName("mat2")[0].value = mat;
document.getElementsByName("no")[0].value = no;
}
// 폼태그를 파라미터로 받아 내용 수정
function send2(f) {
// 유효성 검사는 했다고 가정
f.action = "sung_update.jsp";
f.submit();
}
</script>
</head>
<body>
<table border="1">
<tr>
<th>번호</th>
<th>이름</th>
<th>국어</th>
<th>영어</th>
<th>수학</th>
<th>총점</th>
<th>평균</th>
<th>등수</th>
<th colspan="2">비고</th>
</tr>
<%for(int i=0;i<sung_list.size();i++){
SungjukVO vo = sung_list.get(i);%>
<tr>
<td><%=vo.getNo() %></td>
<td><%=vo.getName() %></td>
<td><%=vo.getKor() %></td>
<td><%=vo.getEng() %></td>
<td><%=vo.getMat() %></td>
<td><%=vo.getTotal()%></td>
<td><%=vo.getAvg()%></td>
<td><%=vo.getRank()%></td>
<td><input type="button" value="삭제" onclick="del('<%=vo.getNo()%>');"></td>
<td><input type="button" value="수정" onclick="update('<%=vo.getNo()%>',
'<%=vo.getName()%>',
'<%=vo.getKor()%>',
'<%=vo.getEng()%>',
'<%=vo.getMat()%>')"></td>
</tr>
<% } %>
<!-- 추가 -->
<tr align="center">
<td colspan="10">
<input type="button" value="학생정보 추가" id="btn_append" onclick="show_form()">
</td>
</tr>
</table>
<div id="disp" style="display: none;">
<form>
<table border="1">
<caption>::성적 등록하기::</caption>
<tr>
<th>이름</th>
<td><input type="text" name="name"></td>
</tr>
<tr>
<th>국어</th>
<td><input type="text" name="kor"></td>
</tr>
<tr>
<th>영어</th>
<td><input type="text" name="eng"></td>
</tr>
<tr>
<th>수학</th>
<td><input type="text" name="mat"></td>
</tr>
<tr>
<td colspan="2" align="center">
<input type="button" value="등록" onclick="send(this.form);">
<input type="button" value="취소" onclick="my_cancel()">
</td>
</tr>
</table>
</form>
</div>
<div id="disp2" style="display:none">
<form>
<input type="hidden" name="no">
<table border="1">
<caption>::항목 수정::</caption>
<tr>
<th>이름</th>
<td><input type="text" name="name2"></td>
</tr>
<tr>
<th>국어</th>
<td><input type="text" name="kor2"></td>
</tr>
<tr>
<th>영어</th>
<td><input type="text" name="eng2"></td>
</tr>
<tr>
<th>수학</th>
<td><input type="text" name="mat2"></td>
</tr>
<tr>
<td colspan="2" align="center">
<input type="button" value="수정" onclick="send2(this.form);">
<input type="button" value="취소" onclick="my_cancel()">
</td>
</tr>
</table>
</form>
</div>
</body>
</html>
# sung_del.jsp
<%@page import="dao.SungjukDAO"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
request.setCharacterEncoding("utf-8");
// 요청 객체 내의 값을 받아온다.
int no = Integer.parseInt(request.getParameter("no"));
int res = SungjukDAO.getInstance().delete(no); // 삭제 메소드 (지운 행의 수를 반환한다.)
// sendRedirect
// - 특정 화면 혹은 주소로 이동하는 방식
// - html에서 location.href를 사용한다면 자바에서는 sendRedirect를 사용한다.
response.sendRedirect("jdbc_Sungjuk.jsp");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
</body>
</html>
# sung_register.jsp
<%@page import="vo.SungjukVO"%>
<%@page import="java.util.List"%>
<%@page import="dao.SungjukDAO"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
request.setCharacterEncoding("utf-8");
//
String name = request.getParameter("name");
int kor = Integer.parseInt(request.getParameter("kor"));
int eng = Integer.parseInt(request.getParameter("eng"));
int mat = Integer.parseInt(request.getParameter("mat"));
// VO 객체에 저장
SungjukVO vo = new SungjukVO();
vo.setName(name);
vo.setKor(kor);
vo.setEng(eng);
vo.setMat(mat);
// 인서트 작업 동작
SungjukDAO.getInstance().insert(vo);
response.sendRedirect("jdbc_Sungjuk.jsp");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
</body>
</html>
# sung_update.jsp
<%@page import="dao.SungjukDAO"%>
<%@page import="vo.SungjukVO"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
request.setCharacterEncoding("utf-8");
String name = request.getParameter("name2");
int kor = Integer.parseInt(request.getParameter("kor2"));
int eng = Integer.parseInt(request.getParameter("eng2"));
int mat = Integer.parseInt(request.getParameter("mat2"));
int no = Integer.parseInt(request.getParameter("no"));
// 받아온 파라미터를 vo객체에 저장
SungjukVO vo = new SungjukVO();
vo.setName(name);
vo.setKor(kor);
vo.setEng(eng);
vo.setMat(mat);
vo.setNo(no);
int res = SungjukDAO.getInstance().update(vo);
response.sendRedirect("jdbc_Sungjuk.jsp");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
</body>
</html>
'Web > Jsp' 카테고리의 다른 글
[Jsp] Forward & Bind (0) | 2025.01.09 |
---|---|
[Jsp] 스크립트릿 (0) | 2025.01.09 |
[Jsp] 내장 객체 (0) | 2025.01.09 |
[Jsp] Dynamic Web Project (0) | 2025.01.09 |
[Jsp] Jsp (0) | 2025.01.09 |