오늘은 두 개 만들었다
3일차, 토요일. 쉬는 날이니까 욕심을 부렸습니다.
아침 10시 50분부터 자정까지. 챌린지 시작 이래 가장 오래 앉아있던 날입니다. 2일차에 아무것도 못 만든 죄책감도 한몫했습니다.
- 카카오톡 대화 생성기 — 가짜 카톡 대화 캡처를 웹에서 만드는 도구
- 유튜브 다운로더 랜딩 — 데스크탑 앱 소개 랜딩페이지
카카오톡 대화 생성기
왜 만들었나
"카톡 대화 캡처를 조작하고 싶다"는 게 아닙니다. 콘텐츠 제작자들이 예시 대화를 만들 때, 블로그에 카톡 형태의 UI를 넣고 싶을 때 쓸 수 있는 도구입니다.
기존에도 비슷한 서비스들이 있긴 한데, 대부분 디자인이 실제 카톡과 많이 다르거나, 기능이 부족했습니다. "진짜 카톡처럼 보이는" 캡처를 만드는 게 핵심이었습니다.
생각보다 까다로웠던 것들
Android vs iOS UI 차이
처음에는 "비슷하니까 하나로 합치자"고 생각했는데, 막상 만들어보니 상단바부터 다릅니다. Android는 뒤로가기 화살표 + 메뉴 버튼, iOS는 유니코드 네비게이션 아이콘. 이런 디테일을 놓치면 "가짜처럼 보이는 가짜"가 됩니다.
메시지 그룹핑 로직
실제 카톡에서는 같은 사람이 연속으로 보낸 메시지에 프로필을 매번 표시하지 않습니다. 또 같은 시간대에 보낸 메시지들은 마지막 메시지에만 시간이 표시됩니다. 이 두 가지 규칙을 코드로 구현하는 게 생각보다 까다로웠습니다.
900줄짜리 컴포넌트
결과적으로 page.tsx 하나가 900줄이 됐습니다. 바이브코딩이니까 컴포넌트 분리 같은 건 과감히 생략했습니다. 나중에 리팩토링하면 되지만, 솔직히 "동작하면 된다"는 마인드가 없었으면 3일차에 완성 못 했을 겁니다.
상태 관리는 useReducer 하나로 통합했습니다.
type Action =
| { type: "ADD_MESSAGE" }
| { type: "DELETE_MESSAGE"; index: number }
| { type: "REORDER_MESSAGES"; from: number; to: number }
| { type: "ADD_PARTICIPANT" }
| { type: "REMOVE_PARTICIPANT"; id: string }
| { type: "UPDATE_SETTINGS"; key: string; value: unknown };메시지, 참여자, 설정을 한 곳에서 관리하니까 복잡도가 확 줄었습니다. useState를 7~8개 쓰는 것보다 훨씬 나았습니다.
이미지 추출에서 배운 것
html2canvas-pro로 미리보기 영역을 캡처해서 PNG로 내보냅니다.
const canvas = await html2canvas(element, {
scale: 2,
backgroundColor: null,
});scale: 2는 레티나 디스플레이 대응입니다. 이걸 안 하면 캡처 이미지가 흐릿하게 나옵니다. 처음에 빠뜨렸다가 "왜 이렇게 화질이 낮지?" 하며 30분을 날렸습니다.
유튜브 다운로더 랜딩페이지
페인포인트 마케팅
유튜브 다운로더를 검색하면 나오는 사이트들, 다들 한 번쯤 써보셨을 겁니다.
- 다운로드 버튼 누르면 광고 팝업 3개
- "로봇이 아닙니다" 인증 반복
- 다운받았더니 화질이 240p
- 10분 넘는 영상은 다운 불가
이 불편함을 정면으로 공략했습니다. "광고 0개. 낚시 0개. 진짜 다운로드만."
인터랙티브 데모가 핵심
말로 설명하는 것보다 직접 보여주는 게 낫습니다. AppMock 컴포넌트로 앱 사용 과정을 그대로 시뮬레이션했습니다.
- macOS 타이틀바 (빨/노/초 신호등 버튼)
- URL 타이핑 애니메이션
- 포맷 선택 (MP4 / MP3 / TXT)
- 프로그래스 바 + 로그 출력
- 다운로드 완료 화면
5단계 상태 머신으로 자동 전환됩니다. 사용자가 스크롤하면서 "아, 이런 식으로 쓰는 거구나"를 느낄 수 있게 했습니다. 스크린샷 한 장 넣는 것보다 인터랙티브 데모가 설득력이 훨씬 강합니다.
3일차 회고
2일차에 아무것도 못 만든 뒤라서, 오늘은 "만회해야 한다"는 마음이 컸습니다. 결과적으로 두 개를 만들었고, 둘 다 나름 완성도가 있습니다.
다만 토요일 하루를 통째로 쓴 건 지속 가능하지 않습니다. 평일에 짧게 집중하는 패턴을 찾아야 합니다. 챌린지는 마라톤이지 단거리 달리기가 아니니까요.
오늘의 교훈: 2일차의 실패가 3일차의 동기부여가 될 수 있다. 중요한 건 다음 날 다시 앉는 것.