프로그래밍/Frontend (React, Javascript, Typescript)

Fixed Menu, 메뉴 바 상단 고정 구현 시 content에 margin-top을 주면 위험한 이유 (feat. margin collapsing)

Turtle-hwan 2024. 9. 28. 22:30

소개

KUIT 교내 개발 동아리에서 WEB 파트장을 맡아 운영 중이다. WEB 파트 부원들에게 준 2주차 미션이 당근마켓 화면 HTML, CSS로 클론코딩하기였다.

미션 결과물 코드리뷰를 하다 좋은 질문들이 많이 나와 공부가 많이 되었는데, 그 중에 fixed menu, 메뉴 바 상단 고정 구현 할 때 menu bar 아래 content에 margin-top을 주면 위험할수도 있겠다는 생각이 들었고, 그 이유에 대해서도 많이 찾아보게 되어서 글을 써보았다.

코드리뷰 하다 찾게 된 margin collapsing

질문

코드와 함께 보는 것이 편할 수도 있을 것 같아 추가한 github discussion 주소이다.

만들고자 하는 화면은 아래 이미지처럼 당근마켓의 한 화면을 구현하되, 스크롤을 내려도 맨 상단 바가 항상 위에 고정되도록 하는 것이었다. 일반적인 블로그나 사이트에서도 많이 쓰이는 fixed menu 구현이다.

 

질문은 
.main__header 에 top 0px 속성을 주니 content 요소에서 margin,padding 속성으로 위치 조정이 가능한 반면, top 0px 속성을 주지 않았을 때 content 요소에서 margin-top 속성을 주니 fixed를 준 main__header 요소가 밑으로 함께 내려오는 이유를 알 수 있을까요? 였다.

유명 개발자 분이 fixed menu를 구현한 블로그에서도 당연한 듯이 margin이 아닌 padding-top을 사용하시길래 그 이유가 정말 궁금해졌다. 생각보다 이유를 정리한 글을 찾기 힘들었다.

 

해결

결론부터 말하자면 margin-collapsing 이 발생해 body element에 영향을 주었기 때문이다.

먼저, absolute position은 veiwport (브라우저에서 보이는 화면 기준)으로 positioning 된다. 그리고 브라우저에 렌더링 될 때 다음 단계를 거친다.

    1. item-list와 부모 요소 body tag가 margin collapsing이 발생해서 body의 viewport 상 시작 위치가 상단 52px부터가 된다.

 

    1. main__header에 absolute 속성을 붙이더라도 offset(top, left, bottom, right)을 지정하지 않거나 auto로 두면 position이 static일 때의 위치와 일치하도록 offset이 자동 지정된다.

 

  1. static의 기준점은 body 기준이므로, body element의 시작 위치부터 지정되게 된다. 따라서 따로 top:0 속성을 주지 않으면 body와 함께 밀린 상태로 있게 된다.
  2. 문제는 main__header에 top: 0을 줘도 사실상 body에는 margin-collapsing이 일어난 상태가 유지된다!!!!!!!

 

새로운 문제 발생

제목에서 적은 Fixed Menu, 메뉴 바 상단 고정 구현 시 content에 margin-top을 주면 위험한 이유는 바로 위 4번 과정에서처럼 body가 margin-collapsing이 발생해 밀려나기에, header는 따로 top: 0을 주어 맞춘다 하더라도 다른 body 기준으로 배치되는 element들이 이상해질 가능성이 높기 때문이다!!!

그럼 이를 해결하기 위해 어떻게 해야 할까?

처음에는 그렇다면 body에 추가 속성을 줘서 margin-collapsing을 방지하거나, item-list에 margin-top이 아닌, padding-top을 주는 것이 추후 오류 예방을 위해 훨씬 좋을 수도 있을 것이라 답변했다.

padding-top 으로 해결하거나, 헤더 동일 크기의 빈 element를 두어 해결

다른 사이트들을 찾아보니 네이버와 카카오는 padding-top으로 해결하는 것을 볼 수 있었다.

그런데 토스의 구현을 보니까 헤더 크기만큼 빈 요소를 두는 것을 발견했다.

굳이 padding 대신 빈 요소를 두는 이유가 이해가 안 되어 현직자 친구의 정말 깔끔한! 답변을 받았다.

padding 대신 동일 크기 빈 요소를 두는 이유는
"본래 목적에 맞지 않는 영역에 관여하는 것을 방지" 하고,
"padding으로 content에서 top menu까지 신경쓰기보다, 상단메뉴에서 구현할 때 아예 자기 자신의 높이만큼 차지하는 빈요소를 두는 것이 관심사 분리에 더 적합" 하기 때문이라 한다.

내 경험으로도 화면 구현할 때 여러 단계 아래 있는 content에서 padding을 일일히 계산하느라 애먹은 적이 있었고, 특히 반응형을 나중에 마이그레이션 할 때도 어느 padding들이 합쳐진 건지 이전 개발자 분이 적어둔 주석을 보고 겨우 찾아냈던 기억이 떠올랐다. 이를 생각해보면 토스가 관심사 분리 측면에서 정말 좋은 접근법을 고안한 것이라 느꼈다.

 

진짜 좋은 질문이었고 이 덕분에 생각지도 않고 쓰던 것을 한 번 더 돌아보며 자세히 파고들 기회였다.

 

<참고링크>