[네이버 부스트캠프 웹・모바일 10기 멤버십] 그룹 프로젝트 4~7주차 회고
온보딩 시스템 개선부터 인프라 모니터링, 실시간 소켓 통신, 그리고 3D 캐릭터 최적화까지 7주간의 그룹 프로젝트 완주 기록
- #네이버 부스트캠프
- #회고
- #Three.js
- #최적화
- #React
- #Web Socket
댓글
입력한 비밀번호는 비밀 댓글 열람, 수정, 삭제에 사용됩니다.
아직 등록된 댓글이 없습니다.
온보딩 시스템 개선부터 인프라 모니터링, 실시간 소켓 통신, 그리고 3D 캐릭터 최적화까지 7주간의 그룹 프로젝트 완주 기록
입력한 비밀번호는 비밀 댓글 열람, 수정, 삭제에 사용됩니다.
아직 등록된 댓글이 없습니다.
링크 정보를 불러오는 중...
프로젝트가 드디어 마무리 되었다.
개발 초기에는 7주라는 짧은 기간 동안, 계획한 기능들을 전부 구현할 수 있을지 걱정이 앞섰는데 어떻게든 해내고야 말았다..ㅎㅎ 워낙 정신없이 달린 탓에 매 주차 회고를 남기겠다는 야심 찬 계획은 지키지 못했지만, 지나온 발자취를 뒤늦게나마 기록해 둔다.
팀원 한 분이 듀오링고처럼 로그인 없이도 사용자가 서비스를 먼저 체험할 수 있는 구조를 제안했다.
우리 프로젝트가 '가볍게 한 문제라도 풀어보자'는 모토를 가진 만큼 팀원 모두가 동의했다.
이를 위해 비로그인 유저의 데이터를 어떻게 유지할지 고민하다가 로컬 스토리지를 활용하기로 했다.
단순히 푼 문제 수뿐만 아니라, 메인 페이지가 긴 연속 배열 형태였기에 마지막으로 풀었던 챕터의 위치까지 저장하여 페이지 이동 시에도 사용자가 편리하게 복귀할 수 있도록 설계했다.
로그인 연동 과정을 프론트엔드와 백엔드로 나누어 진행했다.
나는 프론트엔드 파트를 맡아 표준 Fetch API를 사용하여 구현했고, Application/JSON과 Form Data를 분리하여 처리 + Refresh/Access 토큰 로직을 직접 작성했다.
부캠 전 첫 프로젝트에선 일주일씩 걸리던 작업이었으나, 챌린지 기간에 TCP로 HTTP를 직접 구현해 본 경험 덕분에 데이터가 헤더와 바디에 어떻게 실리는지 머릿속으로 그리며 작업할 수 있었다.
인터랙션과 애니메이션이 강조되는 화면의 즐거움
퀴즈 페이지를 개선하며 스트릭(Streak) 달성 시의 효과 화면을 구현했다.
Framer Motion를 바탕으로 Gemini를 적극 활용했는데, 시각적 피드백이 강한 화면을 만들 때 가장 즐겁고 또 나만의 디테일을 발휘하기 좋은 환경이라는 것을 다시금 깨달았다.
좋았던 점
지난주 아쉬웠던 '데모 전 급박한 합치기' 문제가 일정 관리 개선을 통해 해결되었다.
스토리북 관리와 사용자의 위치 계산 등 디테일한 부분에 신경을 많이 썼다.
아쉬운 점
데모 당시 받은 피드백이 이미 우리가 계획 중인 기능들과 겹쳐 실질적인 인사이트를 얻기 어려웠다.
사용자에게 더 가치 있는 피드백을 끌어내기 위해 질문을 더 똑똑하게 설계하고 수집하는 방안이 필요함을 느꼈다.
우리 서비스에는 듀오링고를 참고한 '중간/최종 점검' 단계가 있다.
이는 별도의 문제가 존재하는 게 아니라 이전 문제들을 무작위로 조합하는 방식이라 DB 설계 단계부터 고민이 많았다.
결국 NestJS에서 DB와 별개로 Steps 정보를 새로 생성하고, orderIndex를 활용해 중간/최종 단계를 구분하는 로직을 도입했다.
이전 스텝의 문제들을 모아 랜덤하게 10개를 추출하는 시스템을 구축하며 도메인 특성에 맞는 복습 환경을 만들었다.
3주차 빌드 오류로 데모를 망칠 뻔한 경험을 바탕으로 Husky를 강화했다.
커밋 스타일뿐만 아니라 Lint, Format, Type Check, Build까지 통과해야 PR이 가능하도록 수정했다.
PNPM Turbo Pack의 캐싱 덕분에 빠른 검사가 가능했다.
| 구성 요소 | 역할 | 설명 |
|---|---|---|
| Prometheus | 메트릭 수집 | 서버 지표를 시계열로 저장 |
| Grafana | 대시보드 | 메트릭/로그 통합 시각화 |
| Loki | 로그 저장 | 애플리케이션 로그 중앙화 |
| Promtail | 로그 수집 | 컨테이너 로그를 Loki로 전달 |
| Node Exporter | 시스템 지표 | CPU/메모리/디스크 모니터링 |
또한, 다른 팀원이 겪는 프로덕션 로그 확인의 불편함을 해결하기 위해 Loki, Grafana, Promtail, Prometheus를 도입했다.
지난 개인 스프린트에서 실패했던 도커 배포를 성공시키기 위해 실 서버에 접속해 사전 체크를 하는 등 신중하게 접근하여 작업하는 과정이 서버 접근의 두려움을 극복하는데 큰 의미가 있었다.
사용자의 재방문을 유도하기 위한 이메일 알림 서비스를 혼자 구현했다.
가입 5일 이후, 1주일 미접속 등 조건을 세밀하게 나누고 수신 거부 시스템까지 구축했다.
특히 보안을 위해 수신 거부 링크에 별도의 토큰을 발급하여 권한이 있는 사용자만 API를 호출할 수 있도록 설계했다.
네트워크 지식과 리눅스 서버의 쉘 스크립트 배치 방법, NestJS의 기능을 숙지했기에 가능한 작업이었기에 ADR(Architecture Decision Record)을 작성하고 기술적 의사결정을 내리는 과정에서 큰 뿌듯함을 느꼈다.
기술의 필요성을 비판적으로 바라보기
랭킹 시스템을 도입하며 Next.js와 Tanstack Query의 캐싱 원리를 분석했다.
단순히 유행하는 기술을 쓰는 것이 아니라, 어떤 원리로 우리 서비스에 도움이 되는지 비판적으로 생각하는 개발자가 되어야겠다고 다짐했다.
좋았던 점
n8n 특강을 활용해 대량의 퀴즈 데이터를 생성하는 워크플로우를 팀원과 공유하며 효율을 높였다.
백엔드 영역까지 직접 설계하고 구현하며 기술적 자신감을 얻었다.
아쉬운 점
기능 구현 속도가 빠듯해지면서 AI 질의응답이나 랭킹 시스템 같은 주요 기능을 설계 단계부터 깊게 논의하기보다, '선 구현 후 설명' 방식으로 합치는 비중이 늘어났다.
특히 CodeRabbit 도입 이후 버그는 찾기 쉬워졌지만 동료의 코드를 직접 읽고 고민하는 시간이 줄어든 점이 아쉬웠다.
실시간 배틀 기능을 위해 Socket.io를 도입했다.
여러 페이지에서 소켓 이벤트를 일관되게 관리하기 위해 전용 Provider와 Hook을 설계했다.
각 컴포넌트에서는 Hook을 호출하기만 하면 되도록 캡슐화했다.
모두의 코드를 합친 후 리팩토링하는 과정에서 수많은 연속적인 에러가 발생해 한 번 작업한 코드를 모두 버리고 다시 시작하는 과정을 겪긴 했지만, 결과적으로 확장성 있는 구조를 갖추게 되었다.
다만, 막바지에 팀원 간 실시간 연락이 예전만큼 원활하지 않아 PR 리뷰가 형식적으로 흐른 점은 아쉬웠다.
Blender에서 작업한 캐릭터를 웹에 올리는 작업에 거의 하루를 온전히 쏟았다.
텍스처 노드가 복잡해 직접 베이킹하여 이미지를 다시 연결해야 했고, Rigify 본이 Three.js에서 제대로 제어되지 않는 문제도 발생했다.
AI가 제안한 디버깅 스크립트를 여러 개 작성했으나 해결되지 않았고, 결국 Rigified 본은 직접 컨트롤이 불가능하다는 것을 뒤늦게 파악했다.
AI에만 의존해 시간을 낭비하기보다 문제의 근본 원인을 파악하기 위한 단계적 디버깅의 중요성을 절감했다.
사용자의 학습 기록을 보여주는 프로필 기능을 구현하며 타임존 문제에 직면했다.
처음에는 단순히 서버 시간에 한국 시간 기준인 9시간을 더하거나 빼는 방식을 사용했으나, 이는 글로벌 환경을 고려하지 못한 임시방편이었다.
이를 해결하기 위해 네트워크 지식을 활용하여 HTTP 헤더에 사용자의 로컬 시간대 정보를 담아 보내도록 개선했다.
서버가 헤더의 정보를 바탕으로 각 사용자에게 맞는 정확한 시간을 계산하여 반환하게 함으로써 기술적으로 더 견고한 시스템을 만들 수 있었다.
디버깅의 기준점 설정
3D 모델링 파이프라인에서 문제가 생겼을 때, 이것이 모델의 문제인지, 익스포트 설정인지, 혹은 Three.js 코드 문제인지 파악하는 기준을 세우는 것이 얼마나 중요한지 깨달았다.
좋았던 점
복잡한 소켓 통신 기능을 일주일 동안 팀원들이 각자 작업을 분배하여 구현했음에도, 마지막 합치기 단계에서 큰 무리 없이 동작하는 결실을 보았다.
복잡한 소켓 통신 로직을 리팩토링하여 깔끔한 책임 분리를 실현했다.
아쉬운 점
캐릭터 작업의 작업 시간이 길어지며 팀원들과의 작업 연동 시점을 놓치거나 기다리게 만든 부분이 있었다.
프로젝트 후반부로 갈수록 팀원들의 체력이 소진되면서 소통의 즉각성이 떨어지는 느낌을 받았다.
소켓처럼 긴밀한 협업이 필요한 기능에서 페어 프로그래밍을 할 기회가 없었던 점이 특히 아쉬웠다.
마지막 주차에는 서비스의 완성도를 높이기 위해 최적화에 집중했다. Gzip과 Brotli를 적용해 리소스를 압축했고, 폰트 파일은 서브셋 모드를 활용해 용량을 대폭 줄였다.
3D 캐릭터는 Shape Key(표정)는 Three.js에서, 몸통 모션은 Blender에서 처리하는 하이브리드 방식을 택해 효율을 높였다.
클릭 시 넘어지는 모션이나 시선 추적 등 사용자 인터랙션을 추가하여 서비스의 와우 포인트를 완성했다.

