안녕하세요! 오늘 함께 살펴볼 MDN 공식 문서는 CSS에서 아주 강력하고 유용한 도구인 기능 쿼리(Feature queries)입니다.
최신 CSS 문법을 사용하고 싶지만 구형 브라우저에서 화면이 깨질까 봐 걱정된 적 있으시죠? 이때 화면을 우아하게 처리하는 기법인 '점진적 향상(Progressive Enhancement)'을 구현할 수 있게 해주는 핵심 기능이랍니다. 앞으로 만드실 포트폴리오 프로젝트에도 당장 적용해 볼 수 있는 좋은 내용이니 함께 차근차근 번역하며 알아볼까요?
기능 쿼리(Feature queries)는 사용자 에이전트(브라우저)가 CSS 속성이나 속성값 같은 하나 이상의 CSS 기능을 지원하는지 혹은 지원하지 않는지를 테스트하는 조건부 그룹 규칙(conditional group rules)입니다. 기능 쿼리는 웹 개발자에게 브라우저가 특정 기능을 지원하는지 확인하고, 그 테스트 결과에 따라 특정 CSS만 실행되도록 하는 방법을 제공합니다. 이 가이드에서는 기능 쿼리를 사용하여 점진적 향상(progressive enhancement)을 구현하는 방법을 배우게 됩니다.
기능 쿼리는 CSS @규칙(at-rule)인 @supports를 사용하여 생성합니다. (또는 @import @규칙 내에서 supports() 함수를 사용할 수도 있습니다.)
💡 강사의 실무 팁!
점진적 향상(Progressive Enhancement)이란, 모든 브라우저에서 동작하는 기본적이고 핵심적인 경험을 먼저 제공한 다음, 최신 기능을 지원하는 브라우저를 사용하는 사용자에게는 더 향상된 레이아웃이나 시각적 효과를 덧붙여 제공하는 개발 전략입니다. 프론트엔드 개발자로서 이 개념을 잘 숙지하고 코드로 녹여내면 면접관들에게 아주 좋은 인상을 줄 수 있답니다!
CSS 기능 쿼리는 CSS 조건부 규칙 (CSS conditional rules) 모듈의 일부이며, 이 모듈에는 미디어 쿼리 @media @규칙도 함께 정의되어 있습니다. 기능 쿼리는 미디어 쿼리(media queries)와 비슷하게 동작합니다. 차이점이 있다면, 미디어 쿼리는 웹 페이지가 실행되는 환경(기기 화면 크기, 해상도 등)에 대한 무언가를 테스트하는 반면, 기능 쿼리는 CSS 기능에 대한 브라우저의 지원 여부(능력)를 테스트한다는 것입니다.
기능 쿼리는 @supports @규칙 뒤에 지원 조건(support condition)이 오거나, @import @규칙 선언 내에 supports() 함수와 선언 매개변수가 오는 형태로 구성됩니다.
/* `@supports` at-rule */
@supports <support-condition> {
/* CSS rules to apply */
}
/* `supports()` function */
@import url_to_import supports(<declaration>);
예를 들어, 만약 사용자 브라우저가 CSS color 속성의 유효한 값으로 red를 지원한다면, 우리는 스타일 세트를 적용하거나 전체 스타일시트를 임포트(import)할 수 있습니다.
/* `@supports` at-rule */
@supports (color: red) {
/* CSS rules to apply */
}
/* `supports()` function */
@import "/css/styles.css" supports(color: red);
또 다른 예로, 브라우저가 row-gap 속성을 지원하는지 확인하고 싶다면 아래와 같이 기능 쿼리를 작성할 수 있습니다. 대부분의 경우 어떤 값을 사용하는지는 중요하지 않습니다. 브라우저가 이 속성 자체를 지원하는지 확인하는 것이 목적이라면, 유효한 값 아무거나 넣어도 상관없습니다.
<div class="box">
If your browser supports the row-gap property, the border will be dashed and
text will be red.
</div>
body {
font: 1.2em / 1.5 sans-serif;
}
.box {
border: 4px solid blue;
color: blue;
padding: 1em;
}
@supports (row-gap: 10px) {
.box {
border: 4px dashed darkgreen;
color: red;
}
}
만약 특정 속성의 새로운 값을 테스트하는 경우라면, 속성과 값의 쌍에서 '값' 부분이 훨씬 더 중요해집니다. 모든 브라우저가 color: red를 지원합니다 (이것은 CSS1 시절로 거슬러 올라갑니다). 하지만, 상대 색상(relative colors)처럼 CSS 속성에 새롭게 추가된 값들은 브라우저에서 아직 지원하지 않을 수도 있습니다. 기능 쿼리는 속성과 값의 쌍 전체를 테스트할 수 있게 해 주므로, 새로운 값에 대한 지원 여부도 감지할 수 있습니다.
위의 color 속성 예제를 확장해서, 브라우저가 color: AccentColor 선언을 지원하는지 확인하는 코드는 다음과 같습니다.
/* `@supports` at-rule */
@supports (color: AccentColor) {
/* CSS rules to apply */
}
/* `supports()` function */
@import "/css/styles.css" supports(color: AccentColor);
지금까지 살펴본 예제들에서는 괄호 안에 단일 선언을 넣어서 사용자 브라우저가 특정 CSS 속성의 특정 값을 지원하는지 확인하는 데 기능 쿼리를 사용했습니다. 이 외에도 여러 개의 속성값을 한 번에 테스트하거나, 기능 지원이 '없는' 상태를 테스트할 수도 있습니다.
브라우저가 기능을 지원하는지 묻는 것 외에도, not 키워드를 추가하여 정반대의 상황(지원하지 않는지)을 테스트할 수도 있습니다.
/* `@supports` at-rule with `not` */
@supports not (property: value) {
/* CSS rules to apply */
}
아래 예제의 기능 쿼리 내부에 있는 CSS는 브라우저가 row-gap 속성을 지원하지 않을 때만 실행됩니다.
<div class="box">
If your browser does not support row-gap, the content will be darkgreen with a
dashed border.
</div>
body {
font: 1.2em / 1.5 sans-serif;
}
.box {
border: 4px solid blue;
color: blue;
padding: 1em;
}
@supports not (row-gap: 10px) {
.box {
border: 4px dashed darkgreen;
color: darkgreen;
}
}
💡 강사의 보충 설명!
최신 기술인 CSS Grid나 Flexbox의gap속성을 쓸 때, 구형 브라우저(예: IE11 등)를 반드시 지원해야 하는 환경이라면@supports not (gap: 10px)등을 활용해margin으로 대체 레이아웃을 잡아주는(Fallback) 방식으로 아주 쏠쏠하게 활용할 수 있습니다.
기능 쿼리에서 두 개 이상의 속성에 대한 지원 여부를 테스트해야 할 수도 있습니다. 이렇게 하려면 and 키워드로 구분하여 테스트할 기능들의 목록을 나열하면 됩니다.
/* multiple feature `@supports` at-rule */
@supports (property1: value) and (property2: value) {
/* CSS rules to apply */
}
예를 들어, 실행하려는 CSS가 브라우저의 CSS Shapes 지원과 CSS Grid 지원을 모두 필요로 한다면, 이 두 기능 모두에 대해 지원 여부를 테스트하는 규칙을 만들 수 있습니다. 다음 규칙은 브라우저가 shape-outside: circle()과 display: grid를 모두 지원할 때만 true를 반환하여 내부 코드를 실행합니다.
<div class="box">
If your browser supports <code>display: grid</code> and
<code>shape-outside: circle()</code>, the content will be darkgreen with a
dashed border.
</div>
body {
font: 1.2em / 1.5 sans-serif;
}
.box {
border: 4px solid blue;
color: blue;
padding: 1em;
}
@supports (display: grid) and (shape-outside: circle()) {
.box {
border: 4px dashed darkgreen;
color: darkgreen;
}
}
💡 강사의 실무 포트폴리오 팁!
웹 프로필 사이트를 만드실 때, CSS Grid를 이용한 복잡한 레이아웃이나shape-outside를 활용한 텍스트 감싸기 같은 화려한 UI를 도입해 보세요. 그리고@supports의and조건을 이용해 해당 기능을 지원하지 않는 브라우저에서는 단순하고 깔끔한 선형 레이아웃으로 보이게 처리해 둔다면, "이 지원자는 크로스 브라우징과 점진적 향상을 고려할 줄 아는구나"라는 확실한 어필 포인트가 될 수 있습니다!
or 키워드를 사용하면, 나열된 선언 중 하나 이상을 지원하는 경우에만 CSS를 적용할 수도 있습니다.
/* any feature `@supports` at-rule */
@supports (property1: value) or (property2: value) {
/* CSS rules to apply */
}
이 기능은 어떤 기능이 벤더 접두사(vendor prefix)를 사용해야 할 때 특히 유용합니다. 표준 속성과 더불어 각종 벤더 접두사가 붙은 버전을 함께 테스트할 수 있기 때문입니다.
<div class="box">
The text and border will be green if your browser supports font smoothing.
</div>
body {
font: 1.2em / 1.5 sans-serif;
}
.box {
border: 4px solid blue;
color: blue;
padding: 1em;
}
@supports (font-smooth: always) or (-webkit-font-smoothing: antialiased) {
.box {
border: 4px dashed darkgreen;
color: darkgreen;
}
}
기능 쿼리는 속성-값 쌍(property-value pairs)에만 국한되지 않습니다. 기능 쿼리 내에 font-tech(), font-format(), 그리고 selector() 함수를 포함시켜서, 사용자 브라우저가 특정 폰트 기술, 폰트 포맷 또는 선택자(selector) 구문을 지원하는지에 따라 선택적으로 CSS를 적용할 수도 있습니다.
예를 들어, selector() 함수는 벤더 접두사가 붙은 가상 요소(pseudo-element)를 지원하는 브라우저를 위해 특정 스타일시트를 임포트하는 데 사용할 수 있습니다.
/* A `selector()` query within a `supports()` function */
@import "/css/webkitShadowStyles.css"
supports(selector(::-webkit-inner-spin-button));
이 예제에서는 브라우저가 AccentColor <system-color>를 지원하는지 확인합니다. 그리고 만약 해당 색상 타입이 지원된다면, display: none을 사용해서 기본값인 "지원하지 않음(not supported)" 메시지를 숨기고 "지원함(supported)" 메시지로 변경합니다.
<p class="accentcolor">
Your browser does <span>not</span> support <code>AccentColor</code> as a color
value.
</p>
body {
font: 1.2em / 1.5 sans-serif;
}
p {
padding: 1em;
}
@supports (color: AccentColor) {
p {
color: green;
border: 2px solid;
}
span {
display: none; /* 지원할 경우 'not' 이라는 글자를 숨김 */
}
}
@supports not (color: AccentColor) {
p {
color: red;
}
}
@supports 규칙은 브라우저가 하나 이상의 속성/값 쌍을 해석(parse)할 수 있는지 확인하고, 그에 따라 브라우저가 해당 기능들을 지원한다고 주장하는지 확인하는 테스트입니다. 만약 속성/값 쌍을 브라우저가 이해했다면 긍정적인 응답(true)을 반환하죠.
즉, 기능 쿼리는 브라우저가 선언을 '유효한 구문'으로 간주하는지를 체크하는 것입니다. 하지만 해당 기능을 버그 없이, 혹은 명세서(spec) 위반 없이 제대로(properly) 지원하는지까지 확인하는 용도로는 사용할 수 없습니다. 기능 쿼리는 브라우저의 부분적인 구현(partial implementations)이나 버그가 있는 구현 상태를 완벽하게 걸러내어 테스트할 수는 없습니다.
💡 면접 단골 질문 대비!
"기능 쿼리는 100% 안전한가요?"라는 질문을 받는다면 위 단락을 떠올려 보세요. "아닙니다.@supports는 브라우저가 그 문법을 파싱(해석)할 수 있는지 여부를 확인할 뿐, 렌더링에 버그가 있거나 스펙이 부분적으로만 구현되어 있는 경우까지 감지하지는 못합니다."라고 정확히 답변하실 수 있을 거예요.
기능 쿼리는 사이트를 점진적으로 향상(progressively enhancing)시키는 데 아주 유용한 도구입니다. 모든 브라우저에 대해 괜찮은 기본 솔루션을 제공하는 동시에, 최신 속성과 값을 지원하는 브라우저를 위해 한층 향상된 솔루션을 덧붙여 제공할 수 있게 해줍니다.
새로운 CSS 기능을 사용하기 시작할 때 무조건 기능 쿼리를 사용할 필요는 없습니다. CSS의 오류 처리 방식(error handling) 덕분에, 브라우저는 자신이 아직 인식하지 못하는 CSS 문법을 만나면 그냥 조용히 무시하고 넘어가기 때문이죠. 하지만, 기능 쿼리는 기존의 단순한 폴백(fallback) 선언들을 대체할 수 있는 훌륭한 대안이며, 한 번 작성해 두면 결국 모든 곳에서 점진적으로 지원받을 수 있는 코드를 작성할 수 있게 해줍니다.
@supports (Browser feature detection: CSS @supports)지금까지 CSS @supports의 기본 사용법부터 not, and, or 연산자, 그리고 실무에서 활용하는 한계점까지 완벽하게 정리해 보았습니다.
이 기능 쿼리 개념을 활용해서, 현재 개발을 준비하고 계신 독후감 사이트나 프로필 사이트의 특정 컴포넌트(예를 들어, 최신 레이아웃 기법이 들어간 영역)에 어떤 점진적 향상을 적용해 볼 수 있을지 아이디어를 함께 구상해 볼까요? 궁금한 점이 있다면 편하게 질문해 주세요!