eslint-config-airbnb
같은 것들이 있지만 내가 잘 모르는 설정까지 그냥 넣어두고 쓰는 게 마음이 쓰였고, 애초에 명확한 컨벤션이 없고 거의 혼자 개발하게 되는 영역인데 불필요한 제약 사항을 추가할 필요는 없다고 생각했다. 그래서 추후에 필요할 때 하나씩 추가하자는 생각으로 최소한의 설정값만 세팅해두었다..gitignore
는 프레임워크나 언어 정도만 고려하면 되는 줄 알았다. https://www.toptal.com/developers/gitignore 를 통해서 최초 .gitignore
세팅을 했지만 IDE 키워드는 따로 넣지 않았다..idea
라는 폴더가 생성된다. 해당 유저의 검색, 사용 기록 등을 기록하는 파일인데 당연히 세팅된 환경이나 사용자에 따라 달라지는 파일이기 때문에 버전 관리를 할 필요가 없다. 이 폴더에 대한 인지가 전혀 없던 와중에 git init
후 git push
로 원격 저장소에 해당 폴더가 올라가게 되었고, 그 찝찝함에 저장소를 다시 만들고 IDE 정보를 넣은 .gitignore
를 재생성해서 세팅했다.useApi
라는 별도의 hook을 만들고 그 hook을 통해 호출하도록 설계했다.props
가 늘어났다. 하나의 prop
을 선언하는 건 쉬운 일이지만 늘어날수록 복잡도가 증가하고, 덜어내는 건 갈수록 더 까다로울 일이 될 수밖에 없다.props
와 별도의 로직까지 적용했다. 문제는 추후 API가 나왔을 때 dummy data와 일부 구조가 달라지면서 이미 선언해놨던 영역을 수정하는 게 도리어 번거로운 작업이었다.prop
을 추가할 때 한번 더 생각하자. prop
을 추가하는 건 5초면 끝나는 일이지만 나중에 prop
하나 제거하는 건 1시간이 걸릴 수도 있을 일이다. 물론 경솔하게 추가한 건 아니지만 2번 고민할 것을 3번 고민하는 태도 자체가 중요하다.state
가 바뀌더라도 리렌더링이 되어서는 안된다. 라이브 스트리밍 채팅 기능의 삭제 로직이 비디오 플레이어 컴포넌트에 영향을 미쳐 리렌더링되는 이슈가 있었고, 고민 끝에 영향을 받지 않는 컴포넌트에서 useState
로 선언하는 방식을 취했다. 일부 어쩔 수 없이 구조상 그대로 선언해야 하는 경우는 useState
대신 useRef
로 대체 사용하여 리렌더링 없이 기능 구현 가능하게 수정했다.atom
을 기준으로 리렌더링 이슈가 있었다. 별도의 로컬 Confirm Dialog로 컴포넌트를 분리하면서 해결하기는 했으나 근원적인 해결책인지는 다소 의문이 드는 부분이 있다.props
나 Recoil을 통해서 전체 비즈니스 로직을 온전히 짜는 데에 자주 트러블이 생겼다. 물론 그 과정에서 트러블슈팅을 해냈으면 문제 없겠지만 한정된 시간 안에 작업을 해야 되기에 아쉽지만 일부 아토믹 패턴을 포기하고 컴포넌트를 합치는 형식으로 해결하기도 했다.아이폰 safe area 대응
meta
tag의 viewport
설정으로 아예 safe area를 없애거나 body
자체에 요소의 컬러와 맞물리는 배경색을 입혀서 자연스럽게 대응하는 방식이 있었다. safe area를 없앨 경우 safe area 주변에 별도의 인디케이터가 들어가는 경우 추가 대응이 필요할 수 있어서 body
에 배경색을 적용하는 방식을 취했다.Safari 등 모바일 브라우저에서 100vh 이슈
Safari 등 특히 모바일 브라우저 상에서 노출되는 영역(ex) 주소 영역) 때문에 100vh 적용한 element 위를 덮어버리는 이슈가 있었다. 브라우저 상에서 덮어지는 영역을 제외하고 1vh를 재계산하여 다시 렌더링하는 방식을 취했다. 추가로 불필요한 버그를 방지하기 위해 모바일 메뉴가 나올 때는 body
의 scrollbar를 없애서 스크롤이 되지 않도록 적용했다.
https://velog.io/@edie_ko/Tip-모바일-브라우저에서-100vh-적용-오류-해결-iosandroid
무한스크롤 및 추가 데이터 페칭할 때 사용성 저하 이슈
Recoil 전역 관리에 해당되는 컴포넌트 리렌더링으로 자식 컴포넌트 리렌더링 이슈
atom
의 영향을 받는 confirm, alert dialog가 뜰 때마다 라이브 스트리밍 컴포넌트가 리렌더링되는 이슈였다. 우선 별도의 로컬 Confirm Dialog로 컴포넌트를 분리하면서 해결했다.video.js 플레이어 인스턴스 상태에 따른 분기 처리 이슈
데이터 페칭 속도보다 컴포넌트 변경이 빠를 때 레이아웃 시프트 생기는 이슈
단순 null checking이 불안정할 수 있는 이슈
null
로 떨어지길래 데이터 바인딩에서 null
만 checking 하도록 작업해두었는데, 추후 운영 기능에서 섹션 타이틀 생성 시 string
빈값이 자동 기입되는 것으로 변경되면서 빈 string
값이 들어오는 이슈가 있었다. 단순히 null
만 체크하는 것이 아니라 falsy한 값은 전부 checking 하는 것으로 수정해서 해결했다.플레이어 별도의 딤처리 및 플레이어 인스턴스 상태에 따른 분기 처리
video
tag를 사용할 때 poster
attribute를 별도로 지정하지 않는 경우 src
attribute나 source
tag에서 사용하는 영상의 첫 번째 프레임을 썸네일로 사용한다. 기본값을 사용하려고 했기 때문에 크게 신경을 안 쓰고 있었는데, 해당 video
tag가 들어가는 페이지의 경우 일반 클립과 라이브 클립으로 나눠지고, 라이브 클립은 방송 상태에 따라 영상이 항상 삽입되는 건 아니라는 점을 간과했다. 그래서 라이브 클립의 상태값을 체크해서 상태에 따라 별도의 figure
와 img
tag를 통해 호출되는 이미지 소스를 삽입하는 형식으로 처리했다. 다만 특정 경우에는 단순히 클립 상태값 뿐만 아니라 플레이어 인스턴스 상태를 체크해야 했고, 플레이어 인스턴스 상태에 맞춰서 별도의 분기 처리를 진행하였다.동적 라우팅의 경우 url params
에 유효하지 않은 값이 나올 때 별도의 에러 핸들링 필요한 이슈
url
을 별도로 입력한다거나 하는 이슈로 값을 제대로 받아오지 못할 수 있어서 별도의 에러 핸들링이 필요하다. 작업하면서 인지는 했지만 메인 페이지나 에러 페이지로 보내거나 혹은 별도의 dialog를 띄우는 등의 기능 정의가 필요했고 임의로 처리할 순 없기 때문에 백로그에 적어둔 이슈였다. 어차피 제대로 된 id를 담아서 보내지 않으면 400 에러가 떨어지고 response로 정의된 에러코드가 날아오도록 서버에서 처리하기 때문에 해당 에러코드로 에러 핸들링을 진행했다. 기존의 404 페이지의 경우 와일드카드로 설정되어 있었기 때문에 해당 페이지로 라우팅하기 위해서 별도의 ‘page-error’라는 path
로 라우팅하게 핸들링했고, 해당 path
는 존재하지 않기 때문에 와일드카드의 404 페이지로 떨어지게끔 작업했다.custom select의 option
이 동적으로 들어가는 경우 여러 케이스를 고려해서 설계해야 하는 이슈
select
의 option
은 원하는 대로 커스텀하는 것이 불가능하기 때문에 주로 custom select를 만들어서 사용하게 되는데, PC 화면상에는 중간 정도에 위치해서 동적으로 늘어나도 이슈가 없었다. 다만 모바일로 전환하면서 해당 custom select가 바텀시트에 붙게 되고, option
의 높이가 바텀시트의 높이를 넘치면서 의도치 않은 스크롤 버그와 option
이 가려지게 되는 이슈가 있었다. 담당 디자이너님과 협의 후 바텀시트의 높이를 조정하고, option
이 동적으로 들어오는 여러 케이스를 체크해서 수정 진행하였다.cloudFront cookie 갱신 관련
tech
객체로 접근해서 IWillNotUseThisInPlugins: true;
로 옵션을 설정하면 내부 API에 직접 접근할 수 있다. 하지만 IWillNotUseThisInPlugins
를 통해서 접근하려는 API들은 공식 API가 아닌 내부 API의 일부로 다소 호환성 문제가 발생할 수 있어서 라이브러리에서 권장하는 방법은 아니었다. 우선 해당 옵션을 허용하면서 HLS 내부 호출에서 vhs-rendition-blacklisted
나 hls-rendition-blacklisted
발생을 감지할 수 있었고, 해당 이벤트가 감지되면 신규 cookieList 받아올 API 재호출 후 useRef
에 별도의 값을 담아주었다(useState
로 담으면 리렌더링 발생). 최종적으로 uri 값을 바꿔주는 videojs.Hls.xhr.beforeRequest
안쪽에서 cookieList를 갱신하려 했다.player.src
에 신규 sources를 주입하는 형식으로 작업해봤으나, cookie는 제대로 업데이트되지만 src
에 새롭게 주입하는 경우 브라우저에 따라 플레이어가 재시작되거나 플레이어가 pause되는 이슈가 있었다. 그래서 플레이어의 현재 재생 지점인 currentTime
을 받아와서 해당 시점에서 다시 플레이하는 방식으로 접근해보았으나, 별도의 play 내장함수로 작동한다고 해도 로딩스피너가 뜨거나 순간적으로 사용자 경험을 해치는 자잘한 문제가 있었다. 그리고 궁극적으로 최초 문제였던 대역폭을 순회하면서 403 오류를 뱉는 문제가 해결되진 않았다.useRef
내에 cookie 값을 갱신했다. 각각 배포 환경, 혹은 클립 타입은 별도의 환경변수나 상수로 지정한 값을 가져와서 분기 처리했다. 사실 이 과정에서 꽤 힘들기도 했고 결국 원하는 방식으로 트러블슈팅을 하진 못했지만, A가 안 되면 B, B가 안 되면 C, C가 안 되면 D라는 식으로 순차적으로 고민하고 해결책을 찾아봤던 경험은 꽤 좋은 경험이라는 생각이 든다.CORS 해결
antialiased
제거
antialiased
는 웹 퍼블리셔 시절부터 관성적으로 써왔던 거라 base.css
나 reset.css
쪽에 webkit-font-smoothing: antialiased;
형식으로 적용해놨던 속성인데, 문제는 배경 대비 뚜렷하지 않은 폰트의 경우 흐리게 보이는 이슈가 있었다. 처음엔 이유를 모르다가 base.css
에 적용해놓은 걸 확인하고 구글링을 통해 관련 이슈와 아티클을 확인할 수 있었다. 사실상 최근 들어서는 안티 패턴에 가깝다는 걸 확인했고 해당 속성을 제거했다.safari blurry 이슈
img
tag로 넣었을 때 이슈가 발생했는데, 해결책은 object
, iframe
, embed
tag로 변경하면 해결되는 이슈였다. 다만 tag 수정보다는 다른 확장자의 파일을 적용하는 편이 혹여나 있을 사이드이펙트가 적을 것 같다는 생각에 png로 교체하는 방법을 택했다.throttling 적용
setTimeout
을 통해 100ms마다 실행되도록 별도의 throttling을 구현해두었다.video.js
배포 환경에서 invalid hook call
invalid hook call
때문이었다. React에서 Rules of Hooks이 존재하는데, 그 Rules of Hooks을 위반해서 invalid hook call
이 발생한 것으로 확인돼서 수정했다.Safari 재생 오류 체크
verrideNative: true;
옵션으로 해결할 수 있었다. 해결이 가능한 이유는 각 브라우저마다 기본 미디어 재생 기능으로 영상 재생을 처리하는데, overrideNative
를 허용하면 기본 미디어 재생 기능을 비활성화하고 JavaScript 기반의 처리 방식을 사용한다. HLS와 같은 스트리밍 포맷을 다룰 때 특히 중요한데, 다양한 브라우저에서 일관된 사용자 경험을 제공한다는 점에서도 필요한 옵션이라는 생각이 들었다.로그인 관련 Safari, Firefox 대응
state
를 추가하고 해당 state
가 false
인 경우에는 API 호출이나 요소 렌더링 자체를 막아두었다.video.js 자막 기능 관련 Safari용 별도 이벤트 핸들러 추가
if (browser.name === 'Safari') {
player.on('loadedmetadata', () => {
player.textTracks().addEventListener('change', trackChangeHandler);
});
}
유지보수 하면서 현재에도 추가적으로 작업하고 있는데, 특정 기점 이후에 진행한 작업은 2차 회고에서 다룰 예정이다.
button
tag에 text node가 없는 경우 aria-label
추가로 웹 접근성 개선fetchpriority
, eslint rules
에 react/no-unknown-property ignore
추가payment_type
, video_type
, error_code
등prop
제거
함께 SCSS로 날아다니던 시절이 떠오르네요..^^ 열쩡열쩡가득했던 시절이 스쳐지나갔던 포스트였습니다...