IE6 호환 CSS — 조건부 주석과 핵의 고고학

IE6 호환 CSS — 조건부 주석과 핵의 고고학

"이 레이아웃 왜 IE에서만 깨지죠?" — 2000년대 프론트엔드 개발자의 매일 아침 인사


2001년에 출시된 Internet Explorer 6는 약 5년간 브라우저 시장의 90% 이상을 독점했음. 문제는 IE6의 CSS 렌더링 엔진이 W3C 표준을 자기 마음대로 해석했다는 것임. CSS 표준을 따르면 Firefox에서는 정상인데 IE에서는 깨지고, IE에 맞추면 Firefox에서 깨지는 "브라우저 전쟁"이 시작됨.

이 전쟁에서 프론트엔드 개발자들은 온갖 기상천외한 "핵(hack)"을 발명했음. 조건부 주석, star hack, underscore hack, holly hack... 지금 보면 전쟁의 흉터 같은 것들이지만, 당시에는 이것 없이는 웹사이트를 만들 수 없었음.

IE6 박스모델의 공포

CSS 역사상 가장 유명한 버그. W3C 표준과 IE의 박스모델이 달랐음:

css
/* W3C 표준 (Firefox, Chrome, Opera 등) */
/*
 * width = 콘텐츠 영역만
 * 총 너비 = width + padding + border
 *
 * width: 300px, padding: 20px, border: 1px이면
 * 총 너비 = 300 + 20*2 + 1*2 = 342px
 */

/* IE6 쿼크 모드 */
/*
 * width = 콘텐츠 + padding + border 포함
 * 총 너비 = width 그 자체
 *
 * width: 300px, padding: 20px, border: 1px이면
 * 콘텐츠 영역 = 300 - 20*2 - 1*2 = 258px
 * 총 너비 = 300px
 */
아이러니한 반전

사실 IE6의 박스모델이 더 직관적이었음. "width를 300px로 설정했는데 실제로는 342px를 차지한다"는 W3C 표준이 오히려 혼란스러운 것이었음. 그래서 CSS3에서 box-sizing: border-box가 도입됐는데, 이건 사실상 IE6의 박스모델을 표준으로 채택한 것임. IE6가 결국 이긴 셈.

이 박스모델 차이를 해결하기 위한 핵:

css
/* Tantek Celik의 박스모델 핵 (2002년) */
.box {
  width: 300px;         /* IE6 쿼크 모드용 */
  voice-family: "\"}\""; /* IE6는 여기서 파싱을 멈춤 */
  voice-family: inherit;
  width: 258px;         /* 표준 브라우저용 (300 - 20*2 - 1*2) */
  padding: 20px;
  border: 1px solid #ccc;
}

/* "be nice to Opera 5" 핵 */
html>body .box {
  width: 258px;
}

ㅋㅋㅋ voice-family라는 CSS 속성을 악용해서 IE6의 CSS 파서를 속이는 기법임. IE6가 "\"}\""를 만나면 선언 블록이 끝났다고 착각해서 그 뒤의 width: 258px를 무시함. 미친 거 아닌가 싶지만 이게 당시의 현실이었음.

조건부 주석 — IE 전용 우편함

Microsoft가 IE의 차이점을 인정하고 제공한 "공식" 해결책:

html
<!-- 모든 IE -->
<!--[if IE]>
  <link rel="stylesheet" href="/css/ie-all.css" />
<![endif]-->

<!-- IE6만 -->
<!--[if IE 6]>
  <link rel="stylesheet" href="/css/ie6.css" />
<![endif]-->

<!-- IE7만 -->
<!--[if IE 7]>
  <link rel="stylesheet" href="/css/ie7.css" />
<![endif]-->

<!-- IE8 이하 -->
<!--[if lte IE 8]>
  <link rel="stylesheet" href="/css/ie8-and-below.css" />
<![endif]-->

