업무 중 Color Picker 기능을 추가해야 할 일이 있었습니다. 아래와 같이 버튼 UI에 선택된 색상을 버튼 텍스트에 16진수(HEX) 형식으로 표현해야 했습니다.

처음에는 텍스트 색상을 흰색으로 고정하여 구현했으나, 선택된 색상이 텍스트와 비슷할 경우 텍스트가 잘 보이지 않는 문제가 발생했습니다.
아이디어가 떠오르지 않아 claude AI를 도움받아 로직 구현 및 공부 해보기로 했습니다.
최종 코드는 아래와 같습니다. 이를 바탕으로 로직을 이해해보려고 합니다.
const getContrastColor = (hexColor: string) => {
if(!hexColor) return '#000000'
const r = parseInt(hexColor.slice(1, 3), 16);
const g = parseInt(hexColor.slice(3, 5), 16);
const b = parseInt(hexColor.slice(5, 7), 16);
const brightness = (r * 299 + g * 587 + b * 114) / 1000;
return brightness > 128 ? '#000000' : '#FFFFFF';
};
const r = parseInt(hexColor.slice(1, 3), 16);
const g = parseInt(hexColor.slice(3, 5), 16);
const b = parseInt(hexColor.slice(5, 7), 16);
16진수 색상값은 #FFFFFF처럼 정의되며, # 뒤의 2자리씩을 각각 Red, Green, Blue에 대응합니다. 이 값들은 8비트 색상 깊이를 사용하는 RGB 색상 모델을 기반으로 합니다.
const brightness = (r * 299 + g * 587 + b * 114) / 1000;
위 코드는 해당 색상의 밝기를 계산하는 공식입니다.
여기서 매직넘버는(299,589,114) 각각의 색상에 가중치를 부여하는 값입니다.
가중치들은 NTSC(National Television System Committee)에서 정의한 표준을 기반으로 합니다. 이는 흑백 TV에서 컬러 신호를 흑백으로 변환할 때 사용되던 공식에서 유래했습니다.
다시 계산식으로 돌아와서
1000으로 나눈 이유는 단순히 계산을 쉽게 하기 위한 것으로, 중요한 부분은 각 색상에 부여된 가중치 비율입니다. (299 + 587 + 114 = 1000)
return brightness > 128 ? '#000000' : '#FFFFFF';
brightness 값은 0에서 255 사이의 값을 가지며 '밝음' 과 '어두움' 두가지로 간단히 분류하여 중간인 128값과 비교합니다.
128보다 크면 현재 색상이 밝다는 뜻으로 텍스트 색상은 검은색(#FFFFFF), 그렇지 않으면 흰색(#000000)으로 반환합니다.
반환된 컬러 값으로 css에 적용하면 의도대로 잘 동작하는것을 확인 할 수 있다.
많이 느리시네요 홧팅입니다 ^^