-
Java Web Game 개발 가이드(12)Java Web Game 개발 가이드 2010. 2. 3. 20:39
Java를 이용한 섬세한 구현
이번 장의 제목 처럼 자바를 이용해 웹 게임을 제작 시 필요할 만한 개념과 사용 팁들을 다루어 볼까 합니다. 사실 자바라는 프로그래밍 언어는 웹에서 많이 사용하고 있지만 그 가능성은 대단히 방대하다 할 수 있습니다. 여러분은 웹 서버라는 컨테이너에 녹아 들어간 자바를 다루고 있기 때문에 기초적인 자바의 소양이 없다면 자바란 이렇게 사용하는 것 이라는 섣부른 결론을 내릴 수 있을지 모르겠습니다. 글의 중간에 잠시 나왔던 배치 서버와 같은 프로그램은 그냥 콘솔에서 실행만 해두면 무한 루프를 돌며 독자적으로 CPU를 점유하여 실행 합니다. 이렇게 UI(사용자가 사용하는 화면) 없이 구동되는 프로그램도 있고, 자바의 스윙을 이용해 사용자 인터페이스를 구현해 원하는 작업을 하도록 구성 할 수도 있습니다.
데이터 입력사항
이 글에서 사용하는 프레임 워크의 로직 분기 클래스인 Act 클래스와 그 상세 구현부인 Mdl클래스는 다음과 같은 구조로 되어 있습니다.
기본적인 작업의 주 내용은 데이터 베이스의 정보를 끌어오는데 있습니다. 하지만 Action 클래스에서 분기된 Action이 도달하는 화면에서 필요로 하는 정보가 대단히 많은 경우가 있습니다. 이때 여러분은 다음과 같이 정보를 가져올 수 있습니다.
- Action 클래스
If(업무 분기 1){
모델.데이터 조회();
}
- Model 클래스
Public void 데이터 조회(){
데이터 1 조회
Box.put(데이터1);
데이터 2 조회
Box.put(데이터2);
….
}
이렇게 model에서 수많은 쿼리를 반복하여 box.put 으로 쓸어 담아 둘 수 있습니다. 하지만 이렇게 작업하면 추후에 유지 보수 시 많은 어려움이 발생 할 수 있습니다. 이런 방식보다는 다음과 같은 방식이 관리에는 훨씬 편하다 할 수 있습니다.
- Action 클래스
If(업무 분기 1){
모델.데이터1 조회();
모델.데이터2 조회();
모델.데이터3 조회();
}
- Model 클래스
Public void 데이터1 조회(){
데이터 1 조회
Box.put(데이터1);
}
Public void 데이터2 조회(){
데이터 2 조회
Box.put(데이터2);
}
Public void 데이터3 조회(){
…
}
어떤 분이 이렇게 외칠지 모르겠습니다. "데이터 베이스 커넥션이 분리 되어 효율이 나빠 성능이 떨어집니다."
네 맞습니다. 이런 광범위한 소스가 적용되면 확실히 첫 번째처럼 하는 경우보다 성능이 떨어질 수 있습니다. 하지만 성능을 조금 포기하고서 라도 지옥 같은 유지보수를 피할 수 있다면 성능을 포기하는 것이 좋을 수 있습니다. 보다 성능을 높이는 것은 쿼리를 튜닝 하거나 데이터 베이스의 구조를 효율적으로 바꾸는 편이 좋을 수 있습니다.
위의 사항은 어디까지만 개개인, 환경에 따라 달라질 수 있음은 인정해야 합니다. 꼭 위처럼 하는 것이 답은 아니지만 어디까지나 혼자서 개발하는 방법을 채택하는 많은 여러분들은 후의 유지보수가 성패를 가를 수 있음을 인지해야 할 것입니다.
여러분은 조회 작업뿐 아니라 데이터의 저장과 수정 역시 매우 많이 수행할 것입니다. 다음은 일반적인 데이터 베이스의 입력 수정 작업의 샘플 입니다.
/**
* 사용자의 입력 예시
* @param box
* @return null
* @throws Exception
*/
public void insertData(Box box) throws Exception {
Connection con = null;
DBWrapper wrapper = null;
SQLStore sql = null;
int iResult = 0;
try{
con = ConnectionManager.getConnection(box);
wrapper = new DBWrapper(con);
wrapper.beginTransaction();
sql = new SQLStore(box,this,"insertData1");
sql.setString(1, box.getString("입력값1"));
sql.setString(2, box.getString("입력값2"));
sql.setString(3, box.getString("입력값3"));
iResult += wrapper.exeUpdateW(sql);
sql = new SQLStore(box,this," insertData2");
sql.setInt (1, box.getInt ("입력값1"));
sql.setInt (2, box.getInt("입력값2"));
iResult += wrapper. exeUpdateW (sql);
box.put("Result", iResult+"건이 입력되었습니다.");
wrapper.endTransaction();
}catch (Exception e){
wrapper.rollbackTransaction();
e.printStackTrace();
log.error(e);
throw e;
}finally{
if(con != null){
con.close();
}
}
}
위와 같이 여러분은 입력의 작업을 하게 됩니다. 위에 빨간색으로 표시된 부분을 트렌젝션 처리라고 합니다. 위의 예에서 두 가지의 입력 처리가 일어나고 있습니다. 만약 insertData1이 입력에 성공하고 insertData2가 입력도중에 실패 하였다면 insertData1의 작업은 취소가 될까요? 위와 같이 1,2의 업무 단위가 같기 때문에 쌍으로 이루어 져야 합니다. 이런 단위를 트렌젝션 단위라 하고 소스상에서 위와 같이 처리를 합니다. 즉 실패하였을 경우는 Exception 으로 에러가 떨어지고 rollbackTransaction 으로 데이터 베이스 작업이 취소 되는 것입니다.
보안이슈
이 밖에 여러분은 로그인한 사용자 혹은 로그인 하지 않은 사용자, 해커, 등 다양한 유입 경로를 가진 사용자의 요청을 처리해야 합니다. 아마도 다양한 사용자들은 여러 가지 비정상적인 루트로의 접근을 시도할 것이고 그것은 다양한 보안적인 이슈들을 생산해 낼 것입니다.
여러분은 개발을 하며 다음과 같은 사항을 주의 해야 할 것입니다.
- 로그 인을 하지 않은 세션이 없는 사용자의 접근을 원천적으로 막아야 합니다.
- 주소 창, 혹은 소스분석을 통해 로그인 없이 접근을 시도하는 사용자가 존재합니다.
- 정상적으로 입장이 불가능한 페이지로의 접근을 시도하는 사용자가 있습니다.
- 개발자가 원치 않는 get 방식의 접근을 막아야 합니다.
- Get 방식으로 접근하면 주소 창에 노출되는 param 값들을 통해 개발자가 원치 않는 값의 변조를 시도하는 사용자가 있습니다.
- 주소 값이 지속적으로 노출되면 주소 값을 분석하는 사용자가 생겨 납니다.
- 게임 내의 중요한 계산 값은 화면에서 param으로 받아오면 안되며 키 값을 통해 다시 한번 데이터베이스에서 조회 하여 서블릿 안에서 계산하여 사용해야 합니다.
- 변수의 변조, 복조를 이용해 해당 컨텐츠에서 의도치 않는 방식으로 아이템을 구매 하기도 합니다.
- 해킹을 원천적으로 막기 위해 param의 이용은 극히 제한 합니다.
- 컨텐츠 중 사용자에게 우연히 접근되도록 접근을 제한하는 메뉴 등이 있다면 주소를 통해 직접적인 접근을 허용하면 안되며 이럴 경우 독자적인 scret 값의 알고리즘을 개발하여 사용합니다.
기본적으로 웹은 보안에 매우 취약 할 수 밖에 없습니다. 아주 간단한 몇 가지의 소스 분석으로 데이터를 지워 버리는 것도 가능합니다. 수많은 보안적인 이슈를 모두 해결 할 수는 없지만 최소한의 피해라도 막기 위해서는 다양한 보안적인 고려사항을 생각해야 합니다. 위의 예는 아주 단편적이며 생각하기에 따라서는 훨씬 많은 위험 요소가 존재합니다.
상업적으로 변모시킬 사이트라면 최대한의 소스적인 보안적 이슈를 해결하고 전문적인 보안업체와 장비를 이용하는 것이 바람 직 합니다.
친절한 에러처리
여러 번 강조하지만 여러분이 개발하는 당장은 문제점이 생겨도 크게 어렵지 않게 디버그 모드를 이용해 잡아 낼 수 있을 것입니다. 하지만 실 운영에 이관된 후에는 소스 Freeze, Complete 과정을 거치기 전에 안정화를 위해 운영 시 발생하는 수많은 버그들을 잡아야 할 것입니다. 이 경우에는 오직 에러의 단서를 잡을 수 있는 것은 로그 밖에 없음을 인식하게 될 것입니다. 더욱이 꼼꼼하고 물샐 틈 없는 에러 처리를 하지 않았다면 도무지 에러는 오리무중으로 빠질 가능성이 높습니다.
여러분은 자바로 개발하다 보면 아래와 같은 에러처리 구문을 많이 볼 수 있을 것입니다.
try{
}catch(Exception ex){
ex.printStackTrace();
}
위의 구문은 최상위 Exception 클래스를 잡아냄으로 그 어떤 에러가 발생해도 이 곳으로 떨어질 수 있습니다. 하지만 모든 소스를 단지 위와 같이 뒤집어 씌워 놨다고 안심하고 있다가는 난감한 경우에 처하게 될 수 있습니다.
개발을 하다 보면 여러분은 다양한 상황에 직면하게 될 것입니다. 예를 들어 다음과 같은 상황들이 있습니다.
사용자의 값의 형 변환
값의 비교
배열의 참조, 동적 배열의 이용
에러가 발생 하여도 멈추지 않는 로직의 흐름이 필요
위의 예는 매우 단순한 예에 불과 하지만 위의 경우에 대한 올바른 예외처리에 대해 짚어 보겠습니다.
int iVal = Integer.parseInt(testVal[i]);
위는 객체 배열에서 strVal 이라는 스트링 값을 참조합니다. 이 단순한 코드에서 발생할 수 있는 에러는 배열 참조오류, 배열은 잘 참조 하였지만 testVal[i] 가 null 일 경우 Integer.parseInt 로 null 을 넘기면 발생하는 number 참조 오류가 발생 합니다. 마땅히 개발자라면 이 모든 오류 사항을 예측하고 적절한 대처를 해 두어야 합니다. 여러분은 아래와 같이 처리할 수 있을 것입니다.
String[] testVal = null;
int iVal = 0;
try{
testVal = new String[]{"3", "가나", null};
if(testVal != null && testVal.length > 0){
for(int i=0;i<testVal.length;i++){
iVal = Integer.parseInt(testVal[i]);
}
}else{
throw new Exception("testVal에 할당된 배열 값이 없습니다");
}
}catch(ArrayIndexOutOfBoundsException ex){
ex.printStackTrace();
throw new Exception("할당된 배열의 유효 인덱스를 벗어 났습니다");
}catch(NumberFormatException ex){
ex.printStackTrace();
throw new Exception("해당 배열 값이 숫자가 아니거나 null 입니다");
}catch(Exception ex){
ex.printStackTrace();
throw new Exception(ex.getMessage());
}
각 에러 발생 사유 별 catch 문은 발생할 수 있는 에러들마다 명시적으로 에러를 설명해 주고 있습니다. 이렇게 설정된 에러 메시지에 에러 발생 위치도 포함 시키면 나중에 로그를 보고 문제점이 발생한 지점을 찾을때 매우 빨리 해결 할 수 있을 것입니다. 또한 마지막에 finally 구문을 추가해 스트림이나 커넥션 변수들을 닫아주는 것도 좋은 방법이 될 수 있습니다.
'Java Web Game 개발 가이드' 카테고리의 다른 글
Java Web Game 개발 가이드(14) (1) 2010.02.03 Java Web Game 개발 가이드(13) (0) 2010.02.03 Java Web Game 개발 가이드(11) (0) 2010.02.03 Java Web Game 개발 가이드(10) (0) 2010.02.03 Java Web Game 개발 가이드(9) (0) 2010.02.03