사용자 기능 확장 — AI와 빠르게 기능 추가하기

 


"이거 하루면 되겠는데"가 진짜 하루에 되는 경험

기능 하나를 추가하는 데 보통 얼마나 걸릴까. 기획하고, 설계하고, 구현하고, 테스트하고. 회사 프로젝트라면 일주일은 잡아야 하는 일이다. 그런데 AI와 함께하면 "이거 하루면 되겠는데"라는 직감이 진짜로 하루 안에 현실이 된다.

이번 편은 타로 마스터에 추가한 여섯 가지 기능의 이야기다. 리딩 히스토리, 스트릭, SNS 공유, PDF 리포트, Instagram 스토리 카드, 자유 선택 모드. 하나하나가 독립적인 기능이지만, 모두 "사용자가 더 오래, 더 자주 돌아오게 만드는" 공통 목적을 가지고 있다.

리딩 히스토리: localStorage로 만드는 "내 타로 일지"

타로 리딩을 하고 나서 결과를 다시 보고 싶을 때가 있다. "지난주에 받은 리딩이 뭐였더라?" 하는 순간. 이 니즈를 충족시키는 가장 간단한 방법이 리딩 히스토리 기능이다.

데이터베이스 없이 localStorage만으로 구현했다. 사이드 프로젝트에서 데이터베이스는 유지 비용과 복잡성을 급격히 높이는 선택이다. 서버를 운영해야 하고, 사용자 인증이 필요해지고, 개인정보 보호 문제까지 따라온다. localStorage는 브라우저에 데이터를 저장하는 가장 단순한 방법이다. 서버 없이, 인증 없이, 사용자의 기기에만 데이터가 남는다.

리딩 결과를 JSON으로 직렬화해서 저장하고, 히스토리 페이지에서 날짜순으로 보여주는 구조다. Claude에게 "localStorage를 활용한 리딩 히스토리 기능을 만들고 싶다. 날짜, 리딩 타입, 뽑은 카드, 해석 텍스트를 저장해야 한다"고 설명하자, 데이터 구조 설계부터 저장/조회 로직까지 빠르게 나왔다.

한계는 분명하다. 브라우저 캐시를 지우면 데이터가 사라진다. 기기 간 동기화도 안 된다. 하지만 사이드 프로젝트에서 이 정도면 충분하다. "내 타로 일지"라는 가치를 제공하면서도 기술적 복잡성은 최소한으로 유지했다. 완벽한 솔루션보다 적절한 솔루션이 사이드 프로젝트에서는 정답인 경우가 많다.

스트릭: 게이미피케이션으로 리텐션 올리기

듀오링고를 써본 사람이라면 스트릭의 힘을 알 것이다. "연속 7일째 학습 중"이라는 작은 숫자가 오늘도 앱을 열게 만드는 마법 같은 동기 부여. 이 메커니즘을 타로 마스터에 적용했다.

매일 한 번 이상 리딩을 하면 스트릭이 1 올라간다. 하루를 건너뛰면 초기화된다. 단순한 규칙이지만 효과는 상당하다. "7일 연속 리딩" 같은 작은 성취감이 사용자를 매일 돌아오게 만든다.

구현은 localStorage에 마지막 리딩 날짜와 현재 스트릭 수를 저장하는 방식이다. 앱을 열 때마다 오늘 날짜와 마지막 리딩 날짜를 비교해서 스트릭을 갱신한다. 코드 자체는 간단하지만, 날짜 비교 로직에서 자정 기준 처리, 타임존 문제 같은 사소한 함정이 있었다. Claude에게 엣지 케이스를 물어보면서 하나씩 해결했다.

스트릭 표시 UI도 중요했다. 숫자만 덜렁 보여주는 것보다, 불꽃 아이콘과 함께 "연속 5일째" 같은 메시지를 보여주는 게 훨씬 동기 부여가 된다. 게이미피케이션의 핵심은 기능이 아니라 보여주는 방식이라는 걸 이 작업에서 다시 한번 확인했다.

