본문 바로가기
source-code/FrontEnd

image decode method를 통한 콘텐츠 scrollHeight 계산

by mattew4483 2023. 8. 18.
728x90
반응형

채팅 기능 개발 중, 다음과 같은 요구사항이 있었다.

 

  1. 채팅방 접근 시, 사용자는 맨 마지막 메시지를 볼 수 있어야 한다. (스크롤은 아래에서 위로 발생한다)
  2. 사용자는 한 번에 여러 개의 이미지 데이터를 전송할 수 있다. 이 때, 이미지 개수에 따라 UI가 달라진다. (카카오톡 처럼)
  3. server는 해당 이미지를 socket event로 전파한다. 이 때, 이미지는 Presigned URL 형태로 제공된다.

기존 요구사항 1을 처리하기 위해, 채팅 메세지 목록 데이터를 받아온 후, 최하단으로 강제 스크롤을 발생시켰다.

이 때 강제 스크롤은? → 채팅방 element의 scrollTop을 해당 element의 scrollHeight만큼 이동하면 간단히 구현할 수 있다.

 

하지만, 채팅 목록에 이미지가 생기면서 위의 방법으로는 해당 채팅 목록의 최하단으로 이동할 수 없었다.

정확히 말하자면

1) 채팅 목록 socket event 수신

2) 수신 완료 후, 해당 채팅방 최초 접근인 경우, scollHeight만큼 scroll 발생

과 같이 구현했지만 이상하게 최하단보다 약간 못미치는 정도까지밖에 스크롤이 발생하지 않았다.

 

왜 이런 일이 생겼을까? 이런저런 테스트 결과 밝혀낸 사실들은...

 

1) 이미지의 정확한 너비와 높이는, 이미지가 로드되고 나서야 계산된다.

이미지가 로드되기 전에는 실제 크기 정보가 없으므로, 이미지의 너비와 높이를 정확히 파악할 수 없다.

 

일반적으로 JS에서 이미지를 로드할 때는 onload 이벤트를 사용해 해당 이미지의 너비와 높이, 혹은 성공적으로 로드되었을 때 호출할 callback 함수를 등록할 수 있다.

 

2) DOM의 scroll height는 요소의 콘텐츠가 모두 로드되고 나서 계산된다.

scrollHeight는 해당 요소의 콘텐츠의 전체 높이를 나타내는 속성이며, 요소의 콘텐츠가 모두 로드되고 나서 계산된다.

이 때 주의할 점은, 스타일이나 레이아웃 변경만으로는 scrollHeight 값이 업데이트되지 않는다는 사실.

 

→ 모든 콘텐츠가 로드되고, 변경이 완료된 시점에서 값을 읽어야 한다.

 

3) Presigned URL를 사용해 이미지를 불러올 경우, load event로는 해당 이미지의 로드 상태를 감지할 수 없다.

 

앞서 얘기한 바와 같이, 이미지의 onload 이벤트는 해당 이미지가 브라우저에 성공적으로 로드되었을 때 발생하는 이벤트이다.

 

그런데 Presigned URL은 이미지 파일 자체를 서명된 url로 제공하는 방식이므로...

이미지 파일 자체는 원본 서버에서 직접 다운로드 된다...! → 브라우저는 해당 이미지의 로드 상태를 감지하지 못한다!

 

즉 이를 종합해봤을 때...

단순히 채팅 목록 event 수신이 완료 되었을 때 scroll height를 계산해서도 안되고,

(이미지 load에 의해 달라진 scrollHeight를 감지하지 못하므로)

마냥 image의 onload 이벤트로 해당 이미지의 로드를 감지해서도 안된다!

(presigned url을 통한 이미지 다운로드는 해당 url 서버에서 이뤄지므로, 브라우저는 감지 불가)

 

그렇다면?

=> 모든 presigned url의 decode가 완료된 시점에서, scrollHeight를 조회해 scroll event를 발생시켜야 할 테다!

 

HTMLImageElement의 decode 메서드

HTMLImageElement에는 decode라는 메서드가 존재한다.

해당 메서드는 이미지의 로딩 및 디코딩을 수행하는 비동기 메서드이며

디코딩이 성공적으로 완료될 경우 promise를 resolve, 오류 발생 시 promise가 reject된다.

const img = new Image();
img.src = 'image.jpg';

img.decode()
  .then(() => {
    // 이미지의 디코딩이 완료된 후에 실행되는 로직
    console.log('이미지의 디코딩이 완료되었습니다.');
  })
  .catch((error) => {
    // 디코딩 중에 오류가 발생한 경우 처리할 로직
    console.error('이미지 디코딩 오류:', error);
  });

 

따라서 우리는 다음과 같이 작성할 수 있다.

1. 최하단으로 스크롤이 발생할 element를 지정, 해당 element 내 image tag들을 조회한다.

2. 각 image를 순회하며 비동기 메서드 decode를 실행한다. 

3. 각 image들이 반환하는 promise가 모두 resolve 되었을 때(Promise.all), 스크롤 대상 요소의 scrollHeight를 조회한다.

4. 해당 요소의 scrollHeight만큼 scroll 발생 → 이미지 로드가 완료된 콘텐츠의 높이만큼, 즉 정확히 최하단까지 스크롤을 발생시킬 수 있다!

 

 

 

 

 

 

 

728x90
반응형

'source-code > FrontEnd' 카테고리의 다른 글

testing-library/react를 통한 컴포넌트 테스트 (with RN + expo)  (0) 2023.08.18
aria-label 을 통한 웹 접근성 향상  (0) 2023.08.18
응집도  (0) 2023.08.17
공유, 공유, 공유  (0) 2023.08.17
socket io Client API  (0) 2023.08.17