테스트의 결과를 모아놓는 컴포넌트인데 한가지 기능을 추가하면서 발생한 문제다.
❌ 바로 로그인한 유저와 비공개 처리를 한 글을 추가하는 과정
testResult.map((testResults)) => {
<ResultField key={testResults.id}/>
}
로 최초에 작성되어 있다면 여기서 이제
filter() 메서드를 활용해 추가하는 과정에 발생한 문제였다.
testResult.filter((testResults)=>
testResults.visibility === true ||
testResults.userid === userProfile.id
)=>.map((testResults)) => {
<ResultField key={testResults.id}/>
}
이렇게 visibility가 참이거나 결과의 userid 값이 로그인된 회원의 id가 일치하는
항목들을 반환받아 그 값을 map을 이용해 동작하였다.
그러나 여기서 작성했을 때 흰 화면만 나오는 문제가 있었는데,
console.log(testResults.visibility)
console.log(testResults.userid)
console.log(userProfile.id)
api 통신을 통해 데이터를 가져오는 순간에 문제가 발생하지 않았기에,
데이터를 받아왔을 때의 문제가 있다고 확신했다.
그래서 위처럼 콘솔을 통해 확인해보니
console.log(userProfile.id) // undefined
이 부분이 undefined 가 출력되는 것이였다.
그래서 나는 Optional chaining을 사용해 해결할 수 있었다
testResult.filter((testResults)=>
testResults.visibility === true ||
testResults.userid === userProfile?.id
)=>.map((testResults)) => {
<ResultField key={testResults.id}/>
}
그럼 여기서 왜 옵셔널 체이닝이 들어가면 작동이 되는 것일까?
그 이유는 userProfile.id로 지정할 경우 반드시 존재한다고 가정했을 때이다.
우리는 api 통신을 통해 userProfile을 가져오기 때문에
전송되는 과정중에서 초기에 없었다가 후에 로딩이 될 수도 있다.
그렇기에 최초에는 값이 존재하지 않을 수도 있다고 가정해야 하기에,
위처럼 userProfile이 undefined 또는 null 일 가능성을 염두해야 한다.
따라서 위처럼 해결하여 문제를 해결했습니다.
boolean 타입을 활용해 1번의 visibility를 db-json 객체에 추가하는 컴포넌트다.
이 부분에서 true false가 원하는 방향으로 잡히지 않았기에 한번 더 체크해본다.
export const updateTestResultVisibility = async (id, visibility) => {
const visibilityResponse = await API_INSTANCE.patch(`/${id}`, {
visibility: visibility,
});
return visibilityResponse.data;
먼저 이해를 돕기 위해 updateTestResultVisibility 가 인자로, id visibility를 받고,
그걸 api에 접근해서 접속한 유저의 id가 일치하면
visibility : boolean 으로 저장해주는 로직을 이용했다.
그러면 이제 TestResultItem 가 props로 받는 인자는?
<TestResultItem
id={testResults.id}
initialVisibility={testResults.visibility}
/>
이렇게 testResults의 id와 visibility를 받아서
prop으로 TestResultItem에 보내준다.
그럼 클릭했을 때 기존의 값에서 바꿔줘야 하기 떄문에
const [visibility, setVisibility] = useState(initialVisibility);
버튼의 텍스트를 바꿔줄 state를 하나 만들어주고,
const newVisibility = visibility;
<button onClick={() => handleVisibility()} className="mx-2.5">
{visibility ? "활성화" : "비활성화"}
</button>
사실 이전에 이것 말고도 글씨가 바뀌지 않는 문제가 있었지만,
제대로 트러블 슈팅 기록에 남기지 않아 지금은 위만 가지고 설명을 해보려고 한다.
반전된 값을 넣어주었기 때문에 당연히 visibility는 이대로 작성했을 때
순조롭게 작동이 되어야 한다고 예상을 했다.
그러나 나의 의도와는 정반대로 true일 때 비활성화 / false일 때 활성화가 떴다.
중간 과정이 생략되서 아쉽지만 그렇게 바꾸는 과정에서
최초의 문제점을 찾았다.
const newVisibility = !visibility;
클릭 했을 때 반전되게 반전된 값을 변수에 저장해야
반전된 값을 계속해서 만들어낼 수 있는데 이 부분을 미처 생각하지 못했다.
<button onClick={() => handleVisibility()} className="mx-2.5">
{!visibility ? "활성화" : "비활성화"}
</button>
그렇게 하여 visibility가 false 면 활성화 true 면 비활성화 되게 만들었다.
이렇게 boolean타입을 활용해 내가 원하는 의도대로 구현하게 되어
문제를 해결했습니다.
해결하면 바로 위의 3번의 들어갈 내용입니다.
useEffect(() => {
fetchAuthenticated(token);
}, [token]);
현재 로그인 시 토큰을 활용해 ProtectedRoute를 활용해
토큰이 존재할 시 접속가능한 path를 이용하였는데...
useEffect는 한번 렌더링 된 다음에 실행 된다,
SPA처럼 단일 페이지의 경우 렌더링이 다시 되게 하려면 생각을 달리 해야한다...
따라서
위와 같이 로그인은 되어서 Header는 변화되었지만
프로필을 클릭했을 때 위처럼 로그인에 접속되는 현상이 있었다.
즉 useEffect는 현재 초기 값 null의 상태인 것이다.
=> 해결 (2024-11-28)
문제는 useEffect는 렌더링 된 이후에 실행되는데,
위처럼 표현하면 리렌더링은 발생하지 않기 때문에 useEffect는 동작이 되지 않습니다.
그래서 해결한 방법은
const [user, setUser] = useState(null);
state 나 prop가 변경되면 해당 컴포넌트를 리렌더링 합니다.
따라서 상단에 state를 하나 만들어주고,
<Route path="/login" element={<Login setUser={setUser} />}></Route>
이렇게 setUser를 prop로 전달하여 login 페이지에 내려줍니다.
그럼 login에서는 무슨 작업을 해주냐,
바로 내려준 setUser에 값을 넣어서 상태변화가 일어나게 만드는 것,
useEffect(() => {
fetchAuthenticated();
console.log("state 확인");
}, [user]);
그렇게 되면 이제 setUser의 값이 변화하면서 user의 상태가 변경
그렇다면 변화한 값인 user를 의존성 배열에 넣어주고,
user의 값이 변할 때마다 useEffect가 일어나게 수정한 것이다.
즉 상태 변경 -> 컴포넌트 리렌더링 -> useEffect 실행 흐름이 중요하다.