바닐라 자바스크립트로 프로젝트를 할 때 비중이 큰 작업 중 하나가 이벤트 위임을 사용해서 이벤트를 잡아내는 것이었다.
app이라는 최상위 태그에 click 이벤트 리스너를 달아놓고 event target에 따라 이벤트 핸들러를 실행하는 방식을 사용했다.
이 때문에 이벤트 리스너의 콜백에서 아주 많은 분기처리가 필요했는데 그때 조건문을 다루면서 코드를 개선시킨 경험을 정리해보겠다.
사실 아래의 상황이 되기 전에 코드를 개선하긴 했지만 조건문을 개선하지 않았다면 아래와 같은 코드를 짜게 되었을 것이다. 보기만 해도 어지럽다...
import * as handler from "../handler";
const {
main: { column },
history,
header,
} = handler;
const onClick = ({ target }) => {
if (target.classList.contains("js-openHistory")) {
header.showHistory(target);
} else if (target.classList.contains("js-closeHistory")) {
history.closeHistory(target);
} else if (target.classList.contains("js-addCardBtn")) {
column.openAddCardForm(target);
} else if (target.classList.contains("js-editCardBtn")) {
column.card.openEditCardForm(target);
} else if (target.classList.contains("js-deleteCardBtn")) {
column.card.clickDeleteCard(target);
} else if (target.classList.contains("js-addFormCancel")) {
column.cardForm.closeAddCardForm(target);
} else if (target.classList.contains("js-editFormCancel")) {
column.cardForm.closeEditCardForm(target);
} else if (target.classList.contains("js-deleteHistory")) {
history.deleteHistory(target);
} else if (target.classList.contains("js-deleteCancel")) {
column.card.cancelDeleteCard(target);
} else if (target.classList.contains("js-deleteConfirm")) {
column.card.deleteCard(target);
}
};
위와 같이 많은 조건들을 처리할 때 조금 더 가독성을 높일 수 있는 것이 switch case문인데 이를 이용해서 코드를 개선해보면 아래와 같다.
전보다 조금 더 나아지긴 했지만 여전히 반복이 많고 코드의 흐름을 파악하려면 위에서부터 타고내려오면서 읽어야한다는 단점이 있다.
또한 조건이 추가될 때 또다시 case와 break를 써야한다.
import * as handler from "../handler";
const {
main: { column },
history,
header,
} = handler;
const onClick = ({ target }) => {
const className = target.classList[0];
switch (className) {
case "js-openHistory":
header.showHistory(target);
break;
case "js-closeHistory":
history.closeHistory(target);
break;
case "js-addCardBtn":
column.openAddCardForm(target);
break;
case "js-editCardBtn":
column.card.openEditCardForm(target);
break;
case "js-deleteCardBtn":
column.card.clickDeleteCard(target);
break;
case "js-addFormCancel":
column.cardForm.closeAddCardForm(target);
break;
case "js-editFormCancel":
column.cardForm.closeEditCardForm(target);
break;
case "js-deleteHistory":
history.deleteHistory(target);
break;
case "js-deleteCancel":
column.card.cancelDeleteCard(target);
break;
case "js-deleteConfirm":
column.card.deleteCard(target);
break;
default:
break;
}
};
가독성을 더 높이고 추가 삭제가 용이한 방법을 찾아본 결과 handlerMap 객체를 만들어서 사용하는 방법을 알게되었다.
핸들러 함수와 target의 선택자를 매핑하는 방식으로 가독성을 높이고 핸들러의 추가 삭제가 쉽도록 했다.
import * as handler from "../handler";
const {
main: { column },
history,
header,
} = handler;
const clickHandlerMap = {
"js-openHistory": header.showHistory,
"js-closeHistory": history.closeHistory,
"js-addCardBtn": column.openAddCardForm,
"js-editCardBtn": column.card.openEditCardForm,
"js-deleteCardBtn": column.card.clickDeleteCard,
"js-addFormCancel": column.cardForm.closeAddCardForm,
"js-editFormCancel": column.cardForm.closeEditCardForm,
"js-deleteHistory": history.deleteHistory,
"js-deleteCancel": column.card.cancelDeleteCard,
"js-deleteConfirm": column.card.deleteCard,
};
const onClick = ({ target }) => {
const executeHandler = clickHandlerMap[target.classList[0]];
if (executeHandler) {
executeHandler(target);
}
};
else if를 코드에서 줄이는 것이 좋다는 말을 많이 들었는데 다양한 방법으로 조건문을 다뤄보니까 왜 else if를 줄여야하는지 조금 더 와닿았다.