naver / egjs-axes

A module used to change the information of user action entered by various input devices such as touch screen or mouse into the logical virtual coordinates.
https://naver.github.io/egjs-axes/
MIT License
179 stars 31 forks source link

`@egjs/flicking`, swipe 시도 중 `Cannot read properties of null` 예외가 발생하는 문제 #197

Closed async3619 closed 2 years ago

async3619 commented 2 years ago

Description

일전에 이슈 등록한 https://github.com/naver/egjs-flicking/issues/709 와 관련된 문제 입니다. 수정하여 릴리즈 된 @egjs/react-flicking의 최신 버전 "^4.9.2"를 이용하고 있습니다. 위 이슈에서 언급했던 상황과 같이 반복해보니 여전히 예외가 발생하는 것으로 확인되었으며 에러 발생의 원인이 된 지점이 이 모듈의 소스코드 였으므로 이 레포지토리에 이슈 남깁니다.

빌드된 코드 중 이슈가 발생한 부분에서 breakpoint를 걸어서 예외가 발생하도록 하여 stacktrace를 따라가보니 다음과 같은 코드가 있었습니다.

https://github.com/naver/egjs-axes/blob/bce26f30fead205157845f3da5440379d6ae9664/packages/axes/src/animation/AnimationManager.ts#L353

예외가 발생한 상황에서는 param.inputEvent가 제공된 상태에서 _animateLoop 메소드가 호출 되는 것으로 파악 되었지만, param.duration의 값이 0인 상태로 호출되어 이런 문제가 발생하는 것 같습니다.

https://github.com/naver/egjs-axes/blob/bce26f30fead205157845f3da5440379d6ae9664/packages/axes/src/EventManager.ts#L201

위 코드가 실행될 시점에는 option, eventInfo 와 같은 데이터가 넘어가지 않은 상태에서 처리하게 되어 null이 할당 되는 것으로 확인 되며, 따라서 inputEvent 데이터가 누락된 상태로 change 이벤트가 계속 처리 되면서 예외가 발생하는 것 같습니다.

malangfox commented 2 years ago

안녕하세요 @async3619 님. 겪고 계신 문제와 관련하여 상세한 확인을 거쳐주셔서 감사합니다.

앞서 한 차례 이슈를 남겨주신 현상에 대해서는 Flicking이 HOLDING 상태일 때 change 이벤트가 트리거될 시 브라우저의 네이티브 이벤트, Axes에서 전달하는 inputEvent 가 존재하지 않을 때 발생하던 에러로 확인이 되었습니다. 패널 전환이 발생하지만 네이티브 이벤트가 존재하지 않는 상황의 예시로 Flicking 내에서 moveTo 메서드를 사용했을 때가 있었는데요, 4.9.2 버전에서 moveTo 메서드와 관련된 오류를 수정한 이후에도 여전히 네이티브 이벤트가 존재하지 않는 경우가 있어서 본 이슈의 오류가 발생하는 것으로 추정됩니다.

현재는 남겨주신 현상을 재현해보는 과정에서 이전 이슈에 남겨주신 크롬 개발자 도구의 모바일 환경을 확인하여보고 있습니다. 혹시 해당 오류가 확정적으로 발생하는 조건이 있을까요?

async3619 commented 2 years ago

@malangfox

저희 쪽에서 개발하는 웹 앱의 큰 비중의 코드를 @egjs/flicking에 의존하고 있는 부분이 있어서 기본적인 use case에서 이러한 이슈가 발생하는지를 분명하게 말씀드리기 어려울 것 같습니다. 또 특정 상황에 에러가 발생한다기 보다는 사용하다보면 은연중에 발생하는 오류인지라 에러가 발생하는 조건을 특정하기가 너무 어려운 점이 있는 것 같습니다.

malangfox commented 2 years ago

확인해주셔서 감사합니다. Flicking이 HOLDING 상태일 때 네이티브 이벤트가 존재하지 않는 방법으로 change 이벤트가 발생할 경우 오류가 발생하지 않도록 조치하여 배포한 뒤 연락을 남기도록 하겠습니다.

async3619 commented 2 years ago

@malangfox

정확히 어느 use case에서 해당 이슈가 발생하는지 파악했습니다. 저희 웹사이트에서 flicking 을 이용한 tab layout 내부에 네이버 지도를 함께 이용하고 있는 상황입니다.

