Skip to content

Commit 1e2ee87

Browse files
authored
Merge pull request #43 from klmhyeonwoo/feature/utils-workspace-yeom
feat: add device type detect utilities
2 parents 781acd0 + a427cfb commit 1e2ee87

File tree

5 files changed

+383
-8
lines changed

5 files changed

+383
-8
lines changed

README.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ A comprehensive collection of TypeScript utility functions for modern web develo
44

55
## Features
66

7-
- 🛠️ **Comprehensive**: String, object, cookie, number, validation, format, search query, and common utilities
7+
- 🛠️ **Comprehensive**: String, object, cookie, number, validation, format, search query, device, and common utilities
88
- 📦 **Tree-shakable**: Import only what you need
99
- 🔒 **Type-safe**: Full TypeScript support with type definitions
1010
-**Lightweight**: Minimal dependencies and optimized for performance
@@ -32,6 +32,7 @@ import {
3232
commonUtil,
3333
formatUtil,
3434
searchQueryUtil,
35+
deviceUtil,
3536
} from "kr-corekit";
3637

3738
// String utilities
@@ -72,6 +73,9 @@ const copied = await commonUtil.copyToClipboard("Hello, World!"); // true if suc
7273
// Search Query utilities
7374
const queryParams = searchQueryUtil.getAllQuery(); // { key: ["value1", "value2"], id: "123" }
7475

76+
// Device utilities
77+
const device = deviceUtil.getDevice(); // { isMobile: false, isTablet: false, isDesktop: true, isIOS: false, isAndroid: false }
78+
7579
// Cookie utilities
7680
cookieUtil.setCookie("theme", "dark");
7781
const theme = cookieUtil.getCookie("theme");
@@ -121,6 +125,10 @@ const formattedPhone = formatUtil.formatPhoneNumber("01012345678"); // "010-1234
121125

122126
- `getAllQuery(): Record<string, string | string[]>` - Parses the current URL's query string and returns an object with key-value pairs. Values appear as arrays when the same key is used multiple times.
123127

128+
### DeviceUtil
129+
130+
- `getDevice(): DeviceInfo` - Detects the user's device environment. Returns information about device type (mobile/tablet/desktop) and operating system (iOS/Android). Uses navigator.userAgent for detection and provides safe fallback for SSR environments.
131+
124132
### CookieUtil
125133

126134
- `setCookie(name: string, value: string, options?: object): void` - Sets a cookie
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/**
2+
* vitest가 JSDOM을 사용하여 브라우저 환경을 시뮬레이션하도록 설정합니다.
3+
* 이를 통해 테스트 환경에서 window, navigator 등의 객체를 사용할 수 있습니다.
4+
*
5+
* @vitest-environment jsdom
6+
*/
7+
import { describe, test, expect, afterEach } from "vitest";
8+
import getDevice from ".";
9+
10+
describe("getDevice", () => {
11+
// userAgent를 테스트 케이스마다 다르게 설정하기 위한 헬퍼 함수
12+
const mockUserAgent = (userAgent: string) => {
13+
// JSDOM이 생성한 window.navigator 객체의 userAgent 속성을 덮어씁니다.
14+
Object.defineProperty(window.navigator, "userAgent", {
15+
value: userAgent,
16+
writable: true,
17+
configurable: true,
18+
});
19+
};
20+
21+
// 각 테스트가 끝난 후, 다음 테스트에 영향을 주지 않도록 userAgent를 초기화합니다.
22+
afterEach(() => {
23+
mockUserAgent("");
24+
});
25+
26+
describe("클라이언트 (브라우저) 환경", () => {
27+
describe("데스크톱", () => {
28+
test("Windows Chrome User Agent를 데스크톱으로 인식해야 합니다", () => {
29+
mockUserAgent(
30+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36"
31+
);
32+
const { isDesktop, isMobile, isTablet } = getDevice();
33+
expect(isDesktop).toBe(true);
34+
expect(isMobile).toBe(false);
35+
expect(isTablet).toBe(false);
36+
});
37+
});
38+
39+
describe("모바일", () => {
40+
test("iPhone User Agent를 모바일 및 iOS로 인식해야 합니다", () => {
41+
mockUserAgent(
42+
"Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148"
43+
);
44+
const { isMobile, isIOS, isDesktop } = getDevice();
45+
expect(isMobile).toBe(true);
46+
expect(isIOS).toBe(true);
47+
expect(isDesktop).toBe(false);
48+
});
49+
50+
test("Android Phone User Agent를 모바일 및 Android로 인식해야 합니다", () => {
51+
mockUserAgent(
52+
"Mozilla/5.0 (Linux; Android 13; SM-S908B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Mobile Safari/537.36"
53+
);
54+
const { isMobile, isAndroid, isTablet } = getDevice();
55+
expect(isMobile).toBe(true);
56+
expect(isAndroid).toBe(true);
57+
expect(isTablet).toBe(false);
58+
});
59+
});
60+
61+
describe("태블릿", () => {
62+
test("iPad User Agent를 태블릿 및 iOS로 인식해야 합니다", () => {
63+
mockUserAgent(
64+
"Mozilla/5.0 (iPad; CPU OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.5 Mobile/15E148 Safari/604.1"
65+
);
66+
const { isTablet, isIOS, isMobile } = getDevice();
67+
expect(isTablet).toBe(true);
68+
expect(isIOS).toBe(true);
69+
expect(isMobile).toBe(false);
70+
});
71+
72+
test("Android Tablet User Agent를 태블릿 및 Android로 인식해야 합니다", () => {
73+
mockUserAgent(
74+
"Mozilla/5.0 (Linux; Android 12; SM-X906C) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.99 Safari/537.36"
75+
);
76+
const { isTablet, isAndroid, isMobile } = getDevice();
77+
expect(isTablet).toBe(true);
78+
expect(isAndroid).toBe(true);
79+
expect(isMobile).toBe(false);
80+
});
81+
});
82+
});
83+
});
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* @typedef {object} DeviceInfo
3+
* @property {boolean} isMobile - 모바일 디바이스 여부
4+
* @property {boolean} isTablet - 태블릿 디바이스 여부
5+
* @property {boolean} isDesktop - 데스크톱 디바이스 여부
6+
* @property {boolean} isIOS - iOS 운영체제 여부
7+
* @property {boolean} isAndroid - 안드로이드 운영체제 여부
8+
*/
9+
10+
/**
11+
* 사용자의 디바이스 환경 정보를 반환합니다.
12+
* window.navigator.userAgent를 기반으로 분석하며, 클라이언트 사이드에서만 정확한 값을 반환합니다.
13+
*
14+
* @returns {DeviceInfo} 디바이스 환경 정보 객체
15+
*/
16+
export default function getDevice() {
17+
// * ----- 클라이언트 환경일 경우, 실행 ----- * //
18+
const userAgent = window.navigator.userAgent;
19+
20+
const isIOS = /iPhone|iPad|iPod/i.test(userAgent);
21+
22+
const isAndroid = /Android/i.test(userAgent);
23+
24+
const isTablet = /(iPad)|(tablet)|(android(?!.*mobi))/i.test(userAgent);
25+
26+
const isMobile =
27+
!isTablet &&
28+
/Mobi|iP(hone|od)|Android|BlackBerry|IEMobile|Opera Mini/i.test(userAgent);
29+
30+
const isDesktop = !isMobile && !isTablet;
31+
32+
return { isMobile, isTablet, isDesktop, isIOS, isAndroid };
33+
}

package/deviceUtil/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default as getDevice } from "./getDevice";

0 commit comments

Comments
 (0)