<!-- IE가 아닌 브라우저 -->
<!--[if !IE]><!-->
  <link rel="stylesheet" href="/css/modern.css" />
<!--<![endif]-->

<!-- IE6에서 PNG 투명도 지원 (ㅋㅋ) -->
<!--[if IE 6]>
  <script src="/js/DD_belatedPNG.js"></script>
  <script>DD_belatedPNG.fix('img, .png-fix');</script>
<![endif]-->
CSS 파일 지옥

대형 프로젝트에서는 main.css, ie6.css, ie7.css, ie8.css, print.css를 모두 관리해야 했음. CSS를 하나 수정하면 4개 파일을 전부 확인해야 하는 유지보수 지옥. "이 스타일 왜 안 먹히지?" → IE 전용 파일에서 오버라이드하고 있었음.

CSS 핵 컬렉션 — 고고학 유물 목록

2000년대 프론트엔드 개발자의 무기고:

css
/* ===========================
   Star Hack — IE7 이하 전용
   =========================== */
.element {
  color: blue;     /* 모든 브라우저 */
  *color: red;     /* IE7 이하만 적용 (속성 앞에 *) */
}

/* ===========================
   Underscore Hack — IE6 전용
   =========================== */
.element {
  color: blue;     /* 모든 브라우저 */
  _color: red;     /* IE6만 적용 (속성 앞에 _) */
}

/* ===========================
   IE7 전용 */
.element {
  color: blue;
  *color: green;   /* IE7 이하 */
  _color: red;     /* IE6만 (더 구체적) */
}
/* IE7에서는 green, IE6에서는 red */

/* ===========================
   !important 핵
   =========================== */
.element {
  color: blue !important;  /* 표준 브라우저 */
  color: red;              /* IE6 (!important 무시하는 버그) */
}

/* ===========================
   자식 선택자 핵
   =========================== */
.parent > .child {
  /* IE6는 > (자식 선택자)를 이해 못 함 */
  /* 그래서 이 규칙은 IE6에서 무시됨 */
  color: blue;
}

/* ===========================
   속성 선택자 핵
   =========================== */
.element[class] {
  /* IE6는 속성 선택자를 모름 */
  color: blue;
}
핵의 계보학

CSS 핵은 브라우저의 CSS 파서 버그를 이용한 것임. *이나 _가 붙은 속성명은 원래 유효하지 않은 CSS인데, IE가 관대하게 해석해서 적용함. 표준 브라우저는 유효하지 않은 CSS를 무시하기 때문에 IE에서만 적용되는 스타일을 만들 수 있었음. 우아하지는 않지만, 효과적이었음.

hasLayout — IE의 숨겨진 보스

IE6/7에서 가장 이해하기 어려운 개념. IE에는 hasLayout이라는 내부 속성이 있었는데, 이 값이 true인 요소만 제대로 렌더링됨:

css
/* hasLayout을 트리거하는 속성들 */
.fix-ie {
  /* 방법 1: zoom (IE 전용 속성) */
  zoom: 1;

  /* 방법 2: width나 height를 지정 */
  width: 100%;
  /* 또는 */
  height: 1%;  /* IE6에서 overflow: hidden과 함께 사용 */

  /* 방법 3: display: inline-block */
  display: inline-block;
  /* 근데 IE6에서 block 요소에는 안 먹혀서... */
  /* 이렇게 해야 함: */
  display: inline-block;
  *display: block; /* star hack으로 IE7 이하에서 다시 block으로 */
}

hasLayout이 없으면 발생하는 문제들:

css
/* 문제 1: float 해제(clearfix)가 안 됨 */
.container {
  /* IE6에서 자식의 float가 부모를 벗어남 */
  /* 해결: hasLayout 트리거 */
  overflow: hidden;
  zoom: 1; /* IE6/7 용 */
}

/* 문제 2: margin collapse가 이상하게 동작 */
.parent {
  background: #eee;
  zoom: 1; /* hasLayout 트리거하면 margin collapse가 멈춤 */
}