SNS 공유: 바이럴의 씨앗 심기

"오늘의 타로 결과"를 친구에게 공유하고 싶은 건 자연스러운 욕구다. 공유 기능이 없으면 사용자는 스크린샷을 찍어서 보내야 한다. 귀찮으면 안 한다. 공유 버튼을 만들어주면 그 마찰이 사라진다.

ShareButtons 컴포넌트를 만들어서 카카오톡, 트위터, 페이스북, URL 복사 버튼을 배치했다. 각 플랫폼의 공유 API를 연동하는 건 플랫폼마다 방식이 달라서 번거로운 작업이지만, AI에게 각 플랫폼의 공유 URL 형식을 물어보면서 빠르게 처리했다.

공유될 때 보이는 미리보기도 중요했다. Open Graph 메타 태그를 설정해서 "오늘의 타로: 운명의 수레바퀴" 같은 제목과 카드 이미지가 공유 미리보기에 나타나도록 했다. 이 미리보기가 "뭐야, 이거 뭐지?" 하는 호기심을 자극해서 클릭을 유도한다.

모바일에서는 웹 공유 API(navigator.share)도 활용했다. 이 API를 지원하는 브라우저에서는 운영 체제의 기본 공유 시트가 열리면서 더 자연스러운 경험을 제공한다. 지원하지 않는 환경에서는 직접 만든 공유 버튼으로 폴백하는 구조를 만들었다.

PDF 리포트: 리딩 결과를 소장하다

"이 결과를 파일로 저장할 수 있나요?" 이런 요청이 있을 거라고 예상하고 PDF 리포트 기능을 만들었다. jspdf와 html2canvas를 조합하면 화면에 보이는 리딩 결과를 그대로 PDF 파일로 만들 수 있다.

원리는 간단하다. html2canvas가 DOM 요소를 캡처해서 이미지로 만들고, jspdf가 그 이미지를 PDF 파일에 삽입한다. 결과물은 카드 이미지, 해석 텍스트, 리딩 날짜가 포함된 깔끔한 리포트다.

구현 과정에서 한글 폰트 문제가 발생했다. jspdf의 기본 폰트는 영문만 지원하기 때문에 한글이 깨진다. 커스텀 폰트를 Base64로 임베드하는 방식으로 해결했는데, 이 과정이 예상보다 까다로웠다. 폰트 파일을 Base64로 변환하고, jspdf에 등록하는 과정을 Claude와 함께 해결했다.

PDF 품질 최적화도 필요했다. html2canvas의 scale 옵션을 조절해서 해상도를 높이고, PDF 페이지 크기와 여백을 조정해서 인쇄해도 깔끔하게 나오도록 했다. "저장할 가치가 있는 결과물"을 만드는 건, 단순히 기능이 동작하는 것과는 다른 차원의 작업이었다.

Instagram 스토리 카드: 시각적 공유의 힘

SNS 공유가 텍스트 링크 중심이라면, Instagram 스토리 카드는 시각적 공유다. 리딩 결과를 Instagram 스토리에 올리기 좋은 9:16 비율의 이미지로 생성하는 기능이다.

타로 마스터의 남색 배경에 뽑은 카드 이미지와 핵심 해석이 담긴 카드형 이미지를 만든다. html2canvas로 숨겨진 DOM 요소를 캡처해서 이미지 파일로 다운로드하는 방식이다. 사용자는 이 이미지를 Instagram 스토리에 바로 올릴 수 있다.

디자인이 핵심이었다. 공유되는 이미지 자체가 "이 앱 뭐야?"라는 호기심을 불러일으켜야 한다. 카드 이미지를 중앙에 크게 배치하고, 앱 이름과 URL을 하단에 넣었다. 보는 사람이 자연스럽게 앱을 찾아올 수 있는 장치다.

