-
Notifications
You must be signed in to change notification settings - Fork 62
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[1팀 이정민] [Chater 1-3] React, Beyond the Basics #54
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
두 가지 접근 방식 모두 장단점이 있지만, 실제 프로덕션 환경에서는 다음과 같은 방법들을 고려해볼 수 있습니다:
- Optional Context + Custom Hook 사용
// UserContext.ts
export const UserContext = createContext<UserContextType | undefined>(undefined);
// useUser.ts
export function useUser() {
const context = useContext(UserContext);
if (!context) {
throw new Error("useUser must be used within UserProvider");
}
return context;
}
// ComplexForm.tsx
const { user, login, logout } = useUser();
이 방식의 장점:
- 컨텍스트 사용 로직을 커스텀 훅으로 분리하여 재사용성 증가
- 컴포넌트에서 타입 체크 로직이 제거되어 깔끔해짐
- 에러 메시지가 더 명확해짐
- 초기값 분리 + Type Guard 사용
// types.ts
export interface User {
id: number;
name: string;
email: string;
}
export interface UserContextType {
user: User | null;
login: (email: string, password: string) => Promise<void>;
logout: () => Promise<void>;
}
// UserContext.ts
const initialContext: UserContextType = {
user: null,
login: async () => {
throw new Error("UserContext not initialized");
},
logout: async () => {
throw new Error("UserContext not initialized");
}
};
export const UserContext = createContext<UserContextType>(initialContext);
// UserProvider.tsx
export function UserProvider({ children }: PropsWithChildren) {
const [user, setUser] = useState<User | null>(null);
const login = useCallback(async (email: string, password: string) => {
// 실제 로그인 로직
}, []);
const logout = useCallback(async () => {
// 실제 로그아웃 로직
}, []);
return (
<UserContext.Provider value={{ user, login, logout }}>
{children}
</UserContext.Provider>
);
}
// useUser.ts
function isLoggedIn(user: User | null): user is User {
return user !== null;
}
export function useUser() {
const { user, login, logout } = useContext(UserContext);
return {
user,
isLoggedIn: isLoggedIn(user),
login,
logout
};
}
이 방식의 장점:
- 타입 안정성이 높음
- 로그인 상태를 명확하게 구분할 수 있음
- 초기값에서 에러를 던져 실수로 초기화되지 않은 컨텍스트를 사용하는 것을 방지
- Type Guard를 통해 타입 체크와 비즈니스 로직을 깔끔하게 분리
실제 사용 예시:
function UserProfile() {
const { user, isLoggedIn, logout } = useUser();
if (!isLoggedIn) {
return <LoginPrompt />;
}
return (
<div>
<h1>{user.name}</h1>
<button onClick={logout}>Logout</button>
</div>
);
}
추가적인 개선사항:
- zod나 다른 런타임 타입 검증 라이브러리를 사용하여 API 응답 데이터의 타입 안정성을 높일 수 있습니다.
- React Query나 SWR 같은 상태관리 라이브러리를 사용하여 캐싱과 데이터 동기화를 더 효율적으로 관리할 수 있습니다.
- 에러 바운더리를 사용하여 컨텍스트 관련 에러를 더 우아하게 처리할 수 있습니다.
이러한 방식들은 타입 안정성을 유지하면서도 불필요한 코드를 최소화하고, 명확한 에러 처리를 가능하게 합니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
zod는 저도 쓰거든요. "zod alternative"로 검색하면 또 비슷한거도 나와요. Npm trend보시고, GitHub가서 유지보수 잘되는지 보시고 하셔서 타입검증 라이브러리 도입을 한번 시도해보세요~
과제 체크포인트
기본과제
심화 과제
과제 셀프회고
이번 주차는 퇴사 직전 닥친 프로젝트 마감과 바쁜 업무로 시간이 너무나도 촉박했지만, 틈틈히 학습 자료를 읽으면서 과제를 제출하는 것을 목표로 하고 임했다.
과제를 하면서 생각해보니 React를 2년간 사용하면서 useMemo, useCallback을 고민하며 사용해본 적이 드물다는 것을 알았다.
또한 그동안 쓰던 hook, 사용하던 방식대로만 코드를 구현했고 React의 업데이트 내용을 많이 놓치고 있다는 생각이 들었다.
기술적 성장
Context API의 이해
: 전역으로 상태관리를 하기 위해서 zustand, recoil 등의 상태관리 라이브러리만 사용해왔다. Context API 는 사용 경험도 없고 이해도 없었는데, 이번 기회에 공부할 수 있는 좋은 기회가 되었다.
직접 사용해보면서 Provider의 역할을 명확히 이해할 수 있었다. Provider를 어디에 배치할지, 어떤 데이터와 메서드를 Context로 제공해야 할지 고민하는 것이 코드에 상당한 영향을 미친다는 것을 배웠다.
typescript 환경에서 useContext를 사용하면서 Context를 초기 설정값이 중요하다는 것을 깨달았다. null 값이나 undefined로 처리하게 되면 이후 Context 를 불러오는 과정, 혹은 provider에서 빈 값에 대한 예외 처리를 해주어야 하거나, 타입 설정을 제대로 해줘야 문제가 없다는 것을 체감했다.
init 값을 제공하여 오류를 방지하는 방법이 좋을지, 혹은 null, undefined에 대한 예외처리를 추후에 해주는 것이 좋을지,, 이 부분은 아직도 고민 중이다.
useMemo
vs
useCallback의 차이: 그전에도 가장 어렵게 느껴졌던 hook 이였다. 이번 기회에 블로그 글, deep dive 책을 여러번 뒤적거리곤 했는데
https://velog.io/@k-svelte-master/react-hook-real-knowledge
이 분의 글처럼 나도 useMemo는 단순히 값을 기억하고, useCallback은 함수를 기억하는 hook 이라고 생각했다. 그리고 어떤 상황에서 사용해야 하는지, 이게 왜 성능 최적화를 위한 도구인지 잘 모르고 있었다. 이번 기회에 useCallback, useMemo 가 렌더링에 미치는 영향과 원리에 대해 이해할 수 있었다
shallow Copy & Deep Copy
: js 에서 얕은 복사, 깊은 복사를 다시 한번 돌아보고, memoization 구현 전 왜 shallowEquals, deepEquals 를 구현하는지, 어떤 상황에서 문제가 생기는 지 몸소 느끼게 되었다.
특히 spead ... 가 완벽한 복사가 아니라는 점을 다시 한번 깨닫게 되었다.
과제 피드백
리뷰 받고 싶은 내용
init 값을 설정해주니 login, logout 과 같은 함수의 경우 type에 억지로 맞추는 불필요한 코드란 생각도 들고, 어떤 방법이 더 좋은 방법인지 잘 모르겠습니다... 아니면 다른 더 좋은 방법이 있을까 궁금합니다.