Fullstack 85

heo4·2026년 8월 17일

Fullstack

목록 보기
40/70

풀스택

EasyPrompt 개발일지

일반사용자용 카테고리 전용 대화형 위저드 만들기

오늘의 목표

EasyPrompt에는 5060세대·어르신을 위한 "일반사용자용" 카테고리가 있다. 문제는 이 카테고리의 프롬프트 10개도 사무용 프롬프트와 똑같은 방식으로 노출되고 있었다는 것. 즉 label/placeholder가 붙은 입력 폼 여러 개를 한 화면에 쭉 늘어놓고, 타이핑하는 대로 실시간 미리보기가 갱신되는 구조였다.

이 방식은 사무직 사용자에게는 편하지만, 어르신 입장에서는 여러 개의 빈 칸이 한 번에 보이는 것 자체가 진입장벽이 될 수 있다. 그래서 한 번에 하나씩만 묻는 대화형 위저드로 바꾸기로 했다. 각 질문에는 "이걸 왜 물어보는지"에 대한 짧은 설명을 붙여서, 완성된 결과를 보여줄 때 그 이유까지 함께 보여주는 것이 핵심 아이디어였다.

단, 사무용 프롬프트(PromptDetail.jsx의 기존 폼 로직)는 절대 건드리지 않는 것을 전제 조건으로 잡았다.

구현 과정

1. DB 스키마 확장

prompt_forms 테이블에 point_reason TEXT 컬럼을 추가했다. 마이그레이션 파일(backend-hono/migrations/0001_add_prompt_forms_point_reason.sql)을 만들고 schema.sql도 동기화해뒀다.

일반사용자용(category_id=7) 프롬프트 10개, 폼 17개 전부에 대해 "이 항목을 왜 물어보는지" 한 줄 설명을 직접 작성해서 UPDATE했다. 예를 들어 "누구에게 보내는 문자인가요?" 같은 질문에는 "받는 사람에 따라 존댓말 수위나 표현이 달라지기 때문이에요" 같은 이유를 달아주는 식이다.

2. 로컬 D1이 스테일 상태였던 문제

원격 D1을 마이그레이션한 뒤 로컬에서 검증하려고 보니, 로컬 D1에는 애초에 "일반사용자용" 카테고리 자체가 없었다. 로컬 시드가 예전 버전에 머물러 있어서 카테고리가 6개뿐이었고, 심지어 id=7 자리는 이미 "인사(HR)"가 차지하고 있는 상태였다.

임시로 카테고리를 id=9로 새로 넣고, 원격에 있는 프롬프트 10개 + 폼 17개를 그대로 복제해서 로컬에 심었다. 이건 검증용 임시 데이터라 테스트 후 삭제했고 저장소에는 커밋하지 않았다.

3. 백엔드 — forms 응답에 point_reason 포함

backend-hono/src/routes/prompts.ts의 GET /prompts/:id 핸들러가 반환하는 forms 배열에 point_reason이 포함되도록 컬럼 목록과 타입을 수정했다.

4. 프론트엔드 — SimpleWizard 컴포넌트

frontend/src/components/SimpleWizard.jsx를 신규로 만들었다. 동작 방식은 다음과 같다.

  • forms를 하나씩 카드 형태로 보여주고, 진행률(n/총 개수)을 표시
  • 마지막 항목까지 입력하면 "완성하기" 버튼으로 /generate를 호출해 서버에서 실제 치환된 문장을 받아옴
  • 완성된 문장 카드 아래에 "이렇게 완성됐어요" 섹션을 두고, 각 질문의 label → 사용자가 입력한 값 → point_reason을 나란히 보여줌
  • 클립보드 복사 버튼 포함

톤은 기존에 만들어둔 Senior.jsx 스타일(큰 폰트 20~28px, 버튼 최소 56px, 진한 파스텔 대비)을 그대로 따랐다.

PromptDetail.jsx에는 카테고리 이름이 '일반사용자용'인 경우에만 기존 폼 UI 대신 <SimpleWizard>를 렌더링하도록 분기를 추가했다. 사무용 분기는 이제 항상 isSenior=false로 고정되므로, 글씨 크기 등을 결정하던 불필요한 삼항연산자들을 정리했다(렌더링 결과 자체는 기존과 동일하게 유지).

로컬 검증에서 겪은 삽질

이 환경(WSL)에는 Playwright가 필요로 하는 libnspr4, libnss3 같은 시스템 라이브러리가 없었고, sudo 권한도 없어서 기본 설치 방법이 막혔다. 다행히 예전에 /home/sbs/.local/browser-libs/extracted에 미리 추출해둔 라이브러리 세트가 있어서, LD_LIBRARY_PATH로 그 경로를 지정해 headless Chromium을 띄우는 데 성공했다.

이후 wrangler dev(백엔드, :8787)와 vite(프론트, :5173)를 동시에 띄우고, .env.development.local로 API base만 잠깐 로컬로 오버라이드한 상태에서 Playwright 스크립트로 실제 흐름을 확인했다.

  • /prompts/65(일반사용자용)에서 위저드가 2단계까지 정상 진행
  • 완성 화면에서 "이렇게 완성됐어요" 섹션에 point_reason이 제대로 노출
  • 클립보드 복사 시 "복사됐어요!" 문구까지 정상 동작
  • /prompts/3(사무용) 페이지는 기존 폼 UI 그대로 렌더링되는 것도 재확인 — 회귀 없음

테스트 후 임시 오버라이드 파일은 삭제했고, 사용자도 별도로 직접 동작을 확인했다.

마무리

백엔드(f92e634)와 프론트엔드(a0d0513) 각각 커밋 후 푸시까지 완료했다.

오늘 작업의 핵심은 "같은 데이터라도 대상 사용자에 따라 노출 방식은 완전히 달라야 한다"는 점이었다. 사무직에게는 여러 입력 필드를 한 화면에서 빠르게 채우는 게 효율적이지만, 어르신에게는 한 번에 하나씩, 그리고 "왜 이걸 물어보는지"를 설명해주는 대화형 흐름이 훨씬 덜 부담스럽다. 같은 백엔드 API(/generate)를 재사용하면서도 프론트 레이어만 완전히 다르게 가져간 것이 이번 작업의 포인트였다.

0개의 댓글