-
Java Web Game 개발 가이드(3)Java Web Game 개발 가이드 2010. 1. 23. 01:19
Index.jsp 페이지 만들어 보기
값의 흐름을 제어하자
여기까지 오셨다면 이제 jsp개발을 할 수 있는 기초적인 지식은 다된 것입니다. 톰켓이 아닌 상용으로 널리 쓰이는 제우스 5 나 mssql 등의 설정 법도 이와 조금 다를 뿐 그리 어렵지 않기 때문 입니다. 사실 톰켓이 잘 사용하기 가장 어렵지요. 하지만 톰켓은 상용 웹 서버에 비해 동시 접속을 많이 버티지 못하며, mysql 역시 상용에 비해 성능이 떨어진다고들 합니다. 물론 필자의 미력한 실력으로 인한 오해 일수 있으나 사실이라 할지라도 우리가 개발하는 웹 게임은 무언가 거대한 어떤 것을 노리는 것은 아니지 않습니까? 아마 톰켓이 동시 접속을 버티지 못하는 상황이 온다면 참 기뻐해야 할 거 같습니다. 그 많은 사람들에게 인정을 받았다는 의미니까요.
일단 jsp를 이용한 개발을 하려면 Action을 이해할 필요가 있습니다. 배포하는 이 소스에 로그인 부분에 대한 샘플이 있습니다. 이 소스가 동작하기 위한 흐름이 존재하는데 이 흐름은 다음 그림을 봐 주세요.
결국 화면에서 버튼을 클릭하여 폼 전송을 하면 스트럿츠 기본 설정에 따라 Action 을 통해 사용자가 추가한 Act 클래스 까지 오게 됩니다. 이곳까지 왔다면 command 분기를 통해 model 클래스를 호출 하는데 이곳에서 기본적인 데이터 베이스 작업, 혹은 자세한 구현 로직을 코딩하게 됩니다. Model 작업이 끝났다면 Action 이 끝나고 다시 struts-config 에 정의된 forward 경로를 따라 output 페이지로 이동하게 됩니다.
즉 하나의 로직(회원가입)을 완성하기 위해서는 Act 클래스, model 클래스, 화면인 jsp, 사용할 쿼리파일의 작성, struts-config.xml 의 수정 작업이 있어야 합니다. 위의 그림에서 AppAction Class 까지는 개발자가 신경 쓸 필요가 없습니다.
간단한 로그인 페이지를 예로 들어 설명 해 보도록 하겠습니다. 이곳에서 기초적인 로그인 액션을 통해 기본적인 모델2 방식의 개발방법을 설명하고 나서는 개발에 필요한 컨텐츠에 대해 구성해 보도록 할 것입니다. 또한 컨텐츠의 구상에서 전체적인 웹 프로세스에 대한 시안을 작성하고, 각 개발 이슈에 따른 필요한 개발 사항들도 보도록 할 것입니다.
먼저 데이터 베이스에 회원정보 테이블을 만들어야 합니다. 저는 TBL_MEMBER로 생성 하도록 하겠습니다. 이 테이블에는 아이디와 비번만이 들어 있습니다.
로그인을 위해 index.jsp에 입력 로그인 박스와 버튼을 만들고, struts-config.xml에 해당 로그인 포워딩을 추가하겠습니다.
<%@ page contentType="text/html;charset=utf-8"%>
<%@ page import="com.struts.box.Box"%>
<%@ page import="com.struts.util.RequestUtils" %>
<%@ page import="java.util.Date" %>
<%@ page import="com.webgames.common.session.UserSession"%><%
String strErrMsg = "";
Exception ex = null;
UserSession userSession = null;
Box box = (Box) request.getAttribute("ActCOM_LGN");
Date date = null;
try{
userSession = (UserSession)session.getAttribute("UserSession");
if(box != null){
ex = (Exception) box.getObject("WEB_EXCEPTION_KEY");
if(ex != null) strErrMsg = ex.getMessage();
date = (Date)box.getObject("CUR_DATE");
}
}catch(Exception e){
strErrMsg = e.getMessage();
}
%>
<style>
<!--
form li{
height:35px;
}
-->
</style>
<script type="text/javascript">
<!--
function showErrMessage(message)
{
var iLen = message.length;
var showMessage;
if (iLen >= 1){
showMessage=message.replace("<||>","\n");
alert(showMessage);
}
}
//-->
</script>
<body onload="showErrMessage('<%=strErrMsg %>')">
<%
if(userSession == null){
%>
<form method="post" action="<%=RequestUtils.getPageLink(request,"/ActCOM_LGN_001000.do")%>" >
<input type="hidden" name="command" value="COM_LGN_001000" />
<div>
<h1>로그인을 해주세요.</h1>
<ul style="float:left">
<li>아이디</li>
<li>비밀번호</li>
</ul>
<ul style="float:left">
<li><input type="text" name="login_id" style="width:100px;" /></li>
<li><input type="password" name="member_pw" style="width:100px;" /></li>
</ul>
<ul style="float:left">
<li><input type="submit" value="로그인" /></li>
</ul>
</div>
</form>
<%
}else{
%>
<div>
<h1><%=userSession.getMEMBER_ID() %>님 안녕하세요.</h1>
<a href="logout.jsp">로그아웃</a>
</div>
<%
}
%>
<div>
오늘은 : <%=date %>
</div>
</body>
Index 페이지는 단순하게 세션이 존재하면 "안녕하세요"를 출력하며 세션이 없다면 로그인 화면이 나오도록 되어 있습니다. 이 페이지의 로그인 버튼을 클릭(submit) 하게 되면 이 컨트롤을 감싸고 있는 form 의 action 경로인 ActCOM_LGN_001000.do를 타도록 되어 있습니다. 이 *.do 로 되어있는 것이 struts 의 action 이라고 생각하시면 됩니다. 잘 이해가 가지 않겠지만 다음 그림을 봐 주세요.
이 소스에는 분기를 위한 1가지 사항이 빠져 있습니다. 매우 중요한 부분이니 잠시 후 설명하도록 하겠습니다.
Index 페이지의 폼 전송이 *.do 로 끝나면 web.xml의 서블릿 맵핑에 따라 이 Action은 struts-config.xml에 정의된(이 부분은 개발자가 추가한 맵핑입니다.) type에 해당하는 클래스를 따라 가게 됩니다. 즉 쉽게 이야기 해서 로그인 버튼을 누름과 동시에 이곳에 있는 input 값들은 /ActCOM_LGN_001000.java로 전송되며 class가 실행 됩니다. 이제부터는 자바를 이용해 모든 로직을 수행하게 됩니다.
이 스트럿츠 방식은 개발의 편의를 위해 약간의 개조 및 변형이 된 소스 입니다. BOX 객체에 모든 값을 쓸어 담는 방식이고 기본 struts 방식에 한 가지의 AppAction을 추가하여 개발 편의성을 향상시킨 소스이며, 자신에게 맞는 방법이 있다면 그 방식대로 사용해도 무방합니다. 어쨌든 이 방식으로 ActCOM_LGN_001000.java 로 왔다면 샘플 소스의 Act 부분에서 MdlCOM_LGN_001000소스의 객체를 생성 하는 부분이 있습니다. 실제적으로 이 MdlCOM_LGN_001000에서 로그인 데이터 베이스 처리를 하도록 되어 있습니다.
MdlCOM_LGN_001000 mdlCOM_LGN_001000 = new MdlCOM_LGN_001000(); //로그인 처리
if("COM_LGN_001000".equals(box.getString("command"))) { // 로그인 분기
}
모든 로직의 분기는 command 라는 hidden 값을 통해 이루어 지게 됩니다. 이 값을 변경하여 로그인, 회원가입, 중복검사 등을 분기하여 결과를 리턴 합니다. 그런데 위쪽에 보면 command 값이 빠져 있지요? 이 command 값에 대한 환기를 위해 빼 둔 것입니다.
소스상에 String strFORWARD = ""; 이 변수는 return mapping.findForward(strFORWARD); 로 넘겨지고 있습니다. 즉 이 포워딩 변수의 값은 struts-config.xml의 맵핑 값인
<forward name="COM_LGN_001000" path="/index.jsp" /> 의 값 과 맵핑 됩니다.
if( "pass".equals(box.get("result")) ){
strFORWARD = "COM_LGN_001000";
//로그인 모듈 수행
}
그럼 화면에서 전송에 해당하는 포워딩 설정을 위해 form 아래 코드를 추가 합니다.
<input type="hidden" name="command" value="COM_LGN_001000" />
결국 위와 같이 로직 수행 후 이동될 페이지 설정은 포워드에 설정하는 값에 따라 path="/index.jsp" 로 이동하게 되는 것입니다. 이해가 되셨나요? 조금 복잡할 수 있겠지만 익숙해 진다면 훨씬 효율적인 소스 관리가 가능 합니다. 이어서 model 작업에서의 데이터베이스 처리 부를 살펴 보겠습니다.
Act 에서 분기시 로그인 체크를 하는 포워드 분기에 모델 로그인 메서드를 호출하는 부분이 있습니다.
if("COM_LGN_001000".equals(box.getString("command"))) { // 로그인처리
mdlCOM_LGN_001000.selTBL_MEMBER_01(box);
}
위의 메서드를 찾아 들어가면 데이터 베이스에 대한 코딩 부가 있습니다.
con = ConnectionManager.getConnection(box);
wrapper = new DBWrapper(con);
sql = new SQLStore(box,this,"selTBL_MEMBER_01");
데이터 베이스 connection pool 에 대한 설정은 이미 이곳까지 오기 전에 box에 모두 담겨 있습니다. 그냥 일괄적으로 사용되도록 구현되어 있는 소스 입니다. 이렇게 간단히 con(커넥션 객체)를 얻어 왔으면 데이터베이스 트랜젝션 처리 객체인 wrapper 객체를 커넥션을 이용해 생성 합니다. 그리고 쿼링을 다루기 위한 sql 객체를 생성 하는데 이 SQLStore 는 3번째 인자 값인 selTBL_MEMBER_01 를 쿼리 파일에서 찾습니다. 또한 쿼리 파일은 자동적으로 이 MdlCOM_LGN_001000.java 와 같은 이름의 MdlCOM_LGN_001000.xml 파일을 참조하도록 되어 있습니다. 이 쿼리 파일에서 title이 selTBL_MEMBER_01 과 같은 sql 부분을 찾게 되는 것입니다. 그럼 이 쿼리에 바인딩(쿼리 조합)은 어떻게 시킬 수 있을까요?
쿼리 파일을 열어보면
SELECT *
FROM TBL_MEMBER
WHERE MEMBER_ID = ?
AND MEMBER_PW = ?
식으로 되어 있는 것을 볼 수 있습니다.
그런데 위에 =? 로 쓰여진 곳은 무엇을 의미 할까요? 네 눈치 빠른 분은 바로 이곳에 자바 소스에서 원하는 값을 채워 넣게 된다는 것을 눈치 채셨을 겁니다.
WHERE MEMBER_ID = sql.setString(1, box.getString("member_id"));
AND MEMBER_PW = sql.setString(2, box.getString("member_pw"));
이렇게 바인딩이 완성 됩니다. 실제로 쿼리가 수행되면
SELECT *
FROM TBL_MEMBER
WHERE MEMBER_ID = 'test'
AND MEMBER_PW = '1234'
라는 쿼리가 수행 되게 됩니다. 또한 이 쿼리에 sql.setInt(1, box.getInt(member_count"));
라고 바인딩 하면 WHERE MEMBER_ID = 5 식의 숫자가 맵핑 됩니다.
그렇다면 앞쪽의 조건이 변경되야 한다면 어떻게 해야 할까요?
어떤 조건때는 WHERE MEMBER_ID = 'test' 지만 어떤 경우에는 WHERE LOGIN_ID = 'test' 를 원한다면
쿼리에 작성시에는
SELECT *
FROM TBL_MEMBER
WHERE @ = ?
AND MEMBER_PW = ?
라고 적은 후
Mdl 소스에서
If(조건 1){
sql.setCondition(1, "MEMBER_ID");
}else{
sql.setCondition(1, "LOGIN_ID");
}
sql.setString(1, box.getString("member_id"));
sql.setString(2, box.getString("member_pw"));
와 같이 합니다. setString과 index가 따로 먹는데 주의 하세요.
jrs = wrapper.exeQuery(sql);
쿼리의 수행 결과는 위와 같이 받습니다. 이 JResultSet은 자바의 ResultSet 과 동일하게 사용 가능 합니다. 이 wrapper.exeQuery(sql);는 조회 결과를 수행 시 사용하며 수정이나 삭제, 입력은 wrapper.exeUpdateW(sql);를 이용합니다.
길게 설명 하였지만 위 그림을 보면 매우 단순하다는 걸 알 수 있습니다. 이렇게 결과를 받으면 조회된 리스트로 로직을 수행 해야 겠습니다.
if( jrs != null ) {
while(jrs.next()){
}
}
위의 while 문 속에서는 jrs 결과 값이 존재하는 동안 계속 루프를 돕니다. 이 안에서 jrs(JResultSet)의 내부 인덱스가 가리키는 부분의 값을 jrs.getString("컬럼명"); 과 같이 얻어 오게 됩니다.
box.put("result", "pass");
userSession = new UserSession();
userSession.setMEMBER_ID(jrs.getString("MEMBER_ID"));
userSession.setMEMBER_PW(jrs.getString("MEMBER_PW"));
box.put("SET_SESSION", userSession);
위와 같이 값을 가져 올 수 있습니다. 그런데 UserSession 이란 객체가 보이시나요? 이 객체는 흔히 세션이라 불리 우며 우리가 하루에도 몇 번이나 로그인 하는 홈페이지에서 사용됩니다. 이 정보가 생성되면 로그인한 사용자의 정보를 유지시켜 주며 지속적으로 참조하게 됩니다.
Box는 해쉬 맵이기 때문에 일단 생성한 세션을 put 하는 것이 보이시나요? 여기 담긴 세션은 이 모델이 끝나면 다시 Act 단에서 세션을 생성할 때 box.get("SET_SESSION") 하여 세션을 생성하는 것을 볼 수 있습니다.
이제 로그인 버튼을 클릭해 보겠습니다.
아래의 콘솔 창에 에러 메시지가 보이시나요?
아마 이런 에러들이 생길 수 있습니다. 위쪽은 log\webgames_log.log 경로가 없다는 것입니다. Log 폴더를 생성해주면 에러는 사라질 것입니다. 그 아래 쪽은 데이터 베이스 Access 를 하기 위해 커넥션을 맺을 때 문제가 있는 것입니다. 이 에러는 여러 가지의 원인이 있을 수 있습니다. 다시 한번 데이터 베이스 설정 부에서 주의 점을 환기해 봅니다.
위의 그림처럼 각 설정 파일들을 찾아 다니며 값이 올바른가 확인해 봅니다. 또한 Mysql 데이터 베이스에 접속하기 위해서는 커넥션.jar가 필요한데 첨부된 소스에는 이미 첨부가 되어 있지만 자주 실수 하는 부분 중의 하나 입니다. 그리고 자바가 실행될 시 전역적으로 참조하기 위한 파일이 하나 있는데 그것은 constants.java 파일입니다. 이 파일에 있는 데이터 베이스, 로그 설정도 잘 수정 합니다.
그리고 마지막으로 보여지는 에러는 sql.dtd 라는 파일이 쿼리 패스에 없어서 생기는 오류 인데 이 파일은 쿼리 포멧을 정의한 파일이라 꼭 있어야 하는 파일 입니다. 물론 첨부되어 있습니다.
로그 인에 성공하여 세션이 생성 되었습니다.
화면에서 값 가져오기
자바에서 수행된 로그인 결과에 대한 에러 값이나 결과로 생성된 객체들을 받아야 합니다. 이렇게 결과를 받아야 여러 가지 값을 화면에 표시할 수 있습니다.
Act 에서 request에 박스를 set 합니다. Request도 일종의 해쉬 맵 같은 거라 생각 하시면 쉽습니다. 이곳에 전역 에러 등을 해쉬 맵을 이용하듯 설정하고 화면에서 사용해도 됩니다. 또한 Act에서 예를 보여주기 위해 날자를 box에 put 한 것도 보이시나요? 이 값은 index.jsp에서 가져와 html 부분에 스크립릿 으로 표현하고 있습니다. 그 결과는 브라우저에 잘 찍히겠지요. 로그 아웃하면 Act를 타지 않고 바로 logout.jsp 를 호출 함으로 Act를 타지 않아 box가 없기 때문에 null 이 찍히는 것을 확인 할 수 있습니다.
여기까지 기본적인 로직의 흐름을 살펴 보았습니다. 이 기본적인 지식에서 조금만 더 노력하면 수 많은 응용이 가능 할 것 입니다.
'Java Web Game 개발 가이드' 카테고리의 다른 글
Java Web Game 개발 가이드(6) (0) 2010.02.03 Java Web Game 개발 가이드(5) (0) 2010.02.03 Java Web Game 개발 가이드(4) (0) 2010.02.03 Java Web Game 개발 가이드(2) (2) 2010.01.22 Java Web Game 개발 가이드(1) (1) 2010.01.22