도리도리s
222
2020-07-31 18:51:29 작성 2020-07-31 18:53:22 수정됨
5
799

Vue.js 라이프사이클 훅 문제 해결 부탁드립니다.



<script>
import axios from 'axios';

let keywordListArray = [];
export default {
  data() {
    return {
      items: [],
      targetKeyword: {}
    }
  },
  created() {
    console.log("LifeCycleHook: created");
    this.removeItems();
    console.log("checkpoint 1");
    this.callSearchApi();

    console.log("checkpoint 2");
    console.log("keywordListArray length:", keywordListArray.length);

    console.log("checkpoint 3");
    this.items = keywordListArray;
    console.log("this.items info:", this.items);

    console.log("checkpoint 4");
    this.targetKeyword = keywordListArray[0];
    console.log("this.targetKeyword info:", this.targetKeyword);
    console.log("checkpoint 5");

  },
  beforeUpdate() {
    console.log("LifeCycleHook: beforeUpdate");
    console.log("keywordListArray length:", keywordListArray.length);
    this.targetKeyword = keywordListArray[0]
    console.log("this.targetKeyword info:", this.targetKeyword);
    console.log("checkpoint 6");

  },
  methods: {
    removeItems: function () {
      console.log("checkpoint 7");
      keywordListArray = [];
      console.log("keywordListArray init complete!");
    },
    callSearchApi: function () {
      console.log("checkpoint 8");
      axios.get('http://localhost:5000/api/v1/searchs', {
        params: {
          hintKeywords: this.$route.params.keyword
        }
      })
          .then((result) => {
            {
              let keywordList = result.data.keywordList;
              for (const i in keywordList) {
                keywordListArray.push(keywordList[i]);
              }
              console.log("keywordListArray push complete!");
            }
          })
          .catch(function (error) {
            console.log(error);
            alert('Network Error');
            return false;
          })
          .then(function () {
            console.log('finish ApiConnection!');
          });
    }

  },
  name: "SearchResult"
}
</script>



이게 문제의 코드와 실행 순서 결과인데요

Vue의 라이프사이클을 봤을 때

created 단계에서 removeItems(배열 초기화 메서드) 이후에 callSearchApi 메서드를 호출합니다.

그런데 callSearchApi는 axios를 통한 비동기 요청이므로 callSearchApi 메서드 이후 코드인

checkpoint2부터 checkpoint5까지 먼저 코드 진행이 되는 것으로 알고 있습니다.


때문에 크롬 개발자 도구에서도 보면 당연히 아직 axios 요청 전이므로

코드에서 checkpoint2 이후 코드인 아래 결과가 0이 찍히고(Q1)

 console.log("keywordListArray length:", keywordListArray.length);


checkpoint4 이후 코드인 아래 결과가 undefined(Q2)가 나오는 것이 이해가 됩니다.

console.log("checkpoint 4");
    this.targetKeyword = keywordListArray[0];
    console.log("this.targetKeyword info:", this.targetKeyword);


그렇다면 checkpoint3 이후 코드인 아래의 경우 keywordListArray는 length가 0인 빈배열인데

왜 개발자도구에선 (Q3) 데이터가 출력이 되는 것인가요??

console.log("checkpoint 3");
    this.items = keywordListArray;
    console.log("this.items info:", this.items);


콘솔창에 따르면

 checkpoint5까지 먼저 코드가 실행되고 나서 axios 비동기 요청이 찍히는데요

해당 비동기 요청으로 받은 데이터를 배열에 push 하는 과정을 통해


Vue 인스턴스가 관찰하고 있던 데이터 items에 변화가 생긴것은 axios 요청에서

배열에 push하기 전인데요...

라이프사이클에 따른다면 checkpoint 3, 사진에서 Q3에 마찬가지로 undefined가 나온 후

beforeupdate 단계에서 Q4와 마찬가지로 변화된 데이터가 감지되어야 하는 것 아닌가요??

해결이 안되서 질문드립니다!

