티스토리 뷰
Operation History
웹 페이지 안에서의 모든 기록으로, Article과 Article-Admin에 다 필요하다.
Operation History는 법적인 부분에서도 근거 자료로 활용할 수 있고, 마케팅 용도로도 활용할 수 있기때문에 반드시 수집을 해두어야 한다.
(네이버가 이걸 참 잘한다고....)
1. DB에 테이블을 만들자(테이블명: OPER_HIST)
1)HISTORY_ID : OPER_HIST의 PK (시퀀스도 만들어주어야 한다. 시퀀스 이름 : HISTORY_ID_SEQ)
2) IP : 클라이언트의 IP 주소를 담는 컬럼
3) MEMBER_ID : 클라이언트의 아이디를 담는 컬럼
4) CRT_DT : 클라이언트가 접속한 현재 시간을 담는 컬럼
5) URL : 클라이언트가 현재 머무르고 있는 URI
6) ACTION_CODE : 수행 코드
7) DESCRIPTION : 상세 수행 내용
8) ETC : 기타
2. Article 프로젝트에 history 패키지를 만든 후, vo, biz, dao 패키지를 만든다. (사용자는 Operation History를 볼 일이 없기 때문에 web 폴더가 필요 없다.) 프로젝트 구성도는 다음과 같다.
3. 이제 내용을 하나하나 채워간다.
먼저 VO부터!
OperationHistoryVO.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
package com.ktds.smahn.history.vo;
import com.ktds.smahn.member.vo.MemberVO;
public class OperationHistoryVO extends MemberVO { private int historyId; private String ip; // private String memberId; (MemberVO에 정의되어 있음) private String createdDate; private String url; private String actionCode; private String description; private String etc;
public int getHistoryId() { return historyId; }
public void setHistoryId(int historyId) { this.historyId = historyId; }
public String getIp() { return ip; }
public void setIp(String ip) { this.ip = ip; }
public String getCreatedDate() { return createdDate; }
public void setCreatedDate(String createdDate) { this.createdDate = createdDate; }
public String getUrl() { return url; }
public void setUrl(String url) { this.url = url; }
public String getActionCode() { return actionCode; }
public void setActionCode(String actionCode) { this.actionCode = actionCode; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public String getEtc() { return etc; }
public void setEtc(String etc) { this.etc = etc; }
}
|
OperationHistoryDAO.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
package com.ktds.smahn.history.dao;
import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException;
import com.ktds.smahn.history.vo.OperationHistoryVO; import com.ktds.smahn.member.dao.Const; import com.ktds.smahn.util.xml.XML;
public class OperationHistoryDAO {
public void insertHistory(OperationHistoryVO historyVO) {
loadOracleDriver();
Connection conn = null; PreparedStatement stmt = null;
try {
conn = DriverManager.getConnection(Const.DB_URL, Const.DB_ID, Const.DB_PASSWORD);
String query = XML.getNodeString("//query/operationHistory/insertHistory/text()"); stmt = conn.prepareStatement(query);
stmt.setString(1, historyVO.getIp()); stmt.setString(2, historyVO.getMemberId()); stmt.setString(3, historyVO.getUrl()); stmt.setString(4, historyVO.getActionCode()); stmt.setString(5, historyVO.getDescription()); stmt.setString(6, historyVO.getEtc());
stmt.executeUpdate();
} catch (SQLException e) { throw new RuntimeException(e.getMessage(), e); } finally { closeDB(conn, stmt, null); }
}
private void loadOracleDriver() { try { Class.forName("oracle.jdbc.driver.OracleDriver"); } catch (ClassNotFoundException e) { throw new RuntimeException(e.getMessage(), e); } }
private void closeDB(Connection conn, PreparedStatement stmt, ResultSet rs) { if (rs != null) { try { rs.close(); } catch (SQLException e) { } } if (stmt != null) { try { stmt.close(); } catch (SQLException e) { } } if (conn != null) { try { conn.close(); } catch (SQLException e) { } }
}
}
|
OperationHistoryBiz.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
package com.ktds.smahn.history.biz;
import com.ktds.smahn.history.dao.OperationHistoryDAO; import com.ktds.smahn.history.vo.OperationHistoryVO;
public class OperationHistoryBiz {
private OperationHistoryDAO dao;
public OperationHistoryBiz(){ dao = new OperationHistoryDAO(); }
public void addHistory(OperationHistoryVO historyVO){
dao.insertHistory(historyVO);
}
}
|
그리고 추가적으로 필요한 클래스와 인터페이스들!
Description.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | package com.ktds.smahn.history.vo;
public interface Description {
// 바뀌는 부분은 %s로, %s는 문자가 들어올 것이다 라는 String format public static final String VISIT_LOGIN_PAGE = "[%s] 가 로그인 페이지에 접근했습니다."; public static final String LOGIN = "[%s] 님이 로그인했습니다."; public static final String LOGIN_FAIL = "[%s]가 [%s]로 로그인을 시도했지만, 실패했습니다."; public static final String ALREADY_LOGIN = "[%s] 님이 이미 로그인되어, List 페이지로 이동합니다.";
public static final String LIST = "[%s]님이 목록보기 페이지에 접근했습니다."; public static final String LIST_PAGING = "[%s]님이 [%s]번째 페이지로 이동했습니다."; public static final String LIST_SEARCH = "[%s]님이 목록보기 페이지에서 [%s]로 [%s]를 검색했습니다.";
public static final String DETAIL = "[%s]님이 [%s]번째 글을 읽었습니다."; public static final String DETAIL_DESCRIPTION = "제목 : [%s]<br/>글쓴이 : [%s]<br/>내용: [%s]<br/>"; }
|
ActionCode.java
1 2 3 4 5 6 7 8 9 10 11 |
package com.ktds.smahn.history.vo; /** * Action Code를 상수로 저장해놓은 클래스 * */ public interface ActionCode { public static final String LOGIN = "MB_L"; public static final String ARTICLE = "AR_L"; }
|
BuildDescription.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
package com.ktds.smahn.history.vo;
public class BuildDescription { /** * * @param format * @param args 가변 인자(파라메터를 제한없이 넣을 수 있는)배열 * @return */ public static String get( String format, String ... args ) { // format : %s에 들어가는 것을 args로 바꿔줌 String desc = String.format(format, args); return desc;
}
}
|
3. 클래스를 다 만들었다면, 이제 jsp를 수정해준다.
Operation History 의 경우 서블릿이 없기 때문에 jsp에서 처리해준다.
서블릿에서는 request.getSession(); 이 jsp에서는 session과 동일하다. session만 쓰면 자동으로 session을 가져올 수 있다.
추가된 부분은 아래와 같다.
이해를 돕기위해 전체 코드도 첨부한다.
index.jsp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | <%@page import="com.ktds.smahn.history.vo.ActionCode"%> <%@page import="com.ktds.smahn.history.vo.Description"%> <%@page import="com.ktds.smahn.history.vo.BuildDescription"%> <%@page import="com.ktds.smahn.history.vo.OperationHistoryVO"%> <%@page import="com.ktds.smahn.history.biz.OperationHistoryBiz"%> <%@page import="com.ktds.smahn.member.vo.MemberVO"%> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<% /* Operation History 의 경우 서블릿이 없기 때문에 jsp에서 처리해준다. 서블릿에서는 request.getSession(); jsp에서는 session만 쓰면 자동으로 session을 가져올 수 있다. Ctrl+Shift+M : 자동 임포트 */ MemberVO member = (MemberVO) session.getAttribute("_MEMBER_"); if ( member != null ){ OperationHistoryVO historyVO = new OperationHistoryVO(); /* 요청자의 원격 호스트를 가져오면 어떤 ip를 쓰는지 알수 있다. */ historyVO.setIp(request.getRemoteHost()); /* 로그인을 한 상태이기 때문에 세션에서 정보를 가져온다. */ historyVO.setMemberId(member.getMemberId()); historyVO.setUrl(request.getRequestURI()); historyVO.setActionCode(ActionCode.LOGIN); historyVO.setDescription(BuildDescription.get(Description.ALREADY_LOGIN, member.getMemberId()));
OperationHistoryBiz biz = new OperationHistoryBiz(); biz.addHistory(historyVO); /* 로그인이 되어있다면 list 페이지로 이동 */ response.sendRedirect("/list"); return; }
/* 로그인이 안되있을 경우는 index페이지로 */
OperationHistoryVO historyVO = new OperationHistoryVO(); /* 요청자의 원격 호스트를 가져오면 어떤 ip를 쓰는지 알수 있다. */ historyVO.setIp(request.getRemoteHost()); /* 로그인을 안한 상태라, 누군지 모른다 */ historyVO.setMemberId(""); historyVO.setUrl(request.getRequestURI()); historyVO.setActionCode(ActionCode.LOGIN); historyVO.setDescription( BuildDescription.get( Description.VISIT_LOGIN_PAGE, request.getRemoteHost() ) );
OperationHistoryBiz biz = new OperationHistoryBiz(); biz.addHistory(historyVO); %>
<jsp:include page="./WEB-INF/view/common/header.jsp"></jsp:include> <jsp:include page="./WEB-INF/view/common/login.jsp"></jsp:include> <jsp:include page="./WEB-INF/view/common/footer.jsp"></jsp:include> |
이렇게 추가를 해주면 이제 맨 첫페이지에 접속을 할 때마다 기록이 OPER_HIST 테이블에 쌓이는 것을 확인할 수 있을 것이다.
4. 이런 식으로 모든 페이지마다 추가를 시켜줘야하는데, 동일한 코드를 매번 치는 것은 굉장히 비효율적이므로, 페이지에 도달하기 전 Filter에 내용을 적용하면 훨씬 효과적이다.
SessionCheckFilter.java 를 열어 아래 부분을 추가해준다.
이해를 돕기 위해 전체 코드도 첨부한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | package com.ktds.smahn.filter;
import java.io.IOException; import java.util.ArrayList; import java.util.List;
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.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession;
import com.ktds.smahn.history.vo.OperationHistoryVO; import com.ktds.smahn.member.vo.MemberVO;
/** * 세션을 체크한다. * Servlet Filter implementation class SessionCheckFilter */ public class SessionCheckFilter implements Filter {
private List<String> whiteList;
private List<String> staticResourceList;
/** * 필터에서 생성자는 의미 없다. * Default constructor. */ public SessionCheckFilter() {
whiteList = new ArrayList<String>(); // 게스트가 바로 통과할 수 있는 uri whiteList.add("/"); whiteList.add("/doLogin"); whiteList.add("/registerMember"); whiteList.add("/addNewMember"); // url 앞에 뜨는 아이콘 whiteList.add("/favicon.ico");
//resource를 jsp에서 보여주려면 따로 list를 만들어주어야 한다. staticResourceList = new ArrayList<String>(); // /resource --> resourceImage 도 그냥 지나감. staticResourceList.add("/resource/"); }
/** * @see Filter#destroy() */ public void destroy() { }
/** * @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain) */ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//ServletRequest, ServletResponse : 인터페이스 //HttpServletRequest, HttpServletResponse : 클래스 // 인터페이스와 클래스는 상속관계, 즉 부모와 자식 관계 HttpServletRequest req = (HttpServletRequest) request;
String uri = req.getRequestURI(); System.out.println(uri);
if( !whiteList.contains(uri) ){
boolean isURIResourceFile = false;
for( String staticResource : staticResourceList ){ if( uri.startsWith(staticResource) ) { //사용자가 요청한 uri가 /resource로 시작한다면 그냥 지나가라. isURIResourceFile = true; break; } }
if( !isURIResourceFile ){
// whiteList에 uri가 없다면 세션을 체크해라. HttpSession session = req.getSession();
// 데이터를 넣을때는 setAttribute, 가지고올때는 getAttribute를 쓴다. MemberVO member = (MemberVO) session.getAttribute("_MEMBER_"); // 세션에 null이 있으면 로그인페이지로 redirect한다. if( member == null ){ HttpServletResponse res = (HttpServletResponse) response; res.sendRedirect("/"); return; } else {
// 매 페이지마다 Operation History의 공통내용을 VO에 저장한다. OperationHistoryVO historyVO = new OperationHistoryVO(); historyVO.setIp(request.getRemoteHost()); historyVO.setMemberId(member.getMemberId()); historyVO.setUrl(req.getRequestURI());
request.setAttribute("OperationHistoryVO", historyVO);
} } }
// pass the request along the filter chain chain.doFilter(request, response); }
/** * web.xml에 어떤 설정을 해 줄때만 가능하다. * @see Filter#init(FilterConfig) */ public void init(FilterConfig fConfig) throws ServletException {
}
}
|
이렇게 공통내용을 Session에서 처리해주면 다른 서블릿에는 이정도의 코드만 추가해주면 된다.
예를 들어 DetailServlet.java 에 추가한 것을 보면 아래와 같다.
빨간색 박스 부분만 더 넣어주면 이제 서블릿에 도달하기 전 필터에서 이미 historyVO에 내용이 담겨서 오기 때문에 달라지는 부분, 즉 Action Code와 Description, Etc 만 서블릿에서 처리해주면 된다.
이런식으로 매 페이지마다 클라이언트가 하는 모든 동작들을 수집해서 DB에 담아두는 것이다.!^^
'프로그래밍 > ㄴ실습' 카테고리의 다른 글
[게시판 만들기] 관리자 페이지 만들기(1) (0) | 2016.03.15 |
---|---|
[게시판 만들기] 11. 댓글달기 (2) | 2016.03.15 |
[게시판 만들기] 10. 파일 테이블에 파일 정보 insert, select하기 (0) | 2016.03.11 |
[게시판 만들기] 9.파일 업로드, 다운로드하기 (0) | 2016.03.10 |
[게시판 만들기] 8. 쿠키 이용하기 (0) | 2016.03.10 |
- Total
- Today
- Yesterday
- er다이어그램
- mybatis
- 제이쿼리
- jQuery
- 뒤로가기 버튼
- spring
- sql
- 메소드
- JSP
- ERD
- 예외처리
- 쿼리
- 배열
- 게시판 만들기
- 뉴스피드 가져오기
- aop
- Linear Layout
- intent
- Erwin
- Relative Layout
- activity
- 자바프로그래밍
- query
- mongo db
- 메뉴바에 버튼 생성하기
- 클래스
- facebook 연동
- 포스팅하기
- MVC
- 글쓰기 버튼
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |