리액트 useContext
App.tsx 파일
import "./App.css";
import { Header_X1 } from "./x1_header/Header";
import { NavMenu } from "./x1_header/NavMenu";
import { Login } from "./main/login/Login";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import { HomeBody } from "./main/home/HomeBody";
import { Register } from "./main/register/Register";
import { MyPage } from "./myPage/myPage";
import { MyPoint } from "./myPage/MyPoint";
import { UserInfo } from "./myPage/UserInfo";
import { DeleteAccount } from "./myPage/DeleteAccount";
function App() {
return (
<BrowserRouter>
<Header_X1 />
<NavMenu />
<Routes>
<Route path="/" element={<HomeBody />} />
<Route path="/login" element={<Login />} />
<Route path="/register" element={<Register />} />
<Route path="/myPage" element={<MyPage />}>
<Route path="point" element={<MyPoint />} />
<Route path="userInfo" element={<UserInfo />} />
<Route path="deleteAccount" element={<DeleteAccount />} />
</Route>
</Routes>
</BrowserRouter>
);
}
export default App;
페이지 구성은 위처럼 헤더, 메인메뉴가 공통으로 들어가있고 하단에 홈화면, 회원가입 화면, 로그인 화면, 마이페이지가 있다. 마이페이지에서는 포인트, 회원정보, 회원탈퇴 하위페이지가 있는 마이페이지 메뉴가 있다.
기능을 구현하면서 가장 고민을 했던 부분이 로그인 한 회원의 정보를 어떻게 저장하고 어디서부터 보내줘야(?) 할지 였는데
먼저 데이터베이스를 사용하지 않았기 때문에 전체 회원의 정보는 로컬스토리지에 userList 라는 키값으로 배열 형태로 저장이 되어있고, 로그인을 하면 로그인한 해당 회원의 고유 id 값을 세션스토리지에 저장을 해주도록 만들었다.
이때 세션에 저장된 id와 일치하는 회원을 로컬스토리지에서 찾아서 그 회원의 정보를 기억해두도록 하고싶었는데
처음에는 아래처럼 헤더에서 로그인한 사용자의 회원정보를 loginUserData에 담아서 Link의 state 에 담아서 하위 컴포넌트로 전달을 해주는 방법을 사용했다.
<Link to="/myPage" state={{ userData: loginUserData }}>
Link의 state를 사용하여 데이터를 전달하는 방식은 브라우저의 주소 창에 데이터를 노출시키지 않고 컴포넌트 간에 데이터를 전달할 수 있는 방법 중 하나이다. Link 컴포넌트를 사용하므로, 페이지 이동을 쉽게 구현할 수 있고, 전역 상태를 관리하는 Context API나 Redux를 사용하지 않고도 컴포넌트 간 데이터 전달이 가능하다.
그런데 이 방법을 사용하면 데이터를 전역적으로 관리할 수 없기 때문에 해당 데이터를 전달받은 컴포넌트에서만 사용할 수 있고, URL이 변경되지 않기 때문에 브라우저의 앞뒤 버튼으로 페이지 이동이 불가능하기도 하고 브라우저의 주소창에는 정보가 노출되지 않아서, 북마크나 공유 기능이 제한된다는 단점이 있기 때문에 컴포넌트 간 데이터 전달은 단방향적으로만 이루어지므로, 복잡한 구조의 애플리케이션에서는 전역 상태를 관리하는 Context API나 Redux를 사용하는 것이 더 효율적일 수 있다.
현재 내가 만들고 있는 페이지는 복잡하지는 않지만 다른 기능들을 만들면서 여러가지 오류를 발견하기도 했고 다른 방법을 사용해보고 싶었다.
useContext는 React에서 제공하는 Hook 중 하나로, Context API를 사용하여 컴포넌트 간에 데이터를 공유할 수 있게 해준다. useContext 는 데이터를 전역적으로 관리하고 필요한 컴포넌트에서 가져와 사용하는 방식이다.
useContext를 사용하면 중간에 있는 부모 컴포넌트를 거치지 않고도 하위 컴포넌트끼리 데이터를 전달할 수 있기 때문에 데이터 전달이 간편하고 코드를 간결하게 작성할 수 있고, 중복되는 코드를 줄일 수 있다. 이는 특히 데이터를 다수의 컴포넌트에서 사용해야 할 때 유용하게 사용될 수 있다. 그리고 useContext를 사용하면 애플리케이션 전체에서 사용하는 데이터를 한 곳에서 중앙 집중적으로 관리할 수 있기 때문에 이를 통해 데이터를 쉽게 관리하고 유지보수할 수 있다는 장점이 있다.
그러나, useContext를 사용하여 전역적으로 데이터를 관리하면 해당 데이터를 관리하는 Context 객체를 생성해야 하므로 초기 설정과 추가 작업이 필요하다.
그러나 useContext를 사용하면 데이터를 가져오기 위해 매번 컴포넌트 트리를 순회해야 하기 때문에 컴포넌트 트리가 깊고 데이터를 자주 사용하는 경우에는 성능 이슈가 발생할 수 있다. 그리고 Context는 여러 컴포넌트에서 사용되기 때문에 데이터 변경이 예기치 않게 일어날 수 있고 예측하지 못한 오류를 발생시킬 수 있으며 데이터가 어디서 온 것인지를 파악하기가 어려워 질 수 있기 때문에 디버깅을 어렵게 만들 수 있다는 단점이 있다.
아직 리액트에 대해서 잘 이해하고 있지 못하기 때문에 어떤 방법을 사용하는게 좋을지 판단하기는 힘들지만 리액트의 여러가지 기능들을 사용해보고 싶어서 Link의 state를 사용해서 데이터를 넘겨주는 방식에서 useContext를 사용해서 헤더부터 데이터를 넘겨주는 방법으로 바꿔보았다.
const loginUser =
sessionStorageController.getSessionStorage<FormDataType>("loginUser");
export const UserContext = createContext<FormDataType>(loginUser);
헤더에서 위처럼 UserContext 라는 context를 생성을 해 주었고,
로그인 페이지에서
const userContext = useContext<FormDataType>(UserContext);
useEffect(() => {
if (userContext.id) {
navigate("/");
}
}, []);
이렇게 UserContext를 불러오니까 데이터가 잘 받아와졌다.
원래 context 를 사용할때는
return (
<AuthContext.Provider value={{ user, login, logout }}>
{children}
</AuthContext.Provider>
);
이런식으로 Provider에서 생성한 context 값을 value로 전달해 주는 것이 일반적인 것 같은데 value 를 사용하지 않고도 context의 값을 참조할 수 있었다.
이유를 찾아보니까 value를 사용하지 않을 경우에는 Provider에서 생성한 context 값을 직접 참조하는데, 이때는 값을 변경할 수 없고, 단순히 읽기만 가능하다. 이런 방식은 단순한 상태를 전역적으로 사용할 때 유용하게 사용될 수 있다고 한다.
value를 사용할 경우에는 Provider에서 생성한 context 값을 value로 전달하고, value를 사용해 값을 변경할 수 있으며 보다 복잡한 상태를 전역적으로 사용할 때 유용하다고 한다.
일단은 시간이 부족해서 value를 사용하지 않고 데이터를 전달받기까지만 했지만 나중에 회원정보를 수정하는 기능을 구현하려면 value를 사용하고 데이터를 전달하도록 만들어야 할 것 같다.