Skip to content

Commit c6d1e4c

Browse files
authored
Merge pull request #54 from frontend-opensource-project/URH-66/use-device-detect
[URH-66] useDeviceDetect 신규
2 parents 1370e4b + e7380f5 commit c6d1e4c

File tree

2 files changed

+260
-0
lines changed

2 files changed

+260
-0
lines changed

src/hooks/useDetectDevice.test.ts

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
import { renderHook } from '@testing-library/react';
2+
import useDetectDevice from './useDetectDevice';
3+
import * as utils from '../utils';
4+
5+
const mockUserAgent = (userAgent: string) => {
6+
Object.defineProperty(navigator, 'userAgent', {
7+
value: userAgent,
8+
configurable: true,
9+
});
10+
};
11+
12+
const uaAndroid14 =
13+
'Mozilla/5.0 (Linux; Android 14; Pixel 6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.88 Mobile Safari/537.36';
14+
const uaiOS17 =
15+
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_6_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Mobile/15E148 Safari/604.1';
16+
const uaWindows10 = {
17+
edge: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/127.0.2651.105',
18+
chrome:
19+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36',
20+
};
21+
const uaMac14 = {
22+
safari:
23+
'Mozilla/5.0 (Macintosh; Intel Mac OS X 14_6_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Safari/605.1.15',
24+
firefox:
25+
'Mozilla/5.0 (Macintosh; Intel Mac OS X 14.6; rv:129.0) Gecko/20100101 Firefox/129.0',
26+
};
27+
const uaLinux =
28+
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.111 Safari/537.36';
29+
const uaWhale =
30+
'mozilla/5.0 (linux; android 11; sm-a908n build/rp1a.200720.012; wv) applewebkit/537.36 (khtml, like gecko) version/4.0 chrome/80.0.3987.163 whale/1.0.0.0 crosswalk/25.80.14.29 mobile safari/537.36 naver(inapp; search; 1000; 11.6.7)';
31+
32+
describe('useDetectDevice', () => {
33+
let originalUserAgent: string;
34+
35+
beforeEach(() => {
36+
originalUserAgent = navigator.userAgent;
37+
});
38+
39+
afterEach(() => {
40+
mockUserAgent(originalUserAgent);
41+
});
42+
43+
test('모바일 디바이스(Android)를 감지한다.', () => {
44+
mockUserAgent(uaAndroid14);
45+
46+
const { result } = renderHook(() => useDetectDevice());
47+
48+
expect(result.current.isMobile).toBe(true);
49+
expect(result.current.isDesktop).toBe(false);
50+
});
51+
52+
test('모바일 디바이스(iOS)를 감지한다.', () => {
53+
mockUserAgent(uaiOS17);
54+
55+
const { result } = renderHook(() => useDetectDevice());
56+
57+
expect(result.current.isMobile).toBe(true);
58+
expect(result.current.isDesktop).toBe(false);
59+
});
60+
61+
test('데스크탑 디바이스를 올바르게 감지한다.', () => {
62+
mockUserAgent(uaWindows10.chrome);
63+
64+
const { result } = renderHook(() => useDetectDevice());
65+
66+
expect(result.current.isMobile).toBe(false);
67+
expect(result.current.isDesktop).toBe(true);
68+
});
69+
70+
test('iOS 환경을 감지한다.', () => {
71+
mockUserAgent(uaiOS17);
72+
73+
const { result } = renderHook(() => useDetectDevice());
74+
75+
expect(result.current.os).toBe('iOS');
76+
});
77+
78+
test('Android 환경을 감지한다.', () => {
79+
mockUserAgent(uaAndroid14);
80+
81+
const { result } = renderHook(() => useDetectDevice());
82+
83+
expect(result.current.os).toBe('Android');
84+
});
85+
86+
test('Windows 환경을 감지한다.', () => {
87+
mockUserAgent(uaWindows10.chrome);
88+
89+
const { result } = renderHook(() => useDetectDevice());
90+
91+
expect(result.current.os).toBe('Windows');
92+
});
93+
94+
test('MacOS 환경을 감지한다.', () => {
95+
mockUserAgent(uaMac14.safari);
96+
97+
const { result } = renderHook(() => useDetectDevice());
98+
99+
expect(result.current.os).toBe('MacOS');
100+
});
101+
102+
test('Linux 환경을 감지한다.', () => {
103+
mockUserAgent(uaLinux);
104+
105+
const { result } = renderHook(() => useDetectDevice());
106+
107+
expect(result.current.os).toBe('Linux');
108+
});
109+
110+
test('Chrome 브라우저를 감지한다.', () => {
111+
mockUserAgent(uaWindows10.chrome);
112+
113+
const { result } = renderHook(() => useDetectDevice());
114+
115+
expect(result.current.browser).toBe('Chrome');
116+
});
117+
118+
test('Firefox 브라우저를 감지한다.', () => {
119+
mockUserAgent(uaMac14.firefox);
120+
121+
const { result } = renderHook(() => useDetectDevice());
122+
123+
expect(result.current.browser).toBe('Firefox');
124+
});
125+
126+
test('Safari 브라우저를 감지한다.', () => {
127+
mockUserAgent(uaMac14.safari);
128+
129+
const { result } = renderHook(() => useDetectDevice());
130+
131+
expect(result.current.browser).toBe('Safari');
132+
});
133+
134+
test('Whale 브라우저를 감지한다.', () => {
135+
mockUserAgent(uaWhale);
136+
137+
const { result } = renderHook(() => useDetectDevice());
138+
139+
expect(result.current.browser).toBe('Whale');
140+
});
141+
142+
test('Edge 브라우저를 감지한다.', () => {
143+
mockUserAgent(uaWindows10.edge);
144+
145+
const { result } = renderHook(() => useDetectDevice());
146+
147+
expect(result.current.browser).toBe('Edge');
148+
});
149+
150+
test('isClient가 false일 때, 디바이스 정보가 초기값으로 유지된다', () => {
151+
jest.spyOn(utils, 'isClient', 'get').mockReturnValue(false);
152+
expect(utils.isClient).toBe(false);
153+
154+
const { result } = renderHook(() => useDetectDevice());
155+
expect(result.current.isMobile).toBe(false);
156+
expect(result.current.isDesktop).toBe(false);
157+
expect(result.current.os).toBe('');
158+
expect(result.current.browser).toBe('');
159+
});
160+
});

