일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- firebase 라이브러리
- GIT
- react native picker
- js
- 문자열 대소문자 구별
- img upload
- Project
- 리액트 네이티브
- react native 세팅
- fire base
- babel.config.js
- Access Key 생성
- AWS Access Key
- react native CLI
- react native 개발
- PongWorld
- React
- 리액트 네이티브 에러
- react native font
- AWS
- 백준
- s3 upload
- aws bucket 정책
- 리엑트 네이티브 아이콘
- 에러
- error
- 문자열 대소문자
- Next.js
- react native
- 리액트
- Today
- Total
밝을희 클태
[ Next.JS ] userAgent를 효율적으로 관리해보자 본문
프로젝트의 대부분의 media query를 사용해서 반응형을 구현했다. 하지만 상품 상세 페이지의 경우 모바일일 때는 이미지 슬라이더를 터치 슬라이더로 구현을 하고 싶었고 PC의 경우 버튼을 클릭해서 이미지를 넘기게 하고 싶었다. 그래서 클라이언트의 userAgent를 확인해 모바일인지 아닌지 체크를 했고 결과는 내가 원하는 데로 잘 동작을 했다.
export function isMobile() {
if (typeof window !== 'undefined') {
if (navigator.maxTouchPoints >= 1) {
const isMobile = /Macintosh|iPad|Mobi|Android|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
navigator.userAgent,
);
if (isMobile) return true;
return false;
}
return false;
}
}
그런데 상품 페이지를 들어갈 때마다 userAgent를 확인하는 건 매우 비효율적이고 또 확인을 할 때마다 비용이 발생해 상품의 이미지를 보여주는데 지연이 발생했다. 물론 엄청 짧은 시간이라 실사용에는 전혀 문제가 없었지만 해당 페이지에 진입할 때마다 검사를 하는 게 마음에 걸렸다.
그래서 생각한 게 서버 컴포넌트에서 한 번만 클라이언트의 정보를 확인하고, 그 결과를 페이지 전역 상태로 관리하는 방식으로 개선하기로 했다.
Next.JS 14 App router 서버 컴포넌트 (RootLayout):
import { headers } from 'next/headers';
import { userAgent } from 'next/server';
export default function RootLayout({ children }) {
// 서버 컴포넌트에서 userAgent 받기
const user = userAgent({ headers: headers() });
return (
<html lang="en">
...
<UserProvider initialUser={user}>
<main>{children}</main>
</UserProvider>
...
</html>
);
}
RootLayout 컴포넌트에서 서버 측에서 받은 userAgent 정보를 UserProvider에 전달합니다. 레이아웃 컴포넌트에서 정보를 받는 이유는 UserProvider는 React Hooks인 useContext와 useState 등을 사용해야 하므로, 클라이언트 컴포넌트로 선언했다.
클라이언트 컴포넌트 (UserProvider):
'use client';
import { useContext, createContext, useState, useEffect } from 'react';
const UserContext = createContext();
export const UserProvider = ({ children, initialUser }) => {
const [user, setUser] = useState(null);
useEffect(() => {
const isIPad = initialUser.device.model === 'Macintosh' && navigator.maxTouchPoints >= 1;
setUser({
...initialUser,
device: {
...initialUser.device,
type: initialUser.device.type || (isIPad ? 'mobile' : 'desktop'), // 기본값으로 'desktop' 설정
},
});
}, []);
return <UserContext.Provider value={{ user, setUser }}>{children}</UserContext.Provider>;
};
export const useUser = () => useContext(UserContext);
UserProvider 컴포넌트는 부모로부터 받은 초기 사용자 정보(initialUser)를 바탕으로, 모바일인지 PC인지 판단해 전역 상태로 관리한다. 아이패드의 경우 Macintosh로 인식되기 때문에, 추가적인 조건문을 통해 판단을 한다.
성능 측정 (4G 기준)
성능 측정 결과 기존 코드는 평균적으로 174.5ms 만큼의 시간이 걸렸고
수정 후 평균 172.1ms 만큼의 시간이 걸렸다.
결론
비록 성능 수치상으로 큰 차이는 없었지만, 페이지에 진입할 때마다 클라이언트 정보를 확인하는 것은 확실히 비효율적이다. 클라이언트 정보를 서버에서 한 번만 확인하고, 이를 전역 상태로 관리하는 방법을 적용한 덕분에 성능이 개선됐고, 이제 다른 페이지에서도 손쉽게 클라이언트 정보를 확인할 수 있다.
참고 자료
'KEYNUT 프로젝트' 카테고리의 다른 글
react-three/drei Environment 태그 렌더링 블로킹 (0) | 2024.11.29 |
---|---|
느린 네트워크 환경에서 PhotoSwipe 이미지 로딩 속도 개선 (2) | 2024.09.09 |
내가 설정한 description이 안 나온다. (0) | 2024.08.28 |
[ React ] Custom Confirm Modal 만들기 (0) | 2024.08.24 |
[ Next.JS ] PWA 설정하기 feat. Please check your GenerateSW plugin configuration:[WebpackGenerateSW] 'images' property is not expected to be here. Did you mean property 'mode'? (0) | 2024.08.19 |