3월 동안 한 주요 업무는 (1) 필터 구조 변경, (2) 홈 피쳐 갈아 엎기, (3) 슬라이더 적용 이렇게 3가지로 볼 수 있을 것 같다. 추가적으로는 늘하고 있는 크롤러 추가 및 버그 수정, 자잘한 ui관련 업무를 했다.
기존 필터는 필터라는 버튼을 클릭하면 백드롭이 나오고 사용자가 필터를 변경하고 확인을 누르면 필터가 적용되는 방식으로 필터가 구현되어 있었다.
운영팀에서는 필터의 접근성을 높이기 위해 지그재그와 브랜디와 같은 칩형태로 필터 디자인을 변경하기로 결정했고, 이번 기회로 여러 어플에서 통용적으로 자주쓰이는 칩형태의 필터를 구현 해 볼 수 있었다!
이번 업무는 지금까지 구현했던 자잘한 기능들에 비해 기존 필터 구조를 크게 변경할 수도 있다는 점에서 도전 과제로 느껴지기도 했다. 하지만 저번 달에 슈퍼어드민에 필터 구조 설계를 해보았기 때문에 이번에는 좀 더 자신감을 가지고 차근차근 구현한 것 같다.
(1) 칩 컴포넌트 추가하기
칩 컴포넌트(Chip)는 칩모양을 가진 가장 base단계의 기본 컴포넌트다. 단순 선택/미선택 기능만 가진 칩과 x버튼을 통해 특정 이벤트를 수행하는 칩에 모두 대응이 가능하도록 했다.
(2) ListFilter 컴포넌트 재구성
ListFilter 컴포넌트는 withListFilterContext라는 HOC으로 감싸 사용된다. 적용된 필터와 (아직 적용되지 않은) 임시 필터의 상태나 필터를 변경하는 액션 등(filter, tempFilter, onFilterChange ...)은 필터 안의 칩 필터, 백드롭, 백드롭 내부의 필터 등의 여러 컴포넌트드로 부터 전역적(globally)으로 공용된다.
이러한 경우 state를 자식과 그 자식의 자식 컴포넌트들에 prop으로 넘겨주는 것보다 context를 사용하여 데이터를 제공하는 것이 효율적이다. 따라서 hoc이 context의 provider역할로 사용되고 있다.
ListFilter 마크업 구조
state: open(true면 백드롭이 열림), current(백드롭이 열렸을 때 가리키는 탭)
(3) bodyFilters와 inputChips만들기
bodyFilters
: 해당 폴더는 bodyFilter에 해당하는 필터 컴포넌트들을 그룹핑한다. 우리 앱에서는 카테고리 필터, 스타일 필터, 키 필터, 가격 필터 등을 제공한다. 카테고리 필터와 스타일 필터는 각각 다른 마크업이기 때문에 다른 컴포넌트로 작성했고, 키필터와 가격 필터는 마크업과 로직이 일부 겹치므로 base컴포넌트로 RangeFilter를 만들어, 컴포넌트를 재사용할 수 있도록 했다.inputChips
: inputChips폴더는 리스트에서 보이는 각각의 필터 칩 컴포넌트들을 그룹핑한다. 스타일, 카테고리, 키 등의 각각의 inputChip들은 특정 상태가 적용되었을 때 selected 상태가 변한다. 예를들어 스타일 필터칩은 한개 이상의 스타일 url query가 적용되었을 때, 키 필터칩은 min과 max 키 중 하나라도 query가 적용되었을 때 선택된 상태가 되어야 한다. 따라서 BaseInputChip을 만들었고, checkSelected(언제 selected 상태가 되어야 하는지 검사하는 함수), formatInputLabel(필터 query에 따라 변경되어야 하는 라벨 text를 반환하는 formatting 함수), queryParamsNames(해당 필터가 적용되었을 때 변경되는 query 이름 배열) 등을 prop으로 전달받는다.(4) 정렬필터 만들기
무튼 여기까지가 대강 어떻게 내가 칩필터를 구현했는지에 대한 내용이다. (아직 설계를 먼저 탄탄히 한 후 구현하는 것이 습관이 안되어서 코드를 갈아엎기도 했지만..ㅎ) 매끄럽게 작동하는 칩필터를 적용해보면서 엄청 뿌듯했다. 더불어 코드리뷰에서 구조 칭찬을 받아서 눈물나게 기뻤다 😂
javascript는 이벤트가 전파(propagation)된다는 특성이 있다.
stopPropagation은 이벤트 캡쳐링과 버블링에 있어 현재 이벤트 이후의 전파를 막는다.
위 칩필터의 경우처럼 Wrapper를 클릭했을 때 handleButtonClick이 실행되어야하고 CloseButton을 클릭했을때 handleCloseClick가 실행되어야한다고 하자.
<Wrapper onClick={handleButtonClick}>
<P>
카테고리
</P>
<CloseButton
onClick={(e) => {
e.stopPropagation();
handleCloseClick(e);
}}
/>
</Wrapper>
이때 CloseButton의 onClick에서 stopPropagation을 호출하지 않는다면, CloseButton을 눌렀을 때 이벤트 버블링에 의해 CloseButton의 부모인 Wrapper의 이벤트인 handleButtonClick 또한 실행되고 만다!! 따라서 이를 막기 위해서는 e.stopPropagation()를 호출하여 이벤트의 전파를 막아야 한다.
2-1. 이미지 투명하게 하기 (흰색 투명 배경이 올라온 것 처럼)
이런경우 mix-blend-mode없이 img의 opacity
만 조정해주면 된다!
2-2. 이미지 위에 검은 투명 배경 올리기
container의 배경을 black으로 설정해주고 mix-blend-mode없이 img의 opacity
만 조정해주거나
container의 배경을 rgba(0,0,0,0.3)과 같이 투명도가 있게 설정해주고 img에 mix-blend-mode: multiply;
속성을 주면 된다!
2-3. 이미지 위에 그라데이션을 덮을 수도 있다.
background: linear-gradient(#ffffff, rgba(0, 0, 0, 0.72));