March 27, 2020
프런트엔드 개발에서 navigator
객체에 대한 사용을 빼놓을 수 없습니다. 이 포스팅에서는 navigator객체의 여러 가지 속성들에 대해 살펴봅니다.
navigator
객체는 대표적인 User Agent뿐만 아니라 사용자의 상태에 관한 여러가지 정보를 담고 있습니다. navigator의 속성들은 읽기 전용으로 접근할 수 있습니다.
ChromeLabs에서 제작한 사용자 디바이스 및 네트워크 환경에 대한 정보를 담은 hooks입니다. 이 hooks 코드는 navigator객체의 여러 속성을 이용해서 제작되었습니다.
이 글에서는 react-adaptive-hooks에서 사용하는 속성들을 포함하여 ‘알아두면 쓸모 있는 navigator 속성’에는 어떤 것들이 있는지 살펴봅니다.
일부 속성들은 브라우저 지원 범위가 매우 제한적입니다. 지원 범위에 따라 세 단계로 나누어 표현했습니다.
사용자가 사용하고 있는 네트워크 환경에 대한 정보를 제공하며, 아래와 같은 정보들을 확인할 수 있습니다.
navigator.connection
---
[결과]
NetworkInformation: {
onchange: null
effectiveType: "4g"
rtt: 100
downlink: 10
saveData: false
}
// Browser Support
const connection = navigator.connection
|| navigator.mozConnection
|| navigator.webkitConnection;
function updateConnectionStatus() {
alert("Connection bandwidth: " + connection.effectiveType + " MB/s");
}
connection.addEventListener("change", updateConnectionStatus);
updateConnectionStatus();
아래 사진은 4g network에 연결되어 있다가 Chrome Network탭에서 Fast 3G로 적용한 뒤의 사진입니다. 실제로 effectiveType과 downlink값이 변한 것을 확인 할 수 있습니다.
이 기능은 현재 실험 단계에 있어, 브라우저 지원 범위가 제한적 입니다.
사용자의 위치 정보에 대한 속성입니다. 사용자가 디바이스 설정에서 위치 정보에 대한 접근 권한을 허용한 경우에만 사용 가능합니다.
navigator.geolocation.getCurrentPosition(function(position) {
console.log(position);
}, err => console.log(err));
---
[결과]
coords: {
latitude: 경도
longitude: 위도
altitude: 고도
accuracy: 위, 경도 정확도
altitudeAccuracy: 고도의 정확도
heading: 움직이는 방향을 나타내는 숫자. 정북에서 시계방향으로 벗어난 각의 크기(정북:0, 동쪽: 90)
speed: 속도
}
timestamp: 1500000000
function success(pos) {
console.log(pos.coords.latitude, pos.coords.longitude)
}
function error(err) {
console.warn('ERROR(' + err.code + '): ' + err.message);
}
navigator.geolocation.watchPosition(success, error);
디바이스 배터리에 대한 정보입니다. 이 함수는 Promise를 반환하며, 아래와 같이 사용할 수 있습니다.
navigator.getBattery().then(res => console.log(res))
---
[결과]
charging: true
chargingTime: 0
dischargingTime: Infinity
level: 1
onchargingchange: null
onchargingtimechange: null
ondischargingtimechange: null
onlevelchange: null
navigator.getBattery().then(battery => {
battery.addEventListener('chargingchagne', () => {
console.log('Battery Charging' + battery.charging ? 'yes' : 'no')
})
})
배터리 충전 상태가 변할 때마다 callback함수가 실행됩니다.
getBattery
API는 DEPRECATED되었으며, 최신 버전 브라우저에서 동작하지 않을 수 있습니다. 개인정보 보호 정책으로 사용하지 않는 방향으로 결정되었습니다.
쿠키 사용 가능 여부를 나타냅니다. 사용자가 브라우저 환경에서 ‘쿠키 차단’을 설정했을 경우 이 값은 false입니다.
디바이스에 설정된 언어 정보를 반환합니다.
크롬의 경우 ‘설정 > 언어’에서 제일 상단에 설정된 언어 기준이며, ‘한국어’로 설정된 경우 navigator.language값은 ko
이며, ‘영어(미국)‘로 설정된 경우에는 en-US
입니다.
주어진 포맷에 대해 디바이스의 인코딩, 디코딩 가능 여부에 관한 정보를 반환합니다.
사용자가 기능을 활성화한 경우에만 사용할 수 있으며, 크롬의 경우 설정에서 변경할 수 있습니다.
//Create media configuration to be tested
const mediaConfig = {
type : 'record', // or 'transmission'
video : {
contentType : "video/webm;codecs=vp8.0", // valid content type
width : 1920, // width of the video
height : 1080, // height of the video
bitrate : 120000, // number of bits used to encode 1s of video
framerate : 48 // number of frames making up that 1s.
}
};
// check support and performance
navigator.mediaCapabilities.encodingInfo(mediaConfig).then(result => {
console.log('This configuration is ' +
(result.supported ? '' : 'not ') + 'supported, ' +
(result.smooth ? '' : 'not ') + 'smooth, and ' +
(result.powerEfficient ? '' : 'not ') + 'power efficient.')
});
기능을 활성화한 후 위 코드를 실행하면 “This configuration is supported, not smooth, and not power efficient.” 결과를 확인할 수 있습니다.
활성화하지 않은 경우에는,
Uncaught TypeError
가 발생합니다.
navigator.mediaCapabilities.decodingInfo({
type : 'file',
audio : {
contentType : "audio/mp3",
channels : 2,
bitrate : 132700,
samplerate : 5200
}
}).then(function(result) {
console.log('This configuration is ' +
(result.supported ? '' : 'not ') + 'supported, ' +
(result.smooth ? '' : 'not ') + 'smooth, and ' +
(result.powerEfficient ? '' : 'not ') + 'power efficient.')
});
decodingInfo는 별도의 기능 활성화가 필요 없습니다. Chrome80 기준으로 위 코드의 실행 결과는 “This configuration is supported, smooth, and power efficient.” 입니다.
이 기능은 아직 실험 단계이므로, 일부 브라우저에서만 지원됩니다.
디바이스에서 동시에 터치할 수 있는 지점이 몇 개인지 반환합니다. TouchEvent를 기준으로 하므로, PC에서 크롬을 데스크탑 모드로 설정했을 경우에는 0, 모바일 모드로 설정한 경우는 1을 반환합니다.
CodePen에서 테스트해 보실 수 있습니다.
// PC - Desktop모드
navigator.maxTouchPoints // result: 0
// PC - Mobile모드
navigator.maxTouchPoints // result: 1
// Mobile Device - iPhoneX
navigator.maxTouchPoints // result: 5
Can I use에서는 Safari에서 제약이 있다고 하지만, 실제로는 잘 동작합니다.
디바이스가 현재 인터넷에 연결되어 있는지 여부를 반환합니다.
// 인터넷 연결
navigator.onLine //true
// 인터넷 연결 끊김
navigator.onLine // false
대부분의 브라우저에서 사용할 수 있지만, IE8에서는 제약이 있습니다.
사용자로부터 권한을 얻어야 하는 기능들(푸쉬 노티, 위치정보 등)에 대한 권한 설정 상태를 조회할 수 있습니다.
navigator.permissions.query({name: 'geolocation'})
.then(res => {
if (res.state === 'granted') {
console.log('권한을 얻었어요!')
} else if (res.state === 'prompt') {
console.log('권한 요청을 한번도 요청한적 없어요.');
}
})
권한의 상태는 세 가지입니다. (문서)
query
의 인자로 PermissionDescriptor를 받고, 이 속성은 세 가지 요소로 구성됩니다.
운영되고 있는 플랫폼에 대한 정보를 반환합니다. 맥북 프로에서는 MacIntel
이고, 대표적인 값들은 다음과 같습니다.
브라우저에서 지원하는 여러가지 플러그인 목록입니다. Chrome에서 조회하면 아래 결과를 확인할 수 있습니다.
navigator.mimeTypes
---
[결과]
0: {
0: MimeType,
application/x-google-chrome-pdf: MimeType,
name: "Chrome PDF Plugin",
filename: "internal-pdf-viewer",
description: "Portable Document Format",
length: 1
}
1: {
0: MimeType,
application/pdf: MimeType,
name: "Chrome PDF Viewer",
filename: "mhjfbmdgcfjbbpaeojofohoefgiehjai",
description: "",
length: 1
}
2: {
0: MimeType,
1: MimeType,
...
namedItem
메서드를 사용해서 특정 플러그인이 설치되어 있는지 확인할 수 있습니다.
function getFlashVersion() {
var flash = navigator.plugins.namedItem('Shockwave Flash');
if (typeof flash != 'object') {
// flash is not present
return undefined;
}
if(flash.version){
return flash.version;
} else {
//No version property (e.g. in Chrome)
return flash.description.replace(/Shockwave Flash /,"");
}
}
StorageManager객체를 반환합니다. Storage Manager는 세 가지 메서드를 지원합니다.
// https://twitter.com
navigator.storage.estimate().then(
res => console.log(res)
)
---
[결과]
quota: 150411345100
usage: 18873830
usageDetails: {
caches: 18312960,
indexedDB: 413132,
serviceWorkerRegistrations: 147738
}
persist: 아래 조건을 만족하는 페이지에 대해 storage를 영속적으로 저장할 수 있습니다.
브라우저의 이름, 버전 및 플랫폼 정보를 담고 있습니다. 서버에 보내는 모든 요청에 userAgent 문자열 이라고 부르는 User-Agent
(이하 UA) HTTP 헤더를 보냅니다. 이 문자열은 브라우저 종류, 버전 번호, 호스트 운영체제 등의 정보를 포함합니다.
여러가지 환경에서 navigator.userAgent
정보를 확인하면 다음과 같습니다.
Nescape Navigator와 IE만 존재하던 시절, Netscape 브라우저는 'Mozilla/version'과 같은 방식으로 버전을 표현하였고, 이후 다른 브라우저들이 Netscape 브라우저의 특정 버전과 호환된다는 의미로 userAgent정보에 Mozilla/version을 추가했습니다. (실제로는 해당 버전 기반이 아닙니다.) 오늘날 많은 브라우저에서 userAgent가 'Mozilla/version'으로 시작하는 이유입니다.
UA
는 Chrome81버전부터 단계적으로 지원을 중단할 계획입니다. 광고주가 웹 사이트 방문자를 추적하거나 문자열 파싱에 기반한 브라우저 지원은 여러 가지 문제를 낳았기 때문입니다.
앞으로의 계획은 Chrome을 사용하는 유저가 Windows 7에서 사용하는지, 특정 디바이스에서 사용 중인지에 대한 정보를 제공하지 않는 것입니다. UA에 대한 단계적 지원 중단 계획은 다음과 같습니다.
“On top of those privacy issues, User-Agent sniffing is an abundant source of compatibility issues, in particular for minority browsers, resulting in browsers lying about themselves (generally or to specific sites), and sites (including Google properties) being broken in some browsers for no good reason,” - Yoav Weiss, Google Engineer-
UA는 Client Hints라는 새로운 스펙으로 대체됩니다. Accept-CH
라는 request header를 명시해주거나 meta tag
를 사용해야 합니다.
<meta http-equiv="Accept-CH" content="DPR, Width, Viewport-Width, Downlink">
정보를 요청하는 방식은 request header혹은 meta tag를 통해 표현하며 comma로 구분합니다. 브라우저가 제공할 수 있는 정보의 목록은 다음과 같습니다.
Example
기본적으로 브라우저는 다음 정보들을 반환합니다.
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/71.1.2222.33 Safari/537.36
Sec-CH-UA: "Chrome"; v="74"
Sec-CH-Mobile: ?0
추가적인 정보는 다음과 같이 요청할 수 있으며
Accept-CH: UA-Full-Version, UA-Platform, UA-Arch
위 헤더를 보고 반환한 값은 다음과 같습니다.
Sec-CH-UA: "Chrome"; v="74"
Sec-CH-UA-Full-Version: "74.0.3424.124"
Sec-CH-UA-Platform: "macOS"
Sec-CH-UA-Arch: "ARM64"
navigator의 여러 가지 속성들을 살펴보면서, 웹에서 할 수 있는 일이 생각보다 많았다는 것을 알 수 있었습니다. 이렇게 브라우저에서 제공되는 기본 기능들을 대략적으로나마 알아두면, 여러 가지 상황에서 활용해볼 수 있을것 같습니다.