색공간이란?
색공간(Color Space)은 색을 숫자로 표현하는 좌표계다.
우리가 #FF0000이라고 쓸 때, 이 숫자가 어떤 "빨간색"인지는 색공간이 정의한다. 같은 좌표라도 색공간이 다르면 다른 색이 된다. GPS 좌표계가 다르면 같은 숫자가 다른 위치를 가리키는 것과 같은 원리다.
예를 들면, 색 공간이 다르다는 건 색 좌표(cf. hex)가 늘어나는 게 아니라, 다이얼의 최대치가 달라지는 것이다.
- sRGB = 최대 볼륨이 80까지인 스피커
- Display P3 = 최대 볼륨이 100까지인 스피커
둘 다 0~100% 눈금은 똑같다. 하지만 "볼륨 100%"로 올렸을 때 나오는 소리 크기가 다르다. 색도 마찬가지로, 같은 (1, 0, 0) 빨강이라도 sRGB에서의 가장 빨간 빨강과 P3에서의 가장 빨간 빨강은 물리적으로 다른 색이다. P3 쪽이 더 선명하다.
즉 hex 코드가 새로 추가되는 게 아니라, 같은 좌표가 가리키는 실제 색 자체가 달라지는 것이다.
아래 CIE 1931 색도 다이어그램은 사람이 볼 수 있는 모든 색의 영역(말발굽 모양)과 각 색공간이 커버하는 삼각형 영역을 보여준다. 말발굽 전체가 인간의 가시 영역이고, 안쪽 삼각형이 각 색공간이 표현할 수 있는 범위다.
지금 CSS에서 쓰는
rgb(),#hex,hsl()은 전부 sRGB라는 하나의 색공간 안에서 동작하고 있다.
sRGB - 웹의 기본
sRGB(Standard RGB)는 1996년 HP와 Microsoft가 당시 CRT 모니터를 기준으로 만든 색공간이다.
- CSS의
rgb(),#hex,hsl()→ 전부 sRGB - 약 30년간 웹 색상의 사실상 표준
- 한계: 사람이 볼 수 있는 색의 약 35%만 표현 가능
/* 이건 전부 sRGB */
color: #ff0000;
color: rgb(255, 0, 0);
color: hsl(0, 100%, 50%);볼륨 비유로 돌아오면, sRGB는 최대 볼륨이 80짜리인 스피커다. 일상적인 소리는 충분히 내지만, "더 선명한 주황"이나 "형광 청록" 같은 강렬한 색은 볼륨이 모자라서 재현하지 못한다. 30년 전 CRT 모니터의 한계에 맞춘 안전한 최소 공통분모인 셈!
충분한 시간 동안 잘 작동해왔지만, 디스플레이 기술이 발전하면서 sRGB의 좁은 색역이 점점 아쉬워지기 시작했다.
Display P3 - 더 넓은 공간
Display P3는 Apple이 주도한 광색역(wide gamut) 색공간이다. sRGB보다 약 25% 더 넓은 색역을 가진다.
- 2016년부터 iPhone, MacBook 디스플레이에 탑재
- 현재 대부분의 모던 디스플레이(OLED 포함)가 P3 지원
- sRGB로 표현 못하는 선명한 빨강, 초록, 시안 영역이 가능
/* Display P3로 더 선명한 빨강 */
color: color(display-p3 1 0 0);
/* sRGB 빨강 - P3보다 덜 선명 */
color: rgb(255, 0, 0);CSS에서 #FF0000이나 rgb(255,0,0)을 쓰면 이건 무조건 sRGB다. hex 표기 자체가 sRGB에 묶여 있기 때문이다. P3의 더 선명한 빨강을 쓰려면 새로운 CSS 문법이 필요하다:
color(display-p3 1 0 0)— P3의 빨강 (hex로 표현 불가!)rgb(255, 0, 0)— sRGB의 빨강
둘 다 "빨강을 최대로" 라는 같은 의도인데, P3의 다이얼 최대치가 더 높기 때문에 더 선명한 빨강이 나오는 것이다. 새로운 hex 코드가 생기는 게 아니라, 기존 hex로는 도달할 수 없는 영역에 새 문법으로 접근하는 것이다.
아래 데모에서 sRGB와 P3 원색을 나란히 비교해보자. 차이가 보이시나요?
Red
sRGB
P3
Green
sRGB
P3
Blue
sRGB
P3
* Display P3 지원 디스플레이에서만 차이가 보입니다
P3 지원 디스플레이(iPhone, MacBook, 최신 OLED 모니터)에서 보면 P3 쪽이 더 채도가 높게 보인다. 일반 sRGB 모니터에서는 차이가 없을 수 있다.
직접 색을 골라서 비교해보자. 컬러피커에서 아무 색이나 선택하면, 같은 RGB 숫자값을 sRGB와 Display P3에서 각각 렌더링한 결과를 나란히 볼 수 있다. P3에서는 더 넓은 색역 안에서 해석되기 때문에 더 선명하고 채도 높은 색이 나온다.
→ (0.902, 0.224, 0.275)
sRGB
color(srgb 0.902 0.224 0.275)
Display P3
color(display-p3 0.902 0.224 0.275)
* 같은 숫자라도 색공간이 다르면 실제 색이 다르다. P3 디스플레이에서만 차이가 보입니다.
OKLCH - 사람 중심의 색공간
OKLCH는 2021년 Björn Ottosson이 제안한 색공간으로, 세 개의 축으로 구성된다.
| 축 | 의미 | 범위 |
|---|---|---|
| L (Lightness) | 밝기 | 0 (검정) ~ 1 (흰색) |
| C (Chroma) | 채도 | 0 (무채색) ~ ~0.4 |
| H (Hue) | 색상 | 0° ~ 360° |
OKLCH 색공간을 3D로 시각화하면 아래와 같다. 세로축이 밝기(L), 중심에서 바깥으로의 거리가 채도(C), 원을 따라 도는 방향이 색상(H)이다.
아래는 OKLCH 색상환(L=0.7, C=0.2)이다. 같은 밝기와 채도에서 색상(Hue)만 바꿨을 때 모든 색이 균일한 밝기로 보이는 것이 OKLCH의 특징이다.
이미지 출처: Wikimedia Commons (CC BY-SA 4.0)
핵심은 지각적 균일성(perceptual uniformity) 이다. OKLCH에서 L=0.5인 빨강과 L=0.5인 파랑은 실제로 비슷한 밝기로 보인다. HSL에서는 그렇지 않다.
/* OKLCH 표기 */
color: oklch(0.7 0.15 150); /* L C H */
/* 밝기만 바꿔도 자연스럽다 */
--primary: oklch(0.6 0.2 250);
--primary-light: oklch(0.8 0.15 250);
--primary-dark: oklch(0.4 0.2 250);HSL의 함정
HSL에서 lightness: 50%는 색상(Hue)에 따라 체감 밝기가 제각각이다. 노란색은 눈부시게 밝고, 파란색은 어둡다.
아래 이미지는 HSV(위)와 OKLCH(아래)에서 색상(Hue)만 변화시킨 그라데이션이다. HSV에서 노랑~시안 구간이 유독 밝은 반면, OKLCH에서는 전체적으로 균일한 밝기를 유지한다.
이미지 출처: Wikimedia Commons (CC BY-SA 4.0)
아래 데모에서도 직접 확인해보자.
HSL — hsl(H 100% 50%)
OKLCH — oklch(0.7 0.15 H)
HSL 줄에서 노란색(60°) 부근이 유독 밝고, 파란색(240°)이 어두운 게 보인다. OKLCH 줄은 모든 색이 비슷한 밝기로 보인다.
이것이 OKLCH가 사람 중심이라 불리는 이유다. 디자인 시스템에서 색 팔레트를 만들거나, 접근성 대비를 판단할 때 HSL보다 훨씬 예측 가능하다.
같은 색, 다른 그라데이션
같은 시작색과 끝색이라도 어떤 색공간에서 보간(interpolation)하느냐에 따라 중간색이 완전히 달라진다.
아래 애니메이션은 Linear RGB, sRGB, Oklab 세 가지 색공간에서의 그라데이션 보간 차이를 보여준다. 같은 두 색 사이를 잇는데도 중간색이 완전히 다르다.
이미지 출처: Wikimedia Commons (CC BY-SA 4.0)
서울에서 부산까지 가는 길을 생각해보자. sRGB 보간은 산을 직선으로 뚫고 가는 것이다. 수학적으로는 최단거리지만 중간에 험한 구간(탁한 색)을 지난다. OKLCH 보간은 고속도로를 따라 가는 것이다. 약간 돌아가더라도 전 구간이 매끄럽고 쾌적하다(선명한 중간색).
- sRGB - 중간에 탁한 보라/갈색이 나온다
- OKLCH - 선명한 중간색이 유지된다
- srgb-linear - 광학적으로 정확하지만 중간이 어둡다
- lab - 지각 기반이지만 hue shift가 있다
sRGB는 감마 보정(gamma correction)이 적용된 비선형 공간이다. 두 색의 RGB 값을 단순 평균하면, 사람의 눈에는 에너지가 부족한 어둡고 탁한 중간색으로 보인다. OKLCH는 인지 기반 보간이라 이 문제가 발생하지 않는다.
초록과 보라 사이 그라데이션도 비교해보자. 보색 계열에서 차이가 더 극적이다.
color-mix() - CSS로 색 섞기
CSS color-mix() 함수를 쓰면 어떤 색공간에서 혼합할지 직접 지정할 수 있다.
/* OKLCH에서 50% 혼합 */
color: color-mix(in oklch, #ff6b6b, #4ecdc4);
/* sRGB에서 30:70 혼합 */
color: color-mix(in srgb, #ff6b6b 30%, #4ecdc4);같은 두 색을 섞어도 색공간에 따라 결과가 다르다:
srgb
oklch
lab
hsl
특히 보색 관계의 색을 섞을 때 차이가 극적이다. sRGB에서는 탁해지지만, OKLCH에서는 채도가 유지된 자연스러운 중간색이 나온다.
Playground
직접 색을 골라서 실험해보자. 두 색을 선택하고 mix ratio를 조절하면 색공간별 그라데이션과 혼합 결과를 실시간으로 비교할 수 있다.
Gradient
Mix (50%)
srgb
srgb-linear
lab
oklch
hsl
CSS 코드 보기
/* Gradient in OKLCH */ background: linear-gradient( to right in oklch, #e63946, #457b9d); /* Color mix in OKLCH (50%) */ color: color-mix( in oklch, #e63946 50%, #457b9d);
어떻게 쓸까?
1. 그라데이션은 OKLCH로
/* 탁하지 않은 선명한 그라데이션 */
background: linear-gradient(to right in oklch, #667eea, #764ba2);2. 디자인 토큰은 OKLCH로 정의
:root {
--brand: oklch(0.65 0.2 250);
--brand-hover: oklch(0.55 0.22 250); /* L만 조절 */
--brand-light: oklch(0.85 0.1 250);
}L(밝기)만 조절하면 hue가 틀어지지 않아서 일관된 팔레트를 만들 수 있다.
3. P3 색상은 fallback과 함께
.badge {
background: rgb(0, 200, 83); /* sRGB fallback */
background: color(display-p3 0 0.82 0.33); /* P3 강화 */
}지원하지 않는 브라우저는 첫 번째 줄을, P3 지원 브라우저는 두 번째 줄을 적용한다.
4. 색 혼합은 목적에 맞게
/* hover 어둡게 - 자연스러운 명도 변화 */
background: color-mix(in oklch, var(--brand), black 20%);
/* 투명 오버레이 */
background: color-mix(in srgb, var(--brand) 10%, transparent);나가며
OKLCH를 알고 나면 HSL로 돌아가기 어렵다?
디자인 시스템에서 색 팔레트를 만들 때, HSL로 밝기 변형을 하면 색상(hue)이 미묘하게 틀어지는 문제가 있었다. 노란 계열은 밝기를 올리면 연두색으로 shift되고, 파란 계열은 보라색으로 shift된다. OKLCH는 이 문제를 근본적으로 해결한다.
특히 접근성(a11y) 측면에서 OKLCH의 L(밝기)은 WCAG 대비 계산과 직관적으로 대응되기 때문에, "이 텍스트 색이 배경과 충분히 대비가 되는가?"를 판단하기 훨씬 쉽다.
아직 모든 디자인 도구(Figma 등)가 OKLCH를 완벽 지원하는 건 아니지만, CSS에서는 이미 실전에서 충분히 사용할 수 있다. Tailwind CSS v4도 내부적으로 OKLCH 기반으로 전환했다.
새 프로젝트를 시작한다면, OKLCH부터 고려해보는 것도!
정리
| 색공간 | 특징 | CSS 표기 | 추천 용도 |
|---|---|---|---|
| sRGB | 웹 표준, 좁은 색역 | rgb() hsl() #hex | 레거시 호환 |
| Display P3 | 광색역, 모던 디스플레이 | color(display-p3 r g b) | 선명한 포인트 컬러 |
| OKLCH | 지각 균일, 직관적 | oklch(L C H) | 디자인 시스템, 팔레트 |
| Lab | 지각 기반 (구세대) | lab(L a b) | 색차 계산 |
| sRGB-linear | 감마 제거된 선형 RGB | color(srgb-linear r g b) | 물리 기반 렌더링 |
이 글의 데모들은 Chrome 111+, Safari 16.2+, Firefox 127+ 에서 정상 동작한다. 2024년 이후 릴리즈된 모던 브라우저라면 문제없다.
레퍼런스
- CSS Color Level 4 Spec - W3C
- OKLCH in CSS: why we moved from RGB and HSL - Evil Martians
- A perceptual color space for image processing (Oklab) - Björn Ottosson
- color-mix() - MDN Web Docs
- High Definition CSS Color Guide - Chrome for Developers