Skip to content

[chore] prod 배포 v0.0.3 - #45

Merged
theminjunchoi merged 7 commits into
prodfrom
dev
Jul 25, 2026
Merged

[chore] prod 배포 v0.0.3#45
theminjunchoi merged 7 commits into
prodfrom
dev

Conversation

@theminjunchoi

@theminjunchoi theminjunchoi commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator

📌 개요

dev → prod 정기 배포. v0.0.2 → v0.0.3 (patch). 지난 릴리스 이후 머지된 기능·수정을 prod로 승격합니다.

🔧 포함 변경 (머지된 PR)

🌐 API · DB 영향

  • DB 마이그레이션(prod DB에 신규 적용): V5__llm_settings · V6__cards · V7__admin_accounts · V8__llm_settings
    • 신규 테이블: llm_settings, cards, admin_accounts 등. Flyway가 배포 시 자동 적용됩니다.
  • API: 신규 엔드포인트 다수 추가(카드 생성·조회, 답글 생성, 채팅방 삭제, 백오피스 LLM·관리자 API). 기존 API 하위 호환 유지.
  • 배포 전 확인: prod 환경변수/시크릿 — GEMINI_API_KEY(prod env secret), ADMIN_EMAILS(prod env var), VITE_FIREBASE_* 세팅 여부.

💬 리뷰 포인트

  • prod DB 마이그레이션(V5~V8)이 문제없이 적용되는지 — 신규 테이블 생성 위주라 위험 낮음
  • 배포 후 헬스체크(actuator) 및 백오피스 로그인 정상 동작 확인

Summary by CodeRabbit

  • 새 기능
    • 관리자 계정 추가·조회·삭제 기능과 관리자 관리 메뉴를 제공합니다.
    • LLM 모델 및 유형별 프롬프트를 백오피스에서 조회·수정할 수 있습니다.
    • 종료된 대화에서 감정 카드를 생성하고 날짜·월별로 확인할 수 있습니다.
    • 대화방 삭제 및 캐릭터 답글 생성 기능을 지원합니다.
  • 개선
    • 삭제된 대화방의 후속 작업을 차단하고 상태를 명확히 안내합니다.
    • 잘못된 요청 본문에 대한 오류 메시지를 개선했습니다.
  • 배포
    • Gemini 기반 기능을 위한 API 키 설정을 지원합니다.

theminjunchoi and others added 7 commits July 24, 2026 23:32
* feat: LLM 모델·프롬프트 백오피스 설정 백엔드

모델·시스템 프롬프트를 DB(llm_settings)로 옮겨 재배포 없이 런타임 반영한다.
값이 없으면 코드 기본값(GeminiProperties.model / PromptProvider.systemPrompt) 폴백.
GeminiCommentGenerator가 매 호출 LlmSettingsService.current()를 읽는다.
모델 드롭다운 목록은 GeminiModelCatalog가 Gemini API에서 동적 조회(신모델 자동 노출, 1h 캐시).
GET/PUT /api/admin/llm-settings 추가.

* feat: 백오피스 LLM 설정 페이지 추가

모델 드롭다운(동적 목록) + 시스템 프롬프트 편집 화면. 변경사항 추적·저장/되돌리기.
Textarea·Select 컴포넌트 추가, 네비·라우팅 연결.

* fix: 모델 목록 필터를 이름 기반으로 변경

supportedActions 는 Gemini 개발자 API 응답에서 비어 올 수 있어, 유효 키가 있어도
목록이 비는 위험이 있었다. gemini- 접두사(임베딩 제외) 이름으로 거른다.

* feat: LLM 설정을 모델·프롬프트 개별 저장·기본값 복원으로 분리

모델과 시스템 프롬프트에 각각 저장/기본값 변경 버튼을 둔다. 한 필드 저장이 다른 필드의
미저장 편집을 건드리지 않도록 편집값과 저장 기준값을 분리한다. '기본값 변경'은 각각
코드 기본값(GeminiProperties.model / PromptProvider.systemPrompt)으로 되돌린다.
GET 응답에 defaultModel·defaultSystemPrompt 추가.

* fix: LLM 설정 조회 실패 흡수 및 저장·조회 실패 피드백 (리뷰 반영)