chrome_0U1jJaUMl9

위 첨부한 영상의 상황을 설명 드리자면, slide 내부에 표시된 네이버 지도와 인터렉션 후 flicking swipe를 방지하는 UI 요소와 인터렉션을 진행 할 경우 예외가 발생하는 것 같습니다.

image

image

추가적으로, moveStart 이벤트의 e.stop() 메소드 호출을 진행한 경우에만 발생하는 예외로 보여집니다. moveStart event handler의 e.stop() 관련 코드를 모두 제거하고 나서는 해당 문제가 발생하지 않는 것으로 확인되었습니다.

현재 저희 쪽에서 개발 중인 웹 앱에 있어서 swipe를 차단하는 부분이 꽤 중요한데 난감하네요

+추가

moveStart 이벤트 대신 holdStart 이벤트에서 e.stop()을 걸어주니 예외 발생 없이 제대로 작동하는 것으로 파악 되었습니다.

네이버 지도가 문제였던 상황은 아니었던 것으로 확인 되었습니다. 다른 컴포넌트에 no-swipe 클래스를 걸어주고 다시 시도하니 문제가 발생한 것으로 확인되는데 아무래도 moveStart 이벤트 처리중 e.stop() 메소드가 호출이 되면 문제가 발생하는 것 같습니다.

async3619 commented 2 years ago

++

https://codesandbox.io/s/distracted-glitter-b5yho4 이 쪽에서 문제가 발생하는 상황을 그대로 테스트 해보실 수 있으십니다.

뷰포트 내 회색 상자 영역을 swipe 시도한 뒤, slide 내부 (검은 영역)를 클릭하시거나 인터렉션 하시면 예외가 발생하게 됩니다

malangfox commented 2 years ago

안녕하세요 @async3619 님. 상세한 확인 절차를 남겨주셔서 정말 감사합니다.

남겨주신 재현 상황을 기반으로 정확한 오류의 원인을 파악할 수 있었습니다. 전달 주신 링크에서 첫 번째 패널 내부 회색 영역을 좌->우로 스와이프하고 나서 아무 검은 영역 내부를 클릭했을 때, 그리고 세 번째 패널 내부 회색 영역을 우->좌로 스와이프하고 나서 검은 영역 내부를 클릭했을 때 두 가지 경우에 오류가 발생하였던 것으로 확인이 됩니다.

moveStart 이벤트는 좌표의 이동을 먼저 처리한 이후에 발생합니다. 그렇기 때문에 첫/마지막 패널에서 외부 바운스 영역으로 진입하는 방향으로 입력을 발생시키던 도중 e.stop() 이 호출되면 후속 이벤트들의 처리를 중단시키는 stop 의 스펙 상 바운스 영역에 미세하게 진입한 상태에서 Flicking이 멈추게 됩니다.

바운스 영역에 진입한 상태에서는 holdEnd 이벤트가 발생될 때 내부 로직을 통해 원래 위치로 복귀하게 되는데요, 이 로직이 위처럼 바운스 영역으로 미세하게 나간 상황에서 다른 지점에서 holdStart 이벤트를 발생시킨 직후에 이어지면 홀드 상태일 때 사용자 입력이나 메서드 외의 방법을 통해 좌표 변화가 발생해서 오류가 발생하던 것으로 파악되었습니다.

말씀을 남겨주신 사용 예시에서는 좌표 변화가 먼저 이루어진 뒤 발생하는 moveStart 이벤트 대신 holdStart 이벤트에서 e.stop()을 사용해보시는 것이 올바른 사용 예시 같습니다. 이 경우 위와 같이 미세하게 바운스 영역으로 빠져나가는 경우가 발생하지 않아 오류가 발생하지 않을 것입니다.

남겨주신 사용 사례에 대해서는 holdStart 이벤트를 통해 처리하여보시고, 추후에 바운스 영역에 진입한 뒤 의도된 stop 이 호출되는 사용 케이스가 확인되는 경우 해당 케이스에 맞춰 저희 쪽 Flicking 컴포넌트를 수정해보게 될 것 같습니다.

만약 위 방법을 사용하시게 된 이후 추가적인 문제 상황을 겪게 되시는 경우 이슈를 남겨주시면 확인해보도록 하겠습니다.