기존의 양옆 아이템을 매칭하는 퀴즈에서 '선택 관계를 파악하기 어렵다'는 피드백을 받았다.
이를 해결하기 위해 퀴즈 결과 화면뿐만 아니라 사용자가 선지를 선택할 때마도 직관적인 시각적 연결을 제공함으로써 사용자가 본인의 선택을 확신할 수 있도록 UX를 크게 개선했다.
개발 중반 이후부터 테스트 코드가 제대로 관리되지 못한 점이 큰 아쉬움으로 남았다.
기능이 개선된 후 테스트가 수정되지 않아 나중에는 빌드를 위한 '형식적인 통과'로 전락했다.
다음 프로젝트에서는 TDD를 도입해 설계 단계부터 테스트가 동반되는 환경을 만들어보고 싶다는 생각이 들었다.
또한, 5주차 이후 관리가 소홀해진 스토리북도 리팩토링을 통해 따로 배포 도메인을 파서 관리해보고 싶다.
기술적 깊이에 대한 갈증

기능 구현은 완벽히 끝냈지만, 피드백이 주로 시각적인 요소에 집중된 점이 아쉬웠다.
개발자로서 서비스의 핵심 로직과 백엔드 설계에 대해서도 더 깊은 피드백을 주고받는 '딥토크'의 기회를 다음 프로젝트에서는 더 적극적으로 만들어보고 싶다.
좋았던 점
내 강점을 3D 캐릭터와 인터랙션으로 확실히 녹여내어 독보적인 결과물을 만들었다.
목표했던 모든 기능을 성공적으로 구현하고 안정적인 데모를 마쳤다.
아쉬운 점
인프라 모니터링을 구축했으나 실제 운영 데이터로 깊이 있게 분석해보지 못한 점이 아쉽다.
캐릭터를 마감 직전까지 작업하다 보니 프로젝트에 삽입하는 데 급급했다.
전 페이지 반응형 레이아웃을 공들여 짰음에도 불구하고, 정작 모바일에서 3D 모델 로딩이 오래 걸리는 최적화 문제를 해결하지 못한 것이 큰 아쉬움으로 남는다.