- GeminiCommentGenerator: LlmSettingsService.current() 조회를 try 안으로 옮겨
  DB 실패도 CommentGenerationFailedException으로 감싸 재시도·FAILED로 처리(500·PENDING 고착 방지)
- LLM 설정 페이지: GET 실패 시 재시도 카드, 저장 실패 시 인라인 에러 표시
* feat: 대화 종료 시 감정 카드 생성 및 캘린더 조회

- 종료된 대화에 대표 감정 캐릭터의 한 줄 대사를 Gemini로 생성해 카드 저장
- 카드 대사 프롬프트는 대화용 SYSTEM_PROMPT와 분리(CardPromptProvider)
- POST /api/cards(종료된 대화만, 중복 409) + 날짜별·월별(캘린더) 조회
- 카드 날짜는 그 대화 생성시간(KST) 기준, conversation_created_at 비정규화
- V5 마이그레이션(cards), 대화 1개당 카드 1개 UNIQUE 보장

* feat: 캘린더 월별 조회를 날짜별 감정 목록만 반환하도록 변경

- 월별(/monthly)은 날짜별 대표 감정 목록만 반환(대사·요약 제외, 캘린더 표시용)
- 날짜별(?date=) 조회는 감정+대사+요약 전체 반환 유지(날짜 탭 시 상세)
- CardCalendarResponse: 대화 생성일(KST)별 그룹핑

* feat: 카드 대사를 유저를 대신해 불특정 다수에게 알리는 컨셉으로 변경

- 캐릭터가 유저를 3인칭(얘/쟤)으로 가리키며 불특정 다수에게 상태를 대변
- 캐릭터별 대변 방식 차별화(기쁨=자랑, 다정=감싸기, 분노=경고, 불안=조심, 까칠=시크방어, 엉뚱=딴소리)

* fix: 카드 생성 동시성·트랜잭션·한 줄 검증 리뷰 반영

- Gemini 호출을 트랜잭션 밖으로 분리해 외부 호출 중 DB 커넥션 장기 점유 방지
- saveAndFlush + 유니크 위반(DataIntegrityViolationException)을 CARD_ALREADY_EXISTS로 변환
- 카드 대사를 trim 후 개행 포함 시 거부해 한 줄 계약 검증

* refactor: 카드 도메인 불변식 가드 추가

- Card가 요약·대사 non-blank와 대사 한 줄 규칙을 init require로 자체 보장(Message·Nickname 패턴)

* refactor: CardGenerationFailedException을 별도 파일로 분리

- 생성기 파일엔 interface·output만 남김(CommentGenerator·CommentFeed 구조와 정렬)

* chore: 카드 마이그레이션 V5→V6 (dev의 V5 llm_settings와 충돌 해소)

* fix: 잘못된 요청 본문(enum·JSON)을 400 INVALID_INPUT으로 응답 (리뷰 반영)

- HttpMessageNotReadableException 핸들러 추가 — 기존엔 catch-all로 떨어져 500 반환되던 문제 수정
- 카드 생성 API 문서의 400(형식 오류)가 실제 동작과 일치
* feat: 백오피스에서 관리자(허용 이메일) DB 관리

- admin_accounts 테이블 + 허용 판정을 ENV 부트스트랩 ∪ DB 합집합으로 변경(재배포 없이 즉시 반영)
- /api/admin/accounts 목록·추가·삭제(ROLE_ADMIN), 자기삭제·중복·형식 검증
- ENV 부트스트랩은 UI 삭제 불가(자기잠금 방지 break-glass)
- 백오피스 '관리자 관리' 페이지(추가/삭제 UI)

* fix: 관리자 관리 리뷰 반영 (ENV 우선·본인 삭제 방지·a11y)

- 목록에서 ENV를 DB보다 우선(ENV와 중복된 DB 항목 숨김) → '삭제=즉시 차단' 안내 일관성 유지
- 본인 계정은 removable=false로 표시해 자기삭제 방지와 UI 일치
- 삭제 버튼에 이메일 포함 aria-label 추가
- DB 허용 로그인 경로 테스트 추가

* refactor: AdminAccount가 이메일 정규화 불변식을 스스로 보장

- 정규화(공백 제거·소문자)되지 않은 이메일이면 init require로 저장 전 차단
- unique 제약·isAllowed 조회가 정규화를 전제하므로 도메인이 fail-fast로 가드
* chore: llm_settings에 prompt_type 컬럼 추가

