AI로 시작하기
Claude, ChatGPT, Cursor 같은 AI에게 맡겨서 채팅을 붙이는 법이다. 사람이 할 일은 둘이다. 가입하고 계정 키 하나를 받고, 아래 프롬프트를 복사해 AI에게 붙여 넣는다. 앱 만들기와 키 받기, 코드는 AI가 한다.
AI는
https://chat.gravex.app/llms.txt를 읽고 이 서비스의 사용법을 안다. 프롬프트마다 그 주소를 넣으면 된다. 영어 문서를 읽히려면https://chat.gravex.app/en/llms.txt다.
1단계. 가입하고 계정 키 하나를 받는다
이 단계는 사람이 한다. 가입과 첫 키 발급은 AI가 대신할 수 없다. 나머지는 AI에게 맡긴다.
- 콘솔에서 가입한다. 이메일로 받은 6자리 코드를 넣으면 된다(비밀번호는 없다).
- 위쪽 메뉴의 API 키 → 새 키 → 이름(예:
노트북 .env)을 적는다. 키(ck_…)가 한 번만 보이니 바로 복사한다. - 프로젝트의
.env에 직접 넣는다. 이 키를 AI 대화창에 붙여 넣지 않는다. 대화는 기록으로 남는다. AI에게는 ".env의CHAT_ACCOUNT_KEY를 읽어서 써"라고 시키면 된다. 아래 첫 프롬프트에 이미 들어 있다.
# .env — 값은 사람이 넣는다
CHAT_ACCOUNT_KEY=ck_...ck_는 내 계정의 전권이다. 서버나 로컬 .env에만 두고, 프런트 코드와 git 저장소에는 넣지 않는다. 일이 끝나면
API 키 화면에서 폐기하는 것을 권한다. 폐기해도 이미 만든 앱과 그 pk_·sk_는 그대로 동작한다 — 계정 키는 콘솔 API를
부르는 자격일 뿐이다. 폐기한 뒤에는 .env의 CHAT_ACCOUNT_KEY 줄도 지운다. 쓰는 법은 콘솔 API에 있다.
AI가 앱을 만들면 응답에 pk_…(프런트에 넣는 공개값)와 sk_…(서버 전용 비밀값)가 같이 온다. sk_는 그 응답에서만 보인다.
그래서 AI가 받자마자 .env의 VITE_CHAT_PK·CHAT_SECRET_KEY에 쓰고 화면이나 대화에 찍지 않게 시킨다. 콘솔에서 앱을 직접 만들고 싶다면
새 앱을 눌러 두 키를 .env에 손으로 넣어도 된다. 그때는 아래 첫 프롬프트의 앞 두 줄을 뺀다.
# .env — 앱을 만들면 AI가 채운다
VITE_CHAT_PK=pk_...
CHAT_SECRET_KEY=sk_...환경변수 이름은 모든 문서가 같다. 프런트가 읽는 것은 VITE_CHAT_PK 하나다 — Vite는 VITE_로 시작하는 변수만 브라우저
번들에 넣는다. CHAT_SECRET_KEY·CHAT_ACCOUNT_KEY·CHAT_WEBHOOK_SECRET·CHAT_GUEST_SECRET은 서버에서만 읽는다.
Vite의 envPrefix를 CHAT_로 넓히지 않는다. 그러면 sk_와 ck_가 브라우저 번들에 그대로 실린다. Vite가 아닌 프런트라면
pk_에만 그 프레임워크의 공개 접두사를 붙인다(Next.js는 NEXT_PUBLIC_CHAT_PK).
2단계. 프롬프트를 복사한다
카드마다 이 프롬프트로 생기는 것 한 줄과, 복사해서 쓰는 프롬프트가 있다. 위에서부터 차례로 해도 되고
필요한 것만 골라도 된다. 새 대화에서 쓰는 프롬프트에는 1번의 첫 줄(…llms.txt 를 읽고)을 앞에 붙인다.
1. 첫 채팅방 (로비)
생기는 것: 실시간으로 이야기하는 채팅방 하나. 로그인이 있는 프로젝트면 회원 이름으로, 없으면 게스트로 들어간다.
https://chat.gravex.app/llms.txt 를 읽고, .env의 CHAT_ACCOUNT_KEY로 앱을 만들고 pk_/sk_를 .env에 채운 뒤 로비 채팅을 붙여줘.
- 허용 origin은 내 프론트 개발 서버 주소야(Vite 기본은 http://localhost:5173). 포트가 다르거나 겹쳐서 다른 포트로 뜨면 실제로 뜬 주소를 넣어줘. 나중에 주소가 바뀌면 PATCH /api/apps/{id}/keys/{keyId}로 고치는데, origin 목록을 통째로 바꾸니 기존 값도 같이 보내줘.
- 콘솔 API 문서(https://chat.gravex.app/docs/console-api)대로 POST /api/apps로 앱을 만들어줘. 응답의 publishableKey.value는 VITE_CHAT_PK에, secretKey.value는 CHAT_SECRET_KEY에 곧바로 파일로 써줘. sk_는 그 응답에서만 보이니 화면, 로그, 대화에 출력하지 마.
- CHAT_ACCOUNT_KEY는 앱을 만드는 스크립트에서만 읽어줘. 앱 코드와 프론트 번들에는 절대 넣지 마. 프론트에는 VITE_CHAT_PK만 보이게 하고 Vite의 envPrefix는 넓히지 마. .env가 .gitignore에 있는지도 확인해줘.
- 내 프로젝트의 프레임워크와 로그인 방식을 먼저 살펴보고 거기에 맞춰 간단한 채팅(로비 방 하나)을 붙여줘. 프로젝트가 없으면 React(Vite) + Express로 가장 단순하게 만들어줘.
- 백엔드: 채팅 토큰을 발급하는 엔드포인트와 방 'lobby' 만들기(get-or-create). 로그인이 있으면 로그인한 사용자에게 chat.token()으로, 로그인이 없으면 문서의 '비로그인 사용자(게스트)'대로 chat.guest()로 발급해줘. 데모용 로그인을 새로 만들지 마. 게스트에 쓰는 CHAT_GUEST_SECRET은 32자 이상 랜덤 값으로 .env에 만들어줘.
- 프론트: @kispi/chat SDK로 접속해서 메시지 목록, 입력창, 보내기
- 끝나면 llms.txt의 '화면 점검표'와 하나씩 대조한 결과, 내가 로컬에서 확인하는 방법, 다 쓴 CHAT_ACCOUNT_KEY를 폐기하는 방법(폐기해도 만든 앱과 pk_/sk_는 그대로 동작한다)을 알려줘.2. 회원 / 비회원 나누기
생기는 것: 로그인한 회원은 자기 이름으로, 로그인 없이 온 방문자는 게스트로 같은 방에서 말하는 채팅.
위 채팅에서 로그인한 회원과 로그인하지 않은 방문자를 나눠서 둘 다 말할 수 있게 해줘.
문서의 '비로그인 사용자(게스트)' 방식대로 chat.guest()로 게스트 신원을 발급하고, 브라우저가 보낸 id에는 절대 서명하지 마.
게스트 이름은 '손님-' 뒤에 짧은 숫자로 보여주고, 게스트용 비밀값 CHAT_GUEST_SECRET이 .env에 없으면 32자 이상 랜덤 값으로 만들어줘.3. 글마다 댓글방
생기는 것: 게시글(상품, 영상, 기사)마다 자기 댓글 채팅방이 생기는 구조.
게시글 상세 페이지마다 그 글 전용 채팅방(댓글방)을 붙여줘.
방 키는 'post-<글 id>' 형태로 하고, 방은 백엔드가 get-or-create로 만들고 프론트는 roomByKey로 열어줘.
글을 열면 최근 메시지가 보이고, 위로 스크롤하면 과거를 더 불러오게 해줘(loadOlder).4. 스레드 답글
생기는 것: 메시지에 "답글"을 누르면 옆 패널에 그 메시지의 스레드가 열리고, 원 메시지에 답글 수가 붙는 채팅.
메시지마다 '답글' 버튼을 달아서 슬랙처럼 스레드를 만들어줘.
- 스레드 답글은 send({ text, threadId: 원 메시지 id })로 보내고, 메인 타임라인에는 threadId가 없는 메시지만 보여줘.
- 원 메시지에는 답글 수(thread.count)를 보여주고, 누르면 오른쪽 패널에 그 스레드를 열어줘.
- 특정 메시지를 인용해서 답하는 건 replyTo로, 스레드와는 따로 구현해줘.5. 이모지 리액션
생기는 것: 메시지에 👍 같은 이모지를 달고 개수가 실시간으로 바뀌는 리액션.
메시지에 이모지 리액션을 붙여줘. 메시지에 마우스를 올리면 이모지 선택 버튼이 나오고, 누르면 room.react / room.unreact를 부르게 해줘.
메시지 아래에는 이모지별 개수를 보여주고, 내가 누른 이모지는 강조해줘. 개수는 서버에서 오는 값을 그대로 쓰고, 한 메시지에 이모지 종류는 최대 20가지라는 한도도 처리해줘.6. 멘션과 안 읽음 배지
생기는 것: @이름으로 사람을 부르고, 방 목록에 안 읽은 메시지 수와 멘션 배지가 뜨는 채팅.
멘션과 안 읽음 표시를 붙여줘.
- 입력창에서 @를 치면 방 참여자 자동완성이 뜨고, 고르면 entities에 { type: 'mention', userId, offset, length }를 담아 보내줘. 본문에서는 멘션을 강조해줘.
- 방 목록(chat.rooms.list)에 방마다 안 읽은 수와 멘션 수 배지를 그리고, 방을 보면 markRead로 읽음 처리해줘.
- 앱 아이콘 옆 전체 안 읽음 수는 chat.unread()로 구해줘.7. 입력 중 · 접속 중 표시
생기는 것: "한나가 입력 중…"과 지금 방에 들어와 있는 사람 수·목록.
입력 중 표시와 접속 중 표시를 붙여줘.
- 입력창에 글자를 칠 때마다 room.typing()을 부르고(SDK가 알아서 줄여서 보내), typing 이벤트로 'OO가 입력 중…'을 보여줘. 내 이벤트는 건너뛰고, 마지막 이벤트 뒤 5초 넘게 지나면 지워줘.
- 방 헤더에 지금 접속 중인 사람 수(room.presence)를 보여주고, 누르면 목록을 보여줘.8. 1:1 DM
생기는 것: 프로필의 "메시지 보내기"를 누르면 두 사람만 있는 대화방이 열린다.
사용자 프로필에 '메시지 보내기' 버튼을 달아서 1:1 DM을 만들어줘.
백엔드에서 두 사람의 id로 rooms.ensureDM을 불러 방 id를 돌려주고, 프론트는 그 방을 열어줘.
내 DM 목록 화면은 chat.rooms.list()로 만들고, 마지막 메시지와 안 읽음 수를 보여줘.9. 공지 채널과 시스템 메시지
생기는 것: 운영자만 글을 쓰고 모두가 읽는 공지 방, 그리고 서버가 보내는 안내 메시지.
운영자만 글을 쓸 수 있고 다른 사람은 읽기만 하는 '공지' 방을 만들어줘. type: 'channel'로 만들고, 운영자 계정을 owner나 admin으로 지정해줘.
백엔드에서 '점검 10분 전입니다' 같은 시스템 메시지를 그 방과 로비에 보내는 함수(messages.send, kind: 'system')도 만들어줘.10. 금칙어와 이미지 업로드
생기는 것: 욕설을 가리고, 검사를 통과한 사진만 올라가는 채팅.
https://chat.gravex.app/docs/uploads 를 읽고 채팅에 이미지 업로드를 붙여줘. 파일은 엔진이 저장하지 않으니 내 S3 버킷에 올리는 방식이야.
- 문서 순서대로: presigned POST 발급, 검사 전에는 비공개 접두사에 두기, Rekognition 검사 후 통과한 것만 공개로 옮기기
- before_publish 웹훅에서 첨부 URL이 내 공개 접두사의 이 사람 파일인지 확인하고, 같은 훅에서 금칙어는 *로 가려줘.
- AWS 자격 증명은 코드에 쓰지 말고 IAM 역할을 쓰는 방향으로 하고, 내가 AWS 콘솔에서 해야 할 일은 따로 목록으로 알려줘.11. 웹훅으로 알림 보내기
생기는 것: 새 메시지가 오면 내 서버가 알아채서 푸시·메일 같은 알림을 보내는 구조.
채팅 웹훅 엔드포인트를 만들어줘. 문서의 '웹훅' 방식대로 서명을 raw 바디로 검증하고, 같은 delivery는 한 번만 처리하고, 먼저 200으로 답한 뒤 비동기로 처리해줘.
message.created가 오면 보낸 사람을 뺀 방 멤버에게 알림을 보내야 해. 알림 수단(웹푸시, FCM, 이메일 등)은 아직 정하지 않았으니 먼저 나한테 물어보고, 정해질 때까지는 보낼 자리에 TODO를 남겨줘.12. 신고 대응과 데이터 내보내기
생기는 것: 문제 사용자를 막고 메시지를 거두는 운영 도구와, 채팅 데이터를 통째로 받는 백업.
운영용 스크립트를 만들어줘.
- 사용자 id를 받아 밴(users.ban)하고 그 사람의 메시지를 모두 지우는(users.purgeMessages, purgeCapped가 false가 될 때까지 반복) 명령
- 앱 전체 데이터를 내려받는 명령. GET /v1/export를 sk_로 불러 NDJSON 파일로 저장하고, 마지막 줄이 end가 아니면 불완전한 파일이라고 알려줘.13. 다른 언어(iOS · Android · Flutter · Python)로 클라이언트 만들기
생기는 것: 공식 SDK 없이 Swift·Kotlin·Dart·Python 등으로 만든 채팅 클라이언트 — 접속, 방 구독, 메시지 주고받기, 재접속과 구멍 메우기.
https://chat.gravex.app/llms.txt 를 읽고, 문서의 'REST API 레퍼런스'와 '실시간 프로토콜(WebSocket)'만 보고 Swift로 채팅 클라이언트를 만들어줘. Kotlin이나 Flutter(Dart)로 바꿔도 돼.
- 먼저 '실시간 프로토콜' 끝의 'SDK 없이 클라이언트 만들기' 체크리스트를 읽고, 항목을 하나씩 구현해줘.
- 접속(auth → hello)과 ping, 방 subscribe(since), 메시지 보내기(clientMessageId로 중복 방지), 이벤트 반영
- 재접속은 지수 백오프에 지터를 넣고, 다시 붙으면 방마다 REST 히스토리로 seq 구멍을 메워줘.
- 유저 토큰은 내 백엔드에서 받아 오는 함수로 두고 접속할 때마다 새로 받아줘. sk_는 앱에 절대 넣지 마.
- 끝나면 체크리스트의 어느 항목을 했고 안 했는지 표로 알려줘.화면 점검표
AI가 화면을 다 만들었으면 이 목록과 대조하게 한다(1번 프롬프트에 들어 있다). SDK가 대신 해 주지 않아 빠뜨리기 쉬운 것들이다.
reset이 오면 다시 그린다. 렌더한 목록을 버리고room.messages로 처음부터 그린다. 옛 목록에 이어 붙이면 구멍이 남는다.- 과거를 불러와도 보던 자리를 지킨다.
loadOlder()로 위에 붙은 만큼 스크롤 위치를 보정하고, 새 메시지에는 사용자가 맨 아래에 있을 때만 따라 내려간다. - 본 타임라인에서 스레드 답글을 뺀다.
room.messages에는threadId가 있는 행도 섞여 있다. - 입력 중 표시는 내 이벤트를 건너뛰고 5초쯤 뒤 지운다.
typing은 보낸 사람에게도 오고, "입력 멈춤" 이벤트는 없다. history()로 읽은 행의 리액션은 직접 고친다. SDK는room.messages안의 행만 고친다. 스레드 패널처럼history()로 그린 목록은reaction.added·reaction.removed의count와userId로 개수와myReactions를 고친다.- 리액션 개수는 서버 값으로 그린다.
react()는myReactions를 먼저 바꾸고 개수는 이벤트가 오면 고친다. 그 사이 개수가 없으면 이모지만 그린다.myReactions가 없는 행은 아무것도 누르지 않은 것으로 본다. - 본문은 평문으로 넣는다.
text를 HTML로 넣지 않는다(innerHTML,dangerouslySetInnerHTML,{@html}). 멘션 강조도 텍스트를 잘라 감싸서 한다. rate_limited면 기다린다.ChatError의retryAfterMs만큼 보내기를 막고 남은 시간을 보여 준다.
되는지 먼저 물어보기
구현을 시키기 전에 되는지부터 물어봐도 된다. AI가 llms.txt를 읽고 이 서비스가 지원하는 범위 안에서 답한다.
https://chat.gravex.app/llms.txt 를 읽고 답해줘. 구현은 하지 말고, 되는지와 어떻게 하는지만 알려줘. 문서에 근거가 없으면 모른다고 해줘.
1. 회원/비회원 나눠서 채팅 가능해?
2. 댓글이나 스레드 가능해?
3. 이모지 리액션 가능해?
4. 읽음 표시나 안 읽은 메시지 수는?
5. 앱 푸시 알림도 보내줘?
6. 사진은 올릴 수 있어? 어디에 저장돼?
7. 1:1 채팅이랑 공지 전용 방은?
8. 나중에 데이터를 통째로 가져갈 수 있어?
9. iOS·Android 앱에서도 붙일 수 있어? 공식 SDK가 없으면 어떻게 해?지원하지 않는 것
AI가 가능하다고 지어내지 않도록 미리 적어 둔다. 대안이 있는 것은 대안과 함께.
| 안 되는 것 | 대신 |
|---|---|
| 푸시 알림 발송 | message.created 웹훅을 받아 내 서버에서 FCM·웹푸시·이메일로 보낸다(프롬프트 11) |
| 파일 저장 | 엔진은 주소와 메타데이터만 싣는다. 내 버킷에 올린다(이미지 업로드와 모더레이션) |
| 네이티브(iOS·Android·Flutter) 공식 SDK | 공식 SDK는 JS/TS뿐이지만 REST(REST API 레퍼런스)와 실시간 프로토콜(실시간 프로토콜)이 공개돼 있어 AI로 다른 언어 클라이언트를 만들 수 있다(프롬프트 13) |
| 로그인·회원가입 | 엔진은 신원을 모르고 내 백엔드가 서명한 토큰을 믿는다. 방문자는 게스트로 받는다(프롬프트 2) |
| 금칙어·이미지 자동 검사 | 훅(before_publish)을 열어 둘 뿐이고 판정은 내가 한다(프롬프트 10) |
| 유저 토큰으로 메시지 검색 | 검색(q)은 서버 키(sk_)로만 된다. 내 백엔드에 검색 API를 둔다 |
| 음성·영상 통화 | 없다. 텍스트 채팅과 첨부 주소만 다룬다 |
막히면
AI가 만든 코드가 안 돌면 에러 메시지와 함께 이렇게 묻는다: "https://chat.gravex.app/llms-full.txt 를 읽고 이 에러의 원인을 찾아줘."
llms-full.txt는 문서와 SDK 레퍼런스 전체를 한 파일에 담은 것이다. 주소를 열어 보지 못하는 AI라면 그 파일의 내용을
대화에 통째로 붙여 넣고 시작한다. 사람이 직접 읽으려면 시작하기부터다.