(완료)개선안 01. 성경 종류 선택 시 앱/웹 표시 언어 변경 → 설정 탭에서 언어 선택 시 변경

(완료)개선안 02. 상담 고도화

(완료)개선안 03. LLM Gateway 를 통해 OpenAI API 연동하여 사용자가 로컬 LLM 을 쓸지 OpenAI를 쓸지 선택할 수 있도록 개선

→ 기본 로컬 LLM / 사용자가 OPENAI_API_KEY를 UI 에서 입력하면 자신의 OPENAI API LLM 을 호출(키가 없으면 기본 로컬 LLM 호출)

(완료)개선안 04. Google OAuth 로그인 연동

(계획)안정성 테스트 01. 상담 기능 테스트 수행


→ 전반적인 테스트 결과 정상적이면 클라우드 이관을 통해 앱배포 준비를 해볼 것!

** OpenAI API KEY 를 일단 내 KEY 로 적용(서버 키로 사용)

⇒ 사용자가 개인의 OpenAI API Key를 사용하고 싶으면 설정 탭에서 설정하도록!

** 개선안 02. 상담 고도화

0) 공통 목표: “서버가 최종 집행자”로 만들기

프론트가 store_messages를 보내도 서버가 마지막으로 검증/강제해야 합니다.

“익명 체험”에서는 무조건 false (프론트가 true 보내도 무시)

“로그인 사용자”는 서버 DB에 저장된 유저 설정값을 우선 적용 (클라이언트 토글만 믿지 않기)

이때, 턴과 TTL 은 Redis 가 관리하도록 개발

1) 익명 체험 상담 기능 (Anonymous Trial)
1-1. 백엔드: conversation 모델에 “모드”와 “TTL/턴 제한” 추가

Conversation 메타에 최소 필드 3개만 추가하면 운영이 쉬워집니다.

conversation_id: UUID

mode: "anonymous" | "authenticated"

store_messages: bool (익명은 항상 false)

expires_at: datetime (예: now + 2h)

turn_limit: int (예: 10)

turn_count: int (서버에서 증가)

DB 테이블로 만들지 않아도 됩니다. 익명은 메모리/Redis에만 둬도 OK.

저장 위치(추천)

Redis(권장): TTL 자동 삭제가 깔끔함

없다면 서버 메모리(Map) + 주기적 청소(하지만 멀티 인스턴스에서 꼬일 수 있음)

1-2. 백엔드: POST /v1/chat/conversations 로직 수정

프론트에서 store_messages를 보내더라도 서버가 모드에 따라 강제합니다.

규칙

요청이 익명이면

store_messages = false 강제

expires_at = now + TTL

turn_limit = N

turn_count = 0

요청이 로그인 사용자면

store_messages = (유저 설정값)으로 최종 결정
(프론트 payload는 참고만 하거나 무시하는 게 더 안전)

1-3. 백엔드: 메시지 처리 API에서 “턴 제한/TTL” 강제

메시지 저장/응답 전에 체크:

expires_at < now 이면

410 Gone (또는 401) + “세션이 만료되었습니다”

turn_count >= turn_limit 이면

429 (또는 403) + “체험 턴이 종료되었습니다”

통과하면:

turn_count += 1

store.add_message(...) 호출 (이미 구현된 분기 로직 그대로 사용)

포인트: turn_count 증가도 서버가 해야 남용/조작을 막습니다.

1-4. 프론트: 익명 체험 UI 필수 요소 3개

“익명 체험: 저장되지 않음” 고정 배지

“남은 턴 표시: 3/10”

제한 도달 시 화면:

로그인 유도 버튼 (강요 느낌 X)

“저장 옵션은 로그인 후 설정에서 선택 가능” 안내

2) 최초 실행 시 “대화 저장 고지 + 설정 가능” 구현

여기서 중요한 건: (1) 최초 실행 고지와 **(2) 저장 토글(설정)**을 “서로 다른 개념”으로 분리하는 겁니다.

최초 실행 고지: “당신은 선택할 수 있다”를 명확히 알리는 UI

실제 저장 여부: 서버 정책에 의해 집행되는 store_messages

2-1. 프론트: 최초 실행 여부 체크 & 로컬 저장
저장 키 (예시)

has_seen_privacy_notice: true

chat_storage_consent: true|false ← 사용자가 선택한 “기본값”

Web: localStorage
RN: AsyncStorage 또는 가능하면 SecureStore