* feat: LLM 설정을 타입별로 분리하고 답글 생성 기능 추가

* feat: 캐릭터 답글에 대한 재응답 생성 서비스 구현

* feat: 캐릭터 답글 재응답 생성 API 추가

* chore: 안 쓰는 import 정리

* fix: llm_settings 마이그레이션을 V8로 재번호 (dev의 V6__cards.sql과 충돌)

* docs: 존재하지 않는 채팅방 상태 적용

* fix: DONE인데 답글 메시지를 못 찾으면 FAILED로 방어 처리 (CodeRabbit 리뷰 반영)

* fix: messages.replies_to_message_id에 unique 제약 추가 (CodeRabbit 리뷰 반영)

findByRepliesToMessageId가 최대 1개를 가정하는데, CAS로 지금 경로는 중복을
막고 있어도 DB 자체는 강제하지 않았다. 다른 경로가 생기면 중복 행이 쌓여
IncorrectResultSizeDataAccessException을 던질 수 있어 DB 제약으로 막는다.

* test: characterMessage/diaryMessage 픽스처에 명시적 id 부여 (CodeRabbit 리뷰 반영)

둘 다 id 기본값(0)에 의존하고 있어 rootMessageId에 잘못된 쪽의 id가
들어가도 테스트가 못 잡아냈다. 실제로 코드를 일부러 바꿔서 이 테스트가
그 실수를 잡아내는 것까지 확인했다.

* test: PromptType.REPLY 분기 테스트 추가 (CodeRabbit 리뷰 반영)

current/defaultPrompt가 COMMENT만 테스트되고 있어 REPLY -> replyPrompt
매핑이 실수로 바뀌어도 안 잡혔다. when 분기를 일부러 바꿔서 이 테스트가
그 실수를 잡아내는 것까지 확인했다.

* fix: 캐릭터 답글 1개 제약 삭제
- image.yml .env 생성부에 GEMINI_API_KEY 시크릿 추가
- dev/prod docker-compose app 서비스 environment에 GEMINI_API_KEY 주입
* feat: 프롬프트 공통화 및 댓글·답글·카드 프롬프트 백오피스 편집

- 공통 프롬프트(톤·경계·말맛지침·6종 보이스카드) 추출 → COMMON + 타입으로 조립
- PromptType에 COMMON·CARD 추가, LlmSettingsService.forGeneration이 공통+타입 조립
- 카드 생성을 LlmSettingsService(CARD)로 배선 → CardPromptProvider 삭제, 카드도 편집 가능
- AdminLlmSettingsController가 promptType별 조회·수정(COMMON은 모델 없이 프롬프트만)
- 백오피스 LLM 설정 페이지에 타입 탭(공통/댓글/답글/카드) + 조립 안내

* chore: 로컬 Flyway 검증 실패 시 자동 clean+재적용

- 브랜치 전환으로 로컬 DB 체크섬이 어긋나면 local 프로필에서 자동 복구(FlywayMigrationStrategy)
- clean 허용은 application-local.yml에만 → dev/prod는 영향 없음

* refactor: LLM 설정의 SRP·OCP 개선

- PromptType에 usesModel 데이터 부여 → COMMON 특수분기(if ==)를 데이터로 대체(OCP)
- 타입→기본프롬프트 매핑을 PromptProvider로 이관 → 서비스에서 when 제거
- 프롬프트 조립을 SystemPromptResolver로 분리 → LlmSettingsService는 설정 CRUD만(SRP)

* refactor: llm 패키지를 역할별 하위 패키지로 분리

- prompt(프롬프트), settings(설정), parsing(파싱·검증), generation(LLM 호출·프로바이더), selection(캐릭터 선택)
- CommentGenerationFailedException을 CommentFeed.kt에서 분리(one-type-per-file)

* feat: LLM 모델을 앱 전체 단일 설정으로 분리

- 모델을 타입별에서 앱 전체 단일값으로 변경(COMMON 행에 저장), 프롬프트는 타입별 유지
- 컨트롤러 엔드포인트 분리: GET/PUT /model(단일 모델), /prompt(타입별 프롬프트)
- 백오피스 UI 재구성: 상단 모델 카드(공통) + 하단 프롬프트 탭(프롬프트만 편집)
- PromptType.usesModel 제거(모델이 더 이상 타입별이 아님)

