Next.js는 클라이언트에서 실행되는 React 기반의 프레임워크지만, 동시에 서버에서도 실행될 수 있는 환경을 제공한답니다.
따라서 window 객체나 localStorage 같은 브라우저 전용 기능을 사용할 때는 서버 환경을 고려해야 합니다.
Vite만 쓰던 저에게 가장 어려운 부분이 이거였습니다. 서버의 존재를 계속 고려해야한다는
이번 글에서는 window 객체의 역할과 Next.js에서 localStorage와 같은 브라우저 전용기능을 활용할 때 서버 동작을 어떻게 고려해야하는지 학습한 내용을 정리합니다.
window 객체란?
어디서 들어봤는데 이거
window 객체는 브라우저 환경에서만 존재하는 전역 객체입니다.
즉, 클라이언트(브라우저)에서만 접근 가능하고, 서버에서는 존재하지 않습니다. 따라서 서버에서도 실행될 수 있는점을 고려해아하는 next 환경에서 이를 무시하면 오류가 난다는 겁니다.
🔹 window 객체 예제
console.log(window.location.href); // ✅ 브라우저에서는 정상 실행됨
console.log(window.localStorage.getItem("key")); // ✅ 브라우저에서 실행 가능
🚨 **하지만 Next.js 서버 환경에서는 ****window**가 없기 때문에 위 코드가 실행되지 않음!
console.log(window.location.href); // ❌ Next.js 서버에서는 오류 발생!
**즉, Next.js에서는 ****window**가 없는 환경이라는 것을 고려해야합니다
아 근데 왜없습니까 window가 서버에는 누구한테 따져야합니까
**📌 Next.js에서 ****window**가 없는 이유 -> 그렇게 만들어져셔
Next.js는 서버에서 미리 HTML을 생성한 후, 클라이언트에서 실행(SSR, Server-Side Rendering) 하는 방식이기 때문입니다.
서버사이드 렌더링 이라는 방식으로 동작하기 때문입니다. (서버사이드니까 서버가 개입하겠네요)
🔹 Next.js 실행 흐름 (서버사이드 렌더링)
- 사용자가 /home 페이지에 접속함
- Next.js 서버에서 HTML을 생성한 후, 클라이언트(브라우저)로 전달
- 브라우저에서 React가 실행되며 클라이언트 측 렌더링(CSR) 시작
✅ **클라이언트 단계에서만 ****window**와 localStorage 사용 가능
❌ **서버 단계에서는 ****window**가 없어서 오류 발생 가능
✅ Vite에서의 실행 흐름 (클라이언트 사이드 렌더링)
1. 사용자가 /home 페이지에 접속함
2. Vite 개발 서버(vite dev)가 index.html을 제공함
3. HTML 내부에서 <script type="module" src="/src/main.tsx">가 실행됨
4. Vite가 main.tsx를 읽고, 필요한 모듈을 ESM(ES Modules) 방식으로 동적으로 로드
5. React가 실행되며, 클라이언트에서 DOM을 렌더링 (CSR - Client Side Rendering)
✅ 모든 렌더링 과정은 브라우저에서 실행되므로 window 및 localStorage를 자유롭게 사용 가능
❌ 서버 단계에서 HTML을 미리 생성하지 않으므로 SEO(검색 엔진 최적화) 측면에서 불리할 수 있음
Vite와 Next.js 실행 흐름 비교 (표)
Vite (CSR) VS Next.js (SSR + CSR)
1. 요청 처리 | 클라이언트가 직접 index.html요청 | 서버가 HTML을 미리 생성하여 전달 |
2. JavaScript 실행 | script 태그를 통해 클라이언트에서 실행 | 초기 HTML 로드 후 React가 클라이언트에서 실행 |
3. 렌더링 방식 | 클라이언트에서 모든 UI 렌더링 (CSR) | 서버에서 HTML을 미리 렌더링 (SSR) 후 클라이언트에서 React 실행 |
4. window, localStorage 사용 | 항상 사용 가능 | 서버에서는 사용 불가 → useEffect 필요 |
✅ Vite는 모든 렌더링이 클라이언트에서 발생하므로 window 및 localStorage를 자유롭게 사용할 수 있음
❌ Next.js에서는 서버 렌더링 시 window 객체가 존재하지 않으므로 useEffect 내부에서 사용해야 함
그에따른 SSR환경에서 localStorage 사용 시 주의할 점
localStorage는 브라우저에서만 사용 가능한 저장소입니다. Next.js의 서버 환경에서는 localStorage가 없기 때문에 직접 호출하면 오류가 발생합니다.
🔹 서버에서 localStorage 사용 시 오류 예제
const value = localStorage.getItem("isFirstTimeUser"); // ❌ Next.js 서버에서는 실행 불가능!
🚨 **서버에서는 ****localStorage**가 없기 때문에 코드 실행 시 오류 발생
➡ 이를 방지하려면, 서버인지 클라이언트인지 확인하는 코드가 필요합니다.
서버에서 실행되지 않도록 방지하는 코드를 넣는다. (typeof window !== "undefined")
**🔹 ****window**가 있는지 확인 후 실행
if (typeof window !== "undefined") {
const value = localStorage.getItem("isFirstTimeUser"); // ✅ 서버에서는 실행되지 않음
}
✔ typeof window !== "undefined" 체크 덕분에 브라우저 환경에서만 실행됨
✔ Next.js 서버에서 실행될 때 오류가 발생하지 않음
** 실전 도입 ! Next.js에서 ****localStorage**를 활용한 온보딩 모달 관리
아래 코드는 사용자가 처음 방문했는지(isFirstTimeUser)를 판단하여 온보딩 모달을 띄우는 함수입니다.
이번 프로젝트 제 첫 과업이라서..
getIsFirstTimeUser() (첫 방문 여부 확인 함수)
export const getIsFirstTimeUser = (): boolean => {
if (typeof window !== 'undefined') {
const value: string | null = localStorage.getItem('isFirstTimeUser'); // "true" or "false"
if (value === null) {
return true;
}
return value === 'true';
}
return true; // 서버 환경에서는 기본값으로 true 반환
};
✔ 동작 방식
- **typeof window !== 'undefined'**로 클라이언트 환경인지 확인 (SSR 고려)
- localStorage.getItem('isFirstTimeUser')** 값이 없으면 true 반환** → 즉, 첫 방문자
- 혹시 몰라 말하지만, localStorage에는 문자열 값만 주고받을 수 있기에
- Boolean 값을 저장하기에 'true'로 로컬스토로지에 저장하고, 함수자체가 Boolean을 주는것 처럼 설계
- 또한 당연히 저장할때는 localStorage의 Value값 ( Boolean type)을 .toString()을 활용해서 문자열로 저장
- 값이 'true'면 true, 'false'면 false 반환
- 서버 환경에서는 기본적으로 true 반환 (서버에서 localStorage 접근 불가)
setIsFirstTimeUser() (첫 방문 여부 설정 함수)
export const setIsFirstTimeUser = (value: boolean): void => {
if (typeof window !== 'undefined') {
localStorage.setItem('isFirstTimeUser', value.toString());
}
};
✔ 동작 방식
- 클라이언트 환경에서만 실행되도록 typeof window !== 'undefined' 체크 (SSR 똑같이 고려)
- **value.toString()****을 사용해 ****localStorage**에 문자열로 저장 (localStorage는 문자열만 저장 가능)
getIsFirstTimeUser() & setIsFirstTimeUser() 로컬스토리지 헬핑 함수 활용
HomePage 컴포넌트에서 사용하기
impgrt { useEffect, useState } from 'react';
import { getIsFirstTimeUser, setIsFirstTimeUser } from '@/utils/localStorage';
import OnboardingModal from '@/components/OnboardingModal';
const HomePage = () => {
const [showOnboarding, setShowOnboarding] = useState(false);
useEffect(() => {
if (getIsFirstTimeUser()) {
setShowOnboarding(true);
}
}, []);
const handleCloseOnboarding = () => {
setShowOnboarding(false);
setIsFirstTimeUser(false);
};
return (
<div>
{showOnboarding && <OnboardingModal onClose={handleCloseOnboarding} />}
<h1>홈페이지</h1>
</div>
);
};
export default HomePage;
✅ **최초 방문이면 ****getIsFirstTimeUser()**가 true 반환 → 온보딩 모달 표시
✅ 사용자가 모달을 닫으면 setIsFirstTimeUser(false) 실행 → 다시 뜨지 않음 ( 이미 사용했기에)
도파민 중독자를 위한 4줄 요약
- **window**는 브라우저에서만 존재하며, 서버(Next.js SSR)에는 없음.
- **Next.js에서 ****localStorage**를 사용할 때는 typeof window !== 'undefined' 체크 필수! (서버에서 실행 방지)
- **getIsFirstTimeUser()****와 ****setIsFirstTimeUser()** 즉, 로컬스토로지 활용 함수를 활용하면 온보딩 모달을 효율적으로 관리 가능.
- Next.js의 서버-클라이언트 환경을 고려하여 안전한 코드 작성이 중요!
'FrontEnd Develop > Project : AI TUTOR' 카테고리의 다른 글
튜토리얼 모달 컴포넌트 기능 개발 #2. 상세 설계서 (0) | 2025.03.11 |
---|---|
튜토리얼 모달 컴포넌트 기능 개발 #1. 기본 설계서 (0) | 2025.03.09 |
Next.js + Typescript 적응기 #1: 프로젝트 구조와 개념 정리 (0) | 2025.03.06 |