리팩토링 (1) - 손쉽게 자식 div에 CSS 적용하기

cherry_·2023년 9월 5일
0
post-thumbnail

학회에서 진행한 프로젝트 발표 및 부스 운영이 끝나고, 우리 팀은 출시를 위해 나아가고 있다.
이전에는 촉박한 기한을 맞추느라 '정상 동작'에 초점을 맞춰 코드를 짰다면
이젠 정말 '출시'에 초점을 맞춰서 기존 코드 리팩토링 및 최적화를 병행하기로 했다.

오늘 진행할 최적화

[React] 'react-spring-bottom-sheet'과의 전쟁 (feat. Custom CSS)
[끝난 줄 알았지? CSS 중첩 적용 문제 해결] 부분이다.

기존 코드

나는 바텀 시트를 만들기 위해 react-spring-bottom-sheet라는 라이브러리를 사용했다.
이를 커스텀하기 위해 custom CSS라는 자체 css 파일을 만들어서 적용했는데, 기본으로 주어지는 style.css를 복붙+수정한 파일이다.
그러나 react-spring-bottom-sheet를 이용한 바텀 시트가 2군데 이상 존재하자 각각에 적용하던 css 파일 2개가 중첩 적용을 일으켰다.

중첩 적용을 해결하기 위해 각각의 바텀 시트에 className을 부여하려고 했다.
그러나 문제는 react-spring-bottom-sheetportal 형태로 브라우저에 동적으로 생성된다는 것이었다.
createPortal 관련 코드는 모두 라이브러리에 내장되어 있어서 내가 건드릴 수 있는 형태가 아니었다. 그래도 시도해봤지만 장렬히 실패.

<BottomSheet className="homeSheet"> </BottomSheet>

이런 식으로 컴포넌트를 렌더링하는 부분에 className을 적용하면 부모 div에만 적용이 됐다. 자식 div는 className을 이용한 css가 적용되지 않는다는 말씀!

<reach-portal>은 자식이 아주 많기 때문에 css 적용이 안 되면 바텀 시트가 보이지 않았다.

부모 id를 찾아서 자식 div에 className을 적용하면 어떨까?

// setTimeout을 사용하여  portal이 생성된 후 일정 시간 후에 작업 실행
    const timeoutId = setTimeout(() => {
      const parentDiv = document.getElementById("parentDiv-home");
      if (parentDiv) {
        const childDivs = parentDiv.querySelectorAll("div");
        childDivs.forEach((childDiv) => {
          childDiv.classList.add("homeSheet");
        });
        return () => {
          childDivs.forEach((childDiv) => {
            childDiv.classList.remove("homeSheet");
          });
        };
      }
      const element = window.getComputedStyle(
        document.querySelector("--rsbs-overlay-h")
      );

      element.style.setProperty("--rsbs-overlay-h", "100px");
    }, 1);

    // 컴포넌트가 언마운트될 때 clearTimeout으로 타이머 해제
    return () => {
      clearTimeout(timeoutId);
    };
  }, []);