* refactor: 다중타입 파일을 타입당 한 파일로 분리

- CommentFeed → CommentFeed/CommentDraft/TikitakaDraft
- CommentGenerator → CommentGenerator/CommentGenerationOutput/ReplyGenerationOutput
- CardMessageGenerator → CardMessageGenerator/CardMessageOutput
- CommentGenerationResult → CommentGenerationResult/CommentGenerationOutcome
- CommentGenerationResponse → CommentGenerationResponse/CommentGenerationStatus

* refactor: if/else 블록을 early-return·guard clause로 정리

- 생성 실패 로그 if/else 2곳을 logGenerationFailure 헬퍼(early-return)로 추출(중복 제거)
- currentReplyStatusResult를 guard clause로 재작성(when + 내부 if/else 제거)

* refactor: llm 패키지 순환 의존성 해소 (CodeRabbit 리뷰 반영)

- GeminiProperties·GeminiModelCatalog → llm.config (settings↔generation 순환 제거)
- 공유 예외(Comment/Card GenerationFailedException) → llm.error (prompt·parsing→generation 순환 제거)
- 카드 생성기 KDoc의 조립 책임 주체를 SystemPromptResolver로 정정

* refactor: 프롬프트 조립 시 COMMON 행 중복 조회 제거 (리뷰 반영)

- currentModel()+currentPrompt(COMMON)으로 COMMON 행을 두 번 읽던 것을
  currentCommonView()로 한 번만 읽어 재사용
- COMMON 조립 2→1, 비-COMMON 조립 3→2 쿼리로 감소
* [feat] 채팅방 삭제 기능 추가

종료(end)와 별개로 채팅방을 삭제할 수 있게 한다. 삭제된 채팅방은 종료 여부와
무관하게 삭제 가능하며, 삭제 후에는 목록 조회·메시지 조회·메시지 추가·종료 등
어떤 요청도 CONVERSATION_ALREADY_DELETED로 거부된다.

* test: 채팅방 삭제 기능 테스트 추가

삭제 성공/중복 삭제/소유권 검증과, 삭제된 채팅방에 대한 종료·메시지 저장·
메시지 조회·목록 조회 차단을 단위·통합 테스트로 검증한다.

* docs: swagger 문서 반영

* fix: 채팅방 상태 변경 요청에 PESSIMISTIC_WRITE 락 적용

삭제와 메시지 저장·종료가 락 없이 상태를 읽어, 삭제 커밋 이후에도
뒤늦게 저장이 커밋될 수 있었다. 상태 변경 조회를 findByIdForUpdate로
바꿔 같은 채팅방에 대한 요청을 직렬화한다.

* test: 삭제·메시지 저장 동시 요청 경합 검증 테스트 추가

실제 MySQL 컨테이너에서 삭제 트랜잭션이 락을 쥔 동안 동시에
saveUserMessage를 호출해, 저장이 락 해제까지 대기했다가
CONVERSATION_ALREADY_DELETED로 실패하고 메시지가 남지 않는지 검증한다.

* fix: 삭제된 채팅방에서 댓글·답글 생성이 가능하던 문제 수정

댓글·답글 생성이 메시지 소유권만 확인하고 채팅방이 삭제됐는지는 보지
않아, 삭제된 채팅방의 메시지에도 CAS 선점·LLM 호출까지 이어질 수
있었다. 두 흐름이 공통으로 거치는 getOwnedRootMessage에
conversation.ensureNotDeleted()를 추가해 생성 시도 자체를 막는다
(메시지 조회를 삭제된 채팅방에서 막는 기존 규칙과 동일한 지점).

* fix: 삭제된 채팅방의 카드가 캘린더·날짜별 조회에 남아있던 문제 수정

카드는 대화 종료 여부와 무관하게 삭제될 수 있는데, 카드 조회 쿼리가
채팅방 상태를 보지 않아 삭제 후에도 캘린더·날짜별 조회에 그대로
남아있었다. findAllByMemberIdAndConversationCreatedAtInRange을
Conversation과 조인해 status <> DELETED 조건을 추가한다. 날짜별·월별
조회가 이 메서드 하나를 공유해 두 화면 모두 한 번에 고쳐진다.
@theminjunchoi theminjunchoi self-assigned this Jul 25, 2026
@theminjunchoi
theminjunchoi merged commit ec9274f into prod Jul 25, 2026
6 of 7 checks passed
@github-actions