/* 문제 3: 배경색이 안 나옴 */
.box {
  background: #f00;
  /* IE6에서 hasLayout이 없으면 배경이 안 보이는 버그 */
  zoom: 1;
}
zoom: 1 만능설

IE6/7에서 레이아웃이 깨지면 일단 zoom: 1을 넣어보는 것이 2000년대 프론트엔드 개발의 기본 디버깅 패턴이었음. 원리를 이해하지 못해도 "일단 넣으면 고쳐진다"는 경험적 지식이 전승되었음. 마치 주술과 같은... 근데 진짜 됐음.

클리어픽스 전쟁

float 레이아웃에서 부모 요소가 자식 float 요소의 높이를 인식하지 못하는 문제. 이걸 해결하기 위한 방법이 시대별로 진화했음:

css
/* 방법 1: 빈 div (초기) — HTML을 더럽힘 */
/* <div class="container">
     <div class="float-left">...</div>
     <div style="clear: both;"></div>
   </div> */

/* 방법 2: overflow: hidden */
.container {
  overflow: hidden; /* float 해제 + 내용이 넘치면 잘림 */
  zoom: 1;          /* IE6 hasLayout */
}

/* 방법 3: Micro Clearfix (Nicolas Gallagher, 2011) */
/* 최종 진화형 — 이것이 표준이 됨 */
.clearfix::before,
.clearfix::after {
  content: " ";
  display: table;
}
.clearfix::after {
  clear: both;
}
/* IE6/7 용 */
.clearfix {
  *zoom: 1;
}

/* 방법 4: 그냥 Flexbox 쓰면 됨 (현대) */
.container {
  display: flex;
  /* 끝. float? clearfix? 그런 거 없음 */
}
float 레이아웃의 종말

Flexbox와 Grid의 등장으로 float 레이아웃은 역사 속으로 사라졌음. clear: both, clearfix 같은 패턴은 이제 레거시 코드에서만 볼 수 있음. 현대 CSS에서 float는 원래 목적인 "텍스트 옆에 이미지 배치"에만 사용됨.

PNG 투명도 — IE6의 치명적 약점

IE6는 PNG 24비트의 알파 투명도를 지원하지 않았음. 투명 영역이 회색으로 표시됨:

css
/* IE6에서 PNG 투명도를 지원하는 CSS 필터 */
.png-image {
  background: url('/images/logo.png') no-repeat;
  /* IE6 전용 필터 */
  _background: none;
  _filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(
    src='/images/logo.png',
    sizingMethod='crop'
  );
}

/* 또는 JavaScript로 해결 */
/* DD_belatedPNG.js 라이브러리 사용 */
/* <!--[if IE 6]>
   <script src="DD_belatedPNG.js"></script>
   <script>DD_belatedPNG.fix('.png-fix');</script>
   <![endif]--> */
css
/* PNG를 피하는 대안: GIF 사용 */
/* PNG 대신 GIF를 쓰면 투명도가 됨 (1비트 투명도만) */
/* 반투명은 포기해야 함 */

/* 또 다른 대안: 8비트 PNG */
/* PNG8은 IE6에서도 투명도가 되지만 256색 제한 */
/* 그라데이션이 있으면 품질이 심각하게 떨어짐 */
DXImageTransform의 부작용

IE6의 filter: progid:DXImageTransform CSS 필터는 PNG 투명도를 해결하지만, 심각한 성능 문제를 유발함. 페이지에 PNG 이미지가 많으면 IE6가 눈에 띄게 느려짐. 또한 이 필터가 적용된 요소는 클릭 이벤트가 안 먹히는 버그도 있었음. 해결책이 새로운 문제를 만드는 전형적인 패턴.

전형적인 IE6 호환 레이아웃

2000년대 중반의 전형적인 2단 레이아웃 코드를 보자:

css
/* 2006년경 프로덕션 CSS */

/* reset (Eric Meyer Reset의 간소화 버전) */
* {
  margin: 0;
  padding: 0;
}

