React/Next.js

[Next.js] :first-child CSS Selector 경고

kicksky 2022. 5. 15. 23:52

Next 환경에서 개발을 하다보면 치명적이지는 않지만 주의하라는 경고들이 가끔 뜨는데 :first-child CSS Selector를 사용하지 말라는 경고도 그 중 하나이다. 저 셀렉터를 수정하지 않는다고 개발 진행이 불가능한 건 아니라서 무시하고 작업을 했다가 코드 리뷰에서 한번 이야기가 나온 적이 있다. 사실 코드 리뷰 받기 전에 다 수정하고 PR 올려야 하는데 내가 놓친 부분이다.

 

아무튼 동료 개발자도, 나도 개발하다가 저 경고를 본 적이 있었고 수정해야 하지않겠냐는 말이 나왔는데 정작 둘 다 왜 쓰면 안 되는지는 정확히 모르고 있어서 같이 찾아봤다. 정확한 답변은 이 링크에서 확인할 수 있었다. 엄밀히 말하면 Next.js 보다는 emotion 쪽 이슈였다.

 

SSR + emotion

정리해보면 Next.js가 서버에서 문서를 렌더링, 즉 노드와 스타일을 그릴 때 emotion 10이 <head>에 스타일 태그를 모아서 그리는 게 아니라 각 컴포넌트에 인라인으로 style 태그를 쓰기 때문이었다. 이렇게 인라인으로 style 태그를 쓸 때 스트리밍이 가능하고 추가적인 설정이 필요하지 않다는 장점이 있지만 nth-child 같은 셀렉터는 사용할 수 없다는 단점이 있다. 해당 링크의 예시 코드에서도 볼 수 있듯이

 

import styledfrom "@emotion/styled";

const Text= styled.p`
  color: gray;
  &:first-child {
    color: black;
  }
`;

export default ()=> (
  <div>
    <Text>Title</Text> /* 둘다 black */
    <Text>Subtitle</Text> /* 둘다 black */
  </div>
);

이 컴포넌트가 다음과 같이 변환된다.

<div>
  <style data-emotion-css="1fyxi0m">
    .css-1fyxi0m {
        color: gray;
    }

    .css-1fyxi0m:first-child {
        color: black;
    }
  </style>
  <p class="css-1fyxi0m">Title</p> /* 얘만 black */
  <style data-emotion-css="1fyxi0m">
    .css-1fyxi0m {
        color: gray;
    }

    .css-1fyxi0m:first-child {
        color: black;
    }
  </style>
  <p class="css-1fyxi0m">Subtitle</p>
</div>

이처럼 스타일 태그 - 엘리먼트 - 스타일 태그 - 엘리먼트 ... 이런 식으로 배치가 되면 원하는 엘리먼트를 first-child가 가리킬 수 없는 경우가 생긴다.

 

서치하다가 styled-components 관련 개발자가 CSS-in-JS에 대해서 더 정확하게 작성한 글이 있어서 설명을 덧붙이자면, "전통적으로는 리액트가 렌더링을 끝낸 이후에 모든 컴포넌트의 스타일이 포함된 <style> 태그를 <head>에 삽입한다. 하지만 스트리밍의 경우에는 컴포넌트가 렌더링 되기 에 <head>가 유저에게 전송된다. 따라서 <head>에 삽입할 수가 없는 것이다. (...) [우리의] 해결 방법은 끝까지 기다려서 모든 컴포넌트를 한 번에 삽입하기 보다, 컴포넌트가 렌더링 될 때 <style> 블록을 HTML 사이에 교차로 끼워넣는 것이다." 앞서도 언급되었지만 스트리밍으로 SSR 하는 케이스를 고려하여 이와 같은 경고를 띄우는 것으로 보인다.

 

first-child 대신 first-of-type

어쨌거나 이 경고를 사라지게 하는 해답은 first-child 대신 first-of-type을 쓰는 것이다. 사실 볼 때마다 헷갈려서 이 셀렉터도 다시 한번 찾아보는 계기가 되었는데, first-child는 형제 요소 중 가장 첫 요소를, first-of-type은 형제 요소 중 자신의 타입과 일치하는 가장 첫번째 요소를 선택한다. 예시로 설명하자면, 

  • div p:first-child 첫번째 자식 요소이면서 p일 때 적용. 첫번째 자식 요소가 div면 적용되지 않는다. 첫번째 자식 && p
  • div p:first-of-type 이 경우 자식 요소 중 첫번째로 오는 p에 스타일이 적용

 

둘이 비슷해보이는데 사용하는 경우가 약간 다르다. 이를테면 전자는 HTML 선택하려는 태그가 첫 번째에 있는지 신경 써야하는 선택자이고, 후자는 무조건 첫번째 요소를 선택하려는 경우에는 맞지 않은 선택자인 것 같다.

 

스트리밍에 대한 의문

어쨌거나 문제는 해결이 되었는데 남은 것은 계속해서 언급되는 스트리밍이 무엇이냐 였다. SSR은 나름 이해하고 있었는데 영상 관련된 키워드로만 알고 있던 스트리밍이 대체 왜 등장하는지 감이 오지 않았다. 이 때부터 SSR, CSR 등을 비롯한 HTML 렌더링에 대한 서치가 시작된다... To Be Continued...