마신 커피 원두를 다시 찾아볼 수 있게 기록해두려고 만든 서비스입니다. 원두 정보와 테이스팅 노트를 남기고, 블렌드를 구성해 보고, 산지 카탈로그를 둘러보다가 쌓인 기록은 통계로 확인할 수 있습니다. 한국어와 영어를 지원합니다.
- 원두 기록을 등록·수정·삭제할 수 있고, 테이스팅 태그와 블렌드 구성요소를 함께 저장합니다.
- 블렌드는 싱글오리진을 비율로 조합하며 합계가 100%가 아니면 저장되지 않습니다.
- 산지 카탈로그에서 국가 → 지역 → 농장/생산자 순서로 탐색할 수 있습니다.
- 산지, 프로세스, 품종, 월별, 점수 분포 통계를 제공합니다.
- 기록 전체를 JSON으로 내려받을 수 있습니다.
- 지정된 운영자는 비공개 운영홈에서 서버·DB 상태를 확인하고 산지 한글 표기를 수정할 수 있습니다. 접속 방법은 관리자 운영 안내를 참고하세요.
- 원두 포장지 사진에서 기본 정보를 읽어 선택한 항목만 등록 화면에 채울 수 있습니다. 사진은 브라우저 안에서만 처리합니다.
| 구분 | 사용 기술 |
|---|---|
| 프런트엔드 | Next.js 16 (App Router, Server Actions), React 19, Tailwind CSS v4, Zustand, Recharts, Zod |
| 백엔드 | Go (Gin) REST API, 학습용 gRPC Stats 서비스 |
| 인증 | Supabase Auth — Google/Kakao OAuth, JWT |
| 데이터베이스 | Supabase / PostgreSQL 17 — RLS, 원자적 저장 함수 |
| 인프라 / 검증 | Docker, Playwright QA 28개 |
요청은 Next.js가 받아 Server Action에서 처리하고, 데이터가 필요한 작업은 Go API로 넘깁니다. Go API는 Supabase가 발급한 JWT를 검증한 뒤 트랜잭션 안에서 쿼리를 실행합니다.
데이터 격리에 신경을 썼습니다. 테이블마다 RLS를 걸어서 인증받은 사용자도 자기 데이터만 읽을 수 있고, Go API는 요청마다 권한을 낮춰 모든 쿼리가 RLS를 거칩니다. 원두를 저장할 때는 원두·태그·블렌드 구성요소를 하나의 DB 함수로 묶어 한 트랜잭션에 기록하고, 실패하면 전부 롤백됩니다.
Docker Desktop이 필요합니다.
npm install
npm run staging:up # 스테이징 실행 → http://localhost:3100
npm run staging:qa # 전체 QA (28개)
npm run staging:down # 종료전체 색상 테마는 환경값 하나로 바꿉니다. 사용자 화면에는 테마 선택 기능이 없습니다.
BEANMAP_THEME=mist # mist | cream | contrast일상 점검과 심층 감사:
npm run check # 타입·린트·단위 테스트·디자인·빌드 전체 실행
npm run check:fast # 빌드를 제외한 빠른 점검
npm run audit # 의존성·보안·컨테이너 설정까지 심층 감사로그인 후 새 기록에서 사진 선택 → 자동 인식 결과 확인 → 기본 정보 채우기를 사용합니다. 사진을 끌어 놓아도 바로 인식하며, 먼저 읽힌 정보를 보여주는 동안 나머지 글자를 확인합니다. 원두명·로스터리·중량, 구성별 비율과 원문, 포장지의 맛 설명을 한 화면에 정리합니다. 결과 옆 다시 인식으로 같은 사진을 다시 읽습니다. 재인식 중에는 이전 결과를 표시하며, 실패하거나 취소해도 이전 결과와 선택 항목을 유지합니다. 다른 사진을 선택하면 새로 시작합니다. 사진의 글자는 PaddleOCR.js 0.4.2와 PP-OCRv5 한국어 모델로 브라우저 안에서 읽습니다. 이름이나 인쇄된 블렌드 배합을 놓친 경우에는 Tesseract.js 7로 추가 확인합니다. 생성형 AI나 외부 인식 API를 호출하지 않으며 API 키나 별도 인식 서버가 필요하지 않습니다.
읽은 글자에서 원두명·로스터리·산지·품종·가공법·배전도·로스팅일·중량 등 명시된 정보를 규칙으로 분류합니다. 모호하거나 충돌하는 값은 채우지 않습니다. 이름과 로스터리는 명시된 항목 표시 또는 블렌드 제목·구성행·커피 워드마크의 문맥이 뒷받침할 때 후보로 제시합니다. 인식 원문도 펼쳐 확인할 수 있습니다. 포장지 디자인과 사진 상태에 따라 읽기가 틀릴 수 있으므로 선택한 후보를 확인하고 최종 저장합니다. 평점·감상·테이스팅 태그는 자동으로 바뀌지 않습니다. 포장지의 맛 설명은 인식 화면의 참고 정보이며, 명확한 영문 향미 표현은 일반 향미 사전으로 한국어 풀이를 함께 보여줍니다. 인식 원문과 풀이를 구분합니다. 구성별 국가와 비율이 명시되고 합이 100%인 블렌드는 같은 나라의 서로 다른 원두도 구분해 보여줍니다. 새 빈 폼에서는 읽힌 블렌드 구성을 미리 선택하여 한 번에 채울 수 있습니다. 이미 입력하거나 복원한 정보의 교체는 항목 선택을 펼쳐 확인합니다. 사용자가 건드리지 않은 새 폼의 기본 가공법은 선택한 블렌드 구성과 함께 채웁니다. 인식 후 직접 수정한 필드와 그 하위 정보를 바꿀 상위 항목은 체크를 다시 선택해야 교체됩니다.
인식 프로그램과 한국어·영어 자료는 npm run build 및 npm run staging:up/qa에서
버전과 해시가 고정된 공식 소스·모델로 준비합니다. 사이트의 /ocr/paddle-0.4.2-v1/과
/ocr/tesseract-7.0.0/에서 직접 제공하며 외부 CDN을 사용하지 않습니다.
최초 빌드의 컴파일 도구와 캐시는 OCR 빌드 안내를 참고하세요.
파일은 해당 엔진을 처음 사용할 때 내려받고 언어 자료는 브라우저에서
캐시합니다. 다운로드 및 인식 중 진행률과 취소를 제공하며, 취소·사진 제거·페이지 이탈 시
OCR worker를 종료합니다. 모바일 기기별 성능에 따라 인식 시간이 달라집니다.
JPEG·PNG·WebP 최대 10MB를 선택할 수 있고, 브라우저에서 방향을 맞춘 PNG로 정규화합니다.
전체 사진으로 원두명을 먼저 확인하고, 글자 영역을 찾은 경우 해당 부분을 추가로 읽습니다.
부분 사진의 잘린 제목이 먼저 확인한 원두명을 덮어쓰지 않도록 처리합니다.
한 장이 대부분 글자인 일반 라벨은 한 번만 읽으며, 출력은 긴 변 2600px와 픽셀 수 상한으로 제한합니다.
여러 번 읽은 결과는 같은 구성의 줄 순서 차이를 허용하되, 서로 다른 값이 충돌하면 추측하지 않습니다.
보조 인식에서도 중량의 단위가 불분명하면 인식된 글자 위치에서 작은 부분만 다시 읽습니다. 숫자 끝의 9나 0을
g로 치환하지 않으며, 레시피·영양·날짜 등 다른 수치는 중량 후보에서 제외합니다.
사진과 인식 원문은 서버로 전송하거나 앱 DB·임시저장에 보관하지 않습니다.
사용자가 적용한 기본 정보만 기존 초안·최종 저장 정책을 따릅니다. 기존 /api/beans/label
서버 인식 경로와 BEAN_LABEL_API_* 설정은 제거했습니다.