src/hooks/useDetectDevice.ts

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { useEffect, useState } from 'react';
2+
import { isClient } from '../utils';
3+
4+
interface UseDeviceDetectReturns {
5+
isMobile: boolean;
6+
isDesktop: boolean;
7+
os: string;
8+
browser: string;
9+
}
10+
11+
const DEVICE_PATTERNS = {
12+
mobile: /Mobi/i,
13+
};
14+
15+
const OS_PATTERNS = {
16+
windows: /Windows/i,
17+
macOS: /Macintosh|Mac/i,
18+
linux: /Linux/i,
19+
android: /Android/i,
20+
iOS: /iPhone|iPad|iPod/i,
21+
};
22+
23+
const BROWSER_PATTERNS = {
24+
whale: /Whale/i,
25+
edge: /Edg/i,
26+
chrome: /Chrome/i,
27+
safari: /Safari/i,
28+
firefox: /Firefox/i,
29+
};
30+
31+
/**
32+
* 사용자 디바이스의 유형, os, 브라우저를 감지하는 훅.
33+
*
34+
* @returns {UseDeviceDetectReturns} - 디바이스 정보 객체(isMobile, isTablet, isDesktop, os, browser)를 반환합니다.
35+
*
36+
* @property {boolean} isMobile - 사용 중인 디바이스의 Mobile 여부를 나타냅니다.
37+
* @property {boolean} isTablet - 사용 중인 디바이스의 Tablet 여부를 나타냅니다.
38+
* @property {boolean} isDesktop - 사용 중인 디바이스의 Desktop 여부를 나타냅니다.
39+
* @property {string} os - 사용 중인 디바이스의 OS 이름을 나타냅니다.
40+
* @property {string} browser - 사용 중인 브라우저의 이름을 나타냅니다.
41+
*
42+
* @description
43+
* 사용자의 userAgent 문자열을 기반으로 디바이스 유형(Mobile, Tablet, Desktop), 운영체제(OS),
44+
* 그리고 브라우저 종류를 감지하여 반환합니다.
45+
*/
46+
47+
const useDetectDevice = (): UseDeviceDetectReturns => {
48+
const [deviceInfo, setDeviceInfo] = useState({
49+
isMobile: false,
50+
isDesktop: false,
51+
os: '',
52+
browser: '',
53+
});
54+
55+
const detectDeviceType = (userAgent: string) => {
56+
const isMobile = DEVICE_PATTERNS.mobile.test(userAgent);
57+
const isDesktop = !isMobile;
58+
return { isMobile, isDesktop };
59+
};
60+
61+
const detectOS = (userAgent: string) => {
62+
if (OS_PATTERNS.iOS.test(userAgent)) return 'iOS';
63+
if (OS_PATTERNS.android.test(userAgent)) return 'Android';
64+
if (OS_PATTERNS.windows.test(userAgent)) return 'Windows';
65+
if (OS_PATTERNS.macOS.test(userAgent)) return 'MacOS';
66+
if (OS_PATTERNS.linux.test(userAgent)) return 'Linux';
67+
return '';
68+
};
69+
70+
const detectBrowser = (userAgent: string) => {
71+
// Order is fixed(due to the structure of the userAgent): Whale -> Edge -> Chrome -> Safari -> Firefox
72+
if (BROWSER_PATTERNS.whale.test(userAgent)) return 'Whale';
73+
if (BROWSER_PATTERNS.edge.test(userAgent)) return 'Edge';
74+
if (BROWSER_PATTERNS.chrome.test(userAgent)) return 'Chrome';
75+
if (BROWSER_PATTERNS.safari.test(userAgent)) return 'Safari';
76+
if (BROWSER_PATTERNS.firefox.test(userAgent)) return 'Firefox';
77+
return '';
78+
};
79+
80+
useEffect(() => {
81+
if (!isClient) return;
82+
83+
const UA = navigator.userAgent;
84+
85+
const { isMobile, isDesktop } = detectDeviceType(UA);
86+
const os = detectOS(UA);
87+
const browser = detectBrowser(UA);
88+
89+
setDeviceInfo({
90+
isMobile,
91+
isDesktop,
92+
os,
93+
browser,
94+
});
95+
}, []);
96+
97+
return deviceInfo;
98+
};
99+
100+
export default useDetectDevice;

0 commit comments

Comments
 (0)