
이번에 14회 Theo Sprint에 참가하면서 Google Sprint(Custom by Theo)라는 협업 방식이 어떤 것인지 맛볼 수 있었습니다. 서비스로서의 새로운 기사를 작성하면서 접한 최고의 방법이라고 생각될 정도로 매우 효과적이었습니다. 나는 Pig Jam과 내 개인 저널에 스프린트 자체에 대한 리뷰를 썼습니다. B. 좋았던 점과 앞으로 개선해야 할 점. 그래서 오늘은 기술적으로 재현 가능한 포인트에 대해 알아보겠습니다.
담당 부분
우리의 아이디어는 벚꽃 시즌에 맞게 초대장을 만들고 장식하고 공유할 수 있는 웹 서비스입니다. 최근에는 단순히 CRUD가 아닌 새로운 기능을 개발하고 싶어서 Interactive Developer의 코드를 따라가 보았습니다. 같은 이유로 캐릭터/스티커를 끌어다 놓아 초대장을 꾸미는 기능을 만들고 싶다고 항의하여 채택하게 되었습니다. 데모 버전에는 MVP만 포함하기로 하고 드래그 앤 드롭 기능 대신 클릭한 좌표를 계산하고 거기에 캐릭터/스티커를 렌더링하는 기능을 개발했습니다.

