Chrome grid 결함과 flexbox로 돌아선 날, 거짓 PASS의 함정

26년 06월 17일

footer가 화면 바닥에 안 붙는다

이날은 CSS 레이아웃 버그와 5번 싸운 날이다.

footer가 화면 바닥에 안 붙었다.

grid로 잡았는데 대화면에서만 실패했다.

소화면에서는 통과한 줄 알았다가 거짓 PASS였다.

결국 flexbox로 전환해서 해결했다.

교육 플랫폼에서 footer 레이아웃 버그가 있었다.

섹션을 grid로 잡았다.

첫 번째 행은 내용이고 두 번째 행은 footer다.

첫 번째 행이 남은 공간을 다

차지해야 footer가 바닥에 붙는다.

근데 대화면에서 footer 아래에 635픽셀 빈 공간이 생겼다.

footer가 바닥에 안 붙고 중간에 떠 있다.

grid minmax가 안 먹는다

CSS 스펙상 minmax(0,1fr)은 첫 번째 행이 남은 공간을 전부 차지해야 한다.

min이 0이니까, 행이 필요한 만큼 늘어난다.

footer 행은 자기 높이만 차지한다.

이래야 footer가 바닥에 붙는다.

근데 Chrome이 이걸 안 지켰다.

고정 높이랑 중첩 grid랑 임베드 컨텍스트가 합쳐지니까, 첫 번째 행을 min-content로 고정해버렸다.

남은 공간을 흡수하지 않는 거다.

footer 행에 715픽셀이 배당됐다.

footer 엘리먼트는 80픽셀인데. overflow

추가해봤다.

안 됐다.

track sizing이 안 바뀐다.

실측으로 확인했다.

소화면에서는 통과한 줄 알았다

더 문제는 검증이었다.

소화면에서는 버그가 안 보였다.

footer 아래에 빈 공간이 없었다.

그래서 “해결됐다”고 판단했다.

근데 대화면에서 같은 페이지를 열어보니 빈 공간이 있었다.

소화면에서는 섹션 높이가 작아서 footer 행이 자기 높이만 차지했다.

빈 공간이 안 생기는 거다.

대화면에서는 섹션 높이가 커져서 빈 공간이 드러난다.

이게 거짓 PASS다.

소화면에서만 검증해서 “해결됐다”고 믿은 거다.

레이아웃 버그는 대화면에서 검증해야 한다.

대화면에서 빈 공간이 가장 크게 드러나니까.

flexbox로 전환

결국 grid를 포기하고 flexbox로 전환했다.

섹션을 flex column으로 만들었다.

내용 영역에 flex

min-height
줬다.

footer에 shrink

줬다.

flexbox는 이 조합을 신뢰성 있게 존중한다.

내용이 남은 공간을 다

차지하고 footer가 자기 높이만 유지한다.

Chrome이 grid에서는 track sizing을 안 지켰지만 flexbox에서는 비율을 지킨다.

진단 방법

이 버그를 진단하는 결정적 증거를 찾았다.

grid 행 높이를 실측한다.

getComputedStyle으로 행 높이를 읽는다.

footer 행이 footer 엘리먼트 높이보다 크면, grid가 제대로 sizing을 안 한 거다.

이 방법이 확실하다.

“빈 공간이 있나”를 눈으로 보는 것보다 정확하다.

왜냐면 눈으로는 grid 행 경계를 못 본다.

근데 행 높이를 읽으면, footer 행에 715픽셀이 배당됐다는 걸 수치로 확인할 수 있다.

JSONL 충돌 병합

CSS 버그 외에도 깃 충돌 병합에서 일반적인 교훈을 찾았다.

append-only JSONL 파일이 충돌했다.

줄 단위로 diff를 보고 병합하려고 했다.

근데 JSONL은 순서가 의미를 갖는다.

줄 단위 diff는 무의미하다.

올바른 병합은 타임스탬프 기준 합집합이다.

양쪽의 엔트리를 모으고 타임스탬프로 정렬하고 같은 타임스탬프는 중복 제거한다.

이렇게 하면 양쪽이 신뢰 원천인 append-only 로그를 올바르게 병합할 수 있다.

부재도 증거다

마지막으로 하나 더. 확장이 실행됐는지 확인할 때, 부재도 증거라는 걸 배웠다.

fire-and-forget로 실행되는 확장이 있었다.

트리거 조건을 만족하는데 실행 부산물이 전혀 없었다.

clientId도 안 바뀌고 콜백 서버도 없고 로그도 없다.

“조건을 만족하는데 왜 안 되지”라고 생각할 수 있다.

근데 부산물이 없다는 건, 실행 자체가 안 됐다는 증거다.

조건을 만족해도 확장이 로드되지 않으면 실행이 안 된다.

부재도 증거다.

“실행됐을 부산물이 없다”는 “실행이 안 됐다”의 증거가 된다.

“조건이 만족되면 실행된다”는 가정이 틀릴 수 있다.

조건 이전에 로드 자체가 안 됐을 수 있다.

마무리

내가 이날 배운 핵심은 다음과 같다.

이날의 교훈은 세 가지다.

첫째, 레이아웃 버그는 대화면에서 검증해야 한다.

소화면에서 통과하면 거짓 PASS다.

빈 공간은 대화면에서 가장 크게 드러난다.

둘째, grid가 안 되면 flexbox로 가라. grid의 minmax(0,1fr)은 스펙상 맞지만 Chrome이 특정 컨텍스트에서 안 지킨다.

실측으로 track sizing이 안 바뀌면 flexbox로 전환하는 게 빠르다.

셋째, 부재도 증거다.

실행됐을 부산물이 없으면, 실행이 안 된 거다.

조건이 만족되어도 로드가 안 됐을 수 있다.

부재를 “아직 안 봤다”가 아니라 “없다”로 읽어야 한다.