Copy link
Copy Markdown

Test Coverage

Overall Project 69.48% -17.52% 🍏
Files changed 54.13% 🍏

File Coverage
CardService.kt 100% 🍏
CommentGenerationStatus.kt 100% 🍏
ConversationResponse.kt 100% 🍏
CardGenerationFailedException.kt 100% 🍏
CommentGenerationFailedException.kt 100% 🍏
AdminAuthService.kt 100% 🍏
AdminAccountSource.kt 100% 🍏
CardCalendarResponse.kt 100% 🍏
CommentFeed.kt 100% 🍏
TikitakaDraft.kt 100% 🍏
CommentDraft.kt 100% 🍏
LlmSettingsView.kt 100% 🍏
SystemPromptResolver.kt 100% 🍏
ErrorCode.kt 100% 🍏
ReplyGenerationOutput.kt 100% 🍏
CommentGenerationOutput.kt 100% 🍏
ConversationStatus.kt 100% 🍏
ReplyGenerationResult.kt 100% 🍏
CommentGenerationResult.kt 100% 🍏
CommentGenerationOutcome.kt 100% 🍏
PromptType.kt 100% 🍏
PromptCharacterId.kt 99.08% 🍏
Conversation.kt 97.26% 🍏
GlobalExceptionHandler.kt 96.95% 🍏
ConversationService.kt 96.67% 🍏
AdminAccountService.kt 96.65% -3.35% 🍏
CommentFeedJsonParser.kt 95.42% 🍏
AdminProperties.kt 93.85% 🍏
CommentFeedValidator.kt 92.11% -6.58% 🍏
EongttungTopicSelector.kt 90% 🍏
CommentGenerationService.kt 89.75% -6.54% 🍏
AdminAccount.kt 89.41% -10.59% 🍏
Card.kt 87.5% -12.5% 🍏
AdminAccountEntry.kt 85.94% -14.06% 🍏
CardMessageOutput.kt 80% -20% 🍏
GeminiProperties.kt 80% 🍏
LlmSettingsService.kt 76.63% -23.37% 🍏
LlmSettings.kt 76.36% -23.64% 🍏
MessageRepository.kt 66.67% 🍏
ConversationRepository.kt 57.14% 🍏
CardRepository.kt 57.14% -42.86% 🍏
ConversationController.kt 52.24% -30.35% 🍏
CharacterSelector.kt 43.24% 🍏
PromptProvider.kt 17.44% -82.56% 🍏
ReplyJsonParser.kt 16.22% -83.78% 🍏
GeminiModelCatalog.kt 11.24% -88.76% 🍏
AdminAccountController.kt 9.84% -90.16% 🍏
GeminiCardMessageGenerator.kt 8.73% -91.27% 🍏
AdminLlmSettingsController.kt 6.59% -93.41% 🍏
CardController.kt 6.45% -93.55% 🍏
GeminiCommentGenerator.kt 5.52% -30.7% 🍏
CommentPersistenceService.kt 4.66% -33.16% 🍏
CommentGenerationResponse.kt 0% 🍏
ReplyGenerationResponse.kt 0% 🍏
CreateCardRequest.kt 0% 🍏
CardResponse.kt 0% 🍏
LocalFlywayConfig.kt 0% 🍏
AddAdminAccountRequest.kt 0% 🍏
PromptSettingResponse.kt 0% 🍏
ModelSettingResponse.kt 0% 🍏
UpdateModelRequest.kt 0% 🍏
AdminAccountResponse.kt 0% 🍏
UpdatePromptRequest.kt 0% 🍏

@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 0ad61a50-e9a3-41d1-84fa-5964e4bc0619

📥 Commits

Reviewing files that changed from the base of the PR and between b533aec and 573ec8e.

