개발자

리액트에서 이벤트 루프관련해서 질문이 있습니다

1월 26일조회 31

안녕하세요. 리액트에서 이벤트 루프 동작 관련해서 제가 이해한내용이 맞는지 질문드립니다! 코드를 간단히 설명드리면 List를 그리는 scroll container가 있고 아이템이 추가되면 마지막으로 추가된 아이템으로 스크롤을 시키려는 코드입니다. (useEffect를 사용해도 되지만 이벤트 루프를 이해하기 위해서 작성하였습니다!) 스크롤을 시키기 위해 작성한 코드는 세가지입니다! 1번 코드는 스크롤이 되지 않습니다. 이유는 리액트에서 batch 업데이트 과정에서 렌더링되기 전에 호출되었으니 스크롤이 되지 않습니다. 2번 코드는 스크롤이 되었습니다. 리액트의 상태변화는 마이크로태스크큐에서 실행이 되었고 렌더링이 발생한 후에 매크 태스크큐가 실행되기 때문에 올바르게 렌더링 후에 실행이 되었습니다. 3번 코드는 스크롤이 되었습니다. 이건 이유를 정확히 모르겠습니다. 개발자 도구에서 실행된 시점을 봐도 마이크로태스크큐에서 스크롤 함수가 실행되었지만 올바르게 실행이 되었습니다. 마이크로 태스크 큐 실행과정에서 appenchild가 발생하고나서 렌더링이 되기전에 이미 레이아웃이 다 계산은 완료가 되는걸까요? 어떻게 이해를 해야될지 모르겠습니다.

1<button
2          type="button"
3          onClick={() => {
4            setListData([...listData, listData.length + 1]);
5            // 1번 코드 call stack에서 바로 실행
6            ref.current?.scrollTo({
7              left: ref.current?.scrollWidth,
8              behavior: "smooth",
9            });
10
11            //2번 코드 매크로태스크 큐를 통해 실행
12            setTimeout(() => {
13              ref.current?.scrollTo({
14                left: ref.current?.scrollWidth,
15                behavior: "smooth",
16              });
17            }, 0);
18
19            //3번 마이크로태스크 큐를 통해 실행
20            Promise.resolve().then(() => {
21              ref.current?.scrollTo({
22                left: ref.current?.scrollWidth,
23                behavior: "smooth",
24              });
25            });
26          }}
27        >
28          <span>+</span>
29        </button>
이 질문이 도움이 되었나요?
'추천해요' 버튼을 누르면 좋은 질문이 더 많은 사람에게 노출될 수 있어요. '보충이 필요해요' 버튼을 누르면 질문자에게 질문 내용 보충을 요청하는 알림이 가요.
profile picture
익명님의 질문

답변 0

답변이 없을 때 보이는 아이콘

첫 답변을 기다리고 있어요.

지금 가입하면 모든 질문의 답변을 볼 수 있어요!

현직자들의 명쾌한 답변을 얻을 수 있어요.

또는

이미 회원이신가요?

AI 답변
커리어리 AI 봇 프로필 이미지

커리어리 AI 봇

GPT가 달아주는 답변입니다.

1월 26일

안녕하세요. 리액트의 이벤트 루프 동작에 대해 궁금하신 점이 있으시군요. 먼저, 당신이 잘 이해하고 계신 것으로 보입니다. 그러나 몇 가지 추가적인 통찰을 낼 수 있습니다. 1번 코드는, 예상대로 DOM 업데이트가 아직 발생하지 않은 콜 스택의 현재 실행 컨텍스트에서 실행되기 때문에 스크롤이 작동하지 않습니다. 2번 코드에서 setTimeout은 브라우저의 Web API를 사용하는 비동기 함수로서, 콜백 함수를 태스크 큐에 넣습니다. 따라서 모든 동기 작업이 완료된 후(즉, 렌더링 후) 태스크 큐에서 콜백이 가져와져 메인 스레드에서 실행됩니다. 3번 코드는 자바스크립트의 프로미스 관련 동작 때문에 스크롤이 작동합니다. Promise.resolve().then() 구조는 "microtask"를 생성하여 microtask 큐에 추가합니다. 이벤트 루프는 현재 태스크가 완료된 직후에 마이크로태스크 큐를 확인하고, 모든 마이크로태스크가 완료될 때까지 이를 처리합니다. 여기서 중요한 점은 마이크로태스크가 진행되는 동안 새로운 마이크로태스크가 생성될 수 있지만, 이러한 모든 태스크를 처리하기 전에는 다른 매크로태스크나 렌더링 업데이트를 하지 않습니다. 즉, 리액트의 상태 변경으로 인한 DOM업데이트(batch update)가 마이크로태스크 큐에서 발생하고, 이 후에 다른 매크로태스크들이 실행됩니다. 따라서 3번 코드가 작동하는 원인은 스크롤 함수 호출이 DOM 업데이트 후에 발생하기 때문입니다. 마지막으로 언급하신 대로, 브라우저는 특정 DOM 업데이트(appendChild와 같은)를 위해 레이아웃을 재계산할 수 있습니다. 그러나 이 경우에도 실제 뷰포트의 스크롤 위치 갱신은 페이지가 리페인팅 되는 시점(렌더링 단계)에 일어납니다. 즉, 약간 혼란스러울 수 있는 부분은 프라미스 진행과 리액트의 비동기성 사이의 상호작용 때문일 수 있습니다. 결국 중요한 것은 DOM 변경과 그 결과를 화면에 표시하는 뷰포트 변경 사이의 타이밍입니다.

목록으로

지금 가입하면 모든 질문의 답변을 볼 수 있어요!