현재 근무 중인 회사에서 데이터를 수정하거나 추가할때 서버와의 통신에서 콜백함수로 처리하는 부분이 상당히 많다. 옆자리의 사수이자 동료인 재훈님께서 물어볼때마다 친절하게 설명을 해주셨지만 정작 나는 볼때마다 정리가 다르게 되서 이번에 확실히 여쭈어보고 문서로 정리하기로 했다. 콜백 함수, Promise, async / await 등 비동기 처리 방식에 대한 정리는 나중에 자세하게 하고 우선 적어도 내가 오늘 본 코드에 안에서 콜백 함수란 것이 어떻게 작동하고 왜 작동하는지 나중에 잊지 않기 위해 적어볼 것이다.
회사 코드이기 때문에 그대로 가져다 쓰는 건 아닌 것 같아서, 비슷한 이름이지만 같은 로직으로 작성해보겠다. 내가 오늘 살펴본 코드는 제품의 정보를 수정하는 컴포넌트이다. 구조는 제품을 수정하는 컴포넌트(부모)에서 실질적인 input form이 있는 컴포넌트(자식)의 유효성 검사(validation)을 하는 함수를 가져다가 유효하다고 판단되었을 시에, 버튼을 클릭하면 실행되는 submit 함수에서 실질적으로 서버에 전송을 하는 것으로 이루어져있다.
//parent-component
submit() {
/* ... */
if (this.$children.find(el => el.vueName)) {
this.$children
.find(el => el.vueName == "modifyInfo") // 자식 컴포넌트에 접근
.checkForm(result => { //
this.sendObj(result); //콜백 함수
}); //
}
}
사실 자식 컴포넌트에 접근하는 저 부분도 몰라서 질문을 드렸던 부분이다. Vue 객체를 새로이 정의 할때 $children이라는 속성이 내장되어있으며 해당 속성으로 현재 컴포넌트의 자식 컴포넌트에 접근, 그 자식 컴포넌트의 함수에도 저렇게 접근이 가능하다. 신기하다고 느꼈다! 어찌 되었든 콜백 함수가 실행되는 부분은
checkForm(result => {
this.sendObj(result);
});
이 부분이다.
자식 컴포넌트의 checkForm이라는 함수에 접근하여
result => { this.sendObj(result)}
라는 함수 자체를 parameter로 전달 한다. 이부분이 중요한데, 익명 함수 자체를 parameter로 넘기기 때문에
parameter인 함수가 실행되기전에는 checkForm이라는 함수는 실행이 되지 않을 것이다. 왜냐하면 해당 함수는 아직 parameter가 정해지지 않았기 때문이다. 여기서 익명 함수(Arrow Function)으로 작성이 되어 parameter가 result인가? 하며 헷갈렸었는데,
function(result) {
this.sendObj(result)
}
재훈님께서 이해를 돕기위해 위와 같이 ES5 함수형으로 작성을 해주셔서 이해가 잘 되었다. (지금 보면 정말 차이점이 없는데 조삼모사의 원숭이 같다..) 부모 컴포넌트에서 자식 컴포넌트인 checkForm 함수에 접근하였으니 이제 그 함수를 살펴 보겠다.
//child-component
/* ... */
checkForm(cb){
/* validation */
if(isAllValid) {
cb(this.editorData()) //input form 중 하나인 text editor의 내용
}
},
editorData(){
/* ... */
}
위에서 부모 컴포넌트에서 접근한 자식 컴포넌트의 checkForm 함수의 모습이다. 위에서 parameter로 넘겼던 익명함수,
result => { this.sendObj(result)}
를 cb라는 이름으로 전달받는 것을 확인할 수 있다. 해당 함수는 아까 말했듯 validation을 하는 함수기 때문에 모든 validation 이 끝나고 모두 유효하다는 것을 확인하는 isAllValid라는 변수가 true일때 그 때, checkForm의 parameter를 결정(?)짓는 것이다. 자신의 함수의 parameter를 자신의 함수에서 결정짓는 다는 것인데 이 부분이 이해가 잘안갔었다.
결과적으로 이 부분, 모든 validation이 통과 하였을 때
checkForm의 parameter로 전달받은 함수( result => { this.sendObj(result)})
의 parameter result에 this.editorData()를 전달하기 때문에, 언제 끝나고 시작할 지 모르는 비동기적 방식이 아닌, 동기적으로 함수의 parameter를 넘기는 시점을 내가 정하여, (이 경우 isAllValid가 true일 때 parameter cb의 parameter를 결정) 함수가 실행되는 순서를 정할 수 있다는 것이다.
cb는 언뜻보면 단순한 변수의 일종으로 볼수 있지만 (재훈님께서 typescript로 해당 parameter의 type을 지정해 놓으면 보기편하고 이해하기 쉬울 것 같다고 말하셨음) 위에서 말했듯이 함수이기 때문에
cb(this.editorData())
이 parameter인 함수, cb의 parameter, this.editorData()는 사실,
result => { this.sendObj(result)} // = cb
위의 함수(cb로 받은)에서 result에 들어가게 되는 것이다. 그리고 다시 부모 컴포넌트의 submit 함수로 돌아가자면
//parent-component
submit(){
/* ... */
checkForm(result => {
this.sendObj(result);
});
/* ... */
}
위의 부분은
//parent-component
submit(){
/* ... */
checkForm(this.editorData() => {
this.sendObj(this.editorData())
})
/* ... */
}
결과적으로 위와 같다고 볼 수 있게 된것이다.
지금 다시 작성을 하면서도 헷갈리고 설명이 기이하게 길어지는 부분이 있는데, 중요한 점은
1. 함수 자체를 parameter로 전달하는 것이 callback 함수이다
2. 익명 함수를 전달하기에 parameter로 전달하는 parameter를 결정하는 시점을 정할 수 있으며, 이것으로 실행되는 함수의 순서를 정할 수 있다
이렇게 정리해보겠다. 잘못 알고있는 부분이 있다면 나중에 수정할 생각이다.
'TIL' 카테고리의 다른 글
SCSS(Sass) 사용법 (1) | 2020.10.20 |
---|---|
CSS-in-JS (styled-components, JSS, glamorous ...) (1) | 2020.10.17 |
[Vue 플러그인] vetur (0) | 2020.09.17 |
[에러 해결] MySQL 서버 접속 불가 (0) | 2020.09.12 |
번들러 란 (모듈 번들러, webpack) (0) | 2020.09.03 |