이 기능은 Claude에게 "Instagram 스토리 비율의 공유 이미지를 생성하는 컴포넌트를 만들고 싶다"고 하자 기본 구조가 빠르게 나왔다. 9:16 비율 계산, 캔버스 렌더링, 이미지 다운로드 트리거까지. 디자인 미세 조정만 내가 직접 하면 됐다.

자유 선택 모드: 78장 중 직접 고르는 경험

기존에는 랜덤으로 카드가 뽑히는 방식만 있었다. 하지만 실제 타로에서는 펼쳐진 카드 중 "끌리는 카드"를 직접 고르는 것이 중요한 의미를 가진다. 이 경험을 디지털로 옮긴 것이 자유 선택 모드다.

78장의 카드 뒷면이 화면에 펼쳐지고, 사용자가 직접 탭해서 카드를 선택한다. 선택한 카드가 뒤집어지면서 결과가 나타난다. 랜덤 뽑기와 결과적으로는 같을 수 있지만, "내가 직접 골랐다"는 행위가 리딩 결과에 대한 몰입도를 완전히 다르게 만든다.

78장을 화면에 보여주는 레이아웃이 기술적 과제였다. 모바일 화면에 78장을 전부 보여주면 카드가 너무 작아진다. 스크롤 가능한 그리드와 페이지네이션 방식을 테스트해본 끝에, 카드들이 약간씩 겹치면서 부채꼴로 펼쳐지는 레이아웃을 선택했다. 실제 타로에서 카드를 펼치는 모습과 가장 비슷한 형태였다.

터치 영역이 겹치는 문제도 있었다. 카드가 겹쳐 있으니 의도한 카드가 아닌 옆 카드가 선택되는 경우가 발생했다. 터치 영역의 z-index 조정과 터치 좌표 기반 가장 가까운 카드 판별 로직을 추가해서 해결했다. 이런 세부적인 UX 문제는 실제로 모바일에서 손가락으로 테스트하지 않으면 발견할 수 없는 종류의 것이다.

AI 협업의 속도감, 그리고 그 이면

여섯 가지 기능을 나열하면 대단해 보이지만, 하나하나는 그렇게 복잡한 기능이 아니다. localStorage 저장, 날짜 비교, 외부 라이브러리 연동, 캔버스 렌더링. 개별적으로 보면 중급 수준의 프론트엔드 작업이다.

AI 협업의 진짜 가치는 "시작의 마찰"을 줄이는 것에 있었다. 새로운 기능을 만들 때 가장 시간이 많이 드는 건 코딩이 아니라 "어떻게 구현할지 방향을 잡는 것"이다. jspdf의 사용법을 공식 문서에서 처음부터 읽어야 하는 것과, AI에게 "이런 결과물을 만들고 싶다"고 말하고 핵심 코드를 받아서 이해한 후 수정하는 것은 속도 차이가 크다.

다만, 속도가 빠르다고 해서 품질을 무시하면 안 된다. AI가 빠르게 만들어준 코드에는 종종 엣지 케이스 처리가 빠져 있거나, 메모리 관리가 허술하거나, 에러 핸들링이 미흡한 경우가 있다. 빠르게 만들고, 꼼꼼히 검증하는 리듬이 중요하다.

결국 이 여섯 가지 기능을 통해 타로 마스터는 "카드를 뽑아주는 웹페이지"에서 "매일 들어와서 기록하고 공유하는 타로 앱"으로 진화했다. 기능 하나하나보다 이 기능들이 모여서 만드는 전체 경험의 변화가 더 중요하다.

다음 편 예고

기능을 쏟아낸 후 마주한 가장 큰 벽은 모바일 최적화였다. 데스크톱에서 완벽하게 동작하던 것이 모바일에서 깨지는 순간들, Chrome 모바일의 악명 높은 하단 탭 위치 버그, 그리고 Linear 스타일로 UI를 전면 개편한 이야기를 다음 편에서 다룬다.

댓글

이 블로그의 인기 게시물

사랑을 직접 올리지 않는 설계

시작의 충동 — "타로 웹앱을 만들어볼까?"

감정을 변수로 옮기다 — 3계층 감정 모델