2-2. 프론트: 최초 실행 모달/화면 구성(권장 3줄)

익명 체험: “대화는 저장되지 않고 앱을 닫으면 사라집니다”

로그인 사용자: “대화 저장은 설정에서 켠 경우에만 저장됩니다”

안전: “위기 표현이 감지되면 상담이 제한되고 도움 안내가 제공될 수 있습니다”

버튼 2개:

[동의하고 시작] → chat_storage_consent = true

[저장하지 않고 사용] → chat_storage_consent = false

마지막에:

“언제든 설정에서 변경 가능” 짧게 표시

2-3. 서버 연동: 최초 선택값을 “바로 저장”하지 말고 이렇게 처리

당신이 말한 대로 로컬에만 저장하는 방향은 좋아요(특히 익명/비로그인 구간).

다만 “로그인 사용자”가 등장하는 순간, 서버에서 최종 설정값을 보장해야 토글 신뢰성 문제가 사라집니다.

추천 방식(현실적인 타협)

최초 실행 선택은 로컬에만 저장 OK

사용자가 로그인한 순간:

프론트가 서버에 “내 기본값을 서버 설정으로 반영할래?”를 1회 요청

서버는 user_settings.store_messages_default에 저장

이후부터는 서버 설정이 기준

즉,

익명/비로그인: 로컬 기준

로그인 이후: 서버 기준 (정합성/법적 방어력↑)

3) UI “대화 저장” 설정 모달 추가 (ON/OFF)
3-1. 서버: 사용자 설정 저장 테이블(또는 기존 settings)에 필드 추가

예: user_settings

user_id

store_messages: boolean (기본 false)

updated_at

핵심: 매 메시지 저장 전에 서버가 이 값을 조회해 최종 결정.

3-2. API 추가 (권장)

GET /v1/users/me/settings

현재 store_messages 상태 반환

PATCH /v1/users/me/settings

{ "store_messages": true|false }

중요 규칙

익명: 이 API 접근 불가(401)

로그인만 가능

3-3. 프론트: 설정 토글 UI + “변경 확인 모달”

설정 화면에 토글 하나:

“대화 저장”

토글을 ON으로 바꾸는 순간 모달 1회 띄우기(정책적으로 강함):

모달 내용(핵심만):

저장되는 것: “대화 내용(질문/답변)”

목적: “상담 품질 제공/이력 제공(또는 기능 목적)”

철회: “언제든 OFF 가능”

삭제: “삭제 경로(설정/문의)를 제공” (당장 기능 없으면 운영정책이라도)

버튼:

[동의하고 저장 켜기]

[취소]

OFF는 모달 없이 바로 적용해도 괜찮습니다(법적으로 더 안전).

3-4. “설정 OFF → 즉시 효력” 구현 (추천 방식)

가장 깔끔한 구현은 이거예요:

사용자가 OFF로 바꾸는 순간:

서버에 PATCH로 store_messages=false

프론트는 현재 대화방 종료

새 conversation 생성 시 store_messages=false로 시작

즉,

기존 대화방은 (이미 저장되고 있었다면) 그대로 남을 수 있음(실무에서 흔함)

새 대화부터 저장이 멈춤 (즉시 효력)

추가 옵션(선택):

“기존 대화도 삭제” 버튼은 별도 기능으로 분리(나중에)

4) “정책이 진짜”가 되게 만드는 운영/로그 설정 체크리스트 (필수)

익명/비저장 모드에서 절대 하면 안 되는 것:

Reverse proxy access log에 request body 기록

앱/서버 debug 로그에 prompt/response 출력

Sentry/APM에 request body 자동 첨부

LLM 벤더 tracing에 prompt 저장

최소한 이것만은 바로 점검하세요:

(서버) request logging 미들웨어가 body 찍는지

(Sentry) send_default_pii, before_send에서 message 제거

(Nginx) log_format에 $request_body 같은 변수 쓰는지

5) 당신이 “바로 수정”할 수 있게 오늘 할 일 순서

서버: 익명 모드 강제 규칙 추가

익명은 store_messages=false 강제

TTL + turn_limit + turn_count 체크

프론트: 최초 실행 고지 화면 + 로컬 저장

has_seen_privacy_notice

chat_storage_consent

서버: 사용자 설정 API 2개 추가

GET/PATCH settings

프론트: 설정 토글 + ON 전 확인 모달

OFF 즉시 적용 + 새 conversation 생성

운영 설정: 로그/에러/트레이싱에서 본문 제거