해당 코드를 작성했다. 오늘 리팩토링할 코드이기도 하다.

      const parentDiv = document.getElementById("parentDiv-home");
      if (parentDiv) {
        const childDivs = parentDiv.querySelectorAll("div");
        childDivs.forEach((childDiv) => {
          childDiv.classList.add("homeSheet");
        });

이 부분을 보면 parentDiv-home을 id로 가진 div(부모)를 찾아서 자식 div에 className homeSheet를 넣어주고 있다.

하지만 여기서 2번째 시련을 맞닥뜨린다.

컴포넌트 렌더링 순서 문제

내가 생각한 정상 작동 시나리오는 이랬다.

portal 생성 -> 부모 id 찾기 -> 자식 div에 className 적용 -> css 적용

그러나 실제 순서는 이랬다.

부모 id 찾기 -> 해당 id를 가진 div가 없습니다. -> ERROR!

portal 생성이 내 코드 실행보다 늦었다.
하지만 리액트에서 렌더링 순서를 제어하는 것은 해도 안 되고, 해서도 안 되는 짓이었다.

리액트에서 컴포넌트의 렌더링 순서는 일반적으로 React의 가상 DOM(Virtual DOM) 및 조화 업데이트(reconciliation) 알고리즘에 따라 자동으로 관리됩니다.

되지도 않았고... 건드리면 무슨 문제가 일어날 지 몰랐음.
우선 학회 마감 기한이 있어서, '정상 작동'에 초점을 맞추고 임시방편으로 setTimeout(1) 처리를 했다.
className 적용 코드를 0.001초 대기를 걸어두고, 그 사이에 portal이 렌더링되면 그 이후에 부모 id를 찾아서 ~~~ 하는 식이다.

다행히 현장에서는 잘 돌아갔으나 이 코드로 출시를 할 순 없었다.
내가 생각한 문제는 아래와 같았다.

  • 모든 환경에서 portal이 0.001초 안에 렌더링 된다는 보장이 없다.
  • 비효율적이다. 분명 css 쪽을 건드리면 방법이 있을 것 같았다.

그래서 리팩토링을 진행했다!
학회 발표 이후 합류하신 추가 개발자 팀원이 코드 리뷰를 통해 아주 큰 도움을 주셨다.

수정 코드

없다! 통째로 삭제했기 때문에!
css 쪽을 수정하니까 자식 div에 className을 적용하기 위해 짰던 코드가 필요 없어졌다!
너무 기쁘다,, 이 맛에 리팩토링 하는구나. (setTimeout이라는 청테이프로 우걱우걱 봉합해놓은 게 마음의 짐이었음)

나는 왜 그런 코드를 짰지?

목적을 다시 생각해봤다.
portal 안에 있는 모든 div에 css를 적용하고 싶었기 때문이다.
모든 div! 이게 핵심이었다.


이전 css 코드는 이런 형식으로 아주 길게 생겼다. (대부분 defaultCss를 보존하고 필요한 부분만 찾아서 수정하는 식으로 커스텀했다)
className으로 중첩적용을 방지했기 때문에 .homeSheet가 앞에 있는 걸 볼 수 있다.

수정한 지금은 이렇게 #parentDeiv-home div가 앞에 있는 것을 볼 수 있다.

뭐가 바뀌었다고?

  • .homeSheet -> #parentDeiv-home div

css 선택자에 대한 개념이 부족하고, 뒤에 [data-rsbs-overlay]와 같은 친구들이 붙어 있어서 혼란한 마음에 그 당시에는 사용하지 못했던... ㅇㅏ주 쉬운 방법...... (변명입니다)

.className 과 #id div의 차이

.homeSheet 선택자와 #parentDiv-home div 선택자의 기능적 차이는 무엇일까?

  • .homeSheet 선택자는 class가 "homeSheet"인 모든 요소를 선택한다.
  • #parentDiv-home div 선택자는 id가 "parentDiv-home"인 요소 내에 있는 모든 div 요소를 선택한다.

내가 사용하는 bottomSheet 라이브러리는 (추가 코드 없이는) 부모 div에 id 혹은 className을 적용하는 게 한계였다.
따라서 id가 "parentDiv-home"인 요소 내에 있는 모든 div 요소를 선택하는 두 번째 방법이 정말 알맞았다.

<BottomSheet id="parentDiv-home"> </BottomSheet>

이렇게 하기만 하면 끝!

결론

부모 div를 찾아서 자식 div에 className을 적용하던 코드를 없애고 css 자체에서 해당 id를 가진 모든 div에 적용하는 식으로 바꿨다.
css 자체에서 id 찾기 -> 이 id의 모든 하위 div에 css 적용을 해주니 당연히 내가 짠 기존 코드는 필요 없을 수 밖에.
더이상 portal 렌더링 순서를 신경쓸 필요도 없다!

css 기초를 좀 더 다지자.
기본 베이스와 기능을 알면 돌아갈 필요가 없다는 걸 절절하게 깨달은 날이었다.,,

0개의 댓글