← 모두의 툴

색맹 시뮬레이터의 숨은 버그, 왜 완전한 회색이 안 나올까

가이드 · 2026-08-19 최종 확인

색맹 시뮬레이터는 원본 이미지의 색상을 특정 색각 유형이 실제로 지각하는 색상으로 바꿔 보여주는 도구입니다. 이 변환은 보통 "색상 변환 행렬(color transformation matrix)"이라는 방식으로 구현되는데, 이 가이드에서는 그 원리와 함께 modoohub.com의 색맹 시뮬레이터 실제 소스코드를 직접 읽고 계산까지 검증해 찾아낸 구현 오류를 정확히 설명합니다.

1. 색맹의 종류 — 어떤 원추세포가 문제인가

사람의 색각은 망막의 세 가지 원추세포(L·M·S, 각각 빨강·초록·파랑 파장에 민감)가 받아들이는 신호 조합으로 결정됩니다. 이 중 하나가 없거나 기능이 떨어지면 색맹(色盲)이 됩니다. 적록색맹은 가장 흔한 유형으로, L 원추세포가 문제인 제1색맹(protanopia, 적색맹)과 M 원추세포가 문제인 제2색맹(deuteranopia, 녹색맹)으로 나뉩니다. 둘 다 원인 세포는 다르지만 결과적으로 빨강과 초록을 구별하기 어렵다는 공통점이 있습니다. 청황색맹(tritanopia, 제3색맹)은 S 원추세포가 문제로, 파랑과 노랑 계열을 혼동합니다. 발생률이 0.003% 수준으로 매우 드뭅니다. 마지막으로 전색맹(achromatopsia)은 세 원추세포 전부가 정상 작동하지 않아 색상을 전혀 구별하지 못하고 명암(밝기)만 인지하는, 발생률 0.001%의 극히 드문 유형입니다.

2. 왜 색상 변환 행렬로 시뮬레이션하는가

각 색각 유형은 원본 RGB 값을 그 유형이 실제로 지각하는 RGB 값으로 매핑하는 선형 변환으로 근사할 수 있습니다. 이 매핑을 숫자 표로 정리하면 R·G·B 각 출력 채널마다 "R 가중치 × 원본 R + G 가중치 × 원본 G + B 가중치 × 원본 B (+ 보정값)"이라는 하나의 방정식이 나오고, 세 채널이면 방정식 3개, 즉 표준적으로는 3행 4열(가중치 3개 + 보정값 1개씩) 형태의 행렬이 됩니다. 이 방식이 널리 쓰이는 이유는 픽셀 하나당 곱셈·덧셈 몇 번이면 끝나 이미지 전체를 실시간으로 처리할 수 있을 만큼 가볍기 때문입니다.

3. 실제 코드를 검증해보니 — 3×4 행렬을 3×3처럼 읽는 인덱싱 버그

color-blindness-simulator.html의 소스를 직접 열어 확인한 결과, 각 색각 유형의 행렬은 실제로 12개 숫자(3행 4열)로 정의돼 있었습니다. 예를 들어 전색맹 행렬은 [0.299,0.587,0.114,0, 0.299,0.587,0.114,0, 0.299,0.587,0.114,0]로, 표준 흑백 변환 공식(밝기 = 0.299R + 0.587G + 0.114B)이 R·G·B 세 출력 채널 모두에 동일하게 반복되는 정상적인 형태입니다. 그런데 이 행렬을 실제로 적용하는 applyMatrix() 함수를 보면 matrix[0]부터 matrix[8]까지, 즉 앞의 9개 값만 3×3 행렬처럼 순서대로 읽고 뒤의 3개 값(인덱스 9~11, 각 행의 보정값)은 아예 사용하지 않습니다.

이 어긋남이 실제로 어떤 결과를 낳는지 순수한 빨간색(255,0,0)을 넣어 직접 계산해보면 명확히 드러납니다. R 출력은 matrix[0..2]인 (0.299, 0.587, 0.114)를 그대로 써서 255×0.299 ≈ 76으로 의도한 값과 일치합니다. 문제는 G 출력부터입니다. 원래대로라면 두 번째 행 (0.299, 0.587, 0.114)를 써야 하는데, 코드는 matrix[3..5]인 (0, 0.299, 0.587)을 대신 읽어 255×0 = 0이 나옵니다. B 출력도 마찬가지로 matrix[6..8]인 (0.114, 0, 0.299)를 읽어 255×0.114 ≈ 29가 됩니다.

