Skip to content

Commit

Permalink
feat: 렌더링 최소화
Browse files Browse the repository at this point in the history
  • Loading branch information
pitangland committed Jan 2, 2025
1 parent 9927641 commit 2c98372
Showing 1 changed file with 121 additions and 73 deletions.
194 changes: 121 additions & 73 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import React, { useState, createContext, useContext } from "react";
import React, {
useState,
createContext,
useContext,
useCallback,
useMemo,
PropsWithChildren,
} from "react";
import { generateItems, renderLog } from "./utils";

// 타입 정의
Expand All @@ -21,33 +28,117 @@ interface Notification {
type: "info" | "success" | "warning" | "error";
}

// AppContext 타입 정의
interface AppContextType {
// Context 정의
interface ThemeContextType {
theme: string;
toggleTheme: () => void;
}

interface UserConTextType {
user: User | null;
login: (email: string, password: string) => void;
logout: () => void;
}

interface NotificationContextType {
notifications: Notification[];
addNotification: (message: string, type: Notification["type"]) => void;
removeNotification: (id: number) => void;
}

const AppContext = createContext<AppContextType | undefined>(undefined);
const ThemeContext = createContext<ThemeContextType | null>(null);
const UserContext = createContext<UserConTextType | null>(null);
const NotificationContext = createContext<NotificationContextType | null>(null);

const ThemeProvider: React.FC<PropsWithChildren> = ({ children }) => {
const [theme, setTheme] = useState("light");
const toggleTheme = useCallback(() => {
setTheme((prev) => (prev === "light" ? "dark" : "lht"));
}, []);
const value = useMemo(() => ({ theme, toggleTheme }), [theme, toggleTheme]);
return (
<ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>
);
};

const UserProvider: React.FC<PropsWithChildren> = ({ children }) => {
const [user, setUser] = useState<User | null>(null);
const { addNotification } = useNotification();

const login = useCallback(
(name: string, email: string) => {
setUser({ id: 1, name, email });
addNotification("Login successful", "success");
},
[addNotification],
);
const logout = useCallback(() => {
setUser(null);
addNotification("Logout successful", "info");
}, [addNotification]);

const value = useMemo(() => ({ user, login, logout }), [user, login, logout]);

return <UserContext.Provider value={value}>{children}</UserContext.Provider>;
};

const NotificationProvider: React.FC<PropsWithChildren> = ({ children }) => {
const [notifications, setNotifications] = useState<Notification[]>([]);

const addNotification = useCallback(
(message: string, type: Notification["type"]) => {
setNotifications((prev) => [...prev, { id: Date.now(), message, type }]);
},
[],
);

const removeNotification = useCallback((id: number) => {
setNotifications((prev) => prev.filter((notif) => notif.id !== id));
}, []);

const value = useMemo(
() => ({
notifications,
addNotification,
removeNotification,
}),
[notifications, addNotification, removeNotification],
);

return (
<NotificationContext.Provider value={value}>
{children}
</NotificationContext.Provider>
);
};

// 커스텀 훅: useAppContext
const useAppContext = () => {
const context = useContext(AppContext);
if (context === undefined) {
throw new Error("useAppContext must be used within an AppProvider");
}
// 커스텀 훅
const useTheme = () => {
const context = useContext(ThemeContext);
if (!context) throw new Error("useTheme must be used within a ThemeProvider");
return context;
};

const useUser = () => {
const context = useContext(UserContext);
if (!context) throw new Error("useUser must be used within a UserProvider");
return context;
};

const useNotification = () => {
const context = useContext(NotificationContext);
if (!context)
throw new Error(
"useNotification must be used within a NotificationProvider",
);
return context;
};

// Header 컴포넌트
export const Header: React.FC = () => {
renderLog("Header rendered");
const { theme, toggleTheme, user, login, logout } = useAppContext();
const { theme, toggleTheme } = useTheme();
const { user, login, logout } = useUser();

const handleLogin = () => {
// 실제 애플리케이션에서는 사용자 입력을 받아야 합니다.
Expand Down Expand Up @@ -96,7 +187,7 @@ export const ItemList: React.FC<{
}> = ({ items, onAddItemsClick }) => {
renderLog("ItemList rendered");
const [filter, setFilter] = useState("");
const { theme } = useAppContext();
const { theme } = useTheme();

const filteredItems = items.filter(
(item) =>
Expand Down Expand Up @@ -151,7 +242,7 @@ export const ItemList: React.FC<{
// ComplexForm 컴포넌트
export const ComplexForm: React.FC = () => {
renderLog("ComplexForm rendered");
const { addNotification } = useAppContext();
const { addNotification } = useNotification();
const [formData, setFormData] = useState({
name: "",
email: "",
Expand Down Expand Up @@ -236,7 +327,7 @@ export const ComplexForm: React.FC = () => {
// NotificationSystem 컴포넌트
export const NotificationSystem: React.FC = () => {
renderLog("NotificationSystem rendered");
const { notifications, removeNotification } = useAppContext();
const { notifications, removeNotification } = useNotification();

return (
<div className="fixed bottom-4 right-4 space-y-2">
Expand Down Expand Up @@ -268,14 +359,7 @@ export const NotificationSystem: React.FC = () => {

// 메인 App 컴포넌트
const App: React.FC = () => {
const [theme, setTheme] = useState("light");
const [items, setItems] = useState(generateItems(1000));
const [user, setUser] = useState<User | null>(null);
const [notifications, setNotifications] = useState<Notification[]>([]);

const toggleTheme = () => {
setTheme((prevTheme) => (prevTheme === "light" ? "dark" : "light"));
};

const addItems = () => {
setItems((prevItems) => [
Expand All @@ -284,61 +368,25 @@ const App: React.FC = () => {
]);
};

const login = (email: string) => {
setUser({ id: 1, name: "홍길동", email });
addNotification("성공적으로 로그인되었습니다", "success");
};

const logout = () => {
setUser(null);
addNotification("로그아웃되었습니다", "info");
};

const addNotification = (message: string, type: Notification["type"]) => {
const newNotification: Notification = {
id: Date.now(),
message,
type,
};
setNotifications((prev) => [...prev, newNotification]);
};

const removeNotification = (id: number) => {
setNotifications((prev) =>
prev.filter((notification) => notification.id !== id),
);
};

const contextValue: AppContextType = {
theme,
toggleTheme,
user,
login,
logout,
notifications,
addNotification,
removeNotification,
};

return (
<AppContext.Provider value={contextValue}>
<div
className={`min-h-screen ${theme === "light" ? "bg-gray-100" : "bg-gray-900 text-white"}`}
>
<Header />
<div className="container mx-auto px-4 py-8">
<div className="flex flex-col md:flex-row">
<div className="w-full md:w-1/2 md:pr-4">
<ItemList items={items} onAddItemsClick={addItems} />
</div>
<div className="w-full md:w-1/2 md:pl-4">
<ComplexForm />
<ThemeProvider>
<NotificationProvider>
<UserProvider>
<Header />
<div className="container mx-auto px-4 py-8">
<div className="flex flex-col md:flex-row">
<div className="w-full md:w-1/2 md:pr-4">
<ItemList items={items} onAddItemsClick={addItems} />
</div>
<div className="w-full md:w-1/2 md:pl-4">
<ComplexForm />
</div>
</div>
</div>
</div>
<NotificationSystem />
</div>
</AppContext.Provider>
<NotificationSystem />
</UserProvider>
</NotificationProvider>
</ThemeProvider>
);
};

Expand Down

0 comments on commit 2c98372

Please sign in to comment.