Cookidge 서비스의 개발이 끝났다. 기능 구현, 배포, 검색 엔진 노출 등으로 마무리 지어졌고, 이제 가독성 좋은 코드, 성능 최적화, 기능 추가 등 유지 보수 작업을 기대하고 있다.
해당 프로젝트로 나의 개발 실력에 많은 도움이 됐다. 프론트 엔드, 백엔드 API 서버를 구현하고 배포를 하여 구글 검색 엔진에 노출 되도록 하는 기나긴 여정을 겪었다.
그래서, 이러한 긴 여정을 작성해볼 생각이다. 꽤나 완성도 있는 프로젝트라고 생각하지만, 이 글을 10년 후에 다시 읽게 된다면 귀여운 프로젝트란 걸 깨닫게 되겠지...
Cookidge는 Cook + Firdge의 합성어로, 냉장고를 관리하고 공유할 수 있는 냉장고 서비스와 요리 레시피 SNS를 결합한 음식 관련 통합 서비스이다.
일반적인 레시피 SNS는 "만개의 레시피", "우리의 식탁", "한식포털" 등 꽤나 존재했다. 이런 것들이 존재하는데 레시피 SNS 프로젝트를 만든다는 것은 이유 없이 그냥 만드는 것과 같다.
따라서, 나는 차별성을 도입하기 위해 냉장고 관리 앱까지 결합하여 레시피 SNS를 제공하자고 생각했다.
"나의 냉장고에 재료를 입력하고 이런 냉장고 상태를 기반으로 만들 수 있는 레시피를 제공해 주는 사이트라면 어떨까?"
이렇게 요리 테마에 관련된 다양한 서비스를 제공하자는 생각으로 만들게 되었다.
4개월이라는 기간동안 나 혼자서 학습하고 개발을 진행했기 때문에, 기존에 내가 알던 지식, 구글링, GPT에 의존하며 외로운 싸움을 했다. 프론트엔드만 개발 했을 때랑은 차원이 다른 난이도였다.
일반적인 앱에서 로그인 기능은 필수이다. 프로젝트에서 로그인 부분에 가장 많은 시간을 들였던 것 같다. Google OAuth
와 함께 백엔드 인증을 구현했는데 코드 구현보다도 전체적인 흐름 로직을 어떻게 가져가야 하는지가 가장 어려웠다.
token
을 어디에 저장해야 하는지?Google OAuth
랑 어떻게 연동하여 흐름을 구현해야 하는지?OAuth
를 사용하면 로그인과 회원가입은 동시에 작동하는가?하면서도 "이게 맞는건가?" 라는 의문점을 갖은 것 같다. 이런 의문점들 말고도 겪었던 오류들도 생각하면 정말 굉장히 어려웠던 부분이다.
수많은 구글링과 GPT 그리고 구현한 코드의 타당성을 찾으며, 만족스럽게 구현이 된 것 같다.
나는 RDBMS
에 친숙했기 때문에, NoSql
은 막연했다. 읽기의 성능 이점을 가져가기 위해 선택했는데, 어떻게 데이터 모델링을 해야할지 많은 고민과 결정을 했다. 심지어 임베딩 모델링을 했다가 싹 다 갈아엎고 참조 모델링을 했다가 다시 임베딩을 했다가 하며 시간을 허비한 경험도 있다.
$lookup
을 피하면서 컬렉션을 구성하지?데이터 모델링 부분에서는 확실히 경험이 중요한 것 같다.
Cookidge 프로젝트에서 레시피 생성 폼에는 꽤나 많은 input
이 들어간다. 여기서 특히 "요리 과정" 부분의 이미지에서 시간을 소모했다.
프론트엔드에서 이미지들을 base64
로 인코딩하여 보내면 되는데, 굳이 사이즈가 33% 증가한다는 문제를 해결하려고 blob
으로 전송하여 백엔드에서 multer
로 굉장히 힘들게 처리한 경험이 있다.
사실, 백엔드에서 Cloudinary
로 보낼 때 base64
로 변환해서 보내기 때문에 의미없는 짓이라고 생각했었지만 서버리스의 요청 본문 크기 제한의 특징 때문에 그렇게 의미없는 짓은 아니라고 또 생각했다.
다양한 화면 사이즈에서도 자연스럽게 보여질 수 있도록 767px
기준으로 모바일 화면도 생각하며 구현했다. 특히 flex
와 text-overflow
가 특히나 말썽이였다.
이 외에도 코드를 어떻게 깔끔하게 작성할지, 성능은 어떻게 개선할지 등의 다양한 고민을했다.
FSD
디자인 패턴에 관심을 갖다가, FSD
를 적용한 좋은 예시의 프로젝트를 발견하게 되고 그것을 Cookidge에 적용시켰다.
덕분에 더 좋은 코드와 폴더 구조를 갖추게 되어 내가 원하는 컴포넌트가 어디 폴더에 위치해 있는지 추측이 더 쉬워졌다. 그래도 아직 부족한 것 같지만...
이번 프로젝트에 좀 더 빠른 성능과 좋은 사용자 경험을 위해 다양한 시도를 했다. 이러한 시도를 통해 더 완성도 있는 프로젝트를 개발할 수 있었다.
어떻게 백엔드에서 api
가 만들어지는지, 어떻게 인증이 흘러가는지 DB는 어떻게 사용되는지, 어떻게 쿼리가 작성되고 데이터를 추출하는지 등의 경험을 하여 시야가 좀 더 넓어진 느낌이다.
검색 결과 URL이 History에 남는 문제
원인: 검색창은 쿼리 스트링을 갖고 GET
요청을 한다. 뒤로가기를 눌렀을 때, 이전 검색으로 이동하여 많은 뒤로가기를 해야 검색창을 빠져나가지는 안좋은 UX를 제공했다.
해결: useNavigate
, setSearchParams
등에는 History를 남기지 않은 속성이 존재했다. replace
속성을 사용하여 History에 이전 검색 기록을 남기지 않고 바로 빠져나올 수 있게 했다.
IOS, 사파리 등에서 로그인이 안되는 오류
원인: Cookidge의 FE와 BE의 도메인이 다르기 때문에 서드파티 쿠키를 제한하는 브라우저에서는 token
이 쿠키에 등록되지 않음
해결 방안: 도메인을 구입하여 백엔드 api
를 하위 도메인으로 통합 후 퍼스트 파티 쿠키로 발급하여 해결하는 방안이 있지만 비용이 들기 때문에 보류.
해결: 프론트엔드에서 프록시를 사용하여 마치 퍼스트 파티 쿠키처럼 동작하도록 구현했다.
모바일 크롬 환경에서 레시피 폼 요리과정 항목을 추가할 때, 미리보기 이미지가 없어지는 오류
원인: 이미지 미리보기를 구현할 때, useWatch
를 통해 모든 요리 과정 이미지들의 createObjectURL
, revokeObjectURL
을 한 번에 처리한다. 요리과정 항목을 추가하거나 삭제할 때, 모바일 크롬 환경에서 한 번에 많은 createObjectURL
을 처리하면서 에러가 발생하는 것으로 보임
해결: PreviewImage
컴포넌트를 생성하고 React.memo
를 적용하여 요리과정 항목이 추가/제거 될 때, 다른 컴포넌트가 리렌더링 되지 않게했다.
배포 환경 고화질 이미지 전송시
413 Error
발생
원인: Vercel
서버리스 요청 본문 크기 4.5MB 제한
해결: 이미지 base64
대신 blob
으로 전송하고, image-compression
라이브러리를 사용하여 이미지 압축
그러나 추가 에러가 발생하였다.
모바일 크롬 환경에서 이미지 압축이 안되는 현상
원인 추정: 폼을 제출할 때, 모든 이미지를 압축하는 로직이다. 그러나 압축할 때 File
이 비어져있다.
Promise.all
을 통해 한 번에 이미지를 압축하는 방법map
함수에서 await
가 보장되지 않는다는 이론으로 for
문을 사용하는 방법useFieldArray
의 항목을 추가/제거 했을 때, 해당 필드만 리렌더링이 발생하지만 이 경우에도 File
이 비어지는 현상이 발생했다.수많은 테스트 결과 폼이 리렌더링 되면서 File
을 잃어버리는 것으로 추정된다. 이 문제에 너무 많은 시간을 소모하여 일단 해결을 미뤘다.
풀스택 웹 개발을 통해 다양한 문제를 해결하고, 여러 가지 지식을 학습하며 값진 경험을 쌓을 수 있었다. 하지만 여기서 끝이 아니다. 현재 남아 있는 오류들을 모두 해결하고, 기회가 된다면 React Native나 Next.js를 적용해 보고 싶다.