body {
  font-family: "굴림", Gulim, Arial, sans-serif;
  font-size: 12px;  /* px 고정 (em, rem? 뭔데 그게) */
  color: #333;
  background: #fff;
}

/* 가운데 정렬 — IE6에서 margin: 0 auto가 안 먹힘 */
#wrapper {
  width: 980px;
  margin: 0 auto;
  /* IE6에서 가운데 정렬하려면 body에 text-align: center 필요 */
}

/* 이걸 위해 body에... */
body {
  text-align: center; /* IE6 가운데 정렬 핵 */
}
#wrapper {
  text-align: left;   /* 자식 요소 정렬 복원 */
}

/* 2단 레이아웃 — float 기반 */
#content {
  float: left;
  width: 700px;
  min-height: 500px;
  _height: 500px;     /* IE6는 min-height를 모름, _핵 사용 */
}

#sidebar {
  float: right;
  width: 260px;
}

/* footer의 float 해제 */
#footer {
  clear: both;
  width: 980px;
  height: 60px;
  background: #333;
  color: #fff;
  text-align: center;
  line-height: 60px;
}

/* 링크 스타일 */
a {
  color: #0066cc;
  text-decoration: none;
}
a:hover {
  text-decoration: underline;
}
/* IE6에서 :hover는 a 태그에만 동작함 */
/* div:hover, li:hover 같은 건 안 됨 */

/* 반투명 오버레이? IE6에서는... */
.overlay {
  background: rgba(0, 0, 0, 0.5); /* IE6는 rgba를 모름 */
  /* IE6 대안 */
  background: transparent;
  _background: none;
  filter: progid:DXImageTransform.Microsoft.gradient(
    startColorstr=#80000000,
    endColorstr=#80000000
  );
  zoom: 1;
}
980px의 의미

2000년대 웹사이트의 너비가 대부분 960px~980px이었던 이유는, 당시 주류 해상도가 1024x768이었기 때문임. 스크롤바(약 17px)를 빼면 사용 가능한 너비가 약 1007px이었고, 여기에 여백을 주면 960px 정도가 됨. "960 Grid System"이라는 CSS 프레임워크도 있었음. 반응형? 모바일? 그런 거 없었음. 해상도는 하나였음.

IE6에서만 발생하는 기괴한 버그들

css
/* 1. Double Margin Bug */
/* float된 요소에 margin을 주면 IE6에서 2배로 적용 */
.float-box {
  float: left;
  margin-left: 20px;
  /* IE6에서는 40px로 렌더링됨 */
  /* 해결: display: inline 추가 (float와 모순이지만 동작함) */
  display: inline;
}

/* 2. Peekaboo Bug */
/* float 옆의 콘텐츠가 스크롤하면 사라졌다 나타남 */
.container {
  /* 해결: hasLayout 트리거 */
  zoom: 1;
  position: relative; /* 또는 이것 */
}

/* 3. 3px Gap Bug */
/* float 요소와 인접 텍스트 사이에 3px 간격 발생 */
.float-element {
  float: left;
  width: 200px;
  /* IE6에서 오른쪽에 3px 빈 공간이 생김 */
  /* 해결: _margin-right: -3px; */
  _margin-right: -3px;
}

/* 4. Guillotine Bug (단두대 버그) */
/* 링크에 hover하면 부모의 하단이 잘림 */
.parent {
  background: #eee;
}
.parent a:hover {
  background: #ddd;
  /* IE6에서 이 hover가 부모의 높이를 재계산하게 만들어
     float된 콘텐츠가 잘림 */
  /* 해결: 부모에 height: 1%; overflow: hidden; */
}

/* 5. 텍스트가 float 옆에서 사라지는 버그 */
/* "Disappearing Text" 버그 */
.text-next-to-float {
  zoom: 1;  /* 이것만이 답 */
}
단두대 버그의 공포