기부금
대체로 담당했던 부분은 클릭 좌표 이미지 렌더링 기능이 완료되었습니다. 구체적으로 관련 논리를 여러 개 생각해냈고 대부분 완성했다.
먼저 클릭한 항목의 유형에 따라 텍스트 영역을 readOnly로 다르게 만드는 논리를 생각해 냈습니다. 이는 기획팀에서 미처 고려하지 못한 문제인데, 현재 선택된 요소에 따라 텍스트 영역의 편집 가능성이 달라져야 했습니다. 현재 배경 카테고리를 선택하고 있거나 항목이 선택되어 있지 않다면 캐릭터/스티커 카테고리이더라도 텍스트 영역을 수정해도 상관없습니다.
단, 캐릭터/스티커 카테고리에서 아이템을 선택한 후 초대 영역을 클릭하는 사용자 동작으로 아이템이 생성되어야 합니다. 그러나 텍스트 영역을 클릭하여 요소를 생성하고 동시에 텍스트 영역 커서에 포커스가 있는 경우 이는 명백한 오류 상황입니다. 이는 사용자의 의도가 항목을 만드는 것이었기 때문입니다. 그래서 우리는 카테고리와 항목의 클릭 여부에 따라 textarea의 readOnly 속성의 부울 값을 다르게 반환하는 로직을 작성했습니다.
둘째, 세션에 현재 상태를 저장하고 완료 후 “Recreate Invitation” 버튼을 클릭하면 복원하는 코드를 작성했습니다. 선택한 배경 종류와 캐릭터/스티커 위치 좌표가 세션에 저장됩니다. 구성 요소가 렌더링되면 세션이 호출되고 무언가가 저장되면 화면에 표시됩니다.
텍스트 영역의 경우가 모호했는데, 세션에서 사용자가 텍스트를 입력할 때마다 저장하는 것이 모호했습니다. Create Invite 버튼 클릭 시 디바운스를 적용할지, 저장을 할지 고민하다가 세션을 아예 저장하지 않는 방향으로 텍스트가 갔습니다. 당시에는 굳이 텍스트를 저장할 필요가 없다고 생각했는데 지금 생각해보면 꼭 필요한 기능인 것 같습니다. 다음 주에 이 기능을 개발에 추가해야 합니다.
세 번째는 좌표 계산이었습니다. 가장 많은 시간이 소요된 부분입니다. DOM 요소의 상하 좌표만 알고 다른 API는 몰랐기 때문입니다. getBoundingClientRect 및 event.clientX/Y와 같은 좌표에 대한 API는 Google 검색을 통해 빠르게 나타났지만 사용자가 제공한 위치 좌표는 초대받은 사람의 장치에서 동일한 위치에 나타나야 했습니다. 초대 자체는 단색 픽셀이고 크기는 같지만 초대 시작 좌표는 장치마다 다르게 보입니다. 따라서 클릭한 위치의 좌표는 장치마다 다릅니다.
결국 화면을 기준으로 한 좌표가 아닌 초대 영역에서 오프셋을 찾아야 했습니다. 화면을 기준으로 클릭한 좌표에서 초대 영역의 시작 좌표를 빼서 초대 영역을 기준으로 오프셋 X, Y를 구했습니다. 간단한 아이디어지만 그 당시에는 떠오르지 않아서 제 머리를 꽤 찢게 만들었습니다.
불행한 부분
드래그앤드롭보다 쉽다고 느꼈던 클릭 기능이 MVP를 받았지만 역시 쉽지 않았습니다. 요소의 오프셋, 왼쪽, 위쪽 등의 좌표를 다루는 것을 개발해본 적이 없었기 때문에 모두 생소했습니다. 많은 시행착오를 거쳐 어떻게든 기능을 구현할 수 있었지만, React를 사용하면서 DOM을 직접 붙인 것이 많이 아쉽습니다. DOM 요소를 직접 가져오지 않고 ref로 React DOM을 가져오는 것도 고려했습니다. 캐릭터/스티커를 동적으로 생성해서 초대장에 붙이는 것까지 생각하면 별 생각 없이 document.createElement를 이용해 실제 DOM 엘리먼트를 만들어 React DOM 트리에 붙였습니다. 안타깝게도 React 방식으로 DOM을 조작할 수 없었습니다.
배포 후 직접 사용해보았을 때 배경/캐릭터/스티커 카테고리를 변경할 때마다 새로운 이미지가 로딩되어 로딩 시간이 오래 걸렸습니다. 재작성이나 업데이트를 통해 기존 설정을 다시 불러와서 다시 렌더링을 해도 에셋을 불러오는 시간이 길게 느껴졌습니다.
이번에 프론트엔드 스터디에서 useEffect를 공부하다가 React의 공식 홈페이지에서 관심사가 다른 effect를 분리하는 것이 좋다는 문장을 봤습니다. 모든 업데이트 로직을 useEffect 후크에 넣었지만 가독성이 좋지 않은 것 같습니다. 마찬가지로 모든 코드는 내가 작성한 디스플레이 구성 요소를 중심으로 배치됩니다. JSX 자체는 짧아서 따로 구분할 것이 없지만 컴포넌트에 선언된 함수가 너무 많아 읽기가 어렵다.
개선/추가할 영역
- DOM 조작에 대응
- 구성 요소 자산 로드 최적화
- DOMuseEffect를 원하는 대로 분리
- 현재 코드가 디스플레이 구성 요소에 집중되어 있는 코드 분리
- 캐릭터/스티커 생성 방법 클릭 -> 드래그 앤 드롭 방식으로 전환
- 캐릭터/스티커 삭제 버튼
- 세부 디자인 변경
검토
기획 3일 + 개발 3일, 6일이라는 짧은 시간에 데모 서비스를 만들었습니다. 사용자가 클릭한 좌표를 찾아 거기에 이미지를 렌더링하는 것이 작업이었습니다. 이런 일을 해본 적이 없어서 해보고 싶어서 이 일을 자원하게 되었습니다. 그 과정에 몰입했고 오랜만에 뭔가를 하는 기분이었다. 평소에 구직활동을 할 때는 오랫동안 집중하지 못하고 시간을 낭비했는데, 이번 개발 과정에서는 개발에만 집중했습니다. 아무 생각 없이 시간 가는 줄 모르고 개발했다.
그 차이가 무엇인지 궁금했습니다. 인터뷰와 이번 스프린트의 차이점은 목표가 명확하고 데드라인이 있다는 점이다. 내 눈에 보이는 명확한 목표와 시간 제한이 내가 파고들 수 있는 요소라는 것을 배웠다. 뻔하지만 다시 새기고 이런 요소들을 적용해서 앞으로 어떤 작품에 몰입해야 할까요.