0
  • 답변 5

  • seacont
    2020-08-01 00:30:48 작성 2020-08-01 00:52:30 수정됨

    콘솔창의 this.items info: ... 오른쪽에 있는 [i] 버튼에 마우스 커서를 대면 "Value below was evaluated just now." 라고 뜹니다. 자바스크립트 코드가 모두 실행되고 난 현재의 값을 표시한다는 의미인 것 같네요.

    해결책으로는 아래와 같이 해보세요.

    console.log("checkpoint 3");
    this.items = keywordListArray;
    console.log("this.items info:", JSON.parse(JSON.stringify(this.items)));

     

  • 도리도리s
    222
    2020-08-01 03:05:13

    @seacont 님 늦은 시간에 답변 감사드립니다.

    말씀하신 것처럼 위 자바스크립트 코드가 다 실행되고 console.log에 값이 출력되는 것이라고 생각하고 console.log가 아닌 alert를 찍었을 때 마찬가지로 length가 0인 것을 확인했습니다.

    근데 제가 의아한 건 결국 비동기 요청이 끝나고

    beforeupdate 단계에서 이 keywordListArray에 대한 데이터 변경이 감지되었기 때문에

    Vue 인스턴스에서 items에 대한 데이터 변경을 감지하고 아래 사진처럼 dom을 변경시킬 수 있었다는 거아닌가요??


    그렇다면 마찬가지로 befreupdate 단계에서 이 변경된 데이터가 감지되었다는 것이고

    그 과정에서 keywordListArray의 첫번째 인덱스를 받아올 수 있게 되었고 

    마찬가지로 created 라이프사이클에서 아래 코드와 같이 keywordListArray[0]에 대한 자바스크립크가 실행된 이후 변경된 데이터가 적용되어서 


    console.log("checkpoint 4");
    this.targetKeyword = keywordListArray[0];
    console.log("this.targetKeyword info:", this.targetKeyword);


    this.targetKeyword 부분도 undefined가 아닌 배열의 첫번째 인덱스를 가리켜야 하는게 아닌가 해서요..

  • yeori
    3k
    2020-08-01 03:58:25
    그런데 callSearchApi는 axios를 통한 비동기 요청이므로
    callSearchApi 메서드 이후 코드인
    checkpoint2부터 checkpoint5까지 먼저
    코드 진행이 되는 것으로 알고 있습니다.

    비동기로 함수를 호출하면 순서는 보장되지 않습니다.

    위처럼 로컬호스트의 서버를 호출하면 요청에 대한 응답이 빨리 올테니까 c2~c5 사이에서 비동기 함수 호출의 응답이 도착할 수 있고,

    외부의 실서버로 요청을 보내면 지연시간이 길어서 c2~c5 이후에 도착할 수도 있습니다.

    비동기라고해서 호출한 쪽이(created) 호출된 쪽보다(api call) 먼저 실행되지는 않습니다.

    그때그때 다릅니다.

  • seacont
    2020-08-01 08:25:26

    각 console.log() 함수들은 자바스크립트 내의 코드 실행 순서에 따라 이미 실행이 되었습니다. 캡처화면을 보시면 각각이 실행된 순서를 알 수 있습니다.

    한편 console.log()의 파라미터에 인자가 전달될 때, 그 인자가 객체(object, array, function 등)일 경우에는 참조를 전달하고 원시값(number, string 등)일 경우에는 값을 전달합니다. 값이 전달된 경우에는 크롬 개발자도구에서 해당 값을 console.log()가 실행된 시점에서 같이 출력을 해주나, 참조가 전달된 경우에는 그렇게 하지 않습니다. 즉, 해당 참조를 찾아가 그 값을 evaluate한 후 출력을 해줘야 하는데 그렇게 해주지 않습니다. 대신 오른쪽을 향한 삼각형 화살표 버튼과 {...} 등으로만 표시해줍니다. 이는 사용자가 해당 버튼을 누를 때 그 값을 evaluate하겠다는 의미입니다. 해당 삼각형 화살표 버튼을 클릭하면, 클릭 시점에서의 객체의 값을 그때서야 evaluate해서 출력해줍니다. 보통은 자바스크립트 코드가 다 실행된 후에서야 버튼을 클릭하는 것이 가능하므로, 위 답변에서 출력값은 "자바스크립트가 코드가 모두 실행되고 난 현재의 값"이라고 말씀드린 것이었습니다.

    console.log("checkpoint 4");
    this.targetKeyword = keywordListArray[0];
    console.log("this.targetKeyword info:", this.targetKeyword);

    위 코드 실행시점에서 keywordListArray는 []이므로 keywordListArray[0]의 값은 undefined가 됩니다. 즉 원시값이죠. 따라서 console.log()의 출력값은 console.log() 실행시점의 값인 undefined가 됩니다.

    console.log("checkpoint 3");
    this.items = keywordListArray;
    console.log("this.items info:", this.items);

    위 코드 실행시점에서 keywordListArray는 []이므로 this.items의 값은 []가 됩니다. 배열이죠. 따라서 console.log()의 출력값은 console.log() 실행시점의 값인 []가 되어야 하나, 위에서 말씀드린대로 크롬 개발자도구의 특성상 해당 배열의 값은 아직 evaluate되지 않았고, 사용자가 화살표 버튼을 클릭하면 언제든 evaluate하겠다는 의미로 화살표 버튼이 표시됩니다. 사용자가 버튼을 누르는 시점은 finish ApiConnection!까지 출력된 이후이므로 그 이후의 keywordArray의 값이 evaluate되어 출력이 됩니다.

    console.log()가 값을 콘솔창에 출력할 때, console.log() 실행시점에서의 값을 즉시 출력하도록 하고 싶으면 console.log()의 인자로 원시값을 전달하거나, JSON.parse(JSON.stringify(this.items))처럼 console.log() 실행시점의 this.items의 값을 deep copy해서 전달해야 합니다. JSON.parse(JSON.stringify(this.items))도 객체이므로 화살표 버튼 클릭시점의 값이 출력되나, 해당 객체는 this.items와 같은 객체가 아닌 this.items와 내용만 같은 전혀 다른 객체이고 따라서 위의 자바스크립트 코드가 모두 실행되어도 그 값이 변하지 않기 때문에 console.log() 실행시점의 값을 확인하는 효과를 볼 수 있습니다.

    Vue 인스턴스에서 items에 대한 데이터 변경을 감지하고 아래 사진처럼 dom을 변경시킬 수 있었다는 거아닌가요?? ... 마찬가지로 created 라이프사이클에서 아래 코드와 같이 keywordListArray[0]에 대한 자바스크립크가 실행된 이후 변경된 데이터가 적용되어서 

    크롬 개발자도구에서 출력되는 객체값이, console.log() 실행시점의 값이 아닌, 화살표 버튼 클릭시점에서의 값인 것은 Vue가 DOM을 변경시킨 것과는 무관한 크롬만의 문제입니다.

  • 도리도리s
    222
    2020-08-02 12:54:56

    @yeori, @seacont 님 답변 감사합니다.

    그냥 크롬 도구에서 표현하는 방식이었군요 ㅠㅠ..!!





  • 로그인을 하시면 답변을 등록할 수 있습니다.