📒 Files selected for processing (101)
  • .github/workflows/image.yml
  • admin-web/src/App.tsx
  • admin-web/src/components/layout/AdminLayout.tsx
  • admin-web/src/components/ui/select.tsx
  • admin-web/src/components/ui/textarea.tsx
  • admin-web/src/pages/admin-accounts/list.tsx
  • admin-web/src/pages/llm-settings.tsx
  • admin-web/src/types/adminAccount.ts
  • deploy/dev/docker-compose.yml
  • deploy/prod/docker-compose.yml
  • src/main/kotlin/com/nexters/gamss/admin/config/AdminProperties.kt
  • src/main/kotlin/com/nexters/gamss/admin/controller/AdminAccountController.kt
  • src/main/kotlin/com/nexters/gamss/admin/controller/AdminLlmSettingsController.kt
  • src/main/kotlin/com/nexters/gamss/admin/controller/dto/AddAdminAccountRequest.kt
  • src/main/kotlin/com/nexters/gamss/admin/controller/dto/AdminAccountResponse.kt
  • src/main/kotlin/com/nexters/gamss/admin/controller/dto/ModelSettingResponse.kt
  • src/main/kotlin/com/nexters/gamss/admin/controller/dto/PromptSettingResponse.kt
  • src/main/kotlin/com/nexters/gamss/admin/controller/dto/UpdateModelRequest.kt
  • src/main/kotlin/com/nexters/gamss/admin/controller/dto/UpdatePromptRequest.kt
  • src/main/kotlin/com/nexters/gamss/admin/domain/AdminAccount.kt
  • src/main/kotlin/com/nexters/gamss/admin/repository/AdminAccountRepository.kt
  • src/main/kotlin/com/nexters/gamss/admin/service/AdminAccountEntry.kt
  • src/main/kotlin/com/nexters/gamss/admin/service/AdminAccountService.kt
  • src/main/kotlin/com/nexters/gamss/admin/service/AdminAccountSource.kt
  • src/main/kotlin/com/nexters/gamss/admin/service/AdminAuthService.kt
  • src/main/kotlin/com/nexters/gamss/card/controller/CardController.kt
  • src/main/kotlin/com/nexters/gamss/card/controller/dto/CardCalendarResponse.kt
  • src/main/kotlin/com/nexters/gamss/card/controller/dto/CardResponse.kt
  • src/main/kotlin/com/nexters/gamss/card/controller/dto/CreateCardRequest.kt
  • src/main/kotlin/com/nexters/gamss/card/domain/Card.kt
  • src/main/kotlin/com/nexters/gamss/card/repository/CardRepository.kt
  • src/main/kotlin/com/nexters/gamss/card/service/CardService.kt
  • src/main/kotlin/com/nexters/gamss/conversation/controller/ConversationController.kt
  • src/main/kotlin/com/nexters/gamss/conversation/controller/dto/CommentGenerationResponse.kt
  • src/main/kotlin/com/nexters/gamss/conversation/controller/dto/CommentGenerationStatus.kt
  • src/main/kotlin/com/nexters/gamss/conversation/controller/dto/ConversationResponse.kt
  • src/main/kotlin/com/nexters/gamss/conversation/controller/dto/ReplyGenerationResponse.kt
  • src/main/kotlin/com/nexters/gamss/conversation/domain/Conversation.kt
  • src/main/kotlin/com/nexters/gamss/conversation/domain/ConversationStatus.kt
  • src/main/kotlin/com/nexters/gamss/conversation/repository/ConversationRepository.kt
  • src/main/kotlin/com/nexters/gamss/conversation/repository/MessageRepository.kt
  • src/main/kotlin/com/nexters/gamss/conversation/service/CommentGenerationOutcome.kt
  • src/main/kotlin/com/nexters/gamss/conversation/service/CommentGenerationResult.kt
  • src/main/kotlin/com/nexters/gamss/conversation/service/CommentGenerationService.kt
  • src/main/kotlin/com/nexters/gamss/conversation/service/CommentPersistenceService.kt
  • src/main/kotlin/com/nexters/gamss/conversation/service/ConversationService.kt
  • src/main/kotlin/com/nexters/gamss/conversation/service/ReplyGenerationResult.kt
  • src/main/kotlin/com/nexters/gamss/global/config/LocalFlywayConfig.kt
  • src/main/kotlin/com/nexters/gamss/global/exception/ErrorCode.kt
  • src/main/kotlin/com/nexters/gamss/global/exception/GlobalExceptionHandler.kt
  • src/main/kotlin/com/nexters/gamss/llm/CommentFeed.kt
  • src/main/kotlin/com/nexters/gamss/llm/CommentGenerator.kt
  • src/main/kotlin/com/nexters/gamss/llm/PromptProvider.kt
  • src/main/kotlin/com/nexters/gamss/llm/config/GeminiModelCatalog.kt
  • src/main/kotlin/com/nexters/gamss/llm/config/GeminiProperties.kt
  • src/main/kotlin/com/nexters/gamss/llm/error/CardGenerationFailedException.kt
  • src/main/kotlin/com/nexters/gamss/llm/error/CommentGenerationFailedException.kt
  • src/main/kotlin/com/nexters/gamss/llm/generation/CardMessageGenerator.kt
  • src/main/kotlin/com/nexters/gamss/llm/generation/CardMessageOutput.kt
  • src/main/kotlin/com/nexters/gamss/llm/generation/CommentGenerationOutput.kt
  • src/main/kotlin/com/nexters/gamss/llm/generation/CommentGenerator.kt
  • src/main/kotlin/com/nexters/gamss/llm/generation/GeminiCardMessageGenerator.kt
  • src/main/kotlin/com/nexters/gamss/llm/generation/GeminiCommentGenerator.kt
  • src/main/kotlin/com/nexters/gamss/llm/generation/ReplyGenerationOutput.kt
  • src/main/kotlin/com/nexters/gamss/llm/parsing/CommentDraft.kt
  • src/main/kotlin/com/nexters/gamss/llm/parsing/CommentFeed.kt
  • src/main/kotlin/com/nexters/gamss/llm/parsing/CommentFeedJsonParser.kt
  • src/main/kotlin/com/nexters/gamss/llm/parsing/CommentFeedValidator.kt
  • src/main/kotlin/com/nexters/gamss/llm/parsing/ReplyJsonParser.kt
  • src/main/kotlin/com/nexters/gamss/llm/parsing/TikitakaDraft.kt
  • src/main/kotlin/com/nexters/gamss/llm/prompt/PromptCharacterId.kt
  • src/main/kotlin/com/nexters/gamss/llm/prompt/PromptProvider.kt
  • src/main/kotlin/com/nexters/gamss/llm/prompt/PromptType.kt
  • src/main/kotlin/com/nexters/gamss/llm/selection/CharacterSelector.kt
  • src/main/kotlin/com/nexters/gamss/llm/selection/EongttungTopicSelector.kt
  • src/main/kotlin/com/nexters/gamss/llm/settings/LlmSettings.kt
  • src/main/kotlin/com/nexters/gamss/llm/settings/LlmSettingsRepository.kt
  • src/main/kotlin/com/nexters/gamss/llm/settings/LlmSettingsService.kt
  • src/main/kotlin/com/nexters/gamss/llm/settings/LlmSettingsView.kt
  • src/main/kotlin/com/nexters/gamss/llm/settings/SystemPromptResolver.kt
  • src/main/resources/application-local.yml
  • src/main/resources/db/migration/V5__llm_settings.sql
  • src/main/resources/db/migration/V6__cards.sql
  • src/main/resources/db/migration/V7__admin_accounts.sql
  • src/main/resources/db/migration/V8__llm_settings.sql
  • src/test/kotlin/com/nexters/gamss/admin/domain/AdminAccountTest.kt
  • src/test/kotlin/com/nexters/gamss/admin/service/AdminAccountServiceTest.kt
  • src/test/kotlin/com/nexters/gamss/admin/service/AdminAuthServiceTest.kt
  • src/test/kotlin/com/nexters/gamss/card/controller/dto/CardCalendarResponseTest.kt
  • src/test/kotlin/com/nexters/gamss/card/domain/CardTest.kt
  • src/test/kotlin/com/nexters/gamss/card/repository/CardRepositoryTest.kt
  • src/test/kotlin/com/nexters/gamss/card/service/CardServiceTest.kt
  • src/test/kotlin/com/nexters/gamss/conversation/controller/ConversationControllerIntegrationTest.kt
  • src/test/kotlin/com/nexters/gamss/conversation/service/CommentGenerationServiceTest.kt
  • src/test/kotlin/com/nexters/gamss/conversation/service/ConversationConcurrencyIntegrationTest.kt
  • src/test/kotlin/com/nexters/gamss/conversation/service/ConversationServiceTest.kt
  • src/test/kotlin/com/nexters/gamss/global/exception/GlobalExceptionHandlerTest.kt
  • src/test/kotlin/com/nexters/gamss/llm/parsing/CommentFeedJsonParserTest.kt
  • src/test/kotlin/com/nexters/gamss/llm/parsing/CommentFeedValidatorTest.kt
  • src/test/kotlin/com/nexters/gamss/llm/settings/LlmSettingsServiceTest.kt
  • src/test/kotlin/com/nexters/gamss/llm/settings/SystemPromptResolverTest.kt

