-
카테고리
-
세부 분야
프론트엔드
-
해결 여부
해결됨
[실습결과] 이벤트 detail 활용 측면의 확장성과 관련된 질문입니다
21.03.02 17:26 작성 조회수 132
1
안녕하세요 김정환 강사님!
`꿀`보이스이셔서 귀 호강하며 잘 듣고있습니다.
강의를 1년전에 들었다면 수많은 시행착오를 안겪지 않았까 싶을 정도로 너무나 알찬 강의 감사드립니다.
다름이 아니라,
강의에는 없지만, 실습 과제 진행중에 잘 하고 있는건지, 확인을 받아보고 싶은 마음에 질문을 하게 되었습니다.
약간의 고도화? 를 거쳐 확장성을 부여하기 위해 고심끝에 아래와 같이 작성해 보았습니다.
대략적인 설명과 소스 코드의 일부분입니다.
이외는 강의 대용과 동일 합니다
-
FormView.js
- `Esc`키를 눌렀을떄 `취소`버튼과 동일하게 작동합니다.
- 내부 핸들러 onXxx 는 Event 객체와 둘째 인자로 detail 객체를 받아
View.emit(e, data) 메서드의 `data` 파라미터로 전달 합니다.
- detail .type | .key 속성은 각각 `Enter` `Esc` 키 와 `Button` 등으로
이벤트 발생지 Type 구분을 위해 쓰입니다.
- bindEvent() 에서 전달되는 detail 객체의 .content 속성은
사용자의 입력된 값을 담습니다.
-
-
-
bindEvent( ) {
const detail = { type: null, key: null, content: null }
}
this.on('submit', e => e.preventDefault())
this.inputEl.addEventListener('keyup', (e) => {
detail.type = 'key'
detail.key = e.key
this.onKeyUp(e, detail)
})
this.resetEl.addEventListener('click', (e) => {
detail.type = 'button'
this.onClickReset(e, detail)
}) -
onKeyUp( event , detail ) {
this.showResetBtn(this.inputEl.value.length)
}
const isValidText = this.inputEl.value.length;
if (!isValidText && event.key !== KEY_ENTER) {
this.onClickReset(event, detail)
return
}
detail.content = this.inputEl.value.trim()
switch (event.key) {
case KEY_ENTER:
isValidText && this.onSubmit(event,detail)
break
case KEY_ESC:
this.onClickReset(event,detail)
break
} -
onClickReset(event , detail ) {
this.showResetBtn(false)
}
this.inputEl.value = ''
this.inputEl.focus()
detail.content = ''
this.emit(EVENT_RESET, detail) -
onSubmit( event , detail ) {
this.emit(EVENT_SUBMIT, detail)
}
-
-
-
-
MainController.js
: 넘겨받은 CustomEvent. detail 속성으로 세부 조건 로직 작성이 가능합니다.
-
-
export default {
...
...
onSubmit(event) {
const { type, key, content } = event.detail
console.log(tag, `onSubmit() type=${ type } key=${ key }`, content)
},
onResetForm(event) {
const { type, key, content } = event.detail
console.log(tag, `onReset() type=${ type } key=${ key }`, content)
},
}
-
-
이런 식으로 FormView 외부에서 작동할 것을 대비해서
이벤트 디테일 객체를 외부로 전달하는 것이 맞는것인지, 혹은 더나은 방식과,
고쳐야 될 부분 에 대한 , 김정환 강사님의 조언을 구하고자 질문하게 되었습니다.
읽어주셔서 감사합니다!
답변을 작성해보세요.
1
김정환
지식공유자2021.03.03
FormView를 사용하는 MainController에서 좀더 세밀한 제어를 하기위한 의도인것 같네요. Truestar님 아이디어처럼 이벤와 함께 상세정보(detail)를 전달하는 아이디어도 좋은것 같습니다. 두 가지 첨언드릴게요.
1. 단일 detail 객체를 사용해서 각 이벤트 핸들러에서 수정하고 있는 부분이 조금 우려됩니다. keyup 이벤트 핸들러에서는 type과 key를 모두 설정하는 반면 click 핸들러에서는 type만 설정하기 때문에 잘못된 key 가 들어갈 여지가 있어 보여요. 리터럴 객체를 전달하거나 원시 타입을 여러개 전달하는 방식은 어떨까요?
- this.onKeyup(e, {type: '', key: '', content: ''})
- this.onKeyup(e, type, key, content)
2. FormView 가 이벤트 정보를 전달하는 것은 외부에서 사용하라는 의도 같은데요. MainController에서 이걸 사용하는 경우가 있을까요? 오히려 상세한 이벤트 동작은 FormView 내부에서 처리해서 감추는 것이 어떨까요?
0
Truestar
질문자2021.03.05
"각 세부 이벤트 핸들러에서 상세정보를 받는것은 좋은 아이디어" 라고 해주셔서 너무 보람찹니다^^
첨언 해주신 부분을 아래와 같이 적용해보았습니다.
아래는 질문은 아니구요, 결과물 정도로 생각해주시면 좋을것 같습니다^^
(질문한 시점 이후로 많은부분을 생각하며 리팩토링을 시도해 보았습니다)
- 말씀해주신 "리터럴 객체를 전달하거나 원시 타입을 여러개 전달하는 방식" 을
" 이벤트 데이터 사용측( event consumer ) 메서드 파라미터는 데이터묶음 보다는
개별적으로 명시적인 것이 유지보수에 용이하다" 는 것으로 이해를 했습니다.
이에 맞춰, 이벤트와 함께 던져지던 `detail = { ... }` 객체를 제거하고,
정보를 리터럴 형태로 꼭 필요한 Data 가 주입되도록 함수 시그니처를 바꿔보았습니다.
적용된 코드는 아래와 같습니다.
호출:
bindEvent() 메서드 내부,
this.inputEl.addEventListener('keyup', (e) => this.onKeyUp(e, e.key, 'key'))
선언:
this.resetEl.addEventListener('click', (e) => this.onResetForm(e, null, 'button'))
`keyup` 이벤트는 확정적이라 기본값 'key'를 할당하고, 나머지는 발생지가 바뀔수 있어서,
파라미터 기본값을 설정하지 않았습니다
FormView.onKeyUp = function(event, key, from = 'key') {
...
FormView.onReset = function(event, key, from) {
...
FormView.onSubmit = function(event, key, from) {
... - 저의 의도였던, FormView 가 외부로 FormEvent 를 전달(emit(...) 하는것은
const tag = '[FormView]'
컴포넌트간 결합도를 낮추는 방안으로 작성해 보았습니다.
...
...
FormView.onSubmit = function(event, key, from) {
this.emit(FormEvent.SUBMIT, {...})
}
(프레임웍만 사용하다 구현해보려니 막히는것과 모르는게 너무 많네요)
MainController 가 마치 옵저버 패턴의 "옵저버"는 아니지만 흡사하게 나마,
MainController 는 이벤트 송/수신 처리(딜리버) 역할 만 하고,
Form 이벤트가 이벤트를 발생시키면, 다른 컴포넌트에서 해당 이벤트에
반응하도록(반응성을 갖기위함) 작성 해보았지만,
역부족이라 아래와 같이 작성해 보았습니다.
(평상시 구상했던 것이지만, 맹점을 몰라서 한번 시도해 보았습니다) - 리팩토링 을 거친 FormView 입니다
( 학창시절 리포트 정도로 여겨주시면 너무나 감사할것 같습니다.
이런 코드는 JS 프로젝트인 경우 실무에서 사용이 가능할까요???ㅜㅜ)
추가기능 & 리펙토링 대략적인 목록
- MainContainer 에서 emit 을 하던 로직을 전부 일반 맴버메서드 호출로 바꾸었습니다.
- 입력값을 나타내는 `input` 을 `content` 에서 `keyword` 로 변경했습니다
- 3항 연산을 비롯해 각종 비교연산으로 작성되는 상태 값을, `State` 객체로 관리합니다.
- 입력데이터는 State.keyword 에 실시간으로 반영됩니다
- State 객체 속성들의 일부는 `input` 이벤트를 통해 실시간으로 변경됩니다.
- FormEvent .RESET .SUBMIT 은 input 값 변경이 없으면 중복 emit 하지 않습니다.(직전 키워드 비교)
import View, { ViewEvent } from './View.js'
/* fields */
const
tag = '[FormView]',
FormView = Object.create(View),//> OLOO 생성패턴 - https://jeonghwan-kim.github.io/2018/05/12/extended-component.html
State = {
reset: true,
isWriting: false,
keyword: null,//>향후 LocalStorage 값 가져오기
submitKeyword: null,//>향후 LocalStorage 값 가져오기
}
export const
Key = {
ENTER: 'Enter',
ESC: 'Escape',
}
/* methods */
FormView.setup = function(el) {
console.log(tag, 'setup()', el)
this.resetEl = el.querySelector('[type=reset]')
this.inputEl = el.querySelector('[type=text]')
this.inputEl.value = ''//> 향후 LocalStorage 에서 로딩
return this
.init(el)
.updateState()
.bindEvent()
.showResetBtn(false)
}
FormView.updateState = function() {
State.keyword = this.resetEl.value.trim()
State.submitKeyword = State.keyword
return this
};
FormView.bindEvent = function() {
this.on('submit', e => e.preventDefault())
this.inputEl.addEventListener('input', (e) => this.onInputForStateSync(e))//> https://developer.mozilla.org/en-US/docs/Web/API/InputEvent
this.inputEl.addEventListener('keyup', (e) => this.onKeyUp(e, e.key, 'key'))
this.resetEl.addEventListener('click', (e) => this.onReset(e, null, 'button'))
return this
}
FormView.showResetBtn = function(show = false) {
this.resetEl.style.display = show ? 'block' : 'none'
return this
}
/* handler methods */
// `반응성`개념 적용을 위한 키 입력값으로 상태값 설정
FormView.onInputForStateSync = function(event) {
State.keyword = this.inputEl.value.trim()
State.isWriting = !! State.keyword.length
this.showResetBtn(State.isWriting)
}
FormView.onKeyUp = function(event, key, from = 'key') {
const isResetBefore = ! State.reset
if (! State.isWriting && key !== Key.ENTER) {
return isResetBefore && this.onReset(event, key, from)
}
switch (event.key) {
case Key.ENTER:
State.isWriting && this.onSubmit(event, key, from)
return
case Key.ESC:
isResetBefore && this.onReset(event, key, from)
return
}
State.reset = false
}
// Reset 조건 - reset 이 이미 되어잇는경우 다시 리셋하지 않음.
FormView.onReset = function(event, key, from) {
console.log(tag, 'onResetForm()')
this.showResetBtn(false)
this.inputEl.value = ''
this.inputEl.focus()
State.keyword = ''
State.reset = true
! State.reset && this.emit(ViewEvent.RESET, { key, from })
}
// 전송조건 - 빈 문자 이거나, 동일 키워드가 아닌경우 전송됨
FormView.onSubmit = function(event, key, from) {
const isEmpty = ! State.keyword.length;
const isSameKeywordBefore = State.keyword === State.submitKeyword;
if (isEmpty || isSameKeywordBefore) return
console.log(tag, 'onSubmit() keyword:', State.keyword)
this.updateState()
this.emit(ViewEvent.SUBMIT, {
key,
from,
keyword: State.keyword,
})
}
export default FormView
강사님의 조언으로 많은 도움이 되었습니다
혹시 뭔가 떠오르는 것이 있으시다면, `옛다 이거 하나더 먹어라`는 마음으로
던저주신다면 감사히 경청하겠습니다.
읽어주셔서 감사합니다!
김정환
지식공유자2021.03.07
무척 열심히 공부하시는 것 같네요. 이런식으로 고민하시면서 구조를 개선해 나가는 건 좋다고 생각합니다. 실무에서도 '딱 이것이 최선이다'라는것은 찾기 어려운 것 같고요. 자주 코드를 들여다보고 변화에 따라 개선해 나가는 것이 방법이라고 생각합니다.
답변 2