ProPro의 프로필 페이지에는 지역, 직무등 여러 개의 select tag가 사용되고 있다.
현재는 하나의 Form 컴포넌트에서 각 select tag의 옵션들을 동적으로 생성하고 이벤트를 달아주는 일을 하고 있는데 select tag의 옵션들을 생성하는 단계에서 중복적으로 보이는 코드가 존재하고 Form 컴포넌트에서 너무 많은 일을 하고 있는 것 같아서 Selector라는 컴포넌트를 생성하여 분리하고자 한다.
renderCallback() {
const sidoSelect = this.container.querySelector('.sido-select');
const positionSelect = this.container.querySelector('.position-select');
this.$sidos = new DocumentFragment();
defaultSido.forEach(sido => {
const opt = createDom('option', {
value: sido,
innerHTML: sido,
});
if (sido === this.props.userInfo.region.sido) opt.selected = true;
this.$sidos.appendChild(opt);
});
this.$positions = new DocumentFragment();
defaultPosition.forEach(position => {
const opt = document.createElement('option');
[opt.value, opt.innerHTML] = position;
if (this.props.userInfo.position === position[0]) opt.selected = true;
this.$positions.appendChild(opt);
});
sidoSelect.appendChild(this.$sidos);
positionSelect.appendChild(this.$positions);
}
setEvent() {
const sidoSelect = this.container.querySelector('.sido-select');
const positionSelect = this.container.querySelector('.position-select');
sidoSelect.addEventListener('change', event => {
this.props.onChangeUserInfo({
...this.props.userInfo,
region: {
sido: event.target.value,
sigungu: '',
},
});
this.sigunguChange(event.target.value);
});
positionSelect.addEventListener('change', event => {
this.props.onChangeUserInfo({
...this.props.userInfo,
position: event.target.value,
});
});
}
우선, 모든 select tag는 옵션 목록들을 받아 체크유무만 확인하여 옵션을 생성하는 과정으로 진행되기 때문에 이런 중복적인 부분들을 제거하기 위해 생성해야할 옵션들을 props로 받아 옵션들을 생성하는 컴포넌트를 만들었다.
class Selector extends CustomComponent {
markup() {
const { items, defaultOption, selectedItem } = this.props;
return `
<option value="" selected disabled hidden>${defaultOption}</option>
${items
.map(
item => `
<option value="${item}"
${item === selectedItem ? 'selected' : ''}>${item}</option>
`,
)
.join('')}
`;
}
setEvent() {
const { onChange } = this.props;
this.container.onchange = onChange;
}
// 생략...
}
그리고 해당 컴포넌트에서 select tag의 옵션들이 선택될 때마다 호출되기 위한 이벤트 핸들러를 달아준다.
이벤트 핸들러 등록 방식으로 addEventListener()를 사용하지 않은 이유는 시/군에 따라 시/군/구 select tag가 바뀌는데 바뀔때마다 select 컴포넌트가 생성되면서 이벤트를 달아주기 때문에 여러 개의 이벤트 핸들러가 달리게 되는 상황이 발생하여 하나의 이벤트만 달 수 있는 프로터피 방식을 사용하게 되었다.
class Selector extends CustomComponent {
// 생략...
setEvent() {
const { onChange } = this.props;
this.container.onchange = onChange;
}
}
만들고보니 마크업과 이벤트 핸들러 등록을 하위 컴포넌트에서 작업하는 것 말고는 바뀐게 없는 것 같은데 그래도 이전에 비해서 중복되는 코드가 통일되고 필요한 정보들이 한 곳에 몰려있다보니 코드가 깔끔해지고 흐름을 이해하기 쉬워지게 된 것 같다.
renderCallback() {
new Selector({
container: sidoSelect,
props: {
defaultOption: '시/도',
items: defaultSido,
selectedItem: this.props.userInfo.current.region.sido,
onChange: event => this.handleSidoChange(event),
},
});
new Selector({
container: positionSelect,
props: {
defaultOption: '직무를 선택하세요',
items: defaultPosition,
selectedItem: this.props.userInfo.current.position,
onChange: event => this.handlePostionChange(event),
},
});
}