See the Pen Browser Identifier by Richard JEON (@hj-rich) on CodePen.
버튼을 클릭하면 현재 Client의 browser, os 등의 정보가 alert된다.
이하는 방문카운트 기능을 추가한 과정을 기록한다.
1. 쿠키를 이용해 24시간 간격으로 fetch하는 함수를 footer에 추가.
document.cookie 를 이용해 쿠키 유무 확인 및 쿠키 생성을 진행한다.
쿠키가 없으면 쿠키를 생성하되, 이름을 지정하고, 유효기간은 현재로부터 24시간으로 한다.
footer template에 담아서 모든 페이지에서 쿠키 유무를 판별하도록 처리한다.
쿠키가 있으면 아무 일도 하지 않는다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
//24시간 간격으로 방문횟수를 카운트 하는 함수입니다
function visitCount() {
// 쿠키가 있으면 return;
if(document.cookie.match(/withit/)) return;
// 쿠키가 없으면 24시간 유효한 쿠키를 생성 후 컨트롤러 전송
let a = new Date();
a.setDate(new Date().getDate()+1);
document.cookie = 'withit=1;Expires='+a;
//컨트롤러 전송
let result = identifyBrowser();
let url = "/visitCount";
let options = myOptions(result);
fetch(url, options);
}
|
cs |
2. 컨트롤러로 보낼 정보를 navigator.userAgent 에서 추출 및 가공
가장 많은 삽질을 했던 부분이다.
우선 navigator.userAgent 안에 정보가 담겨있는 건 알겠는데
그게 참 쓰기가 불편하게 되어 있었다. 라이브러리도 있고 스택오버플로우의 예시 답변도 있었지만
성에 차지 않았다. 최신 OS를 반영하지 않았거나, 예외가 발생할 여지가 있는 코드였다.
결국 직접 추출하는 함수를 짜기로 했고 샘플이 필요해서 찾다가 발견한 게 두 사이트인데,
첫째는 MDN의 userAgent 포맷 안내이고 둘째는 userAgent database를 제공하는 사이트였다.
userAgent 포맷이다.
Mozilla/5.0 (<system-information>) <platform> (<platform-details>) <extensions>
그리고 여기에서 userAgent의 수많은 샘플 데이터를 참고할 수 있다.
데이터도 생겼겠다, 포맷을 확인하며 추출하는 함수를 작성하기 시작했다.
진행하며 덕분에 강제로 정규식 학습도 같이 해야만 했다.
예를 들면 Android 버전을 추출할 때 Android 9 일수도 있고 Android 10 일수도 있고 (글자수가 다르다..)
Android 4.0 Android 6.0.1 까지. 형태가 다양했다. (물론 if 로 노가다를 할 수도 있지만 ;;)
예전에 알아두고 스크랩해뒀던 RegExper라는 사이트가 너무나 큰 도움이 되었다.
/reg/.test() 함수의 활용, value.match(/reg/) 함수의 활용 등 쓰지 않던 기능들을 조금 배웠다.
그리고 아래는 완성한 코드이다.
여러모로 테스트 했지만 물론 부족한 부분이나 예외가 있을 수 있으며
그래도 같은 기능을 구현할 누군가에게 조금의 도움은 되지 않을까 기대해본다.
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
|
//userAgent를 받아서 클라이언트 정보를 출력하는 함수입니다.
function identifyBrowser(userAgent) {
//파라메타가 주어지지 않을 경우 현재 위치의 userAgent를 이용
if(userAgent === undefined) {
userAgent = navigator.userAgent;
}
//결과를 출력할 객체를 선언합니다.
let result = new Object();
result.userAgent = userAgent;
result.width = screen.width;
result.height = screen.height;
result.language = navigator.language;
result.referer = document.referrer;
result.pathname = location.pathname;
result.time = new Date().getTime();
//-----------------------브라우저 검증---------------------------
//1. IE 10 이하 검증
if(/MSIE/.test(userAgent)){
result.browser = userAgent.match(/IE \d+.\d+/);
}
//2. IE11 검증
else if(/Trident/.test(userAgent)) {
result.browser = "IE " + userAgent.match(/rv:\d+\.\d+/)[0].replace("rv:", "");
}
//3. Firefox 검증
else if(/Firefox/.test(userAgent)) {
result.browser = userAgent.match(/Firefox\/\d+\.\d+/)[0].replace("/", " ");
}
//4. Opera Touch 검증
else if(/OPT/.test(userAgent)) {
result.browser = userAgent.match(/OPT\/\d+\.?[0-9]?/)[0].replace("/", " ").replace("OPT", "Opera Touch");
}
//5. Opera Mini 검증
else if(/Opera Mini/.test(userAgent)) {
result.browser = userAgent.match(/Opera Mini\/\d+\.\d+/)[0].replace("/", " ");
}
//6. Opera 검증
else if(/OPR/.test(userAgent) || /Opera/.test(userAgent)) {
if(/OPR/.test(userAgent)) {
result.browser = "Opera" + userAgent.match(/OPR\/\d+\.\d+/)[0].replace("OPR/", " ");
}else if(/Opera\//) {
result.browser = "Opera" + userAgent.match(/Version\/\d+\.\d+/)[0].replace("Version/", " ");
}else {
result.browser = userAgent.match(/Opera \d+\.\d+/)[0];
}
}
//7.Edge 검증
else if(/Edg/.test(userAgent)) {
result.browser = userAgent.match(/Edg[e]*\/\d+\.\d+/)[0].replace("/", " ").replace("Edge", "Edg").replace("Edg", "Edge");
}
//8. SamsungBrowser 검증
else if(/SamsungBrowser/.test(userAgent)) {
result.browser = userAgent.match(/SamsungBrowser\/\d+\.\d+/)[0].replace("/", " ");
}
//9. Whale 검증
else if(/Whale/.test(userAgent)) {
result.browser = userAgent.match(/Whale\/\d+\.\d+/)[0].replace("/", " ");
}
//10. Safari 검증
else if(/Safari/.test(userAgent) && !/Chrome/.test(userAgent)) {
result.browser = "Safari " + userAgent.match(/Version\/\d+\.[0-9]/)[0].replace("Version/", "");
}
//11. Chrome 검증
else if(/Chrome/.test(userAgent)) {
result.browser = userAgent.match(/Chrome\/\d+\.\d+/)[0].replace("/", " ");
}
//12. 기타
else {
result.browser = "unknown";
}
//----------------------------OS 검증-----------------------------
//1. Windows 검증
if(/Windows/.test(userAgent)) {
result.os = "Windows ";
let osv = userAgent.substring(userAgent.indexOf("Windows NT ")+11, userAgent.indexOf("Windows NT ")+15);
if(/5.0/.test(osv)) result.os += "2000";
else if(/5.1/.test(osv)) result.os += "XP";
else if(/5.2/.test(osv)) result.os += "XP";
else if(/6.0/.test(osv)) result.os += "Vista";
else if(/6.1/.test(osv)) result.os += "7";
else if(/6.2/.test(osv)) result.os += "8";
else if(/6.3/.test(osv)) result.os += "8.1";
else if(/10.0/.test(osv)) result.os += "10";
else result.os += "unknown";
}
//2. Android 검증
else if(/Android/.test(userAgent)) {
if(userAgent.match(/Android \d+\.?\d?\.?\d?/)) {
result.os = userAgent.match(/Android \d+\.?\d?\.?\d?/);
}else {
result.os = "Android unknown ver.";
}
}
//3. iPhone 검증
else if(/iPhone/.test(userAgent)) {
result.os = "iPhone";
if(/iPhone OS/.test(userAgent)) {
result.os = userAgent.match(/iPhone OS \d+_\d+_?[0-9]?/)[0].replace(" OS", "").replaceAll("_", ".");
}else if(/iPhone/.test(userAgent)) {
result.os = "iPhone unknown ver.";
}
}
//4. iPad 검증
else if(/iPad/.test(userAgent)) {
result.os = "iPad " + userAgent.match(/CPU OS \d+_\d+_?[0-9]?/)[0].replace("CPU OS ", "").replaceAll("_", ".");
}
//5. Mac 검증
else if(/Mac OS X/.test(userAgent)) {
result.os = userAgent.match(/Mac OS X \d+_\d+_?[0-9]?/)[0].replace(" OS X", "").replaceAll("_", ".");
}
//6. 리눅스 검증
else if(/Linux/.test(userAgent)) {
result.os = "Linux";
if(/Ubuntu/.test(userAgent)) {
result.os += " Ubuntu";
}
}
//7. Bot 검증
else if(/bot/.test(userAgent)) {
result.os = "Bot";
}
//8. 기타
else {
result.os = "unknown";
}
return result;
}
|
cs |
3. 컨트롤러에서 수신
이건 뭐 별거 없다. fetch 할 때 body에 JSON.stringify 해서 보내고 컨트롤러에서 RequestBody로 받는 것.
코드펜으로 결과물을 바로 공유할 수 있으니 기록도 되고 편의성도 증가하고 뿌듯하다.
암튼 이렇게 방문자 기록 처리를 완료 했다.
'JavaScript' 카테고리의 다른 글
예약일 선택용 캘린더 구현하기 without 라이브러리 (0) | 2020.11.10 |
---|---|
RMA : 취업포털에서 공고를 필터링해주는 크롬 확장 프로그램 (10) | 2020.10.24 |
모바일 메뉴 열기 닫기 (2) | 2020.10.02 |
기초적인 XSS 대응 (0) | 2020.10.01 |
Sweet Alert2 와 KakaoMap API 활용 (0) | 2020.09.07 |