이미지 에러핸들링 적용 및 아주 아주 기초적인 실수

김하연·2023년 9월 25일
0

우당탕탕

목록 보기
10/11

Weekly Plan

  • 채팅 기능
    - QA 이슈 대응 및 배포
  • 이미지 개선작업 적용
  • 백로그 이슈 대응

실제 작업한 것

  • 채팅 기능 QA
    • 채팅 관계가 형성된 상대 프로필 진입하여 대화 열기 기능 오픈 시, 오픈 직후 상대방과의 채팅방으로 이동하도록 동선 추가
    • 그 외의 이슈 및 배포 작업은 다른 업무 우선순위가 높아져 참여하지 않음
  • 이미지 개선작업 적용
    • 앱 사용중 네트워크 환경이 좋지 않을 경우에 대한 이미지 에러 핸들링이 되어있지 않아 이미지 영역에 엑박이 뜨는 이슈가 있었다. 해당 서비스의 유저 세그먼트 특성상 파티에 자주 참석할 것이고, 그로 인해 네트워크가 잘 터지지 않는 환경에서 앱을 구동할 경우의 수는 다른 앱과 다르게 비교적 높을 것이라 판단되어 해당 작업을 진행하게 되었다.
    • 타 팀의 시니어분께서 Image url을 훅으로 넘길 경우 pre-loading을 진행하고, pre-loading의 성공 or 실패 or 로딩 중 여부에 따라 fallback 컴포넌트를 분기처리하는 훅을 만들어주셔서 서비스에 적용하는 작업을 진행하였다.
  • 백로그 이슈 대응
    • 마이페이지에서 특정 데이터를 수정하기 위해 상세 화면에 진입해 내용을 변경하고 다시 이전 페이지로 돌아왔을 때, 간헐적으로 데이터가 업데이트되지 않는 이슈가 있어서 해소하기 위해 원인을 파악중이었고 원인은 파악하였으나 해소 방법을 결정하지 못해 아직 현재 진행 중!

작업 내용 및 코드 복습 겸 정리!

이번주는 이미지 개선작업에 대해서 회고할 내용이 정말 많은 것 같아서 그 부분에 대해서 써야겠다..!

우선 다른 팀 시니어분께서 작성해주신 코드가 너무 깔끔하고 각 함수가 하는 일을 확실히 정의한, 그리고 어느정도 추상화 되어있어 다른 케이스에서도 가져다 쓸 수 있는 구조로 작업을 해주셔서 코드를 보며 너무 감탄했다. 클린 코드 책에서 봤던 내용들이 적용되어있는 Best practice 같은 느낌!
코드를 읽으면서 클린 코드의 내용이 같이 오버랩 되는 정도였다.

나도 언제쯤 저런식으로 스스로 설계를 할 수 있게될까 하는 부러움과 그에 따라 조급한 마음도 들었는데.. 내 것으로 흡수하기 위해 이렇게 복습 겸 회고를 작성해보게 되었다.

누군가가 이 글을 보게 된다면, 주절주절 회고할 겸 적는 내용이라 부족하거나 틀린 부분이 있을 수 있음을 인지해 주시길 바랍니다...🙄


1. 문제 상황

서비스 내에서 이미지가 굉장히 중요한 역할을 하는 컴포넌트들에 대해 에러 핸들링이 되어있지 않았다.
따라서 네트워크 환경이 좋지 않을 경우 유저는 한없이 이미지를 기다리거나, 정상적인 응답을 받지 못한 경우 엑박을 마주하게 되는 상황임을 파악했다.

💡 여기서 중요한 것!
매번 팀 회고에서 나오는 내용이지만, 개발 후 QA를 진행할 때에는 개발자의 관점을 완!벽!히! 내려놓고 해당 서비스를 사용하는 유저에 빙의(?)해야함을 다시 한 번 느꼈다.
해당 서비스의 유저 세그먼트는 일반인보다 파티같은 행사에 참석하는 비율이 높다는 특징이 있고, 그에 따라 사람이 많거나 지하에 위치한 장소, 즉 네트워크 환경이 좋지 않은 상황에서 앱을 실행할 확률이 높다는 점을 간과하고 있었다.
개발 테스트 및 QA를 진행할 때 일반적인 네트워크 환경에서만 확인을 했는데, 의도적으로 3G 및 poor한 환경에서 진행을 해봤더라면 조금 더 빨리 이미지 개선작업을 진행할 수 있었을 것이라는 아쉬움이 남았다.
매번 빙의한다고 하는데 잘 안되는.... QA는.. 정말... 어렵다.


2. 진행 방향

이미지가 중요한 역할을 한다고 판단되는 부분은 앱 내에서 크게 1. 이미지 슬라이드쇼, 2. 개별 이미지 두 부분으로 나눌 수 있다.
1번과 2번의 유형에 따라 어떤 정책을 적용할지는 아래와 같이 결정되었다.