Walkthrough

관리자 계정·LLM 설정 관리 화면과 API, 감정 카드 생성·조회, 대화 삭제·답글 생성 기능이 추가되었습니다. Gemini 설정을 저장·조회하고 배포 환경에 API 키를 주입하며, 삭제 상태와 동시성 제어 및 관련 검증 테스트가 확장되었습니다.

Changes

관리자 계정 관리

Layer / File(s) Summary
관리자 계정 도메인과 API
src/main/kotlin/com/nexters/gamss/admin/...
ENV·DB 관리자 계정을 통합하고 목록·추가·삭제 API를 제공합니다.
관리자 웹 화면
admin-web/src/App.tsx, admin-web/src/components/layout/*, admin-web/src/pages/admin-accounts/*
관리자 관리 라우트, 내비게이션, 목록·추가·삭제 UI를 추가했습니다.

대화·답글·카드

Layer / File(s) Summary
대화 삭제와 잠금
src/main/kotlin/com/nexters/gamss/conversation/...
DELETED 상태, 삭제 API, 행 잠금과 삭제된 대화 접근 차단을 추가했습니다.
답글 생성
src/main/kotlin/com/nexters/gamss/conversation/service/*, src/main/kotlin/com/nexters/gamss/llm/generation/*
답글 생성의 PENDING 선점, LLM 재시도, 저장 및 상태 응답을 연결했습니다.
감정 카드
src/main/kotlin/com/nexters/gamss/card/...
종료 대화의 카드 생성과 KST 기준 일·월별 조회 API를 추가했습니다.

LLM 설정과 생성

Layer / File(s) Summary
LLM 설정 저장과 해석
src/main/kotlin/com/nexters/gamss/llm/settings/*, src/main/kotlin/com/nexters/gamss/llm/prompt/*
모델·타입별 프롬프트를 DB와 코드 기본값으로 관리하고 요청별 설정으로 조합합니다.
LLM 관리자 화면
admin-web/src/pages/llm-settings.tsx, admin-web/src/components/ui/*
모델 선택과 프롬프트 편집·저장 UI를 추가했습니다.

배포와 공통 처리

Layer / File(s) Summary
배포 환경과 오류 처리
.github/workflows/image.yml, deploy/*, src/main/resources/*, src/main/kotlin/com/nexters/gamss/global/*
Gemini API 키 주입, 로컬 Flyway 복구, JSON 본문 파싱 오류 응답을 추가했습니다.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Admin
  participant AdminWeb
  participant AdminController
  participant LlmSettingsService
  participant GeminiCommentGenerator
  Admin->>AdminWeb: 모델 또는 프롬프트 저장
  AdminWeb->>AdminController: PUT /api/admin/llm-settings
  AdminController->>LlmSettingsService: updateModel/updatePrompt
  LlmSettingsService-->>AdminController: 최신 설정
  AdminController-->>AdminWeb: 설정 응답
  GeminiCommentGenerator->>LlmSettingsService: 현재 모델·프롬프트 조회
  GeminiCommentGenerator-->>GeminiCommentGenerator: Gemini 요청 생성
Loading

Possibly related PRs

  • Nexters/GAMSS-Server#44: 대화 삭제 상태와 잠금 기반 삭제 흐름이 동일한 코드 경로에 연결됩니다.
  • Nexters/GAMSS-Server#41: LLM 설정 API와 프롬프트 편집 구조가 동일한 코드 경로를 확장합니다.
  • Nexters/GAMSS-Server#33: 관리자 계정 DB 관리와 관리자 웹 화면이 직접 이어집니다.

Suggested reviewers: kite707

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Trivy (0.72.0)

Trivy execution failed: 2026-07-25T16:19:31Z FATAL Fatal error run error: fs scan error: scan error: scan failed: failed analysis: analyze with traversal: walk dir error: unknown error with coderabbit-detekt-report-16.sarif: file info error: lstat ./coderabbit-detekt-report-16.sarif: no such file or directory


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants