[천상화원] 부모님 펜션 홈페이지를 제작해보자 4. 호스팅 서버 구매
[천상화원] 부모님 펜션 홈페이지를 제작해보자 3. 홈페이지 디자인 구매[천상화원] 부모님 펜션 홈페이지를 제작해보자 2. 개발 계획 수립[천상화원] 부모님 펜션 홈페이지를 제작해보자 1. Prolog
dev1song.tistory.com
이전 글
1. 기능 구현 이유
펜션 홈페이지 제작 계획을 세우며 가장 먼저 개발해야겠다고 생각한 기능은 바로 접속 로그를 쌓아 홈페이지 방문자의 대략적인 통계를 수집하는 것이었다.
우선 단순히 홈페이지를 방문하는 사람이 몇 명이나 될 지 궁금한 것이 첫번째 이유이고, 추후에 추가 기능을 개발할 때 개발한 기능의 효용성이 얼마나 있는지도 확인할 수 있다는 점, 그리고 관리자 페이지에서 통계를 그래프로 확인할 수 있는 기능도 추가하고 싶어서 개발하게 되었다.
2. 계획
접속 로그를 수집하는 방식에는 여러 가지가 있겠지만, 난 Java의 interceptor를 활용하여 방문자의 요청을 가로채 접속 정보를 가져오기로 했다. 문제는 방문자의 모든 요청을 interceptor로 가로채 버리면 로그 테이블에는 어마어마한 양의 로그가 쌓일 것이기 때문에 메인 페이지에 접속한 요청에 한해서만 로그를 쌓기로 했다.
3. 소스코드
package com.chunsang.pension.comm.interceptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.util.UrlPathHelper;
import com.chunsang.pension.comm.util.Globals;
import com.chunsang.pension.comm.service.CommonService;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
@Component
public class UserAccessInterceptor implements HandlerInterceptor{
@Resource(name = "CommonService")
private CommonService commonService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
//top 메뉴 조회에서 메뉴접속 이력을 남김(접속정보)
UrlPathHelper urlPathHelper = new UrlPathHelper();
String originalURL = urlPathHelper.getOriginatingRequestUri(request);
String url = originalURL.replaceFirst(request.getContextPath(), "");
// 시스템 접속로그 생성
insertAccessLog(request, url);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
/**
* 시스템 접속시 접속이력 기록
*/
private void insertAccessLog(HttpServletRequest request, String url) {
try {
Map<String, Object> accessInfo = new HashMap<>();
accessInfo.put("accessUrl", url);
accessInfo.put("ip", Globals.getRequestIp());
accessInfo.put("os", getClientOS(request.getHeader("USER-AGENT")));
accessInfo.put("broswser", getClientBrowser(request.getHeader("USER-AGENT")));
accessInfo.put("beforeUrl", Globals.getReferer());
accessInfo.put("header", Globals.getUserAgent());
accessInfo.put("userAgent", request.getHeader("USER-AGENT"));
//accessInfo.put("isBot", isBot(request.getHeader("USER-AGENT")));
System.out.println(accessInfo);
commonService.insertSystemAccessLog(accessInfo);
}catch(SQLException e) {
/* LOGGER.error(e.getMessage()); */
e.printStackTrace();
}catch(NullPointerException e) {
/* LOGGER.error(e.getMessage()); */
e.printStackTrace();
}catch(Exception e) {
e.printStackTrace();
/* LOGGER.error(e.getMessage()); */
}
}
private static String getClientOS(String userAgent) {
String os = "";
userAgent = userAgent.toLowerCase();
if (userAgent.indexOf("windows nt 10.0") > -1) {
os = "Windows10";
}else if (userAgent.indexOf("windows nt 6.1") > -1) {
os = "Windows7";
}else if (userAgent.indexOf("windows nt 6.2") > -1 || userAgent.indexOf("windows nt 6.3") > -1 ) {
os = "Windows8";
}else if (userAgent.indexOf("windows nt 6.0") > -1) {
os = "WindowsVista";
}else if (userAgent.indexOf("windows nt 5.1") > -1) {
os = "WindowsXP";
}else if (userAgent.indexOf("windows nt 5.0") > -1) {
os = "Windows2000";
}else if (userAgent.indexOf("windows nt 4.0") > -1) {
os = "WindowsNT";
}else if (userAgent.indexOf("windows 98") > -1) {
os = "Windows98";
}else if (userAgent.indexOf("windows 95") > -1) {
os = "Windows95";
}else if (userAgent.indexOf("iphone") > -1) {
os = "iPhone";
}else if (userAgent.indexOf("ipad") > -1) {
os = "iPad";
}else if (userAgent.indexOf("android") > -1) {
os = "android";
}else if (userAgent.indexOf("mac") > -1) {
os = "mac";
}else if (userAgent.indexOf("linux") > -1) {
os = "Linux";
}else{
os = "Other";
}
return os;
}
private static String getClientBrowser(String userAgent) {
String browser = "";
if (userAgent.indexOf("Trident/7.0") > -1) {
browser = "ie11";
} else if (userAgent.indexOf("MSIE 10") > -1) {
browser = "ie10";
} else if (userAgent.indexOf("MSIE 9") > -1) {
browser = "ie9";
} else if (userAgent.indexOf("MSIE 8") > -1) {
browser = "ie8";
} else if (userAgent.indexOf("Chrome") > -1) {
browser = "Chrome";
} else if (userAgent.indexOf("Chrome") == -1 && userAgent.indexOf("Safari") >= -1) {
browser = "Safari";
} else if (userAgent.indexOf("Firefox") >= -1) {
browser = "Firefox";
} else {
browser ="Other";
}
return browser;
}
private String isBot(String userAgent) {
String isBot = "";
String[] botList = {
"bot" , "Bot", "spider" ,"crawl", // GENERAL TERMS
"APIs-Google","AdsBot","Googlebot", //GOOGLE ROBOTS
"mediapartners","Google Favicon",
"FeedFetcher","Google-Read-Aloud",
"DuplexWeb-Google","googleweblight",
"bing","yandex","baidu","duckduck","yahoo", // OTHER ENGINES
"ecosia","ia_archiver",
"facebook","instagram","pinterest","reddit", // SOCIAL MEDIA
"slack","twitter","whatsapp","youtube",
"semrush", "censys"
};
if(StringUtils.indexOfAny(userAgent , botList) > -1) {
isBot = "Y";
}else {
isBot = "N";
}
return isBot;
}
}
4. 발생한 문제
사용자가 /main 경로로 접근하면 사용자의 접속정보를 간략하게 파악하여 insertAccessLog() 매서드로 DB의 USER_ACCESS테이블에 저장하는 구조이다. 해당 테이블 확인 결과 정상적인 유저의 접속 정보와, Google, Amazon등에서 데이터를 수집할 때 사용하는 Bot의 접속정보가 함께 수집되어 실질적인 사용자의 접속정보를 구별할 수 없었다.
이 때문에 접속정보의 userAgent를 통해 Bot을 판별하는 로직을 interceptor의 isBot()이라는 매서드로 구현하였다. 하지만 이 로직을 interceptor에 구현하여서 추후 다른 봇들을 판별할 수 있도록 봇 userAgent의 종류를 추가할 때마다 Java파일을 수정해야 했기 때문에 계속 서버에 반영해줘야한다는 문제가 발생했다.
그래서 이를 해결하기 위해 isBot()매서드를 사용하지 않고 insertAccessLog() 매서드를 실행할 때 사용되는 쿼리에서 DB Function을 사용하여 Bot을 구분하도록 변경하였다.
CREATE DEFINER=`접속정보` FUNCTION `sdh4716`.`FN_CHK_BOT`(
P_USER_AGENT VARCHAR(400)
) RETURNS varchar(1) CHARSET utf8
BEGIN
DECLARE RTN_VAL VARCHAR(1);
DECLARE BOT_LIST VARCHAR(4000);
SET BOT_LIST = CONCAT(
'bot|spider|crawl|APIs-Google|AdsBot|Googlebot|mediapartners'
'|Google Favicon|FeedFetcher|Google-ReadS-Aloud|DuplexWeb-Google'
'|googleweblight|bing|yandex|baidu|duckduck|yahoo|ecosia|ia_archiver'
'|facebook|instagram|pinterest|reddit|slack|twitter|whatsapp|youtube'
'|semrush|censys|InternetMeasurement'
);
SELECT
CASE
WHEN P_USER_AGENT REGEXP BOT_LIST THEN 'Y'
ELSE 'N'
END INTO RTN_VAL;
RETURN RTN_VAL;
END
5. 구현결과
로그는 현재까지도 정상적으로 수집되고 있으며 이 자료는 방문자 통계 그래프에서도 사용되고 있다. 하지만 userAgent만으로 Bot을 구분하는 것에는 한계가 있기 때문에 더 나은 방법을 찾아봐야 할 것 같다.
'웹 개발 > Side Project' 카테고리의 다른 글
[천상화원] 부모님 펜션 홈페이지를 제작해보자 4. 호스팅 서버 구매 (0) | 2024.12.17 |
---|---|
[천상화원] 부모님 펜션 홈페이지를 제작해보자 3. 도메인, 디자인 구매 (0) | 2024.11.26 |
[천상화원] 부모님 펜션 홈페이지를 제작해보자 2. 개발 계획 수립 (0) | 2024.03.28 |
[천상화원] 부모님 펜션 홈페이지를 제작해보자 1. Prologue (0) | 2024.03.11 |