아직도 이해가........잘... 그래서 한번 하나하나 이해해보자...
로그인을 하면 생성되는 ? uid를 리덕스 스토어에 저장을 하고,
해당 유저의 급여내역을 보여주는데, 리덕스에 저장되어있던 uid로 조회를 한다.
그리고 유저는 ex) 12월의 급여내역을 보면서 해당 월의 정정신청을 할 수 있는데,
정정신청 내용을 파이어베이스로 보내야하고,
정정 신청 내역에서 요청한 내역들을 불러와서 조회할 수 있어야한다.
loginAuthSlice
const initialState: LoginState = {
uid: null,
isLogined: false,
};
const loginAuthSlice = createSlice({
name: "loginAuth",
initialState,
reducers: {
setUid: (state, action: PayloadAction<{ userId: string } | null>) => {
state.uid = action.payload;
},
clearUid: (state) => {
state.uid = null;
},
setIsLogined: (state, action: PayloadAction<boolean>) => {
state.isLogined = action.payload;
},
},
});
export const { setUid, clearUid, setIsLogined } = loginAuthSlice.actions;
export default loginAuthSlice.reducer;
- 역할:
- Redux를 통해 사용자의 로그인 상태와 UID를 관리합니다.
- uid와 isLogined 속성으로 사용자의 인증 정보를 저장합니다.
- 주요 메서드:
- setUid: 사용자 UID를 저장합니다.
- clearUid: 사용자 UID를 초기화합니다.
- setIsLogined: 사용자가 로그인 상태인지 관리합니다.
- 연관성:
- listenAuthChanges에서 setUid를 호출해 사용자의 UID를 저장합니다.
- 다른 컴포넌트에서 useSelector를 사용하여 UID를 가져옵니다
여기서 Slice와 reducer는??
슬라이스 :
- 리덕스 상태(state)를 기능별로 분리한 작은 단위
- 상태(initial state)와 해당 상태를 변경하는 리듀서 함수들로 이루어짐
리듀서 :
- 이전상태와 액션을 입력받아 새로운 상태를 반환하는 순수함수
- 상태 업데이트 로직의 정의
- 슬라이스는 상태와 리듀서를 묶은 "기능 단위 모듈".
- 리듀서는 상태를 업데이트하는 순수 함수로, 슬라이스 내부에서 상태 변경 로직을 담당.
ListenAuthChanges
export const listenAuthChanges = async (dispatch: AppDispatch) => {
dispatch(setLoading(true));
onAuthStateChanged(auth, async (user) => {
if (user) {
try {
dispatch(setUid({ userId: user.uid }));
const userData = await getUserData(user.uid);
if (userData) {
dispatch(
setUserInfo({
email: userData.email,
name: userData.name,
team: userData.team,
grade: userData.grade,
photoURL: userData.photoURL,
}),
);
} else {
dispatch(setUserInfo(null));
}
} catch (error) {
console.error("Error fetching user data:", error);
dispatch(setUserInfo(null));
}
} else {
dispatch(setUserInfo(null));
dispatch(setUid(null));
}
});
dispatch(setLoading(false));
};
- 역할:
- Firebase의 인증 상태 변화를 감지하고 사용자 정보를 가져옵니다.
- 작동 방식:
- onAuthStateChanged를 통해 로그인/로그아웃 상태를 감지합니다.
- setUid로 UID를 저장하고, setUserInfo로 사용자 정보를 Redux 상태에 저장합니다.
- 연관성:
- loginAuthSlice와 userInfoSlice를 통해 상태를 관리합니다.
- getUserData를 호출해 Firestore에서 사용자 데이터를 가져옵니다.
Dispatch가 뭐냐면
- 리덕스 action을 store에 보냄.
- 상태를 업뎃 하거나 특정 비동기 작업이 완료된 후 스토어에 상태변경 요청을 전달한다.
그러면 Action은?
- 상태를 변경할 때 사용,
비동기 작업과 Thunk 미들웨어...?????
(동기: 동시에 일어남, 비동기: 동시에 x, 독립적으로)
- 비동기작업처리 : 리덕스는 원래 동기적이지만,(), redux-thunk 같은 미들웨어를 사용하면 비동기작업 처리가능
- dispatch내 비동기 함수사용 :async 작성하고 그안에서 여러번 dispatch 호출해 상태 업뎃
src/services/SalaryService
export const getSalaryAmount = async (userId: string, salaryId: string) => {
const salaryRef = doc(db, `user/${userId}/salaries/${salaryId}`);
const salarySnap = await getDoc(salaryRef);
return salarySnap.exists() ? salarySnap.data().actualPayment || 0 : 0;
};
export const saveSalaryCorrection = async ({
uid,
salaryId,
correctionData,
}: {
uid: any;
salaryId: string;
correctionData: {
type: string;
reason: string;
amount: number;
status: string;
};
}) => {
const correctionRef = doc(
db,
`user/${uid}/salaries/${salaryId}/salaryCorrection/${salaryId}`,
);
await setDoc(correctionRef, {
...correctionData,
createdAt: Timestamp.now(),
});
};
- 역할:
- Firestore 데이터베이스와 상호작용하여 급여 데이터를 가져오거나 수정 요청을 저장합니다.
- 주요 함수:
- getSalaryAmount: 특정 사용자의 급여 금액을 반환합니다.
- saveSalaryCorrection: 정정 요청 데이터를 Firestore에 저장합니다.
- 연관성:
- CorrectionModal에서 호출하여 정정 요청 데이터를 저장합니다.
- SalaryPage에서 Firestore 데이터를 가져옵니다.
getSalaryAmount
export const getSalaryAmount = async (userId: string, salaryId: string) => {
const salaryRef = doc(db, `user/${userId}/salaries/${salaryId}`);
const salarySnap = await getDoc(salaryRef);
return salarySnap.exists() ? salarySnap.data().actualPayment || 0 : 0;
};
여기서 보면
특정 사용자(userId)의 특정 급여(salaryId)를 가져오고, actualPayment 필드를 반환한다.
- 특정 경로(user/{userId}/salaries/{salaryId})에 있는 문서 참조를 가져옴.
- 문서를 읽기 위해 getDoc 호출.
- 문서가 존재하면 데이터(data())에서 actualPayment를 반환, 없으면 0 반환.
saveSalaryCorrection
{
const correctionRef = doc(
db,
`user/${uid}/salaries/${salaryId}/salaryCorrection/${salaryId}`,
);
await setDoc(correctionRef, {
...correctionData,
createdAt: Timestamp.now(),
});
};
- 목적: 특정 사용자(uid)의 특정 급여(salaryId)에 대해 수정 데이터를 저장.
- 동작:
- 경로(user/{uid}/salaries/{salaryId}/salaryCorrection/{salaryId})에 문서 참조 생성.
- setDoc으로 수정 데이터를 해당 문서에 저장.
- 수정 데이터에 현재 타임스탬프(createdAt) 포함.
SalaryPage
const SalaryPage = () => {
// `selectedDate`: 선택된 날짜를 상태로 관리 (기본값은 현재 날짜)
const [selectedDate, setSelectedDate] = useState<Date>(new Date());
const [salaryData, setSalaryData] = useState<SalaryData | null>(null);
// 비동기 함수 `fetchUserData`를 생성하여 선택된 날짜의 급여 데이터를 가져옴
const fetchUserData = useCallback(async (date: Date) => {
// 선택된 날짜에서 연도와 월을 추출
const year = date.getFullYear(); // 연도 (예: 2024)
const month = String(date.getMonth() + 1).padStart(2, "0"); // 월 (1월 = 0이므로 +1 필요, 예: 01, 02)
const salaryDocPath = `user/${uid}/salaries/salaries_${year}_${month}`;
// 위 경로에 해당하는 특정 문서 참조 생성
const salaryDocRef = doc(db, salaryDocPath);
// 파이어스토어에서 문서 데이터 가져오기
const salaryDocSnap = await getDoc(salaryDocRef);
// 문서가 존재하는지 확인
if (salaryDocSnap.exists()) {
// 문서가 존재할 경우, 데이터를 `SalaryData` 형식으로 변환하여 상태에 저장
setSalaryData(salaryDocSnap.data() as SalaryData);
} else {
// 문서가 존재하지 않을 경우, 데이터를 업데이트하지 않음 (현재는 처리 로직 없음)
console.log("Salary document does not exist.");
}
}, [uid]); // `useCallback`의 의존성 배열: `uid` 값이 변경될 때만 함수가 새로 생성됨
};
useCallback을 안 쓰면 무한 요청이 발생하는 이유
useCallback의 역할
- useCallback은 메모이제이션된 콜백 함수를 반환하여, 의존성 배열이 변경되지 않는 한 함수가 재생성되지 않음.
- 의존성 배열(예: [uid])에 있는 값이 변경될 때만 새로 함수가 생성됨.
무한 요청이 발생하는 이유
useCallback을 사용하지 않으면 fetchUserData 함수가 매번 새로 생성되고, React의 렌더링 최적화와 관련된 훅에서 반복적으로 호출될 가능성이 높습니다.
일반적인 시나리오:
- 컴포넌트가 렌더링될 때마다 새로운 fetchUserData 함수가 생성됨.
- 이 함수가 다시 렌더링의 원인이 되는 훅(예: useEffect)에서 호출되면, 다시 렌더링 발생.
- 렌더링 → 함수 재생성 → 호출 → 렌더링의 무한 루프.
예시 (무한 루프 발생 가능 코드):
useEffect(() => {
if (uid && selectedDate) {
fetchUserData(selectedDate);
}
}, [uid, selectedDate, fetchUserData]);
이 useEffect는 특정 상태(uid, selectedDate)가 변경될 때마다 **fetchUserData**를 호출하여 데이터를 가져오는 역할
위 코드 문제점:
- fetchUserData가 매번 새로운 참조를 가지기 때문에, useEffect는 이를 의존성 변화로 간주하고 다시 실행.
- 결과적으로 컴포넌트가 계속 렌더링 → 함수 재생성 → 훅 재실행 → 무한 루프.
useCallback으로 해결:
- useCallback을 사용하면 fetchUserData는 동일한 참조를 유지하여, useEffect가 재실행되지 않음.
const fetchUserData = useCallback(async (date: Date) => {
// 비동기 데이터 로직
}, [uid]); // uid가 변경될 때만 함수가 재생성됨.
useEffect(() => {
fetchUserData(selectedDate); // 의존성 배열이 안정적이므로 무한 루프 방지.
}, [selectedDate, fetchUserData]);
CorrectionModal
const CorrectionModal = ({
isOpen,
onClose,
selectedDate,
}: {
isOpen: boolean;
onClose: () => void;
selectedDate: Date;
}) => {
const getSalaryId = () => {
const year = selectedDate.getFullYear();
const month = String(selectedDate.getMonth() + 1).padStart(2, "0");
return `salaries_${year}_${month}`;
};
const handleApply = async () => {
const salaryId = getSalaryId();
const amount = await getSalaryAmount(uid, salaryId);
await saveSalaryCorrection({
uid,
salaryId,
correctionData: { type, reason, amount, status: "검토 중" },
});
};
};
- 역할:
- 정정 요청을 입력받아 Firestore에 저장합니다.
- 선택된 날짜(selectedDate)를 기준으로 급여 데이터를 가져옵니다.
- 연관성:
- SalaryPage에서 호출되어 모달을 표시합니다.
- SalaryService를 호출하여 정정 데이터를 저장합니다.
그래서 정리해보자면, ,, ,. . . .
1. 로그인하면 로그인정보가 리덕스에 저장되고, 이걸통해 uid를 리덕스에 가져올수있다.
2. 사용자가 정정 요청 모달을 열면 useEffect로 초기상태 설정하고, 입력해서 apply함수로 요청보낸다.
3. uid 랑 reason이 제대로 있는지 if문으로 확인하고
4. 해당 월 페이지에서 정정요청을 보내는거니까, 선택한 날짜(selectedDate)를 기준으로 SalaryId를 생성하고
5. firebase에서 getSalaryAmount인 실지급액 가져오는거 호출해서 SalaryId에 저장된 amount 가져온다.
6. saveSalaryCorrection호출해서 정정요청을 firebase에 저장한다. 작성내용에 + 금액, 기본상태(검토중)
파일 간의 흐름
- 로그인 인증 상태 관리:
- loginAuthSlice에서 UID를 저장합니다.
- listenAuthChanges로 Firebase의 인증 변화를 감지하고 상태를 업데이트합니다.
- 급여 데이터 가져오기:
- SalaryPage에서 SalaryService를 호출하여 Firestore의 데이터를 불러옵니다.
- 정정 요청 처리:
- CorrectionModal에서 사용자의 입력을 받아 Firestore에 저장합니다.
- 저장 요청은 SalaryService의 saveSalaryCorrection를 통해 처리됩니다.
- Redux 상태와의 연계:
- Redux를 통해 UID와 사용자 정보를 저장하고 컴포넌트에서 필요시 불러옵니다
아아 복잡해랑
'⚛️ React' 카테고리의 다른 글
이미지 업로드 구현( 미리보기) (2) | 2024.12.22 |
---|---|
[ Context API ] React Context , redux와의 차이점 (1) | 2024.12.18 |
firebase + redux 로 상태관리 하기 (0) | 2024.12.10 |
드롭박스를 만들어보자.. +외부클릭으로 드롭다운 닫히는 동작 (0) | 2024.11.28 |
React-Portal로 Modal 구현하기 (0) | 2024.11.27 |