Guillotine Bug(단두대 버그)는 정말 디버깅이 불가능했음. 마우스를 올리면 콘텐츠가 사라지는데, 마우스를 빼면 돌아옴. 재현 조건이 복잡하고, 원인을 찾기도 어려움. IE6의 렌더링 엔진(Trident)의 reflow 로직 버그였는데, zoom: 1이 이 버그를 우회할 수 있었음. zoom: 1은 진짜 만능이었음.

2000년대 브라우저 테스트 환경

지금은 BrowserStack이나 크롬 DevTools로 편하게 테스트하지만, 당시에는:

테스트 브라우저 목록 (2006년 기준):
- IE 6.0 (Windows XP)
- IE 7.0 (Windows Vista) — 나중에 추가
- Firefox 1.5 / 2.0
- Opera 9
- Safari 2 (Mac만)

테스트 방법:
1. 컴퓨터 여러 대 (Windows XP, Vista 각각)
2. Virtual PC로 가상 머신
3. IETester (여러 IE 버전을 한 프로그램에서)
4. 실제로 IE6가 설치된 컴퓨터에서 직접 확인

디버깅 도구:
- IE6: 없음. alert()으로 디버깅
- Firefox: Firebug (혁명적이었음)
- 크롬 DevTools? 2008년부터
Firebug의 전설

Firefox의 Firebug 확장 프로그램은 웹 개발 도구의 혁명이었음. DOM 검사, CSS 실시간 편집, JavaScript 콘솔, 네트워크 모니터링을 최초로 제공한 도구임. 지금의 Chrome DevTools는 Firebug에서 많은 영감을 받았음. Firebug가 없었으면 웹 개발은 훨씬 더 고통스러웠을 것임.

모던 CSS로의 진화

IE6 시절과 현재의 CSS를 비교하면 격세지감임:

css
/* ===========================
   2006년: 2단 레이아웃
   =========================== */
#wrapper { width: 980px; margin: 0 auto; }
#content { float: left; width: 700px; }
#sidebar { float: right; width: 260px; }
#footer { clear: both; }
/* + IE6 핵 10줄 */

/* ===========================
   2012년: 같은 레이아웃 (Flexbox)
   =========================== */
.layout {
  display: flex;
  max-width: 980px;
  margin: 0 auto;
}
.content { flex: 1; }
.sidebar { width: 260px; }
/* 끝. 핵 없음. clearfix 없음. */

/* ===========================
   2020년: 같은 레이아웃 (Grid)
   =========================== */
.layout {
  display: grid;
  grid-template-columns: 1fr 260px;
  max-width: 980px;
  margin: 0 auto;
}
/* 끝. 2줄. */

/* ===========================
   2024년: 반응형 + Container Queries
   =========================== */
.layout {
  display: grid;
  grid-template-columns: 1fr;
  container-type: inline-size;
}

@container (min-width: 768px) {
  .layout {
    grid-template-columns: 1fr 260px;
  }
}

/* 가운데 정렬? */
.centered {
  margin-inline: auto;
  max-inline-size: 980px;
}

/* 반투명 배경? */
.overlay {
  background: oklch(0 0 0 / 50%);
  /* 또는 */
  background: color-mix(in oklch, black 50%, transparent);
}

타이포그래피의 진화

css
/* 2006년 */
body {
  font-family: "굴림", Gulim, Arial, sans-serif;
  font-size: 12px;
}

/* 2024년 */
body {
  font-family: "Pretendard Variable", Pretendard,
               -apple-system, BlinkMacSystemFont,
               system-ui, Roboto, sans-serif;
  font-size: clamp(1rem, 0.5rem + 1vw, 1.25rem);
  line-height: 1.6;
  text-wrap: pretty;
}

가운데 정렬의 진화

