vue.js로 영화 검색 사이트 구현에 있어 css 스타일 작업을 진행하고 있었다.
초기 작업에서 body 전체를 감싸는 wrap 클래스의 div를 감싸고 그 자식 요소에 상위 마진 값을 주게 되면 wrap으로 부터 마진이 발생하는 것이 아닌 부모 요소 wrap의 div요소가 body태그로 부터 상위 마진 값이 적용되는 문제였다.
처음에 내가 마진에 대해 이해가 부족했나...? 생각해봤지만 마진 속성을 사용하면서 이런 경험을 처음이였다.
문제의 상황이다. 마진 값이 box에 적용되는 것이 아니라 부모 요소 wrap에 적용되어 body로 부터 멀어지는 현상이다.
그래서 같은 팀원인 재영님께 이 현상을 질문했고 마진 상쇄라는 키워드를 얻게되었다.
상당히 당황했던 나여서 확실하게 알고 다음에는 반복되지 말자!라고 생각하여 정리하기로 한다.
뭐 마진 상쇄, 여백 상쇄라고도 불리는데 공식문서에서는 이렇게 정의한다.
여러 블록의 위쪽 및 아래쪽 바깥 여백(마진)은 경우에 따라 제일 큰 여백의 크기를 가진 단일 여백으로 결합(상쇄)되곤 합니다. 이런 동작을 여백 상쇄라고 부릅니다. 단, 플로팅 요소와 절대 위치를 지정한 요소의 여백은 절대 상쇄되지 않습니다.
정리하자면 특정한 조건에 따라 두 개의 Block 요소의 마진 Top, Bottom 중 제일 큰 마진 값으로 결합되는 현상이다. 하지만 float 속성, postion 속성을 지정한 요소의 마진은 절대 결합 되지 않는다.
핵심은 특정 조건에서 마진이 합쳐지는 현상이다. 일단 마진 상쇄의 조건을 살펴보자.
box1은 아래로 마진 100px, box2는 위로 마진 50px 그러면 box1과 box2는 총 150px 많큼 마진 값이 발생해야 하지만 마진이 사이에 경계가 없어 가장 큰 마진 값인 100px 만큼만 적용된 모습이다.
이 조건이 내가 마주한 문제와 동일했다.
부모 블록 태그와와 자식 블록태그가 있을때 두 박스 사이에 다른 요소(예: 텍스트나 이미지 등의 인라인 콘텐츠, 패딩, 테두리 등)가 없다면 부모와 자식 사이 가장 큰 마진 값으로 상쇄된다.
즉, 부모 박스와 자식 박스 사이에 아무것도 없다면, 부모와 자식의 마진이 합쳐져서 하나의 마진으로 처리된다.
부모 wrap 블록 태그와 자식 box 블록 태그 사이에는 아무런 경계 즉, 인라인 요소나, 패딩, 테두리가 없다.
wrap의 마진 값은 0이고 box의 마진 값은 50px이며 더 큰 마진 값 50px이 wrap과 box에 상쇄되어 부모의 밖으로 여기선 wrap 밖으로 마진이 50px 적용된다.
만약 부모와 자식사이에 인라인 태그를 하나 추가했다. 그럼 부모와 자식사이에 마진 상쇄 조건이 적합하지 않아 마진 상쇄가 발생하지 않았다.
추가적인 상쇄 방지 방법은 아래에서 설명하겠다.
빈 블록 요소란 내부에 아무런 내용이 없는 상태다. 이 경우, 요소의 높이가 0인데 height, padding, border 등 요소의 높이를 결정하는 프로퍼티 값이 없거나, 내부에 inline 요소가 없어서 높이가 0이 되는 경우다.
이런 빈 블록 요소에서는 상단과 하단 마진 사이에 실질적인 경계가 없다. 따라서 상단 마진과 하단 마진이 서로 겹치게 되면, 두 마진 값 중에서 더 큰 값이 적용된다.
box2가 우선 높이가 0인 빈 블록 요소다.
먼저 box2의 기준에서 자기 자신의 마진이 상 50px, 하 70px으로 만나게되어 상쇄가 발생해 70px이 적용되었다.
box1의 하단 마진은 30px box2의 상단 마진은 70px이며 실제로 마진이 70px로 상쇄되는 모습이다.
다음 box2의 하단 마진은 70px box3의 상단 마진은 20px 빈 블록 요소의 조건에 의해 더 큰 마진인 70px이 적용되었다.
마진 상쇄는 인접한 두 요소가 블록 요소일 경우에만 적용된다.
마진을 지정하지 않거나 0으로 지정해도 상쇄 될 수 있다.
좌, 우 마진은 적용되지 않고 상, 하 마진만 적용된다.
마진 상쇄를 정리하다보니 핵심 부분은 브라우저에서 마진 영역과 마진 영역이 경계 없이 만나는 부분이거나, 곂치는 영역에서 규칙과, 조건이 일치하면 상쇄 현상이 발생한다.
이러한 마진 상쇄를 해결하는 방법을 알아보자면
부모 요소의 상, 하에 padding 값을 준다.
부모 요소의 상, 하에 border 값을 준다.
부모 요소에 overflow: hidden 값을 준다.
부모 요소에 dispaly: flex 값을 준다.
요소와 요소 사이에 inline 요소의 경계를 준다.
요소에 position: absolute /fixed 값을 준다. 하지만 스타일 제어에 영향을 주기때문에 추천하지 않는다.
요소에 float: left/right 값을 주는데 clear 되지 않은 상태여만 하고 이것도 스타일 제어에 영향을 주니 추천하지 않는다.