Skip to content

Commit 90646b6

Browse files
authored
[FE] fix : 리뷰 모아보기 쿼리 캐시로 인한 변경된 형광펜 데이터 반영 오류 수정 (#976)
* docs: 질문별 모아보기 목 데이터 주관식 답변 변경 * feat : 현재 sectionId sessionSotrage에 저장하고, 모아보기 페이지 언마운트 시 삭제하는 기능 추가 * feat : 형광펜 API 요청 성공 후, 해당 질문 쿼리 무효화하는 기능 추가 * feat : 형광펜 데이터 로컬 스토리지에 저장한 코드 삭제 * feat : ReviewCollectionPage 에서 clearEditorAnswerMapStorage 코드 삭제
1 parent 1b87993 commit 90646b6

File tree

7 files changed

+39
-50
lines changed

7 files changed

+39
-50
lines changed

frontend/src/components/highlight/components/HighlightEditor/hooks/useHighlight.ts

+2-17
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
1-
import { useEffect, useState } from 'react';
1+
import { useState } from 'react';
22

3-
import {
4-
EDITOR_ANSWER_CLASS_NAME,
5-
HIGHLIGHT_EVENT_NAME,
6-
HIGHLIGHT_SPAN_CLASS_NAME,
7-
SESSION_STORAGE_KEY,
8-
} from '@/constants';
3+
import { EDITOR_ANSWER_CLASS_NAME, HIGHLIGHT_EVENT_NAME, HIGHLIGHT_SPAN_CLASS_NAME } from '@/constants';
94
import { EditorAnswerMap, EditorLine, HighlightResponseData, ReviewAnswerResponseData } from '@/types';
105
import {
116
getEndLineOffset,
@@ -76,14 +71,6 @@ const useHighlight = ({
7671
handleModalMessage,
7772
}: UseHighlightProps) => {
7873
const [editorAnswerMap, setEditorAnswerMap] = useState<EditorAnswerMap>(makeInitialEditorAnswerMap(answerList));
79-
const storageKey = `${SESSION_STORAGE_KEY.editorAnswerMap}-${questionId}`;
80-
81-
useEffect(() => {
82-
const item = localStorage.getItem(storageKey);
83-
if (item) {
84-
setEditorAnswerMap(new Map(JSON.parse(item)) as EditorAnswerMap);
85-
}
86-
}, []);
8774

8875
// span 클릭 시, 제공되는 형광펜 삭제 기능 타겟
8976
const [longPressRemovalTarget, setLongPressRemovalTarget] = useState<RemovalTarget | null>(null);
@@ -92,8 +79,6 @@ const useHighlight = ({
9279

9380
const updateEditorAnswerMap = (newEditorAnswerMap: EditorAnswerMap) => {
9481
setEditorAnswerMap(newEditorAnswerMap);
95-
// editorAnswerMap이 변경될 때 새로운 값을 로컬 스토리지에 저장
96-
localStorage.setItem(storageKey, JSON.stringify(Array.from(newEditorAnswerMap)));
9782
};
9883

9984
const resetHighlightMenu = () => {

frontend/src/components/highlight/components/HighlightEditor/hooks/useMutateHighlight/index.ts

+19-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { useMutation } from '@tanstack/react-query';
1+
import { useMutation, useQueryClient } from '@tanstack/react-query';
22

33
import { postHighlight } from '@/apis/highlight';
4-
import { LOCAL_STORAGE_KEY } from '@/constants';
4+
import { LOCAL_STORAGE_KEY, REVIEW_QUERY_KEY, SESSION_STORAGE_KEY } from '@/constants';
55
import { EditorAnswerMap } from '@/types';
66

77
export interface UseMutateHighlightProps {
@@ -17,6 +17,21 @@ const useMutateHighlight = ({
1717
updateEditorAnswerMap,
1818
resetHighlightMenu,
1919
}: UseMutateHighlightProps) => {
20+
const queryClient = useQueryClient();
21+
/**
22+
* 형광펜 API 성공 후, 현재 질문에 대한 쿼리 캐시 무효화해서, 변경된 형광펜 데이터 불러오도록 함
23+
*/
24+
const invalidateCurrentSectionQuery = () => {
25+
const sectionId = sessionStorage.getItem(SESSION_STORAGE_KEY.currentReviewCollectionSectionId);
26+
27+
if (sectionId) {
28+
queryClient.invalidateQueries({
29+
predicate: (query) =>
30+
query.queryKey[0] === REVIEW_QUERY_KEY.groupedReviews && query.queryKey[1] === Number(sectionId),
31+
});
32+
}
33+
};
34+
2035
const mutation = useMutation({
2136
mutationFn: (newEditorAnswerMap: EditorAnswerMap) => postHighlight(newEditorAnswerMap, questionId),
2237
onMutate: () => {
@@ -28,6 +43,8 @@ const useMutateHighlight = ({
2843
// 토스트 모달 지우기
2944
handleErrorModal(false);
3045
localStorage.removeItem(LOCAL_STORAGE_KEY.isHighlightError);
46+
// 해당 질문 쿼리 캐시 무효화
47+
invalidateCurrentSectionQuery();
3148
},
3249
onError: (error) => {
3350
//토스트 모달 띄움

frontend/src/constants/storageKey.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ export const LOCAL_STORAGE_KEY = {
44
};
55

66
export const SESSION_STORAGE_KEY = {
7-
editorAnswerMap: 'editorAnswerMap-question',
7+
currentReviewCollectionSectionId: 'currentReviewCollectionSectionId',
88
};

frontend/src/mocks/mockData/reviewCollection.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ export const GROUPED_REVIEWS_MOCK_DATA: GroupedReviews[] = [
115115
{
116116
id: 2,
117117
content:
118-
'http://localhost:3000/user/review-zone/5WkYQLqW1http://localhost:3000/user/review-zone/5WkYQLqW2http://localhost:3000/user/review-zone/5WkYQLqW3http://localhost:3000/user/review-zone/5WkYQLqW4http://localhost:3000/user/review-zone/5WkYQLqW5http://localhost:3000/user/review-zone/5WkYQLqW6http://localhost:3000/user/review-zone/5WkYQLqW7http://localhost:3000/user/review-zone/5WkYQLqW8http://localhost:3000/user/review-zone/5WkYQLqW9http://localhost:3000/user/review-zone/5WkYQLqW10',
118+
' 복잡한 문제를 체계적으로 분석하고, 창의적인 해결책을 제안하며 이를 실행하는 데 뛰어난 역량을 보여줍니다. 특히, 제한된 시간과 자원 속에서도 효과적으로 우선순위를 정하고 문제를 해결하는 모습을 통해 팀에 큰 신뢰를 주었습니다. 이러한 능력은 팀의 목표 달성과 성장에 큰 기여를 하며, 앞으로도 더 많은 성과를 낼 수 있을 것으로 기대됩니다.!!!!!',
119119
highlights: [
120120
{
121121
lineIndex: 0,
@@ -132,13 +132,13 @@ export const GROUPED_REVIEWS_MOCK_DATA: GroupedReviews[] = [
132132
{
133133
id: 3,
134134
content:
135-
'장의 시작부분은 짧고 직접적이며, 뒤따라 나올 복잡한 정보를 어떻게 해석해야 할 것인지 프레임을 짜주는 역할을 해야 한다. 그러면 아무리 긴 문장이라도 쉽게 읽힌다.',
135+
'문제의 핵심 원인을 빠르게 파악하고, 이를 바탕으로 실행 가능한 솔루션을 제시하며 팀의 목표를 달성하는 데 큰 기여를 했습니다. 특히, 예상치 못한 상황에서도 냉철한 판단과 적극적인 태도로 해결책을 찾아가는 모습은 팀원들에게 좋은 자극이 되었습니다.',
136136
highlights: [],
137137
},
138138
{
139139
id: 4,
140140
content:
141-
'고액공제건강보험과 건강저축계좌를 만들어 노동자와 고용주가 세금공제를 받을 수 있도록 하면 결과적으로 노동자의 의료보험 부담이 커진다. 세금공제를 받을 수 있도록 하면------------------------------------------- 결과적으로 노동자의 의료보험 부담이 커진다.',
141+
'문제를 다양한 관점에서 바라보며 가장 적합한 해결책을 찾아내는 능력이 뛰어납니다. 특히, 여러 이해관계자 간의 의견을 조율하며 모두가 만족할 수 있는 방안을 제안한 점이 돋보였습니다. 이 과정에서 보여준 적극적인 소통과 논리적인 접근법은 팀의 신뢰를 더욱 높였고, 어려운 과제를 성공적으로 마무리할 수 있는 원동력이 되었습니다.',
142142
highlights: [],
143143
},
144144
],
@@ -181,7 +181,7 @@ export const GROUPED_REVIEWS_MOCK_DATA: GroupedReviews[] = [
181181
{
182182
id: 2,
183183
content:
184-
'http://localhost:3000/user/review-zone/5WkYQLqW1http://localhost:3000/user/review-zone/5WkYQLqW2http://localhost:3000/user/review-zone/5WkYQLqW3http://localhost:3000/user/review-zone/5WkYQLqW4http://localhost:3000/user/review-zone/5WkYQLqW5http://localhost:3000/user/review-zone/5WkYQLqW6http://localhost:3000/user/review-zone/5WkYQLqW7http://localhost:3000/user/review-zone/5WkYQLqW8http://localhost:3000/user/review-zone/5WkYQLqW9http://localhost:3000/user/review-zone/5WkYQLqW10',
184+
'효율적인 시간 관리 능력을 통해 중요한 작업을 기한 내에 완수하는 모습이 매우 인상적이었습니다. 특히, 작업의 우선순위를 명확히 구분하고 이를 기반으로 체계적으로 계획을 세워 진행하는 점이 돋보였습니다. 이러한 능력 덕분에 팀 전체의 생산성이 향상되었고, 예상치 못한 문제가 발생했을 때도 유연하게 대처하며 프로젝트를 성공적으로 이끌었습니다.',
185185
highlights: [
186186
{
187187
lineIndex: 0,
@@ -198,13 +198,13 @@ export const GROUPED_REVIEWS_MOCK_DATA: GroupedReviews[] = [
198198
{
199199
id: 3,
200200
content:
201-
'장의 시작부분은 짧고 직접적이며, 뒤따라 나올 복잡한 정보를 어떻게 해석해야 할 것인지 프레임을 짜주는 역할을 해야 한다. 그러면 아무리 긴 문장이라도 쉽게 읽힌다.',
201+
'시간을 효율적으로 활용하는 뛰어난 능력을 보여주셨습니다. 작업 초기부터 명확한 계획을 수립하고 이를 끝까지 유지하는 모습이 인상적이었으며, 예상치 못한 변수에도 침착하게 대처하며 프로젝트의 일정과 품질을 모두 충족시켰습니다. 이러한 점은 팀에 큰 안정감을 주었고, 함께 일하는 사람들에게도 좋은 본보기가 되었습니다.',
202202
highlights: [],
203203
},
204204
{
205205
id: 4,
206206
content:
207-
'고액공제건강보험과 건강저축계좌를 만들어 노동자와 고용주가 세금공제를 받을 수 있도록 하면 결과적으로 노동자의 의료보험 부담이 커진다. 세금공제를 받을 수 있도록 하면------------------------------------------- 결과적으로 노동자의 의료보험 부담이 커진다.',
207+
'타이트한 일정 속에서도 주어진 목표를 체계적으로 달성하며, 동시에 세부적인 디테일까지 놓치지 않는 모습을 보여주셨습니다. 특히, 작업 과정에서 우선순위를 명확히 설정하고, 불필요한 시간 낭비를 줄이는 효율적인 접근 방식은 팀의 전반적인 속도와 성과에 크게 기여했습니다. 앞으로도 이런 시간 관리 능력을 통해 더 많은 성과를 이루시리라 믿습니다.',
208208
highlights: [],
209209
},
210210
],

frontend/src/pages/ReviewCollectionPage/components/ReviewCollectionPageContents/index.tsx

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import React, { useContext, useState } from 'react';
1+
import { useContext, useEffect, useState } from 'react';
22

33
import { Accordion, Dropdown, HighlightEditorContainer } from '@/components';
44
import { DropdownItem } from '@/components/common/Dropdown';
55
import ReviewEmptySection from '@/components/common/ReviewEmptySection';
66
import { ReviewInfoDataContext } from '@/components/layouts/ReviewDisplayLayout/ReviewInfoDataProvider';
7-
import { REVIEW_EMPTY } from '@/constants';
7+
import { REVIEW_EMPTY, SESSION_STORAGE_KEY } from '@/constants';
88
import { GroupedReview } from '@/types';
99
import { substituteString } from '@/utils';
1010

@@ -18,6 +18,7 @@ const ReviewCollectionPageContents = () => {
1818
const { revieweeName, projectName, totalReviewCount } = useContext(ReviewInfoDataContext);
1919

2020
const { data: reviewSectionList } = useGetSectionList();
21+
2122
const dropdownSectionList = reviewSectionList.sections.map((section) => {
2223
return { text: section.name, value: section.id };
2324
});
@@ -29,6 +30,12 @@ const ReviewCollectionPageContents = () => {
2930
review.votes?.sort((voteA, voteB) => voteB.count - voteA.count);
3031
});
3132

33+
useEffect(() => {
34+
return () => {
35+
sessionStorage.removeItem(SESSION_STORAGE_KEY.currentReviewCollectionSectionId);
36+
};
37+
}, []);
38+
3239
const renderContent = (review: GroupedReview) => {
3340
if (review.question.type === 'CHECKBOX') {
3441
const hasNoCheckboxAnswer = review.votes?.every((vote) => vote.count === 0);

frontend/src/pages/ReviewCollectionPage/hooks/useGetGroupedReviews.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useSuspenseQuery } from '@tanstack/react-query';
22

33
import { getGroupedReviews } from '@/apis/review';
4-
import { REVIEW_QUERY_KEY } from '@/constants';
4+
import { REVIEW_QUERY_KEY, SESSION_STORAGE_KEY } from '@/constants';
55
import { GroupedReviews } from '@/types';
66

77
interface UseGetGroupedReviewsProps {
@@ -11,6 +11,7 @@ interface UseGetGroupedReviewsProps {
1111
const useGetGroupedReviews = ({ sectionId }: UseGetGroupedReviewsProps) => {
1212
const fetchGroupedReviews = async () => {
1313
const result = await getGroupedReviews({ sectionId });
14+
sessionStorage.setItem(SESSION_STORAGE_KEY.currentReviewCollectionSectionId, sectionId.toString());
1415
return result;
1516
};
1617

frontend/src/pages/ReviewCollectionPage/index.tsx

-21
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,9 @@
1-
import { useEffect } from 'react';
2-
31
import { AuthAndServerErrorFallback, ErrorSuspenseContainer, TopButton } from '@/components';
42
import ReviewDisplayLayout from '@/components/layouts/ReviewDisplayLayout';
5-
import { SESSION_STORAGE_KEY } from '@/constants';
63

74
import ReviewCollectionPageContents from './components/ReviewCollectionPageContents';
85

96
const ReviewCollectionPage = () => {
10-
const clearEditorAnswerMapStorage = () => {
11-
for (let i = 0; i < localStorage.length; i++) {
12-
const key = localStorage.key(i);
13-
14-
// 키에 특정 문자열이 포함되어 있는지 확인
15-
if (key?.includes(SESSION_STORAGE_KEY.editorAnswerMap)) {
16-
localStorage.removeItem(key); // 해당 키 삭제
17-
i--; // removeItem 후에 인덱스가 변경되므로 i를 감소시켜야 함
18-
}
19-
}
20-
};
21-
22-
useEffect(() => {
23-
return () => {
24-
clearEditorAnswerMapStorage();
25-
};
26-
}, []);
27-
287
return (
298
<ErrorSuspenseContainer fallback={AuthAndServerErrorFallback}>
309
<ReviewDisplayLayout isReviewList={false}>

0 commit comments

Comments
 (0)