css
/* IE6 시절: 수직 가운데 정렬이 불가능에 가까웠음 */
.vertical-center-ie6 {
  /* 방법 1: table-cell (IE6에서 안 됨) */
  display: table-cell;
  vertical-align: middle;

  /* 방법 2: absolute + negative margin (크기를 알아야 함) */
  position: absolute;
  top: 50%;
  left: 50%;
  width: 200px;
  height: 100px;
  margin-top: -50px;  /* height의 절반 */
  margin-left: -100px; /* width의 절반 */
}

/* 2024년 */
.center-anything {
  display: grid;
  place-items: center;
  /* 끝. */
}

/* 또는 */
.center-flex {
  display: flex;
  align-items: center;
  justify-content: center;
}

/* 또는 margin auto */
.center-margin {
  margin: auto;
  /* flex/grid 컨테이너 안에서 완벽한 중앙 정렬 */
}
CSS의 황금기

현재 CSS는 역사상 가장 강력한 시기를 맞이하고 있음. Container Queries, Cascade Layers, Subgrid, :has() 선택자, View Transitions, Scroll-driven Animations, anchor positioning... 과거에 JavaScript로 해야 했던 것들이 CSS만으로 가능해짐. IE6 시절을 생각하면 정말 격세지감임.

IE6 퇴출의 역사

IE6를 죽이기 위한 인류의 노력:

2006 — IE7 출시, 하지만 IE6가 안 죽음 (기업 인트라넷)
2008 — Chrome 출시, 브라우저 전쟁 재개
2009 — YouTube "IE6 지원 중단" 배너
2010 — Google Apps IE6 지원 중단
2010 — "IE6 Countdown" 캠페인 (Microsoft 자체!)
2011 — IE6 글로벌 점유율 10% 이하
2012 — 대부분의 웹사이트가 IE6 지원 중단
2013 — IE6 글로벌 점유율 5% 이하
2014 — Windows XP 지원 종료 (드디어!)
2016 — IE 11만 지원 (이전 버전 전부 지원 종료)
2022 — IE 11 지원 종료 (인류의 승리)
근데 아직도...

2024년에도 IE를 요구하는 곳이 있음. 한국의 일부 관공서, 은행 내부 시스템, 기업 ERP... "ActiveX 때문에 IE 써야 해요"라는 말이 아직 들림. 레거시는 정말 죽지 않음.

IE6 호환 코드를 만났을 때

레거시 프로젝트에서 IE6 관련 코드를 발견했을 때의 대처법:

1단계: 브라우저 지원 범위 확인

javascript
// analytics 데이터를 확인해서 IE 사용자가 있는지 확인
// GA에서 브라우저별 사용자 수 확인
// IE 사용자가 0%면 과감하게 제거

2단계: 핵 코드 제거

css
/* Before */
.box {
  display: inline-block;
  *display: inline;     /* 제거 */
  *zoom: 1;            /* 제거 */
  _height: 100px;      /* 제거 */
  background: #fff;
  filter: progid:DXImageTransform.Microsoft.gradient(
    startColorstr=#80000000,
    endColorstr=#80000000
  );  /* 제거 */
}

/* After */
.box {
  display: inline-block;
  background: #fff;
}

3단계: 조건부 주석 제거

HTML에서 <!--[if로 시작하는 모든 블록을 제거. IE 전용 CSS 파일도 삭제.

4단계: 현대 CSS로 교체

float 레이아웃을 Flexbox/Grid로, 핵을 표준 속성으로, px을 rem으로, 고정 너비를 반응형으로 교체.

고고학자의 노트

IE6 호환 CSS 코드를 보면서 "왜 이따위로 짰지?"라고 생각하기 쉽지만, 당시 개발자들은 진짜 영웅이었음. 제대로 된 디버깅 도구도 없이, 문서화도 안 된 브라우저 버그를 경험으로 파악하고, 창의적인 우회 방법을 발명한 사람들임. zoom: 1이 왜 동작하는지 원리를 이해하고 있던 사람은 전 세계에 몇백 명도 안 됐을 거임. 그 사람들이 지금의 웹을 만든 것임. 존경.