이번 프로젝트 진행 과정에서 좀 난항을 겪은 부분이 있어 블로그에 기술하고자 한다. 두 가지 건으로 좀 시간을 잡아먹었는데 사실 트러블 슈팅이라기 보다는 그냥 개인적으로 겪은 어려움정도라고 보는 게 좋은 듯 하다.
어려움을 겪던 부분은 subCategory select 의 옵션값을 지정해주는 부분이었다. 본래 생각이 없었는데 여기는 재고 추가 버튼으로 여러 개의 생성이 가능한 Stock 컴포넌트의 하위에 포함된 요소다. 그리고 이 각 stock에 맞게 select 가 독립적으로 존재하는데 select 들 끼리 option값을 공유하는 문제였다.
다른 문제 하나는 그냥 단순히 어떤 특정 값 전달에 있어 받아오는 props 에서 null을 받으면서 생기는 사소한 문제였다. 이 부분도 후술할 예정.
문제점 1 - Component의 각 Select 이 Option 값을 공유
Options 는 각 Option 의 상수값들을 import 해와서 이 default를 초기값으로 recoil state에 저장하여 전역상태로 사용하고 있다. 여기서 저 각 options 들은 배열안에 객체들로 존재하는 데이터들이기에 주소값 참조가 이루어져 모든 select가 공유되고 있던 것이다.
이 부분을 해결하고자 굉장히 많이 고민했다. 로직 구상에 대한 어려움이라기 보다는 이렇게 하는게 맞을까? 더 좋은 방법은 없는걸까로 고민한 시간이 더 많았다.
결과적으로는 이 방식을 택했다. 처음에 default 상수값을 가지고 각 객체에 상수가 담긴 length 3의 배열을 생성했다. 그리고 stocks 를 생성할 때 stocks 관련 데이터정보가 담긴 객체에 index 값을 추가하여 후에 options 값을 변경할 때 이 index 값으로 atom의 state에 접근할 수 있도록 하였다.
참고로 저기 deepCopy 는 직접만든 커스텀 유틸함수로, 배열안에 넣은 요소각각을 순서대로 깊은복사 처리 후 반환해 준다. 각 Options는 별개로 인식되어야 하기에 주소값을 독립적으로 재할당했다.
넘겨진 index값은 이렇게 hooks 안의 handleCountChange 에서 기존에 저장되어 있던 값에 접근하기 위해 사용되어진다.
selectOptions 는 isdisabled를 선택된 값에 맞게 다시 재설정한 최종값으로 각 Stocks에 대한 options 들 중 우리가 선택한 stocks의 Index의 값만 바꿔치기하여 적용한다.
+a) 알게된 점 하나 - 리터럴 템플릿을 활용한 form데이터 저장 구조잡기
필자는 React-Hook-Form으로 각 Select의 입력값(선택된 값)을 관리하고 있다. Select는 외부 라이브러리이기 때문에 hook form의 register가 아닌 Controller 에서 직접 name을 지정해서 FormData 에 추가가 가능하다.
stocks 의 경우 각 stocks 에 대해 품목 사이즈, 개수 등의 정보를 구분해서 담아야 나중에 이 값을 불러 사용할 때 어떤 사이즈에 대해 몇 개의 재고가 있다를 인지시켜줄 수가 있다. 그러나 저 name에 그냥 string 값이 들어가면 depth 가 없이 그냥 같은 위치에 key, value로 데이터가 들어오고 있었다.
이 부분을 리터럴템플릿 형식으로 해결가능하다. 이것도 문법적으로 기초적인 부분일텐데 본인은 잘 몰랐다..(구글링하다 알게 됨)
위처럼 작성하게 되면 stocks 라는 key 값으로 배열하나를 만들고 그 안에 내가 지정할 nameValue 가 들어간다. 이후 그 value값을 key로 하는 count value를 .표기법으로 추가해줄 수 있다.
문제점 2 - onchange로 넘겨받는 Options의 Select 값에 접근이 불가능한 문제 (null값 오류)
select.value
에 접근할 수 없다는 에러를 발생시키는 것이다.머리를 싸매고 코드를 훑어보다 찾은 원인은 이렇다. 일단 본인은 Category Select를 main, sub 두개로 사용하고 있다. 이 때 main 카테고리를 눌러 옵션값을 바꾸면 그 아래의 subCategory의 선택값을 초기화시키고자 했다.
이 로직을 구현하기 위해 subRef 로 subCategory를 참조한다음 그 current 값에 접근하여 내부에 정의된 메서드들 중 clearValue로 값을 초기화 시키는 로직을 구현했다.
(참고로 따로 분기를 주었기에 저 clearValue는 현재 선택된 것이 mainCategory일때만 동작한다.)
코드흐름을 따라 정리해본 문제의 원인은 이렇다.
1. 처음 hooks가 실행될 때 내부에 인자로 들어오는 subRef 값은 null이다.(아직 hooks의 로직이 끝나고 props에 ref가 할당되어 전달되기 전)
2. 핸들링 함수가 onChange 이벤트 감지로 실행되는 시점에서의 subRef는 참조를 하고 있다.(초기 렌더링 과정에서 props로 전달이 된 이후 클릭 시 실행되니깐)
3. 이제 클릭을 하면 subCategory의 subRef 객체가 존재함으로 조건문에 따라 선택값을 지워버린다. (Select 값이 지워짐!)
좀 장황하게 설명한 감이 있는데 쉽게말하면 Select 가 공통컴포넌트이고 그에 따라 개별적으로 다른 props 들을 받아 속성에 넣기 때문이다.
현재 main, sub Select는 둘다 같은 위치에서 렌더링되고 있고, 전달하는 props는 subRef 가 null이냐 아니냐 정도의 차이만 있다. 문제는 Selector 컴포넌트의 onChange 에서 받는 Select 도 각각의 Select 의 선택값인데, mainCategory를 선택한 시점에서 subRef.current 객체가 존재하는 상태이기에 그 선택값을 지워버려서 오류가 발생한 것이다.
선택값을 지웠기에 당연히 Selector 에서 이벤트인자로 넘겨주는 Select는 아무것도 없는 빈 값이 될것이고 이 때문에 select.value로 접근이 불가능했던 것이다.