검증 결과: 순수한 빨간색(255,0,0)을 전색맹 필터에 넣으면 의도된 결과는 R·G·B가 모두 같은 완전한 회색 (76,76,76)이어야 합니다. 하지만 실제 코드를 계산기로 그대로 재현하면 (76,0,29)가 나오며, 이는 채널 값이 서로 다른, 여전히 색이 남아있는 픽셀입니다. 이 인덱싱 오류는 전색맹뿐 아니라 12개 값짜리 행렬 구조를 공유하는 나머지 모든 유형(제1·제2색맹, 청황색맹, 색약 포함)에도 동일하게 적용됩니다.

4. 그래서 이 도구, 얼마나 믿을 수 있나

결론부터 말하면 "행렬 기반이라 다소 근사치"라는 통상적인 설명과는 성격이 다른 문제입니다. 일반적인 근사 오차는 실제 색각 경험과 비슷하되 미세하게 다른 정도지만, 이 경우는 행렬 데이터의 정렬 자체가 코드에서 어긋난 것이라 특정 채널이 체계적으로 잘못된 계수와 섞여 계산됩니다. 빨강 계열 색상이 전반적으로 어두워지는 큰 방향성 정도는 여전히 어느 정도 드러나므로 대략적인 감을 잡는 용도로는 참고할 수 있지만, 정확한 색조·채도까지 신뢰하기는 어렵습니다. 실제 접근성 검토가 중요한 프로젝트라면 이 결과만으로 판단하지 말고 다른 검증된 색맹 시뮬레이션 도구와 교차 확인하는 것이 안전합니다.

자주 묻는 질문

Q. 색맹 시뮬레이터는 왜 색상 변환 행렬로 만드나요?

A. 각 색각 유형은 원추세포가 특정 파장에 얼마나 반응하는지가 다르기 때문에, 원본 R·G·B 값을 그 유형이 실제로 지각하는 R·G·B 값으로 바꾸는 선형 변환식으로 근사할 수 있습니다. 이 변환식을 숫자 표로 정리한 것이 색상 변환 행렬이며, 계산이 단순하고 빨라서 실시간 이미지 처리에 널리 쓰입니다.

Q. 전색맹(achromatopsia) 시뮬레이션이 완전한 회색을 만들지 못하는 이유는 정확히 뭔가요?

A. color-blindness-simulator.html의 전색맹 행렬은 R·G·B 출력마다 4개 값(가중치 3개+보정값 1개)씩 3행 4열, 총 12개 숫자로 정의돼 있습니다. 하지만 실제 계산 함수 applyMatrix()는 이를 단순 3×3 행렬로 착각해 앞의 9개 값(인덱스 0~8)만 읽고 나머지 3개(인덱스 9~11)는 쓰지 않습니다. 그 결과 G·B 채널 계산에 들어가는 계수가 한 칸씩 밀려 순수한 빨간색(255,0,0)을 넣었을 때 의도된 (76,76,76)이 아니라 (76,0,29)라는 여전히 색이 남은 값이 나옵니다.

Q. 이 버그는 전색맹에만 영향을 주나요?

A. 아닙니다. 정상 시각을 제외한 모든 유형(제1·제2색맹, 청황색맹, 색약 포함)이 동일한 12개 값짜리 행렬 구조와 동일한 applyMatrix() 함수를 거치므로 전부 같은 인덱싱 오류의 영향을 받습니다. 전색맹에서 유독 눈에 띄는 이유는 정답이 완전한 무채색이라 오차가 육안으로 바로 드러나기 때문일 뿐입니다.

Q. 그래도 디자인 검토용으로 쓸 가치가 있나요?

A. 빨강 계열 색상이 전반적으로 어두워지는 큰 방향성은 여전히 어느 정도 드러나지만, 정확한 색조나 채도까지 신뢰하기는 어렵습니다. 접근성 검토가 실제로 중요한 프로젝트라면 이 결과만으로 판단하지 말고 다른 검증된 도구와 함께 교차 확인하는 것을 권장합니다.

Q. 적록색맹, 청황색맹, 전색맹은 각각 무엇이 다른가요?

A. 적록색맹은 빨강 또는 초록을 감지하는 원추세포가 문제인 유형(제1·제2색맹)이고, 청황색맹은 파랑을 감지하는 원추세포가 문제라 파랑과 노랑을 혼동하는 매우 드문 유형(0.003%)입니다. 전색맹은 세 원추세포 전부가 정상 작동하지 않아 색상 자체를 구별하지 못하고 명암만 인지하는, 발생률 0.001%의 극히 드문 유형입니다.