디자인 시스템 구축, CSS 디버깅, 목업을 코드로 변환하는 작업에서 신뢰할 수 있는 색상 변환기는 가장 많이 사용되는 도구 중 하나입니다. 이 종합 가이드는 모든 주요 색상 모델, 변환의 수학적 원리, JavaScript/Python/Bash 코드 예제, oklch() 및 color-mix() 같은 최신 CSS 기능을 다룹니다.
색상 모델 이해: RGB, HEX, HSL, HSV, CMYK
RGB (빨강, 초록, 파랑)는 화면 표시의 기본 색상 모델입니다. 각 픽셀은 0-255 강도로 빨강, 초록, 파랑 빛을 혼합합니다. RGB는 가산 색상 모델로 세 채널 최대값 혼합 시 흰색이 됩니다.
HEX (16진수)는 웹에서 가장 일반적인 색상 표기법입니다. RGB 값의 16진수 표현으로 #FF5733은 rgb(255, 87, 51)과 같습니다. 3자리, 6자리, 8자리 형식이 있습니다.
HSL (색상, 채도, 명도)는 더 직관적으로 설계되었습니다. 색상은 색상환의 각도(0-360), 채도와 명도는 백분율입니다. HSL은 밝고 어두운 변형 만들기를 쉽게 합니다.
HSV/HSB (색상, 채도, 밝기)는 Photoshop, Figma 등 대부분의 색상 피커에서 사용하는 모델입니다. CSS는 HSV를 직접 지원하지 않습니다.
CMYK (시안, 마젠타, 노랑, 검정)는 인쇄용 감산 색상 모델입니다. PDF 생성과 인쇄 파일 준비에 필수적입니다.
색상 모델 비교 표
다음 표는 각 색상 모델의 주요 속성을 요약합니다:
| Model | Format | Range | Use Case | CSS Support | Human-Readable |
|---|---|---|---|---|---|
| RGB | rgb(R, G, B) | 0-255 per channel | Screen display, Canvas, WebGL | Yes | Low |
| HEX | #RRGGBB | 00-FF per channel | CSS, design tools, tokens | Yes | Low |
| HSL | hsl(H, S%, L%) | H: 0-360, S/L: 0-100% | Theming, shade generation | Yes | High |
| HSV/HSB | hsv(H, S%, V%) | H: 0-360, S/V: 0-100% | Color pickers (Photoshop, Figma) | No | High |
| CMYK | cmyk(C%, M%, Y%, K%) | 0-100% per channel | Print, PDF generation | No | Medium |
| OKLCH | oklch(L C H) | L: 0-1, C: 0-0.4, H: 0-360 | Modern CSS, wide gamut | Yes | High |
색상 변환의 작동 원리
모든 색상 변환기는 잘 알려진 수학 공식을 구현합니다. 이 공식을 이해하면 엣지 케이스 디버깅에 도움이 됩니다.
HEX→RGB: #FF5733 같은 헥스 코드는 세 개의 16진수 쌍으로 구성됩니다. 각 쌍을 독립적으로 파싱하여 RGB 값을 얻습니다.
RGB→HEX: 각 채널 값(0-255)을 2자리 16진수 문자열로 변환하고 # 접두사로 연결합니다.
RGB→HSL: R, G, B를 0-1로 정규화하고 Cmax, Cmin을 찾아 델타, 명도, 채도, 색상을 계산합니다.
HSL→HEX: 먼저 HSL을 RGB로 변환한 후 각 RGB 채널을 16진수로 변환합니다.
RGB→HSV: 색상 계산은 RGB→HSL과 동일합니다. V = Cmax, S = delta / Cmax.
HSL⇔HSV: 둘 다 색상을 공유하지만 채도와 명도/밝기 간 변환에는 특정 공식이 필요합니다.
색상 변환 코드 예제
JavaScript / CSS 색상 변환
JavaScript에는 내장 색상 변환기가 없지만 CSS 색상 함수로 변환이 간단합니다:
// ===== HEX to RGB =====
function hexToRgb(hex) {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
}
hexToRgb('#FF5733'); // { r: 255, g: 87, b: 51 }
// ===== RGB to HEX =====
function rgbToHex(r, g, b) {
return '#' + [r, g, b]
.map(x => x.toString(16).padStart(2, '0'))
.join('');
}
rgbToHex(255, 87, 51); // "#ff5733"
// ===== RGB to HSL =====
function rgbToHsl(r, g, b) {
r /= 255; g /= 255; b /= 255;
const max = Math.max(r, g, b), min = Math.min(r, g, b);
const l = (max + min) / 2;
if (max === min) return { h: 0, s: 0, l: Math.round(l * 100) };
const d = max - min;
const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
let h = 0;
switch (max) {
case r: h = ((g - b) / d + (g < b ? 6 : 0)) / 6; break;
case g: h = ((b - r) / d + 2) / 6; break;
case b: h = ((r - g) / d + 4) / 6; break;
}
return {
h: Math.round(h * 360),
s: Math.round(s * 100),
l: Math.round(l * 100)
};
}
rgbToHsl(255, 87, 51); // { h: 11, s: 100, l: 60 }
// ===== HSL to RGB =====
function hslToRgb(h, s, l) {
s /= 100; l /= 100;
const c = (1 - Math.abs(2 * l - 1)) * s;
const x = c * (1 - Math.abs((h / 60) % 2 - 1));
const m = l - c / 2;
let r = 0, g = 0, b = 0;
if (h < 60) { r = c; g = x; b = 0; }
else if (h < 120) { r = x; g = c; b = 0; }
else if (h < 180) { r = 0; g = c; b = x; }
else if (h < 240) { r = 0; g = x; b = c; }
else if (h < 300) { r = x; g = 0; b = c; }
else { r = c; g = 0; b = x; }
return {
r: Math.round((r + m) * 255),
g: Math.round((g + m) * 255),
b: Math.round((b + m) * 255)
};
}
hslToRgb(11, 100, 60); // { r: 255, g: 87, b: 51 }
// ===== CSS native color parsing (modern browsers) =====
// Use CSS.supports() and getComputedStyle for runtime conversion
const el = document.createElement('div');
el.style.color = 'hsl(14 100% 60%)';
document.body.appendChild(el);
const rgb = getComputedStyle(el).color;
// "rgb(255, 87, 51)" - browser converts any format to RGB
document.body.removeChild(el);Python 색상 변환
Python은 HSL/HSV 변환을 위한 colorsys와 Pillow, matplotlib 라이브러리를 제공합니다:
import colorsys
# ===== RGB to HSL (colorsys uses HLS order) =====
r, g, b = 255, 87, 51
r_norm, g_norm, b_norm = r / 255, g / 255, b / 255
h, l, s = colorsys.rgb_to_hls(r_norm, g_norm, b_norm)
print(f"HSL: ({h*360:.0f}, {s*100:.0f}%, {l*100:.0f}%)")
# HSL: (11, 100%, 60%)
# ===== HSL to RGB =====
r2, g2, b2 = colorsys.hls_to_rgb(h, l, s)
print(f"RGB: ({r2*255:.0f}, {g2*255:.0f}, {b2*255:.0f})")
# RGB: (255, 87, 51)
# ===== RGB to HSV =====
h_hsv, s_hsv, v_hsv = colorsys.rgb_to_hsv(r_norm, g_norm, b_norm)
print(f"HSV: ({h_hsv*360:.0f}, {s_hsv*100:.0f}%, {v_hsv*100:.0f}%)")
# HSV: (11, 80%, 100%)
# ===== HEX to RGB =====
def hex_to_rgb(hex_color):
hex_color = hex_color.lstrip('#')
return tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))
print(hex_to_rgb("#FF5733")) # (255, 87, 51)
# ===== RGB to HEX =====
def rgb_to_hex(r, g, b):
return f"#{r:02x}{g:02x}{b:02x}"
print(rgb_to_hex(255, 87, 51)) # #ff5733
# ===== Using matplotlib colors =====
import matplotlib.colors as mcolors
rgb_tuple = mcolors.to_rgb('#FF5733') # (1.0, 0.341, 0.2)
hex_value = mcolors.to_hex((1.0, 0.341, 0.2)) # '#ff5733'
rgba = mcolors.to_rgba('steelblue', alpha=0.5)
# ===== Using Pillow/PIL for image colors =====
from PIL import Image
img = Image.new('RGB', (1, 1), (255, 87, 51))
pixel = img.getpixel((0, 0)) # (255, 87, 51)
img_hsv = img.convert('HSV') # Convert to HSV color spaceBash / CLI 색상 변환
printf와 ImageMagick 같은 명령줄 도구로 기본 색상 변환이 가능합니다:
# ===== HEX to RGB using printf =====
hex="FF5733"
printf "RGB: %d, %d, %d\n" 0x${hex:0:2} 0x${hex:2:2} 0x${hex:4:2}
# RGB: 255, 87, 51
# ===== RGB to HEX using printf =====
r=255 g=87 b=51
printf "#%02x%02x%02x\n" $r $g $b
# #ff5733
# ===== ImageMagick color conversion =====
# Convert HEX to RGB
magick convert xc:"#FF5733" -format "%[fx:int(255*r)],%[fx:int(255*g)],%[fx:int(255*b)]" info:
# 255,87,51
# Get color info in multiple formats
magick convert xc:"#FF5733" -colorspace HSL -format "HSL: %[fx:r*360],%[fx:g*100]%%,%[fx:b*100]%%" info:
# ===== Convert named color to HEX =====
magick convert xc:"tomato" -format "#%02[hex:r]%02[hex:g]%02[hex:b]" info:
# #FF6347
# ===== Batch convert colors from file =====
# colors.txt: one hex color per line
while IFS= read -r color; do
printf "%s -> RGB: %d, %d, %d\n" "$color" \
0x${color:1:2} 0x${color:3:2} 0x${color:5:2}
done < colors.txt최신 CSS 색상 기능
최신 CSS는 지각적으로 균일한 색 공간, 와이드 가뭇 색상, 강력한 색상 조작 함수를 지원합니다:
/* ===== Modern CSS color syntax (no commas) ===== */
.element {
/* RGB - modern space-separated */
color: rgb(255 87 51);
color: rgb(255 87 51 / 0.5); /* with alpha */
/* HSL - modern space-separated */
background: hsl(14 100% 60%);
background: hsl(14 100% 60% / 0.8);
/* HWB - Hue, Whiteness, Blackness */
border-color: hwb(14 0% 0%); /* pure orange */
border-color: hwb(14 20% 10%); /* muted orange */
}
/* ===== OKLCH - perceptually uniform ===== */
.brand {
--brand-primary: oklch(0.65 0.25 29); /* vivid red-orange */
--brand-light: oklch(0.85 0.12 29); /* light variant */
--brand-dark: oklch(0.45 0.20 29); /* dark variant */
/* Same hue, predictable lightness changes */
}
/* ===== color-mix() - blend colors ===== */
.mixed {
background: color-mix(in oklch, #FF5733 70%, #3366FF);
border: 1px solid color-mix(in srgb, currentColor 40%, transparent);
}
/* ===== Relative color syntax ===== */
.derived {
--base: #FF5733;
/* Lighten by increasing L in oklch */
color: oklch(from var(--base) calc(l + 0.2) c h);
/* Desaturate by reducing chroma */
background: oklch(from var(--base) l calc(c * 0.5) h);
/* Shift hue by 180 degrees (complementary) */
border-color: hsl(from var(--base) calc(h + 180) s l);
}
/* ===== Wide gamut P3 colors ===== */
@supports (color: color(display-p3 1 0 0)) {
.vivid {
color: color(display-p3 1 0.3 0.1); /* more vivid than sRGB */
}
}
/* ===== Dark mode with light-dark() ===== */
:root {
color-scheme: light dark;
--text: light-dark(#1a1a1a, #e5e5e5);
--surface: light-dark(#ffffff, #121212);
}
/* ===== CSS custom properties for theming ===== */
:root {
--color-primary-h: 14;
--color-primary-s: 100%;
--color-primary-l: 60%;
--color-primary: hsl(
var(--color-primary-h)
var(--color-primary-s)
var(--color-primary-l)
);
--color-primary-hover: hsl(
var(--color-primary-h)
var(--color-primary-s)
calc(var(--color-primary-l) - 10%)
);
}CSS 색상 함수 심층 분석
최신 CSS는 풍부한 색상 함수를 제공합니다. rgb()는 공백 구분 구문을 지원합니다. hwb()(색상, 백색도, 흑색도)는 더 직관적인 대안입니다.
oklch()는 웹 색상의 획기적 발전입니다. Oklab 색 공간 기반으로 같은 밝기의 색이 실제로 동일하게 밝아 보입니다. P3 와이드 가뭇도 사용 가능합니다.
color-mix()는 모든 색 공간에서 두 색을 혼합합니다. 상대 색상 구문으로 기존 색에서 새 색을 파생할 수 있습니다.
기타 유용한 값: currentColor, transparent, 테마용 CSS 커스텀 프로퍼티, light-dark() 자동 색 구성 선택.
색상 접근성과 대비
WCAG 대비 비율은 접근 가능한 색상 디자인의 기초입니다. AA는 일반 텍스트 4.5:1, 큰 텍스트 3:1을 요구합니다. AAA는 7:1과 4.5:1을 요구합니다.
남성의 약 8%가 색각 이상을 가지고 있습니다. 정보 전달을 색상에만 의존하지 마세요. 빨강/초록, 파랑/보라 조합을 피하세요.
OKLCH를 사용하여 지각적으로 균일한 색조 생성으로 접근 가능한 팔레트를 구축하세요. 모든 전경/배경 조합을 테스트하세요.
개발자를 위한 색상 모범 사례
팔레트를 디자인 토큰으로 정의하고 CSS 커스텀 프로퍼티에 매핑하세요. --color-text-primary 같은 시맨틱 이름을 사용하세요.
다크 모드는 단순한 밝기 반전이 아닌 별도 테마로 설계하세요.
컴포넌트 스타일에서 원시 HEX나 RGB 값을 직접 사용하지 마세요. 항상 토큰을 참조하세요.
애니메이션에는 oklch() 보간을 사용하여 더 부드러운 지각적 전환을 구현하세요.
자주 묻는 질문
HEX와 RGB 간 변환 방법은?
HEX→RGB: 코드를 세 쌍으로 나누고 각각을 16진 정수로 파싱합니다. RGB→HEX: 각 채널을 2자리 HEX 문자열로 변환하여 연결합니다. 온라인 도구로 즉시 변환할 수 있습니다.
웹 개발에 가장 좋은 색상 형식은?
HEX가 대부분의 프로젝트에서 기본입니다. HSL은 테마 설정에 더 적합합니다. 최신 브라우저에서는 oklch()가 지각적으로 균일한 색상과 P3 가뭇으로 최선의 선택입니다.
WCAG 색상 대비 요구사항은?
AA는 일반 텍스트 4.5:1, 큰 텍스트 3:1을 요구합니다. AAA는 7:1과 4.5:1. 비텍스트 요소는 최소 3:1이 필요합니다. 항상 대비 검사기로 테스트하세요.
색상 변환은 디자인 도구, CSS, 접근성, 백엔드 처리를 연결하는 기본 기술입니다. 무료 도구로 즉시 변환하세요.