1부에서, 저희는 useRef를 사용해서 탭 메뉴의 전환 효과를 구현하는 방법에 대해서 이야기 해 봤습니다. 아직 안 보셨다구요?
얼른 보고 오세요! → 1부 보러가기
아무튼, useRef를 사용해서 탭 전환 효과를 구현한 것을 열심히 자랑하면서 다니다가, 다음과 같은 질문들을 받았습니다.
그냥 내가 만들었다고...자랑하고 싶었을 뿐인데...🥹
물론 듣고 보니 그렇네? 라는 생각이 들기도 했고, 그래서 다른 방법으로 구현해 보기로 했습니다.
useRef
를 지양하는 이유에는 여러 가지가 있겠지만, 대표적으로 다음과 같은 이유들 때문에 사용하지 않는다고 해요.
직관성 감소: useRef
는 주로 DOM 요소에 직접 접근할 때 사용됩니다. 그러나 React는 주로 선언적인 방식을 통해 UI를 구성하는 데 초점을 맞추고 있습니다. useRef
를 과도하게 사용하면 코드의 직관성이 떨어질 수 있습니다.
리렌더링 제어: useRef
가 가리키는 값은 변경되더라도 컴포넌트를 리렌더링하지 않습니다. 때로는 이것이 바람직할 수 있지만, 상태의 변경에 따라 UI가 바뀌어야 하는 경우에는 useState
나 useReducer
와 같은 다른 훅을 사용하는 것이 더 적절합니다.
사이드 이펙트: useRef
를 통해 직접 DOM을 조작하는 것은 예기치 않은 사이드 이펙트를 초래할 수 있습니다. React의 흐름 밖에서 DOM을 조작하면 예측하지 못한 문제가 발생할 수 있습니다.
재사용성 감소: 컴포넌트에서 useRef
를 사용하여 DOM 요소나 다른 값들에 직접 접근하는 경우, 해당 컴포넌트의 재사용성이 감소할 수 있습니다.
결론적으로, useRef
는 꼭 필요한 경우에만 사용하면 좋다는 거군요. 그렇다면 useRef를 사용하지 않고 탭 메뉴를 구현해 볼까요?
useRef 대신 useState를 사용하는 방법도 있겠지만, 그것보단 CSS만을 사용해서 만드는 게 더 재사용성이 높을 것 같아서 CSS를 사용해서 만드는 방법을 찾아보던 중, 사이드프로젝트를 진행하는 팀원분께서 컴포넌트를 만들어서 공유해 주셨습니다! 🥹
그럼 한번 알아볼까요?
export const StepBar = ({ currentStep, howManyTab }) => {
const marginLeft = `calc((100% / ${howManyTab}) * ${currentStep - 1})`;
return (
<Bar>
<CurrentStepBar style={{ marginLeft, transition: "margin-left 0.5s" }} howManyTab={howManyTab}/>
</Bar>
);
};
const Bar = styled.div`
width: 88%;
height: 5px;
background-color: var(--gray100-color);
margin: 0 auto;
border-radius: 5px;
`;
const barStyles = css<{ howManyTab: number }>`
width: ${(props) => `calc(100% / ${props.howManyTab})`};
height: 5px;
background-color: var(--main-color);
box-shadow: 0 4px 4px rgba(0, 0, 0, 0.3);
border-radius: 10px;
`;
const CurrentStepBar = styled.div<{ howManyTab: number }>`
${(props) => props.howManyTab && barStyles}
`;
카테고리별 항목 아래의 주황색 바는 CurrentStepBar
, 전체 배경이 되는 회색 바는 Bar
컴포넌트입니다.
저희가 알아볼 건 CurrentStepBar
의 위치를 계산하는 방법인데, props부터 살펴 볼게요.
margin-left를 주는 방식을 사용해서, CurrentStepBar
를 처음 위치에서 밀어내도록 해 위치를 지정하게 됩니다.
const marginLeft = `calc((100% / ${howManyTab}) * ${currentStep - 1})`;
그리고 그 margin-left의 크기는 이렇게 calc를 사용해서 결정하게 됩니다.
그렇다면 CurrentStepBar
의 width는 어떻게 결정할까요?
width: ${(props) => `calc(100% / ${props.howManyTab})`};
탭의 개수를 사용해서 등분해 주게 됩니다.
그럼 이제 탭 메뉴까지 잘 만들었으니, 예쁜 결과물이 나오겠죠? 아이 신나 바로 npm run start 해버리기~
나한테 왜 그래!!!
카테고리 메뉴와 CurrentStepBar
가 미묘하게 안 맞는 상황이 발생하고 말았습니다 🥲
문제를 해결하기 위해서 이런 저런 시도를 해 봤는데, 개발자 도구로 확인해 보니 다음과 같은 문제를 찾을 수 있었습니다.
버튼의 크기가 미묘하게 달랐던 것입니다! 생각해 보면, 버튼의 width를 따로 지정하지 않았습니다. 이 상황에서, 제가 해결해야 하는 문제는 두 가지였습니다.
그리고 이 두 가지 문제를 한 번에 해결해 줄 수 있는 속성이 바로 flex-grow
입니다.
사실 조금 더 정확히 말하자면, 단축 속성인 flex를 사용했습니다.
flex CSS 속성은 하나의 플렉스 아이템이 자신의 컨테이너가 차지하는 공간에 맞추기 위해 크기를 키우거나 줄이는 방법을 설정하는 속성입니다. flex는 flex-grow, flex-shrink, flex-basis의 단축 속성입니다.
MDN의 설명에 따르면, 이런 속성이네요. flex 속성에는 한 개에서 세 개의 값을 줄 수 있는데, 숫자 하나면 주면 다음과 같이 설정됩니다:
그래서 button에 flex: 1
속성을 주게 되면, 다음과 같은 결과를 얻을 수 있습니다.
이렇게 해서 탭 메뉴를 구현해 봤습니다. 야호~
앞으로 탭 메뉴를 만들 때, 이런 식으로 구현하면 될 것 같네요!😁
멋진데요