이미지 로딩 1개 이상 성공 시
1. 이미지 슬라이드쇼: 이미지 로딩에 성공한 이미지만 슬라이드쇼에 포함한다. 즉, 실패한 이미지는 노출되지 않음.
2. 개별 이미지: 성공/실패 여부 상관없이 모두 노출하되, 실패한 이미지 영역에 새로고침 버튼을 노출하여 유저가 다시 이미지를 불러올지 결정할 수 있도록 한다.

이미지 로딩 N개 모두 실패 시
1. 이미지 슬라이드쇼: 슬라이드쇼 영역에 새로고침 버튼 노출
2. 개별 이미지: 실패한 이미지 영역에 새로고침 버튼 노출


PM 및 프론트엔드 사수와 함께 논의한 끝에 위와 같이 정책이 정해졌고, 해당 부분을 위해 엔지니어로서 어떤 작업들을 진행해야할지 아래와 같이 정리해보았다.

  1. pre-loading 적용

    • pre-loading 을 실행하여 이미지 로드에 성공한 이미지, 실패한 이미지를 나누고 상황별 에러 핸들링을 추가하기 위함.
    • 기존 코드는 보여줄 이미지의 index가 변경되는 시점에 다음 이미지를 로드하고 있는데, 네트워크 환경이 좋지 않을 경우 다음 이미지를 불러오지 못해 엑박이 떠버려 유저 경험에 좋지 않았다.
    • 이 부분의 경우 pre-loading을 적용할 경우 초반에 다운받아야 하는 리소스의 용량은 더 늘어난다는 점이 우려되는 부분이었지만, 해당 이미지 슬라이드쇼는 유저 인터랙션 없이 일방적으로 이미지를 순서대로 보여주기만 하는 기능이라 유저에게 제어권이 없는 상황에서 슬라이드쇼 내에 완전히 로드되지 않은 이미지 or 엑박이 노출되는 것이 더 유저경험에 좋지 않다고 판단했다. 다른 정보들은 노출되고 있으니 이미지 로딩만 좀 더 기다려 달라고 스켈레톤을 띄우는 수밖에........ 어쩔 수 없는 트레이드 오프가 아닌가 싶기도 한............(쭈굴... )
    • 이미지 자체도 Clound Front를 거쳐 최적화 및 캐싱이 진행되고 있기에 초반 리소스를 로드하는 속도에 치명적인 영향을 미치지 않을 것이라고 생각했다.
  2. 스켈레톤 적용 타이밍 수정
    as-is API가 Fetching 되는 동안 스켈레톤 노출
    to-be API가 Fetching 되는 동안 + API Fetching 후 이미지 url들을 pre-loading 하는 동안

  3. 로드에 성공한 이미지 url을 활영하여 base64로 인코딩하여 사용
    이 부분은 우리 팀 스크럼 마스터이자 백엔드개발자인 팀원이 제안해주신 내용인데, 사실 이미지 url을 활용해 이미지 자체를 base64로 변환할 수 있다는 사실을 처음 알았다.... 모자란 내 지식에 다시 한 번 놀랐던........
    조금 더 확실히 하기 위해 이 부분까지 적용하고 싶었는데 우선 url 자체로도 성공/실패 여부를 확인할 수 있기에 여기까지 적용하는 것은 진행되지 않았다.


적용!

그리하여 위와 같이 정리된(좀 더 깔끔한;;) 요구사항에 따라 타 팀의 시니어분께서 관련 hook을 만들어주셨다. 직접 진행하지 못한 부분은 아쉬웠지만 중요도가 높으면서 다른 플랫폼에서도 사용할 수 있어야 함이 목적이었기에 기본기가 탄탄하게 능숙한 시니어분께서 기초 작업을 진행해주셨다. 덕분에 Best Practice를 보고 적용해볼 수 있어서 좋은 기회가 되었던 것 같다. (작성해주신 코드를 보고 내가 직접 진행했으면 큰일났겠다는 생각이 들 정도로... 너무 잘 작성해주셔서 많이 배웠다......)

  • 이미지 pre-loading

// 이미지 url을 인자로 전달하여 pre-loading 진행하는 함수
export const preloadImage = (url: string) => {
  new Promise<string>((resolve, reject) => {
    // 이미지 태그를 생성하여 해당 이미지 태그의 src 프라퍼티에 인자로 전달받은 url을 적용한다.
    const imageElement = new Image(); 

    const handleLoad = () => {
      resolve(imageElement.src);
    };

    const handleError = (error) => {
      reject(error);
    };

    // 이미지 성공, 실패했을 경우에 대한 handling을 추가하고 이미지 url 적용
    imageElement.onload = handleLoad;
    imageElement.onerror = handleError;
    imageElement.onabort = handleError;
    imageElement.src = src;

  }).catch(error => {
    // reject 되었을 경우 에러 핸들링
    throw new ImageError( ... );
  });
}

0개의 댓글