DB를 사용하여 로그인 하기
이번 강좌에서는 이전 예제인 “Struts 더 더 간단한 로그인 예제”의 기능을 확장 하여 예제를 만들어 보도록 하겠습니다.
먼저 이클립스에서 Login 이라는 톰캣 프로젝트를 하나 만드세요~
이전 예제와 달라진 기능은 다음과 같습니다.
1. 개발자가 로깅을 할 수 있도록 Log.java라는 클래스를 이용하여 디버깅을 용이 하도록 했습니다.
2. 데이터베이스 커넥션 풀(Connection Pool) 기능을 이용하여 작성 했습니다. Connection Pool은 ActionServlet을 확장 한 NewActionServlet이 초기에 사용자의 요청을 받는 시점에 여러 개의 커넥션을 생성 하도록 구성 하였으며 Action 부분에서 그 커넥션을 가져다 쓰도록 했습니다. 참고로 본 예제에서는 bitmechanic의 JDBC Pool(http://www.javaservice.net/~java/bbs/read.cgi?m=resource&b=jdbc&c=r_p&n=1073555435&k=Bitmechanic&d=tb#1073555435) (http://www.bitmechanic.com/projects/jdbcpool/) 을 이용 했으며 여러분들의 개발 목적에 따라 Poolman(http://sourceforge.net/projects/poolman/) , Expresso(http://www.jcorporate.com) 또는 Jakarta의 DBCP(http://jakarta.apache.org/commons/index.html) 등을 이용하실 수 있습니다.
“이벤트 리스너를 이용한 로깅” 이라는 강좌에 보시면 Struts의 ActionServlet을 이용하지 않고 ServletContext의 리스너를 이용하여 컴텍스트가 초기화 되는 시점 즉 톰캣이 시작되는 시점에 DB Connection Pool을 초기화 한 예제도 있으니 꼭 참고해 보시기 바랍니다.
3. 사용자의 인증을 처리함에 있어 ILoginDAO.java라는 인터페이스를 만들어 여기에서 authUser라는 메소드를 추상 메소드로 정의 했습니다. 이 인터페이스를 구현한 클래스가 LoginDAO.java 이며 이곳에서 실제 인증이 이루어 집니다.
아래는 전체 소스 입니다.
-----------------
/WEB-INF/app.tld
------------------
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"
"http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">
<taglib>
<tlibversion>1.0</tlibversion>
<jspversion>1.1</jspversion>
<shortname>Application Tag Library</shortname>
<uri>http://jakarta.apache.org/taglibs/struts-example-1.0</uri>
<info>
Example Application.
</info>
<tag>
<name>IsLogin</name>
<tagclass>login2.IsLogin</tagclass>
<bodycontent>empty</bodycontent>
<info>
Validate that there is a currently logged on user, by checking for
the existence of a session-scope bean under the specified name.
If there is no such bean, forward control to the specified page,
which will typically be a logon form.
name - Name of the session-scope bean to check for [user]
page - Context-relative path to the logon page [/logon.jsp]
</info>
<attribute>
<name>name</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>page</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>
----------------------
IsLogin.java
----------------------
package login2;
import javax.servlet.http.HttpSession;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;
import login2.Constants;
public final class IsLogin extends TagSupport {
private String name = login2.Constants.USER_KEY;
private String page = "/login.jsp";
public String getName() {
return (this.name);
}
public void setName(String name) {
this.name = name;
}
public String getPage() {
return (this.page);
}
public void setPage(String page) {
this.page = page;
}
public int doStartTag() throws JspException {
return (SKIP_BODY);
}
public int doEndTag() throws JspException {
// Is there a valid user logged on?
boolean valid = false;
HttpSession session = pageContext.getSession();
if ((session != null) && (session.getAttribute(name) != null))
valid = true;
// Forward control based on the results
if (valid)
return (EVAL_PAGE); //JSP 페이지의 다음을 수행
else {
try {
pageContext.forward(page);
} catch (Exception e) {
throw new JspException(e.toString());
}
return (SKIP_PAGE); //JSP 페이지의 다음을 스킵
}
}
/**
* Release any acquired resources.
*/
public void release() {
super.release();
this.name = Constants.USER_KEY;
this.page = "/login.jsp";
}
}
----------------------
login.jsp
----------------------
<%@ page pageEncoding="euc-kr" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<html>
<head>
<title><bean:message key="login.title" /></title>
<html:base/>
</head>
<body>
<!-- 웹서버가 보내는 응답에 오류가 있다면 출력 -->
<html:errors/>
<!-- LoginSummit에 대해서는 struts-config.xml에 Mapping을 하게 된다 -->
<!-- focus="id" 라는 문법에 의해 자동으로 자바스크립트를 생성 합니다. -->
<html:form action="/LoginSubmit" focus="id">
<table>
<!-- 아래에서 텍스트 출력은 application.properties에서 가져 옵니다... -->
<tr>
<th align="right"><bean:message key="prompt.id"/></th>
<!-- 아래는 input type=text 와 동일한 기능을 합니다 -->
<td><html:text property="id" value=""/></td>
</tr>
<tr>
<th align="right"><bean:message key="prompt.password"/></th>
<!-- 만약 로그인을 실패하여 다시 돌아오는 경우 pwd 항목의 값을 비울때는 아래처럼 redisplay="false" 라고 하면 됩니다. HTML의 password 항목과 유사함 -->
<td><html:password property="pwd" redisplay="false"/></td>
</tr>
<tr>
<!-- Submit 버튼과 Reset 버튼을 생성 -->
<!-- 버튼의 라벨은 application.properties에서 내용을 가져 옵니다 -->
<th></th>
<td>
<html:submit>
<bean:message key="login.login" />
</html:submit>
<html:reset>
<bean:message key="login.reset" />
</html:reset>
</td>
</tr>
</table>
</html:form>
</body>
</html>
-----------------
main.jsp
------------------
<%@ page language="java" %>
<%@ page contentType="text/html;charset=euc-kr" %>
<!-- 최초 사용자가 접속하게 되는 페이지 입니다. -->
<!-- 아래는 import와 동일한 기능을 하는 태그로 태그 확장을 사용 할 수 있도록 합니다 -->
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>
<%@ taglib uri="/WEB-INF/app.tld" prefix="myTag" %>
<!-- 인증이 안된 사용자는 login.jsp로 보내 버립니다. -->
<myTag:IsLogin/>
<html>
<head>
<title><bean:message key="main.title" /></title>
<html:base/>
</head>
<body>
<h3>반갑습니다.<bean:write name="user" property="id"/>님!</h3>
<ul>
<li>
<html:link forward="logoff">로그아웃</html:link>
</li>
</ul>
<body>
</html>
-------------------
MyFilter.java
-------------------
package filter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.RequestProcessor;
public class MyFilter extends RequestProcessor {
protected boolean processPreprocess(
HttpServletRequest request,
HttpServletResponse response) {
try {
request.setCharacterEncoding("euc-kr");
return true;
} catch (Exception e) {
return false;
}
}
}
-----------------
Constants.java
------------------
package login2;
public class Constants {
/**
* 로그인한 사용자의 LoginForm이 저장될 세션 범위의 Attriute 이름
*/
public static final String USER_KEY = "user";
/**
* ActionForward에서 사용되는 값
*/
public static final String SUCCESS = "success";
/**
* ActionForward 로그인 동작을 나타내는 Tocken
*/
public static final String LOGIN = "login";
/**
* ActionForward welcome 동작을 나타내는 Tocken
*/
public static final String WELCOME = "welcome";
}
------------------
NewActionServlet.java
------------------
Connection Pool을 사용자가 요청을 하는 시점에 생성하기 위해 ActionServlet을 상속한 NewActionServlet을 만들었습니다. 이 파일을 만든 후 사용자의 모든 요청을 이 서블릿이 받기 위해서는 web.xml 파일을 수정 해야 합니다. 본 강좌의 마지막 부분에 있는 web.xml을 참고 하시기 바랍니다.
package login2;
import javax.servlet.ServletException;
import javax.servlet.ServletConfig;
import org.apache.struts.action.ActionServlet;
//Connection Pool
import com.bitmechanic.sql.*;
public class NewActionServlet extends ActionServlet {
private ConnectionPoolManager cpm;
/*
* poolAias는 커넥션 풀을 별칭(Alias)를 줘서 여러 개의 풀을 이용할 수 있게 해줍니다.
* 이 값을 잘 기억하였다가 LoginDAO.java에서 실제 커넥션을 수립할때 인자로 줘야 합니다.
*/
private String POOL_ALIAS = "wink";
private String JDBC_DRIVER = "oracle.jdbc.driver.OracleDriver"; // driver
private String DB_URL = "jdbc:oracle:thin:@localhost:1521:WINK"; // url
private String DB_USER = "SCOTT"; // id
private String DB_PASSWORD = "TIGER"; // pw
/* reapConnInterval은 다른 커넥션 풀들과는 틀린 부분이며 중요한
* 옵션입니다.
* reapConnInterval은 두가지 경우에 커넥션 풀의 connVector에서 커넥션을 제거하게 됩니다.
* 1. 풀에서 conn이 나갔는데(checkout) checkoutTimeout 시간안에 돌아오지 않을 경우
* 2. 풀의 connVector에서 전혀사용되지 않는 상태(idle)로 idleTimeout 시간을 초과할 경우
* bitmechanic의 커넥션 풀은 최대 Connection 수를 설정해도 30이면 30개 커넥션이 담겨져
* 있는 것이 아니라 상황에 따라 적절히 조정 하며 전혀 사용이 되지 않으면(Idle)수를 줄입니다.
* 즉, maxConn이 30이어도 사용특성에 따라 몇 개만이 풀에서 운용될 수도 있다는 이야기 입니다.
*/
private int REAP_CONN_INTERVAL = 300; // 여기서부터는 bitmechanic 을 위한 환경 설정 값
/* 커넥션 풀에 담을 수 있는 커넥션의 최대 수 */
private int MAX_CONNECTION = 20;
/* idleTimeout은 ConnectionPoolManager가 그 간격으로 수행을
* 하게 되므로 적당한 시간을 설정하면 됩니다.
* (데이터베이스의 불필요한 Open Session 수를 줄일 수 있슴)
*/
private int IDLE_TIMEOUT = 60;
/* checkoutTimeout은 너무 짧게 설정해도 안되는게 정상적인 query 수행시간이 길다면
* 정상적으로 작동하는 query 수행 중에 Connection을 Reaping(제거)해 버리므로
* 최대의 쿼리 수행 시간보다 크게 설정해야 합니다.
* 즉 풀에서 나갔다가 돌아오는 시간의 Timeout 설정
*/
private int CHECKOUT_TIMEOUT = 60;
//Checkout의 최대 수를 지정 합니다
private int MAX_CHECKOUT = 15;
public void init(ServletConfig config) throws ServletException
{
try
{
Class.forName(JDBC_DRIVER);
cpm = new ConnectionPoolManager(REAP_CONN_INTERVAL);
cpm.addAlias(POOL_ALIAS,JDBC_DRIVER,DB_URL,DB_USER,DB_PASSWORD,MAX_CONNECTION,IDLE_TIMEOUT,CHECKOUT_TIMEOUT, MAX_CHECKOUT);
// connection pool 생성 후에 부모 class의 init method 연속 수행
super.init(config);
}
catch ( Exception e1 )
{
e1.printStackTrace();
}
}
}
---------------------
LoginForm.java
---------------------
package login2;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
public class LoginForm extends ActionForm {
private String id=null;
private String pwd=null;
public String getId() {
return id;
}
public String getPwd() {
return pwd;
}
public void setId(String id) {
this.id = id;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public void reset(ActionMapping mapping, HttpServletRequest request) {
pwd = "";
id = "";
}
/* struts-config.xml 에서 메핑시 validate="true" 라고 설정 되면 아래의
validate 함수가 호출 되어 입력되는 값에 대한 validation check를 하게 됩니다.
HTTP 요청으로 부터 해당 폼의 값을 set 한 후 아래 메소드가 호출 됩니다.
참고로 validate에 위해 반환되는 ActionErrors 객체 역시 Framework에 관련된 클래스 입니다.
만약 validate에서 null이나 empty가 아닌 ActionError 객체를 리턴하게 되면
login.jsp에 있는 <html:errors/> 태그는 오류를 출력하고 아니면 넘어가게 됩니다...
error.id.required, error.pwd.required는 일종의 키값으로 각 Locale마다 고유한 리소스를 가질 수
있으므로 메시지에 대해 손쉬운 관리를 제공 합니다.
예를들면 error.id.required = 사용자 ID는 필수 입력 항목 입니다. , 이러한 식으로 설정 합니다.
*/
public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) {
ActionErrors errors = new ActionErrors();
if((id == null) || (id.length()<1)) {
errors.add("error.id.required", new ActionError("error.id.required"));
}
if((pwd == null) || (pwd.length()<1)) {
errors.add("error.pwd.required", new ActionError("error.pwd.required"));
}
return errors;
}
}
--------------------
LoginAction.java
--------------------
package login2;
import org.apache.struts.action.Action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpServletResponse;
import login2.LoginForm;
import login2.Constants;
import login2.Log;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionForward;
/**
* @author 이종철
*/
public class LoginAction extends Action {
public ActionForward execute(ActionMapping mapping, ActionForm form,HttpServletRequest request, HttpServletResponse response) {
String id = ((LoginForm)form).getId();
String pwd = ((LoginForm)form).getPwd();
//Model을 이용하기 이용하여 인증부분의 처리를 합니다.
LoginDAO loginDAO = new LoginDAO();
UserInfoVO userinfo = loginDAO.authUser(id, pwd);
//에러 내용을 담을 ActionErrors 객체를 생성
ActionErrors errors = new ActionErrors();
if (userinfo==null) {
//인증 실패
errors.add(ActionErrors.GLOBAL_ERROR, new ActionError("error.login.invalid"));
}
//로그인 과정에서 에러가 있을 경우 Action클래스의 saveErrors 메소드를 이용해 에러를 저장하고
//이전의 로그인 페이지로 돌려 보내면 login.jsp의 <html:errors/> 태그에 의해 오류가 출력 돔
//mapping의 getInputForward()에서 반환하게 되는 정보는 struts-config.xml의 action태그의
//input 속성에 정의된 페이지로 이동하게 됩니다.
if (!errors.isEmpty()) {
saveErrors(request, errors);
return (mapping.getInputForward());
}
Log.println("LoginAction", "인증이 성공 함");
//인증 성공한 경우 세션에 LoginInfoVO(로그인한 사용자 정보를 담은 객체)저장
HttpSession session = request.getSession();
session.setAttribute(Constants.USER_KEY, userinfo);
Log.println("LoginAction", "세션에 userinfo빈을 set");
//로그를 남기자.
StringBuffer buf = new StringBuffer("LoginAction : User --> ");
buf.append(id + "logged in session");
servlet.log(buf.toString());
return (mapping.findForward(Constants.SUCCESS));
}
}
--------------------
LogoffAction.java
--------------------
package login2;
import org.apache.struts.action.Action;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import login2.Log;
import login2.Constants;
public class LogoffAction extends Action {
public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) {
//필요한 어트리뷰트를 뽑아 냅니다.
//LoginAction에서 사용자의 로그온개체를 Consants.USER_KEY라는 이름으로 세션에 저장했음
HttpSession session = request.getSession();
UserInfoVO userinfo = (UserInfoVO)session.getAttribute(Constants.USER_KEY);
if (userinfo != null) {
//로그를 남기자
Log.println("LogoffAction","userinfo 빈이 세션에 있다고 함");
StringBuffer buf = new StringBuffer("User Logout : " + userinfo.getId());
servlet.log(buf.toString());
}
//사용자의 로그인을 삭제, session.invaidate() 의 경우 션의 모든 것을 무효화 시킴.
session.removeAttribute(Constants.USER_KEY);
//session.invalidate();
//성공적으로 처리 되었음을 알림
return (mapping.findForward(Constants.SUCCESS));
}
}
--------------------
ILoginDAO.java
--------------------
DAO 클래스가 구현해야 하는 메소드를 정의 했습니다.
package login2;
import login2.UserInfoVO;
/**
* 데이터를 Access 하는 것들에 대한 인터페이스를 정의
*/
public interface ILoginDAO {
public UserInfoVO authUser(String id, String pwd); //abstract method
}
----------------------
LoginDAO.java
----------------------
package login2;
/* 아래의 예문중에 Log.println 이라고 하는 것은 로깅을 위한 것입니다.
* 빈들 사이에서 제어가 왔다 갔다 할때 디버깅을 하는 경우 어려움이 많습니다.
* 전 하드디스크의 특정 폴더에 파일에 로그를 기록 하도록 Log.java를 만들어
* 사용 합니다. Log.java의 내용은 간단 합니다.
* println 메소드는 로그파일의 이름과 로그의 내용을 인자로 줍니다.
*/
import java.sql.SQLException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.DriverManager;
import login2.Log;
import com.bitmechanic.sql.*;
public class LoginDAO implements ILoginDAO {
public UserInfoVO authUser(String id, String pwd) {
/**
* 이부분은 실제 DB에서 ID와 PASWORD로 인증을 해야 하는 부분 입니다.
*/
Connection con=null;
try {
//Connection Pool에서 Connection을 가지고 옵니다. with bitmechanic 커넥션 풀
//아래에서 wink는 NewActionServlet에서 정의한 커넥션 풀에 대한 Alias 입니다.
con = DriverManager.getConnection(ConnectionPoolManager.URL_PREFIX+"wink");
con.setAutoCommit(false);
String sql = "select pwd from userinfo where id = ?";
PreparedStatement pstmt = con.prepareStatement(sql);
pstmt.setString(1, id);
ResultSet rs = pstmt.executeQuery();
if (rs.next()) {
Log.println("LoginDAO","ID같은 데이터가 있다고 함");
if (rs.getString("pwd").toString().equals(pwd)) {
return new UserInfoVO(id,(String)rs.getString("pwd"));
}
else {
return null;
}
}
else {
return null;
}
}
catch (SQLException sqle) {
Log.println("LoginDAO", sqle.toString());
return null;
}
catch (Exception e) {
Log.println("LoginDAO", e.toString());
return null;
}
finally {
try{
if (con != null) {
con.close();
}
}
catch (SQLException e) {
}
}
}
}
--------------------
UserInfoVO.java
--------------------
package login2;
/**
* 로그인이 정상적으로 이루어진 경우 사용자의 정보 등을 저장 하는 빈
* 본 예제에서는 ID와 PASSWORD를 저장 합니다.
*/
public class UserInfoVO {
private String id = null;
private String pwd = null;
//LoginDAO에서 인증이 성공인 경우 아래의 생성자를 불러 값을 setting 합니다.
public UserInfoVO(String id, String pwd){
this.id = id;
this.pwd = pwd;
}
public String getId() {
return id;
}
public String getPwd() {
return pwd;
}
}
---------------------
Log.java
---------------------
필요에 따라 로그를 남기기 위한 클래스 입니다. 아래에서 로그가 남는 경로는 본인의 PC에 맞게 설정 하시기 바랍니다
package login2;
import java.io.*;
public class Log{
public static final String log_path = "c:/tomcat5/logs/";
public static void println(String filename, String log){
try{
FileWriter fw = new FileWriter(log_path+filename+".log", true);
fw.write(log+"\n");
fw.flush();
fw.close();
}catch(Exception e){
}
}
}
-------------------
Application.properties
-------------------
이 파일은 각종 메시지를 보관하는 파일 입니다. 이클립스 작업시 /WEB-INF/src/ 아래에 package(resources)를 만드시고 그곳에 이 파일을 만드세요~
login.title = LogIn
login.login = Login
login.reset = Cancel
prompt.id= User ID :
prompt.password=Password :
main.title=Main
###############################################3
error.id.required=<li>ID REQUIRED.</li>
error.pwd.required=<li>PASSWORD REQUIRED</li>
error.login.invalid=<li>ID/PASSWORD NOT CORRECT</li>
errors.header=<h3><font color="red">ERROR!</font></h3>
You must correct the following error(s) before proceeding:<UL>
errors.footer=</ul><hr>
-----------------------
struts-config.xml
-----------------------
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
<struts-config>
<!-- ========== Form Bean Definitions ================================== -->
<form-beans>
<form-bean name="loginForm" type="login2.LoginForm">
<form-property name="pwd" type="java.lang.String" />
<form-property name="id" type="java.lang.String" />
</form-bean>
</form-beans>
<!-- ========== Global Forward Definitions =============================== -->
<global-forwards>
<forward name="success" path="/main.jsp" />
<forward name="logoff" path="/logoff.do" />
<forward name="login" path="/login.jsp" />
</global-forwards>
<!-- ========== Action Mapping Definitions =============================== -->
<!-- valiedate를 true라고 함으로써 LoginForm의 validate가 호출 됩니다. -->
<action-mappings>
<!-- loginAction에 대한 정의 -->
<action
path="/LoginSubmit"
type="login2.LoginAction"
name="loginForm"
validate="true"
input="/login.jsp"
/>
<!-- logoffAction에 대한 정의 -->
<action
path="/logoff"
type="login2.LogoffAction"
validate="false"
/>
</action-mappings>
<!-- 한글 문제 처리를 위해 -->
<controller
contentType="text/html;charset=euc-kr"
debug="3"
locale="true"
nocache="true"
processorClass="filter.MyFilter"/>
<!-- ========== 아래는 스트러츠 Application에서 사용할 Message Resource들을 설정 ====== -->
<!-- 확장자가 properties인 application.properties를 만들어 넣으면 된다 ============ -->
<message-resources parameter="resources.application"/>
</struts-config>
-------------------
web.xml
--------------------
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<display-name>Welcome to Tomcat</display-name>
<description>
Welcome to Tomcat
</description>
<!-- Struts Tag Library Descriptors -->
<taglib>
<taglib-uri>/WEB-INF/struts-bean.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-bean.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/WEB-INF/struts-html.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-html.tld</taglib-location>
</taglib>
<taglib>
<taglib-uri>/WEB-INF/struts-logic.tld</taglib-uri>
<taglib-location>/WEB-INF/struts-logic.tld</taglib-location>
</taglib>
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>login2.NewActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
댓글 없음:
댓글 쓰기