JavaScript 계산된 프로퍼티 이름: [표현식]: 값 이해하기
객체를 만들 때 프로퍼티 이름을 미리 정할 수 없다면 어떻게 할까? 예를 들어 서버에서 받은 모듈 번호에 따라 MODULE_1 또는 MODULE_2의 상태를 갱신해야 하는 상황이다.
setEquipmentStatus((prev) => ({
...prev,
[moduleNoToKey(payload.moduleNo)]: mapEquipmentStatusEvent(payload),
}));
핵심은 [moduleNoToKey(payload.moduleNo)]다. 대괄호 안의 표현식을 평가한 결과가 새 객체의 프로퍼티 이름이 된다. 이를 계산된 프로퍼티 이름(Computed Property Name) 이라고 한다(ES2015).
기본 문법
const key = 'MODULE_1';
const status = { [key]: '가동 중' };
console.log(status); // { MODULE_1: '가동 중' }
대괄호를 빼고 key: '가동 중'이라고 쓰면 변수 값이 아니라 문자 그대로 key라는 이름의 프로퍼티가 만들어진다.
대괄호 안에는 함수 호출이나 연산식도 넣을 수 있다.
const moduleNoToKey = (moduleNo) => `MODULE_${moduleNo}`;
const status = { [moduleNoToKey(2)]: '대기 중' };
console.log(status); // { MODULE_2: '대기 중' }
평가 결과가 문자열이 아니면 문자열로 변환된다({ [1 + 1]: 'x' } → { '2': 'x' }). Symbol은 예외로, 그대로 심볼 키가 된다.
처음 코드 분석
[moduleNoToKey(payload.moduleNo)]: mapEquipmentStatusEvent(payload)
payload.moduleNo가 2라면 개념적으로 다음과 같다.
MODULE_2: mapEquipmentStatusEvent(payload)
왼쪽은 어느 모듈의 상태를 저장할지, 오른쪽은 저장할 값이다. 대괄호 없이 moduleNoToKey(payload.moduleNo): ...처럼 쓰는 것은 문법 오류다.
setEquipmentStatus 전체 흐름
prev는 직전 상태 객체다....prev로 기존 프로퍼티를 새 객체에 복사한다.- 계산한 키에 새 값을 넣는다. 같은 키가 이미 있으면 덮어쓴다(나중에 쓴 쪽이 이김).
- 새 객체를 React 상태로 넘긴다. 참조가 바뀌므로 리렌더링된다.
const prev = { MODULE_1: '가동 중', MODULE_2: '대기 중' };
const moduleNo = 2;
const next = { ...prev, [`MODULE_${moduleNo}`]: '가동 중' };
console.log(next); // { MODULE_1: '가동 중', MODULE_2: '가동 중' }
MODULE_1은 유지되고 MODULE_2만 바뀐다.
TypeScript에서
moduleNoToKey의 반환 타입을 `MODULE_${number}`나 'MODULE_1' | 'MODULE_2'처럼 좁혀 두면, 상태 타입이 Record<ModuleKey, Status>일 때 잘못된 키를 만드는 실수를 컴파일 단계에서 잡을 수 있다. 반환 타입이 string이면 결과 객체의 타입이 { [x: string]: ... }로 넓어질 수 있다.
요약
[표현식]: 값은 표현식의 결과를 프로퍼티 이름으로 쓰는 문법이다. 위 코드는 이벤트의 모듈 번호로 키를 정하고, 기존 상태를 복사한 새 객체에서 그 모듈의 상태만 갱신한다.