사내 프로젝트를 리엑트로 만드는 중에 생긴 이슈와 관련되어 정리한 내용입니다.

현재 프로젝트 상황은 아래와 같다

 

  • 서버 데이터를 생성/수정/삭제 하는 CRUD 중 CUD 기능은 크게 사용 X
  • 대부분 Read 기능 요구이며 전달 값 (param) 에 맞게 다른 Read 요청 필요
  • 에러 핸들링은 서스팬스와 에러 바운더리 통해서 처리
  • tanstack query (react query) 는 v5
  • useQuery , useSuspenseQuery 사용 하는 훅 생성 중

이슈 1.

이런 환경 속에서 page 컴포넌트에 최초 마운트 되었을 때 필요에 따라 쿼리를 돌릴지 말지 정해야 할 필요가 있는데 useQuery의 경우 enabled 옵션을 false 하면 되지만 서스펜스를 이용하기 위하여 사용 하는 useSuspenseQuery 의 경우에는 마운트 되버리면 실행 되는 상태입니다.

 

// 커스텀 훅
const useLogin = (param) => {
	
	const fetchData = (param) => {
		//fetch 처리
	}
	
	//query 방식
	const {data1} = useQuery({
		queryKey: ['login', param],
		queryFn: () => fetchData(param),
		enabled: false
	});
	
	//suspense 방식
	const {data2} = useSuspenseQuery({
		queryKey: ['login', param],
		queryFn: () => fetchData(param),	
	})
	
	return {data1, data2}
};


// 실제 페이지

const Page = () => {
	const [test, setTest] = useState({});
	const {data} = useLogin(test);
	
	const handleClick = (e) => {
		// 이래저래 이벤트 처리 후 obj 객체 생성하여
		setTest(obj);
	}
	
	return (
		<>
			<button onClick={handleClick}>클릭</button>
			로그인 여부 :{data.isLogin}
		</>
	)
	
}

 

 

이슈 2

위 작업 중에 fetch 에러가 발생하여 throw Error 를 할 경우 에러바운더리로 던져지는 작업 하고 싶은데 실제로 에러 바운더리로 던져지긴 하지만 에러 로그가 2번 이상 발생 되는 상황이 발생 됩니다.

 

const fetchData = (param) => {
	return await axios.get('/',param)
	.then((res) => { return res})
	.catch((err) => { throw err});
}


	//query 방식
	const {data1} = useQuery({
		queryKey: ['login', param],
		queryFn: () => fetchData(param),
		enabled: false
	});
	
	//suspense 방식
	const {data2} = useSuspenseQuery({
		queryKey: ['login', param],
		queryFn: () => fetchData(param),	
	})

 

이런식으로 코드를 작성하면 query 에서는 queryFn 에서 return 되는 data 값이 없다고 에러 1번 발생 되고 fetchData 에서 발생 된 throw 관련 에러도 발생 되면서 필요 없는 에러가 여러번 발생 되고 있습니다 . 

 

 


 

이슈에 대한 생각

이곳 저곳 커뮤니티나 다른 개발자 분들에게 질문 외에도 래퍼나 내가 놓친 것이 없는지 다시 되돌아 공부해보면서 나온 결과입니다.

 

이슈 1.

먼저 이슈 1의 경우에 react의 suspense 목적은 이해하고 있었다고 생각했지만 제대로 이해하지 못한 상태로 사용한 문제로 생긴 이슈입니다.

 

애초에 suspense는 fetching 하는 동안 특정 UI 화면을 보여주기 위한 목적으로 사용 되어야 하는데 저는 에러 바운더링을 사용하기 위해서는 꼭 사용해야하는 기능이라고 생각하여 사용하였고, 그로 인하여 react-query 내장 함수 중 useSuspenseQuery 를 사용하게 된 것입니다.

 

개발 의도에 따르면 최초 마운트 되었을 때 실행 처리를 다르게 해야 하는데 무조건 실행 되어야하는 suspense 기능 때문에 훅이 자동으로 돌게 되는 것...

 

그렇기 때문에 개발 의도에 맞게 사용하려면 useSuspenseQuery가 아닌 useQuery 를 사용해야 하고, 옵션 (enabled : false 또는 useRef로 마운트 못하게 하기...) 등을 통해서 분기를 생성 해야 한다는게 결론입니다.

 

이슈 2.

 

사실 에러 관련 된 내용은 서스팬스 작업 때 발생 되는 오류들 중 하나라서 크게 이슈 될 문제는 아니라고 하는 피드백이 대부분이였지만... 해결책을 찾지는 못했다 ... (이럴꺼면 애초에 이슈라고 남기지도 말껄  ㅠ ..)

 

추가적으로 리엑트 쿼리나 리엑트에 대해서 위 이슈 같은 내용들이 나올 때마다 기록을 남겨두는 습관을 만들 계획이다

 

 

리엑트를 공부하면서 (인강... 레퍼런스 문서 등등) 최근에 왜 함수형 컴포넌트가 클래스 컴포넌트보다 대세가 되었는지 생각해보았습니다... 지극히 개인의 의견 !!

 

먼저 Hook의 도입이 클래스 컴포넌트가 갖고 있던 기존의 문제들을 어떤 식으로 해결 했는지 이해하면 좋을것 같다.

 

대표적인 예로 클래스 컴포넌트들은 대부분 UI를 레너링하는 로직(이벤트 리스너 로직..) 과 내부 데이터 (상태/생에 주기) 를 처리하는 로직을 따로 떼기 어려워 코드가 많이 복잡하여 가독성이 떨어집니다. 그래서 함수형 컴포넌트에 비해 버그도 빈번히 발생되고 유지보수도 떨어진다고 생각함...

 

이런 문제가 있었지만도 기존 클래스 컴포넌트 사용이유는 함수형 컴포넌트의 기능이 상대적으로 많이 제한 되었기 떄문인데 단순한 UI를 보여주는 것 외에 상태관리 , 컴포넌트 생애주기, 하위 컴포넌트 props 전달 같은 기능은 모두 클래스 컴포넌트에서 가능하였습니다.

 

그러나 Hook이 도입 되면서 useState, useEffect를 이용하여 함수형 컴포넌트 내부에서 상태 및 컴포넌트 생애주기도 관리 할 수 있으며 , 클래스 컴포넌트가 할 수 있던 작업들을 보다 쉽고 간결하게 할 수 있다는 것입니다.

 

또한 Hook을 통하여 컴포넌트 내부 데이터 처리 로직을 컴포넌트에서부터 따로 분리 시킬 수 있게 되면서 코드 재사용성이 많이 높아진것 뿐만 아니라 개발자들이 ui 렌더링 로직과 데이터 처리 로직도 따리 분리해서 생각할 수 있게 된 점이 중요합니다. (ex.. 커스텀 훅으로 data fetching 같은 로직을 따로 빼서 관리) 덕분에 디자인 수정할 경우 ui 로직만 손보면 되다보니 클래스 컴포넌트보다 사용하기 편해짐

 

이를 통하여 hook 을 통한 상태로직 재사용이 쉬워지면서 이를 기반으로한 라이브러리 (react-query 등) 이 개발 되며 점점 함수형 컴포넌트 로 사용하는 방향이 되지 않았나... 생각합니다

 

 

 https://ko.reactjs.org/docs/hooks-intro.html#motivation

'Front-end > react' 카테고리의 다른 글

react-query 개발 시 이슈 정리  (1) 2024.07.11

+ Recent posts